diff --git a/.github/workflows/check-configuration-files.yml b/.github/workflows/check-configuration-files.yml new file mode 100644 index 0000000000..0b292ded79 --- /dev/null +++ b/.github/workflows/check-configuration-files.yml @@ -0,0 +1,47 @@ +# +# Copyright © 2016-2023 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: Check configuration files +on: + push: + branches: + - master + pull_request: + paths: + - 'application/src/main/resources/thingsboard.yml' + - 'transport/http/src/main/resources/tb-http-transport.yml' + - 'transport/http/src/main/resources/tb-mqtt-transport.yml' + - 'transport/http/src/main/resources/tb-coap-transport.yml' + - 'transport/http/src/main/resources/tb-lwm2m-transport.yml' + - 'transport/http/src/main/resources/tb-snmp-transport.yml' + - 'msa/vc-executor/src/main/resources/tb-vc-executor.yml' + +jobs: + build: + name: Check thingsboard.yml file + runs-on: ubuntu-20.04 + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.10.2" + architecture: "x64" + env: + AGENT_TOOLSDIRECTORY: /opt/hostedtoolcache + - name: Run Verification Script + run: python3 tools/src/main/python/check_yml_file.py diff --git a/README.md b/README.md index 581e71977d..d479296869 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ ThingsBoard documentation is hosted on [thingsboard.io](https://thingsboard.io/d [![Smart farming](https://user-images.githubusercontent.com/8308069/152984443-a98b7d3d-ff7a-4037-9011-e71e1e6f755f.gif "Smart farming")](https://thingsboard.io/smart-farming/) [**IoT Rule Engine**](https://thingsboard.io/docs/user-guide/rule-engine-2-0/re-getting-started/) -[![IoT Rule Engine](https://thingsboard.io/images/demo/send-email-rule-chain.gif "IoT Rule Engine")](https://thingsboard.io/docs/user-guide/rule-engine-2-0/re-getting-started/) +[![IoT Rule Engine](https://img.thingsboard.io/demo/send-email-rule-chain.gif "IoT Rule Engine")](https://thingsboard.io/docs/user-guide/rule-engine-2-0/re-getting-started/) [**Smart metering**](https://thingsboard.io/smart-metering/) [![Smart metering](https://user-images.githubusercontent.com/8308069/31455788-6888a948-aec1-11e7-9819-410e0ba785e0.gif "Smart metering")](https://thingsboard.io/smart-metering/) diff --git a/application/pom.xml b/application/pom.xml index 8a429bcef9..422a6b4095 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard application diff --git a/application/src/main/data/certs/azure/BaltimoreCyberTrustRoot.crt.pem b/application/src/main/data/certs/azure/BaltimoreCyberTrustRoot.crt.pem deleted file mode 100644 index 2bd16ebd47..0000000000 --- a/application/src/main/data/certs/azure/BaltimoreCyberTrustRoot.crt.pem +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ -RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD -VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX -DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y -ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy -VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr -mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr -IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK -mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu -XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy -dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye -jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 -BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 -DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 -9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx -jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 -Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz -ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS -R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp ------END CERTIFICATE----- - diff --git a/application/src/main/data/certs/azure/DigiCertGlobalRootG2.crt.pem b/application/src/main/data/certs/azure/DigiCertGlobalRootG2.crt.pem new file mode 100644 index 0000000000..798e002751 --- /dev/null +++ b/application/src/main/data/certs/azure/DigiCertGlobalRootG2.crt.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- diff --git a/application/src/main/data/json/demo/dashboards/firmware.json b/application/src/main/data/json/demo/dashboards/firmware.json index 779f2cb93c..7a21e86761 100644 --- a/application/src/main/data/json/demo/dashboards/firmware.json +++ b/application/src/main/data/json/demo/dashboards/firmware.json @@ -1,24 +1,34 @@ { "title": "Firmware", "image": null, + "mobileHide": false, + "mobileOrder": null, "configuration": { "description": "", "widgets": { "cd03188e-cd9d-9601-fd57-da4cb95fc016": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "entities_table", "type": "latest", - "title": "New widget", - "image": null, - "description": null, "sizeX": 7.5, "sizeY": 6.5, "config": { "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, "interval": 1000, - "timewindowMs": 86400000 + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085363133, + "endTimeMs": 1694171763133 + }, + "quickInterval": "CURRENT_DAY" }, "aggregation": { "type": "NONE", @@ -299,16 +309,11 @@ }, "row": 0, "col": 0, - "id": "cd03188e-cd9d-9601-fd57-da4cb95fc016" + "id": "cd03188e-cd9d-9601-fd57-da4cb95fc016", + "typeFullFqn": "system.cards.entities_table" }, "100b756c-0082-6505-3ae1-3603e6deea48": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "timeseries_table", "type": "timeseries", - "title": "New widget", - "image": null, - "description": null, "sizeX": 8, "sizeY": 6.5, "config": { @@ -461,14 +466,11 @@ }, "row": 0, "col": 0, - "id": "100b756c-0082-6505-3ae1-3603e6deea48" + "id": "100b756c-0082-6505-3ae1-3603e6deea48", + "typeFullFqn": "system.cards.timeseries_table" }, "17543c57-af4a-2c1e-bf12-53a7b46791e6": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "html_value_card", "type": "latest", - "title": "New widget", "sizeX": 8, "sizeY": 3, "config": { @@ -496,8 +498,27 @@ } ], "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { - "timewindowMs": 60000 + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085363133, + "endTimeMs": 1694171763133 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 } }, "showTitle": false, @@ -545,14 +566,11 @@ "enableDataExport": false, "displayTimewindow": true }, - "id": "17543c57-af4a-2c1e-bf12-53a7b46791e6" + "id": "17543c57-af4a-2c1e-bf12-53a7b46791e6", + "typeFullFqn": "system.cards.html_value_card" }, "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "html_value_card", "type": "latest", - "title": "New widget", "sizeX": 8, "sizeY": 3, "config": { @@ -580,8 +598,27 @@ } ], "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { - "timewindowMs": 60000 + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085363133, + "endTimeMs": 1694171763133 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 } }, "showTitle": false, @@ -629,14 +666,11 @@ "enableDataExport": false, "displayTimewindow": true }, - "id": "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6" + "id": "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6", + "typeFullFqn": "system.cards.html_value_card" }, "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "html_value_card", "type": "latest", - "title": "New widget", "sizeX": 8, "sizeY": 3, "config": { @@ -664,8 +698,27 @@ } ], "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { - "timewindowMs": 60000 + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085363133, + "endTimeMs": 1694171763133 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 } }, "showTitle": false, @@ -713,14 +766,11 @@ "enableDataExport": false, "displayTimewindow": true }, - "id": "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81" + "id": "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81", + "typeFullFqn": "system.cards.html_value_card" }, "77b10144-b904-edd5-8c7c-8fb75616c6d8": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "html_value_card", "type": "latest", - "title": "New widget", "sizeX": 8, "sizeY": 3, "config": { @@ -748,8 +798,27 @@ } ], "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { - "timewindowMs": 60000 + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085363133, + "endTimeMs": 1694171763133 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 } }, "showTitle": false, @@ -797,23 +866,32 @@ "enableDataExport": false, "displayTimewindow": true }, - "id": "77b10144-b904-edd5-8c7c-8fb75616c6d8" + "id": "77b10144-b904-edd5-8c7c-8fb75616c6d8", + "typeFullFqn": "system.cards.html_value_card" }, "21be08bb-ec90-f760-ad6f-e7678f12c401": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "entities_table", "type": "latest", - "title": "New widget", - "image": null, - "description": null, "sizeX": 7.5, "sizeY": 6.5, "config": { "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { + "realtimeType": 1, "interval": 1000, - "timewindowMs": 86400000 + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085363133, + "endTimeMs": 1694171763133 + }, + "quickInterval": "CURRENT_DAY" }, "aggregation": { "type": "NONE", @@ -1094,23 +1172,32 @@ }, "row": 0, "col": 0, - "id": "21be08bb-ec90-f760-ad6f-e7678f12c401" + "id": "21be08bb-ec90-f760-ad6f-e7678f12c401", + "typeFullFqn": "system.cards.entities_table" }, "e8280043-d3dc-7acb-c2ff-a4522972ff91": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "entities_table", "type": "latest", - "title": "New widget", - "image": null, - "description": null, "sizeX": 7.5, "sizeY": 6.5, "config": { "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, "interval": 1000, - "timewindowMs": 86400000 + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085363133, + "endTimeMs": 1694171763133 + }, + "quickInterval": "CURRENT_DAY" }, "aggregation": { "type": "NONE", @@ -1391,23 +1478,32 @@ }, "row": 0, "col": 0, - "id": "e8280043-d3dc-7acb-c2ff-a4522972ff91" + "id": "e8280043-d3dc-7acb-c2ff-a4522972ff91", + "typeFullFqn": "system.cards.entities_table" }, "3624013b-378c-f110-5eba-ae95c25a4dcc": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "entities_table", "type": "latest", - "title": "New widget", - "image": null, - "description": null, "sizeX": 7.5, "sizeY": 6.5, "config": { "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, "interval": 1000, - "timewindowMs": 86400000 + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085363133, + "endTimeMs": 1694171763133 + }, + "quickInterval": "CURRENT_DAY" }, "aggregation": { "type": "NONE", @@ -1688,23 +1784,32 @@ }, "row": 0, "col": 0, - "id": "3624013b-378c-f110-5eba-ae95c25a4dcc" + "id": "3624013b-378c-f110-5eba-ae95c25a4dcc", + "typeFullFqn": "system.cards.entities_table" }, "d2d13e0d-4e71-889f-9343-ad2f0af9f176": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "entities_table", "type": "latest", - "title": "New widget", - "image": null, - "description": null, "sizeX": 7.5, "sizeY": 6.5, "config": { "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { + "realtimeType": 1, "interval": 1000, - "timewindowMs": 86400000 + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085363133, + "endTimeMs": 1694171763133 + }, + "quickInterval": "CURRENT_DAY" }, "aggregation": { "type": "NONE", @@ -1985,7 +2090,8 @@ }, "row": 0, "col": 0, - "id": "d2d13e0d-4e71-889f-9343-ad2f0af9f176" + "id": "d2d13e0d-4e71-889f-9343-ad2f0af9f176", + "typeFullFqn": "system.cards.entities_table" } }, "states": { @@ -2035,7 +2141,8 @@ "autoFillHeight": true, "backgroundImageUrl": null, "mobileAutoFillHeight": true, - "mobileRowHeight": 70 + "mobileRowHeight": 70, + "outerMargin": true } } } @@ -2062,7 +2169,8 @@ "autoFillHeight": true, "backgroundImageUrl": null, "mobileAutoFillHeight": false, - "mobileRowHeight": 70 + "mobileRowHeight": 70, + "outerMargin": true } } } @@ -2089,7 +2197,8 @@ "autoFillHeight": true, "backgroundImageUrl": null, "mobileAutoFillHeight": false, - "mobileRowHeight": 70 + "mobileRowHeight": 70, + "outerMargin": true } } } @@ -2116,7 +2225,8 @@ "autoFillHeight": true, "backgroundImageUrl": null, "mobileAutoFillHeight": false, - "mobileRowHeight": 70 + "mobileRowHeight": 70, + "outerMargin": true } } } @@ -2143,7 +2253,8 @@ "autoFillHeight": true, "backgroundImageUrl": null, "mobileAutoFillHeight": false, - "mobileRowHeight": 70 + "mobileRowHeight": 70, + "outerMargin": true } } } @@ -2170,7 +2281,8 @@ "autoFillHeight": true, "backgroundImageUrl": null, "mobileAutoFillHeight": false, - "mobileRowHeight": 70 + "mobileRowHeight": 70, + "outerMargin": true } } } @@ -2488,5 +2600,6 @@ "showUpdateDashboardImage": false } }, + "externalId": null, "name": "Firmware" } \ No newline at end of file diff --git a/application/src/main/data/json/demo/dashboards/gateways.json b/application/src/main/data/json/demo/dashboards/gateways.json deleted file mode 100644 index 6051fbd514..0000000000 --- a/application/src/main/data/json/demo/dashboards/gateways.json +++ /dev/null @@ -1,1267 +0,0 @@ -{ - "title": "Gateways", - "configuration": { - "widgets": { - "94715984-ae74-76e4-20b7-2f956b01ed80": { - "isSystemType": true, - "bundleAlias": "entity_admin_widgets", - "typeAlias": "device_admin_table", - "type": "latest", - "title": "New widget", - "sizeX": 24, - "sizeY": 12, - "config": { - "timewindow": { - "realtime": { - "interval": 1000, - "timewindowMs": 86400000 - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, - "showTitle": true, - "backgroundColor": "rgb(255, 255, 255)", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "4px", - "settings": { - "enableSearch": true, - "displayPagination": true, - "defaultPageSize": 10, - "defaultSortOrder": "entityName", - "displayEntityName": true, - "displayEntityType": false, - "entitiesTitle": "List of gateways", - "enableSelectColumnDisplay": true, - "displayEntityLabel": false, - "entityNameColumnTitle": "Gateway Name" - }, - "title": "Devices gateway table", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400, - "padding": "5px 10px 5px 10px" - }, - "useDashboardTimewindow": false, - "showLegend": false, - "datasources": [ - { - "type": "entity", - "dataKeys": [ - { - "name": "active", - "type": "attribute", - "label": "Active", - "color": "#2196f3", - "settings": { - "columnWidth": "0px", - "useCellStyleFunction": true, - "useCellContentFunction": true, - "cellContentFunction": "value = '⬤';\nreturn value;", - "cellStyleFunction": "var color;\nif (value == 'false') {\n color = '#EB5757';\n} else {\n color = '#27AE60';\n}\nreturn {\n color: color,\n fontSize: '18px'\n};" - }, - "_hash": 0.3646047595211721 - }, - { - "name": "eventsSent", - "type": "timeseries", - "label": "Sent", - "color": "#4caf50", - "settings": { - "columnWidth": "0px", - "useCellStyleFunction": false, - "useCellContentFunction": false - }, - "_hash": 0.7235710720767985 - }, - { - "name": "eventsProduced", - "type": "timeseries", - "label": "Events", - "color": "#f44336", - "settings": { - "columnWidth": "0px", - "useCellStyleFunction": false, - "useCellContentFunction": false - }, - "_hash": 0.5085933386303254 - }, - { - "name": "LOGS", - "type": "timeseries", - "label": "Latest log", - "color": "#ffc107", - "settings": { - "columnWidth": "0px", - "useCellStyleFunction": false, - "useCellContentFunction": false - }, - "_hash": 0.3504240371585048, - "postFuncBody": "if(value) {\n return value.substring(0, 31) + \"...\";\n} else {\n return '';\n}" - }, - { - "name": "RemoteLoggingLevel", - "type": "attribute", - "label": "Log level", - "color": "#607d8b", - "settings": { - "columnWidth": "0px", - "useCellStyleFunction": false, - "useCellContentFunction": false - }, - "_hash": 0.9785994222542516 - } - ], - "entityAliasId": "3e0f533a-0db1-3292-184f-06e73535061a" - } - ], - "showTitleIcon": true, - "titleIcon": "list", - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "List device", - "widgetStyle": {}, - "displayTimewindow": true, - "actions": { - "headerButton": [ - { - "name": "Add device", - "icon": "add", - "type": "customPretty", - "customHtml": "
\n \n

Add device

\n \n \n
\n \n \n
\n
\n
\n \n Device name\n \n \n Device name is required.\n \n \n
\n \n Latitude\n \n \n \n Longitude\n \n \n
\n \n Label\n \n \n
\n
\n
\n \n \n \n
\n
\n", - "customCss": "", - "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\nopenAddDeviceDialog();\n\nfunction openAddDeviceDialog() {\n customDialog.customDialog(htmlTemplate, AddDeviceDialogController).subscribe();\n}\n\nfunction AddDeviceDialogController(instance) {\n let vm = instance;\n \n vm.addDeviceFormGroup = vm.fb.group({\n deviceName: ['', [vm.validators.required]],\n deviceLabel: [''],\n attributes: vm.fb.group({\n latitude: [null],\n longitude: [null]\n }) \n });\n \n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n \n vm.save = function() {\n vm.addDeviceFormGroup.markAsPristine();\n let device = {\n additionalInfo: {gateway: true},\n name: vm.addDeviceFormGroup.get('deviceName').value,\n type: 'gateway',\n label: vm.addDeviceFormGroup.get('deviceLabel').value\n };\n deviceService.saveDevice(device).subscribe(\n function (device) {\n saveAttributes(device.id).subscribe(\n function () {\n widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n }\n );\n };\n \n function saveAttributes(entityId) {\n let attributes = vm.addDeviceFormGroup.get('attributes').value;\n let attributesArray = [];\n for (let key in attributes) {\n attributesArray.push({key: key, value: attributes[key]});\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(entityId, \"SERVER_SCOPE\", attributesArray);\n } else {\n return widgetContext.rxjs.of([]);\n }\n }\n}\n", - "customResources": [], - "id": "70837a9d-c3de-a9a7-03c5-dccd14998758" - } - ], - "actionCellButton": [ - { - "id": "78845501-234e-a452-6819-82b5b776e99f", - "name": "Configuration", - "icon": "settings", - "type": "openDashboardState", - "targetDashboardStateId": "__entityname__config", - "openRightLayout": false, - "setEntityId": true - }, - { - "id": "f6ffdba8-e40f-2b8d-851b-f5ecaf18606b", - "name": "Graphs", - "icon": "show_chart", - "type": "openDashboardState", - "targetDashboardStateId": "__entityname_grafic", - "setEntityId": true - }, - { - "name": "Edit device", - "icon": "edit", - "type": "customPretty", - "customHtml": "
\n \n

Edit device

\n \n \n
\n \n \n
\n
\n
\n \n Device name\n \n \n Device name is required.\n \n \n
\n \n Latitude\n \n \n \n Longitude\n \n \n
\n \n Label\n \n \n
\n
\n
\n \n \n \n
\n
\n", - "customCss": "/*=======================================================================*/\n/*========== There are two examples: for edit and add entity ==========*/\n/*=======================================================================*/\n/*======================== Edit entity example ========================*/\n/*=======================================================================*/\n/*\n.edit-entity-form md-input-container {\n padding-right: 10px;\n}\n\n.edit-entity-form .boolean-value-input {\n padding-left: 5px;\n}\n\n.edit-entity-form .boolean-value-input .checkbox-label {\n margin-bottom: 8px;\n color: rgba(0,0,0,0.54);\n font-size: 12px;\n}\n\n.relations-list .header {\n padding-right: 5px;\n padding-bottom: 5px;\n padding-left: 5px;\n}\n\n.relations-list .header .cell {\n padding-right: 5px;\n padding-left: 5px;\n font-size: 12px;\n font-weight: 700;\n color: rgba(0, 0, 0, .54);\n white-space: nowrap;\n}\n\n.relations-list .body {\n padding-right: 5px;\n padding-bottom: 15px;\n padding-left: 5px;\n}\n\n.relations-list .body .row {\n padding-top: 5px;\n}\n\n.relations-list .body .cell {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.relations-list .body md-autocomplete-wrap md-input-container {\n height: 30px;\n}\n\n.relations-list .body .md-button {\n margin: 0;\n}\n\n.relations-list.old-relations tb-entity-select tb-entity-autocomplete button {\n display: none;\n} \n*/\n/*========================================================================*/\n/*========================= Add entity example =========================*/\n/*========================================================================*/\n/*\n.add-entity-form md-input-container {\n padding-right: 10px;\n}\n\n.add-entity-form .boolean-value-input {\n padding-left: 5px;\n}\n\n.add-entity-form .boolean-value-input .checkbox-label {\n margin-bottom: 8px;\n color: rgba(0,0,0,0.54);\n font-size: 12px;\n}\n\n.relations-list .header {\n padding-right: 5px;\n padding-bottom: 5px;\n padding-left: 5px;\n}\n\n.relations-list .header .cell {\n padding-right: 5px;\n padding-left: 5px;\n font-size: 12px;\n font-weight: 700;\n color: rgba(0, 0, 0, .54);\n white-space: nowrap;\n}\n\n.relations-list .body {\n padding-right: 5px;\n padding-bottom: 15px;\n padding-left: 5px;\n}\n\n.relations-list .body .row {\n padding-top: 5px;\n}\n\n.relations-list .body .cell {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.relations-list .body md-autocomplete-wrap md-input-container {\n height: 30px;\n}\n\n.relations-list .body .md-button {\n margin: 0;\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\nopenEditDeviceDialog();\n\nfunction openEditDeviceDialog() {\n customDialog.customDialog(htmlTemplate, EditDeviceDialogController).subscribe();\n}\n\nfunction EditDeviceDialogController(instance) {\n let vm = instance;\n \n vm.device = null;\n vm.attributes = {};\n \n vm.editDeviceFormGroup = vm.fb.group({\n deviceName: ['', [vm.validators.required]],\n deviceLabel: [''],\n attributes: vm.fb.group({\n latitude: [null],\n longitude: [null]\n }) \n });\n \n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n \n vm.save = function() {\n vm.editDeviceFormGroup.markAsPristine();\n vm.device.name = vm.editDeviceFormGroup.get('deviceName').value;\n vm.device.label = vm.editDeviceFormGroup.get('deviceLabel').value;\n deviceService.saveDevice(vm.device).subscribe(\n function () {\n saveAttributes().subscribe(\n function () {\n widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n }\n );\n };\n \n getEntityInfo();\n \n function getEntityInfo() {\n deviceService.getDevice(entityId.id).subscribe(\n function (device) {\n attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE',\n ['latitude', 'longitude']).subscribe(\n function (attributes) {\n for (let i = 0; i < attributes.length; i++) {\n vm.attributes[attributes[i].key] = attributes[i].value; \n }\n vm.device = device;\n vm.editDeviceFormGroup.patchValue(\n {\n deviceName: vm.device.name,\n deviceLabel: vm.device.label,\n attributes: {\n latitude: vm.attributes.latitude,\n longitude: vm.attributes.longitude\n }\n }, {emitEvent: false}\n );\n } \n );\n }\n ); \n }\n \n function saveAttributes() {\n let attributes = vm.editDeviceFormGroup.get('attributes').value;\n let attributesArray = [];\n for (let key in attributes) {\n attributesArray.push({key: key, value: attributes[key]});\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(entityId, 'SERVER_SCOPE', attributesArray);\n } else {\n return widgetContext.rxjs.of([]);\n }\n }\n}\n", - "customResources": [], - "id": "242671f3-76c6-6982-7acc-6f12addf0ccc" - }, - { - "name": "Delete device", - "icon": "delete", - "type": "custom", - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet dialogs = $injector.get(widgetContext.servicesMap.get('dialogs'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenDeleteDeviceDialog();\n\nfunction openDeleteDeviceDialog() {\n let title = \"Are you sure you want to delete the device \" + entityName + \"?\";\n let content = \"Be careful, after the confirmation, the device and all related data will become unrecoverable!\";\n dialogs.confirm(title, content, 'Cancel', 'Delete').subscribe(\n function (result) {\n if (result) {\n deleteDevice();\n }\n }\n );\n}\n\nfunction deleteDevice() {\n deviceService.deleteDevice(entityId.id).subscribe(\n function () {\n widgetContext.updateAliases();\n }\n );\n}\n", - "id": "862ec2b7-fbcf-376e-f85f-b77c07f36efa" - } - ], - "rowClick": [ - { - "id": "ad5fc7e1-5e60-e056-6940-a75a383466a1", - "name": "to_entityname__config", - "icon": "more_horiz", - "type": "openDashboardState", - "targetDashboardStateId": "__entityname__config", - "setEntityId": true, - "stateEntityParamName": "" - } - ] - } - }, - "id": "94715984-ae74-76e4-20b7-2f956b01ed80" - }, - "eadabbc7-519e-76fc-ba10-b3fe8c18da10": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "timeseries_table", - "type": "timeseries", - "title": "New widget", - "sizeX": 14, - "sizeY": 13, - "config": { - "datasources": [ - { - "type": "entity", - "dataKeys": [ - { - "name": "LOGS", - "type": "timeseries", - "label": "LOGS", - "color": "#2196f3", - "settings": { - "useCellStyleFunction": false, - "useCellContentFunction": false - }, - "_hash": 0.3496649158709739, - "postFuncBody": "return value.replace(/ - (.*) - \\[/gi, ' - $1 - [');" - } - ], - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c" - } - ], - "timewindow": { - "realtime": { - "interval": 1000, - "timewindowMs": 2592000000 - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, - "showTitle": true, - "backgroundColor": "rgb(255, 255, 255)", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "showTimestamp": true, - "displayPagination": true, - "defaultPageSize": 10 - }, - "title": "Debug events (logs)", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "useDashboardTimewindow": false, - "showLegend": false, - "widgetStyle": {}, - "actions": {}, - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "displayTimewindow": true - }, - "id": "eadabbc7-519e-76fc-ba10-b3fe8c18da10" - }, - "f928afc4-30d1-8d0c-e3cf-777f9f9d1155": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "basic_timeseries", - "type": "timeseries", - "title": "New widget", - "sizeX": 17, - "sizeY": 4, - "config": { - "datasources": [ - { - "type": "entity", - "dataKeys": [ - { - "name": "opcuaEventsProduced", - "type": "timeseries", - "label": "opcuaEventsProduced", - "color": "#2196f3", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "tooltipValueFormatter": "", - "showSeparateAxis": false, - "axisTitle": "", - "axisPosition": "left", - "axisTicksFormatter": "", - "comparisonSettings": { - "showValuesForComparison": true, - "comparisonValuesLabel": "", - "color": "" - } - }, - "_hash": 0.1477920581839779 - }, - { - "name": "opcuaEventsSent", - "type": "timeseries", - "label": "opcuaEventsSent", - "color": "#4caf50", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "tooltipValueFormatter": "", - "showSeparateAxis": false, - "axisTitle": "", - "axisPosition": "left", - "axisTicksFormatter": "", - "comparisonSettings": { - "showValuesForComparison": true, - "comparisonValuesLabel": "", - "color": "" - } - }, - "_hash": 0.6500957113784758 - } - ], - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c" - } - ], - "timewindow": { - "realtime": { - "interval": 1000, - "timewindowMs": 120000 - }, - "aggregation": { - "type": "NONE", - "limit": 25000 - }, - "hideInterval": false, - "hideAggregation": false - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "Real time information", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "mobileHeight": null, - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "widgetStyle": {}, - "useDashboardTimewindow": false, - "displayTimewindow": true, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "right", - "showMin": true, - "showMax": true, - "showAvg": true, - "showTotal": true - }, - "actions": {} - }, - "id": "f928afc4-30d1-8d0c-e3cf-777f9f9d1155" - }, - "2a95b473-042d-59d0-2da2-40d0cccb6c8a": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "timeseries_table", - "type": "timeseries", - "title": "New widget", - "sizeX": 7, - "sizeY": 7, - "config": { - "datasources": [ - { - "type": "entity", - "dataKeys": [ - { - "name": "eventsSent", - "type": "timeseries", - "label": "Events", - "color": "#2196f3", - "settings": { - "useCellStyleFunction": false, - "useCellContentFunction": false - }, - "_hash": 0.8156044798125357 - }, - { - "name": "eventsProduced", - "type": "timeseries", - "label": "Produced", - "color": "#4caf50", - "settings": { - "useCellStyleFunction": false, - "useCellContentFunction": false - }, - "_hash": 0.6538259344015449 - } - ], - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c" - } - ], - "timewindow": { - "realtime": { - "interval": 1000, - "timewindowMs": 604800000 - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, - "showTitle": true, - "backgroundColor": "rgb(255, 255, 255)", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "showTimestamp": true, - "displayPagination": true, - "defaultPageSize": 6, - "hideEmptyLines": true - }, - "title": "Total Messages", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "useDashboardTimewindow": false, - "showLegend": false, - "widgetStyle": {}, - "actions": {}, - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "displayTimewindow": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "showMin": false, - "showMax": false, - "showAvg": true, - "showTotal": false - } - }, - "id": "2a95b473-042d-59d0-2da2-40d0cccb6c8a" - }, - "aaa69366-aacc-9028-65aa-645c0f8533ec": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "basic_timeseries", - "type": "timeseries", - "title": "New widget", - "sizeX": 17, - "sizeY": 4, - "config": { - "datasources": [ - { - "type": "entity", - "dataKeys": [ - { - "name": "eventsSent", - "type": "timeseries", - "label": "eventsSent", - "color": "#2196f3", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "tooltipValueFormatter": "", - "showSeparateAxis": false, - "axisTitle": "", - "axisPosition": "left", - "axisTicksFormatter": "", - "comparisonSettings": { - "showValuesForComparison": true, - "comparisonValuesLabel": "", - "color": "" - } - }, - "_hash": 0.41414001784591314 - }, - { - "name": "eventsProduced", - "type": "timeseries", - "label": "eventsProduced", - "color": "#4caf50", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "tooltipValueFormatter": "", - "showSeparateAxis": false, - "axisTitle": "", - "axisPosition": "left", - "axisTicksFormatter": "", - "comparisonSettings": { - "showValuesForComparison": true, - "comparisonValuesLabel": "", - "color": "" - } - }, - "_hash": 0.7819101846284422 - } - ], - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c" - } - ], - "timewindow": { - "realtime": { - "timewindowMs": 60000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "History information", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "mobileHeight": null, - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "widgetStyle": {}, - "useDashboardTimewindow": true, - "displayTimewindow": true, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "right", - "showMin": true, - "showMax": true, - "showAvg": true, - "showTotal": true - }, - "actions": {} - }, - "id": "aaa69366-aacc-9028-65aa-645c0f8533ec" - }, - "ce5c7d01-a3ef-5cf0-4578-8505135c23a0": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "basic_timeseries", - "type": "timeseries", - "title": "New widget", - "sizeX": 17, - "sizeY": 4, - "config": { - "datasources": [ - { - "type": "entity", - "dataKeys": [ - { - "name": "bleEventsProduced", - "type": "timeseries", - "label": "bleEventsProduced", - "color": "#2196f3", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "tooltipValueFormatter": "", - "showSeparateAxis": false, - "axisTitle": "", - "axisPosition": "left", - "axisTicksFormatter": "", - "comparisonSettings": { - "showValuesForComparison": true, - "comparisonValuesLabel": "", - "color": "" - } - }, - "_hash": 0.5625165504526104 - }, - { - "name": "bleEventsSent", - "type": "timeseries", - "label": "bleEventsSent", - "color": "#4caf50", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "tooltipValueFormatter": "", - "showSeparateAxis": false, - "axisTitle": "", - "axisPosition": "left", - "axisTicksFormatter": "", - "comparisonSettings": { - "showValuesForComparison": true, - "comparisonValuesLabel": "", - "color": "" - } - }, - "_hash": 0.6817950080745288 - } - ], - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c" - } - ], - "timewindow": { - "realtime": { - "interval": 5000, - "timewindowMs": 120000 - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "Real time information", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "mobileHeight": null, - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "widgetStyle": {}, - "useDashboardTimewindow": false, - "displayTimewindow": true, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "right", - "showMin": true, - "showMax": true, - "showAvg": true, - "showTotal": true - }, - "actions": {} - }, - "id": "ce5c7d01-a3ef-5cf0-4578-8505135c23a0" - }, - "466f046d-6005-a168-b107-60fcb2469cd5": { - "isSystemType": true, - "bundleAlias": "gateway_widgets", - "typeAlias": "attributes_card", - "type": "latest", - "title": "New widget", - "sizeX": 7, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "dataKeys": [], - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c" - } - ], - "timewindow": { - "realtime": { - "timewindowMs": 60000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "eventsTitle": "Gateway Events Form", - "eventsReg": [ - "EventsProduced", - "EventsSent" - ] - }, - "title": "Gateway events", - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": true, - "enableFullscreen": true, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "displayTimewindow": true, - "showLegend": false, - "actions": {} - }, - "id": "466f046d-6005-a168-b107-60fcb2469cd5" - }, - "8fc32225-164f-3258-73f7-e6b6d959cf0b": { - "isSystemType": true, - "bundleAlias": "gateway_widgets", - "typeAlias": "config_form_latest", - "type": "latest", - "title": "New widget", - "sizeX": 10, - "sizeY": 9, - "config": { - "datasources": [ - { - "type": "entity", - "dataKeys": [], - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c" - } - ], - "timewindow": { - "realtime": { - "timewindowMs": 60000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "gatewayTitle": "Gateway configuration (Single device)", - "readOnly": false - }, - "title": "New Gateway configuration (Single device)", - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": true, - "enableFullscreen": true, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "displayTimewindow": true, - "showLegend": false, - "actions": {} - }, - "id": "8fc32225-164f-3258-73f7-e6b6d959cf0b" - }, - "063fc179-c9fd-f952-e714-f24e9c43c05c": { - "isSystemType": true, - "bundleAlias": "control_widgets", - "typeAlias": "rpcbutton", - "type": "rpc", - "title": "New widget", - "sizeX": 4, - "sizeY": 2, - "config": { - "targetDeviceAliases": [], - "showTitle": false, - "backgroundColor": "#e6e7e8", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "0px", - "settings": { - "requestTimeout": 5000, - "oneWayElseTwoWay": true, - "styleButton": { - "isRaised": true, - "isPrimary": false - }, - "methodParams": "{}", - "methodName": "gateway_reboot", - "buttonText": "GATEWAY REBOOT" - }, - "title": "New RPC Button", - "dropShadow": true, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "showLegend": false, - "actions": {}, - "datasources": [], - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "displayTimewindow": true, - "targetDeviceAliasIds": [ - "b2487e75-2fa4-f211-142c-434dfd50c70c" - ] - }, - "id": "063fc179-c9fd-f952-e714-f24e9c43c05c" - }, - "3c2134cc-27a0-93e1-dbe1-2fa7c1ce16b7": { - "isSystemType": true, - "bundleAlias": "control_widgets", - "typeAlias": "rpcbutton", - "type": "rpc", - "title": "New widget", - "sizeX": 4, - "sizeY": 2, - "config": { - "targetDeviceAliases": [], - "showTitle": false, - "backgroundColor": "#e6e7e8", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "0px", - "settings": { - "requestTimeout": 5000, - "oneWayElseTwoWay": true, - "styleButton": { - "isRaised": true, - "isPrimary": false - }, - "methodName": "gateway_restart", - "methodParams": "{}", - "buttonText": "GATEWAY RESTART" - }, - "title": "New RPC Button", - "dropShadow": true, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "showLegend": false, - "actions": {}, - "datasources": [], - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "displayTimewindow": true, - "targetDeviceAliasIds": [ - "b2487e75-2fa4-f211-142c-434dfd50c70c" - ] - }, - "id": "3c2134cc-27a0-93e1-dbe1-2fa7c1ce16b7" - }, - "6770b6ba-eff8-df05-75f8-c1f9326d4842": { - "isSystemType": true, - "bundleAlias": "input_widgets", - "typeAlias": "markers_placement_openstreetmap", - "type": "latest", - "title": "New widget", - "sizeX": 6, - "sizeY": 4, - "config": { - "datasources": [ - { - "type": "entity", - "dataKeys": [ - { - "name": "latitude", - "type": "attribute", - "label": "latitude", - "color": "#2196f3", - "settings": {}, - "_hash": 0.9743324774725604 - }, - { - "name": "longitude", - "type": "attribute", - "label": "longitude", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5530093635101525 - } - ], - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c" - } - ], - "timewindow": { - "realtime": { - "timewindowMs": 60000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "fitMapBounds": true, - "latKeyName": "latitude", - "lngKeyName": "longitude", - "showLabel": true, - "label": "${entityName}", - "tooltipPattern": "${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}

Delete", - "markerImageSize": 34, - "useColorFunction": false, - "markerImages": [], - "useMarkerImageFunction": false, - "color": "#fe7569", - "mapProvider": "OpenStreetMap.Mapnik", - "showTooltip": true, - "autocloseTooltip": true, - "defaultCenterPosition": [ - 0, - 0 - ], - "customProviderTileUrl": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", - "showTooltipAction": "click", - "polygonKeyName": "coordinates", - "polygonOpacity": 0.5, - "polygonStrokeOpacity": 1, - "polygonStrokeWeight": 1, - "zoomOnClick": true, - "showCoverageOnHover": true, - "animate": true, - "maxClusterRadius": 80, - "removeOutsideVisibleBounds": true, - "defaultZoomLevel": 5 - }, - "title": "Gateway Location", - "dropShadow": true, - "enableFullscreen": false, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "showLegend": false, - "widgetStyle": {}, - "actions": { - "tooltipAction": [ - { - "id": "54c293c4-9ca6-e34f-dc6a-0271944c1c66", - "name": "delete", - "icon": "more_horiz", - "type": "custom", - "customFunction": "var $rootScope = widgetContext.$scope.$injector.get('$rootScope');\nvar entityDatasource = widgetContext.map.subscription.datasources.filter(\n function(entity) {\n return entity.entityId === entityId.id\n });\n\nwidgetContext.map.saveMarkerLocation(entityDatasource[0],\n widgetContext.map.locations[0], {\n \"lat\": null,\n \"lng\": null\n }).then(function succes() {\n $rootScope.$broadcast('widgetForceReInit');\n });" - } - ] - }, - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "displayTimewindow": true - }, - "id": "6770b6ba-eff8-df05-75f8-c1f9326d4842" - } - }, - "states": { - "main_gateway": { - "name": "Gateways", - "root": true, - "layouts": { - "main": { - "widgets": { - "94715984-ae74-76e4-20b7-2f956b01ed80": { - "sizeX": 24, - "sizeY": 12, - "row": 0, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70, - "margin": 10 - } - } - } - }, - "__entityname__config": { - "name": "${entityName} Configuration", - "root": false, - "layouts": { - "main": { - "widgets": { - "eadabbc7-519e-76fc-ba10-b3fe8c18da10": { - "sizeX": 14, - "sizeY": 13, - "row": 0, - "col": 10 - }, - "8fc32225-164f-3258-73f7-e6b6d959cf0b": { - "sizeX": 10, - "sizeY": 9, - "row": 0, - "col": 0 - }, - "063fc179-c9fd-f952-e714-f24e9c43c05c": { - "sizeX": 4, - "sizeY": 2, - "row": 9, - "col": 0 - }, - "3c2134cc-27a0-93e1-dbe1-2fa7c1ce16b7": { - "sizeX": 4, - "sizeY": 2, - "row": 11, - "col": 0 - }, - "6770b6ba-eff8-df05-75f8-c1f9326d4842": { - "sizeX": 6, - "sizeY": 4, - "row": 9, - "col": 4 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70, - "margin": 10 - } - } - } - }, - "__entityname_grafic": { - "name": "${entityName} Details", - "root": false, - "layouts": { - "main": { - "widgets": { - "f928afc4-30d1-8d0c-e3cf-777f9f9d1155": { - "sizeX": 17, - "sizeY": 4, - "mobileHeight": null, - "row": 4, - "col": 7 - }, - "2a95b473-042d-59d0-2da2-40d0cccb6c8a": { - "sizeX": 7, - "sizeY": 7, - "row": 5, - "col": 0 - }, - "aaa69366-aacc-9028-65aa-645c0f8533ec": { - "sizeX": 17, - "sizeY": 4, - "mobileHeight": null, - "row": 0, - "col": 7 - }, - "ce5c7d01-a3ef-5cf0-4578-8505135c23a0": { - "sizeX": 17, - "sizeY": 4, - "mobileHeight": null, - "row": 8, - "col": 7 - }, - "466f046d-6005-a168-b107-60fcb2469cd5": { - "sizeX": 7, - "sizeY": 5, - "row": 0, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "backgroundSizeMode": "auto 100%", - "autoFillHeight": true, - "mobileAutoFillHeight": true, - "mobileRowHeight": 70, - "margin": 10 - } - } - } - } - }, - "entityAliases": { - "3e0f533a-0db1-3292-184f-06e73535061a": { - "id": "3e0f533a-0db1-3292-184f-06e73535061a", - "alias": "Gateways", - "filter": { - "type": "deviceType", - "resolveMultiple": true, - "deviceType": "gateway", - "deviceNameFilter": "" - } - }, - "b2487e75-2fa4-f211-142c-434dfd50c70c": { - "id": "b2487e75-2fa4-f211-142c-434dfd50c70c", - "alias": "Current Gateway", - "filter": { - "type": "stateEntity", - "resolveMultiple": false, - "stateEntityParamName": "", - "defaultStateEntity": null - } - } - }, - "timewindow": { - "realtime": { - "interval": 1000, - "timewindowMs": 86400000 - }, - "aggregation": { - "type": "NONE", - "limit": 25000 - }, - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false - }, - "settings": { - "stateControllerId": "entity", - "showTitle": true, - "showDashboardsSelect": true, - "showEntitiesSelect": true, - "showDashboardTimewindow": true, - "showDashboardExport": true, - "toolbarAlwaysOpen": true, - "titleColor": "rgba(0,0,0,0.870588)" - } - }, - "name": "Gateways" -} diff --git a/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json b/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json index b8231ab880..c84faa359e 100644 --- a/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json +++ b/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json @@ -1,13 +1,12 @@ { "title": "Rule Engine Statistics", + "image": null, + "mobileHide": false, + "mobileOrder": null, "configuration": { "widgets": { "81987f19-3eac-e4ce-b790-d96e9b54d9a0": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "basic_timeseries", "type": "timeseries", - "title": "New widget", "sizeX": 12, "sizeY": 7, "config": { @@ -151,6 +150,15 @@ "xaxisSecond": { "axisPosition": "top", "showLabels": true + }, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "showMin": true, + "showMax": true, + "showAvg": false, + "showTotal": true } }, "title": "Queue Stats", @@ -169,25 +177,13 @@ "widgetStyle": {}, "useDashboardTimewindow": false, "displayTimewindow": true, - "showLegend": true, - "actions": {}, - "legendConfig": { - "direction": "column", - "position": "bottom", - "showMin": true, - "showMax": true, - "showAvg": false, - "showTotal": true - } + "actions": {} }, - "id": "81987f19-3eac-e4ce-b790-d96e9b54d9a0" + "id": "81987f19-3eac-e4ce-b790-d96e9b54d9a0", + "typeFullFqn": "system.charts.basic_timeseries" }, "5eb79712-5c24-3060-7e4f-6af36b8f842d": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "timeseries_table", "type": "timeseries", - "title": "New widget", "sizeX": 24, "sizeY": 5, "config": { @@ -272,14 +268,11 @@ "titleTooltip": "", "displayTimewindow": true }, - "id": "5eb79712-5c24-3060-7e4f-6af36b8f842d" + "id": "5eb79712-5c24-3060-7e4f-6af36b8f842d", + "typeFullFqn": "system.cards.timeseries_table" }, "ad3f1417-87a8-750e-fc67-49a2de1466d4": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "basic_timeseries", "type": "timeseries", - "title": "New widget", "sizeX": 12, "sizeY": 7, "config": { @@ -393,6 +386,15 @@ "xaxisSecond": { "axisPosition": "top", "showLabels": true + }, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "showMin": true, + "showMax": true, + "showAvg": false, + "showTotal": true } }, "title": "Processing Failures and Timeouts", @@ -411,18 +413,10 @@ "widgetStyle": {}, "useDashboardTimewindow": false, "displayTimewindow": true, - "showLegend": true, - "actions": {}, - "legendConfig": { - "direction": "column", - "position": "bottom", - "showMin": true, - "showMax": true, - "showAvg": false, - "showTotal": true - } + "actions": {} }, - "id": "ad3f1417-87a8-750e-fc67-49a2de1466d4" + "id": "ad3f1417-87a8-750e-fc67-49a2de1466d4", + "typeFullFqn": "system.charts.basic_timeseries" } }, "states": { @@ -457,14 +451,12 @@ "backgroundColor": "#eeeeee", "color": "rgba(0,0,0,0.870588)", "columns": 24, - "margins": [ - 10, - 10 - ], "backgroundSizeMode": "100%", "autoFillHeight": true, "mobileAutoFillHeight": false, - "mobileRowHeight": 70 + "mobileRowHeight": 70, + "margin": 10, + "outerMargin": true } } } @@ -477,8 +469,10 @@ "filter": { "type": "assetType", "resolveMultiple": true, - "assetType": "TbServiceQueue", - "assetNameFilter": "" + "assetNameFilter": "", + "assetTypes": [ + "TbServiceQueue" + ] } } }, @@ -514,7 +508,9 @@ "showDashboardTimewindow": true, "showDashboardExport": true, "toolbarAlwaysOpen": true - } + }, + "filters": {} }, + "externalId": null, "name": "Rule Engine Statistics" } \ No newline at end of file diff --git a/application/src/main/data/json/demo/dashboards/software.json b/application/src/main/data/json/demo/dashboards/software.json index e9d7e9bbbd..eea81e5368 100644 --- a/application/src/main/data/json/demo/dashboards/software.json +++ b/application/src/main/data/json/demo/dashboards/software.json @@ -1,24 +1,34 @@ { "title": "Software", "image": null, + "mobileHide": false, + "mobileOrder": null, "configuration": { "description": "", "widgets": { "cd03188e-cd9d-9601-fd57-da4cb95fc016": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "entities_table", "type": "latest", - "title": "New widget", - "image": null, - "description": null, "sizeX": 7.5, "sizeY": 6.5, "config": { "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, "interval": 1000, - "timewindowMs": 86400000 + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085343279, + "endTimeMs": 1694171743279 + }, + "quickInterval": "CURRENT_DAY" }, "aggregation": { "type": "NONE", @@ -299,16 +309,11 @@ }, "row": 0, "col": 0, - "id": "cd03188e-cd9d-9601-fd57-da4cb95fc016" + "id": "cd03188e-cd9d-9601-fd57-da4cb95fc016", + "typeFullFqn": "system.cards.entities_table" }, "100b756c-0082-6505-3ae1-3603e6deea48": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "timeseries_table", "type": "timeseries", - "title": "New widget", - "image": null, - "description": null, "sizeX": 8, "sizeY": 6.5, "config": { @@ -461,14 +466,11 @@ }, "row": 0, "col": 0, - "id": "100b756c-0082-6505-3ae1-3603e6deea48" + "id": "100b756c-0082-6505-3ae1-3603e6deea48", + "typeFullFqn": "system.cards.timeseries_table" }, "17543c57-af4a-2c1e-bf12-53a7b46791e6": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "html_value_card", "type": "latest", - "title": "New widget", "sizeX": 8, "sizeY": 3, "config": { @@ -496,8 +498,27 @@ } ], "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { - "timewindowMs": 60000 + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085343280, + "endTimeMs": 1694171743280 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 } }, "showTitle": false, @@ -545,14 +566,11 @@ "enableDataExport": false, "displayTimewindow": true }, - "id": "17543c57-af4a-2c1e-bf12-53a7b46791e6" + "id": "17543c57-af4a-2c1e-bf12-53a7b46791e6", + "typeFullFqn": "system.cards.html_value_card" }, "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "html_value_card", "type": "latest", - "title": "New widget", "sizeX": 8, "sizeY": 3, "config": { @@ -580,8 +598,27 @@ } ], "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { - "timewindowMs": 60000 + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085343280, + "endTimeMs": 1694171743280 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 } }, "showTitle": false, @@ -629,14 +666,11 @@ "enableDataExport": false, "displayTimewindow": true }, - "id": "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6" + "id": "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6", + "typeFullFqn": "system.cards.html_value_card" }, "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "html_value_card", "type": "latest", - "title": "New widget", "sizeX": 8, "sizeY": 3, "config": { @@ -664,8 +698,27 @@ } ], "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { - "timewindowMs": 60000 + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085343280, + "endTimeMs": 1694171743280 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 } }, "showTitle": false, @@ -713,14 +766,11 @@ "enableDataExport": false, "displayTimewindow": true }, - "id": "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81" + "id": "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81", + "typeFullFqn": "system.cards.html_value_card" }, "77b10144-b904-edd5-8c7c-8fb75616c6d8": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "html_value_card", "type": "latest", - "title": "New widget", "sizeX": 8, "sizeY": 3, "config": { @@ -748,8 +798,27 @@ } ], "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { - "timewindowMs": 60000 + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085343280, + "endTimeMs": 1694171743280 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 } }, "showTitle": false, @@ -797,23 +866,32 @@ "enableDataExport": false, "displayTimewindow": true }, - "id": "77b10144-b904-edd5-8c7c-8fb75616c6d8" + "id": "77b10144-b904-edd5-8c7c-8fb75616c6d8", + "typeFullFqn": "system.cards.html_value_card" }, "21be08bb-ec90-f760-ad6f-e7678f12c401": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "entities_table", "type": "latest", - "title": "New widget", - "image": null, - "description": null, "sizeX": 7.5, "sizeY": 6.5, "config": { "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { + "realtimeType": 1, "interval": 1000, - "timewindowMs": 86400000 + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085343280, + "endTimeMs": 1694171743280 + }, + "quickInterval": "CURRENT_DAY" }, "aggregation": { "type": "NONE", @@ -1094,23 +1172,32 @@ }, "row": 0, "col": 0, - "id": "21be08bb-ec90-f760-ad6f-e7678f12c401" + "id": "21be08bb-ec90-f760-ad6f-e7678f12c401", + "typeFullFqn": "system.cards.entities_table" }, "e8280043-d3dc-7acb-c2ff-a4522972ff91": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "entities_table", "type": "latest", - "title": "New widget", - "image": null, - "description": null, "sizeX": 7.5, "sizeY": 6.5, "config": { "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, "interval": 1000, - "timewindowMs": 86400000 + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085343280, + "endTimeMs": 1694171743280 + }, + "quickInterval": "CURRENT_DAY" }, "aggregation": { "type": "NONE", @@ -1391,23 +1478,32 @@ }, "row": 0, "col": 0, - "id": "e8280043-d3dc-7acb-c2ff-a4522972ff91" + "id": "e8280043-d3dc-7acb-c2ff-a4522972ff91", + "typeFullFqn": "system.cards.entities_table" }, "3624013b-378c-f110-5eba-ae95c25a4dcc": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "entities_table", "type": "latest", - "title": "New widget", - "image": null, - "description": null, "sizeX": 7.5, "sizeY": 6.5, "config": { "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, "interval": 1000, - "timewindowMs": 86400000 + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085343280, + "endTimeMs": 1694171743280 + }, + "quickInterval": "CURRENT_DAY" }, "aggregation": { "type": "NONE", @@ -1688,23 +1784,32 @@ }, "row": 0, "col": 0, - "id": "3624013b-378c-f110-5eba-ae95c25a4dcc" + "id": "3624013b-378c-f110-5eba-ae95c25a4dcc", + "typeFullFqn": "system.cards.entities_table" }, "d2d13e0d-4e71-889f-9343-ad2f0af9f176": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "entities_table", "type": "latest", - "title": "New widget", - "image": null, - "description": null, "sizeX": 7.5, "sizeY": 6.5, "config": { "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { + "realtimeType": 1, "interval": 1000, - "timewindowMs": 86400000 + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085343280, + "endTimeMs": 1694171743280 + }, + "quickInterval": "CURRENT_DAY" }, "aggregation": { "type": "NONE", @@ -1985,7 +2090,8 @@ }, "row": 0, "col": 0, - "id": "d2d13e0d-4e71-889f-9343-ad2f0af9f176" + "id": "d2d13e0d-4e71-889f-9343-ad2f0af9f176", + "typeFullFqn": "system.cards.entities_table" } }, "states": { @@ -2035,7 +2141,8 @@ "autoFillHeight": true, "backgroundImageUrl": null, "mobileAutoFillHeight": true, - "mobileRowHeight": 70 + "mobileRowHeight": 70, + "outerMargin": true } } } @@ -2062,7 +2169,8 @@ "autoFillHeight": true, "backgroundImageUrl": null, "mobileAutoFillHeight": false, - "mobileRowHeight": 70 + "mobileRowHeight": 70, + "outerMargin": true } } } @@ -2089,7 +2197,8 @@ "autoFillHeight": true, "backgroundImageUrl": null, "mobileAutoFillHeight": false, - "mobileRowHeight": 70 + "mobileRowHeight": 70, + "outerMargin": true } } } @@ -2116,7 +2225,8 @@ "autoFillHeight": true, "backgroundImageUrl": null, "mobileAutoFillHeight": false, - "mobileRowHeight": 70 + "mobileRowHeight": 70, + "outerMargin": true } } } @@ -2143,7 +2253,8 @@ "autoFillHeight": true, "backgroundImageUrl": null, "mobileAutoFillHeight": false, - "mobileRowHeight": 70 + "mobileRowHeight": 70, + "outerMargin": true } } } @@ -2170,7 +2281,8 @@ "autoFillHeight": true, "backgroundImageUrl": null, "mobileAutoFillHeight": false, - "mobileRowHeight": 70 + "mobileRowHeight": 70, + "outerMargin": true } } } @@ -2488,5 +2600,6 @@ "showUpdateDashboardImage": false } }, + "externalId": null, "name": "Software" } \ No newline at end of file diff --git a/application/src/main/data/json/demo/dashboards/thermostats.json b/application/src/main/data/json/demo/dashboards/thermostats.json index c014d8875a..967c1cb253 100644 --- a/application/src/main/data/json/demo/dashboards/thermostats.json +++ b/application/src/main/data/json/demo/dashboards/thermostats.json @@ -1,20 +1,33 @@ { "title": "Thermostats", + "image": null, + "mobileHide": false, + "mobileOrder": null, "configuration": { "widgets": { "f33c746c-0dfc-c212-395b-b448c8a17209": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "entities_table", "type": "latest", - "title": "New widget", "sizeX": 11, "sizeY": 11, "config": { "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { + "realtimeType": 1, "interval": 1000, - "timewindowMs": 86400000 + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085177686, + "endTimeMs": 1694171577686 + }, + "quickInterval": "CURRENT_DAY" }, "aggregation": { "type": "NONE", @@ -184,14 +197,11 @@ "rowClick": [] } }, - "id": "f33c746c-0dfc-c212-395b-b448c8a17209" + "id": "f33c746c-0dfc-c212-395b-b448c8a17209", + "typeFullFqn": "system.cards.entities_table" }, "7943196b-eedb-d422-f9c3-b32d379ad172": { - "isSystemType": true, - "bundleAlias": "alarm_widgets", - "typeAlias": "alarms_table", "type": "alarm", - "title": "New widget", "sizeX": 13, "sizeY": 5, "config": { @@ -230,7 +240,7 @@ "titleStyle": { "fontSize": "16px", "fontWeight": 400, - "padding": "5px 10px 5px 10px" + "padding": "5px 10px 5px 2px" }, "useDashboardTimewindow": false, "showLegend": false, @@ -290,7 +300,6 @@ } ] }, - "alarmSearchStatus": "ANY", "alarmsPollingInterval": 5, "showTitleIcon": false, "titleIcon": null, @@ -302,16 +311,22 @@ "actions": {}, "datasources": [], "alarmsMaxCountLoad": 0, - "alarmsFetchSize": 100 + "alarmsFetchSize": 100, + "alarmFilterConfig": { + "statusList": [], + "severityList": [], + "typeList": [], + "searchPropagatedAlarms": true + }, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" }, - "id": "7943196b-eedb-d422-f9c3-b32d379ad172" + "id": "7943196b-eedb-d422-f9c3-b32d379ad172", + "typeFullFqn": "system.alarm_widgets.alarms_table" }, "14a19183-f0b2-d6be-0f62-9863f0a51111": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "basic_timeseries", "type": "timeseries", - "title": "New widget", "sizeX": 18, "sizeY": 6, "config": { @@ -395,7 +410,16 @@ "axisPosition": "top", "showLabels": true }, - "smoothLines": true + "smoothLines": true, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "showMin": true, + "showMax": true, + "showAvg": true, + "showTotal": false + } }, "title": "Temperature", "dropShadow": true, @@ -413,25 +437,13 @@ "widgetStyle": {}, "useDashboardTimewindow": false, "displayTimewindow": true, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "showMin": true, - "showMax": true, - "showAvg": true, - "showTotal": false - }, "actions": {} }, - "id": "14a19183-f0b2-d6be-0f62-9863f0a51111" + "id": "14a19183-f0b2-d6be-0f62-9863f0a51111", + "typeFullFqn": "system.charts.basic_timeseries" }, "07f49fd5-a73b-d74c-c220-362c20af81f4": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "basic_timeseries", "type": "timeseries", - "title": "New widget", "sizeX": 18, "sizeY": 6, "config": { @@ -515,7 +527,16 @@ "axisPosition": "top", "showLabels": true }, - "smoothLines": true + "smoothLines": true, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "showMin": true, + "showMax": true, + "showAvg": true, + "showTotal": false + } }, "title": "Humidity", "dropShadow": true, @@ -533,25 +554,13 @@ "widgetStyle": {}, "useDashboardTimewindow": false, "displayTimewindow": true, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "showMin": true, - "showMax": true, - "showAvg": true, - "showTotal": false - }, "actions": {} }, - "id": "07f49fd5-a73b-d74c-c220-362c20af81f4" + "id": "07f49fd5-a73b-d74c-c220-362c20af81f4", + "typeFullFqn": "system.charts.basic_timeseries" }, "c4631f94-2db3-523b-4d09-2a1a0a75d93f": { - "isSystemType": true, - "bundleAlias": "input_widgets", - "typeAlias": "update_multiple_attributes", "type": "latest", - "title": "New widget", "sizeX": 6, "sizeY": 6, "config": { @@ -626,8 +635,27 @@ } ], "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { - "timewindowMs": 60000 + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085177686, + "endTimeMs": 1694171577686 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 } }, "showTitle": true, @@ -661,14 +689,11 @@ "titleTooltip": "", "displayTimewindow": true }, - "id": "c4631f94-2db3-523b-4d09-2a1a0a75d93f" + "id": "c4631f94-2db3-523b-4d09-2a1a0a75d93f", + "typeFullFqn": "system.input_widgets.update_multiple_attributes" }, "3da9a9a1-0b9a-2e1f-0dcb-0ff34a695abb": { - "isSystemType": true, - "bundleAlias": "maps_v2", - "typeAlias": "openstreetmap", "type": "latest", - "title": "New widget", "sizeX": 13, "sizeY": 6, "config": { @@ -725,8 +750,27 @@ } ], "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { - "timewindowMs": 60000 + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085177686, + "endTimeMs": 1694171577686 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 } }, "showTitle": false, @@ -802,14 +846,11 @@ "titleTooltip": "", "displayTimewindow": true }, - "id": "3da9a9a1-0b9a-2e1f-0dcb-0ff34a695abb" + "id": "3da9a9a1-0b9a-2e1f-0dcb-0ff34a695abb", + "typeFullFqn": "system.maps_v2.openstreetmap" }, "00fb2742-ba1f-7e43-673f-d6c08b72ed06": { - "isSystemType": true, - "bundleAlias": "input_widgets", - "typeAlias": "markers_placement_openstreetmap", "type": "latest", - "title": "New widget", "sizeX": 24, "sizeY": 12, "config": { @@ -864,8 +905,27 @@ } ], "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { - "timewindowMs": 60000 + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085177686, + "endTimeMs": 1694171577686 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 } }, "showTitle": false, @@ -937,14 +997,11 @@ "titleTooltip": "", "displayTimewindow": true }, - "id": "00fb2742-ba1f-7e43-673f-d6c08b72ed06" + "id": "00fb2742-ba1f-7e43-673f-d6c08b72ed06", + "typeFullFqn": "system.input_widgets.markers_placement_openstreetmap" }, "0a430429-9078-9ae6-2b67-e4a15a2bf8bf": { - "isSystemType": true, - "bundleAlias": "input_widgets", - "typeAlias": "markers_placement_openstreetmap", "type": "latest", - "title": "New widget", "sizeX": 6, "sizeY": 6, "config": { @@ -999,8 +1056,27 @@ } ], "timewindow": { + "displayValue": "", + "selectedTab": 0, "realtime": { - "timewindowMs": 60000 + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694085177686, + "endTimeMs": 1694171577686 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 } }, "showTitle": false, @@ -1073,7 +1149,8 @@ "titleTooltip": "", "displayTimewindow": true }, - "id": "0a430429-9078-9ae6-2b67-e4a15a2bf8bf" + "id": "0a430429-9078-9ae6-2b67-e4a15a2bf8bf", + "typeFullFqn": "system.input_widgets.markers_placement_openstreetmap" } }, "states": { @@ -1110,7 +1187,8 @@ "autoFillHeight": true, "mobileAutoFillHeight": false, "mobileRowHeight": 70, - "margin": 10 + "margin": 10, + "outerMargin": true } } } @@ -1136,7 +1214,8 @@ "autoFillHeight": true, "mobileAutoFillHeight": false, "mobileRowHeight": 70, - "margin": 10 + "margin": 10, + "outerMargin": true } } } @@ -1182,7 +1261,8 @@ "autoFillHeight": true, "mobileAutoFillHeight": false, "mobileRowHeight": 70, - "margin": 10 + "margin": 10, + "outerMargin": true } } } @@ -1195,8 +1275,10 @@ "filter": { "type": "deviceType", "resolveMultiple": true, - "deviceType": "thermostat", - "deviceNameFilter": "" + "deviceNameFilter": "", + "deviceTypes": [ + "thermostat" + ] } }, "12ae98c7-1ea2-52cf-64d5-763e9d993547": { @@ -1245,5 +1327,6 @@ }, "filters": {} }, + "externalId": null, "name": "Thermostats" } \ No newline at end of file diff --git a/application/src/main/data/json/edge/install_instructions/docker/instructions.md b/application/src/main/data/json/edge/install_instructions/docker/instructions.md deleted file mode 100644 index 6b93447851..0000000000 --- a/application/src/main/data/json/edge/install_instructions/docker/instructions.md +++ /dev/null @@ -1,107 +0,0 @@ -## Install ThingsBoard Edge and connect to cloud instructions - -Here is the list of commands, that can be used to quickly install and connect ThingsBoard Edge to the cloud using docker compose. - -### Prerequisites - -Install Docker CE and Docker Compose. - -### Create data and logs folders - -Run following commands, before starting docker container(s), to create folders for storing data and logs. -These commands additionally will change owner of newly created folders to docker container user. -To do this (to change user) **chown** command is used, and this command requires *sudo* permissions (command will request password for a *sudo* access): - -```bash -mkdir -p ~/.mytb-edge-data && sudo chown -R 799:799 ~/.mytb-edge-data -mkdir -p ~/.mytb-edge-logs && sudo chown -R 799:799 ~/.mytb-edge-logs -{:copy-code} -``` - -### Running ThingsBoard Edge as docker service - -${LOCALHOST_WARNING} - -Create docker compose file for ThingsBoard Edge service: - -```bash -nano docker-compose.yml -{:copy-code} -``` - -Add the following lines to the yml file: - -```bash -version: '3.0' -services: - mytbedge: - restart: always - image: "thingsboard/tb-edge:${TB_EDGE_VERSION}" - ports: - - "8080:8080" - - "1883:1883" - - "5683-5688:5683-5688/udp" - environment: - SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/tb-edge - CLOUD_ROUTING_KEY: ${CLOUD_ROUTING_KEY} - CLOUD_ROUTING_SECRET: ${CLOUD_ROUTING_SECRET} - CLOUD_RPC_HOST: ${BASE_URL} - CLOUD_RPC_PORT: ${CLOUD_RPC_PORT} - CLOUD_RPC_SSL_ENABLED: ${CLOUD_RPC_SSL_ENABLED} - volumes: - - ~/.mytb-edge-data:/data - - ~/.mytb-edge-logs:/var/log/tb-edge - postgres: - restart: always - image: "postgres:12" - ports: - - "5432" - environment: - POSTGRES_DB: tb-edge - POSTGRES_PASSWORD: postgres - volumes: - - ~/.mytb-edge-data/db:/var/lib/postgresql/data -{:copy-code} -``` - -#### [Optional] Update bind ports -If ThingsBoard Edge is going to be running on the same machine where ThingsBoard server (cloud) is running, you'll need to update docker compose port mapping to avoid port collision between ThingsBoard server and ThingsBoard Edge. - -Please update next lines of `docker-compose.yml` file: - -```bash -ports: - - "18080:8080" - - "11883:1883" - - "15683-15688:5683-5688/udp" -``` -Make sure that ports above (18080, 11883, 15683-15688) are not used by any other application. - -#### Start ThingsBoard Edge -Set the terminal in the directory which contains the `docker-compose.yml` file and execute the following commands to up this docker compose directly: - -```bash -docker compose up -d -docker compose logs -f mytbedge -{:copy-code} -``` - -###### NOTE: Docker Compose V2 vs docker-compose (with a hyphen) - -ThingsBoard supports Docker Compose V2 (Docker Desktop or Compose plugin) starting from **3.4.2** release, because **docker-compose** as standalone setup is no longer supported by Docker. -We **strongly** recommend to update to Docker Compose V2 and use it. -If you still rely on using Docker Compose as docker-compose (with a hyphen), then please execute the following commands to start ThingsBoard Edge: - -```bash -docker-compose up -d -docker-compose logs -f mytbedge -``` - -#### Open ThingsBoard Edge UI - -Once started, you will be able to open **ThingsBoard Edge UI** using the following link http://localhost:8080. - -###### NOTE: Edge HTTP bind port update - -Use next **ThingsBoard Edge UI** link **http://localhost:18080** if you updated HTTP 8080 bind port to **18080**. - diff --git a/application/src/main/data/json/edge/install_instructions/docker/localhost_warning.md b/application/src/main/data/json/edge/install_instructions/docker/localhost_warning.md deleted file mode 100644 index d2a5bad26b..0000000000 --- a/application/src/main/data/json/edge/install_instructions/docker/localhost_warning.md +++ /dev/null @@ -1,3 +0,0 @@ -###### WARNING NOTE: 'localhost' can not be used as CLOUD_RPC_HOST - -Please note that your ThingsBoard base URL is **'localhost'** at the moment. **'localhost'** cannot be used for docker containers - please update **CLOUD_RPC_HOST** environment variable below to the IP address of your machine (*docker **host** machine*). IP address must be `192.168.1.XX` or similar format. In other case - ThingsBoard Edge service, that is running in docker container, will not be able to connect to the cloud. diff --git a/application/src/main/data/json/edge/instructions/install/centos/instructions.md b/application/src/main/data/json/edge/instructions/install/centos/instructions.md new file mode 100644 index 0000000000..22bb3f63a0 --- /dev/null +++ b/application/src/main/data/json/edge/instructions/install/centos/instructions.md @@ -0,0 +1,231 @@ +Here is the list of commands, that can be used to quickly install ThingsBoard Edge on RHEL/CentOS 7/8 and connect to the server. + +#### Prerequisites +Before continue to installation execute the following commands in order to install necessary tools: + +```bash +sudo yum install -y nano wget +sudo yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm +``` + +#### Install Java 11 (OpenJDK) +ThingsBoard service is running on Java 11. Follow these instructions to install OpenJDK 11: + +```bash +sudo yum install java-11-openjdk +{:copy-code} +``` + +Please don't forget to configure your operating system to use OpenJDK 11 by default. +You can configure which version is the default using the following command: + +```bash +sudo update-alternatives --config java +{:copy-code} +``` + +You can check the installation using the following command: + +```bash +java -version +{:copy-code} +``` + +Expected command output is: + +```text +openjdk version "11.0.xx" +OpenJDK Runtime Environment (...) +OpenJDK 64-Bit Server VM (build ...) +``` + +#### Configure PostgreSQL +ThingsBoard Edge uses PostgreSQL database as a local storage. +Instructions listed below will help you to install PostgreSQL. + +```bash +# Update your system +sudo yum update +{:copy-code} +``` + +**For CentOS 7:** + +```bash +# Install the repository RPM (for CentOS 7): +sudo yum -y install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm +# Install packages +sudo yum -y install epel-release yum-utils +sudo yum-config-manager --enable pgdg15 +sudo yum install postgresql15-server postgresql15 +# Initialize your PostgreSQL DB +sudo /usr/pgsql-15/bin/postgresql-15-setup initdb +sudo systemctl start postgresql-15 +# Optional: Configure PostgreSQL to start on boot +sudo systemctl enable --now postgresql-15 + +{:copy-code} +``` + +**For CentOS 8:** + +```bash +# Install the repository RPM (for CentOS 8): +sudo yum -y install https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm +# Install packages +sudo dnf -qy module disable postgresql +sudo dnf -y install postgresql15 postgresql15-server +# Initialize your PostgreSQL DB +sudo /usr/pgsql-15/bin/postgresql-15-setup initdb +sudo systemctl start postgresql-15 +# Optional: Configure PostgreSQL to start on boot +sudo systemctl enable --now postgresql-15 + +{:copy-code} +``` + +Once PostgreSQL is installed you may want to create a new user or set the password for the main user. +The instructions below will help to set the password for main PostgreSQL user: + +```text +sudo su - postgres +psql +\password +\q +``` + +Then, press "Ctrl+D" to return to main user console. + +After configuring the password, edit the pg_hba.conf to use MD5 authentication with the postgres user. + +Edit pg_hba.conf file: + +```bash +sudo nano /var/lib/pgsql/15/data/pg_hba.conf +{:copy-code} +``` + +Locate the following lines: + +```text +# IPv4 local connections: +host all all 127.0.0.1/32 ident +``` + +Replace `ident` with `md5`: + +```text +host all all 127.0.0.1/32 md5 +``` + +Finally, you should restart the PostgreSQL service to initialize the new configuration: + +```bash +sudo systemctl restart postgresql-15.service +{:copy-code} +``` + +Connect to the database to create ThingsBoard Edge DB: + +```bash +psql -U postgres -d postgres -h 127.0.0.1 -W +{:copy-code} +``` + +Execute create database statement: + +```bash +CREATE DATABASE tb_edge; +\q +{:copy-code} +``` + +#### ThingsBoard Edge service installation +Download installation package: + +```bash +wget https://github.com/thingsboard/thingsboard-edge/releases/download/v${TB_EDGE_VERSION}/tb-edge-${TB_EDGE_VERSION}.rpm +{:copy-code} +``` + +Go to the download repository and install ThingsBoard Edge service: + +```bash +sudo rpm -Uvh tb-edge-${TB_EDGE_VERSION}.rpm +{:copy-code} +``` + +#### Configure ThingsBoard Edge +To configure ThingsBoard Edge, you can use the following command to automatically update the configuration file with specific values: + +```bash +sudo sh -c 'cat <> /etc/tb-edge/conf/tb-edge.conf +export CLOUD_ROUTING_KEY=${CLOUD_ROUTING_KEY} +export CLOUD_ROUTING_SECRET=${CLOUD_ROUTING_SECRET} +export CLOUD_RPC_HOST=${BASE_URL} +export CLOUD_RPC_PORT=${CLOUD_RPC_PORT} +export CLOUD_RPC_SSL_ENABLED=${CLOUD_RPC_SSL_ENABLED} +EOL' +{:copy-code} +``` + +##### [Optional] Database Configuration +In case you changed default PostgreSQL datasource settings (**postgres**/**postgres**) please update the configuration file (**/etc/tb-edge/conf/tb-edge.conf**) with your actual values: + +```bash +sudo nano /etc/tb-edge/conf/tb-edge.conf +{:copy-code} +``` + +Please update the following lines in your configuration file. Make sure **to replace**: +- Replace 'postgres' with your actual PostgreSQL username; +- Replace 'PUT_YOUR_POSTGRESQL_PASSWORD_HERE' with your actual PostgreSQL password. + +```bash +export SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/tb_edge +export SPRING_DATASOURCE_USERNAME=postgres +export SPRING_DATASOURCE_PASSWORD=PUT_YOUR_POSTGRESQL_PASSWORD_HERE +{:copy-code} +``` + +##### [Optional] Update bind ports +If ThingsBoard Edge is going to be running on the same machine where ThingsBoard server (cloud) is running, you'll need to update configuration parameters to avoid port collision between ThingsBoard server and ThingsBoard Edge. + +Please execute the following command to update ThingsBoard Edge configuration file (**/etc/tb-edge/conf/tb-edge.conf**): + +```bash +sudo sh -c 'cat <> /etc/tb-edge/conf/tb-edge.conf +export HTTP_BIND_PORT=18080 +export MQTT_BIND_PORT=11883 +export COAP_BIND_PORT=15683 +export LWM2M_ENABLED=false +export SNMP_ENABLED=false +EOL' +{:copy-code} +``` + +Make sure that ports above (18080, 11883, 15683) are not used by any other application. + +#### Run installation script +Once ThingsBoard Edge is installed and configured please execute the following install script: + +```bash +sudo /usr/share/tb-edge/bin/install/install.sh +{:copy-code} +``` + +#### Restart ThingsBoard Edge service + +```bash +sudo service tb-edge restart +{:copy-code} +``` + +#### Open ThingsBoard Edge UI + +Once started, you will be able to open **ThingsBoard Edge UI** using the following link http://localhost:8080. + +###### NOTE: Edge HTTP bind port update + +Use next **ThingsBoard Edge UI** link **http://localhost:18080** if you updated HTTP 8080 bind port to **18080**. + diff --git a/application/src/main/data/json/edge/instructions/install/docker/instructions.md b/application/src/main/data/json/edge/instructions/install/docker/instructions.md new file mode 100644 index 0000000000..9910124b3e --- /dev/null +++ b/application/src/main/data/json/edge/instructions/install/docker/instructions.md @@ -0,0 +1,100 @@ +Here is the list of commands, that can be used to quickly install ThingsBoard Edge using docker compose and connect to the server. + +#### Prerequisites + +Install Docker CE and Docker Compose. + +#### Running ThingsBoard Edge as docker service + +Create docker compose file for ThingsBoard Edge service: + +```bash +nano docker-compose.yml +{:copy-code} +``` + +Add the following lines to the yml file: + +```bash +version: '3.8' +services: + mytbedge: + restart: always + image: "thingsboard/tb-edge:${TB_EDGE_VERSION}" + ports: + - "8080:8080" + - "1883:1883" + - "5683-5688:5683-5688/udp" + environment: + SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/tb-edge + CLOUD_ROUTING_KEY: ${CLOUD_ROUTING_KEY} + CLOUD_ROUTING_SECRET: ${CLOUD_ROUTING_SECRET} + CLOUD_RPC_HOST: ${BASE_URL} + CLOUD_RPC_PORT: ${CLOUD_RPC_PORT} + CLOUD_RPC_SSL_ENABLED: ${CLOUD_RPC_SSL_ENABLED} + volumes: + - tb-edge-data:/data + - tb-edge-logs:/var/log/tb-edge + ${EXTRA_HOSTS} + postgres: + restart: always + image: "postgres:15" + ports: + - "5432" + environment: + POSTGRES_DB: tb-edge + POSTGRES_PASSWORD: postgres + volumes: + - tb-edge-postgres-data:/var/lib/postgresql/data + +volumes: + tb-edge-data: + name: tb-edge-data + tb-edge-logs: + name: tb-edge-logs + tb-edge-postgres-data: + name: tb-edge-postgres-data +{:copy-code} +``` + +##### [Optional] Update bind ports +If ThingsBoard Edge is going to be running on the same machine where ThingsBoard server (cloud) is running, you'll need to update docker compose port mapping to avoid port collision between ThingsBoard server and ThingsBoard Edge. + +Please update next lines of `docker-compose.yml` file: + +```text +ports: + - "18080:8080" + - "11883:1883" + - "15683-15688:5683-5688/udp" +``` +Make sure that ports above (18080, 11883, 15683-15688) are not used by any other application. + +#### Start ThingsBoard Edge +Set the terminal in the directory which contains the `docker-compose.yml` file and execute the following commands to up this docker compose directly: + +```bash +docker compose up -d +docker compose logs -f mytbedge +{:copy-code} +``` + +###### NOTE: Docker Compose V2 vs docker-compose (with a hyphen) + +ThingsBoard supports Docker Compose V2 (Docker Desktop or Compose plugin) starting from **3.4.2** release, because **docker-compose** as standalone setup is no longer supported by Docker. +We **strongly** recommend to update to Docker Compose V2 and use it. +If you still rely on using Docker Compose as docker-compose (with a hyphen), then please execute the following commands to start ThingsBoard Edge: + +```bash +docker-compose up -d +docker-compose logs -f mytbedge +``` + +#### Open ThingsBoard Edge UI + +Once started, you will be able to open **ThingsBoard Edge UI** using the following link http://localhost:8080. + +###### NOTE: Edge HTTP bind port update + +Use next **ThingsBoard Edge UI** link **http://localhost:18080** if you updated HTTP 8080 bind port to **18080**. + diff --git a/application/src/main/data/json/edge/instructions/install/ubuntu/instructions.md b/application/src/main/data/json/edge/instructions/install/ubuntu/instructions.md new file mode 100644 index 0000000000..6fcdcd50f2 --- /dev/null +++ b/application/src/main/data/json/edge/instructions/install/ubuntu/instructions.md @@ -0,0 +1,164 @@ +Here is the list of commands, that can be used to quickly install ThingsBoard Edge on Ubuntu Server and connect to the server. + +#### Install Java 11 (OpenJDK) +ThingsBoard service is running on Java 11. Follow these instructions to install OpenJDK 11: + +```bash +sudo apt update +sudo apt install openjdk-11-jdk +{:copy-code} +``` + +Please don't forget to configure your operating system to use OpenJDK 11 by default. +You can configure which version is the default using the following command: + +```bash +sudo update-alternatives --config java +{:copy-code} +``` + +You can check the installation using the following command: + +```bash +java -version +{:copy-code} +``` + +Expected command output is: + +```text +openjdk version "11.0.xx" +OpenJDK Runtime Environment (...) +OpenJDK 64-Bit Server VM (build ...) +``` + +#### Configure PostgreSQL +ThingsBoard Edge uses PostgreSQL database as a local storage. +Instructions listed below will help you to install PostgreSQL. + +```bash +# install **wget** if not already installed: +sudo apt install -y wget + +# import the repository signing key: +wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - + +# add repository contents to your system: +RELEASE=$(lsb_release -cs) +echo "deb http://apt.postgresql.org/pub/repos/apt/ ${RELEASE}"-pgdg main | sudo tee /etc/apt/sources.list.d/pgdg.list + +# install and launch the postgresql service: +sudo apt update +sudo apt -y install postgresql-15 +sudo service postgresql start +{:copy-code} +``` + +Once PostgreSQL is installed you may want to create a new user or set the password for the main user. +The instructions below will help to set the password for main PostgreSQL user: + +```text +sudo su - postgres +psql +\password +\q +``` + +Then, press “Ctrl+D” to return to main user console and connect to the database to create ThingsBoard Edge DB: + +```text +psql -U postgres -d postgres -h 127.0.0.1 -W +CREATE DATABASE tb_edge; +\q +``` + +#### Thingsboard Edge service installation +Download installation package: + +```bash +wget https://github.com/thingsboard/thingsboard-edge/releases/download/v${TB_EDGE_VERSION}/tb-edge-${TB_EDGE_VERSION}.deb +{:copy-code} +``` + +Go to the download repository and install ThingsBoard Edge service: + +```bash +sudo dpkg -i tb-edge-${TB_EDGE_VERSION}.deb +{:copy-code} +``` + +#### Configure ThingsBoard Edge +To configure ThingsBoard Edge, you can use the following command to automatically update the configuration file with specific values: + +```bash +sudo sh -c 'cat <> /etc/tb-edge/conf/tb-edge.conf +export CLOUD_ROUTING_KEY=${CLOUD_ROUTING_KEY} +export CLOUD_ROUTING_SECRET=${CLOUD_ROUTING_SECRET} +export CLOUD_RPC_HOST=${BASE_URL} +export CLOUD_RPC_PORT=${CLOUD_RPC_PORT} +export CLOUD_RPC_SSL_ENABLED=${CLOUD_RPC_SSL_ENABLED} +EOL' +{:copy-code} +``` + +##### [Optional] Database Configuration +In case you changed default PostgreSQL datasource settings (**postgres**/**postgres**) please update the configuration file (**/etc/tb-edge/conf/tb-edge.conf**) with your actual values: + +```bash +sudo nano /etc/tb-edge/conf/tb-edge.conf +{:copy-code} +``` + +Please update the following lines in your configuration file. Make sure **to replace**: +- Replace 'postgres' with your actual PostgreSQL username; +- Replace 'PUT_YOUR_POSTGRESQL_PASSWORD_HERE' with your actual PostgreSQL password. + +```bash +export SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/tb_edge +export SPRING_DATASOURCE_USERNAME=postgres +export SPRING_DATASOURCE_PASSWORD=PUT_YOUR_POSTGRESQL_PASSWORD_HERE +{:copy-code} +``` + +##### [Optional] Update bind ports +If ThingsBoard Edge is going to be running on the same machine where ThingsBoard server (cloud) is running, you'll need to update configuration parameters to avoid port collision between ThingsBoard server and ThingsBoard Edge. + +Please execute the following command to update ThingsBoard Edge configuration file (**/etc/tb-edge/conf/tb-edge.conf**): + +```bash +sudo sh -c 'cat <> /etc/tb-edge/conf/tb-edge.conf +export HTTP_BIND_PORT=18080 +export MQTT_BIND_PORT=11883 +export COAP_BIND_PORT=15683 +export LWM2M_ENABLED=false +export SNMP_ENABLED=false +EOL' +{:copy-code} +``` + +Make sure that ports above (18080, 11883, 15683) are not used by any other application. + +#### Run installation script + +Once ThingsBoard Edge is installed and configured please execute the following install script: + +```bash +sudo /usr/share/tb-edge/bin/install/install.sh +{:copy-code} +``` + +#### Restart ThingsBoard Edge service + +```bash +sudo service tb-edge restart +{:copy-code} +``` + +#### Open ThingsBoard Edge UI + +Once started, you will be able to open **ThingsBoard Edge UI** using the following link http://localhost:8080. + +###### NOTE: Edge HTTP bind port update + +Use next **ThingsBoard Edge UI** link **http://localhost:18080** if you updated HTTP 8080 bind port to **18080**. + diff --git a/application/src/main/data/json/edge/instructions/upgrade/centos/instructions.md b/application/src/main/data/json/edge/instructions/upgrade/centos/instructions.md new file mode 100644 index 0000000000..c0e721ace2 --- /dev/null +++ b/application/src/main/data/json/edge/instructions/upgrade/centos/instructions.md @@ -0,0 +1,15 @@ +#### Upgrading to ${TB_EDGE_VERSION}EDGE + +**ThingsBoard Edge package download:** +```bash +wget https://github.com/thingsboard/thingsboard-edge/releases/download/v${TB_EDGE_TAG}/tb-edge-${TB_EDGE_TAG}.rpm +{:copy-code} +``` +##### ThingsBoard Edge service upgrade + +Install package: +```bash +sudo rpm -Uvh tb-edge-${TB_EDGE_TAG}.rpm +{:copy-code} +``` +${UPGRADE_DB} diff --git a/application/src/main/data/json/edge/instructions/upgrade/docker/instructions.md b/application/src/main/data/json/edge/instructions/upgrade/docker/instructions.md new file mode 100644 index 0000000000..f1b9931e8b --- /dev/null +++ b/application/src/main/data/json/edge/instructions/upgrade/docker/instructions.md @@ -0,0 +1,10 @@ +#### Upgrading to ${TB_EDGE_VERSION} + +Execute the following command to pull **${TB_EDGE_VERSION}** image: + +```bash +docker pull thingsboard/tb-edge:${TB_EDGE_VERSION} +{:copy-code} +``` + +${UPGRADE_DB} diff --git a/application/src/main/data/json/edge/instructions/upgrade/docker/start_service.md b/application/src/main/data/json/edge/instructions/upgrade/docker/start_service.md new file mode 100644 index 0000000000..a6c9c6731c --- /dev/null +++ b/application/src/main/data/json/edge/instructions/upgrade/docker/start_service.md @@ -0,0 +1,23 @@ +Modify ‘main’ docker compose (`docker-compose.yml`) file for ThingsBoard Edge and update version of the image: +```bash +nano docker-compose.yml +{:copy-code} +``` + +```text +version: '3.8' +services: + mytbedge: + restart: always + image: "thingsboard/tb-edge:${TB_EDGE_VERSION}" +... +``` + +Make sure your image is the set to **tb-edge-${TB_EDGE_VERSION}**. +Execute the following commands to up this docker compose directly: + +```bash +docker compose up -d +docker compose logs -f mytbedge +{:copy-code} +``` diff --git a/application/src/main/data/json/edge/instructions/upgrade/docker/upgrade_db.md b/application/src/main/data/json/edge/instructions/upgrade/docker/upgrade_db.md new file mode 100644 index 0000000000..d922fa9155 --- /dev/null +++ b/application/src/main/data/json/edge/instructions/upgrade/docker/upgrade_db.md @@ -0,0 +1,61 @@ +Create docker compose file for ThingsBoard Edge upgrade process: + +```bash +> docker-compose-upgrade.yml && nano docker-compose-upgrade.yml +{:copy-code} +``` + +Add the following lines to the yml file: + +```bash +version: '3.8' +services: + mytbedge: + restart: on-failure + image: "thingsboard/tb-edge:${TB_EDGE_VERSION}" + environment: + SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/tb-edge + volumes: + - tb-edge-data:/data + - tb-edge-logs:/var/log/tb-edge + entrypoint: upgrade-tb-edge.sh + postgres: + restart: always + image: "postgres:15" + ports: + - "5432" + environment: + POSTGRES_DB: tb-edge + POSTGRES_PASSWORD: postgres + volumes: + - tb-edge-postgres-data:/var/lib/postgresql/data + +volumes: + tb-edge-data: + name: tb-edge-data + tb-edge-logs: + name: tb-edge-logs + tb-edge-postgres-data: + name: tb-edge-postgres-data +{:copy-code} +``` + +Execute the following command to start upgrade process: + +```bash +docker compose -f docker-compose-upgrade.yml up +{:copy-code} +``` + +Once upgrade process successfully completed, exit from the docker-compose shell by this combination: + +```text +Ctrl + C +``` + +Execute the following command to stop TB Edge upgrade container: + +```bash +docker compose -f docker-compose-upgrade.yml stop +{:copy-code} +``` diff --git a/application/src/main/data/json/edge/instructions/upgrade/docker/upgrade_preparing.md b/application/src/main/data/json/edge/instructions/upgrade/docker/upgrade_preparing.md new file mode 100644 index 0000000000..a0536f113a --- /dev/null +++ b/application/src/main/data/json/edge/instructions/upgrade/docker/upgrade_preparing.md @@ -0,0 +1,95 @@ +Here is the list of commands, that can be used to quickly upgrade ThingsBoard Edge on Docker (Linux or MacOS). + +#### Prepare for upgrading ThingsBoard Edge +Set the terminal in the directory which contains the `docker-compose.yml` file and execute the following command +to stop and remove currently running TB Edge container: + +```bash +docker compose stop +docker compose rm mytbedge +{:copy-code} +``` + +**OPTIONAL:** If you still rely on Docker Compose as docker-compose (with a hyphen) here is the list of the above commands: +```text +docker-compose stop +docker-compose rm mytbedge +``` + +##### Migrating Data from Docker Bind Mount Folders to Docker Volumes +Starting with the **3.6.2** release, the ThingsBoard team has transitioned from using Docker bind mount folders to Docker volumes. +This change aims to enhance security and efficiency in storing data for Docker containers and to mitigate permission issues across various environments. + +To migrate from Docker bind mounts to Docker volumes, please execute the following commands: + +```bash +docker run --rm -v tb-edge-data:/volume -v ~/.mytb-edge-data:/backup busybox sh -c "cp -a /backup/. /volume" +docker run --rm -v tb-edge-logs:/volume -v ~/.mytb-edge-logs:/backup busybox sh -c "cp -a /backup/. /volume" +docker run --rm -v tb-edge-postgres-data:/volume -v ~/.mytb-edge-data/db:/backup busybox sh -c "cp -a /backup/. /volume" +{:copy-code} +``` + +After completing the data migration to the newly created Docker volumes, you'll need to update the volume mounts in your Docker Compose configuration. +Modify the `docker-compose.yml` file for ThingsBoard Edge to update the volume settings. + +First, please update docker compose file version. Find next snippet: +```text +version: '3.0' +... +``` + +And replace it with: +```text +version: '3.8' +... +``` + +Then update volume mounts. Locate the following snippet: +```text + volumes: + - ~/.mytb-edge-data:/data + - ~/.mytb-edge-logs:/var/log/tb-edge +... +``` + +And replace it with: +```text + volumes: + - tb-edge-data:/data + - tb-edge-logs:/var/log/tb-edge +... +``` + +Apply a similar update for the PostgreSQL service. Find the section: +```text + volumes: + - ~/.mytb-edge-data/db:/var/lib/postgresql/data +... +``` + +And replace it with: +```text + volumes: + - tb-edge-postgres-data:/var/lib/postgresql/data +... +``` + +Finally, please add next volumes section at the end of the file: +```text +... +volumes: + tb-edge-data: + name: tb-edge-data + tb-edge-logs: + name: tb-edge-logs + tb-edge-postgres-data: + name: tb-edge-postgres-data +``` + +##### Backup Database +Make a copy of the database volume before upgrading: + +```bash +docker run --rm -v tb-edge-postgres-data:/source -v tb-edge-postgres-data-backup:/backup busybox sh -c "cp -a /source/. /backup" +{:copy-code} +``` diff --git a/application/src/main/data/json/edge/instructions/upgrade/start_service.md b/application/src/main/data/json/edge/instructions/upgrade/start_service.md new file mode 100644 index 0000000000..c26c183154 --- /dev/null +++ b/application/src/main/data/json/edge/instructions/upgrade/start_service.md @@ -0,0 +1,6 @@ +Start the service + +```bash +sudo systemctl tb-edge start +{:copy-code} +``` diff --git a/application/src/main/data/json/edge/instructions/upgrade/ubuntu/instructions.md b/application/src/main/data/json/edge/instructions/upgrade/ubuntu/instructions.md new file mode 100644 index 0000000000..59d243c63c --- /dev/null +++ b/application/src/main/data/json/edge/instructions/upgrade/ubuntu/instructions.md @@ -0,0 +1,15 @@ +#### Upgrading to ${TB_EDGE_VERSION}EDGE + +**ThingsBoard Edge package download:** +```bash +wget https://github.com/thingsboard/thingsboard-edge/releases/download/v${TB_EDGE_TAG}/tb-edge-${TB_EDGE_TAG}.deb +{:copy-code} +``` +##### ThingsBoard Edge service upgrade + +Install package: +```bash +sudo dpkg -i tb-edge-${TB_EDGE_TAG}.deb +{:copy-code} +``` +${UPGRADE_DB} diff --git a/application/src/main/data/json/edge/instructions/upgrade/upgrade_db.md b/application/src/main/data/json/edge/instructions/upgrade/upgrade_db.md new file mode 100644 index 0000000000..6e7beb1425 --- /dev/null +++ b/application/src/main/data/json/edge/instructions/upgrade/upgrade_db.md @@ -0,0 +1,8 @@ +**NOTE**: Package installer may ask you to merge your tb-edge configuration. It is preferred to use **merge option** to make sure that all your previous parameters will not be overwritten. + +Execute regular upgrade script: + +```bash +sudo /usr/share/tb-edge/bin/install/upgrade.sh --fromVersion=${FROM_TB_EDGE_VERSION} +{:copy-code} +``` diff --git a/application/src/main/data/json/edge/instructions/upgrade/upgrade_preparing.md b/application/src/main/data/json/edge/instructions/upgrade/upgrade_preparing.md new file mode 100644 index 0000000000..bdd6c4ea18 --- /dev/null +++ b/application/src/main/data/json/edge/instructions/upgrade/upgrade_preparing.md @@ -0,0 +1,36 @@ +Here is the list of commands, that can be used to quickly upgrade ThingsBoard Edge on ${OS} + +#### Prepare for upgrading ThingsBoard Edge + +Stop ThingsBoard Edge service: + +```bash +sudo systemctl stop tb-edge +{:copy-code} +``` + +##### Backup Database +Make a backup of the database before upgrading. **Make sure you have enough space to place a backup of the database.** + +Check database size: + +```bash +sudo -u postgres psql -c "SELECT pg_size_pretty( pg_database_size('tb_edge') );" +{:copy-code} +``` + +Check free space: + +```bash +df -h / +{:copy-code} +``` + +If there is enough free space - make a backup: + +```bash +sudo -Hiu postgres pg_dump tb_edge > tb_edge.sql.bak +{:copy-code} +``` + +Check backup file created successfully. diff --git a/application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json b/application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json index ec1341cc71..6b7603c026 100644 --- a/application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json +++ b/application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json @@ -48,9 +48,12 @@ "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", "name": "Save Client Attributes", "debugMode": false, + "configurationVersion": 2, "configuration": { "scope": "CLIENT_SCOPE", - "notifyDevice": "false" + "notifyDevice": false, + "sendAttributesUpdatedNotification": false, + "updateAttributesOnlyOnValueChange": true }, "externalId": null }, diff --git a/application/src/main/data/json/system/widget_bundles/air_quality.json b/application/src/main/data/json/system/widget_bundles/air_quality.json new file mode 100644 index 0000000000..534b509dba --- /dev/null +++ b/application/src/main/data/json/system/widget_bundles/air_quality.json @@ -0,0 +1,111 @@ +{ + "widgetsBundle": { + "alias": "air_quality", + "title": "Air quality", + "image": "tb-image:YWlyX3F1YWxpdHlfd2lkZ2V0c19idW5kbGUuc3Zn:YWlyX3F1YWxpdHlfd2lkZ2V0c19idW5kbGUuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj4KICA8cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9Ijc2IiBmaWxsPSIjZmZmIiByeD0iNCIgLz4KICA8cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9Ijc1IiB4PSIuNSIgeT0iLjUiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii4xIiByeD0iMy41IiAvPgogIDxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiCiAgICBkPSJNMTIgMTYuMWgxYTIuMyAyLjMgMCAwIDEtLjQgMSAxLjkgMS45IDAgMCAxLS43LjdsLTEuMi4zYy0uNCAwLS43IDAtMS0uMmEyIDIgMCAwIDEtLjctLjUgMi40IDIuNCAwIDAgMS0uNC0uOWwtLjItMS4xdi0uNWEzLjYgMy42IDAgMCAxIC4yLTFjMC0uNC4yLS43LjQtMWwuOC0uNSAxLS4yYy40IDAgLjguMSAxIC4zLjQuMi42LjQuOC43LjIuMy4zLjYuMyAxaC0xYTEuOCAxLjggMCAwIDAtLjEtLjYuOS45IDAgMCAwLS40LS40bC0uNy0uMi0uNi4xYTEuMSAxLjEgMCAwIDAtLjQuNCAxLjggMS44IDAgMCAwLS4yLjYgMy42IDMuNiAwIDAgMC0uMS44djEuM2wuMy42LjQuNC42LjEuNy0uMWEuOS45IDAgMCAwIC40LS40IDEuOCAxLjggMCAwIDAgLjEtLjdabTYuNi0xLjF2LjNjMCAuNCAwIC44LS4yIDEuMmEyLjUgMi41IDAgMCAxLS41LjggMiAyIDAgMCAxLS43LjZsLTEgLjJjLS40IDAtLjYgMC0xLS4yYTIuMSAyLjEgMCAwIDEtLjctLjYgMi42IDIuNiAwIDAgMS0uNS0uOCAzLjYgMy42IDAgMCAxLS4xLTEuMlYxNWwuMS0xLjIuNS0uOGMuMi0uMy41LS40LjgtLjZsMS0uMi45LjIuNy42LjUuOC4yIDEuMlptLTEgLjN2LTEuMWwtLjQtLjdhMS4xIDEuMSAwIDAgMC0uNC0uMyAxLjMgMS4zIDAgMCAwLS42LS4yYy0uMiAwLS40IDAtLjUuMi0uMiAwLS40LjItLjUuM2ExLjggMS44IDAgMCAwLS4zLjcgMy40IDMuNCAwIDAgMCAwIC44djEuMmwuMy42LjUuNGguNWExLjMgMS4zIDAgMCAwIC42IDBsLjQtLjQuMy0uNi4xLS45Wm01LjkgMnYuN2gtMy44di0uNmwxLjgtMmE3IDcgMCAwIDAgLjUtLjdsLjItLjRhMS4xIDEuMSAwIDAgMCAwLS45LjcuNyAwIDAgMC0uMy0uMy45LjkgMCAwIDAtLjQtLjEgMSAxIDAgMCAwLS42LjEuOS45IDAgMCAwLS4zLjRsLS4xLjVoLTFsLjMtLjkuNi0uNmEyIDIgMCAwIDEgMS0uM2wxIC4yLjcuNi4yLjgtLjEuNS0uMy42YTQuNSA0LjUgMCAwIDEtLjMuNWwtLjUuNS0xLjIgMS4zaDIuNlptNC4zLTUuM3Y2aC0xdi02aDFabTMuMSA2aC0uOGExLjkgMS45IDAgMCAxLS43LS41IDIgMiAwIDAgMS0uNC0uN0wyOSAxNlYxNmwuMS0xYTIgMiAwIDAgMSAuNC0uNmwuNi0uNS44LS4xLjguMS42LjUuMy42LjEuOXYuNGgtMy4zdi0uN2gyLjRhMS4zIDEuMyAwIDAgMC0uMS0uNi44LjggMCAwIDAtLjMtLjMuOC44IDAgMCAwLS41LS4yLjguOCAwIDAgMC0uNy40bC0uMi41YTIuNSAyLjUgMCAwIDAgMCAuNnYuNmwuMi41YTEgMSAwIDAgMCAuOS4zIDEuMyAxLjMgMCAwIDAgMS0uNWwuNS41LS4zLjQtLjYuM0gzMVptNC0uNyAxLTMuNWgxTDM1LjUgMThoLS43bC4xLS43Wm0tLjgtMy41IDEgMy41LjEuN2gtLjZsLTEuNS00LjJoMVptNS41IDQuM2MtLjMgMC0uNiAwLS45LS4yYTEuOSAxLjkgMCAwIDEtLjYtLjQgMiAyIDAgMCAxLS40LS43bC0uMi0uOFYxNmMwLS40IDAtLjcuMi0xIDAtLjIuMi0uNC40LS42bC42LS41LjgtLjEuNy4xYy4zLjEuNS4zLjYuNWwuMy42LjIuOXYuNEgzOHYtLjdoMi4zYTEuMyAxLjMgMCAwIDAgMC0uNi44LjggMCAwIDAtLjQtLjMuOC44IDAgMCAwLS41LS4yLjguOCAwIDAgMC0uNC4xLjguOCAwIDAgMC0uMy4zbC0uMi41YTIuNSAyLjUgMCAwIDAgMCAuNnYuNmwuMy41LjMuMi41LjFhMS4zIDEuMyAwIDAgMCAxLS41bC42LjVhMS44IDEuOCAwIDAgMS0xIC43aC0uNlptMy42LTYuMXY2aC0uOXYtNmgxWiIgLz4KICA8cGF0aCBmaWxsPSIjM0ZBNzFBIgogICAgZD0iTTY1LjYgNDl2Mi4ySDUzLjN2LTEuN2w3LjMtMTEuNmgyLjNsLTIuNSA0LjItNC4yIDYuOGg5LjRabS0yLTExdjE3aC0zVjM4aDNabTE1IDd2Mi44YzAgMS40IDAgMi41LS4zIDMuNWE2IDYgMCAwIDEtMS4yIDIuMiA0LjQgNC40IDAgMCAxLTEuNyAxLjMgNiA2IDAgMCAxLTIuMy40IDYuNiA2LjYgMCAwIDEtMS45LS4yIDQuNiA0LjYgMCAwIDEtMS42LS44IDQuOCA0LjggMCAwIDEtMS4xLTEuNSA4IDggMCAwIDEtLjgtMmMtLjItLjktLjItMS44LS4yLTIuOVY0NWMwLTEuMyAwLTIuNC40LTMuNGE2IDYgMCAwIDEgMS0yLjIgNC4zIDQuMyAwIDAgMSAxLjgtMS4zYy43LS4zIDEuNS0uNCAyLjMtLjQuNyAwIDEuNCAwIDIgLjNhNC40IDQuNCAwIDAgMSAxLjUuNyA3IDcgMCAwIDEgMiAzLjVsLjEgMi44Wm0tMi44IDMuMnYtNS40bC0uNC0xLjNhMi42IDIuNiAwIDAgMC0uNi0uOSAyIDIgMCAwIDAtLjgtLjUgMi43IDIuNyAwIDAgMC0xLS4yYy0uNCAwLS44LjEtMS4xLjMtLjQuMi0uNy40LS45LjgtLjIuNC0uNC44LS41IDEuNGwtLjIgMi4ydjMuNmwuMSAxLjhjMCAuNi4yIDEgLjQgMS40bC41IDEgLjguNCAxIC4yIDEuMi0uM2MuMy0uMS42LS40LjgtLjguMy0uNC40LS45LjUtMS41LjItLjYuMi0xLjMuMi0yLjJaTTkyLjMgNDV2Mi44YzAgMS40LS4xIDIuNS0uNCAzLjVhNiA2IDAgMCAxLTEuMSAyLjIgNC40IDQuNCAwIDAgMS0xLjggMS4zIDYgNiAwIDAgMS0yLjMuNCA2LjYgNi42IDAgMCAxLTEuOS0uMiA0LjYgNC42IDAgMCAxLTEuNS0uOCA0LjggNC44IDAgMCAxLTEuMi0xLjUgOCA4IDAgMCAxLS43LTJjLS4yLS45LS4zLTEuOC0uMy0yLjlWNDVjMC0xLjMuMS0yLjQuNC0zLjRhNiA2IDAgMCAxIDEuMS0yLjIgNC4zIDQuMyAwIDAgMSAxLjgtMS4zYy43LS4zIDEuNC0uNCAyLjMtLjQuNyAwIDEuMyAwIDEuOS4zYTQuNCA0LjQgMCAwIDEgMS41LjdjLjUuNC45LjkgMS4yIDEuNS4zLjUuNiAxLjIuNyAyIC4yLjguMyAxLjguMyAyLjhabS0yLjggMy4ydi0zLjZjMC0uNyAwLTEuMy0uMi0xLjggMC0uNS0uMS0xLS4zLTEuM2EyLjYgMi42IDAgMCAwLS42LS45IDIgMiAwIDAgMC0uNy0uNSAyLjcgMi43IDAgMCAwLTEtLjJjLS41IDAtLjguMS0xLjIuM2wtLjkuOGMtLjIuNC0uNC44LS41IDEuNGwtLjIgMi4ydjMuNmMwIC43IDAgMS4zLjIgMS44IDAgLjYuMSAxIC4zIDEuNC4yLjQuMy43LjYgMWwuNy40IDEgLjJjLjUgMCAuOS0uMSAxLjItLjMuMy0uMS42LS40LjktLjhsLjUtMS41LjItMi4yWm0xNC4zLTMuNHYxNUgxMDFWNDIuNGgyLjZsLjIgMi41Wm04LjIgMy43di4zYzAgMSAwIDEuOC0uMyAyLjZhNi4zIDYuMyAwIDAgMS0xIDIgNC40IDQuNCAwIDAgMS0xLjUgMS40Yy0uNi4zLTEuMy40LTIgLjQtLjkgMC0xLjYtLjEtMi4xLS40YTQuMyA0LjMgMCAwIDEtMS41LTEuMyA2LjggNi44IDAgMCAxLTEtMS45IDEyLjIgMTIuMiAwIDAgMS0uNS0yLjR2LTFjMC0uOC4yLTEuNy41LTIuNC4yLS44LjYtMS41IDEtMmE0LjIgNC4yIDAgMCAxIDEuNS0xLjNjLjUtLjMgMS4yLS40IDItLjRzMS41LjEgMi4xLjVhNC4zIDQuMyAwIDAgMSAxLjYgMS4zYy40LjUuNyAxLjIgMSAybC4yIDIuNlptLTIuOC4zdi0uM2wtLjEtMS42YTQuMyA0LjMgMCAwIDAtLjYtMS4zIDIuNSAyLjUgMCAwIDAtLjktMWwtMS4zLS4yYy0uNSAwLS45IDAtMS4yLjJsLTEgLjdhMy4zIDMuMyAwIDAgMC0uNSAxbC0uMyAxLjNWNTBsLjUgMS41Yy4yLjUuNS45IDEgMS4yLjQuMiAxIC40IDEuNi40LjUgMCAuOS0uMSAxLjMtLjNsLjgtMWMuMy0uMy40LS44LjYtMS4zYTcuNyA3LjcgMCAwIDAgLjEtMS42Wm04LTR2MTVoLTIuN1Y0Mi40aDIuNmwuMiAyLjVabTguMyAzLjd2LjNjMCAxIDAgMS44LS4zIDIuNmE2LjMgNi4zIDAgMCAxLTEgMiA0LjQgNC40IDAgMCAxLTEuNSAxLjRjLS42LjMtMS4zLjQtMiAuNC0uOSAwLTEuNi0uMS0yLjEtLjRhNC4zIDQuMyAwIDAgMS0xLjUtMS4zIDYuOCA2LjggMCAwIDEtMS0xLjkgMTIuMiAxMi4yIDAgMCAxLS41LTIuNHYtMWMwLS44LjItMS43LjUtMi40LjItLjguNi0xLjUgMS0yYTQuMiA0LjIgMCAwIDEgMS41LTEuM2MuNS0uMyAxLjItLjQgMi0uNHMxLjUuMSAyLjEuNWE0LjMgNC4zIDAgMCAxIDEuNiAxLjNjLjQuNS43IDEuMiAxIDJsLjIgMi42Wm0tMi44LjN2LS4zbC0uMS0xLjZhNC4zIDQuMyAwIDAgMC0uNi0xLjMgMi41IDIuNSAwIDAgMC0uOS0xbC0xLjMtLjJjLS41IDAtLjkgMC0xLjIuMmwtMSAuN2EzLjMgMy4zIDAgMCAwLS41IDFsLS4zIDEuM1Y1MGwuNSAxLjVjLjIuNS41LjkgMSAxLjIuNC4yIDEgLjQgMS42LjQuNSAwIC45LS4xIDEuMy0uM2wuOC0xYy4zLS4zLjQtLjguNi0xLjNhNy43IDcuNyAwIDAgMCAuMS0xLjZabTgtMy45VjU1SDEyOFY0Mi4zaDIuN2wuMiAyLjZabS0uNCAzLjNoLTFjMC0uOS4yLTEuNy40LTIuNGE1LjggNS44IDAgMCAxIDEtMmMuNC0uNS45LTEgMS41LTEuM2E1IDUgMCAwIDEgMi4yLS40Yy42IDAgMS4xIDAgMS42LjJzLjkuNSAxLjIuOGMuNC40LjcuOC45IDEuNC4yLjYuMyAxLjMuMyAyVjU1aC0yLjl2LTguMmMwLS42IDAtMS4xLS4zLTEuNWExLjYgMS42IDAgMCAwLS43LS43Yy0uNC0uMi0uOC0uMi0xLjItLjItLjYgMC0xIDAtMS40LjMtLjQuMi0uNy40LS45LjhhMy44IDMuOCAwIDAgMC0uNSAxLjIgNi40IDYuNCAwIDAgMC0uMiAxLjVabTcuOS0uOC0xLjMuM2MwLS43IDAtMS41LjMtMi4xLjItLjcuNS0xLjMgMS0xLjhhNC41IDQuNSAwIDAgMSAxLjUtMS4zYy42LS4zIDEuMy0uNCAyLjEtLjQuNiAwIDEuMiAwIDEuNy4zYTMuMiAzLjIgMCAwIDEgMS4zLjhjLjQuNC43LjguOSAxLjQuMi42LjIgMS40LjIgMi4yVjU1aC0yLjh2LTguMmMwLS43IDAtMS4yLS4zLTEuNS0uMS0uNC0uNC0uNi0uNy0uNy0uNC0uMi0uOC0uMi0xLjItLjItLjUgMC0uOSAwLTEuMi4yYTIuMyAyLjMgMCAwIDAtLjguNyAzIDMgMCAwIDAtLjYgMWwtLjEgMS4xWiIgLz4KICA8cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9Ijc2IiB5PSI4NCIgZmlsbD0iI2ZmZiIgcng9IjQiIC8+CiAgPHJlY3Qgd2lkdGg9IjE5OSIgaGVpZ2h0PSI3NSIgeD0iLjUiIHk9Ijg0LjUiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii4xIiByeD0iMy41IiAvPgogIDxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiCiAgICBkPSJNMTAuNyA5OS45SDkuM1Y5OWgxLjRsLjctLjFhLjguOCAwIDAgMCAuMy0uNGwuMS0uNXYtLjVhLjkuOSAwIDAgMC0uNC0uNGwtLjctLjFIOS42djQuOWgtMXYtNS43aDIuMWMuNSAwIC45IDAgMS4yLjJsLjcuNy4yIDFjMCAuMyAwIC42LS4yLjlhMS42IDEuNiAwIDAgMS0uNy42bC0xLjIuMlptMy42LTMuNmgxbDEuNiA0LjQgMS42LTQuNGguOWwtMi4yIDUuN2gtLjdsLTIuMi01LjdabS0uNCAwaC45bC4xIDMuOHYxLjloLTF2LTUuN1ptNSAwaC45djUuN2gtMXYtMS45bC4xLTMuOFptNiA1di43SDIxdi0uNmwxLjktMmE3IDcgMCAwIDAgLjUtLjdsLjItLjRhMS4xIDEuMSAwIDAgMCAwLS45LjcuNyAwIDAgMC0uMy0uMy45LjkgMCAwIDAtLjUtLjEgMSAxIDAgMCAwLS41LjEuOS45IDAgMCAwLS4zLjRsLS4xLjVoLTFsLjMtLjljLjEtLjIuMy0uNS42LS42YTIgMiAwIDAgMSAxLS4zbDEgLjJjLjMuMi41LjMuNi42LjIuMi4yLjUuMi44di41bC0uMy42YTQuNSA0LjUgMCAwIDEtLjQuNWwtLjQuNS0xLjMgMS40SDI1Wm0xIC4yYzAtLjEgMC0uMi4yLS4zYS41LjUgMCAwIDEgLjQtLjJjLjIgMCAuMyAwIC40LjJhLjUuNSAwIDAgMSAuMS4zbC0uMS40YS41LjUgMCAwIDEtLjQuMS41LjUgMCAwIDEtLjQtLjEuNS41IDAgMCAxLS4yLS40Wm0zLjQtMi4xLS44LS4yLjQtMi45aDN2LjhoLTIuM2wtLjEgMS40YTEuOCAxLjggMCAwIDEgLjQtLjFsLjUtLjEuNy4xYy4yIDAgLjQuMi41LjQuMi4xLjMuNC40LjZhMi41IDIuNSAwIDAgMSAwIDEuNWMwIC4yLS4yLjQtLjQuNmExLjYgMS42IDAgMCAxLS41LjQgMi4xIDIuMSAwIDAgMS0uOS4ybC0uNi0uMWExLjkgMS45IDAgMCAxLS42LS4zIDEuNiAxLjYgMCAwIDEtLjQtLjUgMS43IDEuNyAwIDAgMS0uMi0uN2guOWMwIC4xIDAgLjMuMi40IDAgLjIuMS4zLjMuM2wuNC4xYTEgMSAwIDAgMCAuNCAwIC44LjggMCAwIDAgLjMtLjNsLjItLjRhMS44IDEuOCAwIDAgMCAwLS45IDEgMSAwIDAgMC0uMi0uNC44LjggMCAwIDAtLjMtLjIgMSAxIDAgMCAwLS41IDBoLS41YTEuNiAxLjYgMCAwIDAtLjMuM1oiIC8+CiAgPGcgY2xpcC1wYXRoPSJ1cmwoI2EpIj4KICAgIDxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii4xIiBzdHJva2Utd2lkdGg9Ii4zIiBkPSJNNy41IDE0MS43SDE5MSIgLz4KICAgIDxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii45IiBzdHJva2Utd2lkdGg9IjEuMyIKICAgICAgZD0iTTguNyAxNDQuOGg5LjhsMTMgMi4yIDExLjItOCAyMS42IDUuOCAyNC44LTUuOSA3LjIuMmM0LjggMi4yIDE0LjUgNy45IDE1IDcuOWgxNC41bDEyLjQtN2gxMC40bDkuMi00IDEyLjQgMS4xIDE5LjYgMi44IiAvPgogICAgPHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNCIgZmlsbC1ydWxlPSJldmVub2RkIgogICAgICBkPSJNOC41IDEzNS41SDh2MmguNVYxMzZIMTB2LS41SDguNVptMCAxMS41di0xLjZIOHYyLjFoMnYtLjVIOC41Wm0xODMtMTEuNWguNXYyaC0uNVYxMzZIMTkwdi0uNWgxLjVabTAgMTEuNXYtMS42aC41djIuMWgtMnYtLjVoMS41WiIKICAgICAgY2xpcC1ydWxlPSJldmVub2RkIiAvPgogIDwvZz4KICA8cGF0aCBmaWxsPSIjRkZBNjAwIgogICAgZD0iTTc4LjEgMTIwLjNoMS43Yy42IDAgMS4yIDAgMS42LS4zLjUtLjIuOC0uNSAxLTEgLjItLjMuMy0uOC4zLTEuMyAwLS42IDAtMS0uMy0xLjRhMiAyIDAgMCAwLS44LS45IDMgMyAwIDAgMC0xLjUtLjMgMyAzIDAgMCAwLTEuNC4zIDIuMyAyLjMgMCAwIDAtMSAuOGMtLjIuNC0uMy44LS4zIDEuM2gtMi44YzAtLjkuMi0xLjcuNy0yLjQuNS0uNyAxLjEtMS4yIDItMS42LjgtLjUgMS43LS43IDIuNy0uN2E3IDcgMCAwIDEgMyAuNmMuOC40IDEuNC45IDEuOSAxLjYuNC43LjYgMS42LjYgMi43IDAgLjUgMCAxLS4zIDEuNS0uMi41LS42IDEtMSAxLjMtLjQuNC0xIC44LTEuNyAxLS42LjMtMS40LjQtMi4zLjRoLTJ2LTEuNlptMCAyLjJWMTIxaDIuMWMxIDAgMS45LjEgMi42LjMuNy4zIDEuMy42IDEuNyAxIC41LjQuOC45IDEgMS40LjIuNS4zIDEgLjMgMS43YTUgNSAwIDAgMS0uNCAyLjEgNC4zIDQuMyAwIDAgMS0xLjIgMS42IDUuNCA1LjQgMCAwIDEtMS45IDEgNy41IDcuNSAwIDAgMS0yLjIuMyA3LjMgNy4zIDAgMCAxLTIuMi0uMyA1LjggNS44IDAgMCAxLTEuOC0xIDQuNSA0LjUgMCAwIDEtMS4yLTEuNGMtLjMtLjctLjUtMS40LS41LTIuMmgyLjhjMCAuNi4yIDEgLjQgMS40YTIuNSAyLjUgMCAwIDAgMSAuOWMuNC4yIDEgLjMgMS41LjMuNiAwIDEuMSAwIDEuNS0uM2EyLjIgMi4yIDAgMCAwIDEtMWMuMy0uMy40LS44LjQtMS40YTMgMyAwIDAgMC0uNC0xLjYgMi4zIDIuMyAwIDAgMC0xLjEtMWMtLjUtLjItMS0uMy0xLjctLjNINzhabTIxLjUtMi4zdjIuOGMwIDEuMy0uMiAyLjQtLjQgMy40YTYgNiAwIDAgMS0xLjIgMi4zIDQuNCA0LjQgMCAwIDEtMS43IDEuMyA2IDYgMCAwIDEtMi4zLjQgNi41IDYuNSAwIDAgMS0xLjktLjMgNC42IDQuNiAwIDAgMS0xLjUtLjggNC44IDQuOCAwIDAgMS0xLjItMS40IDggOCAwIDAgMS0uOC0yLjFjLS4yLS44LS4yLTEuOC0uMi0yLjh2LTIuOGMwLTEuNCAwLTIuNS40LTMuNGE2IDYgMCAwIDEgMS0yLjMgNC4zIDQuMyAwIDAgMSAxLjktMS4zIDYuNSA2LjUgMCAwIDEgNC4yLS4xIDQuNCA0LjQgMCAwIDEgMS41LjhjLjQuMy44LjggMS4xIDEuNC40LjYuNiAxLjMuOCAyIC4yLjkuMyAxLjguMyAyLjlabS0yLjkgMy4yVjExOGwtLjQtMS40YTIuNiAyLjYgMCAwIDAtLjYtLjkgMiAyIDAgMCAwLS44LS41IDIuNyAyLjcgMCAwIDAtMS0uMWMtLjQgMC0uOCAwLTEuMS4yLS40LjItLjcuNS0uOS44bC0uNSAxLjUtLjIgMi4ydjMuNmwuMSAxLjhjMCAuNS4yIDEgLjQgMS4zbC41IDEgLjguNSAxIC4xYy40IDAgLjggMCAxLjItLjIuMy0uMi42LS41LjgtLjkuMy0uMy40LS44LjYtMS40bC4xLTIuMlptMTAuMi40aDF2Ni4zaC0uOWwtLjEtMS40di00LjlabS4yIDMuNGguNGwtLjEgMS4yYTMuMSAzLjEgMCAwIDEtLjQgMSAxLjggMS44IDAgMCAxLS42LjZjLS4zLjItLjYuMy0xIC4zLS4zIDAtLjUgMC0uOC0uMmExLjUgMS41IDAgMCAxLS42LS4zIDIgMiAwIDAgMS0uNC0uN2wtLjItMS43aC43djFjMCAuMi4yLjUuNC42IDAgLjIuMi4yLjQuM2gxLjNsLjYtLjUuMi0uN2E0LjYgNC42IDAgMCAwIDAtMVptLTQuMS0zLjRoMXY4LjhoLTF2LTguOFptMTEuMiAwaDF2Ni4yYzAgLjYtLjEgMS0uMyAxLjRhMi4yIDIuMiAwIDAgMS0xIDEgMyAzIDAgMCAxLTIuMS4xIDMuMSAzLjEgMCAwIDEtMS0uMyAyLjIgMi4yIDAgMCAxLS42LS43bC42LS42LjguNy45LjIuOC0uMi42LS42Yy4yLS4zLjItLjYuMi0xdi00LjhsLjEtMS40Wk0xMTAgMTI3bC4xLTEuNC41LTFhMi4yIDIuMiAwIDAgMSAxLjktMWMuNCAwIC43LjIgMSAuMy4zLjEuNi4zLjguNmwuNSAxYTYgNiAwIDAgMSAuMiAxLjJ2LjVhNS41IDUuNSAwIDAgMS0uMiAxLjMgMyAzIDAgMCAxLS41IDEgMiAyIDAgMCAxLS44LjYgMi41IDIuNSAwIDAgMS0xIC4yYy0uNCAwLS44LS4xLTEtLjNhMi40IDIuNCAwIDAgMS0uOS0uNyAzLjIgMy4yIDAgMCAxLS41LTEgNC42IDQuNiAwIDAgMS0uMS0xLjNabTEgMCAuMSAxIC4zLjcuNi41LjcuMWMuNCAwIC43IDAgMS0uMmwuNS0uNi4zLS44di0xLjRhMi45IDIuOSAwIDAgMC0uMi0uNiAxLjkgMS45IDAgMCAwLS4zLS42IDEuNSAxLjUgMCAwIDAtLjYtLjQgMS43IDEuNyAwIDAgMC0uNy0uMWMtLjMgMC0uNSAwLS43LjJhMS41IDEuNSAwIDAgMC0uNi41IDIuNCAyLjQgMCAwIDAtLjMuNyA0IDQgMCAwIDAgMCAxWm0xMC4xLTUuNC0zLjUgOS4zaC0xbDMuNi05LjNoMVptMi43IDMuNXY1aC0xdi02LjNoMXYxLjNabS0uMiAxLjZoLS41bC4xLTEuMi41LTEgLjgtLjYgMS4xLS4yLjkuMWExLjcgMS43IDAgMCAxIDEgMS4xbC4yIDF2NC4yaC0xVjEyNmMwLS40LS4xLS42LS4zLS44YTEgMSAwIDAgMC0uNC0uNSAxLjcgMS43IDAgMCAwLS43LS4xYy0uNCAwLS42IDAtLjguMmExLjQgMS40IDAgMCAwLS42LjRsLS4yLjdhMy44IDMuOCAwIDAgMC0uMS44Wm00LS42LS42LjNhMy4yIDMuMiAwIDAgMSAuMS0xIDIuNyAyLjcgMCAwIDEgLjUtLjljLjItLjMuNS0uNC44LS42bDEtLjIgMSAuMWMuMi4xLjUuMi42LjRsLjUuNy4xIDF2NC4yaC0xVjEyNmMwLS40LS4xLS43LS4zLS45YS45LjkgMCAwIDAtLjQtLjQgMS45IDEuOSAwIDAgMC0uNy0uMWwtLjcuMWExLjMgMS4zIDAgMCAwLS40LjRjLS4yLjEtLjMuMy0uMy40YTEuOCAxLjggMCAwIDAtLjEuNlptNi44LTIuNmguOWEuNi42IDAgMCAwIC4zLS4zLjYuNiAwIDAgMCAuMS0uMy42LjYgMCAwIDAtLjItLjRsLS42LS4yYTEgMSAwIDAgMC0uNS4xLjUuNSAwIDAgMC0uMi40aC0xYzAtLjIuMS0uNS4zLS42LjEtLjIuMy0uNC42LS41bC44LS4yIDEgLjJjLjIgMCAuNC4yLjUuNC4yLjIuMi41LjIuOGExIDEgMCAwIDEtLjEuNWwtLjYuNC0uOS4yaC0uNnYtLjVabTAgLjd2LS40aC42bDEgLjFjLjIgMCAuNC4yLjUuNC4yLjIuMi40LjIuNiAwIC4zIDAgLjYtLjIuOGExLjUgMS41IDAgMCAxLS43LjRsLS45LjItLjgtLjFhMS41IDEuNSAwIDAgMS0uNy0uNWwtLjItLjhoMWwuMS41LjcuMS42LS4xYS42LjYgMCAwIDAgLjItLjUuNi42IDAgMCAwLS4xLS40LjYuNiAwIDAgMC0uMy0uMmgtMVoiIC8+CiAgPHBhdGggZmlsbD0idXJsKCNiKSIgZmlsbC1vcGFjaXR5PSIuMyIgZD0iTTE1OCAxMjAuN2g1LjR2Ni43bC0yLjcgMi0yLjctMnYtNi43WiIgLz4KICA8cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiCiAgICBkPSJNMTYwLjUgMTI5LjhhLjMuMyAwIDAgMCAuNSAwbDIuNC0yLjQtLjUtLjUtMS4zIDEuM2EuMy4zIDAgMCAxLS41LS4ydi0zLjNoLS43djMuM2EuMy4zIDAgMCAxLS41LjJsLTEuNC0xLjMtLjUuNSAyLjUgMi40WiIKICAgIGNsaXAtcnVsZT0iZXZlbm9kZCIgLz4KICA8cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IgogICAgZD0iTTE3MC4zIDEyNS4ydjFoLTMuMXYtMWgzWm01LTQuNHY4LjZIMTc0di03bC0yIC44VjEyMmwzLjMtMS4yaC4yWm03LjQgMGguMXYxLjFjLS42IDAtMSAuMS0xLjQuM2EyLjQgMi40IDAgMCAwLS45LjZsLS41IDEtLjIgMS4xdjEuNGwuMS45LjQuNi40LjQuNi4yYy4yIDAgLjQgMCAuNi0uMmwuNC0uMy4zLS42YTIuNSAyLjUgMCAwIDAgMC0xLjQgMS43IDEuNyAwIDAgMC0uMy0uNSAxLjIgMS4yIDAgMCAwLS40LS40IDEuMiAxLjIgMCAwIDAtLjYtLjJsLS44LjJhMS42IDEuNiAwIDAgMC0uNS42IDEuMyAxLjMgMCAwIDAtLjMuNmwtLjUtLjJhMyAzIDAgMCAxIC4yLTFsLjYtLjcuNy0uNCAxLS4yYy40IDAgLjcgMCAxIC4ybC44LjdjLjIuMi4zLjUuNC45YTMuNyAzLjcgMCAwIDEgMCAyLjJjLS4xLjQtLjMuNy0uNiAxYTIuNSAyLjUgMCAwIDEtLjguNmwtMS4yLjJjLS40IDAtLjktLjEtMS4yLS4zYTIuNyAyLjcgMCAwIDEtMS0uNyAzLjMgMy4zIDAgMCAxLS41LTEgNC4yIDQuMiAwIDAgMS0uMi0xLjR2LS41YzAtLjcuMS0xLjMuMy0xLjlhNC41IDQuNSAwIDAgMSAuNy0xLjUgMy41IDMuNSAwIDAgMSAxLjMtMWMuNi0uMyAxLjItLjQgMi0uNFptMi44IDMuN3YtLjNsLjEtLjcuNS0uNS43LS4xLjcuMS40LjUuMi43di4zYzAgLjMgMCAuNS0uMi43YTEuMiAxLjIgMCAwIDEtLjQuNWwtLjcuMS0uNy0uMWExLjIgMS4yIDAgMCAxLS41LS41IDEuNCAxLjQgMCAwIDEtLjEtLjdabS42LS4zdi43YS43LjcgMCAwIDAgLjMuMy43LjcgMCAwIDAgLjQuMS43LjcgMCAwIDAgLjQgMCAuNy43IDAgMCAwIC4yLS40IDEgMSAwIDAgMCAwLS40di0uM2ExIDEgMCAwIDAgMC0uNC42LjYgMCAwIDAtLjYtLjQuNy43IDAgMCAwLS40LjEuNy43IDAgMCAwLS4zLjMgMSAxIDAgMCAwIDAgLjRabTIuNSA0di0uNGwuMS0uNy41LS40LjctLjJjLjMgMCAuNSAwIC43LjIuMiAwIC4zLjIuNC40bC4yLjd2LjNjMCAuMyAwIC41LS4yLjdhMS4yIDEuMiAwIDAgMS0uNC41IDEuMyAxLjMgMCAwIDEtLjcuMmwtLjctLjJhMS4zIDEuMyAwIDAgMS0uNS0uNSAxLjQgMS40IDAgMCAxLS4xLS43Wm0uNi0uNHYuN2wuMy4zYS43LjcgMCAwIDAgLjQuMS43LjcgMCAwIDAgLjQgMCAuNy43IDAgMCAwIC4yLS40di0uN2ExIDEgMCAwIDAgMC0uNC42LjYgMCAwIDAtLjYtLjQuNi42IDAgMCAwLS42LjQgMSAxIDAgMCAwLS4xLjRabS45LTQtMy4xIDUtLjUtLjIgMy4xLTUgLjUuM1oiIC8+CiAgPGRlZnM+CiAgICA8bGluZWFyR3JhZGllbnQgaWQ9ImIiIHgxPSIxNjAuNSIgeDI9IjE2MC41IiB5MT0iMTI5LjkiIHkyPSIxMjAuNyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgogICAgICA8c3RvcCBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmY7c3RvcC1vcGFjaXR5OjEiIC8+CiAgICAgIDxzdG9wIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6I2ZmZjtzdG9wLW9wYWNpdHk6MCIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8Y2xpcFBhdGggaWQ9ImEiPgogICAgICA8cGF0aCBmaWxsPSIjZmZmIiBkPSJNOCAxMzUuNWgxODR2MTJIOHoiIC8+CiAgICA8L2NsaXBQYXRoPgogIDwvZGVmcz4KPC9zdmc+", + "description": "Contains widgets displaying air quality telemetry.", + "order": 12600, + "externalId": null, + "name": "Air quality" + }, + "widgetTypeFqns": [ + "air_quality_card", + "air_quality_card_with_background", + "horizontal_air_quality_card", + "horizontal_air_quality_card_with_background", + "air_quality_chart_card", + "air_quality_chart_card_with_background", + "simple_air_quality_chart_card", + "simple_air_quality_chart_card_with_background", + "humidity_card", + "humidity_card_with_background", + "horizontal_humidity_card", + "horizontal_humidity_card_with_background", + "humidity_chart_card", + "humidity_chart_card_with_background", + "simple_humidity_chart_card", + "simple_humidity_chart_card_with_background", + "humidity_progress_bar", + "humidity_progress_bar_with_background", + "co2_card", + "co2_card_with_background", + "horizontal_co2_card", + "horizontal_co2_card_with_background", + "co2_chart_card", + "co2_chart_card_with_background", + "simple_co2_chart_card", + "simple_co2_chart_card_with_background", + "pm2_5_card", + "pm2_5_card_with_background", + "horizontal_pm2_5_card", + "horizontal_pm2_5_card_with_background", + "pm2_5_chart_card", + "pm2_5_chart_card_with_background", + "simple_pm2_5_chart_card", + "simple_pm2_5_chart_card_with_background", + "pm10_card", + "pm10_card_with_background", + "horizontal_pm10_card", + "horizontal_pm10_card_with_background", + "pm10_chart_card", + "pm10_chart_card_with_background", + "simple_pm10_chart_card", + "simple_pm10_chart_card_with_background", + "radon_level_card", + "radon_level_card_with_background", + "horizontal_radon_level_card", + "horizontal_radon_level_card_with_background", + "radon_level_chart_card", + "radon_level_chart_card_with_background", + "simple_radon_level_chart_card", + "simple_radon_level_chart_card_with_background", + "volatile_organic_compounds_card", + "volatile_organic_compounds_card_with_background", + "horizontal_volatile_organic_compounds_card", + "horizontal_volatile_organic_compounds_card_with_background", + "volatile_organic_compounds_chart_card", + "volatile_organic_compounds_chart_card_with_background", + "simple_volatile_organic_compounds_chart_card", + "simple_volatile_organic_compounds_chart_card_with_background", + "individual_allergy_index_card", + "individual_allergy_index_card_with_background", + "horizontal_individual_allergy_index_iai_card", + "horizontal_individual_allergy_index_iai_card_with_background", + "individual_allergy_index_iai_chart_card", + "individual_allergy_index_iai_chart_card_with_background", + "simple_individual_allergy_index_iai_chart_card", + "simple_individual_allergy_index_iai_chart_card_with_background2", + "ozone_o3_card", + "ozone_o3_card_with_background", + "horizontal_ozone_o3_card", + "horizontal_ozone_o3_card_with_background", + "ozone_o3_chart_card", + "ozone_o3_chart_card_with_background", + "simple_ozone_o3_chart_card", + "simple_ozone_o3_chart_card_with_background", + "nitrogen_dioxide_no2_card", + "nitrogen_dioxide_no2_card_with_background", + "horizontal_nitrogen_dioxide_no2_card", + "horizontal_nitrogen_dioxide_no2_card_with_background", + "nitrogen_dioxide_no2_chart_card", + "nitrogen_dioxide_no2_chart_card_with_background", + "simple_nitrogen_dioxide_no2_chart_card", + "simple_nitrogen_dioxide_no2_chart_card_with_background", + "sulfur_dioxide_so2_card", + "sulfur_dioxide_so2_card_with_background", + "horizontal_sulfur_dioxide_so2_card", + "horizontal_sulfur_dioxide_so2_card_with_background", + "sulfur_dioxide_so2_chart_card", + "sulfur_dioxide_so2_chart_card_with_background", + "simple_sulfur_dioxide_so2_chart_card", + "simple_sulfur_dioxide_so2_chart_card_with_background", + "carbon_monoxide_co_card", + "carbon_monoxide_co_card_with_background", + "horizontal_carbon_monoxide_co_card", + "horizontal_carbon_monoxide_co_card_with_background", + "carbon_monoxide_co_chart_card", + "carbon_monoxide_co_chart_card_with_background", + "simple_carbon_monoxide_co_chart_card", + "simple_carbon_monoxide_co_chart_card_with_background" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/alarm_widgets.json b/application/src/main/data/json/system/widget_bundles/alarm_widgets.json index 16a155cebb..576e8aae79 100644 --- a/application/src/main/data/json/system/widget_bundles/alarm_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/alarm_widgets.json @@ -2,29 +2,14 @@ "widgetsBundle": { "alias": "alarm_widgets", "title": "Alarm widgets", - "image": "data:image/png;base64,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", - "description": "Visualization of alarms for devices, assets and other entities." + "image": "data:image/png;base64,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", + "description": "Visualization of alarms for devices, assets, and other entities.", + "order": 3000, + "externalId": null, + "name": "Alarm widgets" }, - "widgetTypes": [ - { - "alias": "alarms_table", - "name": "Alarms table", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAUUElEQVR42u2dh1sUVxeH/ctsUWMSSUETY/xSTDRKNNUYG4IgmhgVC7ZYA7FEERVUNAqoCIJdaeIConSwUAQEBO73zp5l3KAsEHcV3PN78vjcnXJ35s47554ZNr8zxBjz5MmTmpqakpKSOyrVCwiEAKmtrQ2ohkBVaWlpfX19e3u7UaleQCAESOAEVENAjA86KCpvCZyAagjhS2OVyrtxC6iGMDXqWKi8K6BSsFQKlkrBUilYCpZKwVIpWCoFS6V66WDxvisxMfGv3pSRkaEDquoHWJcuXZozZ87q1avX9qzly5ezTXV19aA47fT09JCQkObmZiXgVYJ15swZoPF8GfLz89nm9u3bg+K0+3JGqtcHrFWrVq34t3z0R8zBAhZ/zd2+ffuCBQsYGa+kwrt27Tp48CCNx48f+/T0BxZYe/bs4cy/+uqrKVOm7HLKz8H65ptvvvvuu3/++ee333575513Hjx48IIdXrx48fr163IPL1q0yL+mwiVLloSGhtox7Ny5c5LnMbi1tbXkRvv37//555+3bdsm2DHcZHgsYXlHR8drAxZn98YbbyQlJdHmvBiKW7du0T59+jQnGxYWVlxcfP/+fQaEf1nO6cfFxdE4f/48Z8dy2T4hIWHHjh1sf+XKFbbhI31+/PHH48eP52NMTMyhQ4fYjN7YpampyV/Amj17Ng3GheWVlZVDhw5lIUPz7rvv7t69m1XTpk1jLfn4hx9+yNPrc/ssKyvbu3evHf+6nVFFRQVr+W3aQGNrzZo1Y8eOXbduHT+ds0POm2++yelv3ryZ8+WYP/nkE1jp7OwMDAxkeW5u7ujRoxkHiAkICOAcV65cOWrUKLYHHQaWBKO8vHzu3LmzZs0qKiqKjY2dMGECu+/cuZOR9JeIdePGjZEjR9bV1b399tvciAIWlLBq06ZNP/744927d1kSHR3N4H7//fcQ9tw+2feXX37hxhW23M8IqhYvXsyXPnz4cKCBxfWW8DN8+HD4IG4tW7Zs6tSpnOyBAwc4cQacJGzevHkEpzFjxnBGUPjpp5+yQXx8PHsxgOxoz3oClvtUyFmzGbtD1d9//+0vYDGy3E+///47Nx9MuIPFHTZz5syCggKWcGdvduro0aM9dctrNthiAuUut89IqALHqqqqgUYVl5xZT+4Eh8MxbNiwq1evMjJffPHF5i5x/Fw+kKItNxXcEMPsDYhSgGWP57NgIeYEcgnw8uLbooEOFmJ0GFPYoi1gcTwcDHcY789aWlpgjjSfu5mbmwvgoWdhi1ucKYOjLSwsHLBUIX7dO2LEiMOHD8MWw8sgkCQRqMiNIKCxsZGMSrDjcYfpj2SA9okTJxgQ7j2GiA24i54LFrfiDz/8ICnp8ePH6ZyZ8TVP3hmIX3/91f7I5QcmxtQGa8aMGYw4M8K9e/dYyKpJkyZxw3GnkmF47pxHAdgKDg7maPkXiLl+AzZ/T01N5dQ4ZfIqbjDiNyQRXUY7Zb+OIQeXfEvS/KioKGIYeRVnx/8ww0d7PGnwkUZmZiYbMAK0SdhpSwrvRy9ICTOkpTKC9lT47GuI1tbWvncIWxztgI1V3URUfvaB0fOLGPDq9VmEHiCVBk8GgOXdFLNPYHGXcxlIBo/1LCYjtuEn9N4dU7oleScVlY/uOdYLwhoeHj4oqPK1iBqEt/Xr17+C91g8lDE9zfEoyV28/j6TNzTkp+43GY/KXvkWXj0rVaihocEXP2/Rn82ofCIFS6VgqRQslYKlYKkULJWCpVKwFCyVgqVSsFQKlkqlYKkULJVfg6VSeV0asVQ6FaoULJWCpWCpFCyVgqVSsFQqBUulYKkULJVKwVIpWCoFS6V6iWBhXYKfzgGPwoopKytLB1TVD7CuXbsmZqketHDhQnxBxGVVpfLTAgIq/wILg9DNbqI+j79fn+IY6z93Fe00t3c/f+OSOFOVrGA9R5g+Xr58GaM9HEdp4Pj7kodD7IcHEFipH5hTQ01Dl7FqbY71MW3i8zeuPGXuZypYPYpYJQbuorNnz+KliZO2WKURxniSoJgAPm84whHkWEsWyKqbN2/imrxlyxbW5uXlue754mIwjYyMFB9AHGDxQGcJH3EfxdOb3cUWli/Fk5JkkQHBxBCyjdOBcsOGDcZpa8iO+H9iBMeR4NkcERHByPgcrHPjTf5a18ebq6yPLrA6TflRkx1hHJtMW521oCzBVPxjNZ40W4EtK8zcjcWszjkKe0z5EZP7m3lcpWC5qKLmB3BwIcHFOMsFUIcMx1vcpCmvQAEPfFrfe+89Hl3xQsZRGM9q9mIJ2OGrieMt2F24cGHy5Mk4vQIoDTZ49OjRZ599BqMcM/1gFIhVLra5rMLzc+vWrfv27TNO41cqFcg0jX86a1nCV+NhyVEFBQWJ664PwboVZc4EmM4npr3VnHnb+ihgFe8y5z40VUnm0kyTFWItyV5s8lY4n7bmmotBpiLRnP/M3FxjLWEb9s1fbVruKViWKKBAiCJ44Br6wQcfCFhcURpgRLCRzT766CMYYoltD8x8CgR4Wc+fP/+yU3hWM80BFkzINlhOQiFm1BgwY+XNEkoQ5OTk0HguWOKvD5FvvfWW9Ekw87p7Z3ewyo+b9P+Z6rMWQ5lfWmFJwCIsNeRbISo7zKR98hSslofm1DBrFao+Z1LGms4OC6zCbToVPgXr22+/ZcaRXP6PP/5wByslJWXp0qWyGSGHijruYGGQz8wFQwQV+2kA710bLCIcnYMLJv0Eqr6Dhcc69ZLsPtndt2CVxlvZ+vX55trP1tRWEusCq/SQSR1v7uw12eEm3Q2s5gorD3tcaS2py7La7S1OsHYoWE/BooCHzQHTX69gkX1LKKJ+GIdEaSdmUrHJZ3fcp22wmPjwRhfn9OnTp0uahae+vOblAIRjSqp0A4vemHCZOqUT3xZKscA6bFrum+TRJmWMaa0zJftdYF2bY3KXWw0Hk+PHT8HqbLf2KolzrtpoMr5wTYUKljtY5EmUVqNneJK3D57BokFlLGY9smxZRQkGZjqq7jCrkjzZYIEUFFI+hLVQKFX8oIclJP5wA3Z8Lzt2A8s43bxJ1Jhk2ZEnBp+DZWE0z2SFOt8pdIFVddqijczpwlST9IaVpNs51v0LVlp25l2TGmjqchUsl7j83aoBEBXscmc0xI4bg3z7kKj/Ick7UyH7Un3OfXd2sSv9deuckENv9kJo41HR7l8qX5BUPbsj2zAn+qic4lO1N5qONmej1frP1WjsWttsxTCQetJgPSReX2DyVrlWEbdaalyPhNbJNLl2H1Bg8egENCdPnjzds6i6wTZMHK/w3Yl7juV34pVE0kgrwR9E77H4CyAPWXN6EyWTXm3JPwrdUrrCT8FiymtwDJzD6evPZphomnuTUan6C5ZKpWCpFCyVgqVSKVgqBUulYKlUCpZKwVIpWCqVgqVSsFSvJVgqlRYQUOlUqFKwVCoFS6VgqRQslUrBUilYKgVLpVKwVAqWSsHqSZ7/b1WsDXQ0Vf0DC0+OZcuW9fq/2GO76HOTDNXrBBb2m4PCFEQ1yMBSn/dXLOaBnqYCp8VS94376KPBvgkJxjemGwMLLDzWsEGbOHEi/mk06PAlX0EMI33r0NdfNTSY4GAzfLgZOdKEhJgukzCX2trMiBEmO9tqJyYacdrZssX0cdyYXoYONTU1/hKxutlxG6evn9g92rLt1/i3gdH/1x3b3vTvC8DHbk5unIu9O+ZH9nI8SHNzcz0cm4fv9YnCwsz06da1578pU0xUVPdYRY2Zzk6rMWYMPmZWg6co9wOrq3NtYMcz+3z9GSzc9PAF/emnn7DOlnoCWEUGBwdDAEagbEaDVeLUjfEacQ7LWiy4Mbe1e8Ni9MsvvxSHSKwi6YGI6HA4sIfE65H+cZcEFPxOca39+uuvMVd+rrmt7FhUVITXMo6SHBUGpz70bwIRAtXZs66P+M7HxroYmjbNDBuG6aBFBsMCc3xkeUaG2bTJzJ5tbeZwmIkTzdixZvx4I1URtm61Po4bZ4U09vVnsGgLEPQMHwIWlrU0KDnG1CnutAEBARweYIGIc4pogzb4wAUezmQbaMNBDrCEQvTnn3+KUxxssaXpzTWZsgPGaRUGXpQRkMOLlYvtC/EVXPhnK2UAELcN9RBssNwjlg3WjBnGWfrAYBjOsBDhVqywYCJiMYFevOjXYM2aNQuSfnRqDGPXHztuDNyBEgt42R34qCTg7vPOx9DQUEpREPz6bsddVVU1atQo6fPzzz9fsWKFr8DiknPhncfTHay0NKvhASwyB6Jdt+IGSUncQ2bqVCtpS0nxa7AoVZeWltbQpb6DFR4ezksQCghQxsLenbhlg8XpwJOY3lIWrxtY1FPZu3ev6aGAwPvvv09mJn36cCokHwoIMIcPuz5y2CEhfQULMeXJNIpFLwVguMpMlw6Ha5Wfg8XLM4IWxv9UFuHSmj7YcZM8sTHXnpQfY2NctfnIORLDOAUbLJgghrEK695x48bRm3EWwoiPj6dUzokTJ5hVeS1HqZVn7bg50+joaPxOWSLzsq+0Z48Fwf79BspJj3j06wmswEDDTcWDoQ0W0yWpWHKyCQqysn4GDbA4d/zradCVv4FFqRJK39gfk5OTmbCYccDLOIs0iUs2KVSiDLT1iL2F0CIFBKKioghX2fIQbkxBQQGe73ZMolCA3Tl27SynUAAwXSTnwII/O5snA3YhthG0WMtX2EWabIb4LtYyh8bExJDP+fbBkPkrNNRwCwlMxrKu56xcIY2J2Fl8ygpOvJjAdJ6GpH1gJ0EOOuU12MmT1jYs3L7d6o1oze6+ebZ9rV6Q+rUd92B88858ATT8ufDXnkWQIB+SWPKqRNRhptOLOmjAkqIPf3kUWZEU+FOpjP4eS6VgqRQslYKlYKkULJWCpVKwFCyVgqVSsFQKlg6ESsFSKVgqBUul8j5YKpX6vKt0KlQpWCqVgqVSsFQKlkqlYKkULJWCpVIpWCoFS6VgPVeY+5zuTWqZrOofWHl5eXP6oPnz5+McpGOqMv1ym8GAykNliqysLLXjVv0XsF6CjVF5efmLO4sUFhbmuNkrYpVTWlra307w4sJI0n0JnrZimMst9HKy0nvN9y5XX5X23YaSwroiaWfdyyl7VN7f3tLLM2qaa/wULGz1QsQN8QWEy97GjRvtjxEREdjz9bcTXN3O2nbFXVkmRpLG6cBGbmCc3rjuBnFeFzDNSw1ubGukvTM3OvLKGhodpmNRemjug7z+9naoIOFO/V0Fy7iHHGwagUMM1uPi4qTBBcYJUlJA27/PA1jYG8uObE/UMU7fZQDCUguGODvcuWGlxmmdSNiT2InjMruzHNNKAYvlrMUfEOPusLAw7P/YUfxRibj4PXnr2nR0doSkL7710NFhOhelhcLTo9ZH5Y8qbNpy7ueevHPqWvV1tuTjhYqLBXWFSXeSWzpaUkrOENVSSs9kVGayO2szKy9UNFY0tjWxqryxnM2uVl/rdK6it9OlZ9PK0h21BXkPbvoFWKdOnZoxYwZXMTIyUhxHMYMEC6431rTiJrpkyZLMzEx3sPCGLO8Sho4CFo6jmIvSwLUWo0dZQrcAivlxUFAQ3w5kM2fOZNXRo0fXrLEiBFvyUAJ5s2fPFrDWrl175MgRfFA5XywqAZEDYHvZGG9SL973W7K2Jt89TaQhXO3Iic6qyb5UeXnV5UhWJRafWHkpEiCWXljOvyxZeWnV4vPhO3NiHrU2zk1duPbqhuPFJ1gCMaxdduH3S5VXmF5ZteXG1mO3ExekBYMRI7n26voN1zcfLToGvn/n7/MLsIgKUioC87cJEybU1tZihMzl5+uIFvjesgoDd/fCE4A1efLkkC5hbusBLFmClSiOusZZb0JMv22wAgMDpQiKPRUKWDTAV+ITE6L4y1OOAJS9CNaJ4pPRubuIMfFFR86WpR4qiD9UGH/AcZBVJEwEnuL6O2ywMydawDpTmirTJfQU1lo5GaDE3orrBlZtSy1LgIm4VdpQxpKGVqvORawjzl/Aoq6OOGYjggqOyNiswxOTI5eTbyTFxuq4LzmWB7CIfLbH5OjRo93BEs48gwWOhE9GQEoceFH5D/IJSFuzthFamNqIW+uubrxcZbm3p5WnsQrg1l/bRAYmYDHf2WDdrr0trOzL398NrLoW663QHze2MpPmP7gVnO4a8wP+AxbTkLgdi996Y6OVW1DXBFNuikokJCTQOGw7oXsEi4AnsWe7U30Ei1lS3L8xOH0WLGZq2QurZqqqUFvPu2A1PWmal7qQ7Iq0iTkrPGMp8xdwsIoJ8VxZmhOdAzty/vzPYD14/JCkTR4zo3N2vbZgcevbsxi98ZBP8RKuJTDZl42Km8yDxlmhc/jw4ZVS9ao3sCiVQ+kbEjW84PsOFi7wkyZNWrlyJfGyG1h8ETO11ErhIWDYsGG+8PZdfWUtc5a0Y/J2w5Zk3MduHyclIj2CGLb5z2DROOg4TCq25sq68MyI1xMsnq1uuUmKeMnbV2ZAezPe79vzo4QTd0FbjZspPtjJ3wO44zlISCVREwJ4oJOiYvRmM+Fw1m6Q4gOyhN7I8zgYMZpnud0hT6MSBUmtBDuvi4jy8HGttOtb62uan7LLE2JlUyXPdDSsA2uuJm23Dsx0EoFa2q1iyqRTklHJlk/a21klT5FkaXRI41Gb9bBZ3VRD5DtSeOzlgcUzEdBgn3+jZ1G4hm388+/ZZHs8uqbZBv+DTRtvbCaNI1ELy4hwB9fnYJWVlS1YsKDXvxUyy/hnyXFedvi28ImPRRjjBRgv9CXgvTywjLMyUWlv6lZsUuXP0t9jqRQslYKlUrAULJWCpVKwVAqWgqVSsFQKlsqvweJvq/KbXZXKKwInoBrCX+/r6+t1OFTeEr/+AKohbW1t/KUPtjRuqV48VgESONEYwmd+jQlihC81kVa9iEAIkCRC/R9f3OEsEgi6eAAAAABJRU5ErkJggg==", - "description": "Displays alarms based on defined time window and other filters.", - "descriptor": { - "type": "alarm", - "sizeX": 10.5, - "sizeY": 6.5, - "resources": [], - "templateHtml": "\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.alarmsTableWidget.onDataUpdated();\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true,\n hasShowCondition: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-alarms-table-widget-settings", - "dataKeySettingsDirective": "tb-alarms-table-key-settings", - "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSelection\":true,\"enableSearch\":true,\"displayDetails\":true,\"allowAcknowledgment\":true,\"allowClear\":true,\"allowAssign\":true,\"displayActivity\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"-createdTime\",\"enableSelectColumnDisplay\":true,\"enableStickyAction\":false,\"enableFilter\":true},\"title\":\"Alarms table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"alarmSource\":{\"type\":\"function\",\"dataKeys\":[{\"name\":\"createdTime\",\"type\":\"alarm\",\"label\":\"Created time\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.021092237451093787},{\"name\":\"originator\",\"type\":\"alarm\",\"label\":\"Originator\",\"color\":\"#4caf50\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.2780007688856758},{\"name\":\"type\",\"type\":\"alarm\",\"label\":\"Type\",\"color\":\"#f44336\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.7323586880398418},{\"name\":\"severity\",\"type\":\"alarm\",\"label\":\"Severity\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":false,\"useCellContentFunction\":false},\"_hash\":0.09927019860088193},{\"name\":\"status\",\"type\":\"alarm\",\"label\":\"Status\",\"color\":\"#607d8b\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6588418951443418},{\"name\":\"assignee\",\"type\":\"alarm\",\"label\":\"Assignee\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.5008441077416634}],\"entityAliasId\":null,\"name\":\"alarms\"},\"alarmSearchStatus\":\"ANY\",\"alarmsPollingInterval\":5,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{},\"alarmStatusList\":[],\"alarmSeverityList\":[],\"alarmTypeList\":[],\"searchPropagatedAlarms\":false}" - } - } + "widgetTypeFqns": [ + "alarm_widgets.alarms_table", + "alarm_count" ] } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/analogue_gauges.json b/application/src/main/data/json/system/widget_bundles/analogue_gauges.json index b2ae314324..f04547a919 100644 --- a/application/src/main/data/json/system/widget_bundles/analogue_gauges.json +++ b/application/src/main/data/json/system/widget_bundles/analogue_gauges.json @@ -2,104 +2,17 @@ "widgetsBundle": { "alias": "analogue_gauges", "title": "Analogue gauges", - "image": "data:image/png;base64,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", - "description": "Display temperature, humidity, speed, and other latest values on various analog gauge widgets." + "image": "data:image/png;base64,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", + "description": "Display temperature, humidity, speed, pressure, direction, and other values on analog-style gauges.", + "order": 7000, + "externalId": null, + "name": "Analogue gauges" }, - "widgetTypes": [ - { - "alias": "analogue_compass", - "name": "Compass", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAh70lEQVR42u2d+b9V0//H799yJTKTIUPGMiXKGJlSxjJkqkwJCSGRhPDVTESKjImKkCZRKZkyD6mIhL7Pz3k9PuvztvY++5zb2WcP9+73D/ex7xn22eu9Xus9r/dq2FLQli3//PPPn3/+uXHjxvXr1//8888//PDDd9999/XXX69Zs+aLEn3++eeflYgLvcJbfICP8WG+whf5OjfhVgU/oYaWOey///4bHPzyyy/ff//9V1999VmsxA25LTfnJ/ihAljNH0y//vrrjz/+GDuSKuKMH+WnWxTImj+wUE8Ij2+++eazDBCPwcPwSAWw8o2nKoXT6tWr33rrraeffvr++++/4YYbLrjggtNOO+2YY45p3779brvttuuuu+64446NJeKCf3mRt/gAH+PDfGXkyJFTpkx5++23uVWVYqx5I6y5AQvb+bfffvv222+j5/WTTz557bXXhg8ffskllxx99NEON7UTt+rUqRO3vffee2fOnMkPRT8JHsCGDRuan8nffIC1efNmvDP8tXJT+Omnn7766qtDhgw5+eSTY0RSNO20006nnHIKPwqOeYByz8Zj8/AMoQBWhmjTpk34/BHC6cknn+zdu/dee+3VmCrtvffeffr0mTx5cjkxRiyDgTCcAljpG1I49uXk09SpU5lI7KGtw0GrVq34u8022+y+++5cHFMiLviXF90Hqqc2rVvfe9JJXfbZh0dCVz733HPlZBiqPO/wamh+kPrggw9GjBhx2GGHNWnW9913X9QWF+ecc45e6devH39bt2595ZVX6l+9wr/bbrut+wDUs2dPaT1uEvETvQ4++Jcbb3yzd2/3Ch4AWnLRokWhA2GA+bXu8wcsokGYIy4UbmnOnDnMeps2bapB0n777Sc0nHvuuVwce+yxEkhXXHHFdtttZ3GjCwes/v37e8i7/PLLJdK4CRfcUBjVTziacOaZAOvGzp29J+EOuJavv/56qHIkBpbHAFjOgEXmJNQ8nzZtGjayNFQ0nXDCCVJhAwYM0CsCyp577nneeedxcfbZZwsQ5YDlvc6HzzrrLC7OP//8tm3bWuTpJ/g5frR1q1ZfDBgAsA7cddfQB+Phu3XrNn369FDTHs+xAFa9nL7QIMKMGTO6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", - "description": "Displays latest value of the attribute or timeseries key on the compass. Expects value to be in range of 0 to 360.", - "descriptor": { - "type": "latest", - "sizeX": 6, - "sizeY": 5, - "resources": [], - "templateHtml": "", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueCompass(self.ctx, 'compass');\n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-analogue-compass-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"minorTicks\":22,\"needleCircleSize\":15,\"showBorder\":true,\"borderOuterWidth\":10,\"colorPlate\":\"#222\",\"colorMajorTicks\":\"#f5f5f5\",\"colorMinorTicks\":\"#ddd\",\"colorNeedle\":\"#f08080\",\"colorNeedleCircle\":\"#e8e8e8\",\"colorBorder\":\"#ccc\",\"majorTickFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#ccc\"},\"animation\":true,\"animationDuration\":500,\"animationRule\":\"cycle\",\"animationTarget\":\"needle\",\"majorTicks\":[\"N\",\"NE\",\"E\",\"SE\",\"S\",\"SW\",\"W\",\"NW\"]},\"title\":\"Compass\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "temperature_gauge_canvas_gauges", - "name": "Thermometer scale", - "image": "data:image/png;base64,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", - "description": "Preconfigured widget to display temperature. Allows to configure temperature range, gradient colors and other settings.", - "descriptor": { - "type": "latest", - "sizeX": 7, - "sizeY": 3, - "resources": [], - "templateHtml": "\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueLinearGauge(self.ctx, 'linearGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-analogue-linear-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 30 - 15;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":100,\"defaultColor\":\"#e64a19\",\"barStrokeWidth\":2.5,\"colorBar\":\"rgba(255, 255, 255, 0.4)\",\"colorBarEnd\":\"rgba(221, 221, 221, 0.38)\",\"showUnitTitle\":true,\"minorTicks\":2,\"valueBox\":true,\"valueInt\":3,\"colorPlate\":\"#fff\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"colorNeedleShadowUp\":\"rgba(2,255,255,0.2)\",\"colorNeedleShadowDown\":\"rgba(188,143,143,0.45)\",\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\",\"highlightsWidth\":10,\"animation\":true,\"animationDuration\":1500,\"animationRule\":\"linear\",\"showBorder\":false,\"majorTicksCount\":8,\"numbersFont\":{\"family\":\"Arial\",\"size\":18,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#263238\"},\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#78909c\"},\"unitsFont\":{\"family\":\"Roboto\",\"size\":26,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#37474f\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":40,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#444\",\"shadowColor\":\"rgba(0,0,0,0.3)\"},\"minValue\":-60,\"highlights\":[{\"from\":-60,\"to\":-40,\"color\":\"#90caf9\"},{\"from\":-40,\"to\":-20,\"color\":\"rgba(144, 202, 249, 0.66)\"},{\"from\":-20,\"to\":0,\"color\":\"rgba(144, 202, 249, 0.33)\"},{\"from\":0,\"to\":20,\"color\":\"rgba(244, 67, 54, 0.2)\"},{\"from\":20,\"to\":40,\"color\":\"rgba(244, 67, 54, 0.4)\"},{\"from\":40,\"to\":60,\"color\":\"rgba(244, 67, 54, 0.6)\"},{\"from\":60,\"to\":80,\"color\":\"rgba(244, 67, 54, 0.8)\"},{\"from\":80,\"to\":100,\"color\":\"#f44336\"}],\"unitTitle\":\"Temperature\",\"units\":\"°C\",\"colorBarProgress\":\"#90caf9\",\"colorBarProgressEnd\":\"#f44336\",\"colorBarStroke\":\"#b0bec5\",\"valueDec\":1},\"title\":\"Thermometer scale\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" - } - }, - { - "alias": "temperature_radial_gauge_canvas_gauges", - "name": "Temperature radial gauge", - "image": "data:image/png;base64,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", - "description": "Preconfigured gauge to display temperature. Allows to configure temperature range, gradient colors and other settings.", - "descriptor": { - "type": "latest", - "sizeX": 6, - "sizeY": 5, - "resources": [], - "templateHtml": "\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-analogue-radial-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":60,\"startAngle\":67.5,\"ticksAngle\":225,\"showBorder\":true,\"defaultColor\":\"#e65100\",\"needleCircleSize\":7,\"highlights\":[{\"from\":-60,\"to\":-50,\"color\":\"#42a5f5\"},{\"from\":-50,\"to\":-40,\"color\":\"rgba(66, 165, 245, 0.83)\"},{\"from\":-40,\"to\":-30,\"color\":\"rgba(66, 165, 245, 0.66)\"},{\"from\":-30,\"to\":-20,\"color\":\"rgba(66, 165, 245, 0.5)\"},{\"from\":-20,\"to\":-10,\"color\":\"rgba(66, 165, 245, 0.33)\"},{\"from\":-10,\"to\":0,\"color\":\"rgba(66, 165, 245, 0.16)\"},{\"from\":0,\"to\":10,\"color\":\"rgba(229, 115, 115, 0.16)\"},{\"from\":10,\"to\":20,\"color\":\"rgba(229, 115, 115, 0.33)\"},{\"from\":20,\"to\":30,\"color\":\"rgba(229, 115, 115, 0.5)\"},{\"from\":30,\"to\":40,\"color\":\"rgba(229, 115, 115, 0.66)\"},{\"from\":40,\"to\":50,\"color\":\"rgba(229, 115, 115, 0.83)\"},{\"from\":50,\"to\":60,\"color\":\"#e57373\"}],\"showUnitTitle\":true,\"colorPlate\":\"#cfd8dc\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"minorTicks\":2,\"valueInt\":3,\"valueDec\":1,\"highlightsWidth\":15,\"valueBox\":true,\"animation\":true,\"animationDuration\":1000,\"animationRule\":\"bounce\",\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"colorNeedleShadowDown\":\"rgba(188, 143, 143, 0.78)\",\"units\":\"°C\",\"majorTicksCount\":12,\"numbersFont\":{\"family\":\"Roboto\",\"size\":20,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#263238\"},\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#263238\"},\"unitsFont\":{\"family\":\"Roboto\",\"size\":28,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"size\":30,\"style\":\"normal\",\"weight\":\"normal\",\"shadowColor\":\"rgba(0, 0, 0, 0.49)\",\"color\":\"#444\"},\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\",\"unitTitle\":\"Temperature\",\"minValue\":-60},\"title\":\"Temperature radial gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" - } - }, - { - "alias": "speed_gauge_canvas_gauges", - "name": "Speed gauge", - "image": "data:image/png;base64,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", - "description": "Preconfigured gauge to display speed. Allows to configure speed range, gradient colors and other settings.", - "descriptor": { - "type": "latest", - "sizeX": 7, - "sizeY": 5, - "resources": [], - "templateHtml": "\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-analogue-radial-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 220) {\\n\\tvalue = 220;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":180,\"startAngle\":45,\"ticksAngle\":270,\"showBorder\":false,\"defaultColor\":\"#e65100\",\"needleCircleSize\":7,\"highlights\":[{\"from\":80,\"to\":120,\"color\":\"#fdd835\"},{\"color\":\"#e57373\",\"from\":120,\"to\":180}],\"showUnitTitle\":false,\"colorPlate\":\"#fff\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"minorTicks\":2,\"valueInt\":3,\"minValue\":0,\"valueDec\":0,\"highlightsWidth\":15,\"valueBox\":true,\"animation\":true,\"animationDuration\":1500,\"animationRule\":\"linear\",\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"colorNeedleShadowDown\":\"rgba(188, 143, 143, 0.78)\",\"units\":\"MPH\",\"majorTicksCount\":9,\"numbersFont\":{\"family\":\"Roboto\",\"size\":22,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#888\"},\"unitsFont\":{\"family\":\"Roboto\",\"size\":28,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"valueFont\":{\"size\":32,\"style\":\"normal\",\"weight\":\"normal\",\"shadowColor\":\"rgba(0, 0, 0, 0.49)\",\"color\":\"#444\",\"family\":\"Segment7Standard\"},\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\"},\"title\":\"Speed gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" - } - }, - { - "alias": "radial_gauge_canvas_gauges", - "name": "Radial gauge", - "image": "data:image/png;base64,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", - "description": "Preconfigured gauge to display any value reading. Allows to configure value range, gradient colors and other settings.", - "descriptor": { - "type": "latest", - "sizeX": 6, - "sizeY": 5, - "resources": [], - "templateHtml": "\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-analogue-radial-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nif (value < -100) {\\n\\tvalue = -100;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":100,\"startAngle\":45,\"ticksAngle\":270,\"showBorder\":true,\"defaultColor\":\"#e65100\",\"needleCircleSize\":10,\"highlights\":[],\"showUnitTitle\":true,\"colorPlate\":\"#fff\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"minorTicks\":10,\"valueInt\":3,\"valueDec\":0,\"highlightsWidth\":15,\"valueBox\":true,\"animation\":true,\"animationDuration\":500,\"animationRule\":\"cycle\",\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"numbersFont\":{\"family\":\"Roboto\",\"size\":18,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#888\"},\"unitsFont\":{\"family\":\"Roboto\",\"size\":22,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"size\":36,\"style\":\"normal\",\"weight\":\"normal\",\"shadowColor\":\"rgba(0, 0, 0, 0.49)\",\"color\":\"#444\"},\"minValue\":-100,\"colorNeedleShadowDown\":\"rgba(188,143,143,0.45)\",\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\"},\"title\":\"Radial gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" - } - } + "widgetTypeFqns": [ + "analogue_gauges.temperature_radial_gauge_canvas_gauges", + "analogue_gauges.temperature_gauge_canvas_gauges", + "analogue_gauges.speed_gauge_canvas_gauges", + "analogue_gauges.radial_gauge_canvas_gauges", + "analogue_gauges.analogue_compass" ] } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/cards.json b/application/src/main/data/json/system/widget_bundles/cards.json index 9ab33665e6..39996839f1 100644 --- a/application/src/main/data/json/system/widget_bundles/cards.json +++ b/application/src/main/data/json/system/widget_bundles/cards.json @@ -2,227 +2,25 @@ "widgetsBundle": { "alias": "cards", "title": "Cards", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAT5UlEQVR42u3d95MUVffHcf86/UEtqwxVapmzaH0VE2YR82PAgBkTKCYUAyrGR0mSxERwBXRV0EdFRcAc5vvaPnptZ5bdZWbDwJ5TW1s93T23b5/7vufe7t3PuXv8+OOP36YNZrzUqCzdNUR37fHdd9810gaz4qV01xDdlWAlWAlWgpVgJVgJVnoqwUqwEqwEK8FKsNISrAQrwdqlwfrtt9/+V7Off/55gIJ++OGH8m46bOvWrb/88sv4Aeubb76pu8vb5z/++MPvEaqPwocL8RUrVqhwfc8rr7zSYdsNBFZvb+/hhx/+n79t/fr1ree8+eabn376qY03KrPx2muvff311zaeffbZd955Z/yANWvWLF76v8psPPLII19++eVdd901QvX57LPP7rvvvqGfv3nz5ldffbXfQzfffPPy5cvre9zC999/P4JgnXfeefU9n3/+uX6pEkHMxo0bL7vsMh5UCU786quvfGXSpElz584VwByNkvUtX3nrrbdsRDmrVq1auHDhtm3bdr+h8LHHHpszZ05s//TTT9EbN2zYwG+LFy/mH+OACMGBf/75Z5zW09Pj0Pbt2wsxuuimTZvi46+//qr3vvvuu3H+hx9++MUXX/g6D3/88cdxzkcffbRo0aItW7bER+FHCa5VamW0ef75588555z4iootW7Zs5cqV0SLA0hyuUmJHAUutHOo3pnQElqpsq8xt2DNjxowzzjjjySefPP/8819++eUPPvjAxh133AGpJ5544plnnnHDp512mtNUa/r06QsWLOCOKVOmPPTQQ7rXDTfcoJCHH374+uuvd/KECROaRs/dDCwOvPjii21Mnjz58ssvF8JPPvnkK664wsYll1zCAw5xpvD21FNPcazRR5fj8xdeeIEbuff333/nYWXedttt/On8s88++8ILL5w3b56jV155ZQxbPKxMNJh+QEdRgVGJQ1pw5syZTnj77beRfdZZZ6nk7bffrtgAK/b4CowKWO5UUUoWPlxlOME69NBDL67MtQOsl156yYYa33rrrTbUbMmSJTYCLBvuVhe0EWDpYVwT0w5u1VduvPFG96AP6cfjByxBJToVkmysXr0aTwLGUUcdJbRzztSpU5cuXWq0uvba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", - "description": "Tables and cards to display latest and historical values for multiple entities simultaneously.", + "image": "data:image/png;base64,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", + "description": "Includes cards that display dynamic content based on data from one or more entities. It also includes static HTML cards.", + "order": 2000, "externalId": null, "name": "Cards" }, - "widgetTypes": [ - { - "alias": "attributes_card", - "name": "Attributes card", - "image": "data:image/png;base64,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", - "description": "Displays one or more latest values of the entity. Supports multiple entities.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "", - "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", - "controllerScript": "self.onInit = function() {\n \n self.ctx.datasourceTitleCells = [];\n self.ctx.valueCells = [];\n self.ctx.labelCells = [];\n \n for (var i=0; i < self.ctx.datasources.length; i++) {\n var tbDatasource = self.ctx.datasources[i];\n\n var datasourceId = 'tbDatasource' + i;\n self.ctx.$container.append(\n \"
\"\n );\n\n var datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n\n datasourceContainer.append(\n \"
\" +\n tbDatasource.name + \"
\"\n );\n \n var datasourceTitleCell = $('.tbDatasource-title', datasourceContainer);\n self.ctx.datasourceTitleCells.push(datasourceTitleCell);\n \n var tableId = 'table' + i;\n datasourceContainer.append(\n \"
\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n\n for (var a = 0; a < tbDatasource.dataKeys.length; a++) {\n var dataKey = tbDatasource.dataKeys[a];\n var labelCellId = 'labelCell' + a;\n var cellId = 'cell' + a;\n table.append(\"\" + dataKey.label +\n \"\");\n var labelCell = $('#' + labelCellId, table);\n self.ctx.labelCells.push(labelCell);\n var valueCell = $('#' + cellId, table);\n self.ctx.valueCells.push(valueCell);\n }\n } \n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.valueCells.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData && cellData.data && cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n var textValue;\n //toDo -> + IsNumber\n \n if (isNumber(value)) {\n var decimals = self.ctx.decimals;\n var units = self.ctx.units;\n if (cellData.dataKey.decimals || cellData.dataKey.decimals === 0) {\n decimals = cellData.dataKey.decimals;\n }\n if (cellData.dataKey.units) {\n units = cellData.dataKey.units;\n }\n txtValue = self.ctx.utils.formatValue(value, decimals, units, true);\n } else {\n txtValue = value;\n }\n self.ctx.valueCells[i].html(txtValue);\n }\n }\n \n function isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n }\n}\n\nself.onResize = function() {\n var datasourceTitleFontSize = self.ctx.height/8;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n datasourceTitleFontSize = self.ctx.width/12;\n }\n datasourceTitleFontSize = Math.min(datasourceTitleFontSize, 20);\n for (var i = 0; i < self.ctx.datasourceTitleCells.length; i++) {\n self.ctx.datasourceTitleCells[i].css('font-size', datasourceTitleFontSize+'px');\n }\n var valueFontSize = self.ctx.height/9;\n var labelFontSize = self.ctx.height/9;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n valueFontSize = self.ctx.width/15;\n labelFontSize = self.ctx.width/15;\n }\n valueFontSize = Math.min(valueFontSize, 18);\n labelFontSize = Math.min(labelFontSize, 18);\n\n for (i = 0; i < self.ctx.valueCells; i++) {\n self.ctx.valueCells[i].css('font-size', valueFontSize+'px');\n self.ctx.valueCells[i].css('height', valueFontSize*2.5+'px');\n self.ctx.valueCells[i].css('padding', '0px ' + valueFontSize + 'px');\n self.ctx.labelCells[i].css('font-size', labelFontSize+'px');\n self.ctx.labelCells[i].css('height', labelFontSize*2.5+'px');\n self.ctx.labelCells[i].css('padding', '0px ' + labelFontSize + 'px');\n } \n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}\n", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Attributes card\"}" - } - }, - { - "alias": "html_card", - "name": "HTML Card", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAATFSURBVHja7d3tUxNXFMdx/u7vEkAJsKQK6bilKaBV1Eh5GGNaSyq0WodhRuqMtQJqW5UHCwKGMRJiQpL99cWGEGaaTjtjgaTnvNpz7mYnn8neu/duXtwWFbdWlxs8Vrf21VJcy5TU4FHKrBVbtjJqgshstayWmgFSWm1ZVlPEskEMYhCDGMQgBjGIQQxiEIMYxCAGMYhBDGIQgxjEIAYxiEEMYhCDGMQgBjGIQQxiEIMYxCAG+T9ACrlcLjjyc7lcWflcbfhBfnByOZfL5cpSKZfL+f/2G+WAJ/8dJAHh4GgFeK4BaiOjMYDtyskzAIvSQ+BtI0JmKif3NRBk3PM8Lwwhz/M8LxtAzgf30RoNBJEkjUOk0j4GwJok6XaDQ8LwtSSVXVrP1of4v1w978bu5YNh4fHlfnfwfpDo15Hevm/eHkCyP8Tc/rGV44d8FaLbl/QCRrrqQopXg251bkdSdjBIIpuSNAtAbwXyqgsA595Hh3Sm0+l0Or1QDzJxLWiYhCftdSFJoKMHiPnyvwTOhIHeD9KGA3SfIYBkOsGJtIPz/GNDauIvIfEFGJdKnZwtOPUgOw4kfT0E1vQMmPb12IEZaRJCz1RKBpBJCG+ocAVixw25XArTUdACTBbqdvY56MhLisK8xisDXRyiUi+MStoDnqjUCT9Keg3Oh48MCcXj8Xg8PlwPMqQE/Kzr8HK3LiQBFyozhaI8uClJ8+AUi8CD6qi1DYymUqkksH7Mnf0zLcNIvo2In64LGYXhauLCHUlaAnb3gIUq5FXN7//qmCH98s8RmoXv9KYuZBI+ryZ9cFsKzsvngUdVyAowcDGIjWOGRKRpaINNrdWF3IUeX9LS/PymLsMlSZqCLikM01XIe+CnE3ogRqRtAE9arQtZCVqynbCoWXBWpfddMCaNgLsnLQSj1gVwM5Je3/RPAKIBYPYIJOp5nud5dw8uFIPQxO1eCOf1oRs6Jm71QOtG0FMiydFQAHkKnJlIjTgkTwIyB87uEUglbh5c6G13UGhdkvSyPUicB5J0HYBQe/Bkv+tUPnr/JCDZVq7obyF6d6MNnC+COdTmSCs4A78Hs5dkG3zyYqAy13oec8CJ/XZ6l7qFzfW9apJ/80dNsr5V2yNyGxt7tmY3iEEMYhCDGOTEIJnD6YtBDGKQpocsDrvR5K4kyX96PeoOzu5Lysbj8b2Zvv7SkepphgwBcD4vqRAPVlXR91IauASUVAhWgny6e7ohtEdCwcpdCaDTBYYDCED5sHrxdEOuFZRx4Ya06cC3ZS05sKI04EzM3demA1NlLTqweuo7ewI86U7lHdYQzCgNzEnSHXB9SYPw/amHpKBPGoFzqVQqFYUxpYEX0pHqeINAav4wvXoI+eyweq1BIDHorry+nT6E1FRnGgQyCtFqUxVyo7baGJBHlVG4mFivgRxU9xMbjQIpR4HBqYRLz84hpBwFhqYSLu5Og0C07VZ6dXTvEHKk2iAQZZOdQHcqX3NrSdlb1WrjLHXL26/T/j+s2prdIAYxiEEMYhCDGMQgBjGIQQxiEIMYxCAGMYhBDGIQgxjEIAYxiEEMYhCDGMQgBjGIQQxiEIMY5PRAmmaD4ObYsvndVst+c2yiXWppjm3NS/oTe0OjFEeU1MMAAAAASUVORK5CYII=", - "description": "Useful to inject custom HTML code. Designed to display static information only.", - "descriptor": { - "type": "static", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n var $injector = self.ctx.$scope.$injector;\n var utils = $injector.get(self.ctx.servicesMap.get('utils'));\n\n var cssParser = new cssjs();\n cssParser.testMode = false;\n var namespace = 'html-card-' + hashCode(self.ctx.settings.cardCss);\n cssParser.cssPreviewNamespace = namespace;\n cssParser.createStyleElement(namespace, self.ctx.settings.cardCss);\n self.ctx.$container.addClass(namespace);\n var evtFnPrefix = 'htmlCard_' + Math.abs(hashCode(self.ctx.settings.cardCss + self.ctx.settings.cardHtml + self.ctx.widget.id));\n cardHtml = '
' + \n self.ctx.settings.cardHtml + \n '
';\n cardHtml = replaceCustomTranslations(cardHtml);\n self.ctx.$container.html(cardHtml);\n\n window[evtFnPrefix + '_onClickFn'] = function (event) {\n self.ctx.actionsApi.elementClick(event);\n }\n\n function hashCode(str) {\n var hash = 0;\n var i, char;\n if (str.length === 0) return hash;\n for (i = 0; i < str.length; i++) {\n char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n return hash;\n }\n \n function replaceCustomTranslations (pattern) {\n var customTranslationRegex = new RegExp('{i18n:[^{}]+}', 'g');\n pattern = pattern.replace(customTranslationRegex, getTranslationText);\n return pattern;\n }\n \n function getTranslationText (variable) {\n return utils.customTranslation(variable, variable);\n \n }\n}\n\nself.actionSources = function() {\n return {\n 'elementClick': {\n name: 'widget-action.element-click',\n multiple: true\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-html-card-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"cardHtml\":\"
HTML code here
\",\"cardCss\":\".card {\\n font-weight: bold;\\n font-size: 32px;\\n color: #999;\\n width: 100%;\\n height: 100%;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n}\"},\"title\":\"HTML Card\",\"dropShadow\":true}" - } - }, - { - "alias": "timeseries_table", - "name": "Timeseries table", - "image": "data:image/png;base64,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", - "description": "Displays time series data for one or more entities. Data for each entity is displayed in a separate tab.", - "descriptor": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 6.5, - "resources": [], - "templateHtml": "\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.timeseriesTableWidget.onDataUpdated();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.timeseriesTableWidget.onLatestDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n ignoreDataUpdateOnIntervalTick: true,\n hasAdditionalLatestDataKeys: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true,\n hasShowCondition: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "latestDataKeySettingsSchema": "", - "settingsDirective": "tb-timeseries-table-widget-settings", - "dataKeySettingsDirective": "tb-timeseries-table-key-settings", - "latestDataKeySettingsDirective": "tb-timeseries-table-latest-key-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = (value + 60)/120 * 100;\\n var color = tinycolor.mix('blue', 'red', percent);\\n color.setAlpha(.5);\\n return {\\n paddingLeft: '20px',\\n color: '#ffffff',\\n background: color.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = value;\\n var backgroundColor = tinycolor('blue');\\n backgroundColor.setAlpha(value/100);\\n var color = 'blue';\\n if (value > 50) {\\n color = 'white';\\n }\\n \\n return {\\n paddingLeft: '20px',\\n color: color,\\n background: backgroundColor.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}],\"latestDataKeys\":null}],\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showTimestamp\":true,\"displayPagination\":true,\"defaultPageSize\":10},\"title\":\"Timeseries table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"displayTimewindow\":true}" - } - }, - { - "alias": "html_value_card", - "name": "HTML Value Card", - "image": "data:image/png;base64,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", - "description": "Displays configurable HTML with ability to inject values from the selected datasource. For example, display single or multiple attribute values.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n self.ctx.varsRegex = /\\$\\{([^\\}]*)\\}/g;\n self.ctx.htmlSet = false;\n \n var cssParser = new cssjs();\n cssParser.testMode = false;\n var namespace = 'html-value-card-' + hashCode(self.ctx.settings.cardCss);\n cssParser.cssPreviewNamespace = namespace;\n cssParser.createStyleElement(namespace, self.ctx.settings.cardCss);\n self.ctx.$container.addClass(namespace);\n var evtFnPrefix = 'htmlValueCard_' + Math.abs(hashCode(self.ctx.settings.cardCss + self.ctx.settings.cardHtml + self.ctx.widget.id));\n self.ctx.html = '
' + \n self.ctx.settings.cardHtml + \n '
';\n\n self.ctx.replaceInfo = processHtmlPattern(self.ctx.html, self.ctx.data);\n \n updateHtml();\n \n window[evtFnPrefix + '_onClickFn'] = function (event) {\n self.ctx.actionsApi.elementClick(event);\n }\n\n function hashCode(str) {\n var hash = 0;\n var i, char;\n if (str.length === 0) return hash;\n for (i = 0; i < str.length; i++) {\n char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n return hash;\n }\n \n function processHtmlPattern(pattern, data) {\n var match = self.ctx.varsRegex.exec(pattern);\n var replaceInfo = {};\n replaceInfo.variables = [];\n while (match !== null) {\n var variableInfo = {};\n variableInfo.dataKeyIndex = -1;\n var variable = match[0];\n var label = match[1];\n var valDec = 2;\n var splitVals = label.split(':');\n if (splitVals.length > 1) {\n label = splitVals[0];\n valDec = parseFloat(splitVals[1]);\n }\n variableInfo.variable = variable;\n variableInfo.valDec = valDec;\n if (label == 'entityName') {\n variableInfo.isEntityName = true;\n } else if (label == 'entityLabel') {\n variableInfo.isEntityLabel = true;\n } else if (label.startsWith('#')) {\n var keyIndexStr = label.substring(1);\n var n = Math.floor(Number(keyIndexStr));\n if (String(n) === keyIndexStr && n >= 0) {\n variableInfo.dataKeyIndex = n;\n }\n }\n if (!variableInfo.isEntityName && !variableInfo.isEntityLabel && variableInfo.dataKeyIndex === -1) {\n for (var i = 0; i < data.length; i++) {\n var datasourceData = data[i];\n var dataKey = datasourceData.dataKey;\n if (dataKey.label === label) {\n variableInfo.dataKeyIndex = i;\n break;\n }\n }\n }\n replaceInfo.variables.push(variableInfo);\n match = self.ctx.varsRegex.exec(pattern);\n }\n return replaceInfo;\n } \n}\n\nself.onDataUpdated = function() {\n updateHtml();\n}\n\nself.actionSources = function() {\n return {\n 'elementClick': {\n name: 'widget-action.element-click',\n multiple: true\n }\n };\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n singleEntity: true,\n dataKeysOptional: true\n };\n}\n\n\nself.onDestroy = function() {\n}\n\nfunction isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n}\n\nfunction padValue(val, dec, int) {\n var i = 0;\n var s, strVal, n;\n\n val = parseFloat(val);\n n = (val < 0);\n val = Math.abs(val);\n\n if (dec > 0) {\n strVal = val.toFixed(dec).toString().split('.');\n s = int - strVal[0].length;\n\n for (; i < s; ++i) {\n strVal[0] = '0' + strVal[0];\n }\n\n strVal = (n ? '-' : '') + strVal[0] + '.' + strVal[1];\n }\n\n else {\n strVal = Math.round(val).toString();\n s = int - strVal.length;\n\n for (; i < s; ++i) {\n strVal = '0' + strVal;\n }\n\n strVal = (n ? '-' : '') + strVal;\n }\n\n return strVal;\n}\n\nfunction updateHtml() {\n var $injector = self.ctx.$scope.$injector;\n var utils = $injector.get(self.ctx.servicesMap.get('utils'));\n var text = self.ctx.html;\n var updated = false;\n for (var v in self.ctx.replaceInfo.variables) {\n var variableInfo = self.ctx.replaceInfo.variables[v];\n var txtVal = '';\n if (variableInfo.dataKeyIndex > -1) {\n var varData = self.ctx.data[variableInfo.dataKeyIndex].data;\n if (varData.length > 0) {\n var val = varData[varData.length-1][1];\n if (isNumber(val)) {\n txtVal = padValue(val, variableInfo.valDec, 0);\n } else {\n txtVal = val;\n }\n }\n } else if (variableInfo.isEntityName) {\n if (self.ctx.defaultSubscription.datasources.length) {\n txtVal = self.ctx.defaultSubscription.datasources[0].entityName;\n } else {\n txtVal = 'Unknown';\n }\n } else if (variableInfo.isEntityLabel) {\n if (self.ctx.defaultSubscription.datasources.length) {\n txtVal = self.ctx.defaultSubscription.datasources[0].entityLabel || self.ctx.defaultSubscription.datasources[0].entityName;\n } else {\n txtVal = 'Unknown';\n }\n }\n if (typeof variableInfo.lastVal === undefined ||\n variableInfo.lastVal !== txtVal) {\n updated = true;\n variableInfo.lastVal = txtVal;\n }\n text = text.split(variableInfo.variable).join(txtVal);\n }\n if (updated || !self.ctx.htmlSet) {\n text = replaceCustomTranslations(text);\n self.ctx.$container.html(text);\n if (!self.ctx.htmlSet) {\n self.ctx.htmlSet = true;\n }\n }\n \n function replaceCustomTranslations (pattern) {\n var customTranslationRegex = new RegExp('{i18n:[^{}]+}', 'g');\n pattern = pattern.replace(customTranslationRegex, getTranslationText);\n return pattern;\n }\n \n function getTranslationText (variable) {\n return utils.customTranslation(variable, variable);\n \n }\n}\n\n", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-html-card-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"My value\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.random() * 5.45;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"cardCss\":\".card {\\n width: 100%;\\n height: 100%;\\n border: 2px solid #ccc;\\n box-sizing: border-box;\\n}\\n\\n.card .content {\\n padding: 20px;\\n display: flex;\\n flex-direction: row;\\n align-items: center;\\n justify-content: space-around;\\n height: 100%;\\n box-sizing: border-box;\\n}\\n\\n.card .content .column {\\n display: flex;\\n flex-direction: column; \\n justify-content: space-around;\\n height: 100%;\\n}\\n\\n.card h1 {\\n text-transform: uppercase;\\n color: #999;\\n font-size: 20px;\\n font-weight: bold;\\n margin: 0;\\n padding-bottom: 10px;\\n line-height: 32px;\\n}\\n\\n.card .value {\\n font-size: 38px;\\n font-weight: 200;\\n}\\n\\n.card .description {\\n font-size: 20px;\\n color: #999;\\n}\\n\",\"cardHtml\":\"
\\n
\\n
\\n

Value title

\\n
\\n ${My value:2} units.\\n
\\n
\\n Value description text\\n
\\n
\\n \\n
\\n
\"},\"title\":\"HTML Value Card\",\"dropShadow\":false,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "simple_card", - "name": "Simple card", - "image": "data:image/png;base64,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", - "description": "Designed to display single value of the selected attribute or timeseries data. Widget styles are customizable.", - "descriptor": { - "type": "latest", - "sizeX": 5, - "sizeY": 3, - "resources": [], - "templateHtml": "", - "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\n\n.tbDatasource-table {\n width: 100%;\n height: 100%;\n border-collapse: collapse;\n white-space: nowrap;\n font-weight: 100;\n text-align: right;\n}\n\n.tbDatasource-table td {\n padding: 12px;\n position: relative;\n box-sizing: border-box;\n}\n\n.tbDatasource-data-key {\n opacity: 0.7;\n font-weight: 400;\n font-size: 3.500rem;\n}\n\n.tbDatasource-value {\n font-size: 5.000rem;\n}", - "controllerScript": "self.onInit = function() {\n\n self.ctx.labelPosition = self.ctx.settings.labelPosition || 'left';\n \n if (self.ctx.datasources.length > 0) {\n var tbDatasource = self.ctx.datasources[0];\n var datasourceId = 'tbDatasource' + 0;\n self.ctx.$container.append(\n \"
\"\n );\n \n self.ctx.datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n \n var tableId = 'table' + 0;\n self.ctx.datasourceContainer.append(\n \"
\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n if (self.ctx.labelPosition === 'top') {\n table.css('text-align', 'left');\n }\n \n if (tbDatasource.dataKeys.length > 0) {\n var dataKey = tbDatasource.dataKeys[0];\n var labelCellId = 'labelCell' + 0;\n var cellId = 'cell' + 0;\n if (self.ctx.labelPosition === 'left') {\n table.append(\n \"\" +\n dataKey.label +\n \"\");\n } else {\n table.append(\n \"\" +\n dataKey.label +\n \"\");\n }\n self.ctx.labelCell = $('#' + labelCellId, table);\n self.ctx.valueCell = $('#' + cellId, table);\n self.ctx.valueCell.html(0 + ' ' + self.ctx.units);\n }\n }\n \n $.fn.textWidth = function(){\n var html_org = $(this).html();\n var html_calc = '' + html_org + '';\n $(this).html(html_calc);\n var width = $(this).find('span:first').width();\n $(this).html(html_org);\n return width;\n }; \n \n self.onResize();\n};\n\nself.onDataUpdated = function() {\n \n function isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n }\n\n if (self.ctx.valueCell && self.ctx.data.length > 0) {\n var cellData = self.ctx.data[0];\n if (cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n var txtValue;\n if (isNumber(value)) {\n var decimals = self.ctx.decimals;\n var units = self.ctx.units;\n if (self.ctx.datasources.length > 0 && self.ctx.datasources[0].dataKeys.length > 0) {\n dataKey = self.ctx.datasources[0].dataKeys[0];\n if (dataKey.decimals || dataKey.decimals === 0) {\n decimals = dataKey.decimals;\n }\n if (dataKey.units) {\n units = dataKey.units;\n }\n }\n txtValue = self.ctx.utils.formatValue(value, decimals, units, true);\n } else {\n txtValue = value;\n }\n self.ctx.valueCell.html(txtValue);\n var targetWidth;\n var minDelta;\n if (self.ctx.labelPosition === 'left') {\n targetWidth = self.ctx.datasourceContainer.width() - self.ctx.labelCell.width();\n minDelta = self.ctx.width/16 + self.ctx.padding;\n } else {\n targetWidth = self.ctx.datasourceContainer.width();\n minDelta = self.ctx.padding;\n }\n var delta = targetWidth - self.ctx.valueCell.textWidth();\n var fontSize = self.ctx.valueFontSize;\n if (targetWidth > minDelta) {\n while (delta < minDelta && fontSize > 6) {\n fontSize--;\n self.ctx.valueCell.css('font-size', fontSize+'px');\n delta = targetWidth - self.ctx.valueCell.textWidth();\n }\n }\n }\n } \n \n};\n\nself.onResize = function() {\n var labelFontSize;\n if (self.ctx.labelPosition === 'top') {\n self.ctx.padding = self.ctx.height/20;\n labelFontSize = self.ctx.height/4;\n self.ctx.valueFontSize = self.ctx.height/2;\n } else {\n self.ctx.padding = self.ctx.width/50;\n labelFontSize = self.ctx.height/2.5;\n self.ctx.valueFontSize = self.ctx.height/2;\n if (self.ctx.width/self.ctx.height <= 2.7) {\n labelFontSize = self.ctx.width/7;\n self.ctx.valueFontSize = self.ctx.width/6;\n }\n }\n self.ctx.padding = Math.min(12, self.ctx.padding);\n \n if (self.ctx.labelCell) {\n self.ctx.labelCell.css('font-size', labelFontSize+'px');\n self.ctx.labelCell.css('padding', self.ctx.padding+'px');\n }\n if (self.ctx.valueCell) {\n self.ctx.valueCell.css('font-size', self.ctx.valueFontSize+'px');\n self.ctx.valueCell.css('padding', self.ctx.padding+'px');\n } \n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n};\n\n\nself.onDestroy = function() {\n};\n", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-simple-card-widget-settings", - "hasBasicMode": true, - "basicModeDirective": "tb-simple-card-basic-config", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ff5722\",\"color\":\"rgba(255, 255, 255, 0.87)\",\"padding\":\"16px\",\"settings\":{\"labelPosition\":\"top\"},\"title\":\"Simple card\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true}" - } - }, - { - "alias": "label_widget", - "name": "Label widget", - "image": "data:image/png;base64,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", - "description": "Displays static image and multiple values of selected attributes or timeseries keys on top of it. Position of the values on the image is configurable using advanced settings.", - "descriptor": { - "type": "latest", - "sizeX": 4.5, - "sizeY": 5, - "resources": [], - "templateHtml": "", - "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", - "controllerScript": "self.onInit = function() {\n self.ctx.varsRegex = /\\$\\{([^\\}]*)\\}/g;\n \n var imageUrl = self.ctx.settings.backgroundImageUrl ? self.ctx.settings.backgroundImageUrl :\n 'data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg==';\n\n self.ctx.$container.css('background', 'url(\"'+imageUrl+'\") no-repeat');\n self.ctx.$container.css('backgroundSize', 'contain');\n self.ctx.$container.css('backgroundPosition', '50% 50%');\n \n function processLabelPattern(pattern, data) {\n var match = self.ctx.varsRegex.exec(pattern);\n var replaceInfo = {};\n replaceInfo.variables = [];\n while (match !== null) {\n var variableInfo = {};\n variableInfo.dataKeyIndex = -1;\n var variable = match[0];\n var label = match[1];\n var valDec = 2;\n var splitVals = label.split(':');\n if (splitVals.length > 1) {\n label = splitVals[0];\n valDec = parseFloat(splitVals[1]);\n }\n variableInfo.variable = variable;\n variableInfo.valDec = valDec;\n \n if (label.startsWith('#')) {\n var keyIndexStr = label.substring(1);\n var n = Math.floor(Number(keyIndexStr));\n if (String(n) === keyIndexStr && n >= 0) {\n variableInfo.dataKeyIndex = n;\n }\n }\n if (variableInfo.dataKeyIndex === -1) {\n for (var i = 0; i < data.length; i++) {\n var datasourceData = data[i];\n var dataKey = datasourceData.dataKey;\n if (dataKey.label === label) {\n variableInfo.dataKeyIndex = i;\n break;\n }\n }\n }\n replaceInfo.variables.push(variableInfo);\n match = self.ctx.varsRegex.exec(pattern);\n }\n return replaceInfo;\n }\n\n var configuredLabels = self.ctx.settings.labels;\n if (!configuredLabels) {\n configuredLabels = [];\n }\n \n self.ctx.labels = [];\n\n for (var l = 0; l < configuredLabels.length; l++) {\n var labelConfig = configuredLabels[l];\n var localConfig = {};\n localConfig.font = {};\n \n localConfig.pattern = labelConfig.pattern ? labelConfig.pattern : '${#0}';\n localConfig.x = labelConfig.x ? labelConfig.x : 0;\n localConfig.y = labelConfig.y ? labelConfig.y : 0;\n localConfig.backgroundColor = labelConfig.backgroundColor ? labelConfig.backgroundColor : 'rgba(0,0,0,0)';\n \n var settingsFont = labelConfig.font;\n if (!settingsFont) {\n settingsFont = {};\n }\n \n localConfig.font.family = settingsFont.family || 'Roboto';\n localConfig.font.size = settingsFont.size ? settingsFont.size : 6;\n localConfig.font.style = settingsFont.style ? settingsFont.style : 'normal';\n localConfig.font.weight = settingsFont.weight ? settingsFont.weight : '500';\n localConfig.font.color = settingsFont.color ? settingsFont.color : '#fff';\n \n localConfig.replaceInfo = processLabelPattern(localConfig.pattern, self.ctx.data);\n \n var label = {};\n var labelElement = $('
');\n labelElement.css('position', 'absolute');\n labelElement.css('display', 'none');\n labelElement.css('top', '0');\n labelElement.css('left', '0');\n labelElement.css('backgroundColor', localConfig.backgroundColor);\n labelElement.css('color', localConfig.font.color);\n labelElement.css('fontFamily', localConfig.font.family);\n labelElement.css('fontStyle', localConfig.font.style);\n labelElement.css('fontWeight', localConfig.font.weight);\n \n labelElement.html(localConfig.pattern);\n self.ctx.$container.append(labelElement);\n label.element = labelElement;\n label.config = localConfig;\n label.htmlSet = false;\n label.visible = false;\n self.ctx.labels.push(label);\n }\n\n var bgImg = $('');\n bgImg.hide();\n bgImg.bind('load', function()\n {\n self.ctx.bImageHeight = $(this).height();\n self.ctx.bImageWidth = $(this).width();\n self.onResize();\n });\n self.ctx.$container.append(bgImg);\n bgImg.attr('src', imageUrl);\n \n self.onDataUpdated();\n}\n\nself.onDataUpdated = function() {\n updateLabels();\n}\n\nself.onResize = function() {\n if (self.ctx.bImageHeight && self.ctx.bImageWidth) {\n var backgroundRect = {};\n var imageRatio = self.ctx.bImageWidth / self.ctx.bImageHeight;\n var componentRatio = self.ctx.width / self.ctx.height;\n if (componentRatio >= imageRatio) {\n backgroundRect.top = 0;\n backgroundRect.bottom = 1.0;\n backgroundRect.xRatio = imageRatio / componentRatio;\n backgroundRect.yRatio = 1;\n var offset = (1 - backgroundRect.xRatio) / 2;\n backgroundRect.left = offset;\n backgroundRect.right = 1 - offset;\n } else {\n backgroundRect.left = 0;\n backgroundRect.right = 1.0;\n backgroundRect.xRatio = 1;\n backgroundRect.yRatio = componentRatio / imageRatio;\n var offset = (1 - backgroundRect.yRatio) / 2;\n backgroundRect.top = offset;\n backgroundRect.bottom = 1 - offset;\n }\n for (var l = 0; l < self.ctx.labels.length; l++) {\n var label = self.ctx.labels[l];\n var labelLeft = backgroundRect.left*100 + (label.config.x*backgroundRect.xRatio);\n var labelTop = backgroundRect.top*100 + (label.config.y*backgroundRect.yRatio);\n var fontSize = self.ctx.height * backgroundRect.yRatio * label.config.font.size / 100;\n label.element.css('top', labelTop + '%');\n label.element.css('left', labelLeft + '%');\n label.element.css('fontSize', fontSize + 'px');\n if (!label.visible) {\n label.element.css('display', 'block');\n label.visible = true;\n }\n }\n } \n}\n\n\nfunction isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n}\n\nfunction padValue(val, dec, int) {\n var i = 0;\n var s, strVal, n;\n\n val = parseFloat(val);\n n = (val < 0);\n val = Math.abs(val);\n\n if (dec > 0) {\n strVal = val.toFixed(dec).toString().split('.');\n s = int - strVal[0].length;\n\n for (; i < s; ++i) {\n strVal[0] = '0' + strVal[0];\n }\n\n strVal = (n ? '-' : '') + strVal[0] + '.' + strVal[1];\n }\n\n else {\n strVal = Math.round(val).toString();\n s = int - strVal.length;\n\n for (; i < s; ++i) {\n strVal = '0' + strVal;\n }\n\n strVal = (n ? '-' : '') + strVal;\n }\n\n return strVal;\n}\n\nfunction updateLabels() {\n for (var l = 0; l < self.ctx.labels.length; l++) {\n var label = self.ctx.labels[l];\n var text = label.config.pattern;\n var replaceInfo = label.config.replaceInfo;\n var updated = false;\n for (var v = 0; v < replaceInfo.variables.length; v++) {\n var variableInfo = replaceInfo.variables[v];\n var txtVal = '';\n if (variableInfo.dataKeyIndex > -1) {\n var varData = self.ctx.data[variableInfo.dataKeyIndex].data;\n if (varData.length > 0) {\n var val = varData[varData.length-1][1];\n if (isNumber(val)) {\n txtVal = padValue(val, variableInfo.valDec, 0);\n updated = true;\n } else {\n txtVal = val;\n updated = true;\n }\n }\n }\n text = text.split(variableInfo.variable).join(txtVal);\n }\n if (updated || !label.htmlSet) {\n label.element.html(text);\n if (!label.htmlSet) {\n label.htmlSet = true;\n }\n }\n }\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n singleEntity: true\n };\n};\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-label-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"var\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"backgroundImageUrl\":\"data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg==\",\"labels\":[{\"pattern\":\"Value: ${#0:2} units.\",\"x\":20,\"y\":47,\"font\":{\"color\":\"#515151\",\"family\":\"Roboto\",\"size\":6,\"style\":\"normal\",\"weight\":\"500\"}}]},\"title\":\"Label widget\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" - } - }, - { - "alias": "entities_table", - "name": "Entities table", - "image": "data:image/png;base64,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", - "description": "Displays list of entities that match selected alias and filter with ability of additional full text search and pagination. Highly customizable using widget styles, data source keys and widget actions.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 6.5, - "resources": [], - "templateHtml": "\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.entitiesTableWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n hasDataPageLink: true,\n warnOnPageDataOverflow: false,\n dataKeysOptional: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true,\n hasShowCondition: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n },\n 'rowDoubleClick': {\n name: 'widget-action.row-double-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-entities-table-widget-settings", - "dataKeySettingsDirective": "tb-entities-table-key-settings", - "hasBasicMode": true, - "basicModeDirective": "tb-entities-table-basic-config", - "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSearch\":true,\"enableSelectColumnDisplay\":true,\"enableStickyHeader\":true,\"enableStickyAction\":true,\"reserveSpaceForHiddenAction\":\"true\",\"displayEntityName\":false,\"displayEntityLabel\":false,\"displayEntityType\":false,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"name\",\"useRowStyleFunction\":false,\"entitiesTitle\":\"Entities\"},\"title\":\"Entities table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Entity name\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return 'Simulated';\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Entity type\",\"color\":\"#607d8b\",\"settings\":{},\"_hash\":0.782057645776538,\"funcBody\":\"return 'Device';\",\"decimals\":null,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.904797781901171,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\",\"decimals\":0},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.1961430898042078,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\",\"decimals\":0},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.7678057538205878,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"decimals\":2}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"displayTimewindow\":false,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"list\",\"iconColor\":null}" - } - }, - { - "alias": "entities_hierarchy", - "name": "Entities hierarchy", - "image": "data:image/png;base64,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", - "description": "Displays hierarchy of entities based on their relations. The root of the hierarchy is defined using entity alias. By default, displays entities related using \"Contains\" relation. You may change the behaviour using advanced settings.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3.5, - "resources": [], - "templateHtml": "\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.entitiesHierarchyWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'nodeSelected': {\n name: 'widget-action.node-selected',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-entities-hierarchy-widget-settings", - "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"nodeRelationQueryFunction\":\"var entity = nodeCtx.entity;\\nvar query = {\\n parameters: {\\n rootId: entity.id.id,\\n rootType: entity.id.entityType,\\n direction: \\\"FROM\\\",\\n maxLevel: 1\\n },\\n filters: [{\\n relationType: \\\"Contains\\\",\\n entityTypes: []\\n }]\\n};\\nreturn query;\\n\\n/**\\n\\n// Function should return relations query object for current node used to fetch entity children.\\n// Function can return 'default' string value. In this case default relations query will be used.\\n\\n// The following example code will construct simple relations query that will fetch relations of type 'Contains'\\n// from the current entity.\\n\\nvar entity = nodeCtx.entity;\\nvar query = {\\n parameters: {\\n rootId: entity.id.id,\\n rootType: entity.id.entityType,\\n direction: \\\"FROM\\\",\\n maxLevel: 1\\n },\\n filters: [{\\n relationType: \\\"Contains\\\",\\n entityTypes: []\\n }]\\n};\\nreturn query;\\n\\n**/\\n\",\"nodeHasChildrenFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node has children (whether it can be expanded).\\n\\n// The following example code will restrict entities hierarchy expansion up to third level.\\n\\nreturn nodeCtx.level <= 2;\\n\\n// The next example code will restrict entities expansion according to the value of example 'nodeHasChildren' attribute.\\n\\nvar data = nodeCtx.data;\\nif (data.hasOwnProperty('nodeHasChildren') && data['nodeHasChildren'] !== null) {\\n return data['nodeHasChildren'] === 'true';\\n} else {\\n return true;\\n}\\n \\n**/\\n \",\"nodeOpenedFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node should be opened (expanded) when it first loaded.\\n\\n// The following example code will open by default nodes up to third level.\\n\\nreturn nodeCtx.level <= 2;\\n\\n**/\\n \",\"nodeDisabledFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node should be disabled (not selectable).\\n\\n// The following example code will disable current node according to the value of example 'nodeDisabled' attribute.\\n\\nvar data = nodeCtx.data;\\nif (data.hasOwnProperty('nodeDisabled') && data['nodeDisabled'] !== null) {\\n return data['nodeDisabled'] === 'true';\\n} else {\\n return false;\\n}\\n \\n**/\\n\",\"nodeIconFunction\":\"/** \\n\\n// Function should return node icon info object.\\n// Resulting object should contain either 'materialIcon' or 'iconUrl' property. \\n// Where:\\n - 'materialIcon' - name of the material icon to be used from the Material Icons Library (https://material.io/tools/icons);\\n - 'iconUrl' - url of the external image to be used as node icon.\\n// Function can return 'default' string value. In this case default icons according to entity type will be used.\\n\\n// The following example code shows how to use external image for devices which name starts with 'Test' and use \\n// default icons for the rest of entities.\\n\\nvar entity = nodeCtx.entity;\\nif (entity.id.entityType === 'DEVICE' && entity.name.startsWith('Test')) {\\n return {iconUrl: 'https://avatars1.githubusercontent.com/u/14793288?v=4&s=117'};\\n} else {\\n return 'default';\\n}\\n \\n**/\",\"nodeTextFunction\":\"/**\\n\\n// Function should return text (can be HTML code) for the current node.\\n\\n// The following example code will generate node text consisting of entity name and temperature if temperature value is present in entity attributes/timeseries.\\n\\nvar data = nodeCtx.data;\\nvar entity = nodeCtx.entity;\\nvar text = entity.name;\\nif (data.hasOwnProperty('temperature') && data['temperature'] !== null) {\\n text += \\\" \\\"+ data['temperature'] +\\\" °C\\\";\\n}\\nreturn text;\\n\\n**/\",\"nodesSortFunction\":\"/**\\n\\n// This function is used to sort nodes of the same level. Function should compare two nodes and return \\n// integer value: \\n// - less than 0 - sort nodeCtx1 to an index lower than nodeCtx2\\n// - 0 - leave nodeCtx1 and nodeCtx2 unchanged with respect to each other\\n// - greater than 0 - sort nodeCtx2 to an index lower than nodeCtx1\\n\\n// The following example code will sort entities first by entity type in alphabetical order then\\n// by entity name in alphabetical order.\\n\\nvar result = nodeCtx1.entity.id.entityType.localeCompare(nodeCtx2.entity.id.entityType);\\nif (result === 0) {\\n result = nodeCtx1.entity.name.localeCompare(nodeCtx2.entity.name);\\n}\\nreturn result;\\n \\n**/\"},\"title\":\"Entities hierarchy\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#4caf50\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.8926244886945558,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"widgetStyle\":{},\"actions\":{}}" - } - }, - { - "alias": "qr_code", - "name": "QR Code", - "image": "data:image/png;base64,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", - "description": "Displays QR code of calculated text from configured pattern or function with applied attributes or timeseries values.", - "descriptor": { - "type": "latest", - "sizeX": 4, - "sizeY": 3.5, - "resources": [], - "templateHtml": "\n", - "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.qrCodeWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n datasourcesOptional: true\n };\n}\n\nself.onDestroy = function() {\n}\n\n", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-qrcode-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7036904308224163,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"qrCodeTextPattern\":\"${entityName}\",\"useQrCodeTextFunction\":false,\"qrCodeTextFunction\":\"return data[0] ? data[0]['entityName'] : '';\"},\"title\":\"QR Code\"}" - } - }, - { - "alias": "markdown_card", - "name": "Markdown/HTML Card", - "image": "data:image/png;base64,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", - "description": "Renders markdown/HTML using configurable pattern or function with applied attributes or timeseries values.", - "descriptor": { - "type": "latest", - "sizeX": 5, - "sizeY": 3.5, - "resources": [], - "templateHtml": "\n", - "templateCss": "#container tb-markdown-widget {\n height: 100%;\n display: block;\n}\n\n#container tb-markdown-widget .tb-markdown-view {\n height: 100%;\n overflow: auto;\n}\n", - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.markdownWidget.onDataUpdated();\n}\n\nself.actionSources = function() {\n return {\n 'elementClick': {\n name: 'widget-action.element-click',\n multiple: true\n }\n };\n}\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n datasourcesOptional: true,\n hasDataPageLink: true\n };\n}\n\nself.onDestroy = function() {\n}\n\n", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-markdown-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"markdownTextPattern\":\"### Markdown/HTML card\\n - **Current entity**: ${entityName}.\\n - **Current value**: ${Random}.\",\"markdownTextFunction\":\"return '# Some title\\\\n - Entity name: ' + data[0]['entityName'];\",\"useMarkdownTextFunction\":false},\"title\":\"Markdown/HTML Card\",\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}" - } - }, - { - "alias": "dashboard_state_widget", - "name": "Dashboard state widget", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAYAAABJ/yOpAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAIABJREFUeJztnXd8VFX6/993+iSTMmmkACEghN6UKij8RBF0XSxgb1hAFiyg7iIqu9YvKoLoIjbsywo2RIrSRKQlFBECIQkhQAppk0mZPnPv748xIwnJpE1IcO/79eJFZuaU59xzP/eec+5znitYLBbJ5XIhCAIybYMkSajVaoKCggCwWq3IfdK2VPeJyul0Eh4e3tb2/M9jNpt9ApH7pH1gNptRtLURMjLtGVkgMjJ+kAUiI+MHWSAyMn6QBSIj4wdZIDIyfpAFIiPjB1kgMoiieM532dnZlJaW1vu7P4qLi8nJyWlUPe2dBgVitVqpqqrC7XY3qwJJkrBarc3KK1OTf//736SkpPg+l5WV8eijj7aozIyMDD744INzvt+0aRMZGRnk5uayZMkSADIzM8nLy2uwzLS0NLZt23bO96+++iolJSX15vvpp58ab/h5QtVQgrlz5xIUFIRCoUCtVvPggw8SHx/f6ApOnjzJ1q1buffee1tkqAx4PJ5zrsLVF67y8nL0ej0ajQabzYbH40Gv11NRUYFKpcJkMtGxY0dEUSQ3N5fY2Fj0ej1dunShQ4cOvvLOnDmDQvHHdbNDhw7cfffdVFVVsWXLFqKjo7nqqquw2+1ERUX56tZqteh0uhq2lZaWYrPZ6NixIwAPPPAAYWFhgPfCm5+fT3x8PEFBQRQXF7N8+XJ69+5NTEwMAEVFRbhcLuLj4xEEwdcus9lMcHAwarUag8EAQGFhYY12BIoGBQIwe/ZsoqOjycrK4pVXXmHhwoUolUrsdjtHjhwhNjaW+Ph4ioqKMBgMBAUF4XQ6KS4uJjY2lokTJ/rKysnJoaysjF69evkOaGlpKSdPnqRbt26+A1hWVsbx48fp2LEjsbGxAW/4n40PPviAcePG0b9/f7Zv305JSQnjxo3jiSeeYNCgQbhcLkpLSzEajRgMBg4dOsTChQvJzMzkp59+4tFHH+XLL79k3759JCQk8Ouvv9KvXz9OnTrFihUruO6668jOzqawsJB+/fqxYMECFi1aREhICPPnz2fu3Lk1BJKSkkJ5eTlnzpxh0KBBTJ48mQULFjB79mwcDgevv/46gwYNYu/evcyZM4fU1FScTiebNm3itttuY/ny5Zw5cwaDwUBFRQXz5s1jx44drFq1ik6dOjFmzBi2bNnCs88+S35+PosWLeLVV18N+HFtlECqueiiizAajeTk5BAZGcnzzz/PiBEj+Pbbb5k4cSImk4mqqiqmTJnCnj17yMrKYujQoaxbt44nnniClStXcuLECZKSkvjss8944YUXyMrK4rPPPmP48OF8/vnnPPnkk7jdbhYsWMAVV1zBihUruP322xk8eHDAG38h8tlnn7F69WqARg17jUajbxh2zz338I9//IPw8HAWLlzI8ePHfencbjfr169n2bJlqNVq3nrrrRrl9O/fn0OHDtGhQwe6d+/OiBEj2LNnD/3790en051z9e7Tpw8zZswgPz+fN998k8mTJ/t+S09Pp0uXLtx2222MGjUKgMmTJ7Nu3Tpuu+028vLyOHbsGAsWLABgyZIlvqHl4MGDmTZtGpIk8emnn1JZWcmePXsYPXp0Uw9lo2iSQAAiIiIoKSkhNTWVkSNHMn78ePr27ct//vMfnnzySf75z38yZcoUdu3axU033YTNZgO8w4Mff/yRZcuWoVKpiImJobS0lJUrV3L33XfTuXNnJEli27ZtJCUlYTQaufzyyxk+fLivDBm44447GD58OOC9yz7zzDN+02s0Gt/fWq3W5xCp0WhwuVwolUrA65gXERGBWq0+J19djBkzhs8//xybzVbnyRkcHFyjnrMZO3YslZWVzJs3j44dO54z/C4oKCAxMdH3OTExkYKCAgwGg69cQRC49NJL2bNnDykpKTz++ON+7W0uTRZISUkJUVFRpKamkpeXR3Z2NuBthMFgwGg0kp2dTXFxMV27diUtLQ2AiooKQkNDUam8VY4ZMwbwrnisXr3a9/2AAQO45JJLsNvtLFmyBLfbzfTp0wPR1j81Go0Gp9MJgMPhaHL+8PBwTCYTLpcLtVrdYBndunXDZDKxbds25s2b16S60tLSGD58ODfccAMrVqxg7dq13HLLLUiSBEBcXBwnT570pT958iTDhw+noqKiRjljxoxhyZIlBAUFERkZ2SQbGkuTBJKenk55eTldunShU6dOREZGcuuttyJJEiaTyWf0O++8w7Bhw2rkDQ8Px2KxYLFYCA4OZtOmTfTu3ZuOHTtyzTXX0L9/f99qV2pq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", - "description": "Displays specified dashboard state inside widget. Advanced widget settings allows you to configure target dashboard state to be displayed.", - "descriptor": { - "type": "static", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "\n\n
\n
Dashboard state widget
\n
(Specify dashboard state id in the advanced widget settings)
\n
\n", - "templateCss": ".dashboard-state-widget-prompt {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n font-size: 32px;\n font-weight: 500;\n color: #999;\n}\n\n.dashboard-state-widget-prompt .title {\n font-size: 32px;\n font-weight: 500;\n color: #999;\n}\n\n.dashboard-state-widget-prompt .subtitle {\n font-size: 24px;\n font-weight: normal;\n color: #999;\n text-align: center;\n}", - "controllerScript": "self.onInit = function() {\n var $injector = self.ctx.$scope.$injector;\n self.ctx.$scope.stateId = self.ctx.settings.stateId || \"\";\n self.ctx.$scope.defaultAutofillLayout = self.ctx.settings.defaultAutofillLayout;\n self.ctx.$scope.defaultMargin = self.ctx.settings.defaultMargin;\n self.ctx.$scope.defaultBackgroundColor = self.ctx.settings.defaultBackgroundColor;\n self.ctx.$scope.syncParentStateParams = self.ctx.settings.syncParentStateParams !== false;\n}\n\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-dashboard-state-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"syncParentStateParams\":true,\"defaultAutofillLayout\":true,\"defaultMargin\":0,\"defaultBackgroundColor\":\"#fff\"},\"title\":\"Dashboard state widget\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"noDataDisplayMessage\":\"\",\"showLegend\":false}" - } - } + "widgetTypeFqns": [ + "cards.value_card", + "cards.horizontal_value_card", + "cards.aggregated_value_card", + "simple_value_and_chart_card", + "progress_bar", + "cards.label_widget", + "cards.dashboard_state_widget", + "cards.qr_code", + "cards.attributes_card", + "cards.html_card", + "cards.html_value_card", + "cards.markdown_card", + "cards.simple_card" ] } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/charts.json b/application/src/main/data/json/system/widget_bundles/charts.json index 7ccce1d8c6..14c09d3fc4 100644 --- a/application/src/main/data/json/system/widget_bundles/charts.json +++ b/application/src/main/data/json/system/widget_bundles/charts.json @@ -2,210 +2,25 @@ "widgetsBundle": { "alias": "charts", "title": "Charts", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAfRklEQVR42u2deXwb1bXH89+DAqV0eV3f62uhe6Hs+9ayFRpogEcgoSwhbGFrgZbkASVhJ5BASCAhxImzmiR27MROvMZxLHmRJdvyvsi7LW+yHduSRttImveTrqzI0kgajWZkm9zzueRjbI3mzp3vnHPuOefemcdRoSKDzMN/Lperp6cHPzAMU1dXZ7Va6bjMLVGpVKWlpfihsLAwJyentrZ2VoB18ODB9vZ2/LBv376BgYEDBw7QWzWHxGQy7dq1q7Ky0u12p6am4jf79++febCMRuP69evB1tDQEEGKgjVXxG6349+urq7s7Ozq6uqqqipy79LT0/Gvw+GALZoxsEZGRgoKCiYnJ6FCCelpaWn0ns1CASVQToODg71TMjw8jN9DNYAkrVZbU1OTkpIyOjoKNYHfm81mvV5PPtnX1zc2NgbUEgcW9Ge6V7q7u4uKinJzc4m1xmWQPul0ul4qXsF9SjxPcHkBE5xgwHHixAleOPAn+FW4lfiAWq222Wy8UMLPId8zMTGBD8vuY0UWDCjVFokXlmUNBgOedrgoxORJqPkAFgjDcwKtRsE6VQQqimhHTNJlPZHT6YQjBBcNVlJyBUbBmkVisVjAEwwfbnmC55VQYFCQEjr7FKxZIfCccGvhjM/gPA54QXuNj49TsL4OAhsEVQGq4FSJoYGxDIyMtfX2mxhPWNvl5kz2uIwazCLwij9ITsGaYduHuwhVIdjvdvcOGY6pa7YcyH7zi13PvLv+0X9/RFq9rgsfMDDun200nbfJdPE2852pzAv51nVqe2E3e8IaA20wxLDIJJZBwZp7At2AsRVi+xwsW9mkS0rPef6Dz/0kBbVAsHjbzSnMe2W2in6nUxhjCI8BetGhLwrWDAhUAkYVMafogz9o2HX46LPvbQjHk3Cw/O3y7ea3SmytY9GBhnVGvEO4QqVgzbCfDk0QGsMMkpau3jU7UqPyJAIs0n6+0bT0iLVq0BnVBezv74dypWDNagFPoCqynw4v6sPkfcKREgeWvy0+ZGkciaK9ML2I1eU6ZcBymrihnSLbiDRZeQQ8YVkiOFUWm31HZv5jK9fESlU8YKGdu8m0vMgW2cGH4YbqomCFiLWbK/4Pka3iFxKc32pFTCFCgLuxvfvlNZtFIBU/WKRdmmzO7YikSlGpIJwtClYiwEKyL4KuQhAhNV+x5I2PRFMlCVikQXXZnJH0lkCbSMGSHSx4VJ2dneGyNIhwivCo5AMLDQGwPmNYe430IipzKFgzDBZsX4Ro0Mj4xIp1SfFTJS1YaJclmxvCe/QwiFFjEBQsecHCPQhXmqIfHnnxo02SUCU5WGjnJ5nL9M5wTwsse+S5LQVLRrDg7YbzSIZGT/x99UapqJIDLLTffmlSDzjDRePIAhwKVqLBwtDjseb909iEMZ4JYMLAInqrzuCK9bGhYMkIVriCBbuDXbVpp7RUyQcW8bcGTPxREuSqw6UQKFiygIUMLmZPPN4Jx23clyU5VbKCReaJVj6HClPdcFqZgiU9WGQmyBsLLSivkoMqucFCW1HEr5kQekAFPQUrEWBh+QNvuXq/YfTJtz6Zo2ChFXTxTwN5nyIKlsRg+fcrCFVjKM2TiarEgIWcD28+kdfuU7AkBgsTJV51VaSukY+qxICF9noxv0GEpxWktKKDVduufyTLIq69XGg9pcAKp67MFutz73/2NQALRc/1fNEHhB6CarZ8YBUXF2Pe2NbWlpeXFzQ06la96H5cu8t8SoEFi8Cb60grUMhKVcLAQns4yxLO0woGCynStWvXYkSw2wwXslcJBUs4WLxzb8ZqC1z1MNfBQqvkqztFMWCgDzAPQbysrCwoKoBFdikh/1KwYgULKh/zwdDfHzxWKjdVCQbrsSNW3phW4N4W8zo6OrA5yYYNG7CZBNVY8YCFLTdCN1mA1yVhpnmWgIV6+e4JF2+ywe/C+3ysxsZGPHDYWAaqC5aRC9htpqi6VXQPrkyemCUbxQx0losGiy39mZDdZnjtYHVzWwKoSjBYaB+qePYpwSpqf7A0+qyQaiwhGguVx7wZ2Z7B4R1ZBdIWMswsWOd9YXoo05KpY3knxdgjiYIlJVhwXbGsOUKSp7W7b0924UtrvpijYP1qs8e1Sm12RF5z4Q8pULCkAYu/OMlp5Nix0MQO3PkVn26dE2D9erPp8WxreitrFLYlBBxNUi5LwZIGLP78xOB2z+GaCz1ryBwjvIS9un7bLAQLJX6EJ7Mjti1GkN4hkVIKlgRghQs0cM2PTv+e33ADSZwj2BUznJjIL696NyklnoU6koD1h63mZ/M8PDEOkVvW+N0sCpYEYCGhwVs6wlX8kv8Ly3/K9a3jbH3Bgftx8YTFA9ZFW81IvmFTGoeLv1gjpv3+iFdAwZIALMwHeQopnZNc8WlRvrn0B1zPe5y101sCeFJQuwzCPty+/7GVa+UD67a9THqrY9DsZl1R8lQxbVpJvII5Apbb5TYZRTbGLDdYGEqeZ3pSFcMpSr/P9XzAWdqCCMOqw9KaxnW705euWisVWPceYPI7WZNgY4fZLua8wu8VTCEM4twAyz04YL7pcnGNWXxXAsDiMyG7xJxL+S2uYwXHNMJdCaqPIIQ9vupjcWAtzLDkgqfY9/vDM+OPTgkREnmhYMkGVu9q8Wf0EHYW1/YCZ6rl3M6glLa6oQU7+j319johYD2ZYy3pc1rYaTyxLmf1sPYz7ca28Xbh1k2gYBk+8s4ULNnAav9nXGCdbGdwrU9xxgrOPS3YjdU+NS3tIGzZO5+GgvVMnhVLAu3TqxAcLod6UPNJ1fpFR/42P2MBWnZnruRgYR6D2QwFSzawWpZKBJa/nc61PM5NgjBHEGFVTW0DBs9+CggTYBlgkDNuc9pK+8s+1KxdmLWY8ORvm+u2SA4W1BWUFgVLNrCaH5EarIDWeD83oeBckQp0LayluE/xXsXqezPvD+LJ39ZVbZAcLEwhsXSHgiUbWE0PygiWv9X9hRvL4ZwnwwF2p/1oz7G3yt+9J/O+cDz5G9SY5GAhHw//nYI1NzVWUPNEwqbGinMvV7wWFSnSPtNukhwsbIKCrA4FSzawdM8liKr+jb6IrLKI88Zpraz13qyFQsD6qmWf5GAhXYjCLAqWbGB1vpoIqmpvIzFVV1e7+Y7r7OtW++zwWLMQsBR6peRggSqwRcGSDSz9BtmpUp7j2bTXM/GzWpYsJNfLFuWT8yc37IgK1ph1THKwYAdhDSlYEoDFX4w1elh2sIwaX0Bh7bsnr/evN7mHBj06zO16pvD5CFQtO/q8kMHHchtUWQm/WXh7GWr/KVgSgIVt+3i2GGWa5KWq83XfjVccC7pk6z+e5Lwb6U7YJu7KuCccWOltBwX6TEJeojEnk9CzHCyEbXj2g0QqpuTbclGluQCJec9JDEPMgptDr9qxwxf5RGiUl6p7MhdO2ieFDD4em5jeqDOXymZmOVhkgs3zB+2N8oB1uq9a0Om0vvAE/4XffKVTW0l68aF6bShYe5q/igkUCtYMgBW0VvOkdCyXBSx4byQWmrw50oU/MN89OeFNObMPZS8JpGpJ7hNWp6BtNaCrYnohBbwr+FgULJkXU5w4Kj1ViLsSmuu05luujHzttpXLfTNUk95P1YJD9zaONgkcedQwRlh9xDslJLtXULCkAQsai8d/d9m4ku9ISVX5Tzi3Z6Wo22hkFt0l5PLZLN+GCZnth0HVnRl3C6xoIBJuJ8ioNY/zwONXXoEGO378eH5+fllZGQUrVrAmvMLzh8ZFUoJl6fDl4974l9ARuP1aV0cbSfWsUL52qD0rpjoF3p1UhWjueaTIoaWlBavsya4NqampFKxYwQrrZo1mSkaVfr3P7zmYGtMIWJY+QFI9TrczpmHHzkQxvfw8cLXSPDKpwb4g0GAHDnjenzYLd5uZ/WBx07fECOi6gyv7sRSpm1unUjcdSN3EOgj29R/GOubQODGVupPQqH9RyTy4/Tt37iRvR8FuM/gughfGaNIrJfWdosG6eodxUgoxtulEg2V6YD6+wTTSIPqmusrP9XXDaIwwrFD8+Azfg78q7tTNt3ypG7vd8sRicePgLDkew5PsdmNvmJhWfQU5ZB4fq8orcLugurRaLeppyFcTvyFOsCakkEldazxg4RuMhvp4wPJ1YzJSRJHsz8M3ZTdwyrPjAgtVo8TWfPK+eM294Cb38KDwyWCsr4IGPIEOGZ0VSrmjH1az8L8+Tves+LN3+EIGrPKY6EEISvVEFuiayBs2hZsXB76Jg4IlJVioyuV/vYx9UGR6R/OHqdTNMHP3LXGC5Un17EqKfAnQu7H67CRRHaStKVgSb8cd9k2qYjytqdSN22V9eVn8VHnarVc562siuFaYgoR7PU4EQfkD8aAoWHKBFTb242I49W9jTN1k+vTdji+locqX6rnTbZwMZ85ida1IqCXUuaRgSf/Kk9Dd9KdqK4ui7+bgb02LhaduYm22Vct5qxj4Y7zRBLGr0LQPBUt6sDA/4t/VCILFzYJSN//lS92YjOi/tFT5Uj1HMgItIKYdkee8EbLUvHNhCpYsr5XDfQrdQdmXPay6SkDqps0XX3j7NTmo8qV6Otv9vY1pP5mg5CBvtVYiwBpmDHcfuk9ce6rgmbkIFmZJYcuYkO8r/WHE1M1nPmWQmSYXVb5UzyKS6ok1ECokOp8YsIYFLnMLbY/lPTEXweK820qFdVmMas86CN5zoTaQpG66O813XC8rWJ5Uz4Y1os0ICUyEg5KCJePLxqG0+A2ih7sDnOIbwSdSnOnfqtQ9YrC9uUJWqlAJiHp50WDBCEYo1aJgyQgW5uERnmnOkOohKfBEvauDv0Gjsix7WHqk7rzRvn2zO5YKvtDEKCr9I3yAgiUjWCQWH2nt1Eg6p/zmVP3Cn4M2W/PP2ZylxdaXnpYGqftu9yA1MR7PXAoR1KgrDSlY8oLFeXe4419qQQQ7SiK4gNIae5S1eyiYsSd9zvztbjHjcMd1iF2xxYUc6+DiE8xLUPgQNedDwZIdLJLxwMLzsH+2dnHjxTF4zb3djkOptg9Wekpobrsm7IUvvMO64u/2bZucVRVc7FmacMYdVAlZDUbBSgRYJFYkIlsiqHJqbMTV3ooAPQBCc7U2ufR9XLhJQ3ynQlJBYCaRgpUgsEh+l2dd6xwR2D5QJTyOSsFKEFiELWR5xWVOZlbI9DamdWAUrMSBRQSF4ZEn6rNN4FGBKnuMtpWClWiwyDwR7rzoREoiBX4hqAosDaVgzV6wSHxLhBpIsOABgO0W9wBQsGYGLOK4wB0WVwKVAPOHvsXjDlKwZgwsIvC3IqUUEy7QT1BU4YphKFhzBiwSy0aUK9a3t8khiIbAQEdePknBmjNg+W8qrA+0RawrZKQ6OxQnlhhJBTcFa7aA5Z+FAS/cYP71iXKeUVqgKVizCyz/nLHXK3CfZbKPyMwgogak4OTJcQoK1mwEy59FQdkT7j08MOSw49dhAAjIwpnDdyKQZpMoM03BmmNgBQYmEJVASAluEP4le+kKCVqCJKwjBZSAqccrKJiWlafoYJEtLiBV9c0blMPi2tbyIXyDrku3p+orcS1Nm+7pRGurYUeSuDa0bze+oK+76UTjanFttGkDGQoROxrIARnZSxfLAAFKb0SBqoPzBHuaGJhi01hUqFCwqFCwqFCwqFChYFGhYFGhYFGhQsGiMjvAOnToEIpvioqKNBoNXktRUlKCTZQLCwvp6FCJC6yamhqAVRkidHSoxAVWQ0NDoMYqLS2Fxjp27OQ+JPPmibSYMb2RjB74tTkQnxdEDAWLHkjBogdSsOiBFCw6dsIPPGF1J9U43BQsCpZUBwKm1GbHpcnmK3aYP6u0U7AoWBIc2DXhWnzIcstXTEqjI7+TvWy7ubjXScGKAla/KMGLW0+FA7v6Bt4uHL1gi/HFnLHDdUPZ3rZOOXJhkrGybeAUHBx8nmqseA88WDN8/W7mwUxLbierHnAGtteLrQvSLA7XDHcVHWgYccFGL88b/Vhtd7oTq7Gam5sRFFWpVPX19UEpHQoWr4xY3C/kW69INm7WOoKQIq1iwLnooOUNhS3BXcXsoaTPuaXG8eJR6217mV9/afrTHmbJYctL2WML0phHD1vNDnfiwEKJfrNXgBR55yoFK5zgthxoYeGkP5dnzdAO8VJFmqKXvWEXk9bCytrVIbP7aBe7Tm1/7LD1ul3M+VvM8/dbns61rlLadtY7lH2+zsBAq/qdz+Za4Qjqje4EgYWX9mJlDsBCbodqrAjSPOq6O83yl/0WsEXuVgSw0EDVRdvMrWMuabvaO+l69bjtrlTLb7eYr9nJPJRp+b/j1o3V9iPtbLie+Lv6Zon9iu1m7ZAzEWBFcLAoWEQYh/v9MvvF28wflNsqQu5WhLa2wn7jHsZkd0vV1TqD6/Lt5uXHbLsbHFCKUTsQ2tXN1Z4LyWpjZwwsqrGIHOtmr/UqBhidcHcrQluWa338iNUtRVcztMOwwl9U2wXyFK6rac3sVTuY1eV2NwVrRg4cZtwvFlhhbhBPj3q3wrVyvRPWc3O1I86u7m92XJRk3NXgiJUq3q4WdrPzUy3P5lmtLAVL/gMxIYdLhPn5vxW2v6Zafr/FvLzIVqoXerfC3tcOj78P91lcV6FXPlLZr9vN7FQPi6AqXFcxf3w4y3LPAcuYxU3BkvhAjGiFbjBTx75darsvw/K7LWaEppYesbxV4plVRXZihIOFBp0H3wgqMNauIhb1j6NW6DzomJjOKKSr8BdfOWa7fhejG3PFP6oW9tQGq9/kzu1gP1TZHzxkuWCr+fLkSfhPiDkl1zqO97Dx361w7dXjVrDLumLoqtHuRjxscaalpE/MGQV29RON/ZJtIWkot1PgqCI2dlDHPnbEetFWc3vP1xQsTN8MjLt7wlVvcJXpnQVd7CEdixTepmo7SMLFwyShIbX32nEbwpvwx2W6W6ENpnBhhgWqUeA1Dpjct37FIBKLA8WdUXhX9zQ4Lks2Q0n7zm3t4bQ3cCXfc6gu9bz8vPttzpDGMY2BbymDc5bTwS7Ls56f5BlP0AkFL6PGwh1F1E5usNZp7M/kWZFOgSd0Swpz9Q7z+VuMP99oQmgHDx8mcYg733OAwQUvOWJ5Jtf6r2O2fxfbPq+yQ1fFyUc8Bxb1sJgK7NYYol5g06jrqh3mt0rsCevq4TbHH1MYRFZdhnSu9Af8+zopznJrLhyuXJR7bOU/9+15KbNqTYWlKEDNywVWz6Tr0m0I/jJQifKBtVlrBzfrNfattQ48auktLGKDadXDFYniI9yBTZ1l5oqrWzqORjhqbxOLFHX7eKTdGRExx+OB8psEPwOK7snC/KU+hhoX4eVkI+3p3MAWru1Fd93t9pL/CUXNrfgmU37JWNWD+vq32nSpA/qOk8Tk5+fjjWEKhQLrKbCqIijyLjyz3dQ1cP3OyVfzx5ZkjC/cN9qrlyWdfkBruHTbZKqmNbtuMHuqoAAtTdUZ+L/Cm1QHKrQlDuX3yXAPlS8oqqkMd+Br2Z6B0nXzX+BW1QjCChuUI/J1lbcptUWmkt+h847jZ3+csYFUZzQ1t+TUDb2SO3bJVuP1OyZXZHflqHLqNJ92VjxvKLvNojyXKz4tgLPTdM3VJ8Gq8IomQERoLFhcTFxhcUjk5o6vJt4ptUmusVoM4ytTkwbL/+JWfINRXaZry5oRixZ6YE1vj730PAwuNJZL+R384FJ8q79+VWW/kffAJ3Osz+dbQy9wfaX92l3MQZ1DbuUa1LqbNroUZ6PblvIL67vq3i2zIfOzUmHDzAZ+Hn6AoeQ9sFI/0dip7mzZOVD36oj2iWmmUO0Vv8YSsfwL4R+MFGbpMEbavkGcDIbpht3MnkaHNGC5rNxIhq3uAfvxs4NUsaniupb2YzMLVrV+xKK6yNMZ9Y2V/SZtX99o9SPkUbaXntuu2xt6IGJjsObJdSfHx+niEDP7816moItNgNX2N3T+RNV9ZDANNcvQ/6n4CIv5zTaVIaYTSedj4bWzJwpzFZ/k5T05ofmzo/S/vXb3G6aS3/dUPr0ydWtpR2ccYLm4iVKu/SWu7Ec+i158uqnihp6mT2t6e7uav3SU/nQKrxtU1ZkzAhbuhFH9R/KsV+tP3obGzgqT+lrSPaP6Tw3d2qAD4RdifoofyGR2yRHr/6YzEkbOhBzY2Kmylf0KPXQqv9femhL/GUWB5WI4UzU3vJfretMzBa285OTrsgOas+Q/3YozAn9jL/8d1/I4N7idY1qEgeX2vC+57UXPK5OnvqTn+FUp+WugDKbr4cm+hvdYpW8KM155T0N3TSLB0vTbxyvvJZoJ1jDkM46uliRHyU/IwzZc+1x1nyHwjNtqHVduN2MePT+VgcpX6aOcsaC2FfpvRLtkuPaF1racyn6z6GtEz/UN75A7hQegtqdDksGJBaz2f3J187mKX05303zNUnJeRd5t3dX/6G7aBJOk7e0n97uiOh3ThEnN7fbi7047pOwnXMN9XN86zqjm3CGG0txgqn+Jq/jVyc9rLuC639mubkKZUbjUSpV+tL/+Dafi295DTh+tfqiupzkxYBlqnsZJWeUP67vrw30M3RuqfdmtOJN8srvp85y6fv9fVxTZzttkghMT4UT13Q29jWuMmpvdxdOeWJfy23iWoLn5mI50jdD3k5pbyHAN1L2m6bdJNTixgOVXP4qzOM2FXOMDXNcb3HAKZ6pS901gVkyKk8J1CE/GhuOqpCOfOBsXc+U/nQaZ8hyu9haP/hvL4XpXe1Sg/0+qc7mOFZxJiz6gwOPqnZE8D9KO1jbi/hEPFA+iQftkTW+XrGC1VbziddLPaeosjfphkIfHzGcZSy5sbi/yR02ht0I/r+lnWtvzhmpftJX9+uTcvvjMSc2tvY0f9ze8aVZdGfioYyqDp6upoxxqMvI1YsbDlnhcC/gtLe0F0g5OLGAhjDGayVnaOPe0PLjuhOuSZHNyLRu1Q/Do4dcjnulCoszSwQ3t5nTLOM0feFRg2Q/N2iXchNIf4YWZQGAsrYUVyAeeXXigRD0gxIIbQ5So5GBBQxOCA2em0VnUpftBGateFIq+tlcP6wlvmkwtSYMxHdE+1t66P79WF/LhrUEfZkt+PKp9tL11HzRl0DUCVjx7ZNgnKudjmiX5Uxev847Y+jW7zOs0DoEdKut3Ig6+pmL6gjvHKDea5dFMyB40P8KNZcM4BjrvOMvVO81faB2xKp7antbR6oeh54m9gLYPdKvjB6tNlwafCXeos2VbrMfCMWpVv4ZeEW2nr38TngM8/RAldBqjury/fiX8a78SCtdVENPSnu9Rb+W/CQhdngHrCRsKS4oD63paGNUV3t+f2dv4kXqAlUOdxwUWko537GdWKayxFgAhi54SLQDhBwuBsbvSmHdK7KJdJcRjvBPp08iUQt/wdpV+PP6xgxUjBrdV/bpo5wyadazqb6RvRL/63CbFOVNuU6+4rtZ1N/Y2roULFfi1jPIXTqXH2YW+9JIqlwMqHiwk51HEgwIxER1C3A8+WWlf9MWcMJpP5VgxUYp/CDBPhNHxzViV30McD5Eb0WMHWMkkFFO8+Cf/uMfmims8k8qyX8CCw9ghBCjVbYYi1LVl4msdU6mYE1X3B9rHWQQWfCTE1lHI4c+3x9oh5M+xULgjfKaMgPVBuR3Z5bJ+yYaguaMYwaQpvL6PmLjfOAofO8zJyU0aq14M8yRJVAmTG+FzWLFndJRpc9vaMhIQ5BMJFpyk2/cxQmr4I3To4wobiunCVS2iZ3DVkdY42sVKPgRNHcrxygVTc/XveF17vcCxq+4btpZfgANhZeDTzHgSaXYeOA2s8vJyZHLwLzb4i7D8a2+TQ/j9jtyhfxV6yjXtfCYRKc+Lk80ZLax8Q4AaBC9epxHXvrvi6aC4K5/HbUTuyBO3U13styYUrChg4U2bQKqpqQlZQoAVuAepv7oBNQUXbzXuqBiWJJ1+pG5o8YHxxw+dCCqAQEb9kq0TnypHE1CkoNQeGyy/x4eX4qxe1ZJjNTW8n8yp60Mmn7jAhTX1s6eeYhYeOG3vBuydjLKZCAtWEUyCFklpckhIOmptEUxfH7DjDyabt+5lXs8fTeRDWd9Vq1ct8sYOEH48CxGg2m7d9M+wI9ql3vjQj+p6mk4RxZMIHwvrbrHnE2rrJO8QAhCwrRmtLKmPWHrEirVWMzJ2CIsDKR9eijPxs9+hHqx7xevvf7exS3Pq8JEIsG5KYbD8V6YOZeg8m5VVDTqRLLs/g0EWdgbHDoFElLtM4XUGgt1IkhBNhvDjKcVHIsD6VG2XtUPbahwXJJn/lMIU97CzYexqe9r9SSESAUcV26nGRyLASkCHkBo60uaYVWNX09OJYAQi7Mj4noJ8fE3AmrUHInZ1avJBwaIHUrDogRQsChY9kIJFD5wLYGV7hYJFD5QYLIVXKFj0QOk1Vk5ODgWLHii7j0WFSmwifHWy6F2yRAs949w9I32LPZWZBitoqpgAwc5KgXuTyC2HDx+emJjAixRQl5aYM5aUlKBqFyWWgR6trILTodgO1xg4OZthsIKmigmQLK8k7HQocgRYBw8eLCgoSMwZ8ZoZ8uaiwE2jZBWDwaBUKlmWlfuMsWmshD1YRPK8krDT6XS6yclJjPvRo0cTc8Z9+/ahFrympmb//v2JOSO2QEPROZ4cAC3rif4fXnSErX5aQ1IAAAAASUVORK5CYII=", - "description": "Display timeseries data using customizable line and bar charts. Use various pie charts to display latest values.", + "image": "data:image/png;base64,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", + "description": "Display time series data using customizable line and bar charts. Use various pie charts to display the latest values.", + "order": 1000, "externalId": null, "name": "Charts" }, - "widgetTypes": [ - { - "alias": "bars", - "name": "Bars", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAA8FBMVEUhlvNMr1Bqamp5eXl7e3t8fHx9fX1+fn5/f3+AgICCgoKDg4OEhISGhoaHh4eKioqMjIyNjY2Ojo6QkJCRkZGSkpKWlpaXl5ebm5udnZ2enp6goKChoaGkpKSnp6epqamsrKyurq6xsbGzs7O1tbW2tra3t7e4uLi7u7u9vb3BwcHCwsLDw8PGxsbKysrNzc3Ozs7R0dHS0tLT09PZ2dna2trc3Nzd3d3e3t7g4ODh4eHj4+Pk5OTm5ubn5+fo6Ojp6enu7u7w8PDz8/P0Qzb09PT29vb39/f5+fn6+vr7+/v8/Pz9/f3+/v7/wQf///+dc+aLAAAAAWJLR0RPbmZBSQAAAcFJREFUeNrt3ds2AgEYhuHsaSOZbAvZi0r2YYjCJOW7/7txZhkcDNbM6h/vdwfPmlX/ybtmEorJErGCeJLadz3rkKPpZamaLTp925DHdFvSpKelU9uQ/cLKhtcdk7YqtiHruevtojch7ZZtQ0o1dcdfRqXNqm1IbVU3OaVamm/YhvQW5zIXOknnC5JUt7qEpE5fUv/5HVePy2UHAgQIECBAgAABAgQIECBxgrwGHBAgQIAAAQIECBAgQIAAAQIECJC/QRIBN0iQ+66voDMLuRp2fQWdVUhvNun6CjqrkJ0Dx/UVdEYhzXzfcX0FnVFIrlSZ2mx/LOiMQuqHh6k972NBZ/fv13G/KeiCQkIu4358EL8UdBafSGwuOxAgQIAAAQIECJDYQB4CDggQIECAAAECBAgQIECAAAECBAgQIECA/BrSufn0DjqjkEZmLXkWaUEXEmThXMeFSAu68H4j5b1IC7rQILfZTqQFXViQ1nRTL1EWdCFBnmYuJUVZ0IUEWR1xHKcXZUEXFDLwBR2XHQgQIECAAAEC5H9ChgIOCBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgxiBmv+L6Bl9pkxYph15gAAAAAElFTkSuQmCC", - "description": "Displays latest values of the attributes or timeseries data for multiple entities as separate bars.", - "descriptor": { - "type": "latest", - "sizeX": 7, - "sizeY": 5, - "resources": [ - { - "url": "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js" - } - ], - "templateHtml": "\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showTooltip = utils.defaultValue(settings.showTooltip, true);\n \n Chart.defaults.global.tooltips.enabled = settings.showTooltip;\n \n var barData = {\n labels: [],\n datasets: []\n };\n \n for (var i = 0; i < self.ctx.datasources.length; i++) {\n var datasource = self.ctx.datasources[i];\n for (var d = 0; d < datasource.dataKeys.length; d++) {\n var dataKey = datasource.dataKeys[d];\n var units = dataKey.units && dataKey.units.length ? dataKey.units : self.ctx.units;\n units = units ? (' (' + units + ')') : '';\n var dataset = {\n label: dataKey.label + units,\n data: [0],\n backgroundColor: [dataKey.color],\n borderColor: [dataKey.color],\n borderWidth: 1\n }\n barData.datasets.push(dataset);\n }\n }\n\n var ctx = $('#barChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'bar',\n data: barData,\n options: {\n responsive: false,\n maintainAspectRatio: false,\n scales: {\n yAxes: [{\n ticks: {\n beginAtZero:true\n }\n }]\n }\n }\n });\n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n var c = 0;\n for (var i = 0; i < self.ctx.chart.data.datasets.length; i++) {\n var dataset = self.ctx.chart.data.datasets[i];\n var cellData = self.ctx.data[i]; \n if (cellData.data.length > 0) {\n var decimals;\n if (typeof cellData.dataKey.decimals !== 'undefined' \n && cellData.dataKey.decimals !== null ) {\n decimals = cellData.dataKey.decimals; \n } else {\n decimals = self.ctx.decimals;\n }\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = self.ctx.utils.formatValue(tvPair[1], decimals);\n dataset.data[0] = parseFloat(value);\n }\n }\n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n self.ctx.chart.resize();\n}\n\nself.onDestroy = function() {\n self.ctx.chart.destroy();\n self.ctx.chart = null;\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-chart-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Bars\"}" - } - }, - { - "alias": "doughnut_chart_js", - "name": "Doughnut", - "image": "data:image/png;base64,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", - "description": "Displays latest values of the attributes or timeseries data for multiple entities in a doughnut chart. Supports numeric values only.", - "descriptor": { - "type": "latest", - "sizeX": 7, - "sizeY": 5, - "resources": [ - { - "url": "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js" - } - ], - "templateHtml": "\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showTooltip = utils.defaultValue(settings.showTooltip, true);\n \n Chart.defaults.global.tooltips.enabled = settings.showTooltip;\n \n var pieData = {\n labels: [],\n datasets: []\n };\n\n var dataset = {\n data: [],\n backgroundColor: [],\n borderColor: [],\n borderWidth: [],\n hoverBackgroundColor: []\n }\n \n var borderColor = self.ctx.settings.borderColor || '#fff';\n var borderWidth = typeof self.ctx.settings.borderWidth !== 'undefined' ? self.ctx.settings.borderWidth : 5;\n \n pieData.datasets.push(dataset);\n \n for (var i=0; i < self.ctx.data.length; i++) {\n var dataKey = self.ctx.data[i].dataKey;\n var units = dataKey.units && dataKey.units.length ? dataKey.units : self.ctx.units;\n units = units ? (' (' + units + ')') : '';\n pieData.labels.push(dataKey.label + units);\n dataset.data.push(0);\n var hoverBackgroundColor = tinycolor(dataKey.color).lighten(15);\n dataset.backgroundColor.push(dataKey.color);\n dataset.borderColor.push(borderColor);\n dataset.borderWidth.push(borderWidth);\n dataset.hoverBackgroundColor.push(hoverBackgroundColor.toRgbString());\n }\n\n var options = {\n responsive: false,\n maintainAspectRatio: false,\n legend: {\n display: true,\n labels: {\n fontColor: '#666'\n }\n },\n tooltips: {\n callbacks: {\n label: function(tooltipItem, data) {\n var label = data.labels[tooltipItem.index];\n var value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];\n var content = label + ': ' + value;\n var units = self.ctx.settings.units ? self.ctx.settings.units : self.ctx.units;\n if (units) {\n content += ' ' + units;\n } \n return content;\n }\n }\n }\n };\n\n if (self.ctx.settings.legend) {\n options.legend.display = self.ctx.settings.legend.display !== false;\n options.legend.labels.fontColor = self.ctx.settings.legend.labelsFontColor || '#666';\n }\n\n var ctx = $('#pieChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'doughnut',\n data: pieData,\n options: options\n });\n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.data.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData.data.length > 0) {\n var decimals;\n if (typeof cellData.dataKey.decimals !== 'undefined' \n && cellData.dataKey.decimals !== null ) {\n decimals = cellData.dataKey.decimals; \n } else {\n decimals = self.ctx.decimals;\n }\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = self.ctx.utils.formatValue(tvPair[1], decimals);\n self.ctx.chart.data.datasets[0].data[i] = parseFloat(value);\n }\n }\n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n self.ctx.chart.resize();\n}\n\nself.onDestroy = function() {\n self.ctx.chart.destroy();\n self.ctx.chart = null;\n}\n\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-doughnut-chart-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#26a69a\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#f57c00\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#afb42b\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#673ab7\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"borderWidth\":5,\"borderColor\":\"#fff\",\"legend\":{\"display\":true,\"labelsFontColor\":\"#666666\"}},\"title\":\"Doughnut\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" - } - }, - { - "alias": "pie", - "name": "Pie - Flot", - "image": "data:image/png;base64,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", - "description": "Displays latest values of the attributes or timeseries data for multiple entities in a pie chart. Supports numeric values only.", - "descriptor": { - "type": "latest", - "sizeX": 8, - "sizeY": 5, - "resources": [], - "templateHtml": "", - "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.pie-label {\n font-size: 12px;\n font-family: 'Roboto';\n font-weight: bold;\n text-align: center;\n padding: 2px;\n color: white;\n}\n", - "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'pie'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\nself.actionSources = function() {\n return {\n 'sliceClick': {\n name: 'widget-action.pie-slice-click',\n multiple: false\n }\n };\n}\n", - "settingsSchema": "{}\n", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-flot-pie-widget-settings", - "dataKeySettingsDirective": "tb-flot-pie-key-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.6114638304362894,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.9955906536344441,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.9430835931647599,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"radius\":1,\"fontColor\":\"#545454\",\"fontSize\":10,\"decimals\":1,\"legend\":{\"show\":true,\"position\":\"nw\",\"labelBoxBorderColor\":\"#CCCCCC\",\"backgroundColor\":\"#F0F0F0\",\"backgroundOpacity\":0.85},\"innerRadius\":0,\"showLabels\":true,\"showPercentages\":true,\"stroke\":{\"width\":5},\"tilt\":1,\"animatedPie\":false},\"title\":\"Pie - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" - } - }, - { - "alias": "pie_chart_js", - "name": "Pie - Chart.js", - "image": "data:image/png;base64,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", - "description": "Displays latest values of the attributes or timeseries data for multiple entities in a pie chart. Supports numeric values only.", - "descriptor": { - "type": "latest", - "sizeX": 8, - "sizeY": 5, - "resources": [ - { - "url": "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js" - } - ], - "templateHtml": "\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showTooltip = utils.defaultValue(settings.showTooltip, true);\n \n Chart.defaults.global.tooltips.enabled = settings.showTooltip;\n \n var pieData = {\n labels: [],\n datasets: []\n };\n\n var dataset = {\n data: [],\n backgroundColor: [],\n borderColor: [],\n borderWidth: [],\n hoverBackgroundColor: []\n }\n \n pieData.datasets.push(dataset);\n \n for (var i=0; i < self.ctx.data.length; i++) {\n var dataKey = self.ctx.data[i].dataKey;\n var units = dataKey.units && dataKey.units.length ? dataKey.units : self.ctx.units;\n units = units ? (' (' + units + ')') : '';\n pieData.labels.push(dataKey.label + units);\n dataset.data.push(0);\n var hoverBackgroundColor = tinycolor(dataKey.color).lighten(15);\n var borderColor = tinycolor(dataKey.color).darken();\n dataset.backgroundColor.push(dataKey.color);\n dataset.borderColor.push('#fff');\n dataset.borderWidth.push(5);\n dataset.hoverBackgroundColor.push(hoverBackgroundColor.toRgbString());\n }\n\n var ctx = $('#pieChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'pie',\n data: pieData,\n options: {\n responsive: false,\n maintainAspectRatio: false\n }\n }); \n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.data.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData.data.length > 0) {\n var decimals;\n if (typeof cellData.dataKey.decimals !== 'undefined' \n && cellData.dataKey.decimals !== null ) {\n decimals = cellData.dataKey.decimals; \n } else {\n decimals = self.ctx.decimals;\n }\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = self.ctx.utils.formatValue(tvPair[1], decimals);\n self.ctx.chart.data.datasets[0].data[i] = parseFloat(value);\n }\n }\n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n self.ctx.chart.resize();\n}\n\nself.onDestroy = function() {\n self.ctx.chart.destroy();\n self.ctx.chart = null;\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-chart-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Pie - Chart.js\"}" - } - }, - { - "alias": "polar_area_chart_js", - "name": "Polar Area", - "image": "data:image/png;base64,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", - "description": "Displays latest values of the attributes or timeseries data for multiple entities in a polar area chart. Supports numeric values only.", - "descriptor": { - "type": "latest", - "sizeX": 7, - "sizeY": 5, - "resources": [ - { - "url": "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js" - } - ], - "templateHtml": "\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showTooltip = utils.defaultValue(settings.showTooltip, true);\n \n Chart.defaults.global.tooltips.enabled = settings.showTooltip;\n \n var pieData = {\n labels: [],\n datasets: []\n };\n\n var dataset = {\n data: [],\n backgroundColor: [],\n borderColor: [],\n borderWidth: [],\n hoverBackgroundColor: []\n }\n\n pieData.datasets.push(dataset);\n \n for (var i = 0; i < self.ctx.data.length; i++) {\n var dataKey = self.ctx.data[i].dataKey;\n var units = dataKey.units && dataKey.units.length ? dataKey.units : self.ctx.units;\n units = units ? (' (' + units + ')') : '';\n pieData.labels.push(dataKey.label + units);\n dataset.data.push(0);\n var hoverBackgroundColor = tinycolor(dataKey.color).lighten(15);\n var borderColor = tinycolor(dataKey.color).darken();\n dataset.backgroundColor.push(dataKey.color);\n dataset.borderColor.push('#fff');\n dataset.borderWidth.push(5);\n dataset.hoverBackgroundColor.push(hoverBackgroundColor.toRgbString());\n }\n \n var floatingPoint;\n if (typeof self.ctx.decimals !== 'undefined' && self.ctx.decimals !== null) {\n floatingPoint = self.ctx.widget.config.decimals;\n } else {\n floatingPoint = 2;\n }\n\n\n var ctx = $('#pieChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'polarArea',\n data: pieData,\n options: {\n responsive: false,\n maintainAspectRatio: false,\n scale: {\n ticks: {\n callback: function(tick) {\n \treturn tick.toFixed(floatingPoint);\n }\n }\n }\n }\n });\n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.data.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData.data.length > 0) {\n var decimals;\n if (typeof cellData.dataKey.decimals !== 'undefined' \n && cellData.dataKey.decimals !== null ) {\n decimals = cellData.dataKey.decimals; \n } else {\n decimals = self.ctx.decimals;\n }\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = self.ctx.utils.formatValue(tvPair[1], decimals);\n self.ctx.chart.data.datasets[0].data[i] = parseFloat(value);\n }\n }\n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n if (self.ctx.height >= 70) {\n try {\n self.ctx.chart.resize();\n } catch (e) {}\n }\n}\n\nself.onDestroy = function() {\n self.ctx.chart.destroy();\n self.ctx.chart = null;\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-chart-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fifth\",\"color\":\"#607d8b\",\"settings\":{},\"_hash\":0.2074391823443591,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Polar Area\"}" - } - }, - { - "alias": "radar_chart_js", - "name": "Radar", - "image": "data:image/png;base64,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", - "description": "Displays latest values of the attributes or timeseries data for multiple entities in a radar chart. Supports numeric values only.", - "descriptor": { - "type": "latest", - "sizeX": 7, - "sizeY": 5, - "resources": [ - { - "url": "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js" - } - ], - "templateHtml": "\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showTooltip = utils.defaultValue(settings.showTooltip, true);\n \n Chart.defaults.global.tooltips.enabled = settings.showTooltip;\n \n var barData = {\n labels: [],\n datasets: []\n };\n\n var backgroundColor = tinycolor(self.ctx.data[0].dataKey.color);\n backgroundColor.setAlpha(0.2);\n var borderColor = tinycolor(self.ctx.data[0].dataKey.color);\n borderColor.setAlpha(1);\n var dataset = {\n label: self.ctx.datasources[0].name,\n data: [],\n backgroundColor: backgroundColor.toRgbString(),\n borderColor: borderColor.toRgbString(),\n pointBackgroundColor: borderColor.toRgbString(),\n pointBorderColor: borderColor.darken().toRgbString(),\n borderWidth: 1\n }\n \n barData.datasets.push(dataset);\n \n for (var i = 0; i < self.ctx.data.length; i++) {\n var dataKey = self.ctx.data[i].dataKey;\n var units = dataKey.units && dataKey.units.length ? dataKey.units : self.ctx.units;\n units = units ? (' (' + units + ')') : '';\n barData.labels.push(dataKey.label + units);\n dataset.data.push(0);\n }\n \n var floatingPoint;\n if (typeof self.ctx.decimals !== 'undefined' && self.ctx.decimals !== null) {\n floatingPoint = self.ctx.widget.config.decimals;\n } else {\n floatingPoint = 2;\n }\n\n var ctx = $('#radarChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'radar',\n data: barData,\n options: {\n responsive: false,\n maintainAspectRatio: false,\n scale: {\n ticks: {\n callback: function(tick) {\n \treturn tick.toFixed(floatingPoint);\n }\n }\n }\n }\n });\n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.data.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData.data.length > 0) {\n var decimals;\n if (typeof cellData.dataKey.decimals !== 'undefined' \n && cellData.dataKey.decimals !== null ) {\n decimals = cellData.dataKey.decimals; \n } else {\n decimals = self.ctx.decimals;\n }\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = self.ctx.utils.formatValue(tvPair[1], decimals);\n self.ctx.chart.data.datasets[0].data[i] = parseFloat(value);\n }\n } \n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n if (self.ctx.height >= 70) {\n self.ctx.chart.resize();\n }\n}\n\nself.onDestroy = function() {\n self.ctx.chart.destroy();\n self.ctx.chart = null;\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-chart-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Radar\"}" - } - }, - { - "alias": "state_chart", - "name": "State Chart", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAB9VBMVEUAAAAhlvMilvMymeE+nNVDoetInslNq/VZqOdpuPd3d3d5p5Z5suB6enp8fHyBgYGDg4ODqoqEhISIiIiKioqKuN2MjIyNjY2Ojo6QkJCRkZGSkpKUlJSVlZWWlpaXl5eYmJiZmZmampqcnJydnZ2enp6goKCgr3KhoaGioqKisXSjo6OjvsmkpKSlpaWlwdempqanp6eoqKiosGOo1vqpqampsGOqqqqqsWOrq6usrKysw9Wurq6wsLCysrK0tLS1tbW1wbK2tra3t7e4wau5ubm6urq7u7u8vLy9vb2+vr6/xbTAwMDAxbjCwsLC3ejDw8PExMTFxcXGxsbHx8fIv3jIyMjJycnKysrK5vzMzc7Nzc3Ozs7Pz8/QuDnRzcHS0tLT09PT39HU1NTU6/3VzLLV1dXV3sjW1tbX19fYuTHY2NjZ2dna0Ira2trb29vc3Nzex4De3t7f39/guyvhvCfhvCvh4eHh6Nbi4uLjvCTj4+PkyXbk5OTlvCTm5ubm693o6Ojpxl7q6urr6+vswTTs7Ozt7e3uvhju7u7vvhjv7+/wvxjw8PDx8fHyvxX0yTv09PT19fX29vb39/f4wyL4+Pj5+fn6+vr75J37+/v8whP8/Pz9/f39/v/+/v7/wQf/xRb/3HT/5JH/9tz/++/////APs7XAAAAAWJLR0Smt7AblQAAA1RJREFUeNrt3dlT01AUBvAEd8UNl2oLrdrFolZArUul1hWlVhQXFAUF1xaxIqi4FUQUrDsUClZi4nb+Th96S9NQkjDOOKZ+3wuZw8md++M25OXMlKOccJQnTUYocdRqdw8UAqTfOjG4lvr7uowOqbtAtG6o5OiGFoNDapuJHO8tFK4xOKS9msTVgoUiaQjPclnSl01snZ/y4ly2yBNZbRarbc+3yvdpt/hLawOPJp9ur7O0mRiEmzFkY1M6N/4I8qJpulzR2sBx1sgRjQuyA2pk+STdylw2VqR/FPFfWdcCvjhduiM7kVF2db1Nuspu7JGes+I76SRba7f0Wfnn/6F6It+mfo7m8TvYvh7KTiT3PQIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggPwXkFa7e7AQIP3WiUFzdl7LuBDFvJZxIYp5LeNCFPNaBn7Yc+e1KlheSYcrFCniL7LZqPl8cbp0TjavNcqu9rZJB9gdPdIJVnwjbWG1ndI95UzWM9V5rS9Ti3P4VWy1+5jXAgQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAMK+FeS3Ma2FeC/NamNfCCxEQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBA/hZkTKSkwSGn7VUJOmOLV241NiRSRZ2uYcvt+Io+Y0N83USm+MqGWEnU2JCqXqI1ZE+QhYiIy36tXR5INpOQbB5kftcmK85mtdey2iJekeWqX6/3kc+TLCQTrq6eEuZCgKTsbnNXBsIZOERJMedNQnry73XpW8UAKVhIyBPScVfQE9RqEaP7iMT6g+pdw7vKbxKl3IJqV8K7OUqXPHvG9UMGykk2lz1d4g5yvNXo6QqZiA41aHT5OwQTUWBJSrWrvlMwj1jFU2f1Q8K1dCysCUmVhdenNLvMRKZt3hGNriEnxfxlGqt9aPYRkb9bP6Q1SMFrmltMukKuMT2QpckWjc/WhP2lYB/TgjzdHyCKVM/gGXnsp+qY5hbba+hIVA/ETL0+9SepsoNiTs/igGpXZIhKqVv9QVJARIfXIWqfiM1nS+qBnHdae1V7Qss8ngEijRO5a6sMCAtdqv+HfgPwpNPbU6ipOwAAAABJRU5ErkJggg==", - "description": "Displays changes to the state of the entity over time. For example, online and offline.", - "descriptor": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "resources": [], - "templateHtml": "\n", - "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.flotWidget.onDataUpdated();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.flotWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.flotWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.flotWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.flotWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true,\n hasAdditionalLatestDataKeys: true\n };\n}\n\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}", - "settingsDirective": "tb-flot-line-widget-settings", - "dataKeySettingsDirective": "tb-flot-line-key-settings", - "latestDataKeySettingsDirective": "tb-flot-latest-key-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false,\"axisPosition\":\"left\",\"showSeparateAxis\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"return Math.random() > 0.5 ? 1 : 0;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false,\"axisPosition\":\"left\"},\"_hash\":0.12775350966079668,\"funcBody\":\"return Math.random() <= 0.5 ? 1 : 0;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"tooltipValueFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\",\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":0,\"max\":1.2,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"timeForComparison\":\"previousInterval\",\"comparisonCustomIntervalValue\":7200000,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"right\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"dataKeysListForLabels\":[]},\"title\":\"State Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"legendConfig\":{\"direction\":\"column\",\",position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}" - } - }, - { - "alias": "basic_timeseries", - "name": "Timeseries Line Chart", - "image": "data:image/png;base64,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", - "description": "Displays changes to timeseries data over time. For example, temperature or humidity readings.", - "descriptor": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "resources": [], - "templateHtml": "\n", - "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.flotWidget.onDataUpdated();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.flotWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.flotWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.flotWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.flotWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n hasAdditionalLatestDataKeys: true\n };\n}\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}", - "latestDataKeySettingsSchema": "{}", - "settingsDirective": "tb-flot-line-widget-settings", - "dataKeySettingsDirective": "tb-flot-line-key-settings", - "latestDataKeySettingsDirective": "tb-flot-latest-key-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false},\"title\":\"Timeseries Line Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}" - } - }, - { - "alias": "timeseries_bars_flot", - "name": "Timeseries Bar Chart", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAABOFBMVEUAAAA3oPR3d3d6enp8fHyBgYGDg4OGhoaNjY2RkZGSkpKTk5OUlJSVlZWWlpaXl5eYmJiZmZmampqcnJydnZ2enp6goKChoaGioqKjo6OkpKSlpaWmpqanp6eoqKipqamqqqqrq6usrKytra2urq6wsLCxsbGysrK0tLS1tbW2tra3t7e4uLi5ubm6urq8vLy9vb2+vr6/v7/AwMDBwcHDw8PExMTHx8fIyMjJycnLy8vNzc3Ozs7Pz8/S0tLT09PU1NTV1dXW1tbX19fZ2dna2trb29vc3Nzd3d3e3t7f39/h4eHi4uLj4+Pk5OTl5eXm5ubn5+fo6Ojq6urr6+vs7Ozt7e3u7u7v7+/w8PDx8fH09PT19fX29vb39/f4+Pj5+fn6+vr7+/v8/Pz9/f3+/v7/xx////8KXFhiAAAAAWJLR0RnW9PpswAAAvtJREFUeNrt3GtXElEUBuDpZlAqylVHCyMwyi4SpkaWAl4qk8wwUhJlmOn9//+gLzbKbc6AAm5991p82WvPOedZZw6zYM3aGupCQ4tYkZDSsKDrocNVvz8jHQKYY1ZiT/6OAJllRPSpsnzIqIG9SjYmHpKfAzI4CIuHjJeBtyHfZwCatiI1/m+BYV2Dw35NniOEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQUp/6Wx+DgbRbxOAhEb/fXJ98akiH1AIAHtSWlqRDSvdDGcOHnYR0SPV7zVsaQyHeO0jTJapEV5C9bUz9fIjsvHRIefhRxHqpjxzA3ftajXOudHGJOtHV+1rV04/wHenDc2QQkFbLJISQwUDaP90IIYQQQgghpAvIxRO9gFxG4lZ9XBFIx6sihJD+QVRjEEIIIYQQcoMgahkhhBDSe0iLX3ydQdSLIKR9ghCJkIYEIYQQIhPS8d+AhPQboppFw9HMxBrs/lqCIbObpgezP67YjnQ8iwagHDzrryUZUgsUz/prCYZY05vn+msJhiwM6XrR7q/V9OJU4xQuEpcxRsezNPbXEn3Yzz9Hulg3IYT0C3LxxI2CNCQIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQlpA7P5a0iF2fy3hkLP+WsIhVbu/lvRby+6vJR1i99eSG6ffu1XHxlRNcfWq3I0iIK4tJK2nXVyV0lOqEmvjGWAtvnCu+jMzkQWMiOlYdRSd3MAH/UnVPWR/ApFfSkcpiOBvRc3XtAeYW1ZUJbZMD5C8azhWLX4xvZVR692Se0huHm9ySogxkhs3lFVewPM4WlFUlUMoJEYUox1+jAFIbLuHrKaQWlMu8TicDp+4gdw7/qS4t2qBohk4UUF2nieBfLyDM/ItgXhBucT113i14QbixW7M+SRNb6EQ0u8kHavyZQxj2/kgNUCsYDRoqXfEF/Mdu4G8D43uOtakh3R9H1DsyKZvOmneDjt+D/0DTzolrPMHmggAAAAASUVORK5CYII=", - "description": "Displays changes to timeseries data over time. For example, daily water consumption for last month.", - "descriptor": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "resources": [], - "templateHtml": "\n", - "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.flotWidget.onDataUpdated();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.flotWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.flotWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.flotWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.flotWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n hasAdditionalLatestDataKeys: true\n };\n}\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}", - "settingsDirective": "tb-flot-bar-widget-settings", - "dataKeySettingsDirective": "tb-flot-bar-key-settings", - "latestDataKeySettingsDirective": "tb-flot-latest-key-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000},\"aggregation\":{\"limit\":200,\"type\":\"AVG\"}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"stack\":true,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"defaultBarWidth\":600,\"barAlignment\":\"left\",\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false},\"title\":\"Timeseries Bar Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{}}" - } - } + "widgetTypeFqns": [ + "charts.basic_timeseries", + "charts.state_chart", + "range_chart", + "charts.timeseries_bars_flot", + "cards.aggregated_value_card", + "charts.bars", + "charts.pie", + "charts.pie_chart_js", + "doughnut", + "horizontal_doughnut", + "charts.polar_area_chart_js", + "charts.radar_chart_js", + "charts.doughnut_chart_js" ] } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/control_widgets.json b/application/src/main/data/json/system/widget_bundles/control_widgets.json index 5ecee7ed9c..fb7c6e1ee2 100644 --- a/application/src/main/data/json/system/widget_bundles/control_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/control_widgets.json @@ -2,199 +2,24 @@ "widgetsBundle": { "alias": "control_widgets", "title": "Control widgets", - "image": "data:image/png;base64,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", - "description": "Send commands to devices." + "image": "data:image/png;base64,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", + "description": "Various interactive widgets to control the behavior and state of the IoT devices.", + "order": 8000, + "externalId": null, + "name": "Control widgets" }, - "widgetTypes": [ - { - "alias": "rpc_debug_terminal", - "name": "RPC debug terminal", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAWcklEQVR42u2dB5QWRZeGi6QgklFUDJhFMCJGRFHMomMAMStiBvUXRTFgRjGLERMoiIBZ1GVXdNcF4yIe5agYdvVnfkAwIAoiIs4+/72na3u+NN8wAzsD73vmzOnur7rCrbfuvV3VfSuUlZXNnDnzmGOOadKkSRCEKgAKlZSUzJgxA1IFWNWyZUsJRagutGjRAlIFdJVkIVQvevbsGWQBhWpH06ZNJQRBEARBEARBEARBEARBEARBEARBEARBEARBEARBEARBEARBEITlRK9evc5KwAdAnTt3rlevXvz1oIMOir+efvrp3bp1a9WqVUYOderUOfDAAy+77LJLL7308MMPX2ONNYop99577+ULyaOPPrpwsvfff59kG264Ya0W8uDBg6+55pqq5PDRRx8hh3XXXbfWtPnzzz8vK4/PPvtsjz328F9ffvnljF9///33q6++Ot6+/vrrv/fee+kEX3zxxeabb15biHXqqafecsstK5S4a6655pIlS/7444+11lprtSPWEUcc0alTp/333//hhx/mdNasWa54nFj9+vXj19122+2MM8747rvvuHLiiSe6rnrrrbc4feyxxzp27Aifbr/9dk6nTp1aW4j10ksvkf8uu+yyQoVM/piC1VFjbbbZZtGu+RUXhBMLgxjTQymuvPrqqxzvs88+HH/44Ydps/jJJ59wccstt8wua+ONN7722mvvuOOOQw89NJtYlAIv7777buwp+aSJtd122w0YMIBbUDDRUjMYsL8bbbSRn+63336ctm/f3k/XXnvtiy66iFsw4htssAE/MWzSlWncuDEXUc/kT7L4cXn9+vVxD+65557bbrttr732iunJ7dxzz91mm22o4QEHHECIA27v0aMHgkKFDxs2jIsk23PPPWng0KFD995773jv3wx+3Ldv34svvrh58+a06L777jv77LMbNGiQtgBcf+CBB8hz6623XnWIBd59990CxOrSpQtX3n77bY6HDBnCMfJNZ4gfRl9uuummGQWhz3788UfS//nnn/z/4Ycf0sR6/PHHOcVkLF26lAMkmybW3LlzMSVuaseOHes/jRkzhlPI7aeuLI8//niOGzVq5PxetmzZX3/9VVpayvFdd92Vrs86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", - "description": "Allows to send any RPC command using its name and parameters to device. Useful for debug.", - "descriptor": { - "type": "rpc", - "sizeX": 9.5, - "sizeY": 5.5, - "resources": [], - "templateHtml": "
", - "templateCss": ".cmd .cursor.blink {\n -webkit-animation-name: terminal-underline;\n -moz-animation-name: terminal-underline;\n -ms-animation-name: terminal-underline;\n animation-name: terminal-underline;\n}\n.terminal .inverted, .cmd .inverted {\n border-bottom-color: #aaa;\n}\n\n", - "controllerScript": "var requestTimeout = 500;\nvar requestPersistent = false;\nvar persistentPollingInterval = 5000;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n if (self.ctx.settings.requestPersistent) {\n requestPersistent = self.ctx.settings.requestPersistent;\n }\n if (self.ctx.settings.persistentPollingInterval) {\n persistentPollingInterval = self.ctx.settings.persistentPollingInterval;\n }\n var greetings = 'Welcome to ThingsBoard RPC debug terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = command.trim();\n var requestUUID = uuidv4();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var spaceIndex = localCommand.indexOf(' ');\n if (spaceIndex === -1 && !localCommand.length) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n } else {\n var params;\n if (spaceIndex === -1) {\n spaceIndex = localCommand.length;\n }\n var name = localCommand.substr(0, spaceIndex);\n var args = localCommand.substr(spaceIndex + 1);\n if (args.length) {\n try {\n params = JSON.parse(args);\n } catch (e) {\n params = args;\n }\n }\n performRpc(this, name, params, requestUUID);\n }\n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt,\n enabled: rpcEnabled\n });\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' [params body]]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1:]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2:]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": 2, \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, requestPersistent, persistentPollingInterval, requestUUID).subscribe(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\n\nfunction uuidv4() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\n \nself.onDestroy = function() {\n self.ctx.controlApi.completedCommand();\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-rpc-terminal-widget-settings", - "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\":{}}" - } - }, - { - "alias": "rpc_remote_shell", - "name": "RPC remote shell", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAU6klEQVR42u2dCbSW0xrHtyZDOpVkyFCKIplCZpLMIVJIhsgYmSUakLniInOGyFRXaCn3ilws89Q1RrpRSq4k15Tx3N96Ht9e73m/4Xzn9EVH//8666z97u99997v3v/9PM8e3meH8vLyWbNmdevWrUGDBkEQFgNQqGvXrtOmTYNUAVatssoqqhShVGjcuDGkCsgq1YVQWnTv3j1IAwolR1lZmSpBEARBEARBEARBEARBEARBEARBEARBEARBEARBEARBEARBEAShmujRo8fxGfAB0NZbb127du3465577hl/7d2796677tqkSZNUCsstt9wee+zRv3//c845p0uXLvXq1ftLVtQ222zDJ50PP/xwlZ7abbfdeOruu+8mXKtWLcKzZ89eJoj1/vvvl1fEe++9t9122/mvEyZMSP26aNGiQYMGxcfXXHPNl156KXnDBx980KpVq6Xh1aD7FVdcsdVWW4lYfxqx9t9//y233JJauPXWW7mcM2eOCx4n1imnnMKv1Oyxxx47b948Yg4//HCXVc888wyXt99+e7t27eDT8OHDuXzttdeWhlcbOnQohTnmmGNErD+NWC1btox6zWPQiZFYKMR4P5QiZuLEiYR32WUXwq+//npSLb711ltEbrDBBqmMDjnkENTluuuue+mllx533HEe2axZs/POO++mm24666yzVl11VY9s27Ytd3bo0GHvvfe+/PLLhw0btummmxJ/0EEHXXfddRdddFGbNm2SKcPpCy+8kET69evXqFEjj6QzPPXUU5Rk3LhxJ554Yry5c+fOJHjttdfisIDS5qyTpk2bnn322eR1/vnnR+kbiUV2Q4YMufLKK7fffvvkU+uss86AAQMoxhlnnBEdIIhYLWPMiy++WIBYO+64IzHPP/884csuu4wwJEgmiB2GQbbeeuulMpo8eTI3z5w5k/8kS8xOO+20cOFCLr/77jv+z507t3nz5sT37NnTpSb/f/75Z/5///33Dz74IIGffvrJ74/pQ5pffvmFyB9++MGbbf311yceLxcew81Tp071m2l1T8TTueOOO7IrZOONN/7qq6/IlxTQ+9y57777RmKRPsl6qX799VdeNhLom2++ie+CH4S11lpLxPqd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", - "description": "Allows to emulate remote shell. Requires custom implementation on the target device to work properly.", - "descriptor": { - "type": "rpc", - "sizeX": 9.5, - "sizeY": 5.5, - "resources": [], - "templateHtml": "
", - "templateCss": ".cmd .cursor.blink {\n -webkit-animation-name: terminal-underline;\n -moz-animation-name: terminal-underline;\n -ms-animation-name: terminal-underline;\n animation-name: terminal-underline;\n}\n.terminal .inverted, .cmd .inverted {\n border-bottom-color: #aaa;\n}\n", - "controllerScript": "var requestTimeout = 500;\nvar commandStatusPollingInterval = 200;\n\nvar welcome = 'Welcome to ThingsBoard RPC remote shell.\\n';\n\nvar terminal, rpcEnabled, simulated, deviceName, cwd;\nvar commandExecuting = false;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n rpcEnabled = subscription.rpcEnabled;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n } else {\n deviceName = 'Simulated';\n simulated = true;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n \n terminal = $('#device-terminal', self.ctx.$container).terminal(\n function (command) {\n if (command && command.trim().length) {\n try {\n if (simulated) {\n this.echo(command);\n } else {\n sendCommand(this, command);\n }\n } catch(e) {\n this.error(e + '');\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: false,\n enabled: rpcEnabled,\n prompt: rpcEnabled ? currentPrompt : '',\n name: 'shell',\n pauseEvents: false,\n keydown: function (e, term) {\n if ((e.which == 67 || e.which == 68) && e.ctrlKey) { // CTRL+C || CTRL+D\n if (commandExecuting) {\n terminateCommand(term);\n return false;\n }\n }\n },\n onInit: initTerm\n }\n );\n \n};\n\nfunction initTerm(terminal) {\n terminal.echo(welcome);\n if (!rpcEnabled) {\n terminal.error('Target device is not set!\\n');\n } else {\n terminal.echo('Current target device for RPC terminal: [[b;#fff;]' + deviceName + ']\\n');\n if (!simulated) {\n terminal.pause();\n getTermInfo(terminal,\n function (remoteTermInfo) {\n if (remoteTermInfo) {\n terminal.echo('Remote platform info:');\n terminal.echo('OS: [[b;#fff;]' + remoteTermInfo.platform + ']');\n if (remoteTermInfo.release) {\n terminal.echo('OS release: [[b;#fff;]' + remoteTermInfo.release + ']');\n }\n terminal.echo('\\r');\n } else {\n terminal.echo('[[;#f00;]Unable to get remote platform info.\\nDevice is not responding.]\\n');\n }\n terminal.resume();\n });\n }\n }\n}\n\nfunction currentPrompt(callback) {\n if (cwd) {\n callback('[[b;#2196f3;]' + deviceName +']: [[b;#8bc34a;]' + cwd +']> ');\n } else {\n callback('[[b;#8bc34a;]' + deviceName +']> ');\n }\n}\n\nfunction getTermInfo(terminal, callback) {\n self.ctx.controlApi.sendTwoWayCommand('getTermInfo', null, requestTimeout).subscribe(\n function (termInfo) {\n cwd = termInfo.cwd;\n if (callback) {\n callback(termInfo);\n } \n },\n function () {\n if (callback) {\n callback(null);\n }\n }\n );\n}\n\nfunction sendCommand(terminal, command) {\n terminal.pause();\n var sendCommandRequest = {\n command: command,\n cwd: cwd\n };\n self.ctx.controlApi.sendTwoWayCommand('sendCommand', sendCommandRequest, requestTimeout).subscribe(\n function (responseBody) {\n if (responseBody && responseBody.ok) {\n commandExecuting = true;\n setTimeout( pollCommandStatus.bind(null,terminal), commandStatusPollingInterval );\n } else {\n var error = responseBody ? responseBody.error : 'Unhandled error.';\n terminal.error(error);\n terminal.resume();\n }\n },\n function () {\n onRpcError(terminal);\n }\n );\n}\n\nfunction terminateCommand(terminal) {\n self.ctx.controlApi.sendTwoWayCommand('terminateCommand', null, requestTimeout).subscribe(\n function (responseBody) {\n if (!responseBody.ok) {\n commandExecuting = false;\n terminal.error(responseBody.error);\n terminal.resume();\n } \n },\n function () {\n onRpcError(terminal);\n }\n ); \n}\n\nfunction onRpcError(terminal) {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.resume();\n}\n\nfunction pollCommandStatus(terminal) {\n self.ctx.controlApi.sendTwoWayCommand('getCommandStatus', null, requestTimeout).subscribe(\n function (commandStatusResponse) {\n for (var i=0;i", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-knob-control-widget-settings", - "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"maxValue\":100,\"initialValue\":50,\"minValue\":0,\"title\":\"Knob control\",\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\"},\"title\":\"Knob Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}" - } - }, - { - "alias": "switch_control", - "name": "Switch Control", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC8VBMVEVwcHB1dXV2dnZ3d3d4eHh5eXl6enp7eHh7eXl7enp7e3t8enp8e3t8fHx9e3t9fHx9fX1+fHx+fX1+fn5/fX1/fn5/f3+Afn6Af3+AgICBfn6BgICBgYGCgICCgYGCgoKDgYGDgoKDg4OEgoKEg4OEhISFg4OFhISFhYWGhYWGhoaHhYWHh4eIh4eIiIiJiIiJiYmKiYmKioqLioqLi4uMjIyNjY2Ojo6Pj4+QkJCRkZGSkpKTk5OUlJSVlZWWlpaXl5eYmJiZmZmampqbm5ucnJydnZ2enp6fn5+goKChoaGioqKjo6OkpKSlpaWmpqanp6eoqKipqamqqqqrq6usrKytra2urq6vr6+wsLCxsbGysrKzs7O0tLS1tbW2tra3tbW3t7e4tra4uLi5t7e5ubm6uLi6urq7ubm7u7u8urq8u7u8vLy9u7u9vLy9vb2+vr6/v7/Avr7Av7/AwMDBwcHCwMDCwsLDwcHDw8PEw8PExMTFw8PFxMTFxcXGxcXGxsbHxcXHxsbHx8fIxsbIx8fIyMjJx8fJyMjJycnKyMjKycnKysrLycnLysrLy8vMysrMy8vMzMzNzMzNzc3Ozc3Ozs7Pzc3Pzs7Pz8/Qzs7Qz8/Q0NDRzs7R0NDR0dHS0NDS0dHS0tLT0NDT0tLT09PU09PU1NTV1NTV1dXW1NTW1dXW1tbX1dXX1tbX19fY19fY2NjZ1tbZ2NjZ2dna2dna2trb2trb29vc29vc3Nzd3Nzd3d3e3Nze3d3e3t7f3Nzf3d3f3t7f39/g39/g4ODh39/h4ODh4eHi4ODi4eHi4uLj4uLj4+Pk4+Pk5OTl4+Pl5eXm5eXm5ubn5ubn5+fo5+fo6Ojp6Ojp6enq6enq6urr6urr6+vs6+vs7Ozt7Ozt7e3u7e3u7u7v7+/w7+/w8PDx8PDx8fHy8vLz8vLz8/P08/P09PT19fX29vb39/f49/f4+Pj5+Pj5+fn6+vr7+/v8/Pz9/f3+/v7///835u4GAAAAAWJLR0T61W0GSgAAC7hJREFUeNrtnX9cE+cdx/PEH3VqqT+RtFltoyillDkqoE77w/rb4QBFfkdsy7SbndLqnGvL2Lq5sXW1dcyNls7iRq2rbcdEasGVTqRiKWaIjLIYI4NlybL8GM2ve/7a93J3SUhwd4Y8GdfXff7g7rk83y+f9z3P9+5y5IIMf04kk0A+hyBOe+i2hrKmUec9U9ZIBsRavDRWtS7EoFM1URfSdysqHjVIIcojAmJKRBNSFUheEQyimNCHceayxvBBHlqmjSJIPkozY2o/mmAOesFhgx/LUE34INNQVxRBHkal9CJl5sk2dTXG1GP00JSpDXib2qJRz0Xr1RTGA6UZBScZkLM5WRUeLthTlZ1ZZvNO0P2ZmQedsPKCuvdEdvYhCjeoJ6DsMtyrrmzLKocO+6CDiyBIDj0iXnWgVIzb0UQnto+b6MLjkLYK0fLghon0cjMNkiyHtUxu+j1Eb1fCBOpW0Wt0ptUonV5V41J6kYTrkGoC2sh1sJAD6Y9Fk3OaYK9jKlZuwY8hdAI3oFWYBnHblqAXbHgoFpWa21ToJIDMPGF6HslZdDVK1Zg2o4exJxVlDvYuRxtokIQzplI0mXLabkVn7QCCVlY0eBajLLrDRnIgWL8WdlViJaBkoOM4XSHPgZ25zwvC1kgVSoNXK5L2AEghRChRqzeSUqIGGJZFi91nkAJmjV4uNwHIAdgmRwa2RupQAszERqSEDjo57AJiIBhrdyxCKBfjSlQ0iAqXqaiVqCcQZDvKGlbsybR/kAUhC7N9H1pDLxLghdUICoIJZkHSob0XraU7xKNGkiCwb8sReB9EyytQ0y7UPllFBYIUo60jghgRcjLbd6BN9GIxTMuRQUrR17yHFJiehEBMSUmDzM6sAiPjH5zp6UFrvSPgB9mL1kF7qN8SBOIaj/SwGOj3VKIVsOKejDQ3ADmIHmA6dJMCoZJQEez+/gkw6LgA0TsuGaEjnJcVqBJjDZpopA9vjwWBMIfuPvmtnoFx8g7abTwVCBKLWjgQ6NCJ8fMogSI2terHo9SsjGloGZTkSYSOYlzEHJW8XjJRUrYDfxWpctegmYPBIK3j5OtyFWgHHTMtO2OcvBoHgixHS/NYELDv7VBD8KjVuWo8QpO3WOmT+fjxcKXYgpZgzktvPEJW7MqDE8mKruAagYvIFIRid8OIUk/MhJMGfRUQANI4DSk4EOqJaWwHgsXu1Jtu+Jqn33sR7NJZRn7doHezHfVGKvhFl94ZkGmEDtL7EQlEApFAJBAJRAKRQCQQCUQCkUDEAuK0m01Gw5iX0WS2O28M4rGJgCFANs+IIJRVXBj0wFipUBCHQZRyBIFQVoNIxQ0KC2I2iFbmABBKxBxAQvlAbEafntmUokCyMS6kSNn0jN+zlQPx1/melISikx26gdFL13GyMJ4oTeKuYRUPIBR32D26UnVQ3x856asTiaKsPModhikviI1tHk7Z2NcfWfVuIEqy6DB3aqRBPGyjZlGBVgcyWIccztHKMWQ10Mm02xWkRJMkV7Pm3QDCDcjDG/u0Wu3gkDNSGhqEfH1ZCpIoj/iGRIbZCimb293X12d0RFJGyNidriCJUsa4N1EyF3sMW1Te09MTWQ4ggZzPK0iSLGbtu2V2ZqVc1d3drXdEWnrImk6UhD2fDMmszEpGXldXt20o0rJ3d3UVEgXJZE+KMjOzknJEo9E5Ii+9RnOEJIgilfFvlpmYldiGzk4TARBjZ2c9URAF49/EgSS2dnRYhyIvW0dHK1GORLbaZWZGivb2djsBEDvkJTsiLIAPpK2tzUFCkDe6IK2trUMkBHmjC9LS0kIEBPJGB8TESNHU1EQEBPKSBWEBAkHsJBR1kDNnzhABgbzRAeFqpL6+nggI5I1uscMvtJFQ1EHeffddYbv46nuvvfT9l157/6qw7pD3JlzFzZ466ZZJU2fHhQ9y4sQJQRiv/PaD8x/+6cPzzbWvCEKBvII93REzaQaNEDdjSswdowDhnyeW5lc++jOnC9WnrPwhNwESN2m2ktPsL8wKE6Suro7XlPWtdy5+5NfFP/yenwTyCnQ0Y4pyrl/KmBnhgRw7dozXVPMfLw7XO828MZBX4HjcFsgBJLfNuSkQCyMaxMojbe0nQbpUo+ULEgqinKIK1lSlkEAWwAdSU1PDY8lSd+kvwfqk1sITBXkFgUxXhoAop4cDUl1dzWNJ9+blEF05ruOJgryCBiQmPlSCDl0jgFj+tz64eCVUF87xRAkE+WJcQqjiwgGpqqrisfTGlU9DdeUtnijIK2hmLUwM1fzpYYAcPnyYx1ItfUs1WH2/4YmCvEJApiQkhSphyk2AsFNZcejQIZ7ZfuTa9VBd+yVPFOQVAnJL8kiaJCCS/T0+kMrKSh5Lx/Uj/EGnv44nCvIKAYm570uhui8mDJCDBw/yWKq/9o9QXTvFEwV5hYDMuvfLobp3VhggFRUVPJY+7jKGSvMxTxTkFQJy152LQnXnXeGB8JStvtEcqtN/54kSCHJ3TEqoYu4OA6S8vJzHkqVBH7JJd5ovCPIKAYmdc8/iYN0j6AI4GOTAgQN8nq6HXLZbT/fzBUFeQZco86empA7X4pj5NwPCWqJBzHz65Pxnw+4ifnb+Em+MUBDFvDmpaYFKnTNPUBwL4APZv38/rynj2XaHyy9H+zkTbwzkFQaivCsuLd2v1DvuVIQFsnfvXl5TZpOmyejmZGzqMPKHQF6h7xAX3JacvoRRenLMPEXYICYB0p1t0VldHpdV23JWJyTgJkAUC+fMWnh/2pK0+xdOn7VAESbInj17TILU39XSWN/Y0tUvrDvkFX5HRDl/we0xk2JuX7AwVhEuyO7du80kBHnJ3tcKBtm1a5eJhCBvdEDY20+KHTt2EAGBvGRBWAAfSGlpKREQyBt1ECMJRQ2Eq5Ht27cTAYG80S32bdu2EQGBvNEFUavVRD46CXmjC1JcXExkRCBvdEDYv1km5hcVDRLgMBQV5RPlSGIBOJBlWwoL+wjMrN7Cwi1EQb7C+HfIHMzKuoyCguMEQI4XFGQQBVnP+HfKnMzKvgfy84sMkZ9Zxfn5K4iC7OVA3MxKd0JeXl5dxAfkaF5ebgJRkC7Gv0vmYYtk3ZqtW7dqIszRCTlXE+VYx9r3yDBbJG2Jm3Nycjsjy5Gbk5MdTxSkna11+Lisi72TUJS2BVQzMBgpDdTQCdOIchSz5l0AQrHr9vUrNoNyj/UOGkYNYRjsPZZLp1tOlGONjTVP0Z+Nd3IfP1yblhVhZaaSLRCjb0CYxy64MSlSPZgZSY5HVCQxlEV2zjn7/IjHd8dNkxG/dNWGTZmj16YNq5aSLfP17T7bbu6JHpf/c2I9+9YvT1SMcSUu37Cvx+/Z5X9YzOUQsZyBT705xc7he6DSLVYOd/CToW5xcwQ+dCzCQnFRIz897RIVi8vlufHz7JTbLYq6d7rdlPRVCRKIBCKBSCASiAQigUggEkiEQTzuzwGIp/PFb5SUfP1H51zcln/9bJjgS2IGhm8ZGosg158uYfXkZXbTQMkwwZd9/3X4FusYBLn8aIDB98ULYngcfD3VfFV/4Vna4WUfyLebffoPA/Kcf4tzzIFQ3wWHL3srnXqbnl0uDuTFYf1okNfHcrH3gsGnuSPWT6HRLFKQX4DBS1xjEBrfEScItbOk5HHfURc/BXbtogQx0zXsb/4Kmn8TJYgW/P3a3zwFzfOiBOkGf7/zN5vZUwkNsvM5Vi9zIN/ktrw+9kA04O9Nf/McNN8LPiE+xYH49L2xB9IFtt7wNz8QLcinYOtVf/M0NM+xIM9yT+5pOZAfc1v0Yw/EAP5+6G/WQvOKKIvdDVeMO/33u58Du2Zxntl/AAZ9/4rODlhPivQS5QIY/AnXeJs7hokQxPUt7kIR46swII9aRAriPQCXvPpPCltP0e+wTmGxgnjnE4zE497FzynxguBm/3vdWjcWMQj+d+1O76C8fB1jUYPQXwR8TWsQx50t6ZapBCKBSCASyP9F/wVSxTLixdhKDAAAAABJRU5ErkJggg==", - "description": "Allows to send the RPC call to device when user toggle the switch. Advanced widget settings allow you to configure how to fetch the initial value of the switch.", - "descriptor": { - "type": "rpc", - "sizeX": 4, - "sizeY": 2.5, - "resources": [], - "templateHtml": "", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-switch-control-widget-settings", - "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"showOnOffLabels\":true,\"title\":\"Switch control\"},\"title\":\"Switch Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}" - } - }, - { - "alias": "round_switch", - "name": "Round switch", - "image": "data:image/png;base64,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", - "description": "Allows to send the RPC call to device when user toggle the switch. Advanced widget settings allow you to configure how to fetch the initial value of the switch.", - "descriptor": { - "type": "rpc", - "sizeX": 2.5, - "sizeY": 2, - "resources": [], - "templateHtml": "", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-round-switch-widget-settings", - "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"title\":\"Round switch\",\"retrieveValueMethod\":\"rpc\",\"valueKey\":\"value\",\"parseValueFunction\":\"return data ? true : false;\",\"convertValueFunction\":\"return value;\"},\"title\":\"Round switch\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}" - } - }, - { - "alias": "led_indicator", - "name": "Led indicator", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAABCoklEQVR42u19aZBkV3XmPS8zq6qr90ZILQkEaEXsm0ACg9hNBCZmBsZAYMcYRxA22GGH/csRjvAP//QPhyPswI7xTBgCM8yAPfaETdh4AAMDQgIEQmKxQGhF+95bdVVl5jtz7j3rfZlZ3S1R1ZXtTpWqX77KfPnyvu+d5TsbIGI6+zj7+Fk/mrNLcPZxFlhnH3Pz6J/B3+2GG25YXV196Utfun///sm/Hj16dOfOnQDwFI58/fXXr62tvexlL9u3bx89PX78+GAw6Pf7Z/G0TYH1wAMP/M3f/A1d7N/6rd96+kf70pe+9MQTTzzrWc+aBNYXv/jFf/mXf3n961//rne96ykc+V//9V8PHTp00UUXEbDonP/sz/5s9+7dv/d7v9c0ZzXAtgQWiYF77733qUmRU3qMx2P7/TQfbduSA0S/n/6hvlEez3/+89/+9refBdZcPujKXX311SRmnv6hLrzwwj/4gz8gVfj0xdXhw4fpvjr33HPPSqzT8LjvvvtuuummJ598ktTQK1/5yvPPP9/+RBYVWT+km0j3vfzlL9/gILfeeusdd9xBuuxFL3oRa0YSlm984xu///3v33777YuLi7T/8ssvj0f++te//uCDD9KRX/3qV8dD0ZnQnxYWFt761rfazn8rDzrmOeecc9VVV7Epxg/Szt/+9rcffvjhpaWl5zznOXSehMjhcPj5z3/+rrvu4i/4T//0T/TVzjvvPHq6srJy44033n///bT9vOc9j/abMUf76TgveclL6I333HPPW97yFn7LWWCd8uOrX/3qZz/7WePevva1r33gAx+gleVr/9GPfvShhx6yP21wHELVl7/85de85jUMrOuuu45s+R/96EcESjP8f+mXfokMf7bN6ch0Cc1yjzr0yJEjdKjl5WUGFp3bpz/96e985zv2gq985Ssf/OAHL730Utqmj/jEJz5BMOr1enQQ+hQ62oc//OHRaEQH4dc/VB4EekIJgexjH/sYSTL+Ex2WQPxrv/Zr5HbQ0+9973sE35tvvpnASk8Ni2eBdWqPn/70p4QqUjoEpuc+97m0rP+7PMgoIYFBUoeux969e9///veTnCBj5Qtf+MIpHZ+O/Lu/+7s7duygT7nlllsIbQwsOjKhiqTO+973PjoyQYGM91kHIWlEl3/Xrl10kiRN6TQ+97nPEdR+//d/n2zHv/3bvyVU/cIv/AL5DSTq/vzP/5wkzQ9+8AO6N/7wD/+QvA2CF33ou9/9bvpGZLd96lOfIlQRYn7+53+e7hzybGgR/v7v//6Xf/mXI7KvvfZaOrHtg6o547HoipI8uOaaa17wgheQhCB5Q6qExAlrENJi9Psd73jHJZdcQvAiK4p+n9Lx6XoTFAhAdNXp6aOPPsr7O0emjQ2Ms29961v0+81vfjOJKJIrtHHxxReTfCKhQif/K7/yK7/927/9ute9jkBGWvWyyy6jF5OGpacEaEI2PaUX0zb9/slPfvLII48QRt/znvfQWR08eJDuGXol3VHHjh2zTyQN/s53vpNWIyrcsxLr1Kwr+n3nnXd+8pOfNGuXfj/++ON0c5MAoG2Cmt80p2hN2+tJWtBvEi3s9PGRSUaezJFZYxLHYXtI00VL/7bbbiMpSzgjC4xVGOnBqYdiu4p0ohlVz3zmM/fs2UNMB6ls1q30oHvsrPH+tB6kC/j+pvvYdtLNvb6+TvYKO/xkEf9sP5SuOh+ZLPqTJzJY9kw+CFLf/OY3SQix0P3xj38cZU/nQd9r8hsx6Gdh8SywnsqDrgfd4kRpktif/CvpHTLASbrQy36GH0oXko5Ml5/kBJvMGz9IS9INQKKUhBPvoTcS2kjS0E5CFem43/md36GnfKuwWJr64NeQPI6oZfF5qlr+rI210eOKK65gd49vZTaDyOXmbdY+5GcZC7CBMDhVpioemcBNFMCsF7OGIhOeXVcyAf/kT/7kj/7oj8jE5vMhYcbKi1BiPmzUsKQi+SnxHbTn7rvvJnKL9xAuSUGzvXVWYp3ygy7Jn/7pn8Y9xBmS3Uo2NTlcdDH++I//mDxBugBkVpOeomtJa002LDnztPSEtgMHDpAfbvh7mg86MhlGBCxSwWRx05HZ/Jr14u9+97vkVJL4JFeAXkwopKgivZFEKckzQthf/MVfkOVEtjkDy5BEnh39Jv34mc985lWvehVZ/a997WvpRvrLv/zLF7/4xfx9yXgnJ2MLghNnpsS6t36wRUzm1Ec+8hFinki5kIdIICNj9kMf+hB7Q3QZiA4gYUAcFf3pyiuvpD0/k5Mh4L73ve+lTyfulI78whe+MBrynQcB6Nd//dcJN3QaRFgQjMgH/MVf/EWWVURo0U1ClAH9iQ5INIE5JfSgr0afRaglMcx2Pen9t73tbexsEljpmxK7xrzdNn/APCb60b1LF4wuzKTRQ/qFLgntp7/+bD/0VI9MEotMKILCZNYDGVu0c5Y3x64uG1j8IKlMH024JIG3/WXVHAPr7GP7P86meZx9/LunG56KHzDNNdjoBXASB4KT+rDuqyZUGJzRK39mqkKcDSacvLYY3/jUVwMiVCaODLNBBmeBNb/yCTvXELswsu2NJdosoWNQifCS7fpD4d+HDDtDgIUng6caTLxRXoh2maeuBk5RmFMwIP6aHEGe2SsrkG2IMDgLrG0HqRl4mg4m8O9uL8EAsknxlWaZTeVlBh0HE70g/2kayE6EMDgLrG0KqQAUFEmEJpYw6X9y7fPruvDa4CLjNAFW8GKYosOCQAiCMMub/HqXYWcivOYSWDMhVYuoiCfdKv+B6DxkjJU3IvqxKkk22yCvJJP8uWyK1APBGW2gi6koxhxheuwzBl7zByw8FUih7s7PC7wMTCybWgNcEjVpb4HZVpeBCVksJYOMA67Jz3QPg4zFGCMqAPFk4AVngbWVgmompCDoO90u2Mr/tPwn+u5hJ0rqVU7swvJoc2EXb08xrggRDVDyQQPl0cubPQrUgMikpADKyOMX2U4IMkz3z4bXfIquuQEWTkJKDZQAqa5Yart4UikFiX4TksbtmH4PR6MMpAICNfDlxRsaWQCuB/Mbm4K3XBPd7/cKzpqCG5NeEWGNirlpwgy6tte8ia45ANapQUqkUWKxhHmj5Q3+Ez1dHxKQRuvD9dYxJC/gN5ZL3/UAplrrLFdQoCNWulnrBJ2FwQLBa4GqDklssawqLyhPeUOQBHBGwWtbA+uEuq+j+AQ9si0iKosqSCSZCE8Epiyc1LQyzAk6zWE0RKWEU0kHzzFwu8pAw+fI6GFji7YHGV4LiwsLGVL5LmhA/2SoclCqcpxfzbh9gTXTSFdB1TISVGIJLIrYYnE1zrghy4nQNFxdX2P1x0KLJRkLubYgTxApDqPgNdrvUSn6fjOPitkk+MgCrGHx0wiAGhFOmBYLvPr9Ae3v6f7yLsWWuZNBnpl0nBejfpsCa7r6c9Hiui/jwxVfhlSr0BmOhqtra6PxqC32E/9mkytb6MASK4s0OlRrskpNNJxG2XcIdIhGkqm2rM4UT5jIrjejKtv7/BuaJSrPWFjklzUMr4JSBpNBU3E2Iai2N7a2HbBmqr9JQVUEjDhuKUopXFtfX11bpe0MJt0veEqCPLow4wLKsR4hKEHViUpsduks3R8gpQqx7OkVJZh/F6HVlP2MMJNSvH/H0g4SYPmvJr3KEVRjbii6trFa3F7A2hhV0aJSYePyiQXS2vpahJQJKtkIr2yTWWMiuuhdD7aHHmyPPIKHn8CjR8arwzQetu0I2nUcjsu59MgeJ3sJG+oBMki93b2lZ8Cuc2DPec3ug83enis+sZayKFJTnQWV7WkUXvQughdZYNUr1WhT0VVbXdseW9sIWBt7f7Uh5YKK5Q2hhBTfyuoqGeljHNOucSpYaceCPNDtGoiH2+M/Gj14e/vIg+NDx3CttGRA4F9QWXhTuCw+SxDynhCxC5bOh30X959xZf/gLtjRAUqThVaPLTDZzkjNVFgPevR0eWlp0B80KvMmRZerxW3vLW4XYOEJ1R+4xxcMKSzCCY+vHl8drrGUEkhhywgrYGpLPWvBE72lHd86fvAHo/vuGz9xCFflwwrLIEYUonPj2JELlr4Qoj7CT7BAFXmyt9lxIex70cKFVzQHCTQN81ipyVRqEnGV8VSQx/AiDC32F5Z3LDdgmjGYX8qBbawW4SywTglVKqKS4QkzhpAYhJXjK+MiflgDZkgl14NlWwD38PjIt0d3/3B0/zHCkwQIRctINJovGe8xinSyfgHR/EM+RkAY+p5ylCVcuGzh3KsHl57f7DEAlQ2FV8rakHViU4wtwhZpxl4QXaJey3vnAlvbCVjTjSolFJJgq8AFWRqtrK4QlVAgNR6r6zcq2GLwEZjo93o7umF8x83rdz86PuYJLJhU1ICAIOIpQUUwKFiSW/T2V6wQ5v8YfyrvP9DsfPnCRVf3L1lo+r2CLQYN/e6r+ZX3Q68wq4PlJRZdvabYaqIWxUXoqsWIrbPA6oor3MCoap0yyOz5aEjqb5QtKpVJCUcssQqY+Gd9PPzG+M7r1+4oIsrjvoxWF1SSlSACzC8VnODUnaBXEeX5OWhkWJU6sQMWXjG46A2Lly/BQoFR01MB1lcygjHXb3rZqO8PnKRIOTw5y+SC7SS0TjOwTgpVwagquMmu37HVlVbRUyDFXUHy0wyv1B7H4ZfW/+27w3vXcAR27cFETAkD85UAIQ5YygCamQVmyk+ppuD3o4g6BFGFmuOlAlgAlyzniz9nEfqv6D/7DYtXLkOfTK6+6MFsgfWLxGLAEZh2Li0T3dVT9qsyubYxtk4nsE4SVeNgpJPKW1k9vjZcN0jRHhZUIxTpRQj7zuieL6z9cCWtE+8pK+9iKehCVhyI7AOCgA8KgQ8MxKAE63MX9GRIiQyUTK9yxJDZp1mpqRZmmeAi6fW6wcU/N7ic8CRSKis+xpnDi8j6nWTRi//YGOm1nbF12oDVRVXNqk+gSoyqY8dX1kfrWQO6vhuLlAKk/fePn/zs+s33j59gKQGGJqxS8BKKqJHQnkLKLSdU2bZBsVcdqxYUosg4NGlmOdCgSEtgzD7tO9jse9fiSy7sHSDd1yD0RT/2TEvS/kFvsGt5pzqSMBNbHQr39GHrdAMrGOwxljINVe3RlWNkWhUYZUaKYMRgyrIqtavj4eeG3/vu8J5s4YsoquhDCLKqQpWZvahOYLDUNwaWIMgwCBUJ59iybByovjqKUkWCy0sHz37H4EVLBKGsGXsCssJT9AvIyNgiuUXMxUbYAtgmhvzpAdZUVKVkwbsuqoh5yqgaj8ai73AkgmrM8HpofPh/Hf/GY3jUaANRgrrEINb6VFQF9LlLp+4jdllSN70w1WbWRNyggy1m4yCE1YtCVJ4DD8Cu9y1ddX5/H0OK8ZR/q7e40OuT3JqFLU6m2CbYOg3AmmVatS6xHFVkhhOlfmTl2Kjl1nrZispSqvzO2zj+9vqdnxv+cL0d5wwBdH8ODGT5MxtlyCWy66hqk1jsAVKAxhuobqlOHp2jgBpeDLEmVRn05hnmg7f6HDBwGyXDAgbQvH3hBa9euLjgKSvBfvmd3ca8QT/93cs7C0HfTGBL7PrtYGxtNbA2QFXSdINW/DuRVYaqsaBqxEbVEEfrOP6H9ZtuXr+X3TKxYjiDpY53RMkEwbWrUJUi8RTJLpjgR1EFXHydcRAT2KolmeemFgWJDmip7Hnh4IL/tPCKxYZya/pschGesnUfsGVyKxv1jQSOmDvdDtg6TcAyV9zcwBRRJYRn1oDHV4bjoaAK2ZzCYcbZmEJ7Hz9+3f3jQ+WKiOyI4kpAFlECopUkGog1qrIydjtMEwtghi70II74fU3kt8TT1EiixKQMi6hCrhJacqb5qOc1e3915+t2p8VsuWdUsaXFYqwhW353tuUbI1QNWyqqwfT4aVGIWwqsSXEVDXbJl8qxPPH4jhw7MmzH6gNmVLEGJGA9no59fOW6J8Yr7O+BKBPxwaqiUVdRYlp5NXzyV4rlwwAVGddJnBGJpFl+piFRkrwAza3UeJSJv+gkJhQsmtBCZSyECePiH4o2fnDH689plglYohMdW4S23q7lXVlLQma/IKRRdA350yG0tg5YJzStPFpc6KijK4VZKOb5OHt/I0PVQ3j4r45el8l0lypOmk8RVzBNCWJlvVeogmz7O6tZXxJLu5AIYaO81BRsCSPfUYhixXeEFtqLzbfE5WbpgzuvuQD2ObZIeqlpv9Bf2LW8zKy9xba3ibG15cCqlaCYVq1SoBzgw/bY8WPMghKMyOkbFqFF20Mc3zl8+K9Xv7FG5CcaKIDNdvbqmmRyQSO1WKOKnUSnRMt7haLQKLSKQCe3Us29y+3huRC6R96WrDQWYrmixSHRIZYtPbTUQfRaoXxmi2nhv+x4zXP75w6AdSKlgmUmYlACjoU73dkLAWwoajEaW6dFITZbLa4qjkerHzwbHUv+5xqjKvNVkIYlxSqjqh09OHryr1dvWEtDkJKqFLMuc4kCS5yG86NSflmSpGB/aFoA2/gFoA2/u2yJua6p53yM5D+exidYlL9bUnHBqDkQXitRwsil7AvKQRPXW+QnaAd0YVP+aejLfmL1hntHj/EKlKXIy8KECy0U5XfY0hUOT8q8OzmxkxfiTADWpBJMWrsniZ3AsmpMK7WydtxE14hNK8xK8Alc+fjq9WtpFIwk5zHRBJgHkoM1z5nGGMtqYu0yqsHONECQbjm8Y1AoMEW1yOUFoBwFQvLKsdQpeGabCjEo0eBPmryqiHx5P4U7P7l6wxNppbDBsiYW1KKwaeGN8x46F0uU9cpImFFrNO/A6jaeciWoLRUQuf6Y/qfEYsm4Sq4ZR2l8NK1//PjXDuNxy2NRm0TjfR6HQedFFb/mK6otpI6aVVyBeozmP7ITUGqd9Xso+9AwwoQjAEOsSh29fxDQP9SZeVOyujJO3QsaAGLlGaTDuPbfj/6/o80aLYXHSVPLRUeU6MEl27yMRp61Wg6eOoz0GSaxUM2LSSVYkJTzq4ZkrHMCTFk4DtestaO/OvbVkk0F5k51VwgrA0IMC4RorefXtMxdFRC0hV8osRW+CNCKQZ2Nd1aBtNGWQLP+yB5Wikp2QnFo89Oiiuiw+VAstNryoQbLFE6s0/IPp92RSkI8icc/duRrq+N1WxZbKFo0WrpxCgWVHYUIaYt58GbrxZWEybpKMDPsVE2qUirbVZxuRYv4d6vffgAPxYtQhWNwwoJQU7jSg6qeIES8tcwGgwpSVBlhmaz+JnnDIglIKraCWydCC4NHaWo3akMVThMnXzdrC7fL/XjoH9dukXBWMT0tw3GYGwUMeSVrhdj1SLdGaDWnR1yB9UAT6U2/j5fqGklbKDFmpq9uGt5zy+i+TkwvTdtMUDs/MOHKpTq6DBUDDiElBjRVQfkL9+fM5xNsAaSY5YABHdCpc51+PnVXtslXQvyXMoK+vX73uEisskSeKHs8WxG1QtSl3nqh1WyduIIgrtDrlbnAgTzBUY4xawpoKwb7Q+ND/7B6E2p3jxAOgWScthnTtXDsGlgmMECpBhNRoLntykSICdcYUw+mkoSBaMBeBuhErDcvQsvGUYymCTMrdVl9nPhqvohikLf/uHbz/aMn2JAfl9uPcx5pAbkhBfOx0jQHA9UBWye0tpRu6IorTg0tSpBqbLL1wuKqZaIBV9v1/5Epq5OZNb8hQQOzpAFAp/7GuAZhkTQnBjtJC5yDjBAdwAAQP8YUEQtP/YuUx3oafXrtW+Qq5qyhkjpbtGG2+46vr0rltzehCELrjKIbuuLKAjhqaSZcXV8v2aGiBMcl3YruxX9e/97D48Nq6xsybZXAn5okk219izHbXP1lkqDVKo3Ep4Kg2/qn5FxmEqpChRxCJ45Q3uUxqjZUU9cfWgxuBPDT83gPBtMP0cVV/I7KWVCa0D+v3sKVkkUt6upli2KtTZ22KBiph7Ql1EOz6XqwK64kFlYMzLwcuQPMeFhChGMrs6EbkYr+vrV+1yncyLMwPfsAG1hqyTPbIRjgddOGE799w8889asbj/mt4V33kULUzGy+FTF7iEO24lttkeLLXgstnG+J1XEGw4NvLCGOAVu1Q9tSFfgP67dwb4Wzj6kPgtT/Wb+5lJmI8d6WZaTFJC4QrYfApKU116oQa6o9WlfFNhHrKvdAa0u9MjojSr+/uX7HXcNH0tOMRQBsfADcKOakwTrvkuUt2LwK5wRv3/AzT32OV+eY94wevXF4ly2alOkWPZCJGxNaU93DTTbht5BuqMUVf0lyBttSB8Epo+zdHG/X/u/aD2LTYQsLKrPNdBTaUzBvzFuZKe3Er8qxf08P1+3ytNHdHGC0lxn3YLa4fnJg8vVojeZZgGwLQ9b5UObxEYwYs19geRke07Q+SSHECZ7SQY/PHf/eSrs+FtaqIKyQWLSwOLHkWxnU2XxgebE81s5g5vTYFOC6rpIymu+8L67eegzXndJ0Ozf5duepG+/h7ranLYY/VYYzYvgTFFLeHCoBUPd6aPUpB5olRzG1oU0IWrfvaafXYvf0qjPHk/q+SrceS+tfXfuRsspSy5Tbq1J+URZaWLmHCbGm7OYMWJUe1HXmL+csS+lixTRM8ZCRt9fa9RuHd0wK/pjPCd4ZwfnJGZSshhMx7EFNaQH33mp+ydJpLDuq8ja15FnfBk7kI7ojLJ+LXh7rNRcAuMH6ha9mHb3TlCTpfIzrR3dwnGdcjIpMB5ZtWt42OVNYI2vTteEW0A3+D2prq9KxeEhLwLnIuceQJmN9de22FRhN/754MtZRVX41PfQGikbLLIBoP1kvb++RjR4HqnYqFMHtMAgZFmjqeKPzmdJBd+qgqBm3zwquf339NltAlPy2lBE2ZqElhEowHedXFaIn10WWQdhRSicaj8Wuko4MJWdmvP710R2VYz31pkK/ilUzGE9IxlCAisacpxjst7JSqa8PnYka4CY3mkOcoHb4WTpJw+8m9KrSlHVM8esL+YQaNPTWNKJOq5hg7NmMs7wOrE7outEd6+2w8H+tZ3gX38gWPPIO4ZtgmjuJVelBM9g1B4tSiDhzEovKYCrvG6O7joxXjBI0S4VrDvMla9uYIyi5CW14vdox/GmgOTrZExcRU15R3sKZo1aU411sW5ZQY+Y6vRkz2jkVrU4hARADzu0ueyLErOof4JNhI1tcNGVQfZn867TJv2YqXxwwtRhGaDiVeni88o3hXfK9eT3LZwwLC4+xsSpipQ3n1XivGEqQ+CBi7j1k3RxLwIvJmBvX76i+dLRtodJlVhnPv1BjepV4CUICLd8mFNwnkRi67DGq54dusbaxFKrJAnlmeCVvNA9uItnJi3qFbuYduPEnJpXWQCaY+OI4TZNiunHtDqYAi6UlC1vCZSNb9rp/3Px6hRAX3fRg3sPWOt9YQuWl9MD40EPt4fCtIazjVMoYQ7891JrlJFkHmOIezzZ1cKXgVCQMVYJWCG84LWKgcEJW/GMeCmirNozeWyS6g66EoMgAFUlhj1nulT85YWphuLXK4wF8kirh2E6XJS16gFh4TFVNlN88MHfACv6gWQ2mq5hokMocpRsKKXonWmplqCrBmDYCsd6hih3rZUZQUhMBrFtCRBCqkOPPwiZp9pQVUod4noIWYrgQlJizmn2BUjlUo61IxCfF6E8mK71QSJVTRs+9qbL/dAFDWg1C3fBdqYcbh3e2SuVYj1YmHdxQsKrKDcYjzIVXiGoem0AuziBaBF5SPtrR90vSVZXSlGqRI0pCLjZ2jPA0TRtaAaDyjy5m5NWWcmXY0h5WwEkMiBWbpBfJMxo4w1pQJeQb1kxEoFMtKWaKHox5XxZPh1DVGAVeVZmYH7cM7+Uy8VbYZjHo8oLr4uuohLkmSENXlRT8QRLOrWRki1dIO388evhQu+J+FNaZpzkGZHa61CvLfmvOh0EsWDfcpKlXrcQm5e2tiidkL859JDN8OE4ebg+Q+AjvYV2uREOlhn3OJgh3qokcJcvZqxu4rUBgjrXQHkOPkVb9iRJe7aTxhLQIoA7QPxk9kszA0MEIw5H7hikywjiPwApEg2uBcn0zz6DeEdsE9EPth0Jkw/LlmhRCNhLVKIERLYCQqA4LEImD+CZY1bKVViXtEcvlpQihorBRekuNHykly+0RSi+GhvPgmzAozm11LuER5aedAblhRyxSwzgm079D0oRmi+BIemrp42eRnjBhJZ8Nb6DFsm4Z3dMmDLl+Jc4zHreIKUWrDzbbzNpE5r3KMFJlMWb6SlOypEs7tnePH+uEeYOtbfSz6YiONhQFiJaKjjKjMpjtqFrSCuSzMST2DXrMGUwtil3YTjKbyHeFcFwYes5ojz9U0w3D+VlyPSQrZLWOuqZEUxSe4A1IVEbX06Qh+jZ41/gxtq4kbYaTv7FFjEOHsMMGzp+NpV48GIkyzs2GwFxvFtFH2tXHxkerYgJMRh568iZoZp+TDmbHpFD/ZZl4UNn3OtoiMvSOLVdl1uBBTqR1pky3zVkEdhgDg26oChOAMaxFuJSA5ppA4MHASV1vwYXoJWGRBMYYFUgPj47QYsa15bhi9g0x5GZtvpnVbLKBVdO7kEQsQwoDcBLNhuhmURbLUyySZKZVC1UPIJ0PJ5Sj5sVnLlQvRjZrWglEO3eaqgGGrLqMHRUTTU6ILxCgDLWAQrSCJ3omM5LQvNLkyZtWlavytZxM24rJKFK11dBQG86w9QZbDLy21Rg2qtVoDK5nz/6IWltYgq6O/MwtfKbn2M9rdoNTJqg1OUk7bMswHGx/PHwg1suYhexiX5lIDCnOlUQCp88tjKOdFIxGwqrUKvCgUigoZxU7ZQGqTOr8pCpCCOHtrdd2YF2QprWOqOVhHt5xcYVV7AgxhK7tO1fmAaCTurTr9uGDNmzB7pdRzh3xJcMqs2KOgAVd4RWUQWuhK6vSuQcf18Af+mVITnt6zyC1TJRPj1FnDQ16/Z4FWSCwR3UIr1K1xV5C6zQEzjLE3g3OO4DEpJU+67AldcmYKUw5Jc9g94La0C0kOTfhbZOMqlA+Ldwqcr53t4/FKp0EMWcpWFgzLtZcxApD90Sx3Nu2bl/LsVJqc1X1AZX7GPXqQYod17ss7KTQUiu+oozQm8L4W2qWIYscHoqZa5rBQwUw8SPl8FLEnqHTJqy69ZlEsbJUxC6M7aQ64mrGF9RAO4LNVrTiRkPJY+NjY2w7/adztta49WLh6BjOlyrEugMZrwLVKUGyYgr2vfHh9lCLndSTYqeAUfBVHjCmSmj5JXCGCGJ3f3QtYTyj81adWI8qJYnesEwAxpn9MFeucR75QnXSMVpb9zofLEgfBT2EJwgOviCusM6WtrRWaRrhYUgJPz/SHi62vpiO3H6EeTxMHtZP08Zd/6we/U22sDDQDWZReoCnhAgPC5fIrGlIZ+MXNCGK0YKHx5xSyCnFGIj4fLlbCPGQgDJrx1aNuQm1yh7wFfJ9ciB07G5SJ/h4VhbGKGGL8RNVaYK1jrR+k5Sxo2aVCVi51ZxvEFZZu4cwenTGSv4rLemF/QMircQEC6Vg2mgeAOZPYrnKAYhsVv7IRmjQtsTraOqkiqloL6C1ZMFpQqtya2JnM2CzPfZj6HRIc/4pZmRaGBAn2KJko8jjRztoEStbzMKaYHxYmNkEwYXTk9S2cfZCnC77kxf2x6nU6JqdNx9tD5eOJ9KaonQnTZHB8vOe17SZKmsm2L3u71Pt5ZHYRRhC6BpTMDfNno/sYlA9YAoR7bJhB1vMUoegiHJSlRpmdRc0Ywqsd+hJqvQFoOcZ66yTkNyOUvWRJlBlQBdXGFKlmo3jjeLKUifCeXfGEqSH01FjG/w8iayBretCujmqcEbKJw8G90Sasn24XbHJx0WQe/dMMZIbN5EQw4h3mRuP1mUUfDQqA68p7l3j/Q9a761QFEITKNkwCDOkUXRaWbmFBB3/VbodM5UgHyP6Bkypqxxsymsbi4Fj6FTJ54VOJ2gogADc6HdtDXCNrGjjubRPjI5iFTrFwLNPuTab4RhuSYm93uReuyKhrfzPehqHXjuW0hGElo4Xt36aGGbAIXYaS4GaLEmZg8B3N15vHgxhtbZDCnFwQWckkEYRYsa7t4jEOECgoKiKAWAIG2KVFh2DDJZdk5Qp83nrcakg2oc0bzaNrKgsMivo7Q5wPiXWDGorEnyWc7bWDqUjHqa6tyFIBgKATyLhP7N1lkHVJJs3UdfV6PiSYLNLd8bApaK8JeSctzzOS/RkzHyqQnYWfAb9kNY8TlVOEF4lkwowTPJxFlV6tyk4bIZFrozwxrfBUAhjnywvwidZw8p4zdrQm1XVtriJtNXWSqyptSZdaUzjT0O6GQZLBJ1usITfDh+kvd3V7zT7CULVTGhwbT6BGbOoNi9qCMeGC0SnLGlDv9D+KIXmRuLVonX1dmfF7Ps44BAreY7a71Jjqymcr0Ug/TBKN1Rprh6RoH64aab5v6HJMo/G+xQeolyWNWiV9E7OYgWxZgaIpQ6I5ooKEbQ6xqtP/G52+rpJIcID7ugpvKQCotO6M6WJytHKe01WyW6QqvpClhytJiVPHMPOSWoCY5gkkDSXUBMwrIESYEx99hCYTEMo3RxseeNAqK28wpvMY53EPTGkomd3AK0ogpODdeIpoqeAInqDdhvDxGay8VRmlUnnWaMswDu1aVfuWMqarHNycr5plm53M0x5Ay9nBQ/KobtumpHqdpXNk0b5kogVlZpSqOkX8gvBg0P61Dva0jGmNlPpuIRYZzjPr40V7feOMZxJYdN5FobhW8+63yM3kBXXqbU4s83c1SZ8iDJVFfVyKba4VgvjJK42eT/38jcTnSEwPFvPo+eH2ahVzf+LNhFKJ10wEcxjA8TGK6kHiFUnvyrDRwPNqqhbAVmjYt1y9Vl+Nk2stwtlFFv32FxVeDL3BE1Hdj4mzuX2Pokg6sxLTKuZk6GtYph+iIHgRjd5tBJMRJ/mabXifjXJUnsxsFnTf4wEL2/UtqWtXGqz9kAL8vU0pOdDcDaw6qQZTKsEnpID2qY3mFXO0oO70v2TuKywyVb8abKxtH8M/V5oBjbSqJMpBCFwgt6aU4MY1hYdrRtfF1tGQWCMVOvgOaNZnQs1hCnINvpprFrf8OTcvZwgoJU+plBTpsZRB1Wtfx3NxEGfLRzpiQRTct7E+urZ8Iz0VJolbX9VWH130OGfHZHcbyGiSLgFT9UtUS7VMOYJceiXZ+ioTrSxl6F00LsniOtuCtca2UrQDD3ZC1qs23zDRg6vV5l62BsAfWKvabbGyiVsOHRHVtmkVgwZM2KcJcuHyDQLmO0ujDB6J90F6E1SoDCFdz8TbCyJEqq/4t1jlmAQAhhhdm4coNsq+cw2E8/c4hpSmVMKbIGJHcyWsNFinubrEkAmmbgjByH9PIIHNrpnYoC7DTa9eXIxuIhGo3ulYWVZ+lgn8BpEM/0sQBOsuDC6DmwK3g5cCF235NM5YrhlLf36W6D1ZLqkmdcQOhshDugcQPSYFiJEc19G7SrgNHmzxSnYwqqG3lIZnE+KZDUqvEJYVr2GMHRulpfuV1eIC4A686Gt5/ymYECpewoqgUNIQlEFhqqqrYR2w+kWiaNxdk1agoWEGAIYqUpn4Jkbc2m8w3RTkabNJhuPJDoK9jdLTHf7O9Gn/iFWNedubUg/GAiEs+XoqHxoADpdHi0maXRj6wlg3mdZ5odN5I528khL8A4gZkawm8EmvSYcxIZYQVDlgGEHVTAVVZ63lSytFkMILJTxlIShtL+3bMtrcc2w+FuhD7eObhCmKZQEc9LAeYN9MPxpDqY2Ji5yYBUa7/uRPA1Qwq46dhBTEyLKcboXWsw6mcDULfAAW+jY0W1ZClWjhY5aB09wrssUwKLKdnjwqWcNhNsGtZGlecANaI01i2QbX4CpHlwNaJNe8yw9aRhQyoMAz2t2e6ViLBLbkD+ZG68Q6g5M/EXzjEa5XaXq9NxmD9a9yTAwDR6n8MS3QK+D3NYhqdRiQ5pfkMQgQ2O3zXxnLj5pENpkjxIDXHVjoRz9keS6hLHZEkg2PTMLjYd8rNpaOI4G0NIV4tAVVXFodFtsHmKxx4pND38NbP7B3j6QIY18ffOyl6HRlUreVH+xv8n2FWi2Wb42jZU129xHTAd7e5kJBWyVntQM0MDRoI06xTjqr+wfa/8Pp1bRHSluPmI4b41d1wwwCJHrEPBFG/A0O6pez41NPpEu8AZCg3uynabBtn4GHkxEuVXsbagD0JOXmznLZ1alEa/0elpSa/MbC8htXHSa0nVyToAFTreApIgXgSUJBkxEl30ksUj9Uzc6bSiFYO07Q3KJ6lInEhxbYGpNRlGaX2htO8CtrFDzYh09AsKQ5Th27e6Z8PLAjI9fTNHCdkEbLCRvlqTDN72jKdYpQCkUgWBQ7KG1ZZJbkjZoGvkzertAXYnSwpnZmEbT1kLnw01TjZupCpWJURMy9Xo2DTtZSiaNMd7fLNv8EhuQjeqZh6HMVbm56URls2rbPTSljTkvGvRQEt8LIcJ4bAw9sGcLY+uZHU7Omqnr+TRe+Jqq3sehN27IwpO40GxUKRKlziNF0qvsfAbspDnkmu6q/R9onXuNdsw1D2Huct5x4q72UUaW4guNdjJ/bu8cvb4QkWBVBhtiS0luD4bU07V9cmCww7lAqrGKPs1oNeJcOlnjRnFPrkVOoXmlT0ZM2EhJ9ASkNA3R03KgjuGEaMJUVCXsmlYaJ6KN5w7OYVHYxKb49nsSTThvEivZ/eG2NAz6fXPMtO9Lc/ngIDt33tJa1QQmT5yZga24OF7PaqLLS34qB8GZSrQEFeU3tVSwOyKpMyQKw4vD24UYBU29qyCVqr4kmLppsDhBzU5BFQbCDkIUuvhElw/Ob2wmgiZtLwwGAJGqg83O+OtvniIMERFjpVJxTpQyZYsS05UL58NRaK1jVbHHpRsxqgmuEVmbnQoxbCtNg9DpUM+VgajkULskYevUQOj1bmZhOsGNHZOTPdU5JApau5g2eQqMJh5qnatEujW6BKErUopd4ysNiKF2cuyoouP1EJ4/OFiGCvstLWVRfliYEXWbo+wGifGZEwh8M5GtRQZ7U1o/0c/uZse5/V3qEWqycBvkFlY6sUZCqOnysAaitz+z5JjQUy2hTcnx9hI6TBUwtGI+wU8LShFYU1AvY4ComQXRbeDKEXzynd5WsZbby4FcA2KQVUIRC1dC/53b7NrVLNnC0iL3Cqz6vb7PTZEeXHPIY0HtcYSeYWS/99x+p6citOF5vfMsVpikiLwItqbGlma5BWyVYzWhCENjrrFKzOBlsRcj9jHYdaHdXcUtVpR7fG6cO4YWOJCqOSralqiNMzt0enRoqCy6TAt8Ql1HR1alUprRJi3Jdhr24v55vNo9aGzUT1hwgGBlTYycnguJhR3HUAQwrRthqyhB7nWXSsM8eMXCsz0OLbpN2/5IdEVif6jZpFYg09bZIx4kg064NpQ0gvqdCVyGWeZ6CmZRx7TCbpYyOphqz0FaE0awmgSSzBwXXUEftcYIKzQwJjKCoSp5TbOa/C9beDavJ4ilkRc538zBe6pcwrlsYwTBflemlH6TWG5EyjTCvye4dOG8/b2dmr/ewZZVaMHUoSHeaxTkCykrDtZZHVNItSuXoU1V/7Rw2tFkRzfS408AWwrKzoPZIDLSG750IcX2vUJKsTBrxI02nzENmKpK+bJcB3o7aRm1lSV3mCx3MkUJdfEThC6/89eOe8LMYveEHWD+nuWb89Duppd/ei/pXyggknwjDDoRq2ZUXlhQmVxtnU2A3iwENe3Nba9g48Qa1NCL5ARr743DoYohhe5EWNlSNaQioYAqqCqjKnyz0LLL7CpGVWOFk+1LFy6iZewVSDHdwBKLDaxGQx1bYGBtXnYDVGaWJehKg9lMOiiV1fCsP7rJXrt8KRf5MpJquQU2aNmSQDwd0Js1elupcK97L5rQXVLHobQmw5K1VEZLhagbYk20ywIfvOJdcaW9pFhvNhNAm3d1C4DAmwkE9ddxAJOPr46o4lBpQRVtX7N0MbfdbdRJKqjqaSTNqdHKwNoclG16O24nRW2wY4LiEnI7YmBtSE8P9vc/a7CfmyKi6cTQqFp1RJsq/yilmijycr4uvALdHQVYgxVbjprPjulEOe8af/Eaaqb1KxHl5nmnogG80WHdCTx5foYAoQ0sqNa0gcsq+vWs/oHz+vsaCEtaVjWLK5/DF8nSOW3HPalJClBYIGeaVLWh4IzYFoSrly5Vu6cNBTYpYktCj2pyYZXNYJGUGfDCIMDCTB6wpqWNz7r10VJpwmAPQ2LlYI2SpuAepX1QxzyvIGWwgtAG0kUvavA0ospmjAmqaM/Vi5c0OT0sUwzSs7woRJJYtuydoRdzmzbjXEOYuatm+9LCYhbUTbEJisQmxuXqwSX7YCe6e6R+otlMoXi+KvVES5lPVV+1Cl7WQMHHMoUcQOcPwe7xIJj8J6AszAx2uMRhTg6oJqDfpJt1ZvP0mSiJ0YL5Hk8ET/CSJUrpAOy6ZulSWsB+YXNoSfsFXgv9AZvwEC4CwOYSDZsMLIAO6RC0IdvszLWI6CbskOG50Bu8aflyiMkrFgps5AqI6OK8FOaGXHSBJTDgFHhBMqu86cgwAxk4ZQon/nEdZm9ErORTo4vhNplDSnNzlL3D0MqL0z9aEVRobE3wXRRn6dqdzx8QqGgJEczRzvDquT/o7GjUgwDzqQpTqlqRiW/YFDarL5Z7zv2DnviGzc8tXUHccUkF1JgMaFe8xil4UYucberTu5InrgR4uXJsmSZ1GysuQKXgbOTBjB+MMEoTs7ybFG2sNml5mDUzNkhV3TvUHGu0xs2a53CubBu6QfDgjpR2weLPLVxmC9hwWl/xvssiqyEfA5Bz6hXChG9o1JxzdySoKTKamzyCiOuyvQADWibrqG2Vf+4q6p3aQmjDCBhrpq1/kGUghYFGEWGWAq8Jd80UD6SahZlmdLJuAq2vKjXiyc1zSBWkUpzBpMlY3L5eq/jFVG99IAdqTxB62RuWrlwg1adqOd+uZZsM2UaZkyZkL3X8wU3C2ObnvGsDBq+fKaYlsl0FTQttkVi9kmJCHFfzxuUXXLd622Fq82S9HCyd2FK/c1k6oAR4Pe7nPROwDTHWUAvvE5sD3wUxDUdFB06rK8Q6/yBMERfDEOt2V7H8y7sBWmG+swk688mSZlqJYDcMoVbZLk1sKKpyT7Pz2h1XqLjKy8h0A+tB3lYkBVzN9/SvqUxpsCDLXZWtSwKT2FtlgZZh8B92vTJ2ao99P7Wpgosu9KqwlJwNh6AcQ0qEzQLBwIdHpadxa052T7EgIZSC8F/b8JYqxhPb+JqBjx2Z6mXUfvJcL1SNuMLO17cMRtr+j8uv2AGDnk5y6pXFpG1aWJhY8q0sim422bKyCIeFzviCiz9M4rqnHCnDi3+uWnzeFYMLrKgn26zk5TSNemCloIfUaa/xsJy6Zw1b+lKbVYKwSXtLGqfeNEmZxBQ7RKY6L27DLxjS5yo2XkeUldNomu5HWwyCY6CNhPYsI1SuPzN9ZrHzOZegvQaXgZboVUvPtUUrkBJxlfUgszmodWDVVdhcPbiFEgumCC22ABYGC7QElE3L1EPZ6JPn/P7dV/U7ljlYQpxMhim18ODZMDJSXEdFJBlAMiHA4hBd1Ha3KQw2wiqVb9ZPCryqDbzQXL84LrgjomTIijSRV3MqBRIl1wRYwKdVRj7OKUt9hPftvipTDA3FMXpMMfSzKmxoSdmKnSKuzoTyr0lCK/AOLLSKKUA3V59pUhbp/QKvc3t737h8pam3ahhRLGvRRK04aTnAK6EknrdWL63JDNLzvL780uQYoTsIY4r1CMlfnCrKzMedeZaF4qmR+2QapFBT/oKrEcy41vNi05t3v/C83t58H3LqFQg1OugNaEl7Jq4Cy9Chr2C+bSwjtFwdgaU4inuY5bYwDnmZkizTO5dfelHvgA9RaKvhOsnneoC0FAJrcKD5C1wrWFpmC42KOvA06QBxHaQsojBUEW4c04mVhjJG1QZcCzDBR1IKtIooQuGp0LK3UCGlX8eGkCWNN9rgRPr17N7+dyy+WG7FlBdNuAaKanDGW4Kq5Auq/rdb0IKm2UJNWFlaKZkpkoXWYn+hJBoxpIo5n5oF6P/qntcvQx+dUw+iq0muGQVDaJ1ZzN1zRkrjOJUM8yCx15vaQKMTP7wSSMkx8OnkJp9i5p7dCy6UbBoAgmdkgVS91oIqn+5yM6BlWWwGvEp5uQq86G9Lg8W+EoRuPJp1tXWacNMbr00VWknTlJ1lIS+G7r9+yVfOFkM2F7LRcG5vz/t3XQNsT0GqxixFzRjhZV0ZrTmCTOL0cjDQK2sgq3Bm45lP+IOV+JJBmU0IdmNonKsmneCpsVKvDqQqCsMqR8rXzzU/79v1mqwEyxL11K7KT8mo6PVDlt+EdVVVL50xdMOEe8iakZOy6f9FsuKT+IZ9UYsk6nsvX3rOaxcvs8Zs3ug2dlKZAi8tY28gTKsT48e0pIEMwtSajAxF2wl+GhGcIW3CW3aYvsMwD0DLzkAxNwEp8NHn6KctJUuvW7r8FYvkCfbEYDd/UCKDYEuapjqDW/XobwGedJagBy0YA6WzQB7+2St+Nfb6g/64HeanxarNb6AbkeyT/7zrVfePn7xz9JAAIbQYQht4ApoCb36klRZLuDh0mrc+t1oDFCazTZnLNNuAj32JYvf4OB9KZWsD1jrcuzd4NZqkWklUwN4I3Ng+H/OSwXnv2flK86BLWp9E8YlfYJu9VwKvHChzKmRrxVXa4laRldDqNHTIsoruuYVBWR27EXvZMu0t9gYf2femC5p9rLKiS8haK89tbV1Pumlfxt2KlyiDgNU6D6l93gsEpZl1qNGBGT9hRHQbAz5giX/SutfG6LQYTqYNM4IVmK3sTrVjmA/Y4oX9Z3x475toKbKsSk0U7bRotHS9aLNLNAtOi7jaImBVllZgEM1sjQqRufjMnRI90/QGvfIb+lQi9pv730ZdCZzqBve9xK4PA+dFQ5l+5GYgNnbAzHw1xTAmK3kNPGzoFNbTT7x+X3h59wX8o1vXemaes4XeVuaU9EbRL3ugt/sje9+0q9lBS+HL0vRlrSaVYFnexnNktlRcbbXE8glgRj1oSUVJm8mynQwF8hAlSatYpkzV5BYPvZ0f2fuW3YMdIthZqoiBFQaOtGEZFXzW6F0QFpK2fJyvBmoyMhgcGxcVxlfqZLAwZy7099D+bs6QQZii14FU0gSOVhiuXf3F39jzpgO9XT0l+VgbclYILRctGutESZvRHCy3PdJWd7ndUrphCvVQK0T+Tfcf35FF4MMge4v9QeGXL1g88OE9b9zdX7ZMOG3VqVLIGiK0LsC6CBMp0hoGQwFreO4J6DN+sJZ08qbkePHG3hN4YhHVVnOj0dxS9H5v9GU/sufNFyw9g75+keK0FLQy2cth0cVhQV/AjhKctvhbccW3bBIGxryn5N2IdOxq4SlTS2OjafrcOLWr6+vD8XCENH+9HaV21I6GOB61Y9rzwPoTHz30xcfbY3FOn9dQSKFNZ+xj8koCCNkJdZS7Sx7iSd8unaxfjLNVkqVpVAyCZFn4THUvetQEDOrD8xv733phfz+LqEGJd/XFuiLpvrA4GHDUuZetdWvmA64Ea3F1BgJrKrYwNMVrkc0MJGzl4dhI2FodthlM5CESsAhShq1D45WPPvHFe8ePpxSGvOs0PwxJqB0XD0JRXcwBrmAVCq5ODKw4V6re71X92O3fFsalYGhFGqewpvN7+39z/1uoWtBRVYBF6Vb0dIFY5cGihJ+bkIsbEpFPF6q2Glgp1R3wVGiJZio9gUhgtXm0fRZUtOf4+lqGVB4PKtgaITESI/rr0fb4fz30lZ8MH4wNfxL6TRoEWEBYkE91kVpsEvsUJFbFBE9KpoRdPPlLrXN9aPPwvP4zP7zvzXuaHT32YzQ8X1CVA887FhatJJOzRtlgtQiAtzYB2GJUnT5gTVOIJUXUsEUSK8Ori60ssUZFaJF+HNNIun88etMXj/8Q635S3udsCsKClqyVIMwIz244PqB63i3FwVrfTcVTSh1I0X/XLF783t1XZ2YhSQSCUxgMVUuDhRIozCU4DbfwKahKaq2eRiV4eoB1MsZW1oYlJjwupNJwNFwfjxhbGWpZdLlOpJ/vrN71Pw9fv4JDgI46mkBYRY9W7FpXuz0F36SynKo1jrJ6Op7EMEiU5PiBPa+lYIO5fgN2AEuxCaNqsT8ooZucMsrJfbBtTKvTCayuQkxW+CsSq4OtrBbb8fpoSPYWq0ia9TsyYNHO1D46PvLfnvjyT4eP+mT60O2nQlhQDWE8b8dzQjihNoRKB3anB+u/mCa0LFRTlkKtOF7QP/ChvdceXNjHHh8Dq9ChwsX0S9lcI5x7B1UcyUhuWp0mJXg6gTULW97dehq21khyFaOe8ER2GEPK5Bapxc8euenLx28d4ijFju9RhtkMwwiJBBuqPZi1chsqSKza8kmtTSWfgpOARHu+afnKd+562UJReRK0KfDKfJ7m8S2WpMipqOoa7KcVVdsAWKkqWvBSqmnYIl1JcottLIYUm/lscvGeR0dHPnP0Gz9Yvc8mHE0ibBrI6ukmp6BAsFaCQQtOgmkCT/yCKxYOvm/P1Qf7e3sMpoIhNskNXqQBOca8EaqqUrvTpgRPM7A2MLZmYKstKXRt1onjEYuuvJPwRBtFbrEwI7biO2t3/92xG58YHo0ztDCa5zhNrVUsA5zC9wi8wxTlGbr2RzzRP3uapXfvftVVS5fkDGNNWOCUoSZUAJRQ4KApKe09LZqYgqptYFptC2CdPLY4K7NF5k5Jdo2Pr68y2gq2kPE0FmzlDbLlv3Lsh6QZD41WUt33JgxSmYYzmJBtG5/5xFsq86sGk+nIvb0dVOX2xp1XlhqbnqaA9hhhVsFL58i5ez1tedWohb6dUXX6gXWy2ALht5hEpd/ZMVTR1eZBEGx7ZeSx6Cpasl0drd+w+pPPr/zgsdGRcN0rhVS7+lNPEbuc1bS4G0azLWEtw/xb7h/sunbpimt3PH9Hf7GnFW+SrCfpxU3xAbMGpLIITn70HjJNA1bGtl1RtS2ANdWQ72LLGFQRXWJyUcxnOGK1yPYWA6vlDcbcuBCqX1+57evHb/vp6HGcoqmqCTP1NYGpEm2K2Kpzyad+ynP6z7hmx6XXLF82KCmyjJ7GMv21QJ7DyZRfRWURjdWgajsxaw8wBVWn22DfrsCahq0kfTEEVUrQF8OL4FXU4mg8ItHFwBqzRZ/aAj4JO+ad+cXto6Oj31y9/fqV2yjOeIITOol8AHQdt9HL9vWWX7p00Rt2PD+b5xrXkw1uY5EklaPRnA5O1O55m1qP1RgLmqzPM3YTk84C69SwZU07zOQyoz6TEaP1lrcTm/ki1djAHxc2X/a34x8PH/zu8btuHz58/+jQZox3pwt+fm/vZQsHX7b0nMsXDlrjk0aagWlnlNR0NhZK8VYT8FQZVdJcc7ujahsBazq2NjC5RICJ6GK4DMeZ7hI8MYzQLTMWZuoEyM5j49Vbhw/cuv7g7WsPEcs6Tu1TPn8CxzN7uy9dPO/yxYNXDi5YbhYNQNFI6rkNbpBifddQGRzDzgSVqL8ZRlVK2xRV2wtYExTjVJNLqFTj6FV0JUPPKMd/xoKesqeU/YlT6WiTcTitxb/J6ica7CE8fP/644+0Rx8dHlnBteO4vtoOCb7rieaTpYWUDaClZrAMiztg4ZzB7mc2uy4YHDiv2XNOf3eZjpS0XXEj9W0GLw7qlSZFKrfEeGfdZ+kJNsxRjiOD1tMso2q7oWrbAeuE2Erai3pSdLWR9GrbCC/bz9uGxXFJ9BvrEUwoJpuF6f1hOtSpjRj2ZDoTJ9quuMKWBfJ6tpOjNL38E/dPCirpXjFb/W03VG1HYG2kFmt2nltMtQUcrSIjmF+pSK/xmOGlCtFRCBaaZKEV2pfGDrmVla4upGdc+cSHZK3mGRgMLE+OFSGkmo4h1Tf51ARcJhdU2GHVt7P6mwNgTWJrI9FVmV8y702lFJlaTEyMTHpF9ef9GqSXg3ec1UHLYYJ8aFOatIeldogBm+QWOhdBUGoiujLb2XgLWq8sRRVL3phmhqDa9qja1sDaSC2mKVaXzZ7AIL1EIBXhlKn5NhP0kv6lzIXDK3nHbIyU5+QSgRNWLqvAS5CTTxyRpAMGU04gFh3XxAYWUwLJ0aJK86H+5gZYJ+MtJi/gA0NVUI6T6MkWWNsKg5pqVCWd8x3nJs3kVEOzXCvsjpqRuc2eDvqu1Fzy2aepglS02+bA+5tjYD0VeGlTodaG2ah+DIxrfmSfkfVjCRnZKOvQ333yUsZkhjBnubxIMFR6y0p/iiDVXFeeuZCaM2CdjGZMNuBElaM3nJmCMLCE1SQFMq38Fat0z05DZK0gDHPzbFINNKGfL4TYS6rwxB2LgnEWes7Oq+6bY2CdwKifhFecQzghzGz0c4jL+J/qkfc41cSKQ8d0p7TfNZqgmkkQvTwd2bURpOZQUM0xsKaKro3hFXpfoxbyoFlR6DPOQx/vwG7MCtp0cAa2KTOpFHNBPiUtfu8OpJwNqXlE1bwC6wTwSrENdxdhKbh+iiqIOJsJptl1Fl1bXqcVV2BKaYqICqd6xkBq7oF1UvBKVa91jIMmK44KO69MEFu8zVq86jUQATYBpmDgQwemZxik+PH/AQYYSBDpTYUEAAAAAElFTkSuQmCC", - "description": "Visualize the state of the device. Fetches the value from device using RPC or using attribute subscription.", - "descriptor": { - "type": "rpc", - "sizeX": 2.5, - "sizeY": 2.5, - "resources": [], - "templateHtml": "", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-led-indicator-widget-settings", - "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":true,\"title\":\"Led indicator\",\"ledColor\":\"#4caf50\",\"valueAttribute\":\"value\",\"retrieveValueMethod\":\"attribute\",\"parseValueFunction\":\"return data ? true : false;\",\"performCheckStatus\":true,\"checkStatusMethod\":\"checkStatus\"},\"title\":\"Led indicator\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}" - } - }, - { - "alias": "rpcbutton", - "name": "RPC Button", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAN8SURBVHja7dvbSxRRHAfw/X9+bhcvrJZSmllhZFSCWBDaY4FUhBRZCQXR7akLyoJYlqxU0g0tukF0oYQM3bxsYVRgbuuyu7k7szNn9vSb2Wx9qB56ac72/T7MHGYZmA/nMjvDbzzSTkbpOASPw7CUjkPx2AwhTNNQNKYphE1hiCXMtK5pWkrB8GXraZMl0sMOQ9dEdqApmIzQdIMlDDH1lLIMh5LSTYZYIp1MS6XDAGF5uEPiltoQK85dwhAtKhVPVLMhRj5ADAcyqzpkNgtJqQ9JAQIIIIAAAggggAACCCB/8wog8btfAjd+f9Y3t0GmW5bS8rO/fvavaXB2jWVlZbVHwjLC+7L1x2P8rN1ZQcWtX90EyWws7r7bSuf+BKnz+f1t3norTPV+/0HvNiFP0+5b54sahYsgn+mQrdksZfL6hYcZGe2betoxyG/LtP7OiXlIJW/a6F2Y9nPjKI3MLtrBjbM05CJIsqT6tdOI1BbWFuyRQVpbupw7yGygqqLCBZCTNJqFdNGTAQpwY2bwo5vmyL1iqr+uS3l4yaTsoaEg7bT0VXWyn/xyquQHpDz4JuCrFWHaG48OrSuJXqLHLly1Yl01VDkuq6r7+i5Qd5ABsqlctlMiN0eI0/BZhu09rXggu10J4TVoYMlWWeKr51x2IM3lcp83k4NUfAqVbrQYsn3w/lvuvTt0lQ9Hn027CNJLt3m7YaXctIaXYCHnIWfog5Src3Okg/pldo7Ys8O7S9qHXrgIEllWee35uYJ9MkAHXgVq3s9Dxguan56iHCRZUa3/hPDAa3/U7dtiuGloTTZ7aXFrnO9xPio8Zs5D5JViaqpbsGpdpK4cxDxRRItbpl02R1Jfsje2zIy+8LCI/ekkc0a4b7Lj3y8ggAACCCCAAAIIIIAA8v9C8qaoRosqXngmsmVOXHgWVxsSyxaemXosJFR2mBMxpxRQpOPB0YihKsOIjAbjdnEml8vOhUaGX97s7VEwvTdfDo+E5kyn7pe7JDQWVDZjIbtDMk5JuZGcSyQScQXDlz2XNJyS8vwp8s+bzy6kzJsPYfIigAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggg/yTfATinNXQTM7TQAAAAAElFTkSuQmCC", - "description": "Allows to send RPC command when user press the button.", - "descriptor": { - "type": "rpc", - "sizeX": 4, - "sizeY": 2, - "resources": [], - "templateHtml": "
\n
\n {{title}}\n
\n
\n
\n \n
\n
\n
\n {{ error }}\n
\n
", - "templateCss": ".tb-rpc-button {\n width: 100%;\n height: 100%;\n}\n\n.tb-rpc-button .title-container {\n font-weight: 500;\n white-space: nowrap;\n margin: 10px 0;\n}\n\n.tb-rpc-button .button-container div{\n min-width: 80%\n}\n\n.tb-rpc-button .button-container .mat-mdc-button{\n width: 100%;\n margin: 0;\n}\n\n.tb-rpc-button .error-container {\n position: absolute;\n top: 2%;\n right: 0;\n left: 0;\n z-index: 4;\n height: 14px;\n}\n\n.tb-rpc-button .error-container .button-error {\n color: #ff3315;\n white-space: nowrap;\n}", - "controllerScript": "var requestPersistent = false;\nvar persistentPollingInterval = 5000;\n\nself.onInit = function() {\n if (self.ctx.settings.requestPersistent) {\n requestPersistent = self.ctx.settings.requestPersistent;\n }\n if (self.ctx.settings.persistentPollingInterval) {\n persistentPollingInterval = self.ctx.settings.persistentPollingInterval;\n }\n \n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges();\n });\n};\n\nfunction init() {\n let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\n\n self.ctx.$scope.buttonLable = self.ctx.settings.buttonText;\n self.ctx.$scope.showTitle = self.ctx.settings.title &&\n self.ctx.settings.title.length ? true : false;\n self.ctx.$scope.title = self.ctx.settings.title;\n self.ctx.$scope.styleButton = self.ctx.settings.styleButton;\n\n if (self.ctx.settings.styleButton.isPrimary ===\n false) {\n self.ctx.$scope.customStyle = {\n 'background-color': self.ctx.$scope.styleButton.bgColor,\n 'color': self.ctx.$scope.styleButton.textColor\n };\n }\n\n if (!rpcEnabled) {\n self.ctx.$scope.error =\n 'Target device is not set!';\n }\n\n self.ctx.$scope.sendCommand = function() {\n var rpcMethod = self.ctx.settings.methodName;\n var rpcParams = self.ctx.settings.methodParams;\n if (rpcParams.length) {\n try {\n rpcParams = JSON.parse(rpcParams);\n } catch (e) {}\n }\n var timeout = self.ctx.settings.requestTimeout;\n var oneWayElseTwoWay = self.ctx.settings.oneWayElseTwoWay ?\n true : false;\n\n var commandPromise;\n if (oneWayElseTwoWay) {\n commandPromise = self.ctx.controlApi.sendOneWayCommand(\n rpcMethod, rpcParams, timeout, requestPersistent, persistentPollingInterval);\n } else {\n commandPromise = self.ctx.controlApi.sendTwoWayCommand(\n rpcMethod, rpcParams, timeout, requestPersistent, persistentPollingInterval);\n }\n commandPromise.subscribe(\n function success() {\n self.ctx.$scope.error = \"\";\n self.ctx.detectChanges();\n },\n function fail(rejection) {\n if (self.ctx.settings.showError) {\n self.ctx.$scope.error =\n rejection.status + \": \" +\n rejection.statusText;\n self.ctx.detectChanges();\n }\n }\n );\n };\n}\n\nself.onDestroy = function() {\n self.ctx.controlApi.completedCommand();\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-send-rpc-widget-settings", - "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":5000,\"oneWayElseTwoWay\":true,\"buttonText\":\"Send RPC\",\"styleButton\":{\"isRaised\":true,\"isPrimary\":false},\"methodName\":\"rpcCommand\",\"methodParams\":\"{}\"},\"title\":\"RPC Button\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "update_attributes", - "name": "Update device attribute", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAWhSURBVHja7dv7b1NVHADw/T/fW/dk3SM6B3NTBkoGQ+kkBFeRiVHDL4jxARghZERmcEzqYBMqk+sCyTaUoQQhcwJFBUaZUBDHxmNjK+vWtbf30a/fc2/LY8JCNoy5zff7w93t6bnfnk/POe3N2WkaiojbOkxCmskwbB0mJU0wdF3TVJuGpum6oBDE0LWYEo1GIzYMarYS00iCaeRQlahuDTQbRlyPKipJCKIpEdsyTEpE0Qhi6LGJGNo6CKAbadQhIcPeECNEXUKQaBBtHsGogKipAFFNyIjdISMWJGJ/SIQhDGEIQxjCEIYwhCEMYchUcaHx5lRPDzRenFSiX3n85OM3cKjx3BOHPOemw7n8nQ8UtoDv7vqFf+Bf13RD66SSb+HUFI247qcVg96+xKN3s9UeaHqgguq/NWOI00WHP6D+URAV3nsMyMC28Ska8RGEEXNXJq/24mTITah9cpDDx3o9+yP04Kyn3SsgvoamSzjUAq4DiKF9O35JXHBlp/yTgAx7PadRkakn+uWrl+Uheuabr3qogtK2vTOxZnO75Qs67VwK3h45a4FsHO8IeILdrQTZdchzErFPvo7YcaS/Edw/JDLOHFKRlzMbKlVsd+QWOgmyGSpLHEfPlcKsCuwrzpkLG836vuysIidB/AXOMvgS589H3CINiC488lRhieTF8UXp8xzVpsTvfNol1cTfyIXSpjJHdplek+WEwDvpBMnLy4dm7IAjiHOqjpeAsxr9hWbGJwC5gfXQpj37/Fh8DfgU9zaM5q2whtZbzutY5/hL1HflDlC1VlxSHMQPM0ca4BrOd4mxqM2uiBg1OXqd5MODcFjUbXIHcRP03RtaNVLLkGpCVhnKYudEAmINLZeVceaQclpzgY19UGfNkXh382cFC02IkblAlrdAh1goS19tzpE7UCXL6+BEH+y+Bl5xwVXYQdO6W1lUIMu7RRKKW/u2VcPv90FyqdCE0Bxpggv3Q0ZFxvXw63Qh+a/Q4TdoMCHUar94DWqX8VbW2iQkAoWVFAIShvUmpB+KRFEXvlztdQyJC86SR0RptniiQZweTXfVPhrSCifuhwwkM04TUvmMhrgfDpqQi1AXhHWIe8EXIBuWWxAsElrdrF+wHLELWrWMVVbR1+lLqk35CGyirm2MuPOVZN1lxQbueTikkaYW/N0JnYizLYie8ebdF5kOxAOrj+0vzg9iheQ5uUzqxRVZ8o+l4OuXXvNtlV7CePaLXTQpNp1uLhsU9T+VPD8vpjmyQar3bZ83joMO8REmxuIqZ3vXC+XGYXjbd6D0rKi7Mqvz0Bz6hqmFvbdwTskx7R6k6Pv2vIXxS/B6dy1UUT+7fPiJVH9aZJwmxPDkg2NpL2JF0auQtYeG+WJwrqV27coA16JZMdwK6ahuzYFZn5v/mBhbKWV+TG2PbMiE/GYqWJ4RsiDDNQ6ovEznhZCxNirqBuaCcw18h38WgIy7JBi/B3m/AMovIW6WwF1ahfgBFFsZm2ZyizKsiCMNrTua+fiO9S0QS6wXj4aFd1BNVg9piduSQW1SImXY+juoJEtuW6nUIfEWPPBmG1b2yFhyafehGadzryXmSErcNNa4+TaeIQxhCEMYwhCGMIQhDGHIfwBJmU010aDNN57p1jYn2ngWsjdk1Np4pimjAd3ODu3iqLkVUI+F/OeHVbsy1OHz/pDYnEnbZcOBnjOn2lq8NoyWtlNnegJhzdz3S10S6PXbNnoDokPi5pZydSI8NjYWsmFQs8MTqrmlPHU2+afMzy4QU+aHMCkRDGEIQxjCEIYwhCEMYQhDGMIQhjCEIQxhCEMYwhCGMIQhDGEIQxjCEIYwhCEMYQhDGMIQhjCEIQxhCEMYwhCGMIQhDGEIQxjCEIYwhCEMYQhDGMIQhjCEIQxhCEMYwhCGMIQh/0v8A7Y7NXI35bvdAAAAAElFTkSuQmCC", - "description": "Allows to send shared attribute update when user press the button.", - "descriptor": { - "type": "rpc", - "sizeX": 4, - "sizeY": 2, - "resources": [], - "templateHtml": "
\n
\n {{title}}\n
\n
\n
\n \n
\n
\n
\n {{ error }}\n
\n
", - "templateCss": ".tb-rpc-button {\n width: 100%;\n height: 100%;\n}\n\n.tb-rpc-button .title-container {\n font-weight: 500;\n white-space: nowrap;\n margin: 10px 0;\n}\n\n.tb-rpc-button .button-container div{\n min-width: 80%\n}\n\n.tb-rpc-button .button-container .mat-mdc-button{\n width: 100%;\n margin: 0;\n}\n\n.tb-rpc-button .error-container {\n position: absolute;\n top: 2%;\n right: 0;\n left: 0;\n z-index: 4;\n height: 14px;\n}\n\n.tb-rpc-button .error-container .button-error {\n color: #ff3315;\n white-space: nowrap;\n}", - "controllerScript": "self.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges();\n });\n};\n\nfunction init() {\n self.ctx.$scope.buttonLable = self.ctx.settings.buttonText;\n self.ctx.$scope.showTitle = self.ctx.settings.title &&\n self.ctx.settings.title.length ? true : false;\n self.ctx.$scope.title = self.ctx.settings.title;\n self.ctx.$scope.styleButton = self.ctx.settings.styleButton;\n let entityAttributeType = self.ctx.settings.entityAttributeType;\n let entityParameters = JSON.parse(self.ctx.settings.entityParameters);\n\n if (self.ctx.settings.styleButton.isPrimary ===\n false) {\n self.ctx.$scope.customStyle = {\n 'background-color': self.ctx.$scope.styleButton\n .bgColor,\n 'color': self.ctx.$scope.styleButton.textColor\n };\n }\n\n let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n\n self.ctx.$scope.sendUpdate = function() {\n let attributes = [];\n for (let key in entityParameters) {\n attributes.push({\n \"key\": key,\n \"value\": entityParameters[key]\n });\n }\n \n let entityId = {\n entityType: \"DEVICE\",\n id: self.ctx.defaultSubscription.targetDeviceId\n };\n attributeService.saveEntityAttributes(entityId,\n entityAttributeType, attributes).subscribe(\n function success() {\n self.ctx.$scope.error = \"\";\n self.ctx.detectChanges();\n },\n function fail(rejection) {\n if (self.ctx.settings.showError) {\n self.ctx.$scope.error =\n rejection.status + \": \" +\n rejection.statusText;\n self.ctx.detectChanges();\n }\n }\n\n );\n };\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-update-device-attribute-widget-settings", - "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"styleButton\":{\"isRaised\":true,\"isPrimary\":false},\"entityParameters\":\"{}\",\"entityAttributeType\":\"SERVER_SCOPE\",\"buttonText\":\"Update device attribute\"},\"title\":\"Update device attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"targetDeviceAliases\":[]}" - } - }, - { - "alias": "persistent_table", - "name": "Persistent RPC table", - "image": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0yMDAgMEgwVjE2MEgyMDBWMFoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0yMDAgMTIwSDBWMTIxSDIwMFYxMjBaIiBmaWxsPSIjRTBFMEUwIi8+CjxwYXRoIGQ9Ik0yMDAgODBIMFY4MUgyMDBWODBaIiBmaWxsPSIjRTBFMEUwIi8+CjxwYXRoIGQ9Ik0yMDAgMzlIMFY0MEgyMDBWMzlaIiBmaWxsPSIjRTBFMEUwIi8+CjxwYXRoIGQ9Ik0xNS42Njg1IDE5Ljk4NjhIMTQuMTUzOFYyM0gxMi43OTQ5VjE1LjE3OTdIMTUuNTQ0OUMxNi40NDczIDE1LjE3OTcgMTcuMTQzNyAxNS4zODIgMTcuNjM0MyAxNS43ODY2QzE4LjEyNDggMTYuMTkxMiAxOC4zNzAxIDE2Ljc3NjcgMTguMzcwMSAxNy41NDNDMTguMzcwMSAxOC4wNjU4IDE4LjI0MyAxOC41MDQ0IDE3Ljk4ODggMTguODU4OUMxNy43MzgxIDE5LjIwOTggMTcuMzg3MiAxOS40ODAxIDE2LjkzNiAxOS42Njk5TDE4LjY5MjQgMjIuOTMwMlYyM0gxNy4yMzY4TDE1LjY2ODUgMTkuOTg2OFpNMTQuMTUzOCAxOC44OTY1SDE1LjU1MDNDMTYuMDA4NiAxOC44OTY1IDE2LjM2NjcgMTguNzgxOSAxNi42MjQ1IDE4LjU1MjdDMTYuODgyMyAxOC4zMiAxNy4wMTEyIDE4LjAwMzEgMTcuMDExMiAxNy42MDIxQzE3LjAxMTIgMTcuMTgzMSAxNi44OTEzIDE2Ljg1OSAxNi42NTE0IDE2LjYyOTlDMTYuNDE1IDE2LjQwMDcgMTYuMDYwNSAxNi4yODI2IDE1LjU4NzkgMTYuMjc1NEgxNC4xNTM4VjE4Ljg5NjVaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0yMS4wMTgxIDIwLjA5NDJWMjNIMTkuNjU5MlYxNS4xNzk3SDIyLjY1MDlDMjMuNTI0NiAxNS4xNzk3IDI0LjIxNzQgMTUuNDA3MSAyNC43Mjk1IDE1Ljg2MThDMjUuMjQ1MSAxNi4zMTY2IDI1LjUwMjkgMTYuOTE4MSAyNS41MDI5IDE3LjY2NjVDMjUuNTAyOSAxOC40MzI4IDI1LjI1MDUgMTkuMDI5IDI0Ljc0NTYgMTkuNDU1MUMyNC4yNDQzIDE5Ljg4MTIgMjMuNTQwNyAyMC4wOTQyIDIyLjYzNDggMjAuMDk0MkgyMS4wMTgxWk0yMS4wMTgxIDE5LjAwMzlIMjIuNjUwOUMyMy4xMzQzIDE5LjAwMzkgMjMuNTAzMSAxOC44OTExIDIzLjc1NzMgMTguNjY1NUMyNC4wMTE2IDE4LjQzNjQgMjQuMTM4NyAxOC4xMDY5IDI0LjEzODcgMTcuNjc3MkMyNC4xMzg3IDE3LjI1NDcgMjQuMDA5OCAxNi45MTgxIDIzLjc1MiAxNi42Njc1QzIzLjQ5NDEgMTYuNDEzMiAyMy4xMzk2IDE2LjI4MjYgMjIuNjg4NSAxNi4yNzU0SDIxLjAxODFWMTkuMDAzOVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTMyLjY2MjYgMjAuNDU0MUMzMi41ODM4IDIxLjI4ODQgMzIuMjc1OSAyMS45NDAxIDMxLjczODggMjIuNDA5MkMzMS4yMDE3IDIyLjg3NDcgMzAuNDg3MyAyMy4xMDc0IDI5LjU5NTcgMjMuMTA3NEMyOC45NzI3IDIzLjEwNzQgMjguNDIzIDIyLjk2MDYgMjcuOTQ2OCAyMi42NjdDMjcuNDc0MSAyMi4zNjk4IDI3LjEwODkgMjEuOTQ5MSAyNi44NTExIDIxLjQwNDhDMjYuNTkzMyAyMC44NjA1IDI2LjQ1OSAyMC4yMjg1IDI2LjQ0ODIgMTkuNTA4OFYxOC43NzgzQzI2LjQ0ODIgMTguMDQwNyAyNi41Nzg5IDE3LjM5MDggMjYuODQwMyAxNi44Mjg2QzI3LjEwMTcgMTYuMjY2NCAyNy40NzU5IDE1LjgzMzIgMjcuOTYyOSAxNS41Mjg4QzI4LjQ1MzUgMTUuMjI0NCAyOS4wMTkyIDE1LjA3MjMgMjkuNjYwMiAxNS4wNzIzQzMwLjUyMzEgMTUuMDcyMyAzMS4yMTc4IDE1LjMwNjggMzEuNzQ0MSAxNS43NzU5QzMyLjI3MDUgMTYuMjQ1IDMyLjU3NjcgMTYuOTA3NCAzMi42NjI2IDE3Ljc2MzJIMzEuMzA5MUMzMS4yNDQ2IDE3LjIwMSAzMS4wNzk5IDE2Ljc5NjQgMzAuODE0OSAxNi41NDkzQzMwLjU1MzUgMTYuMjk4NyAzMC4xNjg2IDE2LjE3MzMgMjkuNjYwMiAxNi4xNzMzQzI5LjA2OTMgMTYuMTczMyAyOC42MTQ2IDE2LjM5IDI4LjI5NTkgMTYuODIzMkMyNy45ODA4IDE3LjI1MjkgMjcuODE5NyAxNy44ODQ5IDI3LjgxMjUgMTguNzE5MlYxOS40MTIxQzI3LjgxMjUgMjAuMjU3MiAyNy45NjI5IDIwLjkwMTcgMjguMjYzNyAyMS4zNDU3QzI4LjU2OCAyMS43ODk3IDI5LjAxMiAyMi4wMTE3IDI5LjU5NTcgMjIuMDExN0MzMC4xMjkyIDIyLjAxMTcgMzAuNTMwMyAyMS44OTE4IDMwLjc5ODggMjEuNjUxOUMzMS4wNjc0IDIxLjQxMTkgMzEuMjM3NSAyMS4wMTI3IDMxLjMwOTEgMjAuNDU0MUgzMi42NjI2WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMzguMDU1MiAyM0gzNi43MDE3VjE1LjE3OTdIMzguMDU1MlYyM1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTM5LjcyNTYgMjNWMTUuMTc5N0g0Mi4wMzUyQzQyLjcyNjIgMTUuMTc5NyA0My4zMzg1IDE1LjMzMzcgNDMuODcyMSAxNS42NDE2QzQ0LjQwOTIgMTUuOTQ5NSA0NC44MjQ1IDE2LjM4NjQgNDUuMTE4MiAxNi45NTIxQzQ1LjQxMTggMTcuNTE3OSA0NS41NTg2IDE4LjE2NiA0NS41NTg2IDE4Ljg5NjVWMTkuMjg4NkM0NS41NTg2IDIwLjAyOTggNDUuNDEgMjAuNjgxNSA0NS4xMTI4IDIxLjI0MzdDNDQuODE5MiAyMS44MDU4IDQ0LjM5ODQgMjIuMjM5MSA0My44NTA2IDIyLjU0MzVDNDMuMzA2MyAyMi44NDc4IDQyLjY4MTUgMjMgNDEuOTc2MSAyM0gzOS43MjU2Wk00MS4wODQ1IDE2LjI3NTRWMjEuOTE1SDQxLjk3MDdDNDIuNjgzMyAyMS45MTUgNDMuMjI5MyAyMS42OTMgNDMuNjA4OSAyMS4yNDlDNDMuOTkyIDIwLjgwMTQgNDQuMTg3MiAyMC4xNjA1IDQ0LjE5NDMgMTkuMzI2MlYxOC44OTExQzQ0LjE5NDMgMTguMDQyNSA0NC4wMDk5IDE3LjM5NDQgNDMuNjQxMSAxNi45NDY4QzQzLjI3MjMgMTYuNDk5MiA0Mi43MzcgMTYuMjc1NCA0Mi4wMzUyIDE2LjI3NTRINDEuMDg0NVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTYxLjU1NjYgMTUuMTc5N0w2My44MTI1IDIxLjE3MzhMNjYuMDYzIDE1LjE3OTdINjcuODE5M1YyM0g2Ni40NjU4VjIwLjQyMTlMNjYuNjAwMSAxNi45NzM2TDY0LjI5MDUgMjNINjMuMzE4NEw2MS4wMTQyIDE2Ljk3OUw2MS4xNDg0IDIwLjQyMTlWMjNINTkuNzk0OVYxNS4xNzk3SDYxLjU1NjZaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik03MS44NjM4IDIzLjEwNzRDNzEuMDM2NiAyMy4xMDc0IDcwLjM2NTIgMjIuODQ3OCA2OS44NDk2IDIyLjMyODZDNjkuMzM3NiAyMS44MDU4IDY5LjA4MTUgMjEuMTExMiA2OS4wODE1IDIwLjI0NDZWMjAuMDgzNUM2OS4wODE1IDE5LjUwMzQgNjkuMTkyNSAxOC45ODYgNjkuNDE0NiAxOC41MzEyQzY5LjY0MDEgMTguMDcyOSA2OS45NTUyIDE3LjcxNjYgNzAuMzU5OSAxNy40NjI0QzcwLjc2NDUgMTcuMjA4MiA3MS4yMTU3IDE3LjA4MTEgNzEuNzEzNCAxNy4wODExQzcyLjUwNDcgMTcuMDgxMSA3My4xMTUyIDE3LjMzMzUgNzMuNTQ0OSAxNy44Mzg0QzczLjk3ODIgMTguMzQzMyA3NC4xOTQ4IDE5LjA1NzYgNzQuMTk0OCAxOS45ODE0VjIwLjUwNzhINzAuMzk3NUM3MC40MzY4IDIwLjk4NzYgNzAuNTk2MiAyMS4zNjcyIDcwLjg3NTUgMjEuNjQ2NUM3MS4xNTg0IDIxLjkyNTggNzEuNTEyOSAyMi4wNjU0IDcxLjkzOSAyMi4wNjU0QzcyLjUzNjkgMjIuMDY1NCA3My4wMjM5IDIxLjgyMzcgNzMuMzk5OSAyMS4zNDAzTDc0LjEwMzUgMjIuMDExN0M3My44NzA4IDIyLjM1OSA3My41NTkyIDIyLjYyOTQgNzMuMTY4OSAyMi44MjI4QzcyLjc4MjIgMjMuMDEyNSA3Mi4zNDcyIDIzLjEwNzQgNzEuODYzOCAyMy4xMDc0Wk03MS43MDggMTguMTI4NEM3MS4zNDk5IDE4LjEyODQgNzEuMDU5OSAxOC4yNTM3IDcwLjgzNzkgMTguNTA0NEM3MC42MTk1IDE4Ljc1NSA3MC40Nzk4IDE5LjEwNDIgNzAuNDE4OSAxOS41NTE4SDcyLjkwNThWMTkuNDU1MUM3Mi44NzcxIDE5LjAxODIgNzIuNzYwNyAxOC42ODg4IDcyLjU1NjYgMTguNDY2OEM3Mi4zNTI1IDE4LjI0MTIgNzIuMDY5NyAxOC4xMjg0IDcxLjcwOCAxOC4xMjg0WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNNzguNDcwMiAyMS40MjA5Qzc4LjQ3MDIgMjEuMTg4MiA3OC4zNzM1IDIxLjAxMDkgNzguMTgwMiAyMC44ODkyQzc3Ljk5MDQgMjAuNzY3NCA3Ny42NzM1IDIwLjY2IDc3LjIyOTUgMjAuNTY2OUM3Ni43ODU1IDIwLjQ3MzggNzYuNDE0OSAyMC4zNTU2IDc2LjExNzcgMjAuMjEyNEM3NS40NjYgMTkuODk3MyA3NS4xNDAxIDE5LjQ0MDggNzUuMTQwMSAxOC44NDI4Qzc1LjE0MDEgMTguMzQxNSA3NS4zNTE0IDE3LjkyMjUgNzUuNzczOSAxNy41ODU5Qzc2LjE5NjUgMTcuMjQ5MyA3Ni43MzM2IDE3LjA4MTEgNzcuMzg1MyAxNy4wODExQzc4LjA3OTkgMTcuMDgxMSA3OC42NDAzIDE3LjI1MjkgNzkuMDY2NCAxNy41OTY3Qzc5LjQ5NjEgMTcuOTQwNCA3OS43MTA5IDE4LjM4NjIgNzkuNzEwOSAxOC45MzQxSDc4LjQwNThDNzguNDA1OCAxOC42ODM0IDc4LjMxMjcgMTguNDc1NyA3OC4xMjY1IDE4LjMxMUM3Ny45NDAzIDE4LjE0MjcgNzcuNjkzMiAxOC4wNTg2IDc3LjM4NTMgMTguMDU4NkM3Ny4wOTg4IDE4LjA1ODYgNzYuODY0MyAxOC4xMjQ4IDc2LjY4MTYgMTguMjU3M0M3Ni41MDI2IDE4LjM4OTggNzYuNDEzMSAxOC41NjcxIDc2LjQxMzEgMTguNzg5MUM3Ni40MTMxIDE4Ljk4OTYgNzYuNDk3MiAxOS4xNDUzIDc2LjY2NTUgMTkuMjU2M0M3Ni44MzM4IDE5LjM2NzQgNzcuMTc0IDE5LjQ4MDEgNzcuNjg2IDE5LjU5NDdDNzguMTk4MSAxOS43MDU3IDc4LjU5OTEgMTkuODQgNzguODg5MiAxOS45OTc2Qzc5LjE4MjggMjAuMTUxNSA3OS4zOTk0IDIwLjMzNzcgNzkuNTM5MSAyMC41NTYyQzc5LjY4MjMgMjAuNzc0NiA3OS43NTM5IDIxLjAzOTYgNzkuNzUzOSAyMS4zNTExQzc5Ljc1MzkgMjEuODczOSA3OS41MzczIDIyLjI5ODIgNzkuMTA0IDIyLjYyNEM3OC42NzA3IDIyLjk0NjMgNzguMTAzMiAyMy4xMDc0IDc3LjQwMTQgMjMuMTA3NEM3Ni45MjUxIDIzLjEwNzQgNzYuNTAwOCAyMy4wMjE1IDc2LjEyODQgMjIuODQ5NkM3NS43NTYgMjIuNjc3NyA3NS40NjYgMjIuNDQxNCA3NS4yNTgzIDIyLjE0MDZDNzUuMDUwNiAyMS44Mzk4IDc0Ljk0NjggMjEuNTE1OCA3NC45NDY4IDIxLjE2ODVINzYuMjE0NEM3Ni4yMzIzIDIxLjQ3NjQgNzYuMzQ4NiAyMS43MTQ1IDc2LjU2MzUgMjEuODgyOEM3Ni43NzgzIDIyLjA0NzUgNzcuMDYzIDIyLjEyOTkgNzcuNDE3NSAyMi4xMjk5Qzc3Ljc2MTIgMjIuMTI5OSA3OC4wMjI2IDIyLjA2NTQgNzguMjAxNyAyMS45MzY1Qzc4LjM4MDcgMjEuODA0IDc4LjQ3MDIgMjEuNjMyMiA3OC40NzAyIDIxLjQyMDlaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik04NC4xNTI4IDIxLjQyMDlDODQuMTUyOCAyMS4xODgyIDg0LjA1NjIgMjEuMDEwOSA4My44NjI4IDIwLjg4OTJDODMuNjczIDIwLjc2NzQgODMuMzU2MSAyMC42NiA4Mi45MTIxIDIwLjU2NjlDODIuNDY4MSAyMC40NzM4IDgyLjA5NzUgMjAuMzU1NiA4MS44MDAzIDIwLjIxMjRDODEuMTQ4NiAxOS44OTczIDgwLjgyMjggMTkuNDQwOCA4MC44MjI4IDE4Ljg0MjhDODAuODIyOCAxOC4zNDE1IDgxLjAzNCAxNy45MjI1IDgxLjQ1NjUgMTcuNTg1OUM4MS44NzkxIDE3LjI0OTMgODIuNDE2MiAxNy4wODExIDgzLjA2NzkgMTcuMDgxMUM4My43NjI1IDE3LjA4MTEgODQuMzIyOSAxNy4yNTI5IDg0Ljc0OSAxNy41OTY3Qzg1LjE3ODcgMTcuOTQwNCA4NS4zOTM2IDE4LjM4NjIgODUuMzkzNiAxOC45MzQxSDg0LjA4ODRDODQuMDg4NCAxOC42ODM0IDgzLjk5NTMgMTguNDc1NyA4My44MDkxIDE4LjMxMUM4My42MjI5IDE4LjE0MjcgODMuMzc1OCAxOC4wNTg2IDgzLjA2NzkgMTguMDU4NkM4Mi43ODE0IDE4LjA1ODYgODIuNTQ2OSAxOC4xMjQ4IDgyLjM2NDMgMTguMjU3M0M4Mi4xODUyIDE4LjM4OTggODIuMDk1NyAxOC41NjcxIDgyLjA5NTcgMTguNzg5MUM4Mi4wOTU3IDE4Ljk4OTYgODIuMTc5OSAxOS4xNDUzIDgyLjM0ODEgMTkuMjU2M0M4Mi41MTY0IDE5LjM2NzQgODIuODU2NiAxOS40ODAxIDgzLjM2ODcgMTkuNTk0N0M4My44ODA3IDE5LjcwNTcgODQuMjgxNyAxOS44NCA4NC41NzE4IDE5Ljk5NzZDODQuODY1NCAyMC4xNTE1IDg1LjA4MiAyMC4zMzc3IDg1LjIyMTcgMjAuNTU2MkM4NS4zNjQ5IDIwLjc3NDYgODUuNDM2NSAyMS4wMzk2IDg1LjQzNjUgMjEuMzUxMUM4NS40MzY1IDIxLjg3MzkgODUuMjE5OSAyMi4yOTgyIDg0Ljc4NjYgMjIuNjI0Qzg0LjM1MzQgMjIuOTQ2MyA4My43ODU4IDIzLjEwNzQgODMuMDg0IDIzLjEwNzRDODIuNjA3NyAyMy4xMDc0IDgyLjE4MzQgMjMuMDIxNSA4MS44MTEgMjIuODQ5NkM4MS40Mzg2IDIyLjY3NzcgODEuMTQ4NiAyMi40NDE0IDgwLjk0MDkgMjIuMTQwNkM4MC43MzMyIDIxLjgzOTggODAuNjI5NCAyMS41MTU4IDgwLjYyOTQgMjEuMTY4NUg4MS44OTdDODEuOTE0OSAyMS40NzY0IDgyLjAzMTIgMjEuNzE0NSA4Mi4yNDYxIDIxLjg4MjhDODIuNDYwOSAyMi4wNDc1IDgyLjc0NTYgMjIuMTI5OSA4My4xMDAxIDIyLjEyOTlDODMuNDQzOCAyMi4xMjk5IDgzLjcwNTIgMjIuMDY1NCA4My44ODQzIDIxLjkzNjVDODQuMDYzMyAyMS44MDQgODQuMTUyOCAyMS42MzIyIDg0LjE1MjggMjEuNDIwOVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTkwLjA1MDMgMjNDODkuOTkzIDIyLjg4OSA4OS45NDI5IDIyLjcwODIgODkuODk5OSAyMi40NTc1Qzg5LjQ4NDUgMjIuODkwOCA4OC45NzYxIDIzLjEwNzQgODguMzc0NSAyMy4xMDc0Qzg3Ljc5MDkgMjMuMTA3NCA4Ny4zMTQ2IDIyLjk0MDkgODYuOTQ1OCAyMi42MDc5Qzg2LjU3NyAyMi4yNzQ5IDg2LjM5MjYgMjEuODYzMSA4Ni4zOTI2IDIxLjM3MjZDODYuMzkyNiAyMC43NTMxIDg2LjYyMTcgMjAuMjc4NiA4Ny4wODAxIDE5Ljk0OTJDODcuNTQyIDE5LjYxNjIgODguMjAwOCAxOS40NDk3IDg5LjA1NjYgMTkuNDQ5N0g4OS44NTY5VjE5LjA2ODRDODkuODU2OSAxOC43Njc2IDg5Ljc3MjggMTguNTI3NyA4OS42MDQ1IDE4LjM0ODZDODkuNDM2MiAxOC4xNjYgODkuMTgwMiAxOC4wNzQ3IDg4LjgzNjQgMTguMDc0N0M4OC41MzkyIDE4LjA3NDcgODguMjk1NyAxOC4xNDk5IDg4LjEwNiAxOC4zMDAzQzg3LjkxNjIgMTguNDQ3MSA4Ny44MjEzIDE4LjYzNTEgODcuODIxMyAxOC44NjQzSDg2LjUxNjFDODYuNTE2MSAxOC41NDU2IDg2LjYyMTcgMTguMjQ4NCA4Ni44MzMgMTcuOTcyN0M4Ny4wNDQzIDE3LjY5MzQgODcuMzMwNyAxNy40NzQ5IDg3LjY5MjQgMTcuMzE3NEM4OC4wNTc2IDE3LjE1OTggODguNDY0IDE3LjA4MTEgODguOTExNiAxNy4wODExQzg5LjU5MiAxNy4wODExIDkwLjEzNDQgMTcuMjUyOSA5MC41MzkxIDE3LjU5NjdDOTAuOTQzNyAxNy45MzY4IDkxLjE1MTQgMTguNDE2NyA5MS4xNjIxIDE5LjAzNjFWMjEuNjU3MkM5MS4xNjIxIDIyLjE4IDkxLjIzNTUgMjIuNTk3MiA5MS4zODIzIDIyLjkwODdWMjNIOTAuMDUwM1pNODguNjE2MiAyMi4wNjAxQzg4Ljg3NCAyMi4wNjAxIDg5LjExNTcgMjEuOTk3NCA4OS4zNDEzIDIxLjg3MjFDODkuNTcwNSAyMS43NDY3IDg5Ljc0MjQgMjEuNTc4NSA4OS44NTY5IDIxLjM2NzJWMjAuMjcxNUg4OS4xNTMzQzg4LjY2OTkgMjAuMjcxNSA4OC4zMDY1IDIwLjM1NTYgODguMDYzIDIwLjUyMzlDODcuODE5NSAyMC42OTIyIDg3LjY5NzggMjAuOTMwMyA4Ny42OTc4IDIxLjIzODNDODcuNjk3OCAyMS40ODg5IDg3Ljc4MDEgMjEuNjg5NSA4Ny45NDQ4IDIxLjgzOThDODguMTEzMSAyMS45ODY3IDg4LjMzNjkgMjIuMDYwMSA4OC42MTYyIDIyLjA2MDFaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik05Mi4zMDA4IDIwLjA1MTNDOTIuMzAwOCAxOS4xNDg5IDkyLjUxMiAxOC40MjkyIDkyLjkzNDYgMTcuODkyMUM5My4zNjA3IDE3LjM1MTQgOTMuOTI0NiAxNy4wODExIDk0LjYyNjUgMTcuMDgxMUM5NS4yODg5IDE3LjA4MTEgOTUuODA5OSAxNy4zMTIgOTYuMTg5NSAxNy43NzM5TDk2LjI0ODUgMTcuMTg4NUg5Ny40MjQ4VjIyLjgyMjhDOTcuNDI0OCAyMy41ODU0IDk3LjE4NjcgMjQuMTg3IDk2LjcxMDQgMjQuNjI3NEM5Ni4yMzc4IDI1LjA2NzkgOTUuNTk4NiAyNS4yODgxIDk0Ljc5MyAyNS4yODgxQzk0LjM2NjkgMjUuMjg4MSA5My45NDk3IDI1LjE5ODYgOTMuNTQxNSAyNS4wMTk1QzkzLjEzNjkgMjQuODQ0MSA5Mi44Mjg5IDI0LjYxMzEgOTIuNjE3NyAyNC4zMjY3TDkzLjIzNTQgMjMuNTQyNUM5My42MzY0IDI0LjAxODcgOTQuMTMwNSAyNC4yNTY4IDk0LjcxNzggMjQuMjU2OEM5NS4xNTEgMjQuMjU2OCA5NS40OTMgMjQuMTM4NyA5NS43NDM3IDIzLjkwMjNDOTUuOTk0MyAyMy42Njk2IDk2LjExOTYgMjMuMzI1OCA5Ni4xMTk2IDIyLjg3MTFWMjIuNDc5Qzk1Ljc0MzcgMjIuODk3OSA5NS4yNDI0IDIzLjEwNzQgOTQuNjE1NyAyMy4xMDc0QzkzLjkzNTQgMjMuMTA3NCA5My4zNzg2IDIyLjgzNzEgOTIuOTQ1MyAyMi4yOTY0QzkyLjUxNTYgMjEuNzU1NyA5Mi4zMDA4IDIxLjAwNzMgOTIuMzAwOCAyMC4wNTEzWk05My42MDA2IDIwLjE2NDFDOTMuNjAwNiAyMC43NDc3IDkzLjcxODggMjEuMjA3OCA5My45NTUxIDIxLjU0NDRDOTQuMTk1IDIxLjg3NzQgOTQuNTI2MiAyMi4wNDM5IDk0Ljk0ODcgMjIuMDQzOUM5NS40NzUxIDIyLjA0MzkgOTUuODY1NCAyMS44MTg0IDk2LjExOTYgMjEuMzY3MlYxOC44MTA1Qzk1Ljg3MjYgMTguMzcwMSA5NS40ODU4IDE4LjE0OTkgOTQuOTU5NSAxOC4xNDk5Qzk0LjUyOTggMTguMTQ5OSA5NC4xOTUgMTguMzIgOTMuOTU1MSAxOC42NjAyQzkzLjcxODggMTkuMDAwMyA5My42MDA2IDE5LjUwMTYgOTMuNjAwNiAyMC4xNjQxWiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTAxLjMzIDIzLjEwNzRDMTAwLjUwMiAyMy4xMDc0IDk5LjgzMTEgMjIuODQ3OCA5OS4zMTU0IDIyLjMyODZDOTguODAzNCAyMS44MDU4IDk4LjU0NzQgMjEuMTExMiA5OC41NDc0IDIwLjI0NDZWMjAuMDgzNUM5OC41NDc0IDE5LjUwMzQgOTguNjU4NCAxOC45ODYgOTguODgwNCAxOC41MzEyQzk5LjEwNiAxOC4wNzI5IDk5LjQyMTEgMTcuNzE2NiA5OS44MjU3IDE3LjQ2MjRDMTAwLjIzIDE3LjIwODIgMTAwLjY4MSAxNy4wODExIDEwMS4xNzkgMTcuMDgxMUMxMDEuOTcxIDE3LjA4MTEgMTAyLjU4MSAxNy4zMzM1IDEwMy4wMTEgMTcuODM4NEMxMDMuNDQ0IDE4LjM0MzMgMTAzLjY2MSAxOS4wNTc2IDEwMy42NjEgMTkuOTgxNFYyMC41MDc4SDk5Ljg2MzNDOTkuOTAyNyAyMC45ODc2IDEwMC4wNjIgMjEuMzY3MiAxMDAuMzQxIDIxLjY0NjVDMTAwLjYyNCAyMS45MjU4IDEwMC45NzkgMjIuMDY1NCAxMDEuNDA1IDIyLjA2NTRDMTAyLjAwMyAyMi4wNjU0IDEwMi40OSAyMS44MjM3IDEwMi44NjYgMjEuMzQwM0wxMDMuNTY5IDIyLjAxMTdDMTAzLjMzNyAyMi4zNTkgMTAzLjAyNSAyMi42Mjk0IDEwMi42MzUgMjIuODIyOEMxMDIuMjQ4IDIzLjAxMjUgMTAxLjgxMyAyMy4xMDc0IDEwMS4zMyAyMy4xMDc0Wk0xMDEuMTc0IDE4LjEyODRDMTAwLjgxNiAxOC4xMjg0IDEwMC41MjYgMTguMjUzNyAxMDAuMzA0IDE4LjUwNDRDMTAwLjA4NSAxOC43NTUgOTkuOTQ1NiAxOS4xMDQyIDk5Ljg4NDggMTkuNTUxOEgxMDIuMzcyVjE5LjQ1NTFDMTAyLjM0MyAxOS4wMTgyIDEwMi4yMjcgMTguNjg4OCAxMDIuMDIyIDE4LjQ2NjhDMTAxLjgxOCAxOC4yNDEyIDEwMS41MzUgMTguMTI4NCAxMDEuMTc0IDE4LjEyODRaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0xMDkuMDUzIDE1Ljc3NTlWMTcuMTg4NUgxMTAuMDc5VjE4LjE1NTNIMTA5LjA1M1YyMS4zOTk0QzEwOS4wNTMgMjEuNjIxNCAxMDkuMDk2IDIxLjc4MjYgMTA5LjE4MiAyMS44ODI4QzEwOS4yNzIgMjEuOTc5NSAxMDkuNDI5IDIyLjAyNzggMTA5LjY1NSAyMi4wMjc4QzEwOS44MDUgMjIuMDI3OCAxMDkuOTU3IDIyLjAwOTkgMTEwLjExMSAyMS45NzQxVjIyLjk4MzlDMTA5LjgxNCAyMy4wNjYyIDEwOS41MjggMjMuMTA3NCAxMDkuMjUyIDIzLjEwNzRDMTA4LjI0OSAyMy4xMDc0IDEwNy43NDggMjIuNTU0MiAxMDcuNzQ4IDIxLjQ0NzhWMTguMTU1M0gxMDYuNzkyVjE3LjE4ODVIMTA3Ljc0OFYxNS43NzU5SDEwOS4wNTNaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0xMTMuMTE0IDIxLjEzMDlMMTE0LjI5NSAxNy4xODg1SDExNS42ODdMMTEzLjM3NyAyMy44ODA5QzExMy4wMjIgMjQuODU4NCAxMTIuNDIxIDI1LjM0NzIgMTExLjU3MiAyNS4zNDcyQzExMS4zODIgMjUuMzQ3MiAxMTEuMTczIDI1LjMxNDkgMTEwLjk0NCAyNS4yNTA1VjI0LjI0MDdMMTExLjE5MSAyNC4yNTY4QzExMS41MiAyNC4yNTY4IDExMS43NjcgMjQuMTk2IDExMS45MzIgMjQuMDc0MkMxMTIuMSAyMy45NTYxIDExMi4yMzMgMjMuNzU1NSAxMTIuMzMgMjMuNDcyN0wxMTIuNTE4IDIyLjk3MzFMMTEwLjQ3NyAxNy4xODg1SDExMS44ODRMMTEzLjExNCAyMS4xMzA5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTIxLjUzIDIwLjE1MzNDMTIxLjUzIDIxLjA1MjEgMTIxLjMyNiAyMS43NyAxMjAuOTE4IDIyLjMwNzFDMTIwLjUxIDIyLjg0MDcgMTE5Ljk2MiAyMy4xMDc0IDExOS4yNzQgMjMuMTA3NEMxMTguNjM3IDIzLjEwNzQgMTE4LjEyNyAyMi44OTc5IDExNy43NDQgMjIuNDc5VjI1LjIzNDRIMTE2LjQzOFYxNy4xODg1SDExNy42NDJMMTE3LjY5NSAxNy43NzkzQzExOC4wNzggMTcuMzEzOCAxMTguNTk5IDE3LjA4MTEgMTE5LjI1OCAxNy4wODExQzExOS45NjcgMTcuMDgxMSAxMjAuNTIyIDE3LjM0NiAxMjAuOTIzIDE3Ljg3NkMxMjEuMzI4IDE4LjQwMjMgMTIxLjUzIDE5LjEzNDYgMTIxLjUzIDIwLjA3MjhWMjAuMTUzM1pNMTIwLjIzIDIwLjA0MDVDMTIwLjIzIDE5LjQ2MDQgMTIwLjExNCAxOS4wMDAzIDExOS44ODEgMTguNjYwMkMxMTkuNjUyIDE4LjMyIDExOS4zMjMgMTguMTQ5OSAxMTguODkzIDE4LjE0OTlDMTE4LjM2IDE4LjE0OTkgMTE3Ljk3NiAxOC4zNzAxIDExNy43NDQgMTguODEwNVYyMS4zODg3QzExNy45OCAyMS44Mzk4IDExOC4zNjcgMjIuMDY1NCAxMTguOTA0IDIyLjA2NTRDMTE5LjMxOSAyMi4wNjU0IDExOS42NDMgMjEuODk4OSAxMTkuODc2IDIxLjU2NTlDMTIwLjExMiAyMS4yMjkzIDEyMC4yMyAyMC43MjA5IDEyMC4yMyAyMC4wNDA1WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTI1LjE5OSAyMy4xMDc0QzEyNC4zNzIgMjMuMTA3NCAxMjMuNyAyMi44NDc4IDEyMy4xODUgMjIuMzI4NkMxMjIuNjczIDIxLjgwNTggMTIyLjQxNyAyMS4xMTEyIDEyMi40MTcgMjAuMjQ0NlYyMC4wODM1QzEyMi40MTcgMTkuNTAzNCAxMjIuNTI4IDE4Ljk4NiAxMjIuNzUgMTguNTMxMkMxMjIuOTc1IDE4LjA3MjkgMTIzLjI5IDE3LjcxNjYgMTIzLjY5NSAxNy40NjI0QzEyNC4wOTkgMTcuMjA4MiAxMjQuNTUxIDE3LjA4MTEgMTI1LjA0OCAxNy4wODExQzEyNS44NCAxNy4wODExIDEyNi40NSAxNy4zMzM1IDEyNi44OCAxNy44Mzg0QzEyNy4zMTMgMTguMzQzMyAxMjcuNTMgMTkuMDU3NiAxMjcuNTMgMTkuOTgxNFYyMC41MDc4SDEyMy43MzJDMTIzLjc3MiAyMC45ODc2IDEyMy45MzEgMjEuMzY3MiAxMjQuMjEgMjEuNjQ2NUMxMjQuNDkzIDIxLjkyNTggMTI0Ljg0OCAyMi4wNjU0IDEyNS4yNzQgMjIuMDY1NEMxMjUuODcyIDIyLjA2NTQgMTI2LjM1OSAyMS44MjM3IDEyNi43MzUgMjEuMzQwM0wxMjcuNDM4IDIyLjAxMTdDMTI3LjIwNiAyMi4zNTkgMTI2Ljg5NCAyMi42Mjk0IDEyNi41MDQgMjIuODIyOEMxMjYuMTE3IDIzLjAxMjUgMTI1LjY4MiAyMy4xMDc0IDEyNS4xOTkgMjMuMTA3NFpNMTI1LjA0MyAxOC4xMjg0QzEyNC42ODUgMTguMTI4NCAxMjQuMzk1IDE4LjI1MzcgMTI0LjE3MyAxOC41MDQ0QzEyMy45NTQgMTguNzU1IDEyMy44MTUgMTkuMTA0MiAxMjMuNzU0IDE5LjU1MThIMTI2LjI0MVYxOS40NTUxQzEyNi4yMTIgMTkuMDE4MiAxMjYuMDk2IDE4LjY4ODggMTI1Ljg5MiAxOC40NjY4QzEyNS42ODggMTguMjQxMiAxMjUuNDA1IDE4LjEyODQgMTI1LjA0MyAxOC4xMjg0WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTQ0Ljg4MiAyMC45ODU4QzE0NC44ODIgMjAuNjQyMSAxNDQuNzYxIDIwLjM3NzEgMTQ0LjUxNyAyMC4xOTA5QzE0NC4yNzcgMjAuMDA0NyAxNDMuODQyIDE5LjgxNjcgMTQzLjIxMiAxOS42MjdDMTQyLjU4MiAxOS40MzcyIDE0Mi4wOCAxOS4yMjU5IDE0MS43MDggMTguOTkzMkMxNDAuOTk1IDE4LjU0NTYgMTQwLjYzOSAxNy45NjE5IDE0MC42MzkgMTcuMjQyMkMxNDAuNjM5IDE2LjYxMiAxNDAuODk1IDE2LjA5MjggMTQxLjQwNyAxNS42ODQ2QzE0MS45MjMgMTUuMjc2NCAxNDIuNTkxIDE1LjA3MjMgMTQzLjQxMSAxNS4wNzIzQzE0My45NTUgMTUuMDcyMyAxNDQuNDQgMTUuMTcyNSAxNDQuODY2IDE1LjM3M0MxNDUuMjkyIDE1LjU3MzYgMTQ1LjYyNyAxNS44NiAxNDUuODcxIDE2LjIzMjRDMTQ2LjExNCAxNi42MDEyIDE0Ni4yMzYgMTcuMDExMiAxNDYuMjM2IDE3LjQ2MjRIMTQ0Ljg4MkMxNDQuODgyIDE3LjA1NDIgMTQ0Ljc1MyAxNi43MzU1IDE0NC40OTYgMTYuNTA2M0MxNDQuMjQxIDE2LjI3MzYgMTQzLjg3NiAxNi4xNTcyIDE0My40IDE2LjE1NzJDMTQyLjk1NiAxNi4xNTcyIDE0Mi42MSAxNi4yNTIxIDE0Mi4zNjMgMTYuNDQxOUMxNDIuMTIgMTYuNjMxNyAxNDEuOTk4IDE2Ljg5NjYgMTQxLjk5OCAxNy4yMzY4QzE0MS45OTggMTcuNTIzMyAxNDIuMTMxIDE3Ljc2MzIgMTQyLjM5NiAxNy45NTY1QzE0Mi42NiAxOC4xNDYzIDE0My4wOTcgMTguMzMyNSAxNDMuNzA2IDE4LjUxNTFDMTQ0LjMxNSAxOC42OTQyIDE0NC44MDQgMTguOTAwMSAxNDUuMTcyIDE5LjEzMjhDMTQ1LjU0MSAxOS4zNjIgMTQ1LjgxMiAxOS42MjcgMTQ1Ljk4MyAxOS45Mjc3QzE0Ni4xNTUgMjAuMjI0OSAxNDYuMjQxIDIwLjU3NDEgMTQ2LjI0MSAyMC45NzUxQzE0Ni4yNDEgMjEuNjI2OCAxNDUuOTkxIDIyLjE0NiAxNDUuNDg5IDIyLjUzMjdDMTQ0Ljk5MiAyMi45MTU5IDE0NC4zMTUgMjMuMTA3NCAxNDMuNDU5IDIzLjEwNzRDMTQyLjg5MyAyMy4xMDc0IDE0Mi4zNzIgMjMuMDAzNiAxNDEuODk2IDIyLjc5NTlDMTQxLjQyMyAyMi41ODQ2IDE0MS4wNTUgMjIuMjk0NiAxNDAuNzkgMjEuOTI1OEMxNDAuNTI4IDIxLjU1NyAxNDAuMzk3IDIxLjEyNzMgMTQwLjM5NyAyMC42MzY3SDE0MS43NTZDMTQxLjc1NiAyMS4wODA3IDE0MS45MDMgMjEuNDI0NSAxNDIuMTk3IDIxLjY2OEMxNDIuNDkgMjEuOTExNSAxNDIuOTExIDIyLjAzMzIgMTQzLjQ1OSAyMi4wMzMyQzE0My45MzIgMjIuMDMzMiAxNDQuMjg2IDIxLjkzODMgMTQ0LjUyMiAyMS43NDg1QzE0NC43NjIgMjEuNTU1MiAxNDQuODgyIDIxLjMwMDkgMTQ0Ljg4MiAyMC45ODU4WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTQ4Ljk0MyAxNS43NzU5VjE3LjE4ODVIMTQ5Ljk2OVYxOC4xNTUzSDE0OC45NDNWMjEuMzk5NEMxNDguOTQzIDIxLjYyMTQgMTQ4Ljk4NiAyMS43ODI2IDE0OS4wNzIgMjEuODgyOEMxNDkuMTYxIDIxLjk3OTUgMTQ5LjMxOSAyMi4wMjc4IDE0OS41NDQgMjIuMDI3OEMxNDkuNjk1IDIyLjAyNzggMTQ5Ljg0NyAyMi4wMDk5IDE1MC4wMDEgMjEuOTc0MVYyMi45ODM5QzE0OS43MDQgMjMuMDY2MiAxNDkuNDE3IDIzLjEwNzQgMTQ5LjE0MiAyMy4xMDc0QzE0OC4xMzkgMjMuMTA3NCAxNDcuNjM4IDIyLjU1NDIgMTQ3LjYzOCAyMS40NDc4VjE4LjE1NTNIMTQ2LjY4MlYxNy4xODg1SDE0Ny42MzhWMTUuNzc1OUgxNDguOTQzWiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTU0LjQ0MyAyM0MxNTQuMzg2IDIyLjg4OSAxNTQuMzM1IDIyLjcwODIgMTU0LjI5MiAyMi40NTc1QzE1My44NzcgMjIuODkwOCAxNTMuMzY5IDIzLjEwNzQgMTUyLjc2NyAyMy4xMDc0QzE1Mi4xODMgMjMuMTA3NCAxNTEuNzA3IDIyLjk0MDkgMTUxLjMzOCAyMi42MDc5QzE1MC45NyAyMi4yNzQ5IDE1MC43ODUgMjEuODYzMSAxNTAuNzg1IDIxLjM3MjZDMTUwLjc4NSAyMC43NTMxIDE1MS4wMTQgMjAuMjc4NiAxNTEuNDczIDE5Ljk0OTJDMTUxLjkzNSAxOS42MTYyIDE1Mi41OTMgMTkuNDQ5NyAxNTMuNDQ5IDE5LjQ0OTdIMTU0LjI1VjE5LjA2ODRDMTU0LjI1IDE4Ljc2NzYgMTU0LjE2NSAxOC41Mjc3IDE1My45OTcgMTguMzQ4NkMxNTMuODI5IDE4LjE2NiAxNTMuNTczIDE4LjA3NDcgMTUzLjIyOSAxOC4wNzQ3QzE1Mi45MzIgMTguMDc0NyAxNTIuNjg4IDE4LjE0OTkgMTUyLjQ5OSAxOC4zMDAzQzE1Mi4zMDkgMTguNDQ3MSAxNTIuMjE0IDE4LjYzNTEgMTUyLjIxNCAxOC44NjQzSDE1MC45MDlDMTUwLjkwOSAxOC41NDU2IDE1MS4wMTQgMTguMjQ4NCAxNTEuMjI2IDE3Ljk3MjdDMTUxLjQzNyAxNy42OTM0IDE1MS43MjMgMTcuNDc0OSAxNTIuMDg1IDE3LjMxNzRDMTUyLjQ1IDE3LjE1OTggMTUyLjg1NyAxNy4wODExIDE1My4zMDQgMTcuMDgxMUMxNTMuOTg1IDE3LjA4MTEgMTU0LjUyNyAxNy4yNTI5IDE1NC45MzIgMTcuNTk2N0MxNTUuMzM2IDE3LjkzNjggMTU1LjU0NCAxOC40MTY3IDE1NS41NTUgMTkuMDM2MVYyMS42NTcyQzE1NS41NTUgMjIuMTggMTU1LjYyOCAyMi41OTcyIDE1NS43NzUgMjIuOTA4N1YyM0gxNTQuNDQzWk0xNTMuMDA5IDIyLjA2MDFDMTUzLjI2NyAyMi4wNjAxIDE1My41MDggMjEuOTk3NCAxNTMuNzM0IDIxLjg3MjFDMTUzLjk2MyAyMS43NDY3IDE1NC4xMzUgMjEuNTc4NSAxNTQuMjUgMjEuMzY3MlYyMC4yNzE1SDE1My41NDZDMTUzLjA2MiAyMC4yNzE1IDE1Mi42OTkgMjAuMzU1NiAxNTIuNDU2IDIwLjUyMzlDMTUyLjIxMiAyMC42OTIyIDE1Mi4wOSAyMC45MzAzIDE1Mi4wOSAyMS4yMzgzQzE1Mi4wOSAyMS40ODg5IDE1Mi4xNzMgMjEuNjg5NSAxNTIuMzM3IDIxLjgzOThDMTUyLjUwNiAyMS45ODY3IDE1Mi43MjkgMjIuMDYwMSAxNTMuMDA5IDIyLjA2MDFaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0xNTguNTU3IDE1Ljc3NTlWMTcuMTg4NUgxNTkuNTgzVjE4LjE1NTNIMTU4LjU1N1YyMS4zOTk0QzE1OC41NTcgMjEuNjIxNCAxNTguNiAyMS43ODI2IDE1OC42ODYgMjEuODgyOEMxNTguNzc2IDIxLjk3OTUgMTU4LjkzMyAyMi4wMjc4IDE1OS4xNTkgMjIuMDI3OEMxNTkuMzA5IDIyLjAyNzggMTU5LjQ2MSAyMi4wMDk5IDE1OS42MTUgMjEuOTc0MVYyMi45ODM5QzE1OS4zMTggMjMuMDY2MiAxNTkuMDMyIDIzLjEwNzQgMTU4Ljc1NiAyMy4xMDc0QzE1Ny43NTMgMjMuMTA3NCAxNTcuMjUyIDIyLjU1NDIgMTU3LjI1MiAyMS40NDc4VjE4LjE1NTNIMTU2LjI5NlYxNy4xODg1SDE1Ny4yNTJWMTUuNzc1OUgxNTguNTU3WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTY0LjEwNSAyMi40MzA3QzE2My43MjIgMjIuODgxOCAxNjMuMTc4IDIzLjEwNzQgMTYyLjQ3MyAyMy4xMDc0QzE2MS44NDIgMjMuMTA3NCAxNjEuMzY0IDIyLjkyMyAxNjEuMDM5IDIyLjU1NDJDMTYwLjcxNiAyMi4xODU0IDE2MC41NTUgMjEuNjUxOSAxNjAuNTU1IDIwLjk1MzZWMTcuMTg4NUgxNjEuODZWMjAuOTM3NUMxNjEuODYgMjEuNjc1MSAxNjIuMTY3IDIyLjA0MzkgMTYyLjc3OSAyMi4wNDM5QzE2My40MTMgMjIuMDQzOSAxNjMuODQgMjEuODE2NiAxNjQuMDYyIDIxLjM2MThWMTcuMTg4NUgxNjUuMzY4VjIzSDE2NC4xMzhMMTY0LjEwNSAyMi40MzA3WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTY5Ljk1NSAyMS40MjA5QzE2OS45NTUgMjEuMTg4MiAxNjkuODU4IDIxLjAxMDkgMTY5LjY2NSAyMC44ODkyQzE2OS40NzUgMjAuNzY3NCAxNjkuMTU4IDIwLjY2IDE2OC43MTQgMjAuNTY2OUMxNjguMjcgMjAuNDczOCAxNjcuODk5IDIwLjM1NTYgMTY3LjYwMiAyMC4yMTI0QzE2Ni45NSAxOS44OTczIDE2Ni42MjUgMTkuNDQwOCAxNjYuNjI1IDE4Ljg0MjhDMTY2LjYyNSAxOC4zNDE1IDE2Ni44MzYgMTcuOTIyNSAxNjcuMjU4IDE3LjU4NTlDMTY3LjY4MSAxNy4yNDkzIDE2OC4yMTggMTcuMDgxMSAxNjguODcgMTcuMDgxMUMxNjkuNTY0IDE3LjA4MTEgMTcwLjEyNSAxNy4yNTI5IDE3MC41NTEgMTcuNTk2N0MxNzAuOTggMTcuOTQwNCAxNzEuMTk1IDE4LjM4NjIgMTcxLjE5NSAxOC45MzQxSDE2OS44OUMxNjkuODkgMTguNjgzNCAxNjkuNzk3IDE4LjQ3NTcgMTY5LjYxMSAxOC4zMTFDMTY5LjQyNSAxOC4xNDI3IDE2OS4xNzggMTguMDU4NiAxNjguODcgMTguMDU4NkMxNjguNTgzIDE4LjA1ODYgMTY4LjM0OSAxOC4xMjQ4IDE2OC4xNjYgMTguMjU3M0MxNjcuOTg3IDE4LjM4OTggMTY3Ljg5NyAxOC41NjcxIDE2Ny44OTcgMTguNzg5MUMxNjcuODk3IDE4Ljk4OTYgMTY3Ljk4MiAxOS4xNDUzIDE2OC4xNSAxOS4yNTYzQzE2OC4zMTggMTkuMzY3NCAxNjguNjU4IDE5LjQ4MDEgMTY5LjE3IDE5LjU5NDdDMTY5LjY4MiAxOS43MDU3IDE3MC4wODMgMTkuODQgMTcwLjM3NCAxOS45OTc2QzE3MC42NjcgMjAuMTUxNSAxNzAuODg0IDIwLjMzNzcgMTcxLjAyMyAyMC41NTYyQzE3MS4xNjcgMjAuNzc0NiAxNzEuMjM4IDIxLjAzOTYgMTcxLjIzOCAyMS4zNTExQzE3MS4yMzggMjEuODczOSAxNzEuMDIyIDIyLjI5ODIgMTcwLjU4OCAyMi42MjRDMTcwLjE1NSAyMi45NDYzIDE2OS41ODggMjMuMTA3NCAxNjguODg2IDIzLjEwNzRDMTY4LjQxIDIzLjEwNzQgMTY3Ljk4NSAyMy4wMjE1IDE2Ny42MTMgMjIuODQ5NkMxNjcuMjQgMjIuNjc3NyAxNjYuOTUgMjIuNDQxNCAxNjYuNzQzIDIyLjE0MDZDMTY2LjUzNSAyMS44Mzk4IDE2Ni40MzEgMjEuNTE1OCAxNjYuNDMxIDIxLjE2ODVIMTY3LjY5OUMxNjcuNzE3IDIxLjQ3NjQgMTY3LjgzMyAyMS43MTQ1IDE2OC4wNDggMjEuODgyOEMxNjguMjYzIDIyLjA0NzUgMTY4LjU0NyAyMi4xMjk5IDE2OC45MDIgMjIuMTI5OUMxNjkuMjQ2IDIyLjEyOTkgMTY5LjUwNyAyMi4wNjU0IDE2OS42ODYgMjEuOTM2NUMxNjkuODY1IDIxLjgwNCAxNjkuOTU1IDIxLjYzMjIgMTY5Ljk1NSAyMS40MjA5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTIuNzczNCA2My40NzlDMTIuNzczNCA2My4zMDcxIDEyLjgyMzYgNjMuMTYzOSAxMi45MjM4IDYzLjA0OTNDMTMuMDI3NyA2Mi45MzQ3IDEzLjE4MTYgNjIuODc3NCAxMy4zODU3IDYyLjg3NzRDMTMuNTg5OCA2Mi44Nzc0IDEzLjc0MzggNjIuOTM0NyAxMy44NDc3IDYzLjA0OTNDMTMuOTU1MSA2My4xNjM5IDE0LjAwODggNjMuMzA3MSAxNC4wMDg4IDYzLjQ3OUMxNC4wMDg4IDYzLjY0MzcgMTMuOTU1MSA2My43ODE2IDEzLjg0NzcgNjMuODkyNkMxMy43NDM4IDY0LjAwMzYgMTMuNTg5OCA2NC4wNTkxIDEzLjM4NTcgNjQuMDU5MUMxMy4xODE2IDY0LjA1OTEgMTMuMDI3NyA2NC4wMDM2IDEyLjkyMzggNjMuODkyNkMxMi44MjM2IDYzLjc4MTYgMTIuNzczNCA2My42NDM3IDEyLjc3MzQgNjMuNDc5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTUuNjczOCA2My40NzlDMTUuNjczOCA2My4zMDcxIDE1LjcyNCA2My4xNjM5IDE1LjgyNDIgNjMuMDQ5M0MxNS45MjgxIDYyLjkzNDcgMTYuMDgyIDYyLjg3NzQgMTYuMjg2MSA2Mi44Nzc0QzE2LjQ5MDIgNjIuODc3NCAxNi42NDQyIDYyLjkzNDcgMTYuNzQ4IDYzLjA0OTNDMTYuODU1NSA2My4xNjM5IDE2LjkwOTIgNjMuMzA3MSAxNi45MDkyIDYzLjQ3OUMxNi45MDkyIDYzLjY0MzcgMTYuODU1NSA2My43ODE2IDE2Ljc0OCA2My44OTI2QzE2LjY0NDIgNjQuMDAzNiAxNi40OTAyIDY0LjA1OTEgMTYuMjg2MSA2NC4wNTkxQzE2LjA4MiA2NC4wNTkxIDE1LjkyODEgNjQuMDAzNiAxNS44MjQyIDYzLjg5MjZDMTUuNzI0IDYzLjc4MTYgMTUuNjczOCA2My42NDM3IDE1LjY3MzggNjMuNDc5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMjAuODgzOCA2My4yOTY0QzIxLjIzODMgNjMuMjk2NCAyMS41NDggNjMuMTg5IDIxLjgxMyA2Mi45NzQxQzIyLjA3OCA2Mi43NTkzIDIyLjIyNDggNjIuNDkwNyAyMi4yNTM0IDYyLjE2ODVIMjMuMTkzNEMyMy4xNzU1IDYyLjUwMTUgMjMuMDYwOSA2Mi44MTg0IDIyLjg0OTYgNjMuMTE5MUMyMi42MzgzIDYzLjQxOTkgMjIuMzU1NSA2My42NTk4IDIyLjAwMSA2My44Mzg5QzIxLjY1MDEgNjQuMDE3OSAyMS4yNzc3IDY0LjEwNzQgMjAuODgzOCA2NC4xMDc0QzIwLjA5MjQgNjQuMTA3NCAxOS40NjIyIDYzLjg0NDIgMTguOTkzMiA2My4zMTc5QzE4LjUyNzcgNjIuNzg3OSAxOC4yOTQ5IDYyLjA2NDYgMTguMjk0OSA2MS4xNDc5VjYwLjk4MTRDMTguMjk0OSA2MC40MTU3IDE4LjM5ODggNTkuOTEyNiAxOC42MDY0IDU5LjQ3MjJDMTguODE0MSA1OS4wMzE3IDE5LjExMTMgNTguNjg5OCAxOS40OTggNTguNDQ2M0MxOS44ODgzIDU4LjIwMjggMjAuMzQ4NSA1OC4wODExIDIwLjg3ODQgNTguMDgxMUMyMS41MzAxIDU4LjA4MTEgMjIuMDcwOCA1OC4yNzYyIDIyLjUwMDUgNTguNjY2NUMyMi45MzM4IDU5LjA1NjggMjMuMTY0NyA1OS41NjM1IDIzLjE5MzQgNjAuMTg2NUgyMi4yNTM0QzIyLjIyNDggNTkuODEwNSAyMi4wODE1IDU5LjUwMjYgMjEuODIzNyA1OS4yNjI3QzIxLjU2OTUgNTkuMDE5MiAyMS4yNTQ0IDU4Ljg5NzUgMjAuODc4NCA1OC44OTc1QzIwLjM3MzUgNTguODk3NSAxOS45ODE0IDU5LjA4MDEgMTkuNzAyMSA1OS40NDUzQzE5LjQyNjQgNTkuODA3IDE5LjI4ODYgNjAuMzMxNSAxOS4yODg2IDYxLjAxOVY2MS4yMDdDMTkuMjg4NiA2MS44NzY2IDE5LjQyNjQgNjIuMzkyMyAxOS43MDIxIDYyLjc1MzlDMTkuOTc3OSA2My4xMTU2IDIwLjM3MTcgNjMuMjk2NCAyMC44ODM4IDYzLjI5NjRaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0yNC4wNDc0IDYxLjA0MDVDMjQuMDQ3NCA2MC40NzEyIDI0LjE1ODQgNTkuOTU5MSAyNC4zODA0IDU5LjUwNDRDMjQuNjA2IDU5LjA0OTYgMjQuOTE3NSA1OC42OTg3IDI1LjMxNDkgNTguNDUxN0MyNS43MTYgNTguMjA0NiAyNi4xNzI1IDU4LjA4MTEgMjYuNjg0NiA1OC4wODExQzI3LjQ3NTkgNTguMDgxMSAyOC4xMTUxIDU4LjM1NSAyOC42MDIxIDU4LjkwMjhDMjkuMDkyNiA1OS40NTA3IDI5LjMzNzkgNjAuMTc5NCAyOS4zMzc5IDYxLjA4ODlWNjEuMTU4N0MyOS4zMzc5IDYxLjcyNDQgMjkuMjI4NyA2Mi4yMzI5IDI5LjAxMDMgNjIuNjg0MUMyOC43OTU0IDYzLjEzMTcgMjguNDg1NyA2My40ODA4IDI4LjA4MTEgNjMuNzMxNEMyNy42OCA2My45ODIxIDI3LjIxODEgNjQuMTA3NCAyNi42OTUzIDY0LjEwNzRDMjUuOTA3NiA2NC4xMDc0IDI1LjI2ODQgNjMuODMzNSAyNC43Nzc4IDYzLjI4NTZDMjQuMjkwOSA2Mi43Mzc4IDI0LjA0NzQgNjIuMDEyNyAyNC4wNDc0IDYxLjExMDRWNjEuMDQwNVpNMjUuMDQ2NCA2MS4xNTg3QzI1LjA0NjQgNjEuODAzMiAyNS4xOTUgNjIuMzIwNiAyNS40OTIyIDYyLjcxMDlDMjUuNzkzIDYzLjEwMTIgMjYuMTk0IDYzLjI5NjQgMjYuNjk1MyA2My4yOTY0QzI3LjIwMDIgNjMuMjk2NCAyNy42MDEyIDYzLjA5OTQgMjcuODk4NCA2Mi43MDU2QzI4LjE5NTYgNjIuMzA4MSAyOC4zNDQyIDYxLjc1MzEgMjguMzQ0MiA2MS4wNDA1QzI4LjM0NDIgNjAuNDAzMiAyOC4xOTIxIDU5Ljg4NzUgMjcuODg3NyA1OS40OTM3QzI3LjU4NjkgNTkuMDk2MiAyNy4xODU5IDU4Ljg5NzUgMjYuNjg0NiA1OC44OTc1QzI2LjE5NCA1OC44OTc1IDI1Ljc5ODMgNTkuMDkyNiAyNS40OTc2IDU5LjQ4MjlDMjUuMTk2OCA1OS44NzMyIDI1LjA0NjQgNjAuNDMxOCAyNS4wNDY0IDYxLjE1ODdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0zNC42NzY4IDYxLjM3MzVIMzUuNzYxN1Y2Mi4xODQ2SDM0LjY3NjhWNjRIMzMuNjc3N1Y2Mi4xODQ2SDMwLjExNjdWNjEuNTk5MUwzMy42MTg3IDU2LjE3OTdIMzQuNjc2OFY2MS4zNzM1Wk0zMS4yNDQ2IDYxLjM3MzVIMzMuNjc3N1Y1Ny41Mzg2TDMzLjU1OTYgNTcuNzUzNEwzMS4yNDQ2IDYxLjM3MzVaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0zNy43ODY2IDU4LjE4ODVWNjQuNjcxNEMzNy43ODY2IDY1Ljc4ODYgMzcuMjc5OSA2Ni4zNDcyIDM2LjI2NjYgNjYuMzQ3MkMzNi4wNDgyIDY2LjM0NzIgMzUuODQ1OSA2Ni4zMTQ5IDM1LjY1OTcgNjYuMjUwNVY2NS40NTU2QzM1Ljc3NDMgNjUuNDg0MiAzNS45MjQ2IDY1LjQ5ODUgMzYuMTEwOCA2NS40OTg1QzM2LjMzMjggNjUuNDk4NSAzNi41MDExIDY1LjQzNzcgMzYuNjE1NyA2NS4zMTU5QzM2LjczMzkgNjUuMTk3OCAzNi43OTMgNjQuOTkwMSAzNi43OTMgNjQuNjkyOVY1OC4xODg1SDM3Ljc4NjZaTTM2LjY5MDkgNTYuNjQ3QzM2LjY5MDkgNTYuNDg5NCAzNi43MzkzIDU2LjM1NTEgMzYuODM1OSA1Ni4yNDQxQzM2LjkzNjIgNTYuMTI5NiAzNy4wODEyIDU2LjA3MjMgMzcuMjcxIDU2LjA3MjNDMzcuNDY0NCA1Ni4wNzIzIDM3LjYxMTIgNTYuMTI3OCAzNy43MTE0IDU2LjIzODhDMzcuODExNyA1Ni4zNDk4IDM3Ljg2MTggNTYuNDg1OCAzNy44NjE4IDU2LjY0N0MzNy44NjE4IDU2LjgwODEgMzcuODExNyA1Ni45NDI0IDM3LjcxMTQgNTcuMDQ5OEMzNy42MTEyIDU3LjE1NzIgMzcuNDY0NCA1Ny4yMTA5IDM3LjI3MSA1Ny4yMTA5QzM3LjA3NzYgNTcuMjEwOSAzNi45MzI2IDU3LjE1NzIgMzYuODM1OSA1Ny4wNDk4QzM2LjczOTMgNTYuOTQyNCAzNi42OTA5IDU2LjgwODEgMzYuNjkwOSA1Ni42NDdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik00Mi45ODA1IDYzLjQyNTNDNDIuNTkzOCA2My44OCA0Mi4wMjYyIDY0LjEwNzQgNDEuMjc3OCA2NC4xMDc0QzQwLjY1ODQgNjQuMTA3NCA0MC4xODU3IDYzLjkyODQgMzkuODU5OSA2My41NzAzQzM5LjUzNzYgNjMuMjA4NyAzOS4zNzQ3IDYyLjY3NTEgMzkuMzcxMSA2MS45Njk3VjU4LjE4ODVINDAuMzY0N1Y2MS45NDI5QzQwLjM2NDcgNjIuODIzNyA0MC43MjI4IDYzLjI2NDIgNDEuNDM5IDYzLjI2NDJDNDIuMTk4MSA2My4yNjQyIDQyLjcwMyA2Mi45ODEzIDQyLjk1MzYgNjIuNDE1NVY1OC4xODg1SDQzLjk0NzNWNjRINDMuMDAyTDQyLjk4MDUgNjMuNDI1M1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTY1LjMxMSA1Ny4wMjgzSDYyLjc5NzRWNjRINjEuNzcxNVY1Ny4wMjgzSDU5LjI2MzJWNTYuMTc5N0g2NS4zMTFWNTcuMDI4M1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTcxLjE0OTQgNjIuNjMwNEw3Mi4yNjY2IDU4LjE4ODVINzMuMjYwM0w3MS41Njg0IDY0SDcwLjc2MjdMNjkuMzUwMSA1OS41OTU3TDY3Ljk3NTEgNjRINjcuMTY5NEw2NS40ODI5IDU4LjE4ODVINjYuNDcxMkw2Ny42MTUyIDYyLjUzOTFMNjguOTY4OCA1OC4xODg1SDY5Ljc2OUw3MS4xNDk0IDYyLjYzMDRaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik03NC4wMTIyIDYxLjA0MDVDNzQuMDEyMiA2MC40NzEyIDc0LjEyMzIgNTkuOTU5MSA3NC4zNDUyIDU5LjUwNDRDNzQuNTcwOCA1OS4wNDk2IDc0Ljg4MjMgNTguNjk4NyA3NS4yNzk4IDU4LjQ1MTdDNzUuNjgwOCA1OC4yMDQ2IDc2LjEzNzQgNTguMDgxMSA3Ni42NDk0IDU4LjA4MTFDNzcuNDQwOCA1OC4wODExIDc4LjA3OTkgNTguMzU1IDc4LjU2NjkgNTguOTAyOEM3OS4wNTc1IDU5LjQ1MDcgNzkuMzAyNyA2MC4xNzk0IDc5LjMwMjcgNjEuMDg4OVY2MS4xNTg3Qzc5LjMwMjcgNjEuNzI0NCA3OS4xOTM1IDYyLjIzMjkgNzguOTc1MSA2Mi42ODQxQzc4Ljc2MDMgNjMuMTMxNyA3OC40NTA1IDYzLjQ4MDggNzguMDQ1OSA2My43MzE0Qzc3LjY0NDkgNjMuOTgyMSA3Ny4xODI5IDY0LjEwNzQgNzYuNjYwMiA2NC4xMDc0Qzc1Ljg3MjQgNjQuMTA3NCA3NS4yMzMyIDYzLjgzMzUgNzQuNzQyNyA2My4yODU2Qzc0LjI1NTcgNjIuNzM3OCA3NC4wMTIyIDYyLjAxMjcgNzQuMDEyMiA2MS4xMTA0VjYxLjA0MDVaTTc1LjAxMTIgNjEuMTU4N0M3NS4wMTEyIDYxLjgwMzIgNzUuMTU5OCA2Mi4zMjA2IDc1LjQ1NyA2Mi43MTA5Qzc1Ljc1NzggNjMuMTAxMiA3Ni4xNTg5IDYzLjI5NjQgNzYuNjYwMiA2My4yOTY0Qzc3LjE2NSA2My4yOTY0IDc3LjU2NjEgNjMuMDk5NCA3Ny44NjMzIDYyLjcwNTZDNzguMTYwNSA2Mi4zMDgxIDc4LjMwOTEgNjEuNzUzMSA3OC4zMDkxIDYxLjA0MDVDNzguMzA5MSA2MC40MDMyIDc4LjE1NjkgNTkuODg3NSA3Ny44NTI1IDU5LjQ5MzdDNzcuNTUxOCA1OS4wOTYyIDc3LjE1MDcgNTguODk3NSA3Ni42NDk0IDU4Ljg5NzVDNzYuMTU4OSA1OC44OTc1IDc1Ljc2MzIgNTkuMDkyNiA3NS40NjI0IDU5LjQ4MjlDNzUuMTYxNiA1OS44NzMyIDc1LjAxMTIgNjAuNDMxOCA3NS4wMTEyIDYxLjE1ODdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik04Mi42MTY3IDYxLjA4MzVINzkuOTk1NlY2MC4yNzI1SDgyLjYxNjdWNjEuMDgzNVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTg4LjczNDQgNjIuNjMwNEw4OS44NTE2IDU4LjE4ODVIOTAuODQ1Mkw4OS4xNTMzIDY0SDg4LjM0NzdMODYuOTM1MSA1OS41OTU3TDg1LjU2MDEgNjRIODQuNzU0NEw4My4wNjc5IDU4LjE4ODVIODQuMDU2Mkw4NS4yMDAyIDYyLjUzOTFMODYuNTUzNyA1OC4xODg1SDg3LjM1NEw4OC43MzQ0IDYyLjYzMDRaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik05NS40NDgyIDY0Qzk1LjM5MSA2My44ODU0IDk1LjM0NDQgNjMuNjgxMyA5NS4zMDg2IDYzLjM4NzdDOTQuODQ2NyA2My44Njc1IDk0LjI5NTIgNjQuMTA3NCA5My42NTQzIDY0LjEwNzRDOTMuMDgxNCA2NC4xMDc0IDkyLjYxMDUgNjMuOTQ2MyA5Mi4yNDE3IDYzLjYyNEM5MS44NzY1IDYzLjI5ODIgOTEuNjkzOCA2Mi44ODY0IDkxLjY5MzggNjIuMzg4N0M5MS42OTM4IDYxLjc4MzUgOTEuOTIzIDYxLjMxNDUgOTIuMzgxMyA2MC45ODE0QzkyLjg0MzMgNjAuNjQ0OSA5My40OTE0IDYwLjQ3NjYgOTQuMzI1NyA2MC40NzY2SDk1LjI5MjVWNjAuMDJDOTUuMjkyNSA1OS42NzI3IDk1LjE4ODYgNTkuMzk3IDk0Ljk4MSA1OS4xOTI5Qzk0Ljc3MzMgNTguOTg1MiA5NC40NjcxIDU4Ljg4MTMgOTQuMDYyNSA1OC44ODEzQzkzLjcwOCA1OC44ODEzIDkzLjQxMDggNTguOTcwOSA5My4xNzA5IDU5LjE0OTlDOTIuOTMxIDU5LjMyODkgOTIuODExIDU5LjU0NTYgOTIuODExIDU5Ljc5OThIOTEuODEyQzkxLjgxMiA1OS41MDk4IDkxLjkxNDEgNTkuMjMwNSA5Mi4xMTgyIDU4Ljk2MTlDOTIuMzI1OCA1OC42ODk4IDkyLjYwNTEgNTguNDc0OSA5Mi45NTYxIDU4LjMxNzRDOTMuMzEwNSA1OC4xNTk4IDkzLjY5OTEgNTguMDgxMSA5NC4xMjE2IDU4LjA4MTFDOTQuNzkxMiA1OC4wODExIDk1LjMxNTggNTguMjQ5MyA5NS42OTUzIDU4LjU4NTlDOTYuMDc0OSA1OC45MTg5IDk2LjI3MTggNTkuMzc5MSA5Ni4yODYxIDU5Ljk2NjNWNjIuNjQxMUM5Ni4yODYxIDYzLjE3NDYgOTYuMzU0MiA2My41OTkgOTYuNDkwMiA2My45MTQxVjY0SDk1LjQ0ODJaTTkzLjc5OTMgNjMuMjQyN0M5NC4xMTA4IDYzLjI0MjcgOTQuNDA2MiA2My4xNjIxIDk0LjY4NTUgNjMuMDAxQzk0Ljk2NDggNjIuODM5OCA5NS4xNjcyIDYyLjYzMDQgOTUuMjkyNSA2Mi4zNzI2VjYxLjE4MDJIOTQuNTEzN0M5My4yOTYyIDYxLjE4MDIgOTIuNjg3NSA2MS41MzY1IDkyLjY4NzUgNjIuMjQ5QzkyLjY4NzUgNjIuNTYwNSA5Mi43OTEzIDYyLjgwNCA5Mi45OTkgNjIuOTc5NUM5My4yMDY3IDYzLjE1NDkgOTMuNDczNSA2My4yNDI3IDkzLjc5OTMgNjMuMjQyN1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTk5LjY1OTIgNjIuNTQ0NEwxMDEuMDEzIDU4LjE4ODVIMTAyLjA3Nkw5OS43Mzk3IDY0Ljg5N0M5OS4zNzgxIDY1Ljg2MzggOTguODAzNCA2Ni4zNDcyIDk4LjAxNTYgNjYuMzQ3Mkw5Ny44Mjc2IDY2LjMzMTFMOTcuNDU3IDY2LjI2MTJWNjUuNDU1Nkw5Ny43MjU2IDY1LjQ3NzFDOTguMDYyMiA2NS40NzcxIDk4LjMyMzYgNjUuNDA5IDk4LjUwOTggNjUuMjcyOUM5OC42OTk1IDY1LjEzNjkgOTguODU1MyA2NC44ODggOTguOTc3MSA2NC41MjY0TDk5LjE5NzMgNjMuOTM1NUw5Ny4xMjQgNTguMTg4NUg5OC4yMDlMOTkuNjU5MiA2Mi41NDQ0WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNNjUuOTIzMyAxMDAuMzQyQzY1LjkyMzMgMTAxLjEwOSA2NS43OTQ0IDEwMS43NzggNjUuNTM2NiAxMDIuMzUxQzY1LjI3ODggMTAyLjkyIDY0LjkxMzYgMTAzLjM1NSA2NC40NDA5IDEwMy42NTZDNjMuOTY4MyAxMDMuOTU3IDYzLjQxNjggMTA0LjEwNyA2Mi43ODY2IDEwNC4xMDdDNjIuMTcwNyAxMDQuMTA3IDYxLjYyNDcgMTAzLjk1NyA2MS4xNDg0IDEwMy42NTZDNjAuNjcyMiAxMDMuMzUyIDYwLjMwMTYgMTAyLjkyIDYwLjAzNjYgMTAyLjM2MkM1OS43NzUyIDEwMS44IDU5LjY0MSAxMDEuMTUgNTkuNjMzOCAxMDAuNDEyVjk5Ljg0ODFDNTkuNjMzOCA5OS4wOTYyIDU5Ljc2NDUgOTguNDMyIDYwLjAyNTkgOTcuODU1NUM2MC4yODczIDk3LjI3OSA2MC42NTYxIDk2LjgzODUgNjEuMTMyMyA5Ni41MzQyQzYxLjYxMjEgOTYuMjI2MiA2Mi4xNiA5Ni4wNzIzIDYyLjc3NTkgOTYuMDcyM0M2My40MDI1IDk2LjA3MjMgNjMuOTUzOSA5Ni4yMjQ0IDY0LjQzMDIgOTYuNTI4OEM2NC45MSA5Ni44Mjk2IDY1LjI3ODggOTcuMjY4MiA2NS41MzY2IDk3Ljg0NDdDNjUuNzk0NCA5OC40MTc2IDY1LjkyMzMgOTkuMDg1NCA2NS45MjMzIDk5Ljg0ODFWMTAwLjM0MlpNNjQuODk3NSA5OS44Mzc0QzY0Ljg5NzUgOTguOTEgNjQuNzExMyA5OC4xOTkyIDY0LjMzODkgOTcuNzA1MUM2My45NjY1IDk3LjIwNzQgNjMuNDQ1NSA5Ni45NTg1IDYyLjc3NTkgOTYuOTU4NUM2Mi4xMjQyIDk2Ljk1ODUgNjEuNjEwNCA5Ny4yMDc0IDYxLjIzNDQgOTcuNzA1MUM2MC44NjIgOTguMTk5MiA2MC42NzA0IDk4Ljg4NjcgNjAuNjU5NyA5OS43Njc2VjEwMC4zNDJDNjAuNjU5NyAxMDEuMjQxIDYwLjg0NzcgMTAxLjk0OCA2MS4yMjM2IDEwMi40NjRDNjEuNjAzMiAxMDIuOTc2IDYyLjEyNDIgMTAzLjIzMiA2Mi43ODY2IDEwMy4yMzJDNjMuNDUyNiAxMDMuMjMyIDYzLjk2ODMgMTAyLjk5IDY0LjMzMzUgMTAyLjUwN0M2NC42OTg3IDEwMi4wMiA2NC44ODY3IDEwMS4zMjMgNjQuODk3NSAxMDAuNDE3Vjk5LjgzNzRaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik02OC4yNTQ0IDk4LjE4ODVMNjguMjg2NiA5OC45MTg5QzY4LjczMDYgOTguMzYwNCA2OS4zMTA3IDk4LjA4MTEgNzAuMDI2OSA5OC4wODExQzcxLjI1NSA5OC4wODExIDcxLjg3NDUgOTguNzczOSA3MS44ODUzIDEwMC4xNlYxMDRINzAuODkxNlYxMDAuMTU0QzcwLjg4OCA5OS43MzU0IDcwLjc5MTMgOTkuNDI1NiA3MC42MDE2IDk5LjIyNTFDNzAuNDE1NCA5OS4wMjQ2IDcwLjEyMzUgOTguOTI0MyA2OS43MjYxIDk4LjkyNDNDNjkuNDAzOCA5OC45MjQzIDY5LjEyMDkgOTkuMDEwMyA2OC44Nzc0IDk5LjE4MjFDNjguNjM0IDk5LjM1NCA2OC40NDQyIDk5LjU3OTYgNjguMzA4MSA5OS44NTg5VjEwNEg2Ny4zMTQ1Vjk4LjE4ODVINjguMjU0NFoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTc1Ljc5NTQgMTA0LjEwN0M3NS4wMDc2IDEwNC4xMDcgNzQuMzY2NyAxMDMuODUgNzMuODcyNiAxMDMuMzM0QzczLjM3ODQgMTAyLjgxNSA3My4xMzEzIDEwMi4xMjIgNzMuMTMxMyAxMDEuMjU1VjEwMS4wNzNDNzMuMTMxMyAxMDAuNDk2IDczLjI0MDYgOTkuOTgyNCA3My40NTkgOTkuNTMxMkM3My42ODEgOTkuMDc2NSA3My45ODg5IDk4LjcyMiA3NC4zODI4IDk4LjQ2NzhDNzQuNzgwMyA5OC4yMSA3NS4yMSA5OC4wODExIDc1LjY3MTkgOTguMDgxMUM3Ni40Mjc0IDk4LjA4MTEgNzcuMDE0NiA5OC4zMjk5IDc3LjQzMzYgOTguODI3NkM3Ny44NTI1IDk5LjMyNTQgNzguMDYyIDEwMC4wMzggNzguMDYyIDEwMC45NjVWMTAxLjM3OUg3NC4xMjVDNzQuMTM5MyAxMDEuOTUyIDc0LjMwNTggMTAyLjQxNiA3NC42MjQ1IDEwMi43N0M3NC45NDY4IDEwMy4xMjEgNzUuMzU1IDEwMy4yOTYgNzUuODQ5MSAxMDMuMjk2Qzc2LjIgMTAzLjI5NiA3Ni40OTcyIDEwMy4yMjUgNzYuNzQwNyAxMDMuMDgyQzc2Ljk4NDIgMTAyLjkzOCA3Ny4xOTczIDEwMi43NDkgNzcuMzc5OSAxMDIuNTEyTDc3Ljk4NjggMTAyLjk4NUM3Ny40OTk4IDEwMy43MzMgNzYuNzY5NCAxMDQuMTA3IDc1Ljc5NTQgMTA0LjEwN1pNNzUuNjcxOSA5OC44OTc1Qzc1LjI3MDggOTguODk3NSA3NC45MzQyIDk5LjA0NDMgNzQuNjYyMSA5OS4zMzc5Qzc0LjM5IDk5LjYyNzkgNzQuMjIxNyAxMDAuMDM2IDc0LjE1NzIgMTAwLjU2Mkg3Ny4wNjg0VjEwMC40ODdDNzcuMDM5NyA5OS45ODI0IDc2LjkwMzYgOTkuNTkyMSA3Ni42NjAyIDk5LjMxNjRDNzYuNDE2NyA5OS4wMzcxIDc2LjA4NzIgOTguODk3NSA3NS42NzE5IDk4Ljg5NzVaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik04MS4yODQ3IDEwMS4wODNINzguNjYzNlYxMDAuMjcySDgxLjI4NDdWMTAxLjA4M1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTg3LjQwMjMgMTAyLjYzTDg4LjUxOTUgOTguMTg4NUg4OS41MTMyTDg3LjgyMTMgMTA0SDg3LjAxNTZMODUuNjAzIDk5LjU5NTdMODQuMjI4IDEwNEg4My40MjI0TDgxLjczNTggOTguMTg4NUg4Mi43MjQxTDgzLjg2ODIgMTAyLjUzOUw4NS4yMjE3IDk4LjE4ODVIODYuMDIyTDg3LjQwMjMgMTAyLjYzWiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNOTQuMTE2MiAxMDRDOTQuMDU4OSAxMDMuODg1IDk0LjAxMjQgMTAzLjY4MSA5My45NzY2IDEwMy4zODhDOTMuNTE0NiAxMDMuODY4IDkyLjk2MzIgMTA0LjEwNyA5Mi4zMjIzIDEwNC4xMDdDOTEuNzQ5MyAxMDQuMTA3IDkxLjI3ODUgMTAzLjk0NiA5MC45MDk3IDEwMy42MjRDOTAuNTQ0NCAxMDMuMjk4IDkwLjM2MTggMTAyLjg4NiA5MC4zNjE4IDEwMi4zODlDOTAuMzYxOCAxMDEuNzg0IDkwLjU5MSAxMDEuMzE0IDkxLjA0OTMgMTAwLjk4MUM5MS41MTEyIDEwMC42NDUgOTIuMTU5MyAxMDAuNDc3IDkyLjk5MzcgMTAwLjQ3N0g5My45NjA0VjEwMC4wMkM5My45NjA0IDk5LjY3MjcgOTMuODU2NiA5OS4zOTcgOTMuNjQ4OSA5OS4xOTI5QzkzLjQ0MTIgOTguOTg1MiA5My4xMzUxIDk4Ljg4MTMgOTIuNzMwNSA5OC44ODEzQzkyLjM3NiA5OC44ODEzIDkyLjA3ODggOTguOTcwOSA5MS44Mzg5IDk5LjE0OTlDOTEuNTk5IDk5LjMyODkgOTEuNDc5IDk5LjU0NTYgOTEuNDc5IDk5Ljc5OThIOTAuNDhDOTAuNDggOTkuNTA5OCA5MC41ODIgOTkuMjMwNSA5MC43ODYxIDk4Ljk2MTlDOTAuOTkzOCA5OC42ODk4IDkxLjI3MzEgOTguNDc0OSA5MS42MjQgOTguMzE3NEM5MS45Nzg1IDk4LjE1OTggOTIuMzY3IDk4LjA4MTEgOTIuNzg5NiA5OC4wODExQzkzLjQ1OTEgOTguMDgxMSA5My45ODM3IDk4LjI0OTMgOTQuMzYzMyA5OC41ODU5Qzk0Ljc0MjggOTguOTE4OSA5NC45Mzk4IDk5LjM3OTEgOTQuOTU0MSA5OS45NjYzVjEwMi42NDFDOTQuOTU0MSAxMDMuMTc1IDk1LjAyMjEgMTAzLjU5OSA5NS4xNTgyIDEwMy45MTRWMTA0SDk0LjExNjJaTTkyLjQ2NzMgMTAzLjI0M0M5Mi43Nzg4IDEwMy4yNDMgOTMuMDc0MiAxMDMuMTYyIDkzLjM1MzUgMTAzLjAwMUM5My42MzI4IDEwMi44NCA5My44MzUxIDEwMi42MyA5My45NjA0IDEwMi4zNzNWMTAxLjE4SDkzLjE4MTZDOTEuOTY0MiAxMDEuMTggOTEuMzU1NSAxMDEuNTM2IDkxLjM1NTUgMTAyLjI0OUM5MS4zNTU1IDEwMi41NjEgOTEuNDU5MyAxMDIuODA0IDkxLjY2NyAxMDIuOTc5QzkxLjg3NDcgMTAzLjE1NSA5Mi4xNDE0IDEwMy4yNDMgOTIuNDY3MyAxMDMuMjQzWiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNOTguMzI3MSAxMDIuNTQ0TDk5LjY4MDcgOTguMTg4NUgxMDAuNzQ0TDk4LjQwNzcgMTA0Ljg5N0M5OC4wNDYxIDEwNS44NjQgOTcuNDcxNCAxMDYuMzQ3IDk2LjY4MzYgMTA2LjM0N0w5Ni40OTU2IDEwNi4zMzFMOTYuMTI1IDEwNi4yNjFWMTA1LjQ1Nkw5Ni4zOTM2IDEwNS40NzdDOTYuNzMwMSAxMDUuNDc3IDk2Ljk5MTUgMTA1LjQwOSA5Ny4xNzc3IDEwNS4yNzNDOTcuMzY3NSAxMDUuMTM3IDk3LjUyMzMgMTA0Ljg4OCA5Ny42NDUgMTA0LjUyNkw5Ny44NjUyIDEwMy45MzZMOTUuNzkyIDk4LjE4ODVIOTYuODc3TDk4LjMyNzEgMTAyLjU0NFoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTY1LjkyMzMgMTQwLjM0MkM2NS45MjMzIDE0MS4xMDkgNjUuNzk0NCAxNDEuNzc4IDY1LjUzNjYgMTQyLjM1MUM2NS4yNzg4IDE0Mi45MiA2NC45MTM2IDE0My4zNTUgNjQuNDQwOSAxNDMuNjU2QzYzLjk2ODMgMTQzLjk1NyA2My40MTY4IDE0NC4xMDcgNjIuNzg2NiAxNDQuMTA3QzYyLjE3MDcgMTQ0LjEwNyA2MS42MjQ3IDE0My45NTcgNjEuMTQ4NCAxNDMuNjU2QzYwLjY3MjIgMTQzLjM1MiA2MC4zMDE2IDE0Mi45MiA2MC4wMzY2IDE0Mi4zNjJDNTkuNzc1MiAxNDEuOCA1OS42NDEgMTQxLjE1IDU5LjYzMzggMTQwLjQxMlYxMzkuODQ4QzU5LjYzMzggMTM5LjA5NiA1OS43NjQ1IDEzOC40MzIgNjAuMDI1OSAxMzcuODU1QzYwLjI4NzMgMTM3LjI3OSA2MC42NTYxIDEzNi44MzkgNjEuMTMyMyAxMzYuNTM0QzYxLjYxMjEgMTM2LjIyNiA2Mi4xNiAxMzYuMDcyIDYyLjc3NTkgMTM2LjA3MkM2My40MDI1IDEzNi4wNzIgNjMuOTUzOSAxMzYuMjI0IDY0LjQzMDIgMTM2LjUyOUM2NC45MSAxMzYuODMgNjUuMjc4OCAxMzcuMjY4IDY1LjUzNjYgMTM3Ljg0NUM2NS43OTQ0IDEzOC40MTggNjUuOTIzMyAxMzkuMDg1IDY1LjkyMzMgMTM5Ljg0OFYxNDAuMzQyWk02NC44OTc1IDEzOS44MzdDNjQuODk3NSAxMzguOTEgNjQuNzExMyAxMzguMTk5IDY0LjMzODkgMTM3LjcwNUM2My45NjY1IDEzNy4yMDcgNjMuNDQ1NSAxMzYuOTU4IDYyLjc3NTkgMTM2Ljk1OEM2Mi4xMjQyIDEzNi45NTggNjEuNjEwNCAxMzcuMjA3IDYxLjIzNDQgMTM3LjcwNUM2MC44NjIgMTM4LjE5OSA2MC42NzA0IDEzOC44ODcgNjAuNjU5NyAxMzkuNzY4VjE0MC4zNDJDNjAuNjU5NyAxNDEuMjQxIDYwLjg0NzcgMTQxLjk0OCA2MS4yMjM2IDE0Mi40NjRDNjEuNjAzMiAxNDIuOTc2IDYyLjEyNDIgMTQzLjIzMiA2Mi43ODY2IDE0My4yMzJDNjMuNDUyNiAxNDMuMjMyIDYzLjk2ODMgMTQyLjk5IDY0LjMzMzUgMTQyLjUwN0M2NC42OTg3IDE0Mi4wMiA2NC44ODY3IDE0MS4zMjMgNjQuODk3NSAxNDAuNDE3VjEzOS44MzdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik02OC4yNTQ0IDEzOC4xODhMNjguMjg2NiAxMzguOTE5QzY4LjczMDYgMTM4LjM2IDY5LjMxMDcgMTM4LjA4MSA3MC4wMjY5IDEzOC4wODFDNzEuMjU1IDEzOC4wODEgNzEuODc0NSAxMzguNzc0IDcxLjg4NTMgMTQwLjE2VjE0NEg3MC44OTE2VjE0MC4xNTRDNzAuODg4IDEzOS43MzUgNzAuNzkxMyAxMzkuNDI2IDcwLjYwMTYgMTM5LjIyNUM3MC40MTU0IDEzOS4wMjUgNzAuMTIzNSAxMzguOTI0IDY5LjcyNjEgMTM4LjkyNEM2OS40MDM4IDEzOC45MjQgNjkuMTIwOSAxMzkuMDEgNjguODc3NCAxMzkuMTgyQzY4LjYzNCAxMzkuMzU0IDY4LjQ0NDIgMTM5LjU4IDY4LjMwODEgMTM5Ljg1OVYxNDRINjcuMzE0NVYxMzguMTg4SDY4LjI1NDRaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik03NS43OTU0IDE0NC4xMDdDNzUuMDA3NiAxNDQuMTA3IDc0LjM2NjcgMTQzLjg1IDczLjg3MjYgMTQzLjMzNEM3My4zNzg0IDE0Mi44MTUgNzMuMTMxMyAxNDIuMTIyIDczLjEzMTMgMTQxLjI1NVYxNDEuMDczQzczLjEzMTMgMTQwLjQ5NiA3My4yNDA2IDEzOS45ODIgNzMuNDU5IDEzOS41MzFDNzMuNjgxIDEzOS4wNzYgNzMuOTg4OSAxMzguNzIyIDc0LjM4MjggMTM4LjQ2OEM3NC43ODAzIDEzOC4yMSA3NS4yMSAxMzguMDgxIDc1LjY3MTkgMTM4LjA4MUM3Ni40Mjc0IDEzOC4wODEgNzcuMDE0NiAxMzguMzMgNzcuNDMzNiAxMzguODI4Qzc3Ljg1MjUgMTM5LjMyNSA3OC4wNjIgMTQwLjAzOCA3OC4wNjIgMTQwLjk2NVYxNDEuMzc5SDc0LjEyNUM3NC4xMzkzIDE0MS45NTIgNzQuMzA1OCAxNDIuNDE2IDc0LjYyNDUgMTQyLjc3Qzc0Ljk0NjggMTQzLjEyMSA3NS4zNTUgMTQzLjI5NiA3NS44NDkxIDE0My4yOTZDNzYuMiAxNDMuMjk2IDc2LjQ5NzIgMTQzLjIyNSA3Ni43NDA3IDE0My4wODJDNzYuOTg0MiAxNDIuOTM4IDc3LjE5NzMgMTQyLjc0OSA3Ny4zNzk5IDE0Mi41MTJMNzcuOTg2OCAxNDIuOTg1Qzc3LjQ5OTggMTQzLjczMyA3Ni43Njk0IDE0NC4xMDcgNzUuNzk1NCAxNDQuMTA3Wk03NS42NzE5IDEzOC44OTdDNzUuMjcwOCAxMzguODk3IDc0LjkzNDIgMTM5LjA0NCA3NC42NjIxIDEzOS4zMzhDNzQuMzkgMTM5LjYyOCA3NC4yMjE3IDE0MC4wMzYgNzQuMTU3MiAxNDAuNTYySDc3LjA2ODRWMTQwLjQ4N0M3Ny4wMzk3IDEzOS45ODIgNzYuOTAzNiAxMzkuNTkyIDc2LjY2MDIgMTM5LjMxNkM3Ni40MTY3IDEzOS4wMzcgNzYuMDg3MiAxMzguODk3IDc1LjY3MTkgMTM4Ljg5N1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTgxLjI4NDcgMTQxLjA4M0g3OC42NjM2VjE0MC4yNzJIODEuMjg0N1YxNDEuMDgzWiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNODcuNDAyMyAxNDIuNjNMODguNTE5NSAxMzguMTg4SDg5LjUxMzJMODcuODIxMyAxNDRIODcuMDE1Nkw4NS42MDMgMTM5LjU5Nkw4NC4yMjggMTQ0SDgzLjQyMjRMODEuNzM1OCAxMzguMTg4SDgyLjcyNDFMODMuODY4MiAxNDIuNTM5TDg1LjIyMTcgMTM4LjE4OEg4Ni4wMjJMODcuNDAyMyAxNDIuNjNaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik05NC4xMTYyIDE0NEM5NC4wNTg5IDE0My44ODUgOTQuMDEyNCAxNDMuNjgxIDkzLjk3NjYgMTQzLjM4OEM5My41MTQ2IDE0My44NjggOTIuOTYzMiAxNDQuMTA3IDkyLjMyMjMgMTQ0LjEwN0M5MS43NDkzIDE0NC4xMDcgOTEuMjc4NSAxNDMuOTQ2IDkwLjkwOTcgMTQzLjYyNEM5MC41NDQ0IDE0My4yOTggOTAuMzYxOCAxNDIuODg2IDkwLjM2MTggMTQyLjM4OUM5MC4zNjE4IDE0MS43ODQgOTAuNTkxIDE0MS4zMTQgOTEuMDQ5MyAxNDAuOTgxQzkxLjUxMTIgMTQwLjY0NSA5Mi4xNTkzIDE0MC40NzcgOTIuOTkzNyAxNDAuNDc3SDkzLjk2MDRWMTQwLjAyQzkzLjk2MDQgMTM5LjY3MyA5My44NTY2IDEzOS4zOTcgOTMuNjQ4OSAxMzkuMTkzQzkzLjQ0MTIgMTM4Ljk4NSA5My4xMzUxIDEzOC44ODEgOTIuNzMwNSAxMzguODgxQzkyLjM3NiAxMzguODgxIDkyLjA3ODggMTM4Ljk3MSA5MS44Mzg5IDEzOS4xNUM5MS41OTkgMTM5LjMyOSA5MS40NzkgMTM5LjU0NiA5MS40NzkgMTM5LjhIOTAuNDhDOTAuNDggMTM5LjUxIDkwLjU4MiAxMzkuMjMgOTAuNzg2MSAxMzguOTYyQzkwLjk5MzggMTM4LjY5IDkxLjI3MzEgMTM4LjQ3NSA5MS42MjQgMTM4LjMxN0M5MS45Nzg1IDEzOC4xNiA5Mi4zNjcgMTM4LjA4MSA5Mi43ODk2IDEzOC4wODFDOTMuNDU5MSAxMzguMDgxIDkzLjk4MzcgMTM4LjI0OSA5NC4zNjMzIDEzOC41ODZDOTQuNzQyOCAxMzguOTE5IDk0LjkzOTggMTM5LjM3OSA5NC45NTQxIDEzOS45NjZWMTQyLjY0MUM5NC45NTQxIDE0My4xNzUgOTUuMDIyMSAxNDMuNTk5IDk1LjE1ODIgMTQzLjkxNFYxNDRIOTQuMTE2MlpNOTIuNDY3MyAxNDMuMjQzQzkyLjc3ODggMTQzLjI0MyA5My4wNzQyIDE0My4xNjIgOTMuMzUzNSAxNDMuMDAxQzkzLjYzMjggMTQyLjg0IDkzLjgzNTEgMTQyLjYzIDkzLjk2MDQgMTQyLjM3M1YxNDEuMThIOTMuMTgxNkM5MS45NjQyIDE0MS4xOCA5MS4zNTU1IDE0MS41MzYgOTEuMzU1NSAxNDIuMjQ5QzkxLjM1NTUgMTQyLjU2MSA5MS40NTkzIDE0Mi44MDQgOTEuNjY3IDE0Mi45NzlDOTEuODc0NyAxNDMuMTU1IDkyLjE0MTQgMTQzLjI0MyA5Mi40NjczIDE0My4yNDNaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik05OC4zMjcxIDE0Mi41NDRMOTkuNjgwNyAxMzguMTg4SDEwMC43NDRMOTguNDA3NyAxNDQuODk3Qzk4LjA0NjEgMTQ1Ljg2NCA5Ny40NzE0IDE0Ni4zNDcgOTYuNjgzNiAxNDYuMzQ3TDk2LjQ5NTYgMTQ2LjMzMUw5Ni4xMjUgMTQ2LjI2MVYxNDUuNDU2TDk2LjM5MzYgMTQ1LjQ3N0M5Ni43MzAxIDE0NS40NzcgOTYuOTkxNSAxNDUuNDA5IDk3LjE3NzcgMTQ1LjI3M0M5Ny4zNjc1IDE0NS4xMzcgOTcuNTIzMyAxNDQuODg4IDk3LjY0NSAxNDQuNTI2TDk3Ljg2NTIgMTQzLjkzNkw5NS43OTIgMTM4LjE4OEg5Ni44NzdMOTguMzI3MSAxNDIuNTQ0WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTIuNzczNCAxMDMuNDc5QzEyLjc3MzQgMTAzLjMwNyAxMi44MjM2IDEwMy4xNjQgMTIuOTIzOCAxMDMuMDQ5QzEzLjAyNzcgMTAyLjkzNSAxMy4xODE2IDEwMi44NzcgMTMuMzg1NyAxMDIuODc3QzEzLjU4OTggMTAyLjg3NyAxMy43NDM4IDEwMi45MzUgMTMuODQ3NyAxMDMuMDQ5QzEzLjk1NTEgMTAzLjE2NCAxNC4wMDg4IDEwMy4zMDcgMTQuMDA4OCAxMDMuNDc5QzE0LjAwODggMTAzLjY0NCAxMy45NTUxIDEwMy43ODIgMTMuODQ3NyAxMDMuODkzQzEzLjc0MzggMTA0LjAwNCAxMy41ODk4IDEwNC4wNTkgMTMuMzg1NyAxMDQuMDU5QzEzLjE4MTYgMTA0LjA1OSAxMy4wMjc3IDEwNC4wMDQgMTIuOTIzOCAxMDMuODkzQzEyLjgyMzYgMTAzLjc4MiAxMi43NzM0IDEwMy42NDQgMTIuNzczNCAxMDMuNDc5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTUuNjczOCAxMDMuNDc5QzE1LjY3MzggMTAzLjMwNyAxNS43MjQgMTAzLjE2NCAxNS44MjQyIDEwMy4wNDlDMTUuOTI4MSAxMDIuOTM1IDE2LjA4MiAxMDIuODc3IDE2LjI4NjEgMTAyLjg3N0MxNi40OTAyIDEwMi44NzcgMTYuNjQ0MiAxMDIuOTM1IDE2Ljc0OCAxMDMuMDQ5QzE2Ljg1NTUgMTAzLjE2NCAxNi45MDkyIDEwMy4zMDcgMTYuOTA5MiAxMDMuNDc5QzE2LjkwOTIgMTAzLjY0NCAxNi44NTU1IDEwMy43ODIgMTYuNzQ4IDEwMy44OTNDMTYuNjQ0MiAxMDQuMDA0IDE2LjQ5MDIgMTA0LjA1OSAxNi4yODYxIDEwNC4wNTlDMTYuMDgyIDEwNC4wNTkgMTUuOTI4MSAxMDQuMDA0IDE1LjgyNDIgMTAzLjg5M0MxNS43MjQgMTAzLjc4MiAxNS42NzM4IDEwMy42NDQgMTUuNjczOCAxMDMuNDc5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTguMjg5NiAxMDEuMDQxQzE4LjI4OTYgMTAwLjQ3MSAxOC40MDA2IDk5Ljk1OTEgMTguNjIyNiA5OS41MDQ0QzE4Ljg0ODEgOTkuMDQ5NiAxOS4xNTk3IDk4LjY5ODcgMTkuNTU3MSA5OC40NTE3QzE5Ljk1ODIgOTguMjA0NiAyMC40MTQ3IDk4LjA4MTEgMjAuOTI2OCA5OC4wODExQzIxLjcxODEgOTguMDgxMSAyMi4zNTczIDk4LjM1NSAyMi44NDQyIDk4LjkwMjhDMjMuMzM0OCA5OS40NTA3IDIzLjU4MDEgMTAwLjE3OSAyMy41ODAxIDEwMS4wODlWMTAxLjE1OUMyMy41ODAxIDEwMS43MjQgMjMuNDcwOSAxMDIuMjMzIDIzLjI1MjQgMTAyLjY4NEMyMy4wMzc2IDEwMy4xMzIgMjIuNzI3OSAxMDMuNDgxIDIyLjMyMzIgMTAzLjczMUMyMS45MjIyIDEwMy45ODIgMjEuNDYwMyAxMDQuMTA3IDIwLjkzNzUgMTA0LjEwN0MyMC4xNDk3IDEwNC4xMDcgMTkuNTEwNiAxMDMuODMzIDE5LjAyIDEwMy4yODZDMTguNTMzIDEwMi43MzggMTguMjg5NiAxMDIuMDEzIDE4LjI4OTYgMTAxLjExVjEwMS4wNDFaTTE5LjI4ODYgMTAxLjE1OUMxOS4yODg2IDEwMS44MDMgMTkuNDM3MiAxMDIuMzIxIDE5LjczNDQgMTAyLjcxMUMyMC4wMzUyIDEwMy4xMDEgMjAuNDM2MiAxMDMuMjk2IDIwLjkzNzUgMTAzLjI5NkMyMS40NDI0IDEwMy4yOTYgMjEuODQzNCAxMDMuMDk5IDIyLjE0MDYgMTAyLjcwNkMyMi40Mzc4IDEwMi4zMDggMjIuNTg2NCAxMDEuNzUzIDIyLjU4NjQgMTAxLjA0MUMyMi41ODY0IDEwMC40MDMgMjIuNDM0MiA5OS44ODc1IDIyLjEyOTkgOTkuNDkzN0MyMS44MjkxIDk5LjA5NjIgMjEuNDI4MSA5OC44OTc1IDIwLjkyNjggOTguODk3NUMyMC40MzYyIDk4Ljg5NzUgMjAuMDQwNSA5OS4wOTI2IDE5LjczOTcgOTkuNDgyOUMxOS40MzkgOTkuODczMiAxOS4yODg2IDEwMC40MzIgMTkuMjg4NiAxMDEuMTU5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMjYuMTY4OSA5OS42MDY0SDI2LjkxNTVDMjcuMzg0NiA5OS41OTkzIDI3Ljc1MzQgOTkuNDc1NyAyOC4wMjIgOTkuMjM1OEMyOC4yOTA1IDk4Ljk5NTkgMjguNDI0OCA5OC42NzE5IDI4LjQyNDggOTguMjYzN0MyOC40MjQ4IDk3LjM0NyAyNy45NjgzIDk2Ljg4ODcgMjcuMDU1MiA5Ni44ODg3QzI2LjYyNTUgOTYuODg4NyAyNi4yODE3IDk3LjAxMjIgMjYuMDIzOSA5Ny4yNTkzQzI1Ljc2OTcgOTcuNTAyOCAyNS42NDI2IDk3LjgyNjggMjUuNjQyNiA5OC4yMzE0SDI0LjY0ODlDMjQuNjQ4OSA5Ny42MTIgMjQuODc0NSA5Ny4wOTgxIDI1LjMyNTcgOTYuNjg5OUMyNS43ODA0IDk2LjI3ODIgMjYuMzU2OSA5Ni4wNzIzIDI3LjA1NTIgOTYuMDcyM0MyNy43OTI4IDk2LjA3MjMgMjguMzcxMSA5Ni4yNjc0IDI4Ljc5IDk2LjY1NzdDMjkuMjA5IDk3LjA0OCAyOS40MTg1IDk3LjU5MDUgMjkuNDE4NSA5OC4yODUyQzI5LjQxODUgOTguNjI1MyAyOS4zMDc1IDk4Ljk1NDggMjkuMDg1NCA5OS4yNzM0QzI4Ljg2NyA5OS41OTIxIDI4LjU2OCA5OS44MzAyIDI4LjE4ODUgOTkuOTg3OEMyOC42MTgyIDEwMC4xMjQgMjguOTQ5NCAxMDAuMzQ5IDI5LjE4MjEgMTAwLjY2NUMyOS40MTg1IDEwMC45OCAyOS41MzY2IDEwMS4zNjUgMjkuNTM2NiAxMDEuODE5QzI5LjUzNjYgMTAyLjUyMSAyOS4zMDc1IDEwMy4wNzggMjguODQ5MSAxMDMuNDlDMjguMzkwOCAxMDMuOTAyIDI3Ljc5NDYgMTA0LjEwNyAyNy4wNjA1IDEwNC4xMDdDMjYuMzI2NSAxMDQuMTA3IDI1LjcyODUgMTAzLjkwOSAyNS4yNjY2IDEwMy41MTFDMjQuODA4MyAxMDMuMTE0IDI0LjU3OTEgMTAyLjU4OSAyNC41NzkxIDEwMS45MzhIMjUuNTc4MUMyNS41NzgxIDEwMi4zNDkgMjUuNzEyNCAxMDIuNjc5IDI1Ljk4MSAxMDIuOTI2QzI2LjI0OTUgMTAzLjE3MyAyNi42MDk0IDEwMy4yOTYgMjcuMDYwNSAxMDMuMjk2QzI3LjU0MDQgMTAzLjI5NiAyNy45MDc0IDEwMy4xNzEgMjguMTYxNiAxMDIuOTJDMjguNDE1OSAxMDIuNjcgMjguNTQzIDEwMi4zMSAyOC41NDMgMTAxLjg0MUMyOC41NDMgMTAxLjM4NiAyOC40MDMzIDEwMS4wMzcgMjguMTI0IDEwMC43OTNDMjcuODQ0NyAxMDAuNTUgMjcuNDQxOSAxMDAuNDI1IDI2LjkxNTUgMTAwLjQxN0gyNi4xNjg5Vjk5LjYwNjRaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0zMC43NjEyIDEwMS4wNDZDMzAuNzYxMiAxMDAuMTU0IDMwLjk3MjUgOTkuNDM4MiAzMS4zOTUgOTguODk3NUMzMS44MTc1IDk4LjM1MzIgMzIuMzcwOCA5OC4wODExIDMzLjA1NDcgOTguMDgxMUMzMy43MzUgOTguMDgxMSAzNC4yNzM5IDk4LjMxMzggMzQuNjcxNCA5OC43NzkzVjk1Ljc1SDM1LjY2NVYxMDRIMzQuNzUyTDM0LjcwMzYgMTAzLjM3N0MzNC4zMDYyIDEwMy44NjQgMzMuNzUyOSAxMDQuMTA3IDMzLjA0MzkgMTA0LjEwN0MzMi4zNzA4IDEwNC4xMDcgMzEuODIxMSAxMDMuODMyIDMxLjM5NSAxMDMuMjhDMzAuOTcyNSAxMDIuNzI5IDMwLjc2MTIgMTAyLjAwOSAzMC43NjEyIDEwMS4xMjFWMTAxLjA0NlpNMzEuNzU0OSAxMDEuMTU5QzMxLjc1NDkgMTAxLjgxOCAzMS44OTEgMTAyLjMzMyAzMi4xNjMxIDEwMi43MDZDMzIuNDM1MiAxMDMuMDc4IDMyLjgxMTIgMTAzLjI2NCAzMy4yOTEgMTAzLjI2NEMzMy45MjEyIDEwMy4yNjQgMzQuMzgxMyAxMDIuOTgxIDM0LjY3MTQgMTAyLjQxNlY5OS43NDYxQzM0LjM3NDIgOTkuMTk4MiAzMy45MTc2IDk4LjkyNDMgMzMuMzAxOCA5OC45MjQzQzMyLjgxNDggOTguOTI0MyAzMi40MzUyIDk5LjExMjMgMzIuMTYzMSA5OS40ODgzQzMxLjg5MSA5OS44NjQzIDMxLjc1NDkgMTAwLjQyMSAzMS43NTQ5IDEwMS4xNTlaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik00MC4wMjEgOTkuMDgwMUMzOS44NzA2IDk5LjA1NSAzOS43MDc3IDk5LjA0MjUgMzkuNTMyMiA5OS4wNDI1QzM4Ljg4MDUgOTkuMDQyNSAzOC40MzgzIDk5LjMyIDM4LjIwNTYgOTkuODc1VjEwNEgzNy4yMTE5Vjk4LjE4ODVIMzguMTc4N0wzOC4xOTQ4IDk4Ljg1OTlDMzguNTIwNyA5OC4zNDA3IDM4Ljk4MjYgOTguMDgxMSAzOS41ODA2IDk4LjA4MTFDMzkuNzczOSA5OC4wODExIDM5LjkyMDcgOTguMTA2MSA0MC4wMjEgOTguMTU2MlY5OS4wODAxWiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNNDEuODc5NCA5OC4xODg1TDQxLjkxMTYgOTguOTE4OUM0Mi4zNTU2IDk4LjM2MDQgNDIuOTM1NyA5OC4wODExIDQzLjY1MTkgOTguMDgxMUM0NC44OCA5OC4wODExIDQ1LjQ5OTUgOTguNzczOSA0NS41MTAzIDEwMC4xNlYxMDRINDQuNTE2NlYxMDAuMTU0QzQ0LjUxMyA5OS43MzU0IDQ0LjQxNjMgOTkuNDI1NiA0NC4yMjY2IDk5LjIyNTFDNDQuMDQwNCA5OS4wMjQ2IDQzLjc0ODUgOTguOTI0MyA0My4zNTExIDk4LjkyNDNDNDMuMDI4OCA5OC45MjQzIDQyLjc0NTkgOTkuMDEwMyA0Mi41MDI0IDk5LjE4MjFDNDIuMjU5IDk5LjM1NCA0Mi4wNjkyIDk5LjU3OTYgNDEuOTMzMSA5OS44NTg5VjEwNEg0MC45Mzk1Vjk4LjE4ODVINDEuODc5NFoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTEyLjc3MzQgMTQzLjQ3OUMxMi43NzM0IDE0My4zMDcgMTIuODIzNiAxNDMuMTY0IDEyLjkyMzggMTQzLjA0OUMxMy4wMjc3IDE0Mi45MzUgMTMuMTgxNiAxNDIuODc3IDEzLjM4NTcgMTQyLjg3N0MxMy41ODk4IDE0Mi44NzcgMTMuNzQzOCAxNDIuOTM1IDEzLjg0NzcgMTQzLjA0OUMxMy45NTUxIDE0My4xNjQgMTQuMDA4OCAxNDMuMzA3IDE0LjAwODggMTQzLjQ3OUMxNC4wMDg4IDE0My42NDQgMTMuOTU1MSAxNDMuNzgyIDEzLjg0NzcgMTQzLjg5M0MxMy43NDM4IDE0NC4wMDQgMTMuNTg5OCAxNDQuMDU5IDEzLjM4NTcgMTQ0LjA1OUMxMy4xODE2IDE0NC4wNTkgMTMuMDI3NyAxNDQuMDA0IDEyLjkyMzggMTQzLjg5M0MxMi44MjM2IDE0My43ODIgMTIuNzczNCAxNDMuNjQ0IDEyLjc3MzQgMTQzLjQ3OVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTE1LjY3MzggMTQzLjQ3OUMxNS42NzM4IDE0My4zMDcgMTUuNzI0IDE0My4xNjQgMTUuODI0MiAxNDMuMDQ5QzE1LjkyODEgMTQyLjkzNSAxNi4wODIgMTQyLjg3NyAxNi4yODYxIDE0Mi44NzdDMTYuNDkwMiAxNDIuODc3IDE2LjY0NDIgMTQyLjkzNSAxNi43NDggMTQzLjA0OUMxNi44NTU1IDE0My4xNjQgMTYuOTA5MiAxNDMuMzA3IDE2LjkwOTIgMTQzLjQ3OUMxNi45MDkyIDE0My42NDQgMTYuODU1NSAxNDMuNzgyIDE2Ljc0OCAxNDMuODkzQzE2LjY0NDIgMTQ0LjAwNCAxNi40OTAyIDE0NC4wNTkgMTYuMjg2MSAxNDQuMDU5QzE2LjA4MiAxNDQuMDU5IDE1LjkyODEgMTQ0LjAwNCAxNS44MjQyIDE0My44OTNDMTUuNzI0IDE0My43ODIgMTUuNjczOCAxNDMuNjQ0IDE1LjY3MzggMTQzLjQ3OVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTIxLjM2MTggMTM5LjA4QzIxLjIxMTQgMTM5LjA1NSAyMS4wNDg1IDEzOS4wNDIgMjAuODczIDEzOS4wNDJDMjAuMjIxNCAxMzkuMDQyIDE5Ljc3OTEgMTM5LjMyIDE5LjU0NjQgMTM5Ljg3NVYxNDRIMTguNTUyN1YxMzguMTg4SDE5LjUxOTVMMTkuNTM1NiAxMzguODZDMTkuODYxNSAxMzguMzQxIDIwLjMyMzQgMTM4LjA4MSAyMC45MjE0IDEzOC4wODFDMjEuMTE0NyAxMzguMDgxIDIxLjI2MTYgMTM4LjEwNiAyMS4zNjE4IDEzOC4xNTZWMTM5LjA4WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMjQuMjk0NCAxNDIuNjUyTDI1LjczMzkgMTM4LjE4OEgyNi43NDlMMjQuNjY1IDE0NEgyMy45MDc3TDIxLjgwMjIgMTM4LjE4OEgyMi44MTc0TDI0LjI5NDQgMTQyLjY1MloiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTMxLjQxMTEgMTQwLjU2MkMzMS4yMDM1IDE0MC44MSAzMC45NTQ2IDE0MS4wMDggMzAuNjY0NiAxNDEuMTU5QzMwLjM3ODEgMTQxLjMwOSAzMC4wNjMgMTQxLjM4NCAyOS43MTkyIDE0MS4zODRDMjkuMjY4MSAxNDEuMzg0IDI4Ljg3NDIgMTQxLjI3MyAyOC41Mzc2IDE0MS4wNTFDMjguMjA0NiAxNDAuODI5IDI3Ljk0NjggMTQwLjUxOCAyNy43NjQyIDE0MC4xMTdDMjcuNTgxNSAxMzkuNzEyIDI3LjQ5MDIgMTM5LjI2NiAyNy40OTAyIDEzOC43NzlDMjcuNDkwMiAxMzguMjU3IDI3LjU4ODcgMTM3Ljc4NiAyNy43ODU2IDEzNy4zNjdDMjcuOTg2MiAxMzYuOTQ4IDI4LjI2OSAxMzYuNjI3IDI4LjYzNDMgMTM2LjQwNUMyOC45OTk1IDEzNi4xODMgMjkuNDI1NiAxMzYuMDcyIDI5LjkxMjYgMTM2LjA3MkMzMC42ODYgMTM2LjA3MiAzMS4yOTQ4IDEzNi4zNjIgMzEuNzM4OCAxMzYuOTQyQzMyLjE4NjQgMTM3LjUxOSAzMi40MTAyIDEzOC4zMDcgMzIuNDEwMiAxMzkuMzA2VjEzOS41OTZDMzIuNDEwMiAxNDEuMTE4IDMyLjEwOTQgMTQyLjIyOSAzMS41MDc4IDE0Mi45MzFDMzAuOTA2MiAxNDMuNjI5IDI5Ljk5ODUgMTQzLjk4NyAyOC43ODQ3IDE0NC4wMDVIMjguNTkxM1YxNDMuMTY3SDI4LjgwMDhDMjkuNjIwOCAxNDMuMTUzIDMwLjI1MSAxNDIuOTQgMzAuNjkxNCAxNDIuNTI4QzMxLjEzMTggMTQyLjExMyAzMS4zNzE3IDE0MS40NTggMzEuNDExMSAxNDAuNTYyWk0yOS44ODA0IDE0MC41NjJDMzAuMjEzNCAxNDAuNTYyIDMwLjUxOTUgMTQwLjQ2IDMwLjc5ODggMTQwLjI1NkMzMS4wODE3IDE0MC4wNTIgMzEuMjg3NiAxMzkuOCAzMS40MTY1IDEzOS40OTlWMTM5LjEwMkMzMS40MTY1IDEzOC40NSAzMS4yNzUxIDEzNy45MiAzMC45OTIyIDEzNy41MTJDMzAuNzA5MyAxMzcuMTA0IDMwLjM1MTIgMTM2Ljg5OSAyOS45MTggMTM2Ljg5OUMyOS40ODExIDEzNi44OTkgMjkuMTMwMiAxMzcuMDY4IDI4Ljg2NTIgMTM3LjQwNEMyOC42MDAzIDEzNy43MzcgMjguNDY3OCAxMzguMTc4IDI4LjQ2NzggMTM4LjcyNkMyOC40Njc4IDEzOS4yNTkgMjguNTk0OSAxMzkuNyAyOC44NDkxIDE0MC4wNDdDMjkuMTA2OSAxNDAuMzkxIDI5LjQ1MDcgMTQwLjU2MiAyOS44ODA0IDE0MC41NjJaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0zNS41MDM5IDE0MS4zMDlMMzQuODgwOSAxNDEuOTU5VjE0NEgzMy44ODcyVjEzNS43NUgzNC44ODA5VjE0MC43NEwzNS40MTI2IDE0MC4xMDFMMzcuMjIyNyAxMzguMTg4SDM4LjQzMTJMMzYuMTY5OSAxNDAuNjE2TDM4LjY5NDMgMTQ0SDM3LjUyODhMMzUuNTAzOSAxNDEuMzA5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNNDEuNzYxMiAxNDQuMTA3QzQwLjk3MzUgMTQ0LjEwNyA0MC4zMzI1IDE0My44NSAzOS44Mzg0IDE0My4zMzRDMzkuMzQ0MiAxNDIuODE1IDM5LjA5NzIgMTQyLjEyMiAzOS4wOTcyIDE0MS4yNTVWMTQxLjA3M0MzOS4wOTcyIDE0MC40OTYgMzkuMjA2NCAxMzkuOTgyIDM5LjQyNDggMTM5LjUzMUMzOS42NDY4IDEzOS4wNzYgMzkuOTU0OCAxMzguNzIyIDQwLjM0ODYgMTM4LjQ2OEM0MC43NDYxIDEzOC4yMSA0MS4xNzU4IDEzOC4wODEgNDEuNjM3NyAxMzguMDgxQzQyLjM5MzIgMTM4LjA4MSA0Mi45ODA1IDEzOC4zMyA0My4zOTk0IDEzOC44MjhDNDMuODE4NCAxMzkuMzI1IDQ0LjAyNzggMTQwLjAzOCA0NC4wMjc4IDE0MC45NjVWMTQxLjM3OUg0MC4wOTA4QzQwLjEwNTEgMTQxLjk1MiA0MC4yNzE2IDE0Mi40MTYgNDAuNTkwMyAxNDIuNzdDNDAuOTEyNiAxNDMuMTIxIDQxLjMyMDggMTQzLjI5NiA0MS44MTQ5IDE0My4yOTZDNDIuMTY1OSAxNDMuMjk2IDQyLjQ2MzEgMTQzLjIyNSA0Mi43MDY1IDE0My4wODJDNDIuOTUgMTQyLjkzOCA0My4xNjMxIDE0Mi43NDkgNDMuMzQ1NyAxNDIuNTEyTDQzLjk1MjYgMTQyLjk4NUM0My40NjU3IDE0My43MzMgNDIuNzM1MiAxNDQuMTA3IDQxLjc2MTIgMTQ0LjEwN1pNNDEuNjM3NyAxMzguODk3QzQxLjIzNjcgMTM4Ljg5NyA0MC45MDAxIDEzOS4wNDQgNDAuNjI3OSAxMzkuMzM4QzQwLjM1NTggMTM5LjYyOCA0MC4xODc1IDE0MC4wMzYgNDAuMTIzIDE0MC41NjJINDMuMDM0MlYxNDAuNDg3QzQzLjAwNTUgMTM5Ljk4MiA0Mi44Njk1IDEzOS41OTIgNDIuNjI2IDEzOS4zMTZDNDIuMzgyNSAxMzkuMDM3IDQyLjA1MzEgMTM4Ljg5NyA0MS42Mzc3IDEzOC44OTdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0xNDYuNDQ1IDU3LjI3NTRIMTQ0LjAwN1Y2NEgxNDIuNjU5VjU3LjI3NTRIMTQwLjI0MlY1Ni4xNzk3SDE0Ni40NDVWNTcuMjc1NFoiIGZpbGw9IiNGRkE1MDAiLz4KPHBhdGggZD0iTTE0OC43MzkgNjRIMTQ3LjQzNFY1OC4xODg1SDE0OC43MzlWNjRaTTE0Ny4zNTMgNTYuNjc5MkMxNDcuMzUzIDU2LjQ3ODcgMTQ3LjQxNiA1Ni4zMTIyIDE0Ny41NDEgNTYuMTc5N0MxNDcuNjcgNTYuMDQ3MiAxNDcuODUzIDU1Ljk4MSAxNDguMDg5IDU1Ljk4MUMxNDguMzI1IDU1Ljk4MSAxNDguNTA4IDU2LjA0NzIgMTQ4LjYzNyA1Ni4xNzk3QzE0OC43NjYgNTYuMzEyMiAxNDguODMgNTYuNDc4NyAxNDguODMgNTYuNjc5MkMxNDguODMgNTYuODc2MSAxNDguNzY2IDU3LjA0MDkgMTQ4LjYzNyA1Ny4xNzMzQzE0OC41MDggNTcuMzAyMiAxNDguMzI1IDU3LjM2NjcgMTQ4LjA4OSA1Ny4zNjY3QzE0Ny44NTMgNTcuMzY2NyAxNDcuNjcgNTcuMzAyMiAxNDcuNTQxIDU3LjE3MzNDMTQ3LjQxNiA1Ny4wNDA5IDE0Ny4zNTMgNTYuODc2MSAxNDcuMzUzIDU2LjY3OTJaIiBmaWxsPSIjRkZBNTAwIi8+CjxwYXRoIGQ9Ik0xNTEuMzkyIDU4LjE4ODVMMTUxLjQzIDU4Ljc5NTRDMTUxLjgzOCA1OC4zMTkyIDE1Mi4zOTYgNTguMDgxMSAxNTMuMTA1IDU4LjA4MTFDMTUzLjg4MiA1OC4wODExIDE1NC40MTQgNTguMzc4MyAxNTQuNzAxIDU4Ljk3MjdDMTU1LjEyMyA1OC4zNzgzIDE1NS43MTggNTguMDgxMSAxNTYuNDg0IDU4LjA4MTFDMTU3LjEyNSA1OC4wODExIDE1Ny42MDEgNTguMjU4MyAxNTcuOTEzIDU4LjYxMjhDMTU4LjIyOCA1OC45NjczIDE1OC4zODkgNTkuNDkwMSAxNTguMzk2IDYwLjE4MTJWNjRIMTU3LjA5MVY2MC4yMTg4QzE1Ny4wOTEgNTkuODQ5OSAxNTcuMDEgNTkuNTc5NiAxNTYuODQ5IDU5LjQwNzdDMTU2LjY4OCA1OS4yMzU4IDE1Ni40MjEgNTkuMTQ5OSAxNTYuMDQ5IDU5LjE0OTlDMTU1Ljc1MiA1OS4xNDk5IDE1NS41MDggNTkuMjMwNSAxNTUuMzE4IDU5LjM5MTZDMTU1LjEzMiA1OS41NDkyIDE1NS4wMDEgNTkuNzU2OCAxNTQuOTI2IDYwLjAxNDZMMTU0LjkzMiA2NEgxNTMuNjI2VjYwLjE3NThDMTUzLjYwOSA1OS40OTE5IDE1My4yNTkgNTkuMTQ5OSAxNTIuNTc5IDU5LjE0OTlDMTUyLjA1NiA1OS4xNDk5IDE1MS42ODYgNTkuMzYzIDE1MS40NjcgNTkuNzg5MVY2NEgxNTAuMTYyVjU4LjE4ODVIMTUxLjM5MloiIGZpbGw9IiNGRkE1MDAiLz4KPHBhdGggZD0iTTE2Mi4yOTUgNjQuMTA3NEMxNjEuNDY4IDY0LjEwNzQgMTYwLjc5NyA2My44NDc4IDE2MC4yODEgNjMuMzI4NkMxNTkuNzY5IDYyLjgwNTggMTU5LjUxMyA2Mi4xMTEyIDE1OS41MTMgNjEuMjQ0NlY2MS4wODM1QzE1OS41MTMgNjAuNTAzNCAxNTkuNjI0IDU5Ljk4NiAxNTkuODQ2IDU5LjUzMTJDMTYwLjA3MiA1OS4wNzI5IDE2MC4zODcgNTguNzE2NiAxNjAuNzkyIDU4LjQ2MjRDMTYxLjE5NiA1OC4yMDgyIDE2MS42NDcgNTguMDgxMSAxNjIuMTQ1IDU4LjA4MTFDMTYyLjkzNiA1OC4wODExIDE2My41NDcgNTguMzMzNSAxNjMuOTc3IDU4LjgzODRDMTY0LjQxIDU5LjM0MzMgMTY0LjYyNiA2MC4wNTc2IDE2NC42MjYgNjAuOTgxNFY2MS41MDc4SDE2MC44MjlDMTYwLjg2OCA2MS45ODc2IDE2MS4wMjggNjIuMzY3MiAxNjEuMzA3IDYyLjY0NjVDMTYxLjU5IDYyLjkyNTggMTYxLjk0NCA2My4wNjU0IDE2Mi4zNzEgNjMuMDY1NEMxNjIuOTY5IDYzLjA2NTQgMTYzLjQ1NiA2Mi44MjM3IDE2My44MzIgNjIuMzQwM0wxNjQuNTM1IDYzLjAxMTdDMTY0LjMwMiA2My4zNTkgMTYzLjk5MSA2My42Mjk0IDE2My42MDEgNjMuODIyOEMxNjMuMjE0IDY0LjAxMjUgMTYyLjc3OSA2NC4xMDc0IDE2Mi4yOTUgNjQuMTA3NFpNMTYyLjE0IDU5LjEyODRDMTYxLjc4MiA1OS4xMjg0IDE2MS40OTIgNTkuMjUzNyAxNjEuMjcgNTkuNTA0NEMxNjEuMDUxIDU5Ljc1NSAxNjAuOTExIDYwLjEwNDIgMTYwLjg1MSA2MC41NTE4SDE2My4zMzdWNjAuNDU1MUMxNjMuMzA5IDYwLjAxODIgMTYzLjE5MiA1OS42ODg4IDE2Mi45ODggNTkuNDY2OEMxNjIuNzg0IDU5LjI0MTIgMTYyLjUwMSA1OS4xMjg0IDE2Mi4xNCA1OS4xMjg0WiIgZmlsbD0iI0ZGQTUwMCIvPgo8cGF0aCBkPSJNMTY1LjQgNjEuMDQwNUMxNjUuNCA2MC40NzEyIDE2NS41MTMgNTkuOTU5MSAxNjUuNzM4IDU5LjUwNDRDMTY1Ljk2NCA1OS4wNDYxIDE2Ni4yODEgNTguNjk1MSAxNjYuNjg5IDU4LjQ1MTdDMTY3LjA5NyA1OC4yMDQ2IDE2Ny41NjYgNTguMDgxMSAxNjguMDk2IDU4LjA4MTFDMTY4Ljg4IDU4LjA4MTEgMTY5LjUxNiA1OC4zMzM1IDE3MC4wMDMgNTguODM4NEMxNzAuNDkzIDU5LjM0MzMgMTcwLjc1OCA2MC4wMTI5IDE3MC43OTggNjAuODQ3MkwxNzAuODAzIDYxLjE1MzNDMTcwLjgwMyA2MS43MjYyIDE3MC42OTIgNjIuMjM4MyAxNzAuNDcgNjIuNjg5NUMxNzAuMjUyIDYzLjE0MDYgMTY5LjkzNyA2My40ODk3IDE2OS41MjUgNjMuNzM2OEMxNjkuMTE3IDYzLjk4MzkgMTY4LjY0NCA2NC4xMDc0IDE2OC4xMDcgNjQuMTA3NEMxNjcuMjg3IDY0LjEwNzQgMTY2LjYzIDYzLjgzNTMgMTY2LjEzNiA2My4yOTFDMTY1LjY0NSA2Mi43NDMyIDE2NS40IDYyLjAxNDUgMTY1LjQgNjEuMTA1VjYxLjA0MDVaTTE2Ni43MDUgNjEuMTUzM0MxNjYuNzA1IDYxLjc1MTMgMTY2LjgyOSA2Mi4yMjA0IDE2Ny4wNzYgNjIuNTYwNUMxNjcuMzIzIDYyLjg5NzEgMTY3LjY2NyA2My4wNjU0IDE2OC4xMDcgNjMuMDY1NEMxNjguNTQ3IDYzLjA2NTQgMTY4Ljg4OSA2Mi44OTM2IDE2OS4xMzMgNjIuNTQ5OEMxNjkuMzggNjIuMjA2MSAxNjkuNTAzIDYxLjcwMyAxNjkuNTAzIDYxLjA0MDVDMTY5LjUwMyA2MC40NTMzIDE2OS4zNzYgNTkuOTg3OCAxNjkuMTIyIDU5LjY0NEMxNjguODcxIDU5LjMwMDMgMTY4LjUyOSA1OS4xMjg0IDE2OC4wOTYgNTkuMTI4NEMxNjcuNjcgNTkuMTI4NCAxNjcuMzMyIDU5LjI5ODUgMTY3LjA4MSA1OS42Mzg3QzE2Ni44MyA1OS45NzUzIDE2Ni43MDUgNjAuNDgwMSAxNjYuNzA1IDYxLjE1MzNaIiBmaWxsPSIjRkZBNTAwIi8+CjxwYXRoIGQ9Ik0xNzUuNDI4IDYzLjQzMDdDMTc1LjA0NSA2My44ODE4IDE3NC41IDY0LjEwNzQgMTczLjc5NSA2NC4xMDc0QzE3My4xNjUgNjQuMTA3NCAxNzIuNjg3IDYzLjkyMyAxNzIuMzYxIDYzLjU1NDJDMTcyLjAzOSA2My4xODU0IDE3MS44NzcgNjIuNjUxOSAxNzEuODc3IDYxLjk1MzZWNTguMTg4NUgxNzMuMTgzVjYxLjkzNzVDMTczLjE4MyA2Mi42NzUxIDE3My40ODkgNjMuMDQzOSAxNzQuMTAxIDYzLjA0MzlDMTc0LjczNSA2My4wNDM5IDE3NS4xNjMgNjIuODE2NiAxNzUuMzg1IDYyLjM2MThWNTguMTg4NUgxNzYuNjlWNjRIMTc1LjQ2TDE3NS40MjggNjMuNDMwN1oiIGZpbGw9IiNGRkE1MDAiLz4KPHBhdGggZD0iTTE3OS42NTUgNTYuNzc1OVY1OC4xODg1SDE4MC42ODFWNTkuMTU1M0gxNzkuNjU1VjYyLjM5OTRDMTc5LjY1NSA2Mi42MjE0IDE3OS42OTggNjIuNzgyNiAxNzkuNzg0IDYyLjg4MjhDMTc5Ljg3MyA2Mi45Nzk1IDE4MC4wMzEgNjMuMDI3OCAxODAuMjU2IDYzLjAyNzhDMTgwLjQwNyA2My4wMjc4IDE4MC41NTkgNjMuMDA5OSAxODAuNzEzIDYyLjk3NDFWNjMuOTgzOUMxODAuNDE2IDY0LjA2NjIgMTgwLjEyOSA2NC4xMDc0IDE3OS44NTQgNjQuMTA3NEMxNzguODUxIDY0LjEwNzQgMTc4LjM1IDYzLjU1NDIgMTc4LjM1IDYyLjQ0NzhWNTkuMTU1M0gxNzcuMzk0VjU4LjE4ODVIMTc4LjM1VjU2Ljc3NTlIMTc5LjY1NVoiIGZpbGw9IiNGRkE1MDAiLz4KPHBhdGggZD0iTTE0NS4zMDEgMTAwLjY4NkgxNDIuMTU0VjEwNEgxNDAuNzk1Vjk2LjE3OTdIMTQ1Ljc2M1Y5Ny4yNzU0SDE0Mi4xNTRWOTkuNjAxMUgxNDUuMzAxVjEwMC42ODZaIiBmaWxsPSIjRkYzRTNFIi8+CjxwYXRoIGQ9Ik0xNTAuMDA2IDEwNEMxNDkuOTQ5IDEwMy44ODkgMTQ5Ljg5OSAxMDMuNzA4IDE0OS44NTYgMTAzLjQ1OEMxNDkuNDQxIDEwMy44OTEgMTQ4LjkzMiAxMDQuMTA3IDE0OC4zMzEgMTA0LjEwN0MxNDcuNzQ3IDEwNC4xMDcgMTQ3LjI3MSAxMDMuOTQxIDE0Ni45MDIgMTAzLjYwOEMxNDYuNTMzIDEwMy4yNzUgMTQ2LjM0OSAxMDIuODYzIDE0Ni4zNDkgMTAyLjM3M0MxNDYuMzQ5IDEwMS43NTMgMTQ2LjU3OCAxMDEuMjc5IDE0Ny4wMzYgMTAwLjk0OUMxNDcuNDk4IDEwMC42MTYgMTQ4LjE1NyAxMDAuNDUgMTQ5LjAxMyAxMDAuNDVIMTQ5LjgxM1YxMDAuMDY4QzE0OS44MTMgOTkuNzY3NiAxNDkuNzI5IDk5LjUyNzcgMTQ5LjU2MSA5OS4zNDg2QzE0OS4zOTIgOTkuMTY2IDE0OS4xMzYgOTkuMDc0NyAxNDguNzkyIDk5LjA3NDdDMTQ4LjQ5NSA5OS4wNzQ3IDE0OC4yNTIgOTkuMTQ5OSAxNDguMDYyIDk5LjMwMDNDMTQ3Ljg3MiA5OS40NDcxIDE0Ny43NzcgOTkuNjM1MSAxNDcuNzc3IDk5Ljg2NDNIMTQ2LjQ3MkMxNDYuNDcyIDk5LjU0NTYgMTQ2LjU3OCA5OS4yNDg0IDE0Ni43ODkgOTguOTcyN0MxNDcgOTguNjkzNCAxNDcuMjg3IDk4LjQ3NDkgMTQ3LjY0OCA5OC4zMTc0QzE0OC4wMTQgOTguMTU5OCAxNDguNDIgOTguMDgxMSAxNDguODY4IDk4LjA4MTFDMTQ5LjU0OCA5OC4wODExIDE1MC4wOSA5OC4yNTI5IDE1MC40OTUgOTguNTk2N0MxNTAuOSA5OC45MzY4IDE1MS4xMDcgOTkuNDE2NyAxNTEuMTE4IDEwMC4wMzZWMTAyLjY1N0MxNTEuMTE4IDEwMy4xOCAxNTEuMTkyIDEwMy41OTcgMTUxLjMzOCAxMDMuOTA5VjEwNEgxNTAuMDA2Wk0xNDguNTcyIDEwMy4wNkMxNDguODMgMTAzLjA2IDE0OS4wNzIgMTAyLjk5NyAxNDkuMjk3IDEwMi44NzJDMTQ5LjUyNyAxMDIuNzQ3IDE0OS42OTggMTAyLjU3OCAxNDkuODEzIDEwMi4zNjdWMTAxLjI3MUgxNDkuMTA5QzE0OC42MjYgMTAxLjI3MSAxNDguMjYzIDEwMS4zNTYgMTQ4LjAxOSAxMDEuNTI0QzE0Ny43NzYgMTAxLjY5MiAxNDcuNjU0IDEwMS45MyAxNDcuNjU0IDEwMi4yMzhDMTQ3LjY1NCAxMDIuNDg5IDE0Ny43MzYgMTAyLjY4OSAxNDcuOTAxIDEwMi44NEMxNDguMDY5IDEwMi45ODcgMTQ4LjI5MyAxMDMuMDYgMTQ4LjU3MiAxMDMuMDZaIiBmaWxsPSIjRkYzRTNFIi8+CjxwYXRoIGQ9Ik0xNTMuODc0IDEwNEgxNTIuNTY4Vjk4LjE4ODVIMTUzLjg3NFYxMDRaTTE1Mi40ODggOTYuNjc5MkMxNTIuNDg4IDk2LjQ3ODcgMTUyLjU1IDk2LjMxMjIgMTUyLjY3NiA5Ni4xNzk3QzE1Mi44MDUgOTYuMDQ3MiAxNTIuOTg3IDk1Ljk4MSAxNTMuMjI0IDk1Ljk4MUMxNTMuNDYgOTUuOTgxIDE1My42NDMgOTYuMDQ3MiAxNTMuNzcxIDk2LjE3OTdDMTUzLjkgOTYuMzEyMiAxNTMuOTY1IDk2LjQ3ODcgMTUzLjk2NSA5Ni42NzkyQzE1My45NjUgOTYuODc2MSAxNTMuOSA5Ny4wNDA5IDE1My43NzEgOTcuMTczM0MxNTMuNjQzIDk3LjMwMjIgMTUzLjQ2IDk3LjM2NjcgMTUzLjIyNCA5Ny4zNjY3QzE1Mi45ODcgOTcuMzY2NyAxNTIuODA1IDk3LjMwMjIgMTUyLjY3NiA5Ny4xNzMzQzE1Mi41NSA5Ny4wNDA5IDE1Mi40ODggOTYuODc2MSAxNTIuNDg4IDk2LjY3OTJaIiBmaWxsPSIjRkYzRTNFIi8+CjxwYXRoIGQ9Ik0xNTYuNjg4IDEwNEgxNTUuMzgzVjk1Ljc1SDE1Ni42ODhWMTA0WiIgZmlsbD0iI0ZGM0UzRSIvPgo8cGF0aCBkPSJNMTYwLjY3MyAxMDQuMTA3QzE1OS44NDYgMTA0LjEwNyAxNTkuMTc1IDEwMy44NDggMTU4LjY1OSAxMDMuMzI5QzE1OC4xNDcgMTAyLjgwNiAxNTcuODkxIDEwMi4xMTEgMTU3Ljg5MSAxMDEuMjQ1VjEwMS4wODNDMTU3Ljg5MSAxMDAuNTAzIDE1OC4wMDIgOTkuOTg2IDE1OC4yMjQgOTkuNTMxMkMxNTguNDUgOTkuMDcyOSAxNTguNzY1IDk4LjcxNjYgMTU5LjE2OSA5OC40NjI0QzE1OS41NzQgOTguMjA4MiAxNjAuMDI1IDk4LjA4MTEgMTYwLjUyMyA5OC4wODExQzE2MS4zMTQgOTguMDgxMSAxNjEuOTI1IDk4LjMzMzUgMTYyLjM1NCA5OC44Mzg0QzE2Mi43ODggOTkuMzQzMyAxNjMuMDA0IDEwMC4wNTggMTYzLjAwNCAxMDAuOTgxVjEwMS41MDhIMTU5LjIwN0MxNTkuMjQ2IDEwMS45ODggMTU5LjQwNiAxMDIuMzY3IDE1OS42ODUgMTAyLjY0NkMxNTkuOTY4IDEwMi45MjYgMTYwLjMyMiAxMDMuMDY1IDE2MC43NDkgMTAzLjA2NUMxNjEuMzQ3IDEwMy4wNjUgMTYxLjgzMyAxMDIuODI0IDE2Mi4yMDkgMTAyLjM0TDE2Mi45MTMgMTAzLjAxMkMxNjIuNjggMTAzLjM1OSAxNjIuMzY5IDEwMy42MjkgMTYxLjk3OSAxMDMuODIzQzE2MS41OTIgMTA0LjAxMyAxNjEuMTU3IDEwNC4xMDcgMTYwLjY3MyAxMDQuMTA3Wk0xNjAuNTE4IDk5LjEyODRDMTYwLjE2IDk5LjEyODQgMTU5Ljg2OSA5OS4yNTM3IDE1OS42NDcgOTkuNTA0NEMxNTkuNDI5IDk5Ljc1NSAxNTkuMjg5IDEwMC4xMDQgMTU5LjIyOSAxMDAuNTUySDE2MS43MTVWMTAwLjQ1NUMxNjEuNjg3IDEwMC4wMTggMTYxLjU3IDk5LjY4ODggMTYxLjM2NiA5OS40NjY4QzE2MS4xNjIgOTkuMjQxMiAxNjAuODc5IDk5LjEyODQgMTYwLjUxOCA5OS4xMjg0WiIgZmlsbD0iI0ZGM0UzRSIvPgo8cGF0aCBkPSJNMTYzLjc3OCAxMDEuMDUxQzE2My43NzggMTAwLjE1NiAxNjMuOTg2IDk5LjQzODIgMTY0LjQwMSA5OC44OTc1QzE2NC44MTYgOTguMzUzMiAxNjUuMzczIDk4LjA4MTEgMTY2LjA3MSA5OC4wODExQzE2Ni42ODcgOTguMDgxMSAxNjcuMTg1IDk4LjI5NTkgMTY3LjU2NCA5OC43MjU2Vjk1Ljc1SDE2OC44N1YxMDRIMTY3LjY4OEwxNjcuNjI0IDEwMy4zOThDMTY3LjIzMyAxMDMuODcxIDE2Ni43MTIgMTA0LjEwNyAxNjYuMDYxIDEwNC4xMDdDMTY1LjM4IDEwNC4xMDcgMTY0LjgyOSAxMDMuODMzIDE2NC40MDYgMTAzLjI4NkMxNjMuOTg3IDEwMi43MzggMTYzLjc3OCAxMDEuOTkzIDE2My43NzggMTAxLjA1MVpNMTY1LjA4MyAxMDEuMTY0QzE2NS4wODMgMTAxLjc1NSAxNjUuMTk2IDEwMi4yMTcgMTY1LjQyMSAxMDIuNTVDMTY1LjY1MSAxMDIuODc5IDE2NS45NzUgMTAzLjA0NCAxNjYuMzk0IDEwMy4wNDRDMTY2LjkyNyAxMDMuMDQ0IDE2Ny4zMTcgMTAyLjgwNiAxNjcuNTY0IDEwMi4zM1Y5OS44NDgxQzE2Ny4zMjUgOTkuMzgyNiAxNjYuOTM4IDk5LjE0OTkgMTY2LjQwNCA5OS4xNDk5QzE2NS45ODIgOTkuMTQ5OSAxNjUuNjU2IDk5LjMxODIgMTY1LjQyNyA5OS42NTQ4QzE2NS4xOTggOTkuOTg3OCAxNjUuMDgzIDEwMC40OTEgMTY1LjA4MyAxMDEuMTY0WiIgZmlsbD0iI0ZGM0UzRSIvPgo8cGF0aCBkPSJNMTQwLjc5NSAxNDRWMTM2LjE4SDE0My4xMDRDMTQzLjc5NiAxMzYuMTggMTQ0LjQwOCAxMzYuMzM0IDE0NC45NDEgMTM2LjY0MkMxNDUuNDc5IDEzNi45NSAxNDUuODk0IDEzNy4zODYgMTQ2LjE4OCAxMzcuOTUyQzE0Ni40ODEgMTM4LjUxOCAxNDYuNjI4IDEzOS4xNjYgMTQ2LjYyOCAxMzkuODk2VjE0MC4yODlDMTQ2LjYyOCAxNDEuMDMgMTQ2LjQ3OSAxNDEuNjgxIDE0Ni4xODIgMTQyLjI0NEMxNDUuODg5IDE0Mi44MDYgMTQ1LjQ2OCAxNDMuMjM5IDE0NC45MiAxNDMuNTQzQzE0NC4zNzYgMTQzLjg0OCAxNDMuNzUxIDE0NCAxNDMuMDQ1IDE0NEgxNDAuNzk1Wk0xNDIuMTU0IDEzNy4yNzVWMTQyLjkxNUgxNDMuMDRDMTQzLjc1MyAxNDIuOTE1IDE0NC4yOTkgMTQyLjY5MyAxNDQuNjc4IDE0Mi4yNDlDMTQ1LjA2MSAxNDEuODAxIDE0NS4yNTcgMTQxLjE2IDE0NS4yNjQgMTQwLjMyNlYxMzkuODkxQzE0NS4yNjQgMTM5LjA0MiAxNDUuMDc5IDEzOC4zOTQgMTQ0LjcxIDEzNy45NDdDMTQ0LjM0MiAxMzcuNDk5IDE0My44MDYgMTM3LjI3NSAxNDMuMTA0IDEzNy4yNzVIMTQyLjE1NFoiIGZpbGw9IiM0Q0FGNTAiLz4KPHBhdGggZD0iTTE1MC40MTUgMTQ0LjEwN0MxNDkuNTg3IDE0NC4xMDcgMTQ4LjkxNiAxNDMuODQ4IDE0OC40IDE0My4zMjlDMTQ3Ljg4OCAxNDIuODA2IDE0Ny42MzIgMTQyLjExMSAxNDcuNjMyIDE0MS4yNDVWMTQxLjA4M0MxNDcuNjMyIDE0MC41MDMgMTQ3Ljc0MyAxMzkuOTg2IDE0Ny45NjUgMTM5LjUzMUMxNDguMTkxIDEzOS4wNzMgMTQ4LjUwNiAxMzguNzE3IDE0OC45MTEgMTM4LjQ2MkMxNDkuMzE1IDEzOC4yMDggMTQ5Ljc2NiAxMzguMDgxIDE1MC4yNjQgMTM4LjA4MUMxNTEuMDU2IDEzOC4wODEgMTUxLjY2NiAxMzguMzMzIDE1Mi4wOTYgMTM4LjgzOEMxNTIuNTI5IDEzOS4zNDMgMTUyLjc0NiAxNDAuMDU4IDE1Mi43NDYgMTQwLjk4MVYxNDEuNTA4SDE0OC45NDhDMTQ4Ljk4OCAxNDEuOTg4IDE0OS4xNDcgMTQyLjM2NyAxNDkuNDI2IDE0Mi42NDZDMTQ5LjcwOSAxNDIuOTI2IDE1MC4wNjQgMTQzLjA2NSAxNTAuNDkgMTQzLjA2NUMxNTEuMDg4IDE0My4wNjUgMTUxLjU3NSAxNDIuODI0IDE1MS45NTEgMTQyLjM0TDE1Mi42NTQgMTQzLjAxMkMxNTIuNDIyIDE0My4zNTkgMTUyLjExIDE0My42MjkgMTUxLjcyIDE0My44MjNDMTUxLjMzMyAxNDQuMDEzIDE1MC44OTggMTQ0LjEwNyAxNTAuNDE1IDE0NC4xMDdaTTE1MC4yNTkgMTM5LjEyOEMxNDkuOTAxIDEzOS4xMjggMTQ5LjYxMSAxMzkuMjU0IDE0OS4zODkgMTM5LjUwNEMxNDkuMTcgMTM5Ljc1NSAxNDkuMDMxIDE0MC4xMDQgMTQ4Ljk3IDE0MC41NTJIMTUxLjQ1N1YxNDAuNDU1QzE1MS40MjggMTQwLjAxOCAxNTEuMzEyIDEzOS42ODkgMTUxLjEwNyAxMzkuNDY3QzE1MC45MDMgMTM5LjI0MSAxNTAuNjIgMTM5LjEyOCAxNTAuMjU5IDEzOS4xMjhaIiBmaWxsPSIjNENBRjUwIi8+CjxwYXRoIGQ9Ik0xNTUuMTUyIDE0NEgxNTMuODQ3VjEzNS43NUgxNTUuMTUyVjE0NFoiIGZpbGw9IiM0Q0FGNTAiLz4KPHBhdGggZD0iTTE1Ny45NjYgMTQ0SDE1Ni42NjFWMTM4LjE4OEgxNTcuOTY2VjE0NFpNMTU2LjU4MSAxMzYuNjc5QzE1Ni41ODEgMTM2LjQ3OSAxNTYuNjQzIDEzNi4zMTIgMTU2Ljc2OSAxMzYuMThDMTU2Ljg5NyAxMzYuMDQ3IDE1Ny4wOCAxMzUuOTgxIDE1Ny4zMTYgMTM1Ljk4MUMxNTcuNTUzIDEzNS45ODEgMTU3LjczNSAxMzYuMDQ3IDE1Ny44NjQgMTM2LjE4QzE1Ny45OTMgMTM2LjMxMiAxNTguMDU4IDEzNi40NzkgMTU4LjA1OCAxMzYuNjc5QzE1OC4wNTggMTM2Ljg3NiAxNTcuOTkzIDEzNy4wNDEgMTU3Ljg2NCAxMzcuMTczQzE1Ny43MzUgMTM3LjMwMiAxNTcuNTUzIDEzNy4zNjcgMTU3LjMxNiAxMzcuMzY3QzE1Ny4wOCAxMzcuMzY3IDE1Ni44OTcgMTM3LjMwMiAxNTYuNzY5IDEzNy4xNzNDMTU2LjY0MyAxMzcuMDQxIDE1Ni41ODEgMTM2Ljg3NiAxNTYuNTgxIDEzNi42NzlaIiBmaWxsPSIjNENBRjUwIi8+CjxwYXRoIGQ9Ik0xNjEuNDQxIDE0Mi4zNDZMMTYyLjY3MSAxMzguMTg4SDE2NC4wMkwxNjIuMDA1IDE0NEgxNjAuODcyTDE1OC44NDIgMTM4LjE4OEgxNjAuMTk1TDE2MS40NDEgMTQyLjM0NloiIGZpbGw9IiM0Q0FGNTAiLz4KPHBhdGggZD0iTTE2Ny4zMjMgMTQ0LjEwN0MxNjYuNDk2IDE0NC4xMDcgMTY1LjgyNCAxNDMuODQ4IDE2NS4zMDkgMTQzLjMyOUMxNjQuNzk3IDE0Mi44MDYgMTY0LjU0MSAxNDIuMTExIDE2NC41NDEgMTQxLjI0NVYxNDEuMDgzQzE2NC41NDEgMTQwLjUwMyAxNjQuNjUyIDEzOS45ODYgMTY0Ljg3NCAxMzkuNTMxQzE2NS4wOTkgMTM5LjA3MyAxNjUuNDE0IDEzOC43MTcgMTY1LjgxOSAxMzguNDYyQzE2Ni4yMjMgMTM4LjIwOCAxNjYuNjc1IDEzOC4wODEgMTY3LjE3MiAxMzguMDgxQzE2Ny45NjQgMTM4LjA4MSAxNjguNTc0IDEzOC4zMzMgMTY5LjAwNCAxMzguODM4QzE2OS40MzcgMTM5LjM0MyAxNjkuNjU0IDE0MC4wNTggMTY5LjY1NCAxNDAuOTgxVjE0MS41MDhIMTY1Ljg1NkMxNjUuODk2IDE0MS45ODggMTY2LjA1NSAxNDIuMzY3IDE2Ni4zMzQgMTQyLjY0NkMxNjYuNjE3IDE0Mi45MjYgMTY2Ljk3MiAxNDMuMDY1IDE2Ny4zOTggMTQzLjA2NUMxNjcuOTk2IDE0My4wNjUgMTY4LjQ4MyAxNDIuODI0IDE2OC44NTkgMTQyLjM0TDE2OS41NjIgMTQzLjAxMkMxNjkuMzMgMTQzLjM1OSAxNjkuMDE4IDE0My42MjkgMTY4LjYyOCAxNDMuODIzQzE2OC4yNDEgMTQ0LjAxMyAxNjcuODA2IDE0NC4xMDcgMTY3LjMyMyAxNDQuMTA3Wk0xNjcuMTY3IDEzOS4xMjhDMTY2LjgwOSAxMzkuMTI4IDE2Ni41MTkgMTM5LjI1NCAxNjYuMjk3IDEzOS41MDRDMTY2LjA3OCAxMzkuNzU1IDE2NS45MzkgMTQwLjEwNCAxNjUuODc4IDE0MC41NTJIMTY4LjM2NVYxNDAuNDU1QzE2OC4zMzYgMTQwLjAxOCAxNjguMjIgMTM5LjY4OSAxNjguMDE2IDEzOS40NjdDMTY3LjgxMiAxMzkuMjQxIDE2Ny41MjkgMTM5LjEyOCAxNjcuMTY3IDEzOS4xMjhaIiBmaWxsPSIjNENBRjUwIi8+CjxwYXRoIGQ9Ik0xNzMuNzE0IDEzOS4zODFDMTczLjU0MiAxMzkuMzUyIDE3My4zNjUgMTM5LjMzOCAxNzMuMTgzIDEzOS4zMzhDMTcyLjU4NSAxMzkuMzM4IDE3Mi4xODIgMTM5LjU2NyAxNzEuOTc0IDE0MC4wMjVWMTQ0SDE3MC42NjlWMTM4LjE4OEgxNzEuOTE1TDE3MS45NDcgMTM4LjgzOEMxNzIuMjYyIDEzOC4zMzMgMTcyLjY5OSAxMzguMDgxIDE3My4yNTggMTM4LjA4MUMxNzMuNDQ0IDEzOC4wODEgMTczLjU5OCAxMzguMTA2IDE3My43MiAxMzguMTU2TDE3My43MTQgMTM5LjM4MVoiIGZpbGw9IiM0Q0FGNTAiLz4KPHBhdGggZD0iTTE3Ni45OTEgMTQ0LjEwN0MxNzYuMTY0IDE0NC4xMDcgMTc1LjQ5MiAxNDMuODQ4IDE3NC45NzcgMTQzLjMyOUMxNzQuNDY1IDE0Mi44MDYgMTc0LjIwOCAxNDIuMTExIDE3NC4yMDggMTQxLjI0NVYxNDEuMDgzQzE3NC4yMDggMTQwLjUwMyAxNzQuMzE5IDEzOS45ODYgMTc0LjU0MiAxMzkuNTMxQzE3NC43NjcgMTM5LjA3MyAxNzUuMDgyIDEzOC43MTcgMTc1LjQ4NyAxMzguNDYyQzE3NS44OTEgMTM4LjIwOCAxNzYuMzQzIDEzOC4wODEgMTc2Ljg0IDEzOC4wODFDMTc3LjYzMiAxMzguMDgxIDE3OC4yNDIgMTM4LjMzMyAxNzguNjcyIDEzOC44MzhDMTc5LjEwNSAxMzkuMzQzIDE3OS4zMjIgMTQwLjA1OCAxNzkuMzIyIDE0MC45ODFWMTQxLjUwOEgxNzUuNTI0QzE3NS41NjQgMTQxLjk4OCAxNzUuNzIzIDE0Mi4zNjcgMTc2LjAwMiAxNDIuNjQ2QzE3Ni4yODUgMTQyLjkyNiAxNzYuNjQgMTQzLjA2NSAxNzcuMDY2IDE0My4wNjVDMTc3LjY2NCAxNDMuMDY1IDE3OC4xNTEgMTQyLjgyNCAxNzguNTI3IDE0Mi4zNEwxNzkuMjMgMTQzLjAxMkMxNzguOTk4IDE0My4zNTkgMTc4LjY4NiAxNDMuNjI5IDE3OC4yOTYgMTQzLjgyM0MxNzcuOTA5IDE0NC4wMTMgMTc3LjQ3NCAxNDQuMTA3IDE3Ni45OTEgMTQ0LjEwN1pNMTc2LjgzNSAxMzkuMTI4QzE3Ni40NzcgMTM5LjEyOCAxNzYuMTg3IDEzOS4yNTQgMTc1Ljk2NSAxMzkuNTA0QzE3NS43NDYgMTM5Ljc1NSAxNzUuNjA3IDE0MC4xMDQgMTc1LjU0NiAxNDAuNTUySDE3OC4wMzNWMTQwLjQ1NUMxNzguMDA0IDE0MC4wMTggMTc3Ljg4OCAxMzkuNjg5IDE3Ny42ODQgMTM5LjQ2N0MxNzcuNDc5IDEzOS4yNDEgMTc3LjE5NyAxMzkuMTI4IDE3Ni44MzUgMTM5LjEyOFoiIGZpbGw9IiM0Q0FGNTAiLz4KPHBhdGggZD0iTTE4MC4wOTUgMTQxLjA1MUMxODAuMDk1IDE0MC4xNTYgMTgwLjMwMyAxMzkuNDM4IDE4MC43MTggMTM4Ljg5N0MxODEuMTM0IDEzOC4zNTMgMTgxLjY5IDEzOC4wODEgMTgyLjM4OSAxMzguMDgxQzE4My4wMDUgMTM4LjA4MSAxODMuNTAyIDEzOC4yOTYgMTgzLjg4MiAxMzguNzI2VjEzNS43NUgxODUuMTg3VjE0NEgxODQuMDA1TDE4My45NDEgMTQzLjM5OEMxODMuNTUxIDE0My44NzEgMTgzLjAzIDE0NC4xMDcgMTgyLjM3OCAxNDQuMTA3QzE4MS42OTggMTQ0LjEwNyAxODEuMTQ2IDE0My44MzMgMTgwLjcyNCAxNDMuMjg2QzE4MC4zMDUgMTQyLjczOCAxODAuMDk1IDE0MS45OTMgMTgwLjA5NSAxNDEuMDUxWk0xODEuNCAxNDEuMTY0QzE4MS40IDE0MS43NTUgMTgxLjUxMyAxNDIuMjE3IDE4MS43MzkgMTQyLjU1QzE4MS45NjggMTQyLjg3OSAxODIuMjkyIDE0My4wNDQgMTgyLjcxMSAxNDMuMDQ0QzE4My4yNDQgMTQzLjA0NCAxODMuNjM1IDE0Mi44MDYgMTgzLjg4MiAxNDIuMzNWMTM5Ljg0OEMxODMuNjQyIDEzOS4zODMgMTgzLjI1NSAxMzkuMTUgMTgyLjcyMiAxMzkuMTVDMTgyLjI5OSAxMzkuMTUgMTgxLjk3MyAxMzkuMzE4IDE4MS43NDQgMTM5LjY1NUMxODEuNTE1IDEzOS45ODggMTgxLjQgMTQwLjQ5MSAxODEuNCAxNDEuMTY0WiIgZmlsbD0iIzRDQUY1MCIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE5NiAxSDRDMi4zNDMxNCAxIDEgMi4zNDMxNCAxIDRWMTU2QzEgMTU3LjY1NyAyLjM0MzE0IDE1OSA0IDE1OUgxOTZDMTk3LjY1NyAxNTkgMTk5IDE1Ny42NTcgMTk5IDE1NlY0QzE5OSAyLjM0MzE1IDE5Ny42NTcgMSAxOTYgMVpNNCAwSDE5NkMxOTguMjA5IDAgMjAwIDEuNzkwODYgMjAwIDRWMTU2QzIwMCAxNTguMjA5IDE5OC4yMDkgMTYwIDE5NiAxNjBINEMxLjc5MDg2IDE2MCAwIDE1OC4yMDkgMCAxNTZWNEMwIDEuNzkwODYgMS43OTA4NiAwIDQgMFoiIGZpbGw9IiNFMEUwRTAiLz4KPC9zdmc+Cg==", - "description": "Displays Persistent RPC requests that match selected alias and filter with the ability of pagination and sending persistent RPC requests.", - "descriptor": { - "type": "rpc", - "sizeX": 7.5, - "sizeY": 4, - "resources": [], - "templateHtml": "", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-persistent-table-widget-settings", - "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"enableStickyAction\":true,\"enableFilter\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"enableStickyHeader\":true,\"displayColumns\":[\"rpcId\",\"messageType\",\"status\",\"method\",\"createdTime\",\"expirationTime\"],\"displayDetails\":true,\"defaultSortOrder\":\"-createdTime\",\"allowSendRequest\":true,\"allowDelete\":true},\"title\":\"Persistent RPC table\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px\"},\"targetDeviceAliasIds\":[]}" - } - }, - { - "alias": "slide_toggle_control", - "name": "Slide Toggle Control", - "image": "data:image/png;base64,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", - "description": "Allows to send the RPC call to device when user toggle the slider. Advanced widget settings allow you to configure how to fetch the initial value of the slider.", - "descriptor": { - "type": "rpc", - "sizeX": 3, - "sizeY": 1, - "resources": [], - "templateHtml": "", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-slide-toggle-widget-settings", - "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"title\":\"Slide toggle control\",\"retrieveValueMethod\":\"rpc\",\"valueKey\":\"value\",\"parseValueFunction\":\"return data ? true : false;\",\"convertValueFunction\":\"return value;\",\"requestPersistent\":false,\"labelPosition\":\"after\",\"sliderColor\":\"accent\"},\"title\":\"Slide Toggle Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2,\"widgetCss\":\"\",\"noDataDisplayMessage\":\"\"}" - } - } + "widgetTypeFqns": [ + "control_widgets.switch_control", + "control_widgets.slide_toggle_control", + "control_widgets.round_switch", + "control_widgets.led_indicator", + "control_widgets.rpcbutton", + "control_widgets.knob_control", + "control_widgets.update_attributes", + "control_widgets.persistent_table", + "control_widgets.rpc_debug_terminal", + "control_widgets.rpc_remote_shell", + "gpio_widgets.basic_gpio_control", + "gpio_widgets.raspberry_pi_gpio_control" ] -} +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/count_widgets.json b/application/src/main/data/json/system/widget_bundles/count_widgets.json new file mode 100644 index 0000000000..775947de01 --- /dev/null +++ b/application/src/main/data/json/system/widget_bundles/count_widgets.json @@ -0,0 +1,15 @@ +{ + "widgetsBundle": { + "alias": "count_widgets", + "title": "Count widgets", + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAABaFBMVEXj4+Pj4+Pt7e0AAADi4uLi4uLt7e3m5ubi4uL////39/f6+vrh4eHxjRf5+fnRJzD+/f38/Pz19fX7+/vzxcj74cHdYWf1q1UhISHylSb62bKbm5vz8/PGxsb+9/CZmZn4ypP2s2MkJCTx8fHa2tqrq6vn5+dcXFzBwcG5ubmKiorv7+/r6+vQ0NDNzc2/v7+fn59FRUU/Pz/T09Ozs7Onp6czMzP55ebX19fIyMi8vLzZSlEvLy/k5OS2trakpKSHh4eBgYFgYGBQUFDVNT05OTkrKysoKCji4uL43N31z9HKysqoqKiSkpLidnx5eXlzc3Nra2vbVVxXV1fxjRj//Pv+8uTf39/97t3c3NzDw8Otra3aUVlLS0v0o0TxkBz88PH76+zj4+PwuLvvtrntqa361KfqnqLqmp74yI74woPje4HhcnfgbHL2tWj1q1LXREz++vX85sz85cn73bv2uG7bV17TMjvdKyySAAAACXRSTlPztSoA8bArH+7/EoV4AAAFjUlEQVR42u3dZ1PbMADG8XT3scGVrA63JNCGJJBBFnvvPQt07733+vpVHEpDrZjm2osln/6B3EHe+He+vLKfc+TYySORFsWLHDl6LHLyeCeF4tHO40cjRzoRgjqPRCLKn49KNBJpQShq0RDJ0hDZ0hDZ0hDZ0hDZ0hDZ0hDZ0hDZ0hDZ0hDZCiOEVKKUMsZMyeOHSCkllbyQioILLMswDFPy+CFaFvdULAIIMw3ucDOkzqrGD5cJIJRZ5h7Atg2pX/z43PgBM+qBmAZ3QKm4xDA9EMMyFHMABj9oD4Q7bCiWzSUeCHcQKBbhEi/EJupBiO2FcIdyV0oolwggdB+y/Pa8oLfLkCxKBZAooQx73T0j7C5qWi0UCgnUNoFmxyiJeiCkBnKmTqgpm1vPRUEI0M09lABtaHYcQv4ZAnQAicn+fLZj0syvFZckh3z86AcpMv7Oz0ju1khaGggVQTYI2fCBtPGfCiTZm58IBEL/EjL3EHg4Vx8Sm+juQ76P9cf6+6SGPAPvWR3IOIBUC0ASlCWyKf530/KHMPNPyF3ifrK9D5Esk/0lZAUYHARWVIdsANjeBrChNuTHIwCjowAezSkNeQHemzfgvVAZMnoN+5FRLyS1WZpdN9BIwUDOotLTp6h01gOx58cyW84WGikQyDu4jY7C7d2fkFj5DtC1SNBAgUCW4bayArdl4Xdkdh6NFAhkEAca9EJurM8spNFQQUC+Pzlb05NtLyR2b6GcRyMFABGHP7JnBxgaSEpIb0cKmHIeo4GkhAw7m0aqNEYRXIdD5sSOOdQ2FY87YwUE2OGQl2LISxxoKd3diYDyhzD8avCsoEHIFhNDKINiMaohciWGEBUhRAwxoVRmfYhSElMMsQnlEtmvTNdeo+YOKrw+QihTR2JyBxNf6KmeEtO0lMg0qydECOES+e9D+RWrODgkxHcHqZ2GyJaGyJaGyJaGyJaGyJaGyJaGyJaGyFYFotzuomZ1IYAQdXYXtasLIoBQRXYXNasLKoK4DMPNlnt4YRtuLsULMS1Tsb1ChWIJZhempdpcweYSwexCOYcr0bMLqRLPLmwVZxe27b8fuX5O0HXI1qH7kVe7rYJ2L0OyKCW278XQi63CLh68qSbZx7BfGgF06FqhtU6oqSeG3hEYhTy5FUUPf5HhbgPZkQTqJyuEtqF/vHt16HY0iTake1qStC2XTKFpiSG0YYhRxFSyP00mYwm08ReQyySLvWhajNL/ASGFGDpM1om+IuGMtSwbtoskasKnICAXX13+3QcP5NbkWoygpT+ZQuo2MIFsMjmO/NodGz41BcIOQp6TL+f2+1aFSBbzu/FsH3Lpa2tNUkLMv4Ncaq12/3p7e/vnEECu4PXr5wgFhP+GGZIt3I6CZ8S6bbjZvTn4JSekf9FxFgrAjZLjDPQAsKYXnD74JSXkRnwmlxgoAzsDQ4/L9whwNT6VMOBXIJBL1661u125gvb2Tx5IXzlWOXYadaaBojOEYacDhxQI5Cb2uvIBvHbRl52W7yHlZIARJ43peNvOThp+BQJ5cHGvB63uuwiScbpB5ks22XQm0OWUpkrOMHwKAOJNAEk66wDuxAfmB5xVzI5FkYpfhV9yQkbim6g0VBzJLEbRNQ+gvAOf5ISsLl6lqLY0sAVk4kvILm7Bp6ZDdsWOXdQ0PhafzmQyeWByZqHUCeTGSh2z8V741HTIfTHkPmoa6nJLA9NdHVH3PzPlmWH41mzIzfcXBL2/Ccky9exCskIOCdPsAooV7v1I1B0rSH1d+mDVuUK0zn7EtExDiSzL9NmPUKbK6uKXg+jZhaxpiGxpiGxpiGxpiGxpiGxpiGxpiGxpiGxpiGxpiGyFCBKaBwSH5JHNJyJHQ/EQ7dOnIseOhuCx5idOHfsJzoAQyrcRdLoAAAAASUVORK5CYII=", + "description": "Cards to display the number of alarms or entities based on the selected filter.", + "order": 5000, + "externalId": null, + "name": "Count widgets" + }, + "widgetTypeFqns": [ + "alarm_count", + "entity_count" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/date.json b/application/src/main/data/json/system/widget_bundles/date.json index dc868235df..d6bf61a989 100644 --- a/application/src/main/data/json/system/widget_bundles/date.json +++ b/application/src/main/data/json/system/widget_bundles/date.json @@ -3,27 +3,12 @@ "alias": "date", "title": "Date", "image": "data:image/png;base64,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", - "description": "Contains widgets to change the data range for other widgets on the dashboard." + "description": "Contains widgets to change the data range for other widgets on the dashboard.", + "order": 20000, + "externalId": null, + "name": "Date" }, - "widgetTypes": [ - { - "alias": "date_range_navigator", - "name": "Date-range-navigator", - "image": "data:image/png;base64,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", - "description": "Allows to change the data range for other widgets on the dashboard.", - "descriptor": { - "type": "static", - "sizeX": 5, - "sizeY": 5.5, - "resources": [], - "templateHtml": "", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-date-range-navigator-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"defaultInterval\":\"week\",\"stepSize\":\"day\"},\"title\":\"Date-range-navigator\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - } + "widgetTypeFqns": [ + "date.date_range_navigator" ] } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/digital_gauges.json b/application/src/main/data/json/system/widget_bundles/digital_gauges.json index 7974ae2ea9..39140fbc9c 100644 --- a/application/src/main/data/json/system/widget_bundles/digital_gauges.json +++ b/application/src/main/data/json/system/widget_bundles/digital_gauges.json @@ -2,256 +2,25 @@ "widgetsBundle": { "alias": "digital_gauges", "title": "Digital gauges", - "image": "data:image/png;base64,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", - "description": "Display temperature, humidity, speed, and other latest values on various digital gauge widgets." + "image": "data:image/png;base64,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", + "description": "Display temperature, humidity, speed, pressure, and other values on digital-style gauges.", + "order": 12500, + "externalId": null, + "name": "Digital gauges" }, - "widgetTypes": [ - { - "alias": "gauge_justgage", - "name": "Gauge", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAPS0lEQVR42u2d2W/U3hXH519p+6t+baXuD1VbVV1fWvXl14e+/NqHtj8RFrFTKDtC0ILYxB6yQTaSkoQkJAECYUnIAgkkQAJZISFpFrKSmSxMNvqFKyxjz3g8Ht/ra8/5ykJjx/aM7Q/nnHvuude+dyQSB/noFpAILBKBRSKwSCQCi0RgkQgsEonAsqju7u5zYVRUVIQdrl27pv9TWVkZ/lRVVcVWx8bGNKdV/jQxMYHVxsZG9eHp6eklJSU9PT0Elmc1Ojpa9VGZmZl46uXl5Wy1qalJAevq1atVKjU3N6vpqaurU59zfn7+/PnzerAKCgrY4ThncnIytrS3txNY3ldxcTEe9vDwsHojA6u3t1e/PwMLiGRkZCwuLirbOzs72XYNWA0NDco+r169wpasrCwCi8AKDRbbQe3X4ChTUlLwrwFYUGpqKjYGg0ECK37BamlpGVFJDRbcWVJS0o0bN9jGQCCA1YqKCnZgOLD8fj+2gC21qSOw4g4stcCNGizYKkRgcHyzs7MKQ319fQjPNWDhVM8/CKs5OTnYUl1dTa4wrsGqr69/oZIarK6uLmzBBxbR5+bmZmdnLy0tFRYWhmsVMltVW1sbV+aKwIouxoIrXFhYQAYBjb7BwUHF5eXn52vAqqysZJ4U7hLkUR6LwDICq7W1FZ9rampY6guOEvETtuTl5RkH7wQWgfXeLE2qNDU1pQEL+TDm40pLS9mBBBaBFV3wDiF+0oAFwRViFUksAovA+kQdHR3Itk9PT6s3IjBv0gl74k9o+uGz0p8zMDCAVaTd2WpbWxtWWVNxaGgInxGBEVgkEoFFIrBIBBaJRGCRCCwSgUUiEVgkAotEYJFIBBaJwCIRWCQSgUUisEgEVrwLJeoY+IDadpRbBT/qrUrKRuyA3bBzfFa1E1hhBSzABwr0UPGHUQ8oREYV6Lgl4UAcjpPgVDghTouTE1hxRBJMDp59LAxFSxu+Dl8ab5x5Hyw4LJgQ2BIBJEXkDD8DP0apaSaw3BcnwRlhgI3jMBlAhp+HH+nV+MznPZ5gFcZdJQxOhK/02FBpj4DlRp70wiV4ZkYad4OFiHhmZkZaf2fZSyLed3uw71aw5ubmPGCiIhowXCaBJc7roQ0/HjfCxbrRP/rchdSbN2/G41K4cAT4BBYhFe94yQ4Wcolx5fhMOkf5Yy95wUJeBylEwsggtJe55SgjWMhzIolA6JgRbpScuXsf+T4PBF4Sdj76pDJUSAwSKNaEWyeV6fLJY6io3ecl0yUFWBRR2Wu6CKz3TT/07RMNtpdLOF4r4XPW/Xms/1iqnmxnc12OgYVCSnr8ApIRcQQWGi+U+RQm3GpHWos+8VRRUCU+5BLPllCwEFFS8tOpTITg/h9xYFGo7ng4LzLL5SOqiC23gkVUxWEagjtYuAx6nLJJAFs+slXElsvAciNV6rk9kF1U5paZU0mZfwY7xD6biFfjLV5goXHrinuNdjhSiOgGADEx9q/hcJwEp8IJXVGpgQfELwfBBSzcYpnvrDJvAteeWpxc8vkj2P8rTjfBfrCQ5JUzC4qbCOflSMUSvhROU87/bHhYPPLy9oMlW48Ns0+SVMDhZ0howxAmyg6WVLXFbBSehGMN8JPww6QyYLaXB9oJFm6WJLcJVtMVw9LxI+Ux8PbeMdvAkqQZ6MaZDtCWlCEqtbeR6LPLtjtu2PEDXD25lAzTCOAH2BU52APW/8YCzv5XQ/bIA3MuspG6zhp+tC1kASu3ZfZHaYErLRNOhVMem5DY8QEmtsw7EitY/ZPz308JfO1M4OtnAtsqJodGhBoqd83sE21LyCnThe+NPWsaK1j/KHlPlbL8Idf/+NWEGEPlsdlgQ7aHnArqcXudBOvi02k1VWz51jn/qTq+QaiDg0/Ey6nUIMJWZ8Aa8C8wJxhy+apksmeIi5X2zLzCUTUYxbvFGB2idbD+WuQPRxVbfpERqOq083aIHxEglVsUn4yIpavHIlhX2maNqWLLZ4mBA1WTo2NS98O7qLUoni3L/sEKWMGFpV9mBsyAxZY/F/jb+2MyXYhh45wqhS3B4bzllKkVsM42TJunii0/TA0UW010OTLeUuYkquAJ7q01laIGa3xmAZRECxZLdG0snxwcmRDm5j0skWxZi+KjBmvnHStUKctvs/0N3RNkq2K3WyKz8xb6eaID6+X43OfnYgLLfKKLU2Wjl9gSGW9F2x6PDqyEslipUpaE0sne19QGdE07MVqjFQVYnaNz3zhrG1hYfnrBf7t9gndhkOfZEpY7jeqhRAHWppt2UqVOdI18muiKhzfb2ijcLgmNllmwhgLznyf6bQdLSXS1fUx0ebhggZ+EzQ5sPj4xC9b+e1OcqFISXUXNE3ZVmcWhxDQSzY+5MAVWILho0N9s1/KzdP/beWoGSh3II54z2VQ3BdbZhzO8qUL69NbLIPERi8RM7GOynCYyWODz5+nczdWWCnKCNkhA8Rbsoj1g3Xv1ljdVABfe1hs2Y2Rk5MWLFx0dHYODg+JfEiFmuJSZKZAig7W+nLu5utPtcEsQz6O9vT09PX3Tpk0WmqU4pKKi4siRI8uXL1/2qfbs2VNQUDA8POwlh2imjRUBrOng4neT+VL1txInneDr16+Li4u3bt2qoBBtSW5DQ8PmzZuXGWrlypWFhYXC8nMCuqgj5h0igJX3jG/YjgRp26gD6VA4qbq6umPHjiUkJGggiAqs27dvLzOtEydOiGELT93xIWIRwPqy0M8VrJ13hJordEo8fvw4MTERJiTc4zcPFmyVnktjwdt6I2UacRiPEVgYLvHZWY5UfS8lMDYjKGbv6enJzc3duHFjxGdvEizshoBMc+z69esvXrx47949mMOioqIdO3ZodgCICObERI28+xCNvaERWEmPprmaq/9UC2o0obzEvFExCRa40Rx44MABfJHGQIIzzW779+/3htEyvlFGYP2liKMfRM/j66lFl4IFe6CO96F169ZpqFL2PHz4sOYr+vv7PWC0jL1hWLBm55e+fY4jWNtui4uu9GDBZ2VkZLS2tiLesgAWMlWao9DoC7czfJ/5nd2VLzXo3gkLVsULjnlRhG7dE/PiwVq7dm1aWlpzc7NSWpSUlGQBrLKyMs1Rvb29BpZDE9sdPHhQWAeiU4PDwoIVY2278fJVqdAhEkjoJScnoz2ob+1bA0tz1Jo1a4y7Zs+cOaPef/Xq1cKqrrnmtAwypWHB+lUWR7DKu2bfySFrYCGfrj5k3759xvvn5+drvgWJWQ8k4g36DUOD1T0+x4+qH6cF5qXpGLQGFkyU+pBTp04Z73/r1i3Nt6AzUdg1cu09DFevHBqszCccE+57KiUqZLAAFryYJi+akpJifEh1dbXmW548eSLsGrnmHcKl4EOD9c8KjvWiz4bnXQ0WbmW0+fT6+nrNIcigiuxvEB9mhQYL86dxourXWXKNbLYAlj55EREsdP5oDqmqqhJ5mfxGIIbM3oUGC3N+xD4q1RV+0BpYCFliB6uyslLkZXL1hiFbuCHAahoI8vODlT1BAks8WFyHiIUs2QgBVvpjXl2E30kKBCUbiBonYEH8undCxu8hwNrCLXL/e4l0U8fED1j8MqUhK7BDgPXH//KK3BMfzhBYToGF6xLZGx0CrB9wG0JY2xv0AFi2tApramoEXym/MAtONjJYgbcLnKjChCJTc0seAAs9r5pDUlNTo81jPXz4UPzFimwYasFqHeHVmfO7bBnn5rPWpaOpbEbtjfH+yFppvqWlpUX8xfLLZuk7drRg3eRWLbPmunfA0pTBHD161Hj/8vJyzbegokv8xSJLLqx+RgvWBW65hpP1054BC1XI6kO2bdtmvL++QNmR6U/4xe/6m6YF69/cZpUpbH3rGbCysrLUh2CcqvHUwjBp6v0xDtGRi4VdETazshas1dd5NQkb+uc8A5Y+ZkIVYbidURG1atUq9c4nT5505GL5NQz1BlgL1pdFvMAa8C96BizcSk3lzOnTp8PtfP/+fWd7oBWh7cYJLP2s6Vqw/pTPhapvJgYWpJz6yhpYkGbsDTjD0IyQYY1mdOGKFSscnLxeWI5UCxangpmfXPC/e+cpsPQ5T4z8wTANTffcoUOHok16cRWnalJ9jbIWrN9kxUUZVuxghRwwCO3duxeJeIy6Pn78uH4gP4ZR4DE4eL2cUlmRwcIU2TzA+n2O1ywWNDQ0tGHDBvNDYeEunYquFHGaqlTfq6MFy9p7ciIuX+QFvAcWhOGE8IAmwUKa1PHrdQwsTqOf0dj0JFgskMIgWONpZxC/O9KHoxe/4pkIYP3r1tTmCvuXtCZJ3+IM35T5qczMg6hXX19faWkphjgj+Yl2H4vld+/ejRM+ffpUnlcioChvmo8igEWyRfTKFgKLRGCRCCwSgUUiEVgkAotEYJFI0oCFVBtla7ytuQ8K+Sf0T1ifjjucUHi0a9cuVpKLOcfCzTdCkl+o0ejs7ET3AHql1NsxrzPq+hM+CN0J6mmea2tr2SteUBaLOo5w5EUNFnqFUPuBXv3r169nZ2fjiyOOUSHJqbt3727fvl1fLQ1WMNk4an6uXLmC6ezxAausVwoU4onv3Lnz5s2bbMLpnJwce8BiI5mUecNgsbA6OjpKz8l1wquEUFHNprFUg4XOTWy5fPkyW2UTqLIaRpgofO7u7mbWDp3rsDIhfWLUYLEBKngZH1tlE1M/e/aMnpPrxIwQm4pCDRZekYctjY2NbPXRo0dYxUZ8RkEs4h9l3DPmyMSfQpYuRg0WGMe5lLiKvf5K/EwEJLukBwu2Clva2tqUkBqrsFv4DBOFqg1lT8RC+NPLly9tAAvvRlODdefOHQeHnZB4gJWXl6cH69KlS/iMYEsNFhuI29XVZQNYLKhSapwZWGgp0BPyDFh4JSy2KIOOGFjYyCwW3hqkAYuFXPbEWAMDA2wV1W1Yff78OT0hz4DFwnmEVuoYCxvffRj0hhhLidbxvg/8ydQ0RhGFdibOhVfyUavQq2Chihpb4BDVrUJWWo2Ml+L7EMIjW4F3KdjTKsTvwLngaJHHwiAnZDXQaqXH4yWw0FoEMUhf4W3ZJSUl+IBZT1gTEnE6njgcIhqJbLgAGAh5WiuZd7wnjb1MhmXeI77FlSSz0A5D2ARK1BuRTkLCXcm8K5EP9ODBgy1btrDMO17NZ1vmXRH6CuUZI0DiIdF9hSRSRBFYJAKLRGCRCCwSicAiEVgkAotEsln/B58KkIEdqUQjAAAAAElFTkSuQmCC", - "description": "Preconfigured gauge to display any value reading. Allows to configure value range, gradient colors and other settings.", - "descriptor": { - "type": "latest", - "sizeX": 4, - "sizeY": 3, - "resources": [], - "templateHtml": "", - "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", - "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-digital-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#999999\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":36,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#666666\"},\"neonGlowBrightness\":0,\"decimals\":0,\"dashThickness\":0,\"gaugeColor\":\"#eeeeee\",\"showTitle\":true,\"gaugeType\":\"arc\"},\"title\":\"Gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" - } - }, - { - "alias": "digital_thermometer", - "name": "Digital thermometer", - "image": "data:image/png;base64,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", - "description": "Preconfigured gauge to display temperature. Allows to configure temperature range, gradient colors and other settings.", - "descriptor": { - "type": "latest", - "sizeX": 3, - "sizeY": 3, - "resources": [], - "templateHtml": "", - "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", - "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-digital-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < -60) {\\n\\tvalue = 60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":60,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":1,\"levelColors\":[\"#304ffe\",\"#7e57c2\",\"#ff4081\",\"#d32f2f\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":18},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"dashThickness\":1.5,\"minValue\":-60,\"gaugeColor\":\"#333333\",\"neonGlowBrightness\":35,\"gaugeType\":\"donut\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital thermometer\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "digital_speedometer", - "name": "Digital speedometer", - "image": "data:image/png;base64,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", - "description": "Preconfigured gauge to display speed. Allows to configure speed range, gradient colors and other settings.", - "descriptor": { - "type": "latest", - "sizeX": 5, - "sizeY": 3, - "resources": [], - "templateHtml": "", - "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", - "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-digital-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 45) {\\n\\tvalue = 45;\\n} else if (value > 130) {\\n\\tvalue = 130;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":180,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[\"#008000\",\"#fbc02d\",\"#f44336\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#ffffff\"},\"neonGlowBrightness\":40,\"dashThickness\":1.5,\"unitTitle\":\"MPH\",\"showUnitTitle\":true,\"gaugeColor\":\"#171a1c\",\"gaugeType\":\"arc\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital speedometer\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "neon_gauge_justgage", - "name": "Neon gauge", - "image": "data:image/png;base64,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", - "description": "Preconfigured gauge to display any value reading as an arc. Allows to configure value range, gradient colors and other settings.", - "descriptor": { - "type": "latest", - "sizeX": 5, - "sizeY": 3, - "resources": [], - "templateHtml": "", - "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", - "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-digital-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":70,\"dashThickness\":1,\"gaugeType\":\"arc\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Neon gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "lcd_gauge", - "name": "LCD gauge", - "image": "data:image/png;base64,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", - "description": "Preconfigured gauge to display any value reading as an arc. Allows to configure value range, gradient colors and other settings.", - "descriptor": { - "type": "latest", - "sizeX": 5, - "sizeY": 3, - "resources": [], - "templateHtml": "", - "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", - "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-digital-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 180) {\\n\\tvalue = 180;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#babab2\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":180,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\"linear\",\"refreshAnimationTime\":700,\"startAnimationType\":\"linear\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":1.5,\"decimals\":0,\"unitTitle\":\"MPH\",\"showUnitTitle\":true,\"defaultColor\":\"#444444\",\"gaugeType\":\"arc\"},\"title\":\"LCD gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" - } - }, - { - "alias": "lcd_bar_gauge", - "name": "LCD bar gauge", - "image": "data:image/png;base64,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", - "description": "Preconfigured gauge to display any value reading as a bar. Allows to configure value range, gradient colors and other settings.", - "descriptor": { - "type": "latest", - "sizeX": 2, - "sizeY": 3.5, - "resources": [], - "templateHtml": "", - "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", - "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-digital-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#babab2\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\"linear\",\"refreshAnimationTime\":700,\"startAnimationType\":\"linear\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"400\",\"size\":16},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":1.5,\"decimals\":0,\"showUnitTitle\":true,\"defaultColor\":\"#444444\",\"gaugeType\":\"verticalBar\",\"units\":\"%\"},\"title\":\"LCD bar gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" - } - }, - { - "alias": "simple_neon_gauge_justgage", - "name": "Simple neon gauge", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAolElEQVR42u1daZgsVXnumu7p6WWWO3fuMnMb2TdBAVFWURZFEFkuywVEFgUEERBEBNkX2XfZZVPMozH7/mgSE7M92XcTg9kDmoQsJjH5T96vvnPe89Wp6p6+3Blmurvq6ec+0zU1datOvef93u893zlVee21115++eVTTz11amqqUm7ltg0bILR58+aXXnoJoKoAVWvXri0bpdyWapudnQWoKuCqsi3KbWm3LVu2VMoIWG5Lvk1PT5eNUG7ltpxbMpZUJ6q19vj45Dh3NtY1W5va7U672qzpnv1PePtTrz772CtPffInr+ZhR174nsM/csReR+7dmmmVLTnq21htDDAaGx/Tr0BVe7tJfFoL7QCs9U3dWfPAOuDkA1/4vy/hc+3Xr+dhD/7153Tnnu96s+7Zft8d3nXuuzft2QFey6YeoW1ibUMRU5+uO8aqJroHnwCsOXdYreWAddCWQxRDl33lCgfQ6thz//0F3Tm9YUZ3nnzTqbrnohcuKVt7aLdqvVqfmWjOt4Ae3YN4p4hBsONhEvXSnTxsYnYiAtahZx2miPnIExfonjULs7rn8e89zVMhUOrO937saB52z5/ff/qdZ+528G5JUtLYUGyAlMNH2+knRMB84GtucIFvrO7iY32mHv0hApwi5vQ7ztQ9O719Z91zw6/czFM9/HeP6c7t9n6T+8PzDtc9937rgRJYA6rDK8BBY12DO4iPiblGP/xEqT4+FQPriAuOVHx84OoTKOcdhz15YcRh0PjUWJd++XLdecrNpzGGfuJHP/nOs9813hgvH9oAoAok5PDRqMX8tKldSWL9xMMIIyaGwFO056iL3qv4AMKiPcdeeZzu2ff9++meS7/yCZ8oVBEoIw7b51h32MN/+2i9WS8f3So1C/LC3PJTc6OLhsj+Ipk17vU7EBYpeiSDEbCOvvQYRQPSQ91zyi2n6R7gSfec8JmTnMC65H26Z4/D9tQ9d/7RPbwkwE53XvD5i7hzcq70q1cJpKpJfc1EuzMJ+yDiJ+wk4IAeB5qZenQY8YczuD2zE5EHMT7lgHXsFccpGvY6Ym/dA1jong07b9Q9SBgdOb3FkdNpt5+ue0649iQHoLWTz/znC7pz+3221504w+f/4/kLn7143Y7ryye70sLcUxHRYP0nMg1Bg+MjmdWaD3uARZsqEnyIkrrnuE8d79Cw7w665+qfuxZfgRJoJt1z/7cfigTWbb97p/7Vxl3mdc8xlx+re67++Wv5v1/w9Ed1522/c0f5ZFfaSvDRSvjJkxaDmoVRc0MrUuuUWUSAwpR/RXOL4ZJhbm77dboH0Q1fb/+9Ox00Z9vP/++LVmBBy+uem3/jNl4MobbPMfvqHpzw2f9yftjbjt/fXUC6lU/5jdjAPSAkpmmWnyxpkcnG6tVuKR5lFoXXRAo10fheukWqa/MNp+izb0y6APrEv3weXy/50qX6FQM7kcB694ePcOr+Cqfudz5gF91z15/cS0yf99j5uvMzv3gD7+Lw84+87pdvXNhjU/ncl3cTNd1x+R0fSUZUeTZiQke0kX6CqMpFurqXYuQJ/e8ILJXqoBY9YKLdUDQAcHrA+z/5Ad3zprdub0U6SIskd+7nPqLHADe6Z7az9pnvv6CHwRijDnv05SexE8Lr7ScdUD795c3+6D9ZfgpDNGsmgu+gEspaDKkFavfoMfhzB8eWi6FMBfQASn74onjSj/z9Y/p1frcFhQiGenTPxV/8uBVY1fHqE/8sRgOIx2G3WVfrAcfUW+60H3rwHDcE9PzHeFPnPerwB3jNbJwpn/4yhD8fzuyzt2EO6kqpxZIW6YcDMvnYpzKr6fU7OYwHqGtPvJ55z1nWNXjz4XtFmd1df3qfHUnEsLQeALtL9xz6wXdGJAfQPP3vz0kG8P0X1u+0QXfu+LYdn//Bi87aOOWgwqYot20yPNUQpxtOJSSA2BC0Oe11DAsGhaRZ3vpmrJk8AxFqLvYllQiLSnIE1lkPCLVc/42b9OshZx4q8esHLyr3IDIqGo7++DF6wJbPnoGvz/3PFzk+fc3XrtMskiR0xt1nKYDgSvA6b/zmLW60+4evCJlKo+r4tRT027gxxomo8lQkAdHb61TiIUp2gvziKA0TRlX6zPvyFKVnpurC0JANu+c+8mE87Ct+7CrnPlwl7sN9f/mQft390D0igfXZ378LXz/1s9foVxhUmiFy/AdC6qlXn9HIiIxSd3Lg6HP/+MT0+mlqxCAD1jZKbGwbYdXG2JokHusyWBVP+sm7oMFM96KeMNXz0/GCiWUpSpFNYH34ccndLnzmIktgLPoDUVmBBUmuMMIItB7Achoi79Rbt0R1EIDXo//0hO60mp3JLy6YErDcthpPAUPe/rZEYsksqPjESSKLNgUKqxgYH0l1KrN4EvHxTebovvrfqs/+wXs/ZDM+xDL7WwYvJH2aQioV4b++/68eFgL7GUdgzemWCnmUPNTqtch3+OhzQchz5CAd3KwWNlS5LbIJ8XQyzYcRlQLBbgIid4bRvTACWI2eh4tuHjrKcwyOSmkoobFPlKEHWRse+YnXbdavqoRQl2xtT4wn6lcgDF9RwqBf33r0PooY/KB7Trr+ZMdMJ74j0uwYmWZkRBmPS00Ms7ou18l0tnLrjirfNYXwx0N3pFoCmMhGwWE3Kl5DhiUtNUuJJM0raTowXOpXJUgynMKOwIIRiqf+notd2HrwO4/gK6SVHNkYV+tcM0QaDQef7pyIS37oMmeKpolCc6r52Hef0kIu3WM1O6pxQv/x9WRWWuGyKRLYi8qtu7Ng2ksw5JWQhDlfiGfLFkKRJ4v46tUobjpdxTHpBNGwnRHs6f+o9X3MHC0K+T9q7FOs4EgtStaShJ3esbMVWOpEPP1vzyFVrJhRZxjxeqrjP32iOqK7HrSb0+wXHhWVdtmIL5zKxqgGtpYeWC8DYn+uVavTZmvSBLetmUkGF9oRRenDkD3eRHC5no8jalhQ46vM4jkVZ4ppjaQND6xP/MiVjGXwC9S9tKkcBRaqkO1Qj+p6ZHlaejXRnsDP2ANDNdLsmJrRXtOOUo1ufSxFVels9YyANvCl6sEng6YWVNjIGaEhM6KKCqM33i9luYuapUFIpVKMX12883+uwVRpQPmP9Q5azA5yUj0kQ3u/dIMdqKHA+uwf3G3F062/fQe+QlTpVy2/gSO6wTuimm+CwKjAcIOug3UCuTId0Uaw+9GAZUzMqfVc51Nlmh+9YSe2oYEBkWeATeqkUpJJBl3USDK0pDKLgt1ZsmllqdbbEFhwpFh6td8H9sfPSOL0Vzf9+q0sp1m73Vr8jPFp5acd99/JmqJQYw/9jcwYA6s5zb7/TqrZ4ZMFWtoYl+ozS3VoM/kN7sv5JlNloXM0GgMcZLugUFGnwGhg49JtQshThUsVT780hL8ULpFBlQl/HaffNQnVX6nkooWmvrnma5inip9RWZXir4qhYngHWpWlaomloWc/fB6+gpP0KywrUWPffYo2xE2/dqsarSyayN9jxRRoRKhCo1GY2vKhUd+idsk0mS9nsMMsePx0C1mxTrsrqoohaSktMRmMxJPKLA3H+it6qhZYGEsGtSh6MC1C9Nb7JHKhWBQ/X/7VKzNSLP0V+EmzPy0oRbaolYAMmgpQnJbzXRncMaBEVqaTYifQutbzskF6ZqOUXFEyuGCwZRquZkr5qtbEohfKUee0lwcV7/3S4IWm6sQBNI2GeBIZlkp/laThj/EXhzFKwg5A3ufs0LTgc90OUgyDyTb4+X2XCXvB6nzyX5/BYep5IoUUd/6nrrYaXxzRiZpmi6rZz7r/7ChHscZKEJe2g2VJHcdbnTqyIVCCkR2aQIOyyBONZTslKxrsaIYwULaSmEgiJhSUJC3HUuuzWioNvspnjDs4M0+LczLC3vybt2PeKfUWqhL02QMWFFha6wctr4eh+Bhf937PW5Su7vuLB/H1HZvdWA0mvirO1JUIQzedSaLEjmtZvSU2nkeVpM/VMDSNVhrRsWp9qGivDHUnidri+a4ZEm9MaPb9OPjsXoHRxKKK1/IEPg9VxKpCNHqyGgIXQ8zhMOaMQBV/RjU69BDzPuR6DJGwQzVEarXCnu+WuAY+Q4zDULQaH4edI/Ndb/hV54iqZsdHLVaronjvuFlOuLW2e82IBPQBO0+J0mLkxqr5+KM5MHHik+2gYV68UR6uWqZjpy9PWBWv/xcrrhSg1E8gM4IGMotFyYjFDMe26hwinfoaWaEWp+MABD7oKt2Pai0MyCjIdNAG86cr6QxV2O74iln2VrMzN2S8o8EmatJbVjZ9sUVpNmWOhr9sBxsZwd6oBlGVqzEK6U+ELd9q1nxXkgtM5hN1/qHaoUFaLbQ5YqMg1iAi/77e2AFbYWZ+jbqmqL5SwSSjzt9+6JF/eFznOqvYwsCO/slRHxXNfucf36uWBNCpzGSXk2DlWQZV7QCdujWukmC7+GhQG0mVVU2C15dTCZbqbQio52YISrDIjgYqS1HFq6MR7ND0DCqt8D8u+UpDU+umFGR7H/UWjlgDNwiIcER1BpjWs2NESL1W2h9NE+hD6aJBD2lbZKiRCqlCDY2JmG4bc/g3wCW6YUtOIJKq9UjrwYbI+Fgz9fy0QT2Sba3HRMUwmhDgyeG3b0C7wyZFBSlwhp+h1gEyVDY7zf7khbKOwy2n2dBmU5N8bZltK2GjqKFMTh3VO+BObSY0jNIqZQ40iu1q6hpTn0b8BLLhr2wTU4cFH0u9Hw71+ICoIkP9Uhtl3uDtqp/+NNwsEFUlXaAGgv2OP7xbQyR7RXwvWXeUVI0Gsdm0RMZOcc/Unqze7zDXBtLV1LBlYxB+5jC+Y5ok/Ip/yAwu5JWdYN5ojBPZngQ/IlQho8VXLjigKF6jnmp2BEGkhK4DpCGMw3xi6aWGp1XlJGloedtu3L9ok9r626GLg+3x4LArn2cpGkxGE1la0IznU8baTuyqGMzIf1S2UDdyapVsWPQBI80cjdYclgMAdEetU0B6FhFZ1N8k/E1mMuuqEpVpapsDDaNgH0usm5DvZ2BsDr6KlWwqjcIyV7kZhSL8VacnTsi7sJJUqqsv34bdAKeUIS9cfC4LqXQZNBRW8woBf2IN94iotLlGZSlUNIRNYUQZWOpKMmP4hTap7dDKUkL1SfCp7XjIqu1jYnwgdfWiUHPkhqEl2gcZa7QV8mVBW9LVxBHMDbGPJZnwxlY+MZHAZ+g6oi5pI/9bK9u5P8SFxJexe7TpwtoDoQ3CshEphiS9SDJkLEVXxo4ibUeDFkJUWRMrCo7aLBCgw2ND8IbBUlFgiiKjUJdpRJER3uiyoxYcsZYaQGJrfXNwq7817UhJ13m8Wm1hxYC0hheakU2Vd5sjtsZJCpdOGWxRRd4Ot12Nbrsahp9VE5hRFJ2049JsjstWnRwJXRzjjBuatvpvcNpInJF0nCqxMdE6xlZ6Wr8UDWWJCs0Y1b/jDJHksuvRDbx9lb83sSizt2dzRijTTNGtN7pa5iURTIvY0bFnQJssXHkSbspMRqpyjbioyJYSPp/3qQ+c79XDZmhxTceMDoBHnHSNjJa6pGtySRkjuZw0ybo7g9pEHlV25JRue0aDJlkHK4p9SezsoNmHC1JJEbyy3QhUFOl6mzNG1EWjK1XuYWa9HWIbaNmAeBdeloG+pEqrkxmrEOVgvYasbBWFvrGVgRT6Z16wJ4OMKihKNE0+403hNcF5OMr80WG2z4nhzr5ac7ATCcIumAxbV2TZo8gmc5tqqBbGPjSgHdtA8+LgPKTQbwE1Fj8O3marOxCq8naDBL7pumVsZHmZmlITGZtZ6lJLXVLuiSH0aVipZ20qIaqNrcLYFyl0MBz+MA8pnDZTRzmgXry9hxD1cDNJDC/ICDuZInKKRblvaAZ1lWRy7KEcqRC2XjA2VZIqrU5B7IsUukAqlxWp5LLD/PkJm4PW8xo1UQnZxAQYAt9E/Ql6AlLJThuPdD3d1LRlg7VTGdLNmnZKVLHnmVXorlWzDSKSIxsTXNedawxDDSDuVnrMxlZ8e8hTolklEbwWMqLVRkZLXUO8kaii2AdYNI3RkIcUFEWUg2ufxAmHsDeCaXC3UQeC/Ip7T1Kx8MIBlvwpNarDXs1drVfzsU8UOlPmhZTDkl5CSpEHXA6DGJWCxvkWSKV4mlsi1WeZ/CWVX3EbKdVzBZ+5jK4fSs1eqOItCbF2qJVXq4mohSgsIKNMDyugKDwaPCBxcwaofzIl1g4Hoi605qAARLYbUSmsnisaBgod7XdGqQIkSp+9Qs9DymU/ZqwQP0uv7tbmkxlZYmsnV/uWT0C092COTeHQOvqlZMudXvKL3XHUFu4Z84X/UiESlXRHQkom2TYKy9vTPjxuB2RtrBicHlYVtT6RTtPL3wkkAjpNHmF5jZ+XXxJDTWGJ3VA9hzWM+/lwiYQeG15x0+fZdthvxx7nwfvG+zwPFzzKKwfccgSXSEh1U+XKT40NzfxTEOE1h5nU44NaRSOCfbpe2FccwsaS4vDvQVkgv/KNnyQoKsdbaHRFvB4fzF3GkrK933yE32Jqjb59qfcHUySwQEO3s8kLlsYSrFG76Hmw2BpeiLL4+5iyQkrAMTuRl5vSRVs1QV4nhydo3zXpnwzPWMWYTEJCjMvTmLx0qVUrsPWaNfbLQvnFDbPasUI/VvXUucXdPvd86wFMKMV6eSffeGqPS8V6CjgVTqgTl7t98H9hwQUAAu/PKTzP8decuLD7AiZA68ql3T6Ym4/VubFGCCeHFbaeFVIykpGzmqUOu8g4xNiOhMjBJSfdAB0QTK9lTxJhcqCkwNmaSyVCBLCacJ62qa1rsNu1X78ei23M7zrfmmnd8lu3Fz4/zEjGZFHABa+AA9P0uAWsU4VXwGHlfkwM1CWH8p+7/+w+HIB36WB9W7z0pvA8WJMNU752O2R3sNqVP35V4XmwSCkOAN1iiRHcQvd8qO5Uec5b1h6Yx5OkTTP13uQkiwBOjg/AEg/hRVzp8AKoCP2sx9KrosZatViN0RqOHJpGtVufA7B0OdBdDtq1x+XNvWkOgNAQ1htYugIM31tZuHXe3MEaDbKoX3dg9fPuLoTm675xkyy81h1YuPF8yJOkZ3Yiajpp88le5ISOmlfAq339IzvwnAntfYAsr8acs9fH4IMCC59P/8JnehymKwr1CSx8zn/qwh6HYT0jPaw3sERCfeeRHuc555Hz9LAewIrwFPnMiyqnQjDZz2ofchXdPV1HFmNtlXw+gtrtHuFSk0pZ+6UTxnYKh+u3FljXp8SwVMCC4l4SYPGNvb2BpZWSYbyrI83YQzlpmGt0B5M2LB5Wfbo+SCu2QW+iD2kIj+VU30zm1FhKYz2s0ZEAVroaKshJMsGcTuiTmVIkNYAknGFIlip1OJuqC86KHNTeTNY7oxkFYHVrhN7MhKaWdGpKVPygDloodPrMacXiwahDqwZ6FwevU8xkfTouIwIsC6ZuzITgoIl5/0jSzGD1wo7rFqerwbZw26BcZMJ9xe9ExLs01uxEM4szZbKSsSpuRbE4g0ZzodHQdCInkn5hJBJldgK9l6Jt9Y7rd0sJ9f4hDiS6z9R1+u+iLrN0SuUz3PymRQq0R4WxErdQKpoFjbN4j03SZmzWnAKBVO1MdntGqzcxtEVU/XxSqGGJxQkHtW2g4lELhT2kCBoTTSqJ+XxrK57FQsGU/NUottBR5CZbaV8B5a6T7rIo7FwMTcVmvzHUAAsLMXKZ68JN37TbJ7DwwhIuZly46fsmFgUWFi069srjepzngJMP1PfRbRWwNKIJjNZkItoiQQNxc07iBmCEFpb+PCSTDRMp9iDm0Ci4T9yttEunuDmgRquLrXQIYOFNNXzHX48NK/1j0bNFgYWhmF0O3HXRsylSewALqOKLx3tsWBoe7xzoB1hoCvFuurSVuAkKnTUT0jlbAh0pzBrxl5OjFwrVNWroTxD+KdXJcgyLmu94+/ceh+2JHw4+41AM82GZ/8IPUIVjsIa2fTV8fsObKTt7bQcViPfkdDsVRqBRWlNJX5sD67ybpY7rwQ9Y8x1ru3c71QnXngSOx/qRXEF+UWChWdA4aCLJ/hpC7cO8lC3uFpyMDE7ueSbtMW3Ht3rny5fQosoF1QH6ZrbeH8gmHMlXw3c7Gwaz8crd3qcCG+FV4cBft7PhPPgtFu/DAt2Llt/ghU29r2pbOy22qpcozRoejUSMmQnpvQgaeHDrVutakj2GDvKMreurKGlrzxPebgQUvo6Gw0sA9ZWn3T54wAheOBK1LovkpLXqGXd9UN9B3+2D950cetZhOLjb2TDmjX8PPPWgp159pjewzn7oXFRALHpVhdKCcEHricAIZN/Q5Xe6yoycwF2lwOrn6rfqI1pe3mPTrCT94mz3d+6BFfq7fbD+J47Z59j9EJj6ORuqErqd6mMvXqrvfn7XeYdf9pXiwLr5hlN0rdGFPTZd/IVLup3qkDMlXKISFSU6/XQgeQXLfKvfbtz/pzO5Wn0sEKx+WsK0+kEHAuXqBz0J+Yh+0KXCB9HTf4ST/UdaMP0sofbEU0QKhtfNL8nZIMzBanzZSR5Y8kaTO85c9Dyolka5Hwps+qEoNottK9uGtm3Z4BIW/IOQ+OAfkDws/+Aq5fb6NrwZUKPbkgDrtNtO10DWG1jyyrgHzulxHrzWUN9D3hewym0Vbpd++XJ90ksCLKSffQIL2q7HeZAB6GElsAY1FA4DsEYwFC6beG/1L95HAFijJ96X2G5YBs9rCEPhKNgNixukyQq7w6OssQbYIF2Cm+82pLNE4X8IgDWKQzp9UvdyDEKPGrBGdBD6jS+b6XPT109aYGHG8+HnH6kfHaLuf+NkskWBBZuqx3lYzLNVoXCEymZWsNCvXxU42bjma9dZYG3YeSMH7B7/3tNbdTbM1sfbeBcF1lOvPgsLFIOPmNqf/wDZ+NWFz1687RpraAv9lqU0eaYuVd5Lt3Y0Shsu/+qVSwIscVjWtG/85i09gCVT7A/eTabY/8SnikskfvAiihPRGng/+eLA0tLktMhdp0ss0izDUZq8jJMp1jWWUJziMfM9lNsIrEo6Qb7boiAYn5ZFQWpVlH/1KG0AnlCqCmydcvNpi4a/UZxMsYLTv173hmU/WHCHKLkcbXLRC5csWiUGYkNpYf+nHa3pX/3AbpkmrK7abd2O65kc9P6gmKcfBBSCbMgnrBYjaQmn2I+2VTPSU+xXalGQkQDWKC8Ksi3LGFly2tpljEZqK1zGqAeNVYZhGaPayiy8Ngp0tbwLr63+sLgiS0UO/TbqS0X21p4F5LREi9uOgnLfpsVtF6OxwdvemOW4RwZfo70c94q8QGCIwVS+QMBt5StPlpTyy1eeBKW5dC9pUvIvX9K0jC9pGpx8qHyt3BJaVpafRv21cr2E/Na/CLNZvggzuv2RfRHmoqq8fHVvn1v56t4ufa582fg29EbGrPJl45ktImQVUnlbz2lP3nxOoQNGJH9LVBjhEUprjw8fqgQxC23cYNAGnrqiyGh1vQ6jSfaTt5oRMXKpOh7QoLaOFVL5NFgaJdufcKtWodvYh3axKgF/qN10KFU8bkqbBbfPjkRxmY+MYCbxa8wobWExSCS/IvNikJw9dDvJ5nIqWygalkSnbZEXHWZ5Xt5b7FsJQ4dKYGjlUFE0PK91DE2kthP+HTO3SSsHjROxtX3BvY7k4OA8vBBbZeh66aYRrGQzZSFVt6pL4mOWzERVMPHJEpUorRSOwCuzaDFm5ltDoLdwC2r7UXeqv4Bbtuwi1OXjWhQZlZYyeZJKi7wvPTxdMa1Ti2ZJRArdxj5BD1rEo0da2bO99fRwjBLeMAArSdTek3KDxCSG3skL95i4eofCyBjp+pCDD8nK29bZW9uIEpO8xszkOFmiEv2e9lH0Xc6Kxp/rY8C/yl7YM6DwCleehJvivUiakrYMmsXyk0gxYzpEkTHS9WSvsaF56VdkN0j3yt5bavq1ColKI51LiNCs3vcSCZISfhiTxiNJF1cZPJLHklfp2jsuuKejzk5E+hAGNDDAMVY6Ip+dsFZ7VFaZFPXqIbFpeOdQTpFdHsW+NMfOOKLaxAoga+2o4yUJs0NVRTv6+PRAVgKOp0kuqRe349TVQtuadg0/pCN8VrV+adWOFcaRMaV8jqHhcQyPuipc2CPSAZF9Rf6PRBWQZ/R7xXZxNpnONB8IR4YR34nFQMAVKnfb2SitxGppZAwtS135yFjxC4oMc7GkzfscUVnYQZmSxjoZu4XemK2m1QeQdndH/mhWnHOVEz4uDxeJS3XCwHePxlxQ7sSK9dzRIFQX1ojJU1c+ZxzaLepYed62YsIGApqiEZkrqqQjpieB5nBD1NrLk8oqHKuWS0ocDjIXP+YuXsg4JyQsbYsYsI7D+Fj/LTyEWxT7hMyz8ZE2VSwjfFxwZjQbfa0TIjxSD5N+bLC4qtx5XeGCAlwNz4ZHEm5EKcdSMvl7wpAZk2Ll9WitGDRs1NRDOfblNluDlXFltJ+Z5EU4ybagHSuMunJnkmyvEpirLKMf67ptzD1XMmFMKrxOlQGOjBP3lWmHsJGKSEPMZOtm1rHLuFn5Jp1r2BkrwyutavKki8YixuwQqe18EhnnC9JsjmyQ8/CDjmOMecmiYUUfpwgaWbVmxRpXZodvcrIP96shzHWAmkMSRyAQx/P0TKdU3Czj14ju7Bi1Go2MaVVIZ3LY3NE4GjZrUVbCcft8u1TrITEUjz7XTQnBsVSw24FVPYadXqOJPjl1Dt+A5EjHrxRMtRT3BIrSLQlY9LiW8/vHL+5djqQDtrKF/9JQC6E8xLaVXsZovc4kXe2paVMY+7DlSXQKOIxBgS0umnc+o3k1CJIhklomJlrhtRwvuxMbPb0R1eaMcbp2qEJHGZSEGvSiGfQkK1uqJuAkXzZwSQetmzbwjegcTLGjNmU8PSuAQvNlUWUHzmyUUV/ej4q4IMg/VKnhyCytuZCxfcNk+gy2aXH5pMKTWGZy/1cSaIlXrvfIgChQm/djCdnsJCJsW4+UIaekkime2dQeuWUvxupVmw9jZlKhUx8tVkG0pS+ay4ZFI+G1o1vNbic56VMhB+irDBxA04mQVGxhUDKJi13zx+gSOmSmANyUHXkXyqyRimcOK5fayYY/mFueh2IryyY6GWmRWZdlpKbKBfRE9rGM93WpRGM3tcFChYvtuIQsG1QfTHi0KZmpJ+kyf/9cBRw+dcJhBJy8YoQ/b0xfwZI7RtZc9KBUZLtQWxuzKNe7kI7hs5ZomUZSMu+dgT7GlpEKdryrYuoEB3tRhtcdO6JCDpa25QUEO6iVtyqkMrEjcZTATqzIYzBS0UP0KLj1ocrZTAhLJ382STMBTOl7bEII9u+f0ZCqRqXildegJovrPwjE6RXy7lwHQ2/xROhMmc5kGHH3qUlE4ZI8dooVqlaUlOsShHFlbVNrnJKWZL/nISoSUei+QfXpUrMTZ3wYqsaC2JI83GFOI4j+SmmGyEjX6m0yeJGZFBNKfgp9xyg4c0fGaqy0IkyVtHDxQVqxCjnrkdpRKUbJiMilq5icerWvn/bGm9F2MoUNjlVD+KFBk+C1Up9yzWbCSLFCulJO4vNWr4jCSAsHHOU0Mua4JTn9fzPZZSMQXnS2wFKaAI6PMXuwcY3akTeuF6AFjGSd0MG2y9C5tF4nLPOXkRajvKkXENrFDL/Y7mjTHzN8NkFxpuTEmEUmCLmhWkd0ktIEiihUFzHDXumvdOXPCFjOmkq5h5mHGhyWlihxlEpJgYqkwKxphI0CYphBWeSRxt3PYEvKrWolb2UbOiqPJKps4kMVb2v6CLUoNyRdsd6QAykKOw2jyjeMd057pVyoaxQykGkwVZ2nnMdrcwM1KS2p6dDycdaJ9I5fn9eTlskHx+I79aZJpKts3YfFFs7gOtLUeIkog63s+qopqtr56W9BanSCimcQ5FMhC7KVNWyF7CwNK/yqyCY3KC0pBDXNjIGVwlfREKUCJEiFBWOWfmV4daaAYRf2DcKFQz2CIUPkwVXOErwszzdVL7HUy+UisUuyw/kUXtXaTqxVTVFkcSVy2JMkhbLGF0HUrSTKhEUf71wiWSSeHC1lwx+PVMqM7FAqPMTuyBFg7LaaPZgspoKD49YjuKjTtqWHm9q9W7OwXImwoJXF7usej6/1BtqUz9jd/ejKGEUVfYTILs+Y+P5txXZoOVisyqP8T30sNrlFPQrfoYxxdiJvvtvQb/tYiq1SVPURE0N7meBIAFmDlFUA1OwMZBlpnK2ecyO+nUy2SOWugKB1rkwTAYvMp0/XATRxOAvZXyczLu5qEufj9MIWtNDJY7ZLYyUyPG0PHNBK/5XQ8lllaocpxqyV5Xtt6PT04n1bM3XnCdXcb+TH7IrMcQU6n6jyRxhXLrL1CTuNm9SI+SvhgDqlGFV81sQaM9nxeJTflLpq6zyITPMVjbkyRmQyKbqmYxkjmwyUD0n6+KMCG1KU+5oFVvAs5hr5YhhCNgrBZDimAiSt5nxBhmv5iURucRk1VLltHcJI+DZkVJthkIePLV/D5MKcSceoY6jeIrtLc67I7iLs9KlHBhWBEhS6V0KR/iNFMcrna8uItsgIpSdsx7XK7XVupCU7dGODYCY91OfRMekh/S3/JPTxhNqH8XgY2OWPnho1bkbAYnRTAd7ysonkSty4WbW+S/DNHcEZwWV34qw2JIP2rqsBcCM5urzElOXtStN3WcptNTu7Pl1HRj0+V1IIKY0kR2MiGj5ylS1rMoGy4RUYR8edDZskEf3wqogPjjGHIc6Zgnk4TIEbJgVWlVaOLi+lsxWcd1OKFIQ8x4XsA5uOS1DCqJznp0Y2U8sTmIq2CFjNHhSlxXoeDTyAUOP1h1VlqgkvPtRu1McKy4dK12p5+Mvoj0KzJ4ys+UGb4En62iy7J3Ip+dT5gF3pwUzdMhBVFENb8BTWNuwBvIwgEDMXFo8MZibizk7kA2K5LcvG6jarSEI9qqGrvNnIOJgpjd+uYOQnDW2JtUAjYLHAoeJjnxmrieU5A3eY/JgzdQNpRX6KU42TozUbYmXCYk3Wt7DjsoXLXXAEN8w6ZHFme7xbZORYUFBpWdwQecFB2NS2YC2IfR7ihC97gn0rBNFmk195Iej6ZpkDrsBWNWWANHWCI7+xlYda0NHkkqxHz79KfKSjI0o/iSeJ/2Qs5kUypU01eDHBYTekVS35acW3sN6roStymJ14mIcaVzPjnqjW2cTKrpHO1Rr4sUU5rTdpC+iT0wb9WILlp7De08ZW+WRXWs77FSgztfC5FcbGc7MReViQXDmTKcS1qfEIENHyQxY0FFUFLuh0PbJC7GE6Uzy/MF25rWS2mM8QbRFECD2cD+MFlhlVjI2AMPKYKyOmmqbqYlAjiPlXBGhGVHn82UxwBF9BNTCGKk15PmmSU8Z68M81ACKn7oPuzgMrlwEwXBKgFtl0SchPPGzAV8YeJWxJMmXqThmJCp903gWgdu4FNcLIs1re17AyKyzDZOx1eb1gu3x57OAK/Pm4eDxw2IKR2H4UhXVzHGzJAytoNW+Rs9KmUgmsSX6yE27LJzIslkS9CojI4g7VuA7CElsPDqOxTmu+Nz+xLD3ULFRlHhEuo1qOyQzxFoaA7ICdH5sL+PDxKw8sOzbMP8wPU5ZlCCO3IbdHeApS2iduNjjSD2MOyDzR+k8hXfApJ05rT15uI21Y6CLeduENXYavbV6Hkc7MbkeLA+JPdGHt0iwot3Jb5m1qaqpshHJb2m1mZqayefPmsiHKbWm3008/vfLSSy/Nzs6WbVFuS7XNzc298sorlddee+3ll1/esmXL9PR02Sjlti0bIASuAqoAqv8Hq3jne/3ojC4AAAAASUVORK5CYII=", - "description": "Preconfigured gauge to display any value reading as a doughnut. Allows to configure value range, gradient colors and other settings.", - "descriptor": { - "type": "latest", - "sizeX": 3, - "sizeY": 3, - "resources": [], - "templateHtml": "", - "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", - "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-digital-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#388e3c\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":1,\"levelColors\":[],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":40,\"dashThickness\":1.5,\"gaugeType\":\"donut\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Simple neon gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "vertical_bar_justgage", - "name": "Vertical bar", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAABQVBMVEVqampra2tsbGxtbW1ubm5vb29wcHBycnJzc3N0dHR2dnZ3d3d5eXl6enp7e3t8fHx+fn6BgYGDg4OFhYWHh4eIiIiKioqNjY2Ojo6RkZGTk5OWlpaZmZmbm5uenp6fn5+goKCkpKSlpaWmpqanp6eoqKiqqqqrq6usrKytra2xsbGysrK0tLS1tbW2tra4uLi6urq7u7u9vb2+vr7AwMDBwcHExMTFxcXHx8fIyMjJycnKysrMzMzNzc3Ozs7Pz8/Q0NDR0dHS0tLT09PU1NTV1dXX19fY2NjZ2dnb29vc3Nzd3d3f39/g4ODh4eHj4+Pk5OTl5eXm5ubn5+fo6Ojp6enq6urr6+vs7Ozt7e3u7u7v7+/w8PDx8fHy8vLz8/P09PT1fAD19fX29vb39/f6+vr7+/v8/Pz9/f3+/v7///8uFkw3AAAAAWJLR0RqJWKVDgAAAkBJREFUeNrt3ElTE3EQhvGebKMIiUExGkSNuCEiKAqKRKJsIuISN0giwQFC5v3+H8BD5uDBE2WV9tTTp1x/lTyd/6lNKRkDAgQIECBA/jtIHEXx4FOv3fMMWTSLJOn9eN4+OoZ0zg4gjZxduvXDMWQim7FIaoe5hutGXtn90CJp2mZcx340cv44tEgas9kLufKyW8ht21BokVSwTPWKBdtOIdvBDSm0SH2zFWnaxn1CeqPB03o9b88+9C2IpV0r+YTsWDJ3NWQdqWNDPiGH9Xp98I3ous1Ji1b1/NYKLZI+FzKTd3LBG/cQbZXNii9T8frttGKe8UCAAJGaS5J6j2/O96TuzOTz2CfkxZhVJF0tTY1MqF++eK/w0Cdkar5akdrBa60EnTXb04NzXn9a1yrSln1V094+yUvLduQYsmp7+m5rs2ekhrUdQ9ZtV99sYy6UGrbvGPLJdvTOmkvBsRay8T+CtE45v0NOirVurdRvZR/tX67KMUSbwza8KS2ENvrFKySZA0lSfMgTBUjqIH+pESBAiB0IjQBhawEBQuxAaAQIWwsIEGIHQiNA2FpAgBA7EBoBwtYCAoTYgdAIELYWECDEDoRGgKQK8vOUAwQIsQOhESBsLWIHQuxAaAQIW4vYgRA7EBoBwtYidiDEDoRGgLC1iB0IsQOhESBsrXTHnlws9g8ZXCz230hysdg/JLlY7B+SXCz230hysdg/JLlY7B9yUqx1a8V+CtZvcrE4Df/sBzxRgAABAgQIECB/mF+DKtRemhqPQgAAAABJRU5ErkJggg==", - "description": "Preconfigured gauge to display any value reading as a bar. Allows to configure value range, gradient colors and other settings.", - "descriptor": { - "type": "latest", - "sizeX": 2, - "sizeY": 3.5, - "resources": [], - "templateHtml": "", - "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", - "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-digital-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#f57c00\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#999999\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":12,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#666666\"},\"neonGlowBrightness\":0,\"decimals\":0,\"dashThickness\":1.5,\"gaugeColor\":\"#eeeeee\",\"showTitle\":false,\"gaugeType\":\"verticalBar\"},\"title\":\"Vertical bar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" - } - }, - { - "alias": "simple_gauge_justgage", - "name": "Simple gauge", - "image": "data:image/png;base64,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", - "description": "Preconfigured gauge to display any value reading as a circle. Allows to configure value range, gradient colors and other settings.", - "descriptor": { - "type": "latest", - "sizeX": 2, - "sizeY": 2, - "resources": [], - "templateHtml": "\n", - "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", - "controllerScript": "\nself.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-digital-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#ef6c00\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":0,\"decimals\":0,\"gaugeColor\":\"#eeeeee\",\"gaugeType\":\"donut\"},\"title\":\"Simple gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" - } - }, - { - "alias": "digital_bar", - "name": "Digital horizontal bar", - "image": "data:image/png;base64,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", - "description": "Preconfigured gauge to display any value reading as a horizontal bar. Allows to configure value range, gradient colors and other settings.", - "descriptor": { - "type": "latest", - "sizeX": 6, - "sizeY": 2.5, - "resources": [], - "templateHtml": "", - "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", - "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-digital-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 80) {\\n\\tvalue = 80;\\n} else if (value > 160) {\\n\\tvalue = 160;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":180,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[\"#008000\",\"#fbc02d\",\"#f44336\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":18},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#ffffff\"},\"neonGlowBrightness\":40,\"dashThickness\":1.5,\"unitTitle\":\"MPH\",\"showUnitTitle\":true,\"gaugeColor\":\"#171a1c\",\"gaugeType\":\"horizontalBar\",\"showTitle\":false,\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital horizontal bar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "mini_gauge_justgage", - "name": "Mini gauge", - "image": "data:image/png;base64,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", - "description": "Preconfigured gauge to display any value reading as a circle. Allows to configure value range, gradient colors and other settings.", - "descriptor": { - "type": "latest", - "sizeX": 2, - "sizeY": 2, - "resources": [], - "templateHtml": "", - "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", - "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-digital-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#7cb342\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":0,\"decimals\":0,\"roundedLineCap\":true,\"gaugeType\":\"donut\"},\"title\":\"Mini gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" - } - }, - { - "alias": "horizontal_bar_justgage", - "name": "Horizontal bar", - "image": "data:image/png;base64,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", - "description": "Preconfigured gauge to display any value reading as a horizontal bar. Allows to configure value range, gradient colors and other settings.", - "descriptor": { - "type": "latest", - "sizeX": 7, - "sizeY": 3, - "resources": [], - "templateHtml": "\n", - "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", - "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-digital-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#999999\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":18,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#666666\"},\"neonGlowBrightness\":0,\"decimals\":0,\"dashThickness\":0,\"gaugeColor\":\"#eeeeee\",\"showTitle\":true,\"gaugeType\":\"horizontalBar\"},\"title\":\"Horizontal bar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" - } - }, - { - "alias": "digital_vertical_bar", - "name": "Digital vertical bar", - "image": "data:image/png;base64,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", - "description": "Preconfigured gauge to display any value reading as a vertical bar. Allows to configure value range, gradient colors and other settings.", - "descriptor": { - "type": "latest", - "sizeX": 2.5, - "sizeY": 4.5, - "resources": [], - "templateHtml": "", - "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", - "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n", - "settingsSchema": "{}", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-digital-gauge-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":60,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[\"#3d5afe\",\"#f44336\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":14},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":8,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#cccccc\"},\"neonGlowBrightness\":20,\"showUnitTitle\":true,\"gaugeColor\":\"#171a1c\",\"gaugeType\":\"verticalBar\",\"showTitle\":false,\"minValue\":-60,\"dashThickness\":1.2,\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital vertical bar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - } + "widgetTypeFqns": [ + "digital_gauges.simple_gauge_justgage", + "digital_gauges.vertical_bar_justgage", + "digital_gauges.horizontal_bar_justgage", + "digital_gauges.gauge_justgage", + "digital_gauges.mini_gauge_justgage", + "digital_gauges.digital_thermometer", + "digital_gauges.digital_speedometer", + "digital_gauges.digital_vertical_bar", + "digital_gauges.digital_bar", + "digital_gauges.simple_neon_gauge_justgage", + "digital_gauges.neon_gauge_justgage", + "digital_gauges.lcd_gauge", + "digital_gauges.lcd_bar_gauge" ] } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/edge_widgets.json b/application/src/main/data/json/system/widget_bundles/edge_widgets.json index 5d9c9526fa..158a856eb9 100644 --- a/application/src/main/data/json/system/widget_bundles/edge_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/edge_widgets.json @@ -2,28 +2,13 @@ "widgetsBundle": { "alias": "edge_widgets", "title": "Edge widgets", - "image": "data:image/png;base64,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", - "description": "Widgets to manage ThingsBoard Edge." + "image": "data:image/png;base64,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", + "description": "Widgets to manage ThingsBoard Edge instances and navigate through their entities.", + "order": 16000, + "externalId": null, + "name": "Edge widgets" }, - "widgetTypes": [ - { - "alias": "edges_overview", - "name": "Edge Quick Overview", - "image": "data:image/png;base64,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", - "description": "Overview of entities related to ThingsBoard Edge.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 5, - "resources": [], - "templateHtml": "\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n dataKeysOptional: true\n };\n}\n\nself.onDestroy = function() {\n};\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-edge-quick-overview-widget-settings", - "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"showTitleIcon\":true,\"titleIcon\":\"router\",\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{},\"title\":\"Edge Quick Overview\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"widgetStyle\":{},\"actions\":{}}" - } - } + "widgetTypeFqns": [ + "edge_widgets.edges_overview" ] } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/entity_admin_widgets.json b/application/src/main/data/json/system/widget_bundles/entity_admin_widgets.json index 3348c86d89..fc4779c522 100644 --- a/application/src/main/data/json/system/widget_bundles/entity_admin_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/entity_admin_widgets.json @@ -3,48 +3,13 @@ "alias": "entity_admin_widgets", "title": "Entity admin widgets", "image": "data:image/png;base64,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", - "description": "Templates of complex widgets that allow to list and create/update/delete devices and assets." + "description": "Templates of complex widgets that allow to list and create/update/delete devices and assets.", + "order": 13000, + "externalId": null, + "name": "Entity admin widgets" }, - "widgetTypes": [ - { - "alias": "device_admin_table", - "name": "Device admin table", - "image": "data:image/png;base64,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", - "description": "Customized entity table widget with preconfigured actions to create, update and delete devices.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 6.5, - "resources": [], - "templateHtml": "\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.entitiesTableWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n hasDataPageLink: true,\n warnOnPageDataOverflow: false,\n dataKeysOptional: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true,\n hasShowCondition: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n },\n 'rowDoubleClick': {\n name: 'widget-action.row-double-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-entities-table-widget-settings", - "dataKeySettingsDirective": "tb-entities-table-key-settings", - "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSearch\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"entityName\",\"displayEntityName\":true,\"displayEntityType\":true,\"entitiesTitle\":\"Device admin table\",\"enableSelectColumnDisplay\":true,\"enableStickyHeader\":true,\"enableStickyAction\":true,\"reserveSpaceForHiddenAction\":\"true\",\"displayEntityLabel\":false,\"useRowStyleFunction\":false},\"title\":\"Device admin table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{\"headerButton\":[{\"name\":\"Add device\",\"icon\":\"add\",\"type\":\"customPretty\",\"customHtml\":\"
\\n \\n

Add device

\\n \\n \\n
\\n \\n \\n
\\n
\\n
\\n \\n Device name\\n \\n \\n Device name is required.\\n \\n \\n
\\n \\n \\n Label\\n \\n \\n
\\n
\\n \\n Latitude\\n \\n \\n \\n Longitude\\n \\n \\n
\\n
\\n
\\n
\\n \\n \\n \\n
\\n
\\n\",\"customCss\":\"\",\"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\\nopenAddDeviceDialog();\\n\\nfunction openAddDeviceDialog() {\\n customDialog.customDialog(htmlTemplate, AddDeviceDialogController).subscribe();\\n}\\n\\nfunction AddDeviceDialogController(instance) {\\n let vm = instance;\\n \\n vm.addDeviceFormGroup = vm.fb.group({\\n deviceName: ['', [vm.validators.required]],\\n deviceType: ['', [vm.validators.required]],\\n deviceLabel: [''],\\n attributes: vm.fb.group({\\n latitude: [null],\\n longitude: [null]\\n }) \\n });\\n \\n vm.cancel = function() {\\n vm.dialogRef.close(null);\\n };\\n \\n vm.save = function() {\\n vm.addDeviceFormGroup.markAsPristine();\\n let device = {\\n name: vm.addDeviceFormGroup.get('deviceName').value,\\n type: vm.addDeviceFormGroup.get('deviceType').value,\\n label: vm.addDeviceFormGroup.get('deviceLabel').value\\n };\\n deviceService.saveDevice(device).subscribe(\\n function (device) {\\n saveAttributes(device.id).subscribe(\\n function () {\\n widgetContext.updateAliases();\\n vm.dialogRef.close(null);\\n }\\n );\\n }\\n );\\n };\\n \\n function saveAttributes(entityId) {\\n let attributes = vm.addDeviceFormGroup.get('attributes').value;\\n let attributesArray = [];\\n for (let key in attributes) {\\n attributesArray.push({key: key, value: attributes[key]});\\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\":\"70837a9d-c3de-a9a7-03c5-dccd14998758\"}],\"actionCellButton\":[{\"name\":\"Edit device\",\"icon\":\"edit\",\"useShowWidgetActionFunction\":null,\"showWidgetActionFunction\":\"return true;\",\"type\":\"customPretty\",\"customHtml\":\"
\\n \\n

Edit device

\\n \\n \\n
\\n \\n \\n
\\n
\\n
\\n \\n Device name\\n \\n \\n Device name is required.\\n \\n \\n
\\n \\n \\n Label\\n \\n \\n
\\n
\\n \\n Latitude\\n \\n \\n \\n Longitude\\n \\n \\n
\\n
\\n
\\n
\\n \\n \\n \\n
\\n
\\n\",\"customCss\":\"\",\"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\\nopenEditDeviceDialog();\\n\\nfunction openEditDeviceDialog() {\\n customDialog.customDialog(htmlTemplate, EditDeviceDialogController).subscribe();\\n}\\n\\nfunction EditDeviceDialogController(instance) {\\n let vm = instance;\\n \\n vm.device = null;\\n vm.attributes = {};\\n \\n vm.editDeviceFormGroup = vm.fb.group({\\n deviceName: ['', [vm.validators.required]],\\n deviceType: ['', [vm.validators.required]],\\n deviceLabel: [''],\\n attributes: vm.fb.group({\\n latitude: [null],\\n longitude: [null]\\n }) \\n });\\n \\n vm.cancel = function() {\\n vm.dialogRef.close(null);\\n };\\n \\n vm.save = function() {\\n vm.editDeviceFormGroup.markAsPristine();\\n if (vm.editDeviceFormGroup.get('deviceType').value !== vm.device.type) {\\n delete vm.device.deviceProfileId;\\n }\\n vm.device.name = vm.editDeviceFormGroup.get('deviceName').value,\\n vm.device.type = vm.editDeviceFormGroup.get('deviceType').value,\\n vm.device.label = vm.editDeviceFormGroup.get('deviceLabel').value\\n deviceService.saveDevice(vm.device).subscribe(\\n function () {\\n saveAttributes().subscribe(\\n function () {\\n widgetContext.updateAliases();\\n vm.dialogRef.close(null);\\n }\\n );\\n }\\n );\\n };\\n \\n getEntityInfo();\\n \\n function getEntityInfo() {\\n deviceService.getDevice(entityId.id).subscribe(\\n function (device) {\\n attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE',\\n ['latitude', 'longitude']).subscribe(\\n function (attributes) {\\n for (let i = 0; i < attributes.length; i++) {\\n vm.attributes[attributes[i].key] = attributes[i].value; \\n }\\n vm.device = device;\\n vm.editDeviceFormGroup.patchValue(\\n {\\n deviceName: vm.device.name,\\n deviceType: vm.device.type,\\n deviceLabel: vm.device.label,\\n attributes: {\\n latitude: vm.attributes.latitude,\\n longitude: vm.attributes.longitude\\n }\\n }, {emitEvent: false}\\n );\\n } \\n );\\n }\\n ); \\n }\\n \\n function saveAttributes() {\\n let attributes = vm.editDeviceFormGroup.get('attributes').value;\\n let attributesArray = [];\\n for (let key in attributes) {\\n attributesArray.push({key: key, value: attributes[key]});\\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\":[],\"openInSeparateDialog\":false,\"openInPopover\":false,\"id\":\"93931e52-5d7c-903e-67aa-b9435df44ff4\"},{\"name\":\"Delete device\",\"icon\":\"delete\",\"type\":\"custom\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet dialogs = $injector.get(widgetContext.servicesMap.get('dialogs'));\\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\\n\\nopenDeleteDeviceDialog();\\n\\nfunction openDeleteDeviceDialog() {\\n let title = \\\"Are you sure you want to delete the device \\\" + entityName + \\\"?\\\";\\n let content = \\\"Be careful, after the confirmation, the device and all related data will become unrecoverable!\\\";\\n dialogs.confirm(title, content, 'Cancel', 'Delete').subscribe(\\n function (result) {\\n if (result) {\\n deleteDevice();\\n }\\n }\\n );\\n}\\n\\nfunction deleteDevice() {\\n deviceService.deleteDevice(entityId.id).subscribe(\\n function () {\\n widgetContext.updateAliases();\\n }\\n );\\n}\\n\",\"id\":\"ec2708f6-9ff0-186b-e4fc-7635ebfa3074\"}]}}" - } - }, - { - "alias": "asset_admin_table", - "name": "Asset admin table", - "image": "data:image/png;base64,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", - "description": "Customized entity table widget with preconfigured actions to create, update and delete assets.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 6.5, - "resources": [], - "templateHtml": "\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.entitiesTableWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n hasDataPageLink: true,\n warnOnPageDataOverflow: false,\n dataKeysOptional: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true,\n hasShowCondition: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n },\n 'rowDoubleClick': {\n name: 'widget-action.row-double-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-entities-table-widget-settings", - "dataKeySettingsDirective": "tb-entities-table-key-settings", - "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSearch\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"entityName\",\"displayEntityName\":true,\"displayEntityType\":true,\"entitiesTitle\":\"Asset admin table\",\"enableSelectColumnDisplay\":true},\"title\":\"Asset admin table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{\"headerButton\":[{\"name\":\"Add asset\",\"icon\":\"add\",\"type\":\"customPretty\",\"customHtml\":\"
\\n \\n

Add asset

\\n \\n \\n
\\n \\n \\n
\\n
\\n
\\n \\n Asset name\\n \\n \\n Asset name is required.\\n \\n \\n
\\n \\n \\n Label\\n \\n \\n
\\n
\\n \\n Latitude\\n \\n \\n \\n Longitude\\n \\n \\n
\\n
\\n
\\n
\\n \\n \\n \\n
\\n
\\n\",\"customCss\":\"\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\\nlet assetService = $injector.get(widgetContext.servicesMap.get('assetService'));\\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\\n\\nopenAddAssetDialog();\\n\\nfunction openAddAssetDialog() {\\n customDialog.customDialog(htmlTemplate, AddAssetDialogController).subscribe();\\n}\\n\\nfunction AddAssetDialogController(instance) {\\n let vm = instance;\\n \\n vm.addAssetFormGroup = vm.fb.group({\\n assetName: ['', [vm.validators.required]],\\n assetType: ['', [vm.validators.required]],\\n assetLabel: [''],\\n attributes: vm.fb.group({\\n latitude: [null],\\n longitude: [null]\\n }) \\n });\\n \\n vm.cancel = function() {\\n vm.dialogRef.close(null);\\n };\\n \\n vm.save = function() {\\n vm.addAssetFormGroup.markAsPristine();\\n let asset = {\\n name: vm.addAssetFormGroup.get('assetName').value,\\n type: vm.addAssetFormGroup.get('assetType').value,\\n label: vm.addAssetFormGroup.get('assetLabel').value\\n };\\n assetService.saveAsset(asset).subscribe(\\n function (asset) {\\n saveAttributes(asset.id).subscribe(\\n function () {\\n widgetContext.updateAliases();\\n vm.dialogRef.close(null);\\n }\\n );\\n }\\n );\\n };\\n \\n function saveAttributes(entityId) {\\n let attributes = vm.addAssetFormGroup.get('attributes').value;\\n let attributesArray = [];\\n for (let key in attributes) {\\n attributesArray.push({key: key, value: attributes[key]});\\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\":\"70837a9d-c3de-a9a7-03c5-dccd14998758\"}],\"actionCellButton\":[{\"name\":\"Edit asset\",\"icon\":\"edit\",\"type\":\"customPretty\",\"customHtml\":\"
\\n \\n

Edit asset

\\n \\n \\n
\\n \\n \\n
\\n
\\n
\\n \\n Asset name\\n \\n \\n Asset name is required.\\n \\n \\n
\\n \\n \\n Label\\n \\n \\n
\\n
\\n \\n Latitude\\n \\n \\n \\n Longitude\\n \\n \\n
\\n
\\n
\\n
\\n \\n \\n \\n
\\n
\\n\",\"customCss\":\"\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\\nlet assetService = $injector.get(widgetContext.servicesMap.get('assetService'));\\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\\n\\nopenEditAssetDialog();\\n\\nfunction openEditAssetDialog() {\\n customDialog.customDialog(htmlTemplate, EditAssetDialogController).subscribe();\\n}\\n\\nfunction EditAssetDialogController(instance) {\\n let vm = instance;\\n \\n vm.asset = null;\\n vm.attributes = {};\\n \\n vm.editAssetFormGroup = vm.fb.group({\\n assetName: ['', [vm.validators.required]],\\n assetType: ['', [vm.validators.required]],\\n assetLabel: [''],\\n attributes: vm.fb.group({\\n latitude: [null],\\n longitude: [null]\\n }) \\n });\\n \\n vm.cancel = function() {\\n vm.dialogRef.close(null);\\n };\\n \\n vm.save = function() {\\n vm.editAssetFormGroup.markAsPristine();\\n vm.asset.name = vm.editAssetFormGroup.get('assetName').value,\\n vm.asset.type = vm.editAssetFormGroup.get('assetType').value,\\n vm.asset.label = vm.editAssetFormGroup.get('assetLabel').value\\n assetService.saveAsset(vm.asset).subscribe(\\n function () {\\n saveAttributes().subscribe(\\n function () {\\n widgetContext.updateAliases();\\n vm.dialogRef.close(null);\\n }\\n );\\n }\\n );\\n };\\n \\n getEntityInfo();\\n \\n function getEntityInfo() {\\n assetService.getAsset(entityId.id).subscribe(\\n function (asset) {\\n attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE',\\n ['latitude', 'longitude']).subscribe(\\n function (attributes) {\\n for (let i = 0; i < attributes.length; i++) {\\n vm.attributes[attributes[i].key] = attributes[i].value; \\n }\\n vm.asset = asset;\\n vm.editAssetFormGroup.patchValue(\\n {\\n assetName: vm.asset.name,\\n assetType: vm.asset.type,\\n assetLabel: vm.asset.label,\\n attributes: {\\n latitude: vm.attributes.latitude,\\n longitude: vm.attributes.longitude\\n }\\n }, {emitEvent: false}\\n );\\n } \\n );\\n }\\n ); \\n }\\n \\n function saveAttributes() {\\n let attributes = vm.editAssetFormGroup.get('attributes').value;\\n let attributesArray = [];\\n for (let key in attributes) {\\n attributesArray.push({key: key, value: attributes[key]});\\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\":\"93931e52-5d7c-903e-67aa-b9435df44ff4\"},{\"name\":\"Delete asset\",\"icon\":\"delete\",\"type\":\"custom\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet dialogs = $injector.get(widgetContext.servicesMap.get('dialogs'));\\nlet assetService = $injector.get(widgetContext.servicesMap.get('assetService'));\\n\\nopenDeleteAssetDialog();\\n\\nfunction openDeleteAssetDialog() {\\n let title = \\\"Are you sure you want to delete the asset \\\" + entityName + \\\"?\\\";\\n let content = \\\"Be careful, after the confirmation, the asset and all related data will become unrecoverable!\\\";\\n dialogs.confirm(title, content, 'Cancel', 'Delete').subscribe(\\n function (result) {\\n if (result) {\\n deleteAsset();\\n }\\n }\\n );\\n}\\n\\nfunction deleteAsset() {\\n assetService.deleteAsset(entityId.id).subscribe(\\n function () {\\n widgetContext.updateAliases();\\n }\\n );\\n}\\n\",\"id\":\"ec2708f6-9ff0-186b-e4fc-7635ebfa3074\"}]}}" - } - } + "widgetTypeFqns": [ + "entity_admin_widgets.device_admin_table", + "entity_admin_widgets.asset_admin_table" ] } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/entity_widgets.json b/application/src/main/data/json/system/widget_bundles/entity_widgets.json new file mode 100644 index 0000000000..13194d18b3 --- /dev/null +++ b/application/src/main/data/json/system/widget_bundles/entity_widgets.json @@ -0,0 +1,16 @@ +{ + "widgetsBundle": { + "alias": "entity_widgets", + "title": "Entity widgets", + "image": "data:image/png;base64,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", + "description": "Visualize entity properties and hierarchy using table and tree widgets.", + "order": 17000, + "externalId": null, + "name": "Entity widgets" + }, + "widgetTypeFqns": [ + "cards.entities_table", + "entity_count", + "cards.entities_hierarchy" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/gateway_widgets.json b/application/src/main/data/json/system/widget_bundles/gateway_widgets.json index dc86c0f003..bf48fd1c4f 100644 --- a/application/src/main/data/json/system/widget_bundles/gateway_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/gateway_widgets.json @@ -3,65 +3,20 @@ "alias": "gateway_widgets", "title": "Gateway widgets", "image": "data:image/png;base64,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", - "description": "Widgets to manage ThingsBoard IoT Gateway." + "description": "Widgets to manage ThingsBoard IoT Gateway instances.", + "order": 15000, + "externalId": null, + "name": "Gateway widgets" }, - "widgetTypes": [ - { - "alias": "gateway_configuration", - "name": "Gateway Configuration", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAR50lEQVR42u2dh3MURxaH9SedfXcuXwJjsg1lwMacoeBIJicTTDIgEBlEEDknk7NBJoPIGYQAkRFCRJEFGElg733sM13D7Gq1rNZY4fcrFdXTM9PT0/3t69c9w7yEQCDw8uXL3Nzc69evX5OkEgiEAKmwsBCoEqAqJyfnyZMnr169CkhSCQRCgAROQJUAYmyoUaR4CZyAKgHzJVslxdduAVUCQ6PaQoqvgEpgSX8qWL9JUlBxA8uK+1WSgooGr2LAcki9CuplUIVShZT1vpFQLF7Fg2VUUWJBQUF+fv6LN/pFqjBynQ4AYGB4GVuxgOWoglZDinSUQ6xU/kTXA4DhBRKR2SoeLPCkILAlocaVwAAYQIJELGA5c4Xpe/78OQupalPJdOfOHZAAjAhGqxiwzFyxQm+PFSUJAQNIRDZaxYPFmPrw4UO1puQVSABG7GDBpsCSigILPEoE1oMHD9SUklcgUVKwmAIILCkULMAQWJLAkgSWJLAEliSwJIElCSyBJQksSWBJAuv9gXXy5MmfihAPxnnpggT/X9Z31pYtW9LT010J27ZtszRHcvy9e/e8B1MxMvn/a97M06dPL168eN68edu3b+cAX/m3b9/mlLlz565bt85bGme56u3cuTMzM5N7951LnX/++WfOXbt2bVEvEVEO1/Vl3rp1yxW+efPmU6dOeSt26dIl27Vx48aDBw+WlceyfxpY06dP/+8bVa5c+bPPPnOb2dnZN2/erFSpEq3sO6tBgwajRo2y9MiRI7/66itLcyTH9+rVy3swbwWRSWfbJlX94YcfuFbjxo27dOlSp06devXq0dPueGCqWrUqFejWrVvDhg2rV6/uzp08eTInWvWoA+nmzZt76Tl69Ojnn39erVq1li1b1q5du0qVKqtXrw5tsSZNmnAud+fN37VrF/X88ssvKZw74ty6deu63ww/A/Y2atSIvZ8GlZyczKtOAqt40R8zZ8705sQGlu8UH1jTpk2jVyDAGRjwokDrpBs3bnzyySfgbq3AG2opKSnkmNUErFq1armSQZ++HzJkiG3evXu3Ro0aAwYM4LErmzQLdotLHzt2zGeuoKp+/fqzZ88OBevy5cu2+fTp0z59+oCXgWtgWcnUCqPFpfmFCKz3B1arVq1odDdYeMGCnpo1aw4fPtxbGiMaZsa6nwGOg69cueL2Ug57GYNCwUJ0bbNmzZw9A6zHjx97G4e9Xbt29Z5ChTt06LBgwQKMk/eTBT6wENUgh3wfWCbYIuf48eMxNzgtE8OuigsW3gkoDBw4MBSss2fPkk5LSyuqDvQr5mTSpElhv1LhA4t3IxmY+vXrZ5swFGpCMFoYPDdm0UqQzWiLG8eFDh8+HAGsrKwscnAow4JFy/P7GTduXGytvXXr1t69e584cSJ0V0ZGBrtSU1PLOViDBg368W3RuxHAYjgDIxK7d+/2gbVv3z7S4BWhGqBAlzdt2pQLUZQPLNyv6UExROKltWjRAkRsL04VB/hKs5q4Y9ikBPtoT+fOnbm1CEMhvYsJNNMbChZq166dz6GMXlyOc0PZMqrYBXnlHCwswXdvi76JDBa16tGjB35MXl6eFyxsFWlmWHb8kiVLBr4RI4t3cBw2bBidysHdu3d3M0q4wemx4/v27YsrDX/nz5+3vezyuU0OF9d0nTp1clZtw4YNDjJ3JDfbunVr/mUX9XcmLSxY1I0CY27wULbiS1V5GwrNzGAkGHRGjBjhBQuPhLTz3Dke28PAR+aKFSt8V+GOGIaghxHHbs03FDJcMg4yc7Rx84svvhgzZoyvEBYdKNysDjMAbOHChQsvBEVl2Fy1apUXrDlz5uDPrVmzBrCwna6csGCBdQn9d8cWlcGFiC9V5RMsRPewST85sJhh0ZfLly/3rTw5sC5evIi58t05e637Q5137xWZXbZv395X1fHjx7OiYctd3F2lEDHVCDsUgjsm0zVpKFjPnj0DPjAtYd+DkbEVd6rKLVhUjJEC18e73IBng43xrgB5waI07JO3/x49ehQBLHPIbCXTjNOZM2fcXvwkrm5VxaoxDWTxyXu6+XzQHAoWs0vOHTt2bFFgTZgwgTU2572V3G7FnapyCxbCPaL1vWDRc1gClgBYT7p///65c+cwKs7Tv3r1Kntx4xgXWHPHetHcDKm2mGkLCjaQcQDePZtDhw51I2ObNm2oG6vqLHEBTdu2bcHU1u5tdcC7EmuncACIhJ0VsiTBjJK5oQPrdFDM1/Dw2LVp06Z4EbA1KK1jRQuWeehesOzxCGBhaWwwwlN2C9w2GgIW3WZ7Gd1waZ3z7oYwVllZQMdiea0Ixo/6MEJxAOUzgXDPo1g4ZW0itHGhirGSBgwFiyblqQAjlAPLisWppzQfo1ogLS0CCMDFU4mwl5WqGErm/2cyQvkc7Yopvd0gCSxJYEkCS2BJAksSWJLAEliSwJIEliSwBJYksCSBJQksgSUJLElgSQJLYEkCSxJYksASWJLAkgSWJLAEliSwJIElCSyBJQksSWBJAktgSQJLEliSwBJYksCSBJYksASWJLAkgSUJrD8WLL6MzWeD+dj/3r17SatLBFYcwOJr2Hz1umfPnnwvny+YE3Vtx44d0ZzIZ9ktOJYksPziE/58aJ+wDnZh/iXqGjneoIFFiQ+7EwFL/Sewwoiv8vONfG98QAok+BGRcFwOwR2IAEAoAOyT+zI7VBHbDQRJQKc7l5CWy5YtI2qDiyRABIADBw640ghI5A1MQkgwdzoBTrgEF+JypC1zz549Lq5TIBi8mSu6qF0mQgeQSTAmImktXbqUyF6+AZ1LEDeFwCoE9HKRpIntw1mEKCOMBWdRZ+6Ovfv37w9bCO1AJoUcOnSIr8kLrEgi9HLkSEOEYGBwZJQkxEj//v0ZMS2K6YwZMwibC1gk6AmjCtQokBhahK7AmBGYJBCMJtKxY0cqaRUmYIQLFEhHUoJxg40k0BIBwwgDkZiYSNoCjcIZkSxcR0It8dx8kTKJ20u8k6SkpMGDB8+aNYtKEt7C/QaghAqQT3hzgvlMnTrVWpnqcRYRvKg2MYU5hutOnDiRcjiYaCtc1xHMj4HQQNwXd0c+DRIa6lxg/S56i5blVxjhGOJgEVXGcUDjuiAovqGQIDbsdWFn6RsLf8oPnatYYEFiitC1wAQKbGJgKAF7Q3r+/PlTpkyx++eOgMPihAEcpxujCC8QxH2VNLDocjvdNg13zBjVOHLkiB1Jgl0WXMnActYUW8umqwMnUjeiCQeCQYf5PbjrEiOIXa5MgeUXMUVoSgcKQZFmvJE3+o1dCEMFInSSa18fWAR75kS3ad3GQEMaE4XrRoLxiHCVRLwxrx9bEhpkECPBhSiNPrZLYykteiA9SpkWrysUrPT0dJeDwfP9YPgV0UTEd+VIG4uthu6XwF4vZwijZYHHKNl7JMJa++LjCay3TmeYc0EomeWtDur7779nRLBMeCJ4E4fRVVDF/LEosBih2oTIqr1y5UpgCgTDg2MYoMrCPDFyOayxW5hGxiNMGmaJhIGFWAdhhIUMEniEoW0UChbHW4hyhLEEU8wkVofTOdJCLPnAwqtj0xsomoOtcZg4h94ao6fAKlJ0LYHdfJn0ugNr9OjRMOHcWAJfFQUW2DH8XXtbFmgJ35kjGdQAlPBJeM1sEoaJznY+PtFWMWwuAiBOjAPLuIEDvB8YDb2LCGDRLAzQ/FqsJvj+MYDF8p5ZSu+t2WgusMKLIY/eJcBfWLAomb143+5ytLUDC8PjBYsJJgOE9wbMYbdhCIMHoLjJbigh3adPH3cw7vb69evdJmGhHViBYPw6UHMmMHqwmBmwy80xiT8YA1jm53nLd7cmsMKLc+ljC0vMqIcVASMGo0WLFjlnmT7m6vg3GCTa1wVLxjs2r5xYpm5JjDkm5g0/CSPBJMtNnRg4OJi1A9vEAWLT64YzH2SgxLvHhjExZK+zmja1JAd7GfYuIoBFtakVzhwA4VphFDnS4ntHDxbtjOXGQ6AEhmxMF81CiwmsSHLOjbkOTMdYL3W/SHqaRiQf74qZP+2Lv+JOBAV2OQoxD7S+lUMXuoD1gWCcXDrYzd6hxzvXCwTjblqvYwVx8DFXXnvGJIB85za9k49F8FK7O4wiy2ZUw6Ym0YMVCAYC5rdBHTiG0lj1KP1LWaXiITQrQ5RAx4etAY0eth25OtNy7y5OJ8e3gBm9sJreiNFedAAi5hjM1JBahb21dxLtzIy1TIyDAb3dUKy4R1t01VMagRU3MYYyrjGzs7jfksCKjxgEmSUwkAkUgSUJLElgCSxJYEkCSxJY8QXrwoUL9tYecy7e87RVTcohba+78HDGpXlyTNreniPN4zZ7j4qVa/KpAGkWMMnnMXMg+LiXtD36ZZ2dhyG2rpiTk0O+ranyvIV8S3ODpO0NPlYWeL/FHgfxzgVpaxee2fFuj6V5JMBDJEvzjMW9hnohKEvzuMneseEwDuAUS1OIvXtNmsK5RCC4WkbaFjWoBpWxNqd6pKmqpak8txAIPg0j3x6ic5vkc8subWu5NAuNY4v7NBf5NJ2lybc0a/qk7TE/XUDaupKuIW3TYRao3U3JYkkaCiWBJbCk9wcWbo3AkkLBAoySgoWXV1aeukvvQbwkAhIlAoupioFlsxVJQrwVbGCBR+xgMcVldpqamhr2ZSapogkeeM8RJEjEDhaLLvDEEgj/0YBXfkFVY2KFFV0PAGAADCABGODxzmB5jRZLl7wKzH924wVi3hn/UaqQousBAAyAASQimKuowHJGi+JYYj4rVWABABgUa66KAcvLFmaQZwVwmvdGT6QKI9fpAAAGwBCZqqjAcmyhl0EVShVS1vtGglEVO1g+vCSpWKTeASwvXpIUDS3vAJYkRS+BJQmscqeMSzert0v+oFHiX74uA3/Us1rb5H0nLwus0k7VB40Glwmk3sLr60RqLrBKr7BVZY4q+6vZbpzAKr0qKyNg2D+BVXpVdqkSWAJLYAksgSUJLIElsASWwBJYksASWAJLYAms6P7+2Wz4374ZIrCk+ID1YaPBk5ftePD49Wd5eIsz7diF2h3HCyyppGBNXLyNEzfuOdVj7PJxi7Y++6Ug68a9v/4ZpktglSuwTpzLfpFf6B4yTl2x68mzF037zSL9UZOkfimrZ6xMGzpr439ajCTnmz4zkmZuqNJ6tB3cddSSQVPXWbpul5QJP25LXrilYc9pAktgJabuzeDEMfM3f9RkqDf/X/8bfjH7Tn7BS8h7/qLg7oO8T78d02bI6yCjI+akcgAO2dPn+XtPXCTdbfTSgsJXjKe5D/Nevvq194SVAquig1WjXfLF7NcR5x7lPZ+7dq9zsL4dPP9QxpUOwxaR7jRiMQdgt3DIQGd/+mU7gMz+k9f8vXHSwyfPMy7dAE3G0OOZ16AwhvcsBFZ5mxWCS9fRSw+euhL8f1O/MZxZ/j+aDuubsnrK8p1rdryOHDZ9ZRqZizYexDgxhVy44QD2DMPWfMAc9m4/nMkoyV/a0ddRj6u3TRZYWm74/a9B9ymZV2+BV53OE2t1GI/huZX7eOmmw16wmvSdSRpPP+vm/W0Hzzp7djP3UeaVW+6vXrdJAqvigvXv5iPAaN/JSy4nZcl2ysGXYg2CRMOeUw04BxZ/128/OH4u2/Bis953k0nPW7/PFRLbpFJglSuLteXAGdgCC9wpBr7b9x7jklduOWrsgtfRr2eu3t02aSGjJOnZa/bYKTNX7Wbz2S/55u/jTh05k8WwOHx2aqvEecs2Hzl6Nks+VkUH6+OmwxanHgImK+F81u2WA+daPjO+wOvvfv/GUMi/W4MDH3/1gyZqw+50V0jVNmN3HD7H+ir5DKC9xq2QxZKPlWhrB0wP8dZ9+ZVajPw4JLOoP6aH+OwfxvrfhARWuXXe9RBaEliSwBJYAktgSQJLElgCS2AJLElgSe9FZfejINRcYJVeVWtbVj9jVKv9eIFVesWn8T4om+bqjD68VsrFp/H4iFnZslXRUCWwpD9KAksSWJLAkgSWwJIEllSGwLp+/TqBwtQWUrwETkCVkJubS7BDNYcULxGKHKgSiHGYk5MDW7JbUsltFSCBE4kEtomcCWKYr2uSVAKBECCZhfo//w/mIKeOaZ4AAAAASUVORK5CYII=", - "description": "Allows to define gateway configuration for a single selected device.", - "descriptor": { - "type": "static", - "sizeX": 8, - "sizeY": 6.5, - "resources": [], - "templateHtml": "\n\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-gateway-config-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"widgetTitle\":\"Gateway Configuration\",\"archiveFileName\":\"configurationGateway\"},\"title\":\"Gateway Configuration\",\"dropShadow\":true,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "attributes_card", - "name": "Gateway events", - "image": "data:image/png;base64,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", - "description": "Allows to browse events from the gateway.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 8, - "resources": [], - "templateHtml": "", - "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", - "controllerScript": "let types;\nlet eventsReg = \"eventsReg\";\n\nself.onInit = function() {\n \n self.ctx.datasourceTitleCells = [];\n self.ctx.valueCells = [];\n self.ctx.labelCells = [];\n\n if (self.ctx.datasources.length && self.ctx.datasources[0].type === 'entity') {\n getDatasourceKeys(self.ctx.datasources[0]);\n } else {\n processDatasources(self.ctx.datasources);\n }\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.valueCells.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData && cellData.data && cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n var textValue;\n //toDo -> + IsNumber\n \n if (isNumber(value)) {\n var decimals = self.ctx.decimals;\n var units = self.ctx.units;\n if (cellData.dataKey.decimals || cellData.dataKey.decimals === 0) {\n decimals = cellData.dataKey.decimals;\n }\n if (cellData.dataKey.units) {\n units = cellData.dataKey.units;\n }\n txtValue = self.ctx.utils.formatValue(value, decimals, units, false);\n }\n else {\n txtValue = value;\n }\n self.ctx.valueCells[i].html(txtValue);\n }\n }\n \n function isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n }\n}\n\nself.onResize = function() {\n var datasourceTitleFontSize = self.ctx.height/8;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n datasourceTitleFontSize = self.ctx.width/12;\n }\n datasourceTitleFontSize = Math.min(datasourceTitleFontSize, 20);\n for (var i = 0; i < self.ctx.datasourceTitleCells.length; i++) {\n self.ctx.datasourceTitleCells[i].css('font-size', datasourceTitleFontSize+'px');\n }\n var valueFontSize = self.ctx.height/9;\n var labelFontSize = self.ctx.height/9;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n valueFontSize = self.ctx.width/15;\n labelFontSize = self.ctx.width/15;\n }\n valueFontSize = Math.min(valueFontSize, 18);\n labelFontSize = Math.min(labelFontSize, 18);\n\n for (i = 0; i < self.ctx.valueCells; i++) {\n self.ctx.valueCells[i].css('font-size', valueFontSize+'px');\n self.ctx.valueCells[i].css('height', valueFontSize*2.5+'px');\n self.ctx.valueCells[i].css('padding', '0px ' + valueFontSize + 'px');\n self.ctx.labelCells[i].css('font-size', labelFontSize+'px');\n self.ctx.labelCells[i].css('height', labelFontSize*2.5+'px');\n self.ctx.labelCells[i].css('padding', '0px ' + labelFontSize + 'px');\n } \n}\n\nfunction processDatasources(datasources) {\n var i = 0;\n var tbDatasource = datasources[i];\n var datasourceId = 'tbDatasource' + i;\n self.ctx.$container.append(\n \"
\"\n );\n\n var datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n\n datasourceContainer.append(\n \"
\" +\n tbDatasource.name + \"
\"\n );\n \n var datasourceTitleCell = $('.tbDatasource-title', datasourceContainer);\n self.ctx.datasourceTitleCells.push(datasourceTitleCell);\n \n var tableId = 'table' + i;\n datasourceContainer.append(\n \"
\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n\n for (var a = 0; a < tbDatasource.dataKeys.length; a++) {\n var dataKey = tbDatasource.dataKeys[a];\n var labelCellId = 'labelCell' + a;\n var cellId = 'cell' + a;\n table.append(\"\" + dataKey.label +\n \"\");\n var labelCell = $('#' + labelCellId, table);\n self.ctx.labelCells.push(labelCell);\n var valueCell = $('#' + cellId, table);\n self.ctx.valueCells.push(valueCell);\n }\n self.onResize();\n}\n\nfunction getDatasourceKeys (datasource) {\n let entityService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('entityService'));\n if (datasource.entityId && datasource.entityType) {\n entityService.getEntityKeys({entityType: datasource.entityType, id: datasource.entityId}, '', 'timeseries').subscribe(\n function(data){\n if (data.length) {\n subscribeForKeys (datasource, data);\n }\n });\n }\n}\n\nfunction subscribeForKeys (datasource, data) {\n let eventsRegVals = self.ctx.settings[eventsReg];\n if (eventsRegVals && eventsRegVals.length > 0) {\n var dataKeys = [];\n data.sort();\n data.forEach(dataValue => {eventsRegVals.forEach(event => {\n if (dataValue.toLowerCase().includes(event.toLowerCase())) {\n var dataKey = {\n type: 'timeseries',\n name: dataValue,\n label: dataValue,\n settings: {},\n _hash: Math.random()\n };\n dataKeys.push(dataKey);\n }\n })});\n\n if (dataKeys.length) {\n updateSubscription (datasource, dataKeys);\n }\n }\n}\n\nfunction updateSubscription (datasource, dataKeys) {\n var datasources = [\n {\n type: 'entity',\n name: datasource.aliasName,\n aliasName: datasource.aliasName,\n entityAliasId: datasource.entityAliasId,\n dataKeys: dataKeys\n }\n ];\n \n var subscriptionOptions = {\n datasources: datasources,\n useDashboardTimewindow: false,\n type: 'latest',\n callbacks: {\n onDataUpdated: (subscription) => {\n self.ctx.data = subscription.data;\n self.onDataUpdated();\n }\n }\n };\n \n processDatasources(datasources);\n self.ctx.subscriptionApi.createSubscription(subscriptionOptions, true).subscribe(\n (subscription) => {\n self.ctx.defaultSubscription = subscription;\n }\n );\n}\n\nself.onDestroy = function() {\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\t\n dataKeysOptional: true,\n singleEntity: true\n };\n}\n\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-gateway-events-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Function Math.round\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.826503672916844,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"eventsTitle\":\"Gateway Events Form\",\"eventsReg\":[]},\"title\":\"Gateway events\",\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "config_form_latest", - "name": "Gateway configuration (Single device)", - "image": "data:image/png;base64,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", - "description": "Allows to create or choose the gateway device and edit the configuration.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 9, - "resources": [], - "templateHtml": "\n", - "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", - "controllerScript": "self.onInit = function() {\n}\n\n\nself.onDestroy = function() {\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\t\t\t\n dataKeysOptional: true,\n singleEntity: true\n };\n}\n\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-gateway-config-single-device-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"gatewayTitle\":\"Gateway configuration (Single device)\"},\"title\":\"Gateway configuration (Single device)\"}" - } - } + "widgetTypeFqns": [ + "gateway_widgets.gateway_configuration", + "gateway_widgets.attributes_card", + "gateway_widgets.gateway_general_configuration", + "gateway_widgets.config_form_latest", + "gateway_widgets.gateway_connectors", + "gateway_widgets.gateway_logs", + "gateway_widgets.gateway_custom_statistics", + "gateway_widgets.gateway_general_chart_statistics", + "gateway_widgets.service_rpc" ] -} \ No newline at end of file +} diff --git a/application/src/main/data/json/system/widget_bundles/gpio_widgets.json b/application/src/main/data/json/system/widget_bundles/gpio_widgets.json index e539824aca..78b9c1eea6 100644 --- a/application/src/main/data/json/system/widget_bundles/gpio_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/gpio_widgets.json @@ -3,84 +3,15 @@ "alias": "gpio_widgets", "title": "GPIO widgets", "image": "data:image/png;base64,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", - "description": "Visualization and control of GPIO state for target devices." + "description": "Visualization and control of the state of the GPIO devices.", + "order": 22000, + "externalId": null, + "name": "GPIO widgets" }, - "widgetTypes": [ - { - "alias": "basic_gpio_control", - "name": "Basic GPIO Control", - "image": "data:image/png;base64,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", - "description": "Allows to change state of the GPIO for target device using RPC commands. Requires handling of the RPC commands in the device firmware. Uses 'getGpioStatus' and 'setGpioStatus' RPC calls", - "descriptor": { - "type": "rpc", - "sizeX": 4, - "sizeY": 2, - "resources": [], - "templateHtml": "
\n
\n
\n
\n {{ cell.label }}\n
\n {{cell.pin}}\n \n \n \n \n {{cell.pin}}\n
\n {{ cell.label }}\n
\n
\n \n \n \n
\n
\n
\n {{rpcErrorText}}\n \n
", - "templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.gpio-panel section[fxflex] {\n min-width: 0px;\n}\n\n\n.switch-panel {\n margin: 0;\n height: 32px;\n width: 66px;\n min-width: 66px;\n}\n\n.switch-panel mat-slide-toggle {\n margin: 0;\n width: 36px;\n min-width: 36px;\n}\n\n.switch-panel.col-0 mat-slide-toggle {\n margin-left: 8px;\n margin-right: 4px;\n}\n\n.switch-panel.col-1 mat-slide-toggle {\n margin-left: 4px;\n margin-right: 8px;\n}\n\n.gpio-row {\n height: 32px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.switch-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.switch-panel.col-1 .pin {\n margin-right: auto;\n \n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}", - "controllerScript": "var namespace;\nvar cssParser = new cssjs();\n\nself.onInit = function() {\n var utils = self.ctx.$injector.get(self.ctx.servicesMap.get('utils'));\n namespace = 'gpio-control-' + utils.guid();\n cssParser.testMode = false;\n cssParser.cssPreviewNamespace = namespace;\n self.ctx.$container.addClass(namespace);\n self.ctx.ngZone.run(function() {\n init(); \n });\n}\n\nfunction init() {\n \n var i, gpio;\n var scope = self.ctx.$scope;\n var settings = self.ctx.settings;\n scope.gpioList = [];\n for (var g = 0; g < settings.gpioList.length; g++) {\n gpio = settings.gpioList[g];\n scope.gpioList.push(\n {\n row: gpio.row,\n col: gpio.col,\n pin: gpio.pin,\n label: gpio.label,\n enabled: false\n }\n );\n }\n\n scope.requestTimeout = settings.requestTimeout || 1000;\n\n scope.switchPanelBackgroundColor = settings.switchPanelBackgroundColor || tinycolor('green').lighten(2).toRgbString();\n\n scope.gpioStatusRequest = {\n method: \"getGpioStatus\",\n paramsBody: \"{}\"\n };\n \n if (settings.gpioStatusRequest) {\n scope.gpioStatusRequest.method = settings.gpioStatusRequest.method || scope.gpioStatusRequest.method;\n scope.gpioStatusRequest.paramsBody = settings.gpioStatusRequest.paramsBody || scope.gpioStatusRequest.paramsBody;\n }\n \n scope.gpioStatusChangeRequest = {\n method: \"setGpioStatus\",\n paramsBody: \"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"\n };\n \n if (settings.gpioStatusChangeRequest) {\n scope.gpioStatusChangeRequest.method = settings.gpioStatusChangeRequest.method || scope.gpioStatusChangeRequest.method;\n scope.gpioStatusChangeRequest.paramsBody = settings.gpioStatusChangeRequest.paramsBody || scope.gpioStatusChangeRequest.paramsBody;\n }\n \n scope.parseGpioStatusFunction = \"return body[pin] === true;\";\n \n if (settings.parseGpioStatusFunction && settings.parseGpioStatusFunction.length > 0) {\n scope.parseGpioStatusFunction = settings.parseGpioStatusFunction;\n }\n \n scope.parseGpioStatusFunction = new Function(\"body, pin\", scope.parseGpioStatusFunction);\n \n function requestGpioStatus() {\n self.ctx.controlApi.sendTwoWayCommand(scope.gpioStatusRequest.method, \n scope.gpioStatusRequest.paramsBody, \n scope.requestTimeout)\n .subscribe(\n function success(responseBody) {\n for (var g = 0; g < scope.gpioList.length; g++) {\n var gpio = scope.gpioList[g];\n var enabled = scope.parseGpioStatusFunction.apply(this, [responseBody, gpio.pin]);\n gpio.enabled = enabled; \n self.ctx.detectChanges();\n }\n }\n );\n }\n \n function changeGpioStatus(gpio) {\n var pin = gpio.pin + '';\n var enabled = !gpio.enabled;\n enabled = enabled === true ? 'true' : 'false';\n var paramsBody = scope.gpioStatusChangeRequest.paramsBody;\n var requestBody = JSON.parse(paramsBody.replace(\"\\\"{$pin}\\\"\", pin).replace(\"\\\"{$enabled}\\\"\", enabled));\n self.ctx.controlApi.sendTwoWayCommand(scope.gpioStatusChangeRequest.method, \n requestBody, scope.requestTimeout)\n .subscribe(\n function success(responseBody) {\n var enabled = scope.parseGpioStatusFunction.apply(this, [responseBody, gpio.pin]);\n gpio.enabled = enabled;\n self.ctx.detectChanges();\n }\n );\n }\n \n scope.gpioCells = {};\n var rowCount = 0;\n for (i = 0; i < scope.gpioList.length; i++) {\n gpio = scope.gpioList[i];\n scope.gpioCells[gpio.row+'_'+gpio.col] = gpio;\n rowCount = Math.max(rowCount, gpio.row+1);\n }\n \n scope.prefferedRowHeight = 32;\n scope.rows = [];\n for (i = 0; i < rowCount; i++) {\n var row = [];\n for (var c =0; c<2;c++) {\n if (scope.gpioCells[i+'_'+c]) {\n row[c] = scope.gpioCells[i+'_'+c];\n } else {\n row[c] = null;\n }\n }\n scope.rows.push(row);\n }\n\n scope.gpioClick = function($event, gpio) {\n if (scope.rpcEnabled && !scope.executingRpcRequest) {\n changeGpioStatus(gpio);\n }\n };\n \n scope.gpioToggleChange = function($event, gpio) {\n gpio.enabled = !$event.checked;\n $event.source.toggle();\n self.ctx.detectChanges();\n }\n \n if (scope.rpcEnabled) {\n requestGpioStatus(); \n }\n \n self.onResize();\n}\n\nself.onResize = function() {\n var scope = self.ctx.$scope;\n var rowCount = scope.rows.length;\n var prefferedRowHeight = (self.ctx.height - 35)/rowCount;\n prefferedRowHeight = Math.min(32, prefferedRowHeight);\n prefferedRowHeight = Math.max(12, prefferedRowHeight);\n scope.prefferedRowHeight = prefferedRowHeight;\n var ratio = prefferedRowHeight/32;\n \n var css = '.mat-slide-toggle .mat-slide-toggle-bar {\\n' +\n ' height: ' + 14*ratio+'px;\\n'+\n ' width: ' + 36*ratio+'px;\\n'+\n '}\\n';\n css += '.mat-slide-toggle .mat-slide-toggle-thumb-container {\\n' +\n ' height: ' + 20*ratio+'px;\\n'+\n ' width: ' + 20*ratio+'px;\\n'+\n '}\\n';\n css += '.mat-slide-toggle .mat-slide-toggle-thumb {\\n' +\n ' height: ' + 20*ratio+'px;\\n'+\n ' width: ' + 20*ratio+'px;\\n'+\n '}\\n';\n css += '.mat-slide-toggle .mat-slide-toggle-ripple {\\n' +\n ' height: ' + 40*ratio+'px;\\n'+\n ' width: ' + 40*ratio+'px;\\n'+\n ' top: calc(50% - '+20*ratio+'px);\\n'+\n ' left: calc(50% - '+20*ratio+'px);\\n'+\n '}\\n';\n css += '.gpio-left-label, .gpio-right-label {\\n' +\n ' font-size: ' + 16*ratio+'px;\\n'+\n '}\\n';\n var pinsFontSize = Math.max(9, 12*ratio);\n css += '.pin {\\n' +\n ' font-size: ' + pinsFontSize+'px;\\n'+\n '}\\n';\n\n cssParser.createStyleElement(namespace, css);\n \n self.ctx.detectChanges();\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-gpio-control-widget-settings", - "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 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\":\"Basic GPIO Control\"}" - } - }, - { - "alias": "gpio_panel", - "name": "Basic GPIO Panel", - "image": "data:image/png;base64,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", - "description": "Allows to display state of the GPIO for target device using latest attribute values. You should set the label of the selected data key to GPIO pin number (e.g. '1') and use boolean values for widget to display the data.", - "descriptor": { - "type": "latest", - "sizeX": 5, - "sizeY": 2, - "resources": [], - "templateHtml": "
\n
\n
\n
\n {{ cell.label }}\n
\n {{cell.pin}}\n \n \n \n \n {{cell.pin}}\n
\n {{ cell.label }}\n
\n
\n \n \n \n
\n
\n
\n
", - "templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.gpio-panel section[fxflex] {\n min-width: 0px;\n}\n\n\n.gpio-panel tb-led-light > div {\n margin: auto;\n}\n\n.led-panel {\n margin: 0;\n width: 66px;\n min-width: 66px;\n}\n\n.led-container {\n width: 48px;\n min-width: 48px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.led-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.led-panel.col-1 .pin {\n margin-right: auto;\n \n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}", - "controllerScript": "var namespace;\nvar cssParser = new cssjs();\n\nself.onInit = function() {\n var utils = self.ctx.$injector.get(self.ctx.servicesMap.get('utils'));\n namespace = 'gpio-panel-' + utils.guid();\n cssParser.testMode = false;\n cssParser.cssPreviewNamespace = namespace;\n self.ctx.$container.addClass(namespace);\n self.ctx.ngZone.run(function() {\n init(); \n });\n}\n\nfunction init() {\n var i, gpio;\n \n var scope = self.ctx.$scope;\n var settings = self.ctx.settings;\n \n scope.gpioList = [];\n scope.gpioByPin = {};\n for (var g = 0; g < settings.gpioList.length; g++) {\n gpio = settings.gpioList[g];\n scope.gpioList.push(\n {\n row: gpio.row,\n col: gpio.col,\n pin: gpio.pin,\n label: gpio.label,\n enabled: false,\n colorOn: tinycolor(gpio.color).lighten(20).toHexString(),\n colorOff: tinycolor(gpio.color).darken().toHexString()\n }\n );\n scope.gpioByPin[gpio.pin] = scope.gpioList[scope.gpioList.length-1];\n }\n\n scope.ledPanelBackgroundColor = settings.ledPanelBackgroundColor || tinycolor('green').lighten(2).toRgbString();\n\n scope.gpioCells = {};\n var rowCount = 0;\n for (i = 0; i < scope.gpioList.length; i++) {\n gpio = scope.gpioList[i];\n scope.gpioCells[gpio.row+'_'+gpio.col] = gpio;\n rowCount = Math.max(rowCount, gpio.row+1);\n }\n \n scope.prefferedRowHeight = 32;\n scope.rows = [];\n for (i = 0; i < rowCount; i++) {\n var row = [];\n for (var c =0; c<2;c++) {\n if (scope.gpioCells[i+'_'+c]) {\n row[c] = scope.gpioCells[i+'_'+c];\n } else {\n row[c] = null;\n }\n }\n scope.rows.push(row);\n } \n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n var changed = false;\n for (var d = 0; d < self.ctx.data.length; d++) {\n var cellData = self.ctx.data[d];\n var dataKey = cellData.dataKey;\n var gpio = self.ctx.$scope.gpioByPin[dataKey.label];\n if (gpio) {\n var enabled = false;\n if (cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length - 1];\n enabled = (tvPair[1] === true || tvPair[1] === 'true');\n }\n if (gpio.enabled != enabled) {\n changed = true;\n gpio.enabled = enabled;\n }\n }\n }\n if (changed) {\n self.ctx.detectChanges();\n } \n}\n\nself.onResize = function() {\n var rowCount = self.ctx.$scope.rows.length;\n var prefferedRowHeight = (self.ctx.height - 35)/rowCount;\n prefferedRowHeight = Math.min(32, prefferedRowHeight);\n prefferedRowHeight = Math.max(12, prefferedRowHeight);\n self.ctx.$scope.prefferedRowHeight = prefferedRowHeight;\n \n var ratio = prefferedRowHeight/32;\n \n var css = '.gpio-left-label, .gpio-right-label {\\n' +\n ' font-size: ' + 16*ratio+'px;\\n'+\n '}\\n';\n var pinsFontSize = Math.max(9, 12*ratio);\n css += '.pin {\\n' +\n ' font-size: ' + pinsFontSize+'px;\\n'+\n '}\\n';\n \n cssParser.createStyleElement(namespace, css); \n \n self.ctx.detectChanges();\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-gpio-panel-widget-settings", - "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"gpioList\":[{\"pin\":1,\"label\":\"GPIO 1\",\"row\":0,\"col\":0,\"color\":\"#008000\",\"_uniqueKey\":0},{\"pin\":2,\"label\":\"GPIO 2\",\"row\":0,\"col\":1,\"color\":\"#ffff00\",\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 3\",\"row\":1,\"col\":0,\"color\":\"#cf006f\",\"_uniqueKey\":2}],\"ledPanelBackgroundColor\":\"#b71c1c\"},\"title\":\"Basic GPIO Panel\",\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"1\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.22518255793320163,\"funcBody\":\"var period = time % 1500;\\nreturn period < 500;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"2\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.7008206860666621,\"funcBody\":\"var period = time % 1500;\\nreturn period >= 500 && period < 1000;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"3\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.42600325102193426,\"funcBody\":\"var period = time % 1500;\\nreturn period >= 1000;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}}}" - } - }, - { - "alias": "raspberry_pi_gpio_panel", - "name": "Raspberry Pi GPIO Panel", - "image": "data:image/png;base64,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", - "description": "Allows to change state of the GPIO for Raspberry Pi device using RPC commands. Requires handling of the RPC commands in the device firmware. Uses 'getGpioStatus' and 'setGpioStatus' RPC calls", - "descriptor": { - "type": "latest", - "sizeX": 7, - "sizeY": 10.5, - "resources": [], - "templateHtml": "
\n
\n
\n
\n {{ cell.label }}\n
\n {{cell.pin}}\n \n \n \n \n {{cell.pin}}\n
\n {{ cell.label }}\n
\n
\n \n \n \n
\n
\n
\n
", - "templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.gpio-panel section[fxflex] {\n min-width: 0px;\n}\n\n.gpio-panel tb-led-light > div {\n margin: auto;\n}\n\n.led-panel {\n margin: 0;\n width: 66px;\n min-width: 66px;\n}\n\n.led-container {\n width: 48px;\n min-width: 48px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.led-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.led-panel.col-1 .pin {\n margin-right: auto;\n \n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}", - "controllerScript": "var namespace;\nvar cssParser = new cssjs();\n\nself.onInit = function() {\n var utils = self.ctx.$injector.get(self.ctx.servicesMap.get('utils'));\n namespace = 'gpio-panel-' + utils.guid();\n cssParser.testMode = false;\n cssParser.cssPreviewNamespace = namespace;\n self.ctx.$container.addClass(namespace);\n self.ctx.ngZone.run(function() {\n init(); \n });\n}\n\nfunction init() {\n var i, gpio;\n \n var scope = self.ctx.$scope;\n var settings = self.ctx.settings;\n \n scope.gpioList = [];\n scope.gpioByPin = {};\n for (var g = 0; g < settings.gpioList.length; g++) {\n gpio = settings.gpioList[g];\n scope.gpioList.push(\n {\n row: gpio.row,\n col: gpio.col,\n pin: gpio.pin,\n label: gpio.label,\n enabled: false,\n colorOn: tinycolor(gpio.color).lighten(20).toHexString(),\n colorOff: tinycolor(gpio.color).darken().toHexString()\n }\n );\n scope.gpioByPin[gpio.pin] = scope.gpioList[scope.gpioList.length-1];\n }\n\n scope.ledPanelBackgroundColor = settings.ledPanelBackgroundColor || tinycolor('green').lighten(2).toRgbString();\n\n scope.gpioCells = {};\n var rowCount = 0;\n for (i = 0; i < scope.gpioList.length; i++) {\n gpio = scope.gpioList[i];\n scope.gpioCells[gpio.row+'_'+gpio.col] = gpio;\n rowCount = Math.max(rowCount, gpio.row+1);\n }\n \n scope.prefferedRowHeight = 32;\n scope.rows = [];\n for (i = 0; i < rowCount; i++) {\n var row = [];\n for (var c =0; c<2;c++) {\n if (scope.gpioCells[i+'_'+c]) {\n row[c] = scope.gpioCells[i+'_'+c];\n } else {\n row[c] = null;\n }\n }\n scope.rows.push(row);\n } \n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n var changed = false;\n for (var d = 0; d < self.ctx.data.length; d++) {\n var cellData = self.ctx.data[d];\n var dataKey = cellData.dataKey;\n var gpio = self.ctx.$scope.gpioByPin[dataKey.label];\n if (gpio) {\n var enabled = false;\n if (cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length - 1];\n enabled = (tvPair[1] === true || tvPair[1] === 'true');\n }\n if (gpio.enabled != enabled) {\n changed = true;\n gpio.enabled = enabled;\n }\n }\n }\n if (changed) {\n self.ctx.detectChanges();\n } \n}\n\nself.onResize = function() {\n var rowCount = self.ctx.$scope.rows.length;\n var prefferedRowHeight = (self.ctx.height - 35)/rowCount;\n prefferedRowHeight = Math.min(32, prefferedRowHeight);\n prefferedRowHeight = Math.max(12, prefferedRowHeight);\n self.ctx.$scope.prefferedRowHeight = prefferedRowHeight;\n \n var ratio = prefferedRowHeight/32;\n \n var css = '.gpio-left-label, .gpio-right-label {\\n' +\n ' font-size: ' + 16*ratio+'px;\\n'+\n '}\\n';\n var pinsFontSize = Math.max(9, 12*ratio);\n css += '.pin {\\n' +\n ' font-size: ' + pinsFontSize+'px;\\n'+\n '}\\n';\n \n cssParser.createStyleElement(namespace, css); \n \n self.ctx.detectChanges();\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-gpio-panel-widget-settings", - "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"gpioList\":[{\"pin\":1,\"label\":\"3.3V\",\"row\":0,\"col\":0,\"color\":\"#fc9700\",\"_uniqueKey\":0},{\"pin\":2,\"label\":\"5V\",\"row\":0,\"col\":1,\"color\":\"#fb0000\",\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 2 (I2C1_SDA)\",\"row\":1,\"col\":0,\"color\":\"#02fefb\",\"_uniqueKey\":2},{\"color\":\"#fb0000\",\"pin\":4,\"label\":\"5V\",\"row\":1,\"col\":1},{\"color\":\"#02fefb\",\"pin\":5,\"label\":\"GPIO 3 (I2C1_SCL)\",\"row\":2,\"col\":0},{\"color\":\"#000000\",\"pin\":6,\"label\":\"GND\",\"row\":2,\"col\":1},{\"color\":\"#00fd00\",\"pin\":7,\"label\":\"GPIO 4 (GPCLK0)\",\"row\":3,\"col\":0},{\"color\":\"#fdfb00\",\"pin\":8,\"label\":\"GPIO 14 (UART_TXD)\",\"row\":3,\"col\":1},{\"color\":\"#000000\",\"pin\":9,\"label\":\"GND\",\"row\":4,\"col\":0},{\"color\":\"#fdfb00\",\"pin\":10,\"label\":\"GPIO 15 (UART_RXD)\",\"row\":4,\"col\":1},{\"color\":\"#00fd00\",\"pin\":11,\"label\":\"GPIO 17\",\"row\":5,\"col\":0},{\"color\":\"#00fd00\",\"pin\":12,\"label\":\"GPIO 18\",\"row\":5,\"col\":1},{\"color\":\"#00fd00\",\"pin\":13,\"label\":\"GPIO 27\",\"row\":6,\"col\":0},{\"color\":\"#000000\",\"pin\":14,\"label\":\"GND\",\"row\":6,\"col\":1},{\"color\":\"#00fd00\",\"pin\":15,\"label\":\"GPIO 22\",\"row\":7,\"col\":0},{\"color\":\"#00fd00\",\"pin\":16,\"label\":\"GPIO 23\",\"row\":7,\"col\":1},{\"color\":\"#fc9700\",\"pin\":17,\"label\":\"3.3V\",\"row\":8,\"col\":0},{\"color\":\"#00fd00\",\"pin\":18,\"label\":\"GPIO 24\",\"row\":8,\"col\":1},{\"color\":\"#fd01fd\",\"pin\":19,\"label\":\"GPIO 10 (SPI_MOSI)\",\"row\":9,\"col\":0},{\"color\":\"#000000\",\"pin\":20,\"label\":\"GND\",\"row\":9,\"col\":1},{\"color\":\"#fd01fd\",\"pin\":21,\"label\":\"GPIO 9 (SPI_MISO)\",\"row\":10,\"col\":0},{\"color\":\"#00fd00\",\"pin\":22,\"label\":\"GPIO 25\",\"row\":10,\"col\":1},{\"color\":\"#fd01fd\",\"pin\":23,\"label\":\"GPIO 11 (SPI_SCLK)\",\"row\":11,\"col\":0},{\"color\":\"#fd01fd\",\"pin\":24,\"label\":\"GPIO 8 (SPI_CE0)\",\"row\":11,\"col\":1},{\"color\":\"#000000\",\"pin\":25,\"label\":\"GND\",\"row\":12,\"col\":0},{\"color\":\"#fd01fd\",\"pin\":26,\"label\":\"GPIO 7 (SPI_CE1)\",\"row\":12,\"col\":1},{\"color\":\"#ffffff\",\"pin\":27,\"label\":\"ID_SD\",\"row\":13,\"col\":0},{\"color\":\"#ffffff\",\"pin\":28,\"label\":\"ID_SC\",\"row\":13,\"col\":1},{\"color\":\"#00fd00\",\"pin\":29,\"label\":\"GPIO 5\",\"row\":14,\"col\":0},{\"color\":\"#000000\",\"pin\":30,\"label\":\"GND\",\"row\":14,\"col\":1},{\"color\":\"#00fd00\",\"pin\":31,\"label\":\"GPIO 6\",\"row\":15,\"col\":0},{\"color\":\"#00fd00\",\"pin\":32,\"label\":\"GPIO 12\",\"row\":15,\"col\":1},{\"color\":\"#00fd00\",\"pin\":33,\"label\":\"GPIO 13\",\"row\":16,\"col\":0},{\"color\":\"#000000\",\"pin\":34,\"label\":\"GND\",\"row\":16,\"col\":1},{\"color\":\"#00fd00\",\"pin\":35,\"label\":\"GPIO 19\",\"row\":17,\"col\":0},{\"color\":\"#00fd00\",\"pin\":36,\"label\":\"GPIO 16\",\"row\":17,\"col\":1},{\"color\":\"#00fd00\",\"pin\":37,\"label\":\"GPIO 26\",\"row\":18,\"col\":0},{\"color\":\"#00fd00\",\"pin\":38,\"label\":\"GPIO 20\",\"row\":18,\"col\":1},{\"color\":\"#000000\",\"pin\":39,\"label\":\"GND\",\"row\":19,\"col\":0},{\"color\":\"#00fd00\",\"pin\":40,\"label\":\"GPIO 21\",\"row\":19,\"col\":1}],\"ledPanelBackgroundColor\":\"#008a00\"},\"title\":\"Raspberry Pi GPIO Panel\",\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"7\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.22518255793320163,\"funcBody\":\"var period = time % 1500;\\nreturn period < 500;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"11\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.7008206860666621,\"funcBody\":\"var period = time % 1500;\\nreturn period >= 500 && period < 1000;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"12\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.42600325102193426,\"funcBody\":\"var period = time % 1500;\\nreturn period >= 1000;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"13\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.48362241571415243,\"funcBody\":\"var period = time % 1500;\\nreturn period < 500;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"29\",\"color\":\"#607d8b\",\"settings\":{},\"_hash\":0.7217670147518815,\"funcBody\":\"var period = time % 1500;\\nreturn period >= 500 && period < 1000;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}}}" - } - }, - { - "alias": "raspberry_pi_gpio_control", - "name": "Raspberry Pi GPIO Control", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAUeUlEQVR42u2dCXBV1RnHXwzVSJopbbRCy9KpMo60yNQWOu0YKkhsDFMNSxBCCAIKKoIsQggkQICEsGWDmLAEFEIICVsgCFgXRNwAEVFUFKWCC4qK+4JL+uMdvLm5776QhIg8/J+5w9x3c2/Iu+/3vnPu+c7//3kqKyu/+eab995779ChQ/9TUzuNBkKAdPz4caDyQNXhw4c//vjjb7/9tlJN7TQaCAESOAGVB8R4oZui1lANnIDKQ/hSrFJr2LgFVB66Rt0LtYZtQCWw1ASWmsD6ydvoR0Z7ZnrO/i3x0USBJbAElsCqHVjhueGXL7q89cLWv8z+pcA6R8D67rvvGupXff/993UFq9HsRp1KOvXf2N9sCRUJVy25ypPuqbZlCKwzC9aHH36YkZGRmZn52Wefvf766+zPnDnz6aef5gPeuHEjJzz22GPjx4/ftm2b/apdu3Y999xzZv/rr7/mKnbWrFmTkpLyzDPPsF9QUMDBJUuW8FMyA/PmzZs6deqRI0f4L1544QVz4YYNG8wfUFFR8cknn8yYMWPp0qUfffTRnDlz/IEVNDPoXyv+FZYdZv9EOxZ3tKiytityr/CM9VTbkn9cvARWtda/f39m7iEpOTl58+bNAPH+++9z8K233rr55ptfffXVoUOHwtydd975yiuvmEs+//zza6+9trCw0LxctGjRSy+9VF5enpOT8+mnn8bFxX355Zd9+vSBmAceeCAtLW3y5Mnbt2//4IMP+vXrt2nTpmXLlpkLecmZ8fHxb775Jv/7iy++mJ2dvXPnzvz8fPZdwWq7pG3WrqwW+S2sI0DmSxVbr3W9gsYGOdmaKLDOFFh8uiZswIQBi1n8hIQEAxb0PPHEE5zw+OOPWySlpqauXLnSejl8+PATH//o0e+++y7M8avoGQ1YYDRt2rQBAwaYMwHUDlbfvn27d+9uItzAgQP595FHHikqKoJge9BydIWDNg2yg9Uyv6UrWGyNUxo7wUoSWGcKLD51Zu7ptnr06AFYN910E90iPR0HAeu+++579NFHOY1/6afYIaIAQVZWFiSRoeTI4MGD+ZdOkMh3//33R0ZGEuc6d+7M7ywuLqYfNOzS6AftYPE/Lly4kFho/gxw3LJly4oVKyAyMTGxlmD97p7f+QPr/PHnO8EaL7DOFFjEG3gCFz5adqxP3YBFJ0VXtXfvXtNjcpywBHZ0fxMnTjSpSfpKBmQ7duwYNmzYyy+/bM60YKLRwRGH6G1vu+02wGKwxW8AMnPOggULwHfWrFkMuUaOHEmvum/fPsZk/sC6csmV9jEWI/c+6/v4UnX9iuudVCV6PFMF1pkCi6jDoLu0tJSu8J133rF+iWGFnddee23x4sUHDhywX3X06FHIsMbgDz/8MDvPP/88o3WwYB+M7Ocz2CopKWFExX9R6m1PPfWUdQ6XwygHTbcLiJxW+6dCphgcVMVviA9PDz8Rn6wtxeNJ01NhQE03gOD8+fMb6rcdO3Zs9erVdZ3HalXQKmZNjKEqqjSKOS3NY2mCtMFm3n8x+xfBs4M18y6wlNIRWAJLYAksgSWwBJbAElgCS2AJLIElsASWwBJYAktgCSyBJbAElsASWAJLYAksgSWwBJbAElgCS2AJLIElsH4mYKGq+O2837IoOWhWUD2wCMkMCcsJC54VLLAEVpU8ut2Sdn039D0pVS3v9Yf5f6g9EE1ymkSVRZlr4zbEXXXvVfVDU2D96A3dKUoyhDcof/7rbchfOW7E9SitV61ahfjHfonR9qAFMucjR/MH1kVzLyK62I+AglP+VdG/xYwWTqnqZBcaUJL1Xt/bcfnVhVc7dWPI86dXu7D90vYOfO1gYSyAQQEqX/bffvtt3tFDDz2Ee8BXX32F6LLSK7HkJiB2si5BVon0zexzMtpxgVWtIahHdIqWa8SIEfg73HXXXYi6ECfiZomQEDF0z549cYK45ZZbLB39uHHjIiIi2IE/JIdoxe6++25XsBAVlrxUYhesAlm/in6+usKYshinrnCsC1sRxRGuYtfwqeEuKuoZJ6+KXhVNXJy/Zz7B0hWsQYMGPfjgg+np6ejFUU3m5eWh+O3WrRs3AS3u/v37Ef1yE7gVfPfMl23UqFG9evViH5X57NmzuSdGVCewTjZLqoq0FbCwGGEfww/w4kcEM+TRHOHbye2zrjISe9OmT59+8OBBfxHLoYRm358S+sLkC08psYcP12vbzmvrwmV6tSHdpO2TXCMWPgNDhgyp9Arj8CsALL4q7CPlRdALWOjIzZcKhuDPcRMIV2h6x44du2fPHoFV1dBPV3r9HbBvAKxOnTqNGTNm7ty5hjnUrUY2yF/LOb5gffHFF0iraxhjOcBqnt/cH1ghySH1ByvPDay0qlHd+G3jm+Y1dQUL5xx8AxBIwhBGBICFcQEBae3atYRkDuJnYUYC5d7mCxb2PojCd+/eLbCqGpp9vpdYNhDnrYhlBTPCGLeP4RfhijvoCxZWM08++WQNYHUu6Rw+t0qPesGcC+Ir4n3JuLH0RhcyUp1g2Y217FvTtKY1+D785d6/EK4GbxnMc6hrV9i7d2/cdRhUEaVMxDLHDVh0gvSSDKqwvWAE5rgJeBdw34hkdp8BgXXCUoa7NmnSJO4OSnnjKWIaKnv+ZVjK19fsW62srMx+Tp2eCnkkdGCRsDGh+YzmJwbd1pbkpWqG89rf5P7Gl8vIokjPOE+1y30G7zU/FeJ5gUsKPRrcYKdjOUBhyY9vinm/3ASH65i5CXiiME7lHtKlCqyfeB7rz4v/zEyBwaLnup72vvKUW7O8ZlxiEYmx2/lzztd0g8A6uTG3Sfj5Vc6v7E9qtdy45OJ5Fze/p3loVqgmSAWWUjoCS2AJLIElsASWwBJYPyZYzINTPeDq5Vf/o+gfmLDVY/wusASWc6MURfc13e0TUdeVXBc8NdgzxVO1TXOZxBJYgQQWeXum/shpYEnKn0RqgnlCnJhJlpnEDu7LZKbN1LO5BPNtUh9keyhWkOFt5BZdwWJpw5itY+wz72xdV3X1nTrvsKCDSz5nusAKWLBIYpCxJ+UHT1ZKB7BYQ0JK54033iB1yBFS0eTOKr2rSoxVM5UHzG/A9BaTXFewMN8e/uBw+/wnc1euOZm+6/uel3ieS3EKgRWgYJnVDVu3biXlB1ikwwhF5A3Nspl169aZtCsvSXpYV2GibOIZjWjHuiV/XaEjCc2Iyl8SOjQl1AnWOIEVsGCZOhSAFRMTY8AiXUhG1jDH4hk84tl/9tlnLZJI9ZO6NuWZSKvRS9YwxnKARRrYlSoWaTUa18gJ1gSBFbBg0a+RwIceygX4rm5g1QM5fOy7yf+bigSkWtu3bw9kHGThJWMy+7pKX7DoDe0rSFlG7Bi5m63jvR1dwlW6wArkp0Jy+AQhxuaAwrIk6zhrZiq9NS9Yo2yl7qlg8NoPjUvMOXV6KmRE32dDteIUMatiQqaFeCZ5qrbUH72ynMA6B+exSB53WNYhuiwaWQTLlzF81zyWwNLMu8ASWAJLYAksgSWwBNZZDBYVms6bdZ7AElgNBhZTpv9Z/R+zaP260uvI89QeCFa4d1jaIXZdLFkg0o6t5rcSWGcpWOh6TSVL5rGYnWIi1OSbzRwV+xy0J20okEnq2sy8M+9l9Pj+wGJJjCMsXbbgMmeicENfZyHMZO8CBzeqrEKHVaLCe9pWu3aCV0JdfXFE46zGjkkNB1i8CzOHR+aU92tuCHN4JgnB28dPwErDm8k/M2NM42S0OgKrWkNGh95ywoQJ6H2ZeafwM/o4FJumij3TodQYp4YqR4ykDrkYR5h5J0WIUuqOO+5AX29VEK48lcS+0ZxGjtnRk6V7i6930RX6lO7FgsH32oSKhLBJYS7poBlVwvyJ2ycW7i30J7FHG0gxYoT2VLnmhuA2wN0gPY8sjLTEkSNHePs5OTnk3UlFcD6ie07mKsT4COA4h3tiWTkIrBONO2J2SAtaKZ2CggKyh4DFygUKjHMEjwZuIjuoOskPGvUmhhnmpb9lM3UoNl5Rq2LjPdb1cL28TU6bGiT2aIEQBfmT2BOnjRycsMRXxRKs3n777SjrgYbklSmLTE1kYxzCZ/eetyUlJZHRIrBx38yPBNbJlpCQUOmV2HOPjMSeHYIQtxuwqD9tVjcQ9pOTk62rUJ0bRTmnQRgq9VqC1TK/pb/VDY1TGp9ydYOv1YzZ2uW1q0Fib/4MwqcrWPRi5NSNxJ5IbCT2mHzwbTFKaCITy4c4s6KiAs8ZcxUwEa1NnnT58uWsB3EMCX7uYGEjQ9Qxocs3Cc1YilvM/ipvM8fJWJu1WcQtktDG6McfWLHlsUhM7ctHXcmIXRsbNDbIScYkJ1j/Lv236+UtZ7Z0XptY1RX+9b6/LnxuYQ0Se0gCFEaNfM18JfasHcKQh5csHLLeLOMHbhc73Aq+VzgfcaHAqvan8H0lSuFkxKCVf60fsTTUYMQJaPC59WbQyrcz0dtwc+CTYCc3N7f2T4Usdfcl4/Lsy51kpLhc6+opgu+Dc5FgUrVwdcqnQtai8R5Z0sg6M6iiQ7QeaxgJ8P0horOM1irSDlJ8D3njRDVW3jLc5Atmvp8C6yebbkBJ0bG4o30lVrvF7U6sZbBv/he8X7HoCrvDFg+JYVlhzss13fCznSDF8ZF5hz8u+GPjzMZ1nYLiEkQ+Vy6+ktFb/XwiBZZSOpp5F1gCS2AJLIElsASWwDojYPFcybwDA/86Za8FlsCqaUOZaJ9/jyyJDJkZciJ7Y23TayXPF1gCy2OXJZJydiawV1zvnLUfV22CFEfdIVuGsMbGH1gkrPARJfFs0oXMfDIjSs7K8iAlr4XhtmXjyxw9k6WcQ37aHMHkl+yWwApUsCJXRrqmdH6f8fsaUjpdVnZpvbB1xlMZ/lY3UAqAeXPSMphDm5QOngOxsbEslQEyssswBHOIew1JnEmSB3f7YcOG8RLL5EsvvZR1HwIrUMGqdxKa0djYrWNdIxZhiZwMeRuy0cABWKTeyW5RmYJUKWAh9TY+7yQN169fb71TeCoqKuIS8jkQJrACGCx/y2b+NPdPLmD9YFZzYeaFU56YQofoChbrFlkhA17kRsm7AxZrrViUxqIrk4TGXcdUpiCRauWnyUbDEzusBKEPjYqKIowJrEAFC6M2V7CapDapwammTWGbof8dWsPqBpZ4EH6o70Lg8V3dAFWcwJIsekz44zgrsbp27coKNjLQlF9gsRrrI/bu3SuwAhUsYo9v1ZMOizqcwMi+pdbtqZBxN90fy61YIMrnYg3JGWkZXIhe9HpmvXKl18up1NuoGWaOMNJnta3ACuDpBuT516y4BhWGqXVINNI8lsBqsA2BhmPMJLAEllI6AktgCSyBJbAElsASWAJLYAksgSWwBJbAElgCS2AJLIElsASWwBJYAktgCSyBJbAElsASWAJLYAksgSWwBNa5DRaL35vkNkFrL7AEVsNsv879dfSqaMsgHn/R+pW3EFgCq2oLywnztYmPWBzh1EAnOw0jsXq/bOFl/sBCWoh1Lw7k5rNBfoMwFf2qXaWDNbKl3qn0llgvKytDHo01q1HsoAMTWIEKFny46govmnqRU1eYZBPml0bGrI0p2FPgT2KPz/u2bduoIYAjA7pCHO23b99OkWxcuNEVUhkAXSHGDYjuja4QRSFuv1RlxxQY7eGtt96KWvVs+EAFVj03X1HhyaoneW1rlthT74TiFK4Ri6rE1MOu9Fp9sG8EqyjuUUUTqAArMzPTVJ2wlND4bxPkELIikt65c+cNN9zAaaY4isA6t8Ca17aGyhQEquTHki+ed7ErWBTPwbvBSOxHjx5tJPbUgCEgGSV0Wlqar3cDJiIEKpSuEIYwGkk0dQYEVqCCdU3xNa5gXZJ2iUu5FFsBAcJVDRL73r17U5GKQRV9oq/Enm5xypQpMARwRgxt7B5MWQr6UPzuqcfOOQIrUMHikTB+Q7yDqi7LupwwxEr8YUvyFh/IqMNTIbjgjzV9+nTwwh+L6kDmuOWPRazC/IOhlTlOz2hKKFBAAGU9piBEOyKfwArg6YZL5l3SfW33k3W/NiYwnHfUi9N0g8Cq58aYKTw3vNk9zepRfEBgCSyldASWwBJYAktgCSyBJbAElsASWAJLYAksgSWwBJbAElgCS2AJLIElsASWwBJYAktgCSyBJbAElsASWAJLYAksgSWwBJbAElgCS2AJLIElsASWwBJYAktgCSyBJbAElsASWAJLYAksgSWw3DYMjyOWR3Rb2+3G1Te2X9o+JDNEYAms0wWraV5T3I7tBlc91/QMnRbqmeip2iZX+fEJrIAHKzs7e9SoUXFxcXhvYvM6cODA4cOHT5s2DT9gvMUwvBswYABHUlJSrEuio6MHDx5cWFjoClabwjZYyjbLa2YdwTq757qevpZ8nZd2rtlEVGAFKlgYtoJIpdfeDnPEnJycHTt28DIhIQGDzfT09Nzc3N27d3MkNTX14MGDlV43WNwTd+3ahRWsK1jBs4IHbRrUIr+F3TbN1esxoSKhUVIjJ1gTBFbgg3XgwAHsEqEKB8Ty8nLAAqkRI0YQw/bt2wdYOMACH2cWFRXhPs0OtpxAlp+fjyu1v67QAVarglauYLGFTgytwVJbYAUqWMePH6enY+fYsWP0d1bEohmwiFhYT/MSi3Pzx+PYyY/YodOsJViYiLpSFbc+LigxyAlWisA6J8ZYixYtAqmRI0du3ryZMZbp+GhmjIUBNR0fMcyyB6ZwA876uL6uXLnSH1gXzLnAUZIkqizKF6y/FfzNJVxlCCw9Fdb6qTA0KzRmTUw12+PiLsFpwZ6pnqotTdMNAqvu81hU7Wq9oPU/l//z78v+Tkdpr0SieSyBpZl3gSWwBFagt6NfHN3/4f6zf+PvFFhqagJL7acF69ChQxTn1L1Qa6gGTkDlocidKdepptYgjRlsoPKQQjl8+DBsKW6pnX6sAiRwYsfDa0rdgRjh639qaqfRQAiQTIT6P6smAX3bjvRbAAAAAElFTkSuQmCC", - "description": "Allows to display state of the GPIO for target Raspberry Pi device using latest attribute values. You should set the label of the selected data key to GPIO pin number (e.g. '1') and use boolean values for widget to display the data.", - "descriptor": { - "type": "rpc", - "sizeX": 6, - "sizeY": 10.5, - "resources": [], - "templateHtml": "
\n
\n
\n
\n {{ cell.label }}\n
\n {{cell.pin}}\n \n \n \n \n {{cell.pin}}\n
\n {{ cell.label }}\n
\n
\n \n \n \n
\n
\n
\n {{rpcErrorText}}\n \n
", - "templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.gpio-panel section[fxflex] {\n min-width: 0px;\n}\n\n.switch-panel {\n margin: 0;\n height: 32px;\n width: 66px;\n min-width: 66px;\n}\n\n.switch-panel mat-slide-toggle {\n margin: 0;\n width: 36px;\n min-width: 36px;\n}\n\n.switch-panel.col-0 mat-slide-toggle {\n margin-left: 8px;\n margin-right: 4px;\n}\n\n.switch-panel.col-1 mat-slide-toggle {\n margin-left: 4px;\n margin-right: 8px;\n}\n\n.gpio-row {\n height: 32px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.switch-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.switch-panel.col-1 .pin {\n margin-right: auto;\n \n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}", - "controllerScript": "var namespace;\nvar cssParser = new cssjs();\n\nself.onInit = function() {\n var utils = self.ctx.$injector.get(self.ctx.servicesMap.get('utils'));\n namespace = 'gpio-control-' + utils.guid();\n cssParser.testMode = false;\n cssParser.cssPreviewNamespace = namespace;\n self.ctx.$container.addClass(namespace);\n self.ctx.ngZone.run(function() {\n init(); \n });\n}\n\nfunction init() {\n \n var i, gpio;\n var scope = self.ctx.$scope;\n var settings = self.ctx.settings;\n scope.gpioList = [];\n for (var g = 0; g < settings.gpioList.length; g++) {\n gpio = settings.gpioList[g];\n scope.gpioList.push(\n {\n row: gpio.row,\n col: gpio.col,\n pin: gpio.pin,\n label: gpio.label,\n enabled: false\n }\n );\n }\n\n scope.requestTimeout = settings.requestTimeout || 1000;\n\n scope.switchPanelBackgroundColor = settings.switchPanelBackgroundColor || tinycolor('green').lighten(2).toRgbString();\n\n scope.gpioStatusRequest = {\n method: \"getGpioStatus\",\n paramsBody: \"{}\"\n };\n \n if (settings.gpioStatusRequest) {\n scope.gpioStatusRequest.method = settings.gpioStatusRequest.method || scope.gpioStatusRequest.method;\n scope.gpioStatusRequest.paramsBody = settings.gpioStatusRequest.paramsBody || scope.gpioStatusRequest.paramsBody;\n }\n \n scope.gpioStatusChangeRequest = {\n method: \"setGpioStatus\",\n paramsBody: \"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"\n };\n \n if (settings.gpioStatusChangeRequest) {\n scope.gpioStatusChangeRequest.method = settings.gpioStatusChangeRequest.method || scope.gpioStatusChangeRequest.method;\n scope.gpioStatusChangeRequest.paramsBody = settings.gpioStatusChangeRequest.paramsBody || scope.gpioStatusChangeRequest.paramsBody;\n }\n \n scope.parseGpioStatusFunction = \"return body[pin] === true;\";\n \n if (settings.parseGpioStatusFunction && settings.parseGpioStatusFunction.length > 0) {\n scope.parseGpioStatusFunction = settings.parseGpioStatusFunction;\n }\n \n scope.parseGpioStatusFunction = new Function(\"body, pin\", scope.parseGpioStatusFunction);\n \n function requestGpioStatus() {\n self.ctx.controlApi.sendTwoWayCommand(scope.gpioStatusRequest.method, \n scope.gpioStatusRequest.paramsBody, \n scope.requestTimeout)\n .subscribe(\n function success(responseBody) {\n for (var g = 0; g < scope.gpioList.length; g++) {\n var gpio = scope.gpioList[g];\n var enabled = scope.parseGpioStatusFunction.apply(this, [responseBody, gpio.pin]);\n gpio.enabled = enabled; \n self.ctx.detectChanges();\n }\n }\n );\n }\n \n function changeGpioStatus(gpio) {\n var pin = gpio.pin + '';\n var enabled = !gpio.enabled;\n enabled = enabled === true ? 'true' : 'false';\n var paramsBody = scope.gpioStatusChangeRequest.paramsBody;\n var requestBody = JSON.parse(paramsBody.replace(\"\\\"{$pin}\\\"\", pin).replace(\"\\\"{$enabled}\\\"\", enabled));\n self.ctx.controlApi.sendTwoWayCommand(scope.gpioStatusChangeRequest.method, \n requestBody, scope.requestTimeout)\n .subscribe(\n function success(responseBody) {\n var enabled = scope.parseGpioStatusFunction.apply(this, [responseBody, gpio.pin]);\n gpio.enabled = enabled;\n self.ctx.detectChanges();\n }\n );\n }\n \n scope.gpioCells = {};\n var rowCount = 0;\n for (i = 0; i < scope.gpioList.length; i++) {\n gpio = scope.gpioList[i];\n scope.gpioCells[gpio.row+'_'+gpio.col] = gpio;\n rowCount = Math.max(rowCount, gpio.row+1);\n }\n \n scope.prefferedRowHeight = 32;\n scope.rows = [];\n for (i = 0; i < rowCount; i++) {\n var row = [];\n for (var c =0; c<2;c++) {\n if (scope.gpioCells[i+'_'+c]) {\n row[c] = scope.gpioCells[i+'_'+c];\n } else {\n row[c] = null;\n }\n }\n scope.rows.push(row);\n }\n\n scope.gpioClick = function($event, gpio) {\n if (scope.rpcEnabled && !scope.executingRpcRequest) {\n changeGpioStatus(gpio);\n }\n };\n \n scope.gpioToggleChange = function($event, gpio) {\n gpio.enabled = !$event.checked;\n $event.source.toggle();\n self.ctx.detectChanges();\n }\n \n if (scope.rpcEnabled) {\n requestGpioStatus(); \n }\n \n self.onResize();\n}\n\nself.onResize = function() {\n var scope = self.ctx.$scope;\n var rowCount = scope.rows.length;\n var prefferedRowHeight = (self.ctx.height - 35)/rowCount;\n prefferedRowHeight = Math.min(32, prefferedRowHeight);\n prefferedRowHeight = Math.max(12, prefferedRowHeight);\n scope.prefferedRowHeight = prefferedRowHeight;\n var ratio = prefferedRowHeight/32;\n \n var css = '.mat-slide-toggle .mat-slide-toggle-bar {\\n' +\n ' height: ' + 14*ratio+'px;\\n'+\n ' width: ' + 36*ratio+'px;\\n'+\n '}\\n';\n css += '.mat-slide-toggle .mat-slide-toggle-thumb-container {\\n' +\n ' height: ' + 20*ratio+'px;\\n'+\n ' width: ' + 20*ratio+'px;\\n'+\n '}\\n';\n css += '.mat-slide-toggle .mat-slide-toggle-thumb {\\n' +\n ' height: ' + 20*ratio+'px;\\n'+\n ' width: ' + 20*ratio+'px;\\n'+\n '}\\n';\n css += '.mat-slide-toggle .mat-slide-toggle-ripple {\\n' +\n ' height: ' + 40*ratio+'px;\\n'+\n ' width: ' + 40*ratio+'px;\\n'+\n ' top: calc(50% - '+20*ratio+'px);\\n'+\n ' left: calc(50% - '+20*ratio+'px);\\n'+\n '}\\n';\n css += '.gpio-left-label, .gpio-right-label {\\n' +\n ' font-size: ' + 16*ratio+'px;\\n'+\n '}\\n';\n var pinsFontSize = Math.max(9, 12*ratio);\n css += '.pin {\\n' +\n ' font-size: ' + pinsFontSize+'px;\\n'+\n '}\\n';\n\n cssParser.createStyleElement(namespace, css);\n \n self.ctx.detectChanges();\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-gpio-control-widget-settings", - "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"parseGpioStatusFunction\":\"return body[pin] === true;\",\"gpioStatusChangeRequest\":{\"method\":\"setGpioStatus\",\"paramsBody\":\"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"},\"requestTimeout\":500,\"switchPanelBackgroundColor\":\"#008a00\",\"gpioStatusRequest\":{\"method\":\"getGpioStatus\",\"paramsBody\":\"{}\"},\"gpioList\":[{\"pin\":7,\"label\":\"GPIO 4 (GPCLK0)\",\"row\":3,\"col\":0,\"_uniqueKey\":0},{\"pin\":11,\"label\":\"GPIO 17\",\"row\":5,\"col\":0,\"_uniqueKey\":1},{\"pin\":12,\"label\":\"GPIO 18\",\"row\":5,\"col\":1,\"_uniqueKey\":2},{\"_uniqueKey\":3,\"pin\":13,\"label\":\"GPIO 27\",\"row\":6,\"col\":0},{\"_uniqueKey\":4,\"pin\":15,\"label\":\"GPIO 22\",\"row\":7,\"col\":0},{\"_uniqueKey\":5,\"pin\":16,\"label\":\"GPIO 23\",\"row\":7,\"col\":1},{\"_uniqueKey\":6,\"pin\":18,\"label\":\"GPIO 24\",\"row\":8,\"col\":1},{\"_uniqueKey\":7,\"pin\":22,\"label\":\"GPIO 25\",\"row\":10,\"col\":1},{\"_uniqueKey\":8,\"pin\":29,\"label\":\"GPIO 5\",\"row\":14,\"col\":0},{\"_uniqueKey\":9,\"pin\":31,\"label\":\"GPIO 6\",\"row\":15,\"col\":0},{\"_uniqueKey\":10,\"pin\":32,\"label\":\"GPIO 12\",\"row\":15,\"col\":1},{\"_uniqueKey\":11,\"pin\":33,\"label\":\"GPIO 13\",\"row\":16,\"col\":0},{\"_uniqueKey\":12,\"pin\":35,\"label\":\"GPIO 19\",\"row\":17,\"col\":0},{\"_uniqueKey\":13,\"pin\":36,\"label\":\"GPIO 16\",\"row\":17,\"col\":1},{\"_uniqueKey\":14,\"pin\":37,\"label\":\"GPIO 26\",\"row\":18,\"col\":0},{\"_uniqueKey\":15,\"pin\":38,\"label\":\"GPIO 20\",\"row\":18,\"col\":1},{\"_uniqueKey\":16,\"pin\":40,\"label\":\"GPIO 21\",\"row\":19,\"col\":1}]},\"title\":\"Raspberry Pi GPIO Control\"}" - } - } + "widgetTypeFqns": [ + "gpio_widgets.basic_gpio_control", + "gpio_widgets.gpio_panel", + "gpio_widgets.raspberry_pi_gpio_panel", + "gpio_widgets.raspberry_pi_gpio_control" ] } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/home_page_widgets.json b/application/src/main/data/json/system/widget_bundles/home_page_widgets.json index 2d382b5958..a30d6ee76f 100644 --- a/application/src/main/data/json/system/widget_bundles/home_page_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/home_page_widgets.json @@ -2,106 +2,17 @@ "widgetsBundle": { "alias": "home_page_widgets", "title": "Home page widgets", - "image": "data:image/png;base64,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", + "image": "data:image/png;base64,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", "description": "Contains useful widgets to design the user's home page.", + "order": 18000, "externalId": null, "name": "Home page widgets" }, - "widgetTypes": [ - { - "alias": "getting_started", - "name": "Getting started", - "image": "data:image/png;base64,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", - "description": "Getting started instruction.", - "descriptor": { - "type": "static", - "sizeX": 7.5, - "sizeY": 6.5, - "resources": [], - "templateHtml": "\n\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "", - "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Getting started\",\"dropShadow\":true}" - } - }, - { - "alias": "documentation_links", - "name": "Documentation links", - "image": "data:image/png;base64,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", - "description": "Configure and display links to documentation and any other resources.", - "descriptor": { - "type": "static", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "\n\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-doc-links-widget-settings", - "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"columns\":3},\"title\":\"Documentation links\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false}" - } - }, - { - "alias": "usage_info", - "name": "Usage info", - "image": "data:image/png;base64,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", - "description": "Display statistics about platform entities and API usage.", - "descriptor": { - "type": "static", - "sizeX": 7.5, - "sizeY": 6.5, - "resources": [], - "templateHtml": "\n\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "", - "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Usage info\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false}" - } - }, - { - "alias": "quick_links", - "name": "Quick links", - "image": "data:image/png;base64,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", - "description": "Configure and display quick links to the platform's UI components.", - "descriptor": { - "type": "static", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "\n\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-quick-links-widget-settings", - "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"columns\":3},\"title\":\"Quick links\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false}" - } - }, - { - "alias": "dashboards", - "name": "Dashboards", - "image": "data:image/png;base64,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", - "description": "Display and manage the 'last viewed' or 'starred' dashboards.", - "descriptor": { - "type": "static", - "sizeX": 7.5, - "sizeY": 6.5, - "resources": [], - "templateHtml": "\n\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "", - "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Dashboards\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false}" - } - } + "widgetTypeFqns": [ + "home_page_widgets.getting_started", + "home_page_widgets.quick_links", + "home_page_widgets.documentation_links", + "home_page_widgets.dashboards", + "home_page_widgets.usage_info" ] } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/html_widgets.json b/application/src/main/data/json/system/widget_bundles/html_widgets.json new file mode 100644 index 0000000000..9293ed5fcb --- /dev/null +++ b/application/src/main/data/json/system/widget_bundles/html_widgets.json @@ -0,0 +1,16 @@ +{ + "widgetsBundle": { + "alias": "html_widgets", + "title": "HTML widgets", + "image": "data:image/png;base64,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", + "description": "Visualize HTML based on a configurable template or function and device attributes or time-series values.", + "order": 21000, + "externalId": null, + "name": "HTML widgets" + }, + "widgetTypeFqns": [ + "cards.html_card", + "cards.html_value_card", + "cards.markdown_card" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/indoor_environment.json b/application/src/main/data/json/system/widget_bundles/indoor_environment.json new file mode 100644 index 0000000000..d13fc5ba66 --- /dev/null +++ b/application/src/main/data/json/system/widget_bundles/indoor_environment.json @@ -0,0 +1,94 @@ +{ + "widgetsBundle": { + "alias": "indoor_environment", + "title": "Indoor Environment", + "image": "data:image/png;base64,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", + "description": "Contains widgets displaying indoor environment telemetry.", + "order": 10000, + "externalId": null, + "name": "Indoor Environment" + }, + "widgetTypeFqns": [ + "indoor_temperature_card", + "indoor_temperature_card_with_background", + "indoor_horizontal_temperature_card", + "indoor_horizontal_temperature_card_with_background", + "indoor_temperature_chart_card", + "indoor_temperature_chart_card_with_background", + "indoor_simple_temperature_chart_card", + "indoor_simple_temperature_chart_card_with_background", + "indoor_temperature_progress_bar", + "indoor_temperature_progress_bar_with_background", + "indoor_temperature_range_chart", + "indoor_temperature_range_chart_with_background", + "indoor_temperature_gauge", + "indoor_humidity_card", + "indoor_humidity_card_with_background", + "indoor_horizontal_humidity_card", + "indoor_horizontal_humidity_card_with_background", + "indoor_humidity_chart_card", + "indoor_humidity_chart_card_with_background", + "indoor_simple_humidity_chart_card", + "indoor_simple_humidity_chart_card_with_background", + "indoor_humidity_progress_bar", + "indoor_humidity_progress_bar_with_background", + "indoor_co2_card", + "indoor_co2_card_with_background", + "indoor_horizontal_co2_card", + "indoor_horizontal_co2_card_with_background", + "indoor_co2_chart_card", + "indoor_co2_chart_card_with_background", + "indoor_simple_co2_chart_card", + "indoor_simple_co2_chart_card_with_background", + "indoor_illuminance_card", + "indoor_illuminance_card_with_background", + "indoor_horizontal_illuminance_card", + "indoor_horizontal_illuminance_card_with_background", + "indoor_illuminance_chart_card", + "indoor_illuminance_chart_card_with_background", + "indoor_simple_illuminance_chart_card", + "indoor_simple_illuminance_chart_card_with_background", + "indoor_illuminance_progress_bar", + "indoor_illuminance_progress_bar_with_background", + "noise_level_card", + "noise_level_card_with_background", + "horizontal_noise_level_card", + "horizontal_noise_level_card_with_background", + "noise_level_chart_card", + "noise_level_chart_card_with_background", + "simple_noise_level_chart_card", + "simple_noise_level_chart_card_with_background", + "indoor_pm2_5_card", + "indoor_pm2_5_card_with_background", + "indoor_horizontal_pm2_5_card", + "indoor_horizontal_pm2_5_card_with_background", + "indoor_pm2_5_chart_card", + "indoor_pm2_5_chart_card_with_background", + "indoor_simple_pm2_5_chart_card", + "indoor_simple_pm2_5_chart_card_with_background", + "indoor_pm10_card", + "indoor_pm10_card_with_background", + "indoor_horizontal_pm10_card", + "indoor_horizontal_pm10_card_with_background", + "indoor_pm10_chart_card", + "indoor_pm10_chart_card_with_background", + "indoor_simple_pm10_chart_card", + "indoor_simple_pm10_chart_card_with_background", + "radon_level_card", + "radon_level_card_with_background", + "horizontal_radon_level_card", + "horizontal_radon_level_card_with_background", + "radon_level_chart_card", + "radon_level_chart_card_with_background", + "simple_radon_level_chart_card", + "simple_radon_level_chart_card_with_background", + "volatile_organic_compounds_card", + "volatile_organic_compounds_card_with_background", + "horizontal_volatile_organic_compounds_card", + "horizontal_volatile_organic_compounds_card_with_background", + "volatile_organic_compounds_chart_card", + "volatile_organic_compounds_chart_card_with_background", + "simple_volatile_organic_compounds_chart_card", + "simple_volatile_organic_compounds_chart_card_with_background" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/industial_widgets.json b/application/src/main/data/json/system/widget_bundles/industial_widgets.json new file mode 100644 index 0000000000..940c4e0d31 --- /dev/null +++ b/application/src/main/data/json/system/widget_bundles/industial_widgets.json @@ -0,0 +1,84 @@ +{ + "widgetsBundle": { + "alias": "industrial_widgets", + "title": "Industrial widgets", + "image": "tb-image:aW5kdXN0cmlhbC13aWRnZXRzLWJ1bmxkZS5zdmc=:aW5kdXN0cmlhbC13aWRnZXRzLWJ1bmxkZS5zdmc=;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iNzYiIHJ4PSI0IiBmaWxsPSJ3aGl0ZSIvPgo8cmVjdCB4PSIwLjUiIHk9IjAuNSIgd2lkdGg9IjE5OSIgaGVpZ2h0PSI3NSIgcng9IjMuNSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiLz4KPHBhdGggZD0iTTEyLjMwMDggMTcuMjIyN1YxOEg5LjI4MTI1VjE3LjIyMjdIMTIuMzAwOFpNOS41NTg1OSAxMi4zMTI1VjE4SDguNTc4MTJWMTIuMzEyNUg5LjU1ODU5Wk0xMS45MDYyIDE0LjY4NzVWMTUuNDUzMUg5LjI4MTI1VjE0LjY4NzVIMTEuOTA2MlpNMTIuMjgxMiAxMi4zMTI1VjEzLjA5MzhIOS4yODEyNVYxMi4zMTI1SDEyLjI4MTJaTTE0LjUzMTIgMThIMTMuNTg5OFYxMy4zNjcyQzEzLjU4OTggMTMuMDUyMSAxMy42NDg0IDEyLjc4NzggMTMuNzY1NiAxMi41NzQyQzEzLjg4NTQgMTIuMzU4MSAxNC4wNTYgMTIuMTk1MyAxNC4yNzczIDEyLjA4NTlDMTQuNDk4NyAxMS45NzQgMTQuNzYwNCAxMS45MTggMTUuMDYyNSAxMS45MThDMTUuMTU2MiAxMS45MTggMTUuMjQ4NyAxMS45MjQ1IDE1LjMzOTggMTEuOTM3NUMxNS40MzEgMTEuOTQ3OSAxNS41MTk1IDExLjk2NDggMTUuNjA1NSAxMS45ODgzTDE1LjU4MiAxMi43MTQ4QzE1LjUyOTkgMTIuNzAxOCAxNS40NzI3IDEyLjY5MjcgMTUuNDEwMiAxMi42ODc1QzE1LjM1MDMgMTIuNjgyMyAxNS4yODUyIDEyLjY3OTcgMTUuMjE0OCAxMi42Nzk3QzE1LjA3MTYgMTIuNjc5NyAxNC45NDc5IDEyLjcwNyAxNC44NDM4IDEyLjc2MTdDMTQuNzQyMiAxMi44MTM4IDE0LjY2NDEgMTIuODkwNiAxNC42MDk0IDEyLjk5MjJDMTQuNTU3MyAxMy4wOTM4IDE0LjUzMTIgMTMuMjE4OCAxNC41MzEyIDEzLjM2NzJWMThaTTE1LjQyNTggMTMuNzczNFYxNC40NjA5SDEyLjk0MTRWMTMuNzczNEgxNS40MjU4Wk0xNi45NTMxIDE4SDE2LjAxMTdWMTMuNUMxNi4wMTE3IDEzLjE1ODkgMTYuMDc5NCAxMi44NzExIDE2LjIxNDggMTIuNjM2N0MxNi4zNTAzIDEyLjQwMjMgMTYuNTQ1NiAxMi4yMjQgMTYuODAwOCAxMi4xMDE2QzE3LjA1ODYgMTEuOTc5MiAxNy4zNjQ2IDExLjkxOCAxNy43MTg4IDExLjkxOEMxNy45MjcxIDExLjkxOCAxOC4xMjg5IDExLjk0MDEgMTguMzI0MiAxMS45ODQ0QzE4LjUxOTUgMTIuMDI4NiAxOC43MjAxIDEyLjA4NDYgMTguOTI1OCAxMi4xNTIzTDE4Ljc4MTIgMTIuOTE4QzE4LjY1MSAxMi44NzM3IDE4LjUwMzkgMTIuODMyIDE4LjMzOTggMTIuNzkzQzE4LjE3ODQgMTIuNzUxMyAxNy45OTM1IDEyLjczMDUgMTcuNzg1MiAxMi43MzA1QzE3LjUwMTMgMTIuNzMwNSAxNy4yOTE3IDEyLjc5NTYgMTcuMTU2MiAxMi45MjU4QzE3LjAyMDggMTMuMDUzNCAxNi45NTMxIDEzLjI0NDggMTYuOTUzMSAxMy41VjE4Wk0xNy44MDQ3IDEzLjc3MzRWMTQuNDYwOUgxNS4zNjMzVjEzLjc3MzRIMTcuODA0N1pNMTkuMzc1IDEzLjc3MzRWMThIMTguNDI5N1YxMy43NzM0SDE5LjM3NVpNMjIuMzkwNiAxNy4zMjgxQzIyLjU0NDMgMTcuMzI4MSAyMi42ODIzIDE3LjI5ODIgMjIuODA0NyAxNy4yMzgzQzIyLjkyOTcgMTcuMTc1OCAyMy4wMjk5IDE3LjA4OTggMjMuMTA1NSAxNi45ODA1QzIzLjE4MzYgMTYuODcxMSAyMy4yMjY2IDE2Ljc0NDggMjMuMjM0NCAxNi42MDE2SDI0LjEyMTFDMjQuMTE1OSAxNi44NzUgMjQuMDM1MiAxNy4xMjM3IDIzLjg3ODkgMTcuMzQ3N0MyMy43MjI3IDE3LjU3MTYgMjMuNTE1NiAxNy43NSAyMy4yNTc4IDE3Ljg4MjhDMjMgMTguMDEzIDIyLjcxNDggMTguMDc4MSAyMi40MDIzIDE4LjA3ODFDMjIuMDc5NCAxOC4wNzgxIDIxLjc5ODIgMTguMDIzNCAyMS41NTg2IDE3LjkxNDFDMjEuMzE5IDE3LjgwMjEgMjEuMTE5OCAxNy42NDg0IDIwLjk2MDkgMTcuNDUzMUMyMC44MDIxIDE3LjI1NzggMjAuNjgyMyAxNy4wMzI2IDIwLjYwMTYgMTYuNzc3M0MyMC41MjM0IDE2LjUyMjEgMjAuNDg0NCAxNi4yNDg3IDIwLjQ4NDQgMTUuOTU3VjE1LjgyMDNDMjAuNDg0NCAxNS41Mjg2IDIwLjUyMzQgMTUuMjU1MiAyMC42MDE2IDE1QzIwLjY4MjMgMTQuNzQyMiAyMC44MDIxIDE0LjUxNTYgMjAuOTYwOSAxNC4zMjAzQzIxLjExOTggMTQuMTI1IDIxLjMxOSAxMy45NzI3IDIxLjU1ODYgMTMuODYzM0MyMS43OTgyIDEzLjc1MTMgMjIuMDc4MSAxMy42OTUzIDIyLjM5ODQgMTMuNjk1M0MyMi43MzcgMTMuNjk1MyAyMy4wMzM5IDEzLjc2MyAyMy4yODkxIDEzLjg5ODRDMjMuNTQ0MyAxNC4wMzEyIDIzLjc0NDggMTQuMjE3NCAyMy44OTA2IDE0LjQ1N0MyNC4wMzkxIDE0LjY5NCAyNC4xMTU5IDE0Ljk3MDEgMjQuMTIxMSAxNS4yODUySDIzLjIzNDRDMjMuMjI2NiAxNS4xMjg5IDIzLjE4NzUgMTQuOTg4MyAyMy4xMTcyIDE0Ljg2MzNDMjMuMDQ5NSAxNC43MzU3IDIyLjk1MzEgMTQuNjM0MSAyMi44MjgxIDE0LjU1ODZDMjIuNzA1NyAxNC40ODMxIDIyLjU1ODYgMTQuNDQ1MyAyMi4zODY3IDE0LjQ0NTNDMjIuMTk2NiAxNC40NDUzIDIyLjAzOTEgMTQuNDg0NCAyMS45MTQxIDE0LjU2MjVDMjEuNzg5MSAxNC42MzggMjEuNjkxNCAxNC43NDIyIDIxLjYyMTEgMTQuODc1QzIxLjU1MDggMTUuMDA1MiAyMS41IDE1LjE1MjMgMjEuNDY4OCAxNS4zMTY0QzIxLjQ0MDEgMTUuNDc3OSAyMS40MjU4IDE1LjY0NTggMjEuNDI1OCAxNS44MjAzVjE1Ljk1N0MyMS40MjU4IDE2LjEzMTUgMjEuNDQwMSAxNi4zMDA4IDIxLjQ2ODggMTYuNDY0OEMyMS40OTc0IDE2LjYyODkgMjEuNTQ2OSAxNi43NzYgMjEuNjE3MiAxNi45MDYyQzIxLjY5MDEgMTcuMDMzOSAyMS43ODkxIDE3LjEzNjcgMjEuOTE0MSAxNy4yMTQ4QzIyLjAzOTEgMTcuMjkwNCAyMi4xOTc5IDE3LjMyODEgMjIuMzkwNiAxNy4zMjgxWk0yNi4xMDE2IDEzLjc3MzRWMThIMjUuMTU2MlYxMy43NzM0SDI2LjEwMTZaTTI1LjA5MzggMTIuNjY0MUMyNS4wOTM4IDEyLjUyMDggMjUuMTQwNiAxMi40MDIzIDI1LjIzNDQgMTIuMzA4NkMyNS4zMzA3IDEyLjIxMjIgMjUuNDYzNSAxMi4xNjQxIDI1LjYzMjggMTIuMTY0MUMyNS43OTk1IDEyLjE2NDEgMjUuOTMxIDEyLjIxMjIgMjYuMDI3MyAxMi4zMDg2QzI2LjEyMzcgMTIuNDAyMyAyNi4xNzE5IDEyLjUyMDggMjYuMTcxOSAxMi42NjQxQzI2LjE3MTkgMTIuODA0NyAyNi4xMjM3IDEyLjkyMTkgMjYuMDI3MyAxMy4wMTU2QzI1LjkzMSAxMy4xMDk0IDI1Ljc5OTUgMTMuMTU2MiAyNS42MzI4IDEzLjE1NjJDMjUuNDYzNSAxMy4xNTYyIDI1LjMzMDcgMTMuMTA5NCAyNS4yMzQ0IDEzLjAxNTZDMjUuMTQwNiAxMi45MjE5IDI1LjA5MzggMTIuODA0NyAyNS4wOTM4IDEyLjY2NDFaTTI5LjI0NjEgMTguMDc4MUMyOC45MzM2IDE4LjA3ODEgMjguNjUxIDE4LjAyNzMgMjguMzk4NCAxNy45MjU4QzI4LjE0ODQgMTcuODIxNiAyNy45MzQ5IDE3LjY3NzEgMjcuNzU3OCAxNy40OTIyQzI3LjU4MzMgMTcuMzA3MyAyNy40NDkyIDE3LjA4OTggMjcuMzU1NSAxNi44Mzk4QzI3LjI2MTcgMTYuNTg5OCAyNy4yMTQ4IDE2LjMyMDMgMjcuMjE0OCAxNi4wMzEyVjE1Ljg3NUMyNy4yMTQ4IDE1LjU0NDMgMjcuMjYzIDE1LjI0NDggMjcuMzU5NCAxNC45NzY2QzI3LjQ1NTcgMTQuNzA4MyAyNy41ODk4IDE0LjQ3OTIgMjcuNzYxNyAxNC4yODkxQzI3LjkzMzYgMTQuMDk2NCAyOC4xMzY3IDEzLjk0OTIgMjguMzcxMSAxMy44NDc3QzI4LjYwNTUgMTMuNzQ2MSAyOC44NTk0IDEzLjY5NTMgMjkuMTMyOCAxMy42OTUzQzI5LjQzNDkgMTMuNjk1MyAyOS42OTkyIDEzLjc0NjEgMjkuOTI1OCAxMy44NDc3QzMwLjE1MjMgMTMuOTQ5MiAzMC4zMzk4IDE0LjA5MjQgMzAuNDg4MyAxNC4yNzczQzMwLjYzOTMgMTQuNDU5NiAzMC43NTEzIDE0LjY3NzEgMzAuODI0MiAxNC45Mjk3QzMwLjg5OTcgMTUuMTgyMyAzMC45Mzc1IDE1LjQ2MDkgMzAuOTM3NSAxNS43NjU2VjE2LjE2OEgyNy42NzE5VjE1LjQ5MjJIMzAuMDA3OFYxNS40MThDMzAuMDAyNiAxNS4yNDg3IDI5Ljk2ODggMTUuMDg5OCAyOS45MDYyIDE0Ljk0MTRDMjkuODQ2NCAxNC43OTMgMjkuNzUzOSAxNC42NzMyIDI5LjYyODkgMTQuNTgyQzI5LjUwMzkgMTQuNDkwOSAyOS4zMzcyIDE0LjQ0NTMgMjkuMTI4OSAxNC40NDUzQzI4Ljk3MjcgMTQuNDQ1MyAyOC44MzMzIDE0LjQ3OTIgMjguNzEwOSAxNC41NDY5QzI4LjU5MTEgMTQuNjEyIDI4LjQ5MDkgMTQuNzA3IDI4LjQxMDIgMTQuODMyQzI4LjMyOTQgMTQuOTU3IDI4LjI2NjkgMTUuMTA4MSAyOC4yMjI3IDE1LjI4NTJDMjguMTgxIDE1LjQ1OTYgMjguMTYwMiAxNS42NTYyIDI4LjE2MDIgMTUuODc1VjE2LjAzMTJDMjguMTYwMiAxNi4yMTYxIDI4LjE4NDkgMTYuMzg4IDI4LjIzNDQgMTYuNTQ2OUMyOC4yODY1IDE2LjcwMzEgMjguMzYyIDE2LjgzOTggMjguNDYwOSAxNi45NTdDMjguNTU5OSAxNy4wNzQyIDI4LjY3OTcgMTcuMTY2NyAyOC44MjAzIDE3LjIzNDRDMjguOTYwOSAxNy4yOTk1IDI5LjEyMTEgMTcuMzMyIDI5LjMwMDggMTcuMzMyQzI5LjUyNzMgMTcuMzMyIDI5LjcyOTIgMTcuMjg2NSAyOS45MDYyIDE3LjE5NTNDMzAuMDgzMyAxNy4xMDQyIDMwLjIzNyAxNi45NzUzIDMwLjM2NzIgMTYuODA4NkwzMC44NjMzIDE3LjI4OTFDMzAuNzcyMSAxNy40MjE5IDMwLjY1MzYgMTcuNTQ5NSAzMC41MDc4IDE3LjY3MTlDMzAuMzYyIDE3Ljc5MTcgMzAuMTgzNiAxNy44ODkzIDI5Ljk3MjcgMTcuOTY0OEMyOS43NjQzIDE4LjA0MDQgMjkuNTIyMSAxOC4wNzgxIDI5LjI0NjEgMTguMDc4MVpNMzIuODU1NSAxNC42NzU4VjE4SDMxLjkxNDFWMTMuNzczNEgzMi44MDA4TDMyLjg1NTUgMTQuNjc1OFpNMzIuNjg3NSAxNS43MzA1TDMyLjM4MjggMTUuNzI2NkMzMi4zODU0IDE1LjQyNzEgMzIuNDI3MSAxNS4xNTIzIDMyLjUwNzggMTQuOTAyM0MzMi41OTExIDE0LjY1MjMgMzIuNzA1NyAxNC40Mzc1IDMyLjg1MTYgMTQuMjU3OEMzMyAxNC4wNzgxIDMzLjE3NzEgMTMuOTQwMSAzMy4zODI4IDEzLjg0MzhDMzMuNTg4NSAxMy43NDQ4IDMzLjgxNzcgMTMuNjk1MyAzNC4wNzAzIDEzLjY5NTNDMzQuMjczNCAxMy42OTUzIDM0LjQ1NyAxMy43MjQgMzQuNjIxMSAxMy43ODEyQzM0Ljc4NzggMTMuODM1OSAzNC45Mjk3IDEzLjkyNTggMzUuMDQ2OSAxNC4wNTA4QzM1LjE2NjcgMTQuMTc1OCAzNS4yNTc4IDE0LjMzODUgMzUuMzIwMyAxNC41MzkxQzM1LjM4MjggMTQuNzM3IDM1LjQxNDEgMTQuOTgwNSAzNS40MTQxIDE1LjI2OTVWMThIMzQuNDY4OFYxNS4yNjU2QzM0LjQ2ODggMTUuMDYyNSAzNC40Mzg4IDE0LjkwMjMgMzQuMzc4OSAxNC43ODUyQzM0LjMyMTYgMTQuNjY1NCAzNC4yMzcgMTQuNTgwNyAzNC4xMjUgMTQuNTMxMkMzNC4wMTU2IDE0LjQ3OTIgMzMuODc4OSAxNC40NTMxIDMzLjcxNDggMTQuNDUzMUMzMy41NTM0IDE0LjQ1MzEgMzMuNDA4OSAxNC40ODcgMzMuMjgxMiAxNC41NTQ3QzMzLjE1MzYgMTQuNjIyNCAzMy4wNDU2IDE0LjcxNDggMzIuOTU3IDE0LjgzMkMzMi44NzExIDE0Ljk0OTIgMzIuODA0NyAxNS4wODQ2IDMyLjc1NzggMTUuMjM4M0MzMi43MTA5IDE1LjM5MTkgMzIuNjg3NSAxNS41NTYgMzIuNjg3NSAxNS43MzA1Wk0zOC4zNTE2IDE3LjMyODFDMzguNTA1MiAxNy4zMjgxIDM4LjY0MzIgMTcuMjk4MiAzOC43NjU2IDE3LjIzODNDMzguODkwNiAxNy4xNzU4IDM4Ljk5MDkgMTcuMDg5OCAzOS4wNjY0IDE2Ljk4MDVDMzkuMTQ0NSAxNi44NzExIDM5LjE4NzUgMTYuNzQ0OCAzOS4xOTUzIDE2LjYwMTZINDAuMDgyQzQwLjA3NjggMTYuODc1IDM5Ljk5NjEgMTcuMTIzNyAzOS44Mzk4IDE3LjM0NzdDMzkuNjgzNiAxNy41NzE2IDM5LjQ3NjYgMTcuNzUgMzkuMjE4OCAxNy44ODI4QzM4Ljk2MDkgMTguMDEzIDM4LjY3NTggMTguMDc4MSAzOC4zNjMzIDE4LjA3ODFDMzguMDQwNCAxOC4wNzgxIDM3Ljc1OTEgMTguMDIzNCAzNy41MTk1IDE3LjkxNDFDMzcuMjc5OSAxNy44MDIxIDM3LjA4MDcgMTcuNjQ4NCAzNi45MjE5IDE3LjQ1MzFDMzYuNzYzIDE3LjI1NzggMzYuNjQzMiAxNy4wMzI2IDM2LjU2MjUgMTYuNzc3M0MzNi40ODQ0IDE2LjUyMjEgMzYuNDQ1MyAxNi4yNDg3IDM2LjQ0NTMgMTUuOTU3VjE1LjgyMDNDMzYuNDQ1MyAxNS41Mjg2IDM2LjQ4NDQgMTUuMjU1MiAzNi41NjI1IDE1QzM2LjY0MzIgMTQuNzQyMiAzNi43NjMgMTQuNTE1NiAzNi45MjE5IDE0LjMyMDNDMzcuMDgwNyAxNC4xMjUgMzcuMjc5OSAxMy45NzI3IDM3LjUxOTUgMTMuODYzM0MzNy43NTkxIDEzLjc1MTMgMzguMDM5MSAxMy42OTUzIDM4LjM1OTQgMTMuNjk1M0MzOC42OTc5IDEzLjY5NTMgMzguOTk0OCAxMy43NjMgMzkuMjUgMTMuODk4NEMzOS41MDUyIDE0LjAzMTIgMzkuNzA1NyAxNC4yMTc0IDM5Ljg1MTYgMTQuNDU3QzQwIDE0LjY5NCA0MC4wNzY4IDE0Ljk3MDEgNDAuMDgyIDE1LjI4NTJIMzkuMTk1M0MzOS4xODc1IDE1LjEyODkgMzkuMTQ4NCAxNC45ODgzIDM5LjA3ODEgMTQuODYzM0MzOS4wMTA0IDE0LjczNTcgMzguOTE0MSAxNC42MzQxIDM4Ljc4OTEgMTQuNTU4NkMzOC42NjY3IDE0LjQ4MzEgMzguNTE5NSAxNC40NDUzIDM4LjM0NzcgMTQuNDQ1M0MzOC4xNTc2IDE0LjQ0NTMgMzggMTQuNDg0NCAzNy44NzUgMTQuNTYyNUMzNy43NSAxNC42MzggMzcuNjUyMyAxNC43NDIyIDM3LjU4MiAxNC44NzVDMzcuNTExNyAxNS4wMDUyIDM3LjQ2MDkgMTUuMTUyMyAzNy40Mjk3IDE1LjMxNjRDMzcuNDAxIDE1LjQ3NzkgMzcuMzg2NyAxNS42NDU4IDM3LjM4NjcgMTUuODIwM1YxNS45NTdDMzcuMzg2NyAxNi4xMzE1IDM3LjQwMSAxNi4zMDA4IDM3LjQyOTcgMTYuNDY0OEMzNy40NTgzIDE2LjYyODkgMzcuNTA3OCAxNi43NzYgMzcuNTc4MSAxNi45MDYyQzM3LjY1MSAxNy4wMzM5IDM3Ljc1IDE3LjEzNjcgMzcuODc1IDE3LjIxNDhDMzggMTcuMjkwNCAzOC4xNTg5IDE3LjMyODEgMzguMzUxNiAxNy4zMjgxWk00Mi4yODEyIDE3LjUzOTFMNDMuNDI5NyAxMy43NzM0SDQ0LjQzNzVMNDIuNzQyMiAxOC42NDQ1QzQyLjcwMzEgMTguNzQ4NyA0Mi42NTIzIDE4Ljg2MiA0Mi41ODk4IDE4Ljk4NDRDNDIuNTI3MyAxOS4xMDY4IDQyLjQ0NTMgMTkuMjIyNyA0Mi4zNDM4IDE5LjMzMkM0Mi4yNDQ4IDE5LjQ0NCA0Mi4xMjExIDE5LjUzMzkgNDEuOTcyNyAxOS42MDE2QzQxLjgyNDIgMTkuNjcxOSA0MS42NDQ1IDE5LjcwNyA0MS40MzM2IDE5LjcwN0M0MS4zNTAzIDE5LjcwNyA0MS4yNjk1IDE5LjY5OTIgNDEuMTkxNCAxOS42ODM2QzQxLjExNTkgMTkuNjcwNiA0MS4wNDQzIDE5LjY1NjIgNDAuOTc2NiAxOS42NDA2TDQwLjk3MjcgMTguOTIxOUM0MC45OTg3IDE4LjkyNDUgNDEuMDI5OSAxOC45MjcxIDQxLjA2NjQgMTguOTI5N0M0MS4xMDU1IDE4LjkzMjMgNDEuMTM2NyAxOC45MzM2IDQxLjE2MDIgMTguOTMzNkM0MS4zMTY0IDE4LjkzMzYgNDEuNDQ2NiAxOC45MTQxIDQxLjU1MDggMTguODc1QzQxLjY1NDkgMTguODM4NSA0MS43Mzk2IDE4Ljc3ODYgNDEuODA0NyAxOC42OTUzQzQxLjg3MjQgMTguNjEyIDQxLjkyOTcgMTguNSA0MS45NzY2IDE4LjM1OTRMNDIuMjgxMiAxNy41MzkxWk00MS42MzI4IDEzLjc3MzRMNDIuNjM2NyAxNi45Mzc1TDQyLjgwNDcgMTcuOTI5N0w0Mi4xNTIzIDE4LjA5NzdMNDAuNjE3MiAxMy43NzM0SDQxLjYzMjhaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0yMC42MjExIDQ4LjkzNzVWNTAuNDg0NEwxMy44MDA4IDY2SDEwLjgyNDJMMTcuNjMyOCA1MS4xODc1SDguNzk2ODhWNDguOTM3NUgyMC42MjExWk0zNC4wNTA4IDU2LjAzOTFWNTguODI4MUMzNC4wNTA4IDYwLjE2NDEgMzMuOTE4IDYxLjMwNDcgMzMuNjUyMyA2Mi4yNUMzMy4zOTQ1IDYzLjE4NzUgMzMuMDE5NSA2My45NDkyIDMyLjUyNzMgNjQuNTM1MkMzMi4wMzUyIDY1LjEyMTEgMzEuNDQ1MyA2NS41NTA4IDMwLjc1NzggNjUuODI0MkMzMC4wNzgxIDY2LjA5NzcgMjkuMzE2NCA2Ni4yMzQ0IDI4LjQ3MjcgNjYuMjM0NEMyNy44MDA4IDY2LjIzNDQgMjcuMTc1OCA2Ni4xNDg0IDI2LjU5NzcgNjUuOTc2NkMyNi4wMjczIDY1LjgwNDcgMjUuNTExNyA2NS41MzUyIDI1LjA1MDggNjUuMTY4QzI0LjU4OTggNjQuODAwOCAyNC4xOTUzIDY0LjMyODEgMjMuODY3MiA2My43NUMyMy41NDY5IDYzLjE2NDEgMjMuMjk2OSA2Mi40NjQ4IDIzLjExNzIgNjEuNjUyM0MyMi45NDUzIDYwLjgzOTggMjIuODU5NCA1OS44OTg0IDIyLjg1OTQgNTguODI4MVY1Ni4wMzkxQzIyLjg1OTQgNTQuNjk1MyAyMi45OTIyIDUzLjU2MjUgMjMuMjU3OCA1Mi42NDA2QzIzLjUyMzQgNTEuNzEwOSAyMy45MDIzIDUwLjk1NyAyNC4zOTQ1IDUwLjM3ODlDMjQuODg2NyA0OS43OTMgMjUuNDcyNyA0OS4zNjcyIDI2LjE1MjMgNDkuMTAxNkMyNi44Mzk4IDQ4LjgzNTkgMjcuNjA1NSA0OC43MDMxIDI4LjQ0OTIgNDguNzAzMUMyOS4xMjg5IDQ4LjcwMzEgMjkuNzUzOSA0OC43ODkxIDMwLjMyNDIgNDguOTYwOUMzMC45MDIzIDQ5LjEyNSAzMS40MTggNDkuMzg2NyAzMS44NzExIDQ5Ljc0NjFDMzIuMzMyIDUwLjEwNTUgMzIuNzIyNyA1MC41NzQyIDMzLjA0MyA1MS4xNTIzQzMzLjM3MTEgNTEuNzIyNyAzMy42MjExIDUyLjQxNDEgMzMuNzkzIDUzLjIyNjZDMzMuOTY0OCA1NC4wMzEyIDM0LjA1MDggNTQuOTY4OCAzNC4wNTA4IDU2LjAzOTFaTTMxLjIyNjYgNTkuMjI2NlY1NS42MTcyQzMxLjIyNjYgNTQuOTM3NSAzMS4xODc1IDU0LjMzOTggMzEuMTA5NCA1My44MjQyQzMxLjAzMTIgNTMuMzAwOCAzMC45MTQxIDUyLjg1OTQgMzAuNzU3OCA1Mi41QzMwLjYwOTQgNTIuMTMyOCAzMC40MjE5IDUxLjgzNTkgMzAuMTk1MyA1MS42MDk0QzI5Ljk2ODggNTEuMzc1IDI5LjcxMDkgNTEuMjA3IDI5LjQyMTkgNTEuMTA1NUMyOS4xMzI4IDUwLjk5NjEgMjguODA4NiA1MC45NDE0IDI4LjQ0OTIgNTAuOTQxNEMyOC4wMTE3IDUwLjk0MTQgMjcuNjIxMSA1MS4wMjczIDI3LjI3NzMgNTEuMTk5MkMyNi45MzM2IDUxLjM2MzMgMjYuNjQ0NSA1MS42Mjg5IDI2LjQxMDIgNTEuOTk2MUMyNi4xNzU4IDUyLjM2MzMgMjUuOTk2MSA1Mi44NDc3IDI1Ljg3MTEgNTMuNDQ5MkMyNS43NTM5IDU0LjA0MyAyNS42OTUzIDU0Ljc2NTYgMjUuNjk1MyA1NS42MTcyVjU5LjIyNjZDMjUuNjk1MyA1OS45MTQxIDI1LjczNDQgNjAuNTE5NSAyNS44MTI1IDYxLjA0M0MyNS44OTA2IDYxLjU2NjQgMjYuMDA3OCA2Mi4wMTU2IDI2LjE2NDEgNjIuMzkwNkMyNi4zMjAzIDYyLjc1NzggMjYuNTA3OCA2My4wNjI1IDI2LjcyNjYgNjMuMzA0N0MyNi45NTMxIDYzLjUzOTEgMjcuMjEwOSA2My43MTA5IDI3LjUgNjMuODIwM0MyNy43OTY5IDYzLjkyOTcgMjguMTIxMSA2My45ODQ0IDI4LjQ3MjcgNjMuOTg0NEMyOC45MTggNjMuOTg0NCAyOS4zMTI1IDYzLjg5ODQgMjkuNjU2MiA2My43MjY2QzMwIDYzLjU1NDcgMzAuMjg5MSA2My4yODEyIDMwLjUyMzQgNjIuOTA2MkMzMC43NTc4IDYyLjUyMzQgMzAuOTMzNiA2Mi4wMjczIDMxLjA1MDggNjEuNDE4QzMxLjE2OCA2MC44MDg2IDMxLjIyNjYgNjAuMDc4MSAzMS4yMjY2IDU5LjIyNjZaIiBmaWxsPSIjM0ZBNzFBIi8+CjxwYXRoIGQ9Ik0zOS4xNTcyIDU4LjQ4NzNWNTcuOTYwOUMzOS4xNTcyIDU3LjU4MjcgMzkuMjM5MyA1Ny4yMzg2IDM5LjQwMzMgNTYuOTI4N0MzOS41Njc0IDU2LjYxODggMzkuODA2NiA1Ni4zNzA0IDQwLjEyMTEgNTYuMTgzNkM0MC40MzU1IDU1Ljk5NjcgNDAuODEzOCA1NS45MDMzIDQxLjI1NTkgNTUuOTAzM0M0MS43MTE2IDU1LjkwMzMgNDIuMDk0NCA1NS45OTY3IDQyLjQwNDMgNTYuMTgzNkM0Mi43MTg4IDU2LjM3MDQgNDIuOTU4IDU2LjYxODggNDMuMTIyMSA1Ni45Mjg3QzQzLjI4NjEgNTcuMjM4NiA0My4zNjgyIDU3LjU4MjcgNDMuMzY4MiA1Ny45NjA5VjU4LjQ4NzNDNDMuMzY4MiA1OC44NTY0IDQzLjI4NjEgNTkuMTk2IDQzLjEyMjEgNTkuNTA1OUM0Mi45NjI2IDU5LjgxNTggNDIuNzI1NiA2MC4wNjQxIDQyLjQxMTEgNjAuMjUxQzQyLjEwMTIgNjAuNDM3OCA0MS43MjA3IDYwLjUzMTIgNDEuMjY5NSA2MC41MzEyQzQwLjgyMjkgNjAuNTMxMiA0MC40NDAxIDYwLjQzNzggNDAuMTIxMSA2MC4yNTFDMzkuODA2NiA2MC4wNjQxIDM5LjU2NzQgNTkuODE1OCAzOS40MDMzIDU5LjUwNTlDMzkuMjM5MyA1OS4xOTYgMzkuMTU3MiA1OC44NTY0IDM5LjE1NzIgNTguNDg3M1pNNDAuMjk4OCA1Ny45NjA5VjU4LjQ4NzNDNDAuMjk4OCA1OC42Njk2IDQwLjMzMyA1OC44NDI4IDQwLjQwMTQgNTkuMDA2OEM0MC40NzQzIDU5LjE3MDkgNDAuNTgzNyA1OS4zMDMxIDQwLjcyOTUgNTkuNDAzM0M0MC44NzUzIDU5LjUwMzYgNDEuMDU1MyA1OS41NTM3IDQxLjI2OTUgNTkuNTUzN0M0MS40ODgzIDU5LjU1MzcgNDEuNjY2IDU5LjUwMzYgNDEuODAyNyA1OS40MDMzQzQxLjk0NCA1OS4zMDMxIDQyLjA0ODggNTkuMTcwOSA0Mi4xMTcyIDU5LjAwNjhDNDIuMTg1NSA1OC44NDI4IDQyLjIxOTcgNTguNjY5NiA0Mi4yMTk3IDU4LjQ4NzNWNTcuOTYwOUM0Mi4yMTk3IDU3Ljc3NDEgNDIuMTgzMyA1Ny41OTg2IDQyLjExMDQgNTcuNDM0NkM0Mi4wNDIgNTcuMjY2IDQxLjkzNzIgNTcuMTMxNSA0MS43OTU5IDU3LjAzMTJDNDEuNjU0NiA1Ni45MzEgNDEuNDc0NiA1Ni44ODA5IDQxLjI1NTkgNTYuODgwOUM0MS4wNDYyIDU2Ljg4MDkgNDAuODY4NSA1Ni45MzEgNDAuNzIyNyA1Ny4wMzEyQzQwLjU4MTQgNTcuMTMxNSA0MC40NzQzIDU3LjI2NiA0MC40MDE0IDU3LjQzNDZDNDAuMzMzIDU3LjU5ODYgNDAuMjk4OCA1Ny43NzQxIDQwLjI5ODggNTcuOTYwOVpNNDMuOTU2MSA2NC4wOTI4VjYzLjU1OTZDNDMuOTU2MSA2My4xODU5IDQ0LjAzODEgNjIuODQ0MSA0NC4yMDIxIDYyLjUzNDJDNDQuMzcwOCA2Mi4yMjQzIDQ0LjYxMjMgNjEuOTc1OSA0NC45MjY4IDYxLjc4OTFDNDUuMjQxMiA2MS42MDIyIDQ1LjYxOTUgNjEuNTA4OCA0Ni4wNjE1IDYxLjUwODhDNDYuNTE3MyA2MS41MDg4IDQ2LjkwMDEgNjEuNjAyMiA0Ny4yMSA2MS43ODkxQzQ3LjUyNDQgNjEuOTc1OSA0Ny43NjE0IDYyLjIyNDMgNDcuOTIwOSA2Mi41MzQyQzQ4LjA4NSA2Mi44NDQxIDQ4LjE2NyA2My4xODU5IDQ4LjE2NyA2My41NTk2VjY0LjA5MjhDNDguMTY3IDY0LjQ2NjUgNDguMDg1IDY0LjgwODMgNDcuOTIwOSA2NS4xMTgyQzQ3Ljc2MTQgNjUuNDI4MSA0Ny41MjY3IDY1LjY3NjQgNDcuMjE2OCA2NS44NjMzQzQ2LjkwNjkgNjYuMDUwMSA0Ni41Mjg2IDY2LjE0MzYgNDYuMDgyIDY2LjE0MzZDNDUuNjMwOSA2Ni4xNDM2IDQ1LjI0NTggNjYuMDUwMSA0NC45MjY4IDY1Ljg2MzNDNDQuNjEyMyA2NS42NzY0IDQ0LjM3MDggNjUuNDI4MSA0NC4yMDIxIDY1LjExODJDNDQuMDM4MSA2NC44MDgzIDQzLjk1NjEgNjQuNDY2NSA0My45NTYxIDY0LjA5MjhaTTQ1LjEwNDUgNjMuNTU5NlY2NC4wOTI4QzQ1LjEwNDUgNjQuMjc1MSA0NS4xNDEgNjQuNDQ4MiA0NS4yMTM5IDY0LjYxMjNDNDUuMjkxMyA2NC43NzY0IDQ1LjQwMyA2NC45MTA4IDQ1LjU0ODggNjUuMDE1NkM0NS42OTQ3IDY1LjExNTkgNDUuODcwMSA2NS4xNjYgNDYuMDc1MiA2NS4xNjZDNDYuMzA3NiA2NS4xNjYgNDYuNDk0NSA2NS4xMTU5IDQ2LjYzNTcgNjUuMDE1NkM0Ni43NzcgNjQuOTEwOCA0Ni44NzczIDY0Ljc3ODYgNDYuOTM2NSA2NC42MTkxQzQ3LjAwMDMgNjQuNDU1MSA0Ny4wMzIyIDY0LjI3OTYgNDcuMDMyMiA2NC4wOTI4VjYzLjU1OTZDNDcuMDMyMiA2My4zNzI3IDQ2Ljk5NTggNjMuMTk3MyA0Ni45MjI5IDYzLjAzMzJDNDYuODU0NSA2Mi44NjkxIDQ2Ljc0NzQgNjIuNzM3IDQ2LjYwMTYgNjIuNjM2N0M0Ni40NjAzIDYyLjUzNjUgNDYuMjgwMyA2Mi40ODYzIDQ2LjA2MTUgNjIuNDg2M0M0NS44NDczIDYyLjQ4NjMgNDUuNjY5NiA2Mi41MzY1IDQ1LjUyODMgNjIuNjM2N0M0NS4zODcgNjIuNzM3IDQ1LjI3OTkgNjIuODY5MSA0NS4yMDcgNjMuMDMzMkM0NS4xMzg3IDYzLjE5NzMgNDUuMTA0NSA2My4zNzI3IDQ1LjEwNDUgNjMuNTU5NlpNNDYuNDcxNyA1Ny40Njg4TDQxLjYxMTMgNjUuMjQ4TDQwLjc3MDUgNjQuNzYyN0w0NS42MzA5IDU2Ljk4MzRMNDYuNDcxNyA1Ny40Njg4WiIgZmlsbD0iIzNGQTcxQSIvPgo8cGF0aCBkPSJNMTg2LjM2OCAyN0MxNzAuNDM4IDI3IDE3Mi4zMjUgNTguMDk3OSAxNTUuNzU3IDU4LjA5NzlDMTQxLjczOCA1OC4wOTc5IDEzOS40OSA0NC44NzIzIDEyOC42NTcgNDQuODcyM0MxMTcuODI0IDQ0Ljg3MjMgMTEzLjA2MyA2MyA5OS4wNDM4IDYzQzg3LjU3MzYgNjMgODEuMjAxMyA1OS4zMjM0IDcwLjM2ODQgNTkuMzIzNCIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuODciIHN0cm9rZS13aWR0aD0iMS4zIi8+CjxyZWN0IHk9Ijg0IiB3aWR0aD0iMjAwIiBoZWlnaHQ9Ijc2IiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iMC41IiB5PSI4NC41IiB3aWR0aD0iMTk5IiBoZWlnaHQ9Ijc1IiByeD0iMy41IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC4xMiIvPgo8cGF0aCBkPSJNMTAuNTI3MyA5OC44MTY0TDEyLjAyMzQgOTQuMTY4SDEzLjEwNTVMMTEuMDY2NCA5OS44NTU1SDEwLjMzNTlMMTAuNTI3MyA5OC44MTY0Wk05LjE0NDUzIDk0LjE2OEwxMC42Mjg5IDk4LjgxNjRMMTAuODMyIDk5Ljg1NTVIMTAuMDk3N0w4LjA2NjQxIDk0LjE2OEg5LjE0NDUzWk0xNC45MDYyIDk1LjYyODlWOTkuODU1NUgxMy45NjA5Vjk1LjYyODlIMTQuOTA2MlpNMTMuODk4NCA5NC41MTk1QzEzLjg5ODQgOTQuMzc2MyAxMy45NDUzIDk0LjI1NzggMTQuMDM5MSA5NC4xNjQxQzE0LjEzNTQgOTQuMDY3NyAxNC4yNjgyIDk0LjAxOTUgMTQuNDM3NSA5NC4wMTk1QzE0LjYwNDIgOTQuMDE5NSAxNC43MzU3IDk0LjA2NzcgMTQuODMyIDk0LjE2NDFDMTQuOTI4NCA5NC4yNTc4IDE0Ljk3NjYgOTQuMzc2MyAxNC45NzY2IDk0LjUxOTVDMTQuOTc2NiA5NC42NjAyIDE0LjkyODQgOTQuNzc3MyAxNC44MzIgOTQuODcxMUMxNC43MzU3IDk0Ljk2NDggMTQuNjA0MiA5NS4wMTE3IDE0LjQzNzUgOTUuMDExN0MxNC4yNjgyIDk1LjAxMTcgMTQuMTM1NCA5NC45NjQ4IDE0LjAzOTEgOTQuODcxMUMxMy45NDUzIDk0Ljc3NzMgMTMuODk4NCA5NC42NjAyIDEzLjg5ODQgOTQuNTE5NVpNMTYuMTkxNCA5My44NTU1SDE3LjEzMjhWOTguOTUzMUwxNy4wNDMgOTkuODU1NUgxNi4xOTE0VjkzLjg1NTVaTTE5Ljg5MDYgOTcuNzAzMVY5Ny43ODUyQzE5Ljg5MDYgOTguMDk3NyAxOS44NTU1IDk4LjM4NTQgMTkuNzg1MiA5OC42NDg0QzE5LjcxNzQgOTguOTA4OSAxOS42MTMzIDk5LjEzNTQgMTkuNDcyNyA5OS4zMjgxQzE5LjMzNDYgOTkuNTIwOCAxOS4xNjI4IDk5LjY3MDYgMTguOTU3IDk5Ljc3NzNDMTguNzUzOSA5OS44ODE1IDE4LjUxODIgOTkuOTMzNiAxOC4yNSA5OS45MzM2QzE3Ljk4NyA5OS45MzM2IDE3Ljc1NzggOTkuODg0MSAxNy41NjI1IDk5Ljc4NTJDMTcuMzY3MiA5OS42ODYyIDE3LjIwMzEgOTkuNTQ1NiAxNy4wNzAzIDk5LjM2MzNDMTYuOTQwMSA5OS4xODEgMTYuODM0NiA5OC45NjM1IDE2Ljc1MzkgOTguNzEwOUMxNi42NzMyIDk4LjQ1ODMgMTYuNjE1OSA5OC4xNzk3IDE2LjU4MiA5Ny44NzVWOTcuNjEzM0MxNi42MTU5IDk3LjMwNiAxNi42NzMyIDk3LjAyNzMgMTYuNzUzOSA5Ni43NzczQzE2LjgzNDYgOTYuNTI0NyAxNi45NDAxIDk2LjMwNzMgMTcuMDcwMyA5Ni4xMjVDMTcuMjAzMSA5NS45NDAxIDE3LjM2NTkgOTUuNzk4MiAxNy41NTg2IDk1LjY5OTJDMTcuNzUzOSA5NS42MDAzIDE3Ljk4MTggOTUuNTUwOCAxOC4yNDIyIDk1LjU1MDhDMTguNTEzIDk1LjU1MDggMTguNzUxMyA5NS42MDI5IDE4Ljk1NyA5NS43MDdDMTkuMTY1NCA5NS44MTEyIDE5LjMzODUgOTUuOTU5NiAxOS40NzY2IDk2LjE1MjNDMTkuNjE0NiA5Ni4zNDI0IDE5LjcxNzQgOTYuNTY5IDE5Ljc4NTIgOTYuODMyQzE5Ljg1NTUgOTcuMDk1MSAxOS44OTA2IDk3LjM4NTQgMTkuODkwNiA5Ny43MDMxWk0xOC45NDkyIDk3Ljc4NTJWOTcuNzAzMUMxOC45NDkyIDk3LjUxMyAxOC45MzM2IDk3LjMzNDYgMTguOTAyMyA5Ny4xNjhDMTguODcxMSA5Ni45OTg3IDE4LjgxOSA5Ni44NTAzIDE4Ljc0NjEgOTYuNzIyN0MxOC42NzU4IDk2LjU5NTEgMTguNTc5NCA5Ni40OTQ4IDE4LjQ1NyA5Ni40MjE5QzE4LjMzNzIgOTYuMzQ2NCAxOC4xODc1IDk2LjMwODYgMTguMDA3OCA5Ni4zMDg2QzE3Ljg0MTEgOTYuMzA4NiAxNy42OTc5IDk2LjMzNzIgMTcuNTc4MSA5Ni4zOTQ1QzE3LjQ1ODMgOTYuNDUxOCAxNy4zNTgxIDk2LjUyOTkgMTcuMjc3MyA5Ni42Mjg5QzE3LjE5NjYgOTYuNzI3OSAxNy4xMzI4IDk2Ljg0MjQgMTcuMDg1OSA5Ni45NzI3QzE3LjA0MTcgOTcuMTAyOSAxNy4wMTE3IDk3LjI0MzUgMTYuOTk2MSA5Ny4zOTQ1Vjk4LjEwMTZDMTcuMDE5NSA5OC4yOTY5IDE3LjA2OSA5OC40NzY2IDE3LjE0NDUgOTguNjQwNkMxNy4yMjI3IDk4LjgwMjEgMTcuMzMyIDk4LjkzMjMgMTcuNDcyNyA5OS4wMzEyQzE3LjYxMzMgOTkuMTI3NiAxNy43OTQzIDk5LjE3NTggMTguMDE1NiA5OS4xNzU4QzE4LjE5MDEgOTkuMTc1OCAxOC4zMzcyIDk5LjE0MDYgMTguNDU3IDk5LjA3MDNDMTguNTc2OCA5OSAxOC42NzE5IDk4LjkwMjMgMTguNzQyMiA5OC43NzczQzE4LjgxNTEgOTguNjQ5NyAxOC44NjcyIDk4LjUwMTMgMTguODk4NCA5OC4zMzJDMTguOTMyMyA5OC4xNjI4IDE4Ljk0OTIgOTcuOTgwNSAxOC45NDkyIDk3Ljc4NTJaTTIxLjg4MjggOTYuNDMzNlY5OS44NTU1SDIwLjk0MTRWOTUuNjI4OUgyMS44Mzk4TDIxLjg4MjggOTYuNDMzNlpNMjMuMTc1OCA5NS42MDE2TDIzLjE2OCA5Ni40NzY2QzIzLjExMDcgOTYuNDY2MSAyMy4wNDgyIDk2LjQ1ODMgMjIuOTgwNSA5Ni40NTMxQzIyLjkxNTQgOTYuNDQ3OSAyMi44NTAzIDk2LjQ0NTMgMjIuNzg1MiA5Ni40NDUzQzIyLjYyMzcgOTYuNDQ1MyAyMi40ODE4IDk2LjQ2ODggMjIuMzU5NCA5Ni41MTU2QzIyLjIzNyA5Ni41NTk5IDIyLjEzNDEgOTYuNjI1IDIyLjA1MDggOTYuNzEwOUMyMS45NzAxIDk2Ljc5NDMgMjEuOTA3NiA5Ni44OTU4IDIxLjg2MzMgOTcuMDE1NkMyMS44MTkgOTcuMTM1NCAyMS43OTMgOTcuMjY5NSAyMS43ODUyIDk3LjQxOEwyMS41NzAzIDk3LjQzMzZDMjEuNTcwMyA5Ny4xNjggMjEuNTk2NCA5Ni45MjE5IDIxLjY0ODQgOTYuNjk1M0MyMS43MDA1IDk2LjQ2ODggMjEuNzc4NiA5Ni4yNjk1IDIxLjg4MjggOTYuMDk3N0MyMS45ODk2IDk1LjkyNTggMjIuMTIyNCA5NS43OTE3IDIyLjI4MTIgOTUuNjk1M0MyMi40NDI3IDk1LjU5OSAyMi42Mjg5IDk1LjU1MDggMjIuODM5OCA5NS41NTA4QzIyLjg5NzEgOTUuNTUwOCAyMi45NTgzIDk1LjU1NiAyMy4wMjM0IDk1LjU2NjRDMjMuMDkxMSA5NS41NzY4IDIzLjE0MTkgOTUuNTg4NSAyMy4xNzU4IDk1LjYwMTZaTTI2LjI1NzggOTkuMDA3OFY5Ni45OTIyQzI2LjI1NzggOTYuODQxMSAyNi4yMzA1IDk2LjcxMDkgMjYuMTc1OCA5Ni42MDE2QzI2LjEyMTEgOTYuNDkyMiAyNi4wMzc4IDk2LjQwNzYgMjUuOTI1OCA5Ni4zNDc3QzI1LjgxNjQgOTYuMjg3OCAyNS42Nzg0IDk2LjI1NzggMjUuNTExNyA5Ni4yNTc4QzI1LjM1ODEgOTYuMjU3OCAyNS4yMjUzIDk2LjI4MzkgMjUuMTEzMyA5Ni4zMzU5QzI1LjAwMTMgOTYuMzg4IDI0LjkxNDEgOTYuNDU4MyAyNC44NTE2IDk2LjU0NjlDMjQuNzg5MSA5Ni42MzU0IDI0Ljc1NzggOTYuNzM1NyAyNC43NTc4IDk2Ljg0NzdIMjMuODIwM0MyMy44MjAzIDk2LjY4MSAyMy44NjA3IDk2LjUxOTUgMjMuOTQxNCA5Ni4zNjMzQzI0LjAyMjEgOTYuMjA3IDI0LjEzOTMgOTYuMDY3NyAyNC4yOTMgOTUuOTQ1M0MyNC40NDY2IDk1LjgyMjkgMjQuNjMwMiA5NS43MjY2IDI0Ljg0MzggOTUuNjU2MkMyNS4wNTczIDk1LjU4NTkgMjUuMjk2OSA5NS41NTA4IDI1LjU2MjUgOTUuNTUwOEMyNS44ODAyIDk1LjU1MDggMjYuMTYxNSA5NS42MDQyIDI2LjQwNjIgOTUuNzEwOUMyNi42NTM2IDk1LjgxNzcgMjYuODQ3NyA5NS45NzkyIDI2Ljk4ODMgOTYuMTk1M0MyNy4xMzE1IDk2LjQwODkgMjcuMjAzMSA5Ni42NzcxIDI3LjIwMzEgOTdWOTguODc4OUMyNy4yMDMxIDk5LjA3MTYgMjcuMjE2MSA5OS4yNDQ4IDI3LjI0MjIgOTkuMzk4NEMyNy4yNzA4IDk5LjU0OTUgMjcuMzExMiA5OS42ODEgMjcuMzYzMyA5OS43OTNWOTkuODU1NUgyNi4zOTg0QzI2LjM1NDIgOTkuNzUzOSAyNi4zMTkgOTkuNjI1IDI2LjI5MyA5OS40Njg4QzI2LjI2OTUgOTkuMzA5OSAyNi4yNTc4IDk5LjE1NjIgMjYuMjU3OCA5OS4wMDc4Wk0yNi4zOTQ1IDk3LjI4NTJMMjYuNDAyMyA5Ny44NjcySDI1LjcyNjZDMjUuNTUyMSA5Ny44NjcyIDI1LjM5ODQgOTcuODg0MSAyNS4yNjU2IDk3LjkxOEMyNS4xMzI4IDk3Ljk0OTIgMjUuMDIyMSA5Ny45OTYxIDI0LjkzMzYgOTguMDU4NkMyNC44NDUxIDk4LjEyMTEgMjQuNzc4NiA5OC4xOTY2IDI0LjczNDQgOTguMjg1MkMyNC42OTAxIDk4LjM3MzcgMjQuNjY4IDk4LjQ3NCAyNC42NjggOTguNTg1OUMyNC42NjggOTguNjk3OSAyNC42OTQgOTguODAwOCAyNC43NDYxIDk4Ljg5NDVDMjQuNzk4MiA5OC45ODU3IDI0Ljg3MzcgOTkuMDU3MyAyNC45NzI3IDk5LjEwOTRDMjUuMDc0MiA5OS4xNjE1IDI1LjE5NjYgOTkuMTg3NSAyNS4zMzk4IDk5LjE4NzVDMjUuNTMyNiA5OS4xODc1IDI1LjcwMDUgOTkuMTQ4NCAyNS44NDM4IDk5LjA3MDNDMjUuOTg5NiA5OC45ODk2IDI2LjEwNDIgOTguODkxOSAyNi4xODc1IDk4Ljc3NzNDMjYuMjcwOCA5OC42NjAyIDI2LjMxNTEgOTguNTQ5NSAyNi4zMjAzIDk4LjQ0NTNMMjYuNjI1IDk4Ljg2MzNDMjYuNTkzOCA5OC45NzAxIDI2LjU0MDQgOTkuMDg0NiAyNi40NjQ4IDk5LjIwN0MyNi4zODkzIDk5LjMyOTQgMjYuMjkwNCA5OS40NDY2IDI2LjE2OCA5OS41NTg2QzI2LjA0ODIgOTkuNjY4IDI1LjkwMzYgOTkuNzU3OCAyNS43MzQ0IDk5LjgyODFDMjUuNTY3NyA5OS44OTg0IDI1LjM3NSA5OS45MzM2IDI1LjE1NjIgOTkuOTMzNkMyNC44ODAyIDk5LjkzMzYgMjQuNjM0MSA5OS44Nzg5IDI0LjQxOCA5OS43Njk1QzI0LjIwMTggOTkuNjU3NiAyNC4wMzI2IDk5LjUwNzggMjMuOTEwMiA5OS4zMjAzQzIzLjc4NzggOTkuMTMwMiAyMy43MjY2IDk4LjkxNTQgMjMuNzI2NiA5OC42NzU4QzIzLjcyNjYgOTguNDUxOCAyMy43NjgyIDk4LjI1MzkgMjMuODUxNiA5OC4wODJDMjMuOTM3NSA5Ny45MDc2IDI0LjA2MjUgOTcuNzYxNyAyNC4yMjY2IDk3LjY0NDVDMjQuMzkzMiA5Ny41MjczIDI0LjU5NjQgOTcuNDM4OCAyNC44MzU5IDk3LjM3ODlDMjUuMDc1NSA5Ny4zMTY0IDI1LjM0OSA5Ny4yODUyIDI1LjY1NjIgOTcuMjg1MkgyNi4zOTQ1Wk0zMC4zNzUgOTUuNjI4OVY5Ni4zMTY0SDI3Ljk5MjJWOTUuNjI4OUgzMC4zNzVaTTI4LjY3OTcgOTQuNTkzOEgyOS42MjExVjk4LjY4NzVDMjkuNjIxMSA5OC44MTc3IDI5LjYzOTMgOTguOTE4IDI5LjY3NTggOTguOTg4M0MyOS43MTQ4IDk5LjA1NiAyOS43NjgyIDk5LjEwMTYgMjkuODM1OSA5OS4xMjVDMjkuOTAzNiA5OS4xNDg0IDI5Ljk4MzEgOTkuMTYwMiAzMC4wNzQyIDk5LjE2MDJDMzAuMTM5MyA5OS4xNjAyIDMwLjIwMTggOTkuMTU2MiAzMC4yNjE3IDk5LjE0ODRDMzAuMzIxNiA5OS4xNDA2IDMwLjM2OTggOTkuMTMyOCAzMC40MDYyIDk5LjEyNUwzMC40MTAyIDk5Ljg0MzhDMzAuMzMyIDk5Ljg2NzIgMzAuMjQwOSA5OS44ODggMzAuMTM2NyA5OS45MDYyQzMwLjAzNTIgOTkuOTI0NSAyOS45MTggOTkuOTMzNiAyOS43ODUyIDk5LjkzMzZDMjkuNTY5IDk5LjkzMzYgMjkuMzc3NiA5OS44OTU4IDI5LjIxMDkgOTkuODIwM0MyOS4wNDQzIDk5Ljc0MjIgMjguOTE0MSA5OS42MTU5IDI4LjgyMDMgOTkuNDQxNEMyOC43MjY2IDk5LjI2NjkgMjguNjc5NyA5OS4wMzUyIDI4LjY3OTcgOTguNzQ2MVY5NC41OTM4Wk0zMi4zNTk0IDk1LjYyODlWOTkuODU1NUgzMS40MTQxVjk1LjYyODlIMzIuMzU5NFpNMzEuMzUxNiA5NC41MTk1QzMxLjM1MTYgOTQuMzc2MyAzMS4zOTg0IDk0LjI1NzggMzEuNDkyMiA5NC4xNjQxQzMxLjU4ODUgOTQuMDY3NyAzMS43MjE0IDk0LjAxOTUgMzEuODkwNiA5NC4wMTk1QzMyLjA1NzMgOTQuMDE5NSAzMi4xODg4IDk0LjA2NzcgMzIuMjg1MiA5NC4xNjQxQzMyLjM4MTUgOTQuMjU3OCAzMi40Mjk3IDk0LjM3NjMgMzIuNDI5NyA5NC41MTk1QzMyLjQyOTcgOTQuNjYwMiAzMi4zODE1IDk0Ljc3NzMgMzIuMjg1MiA5NC44NzExQzMyLjE4ODggOTQuOTY0OCAzMi4wNTczIDk1LjAxMTcgMzEuODkwNiA5NS4wMTE3QzMxLjcyMTQgOTUuMDExNyAzMS41ODg1IDk0Ljk2NDggMzEuNDkyMiA5NC44NzExQzMxLjM5ODQgOTQuNzc3MyAzMS4zNTE2IDk0LjY2MDIgMzEuMzUxNiA5NC41MTk1Wk0zMy40NjA5IDk3Ljc4OTFWOTcuNjk5MkMzMy40NjA5IDk3LjM5NDUgMzMuNTA1MiA5Ny4xMTIgMzMuNTkzOCA5Ni44NTE2QzMzLjY4MjMgOTYuNTg4NSAzMy44MDk5IDk2LjM2MDcgMzMuOTc2NiA5Ni4xNjhDMzQuMTQ1OCA5NS45NzI3IDM0LjM1MTYgOTUuODIxNiAzNC41OTM4IDk1LjcxNDhDMzQuODM4NSA5NS42MDU1IDM1LjExNDYgOTUuNTUwOCAzNS40MjE5IDk1LjU1MDhDMzUuNzMxOCA5NS41NTA4IDM2LjAwNzggOTUuNjA1NSAzNi4yNSA5NS43MTQ4QzM2LjQ5NDggOTUuODIxNiAzNi43MDE4IDk1Ljk3MjcgMzYuODcxMSA5Ni4xNjhDMzcuMDQwNCA5Ni4zNjA3IDM3LjE2OTMgOTYuNTg4NSAzNy4yNTc4IDk2Ljg1MTZDMzcuMzQ2NCA5Ny4xMTIgMzcuMzkwNiA5Ny4zOTQ1IDM3LjM5MDYgOTcuNjk5MlY5Ny43ODkxQzM3LjM5MDYgOTguMDkzOCAzNy4zNDY0IDk4LjM3NjMgMzcuMjU3OCA5OC42MzY3QzM3LjE2OTMgOTguODk3MSAzNy4wNDA0IDk5LjEyNSAzNi44NzExIDk5LjMyMDNDMzYuNzAxOCA5OS41MTMgMzYuNDk2MSA5OS42NjQxIDM2LjI1MzkgOTkuNzczNEMzNi4wMTE3IDk5Ljg4MDIgMzUuNzM3IDk5LjkzMzYgMzUuNDI5NyA5OS45MzM2QzM1LjExOTggOTkuOTMzNiAzNC44NDI0IDk5Ljg4MDIgMzQuNTk3NyA5OS43NzM0QzM0LjM1NTUgOTkuNjY0MSAzNC4xNDk3IDk5LjUxMyAzMy45ODA1IDk5LjMyMDNDMzMuODExMiA5OS4xMjUgMzMuNjgyMyA5OC44OTcxIDMzLjU5MzggOTguNjM2N0MzMy41MDUyIDk4LjM3NjMgMzMuNDYwOSA5OC4wOTM4IDMzLjQ2MDkgOTcuNzg5MVpNMzQuNDAyMyA5Ny42OTkyVjk3Ljc4OTFDMzQuNDAyMyA5Ny45NzkyIDM0LjQyMTkgOTguMTU4OSAzNC40NjA5IDk4LjMyODFDMzQuNSA5OC40OTc0IDM0LjU2MTIgOTguNjQ1OCAzNC42NDQ1IDk4Ljc3MzRDMzQuNzI3OSA5OC45MDEgMzQuODM0NiA5OS4wMDEzIDM0Ljk2NDggOTkuMDc0MkMzNS4wOTUxIDk5LjE0NzEgMzUuMjUgOTkuMTgzNiAzNS40Mjk3IDk5LjE4MzZDMzUuNjA0MiA5OS4xODM2IDM1Ljc1NTIgOTkuMTQ3MSAzNS44ODI4IDk5LjA3NDJDMzYuMDEzIDk5LjAwMTMgMzYuMTE5OCA5OC45MDEgMzYuMjAzMSA5OC43NzM0QzM2LjI4NjUgOTguNjQ1OCAzNi4zNDc3IDk4LjQ5NzQgMzYuMzg2NyA5OC4zMjgxQzM2LjQyODQgOTguMTU4OSAzNi40NDkyIDk3Ljk3OTIgMzYuNDQ5MiA5Ny43ODkxVjk3LjY5OTJDMzYuNDQ5MiA5Ny41MTE3IDM2LjQyODQgOTcuMzM0NiAzNi4zODY3IDk3LjE2OEMzNi4zNDc3IDk2Ljk5ODcgMzYuMjg1MiA5Ni44NDkgMzYuMTk5MiA5Ni43MTg4QzM2LjExNTkgOTYuNTg4NSAzNi4wMDkxIDk2LjQ4NyAzNS44Nzg5IDk2LjQxNDFDMzUuNzUxMyA5Ni4zMzg1IDM1LjU5OSA5Ni4zMDA4IDM1LjQyMTkgOTYuMzAwOEMzNS4yNDQ4IDk2LjMwMDggMzUuMDkxMSA5Ni4zMzg1IDM0Ljk2MDkgOTYuNDE0MUMzNC44MzMzIDk2LjQ4NyAzNC43Mjc5IDk2LjU4ODUgMzQuNjQ0NSA5Ni43MTg4QzM0LjU2MTIgOTYuODQ5IDM0LjUgOTYuOTk4NyAzNC40NjA5IDk3LjE2OEMzNC40MjE5IDk3LjMzNDYgMzQuNDAyMyA5Ny41MTE3IDM0LjQwMjMgOTcuNjk5MlpNMzkuMzYzMyA5Ni41MzEyVjk5Ljg1NTVIMzguNDIxOVY5NS42Mjg5SDM5LjMwODZMMzkuMzYzMyA5Ni41MzEyWk0zOS4xOTUzIDk3LjU4NTlMMzguODkwNiA5Ny41ODJDMzguODkzMiA5Ny4yODI2IDM4LjkzNDkgOTcuMDA3OCAzOS4wMTU2IDk2Ljc1NzhDMzkuMDk5IDk2LjUwNzggMzkuMjEzNSA5Ni4yOTMgMzkuMzU5NCA5Ni4xMTMzQzM5LjUwNzggOTUuOTMzNiAzOS42ODQ5IDk1Ljc5NTYgMzkuODkwNiA5NS42OTkyQzQwLjA5NjQgOTUuNjAwMyA0MC4zMjU1IDk1LjU1MDggNDAuNTc4MSA5NS41NTA4QzQwLjc4MTIgOTUuNTUwOCA0MC45NjQ4IDk1LjU3OTQgNDEuMTI4OSA5NS42MzY3QzQxLjI5NTYgOTUuNjkxNCA0MS40Mzc1IDk1Ljc4MTIgNDEuNTU0NyA5NS45MDYyQzQxLjY3NDUgOTYuMDMxMiA0MS43NjU2IDk2LjE5NCA0MS44MjgxIDk2LjM5NDVDNDEuODkwNiA5Ni41OTI0IDQxLjkyMTkgOTYuODM1OSA0MS45MjE5IDk3LjEyNVY5OS44NTU1SDQwLjk3NjZWOTcuMTIxMUM0MC45NzY2IDk2LjkxOCA0MC45NDY2IDk2Ljc1NzggNDAuODg2NyA5Ni42NDA2QzQwLjgyOTQgOTYuNTIwOCA0MC43NDQ4IDk2LjQzNjIgNDAuNjMyOCA5Ni4zODY3QzQwLjUyMzQgOTYuMzM0NiA0MC4zODY3IDk2LjMwODYgNDAuMjIyNyA5Ni4zMDg2QzQwLjA2MTIgOTYuMzA4NiAzOS45MTY3IDk2LjM0MjQgMzkuNzg5MSA5Ni40MTAyQzM5LjY2MTUgOTYuNDc3OSAzOS41NTM0IDk2LjU3MDMgMzkuNDY0OCA5Ni42ODc1QzM5LjM3ODkgOTYuODA0NyAzOS4zMTI1IDk2Ljk0MDEgMzkuMjY1NiA5Ny4wOTM4QzM5LjIxODggOTcuMjQ3NCAzOS4xOTUzIDk3LjQxMTUgMzkuMTk1MyA5Ny41ODU5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDBfNDY3M185Nzc0KSI+CjxwYXRoIGQ9Ik00NC44ODgzIDEyMi41NzdMMTcuNDk2MSAxNDIuMzExTDggMTQ4LjM3MVYxNTUuMTQ1SDE5MlYxNDYuMjM3VjEzNi45ODdMMTc4LjM4OSAxNTEuMDI3TDE3MC44MDMgMTQyLjI0NkwxNjcuNTExIDE0NC44MjhMMTUzLjQ0MSAxNTQuNTE1QzE0NC4wNjIgMTQzLjk3OSAxMzYuMjQ2IDE0MC4wNDggMTI2LjAzOCAxMjkuNjk2TDExNS41MDUgMTQ1LjE1NkgxMDEuOTU5TDk5LjM0MjYgMTIwLjAzNEw5NC4wNzI4IDExMC4xNDVMNjguOTk3OSAxMzQuODE4TDYzLjAzNTQgMTM0LjA0M0w0NC44ODgzIDEyMi41NzdaIiBmaWxsPSJ1cmwoI3BhaW50MF9saW5lYXJfNDY3M185Nzc0KSIvPgo8cGF0aCBkPSJNOCAxNDguMDI3TDE3LjU4NTQgMTQyLjAyMkw0NS4yMzUzIDEyMi40NjVMNjMuNTUzMSAxMzMuODI5TDY5LjU3MTcgMTM0LjU5Nkw5NCAxMTFMOTkuNSAxMTkuOTY2QzEwMC41MzEgMTI5LjY4OSAxMDAuOTY5IDEzNS4xMTkgMTAyIDE0NC44NDFIMTE1LjVMMTI2IDEyOS4zOTVMMTUzLjUgMTU0LjE0NUwxNzAuOTUyIDE0MS45OTRMMTc4LjUgMTUwLjYwOUwxOTIgMTM2LjgzOSIgc3Ryb2tlPSJ1cmwoI3BhaW50MV9saW5lYXJfNDY3M185Nzc0KSIgc3Ryb2tlLXdpZHRoPSIwLjkwMTMxOCIvPgo8L2c+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMV80NjczXzk3NzQpIj4KPHBhdGggZD0iTTggMTEwLjY0NUwxOTIgMTEwLjY0NSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiIHN0cm9rZS13aWR0aD0iMC4yOTQ5OTUiLz4KPHBhdGggZD0iTTggMTIwLjM5NUwxOTIgMTIwLjM5NSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiIHN0cm9rZS13aWR0aD0iMC4yOTQ5OTUiLz4KPHBhdGggZD0iTTggMTMwLjE0NUwxOTIgMTMwLjE0NSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiIHN0cm9rZS13aWR0aD0iMC4yOTQ5OTUiLz4KPHBhdGggZD0iTTggMTM5Ljg5NUwxOTIgMTM5Ljg5NSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiIHN0cm9rZS13aWR0aD0iMC4yOTQ5OTUiLz4KPHBhdGggZD0iTTggMTQ5LjY0NUwxOTIgMTQ5LjY0NSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiIHN0cm9rZS13aWR0aD0iMC4yOTQ5OTUiLz4KPC9nPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzQ2NzNfOTc3NCIgeDE9Ijk1LjU4ODkiIHkxPSIxNjAuMzI1IiB4Mj0iOTUuNTg4OSIgeTI9Ijk4LjMzODciIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agb2Zmc2V0PSIwLjE3MzM4OSIgc3RvcC1jb2xvcj0iIzZGQ0M0QyIvPgo8c3RvcCBvZmZzZXQ9IjAuMzI3MDc0IiBzdG9wLWNvbG9yPSIjNzBDQzRDIi8+CjxzdG9wIG9mZnNldD0iMC4zMzE2NTYiIHN0b3AtY29sb3I9IiNGRUJBM0QiLz4KPHN0b3Agb2Zmc2V0PSIwLjQ4MDg3NiIgc3RvcC1jb2xvcj0iI0ZFQkEzRCIvPgo8c3RvcCBvZmZzZXQ9IjAuNDkxMjQyIiBzdG9wLWNvbG9yPSIjRkQ4RjNDIi8+CjxzdG9wIG9mZnNldD0iMC42Mzc0MzEiIHN0b3AtY29sb3I9IiNGRDhGM0MiLz4KPHN0b3Agb2Zmc2V0PSIwLjY0NTY0OSIgc3RvcC1jb2xvcj0iI0Y1NjE0OCIvPgo8L2xpbmVhckdyYWRpZW50Pgo8bGluZWFyR3JhZGllbnQgaWQ9InBhaW50MV9saW5lYXJfNDY3M185Nzc0IiB4MT0iOTQuNzg2MiIgeTE9IjE1NC41MzciIHgyPSI5NC43NzY0IiB5Mj0iOTguNDQ0OSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBvZmZzZXQ9IjAuMjU4NDUyIiBzdG9wLWNvbG9yPSIjM0ZBNzFBIi8+CjxzdG9wIG9mZnNldD0iMC4yNjk2MDEiIHN0b3AtY29sb3I9IiNGRkE2MDAiLz4KPHN0b3Agb2Zmc2V0PSIwLjQyNzI1OSIgc3RvcC1jb2xvcj0iI0ZGQTYwMCIvPgo8c3RvcCBvZmZzZXQ9IjAuNDQzODQ3IiBzdG9wLWNvbG9yPSIjRjU2RTA4Ii8+CjxzdG9wIG9mZnNldD0iMC42MDM1NDciIHN0b3AtY29sb3I9IiNGNTZFMDgiLz4KPHN0b3Agb2Zmc2V0PSIwLjYwNjQ2NyIgc3RvcC1jb2xvcj0iI0ZGQTYwMCIvPgo8c3RvcCBvZmZzZXQ9IjAuNjExNTkxIiBzdG9wLWNvbG9yPSIjRjA0MDIyIi8+CjwvbGluZWFyR3JhZGllbnQ+CjxjbGlwUGF0aCBpZD0iY2xpcDBfNDY3M185Nzc0Ij4KPHJlY3Qgd2lkdGg9IjE4NCIgaGVpZ2h0PSI0NSIgZmlsbD0id2hpdGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDggMTEwLjE0NSkiLz4KPC9jbGlwUGF0aD4KPGNsaXBQYXRoIGlkPSJjbGlwMV80NjczXzk3NzQiPgo8cmVjdCB3aWR0aD0iMTg0IiBoZWlnaHQ9IjQ1IiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOCAxMTAuMTQ1KSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo=", + "description": "Contains widgets displaying industrial telemetry.", + "order": 9500, + "externalId": null, + "name": "Industrial widgets" + }, + "widgetTypeFqns": [ + "flow_rate_card", + "flow_rate_card_with_background", + "horizontal_flow_rate_card", + "horizontal_flow_rate_card_with_background", + "flow_rate_chart_card", + "flow_rate_chart_card_with_background", + "simple_flow_rate_chart_card", + "simple_flow_rate_chart_card_with_background", + "flow_rate_progress_bar", + "flow_rate_progress_bar_with_background", + "flow_rate_range_chart", + "flow_rate_range_chart_with_background", + "flow_rate_gauge", + "fluid_pressure_card", + "fluid_pressure_card_with_background", + "horizontal_fluid_pressure_card", + "horizontal_fluid_pressure_card_with_background", + "fluid_pressure_chart_card", + "fluid_pressure_chart_card_with_background", + "simple_fluid_pressure_chart_card", + "simple_fluid_pressure_chart_card_with_background", + "fluid_pressure_progress_bar", + "fluid_pressure_progress_bar_with_background", + "fluid_pressure_range_chart", + "fluid_pressure_range_chart_with_background", + "fluid_pressure_gauge", + "pump_vibration_card", + "pump_vibration_card_with_background", + "horizontal_pump_vibration_card", + "horizontal_pump_vibration_card_with_background", + "pump_vibration_chart_card", + "pump_vibration_chart_card_with_background", + "simple_pump_vibration_chart_card", + "simple_pump_vibration_chart_card_with_background", + "pump_vibration_range_chart", + "pump_vibration_range_chart_with_background", + "power_consumption_card", + "power_consumption_card_with_background", + "horizontal_power_consumption_card", + "horizontal_power_consumption_card_with_background", + "power_consumption_chart_card", + "power_consumption_chart_card_with_background", + "simple_power_consumption_chart_card", + "simple_power_consumption_chart_card_with_background", + "power_consumption_range_chart", + "power_consumption_range_chart_with_background", + "rotational_speed_card", + "rotational_speed_card_with_background", + "horizontal_rotational_speed_card", + "horizontal_rotational_speed_card_with_background", + "rotational_speed_chart_card", + "rotational_speed_chart_card_with_background", + "simple_rotational_speed_chart_card", + "simple_rotational_speed_chart_card_with_background", + "rotational_speed_progress_bar", + "rotational_speed_progress_bar_with_background", + "rotational_speed_range_chart", + "rotational_speed_range_chart_with_background", + "rotational_speed_gauge", + "efficiency_card", + "efficiency_card_with_background", + "horizontal_efficiency_card", + "horizontal_efficiency_card_with_background", + "efficiency_chart_card", + "efficiency_chart_card_with_background", + "simple_efficiency_chart_card", + "simple_efficiency_chart_card_with_background", + "efficiency_progress_bar", + "efficiency_progress_bar_with_background", + "efficiency_range_chart", + "efficiency_range_chart_with_background" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/input_widgets.json b/application/src/main/data/json/system/widget_bundles/input_widgets.json index ca2e16e4dd..fe24d68960 100644 --- a/application/src/main/data/json/system/widget_bundles/input_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/input_widgets.json @@ -2,504 +2,38 @@ "widgetsBundle": { "alias": "input_widgets", "title": "Input widgets", - "image": "data:image/png;base64,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", - "description": "Various input forms that allow users to set location, image and other configuration parameters of the target entity" + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC91BMVEX////+/v4wVoD9/f3v7+/u7u77+/zx8fHKysqIiIj39/jyyUz6+vvd3d2qqqr5+fnz8/Xk6vDX1+H29vf39/ny8vJ1dXXc3OTz8/bKytfo6O3T1N0+YonU1N/W1t/V1eDs7PHp6epng6Lv8PTq6u/Pz9udnZ3i4uPR0dyLi4v19fXm5eXGxtSrq6vf3+fa2uPZ2eKNjY3U09Tx8fTr6uvm5+fx9PfS0t3NzdrIyNXl5uze3ubb2trW1dXQ0NC/v8CysrLY2OLi4eHCwsODg4P19ffc3NzZ2dnMzNnIyMmtra3s7OzNzdnGxsampqaenp/h4ejX19fLy8vIx8fExMS2trb09vju7vLh4OC8vLyXl5eHh4fk5Ouvr6+oqKgzWYLDw9KjpKSamprY2Nnj5Ori4+nZ4Ojf3+C5ubq0tLR8fHzu7e3o6OiRkZGQkJDM0tXS0tLPz8/Ozs7Nzc2vvc24uLiVlJRBY4q0wtGnsbaBgYDPz9mapqtaeZp+fn6hoaHI096jtMe+vr7f5eze3t/T1tmtt7x9fX3U3OW1vMG7tLR1jqpif598e3p5eXl8dmbm5+zX292/v87PysqSpr2Mobm+t7eTn6aPm6OqoaGLmaCZmZk4XYXk4+TQ2ePKytS4xdTIzdC6u8y7vMfBwcGytcCqs7mDmrOfqrCWoah3hZBDZoy+xcnGwMDCu7uupqZQcJNIao9xcXBra2t7clbM1uHU2NzEztqxv8/Tzs65wcWesMS4usOJn7e1rq5/jZaTk5OCgoJ8eXHNztXDys3Bwc3Ax8u/v8eZq8B7k65viqeIlpyEkJt8i5NvbGN1blnm6/Df4+Xa3uHBzdnP09bJz9LGxsyouMq3t8d+lrBzgotkZGS4oltbW1uFeVjhvlLowk67yNalrrVMbZFvfojBp1hqZVfSs1XauFDvx03Dw86trcFjdX/KrlmumVmMflfmwVJmYVG0trqkpLmjrrOyqqre3uiioq2ioZ6cjV+lk1yhj1ukkFQDosFnAAAXMklEQVR42uzbzWsiZwDH8d/s6JM4TKLFzpzrFpRRPMxgeii+oRJRiRJfSlRMUKG+JMpqaghCzNtCCpvklJzSLntY2MNedhOWFspCctvtqbCHsix76j/S8SXZdJNNa1HRJd+ZRCY5PPlkdOYZZQCi7G8sMJBBiJJCP6OUZDCD9HOIziAYyCBK9DslBjKIAv1OgUEMcjEGdR4w0hCKomkiR9MUNcIQmUEIy6pZlhBZMrKQpoNVM1p5YdimZGQhsoPRqimAZhg1oUcVQlGyQ8Og1YTD4XZHfRPUKEJoojYuopP/KKPXh0I1Gp2kUpDgogQuVVDrhwvCMgoOnWwJGSJLZtFpn5aiTPw+K8VMqM4kAe9yJFdjyFYYs4w4VBDCai04z7TShmyhk7lsQ3nCYzq21r0JqQ4kK2Iob56plHLJLiGdf46GzWlpNJubaj/0CqI26mh0ynUgUbSjFdyK+yQheleQjFWQhLzGlpf0vsyOvVvIJECH50wrY4Z1+8/4GfaYVeFTw5Bw92qPcNkldHJ0IBc/OJPSebEaC8iQ++WZ+jmkFK2H/wfEYM2w82P5ms4Pvy0at6YnnCDO5Z5BvF600wgtSNqL87QRCWRmjhiQg1VyATlYOLVJ684t5mhXt5CwIwM/l48rzPBHcnZrKXy/l0ctZlFyUZBjZhzZOYNhifTlPFKr5aedIeKcikxEqoV5o99siMa9vYTIu8QuTSkUhordq2EJNZonxNYuUXjvVarVyj2vgiH0yEJaEqtNzqpgRneK0paojRqO0xjVozxpbEtajfY0viWh6FbUSF9YnVvain5DGLTSAlDTvYdc1EeI9n1DRKLMa4Cp/ZIKaXFvGnAWUXnXCOJe3T0qEEOGgC0iHwdmpsHTDlTmoUsWEXcTkCKDPkAUHC5FKQAELBbGiA9pu4Z4jxsJiwrhScjNlYF5PoDkUhGxIl9zvRd5de8h6z4/AQPfElgABcc4YF8JB6wsS7M0aALMpkzdQuR4SxKRFABl0QgYC864OHMyBZBjKYNguPeQSdgn4rXx9Wg47wciISUAM0jK7CjFgwHzOgN7WdstxF1jj2mehOxGxtgIAB52OeR15N+51q2LRW6fFnP9gFTvb22l3Lq0OyVvpZyKNsQvf/dHnQUdFBzpFkJCgoR7opmq2iRBECZsYsYIkBQUp2UTloUY+vDUiqZovcNcjTjcPiAYaWjOIfJiMjvY4T2P/DOGARijGhzGaIBWEgKw8sqiuWiZIT4h/rMRPrNfdAv5NwgZ/18FuoRkTZCTdJAbgT1Ctb+uQqTSukNODJqGGqL2U2gVNEBzxFyFaPnzVoYZos5cvN2TmE0YcBXCXUDKaDYliBakxUkATmRPAZdQHgO2xqJCSUv0gg9hoczB1mHHxHmYRNUEgJQYhKYkTPccolm65AB0vA/XQTxoZ2pDkjqDuByCZwlSYWr+BGhwkh5QsTwVK0T9hKeO6ceTpskTNBurozzXUCr24VrUo5RLL2uLPYdwibmM7QMrkT1y/QcI31rpPYJTY2dLW4fNCUCfDVZRNwLyA3g08+rhdANZPYmB2OvapAJ8zyEYE62Qu/k1cgGhLZcg+gi0pfMtZqeFCieQtsPDQSrhHGILoeCAssgAYGIeztMXCKibj1qXITmhuN5iNrRjHqScQOxe+8/dJ4ZTpgx4VTTycfBUzkMuIDoVQjZtwwJA58P8csJKX4H0sasQ+ozP807ISTvF2bn3ghBWNdk7QKRRdNUqwPukYDY2VEG88wjB1m/OAKwISWqFFwTmiCRFD2Mq1h9fAzHgUkYL2k0Y0UzXS8gYz0f3E7gUkfAhO4WrjSW6eRMbLgtMs4TNTnGGzJIOOkw7A5wVsOrDPYEwRbMcJ/CechzdRXUDqS6blSv2LfN0wuBLc374wzNpS7A6Ay5t7QkE0z45XSAkFAi6rBtIipGsfrUzCLNhvHWR5uQcc2LNC1D0yEwa4azlTSknW2Pj4ULJZI9I8dikybyucMYNvXqxk/HeR244anndZmBkp/GXM1KfCQS4hdxCbiEjBPn+9d1P93b7Y8hs3RNFWuVhAOWZ6ogkEisqziPoUW3s3GeSgkBVPTtTpCSEYPPsuAAvPxgI/fTOTT37GFLjyD7ZR8EBPLaBp4GaIythny6qdYJ9Bh5lkbhKtjSSgTpjUYGcHQ8G8sedm/vuIwjCQjSgwri5M/UHtUdg0h+BB8UDXANF7dReNgNhts4peaSlAe2RL7qFKCIlZRI+PwBjUQFEZBLj3SM8yD7I2c/IJssipRdPlnLJU49rz3syNZSQoJLwNE/NhwlNPAYAe1osR7CjOVNOJyjBBoAKjQO6BgXMOyaczveVoYQYBNUs3EKGRCrhHUGY0JkB9akYg0ElLFr3BMHOio8Bd0IJiHHIHaF3kIegf0Srb998ErJ2d+E6yFAdfp/hy7X2Rwobh6DpayEvH71avbPQsSwMMWR1d3fzxdrdwxd3dx9eBzlcff58bePh7vcLD//a2HgwxJDfth882dg4fLD94u21T63fNw6evnpzsPng0ZONH94MJ2R3+8ffm5CD1UeHT35ZPbgOsrn7fPvg5cuDtT8evP7h6doFpCcfK1h6BNl+/faLF2++2vz67eGvXz85/O46yNM/H+0+W918e+fPnxZera4N5x75r4ffoT9q3UJuIYOHfLdwo+MnelQg2PzpJsevo3OFCPLNp6MxQMjncx/iZ3NnKDuA22gHMwhRKsf6l7I5xCAGue3vZu48uIkqjgN4XrZ9xXRx42ZjjjUXSWimDUkkjc3RFtKamtoUUyimOKUUSSpMUYp4UFtUlFaKB9BqCygiIFgqiCcIIswghwqCIqKjjnjf93394dvN0U3SQlsk5TtO3/sJQ/eTd+zyYDnn0qYDiXF54koLxsYOuBGsAHGpjS993CI1L+YfB0kxxUPIMMSLuuJsEQjnjcaBQ1LzYj5IjpKKK1VhiAx1y7IWTS0GTNrmgEggxkJw0BuCoglO2O0XpJ+9AGb71StcEovb6awOVsutOHMRQkVkRKj9XgpggBYQOAZFxmAwuEb/9YaDpMRnXPfuBoPSQvrsPrvdq9cbfOscMp9dr5AZfW6nSukwupQGg0GlZIMgPPDeeWcv7wHmO6BArdwpl+tMEpPLTevt7WaVHV2USQeAKc/hsxtqSZffUqte5lyms3pEEA2PYA+Ij+KUUyuNJziPd/ZyngB9B5AcvRTEx+JhLhXjZOdbID6q+FJ2TkDyhCA+ZuZ/0FxIj/DUI9J0TkBoa1+QNeXFMYetKwnf24UPv3D//CvPBYhS2xektnhiDNI6ByTEEOtdMoqPcvMF5wDEFegLYmwoiEFeA/1DXuHzH7kTSa48ByESBuKYmBV1ZCRD9NEO89v459Toy5vDDzFpEyFSBlJZH4WkrQKJsUc7D/Pnq6/EyTr+/ecARNTXiKwpXhSF4KeALObf//Al1PZH+AuHH+LH+oJYRpRg0SRDvNGONjq1Hh5+iB70NbV8DeIYpNkKEiLjLvYXb0BHKOHFLhoOiOYayDSQc3PLTYtBSqZUYRgIQzYf7x/i5LP5iC2qSq9JPSRnQsksICCAFV18NsTwDADEFUUA4uIQJYIgN8smhGNwIYEgLRsTIY7e7ouM46nwgJSMFaQekvtgOsydcjUc/Whh6cTb4NOV40omX1sESkZPLakYf5NgdOWYizPuSy8vrED7b/MpIJc8jiB14f7YcSmD2BVKlUrll1gsWuvMyVdMGV1Q7gGXg7kZswLTJ4AGBCnAptfw7tCMnji7wDY9bUaJAMM8PYkbm9Eai3Axn/+yVMtEd7BUrVanekQWla9+cMTq1cTVJTPGrL5NMB5OKJ97RYtibc2JcWOyJlOjy1vKS6arLaRBJlvXs4FWsDFLSJKm6TyjgjbkKVSk2UwqJAtHkXlMVI5ZYwwGQ6ohwFYhACOznPX5AnWhRJsLNNhlOWJQMimfVM/QYMvTRWniDA1gcyzxYcsu4BRSPx3uKGaMQStqWLZfkSTcuqUgkvpcYJFCCH0E9+zhUOKuJeRWfjICsU3Ghwei9VJ9nKS4RAhi50JAjzgB0jCPU8kV4ZbWNAzPiATteFTkBCD2krvJWiZIgLS2JEBsi7DeStc+G2dazcHhgUiCnD5uO2/v0r3j2b7tMmECJLcxAbKokAt5x5bFtNj6yXAAEFAFedmzo1U+GBLEMq8UgnCs3Kde3oRnT2ZmZr5/RAOA65rx4gQIOATjIZMncHZk9fpSFgL+mDmQEYErO3hFT0SrAmxIEPXXLRoQjoXrGP9PJpsPPxeAUNUYMWyKhzS/kbBGqgAHsutGAdt5afbAILvrESR/SacYVZ2wfMvuHZ1LMogtSzp4uUue6ASzl3TWnw4CnOLJ4uSTEOJoZiRf3MTz51YJEiEtO+MhMwvjIO+EO3nYwCCrlxQ9QfyGVzHDshUuWa3Zinc0ZBQJtmZ3ZhErwe76nCdOCwGKssjsNnNm+Y3vZ2b+eHTp30iylBcqqiQSIURzPKS8EnDXSARiGCAEbOl8oqyTl7E7DMkV7uaN3VG/pGOreHc2byW2srNzy+khwB+7j4FYLv4xM/ODMt4tH2ZmPstz1tfDRAhYlRYHGTuPC9l1YpAQ0VY0Irk7JnEhBVtyt2ZsGZuzEiwpKasaAASEQDh6CKK56iQLuZWBYGo5TIa0HedWTTYBF/JSbuRXHBAEQ9dfVMHLLe8gUNWBFQgFO3g5WURHx46M7Ek7VoLsjklpA4HoJICNiAbRjHsfrfNnl/7LTC0AfL2QyBVCkN6YcK7FhaAyaUSGlvyOLVsGtGsFg065s12hZNMeWyX4s5mRfHkdD3gRRE8BlBxNeeGISXdMb8GIdwEnhnhItNSfMQTMzgEDglTrtB6IE5QIx6WmWnVs+73rywjkWRsAdgYihZhVe01p6fGRBS3jlHLnquU6NnImMr8/VF1dLQ+ZTCaXWWk0qVBIv5F0k+5heERx4U48eus+GhmQe6labx5pt7dbaObRnfRLJGaz2WIxb2y1mCX+aiZBv8wtkZiCag8lZOL3qZkEAgoBSuohUIJhLr1EKxKJPODi71jI0TKF1GxFE8QowOJTtpFT6DFu5PrIBJUxVeohOIJgkFK7zOjjpX76Cjm+nwBpocTTFwR+yyl88ZDIGoHDBKHMWG9A/geZmSf3AkyuaKcYiAhLyJ5+IbooxDE8EJEb40Q96/uvjjJXT6n6hmw81tv3xkMi2y82TBApiXFSjd3y+UitGvUknl5I4fUzoLywqKQEYh/vxGKRxUHUChDOMEGsSoyTkBWzYXi1DkGkMQgcMaK48pfLJ1xe7IHLOavdASHEcRgdERUGmWB6iBN4qiC2uRVZzmvmzshP0yo4Dvx3WmVRWdztKtooUblN61Q+LxOHl/7ml2+MPpl3/WtKPbqT+k1updtosHu9dr3BLmPiWFcbzjo3SqogRYUV89aumVk5pUhNx+ZGXp7CbKJwisKt6yjIxGGlYoGAybI9nDs7Fu1BymqSRT4RqIIoqYGgG5mbzlPRTaZQyCVzsTE7QgTaQ3WQjdzDNqSIe0w8bpw4J8BC8N4/VsiJ1hY6AtGnDgIhpLTyZdVO9JxhcsudwVAwAMM3A2cYgqvZRsKF3JGRjYnT24gsW/alYiDUjJTARWlALC6rEqAaOKND600dRAihsAyGo5bE3dXkMBx9GCLgQopLeTXF022Plgouzgb5U0eUj8uZAGsKK2vSUA3k0cUmSx3k6jEzy2eWQzYBM0FRAgrHIU4QopDT40GrxCOyQyZmigvRlIGa4jY4aRasASC/At52PqjIqCnUjNWcDzgQR+ogY6YcfGfXenRQW61S6B1o41HQTPIMBpmRdLmYB0QvCwnFQcQA1DzaljNlevaENJB/xeSG0rm3YQzENr4ebQPDMCKQk4Az7vYcEIRDox8Jyo83//rrxtbWrk3NXStWbG5d0bh556rmzQeaN29e8fOUnV3dOzdvXDtn374Db73V3Lxpxabu1raN3ZsO73rJkJd6iNXEhajVCJEhX36gp2fFa6sONLauncPkLfTfsQBAeePwGxjTBt7Iavl47Zxty5mgn7Fv7YHWtra21sbGxsONKKmHSHshQtvyDZsauw4d6mrdsEHL7KR+IUgIsae/v1SjI8MtbhyeqZWtzEi3bfu4u5uZG40bNmsIJqZqlmZKggheC7c8FBPgxhmFxBY7fGbk2cszkIXg6Wm2Y3Nau7u7D33avGLnvjnbbOGP1USwUQVYSPKIiHpYxqy9S5feNZYXB1FGxiwG4TWcf/bSwHyHTc17Vh1qPrB227ZjUBuMmx/KMERPsBBXMoQ5fhAfQectmV/9cB6vDwgVg5zlJDz9CpXcSi2T+4MigtJjbExSEAkSZYvFOWIBAzlyMpPNZ9fycBdpCWvlquGFSH3cyqrS6YIWwimBbFxRCHbZjIwN8yY+WkJ8CsDlaDzC+cAWkAtF5vBnMMwQ0dczF3HGRwJQYK2Mja/WaPR69UqVQW9sN+zff/DEmqbDpHQpIny/98gX6LD7Kh1Eu52FJEmF3Shj421XKXze/iBvP3XR4HPzPVeeHkI5ygsSIUAGw6VKBBKC92A8BnJh+DjvXoAxkQa0UrlSrRZSEFplpxiR1/lDyz19Q8owEAvRdPmMRIiHjJTKJIioKwx5midkILcALBqdOfHpt488P0TI431DpqZxPuKXftYkQoKBfiGejzGwl1nnS4+eRFPrtl6IPArRnwJywxAhN/cNqcngQNZMHpsI0XuiEEEiRLsHA1M/y4zkh3psWCFTRVzI1CrOHqYKSAHuBv1CQj0YRvwUcXx4BOZmaYqE8ZC81EHGQ+7UmjWRA5FJXJK86mhJJkKw2k8xDFwVGZIPCkIV44uvn1GIoQQGCXkK/eMjT6H2RT4nDwwOciN319q/keBAaAihwe7RarXWgFxHy+U6qU5ejbIsINX56XXffCuXO4nPvwqfdXsMTS81NTnW0Ch5tUomNG1EZ/LK00MWPKZ97En0Xn/dtOh7/WyBeuj1wO3o6wAggBPcUaNJgDSF1H50KCSRa2tdaq0wFqlUKqTfNao9ROkPDOQnsVUrjUWn7O2LqIFMrTv5r25f/Endgtsf3377Y3z+4ufrHn/9+bpp8+c/8uold7/wyO0PDQ6yptjKgTDzwl2SFSm9AZCYw070hXcdOrb/awTgRu3mVgODPPnCw3fXaZ+6e/HCxXw0Gi+/Use//Z6P+NufrJu2mL/w1cFBfOmAAzFAiJs1UyOl3ZoEWRVeQEd+/O5PLeBGNwTI86PevrvuxRtGffT4AgS5+flRdfztCxZPW3xRHf/Ohz5aMDiIAXAh+09UBczjZkVKvTAZYmKbss/38hSAGzk5WMhC/oJHFo56gf/yJw/MH4Ugb78wbSH//ovunv8q/4HHRs1/jH8mkPUH65eF5t4RKWlPEqRrX7jNgIA+E0hy0KqPzyAhNLcSrl8Pg86CnOizliAJ0rg22vu/IQ/x/0eIdsPMbL+6Kj8CcVNJkGVnDcI/QwgZB1l/AtLpFRWR0ownQbLbYl3VuQWJuxzP+l04mT6iIFKaYBIENp4J5LkhQj45PYSI33v2zyaCmpp5/UPwb2NdZfyc40IwLoSbe4bkmHb76SFU/Ex/dLxAS+ZfFoXgICmvDQAiONwfhHflBYPP6zzeACB5gJttEEITFa0kRDJk37Zoj0w814pGviKUmhfzcRSCIDCAIjLgzFE8ABjEMFzrC5lMCkUMQiVDxN0cyPUVI3NySopyqrhrpGWTCKTmxXxjNA6fT99uYeOSSCSoqUWNf51DG4GIQHK6oh03ABX5Uyors4onNVCxI1PYvItd7HiK39WlZMmvUmAKl9GgcLtJmZlMirurxa2iDbTCkqfS6+0Og5/Ok8kcDr1MryRVCvrTFoCSkhfz/wNwtcFevrLR4AAAAABJRU5ErkJggg==", + "description": "Various input forms to set the location, image, and other configuration parameters of the device, asset, or other entity.", + "order": 14000, + "externalId": null, + "name": "Input widgets" }, - "widgetTypes": [ - { - "alias": "markers_placement_openstreetmap", - "name": "Markers Placement - OpenStreetMap", - "image": "data:image/png;base64,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", - "description": "Allows configuring location of the selected entities on the OpenStreetMap.", - "descriptor": { - "type": "latest", - "sizeX": 8.5, - "sizeY": 6.5, - "resources": [], - "templateHtml": "", - "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n", - "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('openstreet-map', false, self.ctx, null, true);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-map-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]},{\"type\":\"function\",\"name\":\"Second point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#607d8b\",\"settings\":{},\"_hash\":0.7867521952070078,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.7040053227577256,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"fitMapBounds\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}

Delete\",\"markerImageSize\":34,\"useColorFunction\":false,\"markerImages\":[],\"useMarkerImageFunction\":false,\"color\":\"#fe7569\",\"mapProvider\":\"OpenStreetMap.Mapnik\",\"showTooltip\":true,\"autocloseTooltip\":true,\"defaultCenterPosition\":\"0,0\",\"customProviderTileUrl\":\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\"showTooltipAction\":\"click\",\"polygonKeyName\":\"coordinates\",\"polygonOpacity\":0.5,\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"zoomOnClick\":true,\"showCoverageOnHover\":true,\"animate\":true,\"maxClusterRadius\":80,\"removeOutsideVisibleBounds\":true,\"defaultZoomLevel\":5,\"provider\":\"openstreet-map\",\"draggableMarker\":true,\"editablePolygon\":true,\"mapPageSize\":16384,\"showPolygon\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${coordinates|ts:7}

Delete\"},\"title\":\"Markers Placement - OpenStreetMap\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"tooltipAction\":[{\"name\":\"delete\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id;\\n });\\n\\nwidgetContext.map.setMarkerLocation(entityDatasource[0], null, null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"54c293c4-9ca6-e34f-dc6a-0271944c1c66\"},{\"name\":\"delete_polygon\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id\\n });\\n\\nwidgetContext.map.savePolygonLocation(entityDatasource[0], null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"6beb7bed-dfd8-388d-b60c-82988ab52f06\"}]},\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"displayTimewindow\":true}" - } - }, - { - "alias": "update_multiple_attributes", - "name": "Update Multiple Attributes", - "image": "data:image/png;base64,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", - "description": "Allows to create an input form and set values for multiple attributes simultaneously.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "\n", - "templateCss": ".tb-toast {\n min-width: 0;\n font-size: 14px !important;\n}", - "controllerScript": "self.onInit = function() {\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n self.ctx.$scope.multipleInputWidget.onDataUpdated();\r\n}\r\n", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-update-multiple-attributes-widget-settings", - "dataKeySettingsDirective": "tb-update-multiple-attributes-key-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update Multiple Attributes\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "device_claiming_widget", - "name": "Device claiming widget", - "image": "data:image/png;base64,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", - "description": "Allows to claim the device using name and optional secret key.", - "descriptor": { - "type": "static", - "sizeX": 7.5, - "sizeY": 4.5, - "resources": [], - "templateHtml": "
\n
\n \n {{deviceLabel}}\n \n \n {{requiredErrorDevice}}\n \n \n \n {{secretKeyLabel}}\n \n \n {{requiredErrorSecretKey}}\n \n \n
\n
\n \n
\n
\n", - "templateCss": ".claim-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n", - "controllerScript": "let $scope;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n}\n\nfunction init() {\n $scope = self.ctx.$scope;\n let $injector = $scope.$injector;\n let utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n let $translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n let deviceService = $scope.$injector.get(self.ctx.servicesMap.get('deviceService'));\n let settings = self.ctx.settings || {};\n \n $scope.toastTargetId = 'device-claiming-widget' + utils.guid();\n $scope.secretKeyField = settings.deviceSecret;\n $scope.showLabel = settings.showLabel;\n\n let titleTemplate = \"\";\n let successfulClaim = utils.customTranslation(settings.successfulClaimDevice, settings.successfulClaimDevice) || $translate.instant('widgets.input-widgets.claim-successful');\n let failedClaimDevice = utils.customTranslation(settings.failedClaimDevice, settings.failedClaimDevice) || $translate.instant('widgets.input-widgets.claim-failed');\n let deviceNotFound = utils.customTranslation(settings.deviceNotFound, settings.deviceNotFound) || $translate.instant('widgets.input-widgets.claim-not-found');\n \n if (settings.widgetTitle && settings.widgetTitle.length) {\n titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n titleTemplate = self.ctx.widgetConfig.title;\n }\n self.ctx.widgetTitle = titleTemplate;\n \n $scope.deviceLabel = utils.customTranslation(settings.deviceLabel, settings.deviceLabel) || $translate.instant('widgets.input-widgets.device-name');\n $scope.requiredErrorDevice= utils.customTranslation(settings.requiredErrorDevice, settings.requiredErrorDevice) || $translate.instant('widgets.input-widgets.device-name-required');\n \n $scope.secretKeyLabel = utils.customTranslation(settings.secretKeyLabel, settings.secretKeyLabel) || $translate.instant('widgets.input-widgets.secret-key');\n $scope.requiredErrorSecretKey= utils.customTranslation(settings.requiredErrorSecretKey, settings.requiredErrorSecretKey) || $translate.instant('widgets.input-widgets.secret-key-required');\n \n $scope.labelClaimButon = utils.customTranslation(settings.labelClaimButon, settings.labelClaimButon) || $translate.instant('widgets.input-widgets.claim-device');\n \n $scope.claimDeviceFormGroup = $scope.fb.group(\n {deviceName: ['', [$scope.validators.required]]}\n );\n if ($scope.secretKeyField) {\n $scope.claimDeviceFormGroup.addControl('deviceSecret', $scope.fb.control('', [$scope.validators.required]));\n }\n \n $scope.claim = function(claimDeviceForm) {\n $scope.loading = true;\n\n let deviceName = $scope.claimDeviceFormGroup.get('deviceName').value;\n let claimRequest = {};\n if ($scope.secretKeyField) {\n claimRequest.secretKey = $scope.claimDeviceFormGroup.get('deviceSecret').value;\n }\n deviceService.claimDevice(deviceName, claimRequest, { ignoreErrors: true }).subscribe(\n function (data) {\n successClaim(claimDeviceForm);\n self.ctx.detectChanges();\n },\n function (error) {\n $scope.loading = false;\n if(error.status == 404) {\n $scope.showErrorToast(deviceNotFound, 'bottom', 'left', $scope.toastTargetId);\n } else {\n let errorMessage = failedClaimDevice;\n if (error.status !== 400) {\n if (error.error && error.error.message) {\n errorMessage = error.error.message;\n }\n }\n $scope.showErrorToast(errorMessage, 'bottom', 'left', $scope.toastTargetId);\n } \n self.ctx.detectChanges();\n }\n );\n }\n\n function successClaim(claimDeviceForm) {\n let deviceObj = {\n deviceName: ''\n };\n if ($scope.secretKeyField) {\n deviceObj.deviceSecret = '';\n } \n claimDeviceForm.resetForm(); \n $scope.claimDeviceFormGroup.reset(deviceObj);\n $scope.loading = false;\n $scope.showSuccessToast(successfulClaim, 2000, 'bottom', 'left', $scope.toastTargetId);\n self.ctx.updateAliases();\n }\n \n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-device-claiming-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"deviceSecret\":true,\"showLabel\":true},\"title\":\"Device claiming widget\",\"dropShadow\":true,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":false,\"enableDataExport\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "markers_placement_image_map", - "name": "Markers Placement - Image Map", - "image": "data:image/png;base64,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", - "description": "Allows configuring location of the selected entities on the image map.", - "descriptor": { - "type": "latest", - "sizeX": 8.5, - "sizeY": 6.5, - "resources": [], - "templateHtml": "", - "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n", - "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('image-map', false, self.ctx, null, true);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-map-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 0.2;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || 0.3;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]},{\"type\":\"function\",\"name\":\"Second point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 0.6;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || 0.7;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showLabel\":true,\"label\":\"${entityName}\",\"tooltipPattern\":\"${entityName}

X Pos: ${xPos:2}
Y Pos: ${yPos:2}

Delete\",\"markerImageSize\":34,\"useColorFunction\":false,\"markerImages\":[],\"useMarkerImageFunction\":false,\"color\":\"#fe7569\",\"mapImageUrl\":\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiCiAgIHhtbG5zOmlua3NjYXBlPSJodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy9uYW1lc3BhY2VzL2lua3NjYXBlIgogICB3aWR0aD0iMTEzNC41MTgzIgogICBoZWlnaHQ9Ijc2Mi43ODI0MSIKICAgaWQ9InN2ZzIiCiAgIHZlcnNpb249IjEuMSIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMC40OC41IHIxMDA0MCIKICAgc29kaXBvZGk6ZG9jbmFtZT0id2ljaGl0YW1hcC1ub2xpYi5zdmciPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0IiAvPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSIwLjM1IgogICAgIGlua3NjYXBlOmN4PSI4OS45MDc4NTciCiAgICAgaW5rc2NhcGU6Y3k9IjQ1My43ODI0MSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzNjYiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iNzIxIgogICAgIGlua3NjYXBlOndpbmRvdy14PSItNCIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTQiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSIKICAgICBpbmtzY2FwZTpvYmplY3QtcGF0aHM9InRydWUiCiAgICAgaW5rc2NhcGU6c25hcC1nbG9iYWw9ImZhbHNlIgogICAgIHNob3dndWlkZXM9InRydWUiCiAgICAgaW5rc2NhcGU6Z3VpZGUtYmJveD0idHJ1ZSIKICAgICBmaXQtbWFyZ2luLXRvcD0iMCIKICAgICBmaXQtbWFyZ2luLWxlZnQ9IjAiCiAgICAgZml0LW1hcmdpbi1yaWdodD0iMCIKICAgICBmaXQtbWFyZ2luLWJvdHRvbT0iMCIgLz4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGE3Ij4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yawogICAgICAgICByZGY6YWJvdXQ9IiI+CiAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+CiAgICAgICAgPGRjOnR5cGUKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPgogICAgICAgIDxkYzp0aXRsZT48L2RjOnRpdGxlPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KICA8ZwogICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgIGlua3NjYXBlOmdyb3VwbW9kZT0ibGF5ZXIiCiAgICAgaWQ9ImxheWVyMSIKICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjcuMDcxNDI4LC0zMDcuOTAyOTkpIj4KICAgIDxwYXRoCiAgICAgICBpZD0icGF0aDM3ODciCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMzY0ZTU5O3N0cm9rZS13aWR0aDoyLjk5OTk5OTc2O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICBkPSJtIDkwNi4wMzMxNSw3MDYuMTMzNjcgMy40MjkyLDE3Ljc5NTUyIE0gMjguNTcxNDI4LDc2NS4wNTA2NyBjIDE1MC40MzUyMDIsNi44MzM0MiAxNDYuMzkyMzIyLC0yNi4zMzQxNSAxNjYuNDM0NTQyLC0yOS4zMjAwOSAzNi4xNDM3NSwtNS4zODQ3NiAxMTQuMjg2NzYsLTYuNTI1NCAxNDguMzI1MDgsLTguNjIzNTQgNDMuMzc4MDgsLTIuNjczODUgMTQxLjc2MjIxLC0xMS4yMzA5OSAxODguODU1NzgsLTE5LjgzNDE4IDM5LjgxMTM4LC03LjI3Mjg0IDIyMS4zNjk5MSwtMC44NjIzNSAzMTkuMDcxNDEsLTAuODYyMzUgNzAuODI3MzUsMCAxNDYuOTE4NjcsLTEuNzI0NyAyMTguMTc1ODYsLTEuNzI0NyAtMzEuNjE5NywwIDExNy44NTUyLC0yLjU4NzA3IDg2LjIzNTUsLTIuNTg3MDcgbSAtMjUuMDkwNywtNjguMTI2MDYgYyAtNTIuNzk5NiwzNC43ODQ4NCAtNjUuODk1MSw1MS43NDg2NSAtOTUuNjM5LDgxLjQ5MjU4IC0yNC45MzEzLDI0LjkzMTI3IC0xNDAuMzk2NTMsLTE5LjEzOTIgLTE3OC45Mzg3MSwzNi42NTAwNyAtMTIuMjgxNCwxNy43NzcxNSAtNDcuMDAyNTcsNDYuNTQ2NTMgLTY1LjEwNzgzLDU5LjA3MTMzIC0yMC4xMDUsMTMuOTA4MTggLTU2LjAzNjcyLDQ0Ljk1NjY0IC02Ny43Njg4NSw3My4wNzgyNyAtNC44MDE0NywxMS41MDkwMiAtMTMuMzgwNDYsMzUuOTkyOTggLTIzLjQ0OTQ5LDQ2LjA2MjAxIC0xMC40OTY5OSwxMC40OTY5OSAtMzguMzc3MzMsNi4zODU2OSAtNDQuMDIzNDUsMTcuNjQ3NjQgLTE5LjAwNTAyLDM3LjkwODEyIC0yNS40NjUzLDEwMC45MjM1MiAtNjcuNjE3ODksMTAyLjA1MTAyIG0gMTkuMjgxNTEsLTYyNC4wMTQ2NCBjIDM0LjY1OTM0LC0xLjg3MzgyIDg0LjAyNzMzLDcuMzkxMzEgMTA5LjkwMDcxLC00LjI4NTQ1IDEzLjI4MTcyLC01Ljk5NDA4IDQxLjQwNzIxLC0yLjQ2MTM1IDY2LjgyODY2LC0yLjMyMDQ2IDM1LjMyMjM4LDAuMTk1NzggNjQuMzgyNDksMC42MzQ3NyAxMDEuOTE2Nyw1LjAyMzIgMjUuMDMwMzYsMi45MjY1IDQ0LjY2MjczLDM0LjI4NzIyIDU4LjUyNjk4LDUwLjY0MzkgMTcuMDk4NzgsMjAuMTcyNjggNjIuNzYzODYsLTEuNzE0NjcgNjYuMzA1NjYsMzIuMTM0MzMgNS4xMDI3LDQ4Ljc2NTg3IC02LjMyODQsNzguNjM3MjUgNi4xNDExLDk3LjM0MTUgMTkuOTY5MiwyOS45NTM3OSA1MC40ODY0LDE3Ljg1NTc5IDQ0LjYxOTMsODMuOTcxMTkgTSA1ODkuMTAyMjcsMzA5LjcyNzE1IGMgNC42NDM0NiwyMy43MjkyMyAxNS4wNjkwNCw3Mi43NzU3NSAxOS4wNjEyOCwxMzAuNjQyODggMC44NzIwNiwxMi42NDA0OCA1LjQ0NzE4LDI0Ljk5MjUzIDQuMjIyMzEsNDUuMjc3NTcgLTIuNTE3MjEsNDEuNjg3NSAtMTUuNzE3MDYsNDMuNjc3MjcgLTE1LjA5MTIyLDYwLjM2NDg2IDEuNDMxOTUsMzguMTgyMjQgMzAuNjEzNjEsOTMuODM3MTkgMzAuNjEzNjEsMTM5LjcwMTU0IDAsMjQuMTgwOCAtMi42Njk2NCwxMTUuMzkwNDUgNy4zMzAwMSwxMzUuMzg5NzYgMC4xNTkxMSwwLjMxODIxIDEwLjA2NDc2LDM1Ljg4MzMyIDEwLjc3OTQ1LDQ5LjE1NDI0IDAuOTQzNzgsMTcuNTI0NjkgLTI0LjQ3OCwzOS40NzAwOCAtMjguMDI2NTUsNDYuNTY3MTYgLTUuNDc3NywxMC45NTUzOSAtMzYuOTczMjQsMTAuODgxOTcgLTQwLjA5OTUsMjQuMTQ1OTUgLTMuODY4ODQsMTYuNDE0NTEgLTMuODY2Myw0My43OTczNSA0LjA0NjQ3LDU5LjQ0MTI5IG0gOTcuMzM3MzQsLTY5MS4wMDk0MSBjIC01LjAxMzMyLDM1LjUxNTk1IC00My42NTkwMSwxMS4zMTY1MiAtNTguNTM4NjEsMjMuNzgxMzEgLTIxLjMzMDE5LDE3Ljg2ODUyIC02Mi40OTk2NCwzMS40MzIxMiAtNzAuMTI0MzcsMzUuMzY3MDggLTM1LjA4NzYzLDE4LjEwNzkzIC0xMTAuNDcyMTUsLTE1LjE0MTk2IC0xMjUuNjE0MSw0LjI2ODQzIC0xNS45NTA2MywyMC40NDcwMyAtMC4wNzM1LDYxLjQ2NjQ4IC05LjE0NjY2LDg0LjE0OTI0IC02LjAzNTcsMTUuMDg5MjYgLTE4Ljg3NjcsMjMuMDE3MzQgLTI3LjQzOTk3LDMyLjkyNzk4IC0xOS43NDgyOSwyMi44NTU1NSAtNjkuOTc0MjgsNjkuODI0MTkgLTg0Ljc1OTA0LDEwMC4wMDM0NiAtNy40OTc0MSwxNS4zMDQwNCAtMy4yODQyNiw0NC40MjA0MSAtMy40NzA1Myw2My4zNDI4NCAtMC4xMjc5MywxMi45OTQxNCAtMC44MTAxNSwyMy4xMDM4NSAyLjQwMzQzLDI4LjI3NjE4IDQuOTYxNTgsNy45ODU4MSAyMy43MjA1LDI4LjExMjA3IDI0LjIzODY1LDUwLjYxMTQ5IDAuMjk0MTEsMTIuNzcxNDYgMC4wMTMzLDc4LjU5MTAxIDMuMDQ4ODgsODcuNjU1NDkgMi4zMTI1Niw2LjkwNTQ2IDQuMjIwMDQsMjYuNTY0OTcgMTAuMjEzNzcsMzYuNTg2NjIgMTEuMzU0MDEsMTguOTg0MTUgNC4zODczNyw0MC4xNTY2MiAyNy44OTczLDUzLjUwNzk1IDE5LjA1MDEyLDEwLjgxODU5IDQ2Ljg3NzgxLDEyLjIxODYyIDgxLjkyNjE4LDE0LjQ2MDU0IDMzLjcwMzQ1LDIuMTU1ODkgNjEuNTEyMTcsLTEuNDMwMzUgNzYuOTIwNzcsNi4xNDExIDExLjU4NTA4LDUuNjkyNjYgOC41ODE1MSwxNy45MzM0NCAxNC4yOTU0MSwyOS4zNjEyMyA1LjY0MDQyLDExLjI4MDg1IDMxLjUwMjYzLDExLjE1NjI3IDQxLjgwNDA5LDQzLjQ1NDg3IDcuNjA1OSwyMy44NDcxIDMuMDg1OTMsNDQuMTU2OSA2LjcwNzU1LDY1Ljg4NjYiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2Nzc3NzY2Njc3Nzc3NzY2Nzc3Nzc3NjY3Nzc3Njc3NzY2Nzc3Nzc3Nzc3Nzc3Nzc3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gNDMuMjc3ODgxLDUxNy45NDY3OSBjIDAsMCAyMzAuODQ4Mjg5LC0zLjYzODA1IDI1MC4wMDg2MzksLTMuNjU4NjcgNy40ODIyMiwtMC4wMDggOC42MTk1NCw1LjE1MTk0IDE0LjAyMDksMTEuNDU4NjkgMjQuNTk2MDgsMjguNzE4OTMgOTMuOTA5NjYsMTEyLjkzNTg1IDkzLjkwOTY2LDExMi45MzU4NSIKICAgICAgIGlkPSJwYXRoMzc4OSIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzc2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDM1Ljk2MDU1NSw1NzcuNzA0OTQgYyAwLDAgMTY1LjUyNDU2NSwtMS42ODQ1NCAyNDguNzc5NTY1LC0xLjY4NDU0IDQuOTQ3NDksMCA3LjcyOTkzLC0yLjg4MzMgMTAuNTM3NzEsLTUuNzI5NzcgOS42NjEwNywtOS43OTQxNiAyNS42MzE5OSwtMjguNTg5OTUgMjUuNjMxOTksLTI4LjU4OTk1IgogICAgICAgaWQ9InBhdGgzNzkxIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOiMzMzMzNjY7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Ik0gMzguMzk5NjYzLDY0MS43MzE1NSA0MzEuNzA1OTMsNjM3LjQ2MzExIgogICAgICAgaWQ9InBhdGgzNzk1IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOiMzMzMzNjY7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Ik0gMzkuMDA5NDQyLDcwNC41Mzg1OSA1MjMuMTcyNTMsNjk3LjgzMTA0IgogICAgICAgaWQ9InBhdGgzNzk3IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gMzAzLjk1NzYyLDY4Mi41ODY2MSAxNDYuNzk1NDIsMS44MjkzMyBjIDEwLjUzNDAzLDAuMTMxMjcgMTQuMzQzNzQsLTIuNjM3MzkgMjUuNDg3MTUsLTYuMzcyOCAxMC40MTIxMiwtMy40OTAyNyAzMS40MjQxNSwtMi42OTg5NiA0MS4zODUzOCwtMi43NzM4NSBsIDQwNS41NjA3OSwtMy4wNDg5IgogICAgICAgaWQ9InBhdGgzNzk5IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3Nzc2MiIC8+CiAgICA8cGF0aAogICAgICAgaWQ9InBhdGgzODA0IgogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDQyNi4yMTc5NCwzMTQuODkwOTggYyAyLjA2NzU0LDkuMDUyNzMgMS44NDE3Nyw1MS43Mjc3NyA2LjUwNzk0LDc0LjgzNDY2IDEuNjc0NzUsOC4yOTMzNiA4LjY3NTA4LDE0LjA2NTk4IDEwLjA1NTQxLDE0Ljg1ODYyIDQuOTAxNDcsMi44MTQ2MyAxMC44MTQ3OSw4LjE0OTgyIDEzLjA0NTc5LDE2LjA4ODMxIDYuNzU3NzksMjQuMDQ1OTEgMC44Nzk3Miw2OC40NTIxMiAwLjg3OTcyLDExMC42ODkzIDAsNi4wOTc4MiAxLjY2MDEsMzAuMTQ2NiAtMi4xNTU4OCwzMy45NjI1OSAtMi41NDA4NSwyLjU0MDgzIC0wLjI4MTYzLDEyLjk5MDY5IC0zLjQzNjc1LDE2LjE0Mzc3IGwgLTkuODQ5NDQsOS44NDMxMSBjIC0xMC4zNjcxNSwxMC4zNjA0NyAtMTEuNTkwMTcsNi41MjYxNCAtMTcuNzM4NDgsMTguODIyNzYgLTMuNTY3NzIsNy4xMzU0MyA1LjQwMjM1LDIwLjY3MjEgNy4zNTQzMiwyNC41NzYwMiAxLjkzMjE0LDMuODY0MyAtMS44NDIxNiw0Ljc3NzczIC0xLjc5MjM1LDcuNDQ2MjYgMC4yNTI4NiwxMy41NDQ4MyAyLjI5NzUsMzczLjkyNzEyIDIuMjk3NSwzNzMuOTI3MTIiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjc3Nzc3Nzc3Nzc2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDM2NS4yNDAyMiw1MTkuNzc2MTIgNC4xMTU5OSw1MDIuMTUxNTgiCiAgICAgICBpZD0icGF0aDM4MDYiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSAxMTYuNTMxNjUsNTA0LjE4Njk5IDMuODgwNTksMzEwLjk2NDM2IgogICAgICAgaWQ9InBhdGgzODMxIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBpZD0icGF0aDM4ODkiCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gMzE3LjY3NzYsNTc2LjQ4NTM5IDEzMC4xODc0MiwxLjUyNDQ0IGMgNC41MTA3OSwzLjI0MTY5IDIwLjM0NDcxLDcuOTY4NTMgMjcuNzQ0ODYsNC4yNjg0NCAzLjE1NTQ2LC0xLjU3NzcyIDkuNDE5LC01LjM4ODE3IDE0LjAyNDg5LC0zLjk2MzU1IDQuMjY2OTgsMS4zMTk4MSA2LjAxNjg5LDMuMTE2MzIgMTAuMzY2MjEsMy4wNDg4OSAxMC4zMDQwMywtMC4xNTk3NSAyMC4yMTE3LDAuMzg3NDEgMzAuNDg4ODYsMC4zMDQ4OSAxNzcuODkwOCwtMS40MjgyNyAzNTYuNTkwMzUsLTIuMTMyNDcgNTM0Ljc3NDU2LC0zLjA0ODg4IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2Nzc3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gNDc1LjMwNTAxLDU4Mi44ODgwNSBjIC0zLjQ0NDE4LDExLjM1MDY2IC0yLjEwMzQzLDEyLjQzMzczIDMuNjU4NjUsMjEuMDM3MzEgMy43OTQ0NSw1LjY2NTY0IDUwLjg2MjYxLDEzLjAzODQ1IDQxLjQ2NDg1LDI3LjEzNTA5IC0xMC41MzY5NywxNS44MDU0NyAtMjIuODk3NDUsLTUuNDc3NzIgLTMzLjg0MjYzLC0xLjgyOTMzIC01LjQ1MjM2LDEuODE3NDUgLTcuMzQ5MDEsNS40NTYzMSAtMy42NTg2Niw5LjE0NjY1IDIuODA2ODMsMi44MDY4NCA0LjA0OCwxLjgwMzk2IDYuNTIwMzQsNS4xMDA0MSIKICAgICAgIGlkPSJwYXRoMzkxMCIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzc3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gNDMyLjAxMDgyLDYzNi44NTMzMyBjIDguMzE4OTksMTMuMTEwMTYgMTguODQ2MjEsMTQuNjM0NjUgMzUuNjcxOTYsMTQuNjM0NjUgMi45Mzg2NSwwIDcuODY5OTgsLTAuOTMzNzEgMTAuNjcxMTEsMCAxMS4zNTkxNywzLjc4NjM5IDI3LjE5Mzk4LDEwLjI3NTc3IDM2LjIwMTkzLDIxLjEyOTQ4IDguMjgwMDIsOS45NzY2MSAxMC4yNTI3OCwyMy44ODMwOCA3LjcwMjAyLDM3LjEwNDI0IC02LjE2OTg5LDMxLjk3OTk4IC0xNi43MTQzMSw1Ni45ODg1MyAtMTkuMDQzNTUsODYuNTY5MDUgLTEuMzQ3OTgsMTcuMTE4OCA0LjUwOTU3LDIyLjUzNTIyIDExLjA3MTQzLDMzLjkyODU3IDEwLjY3MDIzLDE4LjUyNjcyIDguNzI0NTMsMTQuMTk5NTUgOC41NzE0MywzNC4yODU3MiAtMC4xMzk2MywxOC4zMTk0NCAwLDYwLjI2Mzg1IDAsODAuNzE0MjkiCiAgICAgICBpZD0icGF0aDM5MTIiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjc3Nzc3Nzc2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDUyOC41MDgwNiw2NTguOTU3NzYgYyAtMTAuNjgxMjMsMC45MDQ1NCAtNy4xMDgwNCwtNS42MDI1NSAtMTAuODIzNTQsLTguMDc5NTYgLTQuNzg0NTQsLTMuMTg5NjkgLTEyLjIyNzA0LC0xLjI1MTA0IC0xNi43Njg4OCwtNS43OTI4OCAtMC42NjYxMiwtMC42NjYxMiAtOC44MDk2OSwtNC4xMDg3NyAtMTAuMTc0NDcsLTIuNzQzOTkgLTguMzY0NTksOC4zNjQ1OSAtMy4wNDg4OCwyMC41NTE4OCAtMy4wNDg4OCwzMy41Mzc3NCBsIDMuMDIyLDMzOS42OTc0MyIKICAgICAgIGlkPSJwYXRoMzkxNCIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzc3NjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSA1MTcuOTg5NDEsNjUxLjAzMDY1IGMgLTAuMjIxNzEsLTIuNzAxODQgMS45MDM0NiwtNS41NjIxMyAzLjM1Mzc3LC03LjAxMjQ1IDEuNzk5NDMsLTEuNzk5NDIgNi45MjI5NCwxLjAwNDE5IDguODQxNzgsLTAuOTE0NjYgMC4yODc2NSwtMC4yODc2NiAwLjg0MzI5LC0xMS4xNjQxIDAuMjI4NjYsLTEzLjU2NzUzIC0yLjA2NDgzLC04LjA3NDE2IC0yLjA1ODAxLC0yOC42NTY1OCAtMi4wNTgwMSwtMzguNzIwODYgbCAwLC03My4xNzMyNiIKICAgICAgIGlkPSJwYXRoMzkxNiIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzY3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gNTI4LjY2MDUsNjc1LjQyMTczIC0wLjQ1NzMzLC0zMS41NTU5NiIKICAgICAgIGlkPSJwYXRoMzk3NCIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDc2Ni4zMTYyNSw1NzkuNjQ0MzEgMC40MzExOCwxMy43OTc2OCBjIDMuMTM2NDMsNC42NjkxNSAzLjAxODI0LDkuNjAwNjggMy4wMTgyNCwxNi4zODQ3NSBsIDAsMTU3LjM3OTgxIgogICAgICAgaWQ9InBhdGgzOTgyIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gMTEyMi45MDAxLDc2NS45MTMwMyBjIC0yMDIuMzA2NjksNC42OTA1IC00MDMuNzQ0MDUsLTEuMTEzODEgLTYwNS45NTQ1NCwzLjM1MzkgLTEwLjg2MzYyLDAuMjQwMDIgLTMuMzYxNDcsLTguNTg2MyAtMjguNTM2OCwtOC41ODYzIgogICAgICAgaWQ9InBhdGgzOTg0IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSA4NjAuMDA4MDUsNzM3LjA2NjUxIGMgMCwwIC05Ny40NDc1LDAuODU4MDYgLTE0Ny41Njg5MiwwLjg1ODA2IC01LjI2ODYxLDAgLTQuNTE1NDYsLTguMzI5ODYgLTcuMzAwODksLTguMzI5ODYgLTMuOTc0MzUsMCAtOC42MjkyNSwwLjAyMDEgLTEwLjUwOTQ4LDAuMDM1OSAtMi4zMzQ3NywwLjAxOTcgLTEuODEwOTQsOC4zNjU5NyAtNC4xNDU4LDguMzY2OTIgLTQ2LjE2ODk5LDAuMDE4OCAtMTY3LjQwNzY3LC0xLjMwNzk5IC0xNzUuMDUyNjMsLTEuMzA3OTkgLTQuNDI5NTUsMCAtOC41NzYyNywtNi40Mzk3MiAtMTMuMTMxOTgsLTYuNDM5NzIgLTEuMzYxMTUsMCAtNi4yMzg3MywwIC0xNC4zOTQ2NywwIgogICAgICAgaWQ9InBhdGgzOTg2IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3Nzc3Nzc2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJNIDY3NS4wMDcwMyw4MzEuMTc0MDIgNjc0LjM5NzI1LDMwOS40MDI5OSIKICAgICAgIGlkPSJwYXRoMzk4OCIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDc5OS40MDE1NywzMTMuMDYxNjUgMS4yMTk1NSw0OTUuODY2NTMiCiAgICAgICBpZD0icGF0aDM5OTAiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSA3MzYuNTk0NTIsMzEyLjQ1MTg4IC0xLjIxOTU1LDcxNi40ODgyMiIKICAgICAgIGlkPSJwYXRoMzk5MiIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDUzMC4wMzA5NCw2NDMuNDU4NTkgMzkyLjM3MTU5LC0zLjAxODI1IgogICAgICAgaWQ9InBhdGg0MDQ4IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gODU5LjQ1MDYsMzE0LjkwMTI4IDEuMjkzNTQsNTA3Ljk4MDU4IgogICAgICAgaWQ9InBhdGg0MDUwIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjAuOTk5OTk5OTRweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gOTIxLjU0MDE3LDMxMC41ODk0OSAxLjcyNDcxLDUzMS43NTIyNyIKICAgICAgIGlkPSJwYXRoNDA1MiIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDczNi4yODk2Myw0NTMuMzEwNCAxODUuNjc3MTUsLTAuMzA0ODkiCiAgICAgICBpZD0icGF0aDQxODciCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSAxMDYwLjgxMDUsNTE0Ljk2NzY3IGMgMCwwIC0zNjMuMjgxMjYsLTUuNjI2MTggLTU0NC42NTA0MiwyLjUyMTc4IC00LjE3Nzc2LDAuMTg3NjkgLTEyLjUwMDQ0LDEuMDY3MTEgLTEyLjUwMDQ0LDEuMDY3MTEgLTEuNTcwOTUsMC4xMzQxIC0yLjAwMDkzLC0yLjMyNDk1IC0yLjU5MTU1LC0zLjUwNjIzIC0wLjA5NjcsLTAuMTkzNDMgLTcuMDYwODEsLTEuOTMzNCAtNy42MjIyMSwtMS4zNzE5OSAtMi44OTMxNCwyLjg5MzE0IC03LjYzMTY3LDQuMjQ4NjkgLTEyLjE5NTU1LDQuMTE2IEwgMzY5LjIwMTcsNTE0LjUzNjUiCiAgICAgICBpZD0icGF0aDQyNjEiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjc3Nzc3NjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSAzOTkuODE1MzEsNDc5LjYxMTEyIDExLjY0MTgsNS42MDUzIGMgMi45ODQxMiwxLjQzNjc5IDYuNTI4NzgsLTAuNDc3MTIgOS45MTcwOCwtMC40MzExOCBsIDEyNy4xOTczOSwxLjcyNDcxIgogICAgICAgaWQ9InBhdGg0MjYzIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Ik0gNTE5LjI1MTUxLDUxNy4xMjM1NyA1MTguODIwMzIsMzA4LjQzMzYyIgogICAgICAgaWQ9InBhdGg0MjY1IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gNDMyLjkyNTQ5LDM4OS43MTQ5OCBjIDExLjA0NDk2LDAgMzUuNTMzMDcsMC42MTkyNyA0Mi41Nzk3OCwtMS4wMDM5NyA4LjQwNTIyLC0xLjkzNjE4IDcuMDY2LC02Ljk1Mzc4IDE0LjE5NzEyLC02Ljk1Mzc4IDcuODA5NSwwIDYuNTQyOTEsOC4wNjIzNyAyMC4xNDE3LDguMDYyMzcgMTMuOTkwNjgsMCA0NC45NzY4OSwwLjM3ODg2IDYzLjkzOTkyLDAuMzc4ODYgMTIuMDgzOTUsMCA4Mi4wMDI2NiwwLjMwNDg5IDkzLjYwMDgxLDAuMzA0ODkgOC43NjA0NywwIDEzLjE1OTcsLTIuMjg4MjcgMjEuMzQyMTksLTcuMDEyNDMgNy4xOTUxNSwtNC4xNTQxMyAyLjA1NDU5LC05LjQ5MTM3IDIwLjQyNzU0LC04Ljg0MTc3IDIzLjE0NTQsMC44MTgzMyAxMi42NDMzNCwxNC4wMjQ4NyAzMi4zMTgxOSwxNC4wMjQ4NyAyNS4zNTk1NCwwIDEzMC45OTkwMiwwIDE1MC45MTk4NSwwIDE0LjMzMjQ0LDAgLTQuMTE5MTEsLTEzLjExMDIxIDI5LjI2OTMsLTEzLjQxNTEiCiAgICAgICBpZD0icGF0aDQyNjkiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjc3Nzc3Nzc3NzYyIgLz4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjU4OC42Nzk1NyIKICAgICAgIHk9IjczNS44MDQ2MyIKICAgICAgIGlkPSJ0ZXh0NDMxMCIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDMxMiIKICAgICAgICAgeD0iNTg4LjY3OTU3IgogICAgICAgICB5PSI3MzUuODA0NjMiPkxpbmNvbG48L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjY4Ni4zOTg1IgogICAgICAgeT0iNzY1LjYyODQyIgogICAgICAgaWQ9InRleHQ0MzEwLTciCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQzMTItNiIKICAgICAgICAgeD0iNjg2LjM5ODUiCiAgICAgICAgIHk9Ijc2NS42Mjg0MiI+SGFycnk8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjcwOS44NzE4MyIKICAgICAgIHk9Ii04MDIuMzc3MzgiCiAgICAgICBpZD0idGV4dDQzMTAtNy0xIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDMxMi02LTgiCiAgICAgICAgIHg9IjcwOS44NzE4MyIKICAgICAgICAgeT0iLTgwMi4zNzczOCI+V29vZGxhd248L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjU2Mi4xMTkyNiIKICAgICAgIHk9Ii03NzEuOTY4MTQiCiAgICAgICBpZD0idGV4dDQzMTAtNy0xLTkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40MzEyLTYtOC0yIgogICAgICAgICB4PSI1NjIuMTE5MjYiCiAgICAgICAgIHk9Ii03NzEuOTY4MTQiPkVkZ2Vtb29yPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1OTguMzA0ODciCiAgICAgICB5PSItNzM4LjM2NjQ2IgogICAgICAgaWQ9InRleHQ0MzEwLTctMS05LTciCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40MzEyLTYtOC0yLTkiCiAgICAgICAgIHg9IjU5OC4zMDQ4NyIKICAgICAgICAgeT0iLTczOC4zNjY0NiI+T2xpdmVyPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1OTIuMTIyODYiCiAgICAgICB5PSItNjc3LjIwMzk4IgogICAgICAgaWQ9InRleHQ0MzEwLTctMS05LTctNSIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMCwxLC0xLDAsMCwwKSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQzMTItNi04LTItOS00IgogICAgICAgICB4PSI1OTIuMTIyODYiCiAgICAgICAgIHk9Ii02NzcuMjAzOTgiPkhpbGxzaWRlPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1OTcuMzI3MDkiCiAgICAgICB5PSItODYyLjYxNDA3IgogICAgICAgaWQ9InRleHQ0MzEwLTctMS05LTctNS0zIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDMxMi02LTgtMi05LTQtMSIKICAgICAgICAgeD0iNTk3LjMyNzA5IgogICAgICAgICB5PSItODYyLjYxNDA3Ij5Sb2NrPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1ODcuMzcwMTgiCiAgICAgICB5PSItOTI2LjEzNjYiCiAgICAgICBpZD0idGV4dDQzMTAtNy0xLTktNy01LTMtMiIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMCwxLC0xLDAsMCwwKSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQzMTItNi04LTItOS00LTEtMyIKICAgICAgICAgeD0iNTg3LjM3MDE4IgogICAgICAgICB5PSItOTI2LjEzNjYiPldlYmI8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9Ijg3MS4xNjEwMSIKICAgICAgIHk9IjYzNy41NzUyIgogICAgICAgaWQ9InRleHQ0NDY1IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40NDY3IgogICAgICAgICB4PSI4NzEuMTYxMDEiCiAgICAgICAgIHk9IjYzNy41NzUyIj5DZW50cmFsPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI4NzMuODMyMjgiCiAgICAgICB5PSI1NzcuMDMyNDciCiAgICAgICBpZD0idGV4dDQ0NjUtMyIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDQ2Ny00IgogICAgICAgICB4PSI4NzMuODMyMjgiCiAgICAgICAgIHk9IjU3Ny4wMzI0NyI+MTN0aDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgaWQ9InRleHQ0NDkwIgogICAgICAgeT0iNTEwLjI2MTgxIgogICAgICAgeD0iODc1Ljk2NjQ5IgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbgogICAgICAgICB5PSI1MTAuMjYxODEiCiAgICAgICAgIHg9Ijg3NS45NjY0OSIKICAgICAgICAgaWQ9InRzcGFuNDQ5MiIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+MjFzdDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iODgxLjMxNjU5IgogICAgICAgeT0iNDUwLjE5ODc2IgogICAgICAgaWQ9InRleHQ0NDk0IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40NDk2IgogICAgICAgICB4PSI4ODEuMzE2NTkiCiAgICAgICAgIHk9IjQ1MC4xOTg3NiI+Mjl0aDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iNjE1Ljc5MjQ4IgogICAgICAgeT0iMzg3Ljc0NzE2IgogICAgICAgaWQ9InRleHQ0NDY1LTMtMSIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDQ2Ny00LTEiCiAgICAgICAgIHg9IjYxNS43OTI0OCIKICAgICAgICAgeT0iMzg3Ljc0NzE2Ij4zN3RoPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ1MTkiCiAgICAgICB5PSI0ODEuNjUyODYiCiAgICAgICB4PSI0ODQuNjkwMzciCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9IjQ4MS42NTI4NiIKICAgICAgICAgeD0iNDg0LjY5MDM3IgogICAgICAgICBpZD0idHNwYW40NTIxIgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIj4yNXRoPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1NjMuMDQ2NzUiCiAgICAgICB5PSI1MTMuMzYxMzMiCiAgICAgICBpZD0idGV4dDQ1MjMiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQ1MjUiCiAgICAgICAgIHg9IjU2My4wNDY3NSIKICAgICAgICAgeT0iNTEzLjM2MTMzIj4yMXN0PC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ1MjciCiAgICAgICB5PSI1NzcuODk0ODQiCiAgICAgICB4PSI1NjUuOTcxNSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iNTc3Ljg5NDg0IgogICAgICAgICB4PSI1NjUuOTcxNSIKICAgICAgICAgaWQ9InRzcGFuNDUyOSIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+MTN0aDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDUzMSIKICAgICAgIHk9Ii00NjAuNzMzMTIiCiAgICAgICB4PSI0MzMuNTgwNzUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9Ii00NjAuNzMzMTIiCiAgICAgICAgIHg9IjQzMy41ODA3NSIKICAgICAgICAgaWQ9InRzcGFuNDUzMyIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+QW1pZG9uPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI0MDUuNTMwOTgiCiAgICAgICB5PSItNTIzLjU0MDE2IgogICAgICAgaWQ9InRleHQ0NTM1IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDUzNyIKICAgICAgICAgeD0iNDA1LjUzMDk4IgogICAgICAgICB5PSItNTIzLjU0MDE2Ij5BcmthbnNhczwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDUzOSIKICAgICAgIHk9Ii0zNzIuNTg1OTQiCiAgICAgICB4PSI3NDUuNDg0NjIiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9Ii0zNzIuNTg1OTQiCiAgICAgICAgIHg9Ijc0NS40ODQ2MiIKICAgICAgICAgaWQ9InRzcGFuNDU0MSIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+V2VzdDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iNTk2LjcyODMzIgogICAgICAgeT0iLTUzMS4yNTkyOCIKICAgICAgIGlkPSJ0ZXh0NDU0MyIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMCwxLC0xLDAsMCwwKSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQ1NDUiCiAgICAgICAgIHg9IjU5Ni43MjgzMyIKICAgICAgICAgeT0iLTUzMS4yNTkyOCI+V2FjbzwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDU1NSIKICAgICAgIHk9Ii0xMjIuNTAyOTUiCiAgICAgICB4PSI1OTUuNDM0ODEiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9Ii0xMjIuNTAyOTUiCiAgICAgICAgIHg9IjU5NS40MzQ4MSIKICAgICAgICAgaWQ9InRzcGFuNDU1NyIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+TWF6aWU8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjY5NS43NzI5NSIKICAgICAgIHk9IjE2Mi4wNjg3NyIKICAgICAgIGlkPSJ0ZXh0NDU1OSIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMC43MDcxMDY3OCwwLjcwNzEwNjc4LC0wLjcwNzEwNjc4LDAuNzA3MTA2NzgsMCwwKSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQ1NjEiCiAgICAgICAgIHg9IjY5NS43NzI5NSIKICAgICAgICAgeT0iMTYyLjA2ODc3Ij5ab288L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjI0MC41ODk5NyIKICAgICAgIHk9IjU3NC40NDU0MyIKICAgICAgIGlkPSJ0ZXh0NDU2MyIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDU2NSIKICAgICAgICAgeD0iMjQwLjU4OTk3IgogICAgICAgICB5PSI1NzQuNDQ1NDMiPjEzdGg8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDU2NyIKICAgICAgIHk9IjUxMS42MzY2MyIKICAgICAgIHg9IjIwNi4wMzE3NSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iNTExLjYzNjYzIgogICAgICAgICB4PSIyMDYuMDMxNzUiCiAgICAgICAgIGlkPSJ0c3BhbjQ1NjkiCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiPjIxc3Q8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjYyMC40NDMxMiIKICAgICAgIHk9Ii01MDYuNjgyMTkiCiAgICAgICBpZD0idGV4dDQ1NzEiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40NTczIgogICAgICAgICB4PSI2MjAuNDQzMTIiCiAgICAgICAgIHk9Ii01MDYuNjgyMTkiPk5pbXM8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDU4MyIKICAgICAgIHk9IjY5OC44NDAwOSIKICAgICAgIHg9IjM3MC4yMTY4NiIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iNjk4Ljg0MDA5IgogICAgICAgICB4PSIzNzAuMjE2ODYiCiAgICAgICAgIGlkPSJ0c3BhbjQ1ODUiCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiPk1hcGxlPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSIzODQuMDg0MiIKICAgICAgIHk9IjY4MC44NTEzOCIKICAgICAgIGlkPSJ0ZXh0NDU5OSIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDYwMSIKICAgICAgICAgeD0iMzg0LjA4NDIiCiAgICAgICAgIHk9IjY4MC44NTEzOCI+RG91Z2xhczwvdHNwYW4+PC90ZXh0PgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSAzNjcuOTA4MTcsMTAwOS45NTk2IDI2My4wMTgzMywwIgogICAgICAgaWQ9InBhdGg0NjA1IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDx0ZXh0CiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ2MDciCiAgICAgICB5PSItNDMzLjEzNzc2IgogICAgICAgeD0iNzM2LjI2NzQ2IgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbgogICAgICAgICB5PSItNDMzLjEzNzc2IgogICAgICAgICB4PSI3MzYuMjY3NDYiCiAgICAgICAgIGlkPSJ0c3BhbjQ2MDkiCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiPk1lcmlkaWFuPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ5NzkiCiAgICAgICB5PSI2NDAuMjA1MjYiCiAgICAgICB4PSI1NzIuODMyMTUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9IjY0MC4yMDUyNiIKICAgICAgICAgeD0iNTcyLjgzMjE1IgogICAgICAgICBpZD0idHNwYW40OTgxIgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIj5DZW50cmFsPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1NzUuMDg5NjYiCiAgICAgICB5PSI2NzAuOTAzNSIKICAgICAgIGlkPSJ0ZXh0NDk4MyIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDk4NSIKICAgICAgICAgeD0iNTc1LjA4OTY2IgogICAgICAgICB5PSI2NzAuOTAzNSI+RG91Z2xhczwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iNDk5LjQ4OTYyIgogICAgICAgeT0iMTAwOC42MDY5IgogICAgICAgaWQ9InRleHQ1MDQ3IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW41MDQ5IgogICAgICAgICB4PSI0OTkuNDg5NjIiCiAgICAgICAgIHk9IjEwMDguNjA2OSI+NDd0aDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iMjE2LjY0NTQzIgogICAgICAgeT0iNzI1Ljk4Mjk3IgogICAgICAgaWQ9InRleHQ1MDUxIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW41MDUzIgogICAgICAgICB4PSIyMTYuNjQ1NDMiCiAgICAgICAgIHk9IjcyNS45ODI5NyI+S2VsbG9nZzwvdHNwYW4+PC90ZXh0PgogICAgPGZsb3dSb290CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgaWQ9ImZsb3dSb290NTA1NSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6MThweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCwyODcuMzYyMTgpIj48Zmxvd1JlZ2lvbgogICAgICAgICBpZD0iZmxvd1JlZ2lvbjUwNTciPjxyZWN0CiAgICAgICAgICAgaWQ9InJlY3Q1MDU5IgogICAgICAgICAgIHdpZHRoPSIzNDMuNTcxNDQiCiAgICAgICAgICAgaGVpZ2h0PSIxMDMuNTcxNDMiCiAgICAgICAgICAgeD0iMTkuMjg1NzE1IgogICAgICAgICAgIHk9IjE3LjE0Mjg1NyIKICAgICAgICAgICBzdHlsZT0iZm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIgLz48L2Zsb3dSZWdpb24+PGZsb3dQYXJhCiAgICAgICAgIGlkPSJmbG93UGFyYTUwNjEiPjwvZmxvd1BhcmE+PC9mbG93Um9vdD4gICAgPHRleHQKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDYwNy03IgogICAgICAgeT0iLTUwOC4xODk3MyIKICAgICAgIHg9Ijc3NC44NzU2MSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iLTUwOC4xODk3MyIKICAgICAgICAgeD0iNzc0Ljg3NTYxIgogICAgICAgICBpZD0idHNwYW40NjA5LTciCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiPk1jQ2xlYW48L3RzcGFuPjwvdGV4dD4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gMzY0LjE1OTk5LDY1OC40Mjg5MSAyOTkuNTEwMjMsLTEuMDEwMTYgYyA2LjQ5ODcyLC0wLjAyMTkgNi45NzcxOSw5LjI1NDEyIDE2LjU5NjMxLDkuMzkyNDcgMTIuMDU0MjcsMC4xNzMzOSAyOS4xMTA4MywtMC41MzU3MiA1NC4xMTQzNywtMC4zMDExIgogICAgICAgaWQ9InBhdGg1NDQwIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsMjg3LjM2MjE4KSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NzYyIgLz4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjM3My45OTMwNCIKICAgICAgIHk9Ijk0NC4zNTc1NCIKICAgICAgIGlkPSJ0ZXh0NTA0Ny05IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW41MDQ5LTMiCiAgICAgICAgIHg9IjM3My45OTMwNCIKICAgICAgICAgeT0iOTQ0LjM1NzU0Ij5NYWNBcnRodXI8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ2MDctNy0xIgogICAgICAgeT0iLTQ5MC4yNDU5NyIKICAgICAgIHg9Ijc4MC44NDYwNyIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iLTQ5MC4yNDU5NyIKICAgICAgICAgeD0iNzgwLjg0NjA3IgogICAgICAgICBpZD0idHNwYW40NjA5LTctOSIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+U2VuZWNhPC90c3Bhbj48L3RleHQ+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDM2Ny42OTU1Myw1MzcuMjEwNiAxNDEuMjgzMDMsLTEuMDEwMTUgYyA2LjQ4OTk5LC0wLjA0NjQgMTIuNzgxMTQsNy4yMzU0NSAxOS4xOTI5LDcuMzIzNiA1NS45MjM2MiwwLjc2ODkgMTU4LjY4OTk3LC0wLjE3MzMzIDIzNi41MTQwMiwtMS4wMTAxNSA3LjgzOTU2LC0wLjA4NDMgMjIuNjMxNDcsLTE5Ljg1MzU1IDMwLjMwNDU3LC0yMC40NTU1OSAyMi4yNjU4OSwtMS4zNTE4MSA0NS4xNzk0NSwtMC41MDUwNyA2Ny42ODAyMiwtMC41MDUwNyAxNi4xNDczMSwtMC42MzI0MSAzLjYxMDE2LDIwLjcwODEzIDI2Ljc2OTA0LDIwLjcwODEzIGwgMjQzLjQ0Njc5LC0xLjAxMDE2IgogICAgICAgaWQ9InBhdGg1NDk2IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsMjg3LjM2MjE4KSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NzY2NjY2MiIC8+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI2ODUuMjA4MTMiCiAgICAgICB5PSI4MjcuNTMwODIiCiAgICAgICBpZD0idGV4dDQzMTAtNy04IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40MzEyLTYtNiIKICAgICAgICAgeD0iNjg1LjIwODEzIgogICAgICAgICB5PSI4MjcuNTMwODIiPlBhd25lZTwvdHNwYW4+PC90ZXh0PgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSA1NTQuMjg1NzIsNzIxLjQyODU3IDU1MCw1NDMuMjE0MjkgNTQ3LjE0Mjg2LDEwMi41IDU0Ni43ODU3MiwyMy4yMTQyODUiCiAgICAgICBpZD0icGF0aDU1MTkiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCwyODcuMzYyMTgpIiAvPgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iNTI5LjYyNTMxIgogICAgICAgeT0iLTU1MC44NDc3OCIKICAgICAgIGlkPSJ0ZXh0NDU0My01IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDU0NS0wIgogICAgICAgICB4PSI1MjkuNjI1MzEiCiAgICAgICAgIHk9Ii01NTAuODQ3NzgiPkJyb2Fkd2F5PC90c3Bhbj48L3RleHQ+CiAgPC9nPgo8L3N2Zz4K\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"showTooltip\":true,\"autocloseTooltip\":true,\"showTooltipAction\":\"click\",\"defaultCenterPosition\":\"0,0\",\"provider\":\"image-map\",\"fitMapBounds\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"polygonKeyName\":\"coordinates\",\"polygonOpacity\":0.5,\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"mapProvider\":\"HERE.normalDay\",\"draggableMarker\":true,\"editablePolygon\":true,\"mapPageSize\":16384,\"showPolygon\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${coordinates|ts:7}

Delete\"},\"title\":\"Markers Placement - Image Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"tooltipAction\":[{\"name\":\"delete\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id;\\n });\\n\\nwidgetContext.map.setMarkerLocation(entityDatasource[0], null, null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"c39f512a-21c6-6b06-3aa1-715262c6553d\"},{\"name\":\"delete_polygon\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id\\n });\\n\\nwidgetContext.map.savePolygonLocation(entityDatasource[0], null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"94bf5ffd-b526-c6c3-ae3b-ab42191217d9\"}]},\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"displayTimewindow\":true}" - } - }, - { - "alias": "update_integer_timeseries", - "name": "Update integer timeseries", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAANBSURBVHja7d3dS1NhHAfw/Sum1IUQBZYJIdSF4VVQIOSNXZiJg5nmWi18CXG9WJgSqd1IBUWu10PY1EBcIFYqyoTCLOZWA8/0uGmbbjs73y7mS+Z2YdjYs76/i3Oe88C5+PB73jhwnkeHiOxyCh4uOQxdxO1XIXiofndEJ/uRBuGXdS41HSCqS+dEWoSTEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYSQfwl59xkA+teqDfLWl9wOESC9twAo5QsJIdHI6EstkvoQX4UP6LsNzNo/hgGDrI2oCI0B0XG7DADuSzcttaMC9JG2t0DTMEZqezuvAwY5UhbEvB5a213J9A0Awg0mnwid/cM1zBpCmPQgWrGwARlv1GDvBABbd88TESDhc3Ov7wM/H99oKp/dgEgXWlqaGgFAWYl6hBh+H9rqp4COV1EYYpAA5vWwdXi9XkWoeeRLjVkDGofxqdwDg4zqrxjTY+b8HDwOoSDaZQnAeNXFVuMEDDIGKs1temCwpsE8JeLMHg2u95qVGHBR4xKFEEIIIeT/gyxvKYgB0d5sXhAtN0uxgtS8LBJEsxrtm2sko/TbTRSI9tzY92ddj1FavYgD0axbHYBklOLnI2Uh8fIRy0kCR8pBgvdcALRn8R3bhMjdrdYlAFh62mpd/Uk81J8ciGKp/56gXW27aQ3uPVqac9AJTOUePpOfMw1gom5/VpJSsnC1zpU4H9vp7KEDlSqUQ3qg6GQI4cKzwIvMU6eTBYFiMSXOx3aG38mM9wCMBUDDAIAr+YDzB9qTBoFiie/4uwmxuGJ1+CgqAYBkQhDcwSVKX+YIANi6SvKmkw7ZwUXjzL5GAEDlsT2l8wJD5o+UrO1o4S0sFhcSOH4isP7wYFdAVEikpEABgGBWF4D27KigEK06u9/hcDgUlOUNLQ7lmkRtWksZsXiEhaqsjN3mgMij1lqEPcJsY8OPD4QQQgghhOws5E5SgxkhhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCElNSNocEJweRzb7ZF04PQ7RVnXpcay5il+KVmW7YpZ2rQAAAABJRU5ErkJggg==", - "description": "Simple form to input new integer value for pre-defined timeseries key.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n\n
\n \n \n
\n
\n\n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-timeseries-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.attribute-not-allowed' | translate }}\n
\n
\n
\n
", - "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}\n", - "controllerScript": "let $scope;\nlet settings;\nlet utils;\nlet translate;\nlet http;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n http = $scope.$injector.get(self.ctx.servicesMap.get('http'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-timeseries-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n\n $scope.attributeUpdateFormGroup = $scope.fb.group(\n {currentValue: [undefined, [$scope.validators.required,\n $scope.validators.min(settings.minValue),\n $scope.validators.max(settings.maxValue),\n $scope.validators.pattern(/^-?[0-9]+$/)]]}\n );\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"timeseries\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n let observable = saveEntityTimeseries(\n datasource.entityType,\n datasource.entityId,\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n );\n if (observable) {\n observable.subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n\n function saveEntityTimeseries(entityType, entityId, telemetries) {\n var telemetriesData = {};\n for (var a = 0; a < telemetries.length; a++) {\n if (typeof telemetries[a].value !== 'undefined' && telemetries[a].value !== null) {\n telemetriesData[telemetries[a].key] = telemetries[a].value;\n }\n }\n if (Object.keys(telemetriesData).length) {\n var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/scope';\n return http.post(url, telemetriesData);\n }\n return null;\n }\n}\n\nself.onDataUpdated = function() {\n\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(correctValue($scope.originalValue));\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nfunction correctValue(value) {\n if (typeof value !== \"number\") {\n return 0;\n }\n return value;\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n dataKeyOptional: true\n }\n}\n\nself.onDestroy = function() {\n\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-update-integer-attribute-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update integer timeseries\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "update_double_timeseries", - "name": "Update double timeseries", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAANMSURBVHja7d3fS5NRGAfw/SsrhSCKKIugburCi6KCLrropi4izME0c1kLUxquHybmKNPAi4qMJkm9hOkswhVkpaLMfhmlbjHYu+3Vaf7a3r3fLjad5nZh6NiZ3+diO+fAe/HhOc85Z+/Fjg4R2T0ieLjlMHQRT0iF4KGGPBGdHEIWREjWudVsgKhu3QiyIkYIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQghZS8jbbwDQOT9slJc/5HGJAOmoAaAUjKWERCO9z7RI5kPGC8cBxy3A5/wUBoyy1qNirg+I9jtlAPBcvGkt7xWgRmyvgKpu9JR3NF4HjHLk9DSCBmi2eqnsFwCEK8vGRSj2j9fgM85h0Ito4VgC0m/R4GwEgHZ72xMRIOHiwIv7wJ/HN6oKfAmIdL62tsoCAMps1CvE8vuwvWIIaHgehTEGmULQgPYGv9+vCLWP/Cg1a4ClG18LvDDKKPmJPgNGzwXgdQkF0S5JAPrPXqgzDcAo402R2WYAukorzUMi7uzR6YWqmY0BJzQeUQghhBBC1h9kZllDDIj2cumBaKZaijWk6hmRIFqLybl0RDJJi75EgWitJse/Y20mKf4hDkRrWe4AJJOUPB8ZC0mWj1hOUjgyDjJ9zw1Ae5rcsUKIbK9rmVzovatv+hJvfh5ec4hirfidYl6teGp1bd1/Km9XfDhasOnksZxGAIGmA/rWtU/J2NXL7tT5WEmxz+0sUqHsNsR69lwXcDc3AC13b0U6IFCsZanzsZLld1D/AYApP/5wDYBBfTfwPupPCwSKNbnj/zbE44WLOvYcLwCkCYLpVTyiODb2zDe/P7qypQnphKzioXF0myUxMw9t3+MUFBLcd2LxP1qo1bnDQkKmjhydWjqw4YGIkMiJ/MT5ufggAL9eEhCilWzudLlcLgWvbYBDfyfoPpOnCAiZ1MeiGeYdKtCcp9cfHhB11YoXeewFo2+Cv9kJIYQQQghZf5DbaQ1mhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCEkMyFZc0FwdlzZPC7rwtlxibaqy45rzVX8BUWnY6Q1/jMIAAAAAElFTkSuQmCC", - "description": "Simple form to input new double value for pre-defined timeseries key.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-timeseries-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.attribute-not-allowed' | translate }}\n
\n
\n
\n
", - "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", - "controllerScript": "let $scope;\nlet settings;\nlet utils;\nlet translate;\nlet http;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n http = $scope.$injector.get(self.ctx.servicesMap.get('http'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-timeseries-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n\n $scope.attributeUpdateFormGroup = $scope.fb.group(\n {currentValue: [undefined, [$scope.validators.required,\n $scope.validators.min(settings.minValue),\n $scope.validators.max(settings.maxValue)]]}\n );\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"timeseries\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n let observable = saveEntityTimeseries(\n datasource.entityType,\n datasource.entityId,\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n );\n if (observable) {\n observable.subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n\n function saveEntityTimeseries(entityType, entityId, telemetries) {\n var telemetriesData = {};\n for (var a = 0; a < telemetries.length; a++) {\n if (typeof telemetries[a].value !== 'undefined' && telemetries[a].value !== null) {\n telemetriesData[telemetries[a].key] = telemetries[a].value;\n }\n }\n if (Object.keys(telemetriesData).length) {\n var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/scope';\n return http.post(url, telemetriesData);\n }\n return null;\n }\n}\n\nself.onDataUpdated = function() {\n\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(correctValue($scope.originalValue));\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nfunction correctValue(value) {\n if (typeof value !== \"number\") {\n return 0;\n }\n return value;\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n dataKeyOptional: true\n }\n}\n\nself.onDestroy = function() {\n\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-update-double-attribute-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update double timeseries\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "update_boolean_timeseries", - "name": "Update boolean timeseries", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAIQSURBVHja7d3PSxRhHMfx+VcWgmg3ltKD7BJshyBIQusQ3vIYCILCRMyhH6xShlGWMGq1p1gIibxFULRR1F4ixcNqdNhmUXFipWnTYvfZ/TrjJqFQ13ge3p/L8MztBQ/P8/0eZr6WNHyvrHk8vy5WoxIo0TwqqDQsPxADEviWp0yAKM8qixEpAwECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEC5H9DSiP2noyUNIXsc9j2qKYQ2/732lzI7LyukN4jUV7uLrtndIWcuP4lzKYBELf9rD66/bzZhqw9vFcM37ReTzz5pR3kc8fAWPriDmT58NDl5F0R51j2zOm6PpB+13VfyNNbIm8O/Iwg430iz87Lu/iaNDKz+kB6Hcd5HH60PDedjfkR5FXi2oeWyI3j+Xy+56puW2vh6KXcnTZEFq90pQripAbC5HSDDDoin9qQjxVRkwk1dUrLU2v4bLAxGFuNIEPnanK/s7WSeNDcct7rBvFOxg7djBUjSLX/YDJdEHmbiccv1PQrUb7++T/B9/Xfd8sPikbK+L81VqP7HEu0ukCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAADEUYsyAYDNGNn/zrboZQ7SVZcZYcyXbTFGWg8aBYQQAAAAASUVORK5CYII=", - "description": "Simple form to input new boolean value for pre-defined timeseries key.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "
\n
\n
\n
\n
\n \n {{currentValue}}\n \n
\n
\n\n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-timeseries-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.attribute-not-allowed' | translate }}\n
\n
\n
\n
", - "templateCss": ".attribute-update-form {\r\n overflow: hidden;\r\n height: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n.attribute-update-form__grid {\r\n display: flex;\r\n}\r\n.grid__element:first-child {\r\n flex: 1;\r\n}\r\n\r\n.grid__element {\r\n display: flex;\r\n}\r\n\r\n.attribute-update-form .mat-button.mat-icon-button {\r\n width: 32px;\r\n min-width: 32px;\r\n height: 32px;\r\n min-height: 32px;\r\n padding: 0 !important;\r\n margin: 0 !important;\r\n line-height: 20px;\r\n}\r\n\r\n.attribute-update-form .mat-icon-button mat-icon {\r\n width: 20px;\r\n min-width: 20px;\r\n height: 20px;\r\n min-height: 20px;\r\n font-size: 20px;\r\n}\r\n\r\n.tb-toast {\r\n font-size: 14px!important;\r\n}", - "controllerScript": "let settings;\nlet utils;\nlet translate;\nlet http;\nlet $scope;\nlet map;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init();\n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n http = $scope.$injector.get(self.ctx.servicesMap.get('http'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n\n settings.trueValue = utils.defaultValue(utils.customTranslation(settings.trueValue, settings.trueValue), true);\n settings.falseValue = utils.defaultValue(utils.customTranslation(settings.falseValue, settings.falseValue), false);\n\n map = {\n true: settings.trueValue,\n false: settings.falseValue\n };\n \n $scope.checkboxValue = false;\n $scope.currentValue = map[$scope.checkboxValue];\n\n $scope.attributeUpdateFormGroup = $scope.fb.group({checkboxValue: [$scope.checkboxValue]});\n\n $scope.changed = function() {\n $scope.checkboxValue = $scope.attributeUpdateFormGroup.get('checkboxValue').value;\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.updateAttribute();\n };\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"timeseries\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function() {\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n let observable = saveEntityTimeseries(\n datasource.entityType,\n datasource.entityId,\n [{\n key: $scope.currentKey,\n value: $scope.checkboxValue\n }]\n );\n\n if (observable) {\n observable.subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('checkboxValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n }\n };\n\n function saveEntityTimeseries(entityType, entityId, telemetries) {\n var telemetriesData = {};\n for (var a = 0; a < telemetries.length; a++) {\n if (typeof telemetries[a].value !== 'undefined' && telemetries[a].value !== null) {\n telemetriesData[telemetries[a].key] = telemetries[a].value;\n }\n }\n if (Object.keys(telemetriesData).length) {\n var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/scope';\n return http.post(url, telemetriesData);\n }\n return null;\n }\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n $scope.checkboxValue = self.ctx.data[0].data[0][1] === 'true';\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.attributeUpdateFormGroup.get('checkboxValue').patchValue($scope.checkboxValue);\n self.ctx.detectChanges();\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {}", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-update-boolean-attribute-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update boolean timeseries\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "update_string_timeseries", - "name": "Update string timeseries", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAOkSURBVHja7d3bSxRRHAfw/VdG6yklCbPsRk+GhVLYDSXMohC1tczVUkxFXDULzW4a+SA9VIZRm5haIJqBmMqW2cUoddUVR1sved2dnV/nuJaX3X0Q1pjZvj/YM4fDmYfP/uZcdvbhaMgmmnpVHibRShpb/4REKg9pot+mESfIC2JC1Jgkb4BIJk0veUX0AgIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAsp6QN194+epPs1Z0vqm/Uw2QuuussMSOuYXYbe3PZJvyIeNx40T1xUTDTe+sHCK3STTfwQDGpgVU/+Vr+ox2FYyRktdEuS3UllFXVsAhtrMz9DOe5JI7htQfvIM1K3VcDYO9NZ+GtfP00Uz2uLEliDFHpqYy3qG2suaxGiDW86PVFURTD6/mxg4vQQwpRUW5ObyDZc5uVsX0+6A2s5uo9LmdtA7INIfUlo6MjFhUtY58S06TiXJa6HOsmc9aSd+pI576Lo6SuVNVEDndwErjhUs3dO85pCExrSSeqDE5K61bjSu7febvqJlzACdlbFEAAQQQQP4/yKxTRR0Q+eXKDdFsocFRMRTOqgkiP9E1rWwx6AzLLmqByE919avbajihxqVDsRCWj3rnVpYM1/lQLMRVPhw5ceNQHGTmnok7qlw7PAuJOrWOEIs+c8DNc+XxR2tdITSWd8XkPh8eHezrC2E5SXWfjzVNvxUdRn0Bf29V3sZvq2GVD82FuUYy5ukbFiBdN7MbeU+pOvvuV96xoyrT4kGJa8eaF8Sg/cHaEN9WIr/ixe9/8+7QxD0bsgITDgqPWMu2refChXIi28mApIiNzOYX7B826MEB76EtStC+SbLuSFkOiZZoaos/ewcUdYh9AodJ1gX8ovubekhO2Ms6hk8pcdMYlM+KmBPLIUWscoy1UF7Q4hjpEpopTMsq9cKYo6NCIacjV0MiY1hRELgImRQqKUBYiC5VQ4aEatqZ2MljWvGQYP6C9LAz5CirvBA+UUyYndXmSPGQ2GDjwG0fZ4hQPPh21xH2l5BP+oCoD7UrHtITIvgej3CGnIkWhAN9fELfLggh7YrNyLIQp92sWUOOq31QxG92QAABBBBAvBNy658GMgIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAokyI1xwQ7B1HNo+LGqt3HKItabzjWHOJfgM5tA7UOw6xeQAAAABJRU5ErkJggg==", - "description": "Simple form to input new string value for pre-defined timeseries key.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-timeseries-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.attribute-not-allowed' | translate }}\n
\n
\n
\n
", - "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", - "controllerScript": "let $scope;\nlet settings;\nlet utils;\nlet translate;\nlet http;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n http = $scope.$injector.get(self.ctx.servicesMap.get('http'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-timeseries-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n\n $scope.attributeUpdateFormGroup = $scope.fb.group(\n {currentValue: [undefined, [$scope.validators.required,\n $scope.validators.minLength(settings.minLength),\n $scope.validators.maxLength(settings.maxLength)]]}\n );\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"timeseries\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n let observable = saveEntityTimeseries(\n datasource.entityType,\n datasource.entityId,\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n );\n if (observable) {\n observable.subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n\n function saveEntityTimeseries(entityType, entityId, telemetries) {\n var telemetriesData = {};\n for (var a = 0; a < telemetries.length; a++) {\n if (typeof telemetries[a].value !== 'undefined' && telemetries[a].value !== null) {\n telemetriesData[telemetries[a].key] = telemetries[a].value;\n }\n }\n if (Object.keys(telemetriesData).length) {\n var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/scope';\n return http.post(url, telemetriesData);\n }\n return null;\n }\n}\n\nself.onDataUpdated = function() {\n\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-update-string-attribute-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update string timeseries\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "update_server_image_attribute", - "name": "Update server image attribute", - "image": "data:image/png;base64,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", - "description": "Simple form to input new image for pre-defined server-side attribute key.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3.5, - "resources": [], - "templateHtml": "
\n
\n
\n
\n
\n \n \n
\n \n
\n \n \n
\n
\n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", - "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n align-items: center;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.tb-image-preview-container div,\n.tb-flow-drop label {\n font-size: 16px !important;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", - "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet utils;\r\nlet translate;\r\n\r\nself.onInit = function() {\r\n self.ctx.ngZone.run(function() {\r\n init(); \r\n self.ctx.detectChanges(true);\r\n });\r\n};\r\n\r\nfunction init() {\r\n\r\n $scope = self.ctx.$scope;\r\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n $scope.toastTargetId = 'input-widget' + utils.guid();\r\n settings = utils.deepClone(self.ctx.settings) || {};\r\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\r\n settings.displayPreview = utils.defaultValue(settings.displayPreview, true);\r\n settings.displayClearButton = utils.defaultValue(settings.displayClearButton, false);\r\n settings.displayApplyButton = utils.defaultValue(settings.displayApplyButton, true);\r\n settings.displayDiscardButton = utils.defaultValue(settings.displayDiscardButton, true);\r\n $scope.settings = settings;\r\n $scope.isValidParameter = true;\r\n $scope.dataKeyDetected = false;\r\n $scope.entityDetected = false;\r\n \r\n $scope.attributeUpdateFormGroup = $scope.fb.group(\r\n {currentValue: [undefined, []]}\r\n );\r\n \r\n $scope.attributeUpdateFormGroup.valueChanges.subscribe( () => {\r\n self.ctx.detectChanges();\r\n if (!settings.displayApplyButton) {\r\n $scope.updateAttribute();\r\n }\r\n });\r\n\r\n if (self.ctx.datasources && self.ctx.datasources.length) {\r\n var datasource = self.ctx.datasources[0];\r\n \r\n if (datasource.type === 'entity') {\r\n if (datasource.entityType && datasource.entityId) {\r\n $scope.entityName = datasource.entityName;\r\n if (settings.widgetTitle && settings.widgetTitle.length) {\r\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n } else {\r\n $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n }\r\n\r\n $scope.entityDetected = true;\r\n }\r\n }\r\n if (datasource.dataKeys.length) {\r\n if (datasource.dataKeys[0].type !== \"attribute\") {\r\n $scope.isValidParameter = false;\r\n } else {\r\n $scope.currentKey = datasource.dataKeys[0].name;\r\n $scope.dataKeyType = datasource.dataKeys[0].type;\r\n $scope.dataKeyDetected = true;\r\n }\r\n }\r\n }\r\n\r\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n \r\n $scope.updateAttribute = function () {\r\n if ($scope.entityDetected) {\r\n var datasource = self.ctx.datasources[0];\r\n\r\n attributeService.saveEntityAttributes(\r\n datasource.entity.id,\r\n 'SERVER_SCOPE',\r\n [\r\n {\r\n key: $scope.currentKey,\r\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\r\n }\r\n ]\r\n ).subscribe(\r\n function success() {\r\n if (settings.displayApplyButton) {\r\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\r\n }\r\n if (settings.showResultMessage) {\r\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n },\r\n function fail() {\r\n if (settings.showResultMessage) {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n }\r\n );\r\n }\r\n };\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n try {\r\n if ($scope.dataKeyDetected) {\r\n var value = self.ctx.data[0].data[0][1];\r\n if (settings.displayApplyButton || !$scope.originalValue) {\r\n $scope.originalValue = value;\r\n }\r\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(value, {emitEvent: false});\r\n self.ctx.detectChanges();\r\n }\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n};\r\n\r\nself.onResize = function() {\r\n\r\n};\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1,\r\n maxDataKeys: 1,\r\n singleEntity: true\r\n };\r\n};\r\n\r\nself.onDestroy = function() {\r\n $scope.attributeUpdateFormGroup.valueChanges.unsubscribe();\r\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-update-image-attribute-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showResultMessage\":true,\"displayPreview\":true,\"displayClearButton\":false,\"displayApplyButton\":true,\"displayDiscardButton\":true},\"title\":\"Update server image attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "update_server_date_attribute", - "name": "Update server date attribute", - "image": "data:image/png;base64,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", - "description": "Simple form to input new date value for pre-defined server-side attribute key.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3.5, - "resources": [], - "templateHtml": "
\n
\n
\n
\n
\n \n {{ labelValue }}\n \n \n \n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", - "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", - "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet utils;\r\nlet translate;\r\n\r\nself.onInit = function() {\r\n self.ctx.ngZone.run(function() {\r\n init(); \r\n self.ctx.detectChanges(true);\r\n });\r\n};\r\n\r\nfunction init() {\r\n\r\n $scope = self.ctx.$scope;\r\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n $scope.toastTargetId = 'input-widget' + utils.guid();\r\n settings = utils.deepClone(self.ctx.settings) || {};\r\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\r\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\r\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\r\n $scope.settings = settings;\r\n $scope.isValidParameter = true;\r\n $scope.dataKeyDetected = false;\r\n $scope.entityDetected = false;\r\n\r\n $scope.datePickerType = settings.showTimeInput ? 'datetime' : 'date'; \r\n \r\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\r\n $scope.labelValue = translate.instant('widgets.input-widgets.date');\r\n \r\n if (settings.showTimeInput) {\r\n $scope.labelValue += \" & \" + translate.instant('widgets.input-widgets.time');\r\n }\r\n \r\n var validators = [];\r\n \r\n if (settings.isRequired) {\r\n validators.push($scope.validators.required);\r\n }\r\n \r\n $scope.attributeUpdateFormGroup = $scope.fb.group(\r\n {currentValue: [undefined, validators]}\r\n );\r\n\r\n if (self.ctx.datasources && self.ctx.datasources.length) {\r\n var datasource = self.ctx.datasources[0];\r\n \r\n if (datasource.type === 'entity') {\r\n if (datasource.entityType && datasource.entityId) {\r\n $scope.entityName = datasource.entityName;\r\n if (settings.widgetTitle && settings.widgetTitle.length) {\r\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n } else {\r\n $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n }\r\n\r\n $scope.entityDetected = true;\r\n }\r\n }\r\n if (datasource.dataKeys.length) {\r\n if (datasource.dataKeys[0].type !== \"attribute\") {\r\n $scope.isValidParameter = false;\r\n } else {\r\n $scope.currentKey = datasource.dataKeys[0].name;\r\n $scope.dataKeyType = datasource.dataKeys[0].type;\r\n $scope.dataKeyDetected = true;\r\n }\r\n }\r\n }\r\n\r\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n \r\n $scope.clear = function(event) {\r\n event.stopPropagation();\r\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(undefined);\r\n }\r\n \r\n $scope.updateAttribute = function () {\r\n if ($scope.entityDetected) {\r\n var datasource = self.ctx.datasources[0];\r\n var currentValueInMilliseconds;\r\n \r\n if (!$scope.attributeUpdateFormGroup.get('currentValue').value) {\r\n currentValueInMilliseconds = undefined;\r\n } else {\r\n currentValueInMilliseconds = $scope.attributeUpdateFormGroup.get('currentValue').value.getTime();\r\n }\r\n \r\n attributeService.saveEntityAttributes(\r\n datasource.entity.id,\r\n 'SERVER_SCOPE',\r\n [\r\n {\r\n key: $scope.currentKey,\r\n value: currentValueInMilliseconds\r\n }\r\n ]\r\n ).subscribe(\r\n function success() {\r\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\r\n if (settings.showResultMessage) {\r\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n },\r\n function fail() {\r\n if (settings.showResultMessage) {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n }\r\n );\r\n }\r\n };\r\n \r\n $scope.isValidDate = function(date) {\r\n return date instanceof Date && !isNaN(date);\r\n }\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n try {\r\n if ($scope.dataKeyDetected) {\r\n if (!$scope.isFocused) {\r\n $scope.originalValue = moment(self.ctx.data[0].data[0][1]).toDate();\r\n\r\n if (!$scope.isValidDate($scope.originalValue)) {\r\n $scope.originalValue = undefined;\r\n }\r\n \r\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\r\n self.ctx.detectChanges();\r\n }\r\n }\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n};\r\n\r\nself.onResize = function() {\r\n};\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1,\r\n maxDataKeys: 1,\r\n singleEntity: true\r\n };\r\n};\r\n\r\nself.onDestroy = function() {\r\n}\r\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-update-date-attribute-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update server date attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "update_server_boolean_attribute", - "name": "Update server boolean attribute", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAIQSURBVHja7d3PSxRhHMfx+VcWgmg3ltKD7BJshyBIQusQ3vIYCILCRMyhH6xShlGWMGq1p1gIibxFULRR1F4ixcNqdNhmUXFipWnTYvfZ/TrjJqFQ13ge3p/L8MztBQ/P8/0eZr6WNHyvrHk8vy5WoxIo0TwqqDQsPxADEviWp0yAKM8qixEpAwECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEC5H9DSiP2noyUNIXsc9j2qKYQ2/732lzI7LyukN4jUV7uLrtndIWcuP4lzKYBELf9rD66/bzZhqw9vFcM37ReTzz5pR3kc8fAWPriDmT58NDl5F0R51j2zOm6PpB+13VfyNNbIm8O/Iwg430iz87Lu/iaNDKz+kB6Hcd5HH60PDedjfkR5FXi2oeWyI3j+Xy+56puW2vh6KXcnTZEFq90pQripAbC5HSDDDoin9qQjxVRkwk1dUrLU2v4bLAxGFuNIEPnanK/s7WSeNDcct7rBvFOxg7djBUjSLX/YDJdEHmbiccv1PQrUb7++T/B9/Xfd8sPikbK+L81VqP7HEu0ukCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAADEUYsyAYDNGNn/zrboZQ7SVZcZYcyXbTFGWg8aBYQQAAAAASUVORK5CYII=", - "description": "Simple form to input new boolean value for pre-defined server-side attribute key.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "
\r\n
\r\n
\r\n
\r\n
\r\n \r\n {{currentValue}}\r\n \r\n
\r\n
\r\n\r\n
\r\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\r\n
\r\n
\r\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\r\n
\r\n
\r\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\r\n
\r\n
\r\n
\r\n
", - "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", - "controllerScript": "let settings;\nlet attributeService;\nlet utils;\nlet translate;\nlet $scope;\nlet map;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init();\n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n\n settings.trueValue = utils.defaultValue(utils.customTranslation(settings.trueValue, settings.trueValue), true);\n settings.falseValue = utils.defaultValue(utils.customTranslation(settings.falseValue, settings.falseValue), false);\n\n map = {\n true: settings.trueValue,\n false: settings.falseValue\n };\n \n $scope.checkboxValue = false;\n $scope.currentValue = map[$scope.checkboxValue];\n\n $scope.attributeUpdateFormGroup = $scope.fb.group({checkboxValue: [$scope.checkboxValue]});\n\n $scope.changed = function() {\n $scope.checkboxValue = $scope.attributeUpdateFormGroup.get('checkboxValue').value;\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.updateAttribute();\n };\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function() {\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SERVER_SCOPE',\n [\n {\n key: $scope.currentKey,\n value: $scope.checkboxValue\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('checkboxValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n $scope.checkboxValue = self.ctx.data[0].data[0][1] === 'true';\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.attributeUpdateFormGroup.get('checkboxValue').patchValue($scope.checkboxValue);\n self.ctx.detectChanges();\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {}", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-update-boolean-attribute-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update server boolean attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "update_server_double_attribute", - "name": "Update server double attribute", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAANMSURBVHja7d3fS5NRGAfw/SsrhSCKKIugburCi6KCLrropi4izME0c1kLUxquHybmKNPAi4qMJkm9hOkswhVkpaLMfhmlbjHYu+3Vaf7a3r3fLjad5nZh6NiZ3+diO+fAe/HhOc85Z+/Fjg4R2T0ieLjlMHQRT0iF4KGGPBGdHEIWREjWudVsgKhu3QiyIkYIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQghZS8jbbwDQOT9slJc/5HGJAOmoAaAUjKWERCO9z7RI5kPGC8cBxy3A5/wUBoyy1qNirg+I9jtlAPBcvGkt7xWgRmyvgKpu9JR3NF4HjHLk9DSCBmi2eqnsFwCEK8vGRSj2j9fgM85h0Ito4VgC0m/R4GwEgHZ72xMRIOHiwIv7wJ/HN6oKfAmIdL62tsoCAMps1CvE8vuwvWIIaHgehTEGmULQgPYGv9+vCLWP/Cg1a4ClG18LvDDKKPmJPgNGzwXgdQkF0S5JAPrPXqgzDcAo402R2WYAukorzUMi7uzR6YWqmY0BJzQeUQghhBBC1h9kZllDDIj2cumBaKZaijWk6hmRIFqLybl0RDJJi75EgWitJse/Y20mKf4hDkRrWe4AJJOUPB8ZC0mWj1hOUjgyDjJ9zw1Ae5rcsUKIbK9rmVzovatv+hJvfh5ec4hirfidYl6teGp1bd1/Km9XfDhasOnksZxGAIGmA/rWtU/J2NXL7tT5WEmxz+0sUqHsNsR69lwXcDc3AC13b0U6IFCsZanzsZLld1D/AYApP/5wDYBBfTfwPupPCwSKNbnj/zbE44WLOvYcLwCkCYLpVTyiODb2zDe/P7qypQnphKzioXF0myUxMw9t3+MUFBLcd2LxP1qo1bnDQkKmjhydWjqw4YGIkMiJ/MT5ufggAL9eEhCilWzudLlcLgWvbYBDfyfoPpOnCAiZ1MeiGeYdKtCcp9cfHhB11YoXeewFo2+Cv9kJIYQQQghZf5DbaQ1mhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCEkMyFZc0FwdlzZPC7rwtlxibaqy45rzVX8BUWnY6Q1/jMIAAAAAElFTkSuQmCC", - "description": "Simple form to input new double value for pre-defined server-side attribute key.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", - "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", - "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false; \n\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [$scope.validators.min(settings.minValue),\n $scope.validators.max(settings.maxValue)];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n \n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SERVER_SCOPE',\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(correctValue($scope.originalValue));\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nfunction correctValue(value) {\n if (typeof value !== \"number\") {\n return 0;\n }\n return value;\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-update-double-attribute-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showResultMessage\":true,\"showLabel\":true,\"isRequired\":true},\"title\":\"Update server double attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "update_shared_image_attribute", - "name": "Update shared image attribute", - "image": "data:image/png;base64,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", - "description": "Simple form to input new image for pre-defined shared attribute key.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3.5, - "resources": [], - "templateHtml": "
\n
\n
\n
\n
\n \n \n
\n \n
\n \n \n
\n
\n \n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", - "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n align-items: center;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.tb-image-preview-container div,\n.tb-flow-drop label {\n font-size: 16px !important;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", - "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet utils;\r\nlet translate;\r\n\r\nself.onInit = function() {\r\n self.ctx.ngZone.run(function() {\r\n init(); \r\n self.ctx.detectChanges(true);\r\n });\r\n};\r\n\r\nfunction init() {\r\n\r\n $scope = self.ctx.$scope;\r\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n $scope.toastTargetId = 'input-widget' + utils.guid();\r\n settings = utils.deepClone(self.ctx.settings) || {};\r\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\r\n settings.displayPreview = utils.defaultValue(settings.displayPreview, true);\r\n settings.displayClearButton = utils.defaultValue(settings.displayClearButton, false);\r\n settings.displayApplyButton = utils.defaultValue(settings.displayApplyButton, true);\r\n settings.displayDiscardButton = utils.defaultValue(settings.displayDiscardButton, true);\r\n $scope.settings = settings;\r\n $scope.isValidParameter = true;\r\n $scope.dataKeyDetected = false;\r\n $scope.entityDetected = false;\r\n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\r\n \r\n $scope.attributeUpdateFormGroup = $scope.fb.group(\r\n {currentValue: [undefined, []]}\r\n );\r\n \r\n $scope.attributeUpdateFormGroup.valueChanges.subscribe( () => {\r\n self.ctx.detectChanges();\r\n if (!settings.displayApplyButton) {\r\n $scope.updateAttribute();\r\n }\r\n });\r\n\r\n if (self.ctx.datasources && self.ctx.datasources.length) {\r\n var datasource = self.ctx.datasources[0];\r\n \r\n if (datasource.type === 'entity') {\r\n if (datasource.entityType === 'DEVICE') {\r\n if (datasource.entityType && datasource.entityId) {\r\n $scope.entityName = datasource.entityName;\r\n if (settings.widgetTitle && settings.widgetTitle.length) {\r\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n } else {\r\n $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n }\r\n \r\n $scope.entityDetected = true;\r\n }\r\n } else {\r\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\r\n }\r\n }\r\n if (datasource.dataKeys.length) {\r\n if (datasource.dataKeys[0].type !== \"attribute\") {\r\n $scope.isValidParameter = false;\r\n } else {\r\n $scope.currentKey = datasource.dataKeys[0].name;\r\n $scope.dataKeyType = datasource.dataKeys[0].type;\r\n $scope.dataKeyDetected = true;\r\n }\r\n }\r\n }\r\n\r\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n \r\n $scope.updateAttribute = function () {\r\n if ($scope.entityDetected) {\r\n var datasource = self.ctx.datasources[0];\r\n\r\n attributeService.saveEntityAttributes(\r\n datasource.entity.id,\r\n 'SHARED_SCOPE',\r\n [\r\n {\r\n key: $scope.currentKey,\r\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\r\n }\r\n ]\r\n ).subscribe(\r\n function success() {\r\n if (settings.displayApplyButton) {\r\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\r\n }\r\n if (settings.showResultMessage) {\r\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n },\r\n function fail() {\r\n if (settings.showResultMessage) {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n }\r\n );\r\n }\r\n };\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n try {\r\n if ($scope.dataKeyDetected) {\r\n var value = self.ctx.data[0].data[0][1];\r\n if (settings.displayApplyButton || !$scope.originalValue) {\r\n $scope.originalValue = value;\r\n }\r\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(value, {emitEvent: false});\r\n self.ctx.detectChanges();\r\n }\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n};\r\n\r\nself.onResize = function() {\r\n\r\n};\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1,\r\n maxDataKeys: 1,\r\n singleEntity: true\r\n };\r\n};\r\n\r\nself.onDestroy = function() {\r\n $scope.attributeUpdateFormGroup.valueChanges.unsubscribe();\r\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-update-image-attribute-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showResultMessage\":true,\"displayPreview\":true,\"displayClearButton\":false,\"displayApplyButton\":true,\"displayDiscardButton\":true},\"title\":\"Update shared image attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "update_shared_date_attribute", - "name": "Update shared date attribute", - "image": "data:image/png;base64,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", - "description": "Simple form to input new date for pre-defined shared attribute key.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3.5, - "resources": [], - "templateHtml": "
\n
\n
\n
\n
\n \n {{ labelValue }}\n \n \n \n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", - "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", - "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet utils;\r\nlet translate;\r\n\r\nself.onInit = function() {\r\n self.ctx.ngZone.run(function() {\r\n init(); \r\n self.ctx.detectChanges(true);\r\n });\r\n};\r\n\r\nfunction init() {\r\n\r\n $scope = self.ctx.$scope;\r\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n $scope.toastTargetId = 'input-widget' + utils.guid();\r\n settings = utils.deepClone(self.ctx.settings) || {};\r\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\r\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\r\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\r\n $scope.settings = settings;\r\n $scope.isValidParameter = true;\r\n $scope.dataKeyDetected = false;\r\n $scope.entityDetected = false;\r\n\r\n $scope.datePickerType = settings.showTimeInput ? 'datetime' : 'date'; \r\n \r\n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\r\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\r\n $scope.labelValue = translate.instant('widgets.input-widgets.date');\r\n \r\n \r\n if (settings.showTimeInput) {\r\n $scope.labelValue += \" & \" + translate.instant('widgets.input-widgets.time');\r\n }\r\n \r\n var validators = [];\r\n \r\n if (settings.isRequired) {\r\n validators.push($scope.validators.required);\r\n }\r\n \r\n $scope.attributeUpdateFormGroup = $scope.fb.group(\r\n {currentValue: [undefined, validators]}\r\n );\r\n\r\n if (self.ctx.datasources && self.ctx.datasources.length) {\r\n var datasource = self.ctx.datasources[0];\r\n \r\n if (datasource.type === 'entity') {\r\n if (datasource.entityType === 'DEVICE') {\r\n if (datasource.entityType && datasource.entityId) {\r\n $scope.entityName = datasource.entityName;\r\n if (settings.widgetTitle && settings.widgetTitle.length) {\r\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n } else {\r\n $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n }\r\n \r\n $scope.entityDetected = true;\r\n }\r\n } else {\r\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\r\n }\r\n }\r\n if (datasource.dataKeys.length) {\r\n if (datasource.dataKeys[0].type !== \"attribute\") {\r\n $scope.isValidParameter = false;\r\n } else {\r\n $scope.currentKey = datasource.dataKeys[0].name;\r\n $scope.dataKeyType = datasource.dataKeys[0].type;\r\n $scope.dataKeyDetected = true;\r\n }\r\n }\r\n }\r\n\r\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n \r\n $scope.clear = function(event) {\r\n event.stopPropagation();\r\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(undefined);\r\n }\r\n \r\n $scope.updateAttribute = function () {\r\n if ($scope.entityDetected) {\r\n var datasource = self.ctx.datasources[0];\r\n var currentValueInMilliseconds;\r\n \r\n if (!$scope.attributeUpdateFormGroup.get('currentValue').value) {\r\n currentValueInMilliseconds = undefined;\r\n } else {\r\n currentValueInMilliseconds = $scope.attributeUpdateFormGroup.get('currentValue').value.getTime();\r\n }\r\n\r\n attributeService.saveEntityAttributes(\r\n datasource.entity.id,\r\n 'SHARED_SCOPE',\r\n [\r\n {\r\n key: $scope.currentKey,\r\n value: currentValueInMilliseconds\r\n }\r\n ]\r\n ).subscribe(\r\n function success() {\r\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\r\n if (settings.showResultMessage) {\r\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n },\r\n function fail() {\r\n if (settings.showResultMessage) {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n }\r\n );\r\n }\r\n };\r\n \r\n $scope.isValidDate = function(date) {\r\n return date instanceof Date && !isNaN(date);\r\n }\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n try {\r\n if ($scope.dataKeyDetected) {\r\n if (!$scope.isFocused) {\r\n $scope.originalValue = moment(self.ctx.data[0].data[0][1]).toDate();\r\n \r\n if (!$scope.isValidDate($scope.originalValue)) {\r\n $scope.originalValue = undefined;\r\n }\r\n \r\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\r\n self.ctx.detectChanges();\r\n }\r\n }\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n};\r\n\r\nself.onResize = function() {\r\n\r\n};\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1,\r\n maxDataKeys: 1,\r\n singleEntity: true\r\n };\r\n};\r\n\r\nself.onDestroy = function() {\r\n\r\n}\r\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-update-date-attribute-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showResultMessage\":true,\"isRequired\":true,\"showLabel\":true,\"showTimeInput\":true},\"title\":\"Update shared date attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "update_shared_boolean_attribute", - "name": "Update shared boolean attribute", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAIQSURBVHja7d3PSxRhHMfx+VcWgmg3ltKD7BJshyBIQusQ3vIYCILCRMyhH6xShlGWMGq1p1gIibxFULRR1F4ixcNqdNhmUXFipWnTYvfZ/TrjJqFQ13ge3p/L8MztBQ/P8/0eZr6WNHyvrHk8vy5WoxIo0TwqqDQsPxADEviWp0yAKM8qixEpAwECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEC5H9DSiP2noyUNIXsc9j2qKYQ2/732lzI7LyukN4jUV7uLrtndIWcuP4lzKYBELf9rD66/bzZhqw9vFcM37ReTzz5pR3kc8fAWPriDmT58NDl5F0R51j2zOm6PpB+13VfyNNbIm8O/Iwg430iz87Lu/iaNDKz+kB6Hcd5HH60PDedjfkR5FXi2oeWyI3j+Xy+56puW2vh6KXcnTZEFq90pQripAbC5HSDDDoin9qQjxVRkwk1dUrLU2v4bLAxGFuNIEPnanK/s7WSeNDcct7rBvFOxg7djBUjSLX/YDJdEHmbiccv1PQrUb7++T/B9/Xfd8sPikbK+L81VqP7HEu0ukCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAADEUYsyAYDNGNn/zrboZQ7SVZcZYcyXbTFGWg8aBYQQAAAAASUVORK5CYII=", - "description": "Simple form to input new boolean value for pre-defined shared attribute key.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "
\r\n
\r\n
\r\n
\r\n
\r\n \r\n {{currentValue}}\r\n \r\n
\r\n
\r\n\r\n
\r\n
\r\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\r\n
\r\n
\r\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\r\n
\r\n
\r\n
\r\n
", - "templateCss": ".attribute-update-form {\r\n overflow: hidden;\r\n height: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n.attribute-update-form__grid {\r\n display: flex;\r\n}\r\n.grid__element:first-child {\r\n flex: 1;\r\n}\r\n\r\n.grid__element {\r\n display: flex;\r\n}\r\n\r\n.attribute-update-form .mat-button.mat-icon-button {\r\n width: 32px;\r\n min-width: 32px;\r\n height: 32px;\r\n min-height: 32px;\r\n padding: 0 !important;\r\n margin: 0 !important;\r\n line-height: 20px;\r\n}\r\n\r\n.attribute-update-form .mat-icon-button mat-icon {\r\n width: 20px;\r\n min-width: 20px;\r\n height: 20px;\r\n min-height: 20px;\r\n font-size: 20px;\r\n}\r\n\r\n.tb-toast {\r\n font-size: 14px!important;\r\n}", - "controllerScript": "let settings;\nlet attributeService;\nlet utils;\nlet translate;\nlet $scope;\nlet map;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init();\n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\n\n settings.trueValue = utils.defaultValue(utils.customTranslation(settings.trueValue, settings.trueValue), true);\n settings.falseValue = utils.defaultValue(utils.customTranslation(settings.falseValue, settings.falseValue), false);\n\n map = {\n true: settings.trueValue,\n false: settings.falseValue\n };\n \n $scope.checkboxValue = false;\n $scope.currentValue = map[$scope.checkboxValue];\n\n $scope.attributeUpdateFormGroup = $scope.fb.group({checkboxValue: [$scope.checkboxValue]});\n\n $scope.changed = function() {\n $scope.checkboxValue = $scope.attributeUpdateFormGroup.get('checkboxValue').value;\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.updateAttribute();\n };\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType === 'DEVICE') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n \n $scope.entityDetected = true;\n }\n } else {\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function() {\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SHARED_SCOPE',\n [\n {\n key: $scope.currentKey,\n value: $scope.checkboxValue || false\n }\n ]\n ).subscribe(\n function success() {\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n $scope.checkboxValue = self.ctx.data[0].data[0][1] === 'true';\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.attributeUpdateFormGroup.get('checkboxValue').patchValue($scope.checkboxValue);\n self.ctx.detectChanges();\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {}", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-update-boolean-attribute-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update shared boolean attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "update_server_integer_attribute", - "name": "Update server integer attribute", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAANBSURBVHja7d3dS1NhHAfw/Sum1IUQBZYJIdSF4VVQIOSNXZiJg5nmWi18CXG9WJgSqd1IBUWu10PY1EBcIFYqyoTCLOZWA8/0uGmbbjs73y7mS+Z2YdjYs76/i3Oe88C5+PB73jhwnkeHiOxyCh4uOQxdxO1XIXiofndEJ/uRBuGXdS41HSCqS+dEWoSTEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYSQfwl59xkA+teqDfLWl9wOESC9twAo5QsJIdHI6EstkvoQX4UP6LsNzNo/hgGDrI2oCI0B0XG7DADuSzcttaMC9JG2t0DTMEZqezuvAwY5UhbEvB5a213J9A0Awg0mnwid/cM1zBpCmPQgWrGwARlv1GDvBABbd88TESDhc3Ov7wM/H99oKp/dgEgXWlqaGgFAWYl6hBh+H9rqp4COV1EYYpAA5vWwdXi9XkWoeeRLjVkDGofxqdwDg4zqrxjTY+b8HDwOoSDaZQnAeNXFVuMEDDIGKs1temCwpsE8JeLMHg2u95qVGHBR4xKFEEIIIeT/gyxvKYgB0d5sXhAtN0uxgtS8LBJEsxrtm2sko/TbTRSI9tzY92ddj1FavYgD0axbHYBklOLnI2Uh8fIRy0kCR8pBgvdcALRn8R3bhMjdrdYlAFh62mpd/Uk81J8ciGKp/56gXW27aQ3uPVqac9AJTOUePpOfMw1gom5/VpJSsnC1zpU4H9vp7KEDlSqUQ3qg6GQI4cKzwIvMU6eTBYFiMSXOx3aG38mM9wCMBUDDAIAr+YDzB9qTBoFiie/4uwmxuGJ1+CgqAYBkQhDcwSVKX+YIANi6SvKmkw7ZwUXjzL5GAEDlsT2l8wJD5o+UrO1o4S0sFhcSOH4isP7wYFdAVEikpEABgGBWF4D27KigEK06u9/hcDgUlOUNLQ7lmkRtWksZsXiEhaqsjN3mgMij1lqEPcJsY8OPD4QQQgghhOws5E5SgxkhhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCElNSNocEJweRzb7ZF04PQ7RVnXpcay5il+KVmW7YpZ2rQAAAABJRU5ErkJggg==", - "description": "Simple form to input new integer value for pre-defined server attribute key.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", - "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", - "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false; \n\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [\n $scope.validators.min(settings.minValue),\n $scope.validators.max(settings.maxValue),\n $scope.validators.pattern(/^-?[0-9]+$/)\n ];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n\n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n \n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SERVER_SCOPE',\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(correctValue($scope.originalValue));\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nfunction correctValue(value) {\n if (typeof value !== \"number\") {\n return 0;\n }\n return value;\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-update-integer-attribute-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update server integer attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "update_server_string_attribute", - "name": "Update server string attribute", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAOkSURBVHja7d3bSxRRHAfw/VdG6yklCbPsRk+GhVLYDSXMohC1tczVUkxFXDULzW4a+SA9VIZRm5haIJqBmMqW2cUoddUVR1sved2dnV/nuJaX3X0Q1pjZvj/YM4fDmYfP/uZcdvbhaMgmmnpVHibRShpb/4REKg9pot+mESfIC2JC1Jgkb4BIJk0veUX0AgIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAsp6QN194+epPs1Z0vqm/Uw2QuuussMSOuYXYbe3PZJvyIeNx40T1xUTDTe+sHCK3STTfwQDGpgVU/+Vr+ox2FYyRktdEuS3UllFXVsAhtrMz9DOe5JI7htQfvIM1K3VcDYO9NZ+GtfP00Uz2uLEliDFHpqYy3qG2suaxGiDW86PVFURTD6/mxg4vQQwpRUW5ObyDZc5uVsX0+6A2s5uo9LmdtA7INIfUlo6MjFhUtY58S06TiXJa6HOsmc9aSd+pI576Lo6SuVNVEDndwErjhUs3dO85pCExrSSeqDE5K61bjSu7febvqJlzACdlbFEAAQQQQP4/yKxTRR0Q+eXKDdFsocFRMRTOqgkiP9E1rWwx6AzLLmqByE919avbajihxqVDsRCWj3rnVpYM1/lQLMRVPhw5ceNQHGTmnok7qlw7PAuJOrWOEIs+c8DNc+XxR2tdITSWd8XkPh8eHezrC2E5SXWfjzVNvxUdRn0Bf29V3sZvq2GVD82FuUYy5ukbFiBdN7MbeU+pOvvuV96xoyrT4kGJa8eaF8Sg/cHaEN9WIr/ixe9/8+7QxD0bsgITDgqPWMu2refChXIi28mApIiNzOYX7B826MEB76EtStC+SbLuSFkOiZZoaos/ewcUdYh9AodJ1gX8ovubekhO2Ms6hk8pcdMYlM+KmBPLIUWscoy1UF7Q4hjpEpopTMsq9cKYo6NCIacjV0MiY1hRELgImRQqKUBYiC5VQ4aEatqZ2MljWvGQYP6C9LAz5CirvBA+UUyYndXmSPGQ2GDjwG0fZ4hQPPh21xH2l5BP+oCoD7UrHtITIvgej3CGnIkWhAN9fELfLggh7YrNyLIQp92sWUOOq31QxG92QAABBBBAvBNy658GMgIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAokyI1xwQ7B1HNo+LGqt3HKItabzjWHOJfgM5tA7UOw6xeQAAAABJRU5ErkJggg==", - "description": "Simple form to input new string value for pre-defined server-side attribute key.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", - "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", - "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [$scope.validators.minLength(settings.minLength),\n $scope.validators.maxLength(settings.maxLength)];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n \n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n \n var value = $scope.attributeUpdateFormGroup.get('currentValue').value;\n \n if (!$scope.attributeUpdateFormGroup.get('currentValue').value.length) {\n value = null;\n }\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SERVER_SCOPE',\n [\n {\n key: $scope.currentKey,\n value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-update-string-attribute-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showResultMessage\":true,\"showLabel\":true,\"isRequired\":true},\"title\":\"Update server string attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "update_location_timeseries", - "name": "Update location timeseries", - "image": "data:image/png;base64,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", - "description": "Simple form to input new location for pre-defined timeseries keys.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? latLabel : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n \n \n {{ settings.showLabel ? lngLabel : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n \n
\n
\n \n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-timeseries-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-coordinate-specified' | translate }}\n
\n
\n
\n
", - "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex-direction: column;\n flex: 1;\n}\n\n.grid__element.horizontal-alignment {\n flex-direction: row;\n}\n\n.grid__element:last-child {\n align-items: center;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n margin: 0;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-button.getLocation {\n margin-right: 10px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.attribute-update-form mat-form-field{\n width: 100%;\n padding-right: 5px;\n}\n\n.attribute-update-form.small-width mat-form-field{\n width: 150px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", - "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet utils;\r\nlet translate;\r\n\r\nself.onInit = function() {\r\n self.ctx.ngZone.run(function() {\r\n init(); \r\n self.ctx.detectChanges(true);\r\n });\r\n};\r\n\r\n\r\nfunction init() {\r\n $scope = self.ctx.$scope;\r\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n $scope.toastTargetId = 'input-widget' + utils.guid();\r\n settings = utils.deepClone(self.ctx.settings) || {};\r\n \r\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\r\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\r\n settings.showGetLocation = utils.defaultValue(settings.showGetLocation, true);\r\n settings.enableHighAccuracy = utils.defaultValue(settings.enableHighAccuracy, false);\r\n $scope.settings = settings;\r\n $scope.isValidParameter = true;\r\n $scope.dataKeyDetected = false; \r\n\r\n $scope.isHorizontal = (settings.inputFieldsAlignment === 'row');\r\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-coordinate-required');\r\n $scope.latLabel = utils.customTranslation(settings.latLabel, settings.latLabel) || translate.instant('widgets.input-widgets.latitude');\r\n $scope.lngLabel = utils.customTranslation(settings.lngLabel, settings.lngLabel) || translate.instant('widgets.input-widgets.longitude');\r\n\r\n $scope.attributeUpdateFormGroup = $scope.fb.group(\r\n {currentLat: [undefined, [$scope.validators.required,\r\n $scope.validators.min(-90),\r\n $scope.validators.max(90)]],\r\n currentLng: [undefined, [$scope.validators.required,\r\n $scope.validators.min(-180),\r\n $scope.validators.max(180)]]}\r\n );\r\n\r\n if (self.ctx.datasources && self.ctx.datasources.length) {\r\n var datasource = self.ctx.datasources[0];\r\n if (datasource.type === 'entity') {\r\n if (datasource.entityType && datasource.entityId) {\r\n $scope.entityName = datasource.entityName;\r\n if (settings.widgetTitle && settings.widgetTitle.length) {\r\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n } else {\r\n $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n }\r\n\r\n $scope.entityDetected = true;\r\n }\r\n }\r\n if (datasource.dataKeys.length > 1) {\r\n $scope.dataKeyDetected = true;\r\n for (let i = 0; i < datasource.dataKeys.length; i++) {\r\n if (datasource.dataKeys[i].type != \"timeseries\"){\r\n $scope.isValidParameter = false;\r\n }\r\n if (datasource.dataKeys[i].name !== settings.latKeyName && datasource.dataKeys[i].name !== settings.lngKeyName){\r\n $scope.dataKeyDetected = false;\r\n }\r\n }\r\n }\r\n }\r\n\r\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n\r\n $scope.updateAttribute = function () {\r\n $scope.isFocused = false;\r\n if ($scope.entityDetected) {\r\n var datasource = self.ctx.datasources[0];\r\n\r\n attributeService.saveEntityTimeseries(\r\n datasource.entity.id,\r\n 'scope',\r\n [\r\n {\r\n key: settings.latKeyName,\r\n value: $scope.attributeUpdateFormGroup.get('currentLat').value\r\n },{\r\n key: settings.lngKeyName,\r\n value: $scope.attributeUpdateFormGroup.get('currentLng').value\r\n }\r\n ]\r\n ).subscribe(\r\n function success() {\r\n $scope.originalLat = $scope.attributeUpdateFormGroup.get('currentLat').value;\r\n $scope.originalLng = $scope.attributeUpdateFormGroup.get('currentLng').value;\r\n if (settings.showResultMessage) {\r\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n },\r\n function fail() {\r\n if (settings.showResultMessage) {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n }\r\n );\r\n }\r\n };\r\n\r\n $scope.changeFocus = function () {\r\n if ($scope.attributeUpdateFormGroup.get('currentLat').value === $scope.originalLat && $scope.attributeUpdateFormGroup.get('currentLng').value === $scope.originalLng) {\r\n $scope.isFocused = false;\r\n }\r\n };\r\n \r\n $scope.discardChange = function() {\r\n $scope.attributeUpdateFormGroup.setValue({\r\n 'currentLat': $scope.originalLat,\r\n 'currentLng': $scope.originalLng\r\n });\r\n $scope.isFocused = false;\r\n $scope.attributeUpdateFormGroup.markAsPristine();\r\n self.onDataUpdated();\r\n };\r\n \r\n $scope.disableButton = function () {\r\n return $scope.attributeUpdateFormGroup.get('currentLat').value === $scope.originalLat && $scope.attributeUpdateFormGroup.get('currentLng').value === $scope.originalLng || $scope.currentLng === null || $scope.currentLat === null;\r\n };\r\n \r\n $scope.getCoordinate = function() {\r\n if (navigator.geolocation) {\r\n navigator.geolocation.getCurrentPosition(showPosition, function (){\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.blocked-location'), \r\n 'bottom', 'left', $scope.toastTargetId);\r\n }, {\r\n enableHighAccuracy: settings.enableHighAccuracy\r\n });\r\n } else {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.no-support-geolocation'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n };\r\n \r\n function showPosition(position) {\r\n $scope.attributeUpdateFormGroup.setValue({\r\n currentLat: correctValue(position.coords.latitude),\r\n currentLng: correctValue(position.coords.longitude)\r\n });\r\n $scope.attributeUpdateFormGroup.markAsDirty();\r\n $scope.isFocused = true;\r\n }\r\n \r\n self.onResize();\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n try {\r\n if ($scope.dataKeyDetected) {\r\n if (!$scope.isFocused) {\r\n for(let i = 0; i < self.typeParameters().maxDataKeys; i++){\r\n if(self.ctx.data[i].dataKey.name === self.ctx.settings.latKeyName && $scope.attributeUpdateFormGroup.get('currentLat').pristine){\r\n $scope.originalLat = self.ctx.data[i].data[0][1];\r\n $scope.attributeUpdateFormGroup.get('currentLat').patchValue(correctValue($scope.originalLat));\r\n } else if(self.ctx.data[i].dataKey.name === self.ctx.settings.lngKeyName && $scope.attributeUpdateFormGroup.get('currentLng').pristine){\r\n $scope.originalLng = self.ctx.data[i].data[0][1];\r\n $scope.attributeUpdateFormGroup.get('currentLng').patchValue(correctValue($scope.originalLng));\r\n }\r\n }\r\n self.ctx.detectChanges();\r\n }\r\n }\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n};\r\n\r\nfunction correctValue(value) {\r\n if (typeof value !== \"number\") {\r\n return 0;\r\n }\r\n return value;\r\n}\r\n\r\nself.onResize = function() {\r\n $scope.smallWidthContainer = (self.ctx.$container && self.ctx.$container[0].offsetWidth < 320);\r\n $scope.changeAlignment = ($scope.isHorizontal && self.ctx.$container && self.ctx.$container[0].offsetWidth < 480);\r\n self.ctx.detectChanges();\r\n};\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1,\r\n maxDataKeys: 2,\r\n singleEntity: true\r\n };\r\n};\r\n\r\nself.onDestroy = function() {\r\n\r\n};", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-update-location-attribute-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update location timeseries\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "update_shared_double_attribute", - "name": "Update shared double attribute", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAANMSURBVHja7d3fS5NRGAfw/SsrhSCKKIugburCi6KCLrropi4izME0c1kLUxquHybmKNPAi4qMJkm9hOkswhVkpaLMfhmlbjHYu+3Vaf7a3r3fLjad5nZh6NiZ3+diO+fAe/HhOc85Z+/Fjg4R2T0ieLjlMHQRT0iF4KGGPBGdHEIWREjWudVsgKhu3QiyIkYIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQghZS8jbbwDQOT9slJc/5HGJAOmoAaAUjKWERCO9z7RI5kPGC8cBxy3A5/wUBoyy1qNirg+I9jtlAPBcvGkt7xWgRmyvgKpu9JR3NF4HjHLk9DSCBmi2eqnsFwCEK8vGRSj2j9fgM85h0Ito4VgC0m/R4GwEgHZ72xMRIOHiwIv7wJ/HN6oKfAmIdL62tsoCAMps1CvE8vuwvWIIaHgehTEGmULQgPYGv9+vCLWP/Cg1a4ClG18LvDDKKPmJPgNGzwXgdQkF0S5JAPrPXqgzDcAo402R2WYAukorzUMi7uzR6YWqmY0BJzQeUQghhBBC1h9kZllDDIj2cumBaKZaijWk6hmRIFqLybl0RDJJi75EgWitJse/Y20mKf4hDkRrWe4AJJOUPB8ZC0mWj1hOUjgyDjJ9zw1Ae5rcsUKIbK9rmVzovatv+hJvfh5ec4hirfidYl6teGp1bd1/Km9XfDhasOnksZxGAIGmA/rWtU/J2NXL7tT5WEmxz+0sUqHsNsR69lwXcDc3AC13b0U6IFCsZanzsZLld1D/AYApP/5wDYBBfTfwPupPCwSKNbnj/zbE44WLOvYcLwCkCYLpVTyiODb2zDe/P7qypQnphKzioXF0myUxMw9t3+MUFBLcd2LxP1qo1bnDQkKmjhydWjqw4YGIkMiJ/MT5ufggAL9eEhCilWzudLlcLgWvbYBDfyfoPpOnCAiZ1MeiGeYdKtCcp9cfHhB11YoXeewFo2+Cv9kJIYQQQghZf5DbaQ1mhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCEkMyFZc0FwdlzZPC7rwtlxibaqy45rzVX8BUWnY6Q1/jMIAAAAAElFTkSuQmCC", - "description": "Simple form to input new double value for pre-defined shared attribute key.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", - "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", - "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false; \n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\n \n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [\n $scope.validators.min(settings.minValue),\n $scope.validators.max(settings.maxValue)\n ];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n\n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n \n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType === 'DEVICE') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n \n $scope.entityDetected = true;\n }\n } else {\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SHARED_SCOPE',\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(correctValue($scope.originalValue));\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nfunction correctValue(value) {\n if (typeof value !== \"number\") {\n return 0;\n }\n return value;\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-update-double-attribute-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update shared double attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "update_shared_integer_attribute", - "name": "Update shared integer attribute", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAANBSURBVHja7d3dS1NhHAfw/Sum1IUQBZYJIdSF4VVQIOSNXZiJg5nmWi18CXG9WJgSqd1IBUWu10PY1EBcIFYqyoTCLOZWA8/0uGmbbjs73y7mS+Z2YdjYs76/i3Oe88C5+PB73jhwnkeHiOxyCh4uOQxdxO1XIXiofndEJ/uRBuGXdS41HSCqS+dEWoSTEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYSQfwl59xkA+teqDfLWl9wOESC9twAo5QsJIdHI6EstkvoQX4UP6LsNzNo/hgGDrI2oCI0B0XG7DADuSzcttaMC9JG2t0DTMEZqezuvAwY5UhbEvB5a213J9A0Awg0mnwid/cM1zBpCmPQgWrGwARlv1GDvBABbd88TESDhc3Ov7wM/H99oKp/dgEgXWlqaGgFAWYl6hBh+H9rqp4COV1EYYpAA5vWwdXi9XkWoeeRLjVkDGofxqdwDg4zqrxjTY+b8HDwOoSDaZQnAeNXFVuMEDDIGKs1temCwpsE8JeLMHg2u95qVGHBR4xKFEEIIIeT/gyxvKYgB0d5sXhAtN0uxgtS8LBJEsxrtm2sko/TbTRSI9tzY92ddj1FavYgD0axbHYBklOLnI2Uh8fIRy0kCR8pBgvdcALRn8R3bhMjdrdYlAFh62mpd/Uk81J8ciGKp/56gXW27aQ3uPVqac9AJTOUePpOfMw1gom5/VpJSsnC1zpU4H9vp7KEDlSqUQ3qg6GQI4cKzwIvMU6eTBYFiMSXOx3aG38mM9wCMBUDDAIAr+YDzB9qTBoFiie/4uwmxuGJ1+CgqAYBkQhDcwSVKX+YIANi6SvKmkw7ZwUXjzL5GAEDlsT2l8wJD5o+UrO1o4S0sFhcSOH4isP7wYFdAVEikpEABgGBWF4D27KigEK06u9/hcDgUlOUNLQ7lmkRtWksZsXiEhaqsjN3mgMij1lqEPcJsY8OPD4QQQgghhOws5E5SgxkhhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCElNSNocEJweRzb7ZF04PQ7RVnXpcay5il+KVmW7YpZ2rQAAAABJRU5ErkJggg==", - "description": "Simple form to input new integer value for pre-defined shared attribute key.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", - "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", - "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false; \n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\n \n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [\n $scope.validators.min(settings.minValue),\n $scope.validators.max(settings.maxValue),\n $scope.validators.pattern(/^-?[0-9]+$/)\n ];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n\n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType === 'DEVICE') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n \n $scope.entityDetected = true;\n }\n } else {\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SHARED_SCOPE',\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(correctValue($scope.originalValue));\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nfunction correctValue(value) {\n if (typeof value !== \"number\") {\n return 0;\n }\n return value;\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-update-integer-attribute-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update shared integer attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "update_shared_string_attribute", - "name": "Update shared string attribute", - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAOkSURBVHja7d3bSxRRHAfw/VdG6yklCbPsRk+GhVLYDSXMohC1tczVUkxFXDULzW4a+SA9VIZRm5haIJqBmMqW2cUoddUVR1sved2dnV/nuJaX3X0Q1pjZvj/YM4fDmYfP/uZcdvbhaMgmmnpVHibRShpb/4REKg9pot+mESfIC2JC1Jgkb4BIJk0veUX0AgIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAsp6QN194+epPs1Z0vqm/Uw2QuuussMSOuYXYbe3PZJvyIeNx40T1xUTDTe+sHCK3STTfwQDGpgVU/+Vr+ox2FYyRktdEuS3UllFXVsAhtrMz9DOe5JI7htQfvIM1K3VcDYO9NZ+GtfP00Uz2uLEliDFHpqYy3qG2suaxGiDW86PVFURTD6/mxg4vQQwpRUW5ObyDZc5uVsX0+6A2s5uo9LmdtA7INIfUlo6MjFhUtY58S06TiXJa6HOsmc9aSd+pI576Lo6SuVNVEDndwErjhUs3dO85pCExrSSeqDE5K61bjSu7febvqJlzACdlbFEAAQQQQP4/yKxTRR0Q+eXKDdFsocFRMRTOqgkiP9E1rWwx6AzLLmqByE919avbajihxqVDsRCWj3rnVpYM1/lQLMRVPhw5ceNQHGTmnok7qlw7PAuJOrWOEIs+c8DNc+XxR2tdITSWd8XkPh8eHezrC2E5SXWfjzVNvxUdRn0Bf29V3sZvq2GVD82FuUYy5ukbFiBdN7MbeU+pOvvuV96xoyrT4kGJa8eaF8Sg/cHaEN9WIr/ixe9/8+7QxD0bsgITDgqPWMu2refChXIi28mApIiNzOYX7B826MEB76EtStC+SbLuSFkOiZZoaos/ewcUdYh9AodJ1gX8ovubekhO2Ms6hk8pcdMYlM+KmBPLIUWscoy1UF7Q4hjpEpopTMsq9cKYo6NCIacjV0MiY1hRELgImRQqKUBYiC5VQ4aEatqZ2MljWvGQYP6C9LAz5CirvBA+UUyYndXmSPGQ2GDjwG0fZ4hQPPh21xH2l5BP+oCoD7UrHtITIvgej3CGnIkWhAN9fELfLggh7YrNyLIQp92sWUOOq31QxG92QAABBBBAvBNy658GMgIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAokyI1xwQ7B1HNo+LGqt3HKItabzjWHOJfgM5tA7UOw6xeQAAAABJRU5ErkJggg==", - "description": "Simple form to input new string value for pre-defined shared attribute key.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", - "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", - "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false; \n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\n \n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [$scope.validators.minLength(settings.minLength),\n $scope.validators.maxLength(settings.maxLength)];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n \n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType === 'DEVICE') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n \n $scope.entityDetected = true;\n }\n } else {\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n var value = $scope.attributeUpdateFormGroup.get('currentValue').value;\n \n if (!$scope.attributeUpdateFormGroup.get('currentValue').value.length) {\n value = null;\n }\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SHARED_SCOPE',\n [\n {\n key: $scope.currentKey,\n value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-update-string-attribute-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update shared string attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "update_server_location_attribute", - "name": "Update server location attribute", - "image": "data:image/png;base64,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", - "description": "Simple form to input new location for pre-defined server attribute key.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? latLabel : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n \n \n {{ settings.showLabel ? lngLabel : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n \n
\n
\n \n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-coordinate-specified' | translate }}\n
\n
\n
\n
", - "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex-direction: column;\n flex: 1;\n}\n\n.grid__element.horizontal-alignment {\n flex-direction: row;\n}\n\n.grid__element:last-child {\n align-items: center;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-button.getLocation {\n margin-right: 10px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.attribute-update-form mat-form-field{\n width: 100%;\n padding-right: 5px;\n}\n\n.attribute-update-form.small-width mat-form-field{\n width: 150px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", - "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet utils;\r\nlet translate;\r\n\r\nself.onInit = function() {\r\n self.ctx.ngZone.run(function() {\r\n init(); \r\n self.ctx.detectChanges(true);\r\n });\r\n};\r\n\r\nfunction init() {\r\n $scope = self.ctx.$scope;\r\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n $scope.toastTargetId = 'input-widget' + utils.guid();\r\n settings = utils.deepClone(self.ctx.settings) || {};\r\n \r\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\r\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\r\n settings.showGetLocation = utils.defaultValue(settings.showGetLocation, true);\r\n settings.enableHighAccuracy = utils.defaultValue(settings.enableHighAccuracy, false);\r\n settings.isLatRequired = utils.defaultValue(settings.isLatRequired, true);\r\n settings.isLngRequired = utils.defaultValue(settings.isLngRequired, true);\r\n $scope.settings = settings;\r\n $scope.isValidParameter = true;\r\n $scope.dataKeyDetected = false; \r\n\r\n $scope.isHorizontal = (settings.inputFieldsAlignment === 'row');\r\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-coordinate-required');\r\n $scope.latLabel = utils.customTranslation(settings.latLabel, settings.latLabel) || translate.instant('widgets.input-widgets.latitude');\r\n $scope.lngLabel = utils.customTranslation(settings.lngLabel, settings.lngLabel) || translate.instant('widgets.input-widgets.longitude');\r\n \r\n var validatorsLat = [$scope.validators.min(-90), $scope.validators.max(90)];\r\n var validatorsLng = [$scope.validators.min(-180), $scope.validators.max(180)];\r\n \r\n if (settings.isLatRequired) {\r\n validatorsLat.push($scope.validators.required);\r\n }\r\n \r\n if (settings.isLngRequired) {\r\n validatorsLng.push($scope.validators.required);\r\n }\r\n\r\n $scope.attributeUpdateFormGroup = $scope.fb.group({\r\n currentLat: [undefined, validatorsLat],\r\n currentLng: [undefined, validatorsLng]\r\n });\r\n\r\n if (self.ctx.datasources && self.ctx.datasources.length) {\r\n var datasource = self.ctx.datasources[0];\r\n if (datasource.type === 'entity') {\r\n if (datasource.entityType && datasource.entityId) {\r\n $scope.entityName = datasource.entityName;\r\n if (settings.widgetTitle && settings.widgetTitle.length) {\r\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n } else {\r\n $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n }\r\n\r\n $scope.entityDetected = true;\r\n }\r\n }\r\n if (datasource.dataKeys.length > 1) {\r\n $scope.dataKeyDetected = true;\r\n for (let i = 0; i < datasource.dataKeys.length; i++) {\r\n if (datasource.dataKeys[i].type != \"attribute\") {\r\n $scope.isValidParameter = false;\r\n }\r\n if (datasource.dataKeys[i].name !== settings.latKeyName && datasource.dataKeys[i].name !== settings.lngKeyName){\r\n $scope.dataKeyDetected = false;\r\n }\r\n }\r\n }\r\n }\r\n\r\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n\r\n $scope.updateAttribute = function () {\r\n $scope.isFocused = false;\r\n if ($scope.entityDetected) {\r\n var datasource = self.ctx.datasources[0];\r\n\r\n attributeService.saveEntityAttributes(\r\n datasource.entity.id,\r\n 'SERVER_SCOPE',\r\n [\r\n {\r\n key: settings.latKeyName,\r\n value: $scope.attributeUpdateFormGroup.get('currentLat').value\r\n },{\r\n key: settings.lngKeyName,\r\n value: $scope.attributeUpdateFormGroup.get('currentLng').value\r\n }\r\n ]\r\n ).subscribe(\r\n function success() {\r\n $scope.originalLat = $scope.attributeUpdateFormGroup.get('currentLat').value;\r\n $scope.originalLng = $scope.attributeUpdateFormGroup.get('currentLng').value;\r\n if (settings.showResultMessage) {\r\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n },\r\n function fail() {\r\n if (settings.showResultMessage) {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n }\r\n );\r\n }\r\n };\r\n\r\n $scope.changeFocus = function () {\r\n if ($scope.attributeUpdateFormGroup.get('currentLat').value === $scope.originalLat && $scope.attributeUpdateFormGroup.get('currentLng').value === $scope.originalLng) {\r\n $scope.isFocused = false;\r\n }\r\n };\r\n \r\n $scope.discardChange = function() {\r\n $scope.attributeUpdateFormGroup.setValue({\r\n 'currentLat': $scope.originalLat,\r\n 'currentLng': $scope.originalLng\r\n });\r\n $scope.isFocused = false;\r\n $scope.attributeUpdateFormGroup.markAsPristine();\r\n self.onDataUpdated();\r\n };\r\n \r\n $scope.disableButton = function () {\r\n return $scope.attributeUpdateFormGroup.get('currentLat').value === $scope.originalLat && $scope.attributeUpdateFormGroup.get('currentLng').value === $scope.originalLng || $scope.currentLng === null || $scope.currentLat === null;\r\n };\r\n \r\n $scope.getCoordinate = function() {\r\n if (navigator.geolocation) {\r\n navigator.geolocation.getCurrentPosition(showPosition, function (){\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.blocked-location'), \r\n 'bottom', 'left', $scope.toastTargetId);\r\n }, {\r\n enableHighAccuracy: settings.enableHighAccuracy\r\n });\r\n } else {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.no-support-geolocation'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n };\r\n \r\n function showPosition(position) {\r\n $scope.attributeUpdateFormGroup.setValue({\r\n currentLat: correctValue(position.coords.latitude),\r\n currentLng: correctValue(position.coords.longitude)\r\n });\r\n $scope.attributeUpdateFormGroup.markAsDirty();\r\n $scope.isFocused = true;\r\n }\r\n \r\n self.onResize();\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n try {\r\n if ($scope.dataKeyDetected) {\r\n if (!$scope.isFocused) {\r\n for(let i = 0; i < self.typeParameters().maxDataKeys; i++){\r\n if(self.ctx.data[i].dataKey.name === self.ctx.settings.latKeyName && $scope.attributeUpdateFormGroup.get('currentLat').pristine){\r\n $scope.originalLat = self.ctx.data[i].data[0][1];\r\n $scope.attributeUpdateFormGroup.get('currentLat').patchValue(correctValue($scope.originalLat));\r\n } else if(self.ctx.data[i].dataKey.name === self.ctx.settings.lngKeyName && $scope.attributeUpdateFormGroup.get('currentLng').pristine){\r\n $scope.originalLng = self.ctx.data[i].data[0][1];\r\n $scope.attributeUpdateFormGroup.get('currentLng').patchValue(correctValue($scope.originalLng));\r\n }\r\n }\r\n self.ctx.detectChanges();\r\n }\r\n }\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n};\r\n\r\nfunction correctValue(value) {\r\n if (typeof value !== \"number\") {\r\n return 0;\r\n }\r\n return value;\r\n}\r\n\r\nself.onResize = function() {\r\n $scope.smallWidthContainer = (self.ctx.$container && self.ctx.$container[0].offsetWidth < 320);\r\n $scope.changeAlignment = ($scope.isHorizontal && self.ctx.$container && self.ctx.$container[0].offsetWidth < 480);\r\n self.ctx.detectChanges();\r\n};\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1,\r\n maxDataKeys: 2,\r\n singleEntity: true\r\n };\r\n};\r\n\r\nself.onDestroy = function() {\r\n\r\n};", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-update-location-attribute-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update server location attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "markers_placement_google_maps", - "name": "Markers Placement - Google Maps", - "image": "data:image/png;base64,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", - "description": "Allows configuring location of the selected entities on the Google Map.", - "descriptor": { - "type": "latest", - "sizeX": 8.5, - "sizeY": 6, - "resources": [], - "templateHtml": "", - "templateCss": ".error {\n color: red;\n}\n.tb-labels {\n color: #222;\n font: 12px/1.5 \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n text-align: center;\n width: 200px;\n white-space: nowrap;\n}", - "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('google-map', false, self.ctx, null, true);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-map-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]},{\"type\":\"function\",\"name\":\"Second point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"fitMapBounds\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}

Delete\",\"markerImageSize\":34,\"gmDefaultMapType\":\"roadmap\",\"gmApiKey\":\"AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q\",\"useColorFunction\":false,\"markerImages\":[],\"useMarkerImageFunction\":false,\"colorFunction\":\"\\n\",\"color\":\"#fe7569\",\"showTooltip\":true,\"autocloseTooltip\":true,\"defaultCenterPosition\":\"0,0\",\"showTooltipAction\":\"click\",\"polygonKeyName\":\"coordinates\",\"polygonOpacity\":0.5,\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"zoomOnClick\":true,\"defaultZoomLevel\":5,\"provider\":\"google-map\",\"showCoverageOnHover\":true,\"animate\":true,\"maxClusterRadius\":80,\"removeOutsideVisibleBounds\":true,\"mapProvider\":\"HERE.normalDay\",\"draggableMarker\":true,\"editablePolygon\":true,\"mapPageSize\":16384,\"showPolygon\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${coordinates|ts:7}

Delete\",\"showPolygonTooltip\":false},\"title\":\"Markers Placement - Google Maps\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"tooltipAction\":[{\"name\":\"delete\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id;\\n });\\n\\nwidgetContext.map.setMarkerLocation(entityDatasource[0], null, null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"8d3c0156-0a14-7a6f-0ddd-0ec16b9ffc91\"},{\"name\":\"delete_polygon\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id\\n });\\n\\nwidgetContext.map.savePolygonLocation(entityDatasource[0], null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"46bf69cd-8906-234c-a879-e2e4c92f5b67\"}]},\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"displayTimewindow\":true}" - } - }, - { - "alias": "update_shared_location_attribute", - "name": "Update shared location attribute", - "image": "data:image/png;base64,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", - "description": "Simple form to input new location for pre-defined shared attribute key.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? latLabel : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n \n \n {{ settings.showLabel ? lngLabel : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n \n
\n
\n \n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-coordinate-specified' | translate }}\n
\n
\n
\n
", - "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex-direction: column;\n flex: 1;\n}\n\n.grid__element.horizontal-alignment {\n flex-direction: row;\n}\n\n.grid__element:last-child {\n align-items: center;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n margin: 0;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-button.getLocation {\n margin-right: 10px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.attribute-update-form mat-form-field{\n width: 100%;\n padding-right: 5px;\n}\n\n.attribute-update-form.small-width mat-form-field{\n width: 150px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", - "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet utils;\r\nlet translate;\r\n\r\nself.onInit = function() {\r\n self.ctx.ngZone.run(function() {\r\n init(); \r\n self.ctx.detectChanges(true);\r\n });\r\n};\r\n\r\n\r\nfunction init() {\r\n $scope = self.ctx.$scope;\r\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n $scope.toastTargetId = 'input-widget' + utils.guid();\r\n settings = utils.deepClone(self.ctx.settings) || {};\r\n \r\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\r\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\r\n settings.showGetLocation = utils.defaultValue(settings.showGetLocation, true);\r\n settings.enableHighAccuracy = utils.defaultValue(settings.enableHighAccuracy, false);\r\n settings.isLatRequired = utils.defaultValue(settings.isLatRequired, true);\r\n settings.isLngRequired = utils.defaultValue(settings.isLngRequired, true);\r\n $scope.settings = settings;\r\n $scope.isValidParameter = true;\r\n $scope.dataKeyDetected = false; \r\n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\r\n\r\n $scope.isHorizontal = (settings.inputFieldsAlignment === 'row');\r\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-coordinate-required');\r\n $scope.latLabel = utils.customTranslation(settings.latLabel, settings.latLabel) || translate.instant('widgets.input-widgets.latitude');\r\n $scope.lngLabel = utils.customTranslation(settings.lngLabel, settings.lngLabel) || translate.instant('widgets.input-widgets.longitude');\r\n\r\n var validatorsLat = [$scope.validators.min(-90), $scope.validators.max(90)];\r\n var validatorsLng = [$scope.validators.min(-180), $scope.validators.max(180)];\r\n \r\n if (settings.isLatRequired) {\r\n validatorsLat.push($scope.validators.required);\r\n }\r\n \r\n if (settings.isLngRequired) {\r\n validatorsLng.push($scope.validators.required);\r\n }\r\n\r\n $scope.attributeUpdateFormGroup = $scope.fb.group({\r\n currentLat: [undefined, validatorsLat],\r\n currentLng: [undefined, validatorsLng],\r\n });\r\n\r\n if (self.ctx.datasources && self.ctx.datasources.length) {\r\n var datasource = self.ctx.datasources[0];\r\n if (datasource.type === 'entity') {\r\n if (datasource.entityType === 'DEVICE') {\r\n if (datasource.entityType && datasource.entityId) {\r\n $scope.entityName = datasource.entityName;\r\n if (settings.widgetTitle && settings.widgetTitle.length) {\r\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n } else {\r\n $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n }\r\n \r\n $scope.entityDetected = true;\r\n }\r\n } else {\r\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\r\n }\r\n }\r\n if (datasource.dataKeys.length > 1) {\r\n $scope.dataKeyDetected = true;\r\n for (let i = 0; i < datasource.dataKeys.length; i++) {\r\n if (datasource.dataKeys[i].type != \"attribute\"){\r\n $scope.isValidParameter = false;\r\n }\r\n if (datasource.dataKeys[i].name !== settings.latKeyName && datasource.dataKeys[i].name !== settings.lngKeyName){\r\n $scope.dataKeyDetected = false;\r\n }\r\n }\r\n }\r\n }\r\n\r\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n\r\n $scope.updateAttribute = function () {\r\n $scope.isFocused = false;\r\n if ($scope.entityDetected) {\r\n var datasource = self.ctx.datasources[0];\r\n\r\n attributeService.saveEntityAttributes(\r\n datasource.entity.id,\r\n 'SHARED_SCOPE',\r\n [\r\n {\r\n key: settings.latKeyName,\r\n value: $scope.attributeUpdateFormGroup.get('currentLat').value\r\n },{\r\n key: settings.lngKeyName,\r\n value: $scope.attributeUpdateFormGroup.get('currentLng').value\r\n }\r\n ]\r\n ).subscribe(\r\n function success() {\r\n $scope.originalLat = $scope.attributeUpdateFormGroup.get('currentLat').value;\r\n $scope.originalLng = $scope.attributeUpdateFormGroup.get('currentLng').value;\r\n if (settings.showResultMessage) {\r\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n },\r\n function fail() {\r\n if (settings.showResultMessage) {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n }\r\n );\r\n }\r\n };\r\n\r\n $scope.changeFocus = function () {\r\n if ($scope.attributeUpdateFormGroup.get('currentLat').value === $scope.originalLat && $scope.attributeUpdateFormGroup.get('currentLng').value === $scope.originalLng) {\r\n $scope.isFocused = false;\r\n }\r\n };\r\n \r\n $scope.discardChange = function() {\r\n $scope.attributeUpdateFormGroup.setValue({\r\n 'currentLat': $scope.originalLat,\r\n 'currentLng': $scope.originalLng\r\n });\r\n $scope.isFocused = false;\r\n $scope.attributeUpdateFormGroup.markAsPristine();\r\n self.onDataUpdated();\r\n };\r\n \r\n $scope.disableButton = function () {\r\n return $scope.attributeUpdateFormGroup.get('currentLat').value === $scope.originalLat && $scope.attributeUpdateFormGroup.get('currentLng').value === $scope.originalLng || $scope.currentLng === null || $scope.currentLat === null;\r\n };\r\n \r\n $scope.getCoordinate = function() {\r\n if (navigator.geolocation) {\r\n navigator.geolocation.getCurrentPosition(showPosition, function (){\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.blocked-location'), \r\n 'bottom', 'left', $scope.toastTargetId);\r\n }, {\r\n enableHighAccuracy: settings.enableHighAccuracy\r\n });\r\n } else {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.no-support-geolocation'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n };\r\n \r\n function showPosition(position) {\r\n $scope.attributeUpdateFormGroup.setValue({\r\n currentLat: correctValue(position.coords.latitude),\r\n currentLng: correctValue(position.coords.longitude)\r\n });\r\n $scope.attributeUpdateFormGroup.markAsDirty();\r\n $scope.isFocused = true;\r\n }\r\n \r\n self.onResize();\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n try {\r\n if ($scope.dataKeyDetected) {\r\n if (!$scope.isFocused) {\r\n for(let i = 0; i < self.typeParameters().maxDataKeys; i++){\r\n if(self.ctx.data[i].dataKey.name === self.ctx.settings.latKeyName && $scope.attributeUpdateFormGroup.get('currentLat').pristine){\r\n $scope.originalLat = self.ctx.data[i].data[0][1];\r\n $scope.attributeUpdateFormGroup.get('currentLat').patchValue(correctValue($scope.originalLat));\r\n } else if(self.ctx.data[i].dataKey.name === self.ctx.settings.lngKeyName && $scope.attributeUpdateFormGroup.get('currentLng').pristine){\r\n $scope.originalLng = self.ctx.data[i].data[0][1];\r\n $scope.attributeUpdateFormGroup.get('currentLng').patchValue(correctValue($scope.originalLng));\r\n }\r\n }\r\n self.ctx.detectChanges();\r\n }\r\n }\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n};\r\n\r\nfunction correctValue(value) {\r\n if (typeof value !== \"number\") {\r\n return 0;\r\n }\r\n return value;\r\n}\r\n\r\nself.onResize = function() {\r\n $scope.smallWidthContainer = (self.ctx.$container && self.ctx.$container[0].offsetWidth < 320);\r\n $scope.changeAlignment = ($scope.isHorizontal && self.ctx.$container && self.ctx.$container[0].offsetWidth < 480);\r\n self.ctx.detectChanges();\r\n};\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1,\r\n maxDataKeys: 2,\r\n singleEntity: true\r\n };\r\n};\r\n\r\nself.onDestroy = function() {\r\n\r\n};", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-update-location-attribute-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update shared location attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "web_camera_input", - "name": "Photo camera input", - "image": "data:image/png;base64,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", - "description": "Simple form to take webcamera image or upload photo.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "\n", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.photoCameraInputWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-photo-camera-input-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Photo camera input\",\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "update_json_attribute", - "name": "Update JSON attribute", - "image": "data:image/png;base64,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", - "description": "Simple form to input new JSON value for pre-defined attribute/timeseries key.", - "descriptor": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "resources": [], - "templateHtml": "\n", - "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.jsonInputWidget.onDataUpdated();\n}\n\nself.onResize = function() {\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "{}", - "settingsDirective": "tb-update-json-attribute-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"attributeScope\":\"SERVER_SCOPE\",\"showLabel\":true,\"attributeRequired\":true,\"showResultMessage\":true},\"title\":\"Update JSON attribute\",\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}" - } - } + "widgetTypeFqns": [ + "input_widgets.update_multiple_attributes", + "input_widgets.markers_placement_openstreetmap", + "input_widgets.markers_placement_image_map", + "input_widgets.markers_placement_google_maps", + "input_widgets.device_claiming_widget", + "input_widgets.web_camera_input", + "input_widgets.update_server_image_attribute", + "input_widgets.update_shared_image_attribute", + "input_widgets.update_server_location_attribute", + "input_widgets.update_shared_location_attribute", + "input_widgets.update_location_timeseries", + "input_widgets.update_integer_timeseries", + "input_widgets.update_double_timeseries", + "input_widgets.update_boolean_timeseries", + "input_widgets.update_string_timeseries", + "input_widgets.update_server_date_attribute", + "input_widgets.update_server_boolean_attribute", + "input_widgets.update_server_double_attribute", + "input_widgets.update_shared_date_attribute", + "input_widgets.update_shared_boolean_attribute", + "input_widgets.update_server_integer_attribute", + "input_widgets.update_server_string_attribute", + "input_widgets.update_shared_double_attribute", + "input_widgets.update_shared_integer_attribute", + "input_widgets.update_shared_string_attribute", + "input_widgets.update_json_attribute" ] } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/liquid_level_tanks.json b/application/src/main/data/json/system/widget_bundles/liquid_level_tanks.json new file mode 100644 index 0000000000..61f7e234ec --- /dev/null +++ b/application/src/main/data/json/system/widget_bundles/liquid_level_tanks.json @@ -0,0 +1,23 @@ +{ + "widgetsBundle": { + "alias": "liquid_level_tanks", + "title": "Liquid level", + "image": "data:image/png;base64,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", + "description": "Visualize the level of liquid inside the tank. Supports various tank shapes.", + "order": 12000, + "externalId": null, + "name": "Liquid level" + }, + "widgetTypeFqns": [ + "vertical_cylinder_tank", + "rectangle_tank", + "horizontal_cylinder_tank", + "horizontal_ellipse_tank", + "horizontal_oval_tank", + "vertical_oval_tank", + "horizontal_capsule_tank", + "vertical_capsule_tank", + "horizontal_2_1_elliptical_tank", + "horizontal_dish_ends_tank" + ] +} diff --git a/application/src/main/data/json/system/widget_bundles/maps.json b/application/src/main/data/json/system/widget_bundles/maps.json index 21d3b97888..86754f0bd4 100644 --- a/application/src/main/data/json/system/widget_bundles/maps.json +++ b/application/src/main/data/json/system/widget_bundles/maps.json @@ -2,180 +2,24 @@ "widgetsBundle": { "alias": "maps_v2", "title": "Maps", - "image": "data:image/png;base64,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", - "description": "Visualize latest location or trip animation of the devices or other entities on the indoor or outdoor maps." + "image": "data:image/png;base64,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", + "description": "Visualize the latest location or trip of the devices or other entities on the indoor or outdoor maps.", + "order": 6000, + "externalId": null, + "name": "Maps" }, - "widgetTypes": [ - { - "alias": "route_map_tencent_maps", - "name": "Route Map - Tencent Maps", - "image": "data:image/png;base64,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", - "description": "Trip animation on the Tencent maps. Allows to visualize location change over time. Use Trip Animation widget for advanced features.", - "descriptor": { - "type": "timeseries", - "sizeX": 8.5, - "sizeY": 6, - "resources": [], - "templateHtml": "", - "templateCss": ".error {\n color: red;\n}\n.tb-labels {\n color: #222;\n font: 12px/1.5 \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n text-align: center;\n width: 200px;\n white-space: nowrap;\n}", - "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('tencent-map', true, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.map.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true,\n ignoreDataUpdateOnIntervalTick: true,\n hasAdditionalLatestDataKeys: true\n };\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-route-map-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First route\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.5851719234007373,\"funcBody\":\"var lats = [37.7696499,\\n37.7699074,\\n37.7699536,\\n37.7697242,\\n37.7695189,\\n37.7696889,\\n37.7697153,\\n37.7701244,\\n37.7700604,\\n37.7705491,\\n37.7715705,\\n37.771752,\\n37.7707533,\\n37.769866];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lats[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.9015113051937396,\"funcBody\":\"var lons = [-122.4261215,\\n-122.4219157,\\n-122.4199623,\\n-122.4179074,\\n-122.4155876,\\n-122.4155521,\\n-122.4163203,\\n-122.4193876,\\n-122.4210496,\\n-122.422284,\\n-122.4232717,\\n-122.4235138,\\n-122.4247605,\\n-122.4258812];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lons[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.7253460349565717,\"funcBody\":\"var value = prevValue;\\nif (time % 500 < 100) {\\n value = value + Math.random() * 40 - 20;\\n if (value < 45) {\\n \\tvalue = 45;\\n } else if (value > 130) {\\n \\tvalue = 130;\\n }\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"tencent-map\",\"tmApiKey\":\"84d6d83e0e51e481e50454ccbe8986b\",\"tmDefaultMapType\":\"roadmap\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"
${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Speed: ${Speed} MPH
See advanced settings for details
\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#1976d3\",\"useColorFunction\":true,\"colorFunction\":\"var speed = dsData[dsIndex]['Speed'];\\nif (typeof speed !== undefined) {\\n var percent = (speed - 45)/85;\\n if (percent < 0.5) {\\n percent *=2*100; \\n return tinycolor.mix('green', 'yellow', percent).toHexString();\\n } else {\\n percent = (percent - 0.5)*2*100;\\n return tinycolor.mix('yellow', 'red', percent).toHexString();\\n }\\n}\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var speed = dsData[dsIndex]['Speed'];\\nvar res = {\\n url: images[0],\\n size: 55\\n};\\nif (typeof speed !== undefined) {\\n var percent = (speed - 45)/85;\\n var index = Math.min(2, Math.floor(3 * percent));\\n res.url = images[index];\\n}\\nreturn res;\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"strokeWeight\":4,\"strokeOpacity\":0.65},\"title\":\"Route Map - Tencent Maps\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" - } - }, - { - "alias": "test", - "name": "Trip Animation", - "image": "data:image/png;base64,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", - "description": "Trip animation on the OpenStreetMap or other map providers. Allows to visualize location and other timeseries data for each point in time.", - "descriptor": { - "type": "timeseries", - "sizeX": 10, - "sizeY": 6.5, - "resources": [], - "templateHtml": "", - "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", - "controllerScript": "self.onInit = function() {\n var $scope = self.ctx.$scope;\n $scope.self = self;\n}\n\nself.actionSources = function() {\n return {\n 'tooltipAction': {\n name: 'widget-action.tooltip-tag-action',\n multiple: false\n }\n }\n};\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true,\n ignoreDataUpdateOnIntervalTick: true,\n hasAdditionalLatestDataKeys: true\n };\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-trip-animation-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var gpsData = [\\n37.771210000, -122.510960000,\\n 37.771990000, -122.497070000,\\n 37.772730000, -122.480740000,\\n 37.773360000, -122.466870000,\\n 37.774270000, -122.458520000,\\n 37.771980000, -122.454110000,\\n 37.768250000, -122.453380000,\\n 37.765920000, -122.456810000,\\n 37.765930000, -122.467680000,\\n 37.765500000, -122.477180000,\\n 37.765300000, -122.481660000,\\n 37.764780000, -122.493350000,\\n 37.764120000, -122.508360000,\\n 37.766410000, -122.510260000,\\n 37.770010000, -122.510830000,\\n 37.770980000, -122.510930000\\n];\\n let value = gpsData.indexOf(prevValue); \\nreturn gpsData[(value == -1 ? 0 : (value + 2) % gpsData.length)];\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var gpsData = [\\n37.771210000, -122.510960000,\\n 37.771990000, -122.497070000,\\n 37.772730000, -122.480740000,\\n 37.773360000, -122.466870000,\\n 37.774270000, -122.458520000,\\n 37.771980000, -122.454110000,\\n 37.768250000, -122.453380000,\\n 37.765920000, -122.456810000,\\n 37.765930000, -122.467680000,\\n 37.765500000, -122.477180000,\\n 37.765300000, -122.481660000,\\n 37.764780000, -122.493350000,\\n 37.764120000, -122.508360000,\\n 37.766410000, -122.510260000,\\n 37.770010000, -122.510830000,\\n 37.770980000, -122.510930000\\n];\\n let value = gpsData.indexOf(prevValue); \\nreturn gpsData[(value == -1 ? 1 : (value + 2) % gpsData.length)];\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"history\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":500}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"mapProvider\":\"OpenStreetMap.Mapnik\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"showTooltip\":true,\"tooltipColor\":\"#fff\",\"tooltipFontColor\":\"#000\",\"tooltipOpacity\":1,\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
End Time: ${maxTime}
Start Time: ${minTime}\",\"strokeWeight\":2,\"strokeOpacity\":1,\"pointSize\":10,\"markerImageSize\":34,\"rotationAngle\":180,\"provider\":\"openstreet-map\",\"normalizationStep\":1000,\"decoratorSymbol\":\"arrowHead\",\"decoratorSymbolSize\":10,\"decoratorCustomColor\":\"#000\",\"decoratorOffset\":\"20px\",\"endDecoratorOffset\":\"20px\",\"decoratorRepeat\":\"20px\",\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonOpacity\":0.5,\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"pointTooltipOnRightPanel\":true,\"autocloseTooltip\":true,\"useCustomProvider\":false,\"useLabelFunction\":false,\"useTooltipFunction\":false,\"useMarkerImageFunction\":false,\"useColorFunction\":false,\"usePolylineDecorator\":false,\"useDecoratorCustomColor\":false,\"showPoints\":false,\"showPolygon\":false},\"title\":\"Trip Animation\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":false,\"showLegend\":false,\"actions\":{},\"legendConfig\":{\"position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false},\"displayTimewindow\":true}" - } - }, - { - "alias": "route_map", - "name": "Route Map", - "image": "data:image/png;base64,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", - "description": "Trip animation on the Google maps. Allows to visualize location change over time. Use Trip Animation widget for advanced features.", - "descriptor": { - "type": "timeseries", - "sizeX": 8.5, - "sizeY": 6, - "resources": [], - "templateHtml": "", - "templateCss": ".error {\n color: red;\n}\n.tb-labels {\n color: #222;\n font: 12px/1.5 \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n text-align: center;\n width: 200px;\n white-space: nowrap;\n}", - "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('google-map', true, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.map.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true,\n ignoreDataUpdateOnIntervalTick: true,\n hasAdditionalLatestDataKeys: true\n };\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-route-map-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First route\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.5851719234007373,\"funcBody\":\"var lats = [37.7696499,\\n37.7699074,\\n37.7699536,\\n37.7697242,\\n37.7695189,\\n37.7696889,\\n37.7697153,\\n37.7701244,\\n37.7700604,\\n37.7705491,\\n37.7715705,\\n37.771752,\\n37.7707533,\\n37.769866];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lats[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.9015113051937396,\"funcBody\":\"var lons = [-122.4261215,\\n-122.4219157,\\n-122.4199623,\\n-122.4179074,\\n-122.4155876,\\n-122.4155521,\\n-122.4163203,\\n-122.4193876,\\n-122.4210496,\\n-122.422284,\\n-122.4232717,\\n-122.4235138,\\n-122.4247605,\\n-122.4258812];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lons[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.7253460349565717,\"funcBody\":\"var value = prevValue;\\nif (time % 500 < 100) {\\n value = value + Math.random() * 40 - 20;\\n if (value < 45) {\\n \\tvalue = 45;\\n } else if (value > 130) {\\n \\tvalue = 130;\\n }\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"google-map\",\"gmApiKey\":\"AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q\",\"gmDefaultMapType\":\"roadmap\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Speed: ${Speed} MPH
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#1976d2\",\"useColorFunction\":true,\"colorFunction\":\"var speed = dsData[dsIndex]['Speed'];\\nif (typeof speed !== undefined) {\\n var percent = (speed - 45)/85;\\n if (percent < 0.5) {\\n percent *=2*100; \\n return tinycolor.mix('green', 'yellow', percent).toHexString();\\n } else {\\n percent = (percent - 0.5)*2*100;\\n return tinycolor.mix('yellow', 'red', percent).toHexString();\\n }\\n}\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var speed = dsData[dsIndex]['Speed'];\\nvar res = {\\n url: images[0],\\n size: 55\\n};\\nif (typeof speed !== undefined) {\\n var percent = (speed - 45)/85;\\n var index = Math.min(2, Math.floor(3 * percent));\\n res.url = images[index];\\n}\\nreturn res;\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"strokeWeight\":4,\"strokeOpacity\":0.65},\"title\":\"Route Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" - } - }, - { - "alias": "route_map_openstreetmap", - "name": "Route Map - OpenStreetMap", - "image": "data:image/png;base64,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", - "description": "Trip animation on the OpenStreetMap. Allows to visualize location change over time. Use Trip Animation widget for advanced features.", - "descriptor": { - "type": "timeseries", - "sizeX": 8.5, - "sizeY": 6, - "resources": [], - "templateHtml": "", - "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n", - "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('openstreet-map', true, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.map.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true,\n ignoreDataUpdateOnIntervalTick: true,\n hasAdditionalLatestDataKeys: true\n };\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-route-map-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First route\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.5851719234007373,\"funcBody\":\"var lats = [37.7696499,\\n37.7699074,\\n37.7699536,\\n37.7697242,\\n37.7695189,\\n37.7696889,\\n37.7697153,\\n37.7701244,\\n37.7700604,\\n37.7705491,\\n37.7715705,\\n37.771752,\\n37.7707533,\\n37.769866];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lats[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.9015113051937396,\"funcBody\":\"var lons = [-122.4261215,\\n-122.4219157,\\n-122.4199623,\\n-122.4179074,\\n-122.4155876,\\n-122.4155521,\\n-122.4163203,\\n-122.4193876,\\n-122.4210496,\\n-122.422284,\\n-122.4232717,\\n-122.4235138,\\n-122.4247605,\\n-122.4258812];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lons[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.7253460349565717,\"funcBody\":\"var value = prevValue;\\nif (time % 500 < 100) {\\n value = value + Math.random() * 40 - 20;\\n if (value < 45) {\\n \\tvalue = 45;\\n } else if (value > 130) {\\n \\tvalue = 130;\\n }\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"openstreet-map\",\"mapProvider\":\"OpenStreetMap.Mapnik\",\"useCustomProvider\":false,\"customProviderTileUrl\":\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Speed: ${Speed} MPH
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#1976d3\",\"useColorFunction\":true,\"colorFunction\":\"var speed = dsData[dsIndex]['Speed'];\\nif (typeof speed !== undefined) {\\n var percent = (speed - 45)/85;\\n if (percent < 0.5) {\\n percent *=2*100; \\n return tinycolor.mix('green', 'yellow', percent).toHexString();\\n } else {\\n percent = (percent - 0.5)*2*100;\\n return tinycolor.mix('yellow', 'red', percent).toHexString();\\n }\\n}\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var speed = dsData[dsIndex]['Speed'];\\nvar res = {\\n url: images[0],\\n size: 55\\n};\\nif (typeof speed !== undefined) {\\n var percent = (speed - 45)/85;\\n var index = Math.min(2, Math.floor(3 * percent));\\n res.url = images[index];\\n}\\nreturn res;\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"strokeWeight\":4,\"strokeOpacity\":0.65},\"title\":\"Route Map - OpenStreetMap\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" - } - }, - { - "alias": "tencent_maps", - "name": "Tencent Maps", - "image": "data:image/png;base64,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", - "description": "Show latest values and location of the entities on Tencent maps.", - "descriptor": { - "type": "latest", - "sizeX": 9, - "sizeY": 6, - "resources": [], - "templateHtml": "", - "templateCss": ".error {\n color: red;\n}\n.tb-labels {\n color: #222;\n font: 12px/1.5 \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n text-align: center;\n width: 200px;\n white-space: nowrap;\n}", - "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('tencent-map', false, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-map-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.24727730589425012,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.8437014651129422,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.7558240907832925,\"funcBody\":\"return \\\"colorpin\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]},{\"type\":\"function\",\"name\":\"Second Point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#607d8b\",\"settings\":{},\"_hash\":0.19266205227372524,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.7995830793603149,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.04902495467943502,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.44120841439482095,\"funcBody\":\"return \\\"thermometer\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"tencent-map\",\"tmApiKey\":\"84d6d83e0e51e481e50454ccbe8986b\",\"tmDefaultMapType\":\"roadmap\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"
${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See advanced settings for details
\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#fe7569\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'thermometer') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"coordinates\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.5,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"useClusterMarkers\":false,\"zoomOnClick\":true,\"maxClusterRadius\":80,\"animate\":true,\"spiderfyOnMaxZoom\":false,\"showCoverageOnHover\":true,\"chunkedLoading\":false,\"removeOutsideVisibleBounds\":true,\"useIconCreateFunction\":false},\"title\":\"Tencent Maps\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" - } - }, - { - "alias": "here_map", - "name": "HERE Map", - "image": "data:image/png;base64,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", - "description": "Show latest values and location of the entities on HERE maps.", - "descriptor": { - "type": "latest", - "sizeX": 9.5, - "sizeY": 6, - "resources": [], - "templateHtml": "", - "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n", - "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('here', false, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-map-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.9430343126300238,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.1784452363910778,\"funcBody\":\"return \\\"colorpin\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]},{\"type\":\"function\",\"name\":\"Second point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.773875863339494,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.405822538899673,\"funcBody\":\"return \\\"thermometer\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"here\",\"gmApiKey\":\"AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q\",\"gmDefaultMapType\":\"roadmap\",\"mapProvider\":\"HERE.normalDay\",\"useCustomProvider\":false,\"customProviderTileUrl\":\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\"mapProviderHere\":\"HERE.normalDay\",\"credentials\":{\"useV3\":true,\"apiKey\":\"kVXykxAfZ6LS4EbCTO02soFVfjA7HoBzNVVH9u7nzoE\"},\"mapImageUrl\":\"data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg==\",\"tmApiKey\":\"84d6d83e0e51e481e50454ccbe8986b\",\"tmDefaultMapType\":\"roadmap\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#fe7569\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'thermometer') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"coordinates\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.5,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"useClusterMarkers\":false,\"zoomOnClick\":true,\"maxClusterRadius\":80,\"animate\":true,\"spiderfyOnMaxZoom\":false,\"showCoverageOnHover\":true,\"chunkedLoading\":false,\"removeOutsideVisibleBounds\":true,\"useIconCreateFunction\":false},\"title\":\"HERE Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" - } - }, - { - "alias": "image_map", - "name": "Image Map", - "image": "data:image/png;base64,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", - "description": "Show latest values and location of the entities on image map. Uses configurable background image and coordintates in range from 0 to 1.", - "descriptor": { - "type": "latest", - "sizeX": 8.5, - "sizeY": 6.5, - "resources": [], - "templateHtml": "", - "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n", - "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('image-map', false, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-map-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 0.2;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || 0.3;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.9430343126300238,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.1784452363910778,\"funcBody\":\"return \\\"colorpin\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]},{\"type\":\"function\",\"name\":\"Second point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 0.6;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || 0.7;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.773875863339494,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.405822538899673,\"funcBody\":\"return \\\"thermometer\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"image-map\",\"mapImageUrl\":\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiCiAgIHhtbG5zOmlua3NjYXBlPSJodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy9uYW1lc3BhY2VzL2lua3NjYXBlIgogICB3aWR0aD0iMTEzNC41MTgzIgogICBoZWlnaHQ9Ijc2Mi43ODI0MSIKICAgaWQ9InN2ZzIiCiAgIHZlcnNpb249IjEuMSIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMC40OC41IHIxMDA0MCIKICAgc29kaXBvZGk6ZG9jbmFtZT0id2ljaGl0YW1hcC1ub2xpYi5zdmciPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0IiAvPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSIwLjM1IgogICAgIGlua3NjYXBlOmN4PSI4OS45MDc4NTciCiAgICAgaW5rc2NhcGU6Y3k9IjQ1My43ODI0MSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzNjYiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iNzIxIgogICAgIGlua3NjYXBlOndpbmRvdy14PSItNCIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTQiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSIKICAgICBpbmtzY2FwZTpvYmplY3QtcGF0aHM9InRydWUiCiAgICAgaW5rc2NhcGU6c25hcC1nbG9iYWw9ImZhbHNlIgogICAgIHNob3dndWlkZXM9InRydWUiCiAgICAgaW5rc2NhcGU6Z3VpZGUtYmJveD0idHJ1ZSIKICAgICBmaXQtbWFyZ2luLXRvcD0iMCIKICAgICBmaXQtbWFyZ2luLWxlZnQ9IjAiCiAgICAgZml0LW1hcmdpbi1yaWdodD0iMCIKICAgICBmaXQtbWFyZ2luLWJvdHRvbT0iMCIgLz4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGE3Ij4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yawogICAgICAgICByZGY6YWJvdXQ9IiI+CiAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+CiAgICAgICAgPGRjOnR5cGUKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPgogICAgICAgIDxkYzp0aXRsZT48L2RjOnRpdGxlPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KICA8ZwogICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgIGlua3NjYXBlOmdyb3VwbW9kZT0ibGF5ZXIiCiAgICAgaWQ9ImxheWVyMSIKICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjcuMDcxNDI4LC0zMDcuOTAyOTkpIj4KICAgIDxwYXRoCiAgICAgICBpZD0icGF0aDM3ODciCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMzY0ZTU5O3N0cm9rZS13aWR0aDoyLjk5OTk5OTc2O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmUiCiAgICAgICBkPSJtIDkwNi4wMzMxNSw3MDYuMTMzNjcgMy40MjkyLDE3Ljc5NTUyIE0gMjguNTcxNDI4LDc2NS4wNTA2NyBjIDE1MC40MzUyMDIsNi44MzM0MiAxNDYuMzkyMzIyLC0yNi4zMzQxNSAxNjYuNDM0NTQyLC0yOS4zMjAwOSAzNi4xNDM3NSwtNS4zODQ3NiAxMTQuMjg2NzYsLTYuNTI1NCAxNDguMzI1MDgsLTguNjIzNTQgNDMuMzc4MDgsLTIuNjczODUgMTQxLjc2MjIxLC0xMS4yMzA5OSAxODguODU1NzgsLTE5LjgzNDE4IDM5LjgxMTM4LC03LjI3Mjg0IDIyMS4zNjk5MSwtMC44NjIzNSAzMTkuMDcxNDEsLTAuODYyMzUgNzAuODI3MzUsMCAxNDYuOTE4NjcsLTEuNzI0NyAyMTguMTc1ODYsLTEuNzI0NyAtMzEuNjE5NywwIDExNy44NTUyLC0yLjU4NzA3IDg2LjIzNTUsLTIuNTg3MDcgbSAtMjUuMDkwNywtNjguMTI2MDYgYyAtNTIuNzk5NiwzNC43ODQ4NCAtNjUuODk1MSw1MS43NDg2NSAtOTUuNjM5LDgxLjQ5MjU4IC0yNC45MzEzLDI0LjkzMTI3IC0xNDAuMzk2NTMsLTE5LjEzOTIgLTE3OC45Mzg3MSwzNi42NTAwNyAtMTIuMjgxNCwxNy43NzcxNSAtNDcuMDAyNTcsNDYuNTQ2NTMgLTY1LjEwNzgzLDU5LjA3MTMzIC0yMC4xMDUsMTMuOTA4MTggLTU2LjAzNjcyLDQ0Ljk1NjY0IC02Ny43Njg4NSw3My4wNzgyNyAtNC44MDE0NywxMS41MDkwMiAtMTMuMzgwNDYsMzUuOTkyOTggLTIzLjQ0OTQ5LDQ2LjA2MjAxIC0xMC40OTY5OSwxMC40OTY5OSAtMzguMzc3MzMsNi4zODU2OSAtNDQuMDIzNDUsMTcuNjQ3NjQgLTE5LjAwNTAyLDM3LjkwODEyIC0yNS40NjUzLDEwMC45MjM1MiAtNjcuNjE3ODksMTAyLjA1MTAyIG0gMTkuMjgxNTEsLTYyNC4wMTQ2NCBjIDM0LjY1OTM0LC0xLjg3MzgyIDg0LjAyNzMzLDcuMzkxMzEgMTA5LjkwMDcxLC00LjI4NTQ1IDEzLjI4MTcyLC01Ljk5NDA4IDQxLjQwNzIxLC0yLjQ2MTM1IDY2LjgyODY2LC0yLjMyMDQ2IDM1LjMyMjM4LDAuMTk1NzggNjQuMzgyNDksMC42MzQ3NyAxMDEuOTE2Nyw1LjAyMzIgMjUuMDMwMzYsMi45MjY1IDQ0LjY2MjczLDM0LjI4NzIyIDU4LjUyNjk4LDUwLjY0MzkgMTcuMDk4NzgsMjAuMTcyNjggNjIuNzYzODYsLTEuNzE0NjcgNjYuMzA1NjYsMzIuMTM0MzMgNS4xMDI3LDQ4Ljc2NTg3IC02LjMyODQsNzguNjM3MjUgNi4xNDExLDk3LjM0MTUgMTkuOTY5MiwyOS45NTM3OSA1MC40ODY0LDE3Ljg1NTc5IDQ0LjYxOTMsODMuOTcxMTkgTSA1ODkuMTAyMjcsMzA5LjcyNzE1IGMgNC42NDM0NiwyMy43MjkyMyAxNS4wNjkwNCw3Mi43NzU3NSAxOS4wNjEyOCwxMzAuNjQyODggMC44NzIwNiwxMi42NDA0OCA1LjQ0NzE4LDI0Ljk5MjUzIDQuMjIyMzEsNDUuMjc3NTcgLTIuNTE3MjEsNDEuNjg3NSAtMTUuNzE3MDYsNDMuNjc3MjcgLTE1LjA5MTIyLDYwLjM2NDg2IDEuNDMxOTUsMzguMTgyMjQgMzAuNjEzNjEsOTMuODM3MTkgMzAuNjEzNjEsMTM5LjcwMTU0IDAsMjQuMTgwOCAtMi42Njk2NCwxMTUuMzkwNDUgNy4zMzAwMSwxMzUuMzg5NzYgMC4xNTkxMSwwLjMxODIxIDEwLjA2NDc2LDM1Ljg4MzMyIDEwLjc3OTQ1LDQ5LjE1NDI0IDAuOTQzNzgsMTcuNTI0NjkgLTI0LjQ3OCwzOS40NzAwOCAtMjguMDI2NTUsNDYuNTY3MTYgLTUuNDc3NywxMC45NTUzOSAtMzYuOTczMjQsMTAuODgxOTcgLTQwLjA5OTUsMjQuMTQ1OTUgLTMuODY4ODQsMTYuNDE0NTEgLTMuODY2Myw0My43OTczNSA0LjA0NjQ3LDU5LjQ0MTI5IG0gOTcuMzM3MzQsLTY5MS4wMDk0MSBjIC01LjAxMzMyLDM1LjUxNTk1IC00My42NTkwMSwxMS4zMTY1MiAtNTguNTM4NjEsMjMuNzgxMzEgLTIxLjMzMDE5LDE3Ljg2ODUyIC02Mi40OTk2NCwzMS40MzIxMiAtNzAuMTI0MzcsMzUuMzY3MDggLTM1LjA4NzYzLDE4LjEwNzkzIC0xMTAuNDcyMTUsLTE1LjE0MTk2IC0xMjUuNjE0MSw0LjI2ODQzIC0xNS45NTA2MywyMC40NDcwMyAtMC4wNzM1LDYxLjQ2NjQ4IC05LjE0NjY2LDg0LjE0OTI0IC02LjAzNTcsMTUuMDg5MjYgLTE4Ljg3NjcsMjMuMDE3MzQgLTI3LjQzOTk3LDMyLjkyNzk4IC0xOS43NDgyOSwyMi44NTU1NSAtNjkuOTc0MjgsNjkuODI0MTkgLTg0Ljc1OTA0LDEwMC4wMDM0NiAtNy40OTc0MSwxNS4zMDQwNCAtMy4yODQyNiw0NC40MjA0MSAtMy40NzA1Myw2My4zNDI4NCAtMC4xMjc5MywxMi45OTQxNCAtMC44MTAxNSwyMy4xMDM4NSAyLjQwMzQzLDI4LjI3NjE4IDQuOTYxNTgsNy45ODU4MSAyMy43MjA1LDI4LjExMjA3IDI0LjIzODY1LDUwLjYxMTQ5IDAuMjk0MTEsMTIuNzcxNDYgMC4wMTMzLDc4LjU5MTAxIDMuMDQ4ODgsODcuNjU1NDkgMi4zMTI1Niw2LjkwNTQ2IDQuMjIwMDQsMjYuNTY0OTcgMTAuMjEzNzcsMzYuNTg2NjIgMTEuMzU0MDEsMTguOTg0MTUgNC4zODczNyw0MC4xNTY2MiAyNy44OTczLDUzLjUwNzk1IDE5LjA1MDEyLDEwLjgxODU5IDQ2Ljg3NzgxLDEyLjIxODYyIDgxLjkyNjE4LDE0LjQ2MDU0IDMzLjcwMzQ1LDIuMTU1ODkgNjEuNTEyMTcsLTEuNDMwMzUgNzYuOTIwNzcsNi4xNDExIDExLjU4NTA4LDUuNjkyNjYgOC41ODE1MSwxNy45MzM0NCAxNC4yOTU0MSwyOS4zNjEyMyA1LjY0MDQyLDExLjI4MDg1IDMxLjUwMjYzLDExLjE1NjI3IDQxLjgwNDA5LDQzLjQ1NDg3IDcuNjA1OSwyMy44NDcxIDMuMDg1OTMsNDQuMTU2OSA2LjcwNzU1LDY1Ljg4NjYiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2Nzc3NzY2Njc3Nzc3NzY2Nzc3Nzc3NjY3Nzc3Njc3NzY2Nzc3Nzc3Nzc3Nzc3Nzc3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gNDMuMjc3ODgxLDUxNy45NDY3OSBjIDAsMCAyMzAuODQ4Mjg5LC0zLjYzODA1IDI1MC4wMDg2MzksLTMuNjU4NjcgNy40ODIyMiwtMC4wMDggOC42MTk1NCw1LjE1MTk0IDE0LjAyMDksMTEuNDU4NjkgMjQuNTk2MDgsMjguNzE4OTMgOTMuOTA5NjYsMTEyLjkzNTg1IDkzLjkwOTY2LDExMi45MzU4NSIKICAgICAgIGlkPSJwYXRoMzc4OSIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzc2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDM1Ljk2MDU1NSw1NzcuNzA0OTQgYyAwLDAgMTY1LjUyNDU2NSwtMS42ODQ1NCAyNDguNzc5NTY1LC0xLjY4NDU0IDQuOTQ3NDksMCA3LjcyOTkzLC0yLjg4MzMgMTAuNTM3NzEsLTUuNzI5NzcgOS42NjEwNywtOS43OTQxNiAyNS42MzE5OSwtMjguNTg5OTUgMjUuNjMxOTksLTI4LjU4OTk1IgogICAgICAgaWQ9InBhdGgzNzkxIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOiMzMzMzNjY7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Ik0gMzguMzk5NjYzLDY0MS43MzE1NSA0MzEuNzA1OTMsNjM3LjQ2MzExIgogICAgICAgaWQ9InBhdGgzNzk1IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOiMzMzMzNjY7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Ik0gMzkuMDA5NDQyLDcwNC41Mzg1OSA1MjMuMTcyNTMsNjk3LjgzMTA0IgogICAgICAgaWQ9InBhdGgzNzk3IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gMzAzLjk1NzYyLDY4Mi41ODY2MSAxNDYuNzk1NDIsMS44MjkzMyBjIDEwLjUzNDAzLDAuMTMxMjcgMTQuMzQzNzQsLTIuNjM3MzkgMjUuNDg3MTUsLTYuMzcyOCAxMC40MTIxMiwtMy40OTAyNyAzMS40MjQxNSwtMi42OTg5NiA0MS4zODUzOCwtMi43NzM4NSBsIDQwNS41NjA3OSwtMy4wNDg5IgogICAgICAgaWQ9InBhdGgzNzk5IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3Nzc2MiIC8+CiAgICA8cGF0aAogICAgICAgaWQ9InBhdGgzODA0IgogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDQyNi4yMTc5NCwzMTQuODkwOTggYyAyLjA2NzU0LDkuMDUyNzMgMS44NDE3Nyw1MS43Mjc3NyA2LjUwNzk0LDc0LjgzNDY2IDEuNjc0NzUsOC4yOTMzNiA4LjY3NTA4LDE0LjA2NTk4IDEwLjA1NTQxLDE0Ljg1ODYyIDQuOTAxNDcsMi44MTQ2MyAxMC44MTQ3OSw4LjE0OTgyIDEzLjA0NTc5LDE2LjA4ODMxIDYuNzU3NzksMjQuMDQ1OTEgMC44Nzk3Miw2OC40NTIxMiAwLjg3OTcyLDExMC42ODkzIDAsNi4wOTc4MiAxLjY2MDEsMzAuMTQ2NiAtMi4xNTU4OCwzMy45NjI1OSAtMi41NDA4NSwyLjU0MDgzIC0wLjI4MTYzLDEyLjk5MDY5IC0zLjQzNjc1LDE2LjE0Mzc3IGwgLTkuODQ5NDQsOS44NDMxMSBjIC0xMC4zNjcxNSwxMC4zNjA0NyAtMTEuNTkwMTcsNi41MjYxNCAtMTcuNzM4NDgsMTguODIyNzYgLTMuNTY3NzIsNy4xMzU0MyA1LjQwMjM1LDIwLjY3MjEgNy4zNTQzMiwyNC41NzYwMiAxLjkzMjE0LDMuODY0MyAtMS44NDIxNiw0Ljc3NzczIC0xLjc5MjM1LDcuNDQ2MjYgMC4yNTI4NiwxMy41NDQ4MyAyLjI5NzUsMzczLjkyNzEyIDIuMjk3NSwzNzMuOTI3MTIiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjc3Nzc3Nzc3Nzc2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDM2NS4yNDAyMiw1MTkuNzc2MTIgNC4xMTU5OSw1MDIuMTUxNTgiCiAgICAgICBpZD0icGF0aDM4MDYiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSAxMTYuNTMxNjUsNTA0LjE4Njk5IDMuODgwNTksMzEwLjk2NDM2IgogICAgICAgaWQ9InBhdGgzODMxIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBpZD0icGF0aDM4ODkiCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gMzE3LjY3NzYsNTc2LjQ4NTM5IDEzMC4xODc0MiwxLjUyNDQ0IGMgNC41MTA3OSwzLjI0MTY5IDIwLjM0NDcxLDcuOTY4NTMgMjcuNzQ0ODYsNC4yNjg0NCAzLjE1NTQ2LC0xLjU3NzcyIDkuNDE5LC01LjM4ODE3IDE0LjAyNDg5LC0zLjk2MzU1IDQuMjY2OTgsMS4zMTk4MSA2LjAxNjg5LDMuMTE2MzIgMTAuMzY2MjEsMy4wNDg4OSAxMC4zMDQwMywtMC4xNTk3NSAyMC4yMTE3LDAuMzg3NDEgMzAuNDg4ODYsMC4zMDQ4OSAxNzcuODkwOCwtMS40MjgyNyAzNTYuNTkwMzUsLTIuMTMyNDcgNTM0Ljc3NDU2LC0zLjA0ODg4IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2Nzc3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gNDc1LjMwNTAxLDU4Mi44ODgwNSBjIC0zLjQ0NDE4LDExLjM1MDY2IC0yLjEwMzQzLDEyLjQzMzczIDMuNjU4NjUsMjEuMDM3MzEgMy43OTQ0NSw1LjY2NTY0IDUwLjg2MjYxLDEzLjAzODQ1IDQxLjQ2NDg1LDI3LjEzNTA5IC0xMC41MzY5NywxNS44MDU0NyAtMjIuODk3NDUsLTUuNDc3NzIgLTMzLjg0MjYzLC0xLjgyOTMzIC01LjQ1MjM2LDEuODE3NDUgLTcuMzQ5MDEsNS40NTYzMSAtMy42NTg2Niw5LjE0NjY1IDIuODA2ODMsMi44MDY4NCA0LjA0OCwxLjgwMzk2IDYuNTIwMzQsNS4xMDA0MSIKICAgICAgIGlkPSJwYXRoMzkxMCIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzc3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gNDMyLjAxMDgyLDYzNi44NTMzMyBjIDguMzE4OTksMTMuMTEwMTYgMTguODQ2MjEsMTQuNjM0NjUgMzUuNjcxOTYsMTQuNjM0NjUgMi45Mzg2NSwwIDcuODY5OTgsLTAuOTMzNzEgMTAuNjcxMTEsMCAxMS4zNTkxNywzLjc4NjM5IDI3LjE5Mzk4LDEwLjI3NTc3IDM2LjIwMTkzLDIxLjEyOTQ4IDguMjgwMDIsOS45NzY2MSAxMC4yNTI3OCwyMy44ODMwOCA3LjcwMjAyLDM3LjEwNDI0IC02LjE2OTg5LDMxLjk3OTk4IC0xNi43MTQzMSw1Ni45ODg1MyAtMTkuMDQzNTUsODYuNTY5MDUgLTEuMzQ3OTgsMTcuMTE4OCA0LjUwOTU3LDIyLjUzNTIyIDExLjA3MTQzLDMzLjkyODU3IDEwLjY3MDIzLDE4LjUyNjcyIDguNzI0NTMsMTQuMTk5NTUgOC41NzE0MywzNC4yODU3MiAtMC4xMzk2MywxOC4zMTk0NCAwLDYwLjI2Mzg1IDAsODAuNzE0MjkiCiAgICAgICBpZD0icGF0aDM5MTIiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjc3Nzc3Nzc2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDUyOC41MDgwNiw2NTguOTU3NzYgYyAtMTAuNjgxMjMsMC45MDQ1NCAtNy4xMDgwNCwtNS42MDI1NSAtMTAuODIzNTQsLTguMDc5NTYgLTQuNzg0NTQsLTMuMTg5NjkgLTEyLjIyNzA0LC0xLjI1MTA0IC0xNi43Njg4OCwtNS43OTI4OCAtMC42NjYxMiwtMC42NjYxMiAtOC44MDk2OSwtNC4xMDg3NyAtMTAuMTc0NDcsLTIuNzQzOTkgLTguMzY0NTksOC4zNjQ1OSAtMy4wNDg4OCwyMC41NTE4OCAtMy4wNDg4OCwzMy41Mzc3NCBsIDMuMDIyLDMzOS42OTc0MyIKICAgICAgIGlkPSJwYXRoMzkxNCIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzc3NjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSA1MTcuOTg5NDEsNjUxLjAzMDY1IGMgLTAuMjIxNzEsLTIuNzAxODQgMS45MDM0NiwtNS41NjIxMyAzLjM1Mzc3LC03LjAxMjQ1IDEuNzk5NDMsLTEuNzk5NDIgNi45MjI5NCwxLjAwNDE5IDguODQxNzgsLTAuOTE0NjYgMC4yODc2NSwtMC4yODc2NiAwLjg0MzI5LC0xMS4xNjQxIDAuMjI4NjYsLTEzLjU2NzUzIC0yLjA2NDgzLC04LjA3NDE2IC0yLjA1ODAxLC0yOC42NTY1OCAtMi4wNTgwMSwtMzguNzIwODYgbCAwLC03My4xNzMyNiIKICAgICAgIGlkPSJwYXRoMzkxNiIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNzY3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gNTI4LjY2MDUsNjc1LjQyMTczIC0wLjQ1NzMzLC0zMS41NTU5NiIKICAgICAgIGlkPSJwYXRoMzk3NCIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDc2Ni4zMTYyNSw1NzkuNjQ0MzEgMC40MzExOCwxMy43OTc2OCBjIDMuMTM2NDMsNC42NjkxNSAzLjAxODI0LDkuNjAwNjggMy4wMTgyNCwxNi4zODQ3NSBsIDAsMTU3LjM3OTgxIgogICAgICAgaWQ9InBhdGgzOTgyIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gMTEyMi45MDAxLDc2NS45MTMwMyBjIC0yMDIuMzA2NjksNC42OTA1IC00MDMuNzQ0MDUsLTEuMTEzODEgLTYwNS45NTQ1NCwzLjM1MzkgLTEwLjg2MzYyLDAuMjQwMDIgLTMuMzYxNDcsLTguNTg2MyAtMjguNTM2OCwtOC41ODYzIgogICAgICAgaWQ9InBhdGgzOTg0IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSA4NjAuMDA4MDUsNzM3LjA2NjUxIGMgMCwwIC05Ny40NDc1LDAuODU4MDYgLTE0Ny41Njg5MiwwLjg1ODA2IC01LjI2ODYxLDAgLTQuNTE1NDYsLTguMzI5ODYgLTcuMzAwODksLTguMzI5ODYgLTMuOTc0MzUsMCAtOC42MjkyNSwwLjAyMDEgLTEwLjUwOTQ4LDAuMDM1OSAtMi4zMzQ3NywwLjAxOTcgLTEuODEwOTQsOC4zNjU5NyAtNC4xNDU4LDguMzY2OTIgLTQ2LjE2ODk5LDAuMDE4OCAtMTY3LjQwNzY3LC0xLjMwNzk5IC0xNzUuMDUyNjMsLTEuMzA3OTkgLTQuNDI5NTUsMCAtOC41NzYyNywtNi40Mzk3MiAtMTMuMTMxOTgsLTYuNDM5NzIgLTEuMzYxMTUsMCAtNi4yMzg3MywwIC0xNC4zOTQ2NywwIgogICAgICAgaWQ9InBhdGgzOTg2IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3Nzc3Nzc2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJNIDY3NS4wMDcwMyw4MzEuMTc0MDIgNjc0LjM5NzI1LDMwOS40MDI5OSIKICAgICAgIGlkPSJwYXRoMzk4OCIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDc5OS40MDE1NywzMTMuMDYxNjUgMS4yMTk1NSw0OTUuODY2NTMiCiAgICAgICBpZD0icGF0aDM5OTAiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSA3MzYuNTk0NTIsMzEyLjQ1MTg4IC0xLjIxOTU1LDcxNi40ODgyMiIKICAgICAgIGlkPSJwYXRoMzk5MiIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDUzMC4wMzA5NCw2NDMuNDU4NTkgMzkyLjM3MTU5LC0zLjAxODI1IgogICAgICAgaWQ9InBhdGg0MDQ4IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gODU5LjQ1MDYsMzE0LjkwMTI4IDEuMjkzNTQsNTA3Ljk4MDU4IgogICAgICAgaWQ9InBhdGg0MDUwIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjAuOTk5OTk5OTRweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gOTIxLjU0MDE3LDMxMC41ODk0OSAxLjcyNDcxLDUzMS43NTIyNyIKICAgICAgIGlkPSJwYXRoNDA1MiIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDczNi4yODk2Myw0NTMuMzEwNCAxODUuNjc3MTUsLTAuMzA0ODkiCiAgICAgICBpZD0icGF0aDQxODciCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSAxMDYwLjgxMDUsNTE0Ljk2NzY3IGMgMCwwIC0zNjMuMjgxMjYsLTUuNjI2MTggLTU0NC42NTA0MiwyLjUyMTc4IC00LjE3Nzc2LDAuMTg3NjkgLTEyLjUwMDQ0LDEuMDY3MTEgLTEyLjUwMDQ0LDEuMDY3MTEgLTEuNTcwOTUsMC4xMzQxIC0yLjAwMDkzLC0yLjMyNDk1IC0yLjU5MTU1LC0zLjUwNjIzIC0wLjA5NjcsLTAuMTkzNDMgLTcuMDYwODEsLTEuOTMzNCAtNy42MjIyMSwtMS4zNzE5OSAtMi44OTMxNCwyLjg5MzE0IC03LjYzMTY3LDQuMjQ4NjkgLTEyLjE5NTU1LDQuMTE2IEwgMzY5LjIwMTcsNTE0LjUzNjUiCiAgICAgICBpZD0icGF0aDQyNjEiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjc3Nzc3NjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSAzOTkuODE1MzEsNDc5LjYxMTEyIDExLjY0MTgsNS42MDUzIGMgMi45ODQxMiwxLjQzNjc5IDYuNTI4NzgsLTAuNDc3MTIgOS45MTcwOCwtMC40MzExOCBsIDEyNy4xOTczOSwxLjcyNDcxIgogICAgICAgaWQ9InBhdGg0MjYzIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NzYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Ik0gNTE5LjI1MTUxLDUxNy4xMjM1NyA1MTguODIwMzIsMzA4LjQzMzYyIgogICAgICAgaWQ9InBhdGg0MjY1IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gNDMyLjkyNTQ5LDM4OS43MTQ5OCBjIDExLjA0NDk2LDAgMzUuNTMzMDcsMC42MTkyNyA0Mi41Nzk3OCwtMS4wMDM5NyA4LjQwNTIyLC0xLjkzNjE4IDcuMDY2LC02Ljk1Mzc4IDE0LjE5NzEyLC02Ljk1Mzc4IDcuODA5NSwwIDYuNTQyOTEsOC4wNjIzNyAyMC4xNDE3LDguMDYyMzcgMTMuOTkwNjgsMCA0NC45NzY4OSwwLjM3ODg2IDYzLjkzOTkyLDAuMzc4ODYgMTIuMDgzOTUsMCA4Mi4wMDI2NiwwLjMwNDg5IDkzLjYwMDgxLDAuMzA0ODkgOC43NjA0NywwIDEzLjE1OTcsLTIuMjg4MjcgMjEuMzQyMTksLTcuMDEyNDMgNy4xOTUxNSwtNC4xNTQxMyAyLjA1NDU5LC05LjQ5MTM3IDIwLjQyNzU0LC04Ljg0MTc3IDIzLjE0NTQsMC44MTgzMyAxMi42NDMzNCwxNC4wMjQ4NyAzMi4zMTgxOSwxNC4wMjQ4NyAyNS4zNTk1NCwwIDEzMC45OTkwMiwwIDE1MC45MTk4NSwwIDE0LjMzMjQ0LDAgLTQuMTE5MTEsLTEzLjExMDIxIDI5LjI2OTMsLTEzLjQxNTEiCiAgICAgICBpZD0icGF0aDQyNjkiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjc3Nzc3Nzc3NzYyIgLz4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjU4OC42Nzk1NyIKICAgICAgIHk9IjczNS44MDQ2MyIKICAgICAgIGlkPSJ0ZXh0NDMxMCIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDMxMiIKICAgICAgICAgeD0iNTg4LjY3OTU3IgogICAgICAgICB5PSI3MzUuODA0NjMiPkxpbmNvbG48L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjY4Ni4zOTg1IgogICAgICAgeT0iNzY1LjYyODQyIgogICAgICAgaWQ9InRleHQ0MzEwLTciCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQzMTItNiIKICAgICAgICAgeD0iNjg2LjM5ODUiCiAgICAgICAgIHk9Ijc2NS42Mjg0MiI+SGFycnk8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjcwOS44NzE4MyIKICAgICAgIHk9Ii04MDIuMzc3MzgiCiAgICAgICBpZD0idGV4dDQzMTAtNy0xIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDMxMi02LTgiCiAgICAgICAgIHg9IjcwOS44NzE4MyIKICAgICAgICAgeT0iLTgwMi4zNzczOCI+V29vZGxhd248L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjU2Mi4xMTkyNiIKICAgICAgIHk9Ii03NzEuOTY4MTQiCiAgICAgICBpZD0idGV4dDQzMTAtNy0xLTkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40MzEyLTYtOC0yIgogICAgICAgICB4PSI1NjIuMTE5MjYiCiAgICAgICAgIHk9Ii03NzEuOTY4MTQiPkVkZ2Vtb29yPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1OTguMzA0ODciCiAgICAgICB5PSItNzM4LjM2NjQ2IgogICAgICAgaWQ9InRleHQ0MzEwLTctMS05LTciCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40MzEyLTYtOC0yLTkiCiAgICAgICAgIHg9IjU5OC4zMDQ4NyIKICAgICAgICAgeT0iLTczOC4zNjY0NiI+T2xpdmVyPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1OTIuMTIyODYiCiAgICAgICB5PSItNjc3LjIwMzk4IgogICAgICAgaWQ9InRleHQ0MzEwLTctMS05LTctNSIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMCwxLC0xLDAsMCwwKSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQzMTItNi04LTItOS00IgogICAgICAgICB4PSI1OTIuMTIyODYiCiAgICAgICAgIHk9Ii02NzcuMjAzOTgiPkhpbGxzaWRlPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1OTcuMzI3MDkiCiAgICAgICB5PSItODYyLjYxNDA3IgogICAgICAgaWQ9InRleHQ0MzEwLTctMS05LTctNS0zIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDMxMi02LTgtMi05LTQtMSIKICAgICAgICAgeD0iNTk3LjMyNzA5IgogICAgICAgICB5PSItODYyLjYxNDA3Ij5Sb2NrPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1ODcuMzcwMTgiCiAgICAgICB5PSItOTI2LjEzNjYiCiAgICAgICBpZD0idGV4dDQzMTAtNy0xLTktNy01LTMtMiIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMCwxLC0xLDAsMCwwKSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQzMTItNi04LTItOS00LTEtMyIKICAgICAgICAgeD0iNTg3LjM3MDE4IgogICAgICAgICB5PSItOTI2LjEzNjYiPldlYmI8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9Ijg3MS4xNjEwMSIKICAgICAgIHk9IjYzNy41NzUyIgogICAgICAgaWQ9InRleHQ0NDY1IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40NDY3IgogICAgICAgICB4PSI4NzEuMTYxMDEiCiAgICAgICAgIHk9IjYzNy41NzUyIj5DZW50cmFsPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI4NzMuODMyMjgiCiAgICAgICB5PSI1NzcuMDMyNDciCiAgICAgICBpZD0idGV4dDQ0NjUtMyIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDQ2Ny00IgogICAgICAgICB4PSI4NzMuODMyMjgiCiAgICAgICAgIHk9IjU3Ny4wMzI0NyI+MTN0aDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgaWQ9InRleHQ0NDkwIgogICAgICAgeT0iNTEwLjI2MTgxIgogICAgICAgeD0iODc1Ljk2NjQ5IgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbgogICAgICAgICB5PSI1MTAuMjYxODEiCiAgICAgICAgIHg9Ijg3NS45NjY0OSIKICAgICAgICAgaWQ9InRzcGFuNDQ5MiIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+MjFzdDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iODgxLjMxNjU5IgogICAgICAgeT0iNDUwLjE5ODc2IgogICAgICAgaWQ9InRleHQ0NDk0IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40NDk2IgogICAgICAgICB4PSI4ODEuMzE2NTkiCiAgICAgICAgIHk9IjQ1MC4xOTg3NiI+Mjl0aDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iNjE1Ljc5MjQ4IgogICAgICAgeT0iMzg3Ljc0NzE2IgogICAgICAgaWQ9InRleHQ0NDY1LTMtMSIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDQ2Ny00LTEiCiAgICAgICAgIHg9IjYxNS43OTI0OCIKICAgICAgICAgeT0iMzg3Ljc0NzE2Ij4zN3RoPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ1MTkiCiAgICAgICB5PSI0ODEuNjUyODYiCiAgICAgICB4PSI0ODQuNjkwMzciCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9IjQ4MS42NTI4NiIKICAgICAgICAgeD0iNDg0LjY5MDM3IgogICAgICAgICBpZD0idHNwYW40NTIxIgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIj4yNXRoPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1NjMuMDQ2NzUiCiAgICAgICB5PSI1MTMuMzYxMzMiCiAgICAgICBpZD0idGV4dDQ1MjMiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQ1MjUiCiAgICAgICAgIHg9IjU2My4wNDY3NSIKICAgICAgICAgeT0iNTEzLjM2MTMzIj4yMXN0PC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ1MjciCiAgICAgICB5PSI1NzcuODk0ODQiCiAgICAgICB4PSI1NjUuOTcxNSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iNTc3Ljg5NDg0IgogICAgICAgICB4PSI1NjUuOTcxNSIKICAgICAgICAgaWQ9InRzcGFuNDUyOSIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+MTN0aDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDUzMSIKICAgICAgIHk9Ii00NjAuNzMzMTIiCiAgICAgICB4PSI0MzMuNTgwNzUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9Ii00NjAuNzMzMTIiCiAgICAgICAgIHg9IjQzMy41ODA3NSIKICAgICAgICAgaWQ9InRzcGFuNDUzMyIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+QW1pZG9uPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI0MDUuNTMwOTgiCiAgICAgICB5PSItNTIzLjU0MDE2IgogICAgICAgaWQ9InRleHQ0NTM1IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDUzNyIKICAgICAgICAgeD0iNDA1LjUzMDk4IgogICAgICAgICB5PSItNTIzLjU0MDE2Ij5BcmthbnNhczwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDUzOSIKICAgICAgIHk9Ii0zNzIuNTg1OTQiCiAgICAgICB4PSI3NDUuNDg0NjIiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9Ii0zNzIuNTg1OTQiCiAgICAgICAgIHg9Ijc0NS40ODQ2MiIKICAgICAgICAgaWQ9InRzcGFuNDU0MSIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+V2VzdDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iNTk2LjcyODMzIgogICAgICAgeT0iLTUzMS4yNTkyOCIKICAgICAgIGlkPSJ0ZXh0NDU0MyIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMCwxLC0xLDAsMCwwKSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQ1NDUiCiAgICAgICAgIHg9IjU5Ni43MjgzMyIKICAgICAgICAgeT0iLTUzMS4yNTkyOCI+V2FjbzwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDU1NSIKICAgICAgIHk9Ii0xMjIuNTAyOTUiCiAgICAgICB4PSI1OTUuNDM0ODEiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9Ii0xMjIuNTAyOTUiCiAgICAgICAgIHg9IjU5NS40MzQ4MSIKICAgICAgICAgaWQ9InRzcGFuNDU1NyIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+TWF6aWU8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjY5NS43NzI5NSIKICAgICAgIHk9IjE2Mi4wNjg3NyIKICAgICAgIGlkPSJ0ZXh0NDU1OSIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIgogICAgICAgdHJhbnNmb3JtPSJtYXRyaXgoMC43MDcxMDY3OCwwLjcwNzEwNjc4LC0wLjcwNzEwNjc4LDAuNzA3MTA2NzgsMCwwKSI+PHRzcGFuCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiCiAgICAgICAgIGlkPSJ0c3BhbjQ1NjEiCiAgICAgICAgIHg9IjY5NS43NzI5NSIKICAgICAgICAgeT0iMTYyLjA2ODc3Ij5ab288L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjI0MC41ODk5NyIKICAgICAgIHk9IjU3NC40NDU0MyIKICAgICAgIGlkPSJ0ZXh0NDU2MyIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDU2NSIKICAgICAgICAgeD0iMjQwLjU4OTk3IgogICAgICAgICB5PSI1NzQuNDQ1NDMiPjEzdGg8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDU2NyIKICAgICAgIHk9IjUxMS42MzY2MyIKICAgICAgIHg9IjIwNi4wMzE3NSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iNTExLjYzNjYzIgogICAgICAgICB4PSIyMDYuMDMxNzUiCiAgICAgICAgIGlkPSJ0c3BhbjQ1NjkiCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiPjIxc3Q8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjYyMC40NDMxMiIKICAgICAgIHk9Ii01MDYuNjgyMTkiCiAgICAgICBpZD0idGV4dDQ1NzEiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40NTczIgogICAgICAgICB4PSI2MjAuNDQzMTIiCiAgICAgICAgIHk9Ii01MDYuNjgyMTkiPk5pbXM8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDU4MyIKICAgICAgIHk9IjY5OC44NDAwOSIKICAgICAgIHg9IjM3MC4yMTY4NiIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iNjk4Ljg0MDA5IgogICAgICAgICB4PSIzNzAuMjE2ODYiCiAgICAgICAgIGlkPSJ0c3BhbjQ1ODUiCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiPk1hcGxlPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSIzODQuMDg0MiIKICAgICAgIHk9IjY4MC44NTEzOCIKICAgICAgIGlkPSJ0ZXh0NDU5OSIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDYwMSIKICAgICAgICAgeD0iMzg0LjA4NDIiCiAgICAgICAgIHk9IjY4MC44NTEzOCI+RG91Z2xhczwvdHNwYW4+PC90ZXh0PgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0ibSAzNjcuOTA4MTcsMTAwOS45NTk2IDI2My4wMTgzMywwIgogICAgICAgaWQ9InBhdGg0NjA1IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDx0ZXh0CiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ2MDciCiAgICAgICB5PSItNDMzLjEzNzc2IgogICAgICAgeD0iNzM2LjI2NzQ2IgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbgogICAgICAgICB5PSItNDMzLjEzNzc2IgogICAgICAgICB4PSI3MzYuMjY3NDYiCiAgICAgICAgIGlkPSJ0c3BhbjQ2MDkiCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiPk1lcmlkaWFuPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ5NzkiCiAgICAgICB5PSI2NDAuMjA1MjYiCiAgICAgICB4PSI1NzIuODMyMTUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuCiAgICAgICAgIHk9IjY0MC4yMDUyNiIKICAgICAgICAgeD0iNTcyLjgzMjE1IgogICAgICAgICBpZD0idHNwYW40OTgxIgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIj5DZW50cmFsPC90c3Bhbj48L3RleHQ+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI1NzUuMDg5NjYiCiAgICAgICB5PSI2NzAuOTAzNSIKICAgICAgIGlkPSJ0ZXh0NDk4MyIKICAgICAgIHNvZGlwb2RpOmxpbmVzcGFjaW5nPSIxMjUlIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDk4NSIKICAgICAgICAgeD0iNTc1LjA4OTY2IgogICAgICAgICB5PSI2NzAuOTAzNSI+RG91Z2xhczwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iNDk5LjQ4OTYyIgogICAgICAgeT0iMTAwOC42MDY5IgogICAgICAgaWQ9InRleHQ1MDQ3IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW41MDQ5IgogICAgICAgICB4PSI0OTkuNDg5NjIiCiAgICAgICAgIHk9IjEwMDguNjA2OSI+NDd0aDwvdHNwYW4+PC90ZXh0PgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iMjE2LjY0NTQzIgogICAgICAgeT0iNzI1Ljk4Mjk3IgogICAgICAgaWQ9InRleHQ1MDUxIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW41MDUzIgogICAgICAgICB4PSIyMTYuNjQ1NDMiCiAgICAgICAgIHk9IjcyNS45ODI5NyI+S2VsbG9nZzwvdHNwYW4+PC90ZXh0PgogICAgPGZsb3dSb290CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgaWQ9ImZsb3dSb290NTA1NSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6MThweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCwyODcuMzYyMTgpIj48Zmxvd1JlZ2lvbgogICAgICAgICBpZD0iZmxvd1JlZ2lvbjUwNTciPjxyZWN0CiAgICAgICAgICAgaWQ9InJlY3Q1MDU5IgogICAgICAgICAgIHdpZHRoPSIzNDMuNTcxNDQiCiAgICAgICAgICAgaGVpZ2h0PSIxMDMuNTcxNDMiCiAgICAgICAgICAgeD0iMTkuMjg1NzE1IgogICAgICAgICAgIHk9IjE3LjE0Mjg1NyIKICAgICAgICAgICBzdHlsZT0iZm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIgLz48L2Zsb3dSZWdpb24+PGZsb3dQYXJhCiAgICAgICAgIGlkPSJmbG93UGFyYTUwNjEiPjwvZmxvd1BhcmE+PC9mbG93Um9vdD4gICAgPHRleHQKICAgICAgIHRyYW5zZm9ybT0ibWF0cml4KDAsMSwtMSwwLDAsMCkiCiAgICAgICBzb2RpcG9kaTpsaW5lc3BhY2luZz0iMTI1JSIKICAgICAgIGlkPSJ0ZXh0NDYwNy03IgogICAgICAgeT0iLTUwOC4xODk3MyIKICAgICAgIHg9Ijc3NC44NzU2MSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iLTUwOC4xODk3MyIKICAgICAgICAgeD0iNzc0Ljg3NTYxIgogICAgICAgICBpZD0idHNwYW40NjA5LTciCiAgICAgICAgIHNvZGlwb2RpOnJvbGU9ImxpbmUiPk1jQ2xlYW48L3RzcGFuPjwvdGV4dD4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iY29sb3I6IzAwMDAwMDtmaWxsOm5vbmU7c3Ryb2tlOiMzMzMzNjY7c3Ryb2tlLXdpZHRoOjFweDtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7bWFya2VyOm5vbmU7dmlzaWJpbGl0eTp2aXNpYmxlO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7ZW5hYmxlLWJhY2tncm91bmQ6YWNjdW11bGF0ZSIKICAgICAgIGQ9Im0gMzY0LjE1OTk5LDY1OC40Mjg5MSAyOTkuNTEwMjMsLTEuMDEwMTYgYyA2LjQ5ODcyLC0wLjAyMTkgNi45NzcxOSw5LjI1NDEyIDE2LjU5NjMxLDkuMzkyNDcgMTIuMDU0MjcsMC4xNzMzOSAyOS4xMTA4MywtMC41MzU3MiA1NC4xMTQzNywtMC4zMDExIgogICAgICAgaWQ9InBhdGg1NDQwIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsMjg3LjM2MjE4KSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NzYyIgLz4KICAgIDx0ZXh0CiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIgogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTo5LjY1ODM3NzY1cHg7Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDtsaW5lLWhlaWdodDoxMjUlO2xldHRlci1zcGFjaW5nOjBweDt3b3JkLXNwYWNpbmc6MHB4O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtmb250LWZhbWlseTpWZXJkYW5hOy1pbmtzY2FwZS1mb250LXNwZWNpZmljYXRpb246VmVyZGFuYSIKICAgICAgIHg9IjM3My45OTMwNCIKICAgICAgIHk9Ijk0NC4zNTc1NCIKICAgICAgIGlkPSJ0ZXh0NTA0Ny05IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW41MDQ5LTMiCiAgICAgICAgIHg9IjM3My45OTMwNCIKICAgICAgICAgeT0iOTQ0LjM1NzU0Ij5NYWNBcnRodXI8L3RzcGFuPjwvdGV4dD4KICAgIDx0ZXh0CiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICBpZD0idGV4dDQ2MDctNy0xIgogICAgICAgeT0iLTQ5MC4yNDU5NyIKICAgICAgIHg9Ijc4MC44NDYwNyIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4KICAgICAgICAgeT0iLTQ5MC4yNDU5NyIKICAgICAgICAgeD0iNzgwLjg0NjA3IgogICAgICAgICBpZD0idHNwYW40NjA5LTctOSIKICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSI+U2VuZWNhPC90c3Bhbj48L3RleHQ+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImNvbG9yOiMwMDAwMDA7ZmlsbDpub25lO3N0cm9rZTojMzMzMzY2O3N0cm9rZS13aWR0aDoxcHg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2Utb3BhY2l0eToxO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBkPSJtIDM2Ny42OTU1Myw1MzcuMjEwNiAxNDEuMjgzMDMsLTEuMDEwMTUgYyA2LjQ4OTk5LC0wLjA0NjQgMTIuNzgxMTQsNy4yMzU0NSAxOS4xOTI5LDcuMzIzNiA1NS45MjM2MiwwLjc2ODkgMTU4LjY4OTk3LC0wLjE3MzMzIDIzNi41MTQwMiwtMS4wMTAxNSA3LjgzOTU2LC0wLjA4NDMgMjIuNjMxNDcsLTE5Ljg1MzU1IDMwLjMwNDU3LC0yMC40NTU1OSAyMi4yNjU4OSwtMS4zNTE4MSA0NS4xNzk0NSwtMC41MDUwNyA2Ny42ODAyMiwtMC41MDUwNyAxNi4xNDczMSwtMC42MzI0MSAzLjYxMDE2LDIwLjcwODEzIDI2Ljc2OTA0LDIwLjcwODEzIGwgMjQzLjQ0Njc5LC0xLjAxMDE2IgogICAgICAgaWQ9InBhdGg1NDk2IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsMjg3LjM2MjE4KSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY3NzY2NjY2MiIC8+CiAgICA8dGV4dAogICAgICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIKICAgICAgIHN0eWxlPSJmb250LXNpemU6OS42NTgzNzc2NXB4O2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7bGluZS1oZWlnaHQ6MTI1JTtsZXR0ZXItc3BhY2luZzowcHg7d29yZC1zcGFjaW5nOjBweDtmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmU7Zm9udC1mYW1pbHk6VmVyZGFuYTstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOlZlcmRhbmEiCiAgICAgICB4PSI2ODUuMjA4MTMiCiAgICAgICB5PSI4MjcuNTMwODIiCiAgICAgICBpZD0idGV4dDQzMTAtNy04IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiPjx0c3BhbgogICAgICAgICBzb2RpcG9kaTpyb2xlPSJsaW5lIgogICAgICAgICBpZD0idHNwYW40MzEyLTYtNiIKICAgICAgICAgeD0iNjg1LjIwODEzIgogICAgICAgICB5PSI4MjcuNTMwODIiPlBhd25lZTwvdHNwYW4+PC90ZXh0PgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJjb2xvcjojMDAwMDAwO2ZpbGw6bm9uZTtzdHJva2U6IzMzMzM2NjtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MDttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSA1NTQuMjg1NzIsNzIxLjQyODU3IDU1MCw1NDMuMjE0MjkgNTQ3LjE0Mjg2LDEwMi41IDU0Ni43ODU3MiwyMy4yMTQyODUiCiAgICAgICBpZD0icGF0aDU1MTkiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCwyODcuMzYyMTgpIiAvPgogICAgPHRleHQKICAgICAgIHhtbDpzcGFjZT0icHJlc2VydmUiCiAgICAgICBzdHlsZT0iZm9udC1zaXplOjkuNjU4Mzc3NjVweDtmb250LXN0eWxlOm5vcm1hbDtmb250LXZhcmlhbnQ6bm9ybWFsO2ZvbnQtd2VpZ2h0Om5vcm1hbDtmb250LXN0cmV0Y2g6bm9ybWFsO2xpbmUtaGVpZ2h0OjEyNSU7bGV0dGVyLXNwYWNpbmc6MHB4O3dvcmQtc3BhY2luZzowcHg7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO2ZvbnQtZmFtaWx5OlZlcmRhbmE7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpWZXJkYW5hIgogICAgICAgeD0iNTI5LjYyNTMxIgogICAgICAgeT0iLTU1MC44NDc3OCIKICAgICAgIGlkPSJ0ZXh0NDU0My01IgogICAgICAgc29kaXBvZGk6bGluZXNwYWNpbmc9IjEyNSUiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLDEsLTEsMCwwLDApIj48dHNwYW4KICAgICAgICAgc29kaXBvZGk6cm9sZT0ibGluZSIKICAgICAgICAgaWQ9InRzcGFuNDU0NS0wIgogICAgICAgICB4PSI1MjkuNjI1MzEiCiAgICAgICAgIHk9Ii01NTAuODQ3NzgiPkJyb2Fkd2F5PC90c3Bhbj48L3RleHQ+CiAgPC9nPgo8L3N2Zz4K\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}

X Pos: ${xPos:2}
Y Pos: ${yPos:2}
Temperature: ${temperature} °C
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#fe7569\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'thermometer') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false},\"title\":\"Image Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" - } - }, - { - "alias": "google_maps", - "name": "Google Maps", - "image": "data:image/png;base64,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", - "description": "Show latest values and location of the entities on Google Maps.", - "descriptor": { - "type": "latest", - "sizeX": 8.5, - "sizeY": 6, - "resources": [], - "templateHtml": "", - "templateCss": ".error {\n color: red;\n}\n.tb-labels {\n color: #222;\n font: 12px/1.5 \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n text-align: center;\n width: 200px;\n white-space: nowrap;\n}", - "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('google-map', false, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-map-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.9430343126300238,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.1784452363910778,\"funcBody\":\"return \\\"colorpin\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]},{\"type\":\"function\",\"name\":\"Second point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.773875863339494,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.405822538899673,\"funcBody\":\"return \\\"thermometer\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"google-map\",\"gmApiKey\":\"AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q\",\"gmDefaultMapType\":\"roadmap\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#fe7568\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'thermometer') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"useClusterMarkers\":false,\"zoomOnClick\":true,\"maxClusterRadius\":80,\"animate\":true,\"spiderfyOnMaxZoom\":false,\"showCoverageOnHover\":true,\"chunkedLoading\":false,\"removeOutsideVisibleBounds\":true,\"useIconCreateFunction\":false},\"title\":\"Google Maps\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" - } - }, - { - "alias": "openstreetmap", - "name": "OpenStreetMap", - "image": "data:image/png;base64,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", - "description": "Show latest values and location of the entities on OpenStreetMap.", - "descriptor": { - "type": "latest", - "sizeX": 8.5, - "sizeY": 6, - "resources": [], - "templateHtml": "", - "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n", - "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('openstreet-map', false, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", - "settingsSchema": "", - "dataKeySettingsSchema": "", - "settingsDirective": "tb-map-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.9430343126300238,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.1784452363910778,\"funcBody\":\"return \\\"colorpin\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]},{\"type\":\"function\",\"name\":\"Second point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.773875863339494,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.405822538899673,\"funcBody\":\"return \\\"thermometer\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"openstreet-map\",\"mapProvider\":\"OpenStreetMap.Mapnik\",\"useCustomProvider\":false,\"customProviderTileUrl\":\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#fe7569\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'thermometer') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"useClusterMarkers\":false,\"zoomOnClick\":true,\"maxClusterRadius\":80,\"animate\":true,\"spiderfyOnMaxZoom\":false,\"showCoverageOnHover\":true,\"chunkedLoading\":false,\"removeOutsideVisibleBounds\":true,\"useIconCreateFunction\":false},\"title\":\"OpenStreetMap\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" - } - } + "widgetTypeFqns": [ + "maps_v2.openstreetmap", + "maps_v2.google_maps", + "maps_v2.image_map", + "maps_v2.here_map", + "maps_v2.tencent_maps", + "maps_v2.test", + "maps_v2.route_map_openstreetmap", + "maps_v2.route_map", + "maps_v2.route_map_tencent_maps", + "input_widgets.markers_placement_openstreetmap", + "input_widgets.markers_placement_google_maps", + "input_widgets.markers_placement_image_map" ] -} +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/navigation_widgets.json b/application/src/main/data/json/system/widget_bundles/navigation_widgets.json index 9d06c0122a..305cded236 100644 --- a/application/src/main/data/json/system/widget_bundles/navigation_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/navigation_widgets.json @@ -3,46 +3,16 @@ "alias": "navigation_widgets", "title": "Navigation widgets", "image": "data:image/png;base64,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", - "description": "Useful to define home dashboard of the user. Contains widgets that enable navigation to other dashboards and menu items." + "description": "Contains widgets that enable navigation to other dashboards and menu items. Useful to define the home page or dashboard.", + "order": 19000, + "externalId": null, + "name": "Navigation widgets" }, - "widgetTypes": [ - { - "alias": "navigation_cards", - "name": "Navigation cards", - "image": "data:image/png;base64,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", - "description": "Allows to open one or more specific pages using filter defined in the advanced settings of the widget.", - "descriptor": { - "type": "static", - "sizeX": 7, - "sizeY": 6, - "resources": [], - "templateHtml": "", - "templateCss": "/*#widget-container {\n overflow-y: auto;\n box-sizing: content-box !important;\n cursor: auto;\n}*/\n\n#widget-container #container {\n overflow-y: auto;\n box-sizing: content-box;\n cursor: auto;\n}", - "controllerScript": "self.onInit = function() {\n self.ctx.$scope.navigationCardsWidget.resize();\n}\n\nself.onResize = function() {\n self.ctx.$scope.navigationCardsWidget.resize();\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-navigation-cards-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(255,255,255,0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"filterType\":\"all\"},\"title\":\"Navigation cards\",\"dropShadow\":false,\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}" - } - }, - { - "alias": "navigation_card", - "name": "Navigation card", - "image": "data:image/png;base64,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", - "description": "Allows to open specific page using relative path defined in the advanced settings of the widget.", - "descriptor": { - "type": "static", - "sizeX": 2.5, - "sizeY": 2, - "resources": [], - "templateHtml": "", - "templateCss": "", - "controllerScript": "self.onInit = function() {\n\n}\n\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "", - "dataKeySettingsSchema": "{}\n", - "settingsDirective": "tb-navigation-card-widget-settings", - "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(255,255,255,0)\",\"color\":\"rgba(255,255,255,0.87)\",\"padding\":\"8px\",\"settings\":{\"name\":\"{i18n:device.devices}\",\"icon\":\"devices_other\",\"path\":\"/devices\"},\"title\":\"Navigation card\",\"dropShadow\":false,\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}" - } - } + "widgetTypeFqns": [ + "navigation_widgets.navigation_cards", + "navigation_widgets.navigation_card", + "home_page_widgets.quick_links", + "home_page_widgets.documentation_links", + "home_page_widgets.dashboards" ] } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/outdoor_environment.json b/application/src/main/data/json/system/widget_bundles/outdoor_environment.json new file mode 100644 index 0000000000..c7705c5e91 --- /dev/null +++ b/application/src/main/data/json/system/widget_bundles/outdoor_environment.json @@ -0,0 +1,190 @@ +{ + "widgetsBundle": { + "alias": "outdoor_environment", + "title": "Outdoor Environment", + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMkAAAChCAMAAABav2sAAAABa1BMVEUAAAAAAADf39/v7+/19fXw8PD9/f3y8vLAwMAAAAD5+fn39/f////39/fz8/MjTMf5+fnj4+OQkJDHx8esrKzb29t0dHTn5+f29vbx8fH9/f3f8Mqv2XuCgoKAwyzV1dVYWFjU1NTk6fju7u65ubnIycjh4eH4+fjOzs78/PxmZmb7+/s+Ys7/pgDy9PvI0vGenp4wV8utvOrCwsL/vEDg4OCRpeP/9N/c3Nw/Ys7l6vrv9+V1j9yxwfCDmt+dnZ3W3fW6x+6su+rI5aP/zXD3+/Kcr+n/6b+7u7vP6K+/4ZVaedX/78/P6a9KSkqQykb/sSD/qxDy9f3L1vX/+u9mg91aeNVMbdH/5K//x2A8PDzY4Pekt+6RpuP/0oCg0mG+zPPp6en/2JCYzlNLS0v/+e/r6+umpqb/tzDs8OuLoul3k+Xn9NjX7L3/3aDA4Za33Yio1m7/wk8vLy8gICDn9NfY7L3/15CIxzpPyZjhAAAADHRSTlMACyC+76/x7yoI78V/Zk47AAAKpUlEQVR42uybW2/TMBiGJxjazTsnduKYXERpjkSl564dSNXYEGKTNqFN4jCQQAKBgBsuAIm/z+ekXelYUena0Y4+7WLHdWc/sj8n9bq1tRs3N9jccG/dWCN0K+ubc+N23sqNDcyVjRu5yDrmyjq1cgtz5iaJXEkrLubMhja5jTmzvrbGMGeYNtnEnNlcmaxMhszbpFavXQ+TsGJUzlRsIRq4EPcSJjEIN4bvYwCnMtsXcGdostesGw/Rx33Q4K7NuRX7sWtZMbjNfYtOURa+y3kcg55/aSK+ewC4A9u2XHrYvm7BtR1q5ZVPBTMyaVXC+2cm1BhKcclyvLLXcOwSL/ll4fkNz3ZsYdl2KbYbnvV3Ju6TxjcX4K+cku1wh7+yXlllSzhkUuLveTm2Z2ZSb46YvBLUZ0tQW1zY70VDWLysPXITOJ7w/9Kk5DxxizHJTUqghyXKEJbOemU+M5Pa6JgIZ2DilVxPkINVKjXsslUuk4nlee7fmdBbLPRNHjhPBiaUJZMnFh1mZdLs0ZhchMOvYu0S9qxMuoZhdHERPq7CxMaMTEJDs3sNric7hmYHBQHAwEwJ0wQ9MjCmKMsyeuZlxLQmWUbvZ/oXMLNDB53RWRWh31xiElOb9EZMMkQH6YFqI1MySZK2OpBZckDlZpKgE8s0SLIpTZiChEwClcgslSaJZR3VyWTQSSWC1E8ZAtVW6dQmdUPTQkEi/SRIZAKZybafHARJmrWz3KSNpC39IEumNEEQKCWlNFMmlWSgrEolQefoZCqgIr+t2OwjnqkLyny2uHEChNflDlJzJSYMEQAzz+mMDvflNDlAm6UskhGylJmRUmmimGJpunQm7SRQFPRKQke8lKpDP5TKpTNRqYrSiEAqTakUIy9mqiBZOpMxqOi6mCxhxAMrk5XJyuS/N9nfOv708ilwtKV5tLwmX6rHj15Wt4DT6qflNtmqbheHd9V9/BnXQ2yBuwtq8pRE8LV6hDdkM5Y77A58V8BxYD3gi2miXfb17KIpVj0ZN7k+J5/ByUR4LkQ8ucluq1VHQa3V2qvhjHor7JfvtVq7MzGhwah+2gYeVR+9O66+wYUk5QSAC9fVyaQm4aFBVPZAvB5mi9OmUZQ38xfCWZhs7787OX4KzYeTE1yMNU3EV4zKTrdiNGtAyzB63Rdn22E14zF2KsXW0vOdZ1QnnIEJ9HjsD+Iff8A0EdFxUhPqfQiETeo1Xhg9AIeDTaTQqONxM6/SLf7y8uzSJi+38gl2iq2XAI5PgNPTcaEfpWZgRj6b0KRnPAe0xA6NAXUdeGxUUHBY6TV3KCmqoEsDd0kTGo43R/vHJ9s0HO+2H1VJ5+jH0zEinQ6TEVI5oUktrBVz7DHqNDzFKKEg7D3vAhhsK+12u5c2+fC1Wq2enALbx5TZ2qaS0y9jZxdjACYdk+EcG5hQUsPoVvJDFMwm4o+KQTjaP+qXzPAaTyHQxS8m4XmThb6DDA3NQOQ5ltjkvgZErWK8qI2Y4BImkamYYiCuymTIi/41b7cw2TtngmZ/Wd7t9SaIeMbyTS7iyk16fRG9Cu/li+2L8xWaw3QiEz8COpga33IxpDOpyevhpfvQOMyDZgcj0HR7rZOm0fubfeHpRUTseY7NUWBOaEJzqXk/J9TTq/Ks+fv9VZfqHN6nF2tXEfHcK3wcx4Zm8ruVASFQr1D6PPy9ki5vdmuY2sTmv5WIRjxWpMDyHGvqz4zh7sXdrdXDy6zCfKRPhFW2uP+gca6WIMouhri24y/WZ0YHEB7OcD0BjRjVK7kL+Tl+8xc+fqSDLN3rnz5wZD/nvN0cIqj0Au5igcakhBwh+hMLA3gZw7xY/L0V20YBL/F8Yg2JG+jjlhZ2l0g4juNxEM4vAeLwc7UG5x5fRJOUoYA7woLtYzxuf62KxULu3KkOEA3W0bLAn/AdoXEwDvYvTfwUkOaE36l0uWZ8JfnP4+SMa7pXvzJZmVzCJAXyL6fB1BkzoLwyTSjZAVheuCwmEipRSRRAMkkkUaaC4AAqI6dASl+m0ZKY+EoGKkgDpCl1O2unqcpSBcUOEAUy8FPJlsTkF1iE86RyeeJkhKWO+CErk//BZANzxtUm65gzt9fWbmLO3CKRK2jlOv237NqNWy6bGxs3cxHdyu3NubFOraxY8bOdu31tGgjgOL4hlM39ruQuMU2ujUlIqjV5UyhCaSl7USo40EGHb9zAyWSCD6j4gH++l05Bsx6X25pym/1At3TrMr67XtJu690Ku9u12V3bagLCzl4D9bmzU4TcRa3uLlJ2GqhVY2ddaxbcQc321rOawNYu6tbYXstju61t1G5TUtmmRNPtLNlH6odhHwtOCNhBir91U1xBqcRmjOJa1CUuzfrMTn3bocUKAvAzPyiuIE1BxTuHObiCUglLw+xi3QP/Ytc2bJra1NHoU5U4/SyIENHICRgTJQhcKq64xboEUZg5LFhJSd9xWVDsTuzYDvbDCJGd2eICTfIS+jzNMlHiUtelzAYNaRS6iNyApW9YP6DOSkpY6jPbjlhKXQYnWpQwvGR9aJKX4CvdDy5KnCyy4UdiUN64/WJdgoDt21m2khK7uIh92/tBZAduGAXPRUkgstSUJXLUxXUZchT2Ka7LkJIV2JRo+r9LWkigweCSwYMeNBhc0otvS0li3ZZ7l6ZNiSbdEs5zy39sEYtzVGRkiSUSct57S0QQITkqMbCkaeUoxL0mBO4RjgqMK7EsjhJOPKgZVsKJhWU8CyqGlVgcS/E8h4JRJfyx7Db0/LFqVIwq8fwWlkvdb+eKFJNKLAsy1HVxc0q4/AY2AwNXpJRKZp+AD50pMBaXttiQqKFEftpgfcUNlpS0O/eBs3HnY7tzCLyaddqQWHlJ7kGC2VjgHqTKJcJ9jF/h2QFw+HvjO2btMXTpl3gcywUUC+VjtHbJpx/P2p/H0xnU9Euks+ToFMJ8JLtNpZLZIaaLktkZDqZ4+vTZwccP06fQoF1S/nmP3p88ETkvJqVxwxLyEhx8ORsXJfgsNoqS4k0tJdL5Pn/x7gTApFRCcpTISxZmpQ0t+iVJs2nh38+O3j9ZVpLnxeqUy5lwPul146RUcvru6MVodKmEW90mJEwoieP00mSenMyPRxAx/xAlCSRMKGk2W+CDAdQGg1bVe9e0o2X6UCWvOON5fA9q90IA1Up+3tdy6ClVPnbhsvml3rxyybitZfxIhV+x5Hhyenx0NCnNE4tXLiFJa0iGuD7ikYSIXV11TI4wmZyOnhyXSqqPyWCAbhzj+pJhHA+7w/iKZ0bMMRodz09KY6Jxju/Gifj+WIHhMO7GhLZkJfIpwGxI6JQkQw8JxQqQhCbUG2o8FsaCH9iKWgkTzieFP7OZZhRSBJAzp2QxU0LXh4zi6a9JJZbvMMjlHgfkDCoBcQNIqQ7BMKmEv6YcEjnym/NbIiB/3YJE61z15UaV4LUHCdKCglkl0ue3FuFQMK2EN3OOstzLoWRaCZATXvqAR1CBeSXFuFiLgRkU26TqH+dMLAF4TojX7BOBV+0ws2ShFzahweCSVmxBg8Elmv9KZHCJpk2Jpk2Jhk2Jpk2JhvWVrGU1ga3dBuq2u6Y1C/ZQszu36dWya1pNYKfOsW/siRC5G/Sq8u0t4ReWmHw+zwnmiQAAAABJRU5ErkJggg==", + "description": "Contains widgets displaying outdoor environment telemetry.", + "order": 11000, + "externalId": null, + "name": "Outdoor Environment" + }, + "widgetTypeFqns": [ + "temperature_card", + "temperature_card_with_background", + "horizontal_temperature_card", + "horizontal_temperature_card_with_background", + "temperature_chart_card", + "temperature_chart_card_with_background", + "simple_temperature_chart_card", + "simple_temperature_chart_card_with_background", + "temperature_range_chart", + "temperature_range_chart_with_background", + "temperature_gauge", + "humidity_card", + "humidity_card_with_background", + "horizontal_humidity_card", + "horizontal_humidity_card_with_background", + "humidity_chart_card", + "humidity_chart_card_with_background", + "simple_humidity_chart_card", + "simple_humidity_chart_card_with_background", + "humidity_progress_bar", + "humidity_progress_bar_with_background", + "pressure_card", + "pressure_card_with_background", + "horizontal_pressure_card", + "horizontal_pressure_card_with_background", + "pressure_chart_card", + "pressure_chart_card_with_background", + "simple_pressure_chart_card", + "simple_pressure_chart_card_with_background", + "pressure_progress_bar", + "pressure_progress_bar_with_background", + "wind_speed_card", + "wind_speed_card_with_background", + "horizontal_wind_speed_card", + "horizontal_wind_speed_card_with_background", + "wind_speed_chart_card", + "wind_speed_chart_card_with_background", + "simple_wind_speed_chart_card", + "simple_wind_speed_chart_card_with_background", + "wind_speed_and_direction", + "wind_speed_and_direction_with_background", + "rainfall_card", + "rainfall_card_with_background", + "horizontal_rainfall_card", + "horizontal_rainfall_card_with_background", + "rainfall_chart_card", + "rainfall_chart_card_with_background", + "simple_rainfall_chart_card", + "simple_rainfall_chart_card_with_background", + "solar_radiation_card", + "solar_radiation_card_with_background", + "horizontal_solar_radiation_card", + "horizontal_solar_radiation_card_with_background", + "solar_radiation_chart_card", + "solar_radiation_chart_card_with_background", + "simple_solar_radiation_chart_card", + "simple_solar_radiation_chart_card_with_background", + "uv_index_card", + "uv_index_card_with_background", + "horizontal_uv_index_card", + "horizontal_uv_index_card_with_background", + "uv_index_chart_card", + "uv_index_chart_card_with_background", + "simple_uv_index_chart_card", + "simple_uv_index_chart_card_with_background", + "air_quality_card", + "air_quality_card_with_background", + "horizontal_air_quality_card", + "horizontal_air_quality_card_with_background", + "air_quality_chart_card", + "air_quality_chart_card_with_background", + "simple_air_quality_chart_card", + "simple_air_quality_chart_card_with_background", + "visibility_card", + "visibility_card_with_background", + "horizontal_visibility_card", + "horizontal_visibility_card_with_background", + "visibility_chart_card", + "visibility_chart_card_with_background", + "simple_visibility_chart_card", + "simple_visibility_chart_card_with_background", + "ground_temperature_card", + "ground_temperature_card_with_background", + "horizontal_ground_temperature_card", + "horizontal_ground_temperature_card_with_background", + "ground_temperature_chart_card", + "ground_temperature_chart_card_with_background", + "simple_ground_temperature_chart_card", + "simple_ground_temperature_chart_card_with_background", + "soil_moisture_card", + "soil_moisture_card_with_background", + "horizontal_soil_moisture_card", + "horizontal_soil_moisture_card_with_background", + "soil_moisture_chart_card", + "soil_moisture_chart_card_with_background", + "simple_soil_moisture_chart_card", + "simple_soil_moisture_chart_card_with_background", + "soil_moisture_progress_bar", + "soil_moisture_progress_bar_with_background", + "noise_level_card", + "noise_level_card_with_background", + "horizontal_noise_level_card", + "horizontal_noise_level_card_with_background", + "noise_level_chart_card", + "noise_level_chart_card_with_background", + "simple_noise_level_chart_card", + "simple_noise_level_chart_card_with_background", + "vibration_card", + "vibration_card_with_background", + "horizontal_vibration_card", + "horizontal_vibration_card_with_background", + "vibration_chart_card", + "vibration_chart_card_with_background", + "simple_vibration_chart_card", + "simple_vibration_chart_card_with_background", + "leaf_wetness_card", + "leaf_wetness_card_with_background", + "horizontal_leaf_wetness_card", + "horizontal_leaf_wetness_card_with_background", + "leaf_wetness_chart_card", + "leaf_wetness_chart_card_with_background", + "simple_leaf_wetness_chart_card", + "simple_leaf_wetness_chart_card_with_background", + "leaf_wetness_progress_bar", + "leaf_wetness_progress_bar_with_background", + "snow_depth_card", + "snow_depth_card_with_background", + "horizontal_snow_depth_card", + "horizontal_snow_depth_card_with_background", + "snow_depth_chart_card", + "snow_depth_chart_card_with_background", + "simple_snow_depth_chart_card", + "simple_snow_depth_chart_card_with_background", + "co2_card", + "co2_card_with_background", + "horizontal_co2_card", + "horizontal_co2_card_with_background", + "co2_chart_card", + "co2_chart_card_with_background", + "simple_co2_chart_card", + "simple_co2_chart_card_with_background", + "flooding_level_card", + "flooding_level_card_with_background", + "horizontal_flooding_level_card", + "horizontal_flooding_level_card_with_background", + "flooding_level_chart_card", + "flooding_level_chart_card_with_background", + "simple_flooding_level_chart_card", + "simple_flooding_level_chart_card_with_background", + "flooding_level_progress_bar", + "flooding_level_progress_bar_with_background", + "illuminance_card", + "illuminance_card_with_background", + "horizontal_illuminance_card", + "horizontal_illuminance_card_with_background", + "illuminance_chart_card", + "illuminance_chart_card_with_background", + "simple_illuminance_chart_card", + "simple_illuminance_chart_card_with_background", + "illuminance_progress_bar", + "illuminance_progress_bar_with_background", + "pm2_5_card", + "pm2_5_card_with_background", + "horizontal_pm2_5_card", + "horizontal_pm2_5_card_with_background", + "pm2_5_chart_card", + "pm2_5_chart_card_with_background", + "simple_pm2_5_chart_card", + "simple_pm2_5_chart_card_with_background", + "pm10_card", + "pm10_card_with_background", + "horizontal_pm10_card", + "horizontal_pm10_card_with_background", + "pm10_chart_card", + "pm10_chart_card_with_background", + "simple_pm10_chart_card", + "simple_pm10_chart_card_with_background" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/status_indicators.json b/application/src/main/data/json/system/widget_bundles/status_indicators.json new file mode 100644 index 0000000000..3c8192493b --- /dev/null +++ b/application/src/main/data/json/system/widget_bundles/status_indicators.json @@ -0,0 +1,16 @@ +{ + "widgetsBundle": { + "alias": "status_indicators", + "title": "Status indicators", + "image": "data:image/png;base64,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", + "description": "Contains widgets displaying battery level and signal strength.", + "order": 9000, + "externalId": null, + "name": "Status indicators" + }, + "widgetTypeFqns": [ + "battery_level", + "signal_strength", + "progress_bar" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_bundles/tables.json b/application/src/main/data/json/system/widget_bundles/tables.json new file mode 100644 index 0000000000..206971f1d5 --- /dev/null +++ b/application/src/main/data/json/system/widget_bundles/tables.json @@ -0,0 +1,17 @@ +{ + "widgetsBundle": { + "alias": "tables", + "title": "Tables", + "image": "data:image/png;base64,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", + "description": "Contains tables to display alarms, entities, and their telemetry.", + "order": 4000, + "externalId": null, + "name": "Tables" + }, + "widgetTypeFqns": [ + "cards.entities_table", + "alarm_widgets.alarms_table", + "cards.timeseries_table", + "control_widgets.persistent_table" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/air_quality_index_card.json b/application/src/main/data/json/system/widget_types/air_quality_index_card.json new file mode 100644 index 0000000000..c7e8ef72fe --- /dev/null +++ b/application/src/main/data/json/system/widget_types/air_quality_index_card.json @@ -0,0 +1,32 @@ +{ + "fqn": "air_quality_card", + "name": "Air quality index card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest air quality index telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'air', label: 'Air Quality Index', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Air Quality Index\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 320) {\\n\\tvalue = 320;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:weather-windy\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#80C32C\"},{\"from\":50,\"to\":100,\"color\":\"#FFA600\"},{\"from\":100,\"to\":150,\"color\":\"#F36900\"},{\"from\":150,\"to\":200,\"color\":\"#D81838\"},{\"from\":200,\"to\":300,\"color\":\"#8D268C\"},{\"from\":300,\"to\":null,\"color\":\"#6F113A\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":26,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#80C32C\"},{\"from\":50,\"to\":100,\"color\":\"#FFA600\"},{\"from\":100,\"to\":150,\"color\":\"#F36900\"},{\"from\":150,\"to\":200,\"color\":\"#D81838\"},{\"from\":200,\"to\":300,\"color\":\"#8D268C\"},{\"from\":300,\"to\":null,\"color\":\"#6F113A\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Air quality card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"AQI\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "air", + "aqi", + "pollution", + "emission", + "smog" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/air_quality_index_card_with_background.json b/application/src/main/data/json/system/widget_types/air_quality_index_card_with_background.json new file mode 100644 index 0000000000..e5d69dd012 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/air_quality_index_card_with_background.json @@ -0,0 +1,32 @@ +{ + "fqn": "air_quality_card_with_background", + "name": "Air quality index card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest air quality index telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'air', label: 'Air Quality Index', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Air Quality Index\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 320) {\\n\\tvalue = 320;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:weather-windy\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#7CC322\"},{\"from\":50,\"to\":100,\"color\":\"#F89E0D\"},{\"from\":100,\"to\":150,\"color\":\"#F77410\"},{\"from\":150,\"to\":200,\"color\":\"#DE2343\"},{\"from\":200,\"to\":300,\"color\":\"#7B287A\"},{\"from\":300,\"to\":null,\"color\":\"#791541\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":26,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#7CC322\"},{\"from\":50,\"to\":100,\"color\":\"#F89E0D\"},{\"from\":100,\"to\":150,\"color\":\"#F77410\"},{\"from\":150,\"to\":200,\"color\":\"#DE2343\"},{\"from\":200,\"to\":300,\"color\":\"#7B287A\"},{\"from\":300,\"to\":null,\"color\":\"#791541\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Air quality card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"AQI\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "air", + "aqi", + "pollution", + "emission", + "smog" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/air_quality_index_chart_card.json b/application/src/main/data/json/system/widget_types/air_quality_index_chart_card.json new file mode 100644 index 0000000000..5500fddd15 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/air_quality_index_chart_card.json @@ -0,0 +1,35 @@ +{ + "fqn": "air_quality_chart_card", + "name": "Air quality index chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays air quality index data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'air', label: 'Air Quality Index', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'AQI', decimals: 1 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'air', 'AQI', 1);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Air Quality Index\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 320) {\\n\\tvalue = 320;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"AQI\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#80C32C\"},{\"from\":50,\"to\":100,\"color\":\"#FFA600\"},{\"from\":100,\"to\":150,\"color\":\"#F36900\"},{\"from\":150,\"to\":200,\"color\":\"#D81838\"},{\"from\":200,\"to\":300,\"color\":\"#8D268C\"},{\"from\":300,\"to\":null,\"color\":\"#6F113A\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 320) {\\n\\tvalue = 320;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"AQI\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < -20) {\\n\\tvalue = -20;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"AQI\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Air Quality Index\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:weather-windy\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "air", + "aqi", + "pollution", + "emission", + "smog" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/air_quality_index_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/air_quality_index_chart_card_with_background.json new file mode 100644 index 0000000000..b86b8d060b --- /dev/null +++ b/application/src/main/data/json/system/widget_types/air_quality_index_chart_card_with_background.json @@ -0,0 +1,35 @@ +{ + "fqn": "air_quality_chart_card_with_background", + "name": "Air quality index chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays air quality index data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'air', label: 'Air Quality Index', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'AQI', decimals: 1 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'air', 'AQI', 1);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Air Quality Index\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 320) {\\n\\tvalue = 320;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"AQI\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#7CC322\"},{\"from\":50,\"to\":100,\"color\":\"#F89E0D\"},{\"from\":100,\"to\":150,\"color\":\"#F77410\"},{\"from\":150,\"to\":200,\"color\":\"#DE2343\"},{\"from\":200,\"to\":300,\"color\":\"#7B287A\"},{\"from\":300,\"to\":null,\"color\":\"#791541\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 320) {\\n\\tvalue = 320;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"AQI\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < -20) {\\n\\tvalue = -20;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"AQI\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Air Quality Index\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:weather-windy\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "air", + "aqi", + "pollution", + "emission", + "smog" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/alarm_count.json b/application/src/main/data/json/system/widget_types/alarm_count.json new file mode 100644 index 0000000000..38794306a5 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/alarm_count.json @@ -0,0 +1,27 @@ +{ + "fqn": "alarm_count", + "name": "Alarm count", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAolBMVEXg4ODf39/g4OAAAADg4ODf39/////g4OD8/Pz+/v7RJzD29vb6+vr7+/v09PTt7e3z8/P4+Pjx8fHv7+/ok5fXQkqXl5chISHk5OTIyMi6urqpqamgoKDDw8P55OXurrI8PDzc3NyxsbF0dHRYWFjlhYrUND3V1dXroKT88fLR0dHzycvwu76QkJDieH7eY2pKSkovLy/219jieH1mZmbTND12Tj45AAAABnRSTlPvIL8Ar7DvmsykAAAEx0lEQVR42u2aiXLaMBBATdMuUmU5yEBJgEDanL3v//+1rpDI2khA087U63RfjGRJa4k3a5sMpjgZPCuGfef54KQYFGMFPUfVqPG8hifAGE+r3ufDo4piCE+CoYgwQ0S4ISLcEBFuiAg3RIQbIsINEeGGiHBDRLghItwQEW6ICDdEhBsiwg0R4YaIcENEuCEi3BARbogIN0SEGyLCDRHhhohwQ0S48X+IaDiKBibsiGit/hit/i06J0IatkRsaWPZxoZR3OIgheKYjUHbkoLj/jawOWOjsDQ3rUfTUXcIQ5c9IqhROlPlMf5lwm6saYx2KC4dJSg6BhsKzMZTBK3hSqV0XsQ6p9ic9EexximtMyJaOdMfDURjTnIiyrqe/d5fOauyIgb6ha4wJRmRsoKekRXRyvVPxKUiGkVGEFnfvswwXQMzRiiik4xYEpm+zHILDS4Wy8UFeE7jtfUK/i15Ea2seRB5uQeI0Bs/ew1mcWGw7kKkNjYrUj9aZP7m4o1Zzg1W512IOKVSEZUVufm+XyRu89dwOl/OmWQEdFbkHuD+gMgSNy+yNK+7EcllxLpUZEqXflbk3QIv+HdLc7FYvOlCxFkNuiWitc5dIx8AeU8iKSa8DHRBnd61NOREPoXm5wcRZmBGVJIRlRFZAawxKatbriK/efud+AtkisWkRyKZUwsVosjbKVMR91sZWQMyuW9c77vz1NAVlBGAY3etT9Dgcypy/mU2uz6DzsiLQCqyAs/NDXhWicjZ7Mt88fUaOoJOrWMZmQDEa2TDZFfk7uoO4GoGHUGf7PpIRlbQ4m32Yr+8hI4gETgi8g1arFKR8+uvl3fQFXT7PfKBOJ00uZlmRK5+zD5CJ1BGNBw+tfLALlezGjrgkZ8jKe18nAF8nGHRDfTf79+KzK7Pz7u+2J2Fg9fIbd5jCk2Wlyw+EA/etdbTnMfPNbQ56/5fFLVHpGf4jMDTEMl+r+VGvXqqsO8LOq1c1bPHCuWe736t6VdKdGXKVkbo3KpGJfQFbUeVs9mMqBJNxuPT8SnRbKS940MhcaOOXCgu9whoYTyw3nikIt7EOlONPNWorkeBUFe176lDGxuhDANYhMjQsymo8qMRf1gaQathSZ3bmCqO163p8LlufKybnlooosrSObMX95iBdOR4jHtEqEOPRIRyosLjfB/msLKhahYIFiECxzF486IOfPliU1Fo+ENiUSI0l2/EJeKiYeUQZ2NJK/mmRQ0EMiJqg03xevinwm4Thd1xI9R2YBtfKhqIsc3jFM2llI8uYwD2UMAGmlQhwSMRQVSfCBqpCOgNyuuEDaEq9oYOKg9BR+5O125ovTtCA1TRHKEH0EOnIqTixx8ARaXC2pcB5RsQ+o6zEwR4dLuNTZoqzrpdAFqRcS6gfJAIETUASIl2qBGrGA5JCNAOJKPUBiROQiHxXcBma8ydLpKIJMSc6WYrzkz7YY94eAeNYJonjEWzzHK0FwV20Nsh/T//FLBPiAg3RIQbIsINEeGGiHBDRLghItwQEW6ICDdEhBsiwg0R4YaIcENEuCEi3BARbogIN0SEGyLCDRHhhohwQ0S4ISLcGBZFz37Zn0cNi2djeALUL4pBUfc+J2qMGieD58O+UzwbnPwC9rQ9R9RHLAMAAAAASUVORK5CYII=", + "description": "Displays the number of alarms based on the selected filter.", + "descriptor": { + "type": "latest", + "sizeX": 3.5, + "sizeY": 1.5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.countWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.countWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '220px',\n previewHeight: '100px',\n embedTitlePanel: true,\n hideDataSettings: true\n };\n};\n\nself.actionSources = function() {\n return {\n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-alarm-count-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-alarm-count-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"count\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = Number((prevValue + Math.random() * 4 - 2).toFixed(0));\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"],\"assignedToCurrentUser\":false,\"assigneeId\":null}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"showLabel\":true,\"label\":\"Total\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.54)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":20,\"iconSizeUnit\":\"px\",\"icon\":\"warning\",\"iconColor\":{\"type\":\"constant\",\"color\":\"rgba(255, 255, 255, 1)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIconBackground\":true,\"iconBackgroundSize\":36,\"iconBackgroundSizeUnit\":\"px\",\"iconBackgroundColor\":{\"type\":\"range\",\"color\":\"rgba(0, 105, 92, 1)\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"rgba(0, 105, 92, 1)\"},{\"from\":1,\"to\":null,\"color\":\"rgba(209, 39, 48, 1)\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":20,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"24px\"},\"valueColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showChevron\":false,\"chevronSize\":24,\"chevronSizeUnit\":\"px\",\"chevronColor\":\"rgba(0, 0, 0, 0.38)\",\"layout\":\"column\"},\"title\":\"Alarm count\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"\",\"decimals\":null,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.54)\"}" + }, + "externalId": null, + "tags": [ + "alert", + "alerts" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/alarms_table.json b/application/src/main/data/json/system/widget_types/alarms_table.json new file mode 100644 index 0000000000..5a360bcc32 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/alarms_table.json @@ -0,0 +1,28 @@ +{ + "fqn": "alarm_widgets.alarms_table", + "name": "Alarms table", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays alarms based on the defined time window and other filters. Allows the display of alarm fields, entity fields, attributes, and telemetry. Highly customizable via cell content functions and row style functions.", + "descriptor": { + "type": "alarm", + "sizeX": 10.5, + "sizeY": 6.5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.alarmsTableWidget.onDataUpdated();\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true,\n hasShowCondition: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-alarms-table-widget-settings", + "dataKeySettingsDirective": "tb-alarms-table-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-alarms-table-basic-config", + "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSelection\":true,\"enableSearch\":true,\"displayDetails\":true,\"allowAcknowledgment\":true,\"allowClear\":true,\"allowAssign\":true,\"displayActivity\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"-createdTime\",\"enableSelectColumnDisplay\":true,\"enableStickyAction\":false,\"enableFilter\":true,\"entitiesTitle\":null,\"alarmsTitle\":\"Alarms\"},\"title\":\"Alarms table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"alarmSource\":{\"type\":\"function\",\"dataKeys\":[{\"name\":\"createdTime\",\"type\":\"alarm\",\"label\":\"Created time\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.021092237451093787},{\"name\":\"originator\",\"type\":\"alarm\",\"label\":\"Originator\",\"color\":\"#4caf50\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.2780007688856758},{\"name\":\"type\",\"type\":\"alarm\",\"label\":\"Type\",\"color\":\"#f44336\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.7323586880398418},{\"name\":\"severity\",\"type\":\"alarm\",\"label\":\"Severity\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":false,\"useCellContentFunction\":false},\"_hash\":0.09927019860088193},{\"name\":\"status\",\"type\":\"alarm\",\"label\":\"Status\",\"color\":\"#607d8b\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6588418951443418},{\"name\":\"assignee\",\"type\":\"alarm\",\"label\":\"Assignee\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.5008441077416634}],\"entityAliasId\":null,\"name\":\"alarms\"},\"alarmSearchStatus\":\"ANY\",\"alarmsPollingInterval\":5,\"showTitleIcon\":false,\"titleIcon\":\"warning\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{},\"alarmStatusList\":[],\"alarmSeverityList\":[],\"alarmTypeList\":[],\"searchPropagatedAlarms\":false,\"configMode\":\"basic\",\"alarmFilterConfig\":null}" + }, + "externalId": null, + "tags": [ + "alert", + "alerts" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/asset_admin_table.json b/application/src/main/data/json/system/widget_types/asset_admin_table.json new file mode 100644 index 0000000000..0a2eb2c37a --- /dev/null +++ b/application/src/main/data/json/system/widget_types/asset_admin_table.json @@ -0,0 +1,30 @@ +{ + "fqn": "entity_admin_widgets.asset_admin_table", + "name": "Asset admin table", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Customized entity table widget with preconfigured actions to create, update and delete assets.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.entitiesTableWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n hasDataPageLink: true,\n warnOnPageDataOverflow: false,\n dataKeysOptional: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true,\n hasShowCondition: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n },\n 'rowDoubleClick': {\n name: 'widget-action.row-double-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-entities-table-widget-settings", + "dataKeySettingsDirective": "tb-entities-table-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-entities-table-basic-config", + "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"entitiesTitle\":\"Asset admin table\",\"enableSearch\":true,\"enableSelectColumnDisplay\":true,\"enableStickyHeader\":true,\"enableStickyAction\":true,\"showCellActionsMenu\":true,\"reserveSpaceForHiddenAction\":\"true\",\"displayEntityName\":false,\"displayEntityLabel\":false,\"displayEntityType\":false,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"entityName\",\"useRowStyleFunction\":false},\"title\":\"Asset admin table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Entity name\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.6985800247727296,\"funcBody\":\"return 'Simulated';\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Entity type\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.10073938422359707,\"funcBody\":\"return 'Device';\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{\"headerButton\":[{\"name\":\"Add asset\",\"icon\":\"add\",\"type\":\"customPretty\",\"customHtml\":\"
\\n \\n

Add asset

\\n \\n \\n
\\n \\n \\n
\\n
\\n
\\n \\n Asset name\\n \\n \\n Asset name is required.\\n \\n \\n
\\n \\n \\n Label\\n \\n \\n
\\n
\\n \\n Latitude\\n \\n \\n \\n Longitude\\n \\n \\n
\\n
\\n
\\n
\\n \\n \\n \\n
\\n
\\n\",\"customCss\":\"\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\\nlet assetService = $injector.get(widgetContext.servicesMap.get('assetService'));\\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\\n\\nopenAddAssetDialog();\\n\\nfunction openAddAssetDialog() {\\n customDialog.customDialog(htmlTemplate, AddAssetDialogController).subscribe();\\n}\\n\\nfunction AddAssetDialogController(instance) {\\n let vm = instance;\\n \\n vm.addAssetFormGroup = vm.fb.group({\\n assetName: ['', [vm.validators.required]],\\n assetType: ['', [vm.validators.required]],\\n assetLabel: [''],\\n attributes: vm.fb.group({\\n latitude: [null],\\n longitude: [null]\\n }) \\n });\\n \\n vm.cancel = function() {\\n vm.dialogRef.close(null);\\n };\\n \\n vm.save = function() {\\n vm.addAssetFormGroup.markAsPristine();\\n let asset = {\\n name: vm.addAssetFormGroup.get('assetName').value,\\n type: vm.addAssetFormGroup.get('assetType').value,\\n label: vm.addAssetFormGroup.get('assetLabel').value\\n };\\n assetService.saveAsset(asset).subscribe(\\n function (asset) {\\n saveAttributes(asset.id).subscribe(\\n function () {\\n widgetContext.updateAliases();\\n vm.dialogRef.close(null);\\n }\\n );\\n }\\n );\\n };\\n \\n function saveAttributes(entityId) {\\n let attributes = vm.addAssetFormGroup.get('attributes').value;\\n let attributesArray = [];\\n for (let key in attributes) {\\n attributesArray.push({key: key, value: attributes[key]});\\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\":\"70837a9d-c3de-a9a7-03c5-dccd14998758\"}],\"actionCellButton\":[{\"name\":\"Edit asset\",\"icon\":\"edit\",\"type\":\"customPretty\",\"customHtml\":\"
\\n \\n

Edit asset

\\n \\n \\n
\\n \\n \\n
\\n
\\n
\\n \\n Asset name\\n \\n \\n Asset name is required.\\n \\n \\n
\\n \\n \\n Label\\n \\n \\n
\\n
\\n \\n Latitude\\n \\n \\n \\n Longitude\\n \\n \\n
\\n
\\n
\\n
\\n \\n \\n \\n
\\n
\\n\",\"customCss\":\"\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\\nlet assetService = $injector.get(widgetContext.servicesMap.get('assetService'));\\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\\n\\nopenEditAssetDialog();\\n\\nfunction openEditAssetDialog() {\\n customDialog.customDialog(htmlTemplate, EditAssetDialogController).subscribe();\\n}\\n\\nfunction EditAssetDialogController(instance) {\\n let vm = instance;\\n \\n vm.asset = null;\\n vm.attributes = {};\\n \\n vm.editAssetFormGroup = vm.fb.group({\\n assetName: ['', [vm.validators.required]],\\n assetType: ['', [vm.validators.required]],\\n assetLabel: [''],\\n attributes: vm.fb.group({\\n latitude: [null],\\n longitude: [null]\\n }) \\n });\\n \\n vm.cancel = function() {\\n vm.dialogRef.close(null);\\n };\\n \\n vm.save = function() {\\n vm.editAssetFormGroup.markAsPristine();\\n vm.asset.name = vm.editAssetFormGroup.get('assetName').value,\\n vm.asset.type = vm.editAssetFormGroup.get('assetType').value,\\n vm.asset.label = vm.editAssetFormGroup.get('assetLabel').value\\n assetService.saveAsset(vm.asset).subscribe(\\n function () {\\n saveAttributes().subscribe(\\n function () {\\n widgetContext.updateAliases();\\n vm.dialogRef.close(null);\\n }\\n );\\n }\\n );\\n };\\n \\n getEntityInfo();\\n \\n function getEntityInfo() {\\n assetService.getAsset(entityId.id).subscribe(\\n function (asset) {\\n attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE',\\n ['latitude', 'longitude']).subscribe(\\n function (attributes) {\\n for (let i = 0; i < attributes.length; i++) {\\n vm.attributes[attributes[i].key] = attributes[i].value; \\n }\\n vm.asset = asset;\\n vm.editAssetFormGroup.patchValue(\\n {\\n assetName: vm.asset.name,\\n assetType: vm.asset.type,\\n assetLabel: vm.asset.label,\\n attributes: {\\n latitude: vm.attributes.latitude,\\n longitude: vm.attributes.longitude\\n }\\n }, {emitEvent: false}\\n );\\n } \\n );\\n }\\n ); \\n }\\n \\n function saveAttributes() {\\n let attributes = vm.editAssetFormGroup.get('attributes').value;\\n let attributesArray = [];\\n for (let key in attributes) {\\n attributesArray.push({key: key, value: attributes[key]});\\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\":\"93931e52-5d7c-903e-67aa-b9435df44ff4\"},{\"name\":\"Delete asset\",\"icon\":\"delete\",\"type\":\"custom\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet dialogs = $injector.get(widgetContext.servicesMap.get('dialogs'));\\nlet assetService = $injector.get(widgetContext.servicesMap.get('assetService'));\\n\\nopenDeleteAssetDialog();\\n\\nfunction openDeleteAssetDialog() {\\n let title = \\\"Are you sure you want to delete the asset \\\" + entityName + \\\"?\\\";\\n let content = \\\"Be careful, after the confirmation, the asset and all related data will become unrecoverable!\\\";\\n dialogs.confirm(title, content, 'Cancel', 'Delete').subscribe(\\n function (result) {\\n if (result) {\\n deleteAsset();\\n }\\n }\\n );\\n}\\n\\nfunction deleteAsset() {\\n assetService.deleteAsset(entityId.id).subscribe(\\n function () {\\n widgetContext.updateAliases();\\n }\\n );\\n}\\n\",\"id\":\"ec2708f6-9ff0-186b-e4fc-7635ebfa3074\"}]},\"configMode\":\"basic\",\"titleFont\":null,\"titleColor\":null}" + }, + "externalId": null, + "tags": [ + "provisioning", + "management", + "administration", + "admin" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/attributes_card.json b/application/src/main/data/json/system/widget_types/attributes_card.json new file mode 100644 index 0000000000..067360a6cc --- /dev/null +++ b/application/src/main/data/json/system/widget_types/attributes_card.json @@ -0,0 +1,20 @@ +{ + "fqn": "cards.attributes_card", + "name": "Attributes card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays one or more attributes or the latest telemetry values of the entity. Supports multiple entities as separate bars.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "", + "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", + "controllerScript": "self.onInit = function() {\n \n self.ctx.datasourceTitleCells = [];\n self.ctx.valueCells = [];\n self.ctx.labelCells = [];\n \n for (var i=0; i < self.ctx.datasources.length; i++) {\n var tbDatasource = self.ctx.datasources[i];\n\n var datasourceId = 'tbDatasource' + i;\n self.ctx.$container.append(\n \"
\"\n );\n\n var datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n\n datasourceContainer.append(\n \"
\" +\n tbDatasource.name + \"
\"\n );\n \n var datasourceTitleCell = $('.tbDatasource-title', datasourceContainer);\n self.ctx.datasourceTitleCells.push(datasourceTitleCell);\n \n var tableId = 'table' + i;\n datasourceContainer.append(\n \"
\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n\n for (var a = 0; a < tbDatasource.dataKeys.length; a++) {\n var dataKey = tbDatasource.dataKeys[a];\n var labelCellId = 'labelCell' + a;\n var cellId = 'cell' + a;\n table.append(\"\" + dataKey.label +\n \"\");\n var labelCell = $('#' + labelCellId, table);\n self.ctx.labelCells.push(labelCell);\n var valueCell = $('#' + cellId, table);\n self.ctx.valueCells.push(valueCell);\n }\n } \n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.valueCells.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData && cellData.data && cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n var textValue;\n //toDo -> + IsNumber\n \n if (isNumber(value)) {\n var decimals = self.ctx.decimals;\n var units = self.ctx.units;\n if (cellData.dataKey.decimals || cellData.dataKey.decimals === 0) {\n decimals = cellData.dataKey.decimals;\n }\n if (cellData.dataKey.units) {\n units = cellData.dataKey.units;\n }\n txtValue = self.ctx.utils.formatValue(value, decimals, units, true);\n } else {\n txtValue = value;\n }\n self.ctx.valueCells[i].html(txtValue);\n }\n }\n \n function isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n }\n}\n\nself.onResize = function() {\n var datasourceTitleFontSize = self.ctx.height/8;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n datasourceTitleFontSize = self.ctx.width/12;\n }\n datasourceTitleFontSize = Math.min(datasourceTitleFontSize, 20);\n for (var i = 0; i < self.ctx.datasourceTitleCells.length; i++) {\n self.ctx.datasourceTitleCells[i].css('font-size', datasourceTitleFontSize+'px');\n }\n var valueFontSize = self.ctx.height/9;\n var labelFontSize = self.ctx.height/9;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n valueFontSize = self.ctx.width/15;\n labelFontSize = self.ctx.width/15;\n }\n valueFontSize = Math.min(valueFontSize, 18);\n labelFontSize = Math.min(labelFontSize, 18);\n\n for (i = 0; i < self.ctx.valueCells; i++) {\n self.ctx.valueCells[i].css('font-size', valueFontSize+'px');\n self.ctx.valueCells[i].css('height', valueFontSize*2.5+'px');\n self.ctx.valueCells[i].css('padding', '0px ' + valueFontSize + 'px');\n self.ctx.labelCells[i].css('font-size', labelFontSize+'px');\n self.ctx.labelCells[i].css('height', labelFontSize*2.5+'px');\n self.ctx.labelCells[i].css('padding', '0px ' + labelFontSize + 'px');\n } \n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Attributes card\"}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/bars.json b/application/src/main/data/json/system/widget_types/bars.json new file mode 100644 index 0000000000..b3c991f968 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/bars.json @@ -0,0 +1,29 @@ +{ + "fqn": "charts.bars", + "name": "Bars", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAA8FBMVEUhlvNMr1Bqamp5eXl7e3t8fHx9fX1+fn5/f3+AgICCgoKDg4OEhISGhoaHh4eKioqMjIyNjY2Ojo6QkJCRkZGSkpKWlpaXl5ebm5udnZ2enp6goKChoaGkpKSnp6epqamsrKyurq6xsbGzs7O1tbW2tra3t7e4uLi7u7u9vb3BwcHCwsLDw8PGxsbKysrNzc3Ozs7R0dHS0tLT09PZ2dna2trc3Nzd3d3e3t7g4ODh4eHj4+Pk5OTm5ubn5+fo6Ojp6enu7u7w8PDz8/P0Qzb09PT29vb39/f5+fn6+vr7+/v8/Pz9/f3+/v7/wQf///+dc+aLAAAAAWJLR0RPbmZBSQAAAcFJREFUeNrt3ds2AgEYhuHsaSOZbAvZi0r2YYjCJOW7/7txZhkcDNbM6h/vdwfPmlX/ybtmEorJErGCeJLadz3rkKPpZamaLTp925DHdFvSpKelU9uQ/cLKhtcdk7YqtiHruevtojch7ZZtQ0o1dcdfRqXNqm1IbVU3OaVamm/YhvQW5zIXOknnC5JUt7qEpE5fUv/5HVePy2UHAgQIECBAgAABAgQIECBxgrwGHBAgQIAAAQIECBAgQIAAAQIECJC/QRIBN0iQ+66voDMLuRp2fQWdVUhvNun6CjqrkJ0Dx/UVdEYhzXzfcX0FnVFIrlSZ2mx/LOiMQuqHh6k972NBZ/fv13G/KeiCQkIu4358EL8UdBafSGwuOxAgQIAAAQIECJDYQB4CDggQIECAAAECBAgQIECAAAECBAgQIECA/BrSufn0DjqjkEZmLXkWaUEXEmThXMeFSAu68H4j5b1IC7rQILfZTqQFXViQ1nRTL1EWdCFBnmYuJUVZ0IUEWR1xHKcXZUEXFDLwBR2XHQgQIECAAAEC5H9ChgIOCBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgxiBmv+L6Bl9pkxYph15gAAAAAElFTkSuQmCC", + "description": "Displays latest values of the attributes or time-series data for multiple entities as separate bars.", + "descriptor": { + "type": "latest", + "sizeX": 7, + "sizeY": 5, + "resources": [ + { + "url": "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js" + } + ], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showTooltip = utils.defaultValue(settings.showTooltip, true);\n \n Chart.defaults.global.tooltips.enabled = settings.showTooltip;\n \n var barData = {\n labels: [],\n datasets: []\n };\n \n for (var i = 0; i < self.ctx.datasources.length; i++) {\n var datasource = self.ctx.datasources[i];\n for (var d = 0; d < datasource.dataKeys.length; d++) {\n var dataKey = datasource.dataKeys[d];\n var units = dataKey.units && dataKey.units.length ? dataKey.units : self.ctx.units;\n units = units ? (' (' + units + ')') : '';\n var dataset = {\n label: dataKey.label + units,\n data: [0],\n backgroundColor: [dataKey.color],\n borderColor: [dataKey.color],\n borderWidth: 1\n }\n barData.datasets.push(dataset);\n }\n }\n\n var ctx = $('#barChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'bar',\n data: barData,\n options: {\n responsive: false,\n maintainAspectRatio: false,\n scales: {\n yAxes: [{\n ticks: {\n beginAtZero:true\n }\n }]\n }\n }\n });\n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n var c = 0;\n for (var i = 0; i < self.ctx.chart.data.datasets.length; i++) {\n var dataset = self.ctx.chart.data.datasets[i];\n var cellData = self.ctx.data[i]; \n if (cellData.data.length > 0) {\n var decimals;\n if (typeof cellData.dataKey.decimals !== 'undefined' \n && cellData.dataKey.decimals !== null ) {\n decimals = cellData.dataKey.decimals; \n } else {\n decimals = self.ctx.decimals;\n }\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = self.ctx.utils.formatValue(tvPair[1], decimals);\n dataset.data[0] = parseFloat(value);\n }\n }\n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n self.ctx.chart.resize();\n}\n\nself.onDestroy = function() {\n self.ctx.chart.destroy();\n self.ctx.chart = null;\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-chart-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Bars\"}" + }, + "externalId": null, + "tags": [ + "bar", + "bar chart" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/basic_gpio_control.json b/application/src/main/data/json/system/widget_types/basic_gpio_control.json new file mode 100644 index 0000000000..df54377dc5 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/basic_gpio_control.json @@ -0,0 +1,35 @@ +{ + "fqn": "gpio_widgets.basic_gpio_control", + "name": "Basic GPIO Control", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Allows to change state of the GPIO for target device using RPC commands. Requires handling of the RPC commands in the device firmware. Uses 'getGpioStatus' and 'setGpioStatus' RPC calls", + "descriptor": { + "type": "rpc", + "sizeX": 4, + "sizeY": 2, + "resources": [], + "templateHtml": "
\n
\n
\n
\n {{ cell.label }}\n
\n {{cell.pin}}\n \n \n \n \n {{cell.pin}}\n
\n {{ cell.label }}\n
\n
\n \n \n \n
\n
\n
\n {{rpcErrorText}}\n \n
", + "templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.gpio-panel section[fxflex] {\n min-width: 0px;\n}\n\n\n.switch-panel {\n margin: 0;\n height: 32px;\n width: 66px;\n min-width: 66px;\n}\n\n.switch-panel mat-slide-toggle {\n margin: 0;\n width: 36px;\n min-width: 36px;\n}\n\n.switch-panel.col-0 mat-slide-toggle {\n margin-left: 8px;\n margin-right: 4px;\n}\n\n.switch-panel.col-1 mat-slide-toggle {\n margin-left: 4px;\n margin-right: 8px;\n}\n\n.gpio-row {\n height: 32px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.switch-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.switch-panel.col-1 .pin {\n margin-right: auto;\n \n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}", + "controllerScript": "var namespace;\nvar cssParser = new cssjs();\n\nself.onInit = function() {\n var utils = self.ctx.$injector.get(self.ctx.servicesMap.get('utils'));\n namespace = 'gpio-control-' + utils.guid();\n cssParser.testMode = false;\n cssParser.cssPreviewNamespace = namespace;\n self.ctx.$container.addClass(namespace);\n self.ctx.ngZone.run(function() {\n init(); \n });\n}\n\nfunction init() {\n \n var i, gpio;\n var scope = self.ctx.$scope;\n var settings = self.ctx.settings;\n scope.gpioList = [];\n for (var g = 0; g < settings.gpioList.length; g++) {\n gpio = settings.gpioList[g];\n scope.gpioList.push(\n {\n row: gpio.row,\n col: gpio.col,\n pin: gpio.pin,\n label: gpio.label,\n enabled: false\n }\n );\n }\n\n scope.requestTimeout = settings.requestTimeout || 1000;\n\n scope.switchPanelBackgroundColor = settings.switchPanelBackgroundColor || tinycolor('green').lighten(2).toRgbString();\n\n scope.gpioStatusRequest = {\n method: \"getGpioStatus\",\n paramsBody: \"{}\"\n };\n \n if (settings.gpioStatusRequest) {\n scope.gpioStatusRequest.method = settings.gpioStatusRequest.method || scope.gpioStatusRequest.method;\n scope.gpioStatusRequest.paramsBody = settings.gpioStatusRequest.paramsBody || scope.gpioStatusRequest.paramsBody;\n }\n \n scope.gpioStatusChangeRequest = {\n method: \"setGpioStatus\",\n paramsBody: \"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"\n };\n \n if (settings.gpioStatusChangeRequest) {\n scope.gpioStatusChangeRequest.method = settings.gpioStatusChangeRequest.method || scope.gpioStatusChangeRequest.method;\n scope.gpioStatusChangeRequest.paramsBody = settings.gpioStatusChangeRequest.paramsBody || scope.gpioStatusChangeRequest.paramsBody;\n }\n \n scope.parseGpioStatusFunction = \"return body[pin] === true;\";\n \n if (settings.parseGpioStatusFunction && settings.parseGpioStatusFunction.length > 0) {\n scope.parseGpioStatusFunction = settings.parseGpioStatusFunction;\n }\n \n scope.parseGpioStatusFunction = new Function(\"body, pin\", scope.parseGpioStatusFunction);\n \n function requestGpioStatus() {\n self.ctx.controlApi.sendTwoWayCommand(scope.gpioStatusRequest.method, \n scope.gpioStatusRequest.paramsBody, \n scope.requestTimeout)\n .subscribe(\n function success(responseBody) {\n for (var g = 0; g < scope.gpioList.length; g++) {\n var gpio = scope.gpioList[g];\n var enabled = scope.parseGpioStatusFunction.apply(this, [responseBody, gpio.pin]);\n gpio.enabled = enabled; \n self.ctx.detectChanges();\n }\n }\n );\n }\n \n function changeGpioStatus(gpio) {\n var pin = gpio.pin + '';\n var enabled = !gpio.enabled;\n enabled = enabled === true ? 'true' : 'false';\n var paramsBody = scope.gpioStatusChangeRequest.paramsBody;\n var requestBody = JSON.parse(paramsBody.replace(\"\\\"{$pin}\\\"\", pin).replace(\"\\\"{$enabled}\\\"\", enabled));\n self.ctx.controlApi.sendTwoWayCommand(scope.gpioStatusChangeRequest.method, \n requestBody, scope.requestTimeout)\n .subscribe(\n function success(responseBody) {\n var enabled = scope.parseGpioStatusFunction.apply(this, [responseBody, gpio.pin]);\n gpio.enabled = enabled;\n self.ctx.detectChanges();\n }\n );\n }\n \n scope.gpioCells = {};\n var rowCount = 0;\n for (i = 0; i < scope.gpioList.length; i++) {\n gpio = scope.gpioList[i];\n scope.gpioCells[gpio.row+'_'+gpio.col] = gpio;\n rowCount = Math.max(rowCount, gpio.row+1);\n }\n \n scope.prefferedRowHeight = 32;\n scope.rows = [];\n for (i = 0; i < rowCount; i++) {\n var row = [];\n for (var c =0; c<2;c++) {\n if (scope.gpioCells[i+'_'+c]) {\n row[c] = scope.gpioCells[i+'_'+c];\n } else {\n row[c] = null;\n }\n }\n scope.rows.push(row);\n }\n\n scope.gpioClick = function($event, gpio) {\n if (scope.rpcEnabled && !scope.executingRpcRequest) {\n changeGpioStatus(gpio);\n }\n };\n \n scope.gpioToggleChange = function($event, gpio) {\n gpio.enabled = !$event.checked;\n $event.source.toggle();\n self.ctx.detectChanges();\n }\n \n if (scope.rpcEnabled) {\n requestGpioStatus(); \n }\n \n self.onResize();\n}\n\nself.onResize = function() {\n var scope = self.ctx.$scope;\n var rowCount = scope.rows.length;\n var prefferedRowHeight = (self.ctx.height - 35)/rowCount;\n prefferedRowHeight = Math.min(32, prefferedRowHeight);\n prefferedRowHeight = Math.max(12, prefferedRowHeight);\n scope.prefferedRowHeight = prefferedRowHeight;\n var ratio = prefferedRowHeight/32;\n \n var css = '.mat-slide-toggle .mat-slide-toggle-bar {\\n' +\n ' height: ' + 14*ratio+'px;\\n'+\n ' width: ' + 36*ratio+'px;\\n'+\n '}\\n';\n css += '.mat-slide-toggle .mat-slide-toggle-thumb-container {\\n' +\n ' height: ' + 20*ratio+'px;\\n'+\n ' width: ' + 20*ratio+'px;\\n'+\n '}\\n';\n css += '.mat-slide-toggle .mat-slide-toggle-thumb {\\n' +\n ' height: ' + 20*ratio+'px;\\n'+\n ' width: ' + 20*ratio+'px;\\n'+\n '}\\n';\n css += '.mat-slide-toggle .mat-slide-toggle-ripple {\\n' +\n ' height: ' + 40*ratio+'px;\\n'+\n ' width: ' + 40*ratio+'px;\\n'+\n ' top: calc(50% - '+20*ratio+'px);\\n'+\n ' left: calc(50% - '+20*ratio+'px);\\n'+\n '}\\n';\n css += '.gpio-left-label, .gpio-right-label {\\n' +\n ' font-size: ' + 16*ratio+'px;\\n'+\n '}\\n';\n var pinsFontSize = Math.max(9, 12*ratio);\n css += '.pin {\\n' +\n ' font-size: ' + pinsFontSize+'px;\\n'+\n '}\\n';\n\n cssParser.createStyleElement(namespace, css);\n \n self.ctx.detectChanges();\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-gpio-control-widget-settings", + "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 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\":\"Basic GPIO Control\"}" + }, + "externalId": null, + "tags": [ + "pin", + "pins", + "board", + "circuit", + "digital read", + "digital write", + "analog read", + "analog write", + "microcontroller", + "i/o", + "input/output", + "hardware" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/basic_gpio_panel.json b/application/src/main/data/json/system/widget_types/basic_gpio_panel.json new file mode 100644 index 0000000000..5a46e216a1 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/basic_gpio_panel.json @@ -0,0 +1,26 @@ +{ + "fqn": "gpio_widgets.gpio_panel", + "name": "Basic GPIO Panel", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAMPklEQVR42u2dB1AUWRrHcUFUUAFFHMFRoqBYgCIlhpM1g/nUM2upa/bMAcsrU5X5zrPUQj1dC0Fl1eNMJBlFkQUBLTGsuuYARkygmADl/sur7e2bQaYZRwz3/9WrrZ7e5onzfvN9r9+8rzUpKioqKCjIzs7OzMy8RchHAIUgUn5+PqQygVVZWVm5ubmFhYVFhHwEUAgiQSdIZQLF8IJvCjEW0AlSmSB8MVYR48YtSGWC1Mj3ghgXSEWxCMUiFItQLIpFKBahWIRiEUKxCMUiFIsQikUoFqFYhFAsQrEIxSKEYhGKRSgWIRSLUCxCsQihWIRiEYpFCMUiFItQLEIoFqFYhGIRQrEIxSIUixCKRSgWoViEUCxCsQjFIoRiEYpFKBYhFItQLEKxCKFYhGIRikUIxSIUi1Cs8iApICCubl02Je1BbCzFolgUi2JRLIpFsSgWxaJY35ZYIW61R/pad/ev2qt51b/6WIc7qT7dYMd71kkcZ528xDJltUXS/GoJvW3j6jtQrG9NrN2O9u1bWdQMNJW32oFmQ/2sotXGHml13cRR1ulxZic0pvKWur3SoVYqivUbR44cGTx4cIsWLQYMGHDq1ClxMjQ09C+/M2XKlHPnzonzw4YNu3HjhjhOSEjo2bOnv7//uHHj7ty5o9tzbm7u5s2bN27cWA5i/bu+feO2lbSsklpgS8tYo7p1bLqVllJSS482+xRulSjW/fv3p0+fHhAQ0Llz502bNr1//x4nr169Ko3diBEjduzYIc5jLMLDw8UP3rt3b8KECRi7Hj16aDQarW7fvXu3bt269u3bt2vXbsOGDYaIBTmsra23bNly8eJF/NfGxgYHOA+ZevXqdaiYVatWVa9ePSMjA+elg6ioqJo1a8K/48ePT506Va1WP336VN7zggULateu7eLiAu0MECvBx+fXhQuzIiIUvu9dW1T9kFWizfCyMdYYH+5kdyLe9ENiibgVV19RVxp397uRkY+Tkx8nJZX+MdMV6+XLl+7u7mPGjMGIHDt2zMvLa8mSJTifnp6OYRJjFxkZ2bhx47lz5+L8+PHjZ86cKT7wjo6OkyZNwtiFhYXZ2tru2bNH3vPq1av9/PzOnDmDrurVq7dr164yi9WxY0fx2whmzZq1cuVKIdbkyZOl85Bj2rRpcrHw14BV0gVBQUHyfkBqair+5vPmzTNMrHv79l1eujTn9GklwxPmrLIt1So0t/bmsUYSK/kfFqVYJVpCD1slXV1ZufLS4sU4ODVy5P2oqDKJhfff09NTRCOQnJzcqlUrIRY+89JlcA6fcLlYK1aswLhLF2zbtq1Ro0byngcNGhQdHS2OEREROMoslr29fUpKiu55LbEQUeGcJNazZ89MTEwePnwoXbBmzRq4pduPwWKhHfXzUygWolFNfWKhbXQ1QoY6qK6rO7XSbUl/q6akt0R//0MeHjg40b9/5rZtZRILliCd6b6rWmIhbjk4OMjF6tKlC2KSdMGjR48wmk+ePClxjFq3bh0REVFmsSpUqHD58mUcIAPuLiYtLU2Ihfz6r2JwjHQpUqQQ69q1a9999528n507dyJ4fi6xhvtaKRFraaNaRrkT1GsVWso/LcrQp4vL0/T0pO+/L5NYQ4YMwXxD5MTdv4MABrEsLS3F2CH/IOstX75cLlbz5s21XDEzM7ty5YruAK1du7ZDhw6YcpVZrKpVq+L3EFP44ODgNm3aDBw4UIjl6+sbXAyi0d27d8X1QqwHDx7A8ZycHKkfzNAh4ucSa0wTRWKtcrczQsRydVAiFtYglHZYrx6S4JmJE8s6eR87dqzIKkggGCa8zxgUSIABrVKlihi7xYsXY/ImrpfEwqx8/fr1Uj8vXrzAD2I6r9U/sqGTkxPuDwyZvCPQyaMifg9JLHkqlBBi4WOBHBofHy+dHzVqlJiEfRaxljaspdeqWp1Nd9e3N8ocK22nuV6xEidaK+zt1o8//rpokQF3hbhfa9asmfTy5s2bkljyVCghiTVjxgzc3cuXBVQqlVZYQuLCtF2kKUPE2rdvn52dnUh/b9686d+//9ChQ/WKhQPEWMwckRPxCx04cMDKyurSpUufS6wDagfX9uali9WplaXR1homW+sRK95U01TRfO6XGTNe37t3PSRENNwkKhcLGQOTp2XLlhUUFOAl8iAymhKxMGoYL1yPi2/fvt20aVMEFPmVGEokUJHKDF/Hwk0BFgVq1KiBP6x3795iRWrOnDkIpLoXw+KzZ8/iAEELvw3uVCtXrowbWswQS+wct4q4rTBMrHgnp9KnHfK2wNO2lBtDh45moS5GW1uKb2CfFlGptJn7TCuFXR3x9U0JCpLaQUfHMq1jYWKEvFa9GB8fn5iYGJzEYiQGVPdiBCp8zsVxYmIirkfGxLjjpFa4Gj16NG4knWUYvvKOcGXY8mt+fv4XsvI+ydvatnMJVqk7Vvy7h51xlysPtVSl7So5ISYvtfwUX+yUsvJeUEw5j93/13eFaxvUbtvawq7zH4Hqz/7VtjnX+RTfsRxsZJ80t1r6gT+WHlLDKx0dUiNOze8Kv9HdDXvrOWx2USH3RdV1+NSbDg46O2haqA53sNM0UX0ipSgWt81QLIpFsSgWG8WiWBSLYlEsisVGsSgWxaJYFItiUSyK9ZnFinVQR9dxjq7jFFdXTbEolhHaAVWDCMuAULNuoaY90MIqdtlVvWWxYRSLYhna/mPjs9W0u1BK3qDXfjt3ilXeYqFsa/v27YsWLUIl0KtXr8RJVFiITdOoVouNjX379q04j8oQ7L2XfhA7krHtGhv9dLdFY8MWdhGi261bt0o/rlwsjZvb6fHjM8aMiXd2VvK+77P11FVKalsrdo1WOVOs8hML57ELEZWN2KDcr1+/hg0bYgN1kWzP++zZs1EqhPq1x48fF8l2kKIEA7uTseN0/vz5TZo06datW2Fhobxn7MhGISW2z/bp06dTp05lEgtb/J5lZFxcsAAVYI9//ln/++6gDjcPLEUstJ8s/0Sxyk8sVHoMHz5cegkDUHRWpLM1Gd4sXLhQLlbXrl3hnPi/r1+/9vDwQMCTrkeIcnNzE7ENx+bm5lI5hhKxEry9f5k5UxznXb8Oz/SEq1qepVv1W9Ay7R5j70ixykks7GE9fPiwPC2KakEtsbC9eOLEiZJYyJgVK1aUau2Lircg9+3bt8Q/AruqUQskL+lROMdK7dkTxdCoXNVfX2/VTK9YaPvtGlKschILfohKDESdp8Xk5eUJsVC7jZeo9IqLi0PBhSiNFWKhIAQFifLch1kanv6g1fnRo0dR84OMiUJvAybv54OD7+7Zczs0VP/jQKyaKxEL8zCKVU5iIUmJ4oiQkBDsmcfWeqn8C7UVOIMcFxgYKFXvC7EQhCCWfJs88iAqybQ6z8rKwvZ+hDrUspa4vfpDYmlcXfHsBnGMyRbKhUt/0yNtmigRK0rVgGKVk1iYraOIWXqpsK4QlqDWVhgpwGMn5KVqCGbiJkAAQVEWolwsmPT8wgXUnid4eeVdu6ZpoEeIKJWLXqvCzIO+6vXSr0wsVKWhrOz58+eSH0rEwsHIkSNxLymyIYplke+QMeVzNdS4iSJaJFlUiV24cKFMqTDjhx+yExIeajTpffsqed9/smxTuliRNk15V1h+YiH24MYQ1YJ4RBaWFTCXEk9L0isWniGB3IeAh0dk4afEg3LkYH0LdwaYqHl7e5f47ArjLpDG2DuFV/rgikOERcDX/vXOV7nyjkWpvXv34nk30gJpZjG6V54+fRqPoJCWQE+ePInV0Q91jieA4TFa58+fL5+V95g6TjuqtNWxqju+1cG3h1x551c6H9X223nstvKPsGiDFdFIK78olSu/hKZYbBSLYlEsikWxKBYbxaJYFOvLFKvw1avCvDw2Je39/+5Koljkm4ViEYpFKBahWBSLUCxCsQjFoliEYhGKRSgWIRSLUCxCsQihWIRiEYpFCMUiFItQLEIoFqFYhGIRQrEIxSIUixCKRSgWoViEUCxCsQjFIoRiEYpFKBYhFItQLEKxCKFYhGIRikUIxSIUi1AsQigWoViEYhFCsQjFIhSLEIpFKBb5BsXKzMws/PL+tWry9QKdIJVJdnZ2bm4u3w5iLHJyciCVSX5+flZWFtxi3CIfH6sgEnTCgQleFxQUQDGEr1uEfARQCCKJCPVfs7q/gTbG/uYAAAAASUVORK5CYII=", + "description": "Allows to display state of the GPIO for target device using latest attribute values. You should set the label of the selected data key to GPIO pin number (e.g. '1') and use boolean values for widget to display the data.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 2, + "resources": [], + "templateHtml": "
\n
\n
\n
\n {{ cell.label }}\n
\n {{cell.pin}}\n \n \n \n \n {{cell.pin}}\n
\n {{ cell.label }}\n
\n
\n \n \n \n
\n
\n
\n
", + "templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.gpio-panel section[fxflex] {\n min-width: 0px;\n}\n\n\n.gpio-panel tb-led-light > div {\n margin: auto;\n}\n\n.led-panel {\n margin: 0;\n width: 66px;\n min-width: 66px;\n}\n\n.led-container {\n width: 48px;\n min-width: 48px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.led-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.led-panel.col-1 .pin {\n margin-right: auto;\n \n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}", + "controllerScript": "var namespace;\nvar cssParser = new cssjs();\n\nself.onInit = function() {\n var utils = self.ctx.$injector.get(self.ctx.servicesMap.get('utils'));\n namespace = 'gpio-panel-' + utils.guid();\n cssParser.testMode = false;\n cssParser.cssPreviewNamespace = namespace;\n self.ctx.$container.addClass(namespace);\n self.ctx.ngZone.run(function() {\n init(); \n });\n}\n\nfunction init() {\n var i, gpio;\n \n var scope = self.ctx.$scope;\n var settings = self.ctx.settings;\n \n scope.gpioList = [];\n scope.gpioByPin = {};\n for (var g = 0; g < settings.gpioList.length; g++) {\n gpio = settings.gpioList[g];\n scope.gpioList.push(\n {\n row: gpio.row,\n col: gpio.col,\n pin: gpio.pin,\n label: gpio.label,\n enabled: false,\n colorOn: tinycolor(gpio.color).lighten(20).toHexString(),\n colorOff: tinycolor(gpio.color).darken().toHexString()\n }\n );\n scope.gpioByPin[gpio.pin] = scope.gpioList[scope.gpioList.length-1];\n }\n\n scope.ledPanelBackgroundColor = settings.ledPanelBackgroundColor || tinycolor('green').lighten(2).toRgbString();\n\n scope.gpioCells = {};\n var rowCount = 0;\n for (i = 0; i < scope.gpioList.length; i++) {\n gpio = scope.gpioList[i];\n scope.gpioCells[gpio.row+'_'+gpio.col] = gpio;\n rowCount = Math.max(rowCount, gpio.row+1);\n }\n \n scope.prefferedRowHeight = 32;\n scope.rows = [];\n for (i = 0; i < rowCount; i++) {\n var row = [];\n for (var c =0; c<2;c++) {\n if (scope.gpioCells[i+'_'+c]) {\n row[c] = scope.gpioCells[i+'_'+c];\n } else {\n row[c] = null;\n }\n }\n scope.rows.push(row);\n } \n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n var changed = false;\n for (var d = 0; d < self.ctx.data.length; d++) {\n var cellData = self.ctx.data[d];\n var dataKey = cellData.dataKey;\n var gpio = self.ctx.$scope.gpioByPin[dataKey.label];\n if (gpio) {\n var enabled = false;\n if (cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length - 1];\n enabled = (tvPair[1] === true || tvPair[1] === 'true');\n }\n if (gpio.enabled != enabled) {\n changed = true;\n gpio.enabled = enabled;\n }\n }\n }\n if (changed) {\n self.ctx.detectChanges();\n } \n}\n\nself.onResize = function() {\n var rowCount = self.ctx.$scope.rows.length;\n var prefferedRowHeight = (self.ctx.height - 35)/rowCount;\n prefferedRowHeight = Math.min(32, prefferedRowHeight);\n prefferedRowHeight = Math.max(12, prefferedRowHeight);\n self.ctx.$scope.prefferedRowHeight = prefferedRowHeight;\n \n var ratio = prefferedRowHeight/32;\n \n var css = '.gpio-left-label, .gpio-right-label {\\n' +\n ' font-size: ' + 16*ratio+'px;\\n'+\n '}\\n';\n var pinsFontSize = Math.max(9, 12*ratio);\n css += '.pin {\\n' +\n ' font-size: ' + pinsFontSize+'px;\\n'+\n '}\\n';\n \n cssParser.createStyleElement(namespace, css); \n \n self.ctx.detectChanges();\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-gpio-panel-widget-settings", + "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"gpioList\":[{\"pin\":1,\"label\":\"GPIO 1\",\"row\":0,\"col\":0,\"color\":\"#008000\",\"_uniqueKey\":0},{\"pin\":2,\"label\":\"GPIO 2\",\"row\":0,\"col\":1,\"color\":\"#ffff00\",\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 3\",\"row\":1,\"col\":0,\"color\":\"#cf006f\",\"_uniqueKey\":2}],\"ledPanelBackgroundColor\":\"#b71c1c\"},\"title\":\"Basic GPIO Panel\",\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"1\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.22518255793320163,\"funcBody\":\"var period = time % 1500;\\nreturn period < 500;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"2\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.7008206860666621,\"funcBody\":\"var period = time % 1500;\\nreturn period >= 500 && period < 1000;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"3\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.42600325102193426,\"funcBody\":\"var period = time % 1500;\\nreturn period >= 1000;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}}}" + }, + "externalId": null, + "tags": [ + "pin", + "pins", + "board" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/battery_level.json b/application/src/main/data/json/system/widget_types/battery_level.json new file mode 100644 index 0000000000..b9e2d943d9 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/battery_level.json @@ -0,0 +1,32 @@ +{ + "fqn": "battery_level", + "name": "Battery level", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAilBMVEXg4ODf39/g4OAAAADg4ODf39////9c35Dg4ODL9dz5/vzX9+QhISHl+u3f+enB89XC89XHx8c9PT2QkJCu8MiF56x0dHRw453j4+Px8fFYWFisrKz0/fcvLy/S9uDq+/GCgoK6uro8PDzV1dVKSkpw457O9t6enp7i+etmZmarq6tm4ZeZ67p65aQon2vGAAAABnRSTlPvIL8Ar7DvmsykAAAEcklEQVR42u3dbVebMBjG8Tq3Sym3zARlSSA8ax+2ff+vtyCds666oOsSuvxf5CCnL/o7gUIRThfnZx8Wl3Pv49n54mwhMfukYXw8AYeRmM0KJ9HiVCCXAeJZAeJbAeJbAeJbAeJbAeJb/wOkzky5wDx6BcJJKU1cPqHxDmh4Aw97DcLNkFEO06jJiAH5uAaPQD++Yf4JsqKVGTRRJqA06bo2gwJYb1ZJMF5z6ngNs5TCQXYQIRjXAoz6JqUNGkUpYxllDQS1eU01GJHKoLVETQwOsoLQUAp0uRhYTzetjMwqpQ0kA5Ca9byFg+xnZEMNwLKe0x6kJ6WUJsEGqOHUwogcZL+PCNogp75h+zPCKRuSIwRKr9xsWfYQZiAZdc83rc2wChI7SEq6hYMsITpNa05bpFQ3NRlITTVDQ4oZAG8apX5CJDnasux3drUF5IZI9SQhWsog28G0bYn6boSYFHVwl/25lhQYE3I3mFHgV6rFm/LspLGraYuJeQlRpJyfp/wViOjgvP/h+8i8ChDfChDfChDfChDfChDfChDfChDfejekiKOnxUvY5RtkGT3r3lLiGySJnreGVZ5BzIR4MiXvgxRl9HtlAYs8gBTruBy7jw53X47Fycsm55BlYt5obJeBJhYbmhPIsoziwn7ykqi0kLiAlNEVplRZSFxArgZH/O3WsmsjsZA7gJQlcHNh341hH/48dgspogrVxZTM66MKB3ILqaIC8STINZaHj/VuIVdvgiQ4kGvIMkCeFSABEiABEiABEiAm95BqEiT2F4JvExy38BiCm+9fLLspvIZMKUD+BSS5sa3yGnI95eKDx5Di7mJChb+QkzkgBsh+ARIgARIgM4KINMdQrnifwiRTzlU+M4hcKaL0wUE6aykzSxnpTUv5vCAp6X6EcBKQLTEwamEGjbEmFXOAMIb0AdKRMmNNq+HvnWuo7jMuwbyHmMY3zh4gOdW7508VMQz1DJyJVhwZkkyCxBaQ7BlEpVJ3fIsjQ3A7wXGHN0C6Vqd5hqNDim/WjttqKmSX6MUsvrM/7uy75ZpW+5AsTTXfzgUiSZtxQ83DrACaJMbyVpJo+LEhxWfLoupVCBSlXUOthNSUi9podnEhNNixIcnXC9u+Xr8KES0R6cYsbbVZevzAlQxos74+MuTO2mEkxUGIYAIPNatcjmuaFdt/SdPM4IA4dBJnv6YACZAACZAACZAAMQVIgLzv4kPkL2TaxQeP/xmK6vuEiw8+Qwwltqvy/IaBk7nzAUlsmd+Q6s5+X098hky6+HAydz54fBw5mSN7gOwXIAESIAESIAEyZ8iJPBlaRdXkiw+FxXPgDp6eXk87jb8rkJhZPJBTyPhw+tpe8nWNZVniUI4hRRQvDeezXcNcJBaPszuA4GqQ2LeMLZ5mdwIxkigpiqVV1fr+xY8s5xBUZWRfWeGF3EOAorqyqyrwYj5AvCxAfCtAfCtAfCtAfCtAfOtyscBJdLn44PyHKv5G8tPp/Ij2+dnHy7m3+HB2/gO3i0/vBj05fgAAAABJRU5ErkJggg==", + "description": "Displays the current battery level of the device.", + "descriptor": { + "type": "latest", + "sizeX": 2.5, + "sizeY": 2.5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.batteryLevelWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.batteryLevelWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '200px',\n previewHeight: '200px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'batteryLevel', label: 'batteryLevel', type: 'timeseries' }];\n }\n };\n};\n\nself.actionSources = function() {\n return {\n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-battery-level-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-battery-level-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"batteryLevel\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"layout\":\"vertical_solid\",\"showValue\":true,\"autoScaleValueSize\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":20,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"24px\"},\"valueColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"batteryLevelColor\":{\"type\":\"range\",\"color\":\"rgb(224, 224, 224)\",\"rangeList\":[{\"from\":null,\"to\":25,\"color\":\"rgba(227, 71, 71, 1)\"},{\"from\":25,\"to\":50,\"color\":\"rgba(246, 206, 67, 1)\"},{\"from\":50,\"to\":null,\"color\":\"rgba(92, 223, 144, 1)\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"batteryShapeColor\":{\"type\":\"range\",\"color\":\"rgba(224, 224, 224, 0.32)\",\"rangeList\":[{\"from\":null,\"to\":25,\"color\":\"rgba(227, 71, 71, 0.32)\"},{\"from\":25,\"to\":50,\"color\":\"rgba(246, 206, 67, 0.32)\"},{\"from\":50,\"to\":null,\"color\":\"rgba(92, 223, 144, 0.32)\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"sectionsCount\":4},\"title\":\"Battery level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:battery-high\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "accumulator", + "capacity", + "lithium", + "lithium-ion", + "power cell", + "energy cell", + "cell" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/carbon_monoxide__co__card.json b/application/src/main/data/json/system/widget_types/carbon_monoxide__co__card.json new file mode 100644 index 0000000000..955c2f2dc3 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/carbon_monoxide__co__card.json @@ -0,0 +1,37 @@ +{ + "fqn": "carbon_monoxide_co_card", + "name": "Carbon monoxide (CO) card", + "deprecated": false, + "image": "tb-image:Q08tdmFsdWUtY2FyZC5zdmc=:Q08tdmFsdWUtY2FyZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMTIiIHJ4PSIzLjUiLz48cGF0aCBmaWxsPSIjRDgxODM4IiBkPSJNMjkuMjk2IDMwLjc5NGEyLjYyMyAyLjYyMyAwIDAgMC0yLjYxNiAyLjYxNnY3Ljg0N2EyLjYxNiAyLjYxNiAwIDAgMCAyLjYxNiAyLjYxNmgzLjkyNHYtMi42MTZoLTMuOTI0VjMzLjQxaDMuOTI0di0yLjYxNmgtMy45MjRabTcuODQ3IDBhMi42MjMgMi42MjMgMCAwIDAtMi42MTUgMi42MTZ2Ny44NDdhMi42MTYgMi42MTYgMCAwIDAgMi42MTUgMi42MTZoMi42MTZhMi42MDcgMi42MDcgMCAwIDAgMi42MTYtMi42MTZWMzMuNDFhMi42MTUgMi42MTUgMCAwIDAtMi42MTYtMi42MTZoLTIuNjE2Wm0wIDIuNjE2aDIuNjE2djcuODQ3aC0yLjYxNlYzMy40MVoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii44NyIgZD0iTTYyLjg5OCAzMS44MjVoMS41ODdhMy42NjIgMy42NjIgMCAwIDEtLjUwOCAxLjYxOGMtLjI4Ny40Ny0uNjkxLjg0LTEuMjEyIDEuMTExLS41Mi4yNy0xLjE1My40MDYtMS44OTguNDA2YTMuNzYgMy43NiAwIDAgMS0xLjU0Mi0uMzA0IDMuMzgyIDMuMzgyIDAgMCAxLTEuMTc1LS44NzYgMy45NjMgMy45NjMgMCAwIDEtLjc0OS0xLjM3OCA1Ljk4NSA1Ljk4NSAwIDAgMS0uMjU0LTEuODAzdi0uNzY4YzAtLjY2NC4wODctMS4yNjUuMjYtMS44MDIuMTc4LS41MzguNDMyLS45OTcuNzYyLTEuMzc4LjMzLS4zODUuNzI2LS42NzkgMS4xODctLjg4MmEzLjg4IDMuODggMCAwIDEgMS41NjgtLjMwNWMuNzM3IDAgMS4zNTkuMTM2IDEuODY3LjQwNi41MDcuMjcxLjkuNjQ2IDEuMTggMS4xMjQuMjg0LjQ3OC40NTcgMS4wMjYuNTIgMS42NDRoLTEuNTg2YTIuOTM1IDIuOTM1IDAgMCAwLS4yOC0xLjAyMiAxLjQ0IDEuNDQgMCAwIDAtLjYyMi0uNjQ3Yy0uMjc1LS4xNTMtLjYzNC0uMjI5LTEuMDc5LS4yMjktLjM2NCAwLS42ODEuMDY4LS45NTIuMjAzLS4yNy4xMzYtLjQ5Ny4zMzQtLjY3OS41OTctLjE4Mi4yNjItLjMyLjU4Ni0uNDEzLjk3MS0uMDg5LjM4LS4xMzMuODE3LS4xMzMgMS4zMDh2Ljc4YzAgLjQ2Ni4wNC44OS4xMiAxLjI3LjA4NS4zNzcuMjEyLjcuMzgyLjk3MS4xNzMuMjcxLjM5My40OC42Ni42MjkuMjY2LjE0OC41ODYuMjIyLjk1OC4yMjIuNDUzIDAgLjgxOS0uMDcyIDEuMDk4LS4yMTYuMjg0LS4xNDQuNDk4LS4zNTMuNjQxLS42MjkuMTQ5LS4yNzkuMjQ2LS42Mi4yOTItMS4wMjFabTcuMDA0IDEuNjN2LTMuMjc0YzAtLjI0Ni0uMDQ0LS40NTgtLjEzMy0uNjM1YS45MzMuOTMzIDAgMCAwLS40MDYtLjQxM2MtLjE3OC0uMDk3LS40MDItLjE0Ni0uNjczLS4xNDYtLjI1IDAtLjQ2Ni4wNDItLjY0Ny4xMjdhMS4wNDcgMS4wNDcgMCAwIDAtLjQyNi4zNDMuODI3LjgyNyAwIDAgMC0uMTUyLjQ4OUg2NS45NGMwLS4yNzEuMDY2LS41MzQuMTk3LS43ODguMTMxLS4yNTMuMzIyLS40OC41NzItLjY3OWEyLjg1IDIuODUgMCAwIDEgLjg5NS0uNDcgMy43MjIgMy43MjIgMCAwIDEgMS4xNjctLjE3Yy41MTcgMCAuOTc0LjA4NiAxLjM3Mi4yNi40MDIuMTczLjcxNy40MzUuOTQ1Ljc4Ny4yMzMuMzQ3LjM1Ljc4Mi4zNSAxLjMwN3YzLjA1M2MwIC4zMTMuMDIuNTk1LjA2My44NDUuMDQ3LjI0NS4xMTIuNDU5LjE5Ny42NHYuMTAySDcwLjEzYTIuNzA1IDIuNzA1IDAgMCAxLS4xNzEtLjYyOCA1LjEzIDUuMTMgMCAwIDEtLjA1OC0uNzVabS4yMjItMi43OTguMDEzLjk0NUg2OS4wNGEzLjA1IDMuMDUgMCAwIDAtLjc0OS4wODNjLS4yMTYuMDUtLjM5Ni4xMjctLjU0LjIyOGEuOTkyLjk5MiAwIDAgMC0uNDMxLjg1N2MwIC4xODIuMDQyLjM1LjEyNy41MDJhLjg5Ljg5IDAgMCAwIC4zNjguMzQ5Yy4xNjUuMDg0LjM2NC4xMjcuNTk3LjEyNy4zMTMgMCAuNTg2LS4wNjQuODE5LS4xOS4yMzYtLjEzMi40MjMtLjI5LjU1OC0uNDc3LjEzNi0uMTkuMjA3LS4zNy4yMTYtLjU0bC40OTUuNjhjLS4wNS4xNzQtLjEzNy4zNi0uMjYuNTU5YTIuNzEgMi43MSAwIDAgMS0uNDgzLjU3Yy0uMTk0LjE3OS0uNDMuMzI1LS43MDQuNDM5LS4yNzEuMTE0LS41ODQuMTcxLS45NC4xNzFhMi42MiAyLjYyIDAgMCAxLTEuMi0uMjY2IDIuMTM0IDIuMTM0IDAgMCAxLS44MjUtLjczIDEuODkzIDEuODkzIDAgMCAxLS4yOTgtMS4wNDhjMC0uMzY0LjA2OC0uNjg1LjIwMy0uOTY0LjE0LS4yODQuMzQzLS41MjEuNjEtLjcxMi4yNy0uMTkuNi0uMzM0Ljk5LS40MzEuMzktLjEwMi44MzQtLjE1MiAxLjMzMy0uMTUyaDEuMlptNC43LTEuMzg0djUuNTZoLTEuNTN2LTYuODY4aDEuNDZsLjA3IDEuMzA4Wm0yLjEwMS0xLjM1Mi0uMDEyIDEuNDIyYTMuOTYyIDMuOTYyIDAgMCAwLS42MjItLjA1Yy0uMjYzLS4wMDEtLjQ5NC4wMzctLjY5Mi4xMTNhMS4zMDQgMS4zMDQgMCAwIDAtLjUwMi4zMTdjLS4xMzEuMTM2LS4yMzMuMy0uMzA0LjQ5NmEyLjIxMyAyLjIxMyAwIDAgMC0uMTI4LjY1M2wtLjM0OS4wMjZjMC0uNDMyLjA0My0uODMyLjEyNy0xLjIuMDg1LS4zNjguMjEyLS42OTIuMzgxLS45NzEuMTc0LS4yOC4zOS0uNDk3LjY0OC0uNjU0LjI2Mi0uMTU3LjU2NS0uMjM1LjkwNy0uMjM1LjA5MyAwIC4xOTMuMDA5LjI5OS4wMjYuMTEuMDE3LjE5Mi4wMzYuMjQ3LjA1N1ptMS4yMTUtMi44MzhoMS41M3Y4LjI4NGwtLjE0NiAxLjQ2Nkg3OC4xNHYtOS43NVptNi4wMTEgNi4yNTN2LjEzM2MwIC41MDgtLjA1Ny45NzUtLjE3MSAxLjQwMy0uMTEuNDIzLS4yOC43OTEtLjUwOCAxLjEwNGEyLjM1MiAyLjM1MiAwIDAgMS0uODM4LjczYy0uMzMuMTctLjcxMy4yNTQtMS4xNDkuMjU0LS40MjcgMC0uOC0uMDgtMS4xMTctLjI0YTIuMjIyIDIuMjIyIDAgMCAxLS44LS42ODcgMy44MDIgMy44MDIgMCAwIDEtLjUxNC0xLjA2IDYuOTk4IDYuOTk4IDAgMCAxLS4yOC0xLjM1OHYtLjQyNWMuMDU2LS41LjE0OS0uOTUyLjI4LTEuMzU5LjEzMS0uNDEuMzAzLS43NjQuNTE0LTEuMDYuMjE2LS4zLjQ4LS41My43OTQtLjY5Mi4zMTctLjE2LjY4OC0uMjQgMS4xMS0uMjQuNDQgMCAuODI4LjA4NCAxLjE2Mi4yNTMuMzM5LjE3LjYyLjQxLjg0NC43MjQuMjI1LjMwOS4zOTIuNjc3LjUwMiAxLjEwNC4xMTQuNDI4LjE3MS45LjE3MSAxLjQxNlptLTEuNTMuMTMzdi0uMTMzYzAtLjMxLS4wMjUtLjU5OS0uMDc2LS44N2EyLjI4NCAyLjI4NCAwIDAgMC0uMjU0LS43MjQgMS4yNzIgMS4yNzIgMCAwIDAtLjQ3LS40ODhjLS4xOTQtLjEyMy0uNDM3LS4xODQtLjczLS4xODQtLjI3IDAtLjUwMy4wNDYtLjY5Ny4xNC0uMTk1LjA5Mi0uMzU4LjIyLS40OS4zOC0uMTMuMTYxLS4yMzQuMzQ3LS4zMS41NTktLjA3Mi4yMTEtLjEyLjQ0LS4xNDYuNjg1djEuMTVjLjAzOC4zMTcuMTE4LjYwOS4yNDEuODc1LjEyNy4yNjMuMzA1LjQ3NC41MzMuNjM1LjIyOS4xNTcuNTIzLjIzNS44ODMuMjM1LjI4MyAwIC41MjItLjA1Ny43MTctLjE3Mi4xOTQtLjExNC4zNDktLjI3My40NjMtLjQ3Ni4xMTktLjIwNy4yMDMtLjQ0OC4yNTQtLjcyMy4wNTUtLjI3NS4wODMtLjU3Mi4wODMtLjg4OVptMi43ODMuMDA2di0uMTQ2YzAtLjQ5NS4wNzItLjk1NC4yMTYtMS4zNzcuMTQ0LS40MjcuMzUxLS43OTguNjIyLTEuMTEuMjc1LS4zMTguNjEtLjU2NCAxLjAwMy0uNzM3YTMuMjY1IDMuMjY1IDAgMCAxIDEuMzQ2LS4yNjdjLjUwMyAwIC45NTIuMDkgMS4zNDYuMjY3LjM5Ny4xNzMuNzM0LjQxOSAxLjAwOS43MzYuMjc1LjMxMy40ODQuNjg0LjYyOCAxLjExMS4xNDQuNDIzLjIxNi44ODIuMjE2IDEuMzc3di4xNDZjMCAuNDk1LS4wNzIuOTU1LS4yMTYgMS4zNzhhMy4yOTYgMy4yOTYgMCAwIDEtLjYyOCAxLjExYy0uMjc1LjMxNC0uNjEuNTYtMS4wMDMuNzM3LS4zOTQuMTczLS44NC4yNi0xLjM0LjI2YTMuMzUgMy4zNSAwIDAgMS0xLjM1Mi0uMjYgMi45MTIgMi45MTIgMCAwIDEtMS4wMDItLjczNiAzLjI5NyAzLjI5NyAwIDAgMS0uNjI5LTEuMTExIDQuMjUyIDQuMjUyIDAgMCAxLS4yMTYtMS4zNzhabTEuNTMtLjE0NnYuMTQ2YzAgLjMxLjAzMi42MDEuMDk1Ljg3Ni4wNjQuMjc1LjE2My41MTcuMjk5LjcyNC4xMzUuMjA3LjMwOS4zNy41Mi40ODkuMjEyLjExOC40NjMuMTc4Ljc1NS4xNzguMjg0IDAgLjUzLS4wNi43MzctLjE3OC4yMTEtLjExOS4zODUtLjI4Mi41Mi0uNDg5LjEzNi0uMjA3LjIzNS0uNDQ5LjI5OS0uNzI0LjA2Ny0uMjc1LjEwMS0uNTY3LjEwMS0uODc2di0uMTQ2YTMuNTUgMy41NSAwIDAgMC0uMTAxLS44NjMgMi4yMTggMi4yMTggMCAwIDAtLjMwNS0uNzMgMS40NTcgMS40NTcgMCAwIDAtLjUyLS40OTUgMS40MzMgMS40MzMgMCAwIDAtLjc0My0uMTg0Yy0uMjg4IDAtLjUzOC4wNjEtLjc1LjE4NGExLjQ3NSAxLjQ3NSAwIDAgMC0uNTEzLjQ5NSAyLjMwNCAyLjMwNCAwIDAgMC0uMjk5LjczYy0uMDYzLjI3MS0uMDk1LjU1OS0uMDk1Ljg2M1ptNy45MDUtMS44OTh2NS40MDJoLTEuNTN2LTYuODY4aDEuNDQybC4wODggMS40NjZabS0uMjczIDEuNzE0LS40OTUtLjAwNmMuMDA1LS40ODcuMDcyLS45MzMuMjAzLTEuMzQuMTM2LS40MDYuMzIyLS43NTUuNTU5LTEuMDQ3LjI0MS0uMjkyLjUyOS0uNTE2Ljg2My0uNjczLjMzNS0uMTYuNzA3LS4yNCAxLjExOC0uMjQuMzMgMCAuNjI4LjA0Ni44OTUuMTM5LjI3LjA4OS41MDEuMjM1LjY5MS40MzguMTk1LjIwMy4zNDMuNDY3LjQ0NS43OTMuMTAxLjMyMi4xNTIuNzE4LjE1MiAxLjE4N3Y0LjQzN2gtMS41MzZWMzAuMzljMC0uMzMtLjA0OS0uNTktLjE0Ni0uNzhhLjgxNi44MTYgMCAwIDAtLjQxMy0uNDEzIDEuNTQ4IDEuNTQ4IDAgMCAwLS42NjYtLjEyNyAxLjQ4IDEuNDggMCAwIDAtLjcwNS4xNjVjLS4yMDcuMTEtLjM4My4yNi0uNTI3LjQ1LS4xNC4xOS0uMjQ3LjQxLS4zMjMuNjYtLjA3Ni4yNS0uMTE1LjUxNy0uMTE1LjhabTExLjI2LTEuNzgzdjUuNDcxaC0xLjUzdi02Ljg2OGgxLjQ0MWwuMDg5IDEuMzk3Wm0tLjI0OCAxLjc4My0uNTItLjAwNmMwLS40NzQuMDU5LS45MTIuMTc3LTEuMzE0LjExOS0uNDAyLjI5Mi0uNzUxLjUyMS0xLjA0Ny4yMjgtLjMuNTEyLS41MzEuODUtLjY5MmEyLjcwNyAyLjcwNyAwIDAgMSAxLjE4Ny0uMjQ4Yy4zMTQgMCAuNTk5LjA0Ny44NTcuMTQuMjYzLjA4OS40ODkuMjMuNjguNDI1LjE5NC4xOTUuMzQyLjQ0NS40NDQuNzUuMTA2LjMwNC4xNTkuNjcyLjE1OSAxLjEwNHY0LjU3NmgtMS41M1YzMC4zOWMwLS4zMzQtLjA1MS0uNTk3LS4xNTMtLjc4N2EuODQzLjg0MyAwIDAgMC0uNDI1LS40MDYgMS41NDEgMS41NDEgMCAwIDAtLjY1NC0uMTI3Yy0uMjg3IDAtLjUzMy4wNTUtLjczNi4xNjUtLjE5OS4xMS0uMzYyLjI2LS40ODkuNDVzLS4yMi40MS0uMjc5LjY2YTMuNDcgMy40NyAwIDAgMC0uMDg5LjhabTQuMjU5LS40MDYtLjcxNy4xNTljMC0uNDE1LjA1Ny0uODA2LjE3Mi0xLjE3NWEyLjk3IDIuOTcgMCAwIDEgLjUxNC0uOTc3IDIuNDEgMi40MSAwIDAgMSAuODQ0LS42NjdjLjMzNC0uMTYuNzE3LS4yNCAxLjE0OS0uMjQuMzUxIDAgLjY2NC4wNDguOTM5LjE0NS4yOC4wOTMuNTE3LjI0MS43MTEuNDQ0LjE5NS4yMDQuMzQzLjQ2OC40NDUuNzk0LjEwMS4zMjIuMTUyLjcxLjE1MiAxLjE2OHY0LjQ0M2gtMS41MzZ2LTQuNDVjMC0uMzQ2LS4wNTEtLjYxNS0uMTUzLS44MDVhLjc4OC43ODggMCAwIDAtLjQxOS0uMzk0IDEuNjggMS42OCAwIDAgMC0uNjUzLS4xMTRjLS4yMzcgMC0uNDQ3LjA0NC0uNjI5LjEzM2ExLjI0NSAxLjI0NSAwIDAgMC0uNDUuMzYyIDEuNjE3IDEuNjE3IDAgMCAwLS4yOC41MjcgMi4yNjggMi4yNjggMCAwIDAtLjA4OS42NDdabTUuNzQxLjczNnYtLjE0NmMwLS40OTUuMDcyLS45NTQuMjE2LTEuMzc3LjE0NC0uNDI3LjM1MS0uNzk4LjYyMi0xLjExYTIuODAzIDIuODAzIDAgMCAxIDEuMDAzLS43MzcgMy4yNjUgMy4yNjUgMCAwIDEgMS4zNDYtLjI2N2MuNTAzIDAgLjk1Mi4wOSAxLjM0NS4yNjcuMzk4LjE3My43MzUuNDE5IDEuMDEuNzM2LjI3NS4zMTMuNDg0LjY4NC42MjggMS4xMTEuMTQ0LjQyMy4yMTYuODgyLjIxNiAxLjM3N3YuMTQ2YzAgLjQ5NS0uMDcyLjk1NS0uMjE2IDEuMzc4YTMuMjk4IDMuMjk4IDAgMCAxLS42MjggMS4xMWMtLjI3NS4zMTQtLjYxLjU2LTEuMDAzLjczN2EzLjI5IDMuMjkgMCAwIDEtMS4zNC4yNmMtLjUwMyAwLS45NTQtLjA4Ny0xLjM1Mi0uMjZhMi45MTcgMi45MTcgMCAwIDEtMS4wMDMtLjczNiAzLjI5OCAzLjI5OCAwIDAgMS0uNjI4LTEuMTExIDQuMjUgNC4yNSAwIDAgMS0uMjE2LTEuMzc4Wm0xLjUzLS4xNDZ2LjE0NmMwIC4zMS4wMzIuNjAxLjA5NS44NzYuMDY0LjI3NS4xNjMuNTE3LjI5OC43MjQuMTM2LjIwNy4zMDkuMzcuNTIxLjQ4OS4yMTIuMTE4LjQ2My4xNzguNzU1LjE3OC4yODQgMCAuNTI5LS4wNi43MzctLjE3OC4yMTEtLjExOS4zODUtLjI4Mi41Mi0uNDg5LjEzNi0uMjA3LjIzNS0uNDQ5LjI5OS0uNzI0LjA2Ny0uMjc1LjEwMS0uNTY3LjEwMS0uODc2di0uMTQ2YzAtLjMwNC0uMDM0LS41OTItLjEwMS0uODYzYTIuMjQ0IDIuMjQ0IDAgMCAwLS4zMDUtLjczIDEuNDYgMS40NiAwIDAgMC0uNTIxLS40OTUgMS40MzEgMS40MzEgMCAwIDAtLjc0Mi0uMTg0Yy0uMjg4IDAtLjUzOC4wNjEtLjc0OS4xODRhMS40NzYgMS40NzYgMCAwIDAtLjUxNS40OTUgMi4zMjUgMi4zMjUgMCAwIDAtLjI5OC43M2MtLjA2My4yNzEtLjA5NS41NTktLjA5NS44NjNabTcuOTA1LTEuODk4djUuNDAyaC0xLjUzdi02Ljg2OGgxLjQ0MWwuMDg5IDEuNDY2Wm0tLjI3MyAxLjcxNC0uNDk1LS4wMDZjLjAwNC0uNDg3LjA3Mi0uOTMzLjIwMy0xLjM0LjEzNi0uNDA2LjMyMi0uNzU1LjU1OS0xLjA0Ny4yNDEtLjI5Mi41MjktLjUxNi44NjMtLjY3My4zMzQtLjE2LjcwNy0uMjQgMS4xMTctLjI0LjMzIDAgLjYyOS4wNDYuODk1LjEzOS4yNzEuMDg5LjUwMi4yMzUuNjkyLjQzOC4xOTUuMjAzLjM0My40NjcuNDQ1Ljc5My4xMDEuMzIyLjE1Mi43MTguMTUyIDEuMTg3djQuNDM3aC0xLjUzNlYzMC4zOWMwLS4zMy0uMDQ5LS41OS0uMTQ2LS43OGEuODE3LjgxNyAwIDAgMC0uNDEzLS40MTMgMS41NDggMS41NDggMCAwIDAtLjY2Ni0uMTI3IDEuNDggMS40OCAwIDAgMC0uNzA1LjE2NWMtLjIwNy4xMS0uMzgzLjI2LS41MjcuNDUtLjEzOS4xOS0uMjQ3LjQxLS4zMjQuNjYtLjA3Ni4yNS0uMTE0LjUxNy0uMTE0LjhabTUuOTUuMzN2LS4xNDZjMC0uNDk1LjA3Mi0uOTU0LjIxNi0xLjM3Ny4xNDQtLjQyNy4zNTEtLjc5OC42MjItMS4xMS4yNzUtLjMxOC42MS0uNTY0IDEuMDAzLS43MzdhMy4yNjkgMy4yNjkgMCAwIDEgMS4zNDYtLjI2N2MuNTA0IDAgLjk1Mi4wOSAxLjM0Ni4yNjdhMi44IDIuOCAwIDAgMSAxLjAwOS43MzZjLjI3NS4zMTMuNDg0LjY4NC42MjggMS4xMTEuMTQ0LjQyMy4yMTYuODgyLjIxNiAxLjM3N3YuMTQ2YzAgLjQ5NS0uMDcyLjk1NS0uMjE2IDEuMzc4YTMuMjk4IDMuMjk4IDAgMCAxLS42MjggMS4xMSAyLjkwNyAyLjkwNyAwIDAgMS0xLjAwMy43MzcgMy4yOSAzLjI5IDAgMCAxLTEuMzM5LjI2Yy0uNTA0IDAtLjk1NS0uMDg3LTEuMzUyLS4yNmEyLjkwNyAyLjkwNyAwIDAgMS0xLjAwMy0uNzM2IDMuMyAzLjMgMCAwIDEtLjYyOS0xLjExMSA0LjI1IDQuMjUgMCAwIDEtLjIxNi0xLjM3OFptMS41My0uMTQ2di4xNDZjMCAuMzEuMDMyLjYwMS4wOTUuODc2LjA2NC4yNzUuMTYzLjUxNy4yOTkuNzI0LjEzNS4yMDcuMzA5LjM3LjUyLjQ4OS4yMTIuMTE4LjQ2NC4xNzguNzU2LjE3OC4yODMgMCAuNTI5LS4wNi43MzYtLjE3OC4yMTItLjExOS4zODUtLjI4Mi41Mi0uNDg5LjEzNi0uMjA3LjIzNS0uNDQ5LjI5OS0uNzI0YTMuNjQgMy42NCAwIDAgMCAuMTAxLS44NzZ2LS4xNDZjMC0uMzA0LS4wMzMtLjU5Mi0uMTAxLS44NjNhMi4yMjIgMi4yMjIgMCAwIDAtLjMwNS0uNzMgMS40NTcgMS40NTcgMCAwIDAtLjUyLS40OTUgMS40MzUgMS40MzUgMCAwIDAtLjc0My0uMTg0Yy0uMjg4IDAtLjUzNy4wNjEtLjc0OS4xODRhMS40NzQgMS40NzQgMCAwIDAtLjUxNC40OTUgMi4zMDMgMi4zMDMgMCAwIDAtLjI5OS43M2MtLjA2My4yNzEtLjA5NS41NTktLjA5NS44NjNabTcuNDE3LTMuMzY0IDEuMzA3IDIuMjc5IDEuMzMzLTIuMjc5aDEuNjgybC0yLjA4MiAzLjM3IDIuMTY1IDMuNDk4aC0xLjY4MmwtMS4zOTctMi4zNzQtMS4zOTYgMi4zNzRoLTEuNjg5bDIuMTU5LTMuNDk3LTIuMDc2LTMuMzdoMS42NzZabTcuMjU4IDB2Ni44NjhoLTEuNTM3di02Ljg2OGgxLjUzN1ptLTEuNjM4LTEuODAzYzAtLjIzMi4wNzYtLjQyNS4yMjgtLjU3Ny4xNTctLjE1Ny4zNzMtLjIzNS42NDgtLjIzNS4yNzEgMCAuNDg0LjA3OC42NDEuMjM1YS43Ny43NyAwIDAgMSAuMjM1LjU3Ny43NjMuNzYzIDAgMCAxLS4yMzUuNTcyYy0uMTU3LjE1Mi0uMzcuMjI4LS42NDEuMjI4LS4yNzUgMC0uNDkxLS4wNzYtLjY0OC0uMjI4YS43NzYuNzc2IDAgMCAxLS4yMjgtLjU3MlptNy43NTMgNy4yNXYtOC4zMjloMS41MzZ2OS43NWgtMS4zOWwtLjE0Ni0xLjQyMlptLTQuNDY5LTEuOTM3di0uMTMzYzAtLjUyLjA2MS0uOTk0LjE4NC0xLjQyMi4xMjMtLjQzMS4zMDEtLjgwMi41MzMtMS4xMS4yMzMtLjMxNC41MTctLjU1My44NTEtLjcxOC4zMzQtLjE3LjcxMS0uMjU0IDEuMTMtLjI1NC40MTQgMCAuNzc4LjA4IDEuMDkyLjI0MS4zMTMuMTYxLjU3OS4zOTIuNzk5LjY5Mi4yMi4yOTYuMzk2LjY1Mi41MjcgMS4wNjcuMTMxLjQxLjIyNS44NjcuMjggMS4zN3YuNDI2YTYuODggNi44OCAwIDAgMS0uMjggMS4zNDYgMy42MTEgMy42MTEgMCAwIDEtLjUyNyAxLjA1M2MtLjIyLjI5Ny0uNDg4LjUyNS0uODA2LjY4Ni0uMzEzLjE2LS42NzkuMjQxLTEuMDk4LjI0MS0uNDE1IDAtLjc4OS0uMDg3LTEuMTIzLS4yNmEyLjQ4IDIuNDggMCAwIDEtLjg0NS0uNzMgMy40NTcgMy40NTcgMCAwIDEtLjUzMy0xLjEwNSA1LjAwOSA1LjAwOSAwIDAgMS0uMTg0LTEuMzlabTEuNTMtLjEzM3YuMTMzYzAgLjMxNC4wMjcuNjA1LjA4Mi44NzYuMDYuMjcxLjE1MS41MS4yNzMuNzE4LjEyMy4yMDMuMjgyLjM2NC40NzYuNDgyLjE5OS4xMTQuNDM2LjE3MS43MTEuMTcxLjM0NyAwIC42MzMtLjA3Ni44NTctLjIyOC4yMjQtLjE1Mi40LS4zNTguNTI3LS42MTYuMTMxLS4yNjIuMjItLjU1NC4yNjctLjg3NnYtMS4xNDlhMi44NTEgMi44NTEgMCAwIDAtLjE1OS0uNjk4IDEuODcgMS44NyAwIDAgMC0uMzExLS41NjUgMS4zMDcgMS4zMDcgMCAwIDAtLjQ4OS0uMzggMS41MyAxLjUzIDAgMCAwLS42NzktLjE0Yy0uMjc5IDAtLjUxNi4wNTktLjcxMS4xNzdhMS40MzggMS40MzggMCAwIDAtLjQ4Mi40OSAyLjM5MiAyLjM5MiAwIDAgMC0uMjczLjcyM2MtLjA2LjI3NS0uMDg5LjU2OS0uMDg5Ljg4MlptOS4zNTIgMy42MThjLS41MDcgMC0uOTY3LS4wODItMS4zNzctLjI0N2EzLjA0NiAzLjA0NiAwIDAgMS0xLjA0MS0uNzA1IDMuMTExIDMuMTExIDAgMCAxLS42NTQtMS4wNiAzLjcyIDMuNzIgMCAwIDEtLjIyOC0xLjMxNHYtLjI1NGMwLS41MzcuMDc4LTEuMDI0LjIzNC0xLjQ2LjE1Ny0uNDM2LjM3NS0uODA4LjY1NC0xLjExNy4yOC0uMzEzLjYxLS41NTIuOTktLjcxN2EzLjA4IDMuMDggMCAwIDEgMS4yMzgtLjI0OGMuNDkxIDAgLjkyMS4wODMgMS4yODkuMjQ4LjM2OC4xNjUuNjczLjM5OC45MTQuNjk4LjI0NS4yOTYuNDI3LjY1LjU0NiAxLjA2LjEyMy40MS4xODQuODYzLjE4NCAxLjM1OHYuNjU0aC01LjMwN3YtMS4wOThoMy43OTZ2LS4xMmEyLjEyOSAyLjEyOSAwIDAgMC0uMTY1LS43NzUgMS4zMDggMS4zMDggMCAwIDAtLjQ1MS0uNTg0Yy0uMjAzLS4xNDgtLjQ3NC0uMjIyLS44MTItLjIyMi0uMjU0IDAtLjQ4LjA1NS0uNjc5LjE2NWExLjM0IDEuMzQgMCAwIDAtLjQ4OS40NjMgMi40NDUgMi40NDUgMCAwIDAtLjMwNS43MzcgNC4xNzggNC4xNzggMCAwIDAtLjEwMS45NTh2LjI1NGMwIC4zLjA0LjU4LjEyLjgzOC4wODUuMjU0LjIwOC40NzYuMzY4LjY2Ni4xNjEuMTkuMzU2LjM0MS41ODQuNDUxLjIyOS4xMDYuNDg5LjE1OS43ODEuMTU5LjM2OCAwIC42OTYtLjA3NC45ODQtLjIyMi4yODgtLjE0OC41MzgtLjM1OC43NDktLjYyOWwuODA2Ljc4MWMtLjE0OC4yMTYtLjM0LjQyMy0uNTc3LjYyMmEyLjg5IDIuODkgMCAwIDEtLjg3LjQ3NiAzLjQ2MiAzLjQ2MiAwIDAgMS0xLjE4MS4xODRaIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuMzgiIGQ9Ik02MS4zNzQgNTAuOTE4di45MTVoLTMuMzYxdi0uOTE1aDMuMzZabS0zLjA0LTUuNzh2Ni42OTVINTcuMThWNDUuMTRoMS4xNTVabTYuODIyIDUuNjk4di0yLjM3M2MwLS4xNzgtLjAzMi0uMzMxLS4wOTYtLjQ2YS42NzcuNjc3IDAgMCAwLS4yOTQtLjI5OSAxLjAwNiAxLjAwNiAwIDAgMC0uNDg4LS4xMDVjLS4xOCAwLS4zMzcuMDMtLjQ2OS4wOTJhLjc1OC43NTggMCAwIDAtLjMwOC4yNDguNTk5LjU5OSAwIDAgMC0uMTEuMzU0aC0xLjEwNGMwLS4xOTYuMDQ4LS4zODYuMTQzLS41Ny4wOTUtLjE4NC4yMzMtLjM0OC40MTQtLjQ5Mi4xOC0uMTQ0LjM5Ny0uMjU4LjY0OC0uMzQuMjUxLS4wODMuNTMzLS4xMjUuODQ2LS4xMjUuMzc0IDAgLjcwNS4wNjMuOTkzLjE4OS4yOTEuMTI2LjUyLjMxNi42ODUuNTcuMTY5LjI1MS4yNTMuNTY3LjI1My45NDd2Mi4yMTJjMCAuMjI3LjAxNS40My4wNDYuNjExLjAzNC4xNzguMDgxLjMzMy4xNDMuNDY1di4wNzNoLTEuMTM2YTEuOTY1IDEuOTY1IDAgMCAxLS4xMjQtLjQ1NSAzLjcyOSAzLjcyOSAwIDAgMS0uMDQxLS41NDJabS4xNjEtMi4wMjguMDEuNjg1aC0uNzk2Yy0uMjA1IDAtLjM4Ni4wMi0uNTQyLjA2YTEuMTE1IDEuMTE1IDAgMCAwLS4zOTEuMTY1LjcxOC43MTggMCAwIDAtLjMxMy42MjFjMCAuMTMyLjAzLjI1My4wOTIuMzYzLjA2MS4xMDguMTUuMTkyLjI2Ny4yNTMuMTIuMDYxLjI2My4wOTIuNDMyLjA5Mi4yMjcgMCAuNDI1LS4wNDYuNTkzLS4xMzhhMS4yMiAxLjIyIDAgMCAwIC40MDUtLjM0NS43NS43NSAwIDAgMCAuMTU2LS4zOWwuMzU5LjQ5MWMtLjAzNy4xMjYtLjEuMjYxLS4xODkuNDA1YTEuOTU5IDEuOTU5IDAgMCAxLS4zNS40MTQgMS43MzUgMS43MzUgMCAwIDEtMS4xOS40NDFjLS4zMjUgMC0uNjE1LS4wNjQtLjg3LS4xOTNhMS41NDYgMS41NDYgMCAwIDEtLjU5Ny0uNTI5IDEuMzcxIDEuMzcxIDAgMCAxLS4yMTYtLjc1OGMwLS4yNjQuMDQ5LS40OTcuMTQ3LS43YTEuMzcgMS4zNyAwIDAgMSAuNDQyLS41MTRjLjE5Ni0uMTM4LjQzNS0uMjQyLjcxNy0uMzEzLjI4Mi0uMDczLjYwNC0uMTEuOTY1LS4xMWguODdabTUuMDk4IDEuNjc4YzAtLjExLS4wMjgtLjIxLS4wODMtLjI5OS0uMDU1LS4wOTItLjE2LS4xNzQtLjMxNy0uMjQ4YTMuMDc4IDMuMDc4IDAgMCAwLS42OC0uMjAyIDUuODY0IDUuODY0IDAgMCAxLS43MjctLjIwNyAyLjMwNSAyLjMwNSAwIDAgMS0uNTYtLjMgMS4yNTkgMS4yNTkgMCAwIDEtLjM2NC0uNDEzIDEuMTQ2IDEuMTQ2IDAgMCAxLS4xMjktLjU1MmMwLS4yMDIuMDQ0LS4zOTQuMTMzLS41NzUuMDktLjE4LjIxNi0uMzQuMzgyLS40NzguMTY2LS4xMzguMzY2LS4yNDYuNjAyLS4zMjYuMjQtLjA4LjUwNi0uMTIuOC0uMTIuNDE3IDAgLjc3NC4wNyAxLjA3Mi4yMTIuMy4xMzguNTMuMzI2LjY5LjU2NS4xNTkuMjM2LjIzOS41MDMuMjM5LjhoLTEuMTA5YS43MDcuNzA3IDAgMCAwLS4xLS4zNjcuNzA1LjcwNSAwIDAgMC0uMjk1LS4yOCAxLjAxIDEuMDEgMCAwIDAtLjQ5Ny0uMTExYy0uMTkgMC0uMzQ3LjAzLS40NzMuMDkyYS42NDguNjQ4IDAgMCAwLS4yNzYuMjMuNTg3LjU4NyAwIDAgMC0uMDQxLjUzOC41My41MyAwIDAgMCAuMTY1LjE3OWMuMDc3LjA1Mi4xODEuMTAxLjMxMy4xNDcuMTM1LjA0Ni4zMDMuMDkuNTA2LjEzMy4zOC4wOC43MDYuMTgzLjk3OS4zMDguMjc2LjEyMy40ODcuMjgyLjYzNC40NzkuMTQ4LjE5My4yMjEuNDM4LjIyMS43MzUgMCAuMjIxLS4wNDcuNDIzLS4xNDIuNjA3LS4wOTIuMTgxLS4yMjcuMzM5LS40MDUuNDc0YTIuMDMgMi4wMyAwIDAgMS0uNjQuMzA4Yy0uMjQ0LjA3NC0uNTIuMTEtLjgyNy4xMS0uNDUgMC0uODMyLS4wOC0xLjE0NS0uMjM5YTEuODI2IDEuODI2IDAgMCAxLS43MTItLjYyIDEuNDcgMS40NyAwIDAgMS0uMjQtLjc5MWgxLjA3MmEuODMuODMgMCAwIDAgLjE3LjQ5MmMuMTA0LjEyLjIzMy4yMDYuMzg2LjI2Mi4xNTcuMDUyLjMxOC4wNzguNDgzLjA3OC4yIDAgLjM2Ni0uMDI2LjUwMS0uMDc4YS43MjEuNzIxIDAgMCAwIC4zMDgtLjIyMS41MjcuNTI3IDAgMCAwIC4xMDYtLjMyMlptNC41MzItMy42Mjh2LjgxaC0yLjgwNXYtLjgxaDIuODA1Wm0tMS45OTUtMS4yMThoMS4xMDh2NC44MTljMCAuMTUzLjAyMS4yNy4wNjQuMzU0LjA0Ni4wOC4xMDkuMTMzLjE4OS4xNi4wOC4wMjguMTczLjA0Mi4yOC4wNDJhMi4yMDkgMi4yMDkgMCAwIDAgLjM5LS4wNDFsLjAwNS44NDZjLS4wOTEuMDI3LS4xOTkuMDUyLS4zMjEuMDczLS4xMi4wMjEtLjI1OC4wMzItLjQxNC4wMzItLjI1NSAwLS40OC0uMDQ0LS42NzYtLjEzM2EuOTk1Ljk5NSAwIDAgMS0uNDYtLjQ0NmMtLjExLS4yMDUtLjE2NS0uNDc4LS4xNjUtLjgxOFY0NS42NFptOC41OCA1LjAydi0zLjgwMmgxLjExNHY0Ljk3NWgtMS4wNDlsLS4wNjQtMS4xNzJabS4xNTctMS4wMzQuMzczLS4wMDljMCAuMzM0LS4wMzcuNjQyLS4xMS45MjRhMi4xNDMgMi4xNDMgMCAwIDEtLjM0LjczMWMtLjE1NC4yMDYtLjM1LjM2Ny0uNTkuNDgzYTEuOTkgMS45OSAwIDAgMS0uODYuMTdjLS4yNDEgMC0uNDY0LS4wMzUtLjY2Ni0uMTA1YTEuMzY3IDEuMzY3IDAgMCAxLS41MjQtLjMyNyAxLjQ4NiAxLjQ4NiAwIDAgMS0uMzM2LS41NzUgMi42NTUgMi42NTUgMCAwIDEtLjEyLS44NDZ2LTMuMjE0aDEuMTA5djMuMjI0YzAgLjE4LjAyMS4zMzIuMDY0LjQ1NWEuNzcuNzcgMCAwIDAgLjE3NS4yOWMuMDczLjA3My4xNi4xMjUuMjU3LjE1Ni4wOTkuMDMuMjAzLjA0Ni4zMTMuMDQ2LjMxNiAwIC41NjQtLjA2Mi43NDUtLjE4NC4xODQtLjEyNi4zMTQtLjI5NC4zOS0uNTA2LjA4LS4yMTIuMTItLjQ1LjEyLS43MTNabTMuNDAxLTEuODExdjUuOTMxaC0xLjEwOHYtNi44ODhoMS4wMmwuMDg4Ljk1N1ptMy4yNDIgMS40ODV2LjA5NmMwIC4zNjItLjA0My42OTgtLjEyOSAxLjAwN2EyLjQ2IDIuNDYgMCAwIDEtLjM3Mi44MDUgMS43MTUgMS43MTUgMCAwIDEtLjYwMy41MjljLS4yMzkuMTI1LS41MTUuMTg4LS44MjcuMTg4LS4zMSAwLS41ODEtLjA1Ni0uODE0LS4xN2ExLjY2NyAxLjY2NyAwIDAgMS0uNTg0LS40OTIgMi42NjkgMi42NjkgMCAwIDEtLjM4Ni0uNzQ1IDQuNzggNC43OCAwIDAgMS0uMjAzLS45NDdWNDkuMmMuMDQtLjM2NS4xMDgtLjY5Ni4yMDMtLjk5M2EyLjY5IDIuNjkgMCAwIDEgLjM4Ni0uNzY4Yy4xNi0uMjE1LjM1NC0uMzguNTg0LS40OTcuMjMtLjExNi40OTgtLjE3NS44MDQtLjE3NS4zMTMgMCAuNTkuMDYyLjgzMy4xODQuMjQyLjEyLjQ0Ni4yOTEuNjExLjUxNS4xNjYuMjIuMjkuNDg4LjM3My44LjA4Mi4zMS4xMjQuNjU1LjEyNCAxLjAzNVptLTEuMTA4LjA5NlY0OS4zYzAtLjIzLS4wMjItLjQ0My0uMDY1LS42NC0uMDQzLS4xOTgtLjExLS4zNzMtLjIwMi0uNTIzYS45OS45OSAwIDAgMC0uMzU0LS4zNS45NjQuOTY0IDAgMCAwLS41MS0uMTI5Yy0uMTk3IDAtLjM2NS4wMzQtLjUwNi4xMDJhLjk3Ljk3IDAgMCAwLS4zNTQuMjcgMS4yODIgMS4yODIgMCAwIDAtLjIyLjQxYy0uMDUzLjE1My0uMDkuMzItLjExMS41MDF2Ljg5MmMuMDM3LjIyLjEuNDIzLjE4OC42MDcuMDkuMTg0LjIxNS4zMzEuMzc3LjQ0Mi4xNjYuMTA3LjM3Ny4xNi42MzUuMTYuMTk5IDAgLjM3LS4wNDMuNTEtLjEyOC4xNDEtLjA4Ni4yNTYtLjIwNC4zNDUtLjM1NC4wOTItLjE1NC4xNi0uMzMuMjAyLS41My4wNDMtLjE5OC4wNjUtLjQxLjA2NS0uNjM0Wm01LjI4MSAxLjQwN3YtNi4wMzJoMS4xMTN2Ny4wNjJoLTEuMDA3bC0uMTA2LTEuMDNabS0zLjIzNy0xLjQwMnYtLjA5N2MwLS4zNzcuMDQ1LS43Mi4xMzQtMS4wMy4wODgtLjMxMi4yMTctLjU4LjM4Ni0uODA0LjE2OC0uMjI3LjM3NC0uNC42MTYtLjUyLjI0Mi0uMTIyLjUxNS0uMTg0LjgxOC0uMTg0LjMgMCAuNTY0LjA1OS43OTEuMTc1LjIyNy4xMTYuNDIuMjg0LjU4LjUwMWEyLjYgMi42IDAgMCAxIC4zODEuNzczYy4wOTUuMjk3LjE2My42MjguMjAzLjk5M3YuMzA4Yy0uMDQuMzU1LS4xMDguNjgtLjIwMy45NzVhMi42MiAyLjYyIDAgMCAxLS4zODEuNzYzYy0uMTYuMjE1LS4zNTQuMzgtLjU4NC40OTdhMS43MTkgMS43MTkgMCAwIDEtLjc5Ni4xNzRjLS4zIDAtLjU3Mi0uMDYzLS44MTQtLjE4OGExLjc5OSAxLjc5OSAwIDAgMS0uNjExLS41MjkgMi40ODkgMi40ODkgMCAwIDEtLjM4Ni0uOCAzLjYzNiAzLjYzNiAwIDAgMS0uMTM0LTEuMDA3Wm0xLjEwOC0uMDk3di4wOTdjMCAuMjI3LjAyLjQzOC4wNi42MzQuMDQzLjE5Ny4xMDkuMzcuMTk4LjUyLjA4OS4xNDcuMjA0LjI2NC4zNDUuMzUuMTQ0LjA4Mi4zMTYuMTI0LjUxNS4xMjQuMjUxIDAgLjQ1OC0uMDU2LjYyLS4xNjYuMTYzLS4xMS4yOS0uMjU5LjM4Mi0uNDQ2LjA5NS0uMTkuMTYtLjQwMS4xOTMtLjYzNHYtLjgzM2EyLjAzIDIuMDMgMCAwIDAtLjExNS0uNTA1IDEuMzUgMS4zNSAwIDAgMC0uMjI1LS40MS45NDguOTQ4IDAgMCAwLS4zNTQtLjI3NSAxLjExIDEuMTEgMCAwIDAtLjQ5Mi0uMTAyLjk3My45NzMgMCAwIDAtLjUxNS4xMyAxLjA0MyAxLjA0MyAwIDAgMC0uMzUuMzUzYy0uMDg4LjE1LS4xNTQuMzI1LS4xOTcuNTI0LS4wNDMuMi0uMDY1LjQxMy0uMDY1LjY0Wm03LjQwMiAxLjUzMnYtMi4zNzNjMC0uMTc4LS4wMzMtLjMzMS0uMDk3LS40NmEuNjc3LjY3NyAwIDAgMC0uMjk0LS4yOTkgMS4wMDcgMS4wMDcgMCAwIDAtLjQ4OC0uMTA1Yy0uMTggMC0uMzM3LjAzLS40NjkuMDkyYS43NTkuNzU5IDAgMCAwLS4zMDguMjQ4LjYuNiAwIDAgMC0uMTEuMzU0aC0xLjEwM2MwLS4xOTYuMDQ3LS4zODYuMTQyLS41Ny4wOTUtLjE4NC4yMzMtLjM0OC40MTQtLjQ5Mi4xOC0uMTQ0LjM5Ny0uMjU4LjY0OC0uMzQuMjUyLS4wODMuNTM0LS4xMjUuODQ2LS4xMjUuMzc0IDAgLjcwNS4wNjMuOTkzLjE4OS4yOTEuMTI2LjUyLjMxNi42ODUuNTcuMTY5LjI1MS4yNTMuNTY3LjI1My45NDd2Mi4yMTJjMCAuMjI3LjAxNi40My4wNDYuNjExLjAzNC4xNzguMDgxLjMzMy4xNDMuNDY1di4wNzNoLTEuMTM2YTEuOTYgMS45NiAwIDAgMS0uMTI0LS40NTUgMy43MTUgMy43MTUgMCAwIDEtLjA0MS0uNTQyWm0uMTYtMi4wMjguMDEuNjg1aC0uNzk2Yy0uMjA1IDAtLjM4Ni4wMi0uNTQyLjA2YTEuMTE2IDEuMTE2IDAgMCAwLS4zOTEuMTY1LjcyLjcyIDAgMCAwLS4zMTMuNjIxYzAgLjEzMi4wMy4yNTMuMDkyLjM2My4wNjEuMTA4LjE1LjE5Mi4yNjcuMjUzLjEyLjA2MS4yNjMuMDkyLjQzMi4wOTIuMjI3IDAgLjQyNS0uMDQ2LjU5My0uMTM4LjE3Mi0uMDk1LjMwNy0uMjEuNDA1LS4zNDVhLjc1Ljc1IDAgMCAwIC4xNTYtLjM5bC4zNTkuNDkxYy0uMDM3LjEyNi0uMS4yNjEtLjE4OS40MDVhMS45NjEgMS45NjEgMCAwIDEtLjM1LjQxNCAxLjczNiAxLjczNiAwIDAgMS0xLjE5LjQ0MWMtLjMyNSAwLS42MTUtLjA2NC0uODY5LS4xOTNhMS41NDYgMS41NDYgMCAwIDEtLjU5OC0uNTI5IDEuMzcyIDEuMzcyIDAgMCAxLS4yMTYtLjc1OGMwLS4yNjQuMDUtLjQ5Ny4xNDctLjcuMTAxLS4yMDQuMjQ5LS4zNzYuNDQyLS41MTQuMTk2LS4xMzguNDM1LS4yNDIuNzE3LS4zMTMuMjgyLS4wNzMuNjA0LS4xMS45NjYtLjExaC44NjlabTQuNTg4LTEuOTV2LjgxaC0yLjgwNXYtLjgxaDIuODA1Wm0tMS45OTYtMS4yMThoMS4xMDh2NC44MTljMCAuMTUzLjAyMi4yNy4wNjUuMzU0LjA0Ni4wOC4xMDkuMTMzLjE4OC4xNmEuODYuODYgMCAwIDAgLjI4MS4wNDIgMi4yMSAyLjIxIDAgMCAwIC4zOTEtLjA0MWwuMDA0Ljg0NmEyLjgyIDIuODIgMCAwIDEtLjMyMi4wNzMgMi4zNjIgMi4zNjIgMCAwIDEtLjQxNC4wMzJjLS4yNTQgMC0uNDc5LS4wNDQtLjY3NS0uMTMzYS45OS45OSAwIDAgMS0uNDYtLjQ0NmMtLjExMS0uMjA1LS4xNjYtLjQ3OC0uMTY2LS44MThWNDUuNjRabTUuMjQgNi4yODVjLS4zNjcgMC0uNy0uMDYtLjk5Ny0uMTc5YTIuMTkzIDIuMTkzIDAgMCAxLS43NTQtLjUxIDIuMjQ5IDIuMjQ5IDAgMCAxLS40NzQtLjc2OCAyLjY5NyAyLjY5NyAwIDAgMS0uMTY2LS45NTJ2LS4xODRjMC0uMzkuMDU3LS43NDIuMTcxLTEuMDU3LjExMy0uMzE2LjI3MS0uNTg2LjQ3My0uODEuMjAzLS4yMjcuNDQyLS40LjcxNy0uNTIuMjc2LS4xMTkuNTc1LS4xNzkuODk3LS4xNzkuMzU2IDAgLjY2Ny4wNi45MzMuMTguMjY3LjEyLjQ4OC4yODguNjYzLjUwNS4xNzcuMjE1LjMwOS40NzEuMzk1Ljc2OC4wODkuMjk4LjEzMy42MjYuMTMzLjk4NHYuNDc0aC0zLjg0NHYtLjc5NmgyLjc1di0uMDg3YTEuNTQ0IDEuNTQ0IDAgMCAwLS4xMi0uNTYuOTUuOTUgMCAwIDAtLjMyNi0uNDI0Yy0uMTQ3LS4xMDctLjM0My0uMTYtLjU4OS0uMTZhLjk5OC45OTggMCAwIDAtLjQ5MS4xMTkuOTc1Ljk3NSAwIDAgMC0uMzU1LjMzNSAxLjc2NCAxLjc2NCAwIDAgMC0uMjIuNTM0IDIuOTk5IDIuOTk5IDAgMCAwLS4wNzQuNjk0di4xODRjMCAuMjE4LjAyOS40Mi4wODguNjA3LjA2MS4xODQuMTUuMzQ1LjI2Ni40ODMuMTE3LjEzOC4yNTguMjQ2LjQyMy4zMjYuMTY2LjA3Ny4zNTQuMTE1LjU2Ni4xMTUuMjY2IDAgLjUwNC0uMDU0LjcxMi0uMTYuMjA5LS4xMDguMzktLjI2LjU0My0uNDU2bC41ODQuNTY2YTIuMjg3IDIuMjg3IDAgMCAxLS40MTguNDUgMi4xMDMgMi4xMDMgMCAwIDEtLjYzLjM0NSAyLjUxMyAyLjUxMyAwIDAgMS0uODU2LjEzM1ptOC41NTQtNi44MXY2LjcxOGgtMS4xMDh2LTUuNDAybC0xLjY0MS41NTZ2LS45MTVsMi42MTYtLjk1NmguMTMzWm01LjYyMiA1LjY4OHYtNi4wMzJoMS4xMTN2Ny4wNjJoLTEuMDA3bC0uMTA2LTEuMDNabS0zLjIzNy0xLjQwMnYtLjA5N2MwLS4zNzcuMDQ0LS43Mi4xMzMtMS4wM2EyLjQyIDIuNDIgMCAwIDEgLjM4Ny0uODA0Yy4xNjgtLjIyNy4zNzQtLjQuNjE2LS41Mi4yNDItLjEyMi41MTUtLjE4NC44MTgtLjE4NC4zMDEgMCAuNTY0LjA1OS43OTEuMTc1LjIyNy4xMTYuNDIuMjg0LjU3OS41MDEuMTYuMjE1LjI4Ny40NzIuMzgyLjc3My4wOTUuMjk3LjE2Mi42MjguMjAyLjk5M3YuMzA4Yy0uMDQuMzU1LS4xMDcuNjgtLjIwMi45NzVhMi42MDMgMi42MDMgMCAwIDEtLjM4Mi43NjNjLS4xNTkuMjE1LS4zNTQuMzgtLjU4NC40OTdhMS43MTYgMS43MTYgMCAwIDEtLjc5NS4xNzRjLS4zIDAtLjU3Mi0uMDYzLS44MTQtLjE4OGExLjc5NCAxLjc5NCAwIDAgMS0uNjExLS41MjkgMi40ODQgMi40ODQgMCAwIDEtLjM4Ny0uOCAzLjYzMiAzLjYzMiAwIDAgMS0uMTMzLTEuMDA3Wm0xLjEwOC0uMDk3di4wOTdjMCAuMjI3LjAyLjQzOC4wNi42MzQuMDQzLjE5Ny4xMDkuMzcuMTk4LjUyLjA4OS4xNDcuMjAzLjI2NC4zNDQuMzUuMTQ1LjA4Mi4zMTYuMTI0LjUxNS4xMjQuMjUyIDAgLjQ1OS0uMDU2LjYyMS0uMTY2LjE2My0uMTEuMjktLjI1OS4zODItLjQ0NmEyLjA4IDIuMDggMCAwIDAgLjE5My0uNjM0di0uODMzYTIuMDI1IDIuMDI1IDAgMCAwLS4xMTUtLjUwNSAxLjM1IDEuMzUgMCAwIDAtLjIyNS0uNDEuOTQ4Ljk0OCAwIDAgMC0uMzU0LS4yNzUgMS4xMSAxLjExIDAgMCAwLS40OTItLjEwMi45NzEuOTcxIDAgMCAwLS41MTUuMTMgMS4wNDYgMS4wNDYgMCAwIDAtLjM1LjM1M2MtLjA4OS4xNS0uMTU1LjMyNS0uMTk3LjUyNC0uMDQzLjItLjA2NS40MTMtLjA2NS42NFptOS45NDMgMS41MzJ2LTIuMzczYzAtLjE3OC0uMDMzLS4zMzEtLjA5Ny0uNDZhLjY3OC42NzggMCAwIDAtLjI5NC0uMjk5IDEuMDEgMS4wMSAwIDAgMC0uNDg4LS4xMDVjLS4xODEgMC0uMzM3LjAzLS40NjkuMDkyYS43Ni43NiAwIDAgMC0uMzA4LjI0OC42MDMuNjAzIDAgMCAwLS4xMS4zNTRoLTEuMTA0YzAtLjE5Ni4wNDgtLjM4Ni4xNDMtLjU3LjA5NS0uMTg0LjIzMy0uMzQ4LjQxNC0uNDkyYTIuMDcgMi4wNyAwIDAgMSAuNjQ4LS4zNGMuMjUxLS4wODMuNTMzLS4xMjUuODQ2LS4xMjUuMzc0IDAgLjcwNS4wNjMuOTkzLjE4OS4yOTEuMTI2LjUyLjMxNi42ODUuNTcuMTY5LjI1MS4yNTMuNTY3LjI1My45NDd2Mi4yMTJjMCAuMjI3LjAxNi40My4wNDYuNjExLjAzNC4xNzguMDgxLjMzMy4xNDMuNDY1di4wNzNoLTEuMTM2YTEuOTg2IDEuOTg2IDAgMCAxLS4xMjQtLjQ1NSAzLjY1OSAzLjY1OSAwIDAgMS0uMDQxLS41NDJabS4xNi0yLjAyOC4wMS42ODVoLS43OTZjLS4yMDUgMC0uMzg2LjAyLS41NDIuMDZhMS4xMSAxLjExIDAgMCAwLS4zOTEuMTY1LjcyMi43MjIgMCAwIDAtLjIzNS4yNjcuNzgyLjc4MiAwIDAgMC0uMDc4LjM1NGMwIC4xMzIuMDMxLjI1My4wOTIuMzYzYS42NC42NCAwIDAgMCAuMjY3LjI1My45MzQuOTM0IDAgMCAwIC40MzIuMDkyIDEuMjE3IDEuMjE3IDAgMCAwIC45OTgtLjQ4My43NS43NSAwIDAgMCAuMTU2LS4zOWwuMzU5LjQ5MWMtLjAzNy4xMjYtLjEuMjYxLS4xODkuNDA1YTEuOTYyIDEuOTYyIDAgMCAxLS4zNDkuNDE0IDEuNzMgMS43MyAwIDAgMS0xLjE5MS40NDFjLS4zMjUgMC0uNjE1LS4wNjQtLjg2OS0uMTkzYTEuNTQyIDEuNTQyIDAgMCAxLS41OTgtLjUyOSAxLjM3MiAxLjM3MiAwIDAgMS0uMjE2LS43NThjMC0uMjY0LjA0OS0uNDk3LjE0Ny0uNy4xMDEtLjIwNC4yNDgtLjM3Ni40NDItLjUxNC4xOTYtLjEzOC40MzUtLjI0Mi43MTctLjMxMy4yODItLjA3My42MDQtLjExLjk2NS0uMTFoLjg2OVptNS40OTQtMS45NWgxLjAwNnY0LjgzN2MwIC40NDgtLjA5NS44MjgtLjI4NSAxLjE0LS4xOS4zMTMtLjQ1NS41NS0uNzk1LjcxM2EyLjc3MSAyLjc3MSAwIDAgMS0yLjQ4OC0uMTAxIDEuNjggMS42OCAwIDAgMS0uNTM4LS40NzNsLjUyLS42NTNjLjE3OC4yMTEuMzc0LjM2Ni41ODkuNDY0LjIxNC4wOTguNDM5LjE0Ny42NzUuMTQ3LjI1NSAwIC40NzEtLjA0Ny42NDktLjE0MmEuOTYxLjk2MSAwIDAgMCAuNDE4LS40MWMuMDk4LS4xOC4xNDctLjQwMS4xNDctLjY2MnYtMy43MzNsLjEwMi0xLjEyN1ptLTMuMzggMi41NDN2LS4wOTdjMC0uMzc3LjA0Ni0uNzIuMTM4LTEuMDNhMi40NSAyLjQ1IDAgMCAxIC4zOTUtLjgwNGMuMTcyLS4yMjcuMzgtLjQuNjI2LS41Mi4yNDUtLjEyMi41MjItLjE4NC44MzItLjE4NC4zMjIgMCAuNTk2LjA1OS44MjMuMTc1LjIzLjExNi40MjEuMjg0LjU3NS41MDEuMTUzLjIxNS4yNzMuNDcyLjM1OC43NzMuMDg5LjI5Ny4xNTUuNjI4LjE5OC45OTN2LjMwOGMtLjA0LjM1NS0uMTA3LjY4LS4yMDIuOTc1YTIuNjk3IDIuNjk3IDAgMCAxLS4zNzcuNzYzYy0uMTU3LjIxNS0uMzUuMzgtLjU4LjQ5N2ExLjczNyAxLjczNyAwIDAgMS0uODA0LjE3NGMtLjMwNCAwLS41NzgtLjA2My0uODIzLS4xODhhMS44OCAxLjg4IDAgMCAxLS42MjYtLjUyOSAyLjUxNyAyLjUxNyAwIDAgMS0uMzk1LS44IDMuNTIgMy41MiAwIDAgMS0uMTM4LTEuMDA3Wm0xLjEwOC0uMDk3di4wOTdjMCAuMjI3LjAyMi40MzguMDY0LjYzNC4wNDYuMTk3LjExNS4zNy4yMDcuNTIuMDk1LjE0Ny4yMTUuMjY0LjM1OS4zNS4xNDcuMDgyLjMyLjEyNC41Mi4xMjQuMjYgMCAuNDczLS4wNTYuNjM5LS4xNjYuMTY4LS4xMS4yOTctLjI1OS4zODYtLjQ0Ni4wOTItLjE5LjE1Ni0uNDAxLjE5My0uNjM0di0uODMzYTIuMDI1IDIuMDI1IDAgMCAwLS4xMTUtLjUwNSAxLjM1IDEuMzUgMCAwIDAtLjIyNS0uNDEuOTQyLjk0MiAwIDAgMC0uMzU5LS4yNzUgMS4xOTMgMS4xOTMgMCAwIDAtLjUxLS4xMDJjLS4xOTkgMC0uMzczLjA0My0uNTIuMTNhMS4wNTUgMS4wNTUgMCAwIDAtLjM2My4zNTMgMS43NSAxLjc1IDAgMCAwLS4yMDcuNTI0Yy0uMDQ2LjItLjA2OS40MTMtLjA2OS42NFptNC40MTMuMDk3di0uMTA2YzAtLjM1OC4wNTItLjY5MS4xNTYtLjk5OC4xMDQtLjMxLjI1NC0uNTc3LjQ1MS0uODA0YTIuMDMgMi4wMyAwIDAgMSAuNzI2LS41MzRjLjI4OC0uMTI4LjYxMy0uMTkzLjk3NS0uMTkzLjM2NSAwIC42OS4wNjUuOTc1LjE5NC4yODguMTI1LjUzMS4zMDMuNzMxLjUzMy4xOTkuMjI3LjM1MS40OTUuNDU1LjgwNC4xMDQuMzA3LjE1Ni42NC4xNTYuOTk4di4xMDZjMCAuMzU5LS4wNTIuNjkxLS4xNTYuOTk4YTIuMzg3IDIuMzg3IDAgMCAxLS40NTUuODA0IDIuMTIgMi4xMiAwIDAgMS0uNzI3LjUzNCAyLjM4MSAyLjM4MSAwIDAgMS0uOTcuMTg4Yy0uMzY1IDAtLjY5MS0uMDYzLS45NzktLjE4OGEyLjExIDIuMTEgMCAwIDEtLjcyNy0uNTM0IDIuMzg3IDIuMzg3IDAgMCAxLS40NTUtLjgwNCAzLjA4NiAzLjA4NiAwIDAgMS0uMTU2LS45OThabTEuMTA4LS4xMDZ2LjEwNmMwIC4yMjQuMDIzLjQzNS4wNjkuNjM0LjA0Ni4yLjExOC4zNzUuMjE2LjUyNS4wOTguMTUuMjI0LjI2OC4zNzcuMzU0LjE1My4wODUuMzM2LjEyOC41NDcuMTI4LjIwNSAwIC4zODMtLjA0My41MzMtLjEyOC4xNTQtLjA4Ni4yNzktLjIwNC4zNzctLjM1NC4wOTgtLjE1LjE3MS0uMzI1LjIxNy0uNTI1LjA0OS0uMTk5LjA3My0uNDEuMDczLS42MzR2LS4xMDZjMC0uMjItLjAyNC0uNDI5LS4wNzMtLjYyNS0uMDQ2LS4yLS4xMi0uMzc2LS4yMjEtLjUyOWExLjA1NyAxLjA1NyAwIDAgMC0uOTE1LS40OTJjLS4yMDkgMC0uMzg5LjA0NS0uNTQzLjEzMy0uMTUuMDg2LS4yNzQuMjA2LS4zNzIuMzYtLjA5OC4xNTItLjE3LjMyOS0uMjE2LjUyOGEyLjczMyAyLjczMyAwIDAgMC0uMDY5LjYyNVoiLz48cGF0aCBmaWxsPSIjRDgxODM4IiBkPSJNNDguOTM1IDg3LjgwNGguNDF2Mi42OTNoLS4yMzJjLTEuMTc2IDAtMi4xOTIuMTgzLTMuMDQ5LjU0Ny0uODQ3LjM2NS0xLjU0NC44NjYtMi4wOTEgMS41MDRhNi4zODMgNi4zODMgMCAwIDAtMS4yMyAyLjI0MiA5LjA0NSA5LjA0NSAwIDAgMC0uMzk3IDIuNzA3djMuMTMxYzAgLjc5My4wODYgMS40OTUuMjYgMi4xMDYuMTczLjYwMS40MTQgMS4xMDcuNzI0IDEuNTE3LjMxOS40MDEuNjg0LjcwNyAxLjA5NC45MTYuNDEuMjEuODUyLjMxNSAxLjMyNi4zMTUuNDkyIDAgLjkzOS0uMTAxIDEuMzQtLjMwMS40LS4yMS43NDMtLjQ5NyAxLjAyNS0uODYxYTMuODYgMy44NiAwIDAgMCAuNjQzLTEuMjk5IDUuODUgNS44NSAwIDAgMCAuMjE4LTEuNjQxYzAtLjU2NS0uMDcyLTEuMDk0LS4yMTgtMS41ODZhMy45NjQgMy45NjQgMCAwIDAtLjYxNi0xLjMxMiAyLjgyNCAyLjgyNCAwIDAgMC0xLjAyNS0uODg5Yy0uNC0uMjE5LS44NjYtLjMyOC0xLjM5NC0uMzI4LS42NTcgMC0xLjI1NC4xNTUtMS43OTEuNDY1LS41My4zMS0uOTU4LjcxNS0xLjI4NiAxLjIxN2EzLjEzMyAzLjEzMyAwIDAgMC0uNTIgMS41NzJsLTEuMjU3LS40MWMuMDczLS44NDguMjYtMS42MDkuNTYtMi4yODNhNS41IDUuNSAwIDAgMSAxLjIxNy0xLjcyMyA1LjExNSA1LjExNSAwIDAgMSAxLjczNy0xLjA4IDYuMDYgNi4wNiAwIDAgMSAyLjE4Ny0uMzgzYy45NjYgMCAxLjgwNS4xODIgMi41MTYuNTQ3LjcxLjM2NCAxLjI5OS44NjEgMS43NjQgMS40OS40NzQuNjIuODI0IDEuMzMgMS4wNTIgMi4xMzMuMjM3Ljc5My4zNTYgMS42MjIuMzU2IDIuNDg4IDAgLjk1Ny0uMTQ2IDEuODUtLjQzOCAyLjY4LS4yOTEuODItLjcyIDEuNTQtMS4yODUgMi4xNmE1Ljc2IDUuNzYgMCAwIDEtMi4wMzcgMS40NDljLS43OTMuMzQ3LTEuNjk1LjUyLTIuNzA3LjUyLTEuMDY2IDAtMi4wMTktLjIwNS0yLjg1Ny0uNjE1YTYuMjkgNi4yOSAwIDAgMS0yLjEyLTEuNjk2IDcuNzMgNy43MyAwIDAgMS0xLjMxMi0yLjQ4OCA5LjcxIDkuNzEgMCAwIDEtLjQ1MS0yLjk4MVY5OC45NmMwLTEuNTEzLjE5MS0yLjk0LjU3NC00LjI3OS4zODMtMS4zNDkuOTctMi41MzggMS43NjQtMy41NjguODAyLTEuMDMgMS44MjctMS44MzcgMy4wNzYtMi40MiAxLjI0OC0uNTkzIDIuNzM5LS44ODkgNC40Ny0uODg5Wm0xOS4yMSA4LjQwOHYzLjI1NGMwIDEuNTU5LS4xNTUgMi44ODktLjQ2NSAzLjk5Mi0uMzAxIDEuMDk0LS43MzkgMS45ODMtMS4zMTMgMi42NjZhNS4xNDcgNS4xNDcgMCAwIDEtMi4wNjQgMS41MDRjLS43OTMuMzE5LTEuNjgyLjQ3OS0yLjY2Ni40NzlhNy42NjMgNy42NjMgMCAwIDEtMi4xODgtLjMwMSA1LjM1OCA1LjM1OCAwIDAgMS0xLjgwNC0uOTQzYy0uNTM4LS40MjktLjk5OS0uOTgtMS4zODEtMS42NTUtLjM3NC0uNjgzLS42NjYtMS40OTktLjg3NS0yLjQ0Ny0uMi0uOTQ4LS4zMDEtMi4wNDYtLjMwMS0zLjI5NXYtMy4yNTRjMC0xLjU2OC4xNTUtMi44OS40NjUtMy45NjUuMzEtMS4wODQuNzUyLTEuOTY0IDEuMzI2LTIuNjM4YTUuMDUxIDUuMDUxIDAgMCAxIDIuMDUtMS40OWMuODAzLS4zMSAxLjY5Ni0uNDY1IDIuNjgtLjQ2NS43OTMgMCAxLjUyMi4xIDIuMTg4LjNhNS4xMjMgNS4xMjMgMCAwIDEgMS44MDUuOTE2Yy41MzcuNDIuOTkzLjk2NyAxLjM2NyAxLjY0MS4zODMuNjY1LjY3NCAxLjQ3Mi44NzUgMi40Mi4yLjkzOS4zIDIuMDMyLjMgMy4yODFabS0zLjI5NSAzLjcxOVY5NS43MmMwLS43OTMtLjA0Ni0xLjQ5LS4xMzctMi4wOTItLjA5MS0uNjEtLjIyOC0xLjEyNS0uNDEtMS41NDUtLjE3NC0uNDI4LS4zOTItLjc3NC0uNjU3LTEuMDM5YTIuMzA5IDIuMzA5IDAgMCAwLS45MDItLjU4OCAzLjE4NyAzLjE4NyAwIDAgMC0xLjEzNS0uMTkxYy0uNTEgMC0uOTY2LjEtMS4zNjcuMy0uNC4xOTItLjczOC41MDItMS4wMTIuOTMtLjI3My40MjktLjQ4My45OTQtLjYyOCAxLjY5Ni0uMTM3LjY5Mi0uMjA2IDEuNTM1LS4yMDYgMi41Mjl2NC4yMTFjMCAuODAyLjA0NiAxLjUwOC4xMzcgMi4xMTkuMDkxLjYxMS4yMjggMS4xMzUuNDEgMS41NzIuMTgzLjQyOS40MDEuNzg0LjY1NyAxLjA2Ny4yNjQuMjczLjU2NS40NzQuOTAyLjYwMWEzLjI1IDMuMjUgMCAwIDAgMS4xMzUuMTkyYy41MiAwIC45OC0uMTAxIDEuMzgtLjMwMS40MDEtLjIwMS43MzktLjUyIDEuMDEyLS45NTcuMjc0LS40NDcuNDc5LTEuMDI1LjYxNS0xLjczNi4xMzctLjcxMS4yMDYtMS41NjQuMjA2LTIuNTU3Wm0xNi45NjYtMy44ODN2MTEuNzg1aC0zLjI5NVY5My4wNGgzLjEwNGwuMTkxIDMuMDA4Wm0tLjUzMyAzLjg0Mi0xLjEyMS0uMDE0YzAtMS4wMi4xMjgtMS45NjQuMzgzLTIuODMuMjU1LS44NjYuNjI5LTEuNjE4IDEuMTItMi4yNTZhNC45MjggNC45MjggMCAwIDEgMS44MzMtMS40OWMuNzM4LS4zNTUgMS41OS0uNTMzIDIuNTU3LS41MzMuNjc0IDAgMS4yOS4xIDEuODQ1LjNhMy42NTggMy42NTggMCAwIDEgMS40NjMuOTE3Yy40Mi40MTkuNzM5Ljk1Ny45NTcgMS42MTMuMjI4LjY1Ni4zNDIgMS40NS4zNDIgMi4zNzl2OS44NTdoLTMuMjk1di05LjU3YzAtLjcyLS4xMS0xLjI4NS0uMzI4LTEuNjk1YTEuODE4IDEuODE4IDAgMCAwLS45MTYtLjg3NWMtLjM5Mi0uMTgzLS44NjEtLjI3NC0xLjQwOC0uMjc0LS42MiAwLTEuMTQ5LjExOS0xLjU4Ni4zNTYtLjQyOS4yMzctLjc4LjU2LTEuMDUzLjk3YTQuMzUgNC4zNSAwIDAgMC0uNjAxIDEuNDIyIDcuNDQ2IDcuNDQ2IDAgMCAwLS4xOTIgMS43MjNabTkuMTc0LS44NzUtMS41NDUuMzQyYzAtLjg5NC4xMjMtMS43MzcuMzctMi41M2E2LjQ0NSA2LjQ0NSAwIDAgMSAxLjEwNy0yLjEwNSA1LjE5NyA1LjE5NyAwIDAgMSAxLjgxOC0xLjQzNmMuNzItLjM0NiAxLjU0NS0uNTIgMi40NzUtLjUyLjc1NiAwIDEuNDMuMTA2IDIuMDIzLjMxNS42MDIuMiAxLjExMi41MiAxLjUzMS45NTcuNDIuNDM4LjczOSAxLjAwOC45NTcgMS43MS4yMTkuNjkyLjMyOCAxLjUzLjMyOCAyLjUxNXY5LjU3aC0zLjMwOHYtOS41ODRjMC0uNzQ3LS4xMS0xLjMyNi0uMzI4LTEuNzM2LS4yMS0uNDEtLjUxLS42OTMtLjkwMy0uODQ4LS4zOTItLjE2NC0uODYxLS4yNDYtMS40MDgtLjI0Ni0uNTEgMC0uOTYxLjA5Ni0xLjM1My4yODdhMi42ODUgMi42ODUgMCAwIDAtLjk3MS43OCAzLjQ3MSAzLjQ3MSAwIDAgMC0uNjAyIDEuMTM0IDQuODY3IDQuODY3IDAgMCAwLS4xOTEgMS4zOTVabTIxLjkzLTUuOTc1aDIuOTk0djE0LjM4M2MwIDEuMzMxLS4yODMgMi40NjEtLjg0OCAzLjM5MWE1LjQ1IDUuNDUgMCAwIDEtMi4zNjUgMi4xMTljLTEuMDEyLjQ5Mi0yLjE4My43MzgtMy41MTQuNzM4YTguMjQ4IDguMjQ4IDAgMCAxLTEuODg2LS4yNDYgNy43MjcgNy43MjcgMCAwIDEtMS45OTctLjc5MyA0Ljk0IDQuOTQgMCAwIDEtMS41OTktMS40MDhsMS41NDUtMS45NDFjLjUyOC42MjggMS4xMTIgMS4wODkgMS43NSAxLjM4YTQuNzc0IDQuNzc0IDAgMCAwIDIuMDA5LjQzOGMuNzU3IDAgMS40LS4xNDEgMS45MjgtLjQyNGEyLjg1NSAyLjg1NSAwIDAgMCAxLjI0NC0xLjIxN2MuMjkyLS41MzcuNDM4LTEuMTk0LjQzOC0xLjk2OFY5Ni4zOWwuMzAxLTMuMzVabS0xMC4wNDkgNy41NjF2LS4yODdjMC0xLjEyMS4xMzctMi4xNDIuNDEtMy4wNjMuMjczLS45My42NjUtMS43MjcgMS4xNzYtMi4zOTJhNS4xMjYgNS4xMjYgMCAwIDEgMS44NTktMS41NDVjLjcyOS0uMzY1IDEuNTU0LS41NDcgMi40NzUtLjU0Ny45NTcgMCAxLjc3My4xNzMgMi40NDcuNTJhNC42MjIgNC42MjIgMCAwIDEgMS43MDkgMS40OWMuNDU2LjYzOC44MTEgMS40MDMgMS4wNjYgMi4yOTYuMjY1Ljg4NS40NjEgMS44NjkuNTg4IDIuOTU0di45MTZhMTQuNzk4IDE0Ljc5OCAwIDAgMS0uNjAxIDIuODk4Yy0uMjgzLjg3NS0uNjU3IDEuNjMyLTEuMTIxIDIuMjdhNC43NzkgNC43NzkgMCAwIDEtMS43MjMgMS40NzZjLS42NzUuMzQ3LTEuNDcyLjUyLTIuMzkzLjUyLS45MDIgMC0xLjcxOC0uMTg3LTIuNDQ3LS41NjFhNS41NiA1LjU2IDAgMCAxLTEuODU5LTEuNTcyYy0uNTExLS42NzUtLjkwMy0xLjQ2OC0xLjE3Ni0yLjM3OWExMC40OTQgMTAuNDk0IDAgMCAxLS40MS0yLjk5NFptMy4yOTUtLjI4N3YuMjg3YzAgLjY3NC4wNjQgMS4zMDMuMTkxIDEuODg3LjEzNy41ODMuMzQyIDEuMDk4LjYxNSAxLjU0NS4yODMuNDM3LjYzOC43ODMgMS4wNjcgMS4wMzkuNDM3LjI0Ni45NTIuMzY5IDEuNTQ1LjM2OS43NzQgMCAxLjQwOC0uMTY0IDEuOS0uNDkyYTMuMjAyIDMuMjAyIDAgMCAwIDEuMTQ5LTEuMzI3IDYuNzQ2IDYuNzQ2IDAgMCAwIC41NzQtMS44ODZ2LTIuNDc1YTYuMDY1IDYuMDY1IDAgMCAwLS4zNDItMS41MDQgNC4wMTQgNC4wMTQgMCAwIDAtLjY3LTEuMjE3IDIuODAyIDIuODAyIDAgMCAwLTEuMDY2LS44MmMtLjQyOS0uMi0uOTM1LS4zLTEuNTE4LS4zLS41OTIgMC0xLjEwNy4xMjctMS41NDUuMzgyYTMuMTQ2IDMuMTQ2IDAgMCAwLTEuMDggMS4wNTMgNS4yMjMgNS4yMjMgMCAwIDAtLjYxNSAxLjU1OCA4LjQyIDguNDIgMCAwIDAtLjIwNSAxLjkwMVptMjEuODItMTIuMzg3LTcuNzUyIDIxLjYxNWgtMi41ODRsNy43NjYtMjEuNjE1aDIuNTdabTUuNTc4IDguMTIxdjExLjc4NWgtMy4yOTVWOTMuMDRoMy4xMDRsLjE5MSAzLjAwOFptLS41MzMgMy44NDItMS4xMjEtLjAxNGMwLTEuMDIuMTI3LTEuOTY0LjM4My0yLjgzLjI1NS0uODY2LjYyOS0xLjYxOCAxLjEyMS0yLjI1NmE0LjkyOCA0LjkyOCAwIDAgMSAxLjgzMi0xLjQ5Yy43MzgtLjM1NSAxLjU5LS41MzMgMi41NTYtLjUzMy42NzUgMCAxLjI5LjEgMS44NDYuM2EzLjY1OCAzLjY1OCAwIDAgMSAxLjQ2My45MTdjLjQxOS40MTkuNzM4Ljk1Ny45NTcgMS42MTMuMjI4LjY1Ni4zNDIgMS40NS4zNDIgMi4zNzl2OS44NTdoLTMuMjk1di05LjU3YzAtLjcyLS4xMDktMS4yODUtLjMyOC0xLjY5NWExLjgyIDEuODIgMCAwIDAtLjkxNi0uODc1Yy0uMzkyLS4xODMtLjg2Mi0uMjc0LTEuNDA4LS4yNzQtLjYyIDAtMS4xNDkuMTE5LTEuNTg2LjM1Ni0uNDI5LjIzNy0uNzguNTYtMS4wNTMuOTctLjI3My40MS0uNDc0Ljg4NC0uNjAyIDEuNDIyYTcuNDc5IDcuNDc5IDAgMCAwLS4xOTEgMS43MjNabTkuMTc0LS44NzUtMS41NDUuMzQyYzAtLjg5NC4xMjMtMS43MzcuMzY5LTIuNTNhNi40NDMgNi40NDMgMCAwIDEgMS4xMDctMi4xMDUgNS4yMDcgNS4yMDcgMCAwIDEgMS44MTktMS40MzZjLjcyLS4zNDYgMS41NDUtLjUyIDIuNDc0LS41Mi43NTcgMCAxLjQzMS4xMDYgMi4wMjQuMzE1LjYwMS4yIDEuMTEyLjUyIDEuNTMxLjk1Ny40MTkuNDM4LjczOCAxLjAwOC45NTcgMS43MS4yMTkuNjkyLjMyOCAxLjUzLjMyOCAyLjUxNXY5LjU3aC0zLjMwOHYtOS41ODRjMC0uNzQ3LS4xMS0xLjMyNi0uMzI4LTEuNzM2LS4yMS0uNDEtLjUxMS0uNjkzLS45MDMtLjg0OC0uMzkyLS4xNjQtLjg2MS0uMjQ2LTEuNDA4LS4yNDYtLjUxIDAtLjk2Mi4wOTYtMS4zNTQuMjg3YTIuNjg3IDIuNjg3IDAgMCAwLS45Ny43OCAzLjQ2NSAzLjQ2NSAwIDAgMC0uNjAyIDEuMTM0IDQuODg4IDQuODg4IDAgMCAwLS4xOTEgMS4zOTVabTE0LjQ1MS02LjY1OGgxLjE4OWMuMzkyIDAgLjcxNi0uMDU1Ljk3MS0uMTY0LjI1NS0uMTE5LjQ0Ny0uMjc4LjU3NC0uNDc5LjEzNy0uMi4yMDUtLjQzNy4yMDUtLjcxYTEuMTYgMS4xNiAwIDAgMC0uMzgyLS44NjJjLS4yNDYtLjI1NS0uNjYxLS4zODMtMS4yNDUtLjM4My0uNDQ2IDAtLjgyNC4wOTYtMS4xMzQuMjg3LS4zMDEuMTgyLS40NTEuNDQyLS40NTEuNzhoLTIuNDg5YzAtLjY0OC4xODMtMS4yLjU0Ny0xLjY1NS4zNjUtLjQ1Ni44NTItLjgwMiAxLjQ2My0xLjAzOWE1LjQ3NCA1LjQ3NCAwIDAgMSAxLjk5Ni0uMzU1Yy44MyAwIDEuNTU5LjExOCAyLjE4OC4zNTUuNjI5LjIyOCAxLjExNi41NyAxLjQ2MyAxLjAyNS4zNTUuNDU2LjUzMyAxLjAyMS41MzMgMS42OTYgMCAuNTItLjE2NC45OC0uNDkyIDEuMzgtLjMxOS40MDItLjc2Ni43MTYtMS4zNC45NDQtLjU2NS4yMTktMS4yMzEuMzI4LTEuOTk2LjMyOGgtMS42di0xLjE0OFptMCAxLjcwOXYtMS4xMjFoMS42Yy44NDcgMCAxLjU3Mi4xIDIuMTczLjMuNjAyLjE5MiAxLjA1OC40ODggMS4zNjguODkuMzE5LjQuNDc4LjkyLjQ3OCAxLjU1OCAwIC42NzQtLjE5MSAxLjI0OC0uNTc0IDEuNzIyLS4zODMuNDc0LS45MDcuODM0LTEuNTcyIDEuMDgtLjY1Ny4yNDYtMS4zOTUuMzctMi4yMTUuMzctLjY4NCAwLTEuMzQ5LS4xMS0xLjk5Ni0uMzI5YTMuNzA2IDMuNzA2IDAgMCAxLTEuNTg2LTEuMDY2Yy0uNDEtLjQ4My0uNjE1LTEuMTE3LS42MTUtMS45aDIuNTAxYzAgLjM1NS4xNjUuNjYuNDkzLjkxNi4zMjguMjU1Ljc3LjM4MiAxLjMyNi4zODIuNTkyIDAgMS4wMy0uMTI3IDEuMzEyLS4zODIuMjkyLS4yNjUuNDM4LS41OTMuNDM4LS45ODUgMC0uMzQ2LS4wODItLjYyNC0uMjQ2LS44MzRhMS4yOCAxLjI4IDAgMCAwLS42Ny0uNDVjLS4yOTItLjEwMS0uNjM0LS4xNTEtMS4wMjYtLjE1MWgtMS4xODlaIi8+PC9zdmc+", + "description": "A colorless and odorless gas resulting from incomplete combustion, particularly in motor vehicles.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'carbon', label: 'Carbon monoxide', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Carbon monoxide\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:molecule-co\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#3FA71A\"},{\"from\":5,\"to\":10,\"color\":\"#80C32C\"},{\"from\":10,\"to\":25,\"color\":\"#FFA600\"},{\"from\":25,\"to\":50,\"color\":\"#F36900\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":28,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#3FA71A\"},{\"from\":5,\"to\":10,\"color\":\"#80C32C\"},{\"from\":10,\"to\":25,\"color\":\"#FFA600\"},{\"from\":25,\"to\":50,\"color\":\"#F36900\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Carbon monoxide\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"mg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "enviroment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "carbon monoxide", + "CO" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/carbon_monoxide__co__card_with_background.json b/application/src/main/data/json/system/widget_types/carbon_monoxide__co__card_with_background.json new file mode 100644 index 0000000000..009d88fad6 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/carbon_monoxide__co__card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "carbon_monoxide_co_card_with_background", + "name": "Carbon monoxide (CO) card with background", + "deprecated": false, + "image": "tb-image:Q08tdmFsdWUtY2FyZC13aXRoLWJhY2tncm91bmQuc3Zn:Q08tdmFsdWUtY2FyZC13aXRoLWJhY2tncm91bmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuNzYiIHJ4PSI0Ii8+PC9nPjxwYXRoIGZpbGw9IiNERTIzNDMiIGQ9Ik0yMiAyMy43NWEyLjUwNyAyLjUwNyAwIDAgMC0yLjUgMi41djcuNWEyLjUgMi41IDAgMCAwIDIuNSAyLjVoMy43NXYtMi41SDIydi03LjVoMy43NXYtMi41SDIyWm03LjUgMGEyLjUwNyAyLjUwNyAwIDAgMC0yLjUgMi41djcuNWEyLjUgMi41IDAgMCAwIDIuNSAyLjVIMzJjMS4zODggMCAyLjUtMS4xMTIgMi41LTIuNXYtNy41YTIuNSAyLjUgMCAwIDAtMi41LTIuNWgtMi41Wm0wIDIuNUgzMnY3LjVoLTIuNXYtNy41WiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjg3IiBkPSJNNTQuODkgMjUuMjZoMS43MWEzLjk0NCAzLjk0NCAwIDAgMS0uNTQ3IDEuNzQzYy0uMzEuNTA2LS43NDUuOTA1LTEuMzA2IDEuMTk2LS41Ni4yOTItMS4yNDIuNDM4LTIuMDQ0LjQzOC0uNjE1IDAtMS4xNjktLjExLTEuNjYxLS4zMjhhMy42NDMgMy42NDMgMCAwIDEtMS4yNjUtLjk0NGMtLjM1LS40MS0uNjItLjkwNC0uODA2LTEuNDgzYTYuNDQ1IDYuNDQ1IDAgMCAxLS4yNzQtMS45NDJ2LS44MjdjMC0uNzE1LjA5NC0xLjM2Mi4yOC0xLjk0MWE0LjI5IDQuMjkgMCAwIDEgLjgyLTEuNDgzIDMuNTQ1IDMuNTQ1IDAgMCAxIDEuMjgtLjk1IDQuMTc5IDQuMTc5IDAgMCAxIDEuNjg4LS4zMjljLjc5MyAwIDEuNDYyLjE0NiAyLjAxLjQzOC41NDYuMjkxLjk3LjY5NSAxLjI3IDEuMjEuMzA2LjUxNS40OTMgMS4xMDUuNTYxIDEuNzdoLTEuNzA5YTMuMTYzIDMuMTYzIDAgMCAwLS4zLTEuMSAxLjU1IDEuNTUgMCAwIDAtLjY3LS42OThjLS4yOTctLjE2NC0uNjg0LS4yNDYtMS4xNjItLjI0Ni0uMzkyIDAtLjczNC4wNzMtMS4wMjYuMjE5LS4yOTEuMTQ2LS41MzUuMzYtLjczMS42NDJhMy4wOTUgMy4wOTUgMCAwIDAtLjQ0NSAxLjA0NmMtLjA5NS40MS0uMTQzLjg4LS4xNDMgMS40MDl2Ljg0YzAgLjUwMi4wNDMuOTU3LjEzIDEuMzY4LjA5MS40MDUuMjI4Ljc1NC40MSAxLjA0Ni4xODcuMjkxLjQyNC41MTcuNzEuNjc2LjI4OC4xNi42MzIuMjQgMS4wMzMuMjQuNDg4IDAgLjg4Mi0uMDc4IDEuMTgzLS4yMzNhMS41NSAxLjU1IDAgMCAwIC42OS0uNjc3Yy4xNi0uMy4yNjQtLjY2Ny4zMTUtMS4xWm03LjUyNCAxLjc1N3YtMy41MjhjMC0uMjY0LS4wNDgtLjQ5Mi0uMTQ0LS42ODNhMS4wMDUgMS4wMDUgMCAwIDAtLjQzNy0uNDQ1Yy0uMTkxLS4xMDUtLjQzMy0uMTU3LS43MjUtLjE1Ny0uMjY4IDAtLjUuMDQ2LS42OTcuMTM3YTEuMTI4IDEuMTI4IDAgMCAwLS40NTguMzY5Ljg5Ljg5IDAgMCAwLS4xNjQuNTI2aC0xLjY0YzAtLjI5MS4wNy0uNTc0LjIxMS0uODQ3LjE0Mi0uMjc0LjM0Ny0uNTE4LjYxNi0uNzMyLjI2OS0uMjE0LjU5LS4zODMuOTY0LS41MDZhNC4wMDcgNC4wMDcgMCAwIDEgMS4yNTctLjE4NGMuNTU2IDAgMS4wNDguMDkzIDEuNDc3LjI4LjQzMy4xODcuNzcyLjQ3IDEuMDE4Ljg0OC4yNTEuMzczLjM3Ni44NDMuMzc2IDEuNDA4djMuMjg4YzAgLjMzNy4wMjMuNjQuMDY5LjkxLjA1LjI2NC4xMi40OTQuMjEyLjY5di4xMDlINjIuNjZhMi45MTcgMi45MTcgMCAwIDEtLjE4NC0uNjc3IDUuNTI4IDUuNTI4IDAgMCAxLS4wNjItLjgwNlptLjI0LTMuMDE1LjAxMyAxLjAxOGgtMS4xODNjLS4zMDUgMC0uNTc0LjAzLS44MDYuMDlhMS42NTYgMS42NTYgMCAwIDAtLjU4MS4yNDVjLS4xNTUuMTEtLjI3Mi4yNDItLjM0OS4zOTctLjA3Ny4xNTUtLjExNi4zMy0uMTE2LjUyNnMuMDQ1LjM3Ni4xMzcuNTRjLjA5LjE2LjIyMy4yODUuMzk2LjM3Ni4xNzguMDkxLjM5Mi4xMzcuNjQzLjEzNy4zMzcgMCAuNjMtLjA2OC44ODItLjIwNS4yNTUtLjE0MS40NTUtLjMxMi42MDEtLjUxMy4xNDYtLjIwNS4yMjMtLjM5OC4yMzItLjU4bC41MzQuNzNhMi41MDIgMi41MDIgMCAwIDEtLjI4LjYwMmMtLjEzMy4yMTQtLjMwNi40Mi0uNTIuNjE2YTIuNTg0IDIuNTg0IDAgMCAxLTEuNzcuNjU2Yy0uNDg0IDAtLjkxNC0uMDk2LTEuMjkzLS4yODdhMi4yOTggMi4yOTggMCAwIDEtLjg4OC0uNzg3IDIuMDM5IDIuMDM5IDAgMCAxLS4zMjItMS4xMjdjMC0uMzkyLjA3My0uNzM5LjIyLTEuMDQuMTUtLjMwNS4zNjgtLjU2LjY1NS0uNzY1LjI5Mi0uMjA1LjY0OC0uMzYgMS4wNjctLjQ2NS40Mi0uMTEuODk4LS4xNjQgMS40MzUtLjE2NGgxLjI5MlptNS4wNDEtMS40OVYyOC41aC0xLjY0N3YtNy4zOTZoMS41NzJsLjA3NSAxLjQwOFptMi4yNjMtMS40NTYtLjAxNCAxLjUzYTQuMjkxIDQuMjkxIDAgMCAwLS42Ny0uMDU1Yy0uMjgyLjAwMS0uNTMuMDQyLS43NDUuMTI0YTEuNDAzIDEuNDAzIDAgMCAwLS41NC4zNDJjLS4xNDEuMTQ2LS4yNS4zMjQtLjMyOC41MzMtLjA3Ny4yMS0uMTIzLjQ0NS0uMTM3LjcwNGwtLjM3Ni4wMjhjMC0uNDY1LjA0Ni0uODk2LjEzNy0xLjI5Mi4wOTEtLjM5Ny4yMjgtLjc0NS40MS0xLjA0Ni4xODctLjMwMS40Mi0uNTM2LjY5OC0uNzA0YTEuODcgMS44NyAwIDAgMSAuOTc3LS4yNTNjLjEgMCAuMjA3LjAwOS4zMjEuMDI3LjExOS4wMTguMjA4LjAzOS4yNjcuMDYyWk03MS4yNDggMThoMS42NDZ2OC45MmwtLjE1NyAxLjU4aC0xLjQ5VjE4Wm02LjQ3MyA2LjczM3YuMTQ0YzAgLjU0Ny0uMDYyIDEuMDUtLjE4NSAxLjUxYTMuNDggMy40OCAwIDAgMS0uNTQ3IDEuMTljLS4yNDEuMzM3LS41NDIuNi0uOTAyLjc4Ni0uMzU2LjE4My0uNzY4LjI3NC0xLjIzNy4yNzQtLjQ2IDAtLjg2Mi0uMDg3LTEuMjAzLS4yNmEyLjM5MiAyLjM5MiAwIDAgMS0uODYyLS43MzggNC4wOTUgNC4wOTUgMCAwIDEtLjU1NC0xLjE0MiA3LjU0NCA3LjU0NCAwIDAgMS0uMy0xLjQ2M3YtLjQ1OGMuMDU5LS41MzguMTYtMS4wMjUuMy0xLjQ2My4xNDItLjQ0Mi4zMjYtLjgyMi41NTQtMS4xNDEuMjMzLS4zMjQuNTE3LS41NzIuODU1LS43NDUuMzQxLS4xNzQuNzQtLjI2IDEuMTk2LS4yNi40NzQgMCAuODkuMDkgMS4yNS4yNzMuMzY1LjE4My42NjkuNDQyLjkxLjc4LjI0Mi4zMzIuNDIyLjcyOS41NCAxLjE4OS4xMjMuNDYuMTg1Ljk2OC4xODUgMS41MjRabS0xLjY0OC4xNDR2LS4xNDRhNS4wOCA1LjA4IDAgMCAwLS4wODItLjkzNiAyLjQ2IDIuNDYgMCAwIDAtLjI3My0uNzggMS4zNyAxLjM3IDAgMCAwLS41MDYtLjUyNmMtLjIxLS4xMzItLjQ3Mi0uMTk4LS43ODYtLjE5OC0uMjkyIDAtLjU0My4wNS0uNzUyLjE1LS4yMS4xLS4zODUuMjM3LS41MjcuNDFhMi4wMyAyLjAzIDAgMCAwLS4zMzUuNjAyYy0uMDc3LjIyOC0uMTMuNDc0LS4xNTcuNzM4djEuMjM4Yy4wNDEuMzQxLjEyOC42NTYuMjYuOTQzLjEzNy4yODMuMzI4LjUxLjU3NC42ODQuMjQ2LjE2OC41NjMuMjUzLjk1LjI1My4zMDYgMCAuNTYzLS4wNjIuNzczLS4xODVhMS4zNSAxLjM1IDAgMCAwIC40OTktLjUxMyAyLjQ2IDIuNDYgMCAwIDAgLjI3My0uNzc5Yy4wNi0uMjk2LjA5LS42MTUuMDktLjk1N1ptMi45NzguMDA3di0uMTU3YzAtLjUzNC4wNzctMS4wMjguMjMyLTEuNDg0LjE1NS0uNDYuMzc5LS44NTkuNjctMS4xOTZhMy4wMjQgMy4wMjQgMCAwIDEgMS4wOC0uNzkzIDMuNTE2IDMuNTE2IDAgMCAxIDEuNDUtLjI4N2MuNTQyIDAgMS4wMjUuMDk2IDEuNDQ5LjI4Ny40MjguMTg3Ljc5LjQ1MSAxLjA4Ny43OTMuMjk2LjMzNy41MjEuNzM2LjY3NiAxLjE5Ni4xNTUuNDU2LjIzMy45NS4yMzMgMS40ODR2LjE1N2MwIC41MzMtLjA3OCAxLjAyNy0uMjMzIDEuNDgzYTMuNTUgMy41NSAwIDAgMS0uNjc2IDEuMTk3IDMuMTM3IDMuMTM3IDAgMCAxLTEuMDguNzkyIDMuNTQgMy41NCAwIDAgMS0xLjQ0My4yOCAzLjYwNyAzLjYwNyAwIDAgMS0xLjQ1Ni0uMjggMy4xMzYgMy4xMzYgMCAwIDEtMS4wOC0uNzkzIDMuNTUgMy41NSAwIDAgMS0uNjc3LTEuMTk2IDQuNTgyIDQuNTgyIDAgMCAxLS4yMzItMS40ODNabTEuNjQ3LS4xNTd2LjE1N2MwIC4zMzIuMDM0LjY0Ny4xMDMuOTQzLjA2OC4yOTYuMTc1LjU1Ni4zMjEuNzguMTQ2LjIyMy4zMzMuMzk4LjU2LjUyNi4yMjguMTI3LjUuMTkxLjgxNC4xOTEuMzA1IDAgLjU3LS4wNjQuNzkzLS4xOTEuMjI4LS4xMjguNDE1LS4zMDMuNTYtLjUyN2EyLjQgMi40IDAgMCAwIC4zMjItLjc3OWMuMDczLS4yOTYuMTEtLjYxLjExLS45NDN2LS4xNTdjMC0uMzI5LS4wMzctLjYzOS0uMTEtLjkzYTIuMzkgMi4zOSAwIDAgMC0uMzI4LS43ODYgMS41NyAxLjU3IDAgMCAwLS41Ni0uNTM0IDEuNTQzIDEuNTQzIDAgMCAwLS44LS4xOThjLS4zMSAwLS41OC4wNjYtLjgwNy4xOThhMS41ODcgMS41ODcgMCAwIDAtLjU1NC41MzRjLS4xNDYuMjI4LS4yNTMuNDktLjMyMS43ODZhNC4wNjIgNC4wNjIgMCAwIDAtLjEwMy45M1ptOC40OTQtMi4wNDRWMjguNWgtMS42NDd2LTcuMzk2aDEuNTUybC4wOTUgMS41NzlabS0uMjk0IDEuODQ1LS41MzMtLjAwNmE0LjgxMiA0LjgxMiAwIDAgMSAuMjE5LTEuNDQzYy4xNDYtLjQzNy4zNDYtLjgxMy42MDItMS4xMjguMjYtLjMxNC41Ny0uNTU2LjkzLS43MjRhMi43NCAyLjc0IDAgMCAxIDEuMjAyLS4yNmMuMzU2IDAgLjY3Ny4wNS45NjQuMTUuMjkyLjA5Ni41NC4yNTMuNzQ1LjQ3Mi4yMS4yMTkuMzcuNTAzLjQ3OS44NTQuMTEuMzQ3LjE2NC43NzMuMTY0IDEuMjc5VjI4LjVoLTEuNjU0di00Ljc4NWMwLS4zNTYtLjA1My0uNjM2LS4xNTgtLjg0MWEuODguODggMCAwIDAtLjQ0NC0uNDQ0Yy0uMTkxLS4wOTEtLjQzLS4xMzctLjcxOC0uMTM3LS4yODIgMC0uNTM1LjA2LS43NTguMTc4LS4yMjQuMTE4LS40MTMuMjgtLjU2OC40ODUtLjE1LjIwNS0uMjY3LjQ0Mi0uMzQ5LjcxMWEyLjkzOCAyLjkzOCAwIDAgMC0uMTIzLjg2MVptMTIuMDg3LTEuOTJWMjguNWgtMS42NDd2LTcuMzk2aDEuNTUybC4wOTUgMS41MDNabS0uMjY2IDEuOTItLjU2MS0uMDA2YzAtLjUxLjA2NC0uOTgzLjE5Mi0xLjQxNi4xMjctLjQzMy4zMTQtLjgwOS41Ni0xLjEyOC4yNDYtLjMyMy41NTItLjU3MS45MTYtLjc0NS4zNjktLjE3Ny43OTUtLjI2NiAxLjI3OC0uMjY2LjMzOCAwIC42NDUuMDUuOTIzLjE1LjI4My4wOTYuNTI3LjI0OS43MzIuNDU4LjIwOS4yMS4zNjkuNDc5LjQ3OC44MDcuMTE0LjMyOC4xNzEuNzI0LjE3MSAxLjE5VjI4LjVoLTEuNjQ3di00Ljc4NWMwLS4zNi0uMDU1LS42NDMtLjE2NC0uODQ4YS45MTIuOTEyIDAgMCAwLS40NTgtLjQzNyAxLjY1OSAxLjY1OSAwIDAgMC0uNzA0LS4xMzdjLS4zMSAwLS41NzUuMDYtLjc5My4xNzhhMS40OSAxLjQ5IDAgMCAwLS41MjcuNDg1IDIuMTkzIDIuMTkzIDAgMCAwLS4zMDEuNzExIDMuNzY4IDMuNzY4IDAgMCAwLS4wOTUuODYxWm00LjU4Ny0uNDM3LS43NzMuMTdjMC0uNDQ2LjA2Mi0uODY3LjE4NS0xLjI2NGEzLjIyIDMuMjIgMCAwIDEgLjU1My0xLjA1M2MuMjQ3LS4zMDUuNTUtLjU0NC45MS0uNzE3LjM2LS4xNzQuNzcyLS4yNiAxLjIzNy0uMjYuMzc4IDAgLjcxNS4wNTIgMS4wMTIuMTU3LjMuMS41NTYuMjYuNzY1LjQ3OS4yMS4yMTguMzY5LjUwMy40NzkuODU0LjEwOS4zNDYuMTY0Ljc2Ni4xNjQgMS4yNThWMjguNWgtMS42NTR2LTQuNzkyYzAtLjM3NC0uMDU1LS42NjMtLjE2NC0uODY4YS44NTMuODUzIDAgMCAwLS40NTItLjQyNCAxLjgxNyAxLjgxNyAwIDAgMC0uNzA0LS4xMjNjLS4yNTUgMC0uNDgxLjA0OC0uNjc2LjE0M2ExLjM0IDEuMzQgMCAwIDAtLjQ4Ni4zOSAxLjc1MSAxLjc1MSAwIDAgMC0uMzAxLjU2OCAyLjQ2IDIuNDYgMCAwIDAtLjA5NS42OTdabTYuMTYzLjc5M3YtLjE1N2MwLS41MzQuMDc3LTEuMDI4LjIzMi0xLjQ4NC4xNTUtLjQ2LjM3OC0uODU5LjY3LTEuMTk2YTMuMDIgMy4wMiAwIDAgMSAxLjA4LS43OTMgMy41MiAzLjUyIDAgMCAxIDEuNDQ5LS4yODdjLjU0MyAwIDEuMDI2LjA5NiAxLjQ1LjI4Ny40MjguMTg3Ljc5LjQ1MSAxLjA4Ny43OTMuMjk2LjMzNy41MjEuNzM2LjY3NiAxLjE5Ni4xNTUuNDU2LjIzMy45NS4yMzMgMS40ODR2LjE1N2MwIC41MzMtLjA3OCAxLjAyNy0uMjMzIDEuNDgzLS4xNTUuNDU2LS4zOC44NTUtLjY3NiAxLjE5N2EzLjE0NSAzLjE0NSAwIDAgMS0xLjA4MS43OTJjLS40MjMuMTg3LS45MDQuMjgtMS40NDIuMjhhMy42MDcgMy42MDcgMCAwIDEtMS40NTYtLjI4IDMuMTMyIDMuMTMyIDAgMCAxLTEuMDgtLjc5MyAzLjU1NSAzLjU1NSAwIDAgMS0uNjc3LTEuMTk2IDQuNTc5IDQuNTc5IDAgMCAxLS4yMzItMS40ODNabTEuNjQ3LS4xNTd2LjE1N2MwIC4zMzIuMDM0LjY0Ny4xMDMuOTQzLjA2OC4yOTYuMTc1LjU1Ni4zMjEuNzguMTQ2LjIyMy4zMzMuMzk4LjU2MS41MjYuMjI3LjEyNy40OTkuMTkxLjgxMy4xOTEuMzA1IDAgLjU3LS4wNjQuNzkzLS4xOTFhMS41OSAxLjU5IDAgMCAwIC41NjEtLjUyN2MuMTQ1LS4yMjMuMjUzLS40ODMuMzIxLS43NzlhMy45MyAzLjkzIDAgMCAwIC4xMDktLjk0M3YtLjE1N2MwLS4zMjktLjAzNi0uNjM5LS4xMDktLjkzYTIuMzkgMi4zOSAwIDAgMC0uMzI4LS43ODYgMS41NyAxLjU3IDAgMCAwLS41NjEtLjUzNCAxLjU0MyAxLjU0MyAwIDAgMC0uOC0uMTk4Yy0uMzEgMC0uNTc4LjA2Ni0uODA2LjE5OGExLjU4NCAxLjU4NCAwIDAgMC0uNTU0LjUzNGMtLjE0Ni4yMjgtLjI1My40OS0uMzIxLjc4NmE0LjAyOCA0LjAyOCAwIDAgMC0uMTAzLjkzWm04LjQ5NC0yLjA0NFYyOC41aC0xLjY0N3YtNy4zOTZoMS41NTJsLjA5NSAxLjU3OVptLS4yOTQgMS44NDUtLjUzMy0uMDA2YTQuODM0IDQuODM0IDAgMCAxIC4yMTktMS40NDMgMy41NCAzLjU0IDAgMCAxIC42MDItMS4xMjhjLjI1OS0uMzE0LjU2OS0uNTU2LjkyOS0uNzI0LjM2LS4xNzQuNzYxLS4yNiAxLjIwMy0uMjYuMzU2IDAgLjY3Ny4wNS45NjQuMTUuMjkyLjA5Ni41NC4yNTMuNzQ1LjQ3Mi4yMS4yMTkuMzY5LjUwMy40NzkuODU0LjEwOS4zNDcuMTY0Ljc3My4xNjQgMS4yNzlWMjguNWgtMS42NTR2LTQuNzg1YzAtLjM1Ni0uMDUzLS42MzYtLjE1OC0uODQxYS44NzguODc4IDAgMCAwLS40NDQtLjQ0NGMtLjE5MS0uMDkxLS40MzEtLjEzNy0uNzE4LS4xMzctLjI4MiAwLS41MzUuMDYtLjc1OS4xNzgtLjIyMy4xMTgtLjQxMi4yOC0uNTY3LjQ4NS0uMTUuMjA1LS4yNjcuNDQyLS4zNDkuNzExYTIuOTM4IDIuOTM4IDAgMCAwLS4xMjMuODYxWm02LjM4OS4zNTZ2LS4xNTdjMC0uNTM0LjA3OC0xLjAyOC4yMzMtMS40ODQuMTU0LS40Ni4zNzgtLjg1OS42NjktMS4xOTZhMy4wMzMgMy4wMzMgMCAwIDEgMS4wODEtLjc5MyAzLjUxMyAzLjUxMyAwIDAgMSAxLjQ0OS0uMjg3Yy41NDIgMCAxLjAyNS4wOTYgMS40NDkuMjg3LjQyOC4xODcuNzkxLjQ1MSAxLjA4Ny43OTMuMjk2LjMzNy41MjIuNzM2LjY3NyAxLjE5Ni4xNTUuNDU2LjIzMi45NS4yMzIgMS40ODR2LjE1N2MwIC41MzMtLjA3NyAxLjAyNy0uMjMyIDEuNDgzYTMuNTU1IDMuNTU1IDAgMCAxLS42NzcgMS4xOTcgMy4xMzIgMy4xMzIgMCAwIDEtMS4wOC43OTJjLS40MjQuMTg3LS45MDUuMjgtMS40NDMuMjhhMy42MSAzLjYxIDAgMCAxLTEuNDU2LS4yOCAzLjE0MSAzLjE0MSAwIDAgMS0xLjA4LS43OTMgMy41MzYgMy41MzYgMCAwIDEtLjY3Ni0xLjE5NiA0LjU3OSA0LjU3OSAwIDAgMS0uMjMzLTEuNDgzWm0xLjY0OC0uMTU3di4xNTdjMCAuMzMyLjAzNC42NDcuMTAyLjk0My4wNjguMjk2LjE3Ni41NTYuMzIxLjc4LjE0Ni4yMjMuMzMzLjM5OC41NjEuNTI2LjIyOC4xMjcuNDk5LjE5MS44MTMuMTkxLjMwNiAwIC41Ny0uMDY0Ljc5My0uMTkxYTEuNTkgMS41OSAwIDAgMCAuNTYxLS41MjdjLjE0Ni0uMjIzLjI1My0uNDgzLjMyMS0uNzc5LjA3My0uMjk2LjExLS42MS4xMS0uOTQzdi0uMTU3YzAtLjMyOS0uMDM3LS42MzktLjExLS45M2EyLjM5IDIuMzkgMCAwIDAtLjMyOC0uNzg2IDEuNTc2IDEuNTc2IDAgMCAwLS41Ni0uNTM0IDEuNTQ1IDEuNTQ1IDAgMCAwLS44LS4xOThjLS4zMSAwLS41NzkuMDY2LS44MDcuMTk4YTEuNTkyIDEuNTkyIDAgMCAwLS41NTQuNTM0Yy0uMTQ1LjIyOC0uMjUzLjQ5LS4zMjEuNzg2YTQuMDg0IDQuMDg0IDAgMCAwLS4xMDIuOTNabTcuOTY3LTMuNjIzIDEuNDA5IDIuNDU0IDEuNDM1LTIuNDU0aDEuODEybC0yLjI0MyAzLjYzIDIuMzMxIDMuNzY2aC0xLjgxMWwtMS41MDQtMi41NTctMS41MDQgMi41NTdoLTEuODE4bDIuMzI0LTMuNzY3LTIuMjM1LTMuNjNoMS44MDRabTcuNzk3IDBWMjguNWgtMS42NTR2LTcuMzk2aDEuNjU0Wm0tMS43NjMtMS45NDJjMC0uMjUuMDgyLS40NTguMjQ2LS42MjIuMTY4LS4xNjkuNDAxLS4yNTMuNjk3LS4yNTMuMjkyIDAgLjUyMi4wODQuNjkuMjUzYS44My44MyAwIDAgMSAuMjUzLjYyMi44MjIuODIyIDAgMCAxLS4yNTMuNjE1Yy0uMTY4LjE2NC0uMzk4LjI0Ni0uNjkuMjQ2LS4yOTYgMC0uNTI5LS4wODItLjY5Ny0uMjQ2YS44MzUuODM1IDAgMCAxLS4yNDYtLjYxNVptOC4zMyA3LjgwN1YxOGgxLjY1NHYxMC41aC0xLjQ5N2wtLjE1Ny0xLjUzMVptLTQuODEzLTIuMDg1di0uMTQ0YzAtLjU2LjA2Ni0xLjA3LjE5OC0xLjUzMS4xMzItLjQ2NS4zMjQtLjg2NC41NzUtMS4xOTYuMjUtLjMzOC41NTYtLjU5NS45MTYtLjc3My4zNi0uMTgyLjc2NS0uMjczIDEuMjE2LS4yNzMuNDQ3IDAgLjgzOS4wODYgMS4xNzYuMjYuMzM3LjE3My42MjQuNDIxLjg2MS43NDUuMjM3LjMxOS40MjcuNzAxLjU2OCAxLjE0OC4xNDEuNDQyLjI0MS45MzQuMzAxIDEuNDc3di40NThhNy40NzMgNy40NzMgMCAwIDEtLjMwMSAxLjQ0OSAzLjg4NiAzLjg4NiAwIDAgMS0uNTY4IDEuMTM1IDIuNDUgMi40NSAwIDAgMS0uODY4LjczOGMtLjMzNy4xNzMtLjczMS4yNi0xLjE4Mi4yNi0uNDQ3IDAtLjg1LS4wOTQtMS4yMS0uMjhhMi42NjIgMi42NjIgMCAwIDEtLjkwOS0uNzg3IDMuNjkyIDMuNjkyIDAgMCAxLS41NzUtMS4xOSA1LjQxMyA1LjQxMyAwIDAgMS0uMTk4LTEuNDk2Wm0xLjY0OC0uMTQ0di4xNDRjMCAuMzM3LjAyOS42NTEuMDg4Ljk0My4wNjQuMjkyLjE2Mi41NS4yOTQuNzczLjEzMy4yMTguMzAzLjM5MS41MTMuNTIuMjE0LjEyMi40NjkuMTg0Ljc2Ni4xODQuMzczIDAgLjY4MS0uMDgyLjkyMy0uMjQ2LjI0MS0uMTY0LjQzLS4zODYuNTY3LS42NjMuMTQxLS4yODMuMjM3LS41OTguMjg3LS45NDR2LTEuMjM3YTIuOTg4IDIuOTg4IDAgMCAwLS4xNzEtLjc1MiAyLjAxOCAyLjAxOCAwIDAgMC0uMzM1LS42MDggMS40MTMgMS40MTMgMCAwIDAtLjUyNi0uNDEgMS42NSAxLjY1IDAgMCAwLS43MzItLjE1MWMtLjMgMC0uNTU2LjA2NC0uNzY1LjE5MS0uMjEuMTI4LS4zODMuMzAzLS41Mi41MjdhMi42IDIuNiAwIDAgMC0uMjk0Ljc3OSA0LjQ5NiA0LjQ5NiAwIDAgMC0uMDk1Ljk1Wm0xMC4wNTIgMy44OTdhMy45NDEgMy45NDEgMCAwIDEtMS40ODMtLjI2NyAzLjI3IDMuMjcgMCAwIDEtMS4xMjEtLjc1OSAzLjM1MiAzLjM1MiAwIDAgMS0uNzA0LTEuMTQxIDMuOTk5IDMuOTk5IDAgMCAxLS4yNDYtMS40MTV2LS4yNzRjMC0uNTc4LjA4NC0xLjEwMy4yNTMtMS41NzIuMTY4LS40Ny40MDMtLjg3LjcwNC0xLjIwMy4zLS4zMzcuNjU2LS41OTUgMS4wNjYtLjc3My40MS0uMTc3Ljg1NS0uMjY2IDEuMzMzLS4yNjYuNTI5IDAgLjk5MS4wODkgMS4zODguMjY2LjM5Ni4xNzguNzI0LjQyOS45ODQuNzUyLjI2NC4zMi40Ni43LjU4OCAxLjE0Mi4xMzIuNDQyLjE5OC45My4xOTggMS40NjN2LjcwNGgtNS43MTVWMjQuMTFoNC4wODh2LS4xM2EyLjMxOCAyLjMxOCAwIDAgMC0uMTc3LS44MzQgMS40MTggMS40MTggMCAwIDAtLjQ4Ni0uNjI4Yy0uMjE5LS4xNi0uNTEtLjI0LS44NzUtLjI0LS4yNzMgMC0uNTE3LjA2LS43MzEuMTc4LS4yMS4xMTQtLjM4NS4yOC0uNTI3LjUtLjE0MS4yMTgtLjI1LjQ4Mi0uMzI4Ljc5Mi0uMDczLjMwNS0uMTA5LjY1LS4xMDkgMS4wMzJ2LjI3NGMwIC4zMjMuMDQzLjYyNC4xMy45MDIuMDkxLjI3My4yMjMuNTEzLjM5Ni43MTguMTczLjIwNS4zODMuMzY3LjYyOS40ODUuMjQ2LjExNC41MjcuMTcxLjg0MS4xNzEuMzk2IDAgLjc1LS4wOCAxLjA2LS4yNC4zMDktLjE1OS41NzgtLjM4NC44MDYtLjY3NmwuODY4Ljg0YTMuMzQ3IDMuMzQ3IDAgMCAxLS42MjIuNjdjLS4yNTUuMjEtLjU2Ny4zODEtLjkzNi41MTRhMy43MzIgMy43MzIgMCAwIDEtMS4yNzIuMTk4WiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjM4IiBkPSJNNTIuODc0IDQxLjU4NXYuOTE1aC0zLjM2MXYtLjkxNWgzLjM2Wm0tMy4wNC01Ljc4VjQyLjVINDguNjh2LTYuNjk1aDEuMTU1Wm02LjgyMiA1LjY5N1YzOS4xM2MwLS4xNzgtLjAzMi0uMzMxLS4wOTYtLjQ2YS42NzYuNjc2IDAgMCAwLS4yOTQtLjI5OSAxLjAwNiAxLjAwNiAwIDAgMC0uNDg4LS4xMDZjLS4xOCAwLS4zMzcuMDMtLjQ2OS4wOTJhLjc1OC43NTggMCAwIDAtLjMwOC4yNDguNTk5LjU5OSAwIDAgMC0uMTEuMzU1aC0xLjEwNGMwLS4xOTcuMDQ4LS4zODcuMTQzLS41Ny4wOTUtLjE4NC4yMzMtLjM0OS40MTQtLjQ5My4xOC0uMTQ0LjM5Ny0uMjU3LjY0OC0uMzQuMjUxLS4wODMuNTMzLS4xMjQuODQ2LS4xMjQuMzc0IDAgLjcwNS4wNjMuOTkzLjE4OC4yOTEuMTI2LjUyLjMxNi42ODUuNTcuMTY5LjI1Mi4yNTMuNTY4LjI1My45NDh2Mi4yMTFjMCAuMjI3LjAxNi40MzEuMDQ2LjYxMi4wMzQuMTc4LjA4MS4zMzMuMTQzLjQ2NHYuMDc0aC0xLjEzNmExLjk2NSAxLjk2NSAwIDAgMS0uMTI0LS40NTUgMy43MyAzLjczIDAgMCAxLS4wNDEtLjU0M1ptLjE2MS0yLjAyOC4wMS42ODZoLS43OTZjLS4yMDUgMC0uMzg2LjAyLS41NDIuMDZhMS4xMTUgMS4xMTUgMCAwIDAtLjM5MS4xNjUuNzE4LjcxOCAwIDAgMC0uMzEzLjYyYzAgLjEzMy4wMy4yNTMuMDkyLjM2NC4wNjEuMTA3LjE1LjE5MS4yNjcuMjUzLjEyLjA2MS4yNjMuMDkyLjQzMi4wOTIuMjI3IDAgLjQyNS0uMDQ2LjU5My0uMTM4YTEuMjIgMS4yMiAwIDAgMCAuNDA1LS4zNDUuNzUuNzUgMCAwIDAgLjE1Ni0uMzlsLjM1OS40OTFjLS4wMzcuMTI2LS4xLjI2LS4xODkuNDA1YTEuOTYxIDEuOTYxIDAgMCAxLS4zNS40MTQgMS43MzUgMS43MzUgMCAwIDEtMS4xOS40NDFjLS4zMjUgMC0uNjE1LS4wNjQtLjg3LS4xOTNhMS41NDQgMS41NDQgMCAwIDEtLjU5Ny0uNTI5IDEuMzcxIDEuMzcxIDAgMCAxLS4yMTYtLjc1OWMwLS4yNjMuMDQ5LS40OTYuMTQ3LS42OThhMS4zNyAxLjM3IDAgMCAxIC40NDItLjUxNWMuMTk2LS4xMzguNDM1LS4yNDMuNzE3LS4zMTMuMjgyLS4wNzQuNjA0LS4xMS45NjUtLjExaC44N1ptNS4wOTggMS42NzljMC0uMTEtLjAyOC0uMjEtLjA4My0uMy0uMDU1LS4wOTEtLjE2LS4xNzQtLjMxNy0uMjQ3YTMuMDc4IDMuMDc4IDAgMCAwLS42OC0uMjAzIDUuODY0IDUuODY0IDAgMCAxLS43MjctLjIwNyAyLjMwNSAyLjMwNSAwIDAgMS0uNTYtLjI5OCAxLjI1NyAxLjI1NyAwIDAgMS0uMzY0LS40MTQgMS4xNDYgMS4xNDYgMCAwIDEtLjEyOS0uNTUyYzAtLjIwMi4wNDQtLjM5NC4xMzMtLjU3NS4wOS0uMTguMjE2LS4zNC4zODItLjQ3OC4xNjYtLjEzOC4zNjYtLjI0Ny42MDItLjMyNi4yNC0uMDguNTA2LS4xMi44LS4xMi40MTcgMCAuNzc0LjA3IDEuMDcyLjIxMi4zLjEzNy41My4zMjYuNjkuNTY1LjE1OS4yMzYuMjM5LjUwMy4yMzkuOGgtMS4xMDlhLjcwNy43MDcgMCAwIDAtLjEtLjM2OC43MDUuNzA1IDAgMCAwLS4yOTUtLjI4IDEuMDA4IDEuMDA4IDAgMCAwLS40OTctLjExYy0uMTkgMC0uMzQ4LjAzLS40NzMuMDkxYS42NDguNjQ4IDAgMCAwLS4yNzYuMjMuNTg3LjU4NyAwIDAgMC0uMDQxLjUzOC41My41MyAwIDAgMCAuMTY1LjE4Yy4wNzcuMDUyLjE4MS4xLjMxMy4xNDcuMTM1LjA0Ni4zMDMuMDkuNTA2LjEzMy4zOC4wOC43MDYuMTgzLjk3OS4zMDguMjc2LjEyMy40ODcuMjgyLjYzNC40NzguMTQ4LjE5NC4yMjEuNDM5LjIyMS43MzYgMCAuMjItLjA0Ny40MjMtLjE0Mi42MDctLjA5Mi4xOC0uMjI3LjMzOS0uNDA1LjQ3M2EyLjAzIDIuMDMgMCAwIDEtLjY0LjMwOWMtLjI0NC4wNzMtLjUyLjExLS44MjcuMTEtLjQ1IDAtLjgzMi0uMDgtMS4xNDUtLjI0YTEuODI2IDEuODI2IDAgMCAxLS43MTItLjYyIDEuNDcgMS40NyAwIDAgMS0uMjQtLjc5aDEuMDcyYS44My44MyAwIDAgMCAuMTcuNDkxYy4xMDQuMTIuMjMzLjIwNy4zODYuMjYyLjE1Ny4wNTMuMzE3LjA3OC40ODMuMDc4LjIgMCAuMzY2LS4wMjYuNTAxLS4wNzhhLjcyMS43MjEgMCAwIDAgLjMwOC0uMjIuNTI3LjUyNyAwIDAgMCAuMTA2LS4zMjJabTQuNTMyLTMuNjI4di44MWgtMi44MDV2LS44MWgyLjgwNVptLTEuOTk1LTEuMjE4aDEuMTA4djQuODE4YzAgLjE1NC4wMjEuMjcyLjA2NC4zNTQuMDQ2LjA4LjEwOS4xMzQuMTg5LjE2MS4wOC4wMjguMTczLjA0Mi4yOC4wNDJhMi4yMDkgMi4yMDkgMCAwIDAgLjM5LS4wNDFsLjAwNS44NDVjLS4wOTEuMDI4LS4xOTkuMDUyLS4zMjEuMDc0LS4xMi4wMjEtLjI1OC4wMzItLjQxNC4wMzItLjI1NSAwLS40OC0uMDQ0LS42NzYtLjEzM2EuOTk1Ljk5NSAwIDAgMS0uNDYtLjQ0NmMtLjExLS4yMDYtLjE2NS0uNDc5LS4xNjUtLjgxOXYtNC44ODhabTguNTggNS4wMnYtMy44MDJoMS4xMTRWNDIuNWgtMS4wNDlsLS4wNjQtMS4xNzJabS4xNTctMS4wMzQuMzczLS4wMWMwIC4zMzUtLjAzNy42NDMtLjExLjkyNWEyLjE0MiAyLjE0MiAwIDAgMS0uMzQuNzMxIDEuNTkgMS41OSAwIDAgMS0uNTkuNDgzIDEuOTkgMS45OSAwIDAgMS0uODYuMTdjLS4yNDEgMC0uNDY0LS4wMzUtLjY2Ni0uMTA2YTEuMzY2IDEuMzY2IDAgMCAxLS41MjQtLjMyNiAxLjQ4NSAxLjQ4NSAwIDAgMS0uMzM2LS41NzUgMi42NTUgMi42NTUgMCAwIDEtLjEyLS44NDZ2LTMuMjE0aDEuMTA5djMuMjIzYzAgLjE4MS4wMjEuMzMzLjA2NC40NTVhLjc3Ljc3IDAgMCAwIC4xNzUuMjljLjA3My4wNzQuMTYuMTI2LjI1Ny4xNTYuMDk5LjAzMS4yMDMuMDQ2LjMxMy4wNDYuMzE2IDAgLjU2NC0uMDYxLjc0NS0uMTg0LjE4NC0uMTI1LjMxNC0uMjk0LjM5LS41MDUuMDgtLjIxMi4xMi0uNDUuMTItLjcxM1ptMy40MDEtMS44MTJ2NS45MzJoLTEuMTA4di02Ljg4OGgxLjAybC4wODguOTU2Wm0zLjI0MiAxLjQ4NnYuMDk2YzAgLjM2Mi0uMDQzLjY5Ny0uMTI5IDEuMDA3YTIuNDYgMi40NiAwIDAgMS0uMzcyLjgwNSAxLjcxNCAxLjcxNCAwIDAgMS0uNjAzLjUyOGMtLjIzOS4xMjYtLjUxNS4xODktLjgyNy4xODktLjMxIDAtLjU4MS0uMDU3LS44MTQtLjE3YTEuNjY3IDEuNjY3IDAgMCAxLS41ODQtLjQ5MiAyLjY3IDIuNjcgMCAwIDEtLjM4Ni0uNzQ1IDQuNzgyIDQuNzgyIDAgMCAxLS4yMDMtLjk0N3YtLjM3M2MuMDQtLjM2NC4xMDgtLjY5Ni4yMDMtLjk5M2EyLjY5IDIuNjkgMCAwIDEgLjM4Ni0uNzY4Yy4xNi0uMjE0LjM1NC0uMzguNTg0LS40OTZhMS43NiAxLjc2IDAgMCAxIC44MDQtLjE3NWMuMzEzIDAgLjU5LjA2MS44MzMuMTg0LjI0Mi4xMi40NDYuMjkxLjYxMS41MTUuMTY2LjIyLjI5LjQ4Ny4zNzMuOC4wODIuMzEuMTI0LjY1NC4xMjQgMS4wMzVabS0xLjEwOC4wOTZ2LS4wOTZjMC0uMjMtLjAyMi0uNDQzLS4wNjUtLjY0LS4wNDMtLjE5OS0uMTEtLjM3NC0uMjAyLS41MjRhLjk5Ljk5IDAgMCAwLS4zNTQtLjM1Ljk2My45NjMgMCAwIDAtLjUxLS4xMjhjLS4xOTcgMC0uMzY1LjAzNC0uNTA2LjEwMWEuOTcuOTcgMCAwIDAtLjM1NC4yNzEgMS4yODIgMS4yODIgMCAwIDAtLjIyLjQxYy0uMDUzLjE1My0uMDkuMzItLjExMS41di44OTNjLjAzNy4yMi4xLjQyMy4xODguNjA3LjA5LjE4NC4yMTUuMzMuMzc3LjQ0MS4xNjYuMTA4LjM3Ny4xNjEuNjM1LjE2MS4xOTkgMCAuMzctLjA0My41MS0uMTI5LjE0MS0uMDg1LjI1Ni0uMjAzLjM0NS0uMzU0LjA5Mi0uMTUzLjE2LS4zMy4yMDItLjUyOC4wNDMtLjIuMDY1LS40MTEuMDY1LS42MzVabTUuMjgxIDEuNDA3di02LjAzMmgxLjExM1Y0Mi41aC0xLjAwN2wtLjEwNi0xLjAzWm0tMy4yMzctMS40MDJ2LS4wOTdjMC0uMzc3LjA0NS0uNzIuMTM0LTEuMDMuMDg4LS4zMTMuMjE3LS41OC4zODYtLjgwNS4xNjgtLjIyNi4zNzQtLjQuNjE2LS41Mi4yNDItLjEyMi41MTUtLjE4My44MTgtLjE4My4zIDAgLjU2NC4wNTguNzkxLjE3NS4yMjcuMTE2LjQyLjI4My41OC41YTIuNiAyLjYgMCAwIDEgLjM4MS43NzNjLjA5NS4yOTguMTYzLjYyOS4yMDMuOTk0di4zMDhjLS4wNC4zNTUtLjEwOC42OC0uMjAzLjk3NGEyLjYyIDIuNjIgMCAwIDEtLjM4MS43NjRjLS4xNi4yMTQtLjM1NC4zOC0uNTg0LjQ5NmExLjcxOSAxLjcxOSAwIDAgMS0uNzk2LjE3NWMtLjMgMC0uNTcyLS4wNjMtLjgxNC0uMTg5YTEuNzk4IDEuNzk4IDAgMCAxLS42MTEtLjUyOCAyLjQ5IDIuNDkgMCAwIDEtLjM4Ni0uOCAzLjYzNiAzLjYzNiAwIDAgMS0uMTM0LTEuMDA3Wm0xLjEwOC0uMDk3di4wOTdjMCAuMjI3LjAyLjQzOC4wNi42MzQuMDQzLjE5Ni4xMDkuMzcuMTk4LjUyLjA4OS4xNDcuMjA0LjI2My4zNDUuMzUuMTQ0LjA4Mi4zMTYuMTIzLjUxNS4xMjMuMjUxIDAgLjQ1OC0uMDU1LjYyLS4xNjUuMTYzLS4xMS4yOS0uMjYuMzgyLS40NDYuMDk1LS4xOS4xNi0uNDAyLjE5My0uNjM1di0uODMyYTIuMDMgMi4wMyAwIDAgMC0uMTE1LS41MDYgMS4zNSAxLjM1IDAgMCAwLS4yMjUtLjQwOS45NDguOTQ4IDAgMCAwLS4zNTQtLjI3NiAxLjExIDEuMTEgMCAwIDAtLjQ5Mi0uMTAxLjk3Mi45NzIgMCAwIDAtLjUxNS4xMjkgMS4wNDMgMS4wNDMgMCAwIDAtLjM1LjM1NGMtLjA4OC4xNS0uMTU0LjMyNS0uMTk3LjUyNC0uMDQzLjItLjA2NS40MTItLjA2NS42NFptNy40MDIgMS41MzFWMzkuMTNjMC0uMTc4LS4wMzMtLjMzMS0uMDk3LS40NmEuNjc2LjY3NiAwIDAgMC0uMjk0LS4yOTkgMS4wMDcgMS4wMDcgMCAwIDAtLjQ4OC0uMTA2Yy0uMTggMC0uMzM3LjAzLS40NjkuMDkyYS43NTkuNzU5IDAgMCAwLS4zMDguMjQ4LjYuNiAwIDAgMC0uMTEuMzU1aC0xLjEwM2MwLS4xOTcuMDQ3LS4zODcuMTQyLS41Ny4wOTUtLjE4NC4yMzMtLjM0OS40MTQtLjQ5My4xOC0uMTQ0LjM5Ny0uMjU3LjY0OC0uMzQuMjUyLS4wODMuNTM0LS4xMjQuODQ2LS4xMjQuMzc0IDAgLjcwNS4wNjMuOTkzLjE4OC4yOTEuMTI2LjUyLjMxNi42ODUuNTcuMTY5LjI1Mi4yNTMuNTY4LjI1My45NDh2Mi4yMTFjMCAuMjI3LjAxNi40MzEuMDQ2LjYxMi4wMzQuMTc4LjA4MS4zMzMuMTQzLjQ2NHYuMDc0aC0xLjEzNmExLjk2IDEuOTYgMCAwIDEtLjEyNC0uNDU1IDMuNzE2IDMuNzE2IDAgMCAxLS4wNDEtLjU0M1ptLjE2LTIuMDI4LjAxLjY4NmgtLjc5NmMtLjIwNSAwLS4zODYuMDItLjU0Mi4wNmExLjExNiAxLjExNiAwIDAgMC0uMzkxLjE2NS43Mi43MiAwIDAgMC0uMzEzLjYyYzAgLjEzMy4wMy4yNTMuMDkyLjM2NC4wNjEuMTA3LjE1LjE5MS4yNjcuMjUzLjEyLjA2MS4yNjMuMDkyLjQzMi4wOTIuMjI3IDAgLjQyNS0uMDQ2LjU5My0uMTM4LjE3Mi0uMDk1LjMwNy0uMjEuNDA1LS4zNDVhLjc1MS43NTEgMCAwIDAgLjE1Ni0uMzlsLjM1OS40OTFjLS4wMzcuMTI2LS4xLjI2LS4xODkuNDA1YTEuOTY0IDEuOTY0IDAgMCAxLS4zNS40MTQgMS43MzYgMS43MzYgMCAwIDEtMS4xOS40NDFjLS4zMjUgMC0uNjE1LS4wNjQtLjg2OS0uMTkzYTEuNTQ1IDEuNTQ1IDAgMCAxLS41OTgtLjUyOSAxLjM3MiAxLjM3MiAwIDAgMS0uMjE2LS43NTljMC0uMjYzLjA1LS40OTYuMTQ3LS42OTguMTAxLS4yMDYuMjQ5LS4zNzcuNDQyLS41MTUuMTk2LS4xMzguNDM1LS4yNDMuNzE3LS4zMTMuMjgyLS4wNzQuNjA0LS4xMS45NjYtLjExaC44NjlabTQuNTg4LTEuOTQ5di44MUg5MS4yMnYtLjgxaDIuODA1Wm0tMS45OTYtMS4yMThoMS4xMDh2NC44MThjMCAuMTU0LjAyMi4yNzIuMDY1LjM1NC4wNDYuMDguMTA5LjEzNC4xODguMTYxLjA4LjAyOC4xNzMuMDQyLjI4LjA0MmEyLjIwOSAyLjIwOSAwIDAgMCAuMzktLjA0MWwuMDA2Ljg0NWMtLjA5Mi4wMjgtLjIuMDUyLS4zMjIuMDc0LS4xMi4wMjEtLjI1Ny4wMzItLjQxMy4wMzItLjI1NSAwLS40OC0uMDQ0LS42NzYtLjEzM2EuOTk0Ljk5NCAwIDAgMS0uNDYtLjQ0NmMtLjExLS4yMDYtLjE2Ni0uNDc5LS4xNjYtLjgxOXYtNC44ODhabTUuMjQgNi4yODVjLS4zNjcgMC0uNy0uMDYtLjk5Ny0uMThhMi4yMDIgMi4yMDIgMCAwIDEtLjc1NC0uNTEgMi4yNjMgMi4yNjMgMCAwIDEtLjQ3NC0uNzY4IDIuNjkgMi42OSAwIDAgMS0uMTY2LS45NTF2LS4xODRjMC0uMzkuMDU3LS43NDIuMTctMS4wNThhMi40IDIuNCAwIDAgMSAuNDc0LS44MWMuMjAzLS4yMjYuNDQyLS40LjcxOC0uNTE5LjI3NS0uMTIuNTc0LS4xNzkuODk2LS4xNzkuMzU2IDAgLjY2Ny4wNi45MzMuMTguMjY3LjExOS40ODguMjg4LjY2My41MDUuMTc3LjIxNS4zMS40Ny4zOTUuNzY4LjA4OS4yOTcuMTMzLjYyNS4xMzMuOTg0di40NzRoLTMuODQ0di0uNzk2aDIuNzV2LS4wODdhMS41NTQgMS41NTQgMCAwIDAtLjEyLS41NjEuOTUzLjk1MyAwIDAgMC0uMzI2LS40MjNjLS4xNDctLjEwNy0uMzQzLS4xNjEtLjU4OS0uMTYxYTEgMSAwIDAgMC0uNDkyLjEyLjk3My45NzMgMCAwIDAtLjM1My4zMzUgMS43NjcgMS43NjcgMCAwIDAtLjIyMS41MzNjLS4wNS4yMDYtLjA3NC40MzctLjA3NC42OTV2LjE4NGMwIC4yMTcuMDMuNDIuMDg4LjYwNy4wNi4xODQuMTUuMzQ0LjI2Ni40ODIuMTE3LjEzOC4yNTguMjQ3LjQyMy4zMjcuMTY2LjA3Ni4zNTQuMTE1LjU2Ni4xMTUuMjY3IDAgLjUwNC0uMDU0LjcxMy0uMTYxLjIwOC0uMTA3LjM4OS0uMjYuNTQyLS40NTVsLjU4NC41NjVhMi4yOCAyLjI4IDAgMCAxLS40MTguNDUgMi4wOTUgMi4wOTUgMCAwIDEtLjYzLjM0NmMtLjI0Ni4wODgtLjUzLjEzMy0uODU2LjEzM1ptOC41NTQtNi44MVY0Mi41aC0xLjEwOHYtNS40MDNsLTEuNjQxLjU1N3YtLjkxNWwyLjYxNi0uOTU3aC4xMzNabTUuNjIyIDUuNjg4di02LjAzMmgxLjExM1Y0Mi41aC0xLjAwN2wtLjEwNi0xLjAzWm0tMy4yMzctMS40MDJ2LS4wOTdjMC0uMzc3LjA0NC0uNzIuMTMzLTEuMDNhMi40MiAyLjQyIDAgMCAxIC4zODctLjgwNWMuMTY4LS4yMjYuMzc0LS40LjYxNi0uNTIuMjQyLS4xMjIuNTE1LS4xODMuODE4LS4xODMuMzAxIDAgLjU2NC4wNTguNzkxLjE3NS4yMjcuMTE2LjQyLjI4My41NzkuNS4xNi4yMTYuMjg3LjQ3My4zODIuNzczLjA5NS4yOTguMTYyLjYyOS4yMDIuOTk0di4zMDhjLS4wNC4zNTUtLjEwNy42OC0uMjAyLjk3NGEyLjYwMyAyLjYwMyAwIDAgMS0uMzgyLjc2NGMtLjE1OS4yMTQtLjM1NC4zOC0uNTg0LjQ5NmExLjcxNiAxLjcxNiAwIDAgMS0uNzk1LjE3NWMtLjMgMC0uNTcyLS4wNjMtLjgxNC0uMTg5YTEuNzkyIDEuNzkyIDAgMCAxLS42MTEtLjUyOCAyLjQ4NSAyLjQ4NSAwIDAgMS0uMzg3LS44IDMuNjMyIDMuNjMyIDAgMCAxLS4xMzMtMS4wMDdabTEuMTA4LS4wOTd2LjA5N2MwIC4yMjcuMDIuNDM4LjA2LjYzNC4wNDMuMTk2LjEwOS4zNy4xOTguNTIuMDg5LjE0Ny4yMDMuMjYzLjM0NC4zNS4xNDUuMDgyLjMxNi4xMjMuNTE1LjEyMy4yNTIgMCAuNDU5LS4wNTUuNjIxLS4xNjUuMTYzLS4xMS4yOS0uMjYuMzgyLS40NDZhMi4wOCAyLjA4IDAgMCAwIC4xOTMtLjYzNXYtLjgzMmEyLjAyNSAyLjAyNSAwIDAgMC0uMTE1LS41MDYgMS4zNDkgMS4zNDkgMCAwIDAtLjIyNS0uNDA5Ljk0OC45NDggMCAwIDAtLjM1NC0uMjc2IDEuMTEgMS4xMSAwIDAgMC0uNDkyLS4xMDEuOTcuOTcgMCAwIDAtLjUxNS4xMjkgMS4wNDYgMS4wNDYgMCAwIDAtLjM1LjM1NGMtLjA4OS4xNS0uMTU1LjMyNS0uMTk3LjUyNC0uMDQzLjItLjA2NS40MTItLjA2NS42NFptOS45NDMgMS41MzFWMzkuMTNjMC0uMTc4LS4wMzMtLjMzMS0uMDk3LS40NmEuNjc4LjY3OCAwIDAgMC0uMjk0LS4yOTkgMS4wMSAxLjAxIDAgMCAwLS40ODgtLjEwNmMtLjE4MSAwLS4zMzcuMDMtLjQ2OS4wOTJhLjc2Ljc2IDAgMCAwLS4zMDguMjQ4LjYwMy42MDMgMCAwIDAtLjExLjM1NWgtMS4xMDRjMC0uMTk3LjA0OC0uMzg3LjE0My0uNTcuMDk1LS4xODQuMjMzLS4zNDkuNDE0LS40OTNhMi4wNyAyLjA3IDAgMCAxIC42NDgtLjM0Yy4yNTEtLjA4My41MzMtLjEyNC44NDYtLjEyNC4zNzQgMCAuNzA1LjA2My45OTMuMTg4LjI5MS4xMjYuNTIuMzE2LjY4NS41Ny4xNjkuMjUyLjI1My41NjguMjUzLjk0OHYyLjIxMWMwIC4yMjcuMDE2LjQzMS4wNDYuNjEyLjAzNC4xNzguMDgxLjMzMy4xNDMuNDY0di4wNzRoLTEuMTM2YTEuOTg2IDEuOTg2IDAgMCAxLS4xMjQtLjQ1NSAzLjY1OSAzLjY1OSAwIDAgMS0uMDQxLS41NDNabS4xNi0yLjAyOC4wMS42ODZoLS43OTZjLS4yMDUgMC0uMzg2LjAyLS41NDIuMDZhMS4xMSAxLjExIDAgMCAwLS4zOTEuMTY1LjcyMi43MjIgMCAwIDAtLjIzNS4yNjcuNzgyLjc4MiAwIDAgMC0uMDc4LjM1NGMwIC4xMzIuMDMxLjI1Mi4wOTIuMzYzYS42NC42NCAwIDAgMCAuMjY3LjI1My45MzQuOTM0IDAgMCAwIC40MzIuMDkyIDEuMjE3IDEuMjE3IDAgMCAwIC45OTgtLjQ4My43NS43NSAwIDAgMCAuMTU2LS4zOWwuMzU5LjQ5MWMtLjAzNy4xMjYtLjEuMjYtLjE4OS40MDVhMS45NjQgMS45NjQgMCAwIDEtLjM0OS40MTQgMS43MyAxLjczIDAgMCAxLTEuMTkxLjQ0MWMtLjMyNSAwLS42MTUtLjA2NC0uODY5LS4xOTNhMS41NDEgMS41NDEgMCAwIDEtLjU5OC0uNTI5IDEuMzcyIDEuMzcyIDAgMCAxLS4yMTYtLjc1OWMwLS4yNjMuMDQ5LS40OTYuMTQ3LS42OTguMTAxLS4yMDYuMjQ4LS4zNzcuNDQyLS41MTUuMTk2LS4xMzguNDM1LS4yNDMuNzE3LS4zMTMuMjgyLS4wNzQuNjA0LS4xMS45NjUtLjExaC44NjlabTUuNDk0LTEuOTQ5aDEuMDA2djQuODM3YzAgLjQ0OC0uMDk1LjgyOC0uMjg1IDEuMTQtLjE5LjMxMy0uNDU1LjU1LS43OTUuNzEzLS4zNC4xNjYtLjczNC4yNDgtMS4xODIuMjQ4YTIuNzcgMi43NyAwIDAgMS0xLjMwNi0uMzUgMS42OCAxLjY4IDAgMCAxLS41MzgtLjQ3M2wuNTItLjY1M2MuMTc4LjIxMi4zNzQuMzY3LjU4OS40NjUuMjE0LjA5OC40MzkuMTQ3LjY3NS4xNDcuMjU1IDAgLjQ3MS0uMDQ4LjY0OS0uMTQzYS45NjEuOTYxIDAgMCAwIC40MTgtLjQwOWMuMDk4LS4xOC4xNDctLjQwMS4xNDctLjY2MnYtMy43MzNsLjEwMi0xLjEyN1ptLTMuMzggMi41NDN2LS4wOTdjMC0uMzc3LjA0Ni0uNzIuMTM4LTEuMDNhMi40NSAyLjQ1IDAgMCAxIC4zOTUtLjgwNWMuMTcyLS4yMjYuMzgtLjQuNjI2LS41Mi4yNDUtLjEyMi41MjItLjE4My44MzItLjE4My4zMjIgMCAuNTk2LjA1OC44MjMuMTc1LjIzLjExNi40MjEuMjgzLjU3NS41LjE1My4yMTYuMjczLjQ3My4zNTguNzczLjA4OS4yOTguMTU1LjYyOS4xOTguOTk0di4zMDhjLS4wNC4zNTUtLjEwNy42OC0uMjAyLjk3NGEyLjY5NyAyLjY5NyAwIDAgMS0uMzc3Ljc2NGMtLjE1Ny4yMTQtLjM1LjM4LS41OC40OTZhMS43MzcgMS43MzcgMCAwIDEtLjgwNC4xNzVjLS4zMDQgMC0uNTc4LS4wNjMtLjgyMy0uMTg5YTEuODc5IDEuODc5IDAgMCAxLS42MjYtLjUyOCAyLjUxOCAyLjUxOCAwIDAgMS0uMzk1LS44IDMuNTIgMy41MiAwIDAgMS0uMTM4LTEuMDA3Wm0xLjEwOC0uMDk3di4wOTdjMCAuMjI3LjAyMi40MzguMDY0LjYzNC4wNDYuMTk2LjExNS4zNy4yMDcuNTIuMDk1LjE0Ny4yMTUuMjYzLjM1OS4zNS4xNDcuMDgyLjMyLjEyMy41Mi4xMjMuMjYgMCAuNDczLS4wNTUuNjM5LS4xNjUuMTY4LS4xMS4yOTctLjI2LjM4Ni0uNDQ2LjA5Mi0uMTkuMTU2LS40MDIuMTkzLS42MzV2LS44MzJhMi4wMjUgMi4wMjUgMCAwIDAtLjExNS0uNTA2IDEuMzQ5IDEuMzQ5IDAgMCAwLS4yMjUtLjQwOS45NDIuOTQyIDAgMCAwLS4zNTktLjI3NiAxLjE5MyAxLjE5MyAwIDAgMC0uNTEtLjEwMWMtLjE5OSAwLS4zNzMuMDQzLS41Mi4xMjlhMS4wNTUgMS4wNTUgMCAwIDAtLjM2My4zNTQgMS43NSAxLjc1IDAgMCAwLS4yMDcuNTI0Yy0uMDQ2LjItLjA2OS40MTItLjA2OS42NFptNC40MTMuMDk3di0uMTA2YzAtLjM1OS4wNTItLjY5MS4xNTYtLjk5OC4xMDQtLjMxLjI1NC0uNTc4LjQ1MS0uODA0YTIuMDMgMi4wMyAwIDAgMSAuNzI2LS41MzRjLjI4OC0uMTI5LjYxMy0uMTkzLjk3NS0uMTkzLjM2NSAwIC42OS4wNjQuOTc1LjE5My4yODguMTI2LjUzMS4zMDQuNzMxLjUzNC4xOTkuMjI2LjM1MS40OTUuNDU1LjgwNC4xMDQuMzA3LjE1Ni42NC4xNTYuOTk4di4xMDZjMCAuMzU4LS4wNTIuNjktLjE1Ni45OTdhMi4zODcgMi4zODcgMCAwIDEtLjQ1NS44MDVjLS4yLjIyNy0uNDQyLjQwNS0uNzI3LjUzM2EyLjM4IDIuMzggMCAwIDEtLjk3LjE4OWMtLjM2NSAwLS42OTEtLjA2My0uOTc5LS4xODlhMi4xMSAyLjExIDAgMCAxLS43MjctLjUzMyAyLjM4NyAyLjM4NyAwIDAgMS0uNDU1LS44MDUgMy4wODYgMy4wODYgMCAwIDEtLjE1Ni0uOTk3Wm0xLjEwOC0uMTA2di4xMDZjMCAuMjIzLjAyMy40MzUuMDY5LjYzNC4wNDYuMi4xMTguMzc0LjIxNi41MjQuMDk4LjE1LjIyNC4yNjkuMzc3LjM1NC4xNTMuMDg2LjMzNi4xMy41NDcuMTMuMjA1IDAgLjM4My0uMDQ0LjUzMy0uMTMuMTU0LS4wODUuMjc5LS4yMDMuMzc3LS4zNTQuMDk4LS4xNS4xNzEtLjMyNS4yMTctLjUyNC4wNDktLjIuMDczLS40MS4wNzMtLjYzNHYtLjEwNmMwLS4yMi0uMDI0LS40My0uMDczLS42MjVhMS42MSAxLjYxIDAgMCAwLS4yMjEtLjUzIDEuMDU3IDEuMDU3IDAgMCAwLS45MTUtLjQ5MWMtLjIwOSAwLS4zODkuMDQ0LS41NDMuMTMzLS4xNS4wODYtLjI3NC4yMDYtLjM3Mi4zNTlhMS42NyAxLjY3IDAgMCAwLS4yMTYuNTI5IDIuNzMzIDIuNzMzIDAgMCAwLS4wNjkuNjI1WiIvPjxwYXRoIGZpbGw9IiNERTIzNDMiIGQ9Ik00OC45MzUgODguOTdoLjQxdjIuNjk0aC0uMjMyYy0xLjE3NSAwLTIuMTkyLjE4Mi0zLjA0OC41NDctLjg0OC4zNjUtMS41NDUuODY2LTIuMDkyIDEuNTA0YTYuMzg0IDYuMzg0IDAgMCAwLTEuMjMgMi4yNDIgOS4wNDUgOS4wNDUgMCAwIDAtLjM5NyAyLjcwN3YzLjEzMWMwIC43OTMuMDg2IDEuNDk1LjI2IDIuMTA1LjE3My42MDIuNDE0IDEuMTA4LjcyNCAxLjUxOC4zMi40MDEuNjg0LjcwNiAxLjA5NC45MTYuNDEuMjEuODUyLjMxNCAxLjMyNi4zMTQuNDkyIDAgLjkzOS0uMSAxLjM0LS4zLjQtLjIxLjc0My0uNDk3IDEuMDI1LS44NjIuMjgzLS4zNjQuNDk3LS43OTcuNjQzLTEuMjk4YTUuODY0IDUuODY0IDAgMCAwIC4yMTktMS42NDEgNS41NiA1LjU2IDAgMCAwLS4yMi0xLjU4NiAzLjk2OSAzLjk2OSAwIDAgMC0uNjE0LTEuMzEzIDIuODI2IDIuODI2IDAgMCAwLTEuMDI2LS44ODhjLS40LS4yMTktLjg2Ni0uMzI4LTEuMzk0LS4zMjgtLjY1NyAwLTEuMjU0LjE1NS0xLjc5MS40NjUtLjUyOS4zMS0uOTU3LjcxNS0xLjI4NSAxLjIxNmEzLjEzNCAzLjEzNCAwIDAgMC0uNTIgMS41NzNsLTEuMjU4LS40MTFjLjA3My0uODQ3LjI2LTEuNjA4LjU2LTIuMjgzYTUuNSA1LjUgMCAwIDEgMS4yMTgtMS43MjMgNS4xMTMgNS4xMTMgMCAwIDEgMS43MzYtMS4wOCA2LjA1OCA2LjA1OCAwIDAgMSAyLjE4Ny0uMzgyYy45NjYgMCAxLjgwNS4xODIgMi41MTYuNTQ2YTQuOTkyIDQuOTkyIDAgMCAxIDEuNzY0IDEuNDljLjQ3NC42Mi44MjUgMS4zMzEgMS4wNTIgMi4xMzQuMjM3Ljc5My4zNTYgMS42MjIuMzU2IDIuNDg4IDAgLjk1Ny0uMTQ2IDEuODUtLjQzOCAyLjY4LS4yOTEuODItLjcyIDEuNTQtMS4yODUgMi4xNmE1Ljc3MyA1Ljc3MyAwIDAgMS0yLjAzNyAxLjQ0OWMtLjc5My4zNDYtMS42OTUuNTE5LTIuNzA3LjUxOS0xLjA2NiAwLTIuMDE5LS4yMDUtMi44NTctLjYxNWE2LjI3NiA2LjI3NiAwIDAgMS0yLjEyLTEuNjk1IDcuNzM2IDcuNzM2IDAgMCAxLTEuMzEyLTIuNDg4IDkuNzE1IDkuNzE1IDAgMCAxLS40NTEtMi45ODF2LTEuMzY3YzAtMS41MTMuMTkxLTIuOTQuNTc0LTQuMjguMzgzLTEuMzQ4Ljk3LTIuNTM4IDEuNzY0LTMuNTY4LjgwMi0xLjAzIDEuODI3LTEuODM2IDMuMDc2LTIuNDIgMS4yNDgtLjU5MiAyLjczOS0uODg4IDQuNDctLjg4OFptMTkuMjEgOC40MDl2My4yNTRjMCAxLjU1OC0uMTU1IDIuODg5LS40NjUgMy45OTItLjMwMSAxLjA5NC0uNzM5IDEuOTgyLTEuMzEzIDIuNjY2YTUuMTQ3IDUuMTQ3IDAgMCAxLTIuMDY0IDEuNTA0Yy0uNzkzLjMxOS0xLjY4Mi40NzgtMi42NjYuNDc4LS43ODQgMC0xLjUxMy0uMS0yLjE4OC0uM2E1LjM0NCA1LjM0NCAwIDAgMS0xLjgwNC0uOTQ0Yy0uNTM4LS40MjgtLjk5OC0uOTgtMS4zODEtMS42NTQtLjM3NC0uNjg0LS42NjYtMS40OTktLjg3NS0yLjQ0Ny0uMi0uOTQ4LS4zMDEtMi4wNDYtLjMwMS0zLjI5NXYtMy4yNTRjMC0xLjU2OC4xNTUtMi44OS40NjUtMy45NjUuMzEtMS4wODUuNzUyLTEuOTY0IDEuMzI2LTIuNjM5YTUuMDUxIDUuMDUxIDAgMCAxIDIuMDUtMS40OWMuODAzLS4zMSAxLjY5Ni0uNDY1IDIuNjgtLjQ2NS43OTMgMCAxLjUyMy4xIDIuMTg4LjMwMWE1LjEyMiA1LjEyMiAwIDAgMSAxLjgwNS45MTZjLjUzNy40Mi45OTMuOTY2IDEuMzY3IDEuNjQuMzgzLjY2Ni42NzQgMS40NzMuODc1IDIuNDIuMi45NC4zIDIuMDMzLjMgMy4yODJabS0zLjI5NSAzLjcxOXYtNC4yMTFjMC0uNzkzLS4wNDYtMS40OS0uMTM3LTIuMDkyLS4wOTEtLjYxLS4yMjgtMS4xMjYtLjQxLTEuNTQ1LS4xNzMtLjQyOC0uMzkyLS43NzUtLjY1Ni0xLjA0YTIuMzEgMi4zMSAwIDAgMC0uOTAzLS41ODcgMy4xODYgMy4xODYgMCAwIDAtMS4xMzUtLjE5MWMtLjUxIDAtLjk2Ni4xLTEuMzY3LjMtLjQuMTkyLS43MzguNTAyLTEuMDExLjkzLS4yNzQuNDI4LS40ODQuOTk0LS42MyAxLjY5NS0uMTM2LjY5My0uMjA0IDEuNTM2LS4yMDQgMi41M3Y0LjIxMWMwIC44MDIuMDQ1IDEuNTA4LjEzNiAyLjExOS4wOTEuNjEuMjI4IDEuMTM1LjQxIDEuNTcyLjE4My40MjguNDAxLjc4NC42NTcgMS4wNjYuMjY0LjI3NC41NjUuNDc0LjkwMi42MDJhMy4yNSAzLjI1IDAgMCAwIDEuMTM1LjE5MWMuNTIgMCAuOTgtLjEgMS4zOC0uMy40MDItLjIwMS43MzktLjUyIDEuMDEyLS45NTcuMjc0LS40NDcuNDc5LTEuMDI2LjYxNS0xLjczNy4xMzctLjcxMS4yMDYtMS41NjMuMjA2LTIuNTU2Wm0xNi45NjYtMy44ODNWMTA5aC0zLjI5NFY5NC4yMDdoMy4xMDNsLjE5MSAzLjAwOFptLS41MzMgMy44NDItMS4xMi0uMDE0YzAtMS4wMjEuMTI3LTEuOTY0LjM4Mi0yLjgzLjI1NS0uODY2LjYyOS0xLjYxOCAxLjEyMS0yLjI1NmE0LjkyOCA0LjkyOCAwIDAgMSAxLjgzMi0xLjQ5Yy43MzgtLjM1NiAxLjU5LS41MzMgMi41NTctLjUzMy42NzQgMCAxLjI5LjEgMS44NDUuM2EzLjY1OCAzLjY1OCAwIDAgMSAxLjQ2My45MTZjLjQyLjQyLjczOS45NTcuOTU3IDEuNjE0LjIyOC42NTYuMzQyIDEuNDQ5LjM0MiAyLjM3OVYxMDloLTMuMjk1di05LjU3YzAtLjcyLS4xMS0xLjI4Ni0uMzI4LTEuNjk2YTEuODE5IDEuODE5IDAgMCAwLS45MTYtLjg3NWMtLjM5Mi0uMTgyLS44NjEtLjI3My0xLjQwOC0uMjczLS42MiAwLTEuMTQ5LjExOC0xLjU4Ni4zNTUtLjQyOC4yMzctLjc4LjU2MS0xLjA1My45NzEtLjI3My40MS0uNDc0Ljg4NC0uNjAxIDEuNDIyYTcuNDQ2IDcuNDQ2IDAgMCAwLS4xOTIgMS43MjNabTkuMTc0LS44NzUtMS41NDUuMzQxYzAtLjg5My4xMjMtMS43MzYuMzctMi41MjlhNi40NDggNi40NDggMCAwIDEgMS4xMDctMi4xMDUgNS4xOTcgNS4xOTcgMCAwIDEgMS44MTgtMS40MzZjLjcyLS4zNDYgMS41NDUtLjUyIDIuNDc1LS41Mi43NTYgMCAxLjQzLjEwNSAyLjAyMy4zMTUuNjAyLjIgMS4xMTIuNTIgMS41MzEuOTU3LjQyLjQzOC43MzkgMS4wMDcuOTU3IDEuNzEuMjIuNjkyLjMyOSAxLjUzLjMyOSAyLjUxNVYxMDloLTMuMzF2LTkuNTg0YzAtLjc0Ny0uMTA5LTEuMzI2LS4zMjctMS43MzYtLjIxLS40MS0uNTEtLjY5My0uOTAzLS44NDgtLjM5MS0uMTY0LS44Ni0uMjQ2LTEuNDA4LS4yNDYtLjUxIDAtLjk2MS4wOTYtMS4zNTMuMjg3YTIuNjg2IDIuNjg2IDAgMCAwLS45NzEuNzggMy40NzUgMy40NzUgMCAwIDAtLjYwMiAxLjEzNCA0Ljg2NyA0Ljg2NyAwIDAgMC0uMTkxIDEuMzk1Wm0yMS45My01Ljk3NWgyLjk5NHYxNC4zODNjMCAxLjMzMS0uMjgzIDIuNDYxLS44NDggMy4zOWE1LjQ0NSA1LjQ0NSAwIDAgMS0yLjM2NSAyLjEyYy0xLjAxMi40OTItMi4xODMuNzM4LTMuNTE0LjczOGE4LjI0OCA4LjI0OCAwIDAgMS0xLjg4Ni0uMjQ2IDcuNzYzIDcuNzYzIDAgMCAxLTEuOTk3LS43OTMgNC45NTIgNC45NTIgMCAwIDEtMS41OTktMS40MDhsMS41NDUtMS45NDJjLjUyOC42MjkgMS4xMTIgMS4wODkgMS43NSAxLjM4MWE0Ljc3NSA0Ljc3NSAwIDAgMCAyLjAxLjQzOGMuNzU2IDAgMS4zOTktLjE0MiAxLjkyNy0uNDI0YTIuODYgMi44NiAwIDAgMCAxLjI0NC0xLjIxN2MuMjkyLS41MzguNDM4LTEuMTk0LjQzOC0xLjk2OVY5Ny41NTdsLjMwMS0zLjM1Wm0tMTAuMDQ5IDcuNTYxdi0uMjg4YzAtMS4xMjEuMTM3LTIuMTQyLjQxLTMuMDYyLjI3My0uOTMuNjY1LTEuNzI3IDEuMTc2LTIuMzkzYTUuMTI1IDUuMTI1IDAgMCAxIDEuODU5LTEuNTQ0Yy43MjktLjM2NSAxLjU1NC0uNTQ3IDIuNDc1LS41NDcuOTU3IDAgMS43NzMuMTczIDIuNDQ3LjUyYTQuNjIyIDQuNjIyIDAgMCAxIDEuNzA5IDEuNDljLjQ1Ni42MzcuODExIDEuNDAzIDEuMDY2IDIuMjk2LjI2NS44ODQuNDYxIDEuODY5LjU4OCAyLjk1M3YuOTE2YTE0Ljc5MyAxNC43OTMgMCAwIDEtLjYwMSAyLjg5OWMtLjI4My44NzUtLjY1NiAxLjYzMS0xLjEyMSAyLjI2OWE0Ljc4MSA0Ljc4MSAwIDAgMS0xLjcyMyAxLjQ3N2MtLjY3NC4zNDYtMS40NzIuNTE5LTIuMzkzLjUxOS0uOTAyIDAtMS43MTgtLjE4Ni0yLjQ0Ny0uNTZhNS41NzIgNS41NzIgMCAwIDEtMS44NTktMS41NzJjLS41MTEtLjY3NS0uOTAzLTEuNDY4LTEuMTc2LTIuMzc5YTEwLjQ5NCAxMC40OTQgMCAwIDEtLjQxLTIuOTk0Wm0zLjI5NS0uMjg4di4yODhjMCAuNjc0LjA2NCAxLjMwMy4xOTEgMS44ODYuMTM3LjU4NC4zNDIgMS4wOTkuNjE1IDEuNTQ1LjI4My40MzguNjM4Ljc4NCAxLjA2NyAxLjAzOS40MzcuMjQ2Ljk1Mi4zNjkgMS41NDUuMzY5Ljc3NSAwIDEuNDA4LS4xNjQgMS45LS40OTIuNTAxLS4zMjguODg0LS43NyAxLjE0OS0xLjMyNmE2Ljc0MyA2Ljc0MyAwIDAgMCAuNTc0LTEuODg3di0yLjQ3NGE2LjA2NCA2LjA2NCAwIDAgMC0uMzQyLTEuNTA0IDQuMDE0IDQuMDE0IDAgMCAwLS42Ny0xLjIxNyAyLjc5NSAyLjc5NSAwIDAgMC0xLjA2Ni0uODJjLS40MjktLjItLjkzNS0uMzAxLTEuNTE4LS4zMDEtLjU5MiAwLTEuMTA3LjEyNy0xLjU0NS4zODNhMy4xNDYgMy4xNDYgMCAwIDAtMS4wOCAxLjA1MyA1LjIyMyA1LjIyMyAwIDAgMC0uNjE1IDEuNTU4IDguNDIxIDguNDIxIDAgMCAwLS4yMDUgMS45Wm0yMS44Mi0xMi4zODYtNy43NTIgMjEuNjE1aC0yLjU4NGw3Ljc2Ni0yMS42MTVoMi41N1ptNS41NzggOC4xMlYxMDloLTMuMjk1Vjk0LjIwN2gzLjEwNGwuMTkxIDMuMDA4Wm0tLjUzMyAzLjg0My0xLjEyMS0uMDE0YzAtMS4wMjEuMTI4LTEuOTY0LjM4My0yLjgzLjI1NS0uODY2LjYyOS0xLjYxOCAxLjEyMS0yLjI1NmE0LjkyOCA0LjkyOCAwIDAgMSAxLjgzMi0xLjQ5Yy43MzgtLjM1NiAxLjU5LS41MzMgMi41NTctLjUzMy42NzQgMCAxLjI4OS4xIDEuODQ1LjNhMy42NTggMy42NTggMCAwIDEgMS40NjMuOTE2Yy40MTkuNDIuNzM4Ljk1Ny45NTcgMS42MTQuMjI4LjY1Ni4zNDIgMS40NDkuMzQyIDIuMzc5VjEwOWgtMy4yOTV2LTkuNTdjMC0uNzItLjEwOS0xLjI4Ni0uMzI4LTEuNjk2YTEuODIgMS44MiAwIDAgMC0uOTE2LS44NzVjLS4zOTItLjE4Mi0uODYxLS4yNzMtMS40MDgtLjI3My0uNjIgMC0xLjE0OS4xMTgtMS41ODYuMzU1LS40MjkuMjM3LS43OC41NjEtMS4wNTMuOTcxLS4yNzMuNDEtLjQ3NC44ODQtLjYwMiAxLjQyMmE3LjQ4IDcuNDggMCAwIDAtLjE5MSAxLjcyM1ptOS4xNzQtLjg3NS0xLjU0NS4zNDFjMC0uODkzLjEyMy0xLjczNi4zNjktMi41MjlhNi40MjYgNi40MjYgMCAwIDEgMS4xMDgtMi4xMDUgNS4xOTQgNS4xOTQgMCAwIDEgMS44MTgtMS40MzZjLjcyLS4zNDYgMS41NDUtLjUyIDIuNDc0LS41Mi43NTcgMCAxLjQzMS4xMDUgMi4wMjQuMzE1LjYwMS4yIDEuMTEyLjUyIDEuNTMxLjk1Ny40MTkuNDM4LjczOCAxLjAwNy45NTcgMS43MS4yMTkuNjkyLjMyOCAxLjUzLjMyOCAyLjUxNVYxMDloLTMuMzA4di05LjU4NGMwLS43NDctLjExLTEuMzI2LS4zMjgtMS43MzYtLjIxLS40MS0uNTExLS42OTMtLjkwMy0uODQ4LS4zOTItLjE2NC0uODYxLS4yNDYtMS40MDgtLjI0Ni0uNTEgMC0uOTYyLjA5Ni0xLjM1My4yODdhMi42ODMgMi42ODMgMCAwIDAtLjk3MS43OCAzLjQ4NSAzLjQ4NSAwIDAgMC0uNjAyIDEuMTM0IDQuODg4IDQuODg4IDAgMCAwLS4xOTEgMS4zOTVabTE0LjQ1MS02LjY1OWgxLjE4OWMuMzkyIDAgLjcxNi0uMDU0Ljk3MS0uMTY0LjI1NS0uMTE4LjQ0Ny0uMjc4LjU3NC0uNDc4LjEzNy0uMi4yMDUtLjQzOC4yMDUtLjcxMWExLjE2IDEuMTYgMCAwIDAtLjM4Mi0uODYxYy0uMjQ2LS4yNTYtLjY2MS0uMzgzLTEuMjQ0LS4zODMtLjQ0NyAwLS44MjUuMDk2LTEuMTM1LjI4Ny0uMzAxLjE4Mi0uNDUxLjQ0Mi0uNDUxLjc4aC0yLjQ4OWMwLS42NDguMTgzLTEuMi41NDctMS42NTUuMzY1LS40NTYuODUyLS44MDIgMS40NjMtMS4wNGE1LjQ3MiA1LjQ3MiAwIDAgMSAxLjk5Ni0uMzU1Yy44MyAwIDEuNTU5LjExOSAyLjE4OC4zNTYuNjI5LjIyOCAxLjExNi41NyAxLjQ2MyAxLjAyNS4zNTUuNDU2LjUzMyAxLjAyMS41MzMgMS42OTUgMCAuNTItLjE2NC45OC0uNDkyIDEuMzgxLS4zMTkuNDAxLS43NjYuNzE2LTEuMzQuOTQ0LS41NjUuMjE5LTEuMjMxLjMyOC0xLjk5Ni4zMjhoLTEuNnYtMS4xNDlabTAgMS43MVY5NC4xMWgxLjZjLjg0NyAwIDEuNTcyLjEgMi4xNzMuMzAxLjYwMi4xOTEgMS4wNTguNDg4IDEuMzY4Ljg4OS4zMTkuNC40NzguOTIuNDc4IDEuNTU4IDAgLjY3NS0uMTkxIDEuMjUtLjU3NCAxLjcyMy0uMzgzLjQ3NC0uOTA3LjgzNC0xLjU3MiAxLjA4LS42NTcuMjQ2LTEuMzk1LjM2OS0yLjIxNS4zNjktLjY4NCAwLTEuMzQ5LS4xMS0xLjk5Ni0uMzI4YTMuNzA2IDMuNzA2IDAgMCAxLTEuNTg2LTEuMDY2Yy0uNDEtLjQ4My0uNjE1LTEuMTE3LS42MTUtMS45aDIuNTAyYzAgLjM1NS4xNjQuNjYuNDkyLjkxNS4zMjguMjU2Ljc3LjM4MyAxLjMyNi4zODMuNTkyIDAgMS4wMy0uMTI3IDEuMzEyLS4zODMuMjkyLS4yNjQuNDM4LS41OTIuNDM4LS45ODQgMC0uMzQ2LS4wODItLjYyNC0uMjQ2LS44MzRhMS4yNzUgMS4yNzUgMCAwIDAtLjY3LS40NTFjLS4yOTItLjEtLjYzNC0uMTUtMS4wMjYtLjE1aC0xLjE4OVoiLz48ZGVmcz48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2MiIHRyYW5zZm9ybT0ibWF0cml4KC4wMDUwMyAwIDAgLjAwNjI5IDAgMCkiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjE5Ny40MTciIGhlaWdodD0iMTU3LjQxNyIgeD0iMS4yOTIiIHk9IjEuMjkyIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIyLjM1NCIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMTA5XzU0MjgiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzExMDlfNTQyOCIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFJQkFRRUJBUUlCQVFFQ0FnSUNBZ1FEQWdJQ0FnVUVCQU1FQmdVR0JnWUZCZ1lHQndrSUJnY0pCd1lHQ0FzSUNRb0tDZ29LQmdnTERBc0tEQWtLQ2dyLzJ3QkRBUUlDQWdJQ0FnVURBd1VLQndZSENnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ3Ivd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhRQUJBUUFEQVFFQkFRRUFBQUFBQUFBQUF3UUNCUVlCQndBSUNmL0VBQnNCQUFNQkFRRUJBUUFBQUFBQUFBQUFBQUVDQXdBRUJnVUgvOW9BREFNQkFBSVFBeEFBQUFEL0FIMDRxWWJlWTRBSHNPd2tURUJnR0FIRVFXQXVRZkN3SjFGa0dpQzA4Q3BrR1ZNaWVqZmZmTi9ad0RHU0FJbkJzTGFmVERBQ0FLenVvNVRJQnlMNDJKc0RLQTZrNllPdURLUkJaUHZYbVB1am1BR2NrTUp5Z1lBY0IwN2dLZ25tQlF6c2cyT3dVWEFrSFRBZ3lDZFJxaHVoTW4zL0FNajk0UTAyY3d5RFRocFRwWEVUVEY4RExpV0JreURZc0twV3lCMWZSenkxa1Ridzc5aEhXWG1QaEgyanpIMU1RWnhRbGNTM294WWcybGNRVVhFNk9xQTZmc2ZOcDNIcFg4VGlRWkdKeGxSWllMUWt0ei9iUEdlaU1QTVNXUHUwN0hIWW0wdFptMm1vb05qWUJSRFlDd05sRjB4SndPSmx4T0JwelZVS0o5VDhkOTZaOFJ4Z2sySmg0Q0ZGd1lFNG5vaE1uakVhREJrSmw4WVlIRFJBZERJQ2t5WlJaQWVmMVh4dnBjR3hrZzRDaWt5QTJFNG1BVmtUZ1hCdGdkQ2VSdE1iSWJnTWhGU3BJV21GVUNxZzYvYi9BQW5yeWJBK0JzQldaMUFxVHpuZkM0QnNEeUZrSjFGMUtxRVVOcERTWlVRcUFYVUhXZDFuZFB2WGhQWGdkT1FEQUdRU3M5Qk00bWRBWUN5QlZDZFFjRVprMHl0RWFUbnFKM1VHV2FpQldZMVNlaWYwSitlZTBuSUIxRmxuT0IxbWJUbFFaUnBJYUlEcE9aenVnUE9heURVVE9KM0UxWkJhWTBRVE1YbVR3L29md1B1QU9BaVlnR1dkbEZrblpKbVFLTE15elVTWjFDcUEyaHZMWGRFWGxUVDluRythU2tjR1NSNTQwaDR5L3dCQ2VCOXJOUkp5SjNTUjFtSW5ZQ1JPd0YxK2RmVCtkMFhQMTJyVGh2cS9LM2ZOMUZSSlhsK08rZC9UK080MjFuYjU5My9OMXRvV0hidWRQNmIvQUQvM2ZNOW5MK3o3bm42WW1USEV5ZGZTTTdKckx6K0JmYitMMk1xOUhHbXB2enhPS1FmY3BrY1oyOE1GWTlESG80YnI1TlAwY3V0cEhySnYvU2ZodmVjWDE4YkI3UVZaZGR0NFYxVENWMWdaT0Y2bzdvQzVUSFJKMkJzbGVFbEphcXFhUy9Mc0pXRW5WZFBIcUs4OWViNzk0ejJlcmRjTU1XV1lpS3N0V1JleGhlWXNOUlJOU3Jlc0lubTdUeFk1bE5VOGxKMXQrWTUyQU5qYU94dHlRNC9VZkgrdUIxMWo3VU1zekxuaEl5Yk84Qk8wN1BnMGJWdHVMUml3NTVrMTBMYmRrMWFEVmRVRzZlZUpIeXduWmJxdzhPNnZ5bnEvSDNaUGdaZVR5YW9IVnFOUmFTc05tdzBKZkphNFc1K3JXT21OcmJjZkowbkRJbGFZTlBVRTRaZWd4cHlkRFRuNlh6bnJmZXJsNkxwNU9SNHUvbmtmVnNQeEduZWU2R3lwUHIzM3paQnJOWHRhOG5QVjBiUzU5UzJTSnBidVQwOW5HdlR4emMvUjBuemZ0ZG4xOCtQeC90TzJndnhSenJvd1NacDFyT3E1MmprVjNuVHgxWGtZdG9NM044blJRTDlUV1ByL0FEK0FFb21UbkdQMStzL1JUVTgvV0ZoLy84UUFJaEFBQWdNQUFnSURBUUVCQUFBQUFBQUFBUVFDQXdVR0ZCSVRBQWNWRUJFVy85b0FDQUVCQUFFQ0FmNmZoSitINUtSSm1abVJrWm16Mm1Ya1Q1K1htWkg0WmY3NUdabWJUY2JqY2JUYWJEWWJqYVordzJld3pNalB6TmhuN0RhYlpYbTgzRzZWeHRsY2J6ZWJUWjV4ak9yeGxVZjU3RFlaR1JrYit4YlAvZktYd21jcDE5WHF5VmtyQlEwVmZKaXlpYTVVZ3JKU1ZIVkZQYkxKc2w4OTN2bGI1MldTa1p6bjd2YVpuNUc4Tmxuc2htVGszcE5UWTdSdm5jYmkwVzUzZHFiYzNaWGtrZnp6UHp5ODVXZTQzeXZsY2JwTmR1VEhhTG5jTHBkN3BlTDM2QmY3M2U3NWU3cGVrOGRIdjk4dlRlOTV0TnNyamFiZmQ3emViemVXQzBXZXlXU3lXdXlXaXlXaTBXdTJiemVXQ3pKbVRaWkxKYkxSYkxVbUN5V2UwV2kwVzVPRjB2RnlUa25TekpvdEZrM2xpVFVtWk5Gb3Nsb3RGc3Rkb3VGeVRoZExzblM4WEpPU2FMUlpMY215MFdpMFdKTVNaTFpiazVKMlRzblpQRjJUcGNMWmNrMldpMld5M0pzdHliazRYWk9sMHV5ZGs2VzVOdE8xTzJQSzdWajFXaFcrOXJ5MEMvTFFPakxUbHBTMVpha3RPV2tkSHZiMzJKajhqc2ZHNmQyM1NpOWRwVzhoaHpGelR1NWhaeS9QNStkdjlOemwrTnQyNmRtdWQwYi9BUDFGbks3dVVhYm5IZEdqY0RNdGVXN0hVbHJzTnlkL1MxN2M2MzJwYWt0ZGxuOVJmVmsxUFJzMEo2Y3RVNmpqT1pxL3ZXYjFXM0RaR3VkWjNYbnlCWGRhdlVQbXUyTjIvVnR1RHNOVjdsRXVUcjYxYnptMjFPUm5DNXF1enVSMlg5R2IwV2xOKzNrSjVGWFpQU1l1N1RPb05mOEFTdllyYjB0dFBjYTBxK1JJYUczcjVlcHkza1dYekZUUXVhYjFMdDZ2a1k1WDN0WGJXMHYwWU4xNjlqNGZZMEpheldpbnlKdmVTZlkxYTlTVjRTNldmeXFlNjF1Nld2WnIvc1V1NUYrbFRCNmpkeitUYWUydnU5L1VmcmJjME1qNDFndXF3ZHpPY3k1TXh1MWhodWVoMVVEZXhwWm4xNVR5bDdCVzVVMmx3MW5pTnZGbzVMeXFleGs4ZDJjOC9adVp5MDVtM0dISlV1VDhUaHp2QUd4cDZhckNmUGJ2dFJqN2MyK1Uvd0QveEFCRkVBQUJBd0lEQlFNS0FRa0dCd0FBQUFBQkFBSURCQkVTSVRFRkUwRlJZUkFpTWdZVU0wSlNZbkdCa2FFakJ5QkRZNUt4d2RIaEZTUWxNRlB3VkhKemdvT2k4Zi9hQUFnQkFRQURQd0gvQUM3L0FKbDh2endoejdRTzNyMmRlMGR0dVBaMS9QUDVvN2IvQU9SMVhWU1NlRDk2bWJxUG9WZjF3bnR6eHQrcVBNZlhzUFQ2SzQvTWwxM2FrenRDRktkWWg5RWZkVXRzbk0raW05cHFxaDRjS3FqNDQybEczZ2FQbXBIRFZuN2Ftdm5hM1BFdjFpOW1WQWp2ek4rWVRHSEtRL0pNUEg5b0tKd3NXaE92M1NWTWRYSjJwZWZvbXZ5RHltREs3bG5rNjN4VjlaRkV6VnBQeFdWN3BoR2loZG5mN3BsdkNtdDFiODBMK0ZBOTBXVVdtN3o2S2w0dFZOZndPc3FNanhPVWZDVnl0K25QN0t1UFRmWk50NmI3TGxPbnMwbFQvd0RVWDYxeVBGN3ZvbURpbkRSMzBYZHRkcWx6c0dmRlZCOVlvSE95YWMxQmV4VWJUaVk3N29jOGtIT3RmSkRVdXVvdjlUanhYZXlIM1RENG83S0YraElUU1BTaFpYeGhlK0UrMXc1U0hTUUtVZXNFL3N0eFR1YWQvc3A1enVwUGFVdkFvMjlJVkljc1pVdXVNcDR2K0o5MGRONGlQMGlOa2RBbkQvNG5XSXhLYTl0OHBSckw5VnIzbW8rcVFuTjBrUHlWejRqOGd2MTVSc2JTcm05RG12OEFka0Z5S2Q3UVR1WTdSd0tIdEllMGg3UlhKeCtxUHRMNGRudkwzdXhxSHRMMzBQYVh2THIyZFVlenFyY1YxUjVycWp6WHZMcWp6WFhzNm9jMWZpdXE2bzgwZWE5NWRVUnhYVmRWMVhWZTkyOWUzcW04ME9hNjluWHM2bzgxMVhWZGV6cXVia09hQTlaZFYxWFZkVjd5SFBzS3Z4UTVycXVxNnJxaHpRWFZkVjFYVmRVVHhSenU1ZFVUeFZ1SzVGSG4yZFYxWFZkZXdjME9hNnJrNWRWMVhWSG1qelE5cEVSOTEvMFR0N205QmpDNitRQzg0eDQ4ckZBTkphNUY3Q1hIUkNRRTNWUFF4YjJwbXd0T1NEb3hKREpkcmhjRWNVUGF6NVhROXBOOXRNdDQwMzJrT0RsMVI1cnFqelRpb3FGaFpzNXVPVnN0bkNWdVZzMUZ0bWtOWFQ2WTdaaGJ2eG15cXp0RUIxVy9EdmJXdnd1b1h1d1J5Qy9KT2N6QzVBT3ZpVjRuOTcxVlVRT2ZGRTF1dGpkU0J1R1dsQnl6SWN2OE9tM1V0bkZtUkMydHNmWjBYbWpHUzRua2ZpTjArNnI5dGZnMWd3Z1JrNFFQVzVwdEhSTnA2bUIwcGJrM0JsM1Y1M0JGdEtERXplRHdrcU4yczJYUlVJanRTMURybjFuc3RaTXFIdjhBUGF3NGJYYmt0bGdHMVM4OVZzOWc5TVZzb093YnQ1NnFoYURlbmRybFpOYTRoMUt3Y3MxWW5EQXpQcXFrdnhOSWJaUGxxS2w3bmZwZjRsVFE3S3d4U0VmaW0vMGFuaktkeGVtdG44Nk1yYkI5N1hVYmFqR3lVZUpXOWZFRklZdDYzVDRxSXg0UTZ6clozS0puZmJQdkxrUW15UVBaZjFTaTZnaC81M0lpbzE5UjNEcDhGeVp4NWYwVXpLQ09FdXlhTWdtRjRiREpmMnlGbG4xVzRZRzhjSVVkVzNDSk4yVzZ1THNrMXd3dDJoR254SERiL3VjYmZSVHRpRnBBTDhuNnB3dnZaYmtEbWkwV0pOK0F1aExLZSs3bVNVQytheDFmcjlWdWFVNEtpMWpjdEp5dFpVd09jandQZE9hcHNGN2NmQ1hLbm11UlNITHhXVk05OW80bWNpWHlXK2lCbzVab0F3bU53eTFBdmY4QWtvSW1ZcG1ndUp0aGpHWVRHVk1iV3c0bXlTTnNUZFRHcGUwd3R5ZU5CelZQREs5OGoydGUwV0RYTnlQTkRhTVFiUjA1Y0d5SElEb3BJYXN0ZkFXV1k2L2Q2ZkJTWDlHZjJQNktWelREaThQQWxSd3NMQ0hGdzlIaWQvQlNiOWdobkJ4YWtqSUZWVDNZak96VFFYVWtVYjQ5NHczNDVwdExJSkcxRVdQbGV5cTRKaHZaY1ZtOTJ6amtuVFJGa3BOdVpmZk5Pb3BzYldOZGxiQzUzRlN6VG1xcUtyUHhEQ2JsUmVMZVRBbDJaT0U4RlRPakVrQWVYT3o3emhucC9OVllMWFJ4T09JMnd0NzJmeVcwYWFRdWtwWGdjaGtWUExJSUlZbll1WjFzalR5Mk5XNjloNGhZbTZaR3lWa01HOWUrVVdOcjRiWHVuVUVkUlMxa2JtUG13dmp1TXNJdW9xaWNZSnpDVCtrZmNOMFdBVVRwNTI2Z2szK0tZK3BsZTJXNEwyNS9KUlJiUmZUVmpCdThiZ0JoMU4xVzB0U1h3MWhZTG5DMGFLZXBlMHNxYnp1ZUJrYkIxOHMxUEZOTERYVHdzbGpkbTNlYTgxU3dNSWt3RjloaExaTlFqSGFXU2RtZW1hYTUyRjFYR3dFM3hFbitDcUlyNGF2R0NiWW1IVlN0OWZJTEc3TWxQWUc0cDJtNHlzYnFxbmZndTRnWjJBVkhITThlZEM0ZG0wSXp4U2lsZ2tjNFJrV2EyNjJoRkhpcTZNc3dQc2VCdnlSbk1iSTRqaVpsbTRBRlRiT3E0ZjhBRW1IRTRkMXVvK2ZCU1JUVlZUU1NBdUxqWjRJNkRWVFN4UnlWSllYTmxCeHVzUzN2RGlvWXR1U3VrNzdoa0w1aXlwbzVERFVVakJESWZ4QTF4R25KVWRkVHorYjNMV3g5MHVBeXVkTWxGZkJjSHZaZ3Z1blJWR0dId2hvOGRyM1VFRC83ekxoYzZ6aFlMMDlTMFdMbmZodndqTUE2NXB0UysxYzV0UTIrYlhNMXlXeS9QVzFGSSsyQnpYR01FRFRYVXFscWRxeXpSemdpVEZodjFBV3hJNHdOb1NSWWdMV0xibFRUZDEwcmlPRjFDeVVGNUpaNjFqWXFLUmhFT3lvSkFOY05NMDJWT3lMZXo3RmhZMzNxTnVTcDZxZVNjTURJcjZNYUJiNEtqWSs4VG5CaDAzbXYyVDJFTzNodnpDYmZHMXhPZWxsTlNSN3BqemJGZkoxa2E3WmJvekk4dkZuWGMva3BRZDQ2TUVBMzcyaURudURhVWtYMXhKOGJDSUd1akp5YzV6eG90bm5ZbFB2cG5PRXNIcEwrQTR0QUxLbGViUjFraEZ2K0RKL2N0aVU4RHpGSE9aSkFjbnVhQVA0cnplbWRGRFZ5d0QxV3NPcVpESzE3WFpPTjNkZWFnbG1NbWx4eFZGVVRSeU5BZmFQbW9ITndBV3RvQW9qNE9mSmJCYnNWdFZVVkR6TzZjdGZDMXdHRnRzam9xRGFkYkhzbWgySnY1SHZ1MjlRR3VkN292a2VnVkZzWHppamoyUXlZU3lOdkVTWFRRT2JyY2dhSG9WREpRTm5lL3BoNm9VZE1QN09sTFg3Mno4WXViS0Y5VytpYkRUNU5OM3NiWS92dDlrUE41WVgwMFF4dXUyWE82YlROTEhSTmQ4VkpWVldKN2hZdjcxaGtGVHNud2JPbGVZaG1IbGxpU3AzdnRHWEhtSExaTEtJTmhMOTl4YmdHRnZ6NHJaKzBYYnA4aGplMjE4UXl6VU1GTEl6OGRyMkRJYnNaRzNLOTdmSzYycFRWRG13UVR5TjRQWkEreCt5cXBNWDRSUERSUXhiQmtvcXVkckoyTURJck12a0ZXenhqY1V6NUR4ZGdObHRvNVJiUExQOEF4bFNSdi92Rk5JRzg4QkZscytLSU83enJISlF5U05aR3h4WTYxeWNsRkZsYmdPV2k4bSsrZHR4VkEwRWJvckw4bkRKR3hpYmFHWXZpQWJrdkplMGMreHErV1JydGQ2TTc4TFpacnpIOG9Pem9ab1ptRXoydkpFV2c5UENxaGxOVk9obmxCMzV0dTVIZytrUHNpNnBCUlMxWXhsOE00M2QrV1dvMFQ2amFMWXJpNVpsbFpvK1FXMmFtaGwyeHM0NzV3ZFprYmJNRHVlWmNMTHlsY0RpMk82Ly9BRjJaZit5Mi9ENmVnZG4rdFovTmJUZ1phU2lkcnJ2Ry93QTFVYlBMUld3dlpqMDc0UDdsUzBrVDRxbWxFdUwxemtRRlB0a0hhZTlFZE9IV3RmdlBQSWYxVHRpYk9jSUtrNDVYZ2VHMXJLcXFhbWFSKytwdzl1TEtVOFBndHExWlpOSHRDcWJ2ZTluVU9Oc3VWMVNUMSs1bWlaM3k1KzlMTG05c1J5V3o5azdLWnRHVFpUSk44Nnd2Yk1LcjJtNXRCVHh4VWpINlBhM01OVkZHUE5ObitleUROenpXbGh0OE1LZDVUYlNrMlpXMUw0aHVpK1BCRTEzeSs2OG12SmhqRzFtMEo2cUo4bHZ3NGNHQjlsNUpNYUkyeTFmd3dCYkhxZ0hRek9adXNtRXdqdkM2cEl0b3h6a0NhTVNCejJ2Wms0WDBXellXTnBJUEpuWTV3aklPb1NmdVNWUjBEKzc1SjdGeHR6YnU2TENma2VDMnpYVmtUNUtlT1BjMWJKUTZLOThqcG1xaXRtbWlsa0c3ZTdFV0Z2VzYvOFFBSkJBQkFBSUNBZ0lEQUFNQkFRQUFBQUFBQVFBUklURkJVV0Z4Z1pHaEVMSEIwZUgvMmdBSUFRRUFBVDhRV0xOUkRLTk1RM2NaTExqZy93QVFFRzJZVlNxVjJSc3kxTDl4RUh1UDRpRW1HR0lpVmtPUzBTUDhJbHR3Z2Fpb1M1dVV3cVpkYk15MkVYTWJMRk1DM2FRVzZoank2aVVXL3dDSXk4d2U0eUdZTEdlVTNaaHNYL0FYZXB4dEViVGNWOU1mdU9XL2szVXhrUmZtTTNUTWJjclJIU0hORDJrZ3EvT0xMN205TUFYWDNDQWwzcUZRMmVMWWxhSHZTT2cxUnFOQUxwbnN1VVZERVZBMi9Fd3hQVWc4ajRsRklUekQxUWhkRHF5QW9MN0k5TGIrMHVFSU9HRlVnOEFJTlhoOGtZd0MvREYxRHhraUw2MG1uOTRQV2EwaEdPbDRpQ1lmVUdGTnpVcktLZW9qZjhwbkFqdkZnUmUzcVhsN0RSQUQ5eExZczIxVU1yYnM1eEJKOW1DNnFzNVlnTlFhMlZGVzI5T0l1M3RUUWJsZEZQQ1FxWHlEbVlBTDRsaERlVWlEaDJCTmJwM2NsZUUxTWcrS1NYRnRSYWkwaVBYV2pNUk1vNFV5clJqMmpNUjVWQ1IxWEh0Z1NteGtIbVo0SThGU3FCSXROa2FXME9CNXVZQXA4MlhBcS9OS1pWR3ZaRHBZdXlEVmd2Tk9TVXRtTGhrcDh3eFdQcTQvc1dWMTJGV3l3VmZWeEFJUHBnckh4eHN5elU0QjZUOWdsNUViSk56Q3c2a1V0RXZ6TitEQTR2MU5sUy9NR05FdUN5SjZnMVRQdVVhZWtZcHBLc0xBN2pKVkMrN2plRnNXelpLcW5QaVpGSk5XSHM5eFdVK3BtUHVwY1JwZGNSVUFQZVltVHlMV2ZUSzJ2c3BsbHRQVmtLNU5Zc202djZKckJnOTFpT3RqdFRuK05DdUNNMGtJeXdkdFBwbEpwVjdsSnAvY3JOd2FXUUhOZHZiS3hBKzBJdjhBN0J5ZjNtMWdLNFdNRlFPMFloYVA3ZzZqTzhZWnIxanZPeFVnbHQ0YlN4MjkrWVNPY1NydkVxb2RjdG5EeEVseXVtRWJKZXBOUzJwMU03b3pzNGRpQmt3bk82Um94Q3QzSExPb2hlTTRzSTJUMWZLTGFTSVdNSUdDYWk2d1k3Y3pOd0tCRkVxMnNwa0dJcThaNUp0ak1oS3EwM2tKdGtDNlpZc3FXZXlWTVNRM0hDVGlYYVBtZnBhbTBaY01tWUZ4QmFRSXRqZWM3UDhBajdxamJOSTFJYmtpMHduTllBSVFaWktxeUpjS05SbUliUXJOeEI3VGpUNUpsWGVLY3NNR1NuZlVqU3NrRFRCWE9qSlVwR3lGVi90bTRoSEJyRldrdUc1QXl5c0d6Szl4UXZiYzI3M0ZFbFNoRFpBY1RlS2g3VkQzRG10dHoreEhjR0NZaFJWYVhqd1Fmb0dYQTZZc0FEUXdGekxVL2M0NWxoY08xT2JBY3pvZjM5eE1rTVdIYldGZnV1RmFGS2IyUU1HcXJRM1E2dDdoOTh4SXdQaFBCTDZUYXZZa1hLVGVLb0hsQitNQjBNaTF0YTVKZkF0S2UzdXFqbTdoRnBvWVBad1VhZ1VZRXB4OHphamZnVmd6RnFXdlNvME43ZlVSOWF0SlF5WnFaU3hSVnBnejVqdDBFS0JISVp1SjRLVU4yL01UQ2pTQVArekprVUtNWS9aU05EQzNFV2xXbGdiT0o0RUJZU1V3ZUZOMzNNeXgwR1AvQUdLd2JzZkxSUFpNUmtaZmx4cFh4U3l4UGsyU1hMTkFOTGU3aDUwbzBadmZNUUNXR3lBaEtOOGEyNU05UUtlUy93QnpNR1I1R0E0ZEZEQjhUYzRIZzhNMmwzUG04WU9yUlhSa0xjcTA0TXMyb0FCb3kxODVqZHZyZkh6TE9LMk1ZQ1hsQXRxN2YzY2VBOGl0eEtXMk5Vam5lRWJPR09XM0ZjUzF2U1cxTnVCakErb3ZuVHBuZ0J3YUY3WUNXMDBOTk1aT0hEbFVHT200bzVkYjVXYW1adFREaFhoeDYxTlFUZHQ0YnBpOXhFRDJ2QUM4NWhjWWJMWVh3cG4vQUZGVnpDaDZpM0VJK09ySEtsNTlPSGNvTnowSU5MWEJOY2t4bFFBbW11NC9uRUVoZ3RYWkdOZ0VnckxrSDl3dFcvcFRoZ0h4dnlvTVdSUnRKbkZxM0hMbzAxU1VUREtOYTlCVXFwdDI2WnNoWWdQbGRQMUE0Tm9CdFkvY1lIWEk5bFI0L3dCWEEzL3FYSWlWRUlMVG5DY1ppd0w4TlIyd0VKcGFnZ3FTNDFCUEhNRk8xVmxIczNMY0J4WnVBMU9yUzZIZ3ZNYW1oYXlRMWl4SUJoYmNvOWpHYitLaktNS0NRenhkR1lMSjJiVVVyT09iUGtnRzFyMWNDcnErTVhCYnNPWjZNYWNGUXEyQTR1b0hoaXNCYUsxQzBwbUxMRFlxOHh3MGMrOGxSeGpIMlFRaUlzUGt2Y09YQzNiY2N3M0VBZnMva011UEJsVjk1cWNsazRIbWMzRWRHN0pIaHFDY29xeFJlV3BiOGNXTFBHU2JHRENyYXZENXVJdGg5YkloU1ZYcmpwRHRBMUQxdHJGZWpaUmNKa1plVXdlVnF6Y0krZURRU0s2MzNHQ3gzUWd1ajBRWlZsWVJYU2JwYTNqY1ZhMkdZN0JGcWQvRTFaZ0w1ZDlPbU5HcGFyOHJtdzZ4QWZPQU5ncDh3YXBiWGExc2VHSlowaWpGL0xEdUJkMVM0R21EeUZxeE1aSk16Z0ZlWWpHV3pvdDNtc2Y2d2NEQjJVZEVabmtSZEhOYmxOZWgrdmF6SFdwVkZadmk0VkVyT1VGMEhCNjRKV3BOcGorQVRjVk5ZYjhZaldoVXE1ZDNlWXpCYWF5dXEyY2FpYjg1MHBPZkRkUTdWNEwyZkpHK1RSUWZqaVh1bnFhOVdCOThRaklpc0pZeXZEbnVBVmRMWWZWcUNmcFdhQnJDZ3UrSVIwMnhuWEMyWll0ZERVN1lzM3l0eWtUUkVXcXVXdzJFVmh2V0V1R1FuSENLVlhheFJSRk1oQnk2bUR6WHhUVFpjMmZFWGFzNThHMmlOWkM3MTFBRGhncDBWQmx2UjJXUTc5NUEyQ3hRajBuSm5aS1hKVmV0UVYrNGdxRG5Gemd0aGRXazZpY1UzbTg3dUJXTTdVNTVncVdhdUd2T1BVWGtFN0YyTGFsWHl2VXIzRWVtalJiQktOcldZMURJTmxsc2xXN2VtUHRmQ1hGeUN5Wk5nNm1LbXE5Wm5rdlpGcVliZStIa1l0VGRVWmdmZFl1T2I1YkVSYnBNeFoxNHRaMWtxdjN4QnhXYVRxTjZ6RzZuU1IrWnV6dTZObjVGNDNsaXlxaXhIcU5EKzhDcFRhbVhxUDRaMUw3aVg5UXZGeDVsb1UyWFZkeXR1eDVGT1N2MGxkcHN6ci9pSFJkOUk4dHMzZTJ3VzNEazEwaHFxNksvTHpBdjQxQ3VoWmNqVU55V3Vyenh6bE0yb0dQL0FBbFlBSW9ldlBMTVI5cERubzVOdXlIcUxGMlM0S1JmT0lsM2tYdGlnS1F0NXBNUGxFMlY1UmJ4WkNscVM5ZGNDTXloQ0ptRktXRldXUEVya0I1THF5TzBlY1JQSGJBREMzUVdhSUJNZHFMeVpDbDBYTDlDRjlBVlRGMTdpM3dsb0VHaW5GcnVNOUMweFlhQmJmZmlGUkFBTk9zWFhkeGpNQzVMRzZOMGtYRE9RSlpZSW9uU1FZMW8vUGFmc3hQT1dJdDRRTXB5cDY2M0RVMmZzZWh5cTVWdHZiUC94QUFzRVFBREFBSUNBZ0VEQXdRQ0F3QUFBQUFBQVFJREVSSWhCREZCRUJOaEZDSlJNa0p4Z1FVanNjSGgvOW9BQ0FFQ0FRRS9BRGFOb1ZDWnRDZjBUK3ZJNUV0Q2JFeE5vVnY1RmV4TVRRdnJOSEkyYzBjem10SElURTBLeFdLaGZSSmlla0pvVEYzOUZiUXFRbm9UMzlkbS9wczJiRStqWWhNbm8ydnBzZHpQc1dTR0s5ZkFzaUZaelFrSnRFc2xpUHVzNTdRc2g5eG5NNW9WeXhYQytTY3EvaysvSXZJalF2SUY1S0g1T3ZRczdwZWgyMFRsdE1qTjEyTHlKWHdMeWwvQitxcEwwTHlxYUo4Z1hsdEg2bW12Wjl3KzRmY1B1bkk1b1ZmSno2RTZGVkNxeWFyWG9UL0J2OENwL3dBRTMrRDJhUXVsNitxVWt3aVZqZnl5WnhyMGNqbXhVem05ZG14UFNaTjlhRlZhSmUxN0pUK0dMa2hYcjRKeXRNV1VXVmVtaFpKMTZGbGtWSTJKaTBMUXRFdkdMUWpvWEUwS2V2UncvQW83SnhpaE1VTFlvalhvVVQvQXNiL2dXUHJzVUppeHZRb2V2UXNmNEpqOEU0MkxDejdTUHRvV0phSmpvU0VMWkloQ1FraFNLV1NoU0pDUWsyS2Z3UW12Z1gwU0ZMRkd4WXlZUW9TRkFwUXBGSXBGSXBRcFFwRklwRWhDa21Qb2laRkFzWk9NVUlVaWtVTVNFaEltZGlRa1RERk9pWkVoTFlzWW9XaFN2Z1NKbGs0eFkwVENGQXAyVGpiRmlGQ1NGSW9vV0toWW1MR0tFaFNraElTRW1LU1ZvU0VoU3lZSmpRcEZJbHYwVGlaTUpDVEVoSVNFaEk0aWtTRklvRkFvRklvSnhrd2hTaEl3eHVpb1hCNlJHUGt5c0NoTFJNampUNk9IRXhZNnQ2UkVhMm1LR0xFeFlXTERvV0w4Q3hDeEN4Q3hvV0pFd1lQK09xKzdmV3ZneWVPOEZjYUp4Y3ZSK254L1o5ZkF2RmFXMlJpVXZhT0pFYW9uRE5wTm42TmZESXg3eUxZdkd4NXNqMzBZL0ZqQytVbCtIenQxTDBZOFhGMUZmQXNWR0x3cjN1L1I1R0J3a29ST0x5SDdST0RNeGVONUh2WitselAwejlEMS9VeGVBdmxzbnc4U1d2WmpYR0VqSkNxK3g0Vi9hYTNPamczR2hZbG80SVdOa0pjRUpDampTWmk2eXY4QXd2OEF5eCtoSENlVFlvMnR0RWpsYk9BbzBoSVVuQW1ObkRTSVhRNTc5Q2s0djBjZjVHbm9Vcm5wam45MnZnbGJnaUZVbHczMFErTnZiK0RmWDA1Sk1VRWlhRTBiMkpwL0FscERpbXRhWjl2VWs0cFNPUDdSY2ZrWEIrbWFKUS9lMnlVdVhJVDJ4UFNhTUpsbW5MMFlzYWM5K3hLay93QUV2YU5VaUxYcENleEplamo3RW1rZEkvQ0ZVcEcxU2FSTGFYb1ZmZzRyYWJPTTZla1NrKzJpdTI5RlJUWDdmWnh5TlA4QTlDVDNwK3hSdzNwa2FuMmU2TWlibDhYbzdjZXlFOUpiSEx2YTJ6V2wwanJScUY3WnFWM3RpWEpNNGt6VGV5VjBka3Q2ZXgwMThrNkUzclNZN3B2V3hLdmhFTnRkb2kzTyt5ZSsyVmFPVzMwSlY3TlBXdEV1K1RUWFJXU2NYN3FmUkhLbW12OEE0ejkvK2pCVXl1dlpqV0c4amxGYVNleE4vQXRlMmExNkpOdVdRS09Xeld1bVRxU0Y4bzVJNUk1YVRGZHZvVHRKdGl6Wkgya2VQa3h0djd1LzlHL0MzN296NXNjTmZhMjErVHo4bFY0TjlhUEdmL1RQK0VYenhaT0taaWJwYlpHSzJ1VXNjNVgvQUdpaTllaUkzdE5Hc1NYb3gzOGFKVGFKZnZaR1ZTdXV0RTAzMm14ZU5PVysyWlBHNFFtbXh4YTN0aWpQUDliWCt0ajNyb243djIrVFJ6ZWlMWGFIYTJTNWZTTGFYUmVPY3NPSDhtTktWeFh3Zi8vRUFETVJBQUlCQkFBRkF3SUVCQWNBQUFBQUFBQUJBZ01SRWlFRUV6RkJVUkFpWVJReUJYR1JvU0JTZ2JFakpETkN3ZEhoLzlvQUNBRURBUUUvQUN6TE1zS0xMRmpFcy80TGlaWXhNVVlHTmh4TE5GaFJNZEZoS3drV1Fpd2tpeGlZZFJ4RWtXR2hMK0Jvc09DTVdXTVN4YXdvTVVTeXQ2UG9XTGVxUTRqVFIxTVJVcHkyaDBhaVhRVWJqZzBXTVRwNk5HS01iaXB3N001Y2ZKR0tGRVVSUmR0R014d3FOYlJ5V2ZUelBwNmcrR1o5TUxoVTBmVHhnUXBxdzZNYkV1RzhNZkN5OG4wYjhrZUZUN24wa1VQaHJDNFQ1UHBvSytoVU93cU1reU1KZEJSTVBCaVlNVVN5UmlqQldIQmVSSmVSSmQyWXg4bUtTMC9WUnYzRXZrU1ZodVhrYzVkckdWWGRraVhOWXFJcWJGVE1FS0JnWWFGQWEzMEx4N290RXdIU3VqbEk1T2psTTVNakJpZ09EUloyTVpEVXh3YVJhVml6TEZtTFJvVFM3bDlEYlBjV2xaM1BjdTRuSy9VelNSbDNSZHBkUnpYY3lYa1RYWm1TdjFITTVqTXhTMXBqcU1kV2FmWVN1WWlSWXhNREczY3N4dElidUkxNk54RzRqZnlhTkRZNUpGeHN5OUhwQ2JFMzZJdVpzYzVNdXhHU00wU2todjBmb3h5OERabVptUmtaR2FMb3lYWXpISm1Sa3k1bWh5WmV4a3pJeU0wT3FPc09yY2N5N0xtU1E1am1aanFKR2NUTkhNTXk1Y3Y1TWtPYUhVUTVvemJNeHpISXViZm8ybHNkUWNoeXNPZGh6SFVOaUxvelNPYWMwZFVkUXpiTWh6U0hObVRMbHliYWlRbTFOYkoxYkpzcGNUemIzSlRTaTJtVTYwcEozSTFWTzVVclFweHU5SWRTNnZGaXFJNTV6eDFtYzNYVTVwekRtTTVyT1l5N1pXNCtNTHFIVW9WbzE0WlJISlI2bjFGVG5kZFhIeGNYZElsWGJqWVZSSms2aXhZNjg2ZDBoY2ZLMW1qbXVOS1ZuMlBxcXRDanJ5Vk9McVZvNHk2RkhqdVhTeGtyMktsWEtNYWtkWE1rVk9MZ2xhSncxV003dWJIUGhsZmJIVm9MdU92d3ExWVhFVVBBdU1TLzJvZkcrSWk0cVVwYjBUZDV0L0pUbktGTFQ3bFBpWkplN1luYWQva3phbGM1ejJLbytxT2FyRlNXMzZScVpRYStDdC9vcjh5TWVwYlJuTEJMc2hWTzF6dVJsaWptYTBaTXpNdmt6UnpMTFFwdHM3aSszcUpwSnRzeUZMSk1VbGRqazFTY3ZsZjhpbDdma2sza1RuS0x1bVVwcGU0cUxPbGRlUmF2NlJoT2EwS2Z6NnE3OU54N21SR1VQNWwrL3dEMEthNWxoMVpOc1UyMjdscE5YUktGU0t1MEpON1NIcE1YMnRKWGVpVG1vT0RYZS84QWMxR1B5V1pXWGtvU2pHU3lXaXBWOTd0MEZnNHRkMnlTeGJzUmFaYXdteStyMk1rZGZUb1lOa0lOUzJ5Y0lMN1pYTWJKdHNoS1VrNHJvTzZXTGZZVXBwV1QwN2k5aUlWNDlKYmkrcEtkTHNKNkpPNVYyUmYrR1VjY2xrcmtjRTNkRG5HN2RpRldOSjNhWDl4TVFxai9BSkYrNHF1N1lMOXg2WkVreDlXSjJPcFNvenFkdEgrWlMwbVRoSnR1YS9VU3BxU2t0SE84eS9Za2xGKzE2R3FjdXFITEV1cjdZNVUyOW5OcEtGa2M2Q2Z0SnpwY3RTaTl2cXZCU3pyUGx3amRsZXJEaG0xSlh2NFYydjFFaXJHVW52cFluS3RDamsyVTJ4cE10SnUwUnhtdnVMTnV5RzlXc05rT0xuU2RvOURuVCs1T3hueE00UHVtU25lTmlNSnpUYVRzT2xWN1JZcUV1cnZiOGlVS01WZlkzRSttK1AzUlg0WGlMSjBsK3JIUjQyS2YybkQwNXVMNXZYNFB3ZWxHUDRoQjdPS1Y2OC96S1NoT2xrVlVvYVJPdENQdG1RcTByYmx2K29wMCtyWkdxNGJnejZuaWIvY1RsVW5LN1kwbkluQ3k2a3FieHZMZHh3aHRXUkhqblFvMnhUdDVLWDRqR2RWcWRPTEZ4Ym1zRnBmQlM0bWg5c0x2emwvNFJtb3QzYmEvTXI0U3F1TkwrcVlvVm10RTF4RCsyekl3cTQrOVdZNzlTbENVdmNrVXEwNk0xUHhzMVViays1Ly8yUT09IiBpZD0iYyIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiLz48L2RlZnM+PC9zdmc+", + "description": "A colorless and odorless gas resulting from incomplete combustion, particularly in motor vehicles.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'carbon', label: 'Carbon monoxide', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Carbon monoxide\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:molecule-co\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#3B911C\"},{\"from\":5,\"to\":10,\"color\":\"#7CC322\"},{\"from\":10,\"to\":25,\"color\":\"#F89E0D\"},{\"from\":25,\"to\":50,\"color\":\"#F77410\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":28,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#3B911C\"},{\"from\":5,\"to\":10,\"color\":\"#7CC322\"},{\"from\":10,\"to\":25,\"color\":\"#F89E0D\"},{\"from\":25,\"to\":50,\"color\":\"#F77410\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:Q08tdmFsdWUtY2FyZC1iYWNrZ3JvdW5kLnBuZw==:Q08tdmFsdWUtY2FyZC1iYWNrZ3JvdW5kLnBuZw==;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Carbon monoxide\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"mg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "enviroment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "carbon monoxide", + "CO" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/carbon_monoxide__co__chart_card.json b/application/src/main/data/json/system/widget_types/carbon_monoxide__co__chart_card.json new file mode 100644 index 0000000000..f826adb4e2 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/carbon_monoxide__co__chart_card.json @@ -0,0 +1,39 @@ +{ + "fqn": "carbon_monoxide_co_chart_card", + "name": "Carbon monoxide (CO) chart card", + "deprecated": false, + "image": "tb-image:Q08tdmFsdWUtYW5kLWNoYXJ0LWNhcmQuc3Zn:Q08tdmFsdWUtYW5kLWNoYXJ0LWNhcmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSIxOTkiIGhlaWdodD0iMTU5IiByeD0iMy41IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC4xMiIvPgo8cGF0aCBkPSJNMTcuMzMzMyAxNi42NjY3QzE2LjYgMTYuNjY2NyAxNiAxNy4yNjY3IDE2IDE4VjIyQzE2IDIyLjM1MzYgMTYuMTQwNSAyMi42OTI3IDE2LjM5MDUgMjIuOTQyOEMxNi42NDA2IDIzLjE5MjggMTYuOTc5NyAyMy4zMzMzIDE3LjMzMzMgMjMuMzMzM0gxOS4zMzMzVjIySDE3LjMzMzNWMThIMTkuMzMzM1YxNi42NjY3SDE3LjMzMzNaTTIxLjMzMzMgMTYuNjY2N0MyMC42IDE2LjY2NjcgMjAgMTcuMjY2NyAyMCAxOFYyMkMyMCAyMi4zNTM2IDIwLjE0MDUgMjIuNjkyNyAyMC4zOTA1IDIyLjk0MjhDMjAuNjQwNiAyMy4xOTI4IDIwLjk3OTcgMjMuMzMzMyAyMS4zMzMzIDIzLjMzMzNIMjIuNjY2N0MyMy40MDY3IDIzLjMzMzMgMjQgMjIuNzQgMjQgMjJWMThDMjQgMTcuNjQ2NCAyMy44NTk1IDE3LjMwNzIgMjMuNjA5NSAxNy4wNTcyQzIzLjM1OTQgMTYuODA3MSAyMy4wMjAzIDE2LjY2NjcgMjIuNjY2NyAxNi42NjY3SDIxLjMzMzNaTTIxLjMzMzMgMThIMjIuNjY2N1YyMkgyMS4zMzMzVjE4WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMzguODkwNiAyMS43NTk4SDQwLjU5OTZDNDAuNTQ0OSAyMi40MTE1IDQwLjM2MjYgMjIuOTkyNSA0MC4wNTI3IDIzLjUwMjlDMzkuNzQyOCAyNC4wMDg4IDM5LjMwNzYgMjQuNDA3NiAzOC43NDcxIDI0LjY5OTJDMzguMTg2NSAyNC45OTA5IDM3LjUwNTIgMjUuMTM2NyAzNi43MDMxIDI1LjEzNjdDMzYuMDg3OSAyNS4xMzY3IDM1LjUzNDIgMjUuMDI3MyAzNS4wNDIgMjQuODA4NkMzNC41NDk4IDI0LjU4NTMgMzQuMTI4MyAyNC4yNzA4IDMzLjc3NzMgMjMuODY1MkMzMy40MjY0IDIzLjQ1NTEgMzMuMTU3NiAyMi45NjA2IDMyLjk3MDcgMjIuMzgxOEMzMi43ODg0IDIxLjgwMzEgMzIuNjk3MyAyMS4xNTU5IDMyLjY5NzMgMjAuNDQwNFYxOS42MTMzQzMyLjY5NzMgMTguODk3OCAzMi43OTA3IDE4LjI1MDcgMzIuOTc3NSAxNy42NzE5QzMzLjE2ODkgMTcuMDkzMSAzMy40NDI0IDE2LjU5ODYgMzMuNzk3OSAxNi4xODg1QzM0LjE1MzMgMTUuNzczOCAzNC41Nzk0IDE1LjQ1NyAzNS4wNzYyIDE1LjIzODNDMzUuNTc3NSAxNS4wMTk1IDM2LjE0MDMgMTQuOTEwMiAzNi43NjQ2IDE0LjkxMDJDMzcuNTU3NiAxNC45MTAyIDM4LjIyNzUgMTUuMDU2IDM4Ljc3NDQgMTUuMzQ3N0MzOS4zMjEzIDE1LjYzOTMgMzkuNzQ1MSAxNi4wNDI2IDQwLjA0NTkgMTYuNTU3NkM0MC4zNTEyIDE3LjA3MjYgNDAuNTM4MSAxNy42NjI4IDQwLjYwNjQgMTguMzI4MUgzOC44OTc1QzM4Ljg1MTkgMTcuODk5NyAzOC43NTE2IDE3LjUzMjkgMzguNTk2NyAxNy4yMjc1QzM4LjQ0NjMgMTYuOTIyMiAzOC4yMjMgMTYuNjg5OCAzNy45MjY4IDE2LjUzMDNDMzcuNjMwNSAxNi4zNjYyIDM3LjI0MzIgMTYuMjg0MiAzNi43NjQ2IDE2LjI4NDJDMzYuMzcyNyAxNi4yODQyIDM2LjAzMDkgMTYuMzU3MSAzNS43MzkzIDE2LjUwMjlDMzUuNDQ3NiAxNi42NDg4IDM1LjIwMzggMTYuODYzIDM1LjAwNzggMTcuMTQ1NUMzNC44MTE4IDE3LjQyODEgMzQuNjYzNyAxNy43NzY3IDM0LjU2MzUgMTguMTkxNEMzNC40Njc4IDE4LjYwMTYgMzQuNDE5OSAxOS4wNzEgMzQuNDE5OSAxOS41OTk2VjIwLjQ0MDRDMzQuNDE5OSAyMC45NDE3IDM0LjQ2MzIgMjEuMzk3NSAzNC41NDk4IDIxLjgwNzZDMzQuNjQxIDIyLjIxMzIgMzQuNzc3NyAyMi41NjE4IDM0Ljk2IDIyLjg1MzVDMzUuMTQ2OCAyMy4xNDUyIDM1LjM4MzggMjMuMzcwOCAzNS42NzA5IDIzLjUzMDNDMzUuOTU4IDIzLjY4OTggMzYuMzAyMSAyMy43Njk1IDM2LjcwMzEgMjMuNzY5NUMzNy4xOTA4IDIzLjc2OTUgMzcuNTg1IDIzLjY5MjEgMzcuODg1NyAyMy41MzcxQzM4LjE5MTEgMjMuMzgyMiAzOC40MjEyIDIzLjE1NjYgMzguNTc2MiAyMi44NjA0QzM4LjczNTcgMjIuNTU5NiAzOC44NDA1IDIyLjE5MjcgMzguODkwNiAyMS43NTk4Wk00Ni40MTQxIDIzLjUxNjZWMTkuOTg5M0M0Ni40MTQxIDE5LjcyNDkgNDYuMzY2MiAxOS40OTcxIDQ2LjI3MDUgMTkuMzA1N0M0Ni4xNzQ4IDE5LjExNDMgNDYuMDI5IDE4Ljk2NjEgNDUuODMzIDE4Ljg2MTNDNDUuNjQxNiAxOC43NTY1IDQ1LjQwMDEgMTguNzA0MSA0NS4xMDg0IDE4LjcwNDFDNDQuODM5NSAxOC43MDQxIDQ0LjYwNzEgMTguNzQ5NyA0NC40MTExIDE4Ljg0MDhDNDQuMjE1MiAxOC45MzIgNDQuMDYyNSAxOS4wNTUgNDMuOTUzMSAxOS4yMUM0My44NDM4IDE5LjM2NDkgNDMuNzg5MSAxOS41NDA0IDQzLjc4OTEgMTkuNzM2M0g0Mi4xNDg0QzQyLjE0ODQgMTkuNDQ0NyA0Mi4yMTkxIDE5LjE2MjEgNDIuMzYwNCAxOC44ODg3QzQyLjUwMTYgMTguNjE1MiA0Mi43MDY3IDE4LjM3MTQgNDIuOTc1NiAxOC4xNTcyQzQzLjI0NDUgMTcuOTQzIDQzLjU2NTggMTcuNzc0NCA0My45Mzk1IDE3LjY1MTRDNDQuMzEzMiAxNy41MjgzIDQ0LjczMjQgMTcuNDY2OCA0NS4xOTczIDE3LjQ2NjhDNDUuNzUzMyAxNy40NjY4IDQ2LjI0NTQgMTcuNTYwMiA0Ni42NzM4IDE3Ljc0NzFDNDcuMTA2OCAxNy45MzM5IDQ3LjQ0NjMgMTguMjE2NSA0Ny42OTI0IDE4LjU5NDdDNDcuOTQzIDE4Ljk2ODQgNDguMDY4NCAxOS40Mzc4IDQ4LjA2ODQgMjAuMDAyOVYyMy4yOTFDNDguMDY4NCAyMy42MjgzIDQ4LjA5MTEgMjMuOTMxMyA0OC4xMzY3IDI0LjIwMDJDNDguMTg2OCAyNC40NjQ1IDQ4LjI1NzUgMjQuNjk0NyA0OC4zNDg2IDI0Ljg5MDZWMjVINDYuNjYwMkM0Ni41ODI3IDI0LjgyMjMgNDYuNTIxMiAyNC41OTY3IDQ2LjQ3NTYgMjQuMzIzMkM0Ni40MzQ2IDI0LjA0NTIgNDYuNDE0MSAyMy43NzY0IDQ2LjQxNDEgMjMuNTE2NlpNNDYuNjUzMyAyMC41MDJMNDYuNjY3IDIxLjUyMDVINDUuNDg0NEM0NS4xNzkgMjEuNTIwNSA0NC45MTAyIDIxLjU1MDEgNDQuNjc3NyAyMS42MDk0QzQ0LjQ0NTMgMjEuNjY0MSA0NC4yNTE2IDIxLjc0NjEgNDQuMDk2NyAyMS44NTU1QzQzLjk0MTcgMjEuOTY0OCA0My44MjU1IDIyLjA5NyA0My43NDggMjIuMjUyQzQzLjY3MDYgMjIuNDA2OSA0My42MzE4IDIyLjU4MjQgNDMuNjMxOCAyMi43NzgzQzQzLjYzMTggMjIuOTc0MyA0My42Nzc0IDIzLjE1NDMgNDMuNzY4NiAyMy4zMTg0QzQzLjg1OTcgMjMuNDc3OSA0My45OTE5IDIzLjYwMzIgNDQuMTY1IDIzLjY5NDNDNDQuMzQyOCAyMy43ODU1IDQ0LjU1NyAyMy44MzExIDQ0LjgwNzYgMjMuODMxMUM0NS4xNDQ5IDIzLjgzMTEgNDUuNDM4OCAyMy43NjI3IDQ1LjY4OTUgMjMuNjI2QzQ1Ljk0NDcgMjMuNDg0NyA0Ni4xNDUyIDIzLjMxMzggNDYuMjkxIDIzLjExMzNDNDYuNDM2OCAyMi45MDgyIDQ2LjUxNDMgMjIuNzE0NSA0Ni41MjM0IDIyLjUzMjJMNDcuMDU2NiAyMy4yNjM3QzQ3LjAwMiAyMy40NTA1IDQ2LjkwODUgMjMuNjUxIDQ2Ljc3NjQgMjMuODY1MkM0Ni42NDQyIDI0LjA3OTQgNDYuNDcxIDI0LjI4NDUgNDYuMjU2OCAyNC40ODA1QzQ2LjA0NzIgMjQuNjcxOSA0NS43OTQzIDI0LjgyOTEgNDUuNDk4IDI0Ljk1MjFDNDUuMjA2NCAyNS4wNzUyIDQ0Ljg2OTEgMjUuMTM2NyA0NC40ODYzIDI1LjEzNjdDNDQuMDAzMyAyNS4xMzY3IDQzLjU3MjYgMjUuMDQxIDQzLjE5NDMgMjQuODQ5NkM0Mi44MTYxIDI0LjY1MzYgNDIuNTE5OSAyNC4zOTE2IDQyLjMwNTcgMjQuMDYzNUM0Mi4wOTE1IDIzLjczMDggNDEuOTg0NCAyMy4zNTQ4IDQxLjk4NDQgMjIuOTM1NUM0MS45ODQ0IDIyLjU0MzYgNDIuMDU3MyAyMi4xOTczIDQyLjIwMzEgMjEuODk2NUM0Mi4zNTM1IDIxLjU5MTEgNDIuNTcyMyAyMS4zMzU5IDQyLjg1OTQgMjEuMTMwOUM0My4xNTEgMjAuOTI1OCA0My41MDY1IDIwLjc3MDggNDMuOTI1OCAyMC42NjZDNDQuMzQ1MSAyMC41NTY2IDQ0LjgyMzYgMjAuNTAyIDQ1LjM2MTMgMjAuNTAySDQ2LjY1MzNaTTUxLjY5NTMgMTkuMDExN1YyNUg1MC4wNDc5VjE3LjYwMzVINTEuNjIwMUw1MS42OTUzIDE5LjAxMTdaTTUzLjk1OCAxNy41NTU3TDUzLjk0NDMgMTkuMDg2OUM1My44NDQxIDE5LjA2ODcgNTMuNzM0NyAxOS4wNTUgNTMuNjE2MiAxOS4wNDU5QzUzLjUwMjMgMTkuMDM2OCA1My4zODgzIDE5LjAzMjIgNTMuMjc0NCAxOS4wMzIyQzUyLjk5MTkgMTkuMDMyMiA1Mi43NDM1IDE5LjA3MzIgNTIuNTI5MyAxOS4xNTUzQzUyLjMxNTEgMTkuMjMyNyA1Mi4xMzUxIDE5LjM0NjcgNTEuOTg5MyAxOS40OTcxQzUxLjg0OCAxOS42NDI5IDUxLjczODYgMTkuODIwNiA1MS42NjExIDIwLjAzMDNDNTEuNTgzNyAyMC4yMzk5IDUxLjUzODEgMjAuNDc0NiA1MS41MjQ0IDIwLjczNDRMNTEuMTQ4NCAyMC43NjE3QzUxLjE0ODQgMjAuMjk2OSA1MS4xOTQgMTkuODY2MiA1MS4yODUyIDE5LjQ2OTdDNTEuMzc2MyAxOS4wNzMyIDUxLjUxMyAxOC43MjQ2IDUxLjY5NTMgMTguNDIzOEM1MS44ODIyIDE4LjEyMyA1Mi4xMTQ2IDE3Ljg4ODMgNTIuMzkyNiAxNy43MTk3QzUyLjY3NTEgMTcuNTUxMSA1My4wMDEgMTcuNDY2OCA1My4zNzAxIDE3LjQ2NjhDNTMuNDcwNCAxNy40NjY4IDUzLjU3NzUgMTcuNDc1OSA1My42OTE0IDE3LjQ5NDFDNTMuODA5OSAxNy41MTI0IDUzLjg5ODggMTcuNTMyOSA1My45NTggMTcuNTU1N1pNNTUuMjQ3MSAxNC41SDU2Ljg5NDVWMjMuNDIwOUw1Ni43MzczIDI1SDU1LjI0NzFWMTQuNVpNNjEuNzIwNyAyMS4yMzM0VjIxLjM3N0M2MS43MjA3IDIxLjkyMzggNjEuNjU5MiAyMi40Mjc0IDYxLjUzNjEgMjIuODg3N0M2MS40MTc2IDIzLjM0MzQgNjEuMjM1NCAyMy43Mzk5IDYwLjk4OTMgMjQuMDc3MUM2MC43NDc3IDI0LjQxNDQgNjAuNDQ2OSAyNC42NzY0IDYwLjA4NjkgMjQuODYzM0M1OS43MzE0IDI1LjA0NTYgNTkuMzE5IDI1LjEzNjcgNTguODQ5NiAyNS4xMzY3QzU4LjM4OTMgMjUuMTM2NyA1Ny45ODgzIDI1LjA1MDEgNTcuNjQ2NSAyNC44NzdDNTcuMzA0NyAyNC43MDM4IDU3LjAxNzYgMjQuNDU3NyA1Ni43ODUyIDI0LjEzODdDNTYuNTU3MyAyMy44MTk3IDU2LjM3MjcgMjMuNDM5MSA1Ni4yMzE0IDIyLjk5NzFDNTYuMDkwMiAyMi41NTUgNTUuOTg5OSAyMi4wNjc0IDU1LjkzMDcgMjEuNTM0MlYyMS4wNzYyQzU1Ljk4OTkgMjAuNTM4NCA1Ni4wOTAyIDIwLjA1MDggNTYuMjMxNCAxOS42MTMzQzU2LjM3MjcgMTkuMTcxMiA1Ni41NTczIDE4Ljc5MDcgNTYuNzg1MiAxOC40NzE3QzU3LjAxNzYgMTguMTQ4MSA1Ny4zMDI0IDE3Ljg5OTcgNTcuNjM5NiAxNy43MjY2QzU3Ljk4MTQgMTcuNTUzNCA1OC4zODAyIDE3LjQ2NjggNTguODM1OSAxNy40NjY4QzU5LjMwOTkgMTcuNDY2OCA1OS43MjY5IDE3LjU1NzkgNjAuMDg2OSAxNy43NDAyQzYwLjQ1MTUgMTcuOTIyNSA2MC43NTQ2IDE4LjE4MjMgNjAuOTk2MSAxOC41MTk1QzYxLjIzNzYgMTguODUyMiA2MS40MTc2IDE5LjI0ODcgNjEuNTM2MSAxOS43MDlDNjEuNjU5MiAyMC4xNjkzIDYxLjcyMDcgMjAuNjc3NCA2MS43MjA3IDIxLjIzMzRaTTYwLjA3MzIgMjEuMzc3VjIxLjIzMzRDNjAuMDczMiAyMC45MDA3IDYwLjA0NTkgMjAuNTg4NSA1OS45OTEyIDIwLjI5NjlDNTkuOTM2NSAyMC4wMDA3IDU5Ljg0NTQgMTkuNzQwOSA1OS43MTc4IDE5LjUxNzZDNTkuNTk0NyAxOS4yOTQzIDU5LjQyNjEgMTkuMTE4OCA1OS4yMTE5IDE4Ljk5MTJDNTkuMDAyMyAxOC44NTkgNTguNzQwMiAxOC43OTMgNTguNDI1OCAxOC43OTNDNTguMTM0MSAxOC43OTMgNTcuODgzNSAxOC44NDMxIDU3LjY3MzggMTguOTQzNEM1Ny40NjQyIDE5LjA0MzYgNTcuMjg4NyAxOS4xODAzIDU3LjE0NzUgMTkuMzUzNUM1Ny4wMDYyIDE5LjUyNjcgNTYuODk0NSAxOS43MjcyIDU2LjgxMjUgMTkuOTU1MUM1Ni43MzUgMjAuMTgyOSA1Ni42ODI2IDIwLjQyOSA1Ni42NTUzIDIwLjY5MzRWMjEuOTMwN0M1Ni42OTYzIDIyLjI3MjUgNTYuNzgyOSAyMi41ODY5IDU2LjkxNSAyMi44NzRDNTcuMDUxOCAyMy4xNTY2IDU3LjI0MzIgMjMuMzg0NCA1Ny40ODkzIDIzLjU1NzZDNTcuNzM1NCAyMy43MjYyIDU4LjA1MjEgMjMuODEwNSA1OC40Mzk1IDIzLjgxMDVDNTguNzQ0OCAyMy44MTA1IDU5LjAwMjMgMjMuNzQ5IDU5LjIxMTkgMjMuNjI2QzU5LjQyMTUgMjMuNTAyOSA1OS41ODc5IDIzLjMzMiA1OS43MTA5IDIzLjExMzNDNTkuODM4NSAyMi44OSA1OS45Mjk3IDIyLjYzMDIgNTkuOTg0NCAyMi4zMzRDNjAuMDQzNiAyMi4wMzc4IDYwLjA3MzIgMjEuNzE4OCA2MC4wNzMyIDIxLjM3N1pNNjMuMDUwOCAyMS4zODM4VjIxLjIyNjZDNjMuMDUwOCAyMC42OTM0IDYzLjEyODMgMjAuMTk4OSA2My4yODMyIDE5Ljc0MzJDNjMuNDM4MiAxOS4yODI5IDYzLjY2MTUgMTguODg0MSA2My45NTMxIDE4LjU0NjlDNjQuMjQ5MyAxOC4yMDUxIDY0LjYwOTQgMTcuOTQwOCA2NS4wMzMyIDE3Ljc1MzlDNjUuNDYxNiAxNy41NjI1IDY1Ljk0NDcgMTcuNDY2OCA2Ni40ODI0IDE3LjQ2NjhDNjcuMDI0NyAxNy40NjY4IDY3LjUwNzggMTcuNTYyNSA2Ny45MzE2IDE3Ljc1MzlDNjguMzYgMTcuOTQwOCA2OC43MjIzIDE4LjIwNTEgNjkuMDE4NiAxOC41NDY5QzY5LjMxNDggMTguODg0MSA2OS41NDA0IDE5LjI4MjkgNjkuNjk1MyAxOS43NDMyQzY5Ljg1MDMgMjAuMTk4OSA2OS45Mjc3IDIwLjY5MzQgNjkuOTI3NyAyMS4yMjY2VjIxLjM4MzhDNjkuOTI3NyAyMS45MTcgNjkuODUwMyAyMi40MTE1IDY5LjY5NTMgMjIuODY3MkM2OS41NDA0IDIzLjMyMjkgNjkuMzE0OCAyMy43MjE3IDY5LjAxODYgMjQuMDYzNUM2OC43MjIzIDI0LjQwMDcgNjguMzYyMyAyNC42NjUgNjcuOTM4NSAyNC44NTY0QzY3LjUxNDYgMjUuMDQzMyA2Ny4wMzM5IDI1LjEzNjcgNjYuNDk2MSAyNS4xMzY3QzY1Ljk1MzggMjUuMTM2NyA2NS40Njg0IDI1LjA0MzMgNjUuMDQgMjQuODU2NEM2NC42MTYyIDI0LjY2NSA2NC4yNTYyIDI0LjQwMDcgNjMuOTYgMjQuMDYzNUM2My42NjM3IDIzLjcyMTcgNjMuNDM4MiAyMy4zMjI5IDYzLjI4MzIgMjIuODY3MkM2My4xMjgzIDIyLjQxMTUgNjMuMDUwOCAyMS45MTcgNjMuMDUwOCAyMS4zODM4Wk02NC42OTgyIDIxLjIyNjZWMjEuMzgzOEM2NC42OTgyIDIxLjcxNjUgNjQuNzMyNCAyMi4wMzA5IDY0LjgwMDggMjIuMzI3MUM2NC44NjkxIDIyLjYyMzQgNjQuOTc2MiAyMi44ODMxIDY1LjEyMjEgMjMuMTA2NEM2NS4yNjc5IDIzLjMyOTggNjUuNDU0OCAyMy41MDUyIDY1LjY4MjYgMjMuNjMyOEM2NS45MTA1IDIzLjc2MDQgNjYuMTgxNiAyMy44MjQyIDY2LjQ5NjEgMjMuODI0MkM2Ni44MDE0IDIzLjgyNDIgNjcuMDY1OCAyMy43NjA0IDY3LjI4OTEgMjMuNjMyOEM2Ny41MTY5IDIzLjUwNTIgNjcuNzAzOCAyMy4zMjk4IDY3Ljg0OTYgMjMuMTA2NEM2Ny45OTU0IDIyLjg4MzEgNjguMTAyNSAyMi42MjM0IDY4LjE3MDkgMjIuMzI3MUM2OC4yNDM4IDIyLjAzMDkgNjguMjgwMyAyMS43MTY1IDY4LjI4MDMgMjEuMzgzOFYyMS4yMjY2QzY4LjI4MDMgMjAuODk4NCA2OC4yNDM4IDIwLjU4ODUgNjguMTcwOSAyMC4yOTY5QzY4LjEwMjUgMjAuMDAwNyA2Ny45OTMyIDE5LjczODYgNjcuODQyOCAxOS41MTA3QzY3LjY5NjkgMTkuMjgyOSA2Ny41MTAxIDE5LjEwNTEgNjcuMjgyMiAxOC45Nzc1QzY3LjA1ODkgMTguODQ1NCA2Ni43OTIzIDE4Ljc3OTMgNjYuNDgyNCAxOC43NzkzQzY2LjE3MjUgMTguNzc5MyA2NS45MDM2IDE4Ljg0NTQgNjUuNjc1OCAxOC45Nzc1QzY1LjQ1MjUgMTkuMTA1MSA2NS4yNjc5IDE5LjI4MjkgNjUuMTIyMSAxOS41MTA3QzY0Ljk3NjIgMTkuNzM4NiA2NC44NjkxIDIwLjAwMDcgNjQuODAwOCAyMC4yOTY5QzY0LjczMjQgMjAuNTg4NSA2NC42OTgyIDIwLjg5ODQgNjQuNjk4MiAyMS4yMjY2Wk03My4xOTI0IDE5LjE4MjZWMjVINzEuNTQ0OVYxNy42MDM1SDczLjA5NjdMNzMuMTkyNCAxOS4xODI2Wk03Mi44OTg0IDIxLjAyODNMNzIuMzY1MiAyMS4wMjE1QzcyLjM2OTggMjAuNDk3NCA3Mi40NDI3IDIwLjAxNjYgNzIuNTg0IDE5LjU3OTFDNzIuNzI5OCAxOS4xNDE2IDcyLjkzMDMgMTguNzY1NiA3My4xODU1IDE4LjQ1MTJDNzMuNDQ1MyAxOC4xMzY3IDczLjc1NTIgMTcuODk1MiA3NC4xMTUyIDE3LjcyNjZDNzQuNDc1MyAxNy41NTM0IDc0Ljg3NjMgMTcuNDY2OCA3NS4zMTg0IDE3LjQ2NjhDNzUuNjczOCAxNy40NjY4IDc1Ljk5NTEgMTcuNTE2OSA3Ni4yODIyIDE3LjYxNzJDNzYuNTczOSAxNy43MTI5IDc2LjgyMjMgMTcuODcwMSA3Ny4wMjczIDE4LjA4ODlDNzcuMjM3IDE4LjMwNzYgNzcuMzk2NSAxOC41OTI0IDc3LjUwNTkgMTguOTQzNEM3Ny42MTUyIDE5LjI4OTcgNzcuNjY5OSAxOS43MTU4IDc3LjY2OTkgMjAuMjIxN1YyNUg3Ni4wMTU2VjIwLjIxNDhDNzYuMDE1NiAxOS44NTk0IDc1Ljk2MzIgMTkuNTc5MSA3NS44NTg0IDE5LjM3NEM3NS43NTgxIDE5LjE2NDQgNzUuNjEgMTkuMDE2MyA3NS40MTQxIDE4LjkyOTdDNzUuMjIyNyAxOC44Mzg1IDc0Ljk4MzQgMTguNzkzIDc0LjY5NjMgMTguNzkzQzc0LjQxMzcgMTguNzkzIDc0LjE2MDggMTguODUyMiA3My45Mzc1IDE4Ljk3MDdDNzMuNzE0MiAxOS4wODkyIDczLjUyNTEgMTkuMjUxIDczLjM3MDEgMTkuNDU2MUM3My4yMTk3IDE5LjY2MTEgNzMuMTAzNSAxOS44OTgxIDczLjAyMTUgMjAuMTY3QzcyLjkzOTUgMjAuNDM1OSA3Mi44OTg0IDIwLjcyMyA3Mi44OTg0IDIxLjAyODNaTTg0Ljk4NTQgMTkuMTA3NFYyNUg4My4zMzc5VjE3LjYwMzVIODQuODg5Nkw4NC45ODU0IDE5LjEwNzRaTTg0LjcxODggMjEuMDI4M0w4NC4xNTgyIDIxLjAyMTVDODQuMTU4MiAyMC41MTExIDg0LjIyMiAyMC4wMzk0IDg0LjM0OTYgMTkuNjA2NEM4NC40NzcyIDE5LjE3MzUgODQuNjY0MSAxOC43OTc1IDg0LjkxMDIgMTguNDc4NUM4NS4xNTYyIDE4LjE1NDkgODUuNDYxNiAxNy45MDY2IDg1LjgyNjIgMTcuNzMzNEM4Ni4xOTUzIDE3LjU1NTcgODYuNjIxNCAxNy40NjY4IDg3LjEwNDUgMTcuNDY2OEM4Ny40NDE3IDE3LjQ2NjggODcuNzQ5MyAxNy41MTY5IDg4LjAyNzMgMTcuNjE3MkM4OC4zMDk5IDE3LjcxMjkgODguNTUzNyAxNy44NjU2IDg4Ljc1ODggMTguMDc1MkM4OC45Njg0IDE4LjI4NDggODkuMTI3OSAxOC41NTM3IDg5LjIzNzMgMTguODgxOEM4OS4zNTEyIDE5LjIxIDg5LjQwODIgMTkuNjA2NCA4OS40MDgyIDIwLjA3MTNWMjVIODcuNzYwN1YyMC4yMTQ4Qzg3Ljc2MDcgMTkuODU0OCA4Ny43MDYxIDE5LjU3MjMgODcuNTk2NyAxOS4zNjcyQzg3LjQ5MTkgMTkuMTYyMSA4Ny4zMzkyIDE5LjAxNjMgODcuMTM4NyAxOC45Mjk3Qzg2Ljk0MjcgMTguODM4NSA4Ni43MDggMTguNzkzIDg2LjQzNDYgMTguNzkzQzg2LjEyNDcgMTguNzkzIDg1Ljg2MDQgMTguODUyMiA4NS42NDE2IDE4Ljk3MDdDODUuNDI3NCAxOS4wODkyIDg1LjI1MiAxOS4yNTEgODUuMTE1MiAxOS40NTYxQzg0Ljk3ODUgMTkuNjYxMSA4NC44NzgzIDE5Ljg5ODEgODQuODE0NSAyMC4xNjdDODQuNzUwNyAyMC40MzU5IDg0LjcxODggMjAuNzIzIDg0LjcxODggMjEuMDI4M1pNODkuMzA1NyAyMC41OTA4TDg4LjUzMzIgMjAuNzYxN0M4OC41MzMyIDIwLjMxNTEgODguNTk0NyAxOS44OTM2IDg4LjcxNzggMTkuNDk3MUM4OC44NDU0IDE5LjA5NiA4OS4wMjk5IDE4Ljc0NTEgODkuMjcxNSAxOC40NDQzQzg5LjUxNzYgMTguMTM5IDg5LjgyMDYgMTcuODk5NyA5MC4xODA3IDE3LjcyNjZDOTAuNTQwNyAxNy41NTM0IDkwLjk1MzEgMTcuNDY2OCA5MS40MTggMTcuNDY2OEM5MS43OTYyIDE3LjQ2NjggOTIuMTMzNSAxNy41MTkyIDkyLjQyOTcgMTcuNjI0QzkyLjczMDUgMTcuNzI0MyA5Mi45ODU3IDE3Ljg4MzggOTMuMTk1MyAxOC4xMDI1QzkzLjQwNDkgMTguMzIxMyA5My41NjQ1IDE4LjYwNjEgOTMuNjczOCAxOC45NTdDOTMuNzgzMiAxOS4zMDM0IDkzLjgzNzkgMTkuNzIyNyA5My44Mzc5IDIwLjIxNDhWMjVIOTIuMTgzNlYyMC4yMDhDOTIuMTgzNiAxOS44MzQzIDkyLjEyODkgMTkuNTQ0OSA5Mi4wMTk1IDE5LjMzOThDOTEuOTE0NyAxOS4xMzQ4IDkxLjc2NDMgMTguOTkzNSA5MS41Njg0IDE4LjkxNkM5MS4zNzI0IDE4LjgzNCA5MS4xMzc3IDE4Ljc5MyA5MC44NjQzIDE4Ljc5M0M5MC42MDkgMTguNzkzIDkwLjM4MzUgMTguODQwOCA5MC4xODc1IDE4LjkzNjVDODkuOTk2MSAxOS4wMjc3IDg5LjgzNDMgMTkuMTU3NiA4OS43MDIxIDE5LjMyNjJDODkuNTcgMTkuNDkwMiA4OS40Njk3IDE5LjY3OTQgODkuNDAxNCAxOS44OTM2Qzg5LjMzNzYgMjAuMTA3NyA4OS4zMDU3IDIwLjM0MDIgODkuMzA1NyAyMC41OTA4Wk05NS40Njg4IDIxLjM4MzhWMjEuMjI2NkM5NS40Njg4IDIwLjY5MzQgOTUuNTQ2MiAyMC4xOTg5IDk1LjcwMTIgMTkuNzQzMkM5NS44NTYxIDE5LjI4MjkgOTYuMDc5NCAxOC44ODQxIDk2LjM3MTEgMTguNTQ2OUM5Ni42NjczIDE4LjIwNTEgOTcuMDI3MyAxNy45NDA4IDk3LjQ1MTIgMTcuNzUzOUM5Ny44Nzk2IDE3LjU2MjUgOTguMzYyNiAxNy40NjY4IDk4LjkwMDQgMTcuNDY2OEM5OS40NDI3IDE3LjQ2NjggOTkuOTI1OCAxNy41NjI1IDEwMC4zNSAxNy43NTM5QzEwMC43NzggMTcuOTQwOCAxMDEuMTQgMTguMjA1MSAxMDEuNDM3IDE4LjU0NjlDMTAxLjczMyAxOC44ODQxIDEwMS45NTggMTkuMjgyOSAxMDIuMTEzIDE5Ljc0MzJDMTAyLjI2OCAyMC4xOTg5IDEwMi4zNDYgMjAuNjkzNCAxMDIuMzQ2IDIxLjIyNjZWMjEuMzgzOEMxMDIuMzQ2IDIxLjkxNyAxMDIuMjY4IDIyLjQxMTUgMTAyLjExMyAyMi44NjcyQzEwMS45NTggMjMuMzIyOSAxMDEuNzMzIDIzLjcyMTcgMTAxLjQzNyAyNC4wNjM1QzEwMS4xNCAyNC40MDA3IDEwMC43OCAyNC42NjUgMTAwLjM1NiAyNC44NTY0Qzk5LjkzMjYgMjUuMDQzMyA5OS40NTE4IDI1LjEzNjcgOTguOTE0MSAyNS4xMzY3Qzk4LjM3MTcgMjUuMTM2NyA5Ny44ODY0IDI1LjA0MzMgOTcuNDU4IDI0Ljg1NjRDOTcuMDM0MiAyNC42NjUgOTYuNjc0MiAyNC40MDA3IDk2LjM3NzkgMjQuMDYzNUM5Ni4wODE3IDIzLjcyMTcgOTUuODU2MSAyMy4zMjI5IDk1LjcwMTIgMjIuODY3MkM5NS41NDYyIDIyLjQxMTUgOTUuNDY4OCAyMS45MTcgOTUuNDY4OCAyMS4zODM4Wk05Ny4xMTYyIDIxLjIyNjZWMjEuMzgzOEM5Ny4xMTYyIDIxLjcxNjUgOTcuMTUwNCAyMi4wMzA5IDk3LjIxODggMjIuMzI3MUM5Ny4yODcxIDIyLjYyMzQgOTcuMzk0MiAyMi44ODMxIDk3LjU0IDIzLjEwNjRDOTcuNjg1OSAyMy4zMjk4IDk3Ljg3MjcgMjMuNTA1MiA5OC4xMDA2IDIzLjYzMjhDOTguMzI4NSAyMy43NjA0IDk4LjU5OTYgMjMuODI0MiA5OC45MTQxIDIzLjgyNDJDOTkuMjE5NCAyMy44MjQyIDk5LjQ4MzcgMjMuNzYwNCA5OS43MDcgMjMuNjMyOEM5OS45MzQ5IDIzLjUwNTIgMTAwLjEyMiAyMy4zMjk4IDEwMC4yNjggMjMuMTA2NEMxMDAuNDEzIDIyLjg4MzEgMTAwLjUyMSAyMi42MjM0IDEwMC41ODkgMjIuMzI3MUMxMDAuNjYyIDIyLjAzMDkgMTAwLjY5OCAyMS43MTY1IDEwMC42OTggMjEuMzgzOFYyMS4yMjY2QzEwMC42OTggMjAuODk4NCAxMDAuNjYyIDIwLjU4ODUgMTAwLjU4OSAyMC4yOTY5QzEwMC41MjEgMjAuMDAwNyAxMDAuNDExIDE5LjczODYgMTAwLjI2MSAxOS41MTA3QzEwMC4xMTUgMTkuMjgyOSA5OS45MjgxIDE5LjEwNTEgOTkuNzAwMiAxOC45Nzc1Qzk5LjQ3NjkgMTguODQ1NCA5OS4yMTAzIDE4Ljc3OTMgOTguOTAwNCAxOC43NzkzQzk4LjU5MDUgMTguNzc5MyA5OC4zMjE2IDE4Ljg0NTQgOTguMDkzOCAxOC45Nzc1Qzk3Ljg3MDQgMTkuMTA1MSA5Ny42ODU5IDE5LjI4MjkgOTcuNTQgMTkuNTEwN0M5Ny4zOTQyIDE5LjczODYgOTcuMjg3MSAyMC4wMDA3IDk3LjIxODggMjAuMjk2OUM5Ny4xNTA0IDIwLjU4ODUgOTcuMTE2MiAyMC44OTg0IDk3LjExNjIgMjEuMjI2NlpNMTA1LjYxIDE5LjE4MjZWMjVIMTAzLjk2M1YxNy42MDM1SDEwNS41MTVMMTA1LjYxIDE5LjE4MjZaTTEwNS4zMTYgMjEuMDI4M0wxMDQuNzgzIDIxLjAyMTVDMTA0Ljc4OCAyMC40OTc0IDEwNC44NjEgMjAuMDE2NiAxMDUuMDAyIDE5LjU3OTFDMTA1LjE0OCAxOS4xNDE2IDEwNS4zNDggMTguNzY1NiAxMDUuNjA0IDE4LjQ1MTJDMTA1Ljg2MyAxOC4xMzY3IDEwNi4xNzMgMTcuODk1MiAxMDYuNTMzIDE3LjcyNjZDMTA2Ljg5MyAxNy41NTM0IDEwNy4yOTQgMTcuNDY2OCAxMDcuNzM2IDE3LjQ2NjhDMTA4LjA5MiAxNy40NjY4IDEwOC40MTMgMTcuNTE2OSAxMDguNyAxNy42MTcyQzEwOC45OTIgMTcuNzEyOSAxMDkuMjQgMTcuODcwMSAxMDkuNDQ1IDE4LjA4ODlDMTA5LjY1NSAxOC4zMDc2IDEwOS44MTQgMTguNTkyNCAxMDkuOTI0IDE4Ljk0MzRDMTEwLjAzMyAxOS4yODk3IDExMC4wODggMTkuNzE1OCAxMTAuMDg4IDIwLjIyMTdWMjVIMTA4LjQzNFYyMC4yMTQ4QzEwOC40MzQgMTkuODU5NCAxMDguMzgxIDE5LjU3OTEgMTA4LjI3NiAxOS4zNzRDMTA4LjE3NiAxOS4xNjQ0IDEwOC4wMjggMTkuMDE2MyAxMDcuODMyIDE4LjkyOTdDMTA3LjY0MSAxOC44Mzg1IDEwNy40MDEgMTguNzkzIDEwNy4xMTQgMTguNzkzQzEwNi44MzIgMTguNzkzIDEwNi41NzkgMTguODUyMiAxMDYuMzU1IDE4Ljk3MDdDMTA2LjEzMiAxOS4wODkyIDEwNS45NDMgMTkuMjUxIDEwNS43ODggMTkuNDU2MUMxMDUuNjM4IDE5LjY2MTEgMTA1LjUyMSAxOS44OTgxIDEwNS40MzkgMjAuMTY3QzEwNS4zNTcgMjAuNDM1OSAxMDUuMzE2IDIwLjcyMyAxMDUuMzE2IDIxLjAyODNaTTExMS43MDUgMjEuMzgzOFYyMS4yMjY2QzExMS43MDUgMjAuNjkzNCAxMTEuNzgzIDIwLjE5ODkgMTExLjkzOCAxOS43NDMyQzExMi4wOTIgMTkuMjgyOSAxMTIuMzE2IDE4Ljg4NDEgMTEyLjYwNyAxOC41NDY5QzExMi45MDQgMTguMjA1MSAxMTMuMjY0IDE3Ljk0MDggMTEzLjY4OCAxNy43NTM5QzExNC4xMTYgMTcuNTYyNSAxMTQuNTk5IDE3LjQ2NjggMTE1LjEzNyAxNy40NjY4QzExNS42NzkgMTcuNDY2OCAxMTYuMTYyIDE3LjU2MjUgMTE2LjU4NiAxNy43NTM5QzExNy4wMTQgMTcuOTQwOCAxMTcuMzc3IDE4LjIwNTEgMTE3LjY3MyAxOC41NDY5QzExNy45NjkgMTguODg0MSAxMTguMTk1IDE5LjI4MjkgMTE4LjM1IDE5Ljc0MzJDMTE4LjUwNSAyMC4xOTg5IDExOC41ODIgMjAuNjkzNCAxMTguNTgyIDIxLjIyNjZWMjEuMzgzOEMxMTguNTgyIDIxLjkxNyAxMTguNTA1IDIyLjQxMTUgMTE4LjM1IDIyLjg2NzJDMTE4LjE5NSAyMy4zMjI5IDExNy45NjkgMjMuNzIxNyAxMTcuNjczIDI0LjA2MzVDMTE3LjM3NyAyNC40MDA3IDExNy4wMTcgMjQuNjY1IDExNi41OTMgMjQuODU2NEMxMTYuMTY5IDI1LjA0MzMgMTE1LjY4OCAyNS4xMzY3IDExNS4xNSAyNS4xMzY3QzExNC42MDggMjUuMTM2NyAxMTQuMTIzIDI1LjA0MzMgMTEzLjY5NCAyNC44NTY0QzExMy4yNzEgMjQuNjY1IDExMi45MSAyNC40MDA3IDExMi42MTQgMjQuMDYzNUMxMTIuMzE4IDIzLjcyMTcgMTEyLjA5MiAyMy4zMjI5IDExMS45MzggMjIuODY3MkMxMTEuNzgzIDIyLjQxMTUgMTExLjcwNSAyMS45MTcgMTExLjcwNSAyMS4zODM4Wk0xMTMuMzUzIDIxLjIyNjZWMjEuMzgzOEMxMTMuMzUzIDIxLjcxNjUgMTEzLjM4NyAyMi4wMzA5IDExMy40NTUgMjIuMzI3MUMxMTMuNTIzIDIyLjYyMzQgMTEzLjYzMSAyMi44ODMxIDExMy43NzYgMjMuMTA2NEMxMTMuOTIyIDIzLjMyOTggMTE0LjEwOSAyMy41MDUyIDExNC4zMzcgMjMuNjMyOEMxMTQuNTY1IDIzLjc2MDQgMTE0LjgzNiAyMy44MjQyIDExNS4xNSAyMy44MjQyQzExNS40NTYgMjMuODI0MiAxMTUuNzIgMjMuNzYwNCAxMTUuOTQzIDIzLjYzMjhDMTE2LjE3MSAyMy41MDUyIDExNi4zNTggMjMuMzI5OCAxMTYuNTA0IDIzLjEwNjRDMTE2LjY1IDIyLjg4MzEgMTE2Ljc1NyAyMi42MjM0IDExNi44MjUgMjIuMzI3MUMxMTYuODk4IDIyLjAzMDkgMTE2LjkzNSAyMS43MTY1IDExNi45MzUgMjEuMzgzOFYyMS4yMjY2QzExNi45MzUgMjAuODk4NCAxMTYuODk4IDIwLjU4ODUgMTE2LjgyNSAyMC4yOTY5QzExNi43NTcgMjAuMDAwNyAxMTYuNjQ3IDE5LjczODYgMTE2LjQ5NyAxOS41MTA3QzExNi4zNTEgMTkuMjgyOSAxMTYuMTY0IDE5LjEwNTEgMTE1LjkzNyAxOC45Nzc1QzExNS43MTMgMTguODQ1NCAxMTUuNDQ3IDE4Ljc3OTMgMTE1LjEzNyAxOC43NzkzQzExNC44MjcgMTguNzc5MyAxMTQuNTU4IDE4Ljg0NTQgMTE0LjMzIDE4Ljk3NzVDMTE0LjEwNyAxOS4xMDUxIDExMy45MjIgMTkuMjgyOSAxMTMuNzc2IDE5LjUxMDdDMTEzLjYzMSAxOS43Mzg2IDExMy41MjMgMjAuMDAwNyAxMTMuNDU1IDIwLjI5NjlDMTEzLjM4NyAyMC41ODg1IDExMy4zNTMgMjAuODk4NCAxMTMuMzUzIDIxLjIyNjZaTTEyMS4zMiAxNy42MDM1TDEyMi43MjkgMjAuMDU3NkwxMjQuMTY0IDE3LjYwMzVIMTI1Ljk3NkwxMjMuNzMzIDIxLjIzMzRMMTI2LjA2NCAyNUgxMjQuMjUzTDEyMi43NDkgMjIuNDQzNEwxMjEuMjQ1IDI1SDExOS40MjdMMTIxLjc1MSAyMS4yMzM0TDExOS41MTYgMTcuNjAzNUgxMjEuMzJaTTEyOS4xMTcgMTcuNjAzNVYyNUgxMjcuNDYzVjE3LjYwMzVIMTI5LjExN1pNMTI3LjM1NCAxNS42NjIxQzEyNy4zNTQgMTUuNDExNSAxMjcuNDM2IDE1LjIwNDEgMTI3LjYgMTUuMDRDMTI3Ljc2OCAxNC44NzE0IDEyOC4wMDEgMTQuNzg3MSAxMjguMjk3IDE0Ljc4NzFDMTI4LjU4OSAxNC43ODcxIDEyOC44MTkgMTQuODcxNCAxMjguOTg3IDE1LjA0QzEyOS4xNTYgMTUuMjA0MSAxMjkuMjQgMTUuNDExNSAxMjkuMjQgMTUuNjYyMUMxMjkuMjQgMTUuOTA4MiAxMjkuMTU2IDE2LjExMzMgMTI4Ljk4NyAxNi4yNzczQzEyOC44MTkgMTYuNDQxNCAxMjguNTg5IDE2LjUyMzQgMTI4LjI5NyAxNi41MjM0QzEyOC4wMDEgMTYuNTIzNCAxMjcuNzY4IDE2LjQ0MTQgMTI3LjYgMTYuMjc3M0MxMjcuNDM2IDE2LjExMzMgMTI3LjM1NCAxNS45MDgyIDEyNy4zNTQgMTUuNjYyMVpNMTM1LjY4NCAyMy40Njg4VjE0LjVIMTM3LjMzOFYyNUgxMzUuODQxTDEzNS42ODQgMjMuNDY4OFpNMTMwLjg3MSAyMS4zODM4VjIxLjI0MDJDMTMwLjg3MSAyMC42Nzk3IDEzMC45MzcgMjAuMTY5MyAxMzEuMDY5IDE5LjcwOUMxMzEuMjAxIDE5LjI0NDEgMTMxLjM5MyAxOC44NDU0IDEzMS42NDQgMTguNTEyN0MxMzEuODk0IDE4LjE3NTUgMTMyLjIgMTcuOTE4IDEzMi41NiAxNy43NDAyQzEzMi45MiAxNy41NTc5IDEzMy4zMjUgMTcuNDY2OCAxMzMuNzc2IDE3LjQ2NjhDMTM0LjIyMyAxNy40NjY4IDEzNC42MTUgMTcuNTUzNCAxMzQuOTUyIDE3LjcyNjZDMTM1LjI4OSAxNy44OTk3IDEzNS41NzYgMTguMTQ4MSAxMzUuODEzIDE4LjQ3MTdDMTM2LjA1IDE4Ljc5MDcgMTM2LjI0IDE5LjE3MzUgMTM2LjM4MSAxOS42MjAxQzEzNi41MjIgMjAuMDYyMiAxMzYuNjIyIDIwLjU1NDQgMTM2LjY4MiAyMS4wOTY3VjIxLjU1NDdDMTM2LjYyMiAyMi4wODMzIDEzNi41MjIgMjIuNTY2NCAxMzYuMzgxIDIzLjAwMzlDMTM2LjI0IDIzLjQ0MTQgMTM2LjA1IDIzLjgxOTcgMTM1LjgxMyAyNC4xMzg3QzEzNS41NzYgMjQuNDU3NyAxMzUuMjg3IDI0LjcwMzggMTM0Ljk0NSAyNC44NzdDMTM0LjYwOCAyNS4wNTAxIDEzNC4yMTQgMjUuMTM2NyAxMzMuNzYzIDI1LjEzNjdDMTMzLjMxNiAyNS4xMzY3IDEzMi45MTMgMjUuMDQzMyAxMzIuNTUzIDI0Ljg1NjRDMTMyLjE5NyAyNC42Njk2IDEzMS44OTQgMjQuNDA3NiAxMzEuNjQ0IDI0LjA3MDNDMTMxLjM5MyAyMy43MzMxIDEzMS4yMDEgMjMuMzM2NiAxMzEuMDY5IDIyLjg4MDlDMTMwLjkzNyAyMi40MjA2IDEzMC44NzEgMjEuOTIxNSAxMzAuODcxIDIxLjM4MzhaTTEzMi41MTkgMjEuMjQwMlYyMS4zODM4QzEzMi41MTkgMjEuNzIxIDEzMi41NDggMjIuMDM1NSAxMzIuNjA3IDIyLjMyNzFDMTMyLjY3MSAyMi42MTg4IDEzMi43NjkgMjIuODc2MyAxMzIuOTAxIDIzLjA5OTZDMTMzLjAzNCAyMy4zMTg0IDEzMy4yMDQgMjMuNDkxNSAxMzMuNDE0IDIzLjYxOTFDMTMzLjYyOCAyMy43NDIyIDEzMy44ODMgMjMuODAzNyAxMzQuMTggMjMuODAzN0MxMzQuNTUzIDIzLjgwMzcgMTM0Ljg2MSAyMy43MjE3IDEzNS4xMDMgMjMuNTU3NkMxMzUuMzQ0IDIzLjM5MzYgMTM1LjUzMyAyMy4xNzI1IDEzNS42NyAyMi44OTQ1QzEzNS44MTEgMjIuNjEyIDEzNS45MDcgMjIuMjk3NSAxMzUuOTU3IDIxLjk1MTJWMjAuNzEzOUMxMzUuOTMgMjAuNDQ1IDEzNS44NzMgMjAuMTk0MyAxMzUuNzg2IDE5Ljk2MTlDMTM1LjcwNCAxOS43Mjk1IDEzNS41OTIgMTkuNTI2NyAxMzUuNDUxIDE5LjM1MzVDMTM1LjMxIDE5LjE3NTggMTM1LjEzNCAxOS4wMzkxIDEzNC45MjUgMTguOTQzNEMxMzQuNzIgMTguODQzMSAxMzQuNDc2IDE4Ljc5MyAxMzQuMTkzIDE4Ljc5M0MxMzMuODkzIDE4Ljc5MyAxMzMuNjM3IDE4Ljg1NjggMTMzLjQyOCAxOC45ODQ0QzEzMy4yMTggMTkuMTEyIDEzMy4wNDUgMTkuMjg3NCAxMzIuOTA4IDE5LjUxMDdDMTMyLjc3NiAxOS43MzQgMTMyLjY3OCAxOS45OTM4IDEzMi42MTQgMjAuMjlDMTMyLjU1IDIwLjU4NjMgMTMyLjUxOSAyMC45MDMgMTMyLjUxOSAyMS4yNDAyWk0xNDIuNTcxIDI1LjEzNjdDMTQyLjAyNCAyNS4xMzY3IDE0MS41MyAyNS4wNDc5IDE0MS4wODggMjQuODcwMUMxNDAuNjUgMjQuNjg3OCAxNDAuMjc3IDI0LjQzNDkgMTM5Ljk2NyAyNC4xMTEzQzEzOS42NjEgMjMuNzg3OCAxMzkuNDI3IDIzLjQwNzIgMTM5LjI2MyAyMi45Njk3QzEzOS4wOTkgMjIuNTMyMiAxMzkuMDE3IDIyLjA2MDUgMTM5LjAxNyAyMS41NTQ3VjIxLjI4MTJDMTM5LjAxNyAyMC43MDI1IDEzOS4xMDEgMjAuMTc4NCAxMzkuMjcgMTkuNzA5QzEzOS40MzggMTkuMjM5NiAxMzkuNjczIDE4LjgzODUgMTM5Ljk3NCAxOC41MDU5QzE0MC4yNzQgMTguMTY4NiAxNDAuNjMgMTcuOTExMSAxNDEuMDQgMTcuNzMzNEMxNDEuNDUgMTcuNTU1NyAxNDEuODk1IDE3LjQ2NjggMTQyLjM3MyAxNy40NjY4QzE0Mi45MDIgMTcuNDY2OCAxNDMuMzY0IDE3LjU1NTcgMTQzLjc2MSAxNy43MzM0QzE0NC4xNTcgMTcuOTExMSAxNDQuNDg1IDE4LjE2MTggMTQ0Ljc0NSAxOC40ODU0QzE0NS4wMDkgMTguODA0NCAxNDUuMjA1IDE5LjE4NDkgMTQ1LjMzMyAxOS42MjdDMTQ1LjQ2NSAyMC4wNjkgMTQ1LjUzMSAyMC41NTY2IDE0NS41MzEgMjEuMDg5OFYyMS43OTM5SDEzOS44MTZWMjAuNjExM0gxNDMuOTA0VjIwLjQ4MTRDMTQzLjg5NSAyMC4xODUyIDE0My44MzYgMTkuOTA3MiAxNDMuNzI3IDE5LjY0NzVDMTQzLjYyMiAxOS4zODc3IDE0My40NiAxOS4xNzgxIDE0My4yNDEgMTkuMDE4NkMxNDMuMDIyIDE4Ljg1OSAxNDIuNzMxIDE4Ljc3OTMgMTQyLjM2NiAxOC43NzkzQzE0Mi4wOTMgMTguNzc5MyAxNDEuODQ5IDE4LjgzODUgMTQxLjYzNSAxOC45NTdDMTQxLjQyNSAxOS4wNzEgMTQxLjI1IDE5LjIzNzMgMTQxLjEwOCAxOS40NTYxQzE0MC45NjcgMTkuNjc0OCAxNDAuODU4IDE5LjkzOTEgMTQwLjc4IDIwLjI0OUMxNDAuNzA3IDIwLjU1NDQgMTQwLjY3MSAyMC44OTg0IDE0MC42NzEgMjEuMjgxMlYyMS41NTQ3QzE0MC42NzEgMjEuODc4MyAxNDAuNzE0IDIyLjE3OSAxNDAuODAxIDIyLjQ1N0MxNDAuODkyIDIyLjczMDUgMTQxLjAyNCAyMi45Njk3IDE0MS4xOTcgMjMuMTc0OEMxNDEuMzcgMjMuMzc5OSAxNDEuNTggMjMuNTQxNyAxNDEuODI2IDIzLjY2MDJDMTQyLjA3MiAyMy43NzQxIDE0Mi4zNTMgMjMuODMxMSAxNDIuNjY3IDIzLjgzMTFDMTQzLjA2MyAyMy44MzExIDE0My40MTcgMjMuNzUxMyAxNDMuNzI3IDIzLjU5MThDMTQ0LjAzNiAyMy40MzIzIDE0NC4zMDUgMjMuMjA2NyAxNDQuNTMzIDIyLjkxNUwxNDUuNDAxIDIzLjc1NTlDMTQ1LjI0MiAyMy45ODgzIDE0NS4wMzUgMjQuMjExNiAxNDQuNzc5IDI0LjQyNThDMTQ0LjUyNCAyNC42MzU0IDE0NC4yMTIgMjQuODA2MyAxNDMuODQzIDI0LjkzODVDMTQzLjQ3OCAyNS4wNzA2IDE0My4wNTQgMjUuMTM2NyAxNDIuNTcxIDI1LjEzNjdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik02NC4wODIgNTguMjQ4SDY0LjYwOTRWNjEuNzEwOUg2NC4zMTA1QzYyLjc5ODggNjEuNzEwOSA2MS40OTIyIDYxLjk0NTMgNjAuMzkwNiA2Mi40MTQxQzU5LjMwMDggNjIuODgyOCA1OC40MDQzIDYzLjUyNzMgNTcuNzAxMiA2NC4zNDc3QzU2Ljk5OCA2NS4xNjggNTYuNDcwNyA2Ni4xMjg5IDU2LjExOTEgNjcuMjMwNUM1NS43NzkzIDY4LjMyMDMgNTUuNjA5NCA2OS40ODA1IDU1LjYwOTQgNzAuNzEwOVY3NC43MzYzQzU1LjYwOTQgNzUuNzU1OSA1NS43MjA3IDc2LjY1ODIgNTUuOTQzNCA3Ny40NDM0QzU2LjE2NiA3OC4yMTY4IDU2LjQ3NjYgNzguODY3MiA1Ni44NzUgNzkuMzk0NUM1Ny4yODUyIDc5LjkxMDIgNTcuNzUzOSA4MC4zMDI3IDU4LjI4MTIgODAuNTcyM0M1OC44MDg2IDgwLjg0MTggNTkuMzc3IDgwLjk3NjYgNTkuOTg2MyA4MC45NzY2QzYwLjYxOTEgODAuOTc2NiA2MS4xOTM0IDgwLjg0NzcgNjEuNzA5IDgwLjU4OThDNjIuMjI0NiA4MC4zMjAzIDYyLjY2NDEgNzkuOTUxMiA2My4wMjczIDc5LjQ4MjRDNjMuMzkwNiA3OS4wMTM3IDYzLjY2NiA3OC40NTcgNjMuODUzNSA3Ny44MTI1QzY0LjA0MSA3Ny4xNjggNjQuMTM0OCA3Ni40NjQ4IDY0LjEzNDggNzUuNzAzMUM2NC4xMzQ4IDc0Ljk3NjYgNjQuMDQxIDc0LjI5NjkgNjMuODUzNSA3My42NjQxQzYzLjY3NzcgNzMuMDE5NSA2My40MTQxIDcyLjQ1NyA2My4wNjI1IDcxLjk3NjZDNjIuNzEwOSA3MS40ODQ0IDYyLjI3MTUgNzEuMTAzNSA2MS43NDQxIDcwLjgzNEM2MS4yMjg1IDcwLjU1MjcgNjAuNjMwOSA3MC40MTIxIDU5Ljk1MTIgNzAuNDEyMUM1OS4xMDc0IDcwLjQxMjEgNTguMzM5OCA3MC42MTEzIDU3LjY0ODQgNzEuMDA5OEM1Ni45Njg4IDcxLjQwODIgNTYuNDE4IDcxLjkyOTcgNTUuOTk2MSA3Mi41NzQyQzU1LjU4NTkgNzMuMjA3IDU1LjM2MzMgNzMuODgwOSA1NS4zMjgxIDc0LjU5NTdMNTMuNzEwOSA3NC4wNjg0QzUzLjgwNDcgNzIuOTc4NSA1NC4wNDQ5IDcyIDU0LjQzMTYgNzEuMTMyOEM1NC44MzAxIDcwLjI2NTYgNTUuMzUxNiA2OS41MjczIDU1Ljk5NjEgNjguOTE4QzU2LjY0MDYgNjguMzA4NiA1Ny4zODQ4IDY3Ljg0NTcgNTguMjI4NSA2Ny41MjkzQzU5LjA4NCA2Ny4yMDEyIDYwLjAyMTUgNjcuMDM3MSA2MS4wNDEgNjcuMDM3MUM2Mi4yODMyIDY3LjAzNzEgNjMuMzYxMyA2Ny4yNzE1IDY0LjI3NTQgNjcuNzQwMkM2NS4xODk1IDY4LjIwOSA2NS45NDUzIDY4Ljg0NzcgNjYuNTQzIDY5LjY1NjJDNjcuMTUyMyA3MC40NTMxIDY3LjYwMzUgNzEuMzY3MiA2Ny44OTY1IDcyLjM5ODRDNjguMjAxMiA3My40MTggNjguMzUzNSA3NC40ODQ0IDY4LjM1MzUgNzUuNTk3N0M2OC4zNTM1IDc2LjgyODEgNjguMTY2IDc3Ljk3NjYgNjcuNzkxIDc5LjA0M0M2Ny40MTYgODAuMDk3NyA2Ni44NjUyIDgxLjAyMzQgNjYuMTM4NyA4MS44MjAzQzY1LjQyMzggODIuNjE3MiA2NC41NTA4IDgzLjIzODMgNjMuNTE5NSA4My42ODM2QzYyLjUgODQuMTI4OSA2MS4zMzk4IDg0LjM1MTYgNjAuMDM5MSA4NC4zNTE2QzU4LjY2OCA4NC4zNTE2IDU3LjQ0MzQgODQuMDg3OSA1Ni4zNjUyIDgzLjU2MDVDNTUuMjk4OCA4My4wMzMyIDU0LjM5MDYgODIuMzA2NiA1My42NDA2IDgxLjM4MDlDNTIuOTAyMyA4MC40NTUxIDUyLjMzOTggNzkuMzg4NyA1MS45NTMxIDc4LjE4MTZDNTEuNTY2NCA3Ni45NzQ2IDUxLjM3MyA3NS42OTczIDUxLjM3MyA3NC4zNDk2VjcyLjU5MThDNTEuMzczIDcwLjY0NjUgNTEuNjE5MSA2OC44MTI1IDUyLjExMTMgNjcuMDg5OEM1Mi42MDM1IDY1LjM1NTUgNTMuMzU5NCA2My44MjYyIDU0LjM3ODkgNjIuNTAyQzU1LjQxMDIgNjEuMTc3NyA1Ni43Mjg1IDYwLjE0MDYgNTguMzM0IDU5LjM5MDZDNTkuOTM5NSA1OC42Mjg5IDYxLjg1NTUgNTguMjQ4IDY0LjA4MiA1OC4yNDhaTTg4Ljg2OTMgNjkuMDU4NlY3My4yNDIyQzg4Ljg2OTMgNzUuMjQ2MSA4OC42NzAxIDc2Ljk1NyA4OC4yNzE2IDc4LjM3NUM4Ny44ODQ5IDc5Ljc4MTIgODcuMzIyNCA4MC45MjM4IDg2LjU4NDEgODEuODAyN0M4NS44NDU5IDgyLjY4MTYgODQuOTYxMSA4My4zMjYyIDgzLjkyOTggODMuNzM2M0M4Mi45MTAzIDg0LjE0NjUgODEuNzY3NyA4NC4zNTE2IDgwLjUwMjEgODQuMzUxNkM3OS40OTQzIDg0LjM1MTYgNzguNTU2OCA4NC4yMjI3IDc3LjY4OTYgODMuOTY0OEM3Ni44MzQxIDgzLjcwNyA3Ni4wNjA3IDgzLjMwMjcgNzUuMzY5MyA4Mi43NTJDNzQuNjc3OSA4Mi4yMDEyIDc0LjA4NjEgODEuNDkyMiA3My41OTM5IDgwLjYyNUM3My4xMTM0IDc5Ljc0NjEgNzIuNzM4NCA3OC42OTczIDcyLjQ2ODkgNzcuNDc4NUM3Mi4yMTExIDc2LjI1OTggNzIuMDgyMiA3NC44NDc3IDcyLjA4MjIgNzMuMjQyMlY2OS4wNTg2QzcyLjA4MjIgNjcuMDQzIDcyLjI4MTQgNjUuMzQzOCA3Mi42Nzk4IDYzLjk2MDlDNzMuMDc4MyA2Mi41NjY0IDczLjY0NjYgNjEuNDM1NSA3NC4zODQ5IDYwLjU2ODRDNzUuMTIzMiA1OS42ODk1IDc2LjAwMjEgNTkuMDUwOCA3Ny4wMjE2IDU4LjY1MjNDNzguMDUyOSA1OC4yNTM5IDc5LjIwMTMgNTguMDU0NyA4MC40NjcgNTguMDU0N0M4MS40ODY1IDU4LjA1NDcgODIuNDI0IDU4LjE4MzYgODMuMjc5NSA1OC40NDE0Qzg0LjE0NjYgNTguNjg3NSA4NC45MjAxIDU5LjA4MDEgODUuNTk5OCA1OS42MTkxQzg2LjI5MTIgNjAuMTU4MiA4Ni44NzcxIDYwLjg2MTMgODcuMzU3NiA2MS43Mjg1Qzg3Ljg0OTggNjIuNTg0IDg4LjIyNDggNjMuNjIxMSA4OC40ODI2IDY0LjgzOThDODguNzQwNCA2Ni4wNDY5IDg4Ljg2OTMgNjcuNDUzMSA4OC44NjkzIDY5LjA1ODZaTTg0LjYzMyA3My44Mzk4VjY4LjQyNThDODQuNjMzIDY3LjQwNjIgODQuNTc0NCA2Ni41MDk4IDg0LjQ1NzIgNjUuNzM2M0M4NC4zNCA2NC45NTEyIDg0LjE2NDIgNjQuMjg5MSA4My45Mjk4IDYzLjc1QzgzLjcwNzIgNjMuMTk5MiA4My40MjU5IDYyLjc1MzkgODMuMDg2MSA2Mi40MTQxQzgyLjc0NjMgNjIuMDYyNSA4Mi4zNTk1IDYxLjgxMDUgODEuOTI1OSA2MS42NTgyQzgxLjQ5MjMgNjEuNDk0MSA4MS4wMDYgNjEuNDEyMSA4MC40NjcgNjEuNDEyMUM3OS44MTA3IDYxLjQxMjEgNzkuMjI0OCA2MS41NDEgNzguNzA5MSA2MS43OTg4Qzc4LjE5MzUgNjIuMDQ0OSA3Ny43NTk5IDYyLjQ0MzQgNzcuNDA4NCA2Mi45OTQxQzc3LjA1NjggNjMuNTQ0OSA3Ni43ODczIDY0LjI3MTUgNzYuNTk5OCA2NS4xNzM4Qzc2LjQyNCA2Ni4wNjQ1IDc2LjMzNjEgNjcuMTQ4NCA3Ni4zMzYxIDY4LjQyNThWNzMuODM5OEM3Ni4zMzYxIDc0Ljg3MTEgNzYuMzk0NyA3NS43NzkzIDc2LjUxMTkgNzYuNTY0NUM3Ni42MjkxIDc3LjM0OTYgNzYuODA0OCA3OC4wMjM0IDc3LjAzOTIgNzguNTg1OUM3Ny4yNzM2IDc5LjEzNjcgNzcuNTU0OCA3OS41OTM4IDc3Ljg4MyA3OS45NTdDNzguMjIyOCA4MC4zMDg2IDc4LjYwOTUgODAuNTY2NCA3OS4wNDMxIDgwLjczMDVDNzkuNDg4NCA4MC44OTQ1IDc5Ljk3NDggODAuOTc2NiA4MC41MDIxIDgwLjk3NjZDODEuMTcwMSA4MC45NzY2IDgxLjc2MTkgODAuODQ3NyA4Mi4yNzc1IDgwLjU4OThDODIuNzkzMSA4MC4zMzIgODMuMjI2NyA3OS45MjE5IDgzLjU3ODMgNzkuMzU5NEM4My45Mjk4IDc4Ljc4NTIgODQuMTkzNSA3OC4wNDEgODQuMzY5MyA3Ny4xMjdDODQuNTQ1MSA3Ni4yMTI5IDg0LjYzMyA3NS4xMTcyIDg0LjYzMyA3My44Mzk4WiIgZmlsbD0iI0Q4MTgzOCIvPgo8cGF0aCBkPSJNOTYuMjIxNyA3OC4wNzMyVjg0SDk0Ljk1MDJWNzYuNjAzNUg5Ni4xNTMzTDk2LjIyMTcgNzguMDczMlpNOTUuOTYxOSA4MC4wMjE1TDk1LjM3NCA4MC4wMDFDOTUuMzc4NiA3OS40OTUxIDk1LjQ0NDcgNzkuMDI4IDk1LjU3MjMgNzguNTk5NkM5NS42OTk5IDc4LjE2NjcgOTUuODg5IDc3Ljc5MDcgOTYuMTM5NiA3Ny40NzE3Qzk2LjM5MDMgNzcuMTUyNyA5Ni43MDI1IDc2LjkwNjYgOTcuMDc2MiA3Ni43MzM0Qzk3LjQ0OTkgNzYuNTU1NyA5Ny44ODI4IDc2LjQ2NjggOTguMzc1IDc2LjQ2NjhDOTguNzIxNCA3Ni40NjY4IDk5LjA0MDQgNzYuNTE2OSA5OS4zMzIgNzYuNjE3MkM5OS42MjM3IDc2LjcxMjkgOTkuODc2NiA3Ni44NjU2IDEwMC4wOTEgNzcuMDc1MkMxMDAuMzA1IDc3LjI4NDggMTAwLjQ3MSA3Ny41NTM3IDEwMC41OSA3Ny44ODE4QzEwMC43MDggNzguMjEgMTAwLjc2OCA3OC42MDY0IDEwMC43NjggNzkuMDcxM1Y4NEg5OS41MDI5Vjc5LjEzMjhDOTkuNTAyOSA3OC43NDU0IDk5LjQzNjggNzguNDM1NSA5OS4zMDQ3IDc4LjIwMzFDOTkuMTc3MSA3Ny45NzA3IDk4Ljk5NDggNzcuODAyMSA5OC43NTc4IDc3LjY5NzNDOTguNTIwOCA3Ny41ODc5IDk4LjI0MjggNzcuNTMzMiA5Ny45MjM4IDc3LjUzMzJDOTcuNTUwMSA3Ny41MzMyIDk3LjIzOCA3Ny41OTkzIDk2Ljk4NzMgNzcuNzMxNEM5Ni43MzY3IDc3Ljg2MzYgOTYuNTM2MSA3OC4wNDU5IDk2LjM4NTcgNzguMjc4M0M5Ni4yMzU0IDc4LjUxMDcgOTYuMTI2IDc4Ljc3NzMgOTYuMDU3NiA3OS4wNzgxQzk1Ljk5MzggNzkuMzc0MyA5NS45NjE5IDc5LjY4ODggOTUuOTYxOSA4MC4wMjE1Wk0xMDAuNzU0IDc5LjMyNDJMOTkuOTA2MiA3OS41ODRDOTkuOTEwOCA3OS4xNzg0IDk5Ljk3NjkgNzguNzg4NyAxMDAuMTA0IDc4LjQxNUMxMDAuMjM3IDc4LjA0MTMgMTAwLjQyNiA3Ny43MDg3IDEwMC42NzIgNzcuNDE3QzEwMC45MjMgNzcuMTI1MyAxMDEuMjMgNzYuODk1MiAxMDEuNTk1IDc2LjcyNjZDMTAxLjk1OSA3Ni41NTM0IDEwMi4zNzYgNzYuNDY2OCAxMDIuODQ2IDc2LjQ2NjhDMTAzLjI0MiA3Ni40NjY4IDEwMy41OTMgNzYuNTE5MiAxMDMuODk4IDc2LjYyNEMxMDQuMjA4IDc2LjcyODggMTA0LjQ2OCA3Ni44OTA2IDEwNC42NzggNzcuMTA5NEMxMDQuODkyIDc3LjMyMzYgMTA1LjA1NCA3Ny41OTkzIDEwNS4xNjMgNzcuOTM2NUMxMDUuMjcyIDc4LjI3MzggMTA1LjMyNyA3OC42NzQ4IDEwNS4zMjcgNzkuMTM5NlY4NEgxMDQuMDU2Vjc5LjEyNkMxMDQuMDU2IDc4LjcxMTMgMTAzLjk5IDc4LjM5IDEwMy44NTcgNzguMTYyMUMxMDMuNzMgNzcuOTI5NyAxMDMuNTQ4IDc3Ljc2NzkgMTAzLjMxMSA3Ny42NzY4QzEwMy4wNzggNzcuNTgxMSAxMDIuOCA3Ny41MzMyIDEwMi40NzcgNzcuNTMzMkMxMDIuMTk5IDc3LjUzMzIgMTAxLjk1MiA3Ny41ODExIDEwMS43MzggNzcuNjc2OEMxMDEuNTI0IDc3Ljc3MjUgMTAxLjM0NCA3Ny45MDQ2IDEwMS4xOTggNzguMDczMkMxMDEuMDUyIDc4LjIzNzMgMTAwLjk0MSA3OC40MjY0IDEwMC44NjMgNzguNjQwNkMxMDAuNzkgNzguODU0OCAxMDAuNzU0IDc5LjA4MjcgMTAwLjc1NCA3OS4zMjQyWk0xMTIuMjMzIDc2LjYwMzVIMTEzLjM4MlY4My44NDI4QzExMy4zODIgODQuNDk0NSAxMTMuMjQ5IDg1LjA1MDUgMTEyLjk4NSA4NS41MTA3QzExMi43MjEgODUuOTcxIDExMi4zNTIgODYuMzE5NyAxMTEuODc4IDg2LjU1NjZDMTExLjQwOCA4Ni43OTgyIDExMC44NjYgODYuOTE4OSAxMTAuMjUxIDg2LjkxODlDMTA5Ljk5NiA4Ni45MTg5IDEwOS42OTUgODYuODc3OSAxMDkuMzQ4IDg2Ljc5NTlDMTA5LjAwNyA4Ni43MTg0IDEwOC42NjkgODYuNTg0IDEwOC4zMzcgODYuMzkyNkMxMDguMDA5IDg2LjIwNTcgMTA3LjczMyA4NS45NTI4IDEwNy41MSA4NS42MzM4TDEwOC4xNzMgODQuODgxOEMxMDguNDgzIDg1LjI1NTUgMTA4LjgwNiA4NS41MTUzIDEwOS4xNDMgODUuNjYxMUMxMDkuNDg1IDg1LjgwNyAxMDkuODIyIDg1Ljg3OTkgMTEwLjE1NSA4NS44Nzk5QzExMC41NTYgODUuODc5OSAxMTAuOTAyIDg1LjgwNDcgMTExLjE5NCA4NS42NTQzQzExMS40ODYgODUuNTAzOSAxMTEuNzExIDg1LjI4MDYgMTExLjg3MSA4NC45ODQ0QzExMi4wMzUgODQuNjkyNyAxMTIuMTE3IDg0LjMzMjcgMTEyLjExNyA4My45MDQzVjc4LjIzMDVMMTEyLjIzMyA3Ni42MDM1Wk0xMDcuMTQgODAuMzgzOFY4MC4yNDAyQzEwNy4xNCA3OS42NzUxIDEwNy4yMDcgNzkuMTYyNCAxMDcuMzM5IDc4LjcwMjFDMTA3LjQ3NSA3OC4yMzczIDEwNy42NjkgNzcuODM4NSAxMDcuOTIgNzcuNTA1OUMxMDguMTc1IDc3LjE3MzIgMTA4LjQ4MyA3Ni45MTggMTA4Ljg0MyA3Ni43NDAyQzEwOS4yMDMgNzYuNTU3OSAxMDkuNjA4IDc2LjQ2NjggMTEwLjA1OSA3Ni40NjY4QzExMC41MjQgNzYuNDY2OCAxMTAuOTMgNzYuNTQ4OCAxMTEuMjc2IDc2LjcxMjlDMTExLjYyNyA3Ni44NzI0IDExMS45MjMgNzcuMTA3MSAxMTIuMTY1IDc3LjQxN0MxMTIuNDExIDc3LjcyMjMgMTEyLjYwNSA3OC4wOTE1IDExMi43NDYgNzguNTI0NEMxMTIuODg3IDc4Ljk1NzQgMTEyLjk4NSA3OS40NDczIDExMy4wNCA3OS45OTQxVjgwLjYyM0MxMTIuOTkgODEuMTY1NCAxMTIuODkyIDgxLjY1MyAxMTIuNzQ2IDgyLjA4NTlDMTEyLjYwNSA4Mi41MTg5IDExMi40MTEgODIuODg4IDExMi4xNjUgODMuMTkzNEMxMTEuOTIzIDgzLjQ5ODcgMTExLjYyNyA4My43MzM0IDExMS4yNzYgODMuODk3NUMxMTAuOTI1IDg0LjA1NyAxMTAuNTE1IDg0LjEzNjcgMTEwLjA0NiA4NC4xMzY3QzEwOS42MDQgODQuMTM2NyAxMDkuMjAzIDg0LjA0MzMgMTA4Ljg0MyA4My44NTY0QzEwOC40ODcgODMuNjY5NiAxMDguMTgyIDgzLjQwNzYgMTA3LjkyNyA4My4wNzAzQzEwNy42NzEgODIuNzMzMSAxMDcuNDc1IDgyLjMzNjYgMTA3LjMzOSA4MS44ODA5QzEwNy4yMDcgODEuNDIwNiAxMDcuMTQgODAuOTIxNSAxMDcuMTQgODAuMzgzOFpNMTA4LjQwNSA4MC4yNDAyVjgwLjM4MzhDMTA4LjQwNSA4MC43NTI5IDEwOC40NDIgODEuMDk5MyAxMDguNTE0IDgxLjQyMjlDMTA4LjU5MiA4MS43NDY0IDEwOC43MDggODIuMDMxMiAxMDguODYzIDgyLjI3NzNDMTA5LjAyMyA4Mi41MjM0IDEwOS4yMjUgODIuNzE3MSAxMDkuNDcxIDgyLjg1ODRDMTA5LjcxOCA4Mi45OTUxIDExMC4wMTIgODMuMDYzNSAxMTAuMzUzIDgzLjA2MzVDMTEwLjc3MyA4My4wNjM1IDExMS4xMTkgODIuOTc0NiAxMTEuMzkyIDgyLjc5NjlDMTExLjY2NiA4Mi42MTkxIDExMS44ODIgODIuMzg0NCAxMTIuMDQyIDgyLjA5MjhDMTEyLjIwNiA4MS44MDExIDExMi4zMzMgODEuNDg0NCAxMTIuNDI1IDgxLjE0MjZWNzkuNDk1MUMxMTIuMzc0IDc5LjI0NDUgMTEyLjI5NyA3OS4wMDI5IDExMi4xOTIgNzguNzcwNUMxMTIuMDkyIDc4LjUzMzUgMTExLjk2IDc4LjMyMzkgMTExLjc5NiA3OC4xNDE2QzExMS42MzYgNzcuOTU0OCAxMTEuNDM4IDc3LjgwNjYgMTExLjIwMSA3Ny42OTczQzExMC45NjQgNzcuNTg3OSAxMTAuNjg2IDc3LjUzMzIgMTEwLjM2NyA3Ny41MzMyQzExMC4wMjEgNzcuNTMzMiAxMDkuNzIyIDc3LjYwNjEgMTA5LjQ3MSA3Ny43NTJDMTA5LjIyNSA3Ny44OTMyIDEwOS4wMjMgNzguMDg5MiAxMDguODYzIDc4LjMzOThDMTA4LjcwOCA3OC41ODU5IDEwOC41OTIgNzguODczIDEwOC41MTQgNzkuMjAxMkMxMDguNDQyIDc5LjUyNDcgMTA4LjQwNSA3OS44NzExIDEwOC40MDUgODAuMjQwMlpNMTE5LjkwNSA3NC4wNDY5TDExNS43NTUgODQuODU0NUgxMTQuNjY5TDExOC44MjUgNzQuMDQ2OUgxMTkuOTA1Wk0xMjIuNzQ0IDc4LjA3MzJWODRIMTIxLjQ3MlY3Ni42MDM1SDEyMi42NzVMMTIyLjc0NCA3OC4wNzMyWk0xMjIuNDg0IDgwLjAyMTVMMTIxLjg5NiA4MC4wMDFDMTIxLjkgNzkuNDk1MSAxMjEuOTY3IDc5LjAyOCAxMjIuMDk0IDc4LjU5OTZDMTIyLjIyMiA3OC4xNjY3IDEyMi40MTEgNzcuNzkwNyAxMjIuNjYyIDc3LjQ3MTdDMTIyLjkxMiA3Ny4xNTI3IDEyMy4yMjQgNzYuOTA2NiAxMjMuNTk4IDc2LjczMzRDMTIzLjk3MiA3Ni41NTU3IDEyNC40MDUgNzYuNDY2OCAxMjQuODk3IDc2LjQ2NjhDMTI1LjI0MyA3Ni40NjY4IDEyNS41NjIgNzYuNTE2OSAxMjUuODU0IDc2LjYxNzJDMTI2LjE0NiA3Ni43MTI5IDEyNi4zOTkgNzYuODY1NiAxMjYuNjEzIDc3LjA3NTJDMTI2LjgyNyA3Ny4yODQ4IDEyNi45OTMgNzcuNTUzNyAxMjcuMTEyIDc3Ljg4MThDMTI3LjIzIDc4LjIxIDEyNy4yODkgNzguNjA2NCAxMjcuMjg5IDc5LjA3MTNWODRIMTI2LjAyNVY3OS4xMzI4QzEyNi4wMjUgNzguNzQ1NCAxMjUuOTU5IDc4LjQzNTUgMTI1LjgyNyA3OC4yMDMxQzEyNS42OTkgNzcuOTcwNyAxMjUuNTE3IDc3LjgwMjEgMTI1LjI4IDc3LjY5NzNDMTI1LjA0MyA3Ny41ODc5IDEyNC43NjUgNzcuNTMzMiAxMjQuNDQ2IDc3LjUzMzJDMTI0LjA3MiA3Ny41MzMyIDEyMy43NiA3Ny41OTkzIDEyMy41MDkgNzcuNzMxNEMxMjMuMjU5IDc3Ljg2MzYgMTIzLjA1OCA3OC4wNDU5IDEyMi45MDggNzguMjc4M0MxMjIuNzU3IDc4LjUxMDcgMTIyLjY0OCA3OC43NzczIDEyMi41NzkgNzkuMDc4MUMxMjIuNTE2IDc5LjM3NDMgMTIyLjQ4NCA3OS42ODg4IDEyMi40ODQgODAuMDIxNVpNMTI3LjI3NiA3OS4zMjQyTDEyNi40MjggNzkuNTg0QzEyNi40MzMgNzkuMTc4NCAxMjYuNDk5IDc4Ljc4ODcgMTI2LjYyNiA3OC40MTVDMTI2Ljc1OSA3OC4wNDEzIDEyNi45NDggNzcuNzA4NyAxMjcuMTk0IDc3LjQxN0MxMjcuNDQ0IDc3LjEyNTMgMTI3Ljc1MiA3Ni44OTUyIDEyOC4xMTcgNzYuNzI2NkMxMjguNDgxIDc2LjU1MzQgMTI4Ljg5OCA3Ni40NjY4IDEyOS4zNjggNzYuNDY2OEMxMjkuNzY0IDc2LjQ2NjggMTMwLjExNSA3Ni41MTkyIDEzMC40MiA3Ni42MjRDMTMwLjczIDc2LjcyODggMTMwLjk5IDc2Ljg5MDYgMTMxLjIgNzcuMTA5NEMxMzEuNDE0IDc3LjMyMzYgMTMxLjU3NiA3Ny41OTkzIDEzMS42ODUgNzcuOTM2NUMxMzEuNzk0IDc4LjI3MzggMTMxLjg0OSA3OC42NzQ4IDEzMS44NDkgNzkuMTM5NlY4NEgxMzAuNTc4Vjc5LjEyNkMxMzAuNTc4IDc4LjcxMTMgMTMwLjUxMSA3OC4zOSAxMzAuMzc5IDc4LjE2MjFDMTMwLjI1MiA3Ny45Mjk3IDEzMC4wNjkgNzcuNzY3OSAxMjkuODMyIDc3LjY3NjhDMTI5LjYgNzcuNTgxMSAxMjkuMzIyIDc3LjUzMzIgMTI4Ljk5OCA3Ny41MzMyQzEyOC43MiA3Ny41MzMyIDEyOC40NzQgNzcuNTgxMSAxMjguMjYgNzcuNjc2OEMxMjguMDQ2IDc3Ljc3MjUgMTI3Ljg2NiA3Ny45MDQ2IDEyNy43MiA3OC4wNzMyQzEyNy41NzQgNzguMjM3MyAxMjcuNDYzIDc4LjQyNjQgMTI3LjM4NSA3OC42NDA2QzEyNy4zMTIgNzguODU0OCAxMjcuMjc2IDc5LjA4MjcgMTI3LjI3NiA3OS4zMjQyWk0xMzQuODE4IDc2LjMwMjdIMTM1LjM5MkMxMzUuNjE1IDc2LjMwMjcgMTM1LjggNzYuMjcwOCAxMzUuOTQ2IDc2LjIwN0MxMzYuMDkxIDc2LjE0MzIgMTM2LjIwMSA3Ni4wNTY2IDEzNi4yNzQgNzUuOTQ3M0MxMzYuMzUxIDc1LjgzMzMgMTM2LjM5IDc1LjcwMzUgMTM2LjM5IDc1LjU1NzZDMTM2LjM5IDc1LjM1NzEgMTM2LjMxNyA3NS4xODg1IDEzNi4xNzEgNzUuMDUxOEMxMzYuMDI1IDc0LjkxNSAxMzUuNzk1IDc0Ljg0NjcgMTM1LjQ4MSA3NC44NDY3QzEzNS4yMjEgNzQuODQ2NyAxMzUuMDA0IDc0LjkwMzYgMTM0LjgzMSA3NS4wMTc2QzEzNC42NjMgNzUuMTMxNSAxMzQuNTc4IDc1LjI4NjUgMTM0LjU3OCA3NS40ODI0SDEzMy41MDVDMTMzLjUwNSA3NS4xNzI1IDEzMy41OTIgNzQuOTA1OSAxMzMuNzY1IDc0LjY4MjZDMTMzLjkzOCA3NC40NTQ4IDEzNC4xNzMgNzQuMjc5MyAxMzQuNDY5IDc0LjE1NjJDMTM0Ljc2NSA3NC4wMzMyIDEzNS4wOTYgNzMuOTcxNyAxMzUuNDYgNzMuOTcxN0MxMzUuODU3IDczLjk3MTcgMTM2LjIwNSA3NC4wMzA5IDEzNi41MDYgNzQuMTQ5NEMxMzYuODA3IDc0LjI2MzMgMTM3LjA0MiA3NC40MzQyIDEzNy4yMSA3NC42NjIxQzEzNy4zNzkgNzQuODkgMTM3LjQ2MyA3NS4xNzAyIDEzNy40NjMgNzUuNTAyOUMxMzcuNDYzIDc1Ljc0NDUgMTM3LjM4OCA3NS45Njc4IDEzNy4yMzcgNzYuMTcyOUMxMzcuMDkyIDc2LjM3MzQgMTM2Ljg3NyA3Ni41MzUyIDEzNi41OTUgNzYuNjU4MkMxMzYuMzE3IDc2Ljc3NjcgMTM1Ljk3NyA3Ni44MzU5IDEzNS41NzYgNzYuODM1OUgxMzQuODE4Vjc2LjMwMjdaTTEzNC44MTggNzcuMDg4OVY3Ni41NTU3SDEzNS41NzZDMTM2LjAzMiA3Ni41NTU3IDEzNi40MDYgNzYuNjEwNCAxMzYuNjk3IDc2LjcxOTdDMTM2Ljk4OSA3Ni44MjkxIDEzNy4yMDYgNzYuOTg2MyAxMzcuMzQ3IDc3LjE5MTRDMTM3LjQ4OCA3Ny4zOTY1IDEzNy41NTkgNzcuNjQ0OSAxMzcuNTU5IDc3LjkzNjVDMTM3LjU1OSA3OC4yNjkyIDEzNy40NjggNzguNTU0IDEzNy4yODUgNzguNzkxQzEzNy4xMDMgNzkuMDI4IDEzNi44NTIgNzkuMjA4IDEzNi41MzMgNzkuMzMxMUMxMzYuMjE5IDc5LjQ1NDEgMTM1Ljg2MyA3OS41MTU2IDEzNS40NjcgNzkuNTE1NkMxMzUuMTI1IDc5LjUxNTYgMTM0Ljc5NyA3OS40NjA5IDEzNC40ODMgNzkuMzUxNkMxMzQuMTczIDc5LjIzNzYgMTMzLjkyIDc5LjA2MjIgMTMzLjcyNCA3OC44MjUyQzEzMy41MjggNzguNTg4MiAxMzMuNDMgNzguMjg1MiAxMzMuNDMgNzcuOTE2SDEzNC41MDNDMTM0LjUwMyA3OC4xMjExIDEzNC41OTQgNzguMjk0MyAxMzQuNzc3IDc4LjQzNTVDMTM0Ljk1OSA3OC41NzIzIDEzNS4yIDc4LjY0MDYgMTM1LjUwMSA3OC42NDA2QzEzNS44MiA3OC42NDA2IDEzNi4wNjIgNzguNTcgMTM2LjIyNiA3OC40Mjg3QzEzNi4zOTQgNzguMjg3NCAxMzYuNDc5IDc4LjEwNTEgMTM2LjQ3OSA3Ny44ODE4QzEzNi40NzkgNzcuNjkwNCAxMzYuNDMzIDc3LjUzNzggMTM2LjM0MiA3Ny40MjM4QzEzNi4yNTUgNzcuMzA1MyAxMzYuMTMgNzcuMjIxIDEzNS45NjYgNzcuMTcwOUMxMzUuODA3IDc3LjExNjIgMTM1LjYxNSA3Ny4wODg5IDEzNS4zOTIgNzcuMDg4OUgxMzQuODE4WiIgZmlsbD0iI0Q4MTgzOCIvPgo8cGF0aCBkPSJNMTQ3LjQ0NCA3MS43Nzc4TDE1NC41NTUgNzEuNzc3OEwxNTQuNTU1IDgwLjY2NjdMMTUxLjAwNCA4My4zMzMzTDE0Ny40NDQgODAuNjY2N0wxNDcuNDQ0IDcxLjc3NzhaIiBmaWxsPSJ1cmwoI3BhaW50MF9saW5lYXJfMTEzMl8yOTQxKSIgZmlsbC1vcGFjaXR5PSIwLjMiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNTAuNjc0IDgzLjg5NkMxNTAuODU0IDg0LjA3NjIgMTUxLjE0NiA4NC4wNzYyIDE1MS4zMjYgODMuODk2TDE1NC41NTUgODAuNjY2N0wxNTMuOTAzIDgwLjAxNDJMMTUyLjQ4NCA4MS40MzMxQzE1Mi4xMDcgODEuODEwNiAxNTEuNDYxIDgxLjU0MzIgMTUxLjQ2MSA4MS4wMDkzTDE1MS40NjEgNzcuMTExMUwxNTAuNTM5IDc3LjExMTFMMTUwLjUzOSA4MS4wMDkzQzE1MC41MzkgODEuNTQzMiAxNDkuODkzIDgxLjgxMDYgMTQ5LjUxNiA4MS40MzMxTDE0OC4wOTcgODAuMDE0MkwxNDcuNDQ0IDgwLjY2NjdMMTUwLjY3NCA4My44OTZaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0xNjIuNTY4IDc5Ljc5M1Y4MC45MThIMTU5LjQ2OVY3OS43OTNIMTYyLjU2OFpNMTY3LjgyOCA3NS40Mzk1Vjg0SDE2Ni40MTZWNzcuMTE1MkwxNjQuMzI0IDc3LjgyNDJWNzYuNjU4MkwxNjcuNjU4IDc1LjQzOTVIMTY3LjgyOFpNMTc1LjMwOSA3NS40MTZIMTc1LjQ4NFY3Ni41NzAzSDE3NS4zODVDMTc0Ljg4MSA3Ni41NzAzIDE3NC40NDUgNzYuNjQ4NCAxNzQuMDc4IDc2LjgwNDdDMTczLjcxNSA3Ni45NjA5IDE3My40MTYgNzcuMTc1OCAxNzMuMTgyIDc3LjQ0OTJDMTcyLjk0NyA3Ny43MjI3IDE3Mi43NzEgNzguMDQzIDE3Mi42NTQgNzguNDEwMkMxNzIuNTQxIDc4Ljc3MzQgMTcyLjQ4NCA3OS4xNjAyIDE3Mi40ODQgNzkuNTcwM1Y4MC45MTIxQzE3Mi40ODQgODEuMjUyIDE3Mi41MjEgODEuNTUyNyAxNzIuNTk2IDgxLjgxNDVDMTcyLjY3IDgyLjA3MjMgMTcyLjc3MyA4Mi4yODkxIDE3Mi45MDYgODIuNDY0OEMxNzMuMDQzIDgyLjYzNjcgMTczLjE5OSA4Mi43Njc2IDE3My4zNzUgODIuODU3NEMxNzMuNTUxIDgyLjk0NzMgMTczLjc0IDgyLjk5MjIgMTczLjk0MyA4Mi45OTIyQzE3NC4xNTQgODIuOTkyMiAxNzQuMzQ2IDgyLjk0OTIgMTc0LjUxOCA4Mi44NjMzQzE3NC42ODkgODIuNzczNCAxNzQuODM2IDgyLjY1MDQgMTc0Ljk1NyA4Mi40OTQxQzE3NS4wNzggODIuMzM3OSAxNzUuMTcgODIuMTUyMyAxNzUuMjMyIDgxLjkzNzVDMTc1LjI5NSA4MS43MjI3IDE3NS4zMjYgODEuNDg4MyAxNzUuMzI2IDgxLjIzNDRDMTc1LjMyNiA4MC45OTIyIDE3NS4yOTUgODAuNzY1NiAxNzUuMjMyIDgwLjU1NDdDMTc1LjE3NCA4MC4zMzk4IDE3NS4wODYgODAuMTUyMyAxNzQuOTY5IDc5Ljk5MjJDMTc0Ljg1MiA3OS44MjgxIDE3NC43MDUgNzkuNzAxMiAxNzQuNTI5IDc5LjYxMTNDMTc0LjM1NyA3OS41MTc2IDE3NC4xNTggNzkuNDcwNyAxNzMuOTMyIDc5LjQ3MDdDMTczLjY1IDc5LjQ3MDcgMTczLjM5NSA3OS41MzcxIDE3My4xNjQgNzkuNjY5OUMxNzIuOTM4IDc5LjgwMjcgMTcyLjc1NCA3OS45NzY2IDE3Mi42MTMgODAuMTkxNEMxNzIuNDc3IDgwLjQwMjMgMTcyLjQwMiA4MC42MjcgMTcyLjM5MSA4MC44NjUyTDE3MS44NTIgODAuNjg5NUMxNzEuODgzIDgwLjMyNjIgMTcxLjk2MyA4MCAxNzIuMDkyIDc5LjcxMDlDMTcyLjIyNSA3OS40MjE5IDE3Mi4zOTggNzkuMTc1OCAxNzIuNjEzIDc4Ljk3MjdDMTcyLjgyOCA3OC43Njk1IDE3My4wNzYgNzguNjE1MiAxNzMuMzU3IDc4LjUwOThDMTczLjY0MyA3OC40MDA0IDE3My45NTUgNzguMzQ1NyAxNzQuMjk1IDc4LjM0NTdDMTc0LjcwOSA3OC4zNDU3IDE3NS4wNjggNzguNDIzOCAxNzUuMzczIDc4LjU4MDFDMTc1LjY3OCA3OC43MzYzIDE3NS45MyA3OC45NDkyIDE3Ni4xMjkgNzkuMjE4OEMxNzYuMzMyIDc5LjQ4NDQgMTc2LjQ4MiA3OS43ODkxIDE3Ni41OCA4MC4xMzI4QzE3Ni42ODIgODAuNDcyNyAxNzYuNzMyIDgwLjgyODEgMTc2LjczMiA4MS4xOTkyQzE3Ni43MzIgODEuNjA5NCAxNzYuNjcgODEuOTkyMiAxNzYuNTQ1IDgyLjM0NzdDMTc2LjQyIDgyLjY5OTIgMTc2LjIzNiA4My4wMDc4IDE3NS45OTQgODMuMjczNEMxNzUuNzU2IDgzLjUzOTEgMTc1LjQ2NSA4My43NDYxIDE3NS4xMjEgODMuODk0NUMxNzQuNzgxIDg0LjA0MyAxNzQuMzk1IDg0LjExNzIgMTczLjk2MSA4NC4xMTcyQzE3My41MDQgODQuMTE3MiAxNzMuMDk2IDg0LjAyOTMgMTcyLjczNiA4My44NTM1QzE3Mi4zODEgODMuNjc3NyAxNzIuMDc4IDgzLjQzNTUgMTcxLjgyOCA4My4xMjdDMTcxLjU4MiA4Mi44MTg0IDE3MS4zOTUgODIuNDYyOSAxNzEuMjY2IDgyLjA2MDVDMTcxLjEzNyA4MS42NTgyIDE3MS4wNzIgODEuMjMyNCAxNzEuMDcyIDgwLjc4MzJWODAuMTk3M0MxNzEuMDcyIDc5LjU0ODggMTcxLjE1NCA3OC45Mzc1IDE3MS4zMTggNzguMzYzM0MxNzEuNDgyIDc3Ljc4NTIgMTcxLjczNCA3Ny4yNzU0IDE3Mi4wNzQgNzYuODM0QzE3Mi40MTggNzYuMzkyNiAxNzIuODU3IDc2LjA0NjkgMTczLjM5MyA3NS43OTY5QzE3My45MjggNzUuNTQzIDE3NC41NjYgNzUuNDE2IDE3NS4zMDkgNzUuNDE2WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTc4LjU4NiA3Ny41NjA1Vjc3LjEwOTRDMTc4LjU4NiA3Ni43ODUyIDE3OC42NTYgNzYuNDkwMiAxNzguNzk3IDc2LjIyNDZDMTc4LjkzOCA3NS45NTkgMTc5LjE0MyA3NS43NDYxIDE3OS40MTIgNzUuNTg1OUMxNzkuNjgyIDc1LjQyNTggMTgwLjAwNiA3NS4zNDU3IDE4MC4zODUgNzUuMzQ1N0MxODAuNzc1IDc1LjM0NTcgMTgxLjEwNCA3NS40MjU4IDE4MS4zNjkgNzUuNTg1OUMxODEuNjM5IDc1Ljc0NjEgMTgxLjg0NCA3NS45NTkgMTgxLjk4NCA3Ni4yMjQ2QzE4Mi4xMjUgNzYuNDkwMiAxODIuMTk1IDc2Ljc4NTIgMTgyLjE5NSA3Ny4xMDk0Vjc3LjU2MDVDMTgyLjE5NSA3Ny44NzcgMTgyLjEyNSA3OC4xNjggMTgxLjk4NCA3OC40MzM2QzE4MS44NDggNzguNjk5MiAxODEuNjQ1IDc4LjkxMjEgMTgxLjM3NSA3OS4wNzIzQzE4MS4xMDkgNzkuMjMyNCAxODAuNzgzIDc5LjMxMjUgMTgwLjM5NiA3OS4zMTI1QzE4MC4wMTQgNzkuMzEyNSAxNzkuNjg2IDc5LjIzMjQgMTc5LjQxMiA3OS4wNzIzQzE3OS4xNDMgNzguOTEyMSAxNzguOTM4IDc4LjY5OTIgMTc4Ljc5NyA3OC40MzM2QzE3OC42NTYgNzguMTY4IDE3OC41ODYgNzcuODc3IDE3OC41ODYgNzcuNTYwNVpNMTc5LjU2NCA3Ny4xMDk0Vjc3LjU2MDVDMTc5LjU2NCA3Ny43MTY4IDE3OS41OTQgNzcuODY1MiAxNzkuNjUyIDc4LjAwNTlDMTc5LjcxNSA3OC4xNDY1IDE3OS44MDkgNzguMjU5OCAxNzkuOTM0IDc4LjM0NTdDMTgwLjA1OSA3OC40MzE2IDE4MC4yMTMgNzguNDc0NiAxODAuMzk2IDc4LjQ3NDZDMTgwLjU4NCA3OC40NzQ2IDE4MC43MzYgNzguNDMxNiAxODAuODU0IDc4LjM0NTdDMTgwLjk3NSA3OC4yNTk4IDE4MS4wNjQgNzguMTQ2NSAxODEuMTIzIDc4LjAwNTlDMTgxLjE4MiA3Ny44NjUyIDE4MS4yMTEgNzcuNzE2OCAxODEuMjExIDc3LjU2MDVWNzcuMTA5NEMxODEuMjExIDc2Ljk0OTIgMTgxLjE4IDc2Ljc5ODggMTgxLjExNyA3Ni42NTgyQzE4MS4wNTkgNzYuNTEzNyAxODAuOTY5IDc2LjM5ODQgMTgwLjg0OCA3Ni4zMTI1QzE4MC43MjcgNzYuMjI2NiAxODAuNTcyIDc2LjE4MzYgMTgwLjM4NSA3Ni4xODM2QzE4MC4yMDUgNzYuMTgzNiAxODAuMDUzIDc2LjIyNjYgMTc5LjkyOCA3Ni4zMTI1QzE3OS44MDcgNzYuMzk4NCAxNzkuNzE1IDc2LjUxMzcgMTc5LjY1MiA3Ni42NTgyQzE3OS41OTQgNzYuNzk4OCAxNzkuNTY0IDc2Ljk0OTIgMTc5LjU2NCA3Ny4xMDk0Wk0xODIuNjk5IDgyLjM2NTJWODEuOTA4MkMxODIuNjk5IDgxLjU4NzkgMTgyLjc3IDgxLjI5NDkgMTgyLjkxIDgxLjAyOTNDMTgzLjA1NSA4MC43NjM3IDE4My4yNjIgODAuNTUwOCAxODMuNTMxIDgwLjM5MDZDMTgzLjgwMSA4MC4yMzA1IDE4NC4xMjUgODAuMTUwNCAxODQuNTA0IDgwLjE1MDRDMTg0Ljg5NSA4MC4xNTA0IDE4NS4yMjMgODAuMjMwNSAxODUuNDg4IDgwLjM5MDZDMTg1Ljc1OCA4MC41NTA4IDE4NS45NjEgODAuNzYzNyAxODYuMDk4IDgxLjAyOTNDMTg2LjIzOCA4MS4yOTQ5IDE4Ni4zMDkgODEuNTg3OSAxODYuMzA5IDgxLjkwODJWODIuMzY1MkMxODYuMzA5IDgyLjY4NTUgMTg2LjIzOCA4Mi45Nzg1IDE4Ni4wOTggODMuMjQ0MUMxODUuOTYxIDgzLjUwOTggMTg1Ljc2IDgzLjcyMjcgMTg1LjQ5NCA4My44ODI4QzE4NS4yMjkgODQuMDQzIDE4NC45MDQgODQuMTIzIDE4NC41MjEgODQuMTIzQzE4NC4xMzUgODQuMTIzIDE4My44MDUgODQuMDQzIDE4My41MzEgODMuODgyOEMxODMuMjYyIDgzLjcyMjcgMTgzLjA1NSA4My41MDk4IDE4Mi45MSA4My4yNDQxQzE4Mi43NyA4Mi45Nzg1IDE4Mi42OTkgODIuNjg1NSAxODIuNjk5IDgyLjM2NTJaTTE4My42ODQgODEuOTA4MlY4Mi4zNjUyQzE4My42ODQgODIuNTIxNSAxODMuNzE1IDgyLjY2OTkgMTgzLjc3NyA4Mi44MTA1QzE4My44NDQgODIuOTUxMiAxODMuOTM5IDgzLjA2NjQgMTg0LjA2NCA4My4xNTYyQzE4NC4xODkgODMuMjQyMiAxODQuMzQgODMuMjg1MiAxODQuNTE2IDgzLjI4NTJDMTg0LjcxNSA4My4yODUyIDE4NC44NzUgODMuMjQyMiAxODQuOTk2IDgzLjE1NjJDMTg1LjExNyA4My4wNjY0IDE4NS4yMDMgODIuOTUzMSAxODUuMjU0IDgyLjgxNjRDMTg1LjMwOSA4Mi42NzU4IDE4NS4zMzYgODIuNTI1NCAxODUuMzM2IDgyLjM2NTJWODEuOTA4MkMxODUuMzM2IDgxLjc0OCAxODUuMzA1IDgxLjU5NzcgMTg1LjI0MiA4MS40NTdDMTg1LjE4NCA4MS4zMTY0IDE4NS4wOTIgODEuMjAzMSAxODQuOTY3IDgxLjExNzJDMTg0Ljg0NiA4MS4wMzEyIDE4NC42OTEgODAuOTg4MyAxODQuNTA0IDgwLjk4ODNDMTg0LjMyIDgwLjk4ODMgMTg0LjE2OCA4MS4wMzEyIDE4NC4wNDcgODEuMTE3MkMxODMuOTI2IDgxLjIwMzEgMTgzLjgzNCA4MS4zMTY0IDE4My43NzEgODEuNDU3QzE4My43MTMgODEuNTk3NyAxODMuNjg0IDgxLjc0OCAxODMuNjg0IDgxLjkwODJaTTE4NC44NTUgNzYuNjg3NUwxODAuNjg5IDgzLjM1NTVMMTc5Ljk2OSA4Mi45Mzk1TDE4NC4xMzUgNzYuMjcxNUwxODQuODU1IDc2LjY4NzVaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8xMTMyXzI5NDEpIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xODguMDA0IDEyNC45OTlMMTIgMTI1LjAwMUwxMiAxMjQuMDAxTDE4OC4wMDQgMTIzLjk5OUwxODguMDA0IDEyNC45OTlaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjEyIi8+CjxwYXRoIGQ9Ik0xMiAxMzMuNzc1QzEzLjAxNjYgMTMzLjc3NSAxOC41MDk4IDEzMy43NzUgMjEuMjYzMiAxMzMuNzc1TDM0LjY0MzMgMTQzTDQ0LjkzNTcgMTEyLjI1TDY2LjU0OTcgMTM0LjhMOTAuMjIyMiAxMTIuMjVMOTcuNDI2OSAxMTMuMjc1TDExMS44MzYgMTQzSDEyNi4yNDZMMTM4LjU5NiAxMTYuMzVIMTQ3Ljg2TDE1Ny4xMjMgMTAyTDE2OS40NzQgMTA2LjFMMTg4IDExNi4zNSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuODciIHN0cm9rZS13aWR0aD0iMS41Ii8+CjwvZz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl8xMTMyXzI5NDEiIHgxPSIxNTAuNjgzIiB5MT0iODQuMDI5OCIgeDI9IjE1MC42ODMiIHkyPSI3MS43Nzc4IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CiAgPHN0b3Agb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojZmZmO3N0b3Atb3BhY2l0eToxIiAvPgogIDxzdG9wIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6I2ZmZjtzdG9wLW9wYWNpdHk6MCIgLz4KPC9saW5lYXJHcmFkaWVudD4KPGNsaXBQYXRoIGlkPSJjbGlwMF8xMTMyXzI5NDEiPgo8cmVjdCB3aWR0aD0iMTc2IiBoZWlnaHQ9IjYwIiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTIgOTApIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==", + "description": "A colorless and odorless gas resulting from incomplete combustion, particularly in motor vehicles. Results displayed by combining the latest and aggregated values and optional simplified chart. ", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'carbon', label: 'Carbon monoxide', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'mg/m³', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'carbon', 'mg/m³', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Carbon monoxide\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"mg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#3FA71A\"},{\"from\":5,\"to\":10,\"color\":\"#80C32C\"},{\"from\":10,\"to\":25,\"color\":\"#FFA600\"},{\"from\":25,\"to\":50,\"color\":\"#F36900\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"mg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nif (value < -10) {\\n\\tvalue = -10;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"mg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Carbon monoxide\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:molecule-co\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "carbon monoxide", + "CO" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/carbon_monoxide__co__chart_card_with_background.json b/application/src/main/data/json/system/widget_types/carbon_monoxide__co__chart_card_with_background.json new file mode 100644 index 0000000000..0e44a82445 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/carbon_monoxide__co__chart_card_with_background.json @@ -0,0 +1,39 @@ +{ + "fqn": "carbon_monoxide_co_chart_card_with_background", + "name": "Carbon monoxide (CO) chart card with background", + "deprecated": false, + "image": "tb-image:Q08tdmFsdWUtY2hhcnQtY2FyZC13aXRoLWJhY2tncm91bmQuc3Zn:Q08tdmFsdWUtY2hhcnQtY2FyZC13aXRoLWJhY2tncm91bmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+CiAgPHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0IiAvPgogIDxnIGZpbHRlcj0idXJsKCNiKSI+CiAgICA8cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuNzYiIHJ4PSI0IiB0cmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSA2IDE1NCkiIC8+CiAgPC9nPgogIDxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjg3IgogICAgZD0iTTE3LjMzMyAxNi42NjdjLS43MzMgMC0xLjMzMy42LTEuMzMzIDEuMzMzdjRhMS4zMzQgMS4zMzQgMCAwIDAgMS4zMzMgMS4zMzNoMlYyMmgtMnYtNGgydi0xLjMzM2gtMlptNCAwYy0uNzMzIDAtMS4zMzMuNi0xLjMzMyAxLjMzM3Y0YTEuMzM0IDEuMzM0IDAgMCAwIDEuMzMzIDEuMzMzaDEuMzM0Yy43NCAwIDEuMzMzLS41OTMgMS4zMzMtMS4zMzN2LTRhMS4zMzMgMS4zMzMgMCAwIDAtMS4zMzMtMS4zMzNoLTEuMzM0Wm0wIDEuMzMzaDEuMzM0djRoLTEuMzM0di00Wk0zOC44OSAyMS43NmgxLjcxYTMuOTQ0IDMuOTQ0IDAgMCAxLS41NDcgMS43NDNjLS4zMS41MDYtLjc0NS45MDUtMS4zMDYgMS4xOTYtLjU2LjI5Mi0xLjI0Mi40MzgtMi4wNDQuNDM4LS42MTUgMC0xLjE2OS0uMTEtMS42NjEtLjMyOGEzLjY0MyAzLjY0MyAwIDAgMS0xLjI2NS0uOTQ0Yy0uMzUtLjQxLS42Mi0uOTA0LS44MDYtMS40ODNhNi40NDUgNi40NDUgMCAwIDEtLjI3NC0xLjk0MnYtLjgyN2MwLS43MTUuMDk0LTEuMzYyLjI4LTEuOTQxYTQuMjkgNC4yOSAwIDAgMSAuODItMS40ODMgMy41NDUgMy41NDUgMCAwIDEgMS4yOC0uOTUgNC4xNzkgNC4xNzkgMCAwIDEgMS42ODgtLjMyOWMuNzkzIDAgMS40NjIuMTQ2IDIuMDEuNDM4LjU0Ni4yOTEuOTcuNjk1IDEuMjcgMS4yMS4zMDYuNTE1LjQ5MyAxLjEwNS41NjEgMS43N2gtMS43MDlhMy4xNjMgMy4xNjMgMCAwIDAtLjMtMS4xIDEuNTUgMS41NSAwIDAgMC0uNjctLjY5OGMtLjI5Ny0uMTY0LS42ODQtLjI0Ni0xLjE2Mi0uMjQ2LS4zOTIgMC0uNzM0LjA3My0xLjAyNi4yMTktLjI5MS4xNDYtLjUzNS4zNi0uNzMxLjY0MmEzLjA5NSAzLjA5NSAwIDAgMC0uNDQ1IDEuMDQ2Yy0uMDk1LjQxLS4xNDMuODgtLjE0MyAxLjQwOXYuODRjMCAuNTAyLjA0My45NTcuMTMgMS4zNjguMDkxLjQwNS4yMjguNzU0LjQxIDEuMDQ2LjE4Ny4yOTEuNDI0LjUxNy43MS42NzYuMjg4LjE2LjYzMi4yNCAxLjAzMy4yNC40ODggMCAuODgyLS4wNzggMS4xODMtLjIzM2ExLjU1IDEuNTUgMCAwIDAgLjY5LS42NzdjLjE2LS4zLjI2NC0uNjY3LjMxNS0xLjFabTcuNTI0IDEuNzU3di0zLjUyOGMwLS4yNjQtLjA0OC0uNDkyLS4xNDQtLjY4M2ExLjAwNSAxLjAwNSAwIDAgMC0uNDM3LS40NDVjLS4xOTEtLjEwNS0uNDMzLS4xNTctLjcyNS0uMTU3LS4yNjggMC0uNS4wNDYtLjY5Ny4xMzdhMS4xMjggMS4xMjggMCAwIDAtLjQ1OC4zNjkuODkuODkgMCAwIDAtLjE2NC41MjZoLTEuNjRjMC0uMjkxLjA3LS41NzQuMjExLS44NDcuMTQyLS4yNzQuMzQ3LS41MTguNjE2LS43MzIuMjY5LS4yMTQuNTktLjM4My45NjQtLjUwNmE0LjAwNyA0LjAwNyAwIDAgMSAxLjI1Ny0uMTg0Yy41NTYgMCAxLjA0OC4wOTMgMS40NzcuMjguNDMzLjE4Ny43NzIuNDcgMS4wMTguODQ4LjI1MS4zNzMuMzc2Ljg0My4zNzYgMS40MDh2My4yODhjMCAuMzM3LjAyMy42NC4wNjkuOTEuMDUuMjY0LjEyLjQ5NC4yMTIuNjlWMjVINDYuNjZhMi45MTcgMi45MTcgMCAwIDEtLjE4NC0uNjc3IDUuNTI4IDUuNTI4IDAgMCAxLS4wNjItLjgwNlptLjI0LTMuMDE1LjAxMyAxLjAxOGgtMS4xODNjLS4zMDUgMC0uNTc0LjAzLS44MDYuMDlhMS42NTYgMS42NTYgMCAwIDAtLjU4MS4yNDVjLS4xNTUuMTEtLjI3Mi4yNDItLjM0OS4zOTctLjA3Ny4xNTUtLjExNi4zMy0uMTE2LjUyNnMuMDQ1LjM3Ni4xMzcuNTRjLjA5LjE2LjIyMy4yODUuMzk2LjM3Ni4xNzguMDkxLjM5Mi4xMzcuNjQzLjEzNy4zMzcgMCAuNjMtLjA2OC44ODItLjIwNS4yNTUtLjE0MS40NTUtLjMxMi42MDEtLjUxMy4xNDYtLjIwNS4yMjMtLjM5OC4yMzItLjU4bC41MzQuNzNhMi41MDIgMi41MDIgMCAwIDEtLjI4LjYwMmMtLjEzMy4yMTQtLjMwNi40Mi0uNTIuNjE2YTIuNTg0IDIuNTg0IDAgMCAxLTEuNzcuNjU2Yy0uNDg0IDAtLjkxNC0uMDk2LTEuMjkzLS4yODdhMi4yOTggMi4yOTggMCAwIDEtLjg4OC0uNzg3IDIuMDM5IDIuMDM5IDAgMCAxLS4zMjItMS4xMjdjMC0uMzkyLjA3My0uNzM5LjIyLTEuMDQuMTUtLjMwNS4zNjgtLjU2LjY1NS0uNzY1LjI5Mi0uMjA1LjY0OC0uMzYgMS4wNjctLjQ2NS40Mi0uMTEuODk4LS4xNjQgMS40MzUtLjE2NGgxLjI5MlptNS4wNDEtMS40OVYyNWgtMS42NDd2LTcuMzk2aDEuNTcybC4wNzUgMS40MDhabTIuMjYzLTEuNDU2LS4wMTQgMS41M2E0LjI5MSA0LjI5MSAwIDAgMC0uNjctLjA1NWMtLjI4Mi4wMDEtLjUzLjA0Mi0uNzQ1LjEyNGExLjQwMyAxLjQwMyAwIDAgMC0uNTQuMzQyYy0uMTQxLjE0Ni0uMjUuMzI0LS4zMjguNTMzLS4wNzcuMjEtLjEyMy40NDUtLjEzNy43MDRsLS4zNzYuMDI4YzAtLjQ2NS4wNDYtLjg5Ni4xMzctMS4yOTIuMDkxLS4zOTcuMjI4LS43NDUuNDEtMS4wNDYuMTg3LS4zMDEuNDItLjUzNi42OTgtLjcwNGExLjg3IDEuODcgMCAwIDEgLjk3Ny0uMjUzYy4xIDAgLjIwOC4wMDkuMzIxLjAyNy4xMTkuMDE4LjIwOC4wMzkuMjY3LjA2MlptMS4yOS0zLjA1NmgxLjY0N3Y4LjkyTDU2LjczNiAyNWgtMS40OVYxNC41Wm02LjQ3MyA2LjczM3YuMTQ0YzAgLjU0Ny0uMDYyIDEuMDUtLjE4NSAxLjUxYTMuNDggMy40OCAwIDAgMS0uNTQ3IDEuMTljLS4yNDEuMzM3LS41NDIuNi0uOTAyLjc4Ni0uMzU2LjE4My0uNzY4LjI3NC0xLjIzNy4yNzQtLjQ2IDAtLjg2Mi0uMDg3LTEuMjAzLS4yNmEyLjM5MiAyLjM5MiAwIDAgMS0uODYyLS43MzggNC4wOTUgNC4wOTUgMCAwIDEtLjU1NC0xLjE0MiA3LjU0NCA3LjU0NCAwIDAgMS0uMy0xLjQ2M3YtLjQ1OGMuMDU5LS41MzguMTYtMS4wMjUuMy0xLjQ2My4xNDItLjQ0Mi4zMjYtLjgyMi41NTQtMS4xNDEuMjMzLS4zMjQuNTE3LS41NzIuODU1LS43NDUuMzQxLS4xNzQuNzQtLjI2IDEuMTk2LS4yNi40NzQgMCAuODkuMDkgMS4yNS4yNzMuMzY2LjE4My42NjkuNDQyLjkxLjc4LjI0Mi4zMzIuNDIyLjcyOS41NCAxLjE4OS4xMjMuNDYuMTg1Ljk2OC4xODUgMS41MjRabS0xLjY0OC4xNDR2LS4xNDRhNS4wOCA1LjA4IDAgMCAwLS4wODItLjkzNiAyLjQ2IDIuNDYgMCAwIDAtLjI3My0uNzggMS4zNyAxLjM3IDAgMCAwLS41MDYtLjUyNmMtLjIxLS4xMzItLjQ3Mi0uMTk4LS43ODYtLjE5OC0uMjkyIDAtLjU0My4wNS0uNzUyLjE1LS4yMS4xLS4zODUuMjM3LS41MjYuNDFhMi4wMyAyLjAzIDAgMCAwLS4zMzYuNjAyYy0uMDc3LjIyOC0uMTMuNDc0LS4xNTcuNzM4djEuMjM4Yy4wNDEuMzQxLjEyOC42NTYuMjYuOTQzLjEzNy4yODMuMzI4LjUxLjU3NC42ODQuMjQ2LjE2OC41NjMuMjUzLjk1LjI1My4zMDYgMCAuNTYzLS4wNjIuNzczLS4xODVhMS4zNSAxLjM1IDAgMCAwIC40OTktLjUxMyAyLjQ2IDIuNDYgMCAwIDAgLjI3My0uNzc5Yy4wNi0uMjk2LjA5LS42MTUuMDktLjk1N1ptMi45NzguMDA3di0uMTU3YzAtLjUzNC4wNzctMS4wMjguMjMyLTEuNDg0LjE1NS0uNDYuMzc4LS44NTkuNjctMS4xOTZhMy4wMjQgMy4wMjQgMCAwIDEgMS4wOC0uNzkzIDMuNTE2IDMuNTE2IDAgMCAxIDEuNDUtLjI4N2MuNTQyIDAgMS4wMjUuMDk2IDEuNDQ5LjI4Ny40MjguMTg3Ljc5LjQ1MSAxLjA4Ny43OTMuMjk2LjMzNy41MjEuNzM2LjY3NiAxLjE5Ni4xNTUuNDU2LjIzMy45NS4yMzMgMS40ODR2LjE1N2MwIC41MzMtLjA3OCAxLjAyNy0uMjMzIDEuNDgzYTMuNTUgMy41NSAwIDAgMS0uNjc2IDEuMTk3IDMuMTM3IDMuMTM3IDAgMCAxLTEuMDguNzkyIDMuNTQgMy41NCAwIDAgMS0xLjQ0My4yOCAzLjYwNyAzLjYwNyAwIDAgMS0xLjQ1Ni0uMjggMy4xMzYgMy4xMzYgMCAwIDEtMS4wOC0uNzkzIDMuNTUgMy41NSAwIDAgMS0uNjc3LTEuMTk2IDQuNTgyIDQuNTgyIDAgMCAxLS4yMzItMS40ODNabTEuNjQ3LS4xNTd2LjE1N2MwIC4zMzIuMDM0LjY0Ny4xMDMuOTQzLjA2OC4yOTYuMTc1LjU1Ni4zMjEuNzguMTQ2LjIyMy4zMzMuMzk4LjU2LjUyNi4yMjguMTI3LjUuMTkxLjgxNC4xOTEuMzA1IDAgLjU3LS4wNjQuNzkzLS4xOTEuMjI4LS4xMjguNDE1LS4zMDMuNTYtLjUyN2EyLjQgMi40IDAgMCAwIC4zMjItLjc3OWMuMDczLS4yOTYuMTEtLjYxLjExLS45NDN2LS4xNTdjMC0uMzI5LS4wMzctLjYzOS0uMTEtLjkzYTIuMzkgMi4zOSAwIDAgMC0uMzI4LS43ODYgMS41NyAxLjU3IDAgMCAwLS41Ni0uNTM0IDEuNTQzIDEuNTQzIDAgMCAwLS44LS4xOThjLS4zMSAwLS41OC4wNjYtLjgwNy4xOThhMS41ODcgMS41ODcgMCAwIDAtLjU1NC41MzRjLS4xNDYuMjI4LS4yNTMuNDktLjMyMS43ODZhNC4wNjIgNC4wNjIgMCAwIDAtLjEwMy45M1ptOC40OTQtMi4wNDRWMjVoLTEuNjQ3di03LjM5NmgxLjU1MmwuMDk1IDEuNTc5Wm0tLjI5NCAxLjg0NS0uNTMzLS4wMDZhNC44MTIgNC44MTIgMCAwIDEgLjIxOS0xLjQ0M2MuMTQ2LS40MzcuMzQ2LS44MTMuNjAyLTEuMTI4LjI2LS4zMTQuNTctLjU1Ni45My0uNzI0YTIuNzQgMi43NCAwIDAgMSAxLjIwMi0uMjZjLjM1NiAwIC42NzcuMDUuOTY0LjE1LjI5Mi4wOTYuNTQuMjUzLjc0NS40NzIuMjEuMjE5LjM3LjUwMy40NzkuODU0LjExLjM0Ny4xNjQuNzczLjE2NCAxLjI3OVYyNWgtMS42NTR2LTQuNzg1YzAtLjM1Ni0uMDUzLS42MzYtLjE1OC0uODQxYS44OC44OCAwIDAgMC0uNDQ0LS40NDRjLS4xOTEtLjA5MS0uNDMtLjEzNy0uNzE4LS4xMzctLjI4MiAwLS41MzUuMDYtLjc1OC4xNzgtLjIyNC4xMTgtLjQxMy4yOC0uNTY4LjQ4NS0uMTUuMjA1LS4yNjcuNDQyLS4zNDkuNzExYTIuOTM4IDIuOTM4IDAgMCAwLS4xMjMuODYxWm0xMi4wODctMS45MlYyNWgtMS42NDd2LTcuMzk2aDEuNTUybC4wOTUgMS41MDNabS0uMjY2IDEuOTItLjU2LS4wMDZjMC0uNTEuMDYzLS45ODMuMTktMS40MTYuMTI4LS40MzMuMzE1LS44MDkuNTYxLTEuMTI4LjI0Ni0uMzIzLjU1Mi0uNTcxLjkxNi0uNzQ1LjM3LS4xNzcuNzk1LS4yNjYgMS4yNzktLjI2Ni4zMzcgMCAuNjQ0LjA1LjkyMi4xNS4yODMuMDk2LjUyNy4yNDkuNzMyLjQ1OC4yMS4yMS4zNjkuNDc5LjQ3OC44MDcuMTE0LjMyOC4xNzEuNzI0LjE3MSAxLjE5VjI1aC0xLjY0N3YtNC43ODVjMC0uMzYtLjA1NS0uNjQzLS4xNjQtLjg0OGEuOTEuOTEgMCAwIDAtLjQ1OC0uNDM3IDEuNjU4IDEuNjU4IDAgMCAwLS43MDQtLjEzN2MtLjMxIDAtLjU3NS4wNi0uNzkzLjE3OC0uMjE1LjExOC0uMzkuMjgtLjUyNy40ODVhMi4xNzMgMi4xNzMgMCAwIDAtLjMuNzExIDMuNzIzIDMuNzIzIDAgMCAwLS4wOTYuODYxWm00LjU4Ny0uNDM3LS43NzMuMTdjMC0uNDQ2LjA2Mi0uODY3LjE4NS0xLjI2NC4xMjctLjQwMS4zMTItLjc1Mi41NTMtMS4wNTMuMjQ3LS4zMDUuNTUtLjU0NC45MS0uNzE3YTIuODIgMi44MiAwIDAgMSAxLjIzNy0uMjZjLjM3OCAwIC43MTUuMDUyIDEuMDEyLjE1Ny4zLjEuNTU2LjI2Ljc2NS40NzkuMjEuMjE4LjM3LjUwMy40NzkuODU0LjExLjM0Ni4xNjQuNzY2LjE2NCAxLjI1OFYyNWgtMS42NTR2LTQuNzkyYzAtLjM3NC0uMDU1LS42NjMtLjE2NS0uODY4YS44NS44NSAwIDAgMC0uNDUtLjQyNCAxLjgyIDEuODIgMCAwIDAtLjcwNS0uMTIzYy0uMjU1IDAtLjQ4LjA0OC0uNjc3LjE0M2ExLjM0NCAxLjM0NCAwIDAgMC0uNDg1LjM5IDEuNzM1IDEuNzM1IDAgMCAwLS4zLjU2OCAyLjQzMiAyLjQzMiAwIDAgMC0uMDk2LjY5N1ptNi4xNjMuNzkzdi0uMTU3YzAtLjUzNC4wNzctMS4wMjguMjMyLTEuNDg0LjE1NS0uNDYuMzc4LS44NTkuNjctMS4xOTZhMy4wMjMgMy4wMjMgMCAwIDEgMS4wOC0uNzkzIDMuNTE2IDMuNTE2IDAgMCAxIDEuNDUtLjI4N2MuNTQyIDAgMS4wMjUuMDk2IDEuNDQ5LjI4Ny40MjguMTg3Ljc5LjQ1MSAxLjA4Ny43OTMuMjk2LjMzNy41MjEuNzM2LjY3NiAxLjE5Ni4xNTUuNDU2LjIzMy45NS4yMzMgMS40ODR2LjE1N2MwIC41MzMtLjA3OCAxLjAyNy0uMjMzIDEuNDgzLS4xNTUuNDU2LS4zOC44NTUtLjY3NiAxLjE5N2EzLjE0NSAzLjE0NSAwIDAgMS0xLjA4MS43OTJjLS40MjMuMTg3LS45MDQuMjgtMS40NDIuMjhhMy42MDcgMy42MDcgMCAwIDEtMS40NTYtLjI4IDMuMTM3IDMuMTM3IDAgMCAxLTEuMDgtLjc5MyAzLjU1IDMuNTUgMCAwIDEtLjY3Ny0xLjE5NiA0LjU3OSA0LjU3OSAwIDAgMS0uMjMyLTEuNDgzWm0xLjY0Ny0uMTU3di4xNTdjMCAuMzMyLjAzNC42NDcuMTAzLjk0My4wNjguMjk2LjE3NS41NTYuMzIxLjc4LjE0Ni4yMjMuMzMzLjM5OC41Ni41MjYuMjI5LjEyNy41LjE5MS44MTQuMTkxLjMwNSAwIC41Ny0uMDY0Ljc5My0uMTkxYTEuNTkgMS41OSAwIDAgMCAuNTYxLS41MjdjLjE0NS0uMjIzLjI1My0uNDgzLjMyMS0uNzc5YTMuOTMgMy45MyAwIDAgMCAuMTA5LS45NDN2LS4xNTdjMC0uMzI5LS4wMzYtLjYzOS0uMTA5LS45M2EyLjM5IDIuMzkgMCAwIDAtLjMyOC0uNzg2IDEuNTcgMS41NyAwIDAgMC0uNTYtLjUzNCAxLjU0MyAxLjU0MyAwIDAgMC0uOC0uMTk4Yy0uMzEgMC0uNTguMDY2LS44MDcuMTk4YTEuNTg4IDEuNTg4IDAgMCAwLS41NTQuNTM0IDIuNDggMi40OCAwIDAgMC0uMzIxLjc4NiA0LjA2MiA0LjA2MiAwIDAgMC0uMTAzLjkzWm04LjQ5NC0yLjA0NFYyNWgtMS42NDd2LTcuMzk2aDEuNTUybC4wOTUgMS41NzlabS0uMjk0IDEuODQ1LS41MzMtLjAwNmE0LjgzNCA0LjgzNCAwIDAgMSAuMjE5LTEuNDQzIDMuNTQgMy41NCAwIDAgMSAuNjAyLTEuMTI4Yy4yNTktLjMxNC41NjktLjU1Ni45MjktLjcyNC4zNi0uMTc0Ljc2MS0uMjYgMS4yMDMtLjI2LjM1NiAwIC42NzcuMDUuOTY0LjE1LjI5Mi4wOTYuNTQuMjUzLjc0NS40NzIuMjEuMjE5LjM2OS41MDMuNDc5Ljg1NC4xMDkuMzQ3LjE2NC43NzMuMTY0IDEuMjc5VjI1aC0xLjY1NHYtNC43ODVjMC0uMzU2LS4wNTMtLjYzNi0uMTU4LS44NDFhLjg3OC44NzggMCAwIDAtLjQ0NC0uNDQ0Yy0uMTkxLS4wOTEtLjQzMS0uMTM3LS43MTgtLjEzNy0uMjgyIDAtLjUzNS4wNi0uNzU5LjE3OC0uMjIzLjExOC0uNDEyLjI4LS41NjcuNDg1LS4xNS4yMDUtLjI2Ny40NDItLjM0OS43MTFhMi45MzggMi45MzggMCAwIDAtLjEyMy44NjFabTYuMzg5LjM1NnYtLjE1N2MwLS41MzQuMDc4LTEuMDI4LjIzMy0xLjQ4NC4xNTQtLjQ2LjM3OC0uODU5LjY2OS0xLjE5NmEzLjAzMyAzLjAzMyAwIDAgMSAxLjA4MS0uNzkzIDMuNTEzIDMuNTEzIDAgMCAxIDEuNDQ5LS4yODdjLjU0MiAwIDEuMDI1LjA5NiAxLjQ0OS4yODcuNDI4LjE4Ny43OTEuNDUxIDEuMDg3Ljc5My4yOTYuMzM3LjUyMi43MzYuNjc3IDEuMTk2LjE1NS40NTYuMjMyLjk1LjIzMiAxLjQ4NHYuMTU3YzAgLjUzMy0uMDc3IDEuMDI3LS4yMzIgMS40ODNhMy41NTUgMy41NTUgMCAwIDEtLjY3NyAxLjE5NyAzLjEzMiAzLjEzMiAwIDAgMS0xLjA4Ljc5MmMtLjQyNC4xODctLjkwNS4yOC0xLjQ0My4yOGEzLjYxIDMuNjEgMCAwIDEtMS40NTYtLjI4IDMuMTQxIDMuMTQxIDAgMCAxLTEuMDgtLjc5MyAzLjUzNiAzLjUzNiAwIDAgMS0uNjc2LTEuMTk2IDQuNTc5IDQuNTc5IDAgMCAxLS4yMzMtMS40ODNabTEuNjQ4LS4xNTd2LjE1N2MwIC4zMzIuMDM0LjY0Ny4xMDIuOTQzLjA2OC4yOTYuMTc2LjU1Ni4zMjEuNzguMTQ2LjIyMy4zMzMuMzk4LjU2MS41MjYuMjI4LjEyNy40OTkuMTkxLjgxMy4xOTEuMzA2IDAgLjU3LS4wNjQuNzkzLS4xOTFhMS41OSAxLjU5IDAgMCAwIC41NjEtLjUyN2MuMTQ2LS4yMjMuMjUzLS40ODMuMzIxLS43NzkuMDczLS4yOTYuMTEtLjYxLjExLS45NDN2LS4xNTdjMC0uMzI5LS4wMzctLjYzOS0uMTEtLjkzYTIuMzkgMi4zOSAwIDAgMC0uMzI4LS43ODYgMS41NzYgMS41NzYgMCAwIDAtLjU2LS41MzQgMS41NDUgMS41NDUgMCAwIDAtLjgtLjE5OGMtLjMxIDAtLjU3OS4wNjYtLjgwNy4xOThhMS41OTIgMS41OTIgMCAwIDAtLjU1NC41MzRjLS4xNDUuMjI4LS4yNTMuNDktLjMyMS43ODZhNC4wODQgNC4wODQgMCAwIDAtLjEwMi45M1ptNy45NjctMy42MjMgMS40MDkgMi40NTQgMS40MzUtMi40NTRoMS44MTJsLTIuMjQzIDMuNjNMMTI2LjA2NCAyNWgtMS44MTFsLTEuNTA0LTIuNTU3TDEyMS4yNDUgMjVoLTEuODE4bDIuMzI0LTMuNzY3LTIuMjM1LTMuNjNoMS44MDRabTcuNzk3IDBWMjVoLTEuNjU0di03LjM5NmgxLjY1NFptLTEuNzYzLTEuOTQyYzAtLjI1LjA4Mi0uNDU4LjI0Ni0uNjIyLjE2OC0uMTY5LjQwMS0uMjUzLjY5Ny0uMjUzLjI5MiAwIC41MjIuMDg0LjY5LjI1M2EuODMuODMgMCAwIDEgLjI1My42MjIuODIyLjgyMiAwIDAgMS0uMjUzLjYxNWMtLjE2OC4xNjQtLjM5OC4yNDYtLjY5LjI0Ni0uMjk2IDAtLjUyOS0uMDgyLS42OTctLjI0NmEuODM1LjgzNSAwIDAgMS0uMjQ2LS42MTVabTguMzMgNy44MDdWMTQuNWgxLjY1NFYyNWgtMS40OTdsLS4xNTctMS41MzFabS00LjgxMy0yLjA4NXYtLjE0NGMwLS41Ni4wNjYtMS4wNy4xOTgtMS41MzEuMTMyLS40NjUuMzI0LS44NjQuNTc1LTEuMTk2LjI1LS4zMzguNTU2LS41OTUuOTE2LS43NzMuMzYtLjE4Mi43NjUtLjI3MyAxLjIxNi0uMjczLjQ0NyAwIC44MzkuMDg2IDEuMTc2LjI2LjMzNy4xNzMuNjI0LjQyMS44NjEuNzQ1LjIzNy4zMTkuNDI3LjcwMS41NjggMS4xNDguMTQxLjQ0Mi4yNDEuOTM0LjMwMSAxLjQ3N3YuNDU4YTcuNDczIDcuNDczIDAgMCAxLS4zMDEgMS40NDkgMy44ODYgMy44ODYgMCAwIDEtLjU2OCAxLjEzNSAyLjQ1IDIuNDUgMCAwIDEtLjg2OC43MzhjLS4zMzcuMTczLS43MzEuMjYtMS4xODIuMjYtLjQ0NyAwLS44NS0uMDk0LTEuMjEtLjI4YTIuNjYyIDIuNjYyIDAgMCAxLS45MDktLjc4NyAzLjY5MiAzLjY5MiAwIDAgMS0uNTc1LTEuMTkgNS40MTMgNS40MTMgMCAwIDEtLjE5OC0xLjQ5NlptMS42NDgtLjE0NHYuMTQ0YzAgLjMzNy4wMjkuNjUxLjA4OC45NDMuMDY0LjI5Mi4xNjIuNTUuMjk0Ljc3My4xMzMuMjE4LjMwMy4zOTEuNTEzLjUyLjIxNC4xMjIuNDY5LjE4NC43NjYuMTg0LjM3MyAwIC42ODEtLjA4Mi45MjMtLjI0Ni4yNDEtLjE2NC40My0uMzg2LjU2Ny0uNjYzLjE0MS0uMjgzLjIzNy0uNTk4LjI4Ny0uOTQ0di0xLjIzN2EyLjk4OCAyLjk4OCAwIDAgMC0uMTcxLS43NTIgMi4wMTggMi4wMTggMCAwIDAtLjMzNS0uNjA4IDEuNDEzIDEuNDEzIDAgMCAwLS41MjYtLjQxIDEuNjUgMS42NSAwIDAgMC0uNzMyLS4xNTFjLS4zIDAtLjU1Ni4wNjQtLjc2NS4xOTEtLjIxLjEyOC0uMzgzLjMwMy0uNTIuNTI3YTIuNiAyLjYgMCAwIDAtLjI5NC43NzkgNC40OTYgNC40OTYgMCAwIDAtLjA5NS45NVptMTAuMDUyIDMuODk3YTMuOTQxIDMuOTQxIDAgMCAxLTEuNDgzLS4yNjcgMy4yNyAzLjI3IDAgMCAxLTEuMTIxLS43NTkgMy4zNTIgMy4zNTIgMCAwIDEtLjcwNC0xLjE0MSAzLjk5OSAzLjk5OSAwIDAgMS0uMjQ2LTEuNDE1di0uMjc0YzAtLjU3OC4wODQtMS4xMDMuMjUzLTEuNTcyLjE2OC0uNDcuNDAzLS44Ny43MDQtMS4yMDMuMy0uMzM3LjY1Ni0uNTk1IDEuMDY2LS43NzMuNDEtLjE3Ny44NTUtLjI2NiAxLjMzMy0uMjY2LjUyOSAwIC45OTEuMDg5IDEuMzg4LjI2Ni4zOTYuMTc4LjcyNC40MjkuOTg0Ljc1Mi4yNjQuMzIuNDYuNy41ODggMS4xNDIuMTMyLjQ0Mi4xOTguOTMuMTk4IDEuNDYzdi43MDRoLTUuNzE1VjIwLjYxaDQuMDg4di0uMTNhMi4zMTggMi4zMTggMCAwIDAtLjE3Ny0uODM0IDEuNDE4IDEuNDE4IDAgMCAwLS40ODYtLjYyOGMtLjIxOS0uMTYtLjUxLS4yNC0uODc1LS4yNC0uMjczIDAtLjUxNy4wNi0uNzMxLjE3OC0uMjEuMTE0LS4zODUuMjgtLjUyNy41LS4xNDEuMjE4LS4yNS40ODItLjMyOC43OTItLjA3My4zMDUtLjEwOS42NS0uMTA5IDEuMDMydi4yNzRjMCAuMzIzLjA0My42MjQuMTMuOTAyLjA5MS4yNzMuMjIzLjUxMy4zOTYuNzE4LjE3My4yMDUuMzgzLjM2Ny42MjkuNDg1LjI0Ni4xMTQuNTI3LjE3MS44NDEuMTcxLjM5NiAwIC43NS0uMDggMS4wNi0uMjQuMzA5LS4xNTkuNTc4LS4zODQuODA2LS42NzZsLjg2OC44NGEzLjM0NyAzLjM0NyAwIDAgMS0uNjIyLjY3Yy0uMjU1LjIxLS41NjcuMzgxLS45MzYuNTE0YTMuNzMyIDMuNzMyIDAgMCAxLTEuMjcyLjE5OFoiIC8+CiAgPHBhdGggZmlsbD0iI0RFMjM0MyIKICAgIGQ9Ik02NC4wODIgNTguMjQ4aC41Mjd2My40NjNoLS4yOThjLTEuNTEyIDAtMi44MTkuMjM0LTMuOTIuNzAzLTEuMDkuNDY5LTEuOTg3IDEuMTEzLTIuNjkgMS45MzQtLjcwMy44Mi0xLjIzIDEuNzgtMS41ODIgMi44ODMtLjM0IDEuMDktLjUxIDIuMjUtLjUxIDMuNDh2NC4wMjVjMCAxLjAyLjExMiAxLjkyMi4zMzQgMi43MDcuMjIzLjc3NC41MzQgMS40MjQuOTMyIDEuOTUxLjQxLjUxNi44NzkuOTA5IDEuNDA2IDEuMTc4LjUyOC4yNyAxLjA5Ni40MDUgMS43MDUuNDA1LjYzMyAwIDEuMjA3LS4xMyAxLjcyMy0uMzg3YTMuOTkgMy45OSAwIDAgMCAxLjMxOC0xLjEwOCA0Ljk1MiA0Ljk1MiAwIDAgMCAuODI2LTEuNjcgNy41MjcgNy41MjcgMCAwIDAgLjI4Mi0yLjEwOWMwLS43MjYtLjA5NC0xLjQwNi0uMjgyLTIuMDM5YTUuMDk3IDUuMDk3IDAgMCAwLS43OS0xLjY4NyAzLjYzMyAzLjYzMyAwIDAgMC0xLjMxOS0xLjE0M2MtLjUxNi0uMjgxLTEuMTEzLS40MjItMS43OTMtLjQyMi0uODQ0IDAtMS42MTEuMi0yLjMwMy41OTgtLjY4LjM5OC0xLjIzLjkyLTEuNjUyIDEuNTY0YTQuMDI4IDQuMDI4IDAgMCAwLS42NjggMi4wMjJsLTEuNjE3LS41MjhjLjA5NC0xLjA5LjMzNC0yLjA2OC43Mi0yLjkzNWE3LjA3MSA3LjA3MSAwIDAgMSAxLjU2NS0yLjIxNSA2LjU3NSA2LjU3NSAwIDAgMSAyLjIzMi0xLjM4OWMuODU2LS4zMjggMS43OTQtLjQ5MiAyLjgxMy0uNDkyIDEuMjQyIDAgMi4zMi4yMzQgMy4yMzQuNzAzYTYuNDIyIDYuNDIyIDAgMCAxIDIuMjY4IDEuOTE2IDguMTE4IDguMTE4IDAgMCAxIDEuMzUzIDIuNzQyYy4zMDUgMS4wMi40NTcgMi4wODYuNDU3IDMuMiAwIDEuMjMtLjE4NyAyLjM3OS0uNTYyIDMuNDQ1YTguMTA0IDguMTA0IDAgMCAxLTEuNjUyIDIuNzc3IDcuNDE2IDcuNDE2IDAgMCAxLTIuNjIgMS44NjRjLTEuMDE5LjQ0NS0yLjE4LjY2OC0zLjQ4LjY2OC0xLjM3MSAwLTIuNTk2LS4yNjQtMy42NzQtLjc5MWE4LjA3MiA4LjA3MiAwIDAgMS0yLjcyNC0yLjE4IDkuOTQxIDkuOTQxIDAgMCAxLTEuNjg4LTMuMiAxMi40ODQgMTIuNDg0IDAgMCAxLS41OC0zLjgzMXYtMS43NThjMC0xLjk0NS4yNDYtMy43OC43MzgtNS41MDIuNDkyLTEuNzM0IDEuMjQ4LTMuMjY0IDIuMjY4LTQuNTg4IDEuMDMxLTEuMzI0IDIuMzUtMi4zNjEgMy45NTUtMy4xMTEgMS42MDUtLjc2MiAzLjUyMS0xLjE0MyA1Ljc0OC0xLjE0M1ptMjQuNzg3IDEwLjgxdjQuMTg0YzAgMi4wMDQtLjE5OSAzLjcxNS0uNTk3IDUuMTMzLS4zODcgMS40MDYtLjk1IDIuNTQ5LTEuNjg4IDMuNDI4YTYuNjIxIDYuNjIxIDAgMCAxLTIuNjU0IDEuOTMzYy0xLjAyLjQxLTIuMTYyLjYxNi0zLjQyOC42MTZhOS44MjYgOS44MjYgMCAwIDEtMi44MTItLjM4NyA2Ljg3MSA2Ljg3MSAwIDAgMS0yLjMyLTEuMjEzYy0uNjkyLS41NS0xLjI4NC0xLjI2LTEuNzc2LTIuMTI3LS40OC0uODc5LS44NTYtMS45MjgtMS4xMjUtMy4xNDctLjI1OC0xLjIxOC0uMzg3LTIuNjMtLjM4Ny00LjIzNlY2OS4wNmMwLTIuMDE2LjItMy43MTUuNTk4LTUuMDk4LjM5OC0xLjM5NS45NjctMi41MjYgMS43MDUtMy4zOTMuNzM4LS44NzggMS42MTctMS41MTcgMi42MzctMS45MTYgMS4wMy0uMzk4IDIuMTgtLjU5NyAzLjQ0NS0uNTk3IDEuMDIgMCAxLjk1Ny4xMjkgMi44MTMuMzg2Ljg2Ny4yNDYgMS42NC42NCAyLjMyIDEuMTc4LjY5MS41NCAxLjI3NyAxLjI0MiAxLjc1OCAyLjExLjQ5Mi44NTUuODY3IDEuODkyIDEuMTI1IDMuMTEuMjU3IDEuMjA4LjM4NiAyLjYxNC4zODYgNC4yMlptLTQuMjM2IDQuNzgydi01LjQxNGMwLTEuMDItLjA1OS0xLjkxNi0uMTc2LTIuNjktLjExNy0uNzg1LS4yOTMtMS40NDctLjUyNy0xLjk4Ni0uMjIzLS41NS0uNTA0LS45OTYtLjg0NC0xLjMzNmEyLjk3IDIuOTcgMCAwIDAtMS4xNi0uNzU2IDQuMDk2IDQuMDk2IDAgMCAwLTEuNDU5LS4yNDZjLS42NTYgMC0xLjI0Mi4xMjktMS43NTguMzg3LS41MTYuMjQ2LS45NS42NDQtMS4zIDEuMTk1LS4zNTIuNTUtLjYyMiAxLjI3OC0uODEgMi4xOC0uMTc1Ljg5LS4yNjMgMS45NzQtLjI2MyAzLjI1MnY1LjQxNGMwIDEuMDMxLjA1OSAxLjk0LjE3NiAyLjcyNC4xMTcuNzg2LjI5MyAxLjQ2LjUyNyAyLjAyMi4yMzUuNTUuNTE2IDEuMDA4Ljg0NCAxLjM3MS4zNC4zNTIuNzI3LjYxIDEuMTYuNzc0LjQ0NS4xNjMuOTMyLjI0NiAxLjQ2LjI0Ni42NjcgMCAxLjI1OS0uMTMgMS43NzUtLjM4Ny41MTUtLjI1OC45NDktLjY2OCAxLjMtMS4yMy4zNTItLjU3NS42MTUtMS4zMTkuNzkxLTIuMjMzLjE3Ni0uOTE0LjI2NC0yLjAxLjI2NC0zLjI4N1pNOTYuMjIyIDc4LjA3M1Y4NEg5NC45NXYtNy4zOTdoMS4yMDNsLjA2OSAxLjQ3Wm0tLjI2IDEuOTQ5TDk1LjM3NCA4MGMuMDA1LS41MDYuMDctLjk3My4xOTgtMS40MDEuMTI4LS40MzMuMzE3LS44MS41NjgtMS4xMjguMjUtLjMyLjU2My0uNTY1LjkzNi0uNzM5LjM3NC0uMTc3LjgwNy0uMjY2IDEuMjk5LS4yNjYuMzQ2IDAgLjY2NS4wNS45NTcuMTUuMjkyLjA5Ni41NDUuMjQ5Ljc1OS40NTguMjE0LjIxLjM4LjQ3OS40OTkuODA3LjExOC4zMjguMTc4LjcyNC4xNzggMS4xOVY4NGgtMS4yNjV2LTQuODY3YzAtLjM4OC0uMDY2LS42OTctLjE5OC0uOTNhMS4xMzMgMS4xMzMgMCAwIDAtLjU0Ny0uNTA2IDEuOTczIDEuOTczIDAgMCAwLS44MzQtLjE2NGMtLjM3NCAwLS42ODYuMDY2LS45MzcuMTk4LS4yNS4xMzMtLjQ1LjMxNS0uNjAxLjU0Ny0uMTUuMjMzLS4yNi41LS4zMjguOC0uMDY0LjI5Ni0uMDk2LjYxLS4wOTYuOTQ0Wm00Ljc5Mi0uNjk4LS44NDguMjZhMy43NCAzLjc0IDAgMCAxIC4xOTgtMS4xNjljLjEzMy0uMzc0LjMyMi0uNzA2LjU2OC0uOTk4LjI1MS0uMjkyLjU1OC0uNTIyLjkyMy0uNjkuMzY0LS4xNzQuNzgxLS4yNiAxLjI1MS0uMjYuMzk2IDAgLjc0Ny4wNTIgMS4wNTIuMTU3LjMxLjEwNS41Ny4yNjcuNzguNDg1LjIxNC4yMTUuMzc2LjQ5LjQ4NS44MjguMTA5LjMzNy4xNjQuNzM4LjE2NCAxLjIwM1Y4NGgtMS4yNzF2LTQuODc0YzAtLjQxNS0uMDY2LS43MzYtLjE5OS0uOTY0YTEuMDI5IDEuMDI5IDAgMCAwLS41NDYtLjQ4NSAyLjE5IDIuMTkgMCAwIDAtLjgzNC0uMTQ0Yy0uMjc4IDAtLjUyNS4wNDgtLjczOS4xNDRhMS41MyAxLjUzIDAgMCAwLS41NC4zOTYgMS43MzggMS43MzggMCAwIDAtLjMzNS41NjggMi4xMDUgMi4xMDUgMCAwIDAtLjEwOS42ODNabTExLjQ3OS0yLjcyaDEuMTQ5djcuMjM5YzAgLjY1MS0uMTMzIDEuMjA3LS4zOTcgMS42NjgtLjI2NC40Ni0uNjMzLjgwOS0xLjEwNyAxLjA0Ni0uNDcuMjQxLTEuMDEyLjM2Mi0xLjYyNy4zNjItLjI1NSAwLS41NTYtLjA0MS0uOTAzLS4xMjNhMy42MzcgMy42MzcgMCAwIDEtMS4wMTEtLjQwMyAyLjUyNiAyLjUyNiAwIDAgMS0uODI3LS43NmwuNjYzLS43NTFjLjMxLjM3My42MzMuNjMzLjk3Ljc4LjM0Mi4xNDUuNjc5LjIxOCAxLjAxMi4yMTguNDAxIDAgLjc0Ny0uMDc1IDEuMDM5LS4yMjYuMjkyLS4xNS41MTctLjM3My42NzctLjY3LjE2NC0uMjkxLjI0Ni0uNjUxLjI0Ni0xLjA4di01LjY3M2wuMTE2LTEuNjI3Wm0tNS4wOTMgMy43OHYtLjE0NGMwLS41NjUuMDY3LTEuMDc4LjE5OS0xLjUzOC4xMzYtLjQ2NS4zMy0uODY0LjU4MS0xLjE5NmEyLjYwOSAyLjYwOSAwIDAgMSAyLjEzOS0xLjA0Yy40NjUgMCAuODcxLjA4MyAxLjIxNy4yNDcuMzUxLjE2LjY0Ny4zOTQuODg5LjcwNC4yNDYuMzA1LjQ0LjY3NC41ODEgMS4xMDcuMTQxLjQzMy4yMzkuOTIzLjI5NCAxLjQ3di42MjlhNi40MjQgNi40MjQgMCAwIDEtLjI5NCAxLjQ2MyAzLjQ0OCAzLjQ0OCAwIDAgMS0uNTgxIDEuMTA3Yy0uMjQyLjMwNi0uNTM4LjU0LS44ODkuNzA0LS4zNTEuMTYtLjc2MS4yNC0xLjIzLjI0LS40NDIgMC0uODQzLS4wOTQtMS4yMDMtLjI4YTIuNzMyIDIuNzMyIDAgMCAxLS45MTYtLjc4NyAzLjY5MiAzLjY5MiAwIDAgMS0uNTg4LTEuMTkgNS40MTIgNS40MTIgMCAwIDEtLjE5OS0xLjQ5NlptMS4yNjUtLjE0NHYuMTQ0YzAgLjM2OS4wMzcuNzE1LjEwOSAxLjAzOS4wNzguMzIzLjE5NC42MDguMzQ5Ljg1NC4xNi4yNDYuMzYyLjQ0LjYwOC41ODEuMjQ3LjEzNy41NDEuMjA2Ljg4Mi4yMDYuNDIgMCAuNzY2LS4wOSAxLjAzOS0uMjY3LjI3NC0uMTc4LjQ5LS40MTMuNjUtLjcwNC4xNjQtLjI5Mi4yOTEtLjYwOS4zODMtLjk1di0xLjY0OGEzLjQgMy40IDAgMCAwLS4yMzMtLjcyNSAyLjIyNSAyLjIyNSAwIDAgMC0uMzk2LS42MjggMS43MiAxLjcyIDAgMCAwLS41OTUtLjQ0NSAxLjk3MyAxLjk3MyAwIDAgMC0uODM0LS4xNjRjLS4zNDYgMC0uNjQ1LjA3My0uODk2LjIxOWExLjc0OCAxLjc0OCAwIDAgMC0uNjA4LjU4OCAyLjgzMyAyLjgzMyAwIDAgMC0uMzQ5Ljg2MWMtLjA3Mi4zMjQtLjEwOS42Ny0uMTA5IDEuMDRabTExLjUtNi4xOTMtNC4xNSAxMC44MDhoLTEuMDg2bDQuMTU2LTEwLjgwOGgxLjA4Wm0yLjgzOSA0LjAyNlY4NGgtMS4yNzJ2LTcuMzk3aDEuMjAzbC4wNjkgMS40N1ptLS4yNiAxLjk0OS0uNTg4LS4wMjFjLjAwNC0uNTA2LjA3MS0uOTczLjE5OC0xLjQwMS4xMjgtLjQzMy4zMTctLjgxLjU2OC0xLjEyOC4yNS0uMzIuNTYyLS41NjUuOTM2LS43MzkuMzc0LS4xNzcuODA3LS4yNjYgMS4yOTktLjI2Ni4zNDYgMCAuNjY1LjA1Ljk1Ny4xNS4yOTIuMDk2LjU0NS4yNDkuNzU5LjQ1OC4yMTQuMjEuMzguNDc5LjQ5OS44MDcuMTE4LjMyOC4xNzcuNzI0LjE3NyAxLjE5Vjg0aC0xLjI2NHYtNC44NjdjMC0uMzg4LS4wNjYtLjY5Ny0uMTk4LS45M2ExLjEzNSAxLjEzNSAwIDAgMC0uNTQ3LS41MDYgMS45NzMgMS45NzMgMCAwIDAtLjgzNC0uMTY0Yy0uMzc0IDAtLjY4Ni4wNjYtLjkzNy4xOTgtLjI1LjEzMy0uNDUxLjMxNS0uNjAxLjU0Ny0uMTUxLjIzMy0uMjYuNS0uMzI5LjgtLjA2My4yOTYtLjA5NS42MS0uMDk1Ljk0NFptNC43OTItLjY5OC0uODQ4LjI2Yy4wMDUtLjQwNi4wNzEtLjc5NS4xOTgtMS4xNjkuMTMzLS4zNzQuMzIyLS43MDYuNTY4LS45OTguMjUtLjI5Mi41NTgtLjUyMi45MjMtLjY5LjM2NC0uMTc0Ljc4MS0uMjYgMS4yNTEtLjI2LjM5NiAwIC43NDcuMDUyIDEuMDUyLjE1Ny4zMS4xMDUuNTcuMjY3Ljc4LjQ4NS4yMTQuMjE1LjM3Ni40OS40ODUuODI4LjEwOS4zMzcuMTY0LjczOC4xNjQgMS4yMDNWODRoLTEuMjcxdi00Ljg3NGMwLS40MTUtLjA2Ny0uNzM2LS4xOTktLjk2NGExLjAzIDEuMDMgMCAwIDAtLjU0Ny0uNDg1IDIuMTgzIDIuMTgzIDAgMCAwLS44MzQtLjE0NGMtLjI3OCAwLS41MjQuMDQ4LS43MzguMTQ0YTEuNTMgMS41MyAwIDAgMC0uNTQuMzk2IDEuNzM4IDEuNzM4IDAgMCAwLS4zMzUuNTY4IDIuMTA1IDIuMTA1IDAgMCAwLS4xMDkuNjgzWm03LjU0Mi0zLjAyMWguNTc0Yy4yMjMgMCAuNDA4LS4wMzIuNTU0LS4wOTZhLjY4LjY4IDAgMCAwIC40NDQtLjY1YzAtLjItLjA3My0uMzY4LS4yMTktLjUwNS0uMTQ2LS4xMzctLjM3Ni0uMjA1LS42OS0uMjA1LS4yNiAwLS40NzcuMDU3LS42NS4xN2EuNTM1LjUzNSAwIDAgMC0uMjUzLjQ2NWgtMS4wNzNjMC0uMzEuMDg3LS41NzYuMjYtLjguMTczLS4yMjcuNDA4LS40MDMuNzA0LS41MjZzLjYyNy0uMTg0Ljk5MS0uMTg0Yy4zOTcgMCAuNzQ1LjA1OSAxLjA0Ni4xNzcuMzAxLjExNC41MzYuMjg1LjcwNC41MTMuMTY5LjIyOC4yNTMuNTA4LjI1My44NCAwIC4yNDMtLjA3NS40NjYtLjIyNi42Ny0uMTQ1LjIwMS0uMzYuMzYzLS42NDIuNDg2LS4yNzguMTE5LS42MTguMTc4LTEuMDE5LjE3OGgtLjc1OHYtLjUzM1ptMCAuNzg2di0uNTMzaC43NThjLjQ1NiAwIC44My4wNTQgMS4xMjEuMTY0LjI5Mi4xMS41MDkuMjY2LjY1LjQ3MS4xNDEuMjA2LjIxMi40NTQuMjEyLjc0NSAwIC4zMzMtLjA5MS42MTgtLjI3NC44NTVhMS42ODcgMS42ODcgMCAwIDEtLjc1Mi41NGMtLjMxNC4xMjMtLjY3LjE4NS0xLjA2Ni4xODUtLjM0MiAwLS42Ny0uMDU1LS45ODQtLjE2NGExLjc0MyAxLjc0MyAwIDAgMS0uNzU5LS41MjdjLS4xOTYtLjIzNy0uMjk0LS41NC0uMjk0LS45MDloMS4wNzNjMCAuMjA1LjA5MS4zNzguMjc0LjUyLjE4Mi4xMzYuNDIzLjIwNS43MjQuMjA1LjMxOSAwIC41NjEtLjA3MS43MjUtLjIxMmEuNjgzLjY4MyAwIDAgMCAuMjUzLS41NDcuNzEzLjcxMyAwIDAgMC0uMTM3LS40NTguNzA0LjcwNCAwIDAgMC0uMzc2LS4yNTMgMS43NzYgMS43NzYgMCAwIDAtLjU3NC0uMDgyaC0uNTc0WiIgLz4KICA8cGF0aCBmaWxsPSJ1cmwoI2MpIiBmaWxsLW9wYWNpdHk9Ii4zIiBkPSJNMTQ3LjQ0NCA3MS43NzhoNy4xMTF2OC44ODlsLTMuNTUxIDIuNjY2LTMuNTYtMi42NjZ2LTguODlaIiAvPgogIDxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjg3IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiCiAgICBkPSJNMTUwLjY3NCA4My44OTZjLjE4LjE4LjQ3Mi4xOC42NTIgMGwzLjIyOS0zLjIzLS42NTItLjY1Mi0xLjQxOSAxLjQyYS42LjYgMCAwIDEtMS4wMjMtLjQyNXYtMy44OThoLS45MjJ2My44OThhLjYuNiAwIDAgMS0xLjAyMy40MjRsLTEuNDE5LTEuNDE5LS42NTMuNjUzIDMuMjMgMy4yMjlaIgogICAgY2xpcC1ydWxlPSJldmVub2RkIiAvPgogIDxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjg3IgogICAgZD0iTTE2Mi41NjggNzkuNzkzdjEuMTI1aC0zLjA5OXYtMS4xMjVoMy4wOTlabTUuMjYtNC4zNTRWODRoLTEuNDEydi02Ljg4NWwtMi4wOTIuNzF2LTEuMTY3bDMuMzM0LTEuMjE5aC4xN1ptNy40ODEtLjAyM2guMTc1djEuMTU0aC0uMDk5Yy0uNTA0IDAtLjk0LjA3OC0xLjMwNy4yMzVhMi40MTMgMi40MTMgMCAwIDAtLjg5Ni42NDQgMi43MjUgMi43MjUgMCAwIDAtLjUyOC45NjFjLS4xMTMuMzYzLS4xNy43NS0uMTcgMS4xNnYxLjM0MmMwIC4zNC4wMzcuNjQuMTEyLjkwMi4wNzQuMjU4LjE3Ny40NzUuMzEuNjUuMTM3LjE3My4yOTMuMzA0LjQ2OS4zOTMuMTc2LjA5LjM2NS4xMzUuNTY4LjEzNS4yMTEgMCAuNDAzLS4wNDMuNTc1LS4xMjkuMTcxLS4wOS4zMTgtLjIxMy40MzktLjM2OXMuMjEzLS4zNDIuMjc1LS41NTZhMi40OSAyLjQ5IDAgMCAwIC4wOTQtLjcwNGMwLS4yNDItLjAzMS0uNDY4LS4wOTQtLjY4YTEuNjkgMS42OSAwIDAgMC0uMjYzLS41NjIgMS4yMSAxLjIxIDAgMCAwLS40NC0uMzggMS4yMjggMS4yMjggMCAwIDAtLjU5Ny0uMTQxIDEuNTEgMS41MSAwIDAgMC0uNzY4LjE5OWMtLjIyNi4xMzMtLjQxLjMwNy0uNTUxLjUyMS0uMTM2LjIxMS0uMjExLjQzNi0uMjIyLjY3NGwtLjUzOS0uMTc1Yy4wMzEtLjM2NC4xMTEtLjY5LjI0LS45OC4xMzMtLjI4OC4zMDYtLjUzNC41MjEtLjczNy4yMTUtLjIwNC40NjMtLjM1OC43NDQtLjQ2My4yODYtLjExLjU5OC0uMTY0LjkzOC0uMTY0LjQxNCAwIC43NzMuMDc4IDEuMDc4LjIzNC4zMDUuMTU2LjU1Ny4zNy43NTYuNjM5LjIwMy4yNjUuMzUzLjU3LjQ1MS45MTQuMTAyLjM0LjE1Mi42OTUuMTUyIDEuMDY2IDAgLjQxLS4wNjIuNzkzLS4xODcgMS4xNDktLjEyNS4zNTEtLjMwOS42Ni0uNTUxLjkyNWEyLjQ2NiAyLjQ2NiAwIDAgMS0uODczLjYyMSAyLjg3IDIuODcgMCAwIDEtMS4xNi4yMjNjLS40NTcgMC0uODY1LS4wODgtMS4yMjUtLjI2NGEyLjY5NiAyLjY5NiAwIDAgMS0uOTA4LS43MjYgMy4zMTcgMy4zMTcgMCAwIDEtLjU2Mi0xLjA2NiA0LjE1OCA0LjE1OCAwIDAgMS0uMTk0LTEuMjc4di0uNTg2YzAtLjY0OC4wODItMS4yNi4yNDYtMS44MzRhNC40ODIgNC40ODIgMCAwIDEgLjc1Ni0xLjUyOSAzLjUzNyAzLjUzNyAwIDAgMSAxLjMxOS0xLjAzN2MuNTM1LS4yNTQgMS4xNzMtLjM4MSAxLjkxNi0uMzgxWk0xNzguNTg2IDc3LjU2di0uNDVjMC0uMzI1LjA3LS42Mi4yMTEtLjg4NS4xNDEtLjI2Ni4zNDYtLjQ3OS42MTUtLjY0LjI3LS4xNi41OTQtLjI0Ljk3My0uMjQuMzkgMCAuNzE5LjA4Ljk4NC4yNC4yNy4xNjEuNDc1LjM3NC42MTUuNjQuMTQxLjI2NS4yMTEuNTYuMjExLjg4NHYuNDUyYzAgLjMxNi0uMDcuNjA3LS4yMTEuODczYTEuNTYxIDEuNTYxIDAgMCAxLS42MDkuNjM4Yy0uMjY2LjE2LS41OTIuMjQtLjk3OS4yNC0uMzgyIDAtLjcxLS4wOC0uOTg0LS4yNGExLjYxNiAxLjYxNiAwIDAgMS0uNjE1LS42MzggMS44MzIgMS44MzIgMCAwIDEtLjIxMS0uODczWm0uOTc4LS40NXYuNDVjMCAuMTU3LjAzLjMwNS4wODguNDQ2LjA2My4xNC4xNTcuMjU0LjI4Mi4zNGEuNzk2Ljc5NiAwIDAgMCAuNDYyLjEyOWMuMTg4IDAgLjM0LS4wNDMuNDU4LS4xMy4xMjEtLjA4NS4yMS0uMTk4LjI2OS0uMzQuMDU5LS4xNC4wODgtLjI4OC4wODgtLjQ0NHYtLjQ1MmMwLS4xNi0uMDMxLS4zMS0uMDk0LS40NWEuNzYyLjc2MiAwIDAgMC0uMjY5LS4zNDcuNzgyLjc4MiAwIDAgMC0uNDYzLS4xMjguNzg3Ljc4NyAwIDAgMC0uNDU3LjEyOS44MTIuODEyIDAgMCAwLS4yNzYuMzQ1Yy0uMDU4LjE0LS4wODguMjkxLS4wODguNDUxWm0zLjEzNSA1LjI1NXYtLjQ1N2MwLS4zMi4wNzEtLjYxMy4yMTEtLjg3OWExLjY2IDEuNjYgMCAwIDEgLjYyMS0uNjM4Yy4yNy0uMTYuNTk0LS4yNC45NzMtLjI0LjM5MSAwIC43MTkuMDguOTg0LjI0LjI3LjE2LjQ3My4zNzMuNjEuNjM4LjE0LjI2Ni4yMTEuNTU5LjIxMS44OHYuNDU2YzAgLjMyLS4wNzEuNjE0LS4yMTEuODhhMS41OCAxLjU4IDAgMCAxLS42MDQuNjM4Yy0uMjY1LjE2LS41OS4yNC0uOTczLjI0LS4zODYgMC0uNzE2LS4wOC0uOTktLjI0YTEuNjU4IDEuNjU4IDAgMCAxLS42MjEtLjYzOSAxLjg1NiAxLjg1NiAwIDAgMS0uMjExLS44NzlabS45ODUtLjQ1N3YuNDU3YzAgLjE1Ny4wMzEuMzA1LjA5My40NDYuMDY3LjE0LjE2Mi4yNTUuMjg3LjM0NWEuNzc5Ljc3OSAwIDAgMCAuNDUyLjEzYy4xOTkgMCAuMzU5LS4wNDQuNDgtLjEzYS43MzcuNzM3IDAgMCAwIC4yNTgtLjM0Yy4wNTUtLjE0LjA4Mi0uMjkuMDgyLS40NXYtLjQ1OGMwLS4xNi0uMDMxLS4zMS0uMDk0LS40NTFhLjc1My43NTMgMCAwIDAtLjI3NS0uMzQuNzgyLjc4MiAwIDAgMC0uNDYzLS4xMjkuNzY5Ljc2OSAwIDAgMC0uNDU3LjEzLjgxOC44MTggMCAwIDAtLjI3Ni4zMzljLS4wNTguMTQtLjA4Ny4yOTEtLjA4Ny40NTFabTEuMTcxLTUuMjItNC4xNjYgNi42NjgtLjcyLS40MTYgNC4xNjYtNi42NjguNzIuNDE1WiIgLz4KICA8ZyBjbGlwLXBhdGg9InVybCgjZCkiPgogICAgPHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuMTIiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTE4OC4wMDQgMTI0Ljk5OSAxMiAxMjUuMDAxdi0xbDE3Ni4wMDQtLjAwMnYxWiIKICAgICAgY2xpcC1ydWxlPSJldmVub2RkIiAvPgogICAgPHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjg3IiBzdHJva2Utd2lkdGg9IjEuNSIKICAgICAgZD0iTTEyIDEzMy43NzVoOS4yNjNMMzQuNjQzIDE0M2wxMC4yOTMtMzAuNzVMNjYuNTUgMTM0LjhsMjMuNjcyLTIyLjU1IDcuMjA1IDEuMDI1TDExMS44MzYgMTQzaDE0LjQxbDEyLjM1LTI2LjY1aDkuMjY0bDkuMjYzLTE0LjM1IDEyLjM1MSA0LjFMMTg4IDExNi4zNSIgLz4KICA8L2c+CiAgPGRlZnM+CiAgICA8bGluZWFyR3JhZGllbnQgaWQ9ImMiIHgxPSIxNTAuNjgzIiB4Mj0iMTUwLjY4MyIgeTE9Ijg0LjAzIiB5Mj0iNzEuNzc4IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CiAgICAgIDxzdG9wIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6I2ZmZjtzdG9wLW9wYWNpdHk6MSIgLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIxIiBzdHlsZT0ic3RvcC1jb2xvcjojZmZmO3N0b3Atb3BhY2l0eTowIiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxjbGlwUGF0aCBpZD0iZCI+CiAgICAgIDxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0xMiA5MGgxNzZ2NjBIMTJ6IiAvPgogICAgPC9jbGlwUGF0aD4KICAgIDxwYXR0ZXJuIGlkPSJhIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBwYXR0ZXJuQ29udGVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCI+CiAgICAgIDx1c2UgeGxpbms6aHJlZj0iI2UiIHRyYW5zZm9ybT0ibWF0cml4KC4wMDUwMyAwIDAgLjAwNjI5IDAgMCkiIC8+CiAgICA8L3BhdHRlcm4+CiAgICA8ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMTk1LjE2NCIgaGVpZ2h0PSIxNTUuMTY0IiB4PSIyLjQxOCIgeT0iMi40MTgiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIKICAgICAgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KICAgICAgPGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIC8+CiAgICAgIDxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjEuNzkxIiAvPgogICAgICA8ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTEwOV80MzY4IiAvPgogICAgICA8ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzExMDlfNDM2OCIgcmVzdWx0PSJzaGFwZSIgLz4KICAgIDwvZmlsdGVyPgogICAgPGltYWdlCiAgICAgIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3QkRBQUlCQVFFQkFRSUJBUUVDQWdJQ0FnUURBZ0lDQWdVRUJBTUVCZ1VHQmdZRkJnWUdCd2tJQmdjSkJ3WUdDQXNJQ1FvS0Nnb0tCZ2dMREFzS0RBa0tDZ3IvMndCREFRSUNBZ0lDQWdVREF3VUtCd1lIQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnci93Z0FSQ0FDZkFNY0RBUkVBQWhFQkF4RUIvOFFBSFFBQkFRQURBUUVCQVFFQUFBQUFBQUFBQXdRQ0JRWUJCd0FJQ2YvRUFCc0JBQU1CQVFFQkFRQUFBQUFBQUFBQUFBRUNBd0FFQmdVSC85b0FEQU1CQUFJUUF4QUFBQUQvQUgwNHFZYmVZNEFIc093a1RFQmdHQUhFUVdBdVFmQ3dKMUZrR2lDMDhDcGtHVk1pZWpmZmZOL1p3REdTQUluQnNMYWZUREFDQUt6dW81VElCeUw0MkpzREtBNms2WU91REtSQlpQdlhtUHVqbUFHY2tNSnlnWUFjQjA3Z0tnbm1CUXpzZzJPd1VYQWtIVEFneUNkUnFodWhNbjMvQU1qOTRRMDJjd3lEVGhwVHBYRVRURjhETGlXQmt5RFlzS3BXeUIxZlJ6eTFrVGJ3NzloSFdYbVBoSDJqekgxTVFaeFFsY1Mzb3hZZzJsY1FVWEU2T3FBNmZzZk5wM0hwWDhUaVFaR0p4bFJaWUxRa3R6L2JQR2VpTVBNU1dQdTA3SEhZbTB0Wm0ybW9vTmpZQlJEWUN3TmxGMHhKd09KbHhPQnB6VlVLSjlUOGQ5Nlo4UnhnazJKaDRDRkZ3WUU0bm9oTW5qRWFEQmtKbDhZWUhEUkFkRElDa3laUlpBZWYxWHh2cGNHeGtnNENpa3lBMkU0bUFWa1RnWEJ0Z2RDZVJ0TWJJYmdNaEZTcElXbUZVQ3FnNi9iL0FBbnJ5YkErQnNCV1oxQXFUem5mQzRCc0R5RmtKMUYxS3FFVU5wRFNaVVFxQVhVSFdkMW5kUHZYaFBYZ2RPUURBR1FTczlCTTRtZEFZQ3lCVkNkUWNFWmsweXRFYVRucUozVUdXYWlCV1kxU2VpZjBKK2VlMG5JQjFGbG5PQjFtYlRsUVpScElhSURwT1p6dWdQT2F5RFVUT0ozRTFaQmFZMFFUTVhtVHcvb2Z3UHVBT0FpWWdHV2RsRmtuWkptUUtMTXl6VVNaMUNxQTJodkxYZEVYbFRUOW5HK2FTa2NHU1I1NDBoNHkvd0JDZUI5ck5SSnlKM1NSMW1JbllDUk93RjErZGZUK2QwWFAxMnJUaHZxL0szZk4xRlJKWGwrTytkL1QrTzQyMW5iNTkzL04xdG9XSGJ1ZFA2Yi9BRC8zZk05bkwrejdubjZZbVRIRXlkZlNNN0pyTHorQmZiK0wyTXE5SEdtcHZ6eE9LUWZjcGtjWjI4TUZZOURIbzRicjVOUDBjdXRwSHJKdi9TZmh2ZWNYMThiQjdRVlpkZHQ0VjFUQ1YxZ1pPRjZvN29DNVRIUkoyQnNsZUVsSmFxcWFTL0xzSldFblZkUEhxSzg5ZWI3OTR6MmVyZGNNTVdXWWlLc3RXUmV4aGVZc05SUk5TcmVzSW5tN1R4WTVsTlU4bEoxdCtZNTJBTmphT3h0eVE0L1VmSCt1QjExajdVTXN6TG5oSXliTzhCTzA3UGcwYlZ0dUxSaXc1NWsxMExiZGsxYURWZFVHNmVlSkh5d25aYnF3OE82dnlucS9IM1pQZ1plVHlhb0hWcU5SYVNzTm13MEpmSmE0VzUrcldPbU5yYmNmSjBuRElsYVlOUFVFNFplZ3hweWREVG42WHpucmZlcmw2THA1T1I0dS9ua2ZWc1B4R25lZTZHeXBQcjMzelpCck5YdGE4blBWMGJTNTlTMlNKcGJ1VDA5bkd2VHh6Yy9SMG56ZnRkbjE4K1B4L3RPMmd2eFJ6cm93U1pwMXJPcTUyamtWM25UeDFYa1l0b00zTjhuUlFMOVRXUHIvQUQrQUVvbVRuR1AxK3MvUlRVOC9XRmgvLzhRQUloQUFBZ01BQWdJREFRRUJBQUFBQUFBQUFRUUNBd1VHRkJJVEFBY1ZFQkVXLzlvQUNBRUJBQUVDQWY2ZmhKK0g1S1JKbVptUmtabXoybVhrVDUrWG1aSDRaZjc1R1ptYlRjYmpjYlRhYkRZYmphWit3MmV3ek1qUHpOaG43RGFiWlhtODNHNlZ4dGxjYnplYlRaNXhqT3J4bFVmNTdEWVpHUmtiK3hiUC9mS1h3bWNwMTlYcXlWa3JCUTBWZkppeWlhNVVnckpTVkhWRlBiTEpzbDg5M3ZsYjUyV1NrWnpuN3ZhWm41RzhObG5zaG1UazNwTlRZN1J2bmNiaTBXNTNkcWJjM1pYa2tmenpQenk4NVdlNDN5dmxjYnBOZHVUSGFMbmNMcGQ3cGVMMzZCZjczZTc1ZTdwZWs4ZEh2OTh2VGU5NXROc3JqYWJmZDd6ZWJ6ZVdDMFdleVdTeVd1eVdpeVdpMFd1MmJ6ZVdDekptVFpaTEpiTFJiTFVtQ3lXZTBXaTBXNU9GMHZGeVRrblN6Sm90RmszbGlUVW1aTkZvc2xvdEZzdGRvdUZ5VGhkTHNuUzhYSk9TYUxSWkxjbXkwV2kwV0pNU1pMWmJrNUoyVHNuWlBGMlRwY0xaY2syV2kyV3kzSnN0eWJrNFhaT2wwdXlkazZXNU50TzFPMlBLN1ZqMVdoVys5cnkwQy9MUU9qTFRscFMxWmFrdE9Xa2RIdmIzMkpqOGpzZkc2ZDIzU2k5ZHBXOGhoekZ6VHU1aFp5L1A1K2R2OU56bCtOdDI2ZG11ZDBiL0FQMUZuSzd1VWFibkhkR2pjRE10ZVc3SFVscnNOeWQvUzE3YzYzMnBha3RkbG45UmZWazFQUnMwSjZjdFU2ampPWnEvdldiMVczRFpHdWRaM1hueUJYZGF2VVBtdTJOMi9WdHVEc05WN2xFdVRyNjFiem0yMU9SbkM1cXV6dVIyWDlHYjBXbE4rM2tKNUZYWlBTWXU3VE9vTmY4QVN2WXJiMHR0UGNhMHErUklhRzNyNWVweTNrV1h6RlRRdWFiMUx0NnZrWTVYM3RYYlcwdjBZTjE2OWo0ZlkwSmF6V2lueUp2ZVNmWTFhOVNWNFM2V2Z5cWU2MXU2V3Zaci9zVXU1RitsVEI2amR6K1RhZTJ2dTkvVWZyYmMwTWo0MWd1cXdkek9jeTVNeHUxaGh1ZWgxVURleHBabjE1VHlsN0JXNVUybHcxbmlOdkZvNUx5cWV4azhkMmM4L1p1WnkwNW0zR0hKVXVUOFRoenZBR3hwNmFyQ2ZQYnZ0Umo3YzIrVS93RC94QUJGRUFBQkF3SURCUU1LQVFrR0J3QUFBQUFCQUFJREJCRVNJVEVGRTBGUllSQWlNZ1lVTTBKU1luR0JrYUVqQnlCRFk1S3h3ZEhoRlNRbE1GUHdWSEp6Z29PaThmL2FBQWdCQVFBRFB3SC9BQzcvQUpsOHZ6d2h6N1FPM3IyZGUwZHR1UFoxL1BQNW83Yi9BT1IxWFZTU2VEOTZtYnFQb1ZmMXdudHp4dCtxUE1mWHNQVDZLNC9NbDEzYWt6dENGS2RZaDlFZmRVdHNuTStpbTlwcXFoNGNLcWo0NDJsRzNnYVBtcEhEVm43YW12bmEzUEV2MWk5bVZBanZ6TitZVEdIS1EvSk1QSDlvS0p3c1doT3YzU1ZNZFhKMnBlZm9tdnlEeW1ESzdsbms2M3hWOVpGRXpWcFB4V1Y3cGhHaWhkbmY3cGx2Q210MWI4MEwrRkE5MFdVV203ejZLbDR0Vk5md09zcU1qeE9VZkNWeXQrblA3S3VQVGZaTnQ2YjdMbE9uczBsVC93RFVYNjF5UEY3dm9tRGluRFIzMFhkdGRxbHpzR2ZGVkI5WW9IT3lhYzFCZXhVYlRpWTc3b2M4a0hPdGZKRFV1dW92OVRqeFhleUgzVEQ0bzdLRitoSVRTUFNoWlh4aGUrRSsxdzVTSFNRS1Vlc0Uvc3R4VHVhZC9zcDV6dXBQYVV2QW8yOUlWSWNzWlV1dU1wNHYrSjkwZE40aVAwaU5rZEFuRC80bldJeEthOXQ4cFJyTDlWcjNtbytxUW5OMGtQeVZ6NGo4Z3YxNVJzYlNybTlEbXY4QWRrRnlLZDdRVHVZN1J3S0h0SWUwaDdSWEp4K3FQdEw0ZG52TDN1eHFIdEwzMFBhWHZMcjJkVWV6cXJjVjFSNXJxanpYdkxxanpYWHM2b2MxZml1cTZvODBlYTk1ZFVSeFhWZFYxWFZlOTI5ZTNxbTgwT2E2OW5YczZvODExWFZkZXpxdWJrT2FBOVpkVjFYVmRWN3lIUHNLdnhRNXJxdXE2cnFoelFYVmRWMVhWZFVUeFJ6dTVkVVR4VnVLNUZIbjJkVjFYVmRld2MwT2E2cms1ZFYxWFZIbWp6UTlwRVI5MS8wVHQ3bTlCakM2K1FDODR4NDhyRkFOSmE1RjdDWEhSQ1FFM1ZQUXhiMnBtd3RPU0RveEpESmRyaGNFY1VQYXo1WFE5cE45dE10NDAzMmtPRGwxUjVycWp6VGlvcUZoWnM1dU9Wc3RuQ1Z1VnMxRnRta05YVDZZN1poYnZ4bXlxenRFQjFXL0R2Yld2d3VvWHV3UnlDL0pPY3pDNUFPdmlWNG45NzFWVVFPZkZFMXV0amRTQnVHV2xCeXpJY3Y4T20zVXRuRm1SQzJ0c2ZaMFhtakdTNG5rZmlOMCs2cjl0ZmcxZ3dnUms0UVBXNXB0SFJOcDZtQjBwYmszQmwzVjUzQkZ0S0RFemVEd2txTjJzMlhSVUlqdFMxRHJuMW5zdFpNcUh2OEFQYXc0Ylhia3RsZ0cxUzg5VnM5ZzlNVnNvT3didDU2cWhhRGVuZHJsWk5hNGgxS3djczFZbkRBelBxcWt2eE5JYlpQbHFLbDduZnBmNGxUUTdLd3hTRWZpbS8wYW5qS2R4ZW10bjg2TXJiQjk3WFViYWpHeVVlSlc5ZkVGSVl0NjNUNHFJeDRRNnpyWjNLSm5mYlB2TGtRbXlRUFpmMVNpNmdoLzUzSWlvMTlSM0RwOEZ5Wng1ZjBVektDT0V1eWFNZ21GNGJESmYyeUZsbjFXNFlHOGNJVWRXM0NKTjJXNnVMc2sxd3d0MmhHbnhIRGIvdWNiZlJUdGlGcEFMOG42cHd2dlpia0RtaTBXSk4rQXVoTEtlKzdtU1VDK2F4MWZyOVZ1YVU0S2kxamN0Snl0WlV3T2Nqd1BkT2Fwc0Y3Y2ZDWEtubXVSU0hMeFdWTTk5bzRtY2lYeVcraUJvNVpvQXdtTnd5MUF2ZjhBa29JbVlwbWd1SnRoakdZVEdWTWJXdzRteVNOc1RkVEdwZTB3dHllTkJ6VlBESzk4ajJ0ZTBXRFhOeVBORGFNUWJSMDVjR3lISURvcElhc3RmQVdXWTYvZDZmQlNYOUdmMlA2S1Z6VERpOFBBbFJ3c0xDSEZ3OUhpZC9CU2I5Z2huQnhha2pJRlZUM1lqT3pUUVhVa1ViNDk0dzM0NXB0TElKRzFFV1BsZXlxNEpodlpjVm05Mnpqa25UUkZrcE51WmZmTk9vcHNiV05kbGJDNTNGU3pUbXFxS3JQeERDYmxSZUxlVEFsMlpPRThGVE9qRWtBZVhPejd6aG5wL05WWUxYUnhPT0kyd3Q3MmZ5VzBhYVF1a3BYZ2Noa1ZQTElJSVluWXVaMXNqVHkyTlc2OWg0aFltNlpHeVZrTUc5ZStVV05yNGJYdW5VRWRSUzFrYm1QbXd2anVNc0l1b3FpY1lKekNUK2tmY04wV0FVVHA1MjZnazMrS1krcGxlMlc0TDI1L0pSUmJSZlRWakJ1OGJnQmgxTjFXMHRTWHcxaFlMbkMwYUtlcGUwc3FienVlQmtiQjE4czFQRk5MRFhUd3NsamRtM2VhODFTd01Ja3dGOWhoTFpOUWpIYVdTZG1lbWFhNTJGMVhHd0UzeEVuK0NxSXI0YXZHQ2JZbUhWU3Q5ZklMRzdNbFBZRzRwMm00eXNicXFuZmd1NGdaMkFWSEhNOGVkQzRkbTBJenhTaWxna2M0UmtXYTI2MmhGSGlxNk1zd1BzZUJ2eVJuTWJJNGppWmxtNEFGVGJPcTRmOEFFbUhFNGQxdW8rZkJTUlRWVlRTU0F1TGpaNEk2RFZUU3hSeVZKWVhObEJ4dXNTM3ZEaW9ZdHVTdWs3N2hrTDVpeXBvNUREVVVqQkRJZnhBMXhHbkpVZGRUeitiM0xXeDkwdUF5dWRNbEZmQmNIdlpndnVuUlZHR0h3aG84ZHIzVUVELzd6TGhjNnpoWUwwOVMwV0xuZmh2d2pNQTY1cHRTKzFjNXRRMitiWE0xeVd5L1BXMUZJKzJCelhHTUVEVFhVcWxxZHF5elJ6Z2lURmh2MUFXeEk0d05vU1JZZ0xXTGJsVFRkMTByaU9GMUN5VUY1Slo2MWpZcUtSaEVPeW9KQU5jTk0wMlZPeUxlejdGaFkzM3FOdVNwNnFlU2NNRElyNk1hQmI0S2pZKzhUbkJoMDNtdjJUMkVPM2h2ekNiZkcxeE9lbGxOU1I3cGp6YkZmSjFrYTdaYm96STh2Rm5YYy9rcFFkNDZNRUEzNzJpRG51RGFVa1gxeEo4YkNJR3VqSnljNXp4b3RubllsUHZwbk9Fc0hwTCtBNHRBTEtsZWJSMWtoRnYrREovY3RpVThEekZIT1pKQWNudWFBUDRyemVtZEZEVnl3RDFXc09xWkRLMTdYWk9OM2RlYWdsbU1tbHh4VkZVVFJ5TkFmYVBtb0hOd0FXdG9Bb2o0T2ZKYkJic1Z0VlVWRHpPNmN0ZkMxd0dGdHNqb3FEYWRiSHNtaDJKdjVIdnUyOVFHdWQ3b3ZrZWdWRnNYemlqajJReVlTeU52RVNYVFFPYnJjZ2FIb1ZESlFObmUvcGg2b1VkTVA3T2xMWDcyejhZdWJLRjlXK2liRFQ1Tk4zc2JZL3Z0OWtQTjVZWDAwUXh1dTJYTzZiVE5MSFJOZDhWSlZWV0o3aFl2NzFoa0ZUc253Yk9sZVlobUhsbGlTcDN2dEdYSG1ITFpMS0lOaEw5OXhiZ0dGdno0clorMFhicDhoamUyMThReXpVTUZMSXo4ZHIyRElic1pHM0s5N2ZLNjJwVFZEbXdRVHlONFBaQSt4K3lxcE1YNFJQRFJReGJCa29xdWRySjJNRElyTXZrRld6eGpjVXo1RHhkZ05sdG81UmJQTFA4QXhsU1J2L3ZGTklHODhCRmxzK0tJTzd6ckhKUXlTTlpHeHhZNjF5Y2xGRmxiZ09XaThtKytkdHhWQTBFYm9yTDhuREpHeGliYUdZdmlBYmt2SmUwYyt4cStXUnJ0ZDZNNzhMWlpyekg4b096b1pvWm1FejJ2SkVXZzlQQ3FobE5WT2hubEIzNXR1NUhnK2tQc2k2cEJSUzFZeGw4TTQzZCtXV28wVDZqYUxZcmk1WmxsWm8rUVcyYW1obDJ4czQ3NXdkWmtiYk1EdWVaY0xMeWxjRGkyTzYvL0FGMlpmK3kyL0Q2ZWdkbit0Wi9OYlRnWmFTaWRycnZHL3dBMVViUExSV3d2WmowNzRQN2xTMGtUNHFtbEV1TDF6a1FGUHRrSGFlOUVkT0hXdGZ2UFBJZjFUdGliT2NJS2s0NVhnZUcxcktxcWFtYVIrK3B3OXVMS1U4UGd0cTFaWk5IdENxYnZlOW5VT05zdVYxU1QxKzVtaVozeTUrOUxMbTlzUnlXejlrN0tadEdUWlRKTjg2d3ZiTUtyMm01dEJUeHhVakg2UGEzTU5WRkdQTk5uK2V5RE56eldsaHQ4TUtkNVRiU2syWlcxTDRodWkrUEJFMTN5KzY4bXZKaGpHMW0wSjZxSjhsdnc0Y0dCOWw1Sk1hSTJ5MWZ3d0JiSHFnSFF6T1p1c21Fd2p2QzZwSXRveHprQ2FNU0J6MnZaazRYMFd6WVdOcElQSm5ZNXdqSU9vU2Z1U1ZSMEQrNzVKN0Z4dHpidTZMQ2ZrZUMyelhWa1Q1S2VPUGMxYkpRNks5OGpwbXFpdG1taWxrRzdlN0VXRnZXNi84UUFKQkFCQUFJQ0FnSURBQU1CQVFBQUFBQUFBUUFSSVRGQlVXRnhnWkdoRUxIQjBlSC8yZ0FJQVFFQUFUOFFXTE5SREtOTVEzY1pMTGpnL3dBUUVHMllWU3FWMlJzeTFMOXhFSHVQNGlFbUdHSWlWa09TMFNQOElsdHdnYWlvUzV1VXdxWmRiTXkyRVhNYkxGTUMzYVFXNmhqeTZpVVcvd0NJeTh3ZTR5R1lMR2VVM1poc1gvQVhlcHh0RWJUY1Y5TWZ1T1cvazNVeGtSZm1NM1RNYmNyUkhTSE5EMmtncS9PTEw3bTlNQVhYM0NBbDNxRlEyZUxZbGFIdlNPZzFScU5BTHBuc3VVVkRFVkEyL0V3eFBVZzhqNGxGSVR6RDFRaGREcXlBb0w3STlMYiswdUVJT0dGVWc4QUlOWGg4a1l3Qy9ERjFEeGtpTDYwbW45NFBXYTBoR09sNGlDWWZVR0ZOelVyS0tlb2pmOHBuQWp2RmdSZTNxWGw3RFJBRDl4TFlzMjFVTXJiczV4Qko5bUM2cXM1WWdOUWEyVkZXMjlPSXUzdFRRYmxkRlBDUXFYeURtWUFMNGxoRGVVaURoMkJOYnAzY2xlRTFNZytLU1hGdFJhaTBpUFhXak1STW80VXlyUmoyak1SNVZDUjFYSHRnU214a0htWjRJOEZTcUJJdE5rYVcwT0I1dVlBcDgyWEFxL05LWlZHdlpEcFl1eURWZ3ZOT1NVdG1MaGtwOHd4V1BxNC9zV1YxMkZXeXdWZlZ4QUlQcGdySHh4c3l6VTRCNlQ5Z2w1RWJKTnpDdzZrVXRFdnpOK0RBNHYxTmxTL01HTkV1Q3lKNmcxVFB1VWFla1lwcEtzTEE3akpWQys3amVGc1d6WktxblBpWkZKTldIczl4V1UrcG1QdXBjUnBkY1JVQVBlWW1UeUxXZlRLMnZzcGxsdFBWa0s1TllzbTZ2NkpyQmc5MWlPdGp0VG4rTkN1Q00wa0l5d2R0UHBsSnBWN2xKcC9jck53YVdRSE5kdmJLeEErMEl2OEE3QnlmM20xZ0s0V01GUU8wWWhhUDdnNmpPOFlacjFqdk94VWdsdDRiU3gyOStZU09jU3J2RXFvZGN0bkR4RWx5dW1FYkplcE5TMnAxTTdvenM0ZGlCa3duTzZSb3hDdDNITE9vaGVNNHNJMlQxZktMYVNJV01JR0NhaTZ3WTdjek53S0JGRXEyc3BrR0lxOFo1SnRqTWhLcTAza0p0a0M2WllzcVdleVZNU1EzSENUaVhhUG1mcGFtMFpjTW1ZRnhCYVFJdGplYzdQOEFqN3FqYk5JMUlia2kwd25OWUFJUVpaS3F5SmNLTlJtSWJRck54QjdUalQ1SmxYZUtjc01HU25mVWpTc2tEVEJYT2pKVXBHeUZWL3RtNGhIQnJGV2t1RzVBeXlzR3pLOXhRdmJjMjczRkVsU2hEWkFjVGVLaDdWRDNEbXR0eit4SGNHQ1loUlZhWGp3UWZvR1hBNllzQURRd0Z6TFUvYzQ1bGhjTzFPYkFjem9mMzl4TWtNV0hiV0ZmdXVGYUZLYjJRTUdxclEzUTZ0N2g5OHhJd1BoUEJMNlRhdllrWEtUZUtvSGxCK01CME1pMXRhNUpmQXRLZTN1cWptN2hGcG9ZUFp3VWFnVVlFcHg4emFqZmdWZ3pGcVd2U28wTjdmVVI5YXRKUXlacVpTeFJWcGd6NWp0MEVLQkhJWnVKNEtVTjIvTVRDalNBUCt6SmtVS01ZL1pTTkRDM0VXbFdsZ2JPSjRFQllTVXdlRk4zM015eDBHUC9BR0t3YnNmTFJQWk1Sa1pmbHhwWHhTeXhQazJTWExOQU5MZTdoNTBvMFp2Zk1RQ1dHeUFoS044YTI1TTlRS2VTL3dCek1HUjVHQTRkRkRCOFRjNEhnOE0ybDNQbThZT3JSWFJrTGNxMDRNczJvQUJveTE4NWpkdnJmSHpMT0syTVlDWGxBdHE3ZjNjZUE4aXR4S1cyTlVqbmVFYk9HT1czRmNTMXZTVzFOdUJqQStvdm5UcG5nQndhRjdZQ1cwME5OTVpPSERsVUdPbTRvNWRiNVdhbVp0VERoWGh4NjFOUVRkdDRicGk5eEVEMnZBQzg1aGNZYkxZWHdwbi9BRkZWekNoNmkzRUkrT3JIS2w1OU9IY29OejBJTkxYQk5ja3hsUUFtbXU0L25FRWhndFhaR05nRWdyTGtIOXd0Vy9wVGhnSHh2eW9NV1JSdEpuRnEzSExvMDFTVVRES05hOUJVcXB0MjZac2hZZ1BsZFAxQTROb0J0WS9jWUhYSTlsUjQvd0JYQTMvcVhJaVZFSUxUbkNjWml3TDhOUjJ3RUpwYWdncVM0MUJQSE1GTzFWbEhzM0xjQnhadUExT3JTNkhndk1hbWhheVExaXhJQmhiY285akdiK0tqS01LQ1F6eGRHWUxKMmJVVXJPT2JQa2dHMXIxY0NycStNWEJic09aNk1hY0ZRcTJBNHVvSGhpc0JhSzFDMHBtTExEWXE4eHcwYys4bFJ4akgyUVFpSXNQa3ZjT1hDM2JjY3czRUFmcy9rTXVQQmxWOTVxY2xrNEhtYzNFZEc3SkhocUNjb3F4UmVXcGI4Y1dMUEdTYkdEQ3JhdkQ1dUl0aDliSWhTVlhyanBEdEExRDF0ckZlalpSY0prWmVVd2VWcXpjSStlRFFTSzYzM0dDeDNRZ3VqMFFaVmxZUlhTYnBhM2pjVmEyR1k3QkZxZC9FMVpnTDVkOU9tTkdwYXI4cm13NnhBZk9BTmdwOHdhcGJYYTFzZUdKWjBpakYvTER1QmQxUzRHbUR5RnF4TVpKTXpnRmVZakdXem90M21zZjZ3Y0RCMlVkRVpua1JkSE5ibE5laCt2YXpIV3BWRlp2aTRWRXJPVUYwSEI2NEpXcE5waitBVGNWTlliOFlqV2hVcTVkM2VZekJhYXl1cTJjYWliODUwcE9mRGRRN1Y0TDJmSkcrVFJRZmppWHVucWE5V0I5OFFqSWlzSll5dkRudUFWZExZZlZxQ2ZwV2FCckNndStJUjAyeG5YQzJaWXRkRFU3WXMzeXR5a1RSRVdxdVd3MkVWaHZXRXVHUW5IQ0tWWGF4UlJGTWhCeTZtRHpYeFRUWmMyZkVYYXM1OEcyaU5aQzcxMUFEaGdwMFZCbHZSMldRNzk1QTJDeFFqMG5KblpLWEpWZXRRVis0Z3FEbkZ6Z3RoZFdrNmljVTNtODd1QldNN1U1NWdxV2F1R3ZPUFVYa0U3RjJMYWxYeXZVcjNFZW1qUmJCS05yV1kxRElObGxzbFc3ZW1QdGZDWEZ5Q3laTmc2bUttcTlabmt2WkZxWWJlK0hrWXRUZFVaZ2ZkWXVPYjViRVJicE14WjE0dFoxa3F2M3hCeFdhVHFONnpHNm5TUitadXp1Nk5uNUY0M2xpeXFpeEhxTkQrOENwVGFtWHFQNFoxTDdpWDlRdkZ4NWxvVTJYVmR5dHV4NUZPU3YwbGRwc3pyL2lIUmQ5STh0czNlMndXM0RrMTBocXE2Sy9MekF2NDFDdWhaY2pVTnlXdXJ6eHpsTTJvR1AvQUFsWUFJb2V2UExNUjlwRG5vNU51eUhxTEYyUzRLUmZPSWwza1h0aWdLUXQ1cE1QbEUyVjVSYnhaQ2xxUzlkY0NNeWhDSm1GS1dGV1dQRXJrQjVMcXlPMGVjUlBIYkFEQzNRV2FJQk1kcUx5WkNsMFhMOUNGOUFWVEYxN2kzd2xvRUdpbkZydU05QzB4WWFCYmZmaUZSQUFOT3NYWGR4ak1DNUxHNk4wa1hET1FKWllJb25TUVkxby9QYWZzeFBPV0l0NFFNcHlwNjYzRFUyZnNlaHlxNVZ0dmJQL3hBQXNFUUFEQUFJQ0FnRURBd1FDQXdBQUFBQUFBUUlERVJJaEJERkJFQk5oRkNKUk1rSnhnUVVqc2NIaC85b0FDQUVDQVFFL0FEYU5vVkNadENmMFQrdkk1RXRDYkV4Tm9WdjVGZXhNVFF2ck5ISTJjMGN6bXRISVRFMEt4V0toZlJKaWVrSm9URjM5RmJRcVFub1QzOWRtL3BzMmJFK2pZaE1ubzJ2cHNkelBzV1NHSzlmQXNpRlp6UWtKdEVzbGlQdXM1N1FzaDl4bk01b1Z5eFhDK1NjcS9rKy9JdklqUXZJRjVLSDVPdlFzN3BlaDIwVGx0TWpOMTJMeUpYd0x5bC9CK3FwTDBMeXFhSjhnWGx0SDZtbXZaOXcrNGZjUHVuSTVvVmZKejZFNkZWQ3F5YXJYb1QvQnY4Q3Avd0FFMytEMmFRdWw2K3FVa3dpVmpmeXlaeHIwY2pteFV6bTlkbXhQU1pOOWFGVmFKZTE3SlQrR0xraFhyNEp5dE1XVVdWZW1oWkoxNkZsa1ZJMkppMExRdEV2R0xRam9YRTBLZXZSdy9BbzdKeGloTVVMWW9qWG9VVC9Bc2IvZ1dQcnNVSml4dlFvZXZRc2Y0Smo4RTQyTEN6N1NQdG9XSmFKam9TRUxaSWhDUWtoU0tXU2hTSkNRazJLZndRbXZnWDBTRkxGR3hZeVlRb1NGQXBRcEZJcEZJcFFwUXBGSXBFaENrbVBvaVpGQXNaT01VSVVpa1VNU0VoSW1kaVFrVERGT2laRWhMWXNZb1doU3ZnU0psazR4WTBUQ0ZBcDJUamJGaUZDU0ZJb29XS2hZbUxHS0VoU2toSVNFbUtTVm9TRWhTeVlKalFwRklsdjBUaVpNSkNURWhJU0VoSTRpa1NGSW9GQW9GSW9KeGt3aFNoSXd4dWlvWEI2UkdQa3lzQ2hMUk1qalQ2T0hFeFk2dDZSRWEybUtHTEV4WVdMRG9XTDhDeEN4Q3hDeG9XSkV3WVArT3ErN2ZXdmd5ZU84RmNhSnhjdlIrbngvWjlmQXZGYVcyUmlVdmFPSkVhb25ETnBObjZOZkRJeDd5TFl2R3g1c2ozMFkvRmpDK1VsK0h6dDFMMFk4WEYxRmZBc1ZHTHdyM3UvUjVHQndrb1JPTHlIN1JPRE14ZU41SHZaK2x6UDB6OUQxL1V4ZUF2bHNudzhTV3ZaalhHRWpKQ3EreDRWL2FhM09qZzNHaFlsbzRJV05rSmNFSkNqalNaaTZ5djhBd3Y4QXl4K2hIQ2VUWW8ydHRFamxiT0FvMGhJVW5BbU5uRFNJWFE1NzlDazR2MGNmNUdub1VybnBqbjkydmdsYmdpRlVsdzMwUStOdmIrRGZYMDVKTVVFaWFFMGIySnAvQWxwRGltdGFaOXZVazRwU09QN1JjZmtYQittYUpRL2UyeVV1WElUMnhQU2FNSmxtbkwwWXNhYzkreEtrL3dBRXZhTlVpTFhwQ2V4SmVqajdFbWtkSS9DRlVwRzFTYVJMYVhvVmZnNHJhYk9NNmVrU2srMml1MjlGUlRYN2ZaeHlOUDhBOUNUM3AreFJ3M3BrYW4yZTZNaWJsOFhvN2NleUU5SmJITHZhMnpXbDBqclJxRjdacVYzdGlYSk00a3pUZXlWMGRrdDZleDAxOGs2RTNyU1k3cHZXeEt2aEVOdGRvaTNPK3llKzJWYU9XMzBKVjdOUFd0RXUrVFRYUldTY1g3cWZSSEttbXY4QTR6OS8rakJVeXV2WmpXRzhqbEZhU2V4Ti9BdGUyYTE2Sk51V1FLT1d6V3VtVHFTRjhvNUk1STVhVEZkdm9UdEp0aXpaSDJrZVBreHR2N3UvOUcvQzM3b3o1c2NOZmEyMStUejhsVjROOWFQR2YvVFArRVh6eFpPS1ppYnBiWkdLMnVVc2M1WC9BR2lpOWVpSTN0TkdzU1hveDM4YUpUYUpmdlpHVlN1dXRFMDMybXhlTk9XKzJaUEc0UW1teHhhM3RpalBQOWJYK3RqM3Jvbjd2MitUUnplaUxYYUhhMlM1ZlNMYVhSZU9jc09IOG1OS1Z4WHdmLy9FQURNUkFBSUJCQUFGQXdJRUJBY0FBQUFBQUFBQkFnTVJFaUVFRXpGQlVSQWlZUlF5QlhHUm9TQlNnYkVqSkROQ3dkSGgvOW9BQ0FFREFRRS9BQ3pMTXNLTExGakVzLzRMaVpZeE1VWUdOaHhMTkZoUk1kRmhLd2tXUWl3a2l4aVlkUnhFa1dHaEwrQm9zT0NNV1dNU3hhd29NVVN5dDZQb1dMZXFRNGpUUjFNUlVweTJoMGFpWFFVYmpnMFdNVHA2TkdLTWJpcHc3TTVjZkpHS0ZFVVJSZHRHTXh3cU5iUnlXZlR6UHA2ZytHWjlNTGhVMGZUeGdRcHF3Nk1iRXVHOE1mQ3k4bjBiOGtlRlQ3bjBrVVBockM0VDVQcG9LK2hVT3dxTWt5TUpkQlJNUEJpWU1VU3lSaWpCV0hCZVJKZVJKZDJZeDhtS1MwL1ZSdjNFdmtTVmh1WGtjNWRyR1ZYZGtpWE5ZcUlxYkZUTUVLQmdZYUZBYTMwTHg3b3RFd0hTdWpsSTVPamxNNU1qQmlnT0RSWjJNWkRVeHdhUmFWaXpMRm1MUm9UUzdsOURiUGNXbFozUGN1NG5LL1V6U1JsM1JkcGRSelhjeVhrVFhabVN2MUhNNWpNeFMxcGpxTWRXYWZZU3VZaVJZeE1ERzNjc3h0SWJ1STE2TnhHNGpmeWFORFk1SkZ4c3k5SHBDYkUzNkl1WnNjNU11eEdTTTBTa2h2MGZveHk4RFptWm1Sa1pHYUxveVhZekhKbVJreTVtaHlaZXhrekl5TTBPcU9zT3JjY3k3TG1TUTVqbVpqcUpHY1ROSE1NeTVjdjVNa09hSFVRNW96Yk14ekhJdWJmbzJsc2RRY2h5c09kaHpIVU5pTG96U09hYzBkVWRRemJNaHpTSE5tVExseWJhaVFtMU5iSjFiSnNwY1R6YjNKVFNpMm1VNjBwSjNJMVZPNVVyUXB4dTlJZFM2dkZpcUk1NXp4MW1jM1hVNXB6RG1NNXJPWXk3Wlc0K01McUhVb1ZvMTRaUkhKUjZuMUZUbmRkWEh4Y1hkSWxYYmpZVlJKazZpeFk2ODZkMGhjZksxbWptdU5LVm4yUHFxdENqcnlWT0xxVm80eTZGSGp1WFN4a3IyS2xYS01ha2RYTWtWT0xnbGFKdzFXTTd1YkhQaGxmYkhWb0x1T3Z3cTFZWEVVUEF1TVMvMm9mRytJaTRxVXBiMFRkNXQvSlRuS0ZMVDdsUGlaSmU3WW5hZC9remFsYzV6MktvK3FPYXJGU1czNlJxWlFhK0N0L29yOHlNZXBiUm5MQkxzaFZPMXp1Umxpam1hMFpNek12a3pSekxMUXB0czdpKzNxSnBKdHN5RkxKTVVsZGprMVNjdmxmOGlsN2ZrazNrVG5LTHVtVXBwZTRxTE9sZGVSYXY2UmhPYTBLZno2cTc5Tng3bVJHVVA1bCsvd0QwS2E1bGgxWk5zVTIyN2xwTlhSS0ZTS3UwSk43U0hwTVgydEpYZWlUbW9PRFhlLzhBYzFHUHlXWldYa29TakdTeVdpcFY5N3QwRmc0dGQyeVN4YnNSYVphd215K3IyTWtkZlRvWU5rSU5TMnljSUw3WlhNYkp0c2hLVWs0cm9PNldMZllVcHBXVDA3aTlpSVY0OUpiaStwS2RMc0o2Sk81VjJSZitHVWNjbGtya2NFM2REbkc3ZGlGV05KM2FYOXhNUXFqL0FKRis0cXU3WUw5eDZaRWt4OVdKMk9wU296cWR0SCtaUzBtVGhKdHVhL1VTcHFTa3RITzh5L1lrbEYrMTZHcWN1cUhMRXVyN1k1VTI5bk5wS0ZrYzZDZnRKenBjdFNpOXZxdkJTenJQbHdqZGxlckRobTFKWHY0VjJ2MUVpckdVbnZwWW5LdENqazJVMnhwTXRKdTBSeG12dUxOdXlHOVdzTmtPTG5TZG85RG5UKzVPeG54TTRQdW1TbmVOaU1KelRhVHNPbFY3UllxRXVydmI4aVVLTVZmWTNFK20rUDNSWDRYaUxKMGwrckhSNDJLZjJuRDA1dUw1dlg0UHdlbEdQNGhCN09LVjY4L3pLU2hPbGtWVW9hUk90Q1B0bVFxMHJibHYrb3AwK3JaR3E0Ymd6Nm5pYi9jVGxVbks3WTBuSW5DeTZrcWJ4dkxkeHdodFdSSGpuUW8yeFR0NUtYNGpHZFZxZE9MRnhibXNGcGZCUzRtaDlzTHZ6bC80Um1vdDNiYS9NcjRTcXVOTCtxWW9WbXRFMXhEKzJ6SXdxNCs5V1k3OVNsQ1V2Y2tVcTA2TTFQeHMxVWJrKzUvLzJRPT0iCiAgICAgIGlkPSJlIiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgLz4KICA8L2RlZnM+Cjwvc3ZnPg==", + "description": "A colorless and odorless gas resulting from incomplete combustion, particularly in motor vehicles. Results displayed by combining the latest and aggregated values and optional simplified chart. ", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'carbon', label: 'Carbon monoxide', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'mg/m³', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'carbon', 'mg/m³', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Carbon monoxide\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"mg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#3B911C\"},{\"from\":5,\"to\":10,\"color\":\"#7CC322\"},{\"from\":10,\"to\":25,\"color\":\"#F89E0D\"},{\"from\":25,\"to\":50,\"color\":\"#F77410\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"mg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nif (value < -10) {\\n\\tvalue = -10;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"mg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:Q08tdmFsdWUtYW5kLWNoYXJ0LWNhcmQtYmFja2dyb3VuZC5wbmc=:Q08tdmFsdWUtYW5kLWNoYXJ0LWNhcmQtYmFja2dyb3VuZC5wbmc=;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Carbon monoxide\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:molecule-co\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "carbon monoxide", + "CO" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/co2_card.json b/application/src/main/data/json/system/widget_types/co2_card.json new file mode 100644 index 0000000000..3f8260de3b --- /dev/null +++ b/application/src/main/data/json/system/widget_types/co2_card.json @@ -0,0 +1,40 @@ +{ + "fqn": "co2_card", + "name": "CO2 card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest CO2 level telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'co2', label: 'CO2 level', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"CO2 level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"co2\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3FA71A\"},{\"from\":600,\"to\":1000,\"color\":\"#80C32C\"},{\"from\":1000,\"to\":1500,\"color\":\"#F36900\"},{\"from\":1500,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3FA71A\"},{\"from\":600,\"to\":1000,\"color\":\"#80C32C\"},{\"from\":1000,\"to\":1500,\"color\":\"#F36900\"},{\"from\":1500,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"CO2 card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"ppm\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "co2", + "carbon dioxide", + "carbon emission", + "carbon footprint", + "carbon output", + "carbon pollution", + "carbon capture", + "carbon offset", + "carbon reduction", + "carbon neutral", + "climate gas", + "fossil fuel emission", + "carbon cycle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/co2_card_with_background.json b/application/src/main/data/json/system/widget_types/co2_card_with_background.json new file mode 100644 index 0000000000..19d8b82c49 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/co2_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "co2_card_with_background", + "name": "CO2 card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest CO2 level telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'co2', label: 'CO2 level', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"CO2 level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"co2\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3B911C\"},{\"from\":600,\"to\":1000,\"color\":\"#7CC322\"},{\"from\":1000,\"to\":1500,\"color\":\"#F77410\"},{\"from\":1500,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3B911C\"},{\"from\":600,\"to\":1000,\"color\":\"#7CC322\"},{\"from\":1000,\"to\":1500,\"color\":\"#F77410\"},{\"from\":1500,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMwAAADMCAMAAAAI/LzAAAADAFBMVEUhJx0pMygbISMvNyklMCcpLiEgJycTFBYYHh0dIR0yPi4rNiwnMC06QCRITi5ESytKUTEzOjE2PCc7QS8uOi4lKSNNVDViay0/RS44Pis0OStOVC4iLCpQWCorMyA9Qyk0PzU/SS02PSE4PTFTXS5ARjVHTidcYS1GSzZQWTJFSTAzOCQhLSM6QjUgIyQ/RCAVFxwnKyldZihVXCVLViUdJyMxNS4mLBw6RzEuMidhZjFLUShDSSNdZDZibjhRWDg3QipGVDZiaSNXYSlCTTJFTiB3fkczOR5KUDlnaz5VYjdYZDFBShw2QjAwNRwXGhZaYkRCRihMVEBZZDspMzBmazMwPScRGhg6RzpBUDhaYSJweDU9SzMsNzNoc0NTYT9BSjpdaThVXTMwNSJQWR9aXzZQXDtiZzhhbEBudz45Qx0OEhJpbzk4PhpKWTwtMC5nbicqOCQqMBkgJBZ+gzpbaEFYXT5YXS4rLilWWzgvPDZsdCY9RyZyeCEZIBZ5fTtmbEdpdDp9gkVUVy8ZGiNgYz2NkzlvcjlqdDEYJRxpcB01QzpKUCBKWjRzez14fzJcaDB3fidQVCQuPB1FVT8/TiZWXhtyeixGUD2CiiiTllJJVy5FUyszQyWLj0JSWUFmcDA5SCl9hCQdLBthZ0R4gz6Bh0EkMx1LVBxvdEd0fDWMkE2FizxPXzVCUjB8gU2IjyFwdS0zOxZtcUGCiTSGjjJ+gjEmJilxekdrbzCBiRiGilFCTkCFjEc5SSBeZRx2d0ElKhVeaUyCh0pUXkiZnzZGTxadoFeZnUyVmj9MWkVMXCs+RhSRlkWLki11fBZtdRWgpjuepSmXniU8Sz+QmBhkbBSSmTIuQiCQliUtNRWnrS6mqkqfo0iaoRZwdlAlOh5PVxONkFx2flKxs12rrzp6gxioqV17hzBbYhOBhVlnck6jqRtCVSW1ujCrsRyvskojHCWJkBQ4Tii3uRyWmWKfoWS8v0rAv2qtrXBBPTNUTzzDxh7IyjdsWk2IZVelbVVXAACZK0lEQVR42hRX20/zZBzu1nYytrZbu0PbHQotW6HdKGyy2rXIBGch8g2rm9HpEl1BlLB8uCkTRTRORD8XD7vA8yHGGDwkCh6JRkATEiMerrj4LjTemKD/hK9vk2YXb9bf4fk9z/ODzuZLzeZJ4+rBycLCQl+L74lKpTf90b5olHo9QzdKBtZoFA211QKXFq/GIQiSUdU2Olgzbm/UO7Zzoi+FOL1acJju4J1KhWAdDsQDE+282NmfJmlGu90mDIYkSbxSmbnr0CqQLEMQNsuwsKMAfuq6zgxiOEYTyXYERTmsq9uy1Z3unp43eD3c3q9ayZgNM0ZQ+WuRSwyDf7SS4xPo/HzRtFVThaCgGodQA5Wh5kHzpNUolg5UaGFsocTRkk9XeplDUTTEWLuXVKo9rHF1BdraapilotJAoSCqYr3DNUW1fJ/P3XSM9TlzQVeQpX2rk5OxJRxOaTAdZxlSikTEMIZ4PQ/yeQ6GNbgT6eQr+yLmI6VOmMtpvg1/wBPEZZwmZZojKbrewVFbzh6t9fRer7qbvTY5k5yZqf72eDeCa2gB5Xfne9T2uqZiBqbxRuO0acfjfNANoXE0+VsTgkoltWSWrh60tlrXXLOwYDMyoc8r3axY7cWy4Xy1280qmyXe3bdw0jxRzBKogxvlxVi2Ki5tgOCWPyfRpR3SJdxISqFIuyIIodDcE8egfFzlxceevYtCYHz1cB/XYKZQiDxb+eoLER4sUDStMfVVFuRNDLIoKTEsTXPLlY4lw93z6V62p4hra00Uk0nZXtyd5pMkS2ma0u2FJ9dZm+d5jDeU+YaKuoOjbghCuS+vtIKQCgBkqo1Ga2FrYcztjnM4hl09m+91Z6qgOkfTeqnXgLbitgbFm02jqbYgDSVlC3zOiuDd3kx2bnmk/kQ+wTCwPBkRH+JumXzquLJTmbQs/PrDoWd3OJskC5lMF+VIkgqJlfYHJMc4PAGNHnlhLi7TNIPA+ppCUgLsW25bSRmbP0r2OCupZ0T5ESQFs1jxXEm2kzDC8qfFbjVLcCjGcyVFLxom5Hb3ud1u6fP9tRIKFZsnW62S0mhunUAL11zTojuW3Ww0FD6yW42Fq2uNUrJ7orqDkBtttopqK26jmsxYeszSaKs4X318aZ0k5j6fFTxo/YGlkXbHNzXS2VjOt3dWN0M+fSgcYUB1N2M6xVAwS1nbq3fVGdjh8QhMaGS1g8EsGpCL1WkN7mdlliRt2+IjHI9hHJHP6+SEK4UOJqtVjOOQVFBW5ovZva6tKhjfVPiVedPtHBsbCy4P7nzw8u9HkNk8keONXqMxb7QOrnEHSY4w1ZODeT05v5vsdXvJk2aXdy/0uSHQ0iYvqyqqgWnGSrDD1rvKXnh5lrkx9OmHlEOSKIHFN6Rh34f1KdzCl7dpsh65mYMdP9dlhkZZGB6WKHwnv0QLSNkBM8PEcjuGulwMpmSrWI40Io6gIwVbvYj8P10Q+tp7XddEAEHgoSuLGhyNxvnG4tmKadioBeIwGorSdDv7xvpYMN35zP570NbCQusEa5ycXG1dc3YAsSokm6WSMr84vzK/qCjFBgSZysJYX58KaIPvJf9/i21M0hAEMYzGXz1OYCnG9+GGX7guEKASrCCw0vsSy1LSxpQDv5m43QO/NfkkHtpgcjkmREwt49bgpPZI5yNAA3N53DvKFmQy34V58XDfgpGUHYsRoJcyiVvZ3z+zow7EUeCuNFxIFDXMRpVHZQOwhIqqnGkYK0FnXx+dP97p4HjkCaikQuaZcjV7crJ1cvZXQ3OQPNY8wZS9xsHBStdsdBvKiamOgc64IVusHIpQcLAd5uHxFAKowjIUpZcr+DbmpgSPwxFIX/IIQppKCDAM09R1jjuJgiNwO+mLrIoPiRILU/TURn5DkLan2vvtqeXJY+aywJCUjXcJg+i2CQRxcFaBQ2GNpLFkcSXTHXeVPYW4bkwgmMLzxWwXtTmUB1EDcsCMBvT0WBxtLy11dGupAm1dnJ1dXG0+vtdUzs7+Oj0As4WpLXP+rNQsKSumemB0Vcjd17fgdgMh6LR1mR7ERU7SRgEsdp7KJ40eKf6yJHnKUW8ZCQRYGs/lAuVoIAcLiMPjcEX7Cww7PJl/6IknCAocur5xIyMNvSSGOQl/ihL6BeY6khzc1G4HU91haSA/l2AkB/cTOja4+PLuaPTu2x2bM5yxWEKVZDerxm3ItlXIJktYsegeGxu2aYnc3MQjBQg6++3i4mpy+u+L3bODs78u/s8GbTUPVkyzpDeaKECm7B4NAmTmyIKMEfU4qOQIHyGctoLPHf+4Iz50vPrQJx/0e6PgYS/7OpM+bRxklpY66zCCeKMBlk0wkvXEQz89gTNS+pGpJTG/M30orpHW5GTuspelJZx8MOVybGI7dx3jcD/s6UceCaQKPFkQp38zJ7zjjvuwzWL1wDRM5egAkmUgdhBnciZmjo2OBWHWxMgSjSLQ1sn5+dmJ8uKVi+zpQaPx12kJ2lJbmMmrKt9RSs3s1RM+CMjP2Q+QgzxIS7DHQf1PU5qDpqfWPz9OLh8eh3YqPiLqjEaHp0JLFQP3uPzeuSee2sFnPTUk4In2UwL94c4cuEUJvsn8XfmZzP4rh0MsNVyO+nHRJ2iBqCsA40s7H1QAWANeJEKwBbJQGKpeeTzlcnlRwsB6p5u8qu9OE5gcdAOxU2VUUVAWPP+bEi3ucEEn2b/O/5rWxY/WensHJ1dPT1dKqLtV4pstyNQ7DbOT7S3G1NG+sRSCBBxMgfUgCAsnKAZhExQ9/P6TtG/yuDP31M4hP+7ysiGfT4zs7X19qzT3YeWm4zkQpGPC6/HD70ih2z4+XmKpkA6MwLPh/f2KyHjKXmw5v/Mj7o26ot5Egpv7fJJFolGHDycCl+DbYWNmcRp+JKeBwYrvLfIyaug9jFSDExOQHEf5TQP12FgEJZWuGg+mIKy6e3pRxay2mO8eAEqeX4lvud0QFgeo7GQVo1dUMMVQx5xuD5JCHqTSKSfgSs2f88BUgqam1snCw/WnbgLhDZVdfnYKThjXLp6zs9LG9vFc54HrvN6osyYE0gnqySe/ef14PRJJDuJT+XZ7f3+ISeToJWL58yd2pJRzfHhWEIZDc7OuaFQosMKA/9LtZCx27WOk18P2jcKqksRQm0xeO0Si424oqKGyqeyavGiFs93po7UrCgGVVk7PGrzN6e2kUTopnZ01AFNstUzw4qyOZXUOcNOMj426EU9qAkHhoMeBOP0JVk4iLD0r+R7w0dItt9z0VF1/d9AlrD81l2D0YlaPUMPDHy6Ftu/xTjgfTecCfkGQfO/f9OGHn0eWfTSHE/jMY3nGTw1LU/jc59ff63JdSt9D0TlmKlF2Bfx+v7c84IdZPnbtWnbCGXVNIOrKJmPLpKGTuA0sTBBCyRXgB4pk8rHHH6v2lD0Mg0qmclCSWSJCkKWTq01zZaW0sOBeQN0qanZ0GbOUjNItBfuiwAtPIP2eCSSAgMgEXyYeFEI07fMNs+/MPvnpRjIbs46/BaDvRJTYlUrbF3r/87kn0+Wo81G/xzOQFujVpz6cnU3Q+DCz3V4dqYtrunDP7PZNO5Gp7VseGfcHBhK3+Ol3/IFRf6DmrZUDA36ZA+o/bbuiCJKSSRTID4ajARxN9fU5R4OKsaknzXi7l+npa1lTZqDm1WazCZMYR9ul5rxSOrhqYk35fzupyaYlY0p397GsEh8bm0BSDkfK6Q1SbDrn9RN7WYbK6GS/kKBvDc2tEqVedlVc/vGDt79Kloxq57HDTmJ9Nj1QKztBYKDOiXXxmU/fHxYGafoROiLW87FKRPDfsz745FOhP/+sZLIDnoEB76u3Br6rgftRb8DjH6AGB1967Lei82knsKuYHLdJqx23Yva8HXQ7bZ0vGZwtWxhnWXlxhtEgtak2wSUMs2TZbB00wXajF3kg931bEF9S+Ga+ml1bhIAD6pt4DhSExgGfPVr2FrK7Rnb6Sj4+AINpmGtLmNLbz0TmVp+qKHavp+fX2p0w7E0/6n3a+3w06hVmaenFl5+hHglJlIP1dZbvjQz5/KBh1NIHy6H17PmVWLrm9Xq83pq3XCuD1twxIJCDI+En1jbHnh51aSRgLGDcesnHF4/Oj44stxkhUdLWNJJkGa6ji1wOWojHsRGOxEFrsGarpJ60msmMAVqzBdkm1jVL7Z6Sqe6hwT7nBMBZUPKxfq/LA5uNRSOpTIeX4QTgtDlCsSSrE8OlyeUPQyoBLEkknLlijA/UalEXQBAgAEoKPfveu/DscH8qQQD1vTEQAL3wU+9v/7idx3rTMzdHy+k7yx4/yMXrr5VBM32RyevX3stiUeeoE0WDKKrxenfxtPr7379fdLM9NBVEtUdglgHWj5JYDYJQFRuUGYaxmervpwfNeKuRjF2rNwGlbbWaJfSkq5ugFGcmn0qlokBAPLXaOFwRxW52flOO3VVPSKFbx7kRDJPwZKfDza4vETCWxAvWTj6T58q1aLnmL5eFBJVI0xv7lYgk9I8LdKdn0YHo84Ga557E+ocbN5OkHovkrksM3DGAlB/1egcCXq9fkOqbhWsfn743+rQHmUCDKTZo6MYmt7n7e/JKNXlzST3K9iRYE7QcSxg00Bs1frstdzCa1HePzv8+bTW7m7xy7WYrOAoIA8iRwqux6u7F0bSR+t6D+L3RaPTrpZmXH6sqmI8zMoVLs9KsAFsYRksUsU0PhyRS4zphPZbJrmXuSkdd3rLnUZcf5MKGlp6o39g/4Ln8CDM1OVJ3TJT96XR6+Id/P+1QGidGtiPDZTBe/xcgUANj0++jNgtEeP/N6NMuBziIw54Z4miO3jyyYmtZUSxeXMnoMAz8OFfcVQwI0mRHyn7tj0WF/33v4rffW5BN8HoMQ6H/veXoKFCceHz+6BwsA+QRWQO5uC57QknxlV+bMq9H2qEbb7n1yblbUJLs+Cgm4X9VCmmPsEkx2c4ryn54yn8ZoMY1ILBpP1W37lqiEY9rPEdJPjwOyMEzKAj0+9v/+DiNGBlZnhp4tPaot/w/lQEWHGdELnB74c03brzkdU64gPoGI/ggWWA4otqeFKfDxKah8Dp5+bLGcMWizkBBGXYg6toff/z219He7uJZU+XjTavLW+o1fX3uUXectyG5qOz+pa/g3cVC1FX25mici7z7cokwFT0izn0+tS1xfpTs0X7A2e9ItDaeI5aGiZiiZF/Ob0z5aW/0eX+Cgm8c4juZQZe/lhYSOE7VAs/7uQoHpHfj03VSmCU69dz/4/Koy1tLDz/iGsfzr73ShtOeS5eum5hwOl2uUc2iCyTwhslMLx8L5yMP3s4lY2wuCHPAHjM5CGXZfgc3/dtvv59f7PauXVFszka5nq43UeDIFlQyjrrlldLiRVYvzi/uBl3BRLpQ6OdjYbENlihdj3zDaUDmEIrxC4xwR2KWDgapjXqII8Dqmw23H3qoLiSE2cn7tysRmzjMfe2N+gV2MpGrpdNEplIJpRPEiMQKFC7qZVetDBjDe+u3nz65IQ29m3ntZfqO/oH+SwHn00+PBoIMjYdklmGI2Ewm+e4aYYjVzF0zSoTmSQ4sgRDsCPhRrFutZoxr5+cbpqGSthwR83odK7mh5vmKmnKjWLNxUcwa5srRtXD6Un//7dfdGBnqYSMspcR86yEKgA/QnCCsMwPp21Y/pNhOxMeQGENM4kRncjBBvdOpr4YP95PMCH23N3rZDy/BoP5Ub2YonLzsosGSkLPyHY/XVQbnh1dvnVt+4FgfvHGw/lV7MFCuDaQnQDbe4cHQ/SEP0k/RRCyPWbHsb1eKmS8eWxNJhtXgHAI5AixFFs//2ttdacyvbJoYB2S2I2Yaln5tq6Xsnu+Zbitmmrs9fUWVi91BBuTSXyDEa5/t2PZSRCQidcblvCxojJC757t7rrth6vObfDgnREiOIYoVkevvHxCGfaGNDqgQ7Ivo4zXXZf/I5VzCv1THR8LP1oVcUJM0ThcfrQF2qZVvfWd2ajhUORy8xC2NTO8nc4jnuprr6afHGd/G8B0DAAA47muTXDuS7Int8M1hUUP+3wwdkCOVi4vZxYu9+ZVsxmgYssySy7py2FP4pFkqHpwemWhE7PGGYlk8ZyocSKb/0nU4sRLOkLaFtTmL7AdwZoOJxHcDj6bvuCN9y9JUSHNxt9tLFvEmwfUHAv9RZB5ObZdxGM8WsjdJftl7mIQQSDMJhAChkEgS4CIkNiQESg8IRSQJQywUqQQpoEKqFbCilopipYJFelWLe1Q91HOcvXPUPc6reud5nq9vfv/Am/c7nufzBO4aoGVI4woFVWAj4ZMGJ4ejo8AbwJ1J1vOTSloyaZIQx+Wz3Aj4Nez1hF92OhRnlgjWrcZ3R06dbE4N6f+/jEypFQqjYDWBJ4ISaqZIHlOzUpPjBSQMEN0oHLgMCm1vq1v46Pvq4e666Xkfx6KZn9QrFFaBRtx3332X39bA9Ap9X7VezPf542IWBQenH33YD/HBcGRzNE5IY8Jhjqctwl3hfsP9Qi89OqAMBCQGLRYpgGzyNptISAlc+3EAMI6UVQ0KD8+1rUgRCESETiNS/ZMXiSwTYxtPVSvHCRhuA5hm0fvNpaWtqtTFyYKtgsnzc1ffG3KdiiO2TVohha7dFeKwXlqCpQSOlA5qRJkgzY7juDguBgXjAlDQbas8e/m9733AYhcTLBz18LjNrjAS0NXiR++rs/P9muoj/LiPz2Dwh4+ibuai4CgaSQ4seErMoQj8acNxQHsYg9nsPhCHDrowFJXJzAgKksaR8fR8NBceWN572UmiqccVjpSKKaDVSmsNxxFaIkuAZNsmpVGTLG3SRtkpmQxy4yODOCxpkqhWA6u2uLV10Xp9p7fINtT8/3CHC+neQJRL90qcSG0ShaPT4WFzrPOEFRhDPAYB43JLxJWVR84ufH+1re/IEX71ARZBI05ddBXF1foj8Cf4p6vFgvjwAQBEyhgM9M1IYIS93ofFbEKcUKcxmSAqW1NYqCspiUQG3xz0BjPup7LZQfBR4EgkVUSw8coiud1AQMmiKRNWyEYmq3U0qICNKHzIT6XRNKTed5nRKMVkorGoMhY7TxgRUlhQBc3MZmcyjYrzo+NzQwXNQ1NnHmIJKTg6xZvTJoUUtYeDx+NRQBBhkUFiRxGKi8encTBuSQmfIPZ1GxemF/qKfbdrSGJCPO6bnhoBDSGN5+Wj0XxCtQ8oo9tn47r0UT4cCdZXGBvnEUTN8TSFJYEs22RCyf/aUJitT1w0yrKD4BeiYyxlSQZchMTt7ms9SppaohwnqSAC0snxkIxqTPYQW2bRqKHzp4pCUVPSMJ5iUQTjQW3IS2K6dohes1IN0Mn4ouLUyEivgigVyYT/30Zy13IoK2FiSxpwdCEOHC1RNHARjtMbz9th4HGG9fw+3/S8cQFgGpiPgOZrfNDQRzv6AwIbOt+iQ/MBUIKhELBOaJtxFAVHmoX3oOBNehKg/ulkkqbBUFemEQZcJJvlSiDr+CK9IVuOHRTiDBZDOsrADO4Lo2SiWsCmJVKQg+xU0zLMWqrs1lAIKTGxM40ji/RDIYolNaUnbnbePbbqcEFzI9Kol6VUss2g1vrnRnpZLLdXqwXaE86u2lCmKTItHoXCCYG3wmGV6vpNP2Hq6o4RhkDk87t9hPlmW91C3UK37/Y+/+3xPr9x/vIUXyCHlRxFW44ebX4WaDhabQs5iQHzAl/CBdYmTv2bNEsgWQyG7YpeFykJ5lgkZKYRrVZcw2DpbE4LWnJ7YMOD2wXKWcamqdRqRSPRQ2OzBEqqtfaQGRsyUNJMB2/xVKzU/CY0/d7cWuxY49hYxWkpz9VbFpRpJVrqbdLYykUrm+ykA68mRCrv2oh1vbgaoQvpQiEcDipNSA8w3R0K15TrpBUG2B4AUID6+Svbus/WiWEHxAIqST9d13aGYJPzLX4B+uYDvWcPwCwZKKWS3XwUqcOgIlg60oK2CPRyIvBmJpF8RUESCkPZqJlGOuZw4A9WLDq8FMq2Kbj6srAnDMQjoJlKJQQCDyXEkUlQCqvyVs/mi05TZ2z44qlaeggnidsyTmmNdb2/6PSsXWGrZUbpIazn2KTUZj3f6BZqwRKitI5tqjx31dSYgHoDoggOBgAuWp6jrY0WLBTxTsAa7cMEX7W4Og6gtM/exweUoJpa7e+26xdc8oxNbo9rYOjpV6vzDWFCTYxVUpKP4Ooa6EDBwgE5TxE5apaM7VdJ6RGhUNbezhI0TncizbWu3nDUbRhgRt/cS5QPNjRQKCZldDwFWWgn/GyMl8e8NbC6MValhliCgq1qupCr4zA9pwsgUkHFSsWJ4oIpBfBAFHXQ0SUVxE6QURFhhC4J8k4WKPeW73h8ktLAzZooERw9G7pVRlFOSG0VUvksDKB2AsF+xEgQdLfJ7YDViu1EDVVubF6wjxDttjaCBZgi/bwdiCdOkIzcZRxHIHT4NOPmm5vS8GE5NW0BqhNSlcIjWHMN5CdQaxp7VSKxtPc2J4MykAu9OZAoNw/ikxyAbZn+tEVttcYt7IlwdLn+5WsbEwkBBxrdUtQnEVotrZMsElhXiASSYHzIyDIL4YcTxM4a/yTJG8UP4oiLDqJifcKjnNn6MGVoGGwA5Q5aBksT1qvt3RVtEA8GyQGVEft8PrXf/ilQlZq+YjHaL5/vnp6/brTzCvoYKJjlgDGeB6wcU0BxM47nYRowDEwJk2zAMEDDINDiIZBjmGXR06kYOUO2nnfJy7DFKy0xjjaQzVKUtFtvpSW53LRJrfSoHES/muZkB6KBgCfwcv0ymS0hWs8t8nQYvFbW2kokiaQtw2KSQm8tkGLLnEzVMba/hnkQh6HP8khu0cTjM57Ed+ev7tAMwCcNcvFwOPBYkJ9aPd2WIsKGgR6Lw9B9AF34fGI0Gn27z8/XEHg2e91Opd4qz89npNEH/JbCNJKh1khM6SSGCzggysAWMZIInQ6RRs6eUjidKln4KalDAQGK1WtrOjpctKhQ5faB9GeFe7BeFhaXTDSSE7XAjzgZ2vt3wcnl9pYDLBaHZQcSGIFhkN1uUplIRCpg0aQpHo8nbz3MVBNjZP94jCfDk+RkUZhVbn0s1lnLc11vBK4vLzxvPNM7nqGQjhDEtmIbBCPo9b4DaEZcwyZWa/gc9O0Wdj6fH6+er6y8/F7RfKFOt52XRyrVGGRYgwWouR5QriVc4C9KLODf2cYY8HSpawpik2Uq1emnxgXUxHhjERqOrK2EPKH9/f2oF+6lc5Amikk+Y9E0xkUl8OReKBvK5uoDuTf3lgck7AopCcnFC2NdTzXRy5pOF/kJZ0BwEyOliZCaFZMSZU5mjYVJZLKiWrNqsosMPcS7vuXH4HXSqanrZ+ZOdmgqqwWEMhIW1gdodJ8mzo9riAQ+Og7MJQwAtrjY1n2k7frUS2gxSEA0PBvbQkXikoaIuxQNkhIMAjROUqfDbKcxdHPt9DkCzRKQkodP02hkNcRTEDluUVnQLcxmsxFcGGswYDjbCJItnRo9IUXho1ogEIS5QG43tL/3I1N98QQPCuKSqtnWsjCqKWaT8xY3HSmOQcJUyzStRCVHnenKsJRhsza0vEYmM8lQ784X7zK3JVLjuMulKLoot1X7LYN0Ouz/bIkPtgiI9sQwdDVMl4+OWxhieaW8um5qh5TyG/Ly++f5Jo4EVKcBjuShC/PAXTDsjViagtFhANF6aPpEMdUwkBAzSVKSwAk2zfxs571lTdjs4OBgQIaiICkekQGD5FBGOxIqmTa0n83u7rtmQ4Pt5aHoaqJlkSiFdAFiMUTD4YmzRjlJ3gEMEdSlLgtzRCwJkp147Utyk5BOuT/sVrqdytOz0pGdlgCwE8VGEtGaGtaIBTjAQGG+rsZifR+gOD4/4QDaJr6TgQZxDFrcrT8inr88b5RTCznWdQcDCUM14DF4rzONj2AKCyVfvvban8+XJfP1MKldb+PZJczhMjZBXi2SQsbp5nMz58I3N2QbBgM/vPCVpMfCVm5vp2UWpVLpTHIHd0GRBYdcpsHWIEU7Mdnfy+LRccGDcpEpCcc+VHnVn0kVSKHWKibOrFXKGMqJFza+XMZpvaF2qJy1zFSTHlItXmiZYLKIRLe77BGRBv1EExZgUFjH+xcKjlXDqrvRVL7FokEzGAwd4Bh8aV233j/yUZs+H0ZWjfWn4BwdOFwUElXyfyZ67avbNl744667oAW7gEeAiglq0ulHvDQRsowpuFzJ4LXWlg1iGrjL1+rXvgS0ArxlchMiy7S0huPHGwZDQvKxlTOl5NKiIua1S42TCnlM1Sp9CEmRNHhd7334ql9eMdwTvO0gDkcD+ufLJ9d+/POaMzGQ8YTJczGnOvx06eLcyXEay6Mk5+h0LzUNaGuay4WxTz5nlLfeXnf9VTDJYODLBw9zU2HeAQVgSfqdNhsV5Puxx/sBrN7WYUBUguFiAGGsXx6464c/N9Ye+n6o6IE4sZhMJg03edlh99NIDrGXQSMzmXDAMfdWl7/69oWOUgzm+MTSUgeAUgC1NgxmA6WKtvNGXm3v3PTG85uzRn/4cGuwHWBCdvHO1eu/fy+iugO3HnqMiSU7ajb+WAuEf/jh9dee/HoiZz1jdAtE7smtuRM2jlamZpmj3kfIhpvhKODPQKK2vgiR+lxT7/ksAJOB4BKdV3gTKDu9XmyXXz1r17Bl6g6Q4akxmBJKhMJF5CGA59tTZhwPPvjnVwKXq+gBLPK0k+kWuc0SrIiKkzjH5UQ1y90Tiuy+ee1wzZev9S9BOnVN1ZVL/S9mto8jItnB8paVtpYKUlO4IjUWs89Mlububb/3XiaZxZp6773rv1+1m6Ih+qHSGDNO/nOz6rDZ/NW15/948uvXX39xtIjcOqyq6Z87WdSo1iJxwPdCZF0JCt4Aekb10nOn1q0Fl10fnRUQ4joGutp3U2HhcQ3Yo8N2gqtOkE+nh6Gqx5ca2Q0IRloCKK3jxdybAefYpUtX3njQbW+TP4DlsERIJK4JrqVgcWmTk7/4XTkWi4lk90MvT0w8+ELjmbsdHXcE77hy7p3HadwQnhvRqmJG1xArnB4t6iCpq7o6g8FD7e7DHFZRm+vVj65erRPS6Q31rTV+4t5Xt3WOHVqtutZ++K4vX3jtwSfXt0bmAGZoLEgNTfmRXLqq0UgFA6mkBLzMWP/dF9YvuNrqis5eLo7/HzP55AgDg3GAL5b7NDySuNlFKAuTasbWH4MmVveWg6SarqW1VaUnkUhMVH3+jtRmpyKRdHMUrGS2E4u6eTtpcnY+1goYPha/HwrUr72wqmpZ7wou97RvbDz+5LFwg45rruXI7UVzaAynM0F2e6DOmoMis/tghlU0NV18pHihDa01C7nep2gm7Y8Tt3WUHvaU39Gj7Dlc+/m3V5bOfDJU0+Igq05OjfQWkJDu2tkw9ziCWsHFwwqkK70rW71tdc9O11UKwI5h+3yF+Roq36ffmW5W++d//+J6I/mg88WxK0tLf/7wx9Kl5xNLow9mJjZfeHHjya5LtXINGk4HptKzenrMoaU3ZdjMY50tJ5BwXFYIHOby6mrn6ZWP14Pth9o319auVGkxiIjW4VQvNj5ztpXd4YREzI7x4K3h8qefLvPMTxX9/EDl2bO+NnmDCdtKlewNrAVbjwW17jBootIrT357SSG9+uEzpy5OOppP9Z5XTFV4lYDAE3tdLk4JzO6vlRpbKqVnu33ibmN1HoOj0elofVtnmkdu/PrN3AcffPHPP7/ezauZdKxdWbr25UbXWidz87HOidX2gRefrEpc6XRAfK9FQCZfurS+9I6CRuL1HQyqOmbeKUWh9vbAZpTVH3y4pn/0QrDH8+ahry49KRkEXFxUxh5qee6yAOqyk2meTIbZ89TTj5RTVomVR8Tiit7rQ0ZUA0Wouo2D86wqVZ2dAprZndO2Z778I0Mm9g5ZT26dM1qtUKNiqBeLM5vHK0famouQsEeIILUz2oa7F21iwvQB9C15heii/vVTc1M3fn3llxs//Xrjxj83fvpsruvYRNdSzW3A4/e4me3B+vo95dqLVSBl2pQBk+S8NFPT1f/+559dGD1/rtP9lCg2M4OM5EBv5TKH3E2P8WodmwMDPfdfu1L15h5eGBHRBHO9WyPGCR5ES9ImJlpnax/yYplrjorK+NCHF56Zqjb6GEmnRStZ9rASjY1Wp7ZnT4t1XmO2pKCCFaJKkUjEupyqi4tbw5EszjZ0prlgrhn2RFlHlYPHY/a1ucRA2fiBnnGdeffXXz/88NdfP/7ms18+e+Wzf258duPG+1+PWUe3OsqjoVt76j25QDTq2azaJAcTQaHAHSqdccRGn7v77rs/+3i9v/XpctYM7wQSl8tFc552dyV0rKW15Vim3VnvvLZWL8TQQ4Cir8yvzC2QVEq0bFWeYFtb2NjS9ctvFUzZF89ZT343tTMympmZDDqXe0ROR1WXMtcT2M15AqxzBQDwJMgDLKXotpTDcX7Rj2/7Xl5ZPCw/eQ529D5yqbWWKBAfcYnlcr4mn2CcG3KB5/jpn58++fjXD944/8EvN8D57K/fLkw+x1Mx788dCkSFod1cYCCjFqjXg7ethqJM57F3Zh4bvdi19M6FURVAK50tKsr+bm43p3SSHjtna3adO3nhjkO5l7/6ak+IOI7BUQlSIqm2Qs7iuJn/OlPApN3c2nLuVFHzjr71ILn1xPWRC+tLSzOHnWoZjeNhKWkTMx23BpbHidIT5HJZfdlBmVeW6FAzZxbb2hbeqys7Kp+zxv7j2MyD0s7PMK4xtS0qRzTgESNUUHBrth5rCQYQNZRj7RCJjgiC2MixQ9G2oPEaDhekMYJmyapoxFi0q3jWIxg1az3qVd31qrVqnKqJqWaTNInZpNt2+k0Z/2NkePl9j+d9ns/r5nbeI5lAAF3Ar2/6fPzn8wkJX9YP9N5//frhw6f3Hz5/vb766N/PV58/+8+zreXm8E6LMQcdmVHp5105ik7lOKjwAAOZQEVDRPTFpfjJJpJurq2VhAwNDcjRB+NBzaPZjv60ZuOXSdWocLiyDqed2p86B2w29wZkIzuegIXhAooYDHwOIYeMoWB1AzG3F2IpINuo6FPp5Xsuhz0VEXjdPxKBtxMUm4apbP8UKpFXl83AcBlUJFeGZJLSvir5+B9/vxGSFA6Du/34V+mf/ewXgNz40S8Cv/jDz8//9JbRMvT63+tPt14/Xl/afPn8+dMV49LKt4+2FYWWWwoVKqcRaN619xLegYQzI+BwqTALnZIztBRGqCaIbRSyApkd6S816ShMGXp0LZVAZlfMSU0NXVUyoUs7EfopMFo9aRJYxJX46rDSkJAcakERISitkwXFsGtaYhrAOSyTSiuYMqZjah68QOaO9MbBQ5M7xnHzeQh8aAYkGxEpoLGI2Qy7gc+vMcX85csb1+KxZ864pf8qvTG29Gfnfw5S2uiYzJygCk3tUO9639DsRm3L5tL0+vN/P9t6Nrg0q0orR5XDKlT8BJA6AG2fPZWKD4AFADQpGSfisZsHW0vBb0V3SHORkEB8LiHIfIWMy0jFEwiYIGcfP4kAmuvdgv26C6c+PVecIhOGBPjTML5u/g3IgmpSknmRlBxI6KsfqCZQI8gKaVqRyzH6/jUFGDsMJJmkQUoHmyWVgBDICFJRiyEQATq7LlWrGA5XxHz1ebQOmwaHuH10OiE4/jMP4ZWflv/S55d/gIUXfnhzeDi/aXn5P1vPVutXawZXNpe2pucU5sIgaU5QUhIf4lcMqsmet9uzQWpEIOn4/CCIKzhMJS0Ahxxjv50aOsXQkqpLQ8m04lAqPOQMITdtSBrgi0FnT01MrWV5eiqRFB0hzovonuKbAENGXNH3Vag6YBCMYihNegXpcBRo2xTcuoyM0akpHD7guvdVSPXXclJNrSkj66o7OS1pyBc4m4jU44kCaUVQWmbi50mwCDjD3+2D337vZ5qf/uSDkMu//On5mFu3UMH3X9YPmea26genex/3DZZ0bmc+edFmG09jhxkiigotFpMmNKP4XigCkS1IhfzruwgMRUXMVgcjh+VcqpII+pP+AJw/yNJx+NyLnzKQAR6BckXQcKFvBqR4rY5jR1dO8LiUarYoEuHnDnF3j8jtz43fGFDNQRERCkDBpRZRC3b75WZq3VqWFVQTGRd1wRvNb+kz9basxpz1w0OhmBYSOpuIrmPYOUzyeEliTATBH+1+McDNGO/Db/nQ//c+/Mwfut28eW2p5uX6ellJvsmUZuLHZPYODo2F68ZJ4825MGAM4IeTOlSmSBCiro3GXffyCAzEw0OQ37tO9eeXPK6GUXF1owb6jAb6XQEVDy/yP5vFYAiI+PKIok5FQIp7YHHP0tgIhyapkrFYIpHaXkcVIOqYtpEeM7l1ehyBMzD15CAy1dBPlytYXmujqZzRtdDQvGI/bvR64kJ07cv7+ZFEHJJQMhzqn4K3T+E59PGYzJs3MD4eCL84X7fVZ+CSryff/aRl+vMf3oyuna796i8v79eURcdIpZ2k/Fozf7KR0M6cIQeVwtJIEYkt0aj4QhygkLxC/QARcx2RXKQPy20TYweW8hMcR3QH3azRn4lgIEKL4v3zvHEQHC4AQCmqcKZEHIxXfbuy3S4UcWViG0snb1dP0IjaHqdTbkC2Npk/CMi9VN7PJDANhkIyIbUYNNXzxTitFg2BomJrYgC8+riekpBxmijABlOHBpC5+GzE7vitH4Vd+0Wgl9f1ixfdnj58+HxltY+dWb/5/NLPgtnR0Zc/+frrG19nalRsFDatr2SAfTuH4prMgSY0CHO/K8yMkYbXBl895YnG8QSQuKi8QBiBRc4JuoTqe7g03l9hYGHDxyMYF+NC4Qnu59Ap6RiwFJFQ+VDMFXPt8n+Wdx7tzFL0iqadts3VrmMeo2C/v7+nn8oISmtSlKdRc5kGKNfAZOrLGRL16FqGWcuITPGIjv04vz7/SnX947sQdFalX05h8/BmYktJTlzQlXjAYZ71QuddjItze/j85dPNzdrNr3p7N5/n/+GPZTc+/vnnN17e6L2fGB2j0M3VZmqMSQTz2KUQDzgFRg1K44c0XnP3PvWTcxl+xd5RUXFxiKiLZ3yCa02ZG/Vh2EYYFNnDzGHCAdqLFgcLkonZkfgIZG4QfajvcHvg2fbMjsVFMq5sLC2/XpUci9UcQ7vTLBYhCXyUgplrh+VSGXApXaGRM2WBecWyuXZ7nG9w8IfA4/zcAk9q9irOuhoHezLAZg8lNi8NFMZLOyuKPsDzPKLyoq67fb3+8P5m/ovnj2+sv1x/HPvrj298XvrJ32uvfX2jL4nARnU0o4LH0sbIEf5nfNhhQVTYj5JgpWcAMuUJLBrvU5738vLyogL9c+o3O4anS8Lyk4KQVDqUVkjxxReJNtahyYEXffEMA2h5VY+2UKaNadUsxfxi6NH23NzrZ3TFrAOvZeR2iJEhVLpOTIYzAEoYEUEmjWtIub6+UdDgekscGYMphcUn/iV/Z1+B9cIFFASZMtmEHHFzEqpEw+zXMwsiUlP9os6evu6WWbb1+t+9z57/w7jSe+NazG/cHvzpD5999cUnn3/5OKZUXF2ydCPNHHabdiYimM2+TYjIDYEi85sjK9+bGnEQALjkXY87fdEjsKTkVvhKonnJoo7AOxI4uWcYSLbOstKR64uI+I5BUAQZTuYG2NX87RfPpncW5ywHs7WW5a1pVQ9Hy82tJpRGkPW5XQTgJFzB+/oXMZmwkAj7vbgbNTVLSVeCb18ijG1r2vSHto4AlaqpE1WIlCDJ0SYLKidXzWGkFjhS66YQeVFuqJKh50/f65bplZYY7LWbPmGk4F8GXwlO+uJGtDm6mZTfq2kboXzvt5B0n0swZCAiMB2AZRmiEKBJzhV7ewL7LM7PA3D6IAQdLCyMJgj1DrtEmzqaQUQNtNYOLV0JzP4O71sRu29TYMU5FYlLT5eWm+YqOuemZ43T249meor00pwwSUSuPggQAXg7DhEXSiWPO4qg984G15aU3H9JgMWPzcjfHB7Q6YdvKsxmvaqzEEtI5lIoUnYBp846n5o6as1m5F247la2bFn9S/3Dp88fPXu0fY0di/2w+jIKGsS8+eXjday5s8PMp4hbFRc/+rF7AzwhMC7UKwVKOZMCvx51DxhPIIGJ8s6L8zjbH04pj17dqDDxyQRHqj01IgXhLsRGo1pqL8Zh6v4VACNQFhYac7Qy2QBK8+zpBmnnmhg18O/pxdmZHDiTRJDhMUVwzMXQSJAHhgIFmdsvRQY9qb+WeaP2Wm77iN6mPzw5PDg8nJEVuOTOzs5FIY8mIlB8laMZo6MZGRl2EAmDYnofDa3UD9T0bq0OvpiuDvnZz37JZn+9SZa+/Dzx4d8vfwAs1y6d2TlZxZMk/9z9YoTBwysZ5g7JQ6DXQGByyj0Qd++f//KAYlHBsFu1TPJwTCvBXsfA19/3P4sUQ4OXak9dgBBD4VVd3d3V9VLQHFD4K6uPBg93Br8Z/PbFqnHZKQxFJmGx0AgonObrEReX5c75IKec1NbPHOg1xcb0taDS9uF0mRBYVu09NmeBo53c36/Xj4kxyUIHOju7bg0NEuF+16ij4Lrbjb6lDVN0Yk3tSo3GBP3hb7BJwYRbJYb4rz7JvH/j67APSxsXusQ5zQ1VVAnXHQI1IeOiruchIqk0IkiQIpEz24aRETymQhwUEqGQDn+ZZspx4Jmm1YfriQ3iRsKgP8I9AYbndjeqq9jmnIauBqyxpmSOnaZqrZ5erXmkKuFy6vCF2FI8Hs4NjYsD2WaU/9mQ/KXpweHamqQHn2RuPrYAfcpySRy7HPq7I47WodU6XOOavvpqbQZ6bXStGFBdErqB0V8U6paYX3stOPbLpObEDjG7iugf5hNfnhRUkPbVzZjopLKbsQvfsLGwRh1LxNFxkeXGTii+8OxZd3yYV9a5wL03h2+/PZg5dHAm2wjJ8HHDzmA+30n911J9c03vU0VOSHwnEy8fsLW7qhrU3VVcVquYzS4r6TB+w+Yvdwy+Xpwz66ZchqI0WHoyFS7hROI8713wBudjR4eqfrD+L2HYS9GJ+dgpkRJYM0q1lXN05ODYpxz9+vbWxU5qKjojshJ9FZAUnPZInIYa4RYdfaMv9g99qJ361oUFKAPv9oMQQkN8+PoXDxqTYk3xl4Kx7NsJQqetHSq3qZrmBst963Mh7tdBU7Jre/v24P063jvZmxkPgpdG5PbNDf7cQI2oR1Xzw4c2SOF84xOZZaBtx8mVTJqruho6m8y6b+LZsWOdL/5tHDSaTDq5a3MlPDeZhpGRMUhWNajkXtR1nKxLJEaZYsJikxJ7E3NTrVbQ3TomJjgc55aKXnDM1B+5pGP20XPo+UpQCxpY8NmcdqbQDfX5F8bnXy79SdUprKri0Fg/+757Ag0W3PJVWGliYRgcZkqSJ9MmbU7bntzcQ5EvNpWrfiRmeV/1/FR7uDP77tC2p9XuvZlxFcHg3423LV4KDE8racaiBnotA82kJx1kFkpH2XnT0y3XdakxKFRm9V0iRWfqfDL9jN8sZnd1bC2thvsLUogsChxzW+0dde9eSW2IJEVUlFsYTLiUFH+5EUnlCeaVIhFPtK81q1QrxqJd7T63X8OwelVmZJ0rvorg1eHsDoOsyC34y4+vlSTWfBFsquru5hJYhIun/X7VSLqZeS2ksLMlAOMW+zTa0OO09dgIYoNWODI7OzAiF59nSpEjNtvYyN7Rvmx372TyzTst/ujEtjM7UFhoysw5k7RU0pxZvlRTGP6k2bU4t9/VJaiKp+puJzWk/6rxWhIqunORzBJ1dan1Has1/qW4wCJ8JBETeA9smdrVJ90ptKqcoNjYK77J6QnI+QzrPI83X2edOC6qUCjMxjYtp92mYflloNFXsyrfI6rFkbtaA8MNW/Pxh7+83FuGbXeKnU0zBOLp36WfvxMcHQwSzsSvzvz0p4PTj5rG6Xs9zvFxQ1H7pG3GOX7wFiyuNyP9ihFX+/5R+/ERfc92eNLTcwiWXZ/pR/W9MdBLiajN4NI2TY4pLV2AdSpdVV7NZSUx33Tdde9uXLgND8/EImmNjaxWHenJ5mASMQXgasmsgEjsktm0+bgTExkpCf6Rjy/kdjfCOp/NU2bMWzNAbEgVt7ZWD80ZelxygxByOqP4XPGad1YlLzUymwP2TGzs5csP+MbpHXobv6N1wznZ0IU57x4SdDMWdr60rDQZ1clf3ursb99tHykq6JKNvHGSDg7ezhy6hEwDs12mFh1pi/r3Tui2mXdv3x6+fTs727f5sCT4/AfskmuFRWRStD8xJYHnqydC9IaL7IVXVV3ixkbfOgYyQXC3+65pupkgG64+L4wgsgIg8GRfUz2/ebq+g+UHaSj9JTYZkQ7xm7LOpwiAEMywc5QNOWShnN87rapwGnZFfqe8iyuvnruaXWcoqMPbfd0+fvDgkweoR8uPyjIbsWyVwpQM/8FPAn3YsWGXox/cxpwpMVGa65fatA7Hfv9gVXfP3gwTbPqRkUmtrF0r4or3T/b293ra+3sm34HT4M0728lI085AkgIKW9pMjFEAPUzQWC56ddO6mmwG7KtXC9FsNsXn4r/+mwJ59aoL22oRc3Xxiid4MEXIOkMYQJlzKE9qmwnuyT4fNDYGBGIaG2nWtUprXbEVjZBIaAKa2iys0HcOhhcV2Cs/BXwasBTdj1MZ8zhcqtsnnz14cDu6rH69viy9aHr6xSz3e6d+4nbmNuzD6OeXdTmEPlK8ZWijop/q0CqWVUH0kx7G3sjepEOmlmm1om6ukNluaNdO2t709DjBuXYk3G+frB8YGA4PKetFXTJRc3KxfQ97/XgpyIqB+PN3Xz2IuXMbyHESxwtyt2FBVl1jTCvUdfDzrnoR2dCOzuHyhoWBIUtMAAaengDBIW7HlvKso5XA2pr38qfRBDwatyOtYlKjMrZMa7iE0UpAp2aNptZFQlJxoW6f/fWv1Zmx1xK/uNkGdxiH+G1CmvtPTv0Wcv48dmWojRQbXd5/yWQcNpYbdqX1W03OmdmA//b07HKTqVwugUZs2J3s4rwpYh44pZSxvTfgYYlE45aSgaU0n5rwD+ARvsn46PXeh4kJPeMkY5uwu6thobHxC349u7uqqjVpcU6HGlgee1L/C8SE6E7rjQFFGgGO0VSoVMbe28lVAt+LHue/lzGazUN7ZwgYVbx5Hg1aRBYWgPba0texOVTjY8o5/dF7AjoyNDUU4fbZZ3+79qcHCwu6YGNtkumaWd7QnZDt7vdj94uFG87ZX17GwkNKVcakskJpcBoQC9u2Hk7E/pt2iSBZSUum0RzvZsDaahIL9130niPtBMaqlNCR9A0jvHCzxbIRfBWR3VzGH9xomqlImxsyotgLsdgH31xOWuh+dXehlU+BFmmGNCtlF+ZFMiVb0zae1IiJSOtUKYajI5oveeBD/dKJELS1EoRtEiVNkBGZDYKgXReY1WWzSZtDnQP5i0xvtCdgIb0zEKFun5Te/utnd7qwNxeSkiix3zTKEpQSj+KPLpzGNO/YZj88fwUKz9HcIq2YB5cGa1s2ept2d2WTbw+1xxPK7Gw7jQPumHfv6ELXm5mDnl3RBE+glDDLCXrNOGkpLBo1ft0bmY8aHqq/X0GKbeucZsO6Flhe33wR6+/1ynV7oRrpD4IR9mJTIQtddxzLzmXmwhJAMjtb2zc8sNIZAYP43RHMF6+lFlitNJpImY1G86okx6wcsriIoFt6QiKzO2r5vggE0Lxo0Gle/uabri7QII5pOrA6dldVA4PW7e7+6fc/Oh/PH+v5ICInOAQ2Xh7eggKTh9cGjINkh/J473ByX6bOSDmmCrkyiq59Zk8sm9HsHQlF8/NWGqegoIg8PDupGb75C2ikJ6+hITNzSCUlUMdnm+j0Llp3lK8P7KpfOoGFRHZD2YpdW3N0U9faLiPgrr8/FJYOZ4ahmsrbEgenC+MAo01EZ8xPiVIFRAFPaV2LVJIpshwWASlhSflDzZpOjUrF9QZcNyI7L8vtTuw3DSO2N1dQfEv4TuI3ugYgj0Xnz3qev4IFc25n4QTYj6Ty8rT4cpMqPylp2kKmJuPIhip1O23UOiHTqiliar7eSRPuvBCq1copqzXFOpVaJNe3DVeQSCF5ce5VUPITPhNFIeznajpnKygTgMO8dy/K705jVxUj1J9cQNdYBuXC0cg4MO8J/nzDVOR+5pW2xcEb530/+sjdDwTtqRx0HS9FKeCtZSUrJEgJtQuOpMmZA4ocvlgmweV5AvkcF+Xtdjs45pP2vaOjPTlBcz9zoUHAE1Vhuqvukpurw+BM3+/5+PiAnC6tSNqZpgm+XDNNIBJ9R94IJZKMUY5WqE5mKbX7eg1nRLOoFoneXwlWHnq+QKGoaFPcwgI2xUvQHYqBwjTSQltuG0osyphPyTp34dQ5v/TbOrWIEaHo6enrq+6aD8UHSpSpqTicP5I/pNE4NWnNbW2YdC+eFQ1wgqtogbVBbFYr0b4ydTetKLeoiIWkNYiqKFSkkgmgWiC3QTELYc2d8qO9vf2RPbn4bqAfREB8JeiiYBeaY+8wUgMwsEZMVVHOpcJwlS0trHSgpVEgyGhg0dTdSitPxFGL1BKtiyvbpVc0sZTza1arVbBWPFrgGtdX6DUBp/KuolMEKb6oZnOutMjQZLbystFWSDHIAXgSMVekFMnYqnbF54LKKEcIEs9LIIYScREdi/E2gow8TiK4nxGBYoq9wY7ASMTN4clqiYjFbYhZ5cMZBlxaUkERS0mr3hgGCUlU3gVPt9ufpS2tPnEeHR/v0d+PBaR0K2k07lsLixBbmowLPMP6ESzZP4JwBTtegcRCO/K7iRCipDtFSRQAWJunVB9LqrqUSBGxcLFBqYTMT82vVZ77F5xMHyfBGIGhcfe8Umi8MwwqCyOXFMhHBNa1UavVG3ghySJul1IsUUODFTl0FktsSMVBsnm+dYAcMjc15e7qpOM+dz20BSnvH8y5SF5V193o6C5asrhhaHUdRJPDHVKpQsHNJiplqsXxUqM0L8/H7TPu5sv1hxt7WsPeXvnuO7y7QKlbOLKYGgTpn3UH4CMw3XfCAgK4Ug2pMAHe0MhqAMWMuvPc0SmnrawuUVcyrapbQKNJmK1379Lmp1IBqOmdgc/V28aaDN9FIhwTXine1yNxmAbA2cO12ZUQnhLtXXyOJ1JWqWUWybHMkiZdJMVixR6hREjd1AQD94GpYgDgMShj/WM57Mx19FrWOUF3VzeNh2pIcU9c79tcf/ry/uv1pyXk6t5VZEGRQ8ZfGejcqB2ac3uArf/q69Uh7VxH+8nB4dt3brRuLAxCYad0/6ihkX7ovPi90gryGehlzcfSHPgCHIr9HUI5YVX6oXkiNUtQJZwQ0BIkIpqSTM4Vsmjzo8VZp4rXcEjD5HgFOSK1XQymzOIMvhf8UgQMFxKX4QfxApcGuk5Jy6bRutRKO9em0lSkdVVV0WjufvOcfX8cokHaybAnDQ/UDJZHnI3yBBsmuVtGS1byEN6nN9dr+mpqBlvWn6487UP1rQyMQEUF4SqVmd+paXJrVJv77tevWlbn9o5mDt6+mD2A+p3+fXoDLeF7zEPnwcF4wXm4UPHt8nbL9tCHWHzCXRrvxwlVVTxiF4ulfkXrOp5XKpU0Ho8mSZVweaPz1iwgZO3UIi0sSIpMnafThRwPXyA1UpRUPDcXkbFW/J56po85J2g0pXVCQnfOPQpHgjVLjMtbm7IzmOPpCxgfHC4srSTf0tQB9zxFVPKI4POJkMqoT0NLylDs5jZTSW1Ny/OHD3t75UUOfWuzpalC37bY5EYRtkXfQj3++uEcXagzH7xYPHx3hpNcddvdGz8pd80cHNr0ue3Ona3BnaYXA4924B53Iq57SXhA8D1ITxcolSIe4GBTUmipIPCrgtAyrGvFV7Nw0M5waDkj26oUtrPUUXkIIo9L88MJZO7FlWtro/OiQ9u7EVKbUrSvFkqNW8EgSxbQAO+k3mcYNA3pGTR0SinBVBhmqvb0HOXRaDSeQJCSEXoqLjCYXc3mdw6nFY2j1kG418v318p2uWa+ztmpYroFW0hDKGh8/mqZrqeVLN9RTR4eToqFpZ5e+0KRlm47mJnRvxsbW35xePDti28fWQLjLl741BPyqqvrTvpHP1ZaQUtrzYCkKCWpdp5VeCBEXy1G1Plfe/38aSYiI0NUpXw/jJDlTRSl+6UQiZCrp8Cg2fFx+/7e24NpiVKJLKDPNvG1AkgCBnp6bV557DBwAiEeRHwyFDXGaqWBmx88lgyigAhp6PBnQJlD0ablraVwco6upP7hem9fZAQVEAZdFCHQQm6B8GtlJdBy0pf3WzKHFXKbmQSuULrN54Jdy1JyXSO2mZm9kTHzjHNmZnZ2+9ly5lnGD35yz+9XNFaXwAsiESmtKX4X3GlE4igTiW11Tg8GROFwlvz1p0/Xs9E8kBYCKyzKE3HW09urDpINmKaMyOOJCbV8783Ju12GHUfVzw1tdbCIHt8DmRiwLEZGRqjQDxqT8Qy2vjGWlS7hIouSm5sS/FgNmGwqoWJ5uq2t6dkjs7b6Se/LFovmoshuV9K6hWqZ7sQtBs++VnZDmvY1v/arWwQCS70vG5npaVfuCiUiEXdSPqbbb99vNx84bU7QMS8uo/QzmqAe93Mpr17dcYe86rbyKrOi/CDuV3FQ0vQif3ZZfy4oXmqy9N7vzc9hzs8rQ9FrUXkZeafy0KFX0fMQz+OJ44kp9T597+iddl9Gpc5aVpenxwQ4X6o/InLq5GhkdwIRFZrOsLPl3Y1cLrGuvY0unx3xxbAIsDCKyqiR61qXH23Lg8jNLS1lj5F1do5vtlLCyZbo3W7HxvrcLLvVTr72j0yYg5C839NzYmuf8Ktj8GjqdufioVDNyVbKZ216+otvZxc7xya3t6YfxZz7cfqdOxBe910vvzUwtpni5ecbFN6hNzdpkOcC57DRS5tP1x+u3obwAFsKQGDPCwhEXt65rLt0pGhiYqIAtEJHu/snJzI13VZRuDPd6RF1wR9SaQXv7U6EZuflZcXVYRowGEyyHWJ3KFkjLpEfg6oTB+md8h5zh3F5bhZDra5tyS9BMeqm5ketgoL+dqbbHcCrlvwlreDK44c1Q9LuZMmRa39kZDcle8rLl0tRtNHVHHs7t4fuErqa5rZfqFz02c4Xy72qX6Wnp58+/Srl9NW1yizA/Aj7B5c3FPBZS0dD5mZnczS2pKS3935XRnEW6J/iUNgUCCjJLz1Jz5ma4mh3jyfaT7SHB9JJvomUu7O44/vPU55rlXXHE7ucKcT8hbx7obj0lBQPP0gdgiMRiPVqpQCH62dSwa1CbgVnxqAQnmbc2KpJy1Gnzlsh1NzauY1pt9/5Jzd83Gc0hDwGTMZ9VmOVsu5o36Hl4LpFHBlXKJZVsZizZrFcJqePtG3vbIPt07q9tbNy+8enPO2QV3d+5edXWen9kfv2t9++PRjGNg01JRlbTMGFjEJ2PWqTXQyOL++oD1eXn5R6x3lfRSSIQEZrP+Zwjk/evTvYttHlKP34waMlZ384etTK2d2bmKdZ6zIu3EMgiHd/JUhBXIgTSmgu4byVIykooDnsEVwA0A6sbpFlAx2PVqZNNHAdjGab6zcGV3vd1AaHLPxWYv7m+suXT1+WmBqrCrQT1gnRMUdyfExTqyUSdU+rXNwjpMvNbbOz08tNh6gns7Nbt/2sAa6q7m8Epz/yAoMsCbNNy//5zwyZbBlqWqx9gqIkU8nBHagFvyywpfKumztXawG5H+UeOQ+KmbJPOI72e5yHs7WaCvJ4z4lGNTtEiMz2y37/5qh1Ku96bnmgx53byLiLUThOakad6LtUe4C2gFrAoTKAJNcMPBvMoejGdjbTcj0QdZHQDoWcD56MTGobmww4b1p/+fVfnj+8/5jC/y41te7YzigAhcyLlDw1iywm81v1XWKzWEjvbNp+a2xatMF+fC5FLZOw7qR85G61ClkuvaVj+j8v9Ln6oSG9pkIKM+TkSFs7LJVgmQl1OrFpgOLl5XcOFzjl2N912Cf2tccFh9uDtRpSOFNhrrAMqyIz5gFr+39XP9R3bHBrOizwYpwfBLKWap2yO7T73+H++x1nClA7Eq5O1/ZCE8Hqt9nmjC9snF0qU6+gUNpsbpMU8CVDoPn3y278/fH9x2Udc3T7e7vNwRFyaEpgiwqFMmnr6jMF61jcJXd1TLY5D2Z2FsvPe3onV1Fcr34fbGztNjtVcoqrde4/LwbaJpup0GpsUM2goSKaIqgEU7LVGx2NrZZfnLvgeZ2zqwUk7L6jYN9OpU8amzR8ko7UpCJpnJ3jEPRoZRaQB5FTWlG/9MBo1EOBlhkdXeNZd3eBiYnUFjkY2UALiGRMnZFeIFQsHu5sP3KWM+kumZpLH5t1s1DEIwrhJX3mF1/8ufb++sOhJzmpE+BwIKZylIL5KaU6R5ZD2th6OuhkNbJRzTOTdLrt7ezcXKbbp9SR7YPFyabluXaFTtdsHqODJ21UoYin42AwTDCfWX032kMATXU366oEHUbm9wK9U7X7u0d7PXsnAH5wLlLkrRqTVFzNH7BU0NM66FnFWe8H10YlJ1yhvPXRgWqOkwXI6IzRCQ74N1v7fwvs2V7ZAKvqEqsmhYZcJr2nYumFasksM3C53CJdh1s9uaHz1gQZFRv74BPARfVaaoOEkXbcGmhW56fqsoGclOWantSvPt9oZi3AhEKucH/kADjm28Ymp3Nsbnv74NGyk5Lk0tHNPU6zTW8LF0ThkAk+yFx2W8dqb0sNxnUJWyXAsCkSah04d493tUc9ewWpEz1SVvVic2Gu1KwZG+L7pA27iOeystbW5qeEI/2TQuHYzuzYztT7xmwtcsJ+fDSDnyqYwGWDRAYp0cl1dK1Dtm9I08+2OmFgYyMlWoPQbdWkSyRBv7zxcWxJWFjM/Sd9+Sb2fETBGRr4KVz2s3UCpRoY8gAFWn1dk/NGz5ILjyYn38y8VYzPuugj24uLB9t6oUyso8j5Y5N628zheMinvsmBEbk5pN6k2JLg01lilYWVIjB130NMgWuEo+2Zce612+09Qm5Q61hJTLhGUzI9tGTM/ASZ6ptVmSETjzjlFJnMvDEtHNkDsvSqt/fUd9qepu/sDDDcUUe0S1y7Dq2DAQd7zax3TqaxkDojisPB49xqBioSOxL/UouK/bzkE+nm0EBvGZlOS6aP9M84neo6ImRCQKkW166uD65ujX37741Wes9s68zhwYl+sofutB2MzRyI1cdqOVtHnzk4eXcwY0NeIKZ4pZOZqM38n/8Q9JR3n61chnSbJQwOhwOEjJo+MqIfsfXQxRhoW9NAcxLfYnm89dSyNdSnqSdbd11koUwo5srG2raNi/to0DCseeKBQJy1aadwDEa2n6BbZufIODyOtEeWS3d2mHT6MaNxcJhAdBvmhw+j0jKjY0Oayy6Hr8Tc6B3Kr63KpRvezY5N1tmnrMRkQpe8ozWxdmvl2cbW1rPt2bcam7PpcNYFnoxtp9M2IlSLZDoKZeSEvndie3dy5AuG2wKWpwtjW0M+/fRTd/nGVicO5yEkCkAtEyK11rALhpqEQmEV1azSJFH61uvrh4xL9arGX3kpU4GGYnUBbJjZOtO28mi2bq34XJZ3/4yK1PbocCISFwkMMhoNfIiEKOEUOOhaWG6Jhm/uHJhb4he5RTOl2MJLscGln50P8TFNS8tivriRWBMkFc4cHtr2gUUhEVeLzebqmAO+y/hia+vFs9f/ebHnGtvrER7vHrlmF4XHE+Du2EWxhHtHb0ZsQHLtpwj8Plh+ZumosVw/dYpstKxsJSG87op4gNgElFoBkDOT46xkTAGVXBhOKssfGBgc6Kvle6GBnS9zjqklyRK1TDY20j62sVMUeA4wVAYVX9rmZEamgGG8KO9IWjurgAGRKI+P2jk4D2mrXCfvV20YjW6koCAW0gNTeqex9NdJvaSwwXXL55k+2hnmiXNs0nUMIMYudheFoqjYtgn5xs2VgenXr//z7UA7UiZUTxy7XK1q0PdbZTKZTS48lrno+0cn+/iLjYPTW8s1y9Xup3z7aps1m8NxXgANHgVoj71gt93eP4kVs7kcO5cZJEXFohQbLSVXzpz2AiaJ7NnKtJqGqVJr6TK1bXvHxYkctaKL+JT+2s4THCISABoZhNYxSwHU164Ebn6Pw0CWM3V0nanVMvg/Cs4+KO06juMgaAtMQBSfZkrIBBs0mhJqPCURYHQE0yCMpwKhI7Q7YZgugVK5CrVy+YDJ3Aml4sPOOXBqeaLndKelVp43nXdblufMVtequfXw9Xfn/7+f3O/7+3zeDy+IPiXnBA46WPsp46uiNz8TMj87t3X6hB1nlYG4e2dY9d2ietDbNcQ06odNZrd/7GPl6j8PH95osvbKjDPeO1Zr66x8cXLRstTw82ZApeoDMu2f9/auPKHsb77R0VaKjMkYa2ElrT1XSI+CQ8GM3+6TSqolzJZicrGu2iHCxcUlx7GSLo38XqNUXmmLmFSbB38pp6ZkEvAe3p6Ymr8llRpxk7bxsDA4d62RkAkMjFZRt3lBbyP5DAJf7ygJB3rQLKbTZGragrxHQtmeTGQw3oZ98fz5nAe4Tz6auFZlMajv3J6Ydt22gL2QW9bTk+X1lA8ElU7tWFFbM8glOK0ytVViHJi33lEr2nnbuGsrV+7uzPWr+obBnuJo9ivdVc0NaaDEj3SnZykblpd36RSGRrrom9X4pDOqdBTey0KLBtAZ6Nr0pspzT18d+bix4MpSlda9troyd7BaZQNu7L2Je/egr+GAM2yyOeouVS20wWOjYstEuqxlv1Qj8RHpFEKmxKovZ8pk2nBjAJIGDHgYTDYE/fzzZzJynkybjrj6t8w4kqVsux69mAnoL7yeQWcxnqgyVZ0evN7vvNDd9pyKZR1UW2ccM8N8B//sUmR427Uz0nFj3SgbvnX/z/vTcf6s3fWG3Xzw/hNQxcU6d81uEVB1pCAKKZ+UkrZH00iUMhGuHRWfUi3Mrjy3Yc4ObozML01HjMoby0ubfzx8uCAc3rsdGeh8OVNWbHIy6x11le4n3kuVvybQMnmGE4zYCg0AptAz5QaZ0ciUqY0SWSMk5zAZjeeh3oa9UPvE9c+j38vuMzX+dbf7mp/L8Im/A9IoRe0tbvHa2kWsQD/wiVqcN+b8Tq3IClSM1tY7RsVtpkprmVQHxi6vLhhvTQ/v3d97EKk3eSt/rPOXP/ZJWsvp4iZT9u6an2IHczHQLGeINh2QmblcEVEn0rkicfqkrz++uvn3z8pw2NSw8Jdy1L/effBwZ7pvj783POxIE+lYxUyhOyk99VTs4xT3uhnfxWJnVsxK+ozE92nJvUMGmdVgYUiFEMJhjhGF58Dh+U+ef/X5/IocfeHZ/rbL48liilSeKW/VtJZ5PNcD/EHPdfAkxW6vaqujZdA6KJArFKCE4ZLdGgXHj1wtyypZKK8N79368/69w36RrKdnwL3rb3Gbs7xefVZ628e76EnQMWuV3HHYeBY2rywVa01mk7OYOYe2xrmLq3f/vusfrwpU+VfmXOFw09bqweZOnWNvm6lSMVkirU6bVZSUH0XjKUe62woFHKRGiqTEZNIpRAbX0Es2WGa0fiUEm5JBIqSkQI8jPs1L8UFC9cELxaattedy5gWZoC6jUCBp4qGWZrdWNCgylHqKZQYZ8Ig9hvbF1u++e4cx0BmZvx350SABtzXQ2a0X3rp1f5Ze0DLE4SgvswQiXXoXaFfqmbmssymaozDs4rYqnYugiMXJghRxNUqXklY4/uqVpa27P26uN0yZtKbg5W6+kN+9uXMDnGWzEpvJJJRVn0AXVzN12ERzScmVhWXwX+UJKgjxL1HfD2E0AH4CxIjyug4XBGR7ElPtoG95Et0Tn08N+QqTigrm9ue6y2mk5o2k1TENGsL4/LrI2SLyeoZ6vB4LkJPVXjWjVQ6sxjv+7nngql+bN7LQ1ZGka654263palJ29vXPOf5iEgVVi457rNqGJrvP48u6jK1g3BP2+zllSCjXoKFjvXo0g9xS1985MfHnz9c6q5pkRqcyGOh2TfADDTubW67v3qHaeVxyNZmdwUMVj4KiiKugvwBRO6RApZs3fn+y4hTBYNEQkGWiqSadDALYQBWafFrUsbyUTz+nHrObS7Ro/tbBr1vz763/unP35h+F6x8ZuJ/XZmUNktWBIpEageAZFEMW5HeK5Q5y0q9rbXFcmavOVJ5d/3Pd9HZK/XDwkiOZR+4pHWQNgcAvnifTCXw2ZcPp8bH9Grnc2uRkiVhDHAmXgiHySpqI+Pr7rr7RvchEhG/SqqzOiHN5BWygwL6+tjQFojExSA4PbTHEnTiBry3pN5mLLtfxpy1c3dhYx/fPRYewIqtUUO13uswBIYSUaqeTsCEMFrBdIFGPOhqLzsuCdQuXmrub5+YC66sHH6zduC4qGurCa8lk43VjWMEFL5sn4Yx8d/8PIFr8eqMxNVwyXqLfGw0uTQxvA6d+rx0mZrwNyj3WP+9H4t6ujcfomS3NIO59YzWJmNFSVOzUxxsUYk28jiegVcBw20d7QR+/TyWzlkt6J5hO/8BKc9/80s8Dw0Cr+DA24W0Djt2b5k/jyrTF5Pq2pXJOQmJ8M3gNlfn5FrBOC0enXPzm9UZILSzargH90XwE5NixYwRpfGHL+MLZxo4rwDK+vAzs05GRTXzNOVFtl1Ob9BzPoJIZqmcY8LzXrOf29/+4eXX/178c5QXC7unO+uEt1+gof2l0j5CPhJ4sy2eopuc3J+IZ0Ap0bsc3BzfGVpcjcTqR1yiSQaEwCoUQh+AhqDNhx+3b23yTQ2WUgN1Vxt8Nq/gNA32dAT5/exIMDQQpz0jiKluenhIWZpFRrLqBBeXpQmZLepLbq5Ye2SpHAdyqhaUgBJufSCARQUsZtJZBpfF9ql01Hkif3tmaW7kY6D/69C1f9rh3a3Vksnbj+yymFlfe+Sf7WF7C7ljHzYPvr/4+8rF5Stl4SVlVMlDVGIks/blnI2LA3E5E35ufGPj5bxyWmVIRv3Gw0VC5vBpcXS7Uej2DvDINhUYncIYG0Y0Ox+1h/9yOy8ruVUuAlGLU3hkOLFTxl8q3fSApi5UIkpNB+aK5xlRiVaNxjYHGhWDHi+e7JDoZGiudmel1yITDJv70QDckkRYNfBTy4zSqHcMpg8LhiWeX3sg927DSHWx28lcazf6G6+7Vm0WQp9aSDg5Avupe59S9bYldw6qpuTTSsfbtDzXZr+vKhaONfXVTaXqt9s97xBhACIJqOuen+K5rP05H/PEYTU1blfL0tzdW187VeCkKMcUjzqRVVODRIHZnDvcFRsZWlFyLzddrlLBZU+GqQPdWk7N3ZhYAaTTEVCAJ87iyLnK1p/ZlsrmhZLzKacEPWW0+HDVEspbXMYf3VEaZlQ+poOXgAQ/v+cTY2Dw4POZkImSv5fmi7Ctr1+ounEBVNdz9eWlq7uq5t+ZWr26s/XFzLWtiPrI3vF1e7mnJdS9fWdu99NnL2HJmNTO7/vZUiUoWvt8bep9Ki1JMRKbNqs3NP+ev5eqxF68+Te7KupCrbFjr6KJkSgXo12hUECwDAU1Q7DNfbqhaWO/nAQGq1yfhB0aXVM6ITDI74yDQXkMCbqidjuXhGfh4kiCuPs1Z0n8pHvH2END4UmhUAZNfN9w77LBMGiYg0YlRMfn2DBg8hnqKCiqCcDin56us3e5z/c6irtqVyoW/KluW1jZW12uefjVp9+oH+Y7wreGFizs7wSL02TZQJBwZeRNix6PJOb0P2CQBKc4hfLQiFGWXkM0mFnqi2zUN1t+UV9fcHk/xLvN8VlOxiKtJMODs1FAFLTa5T3g7st4WHG9a6KizqWaAraxmuZqChr7wncXFO7OZNDvwKmNodpaOxyOzk5N15eiCN8yXaqJ5aLFnEIX3OKfMxSpZL1vqE6kgCcdO2VPLSuGn3gfMLcDkQ0BPijm4EuVKM4iI9uj7Rqfa+HWBy0H+068+m+u8uNsr2JvQ7z+8cWNnJ/1EUmDl4OH+x58lguanqfrQQcJSymZ1ybSoKAx/wsSvJvv/mhq4u3OioKYNNYRHXwDvn5OpA4A2DS8jVQN+Gxq2z6jc2BxnmlYu1wWdYBcFk7DWNKZ1OGaOVrlFKcAnAG/ajj7BA3I03lZeH3ehcVxP8vIwlJ6ez73GrGLyoEHi8xFtvTYIDHLKLkaURp16hEoF1c2TMZmleANZrxsfmZgqOq2P2Kpd49lJbS+83rSawe3yDokWw8JA28WGsZVzNW2fvDe2cffXX29WZrE2m+MOhW5sSjyPTP+NSiU1RYxWqc25VOXyg3MgEHyMy6WQRFnFLU3XptlyilzDZQA4XhyJNFp/pR/wJLaCBVU+os8KdlEpmhW+I52xzM6Gb+8RY2OPwoGgw46AI6Qkm/697CvPaaHRYjoUwUGWGTxdHrVBMtuK7rXJICknqRnuaDstAfIoAEpQ7XDE+cEh5mh6ifLZ90zrNXUPmFsbbmdJZ0lSpbGMW8pJbb+1/R5TqRxvuNxR2bazsrDzxyr42jTPlQiI9SvNEoL4TChEh0nCIjIXDDLDE5GBic4flf49Hp5AMo7iGRy2QqGRQ89AE2KotNRUTDk63SzLSWsozqm2SaUgXtRn1RgMPkVxGDhVMgng64TsFdC3PRyGIY6HdZxNAmM2PtFur0CWwqCKLu95rsejjrM5tg06SGkp9dmiVNqjZY+HQNH/WHQ+nP05p69b2SLC2ZwXti7Xd/9VJSys68cb2Gx8L1Z8PIF0x05Ibzq9c9F8Oti8ENxcPVhda0ijp0o1Dy41oHyD4uPVARFe9hi5h21TTbumWTKZaZTT41EoSAU75XVJHHhCqxi0CDA0TAXF/kBvPSLsjE7jctA2i8EaVrG21b0KRas2ENhV3Z5ZzATUvZgEaCmjrCceh5XgCnNL4gzvAt8/gQGHl3pFWVwLd1BNbLf1qiAMyBH5D0N7NAaQf0/ZM/AMRo/p2sfB897zPKN/3c2sFw7zC9aKeL1pj8VfMKJAwkuqFrKchUtz6wUNc+Nm193lhrrRynzN7L+kB1iMIM5Vl4weVOnBF0P6YHp+Wujr3b51KwVQwaXoYBbeOddG5NTC7BVETCgWM5NcLRiUCbC44eyVwEKJTLGoVg/P+AQKYCAarGoLn3z8eFQsLSoPKgYiOs1Ot6HwXWNvMpmQTBoUmQ8TCwTpHjDWi6R2itQGic+Ppp46heTBHq0rUhC5XhQHln35yhMXzg+hUERBDhEnwK18XLn7Cy+NcRKW/N5y4vsxeVJWUTorO9ioD/QrzaNbB67ppZs1S9UPHpBAXlyMKoAlcsgqlqEMm8zvnLD52GxnYCIxiq4hmasZg0mXmpqfw6aQaCEqhkTCZZBIghysKe1sENSnyUT5IpCh24EUqJ4x3FlstYbbJ4+fia1AKADp5jgSk3IWVdpzqYhTkkaHA+byELkojjzYUzZoOROrmRFAHs97FJzJj4OT+ZFnHj8Jffvxtx9XPRtN9Q6BzIkYJqYk+3zOudzcIWMOHZ6Cqj0BozMsZd6s3EjadMmzJv6F6+H7gc77d3ce3symER7MVGDkMYkg1Wc1cEGlSR+ZCgMJfrs+hYSRE+iHrDfMaeTTXmwiQZIDJvVEm9HAO+Ilk4RsfX9581mGZFKzaHRqPpRPysva5R86HC7N5GugEgI/uthYekoWh9dj4omhKbS80tejuR4e6ERzPSIMYLVJKRDoMSoVoPWj4HQ7hwMthSHEsMdTYQBlhuBxEuQpBK/OZpseWf+oFpYfA4NBY2LhMBEng5wefLa4/KkWlohc99fOtKlxamUVFTo81NhjY0EFme1QkxgcSi9fONGHE3h0nVVicSaUPilyF7kFKiUZdRoGoC8YuoTLk0lJOr1ACviNyTwcXuCbtcxIMQnvvPMdyMsuFvP5mUjQODi64KI4TCjH+znxUTsUWcZV5MdQ4T0epJgxmWmRtMvPnJFPQjiPPQX55FzWKycBbSIv7yT4y0tMhKQK2kkkIoVyiNDmsnHXV/ermsYvFsLgyOOA1JCg4AG+Ioqsr9VqRUMD/+wXFCj95mg75rAiBMguNJBEICAwWKnUBuQ+XHtrGR5/L3qSDpZ2MaLMW8qprGp4+mQygU4NJVKR3DhShhVgTAjtmkxSnK1ssbV1kat5TWHxmCKCit5wCLQ6AUOThqRDMXYavhTeTgOMCSSMwU2kweVIBJfS7kPIW0F46/iHkBMboPj7sCMXD4jOqDch514E+OBTcM/MLFsWTyQQsDKU0SuaP4suWDmoccPzTsYC4sxxXBpAj6W1eEUeQXvajyWjSzvK5BA4kUPUUOi/fw9D7/9GxFstGpLN4ZsFCq8mtV0jEVApXBqdwvOffhrP8wyxKez8CgXgCyMQqViNQIBJlepxMrwYxDFbLa3Axhh0dptpMZMAUwEeB8GAARw3FYaPfkkLAxz4BHAn9oozcKhCjDTIpMAZ8M3GnIEkrf26v79/7umRn25++fTFz/Y3TGAV+FDMPpIeQS/aZiPg2+3ClYxqlrvy+dioM1GnAHMmpTD+cVzBaR3OQiLZdtav3/1jZ7UyJioUiqL+e+u/B4d0WiLOwykTCF2SQ4oFK5dyDbhqQD3IrKggFn38LBIgglJp74OdF0HRHNVMKFgBhsIDIGcF8vgZEI36ENxAcSRiJUSBJwEXOJpj8hAnQ/G5wJJ/IREOP2Iz0o5lapCZCQwdDjCHpJJFAATZvHh17adfP3gVJIUeXl2/eXPNffGFY/IZdesikU3Sa7OSUpGYipyVQhhC1n8kHYOf/VReBuoJ/BNWXbq6HQhHOmaaf71y7JOoELjo//333+EDOph2YyAaYnXkDhHLpVD+5+hc49k+ozieuK1N3Io0LeKaulWpCENJSdBK6KoudVeqSEexT1SLzGgbRiNJSzUuaWSNqbsRdWno1qmk6l7UiE3r1rVTum7tuuuxvPDCCx+//P/neZ7znN/5nn3etMMOF+AEDIjpuPW9wXoob1e1rctbgKYHqhcQ9A4fdzZ3NtfVPfBETT373pOSJ14xNNKt2BNKdqBm+2SipoTxdNh/9c2r6ddLCXCEdNhp52AH4K40dfVwF5dz3ree3yq5iRiVpIum3ibUiySjG4tNo3PTzMW5FjCRbPcVHtW0aKZ5wt6WdgCDDHCJTWbDmc/hcrBLba2yMgD1tWOjXPYd1faI+aIVf2gbr7W19eGff//9518IHozu4X1gD63cd+DEPn0rqxN/XrnonHYAvSugWktFXR1liti6fHkrLRiZEX7K+aLuiWTWK/LefdCmBBRdE+iTLUnmVH53Kg2ezEEQA39aySEaI1pc7B1bXHzodNcOoIoQS9GQNxkCEoR65njr+dY7IGZy8tU8efS1CCt5xYsYJTMkq2y4EYNmXKglXmzOV71gDqnbNnUcDGZoFHgId+pGKavUsPVdVVEu+ubH//RLwtEGN47sV7285fzPP//er+Mk2WG8Yr1unvigKz1q4nLz+4tXrqSII3vS0AAkbE5C2IHdbM/dbTF6//sR9T1u8f6a/6vN3LnskUl2cDbYy4PLvJ1LstUuK905fRmkQJ+ekcaF1eleqFQuDnxsZ6gGhrptBghkb3ph5lpBZ84nMXwREj6P+XpeXiGfkPP5f03NYbnrFaEuAfegLJRZ9kg/XqxvhzrgYGfncA+w13pIQzsHwx0nVZAodlU4AA7ASHfrw5/3pYKJd5MV8Bam/flvYQN/kb8nINQ26sTNtLTvbwWgA6KofdottKQLacAHNmwRcpEQBOoO8JptnQI0qPOFSzxFysTE61fJpV7nHoGYYCe1bHUTOCynqfYsdNlcVlKHkrZo1b53fGYGBI1NY5Cepr5h2YCpNMw2yX5ivnuvm80+bhEin0K8Khps6iziMOcG4DxP4RbzqlDg8Hv2PLHsnjoiVDc0yvzuBbsDt+DrCkPCUBG7HR+pthzf//Kl6S7dPYmHzf+0gukFy3kKhRnmruvK3MbEyiSZxkZ5s8NRammHEwPSwAz4xRWDjrvw9QLy1wFVG3UTjTZ0uAxiLqDR0WB6nB/MEXP+ejU/r5kfbgiMjexovQOuVvrSnaOD8kkzbQzsmsji1229M0DIfNU7/fgzbKWgsspEBSiK0Upq8O7/WCe1MvVHUIgWeY5TowQ3yiRTyJsn0zjzG9c9vWPZ1NjwJ48e/dyn+fOVm2k/WiUmAgss2umgg47Xcehi+y32x5d3EpWN9V33nf/nYpLlSh5dTDPY6/b+743Nv+VCWq2K6ZFgu7u7rc85bAOYPIlNe7e21SAxaWpqaABgQvzDWwnUQnL7ygJrvbG04a/XG6/WZ8NLsuFjovc9VhI/mj7IZwRqjGbtOEZBDozBg1kaH18cH0jgs3ruCxby9cC7qbLNqw3GxBM8drsj8MORDYcc84ooDQRH65y/BiF3WQ0Ng/3XuxYZrJdNbcr3lfm9jAFv6JOSMJMoJSWvDig3+/6R+Sz29nErK13XKx1XOs70THdmmQaRhu0r3/2teNe1wcO3rNel2aXFHD1wwSHt1Cl93MWtra27QDmDvFEJ7YTcu7Ut5i4Shal7x1qBakiIYA4Kp68mw7ZxYSXw02ueWShYyWmQFx2x77IXpY6PDxQv1UMR+QWFIJTkuzWwqNHwXINNgtXRaCWNq/6aexD9v5w11tByg2as3PKz2CmGZRJNK8wEFR4VxMKjwlDhz/ruKIZab8d6hT2BfbYMfSLmt77nlcsLv9O1ZY0dRnv3W1+x0RS9fv1mdbjGq7qgaUQhWNjs6krHswrtHMB1pXtb3xnlZS289AHE3AVFafsCE2k007sgBn6jhBFKBRyBmGJJ6lyRT2yInmTDpyScXRK2MpvX3kQR1NkTWBGY9vSZmQH/h/72hDamEe8hmZgvYFRBE4CaUnAw+vhzc6Sxv4UpAmkUqq1ho2NJ/qVam8R823Un0h7i/6iXN1UaFwWrT1Ujfa3RNwgcuWBj3WasJb+EeBpaHtpc2NxcyxvhdNz+8GcIf3q6N4dbMFIk5dfVRSp4Oav8ThvYdcyV7w+978vyG/b11v+wlbat5vIFEjdSSMPAngXq9muPCn3FDdI6N5KcUyRpiCspATktQetl4XQBoy7SUSKiNzB57mYVNx4mPFwqJmOZb94szmFJPkWDktOQ6CsBo+n4d4/C9M18NBCQMpvmauhfouCrNYjkSGLK26n1CipvDrrhnwcohbPj6A8aW/FtwyXbWtBhj2Jak4GF0be2tlZQubY8srxZmZLkd4jHnJomDHvIFHl1pdj8dxtzr3id6TYqu7T7ZNArunxfFYU2h+cA//0F5x0tXBp3+KD+QRCzf8U/v4hOisznpkg3ePzVKRpJfC/6yfMYcWxZHCePUprVM1FYwWknpo69KE6IaGD2iviLi+2WeGscJVIi2wVk422mn1O2UtTp3COIjIxaLaqGayDN57qxQU7R/HrvbFsxboDi4tFN1z8V1kyilwZFcTZWSM1gvkTGJgclv0zO7IZ3zbeg8b1sSAG3hQwSFpde3DZcnkFpL0hJORP/7m+miCcYlFzbWbewBnrfyW1s9jjDKwZLgLBQZy+tmj2shbfbXgEkTVlQqC6S0vygxDorkTcNTulFh4U1x8WdQ3EtCYLGJIaEb6EZkcBcSvWnELC8yZnxYmK5WTWOEBm3Dx0NGw7A3JXskICVN0NoVRHZNadPUx1ptTU6qcYXmZNAzXnz+rrXgvhB3LlHLXF+LS190sZ30xx0SVliZib7i8zk2OSY1u7uSPry+0rB5lAlvbq8praIUJuBrMKm3PE7f/j93yubE6ymUsssWKg2NxcW5Ky1EJ2t7Q80kmubUj09m5t1YJ+57MoI6RMSsA1i39vn7xeOrGysT77mCauDS0yo1HAcgdwpcCPPTkY25HBzxl8QLHEUES9nxj/V49AwsSEkQlXJcDvZgU0V2CzKRhqI3GFadf9Z61oNs9rymtNHjRnFsq7Zv966e8tI4AcNyLT2/J49IiaxJiYywmKPx5aByymT1O37Y3eyry9jeVM69HtlKT6otqbcx5adu7ejw48eo125PFK5ubk5D4Ag+Yp8c21zQaF41gfxAkGPCahll6MDar0haH4M3NIuosaUMrrW6ef9Cgs7JyWi0cn5+fmAaJSyheYhx/R2ckEXv4JAcYzwryfWP464OjOzdCMB10PJ6m8ffVtcrqZ0ELQgqYOD6R8bHUF4woCAQA2jrzVVP67xPGe8f0/g5BwLUBRmpbnnXn6X2OyCfBJwrqGOLhej9MIPnPJiZ1KpdD/wd8bkFwwtP/CVLfz+QEbN8Kwhny1PsQgMqStIudJRkMSom51YjoQQLhXXKWQycEyv3YcFDOQ4ZHxdY74rtFbb6qAFB6fMFXM9UlrrSPT7fZHiOgI3ch0r7pRrmSoh43Fu6W6Wlnn8dUrIi9XVGw9hOoF9xFLxjYSBHFH71bGNqZnRdNWDB3cyj1Uo1oHTmopQUflSFWYhGFkbG7PxVVrKHwJzWCuDc/OUahRapSyTrXbySVhsPsVa1spORDofB5IHREy3R7PHF8kkgUBAr9z8A/csxgqZkcB/WurRfT6O4vasr7JwcGWjsKO0MKQjmd46JFurlK3J7utvQdw4OKC8XQ6gwfmLOTAqWueyaQThgFgcl3L/WVORMITRkHgxpy0dJ6S64FPcei5dIeSwxB545urY+MP+cv+Es/3E+uIl/hRYtJmTbXwK1f7jhLb5CcYEC3stAmF7UkndaYeSa2Au/pfaWjPXi5ashhCKfKNUx9bJxCs53MXENiPWt7T5cAwpCOkSC30lybSQtvXh5GQoptEVy8tNm0P0Pu2PfXrHhy17DAKrQ9Ya7wxFjs4T8ipSoLnOg95xXypXLC9MDP3pfHTrrq5pi2dGIjogA7PrUCePUUS0pmAZDSQuxe/nFI60sIK1DnilHBLWkUazIPTs1nIbnyGL18dn6uv7/Y9dv3FjJsLiatti25vptoG5RZ/Q1KmxqQ2WMK9CeLWnC+HylcrOkycx+nhieX/G6T0avM6uUUvLkQkWbyDX4wu0XrZtWW1iHMnrhFc4yiU8NjExLi55/jVLlkQ6gy2lkhobSxvpFAF/buzN6lV3t8LOusoVRYF0YWq+J0l1dww1M6ZA2ti4AKI313x1D8AUnlB1Q7sDzt6xey6w87HClCwt7UshSfEkSmEdVz7I6CGki8RFeLYW3pFoYJlkYxlvEIn9of5Fb30qjDl7fOOHtjZyhOPsyhyTPNA7TWay+PKKdLx70Wiv2xIDUV2u/CVq9x4Ho9pco7P1GtftiyVNITg5mP9l70RiPdsMalVLMxuOnuF6ATWJu2PZXhXpvfyNd9jmTLBGxNHFlSm/t3aQ18dXc+rjtfBn8pMLRnwru2Y7sR/vrhPHfv9cNrspGGmULywvSINOAJEQrYbxBhwD0MV147AkQkg8Fpt08bA1rVBAbtqQlMqmpgaLvkTuNc4i4OLjP1wzgMFdRQM3YDyRhs7pVMq3zMW2ka52Tjq5njnKm3s1JyKnY4d90lmzPRb1iPJqLU+dKk/lnbYZJzWIPv2klBASHt+kIEtlLImpLdzsVJ0LTwaXf1T0vXPs47HHv2e0jU/PjlZ6BJFEQV6kAvqdUnqrL3N8gEsJDCqPp8ff7rjtx20otDCVKiIPW51XTMxvTEwMwQL9IPweSt8Baefg4untDOUgXW0KjZsl3Rg8lFt1+MoXDNF6V3uDsF2gKKbpuAOPVNMHuLzujof8q6uCrhjgjugYwVzF1VFeHYNASMDWt4nGQM0bRx8cMaGBJYk444j4tQWmhn2OtHVCZSD0jSo4WKyBBS6CI58tzItk1HUXZBoaoqMyz6FQsRklLqiol7Fe3pTiiJ63bb3J3EE2uqzPyuu3B4mn0oEoQCPhyrluSd1J2rdDeOuTHCEr6Qvti9Imxcrg7KasacS3LOyBXFXT4w7MvNh1Uxmt70rElW4qFiY28FVn7vsVWkq62i0pQn5kSmeOCJJFM9yl84H8FaZGTU25hamyqquq2fWrvaIcApAKBhIe9s71jo3xigkp0FUubK+QY68hypsRJ5VVYcLc5y6moV/7R3ApePcsxexKOzbSY5jrpaxieNIpDCYBgL3LMPrLHdn3gvCUBKxQwhzI4QCS1aqx1dc3yMorKYHoT/A3ytUm5Y8I9hy9QxGKD2UV3bp1NI4mLJJz5POzAhI9/Enf30PvB+V3nO1UQqOcTLWMTLkygWxiIUs7ScxqKuSA65WC5WwzWPivp0btdfT9rnUxJnHlnx05omOLwWR89tmxAVFxPdHgkCMeBqKSefP8nvtulteWIhrSKxp6EFDk+PrX67affoQxss343IxSn0/yZUgmWILOBIvacLSTreGXaFsV9BMTJKSJwXrqO/Y+EOaQSxcq0l+t+qvvoke0xPheQbGLIoj9FMIvx8zYpKm3nMCjHm7WmMAjNpp7wKuS/6CCNTglJ3V7hYsXpJWb8jxdtQsBaHQVkfojV0wfUSwkGZxJcnsg4VdwcoqXem84RmBvtE2v8jz2G3Q1hbCY+GM+eNUvnTK+Ls/huF9dSvDxsSBey2IOjK22FfukEi0CHy/ldHLIxYjQX5/+2l+TYQt3fzt2IDQJDbx0Alc4WNw+MjHRDOYdlwCXgAC4ZQNDf7S60g4VPSN3nyrtRhmD2JaQgSfRc73jYlrjMnEPaU/rZ7JSrlaHEsjtHn5+RWYYIL2CmwMqMr6RYEwdGfLtZg89qBxZeB9JhVoQnEGwMVKhOCV/hE8W5Xng6F15CXWyzXebluXD5dicinYmOQlrQaiXkHciSLkwatL264zTEUsEbATZ0Yd49SzFLcE9ovjbbx7aa9rfuJGTI5pGfF3z69Py6zWenhi7HZjccndK8Uxxe9M68RAusv1wuGcAkEtPolXgGkavRCl4F1KvBotNH67e58aqjjsXJBZ265t7J8bd4RIJCaEtRLzqJwhlHU8d4/SN1yIDCw88iSYUj9b1pUj7Kkfg9CyVNqasCQr3xMAlBXAMdLjiOjqVLlBMjPZc9Mi/dq1BMSIdWosUe5gOr1d0OhZahHjgCF3Hdh53VVI13uFkpHps9cXj1AR//whHf6IjwRKQ+ampR8x8lsYtKeSxGcTTX3/56aefntZ+/slOhGtWlYbRsf6zOPez/bl78Fgtze7qkzuUsvWcgCNgUqaspI5sqR0m/tJ//ZacoWlTPUyrcVGxBZpnd3y8P/HpTU+jnYEYOxgIosl/+7YNPIbE8jiolHWAiTpOUCd7/+xBh4ff2khpx6Wt7aLjKS8adTizWtzUI5Beuti8zmmUjDS2Ni5IRnCeuUSRCEexdMTHGzDaPPZjbLBHlBCULB9INh8+/uYsZGkRvAFs+lWiI5Y5VjzTlu4/MA5i+qt++ul6je0nrp6njQx0nFWVETpmRwjEWh3PalPj/DqTkoAAJyeVMNsLUbG7dqF+IVYT/Pv5FGts5D4v0xOm3j8/fxkeluFS+7T6aQtbR9f7oAMaY2McsvJ2vh0Glpri8ckx1t0deCr03Uifd8fDHMARxYiG3RZc5HlS484HsUlSsWIwZP8HjbgOg0ZpSpxA3unIMdJKoOxWtbEXEnG4vLlrOA+Djz/+MrQ8gYinEN3d3R8/9rF40Tv28M2qRarFw4gbM70DS0svyP9xdN1RTZ1R/GXBSyJDJCzZIqM8QhiPsAxhy7RoScpQRKAoEMoD1FCGgEUbyqbBMp4lpBhBoYhAFSqWWqNWLIgHDbTa0KpFK9qatnae00s/9PgHAudyv3u/O38/hJTLZTKKct28ig0c/Pzs3IOnu+XjvUOGDCLdVGRnv17ZZUNDkeLPsmaLetYauyMMys23fhwE8VWEnCiE/hCb+oYpoRCRTgJNHHSuuCbCiLiFZ5dbps/ZOLUIksuTe2Izq82men4SnI8qGLh+/48rtScteVzXWDfjj77c319zoLbs04CygpjzuWl7BA9vXR+7X91a6WjBtDWwsfG5OPZ1mOA1DhNDbeXeaoBmd3QPPi0xiKx8sHhn8XG8Y3Hxyqx6fu7ukxVvhNANaVRaLSbDmAE9ImDZCgmH7EQeNySUehEpH3yYoE+VvUHZy6K8SXt/w277iLg1UiSKjz0ETYKdCQpRSopHegYK1UWCEDnYnJgWbQZk6l+EQLFcefvRi6vbjGOTr716dfsEIB8ARAoAfcVMTT1Mu3jkehTTTwjTXqGhQjdwAkctnX1ubd63b//BoTPXevuDN10+J6FRaAYG3uM/P//3kSH3XR4VRWyjL0cuJyaOqlM9l+OhEDs3/2BSkgXkWMsrc0/UT+oRgtSCLCKCUNLl7SoRn8JhtE/nqYxbDBifWqxuS08Q8/KpetZQuTL9UA+l81m6aXnEwtc+rw0YW3z2FXSzHFK+QqleTFNC3u1uRaFZZX18qkTKkRoVpH19bynLUvDzjfu//vOq9/4fMLExkVv2OSBWn29uipo6RBUe3D9c6O+/x+lUUy0n+dSZ2hjB/oyemutdAr77qksk0JJ1h7V+fXXp13sBUOBGbbEqhK9ybmhxifT19VwduVw//3jxZX3xaXW9+sndFfXdecRfptRqlWJES8eBOpRBuEuRimmN061wW44fZZ+A/hnbmqrPg2TOmh3BkqXHNYamp5tcnrOwOv65JQW2sDDsqw+An4WF0cmbFYS/g9HDH191vRZgZRKQ63Lq68Ds28+/PXt7/FXzVG1B1KGPcw/H/B7zU1ngrtidXnqNInLYf4tdl5ljYDN/6sCeCxdg27knqmbw5JmmvCyWlNmxOvLNjf7BF1fbPFGUiikxBrNKMhu2MjodWZzXUX+6vnxkvkQyWzw/+fLFJNwzRCWr0mqUqEyrwphvB6DAHAgMzUqyW65Q0iyxhIQUTI+ar+8FqDlseENZw3IFSbPaErplY1TdTi5mqtjF2cCGBpRCi9FpZE9uzcNrcK2uPX2NxnUXGru4jD96dKnp0cLzTvMzgc679m+ry/n9UExU0L5hSrrVRxkYkf7hnlMXcpK30T6OEgj274l1kLfcaq4IKu13YHCl3b1jp5rikp+PtL7g86CVghH84o6Xz0Zm42cj76rV8akdSxIOf9RxpXL27uLc41lEJhYrNetHpaRzUXcDJMCSoy8iIxzacQAWSHjzQ4oXFxgIuF44nk6jsKCGbGW/xS9EKo3Z57EVMh5CypPaS3EZTgUm76JX1378HjYwf//7pwBpgKVZ8L17q8daBNdfPQeE1P646kNmH5n9WDZwXGCWAXt01REpexNSHPbbnBkTcWlOvbEwLulw6MCZLiPz0rQ4W4rtWvh7AsPIojuvbizMgCmiMqRhbvZl57PJ7LmO0+DR1I8n+8JmgP13Ng8MZw40o1WRIApoR0swvYyBgMKSRQw1Cmkssb9G18hipfhttH8H4/F272Z5YEKFAtA4/coOBGwb8zZ5nSlzsJDyvPy4KPwoEcfS5uGl7wemrv3xSeCVv6fqAgLsxkvTTgw6pV2vqWnuF8Q6OLiGbM95fY/ZhUOuoa4Ob0Fnn5XOGnbYdd0piyFy6J4mydiyXPNrN/pOljZ3GVRmO/qG+5Z2dnaWZ6MgDMcq7/Fia2vl4p1EiSTPpyM+/smTlUrzqpLlWSBoHQUKbZVSpVSCP9OQWjq6BajCqph8RNgjZ1D0KCKFw3Ahh7uRCe0y/Q3vmDpQRPKbOpHT1ZPODKvDxtsFHCqVDtMJXB68GiKC87Tr4ZW6mD0//nnk2vdR551plumDpWn9ZwdrthnFZsZmhOwyNgR834+nNiWwQhlSfwWbIhIN0zYlEN+fumDmny5sLEx5ahhbXnT2VnLUlevXyo/1nvZ+r7ehbWTpnjsk+8ENgD1Z7g1bhnMdmrzReEl8X9jdx5PA1j77Ui1Rd8QjShkG4pC6NRVJELaYTA+3bUcphAJh62Eog6xoNOV6wGCPPb7BFDcVEXI5KSJOmNtS6FtpW0TWXlQ96rt+IAxHSVK8Bh6emTpY9/S3P76vPjRQe6UrLdp7teZCv3lvf0+m3C3OLdPEcu8W14Kr0PPz8MI3YkAhT/P3k1KJWJfMCNjhKExpnHKLDUxtAWSOh98C9p9A0LLmmzq2WjrZGrjJ+WJfZX1gQ3hY5fx8pFwTneebV65euZud/eTu7EqHWl0PNqOq+j+cWXfQBIGiOMJHKLhYKKRhBJOL+ZtSZRtpUioQtZGENUbXTNNsaQycalQREWDlD1YGPHefwrIzo4KGB8R8/+fUD7+duvLHbwOfn/nxTP+tk7AjDjtKLcbbDzbdPnaze+dOD+nrGT+4fuax2/6z3RwTdyhwwpI8JnQofKsQylj+ZMahzAOlOXYRsTfudx2IPTjUmJY9Xt4X2JpawnQOPlnvDZzacw8aJO3T7e62o4BUP7rcPqouWR4dhVsmQVAl+OYqjUYHHxqCwDBqPlqFoxQwdgrwG8vex1lCMkIBn2L5KyiYbbcC/g/KtQqO47DlQqo1jnK9UCrKlDOZrqfORz2tvnCga+r8mT+mumoPh3vfWR36CSA7DoXY7evvcghxhabmzs/27qTt3Kv3hj0tgY6bvg/sekA1bwrfPoFFsCJiw6P6L3zZKAAk3YOFjRlmrZ0T9b9WPns86pzo2daQJ4mWhC0+Hi1u5zOKJZOT6uLiGclysWfJjPqJxB0Ry2RKpQrMX6fTKmUyJm+deZIuZoqUCXg+1+tDkhTp5PJh3boMFIwk2XTIFnAutUJOlTqwoQDntf4IQfWKF1B3prq6LuStWIAXOd915UBsnW+44ERm48FL3fsv7DeL3UOjeCRQvfy2frlpt/1XAFTAsgeSMRhA3IAD9RIbpnoS0kUpQ02bb9a6DfXWDIUWRmQc/KF0fKz+xcKLyfLTDPK0JLIleFtDx+Ts6Ghxlmfb3GT9ZAdwGkvUEtvZeEc+oi+WQSij0ioUWo1Wi4uBeYK5A2FShP6maD4rAt4VIlRHKkTp69xzGOQ9dByn6nE/pbK5HHIYhbfUmoeCNFz6BcGUa4bZ9kO7ql//IacsJyozzge2ZPZkJERdaqw54BRnyAJIZOj20r70x63hovHo1lymlE7BKfR36Hp0DMI8lmVKhJnFUO2V7vdsHP39dW8NGKfdK62/s/jgWRrcrdE8YDAEW4lclsQ7x18ufQSl5M7RZfXs8rJaEp14FxHjMjhKraIdZJGhYjEkQUwyS0VYcsXi7pswDaiSA3YXrtBpdSSeDxmBngyzFr+NbvTiihQYGBmdCpVr8GehtZ+4K+rcjIxff/23uuo6t8ye2LLYnoxConE4QwBhuIcpa6M1yvbDv9y0l86y3PA+xSEeRaGMT7UG1g12ylspplKom1gZ2Bz1Xnjwa4douLv2932DZ3/uHO+rvzMyNxg5PeMbb5eYnZfoaBA9sTAWduPnIzfaimdKSkpmRi+rX75E8tF8sVgGWiEslevCQEZHF62t9ZB8KlXV7duzJlfIFTo6kq7TrYlw0AFVxqRQQE0iCpdObOTp6+lBm96ayvOjFX73sUMSIOFWV58/dTRku6GgpXEIJu799wjc+r8ZTHMnyGGanv0GqkUoHc/ajkZ3VHbaMOHLIVNj6u2G4q8eO0AxbHbkXt/SwotfH08PHbj/ee2lwbPXewOzO5f62pyzEuN9shi+0XmRzknOzc1HLg4GHlt6ObJcUjK6PDL3YBEpRpCSfKVWqZRtQQmZ+P+zBmeaxJRxvnlV5M219h5SoVCK1kilEofPbqDgfLqYoLD0qJjIX0y1BmHA1JCD3TV1H330kdnA0+qoWyE0xqZ9TkLQ6U+xoZcuweTsydBGp35b6mefUu0L2RY2dvzKxccvT0MT/BfYGITfEZa+kcHy2iGSH3u2ALp48aAvN6bgVFdtauvYYGTRvYl7px35HWkuJpwsu6S0hrbottOHA82B8W1poS9xZlL9uPLlJNKOMEr0UbhpYhyHf1B9lKW5KdeBNErl9Jqme22NHNa1a0gFqdNiuBgnRBgmFiF6NCqFwcMJBxabuh5Uc3mI4JvOba7bt8Z+8klIiGGAlwfFxBLIVWjEW3vdPVwLRVLWcIu5o/DDd6nWEYJj35wIgzmXxZ5Idy74DxRuK+rPtED8OO7hq1eP2dSkrT5brPR+LefoxfH68Ynw94q860eKXMKXszgIP6vExaXAJrLFJbPX5tzYeGtYsWdla3n5fD2irMrPz8dlYjQ/X5wPmsnHNAAQtqbTalUayJDb5XKFtv2mRktqtWBP4g2kCq6fiIsQKJaAQugnw63BIaBcHv/c819bMrZDNe98tStA4G8sDM0wGoqgOX6RvsXv9QQgON6yPVNwUIpSpdduQb8jrO/FsxGJd5YeTCCDQ0Spu+n+Sg4RH/6eS1PcPl/z8RcTZ3Mu+bT1nrwIuLYXJ1pH6n34lpYISJPYOd50+D2gabMJH79VOr5Un9QxOf+kYwWRoei6IPogCwgjE+vLQCtrEK21a9emNRIodwxP35wG/Wi0KlAeViEUmzhTeCgdxRMwHpUuo+I8FFzzjra+kdt3fHIAPMlswHX7zi8zMkLfEgoT3HcpDm7a29QDhJQWTj3pOPdt6uarsIb4/NnIyMvK8mQ+HeVB5rXuRChCUWZYmLmPoVNshfNEa/PFyzHmZwuOH3c5XpdW2Xns3qNA92D3KoZl9GhqW3BQslNQ0OFIANG62NHacXf+sXf2E4QJzNn5Yn0QBQ6Gw19SNw1x54yqfW0aYtBpUrM2TZAaHUEqxbgpkTfGjwmGweEdVJTN1Od5oQiPHq1HMHeE1fdd/Wbh6mEA+bfYCrwTGemWIpE/jb51CGj2rxi6ehiXdRfi1LIc6pHbd1bPPhoPdOlL3ZwoXQ8h13MvFOYw5d0AS+ZkaOwkBJTxiaLL8cGDTWEuOcGfXymCimzqrRuXPZl8w6BUX1/fzCCjoNyjkRcnUo+NpS71zadG5qkROHSaKZ2Oi/NRzBTDUTahmdaRSp18GvTRrpWvydtFqAoMBhOv36q0b/tudwZx9PU3weOqr48vj/kEhXkmu/G9zc2vPmgtTS7OS9q05c29AVutEOY693BARrfFTiv2lo/MhBQ6+m71o2jnkxM1x/69sc/9spOjO1McAHqF103GNsWGI2A7YMjApLHCpsV8aT7VxbPXx9AiqbbgqWHFzExWkHczn8uUeCZKgp18fZ2PNgQHX7xXX78U2HHu3Ip61BPBYHgLg3dzR74MA246CorDhVJCIK2dntZAaACvD4imJTXD4CIwVHagFPqYt/jcUkc+D24I0rnwYPycU9y2JBdz7xO3+5YakizZfn/t9GB/ttcKeJatrBgBe1qeHoVhyJrQ3aa7ebzzPz68Dy3dn12K7YJ8wr2hbA+0Svo8PTq+XjOhOAhDHUx2CTLtjFt8vK9HOYV7Gm5Ojvkk19gt8LhLeDwTKQlzMbBFHPPUh3M2GwQ1r5ZP9J3I9jkKWYA3IpPhsvW3cgeGESxgDKazyXatUquZ1ozCI6okVARIptISWh2B0cVit5amwCv3z7r3hxUF6jN3iM1TR/pqc93cDbb1erf4NvctPOt7m4OYFNrDYJQHI68HLhrX0OTiVQabRYCr4FkkI2Y1TQVHBaXPTwdtyx45logweXDABhEunWbk7yAUGRtZ2rS4V8iDDGza3DYHJyUdBUh3OwODtgZgYQk/UR49G+lokDj6WpJhUkN8Q2qlc4lEHRmvXnmCYGD36x96IAybjbFh5BZTye/Or3SsaUFFVe1KJQHCKCGy3gA+zyCuILD2x7P9E1+fKNHXZ+g7LpUHVocEt9sGh/f69jgXlXd6SjlWwHaeQggBPq+CpOlxB+xivuVIUS7EPbyAG0faXMwFTvtO3HvV9fHgqo2Rs00WF0VNOJ4dTB4j1MzORCQ0MgqOc8xriC+uAOI0o6SggZNnjm4v25Z8/HhymE9bcXRH6uyoDRCSbU467FNhU+SJ2I7mBQeldiDpBCaGo4/ToT5O2QB/cDq2Nv78xeSi9xp4aPDRhBJCap1GBY5bJiPifI6fHPf1ObGcVTDovGw8GhmXaxzs7OLt4+Ts2x1Tlyi1stpF+4WT7mS+cDstiJslo0u5HCYH0iFrzJrHYxTkbjMX5OYOnowpM77ZY+cocnRniBjAbtYBMSHZI9pihQkN7OySgOXXwMDEFlF25xa41MJ4T9q5ic7svGiEOdOe5xkcGWy02S0pCaBmi8ydq2b4Bj42owhdQeSL4ZrhbDqbrcSgv4nRq3y+vvrrgxf1vu1QGlBpwC+TJChJtn5UAue648camosONB1J5FeGGbjn2IQ4GyfxE32dHY2vDBga78rcZ48yLW4KVjsvVWS53YS457+irjW0eTIKv7rFJEpVpnXO2Qzj7NAWO9tonKs6O2O8xISBrlD9o42KqcE28craiIqXlnrZQjs+3MC1YIKobPXGKqbmhwVZtdUP66ww7A8/LDjF6S9/eepEz8bYRprmec85z7mlec+AQHLW+QRsQD8rQSJ79g13P3LLjP9CbAGjLiPIEBURThwdqChD7e+9tzxCTUzRyRCDYWbAM9ycfJL+8O6nx2OwDWBU1EkJEmFDy/p8ExcicWp+XmGme4GxbDT9jILywSEvp8C5RyHHvxhi/MXVahSen3Tw60n7jup+FSggWP3jR+usIEQhsEBu/oUHYBYFM8XHPOTz1+3dOH/uE/QVFEYfbmWXXn79ko3p8NlXXjWHAre//PZ4+OdpemIM4hBEpLMgxgYl7Fx4PNT997/wyTuJKTRHXT4pkSGMrbSPDipFnrLee285eckEFqAnpy47mwpCGCQDN1744XW+YnQt5J/0fTZD09nljIWoqQDsYy5w94d43cmqoYlQowlHnw56gZgJQaYaHIVaBaqWEysrK9+v5srh0o95qHLAvKxRC3Ce+UqJcR657rZncvc89k1sZgr3JoPWextnX0RhAc7Zf3fqembv3Mun5z72Xnjjvclnv+TGH4slA1MkNDwIAp40etbpV/0+cfn0udMPPP3k3V98/fWJLybPJsgoY6S3T602GB8dMJeX71wYQWyAvhW0wAoae46CeflQ+DtMTITDu7vh3Tcdk2IzlyQD/NmhkJD1ZP0ir9GX9uw+CgKVpUDi4CqZjeVhdhI3fOPy+ImDk9vfhx2uOvSpPPBaEOqbzS2ndJ3v+Yfe4uT0rpTCSXzBWN4Y8wbGPFzJeX3qwmxp8zQSGGDuwoX3PK/ff+F0eoc9cxTGJaCau07DcIgoS6vj40/eeOP9dzz79svhNZ5ErZXQm41Y2WYUqMhP8yKMncMoD9zVLqorKzlxwvsUcuyXQw9cNxMSnl9T3YBpwiRWpzzzblIR+SzN06KLcc0B2GmVSP2DJk+tf39CXrgYFGUyezurX3UrB4JciBtDxQBLX50/PZ8pbTi1jbMfemnNf08olQpS0F/xTGBzlN9HH3JhH1CRRH58FelFgffeG/HdP/F+JT2xjI2MEiCzEp4ivbN4dPKGUunBm5fOPLO0uC562W53CamCvejj3hjz8VfC/p1wQo9HDHDdTvtghh19iqCF8XBi8orzPIqgIDZjOo5GekXfvCbyPXrB0kSR52trCFnB/D+qIbLbcLv2qXB+NJ53OKfy+Vcn6205ln7y7icV6KydCflG8NqtLL1nmtfPPPLR80+CYvSFDJWNjnnp5BQtMvOR0hjrfepjCBtPBbw3XX7R/a8GPOdedM212Gjwn3QBjNnvw3HeMS/ZevfxF7KZhXNYkEH7hSeeVzhVUNTQYkMhyeRfc96kjozOESTW34QCiEAaLyeepwOerEhJGFXFdImk9GjINcUFXTJYdk7TAzbEmWDwHzDS+nYObktbzR7+6CyWGNiF7ES30a188Pn2t6uJlUopY+SrllWlKMX/Vui5Z+7m4dKMhUyABxOPTpFJgeNp7BKP96IkO3sVFsCu2Hgj/OlfXjTlMXTstNnZf7CkAouklNzKLI+cszj+IkPNjWQp1pU5v2KZClfa44ROuah6pgJPFXOq3oFx8lH9ZGSEJBFGYdgYkhDt0ZBEIilf3TSzGoXQfmnBYK+Z00ldQxmAEo8P4aRfe7kbi+2+3lMch1mceV5e/aUT46K7J75fz23XS5vMu5ZFVePVzegz2geR55/ESMKLRk9DT41IIRGjKbq0jLIbQWszG8AufOLcczZ8Mw/99f4H3/x2dHJydBaQ4LSEC+EYq5hb1MRElnnxRfnC6SkMsfuaSSGKomu8yJ892f62IRcDLz1TKKuNQq5TqdfT2nV6uUsirzeZlCSdhICEEIBxq1oS1OKyyTFMd10DZUaPM+aUvsithd98eJrOMo6yx/iERPvbg07TjhROHnR+XpcPy4ebphUcXV7YmA/sjvNjC5lR7ygiCQKhpmcOoXMO95G1twloShfMLVHBCS9592vf/fDVd799sFRFOAiXlpjF9Pj03htz1zy1dTj90i+5CE+SJhPdHCERovgA7Xv6+RjcNPHL9w99tz4vNGKRRufkqXa302rXixhpsJlrkCHhOImqZskEU+nxmqZrySSSaoMQNM5B/f+AUbm9EL10ztgC7QjCYqwZazd++fbbTqz+/Ve5N8dfF0p7e5DbUJeMUCz7/HU8G9y6hGKrCF2JSAwzUGCatrDM3gYcIk4RKBi8lCQ9UXgMy3ffrtsrggxgdF+EYd485DYoSKdpptDJ/cQaJKuEDrMIXUSyI1hybGo8Xd5eXY1NPjn5ZKQQSwwqsFloHfbYPIrNXeM1SDpK6wDGMEsKJUH0VDya6z7tSrjWbKGPdtrfNtXZFLW6s8vQsEU0xmpJ3dVq9rhwXa5TruTSsTIoarHkHO69t7lFYWwS6fyS13vJys/TcO2kJElPIUm64uykxbLO3iZCs9KV3szyHMGS1Bfb6x/+8P337UYCPN0HM+nD0ht7JiJHLvLSuZMDNjPmJcwxyjrNS09JVxNXzXmj4Z1KbmW7XJz0N3KnVr/d7g7q9Xb9aKAbGDVG6ZqSZFlU1RGWMaQUK9K8q86omqupCmoc/Fk/0oGYWlwo6jknEFhGGlutGr3xYmgtscidODiVCJf+KB3u7f+xtbk5RQVJMk5OXEEYTHglvLg4NkvgBNDXueclRZEi0eXk7OmzpNebP+cpsq3JhcrOyneF8Z2KZiyyouIw5tYbb1QJ7FqSuNqJVaIZ9ik8TpCGl+wJgpQnPGuxnUSnDeOjwqBcr3/byW0PykAE8IRWaCsnKepcHTwGpIqorSpom3IVpmWrMAYQe2jQPviznQIjq8G00MP5gTRG5sCchFU7pApMerDaLcf2epCgbezvL2TMBWjhzHrZS9De3h1y5+U3RUkiCNA7KeGkjq4kSAIHapZI6TQkPfZYIgyGlc4lYgVesaemskp2uXQ4rpKj1xpXnmGUXnuZxJ9CuESgq2bdci4hRvvtbwtg4J12rnCqDkgahXqh3+104RMxR0cdkdV0Q4rnEWKNqmYaJJ6SqJ5QDLnRXqvWQ/3BwTcnG7LaU6YCmiuKGCINOJTtNfsqz3Fy7rUIIxwuOpsOI/wBs9kfq6nULIFG0O2y0LyHKdFUHFgXyApWiSAA0xAMsMLoaQTOLoYXQ3x0/MNcO1fTNRGxHsYyS36epX0YeXXQXL8DEdgEabhXkES8dXRUbnUL3dhupDAIJ+TGSkJeGdTBYQrtbqV+sN3UwJrxIeNUg0beCkhEnkxJLPTMyyq0AWsuag623x6U4ddaj6V0ROvDpoBhaLrdUmMnXpMj3JuM6TClqsKUNqFU+9GqBqU4kck6e29sOmFnWQIcxyIRElRaxLAINsirvLPAxbYtKmt3z8wUEvYw8VhOYuiwdNolpXM8oM+qaW4CJ+u63hPgTxbIs9Fv5GJrxb4ty7Ytl/vdbq5+UGmAwR21d3T9nxWLS4ZB4IbF4nGgZQMZugtTALXX01Gt240Wm2trNbdHGbrmMSTSa+Cox7oa34QzMq1G1+ktTkMV7bzyo2VlLCsPhJ5xNs2areb8TBY+GfRPdCekY7UMw6M+EiRnIagysup78rOlJ21BjijsMgS1jBkczXhA/xjKW0FqJDDn2oPOtwJBSIn2SrkVCRebcrPVanK2bPdPNcoxuNe8DATQOdVSJVgxaTYOhiZJVTKeMvIsC2tpSHqv5+oG6sstNarWXLdGZyhdZ4eGUqUsQ0d6bVFgBEdhVlcSXHnR/NHZs6CChkIAXZ23YE2dReZNZ0/ZgrJtuGIGCdkXlCtDMKwRJOJ4nFVqfHQJ9n3x+xhB1zGI3eegKew0H+/1jhAkysM/DHXQgU3vDAlF1xKyLPs5QRA4yDoFwBNJF2LrCTvRj7WPCoW+gA8F9GMM0zwCeJR1IdzDexuuBGcYNJWaqtZARMqi3EA1j/LgY8AYlmP3OKW3r5TbDSUhW6ZwT2vFgoIgD0kDZDbO3j57TtKloEuQl4bLA0s1C+aSmnf1AI5gnpA3ayEAszQfijZzMUG2wdMxjIkEGWVOmsXJ/NVeCYfRSqfTPtJScDFCxI7FShxXlCOy6tiMHQ73Y7F+P1dvDjr1g9VdgALXHseHKsrHwSAsKiXpuiQNv8Geaiq4vev2etDAuaehGWw1bxhSyIVhp6NE1ZJpRRo5uf2z0Fjd/r5tVQGNQaRY3dh0Mmhe2Spl909DRGr2eM0kkdC//n67oA3XgxY4ZWx+6QqfWGiu/lCpJ5DoQcgzSXqmSSANYmg0BsyKm916+ZRh4MsKV2QYh46GhJaqtmSYaHLNTt9ullfT/Vz75GpFx0EADIiRj0vS0IkADIgEF43A2jQQvbjaiHHyOuM+y6lGii/3TUBjmqbimLGCUINOb/fEoF5YHPY9R/MpimJNZ5hQmc4GvQWpnTSEkmKxL3bf+fqbzxvaGGsYiqJGA8nkZLqb63z0+cmDcrJhAxlBFMLG8H+9ywD/aPVzu52eiJbprCMoZpbOKqbKMbYMdm43+i07AnPLQSOWKB8TDVzD8CcoQ5TISaArBGBANygPGZqm667QLwxW7HKYi8USppgb9GuKpZv7lmkpi42IYndX281+rGXniTx0NuMSuEwW4uejJLLMfQADAhbw7EdfvQ273Xz9IbS4JIlEuijSocn0h6vfvfhRfTtNh/nZK0lSS2YQiR8L8GoZrrTc7phWT1AcRTWtMdqkBbtoy5H5mtAqNN4M350oy92I3NTx/1UTN1y+39cYxQUcRkqXcAS4dHeY47QEOQfMGIuVVxPldr/ZNEEscCNT5mLpmiA3BduEGroKdEZcnapubf5oEPBpSRJ8aNRIHct1L752/f2vfrT+2mNhkfjHBKLPQ+yFejeX286dms/1CcTCFzjv8SIbtXJLAYdtDHTLpBTFhO+eSkOqwIFCpmu02hyU05F0UU4L3Kn2Dv4fmpQkaaBTB4QeVjCukUL6MKa4UKepYsQG98t91E8P+t1+osyETPAQAwoNi4OYWlPk8UQVoJgwDohTYIJVI05KiFoGSsjjx7oRX4r4ozPPpBNPvsxTYMOBWjRKvyzUisWV8EePnah0Oi0hRKNjJKBKQx8GuHKzV27h8E49xeTgYC6khDhFsMt+T4gTErJqD+Ryu51u9g2AP3zdMQ/oTSGkOk5J+QeMDmDcnr9ZsEVa8+d2Eivd3Ha7obbK/ZUC01uOw8WbzuuKb8tSBF5o2VWAAk0nIw84EboanJbIU1sGDtMOPOXx8AKUDr77/ImTJ9YT08u0pvFiaMwzMxmKyI889Mxrq9uV+qlOM8Tjx2hSEC5bJttTes1mSgpovNnzqHyEy0ajvAKctqZEm0JTCRTTdqNd+Uguh1KaJgGO4WtJ3VChiIEX9/B/wOAInCia6PeVHtIaabkoxDrtdsG2y3bYFiwDumamIAPrm1wL4qxlMGmzGs8PCS8O/haUcC9hSASZJ/MSH1qPCSG/b9q/FBm/e60RGec0l9LtGQ9Nc2v+RGHtpcrO7kqioUbYWfxYwGHhpGoNGA0zEKVpJh8CGCFaUKJrRYYTwXXKMlcscv1BodKRi66WxFPHatWBOSA+mrQBRg46llLI0DQuJDRaO4i2Y2FGKA+ag263227JQgt8BNxGTFdyES62Uh+AZXUrPQtKUytOAHNJVQmvFpuQagIecr7YbzQviPo5mvdzql+Z92VdXdd0sWYGok+evbYSkRMNLryzpuv4v5Jya5prSGpPrQFV1DTW5WuUyfIK2N45gECFyClHilAtCpDbDLr9XLPH68dGavRavZouaVqvZ9KgGT2V+hvHixOsxkW29QAAAABJRU5ErkJggg==\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"CO2 card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"ppm\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "co2", + "carbon dioxide", + "carbon emission", + "carbon footprint", + "carbon output", + "carbon pollution", + "carbon capture", + "carbon offset", + "carbon reduction", + "carbon neutral", + "climate gas", + "fossil fuel emission", + "carbon cycle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/co2_chart_card.json b/application/src/main/data/json/system/widget_types/co2_chart_card.json new file mode 100644 index 0000000000..d6fada6ca5 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/co2_chart_card.json @@ -0,0 +1,43 @@ +{ + "fqn": "co2_chart_card", + "name": "CO2 chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAt1BMVEXg4ODf39/g4ODg4OAAAAD////g4OCAwyzs7OzCwsLOzs7j4+P09PTIyMjV1dXx8fH5+fl0dHQgICDn5+fv9+W8vLyQkJBYWFg9PT3a2tq2trb3+/KQy0Y8PDzf8Mqg0mAuLi6v2Xvh4eG/4ZWCgoKIxzmdnZ2srKy43YjP6bDb29vP6LBKSkrY7L1lZWWYzlPn9NjQ6a+rq6uwsLDH5aOo1m6w2Xupqak4ODjJycmYzlRTU1OVlZXaQn3KAAAABXRSTlPvIL+vAC9A4IoAAAjoSURBVHja7Z2Jdto4FIbTaX/GkiV5wytLgFBIKRCadJvl/Z9rrmwTNwTiJMMiUn/Btrgm59wvurKED4dcfHj3x0Xr3Hn/7sPFuwsHZ49DGu/fgAeZUFnhTXDxVkRajYhhNCKm0YiYRiNiGr+5iHRqIvUcXSSME8+G4yXzEJjoPeHb2IAip6RexPYlxjG8pZP5dthz5GCdtu3oh+Po1hmITGJKNISvgHhiK6heIWIn80RFY4w9ez7vSQNFVLx06IGSyANh6zy9CHCSqBAZe5gsqXvmcrqk8jNQRE5hR5E9iaChbCl76eg8l1EuU4h4vTgeI572oFuegSLKi51l7EzmpdfA1mOEkrYHEtQPpQj1lGMjmntUfNQyUKQkjta1NUjmNuzeIJlA+kmSqFzE6SXJbX4pcJbUMlfErhIrWo6DijJQtU7Pbz6zG0gjYhq/j4hr4dlY7uYvKxc1HEtEhLNwSzJK4BESPNgMBajhaCLcgmUpWK2AjZR0U67AJBd3CnCZlExCcCYsJlXwI+Ahs8id0T7kM8C949RQCCVqOLwIE3qHIE0DOlgsuEMIlocFpyYXIVwZpBTLwAOKwZJW0AoQ5r/tWkFIzRqOIKICBBZzKxE6VCKMRCTAHoi4gqt0VIoIJvTraziCiJtxhhnLChGEnOciit+LjJgUuUgYrHtEKjeTvOiRUGZGiBS4j1ruU69yy62eZh5pRIzndxYRLtZY+WYEdSItDqQMGvXLHFkG9ImMjgZQJ5JmLliIlI+goDjXIpy7QYCUfhiUvCtPCMlPWKX1IpwLnmEmQjDcQQoSsSyepuUET8HyBBPUeyejXiSQTGTIZlmxCNEiAmwtEqRYn2AIzBZREpmbjX4RSeXMkioXccPRXXlCKWmyiHBdFxaERZs+UsgSFhBYAvqnOuEGrRleionziGDcxfMxV+RlNCKNyLnQiJhGI2IajYhpNCKm0YiYRiNiGo3IBp3+ly/9zoPIgiL4lfbiy2KFEjNF2t8//qn53r6PXOaBj9f3cv0i0v2MAhNF2lqjSHyxGemWJp/L52SLHBNFuvnf/jo/5Hl3dPOyiHwrzLTC5dVQR76CMFHkk05XC1xT42od0Y3O39Tog/hOjc9aqLuWNVDkGyW5AtGhgroBcUMlBU2fTl3rM3QcQvO1NIKBInoU06ES6JTpE91CrV+mvz73bFzhCvHgg2HKPlyPVCKbaWNIlVT2w+qB2jNxLAI5duyTgxdLYDo9iMjX9UBolwX0qQyUw6ZTHNr3/dfFs7FsYt0fiY2pp3skmqjwACJ6aHTbdLzR6T4UuSpCw9eK2DNvluqjpPxJZDz3EgfReOIcQAQrPWtc3uj9Z+xXZMaXStExi2Qu4k31I/9E70Fm9m45H/axZ5Hxj/lkiRISufWchJSiCNMDiJBHSXe1ZxFn7v+0UbJ0yCye6LCK1J5F1pPc1Wr1SR9X+xWB49lHm0e+6yzXK6zLZ4sYNyHms3aV9qoQWTy8/J6DSL9cWBGL4rq10odqstyYEIteM1BktSHyYBlyQ2lvX6KYJ6JTu6xGS1+Xz3qJ214vGsvRU5Vde/WljX4/3y06XxaoQ0h5jEXj1brIuiC+FvlXcz2Gvyzju7nQt/7f7eFV/wbdxdXNYthHLQypDIXizJVK0n7fIpQ/cXN9TUKlUaerI1fXeq4fVm8Zv10NP96/ZflMj2Ebw/Yl2kP99ElGXItwSyiGVsp0W+xdhC5WBdVgWXXXgZstb3VLka8LEvnWWYvUopO3tMjoUCJYXRY5Xq5LnXIr1ixXnfvITRH5ikLk8uqyPRxeX+PZIgpcMsEU0y3M9l9amjbdDlq0H0QW1Q2iKrJCAWXe7lBdtQF6dNDpoJ68F5hhN+gWfV2THZMuv2bQiJhGI2IajYhpNCKm0YiYxtsVEVwh5SxocQ7Ojfj8/utEXIvTnkOCC0n7c+FxaQlO2wycRBgYzoXtIkzoHnEZQmgYm+EkSOlgOzMW1orM4JLLiHOocoxwnIZbf+DPlxOJbfBXXLUsnITQj6Bux7Hvz72p/Sips7n8qkGEAjnx5n48dc5zHrG1R4XyegNPnqGInUTYQHp+4tkHE4lC1OJEXhRNQ/kSjzEe49zG/nxqH0TE9v2xg6eRSc9bxvPEj57v4e06M+n5y+kBRGLP7iX2k0nFg8gph+8ETxJ6Jckcu5FeL5nuWyTzSSLyoyeqahDfe04G6ul+WK5Najo5U/sWKUpZUqfsrCqJinHyVIZe72TL+GmvELA9P9pdVRXO3MNOpgP7wCKOtYuf43Vr8vMftXGyNR78wzdifPCvtQP1M7JegLPPHpn2qrbtJdOdVVVxu/PPvoxP9g7RTm5BVEPZs3dVVf0wmfTsk4lEvQ2x/LJYXau24vTG21cktziViO3fPlKj2XFdVbtQ/rZziYeTiXhbilrPjruqqipBe0vn2kcRkXHJ9NcOUVvrzddV9SRxXNO5hxNRUYFeHKyz9Lwd0rJ+hT7ZMq8et7Sc257v2etv03wt2UBtFJZzIJEUgo9gcWvLu8os9uNQrxbxPOqvwfI1fxPrOSIqA3OZKxFuvflge4PE8228HqfnyXvCJMLL4c8QETP24L5WyNJNldtehP+D3fMrengxKWPPEGGzzJIkIs/6Bh0hROiqQCIchTgXdgx2AQRuvp0Lxt5FaUTOnUbENN6uyAgVLh4QWEW0eioM+bqzbSJMpxoIvbmZRe3iq44EYPEAFj1CF/opeIYgzV9d/IaOHpV6EZrRaWMitKBzZbgbZS4bsWD0l2xZmctH3CWRGdci7l8BRzb6ATmSAY5JvYjiGbjUBqUIy1eQaSAk4/Qk5GwECvE0LV4d6CiD4Dgi9SIu9QdSZLmIpfKnjPYq4FaQi7hCaBH3B4m0UpG5FKOf0QxHo16EMVdJjhFXCBQFJDUy2oRUlpB8BG4Jrlxg5CLQw0MqhRm/0y/A6dAi9TDUIE/+GYl9zSMCL8RUkZPTiJhGI2IajYhpNCKm0YiYRuviAm+C1sUfJvzzpv+N8/7t/BPtD+/et86diz/effgPZJD8+hrmxCsAAAAASUVORK5CYII=", + "description": "Displays a CO2 level data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'co2', label: 'CO2 level', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'ppm', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'co2', 'ppm', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"CO2 level\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"ppm\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3FA71A\"},{\"from\":600,\"to\":1000,\"color\":\"#80C32C\"},{\"from\":1000,\"to\":1500,\"color\":\"#F36900\"},{\"from\":1500,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"ppm\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"ppm\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"CO2 level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"co2\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "co2", + "carbon dioxide", + "carbon emission", + "carbon footprint", + "carbon output", + "carbon pollution", + "carbon capture", + "carbon offset", + "carbon reduction", + "carbon neutral", + "climate gas", + "fossil fuel emission", + "carbon cycle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/co2_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/co2_chart_card_with_background.json new file mode 100644 index 0000000000..cb6e3761eb --- /dev/null +++ b/application/src/main/data/json/system/widget_types/co2_chart_card_with_background.json @@ -0,0 +1,43 @@ +{ + "fqn": "co2_chart_card_with_background", + "name": "CO2 chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a CO2 level data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'co2', label: 'CO2 level', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'ppm', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'co2', 'ppm', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"CO2 level\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"ppm\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3B911C\"},{\"from\":600,\"to\":1000,\"color\":\"#7CC322\"},{\"from\":1000,\"to\":1500,\"color\":\"#F77410\"},{\"from\":1500,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"ppm\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"ppm\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"CO2 level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"co2\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "co2", + "carbon dioxide", + "carbon emission", + "carbon footprint", + "carbon output", + "carbon pollution", + "carbon capture", + "carbon offset", + "carbon reduction", + "carbon neutral", + "climate gas", + "fossil fuel emission", + "carbon cycle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/compass.json b/application/src/main/data/json/system/widget_types/compass.json new file mode 100644 index 0000000000..c74108d034 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/compass.json @@ -0,0 +1,33 @@ +{ + "fqn": "analogue_gauges.analogue_compass", + "name": "Compass", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays latest value of the attribute or time-series data on the compass. Expects value to be in range of 0 to 360.", + "descriptor": { + "type": "latest", + "sizeX": 6, + "sizeY": 5, + "resources": [], + "templateHtml": "", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueCompass(self.ctx, 'compass');\n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'direction', label: 'Direction', type: 'timeseries' }];\n }\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n", "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-analogue-compass-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-compass-gauge-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"minorTicks\":22,\"needleCircleSize\":15,\"showBorder\":true,\"borderOuterWidth\":10,\"colorPlate\":\"#222\",\"colorMajorTicks\":\"#f5f5f5\",\"colorMinorTicks\":\"#ddd\",\"colorNeedle\":\"#f08080\",\"colorNeedleCircle\":\"#e8e8e8\",\"colorBorder\":\"#ccc\",\"majorTickFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#ccc\"},\"animation\":true,\"animationDuration\":500,\"animationRule\":\"cycle\",\"animationTarget\":\"needle\",\"majorTicks\":[\"N\",\"NE\",\"E\",\"SE\",\"S\",\"SW\",\"W\",\"NW\"]},\"title\":\"Compass\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"decimals\":0,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\"}" }, + "externalId": null, + "tags": [ + "direction finder", + "magnetic needle", + "navigator tool", + "orienting device", + "gyrocompass", + "course plotter", + "bearing pointer", + "directional guide", + "north pointer", + "magnetometer" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/dashboard_state_widget.json b/application/src/main/data/json/system/widget_types/dashboard_state_widget.json new file mode 100644 index 0000000000..5ba95e2593 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/dashboard_state_widget.json @@ -0,0 +1,26 @@ +{ + "fqn": "cards.dashboard_state_widget", + "name": "Dashboard state widget", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays specified dashboard state inside widget. Advanced widget settings allows you to configure target dashboard state to be displayed.", + "descriptor": { + "type": "static", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "\n\n
\n
Dashboard state widget
\n
(Specify dashboard state id in the advanced widget settings)
\n
\n", + "templateCss": ".dashboard-state-widget-prompt {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n font-size: 32px;\n font-weight: 500;\n color: #999;\n}\n\n.dashboard-state-widget-prompt .title {\n font-size: 32px;\n font-weight: 500;\n color: #999;\n}\n\n.dashboard-state-widget-prompt .subtitle {\n font-size: 24px;\n font-weight: normal;\n color: #999;\n text-align: center;\n}", + "controllerScript": "self.onInit = function() {\n var $injector = self.ctx.$scope.$injector;\n self.ctx.$scope.stateId = self.ctx.settings.stateId || \"\";\n self.ctx.$scope.defaultAutofillLayout = self.ctx.settings.defaultAutofillLayout;\n self.ctx.$scope.defaultMargin = self.ctx.settings.defaultMargin;\n self.ctx.$scope.defaultBackgroundColor = self.ctx.settings.defaultBackgroundColor;\n self.ctx.$scope.syncParentStateParams = self.ctx.settings.syncParentStateParams !== false;\n}\n\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-dashboard-state-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"syncParentStateParams\":true,\"defaultAutofillLayout\":true,\"defaultMargin\":0,\"defaultBackgroundColor\":\"#fff\"},\"title\":\"Dashboard state widget\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"noDataDisplayMessage\":\"\",\"showLegend\":false}" + }, + "externalId": null, + "tags": [ + "embed", + "embedded", + "inner" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/dashboards.json b/application/src/main/data/json/system/widget_types/dashboards.json new file mode 100644 index 0000000000..a0dc6c4172 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/dashboards.json @@ -0,0 +1,30 @@ +{ + "fqn": "home_page_widgets.dashboards", + "name": "Dashboards", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Display and manage the 'last viewed' or 'starred' dashboard lists.", + "descriptor": { + "type": "static", + "sizeX": 7.5, + "sizeY": 6.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "", + "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Dashboards\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "air", + "aqi", + "pollution", + "emission", + "smog" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/date_range_navigator.json b/application/src/main/data/json/system/widget_types/date_range_navigator.json new file mode 100644 index 0000000000..06231d055d --- /dev/null +++ b/application/src/main/data/json/system/widget_types/date_range_navigator.json @@ -0,0 +1,27 @@ +{ + "fqn": "date.date_range_navigator", + "name": "Date-range-navigator", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "The date-time stepper to change the time window of the dashboard. The change affects all widgets that use the dashboard time window.", + "descriptor": { + "type": "static", + "sizeX": 5, + "sizeY": 5.5, + "resources": [], + "templateHtml": "", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-date-range-navigator-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"defaultInterval\":\"week\",\"stepSize\":\"day\"},\"title\":\"Date-range-navigator\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null, + "tags": [ + "time-window", + "interval", + "date", + "date-range" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/device_admin_table.json b/application/src/main/data/json/system/widget_types/device_admin_table.json new file mode 100644 index 0000000000..af460a286c --- /dev/null +++ b/application/src/main/data/json/system/widget_types/device_admin_table.json @@ -0,0 +1,30 @@ +{ + "fqn": "entity_admin_widgets.device_admin_table", + "name": "Device admin table", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Customized entity table widget with preconfigured actions to create, update and delete devices.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.entitiesTableWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n hasDataPageLink: true,\n warnOnPageDataOverflow: false,\n dataKeysOptional: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true,\n hasShowCondition: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n },\n 'rowDoubleClick': {\n name: 'widget-action.row-double-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-entities-table-widget-settings", + "dataKeySettingsDirective": "tb-entities-table-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-entities-table-basic-config", + "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"entitiesTitle\":\"Device admin table\",\"enableSearch\":true,\"enableSelectColumnDisplay\":true,\"enableStickyHeader\":true,\"enableStickyAction\":true,\"showCellActionsMenu\":true,\"reserveSpaceForHiddenAction\":\"true\",\"displayEntityName\":false,\"displayEntityLabel\":false,\"displayEntityType\":false,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"entityName\",\"useRowStyleFunction\":false},\"title\":\"Device admin table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Entity name\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.8332260553092266,\"funcBody\":\"return 'Simulated';\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Entity type\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.08583217494773887,\"funcBody\":\"return 'Device';\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{\"headerButton\":[{\"name\":\"Add device\",\"icon\":\"add\",\"type\":\"customPretty\",\"customHtml\":\"
\\n \\n

Add device

\\n \\n \\n
\\n \\n \\n
\\n
\\n
\\n \\n Device name\\n \\n \\n Device name is required.\\n \\n \\n
\\n \\n \\n Label\\n \\n \\n
\\n
\\n \\n Latitude\\n \\n \\n \\n Longitude\\n \\n \\n
\\n
\\n
\\n
\\n \\n \\n \\n
\\n
\\n\",\"customCss\":\"\",\"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\\nopenAddDeviceDialog();\\n\\nfunction openAddDeviceDialog() {\\n customDialog.customDialog(htmlTemplate, AddDeviceDialogController).subscribe();\\n}\\n\\nfunction AddDeviceDialogController(instance) {\\n let vm = instance;\\n \\n vm.addDeviceFormGroup = vm.fb.group({\\n deviceName: ['', [vm.validators.required]],\\n deviceType: ['', [vm.validators.required]],\\n deviceLabel: [''],\\n attributes: vm.fb.group({\\n latitude: [null],\\n longitude: [null]\\n }) \\n });\\n \\n vm.cancel = function() {\\n vm.dialogRef.close(null);\\n };\\n \\n vm.save = function() {\\n vm.addDeviceFormGroup.markAsPristine();\\n let device = {\\n name: vm.addDeviceFormGroup.get('deviceName').value,\\n type: vm.addDeviceFormGroup.get('deviceType').value,\\n label: vm.addDeviceFormGroup.get('deviceLabel').value\\n };\\n deviceService.saveDevice(device).subscribe(\\n function (device) {\\n saveAttributes(device.id).subscribe(\\n function () {\\n widgetContext.updateAliases();\\n vm.dialogRef.close(null);\\n }\\n );\\n }\\n );\\n };\\n \\n function saveAttributes(entityId) {\\n let attributes = vm.addDeviceFormGroup.get('attributes').value;\\n let attributesArray = [];\\n for (let key in attributes) {\\n attributesArray.push({key: key, value: attributes[key]});\\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\":\"70837a9d-c3de-a9a7-03c5-dccd14998758\"}],\"actionCellButton\":[{\"name\":\"Edit device\",\"icon\":\"edit\",\"useShowWidgetActionFunction\":null,\"showWidgetActionFunction\":\"return true;\",\"type\":\"customPretty\",\"customHtml\":\"
\\n \\n

Edit device

\\n \\n \\n
\\n \\n \\n
\\n
\\n
\\n \\n Device name\\n \\n \\n Device name is required.\\n \\n \\n
\\n \\n \\n Label\\n \\n \\n
\\n
\\n \\n Latitude\\n \\n \\n \\n Longitude\\n \\n \\n
\\n
\\n
\\n
\\n \\n \\n \\n
\\n
\\n\",\"customCss\":\"\",\"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\\nopenEditDeviceDialog();\\n\\nfunction openEditDeviceDialog() {\\n customDialog.customDialog(htmlTemplate, EditDeviceDialogController).subscribe();\\n}\\n\\nfunction EditDeviceDialogController(instance) {\\n let vm = instance;\\n \\n vm.device = null;\\n vm.attributes = {};\\n \\n vm.editDeviceFormGroup = vm.fb.group({\\n deviceName: ['', [vm.validators.required]],\\n deviceType: ['', [vm.validators.required]],\\n deviceLabel: [''],\\n attributes: vm.fb.group({\\n latitude: [null],\\n longitude: [null]\\n }) \\n });\\n \\n vm.cancel = function() {\\n vm.dialogRef.close(null);\\n };\\n \\n vm.save = function() {\\n vm.editDeviceFormGroup.markAsPristine();\\n if (vm.editDeviceFormGroup.get('deviceType').value !== vm.device.type) {\\n delete vm.device.deviceProfileId;\\n }\\n vm.device.name = vm.editDeviceFormGroup.get('deviceName').value,\\n vm.device.type = vm.editDeviceFormGroup.get('deviceType').value,\\n vm.device.label = vm.editDeviceFormGroup.get('deviceLabel').value\\n deviceService.saveDevice(vm.device).subscribe(\\n function () {\\n saveAttributes().subscribe(\\n function () {\\n widgetContext.updateAliases();\\n vm.dialogRef.close(null);\\n }\\n );\\n }\\n );\\n };\\n \\n getEntityInfo();\\n \\n function getEntityInfo() {\\n deviceService.getDevice(entityId.id).subscribe(\\n function (device) {\\n attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE',\\n ['latitude', 'longitude']).subscribe(\\n function (attributes) {\\n for (let i = 0; i < attributes.length; i++) {\\n vm.attributes[attributes[i].key] = attributes[i].value; \\n }\\n vm.device = device;\\n vm.editDeviceFormGroup.patchValue(\\n {\\n deviceName: vm.device.name,\\n deviceType: vm.device.type,\\n deviceLabel: vm.device.label,\\n attributes: {\\n latitude: vm.attributes.latitude,\\n longitude: vm.attributes.longitude\\n }\\n }, {emitEvent: false}\\n );\\n } \\n );\\n }\\n ); \\n }\\n \\n function saveAttributes() {\\n let attributes = vm.editDeviceFormGroup.get('attributes').value;\\n let attributesArray = [];\\n for (let key in attributes) {\\n attributesArray.push({key: key, value: attributes[key]});\\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\":[],\"openInSeparateDialog\":false,\"openInPopover\":false,\"id\":\"93931e52-5d7c-903e-67aa-b9435df44ff4\"},{\"name\":\"Delete device\",\"icon\":\"delete\",\"type\":\"custom\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet dialogs = $injector.get(widgetContext.servicesMap.get('dialogs'));\\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\\n\\nopenDeleteDeviceDialog();\\n\\nfunction openDeleteDeviceDialog() {\\n let title = \\\"Are you sure you want to delete the device \\\" + entityName + \\\"?\\\";\\n let content = \\\"Be careful, after the confirmation, the device and all related data will become unrecoverable!\\\";\\n dialogs.confirm(title, content, 'Cancel', 'Delete').subscribe(\\n function (result) {\\n if (result) {\\n deleteDevice();\\n }\\n }\\n );\\n}\\n\\nfunction deleteDevice() {\\n deviceService.deleteDevice(entityId.id).subscribe(\\n function () {\\n widgetContext.updateAliases();\\n }\\n );\\n}\\n\",\"id\":\"ec2708f6-9ff0-186b-e4fc-7635ebfa3074\"}]},\"configMode\":\"basic\"}" + }, + "tags": [ + "provisioning", + "management", + "administration", + "admin" + ], + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/device_claiming_widget.json b/application/src/main/data/json/system/widget_types/device_claiming_widget.json new file mode 100644 index 0000000000..4ac8654ab4 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/device_claiming_widget.json @@ -0,0 +1,25 @@ +{ + "fqn": "input_widgets.device_claiming_widget", + "name": "Device claiming widget", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAQJUlEQVR42u2diVcVRxaH+ZMmzkxmn+xzZubMmcks0cmemETc93FXUBEQwRU33Pc9uIu4QUBN3NBoFBFUwBVBQUHZDPM119Tp8x7r4/k05vc773C6q7uri66v771VD+pGNTU1NTQ0lJWVlZSUXJWkLgiEAKm+vh6ooqCqtLS0qqqqsbGxSZK6IBACJHACqigQY0cPRQqXwAmoojBfslVSeO0WUEXhGvUspPAKqASWJLAkgSUJLIElCSxJYEkCS5IEliSwJIElSQJLEliSwJIkgSUJLElgSZLAkgSWJLAkSWBJAksSWJIksCSBJQksSYoIWOfPn/+qWUeOHLlw4UJdXV0X733x4sXTp0+/eM/0+PHjd+7cEVgdBWv+/Pl9+/YdM2bMqFGjoqOjBw0atG/fvq7ce8GCBVOmTHnxnmlsbOypU6cEVifAmjRpkm3X1tYeOnSoT58+GRkZId/78ePHLHbzwjzK8vJyeOI3iouLO3v27N27d7HxAqtzYJm2bdvWr1+/mpoaV8IaI+np6Vu2bMHHff/995QUFRXt3bvXtk0QaU88Ly/v66+/duXUc/DgwXXr1gHrvXv3/De6fv361q1bqZae81fldOXKlf379z969Iifa9eupR5b+8tEZx8+fJiaqYT2uPJz584dPXoUt0ULOco5VH7//n0aEFwJys/P37Rp0/bt23HiwW0ApuHDhw8ZMgRbPmHChN69e6empgqsUMC6fft2r1696BvbJfziac6ePTstLQ2nuWLFCrsB5xQUFNg5LJvEroVWuMKpU6e613306NE42WXLlk2ePHngwIGwYocI6ah21qxZVAvHixcvDm4bTpk74oNmzpw5b948TKnr1IcPH9LsESNGrFq1irbhxHNycuzQ6tWrhw4dyk0XLVo0bdo0GrZkyRLO5LS5c+dS4YwZM9wtNm/eTLULFy6kZipp0VTD5e7du6mHlmCx5ApDBAtfRpfv2LHDyKAnMjMz7RCmhedrNcfExGAPrBzbwDttHtAPFtvjxo0DAuseMLJDmC5g2rVrl7MKVHvp0qVgsCg/duyYf/fBgwdswz01u5XlYIhdB9aAAQMqKipsd+nSpVzF6+HeE3b5vWycAUxnzpyxQ3v27OGXtfr94i4YLUwdvPqNscDqHFgI08KrbMRAgPMdwEGf4fXY5iXGDJgLS0xMXLNmjYPJ6IEz+slvA4qLi21kQCfBLiGdOwSXDt8AsJyXhAN2qcR/Do4Sr4c/pUIH1vjx4/0+mquqq6vdo2HXvN7KlSv9Z2KNOBQcQtGMOXPmsAFVGK0WvbbAah8sDAzPF6TYBpdeQaIXzZjxuuMNMT9sFBYWBoBFud/e+LVx48bgatevX982WNyLXedMOQrZcM9PuGwNrOzsbK6CPxcvsktcxfb06dODm5GbmxvcYPdquXoEVqfB4qHzfJnTYhuGsP8Ba+i6OCM5ORkaMD/MVrjud2AxJQZwLfYTkTJGMaBa57w6AhYhOUi5wQR2MQSwiNuYGQloBmG+JkLDDxb+KykpiXiFSMtCGbrhxo0bwe8uIl7GWoAX4zL/PJaLsYjcCa7dIThYvnw5GydOnKBa+rjFajsCFvG4f7aMkWwIYNHswYMH+8e/LTZDChEsMOJkwg7CEcZuuBXnbgiDgGPixImM53GRBO/Dhg1zUQ4lmA366ebNmy2CRdhEmIV1IcQBCxvEWf9xU8J/AnYqIXwmOvZPGbQL1s6dOxnNEfQwhs3KyiLywzraYr4dBwtvjuFkkMjEB3hRD7+dLFbYwLLYgjcehgiqbMTkn32wQTvi/Wbo5D/KQD1gnt0PFkzg9eh1roUwqHImgbsQ4kADh5giYhAaHBS3ARb1uJbzMjDNxgZ8dAosm5DjtbF64P4nPugLJ1gdFCaHGCi09b1xrwzc/GNAf7UQFvJMPdYueHYgBFVWVhI4mveXIgqWJAksSWBJAkuSBJYksCSBJQksgSUJLOkFAytVknySxZLkCiWBJUkCSxJYksCSJIElCSxJYEmSwJIEliSwJElgSQJLEliSJLAkgSUJLEkSWJLAkgSWJAksSWBJAkuSBJb0PIHFEqAnT54kAQmrpbs8AM9WtIRFkZ9Gzax6StaTn3J2iQiBRX4llv9n3WKWRo6Pj2cZYzIZRSYvHCsWB6c5edpgsWa9WxFeeopgsfw/6627VDNYLNbNZg3tCLQSG8ky8REDiyQ5PBPWb2Z5cPtN/cvTS2EGq3///mS28ZewUDu0ud3gZIUmVlq3dIGs5e9SV1AIE6WlpeQi4JAtcM0i3qyfTnIUDt26daupef13tkkGZkk33arrrYFF3hQSq7jlslnwnRxS5JVwCZVoGyf4/R01XLt2zV/nhg0b4BjzTM2kL2CbJeDFytMCi+X/WW7f5WcLUIvJCpuacxWRcZQ0O+QZhEK6iiitqTntJSvxjxw5kkOs6w8KoMYJZHwgvRv3ohJSYFBO5gEyaVE5G8HL9vvBAiCuIr2l7WJNuYrsAbhsHLcZV8sSYNQaiBwKTuHMWvMkN+BMIOt69muB1c6ppBsh8wdpIzAA/kX020hWiA3jEpdWicyloIMdMrBIwuaiNFJOEMDZmZzAIYDruCu07Esk+rbyy5cvQ4zbPXDgAEdJM8Z9aapLgMiKPGS6C66WC0nogjWF+4B0r1L4R4U4Drp/7NixZmxcbtU2khVi5Jz1QtgGCMAeGFi4SHeIM0kL6HYxTpBhdbYLlhkel8MS4U9pob/lLrOryzZFM6jW7xld+zFXZHZtas77FZk4UvNYT+yBJaixXHBtJCuEMEvBGqAAsOhL+AiuhPw87YIFUn2b5c98iSkKrs0YMoOKN2QDM9aicwdoCxNx5ZpxeIpg8fRxf/4sNzzuhIQEy7vURrJCzIYR1jZYTc2pU7FYAZV0xGJRD0YFj0ag7VIsEdIRmQXUZgzhfMkhRZ3kn/ZnfZaeAViWT8uFLCbCIALzpjaTFZL92z9yJCMXMTjdHwwW02MkQAwwG7ZB8t92YyziPNBk6OAuwVj6M7/5MwwSJpLKi/NbzL4pRQ4s7JMN2WCILmSy1HyQJXJuI1khGQYBiG7GWjAfQXCTkpLSosXKy8uzURimjniZMB8jZG7I8n6TsjA4g5d/VMi9aJJNDVAD3AArMxrcC4CwqS7jq70neE9/bkvp2cRYmBnmsegti1dAh0518UcbyQrBgk6lnF4nMjM4gsFqak6zy8DT5RZ0DeMSZpUohLa257GYtcK2AZP9blRitVGt3zjRbN4EAnMR8LwE78wy8N63lly0tWSFXIWd64h5oMupIbh+u28I3yBhKYMzDFIPb0iAZ5ee/ajwxy4MKhZXeZ0FVtjEF3+4RZyycu8KrHAKJ0skF/DloCSwJIElCSw9CElgSQJLEliS9AzAUoY+SfkKJblCSWBJksCSBJYksCRJYEkCSxJYkiSwJIElCSxJEliSwHqa+vbS9bd6p7zUI/Zn3fUJ8cPTezM6JTevUGA90ZmCay/1mCgywoNX91iep8Dy/rX6rehkARHGz5+ik9td2unFB4v/pn+pe4xoCOfnnZh21zp48cHi/+iFQtg/7a5OILD0EVgCS2AJLIElsPQRWAJLYAksgSWw9BFYkQXrF+9O+tUHcf6SnhOWzlqz74+fJrZ2SbceE/81dG7Xv5ccPn1j8oqMzl71xhfTXvs8SWA9v2D9Y9CcI2cKHz/2vrsouVkxNvVLK0/b4i33/fdBs1u7cMxsL2HCoKR1Xey8r04W1Dyq6+xVV66XX7x6S2A9p2C9/sW0u1U1lQ8eTl+VCVLfnL1MVZiQjoD1VnRK2ubsrpuN0MAC6xEzNgms5xSspVu9ReH7xK2y3V9/OAXITl8sCQbr89jl8zYegj/cn5VwaHLajr/0m8H26Nlb+k5Z1f1/8/GeScv34EBBdtryjDnrDvQYsaBFNwq+qesP9IxZFgDWFxO9G1EzNbD7/ug0tl/5bKodHZy0bvzcdDZGztw0LHmDFf783UkYTmqbtHD77z5OcFXRQtqTsnLvf4bNE1gRBevClZtV1Y/8Jb0mrRgybX0AWOv2fMP2d0XXb5VX4TT7xa+mcOycLz1XOHUt2zfLq+7dr6l+WMsJFOKkblfcL711t66+oaHx8TvD5wfcd1fOt5xWXlldW9dQ/bDOgbVml7fGMxdSWH7vwd8GzIJXSmCLoy+/P5kzDx3PZ/vqjXIab38+dfRMEX+GUHyj4lFtPQ14o5f3Vx5gV9/QyC3u3H1AG8wMC6wIgcVDv1Ryu8VDDizMGD2HFaHwtx/F0+XZJy8Gg0WPvtLTsyuZR7+jfMbqTG8EELOM7QWbsvw1fzBmEYU7sk/DBJdcu33PwPpo3GLKl23L9f5kpXcKN9p6KA9rVFFZbXc0yEbO3OwHi10KE5fuZhtrCvdL03Nefj+u8r5nemGRcUlefsnNO5UCK3Jg8UK3C5ZZBdwlPgXXeb/60bnC68FgmQPlg0ui/J3hnvfBhbG9KfO4v+apy7w8MZ+MXxIQY9kdiduwT3ywWwXFXtvWZxzDsME3hvNhbf1vPpziB2vjXi/vBsS7wSy3/izWAxrErSq4ZNd8q8CKBFgFxbdwYf6S90alEeX4wer234k5pwrwMtuzTncGLM/9YZCCwZq73kun+M8hqQFgmcOlSeeLbtiH4SrlIGg3wrbtyT1rVzmwMg6fbWx8HDDrQcjFJZzvquKDYxVYEQLLYpqPxz0xHjgOBonGjQOL6JuNWWv32zn0VhfBGpeaTuHgH+YpcvMuGVhE/ZQT5LmQ3P0R+o2yypMXit3t/GAt/tLLN/vXfjOdOYSqfw+dS+GS9Bx3U1ebwIoEWH/uO4NQhvAodsE2YvasE57LsKksB9bbg+cQGhNmETAt3+alMctv7tGQwWLMSJh/5dqdgYlrmRolsjawCLoZSRCDQ8aAxDUnz1+d/QPNhE3Uw9Ffvjc5AKy3h6RSAzaV5tmtE5bsgkVAxMrGLdrJeJYGMJMSwlyuwAp9gvTdUQsZ7lklxPJxzeOvgBiLDqbz2D11oZgR34OaWiLitsFi6qE1sPj0T1iDC+ZQUWkZXe5GhcTvuEJrzLeXrjl3SW2UpB845WpwYNkAsOyul0eNQejKHUe6Nf9HyZvRyVnH8xubm82b4+YmBFZEv9L5w6eJbq6oxQ9e8tXPwvkVCvajtcnV33+S0HZjWv6Sp1cyg8GAQiwcE7ndQv3nJYGlL6E18y6wBJbAElgCS2AJLIElsASWwBJYQkFgCSyB9WMBS4uChPsTI7A8sF79NF40hPHzxueJAssDa0tGroxWGM1VZvYxgeWBxS+4++DR13rGs7CTyOjKsliv90zYc/Aoz1NgeQuvlZaWFhUVFUrhEE+S56mF17ylImtra3kWV6VwiCfJ89RSkU/YamhWvdQ12WNsl6omrfMuPSUJLElgST8usEpKShobG/UspHAJnIAqqqysrKqqSo9DCpcqKyuBKoponzEkbMluSV23VYAETmxE2SwiiGG+NE8jdUUgBEhmof4PL8B473MwkX8AAAAASUVORK5CYII=", + "description": "Allows to claim the device using name and optional secret key.", + "descriptor": { + "type": "static", + "sizeX": 7.5, + "sizeY": 4.5, + "resources": [], + "templateHtml": "
\n
\n \n {{deviceLabel}}\n \n \n {{requiredErrorDevice}}\n \n \n \n {{secretKeyLabel}}\n \n \n {{requiredErrorSecretKey}}\n \n \n
\n
\n \n
\n
\n", + "templateCss": ".claim-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n", + "controllerScript": "let $scope;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n}\n\nfunction init() {\n $scope = self.ctx.$scope;\n let $injector = $scope.$injector;\n let utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n let $translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n let deviceService = $scope.$injector.get(self.ctx.servicesMap.get('deviceService'));\n let settings = self.ctx.settings || {};\n \n $scope.toastTargetId = 'device-claiming-widget' + utils.guid();\n $scope.secretKeyField = settings.deviceSecret;\n $scope.showLabel = settings.showLabel;\n\n let titleTemplate = \"\";\n let successfulClaim = utils.customTranslation(settings.successfulClaimDevice, settings.successfulClaimDevice) || $translate.instant('widgets.input-widgets.claim-successful');\n let failedClaimDevice = utils.customTranslation(settings.failedClaimDevice, settings.failedClaimDevice) || $translate.instant('widgets.input-widgets.claim-failed');\n let deviceNotFound = utils.customTranslation(settings.deviceNotFound, settings.deviceNotFound) || $translate.instant('widgets.input-widgets.claim-not-found');\n \n if (settings.widgetTitle && settings.widgetTitle.length) {\n titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n titleTemplate = self.ctx.widgetConfig.title;\n }\n self.ctx.widgetTitle = titleTemplate;\n \n $scope.deviceLabel = utils.customTranslation(settings.deviceLabel, settings.deviceLabel) || $translate.instant('widgets.input-widgets.device-name');\n $scope.requiredErrorDevice= utils.customTranslation(settings.requiredErrorDevice, settings.requiredErrorDevice) || $translate.instant('widgets.input-widgets.device-name-required');\n \n $scope.secretKeyLabel = utils.customTranslation(settings.secretKeyLabel, settings.secretKeyLabel) || $translate.instant('widgets.input-widgets.secret-key');\n $scope.requiredErrorSecretKey= utils.customTranslation(settings.requiredErrorSecretKey, settings.requiredErrorSecretKey) || $translate.instant('widgets.input-widgets.secret-key-required');\n \n $scope.labelClaimButon = utils.customTranslation(settings.labelClaimButon, settings.labelClaimButon) || $translate.instant('widgets.input-widgets.claim-device');\n \n $scope.claimDeviceFormGroup = $scope.fb.group(\n {deviceName: ['', [$scope.validators.required]]}\n );\n if ($scope.secretKeyField) {\n $scope.claimDeviceFormGroup.addControl('deviceSecret', $scope.fb.control('', [$scope.validators.required]));\n }\n \n $scope.claim = function(claimDeviceForm) {\n $scope.loading = true;\n\n let deviceName = $scope.claimDeviceFormGroup.get('deviceName').value;\n let claimRequest = {};\n if ($scope.secretKeyField) {\n claimRequest.secretKey = $scope.claimDeviceFormGroup.get('deviceSecret').value;\n }\n deviceService.claimDevice(deviceName, claimRequest, { ignoreErrors: true }).subscribe(\n function (data) {\n successClaim(claimDeviceForm);\n self.ctx.detectChanges();\n },\n function (error) {\n $scope.loading = false;\n if(error.status == 404) {\n $scope.showErrorToast(deviceNotFound, 'bottom', 'left', $scope.toastTargetId);\n } else {\n let errorMessage = failedClaimDevice;\n if (error.status !== 400) {\n if (error.error && error.error.message) {\n errorMessage = error.error.message;\n }\n }\n $scope.showErrorToast(errorMessage, 'bottom', 'left', $scope.toastTargetId);\n } \n self.ctx.detectChanges();\n }\n );\n }\n\n function successClaim(claimDeviceForm) {\n let deviceObj = {\n deviceName: ''\n };\n if ($scope.secretKeyField) {\n deviceObj.deviceSecret = '';\n } \n claimDeviceForm.resetForm(); \n $scope.claimDeviceFormGroup.reset(deviceObj);\n $scope.loading = false;\n $scope.showSuccessToast(successfulClaim, 2000, 'bottom', 'left', $scope.toastTargetId);\n self.ctx.updateAliases();\n }\n \n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-device-claiming-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"deviceSecret\":true,\"showLabel\":true},\"title\":\"Device claiming widget\",\"dropShadow\":true,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":false,\"enableDataExport\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null, + "tags": [ + "provisioning", + "management" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/digital_horizontal_bar.json b/application/src/main/data/json/system/widget_types/digital_horizontal_bar.json new file mode 100644 index 0000000000..deb82422c6 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/digital_horizontal_bar.json @@ -0,0 +1,25 @@ +{ + "fqn": "digital_gauges.digital_bar", + "name": "Digital horizontal bar", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Preconfigured gauge to display any value reading as a horizontal bar. Allows to configure value range, gradient colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 6, + "sizeY": 2.5, + "resources": [], + "templateHtml": "", + "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-digital-gauge-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 80) {\\n\\tvalue = 80;\\n} else if (value > 160) {\\n\\tvalue = 160;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":180,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[\"#008000\",\"#fbc02d\",\"#f44336\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":18},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#ffffff\"},\"neonGlowBrightness\":40,\"dashThickness\":1.5,\"unitTitle\":\"MPH\",\"showUnitTitle\":true,\"gaugeColor\":\"#171a1c\",\"gaugeType\":\"horizontalBar\",\"showTitle\":false,\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital horizontal bar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null, + "tags": [ + "provisioning", + "management" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/digital_speedometer.json b/application/src/main/data/json/system/widget_types/digital_speedometer.json new file mode 100644 index 0000000000..a3844fc560 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/digital_speedometer.json @@ -0,0 +1,34 @@ +{ + "fqn": "digital_gauges.digital_speedometer", + "name": "Digital speedometer", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Preconfigured gauge to display speed. Allows to configure speed range, gradient colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 3, + "resources": [], + "templateHtml": "", + "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-digital-gauge-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 45) {\\n\\tvalue = 45;\\n} else if (value > 130) {\\n\\tvalue = 130;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":180,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[\"#008000\",\"#fbc02d\",\"#f44336\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#ffffff\"},\"neonGlowBrightness\":40,\"dashThickness\":1.5,\"unitTitle\":\"MPH\",\"showUnitTitle\":true,\"gaugeColor\":\"#171a1c\",\"gaugeType\":\"arc\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital speedometer\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null, + "tags": [ + "velocity", + "velocimeter", + "pace", + "rate", + "tempo", + "momentum", + "haste", + "swiftness", + "rapidity", + "acceleration", + "quickness" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/digital_thermometer.json b/application/src/main/data/json/system/widget_types/digital_thermometer.json new file mode 100644 index 0000000000..ab75474f9c --- /dev/null +++ b/application/src/main/data/json/system/widget_types/digital_thermometer.json @@ -0,0 +1,28 @@ +{ + "fqn": "digital_gauges.digital_thermometer", + "name": "Digital thermometer", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAABfVBMVEUAAAD/lMQzMzMEAwT9k8IHBQYvLy967//6kcBmO07riLQVDBANCQoNDQ0KBggZDhMWFhaqY4MICQkRCg32j71uQFV3RVuJT2mZWXYhISHtirYkJCQaGhrNd52RVG8sLCwTExNUMECuZYVEJzQdEBbzjbvnhrERERHwi7g1HikjFBuyZ4mASmJMLDqiXn0yHSaNUm0pKSkvGyQnJydIKji8bZDWfKTafqfEcpeETGVeN0gfEhjReaBbNEUeHh4cHBylYH9hOEufXHppPVE4ICsIDxBPLj0rGSE/JDA8Iy65a47jhK/Ue6NxQVfhgq3fgavdgKrBcJQ6Ii22aYzJdZvHc5kYMDOcW3hIj5h0Q1knFx6WV3MwX2V7R18fPEA5cHc2a3INGhy/bpIkR0tCJjJXM0Nqz91hv8xasb1QnagvXGJ+SWEqUlfg4OBWqbR46vpv2ehEho8nTFF25/dOmaNAfoYRISNmyNUUJyn///9x3u1VVVVRUVEdOT09d4Cm9A3aAAAXVklEQVR42tyX208TURDGv9nds9m2u+2yrZdtCy210JZ7KVqQSwUMIiCoaIgaX4w+Gf8Ao3+8M3OKPhijD5SLk/R0z9k+nC/f/Gam+Prle4QbHtH3L1/xpYX/IFpf8B3/RXzDjc8rjf9FxijDjeoxgIrvt4C9qewpgN3MvSJuUCT1BIgcpwag5zgsZJLoA4BDon1gcfsJbkSkjlMFjOM44ogK2SRqAG5AVADWiB7hekc0qBkgdpwKAN9xeLPlOHXgIdEZUCBqAzglusWb/tJLXNeo6a0Tx/FtUiXAQI+2ie4AHaLXAKaJlvXoOa5h1MWEquOkP71INaliFZIhus3IE+2oL2UAY0RrgDu7bXCdgu8eWS8sF5FKiM/XXaKMrm+AectLm33RzXQe1yRcBVy96FoJSnuka0uFPCOaVMrngfeKyD7RAwANNQk5XH2YgZ+oF46ruVXVTQUwqi1SIW+IPgI7RDNAQ624Q/SeBQS6mSktrONqQ01ILdqxXr6riVaTVeQken5b73ufAhc4oSyADRalTt2HKtzAVUfLUbQjkaCyjELiynNXpVU1ndaBB3LdoieIND0at3JWgHX2ZQ9XGKZXt15s2dIb2iKlawSkQr6rQuaIQqBNY8A9RSSj3D8m6gO4S/QCfLZ6JR1fvfBdi4dtg1sWDJtPCgwAFbJGARASzdksE1C8pirIAM2AvA6Qy1JpElcRri9314o1UCRsG/T1OVWhRh6rgvY08JZoE3hEZS2+q0C+TeU8MCEji6rKFnDpEQ7x4G/DEowa0DrPqorQnuhjTVQuSa39SLQI9KmhxXcXeKg9sVCigLlf9Ig+AXiMS40tuTRSIVwlDNSg3jkTsdKuynrybkIM2CQvj9CjW+KQVwA+UMDrko4veK19EivewuU1SFXgG5tcdflSWNJhxer+MiOW/RbwjmaFjhPgqSJyRAfAjCfNMDdF5aLSn80BhezljsaGFfR+8R7bPqhr3ZKhGsSMVISc0Y7YcgrcorIW321t9MuyikVFFiCk3ycqz+CSwkDbhlwV6bD2qh4lw1WTKkL74OeyQ0tAQ5YdSaAMBUXgWGwptqnvKukL0H7jrQC4lD4fO5GuCrpr20fLTldD0Cvyuivm9ASgVErVZ6EgIy3wmUiaZRq0Gc6xE0r6NEvrBCSvsR/cxohDyfYTxd2puT87ek8tqdmq62uaAZE4FMvZmBSpQ3oKE9CyFN9NOesDYZlWLel7QPHE+vKkLYPYqCPxLejo2SEr1VQKOdOUD1f4iGQJJctEVwo05OYB5dARRD5SKZR5MSO55L3U+f4M4huN54HCMVG7g5FH6FsTzLBkKR86jyjj8liVBKvL1iCSTJulFRQoK3Q0pOCOCfXZPPJZeqSkHxb1P+N0E8i/ugQd1URAZwUVUWSTLLKziZ1KRGJLllgOe06CUH57So+xSC8YamHgSLDYkHnljfbCMyV8ZoqCfcBtEJX4G4sjzK7Y8cOhki1oJnUtLaFUJ187SmKNSJyevIlgRMgBzWCP7gKvaBFNaufRkWZoDqWQdTx6B5hxHbzwjihYAZ+W6Q1GFMZhD6Lz4mslDLT4dlVDzCeiMNW08kV4DFcUfWA8dtkMt9R2kaEFue+ZNpWiiDsJJd9Yjs6UwTyAp1P8MLIqHPmqxE5ZLQs8byOVFdv5yofFosLeREKLCFn1gDXmZJkaFhhTlup1zNpYoLcvowmtusCkR94nKcCsY2oFI4ukNlTQUkVKugEGtvjaTtg6d6POFqa8YSGvy9IK17EtrkyVXWzSqZD/3Mj0PgEUpuk4B3RK5G2CZZWIDpcxmkgNANMdKqlb0hOZFEXDloirqRuWjUSyzudNrQuM95mTEjDBiNwTVu6zK+4JTQrpz0PZlxaZ9yx5wsl8QLTRBB88uvgJMrVJhQorqasSbYgtTatQee/Ji66W3IF1otvlU/5svAKONoBVRmSJPmLdO5Jy+1pI954yGWpErk/eLoBPrOMglATLXvwEGToKuBJulcR2bqzamiW8h2JJXbjweVfxWWYLcMGfENjbA8qzwINpF3O0DbfPvdAd17oVyCRvVnV+xLbH3LsAnvGD9xgaFwu6tnLEQ0mx3VfUma62QgWkJroMDH6PfAEFj5E4zhrGZExIf2AQ9unAlZmFZMTKMO9SeM0OEWXnceFhJKm6Rkj3RYmaUVXgUyAR3o1YUpXm4eKPkSswJp+BI+6FuXLQARaoX9T6Owfglmf/uje5wdOHJi42WmZohYKiU0rVKokV+Lo8b4kTLbhVg79E804Ok7QkpK8BD6n9ROetO7aPlBkavJwmsjNwYfIiB0W/rqDUhlllalbJQEtYpAWsK2OiX8E/xkE5x6RzYs20vXlg3qMJq+P5W8kv5v34JYT8bPAWFxU9weMHs+ba1DQQheGzbpPeSxp6C70FCi3QFii1CKitd0WRiyAqMgrijMo4IqjoOKO/3T0nW2prHW0THJ8v7oax7us5z+4GIGkNMarjQDxqkPlqQF6xdPId/pbQBMCmaCz+EK/s02MsAfS6+NAPwOOMsfAg9mFYjNbBKUwUXS00h1VFvhtaSRScNCw5eiLnKqEa+I3HPL2IZMTyFQD/C8YiURDc8qHwXnAMLnpIFkWviAMkQIJQEsPatOiqAj3ygIuryVwAApdYmN53SZPZVcaWhTUwKB4w15QfnESvyqJQAlLGbL5ZVfHlROXQB4N53yTwu2yEA79mHYeXI8w1gzU/f0/kSDt3jiiVQqu/DC6PlAb9KQ8SQ4xN6IurObyF3VWAT7GxMurhYj68LQawHENRTj24OQr2qQspgrK/5PbLxZGS5HSHTNJekAIblNh8CN+n8tMAgSJjtQFc+8ppV02s4w3fNlwlPRTZX7IomhBFx2pZSbLQP6E5kSO0wZZHAbbnmKdE5XAxtjkBgotXxJBFHBBFqZ9DGrzZX9kgUDHUAtWmyoFzsIE/AKEaqw0KK8bZKh4ed4TuQ17ARKUICh/edsaSBm2/muwveZIY1plYxyQ2Cbxg1zhAzEWHRyjukl3Fvbj9eqYmnRM+hVGyBau/ZFEKKomiZW0H8S+XRJowG1sUkxsXmntVbhljJCzRFbCHagQsVTCKtN5UraIoVbxE2odWOZpm6UkxirvkXnVjXsQYmxkAZDZh86fYJq6+8FOUpEL9JYuSwl3MEe74XBkOMDEnu2p0yiViZKwYuaKYXAU7JEn0rGZF0VQUhMv+StFRYoIjXM2fB+Axj9VVg/GIiBEfpF3NO8eQItgiaFAUtaEAglFUTfZXRQfg4BABgNk0G49y6bivRDEGFnwMTRkp2/6XuFmhLIZ+GiVryv5KgVNQOayuKl/AGAFLjQgTzMX84Ag67b9SdTCzchjAq6JjPHBhV9E5no+R2UuoBhufyoFdgtlGc6WFpCWLjCKtD4KD5Ljl+EoUP1pZXGaCtDcExOzC2gPolwauPaUDoWhZlCWlUK4sWe8s5Piql+PJEsOr7734JBAXo2kxnbHxi2RERVOa5qski4yiBsBRJobY8mWMsZ2JCL+LwxyQ0SdpRlzo37+CobZnIfOlLIUkOEvo7rBUo+W3f7HmYshY0TtqT/RUxcpS1bh8oJIsZwNHNcYTS1arLRY9DFnLoEC24aYsTNLk0nxLFqchNVzzlt+hcthjvxSEoemdhVGTprxFZvEQcZo4WyldpM8vT411lmI2eqXPpqKFt8IoVmFUI2iZr4DjjOaoA5YS4/gyVYuelmLSG8YT/nafF0akPUwwVZFZzoyljM8qhdIWgrjap+ZJUqMjjKLhY7p8Oc9AJi+sGEErZDuNDDFJfmNhun/Pg5qR7QgjC8PhDPC71hJl5ZdK5Iul8iTYhtJ0huksyE2J0jbToQvKTUm3kgBx2wohM/jtLb/DZt1sVJthuiiy6yben3Bc6v4hzT5sKe2habT32U18gg7aQ3jmpqK5wbaUL3HaK0Gx5IqRKujtaepVlcL8osh3t+Dr1hv8f955foxJDve/PwPBsyOw2DnAz3h9Qkk+dpWkSBvWZsI7ofz8OBdN1IQ+uX43LSKbTJnBjtqo0J13bgri3gXkk9sK4n4MQI938K+7X8FvCfnWM4uzLf/47eHYVDrCJF7oHU1uWa04DTP4R8M/yCAfAXklg8ilv5FB3H/l/R3vzMaqi/2E5971PjXRg6ZWT1ZakbDbtIJ+tkEGct4M9lGTyNp8OB4dXprkYBs9WNAaRjV7GqdwVkFuXZJ95FmZD2dil+9sh+AsUPSgljKSZxhk+e61hevl6QFwimQ1WW9oWiGoKz2U9DUGIauRxzKI3Gl3ZZDX0BOBi9O5YW9s5tHI+gb0TpvrqpqtGo2UZgZFLujK/nNk7x0GOTgAiyMryKcT+uK3Xdp+3+3t4uwYfkto4PZSeTFaShTn5/Ie1sJnL0gnIhV0cuwmPuu87QTUAad7X93I4ZY1feUmDrpm2FwbYr8lAr1TEJimqQlSgrrAMIykoFqpVOAXdh4jJ0d7mOoYLA72gdiiLx4/szpu7whnuyfdjnaeTqfXa7XaSDgcTiQST+Px+EIsFrvv9XovDw8Pwz+BrMAgbw/fSin226p2uAstvrx/A/8vP9q78h+loSA8k5ZYWoQ0nCGBCAhB7hiFFdgE3HVRUVbEEzXe9xGNt8b/3Zl5VcRbaRGN88Pa7sa235v53rzXvvfNYwVkxyOH+tfnUX4K5N6O5QAhP66TP4+TXw+Qfy+Qn8nbWfL6mLyvuQXE+9CK4DcteeJU0C0g3pPdnLtAoUl5thJtV48MQpqboeV99zsZZ882Yv5Mrt4J/EpCVECeqDRCQJ6c+wjm4hyQd0/vLpAQF7VQvbpx4ODRY1vwuT24RXb71R0Bcu7MSz59RyAe77g8m30REEmJ/Mfn158tY4gy7+rp4fVY2l4bGU6kdb8A4swJX17lnHfolcqAb14dgju3P0l8hyQh3tkhduXQsgaN4UGuHUuUUwV9xvNULZYZfIUlr84ouypnL2ZnX9qhM44tZxi/MTTwE9O3Jw3/YQvcNu8nVn50zDhRjvuPzAivDdrxsld7noPJtUT0iM/NqW4OsZiyK3Nx5NvjT5ziqxbLbpHR+5cP4Rtzzxqot9Nr6vX4/tgRDVwx718HzUM6Eq01dZwH4ZZHvH9BN49Bgag7gXskntI/RbLar0zrByYFdCyZje4FZbv92TxdOj0Dsuovsfv4uSeUKwhWreodSdRnBXKMa58VBvMgINS2pUexFYrcbpjZkj70TOG3rCfhJKbVK+wKjJTbkkA6lRHGYWYr/eltxkDlCtT3twNyl0xZRzM+RxIPP4ayYxa1mSvo+lFLRVzcRGxFPcrsWrvrwedpK1MrolgqGlKX9qfobK0qc+GjH8Cs/IKBloPivHOJXC3CayWZOdp6E5N7QJnbSzhKbi/haCBiszJ1epTYNiJRY7cEQAuNhAUuWzhufGdRzQK9y8kTB7j1hd/iYaGGwMByiQ6kt1zOMqdBpbW4jFVdcpRDDc0/QjzFbNxM6XLt1V945vC7iyjUEEJGyeejNv8+rRtxDgMPlwJO024tBdSqWekAzQPSawWiBcRkjF3fNvFYXdZUero406qYKoVlFmuzMrI1VaAGYkl0OF4/jUac8Owr9MEVWze9XS7bZ2pkNNU2RRSOS1ThCWKJVtHNHCxpAXMbFjHf0FbJItTIM6ZNUFGlJ8hHndPY7QD4vFhSLqxnKLudJeVD38K34PYR0rUyoKIKtw8znjzaBCfaWpiJVrMCELQx7/dskb9YR2AIxyWq9ESACFNDPQ6gJXAt8HdsuyglDL5aWpjnTzru2NPC/BZAYILjAAQsdzfCnGwhx5e7G2EsuVKvI4OhU6jcAVEDWwMKuWM4CUJwrRlccGuSr4zHOrOtSeQUji93tyYlEE+fBCeqsFBlcBPESZgoOMSyj5vz/mKbxfYvY7MY7HZeWLfND/tO95moc06vEts10MrYoxv33dm+BxUdzaraLybxpV5suzatlr4KCwzJ19Axsq42BvcIh43XNCiZhYBLGypvRlBPe7Gh8mNUoc2enqYQh3tkfo0JAK2HzTAEunoVtJMLbnFNI6a93eIqvaC5BSp54FqI/GKru9ZEJsfGBgdF7rc3HQ+xpuY/2SCAtd+rTcfjD+4I9JD8oAGEx2qek8A8UfQ+pjTYa9CPPcHf2AZ+X7aBn/3aNvBwzdVt4FPDvCn9xwlEg5uq1FI40iIrs6Xnp+Dr6ptgJY/+/Mb8sLMxP69niBDGNzbmZ0xjAK5ZJgR8F4P4zlffNBHvK5psiBxWm4/PcmxkwIrv/jWphD7mB/I5pqGQJA9/kEqoaC5LJYiFjnIn0mFYEdSPSwJgt4RbaLMiSNKCktHk3+YgZH1XvGIfVhzxCpPcCD3ctj6KV2zoaKx7Jl4BPPNARBGOOa8rPZkYMgKYsNSMr8m+sXEdfIWRBmk9BKHgz8mJBD/IifRmciIV7+RENnVUbtAu0EEfRBl2zcchRYyFBtP9pN7SoI8V0ArkmXJSiboEtG8KvGwS3+cFXs7zHMtAPOudwMtBTOa4TcuIelvpyZywRH88xjD5hmtIALt6CXL8dMUxnXTpUbszyZ3x1yR3DotzlyW5A8FeiYmSQjSYfW0dzSlAPcIahcEWM/0mjijBsYBvgmhSZzAR+uOJUz8hgnQUI3s+EUGKeCiCJDYYEY4t5ruBkZMA1rYIqaaRmK610M9PtA6amfdR5LWhw/iSp2eyVO2ZLBVB9OP28mWpxHJFxEiOM64hN9TGyOIsN3TcYM4MfTDQi0GoKjmwAbnnAGj6mIXCtO8Jhe1brlCYKCXnj4BkL6HlBen7A9t4SqT9ohwoFwBq7JpkRIPzdBDGayzdFp5Jt238eem2KBY32THk+IR6xdL1MSOY6Q3WTbciRAJfETtEkdMMqwolPKrE9KozMb3852J6iS/F9OomtZVndqnOSTePONHoXwPzUwmKs+Kt85wMuwAZPMaBdoG7sTo98YRzzR4lb3hhNeQNxTYJRypA/BzKx8nwiJkONpoBSoYY5cM4QJab10SLeJL9quDk8T8oOCm2N4k4pPv56H62kkr3S76MiWqsBYEI3mAMe8BCAziB/4wE6MGlS4BeorsMFIDtsNQUSGkcRElyUhNtfvI8jenR8MFhHBGv+Hc2e2r0dVHW03OirL2libJC1MiBaPMbRPHwUJieQax8EL4tY5YDp8sOOs20qa2mTK5SFc4ZaoTXU/PdFhbDnK6HGoQlZLJ40OFCmo9EuNj+hnDxCSXk1Fu2cLFgMRHHGsAWCtOjIjc+0PmnH5EYJEllwjm7xkjPYm0FpaTFETI0sQrEdHmKvMWNrZf4UbpAFEHi8DY7rYwXvibuvR+g87m4d+gPiHsn9C1VDCWlJooNfiXJOTykY5wpogcgqOM6MyO9wnLrSqf+JgrTOxGMqBpCbYmyw0yRJjFYanU0GWSDzw+TM2YC+NHVEMBns0xmusRZGji3FYPcDxc1pkhPpiolPoyvdEkCNXU3LRkqGR1JaheExlmhyHlxUZDT4AGnSEQQ1ajEKRLR+bRIxOafLBIBpf08yhsrvzQR98jgtU8UkaCysahKXKx62Q5lWygOqSIPFSGF+m5p7zDAMf5NR5L0/c8LqYQ+FlKprkAhFbGuGiddk5xcUjVqTO5oNZkF1yVxxqTQxVGntE1S/bfpapW2oc+LU2IFEcWSlo8LRWwBdVAi6bwQI/dFsaH7q1VsSNVuqqmh8DHpfKMSTlXpXG8SJf6O8k9iwQJiVVxR1OTp9okXNqQz2/hBQa6VKi3m65c1aXJbKIIhCaQtaX//JyXSzn5RIq29UjhmC9X64pehGnPUZQDS/6uK1omdbLRCQpEynSTFL2cR239fGUFlPTWEQjQUX25+KOwIf01hR8cG/brUSBqpvJ+RGeT47yu1qSyc829wHJn5rb+2+Onv2f/KugrGv1Oy+d8oor3rIZU1f/vXR9fOtw9fvwdzUlkiaKcHmwAAAABJRU5ErkJggg==", + "description": "Preconfigured gauge to display temperature. Allows to configure temperature range, gradient colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "", + "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-digital-gauge-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < -60) {\\n\\tvalue = 60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":60,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":1,\"levelColors\":[\"#304ffe\",\"#7e57c2\",\"#ff4081\",\"#d32f2f\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":18},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"dashThickness\":1.5,\"minValue\":-60,\"gaugeColor\":\"#333333\",\"neonGlowBrightness\":35,\"gaugeType\":\"donut\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital thermometer\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null, + "tags": [ + "pyrometer", + "temp probe", + "heat indicator", + "mercury column", + "clinical indicator" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/digital_vertical_bar.json b/application/src/main/data/json/system/widget_types/digital_vertical_bar.json new file mode 100644 index 0000000000..aecd305300 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/digital_vertical_bar.json @@ -0,0 +1,27 @@ +{ + "fqn": "digital_gauges.digital_vertical_bar", + "name": "Digital vertical bar", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Preconfigured gauge to display any value reading as a vertical bar. Allows to configure value range, gradient colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 2.5, + "sizeY": 4.5, + "resources": [], + "templateHtml": "", + "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-digital-gauge-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":60,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[\"#3d5afe\",\"#f44336\"],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":14},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":8,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#cccccc\"},\"neonGlowBrightness\":20,\"showUnitTitle\":true,\"gaugeColor\":\"#171a1c\",\"gaugeType\":\"verticalBar\",\"showTitle\":false,\"minValue\":-60,\"dashThickness\":1.2,\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Digital vertical bar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null, + "tags": [ + "vertical stripe", + "pillar", + "stanchion", + "pole" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/documentation_links.json b/application/src/main/data/json/system/widget_types/documentation_links.json new file mode 100644 index 0000000000..f33711c532 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/documentation_links.json @@ -0,0 +1,33 @@ +{ + "fqn": "home_page_widgets.documentation_links", + "name": "Documentation links", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Configure and display links to documentation or any other resources.", + "descriptor": { + "type": "static", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "\n\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-doc-links-widget-settings", + "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"columns\":3},\"title\":\"Documentation links\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false}" + }, + "externalId": null, + "tags": [ + "link", + "web link", + "url", + "web address", + "anchor", + "hotlink", + "reference", + "pointer", + "shortcut", + "redirect" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/doughnut.json b/application/src/main/data/json/system/widget_types/doughnut.json new file mode 100644 index 0000000000..8764bbcace --- /dev/null +++ b/application/src/main/data/json/system/widget_types/doughnut.json @@ -0,0 +1,29 @@ +{ + "fqn": "doughnut", + "name": "Doughnut", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest values of the attributes or time-series data in a doughnut chart. Supports numeric values only.", + "descriptor": { + "type": "latest", + "sizeX": 4, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.doughnutWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.doughnutWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n previewWidth: '380px',\n previewHeight: '300px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'windPower', label: 'Wind power', type: 'timeseries', color: '#08872B' },\n { name: 'solarPower', label: 'Solar power', type: 'timeseries', color: '#FF4D5A' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-doughnut-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-doughnut-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind power\",\"color\":\"#08872B\",\"settings\":{},\"_hash\":0.7227918773301678,\"funcBody\":\"if (prevValue === 0) {\\n prevValue = Math.random() * 200;\\n}\\nvar value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Solar power\",\"color\":\"#FF4D5A\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"if (prevValue === 0) {\\n prevValue = Math.random() * 200;\\n}\\nvar value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"totalValueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1\"},\"totalValueColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showLegend\":true,\"legendPosition\":\"bottom\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.38)\",\"legendValueFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"20px\"},\"legendValueColor\":\"rgba(0, 0, 0, 0.87)\",\"showTooltip\":true,\"tooltipValueType\":\"percentage\",\"tooltipValueDecimals\":0,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Doughnut\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"headerButton\":[]},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"donut_large\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "ring", + "circle", + "pie chart", + "donut" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/doughnut_deprecated.json b/application/src/main/data/json/system/widget_types/doughnut_deprecated.json new file mode 100644 index 0000000000..f0fc4e30c0 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/doughnut_deprecated.json @@ -0,0 +1,30 @@ +{ + "fqn": "charts.doughnut_chart_js", + "name": "Doughnut", + "deprecated": true, + "image": "data:image/png;base64,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", + "description": "Displays the latest values of the attributes or time-series data for multiple entities in a doughnut chart. Supports numeric values only.", + "descriptor": { + "type": "latest", + "sizeX": 7, + "sizeY": 5, + "resources": [ + { + "url": "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js" + } + ], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showTooltip = utils.defaultValue(settings.showTooltip, true);\n \n Chart.defaults.global.tooltips.enabled = settings.showTooltip;\n \n var pieData = {\n labels: [],\n datasets: []\n };\n\n var dataset = {\n data: [],\n backgroundColor: [],\n borderColor: [],\n borderWidth: [],\n hoverBackgroundColor: []\n }\n \n var borderColor = self.ctx.settings.borderColor || '#fff';\n var borderWidth = typeof self.ctx.settings.borderWidth !== 'undefined' ? self.ctx.settings.borderWidth : 5;\n \n pieData.datasets.push(dataset);\n \n for (var i=0; i < self.ctx.data.length; i++) {\n var dataKey = self.ctx.data[i].dataKey;\n var units = dataKey.units && dataKey.units.length ? dataKey.units : self.ctx.units;\n units = units ? (' (' + units + ')') : '';\n pieData.labels.push(dataKey.label + units);\n dataset.data.push(0);\n var hoverBackgroundColor = tinycolor(dataKey.color).lighten(15);\n dataset.backgroundColor.push(dataKey.color);\n dataset.borderColor.push(borderColor);\n dataset.borderWidth.push(borderWidth);\n dataset.hoverBackgroundColor.push(hoverBackgroundColor.toRgbString());\n }\n\n var options = {\n responsive: false,\n maintainAspectRatio: false,\n legend: {\n display: true,\n labels: {\n fontColor: '#666'\n }\n },\n tooltips: {\n callbacks: {\n label: function(tooltipItem, data) {\n var label = data.labels[tooltipItem.index];\n var value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];\n var content = label + ': ' + value;\n var units = self.ctx.settings.units ? self.ctx.settings.units : self.ctx.units;\n if (units) {\n content += ' ' + units;\n } \n return content;\n }\n }\n }\n };\n\n if (self.ctx.settings.legend) {\n options.legend.display = self.ctx.settings.legend.display !== false;\n options.legend.labels.fontColor = self.ctx.settings.legend.labelsFontColor || '#666';\n }\n\n var ctx = $('#pieChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'doughnut',\n data: pieData,\n options: options\n });\n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.data.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData.data.length > 0) {\n var decimals;\n if (typeof cellData.dataKey.decimals !== 'undefined' \n && cellData.dataKey.decimals !== null ) {\n decimals = cellData.dataKey.decimals; \n } else {\n decimals = self.ctx.decimals;\n }\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = self.ctx.utils.formatValue(tvPair[1], decimals);\n self.ctx.chart.data.datasets[0].data[i] = parseFloat(value);\n }\n }\n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n self.ctx.chart.resize();\n}\n\nself.onDestroy = function() {\n self.ctx.chart.destroy();\n self.ctx.chart = null;\n}\n\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-doughnut-chart-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#26a69a\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#f57c00\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#afb42b\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#673ab7\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"borderWidth\":5,\"borderColor\":\"#fff\",\"legend\":{\"display\":true,\"labelsFontColor\":\"#666666\"}},\"title\":\"Doughnut\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" + }, + "externalId": null, + "tags": [ + "ring", + "circle", + "pie chart" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/edge_quick_overview.json b/application/src/main/data/json/system/widget_types/edge_quick_overview.json new file mode 100644 index 0000000000..018b3f7587 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/edge_quick_overview.json @@ -0,0 +1,24 @@ +{ + "fqn": "edge_widgets.edges_overview", + "name": "Edge Quick Overview", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Overview of entities related to the specified ThingsBoard Edge instance.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n dataKeysOptional: true\n };\n}\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-edge-quick-overview-widget-settings", + "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"showTitleIcon\":true,\"titleIcon\":\"router\",\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{},\"title\":\"Edge Quick Overview\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"widgetStyle\":{},\"actions\":{}}" + }, + "externalId": null, + "tags": [ + "gateway" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/efficiency_card.json b/application/src/main/data/json/system/widget_types/efficiency_card.json new file mode 100644 index 0000000000..b9556ce169 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/efficiency_card.json @@ -0,0 +1,29 @@ +{ + "fqn": "efficiency_card", + "name": "Efficiency card", + "deprecated": false, + "image": "tb-image:ZWZmaWNpZW5jeV9jYXJkLnN2Zw==:ZWZmaWNpZW5jeV9jYXJkLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxwYXRoIGZpbGw9IiMzQjkxMUMiIGQ9Im0zMiAyMi41IDIuOSAyLjktNi4xIDYtNS01LTkuMyA5LjMgMS44IDEuOCA3LjUtNy41IDUgNSA3LjgtNy45IDIuOSAyLjl2LTcuNUgzMloiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNNTUuNSAyNy4xdjEuNGgtNS4zdi0xLjRoNS4zWm0tNC44LTguNnYxMEg0OXYtMTBoMS43Wm00LjEgNC4yVjI0aC00LjZ2LTEuM2g0LjZabS43LTQuMlYyMGgtNS4zdi0xLjRoNS4zWm0zLjcgMTBoLTEuNnYtOC4xYzAtLjYuMS0xIC4zLTEuNC4yLS40LjUtLjcuOS0uOGEzLjQgMy40IDAgMCAxIDIuMy0uMnYxLjNsLS4zLS4xYTQgNCAwIDAgMC0uNCAwbC0uNi4xYTEgMSAwIDAgMC0uNC40bC0uMi43djguMVptMS42LTcuNHYxLjJoLTQuM3YtMS4yaDQuM1ptMi43IDcuNGgtMS43di03LjljMC0uNi4yLTEgLjQtMS41LjItLjQuNi0uNyAxLTFsMS42LS4yaDFsMS4xLjQtLjIgMS4zLS44LS4yLTEtLjFjLS41IDAtLjggMC0xIC4zLS4zLjItLjQuNi0uNCAxdjcuOVptMS41LTcuNHYxLjJoLTQuM3YtMS4ySDY1Wm0yLjcgMHY3LjRoLTEuNnYtNy40aDEuNlptNS4xIDYuMi43LS4xYy4zLS4xLjQtLjMuNi0uNWwuMi0uNmgxLjVjMCAuNC0uMS45LS40IDEuM2EzIDMgMCAwIDEtMSAuOWMtLjUuMi0xIC4zLTEuNi4zLS41IDAtMSAwLTEuNC0uM2EzIDMgMCAwIDEtMS0uOGMtLjQtLjMtLjYtLjctLjctMS4xLS4yLS41LS4yLTEtLjItMS41di0uMmMwLS41IDAtMSAuMi0xLjQuMS0uNS4zLS45LjYtMS4yLjMtLjQuNi0uNiAxLS44bDEuNS0uM2MuNiAwIDEuMSAwIDEuNi4zLjQuMy44LjYgMSAxIC4zLjQuNC45LjQgMS40aC0xLjVMNzQgMjNsLS41LS41Yy0uMi0uMi0uNS0uMi0uOC0uMi0uMyAwLS42IDAtLjguMmwtLjUuNWMtLjIuMy0uMy41LS4zLjh2Mi4xYzAgLjIuMS41LjMuNyAwIC4yLjMuNC41LjUuMi4yLjUuMi44LjJabTYuMy02LjJ2Ny40aC0xLjZ2LTcuNEg3OVptLTEuNy0yYzAtLjIgMC0uNC4yLS42bC43LS4yYy4zIDAgLjUgMCAuNy4yLjIuMi4yLjQuMi43IDAgLjIgMCAuNC0uMi42YTEgMSAwIDAgMS0uNy4yIDEgMSAwIDAgMS0uNy0uMi44LjggMCAwIDEtLjItLjZabTcgOS41Yy0uNSAwLTEgMC0xLjUtLjJhMy4zIDMuMyAwIDAgMS0xLjgtMiA0IDQgMCAwIDEtLjItMS4zdi0uM2MwLS42IDAtMS4xLjItMS42cy40LS45LjctMS4yYTMgMyAwIDAgMSAxLjEtLjhsMS4zLS4yYy42IDAgMSAwIDEuNC4ybDEgLjhjLjMuMy41LjcuNiAxLjFsLjIgMS41di43aC01LjdWMjRoNFYyNGwtLjEtLjktLjUtLjYtLjktLjJjLS4yIDAtLjUgMC0uNy4yLS4yIDAtLjQuMi0uNS41bC0uNC43VjI2bC41LjcuNi41LjguMWEyLjMgMi4zIDAgMCAwIDEuOS0uOWwuOS45LS43LjZjLS4yLjItLjUuNC0uOS41LS40LjItLjguMi0xLjMuMlptNi4yLTZ2NS45aC0xLjd2LTcuNGgxLjZ2MS42Wm0tLjMgMmgtLjZjMC0uNi4xLTEgLjMtMS41LjEtLjUuMy0uOC42LTEuMWEyLjcgMi43IDAgMCAxIDItMWwxIC4xLjguNS41LjguMSAxLjN2NC44aC0xLjZ2LTQuOGMwLS4zIDAtLjYtLjItLjhhLjkuOSAwIDAgMC0uNC0uNWwtLjctLjFhMS42IDEuNiAwIDAgMC0xLjQuN2wtLjMuNy0uMS44Wm05LjcgMi43LjctLjEuNS0uNS4zLS42aDEuNWMwIC40LS4xLjktLjQgMS4zYTMgMyAwIDAgMS0xIC45Yy0uNS4yLTEgLjMtMS42LjMtLjYgMC0xIDAtMS41LS4zYTMgMyAwIDAgMS0xLS44Yy0uMy0uMy0uNS0uNy0uNi0xLjEtLjItLjUtLjItMS0uMi0xLjV2LS4yYzAtLjUgMC0xIC4yLTEuNC4xLS41LjMtLjkuNi0xLjIuMy0uNC42LS42IDEtLjhsMS41LS4zYy42IDAgMS4xIDAgMS42LjMuNC4zLjguNiAxIDEgLjMuNC40LjkuNCAxLjRoLTEuNWwtLjItLjctLjUtLjVjLS4zLS4yLS41LS4yLS44LS4yLS4zIDAtLjYgMC0uOC4yLS4zLjEtLjQuMy0uNi41bC0uMi44LS4xLjl2MS4ybC4zLjdjLjIuMi4zLjQuNi41LjIuMi41LjIuOC4yWm02LjcuNCAyLTYuNmgxLjhsLTMgOC41LS4zLjZjMCAuMi0uMi40LS40LjZhMiAyIDAgMCAxLS43LjVsLS45LjJoLS40YTcgNyAwIDAgMS0uNC0uMVYzMGE2IDYgMCAwIDAgLjMgMGguN2wuNC0uNGMuMi0uMS4zLS4zLjMtLjZsLjYtMS40Wm0tMS4yLTYuNiAxLjggNS41LjMgMS44LTEuMS4zLTIuNy03LjZoMS43WiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjQiIGQ9Ik01Mi45IDQxLjZ2LjloLTMuNHYtMUg1M1ptLTMtNS44djYuN2gtMS4ydi02LjdoMS4xWm02LjggNS43di0yLjRsLS4xLS40LS4zLS4zYTEgMSAwIDAgMC0uNS0uMWgtLjVsLS4zLjMtLjEuNGgtMS4xbC4xLS42LjQtLjVjLjItLjEuNC0uMy43LS4zbC44LS4yIDEgLjJjLjMuMS42LjMuNy42LjIuMi4zLjYuMyAxVjQybC4yLjR2LjFoLTEuMmwtLjEtLjV2LS41Wm0uMS0ydi43aC0xLjNsLS40LjJhLjcuNyAwIDAgMC0uMy42di40bC4zLjIuNS4xYTEuMiAxLjIgMCAwIDAgMS0uNWwuMS0uNC40LjUtLjIuNGMwIC4yLS4yLjMtLjMuNWExLjcgMS43IDAgMCAxLTEuMi40Yy0uNCAwLS43IDAtMS0uMmwtLjUtLjVjLS4yLS4zLS4yLS41LS4yLS44bC4xLS43LjUtLjUuNy0uMyAxLS4xaC44Wm01LjEgMS43di0uM2wtLjQtLjNhMyAzIDAgMCAwLS43LS4ybC0uNy0uMi0uNi0uMy0uMy0uNC0uMS0uNnYtLjVsLjUtLjVjLjEtLjIuMy0uMy42LS4zbC44LS4yIDEgLjIuNy42Yy4yLjIuMy41LjMuOGgtMS4xbC0uMS0uNC0uMy0uMmExIDEgMCAwIDAtLjUtLjEgMSAxIDAgMCAwLS41IDBsLS4zLjNhLjYuNiAwIDAgMCAwIC41bC4xLjIuNC4xLjUuMiAxIC4zLjYuNS4yLjdhMS4zIDEuMyAwIDAgMS0uNSAxIDIgMiAwIDAgMS0uNy40SDYxbC0xLjItLjEtLjctLjctLjItLjhoMWwuMi41Yy4xLjIuMy4yLjQuM2gxbC4zLS4yLjEtLjNabTQuNS0zLjd2LjhoLTIuOHYtLjhoMi44Wm0tMi0xLjJoMS4ydjUuMmwuMi4xaC4zYTEuNyAxLjcgMCAwIDAgLjQgMHYuOWEyLjggMi44IDAgMCAxLS43IDBINjVhMSAxIDAgMCAxLS41LS41bC0uMS0uOHYtNC45Wm04LjYgNXYtMy44aDEuMXY1aC0xdi0xLjJabS4yLTFoLjRsLS4xIDEtLjQuNmMtLjEuMi0uMy40LS42LjVhMiAyIDAgMCAxLS44LjIgMiAyIDAgMCAxLS43LS4xYy0uMiAwLS40LS4yLS41LS4zbC0uNC0uNlYzNy41aDF2My43bC4zLjMuMi4xLjMuMWMuMyAwIC42IDAgLjgtLjJsLjQtLjV2LS43Wm0zLjQtMS44djZoLTEuMXYtN2gxdjFabTMuMiAxLjUtLjEgMWMwIC40LS4yLjYtLjQuOS0uMS4yLS4zLjQtLjYuNWwtLjguMmMtLjMgMC0uNiAwLS44LS4yLS4yIDAtLjQtLjMtLjYtLjVsLS40LS43LS4yLTFWNDBsLjItMSAuNC0uOGExLjcgMS43IDAgMCAxIDEuNC0uN2MuMyAwIC42IDAgLjguMi4zLjEuNS4zLjYuNWwuNC44LjEgMVptLTEgMC0uMS0uNy0uMi0uNWExIDEgMCAwIDAtMS0uNWwtLjQuMWExIDEgMCAwIDAtLjQuM2wtLjIuNC0uMS41di45bC4yLjZjMCAuMi4yLjMuNC40LjEuMi4zLjIuNi4ybC41LS4xLjQtLjQuMi0uNXYtLjZabTUuMiAxLjV2LTZoMS4xdjdoLTF2LTFaTTgwLjggNDBWNDBsLjEtMWMwLS40LjItLjYuNC0uOWExLjcgMS43IDAgMCAxIDEuNC0uN2MuMyAwIC42IDAgLjguMmwuNi41LjQuOC4yIDF2LjNsLS4yIDEtLjQuN2ExLjYgMS42IDAgMCAxLTEuNC43IDEuNyAxLjcgMCAwIDEtMS40LS43bC0uNC0uOC0uMS0xWm0xLS4xLjEuNy4yLjUuNC40LjUuMWMuMiAwIC41IDAgLjYtLjIuMiAwIC4zLS4yLjQtLjRsLjItLjd2LS44bC0uMS0uNS0uMy0uNGExIDEgMCAwIDAtLjMtLjNIODNhMSAxIDAgMCAwLS41IDAgMSAxIDAgMCAwLS40LjRsLS4yLjV2LjdabTcuNSAxLjV2LTIuNGwtLjEtLjRjMC0uMi0uMi0uMy0uMy0uM2ExIDEgMCAwIDAtLjUtLjFoLS41bC0uMy4zdi40aC0xLjJjMC0uMiAwLS40LjItLjYgMC0uMi4yLS40LjQtLjVsLjYtLjMuOS0uMiAxIC4yYy4yLjEuNS4zLjYuNi4yLjIuMy42LjMgMVY0MmwuMi40di4xaC0xLjJWNDJsLS4xLS41Wm0uMS0ydi43SDg4LjFsLS40LjJhLjcuNyAwIDAgMC0uMy42bC4xLjRzLjEuMi4zLjJsLjQuMWExLjIgMS4yIDAgMCAwIDEtLjVsLjItLjQuMy41YzAgLjIgMCAuMy0uMi40IDAgLjItLjIuMy0uMy41YTEuNyAxLjcgMCAwIDEtMS4yLjRjLS4zIDAtLjYgMC0uOS0uMmwtLjYtLjUtLjItLjguMS0uNy41LS41LjctLjMgMS0uMWguOFptNC42LTJ2LjhoLTIuOHYtLjhIOTRabS0yLTEuMmgxLjF2NC44bC4xLjQuMi4xaC4zYTEuNyAxLjcgMCAwIDAgLjQgMHYuOWEyLjggMi44IDAgMCAxLS44IDBoLS42YTEgMSAwIDAgMS0uNS0uNWwtLjItLjh2LTQuOVptNS4zIDYuM2MtLjQgMC0uNyAwLTEtLjJhMi4yIDIuMiAwIDAgMS0xLjMtMS4zbC0uMS0xVjQwbC4xLTEgLjUtLjkuNy0uNSAxLS4yYy4zIDAgLjYgMCAuOS4ybC42LjUuNC44LjIgMXYuNGgtMy45di0uOGgyLjhjMC0uMiAwLS40LS4yLS42YTEgMSAwIDAgMC0uMy0uNCAxIDEgMCAwIDAtLjYtLjIgMSAxIDAgMCAwLS44LjVsLS4yLjUtLjEuN3YuOGwuMy41LjUuMy41LjFhMS41IDEuNSAwIDAgMCAxLjMtLjZsLjYuNi0uNC40LS43LjRoLS44Wm04LjUtNi44djYuN2gtMXYtNS40bC0xLjcuNnYtMWwyLjYtMWguMVptNS42IDUuN3YtNmgxLjJ2N2gtMWwtLjItMVptLTMuMi0xLjRWNDBsLjEtMSAuNC0uOS42LS41LjktLjJjLjMgMCAuNSAwIC44LjJsLjUuNS40LjguMiAxdi4zYzAgLjMgMCAuNy0uMiAxIDAgLjMtLjIuNS0uNC43YTEuNiAxLjYgMCAwIDEtMS4zLjcgMS43IDEuNyAwIDAgMS0xLjUtLjdsLS40LS44di0xWm0xLjEtLjF2LjdsLjMuNWMwIC4yLjIuMy4zLjRsLjUuMWMuMyAwIC41IDAgLjctLjJsLjMtLjQuMi0uN3YtMS4zbC0uMy0uNGExIDEgMCAwIDAtLjQtLjNoLS41YTEgMSAwIDAgMC0uNSAwIDEgMSAwIDAgMC0uMy40bC0uMi41di43Wm0xMCAxLjV2LTIuNGwtLjEtLjQtLjMtLjNhMSAxIDAgMCAwLS41LS4xaC0uNWwtLjMuMy0uMS40aC0xLjFsLjEtLjYuNC0uNWMuMi0uMS40LS4zLjctLjNsLjgtLjIgMSAuMmMuMy4xLjYuMy43LjYuMi4yLjMuNi4zIDFWNDJsLjIuNHYuMWgtMS4ybC0uMS0uNXYtLjVabS4xLTJ2LjdoLTEuM2wtLjQuMmEuNy43IDAgMCAwLS4zLjZ2LjRsLjMuMi41LjFhMS4yIDEuMiAwIDAgMCAxLS41bC4xLS40LjQuNS0uMi40YzAgLjItLjIuMy0uMy41YTEuNyAxLjcgMCAwIDEtMS4yLjRjLS40IDAtLjcgMC0xLS4ybC0uNS0uNS0uMi0uOC4xLS43LjUtLjUuNy0uMyAxLS4xaC44Wm01LjUtMmgxdjQuOWMwIC40IDAgLjgtLjMgMS4xLS4yLjMtLjQuNi0uOC43YTIuNyAyLjcgMCAwIDEtMS44LjJsLS42LS4zYy0uMy0uMS0uNC0uMy0uNi0uNWwuNS0uNi42LjVhMS42IDEuNiAwIDAgMCAxLjMgMGwuNS0uNS4xLS42di0zLjdsLjEtMS4yWm0tMy40IDIuNlY0MGMwLS40IDAtLjcuMi0xIDAtLjQuMi0uNi40LS45LjEtLjIuMy0uNC42LS41bC44LS4yYy4zIDAgLjYgMCAuOC4yLjMuMS41LjMuNi41bC40LjguMiAxdi4zbC0uMiAxLS40LjdhMS42IDEuNiAwIDAgMS0xLjQuNyAxLjggMS44IDAgMCAxLTEuNC0uN2wtLjQtLjgtLjItMVptMS4xLS4xLjEuNy4yLjUuNC40LjUuMWMuMyAwIC41IDAgLjYtLjIuMiAwIC4zLS4yLjQtLjRsLjItLjd2LS44bC0uMS0uNS0uMi0uNGExIDEgMCAwIDAtLjQtLjNoLS41YTEgMSAwIDAgMC0uOS40bC0uMi41di43Wm00LjUgMCAuMS0xIC41LS44LjctLjYgMS0uMiAxIC4yLjcuNi40LjguMiAxYzAgLjQgMCAuOC0uMiAxIDAgLjQtLjIuNi0uNC45YTIuMSAyLjEgMCAwIDEtMS43LjdjLS40IDAtLjcgMC0xLS4ybC0uNy0uNS0uNS0uOGEzIDMgMCAwIDEtLjEtMVptMSAwIC4xLjcuMi41LjQuNC42LjFhMSAxIDAgMCAwIC45LS41bC4yLS41di0xLjRsLS4yLS41YTEgMSAwIDAgMC0xLS41IDEgMSAwIDAgMC0uNS4xIDEgMSAwIDAgMC0uNC40bC0uMi41di43WiIvPjxwYXRoIGZpbGw9IiMzQjkxMUMiIGQ9Ik03Ni4zIDgyLjd2Mi45TDYzLjggMTE0aC01LjVsMTIuNS0yNy4ySDU0LjZ2LTRoMjEuN1ptMjQuNyAxM3Y1LjJjMCAyLjQtLjIgNC41LS43IDYuMmExMSAxMSAwIDAgMS0yIDQuMiA4IDggMCAwIDEtMy4zIDIuNGMtMS4zLjUtMi43LjctNC4yLjdhMTIgMTIgMCAwIDEtMy40LS40IDguNCA4LjQgMCAwIDEtNS00LjFjLS42LTEuMS0xLjEtMi40LTEuNC0zLjktLjMtMS41LS41LTMuMi0uNS01LjF2LTUuMmMwLTIuNC4yLTQuNS43LTYuMmExMSAxMSAwIDAgMSAyLjEtNC4xIDggOCAwIDAgMSAzLjItMi40YzEuMy0uNSAyLjctLjcgNC4yLS43IDEuMyAwIDIuNC4xIDMuNS41YTggOCAwIDAgMSA1IDRjLjYgMSAxIDIuMyAxLjMgMy44LjQgMS41LjUgMy4yLjUgNS4xWm0tNS4yIDUuOVY5NWMwLTEuMyAwLTIuNC0uMi0zLjMtLjEtMS0uMy0xLjgtLjYtMi41LS4zLS42LS42LTEuMi0xLTEuNmwtMS41LTFhNSA1IDAgMCAwLTEuOC0uMmMtLjggMC0xLjUuMi0yLjEuNS0uNi4zLTEuMi44LTEuNiAxLjRhOCA4IDAgMCAwLTEgMi43Yy0uMiAxLS4zIDIuNC0uMyA0djYuNmMwIDEuMiAwIDIuNC4yIDMuMy4yIDEgLjQgMS44LjcgMi41YTYgNiAwIDAgMCAxIDEuN2MuNC40LjkuNyAxLjQuOS42LjIgMS4xLjMgMS44LjMuOCAwIDEuNS0uMiAyLjItLjVhNCA0IDAgMCAwIDEuNi0xLjVjLjQtLjcuNy0xLjYgMS0yLjdsLjItNFptMjAuOC0xMS4ydi0xLjdjMC0xLjIuMy0yLjIuOC0zLjJhNiA2IDAgMCAxIDIuMi0yLjRjMS0uNSAyLjItLjggMy42LS44czIuNi4zIDMuNi44YzEgLjYgMS43IDEuNCAyLjMgMi40LjUgMSAuNyAyIC43IDMuMnYxLjdjMCAxLjEtLjIgMi4yLS43IDMuMi0uNSAxLTEuMyAxLjctMi4zIDIuMy0xIC42LTIuMSAxLTMuNiAxYTcgNyAwIDAgMS0zLjYtMSA2IDYgMCAwIDEtMi4yLTIuM2MtLjUtMS0uOC0yLS44LTMuMlptMy42LTEuN3YxLjdjMCAuNi4xIDEuMS4zIDEuNi4yLjUuNiAxIDEgMS4zLjUuMyAxIC40IDEuNy40czEuMy0uMSAxLjctLjRjLjUtLjMuOC0uOCAxLTEuMy4yLS41LjMtMSAuMy0xLjZ2LTEuN2E0IDQgMCAwIDAtLjMtMS42Yy0uMi0uNS0uNi0xLTEtMS4zLS41LS4zLTEtLjUtMS43LS41cy0xLjIuMi0xLjcuNWEzIDMgMCAwIDAtMSAxLjNjLS4yLjUtLjMgMS0uMyAxLjZabTExLjUgMTkuM3YtMS43YzAtMS4xLjItMi4yLjgtMy4yYTYgNiAwIDAgMSAyLjItMi4zYzEtLjYgMi4yLTEgMy42LTFzMi42LjQgMy42IDEgMS43IDEuMyAyLjIgMi4zYy42IDEgLjggMiAuOCAzLjJ2MS43YzAgMS4yLS4yIDIuMy0uOCAzLjItLjUgMS0xLjIgMS44LTIuMiAyLjQtMSAuNi0yLjEuOS0zLjUuOWE3IDcgMCAwIDEtMy43LTEgNiA2IDAgMCAxLTIuMi0yLjNjLS42LTEtLjgtMi0uOC0zLjJabTMuNi0xLjd2MS43YzAgLjYuMSAxLjEuMyAxLjYuMy42LjYgMSAxIDEuMy41LjMgMS4xLjUgMS43LjVhMyAzIDAgMCAwIDEuOC0uNWMuNC0uMy44LS43IDEtMS4yLjItLjYuMy0xLjEuMy0xLjd2LTEuN2E0IDQgMCAwIDAtLjQtMS42IDIuOCAyLjggMCAwIDAtMi43LTEuN2MtLjcgMC0xLjIuMS0xLjcuNGEzIDMgMCAwIDAtMSAxLjNjLS4yLjUtLjMgMS0uMyAxLjZabTQuMy0xOS4xLTE1LjMgMjQuNC0yLjYtMS41IDE1LjItMjQuNCAyLjcgMS41WiIvPjwvc3ZnPg==", + "description": "Displays the latest efficiency telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'efficiency', label: 'Efficiency', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Efficiency\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 30;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"square\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"trending_up\",\"iconColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":60,\"color\":\"#FFA600\"},{\"from\":60,\"to\":80,\"color\":\"#3FA71A\"},{\"from\":80,\"to\":null,\"color\":\"#305AD7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"52px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":60,\"color\":\"#FFA600\"},{\"from\":60,\"to\":80,\"color\":\"#3FA71A\"},{\"from\":80,\"to\":null,\"color\":\"#305AD7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Efficiency card\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"%\",\"decimals\":0,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "productivity", + "effectiveness", + "performance", + "capability" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/efficiency_card_with_background.json b/application/src/main/data/json/system/widget_types/efficiency_card_with_background.json new file mode 100644 index 0000000000..643467a925 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/efficiency_card_with_background.json @@ -0,0 +1,29 @@ +{ + "fqn": "efficiency_card_with_background", + "name": "Efficiency card with background", + "deprecated": false, + "image": "tb-image:ZWZmaWNpZW5jeV9jYXJkX3dpdGhfYmFja2dyb3VuZC5zdmc=:ZWZmaWNpZW5jeV9jYXJkX3dpdGhfYmFja2dyb3VuZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48L2c+PHBhdGggZmlsbD0iIzNCOTExQyIgZD0ibTMyIDIyLjUgMi45IDIuOS02LjEgNi01LTUtOS4zIDkuMyAxLjggMS44IDcuNS03LjUgNSA1IDcuOC03LjkgMi45IDIuOXYtNy41SDMyWiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik01NS41IDI3LjF2MS40aC01LjN2LTEuNGg1LjNabS00LjgtOC42djEwSDQ5di0xMGgxLjdabTQuMSA0LjJWMjRoLTQuNnYtMS4zaDQuNlptLjctNC4yVjIwaC01LjN2LTEuNGg1LjNabTMuNyAxMGgtMS42di04LjFjMC0uNi4xLTEgLjMtMS40LjItLjQuNS0uNy45LS44YTMuNCAzLjQgMCAwIDEgMi4zLS4ydjEuM2wtLjMtLjFhNCA0IDAgMCAwLS40IDBsLS42LjFhMSAxIDAgMCAwLS40LjRsLS4yLjd2OC4xWm0xLjYtNy40djEuMmgtNC4zdi0xLjJoNC4zWm0yLjcgNy40aC0xLjd2LTcuOWMwLS42LjItMSAuNC0xLjUuMi0uNC42LS43IDEtMWwxLjYtLjJoMWwxLjEuNC0uMiAxLjMtLjgtLjItMS0uMWMtLjUgMC0uOCAwLTEgLjMtLjMuMi0uNC42LS40IDF2Ny45Wm0xLjUtNy40djEuMmgtNC4zdi0xLjJINjVabTIuNyAwdjcuNGgtMS42di03LjRoMS42Wm01LjEgNi4yLjctLjFjLjMtLjEuNC0uMy42LS41bC4yLS42aDEuNWMwIC40LS4xLjktLjQgMS4zYTMgMyAwIDAgMS0xIC45Yy0uNS4yLTEgLjMtMS42LjMtLjUgMC0xIDAtMS40LS4zYTMgMyAwIDAgMS0xLS44Yy0uNC0uMy0uNi0uNy0uNy0xLjEtLjItLjUtLjItMS0uMi0xLjV2LS4yYzAtLjUgMC0xIC4yLTEuNC4xLS41LjMtLjkuNi0xLjIuMy0uNC42LS42IDEtLjhsMS41LS4zYy42IDAgMS4xIDAgMS42LjMuNC4zLjguNiAxIDEgLjMuNC40LjkuNCAxLjRoLTEuNUw3NCAyM2wtLjUtLjVjLS4yLS4yLS41LS4yLS44LS4yLS4zIDAtLjYgMC0uOC4ybC0uNS41Yy0uMi4zLS4zLjUtLjMuOHYyLjFjMCAuMi4xLjUuMy43IDAgLjIuMy40LjUuNS4yLjIuNS4yLjguMlptNi4zLTYuMnY3LjRoLTEuNnYtNy40SDc5Wm0tMS43LTJjMC0uMiAwLS40LjItLjZsLjctLjJjLjMgMCAuNSAwIC43LjIuMi4yLjIuNC4yLjcgMCAuMiAwIC40LS4yLjZhMSAxIDAgMCAxLS43LjIgMSAxIDAgMCAxLS43LS4yLjguOCAwIDAgMS0uMi0uNlptNyA5LjVjLS41IDAtMSAwLTEuNS0uMmEzLjMgMy4zIDAgMCAxLTEuOC0yIDQgNCAwIDAgMS0uMi0xLjN2LS4zYzAtLjYgMC0xLjEuMi0xLjZzLjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEuMS0uOGwxLjMtLjJjLjYgMCAxIDAgMS40LjJsMSAuOGMuMy4zLjUuNy42IDEuMWwuMiAxLjV2LjdoLTUuN1YyNGg0VjI0bC0uMS0uOS0uNS0uNi0uOS0uMmMtLjIgMC0uNSAwLS43LjItLjIgMC0uNC4yLS41LjVsLS40LjdWMjZsLjUuNy42LjUuOC4xYTIuMyAyLjMgMCAwIDAgMS45LS45bC45LjktLjcuNmMtLjIuMi0uNS40LS45LjUtLjQuMi0uOC4yLTEuMy4yWm02LjItNnY1LjloLTEuN3YtNy40aDEuNnYxLjZabS0uMyAyaC0uNmMwLS42LjEtMSAuMy0xLjUuMS0uNS4zLS44LjYtMS4xYTIuNyAyLjcgMCAwIDEgMi0xbDEgLjEuOC41LjUuOC4xIDEuM3Y0LjhoLTEuNnYtNC44YzAtLjMgMC0uNi0uMi0uOGEuOS45IDAgMCAwLS40LS41bC0uNy0uMWExLjYgMS42IDAgMCAwLTEuNC43bC0uMy43LS4xLjhabTkuNyAyLjcuNy0uMS41LS41LjMtLjZoMS41YzAgLjQtLjEuOS0uNCAxLjNhMyAzIDAgMCAxLTEgLjljLS41LjItMSAuMy0xLjYuMy0uNiAwLTEgMC0xLjUtLjNhMyAzIDAgMCAxLTEtLjhjLS4zLS4zLS41LS43LS42LTEuMS0uMi0uNS0uMi0xLS4yLTEuNXYtLjJjMC0uNSAwLTEgLjItMS40LjEtLjUuMy0uOS42LTEuMi4zLS40LjYtLjYgMS0uOGwxLjUtLjNjLjYgMCAxLjEgMCAxLjYuMy40LjMuOC42IDEgMSAuMy40LjQuOS40IDEuNGgtMS41bC0uMi0uNy0uNS0uNWMtLjMtLjItLjUtLjItLjgtLjItLjMgMC0uNiAwLS44LjItLjMuMS0uNC4zLS42LjVsLS4yLjgtLjEuOXYxLjJsLjMuN2MuMi4yLjMuNC42LjUuMi4yLjUuMi44LjJabTYuNy40IDItNi42aDEuOGwtMyA4LjUtLjMuNmMwIC4yLS4yLjQtLjQuNmEyIDIgMCAwIDEtLjcuNWwtLjkuMmgtLjRhNyA3IDAgMCAxLS40LS4xVjMwYTYgNiAwIDAgMCAuMyAwaC43bC40LS40Yy4yLS4xLjMtLjMuMy0uNmwuNi0xLjRabS0xLjItNi42IDEuOCA1LjUuMyAxLjgtMS4xLjMtMi43LTcuNmgxLjdaIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNCIgZD0iTTUyLjkgNDEuNnYuOWgtMy40di0xSDUzWm0tMy01Ljh2Ni43aC0xLjJ2LTYuN2gxLjFabTYuOCA1Ljd2LTIuNGwtLjEtLjQtLjMtLjNhMSAxIDAgMCAwLS41LS4xaC0uNWwtLjMuMy0uMS40aC0xLjFsLjEtLjYuNC0uNWMuMi0uMS40LS4zLjctLjNsLjgtLjIgMSAuMmMuMy4xLjYuMy43LjYuMi4yLjMuNi4zIDFWNDJsLjIuNHYuMWgtMS4ybC0uMS0uNXYtLjVabS4xLTJ2LjdoLTEuM2wtLjQuMmEuNy43IDAgMCAwLS4zLjZ2LjRsLjMuMi41LjFhMS4yIDEuMiAwIDAgMCAxLS41bC4xLS40LjQuNS0uMi40YzAgLjItLjIuMy0uMy41YTEuNyAxLjcgMCAwIDEtMS4yLjRjLS40IDAtLjcgMC0xLS4ybC0uNS0uNWMtLjItLjMtLjItLjUtLjItLjhsLjEtLjcuNS0uNS43LS4zIDEtLjFoLjhabTUuMSAxLjd2LS4zbC0uNC0uM2EzIDMgMCAwIDAtLjctLjJsLS43LS4yLS42LS4zLS4zLS40LS4xLS42di0uNWwuNS0uNWMuMS0uMi4zLS4zLjYtLjNsLjgtLjIgMSAuMi43LjZjLjIuMi4zLjUuMy44aC0xLjFsLS4xLS40LS4zLS4yYTEgMSAwIDAgMC0uNS0uMSAxIDEgMCAwIDAtLjUgMGwtLjMuM2EuNi42IDAgMCAwIDAgLjVsLjEuMi40LjEuNS4yIDEgLjMuNi41LjIuN2ExLjMgMS4zIDAgMCAxLS41IDEgMiAyIDAgMCAxLS43LjRINjFsLTEuMi0uMS0uNy0uNy0uMi0uOGgxbC4yLjVjLjEuMi4zLjIuNC4zaDFsLjMtLjIuMS0uM1ptNC41LTMuN3YuOGgtMi44di0uOGgyLjhabS0yLTEuMmgxLjJ2NS4ybC4yLjFoLjNhMS43IDEuNyAwIDAgMCAuNCAwdi45YTIuOCAyLjggMCAwIDEtLjcgMEg2NWExIDEgMCAwIDEtLjUtLjVsLS4xLS44di00LjlabTguNiA1di0zLjhoMS4xdjVoLTF2LTEuMlptLjItMWguNGwtLjEgMS0uNC42Yy0uMS4yLS4zLjQtLjYuNWEyIDIgMCAwIDEtLjguMiAyIDIgMCAwIDEtLjctLjFjLS4yIDAtLjQtLjItLjUtLjNsLS40LS42VjM3LjVoMXYzLjdsLjMuMy4yLjEuMy4xYy4zIDAgLjYgMCAuOC0uMmwuNC0uNXYtLjdabTMuNC0xLjh2NmgtMS4xdi03aDF2MVptMy4yIDEuNS0uMSAxYzAgLjQtLjIuNi0uNC45LS4xLjItLjMuNC0uNi41bC0uOC4yYy0uMyAwLS42IDAtLjgtLjItLjIgMC0uNC0uMy0uNi0uNWwtLjQtLjctLjItMVY0MGwuMi0xIC40LS44YTEuNyAxLjcgMCAwIDEgMS40LS43Yy4zIDAgLjYgMCAuOC4yLjMuMS41LjMuNi41bC40LjguMSAxWm0tMSAwLS4xLS43LS4yLS41YTEgMSAwIDAgMC0xLS41bC0uNC4xYTEgMSAwIDAgMC0uNC4zbC0uMi40LS4xLjV2LjlsLjIuNmMwIC4yLjIuMy40LjQuMS4yLjMuMi42LjJsLjUtLjEuNC0uNC4yLS41di0uNlptNS4yIDEuNXYtNmgxLjF2N2gtMXYtMVpNODAuOCA0MFY0MGwuMS0xYzAtLjQuMi0uNi40LS45YTEuNyAxLjcgMCAwIDEgMS40LS43Yy4zIDAgLjYgMCAuOC4ybC42LjUuNC44LjIgMXYuM2wtLjIgMS0uNC43YTEuNiAxLjYgMCAwIDEtMS40LjcgMS43IDEuNyAwIDAgMS0xLjQtLjdsLS40LS44LS4xLTFabTEtLjEuMS43LjIuNS40LjQuNS4xYy4yIDAgLjUgMCAuNi0uMi4yIDAgLjMtLjIuNC0uNGwuMi0uN3YtLjhsLS4xLS41LS4zLS40YTEgMSAwIDAgMC0uMy0uM0g4M2ExIDEgMCAwIDAtLjUgMCAxIDEgMCAwIDAtLjQuNGwtLjIuNXYuN1ptNy41IDEuNXYtMi40bC0uMS0uNGMwLS4yLS4yLS4zLS4zLS4zYTEgMSAwIDAgMC0uNS0uMWgtLjVsLS4zLjN2LjRoLTEuMmMwLS4yIDAtLjQuMi0uNiAwLS4yLjItLjQuNC0uNWwuNi0uMy45LS4yIDEgLjJjLjIuMS41LjMuNi42LjIuMi4zLjYuMyAxVjQybC4yLjR2LjFoLTEuMlY0MmwtLjEtLjVabS4xLTJ2LjdIODguMWwtLjQuMmEuNy43IDAgMCAwLS4zLjZsLjEuNHMuMS4yLjMuMmwuNC4xYTEuMiAxLjIgMCAwIDAgMS0uNWwuMi0uNC4zLjVjMCAuMiAwIC4zLS4yLjQgMCAuMi0uMi4zLS4zLjVhMS43IDEuNyAwIDAgMS0xLjIuNGMtLjMgMC0uNiAwLS45LS4ybC0uNi0uNS0uMi0uOC4xLS43LjUtLjUuNy0uMyAxLS4xaC44Wm00LjYtMnYuOGgtMi44di0uOEg5NFptLTItMS4yaDEuMXY0LjhsLjEuNC4yLjFoLjNhMS43IDEuNyAwIDAgMCAuNCAwdi45YTIuOCAyLjggMCAwIDEtLjggMGgtLjZhMSAxIDAgMCAxLS41LS41bC0uMi0uOHYtNC45Wm01LjMgNi4zYy0uNCAwLS43IDAtMS0uMmEyLjIgMi4yIDAgMCAxLTEuMy0xLjNsLS4xLTFWNDBsLjEtMSAuNS0uOS43LS41IDEtLjJjLjMgMCAuNiAwIC45LjJsLjYuNS40LjguMiAxdi40aC0zLjl2LS44aDIuOGMwLS4yIDAtLjQtLjItLjZhMSAxIDAgMCAwLS4zLS40IDEgMSAwIDAgMC0uNi0uMiAxIDEgMCAwIDAtLjguNWwtLjIuNS0uMS43di44bC4zLjUuNS4zLjUuMWExLjUgMS41IDAgMCAwIDEuMy0uNmwuNi42LS40LjQtLjcuNGgtLjhabTguNS02Ljh2Ni43aC0xdi01LjRsLTEuNy42di0xbDIuNi0xaC4xWm01LjYgNS43di02aDEuMnY3aC0xbC0uMi0xWm0tMy4yLTEuNFY0MGwuMS0xIC40LS45LjYtLjUuOS0uMmMuMyAwIC41IDAgLjguMmwuNS41LjQuOC4yIDF2LjNjMCAuMyAwIC43LS4yIDEgMCAuMy0uMi41LS40LjdhMS42IDEuNiAwIDAgMS0xLjMuNyAxLjcgMS43IDAgMCAxLTEuNS0uN2wtLjQtLjh2LTFabTEuMS0uMXYuN2wuMy41YzAgLjIuMi4zLjMuNGwuNS4xYy4zIDAgLjUgMCAuNy0uMmwuMy0uNC4yLS43di0xLjNsLS4zLS40YTEgMSAwIDAgMC0uNC0uM2gtLjVhMSAxIDAgMCAwLS41IDAgMSAxIDAgMCAwLS4zLjRsLS4yLjV2LjdabTEwIDEuNXYtMi40bC0uMS0uNC0uMy0uM2ExIDEgMCAwIDAtLjUtLjFoLS41bC0uMy4zLS4xLjRoLTEuMWwuMS0uNi40LS41Yy4yLS4xLjQtLjMuNy0uM2wuOC0uMiAxIC4yYy4zLjEuNi4zLjcuNi4yLjIuMy42LjMgMVY0MmwuMi40di4xaC0xLjJsLS4xLS41di0uNVptLjEtMnYuN2gtMS4zbC0uNC4yYS43LjcgMCAwIDAtLjMuNnYuNGwuMy4yLjUuMWExLjIgMS4yIDAgMCAwIDEtLjVsLjEtLjQuNC41LS4yLjRjMCAuMi0uMi4zLS4zLjVhMS43IDEuNyAwIDAgMS0xLjIuNGMtLjQgMC0uNyAwLTEtLjJsLS41LS41LS4yLS44LjEtLjcuNS0uNS43LS4zIDEtLjFoLjhabTUuNS0yaDF2NC45YzAgLjQgMCAuOC0uMyAxLjEtLjIuMy0uNC42LS44LjdhMi43IDIuNyAwIDAgMS0xLjguMmwtLjYtLjNjLS4zLS4xLS40LS4zLS42LS41bC41LS42LjYuNWExLjYgMS42IDAgMCAwIDEuMyAwbC41LS41LjEtLjZ2LTMuN2wuMS0xLjJabS0zLjQgMi42VjQwYzAtLjQgMC0uNy4yLTEgMC0uNC4yLS42LjQtLjkuMS0uMi4zLS40LjYtLjVsLjgtLjJjLjMgMCAuNiAwIC44LjIuMy4xLjUuMy42LjVsLjQuOC4yIDF2LjNsLS4yIDEtLjQuN2ExLjYgMS42IDAgMCAxLTEuNC43IDEuOCAxLjggMCAwIDEtMS40LS43bC0uNC0uOC0uMi0xWm0xLjEtLjEuMS43LjIuNS40LjQuNS4xYy4zIDAgLjUgMCAuNi0uMi4yIDAgLjMtLjIuNC0uNGwuMi0uN3YtLjhsLS4xLS41LS4yLS40YTEgMSAwIDAgMC0uNC0uM2gtLjVhMSAxIDAgMCAwLS45LjRsLS4yLjV2LjdabTQuNSAwIC4xLTEgLjUtLjguNy0uNiAxLS4yIDEgLjIuNy42LjQuOC4yIDFjMCAuNCAwIC44LS4yIDEgMCAuNC0uMi42LS40LjlhMi4xIDIuMSAwIDAgMS0xLjcuN2MtLjQgMC0uNyAwLTEtLjJsLS43LS41LS41LS44YTMgMyAwIDAgMS0uMS0xWm0xIDAgLjEuNy4yLjUuNC40LjYuMWExIDEgMCAwIDAgLjktLjVsLjItLjV2LTEuNGwtLjItLjVhMSAxIDAgMCAwLTEtLjUgMSAxIDAgMCAwLS41LjEgMSAxIDAgMCAwLS40LjRsLS4yLjV2LjdaIi8+PHBhdGggZmlsbD0iIzNCOTExQyIgZD0iTTc2LjMgODIuN3YyLjlMNjMuOCAxMTRoLTUuNWwxMi41LTI3LjJINTQuNnYtNGgyMS43Wm0yNC43IDEzdjUuMmMwIDIuNC0uMiA0LjUtLjcgNi4yYTExIDExIDAgMCAxLTIgNC4yIDggOCAwIDAgMS0zLjMgMi40Yy0xLjMuNS0yLjcuNy00LjIuN2ExMiAxMiAwIDAgMS0zLjQtLjQgOC40IDguNCAwIDAgMS01LTQuMWMtLjYtMS4xLTEuMS0yLjQtMS40LTMuOS0uMy0xLjUtLjUtMy4yLS41LTUuMXYtNS4yYzAtMi40LjItNC41LjctNi4yYTExIDExIDAgMCAxIDIuMS00LjEgOCA4IDAgMCAxIDMuMi0yLjRjMS4zLS41IDIuNy0uNyA0LjItLjcgMS4zIDAgMi40LjEgMy41LjVhOCA4IDAgMCAxIDUgNGMuNiAxIDEgMi4zIDEuMyAzLjguNCAxLjUuNSAzLjIuNSA1LjFabS01LjIgNS45Vjk1YzAtMS4zIDAtMi40LS4yLTMuMy0uMS0xLS4zLTEuOC0uNi0yLjUtLjMtLjYtLjYtMS4yLTEtMS42bC0xLjUtMWE1IDUgMCAwIDAtMS44LS4yYy0uOCAwLTEuNS4yLTIuMS41LS42LjMtMS4yLjgtMS42IDEuNGE4IDggMCAwIDAtMSAyLjdjLS4yIDEtLjMgMi40LS4zIDR2Ni42YzAgMS4yIDAgMi40LjIgMy4zLjIgMSAuNCAxLjguNyAyLjVhNiA2IDAgMCAwIDEgMS43Yy40LjQuOS43IDEuNC45LjYuMiAxLjEuMyAxLjguMy44IDAgMS41LS4yIDIuMi0uNWE0IDQgMCAwIDAgMS42LTEuNWMuNC0uNy43LTEuNiAxLTIuN2wuMi00Wm0yMC44LTExLjJ2LTEuN2MwLTEuMi4zLTIuMi44LTMuMmE2IDYgMCAwIDEgMi4yLTIuNGMxLS41IDIuMi0uOCAzLjYtLjhzMi42LjMgMy42LjhjMSAuNiAxLjcgMS40IDIuMyAyLjQuNSAxIC43IDIgLjcgMy4ydjEuN2MwIDEuMS0uMiAyLjItLjcgMy4yLS41IDEtMS4zIDEuNy0yLjMgMi4zLTEgLjYtMi4xIDEtMy42IDFhNyA3IDAgMCAxLTMuNi0xIDYgNiAwIDAgMS0yLjItMi4zYy0uNS0xLS44LTItLjgtMy4yWm0zLjYtMS43djEuN2MwIC42LjEgMS4xLjMgMS42LjIuNS42IDEgMSAxLjMuNS4zIDEgLjQgMS43LjRzMS4zLS4xIDEuNy0uNGMuNS0uMy44LS44IDEtMS4zLjItLjUuMy0xIC4zLTEuNnYtMS43YTQgNCAwIDAgMC0uMy0xLjZjLS4yLS41LS42LTEtMS0xLjMtLjUtLjMtMS0uNS0xLjctLjVzLTEuMi4yLTEuNy41YTMgMyAwIDAgMC0xIDEuM2MtLjIuNS0uMyAxLS4zIDEuNlptMTEuNSAxOS4zdi0xLjdjMC0xLjEuMi0yLjIuOC0zLjJhNiA2IDAgMCAxIDIuMi0yLjNjMS0uNiAyLjItMSAzLjYtMXMyLjYuNCAzLjYgMSAxLjcgMS4zIDIuMiAyLjNjLjYgMSAuOCAyIC44IDMuMnYxLjdjMCAxLjItLjIgMi4zLS44IDMuMi0uNSAxLTEuMiAxLjgtMi4yIDIuNC0xIC42LTIuMS45LTMuNS45YTcgNyAwIDAgMS0zLjctMSA2IDYgMCAwIDEtMi4yLTIuM2MtLjYtMS0uOC0yLS44LTMuMlptMy42LTEuN3YxLjdjMCAuNi4xIDEuMS4zIDEuNi4zLjYuNiAxIDEgMS4zLjUuMyAxLjEuNSAxLjcuNWEzIDMgMCAwIDAgMS44LS41Yy40LS4zLjgtLjcgMS0xLjIuMi0uNi4zLTEuMS4zLTEuN3YtMS43YTQgNCAwIDAgMC0uNC0xLjYgMi44IDIuOCAwIDAgMC0yLjctMS43Yy0uNyAwLTEuMi4xLTEuNy40YTMgMyAwIDAgMC0xIDEuM2MtLjIuNS0uMyAxLS4zIDEuNlptNC4zLTE5LjEtMTUuMyAyNC40LTIuNi0xLjUgMTUuMi0yNC40IDIuNyAxLjVaIi8+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHBhdHRlcm5Db250ZW50VW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48dXNlIHhsaW5rOmhyZWY9IiNjIiB0cmFuc2Zvcm09Im1hdHJpeCguMDA1MDMxNDUgMCAwIC4wMDYyODkzMSAwIDApIi8+PC9wYXR0ZXJuPjxmaWx0ZXIgaWQ9ImIiIHdpZHRoPSIxOTcuNCIgaGVpZ2h0PSIxNTcuNCIgeD0iMS4zIiB5PSIxLjMiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjIuNCIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xNTE2XzczMzI5Ii8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xNTE2XzczMzI5IiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PGltYWdlIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3QkRBQUlCQVFFQkFRSUJBUUVDQWdJQ0FnUURBZ0lDQWdVRUJBTUVCZ1VHQmdZRkJnWUdCd2tJQmdjSkJ3WUdDQXNJQ1FvS0Nnb0tCZ2dMREFzS0RBa0tDZ3IvMndCREFRSUNBZ0lDQWdVREF3VUtCd1lIQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnci93Z0FSQ0FDZkFNY0RBUkVBQWhFQkF4RUIvOFFBSFFBQUFnSURBUUVCQUFBQUFBQUFBQUFBQmdjRkNBTUVDUUlBQWYvRUFCc0JBQUlEQVFFQkFBQUFBQUFBQUFBQUFBTUVBUUlGQUFZSC85b0FEQU1CQUFJUUF4QUFBQUIzWTdOZHhhTldyc09EYTh3ejB6dCtzV05zT0tyYkdPNU1RSHFoaWtxZVR1L1pKNGlkcWVMNmszTzRMU1BVeWpjYmJ0V2UxNXFzNWxEbEhZU0IyZTBjMW9IRnlhcTAwMG1ISlhyR1NHSXFYd0taZTFadnVKU3I1Wmo2Tytuc3ZXSnFrMlJFRVZpMWRxeEJ6SHVlL1pyWEVrMW5OM1dUazRWbFk3WUZ6WXF3MGxUdU9LMktrZXBTMGFNbjVXMHhTNUNZZm1KdzB2NG1zMlFScmFKR0pGbFNWc0E5QTFKNUlPbWhySXRvRWhFOVFWUm1aSUt0Zko1cEJiWXdDVFFpUGNOOW1PbnloeGlMc2dabExqK29UVUdhTEVZdllVYnVubHpYY0tLbHFmbGVoZ0FQNUlKOTNlckI5V3FjNi9tV0FRTWZvcWM5cU1Ib3JHZExXQ2l1VG93MW1wakFoV0ozK0pGeDA4czFQTFBUN09mWkFpOWx5Q0ZseTFld2ZZUUMranFDYjI2U3VnUFJsRHZQMm55UXhtbWQ5T2pYSE1hek5CSllpdGMvZHVXcFhRa1VlMU0wc0ViWmdoUXV3YUJNN1NxdnRjdGdiMEdsVFZxODE3dlRFOW42MzdYb2RWdVBpKzM3SDVneXlnMFhWS1k4WXppUzRGMzZDMGQxaVZsWGx2cW9TQVd2dXM2aFcyUkdoWUlYUlF1cEYvTENIVlMxdDg1N3VQRTVzZ3RuamgwRCtwVzR0N0w1a1dPWjV1eXZUMlRsRlpPVkRQRUJNVThiUG84cnRCVW1BeEJFNDRDU2VFVGFpelpnWHc3WHZrY2NPeWxVYldQRjhLNk9SVjZOVmU5NUhvSUwxbnowOWJ6NHZRUXE2UWhURTdTakRXUWVIODMwcmY4QVRlRjVDNzJMcnh5L3RXdzJkb3FJSnB4MU45TE1sb3pXemdVc3FaZUtPbFBSOU02cXI0TGgrazUwaTlUMGc5RDhzWWp5SDJpaFZPRGtmZHZKTk9QTzBpUGh3Kzc1K2h6ZEU4MGpYOXRVMUVURmFONkpmeUdoYUhOMHJuTm9teTkwbm42V25hZENrNm5YaFBHL1E2OEIxck8vUnZpNC9FeWpDOWFKTVNWNXVsWHRPUUtVeTlGTm1HbEdSVmExTXBYbUFYQ05PVnREMm95VjJiYzVXeGY1dElpRHd5QStLMVJ1aFlaWTBRTXZPdWo5K21zbVFubGd3SktrNlc2SHh3N1NYRWk3MXFKYU15N2xmZEROUTdpUlNNc3ZTNHpjYlhVY3NqbjZyT1lTc2NHeG1LTWQ3REZDZ3k1K2ZUUGM3OUZMcmNpWE5YaGk4YnMzMnJRMzZEc2VPd0pGcTJaK25vYW1iVXJWeDFleXNXQlBPMXVGRkMwMVhMSTV1cGQ1aEZrQXREanRyWHVKaExXcnI4OUNNTVpac0QwOE1TSUZZdEI2SVoraEpSd2NLYlFLTW1UQzR0Z2VtUzNwUEwxZDFNaFVzTEZ3anlNY04zb3lsWExKWitwZnc2SlN0Y2JHZlN0QUl1ZEMzbmxucEwzdHptdVptL2p6aUdpUE1nNlNLM2xheUZNb1c5UWVVb1dZN0Y5R3NQVmVOMWlDNkZMTVhFZFVBeFh4OVBPeFJ4RE1BNjJ3UGVYdlQxRFVFeVVWTFFhb2J2bjFPcm9kVU1iUXI3cUlSNnhsWWFqL0FJTEswc0pNSTJWek5KUndTU1hhcmo2UHpJMWFuYUhPMHZ1bm0yUVpEZWliSlFtN3V2cVpQSUMwd1UwUXk5VnVjVlRmUWVkV3FtaDFFeU5HbG01alNPZTZlVnVVRXR0ZElsS3oremRKTXNpMG1WazAvblNsTDlUTTdTaTZFcEV5c2QxbE9HcElSTjExUzJtQjFLVTlRUXVGZE1ocS90NGFJTERweFBSczUvRkQyRm44czFFallsNHVGeXZaUkI1S05CSTZXcGZ0NFc5ZW94TlpnTEJaMEJNMU1LRS9ZbnBDZzdrcnlUUjB5VVYxc1lDRDFzcE9hQ0VmU2JYcHZPWVpweWFwU0x6ZFMxcFpRblZXSmE4eTFMVkoxOFhMM1MvUWYwTHRWbG9MdWIwV3JxNGlyWU0yMVdKcXZldWdWT0NLbW1XOUo0Qkw3bzZSZk1VYmNTYlFXNWVoS1phT1hFMXQrVFhhaVJzNnUzRVpaNHA2U3FoRzRvOE5sQ2tXVlZOVmpYRWU0aTlpdWt4OWVnN2lnSTRqUmMvL3hBQTRFQUFCQkFFREFnUURCQWtGQVFBQUFBQURBUUlFQlFBR0VSSVRJUWNVSWpFVkl5UXlORUZDRUJZWEpTWXpOVkZoUTBSU1UxUkYvOW9BQ0FFQkFBRU1BbzFuSW5UVWxUWXZSUDRwUmIrZDVOdEhFbUVXQlI2ckNaSkUycXNXaWhwOWU3TE52MUM1b1JlSXpaend6OHIxOWFya3Qyd2xYSUg4bE1iK2xjaUxzVmNZcTc0N0pydXk0Um5yZG5IL0FCaXQvd0FaM1R0dG1wTFJ0TkE2d21jajNvbGpScS9TZGVyVFRielYwYVBydzlNeEhPSjRUNnFnYXNqU0pzRUx4dDhSTzJqcDJ5SmtUNzZ2ZkxCZU1oMmFEUkZHWmMyVERLM0lPM0pWeVk3NVM1WG9ubDJyaWQ4N2ZqbXlaMndHM1h4anNjdmJKaWJzWEZNeEh1WmhKYkd1NDlzUnk4bFFqRVRGVWUyVHhNL1hjUmJNakdpRnE2cWdhbVBhMlJPSnBkSnB3VmpObnpKQ05mNFo2YzAvcDQxaDhFT2h3Ni9peEUwcExlZ1Zhc0VYMWk1YUo5Vy9OQXZDMEprZTlFWHF4LzhBc1RDRmc3ZDNKa1ZSdWU1UmZabXUrVmtLMGpDQTFqMDdwZHd2eFhQamtIL2t1TGZRdlpOOCtQUkZ5dm5nbW4rVmlJdStLM3RrckNTYThiM0lvZTVKVmN2c0x1U1VGaXE1UmJ1MWZYK0lWdllxNnBzVUJGZDRXNjd0bnE2eXVSdXgvZ3BPWHUrZVBuTmUyYklteDJPelEwSmE5aHdxNXJzOFFYZndoTVhLL3dDOTVjZmZ5WnBXdFdZUWgvTWpiaVVoUC9XTFBnem1wM21DeUF6b29vK1crV0QvQUplMlI2a0wyN3VrcGphWVMvNjJPcEFmK25GcFFwN1NzU3BiMU9wMXUxWUJrRXFxd3ZMQlBURjlzbDVJamZVbEp2MjhzbnU5KzJlV2ovakl6eTRQd2xZa2VMK2VZbUkyQ3ovZjlwRFJwWm45T1VIQkNsUmpWVE5mTC9DVXJLd2U4MU11azRUeU4zelJjZ0krc041RVJXekFwL3JKaFpBMWIySmtSZDkzWmY2dzFWRGxGWW1tZmt4dkZEVmtnU0dnMHJuTS9hVHI5M3RURnlUNG1hdGh4U1RMeURLR0NENGphaG14QnlvRmRKZUJOZmFrOW5WaE1rNjZ0b01MejlqQVVJcW0zMWZJa0RROUd4Z2Z3eVZraVV4REVId3hUYy95WngveGlOVGI3T1Q0a2swdEhCT1JqQ2lQSFRoNXA2NVlUZW5KT1JzZEZYVHh5RzVFY3paZGNyL0M4aE9XVnYzMW1YNmZ2WSthVGdpTVlwbnF1N0s4RzNaVngwVUtKMjN5T0Z3MmUzYlhWeDVTSStFRW5FbFl0bFZ4V3dZVnRJR0ZrcTVkL3dEYmxZY0VtZkhXTFBuSE9PdmpraXgyeFlza294aGpTeWR2UG13Mmc2NjEweWtoOWpNUG1rclo1R2ZEWlJWY1Q4dVNja3NiMW45c1JxZjJ4TnY3WnczL0FBeDQyS3E4a3lRRkhwMDJPMldiNnBadDhvRTJHN05hcW42cm55cVQ2MW1hZy9xeDgwY3greG5JbWRKemRsYWk0UXo0dnJlUElUa01ORkZBT21ham15cEdxSkxUdmNxRVJwUGNiY1lORVQ3T2FSclljK3hZS2FIbXkyakJpVHlCampSclJ1VnE5bHdjK2F2cFNRNUVwVG5XNkM5U1pzdkZNTzFkc2xKOVE5UDBOMnhNSjFPdXZCRndodmYxSnRxcTIrQ1E1MW4wK2E2QTFHeTBwVW5USk1aWDYyV08vU2huTmtONVZmM3RtMlh2OVRObWpYTlFaZVNwZ0pNZmZicnN5VWFNVU82U0dMbGFZWUt4WkpIcnRNOG5aWHhaZ2x5eldHTXZDQW5wWThpL2x5dG1UNGhVSkRjcU9rU0RGY3BaaStxSWdYdVRsN1M0OVFJWTN4Q3MzYStKR2tqa3huYjRLYkdmRllWT1d6aUFkM2VUYkprQ0R1OTZUZlV1MzZPV0VNMFBjamtUR0dqT2J2eWJqWEJlVlNMSkRsWTZNVWF0QVJyczFvakdhYU1palRlbjI4NlB2bC9zMjFQdG1tb1pwWFdleFV5RFJ0WTkvbitDdGRwZ3NjS0JnQ0V3SjFIQjBYSjh3OXJVcTVMQ09jakJFVkxHeGhBanVqQ3NuQWNrdXNjbkorcVpmS2kxZlF3NEh3NlRhbFYycTcydG1IVnRaSWU5UER5K2cwRHBiN0NhOTJIMWZwMDRURFdkS0lpV2NhRUZpZEdSd2pXbXFscXdGaTJmb1NmcmRZcm1FbEFhK0pMMW9TVnpzclVSQTdmNS9RNWY3WmRLdlNiL0FIcnRRM2RocXY1Qk9iUnZpRFoxWFRHTGxOYjZmZDZIMnczRzE3MjB3YmJLbmJ6bzgxQ3Y3M2s1cEd5RUFoWTZzSXFrczI5dDQ1c0M4azRQMGc5czF5QnkrR2RrSDgrbGE1eWlYdG1wWWpVODIxY1dGNlNLaXJrU0V4cEQ4OThiWGtZeGlGYnhkQ2hmYlJkODB3QnJqVGtjbUdDMXpmYmZOTVYvOE9SSFlRYld0VnZESHhpODNjUnJzc1kvL1V1S0V2OEF3WEZDYi9ndUhoR001UFJqOUYwOVJNK0lWMFFZekZzcktBNVlZTk1kWmkya3dmekM2WGtDeldNa2t6U0pqK1VVQ1ZHNnpoWnFMZGJTUm5oVXN4OGlVd2ZIaFkxWjVET1hzczJzT3gzVTJQbXBTdkpTU0FvS2U1S1I1QVJDdEV6ZDF2SmZJUElZL2tqK2tYWTN5a1hHZ2t2SWNZaHNSdGpFZkhsK1dBeHZBSWkrcnMxdVYxa3NPV1lTWXdxT0cxclNiNXBaVi9WeUp2a25iamdrK1VpNFg3T1B4ZmZKTXFMRlJGa1NHRHpXdmlSOEN2aTZjQkNocG54T1dHU3dUZUhHUlpzZUZXT0lITlcyTXQrbmxHNlMwZzZwclVuaVROUS8xV1QzendzV1I1R1E4Y2hHSUk4dGoxNnhlcG5pRmRtcDRJVERDMGJkWlg4MnJIOE1qWE1TeVRUd2RwR3p2ZlZ6VWJZeWRzVXF0YVJxTlRCSFhxa2J4Yms2UnZQM1JpSWlHOUtyMnlpaHhwY3lRNDRVZGcwYXhHb3h2YlROMnljbnd1L3V6MXd0TnpaVXV1S2g1U3lHaC9rcGhQYkNMdGxqUEJYd2pUNUMvTHFmZ3NxcC9hUjRpUFFwYkdSWFRaa21kTGFRN2g2cDA4aSttekkzUDFycDNkdmpVakxuVVlUeFZxWFdyek9xM3A1NE9haGR2WnlGM3p3MXVoUW9CNDVOa1FtcFlxSjl0RnkwMWJWSUJ3enVZNXBlTmdqbVYxWXdJSXV3amRkcnMxSFBiTGxuSnhWTVZCY0hZQkFvUTN0bHQwM1QzT1Z5YnM0OFVYZktDU3dCNU8rSTdkRXdVYUFmVGRlV1hEYWJDTllNZkViVVJBL3lVd2k5c083OE04UTN5RGFNdGh4YzFpVlRlRzFPd2J0MG90QmFLUFhDUE5qR1FsbkE4SW9CT2tLNG5HV1l6d3pERVU4R1ZiTEkwKytZZlVDUzVMM3Z5cWQ5WUxMNTM3eFBtbUxLaWRhcFRXVmlZUnBtbXRQeEhpWWVSTlZhcXQwckVkMVJRRWM2eEt6Z3F0WEs5d3lGYTBpYnBxU3ZqUjVjbFFBNDU4TnRDRGU1c0Fqc2oxMDlDRmM2dUpoNnVlOHF5QlJEa3dWUGNxaU5mQU9tVVZiMTVraExHSXU3a2FKcWROcU5Ta0Y1alRVTm5MWlRidEZ4M3dKaGRGTnlwaHBVYi92Wmt5WkhhanZuc3k3dXFkMVZJaG5uaVRQUEF0ZERWOUdzcjU5RlFXSVlUZk14aGNLN1RFMmFSekJ5SEprclJ0NURSU3lKUStHbXBjbGRSQ2h2TnVsUTVmTWp5ODM4K2JkYzhPYXF0c05mbk5NaXNJN1dsakFwM3dwYzQvVFpYNnBqVENJWWZOb1RXcGpXSGxTZWxHMzhLcnNFRE1rc0c4SllsaGZJN21oQjZYMFZadlVGeUZnbkJscDVXTVNSOEpBcWFjMUkzVURpK1RxUkR6VldtWjJvbWlSQ3h3NXFlQXROWVBpR2UxVkJxaURhV0xhdW1Na2cxVEhkQ3E0OFZ6dDFrL1lWY2xQMk8vRHY3ZStXYnQyWlltK1c0V1BIR2kvRDBIU3FCMUQ1QndlY3BmWFk2aXFRUkNCWkY0dkRFRGYwQ0pLdEJNYW1oOVBVY2x0bEZ1M21QVHI5U1BMdGZyamQ4OEx5b3p4Qm1acnhsZllKQmoyZ1hPR2x5TXNoS2l0WGloR1JXMmtOakJwMHRWeW9saE9MS2FKdWFPRlp2c1d5UmJPancyTjAvR1pFY25WT1JZMXMzeVVwdlRmY3pOUjJXcDAwVnArYXNSMGp3NnZZVVYwdWkxM1lQbFR0UVRyM1RRcHZVNko5RDJrYXBuc25xTkZXTkpGTWpEa3h5Y21TVitXdVMzZlBma2gzYkozZG1YT3pKRGtUSmxySmVzQ01iaTBXbEp0RWVwSEpESDZtQmxFWXliZUxEWHk5RlpSeVZQbk9BeDVMTjFvWE5Pc2pLbjd5UEx0ZHBoczhOSHFtdnBUZDJiZUlMWkx4eEVqOW5RSWNZUzhIZDgxUFlrZ2pBTURFeDhGc3dmcUs1dVVqaTBoZkxqazdqaGFzb3JPdldkTnN4Q08zVUZGRWhwWVFiWUpTU2kyTmZxdG1ycThiREwrMHlKYTllTlExQlhUUlU0YTZxWlR5cEhVSkJxNDBjdldGelZmRFdmSmNZMWM0MjRwSytoY2xkenZ3L3dDT1RQc3JsOGlkYmZMSmorS09HemZOUGVLbHpweXBGVGdwd09IUzNNV2Q4U1NRSmZJeTZnTFF4aWhzMnlNK09VUjlIa3JINlZucE1vSkE1Q0FJTmQwdDNmVkZ6dzQ2S2VJVXAvUDE2NWxJMWtORVRkWWt1UTkzcGhiSkpwMlhER2luSTFFa1NDQnNqeG9saTlCN3pGOTVpcmswQkNSbmo2MmFhaUY2VE5wSEZiZHBnazJkSzVaVXdqdWNVOE16a0lsWmU3UGM2MWNtTWl5a2xNWWUrSTdLdnhUOE1haEZheFh4WFRmSGp3N1Jla3lYSmRrTFhPbkx5ZDVXdW43a2tQWGJKait5NWJwdXpmamlzNUw3WjVaRlJjdHRESFBBZFBtU280UWVIdDFwM1RNdDBVY0h6U1duamFPaWx5bVNxQmMwOC9xOU1uQkdaYjlwSk1pcnFHTHFVbGxwN3A5UzBKck9XcVQ3STBabVZscHE2YkRiT3JQSTlQNHo0Z043dlNBbVNHZWNQMUpQZHpZUUdmbWRrWUVkVEl4Ujc0SHA3S3FJaVp3NnowSHZsZkViRUYwbTk4NnZGaEhPeW5lMGxzZVR4OVUrcU00WmJOZzJJS2dTMXNLNXZrQlU3R1I2UFVFVTdqUkw4QVhuZzZvTDJOcXlTL0NhZmx2N3lMZVF1TFJzQ3YzZ3k0eUtvdnNrTGdSTlJ2cjZtYVAwQkFmdEN0dm5KcVNyMDU0YTJDWFVablRUV1YzQzFWcU1ka0tQd1pTcWpPaWpQYTBYNXo4c3J1ZFYzQnZKeVhNeVRxaWRPQzRNbVdWeksvVWxoV2g2TVFqbXQvVzIwZXhmTUhlcU1meWJtNnY5c2FybXExMitCTTNiWnE0RW5FemVHK1I1UG8zZGg1L09JWGdtVXE5TVpENGVkTmRHV1A1aDNUcXJXZFR5MG0xNTFZK2wxQkN1Z09Pa0xpVWtscEFLc1VMZVRPcDBmcWhzUjVlRFc4ZHNjamQvYklveHZibi94QUJCRUFBQkFnTUZCQVVJQndnREFBQUFBQUFCQUFJREVSSVRJU0l4UVFRUVVXRVVJREp4Z1NOQ1VtSnlrYUd4SkROamM3TEIwUVVWTUhTQ2tzTFNOS0xoLzlvQUNBRUJBQTAvQW91ekIwYUVSMkhhaEFQdE9pakxMTk5paTBmRmhHa1hqTXpWZTZ2K0ZMcVZkU0s4UTlsaCtuRU9TN1phWFMwTlRqNGxRekV0QTBBekFGWEZORGZyUzJldkEzS2h0LzhBVUZYdXIvaFU5U3BGYVNPNkhzbjBDME1oYUU0dkdVa0hSQkVZV1ltWDB0WU9PRVQ4VkdMcW5VakQ0eVVkN0RoQXdtOVNiNXZyS3ZkWHU3bFBkTDBWN0s5bGR5N2tCMUtyelN0TUc2bVFoc2lEM3J6YlhhQzc0U0sxSmpPUCtLdG9rTXo3MDU3VGhWTGZ4aFZxYUYwbkx2WGVVRG4xTzVkeWJ5VGh3NmhjVjNMdUs5Z3IyU3VjTXEyZE9TdXpXSDhhclUxVk9XK2FoaytWRFhHNWFPYnNwSVB4WDhtUDFUZTA1a0JsM3hUMnpodmZDWmVQZXY1Y2Y3SnBIbEtQMEtKRlR1WHYzaDU2a3BPRFlrbHptZzkwc1NlUk1UdUZ5d2ZpVlkrYXJVNUljOTJoVVJwQkl6RTB6c3NiRXlYM3hUdTB5SkZKQlRMbXNaRklBWDN4UWY1ZUUrUGg1RlF4aGM3TnczMW5xVFhGV2grYXFXRDhTdEFyUlZvODBlS1BvT1RZMGcxeUhCdTZkNG1nNlFHN1VOdVZvTjlSNm53VFRJOTZnVmtOQW5NcDBTL290VkdsMkpUWUpWWjQxV3ExV3ZhV21JS0d5b21mQlJJaGRtcFpuZDNJNjdwWXdDaEk1cHpaakxjVE1DN3FIYzV4SjhweFF2ZGRjRWJPK1hyQldnVm9WUFVvakRKeVlKUTJWNUJNMk4xUkp1eVU4d3d5UWRqSmhPSzQ0cEsxbmFtRTdEeVJuZlE1Um9MYWFXRTBpZWFjNmw4NEp3Y2hJWElBQUd5S2V6eVE2TzNDRk9iWTBnWmpoVEphc0d6TmFlcE82YVpYRG9jZmo3MFBzMy9vb3pnMkhBWk1rcXVGK0lLMEN0U3M4TFY5MmdMNjdsMFdjdjZnaUpTOEVDVllzS3RBUWZGVGlOSUI0elIyT1h1VnF3LzlWSlVINTdwbGQyOGNWTTFQdGpybmNoNThQYldqNWhWQ2J4dE1NeXZSanc1UTNQbWMxYUJXcFFIRFZEZ3ZzZG9zL2VuQ21jVGJTV3lubkxWTnFMUjRKM2FhWVZONnNSZVNueFdWejRjVTJNLzVJN0hweUlVYVBCYVhUN09GWlZLZy9NOVp4azJ0OHBsUGZhUDIzYUJXQU9GSVJGNTZPNlFYT0M1UklqUVhzZzBnbWFyQ3RTcmFYMVUxNnJhVnRPMU5oUmRwaUNiWUE5SjBsdEVBeXNXU01LL1BEY2ptcTBJQjg3bW5PaER0ODBZNTg3MVYwVjJSNWhNc2kyZnNvYUJRVy9STWRrSXdubURxb1VaeklHMGtTTVZtaDZrQ0U2SS91QW10cmZWc2NHTE10MmVIUENHRGp6VG81c2cwMCtUMFhxeFNQelI0UnorcUVjVXc0bm1xMEN0U3JZa1hMWEpFWHRkZk5PN1Q2SkFwZ2NYZDBrKzhUVmc3NXFVTStNMTBtNysxZEhpZk1KMWlCZjZxMWtvTEdPYXdzQnZRRndBNm5RSWsvY3JPSFQvWW9rRmgvd0NRY1Z3UEJmWnh6L29nTUVOenNqN2s2SVhGNW5tcXdyUXFPN3lVTnJwTmNvanBTYnRCdlE4Nk5qSTk2cS9WT1k2NU5OMGtZUnlnbmlwUXkzeWVzMDZKcnM4cFllNVdMeDlWck5NYkNjMFBISk5Gd0NzRzNvQmQ2OXBTOUpSWUxtT3hjUW9PMkdFUU01WDNxeUZEeVh6T1drN2syNnF3a1BlaExGYUlSQ085VmhWbFFOaXJoVmVhYXMxVzhWZUNGekFJSkplZUtlWmR6dUNhUzF3THNsRWpYRUhPNU9lSFV4SXVZQ2hzTHBBajlGQ2xWVy9qNEtGUFVtYzFDbUM5dVJUK3pEWURlbVFnRHVyTzhGUGlsN3RwdHFyYk1UcDBWaXdERTg4SnFSR0xNSjhyd1JPNVY0WWVHVi9jcXdxeXYzYi9BSmhXN3NQSENtTjhwRTBaeTcxRHFuODFsT1dhaFJNZVYwNXF6QmpPZS9DeWVnQ2NLWU1lRVNMK2ZGQmhmdG0xRHpHcHQ5TzB4SnNlZUhKTWptSHRFdUtoSEhkOFU5czJrYTdxenZLMldLYVh0blZlakRBdE9qbStWM0JNd3h5WWZZT2srOUh0MC9zMmN2SFZPaVlRN1oyc0F2eXVWWVZhT3dlT2E2UVExM0REZXAzeXlVUXUweXVSMUNpUkJVUzFCdmxtdnVCbHFPS0xxb1VKbDgzVDE0SnpERDJ1QzU5TlFPb1BGTWJMNlM0Tll3L21ueERHalBZOENiaXNqVkVURzFNYnd2M1ZucUIyU2dpVFhSSE9ubXRxQlphdWRTMG5UUEpSTDNiTHNaYzZnNkxaelRFaU0yY3lVeGVxbDBHNFQ1b3huU3ZrT3l2dmYvRUwyMFpoTWlFTUR1RzZXU2RPK2tJYTBCVnlKbG91U3FhNThJUHZJbXNuQ3huOGlpTzBJRjN4S2ZNdFlSdjQ3NE8wVWhqb2tpNGFYSWkreUV5Q296SlFRKzR2UE5QaVZCbzB2VlNiQ3BOcHdLZ05KTitpY0xxbTNvTGpOZTBqeFhFYW9uVlp6NHF0QXlxOFZhbk9LSjVwbHp5ZjJjeDhRZTFNRStLT3NDQzFxNVBrdWNVcjcwcjIxM3AvcEZTRUxvOEZuYTVsTWhGczlUSlhLYUxSa25adExzL2d2UkRrN2daZmx2MDNETzlhSzlQaUVxZlkwUStQSXBsOGVHQmQzaFN3MXJXakxxZi94QUFtRUFBQ0FnSUNBUVFDQXdFQUFBQUFBQUFCRVFBaE1VRlJZWEdCa2FHeDBmREI0ZkVRLzlvQUNBRUJBQUUvSVRyak16TG8wUVpsK1E1VENoSDY0Y2dzQUEvaU1HNS9zd2dBTkNKbjZxQVZEb3hrM2hpQ2ZSakFKd1J3SERYditld2dBTXd4cDIrNFRnNHhDQVNJejJhOEVqNDJlaEZqZWNTQS9DWkY1aHZWVm1ZV2g0ZVp0eEU0K0J0TUZHSnM2U1JpbjlERWhuRS9SQ2hBTnl3WVpEWWNHNGNRVHBjd1k2SXovWUNPcHEvcHdRSm10RlFjR0dFTGI3Z29yRWhjcnZqc1FnelE5WWRZMnhuSkJoNUgyREQwc0tQQnhoajRrN1RrNHpWRWdHK1ljZFRlb2U2eTRNSTBueGNRWFIrVVdMaENhVVlrZFE5ZDQvcUVrMzMvQUpoekJvUWlTVVZDQVlDcjJKVzNJbERHd2EvZTRRc2Q3RUdRR1lKTUEySU0xWnpERG9YWVYyanFCdXJoOGNFUFc4bWpESzlnbUxjaGZYL29rREZDU0NCMDRJSG1CUFJnVHVKaHRyL1Vjak9sZ0NOeStXVFhVeFBiS0NkQUFZTkJneEN6Qk1XU1BhSEN4QWFCUnVCYSsvOEE1UXVONjJTT2ZlVlpwd2h0emdoQUFrOFpoUmMxbDBXWXcvTWlvQ2E1Y2FmTUF5U3BKMlNIQnVDUTVtUm00UVFtYTI1cEcxK0VPQXNqOXpLelV6QXBJanBRaFVLWXhOelpRWDRuTVI1TkdHTFBYcFdUSUd5K2gvY2g0ZldiRmFhQXhUSXg1TjRMdTZSL2poUU5ab3NsQVc1TWZ6bWFJUGVlcHhFVWd3NERvTjl3UkVmZVcvdkFaQzFnenJJNVN6M08wWUhNNTVoanVQeTNNTmlEQmhGcTdlMklLcEtZQ21VQmtpbEhVTVB0SXpZUG1Kd08xRmd3QmR3NlFGSDlrcmhaemFya2p3ZGVSZGh3WkMwZ2VBSEw2RWNuOHNLbkFBMGhiVjVtZEluWVBQWWhKRklXYWoybmc3aVA0UWNUSUN5eFAyRTVnSWh4QmFMcmhsNVVld2k4L3dCakFuSi9kRUJSeWhEdU5uMElRRHdoZEkyZUZBQ3pPYzRoZjFEUGQ1MEI3Z3dFVkVGQ0pBV01BR2toajd3dUkrSWxrMkZRSHlCbUh2Y3dycUUvaWpwaFF3aWtIM0Jna1RVY3ppcml3bHZCMXdxQk9rNmxhUUZhY09nUU1BaGtVa2paeEN0WUxSZEd2V0hYdy9jZDdkNE54clVUMEl5Q2pyT1dwSUdCam83QTZoQm8wVUo3d1ZKVWJXU1ovVElCQ3NFU0t1TENTRGhDSUZyUmhLb3JLVjd3U3dqVG9lSVhNbUhKZThIcUFkT0tOYXczM0xGUkhNRldENlRISWxIMHFKTVBDVS9tVjU2dzJ4Z2hHVkpobEs4NGhJd2g3bklGelUvWkZHMkkrc0NJb1hCREFHbzFIMEUvTC9DSkFpUGtPQXNNbUN4SXdIeTlIY01JR1R5T2JHSU1MV1V6WXBTNXBUTmtFdTNwR1ppTUJnWk44UUYreG5qWkR1WGRJR0REVTdRM3lOd0ZhekJ1c2x6SlFOWFVIYWFLb1pVank0REgxcHRSQ1lETjNHeEFDbUwzd05TdHMyb2JNVERLUUxScHA3UlNJU3NTSHNoQWk0Wm1yTlVQTXlYQW5OWVJmNUplSllSeGNhTmUrRkh3SkJKYWhOSmtGSUNQb041NUlXY0FoNEJDYXpHK29jcEQ0UUVzQVIwY01JSllGNWtBTFBpQ1o1MmcrUVVkMUdhT1FnMlZJVWNHeWpZbmk4VlJqaWNReVE3Z3N3NytxRlY4VUs4TnBueHRiWkNyY0FTUldTRGg4WGhRVm04VjE5a3Rja1ZCdUIvYkxEUi9sbWJnSG1hUzc4bmhCWUFNcSszS1E5Q1pKSmtTblV4R0tHYUlSNXVNc2pXTGhNNFlySnRFUmxQQXh1ZmxEd3VJS2FacU10ZXNLRFBnYjVnQWtnSmtRNUNlWmRHMUJCWnlnSWlCQndEZVZkUm9RQ2xOMGc4bWJqS0F3WTVqQ3pVTEF6bUpSMjh3K095RGhNWTNveEY2YWFDVmJPb1F6UmY3eGFLWno1Z2JHd1oyaHR4SFFrVVVCNkZ3M3FpSlFIeE5RN2FHT1NaRWJhY0tmSG1vZHFCMndUK0lzckZENVF4ajQzWDBaNmhDblNvL2lpZXM3RDdVVlMzQ0hhQWlqRUNhSDVtUTdJeE1YUmFlZVh2dmNVbDZoV2dtY1FkSVBhTDZoYjI1Q2kzZFBhRGJGcVlabmcycVV1QmZzS1NWVjRYUGVLeGw4T3JBbFBHRys4SGp4KytIallKWGdSZ0hqQVFpV1VsQTlFS3ZZbEh4QnVLcUJZT0VLNTE1WTVtdlcyK0tRMTJyZlBINk5qaVRBSUFjUTBKRndHcjZYQWt5RnlOUWVzZGdZRW85RW4wbFpyRlFFSUR3eEpNeS93Q1VVd01ya1pnZWpnb3RVR2tVTUx3RmJFd3JDM1hFQ1N3TGIvQ05FTHBkWk5HSEV0dkVqY3ZFV1pmN3dmUVB1aXFyR2FPTFZHRlZLdWhlT0lONHRPN1dRRXhOUVY1ZzR5d0lMTXlCakJQWWtYNklSQlp0NnlhekRNVWR0b21zUHhPUmp5TUhVRUFKTWd0eDZ6cmlHUk5naXE3bGl3UStZR04xV3hnUVh4SWRGZUdBeGdvWHlpQkpnRVdPTlBXZFNQc0VIY0h0Q2hvYlNLdGNvTERJMnQ0b3h3UG9nNk80QWMrWDFoRjJFZVlGTU10elhUdU5CN1lFMmV2aUEyRmVTWUFJRGdRUXVjUE1mY2lIRWRBamxpTFJqSG1XUXVoK3owNWtKSkxVSTQySEpWMUtHMXBqRHNrdWtCdnp1Y0E2SEVlSzVlQkdWUFhKaXRuVWVHSURtVUVPcCt6TXkrWWZqa0VUSDdpZUtxZ0tpZ1ZLZEdNeU9RQXBtaG5pYytRbG5LZzhiblVXWWJQVVZlOGxORDN0aUV0djhrVzJsSHYvQU1Lai9DUUJUNFBxSDJwUmhjb2VFSW9lOEkyNXhCTW8xQzAzNWh0cW1wVUFYTXVxZ1pvTzlybURaN3VKb0FPaGgzWm1saEtRWDdjclA2N3FPM3JIcWdPT2xITGxWdVlBMGRQbUJQZzljTWNEcUdwL1F5K2pDZEZGZ3JlS3lCeVdSeDFFL3dBMG14Rlp0WWdFU3pEUXk4bFZCb3RXdjJWY29yaXdyQ01vQ210eXViZ0FYK3AzS2h5KzV4WEU5UitJVjNLbkVreEc2aHVHN2l3SThwL2NXQ2hFNmt4RTRqbEUrSU9SVEN4SGxHNDlKdlFlWWFzSzlnc1FPREV0TzIzWjhBSmczZ1d5VEpCS3ZpRzBPYWxNdGdvY1ErckNLdmdUT3IvdHphNEwwSVpzL3dDa0lKb1ZEbEc0aHIxWkJ1VTJ2WkcxNW1BempneG9ZaXdFRnoxWTRKUFVkTm9oaDVxb3htZ0JCYmcvV3d5ek5HME9tNVhROUtpTmt5cnBiQkxmVU5aQTBmY01YeWlNSGhtTG13NUFuSkRqTXBFcEtKNzFEMmtnMHg4Q1Uzd1lnTmE5NE5vNXkrb09RaFJBQWRUUEVZQmwyYUVmRHgvQmhHSWF2eVJtRUVvVmw0NGd6WnVrZU9DeWN3NDg0UTF0KzNDaXZjelZSS3poTGlxK0RtWUxPQUp3aHM5b2tlSVVnVm1vNWdDUXZtbUZsYm9RdnBrMlJRckVJWnVaa2hWRFhoQ1RLeStUQXBqd1l4QnZBdFNrTjVFRGJGUWV5TGpzWXh1Z01wWmF6OC82VGQ3RmpaYUVHeS9NUjVLNDlFWWxMblhDRlFpV1hJL2pUYXp2KzFQZjNvbjJZZUcxaDVqbVFMQ0FZY1pyeUU1TWR6TEcvRTNFU0ZQQmpoeXlDQWh4Sy9pQjJzRS8waTFWblErUkNHalFpbUN3bkVLVEY2aDZaUDhBS0ZPQjFDM0Uyd1RwYVFLREZka1g1aHk1WklkU3dOY3ZCYlA2d093YkVTdUtBdmJ4MUNqdlJCOXFBazg1ejlvQmhlRUxZWDBJVVZqTS84UUFKQkFCQUFNQUFnSUNBd0VCQVFFQUFBQUFBUUFSSVRGQlVXRnhrWUdoc2RIQjRmRC8yZ0FJQVFFQUFUOFFDZmhOUnJyYkFwNGppVFF0NTVHRnBxQjh2WjVlc2dONURha1BVVXNUeEppQ0FLTUlucjlLeGJDOGlRdXdKWFQyQkFBTDRSVGViUnBINFJHckFJaCtadTNPWVlsckg5WXBLZkpHMWx0bURYdm1Bb211bnUwdHFxdlJITFQzbk1yQ1BaSEc0VXRkZzVxNzlKQ1lxajYvT21sYTdmQWwzZXpnWlZzZ1VBV1lIcWVsMjhxS0YzMGpVdlp3UWgxU3hhTWF0OHdRbkVLcjF6UEpYZ3lMVDFDYWRTdVNoM3FGWXVHcFExRWcvQitHYUVFcmlkUXpPSXVjL2NZYTZ2N1ZDd1BleDIvQk9ZcER3UTNxT2Q1YlF5VnNudXRTcmFyZGhQWjFMbDFITEZ6VjlqY0tvejJMaUpRUXVkcGloY1ZBWXVHdFFleW1hcldVVEtmaHJDenBzdnlobEZ6NXgzQjBDMkNzR3dkQUNzREl4VkpDdVU5TVR3UEM2d1JBMXFsYkNLNGpqb2lXVlhHMHNFNVhYT2tMdUQyakhjOWRCbG9ud1Qvc1BlUGtKdjZoM1JRVmlyV3UyT2xhc0tyY0t4aHQrU3FnTzlZallLUHpIb21vZGxzUnJJQmhLbW9mS3k3TmhDQ1hsYm5Da0pmaW1UbHRRYXZSaWMrZ1djS3orbExpcmpGWVdsNkhJakpUUkFIVE9UckNRM2NQQ25pZE84VHVzZzZwY1BZc3BaSzliUFArV1ZhbDRkUDFGNmFjdjRMZ0dGRkM3aHFSMklzVXU5cHVCM1ZkOVRmL0FCbitVUTB2am4va1dCM2dsdEFhR0RuaUNWWnJzQkY4azBXTHMwVHVUakxOL2FLaE9FdU9xczd1TnJDMDAvQVpHaHBuQ05mNHFjQUYxUWYwUXdCSEFhYzkxQjlUb0lmWVM2a0ZodEx1RGE2eUpaZWxMbFJTeHZJZFhyRHNhb0hiTjZKREVnUzdlcXdMbWhQRzdWU25wVHhjR3ZCM3pCNU9iTUtyblFDeTIzSmZBVlJMcEl4QjlwZVhINHdqc1VtM3FkYnAzRVVwVDh5MnBoYU9EN3Uvc3ZzVFdIRWJ2ZlZXRml0TFdaRlBBR2ljUThrZW5VME5wUXk2TTNuRnRWMDhlR3NYMmJERllZd1FBdHVTc0JuU0MzUXQyNHJrNDF3RVhvUUsyS3JLN2wxQjVKQU4zZTRpcllYZ0V5OE4yd2dnZlROS2dLd0F3TVljSVVnemlKeG1MenJGeXlhRnNYMmQrb242Q3dRSUZHbEZ2MU5odWd0SkI0dFZtOTRScHI2WUtFUmo2TGFPZ3owc1Vzb0tCQ1JkZFFUN2hJZTdaNjcxR0wzS2pKOUtDbDM1K3lISHdtYU9CRUZ2VVh5dEtvQm5qaEVpWGpNbGtQOEFsRElBOXdhWDVpaTNCMFZ1Rm1PRjJoc0czZmNNa3BzSzBPbUR4MmczM0d4ejloOVp1RFYxMmNQQllVUHVZQUo5RmcyOEpRNllxWXJ0TXZ4RDZCTjY5NUZZMm5ud0pGY3JmTVNnVUdEK09ZV0Jua2hoaGhVRStSbW1TMDZDaHFyRGFtZGt5dmRvR2NZRDhyamlMU2tXdDhQSkdJRjhJemRITW9zdkZYeXdHbTJOc2doRmhBTmRUUjkrSWw5ZWZNSWVRK0tsdENjYzBVTkxUWVhYVVllOWdLcG91N0FQUlliRDgveU5CdG9ncnZUajRrSURRSytkaXBWWUdOY21zRWZWMDQ5ZmNkQmsrUm9JeXp2bElMRjNpc25NZjVJcmFvOHpjRm9ONDQyNHVQQ3RSN3pOci9uUDFaRmVmcXFlVk9wUXo2K0FYem5VMk1yMmZsYUhIUkd6MXdtbEZGdS9QdUF1OHFqWGs0VGNqTmtmVzNVWVFCSlZYZ1hVZU5CK0Ryc2QwNTVrYlN5dVZKeFZHMkN3a3BXYWNmS0FwWXdTMUYxc29MVCtOQ3dMYzJpdENWMnEwK1d1T3EybitNM3NBMDdMUXdkRHVWMGVwUlJZVldsN1pveWxZNk4wV3NMMnp1S3F2Y1ZBV2hIQlpYTFlHaWg3TXJ1YkdjbmtwQSt4a3ZFa1N1YzJMQWxRdWQxS014UlN0bFBNSk9wVlU3QTNHekZMM2RMbGFFSFVmUFVjcFd5V0M3Y1ovWW1nQlpWclJzNTRnOFFLOGxBTmRndUwrVUVka2RvdmlZejErTlcxUXIwd3J2R2tTVWhkSW8yTldmS0dkNHBKVHdmaUJRSlErOXE4aEJETXhGWVc5akdzeHZqdVVHd09LOWFBRUJXd1lrZElrdlBPWlpSa3FhN016SnZQL2FlRG0rY2ovd0Q0ZkNZdjFTQXRtQzYrcnVQSFB0V3FLanBhNFd5MHRmbVc5YW1WeXVKanppQ2dCR3BTZTNGMyt2VnpGSWRPUUE1dEQ2dUl3KzlpVjRHZll1d1IvU1VXZ0tidDZKVHBWZk5iT0JBYTd6cHdyaTBsdUpZNFp5cVc1dDlxV2dvTmZXWW4xUytHdi9rdGZWVVd2d2F6K0piWFlRdW1DNWVaWFd6VCtnNVMyMGNkeE1IRlJGeHlyNW80b2dFRmVqd1psaUlpWnh5aFpsd2RybGhlaW8xdUU2dkRwbUYzeTFUbGdqOHNHY3F4QTFrd3lMVXVzWmVqRDJRU2tGK004RHFoOTNMNE1DS0NLMDFaNng3Z1hkcUNDQUM2bXVFUEVXK0hWbGlRb1VXN1RCS29NYldTYThmOFlYYzVKRGpkMzNjbzRsME5IQzB4V0lNVkJlbDVwMG5FdUU5YTYvTEI3bUNrQ2s5dlRCMVdpY2FMNVk0QmJMdXhtbDBhbDgyZGNRU0Z1aXFWWTBsb0VPM3FOc3FoeFFVMmNBYTlFUllXc0R6Y0VPVndsQitxWTlRaWdLV2xGR1ZUZ3VzWTA3NGowVnRkMlRPY0dMOHRiSUZ4QXVvQXF0YXhYQ0ZKWEQ1UVd5eUVPVGs4WXdpVlZLbmFsZFBCdCtvSHF1RTBHMXl4dUs4d3IzSmhTTTRkbmx5dmNJMGZHWkxQOFBEMzRKbnQ2N0hyeWlKTXlRTTlUR1A5SUVyNFlJMUEzVm5RWVFCcGlNcGNTQlJQK21BeUZUeTVRTUdXditXVWdyalkyOHFxaTJrV09mNDg4Q1VzbDhxc3M5NnNWbnh3eGJTb0xCVitGaGlibjF4YkJFNGJVWDNDT2JkdjNtWTZyWDUzRTB4U2xrTzRWREZVUGRNNWlDSE9oZUZZeGZjVjVtOE1MNXpWSG1JakQrVmlXbWNBUElGTkhCYTRpU3E0MVhhSWJJenh2SU5QZU1JZ1NMVGtiakU1MU5GUGVtUldyTURWS3JYcVhkRWxpQzhEbDFUTExwQzBFZ1ZqTVpqV0VicWc0SmZYUCsyQXo1SVkrdFhEUkdlYkJQekJGcXUvQkYwL0ZNVHoxUkRtMk5XU2hDNjRXckhxbHN1VmhlVWFTcmFtalEyeDZ5YnRLcWtjclNtQlIwb2ZyQ0NKaytBdFBicUJRMEpXbXRxVjRSR0FwckZWSFc0TXFidDVmQzZQZ0k3SDFsY1lnb25NMGhab2p4R2pyeVhFTXI1NmxCOVd3UU0rUnhMN2JyZVZxUElGMzNGUFcrZnJSWFpxM21EYXFsWENjQ3h6V0V0Mm9EUXhCelFrQ3lJakRnNElLMStvNHRsMDg1Wk5CTFkrYUhMRFd3d0FSdHZ1STNaZVhmOEEyTDJvclU2L2NIeGlURGRoZm1INmFJTHdXTEVBRnh4VWNUd1dwMnRVdzBReUdRdXVBeFo4N2NBb1lOVzBPQTM2NVdMTVVKU212dHhPWWIrVEdIRnBUTlBFRzV1MkNxOUFVdUM1VnNlVUtLeGR3ZS9UaVYwTEwxV0hjQU1Ea09Md3J1bHFFaktMakdUeHdkUUxDeW5qUWlleVY4WTlCYjJDdUxBcVFNQ0NnSEtpVUJFaldTamV1Y1VzaDllbFZRdFBjSU5BdTF2Y0JKV0I1UVZXZ0Z1NFBuTEYyWE9vcWtlbFFLTGovWENPWTc5dzNrdFM1VTdhNTcyZC93QWpBM0NoOTFjVXlvODJ3WU14UTNhaElGVkRQaVRVZGxCVmVtTHlmYzIrTVZ2aVhqRUZzS0Zzb0dta2JtZkh0ODVtWVZjVGpVM3cxbGVhU0EwdGZKM0ZXOTdMTzNiMHFLT0x4ZlMrWWUvM25DOFU4cXN2S3NTT3R0QUt1cDBPWmFLZGF0WWlyc3BGNnNoT0hRQWhMVzNEWElDaW1yUlZDcGV6aTM5QVNLRlZvSzFRV0VZNDFWQ0d3SGtLT0VxcHk2cWRJQVU4cExWU0NERkZLT3Z0T0hoMmVyN2llQmNPalA4QTBTbm0rNFExczhSbVNqKzQ1ZmdvWVJZZFdsTEkrTXFkS3lMRmwzdU9ieHY4dTh0UkEweHRZdWkyQTBBSjJKOThsYXk4c29hTnNNbHB1QnRwTDN1VzlMcUJGWnI2N1F2RWpodThjNTV3YkRxMEJPZ2E3ekc0N1BDM0wzWXZCeVlkWnovQ0YwU2x0TzRsQkJVSE44azV1ZUd4T0hWN01hTUJLSnNJa0ZHclRqcE1Sd3RQYWI0SVhxeG5QZnNkeGVaRzY1T1U2L2k2RFl6aTA1NXdJYUZXMTFDSnlUcUZ1d0RoTEkvQzBENnVYVDQ0SU4rbjZjd1FiZjY0QkZ3YkdyZFZoS0phckNKRVBSeFNrZjdGMnJpQ2lXaHl1b210WE10WjRUdHhjTFUwcEtSeEZVaGppUmZkdmhocmNkOWRWR2dhZE9SbXJiL2RsZUladGFWU21panNxTFhCZzZERVdxYXp0bGJ6ODdFeFUwSHF1TzRnWFVOOCtKUlhPYkhpQlNFOHEwanllUkpRdFplSCtTOWhHc0FGODFCTUQ2a0ZORkh3YWlkZHhMTnJvaXI3ZHgwMmI3RGdRR2lnc3FlVFMxeTVISmF3RU5nbU9keXEyYitlS2hvZk5NVXNaakg1VVJjRFFSTFhGbGgxY1JiMGVDSzQ2eHNGa29HL0JLcDI3UEFuRmN4T05qVHNxcUNEeXhZdjIxSzRQZ01WUjIweVpTNldtMFRjbmxrSDBHUlRFYTFHeDE0WW41TllSNFJhanU0cFFYNW9PVkZRWlVvVFFiZ3U5ZGxzcGRLeTRESDZLdzA1ZUw4Wm5nakFuS000SDF4RlAyRm5TZmxnMDNtdEdyeUFjSGxIeWJndjNXSmU1WDQ0K0FsT1ZaWnl5SjZtaTVTeXZxQnB4WFlVWGUzOHpmcmJ0cUdCMmcvZHRjcGFVS2VqSVBZeFd2Mkp6S2FORnNPREF0STE5TXhuK1I2bjU2Z1EvVzBXSG9VZ08wU0Y4KzVaTElhcUwwck45blppa29BN0NyaGRnQlQzV1J2WnJyNHVjalQ0M1ZidFEvMm5tbkZ0cVZHVkVUWmJVR3B1T0YvZzFwUnd1T1piTzBDUG1GaWs0TU1VTFZMdWpGNjVxTGV0VXZEOHhkc2VBV2ZaN2xaWVFnZFRsV05hc0E0MlFlVVFGNE1QNHh3c3ZNdGJxZHlsNEp2emZBUFdqRmpmbmdROW52cEUydkN3cmkycStKby9FdEtYbGN1SXV0QjZYY3hTSFFVUlBlWUFHbjNrLzhRQUtSRUFBd0FEQUFFREJBSUNBd0VBQUFBQUFRSURBQVFSRWhNaE1nVWlNVE1RRkNBakJoVkJKUC9hQUFnQkFnRUJCUUNqbG5kV2JOT1Ryc1UvU3Y0ZjkyRThXSDVyN0pIOWYrQUpHZS9UanR3cjhRTUhSL0NKNXNvOGplRkEwSW1CSTRLZnBRZEQvdHhuQXlKNmJmcmordjhBR2RIOEE4d1ozSFk4WWR5WiszeXdFNEd3c2ZRTXJ2RzVSbTE5a1hCSE1vM1pwOFc1Nm5WNTJPUzU1YkI1T1Y1cWcycFovYmdCL2RobjlxSUVicFppdUZPaGw4U0hrbWV0STU2ODFNOW5TQ0o5UjFFQStwY3pZK2YwNzdRUHhUNExsL3VJaWVlaWNnT1p0SDdFZ3BBMVZ4dFZlSFhVRDB6elVVUVBSL0ZQWW4zSVVEQ0pIQVpEQVk0dElBVi9icCtJSXloNmd5bnRYelRDNjhqOGZTMHJLOC9wK3ZUMS9wWEZPanNVcStoS2gyUHBYTlpkVzdCMC9pZ0dQY0F0WHVkenlHR3pER3Njb1I2a1QwcmpkOGZ3YWtzd21tRUp5U2tMS0hxMmVjS01ZNjJMNk0zb0pzeFdISmhKSVBZNVUrOWYyZngxTUlVNXdaUWRhQUF5WnhqN0g4MlBISlk0Q3laRStTeFJWZ2hJSlk0VC9zUGU1K0F4KzMveDhkR2FyZ3BoWXNNSjl5NHhBWDJtOFkwa29PTm4vdXdCNml1blhyUG1yd3lCb2tKQm14Z0FHQy95M09NdlFvSW42Sk9PZ2dvQzBENkVIei9yRDIyaExYRnE2VW5EcDV6SWJJbjdtdy9MWkk4ekNIVzA3dFhRSGpyT0JpS3hKUTVzNlZxblgwTmliWDE2dWs5VFlVbWJPVDVxSGV3VTBheUJjNWxmV1YvcnJ6YVczOVArbmEvMHBWQzVyK0FFMUFkdTV6cmJjM0Zic3lLaGVxZlRlS2R1dnZyTVNveGo3QmdjTGN6WllnQW5BZ00ySHNxbm5nK2NiQ09qL2tQMDdaM0NYMzc2Y3ZaSDZGaDBsK1lQbE1BYkZvZXFyUk1EclZEVXZ4bWtnVkFSblZBay9rblFNZVFkT2NLZkNtVCtCL0dIM0xKNUNYMGRkaUF1eWw3RXJyTTdGajNCOG1KUzZXb3JGUnM1cjZzamxUM05VandBeGg3VFRpZU9Ybzgwd0llUGt1bWVIMndBdTE2TkU2MVZDLzA2akRxMjhkU05UaHhmZHJPSjJOMHc3SzhPeVRiaGJOZVJTWTdqZDVIb25sMExybmVUYm5KZXlZeEdhLzd3UC9zdFd2WTcyOWRCdGJoeVZJK25UbkpqTi9XclYxMVVRaWMxSGY4QWMzMmpXbzdKNmtnV3JQaVZRQTFrTXZYeFQzT2M3SnZaWWtDWlplRnVtYzdGakVqYXNOWGxvejhFbEI4bnFTazFSd1MvRzMwdzkrTjBuWWl3b0lQV1pEcHJ5aTc0ZlpaVjlYTFJOTUNGVy9xdWlLUHNvUHRYOFRBN0g1UlE5SWMzb0tESTYxaTFybUY5YmN2ZHJaSWUyNlF1dkl0UXQ5bU9GYXVtalRUYk14TWY2aDMxTWhPY3BEYVJzaEJKN08wakhHSjVUMldmajRxdzdNKyt1ZXpPbWs5aXlYU2pxT1drV3RySjRWdCtOY0RuMUJldzF3L2taak5LQ1VZVUtaWlJkRHdMTmtLU1pYajZBUXJWbXBTenVHL05QZ254QU9TeldQdGRXS1YxS1Vlc3lxeVBNOUVpdXdwVTYzeCtva2pYMUZMczgvRVNzMFNnRG9FUVlWOW1WWnpod3F3bXA4bzUxQ0JxVTQranNPdE5QWWdpTUFKa2MxOEI5bmJpeTJsT1U5VmpPTkd6WkhCcmZDOG8xMTlaZExXeWcxQS9OSG85aC9CQjczd0RzWFBNWWRYdVVFZ3hFV1ZaNnE0cGpnZE9lb3E0OWp4OXA2VVkycWRPYnpTL3VzUGg0STZMcnhVdEdUa2E4Ui9pUjFlWXN5ci9BTU9pMFVvODg5d1E0TEE0VzRMT1FmL0VBQzBSQUFJQkF3RUdCZ01CQUFNQUFBQUFBQUFCRVFJUUlURURFaUJCVVhFaU1tR0JvYkVUa2RIQkJCVHcvOW9BQ0FFQ0FRWS9BSFU5YktwLyt3eGRyTHRaakcrSnhkOE1jdVpWdEhoRDJqMG43R25aV1hhN0h4Tzd2bXozZXVlM0lWTkNsWTdkV3lwVnN6bUNWZlBRMXRWRm80SkdxYndhWDhhbGtVVW1GOEZhS2wyNEZTYW1vN1pacWVZeFVQSTJuTjVIbkJsa3VvODQ1cit6emZES3U1Vkhwd0p2b2FtbzJKVjdTSDBIUlh0RW11VW1kcXYyL3dDQzJleHJUcWZjZEcwMmlUV3V2OEkvSjkvdzNkalZMSVYyb05MdEl5eXJ1T0RXekZUd1M5RU9xcWhOdm5CNUYraWFLVW4yUTZxcVZMOUR5TDlINU5sU2svUmNENEhKZ2ZBeFIwdExSaENhNWp6WlU4aDNidTBqSzRkeWRXT2xTVE4xMklrYWxFa01jMm02Z2FGekpaRE1vd29QTVR0Tm9sSnVyYUptcVBDSldZbDZHOUdTcXZHU21qbVpadk5TWXBNWVBGcDNGdTJlVklodEhpVjVUd1VLdDh6eHJkYmh5dnFleEtjajN1ZDJLcGFRWlRaNEVVSmtGSit4RGZZOXltT2xrTkdocGVqOGEwMVArdjhBOGpLVUw5RUpRTndTMUZtWTFqL2JOdEtCWVNGSW9FS1NlZUJkeFBvbjlqRndzWlZ0YXBlY0pjeUVOU3YwZWFWWmtweGc4VGtkTFg5WkxVUlpDRjdrSHVVcFBXZnV5ZXFIQXJ3dVorSFpmSE5pcGJoUjhtakhOUHdiOGVHTE04WE5Xd0xQT3lQY1h1ZTMrbnVVKy8zWlE3S3pLZTZLcDZzaWszcUVvOWNmWkZXN0IrT2hyVFNiTVZTWGhqVWpVbUJkL3dEUmlraVJSVWlHMSt6VkMzSHJOb1F4Wk5iSjAwc2RVWWFITGZ3WklTK0IxcFpnZHFWeWtnOUQ4cVhNM3FWcVBxa0t2a053T0ZaMGpxcnhaMnphU3RiOHJwMEhHbnQxSmJ3TlVVNkRwcXBoREVVOXh4eU4wcHA2RzZ4cDZzaEVQOWoydGVlU1JGZENnZE9xaVVRaERNa1hyMnllYW94MkdtL2twb25QSWRPdnVOT0o3c2RxV09IeU41amI1RUV0WlEzWHFoVjBqMmRmZEUxdkE5cFQwaEc2N08yTFFlRWRibVg2Rkc2L0VodHFIMVp2cXBhNnlPMU1GVWREemZCTkluVXNtaEJFRGNHVFFjVWt4TDdqUyt5YWxnaDJSbTNoVXQvWnY3UllKVFRuWDBHcktuYXpIb09taVhQVWROVXljeU9HYjdwRlVtNjZXMTZua01MZzhHRDhiYzh4cXJVcWRrcTFKS1dTV2lWVHd1eW0rN1ZsRVRoNkhpWjRkT0QveEFBbkVRQURBQU1BQVFNRUF3RUJBUUFBQUFBQkFnTUFCQkVTQlNFeEV4UWlJeEF5TXdaQlF2L2FBQWdCQXdFQkJRQlpvaStuMWhJZXBiV3BUUlQvQUZPYy9Ya3hsZmlIdWJEbE03blFBR0J3NXdjWEVIUm5SL0RPTUxjWCtvMTZ6TTdWRmhJL2xQOEEwT2ZFOFJUbE1ndjUyNlgrbm5nYzhEZ1E1enhVRHVJQmc0cEtFbjZSNTREZ1E0Z0gxRWFRcHIrWVcyczBETW55bXZIT0h2Z0ErS0xZL2Vhdy9ZK3ZZdDlyWTROTFpPRFEyY0duWTVmV3JCVVBRcHdIdUNGNkQ3UFpBbnEyY2ZZN2lsdEhZT0RSSkdzZUo2alQ2aFFmblA1T1NQZ1Bycmd1RGxUNUhUSGFQdFVHZmVOaWI5T0p2dnhkbnJiOVRzcUFRQmlaQmlpTFp5cTEyUUMyMGNJMmpqUTNXeVBSSGFKWkUrVUhEM29BSlR3T0tqQTB3VjNaTk03K3lnMVBWT0dPN0NjTlhmc2llbmVzc050ZGpXSEIvQzVEUm8wWmFoUUtuaU9jeU1wbEpSbGtVUDBySUFxODhobi9BTXA3SjVuQXo5YzlOS21jSnZlYUI5blBwMG9zL09TcTFrRldlemtZbzdpRHVhSVAyNEdjT2R5WllLaEl5SjVQWlBXNGVybnhOT2VDOHp4Vjhxb1ZuY20zQVFxQVkzdE9ZNnVGVnpnNE1UMkdyZVV0U2RaMVh1QVpKZjFxbnNlSnFjZWlkSVpjK1VsL20wYStFOWUzZGdFVzhKdlp5RkNNVG56aUh4SFFRcGJxbm1GaDVmVUdUbzFTanZObytxN2NnUFd3QnIrc2JHMW10UGV2SXE0bFZXR0hCN1lEK3VIc2k3bXo0UTlaMFo2dnFORWZjZ2NaazRwSE5UYmxETm4xQ0ZVaHRTUm4zTlpsRkVSRldiR2FRSkU1UWZtY3pXKzJlUDhBekV0bExlbVc5VTI5dDNkemRLRTkvSEIzdzFtUXBxUUZxVVZOZXZxUFh6U240cnNxTy84QWsvbnhJQ3FUbWtQZkhKRlo5NzVUSitvbUJ4aU1BZjhBay9WdFRSbE8rcERhcDdzbnk0Q2tZUGl4L1JDNGtWb0xpOFNKdzZFcXhaL2ZpaGpsRUtNb0pFcStEamdEL3dDa3orVGM2ZzRlbkZIQk9qSXkrc1BIWitncEU0Y2Fxb3BHRCtwQWVieVJsVi90OHRzMVhKamcydjc5OWd3Qm8zNWcrMnZOSGZnQUxnc2k4Tk9oMTc0alA2cnJhNXVkclV1aEd6QTRMdzdlc2xZWjhCSk5TWTEzSVRVYzRtbUZnUGJObHd6OEhFOGN0dzBIeHJPRWIvem5hVC9zNmtzTVJUbE9mU2dSOXZyVThHdDZWRFdyOWpBWmJYdWpML0dqdHBKVzI2MUJhellFSjFsOTgyWktyaVZTRWxYcGxRbFlXSTE0aDM0QXZseWtoMHVwODFWc1FjRFBGUlBaQTF0YXU0S2EyNVZHYmYyRUd6NmpmWWpJOVZ2alc5ckw4cU9EMCs4akU3RTVVRHBTOUtvb0hYeWt2cGlOQkxPOVg3cFdZKzdSL3NXNFNTY3JsbS9HSlU2c2pQdGRxU0pHSTJkYlkxb3hTSjluOWhwanRqeVpRK2VRSlNHMnlPMmdLbW5EWThNeFozcFE2cnJsTDBmVjFDbk1qOHQvYkh6WUFGTmZjYW1yQm90T1JKYUxxa3Rpbm5LT1YrTkEvdHI0ZUtQd2J0M21ETVVHc1RCcG96UGFWSlZvckxVWGR3WktzbzY4MFoxSGpISDlseWh6WVVGOUI1cFdGdFFKS2dkblQ4UllHV3E0ZExIUFR4KzdaUGdFb3paU0MzVjJaS2VkRGlzeXNyTmF0d3lNaHJSUlBZQUFwMDdNems5cVNHZTlDMzhVOXMyY1pPNGdLaXVvMlNFVlY2cU0wejBXK2RhbHAydWR2WXlUYmhUNm05d09SbmtjQnhTT2M4c2pQd3dOd0lUaWN4RFRpbW9ZMjJUaHBiUE55Q081T1l5ZW1rcHA5R0s3ZFVxZFhnRi9rRjBMVnZpVnJQQmF4em5QNUdBOE02Y3hxZVUrOHlZR0t6SWVwbm1yTDRFSndEQW95RXcyZi8vRUFEQVJBQUlDQVFJRUJRTURCUUVCQUFBQUFBQUJBaEVoRURFREVsRnhJQ0pCWWFFVGdaRXlzZEVFTThIaDhEQnkvOW9BQ0FFREFRWS9BT1dEdExZbjlUMkpRaHZpc0xyMzZEOEdkS0hyYTBXajhPTjlGRmJpaFdhRTB0dEg0cUgvQU9ONlllbFBwZ2JtNkZ5ckowc3BqMHdiYUs5OUcxcFNNb3BIbjhDYVpiTUdIOGo1bnBDWFlpMWpSNk9Sc2JNdlNsRTJQMG42UmVYSWsxVmVCVXVoaUJVZUZmNE04TDVSNWVGOG8vcy9LSTEwRmVqMGFSc1pXajVPSGNlcHo4UGh0cCt0R2VFL2orUjhUamNOcUs3ZnlMaWNQaHRwNVdZL3lXdUY4eC9rditvaTRyN0QxakpQZEptK3R5M28ydjhBQXV4blZEZW1kSFR5emxqeEdrdmMvdVA4alVwTnIzYk9WU2RkMlltMTkyZlQ0cmJYcGJmZ2oyWGdWRnNYZ1F4MlVtTk4yTlBaRzJqYUUvQTlJT1Q5RVhCM3JuVG5yS1J6T2lxMFF6bXAvZ3ZsZGRtTkZwbURKUmt3UG0wWlJTTU9tVTNhOXo5SHljdkI0RGsxMHljMCtFNHZvMFZUTGxveERmdWZUVGRkQ1BDbGRyMkp6OUd6WWNJeXBtWnNxVHM4cUh6RFQ3Yk0yZGRoM2d5ZVZqYjBhbkh6ZFRpUzRLZXkvY2ZHNGI1MUJPTFRmdnZYY3BxdndWSDB5Wi83SXhFbyt0bEtTWGQwTlRhZmJLSnVPdzJ5VEg5aDl5dlprcjZJbXZmUnJUTDF0SEVYRWVYUStOL1RPdWE3Uzk3ZlU1bTcwcS8rdlJEdnJweXAwTkoyTmRDVmordzY2bEwzSDJKTHExcExSbDY0WkdFWmN0TE1uc3NQb05zdW4rVENyYlJGTlhrd3ErUlBmOWtWRjNla2hqK3cyTit4SzF0V2pTM01qUm5Ybmwvb2JpcnRtNk1OZmtjRTgydGJqMTFsajA2YVNHUyt3KzU5aWYyMGxvOVpkaGRoOHh5Y1JPL2FuK3hjSXk5c0M0azROWnk2MWxHL05ldzlrYmovQVBuU1RTSGo0RytWbHBQOERWUFlsenJvVWhqWTZOaWg4MGtocGJseFMrVHl4TGY3aTRjbTZiV2pKTkZudXlYQmJ2RkRqSm1NcXh4S1RNc2Q1RXlvWkc5SzFhT2JselcvVVhYN2pRdWVWV0p4bmJ0YU1taGU0NU00a242amFGSmJMY2I5QzQ3SDBvT3ZWc3VFbllwTERUcGpUMWVyb1hBYXhGT24zRktLK0NYRXJEd3kvd0RCYXV1eTFtSzl5bHNSVWZVeVVuaGloRE43RDRVMVRXK1Q2a096S2dzbjA1UE83TFYva3ZSNnNmTzZWRktXTzV4TFhsZnFMTnJvc2ppNHVsaXEvd0FpYUtKa2I2bjZma3FZNHhlRGNzY3JvcTdQSzlodXpNaW02UDhBUnl4dSsybDZZMHk2Vi9BK0h3bmthYWFyYjNMNmpKUGg3bHpwVVdxSHNQMzhGRkVucGRubE9aTlgzTnpmd05UOHg5Vkt2U2pBaGpsRjBVNUdKRFRsNGNhU3JXNDRaZkwzSHlvOHl5VnIvOWs9IiBpZD0iYyIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiLz48L2RlZnM+PC9zdmc+", + "description": "Displays the latest efficiency telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'efficiency', label: 'Efficiency', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Efficiency\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 30;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"square\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"trending_up\",\"iconColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":60,\"color\":\"#F89E0D\"},{\"from\":60,\"to\":80,\"color\":\"#3B911C\"},{\"from\":80,\"to\":null,\"color\":\"#2B54CE\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"52px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":60,\"color\":\"#F89E0D\"},{\"from\":60,\"to\":80,\"color\":\"#3B911C\"},{\"from\":80,\"to\":null,\"color\":\"#2B54CE\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:ZWZmaWNpZW5jeV9jYXJkX2JhY2tncm91bmQucG5n:ZWZmaWNpZW5jeV9jYXJkX2JhY2tncm91bmQucG5n;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Efficiency card with background\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"%\",\"decimals\":0,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "productivity", + "effectiveness", + "performance", + "capability" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/efficiency_chart_card.json b/application/src/main/data/json/system/widget_types/efficiency_chart_card.json new file mode 100644 index 0000000000..4678594d2b --- /dev/null +++ b/application/src/main/data/json/system/widget_types/efficiency_chart_card.json @@ -0,0 +1,31 @@ +{ + "fqn": "efficiency_chart_card", + "name": "Efficiency chart card", + "deprecated": false, + "image": "tb-image:ZWZmaWNpZW5jeV9jaGFydF9jYXJkLnN2Zw==:ZWZmaWNpZW5jeV9jaGFydF9jYXJkLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj4KICA8cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiIC8+CiAgPHJlY3Qgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiIHg9Ii41IiB5PSIuNSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjEiIHJ4PSIzLjUiIC8+CiAgPHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIKICAgIGQ9Im0yMi43IDE2IDEuNSAxLjUtMy4zIDMuMy0yLjYtMi43LTUgNSAxIC45IDQtNCAyLjYgMi43IDQuMi00LjIgMS42IDEuNXYtNGgtNFptMTYuOCA3LjZWMjVoLTUuM3YtMS40aDUuM1pNMzQuNyAxNXYxMEgzM1YxNWgxLjdabTQuMSA0LjJ2MS4zaC00LjZ2LTEuM2g0LjZabS43LTQuMnYxLjRoLTUuM1YxNWg1LjNabTMuNyAxMGgtMS42di04LjFjMC0uNi4xLTEgLjMtMS40LjItLjQuNS0uNy45LS44YTMuNCAzLjQgMCAwIDEgMi4zLS4ydjEuM2wtLjMtLjFhNCA0IDAgMCAwLS40IDBsLS42LjFhMSAxIDAgMCAwLS40LjRsLS4yLjdWMjVabTEuNi03LjR2MS4yaC00LjN2LTEuMmg0LjNabTIuNyA3LjRoLTEuN3YtNy45YzAtLjYuMi0xIC40LTEuNS4yLS40LjYtLjcgMS0xbDEuNi0uMmgxbDEuMS40LS4yIDEuMy0uOC0uMi0xLS4xYy0uNSAwLS44IDAtMSAuMy0uMy4yLS40LjYtLjQgMVYyNVptMS41LTcuNHYxLjJoLTQuM3YtMS4ySDQ5Wm0yLjcgMFYyNWgtMS42di03LjRoMS42Wm01LjEgNi4yLjctLjFjLjMtLjEuNC0uMy42LS41bC4yLS42aDEuNWMwIC40LS4xLjktLjQgMS4zYTMgMyAwIDAgMS0xIC45Yy0uNS4yLTEgLjMtMS42LjMtLjUgMC0xIDAtMS40LS4zYTMgMyAwIDAgMS0xLS44Yy0uNC0uMy0uNi0uNy0uNy0xLjEtLjItLjUtLjItMS0uMi0xLjV2LS4yYzAtLjUgMC0xIC4yLTEuNC4xLS41LjMtLjkuNi0xLjIuMy0uNC42LS42IDEtLjhsMS41LS4zYy42IDAgMS4xIDAgMS42LjMuNC4zLjguNiAxIDEgLjMuNC40LjkuNCAxLjRoLTEuNWwtLjItLjctLjUtLjVjLS4yLS4yLS41LS4yLS44LS4yLS4zIDAtLjYgMC0uOC4ybC0uNS41Yy0uMi4zLS4zLjUtLjMuOHYyLjFjMCAuMi4xLjUuMy43IDAgLjIuMy40LjUuNS4yLjIuNS4yLjguMlptNi4zLTYuMlYyNWgtMS42di03LjRINjNabS0xLjctMmMwLS4yIDAtLjQuMi0uNmwuNy0uMmMuMyAwIC41IDAgLjcuMi4yLjIuMi40LjIuNyAwIC4yIDAgLjQtLjIuNmExIDEgMCAwIDEtLjcuMiAxIDEgMCAwIDEtLjctLjIuOC44IDAgMCAxLS4yLS42Wm03IDkuNWMtLjUgMC0xIDAtMS41LS4yYTMuMyAzLjMgMCAwIDEtMS44LTIgNCA0IDAgMCAxLS4yLTEuM3YtLjNjMC0uNiAwLTEuMS4yLTEuNnMuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS4xLS44bDEuMy0uMmMuNiAwIDEgMCAxLjQuMmwxIC44Yy4zLjMuNS43LjYgMS4xbC4yIDEuNXYuN2gtNS43di0xLjJoNHYtLjFsLS4xLS45LS41LS42LS45LS4yYy0uMiAwLS41IDAtLjcuMi0uMiAwLS40LjItLjUuNWwtLjQuN3YyLjNsLjUuNy42LjUuOC4xYTIuMyAyLjMgMCAwIDAgMS45LS45bC45LjktLjcuNmEyIDIgMCAwIDEtLjkuNWMtLjQuMi0uOC4yLTEuMy4yWm02LjItNlYyNWgtMS43di03LjRoMS42djEuNlptLS4zIDJoLS42YzAtLjYuMS0xIC4zLTEuNS4xLS41LjMtLjguNi0xLjFhMi43IDIuNyAwIDAgMSAyLTFsMSAuMS44LjUuNS44LjEgMS4zVjI1aC0xLjZ2LTQuOGMwLS4zIDAtLjYtLjItLjhhLjkuOSAwIDAgMC0uNC0uNWwtLjctLjFhMS42IDEuNiAwIDAgMC0xLjQuN2wtLjMuNy0uMS44Wm05LjcgMi43LjctLjEuNS0uNS4zLS42SDg3YzAgLjQtLjEuOS0uNCAxLjNhMyAzIDAgMCAxLTEgLjljLS41LjItMSAuMy0xLjYuMy0uNiAwLTEgMC0xLjUtLjNhMyAzIDAgMCAxLTEtLjhjLS4zLS4zLS41LS43LS42LTEuMS0uMi0uNS0uMi0xLS4yLTEuNXYtLjJjMC0uNSAwLTEgLjItMS40LjEtLjUuMy0uOS42LTEuMi4zLS40LjYtLjYgMS0uOGwxLjUtLjNjLjYgMCAxLjEgMCAxLjYuMy40LjMuOC42IDEgMSAuMy40LjQuOS40IDEuNGgtMS41bC0uMi0uNy0uNS0uNWMtLjMtLjItLjUtLjItLjgtLjItLjMgMC0uNiAwLS44LjItLjMuMS0uNC4zLS42LjVsLS4yLjgtLjEuOXYxLjJsLjMuN2MuMi4yLjMuNC42LjUuMi4yLjUuMi44LjJabTYuNy40IDItNi42aDEuOGwtMyA4LjUtLjMuNmMwIC4yLS4yLjQtLjQuNmEyIDIgMCAwIDEtLjcuNWwtLjkuMmgtLjRsLS40LS4xdi0xLjNhNi4zIDYuMyAwIDAgMCAuMyAwaC43bC40LS40Yy4yLS4xLjMtLjMuMy0uNmwuNi0xLjRabS0xLjItNi42IDEuOCA1LjUuMyAxLjgtMS4xLjMtMi43LTcuNmgxLjdaIiAvPgogIDxwYXRoIGZpbGw9IiMzRkE3MUEiCiAgICBkPSJNOTkgNTguNHYyLjNMODguNiA4NGgtNC41bDEwLjItMjIuMkg4MS4ydi0zLjRoMTcuN1ptMjAuMiAxMC43djQuMWMwIDItLjIgMy44LS42IDUuMmE5IDkgMCAwIDEtMS43IDMuNGMtLjguOS0xLjYgMS41LTIuNyAyYTkuOCA5LjggMCAwIDEtNi4yLjIgNi45IDYuOSAwIDAgMS00LjEtMy40Yy0uNS0uOS0uOS0xLjktMS4xLTMuMS0uMy0xLjItLjQtMi43LS40LTQuM3YtNC4xYzAtMiAuMi0zLjguNi01LjFhOSA5IDAgMCAxIDEuNy0zLjRjLjctMSAxLjYtMS41IDIuNi0yIDEtLjMgMi4yLS41IDMuNS0uNSAxIDAgMiAwIDIuOC4zYTYuNiA2LjYgMCAwIDEgNCAzLjNjLjUuOSAxIDIgMS4yIDMuMS4yIDEuMi40IDIuNy40IDQuM1ptLTQuMyA0Ljd2LTUuNGwtLjEtMi43YTggOCAwIDAgMC0uNi0yIDQgNCAwIDAgMC0uOC0xLjMgMyAzIDAgMCAwLTEuMi0uNyAzLjkgMy45IDAgMCAwLTMuMi4xYy0uNS4yLTEgLjYtMS4zIDEuMi0uMy41LS42IDEuMy0uOCAyLjItLjIuOS0uMyAyLS4zIDMuMnY1LjRjMCAxIDAgMiAuMiAyLjhsLjUgMiAuOSAxLjRjLjMuMy43LjYgMS4xLjdsMS41LjNhNCA0IDAgMCAwIDEuOC0uNGMuNS0uMyAxLS43IDEuMy0xLjIuMy0uNi42LTEuNC44LTIuM2wuMi0zLjNabTkuOCAyLjdWNzZjMC0uNC4xLS44LjMtMSAuMS0uNC40LS42LjctLjhhMiAyIDAgMCAxIDEtLjNjLjUgMCAuOC4xIDEuMS4zLjQuMi42LjQuOC43bC4yIDF2LjZjMCAuNCAwIC43LS4yIDFsLS43LjhhMiAyIDAgMCAxLTEuMS4yIDIgMiAwIDAgMS0xLjEtLjIgMiAyIDAgMCAxLS43LS44Yy0uMi0uMy0uMy0uNi0uMy0xWm0xLS41djFsLjUuNGMuMS4yLjMuMi42LjIuMiAwIC40IDAgLjYtLjIuMSAwIC4zLS4yLjMtLjRsLjEtLjZ2LTEuMWExIDEgMCAwIDAtMS0uNyAxIDEgMCAwIDAtLjcuMiAxIDEgMCAwIDAtLjMuNWwtLjEuNlptMy45IDZ2LS40YzAtLjQgMC0uOC4yLTEgLjItLjQuNC0uNi43LS44YTIgMiAwIDAgMSAxLjEtLjNjLjQgMCAuOC4xIDEgLjMuNC4yLjYuNC44LjcuMi4zLjIuNy4yIDF2LjZjMCAuNCAwIC43LS4yIDFsLS43LjhhMiAyIDAgMCAxLTEgLjIgMiAyIDAgMCAxLTEuOC0xYy0uMy0uMy0uMy0uNi0uMy0xWm0xLS40djEuMWwuNC41LjYuMS42LS4xLjQtLjUuMS0uNnYtLjVsLS4xLS42YTEgMSAwIDAgMC0xLS43IDEgMSAwIDAgMC0xIC43di42Wm0xLjMtNi4xLTQuOCA3LjctLjgtLjQgNS03LjguNi41WiIgLz4KICA8cGF0aCBmaWxsPSJ1cmwoI2EpIiBmaWxsLW9wYWNpdHk9Ii4zIiBkPSJNMTQ3LjQgNzEuOGg3LjJ2OC45bC0zLjYgMi42LTMuNi0yLjZ2LTlaIiAvPgogIDxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGZpbGwtcnVsZT0iZXZlbm9kZCIKICAgIGQ9Ik0xNTAuNyA4My45Yy4yLjIuNC4yLjYgMGwzLjMtMy4yLS43LS43LTEuNCAxLjRjLS40LjQtMSAuMS0xLS40di0zLjloLTF2NGMwIC40LS42LjctMSAuM2wtMS40LTEuNC0uNy43IDMuMyAzLjJaIgogICAgY2xpcC1ydWxlPSJldmVub2RkIiAvPgogIDxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiCiAgICBkPSJNMTYyLjYgNzkuOHYxLjFoLTMuMXYtMS4xaDNabTUuMi00LjRWODRoLTEuNHYtNi45bC0yIC43di0xLjFsMy4zLTEuM2guMVptNy41IDBoLjJ2MS4yaC0uMWMtLjUgMC0xIDAtMS4zLjJsLTEgLjYtLjQgMWMtLjIuNC0uMi44LS4yIDEuMnYxLjNsLjEgMSAuMy42LjUuNGgxLjFsLjUtLjQuMi0uNmEyLjUgMi41IDAgMCAwLS4yLTJsLS41LS4zLS42LS4xYy0uMyAwLS41IDAtLjcuMi0uMy4xLS40LjMtLjYuNWwtLjIuNy0uNS0uMmMwLS40IDAtLjcuMi0xbC41LS43LjgtLjUuOS0uMmMuNCAwIC44LjEgMSAuMy40LjEuNi4zLjguNmwuNSAxYTMuNyAzLjcgMCAwIDEgMCAyLjFsLS42IDEtLjkuNi0xLjEuMmMtLjUgMC0uOSAwLTEuMy0uMmwtLjktLjgtLjUtMWMtLjItLjQtLjItLjktLjItMS4zdi0uNmMwLS43IDAtMS4zLjItMS44LjItLjYuNC0xLjEuOC0xLjYuMy0uNC44LS44IDEuMy0xYTQgNCAwIDAgMSAyLS40Wm0zLjMgMi4yVjc3YzAtLjMgMC0uNi4yLS45LjEtLjIuMy0uNS42LS42LjMtLjIuNi0uMyAxLS4zcy43LjEgMSAuM2wuNi42LjIgMXYuNGMwIC4zIDAgLjYtLjIuOGwtLjYuNy0xIC4yYTIgMiAwIDAgMS0xLS4yYy0uMy0uMi0uNS0uNC0uNi0uN2wtLjItLjhabTEtLjV2LjlsLjMuMy41LjJjLjIgMCAuMyAwIC41LS4ybC4yLS4zLjEtLjR2LS45bC0uNC0uNGEuOC44IDAgMCAwLS40LS4xbC0uNS4xLS4yLjQtLjEuNFptMy4xIDUuM3YtLjVjMC0uMyAwLS42LjItLjlsLjYtLjZjLjMtLjIuNi0uMiAxLS4ycy43IDAgMSAuMmwuNi42LjIgMXYuNGMwIC4zIDAgLjYtLjIuOC0uMS4zLS4zLjUtLjYuN2wtMSAuMmEyIDIgMCAwIDEtMS0uMmwtLjYtLjctLjItLjhabTEtLjV2LjlsLjQuNGguOWwuMy0uNHYtMS4zYS44LjggMCAwIDAtLjgtLjVsLS41LjEtLjIuNC0uMS40Wm0xLjItNS4yLTQuMiA2LjctLjctLjUgNC4xLTYuNi44LjRaIiAvPgogIDxnIGNsaXAtcGF0aD0idXJsKCNiKSI+CiAgICA8cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii4xIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xODggMTI1SDEydi0xaDE3NnYxWiIgY2xpcC1ydWxlPSJldmVub2RkIiAvPgogICAgPHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjkiIHN0cm9rZS13aWR0aD0iMS41IgogICAgICBkPSJNMTIgMTMzLjhoOS4zbDEzLjMgOS4yTDQ1IDExMi4ybDIxLjYgMjIuNiAyMy43LTIyLjYgNy4yIDEgMTQuNCAyOS44aDE0LjRsMTIuNC0yNi43aDkuM2w5LTE0LjMgMTIuNCA0LjEgMTguNSAxMC4zIiAvPgogIDwvZz4KICA8ZGVmcz4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iYSIgeDE9IjE1MC43IiB4Mj0iMTUwLjciIHkxPSI4NCIgeTI9IjcxLjgiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KICAgICAgPHN0b3Agb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojZmZmO3N0b3Atb3BhY2l0eToxIiAvPgogICAgICA8c3RvcCBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmY7c3RvcC1vcGFjaXR5OjAiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGNsaXBQYXRoIGlkPSJiIj4KICAgICAgPHBhdGggZmlsbD0iI2ZmZiIgZD0iTTEyIDkwaDE3NnY2MEgxMnoiIC8+CiAgICA8L2NsaXBQYXRoPgogIDwvZGVmcz4KPC9zdmc+", + "description": "Displays efficiency data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'efficiency', label: 'Efficiency', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: '%', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'efficiency', '%', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Efficiency\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 30;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"52px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":60,\"color\":\"#FFA600\"},{\"from\":60,\"to\":80,\"color\":\"#3FA71A\"},{\"from\":80,\"to\":null,\"color\":\"#305AD7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 30;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#D12730\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0,0,0,0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#198038\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Efficiency\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"trending_up\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"margin\":\"0px\"}" + }, + "tags": [ + "productivity", + "effectiveness", + "performance", + "capability" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/efficiency_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/efficiency_chart_card_with_background.json new file mode 100644 index 0000000000..dac9b3a3c3 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/efficiency_chart_card_with_background.json @@ -0,0 +1,31 @@ +{ + "fqn": "efficiency_chart_card_with_background", + "name": "Efficiency chart card with background", + "deprecated": false, + "image": "tb-image:ZWZmaWNpZW5jeV9jaGFydF9jYXJkX3dpdGhfYmFja2dyb3VuZC5zdmc=:ZWZmaWNpZW5jeV9jaGFydF9jYXJkX3dpdGhfYmFja2dyb3VuZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiIHRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDYgMTU0KSIvPjwvZz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJtMjIuNyAxNiAxLjUgMS41LTMuMyAzLjMtMi42LTIuNy01IDUgMSAuOSA0LTQgMi42IDIuNyA0LjItNC4yIDEuNiAxLjV2LTRoLTRabTE2LjggNy42VjI1aC01LjN2LTEuNGg1LjNaTTM0LjcgMTV2MTBIMzNWMTVoMS43Wm00LjEgNC4ydjEuM2gtNC42di0xLjNoNC42Wm0uNy00LjJ2MS40aC01LjNWMTVoNS4zWm0zLjcgMTBoLTEuNnYtOC4xYzAtLjYuMS0xIC4zLTEuNC4yLS40LjUtLjcuOS0uOGEzLjQgMy40IDAgMCAxIDIuMy0uMnYxLjNsLS4zLS4xYTQgNCAwIDAgMC0uNCAwbC0uNi4xYTEgMSAwIDAgMC0uNC40bC0uMi43VjI1Wm0xLjYtNy40djEuMmgtNC4zdi0xLjJoNC4zWm0yLjcgNy40aC0xLjd2LTcuOWMwLS42LjItMSAuNC0xLjUuMi0uNC42LS43IDEtMWwxLjYtLjJoMWwxLjEuNC0uMiAxLjMtLjgtLjItMS0uMWMtLjUgMC0uOCAwLTEgLjMtLjMuMi0uNC42LS40IDFWMjVabTEuNS03LjR2MS4yaC00LjN2LTEuMkg0OVptMi43IDBWMjVoLTEuNnYtNy40aDEuNlptNS4xIDYuMi43LS4xYy4zLS4xLjQtLjMuNi0uNWwuMi0uNmgxLjVjMCAuNC0uMS45LS40IDEuM2EzIDMgMCAwIDEtMSAuOWMtLjUuMi0xIC4zLTEuNi4zLS41IDAtMSAwLTEuNC0uM2EzIDMgMCAwIDEtMS0uOGMtLjQtLjMtLjYtLjctLjctMS4xLS4yLS41LS4yLTEtLjItMS41di0uMmMwLS41IDAtMSAuMi0xLjQuMS0uNS4zLS45LjYtMS4yLjMtLjQuNi0uNiAxLS44bDEuNS0uM2MuNiAwIDEuMSAwIDEuNi4zLjQuMy44LjYgMSAxIC4zLjQuNC45LjQgMS40aC0xLjVsLS4yLS43LS41LS41Yy0uMi0uMi0uNS0uMi0uOC0uMi0uMyAwLS42IDAtLjguMmwtLjUuNWMtLjIuMy0uMy41LS4zLjh2Mi4xYzAgLjIuMS41LjMuNyAwIC4yLjMuNC41LjUuMi4yLjUuMi44LjJabTYuMy02LjJWMjVoLTEuNnYtNy40SDYzWm0tMS43LTJjMC0uMiAwLS40LjItLjZsLjctLjJjLjMgMCAuNSAwIC43LjIuMi4yLjIuNC4yLjcgMCAuMiAwIC40LS4yLjZhMSAxIDAgMCAxLS43LjIgMSAxIDAgMCAxLS43LS4yLjguOCAwIDAgMS0uMi0uNlptNyA5LjVjLS41IDAtMSAwLTEuNS0uMmEzLjMgMy4zIDAgMCAxLTEuOC0yIDQgNCAwIDAgMS0uMi0xLjN2LS4zYzAtLjYgMC0xLjEuMi0xLjZzLjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEuMS0uOGwxLjMtLjJjLjYgMCAxIDAgMS40LjJsMSAuOGMuMy4zLjUuNy42IDEuMWwuMiAxLjV2LjdoLTUuN3YtMS4yaDR2LS4xbC0uMS0uOS0uNS0uNi0uOS0uMmMtLjIgMC0uNSAwLS43LjItLjIgMC0uNC4yLS41LjVsLS40Ljd2Mi4zbC41LjcuNi41LjguMWEyLjMgMi4zIDAgMCAwIDEuOS0uOWwuOS45LS43LjZhMiAyIDAgMCAxLS45LjVjLS40LjItLjguMi0xLjMuMlptNi4yLTZWMjVoLTEuN3YtNy40aDEuNnYxLjZabS0uMyAyaC0uNmMwLS42LjEtMSAuMy0xLjUuMS0uNS4zLS44LjYtMS4xYTIuNyAyLjcgMCAwIDEgMi0xbDEgLjEuOC41LjUuOC4xIDEuM1YyNWgtMS42di00LjhjMC0uMyAwLS42LS4yLS44YS45LjkgMCAwIDAtLjQtLjVsLS43LS4xYTEuNiAxLjYgMCAwIDAtMS40LjdsLS4zLjctLjEuOFptOS43IDIuNy43LS4xLjUtLjUuMy0uNkg4N2MwIC40LS4xLjktLjQgMS4zYTMgMyAwIDAgMS0xIC45Yy0uNS4yLTEgLjMtMS42LjMtLjYgMC0xIDAtMS41LS4zYTMgMyAwIDAgMS0xLS44Yy0uMy0uMy0uNS0uNy0uNi0xLjEtLjItLjUtLjItMS0uMi0xLjV2LS4yYzAtLjUgMC0xIC4yLTEuNC4xLS41LjMtLjkuNi0xLjIuMy0uNC42LS42IDEtLjhsMS41LS4zYy42IDAgMS4xIDAgMS42LjMuNC4zLjguNiAxIDEgLjMuNC40LjkuNCAxLjRoLTEuNWwtLjItLjctLjUtLjVjLS4zLS4yLS41LS4yLS44LS4yLS4zIDAtLjYgMC0uOC4yLS4zLjEtLjQuMy0uNi41bC0uMi44LS4xLjl2MS4ybC4zLjdjLjIuMi4zLjQuNi41LjIuMi41LjIuOC4yWm02LjcuNCAyLTYuNmgxLjhsLTMgOC41LS4zLjZjMCAuMi0uMi40LS40LjZhMiAyIDAgMCAxLS43LjVsLS45LjJoLS40bC0uNC0uMXYtMS4zYTYuMyA2LjMgMCAwIDAgLjMgMGguN2wuNC0uNGMuMi0uMS4zLS4zLjMtLjZsLjYtMS40Wm0tMS4yLTYuNiAxLjggNS41LjMgMS44LTEuMS4zLTIuNy03LjZoMS43WiIvPjxwYXRoIGZpbGw9IiMzQjkxMUMiIGQ9Ik05OSA1OC40djIuM0w4OC42IDg0aC00LjVsMTAuMi0yMi4ySDgxLjJ2LTMuNGgxNy43Wm0yMC4yIDEwLjd2NC4xYzAgMi0uMiAzLjgtLjYgNS4yYTkgOSAwIDAgMS0xLjcgMy40Yy0uOC45LTEuNiAxLjUtMi43IDJhOS44IDkuOCAwIDAgMS02LjIuMiA2LjkgNi45IDAgMCAxLTQuMS0zLjRjLS41LS45LS45LTEuOS0xLjEtMy4xLS4zLTEuMi0uNC0yLjctLjQtNC4zdi00LjFjMC0yIC4yLTMuOC42LTUuMWE5IDkgMCAwIDEgMS43LTMuNGMuNy0xIDEuNi0xLjUgMi42LTIgMS0uMyAyLjItLjUgMy41LS41IDEgMCAyIDAgMi44LjNhNi42IDYuNiAwIDAgMSA0IDMuM2MuNS45IDEgMiAxLjIgMy4xLjIgMS4yLjQgMi43LjQgNC4zWm0tNC4zIDQuN3YtNS40bC0uMS0yLjdhOCA4IDAgMCAwLS42LTIgNCA0IDAgMCAwLS44LTEuMyAzIDMgMCAwIDAtMS4yLS43IDMuOSAzLjkgMCAwIDAtMy4yLjFjLS41LjItMSAuNi0xLjMgMS4yLS4zLjUtLjYgMS4zLS44IDIuMi0uMi45LS4zIDItLjMgMy4ydjUuNGMwIDEgMCAyIC4yIDIuOGwuNSAyIC45IDEuNGMuMy4zLjcuNiAxLjEuN2wxLjUuM2E0IDQgMCAwIDAgMS44LS40Yy41LS4zIDEtLjcgMS4zLTEuMi4zLS42LjYtMS40LjgtMi4zbC4yLTMuM1ptOS44IDIuN1Y3NmMwLS40LjEtLjguMy0xIC4xLS40LjQtLjYuNy0uOGEyIDIgMCAwIDEgMS0uM2MuNSAwIC44LjEgMS4xLjMuNC4yLjYuNC44LjdsLjIgMXYuNmMwIC40IDAgLjctLjIgMWwtLjcuOGEyIDIgMCAwIDEtMS4xLjIgMiAyIDAgMCAxLTEuMS0uMiAyIDIgMCAwIDEtLjctLjhjLS4yLS4zLS4zLS42LS4zLTFabTEtLjV2MWwuNS40Yy4xLjIuMy4yLjYuMi4yIDAgLjQgMCAuNi0uMi4xIDAgLjMtLjIuMy0uNGwuMS0uNnYtMS4xYTEgMSAwIDAgMC0xLS43IDEgMSAwIDAgMC0uNy4yIDEgMSAwIDAgMC0uMy41bC0uMS42Wm0zLjkgNnYtLjRjMC0uNCAwLS44LjItMSAuMi0uNC40LS42LjctLjhhMiAyIDAgMCAxIDEuMS0uM2MuNCAwIC44LjEgMSAuMy40LjIuNi40LjguNy4yLjMuMi43LjIgMXYuNmMwIC40IDAgLjctLjIgMWwtLjcuOGEyIDIgMCAwIDEtMSAuMiAyIDIgMCAwIDEtMS44LTFjLS4zLS4zLS4zLS42LS4zLTFabTEtLjR2MS4xbC40LjUuNi4xLjYtLjEuNC0uNS4xLS42di0uNWwtLjEtLjZhMSAxIDAgMCAwLTEtLjcgMSAxIDAgMCAwLTEgLjd2LjZabTEuMy02LjEtNC44IDcuNy0uOC0uNCA1LTcuOC42LjVaIi8+PHBhdGggZmlsbD0idXJsKCNjKSIgZmlsbC1vcGFjaXR5PSIuMyIgZD0iTTE0Ny40IDcxLjhoNy4ydjguOWwtMy42IDIuNi0zLjYtMi42di05WiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTE1MC43IDgzLjljLjIuMi40LjIuNiAwbDMuMy0zLjItLjctLjctMS40IDEuNGMtLjQuNC0xIC4xLTEtLjR2LTMuOWgtMXY0YzAgLjQtLjYuNy0xIC4zbC0xLjQtMS40LS43LjcgMy4zIDMuMloiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik0xNjIuNiA3OS44djEuMWgtMy4xdi0xLjFoM1ptNS4yLTQuNFY4NGgtMS40di02LjlsLTIgLjd2LTEuMWwzLjMtMS4zaC4xWm03LjUgMGguMnYxLjJoLS4xYy0uNSAwLTEgMC0xLjMuMmwtMSAuNi0uNCAxYy0uMi40LS4yLjgtLjIgMS4ydjEuM2wuMSAxIC4zLjYuNS40aDEuMWwuNS0uNC4yLS42YTIuNSAyLjUgMCAwIDAtLjItMmwtLjUtLjMtLjYtLjFjLS4zIDAtLjUgMC0uNy4yLS4zLjEtLjQuMy0uNi41bC0uMi43LS41LS4yYzAtLjQgMC0uNy4yLTFsLjUtLjcuOC0uNS45LS4yYy40IDAgLjguMSAxIC4zLjQuMS42LjMuOC42bC41IDFhMy43IDMuNyAwIDAgMSAwIDIuMWwtLjYgMS0uOS42LTEuMS4yYy0uNSAwLS45IDAtMS4zLS4ybC0uOS0uOC0uNS0xYy0uMi0uNC0uMi0uOS0uMi0xLjN2LS42YzAtLjcgMC0xLjMuMi0xLjguMi0uNi40LTEuMS44LTEuNi4zLS40LjgtLjggMS4zLTFhNCA0IDAgMCAxIDItLjRabTMuMyAyLjJWNzdjMC0uMyAwLS42LjItLjkuMS0uMi4zLS41LjYtLjYuMy0uMi42LS4zIDEtLjNzLjcuMSAxIC4zbC42LjYuMiAxdi40YzAgLjMgMCAuNi0uMi44bC0uNi43LTEgLjJhMiAyIDAgMCAxLTEtLjJjLS4zLS4yLS41LS40LS42LS43bC0uMi0uOFptMS0uNXYuOWwuMy4zLjUuMmMuMiAwIC4zIDAgLjUtLjJsLjItLjMuMS0uNHYtLjlsLS40LS40YS44LjggMCAwIDAtLjQtLjFsLS41LjEtLjIuNC0uMS40Wm0zLjEgNS4zdi0uNWMwLS4zIDAtLjYuMi0uOWwuNi0uNmMuMy0uMi42LS4yIDEtLjJzLjcgMCAxIC4ybC42LjYuMiAxdi40YzAgLjMgMCAuNi0uMi44LS4xLjMtLjMuNS0uNi43bC0xIC4yYTIgMiAwIDAgMS0xLS4ybC0uNi0uNy0uMi0uOFptMS0uNXYuOWwuNC40aC45bC4zLS40di0xLjNhLjguOCAwIDAgMC0uOC0uNWwtLjUuMS0uMi40LS4xLjRabTEuMi01LjItNC4yIDYuNy0uNy0uNSA0LjEtNi42LjguNFoiLz48ZyBjbGlwLXBhdGg9InVybCgjZCkiPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjEiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTE4OCAxMjVIMTJ2LTFoMTc2djFaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuOSIgc3Ryb2tlLXdpZHRoPSIxLjUiIGQ9Ik0xMiAxMzMuOGg5LjNsMTMuMyA5LjJMNDUgMTEyLjJsMjEuNiAyMi42IDIzLjctMjIuNiA3LjIgMSAxNC40IDI5LjhoMTQuNGwxMi40LTI2LjdoOS4zbDktMTQuMyAxMi40IDQuMSAxOC41IDEwLjMiLz48L2c+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJjIiB4MT0iMTUwLjciIHgyPSIxNTAuNyIgeTE9Ijg0IiB5Mj0iNzEuOCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6I2ZmZjtzdG9wLW9wYWNpdHk6MSIvPjxzdG9wIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6I2ZmZjtzdG9wLW9wYWNpdHk6MCIvPjwvbGluZWFyR3JhZGllbnQ+PGNsaXBQYXRoIGlkPSJkIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTIgOTBoMTc2djYwSDEyeiIvPjwvY2xpcFBhdGg+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHBhdHRlcm5Db250ZW50VW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48dXNlIHhsaW5rOmhyZWY9IiNlIiB0cmFuc2Zvcm09InNjYWxlKC4wMDUwMzE0NSAuMDA2Mjg5MzEpIi8+PC9wYXR0ZXJuPjxmaWx0ZXIgaWQ9ImIiIHdpZHRoPSIxOTUuMiIgaGVpZ2h0PSIxNTUuMiIgeD0iMi40IiB5PSIyLjQiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjEuOCIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xNTE2XzczNjMxIi8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xNTE2XzczNjMxIiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PGltYWdlIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3QkRBQUlCQVFFQkFRSUJBUUVDQWdJQ0FnUURBZ0lDQWdVRUJBTUVCZ1VHQmdZRkJnWUdCd2tJQmdjSkJ3WUdDQXNJQ1FvS0Nnb0tCZ2dMREFzS0RBa0tDZ3IvMndCREFRSUNBZ0lDQWdVREF3VUtCd1lIQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnci93Z0FSQ0FDZkFNY0RBUkVBQWhFQkF4RUIvOFFBSFFBQUFnSURBUUVCQUFBQUFBQUFBQUFBQmdjRkNBTUVDUUlBQWYvRUFCc0JBQUlEQVFFQkFBQUFBQUFBQUFBQUFBTUVBUUlGQUFZSC85b0FEQU1CQUFJUUF4QUFBQUIzWTdOZHhhTldyc09EYTh3ejB6dCtzV05zT0tyYkdPNU1RSHFoaWtxZVR1L1pKNGlkcWVMNmszTzRMU1BVeWpjYmJ0V2UxNXFzNWxEbEhZU0IyZTBjMW9IRnlhcTAwMG1ISlhyR1NHSXFYd0taZTFadnVKU3I1Wmo2Tytuc3ZXSnFrMlJFRVZpMWRxeEJ6SHVlL1pyWEVrMW5OM1dUazRWbFk3WUZ6WXF3MGxUdU9LMktrZXBTMGFNbjVXMHhTNUNZZm1KdzB2NG1zMlFScmFKR0pGbFNWc0E5QTFKNUlPbWhySXRvRWhFOVFWUm1aSUt0Zko1cEJiWXdDVFFpUGNOOW1PbnloeGlMc2dabExqK29UVUdhTEVZdllVYnVubHpYY0tLbHFmbGVoZ0FQNUlKOTNlckI5V3FjNi9tV0FRTWZvcWM5cU1Ib3JHZExXQ2l1VG93MW1wakFoV0ozK0pGeDA4czFQTFBUN09mWkFpOWx5Q0ZseTFld2ZZUUMranFDYjI2U3VnUFJsRHZQMm55UXhtbWQ5T2pYSE1hek5CSllpdGMvZHVXcFhRa1VlMU0wc0ViWmdoUXV3YUJNN1NxdnRjdGdiMEdsVFZxODE3dlRFOW42MzdYb2RWdVBpKzM3SDVneXlnMFhWS1k4WXppUzRGMzZDMGQxaVZsWGx2cW9TQVd2dXM2aFcyUkdoWUlYUlF1cEYvTENIVlMxdDg1N3VQRTVzZ3RuamgwRCtwVzR0N0w1a1dPWjV1eXZUMlRsRlpPVkRQRUJNVThiUG84cnRCVW1BeEJFNDRDU2VFVGFpelpnWHc3WHZrY2NPeWxVYldQRjhLNk9SVjZOVmU5NUhvSUwxbnowOWJ6NHZRUXE2UWhURTdTakRXUWVIODMwcmY4QVRlRjVDNzJMcnh5L3RXdzJkb3FJSnB4MU45TE1sb3pXemdVc3FaZUtPbFBSOU02cXI0TGgrazUwaTlUMGc5RDhzWWp5SDJpaFZPRGtmZHZKTk9QTzBpUGh3Kzc1K2h6ZEU4MGpYOXRVMUVURmFONkpmeUdoYUhOMHJuTm9teTkwbm42V25hZENrNm5YaFBHL1E2OEIxck8vUnZpNC9FeWpDOWFKTVNWNXVsWHRPUUtVeTlGTm1HbEdSVmExTXBYbUFYQ05PVnREMm95VjJiYzVXeGY1dElpRHd5QStLMVJ1aFlaWTBRTXZPdWo5K21zbVFubGd3SktrNlc2SHh3N1NYRWk3MXFKYU15N2xmZEROUTdpUlNNc3ZTNHpjYlhVY3NqbjZyT1lTc2NHeG1LTWQ3REZDZ3k1K2ZUUGM3OUZMcmNpWE5YaGk4YnMzMnJRMzZEc2VPd0pGcTJaK25vYW1iVXJWeDFleXNXQlBPMXVGRkMwMVhMSTV1cGQ1aEZrQXREanRyWHVKaExXcnI4OUNNTVpac0QwOE1TSUZZdEI2SVoraEpSd2NLYlFLTW1UQzR0Z2VtUzNwUEwxZDFNaFVzTEZ3anlNY04zb3lsWExKWitwZnc2SlN0Y2JHZlN0QUl1ZEMzbmxucEwzdHptdVptL2p6aUdpUE1nNlNLM2xheUZNb1c5UWVVb1dZN0Y5R3NQVmVOMWlDNkZMTVhFZFVBeFh4OVBPeFJ4RE1BNjJ3UGVYdlQxRFVFeVVWTFFhb2J2bjFPcm9kVU1iUXI3cUlSNnhsWWFqL0FJTEswc0pNSTJWek5KUndTU1hhcmo2UHpJMWFuYUhPMHZ1bm0yUVpEZWliSlFtN3V2cVpQSUMwd1UwUXk5VnVjVlRmUWVkV3FtaDFFeU5HbG01alNPZTZlVnVVRXR0ZElsS3oremRKTXNpMG1WazAvblNsTDlUTTdTaTZFcEV5c2QxbE9HcElSTjExUzJtQjFLVTlRUXVGZE1ocS90NGFJTERweFBSczUvRkQyRm44czFFallsNHVGeXZaUkI1S05CSTZXcGZ0NFc5ZW94TlpnTEJaMEJNMU1LRS9ZbnBDZzdrcnlUUjB5VVYxc1lDRDFzcE9hQ0VmU2JYcHZPWVpweWFwU0x6ZFMxcFpRblZXSmE4eTFMVkoxOFhMM1MvUWYwTHRWbG9MdWIwV3JxNGlyWU0yMVdKcXZldWdWT0NLbW1XOUo0Qkw3bzZSZk1VYmNTYlFXNWVoS1phT1hFMXQrVFhhaVJzNnUzRVpaNHA2U3FoRzRvOE5sQ2tXVlZOVmpYRWU0aTlpdWt4OWVnN2lnSTRqUmMvL3hBQTRFQUFCQkFFREFnUURCQWtGQVFBQUFBQURBUUlFQlFBR0VSSVRJUWNVSWpFVkl5UXlORUZDRUJZWEpTWXpOVkZoUTBSU1UxUkYvOW9BQ0FFQkFBRU1BbzFuSW5UVWxUWXZSUDRwUmIrZDVOdEhFbUVXQlI2ckNaSkUycXNXaWhwOWU3TE52MUM1b1JlSXpaend6OHIxOWFya3Qyd2xYSUg4bE1iK2xjaUxzVmNZcTc0N0pydXk0Um5yZG5IL0FCaXQvd0FaM1R0dG1wTFJ0TkE2d21jajNvbGpScS9TZGVyVFRielYwYVBydzlNeEhPSjRUNnFnYXNqU0pzRUx4dDhSTzJqcDJ5SmtUNzZ2ZkxCZU1oMmFEUkZHWmMyVERLM0lPM0pWeVk3NVM1WG9ubDJyaWQ4N2ZqbXlaMndHM1h4anNjdmJKaWJzWEZNeEh1WmhKYkd1NDlzUnk4bFFqRVRGVWUyVHhNL1hjUmJNakdpRnE2cWdhbVBhMlJPSnBkSnB3VmpObnpKQ05mNFo2YzAvcDQxaDhFT2h3Ni9peEUwcExlZ1Zhc0VYMWk1YUo5Vy9OQXZDMEprZTlFWHF4LzhBc1RDRmc3ZDNKa1ZSdWU1UmZabXUrVmtLMGpDQTFqMDdwZHd2eFhQamtIL2t1TGZRdlpOOCtQUkZ5dm5nbW4rVmlJdStLM3RrckNTYThiM0lvZTVKVmN2c0x1U1VGaXE1UmJ1MWZYK0lWdllxNnBzVUJGZDRXNjd0bnE2eXVSdXgvZ3BPWHUrZVBuTmUyYklteDJPelEwSmE5aHdxNXJzOFFYZndoTVhLL3dDOTVjZmZ5WnBXdFdZUWgvTWpiaVVoUC9XTFBnem1wM21DeUF6b29vK1crV0QvQUplMlI2a0wyN3VrcGphWVMvNjJPcEFmK25GcFFwN1NzU3BiMU9wMXUxWUJrRXFxd3ZMQlBURjlzbDVJamZVbEp2MjhzbnU5KzJlV2ovakl6eTRQd2xZa2VMK2VZbUkyQ3ovZjlwRFJwWm45T1VIQkNsUmpWVE5mTC9DVXJLd2U4MU11azRUeU4zelJjZ0krc041RVJXekFwL3JKaFpBMWIySmtSZDkzWmY2dzFWRGxGWW1tZmt4dkZEVmtnU0dnMHJuTS9hVHI5M3RURnlUNG1hdGh4U1RMeURLR0NENGphaG14QnlvRmRKZUJOZmFrOW5WaE1rNjZ0b01MejlqQVVJcW0zMWZJa0RROUd4Z2Z3eVZraVV4REVId3hUYy95WngveGlOVGI3T1Q0a2swdEhCT1JqQ2lQSFRoNXA2NVlUZW5KT1JzZEZYVHh5RzVFY3paZGNyL0M4aE9XVnYzMW1YNmZ2WSthVGdpTVlwbnF1N0s4RzNaVngwVUtKMjN5T0Z3MmUzYlhWeDVTSStFRW5FbFl0bFZ4V3dZVnRJR0ZrcTVkL3dEYmxZY0VtZkhXTFBuSE9PdmpraXgyeFlza294aGpTeWR2UG13Mmc2NjEweWtoOWpNUG1rclo1R2ZEWlJWY1Q4dVNja3NiMW45c1JxZjJ4TnY3WnczL0FBeDQyS3E4a3lRRkhwMDJPMldiNnBadDhvRTJHN05hcW42cm55cVQ2MW1hZy9xeDgwY3greG5JbWRKemRsYWk0UXo0dnJlUElUa01ORkZBT21ham15cEdxSkxUdmNxRVJwUGNiY1lORVQ3T2FSclljK3hZS2FIbXkyakJpVHlCampSclJ1VnE5bHdjK2F2cFNRNUVwVG5XNkM5U1pzdkZNTzFkc2xKOVE5UDBOMnhNSjFPdXZCRndodmYxSnRxcTIrQ1E1MW4wK2E2QTFHeTBwVW5USk1aWDYyV08vU2huTmtONVZmM3RtMlh2OVRObWpYTlFaZVNwZ0pNZmZicnN5VWFNVU82U0dMbGFZWUt4WkpIcnRNOG5aWHhaZ2x5eldHTXZDQW5wWThpL2x5dG1UNGhVSkRjcU9rU0RGY3BaaStxSWdYdVRsN1M0OVFJWTN4Q3MzYStKR2tqa3huYjRLYkdmRllWT1d6aUFkM2VUYkprQ0R1OTZUZlV1MzZPV0VNMFBjamtUR0dqT2J2eWJqWEJlVlNMSkRsWTZNVWF0QVJyczFvakdhYU1palRlbjI4NlB2bC9zMjFQdG1tb1pwWFdleFV5RFJ0WTkvbitDdGRwZ3NjS0JnQ0V3SjFIQjBYSjh3OXJVcTVMQ09jakJFVkxHeGhBanVqQ3NuQWNrdXNjbkorcVpmS2kxZlF3NEh3NlRhbFYycTcydG1IVnRaSWU5UER5K2cwRHBiN0NhOTJIMWZwMDRURFdkS0lpV2NhRUZpZEdSd2pXbXFscXdGaTJmb1NmcmRZcm1FbEFhK0pMMW9TVnpzclVSQTdmNS9RNWY3WmRLdlNiL0FIcnRRM2RocXY1Qk9iUnZpRFoxWFRHTGxOYjZmZDZIMnczRzE3MjB3YmJLbmJ6bzgxQ3Y3M2s1cEd5RUFoWTZzSXFrczI5dDQ1c0M4azRQMGc5czF5QnkrR2RrSDgrbGE1eWlYdG1wWWpVODIxY1dGNlNLaXJrU0V4cEQ4OThiWGtZeGlGYnhkQ2hmYlJkODB3QnJqVGtjbUdDMXpmYmZOTVYvOE9SSFlRYld0VnZESHhpODNjUnJzc1kvL1V1S0V2OEF3WEZDYi9ndUhoR001UFJqOUYwOVJNK0lWMFFZekZzcktBNVlZTk1kWmkya3dmekM2WGtDeldNa2t6U0pqK1VVQ1ZHNnpoWnFMZGJTUm5oVXN4OGlVd2ZIaFkxWjVET1hzczJzT3gzVTJQbXBTdkpTU0FvS2U1S1I1QVJDdEV6ZDF2SmZJUElZL2tqK2tYWTN5a1hHZ2t2SWNZaHNSdGpFZkhsK1dBeHZBSWkrcnMxdVYxa3NPV1lTWXdxT0cxclNiNXBaVi9WeUp2a25iamdrK1VpNFg3T1B4ZmZKTXFMRlJGa1NHRHpXdmlSOEN2aTZjQkNocG54T1dHU3dUZUhHUlpzZUZXT0lITlcyTXQrbmxHNlMwZzZwclVuaVROUS8xV1QzendzV1I1R1E4Y2hHSUk4dGoxNnhlcG5pRmRtcDRJVERDMGJkWlg4MnJIOE1qWE1TeVRUd2RwR3p2ZlZ6VWJZeWRzVXF0YVJxTlRCSFhxa2J4Yms2UnZQM1JpSWlHOUtyMnlpaHhwY3lRNDRVZGcwYXhHb3h2YlROMnljbnd1L3V6MXd0TnpaVXV1S2g1U3lHaC9rcGhQYkNMdGxqUEJYd2pUNUMvTHFmZ3NxcC9hUjRpUFFwYkdSWFRaa21kTGFRN2g2cDA4aSttekkzUDFycDNkdmpVakxuVVlUeFZxWFdyek9xM3A1NE9haGR2WnlGM3p3MXVoUW9CNDVOa1FtcFlxSjl0RnkwMWJWSUJ3enVZNXBlTmdqbVYxWXdJSXV3amRkcnMxSFBiTGxuSnhWTVZCY0hZQkFvUTN0bHQwM1QzT1Z5YnM0OFVYZktDU3dCNU8rSTdkRXdVYUFmVGRlV1hEYWJDTllNZkViVVJBL3lVd2k5c083OE04UTN5RGFNdGh4YzFpVlRlRzFPd2J0MG90QmFLUFhDUE5qR1FsbkE4SW9CT2tLNG5HV1l6d3pERVU4R1ZiTEkwKytZZlVDUzVMM3Z5cWQ5WUxMNTM3eFBtbUxLaWRhcFRXVmlZUnBtbXRQeEhpWWVSTlZhcXQwckVkMVJRRWM2eEt6Z3F0WEs5d3lGYTBpYnBxU3ZqUjVjbFFBNDU4TnRDRGU1c0Fqc2oxMDlDRmM2dUpoNnVlOHF5QlJEa3dWUGNxaU5mQU9tVVZiMTVraExHSXU3a2FKcWROcU5Ta0Y1alRVTm5MWlRidEZ4M3dKaGRGTnlwaHBVYi92Wmt5WkhhanZuc3k3dXFkMVZJaG5uaVRQUEF0ZERWOUdzcjU5RlFXSVlUZk14aGNLN1RFMmFSekJ5SEprclJ0NURSU3lKUStHbXBjbGRSQ2h2TnVsUTVmTWp5ODM4K2JkYzhPYXF0c05mbk5NaXNJN1dsakFwM3dwYzQvVFpYNnBqVENJWWZOb1RXcGpXSGxTZWxHMzhLcnNFRE1rc0c4SllsaGZJN21oQjZYMFZadlVGeUZnbkJscDVXTVNSOEpBcWFjMUkzVURpK1RxUkR6VldtWjJvbWlSQ3h3NXFlQXROWVBpR2UxVkJxaURhV0xhdW1Na2cxVEhkQ3E0OFZ6dDFrL1lWY2xQMk8vRHY3ZStXYnQyWlltK1c0V1BIR2kvRDBIU3FCMUQ1QndlY3BmWFk2aXFRUkNCWkY0dkRFRGYwQ0pLdEJNYW1oOVBVY2x0bEZ1M21QVHI5U1BMdGZyamQ4OEx5b3p4Qm1acnhsZllKQmoyZ1hPR2x5TXNoS2l0WGloR1JXMmtOakJwMHRWeW9saE9MS2FKdWFPRlp2c1d5UmJPancyTjAvR1pFY25WT1JZMXMzeVVwdlRmY3pOUjJXcDAwVnArYXNSMGp3NnZZVVYwdWkxM1lQbFR0UVRyM1RRcHZVNko5RDJrYXBuc25xTkZXTkpGTWpEa3h5Y21TVitXdVMzZlBma2gzYkozZG1YT3pKRGtUSmxySmVzQ01iaTBXbEp0RWVwSEpESDZtQmxFWXliZUxEWHk5RlpSeVZQbk9BeDVMTjFvWE5Pc2pLbjd5UEx0ZHBoczhOSHFtdnBUZDJiZUlMWkx4eEVqOW5RSWNZUzhIZDgxUFlrZ2pBTURFeDhGc3dmcUs1dVVqaTBoZkxqazdqaGFzb3JPdldkTnN4Q08zVUZGRWhwWVFiWUpTU2kyTmZxdG1ycThiREwrMHlKYTllTlExQlhUUlU0YTZxWlR5cEhVSkJxNDBjdldGelZmRFdmSmNZMWM0MjRwSytoY2xkenZ3L3dDT1RQc3JsOGlkYmZMSmorS09HemZOUGVLbHpweXBGVGdwd09IUzNNV2Q4U1NRSmZJeTZnTFF4aWhzMnlNK09VUjlIa3JINlZucE1vSkE1Q0FJTmQwdDNmVkZ6dzQ2S2VJVXAvUDE2NWxJMWtORVRkWWt1UTkzcGhiSkpwMlhER2luSTFFa1NDQnNqeG9saTlCN3pGOTVpcmswQkNSbmo2MmFhaUY2VE5wSEZiZHBnazJkSzVaVXdqdWNVOE16a0lsWmU3UGM2MWNtTWl5a2xNWWUrSTdLdnhUOE1haEZheFh4WFRmSGp3N1Jla3lYSmRrTFhPbkx5ZDVXdW43a2tQWGJKait5NWJwdXpmamlzNUw3WjVaRlJjdHRESFBBZFBtU280UWVIdDFwM1RNdDBVY0h6U1duamFPaWx5bVNxQmMwOC9xOU1uQkdaYjlwSk1pcnFHTHFVbGxwN3A5UzBKck9XcVQ3STBabVZscHE2YkRiT3JQSTlQNHo0Z043dlNBbVNHZWNQMUpQZHpZUUdmbWRrWUVkVEl4Ujc0SHA3S3FJaVp3NnowSHZsZkViRUYwbTk4NnZGaEhPeW5lMGxzZVR4OVUrcU00WmJOZzJJS2dTMXNLNXZrQlU3R1I2UFVFVTdqUkw4QVhuZzZvTDJOcXlTL0NhZmx2N3lMZVF1TFJzQ3YzZ3k0eUtvdnNrTGdSTlJ2cjZtYVAwQkFmdEN0dm5KcVNyMDU0YTJDWFVablRUV1YzQzFWcU1ka0tQd1pTcWpPaWpQYTBYNXo4c3J1ZFYzQnZKeVhNeVRxaWRPQzRNbVdWeksvVWxoV2g2TVFqbXQvVzIwZXhmTUhlcU1meWJtNnY5c2FybXExMitCTTNiWnE0RW5FemVHK1I1UG8zZGg1L09JWGdtVXE5TVpENGVkTmRHV1A1aDNUcXJXZFR5MG0xNTFZK2wxQkN1Z09Pa0xpVWtscEFLc1VMZVRPcDBmcWhzUjVlRFc4ZHNjamQvYklveHZibi94QUJCRUFBQkFnTUZCQVVJQndnREFBQUFBQUFCQUFJREVSSVRJU0l4UVFRUVVXRVVJREp4Z1NOQ1VtSnlrYUd4SkROamM3TEIwUVVWTUhTQ2tzTFNOS0xoLzlvQUNBRUJBQTAvQW91ekIwYUVSMkhhaEFQdE9pakxMTk5paTBmRmhHa1hqTXpWZTZ2K0ZMcVZkU0s4UTlsaCtuRU9TN1phWFMwTlRqNGxRekV0QTBBekFGWEZORGZyUzJldkEzS2h0LzhBVUZYdXIvaFU5U3BGYVNPNkhzbjBDME1oYUU0dkdVa0hSQkVZV1ltWDB0WU9PRVQ4VkdMcW5VakQ0eVVkN0RoQXdtOVNiNXZyS3ZkWHU3bFBkTDBWN0s5bGR5N2tCMUtyelN0TUc2bVFoc2lEM3J6YlhhQzc0U0sxSmpPUCtLdG9rTXo3MDU3VGhWTGZ4aFZxYUYwbkx2WGVVRG4xTzVkeWJ5VGh3NmhjVjNMdUs5Z3IyU3VjTXEyZE9TdXpXSDhhclUxVk9XK2FoaytWRFhHNWFPYnNwSVB4WDhtUDFUZTA1a0JsM3hUMnpodmZDWmVQZXY1Y2Y3SnBIbEtQMEtKRlR1WHYzaDU2a3BPRFlrbHptZzkwc1NlUk1UdUZ5d2ZpVlkrYXJVNUljOTJoVVJwQkl6RTB6c3NiRXlYM3hUdTB5SkZKQlRMbXNaRklBWDN4UWY1ZUUrUGg1RlF4aGM3TnczMW5xVFhGV2grYXFXRDhTdEFyUlZvODBlS1BvT1RZMGcxeUhCdTZkNG1nNlFHN1VOdVZvTjlSNm53VFRJOTZnVmtOQW5NcDBTL290VkdsMkpUWUpWWjQxV3ExV3ZhV21JS0d5b21mQlJJaGRtcFpuZDNJNjdwWXdDaEk1cHpaakxjVE1DN3FIYzV4SjhweFF2ZGRjRWJPK1hyQldnVm9WUFVvakRKeVlKUTJWNUJNMk4xUkp1eVU4d3d5UWRqSmhPSzQ0cEsxbmFtRTdEeVJuZlE1Um9MYWFXRTBpZWFjNmw4NEp3Y2hJWElBQUd5S2V6eVE2TzNDRk9iWTBnWmpoVEphc0d6TmFlcE82YVpYRG9jZmo3MFBzMy9vb3pnMkhBWk1rcXVGK0lLMEN0U3M4TFY5MmdMNjdsMFdjdjZnaUpTOEVDVllzS3RBUWZGVGlOSUI0elIyT1h1VnF3LzlWSlVINTdwbGQyOGNWTTFQdGpybmNoNThQYldqNWhWQ2J4dE1NeXZSanc1UTNQbWMxYUJXcFFIRFZEZ3ZzZG9zL2VuQ21jVGJTV3lubkxWTnFMUjRKM2FhWVZONnNSZVNueFdWejRjVTJNLzVJN0hweUlVYVBCYVhUN09GWlZLZy9NOVp4azJ0OHBsUGZhUDIzYUJXQU9GSVJGNTZPNlFYT0M1UklqUVhzZzBnbWFyQ3RTcmFYMVUxNnJhVnRPMU5oUmRwaUNiWUE5SjBsdEVBeXNXU01LL1BEY2ptcTBJQjg3bW5PaER0ODBZNTg3MVYwVjJSNWhNc2kyZnNvYUJRVy9STWRrSXdubURxb1VaeklHMGtTTVZtaDZrQ0U2SS91QW10cmZWc2NHTE10MmVIUENHRGp6VG81c2cwMCtUMFhxeFNQelI0UnorcUVjVXc0bm1xMEN0U3JZa1hMWEpFWHRkZk5PN1Q2SkFwZ2NYZDBrKzhUVmc3NXFVTStNMTBtNysxZEhpZk1KMWlCZjZxMWtvTEdPYXdzQnZRRndBNm5RSWsvY3JPSFQvWW9rRmgvd0NRY1Z3UEJmWnh6L29nTUVOenNqN2s2SVhGNW5tcXdyUXFPN3lVTnJwTmNvanBTYnRCdlE4Nk5qSTk2cS9WT1k2NU5OMGtZUnlnbmlwUXkzeWVzMDZKcnM4cFllNVdMeDlWck5NYkNjMFBISk5Gd0NzRzNvQmQ2OXBTOUpSWUxtT3hjUW9PMkdFUU01WDNxeUZEeVh6T1drN2syNnF3a1BlaExGYUlSQ085VmhWbFFOaXJoVmVhYXMxVzhWZUNGekFJSkplZUtlWmR6dUNhUzF3THNsRWpYRUhPNU9lSFV4SXVZQ2hzTHBBajlGQ2xWVy9qNEtGUFVtYzFDbUM5dVJUK3pEWURlbVFnRHVyTzhGUGlsN3RwdHFyYk1UcDBWaXdERTg4SnFSR0xNSjhyd1JPNVY0WWVHVi9jcXdxeXYzYi9BSmhXN3NQSENtTjhwRTBaeTcxRHFuODFsT1dhaFJNZVYwNXF6QmpPZS9DeWVnQ2NLWU1lRVNMK2ZGQmhmdG0xRHpHcHQ5TzB4SnNlZUhKTWptSHRFdUtoSEhkOFU5czJrYTdxenZLMldLYVh0blZlakRBdE9qbStWM0JNd3h5WWZZT2srOUh0MC9zMmN2SFZPaVlRN1oyc0F2eXVWWVZhT3dlT2E2UVExM0REZXAzeXlVUXUweXVSMUNpUkJVUzFCdmxtdnVCbHFPS0xxb1VKbDgzVDE0SnpERDJ1QzU5TlFPb1BGTWJMNlM0Tll3L21ueERHalBZOENiaXNqVkVURzFNYnd2M1ZucUIyU2dpVFhSSE9ubXRxQlphdWRTMG5UUEpSTDNiTHNaYzZnNkxaelRFaU0yY3lVeGVxbDBHNFQ1b3huU3ZrT3l2dmYvRUwyMFpoTWlFTUR1RzZXU2RPK2tJYTBCVnlKbG91U3FhNThJUHZJbXNuQ3huOGlpTzBJRjN4S2ZNdFlSdjQ3NE8wVWhqb2tpNGFYSWkreUV5Q296SlFRKzR2UE5QaVZCbzB2VlNiQ3BOcHdLZ05KTitpY0xxbTNvTGpOZTBqeFhFYW9uVlp6NHF0QXlxOFZhbk9LSjVwbHp5ZjJjeDhRZTFNRStLT3NDQzFxNVBrdWNVcjcwcjIxM3AvcEZTRUxvOEZuYTVsTWhGczlUSlhLYUxSa25adExzL2d2UkRrN2daZmx2MDNETzlhSzlQaUVxZlkwUStQSXBsOGVHQmQzaFN3MXJXakxxZi94QUFtRUFBQ0FnSUNBUVFDQXdFQUFBQUFBQUFCRVFBaE1VRlJZWEdCa2FHeDBmREI0ZkVRLzlvQUNBRUJBQUUvSVRyak16TG8wUVpsK1E1VENoSDY0Y2dzQUEvaU1HNS9zd2dBTkNKbjZxQVZEb3hrM2hpQ2ZSakFKd1J3SERYditld2dBTXd4cDIrNFRnNHhDQVNJejJhOEVqNDJlaEZqZWNTQS9DWkY1aHZWVm1ZV2g0ZVp0eEU0K0J0TUZHSnM2U1JpbjlERWhuRS9SQ2hBTnl3WVpEWWNHNGNRVHBjd1k2SXovWUNPcHEvcHdRSm10RlFjR0dFTGI3Z29yRWhjcnZqc1FnelE5WWRZMnhuSkJoNUgyREQwc0tQQnhoajRrN1RrNHpWRWdHK1ljZFRlb2U2eTRNSTBueGNRWFIrVVdMaENhVVlrZFE5ZDQvcUVrMzMvQUpoekJvUWlTVVZDQVlDcjJKVzNJbERHd2EvZTRRc2Q3RUdRR1lKTUEySU0xWnpERG9YWVYyanFCdXJoOGNFUFc4bWpESzlnbUxjaGZYL29rREZDU0NCMDRJSG1CUFJnVHVKaHRyL1Vjak9sZ0NOeStXVFhVeFBiS0NkQUFZTkJneEN6Qk1XU1BhSEN4QWFCUnVCYSsvOEE1UXVONjJTT2ZlVlpwd2h0emdoQUFrOFpoUmMxbDBXWXcvTWlvQ2E1Y2FmTUF5U3BKMlNIQnVDUTVtUm00UVFtYTI1cEcxK0VPQXNqOXpLelV6QXBJanBRaFVLWXhOelpRWDRuTVI1TkdHTFBYcFdUSUd5K2gvY2g0ZldiRmFhQXhUSXg1TjRMdTZSL2poUU5ab3NsQVc1TWZ6bWFJUGVlcHhFVWd3NERvTjl3UkVmZVcvdkFaQzFnenJJNVN6M08wWUhNNTVoanVQeTNNTmlEQmhGcTdlMklLcEtZQ21VQmtpbEhVTVB0SXpZUG1Kd08xRmd3QmR3NlFGSDlrcmhaemFya2p3ZGVSZGh3WkMwZ2VBSEw2RWNuOHNLbkFBMGhiVjVtZEluWVBQWWhKRklXYWoybmc3aVA0UWNUSUN5eFAyRTVnSWh4QmFMcmhsNVVld2k4L3dCakFuSi9kRUJSeWhEdU5uMElRRHdoZEkyZUZBQ3pPYzRoZjFEUGQ1MEI3Z3dFVkVGQ0pBV01BR2toajd3dUkrSWxrMkZRSHlCbUh2Y3dycUUvaWpwaFF3aWtIM0Jna1RVY3ppcml3bHZCMXdxQk9rNmxhUUZhY09nUU1BaGtVa2paeEN0WUxSZEd2V0hYdy9jZDdkNE54clVUMEl5Q2pyT1dwSUdCam83QTZoQm8wVUo3d1ZKVWJXU1ovVElCQ3NFU0t1TENTRGhDSUZyUmhLb3JLVjd3U3dqVG9lSVhNbUhKZThIcUFkT0tOYXczM0xGUkhNRldENlRISWxIMHFKTVBDVS9tVjU2dzJ4Z2hHVkpobEs4NGhJd2g3bklGelUvWkZHMkkrc0NJb1hCREFHbzFIMEUvTC9DSkFpUGtPQXNNbUN4SXdIeTlIY01JR1R5T2JHSU1MV1V6WXBTNXBUTmtFdTNwR1ppTUJnWk44UUYreG5qWkR1WGRJR0REVTdRM3lOd0ZhekJ1c2x6SlFOWFVIYWFLb1pVank0REgxcHRSQ1lETjNHeEFDbUwzd05TdHMyb2JNVERLUUxScHA3UlNJU3NTSHNoQWk0Wm1yTlVQTXlYQW5OWVJmNUplSllSeGNhTmUrRkh3SkJKYWhOSmtGSUNQb041NUlXY0FoNEJDYXpHK29jcEQ0UUVzQVIwY01JSllGNWtBTFBpQ1o1MmcrUVVkMUdhT1FnMlZJVWNHeWpZbmk4VlJqaWNReVE3Z3N3NytxRlY4VUs4TnBueHRiWkNyY0FTUldTRGg4WGhRVm04VjE5a3Rja1ZCdUIvYkxEUi9sbWJnSG1hUzc4bmhCWUFNcSszS1E5Q1pKSmtTblV4R0tHYUlSNXVNc2pXTGhNNFlySnRFUmxQQXh1ZmxEd3VJS2FacU10ZXNLRFBnYjVnQWtnSmtRNUNlWmRHMUJCWnlnSWlCQndEZVZkUm9RQ2xOMGc4bWJqS0F3WTVqQ3pVTEF6bUpSMjh3K095RGhNWTNveEY2YWFDVmJPb1F6UmY3eGFLWno1Z2JHd1oyaHR4SFFrVVVCNkZ3M3FpSlFIeE5RN2FHT1NaRWJhY0tmSG1vZHFCMndUK0lzckZENVF4ajQzWDBaNmhDblNvL2lpZXM3RDdVVlMzQ0hhQWlqRUNhSDVtUTdJeE1YUmFlZVh2dmNVbDZoV2dtY1FkSVBhTDZoYjI1Q2kzZFBhRGJGcVlabmcycVV1QmZzS1NWVjRYUGVLeGw4T3JBbFBHRys4SGp4KytIallKWGdSZ0hqQVFpV1VsQTlFS3ZZbEh4QnVLcUJZT0VLNTE1WTVtdlcyK0tRMTJyZlBINk5qaVRBSUFjUTBKRndHcjZYQWt5RnlOUWVzZGdZRW85RW4wbFpyRlFFSUR3eEpNeS93Q1VVd01ya1pnZWpnb3RVR2tVTUx3RmJFd3JDM1hFQ1N3TGIvQ05FTHBkWk5HSEV0dkVqY3ZFV1pmN3dmUVB1aXFyR2FPTFZHRlZLdWhlT0lONHRPN1dRRXhOUVY1ZzR5d0lMTXlCakJQWWtYNklSQlp0NnlhekRNVWR0b21zUHhPUmp5TUhVRUFKTWd0eDZ6cmlHUk5naXE3bGl3UStZR04xV3hnUVh4SWRGZUdBeGdvWHlpQkpnRVdPTlBXZFNQc0VIY0h0Q2hvYlNLdGNvTERJMnQ0b3h3UG9nNk80QWMrWDFoRjJFZVlGTU10elhUdU5CN1lFMmV2aUEyRmVTWUFJRGdRUXVjUE1mY2lIRWRBamxpTFJqSG1XUXVoK3owNWtKSkxVSTQySEpWMUtHMXBqRHNrdWtCdnp1Y0E2SEVlSzVlQkdWUFhKaXRuVWVHSURtVUVPcCt6TXkrWWZqa0VUSDdpZUtxZ0tpZ1ZLZEdNeU9RQXBtaG5pYytRbG5LZzhiblVXWWJQVVZlOGxORDN0aUV0djhrVzJsSHYvQU1Lai9DUUJUNFBxSDJwUmhjb2VFSW9lOEkyNXhCTW8xQzAzNWh0cW1wVUFYTXVxZ1pvTzlybURaN3VKb0FPaGgzWm1saEtRWDdjclA2N3FPM3JIcWdPT2xITGxWdVlBMGRQbUJQZzljTWNEcUdwL1F5K2pDZEZGZ3JlS3lCeVdSeDFFL3dBMG14Rlp0WWdFU3pEUXk4bFZCb3RXdjJWY29yaXdyQ01vQ210eXViZ0FYK3AzS2h5KzV4WEU5UitJVjNLbkVreEc2aHVHN2l3SThwL2NXQ2hFNmt4RTRqbEUrSU9SVEN4SGxHNDlKdlFlWWFzSzlnc1FPREV0TzIzWjhBSmczZ1d5VEpCS3ZpRzBPYWxNdGdvY1ErckNLdmdUT3IvdHphNEwwSVpzL3dDa0lKb1ZEbEc0aHIxWkJ1VTJ2WkcxNW1BempneG9ZaXdFRnoxWTRKUFVkTm9oaDVxb3htZ0JCYmcvV3d5ek5HME9tNVhROUtpTmt5cnBiQkxmVU5aQTBmY01YeWlNSGhtTG13NUFuSkRqTXBFcEtKNzFEMmtnMHg4Q1Uzd1lnTmE5NE5vNXkrb09RaFJBQWRUUEVZQmwyYUVmRHgvQmhHSWF2eVJtRUVvVmw0NGd6WnVrZU9DeWN3NDg0UTF0KzNDaXZjelZSS3poTGlxK0RtWUxPQUp3aHM5b2tlSVVnVm1vNWdDUXZtbUZsYm9RdnBrMlJRckVJWnVaa2hWRFhoQ1RLeStUQXBqd1l4QnZBdFNrTjVFRGJGUWV5TGpzWXh1Z01wWmF6OC82VGQ3RmpaYUVHeS9NUjVLNDlFWWxMblhDRlFpV1hJL2pUYXp2KzFQZjNvbjJZZUcxaDVqbVFMQ0FZY1pyeUU1TWR6TEcvRTNFU0ZQQmpoeXlDQWh4Sy9pQjJzRS8waTFWblErUkNHalFpbUN3bkVLVEY2aDZaUDhBS0ZPQjFDM0Uyd1RwYVFLREZka1g1aHk1WklkU3dOY3ZCYlA2d093YkVTdUtBdmJ4MUNqdlJCOXFBazg1ejlvQmhlRUxZWDBJVVZqTS84UUFKQkFCQUFNQUFnSUNBd0VCQVFFQUFBQUFBUUFSSVRGQlVXRnhrWUdoc2RIQjRmRC8yZ0FJQVFFQUFUOFFDZmhOUnJyYkFwNGppVFF0NTVHRnBxQjh2WjVlc2dONURha1BVVXNUeEppQ0FLTUlucjlLeGJDOGlRdXdKWFQyQkFBTDRSVGViUnBINFJHckFJaCtadTNPWVlsckg5WXBLZkpHMWx0bURYdm1Bb211bnUwdHFxdlJITFQzbk1yQ1BaSEc0VXRkZzVxNzlKQ1lxajYvT21sYTdmQWwzZXpnWlZzZ1VBV1lIcWVsMjhxS0YzMGpVdlp3UWgxU3hhTWF0OHdRbkVLcjF6UEpYZ3lMVDFDYWRTdVNoM3FGWXVHcFExRWcvQitHYUVFcmlkUXpPSXVjL2NZYTZ2N1ZDd1BleDIvQk9ZcER3UTNxT2Q1YlF5VnNudXRTcmFyZGhQWjFMbDFITEZ6VjlqY0tvejJMaUpRUXVkcGloY1ZBWXVHdFFleW1hcldVVEtmaHJDenBzdnlobEZ6NXgzQjBDMkNzR3dkQUNzREl4VkpDdVU5TVR3UEM2d1JBMXFsYkNLNGpqb2lXVlhHMHNFNVhYT2tMdUQyakhjOWRCbG9ud1Qvc1BlUGtKdjZoM1JRVmlyV3UyT2xhc0tyY0t4aHQrU3FnTzlZallLUHpIb21vZGxzUnJJQmhLbW9mS3k3TmhDQ1hsYm5Da0pmaW1UbHRRYXZSaWMrZ1djS3orbExpcmpGWVdsNkhJakpUUkFIVE9UckNRM2NQQ25pZE84VHVzZzZwY1BZc3BaSzliUFArV1ZhbDRkUDFGNmFjdjRMZ0dGRkM3aHFSMklzVXU5cHVCM1ZkOVRmL0FCbitVUTB2am4va1dCM2dsdEFhR0RuaUNWWnJzQkY4azBXTHMwVHVUakxOL2FLaE9FdU9xczd1TnJDMDAvQVpHaHBuQ05mNHFjQUYxUWYwUXdCSEFhYzkxQjlUb0lmWVM2a0ZodEx1RGE2eUpaZWxMbFJTeHZJZFhyRHNhb0hiTjZKREVnUzdlcXdMbWhQRzdWU25wVHhjR3ZCM3pCNU9iTUtyblFDeTIzSmZBVlJMcEl4QjlwZVhINHdqc1VtM3FkYnAzRVVwVDh5MnBoYU9EN3Uvc3ZzVFdIRWJ2ZlZXRml0TFdaRlBBR2ljUThrZW5VME5wUXk2TTNuRnRWMDhlR3NYMmJERllZd1FBdHVTc0JuU0MzUXQyNHJrNDF3RVhvUUsyS3JLN2wxQjVKQU4zZTRpcllYZ0V5OE4yd2dnZlROS2dLd0F3TVljSVVnemlKeG1MenJGeXlhRnNYMmQrb242Q3dRSUZHbEZ2MU5odWd0SkI0dFZtOTRScHI2WUtFUmo2TGFPZ3owc1Vzb0tCQ1JkZFFUN2hJZTdaNjcxR0wzS2pKOUtDbDM1K3lISHdtYU9CRUZ2VVh5dEtvQm5qaEVpWGpNbGtQOEFsRElBOXdhWDVpaTNCMFZ1Rm1PRjJoc0czZmNNa3BzSzBPbUR4MmczM0d4ejloOVp1RFYxMmNQQllVUHVZQUo5RmcyOEpRNllxWXJ0TXZ4RDZCTjY5NUZZMm5ud0pGY3JmTVNnVUdEK09ZV0Jua2hoaGhVRStSbW1TMDZDaHFyRGFtZGt5dmRvR2NZRDhyamlMU2tXdDhQSkdJRjhJemRITW9zdkZYeXdHbTJOc2doRmhBTmRUUjkrSWw5ZWZNSWVRK0tsdENjYzBVTkxUWVhYVVllOWdLcG91N0FQUlliRDgveU5CdG9ncnZUajRrSURRSytkaXBWWUdOY21zRWZWMDQ5ZmNkQmsrUm9JeXp2bElMRjNpc25NZjVJcmFvOHpjRm9ONDQyNHVQQ3RSN3pOci9uUDFaRmVmcXFlVk9wUXo2K0FYem5VMk1yMmZsYUhIUkd6MXdtbEZGdS9QdUF1OHFqWGs0VGNqTmtmVzNVWVFCSlZYZ1hVZU5CK0Ryc2QwNTVrYlN5dVZKeFZHMkN3a3BXYWNmS0FwWXdTMUYxc29MVCtOQ3dMYzJpdENWMnEwK1d1T3EybitNM3NBMDdMUXdkRHVWMGVwUlJZVldsN1pveWxZNk4wV3NMMnp1S3F2Y1ZBV2hIQlpYTFlHaWg3TXJ1YkdjbmtwQSt4a3ZFa1N1YzJMQWxRdWQxS014UlN0bFBNSk9wVlU3QTNHekZMM2RMbGFFSFVmUFVjcFd5V0M3Y1ovWW1nQlpWclJzNTRnOFFLOGxBTmRndUwrVUVka2RvdmlZejErTlcxUXIwd3J2R2tTVWhkSW8yTldmS0dkNHBKVHdmaUJRSlErOXE4aEJETXhGWVc5akdzeHZqdVVHd09LOWFBRUJXd1lrZElrdlBPWlpSa3FhN016SnZQL2FlRG0rY2ovd0Q0ZkNZdjFTQXRtQzYrcnVQSFB0V3FLanBhNFd5MHRmbVc5YW1WeXVKanppQ2dCR3BTZTNGMyt2VnpGSWRPUUE1dEQ2dUl3KzlpVjRHZll1d1IvU1VXZ0tidDZKVHBWZk5iT0JBYTd6cHdyaTBsdUpZNFp5cVc1dDlxV2dvTmZXWW4xUytHdi9rdGZWVVd2d2F6K0piWFlRdW1DNWVaWFd6VCtnNVMyMGNkeE1IRlJGeHlyNW80b2dFRmVqd1psaUlpWnh5aFpsd2RybGhlaW8xdUU2dkRwbUYzeTFUbGdqOHNHY3F4QTFrd3lMVXVzWmVqRDJRU2tGK004RHFoOTNMNE1DS0NLMDFaNng3Z1hkcUNDQUM2bXVFUEVXK0hWbGlRb1VXN1RCS29NYldTYThmOFlYYzVKRGpkMzNjbzRsME5IQzB4V0lNVkJlbDVwMG5FdUU5YTYvTEI3bUNrQ2s5dlRCMVdpY2FMNVk0QmJMdXhtbDBhbDgyZGNRU0Z1aXFWWTBsb0VPM3FOc3FoeFFVMmNBYTlFUllXc0R6Y0VPVndsQitxWTlRaWdLV2xGR1ZUZ3VzWTA3NGowVnRkMlRPY0dMOHRiSUZ4QXVvQXF0YXhYQ0ZKWEQ1UVd5eUVPVGs4WXdpVlZLbmFsZFBCdCtvSHF1RTBHMXl4dUs4d3IzSmhTTTRkbmx5dmNJMGZHWkxQOFBEMzRKbnQ2N0hyeWlKTXlRTTlUR1A5SUVyNFlJMUEzVm5RWVFCcGlNcGNTQlJQK21BeUZUeTVRTUdXditXVWdyalkyOHFxaTJrV09mNDg4Q1VzbDhxc3M5NnNWbnh3eGJTb0xCVitGaGlibjF4YkJFNGJVWDNDT2JkdjNtWTZyWDUzRTB4U2xrTzRWREZVUGRNNWlDSE9oZUZZeGZjVjVtOE1MNXpWSG1JakQrVmlXbWNBUElGTkhCYTRpU3E0MVhhSWJJenh2SU5QZU1JZ1NMVGtiakU1MU5GUGVtUldyTURWS3JYcVhkRWxpQzhEbDFUTExwQzBFZ1ZqTVpqV0VicWc0SmZYUCsyQXo1SVkrdFhEUkdlYkJQekJGcXUvQkYwL0ZNVHoxUkRtMk5XU2hDNjRXckhxbHN1VmhlVWFTcmFtalEyeDZ5YnRLcWtjclNtQlIwb2ZyQ0NKaytBdFBicUJRMEpXbXRxVjRSR0FwckZWSFc0TXFidDVmQzZQZ0k3SDFsY1lnb25NMGhab2p4R2pyeVhFTXI1NmxCOVd3UU0rUnhMN2JyZVZxUElGMzNGUFcrZnJSWFpxM21EYXFsWENjQ3h6V0V0Mm9EUXhCelFrQ3lJakRnNElLMStvNHRsMDg1Wk5CTFkrYUhMRFd3d0FSdHZ1STNaZVhmOEEyTDJvclU2L2NIeGlURGRoZm1INmFJTHdXTEVBRnh4VWNUd1dwMnRVdzBReUdRdXVBeFo4N2NBb1lOVzBPQTM2NVdMTVVKU212dHhPWWIrVEdIRnBUTlBFRzV1MkNxOUFVdUM1VnNlVUtLeGR3ZS9UaVYwTEwxV0hjQU1Ea09Md3J1bHFFaktMakdUeHdkUUxDeW5qUWlleVY4WTlCYjJDdUxBcVFNQ0NnSEtpVUJFaldTamV1Y1VzaDllbFZRdFBjSU5BdTF2Y0JKV0I1UVZXZ0Z1NFBuTEYyWE9vcWtlbFFLTGovWENPWTc5dzNrdFM1VTdhNTcyZC93QWpBM0NoOTFjVXlvODJ3WU14UTNhaElGVkRQaVRVZGxCVmVtTHlmYzIrTVZ2aVhqRUZzS0Zzb0dta2JtZkh0ODVtWVZjVGpVM3cxbGVhU0EwdGZKM0ZXOTdMTzNiMHFLT0x4ZlMrWWUvM25DOFU4cXN2S3NTT3R0QUt1cDBPWmFLZGF0WWlyc3BGNnNoT0hRQWhMVzNEWElDaW1yUlZDcGV6aTM5QVNLRlZvSzFRV0VZNDFWQ0d3SGtLT0VxcHk2cWRJQVU4cExWU0NERkZLT3Z0T0hoMmVyN2llQmNPalA4QTBTbm0rNFExczhSbVNqKzQ1ZmdvWVJZZFdsTEkrTXFkS3lMRmwzdU9ieHY4dTh0UkEweHRZdWkyQTBBSjJKOThsYXk4c29hTnNNbHB1QnRwTDN1VzlMcUJGWnI2N1F2RWpodThjNTV3YkRxMEJPZ2E3ekc0N1BDM0wzWXZCeVlkWnovQ0YwU2x0TzRsQkJVSE44azV1ZUd4T0hWN01hTUJLSnNJa0ZHclRqcE1Sd3RQYWI0SVhxeG5QZnNkeGVaRzY1T1U2L2k2RFl6aTA1NXdJYUZXMTFDSnlUcUZ1d0RoTEkvQzBENnVYVDQ0SU4rbjZjd1FiZjY0QkZ3YkdyZFZoS0phckNKRVBSeFNrZjdGMnJpQ2lXaHl1b210WE10WjRUdHhjTFUwcEtSeEZVaGppUmZkdmhocmNkOWRWR2dhZE9SbXJiL2RsZUladGFWU21panNxTFhCZzZERVdxYXp0bGJ6ODdFeFUwSHF1TzRnWFVOOCtKUlhPYkhpQlNFOHEwanllUkpRdFplSCtTOWhHc0FGODFCTUQ2a0ZORkh3YWlkZHhMTnJvaXI3ZHgwMmI3RGdRR2lnc3FlVFMxeTVISmF3RU5nbU9keXEyYitlS2hvZk5NVXNaakg1VVJjRFFSTFhGbGgxY1JiMGVDSzQ2eHNGa29HL0JLcDI3UEFuRmN4T05qVHNxcUNEeXhZdjIxSzRQZ01WUjIweVpTNldtMFRjbmxrSDBHUlRFYTFHeDE0WW41TllSNFJhanU0cFFYNW9PVkZRWlVvVFFiZ3U5ZGxzcGRLeTRESDZLdzA1ZUw4Wm5nakFuS000SDF4RlAyRm5TZmxnMDNtdEdyeUFjSGxIeWJndjNXSmU1WDQ0K0FsT1ZaWnl5SjZtaTVTeXZxQnB4WFlVWGUzOHpmcmJ0cUdCMmcvZHRjcGFVS2VqSVBZeFd2Mkp6S2FORnNPREF0STE5TXhuK1I2bjU2Z1EvVzBXSG9VZ08wU0Y4KzVaTElhcUwwck45blppa29BN0NyaGRnQlQzV1J2WnJyNHVjalQ0M1ZidFEvMm5tbkZ0cVZHVkVUWmJVR3B1T0YvZzFwUnd1T1piTzBDUG1GaWs0TU1VTFZMdWpGNjVxTGV0VXZEOHhkc2VBV2ZaN2xaWVFnZFRsV05hc0E0MlFlVVFGNE1QNHh3c3ZNdGJxZHlsNEp2emZBUFdqRmpmbmdROW52cEUydkN3cmkycStKby9FdEtYbGN1SXV0QjZYY3hTSFFVUlBlWUFHbjNrLzhRQUtSRUFBd0FEQUFFREJBSUNBd0VBQUFBQUFRSURBQVFSRWhNaE1nVWlNVE1RRkNBakJoVkJKUC9hQUFnQkFnRUJCUUNqbG5kV2JOT1Ryc1UvU3Y0ZjkyRThXSDVyN0pIOWYrQUpHZS9UanR3cjhRTUhSL0NKNXNvOGplRkEwSW1CSTRLZnBRZEQvdHhuQXlKNmJmcmordjhBR2RIOEE4d1ozSFk4WWR5WiszeXdFNEd3c2ZRTXJ2RzVSbTE5a1hCSE1vM1pwOFc1Nm5WNTJPUzU1YkI1T1Y1cWcycFovYmdCL2RobjlxSUVicFppdUZPaGw4U0hrbWV0STU2ODFNOW5TQ0o5UjFFQStwY3pZK2YwNzdRUHhUNExsL3VJaWVlaWNnT1p0SDdFZ3BBMVZ4dFZlSFhVRDB6elVVUVBSL0ZQWW4zSVVEQ0pIQVpEQVk0dElBVi9icCtJSXloNmd5bnRYelRDNjhqOGZTMHJLOC9wK3ZUMS9wWEZPanNVcStoS2gyUHBYTlpkVzdCMC9pZ0dQY0F0WHVkenlHR3pER3Njb1I2a1QwcmpkOGZ3YWtzd21tRUp5U2tMS0hxMmVjS01ZNjJMNk0zb0pzeFdISmhKSVBZNVUrOWYyZngxTUlVNXdaUWRhQUF5WnhqN0g4MlBISlk0Q3laRStTeFJWZ2hJSlk0VC9zUGU1K0F4KzMveDhkR2FyZ3BoWXNNSjl5NHhBWDJtOFkwa29PTm4vdXdCNml1blhyUG1yd3lCb2tKQm14Z0FHQy95M09NdlFvSW42Sk9PZ2dvQzBENkVIei9yRDIyaExYRnE2VW5EcDV6SWJJbjdtdy9MWkk4ekNIVzA3dFhRSGpyT0JpS3hKUTVzNlZxblgwTmliWDE2dWs5VFlVbWJPVDVxSGV3VTBheUJjNWxmV1YvcnJ6YVczOVArbmEvMHBWQzVyK0FFMUFkdTV6cmJjM0Zic3lLaGVxZlRlS2R1dnZyTVNveGo3QmdjTGN6WllnQW5BZ00ySHNxbm5nK2NiQ09qL2tQMDdaM0NYMzc2Y3ZaSDZGaDBsK1lQbE1BYkZvZXFyUk1EclZEVXZ4bWtnVkFSblZBay9rblFNZVFkT2NLZkNtVCtCL0dIM0xKNUNYMGRkaUF1eWw3RXJyTTdGajNCOG1KUzZXb3JGUnM1cjZzamxUM05VandBeGg3VFRpZU9Ybzgwd0llUGt1bWVIMndBdTE2TkU2MVZDLzA2akRxMjhkU05UaHhmZHJPSjJOMHc3SzhPeVRiaGJOZVJTWTdqZDVIb25sMExybmVUYm5KZXlZeEdhLzd3UC9zdFd2WTcyOWRCdGJoeVZJK25UbkpqTi9XclYxMVVRaWMxSGY4QWMzMmpXbzdKNmtnV3JQaVZRQTFrTXZYeFQzT2M3SnZaWWtDWlplRnVtYzdGakVqYXNOWGxvejhFbEI4bnFTazFSd1MvRzMwdzkrTjBuWWl3b0lQV1pEcHJ5aTc0ZlpaVjlYTFJOTUNGVy9xdWlLUHNvUHRYOFRBN0g1UlE5SWMzb0tESTYxaTFybUY5YmN2ZHJaSWUyNlF1dkl0UXQ5bU9GYXVtalRUYk14TWY2aDMxTWhPY3BEYVJzaEJKN08wakhHSjVUMldmajRxdzdNKyt1ZXpPbWs5aXlYU2pxT1drV3RySjRWdCtOY0RuMUJldzF3L2taak5LQ1VZVUtaWlJkRHdMTmtLU1pYajZBUXJWbXBTenVHL05QZ254QU9TeldQdGRXS1YxS1Vlc3lxeVBNOUVpdXdwVTYzeCtva2pYMUZMczgvRVNzMFNnRG9FUVlWOW1WWnpod3F3bXA4bzUxQ0JxVTQranNPdE5QWWdpTUFKa2MxOEI5bmJpeTJsT1U5VmpPTkd6WkhCcmZDOG8xMTlaZExXeWcxQS9OSG85aC9CQjczd0RzWFBNWWRYdVVFZ3hFV1ZaNnE0cGpnZE9lb3E0OWp4OXA2VVkycWRPYnpTL3VzUGg0STZMcnhVdEdUa2E4Ui9pUjFlWXN5ci9BTU9pMFVvODg5d1E0TEE0VzRMT1FmL0VBQzBSQUFJQkF3RUdCZ01CQUFNQUFBQUFBQUFCRVFJUUlURURFaUJCVVhFaU1tR0JvYkVUa2RIQkJCVHcvOW9BQ0FFQ0FRWS9BSFU5YktwLyt3eGRyTHRaakcrSnhkOE1jdVpWdEhoRDJqMG43R25aV1hhN0h4Tzd2bXozZXVlM0lWTkNsWTdkV3lwVnN6bUNWZlBRMXRWRm80SkdxYndhWDhhbGtVVW1GOEZhS2wyNEZTYW1vN1pacWVZeFVQSTJuTjVIbkJsa3VvODQ1cit6emZES3U1Vkhwd0p2b2FtbzJKVjdTSDBIUlh0RW11VW1kcXYyL3dDQzJleHJUcWZjZEcwMmlUV3V2OEkvSjkvdzNkalZMSVYyb05MdEl5eXJ1T0RXekZUd1M5RU9xcWhOdm5CNUYraWFLVW4yUTZxcVZMOUR5TDlINU5sU2svUmNENEhKZ2ZBeFIwdExSaENhNWp6WlU4aDNidTBqSzRkeWRXT2xTVE4xMklrYWxFa01jMm02Z2FGekpaRE1vd29QTVR0Tm9sSnVyYUptcVBDSldZbDZHOUdTcXZHU21qbVpadk5TWXBNWVBGcDNGdTJlVklodEhpVjVUd1VLdDh6eHJkYmh5dnFleEtjajN1ZDJLcGFRWlRaNEVVSmtGSit4RGZZOXltT2xrTkdocGVqOGEwMVArdjhBOGpLVUw5RUpRTndTMUZtWTFqL2JOdEtCWVNGSW9FS1NlZUJkeFBvbjlqRndzWlZ0YXBlY0pjeUVOU3YwZWFWWmtweGc4VGtkTFg5WkxVUlpDRjdrSHVVcFBXZnV5ZXFIQXJ3dVorSFpmSE5pcGJoUjhtakhOUHdiOGVHTE04WE5Xd0xQT3lQY1h1ZTMrbnVVKy8zWlE3S3pLZTZLcDZzaWszcUVvOWNmWkZXN0IrT2hyVFNiTVZTWGhqVWpVbUJkL3dEUmlraVJSVWlHMSt6VkMzSHJOb1F4Wk5iSjAwc2RVWWFITGZ3WklTK0IxcFpnZHFWeWtnOUQ4cVhNM3FWcVBxa0t2a053T0ZaMGpxcnhaMnphU3RiOHJwMEhHbnQxSmJ3TlVVNkRwcXBoREVVOXh4eU4wcHA2RzZ4cDZzaEVQOWoydGVlU1JGZENnZE9xaVVRaERNa1hyMnllYW94MkdtL2twb25QSWRPdnVOT0o3c2RxV09IeU41amI1RUV0WlEzWHFoVjBqMmRmZEUxdkE5cFQwaEc2N08yTFFlRWRibVg2Rkc2L0VodHFIMVp2cXBhNnlPMU1GVWREemZCTkluVXNtaEJFRGNHVFFjVWt4TDdqUyt5YWxnaDJSbTNoVXQvWnY3UllKVFRuWDBHcktuYXpIb09taVhQVWROVXljeU9HYjdwRlVtNjZXMTZua01MZzhHRDhiYzh4cXJVcWRrcTFKS1dTV2lWVHd1eW0rN1ZsRVRoNkhpWjRkT0QveEFBbkVRQURBQU1BQVFNRUF3RUJBUUFBQUFBQkFnTUFCQkVTQlNFeEV4UWlJeEF5TXdaQlF2L2FBQWdCQXdFQkJRQlpvaStuMWhJZXBiV3BUUlQvQUZPYy9Ya3hsZmlIdWJEbE03blFBR0J3NXdjWEVIUm5SL0RPTUxjWCtvMTZ6TTdWRmhJL2xQOEEwT2ZFOFJUbE1ndjUyNlgrbm5nYzhEZ1E1enhVRHVJQmc0cEtFbjZSNTREZ1E0Z0gxRWFRcHIrWVcyczBETW55bXZIT0h2Z0ErS0xZL2Vhdy9ZK3ZZdDlyWTROTFpPRFEyY0duWTVmV3JCVVBRcHdIdUNGNkQ3UFpBbnEyY2ZZN2lsdEhZT0RSSkdzZUo2alQ2aFFmblA1T1NQZ1Bycmd1RGxUNUhUSGFQdFVHZmVOaWI5T0p2dnhkbnJiOVRzcUFRQmlaQmlpTFp5cTEyUUMyMGNJMmpqUTNXeVBSSGFKWkUrVUhEM29BSlR3T0tqQTB3VjNaTk03K3lnMVBWT0dPN0NjTlhmc2llbmVzc050ZGpXSEIvQzVEUm8wWmFoUUtuaU9jeU1wbEpSbGtVUDBySUFxODhobi9BTXA3SjVuQXo5YzlOS21jSnZlYUI5blBwMG9zL09TcTFrRldlemtZbzdpRHVhSVAyNEdjT2R5WllLaEl5SjVQWlBXNGVybnhOT2VDOHp4Vjhxb1ZuY20zQVFxQVkzdE9ZNnVGVnpnNE1UMkdyZVV0U2RaMVh1QVpKZjFxbnNlSnFjZWlkSVpjK1VsL20wYStFOWUzZGdFVzhKdlp5RkNNVG56aUh4SFFRcGJxbm1GaDVmVUdUbzFTanZObytxN2NnUFd3QnIrc2JHMW10UGV2SXE0bFZXR0hCN1lEK3VIc2k3bXo0UTlaMFo2dnFORWZjZ2NaazRwSE5UYmxETm4xQ0ZVaHRTUm4zTlpsRkVSRldiR2FRSkU1UWZtY3pXKzJlUDhBekV0bExlbVc5VTI5dDNkemRLRTkvSEIzdzFtUXBxUUZxVVZOZXZxUFh6U240cnNxTy84QWsvbnhJQ3FUbWtQZkhKRlo5NzVUSitvbUJ4aU1BZjhBay9WdFRSbE8rcERhcDdzbnk0Q2tZUGl4L1JDNGtWb0xpOFNKdzZFcXhaL2ZpaGpsRUtNb0pFcStEamdEL3dDa3orVGM2ZzRlbkZIQk9qSXkrc1BIWitncEU0Y2Fxb3BHRCtwQWVieVJsVi90OHRzMVhKamcydjc5OWd3Qm8zNWcrMnZOSGZnQUxnc2k4Tk9oMTc0alA2cnJhNXVkclV1aEd6QTRMdzdlc2xZWjhCSk5TWTEzSVRVYzRtbUZnUGJObHd6OEhFOGN0dzBIeHJPRWIvem5hVC9zNmtzTVJUbE9mU2dSOXZyVThHdDZWRFdyOWpBWmJYdWpML0dqdHBKVzI2MUJhellFSjFsOTgyWktyaVZTRWxYcGxRbFlXSTE0aDM0QXZseWtoMHVwODFWc1FjRFBGUlBaQTF0YXU0S2EyNVZHYmYyRUd6NmpmWWpJOVZ2alc5ckw4cU9EMCs4akU3RTVVRHBTOUtvb0hYeWt2cGlOQkxPOVg3cFdZKzdSL3NXNFNTY3JsbS9HSlU2c2pQdGRxU0pHSTJkYlkxb3hTSjluOWhwanRqeVpRK2VRSlNHMnlPMmdLbW5EWThNeFozcFE2cnJsTDBmVjFDbk1qOHQvYkh6WUFGTmZjYW1yQm90T1JKYUxxa3Rpbm5LT1YrTkEvdHI0ZUtQd2J0M21ETVVHc1RCcG96UGFWSlZvckxVWGR3WktzbzY4MFoxSGpISDlseWh6WVVGOUI1cFdGdFFKS2dkblQ4UllHV3E0ZExIUFR4KzdaUGdFb3paU0MzVjJaS2VkRGlzeXNyTmF0d3lNaHJSUlBZQUFwMDdNems5cVNHZTlDMzhVOXMyY1pPNGdLaXVvMlNFVlY2cU0wejBXK2RhbHAydWR2WXlUYmhUNm05d09SbmtjQnhTT2M4c2pQd3dOd0lUaWN4RFRpbW9ZMjJUaHBiUE55Q081T1l5ZW1rcHA5R0s3ZFVxZFhnRi9rRjBMVnZpVnJQQmF4em5QNUdBOE02Y3hxZVUrOHlZR0t6SWVwbm1yTDRFSndEQW95RXcyZi8vRUFEQVJBQUlDQVFJRUJRTURCUUVCQUFBQUFBQUJBaEVoRURFREVsRnhJQ0pCWWFFVGdaRXlzZEVFTThIaDhEQnkvOW9BQ0FFREFRWS9BT1dEdExZbjlUMkpRaHZpc0xyMzZEOEdkS0hyYTBXajhPTjlGRmJpaFdhRTB0dEg0cUgvQU9ONlllbFBwZ2JtNkZ5ckowc3BqMHdiYUs5OUcxcFNNb3BIbjhDYVpiTUdIOGo1bnBDWFlpMWpSNk9Sc2JNdlNsRTJQMG42UmVYSWsxVmVCVXVoaUJVZUZmNE04TDVSNWVGOG8vcy9LSTEwRmVqMGFSc1pXajVPSGNlcHo4UGh0cCt0R2VFL2orUjhUamNOcUs3ZnlMaWNQaHRwNVdZL3lXdUY4eC9rditvaTRyN0QxakpQZEptK3R5M28ydjhBQXV4blZEZW1kSFR5emxqeEdrdmMvdVA4alVwTnIzYk9WU2RkMlltMTkyZlQ0cmJYcGJmZ2oyWGdWRnNYZ1F4MlVtTk4yTlBaRzJqYUUvQTlJT1Q5RVhCM3JuVG5yS1J6T2lxMFF6bXAvZ3ZsZGRtTkZwbURKUmt3UG0wWlJTTU9tVTNhOXo5SHljdkI0RGsxMHljMCtFNHZvMFZUTGxveERmdWZUVGRkQ1BDbGRyMkp6OUd6WWNJeXBtWnNxVHM4cUh6RFQ3Yk0yZGRoM2d5ZVZqYjBhbkh6ZFRpUzRLZXkvY2ZHNGI1MUJPTFRmdnZYY3BxdndWSDB5Wi83SXhFbyt0bEtTWGQwTlRhZmJLSnVPdzJ5VEg5aDl5dlprcjZJbXZmUnJUTDF0SEVYRWVYUStOL1RPdWE3Uzk3ZlU1bTcwcS8rdlJEdnJweXAwTkoyTmRDVmordzY2bEwzSDJKTHExcExSbDY0WkdFWmN0TE1uc3NQb05zdW4rVENyYlJGTlhrd3ErUlBmOWtWRjNla2hqK3cyTit4SzF0V2pTM01qUm5Ybmwvb2JpcnRtNk1OZmtjRTgydGJqMTFsajA2YVNHUyt3KzU5aWYyMGxvOVpkaGRoOHh5Y1JPL2FuK3hjSXk5c0M0azROWnk2MWxHL05ldzlrYmovQVBuU1RTSGo0RytWbHBQOERWUFlsenJvVWhqWTZOaWg4MGtocGJseFMrVHl4TGY3aTRjbTZiV2pKTkZudXlYQmJ2RkRqSm1NcXh4S1RNc2Q1RXlvWkc5SzFhT2JselcvVVhYN2pRdWVWV0p4bmJ0YU1taGU0NU00a242amFGSmJMY2I5QzQ3SDBvT3ZWc3VFbllwTERUcGpUMWVyb1hBYXhGT24zRktLK0NYRXJEd3kvd0RCYXV1eTFtSzl5bHNSVWZVeVVuaGloRE43RDRVMVRXK1Q2a096S2dzbjA1UE83TFYva3ZSNnNmTzZWRktXTzV4TFhsZnFMTnJvc2ppNHVsaXEvd0FpYUtKa2I2bjZma3FZNHhlRGNzY3JvcTdQSzlodXpNaW02UDhBUnl4dSsybDZZMHk2Vi9BK0h3bmthYWFyYjNMNmpKUGg3bHpwVVdxSHNQMzhGRkVucGRubE9aTlgzTnpmd05UOHg5Vkt2U2pBaGpsRjBVNUdKRFRsNGNhU3JXNDRaZkwzSHlvOHl5VnIvOWs9IiBpZD0iZSIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiLz48L2RlZnM+PC9zdmc+", + "description": "Displays efficiency data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'efficiency', label: 'Efficiency', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: '%', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'efficiency', '%', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Efficiency\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 30;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"52px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":60,\"color\":\"#F89E0D\"},{\"from\":60,\"to\":80,\"color\":\"#3B911C\"},{\"from\":80,\"to\":null,\"color\":\"#2B54CE\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 30;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#D12730\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0,0,0,0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#198038\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:ZWZmaWNpZW5jeV9jaGFydF9jYXJkX2JhY2tncm91bmQucG5n:ZWZmaWNpZW5jeV9jaGFydF9jYXJkX2JhY2tncm91bmQucG5n;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAU8AAAELCAMAAABu2vS6AAADAFBMVEXEysN2dnFzdHB4eHO3ubLCycFsbWu0tq9xcm6ppZl+e3ZbYWGclooiNTrDxr9ub21qa2mrqJwfMTdgZWQlOD2moZSDgXuYk4e6vLQcLjQZKzFdY2OBf3nFzMeemY2/w71GUlTAxr+po5Y9S09YX2BRWlu7v7dlaGYNGyJATVCMjISFg31/fnjBxLx7e3ZTXF3T1c45SE29wbqzsaZPWVpDUFIoO0AXKC5WXV+/nIfV2NBJVFVNV1ixtK4QICZwcG1LVlewsquhm44zQ0mKiYJoambP0sqwr6OvsKg2RkvIzsjHy8Smo5e9moWPjoeknpETJCsHERgwQUcuP0QrPUPS1Muuq557eXOUj4NnammioJRiZ2aIh4CgnZHK0MytraMLFh2rqp+6u7GTk4uHhX+xraC5uKzCn4fN08+QjICRkYgFDBKuppesnY20tKiztKunnY0rOTw2QkM4Rki/wbfKyr6yqpvO1dOVlY5kZ2XOz8fHx7qYmJEuPECPiHy4tai1saPh6uuGg3eyj3w8SUqLhXknNTitopKjpJ7Al35ub2qlp6HFzczX2tOrraeoqqTAv7O2u7ednpegoZukmYibm5S2kn6Df3R1dGtYX1zV3969k3nI0tEQEBTBycemi3lET09AS0zP2Neek4PR29rOzsEWEhSklIO6wL6xoI+rmYenj32sr6q0rp/S0cXExLagjn15d2+4jHIXGRy9xcOAfHBgYlxkZmAdIiSqlIKZjn7Fm4GLiH0eFBS1ppVsbWYWICOzt7OynIq9u60iLjA9RUNIS0aUi3zLzcO5loFSWFOujXlZXFY0Pj26nYja5OWMgXLCvKyzhmx1b2NtaF40NjM5HRmpgGqyl4SThnWriHRTU0ouLiydfmkkKCiIe2ukc1djXFIqHBmdiXV8dmlrYlZ2Z1g+PTiTeGO7sZ99bl9MU0+neWO+t6ZGQjxfVUlJKSDFgFSEdWa3ckxZNipTS0KQblqfZUqRY0xxWErc39g+Mi1jRTl9SzbDi2VuIR0jDTATAADUc0lEQVR42nSbC0wcVRSGFygLiwiszRZUWIQomCJoQKNRK0rUJZSAj7hSFBAqBEiFahBQCZpGrKn4oNZUCVaFqvjW0CoS3y+MTwylESEQFCQ2EY2piUZN/M65dxg0+u/cO7Pj+vr8zz3nnhk9HR3vPlQa3Bus9NfU1IRrUK1/oih5PrBYNP3Lkd+PfD+2MBH/yClJkd4TTz/9nJNPXJ/tLYw4bWPZxg1xr0RERsaKCgu9salBX+2mdSnr1q1LWSO+cCcmZtOmi4qLi/kb1IbDBa0x67Ly89M50rNQyrqYlvpwfUtdSlZ+OspNyMhIz8jI8BhFR+uEODPsxJwgp4QEhipXRlu6p7oDNZ577rlnnMF071qdewY3zr3qjKtuvfXJJ2+++baXHni/PVgUREVFnFB8fHwyg0lnV9wwt7hZUVHR1N3d3YTk1L0yOsm5Z3Jq9pCnY/DdqtLgjvgiX1gkPH0//fnnz3/+8efRn3/++ah8js6e5I1NOuYcdMz6S5IKI1I3ll2cExcVERERC9LCRK838TTDE4KqPrmCLQPBs/Uiw9MfDsVwH6DwzMhXnuta7gjXF8RwU4AmZCDltznakoSZ3pABRi6UKpcwZDY8c3XyVDc2doASnEh5OlC5JX/gqksvVZ4AbXv/BUAWiZQlA7nkECd7ycxdF2gTk2jv3Nxew3PqkGeg490qX3BHcnk4FIInBvL7fj169PDRb38+fPjnwxDlmD0rKTLpxJPx5zHrt3gLozY4PMWfu2K93ku8wvMihZdiDgPUwYlBwVlb6/fXFsTwBwUdygdn1rqY28WfChl7Zqgg51BjVmXAEcgJcI7eDEwLVCdH+BOejZe6PEX4VLGam1dddemtL8Lzttty295vtzxdg0LV5Wr4MSxbNTBAm5BSRZMjyz3dPT09k8tTk57egSxfafzeZF/B7QUSjfwrB5bFn4ePop//PPrX0T//mjrPG5F4zDknw/PEbG8s/mzeuOG0q4l3gBYmij9Tg6W1F4nzFKnKwozhuOgix55+f0h5ZgFPA57ftTjxLkBzcw1Ra0gxY4bHhrtyRObaY1ES6Trz3yK9LTe6pLrj0ksVmxJ1Q94sAdy96kVwSrzfJjyDLk+hxeSKa6XpGlQ+xqEGJ/po5NVueBp/xqSl11QG4RkO1fr8APX7M1eOYsxviXZ1KP6cejwpatd6wl39GRuVem3ZxpzTro4CqBKVeI/PrC2Gp0qt6fAUdxqeYeHJ+mn9OZyev8qz3fhTeRLxsn5aP0LOkjXarJ50Fk9XuaL0rPToEuwJMyu1qONSJao8XzQ8H4DnWncKQJ0YRopPJySzXV9FAIXow3OjewVsz6Hl5Z88B+9fV1sZzKko9/n9pX7CUXj+fPjbwxLvwpOTrJ9R3hPVntcQ77J+brz+NMUZy0j0JsIzYHgCi6EfpSk8oWl4+h2eKfnQHM4flvWT5bO+vd2Jd8BoQoo2EW8m5NDcLCg3y60EDjfW1aG5bW0JJdXVl14KR0VK7hGga3jyRy61PG+G5wtuuBe58b5WxpgiN0NZpsKz56ORFVKTrJ+//rri+fNI2F8Vv6OpPODz+3w+AVr65+94UuLd5qPfp85KvNp7jPJcn4Q/c5qbLU8FSsTvgqe/gNhWopysuLMpBp7QDEm4EwKh1nVqUNUqTxvv6Qgyak8b8khzvX5RomJRg5nJyUY6GZ7ghCOT8ERuOgIoEX8VNK0/4YnKHaRiTyY7G7YOzwqGlTWs5qVDoyOTTcK1Z2lqas7zBzyDyQebyn2YB54oAM+jf/78158Q/evoX3/+9dvUWUkR3vXHCM5rtiTFRlzfrP5UnIUQLdyVmBof8BVvihE5ke6GezGScswvPI0/tWLCoVouhcJOvGNPPpCEpqt/LKCb5eRKaSpTBZqfkAZQBE6xqZRIFigwFamEu7oTnj++UFRVZZEafxqaSL4anm6oM21NTu50sIJzcfmp5W7jz5nRkTnP4SWfL77pYE9VGGeqP33lKyyeR836iUNhO3vWlohd68F54om6fl5b1lyWgz9l8RSDxu4i3st9odZNm4w1me3FJodniOwu/gyb/C7swGl5+rfB0+YjqGBO5bdqUIvTFkzmplOeuiUoMv4EpEW6Jsnb7I5zDU54vgRPBQlPZjfBW3EFTzfkzdy8BmfPT7NzrzYpzr0rj47+6vn212Bmcs/BnqC40/AM/HH422+/fZMhOswxexb5/cSTT4anrJ9R1zc3l204Le9qCiZwMhIT44j3Yl0t+SCMaWai3fCsVZ619SHHn8Mcko5aQ/Xt2+pvr7NFFMsgOA1RB6SZ7XeXpyjDGnQtz1tdnuJIZHgahxLu/+QJzXI9iSkVJpNly7A8LUMZzas8l+ZGVvZqIbrj0OxTKw97Dv8UDCS/cXBHfHkgUFpaKlkp8MfRw3/8cXjlj8OHZ/84fPTw7PLn5yVG7VKe629JEn9u3Yo/4+IAait68ae/uHVTTCsfAarWtPZk9byvJgRNUT3xbgyaJfYELeEOz4aULIDK9igdnu7yyZCPi3UVpvMHsCdZyvLMgmcJ9ZIyVaBoTYIXh1LMW5xfKc8gPJHhyccCZXJ46mS0lQ8yi+eO5ZGRQxLuZPepkblDPZ7JitJgctMrPcnxRQFwSlIqXRhamJ5fGvq+Z+n7I0cyF3ZURZ7ijSIfSXrPThJ/PtS1MSc1Lu/qqCgtmCK8sfAsrWkl82xSg3Ko+IY7BWhxTa2GQLiYeNfYVofiz4JQeNu2cENdluYjyiUhie1kdopQhWtvODgNakhyyZxreOr6qSzZVYKTNLQ2wWved3h+pTyxZ0B5BpEGf1URPB2tzfSGZXNzhSHatDD61MoOzUaTs48+OrXY7dnnebApuenhHfFVpaUBsaffV9rdtje3rrSuqCE5WBu69e7chLJHCiN2Sb2EPyUfbShr3og/o0SREVKAJuaxfgpPhQlGTjprKa+CJ+b31RTEtFqcTLJ8mnhvMPv3BGj+o5xXQc/IshWcLlcZsORPTchPyS0pqQanye9nKEw9LE9uan53/HmH2LK8sjJQiarWKhgkU611q1Enw4n3SfHkeBPxvjj16KOPruzt9vRG764oqphkw+kz4Y7uGTzQMZjWkVudkZGffm5aY0nzyYlR7DdNvZRYGLlB+iHCE4PqnjMRnuR3BYhWz2b5BKhEfI1f8ntNcavyBCgT8R7TUrCGpyZ3RWrpMQDmgHS+unHPMNSlyKIhkEH9WY09z1Ceuj+S/O5mJMVJRoLnS/gzUI49SxE8FSmDGQVlWAE32CWf+K6urnibknpWRkdWDsrGs2dhZGR05KOebk9aWmVFVfJkd3JRaQB/SkFT07d///7+wf79qKOjn6nz5KTI7GOkHSL5PZH+0sZrU+Pi8qKuZo8EUPUn+UgJMpxcZOt5tag1aC08JfULUMWp3aVtyhOc+eR2F53mJaej5GzgGe6sk9ZQxrL5WVLPE+eXqjCoBLy1p6Z3yijdbro8Kb19PgcoEqLIIWqZmjM0HwracrTptbmR2cke2SXtnXp0amX0s2786cGfyYvdycFAeSXrp/DMF577+xEXjMHd53gjslk/xZ/ZSbsiT7u47PrUOLxJvANUdvCS30P4UjAytFwy17o94rD9kBrp12FQxOq5yjOs/sSeJpwZ1o0ojQ8yhN1It9z1B5vT0rQuzRV/yn7TZiMJeI135O42Dc+XlGd5Ff/eDlAOBkANUlcPBZWmTA9V2eS0d2Vk9LOeJuH58MjITzOzS8IzrRLcBw+OjRXx11WcNcOK8ct+JSrjwVO8sbdYnrdkJ8bGXVt28YY8iiWAij3p2OVtrfIVC0lkWKrUrrBE9wE0DE/thyjPYRwKzwJn/dQWnsgJcOM+BQpRrlXaymPiNhcWOJKr3JRceAKTg2H7IQwmBF7C3WmHGJ74k2XOEDU8XZsK3CpzIMvYLqUVn7wz8tHeCsG5Y+apqcWZqUnhGV0Fz8Uj09PjRYZn7VqenPb3488tiVuOIdzVn4mxeayfqXmUnhEInBg0NUi8K04ms0NyC3rs+bztJ+NPUy+pLE/8eQf2ZPXUcHdQ6hCUfJhVXMHV8tSvOtJKDPOMrNy0EniCzeJEbjYSmIy1PANI07D1qByWqTWqObuLADy7wHmQGJ8clyZT0+Ls6MziyvJkT4+nNz2IdRe/n5+fCFaVCs77a9ItT2jq6Nh9SlLsJezfjwFodhLFUZn6MzYCoJKP2MHHKU/4MUQWqbuAXiQZiXh3efZlDYPTbI+2FcDTZCO3oSQ+FFrAAhWHGZu5CWflx2xUYrjmwhN/WpmE5EpK+RfBqUAdnpWkDQcnCgg5dw21VN3vCpSA37swOjIzMT7eNN7dvfTU3MKhqeVFeKblBlkBJr9fWpqPx58S7jXp/YNr/DkIz9OVJzi1oCfeyUdSzEvIx6JE+smlta0OSZiCzHEoHl2zfoYMzxTN75ZnWHhqsSQ8zQ6IQyTwFJuLT+8xiS0Rt0t0lon1U3lare6P3FhnIJuPHtB4r/SpVlN8UdCKmj0oOclN/AwFCs+Zd0aXpsdR98TyyPKhhbnlxYkJ8lF5RXf34pGlhV+SKwFai0GH9w/2k9/t+jnQP/DQ6d5IeAKUeN/C+lm2lfxObicZ2XpJ+8nrXLk81aDU9LafXKP7TbSa3wvq8WeoBXtKs073jhBVqG4+hyIYmS1lzhwMRCoCrvJOoJ5vNOuntScY+SCuhCUHknAHqOUZCFQS58zl5cBSXNqcG5+eGI+nYWLdaS+07I/v/mR09hA8+c2hueXJ+ZmRlV/m5z29Jf4di68sHlxcnBiqOC11Q861G7cOA1NDndQ+2F8Nz1O2xG458WTE8sn6eX3n1os3xAEToAR74S4vPH21JG6XJrI4EeGuPMP4s1j8mWV4yo9aC8LbMrfVh+rygamSk4dhwtrukmCmALVHxxcnObmEBXFCW4I88AAmMl1QQGpRjzuR0nTqJfp1P2wvF8GS2XZFoKk8K7on5il8uEuYU9uvxrv8qmnm0blJeAJ0eWRhHI/++suR7z0dJRtnpkQfTYy/Onr22WefcNwjB77s/5JDl87B/QP9vbtPz94l9Sc7+OwtSYWROWVlOXF56k0+ZPckb2qVX+PdBSoH4lFcays0DU7q+YuEZz48kW2HwLOgAZ5K0+m+c4aRFusyW26mX8dsZDA7SMnvbbnwdBdQESDVoIYmY7X9qTzZXbLjNOJKcRLoyUWWK/is1KGWZ3zFwsjc5PjY2Nj4xMhTK/OHRud+OrL0q2ewumtmZmVqdG55YnzhnXfOfmLLlVv29aMvkRBFHXefnlRI/QlQ4j17l+T3a3l85Dwujk2Ep/jTxenGOjBbbbSHa1mlaqWeT1EpTniGhSflUoLL0/Gf0kKWmJWCVilPc1YlGJ6NliYONU1la09oqlyed2zXzaTbYsKX1EDjFfES+nJDDlf8ivVVqL8yOzc5JsS7R2jULY/M/r6wPOdh8zMz86vwHB979Z1Tz/5kU2RE+mC/qwFS0z3nb4lVfx6jz4tj82T9lMdHijORcl55XiQE3dSOaDbJDr71ogLan9KeL/XXtOpPrOQntJcy/XfQD7E0HW8qTCaFpnf0WCs1come+caXhJTczSWK81Y+LKKwZD7DFvMv/gMoGf6rH/bEI0OMwDbmlCSTXBngq/ANGqicjVU5q4tfnf11Qn+9MoJG//hjdGTEMzDQ9dns3AejcytjQ4tT1516V+Tjef/gSW4afPD87MRs7CkbeKk/T9N8pP7Ux3HwzKkK+OH572IJCcxQGBHs1Lfhllb+qEWqyAvC7eT32+vazPrJ5HBdjem10htuvLOWuivE5oSsXA88G3nCDlB6ds5DJJHUSy5O7YjgT4iJFCjXEBrvRhVkKL3BhISrMXK8o4rPRmc/W6Rkmp8aoRvyFMeIZ/hA1IcfvDM6OvfR0ND0R9ehyxL7egc6OgY7BkVSiXY8eGZ20i0nn3PK6YZnIfXntal5mto15nm+uSGoPO020+K09iwOherr63Engqf5oy5PtpuWp8rh6VLjbCLcucdwvauGtkypl9I9bOAb8acIe9pGk3kg77gToDbBE+97hhyHYjrF2TMxMVFBl8T0PmEpHyN+Mj1t23VsOB+dW1lYmvwJnqoRz4GBxBtnp5aXP1raMzT+2amXw/PxTQfe7d2UduDdd5/vu2fTPc/3bb34vGwvPE+XgM/2eiPj6Ndt0O684CQfJRamCk9L08EKK1RQEIKnPNjPzMz0+UO0lxyg6uAC5Vl/R4PD0zTbIeUKkEid6EKWmxapSAvWBMNT4v1j5QlMWzaZJdT1J4Ln7duHhsaGwCRxrDjHiHaAxvM2QlDAGWOyapLiOQfHfvllnN9MLE5OLswKw9HlZYfnO56BwQebu7qqKn2Z24emJ+869YLjrzivr6OvI32gevjAu/vu77v/Ps/FGy+8BX+ejj3JR5d4I09r7tx6vfgTkY5iKfE3BGl/rnsXiq6ABc+WFoCSjbS1ij+h7q4IIDc87yhoyFWPgaqkZPPHJSXSdyspubXk1mr4yNdLq5kQ5uOOHPwh+d7IpHf4E8hHbOCtP80WSYkC1OUp7ToKJuWZuWdseoiVCGUGpHCiHJWyKcDOxkcRRa2k+xz8UFtJqd5z8PulQ98vLazMzs3Njb4zOseqOTdKsItk/Xzw4g08D9oaeiB58uFHnjjzvAtPHu4/sJ+ATxtIG/T07osevD/q/FsuWU+4U9FTz/O+TXNn57V5Ui3JwKBJhdf6G1LS5TFFbgZKR5y4sN/0wRu1Ecso9adb70M8RDm/cxs0owWl5djIK12WExQRjO2J/aZOafY+nSU+9toDTyEMTgWKQxUqMCmYaNVZmeVTeN4hKmjRKNJAQtANFAlj7TmVCsu77/bfXdn88MLMRwJydPQdLCnenJ2dmpNLl2fZ+jvX79j7xu115Qc/++SJm449+7zh/jSp5GkkD6ZVp6Xtv6/z9Oyk9YQ7OG+5xEt/vrOrbIPjTxPwp3VVmufFIHo35l2u1J3ujl6+knp48UtuuinrIsol4ZmVn+tEuHhRWCpZ5clAlpmd+J2cgevhbCuojBRbL+FrI3jeCk+AGnvK0Gi3POtDBaKQFf+AcIRnlY8nacA0beHSyviKN16dHRGAIJzFlZaiK7njGejdff2G64OV9S+1hYJNpxVect5xZw739wrP6o5qPNrb23/P1nO2eK85Wft18KQ/v7Vzo7xug4RpouSjKm2H9GX19TGy+vSFMLnmC/dS+vreZTEoDvvrW2Icngpby8+dmS11AGURNFuhNLfchJTLWVlayR1L2L3vyZd6Cd3K+qlJHsHzDOdpMbr0XzxDBS2oQKVE6XoDUHtOXKKa++VGcAf557/01FNaMIk8A40xteFQy7q2hOi6kK8y2Jx63jXD/QPw3E+K39/bUV3df3/XKVJ/HqPP37O9kRE5XZ0XU3/mKU9tzxduqKT8VH75QpBaHYxcc2jjQ4j2rWvx++qp5539KGSV57btmTzeBCcs3fJcQ1r35lwS0iquJL6Vu/PQiFWGIdJ6Hp7YG5hXacV0q/UnRFlB5WPfXlKeDfU8brEoC1jKwVnrR/CsJdwJehFt0GDyjtemnlprx7nZ5ZXldx59dHZhYWlp6dDSwk+/LhPv+XUpbVL0kUVzH2gI7d5wfTQ9kH7ansp0oKP/3YfO8dJfAqe2lyLzcjo76ddRz5t+XSLxnnN3cUwWyyTLJTMH4mxXT4RlZf2kXqKIgqURracaPzwDLXUPtEluByRYhKssjNZ1EBW0GtamABWC/Dqadx/oSlES2PyeQX6HJ6pWbzKZNpOVOtRuOA3PO26/vWVVGvb1IMWPMGVgTgxaWpX8xsMfzRLkVgJWLYlj55bR1NSsLALwzKpjzQEn/5gAjanterAEY6rgKR2RA7tPlvc/j0Hy+CgyKqfzoY30QwBqwl14VtYWE+4S3CquxaNKUqS3YlrCxILxp1OhFtQQ79u3h/X9pTZhw7hZ7Ak9IwlpllFmhoC2hRL1ADMTKIHPkHopOs3wVJwCU3L8mrcZ0Fqe4XpqOQEoeUgksyygvtJAOf0PFtAqzNn9yqHPZpaXV1Z+5a2vleWpWUDqamo1RdHJH/MMVGdRSPNvoTjb6tYVV1VlHdi3L+3Avt7qgYGBQdTbeY6X973t+0u8z3B9Vxf9eXiiyAh9AzSntGZTn/IbHuatOYbSxJzDw/ka+yl90OP15OI1PNk8hZTnzvqWBlRXV8e2vi0rt02VhWXtpXMnhZ8wVA0NDGbODKu26M2SkDrcnojY07xSqyW9fd3biff2bX7ky/RxFo6ctzGXF1E5BYNS5w/R9KAkPTh56KMpaiSBB1jIziwdmZg2GqczMkQly/vJ+eskE9wmtnip7f2GgsqqjH3DvcMH0oejDxzo7R0cGOzdeAo81x8jOG9huxmV89BDZcrTrp9sma73tazLl7cM3TIJSQEl73ki7R0X+Ogn2zfGbMFPeIk/KUCJt9XQK7hDVVDQcjsVjeoF1G61zZ7xVD3vOvIHHN2Rog07qi1sqQmJs7IEKTSV54vCU/35UsPO7VZ7tu9BT4uEzFi89kaG0Msvvzz28hjUJuaPfI+OHPnll/mJ6bGX9cd7nt7j6OkheEbjojr0QN37779/++0N/tLewYyB3mqOfb0HDuwbPtCXdybrp/C8htcZEiPxZyfxHqGK1P8foTA1OUjEFz9/333F6PnimmKeszPTlJe2vEaUjxDyhVg+na0Rk5Sf8NwztGenoVTfHq6/Ixx+4YV6KEGKc/sL7fXM/9Bb7PlV7S8A8QW5gfgT6hI2a/Xq+PNW+8qNSPKR9kdMQnoJnu9vH3oaiDLAZvXdd9PTY3t8mUXlIHrZ+QPPPffc668/rbIkn2W4LEWsn7vjS8PxO3yBYM/3R9DifDC6I6O6sRpjko2ol3rfLaO/dAnr53petyHc867vfKhZ+sk23ndh0NN4waSGt21i6HcyySNN57E7h767JAtSJvtNQLqv2gpPH/7cSRG6c+cNN+zcxgdxeQOTueQL3565gc8zXCEuntm5s1yUmbmNX/Bd/1D7+/A0/nR2RxruRtoTwbc23g3PPUOKyOqxl78D6PT00B51LfchaXg+/Zxe2O/C8lnzI+yALE/pq5dX1ITLu62Zkz2DGVTS0CTDQ3RweCs8k/An3XlKo4irr+3aLTwFKBme/J7kzSuLL62tuc80jov1ZEDW+GuLyZDQBGcRL+UUwxucOrT89GcSc+131As66zmGK/iKQKgCquWp1APbA3LhcG4Xf2pFb6SOdB/IIe64PB+ApzEogIAJSp2++w7IGv56xzEo19+B20osCtBAZkDcqVb17Kum/AyVxodCgfH5X9D8fLynI6OxJKMRf5Lhewf27+tk/YQny+clpPfIvIt3P7g1J+7q1YCHZ2TEKzsqpHVAMmS4z7AZGFM2btKwLac/H4NWd/jF0p3fzvr5Qjs0FKlYFWVauooNYErMcagLdzvaKfZ8T3TDWw250RQDq/ldEhBxDktbgQpTjXfLcyck4IZef/051WPPPYa4AJh8tbJ/EKoqByq/AjpwkfDsrSwq9WUGQyHf+C+/EO+/zMdvHszoqI6Wlh3+3DfQv4/8Huu9RqrPS4j3iLiNu+/empMqBShiik1K2hX79ivdNLaT5XDeijbv9WofFtSIlxpD8LTm1AQvPHeSDG7YvufZZ29wtJ3xf5Kfvef+cg+6wdD8+r1n4Sn7VTZIyH1m7ES8zUkYFJ43S7zvHCMzw1KkwFb52ekbF6ie4C1MjWm/44bjWMHq6a1uHqepEg/PsXnMSeIq8nR4GqsboSllKDx7O0/3xiZeI70l4j2W9tKDu3mAJDyxJ0ALvTxUij3YZDGuyu0fMrS/HYDnJk3w9tWcVknvN9zAUqR0dsrHkXtlI9wixIbCz1zC9+mxl5997wtD9JnblWe11vPuI2P3fUX4UjB9bBvKDzTgz7Hp7wSo601X4LQyBrWcXeYKFLSSqnC5p7e3IjnsK42vqYWnaqJ8c4enw+HJjlP8WRjrXQ/PJOwZGRXXvHt3mZOPTIee24VxTQ5OpQdJK+zpCJ7F4IRnjMOT5ZOgVZga4Rrzrt7iI0TfesYRMJUneJkBCs/2t+D5xddfvPdMQ65Hm3slqzx1s4nceh5/Ck2EP59+emzi5defdf35TylLA/RTkH7q2Ba5Z10FnibA4JmcXO8rDdb4fEOG53Rgc2O08NR4h+f+fVtPLqT+XH+NpveIqNTm3Q/hT+Ep73+CMmkLWFMrTIT/U4JWgZrnsrXFEu+mWtInycoTbWc2IDPdpMRQpMLTBQpPNSMX4Hz2hj1jQ1/9AE+9935bdEkaDSnb/kRaMClO9afqxY/N8w7Dc/rlZ+GJjAFdWjCE5ad66B/hsNTtNyYjeCLeB0uOJ/kGwz5f/IThmemp9nT0NnbwYXPUAc9OeNr1U/yZWnb3g9KfR4S85CPvlsLIwr3kI10wFaKVwpQhKq8q/Zuu84CJrIrC8LAIuwMIjEhRKYIyKAOzOqy6xl0poiCLsDbaUqSNK1GwoKKiGCuCDiw49lhjxBVjiV1jw4Ia26pZS2yxmzUmJrZEE79z7n08bP/Me1Mg6n77n3vOPe/eZ1NEiirLU/IR6V1GyyVXahVqda9bbjqCKyfY8hTCMpxSFBqefMP4yVTP5an5/ar9lhtUIC8uLoJTeJ689S5y9uMy3MBUGVmmSo6vXrvltXtee81S4+lAlN/YunWr5jEiXoHSL85MotDO7RaeKuGZT0f3dnmQ40lJvYWpKalp4DyH3Qjko4bIZAP9JQom/Cm7ZeJTJd41D7k43a0S9hJhIzzJ75qJbLhX1jZ50yXUASnF+4VGTIo4ceZAfPqOH36HLuQwuvc7voH03Tj07u++AzNfnnVvuFR6O4uS21V6rQPZch5BGZ52hfLCyafcBdB7TrF/NQ4wSCK+4CtrXZ4W9ePOW4CiU+RvVHGS3/MzM9PrfR14NMlORQuSO0M0IPDnTVSgPG9uKGyNLyLei1jNgEETGmajxfDEnhySjuKLymOuE38uIeyQR5Uuqqji1Nihq4DTWT5fqZnIaD08qebB2X/vhWPhBZmlTU9PZw2PDY6Fe/g8LJoWceak4u2C6AdBfO9ZcEQ/vP3D22+/vTDGZaj7mPnY5idmlGwkOI05CX6+4+qSGUDVn1u33v/FXZRiWyGi4BSlFbQ0GZrCAsBLX4s0fTIcOfLcdHPSyyyFbcOjjXcYFYTq/J0euska70s8qT7ByWpaqZfgmaAGFZ5c34xPaMjs8HZ3qWpQLc/ayqHBysoyuSY3xGRpqIwtot018HSqeXDWNBWkw7MJfNDCW6WyvWC4bDCcBbdSaXKWykM3v6lYxSUSnmPffTe2sHDhD9CEp349Fp6evg8tgtNGvLMPHp4qOvZSLyHdAL91692XX/3FXQzD+PTqyy+HmCA0UzR5ZbhxWwQnW8kMuL//wn/Kc1NO5qcfdPga6fd5G09VFeTlhDbjT3mQk86G55r48tQ0Zu8yPyK/F89Gq3U9mOVJPd/eyJIEWj+iHgQvxGdZiixPliytrJX1dVwVccbP9XQ/ifdTL0/vhwO2K0VylTLc417IlAtH7goRoSAqLQWaeBa65y1M33ceP9iw4czzFsJClY8bFi1PFjPwWJ6QAGrCXXjeK8MMDmO8EaynUsyefJYdfmgm9DMGySjEuZ9XzjWG3OAYop8lL4P6dmEhHOZ6x0XvfFrly/XWNw3q9Z6sHt/whtBmz2ZSPBNh+sm3P9SwGp5Fm2w5H0u8z+JPO34qz/KGsiwSa90GKxZk0lc319akc6lLNvP9ZT4fCR6SsASqbIr3ecWfYY9WOZ0AkFbb4LSdMG4+onPzfmb2SIxuWFw8d3Ex2SI9c/FMhj/8c/x9WFIk/hyzPMEpOQl4rAjDn8g4VXBKvFueZ1GsSULESFvvulpimgyjj7sk6nlY8WH5i/v9PSL7jnh/5qtPuaOAt8BHP1WqlYJThWcOPM++koint3Bzw9r48viiTeecU9QqPEsGxieLWQ8GT1E55Xxqc4vEezdTd20rIYlwefC5lmcXIun5+Bko1Zw23vmTMH4SP+ZvOvxfGhNJA89JUEjSD52lC+878ztJR2dxJv6xyYL1pzjUzuFNOhKu6k942g0JxLuZu0JSxIsLSV7J76pbOC7Wd2R7fqavqg+ttm/fvgOe57x7Qa43tz4dn4iY4fvheTMz4JvquBMHPB8qToxPSd2UJgaF54pgw+ykXI9DChTWrc0sPU03y+9RrcrQrJTFYCrp2HUrT8GJKsWfyvMs+JiYIRtlad4p/ZuGTUJy9Tb6YeyHH94GCu8kI5GO3tZ/hOGpOtfwNHsSNNHr+Cn1JzjZgHTWKZRgSGcHFLRb5fH++6B87fHHL+YNEBXlxUjfOO/lo6lNXaDwnPgyM7c+Nz29oCCdxgJT6XSG/81xGu9n3yQ854rXxse37pEoi+tSZfzsHR9v0HwETCmYUlMPL6H/6ROQuHK9pHArZ/2nqsaHPyPUnzz4Hd0TD0/ivV9xAgNS7robF+d5pSSiaVcLCIrgRNO8AygfIBwOG54K1FpU/WnbdZz5xjaUhSfeNECf37rVzFkRGB8HniKUw7y3H6/h5e/WdIB+CM/sS6uSvOyWSYenjCNXFyQnh26yPMlJnWfPVa+lYqdfx2Ztac+X9DJ/zy4JajriSCHeSzKrqC2NKdebPqimcA53k1wXPCMQl1AHqKyol3KJlte94NRQBeffeOohCX54CSXeNEB/QJB0eCrbZTwBapO8XW+j4q1bL7GAXiYFOkMg4jEoPA3Oi9+3ANWIyNK0guV/CZ4tLY253irhicSfTfA8O7+OFQXSFmG7RF77Ws3vrA2ReI8p6ZX5ZgkNO2gy34xP5X4sSVX0k6EGTbvhUBtytjCyPIl3g1MdyjfwlL2TwhMSAKXktDA5LFGeEpvTPeMDo938jkLFnzjUETiNWaVwDfdovBuWyC2Yli7Cnyv2ROR3aIpMx48DoCplyeHAxJdWGPS1D7dJfHMg5w2CZ25uW663UUrtk31nSS+y35MsHXrk6axTnn176/hJvSQ8YzOkHyL74yxPyUfZ2p8HFrTkEKDIGFT9GVnyp/7I8uwWnpc3arwbnmJQmFoN8xTJ1beyaHF1b6RHeao/JdQXhKpaVXlic290tNYv4Q5N26O3ONHxdo8cPG1+d3haouA0gptydH0JSCPlue2XP/74/bfffvvzj9/++GPH9s9+FcGzo6qtysvuTZ9c25K1Gr64Uo/lScXk6cSfiQ5PmR41w3NU6nm7w0PqpdZs/NmtSR10EHWgrXRw1pqERD7S0cDe46pG/VmVW+/tL8NZSGrXlZrF5iNTU5OzM9Ho6EhvQ3FxcXVxX191w8jI6Mzs7KRsBZXFO5SDYyLliWX5J7SN9E7uRLjjUDWn1kuOTEF/vMa75WmBKk+eALXCm66U5SvXvPKKvEJ427bfuH2F8Pz9l99/37F9B/rsM+HZUW/qecGJCnxs6N8MzzrLszOveI/UVVJ/SjlPPyQwqjyDJr9TgKakFgUZP0EFUYWqJzUpQNWK0DT53e7ohqdkKPVnbnRyZGRkgE22DCaxK1Ja41Grqog2DJkwMY1LAFS+2Rmso6DxGuRfHgwGS1CwJBiobq+uHom2jU92T83z9xCpXCkTCdnAiL3PJQvZOTxkladTfzKAEu8uTwKeBzIOdTByUppGRwnT1x58EHyq7aodKuHZmN7R1OFt664xq068BaFQ3ObkzcKTvuzNnZ2h6j3iU+Cp8c7lo8Do5Az7i4Nmv7byTE1gg7KmI0XJ01oUyQBgdxvW1xt/osjMSCCmtXVToqwzYy6buLqwcPWa1atXF65ZU7FmTSF3dxMlskySd/BcERvMaG9vlz6hLjotJzDkWc5fMa8TqwCMMjICfYK3OpCRnZ1RXdwwkmycaYZPdahM7hdtv+67v/F0Ix6cLkpD0wHKG+G5/bffPvvs18+2b4OpRDvvxZ9t7Kb3dfjqm8rCQ4PEW3+j3+/ZnC8L3OLgycUYf/veXH+H5yat55szRiZnGwLkd2TGz8ONP+fnhajlSZ2JzEApnsGgwpN+ndZK4xnx6/Y67JhjjtxrrwMO2LNi3Zp1aA0wkbyBJQvQuIeW4NxbeZb0VRe3Z9gWIRThiOhvmcsEVrHN4uIShvfmYGxCoK+4R3G6e2LJRmpQO38Ho8BEDkiM+S/B01rU0H0Qf/7626+Q3GZ4GqjWn74qLzzNSoKC9NKQpzMOnnVcNmai1LlTRiLXj4pYGyLxLv6cGjc8CXjxBgVTs/CMKE17Mjxt3Q5QeLKeFnvK+AnPQErFXvsfduT+++91QEXFIRUwBKTSrKiowJ9rYYllgcvXaxPpWJvwBib3e5ILrWJPjnLEK/81qgeaSxJKBCdgm4OYdVBvK+DQNFs7XX8+Tc3uyBaarkg89o3KxXnxgw9u2/Hbb7hyxzbVdp7bJd7j4JnrY37E8CYquNob56cDupl5dxzFPKtAa4N7G55U8zJ9D0TnJwcCGu9mxyELmGKZHnXbaaa6VKX2BKnNRz7237HWQXjWjgcm4HnkkUfCc8899zxkT876gva9ZK999z3jjNOPPe3880+7bBd02WXnv/7666eLzjjjkjPOuO22W/esKCxce/31iZsQ42xrilhWqMY0B5ubOXSEDVQ3lJ3L6GlqJuVJxseelufbT38ILooj5kJOKnc5unKDXgXObTJ+7tixHZh8EAnPs8nv1J9JBW2+9CTVHb4N/uTOPIbPHPwpPFfGrk1ZldLK7B2eK2Ky26OT4/Bs5j9dx0/xZ2wL/lTv8XBxPqe5x/LEnz75HflmfjYjlXg/Fh144IG77rrzzrsdtHHjxoMO2njoPvvwRIda8e1GxA/YLXH0PvyOeaKNjz37LIvcDmJZ0QsvvHDa6ae/eNutFWtWF1YUAvrRRxmc+hrassjx1pwqwan+1BtaPf3h3yt1a1A3F7llksnw5luDcPnZ+jO/o4q1zUneDmdtWZLXgz9DsijYwxh6c2dOVnBv8vvhhx+e6vCMTPb2ZTQ7l4tlgXLMUy259VNdEZaeIt+4j1P31BTDJrLrGVhFCXPsKcynRjLiE1+99ZIXT3/99U82HrbLoZe9p3pd1lp985Loo48++vHHzxHrK1lo8dFj+5xw9Ak/f/+L6lvEi+g3Ed98/AELt+7IfWS3w3Yu8lFHLUjbiT6U8HSIHi8GNfnoPDEoPN0Id8E6daZ7xpp6OP4EosT8Dn0xZkUOz1x4smQP+e5oTM7aUBfqzDE8czbnhEpk/DxcRs8Uipbs6tnIeK9umIFnrOSj+MMnmq89KbdtSR161OsNH1gtbdXWpjzNqNrdkcSl2pfRE59vPODA03769n8EOtXvn4hBf7QQLUkrMsOO7R/eQhfjlJMndjtm11XSXVaYZy4u2t1HCK4ImsoTwVP5udJ2h0vTfWfyEQ/9wvDkxIuIwZSURD56CJ65mBKe3U39rCa6oyDZn8MVJFZRxynPuLzsPVIkph2exfDU8ROeqpQUWQWenWmucyDd3Swnbz0POdWbNSLeLsZPk6a60qtyISrLCd7duNeul3368X/rCzlEX78gEf/zt//4+bcff/utDmQYTXieNbHbcbukTAMLlrYbsoRTy0/jT4Banv8gKiHuhrfjSs62ZHJ5/kPbbLzX00+WeBeeNU1JTXFZcZ2ldZ46T9yVMkfKy8tIY48mOOOlnMefM/Pkd9tfkhIQ87JkEZ4IlnC0gqWVWDVdeUYsT7k/i2xRoR1y6bO77n7ZWyywYO0LJ13thnhdri93h+c+P3/L76nukUN0ixE0tz7/+N33xuy+6y4p2hGx3SUgOkAl8jW7C05EPsKQxoEuR2TrzP+Scr0RfMqQA6lT+SA8O+CZVN/oU5rd3pf744aG6/xyEz4POEn1eYHEVTSRsCc4mcHAc7Ih4Kz/ROUpNO7lejE41Z6OxsFoHKoSnoS7mYVWaoXvpYF98qkH7XPCC2/dsbQndfmt5Ny7bU7svnH3jRtf+ullRkkenE866YMncDiLM2XNC08uVV5dsGK3nQ+MnRZ3Kk/3GhJnkVsuiT/Pgs4bLqujVPbl4IMPNmdenB/wvOKoo664EaA33ngjSUheHrwRnnLy3JTX0QhLOiKMn+QNb+MF3uTBrBx/aT6L/gn5/Lo44SkX3eBZLpO+6shzkYEMpz9PRlLzTghPtMQTfDqA6snwTPfZ8VP8GYkYh1a1NO++zz4H3Xp9IjPL69euXlexbs9bqfEP2NdoL3lIAXUclcBxhx1wyCGF6/iddesOOWSdzKOuv37vxOsLC/mSrM7Mal9+bW1DF0QBajcguTilnLc8tWH33fvvvyESUOB0sEGRpyuHqZ55rzw5IWiq+LTDc2WIOYu3u6O7vokNDkM1vsaXvcll/g20dtifsMGTn1cXFyrZYxVFpqhc6s/2ceGZQLWE7AI7RgLW17k40xWokOz2Ip8iTTc8KzUfme4SG6eeSjvtoN1322XXXY879thjDhMdyVMK0732339/KfiF6Z4iyn4AMl3aG/RmwwldmjSuHCRS/Mtc4BB+p2LPAw5Jy+getgGvC+jd1YoKeNHx5/R3sETCUk+c37DgLMcTTzQvHA5liIPviivgaWFiWCzK+DnXVcMVXh/bqeVW5k3exhZfaVmcZzh52FPaQwsyry7PH9xD7xqQQjqi/ZnQPvncVK/cr9ICjWGDXEpM81NJChMfalJHeneX2q6pri7drW3bIYrTtJzwZ/pJsUUHiPGOgSH8xIn7HiCGpMDX5yHGh0DUOzzKMoCic2iVSMGRSknM6n3as/Gt5EQqeOabsQRRMHugVg2qO7alnbyfFTTNbNPcgYmFJcJT+xxukPNEMLQ8Oaz4kXyCJTyBqDDNW71+5PdJqSgiX8CiMbNpeHBDck/pdGlyFpe/8+pCWcE9yolpCXfqI+J9ag6eup7WXDLmZ7GXnpTE9SOwyd+PtpkQ8DS8gSebjLVdB087ZYpIhZ8ZiF21BhseaUhCkcknj0MqmGky89SQ3hsZM6bhR4IhHogT7IOgLSLNEcZwOPbNzD20fiY62ktzj5ZINEsNiqg5ndSuIuCdC8bEuzGopWlQgg0pTAXK2f2gVA1PV3ygQ0I/eYtXWaL0zAtakri14ODwoGf4wmE/5qxj51RnaKeStBUyfpoJHZffJ7dMmfV1IOUpC0SaW/T6kU6R5GCEtNNNe48wCHcJT/iqwCk8O6qzY+MrsOG+B6gTiVaaIjKb54VBUaObwJbYhmURE0tw8l9iBUlSJH4sCRTPznEbmS20TEd6B2j+DRDxGNTypFcnLw7PpTtWmni3Qb6E8kTOhh9nl6c++anledQVT3JcccUrwhOfbt/m6axskXtktORmVp308MPXXsDqo7HSsp7hseQ8dvXUSQ2al1WyST2Yom2HYEn17JZK1i/B0l6CZ4NxQmauDI5oyJm3D3E5X6X0MCjh3S2WVZzUoQBu45+zKhE36uBIXCNhKM261YW0RBRlWlqaWpMFVMzRqNsckuRCjZGSbPpII+MPcZeOm+cnZ6Pw7B1oGKk5bxGgbj7SXgiflsc7PJ8Gp/K00e7mdAejO4QiGQZegR+WBKZzekWzk+fK2qe4xVXSpUlPwfPaS0+C5+BwU39p2JOvm9I8eZ2Mn2mrhCf3sZlYEcv4OfPcvPA0BkWr6H+qP+1FYpCxstPwdG9IzTocn4/xUwMenvPYc7QvIaaVzAxRIluDWzEaacpJ22OP1iJYMmoanJRm4BSOiHs80pfjtjw0Ose33P7mmzc9Nzk+OwpQHBrtEYPaRUyWptNd0nBXnlaWpzIzWDkcnvq69NmNdzkwKUxvNDxvXimrX+tb2pI6TgKnpOim4YKC+8o2JOcky5STfBSCJ+Mnt1XjYH7UPrN+vR0/OcyEMyj7ZWocewIMgRRzyu38BajceInNM/bH+k13R3FGbPzapZ7nOvrJsNSGJ2LIBKfYslVQIulnx6eSFVdIzqGTpL25EmkhwzMaeYjbb81FpsaJeA35yWEMeoSsuLECpi3n1Z4Oz4MPBqeRUjNRz+FCNSCdjG95mlykMlneky88q9IzG+F50QXYE57T3vB0uA6e+BOb+vFnOfldJfVS3+yW9aa/JJIKFJ5m/LTrGBg1zb1WbHteo72plnvR1hqcGvBd9cUZwVVpBPYaYBbKGZQ05BPNmInSjDFbtRmj3gQnRbBak7ZcQkJ2INAXaG+HZ+/o+Jabbnjzpi1TRLwB2lubzDX4I9SfLlCIWp52/DwRdzoRrg8XofJ1LMqriA8CUHyp9tTXB7eB82DPHDy5GUFLY1JVi7FnVU1P/7Dfn5PPo64uLsvjt/WSaEKWezN+zg8EWMBkeKIUNx+JSD4r/aEsZ4tRZa3eoIE7K/r9eHUpH3WPBEpiivZeXbgalmv0WE0u13DHmurOTTwojlINTipg2fog/fhYIh2e3IEr0N5XDc+B3tGZ+TluE/dQRCJeeTZE/WJQgNrq0zWojp+S30lFB79huImUputK5asgXZw2vwPRAhW627YZf1bCMwl/ZlYxh8vkQ1VNuCk/FPLkeUL5skE/JxSiXoohpon2cv4cJcUzz00NBBKYwMvoZQom4SnrFWWtUgSkK0M71ZTJekUGAFm+yEaOmqGuIX9+nt9ZaxOZqS6Jjd97NSTByEnEGbxcUVKDpsk1Fol3x5wy66Uvb9wZDCZkZ3Nto6+vT/w5MBodX3+TjqCT41Eu75GSBqaSZQS1/WR42l2ITj2v8Q4fgCKHqJFFaz8anoaoG++UnybambtLyHs862X8hGZm1R3MhcGZOxbuZjmGJ5Tsz2dBQZaMn5sm6HHa+SY8x+eYHwlPxEkG0BJuAGXjXa+rD2WtZJ/+0m0kmLCXMWvwlmXFMYVlnydcK0czgis2rdbCSJIQNDWtE/Tg1HAHJ11s/LkMaAoFpwzbQQ33BBwKzfZiDDoyGp2cu/3Ns+emxmdnJCVxmXlk5ZnnWp7qUPEqOJfzNMDg6MrS5KRvhaCkdVca70c9KZNO3vABKc+clblJ8GyBJ0tpk6QbEQ774rjE6efepHGlefgzL3ZTuc6P1Bn0l8aJ92z+MEFwapov13wET0njJJra7q5wWQ04RdwsqExuA9Yly8XkrgN+P3dBzAlFirNjWhktDUf4EeiSjHiYvI49UevhRXCkla0itUvNJiA5qTICmt4bBnq5ND9ZuWUuMj4zC9ABviHiS5Wn06vT6lNwsoDJ5SlPK51zus7kWF4zcVrypw6cNuJ5i0eJ95vXZ1qeueC8ugoNhZs83BbKn9cjdzkMEe8mv8vsnXQEz8ktleJPWEKTeh6eNh9FBChPry9c048/9QYS/U24dSi8E7PaoSF4a3cpMjnSHrtpLXmdqeQ5KVxRZ8stI6ZbuzNwFqFzlKcWv1ofycUhSeoJViXZ6tCGkdlIZHy0VzUSJeKBKSmp5kwqUGgCU2XS0Z1u/akwLU/njRv5toAy4W6lBRPWhKGwVJ46lyfeK2WDSxU86e2Ck4ZZuKcmh235/lBPnEf6If6sGHiWE20a7saflmdQeQI72JLU5iPtVHLffmbr9fU9wlO82YTguTKLG2uwXlfz/Tw1TbQ4mGbKztVpE7ElGaR6XRNJ7c6h19gOR5wV5wqm5NXFRLZcV08Qms0OT6p5WTkSnZ0dbWivRsXFA9HoSAOvvJs5z+GpxSc87fhpeWq5tDzU3UadYbj8sEh5e+O2Hdvp1z3o2BO2Gu+V9Bjh2ZFJs1zvknV1eHioLitUulNPD3c/yMujXlql800iTtpL3N179rmphoBc4iYl6JoG9Wej8OxGTSxc8Ib7m8ScBugQqd7vrxnM8tubAOLP0ZK0in2ZZsJzj9Z4JuITqa1FOBKem4pQK0Cl4ixq1YlusLp3fMvclsmR4r4MYl1hNgvMBB4Q1kyfAWt5AXt7APUFwDx9p9mzTS5CpCPrTxvvr0jx6RK1IO0bnm4acs9IMtCNdvy0MJGnc0j6tY0tZCTntnhh/6CHu8WyXsVDOyS/zh+KgecKHT91plw8NRfpFZfwx4KmJCTlKfmddgh5SLzZ1ARMoUl7YJBSlNsN8ECceO4UXVG4Lz2QA2h87LEJcOLGIoBKoGvBKQefW1PVndnFo5G5Kx+aj8r/SYDASDDCqoYnIKGZka1AqUeFJ99UN8wsLFqDHm/syeI6kcPTpajmVJKOnEn7f+gKaFopTthafyJ4JsGzStXjH4zrGebOev4NyjMrK1b8CU/mzLLqpSHy0HMjGYJT7Uk+mojXeIdnFzfy5BEeu/DkJsWp9wFkaX5X08pQaVyydADzS7nblT+6Yp105Zi2F7IRHAm8IsZMcpCy5UBUnrJoP9hXHK186MqHJqPVARlpgjjUqkSTvKNAdjY44dnXJ5+I/IVFg9NO3iW9L+GEp3rTTUlmrFyW5607OXEYacPOEsShWi8ZefK7MjXeLU+54JA1PJi/cjg5VJrFGrVSePpjNB/pggz8GRiYn1s/Qj1PXrCLvlMmEpQnqUeBjjFwYk/Lk7zOMmR2/ndxdw82KuTIRgXP7CrlSX+uUG7TKv7UKfo5yvMceQ9KcJoudqAhOg9P4j2QDVCecijPEsxpWOpT7VlNURpADQMd8Dzebny34+fi0v53FtQ69ZIydEtP41PHn+ZV0Fo9eQX0oKg8nQ/w3Kk7kxtlNCYJz9xGVNWYNVyTvNMw+T2LDpPEuz9W+iGa3vFnQsZAZMvUAP5UnwhOlhCVkN/BhmpYcjioJJG80g1kLXm6XBpiSJDbrflD+HN01TrTNK5Ys1Z22spoyROcMoC2aqwT81J0skCJv8Uo8T4XGS0OZGBJtMygJXbcBCav0IQnOYon/hw7U/x5lY13N9yFqPJUeC5N5BJeYuimd5cnsvHufPCEuqjn4Un9afcBt/n9XXE9NJPzeuDpL4Un9TwlkUlH1HvkI+bvTkFPhmJ1G/EOrO4mLMo6zkGsyRYedSfexKEF+JN/vlz8jM7Mopn2FHia7vtaNjoIzVZxJdcvqOIxKFkJyq2prSl0tRICvTNTW26ei4xUgw2WWszbg0FUJ0qGp1SjqL0dgxLvo+FF9iDpdhnHnndaooYnBl1eytuTE/uuJZ1mveVpQ9wW8vatJ6eWNMT8SP0JTdiGsmT8zMsK9SQn41JP1k4xRViT+lPqeXJq8fiWSK9mWYfnREqC4QnRsgtrysKAhCc3oLG9avEnYVAvQ+zUJOtkJ2eLUyqkI0/fc10hsyBJQIgUz2CqXuXFTIpoG8Qk4M+puZvnpkariXcrJ+IxrLKUA5fqekVwovaG3r/oOu+YSMsgDq90BEHEXVFBhMiiB1jALohnQwEXS1xFxdNdWJUgoFFEXYUYUYTzkBY7FjS2w9PYsYHtYg323j01trPmLDnj85vvWxc1zu1+uxj/OJ6beWfeeWfmnYzgjxwH7xC9U+OC3Av5jKepJ8dyPOMS37fD0YUZB0uARLxkR/CydsXyENZ+U/op7dxKPCXeM0JV8KzwV0Y4lMur9OSlJpSyP1JW3HjmB0ZuXRludewdnrxq08painNsEBwNlExQ6mMFVeGwdkg66FARMm7Pbd+cp2muJlxb7fIkAIWfeDorqOCmyA+ZOIf+5cfJ3qfulb3jzReLgiYZvPFslZWz+QSou62PRpjhrwaPGFBVJ8NTQGP6SQz6hAoWLQvq7o8QQ4iKiqmecVW1hJJppIJPnb7Dkw94mj9y9ZP1syU9L9SZFQoVnVER4eoxePpT03KzNYRWOOGJf3fyS8pIuAnltOM4H3VbsZg6FZLk5SncREKRNq8drXsJRP0a0uShimcsYefttH7KH+XKHSG2aMKTgzaQpvCzJegs6RoeWzl67+hS4iVizrgo8hRSRMrZ6mbvWEDx8sqSwFPq+TsXF8e28e7ISkQ8EeN5MQLT2Nn74tynEV2solJGQJqgoPC85OGrv/noFU/yskyXp0YFN+mUNwTPJH9yRUUq6ZAkPzxTqLehxhKBHTxHON/08Vu52qkrUXx0c7FYKjiqYioVm0zTVuZQ13E/h3crG6mb3hmqLAKm5lHUjyfE9LMkwxy6G25i5vHknB62zOS3hsfFc8Uk8bz005ZQHhKyyuCUtSMY+v4ScGL24klBmPE0lu4VM3F7f4zBDqaeF9PfwniI2554SyUL8Zj0367IcMLTUCKY/rqP0E+oujzNG1mHuqq48kIRT5cflhFaLkP+5FBX9pZD2YjUk+MjX0DncegnOCVy77Wt+xZ39Fs9Li6935pWQjZjXneXptbhjxqL0+vQTht3rFrdsTTjyfJZvQ3+B4U0QVGh2MxJMCw1aVA8CefL2sdWzo/Oj0Th6Uad2sibgBMBpzwQiqkdpyxeeafwZCf27vZ3SIQzPpIanh1/C0DFtJhKZcuKxIN8o2hY7QfpJCxj8opiUGPsSQ5OTOyb2TK7b2aTyxP9jDSkZhWxhcnCv/uzuqpSjKdGBaGOeIbBmuGAT/YeO4+rTfNldhc70VG62phZNkHa2dkVMcZ0HWiWbrDTQqWiQhpxCsfc+JMdPPppKslDHslRTl6IHQ0TzucTf47M3zo/DM8yAEriPp70CDgRcstm7eAUTx7t0T7j+TtA/4cnPrOjQ4U/Jl5e9GGhtTA1okh8s8k7rp+uaMupp3jmUHU13TRtPFtUA9eYF0r1JHkqKgtTaT/Py8taVpfN+ukoaIJ4BgZTh9t9TvBp5o4CGU8Dmk5jOPt2PBN2j9gA5WCOt4VjFXqY8ypZUP2e5NFo2s67ODxLtnRyHsaTIAmIZury7OaPdEjNAjq+YuXK8TCRZXn5fxySFs9WYEIQmjH9tFOQvnONp4DGbu2J87xMPCXpoHQiEd6IizQelxpPwJqIp3sWFxOj6snKmWDg2MXT3dMK5Y2nPxJpiCRXVFamZnngWVRV1bzlUEKCdVSo3J/xITXUK1q0FOOJvV9s9t7XqY7xfndaooQu+EhXFT6emCmnM2T66U8qrIym7U5hDOaOO0qpJcLcPiEbKz8bN2809bB8vBk8Hp5DN+5RJbBsJd50lVOaaZYudL3RKFl5ZelIhfJNwinIzKqbbz4SnrEWTlc/Ywra0xFEPx2kbmUQX0xP8U/UjcTyI4t47uFwtPWTj7jAkzTdLafCtBsHr+kU6fDMqiqsqPRHshpIJifnpTbncmaj3ZHtN+G5dLCd9FLs+Eh7+/zNJrwdQUt/oJ98tqGksey8ipea9p3O9NZF/EWeAoQC0+iGzdtwZEQnR24z7gZKibWoaCmh/NkYPGgPcFYBYR0a0qmRKuLRxX/6d0RppOigloORSWoZyH+ODQ4Pqg9M0vjyuWfeuSc8JYvt3ZDC09FPiYgayph4Gf2EmkLT5ckTAaRei+Ukh2cRPBlJgH52bzb0wuNDV9zX4o90VlQp9KyrKCgo9BQUJTVT/2n7d/uVygLj9CMQBIomYgX0ZVt5OzReEtcOz5lYq5tJXoQNfTAd9cQhFbGDX15//PJbe1sTcknH77xDxtnZie3DgwFf4tABSoCWahKEhU2wJaSnRClFotMra8BNRC3dbJ2rnfuL54r50ZoRzjnA2Ts5vmKYypsobWLhyZdvvnPH3xGIGlL3UhTePFfN9LDeSxE6FrHkm/2ANHlRUwWmtoVHQx8mnI/ZuPOIM4Un6yc8sfoLnp5jnPqLR1eE6jzYe4VfPIt4JR27ZXZtLL1E+6Z4yr8r+BRT1eL4Npso7rEcSBCedGMika6IYX3SFlAcEg5e+rn8+PrlBUsDZWnk49VaVDpUPkYclJ/WTDYZmOKpb2pwFE/XMTldMYkwRSkBatbu5udMP1fWzK/kpFg8o1LQMRS0l1fkUqw9JsRNZ95sA0Bvtl7OM1/+dtWZyo683I+OgrIRonzyRsSTd7HCKO1LX0JscpZbavvWW08ghwimCf5I4wW774LnwDNzc0+9+PUxnsoz6ovqdZfQOSdTRb/cU9icofM497xBPOdXaH6dwk8JufsU+je9OY6Dl34Cluy8iRNEkV/qbipuS9I83uXIrWNLyhkhzOEwI4SHyifx2xuinsdqyhP1NTDli+kolu+ETdiHOUTzTjFPhIpqUwTP8cGRpaPzw+PSySgHSXTgRc34w4PCGee54803w5Ekk2ru9jzw3FWOup7Z1yGtFE+IGkve9mlwLdi3MVG83QF7TELo1veX4jyD1FhPw3P27Xc/ePCi7GM283JVQZ6ZaRIT+j0MJ00u3TKWrwMn+U/t38tkcfCUMD+ktYkrnW3uro2Wxd4JP9XMnsf7sjwMHtLYu3gCdGrpkjKuoFSzYa5KxXvHwr4NNj7blPLYDH3Y0buSojJ3gBLjoqBu9ddRcddODpl8PGHR8Iqae5dP1egIqTfMSef80vFJR0Gjq5whYdCDnOxc3okfcfRsPjmas+V0Vd9MT3ojcTJAjaVgamE1qnwQmZqIulfShHRLDOhJsveTl8V4Lrw+9+IjT9Zt1j0Dj44e7tPgsmrhvAb9TNsgnv/0yb8HbP/uHh8xTzX/6OkJb7rE24b0sD9CRZ13m2b6eved6C5uwx+ZfhaNtW7wODfOctJeStzAGeX2G9Ra7yskOdpEUaEpOcA9dTftROzMP47T7N1H3iPKRSPMfB4dlsGjoOMjI7J9U9Ccvssa6Np0R6ytUp8CaSadIzXEKm+sif7bHnPs9nJ5Un1oBF2SBhdhFdAbm+s2ud01ec9Zoem78EizE6tfndv2vQcuf+61zI4ZzSruy2NMNaM0mQpYmKLzOHgCFFuD5zzewzokxdNaUuWPQNhf1R/sgafNW1g8SoVZLwg8C5OnCpYXVAbKs+2GKvEEEjvGDWqbVWksS4clIpZs5eHpbjotWhPCcoBuL6i8FXkClLycwtMVhjNsR8f4IxUu8tP4kw0nX3XpWZqQo754q6rnwVviVt5oKMHF3kYv0ugwQ/QZ5xn/QmBprRS0SUCTUVfPXuLwPH4ZPO8Sz9do7nnwwW2fObq4mDWQA6AQm22PRsGNphxr9u4EoPAcHjWepEMsW6eK71aWT2J4Fs2gzYvuJxKNwdTwIv4bEoRnkWeqYKrGl5ZLq2sJpQu5G7P9wWUnpm3cLHOXQxJQEvVYO0zd02LaCOV+2AH5fPqXtHobqJpD0gE8l4rAMoAQpKKjBpYfwqTs6s+6iitJVM0ARhtDZNdYGNaGrDNXUQkKTngWp4PT5ejaPJpoX9HH2MKqFw5h4joGMyDk7f50xFNfPAvPbunnB/eccO1hJ3xxSktmy+XHNG3WstWJY4N0Bo+vGM/IxR1JR8Dp6ueSMtNPt6CW+6BPUQKU5aeJdhfNddOIrQk0/wk84O3IQ8QR3T0RBv5PFRRMDSduWW1nxSUlGaXMKCDAJJQ/2+HJk08zdocm/16qB1EyjnNLgqRENcUOmcoqIrWdu+2NlEdmX6R6O4kVKrNFOjGp6PhkDgGVUSBX6E4bt0I1VQs0opA24xaXaWX+vI2dPe1D7zhOHqjqxc9/89FPyA/In/zh5akPTounNPTtHz/TOOqmusZlLcETM9PTu0aGxwaTxg6KVm85ZMXV8ORgsWzJ8Lz6i9mgGE88Lr/y5QOrSaxwG9OHXyLWiHXXh2v/+O1NWkqQ9yXXdbB+yt6nJg9g887ZZrVah3OJm3SI5NSEZOgwiQheO3hlDQjkVX2cn5YfIMwMlB119+MHbPzAAxlfX3/911fmEgw0L+kNcJrZ6m42W0mFJuxeClWkdX/Sd+29S8+6Cv1EGJZl07+dS34Ys0Wa4oy8LoLmHo0WnPDKHYmnTF6vmBSb+zFldfEyZvMV0TSgMfEkTy+sNv2cdodUz3ojOV3Bzj5vJxdpdQ2f6D8oaXCHDPJ1yn/Ck30f+c/hMPYeP/Co3XiDyy9YrfGfR7+98Omn14FPPN9e+x48Deeb73/j8vTg3++dLKW52DncxCXh53c5eC+NFaBhZgc+1L2xA9XzJSwJV2aU1ua3h5eUlz3yyOWfvbpm7cdrn3n3uy++4H4HZus/9dSLF4ZZKrFzK1iyeofEnQ8uVRUO+izQgd5lJ3O54I0G9OQ9edoXE+tNaGABlb0/RvSpXj4RLeZpOBdpqgs29v3UN3+Ky7r169ev+8mTNf12jCf7d2Sr9EiQ7XZnDpkhjKKLcf81ueKJqsi+jOe8eALU5Zk2tHHC5Qur7Z7F1fRWQVSP2bc/Np4uUPFMYknGvye7PEl+agndYZuS7Q7fpQSQGSVb73TwfkecdtFTJvRpn3beixdefwAh0AWf/bigAc9r1q7VjQ+0zn6APMgOBNcj32O7oQATB8oTbjhim+3ztQjYa0n4RHjeZOrJ9LYYzB31yaVU9Q1xnlwzgcWbhrooF5u5G93bU6HSm5g7L2CuW//Goz+v/+knT8X06lnipdlbZrtJqCH75vS1UcvVV8WhGutMqJBz4+aMbGq93XyI8vPES+Tr5JEcntj7BQvnWxfb+avf1hBRpo8zxP2rNV+qkVmdzOJ5RwcuTvqZPHZ2TD93Rj93r97uiP223iGD0HPr/XbdZPPd9t57bm/edGo/tQlAT88IR8MXAPPzr378/HMU9Du7gASYEnj2Rtsf6T36s4/XrvFh+vkJ1RvdkG1HHz6f9DN8Yv1NN8nC5YekkZq4Kq/EvV98p/Uw5t6VpjVmQuo+lHEyrBbHE8P/HXm+43QVr/sZ3fzk0fXr1sEzc3ZaPFk/mxBOzXIibUQ9kapIVRsXxqTC039sRm2aMqAJVvvvC6xMNZ6xA6QEWz8XzN5POX9gYGFg4Px9GU+oFYlGcWjqj3j2hMiv6HKQseadjec+dKtrRkj1Lvtg59vsvPXhG22yyRa7IVvsdujc3BxATzvv9BvO7o32Dtg4d+5gXePwfM948v+8Hh0Lh+/7+NU1v/zxx3dHtQcoyU/JSNkQ1UQU7Afa99/AyTEHlmhZZV1opGRNf9eF++777NX7H7jitY9fncZ9dqOW/xIMvxGeyMXk8Z99CwFpN7k448kfcP66/udP0E94njFhPO8ynmbvjZG2nP7+SF1SVTCvsLKL9dpfmsvmxI3nj8r3BZY6+XmJQiZiKfkjekOwdy5bvACeTTORJyP9PY+9ZTwR1k94Yu/Kh0yNN7vFIdVYOTf5Wq9WRvV2B29Kp9wmm2y06RFHnHfR5psA96Lz4HksCjhw32dffbhwOQq6RvbuqifjBF7vdeL23lMGBh7hYjslZo8qRzEd7cTeA/kvqMZRBzS1JK229z2yzF+5bOBV3Vn07jO/XPvBax+//m56T+ZEE/x4uXbtwCVm4avL85C3IApPSUw/GcD08xuS79ev89TNEi/dwqUL2DvCnSo5kZlgcZuMPoh+VhFZwFP5Jau8VKY8PH9vTWBR/RL5ENbPC04xf3SKNPT8U5qC5EFmHrtNPAEKUlbQh9BPwk9tj/7i6txj2qqjOF6eQ/E6vdTae0G5WFoodDRA29ukpXYry6yaUdtmE0U3GSNKGNB2mS5aIVo7o3M67Xwus4gJOCFigiYSnY8ZNb6y+BwxOpX5fs23/uHje869FeJZe1vjxOWz8/ud8zu/8ygv8HRSarcuXiUkqJIgCPGwz+Uih0rB78pklOr1W9Y/fcNxDASncXjgycudcWL/vAabJ3QOQh5oE9/GgydwakHm9ev7TtvCLZz6KOu/6N5779106vaTbsJ4E/yw7z//4t3j73xww8w5XVBFgqj7nXjSYV7/AqSP6Pr5iA40p/H85Y+/iOan7733yx+GS2jQzKzGk+XC0X7kImwd2tDf3o79cx3N0auq5jGGnO29EYHyjrlp8NTskd7ArgLFIdrg36t4GugZO2ZGx9A75uhbBf3E66mtpZr/WXcn9BO1haSfPFWeBG6Tyx2Kx0NoeUNmyot6Dz9KMVEk19000GlqevqmgYuffuLtzz75gMc3adboxXS+Cf2ucLFBCbX4BnvEJElMWq7tQFMLRZuuu47sv6kGU/WePPce7PTYQmDgPv/k7WMY1/7yxBpWTiBEr0liyML+EYVJ8HWb1gCHAiHM833ePk/8/ovG89MTfxgmFrDeyf+cHTmDFXT0sSHsn+t6T+8dwt5p3mQ2D5dWV2v6SdlgKOe6pmNKX+96R5Zi4onR3gwU+kk8uT33I9tuBE/ePj8Gz8mt0E+ao1W7r8GPYldKXuLsbkZ6KZ82mateeETTFBuQPH9LCWxMVcV6EBl4+vg7P3zwPfOEvJhN2OtLOuEXkRvP10aAiJ2TeVLGHQdHm/o6VqPoA0AHBq666ul73/ngk+MX00DyIz8dm184MrHwEz7XLB2L9Dgof2poQRpgGejB//H8RV/urJ+Yo4LBP7smMfGB7vdOGrv2DkQt+tt728Gxtta2yVy6rrQe8boK3j7pfrNny/QcopesnsyziOvjcl1Uucg+xSVdl2wYRc++URgkrT3UQW5Rum0GPIHT0Lal0k1FxGSS/E4u0KQuYMDJAVCEmZdVc6EcYSXa4+4s7sFBsrnknddffYl4vphNp5KpjN3u76YotwOCgz2E66JasOhbtOlh57Y0mzqf3zvNQK/bh359aNaHvEoEak4aWfhyYs08BOFGhqlH6PHU/0n7qm+m27SzHvHE6+D72v554gTvnsyzH3K4n9uTf3d4FD1MRs+YH+ztbb9jyDzUjwPaFeb+0/vv84JnBWd74w/ad83zc3uaKJ+2wPPcW265AW2Hx0ZxgMclkhYKoa593AxR7x1NvYfHehEPMSDqv29ltVuA2THGUNveCjvvc1sy42gURF2CkrF0Oh1Npiz4mkyiS9A4tQhSsAVU11u/yWSS8E4/UiNSNhENxaLRkH0cio4GQoqSH89kMvZ8ZtznrNpZ6cu78/h5aaWye/3c1PNIqye5C4dortzj2UuTCxPndOVyXVonCSaoR5N1KSgtpMCzMEZE5/n7iU9B88Q///wDe8QNaemBRtYjUH1qmA6eQ9vbzaNmpIess604vdQKf56jjxV83txC691UoztLlM1QfkvNBI3YGkOzZgTnaVIg/CWeB6U1k9cDIljvbXWIqdbdtbIyGUDrHynaGopht/S3wpRLokRdgdAkJLAZPYMCkUZ64qXSd1HYDJ9UDGyOBC4KbN6sRvAbG+UAPgOiaJQCYloNNKpJCZ5WQI1iAmv1fZVrq/xOX/S+nSX7puZwGXLzzTdT1HlFYTrY0NYdu2ZzPKYab5YCwAv5xcKENZM/wkFkfqMz8NH3dffzGagn8/zZQKdttDRZ/PLLhdn5IzSu58j8YP/hdvOG3iEz1nt7W/8ms7WSq9+pHxdnuq2emka+N9STefJA/SfmJ0cGT0cSGHU9xHQtfDuEkVlaG0p6kX4OjoFnLebj325NSLIoRsd9rqDip+51igdm3SjKaYl4xgNEUqanFIuAp5puFLONiUQkqm5Ov7E5sjmQSaObyHMpUVUbRa8nbAyk3FKkMeENxaOCLCrlJfjrdjiKsIUEuyta7pw6sLdjmjLRpjuWJlEjjWUQ/cwLLGmt6wuej52gSP/AgPX48QhIFtTz6I3f4mi0nCf8z5+OLH65+CF6nCwQSdh63CSdZIY3f85we2ndKttw24r2FVbkM3C6YuF+swP3R5xfx/djNICzeWQQl3CjQ2OkoOtwwQGcyLyAlQdJDIvS4nXYP6n2qMwRF6U0lrm13ttNgY/uBmc4FotH40IqG4CG2lXopRySGkHIHYlA+dwBIRtIJlVPZHM+36g2RoKpiBrIBgVJCKSrovaomvcLKr7CXQha7A0O9A7EdlqMLKj70GdgYO/+qb3UOnP56Hksw3U7do1o+nkFYSxsnkC5pJ5s38GSiY6QMFPw/FE7uZ945lMAJZ4/GhYWaTLV4sL8CP4z7Vx1ygbccPafNNxO6720rnRohbN6JYdDoJ18PrprmuJL8D6JJutn+ckTMGjafDy8x6ij+6FD6GU+s3yUHq33VTDwp1bEjUIUjdZcXmc5ujuAaL2FmjOEPG7maVEDkYAUEiNQvlZVFgOSPWKMBVIpVYg0KuONUkANZiQxkvXD6Q+kLw1b4rLijUqRdNWl5eWVTmexCUlMNS2OjTyupbikc3o3eEK0JDXO4od16B2dOQc88WvpxhiifxSMO0fiiSd/FoC+cDeFlwgp8YQwz8XF3OTi7OQ8uaHcYAZ5hRu2D/f2b+hthzvfW2owD3dgvTNPmHeKhwzsmesgnvrECfA8be09NFBvK2SGHtBH4MTd9WCBJM/hGZwxU/7SWYZuI3haLJagt76c+ouBgZv6M7TGfGnaQt0qnnJUVJmnKhFPTzKQSsrGSGMwHxACqi8jQz9dWfyWdEMIa1ypDwuR9E5c3u2srt5ISXZ9pmYHuhY68Kfs2bN7zkYjagGzFgn8dadCaD7m2OAI8Ok8WXQbX7jfZJ46Sn4QTXofJZ4/Qgo8TxBPdNnJ5WZ5fCTKZXKUUXsmeNqGh23tKLY095bZejuc9f/l21B58ZbVGNFH6cl6siJgP+sAT4ThNV2k7ZN4Qkdx8YHX2BimD2yb3DZjXkU8z+qGZQ+5La2Ks76cL6DOrVYsiuK2hIJp2JNGRUX/OjUmQVfFTATGSPapsRT0U/aojf58RIioviT+ZTYoqtgSqkJhQVUqW41qrAEHYzSDcjDP5mYH3OQe2M2+23fXaSNB6YJAE5rOi/mbXbiOgGgkWeiDbo2WB5i0mBIL48wdJJ4/shTczxPYP2dnMcoPAx+66IfwZV7XKevaStvM/W1Dtro6W2+Z2bzCX0mhXc1fovz5jgPTnTWcv6T3A0Q//4VZBGMLaWo7aOQgiB6aAUwIYSYdHbOt4vyQCtEYDyn2VreXeBahgGmnz06NFcPgKamN7gh4ylEJJMETG+WLfinjjmQyYhw8FcmjqvmkLAWyCkxYY6I6bE/LzPONbq3BqoMcfJyFwNPR01KzsW/P/rm5NoiWfYpfEOJ5eIbc5uUGXnuwP6/zZGGYy240iSdYsoL+/jE30PsDPBcnZo8cw7DJHGXT6if49jqy7LYVbeBpJp5B72mkncyT8mk7DuzpdKDur9AuCJF0+J8w5myO+GREA7V2IUVVW+wIdOXQcmrbDOoYDWjkViwZ42G/r1WpRmlcC3WkaVDsCvH0x2SE6xQVrpGUEqCqQkbGRim5xKBVHbcLUbExqMhxWR1PisaI6DYaYY8qW4Otkq8+BP1cS38ejMCh4xLO6zU9zQ5TS3NLU8f+3XO1gEk0ARNzzvGNeI6doSknPfELLGHvtZMRC3+CCgtnBRPN3Mhynn8jSAH57fePDQhS0lqfZ9OOxc4FsRfgFIP0ulriacAIXp8TReckWO4Fnro3z9El6Oc95C/tYHr8Ae2EgCyZI+CFFzyS2za2va6MecqCp9Xqsvuqq1biJIODTAP0U/EpFmtSkuRAUFJV8DSKUEG7KAgR0WX0etXxvBCXGhWfEBJVe8oYk0R7LE723W51i75qi0eOdfMItiIHXX0gDmLq7KsxlZg2rn/+6t0HWDSeBszohn6uIp5gycJUSTXpwSGQJVla7QCk3RAfvPvv/3g+wEDR/9dANxw0ujN3Cqo39Qs82+VlZxlqNZ42A6a2u7ynnVvoJbOR1vvu6SbiWejvjUy4xxcWc4X5jpr10XCCJwTqenB28pEdo6vKePBJiSx6wuAZvLShAgdDBH1O87tR6OGze5MyvPqgKEkRETxhwt0C8bR66p1yXhFFtdHlN4ZFdTxjjEuCPRr1wL7bvYqoVCtxNb22mCMKDsIJ/XTg1dxi2jiw9yHwpJtA4KROh0CK4jKNJwNdlg5GrzOIp0ZUM0PLFZTviJfxxJua/aLFouGnY6yfudwE3iNrcpjO2X/lQ+hbV1dbisWB9V5q3uSqpOtv4qnle09P3crzj9i6o1gGu+s9cJcYnTYod5BGbh2igWX0D1jrBw8dmpzcNbq97gIaku0QxXiry2V3Fq+scFB0rWSly+1DdFnnWS8wTw9IJnxGo0eV/ILfKufzciSw2en0YKcct3hiYtYeDacCiSp7vU/MVysxGTz5+O6goBJZeFSDmHAZfc3cQ/t3Ayfvm+fzB9Y7DPzhsXN0mDpPzZ7rtoiBXoL3cpxdXXymPHijvt75dhNuKIkBI9+RnD22AQoFJYV0jV1528O3XYm9cy+Yms3IAN1jrSTfU6subuH74ls7HXo2A4/TX9t977FjL+cga/AmdZ+cxaXxUfQfx+qHV79r8tFdh3K7RldhqBEuFh2CELNYXe7KonOLTH0EtMKJnkF+v70+JQlixAnNVMXxmAc8/UYPeAaFoFPN++hA6vd7YH7y456wkFVCYbcKni63oFSBZ2In7vKpBhqxeKgo1SaY+kqaHXdefv9D+y/fzSB5LD9xZZ6jSAHBIAO8B0/XLBN0k49IdDm3tNaXIT1D80BvxKX7kvzIZ3kDFamPckgE8hXiIY+9Qi0dh9a0Te09UFu711xms03Dq6kgN5ETwjh/6XZe7xpP+OMYFv3kZz/d9LIuCALgHPsx5NsPFydzrLoY2LRt8PAqAw8b3eLxhOy43aiCv93MlcBFXminP+gGT6Mx4syKgirkox6jmnAZYx5VdBpJP/2h1pBs9ceVqGy3xIlnOByKpLvdLkV0VylR8OSGEeAJ6aRXT01PUY/pzv33P/zQ1bhCgm7idUEZ49R4cv5nYfo7myR9I9VjS+C73MbjqfE8yinKnOcN+ZsDvYav6cS+QDM9cY1GU6UHaYjXmp+OmG2lZThBlMK+r/ZX0uGI022Y556pPdo8Kb24o7uh2vn6k8f/4zmPO7kvP+Qw8vuT0NYdo9eeegfSREa3Uz2s4YIrr4vHWt3I/ex2lJQ0c7Fli1fB/ul3O8FTiLgSgqBm7aG4J5JweaJxKVvpCQbl8WCm3ioHXVElJI/bY5ZE1oe+gWq6we70C+4qH/M8mRTU1ESynpZ8TWdLH+knAQVR7DbwlsqIZhnzPImOdFv1g51GFQ/+XH5qwpukSzP4cFgQC+UCJSR/6xVymOFhoBsuXMscOUY3XYj+f/718ePvfPLrnwulttI5Wy3xbJv21yMHDlLID5lGvA7rXaMJ8w71JJ40bxeb8cXzkIXFDzElBC14D+5C4cdh9LLH6CwU3G2nKSeX7wuF7b76Sme5qaikr5NyuFvqFWoPpjhTiOIFrAmjUc0qraG4mvV7QjE5WxUPWqVxX8rplxV/yJeW7fao3UM8LWrsNLfXL2D/DEmJqiLqK1KE1gOsobjtMDXVNOn6SUItd9EploGe2gaeG7SmHPzU+53wcyvXoFJvPw0ys+UpF3CwNYv7plYmC54M9MbX3jeggzHfm89ffz2uUz775JMffvjg119/ODaPDgB1trZSMyoOwXNt+VK+TQ3s0e3w58kgFUOQpY3Mw7X33nTKZf9J1xq0mMVsSgxNpUAJTYdDMBSpi1DPU1cZrmttdfuQC1bcU9LSg9ZoCBNb7VDQoBv+ktETsSbiIVVULOGYavR6LFFZrIy7/PK4kqm2yoo1FIyBZ8ifTART4ZSaLLd7K43KpVaLiP0TjjF6Hug8O+EyDfRtMd15+cM0SpQTQ7DqL6DhdLx/0t8yJt5xgESvQMOTWh0hJkaVFDpjvIEYkPENakys4RWiUxvcmDdvfE0fJ/UxeM6fffPtVFSJpkX7TjlzADr29PWY9Fq6enWd2bbCXNZWdx7588xTu5Br2qPbd7buOOCVNzQUXXXKJtscZIpkb8fqafwt43+9K4dLQ2Quo0IB6aEojcfDbBugwjh0XynubAFPusUtCrYST7szKXniWO+eOPEk/XR5EOzIVsa8LimVD7ncstsVdkdlixKyJxKu1lA0kii3WOsFd7XLIhp3Fp0MBQVP0nr49AOdNZ3NW0g/dZ6Uy4C2Zyx0gB/tx5CPWhKOkeANsSH2hBxYijkD7DAJYW3XZIzjFPgFoJCv3nzwS+43jvbYBgRCLqGJWdw/ElWqBTkbP6K2o3TYBsd+j8vbTTw5YFe8ETzZHmnWaCUVtew8zYQ6hv0PYU3t3n9g94Gp5zs2aZv8ORM4hp6uV3jZOE/XdkfvekTkXbgjLkKDsB4ubUF+AzUEdHtTkicW8WfhL0n5pAD/0yoaxYhYFfL6RcFjjBtVnzWcp/3TaJQTrpBHCCTKEUsRx8FTSDQUOTAtpagFmzLhRClnTbNpi2mftt713BBkLpGGajwRQoSsqmOpraVXm02b08hjGOkxjNfSrkCaOsOOIQ3/xKyKB4Fz8amjj374u2H+lMvQwudfus49tqn7iuN+5GFwLkmuTRI/kphdO96NXVsOtmPJBpw6ETFDAeyIxDiMQryKhC2MGI0gBTZlDosalCzJopCpQ4RVDG1MGxqvTe2AiYdUpRIS6aQKNlQKdOqmMlXbtKn7Y99z7jXsoZ34EaoKko/P73fO7/zOo2RmphxMkT45QTI5ObkLf8V5jHIFz3XzfmeminptcsTOh3rYmSmsd17tyunIXebZtqru5Jssp6GjM23Va5Vu6WvXfpUaec+jt+jkHhZkmflCuNgETz3aBrcPYk3mDMYQVRqHoJ/mlDV6gs6bS4fQ3f+E34LUhlvZhMvVbaG7+Y0OVyCasBTyCMof8ufhAhyrDCRTwpmsKyAecuupj2bZJjhKPOGnrxk8u7zz8JdA8iXPehBlnq/XAeYrrKJ4B1LSVKWur0QRpRNfG0AyTH5+hWsYiCiNAvzJ3/7wG174f9JsnZyewgVAU9PN8nIMLZmeZJmenF/VVt5Y0oL6gZbSZlfEDZ5qBjts54VLF/rAUzHuVCfUAf3cWlJPNJHLDaAzbTTLkIYq7VnVe/3M7ffeO3SIx0XwhVDeEYpRmk1E3+z1Uc6cp91uo9JDRyDO6z16KBzG4UfKd1vDpmQM+6ct4TIWEv5sJmR1+JOOJPbSRFA6tugIhMRjhoAUEM5kIpJ4Iqv3rtaVOWM6TzvvnrmcPbe6uQY8KX+JhYHCzDNPutVhmpgCtg5vqig4sebrmCYeWPD/LmSv6HLnpzRb8Sd/+MmvWb6twQyD6akNJTM3acobeJKA53Rt794mtEyvQwy01OOKcHkVl1jg7IH9E+d35gmfdBMXYWG+TOel06dPkpw+iQ10j8Jzz+YvINF2M2nlBL7oX7sw3dkcp/69kYiuvYYth92XDSVhogIp4hneGD2WCAgnQqF4QESww5kURX8q7jomRLUeJ3gmHHlrwZFUeAYshyolKSksuY2y5cRR32qtyeR3VPq464UHxYme1e3e+f3guV+hqSqoqp/f/C54wtQDKr0ogkgzr/c6fGHFE1HmybqpCngi1sshi5//7ZdIHkXyxh802Hsh+EAQswbUCxcuENMLk7UNnVvwr5bA3K2rcTk7uKyfDRL3p53IKbnzuJWHeqJiKHNxbOvM+fNAeuk0rBIB3Qv3De0Ddq1dtXmIBqU2QdTtfhRpYNQvVd/utWNVejO2UCLVncL0nuCwCP0MnRDN0M9Aqtt6yJhKhC2ivzuZsm4U5YjUGrGlEEnKh8Jp4ZBT6obhqsJ6tyyRfT/h7ukwukybkMWSY54IKefsHu/8SXaXGKfKs/4FTzjFYEpQ12H7bOEFX7wR4eXOC/4/3SreRdky4Tz90ScfYYjn9x49eKgZgn14pVGzhoWKDLEE8JcN7Vx/GMl9JXubSkoa25HkXsy65v51yE9GvE4pLUbiHdpQGY1HL86C53mwPA2cBHRqL9Rx7961CNhuRhlnCRcc4+OD9eyPocUaHoacF5Yj50b+kismJ5IOKT4sppOt8gm0mrfIKcRDBJvZjLOnUxRglZJSMNAaNOLw1Dq8BJdfCCZwxLdkkoGEZemoX7KI7jJb3GHTc/MlbKA1duJpt4PnWwpNaGlRQdHAHAHlOtJLjtavI57Q0xZFQcESRFk/Iayfxe1TEQDFqke056NP/vDLH3324OFrP9Dch5y6eHFs7G1Kl8F0g81DRB0FL/jcXh+oaCovaez3+8ETQNUOIu2TN+f6vKvV4cXg6UZCR1XPBCknC2sobPwUpLMahba1sEi1yGLZTdLQ32cImkxOQKxE7qunvWYx4sRmGk8GYgHiGdguiYghW5fSonXjiUXBjNiI32qxbh92Ob02S8xvRQremTNQYTGWEszbb2UCDodlyQ17JNiMQTkR9HGfG5y77F7y5+1k39ldIuFXsvDgCX/+u6xKGqioat9JV1uKBh5VQ6p+Dqks4YXSN+oLKejOnz97/unDX+w4cO2fGrTTX7hzm7J9kQJI8vGndx5gtMvzlSdPnty/P4pLrHXzrkimih16tkf2wanzc2zfsX/y8Shrc5q04xdOky2CXKI3EL3JXT7RMmzX7oqKgSe9DePoKFw7MKZdjARtGRMSQMo8dn2uy8vtKoOOgBRNxvLiscR2+YSV9DOBo6foFMJC6y2jYEZMyZbty4jxiNAtthZC4ZRwIhhIJVpPVBZiQbFg8kui6PTLabNDy4lLOBx5V+fsXd4+6Of3z+5XpGiPyMATTzqx0VEeAqLK2i86TNBOFef6/5JiJRBXrP782acf3zhw4MCnf9fA5e5dWbl6dfndd5eXrywvL2NIzh3I8pPLT1dODUzMNLK/5HaDp1IwA54XZoo8eVgP6WfVWG/byZMw7yTwmcjKz8zcpHbpnQf3NqBJXu8Xto1fHhuo6L2sx3Hf6M4i37PKbtfWdHmcQRoaJyUC8QJOPt2p7fIxLGbLUj4dtp6IhBPdVjETlgqWJZM7lxEdxu5EuLXgSEndQlBOStZDZXLQJRayEUmwRGJSWJS11CkMgQHs8u32Qc+gZ/Tk91k9VaEzJ9KSEU/eh5gCBECJ43/zLNnH7ifvntx9U1HSIXXl85kUlUDfe/TxawcUnvgEqnfPzs+NDo5whQkdVnLYxq8P1I71zk9MNZWeXzeK356L1KjTu9ZX03+T9NPnU3LBmKfpjfHaDSfPHnnzTTzg1sPG44KBDkrUfqVzd8WrKJfdNo6Owr3HFxa8lU5j9ijSwDqwBfc117ijeUyS6zZ3BxLxhJAItYaOdcNHCskwM2YjLtctJ0wJV0wMZTOeLHimQklrPp5E8i3Swx2WE4gv+cUlN/F0xqJhsaBTuy56iGeXp78PPM8yTqJKysn+EvPk9c5CC14x8djiy6GciijuZ9t/mPfiat/MIza/+uqdGwT0839o4Frt2To7B49zegKeEgvMyMT1gYbevdNTaCnTWNrvNB1Vdk/OsIN+XppQ9FMHgT+fsZlO3a0ofxN7PlgCKYK3eJIjCqSXLs10oo3QV7617fhABQb61NYO6o1InYN+HoW6t7d7DSZH2GKFty4I8byQcLQ60qmkRTgjSwmL2ZaKJ7GOk86I4Mhm7BnhjDEZlyyFYDKaN7scclA8URYKxsSlTBBJO+jF3C0WtErHBg+GS9Z4cd6Efr6l8ARNSHG9ryH7zupJApy0fULgf8J4MlGYo+L+qVokfueYyU4Syll4dfnaDvA8d02zanP5FI6XgAiiiv85hRmWEysDDSNT8J+wDdb3R2xcRMlAoZ+DxfO7Th3Gh0RDfe2rJUfeeuvsW99/CzzxDdjipASBlWq6WTK0/hs/RRcyGjWDnlY5JNJljPCykBBHVqPGm5GRHRLuDsfz5iTzlC0iRqIkRcGVdIJTJm9yCg438ZSMoYgLPGG9BFdIcopiRnY5sX/GoZ8ulyMtSFX4nHCRgPRZeJ+5XL+H9k/V9VS+ttCCV/0lcCXh2b78VjwgFXmSYLW/FOaJlb5r4OITXL49ufLeDuJ5YIdm6PXyqUkQnJq6MNV2oa0N/ifKnjdMP6ndtRXvbeBRPwtviC+MN6nnzambxFOZJ0WNhFDXZq/4QsvZ74MjBCy/j9c3oaAnoaJwnurKh9Z/9auIe1cjbx09IJorM24DeLqRXtg1SKntdneIcpGTMein3BpKBRwi8QyIcInAU8hKGZMg25z6rHjGKPmhjhEpVjD7HVKMeEI/C1m/DJ6OhBmfSBBld/qanJ16XbXDg5g9+RZwsl4W9RMBJubJIa+iQDsBlH3kOhXo//LcrNijXZ7l927suINWNvffHd4BoMSzeg8amN8kQfIEvZTzd22XK6o7m87P3IR6new02Y52VKoZYeBJ/W04nsyDRYnn0Ux79TfXQDkpavtWUd6EIDayAWseFTiUm169t21PNXgO6pAhjvxuRCv1uOhBXqe9CmnI4BcskH5KCckvCDjRg6Q/aYR+mqRMVlhy+nUmXBQFXDFQJZ4uhxQVLRla73mTCzz9Z8K4OnXEjVU6VCTUoPuWtw81yOCJjRNLHBWp+5knO/TwP9m+s6wrxVNh2vLSwisb6H/GQ9i0VyzfIobvd1y/eufOrQOKaBANAUscBQDxfBM8cnQ3xredY9V7y2eaZm6WnseIpKytg9e7EqBH/yXEP5XxMsBJTT8ylf3f/K4Ghgg4ERd762Q9jDzp6RFYJZT9ni8Z+so2aGd1Z/leGoU0iDY2m2zYdvFXdFEtQbOnLBYEUdkvhZPBViklRwRBDmEFh415oySIWclmFArORR/0MxuIhUBVihYO+aNSXBQroy4XePpltu/JQNBNpShUf4R06tV9yPQnnluQUov0z6kZ3Ljw+X0LGfhvNq0hjoBJ5ojPnOR/ckkiYLJRYv2EMEugxGNn7r3XdhzYce39O3+8c/u9939f5LmZJpdMTbaVENEm4CwBz3UtKDbcXEd/65pGzZY2RIvV5U5AKb40h3F8xdsjhEM6KvtfP1y/n407bFF9aWPpl8/C1vOivwSedesRaELaWhviJAhgDTrj2TdMZaYs1B2FBECaK4vTiISQq2CGPZKTst9MPGVz2JmPSGbBHbCZhILN6HMLsikZdxDPeOBEMBqKibcqQ7GoBfoZMlv8NlfMbzLgFoEaLSLbBvYIBQqjmv0z84haU+e2rol66CcveDofHUa+H3CW4oWE/SbGqXpMRYPEwWXWTQSUV9U8BM33O7qoROjUu4p+Ql01jcpeUY5XfCwwcJw/XK/Zgn/rZ6iAR4eAzRVOhafacAL3HTdnEV/SQsATB3h4p4NDhzW4P8T2VE/uRmPpSbAlHQVPckXJpx/M5UZGUek/NdUfcZjKjlaZ3NoebTN4Dg72VdF0nqDDBXsUt57JSw5a70FZMBsLRtksQD9N5iWbzV5mVniGTBLsEW7s48ItAwrtsH86o+CZwbmDimO47thOOeAUqm1ua+vz6N0IwQTjRt9kI5Y7nY94/ySKKkxWUOaJU7Gy2vEont8Z5no+ZL79/oEDr91upsvQbRUf7wBLUtYbOzQTeyneCdOOzwERCyWgQrNH11YgdN/bsPvt2oquCHjycFvmae+/cH42Z+9hnDSZuQN1wT212/YMHcSyoIVRtw4KeuQsNlBsonRYOt8536wvc2eo30pZ14WZ0VjIWJVBgTZCv+39JO2VriCGmIWCeXMgaA3lpZggnHH4QdUfcMpmMStn3OYl2L1Kc8iUjMZF2SQFJXOEeIpluLsX81lb3GxZLFMvDblhGHcGRfRK60O3Wq++fXIDIg/BoG+ukXgW7REUlDUUAv1EYKRo4PeBZtEiqc4Sy86G2zsOvHamqxoTmVEBB7YHbvz+i+//cemQpqF2YHzh4qlTl3vH366tpRsg9AyiFmx3H6GU5LOGzq6GBq/LVKUMJVB+1JENl+ZyXr3aSbWS/Hnn9XsfNOw9uH4VVYR9b23JlnrNySPgCQ8fPFvaRr1uF+6Do0ihk+OZdm0oGclk0dahx6vto3YK880Gvws8Hf5CWPJbHPmASxBQqyDDo0xiDxDdoYzJHHKbSD+NiWjIItuIpz8uBQVLpRyNYv8ET+siWi1TVzgq7VgNPww8sd6RO6C352brUM9fv27OYcptqKdwSJGnilIVKChZeAb6MiJSDNfthH6uMrwGQ9RH1/XgWfEQPH9/+713l+NnNGuRz9xARS5PaFQPD4DjWXoNv/pgHAWE1eWY/9juslVx7SZUFEsJ9VynJ/vAU6f6Sx3wl2rWIle2CVMAhjZT2LNEUw+iygEU1wezblcolB2ZnhzRS4lkCnmeQhRF9VWG1T7iOT87P6hDARfdx0m03h1SOiGKZ5C/CP8z4UgKoi3gDwpL2D+rhIIRqXmWQiSJ/x6l86YV+hkXh1GkbLYaN4EmOSE1LNDP1c3enjeyRp13pG4NLBFMUXusbLLxS6q/NIT9U6NRePJ+h7gdb6CMk1f8y+iSeh9XcQuxjzJOfUC6bC+M0Q/eX76Dgj6rZhecGIR7Vy0sIFZJk2HaNtCK34xRBgeh6Xs6+2dHKmy2qkpe3DreP6m+g3hq1eT5DjjmXev3fYk8z7P7j5w9smXfUFMpBRLRbQg0XzlcJiXj83WH8Te3zdmiDgQtBZfbpKvS2e36rtF5Ou7a/QAal6FziaBVHqb7oyUwbbW4wshJtPpPIA0nb3TEQ5aCUaRYE/JGBEtQQvKItTIUSFqGsy7ZbHEiGZmbmNmLPO2ICVY5IwZtV8sW5cQ54zLMl2uIJ8dDeKkzUj4eUXzppUMPnjx2aOjlYt+88wo2yz9urSb13NZQe/f5Lw784OGV261IrNZgYAFdK+9+snWICt5p+5wBT8zmK8GWXL5nqmuyZhS8KoGORd9Tg/q46T4+vUMM4GkzupvX19XDnMPLwxI/otn3Oj5i+qw57H042p2orEMhaB0KwLVxmzOVDhu1GW3VJrQNIZ6jI+3aiAsKGgrCvvutMpxyJM8V0mkLIvPJtGgxJgoJa8EYjLign2kk1g77k6GEiKBUzNwKngFL3u1nnlpe73Z1/0RnIY+vp8ofXTR5mr6s8NzQ4Rst0Sj75+vfAk+wZGEdZfeTl3vxBK/qp+p34oSJDfOaDzRpOiNqwZ6/84Mf/OLO+9dQSKHB9BIMMa395Mm2vZvpWoI78mO+wReq9+Av2Fs91T+V7XNCP9XZz7SUUN8x0YeVWuSZsUVMXZu/eRgfOQRNLinPkdwFDtTis2+BG2krb+SNqikjxWOYCZnVZ3SVOjStax+lPl7N+gjNlCKeyWgrapKiZgEXdBLZo2zILBgdWb8gmZw6gzlvCyy6RFgpf0iIRmVb2OqWQ7Jl2LToCFuhhyQ9XuZJJabgqa90JRNy2QUsd5yS6tv1NaNNGg2IUn7dd8kaFWlCQ0kHmCd1hUVvRALKzjxQQjvRBX6AXKV5KOeuV3/46B///Mtv33nn3LnHv6dx1RraMSsaLj9/1ltRlFo8irJrarRN63PZOlg/seA5vjR1cwJVvC/CSwhlurvW4zr48CvK+VeN0EKAkAgjxh7wlKNSW7NuPo4Zm8idr9RnsAGj4LdvZH4e3ft8fhd4Okg/41YpkMbJCPtnUhBcARt4+mVXTJCzuBQVCplkVBLzfsmRF3GCksVWtyxhvdsi0bBl0cDnDJ/aYRVcfTm7r8zRLaT9zZ3rkD7VMov+q3Nr1P0TPNeo7hKAAiepgHIhpzqg7MwrCSN4gufO6/CV/vgF6GbF3Wf//PNvHz9+/A7k8S/O/QA8mR5qQ2tViAQU0st5M3d3Hdxb7tGDJ1rqE1E1HkL3R/ri/WbVUZsz0z+EeP4aaCg/13D+BQk8UiRgTToEc8DUNTE56omkqCrOcUbnzWhJ23XgOYthG7ogzTUkfykRsRaGETIWQsiWFcVgKpAUrDEkfoEnzvhiIJtOpq15ZzIZtsQcCVR1ZKVkwjJsdIWIJ09k8gGmFzx9fN7UZzKzXbJkM/h8zf3tNXptcxNmj2mYJ53fX9p3js6vYfVknOpqZ0tExSt4bN55G8tdC0tUe/eTPzNKlsdQ0nc0yiRctE3etnYXGauiAGhv769+iD6LN3NeP3jSUmeT5POin3+RJ4XnqxCed4+8fhgkWYgkPxgnhOqJY+lwMmbKRqLJVMHYEYm59L4q/SaDz9PTNT+KJn7gGXMFeb2nYtZCIpywWGQplRbFWDd4WvwJSYLL7iozgWo4kGgd9qeIZyghWbabQlLIcgzl3qyfPbTeAbIGQiWd7T69wXPz/GwmhCwKE4INupGSV9T8ZDU+z+td9ZWw4WPr5/2T7RFtnqSd3KGLeK66duDAra61sET3nr/Aee6dx4+x6DVUbDmExPchatz3Oun1HprkCrtVAV3dqdny5Q3t9oitChEw9j+poUA/eHp8qnqiVPUoqrJG9oFnPbNkmPyoVwTfNc5pXclUHqWawaz97vFT0YjeV+bV6Xx6XTPGaqFuWEfjzsBTMqcc1nyiW7JalpagfKIfk2PhheZDMlxMo1cXljLhQL4170oFui3BUCoqbs84HC4xDX8pbF3EBRdPGVEsktcDLx4t96Ywf/1CeyaZQC9c71wLGnHWa9T8Jejny+MmLXV4S+oNJ9sjVk9e7XhBf/3N8+R8NgBQ72efvuR5jt8019Gi8mLP2721u7dia0Ao/yC1oPsuWnJv/ua3Xn9ly5Y18801sEd87GCbxPHkyZyS/Uld6TNY75Wj+1o0W0i+hOd+vFEWMD04N/B0acuGuZH2GlStDfT2Hh+/3GHU+jZ50I3KUNU+QoXpzdoIzXgOxQLmlGxFIpgkiksSri1FJ/Zbs8VViMWEfDZbozNLhoSUhH1PyAlLLJoEz6zscAppozMatrrwEQGpHupJ+yeO7z3IkepvqT97pL5lzqabm2prWVOKH+5F/lLTKy+Pm3zUbHwZAOXzEfMETTJItOibcby8/eqrFbUD9z9miOp6J9GMjS1cvnjq/srKleUHy6euLl+5cuXoGz0+nX4BczkxonQvBkk2+2HfDezQc3wJ/RmmPavVwXFoKIlKa8OcBraTr7gRZOLYGKDWQzT1p8m3u9S0oXPv1t3Q+l6+86jy1eg8em1O32FHGwCc4HU8NNuBGFwaznogJYMnzAwiS4lAAH4QUujCBZyPdGG5MiFL1rwR/rwlGA/EBKz3UEw45vRj//RrV/cYkPWsTqDAmz7n9Q6exsH3y/Wz+jmCyZsQVpLqz69bU8pESTWBkrUT0lQMJzNP1VeCVPfgtH6HfxHieY6EeHJRlwb5dF29JAMDeOD5tgddazquXlmGrCxfHat9dVdz0KYWx4EonTdneL1rOfyJdpLo6LVprvFLdPjAA/E6xMOgpxoWBEPwwaP4p24D8VRGuY9fJp41Ol3OXuUd5MmOWhfiIdBPyZwOWZbg/wvg6UCkbjGP/yY65az7GHh6DVjvgXjUkrAlY7KImhuXuZV5Di8GZbMVPOE2GFSerJ+oPOu6dOTs/i9rJmfLS3kRUTgZH7LC88X2yYlMDFO1R3CYeC6r4i4RT5rofpV40jI7fv9z5kmqCZyo2X6s6X8htSyDtb1dAyrg8YXl5XH8wYd6LjSZ4wWu6OfMXG41cBJPrPeszZTpP7ivUdmDsFheYYvJx45SCteVksEsgYLCY8O2c3d84UmZr0br0etzNR36Zup63E48o7EzxDNqkaSA0yyAJ0j6A34ZryF3Jr3kNukrD+URCYlbhp0BKpKJBvxm7J9yDOdNf6jIU6tXB3qstvf40Km06/RZ6KemcbKNwsmgyZu6ylNxlViUrRNP4FQ3ULqLO1hMtKX4R8MD4nkcMqby/PzZyspTSkV+9g/NIHdvrsWTYEJq+ykFFO/Q5/Gr148PjI9VRo2ZMh2cOk741NoVnsXbDoRDTMZsezXK6UoBjraelkYEZ3mK4WkS9J+eKcHdyVQ1LfheJO4+uV/mrdF7kGOkP6pv7h9BQq0O4WR457TeXVjvBRvFP6MgGQwsymEx4nC7UzLrZ+CoFAyKqUjAkbeciUsx83a4UXFh2ET23a/DD0ZOnWLee3BBhVNSVz3WOzhOHaxX7uLU+rhGioe8wgFQFmWxv3SX2Ftqa1OS62j+MqKOtQ92gOc4us6Mr3z+zjkA/SuFkZBYj6JADe1d/TjvIcRDryMVI/14g4EYrQDWsTFsAGO6KO6LdWp1DHgObjg/l/Oxt4RkWvI//abc1j37kJeOn2gN/3gtTfiYiSk5Hjw07sIe9EwnmmiCcn9FZ4ejrc/1+dw+GkWa85YF45BQcMmcilikgOQ0m5EPFgqDp002g2fWnQqYbHZtWCqT/E5xOBLAeTOEEz/zDIrD2UiIeCpF5F5uHeKF2+Tr860m/aRM78YNpVv+Dai63vHz/ttRk5c881TX+3p0DQRNwrmVtk3mOXZ8fOweeELO/bXWU4Nz0bbvgOfc3Pzs6BxykyfapimddgK+9cj07BRaJM/39o4PTqDrhi+O/CUckBgnBbjbiKeqn+CJcJ2tZnd1OTrXaBCPxo+K8UbYdUrYH+ZhhlNIr9sKmgPjjyhP/9lYHyIhfT7UqFd5uiif1pOJRbF/yojIp4NWSZKcYaEgR0PdWOlZUEUA2p1CSM4Df6lDivjFNOsnbkT85labIxQR0yYb+Ut05qgyLRp4A6XWQT1dNTVdl86ehX6++eULTeDJPjH2T6RrocD4MMFkorx9qs6Sop+EEzT58h3T65HICs/83QOkn+MASvoJ+evbZ25fRjHHRx/9ToOdC1+wB1j3uBajqjL8uZmuHLFzLoy93dyVM0TBU7FH1EOZ8udnPT1a5snhJfD0VuzafLMO6xoCjhhmSKOjDm7Y0EkpN0jTnSPdHF/gqodnj37YXuNZ3edr7/NW5gZRt6ov88tY3lEpCv10WlAKHwwL6L9IJGUTXl3xbCYRyNo8sEdVknMR26UUDViQgZsNbwdPv6D4n36D22VDh8FKHxSUWkTpL6L1QfOlI0fA88snp+fXbWGBq1y0R42snYp9L6Z9vzgdHeykibfEE5MfsPujpfx14kltfBbAkzz5v/aeulpbi+GlP/y5Rs/iQ5UJOsTgxdfzYlDo2MD4qVMX9dpKmyPiVkeuE8/VuD+a9bzIToZ9RyMafW/Dqs1tENptaCTkHg6s0CsFDpR98/IT6nb07NG9j77XjrOgR59r9m7KITi/yRgMIRM+kUw6Aua034LeAn7SzzhWsEuyheC4R03uRN5m9FaGA1jv0E+j5AhYEHF2hlud7C8ZXTL+b0OVPxip9Cnjuqg2Afbd3rxuP3CC5wXdBQWnBjzXUL02+/P4dg1ZI7bwEMbJ1qgNxgg4ccrBr4FtctfWajv5nzDvzJPkrwuXx47fvVuxu+JXGu7rXkZpsXx1gb75m/DEHxGyv3vxihv9Io76Q04MdIQYiKfejvky8x4lvITlDp7OiFE3UIv5cRBiSBjBkZtwgOUuBKypfESpyPnkgx9/9NHXu/pzevBs129CfMmLehvE6pLw0x0oy4yICZWnAxG4ICJJ2EUdRncyZXT6tOlkRvZDHf0yeC7FZKz3RQf7ny4J+2dZJhJ06uh05KUvaIgdNSTQT+K5brZyQkPu0st6beyfLIiDME/F92wpnt0pY6mTchfw61CcHXHhdvB8j3iOPf2cj+2frzy98vT+wl20s9H4I5QriC+jInhHD04jHlcvj1+N0pTmIFZTtgM03+B8b613cPrmbI2Pvc9Kuu0wYsK47njvqyjPw78IiLvW0jcodeT9BjcqODQgWe8ZORUf/PLHXwPP5j4vKjHA044xzpVOP36EYCApIfIuhF2WYVmKh0UJBdzYPwNB8MRNyGI6sRjRatOprIx0ujTiS7TeZVf3diMqlYVjEb8MnkczkVhkEy0xEK3h+03f6vZG5lk/XVI+WaoqKPlLhw+Dp5oTQk8WMqVcOQGealwePAnnVsJZ3Y/z5q0x2PeFDx8+fvz5uXOfLixcRmfSJ/fvfaLBzi9LLFzPr4iEZ2j58tgfkzC06OohR0wdNE5fzf/smmybr+nhYB20E0348ZkYcDsCG6dcmXDMT21md1yFqXhoH6AoiXni7KnzYb3rff2zo5X0kTqDAVyFSAUhHLIm8sm4WcQlJwjFAlFax46gP523RfTa7pQbdko45pdCeYsDNfTmjUb4BeIh4ml1HTUFo7EymkwBoHS/qc9hYuj5N7HeT2o660pxFQeh8xHz3KfkM7BmKuqpGHeVJ0+0xoM3LeZZXb37Gq6LDArPd4jnA4KJMrgHAyuacLibH+Zwdxpf3XjijyhBTb1778kw3s14StQkngRrnuz73PRIDfufXIyQzaKd1xv37qE0DnIXgs1koBenoAV8cGgICZpPn3749P6jD36idNf6ADy9PgPiFH06Q/NIc4ZqEyLRRBq9mJaEsMOSypuRm5xPppEmFkwkEJ9HuzsJLqYrazIfMiKASqUyyW5UcCPRfmOEqxKcLvAM2lzQgSBaEFVpgZP6PyJ83N5UT7tn/VR5/USLEvNSy7leJ/1kBaUonaqc6lXcS/3csxc4IdwKZ9dDXGdegft5j3g+PHfuORQGv+fKh+PPNNYXYiGxKk+03xTvjN+/1YrvRYsY8Ns4QZmI6pA/Pzs9QvunVsdDYDI2rNY3Hj36AFWbEC5efISqUFZLyAq1hfzwVO+qrwIm8/zdR9/L+XzcSnpTmafZsymImzfkKwr46CTimSiIZsGSRzK3xRpPmbtF3MiHu3FFFIsj5mEOg2cYQf6wdSmUkFGeGEpQ4YJfMlujrlQ4nQ7IMdum1RSeh6lFp+D2GR7KsKWzXDPVtKWecao8v0Vx20ZVeOvE3kmhOsbJR3fYdoUn1JMmuyD++Yt3xxT9fOfhO+eeowvxygqU5pNPNBsV2a68budvWFpvP3nwxdeUb5MuE8+45ooE8JyYmu/r0XFnBjZHaIl6/TO1CJTkmSJPIR+y3B+oPtjU9JWvEU7sn+DpgeJgKeYqDTmU/MNbSnWTmAPQTxd4okOVKCE+b7bEk5KMV8Quw8Mmf6VPFruT8Yh4LBhwFNBUJOlMb3fJSYd4DJppbnUg8IxkHTfCtHR9pCeeOIfNnKT1fnJDZ2lJJ8KI/xZe+lbdGl7qRWkiX0mNhaiROriesK3Mkwzstndx+X5nDPvYUyx2+EsfIwP5+dP7K89w3vzi/5ONtz59iKQxNKXfaE0GbVSApCYsgueF2ZxecebL2J33u5Y/xPYI+cc/nj/HA/LgAT0+/HDlVPNESQsOHoe/AZ6soH8AT7izXoR+qgwYSdZX46t0yXIAKKQlczhiSRW6wbAgBxKCxZEMYf+MOvy2Y8O2YFbrTMUD0ZgAnnLemod+hrfH0FaA9FNG8VwkjmSwN7T2GnaX4N1h/9TWzLx5dj9NDZluaZwjD5SrO9j9/Mo+mCLVsBeDIcWjezGytAfGiHAyz13sgL6/AJ4r4En+/IcPHuD3fHLv3meaonLewJNeVZo3tt+49v7D1yBfvLHRkowxT3WiPuzR1GiNntNFiOdRG2zJ0scffwp78xlr5ye0yvEvXLQ3j8x1zmCC3LpG4sk00ZqeeFKbD4PdmzHg+D46mPOalPy6EDKWZEs6L4RFUZa7w6LVEUil0V0JPFPDi4VjaRR7pEIh6GcibUaVRzotbozK3d2olSOeMYxBPVpp6OHxxV6aG631GXSeGaRWgWf9ZJ1mV0u9Evtaw+nJP/0uYSz68izq9vky8rlT5QkhV3qB8hcujr3g+eGTlftPLi8cr/3gDxodhbJ9cCvfuM5y9MrilcXF5SvX3732+xtQT6AVwsSTCxLAlPyliQujXkU/2R7BvXJ2PLn/WcW2tUjW/dGPvoBADGo5ZqdRw1iHuiM8oZ9NTd9AzTh4/lLhSWE1u71K10xzDHKesmAMm6PkOGMOJy3pBO6PEGUKg2cskAoL1pDDb0qlF6WwGc2tUtGYeMyVSIGnhHZ1Gx2hFFLCg6jfbI2WGfgzpxagsO494GnXaj1NKs+pyc65FtZObh/StO+b39in+klFZwkfPBcYs34CJ9STeaquIO6E3obDdO362NhYkec4ymNggBtqf6UZauuEShcbE9CTnEjEmvTXbrymLHezkIwbs1UK0E283tvmkb5k4MM7LXc4rRd/hQbMX0AbKJqB8Erd0Ff4/h5Je4BJPEl+yu0eWT9/VEPxKm2Np1LX3N+FS7I+gyvmcsXlKHimLMcSQtpiLUjdZktrFA6b2BoKuWypY/54zCaZXelQUEhH0tDPgtydEDfG5UTAciIWk4TtsTIdJ1GjiAtEef/06HV26Od+4lk6Pd1C/ZcUc9RSB57I94Zy4gk95TywF9WbapHMHsW67wZQwEGAaeD3MEjLC6yfv8BivjU8LEWDkaxXX6Z5ddvuhgaOmZPUoqvotm10sXz8yonfgyeAfrHVHIhjCgHNb2Sgvnbw9PLdEc/QM/kx0fnUr364luZv7qGkiJvle9bupII4fMQbUGerFMcRT1U//8XW2Yc0Xsdx3G21llM0V7tSu1ZuTd3asHwIpudMR2eEmUY6tbK0SOlJjVLQKDlNNDQ1cZOuNKJHg8R8KAw9mAaidHBeUfbAlXdFwh1EF0X90evz/W09f27u7O7yzvfv8/z47guTJFb0xenWJPylVpIG6cZc6u8sZaGjDmeo/NIqwZMpzkp6FapSKo9k5Jqr2xz+NIOtKlieZUPeo3jiL9F1Qxe935+TWFCYDOMjdLI/WarvbpfOqit2j0o+BDxhTJKyij2V/rxc8BQFGiMteL9PE3hVJ76T0Ahp47BznRgl6hwfYeAbN6J4si97m5XZpSwyMabZ4mhmB/HJ0zvi25w+c1wqmwkHVkJLG+3bywrQzNTEnFxzfRCJ13qUa8AT/RnFM1nw9OeuHv/ohYsp/h0uknzIBSV1F5OWuaCIUJ59Q/yKLOG7/+E3H9bwfPmFXtW947bqkxq6K8jHuANUj9jPIHiWpl7agdAXdHRQL67M6PDBn7LrTvC0uH1VSeV5tpR8euzzCzoyqsvRn3k5tN/6fOUpldMKT5xkaQhTQZIJRxl7pOw7gFLQjqlP8BT9ed0/8Iy6n8q8gydwgiexnmrsAi1p9pIM0/bc5OTOyde+2F+a6wvzFJ1mfWddbRyhNkex6wiY+hZOiDEOTfb1hbY2trlO0B9Za5yHP1Ny/GTgKWxoE7G0/ZV0phti6pNhIkqTm6dIGjH9epj+XPm33HWwm4PYAzIBW1ICh8qvPQR/Cpzvf//tu+mobJxDq8loZfC/tSI9CJwOhw88EzOuyKcrDDylXlzQUe5jOx3Tx46eNoevMGm6Jwn+vKLfVlV6B/qzOgv9actxpCT6faXgadR5RLUzi6Au2LqtUuV3Hbtb1Gd0G0ucqmhr9ugR8NRsUSxVp+XqRHnK+WXFnuTB5XourEcAzdr6HSL4yOrkguDZ1nZkYdNh0XNDYqxuL+4aDnPtDQa29teZqT7Zv72+vrG7+13kq7X5eY7JLa9FljPxl/z2+qB2AVcCpGz4E3kXgy+zxRZCb9/0xvlzZ5669bDgCaKP3nTASqcXR4nGxgaGB2aH2GN6Lzu1FJxvgecCGX+mWdxsDs0GUI6f+QspGPv8rAnIuiI/Jz8jJbWDlBN4lvpyhD8VnjgApT2m8iw/eNLXpPC8FHtUXnhFv49p5EqbyaMysyxasxIf4eTiSBisxzT+lC5vwVOkHjjhT/AEzVgRTj5iheJHVaxJIiRaPpdz2QeBkxzuiTUM0hYR0RdHv+rZnOkKNXAeO7v10Vu/juNSwk9fyJkBGHFtLZPPluVjfn7+6GtHeV+bX6tMLfXbLbHLjeDZPD4seBZreAbB059X1Q8/707eQ6pOXcFp6TWZmrg9RjPN2NDExAB012Nvvf3+2++D6M/ffrtg6aIMafAEA+4yNbGd5HDA5j4/vTV5V+R3VPkSwRNLRO8NXcqpGVmiP33lBMA9zo4MX2KbnyHYVGrGeT0FhbYMLwF9LnhOJ9VQnZV7xyrDJPoTSj8mnZMwJ8S7Fh0B6OUPPE2bpXiej0eLcGp/iNb0GWPPO1UiWe2Cp1oRPzcX2M9EgW5vLW589dpXPdPTlhaxPHe1tHz0ddxPP1GVAz7M/jzb3L8QJF+b/+Lk8mvQ/Pwyry/6S/PsOEwx/qxpJt50a9ER9zLrzeCZlVi5lrnWtlj3qKA5OzwwyR2FJna+j5H+7wbSkYG62x5+6933X34fYr/wpNGIvnC7jYGa7Gz0pyvo8AKoj3gysTy/lEVMWRmOG5kmmrGH08K+abu5yxh2oKodjjCTDIFwONkZNPu8Zm6ohJMDwWKL2em0H7li2gie0XoHwzJ0g1E8hj/vVngCaCzaVOYIeWeZuQj4fRo9oNn0e+6/U/Z/I+rKg78tgfRx70JoNbBk9ub6j+Qfko7ktva25aNfrbf3rNK9UHfxxSyh+SDuk2+g5deOSg2ZZMlv3/xJstAS+un3X8/m+Oxp6sYo8ab4yuMjTelRPLmhJ39FBhtjGzMjGxX3MLEJpAO6QHr2xEATwzByAZP0fB17M19/9/WX32Zv2M/gmUTGfzA+3hj0NFR0tjY1gKfXi/705RPdBjEokhJWF03UtQPFbVaIX9E2yzeUKWIhkCKcTbcnKS832SCZL5lHUHi603UmqnNF1DoUnuqHVDeRd1XueOiaW1VpSDJxuEN0HqErhUS2Ee4VqHdSoMQv9zL/QDPwpZXgmYl9mQfPUGjl4F23JnB/q+7AZ3FHowSYQPh3EjA1QH/LKbwxrUs7xk6EhLwPNbl1UitWeNoLHb6MgsbG2xvXNlouUXgWjZicrU1jA9T6pM43AZMerOPxffD6yy+//fLbP7/87UKSXOWq0QWMbvCsaG1OzvUSthIsOgKe5gbKS9lyOJdz44y4jA9LcYvNDiVFcmJ79Cpq0FciwU8KvTKUrWAVD96ttwel3wY8gdNjEHtkILBNn2U84kpIgYm/BAmez9/39G215BbV64AiVSqHVspWegmB4ud0rIMPmKlmq6FdGzOlzDuDZ2NlZBk8+/cH6a2pa1mpTbjk4Adxtx8VVXlUcWeM/uweiUL8RakPPOX8mIxtmwwNAyOCp+z8ZlSm3uynq7XgduBcZwoMcQfRIVOwic5mzjFzqrWTAnsnBv+uhHdf5zTS5y///OWXC8U8Gg6ZOU1WAnjk3cihBBjdZ0721rNOxBxQx/AnXnr1Y+iZj4WmptRPL/Kfz/yNSioUoCyzweVMCqguK1V/9+DMWwkcKCXNXqn4U5SnQpQm1ei+35sA8i+KdXYwFOyCrOnC7BwTsDsoZhfSQG2bKWcu8gqZ6FhLFTzbLm0z6uuD3A9MynbFxxHZn1w7+tr8hzECWT400uD85qtqgmcL1/HEA0UbIe/oT7wlKBn9yeh6ecHtjW2rLQl190Tx1Kc1MFhEbA6gtMsyCDs2fmvCZ+9+++XnX37+85ffL+gl2adnCMkNcFyjS3Z4ocJCapNGq0u3r3eBZ+fw1DP/JJDU8P3rV4qaWqOSz+E4T0D5n/CnWmhEDlRdS5vlpqJG4Cnyrtl30nW3KRwVqFiVGHPycBSc8YSBCk+vn3VGRMTshKomamsEz+XIV/OvfbURWo2n4cjOvQzkIw7GPSofr/0fgaiw63KV6P2urqDKgGLfEWQ3ixkAhPPidCs6fAQpBYsJl13MuLvUVw8PGSwNTXR10urF6ayyhb4DB8cHBmo/e5e9dhqeOnFnTcY0Y40cx3bFBymtIO+5OtlUYbUkC3tWdJZM/cWLgqLC8e948ukxAlZI27eUPmjQRfGs0e4Bm+gIBU/MkaJYsi6KJ/wp9G9Zl5N+LtUvPhdCrwWob9kEUJF38jAphyIb+JUnwXNzcz/01MHalcmWu7gpDJ6KYmoUXI/yA5JPoz+DJ1vdZWY7WR3Vd02MtSr+TOZaCfbInuvLYOtE4OLDcsahRCLMEUM9anGCa+Eyu7FzouVA58TA2FOfffvjme+//xQ894xOHo46/CSDbA3ugANzTk+YTuyMS28Vq9/aOTv1N9ZUFIPxLzrWCpi8IPCU/nmZ2tXWZasTBB6F55Wo3D9VqOpXIvER48/WKJwQ0i4LCoU7sXHxc6bBQWMX/MntFr+cxUDeyXVvkZ7f+w48e65IHTx4YK938uAlN9R98B88YwSqiuS31kr9nMm0aJfZaVR2jXFsWD8oe9a6nIE/8dQdlpwM7Qvscxo2hMuauseQdlo7d87ttbTyWdPxHz/7+vQPP3/Jj4VBIwSeyR4mMNjR6VQbWnK9HoBkLyIMJ/d3hl79E82/UPyn/vx4VPFn1ChZpc9KdVXGa+uMkpOLSYEWaVjG4MSdV91LMXmXBiOFpUZlmi6G2JwLnsldZgbH8nDmbPBndX5+YspW7QfH986iP4+kJNbeltCHNbrz0Xsvi8MqC81rpIEIg86L86khjWdQLfyZBnYBid8NronxVo8afMdESX7pRh+ZnYjpMGAKAethnbkM5VlBBw9w7ky2TEyQ5Vw4c/z46TO//gx/Hl/l0nNQX5wWMNDqjj13OnLFo7dbKzDq2WV8QK3jHNJTNIU1EtJGwrHqGtGl8N4Q+lMjNgRZ0UcKTwoyJgSIWqHFYmpi15bWPBklAKUXWfFnbYwzBU8FqTwY7dgP8i54BgTPvDwbcOaRjb2UCZ51oqMtakdfrW61rd60Mhnqra274a6P4q7IZCsC28urO6r7CXEkTmqUUJNIMwKUijKr/HY5m9MlFmlQr7NODFR4NHnHk2Yo0ssc8KGIkbyHAhNYhw0WyRNXAMsgcLZKz2xnxd6Z41//Cp6/fPn9tyEzzyh50OIN6tzNFc3uLsJ3L7OsbuXew6IKnm5uPD8nQ/QQ66Fk3wG9JrL5oOQYw9AyYz47OyTtJwPjY01YJFeyyLtJXwN7Jqfd6OXWX2G4c3gUPAHzbxoUedfwBE3hTwVmbZQ9xborOGuQd8Wf6E9qvpSCc0rBk8Xjch8Mx2h5c9G3WLu52dWScOtFjzwW11/A75Iiy7FNd7SzFa2/YE2jyHpb4594FtrNmoHHIhHLKXukZZfQAXJOLws8dSXCn+SToGHPUkNTd3f23NKpczu9tO11023We+bMR3tUQxSeCzey0KremObwhZNYQFNjyYU/se+e7AoAlbhqXK6XHHsvujWH3NB7sziqUFPTKN7nk6/CrIpheRd6ZbaCoD0Z/mTVnq6mRi+RGwfQzfUDF131nOhPLivyrrWHACjpZOwRaALm3+BcyYY/lfaUe4NzxcWrATrecm0ZkMIz/9LEVOUwCZ5tTOy2zIVWcEIfuf/ZuPY7wLoyNfGOLP8mK24WN/dZbjOzDq/ObGeqIWTwLM290Q6eCtAkk846NgSeKnoPcGE4jXQIlbB+T4nAyQ9+uid+saZpvNO1dX731IK6IVxRtnDmzI+fnRY8f/3++2+/DgbTGAtl4rcwTCWpxlKY63XwlfSiOjtHX4mCpCxQzK6/0ZkN4Ub924uKKVL4yqiTdYByacTIyV5zPbt3gkPXv3S3ZD8BE7qbDzHwij/fAc8DMTxVgN7SwnpXcZbI7gMn3vxqwGkhRUEnAnCWl94BA6aSytTwbHeE5/gSBxmXu+fiF+KqLq1UJbdLcxzm8GIgtBeClgInlrZn1sETEjwdXuw7d7LgT461WImP0vWIu2Q/g8SbfoWn4dFo9hiZH55j5dDWxtmz5ynJQfJ2+oevP5qkbHfuzO+sdzw+qG6b5fJ1/VhgQ73wJ16zSfCceO//AJs61lSmtvxNPPO/9N5ERVkzzxv+TE6z+zMkf5pffsQWLLnqOdLzf5kjwVP66ST9eROyLvqz4k9jRGTE5Q8t3QegplWYhiAQPDOANAd3niMCKc7mtuXl+deW2y7dT3eJi9uQjZsdl3gFJbjKlKoMH8orvLi4FN4MQ/tH+tt7/sST+qzIuzptj8BbsUfoaa3aUW8R3ULSLGLAHpUoPEkhz32n0fnd3fN8SKXzRG9t3+lTCyfA88fvv/1MFy7MkNEumkLyCuFSv8IzIwk8W8du+Q/zydtzE8oxqhj/fzxfGkNSpUPVGPDJiGemcMp009j4RVe9FONOlGgMT7Wt8v6bNO6saKkQMLPxPfHmNWukxD2EuCPvYWSQLrVyuV4reLYFKtYVnj0dR5achE7htHrOLcWhC/rbj/i52MY1sYAzmVA1vOQM9ldGehiijfGnX/SnpV7zl/TWbmXf9fgiiLvCMwMPVy+rHiDwLLonxKaxk9DZ3Z3FVZ0pFNK5WruzV3eXVnbO/3r+h+/f/WAlyV96qXzbBako8HxqQrifPg3P8ef+F7Anx0ETeR8D3X8TSuEq+LNButCNlh6KtZlSrTlkvpU6wdUx3zOKKPYdPGP8CbVoYEKkP5oldxpVnyHwdMbwFEBLFX9e2rP01O5XCs/pRfdKGQa3s2nsroQ4H4sOOWgdDHIGDzRZp9TlTAoUt1UWtP2J5yH0J3h2KXuEPKV3j4l9lzMoKl/npd5wReaa8zB4lihAwRM42eF2dne1gQwTNgYj07CzuzVXsXPu17Mo0toml7cq8QoO81Sm4mKwRIVwDkbHn4c/R9GYsZhdLXlRuzPe6FZuJupVrL46ePCSkNoyfO1FA3K9WEcOzOJoK+CLyjHUI/rha68HTvnjUdOuUvT4n2xZohyXELVGRO2KPYWae3tlsYOwJ9pv0Im8gyf9ARDhpgDavnP8zHeC58x2W9hjYV6YqrTVEIcMqypbMX1ddm896WJnUD/oaa8s+Bt/lhcKnlFAkXfwjNfw5NHJlVzwbFyr1/BU9mh47rvdL7767fzpOVd2k/iHJO7ST28txmc3gec5Voq3NGXX55SjjapLRR1RlLZJvThLhyKiaj9bJHt2gIk3HCW1aWtgQrwoYVC600lbNZW5ad834G6x2kwYpFUWMQdZN2630QrBlhvvjRZXiZz4BU5A1FJLUQc0Wj5KUGj+pTwJ3VeaXb3p6aBJtDkHngHSnjdSe81BPcGf1XfAn+s/fPn9+a/Acylk6TIW+nD20gxuQ5za4auD9MVBb6Edb52AQufuSE1tizTeHsNTXXElQAJP4iP0ZxN4ivpEtVhQfXk5qY2ZZsGT/LwYpNm5sxtffHH23J4ctic85+at6zSOKKYeC//rD5O1nMJ01efxfWeYy++orqouzcmThgafzqoSIRL4D4++9OKLw0VPvvLc6NBEE45ShXwt+BdqsCZ5j/SkcMeLpqcjXqObvwiCP1UF20Ge0obk1Rc3lchJRUnPazePoniK+yl4PnGwNhq2VyjlydfXsq7gCYEn7jz+PF+RyF2RwJnY/sO3358TPGd6+ve5MNzSmnArZZE4nXar0molUrMX5gZNBkkaG3siqf3gqW1tOVQuSgEGFX9e+UvIO60hGp71PDr2yTWugSdFLAAlhC+ZY1Hj1tKOO1tBAAbWnVM7Cysk8ATPr/FNcaE8wtlZNi7FVNHYVy4Os01HvhgN2to98saLU8j77DF5f+W92W6JKrUoCP3mnO7Hz+OUF9cPke3EPKNL6VaXvjCP45Id3J615Zq76vWd8KfI+90Ss4uYK+ZEfyLvj0flnWhTeFOLWZWvJJFmFE/8pa6lNLMXhwlAeSn9uX3myy/PfSH6M7Ug8BS3mw9edrHkQ2rAEuZpIremM/tsji6jM8kU0K1HIn/hWVmO/2kGUNmqiqtMPiQbPMV5Ev7k0dnK0Z/mey4qerSEpnk49HD8dxs7zWXx8JpaMFjhWgXOZvmvU+B5HHml7K4DT7lyxoQmLX1oUc5Q6Pl2gL+76DnB8dUHr77qFjx3vPU3BtR1bIhrCmk9BZVicoT4mcOT1RZrg1xaKKbrDgtXlVOYSxebRZ9dgv68EP0JiioLEmVT3M/rKcfBnxh3iW5beFzCnlpmWh0PV+IeWkXeKarkxhhU/PnEtnNnzpylrrE8XZ3qaRE8b7uVGxVxSrg6SVOWpQdzuRts76p36p1J+5FIW/9aI7S2Rj+D4KkEXvxPffPEeIVBbwzijgInwpXLnrnMtTB4HqYUd/gwePbu7jR3tsL2JIvK6NU4sbtTQxzJsMOpX3/9tY8yXRM1YnshAp9VfkdpqZj5ghT8kCTKGg3NTW+8wgV8WOmq0aKL2Ef8JIi+OIJ+VEnOZntKFMqCSKSgkqYgjFp+YbqkppIyGDsGzgxHmvi2eteQdjM5BqfwqPI+Y3jieJKBifG+Fmsi7XhLWCMI/1O4JuxAqwmgxJvQNnieX2Yvw3p4Zr+2r/YgvYx3XgKefCXO2tCZUmy24a9mbTo2aV9iDagKl3ZOLEwumLPkyLCySEEYVNfcLXhKbj4QJP1JuMlkamNm+N5HHwXOi+9hEcm9fYvW1u7usma1S60mfef8qRMIJBzmOgeeLQMT3RUNOosDjZmVV1qaQXcNd99T7ri0WvJ3zZ1vMAR65fWjKGNejNsUvfTK1NSTRRIAsVXJIk4zt0stZZIW6DYcSeU2auqlYTeqz1iaT8cOno1NFl5b2BZ1+HJmeGhOFkCjcEISviv7XiGuvIYm1ogH1teLeYc9RdpFf1I6cjrB0weeVVVijqAIzqck3+Y32gNzLaR3acahaStO53arsbck+viRvbzCpRDrfxeEJo8f7xOaTMM1FINETh8ZR97B08P/AaBaOtmWVZraeAg8Dwt/qolv61YzozhWsRJWz+rSuXM7C3wKLeyC5wqF5GxP0E49hiZzxhDYHkDvLK9LnZ50d1kRcF54weywLCiEhtlW+AYs+tx4hbjaSW3ozUP9llaZmxrnGGi3cZsLsqnV9az8NNqqYHdCQ5qUyZrrPdYxGFTxZ1yMPeUls4VKf7bUIjeiSpTyXFnp6+ulr1qzRiGF5+oSJF4hsXs1IZfYI/VEj8Kfm21tS7UtBwC0jgGQuGIoadAYJG+Qi631OZbmaMzhCR1XaNL+LngW0jEreHaBZ7IpvVP8pWIUKAk77DumT/Rn+N7DQlQLWVni3k/PJgtnxba7lzbOnzoTz4UnkarJc2fO/dpHKs+dbGYlE2lFvneuHCinicO59ZQmx56ceuXqopFhCO4sGVE0CqCj3ax61c2IGerxTJBW4jUgl7ObZ9ChKVkG4ndvR1UOBG+Qs8UxLG4egT8lJALSqLSrbAj8qeEpciN44sz34X2qMhzKU2PPEAYeOBfBk3Fn1RIveCam0Mg9D57tPdPhlhVmB+6661bse9BsJm3AaXBSEeKybS7tCZpknyHwjPKnXfSnpkCT2GQEf5qKteyS2CPaGbDv4UtgToi5p8sO6maMLis13+YG6+rGd7un98jatGKCWidPnTl/uoWZIz0hti23MIswjqNl0U79/Ko09iIi7VdeNDAiebkSEfcRydMNX/Dkx88NcG8rub+Snjb9+AA0IltLh7jy7GpH4vOdrmad0UYcQ6zNiIiTcgyTU83HriY80oy65jLxLvMcGp48ZjXAhrQL9Ym8x0PKvOsIkBDDv/iTWQPsEQSiJETm93UNRKrgSdEY/jTnZdi8NzoQPOrK+VV5S5PwJHt5AVRjUbjfnOVziH0HePJLxVr+06TO5MqjsJP+rC7IXHPcepjOvroDLQfo4/XM2M1Ot2eQWqtE8acny0DCimHaQY+f6eOsRr05V0oyWcRwEnHw3HPCRi6rFLs7iYlGh4aRdkWIfMms5D1HX5kqKrN6/KkYdvOA1hyloBZkrZFDXPFif0aXDTghILUHuoL8m40V176k4QmOfKhknSxGV/KOMy8FE03ao+w5qdx52FO4c5V8MvwZ9ufhzKvoSAOUWHmN4kVqf0+7X44KljVle+Lq/Tk5Pr8NLpGyXeLM0uRTLU9B4EiPWC+DC4NL/gy/165lQKUipxd/yYD+VNJuScP/zOBM81rhZTffC5KXqIYfT7vZaWSfoXHz7Mmz58+fXmByGodmcu+H78FzJdttTPP6cN/9fOM4n8KfDLYzNDeotw5Pffxi0ZCS9dlh+I/P4FFQe+/jN5qshnYWR0W6mTUdkrFTUOXO8/hA5z4MesRkrbHkCXvKq5yZedYOOoOG8feiBl7hKe8i74LnnQmxyEhhqZGMpQCnEHhikGBP7NF0jgSbMKgSeJSTwrMSz6THTnuKwUr/Z3GaH4uA3eJbSsGL2zzBMvq+1cmdxaVTp0ILmPn9njsyiDftdvxPiU41PGsEz6C4S6Q/bYJn5n4dbRb8kPNLdaFqblSQiFxc/+7sWYz75Fx8vMtas7fw9dc/nD+xku0ho2iTEgJ8VCUeHdKekWy16pJNVoL3l0ZmIQ7ewICQ6FBAG516qdNq6MHzPKJO4YyxWQxEwZPP67nDXW0kKsnT1Cd4ljucacEuEqCuiwRPuBJ3XsNTHevAX3ooIZYJ0YRdwQmemi8fQtxNgxp/OmzTRyQZAgFnDM/5SAHebqHBwzpxgrY4vYXEXnV+dWl5B45wamo/20FXQ19sLZ7cPLu9c+Lk9slNf1WGH3n/M0NP/N4teCpnXvBkaWQp/SEbBy8R3gRSenlC7fbkYo/RsnHyLLnPE3vKldubW4DhT+zu9aYbgxRkCIhsMoRwB3AKnsGGBoPRZH3v41dnx2eJ4EtGBsaHhmdF3tWC52NTz9EoiXWv9MKXoNhdMaHwHADZpMihQz3JVp3TJniWy0dpVlo9psGRVtx9/UuwpObMS3VT2xUCnsi72kpBGjkm7ArPBY07IQy82CP8efhTHj0UlfcCafWapyV+FfuOAj2YEGcK55XfkZifFQaWvOqegsh6pH8fpto5ubmxvXhiiU76kHfG78C+S8YuEADP9M7uZoPoT8FTS3+WVjaubdXdewmDOupI4GWhHi/Qmzc3Tn536vQOdHpnNbS6c2JuYRI8RdrzVDkG7kQ2qGBBWUmuZjdpGvAcH5PemiI5JnKMbe7D8onCs9ujEzxv7BwT7TkEizZk05CDRTJFDlVWD1oNJl+OUqA8KA4uONPSsEhG/Zi2dCXqfGrzxBwVBs8WjQBTjIUimUJT5kiDc7ULPJF3pRSr87V8svAnBj6Kp3Qz0up9U5w+LFcySv1OE+5Loa2njbz65tLc4twOqXoUqPwdbNaN+fMSwZvSm8BTsiFBxZ+S/izlNvPOTfBm9M5qXaitUO9xbm5Au2rbyxZPZk6WIrkGN0PFFiIjSYaUoz155riegmcxHkGxyTX68dQFQ/CnePMYoopiHdIvRv6Nj9+j0NKBPZoGT35BAO1u8GSD53gSN+OP6F01MTwVk+al1ds5aRywuIaYMWEyWzNIAifsKXgeeKqlBZMhjoxLg1Ob6ot5S8XMB9MLhr/knz6iDFK+ZpDgTw3Po43w50HwZGA1Lsnsxx7k+C2mpHp7bq49MCcYKnukCLM0x/0mCh4qgA9AJnfnhMtglPBIsvPiaWVVVS6vnzgImgpOZsjm+qeL9bv7+7ubVDq2oKVQfLoLOBkxXAxJGY5tvjY8tHIF5xWJUFaSh556vXVkauq9kSLwhNCNdJXJkR98pys/Hq1I1xWCZ/+Y2CIM0hgCjyadGJ+w4+P7DFZP0IavlBVNVpY7kuxppiS7L1g2UkQgHw02L4yejXrghocUnkLNfX1KfU4qUngKd8rci+hPBN420/F3BSr2/bX5kzTOLq/WJhwEUfBMplvbn5PhNycZ60nyWWp6exd6+9ghNBiaXIWn9rY2OUVGdk8pUAEUf36iu1kv/EmeFfXkBc/qguXtxYTYlAiO2N72+ubuxhZ3LHY3ATMQMsTLYV15o2kjSPDG9HAGo2+SXkgh2pSxRovRSKtifOcrUy+WHCsiA8rbMD4mBHsOH3vxFdmDXZ/CyF4ZOhPViZxPYOCxShXtXOS2u9NNXtw+4uwqXEWimawkU5ivaja6W7uHwVNRbBfD4w88Ap7AKYgi7lEDT4gtBl7hGbPviyoh0sFXzWfZpoYnDij682jj0cZFhmVk1QD+POY5q7o6z2uhsObIa99fD59kAfhiuH9rbvGL5fVBXyTtxhw/8ZE4oF0BYiLqR90uXcwcCX/a4M/MyOYBrJH0UF5Td+tNx+WoysbiicWtxa6lVSm4yepymQ8woCLsuXkQnjyLU0GzAI1YUFCQD8fm3Zisax69sohkskIUBgXJIVmRP9I0e6yTxI+ug/DIRqw5JLV4sVijnBIMHmrM7DGlW/XmrJ5SBF6uffm93jRdugeSkrxn7FpRoDH9KVstNDyFQVVgJAw6GaW5mhCEgYc/gXNRulwVnj3UN2N40kn3FYH88uqBBJiThto4ssHsQGCi0O60OGztkfbITGRzPTKz3z+9Gtrf3hxc3N8KZ3GdDDgFT0pM9NuMNYOn9NYJnl6qPnccun1tE3lXtug2piGOnz51foOZuY3F5KXAoDRsuNXySLdbnyPfbp6Ni3xUOMj7kq5UOZvE6iwWpvudxYbs8WzuCLAOHzyHlcckvZ8jJMLcBtpl7akklgq75ZyDtDKA50i3k9v7KQ43DpfJ2srZI4P8QZ1cX6SOQCWh3hIobroAm6R8ULXHW8XvD9U+pQk8UEZJqc8Yf5rgzoDoT2WQ+If29PT8GSFF1uiZ++K3386eP4cTc/r0r7/G2TEm+RgkX675xqye/oLt9f0NWsYQ1MUTC2KRcB8sCk/cpS7wDBjpZxhrMJCeJ7lUb1b8KXg2zjTdK64SwxAcPPr61O53J89u7C9KRCVr74ASQt71pXdUCf8AKRtYgNQP9zsYlNGiGq84ZAPvTb1y5XvS/XERDv0s5r2oaPTK9waYSq4hkpgWfra3Dg2LE0rbQ9FYUv8h7iEWu4s9uqBeL139yYOIkrSrOSVn6+wKBgdbwVPzPzV5V+3JtX0Kz5Vo6P6XgZ+r0fCEP52iPsETB7QaOEWFRgPOvzoRIVoR48CivDQnA1PorW+PFEQ2BhfiMesxtdwH9VqyOPem3E8B1Ej83l1moOCkzBGRv8aft6+3CnvK4D3L3fbOkqBf4k8Xm6QFQk0EueMhg07wBM4sDU+Z4vJK4R3vScKaMKua3dlFUx8/+RInqWBQDH0JaL7x4ivHyMLoZLtSoL0SFdqR1DqGvI+Nj7lt3J9KtCW59Wa9iScsvgixO5ERVbGAFMi6uig2tl4k4i4vwVOT92sOaNpTQxNTHLPvMf4sFnu0JIpN8GwX/owFSPhLQPkPSOOysK9VXmNeSr7FM0OXzUaod0VDsya0ujQYCC1umdKI37UCPOIu/lJrdwM1bv7B9ayS4VIZWrDy9sYj8KcC9DLkfQ9ZX3S7DHwnyWrPiB5/TvUQ6hlQx02SdTaCJqlABy9qcT6VrM910qEfny0ZO876RY9g0HTzyqujFdQgEGGejnlG8s8p+Xlm2n1sbSmM6ffcyCQkeNdLKGeO1rfpVuODje7i5hk6L7pKhD1m39mZiryDJ4BCAKoMPCTuPBFSSEjpz67FtHAYh2mmNIonJAnG5RiW8lL8ibPC4lJTxxXV/twI2fj19a3F/f39/pl1XgXtm4HtFH84j3yI/PvqeeKCp+LPP+o6t9B06zCO7xBrtewwZaGLMpw5tzGR9FXQyjIpKZapUKtF63DRAQo1aEEWhElYFBVRQoRWF7voJjoSQQWuYLSr6iI6EdVNu+iqKOiiz/d5X7PjM51u//2316/P+fSbA1311knek/XQ6Kh0tgVHWh9y3SUPoTZKJfyBGOO+EGs5mU1FXDe3uZY6IpG2A6DVxIC401kHoMai7BLHdtz6FAnPl5/fZ8L3rhv3n3/5xReexrTDnHYOvWbyckwVq9nq7fdruXw2Nk/IR159fpBb9uMo+1Ht12u8p8eZTfPQaT3k3fgTss1qTG+69ogPA9PCzbOe9eJNz/3swZ5MCS8g71SmmtVauxFwCQYFzzF58q72pVBx5vporq2enPcbo9Gw231rNByMBgejo7ee7Q9zK5Gc3CXll8BT9eJnKHPjf1q1gw42RqqdxlH/TMEJe16Hv/TGh8f93nxpbhsjhsBpX5tt5WQ3tEM5syyTJERtiiuRAk38e/CMlxkVnZvXFrJH9x+797zvvvrqq3due/6FV/bWz8JHsJX3gKpcjD8bGa0ydJbJ0welFZVk7q/phReup3Sg1Nf2thQNb46296hXYOvEc84d42mzrzfjf15reArQcbwJoBM8e3JArSD3cb5g8m7s6Ql8xsaLjDldmoowrJCpLd1/y2zzwl0o0wXKg/cGw047Ff34zeeefbU3sxAZy3tMCeXppT3JO6IEnGaPwJN89cEW+RDbZMDtjYNEbB7qcXYX0mbnKwAoW6znHMpvwCkGlfa0Oa6EpD0teY+kkIUYZVTq6nc8c+0vv1JdpsV53Y77BM+LpufB1BYZXJ/qvg+eo5U5W5msdWDz/qhbOfTbm9+DLy/SlD7yTtrubipzEJ8UvoPng5fJvj/s4WkxjLtmiGSyaFr8aVPsfvhzrD8DHqKKOKG3JSM0eorIf5L27Kw99cilYRy41bdHB4elXu+5PlN+K/3rn3uDIaa5RDrMMIvyn6h1kNm4ZW9n9jR5S8JzJSp5r+xefbQm/akJKGz8Q8PE6Wz6n+OHtvVaUKGw5yz2aK5ueJq8p8AyIUCjeQANC1LaqrOJZWuV2u6d9u3vv//2+8/vTffgNWwaSGoYZgYHSBWFwsheSvaiWVvovTFNnEDogTBd70c30WhA5+Kc9aWrV/2WE8FzSnhauCl5P194TvgTRJfO0ofhacF7z+yR5D2lls625y+NPVAjKtdvVwTp++TrsCbxnaee3lvOrFaCjaMP33ru1d6bdV+yEI/nef5cyU+iMhKmVqhrjKHeN7f28D/RjIao7FFS/tLBlvJLNwEmNmlnmKVvcFvzSbZbjEVDxkSbM6fXnVq1KocbYXcVaB7tCZqCdCAd2lbCIIWLGmm8/drXb389bDnBRjjrV+8ZyiHRjybCccr1tYrxRSvaT+nyVtSTLTSzWZzlBa5Xf5+krVQ/B1jtnYAR8pZT4i+ZvJ907QPXuv7nOFu3ZL0MSieP403zP/uHh+LPdq3m+p9eyk5kW2xgVEKSqZlSgrTzaaxY3GxlFputxtHw42izHQzWOfI+WnqVba+n+7PpejGKmpfA4wFtbO0tYd9LsW3z54kIyg54NnfGB/pjk3Y6BRuOB/W52Yt67vEfyCR11DJhoFxQM/DSn6DKE8NTGEaq5O5IOqmtuvLaa1+/tlvh0p0W7wAb7UgO2siKwwVijzLwZzcsNxYelxIGTswbFp75Bs69W8jxa7NYppnNtTPA06Td5U8bRzhJ8RHThJCbX6JvHsdOAv8PPPuH+cGw1WrCoo2xgTdE8UP1YYhOTc9lKbCm6Nm6NRfstjrd4KjZroB4oBpJbdMs/O4S1jRVr+UWgBPapr9/7Y6NmR7ZOtjT9Gc8KTzb67JG0CPYo/VBFF7WtrYYAzxSnqb/GArqRXMkFcSjRdxQAFRg6Ip63GupLhfVyChIK7tvk8DJlLXIOp8lMzzfI9WnoT/eDrpS3tcobxu/Ky91oeJzIZqSb5smlikSeNV5tfnZdebJrjzj/PNk303iYVP5S/AneELCEzjJiUw6FSHw9OKjPvqT6vaw0zIV2pjgCYGmiT54zs6tFANO5ywqpv52d5iuNRYDIVo14JD08sYtJG3prTk9yoS04WkFuZO37jhrRvzp6c98Wnhe3VhSeCQWZU782YF2WPPD23M9bIIZIxE6tNcjhCD3EwiFVCWm968ujoUw/MUqNP7S8VV2X6POXamVy3JZCVETeeoJyr2L6pVVBD7k0/9v+4xT7BOCx0uUAFoXyeYplGFuuvxEg1ObvS39ybm6bAvCXxKgBqcr8OpU9KrFgnOC58cfk1IGTwEagBB6l4SnUB2FQvDnSqSWLJzGWMVsoRPODzgnlOyZOgLCsZ2naF9/8lGOd0Qz4YEqfKdBZJN1CjMIsxturmDfjT+P1tmzITqJNQYbg1TMAj8/ERJOINYIDxQW5dNbsuXheLtSsTVauhbvjVaiAfLeeoChJ5Y1JheOv4+U88wzBtwqoBko+9TxaFahokA0GFRJBzgNT56vX3mitruabb/CSsc8sXyIybuX/RSt4y6Bp9tIC6nXW3B6/HkYRuJN4Btd41ADNcB1jxWp6h1xwkn/5todt27nEZlhkpNInKI0lX/96X1SZxzGNV8oxhOWrSPBNM+RhEuGp9QnHn00jT3affvja9GbIq3Z2MlFl/FP56/plS567zmSNShP1WBxmGJ+9VMuRJN4vgJTyBhaHo2fWEVhdReqqPopJrT2DG2G8xRYqFLhYp2gsA0CnxBUV0TI5A9A9Sd8a3DmuTq3Elm3dlov4LxH8m54QqY8xZ7C8+/WyPjz4+jhIdkbCXzbE/jxW+txg6+7OJqai5I1oz40c+kda/O5DglTIV4FzlxhZfb+fZoFP3h6a3o5ncvKoTedyJFv4FmKGZ748wXJ+/uf/XAtph0oDdDb37pvjqs5+faH3ivhJZBO5R1XYvbj/n2Wa9qcLwCmh6PW5/1Jjm5QAKJPCTh96AEiAIWo0pFJkqZgKnslBkd7Q3IHTdz5tl6h6yAG0NOdLfykF4zcSQS3NAeDevwJWZ7CwxNzZNI+wXPMnjQawJ+kQAWnx6AmK4amjyGOKY4tYMlRvRVjfHVz2G1HotpzmIxIJFl+8siTr++zDIwxoahGYil4sNhqdmsL+y7TjbyDp/JLoUzj43fpgARO2/n07lu6hHx/4ePB8eCt0sIhRBx72F+wVWjQZnTRAxOGg8wE1UQ8VD0FGqB1YTeDKU/isKI9Sb+QhkyX9SP8s+x+OYt9g+wrQlnMmDxcK3F2InEs2doFT9qR3x6iLqiqcApPbLvgnNSOKGwbewrQT8y4W7oO9RnNoz87wrMBmBJ5w9PxibpGU2uxMk3XISdBY9hMM9iZ2Xv6/jkOJcvFVX3JzuDpP3Xp9YV0Do9egKo9YJP1FDNuM4PpTzxYRdIHZ/0p7lfecHKz0RoOWu12rd3kkK12X5mU97RpagM016GNbEAkzhT3FXX3CGwN3DqT+0o0W3gap+giL1Mj/ZZ/NxaulXPLKne770GRglQ4jUdlkVde56th8rcvffQ8BZoGpocolulU85duf9ijcSqE3qU/8QROD08KYDig4YFr3x2mtKGxapJU2De6U5scx0TqsB7pc3zQMDzzzAf7j0+7Y2BaZFkML89tp8oBtbcrZceQnPz5taV5ybvEnek48+ffP3rrdu2iFd1w5dnPNZrLpeUsDmV+ZTmVsH2X2h2KpNNxqo7/7WLQZ8ypcB4w1e2vVLM7RFF0fSaHPFIlxL8ae65cj4eWsJaSorkADpKmbBW8qO3LYRIs7hR9nAP7lbTSBg3+OsOsiQ3mSZ92i03337EdHgwGx4fRft9fok5IB5zHnaKTx9wJewKoqsUfm/7EX0J/VmWPAt2uhZ3GqjCnwQl/dhbxKIK8oHSfSb2Nnb1HHr1lQYddSwtJr+NZO4uodQC1HJPiozvuoH6O9nTDd/OXVN98BzzJ+l93w5lX3jDTiGxPz/mz/ECMH7XdDuC5YX3+Z4HnZjyYoUoMmgIu6eEpKk8YVfuCMuyrw1uSgczS4RcjcEKYxb7CMxSqJxUJKM5UHiDFpAono7bTLN4FT8DPAWqZPxMJw+CKGoGk1fAF8dYaVXrFW53B4SDcf9VjTyPw9OBE+0Owp9kj7Lvh6YCdREuIGp4Iv2P8qR7fYJr227mzmJkmkUDxKqKfc1i/5c6ywAQhrKQTQYMqo3za5h1rmxfp6EcqQYi7xZurV68ev3vddVcBKPuxrju7x7HNp8ekXZfBfds2N5Gkt7UJ8Oelt84MaxX8T8Rdjr0wnOCprz08nQzCU0WM48z4ZNE48Ge5CvPC15K0TIg5hqglqVYUEuHzU2As4Nbb8WlsJEFVAXI2kVYZTTyF8UDD4RqYE2DeI3RoeTrwNLrGA/RTw1PmHUBNfyLwwOnAoJL5QNue8gUkPOnxrdDTNhfr54ckTooYUY1GtFqF02eWq4u+4fVUX5YLLSrkOr9IQ12nIe8nzyu55OEp+75659WNaTbfwaHU9m845T4nHaXUx1a3GG4qaJJUJjySuE9vrNP/XWqFKxcqcDA3HjA94gmy7rr13J0M0iN8I4isMpsxTnw2ebemEng8jkCTVGGHM4tM6AqSwBB9wqSRqq8bQVHkCy29Vtc9FYXcXnG+WOVRtLt69JybmXcRFZxiz08/VTEOeYekQNPDofB0gFTEE3sUtEZTvEtOJDq3XQja5tRFPPl41E+2cuuJCx7hoJ5bn1Ch8Yn1JBo0r9dTkn3Hn2fNr0VHyJfwZHhq1NMGLRYVUYu+9ridWliGoTjqHJe1N8/WLlJAEnd2pKwzV90fLixeWNGLUuN8XaYdFIEQNKUcuTly30NcYF0GidBUMu3XFIh22jnBoMO/OpJ0hJuIPYGGxQGAQZWj6XRXGeeKhAns03JjqeFV5H0xoa41cvrEbMCYLjzoWZVngucnEHh+OjZGx4fHaGjhWVMEr3vdaSv4BEzJvdTqVL2eZnAhn2hV9HYhgRzHPLfONM+jT76wTxKXsww1MPX0CsF2HnHDHp2Gfd+cKXnq0+NPLnT0FkCyLe/hzz9/+NmGk4NPEEL/dmx7jujdPTFLzrJW9e0sLXcS1FUt52qd8CJsOfZcfrg9M/JpONq3WEWPw7pp/B/amem8FnvWCT0YAyPjZ3hGc8JTDEo9mg5fwVSU0xonGoY8BBnxHZMBqh2nmaM3X3XxNFJXMjgeHjMdrMVpjREU5N51KQB8jrEnZA0jkOGZjmSXUUj1kF4N5ofTnK5fv58mrA/ks33w9DMX2wD6K7Nh0nd0KGvrxez6zuY8p6BYNsSW/GCPwPPNl0556cxrP/r+E5qVRr5wile6fP32nAqcio5sf5KStbjMly7561VUl/AUoNyMDFrAtU8GsS9ZC2aK3XJd5p67mLdcazlQreYLOph90qfGoIkcfZXWEMDu9aobCgjPcMTD0yAEz93RwQBf+BAZpkurcdQcUkyAM4VjCW48/PCgyej6aESyZdU4eUK6XGGFVBEtYFCteOwB2p5axg3SXmwLeXnHmTOZvuXRD8BSB+G//sETr7yuU9yfXIum43mlFGMlEvQ7J+tUQ9Qp8m7jRwGAyRx+zn7vz3V6zBcfhkKYkLzOTaJ+o+XlJ2sxlU320NewDp4BmQHNrP4NTHEq1wJhMhTX1+gYS7cHVRRACz3aTgbYbVcVexbLBJqRAvLswml4Zhdoy0hlC+U6L4mSvNREuOX+iV0XkNXcxiMX2BnPN1559gOfDg4OmiJDUOMiYMiPGt353+T9Hi5UVUF5Cm5ogr90GvYvoYX5IWmxYjycWt7Ye/z151++97H9Dxj1e+KV/ecZLtt/ajltW8g9PDfmicEl8OaehMuBCnh+yEr0H7/96ZNvvvkBPGmmyZFjslO5LTkPi+LMK82kL7KWg8UyCEI+REKyQnZL6Q2f3lxm0ar16mKqqd7gMh57uSj7T/o0UKsnGad1cikPTuS9EEaB+pVQyIaVvSKiD1heL+kZnV1DoTZ76aUbpy0cDpqNL1l8qH8Q/akR/gndBEGjVd6m8dhTxVIGFZNsKNCdmklXk4VsXkkQonbqmCxEuuWJ119UbfHix1959P6nn5S87z+yHffwxB5xPZyNjXWX+pS8lx0sS5DL++wzFM6Hh0d4WkKkRllYGYROs9nihi5nci3eITOJM+ZSBgpmvKfASDBsH1xgTb5pkm61amtYIwuG81GO1DrYLTwm4Kz52JyLiaL5jyyy4ZnPykRGE2lWsIrFa+qKLBRGevXIP3CChQfu//OgYecJi/dGiyQyAOclv3SD7GIDiDqB8tHR1Aap93SqUEa7F1lZ6qducw1pusdeZmjy8adYG7DzxP7rT77yzFn5ciRlRW3y7dgj9KeXDRnLu3uN7vvG3QhmAyVNtyB8f3KgyDJqIh4heaJ4g4BoVS5dJN8h6QEz+or1DqtAk57bn2RDZTJQrhNFOS2cS+EJf3Il4k/kXtXnOIEAv3uxSKqU4LNrgFw44cMJ2/1DN3qvwQNQPG7SPEncSFkqJgp4bqeioka71mzCKgcHUzvRWhXXLkkQHWHLJ/KJa7P2yJMvYN2Z2vInSjvPPL12WjYNp2A9/fdRTmcqZXYGe+Sad1xmDoUh/zlRgEaCTlLA6+CjW5n8oz67eHrveHAxZAUZwRggn+0LeS9AzeoK0yN1muJb+FTFVjVS1EAI10uaKR8N54kseVO54brTASa3Cf50yOXB71IPeC8Nlz13PXH2sBtf8URhWx7VklTjDCIhGHfIPrUdi4YA1KDEpQdMnrWBszMcHjSn9uZYf58j6NX4aOx0Fc56bKB9hoa2p7aY6GzVDj599q2qrKvLn9ul02Z3bp3t2aih8MRxRn+ummoSG0IwJRCGnJpC1mbAwBWIY1Gz1+DJOgpd7R0m5BCfla/hbiJfr5lNhx1bNVOexUgg4utUVRJJ4+ctFMIJsafMIm1+arzQFUU5R0UeQqgagfI4gy6YIi5UF6LQz6kWqa4FsHSdCKpIRMZS7xvSW4UUCnOHlDDgu05DsbtPKIqYsIcAFB+hedABz1tm4uXs9NbeLRsxugJsqRJpeFwidOR2IVJkcO3wk2OCPtRCwp2JZYHyHRuniT9N3lMkfNCfyoXzgYEcwW/8Pd1IsHf5W5DJxSjE3/dIRUFgNwspKf+zeMCUSRAsjVeLNaS6ulhmADnOeFQLoY+YyBe71SoHpClXmII/zSCJP1NZzDzxEU4W71iFxARQhKurxpFjE7/qQzx5PZpa6bO4hjyKCtaFnIhSVrwz7KDqKT8BbrKI39vRUaBu4YibYRmQuIsFXGwhvc6pdfbszj5D9+r9m7bfc5uxIGri2ke7zFWJxRpHGd7OQDFsSwUUb67dsnEa9W8BSjYnH1YLfr3c6cQ78VRhED2kEcKi3kFtMdTv9eafu6YXI2rr4/ERCPf7SicGxiVW8DT9Pi4WGpAAKvZ06q0kmhIjX2Obbw0uQY+KbTDx6ZUE1wkMUSghvRlWSk/9AODp2MTsYrWoSLV99e7b3YPBoFMNmv4csQ+a1evR/n1sCljoJ6I85bMOoAdbBelDEh9/JX3lVjqEqYk55OHYtWdinMbUqyzD3Xtl//X9pzapVLPHBW4jyYaTLDNTa8WrISme1VA1aUtEAJT65tbJF+F+UuQ2OctR7/D5igsljkp4883Y6cBleB5nUAFDMvrv9eat6KrQze6l3oIDmoLU3N4xngambqAJoccAsV52kD4MPDBqQ0I9shiBc+PqJVtYUBBm5ojkoEbTKF/DaJwoCzNe6CtLf5Lt9iXD2NpEOeg6TaMoTfEpNg7grQIokPb5JEitfJ0e4LQOhvHhcDDgQVhyUyWuBqDQJGTvCkx9g7Xp0BST73tPEQw9vhXLC0NrX4yn2N8Pp5I/LLQzuoTVYDGSXwFA7Htv89JLNy8Sf5o5ShUIAmu+QNlf0jo7AlJ6R8Gz9OaBLr37MciqhdIvjN8Ufcy/Hztm1kXwIfj9CSrGycFxMjxxuBwfSbUiuMa1+qiO99SOV/UsHMb6gAdvPU1pECvmCllgVfEZd0zRO0dJ80GDK6ydTGsrnGvgV7vR3rx/MIxqjhIol/ks7y+rTiqRGJbsHLgi/kj/EDwFaBM8wW6MKV909ZkYy6WpW9Y4jnZtb2tpQVPotruF4yVm1re22LJ36+b1rYy8DMQd874ALeMvXXMrUxRzMeNPhI4keBI1W8xave6+HvOj/bfEiAcyoqOPS2/SwcPWHUJiSK39CP0QDD1AF0XAaLYVeBF3qQC0azDYCtd8ii+TjqTckcDXy4CLXqVqLylK8IGwQ/SM5JUPiWpDgS8koQJGEkxhH+YvWIvghmQMT5mkznuvPrdAKBLpxGFA3FS1UwzIS+Mx84Yg8dYNEB/Eh61a0+AUou0m2/vtdmRAmnWHmpDwJCkfLyzPXU8ZCQ6x1+grZ8/iTHaNn235G4oISZOwjiKlq8YgYayW6Box9QmeWkoDnhzgWNLw8ewMNaw3SwqDj3Yzo/e7fQoGBEjArHKcAU0texh0yRV6sHSte0jRkc9zXIC2WvZB1UCxjqCrtIQq5dCpZrwR1zZOehxILsmmA2Q2F07pcQWfs+bLgGeQq0betZzUR9pshfORPDx5TQc9muQ1869RELwXijxoOhDk0yDntVMBKsKPuBuaCH5TWtTgPGpLxptHTW7QgdGUNssUyRDGlbvlC7nT1cQG4z2ajtzbbqM9V+Va11l6YXhuE28u0Z4cc90l4ckcc3CxuHA6mGlcBy4F0uNmyE6raPX7b+l7zLqCpovn4XFHSHocKkY078gVf9DVXV8tOgE5UABK+I6PDF86xTrPkorl2CqTQERhT0jyHs6bHk1HeH8rwrNWBc94kiNUnHI8nzf+HLvu7zffe+PZ5/z8lrDX9aPWXt4fWETVJ0gcOyD7Ly0qo4Q+VcFDABodCdYxmMMhu/unsK4BKgb8QlzQZCRxXz9erxauucXwfOQZm6Wo8LLQZCxpsKHDuVn28c677qdeDjJTdBZ9VTpI+BZKANWOwA9Drq+cIeHbF/LSVNZOd5jKD1oWYxqcenSNkKIpD1EwVfypDQPBuk8TVIzBOFTcOPW97CsH6K6Sn7MMfmDJh+Q9XcCtx85r5UUF9SnzLnsUQsACDi0kmXGiw0S+G+WIhgXQBEaSKahh+Ql9HtQLpQ+pYjGq6nCy+SKeAamrTUF2DCdoMrs2pUCvil+s84l1NPH9a3McOXDRGlM9mpZaz2VWEZZ6AHcpKjzVHju9dNY09gjkwDOb0GFlgQq56K5VvruLeJl84Hsu+gj5HKBIplO5YZqLCgSqzUag2Fb0EvIAFYDGqULQvNhgxv22qhHAGSRIqQU0UBSo0gcUKNc055tMYpDk/8pXssQh+Gb5IluIJMFTbSV1q5UmvdjMwJwQ+90/fu6aaQbf3ABLv8M0B9UfglZjWJwX/FJQhT/FneaZAqiH5gTODmgKT+pCsOfCNIs+2BLJFOozmxw5sM2KkEehp7dWRqhyh8uPpPTm2dag6bMovwtP2EJ/F/70oRUqkBsbKToS6UFhpeK5IHduWG7waoSEp8XuPIwhDVm0z92gtOAZ5QOUpO4x8bypqJ1iPRmIqywqi5TTkfHKIsOjGEbkP4Wc5criT/by+AhKVeZjY8s/Qnf3cXV0HDu91B/oLVHilJsgXXizL/OGNHHHK4NLJfgD1Kl4FDhh0gmegAl7fkh3AXjih7PU5iIa0LBArNu8+KnNaGr5tPU9d5pvZ6ErXY7lp7HSZQR/jLOcp0+z8MjDM14Vnmg8UKHeHsBXb/sQUEmtxXOEwGSDLEL2OV3O92s2KsLOWNLA9DA1SO17PIgCyAYWCZF33KMXVYzrgK5ZDzg0lwNPS4fkbaRJwaZOmiKhkEHGi4r20VkTPEUenmj3o/78RSU4NGsb5RhXgc/lkR6a25kTpof5ArZ/GBegYSCV7+S5T0KTyN1kndnKL46n0OuMcm3u3MKZENpx9vj96xtsIV7fkoG/f603hD99ZGnKuMqGJxn3eTqB6ZMFz2WtkKae4+EJB4Ya3WYwQIl3EfHvhsShWBuB1Q6IP01VUoLp8n3IY06AN570InoEXs+kTxelO0OC07Guu6KTrBdllCKapw3b0m0gyFqaK40JwSOPVzFHq/jMqp2qZU8un+H5TyLe6C6TiejnTc79fqs3qLemaeLUQtkfplJmmIzQpGJO4QlJ0gXp0IRdG7+nqELGmNBau+WZRy/QAt1Xnn6KEb5b2DFzxx07veOjEYgEIuwWVIMIJANPjwc9oXCn/fUoeBZ9XL6XORJ5dUMveTgmr5jhkelKbh6m1uVr8u9yrcq52JGQ2feyCnRK1jtxBTy+alD5Crk3hOwFsNQxtvQAIvJp15sXngFlU7jVhaexqDjz77S6O0rFeqcvFPIq66Eu3F+VbtvLaA8KdDGAJjcJu8j4U6SHA5kkpN2D83BqJXYRu4ZvZQPf40/uU+V4UvNTj26VDvul9978EPcKRReIkB1nZxBMoHpXDP5km5XJe9ZWj0Sqi1wtCtRDU7U91Kj7xQRNaAKqx5+mQqVKjYf1CHsagwZ9MstmjxxFT0rdqWkJkVeLDqtbOt4MU04ZkJQG7EAY4pg0BENdeSCqzjE3o4U5+i/arQxJKESN19V4bqM74XitXa22Bjm1XeVy4stB3POXIFizdSBqGpm4I+/w5+kz12wwQ73FRkNbAilAn3hqc9g4Yg7+4OB40AXPeJr0Q978PGmZizZONjwtHaLwPVJbVOp7jJpxXzfo4TjBU+Q9GJ76MO40jTkhJN0SeMaiRJ++AMgQ0fKB0CtW4tGhWUmNyGCQT6hhPI3piZg7bniuEiJXwRJS8dXwtBrxv3n0wlYctEBpmM5Zp8mf3JiDPQEqPHDlfQh5Pr1pT1faeQBPofnhF+yzWtK2vi34Ezyff+yFJx9/eu+saCM4areOuo2Dgy7xSrLMIY7Ig7wJ4akDEa05WXjmSYfUFlfBUzRJ007g+1veU7XLMaJSkoafnkwcJ2NOxwfawMkdz1RYQuqeC+AsgW+yWNScO+dy5imxg0BEHU80XqW1v8PwVC+PWDqgq5qUOP6B5514okRbh/kOadA4GOaOmTQCV2sJ1H3gkoEp3QlvIu6TuMhF9AvR1JLWP2ph9FOPXrD/wmPKyu8tvdXperbX7AcG2se8R1ReBZMT9zGKNuPa9yw2ytJLwd2/k4eeoBtn68co4lMbydUf04Q3YUyqyLQp0BiiSzCfASWgDQ6y8ci6LD5WPoKVlIQrjRwuaKcMLEvwSMhYDYT4axnS0tZM6lv16D9t0p0S+dYyRilSbbUQabW+wpWHLvHMAD2eAApzcnexdO8QDAr9AbsLj23dYI8OAAAAAElFTkSuQmCC\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Efficiency\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"trending_up\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"margin\":\"0px\"}" + }, + "tags": [ + "productivity", + "effectiveness", + "performance", + "capability" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/efficiency_progress_bar.json b/application/src/main/data/json/system/widget_types/efficiency_progress_bar.json new file mode 100644 index 0000000000..439c40f73f --- /dev/null +++ b/application/src/main/data/json/system/widget_types/efficiency_progress_bar.json @@ -0,0 +1,29 @@ +{ + "fqn": "efficiency_progress_bar", + "name": "Efficiency progress bar", + "deprecated": false, + "image": "tb-image:ZWZmaWNpZW5jeV9wcm9ncmVzc19iYXIuc3Zn:ZWZmaWNpZW5jeV9wcm9ncmVzc19iYXIuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik0yMy41IDIzLjZWMjVoLTUuM3YtMS40aDUuM1pNMTguNyAxNXYxMEgxN1YxNWgxLjdabTQuMSA0LjJ2MS4zaC00LjZ2LTEuM2g0LjZabS43LTQuMnYxLjRoLTUuM1YxNWg1LjNabTMuNyAxMGgtMS42di04LjFjMC0uNi4xLTEgLjMtMS40LjItLjQuNS0uNy45LS44YTMuNCAzLjQgMCAwIDEgMi4zLS4ydjEuM2wtLjMtLjFhNCA0IDAgMCAwLS40IDBsLS42LjFhMSAxIDAgMCAwLS40LjRsLS4yLjdWMjVabTEuNi03LjR2MS4yaC00LjN2LTEuMmg0LjNabTIuNyA3LjRoLTEuN3YtNy45YzAtLjYuMi0xIC40LTEuNS4yLS40LjYtLjcgMS0xbDEuNi0uMmgxbDEuMS40LS4yIDEuMy0uOC0uMi0xLS4xYy0uNSAwLS44IDAtMSAuMy0uMy4yLS40LjYtLjQgMVYyNVptMS41LTcuNHYxLjJoLTQuM3YtMS4ySDMzWm0yLjcgMFYyNWgtMS42di03LjRoMS42Wm01LjEgNi4yLjctLjFjLjMtLjEuNC0uMy42LS41bC4yLS42aDEuNWMwIC40LS4xLjktLjQgMS4zYTMgMyAwIDAgMS0xIC45Yy0uNS4yLTEgLjMtMS42LjMtLjUgMC0xIDAtMS40LS4zYTMgMyAwIDAgMS0xLS44Yy0uNC0uMy0uNi0uNy0uNy0xLjEtLjItLjUtLjItMS0uMi0xLjV2LS4yYzAtLjUgMC0xIC4yLTEuNC4xLS41LjMtLjkuNi0xLjIuMy0uNC42LS42IDEtLjhsMS41LS4zYy42IDAgMS4xIDAgMS42LjMuNC4zLjguNiAxIDEgLjMuNC40LjkuNCAxLjRoLTEuNWwtLjItLjctLjUtLjVjLS4yLS4yLS41LS4yLS44LS4yLS4zIDAtLjYgMC0uOC4ybC0uNS41Yy0uMi4zLS4zLjUtLjMuOHYyLjFjMCAuMi4xLjUuMy43IDAgLjIuMy40LjUuNS4yLjIuNS4yLjguMlptNi4zLTYuMlYyNWgtMS42di03LjRINDdabS0xLjctMmMwLS4yIDAtLjQuMi0uNmwuNy0uMmMuMyAwIC41IDAgLjcuMi4yLjIuMi40LjIuNyAwIC4yIDAgLjQtLjIuNmExIDEgMCAwIDEtLjcuMiAxIDEgMCAwIDEtLjctLjIuOC44IDAgMCAxLS4yLS42Wm03IDkuNWMtLjUgMC0xIDAtMS41LS4yYTMuMyAzLjMgMCAwIDEtMS44LTIgNCA0IDAgMCAxLS4yLTEuM3YtLjNjMC0uNiAwLTEuMS4yLTEuNnMuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS4xLS44bDEuMy0uMmMuNiAwIDEgMCAxLjQuMmwxIC44Yy4zLjMuNS43LjYgMS4xbC4yIDEuNXYuN2gtNS43di0xLjJoNHYtLjFsLS4xLS45LS41LS42LS45LS4yYy0uMiAwLS41IDAtLjcuMi0uMiAwLS40LjItLjUuNWwtLjQuN3YyLjNsLjUuNy42LjUuOC4xYTIuMyAyLjMgMCAwIDAgMS45LS45bC45LjktLjcuNmMtLjIuMi0uNS40LS45LjUtLjQuMi0uOC4yLTEuMy4yWm02LjItNlYyNWgtMS43di03LjRoMS42djEuNlptLS4zIDJoLS42YzAtLjYuMS0xIC4zLTEuNS4xLS41LjMtLjguNi0xLjFhMi43IDIuNyAwIDAgMSAyLTFsMSAuMS44LjUuNS44LjEgMS4zVjI1aC0xLjZ2LTQuOGMwLS4zIDAtLjYtLjItLjhhLjkuOSAwIDAgMC0uNC0uNWwtLjctLjFhMS42IDEuNiAwIDAgMC0xLjQuN2wtLjMuNy0uMS44Wm05LjcgMi43LjctLjEuNS0uNS4zLS42SDcxYzAgLjQtLjEuOS0uNCAxLjNhMyAzIDAgMCAxLTEgLjljLS41LjItMSAuMy0xLjYuMy0uNiAwLTEgMC0xLjUtLjNhMyAzIDAgMCAxLTEtLjhjLS4zLS4zLS41LS43LS42LTEuMS0uMi0uNS0uMi0xLS4yLTEuNXYtLjJjMC0uNSAwLTEgLjItMS40LjEtLjUuMy0uOS42LTEuMi4zLS40LjYtLjYgMS0uOGwxLjUtLjNjLjYgMCAxLjEgMCAxLjYuMy40LjMuOC42IDEgMSAuMy40LjQuOS40IDEuNGgtMS41bC0uMi0uNy0uNS0uNWMtLjMtLjItLjUtLjItLjgtLjItLjMgMC0uNiAwLS44LjItLjMuMS0uNC4zLS42LjVsLS4yLjgtLjEuOXYxLjJsLjMuN2MuMi4yLjMuNC42LjUuMi4yLjUuMi44LjJabTYuNy40IDItNi42aDEuOGwtMyA4LjUtLjMuNmMwIC4yLS4yLjQtLjQuNmEyIDIgMCAwIDEtLjcuNWwtLjkuMmgtLjRsLS40LS4xdi0xLjNhNi4zIDYuMyAwIDAgMCAuMyAwaC43bC40LS40Yy4yLS4xLjMtLjMuMy0uNmwuNi0xLjRabS0xLjItNi42IDEuOCA1LjUuMyAxLjgtMS4xLjMtMi43LTcuNmgxLjdaIi8+PHBhdGggZmlsbD0iIzNCOTExQyIgZD0iTTM1IDgwLjR2Mi4zTDI0LjYgMTA2aC00LjVsMTAuMi0yMi4ySDE3LjJ2LTMuNGgxNy43Wm0yMC4yIDEwLjd2NC4xYzAgMi0uMiAzLjgtLjYgNS4yYTkgOSAwIDAgMS0xLjcgMy40Yy0uOC45LTEuNiAxLjUtMi43IDJhOS44IDkuOCAwIDAgMS02LjIuMiA2LjkgNi45IDAgMCAxLTQuMS0zLjRjLS41LS45LS45LTEuOS0xLjEtMy4xLS4zLTEuMi0uNC0yLjctLjQtNC4zdi00LjFjMC0yIC4yLTMuOC42LTUuMWE5IDkgMCAwIDEgMS43LTMuNGMuNy0xIDEuNi0xLjUgMi42LTIgMS0uMyAyLjItLjUgMy41LS41IDEgMCAyIDAgMi44LjNhNi42IDYuNiAwIDAgMSA0IDMuM2MuNS45IDEgMiAxLjIgMy4xLjIgMS4yLjQgMi43LjQgNC4zWm0tNC4zIDQuN3YtNS40bC0uMS0yLjdhOCA4IDAgMCAwLS42LTIgNCA0IDAgMCAwLS44LTEuMyAzIDMgMCAwIDAtMS4yLS43IDMuOSAzLjkgMCAwIDAtMy4yLjFjLS41LjItMSAuNi0xLjMgMS4yLS4zLjUtLjYgMS4zLS44IDIuMi0uMi45LS4zIDItLjMgMy4ydjUuNGMwIDEgMCAyIC4yIDIuOGwuNSAyIC45IDEuNGMuMy4zLjcuNiAxLjEuN2wxLjUuM2E0IDQgMCAwIDAgMS44LS40Yy41LS4zIDEtLjcgMS4zLTEuMi4zLS42LjYtMS40LjgtMi4zbC4yLTMuM1pNNjIuMiA5My4xdi0uOWMwLS42LjEtMS4yLjQtMS44LjMtLjUuNy0xIDEuMi0xLjIuNi0uMyAxLjItLjUgMi0uNXMxLjQuMiAyIC41Yy41LjMuOS43IDEuMiAxLjIuMy42LjQgMS4yLjQgMS44djFjMCAuNi0uMiAxLjEtLjQgMS43LS4zLjUtLjcgMS0xLjMgMS4yLS41LjQtMS4xLjUtMiAuNS0uNyAwLTEuMy0uMS0xLjktLjUtLjUtLjMtMS0uNy0xLjItMS4yLS4zLS42LS40LTEuMS0uNC0xLjhabTItLjl2MWwuMS44Yy4xLjMuMy41LjYuNy4yLjIuNS4yLjkuMnMuNyAwIDEtLjJsLjQtLjdjLjItLjMuMi0uNi4yLS45di0uOWMwLS4zIDAtLjYtLjItLjkgMC0uMy0uMy0uNS0uNS0uN2wtMS0uMmMtLjMgMC0uNiAwLS44LjItLjMuMi0uNS40LS42LjdsLS4yIDFabTYuMiAxMC41di0uOWEzLjMgMy4zIDAgMCAxIDEuNy0zYy41LS4zIDEuMi0uNSAyLS41LjcgMCAxLjMuMiAxLjkuNS41LjMgMSAuNyAxLjIgMS4zLjMuNS40IDEgLjQgMS43djFjMCAuNi0uMSAxLjItLjQgMS43LS4zLjUtLjcgMS0xLjIgMS4zLS41LjMtMS4yLjQtMiAuNC0uNyAwLTEuNC0uMS0yLS40LS41LS40LS45LS44LTEuMi0xLjMtLjMtLjUtLjQtMS4xLS40LTEuOFptMi0uOXYxYzAgLjIgMCAuNS4yLjggMCAuMy4zLjUuNS43LjMuMi42LjMgMSAuMy4zIDAgLjctLjEuOS0uM2wuNS0uNy4yLS45di0uOWMwLS4zIDAtLjYtLjItLjktLjEtLjMtLjMtLjUtLjYtLjdsLS45LS4yYy0uNCAwLS43IDAtMSAuMmwtLjUuNy0uMSAxWm0yLjMtMTAuNC04LjMgMTMuMy0xLjUtLjggOC40LTEzLjQgMS40LjlaIi8+PHJlY3Qgd2lkdGg9IjE2OCIgaGVpZ2h0PSI2IiB4PSIxNiIgeT0iMTIyIiBmaWxsPSJub25lIiByeD0iMyIvPjxyZWN0IHdpZHRoPSIxMjQiIGhlaWdodD0iNiIgZmlsbD0iIzNGQTcxQSIgcng9IjMiIHRyYW5zZm9ybT0ibWF0cml4KC0xIDAgMCAxIDE0MCAxMjIpIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTIxLjYgMTM5LjV2MS4ybC0uMiAxLjYtLjUgMWMtLjIuMy0uNS41LS44LjZsLTEgLjJhMyAzIDAgMCAxLS44LS4xIDIgMiAwIDAgMS0uNy0uNGwtLjUtLjYtLjQtMWE3IDcgMCAwIDEgMC0xLjN2LTIuOGwuNi0xYy4yLS40LjUtLjUuOC0uN2wxLS4xaC44YTEuOSAxLjkgMCAwIDEgMS4yIDFjLjIuMy4zLjcuMyAxbC4yIDEuNFptLTEgMS4zdi0xLjVsLS4xLTFhMyAzIDAgMCAwLS4yLS42YzAtLjItLjItLjQtLjMtLjVhMSAxIDAgMCAwLS40LS4ybC0uNS0uMS0uNi4xLS41LjQtLjMuOHYzLjZjMCAuMiAwIC41LjIuNiAwIC4yLjEuNC4zLjUgMCAuMi4yLjIuNC4zaDEuMWwuNS0uNS4zLS44di0xWk0xNjggMTM2LjF2Ny45aC0xdi02LjZsLTIgLjd2LS45bDIuOC0xaC4xWm04IDMuNHYxLjJjMCAuNiAwIDEuMS0uMiAxLjYgMCAuNC0uMy44LS41IDEtLjIuMy0uNC41LS43LjZsLTEgLjJhMyAzIDAgMCAxLS45LS4xIDIgMiAwIDAgMS0uNy0uNGwtLjUtLjZjLS4xLS4zLS4zLS42LS4zLTFhNyA3IDAgMCAxLS4xLTEuM3YtMS4ybC4xLTEuNmMuMS0uNS4zLS44LjUtMSAuMi0uNC41LS41LjgtLjdsMS0uMWguOWEyIDIgMCAwIDEgMS4yIDFsLjMgMSAuMSAxLjRabS0xIDEuM3YtMi41bC0uMi0uNi0uMy0uNWExIDEgMCAwIDAtLjUtLjJsLS41LS4xLS42LjEtLjUuNC0uMi44LS4xIDEuMXYyLjVsLjIuNmMwIC4yLjIuNC4zLjUuMS4yLjMuMi40LjNoMS4ybC40LS41Yy4yLS4yLjItLjQuMy0uOGwuMS0xWm03LjQtMS4zdjEuMmwtLjEgMS42LS41IDFjLS4yLjMtLjUuNS0uOC42bC0xIC4yYTMgMyAwIDAgMS0uOS0uMSAyIDIgMCAwIDEtLjctLjRsLS41LS42LS4zLTFhNyA3IDAgMCAxLS4xLTEuM3YtMS4yYzAtLjcgMC0xLjIuMi0xLjYgMC0uNS4yLS44LjUtMSAuMi0uNC40LS41LjctLjdsMS0uMWguOWExLjkgMS45IDAgMCAxIDEuMiAxbC4zIDEgLjEgMS40Wm0tMSAxLjN2LTIuNWEzIDMgMCAwIDAtLjItLjZjMC0uMi0uMi0uNC0uMy0uNWExIDEgMCAwIDAtLjQtLjJsLS41LS4xLS43LjEtLjQuNGMtLjIuMi0uMi41LS4zLjhsLS4xIDEuMXYyLjVsLjIuNi4zLjUuNC4zaDEuMmwuNS0uNS4yLS44LjEtMVoiLz48L3N2Zz4=", + "description": "Displays efficiency reading as a horizontal progress bar. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'efficiency', label: 'Efficiency', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Efficiency\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 30;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":60,\"color\":\"#FFA600\"},{\"from\":60,\"to\":80,\"color\":\"#3FA71A\"},{\"from\":80,\"to\":null,\"color\":\"#305AD7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":100,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":60,\"color\":\"#FFA600\"},{\"from\":60,\"to\":80,\"color\":\"#3FA71A\"},{\"from\":80,\"to\":null,\"color\":\"#305AD7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Efficiency\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "productivity", + "effectiveness", + "performance", + "capability" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/efficiency_progress_bar_with_background.json b/application/src/main/data/json/system/widget_types/efficiency_progress_bar_with_background.json new file mode 100644 index 0000000000..abff2b60cd --- /dev/null +++ b/application/src/main/data/json/system/widget_types/efficiency_progress_bar_with_background.json @@ -0,0 +1,29 @@ +{ + "fqn": "efficiency_progress_bar_with_background", + "name": "Efficiency progress bar with background", + "deprecated": false, + "image": "tb-image:ZWZmaWNpZW5jeV9wcm9ncmVzc19iYXJfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==:ZWZmaWNpZW5jeV9wcm9ncmVzc19iYXJfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48L2c+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZD0iTTIzLjUgMjMuNlYyNWgtNS4zdi0xLjRoNS4zWk0xOC43IDE1djEwSDE3VjE1aDEuN1ptNC4xIDQuMnYxLjNoLTQuNnYtMS4zaDQuNlptLjctNC4ydjEuNGgtNS4zVjE1aDUuM1ptMy43IDEwaC0xLjZ2LTguMWMwLS42LjEtMSAuMy0xLjQuMi0uNC41LS43LjktLjhhMy40IDMuNCAwIDAgMSAyLjMtLjJ2MS4zbC0uMy0uMWE0IDQgMCAwIDAtLjQgMGwtLjYuMWExIDEgMCAwIDAtLjQuNGwtLjIuN1YyNVptMS42LTcuNHYxLjJoLTQuM3YtMS4yaDQuM1ptMi43IDcuNGgtMS43di03LjljMC0uNi4yLTEgLjQtMS41LjItLjQuNi0uNyAxLTFsMS42LS4yaDFsMS4xLjQtLjIgMS4zLS44LS4yLTEtLjFjLS41IDAtLjggMC0xIC4zLS4zLjItLjQuNi0uNCAxVjI1Wm0xLjUtNy40djEuMmgtNC4zdi0xLjJIMzNabTIuNyAwVjI1aC0xLjZ2LTcuNGgxLjZabTUuMSA2LjIuNy0uMWMuMy0uMS40LS4zLjYtLjVsLjItLjZoMS41YzAgLjQtLjEuOS0uNCAxLjNhMyAzIDAgMCAxLTEgLjljLS41LjItMSAuMy0xLjYuMy0uNSAwLTEgMC0xLjQtLjNhMyAzIDAgMCAxLTEtLjhjLS40LS4zLS42LS43LS43LTEuMS0uMi0uNS0uMi0xLS4yLTEuNXYtLjJjMC0uNSAwLTEgLjItMS40LjEtLjUuMy0uOS42LTEuMi4zLS40LjYtLjYgMS0uOGwxLjUtLjNjLjYgMCAxLjEgMCAxLjYuMy40LjMuOC42IDEgMSAuMy40LjQuOS40IDEuNGgtMS41bC0uMi0uNy0uNS0uNWMtLjItLjItLjUtLjItLjgtLjItLjMgMC0uNiAwLS44LjJsLS41LjVjLS4yLjMtLjMuNS0uMy44djIuMWMwIC4yLjEuNS4zLjcgMCAuMi4zLjQuNS41LjIuMi41LjIuOC4yWm02LjMtNi4yVjI1aC0xLjZ2LTcuNEg0N1ptLTEuNy0yYzAtLjIgMC0uNC4yLS42bC43LS4yYy4zIDAgLjUgMCAuNy4yLjIuMi4yLjQuMi43IDAgLjIgMCAuNC0uMi42YTEgMSAwIDAgMS0uNy4yIDEgMSAwIDAgMS0uNy0uMi44LjggMCAwIDEtLjItLjZabTcgOS41Yy0uNSAwLTEgMC0xLjUtLjJhMy4zIDMuMyAwIDAgMS0xLjgtMiA0IDQgMCAwIDEtLjItMS4zdi0uM2MwLS42IDAtMS4xLjItMS42cy40LS45LjctMS4yYTMgMyAwIDAgMSAxLjEtLjhsMS4zLS4yYy42IDAgMSAwIDEuNC4ybDEgLjhjLjMuMy41LjcuNiAxLjFsLjIgMS41di43aC01Ljd2LTEuMmg0di0uMWwtLjEtLjktLjUtLjYtLjktLjJjLS4yIDAtLjUgMC0uNy4yLS4yIDAtLjQuMi0uNS41bC0uNC43djIuM2wuNS43LjYuNS44LjFhMi4zIDIuMyAwIDAgMCAxLjktLjlsLjkuOS0uNy42Yy0uMi4yLS41LjQtLjkuNS0uNC4yLS44LjItMS4zLjJabTYuMi02VjI1aC0xLjd2LTcuNGgxLjZ2MS42Wm0tLjMgMmgtLjZjMC0uNi4xLTEgLjMtMS41LjEtLjUuMy0uOC42LTEuMWEyLjcgMi43IDAgMCAxIDItMWwxIC4xLjguNS41LjguMSAxLjNWMjVoLTEuNnYtNC44YzAtLjMgMC0uNi0uMi0uOGEuOS45IDAgMCAwLS40LS41bC0uNy0uMWExLjYgMS42IDAgMCAwLTEuNC43bC0uMy43LS4xLjhabTkuNyAyLjcuNy0uMS41LS41LjMtLjZINzFjMCAuNC0uMS45LS40IDEuM2EzIDMgMCAwIDEtMSAuOWMtLjUuMi0xIC4zLTEuNi4zLS42IDAtMSAwLTEuNS0uM2EzIDMgMCAwIDEtMS0uOGMtLjMtLjMtLjUtLjctLjYtMS4xLS4yLS41LS4yLTEtLjItMS41di0uMmMwLS41IDAtMSAuMi0xLjQuMS0uNS4zLS45LjYtMS4yLjMtLjQuNi0uNiAxLS44bDEuNS0uM2MuNiAwIDEuMSAwIDEuNi4zLjQuMy44LjYgMSAxIC4zLjQuNC45LjQgMS40aC0xLjVsLS4yLS43LS41LS41Yy0uMy0uMi0uNS0uMi0uOC0uMi0uMyAwLS42IDAtLjguMi0uMy4xLS40LjMtLjYuNWwtLjIuOC0uMS45djEuMmwuMy43Yy4yLjIuMy40LjYuNS4yLjIuNS4yLjguMlptNi43LjQgMi02LjZoMS44bC0zIDguNS0uMy42YzAgLjItLjIuNC0uNC42YTIgMiAwIDAgMS0uNy41bC0uOS4yaC0uNGwtLjQtLjF2LTEuM2E2LjMgNi4zIDAgMCAwIC4zIDBoLjdsLjQtLjRjLjItLjEuMy0uMy4zLS42bC42LTEuNFptLTEuMi02LjYgMS44IDUuNS4zIDEuOC0xLjEuMy0yLjctNy42aDEuN1oiLz48cGF0aCBmaWxsPSIjM0I5MTFDIiBkPSJNMzUgODAuNHYyLjNMMjQuNiAxMDZoLTQuNWwxMC4yLTIyLjJIMTcuMnYtMy40aDE3LjdabTIwLjIgMTAuN3Y0LjFjMCAyLS4yIDMuOC0uNiA1LjJhOSA5IDAgMCAxLTEuNyAzLjRjLS44LjktMS42IDEuNS0yLjcgMmE5LjggOS44IDAgMCAxLTYuMi4yIDYuOSA2LjkgMCAwIDEtNC4xLTMuNGMtLjUtLjktLjktMS45LTEuMS0zLjEtLjMtMS4yLS40LTIuNy0uNC00LjN2LTQuMWMwLTIgLjItMy44LjYtNS4xYTkgOSAwIDAgMSAxLjctMy40Yy43LTEgMS42LTEuNSAyLjYtMiAxLS4zIDIuMi0uNSAzLjUtLjUgMSAwIDIgMCAyLjguM2E2LjYgNi42IDAgMCAxIDQgMy4zYy41LjkgMSAyIDEuMiAzLjEuMiAxLjIuNCAyLjcuNCA0LjNabS00LjMgNC43di01LjRsLS4xLTIuN2E4IDggMCAwIDAtLjYtMiA0IDQgMCAwIDAtLjgtMS4zIDMgMyAwIDAgMC0xLjItLjcgMy45IDMuOSAwIDAgMC0zLjIuMWMtLjUuMi0xIC42LTEuMyAxLjItLjMuNS0uNiAxLjMtLjggMi4yLS4yLjktLjMgMi0uMyAzLjJ2NS40YzAgMSAwIDIgLjIgMi44bC41IDIgLjkgMS40Yy4zLjMuNy42IDEuMS43bDEuNS4zYTQgNCAwIDAgMCAxLjgtLjRjLjUtLjMgMS0uNyAxLjMtMS4yLjMtLjYuNi0xLjQuOC0yLjNsLjItMy4zWk02Mi4yIDkzLjF2LS45YzAtLjYuMS0xLjIuNC0xLjguMy0uNS43LTEgMS4yLTEuMi42LS4zIDEuMi0uNSAyLS41czEuNC4yIDIgLjVjLjUuMy45LjcgMS4yIDEuMi4zLjYuNCAxLjIuNCAxLjh2MWMwIC42LS4yIDEuMS0uNCAxLjctLjMuNS0uNyAxLTEuMyAxLjItLjUuNC0xLjEuNS0yIC41LS43IDAtMS4zLS4xLTEuOS0uNS0uNS0uMy0xLS43LTEuMi0xLjItLjMtLjYtLjQtMS4xLS40LTEuOFptMi0uOXYxbC4xLjhjLjEuMy4zLjUuNi43LjIuMi41LjIuOS4ycy43IDAgMS0uMmwuNC0uN2MuMi0uMy4yLS42LjItLjl2LS45YzAtLjMgMC0uNi0uMi0uOSAwLS4zLS4zLS41LS41LS43bC0xLS4yYy0uMyAwLS42IDAtLjguMi0uMy4yLS41LjQtLjYuN2wtLjIgMVptNi4yIDEwLjV2LS45YTMuMyAzLjMgMCAwIDEgMS43LTNjLjUtLjMgMS4yLS41IDItLjUuNyAwIDEuMy4yIDEuOS41LjUuMyAxIC43IDEuMiAxLjMuMy41LjQgMSAuNCAxLjd2MWMwIC42LS4xIDEuMi0uNCAxLjctLjMuNS0uNyAxLTEuMiAxLjMtLjUuMy0xLjIuNC0yIC40LS43IDAtMS40LS4xLTItLjQtLjUtLjQtLjktLjgtMS4yLTEuMy0uMy0uNS0uNC0xLjEtLjQtMS44Wm0yLS45djFjMCAuMiAwIC41LjIuOCAwIC4zLjMuNS41LjcuMy4yLjYuMyAxIC4zLjMgMCAuNy0uMS45LS4zbC41LS43LjItLjl2LS45YzAtLjMgMC0uNi0uMi0uOS0uMS0uMy0uMy0uNS0uNi0uN2wtLjktLjJjLS40IDAtLjcgMC0xIC4ybC0uNS43LS4xIDFabTIuMy0xMC40LTguMyAxMy4zLTEuNS0uOCA4LjQtMTMuNCAxLjQuOVoiLz48cmVjdCB3aWR0aD0iMTY4IiBoZWlnaHQ9IjYiIHg9IjE2IiB5PSIxMjIiIGZpbGw9Im5vbmUiIHJ4PSIzIi8+PHJlY3Qgd2lkdGg9IjEyNCIgaGVpZ2h0PSI2IiBmaWxsPSIjM0ZBNzFBIiByeD0iMyIgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgMTQwIDEyMikiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii41IiBkPSJNMjEuNiAxMzkuNXYxLjJsLS4yIDEuNi0uNSAxYy0uMi4zLS41LjUtLjguNmwtMSAuMmEzIDMgMCAwIDEtLjgtLjEgMiAyIDAgMCAxLS43LS40bC0uNS0uNi0uNC0xYTcgNyAwIDAgMSAwLTEuM3YtMi44bC42LTFjLjItLjQuNS0uNS44LS43bDEtLjFoLjhhMS45IDEuOSAwIDAgMSAxLjIgMWMuMi4zLjMuNy4zIDFsLjIgMS40Wm0tMSAxLjN2LTEuNWwtLjEtMWEzIDMgMCAwIDAtLjItLjZjMC0uMi0uMi0uNC0uMy0uNWExIDEgMCAwIDAtLjQtLjJsLS41LS4xLS42LjEtLjUuNC0uMy44djMuNmMwIC4yIDAgLjUuMi42IDAgLjIuMS40LjMuNSAwIC4yLjIuMi40LjNoMS4xbC41LS41LjMtLjh2LTFaTTE2OCAxMzYuMXY3LjloLTF2LTYuNmwtMiAuN3YtLjlsMi44LTFoLjFabTggMy40djEuMmMwIC42IDAgMS4xLS4yIDEuNiAwIC40LS4zLjgtLjUgMS0uMi4zLS40LjUtLjcuNmwtMSAuMmEzIDMgMCAwIDEtLjktLjEgMiAyIDAgMCAxLS43LS40bC0uNS0uNmMtLjEtLjMtLjMtLjYtLjMtMWE3IDcgMCAwIDEtLjEtMS4zdi0xLjJsLjEtMS42Yy4xLS41LjMtLjguNS0xIC4yLS40LjUtLjUuOC0uN2wxLS4xaC45YTIgMiAwIDAgMSAxLjIgMWwuMyAxIC4xIDEuNFptLTEgMS4zdi0yLjVsLS4yLS42LS4zLS41YTEgMSAwIDAgMC0uNS0uMmwtLjUtLjEtLjYuMS0uNS40LS4yLjgtLjEgMS4xdjIuNWwuMi42YzAgLjIuMi40LjMuNS4xLjIuMy4yLjQuM2gxLjJsLjQtLjVjLjItLjIuMi0uNC4zLS44bC4xLTFabTcuNC0xLjN2MS4ybC0uMSAxLjYtLjUgMWMtLjIuMy0uNS41LS44LjZsLTEgLjJhMyAzIDAgMCAxLS45LS4xIDIgMiAwIDAgMS0uNy0uNGwtLjUtLjYtLjMtMWE3IDcgMCAwIDEtLjEtMS4zdi0xLjJjMC0uNyAwLTEuMi4yLTEuNiAwLS41LjItLjguNS0xIC4yLS40LjQtLjUuNy0uN2wxLS4xaC45YTEuOSAxLjkgMCAwIDEgMS4yIDFsLjMgMSAuMSAxLjRabS0xIDEuM3YtMi41YTMgMyAwIDAgMC0uMi0uNmMwLS4yLS4yLS40LS4zLS41YTEgMSAwIDAgMC0uNC0uMmwtLjUtLjEtLjcuMS0uNC40Yy0uMi4yLS4yLjUtLjMuOGwtLjEgMS4xdjIuNWwuMi42LjMuNS40LjNoMS4ybC41LS41LjItLjguMS0xWiIvPjxkZWZzPjxwYXR0ZXJuIGlkPSJhIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBwYXR0ZXJuQ29udGVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCI+PHVzZSB4bGluazpocmVmPSIjYyIgdHJhbnNmb3JtPSJtYXRyaXgoLjAwNTAzMTQ1IDAgMCAuMDA2Mjg5MzEgMCAwKSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgeD0iMCIgeT0iMCIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMyIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xNTE2Xzc0MjQyIi8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xNTE2Xzc0MjQyIiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PGltYWdlIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3QkRBQUlCQVFFQkFRSUJBUUVDQWdJQ0FnUURBZ0lDQWdVRUJBTUVCZ1VHQmdZRkJnWUdCd2tJQmdjSkJ3WUdDQXNJQ1FvS0Nnb0tCZ2dMREFzS0RBa0tDZ3IvMndCREFRSUNBZ0lDQWdVREF3VUtCd1lIQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnci93Z0FSQ0FDZkFNY0RBUkVBQWhFQkF4RUIvOFFBSFFBQUFnSURBUUVCQUFBQUFBQUFBQUFBQmdjRkNBTUVDUUlBQWYvRUFCc0JBQUlEQVFFQkFBQUFBQUFBQUFBQUFBTUVBUUlGQUFZSC85b0FEQU1CQUFJUUF4QUFBQUIzWTdOZHhhTldyc09EYTh3ejB6dCtzV05zT0tyYkdPNU1RSHFoaWtxZVR1L1pKNGlkcWVMNmszTzRMU1BVeWpjYmJ0V2UxNXFzNWxEbEhZU0IyZTBjMW9IRnlhcTAwMG1ISlhyR1NHSXFYd0taZTFadnVKU3I1Wmo2Tytuc3ZXSnFrMlJFRVZpMWRxeEJ6SHVlL1pyWEVrMW5OM1dUazRWbFk3WUZ6WXF3MGxUdU9LMktrZXBTMGFNbjVXMHhTNUNZZm1KdzB2NG1zMlFScmFKR0pGbFNWc0E5QTFKNUlPbWhySXRvRWhFOVFWUm1aSUt0Zko1cEJiWXdDVFFpUGNOOW1PbnloeGlMc2dabExqK29UVUdhTEVZdllVYnVubHpYY0tLbHFmbGVoZ0FQNUlKOTNlckI5V3FjNi9tV0FRTWZvcWM5cU1Ib3JHZExXQ2l1VG93MW1wakFoV0ozK0pGeDA4czFQTFBUN09mWkFpOWx5Q0ZseTFld2ZZUUMranFDYjI2U3VnUFJsRHZQMm55UXhtbWQ5T2pYSE1hek5CSllpdGMvZHVXcFhRa1VlMU0wc0ViWmdoUXV3YUJNN1NxdnRjdGdiMEdsVFZxODE3dlRFOW42MzdYb2RWdVBpKzM3SDVneXlnMFhWS1k4WXppUzRGMzZDMGQxaVZsWGx2cW9TQVd2dXM2aFcyUkdoWUlYUlF1cEYvTENIVlMxdDg1N3VQRTVzZ3RuamgwRCtwVzR0N0w1a1dPWjV1eXZUMlRsRlpPVkRQRUJNVThiUG84cnRCVW1BeEJFNDRDU2VFVGFpelpnWHc3WHZrY2NPeWxVYldQRjhLNk9SVjZOVmU5NUhvSUwxbnowOWJ6NHZRUXE2UWhURTdTakRXUWVIODMwcmY4QVRlRjVDNzJMcnh5L3RXdzJkb3FJSnB4MU45TE1sb3pXemdVc3FaZUtPbFBSOU02cXI0TGgrazUwaTlUMGc5RDhzWWp5SDJpaFZPRGtmZHZKTk9QTzBpUGh3Kzc1K2h6ZEU4MGpYOXRVMUVURmFONkpmeUdoYUhOMHJuTm9teTkwbm42V25hZENrNm5YaFBHL1E2OEIxck8vUnZpNC9FeWpDOWFKTVNWNXVsWHRPUUtVeTlGTm1HbEdSVmExTXBYbUFYQ05PVnREMm95VjJiYzVXeGY1dElpRHd5QStLMVJ1aFlaWTBRTXZPdWo5K21zbVFubGd3SktrNlc2SHh3N1NYRWk3MXFKYU15N2xmZEROUTdpUlNNc3ZTNHpjYlhVY3NqbjZyT1lTc2NHeG1LTWQ3REZDZ3k1K2ZUUGM3OUZMcmNpWE5YaGk4YnMzMnJRMzZEc2VPd0pGcTJaK25vYW1iVXJWeDFleXNXQlBPMXVGRkMwMVhMSTV1cGQ1aEZrQXREanRyWHVKaExXcnI4OUNNTVpac0QwOE1TSUZZdEI2SVoraEpSd2NLYlFLTW1UQzR0Z2VtUzNwUEwxZDFNaFVzTEZ3anlNY04zb3lsWExKWitwZnc2SlN0Y2JHZlN0QUl1ZEMzbmxucEwzdHptdVptL2p6aUdpUE1nNlNLM2xheUZNb1c5UWVVb1dZN0Y5R3NQVmVOMWlDNkZMTVhFZFVBeFh4OVBPeFJ4RE1BNjJ3UGVYdlQxRFVFeVVWTFFhb2J2bjFPcm9kVU1iUXI3cUlSNnhsWWFqL0FJTEswc0pNSTJWek5KUndTU1hhcmo2UHpJMWFuYUhPMHZ1bm0yUVpEZWliSlFtN3V2cVpQSUMwd1UwUXk5VnVjVlRmUWVkV3FtaDFFeU5HbG01alNPZTZlVnVVRXR0ZElsS3oremRKTXNpMG1WazAvblNsTDlUTTdTaTZFcEV5c2QxbE9HcElSTjExUzJtQjFLVTlRUXVGZE1ocS90NGFJTERweFBSczUvRkQyRm44czFFallsNHVGeXZaUkI1S05CSTZXcGZ0NFc5ZW94TlpnTEJaMEJNMU1LRS9ZbnBDZzdrcnlUUjB5VVYxc1lDRDFzcE9hQ0VmU2JYcHZPWVpweWFwU0x6ZFMxcFpRblZXSmE4eTFMVkoxOFhMM1MvUWYwTHRWbG9MdWIwV3JxNGlyWU0yMVdKcXZldWdWT0NLbW1XOUo0Qkw3bzZSZk1VYmNTYlFXNWVoS1phT1hFMXQrVFhhaVJzNnUzRVpaNHA2U3FoRzRvOE5sQ2tXVlZOVmpYRWU0aTlpdWt4OWVnN2lnSTRqUmMvL3hBQTRFQUFCQkFFREFnUURCQWtGQVFBQUFBQURBUUlFQlFBR0VSSVRJUWNVSWpFVkl5UXlORUZDRUJZWEpTWXpOVkZoUTBSU1UxUkYvOW9BQ0FFQkFBRU1BbzFuSW5UVWxUWXZSUDRwUmIrZDVOdEhFbUVXQlI2ckNaSkUycXNXaWhwOWU3TE52MUM1b1JlSXpaend6OHIxOWFya3Qyd2xYSUg4bE1iK2xjaUxzVmNZcTc0N0pydXk0Um5yZG5IL0FCaXQvd0FaM1R0dG1wTFJ0TkE2d21jajNvbGpScS9TZGVyVFRielYwYVBydzlNeEhPSjRUNnFnYXNqU0pzRUx4dDhSTzJqcDJ5SmtUNzZ2ZkxCZU1oMmFEUkZHWmMyVERLM0lPM0pWeVk3NVM1WG9ubDJyaWQ4N2ZqbXlaMndHM1h4anNjdmJKaWJzWEZNeEh1WmhKYkd1NDlzUnk4bFFqRVRGVWUyVHhNL1hjUmJNakdpRnE2cWdhbVBhMlJPSnBkSnB3VmpObnpKQ05mNFo2YzAvcDQxaDhFT2h3Ni9peEUwcExlZ1Zhc0VYMWk1YUo5Vy9OQXZDMEprZTlFWHF4LzhBc1RDRmc3ZDNKa1ZSdWU1UmZabXUrVmtLMGpDQTFqMDdwZHd2eFhQamtIL2t1TGZRdlpOOCtQUkZ5dm5nbW4rVmlJdStLM3RrckNTYThiM0lvZTVKVmN2c0x1U1VGaXE1UmJ1MWZYK0lWdllxNnBzVUJGZDRXNjd0bnE2eXVSdXgvZ3BPWHUrZVBuTmUyYklteDJPelEwSmE5aHdxNXJzOFFYZndoTVhLL3dDOTVjZmZ5WnBXdFdZUWgvTWpiaVVoUC9XTFBnem1wM21DeUF6b29vK1crV0QvQUplMlI2a0wyN3VrcGphWVMvNjJPcEFmK25GcFFwN1NzU3BiMU9wMXUxWUJrRXFxd3ZMQlBURjlzbDVJamZVbEp2MjhzbnU5KzJlV2ovakl6eTRQd2xZa2VMK2VZbUkyQ3ovZjlwRFJwWm45T1VIQkNsUmpWVE5mTC9DVXJLd2U4MU11azRUeU4zelJjZ0krc041RVJXekFwL3JKaFpBMWIySmtSZDkzWmY2dzFWRGxGWW1tZmt4dkZEVmtnU0dnMHJuTS9hVHI5M3RURnlUNG1hdGh4U1RMeURLR0NENGphaG14QnlvRmRKZUJOZmFrOW5WaE1rNjZ0b01MejlqQVVJcW0zMWZJa0RROUd4Z2Z3eVZraVV4REVId3hUYy95WngveGlOVGI3T1Q0a2swdEhCT1JqQ2lQSFRoNXA2NVlUZW5KT1JzZEZYVHh5RzVFY3paZGNyL0M4aE9XVnYzMW1YNmZ2WSthVGdpTVlwbnF1N0s4RzNaVngwVUtKMjN5T0Z3MmUzYlhWeDVTSStFRW5FbFl0bFZ4V3dZVnRJR0ZrcTVkL3dEYmxZY0VtZkhXTFBuSE9PdmpraXgyeFlza294aGpTeWR2UG13Mmc2NjEweWtoOWpNUG1rclo1R2ZEWlJWY1Q4dVNja3NiMW45c1JxZjJ4TnY3WnczL0FBeDQyS3E4a3lRRkhwMDJPMldiNnBadDhvRTJHN05hcW42cm55cVQ2MW1hZy9xeDgwY3greG5JbWRKemRsYWk0UXo0dnJlUElUa01ORkZBT21ham15cEdxSkxUdmNxRVJwUGNiY1lORVQ3T2FSclljK3hZS2FIbXkyakJpVHlCampSclJ1VnE5bHdjK2F2cFNRNUVwVG5XNkM5U1pzdkZNTzFkc2xKOVE5UDBOMnhNSjFPdXZCRndodmYxSnRxcTIrQ1E1MW4wK2E2QTFHeTBwVW5USk1aWDYyV08vU2huTmtONVZmM3RtMlh2OVRObWpYTlFaZVNwZ0pNZmZicnN5VWFNVU82U0dMbGFZWUt4WkpIcnRNOG5aWHhaZ2x5eldHTXZDQW5wWThpL2x5dG1UNGhVSkRjcU9rU0RGY3BaaStxSWdYdVRsN1M0OVFJWTN4Q3MzYStKR2tqa3huYjRLYkdmRllWT1d6aUFkM2VUYkprQ0R1OTZUZlV1MzZPV0VNMFBjamtUR0dqT2J2eWJqWEJlVlNMSkRsWTZNVWF0QVJyczFvakdhYU1palRlbjI4NlB2bC9zMjFQdG1tb1pwWFdleFV5RFJ0WTkvbitDdGRwZ3NjS0JnQ0V3SjFIQjBYSjh3OXJVcTVMQ09jakJFVkxHeGhBanVqQ3NuQWNrdXNjbkorcVpmS2kxZlF3NEh3NlRhbFYycTcydG1IVnRaSWU5UER5K2cwRHBiN0NhOTJIMWZwMDRURFdkS0lpV2NhRUZpZEdSd2pXbXFscXdGaTJmb1NmcmRZcm1FbEFhK0pMMW9TVnpzclVSQTdmNS9RNWY3WmRLdlNiL0FIcnRRM2RocXY1Qk9iUnZpRFoxWFRHTGxOYjZmZDZIMnczRzE3MjB3YmJLbmJ6bzgxQ3Y3M2s1cEd5RUFoWTZzSXFrczI5dDQ1c0M4azRQMGc5czF5QnkrR2RrSDgrbGE1eWlYdG1wWWpVODIxY1dGNlNLaXJrU0V4cEQ4OThiWGtZeGlGYnhkQ2hmYlJkODB3QnJqVGtjbUdDMXpmYmZOTVYvOE9SSFlRYld0VnZESHhpODNjUnJzc1kvL1V1S0V2OEF3WEZDYi9ndUhoR001UFJqOUYwOVJNK0lWMFFZekZzcktBNVlZTk1kWmkya3dmekM2WGtDeldNa2t6U0pqK1VVQ1ZHNnpoWnFMZGJTUm5oVXN4OGlVd2ZIaFkxWjVET1hzczJzT3gzVTJQbXBTdkpTU0FvS2U1S1I1QVJDdEV6ZDF2SmZJUElZL2tqK2tYWTN5a1hHZ2t2SWNZaHNSdGpFZkhsK1dBeHZBSWkrcnMxdVYxa3NPV1lTWXdxT0cxclNiNXBaVi9WeUp2a25iamdrK1VpNFg3T1B4ZmZKTXFMRlJGa1NHRHpXdmlSOEN2aTZjQkNocG54T1dHU3dUZUhHUlpzZUZXT0lITlcyTXQrbmxHNlMwZzZwclVuaVROUS8xV1QzendzV1I1R1E4Y2hHSUk4dGoxNnhlcG5pRmRtcDRJVERDMGJkWlg4MnJIOE1qWE1TeVRUd2RwR3p2ZlZ6VWJZeWRzVXF0YVJxTlRCSFhxa2J4Yms2UnZQM1JpSWlHOUtyMnlpaHhwY3lRNDRVZGcwYXhHb3h2YlROMnljbnd1L3V6MXd0TnpaVXV1S2g1U3lHaC9rcGhQYkNMdGxqUEJYd2pUNUMvTHFmZ3NxcC9hUjRpUFFwYkdSWFRaa21kTGFRN2g2cDA4aSttekkzUDFycDNkdmpVakxuVVlUeFZxWFdyek9xM3A1NE9haGR2WnlGM3p3MXVoUW9CNDVOa1FtcFlxSjl0RnkwMWJWSUJ3enVZNXBlTmdqbVYxWXdJSXV3amRkcnMxSFBiTGxuSnhWTVZCY0hZQkFvUTN0bHQwM1QzT1Z5YnM0OFVYZktDU3dCNU8rSTdkRXdVYUFmVGRlV1hEYWJDTllNZkViVVJBL3lVd2k5c083OE04UTN5RGFNdGh4YzFpVlRlRzFPd2J0MG90QmFLUFhDUE5qR1FsbkE4SW9CT2tLNG5HV1l6d3pERVU4R1ZiTEkwKytZZlVDUzVMM3Z5cWQ5WUxMNTM3eFBtbUxLaWRhcFRXVmlZUnBtbXRQeEhpWWVSTlZhcXQwckVkMVJRRWM2eEt6Z3F0WEs5d3lGYTBpYnBxU3ZqUjVjbFFBNDU4TnRDRGU1c0Fqc2oxMDlDRmM2dUpoNnVlOHF5QlJEa3dWUGNxaU5mQU9tVVZiMTVraExHSXU3a2FKcWROcU5Ta0Y1alRVTm5MWlRidEZ4M3dKaGRGTnlwaHBVYi92Wmt5WkhhanZuc3k3dXFkMVZJaG5uaVRQUEF0ZERWOUdzcjU5RlFXSVlUZk14aGNLN1RFMmFSekJ5SEprclJ0NURSU3lKUStHbXBjbGRSQ2h2TnVsUTVmTWp5ODM4K2JkYzhPYXF0c05mbk5NaXNJN1dsakFwM3dwYzQvVFpYNnBqVENJWWZOb1RXcGpXSGxTZWxHMzhLcnNFRE1rc0c4SllsaGZJN21oQjZYMFZadlVGeUZnbkJscDVXTVNSOEpBcWFjMUkzVURpK1RxUkR6VldtWjJvbWlSQ3h3NXFlQXROWVBpR2UxVkJxaURhV0xhdW1Na2cxVEhkQ3E0OFZ6dDFrL1lWY2xQMk8vRHY3ZStXYnQyWlltK1c0V1BIR2kvRDBIU3FCMUQ1QndlY3BmWFk2aXFRUkNCWkY0dkRFRGYwQ0pLdEJNYW1oOVBVY2x0bEZ1M21QVHI5U1BMdGZyamQ4OEx5b3p4Qm1acnhsZllKQmoyZ1hPR2x5TXNoS2l0WGloR1JXMmtOakJwMHRWeW9saE9MS2FKdWFPRlp2c1d5UmJPancyTjAvR1pFY25WT1JZMXMzeVVwdlRmY3pOUjJXcDAwVnArYXNSMGp3NnZZVVYwdWkxM1lQbFR0UVRyM1RRcHZVNko5RDJrYXBuc25xTkZXTkpGTWpEa3h5Y21TVitXdVMzZlBma2gzYkozZG1YT3pKRGtUSmxySmVzQ01iaTBXbEp0RWVwSEpESDZtQmxFWXliZUxEWHk5RlpSeVZQbk9BeDVMTjFvWE5Pc2pLbjd5UEx0ZHBoczhOSHFtdnBUZDJiZUlMWkx4eEVqOW5RSWNZUzhIZDgxUFlrZ2pBTURFeDhGc3dmcUs1dVVqaTBoZkxqazdqaGFzb3JPdldkTnN4Q08zVUZGRWhwWVFiWUpTU2kyTmZxdG1ycThiREwrMHlKYTllTlExQlhUUlU0YTZxWlR5cEhVSkJxNDBjdldGelZmRFdmSmNZMWM0MjRwSytoY2xkenZ3L3dDT1RQc3JsOGlkYmZMSmorS09HemZOUGVLbHpweXBGVGdwd09IUzNNV2Q4U1NRSmZJeTZnTFF4aWhzMnlNK09VUjlIa3JINlZucE1vSkE1Q0FJTmQwdDNmVkZ6dzQ2S2VJVXAvUDE2NWxJMWtORVRkWWt1UTkzcGhiSkpwMlhER2luSTFFa1NDQnNqeG9saTlCN3pGOTVpcmswQkNSbmo2MmFhaUY2VE5wSEZiZHBnazJkSzVaVXdqdWNVOE16a0lsWmU3UGM2MWNtTWl5a2xNWWUrSTdLdnhUOE1haEZheFh4WFRmSGp3N1Jla3lYSmRrTFhPbkx5ZDVXdW43a2tQWGJKait5NWJwdXpmamlzNUw3WjVaRlJjdHRESFBBZFBtU280UWVIdDFwM1RNdDBVY0h6U1duamFPaWx5bVNxQmMwOC9xOU1uQkdaYjlwSk1pcnFHTHFVbGxwN3A5UzBKck9XcVQ3STBabVZscHE2YkRiT3JQSTlQNHo0Z043dlNBbVNHZWNQMUpQZHpZUUdmbWRrWUVkVEl4Ujc0SHA3S3FJaVp3NnowSHZsZkViRUYwbTk4NnZGaEhPeW5lMGxzZVR4OVUrcU00WmJOZzJJS2dTMXNLNXZrQlU3R1I2UFVFVTdqUkw4QVhuZzZvTDJOcXlTL0NhZmx2N3lMZVF1TFJzQ3YzZ3k0eUtvdnNrTGdSTlJ2cjZtYVAwQkFmdEN0dm5KcVNyMDU0YTJDWFVablRUV1YzQzFWcU1ka0tQd1pTcWpPaWpQYTBYNXo4c3J1ZFYzQnZKeVhNeVRxaWRPQzRNbVdWeksvVWxoV2g2TVFqbXQvVzIwZXhmTUhlcU1meWJtNnY5c2FybXExMitCTTNiWnE0RW5FemVHK1I1UG8zZGg1L09JWGdtVXE5TVpENGVkTmRHV1A1aDNUcXJXZFR5MG0xNTFZK2wxQkN1Z09Pa0xpVWtscEFLc1VMZVRPcDBmcWhzUjVlRFc4ZHNjamQvYklveHZibi94QUJCRUFBQkFnTUZCQVVJQndnREFBQUFBQUFCQUFJREVSSVRJU0l4UVFRUVVXRVVJREp4Z1NOQ1VtSnlrYUd4SkROamM3TEIwUVVWTUhTQ2tzTFNOS0xoLzlvQUNBRUJBQTAvQW91ekIwYUVSMkhhaEFQdE9pakxMTk5paTBmRmhHa1hqTXpWZTZ2K0ZMcVZkU0s4UTlsaCtuRU9TN1phWFMwTlRqNGxRekV0QTBBekFGWEZORGZyUzJldkEzS2h0LzhBVUZYdXIvaFU5U3BGYVNPNkhzbjBDME1oYUU0dkdVa0hSQkVZV1ltWDB0WU9PRVQ4VkdMcW5VakQ0eVVkN0RoQXdtOVNiNXZyS3ZkWHU3bFBkTDBWN0s5bGR5N2tCMUtyelN0TUc2bVFoc2lEM3J6YlhhQzc0U0sxSmpPUCtLdG9rTXo3MDU3VGhWTGZ4aFZxYUYwbkx2WGVVRG4xTzVkeWJ5VGh3NmhjVjNMdUs5Z3IyU3VjTXEyZE9TdXpXSDhhclUxVk9XK2FoaytWRFhHNWFPYnNwSVB4WDhtUDFUZTA1a0JsM3hUMnpodmZDWmVQZXY1Y2Y3SnBIbEtQMEtKRlR1WHYzaDU2a3BPRFlrbHptZzkwc1NlUk1UdUZ5d2ZpVlkrYXJVNUljOTJoVVJwQkl6RTB6c3NiRXlYM3hUdTB5SkZKQlRMbXNaRklBWDN4UWY1ZUUrUGg1RlF4aGM3TnczMW5xVFhGV2grYXFXRDhTdEFyUlZvODBlS1BvT1RZMGcxeUhCdTZkNG1nNlFHN1VOdVZvTjlSNm53VFRJOTZnVmtOQW5NcDBTL290VkdsMkpUWUpWWjQxV3ExV3ZhV21JS0d5b21mQlJJaGRtcFpuZDNJNjdwWXdDaEk1cHpaakxjVE1DN3FIYzV4SjhweFF2ZGRjRWJPK1hyQldnVm9WUFVvakRKeVlKUTJWNUJNMk4xUkp1eVU4d3d5UWRqSmhPSzQ0cEsxbmFtRTdEeVJuZlE1Um9MYWFXRTBpZWFjNmw4NEp3Y2hJWElBQUd5S2V6eVE2TzNDRk9iWTBnWmpoVEphc0d6TmFlcE82YVpYRG9jZmo3MFBzMy9vb3pnMkhBWk1rcXVGK0lLMEN0U3M4TFY5MmdMNjdsMFdjdjZnaUpTOEVDVllzS3RBUWZGVGlOSUI0elIyT1h1VnF3LzlWSlVINTdwbGQyOGNWTTFQdGpybmNoNThQYldqNWhWQ2J4dE1NeXZSanc1UTNQbWMxYUJXcFFIRFZEZ3ZzZG9zL2VuQ21jVGJTV3lubkxWTnFMUjRKM2FhWVZONnNSZVNueFdWejRjVTJNLzVJN0hweUlVYVBCYVhUN09GWlZLZy9NOVp4azJ0OHBsUGZhUDIzYUJXQU9GSVJGNTZPNlFYT0M1UklqUVhzZzBnbWFyQ3RTcmFYMVUxNnJhVnRPMU5oUmRwaUNiWUE5SjBsdEVBeXNXU01LL1BEY2ptcTBJQjg3bW5PaER0ODBZNTg3MVYwVjJSNWhNc2kyZnNvYUJRVy9STWRrSXdubURxb1VaeklHMGtTTVZtaDZrQ0U2SS91QW10cmZWc2NHTE10MmVIUENHRGp6VG81c2cwMCtUMFhxeFNQelI0UnorcUVjVXc0bm1xMEN0U3JZa1hMWEpFWHRkZk5PN1Q2SkFwZ2NYZDBrKzhUVmc3NXFVTStNMTBtNysxZEhpZk1KMWlCZjZxMWtvTEdPYXdzQnZRRndBNm5RSWsvY3JPSFQvWW9rRmgvd0NRY1Z3UEJmWnh6L29nTUVOenNqN2s2SVhGNW5tcXdyUXFPN3lVTnJwTmNvanBTYnRCdlE4Nk5qSTk2cS9WT1k2NU5OMGtZUnlnbmlwUXkzeWVzMDZKcnM4cFllNVdMeDlWck5NYkNjMFBISk5Gd0NzRzNvQmQ2OXBTOUpSWUxtT3hjUW9PMkdFUU01WDNxeUZEeVh6T1drN2syNnF3a1BlaExGYUlSQ085VmhWbFFOaXJoVmVhYXMxVzhWZUNGekFJSkplZUtlWmR6dUNhUzF3THNsRWpYRUhPNU9lSFV4SXVZQ2hzTHBBajlGQ2xWVy9qNEtGUFVtYzFDbUM5dVJUK3pEWURlbVFnRHVyTzhGUGlsN3RwdHFyYk1UcDBWaXdERTg4SnFSR0xNSjhyd1JPNVY0WWVHVi9jcXdxeXYzYi9BSmhXN3NQSENtTjhwRTBaeTcxRHFuODFsT1dhaFJNZVYwNXF6QmpPZS9DeWVnQ2NLWU1lRVNMK2ZGQmhmdG0xRHpHcHQ5TzB4SnNlZUhKTWptSHRFdUtoSEhkOFU5czJrYTdxenZLMldLYVh0blZlakRBdE9qbStWM0JNd3h5WWZZT2srOUh0MC9zMmN2SFZPaVlRN1oyc0F2eXVWWVZhT3dlT2E2UVExM0REZXAzeXlVUXUweXVSMUNpUkJVUzFCdmxtdnVCbHFPS0xxb1VKbDgzVDE0SnpERDJ1QzU5TlFPb1BGTWJMNlM0Tll3L21ueERHalBZOENiaXNqVkVURzFNYnd2M1ZucUIyU2dpVFhSSE9ubXRxQlphdWRTMG5UUEpSTDNiTHNaYzZnNkxaelRFaU0yY3lVeGVxbDBHNFQ1b3huU3ZrT3l2dmYvRUwyMFpoTWlFTUR1RzZXU2RPK2tJYTBCVnlKbG91U3FhNThJUHZJbXNuQ3huOGlpTzBJRjN4S2ZNdFlSdjQ3NE8wVWhqb2tpNGFYSWkreUV5Q296SlFRKzR2UE5QaVZCbzB2VlNiQ3BOcHdLZ05KTitpY0xxbTNvTGpOZTBqeFhFYW9uVlp6NHF0QXlxOFZhbk9LSjVwbHp5ZjJjeDhRZTFNRStLT3NDQzFxNVBrdWNVcjcwcjIxM3AvcEZTRUxvOEZuYTVsTWhGczlUSlhLYUxSa25adExzL2d2UkRrN2daZmx2MDNETzlhSzlQaUVxZlkwUStQSXBsOGVHQmQzaFN3MXJXakxxZi94QUFtRUFBQ0FnSUNBUVFDQXdFQUFBQUFBQUFCRVFBaE1VRlJZWEdCa2FHeDBmREI0ZkVRLzlvQUNBRUJBQUUvSVRyak16TG8wUVpsK1E1VENoSDY0Y2dzQUEvaU1HNS9zd2dBTkNKbjZxQVZEb3hrM2hpQ2ZSakFKd1J3SERYditld2dBTXd4cDIrNFRnNHhDQVNJejJhOEVqNDJlaEZqZWNTQS9DWkY1aHZWVm1ZV2g0ZVp0eEU0K0J0TUZHSnM2U1JpbjlERWhuRS9SQ2hBTnl3WVpEWWNHNGNRVHBjd1k2SXovWUNPcHEvcHdRSm10RlFjR0dFTGI3Z29yRWhjcnZqc1FnelE5WWRZMnhuSkJoNUgyREQwc0tQQnhoajRrN1RrNHpWRWdHK1ljZFRlb2U2eTRNSTBueGNRWFIrVVdMaENhVVlrZFE5ZDQvcUVrMzMvQUpoekJvUWlTVVZDQVlDcjJKVzNJbERHd2EvZTRRc2Q3RUdRR1lKTUEySU0xWnpERG9YWVYyanFCdXJoOGNFUFc4bWpESzlnbUxjaGZYL29rREZDU0NCMDRJSG1CUFJnVHVKaHRyL1Vjak9sZ0NOeStXVFhVeFBiS0NkQUFZTkJneEN6Qk1XU1BhSEN4QWFCUnVCYSsvOEE1UXVONjJTT2ZlVlpwd2h0emdoQUFrOFpoUmMxbDBXWXcvTWlvQ2E1Y2FmTUF5U3BKMlNIQnVDUTVtUm00UVFtYTI1cEcxK0VPQXNqOXpLelV6QXBJanBRaFVLWXhOelpRWDRuTVI1TkdHTFBYcFdUSUd5K2gvY2g0ZldiRmFhQXhUSXg1TjRMdTZSL2poUU5ab3NsQVc1TWZ6bWFJUGVlcHhFVWd3NERvTjl3UkVmZVcvdkFaQzFnenJJNVN6M08wWUhNNTVoanVQeTNNTmlEQmhGcTdlMklLcEtZQ21VQmtpbEhVTVB0SXpZUG1Kd08xRmd3QmR3NlFGSDlrcmhaemFya2p3ZGVSZGh3WkMwZ2VBSEw2RWNuOHNLbkFBMGhiVjVtZEluWVBQWWhKRklXYWoybmc3aVA0UWNUSUN5eFAyRTVnSWh4QmFMcmhsNVVld2k4L3dCakFuSi9kRUJSeWhEdU5uMElRRHdoZEkyZUZBQ3pPYzRoZjFEUGQ1MEI3Z3dFVkVGQ0pBV01BR2toajd3dUkrSWxrMkZRSHlCbUh2Y3dycUUvaWpwaFF3aWtIM0Jna1RVY3ppcml3bHZCMXdxQk9rNmxhUUZhY09nUU1BaGtVa2paeEN0WUxSZEd2V0hYdy9jZDdkNE54clVUMEl5Q2pyT1dwSUdCam83QTZoQm8wVUo3d1ZKVWJXU1ovVElCQ3NFU0t1TENTRGhDSUZyUmhLb3JLVjd3U3dqVG9lSVhNbUhKZThIcUFkT0tOYXczM0xGUkhNRldENlRISWxIMHFKTVBDVS9tVjU2dzJ4Z2hHVkpobEs4NGhJd2g3bklGelUvWkZHMkkrc0NJb1hCREFHbzFIMEUvTC9DSkFpUGtPQXNNbUN4SXdIeTlIY01JR1R5T2JHSU1MV1V6WXBTNXBUTmtFdTNwR1ppTUJnWk44UUYreG5qWkR1WGRJR0REVTdRM3lOd0ZhekJ1c2x6SlFOWFVIYWFLb1pVank0REgxcHRSQ1lETjNHeEFDbUwzd05TdHMyb2JNVERLUUxScHA3UlNJU3NTSHNoQWk0Wm1yTlVQTXlYQW5OWVJmNUplSllSeGNhTmUrRkh3SkJKYWhOSmtGSUNQb041NUlXY0FoNEJDYXpHK29jcEQ0UUVzQVIwY01JSllGNWtBTFBpQ1o1MmcrUVVkMUdhT1FnMlZJVWNHeWpZbmk4VlJqaWNReVE3Z3N3NytxRlY4VUs4TnBueHRiWkNyY0FTUldTRGg4WGhRVm04VjE5a3Rja1ZCdUIvYkxEUi9sbWJnSG1hUzc4bmhCWUFNcSszS1E5Q1pKSmtTblV4R0tHYUlSNXVNc2pXTGhNNFlySnRFUmxQQXh1ZmxEd3VJS2FacU10ZXNLRFBnYjVnQWtnSmtRNUNlWmRHMUJCWnlnSWlCQndEZVZkUm9RQ2xOMGc4bWJqS0F3WTVqQ3pVTEF6bUpSMjh3K095RGhNWTNveEY2YWFDVmJPb1F6UmY3eGFLWno1Z2JHd1oyaHR4SFFrVVVCNkZ3M3FpSlFIeE5RN2FHT1NaRWJhY0tmSG1vZHFCMndUK0lzckZENVF4ajQzWDBaNmhDblNvL2lpZXM3RDdVVlMzQ0hhQWlqRUNhSDVtUTdJeE1YUmFlZVh2dmNVbDZoV2dtY1FkSVBhTDZoYjI1Q2kzZFBhRGJGcVlabmcycVV1QmZzS1NWVjRYUGVLeGw4T3JBbFBHRys4SGp4KytIallKWGdSZ0hqQVFpV1VsQTlFS3ZZbEh4QnVLcUJZT0VLNTE1WTVtdlcyK0tRMTJyZlBINk5qaVRBSUFjUTBKRndHcjZYQWt5RnlOUWVzZGdZRW85RW4wbFpyRlFFSUR3eEpNeS93Q1VVd01ya1pnZWpnb3RVR2tVTUx3RmJFd3JDM1hFQ1N3TGIvQ05FTHBkWk5HSEV0dkVqY3ZFV1pmN3dmUVB1aXFyR2FPTFZHRlZLdWhlT0lONHRPN1dRRXhOUVY1ZzR5d0lMTXlCakJQWWtYNklSQlp0NnlhekRNVWR0b21zUHhPUmp5TUhVRUFKTWd0eDZ6cmlHUk5naXE3bGl3UStZR04xV3hnUVh4SWRGZUdBeGdvWHlpQkpnRVdPTlBXZFNQc0VIY0h0Q2hvYlNLdGNvTERJMnQ0b3h3UG9nNk80QWMrWDFoRjJFZVlGTU10elhUdU5CN1lFMmV2aUEyRmVTWUFJRGdRUXVjUE1mY2lIRWRBamxpTFJqSG1XUXVoK3owNWtKSkxVSTQySEpWMUtHMXBqRHNrdWtCdnp1Y0E2SEVlSzVlQkdWUFhKaXRuVWVHSURtVUVPcCt6TXkrWWZqa0VUSDdpZUtxZ0tpZ1ZLZEdNeU9RQXBtaG5pYytRbG5LZzhiblVXWWJQVVZlOGxORDN0aUV0djhrVzJsSHYvQU1Lai9DUUJUNFBxSDJwUmhjb2VFSW9lOEkyNXhCTW8xQzAzNWh0cW1wVUFYTXVxZ1pvTzlybURaN3VKb0FPaGgzWm1saEtRWDdjclA2N3FPM3JIcWdPT2xITGxWdVlBMGRQbUJQZzljTWNEcUdwL1F5K2pDZEZGZ3JlS3lCeVdSeDFFL3dBMG14Rlp0WWdFU3pEUXk4bFZCb3RXdjJWY29yaXdyQ01vQ210eXViZ0FYK3AzS2h5KzV4WEU5UitJVjNLbkVreEc2aHVHN2l3SThwL2NXQ2hFNmt4RTRqbEUrSU9SVEN4SGxHNDlKdlFlWWFzSzlnc1FPREV0TzIzWjhBSmczZ1d5VEpCS3ZpRzBPYWxNdGdvY1ErckNLdmdUT3IvdHphNEwwSVpzL3dDa0lKb1ZEbEc0aHIxWkJ1VTJ2WkcxNW1BempneG9ZaXdFRnoxWTRKUFVkTm9oaDVxb3htZ0JCYmcvV3d5ek5HME9tNVhROUtpTmt5cnBiQkxmVU5aQTBmY01YeWlNSGhtTG13NUFuSkRqTXBFcEtKNzFEMmtnMHg4Q1Uzd1lnTmE5NE5vNXkrb09RaFJBQWRUUEVZQmwyYUVmRHgvQmhHSWF2eVJtRUVvVmw0NGd6WnVrZU9DeWN3NDg0UTF0KzNDaXZjelZSS3poTGlxK0RtWUxPQUp3aHM5b2tlSVVnVm1vNWdDUXZtbUZsYm9RdnBrMlJRckVJWnVaa2hWRFhoQ1RLeStUQXBqd1l4QnZBdFNrTjVFRGJGUWV5TGpzWXh1Z01wWmF6OC82VGQ3RmpaYUVHeS9NUjVLNDlFWWxMblhDRlFpV1hJL2pUYXp2KzFQZjNvbjJZZUcxaDVqbVFMQ0FZY1pyeUU1TWR6TEcvRTNFU0ZQQmpoeXlDQWh4Sy9pQjJzRS8waTFWblErUkNHalFpbUN3bkVLVEY2aDZaUDhBS0ZPQjFDM0Uyd1RwYVFLREZka1g1aHk1WklkU3dOY3ZCYlA2d093YkVTdUtBdmJ4MUNqdlJCOXFBazg1ejlvQmhlRUxZWDBJVVZqTS84UUFKQkFCQUFNQUFnSUNBd0VCQVFFQUFBQUFBUUFSSVRGQlVXRnhrWUdoc2RIQjRmRC8yZ0FJQVFFQUFUOFFDZmhOUnJyYkFwNGppVFF0NTVHRnBxQjh2WjVlc2dONURha1BVVXNUeEppQ0FLTUlucjlLeGJDOGlRdXdKWFQyQkFBTDRSVGViUnBINFJHckFJaCtadTNPWVlsckg5WXBLZkpHMWx0bURYdm1Bb211bnUwdHFxdlJITFQzbk1yQ1BaSEc0VXRkZzVxNzlKQ1lxajYvT21sYTdmQWwzZXpnWlZzZ1VBV1lIcWVsMjhxS0YzMGpVdlp3UWgxU3hhTWF0OHdRbkVLcjF6UEpYZ3lMVDFDYWRTdVNoM3FGWXVHcFExRWcvQitHYUVFcmlkUXpPSXVjL2NZYTZ2N1ZDd1BleDIvQk9ZcER3UTNxT2Q1YlF5VnNudXRTcmFyZGhQWjFMbDFITEZ6VjlqY0tvejJMaUpRUXVkcGloY1ZBWXVHdFFleW1hcldVVEtmaHJDenBzdnlobEZ6NXgzQjBDMkNzR3dkQUNzREl4VkpDdVU5TVR3UEM2d1JBMXFsYkNLNGpqb2lXVlhHMHNFNVhYT2tMdUQyakhjOWRCbG9ud1Qvc1BlUGtKdjZoM1JRVmlyV3UyT2xhc0tyY0t4aHQrU3FnTzlZallLUHpIb21vZGxzUnJJQmhLbW9mS3k3TmhDQ1hsYm5Da0pmaW1UbHRRYXZSaWMrZ1djS3orbExpcmpGWVdsNkhJakpUUkFIVE9UckNRM2NQQ25pZE84VHVzZzZwY1BZc3BaSzliUFArV1ZhbDRkUDFGNmFjdjRMZ0dGRkM3aHFSMklzVXU5cHVCM1ZkOVRmL0FCbitVUTB2am4va1dCM2dsdEFhR0RuaUNWWnJzQkY4azBXTHMwVHVUakxOL2FLaE9FdU9xczd1TnJDMDAvQVpHaHBuQ05mNHFjQUYxUWYwUXdCSEFhYzkxQjlUb0lmWVM2a0ZodEx1RGE2eUpaZWxMbFJTeHZJZFhyRHNhb0hiTjZKREVnUzdlcXdMbWhQRzdWU25wVHhjR3ZCM3pCNU9iTUtyblFDeTIzSmZBVlJMcEl4QjlwZVhINHdqc1VtM3FkYnAzRVVwVDh5MnBoYU9EN3Uvc3ZzVFdIRWJ2ZlZXRml0TFdaRlBBR2ljUThrZW5VME5wUXk2TTNuRnRWMDhlR3NYMmJERllZd1FBdHVTc0JuU0MzUXQyNHJrNDF3RVhvUUsyS3JLN2wxQjVKQU4zZTRpcllYZ0V5OE4yd2dnZlROS2dLd0F3TVljSVVnemlKeG1MenJGeXlhRnNYMmQrb242Q3dRSUZHbEZ2MU5odWd0SkI0dFZtOTRScHI2WUtFUmo2TGFPZ3owc1Vzb0tCQ1JkZFFUN2hJZTdaNjcxR0wzS2pKOUtDbDM1K3lISHdtYU9CRUZ2VVh5dEtvQm5qaEVpWGpNbGtQOEFsRElBOXdhWDVpaTNCMFZ1Rm1PRjJoc0czZmNNa3BzSzBPbUR4MmczM0d4ejloOVp1RFYxMmNQQllVUHVZQUo5RmcyOEpRNllxWXJ0TXZ4RDZCTjY5NUZZMm5ud0pGY3JmTVNnVUdEK09ZV0Jua2hoaGhVRStSbW1TMDZDaHFyRGFtZGt5dmRvR2NZRDhyamlMU2tXdDhQSkdJRjhJemRITW9zdkZYeXdHbTJOc2doRmhBTmRUUjkrSWw5ZWZNSWVRK0tsdENjYzBVTkxUWVhYVVllOWdLcG91N0FQUlliRDgveU5CdG9ncnZUajRrSURRSytkaXBWWUdOY21zRWZWMDQ5ZmNkQmsrUm9JeXp2bElMRjNpc25NZjVJcmFvOHpjRm9ONDQyNHVQQ3RSN3pOci9uUDFaRmVmcXFlVk9wUXo2K0FYem5VMk1yMmZsYUhIUkd6MXdtbEZGdS9QdUF1OHFqWGs0VGNqTmtmVzNVWVFCSlZYZ1hVZU5CK0Ryc2QwNTVrYlN5dVZKeFZHMkN3a3BXYWNmS0FwWXdTMUYxc29MVCtOQ3dMYzJpdENWMnEwK1d1T3EybitNM3NBMDdMUXdkRHVWMGVwUlJZVldsN1pveWxZNk4wV3NMMnp1S3F2Y1ZBV2hIQlpYTFlHaWg3TXJ1YkdjbmtwQSt4a3ZFa1N1YzJMQWxRdWQxS014UlN0bFBNSk9wVlU3QTNHekZMM2RMbGFFSFVmUFVjcFd5V0M3Y1ovWW1nQlpWclJzNTRnOFFLOGxBTmRndUwrVUVka2RvdmlZejErTlcxUXIwd3J2R2tTVWhkSW8yTldmS0dkNHBKVHdmaUJRSlErOXE4aEJETXhGWVc5akdzeHZqdVVHd09LOWFBRUJXd1lrZElrdlBPWlpSa3FhN016SnZQL2FlRG0rY2ovd0Q0ZkNZdjFTQXRtQzYrcnVQSFB0V3FLanBhNFd5MHRmbVc5YW1WeXVKanppQ2dCR3BTZTNGMyt2VnpGSWRPUUE1dEQ2dUl3KzlpVjRHZll1d1IvU1VXZ0tidDZKVHBWZk5iT0JBYTd6cHdyaTBsdUpZNFp5cVc1dDlxV2dvTmZXWW4xUytHdi9rdGZWVVd2d2F6K0piWFlRdW1DNWVaWFd6VCtnNVMyMGNkeE1IRlJGeHlyNW80b2dFRmVqd1psaUlpWnh5aFpsd2RybGhlaW8xdUU2dkRwbUYzeTFUbGdqOHNHY3F4QTFrd3lMVXVzWmVqRDJRU2tGK004RHFoOTNMNE1DS0NLMDFaNng3Z1hkcUNDQUM2bXVFUEVXK0hWbGlRb1VXN1RCS29NYldTYThmOFlYYzVKRGpkMzNjbzRsME5IQzB4V0lNVkJlbDVwMG5FdUU5YTYvTEI3bUNrQ2s5dlRCMVdpY2FMNVk0QmJMdXhtbDBhbDgyZGNRU0Z1aXFWWTBsb0VPM3FOc3FoeFFVMmNBYTlFUllXc0R6Y0VPVndsQitxWTlRaWdLV2xGR1ZUZ3VzWTA3NGowVnRkMlRPY0dMOHRiSUZ4QXVvQXF0YXhYQ0ZKWEQ1UVd5eUVPVGs4WXdpVlZLbmFsZFBCdCtvSHF1RTBHMXl4dUs4d3IzSmhTTTRkbmx5dmNJMGZHWkxQOFBEMzRKbnQ2N0hyeWlKTXlRTTlUR1A5SUVyNFlJMUEzVm5RWVFCcGlNcGNTQlJQK21BeUZUeTVRTUdXditXVWdyalkyOHFxaTJrV09mNDg4Q1VzbDhxc3M5NnNWbnh3eGJTb0xCVitGaGlibjF4YkJFNGJVWDNDT2JkdjNtWTZyWDUzRTB4U2xrTzRWREZVUGRNNWlDSE9oZUZZeGZjVjVtOE1MNXpWSG1JakQrVmlXbWNBUElGTkhCYTRpU3E0MVhhSWJJenh2SU5QZU1JZ1NMVGtiakU1MU5GUGVtUldyTURWS3JYcVhkRWxpQzhEbDFUTExwQzBFZ1ZqTVpqV0VicWc0SmZYUCsyQXo1SVkrdFhEUkdlYkJQekJGcXUvQkYwL0ZNVHoxUkRtMk5XU2hDNjRXckhxbHN1VmhlVWFTcmFtalEyeDZ5YnRLcWtjclNtQlIwb2ZyQ0NKaytBdFBicUJRMEpXbXRxVjRSR0FwckZWSFc0TXFidDVmQzZQZ0k3SDFsY1lnb25NMGhab2p4R2pyeVhFTXI1NmxCOVd3UU0rUnhMN2JyZVZxUElGMzNGUFcrZnJSWFpxM21EYXFsWENjQ3h6V0V0Mm9EUXhCelFrQ3lJakRnNElLMStvNHRsMDg1Wk5CTFkrYUhMRFd3d0FSdHZ1STNaZVhmOEEyTDJvclU2L2NIeGlURGRoZm1INmFJTHdXTEVBRnh4VWNUd1dwMnRVdzBReUdRdXVBeFo4N2NBb1lOVzBPQTM2NVdMTVVKU212dHhPWWIrVEdIRnBUTlBFRzV1MkNxOUFVdUM1VnNlVUtLeGR3ZS9UaVYwTEwxV0hjQU1Ea09Md3J1bHFFaktMakdUeHdkUUxDeW5qUWlleVY4WTlCYjJDdUxBcVFNQ0NnSEtpVUJFaldTamV1Y1VzaDllbFZRdFBjSU5BdTF2Y0JKV0I1UVZXZ0Z1NFBuTEYyWE9vcWtlbFFLTGovWENPWTc5dzNrdFM1VTdhNTcyZC93QWpBM0NoOTFjVXlvODJ3WU14UTNhaElGVkRQaVRVZGxCVmVtTHlmYzIrTVZ2aVhqRUZzS0Zzb0dta2JtZkh0ODVtWVZjVGpVM3cxbGVhU0EwdGZKM0ZXOTdMTzNiMHFLT0x4ZlMrWWUvM25DOFU4cXN2S3NTT3R0QUt1cDBPWmFLZGF0WWlyc3BGNnNoT0hRQWhMVzNEWElDaW1yUlZDcGV6aTM5QVNLRlZvSzFRV0VZNDFWQ0d3SGtLT0VxcHk2cWRJQVU4cExWU0NERkZLT3Z0T0hoMmVyN2llQmNPalA4QTBTbm0rNFExczhSbVNqKzQ1ZmdvWVJZZFdsTEkrTXFkS3lMRmwzdU9ieHY4dTh0UkEweHRZdWkyQTBBSjJKOThsYXk4c29hTnNNbHB1QnRwTDN1VzlMcUJGWnI2N1F2RWpodThjNTV3YkRxMEJPZ2E3ekc0N1BDM0wzWXZCeVlkWnovQ0YwU2x0TzRsQkJVSE44azV1ZUd4T0hWN01hTUJLSnNJa0ZHclRqcE1Sd3RQYWI0SVhxeG5QZnNkeGVaRzY1T1U2L2k2RFl6aTA1NXdJYUZXMTFDSnlUcUZ1d0RoTEkvQzBENnVYVDQ0SU4rbjZjd1FiZjY0QkZ3YkdyZFZoS0phckNKRVBSeFNrZjdGMnJpQ2lXaHl1b210WE10WjRUdHhjTFUwcEtSeEZVaGppUmZkdmhocmNkOWRWR2dhZE9SbXJiL2RsZUladGFWU21panNxTFhCZzZERVdxYXp0bGJ6ODdFeFUwSHF1TzRnWFVOOCtKUlhPYkhpQlNFOHEwanllUkpRdFplSCtTOWhHc0FGODFCTUQ2a0ZORkh3YWlkZHhMTnJvaXI3ZHgwMmI3RGdRR2lnc3FlVFMxeTVISmF3RU5nbU9keXEyYitlS2hvZk5NVXNaakg1VVJjRFFSTFhGbGgxY1JiMGVDSzQ2eHNGa29HL0JLcDI3UEFuRmN4T05qVHNxcUNEeXhZdjIxSzRQZ01WUjIweVpTNldtMFRjbmxrSDBHUlRFYTFHeDE0WW41TllSNFJhanU0cFFYNW9PVkZRWlVvVFFiZ3U5ZGxzcGRLeTRESDZLdzA1ZUw4Wm5nakFuS000SDF4RlAyRm5TZmxnMDNtdEdyeUFjSGxIeWJndjNXSmU1WDQ0K0FsT1ZaWnl5SjZtaTVTeXZxQnB4WFlVWGUzOHpmcmJ0cUdCMmcvZHRjcGFVS2VqSVBZeFd2Mkp6S2FORnNPREF0STE5TXhuK1I2bjU2Z1EvVzBXSG9VZ08wU0Y4KzVaTElhcUwwck45blppa29BN0NyaGRnQlQzV1J2WnJyNHVjalQ0M1ZidFEvMm5tbkZ0cVZHVkVUWmJVR3B1T0YvZzFwUnd1T1piTzBDUG1GaWs0TU1VTFZMdWpGNjVxTGV0VXZEOHhkc2VBV2ZaN2xaWVFnZFRsV05hc0E0MlFlVVFGNE1QNHh3c3ZNdGJxZHlsNEp2emZBUFdqRmpmbmdROW52cEUydkN3cmkycStKby9FdEtYbGN1SXV0QjZYY3hTSFFVUlBlWUFHbjNrLzhRQUtSRUFBd0FEQUFFREJBSUNBd0VBQUFBQUFRSURBQVFSRWhNaE1nVWlNVE1RRkNBakJoVkJKUC9hQUFnQkFnRUJCUUNqbG5kV2JOT1Ryc1UvU3Y0ZjkyRThXSDVyN0pIOWYrQUpHZS9UanR3cjhRTUhSL0NKNXNvOGplRkEwSW1CSTRLZnBRZEQvdHhuQXlKNmJmcmordjhBR2RIOEE4d1ozSFk4WWR5WiszeXdFNEd3c2ZRTXJ2RzVSbTE5a1hCSE1vM1pwOFc1Nm5WNTJPUzU1YkI1T1Y1cWcycFovYmdCL2RobjlxSUVicFppdUZPaGw4U0hrbWV0STU2ODFNOW5TQ0o5UjFFQStwY3pZK2YwNzdRUHhUNExsL3VJaWVlaWNnT1p0SDdFZ3BBMVZ4dFZlSFhVRDB6elVVUVBSL0ZQWW4zSVVEQ0pIQVpEQVk0dElBVi9icCtJSXloNmd5bnRYelRDNjhqOGZTMHJLOC9wK3ZUMS9wWEZPanNVcStoS2gyUHBYTlpkVzdCMC9pZ0dQY0F0WHVkenlHR3pER3Njb1I2a1QwcmpkOGZ3YWtzd21tRUp5U2tMS0hxMmVjS01ZNjJMNk0zb0pzeFdISmhKSVBZNVUrOWYyZngxTUlVNXdaUWRhQUF5WnhqN0g4MlBISlk0Q3laRStTeFJWZ2hJSlk0VC9zUGU1K0F4KzMveDhkR2FyZ3BoWXNNSjl5NHhBWDJtOFkwa29PTm4vdXdCNml1blhyUG1yd3lCb2tKQm14Z0FHQy95M09NdlFvSW42Sk9PZ2dvQzBENkVIei9yRDIyaExYRnE2VW5EcDV6SWJJbjdtdy9MWkk4ekNIVzA3dFhRSGpyT0JpS3hKUTVzNlZxblgwTmliWDE2dWs5VFlVbWJPVDVxSGV3VTBheUJjNWxmV1YvcnJ6YVczOVArbmEvMHBWQzVyK0FFMUFkdTV6cmJjM0Zic3lLaGVxZlRlS2R1dnZyTVNveGo3QmdjTGN6WllnQW5BZ00ySHNxbm5nK2NiQ09qL2tQMDdaM0NYMzc2Y3ZaSDZGaDBsK1lQbE1BYkZvZXFyUk1EclZEVXZ4bWtnVkFSblZBay9rblFNZVFkT2NLZkNtVCtCL0dIM0xKNUNYMGRkaUF1eWw3RXJyTTdGajNCOG1KUzZXb3JGUnM1cjZzamxUM05VandBeGg3VFRpZU9Ybzgwd0llUGt1bWVIMndBdTE2TkU2MVZDLzA2akRxMjhkU05UaHhmZHJPSjJOMHc3SzhPeVRiaGJOZVJTWTdqZDVIb25sMExybmVUYm5KZXlZeEdhLzd3UC9zdFd2WTcyOWRCdGJoeVZJK25UbkpqTi9XclYxMVVRaWMxSGY4QWMzMmpXbzdKNmtnV3JQaVZRQTFrTXZYeFQzT2M3SnZaWWtDWlplRnVtYzdGakVqYXNOWGxvejhFbEI4bnFTazFSd1MvRzMwdzkrTjBuWWl3b0lQV1pEcHJ5aTc0ZlpaVjlYTFJOTUNGVy9xdWlLUHNvUHRYOFRBN0g1UlE5SWMzb0tESTYxaTFybUY5YmN2ZHJaSWUyNlF1dkl0UXQ5bU9GYXVtalRUYk14TWY2aDMxTWhPY3BEYVJzaEJKN08wakhHSjVUMldmajRxdzdNKyt1ZXpPbWs5aXlYU2pxT1drV3RySjRWdCtOY0RuMUJldzF3L2taak5LQ1VZVUtaWlJkRHdMTmtLU1pYajZBUXJWbXBTenVHL05QZ254QU9TeldQdGRXS1YxS1Vlc3lxeVBNOUVpdXdwVTYzeCtva2pYMUZMczgvRVNzMFNnRG9FUVlWOW1WWnpod3F3bXA4bzUxQ0JxVTQranNPdE5QWWdpTUFKa2MxOEI5bmJpeTJsT1U5VmpPTkd6WkhCcmZDOG8xMTlaZExXeWcxQS9OSG85aC9CQjczd0RzWFBNWWRYdVVFZ3hFV1ZaNnE0cGpnZE9lb3E0OWp4OXA2VVkycWRPYnpTL3VzUGg0STZMcnhVdEdUa2E4Ui9pUjFlWXN5ci9BTU9pMFVvODg5d1E0TEE0VzRMT1FmL0VBQzBSQUFJQkF3RUdCZ01CQUFNQUFBQUFBQUFCRVFJUUlURURFaUJCVVhFaU1tR0JvYkVUa2RIQkJCVHcvOW9BQ0FFQ0FRWS9BSFU5YktwLyt3eGRyTHRaakcrSnhkOE1jdVpWdEhoRDJqMG43R25aV1hhN0h4Tzd2bXozZXVlM0lWTkNsWTdkV3lwVnN6bUNWZlBRMXRWRm80SkdxYndhWDhhbGtVVW1GOEZhS2wyNEZTYW1vN1pacWVZeFVQSTJuTjVIbkJsa3VvODQ1cit6emZES3U1Vkhwd0p2b2FtbzJKVjdTSDBIUlh0RW11VW1kcXYyL3dDQzJleHJUcWZjZEcwMmlUV3V2OEkvSjkvdzNkalZMSVYyb05MdEl5eXJ1T0RXekZUd1M5RU9xcWhOdm5CNUYraWFLVW4yUTZxcVZMOUR5TDlINU5sU2svUmNENEhKZ2ZBeFIwdExSaENhNWp6WlU4aDNidTBqSzRkeWRXT2xTVE4xMklrYWxFa01jMm02Z2FGekpaRE1vd29QTVR0Tm9sSnVyYUptcVBDSldZbDZHOUdTcXZHU21qbVpadk5TWXBNWVBGcDNGdTJlVklodEhpVjVUd1VLdDh6eHJkYmh5dnFleEtjajN1ZDJLcGFRWlRaNEVVSmtGSit4RGZZOXltT2xrTkdocGVqOGEwMVArdjhBOGpLVUw5RUpRTndTMUZtWTFqL2JOdEtCWVNGSW9FS1NlZUJkeFBvbjlqRndzWlZ0YXBlY0pjeUVOU3YwZWFWWmtweGc4VGtkTFg5WkxVUlpDRjdrSHVVcFBXZnV5ZXFIQXJ3dVorSFpmSE5pcGJoUjhtakhOUHdiOGVHTE04WE5Xd0xQT3lQY1h1ZTMrbnVVKy8zWlE3S3pLZTZLcDZzaWszcUVvOWNmWkZXN0IrT2hyVFNiTVZTWGhqVWpVbUJkL3dEUmlraVJSVWlHMSt6VkMzSHJOb1F4Wk5iSjAwc2RVWWFITGZ3WklTK0IxcFpnZHFWeWtnOUQ4cVhNM3FWcVBxa0t2a053T0ZaMGpxcnhaMnphU3RiOHJwMEhHbnQxSmJ3TlVVNkRwcXBoREVVOXh4eU4wcHA2RzZ4cDZzaEVQOWoydGVlU1JGZENnZE9xaVVRaERNa1hyMnllYW94MkdtL2twb25QSWRPdnVOT0o3c2RxV09IeU41amI1RUV0WlEzWHFoVjBqMmRmZEUxdkE5cFQwaEc2N08yTFFlRWRibVg2Rkc2L0VodHFIMVp2cXBhNnlPMU1GVWREemZCTkluVXNtaEJFRGNHVFFjVWt4TDdqUyt5YWxnaDJSbTNoVXQvWnY3UllKVFRuWDBHcktuYXpIb09taVhQVWROVXljeU9HYjdwRlVtNjZXMTZua01MZzhHRDhiYzh4cXJVcWRrcTFKS1dTV2lWVHd1eW0rN1ZsRVRoNkhpWjRkT0QveEFBbkVRQURBQU1BQVFNRUF3RUJBUUFBQUFBQkFnTUFCQkVTQlNFeEV4UWlJeEF5TXdaQlF2L2FBQWdCQXdFQkJRQlpvaStuMWhJZXBiV3BUUlQvQUZPYy9Ya3hsZmlIdWJEbE03blFBR0J3NXdjWEVIUm5SL0RPTUxjWCtvMTZ6TTdWRmhJL2xQOEEwT2ZFOFJUbE1ndjUyNlgrbm5nYzhEZ1E1enhVRHVJQmc0cEtFbjZSNTREZ1E0Z0gxRWFRcHIrWVcyczBETW55bXZIT0h2Z0ErS0xZL2Vhdy9ZK3ZZdDlyWTROTFpPRFEyY0duWTVmV3JCVVBRcHdIdUNGNkQ3UFpBbnEyY2ZZN2lsdEhZT0RSSkdzZUo2alQ2aFFmblA1T1NQZ1Bycmd1RGxUNUhUSGFQdFVHZmVOaWI5T0p2dnhkbnJiOVRzcUFRQmlaQmlpTFp5cTEyUUMyMGNJMmpqUTNXeVBSSGFKWkUrVUhEM29BSlR3T0tqQTB3VjNaTk03K3lnMVBWT0dPN0NjTlhmc2llbmVzc050ZGpXSEIvQzVEUm8wWmFoUUtuaU9jeU1wbEpSbGtVUDBySUFxODhobi9BTXA3SjVuQXo5YzlOS21jSnZlYUI5blBwMG9zL09TcTFrRldlemtZbzdpRHVhSVAyNEdjT2R5WllLaEl5SjVQWlBXNGVybnhOT2VDOHp4Vjhxb1ZuY20zQVFxQVkzdE9ZNnVGVnpnNE1UMkdyZVV0U2RaMVh1QVpKZjFxbnNlSnFjZWlkSVpjK1VsL20wYStFOWUzZGdFVzhKdlp5RkNNVG56aUh4SFFRcGJxbm1GaDVmVUdUbzFTanZObytxN2NnUFd3QnIrc2JHMW10UGV2SXE0bFZXR0hCN1lEK3VIc2k3bXo0UTlaMFo2dnFORWZjZ2NaazRwSE5UYmxETm4xQ0ZVaHRTUm4zTlpsRkVSRldiR2FRSkU1UWZtY3pXKzJlUDhBekV0bExlbVc5VTI5dDNkemRLRTkvSEIzdzFtUXBxUUZxVVZOZXZxUFh6U240cnNxTy84QWsvbnhJQ3FUbWtQZkhKRlo5NzVUSitvbUJ4aU1BZjhBay9WdFRSbE8rcERhcDdzbnk0Q2tZUGl4L1JDNGtWb0xpOFNKdzZFcXhaL2ZpaGpsRUtNb0pFcStEamdEL3dDa3orVGM2ZzRlbkZIQk9qSXkrc1BIWitncEU0Y2Fxb3BHRCtwQWVieVJsVi90OHRzMVhKamcydjc5OWd3Qm8zNWcrMnZOSGZnQUxnc2k4Tk9oMTc0alA2cnJhNXVkclV1aEd6QTRMdzdlc2xZWjhCSk5TWTEzSVRVYzRtbUZnUGJObHd6OEhFOGN0dzBIeHJPRWIvem5hVC9zNmtzTVJUbE9mU2dSOXZyVThHdDZWRFdyOWpBWmJYdWpML0dqdHBKVzI2MUJhellFSjFsOTgyWktyaVZTRWxYcGxRbFlXSTE0aDM0QXZseWtoMHVwODFWc1FjRFBGUlBaQTF0YXU0S2EyNVZHYmYyRUd6NmpmWWpJOVZ2alc5ckw4cU9EMCs4akU3RTVVRHBTOUtvb0hYeWt2cGlOQkxPOVg3cFdZKzdSL3NXNFNTY3JsbS9HSlU2c2pQdGRxU0pHSTJkYlkxb3hTSjluOWhwanRqeVpRK2VRSlNHMnlPMmdLbW5EWThNeFozcFE2cnJsTDBmVjFDbk1qOHQvYkh6WUFGTmZjYW1yQm90T1JKYUxxa3Rpbm5LT1YrTkEvdHI0ZUtQd2J0M21ETVVHc1RCcG96UGFWSlZvckxVWGR3WktzbzY4MFoxSGpISDlseWh6WVVGOUI1cFdGdFFKS2dkblQ4UllHV3E0ZExIUFR4KzdaUGdFb3paU0MzVjJaS2VkRGlzeXNyTmF0d3lNaHJSUlBZQUFwMDdNems5cVNHZTlDMzhVOXMyY1pPNGdLaXVvMlNFVlY2cU0wejBXK2RhbHAydWR2WXlUYmhUNm05d09SbmtjQnhTT2M4c2pQd3dOd0lUaWN4RFRpbW9ZMjJUaHBiUE55Q081T1l5ZW1rcHA5R0s3ZFVxZFhnRi9rRjBMVnZpVnJQQmF4em5QNUdBOE02Y3hxZVUrOHlZR0t6SWVwbm1yTDRFSndEQW95RXcyZi8vRUFEQVJBQUlDQVFJRUJRTURCUUVCQUFBQUFBQUJBaEVoRURFREVsRnhJQ0pCWWFFVGdaRXlzZEVFTThIaDhEQnkvOW9BQ0FFREFRWS9BT1dEdExZbjlUMkpRaHZpc0xyMzZEOEdkS0hyYTBXajhPTjlGRmJpaFdhRTB0dEg0cUgvQU9ONlllbFBwZ2JtNkZ5ckowc3BqMHdiYUs5OUcxcFNNb3BIbjhDYVpiTUdIOGo1bnBDWFlpMWpSNk9Sc2JNdlNsRTJQMG42UmVYSWsxVmVCVXVoaUJVZUZmNE04TDVSNWVGOG8vcy9LSTEwRmVqMGFSc1pXajVPSGNlcHo4UGh0cCt0R2VFL2orUjhUamNOcUs3ZnlMaWNQaHRwNVdZL3lXdUY4eC9rditvaTRyN0QxakpQZEptK3R5M28ydjhBQXV4blZEZW1kSFR5emxqeEdrdmMvdVA4alVwTnIzYk9WU2RkMlltMTkyZlQ0cmJYcGJmZ2oyWGdWRnNYZ1F4MlVtTk4yTlBaRzJqYUUvQTlJT1Q5RVhCM3JuVG5yS1J6T2lxMFF6bXAvZ3ZsZGRtTkZwbURKUmt3UG0wWlJTTU9tVTNhOXo5SHljdkI0RGsxMHljMCtFNHZvMFZUTGxveERmdWZUVGRkQ1BDbGRyMkp6OUd6WWNJeXBtWnNxVHM4cUh6RFQ3Yk0yZGRoM2d5ZVZqYjBhbkh6ZFRpUzRLZXkvY2ZHNGI1MUJPTFRmdnZYY3BxdndWSDB5Wi83SXhFbyt0bEtTWGQwTlRhZmJLSnVPdzJ5VEg5aDl5dlprcjZJbXZmUnJUTDF0SEVYRWVYUStOL1RPdWE3Uzk3ZlU1bTcwcS8rdlJEdnJweXAwTkoyTmRDVmordzY2bEwzSDJKTHExcExSbDY0WkdFWmN0TE1uc3NQb05zdW4rVENyYlJGTlhrd3ErUlBmOWtWRjNla2hqK3cyTit4SzF0V2pTM01qUm5Ybmwvb2JpcnRtNk1OZmtjRTgydGJqMTFsajA2YVNHUyt3KzU5aWYyMGxvOVpkaGRoOHh5Y1JPL2FuK3hjSXk5c0M0azROWnk2MWxHL05ldzlrYmovQVBuU1RTSGo0RytWbHBQOERWUFlsenJvVWhqWTZOaWg4MGtocGJseFMrVHl4TGY3aTRjbTZiV2pKTkZudXlYQmJ2RkRqSm1NcXh4S1RNc2Q1RXlvWkc5SzFhT2JselcvVVhYN2pRdWVWV0p4bmJ0YU1taGU0NU00a242amFGSmJMY2I5QzQ3SDBvT3ZWc3VFbllwTERUcGpUMWVyb1hBYXhGT24zRktLK0NYRXJEd3kvd0RCYXV1eTFtSzl5bHNSVWZVeVVuaGloRE43RDRVMVRXK1Q2a096S2dzbjA1UE83TFYva3ZSNnNmTzZWRktXTzV4TFhsZnFMTnJvc2ppNHVsaXEvd0FpYUtKa2I2bjZma3FZNHhlRGNzY3JvcTdQSzlodXpNaW02UDhBUnl4dSsybDZZMHk2Vi9BK0h3bmthYWFyYjNMNmpKUGg3bHpwVVdxSHNQMzhGRkVucGRubE9aTlgzTnpmd05UOHg5Vkt2U2pBaGpsRjBVNUdKRFRsNGNhU3JXNDRaZkwzSHlvOHl5VnIvOWs9IiBpZD0iYyIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiLz48L2RlZnM+PC9zdmc+", + "description": "Displays efficiency reading as a horizontal progress bar with background. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'efficiency', label: 'Efficiency', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Efficiency\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 30;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":60,\"color\":\"#F89E0D\"},{\"from\":60,\"to\":80,\"color\":\"#3B911C\"},{\"from\":80,\"to\":null,\"color\":\"#2B54CE\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":100,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:ZWZmaWNpZW5jeV9wcm9ncmVzc19iYXJfYmFja2dyb3VuZC5wbmc=:ZWZmaWNpZW5jeV9wcm9ncmVzc19iYXJfYmFja2dyb3VuZC5wbmc=;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":60,\"color\":\"#F89E0D\"},{\"from\":60,\"to\":80,\"color\":\"#3B911C\"},{\"from\":80,\"to\":null,\"color\":\"#2B54CE\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Efficiency\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "productivity", + "effectiveness", + "performance", + "capability" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/efficiency_range_chart.json b/application/src/main/data/json/system/widget_types/efficiency_range_chart.json new file mode 100644 index 0000000000..8d8e2501b7 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/efficiency_range_chart.json @@ -0,0 +1,32 @@ +{ + "fqn": "efficiency_range_chart", + "name": "Efficiency range chart", + "deprecated": false, + "image": "tb-image:ZWZmaWNpZW5jeV9yYW5nZV9jaGFydC5zdmc=:ZWZmaWNpZW5jeV9yYW5nZV9jaGFydC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxnIGNsaXAtcGF0aD0idXJsKCNhKSI+PHBhdGggZmlsbD0idXJsKCNiKSIgZD0ibTY2LjUgNDkuMi0yMi43IDUyLjYtNy44IDE2LjFWMTM2aDE1MnYtMjMuOGwtLjQtMjQuNy0xMC44IDM3LjUtNi4zLTIzLjQtMi43IDYuOS0xMS43IDI1LjhjLTcuNy0yOC0xNC4yLTM4LjYtMjIuNi02Ni4ybC04LjcgNDEuM2gtMTEuMmwtMi4xLTY3TDEwNyAxNiA4Ni40IDgxLjhsLTUtMi0xNS0zMC42WiIvPjxwYXRoIHN0cm9rZT0idXJsKCNjKSIgc3Ryb2tlLXdpZHRoPSIuOSIgZD0ibTM2IDExOCA3LjktMTYuMiAyMi43LTUyLjYgMTUuMSAzMC42IDUgMkwxMDcuNSAxNmw0IDI2Yy44IDI2LjIgMS43IDQxLjMgMi42IDY3LjRIMTI1bDguNC00MiAyMi41IDY2LjYgMTQuOC0zMi44IDYgMjMuNCAxMS4zLTM3Ii8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Ik0xNC44IDEzLjNWMTloLS43di00LjhsLTEuNC41di0uNmwyLS44aC4xWm01LjkgMi40di45YzAgLjQgMCAuOC0uMiAxLjEgMCAuNC0uMi42LS4zLjgtLjIuMi0uNC40LS42LjRhMiAyIDAgMCAxLS43LjJsLS42LS4xYy0uMiAwLS40LS4xLS41LS4zLS4yIDAtLjMtLjItLjQtLjRsLS4zLS44di0zbC41LS43Yy4xLS4yLjMtLjQuNS0uNGwuOC0uMi42LjFhMS40IDEuNCAwIDAgMSAuOC43bC4zLjd2MVptLS44IDF2LTEuOGwtLjEtLjVhMSAxIDAgMCAwLS4zLS4zYzAtLjEtLjEtLjItLjMtLjJhMSAxIDAgMCAwLS4zIDAgMSAxIDAgMCAwLS41IDBsLS4zLjMtLjIuNi0uMS44djEuOGwuMi41LjIuMy4zLjJoLjhjLjIgMCAuMy0uMi40LS4zbC4yLS42di0uOFptNS40LTF2LjlsLS4xIDEuMS0uNC44Yy0uMi4yLS40LjQtLjYuNGEyIDIgMCAwIDEtLjcuMmwtLjYtLjFjLS4yIDAtLjQtLjEtLjUtLjMtLjIgMC0uMy0uMi0uNC0uNGwtLjItLjgtLjEtMXYtLjhsLjEtMS4yLjQtLjdjLjEtLjIuMy0uNC41LS40bC44LS4yLjYuMWExLjQgMS40IDAgMCAxIC45LjdsLjIuN3YxWm0tLjcgMXYtMS44bC0uMi0uNWExIDEgMCAwIDAtLjItLjNsLS4zLS4yYTEgMSAwIDAgMC0uNCAwIDEgMSAwIDAgMC0uNSAwbC0uMy4zLS4yLjZ2My4xbC4zLjMuMy4yaC45bC4zLS4zLjItLjZ2LS44Wm0xLjctMnYtLjNsLjEtLjYuNC0uNC42LS4yYy4zIDAgLjUgMCAuNy4ybC40LjQuMS42di4zbC0uMS42LS40LjQtLjcuMmMtLjIgMC0uNCAwLS42LS4yYTEgMSAwIDAgMS0uNC0uNGwtLjEtLjZabS41LS4zdi42bC4zLjMuMy4xaC40bC4yLS40VjE0LjFhLjYuNiAwIDAgMC0uNi0uNC42LjYgMCAwIDAtLjUuNHYuM1pNMjkgMTh2LS4zYzAtLjIgMC0uNC4yLS42IDAtLjIuMi0uMy40LS40bC42LS4yYy4yIDAgLjUgMCAuNi4ybC40LjQuMi42di4zYzAgLjIgMCAuNC0uMi42IDAgLjItLjIuMy0uNC40bC0uNi4yYy0uMiAwLS40IDAtLjYtLjItLjIgMC0uMy0uMi0uNC0uNGwtLjItLjZabS42LS4zdi43bC4zLjIuMy4xaC40YzAtLjIuMS0uMi4yLS4zdi0xYS42LjYgMCAwIDAtLjYtLjQuNi42IDAgMCAwLS42LjR2LjNabS44LTMuNS0yLjggNC41LS40LS4zTDMwIDE0bC40LjJaTTIwIDQxLjZjMCAuMyAwIC42LS4yLjgtLjEuMy0uNC41LS42LjZsLTEgLjJjLS4zIDAtLjYgMC0uOS0uMi0uMi0uMS0uNS0uMy0uNi0uNi0uMi0uMi0uMy0uNS0uMy0uOGExLjUgMS41IDAgMCAxIDEuMS0xLjVsLjctLjFjLjQgMCAuNyAwIDEgLjJsLjYuNS4zLjlabS0uNyAwLS4xLS42YTEgMSAwIDAgMC0uNC0uNGwtLjYtLjEtLjUuMWExIDEgMCAwIDAtLjQuNGwtLjEuNXYuNmwuNS40aDEuMWwuNC0uNC4xLS42Wm0uNi0yLjdjMCAuMyAwIC41LS4yLjctLjEuMy0uMy40LS42LjZsLS45LjFhMiAyIDAgMCAxLS44LS4xYy0uMy0uMi0uNS0uMy0uNi0uNi0uMi0uMi0uMi0uNC0uMi0uNyAwLS4zIDAtLjYuMi0uOS4xLS4yLjMtLjQuNi0uNWwuOC0uMmMuNCAwIC43IDAgMSAuMmwuNS41Yy4yLjMuMi42LjIuOVptLS43IDAtLjEtLjUtLjQtLjRhMSAxIDAgMCAwLS41IDAgMSAxIDAgMCAwLS41IDBsLS4zLjQtLjEuNS4xLjVjMCAuMi4yLjMuNC4zbC40LjJjLjIgMCAuNCAwIC41LS4yLjIgMCAuMy0uMS40LS4zbC4xLS41Wm01LjUuOXYuOWMwIC40IDAgLjgtLjIgMS4xIDAgLjQtLjIuNi0uMy44LS4yLjItLjQuNC0uNi40YTIgMiAwIDAgMS0uNy4ybC0uNi0uMWMtLjIgMC0uNC0uMi0uNS0uM2wtLjQtLjQtLjMtLjh2LTNsLjUtLjdjLjEtLjIuMy0uNC41LS40bC44LS4yLjYuMWExLjQgMS40IDAgMCAxIC44LjdsLjMuN3YxWm0tLjggMVYzOWwtLjEtLjVhMSAxIDAgMCAwLS4zLS4zYzAtLjEtLjEtLjItLjMtLjJhMSAxIDAgMCAwLS4zIDAgMSAxIDAgMCAwLS41IDBsLS4zLjMtLjIuNi0uMS44djEuOGwuMi41LjIuMy4zLjJoLjhjLjIgMCAuMy0uMi40LS4zbC4yLS42di0uOFptMS43LTJ2LS4zYzAtLjIgMC0uNC4yLS42IDAtLjIuMi0uMy40LS40bC42LS4yYy4zIDAgLjUgMCAuNi4yLjIgMCAuMy4yLjQuNGwuMi42di4zYzAgLjIgMCAuNC0uMi42IDAgLjItLjIuMy0uNC40bC0uNi4yYy0uMiAwLS40IDAtLjYtLjJhMSAxIDAgMCAxLS40LS40bC0uMi0uNlptLjYtLjN2LjZsLjMuMy4zLjEuNC0uMS4yLS4zVjM4LjJhLjYuNiAwIDAgMC0uNi0uNC42LjYgMCAwIDAtLjUuNGwtLjEuM1ptMi4yIDMuNXYtLjNjMC0uMiAwLS40LjItLjYgMC0uMi4yLS4zLjQtLjRsLjYtLjJjLjIgMCAuNCAwIC42LjIuMiAwIC4zLjIuNC40bC4xLjZWNDIuNmwtLjUuNC0uNi4yYy0uMyAwLS41IDAtLjYtLjItLjIgMC0uMy0uMi0uNC0uNGwtLjItLjZabS42LS4zdi42YzAgLjIuMS4yLjIuM2wuNC4xaC4zbC4yLS40LjEtLjN2LS42YS42LjYgMCAwIDAtLjYtLjQuNi42IDAgMCAwLS42LjR2LjNabS44LTMuNUwyNyA0Mi43bC0uNC0uMyAyLjgtNC40LjQuMlpNMTkuMiA2MS41aC4xdi42Yy0uNCAwLS44IDAtMSAuMi0uMy4xLS41LjMtLjYuNWEyIDIgMCAwIDAtLjQuN3YyLjNsLjMuNS4zLjNoLjlsLjMtLjMuMi0uNGExLjggMS44IDAgMCAwIDAtMWMwLS4yIDAtLjMtLjItLjRhMSAxIDAgMCAwLS43LS40bC0uNi4xLS40LjRhMSAxIDAgMCAwLS4yLjVoLS40bC4yLS44LjQtLjUuNS0uM2ExLjkgMS45IDAgMCAxIDEuMy4xbC41LjQuMy42YTIuNCAyLjQgMCAwIDEgMCAxLjVsLS4zLjYtLjYuNC0uOC4yLS44LS4yLS41LS41LS40LS43VjYzLjZsLjUtMWMuMi0uNC40LS42LjgtLjhhMyAzIDAgMCAxIDEuMy0uM1ptNS41IDIuNHYuOWMwIC40IDAgLjgtLjIgMS4xIDAgLjQtLjIuNi0uMy44bC0uNi40YTIgMiAwIDAgMS0uNy4ybC0uNi0uMWMtLjIgMC0uNC0uMi0uNS0uM2wtLjQtLjUtLjMtLjd2LTNjLjItLjMuMy0uNi41LS43bC41LS41aDEuNGExLjQgMS40IDAgMCAxIC44LjdsLjMuN3YxWm0tLjggMVY2M2wtLjEtLjVhMSAxIDAgMCAwLS4zLS4zYzAtLjEtLjEtLjItLjMtLjJhMSAxIDAgMCAwLS4zIDAgMSAxIDAgMCAwLS41IDBsLS4zLjMtLjIuNi0uMS44djEuN2wuMi42LjIuMy4zLjJoLjhsLjQtLjMuMi0uNnYtLjhabTEuNy0ydi0uM2MwLS4yIDAtLjQuMi0uNiAwLS4yLjItLjMuNC0uNGwuNi0uMmMuMyAwIC41IDAgLjYuMi4yIDAgLjMuMi40LjRsLjIuNnYuM2MwIC4yIDAgLjQtLjIuNiAwIC4yLS4yLjMtLjQuNGwtLjYuMmMtLjIgMC0uNCAwLS42LS4yYTEgMSAwIDAgMS0uNC0uNGwtLjItLjZabS42LS4zdi42bC4zLjNoLjdsLjItLjN2LTFhLjYuNiAwIDAgMC0uNi0uMy42LjYgMCAwIDAtLjUuM2wtLjEuNFptMi4yIDMuNXYtLjNjMC0uMiAwLS40LjItLjYgMC0uMi4yLS4zLjQtLjRsLjYtLjJjLjIgMCAuNCAwIC42LjIuMiAwIC4zLjIuNC40bC4xLjZWNjYuN2wtLjUuNC0uNi4yYy0uMyAwLS41IDAtLjYtLjJsLS40LS40LS4yLS42Wm0uNi0uM3YuNmwuMi4zLjQuMS4zLS4xLjItLjMuMS0uM3YtLjdhLjYuNiAwIDAgMC0uNi0uMy42LjYgMCAwIDAtLjYuM3YuNFptLjgtMy41TDI3IDY2LjhsLS40LS4zIDIuOC00LjUuNC4zWk0yMC4zIDg5LjR2LjZoLTR2LS41bDIuNS00aC41bC0uNiAxLjItMS43IDIuN2gzLjNabS0uOC0zLjh2NS43aC0uN3YtNS43aC43Wm01LjIgMi40di44YzAgLjUgMCAxLS4yIDEuMiAwIC4zLS4yLjYtLjMuOGwtLjYuNGEyIDIgMCAwIDEtLjcuMmwtLjYtLjFjLS4yIDAtLjQtLjItLjUtLjNsLS40LS41LS4zLS43di0zYy4yLS4zLjMtLjYuNS0uN2wuNS0uNWgxLjRhMS40IDEuNCAwIDAgMSAuOC43bC4zLjd2MVptLS44IDF2LTEuOGwtLjEtLjVhMSAxIDAgMCAwLS4zLS4zYzAtLjEtLjEtLjItLjMtLjJhMSAxIDAgMCAwLS4zLS4xIDEgMSAwIDAgMC0uNS4xbC0uMy4zLS4yLjUtLjEuOXYxLjdsLjIuNS4yLjQuMy4yaC44bC40LS40LjItLjVWODlabTEuNy0ydi0uM2MwLS4yIDAtLjQuMi0uNiAwLS4yLjItLjMuNC0uNGwuNi0uMmMuMyAwIC41IDAgLjYuMi4yIDAgLjMuMi40LjRsLjIuNnYuM2MwIC4yIDAgLjQtLjIuNmwtLjQuNC0uNi4yLS42LS4yYTEgMSAwIDAgMS0uNC0uNGwtLjItLjZabS42LS4zdi42bC4zLjNoLjdsLjItLjN2LTFhLjYuNiAwIDAgMC0uNi0uMy42LjYgMCAwIDAtLjUuM2wtLjEuNFptMi4yIDMuNXYtLjNjMC0uMiAwLS40LjItLjYgMC0uMi4yLS4zLjQtLjRsLjYtLjJjLjIgMCAuNCAwIC42LjIuMiAwIC4zLjIuNC40bC4xLjZWOTAuOGwtLjUuNC0uNi4yYy0uMyAwLS41IDAtLjYtLjJsLS40LS40LS4yLS42Wm0uNi0uM3YuNmwuMi4zaC43bC4yLS4zLjEtLjN2LS43YS42LjYgMCAwIDAtLjYtLjMuNi42IDAgMCAwLS42LjN2LjRabS44LTMuNUwyNyA5MC44bC0uNC0uMiAyLjgtNC41LjQuM1pNMjAuMiAxMTQuOHYuNmgtMy43di0uNWwxLjgtMi4xLjYtLjcuMi0uNS4xLS40LS4xLS41YzAtLjItLjItLjMtLjMtLjRhMSAxIDAgMCAwLS42LS4xbC0uNi4xYTEgMSAwIDAgMC0uNC41bC0uMS42aC0uN2MwLS40IDAtLjcuMi0xbC42LS42YTIgMiAwIDAgMSAxLS4yYy40IDAgLjcgMCAxIC4ybC41LjVjLjIuMy4yLjUuMi44di41bC0uMy41YTcuNiA3LjYgMCAwIDEtLjggMWwtMS41IDEuN2gyLjlabTQuNS0yLjd2LjhjMCAuNSAwIC45LS4yIDEuMiAwIC4zLS4yLjYtLjMuOGwtLjYuNGEyLjIgMi4yIDAgMCAxLTEuMyAwYy0uMiAwLS40IDAtLjUtLjJsLS40LS41LS4zLS43di0zbC41LS44LjUtLjRoMS40YTEuNCAxLjQgMCAwIDEgLjguN2wuMy43djFabS0uOCAxdi0xLjhsLS4xLS41YTEgMSAwIDAgMC0uMy0uNGwtLjMtLjEtLjMtLjFhMSAxIDAgMCAwLS41IDBsLS4zLjQtLjIuNS0uMS45djEuN2wuMi41LjIuNC4zLjJoLjhsLjQtLjQuMi0uNXYtLjhabTEuNy0ydi0uM2MwLS4yIDAtLjQuMi0uNmExIDEgMCAwIDEgMS0uNmMuMyAwIC41IDAgLjYuMi4yIDAgLjMuMi40LjRsLjIuNnYuM2MwIC4yIDAgLjQtLjIuNmwtLjQuNC0uNi4xLS42LS4xYTEgMSAwIDAgMS0uNC0uNGwtLjItLjZabS42LS4zdi42bC4zLjNoLjdsLjItLjN2LTFhLjYuNiAwIDAgMC0uNi0uM2gtLjNsLS4yLjMtLjEuNFptMi4yIDMuNXYtLjNjMC0uMiAwLS40LjItLjZhMSAxIDAgMCAxIDEtLjZjLjIgMCAuNCAwIC42LjIuMiAwIC4zLjIuNC40bC4xLjZWMTE0LjlsLS41LjQtLjYuMi0uNi0uMi0uNC0uNC0uMi0uNlptLjYtLjN2LjZsLjIuM2guN2wuMi0uMy4xLS4zdi0uN2EuNi42IDAgMCAwLS42LS4zaC0uNGwtLjIuM3YuNFptLjgtMy41LTIuOCA0LjQtLjQtLjIgMi44LTQuNS40LjNaTTI1IDEzNi4ydjJsLS40LjgtLjYuNGEyIDIgMCAwIDEtLjguMWgtLjZsLS41LS4zLS4zLS41LS4zLS43di0zbC40LS44LjYtLjQuNy0uMWguN2ExLjQgMS40IDAgMCAxIC44LjhsLjMuN3YxWm0tLjcgMVYxMzUuNGwtLjItLjVhMSAxIDAgMCAwLS4yLS40bC0uMy0uMmExIDEgMCAwIDAtLjQgMCAxIDEgMCAwIDAtLjQgMGwtLjQuNC0uMi41djIuNmwuMi41YzAgLjIgMCAuMy4yLjRsLjMuMmguOGMuMS0uMS4zLS4yLjMtLjRsLjMtLjV2LS44Wm0xLjctMnYtLjNjMC0uMiAwLS40LjItLjYgMC0uMi4yLS4zLjQtLjQuMS0uMi40LS4yLjYtLjIuMiAwIC40IDAgLjYuMi4yIDAgLjMuMi40LjRsLjIuNnYuM2MwIC4yIDAgLjQtLjIuNmwtLjQuNC0uNi4xLS42LS4xYTEgMSAwIDAgMS0uNC0uNGwtLjItLjZabS42LS4zdi42bC4yLjNoLjdsLjMtLjN2LTFhLjYuNiAwIDAgMC0uNi0uMy42LjYgMCAwIDAtLjYuM3YuNFptMi4yIDMuNXYtLjNsLjEtLjZhMSAxIDAgMCAxIDEtLjZjLjMgMCAuNSAwIC43LjIuMiAwIC4zLjIuNC40bC4xLjZ2LjNsLS4xLjYtLjQuNC0uNi4xLS43LS4xLS40LS40LS4xLS42Wm0uNS0uM3YuM2wuMS4zLjIuM2guN2wuMi0uM3YtMWEuNi42IDAgMCAwLS41LS4zLjYuNiAwIDAgMC0uNi4zdi40Wm0uOC0zLjUtMi43IDQuNC0uNC0uMiAyLjctNC41LjQuM1oiLz48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuNSIgc3Ryb2tlLXdpZHRoPSIuNCIgZD0iTTM2IDE2aDE1Mk0zNiA0MC4zaDE1Mk0zNiA2NC41aDE1Mk0zNiA4OC44aDE1Mk0zNiAxMTNoMTUyIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTQwLjggMTQxLjN2NS43aC0uN3YtNC44bC0xLjUuNXYtLjZsMi4xLS44aC4xWm00LjUgMHY1LjdoLS43di00LjhsLTEuNS41di0uNmwyLjEtLjhoLjFabTguNyAwdjUuN2gtLjhsLTIuOC00LjR2NC40aC0uOHYtNS43aC44bDIuOCA0LjR2LTQuNGguOFptMSAzLjZjMC0uNCAwLS42LjItLjkgMC0uMy4yLS41LjMtLjdsLjYtLjRjLjMtLjIuNS0uMi44LS4yLjMgMCAuNiAwIC44LjIuMyAwIC41LjIuNi40bC40LjcuMi44di4xYzAgLjMgMCAuNi0uMi45bC0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zdi0uOVptLjcgMCAuMS42LjIuNS40LjRoMS4xbC4zLS40LjMtLjV2LTEuMmMwLS4yLS4yLS40LS4zLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41di41Wm01LjMgMS40IDEuMi0zLjVoLjdsLTEuNSA0LjJoLS41bC4xLS43Wm0tMS0zLjUgMS4yIDMuNi4xLjZoLS41bC0xLjUtNC4yaC43Wk03MS42IDE0MS4zdjUuN2gtLjh2LTQuOGwtMS40LjV2LS42bDItLjhoLjJabTUuOCA1LjF2LjZoLTMuN3YtLjVsMS45LTIgLjUtLjcuMy0uNXYtMWwtLjQtLjNhMSAxIDAgMCAwLS42LS4yYy0uMiAwLS40IDAtLjYuMmExIDEgMCAwIDAtLjQuNGwtLjEuNmgtLjdjMC0uMyAwLS42LjItLjkuMS0uMy4zLS41LjYtLjZhMiAyIDAgMCAxIDEtLjNsMSAuMmMuMi4yLjQuMy41LjYuMi4yLjMuNC4zLjhsLS4xLjUtLjMuNWE3LjggNy44IDAgMCAxLS44IDFsLTEuNCAxLjZoMi44Wm03LjMtNXY1LjZIODRsLTIuOS00LjR2NC40aC0uN3YtNS43aC43bDIuOSA0LjR2LTQuNGguN1ptMSAzLjUuMi0uOWMwLS4zLjItLjUuNC0uN2wuNi0uNGMuMi0uMi41LS4yLjgtLjIuMyAwIC42IDAgLjguMi4yIDAgLjQuMi42LjRsLjQuNy4xLjh2LjFsLS4xLjktLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM2wtLjEtLjlabS44IDB2LjZsLjMuNWMwIC4yLjIuMy40LjRoMWwuNC0uNC4yLS41di0xLjJsLS4yLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41LS4xLjVabTUuMiAxLjQgMS4yLTMuNWguN2wtMS41IDQuMmgtLjVsLjEtLjdabS0xLTMuNSAxLjMgMy42di42aC0uNGwtMS42LTQuMmguOFpNMTAyLjMgMTQxLjN2NS43aC0uN3YtNC44bC0xLjUuNXYtLjZsMi4xLS44aC4xWm0zLjIgMi41aC41bC42LS4xLjQtLjQuMS0uNXYtLjVsLS40LS40aC0xLjFhLjkuOSAwIDAgMC0uNS45aC0uN2MwLS4zIDAtLjYuMi0uOGwuNi0uNmEyIDIgMCAwIDEgMS0uMmwuOC4yYy4zLjEuNS4zLjYuNi4yLjIuMy41LjMuOGwtLjEuNS0uNC41LS41LjMtLjguMWgtLjZ2LS40Wm0wIC42di0uNGgxLjZsLjUuNC4zLjV2MS4ybC0uNS41LS41LjRoLTEuNWwtLjUtLjNhMS40IDEuNCAwIDAgMS0uNi0xLjJoLjh2LjVsLjUuNGgxLjFsLjQtLjQuMS0uNi0uMS0uNmEuOS45IDAgMCAwLS41LS4zbC0uNi0uMWgtLjVabTEwLTN2NS42aC0uOGwtMi44LTQuNHY0LjRoLS44di01LjdoLjhsMi44IDQuNHYtNC40aC44Wm0xIDMuNWMwLS40IDAtLjYuMi0uOSAwLS4zLjItLjUuMy0uN2wuNi0uNGMuMy0uMi41LS4yLjgtLjIuMyAwIC42IDAgLjguMi4zIDAgLjUuMi42LjRsLjQuNy4yLjh2LjFjMCAuMyAwIC42LS4yLjlsLS40LjdhMS44IDEuOCAwIDAgMS0xLjQuNiAxLjggMS44IDAgMCAxLTEuOC0xLjN2LS45Wm0uNyAwIC4xLjYuMi41LjQuNGgxLjFsLjMtLjQuMy0uNXYtMS4yYzAtLjItLjItLjQtLjMtLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0uNS4xbC0uNC40LS4yLjV2LjVabTUuMyAxLjQgMS4yLTMuNWguN2wtMS41IDQuMmgtLjVsLjEtLjdabS0xLTMuNSAxLjIgMy42LjEuNmgtLjVsLTEuNS00LjJoLjdaTTEzMyAxNDEuM3Y1LjdoLS43di00LjhsLTEuNC41di0uNmwyLS44aC4yWm02IDMuOHYuNmgtNHYtLjRsMi41LTRoLjZsLS43IDEuMi0xLjcgMi42aDMuM1ptLS44LTMuOHY1LjdoLS43di01LjdoLjdabTggMHY1LjdoLS43bC0yLjktNC40djQuNGgtLjd2LTUuN2guN2wyLjkgNC40di00LjRoLjdabTEgMy42LjItLjljMC0uMy4yLS41LjQtLjdsLjYtLjRjLjItLjIuNS0uMi44LS4yLjMgMCAuNiAwIC44LjIuMiAwIC40LjIuNi40bC40LjcuMS44di4xbC0uMS45LS40LjdhMS44IDEuOCAwIDAgMS0xLjQuNiAxLjggMS44IDAgMCAxLTEuOC0xLjNsLS4xLS45Wm0uOCAwdi42bC4zLjVjMCAuMi4yLjMuNC40aDFsLjQtLjQuMi0uNXYtMS4ybC0uMi0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLS41LjFsLS40LjQtLjIuNS0uMS41Wm01LjIgMS40IDEuMi0zLjVoLjdsLTEuNSA0LjJoLS41bC4xLS43Wm0tMS0zLjUgMS4zIDMuNnYuNmgtLjRsLTEuNi00LjJoLjhaTTE2My44IDE0MS4zdjUuN2gtLjd2LTQuOGwtMS41LjV2LS42bDIuMS0uOGguMVptMyAzLS41LS4yLjMtMi44aDIuOXYuN2gtMi4zbC0uMiAxLjUuNC0uMS41LS4xLjguMS41LjQuMy42YTIuNSAyLjUgMCAwIDEgMCAxLjVjMCAuMy0uMi41LS4zLjYtLjEuMi0uMy4zLS42LjRhMiAyIDAgMCAxLS44LjJsLS42LS4xLS42LS4zLS4zLS41YTIgMiAwIDAgMS0uMi0uN2guN2wuMS41LjQuNGgxbC4zLS4zLjItLjRhMS44IDEuOCAwIDAgMCAwLTFjMC0uMiAwLS4zLS4yLS40YTEgMSAwIDAgMC0uMy0uM2gtMS4xbC0uMy4zWm0xMC4yLTN2NS43aC0uOGwtMi44LTQuNHY0LjRoLS44di01LjdoLjhsMi44IDQuNHYtNC40aC44Wm0xIDMuNmMwLS40IDAtLjYuMi0uOSAwLS4zLjItLjUuMy0uN2wuNi0uNGMuMy0uMi41LS4yLjgtLjIuMyAwIC42IDAgLjguMi4zIDAgLjUuMi42LjRsLjQuNy4yLjh2LjFjMCAuMyAwIC42LS4yLjlsLS40LjdhMS44IDEuOCAwIDAgMS0xLjQuNiAxLjggMS44IDAgMCAxLTEuOC0xLjN2LS45Wm0uNyAwIC4xLjYuMi41LjQuNGgxLjFsLjMtLjQuMy0uNXYtMS4yYzAtLjItLjItLjQtLjMtLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0uNS4xbC0uNC40LS4yLjV2LjVabTUuMyAxLjQgMS4yLTMuNWguN2wtMS41IDQuMmgtLjVsLjEtLjdabS0xLTMuNSAxLjIgMy42LjEuNmgtLjVsLTEuNS00LjJoLjdaIi8+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJiIiB4MT0iMTA4LjQiIHgyPSIxMDguNCIgeTE9IjE0OS44IiB5Mj0iLTE1LjUiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9Ii40IiBzdG9wLWNvbG9yPSIjRjQ2MDQ3Ii8+PHN0b3Agb2Zmc2V0PSIuNCIgc3RvcC1jb2xvcj0iI0ZFQkEzRCIvPjxzdG9wIG9mZnNldD0iLjUiIHN0b3AtY29sb3I9IiNGRUJBM0QiLz48c3RvcCBvZmZzZXQ9Ii41IiBzdG9wLWNvbG9yPSIjNkZDQzRDIi8+PHN0b3Agb2Zmc2V0PSIuNyIgc3RvcC1jb2xvcj0iIzZGQ0M0QyIvPjxzdG9wIG9mZnNldD0iLjciIHN0b3AtY29sb3I9IiM1MTZGQzkiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iYyIgeDE9IjEwNy4zIiB4Mj0iMTA3LjMiIHkxPSIxMjEuNiIgeTI9Ii0xNS41IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIuMiIgc3RvcC1jb2xvcj0iI0YwNDAyMiIvPjxzdG9wIG9mZnNldD0iLjIiIHN0b3AtY29sb3I9IiNGRkE2MDAiLz48c3RvcCBvZmZzZXQ9Ii40IiBzdG9wLWNvbG9yPSIjRkZBNjAwIi8+PHN0b3Agb2Zmc2V0PSIuNCIgc3RvcC1jb2xvcj0iIzNGQTcxQSIvPjxzdG9wIG9mZnNldD0iLjYiIHN0b3AtY29sb3I9IiMzRkE3MUEiLz48c3RvcCBvZmZzZXQ9Ii42IiBzdG9wLWNvbG9yPSIjMzA1QUQ3Ii8+PC9saW5lYXJHcmFkaWVudD48Y2xpcFBhdGggaWQ9ImEiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0zNiA3aDE1MnYxMjlIMzZ6Ii8+PC9jbGlwUGF0aD48L2RlZnM+PC9zdmc+", + "description": "Displays changes to efficiency data over time visualized with color ranges.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.rangeChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.rangeChartWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: false,\n defaultDataKeysFunction: function() {\n return [{ name: 'efficiency', label: 'Efficiency', type: 'timeseries' }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-range-chart-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-range-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Efficiency\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 30;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":2592000000,\"interval\":86400000,\"fixedTimewindow\":{\"startTimeMs\":1700577227898,\"endTimeMs\":1700663627898},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"dataZoom\":true,\"rangeColors\":[{\"from\":0,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":60,\"color\":\"#FFA600\"},{\"from\":60,\"to\":80,\"color\":\"#3FA71A\"},{\"from\":80,\"to\":100,\"color\":\"#305AD7\"}],\"outOfRangeColor\":\"#ccc\",\"fillArea\":true,\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm\",\"lastUpdateAgo\":false,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Efficiency\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"%\",\"decimals\":0,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "productivity", + "effectiveness", + "performance", + "capability" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/efficiency_range_chart_with_background.json b/application/src/main/data/json/system/widget_types/efficiency_range_chart_with_background.json new file mode 100644 index 0000000000..25cdbb6608 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/efficiency_range_chart_with_background.json @@ -0,0 +1,32 @@ +{ + "fqn": "efficiency_range_chart_with_background", + "name": "Efficiency range chart with background", + "deprecated": false, + "image": "tb-image:ZWZmaWNpZW5jeV9yYW5nZV9jaGFydF93aXRoX2JhY2tncm91bmQuc3Zn:ZWZmaWNpZW5jeV9yYW5nZV9jaGFydF93aXRoX2JhY2tncm91bmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48L2c+PGcgY2xpcC1wYXRoPSJ1cmwoI2MpIj48cGF0aCBmaWxsPSJ1cmwoI2QpIiBkPSJtNjYuNSA0OS4yLTIyLjcgNTIuNi03LjggMTYuMVYxMzZoMTUydi0yMy44bC0uNC0yNC43LTEwLjggMzcuNS02LjMtMjMuNC0yLjcgNi45LTExLjcgMjUuOGMtNy43LTI4LTE0LjItMzguNi0yMi42LTY2LjJsLTguNyA0MS4zaC0xMS4ybC0yLjEtNjdMMTA3IDE2IDg2LjQgODEuOGwtNS0yLTE1LTMwLjZaIi8+PHBhdGggc3Ryb2tlPSJ1cmwoI2UpIiBzdHJva2Utd2lkdGg9Ii45IiBkPSJtMzYgMTE4IDcuOS0xNi4yIDIyLjctNTIuNiAxNS4xIDMwLjYgNSAyTDEwNy41IDE2bDQgMjZjLjggMjYuMiAxLjcgNDEuMyAyLjYgNjcuNEgxMjVsOC40LTQyIDIyLjUgNjYuNiAxNC44LTMyLjggNiAyMy40IDExLjMtMzciLz48L2c+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTE0LjggMTMuM1YxOWgtLjd2LTQuOGwtMS40LjV2LS42bDItLjhoLjFabTUuOSAyLjR2LjljMCAuNCAwIC44LS4yIDEuMSAwIC40LS4yLjYtLjMuOC0uMi4yLS40LjQtLjYuNGEyIDIgMCAwIDEtLjcuMmwtLjYtLjFjLS4yIDAtLjQtLjEtLjUtLjMtLjIgMC0uMy0uMi0uNC0uNGwtLjMtLjh2LTNsLjUtLjdjLjEtLjIuMy0uNC41LS40bC44LS4yLjYuMWExLjQgMS40IDAgMCAxIC44LjdsLjMuN3YxWm0tLjggMXYtMS44bC0uMS0uNWExIDEgMCAwIDAtLjMtLjNjMC0uMS0uMS0uMi0uMy0uMmExIDEgMCAwIDAtLjMgMCAxIDEgMCAwIDAtLjUgMGwtLjMuMy0uMi42LS4xLjh2MS44bC4yLjUuMi4zLjMuMmguOGMuMiAwIC4zLS4yLjQtLjNsLjItLjZ2LS44Wm01LjQtMXYuOWwtLjEgMS4xLS40LjhjLS4yLjItLjQuNC0uNi40YTIgMiAwIDAgMS0uNy4ybC0uNi0uMWMtLjIgMC0uNC0uMS0uNS0uMy0uMiAwLS4zLS4yLS40LS40bC0uMi0uOC0uMS0xdi0uOGwuMS0xLjIuNC0uN2MuMS0uMi4zLS40LjUtLjRsLjgtLjIuNi4xYTEuNCAxLjQgMCAwIDEgLjkuN2wuMi43djFabS0uNyAxdi0xLjhsLS4yLS41YTEgMSAwIDAgMC0uMi0uM2wtLjMtLjJhMSAxIDAgMCAwLS40IDAgMSAxIDAgMCAwLS41IDBsLS4zLjMtLjIuNnYzLjFsLjMuMy4zLjJoLjlsLjMtLjMuMi0uNnYtLjhabTEuNy0ydi0uM2wuMS0uNi40LS40LjYtLjJjLjMgMCAuNSAwIC43LjJsLjQuNC4xLjZ2LjNsLS4xLjYtLjQuNC0uNy4yYy0uMiAwLS40IDAtLjYtLjJhMSAxIDAgMCAxLS40LS40bC0uMS0uNlptLjUtLjN2LjZsLjMuMy4zLjFoLjRsLjItLjRWMTQuMWEuNi42IDAgMCAwLS42LS40LjYuNiAwIDAgMC0uNS40di4zWk0yOSAxOHYtLjNjMC0uMiAwLS40LjItLjYgMC0uMi4yLS4zLjQtLjRsLjYtLjJjLjIgMCAuNSAwIC42LjJsLjQuNC4yLjZ2LjNjMCAuMiAwIC40LS4yLjYgMCAuMi0uMi4zLS40LjRsLS42LjJjLS4yIDAtLjQgMC0uNi0uMi0uMiAwLS4zLS4yLS40LS40bC0uMi0uNlptLjYtLjN2LjdsLjMuMi4zLjFoLjRjMC0uMi4xLS4yLjItLjN2LTFhLjYuNiAwIDAgMC0uNi0uNC42LjYgMCAwIDAtLjYuNHYuM1ptLjgtMy41LTIuOCA0LjUtLjQtLjNMMzAgMTRsLjQuMlpNMjAgNDEuNmMwIC4zIDAgLjYtLjIuOC0uMS4zLS40LjUtLjYuNmwtMSAuMmMtLjMgMC0uNiAwLS45LS4yLS4yLS4xLS41LS4zLS42LS42LS4yLS4yLS4zLS41LS4zLS44YTEuNSAxLjUgMCAwIDEgMS4xLTEuNWwuNy0uMWMuNCAwIC43IDAgMSAuMmwuNi41LjMuOVptLS43IDAtLjEtLjZhMSAxIDAgMCAwLS40LS40bC0uNi0uMS0uNS4xYTEgMSAwIDAgMC0uNC40bC0uMS41di42bC41LjRoMS4xbC40LS40LjEtLjZabS42LTIuN2MwIC4zIDAgLjUtLjIuNy0uMS4zLS4zLjQtLjYuNmwtLjkuMWEyIDIgMCAwIDEtLjgtLjFjLS4zLS4yLS41LS4zLS42LS42LS4yLS4yLS4yLS40LS4yLS43IDAtLjMgMC0uNi4yLS45LjEtLjIuMy0uNC42LS41bC44LS4yYy40IDAgLjcgMCAxIC4ybC41LjVjLjIuMy4yLjYuMi45Wm0tLjcgMC0uMS0uNS0uNC0uNGExIDEgMCAwIDAtLjUgMCAxIDEgMCAwIDAtLjUgMGwtLjMuNC0uMS41LjEuNWMwIC4yLjIuMy40LjNsLjQuMmMuMiAwIC40IDAgLjUtLjIuMiAwIC4zLS4xLjQtLjNsLjEtLjVabTUuNS45di45YzAgLjQgMCAuOC0uMiAxLjEgMCAuNC0uMi42LS4zLjgtLjIuMi0uNC40LS42LjRhMiAyIDAgMCAxLS43LjJsLS42LS4xYy0uMiAwLS40LS4yLS41LS4zbC0uNC0uNC0uMy0uOHYtM2wuNS0uN2MuMS0uMi4zLS40LjUtLjRsLjgtLjIuNi4xYTEuNCAxLjQgMCAwIDEgLjguN2wuMy43djFabS0uOCAxVjM5bC0uMS0uNWExIDEgMCAwIDAtLjMtLjNjMC0uMS0uMS0uMi0uMy0uMmExIDEgMCAwIDAtLjMgMCAxIDEgMCAwIDAtLjUgMGwtLjMuMy0uMi42LS4xLjh2MS44bC4yLjUuMi4zLjMuMmguOGMuMiAwIC4zLS4yLjQtLjNsLjItLjZ2LS44Wm0xLjctMnYtLjNjMC0uMiAwLS40LjItLjYgMC0uMi4yLS4zLjQtLjRsLjYtLjJjLjMgMCAuNSAwIC42LjIuMiAwIC4zLjIuNC40bC4yLjZ2LjNjMCAuMiAwIC40LS4yLjYgMCAuMi0uMi4zLS40LjRsLS42LjJjLS4yIDAtLjQgMC0uNi0uMmExIDEgMCAwIDEtLjQtLjRsLS4yLS42Wm0uNi0uM3YuNmwuMy4zLjMuMS40LS4xLjItLjNWMzguMmEuNi42IDAgMCAwLS42LS40LjYuNiAwIDAgMC0uNS40bC0uMS4zWm0yLjIgMy41di0uM2MwLS4yIDAtLjQuMi0uNiAwLS4yLjItLjMuNC0uNGwuNi0uMmMuMiAwIC40IDAgLjYuMi4yIDAgLjMuMi40LjRsLjEuNlY0Mi42bC0uNS40LS42LjJjLS4zIDAtLjUgMC0uNi0uMi0uMiAwLS4zLS4yLS40LS40bC0uMi0uNlptLjYtLjN2LjZjMCAuMi4xLjIuMi4zbC40LjFoLjNsLjItLjQuMS0uM3YtLjZhLjYuNiAwIDAgMC0uNi0uNC42LjYgMCAwIDAtLjYuNHYuM1ptLjgtMy41TDI3IDQyLjdsLS40LS4zIDIuOC00LjQuNC4yWk0xOS4yIDYxLjVoLjF2LjZjLS40IDAtLjggMC0xIC4yLS4zLjEtLjUuMy0uNi41YTIgMiAwIDAgMC0uNC43djIuM2wuMy41LjMuM2guOWwuMy0uMy4yLS40YTEuOCAxLjggMCAwIDAgMC0xYzAtLjIgMC0uMy0uMi0uNGExIDEgMCAwIDAtLjctLjRsLS42LjEtLjQuNGExIDEgMCAwIDAtLjIuNWgtLjRsLjItLjguNC0uNS41LS4zYTEuOSAxLjkgMCAwIDEgMS4zLjFsLjUuNC4zLjZhMi40IDIuNCAwIDAgMSAwIDEuNWwtLjMuNi0uNi40LS44LjItLjgtLjItLjUtLjUtLjQtLjdWNjMuNmwuNS0xYy4yLS40LjQtLjYuOC0uOGEzIDMgMCAwIDEgMS4zLS4zWm01LjUgMi40di45YzAgLjQgMCAuOC0uMiAxLjEgMCAuNC0uMi42LS4zLjhsLS42LjRhMiAyIDAgMCAxLS43LjJsLS42LS4xYy0uMiAwLS40LS4yLS41LS4zbC0uNC0uNS0uMy0uN3YtM2MuMi0uMy4zLS42LjUtLjdsLjUtLjVoMS40YTEuNCAxLjQgMCAwIDEgLjguN2wuMy43djFabS0uOCAxVjYzbC0uMS0uNWExIDEgMCAwIDAtLjMtLjNjMC0uMS0uMS0uMi0uMy0uMmExIDEgMCAwIDAtLjMgMCAxIDEgMCAwIDAtLjUgMGwtLjMuMy0uMi42LS4xLjh2MS43bC4yLjYuMi4zLjMuMmguOGwuNC0uMy4yLS42di0uOFptMS43LTJ2LS4zYzAtLjIgMC0uNC4yLS42IDAtLjIuMi0uMy40LS40bC42LS4yYy4zIDAgLjUgMCAuNi4yLjIgMCAuMy4yLjQuNGwuMi42di4zYzAgLjIgMCAuNC0uMi42IDAgLjItLjIuMy0uNC40bC0uNi4yYy0uMiAwLS40IDAtLjYtLjJhMSAxIDAgMCAxLS40LS40bC0uMi0uNlptLjYtLjN2LjZsLjMuM2guN2wuMi0uM3YtMWEuNi42IDAgMCAwLS42LS4zLjYuNiAwIDAgMC0uNS4zbC0uMS40Wm0yLjIgMy41di0uM2MwLS4yIDAtLjQuMi0uNiAwLS4yLjItLjMuNC0uNGwuNi0uMmMuMiAwIC40IDAgLjYuMi4yIDAgLjMuMi40LjRsLjEuNlY2Ni43bC0uNS40LS42LjJjLS4zIDAtLjUgMC0uNi0uMmwtLjQtLjQtLjItLjZabS42LS4zdi42bC4yLjMuNC4xLjMtLjEuMi0uMy4xLS4zdi0uN2EuNi42IDAgMCAwLS42LS4zLjYuNiAwIDAgMC0uNi4zdi40Wm0uOC0zLjVMMjcgNjYuOGwtLjQtLjMgMi44LTQuNS40LjNaTTIwLjMgODkuNHYuNmgtNHYtLjVsMi41LTRoLjVsLS42IDEuMi0xLjcgMi43aDMuM1ptLS44LTMuOHY1LjdoLS43di01LjdoLjdabTUuMiAyLjR2LjhjMCAuNSAwIDEtLjIgMS4yIDAgLjMtLjIuNi0uMy44bC0uNi40YTIgMiAwIDAgMS0uNy4ybC0uNi0uMWMtLjIgMC0uNC0uMi0uNS0uM2wtLjQtLjUtLjMtLjd2LTNjLjItLjMuMy0uNi41LS43bC41LS41aDEuNGExLjQgMS40IDAgMCAxIC44LjdsLjMuN3YxWm0tLjggMXYtMS44bC0uMS0uNWExIDEgMCAwIDAtLjMtLjNjMC0uMS0uMS0uMi0uMy0uMmExIDEgMCAwIDAtLjMtLjEgMSAxIDAgMCAwLS41LjFsLS4zLjMtLjIuNS0uMS45djEuN2wuMi41LjIuNC4zLjJoLjhsLjQtLjQuMi0uNVY4OVptMS43LTJ2LS4zYzAtLjIgMC0uNC4yLS42IDAtLjIuMi0uMy40LS40bC42LS4yYy4zIDAgLjUgMCAuNi4yLjIgMCAuMy4yLjQuNGwuMi42di4zYzAgLjIgMCAuNC0uMi42bC0uNC40LS42LjItLjYtLjJhMSAxIDAgMCAxLS40LS40bC0uMi0uNlptLjYtLjN2LjZsLjMuM2guN2wuMi0uM3YtMWEuNi42IDAgMCAwLS42LS4zLjYuNiAwIDAgMC0uNS4zbC0uMS40Wm0yLjIgMy41di0uM2MwLS4yIDAtLjQuMi0uNiAwLS4yLjItLjMuNC0uNGwuNi0uMmMuMiAwIC40IDAgLjYuMi4yIDAgLjMuMi40LjRsLjEuNlY5MC44bC0uNS40LS42LjJjLS4zIDAtLjUgMC0uNi0uMmwtLjQtLjQtLjItLjZabS42LS4zdi42bC4yLjNoLjdsLjItLjMuMS0uM3YtLjdhLjYuNiAwIDAgMC0uNi0uMy42LjYgMCAwIDAtLjYuM3YuNFptLjgtMy41TDI3IDkwLjhsLS40LS4yIDIuOC00LjUuNC4zWk0yMC4yIDExNC44di42aC0zLjd2LS41bDEuOC0yLjEuNi0uNy4yLS41LjEtLjQtLjEtLjVjMC0uMi0uMi0uMy0uMy0uNGExIDEgMCAwIDAtLjYtLjFsLS42LjFhMSAxIDAgMCAwLS40LjVsLS4xLjZoLS43YzAtLjQgMC0uNy4yLTFsLjYtLjZhMiAyIDAgMCAxIDEtLjJjLjQgMCAuNyAwIDEgLjJsLjUuNWMuMi4zLjIuNS4yLjh2LjVsLS4zLjVhNy42IDcuNiAwIDAgMS0uOCAxbC0xLjUgMS43aDIuOVptNC41LTIuN3YuOGMwIC41IDAgLjktLjIgMS4yIDAgLjMtLjIuNi0uMy44bC0uNi40YTIuMiAyLjIgMCAwIDEtMS4zIDBjLS4yIDAtLjQgMC0uNS0uMmwtLjQtLjUtLjMtLjd2LTNsLjUtLjguNS0uNGgxLjRhMS40IDEuNCAwIDAgMSAuOC43bC4zLjd2MVptLS44IDF2LTEuOGwtLjEtLjVhMSAxIDAgMCAwLS4zLS40bC0uMy0uMS0uMy0uMWExIDEgMCAwIDAtLjUgMGwtLjMuNC0uMi41LS4xLjl2MS43bC4yLjUuMi40LjMuMmguOGwuNC0uNC4yLS41di0uOFptMS43LTJ2LS4zYzAtLjIgMC0uNC4yLS42YTEgMSAwIDAgMSAxLS42Yy4zIDAgLjUgMCAuNi4yLjIgMCAuMy4yLjQuNGwuMi42di4zYzAgLjIgMCAuNC0uMi42bC0uNC40LS42LjEtLjYtLjFhMSAxIDAgMCAxLS40LS40bC0uMi0uNlptLjYtLjN2LjZsLjMuM2guN2wuMi0uM3YtMWEuNi42IDAgMCAwLS42LS4zaC0uM2wtLjIuMy0uMS40Wm0yLjIgMy41di0uM2MwLS4yIDAtLjQuMi0uNmExIDEgMCAwIDEgMS0uNmMuMiAwIC40IDAgLjYuMi4yIDAgLjMuMi40LjRsLjEuNlYxMTQuOWwtLjUuNC0uNi4yLS42LS4yLS40LS40LS4yLS42Wm0uNi0uM3YuNmwuMi4zaC43bC4yLS4zLjEtLjN2LS43YS42LjYgMCAwIDAtLjYtLjNoLS40bC0uMi4zdi40Wm0uOC0zLjUtMi44IDQuNC0uNC0uMiAyLjgtNC41LjQuM1pNMjUgMTM2LjJ2MmwtLjQuOC0uNi40YTIgMiAwIDAgMS0uOC4xaC0uNmwtLjUtLjMtLjMtLjUtLjMtLjd2LTNsLjQtLjguNi0uNC43LS4xaC43YTEuNCAxLjQgMCAwIDEgLjguOGwuMy43djFabS0uNyAxVjEzNS40bC0uMi0uNWExIDEgMCAwIDAtLjItLjRsLS4zLS4yYTEgMSAwIDAgMC0uNCAwIDEgMSAwIDAgMC0uNCAwbC0uNC40LS4yLjV2Mi42bC4yLjVjMCAuMiAwIC4zLjIuNGwuMy4yaC44Yy4xLS4xLjMtLjIuMy0uNGwuMy0uNXYtLjhabTEuNy0ydi0uM2MwLS4yIDAtLjQuMi0uNiAwLS4yLjItLjMuNC0uNC4xLS4yLjQtLjIuNi0uMi4yIDAgLjQgMCAuNi4yLjIgMCAuMy4yLjQuNGwuMi42di4zYzAgLjIgMCAuNC0uMi42bC0uNC40LS42LjEtLjYtLjFhMSAxIDAgMCAxLS40LS40bC0uMi0uNlptLjYtLjN2LjZsLjIuM2guN2wuMy0uM3YtMWEuNi42IDAgMCAwLS42LS4zLjYuNiAwIDAgMC0uNi4zdi40Wm0yLjIgMy41di0uM2wuMS0uNmExIDEgMCAwIDEgMS0uNmMuMyAwIC41IDAgLjcuMi4yIDAgLjMuMi40LjRsLjEuNnYuM2wtLjEuNi0uNC40LS42LjEtLjctLjEtLjQtLjQtLjEtLjZabS41LS4zdi4zbC4xLjMuMi4zaC43bC4yLS4zdi0xYS42LjYgMCAwIDAtLjUtLjMuNi42IDAgMCAwLS42LjN2LjRabS44LTMuNS0yLjcgNC40LS40LS4yIDIuNy00LjUuNC4zWiIvPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii41IiBzdHJva2Utd2lkdGg9Ii40IiBkPSJNMzYgMTZoMTUyTTM2IDQwLjNoMTUyTTM2IDY0LjVoMTUyTTM2IDg4LjhoMTUyTTM2IDExM2gxNTIiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii41IiBkPSJNNDAuOCAxNDEuM3Y1LjdoLS43di00LjhsLTEuNS41di0uNmwyLjEtLjhoLjFabTQuNSAwdjUuN2gtLjd2LTQuOGwtMS41LjV2LS42bDIuMS0uOGguMVptOC43IDB2NS43aC0uOGwtMi44LTQuNHY0LjRoLS44di01LjdoLjhsMi44IDQuNHYtNC40aC44Wm0xIDMuNmMwLS40IDAtLjYuMi0uOSAwLS4zLjItLjUuMy0uN2wuNi0uNGMuMy0uMi41LS4yLjgtLjIuMyAwIC42IDAgLjguMi4zIDAgLjUuMi42LjRsLjQuNy4yLjh2LjFjMCAuMyAwIC42LS4yLjlsLS40LjdhMS44IDEuOCAwIDAgMS0xLjQuNiAxLjggMS44IDAgMCAxLTEuOC0xLjN2LS45Wm0uNyAwIC4xLjYuMi41LjQuNGgxLjFsLjMtLjQuMy0uNXYtMS4yYzAtLjItLjItLjQtLjMtLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0uNS4xbC0uNC40LS4yLjV2LjVabTUuMyAxLjQgMS4yLTMuNWguN2wtMS41IDQuMmgtLjVsLjEtLjdabS0xLTMuNSAxLjIgMy42LjEuNmgtLjVsLTEuNS00LjJoLjdaTTcxLjYgMTQxLjN2NS43aC0uOHYtNC44bC0xLjQuNXYtLjZsMi0uOGguMlptNS44IDUuMXYuNmgtMy43di0uNWwxLjktMiAuNS0uNy4zLS41di0xbC0uNC0uM2ExIDEgMCAwIDAtLjYtLjJjLS4yIDAtLjQgMC0uNi4yYTEgMSAwIDAgMC0uNC40bC0uMS42aC0uN2MwLS4zIDAtLjYuMi0uOS4xLS4zLjMtLjUuNi0uNmEyIDIgMCAwIDEgMS0uM2wxIC4yYy4yLjIuNC4zLjUuNi4yLjIuMy40LjMuOGwtLjEuNS0uMy41YTcuOCA3LjggMCAwIDEtLjggMWwtMS40IDEuNmgyLjhabTcuMy01djUuNkg4NGwtMi45LTQuNHY0LjRoLS43di01LjdoLjdsMi45IDQuNHYtNC40aC43Wm0xIDMuNS4yLS45YzAtLjMuMi0uNS40LS43bC42LS40Yy4yLS4yLjUtLjIuOC0uMi4zIDAgLjYgMCAuOC4yLjIgMCAuNC4yLjYuNGwuNC43LjEuOHYuMWwtLjEuOS0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zbC0uMS0uOVptLjggMHYuNmwuMy41YzAgLjIuMi4zLjQuNGgxbC40LS40LjItLjV2LTEuMmwtLjItLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0uNS4xbC0uNC40LS4yLjUtLjEuNVptNS4yIDEuNCAxLjItMy41aC43bC0xLjUgNC4yaC0uNWwuMS0uN1ptLTEtMy41IDEuMyAzLjZ2LjZoLS40bC0xLjYtNC4yaC44Wk0xMDIuMyAxNDEuM3Y1LjdoLS43di00LjhsLTEuNS41di0uNmwyLjEtLjhoLjFabTMuMiAyLjVoLjVsLjYtLjEuNC0uNC4xLS41di0uNWwtLjQtLjRoLTEuMWEuOS45IDAgMCAwLS41LjloLS43YzAtLjMgMC0uNi4yLS44bC42LS42YTIgMiAwIDAgMSAxLS4ybC44LjJjLjMuMS41LjMuNi42LjIuMi4zLjUuMy44bC0uMS41LS40LjUtLjUuMy0uOC4xaC0uNnYtLjRabTAgLjZ2LS40aDEuNmwuNS40LjMuNXYxLjJsLS41LjUtLjUuNGgtMS41bC0uNS0uM2ExLjQgMS40IDAgMCAxLS42LTEuMmguOHYuNWwuNS40aDEuMWwuNC0uNC4xLS42LS4xLS42YS45LjkgMCAwIDAtLjUtLjNsLS42LS4xaC0uNVptMTAtM3Y1LjZoLS44bC0yLjgtNC40djQuNGgtLjh2LTUuN2guOGwyLjggNC40di00LjRoLjhabTEgMy41YzAtLjQgMC0uNi4yLS45IDAtLjMuMi0uNS4zLS43bC42LS40Yy4zLS4yLjUtLjIuOC0uMi4zIDAgLjYgMCAuOC4yLjMgMCAuNS4yLjYuNGwuNC43LjIuOHYuMWMwIC4zIDAgLjYtLjIuOWwtLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM3YtLjlabS43IDAgLjEuNi4yLjUuNC40aDEuMWwuMy0uNC4zLS41di0xLjJjMC0uMi0uMi0uNC0uMy0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLS41LjFsLS40LjQtLjIuNXYuNVptNS4zIDEuNCAxLjItMy41aC43bC0xLjUgNC4yaC0uNWwuMS0uN1ptLTEtMy41IDEuMiAzLjYuMS42aC0uNWwtMS41LTQuMmguN1pNMTMzIDE0MS4zdjUuN2gtLjd2LTQuOGwtMS40LjV2LS42bDItLjhoLjJabTYgMy44di42aC00di0uNGwyLjUtNGguNmwtLjcgMS4yLTEuNyAyLjZoMy4zWm0tLjgtMy44djUuN2gtLjd2LTUuN2guN1ptOCAwdjUuN2gtLjdsLTIuOS00LjR2NC40aC0uN3YtNS43aC43bDIuOSA0LjR2LTQuNGguN1ptMSAzLjYuMi0uOWMwLS4zLjItLjUuNC0uN2wuNi0uNGMuMi0uMi41LS4yLjgtLjIuMyAwIC42IDAgLjguMi4yIDAgLjQuMi42LjRsLjQuNy4xLjh2LjFsLS4xLjktLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM2wtLjEtLjlabS44IDB2LjZsLjMuNWMwIC4yLjIuMy40LjRoMWwuNC0uNC4yLS41di0xLjJsLS4yLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41LS4xLjVabTUuMiAxLjQgMS4yLTMuNWguN2wtMS41IDQuMmgtLjVsLjEtLjdabS0xLTMuNSAxLjMgMy42di42aC0uNGwtMS42LTQuMmguOFpNMTYzLjggMTQxLjN2NS43aC0uN3YtNC44bC0xLjUuNXYtLjZsMi4xLS44aC4xWm0zIDMtLjUtLjIuMy0yLjhoMi45di43aC0yLjNsLS4yIDEuNS40LS4xLjUtLjEuOC4xLjUuNC4zLjZhMi41IDIuNSAwIDAgMSAwIDEuNWMwIC4zLS4yLjUtLjMuNi0uMS4yLS4zLjMtLjYuNGEyIDIgMCAwIDEtLjguMmwtLjYtLjEtLjYtLjMtLjMtLjVhMiAyIDAgMCAxLS4yLS43aC43bC4xLjUuNC40aDFsLjMtLjMuMi0uNGExLjggMS44IDAgMCAwIDAtMWMwLS4yIDAtLjMtLjItLjRhMSAxIDAgMCAwLS4zLS4zaC0xLjFsLS4zLjNabTEwLjItM3Y1LjdoLS44bC0yLjgtNC40djQuNGgtLjh2LTUuN2guOGwyLjggNC40di00LjRoLjhabTEgMy42YzAtLjQgMC0uNi4yLS45IDAtLjMuMi0uNS4zLS43bC42LS40Yy4zLS4yLjUtLjIuOC0uMi4zIDAgLjYgMCAuOC4yLjMgMCAuNS4yLjYuNGwuNC43LjIuOHYuMWMwIC4zIDAgLjYtLjIuOWwtLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM3YtLjlabS43IDAgLjEuNi4yLjUuNC40aDEuMWwuMy0uNC4zLS41di0xLjJjMC0uMi0uMi0uNC0uMy0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLS41LjFsLS40LjQtLjIuNXYuNVptNS4zIDEuNCAxLjItMy41aC43bC0xLjUgNC4yaC0uNWwuMS0uN1ptLTEtMy41IDEuMiAzLjYuMS42aC0uNWwtMS41LTQuMmguN1oiLz48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImQiIHgxPSIxMDguNCIgeDI9IjEwOC40IiB5MT0iMTQ5LjgiIHkyPSItMTUuNSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iLjQiIHN0b3AtY29sb3I9IiNGNDYwNDciLz48c3RvcCBvZmZzZXQ9Ii40IiBzdG9wLWNvbG9yPSIjRkVCQTNEIi8+PHN0b3Agb2Zmc2V0PSIuNSIgc3RvcC1jb2xvcj0iI0ZFQkEzRCIvPjxzdG9wIG9mZnNldD0iLjUiIHN0b3AtY29sb3I9IiM2RkNDNEMiLz48c3RvcCBvZmZzZXQ9Ii43IiBzdG9wLWNvbG9yPSIjNkZDQzRDIi8+PHN0b3Agb2Zmc2V0PSIuNyIgc3RvcC1jb2xvcj0iIzUxNkZDOSIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJlIiB4MT0iMTA3LjMiIHgyPSIxMDcuMyIgeTE9IjEyMS42IiB5Mj0iLTE1LjUiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9Ii4yIiBzdG9wLWNvbG9yPSIjRjA0MDIyIi8+PHN0b3Agb2Zmc2V0PSIuMiIgc3RvcC1jb2xvcj0iI0ZGQTYwMCIvPjxzdG9wIG9mZnNldD0iLjQiIHN0b3AtY29sb3I9IiNGRkE2MDAiLz48c3RvcCBvZmZzZXQ9Ii40IiBzdG9wLWNvbG9yPSIjM0ZBNzFBIi8+PHN0b3Agb2Zmc2V0PSIuNiIgc3RvcC1jb2xvcj0iIzNGQTcxQSIvPjxzdG9wIG9mZnNldD0iLjYiIHN0b3AtY29sb3I9IiMzMDVBRDciLz48L2xpbmVhckdyYWRpZW50PjxjbGlwUGF0aCBpZD0iYyI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTM2IDdoMTUydjEyOUgzNnoiLz48L2NsaXBQYXRoPjxwYXR0ZXJuIGlkPSJhIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBwYXR0ZXJuQ29udGVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCI+PHVzZSB4bGluazpocmVmPSIjZiIgdHJhbnNmb3JtPSJtYXRyaXgoLjAwNTAzMTQ1IDAgMCAuMDA2Mjg5MzEgMCAwKSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgeD0iMCIgeT0iMCIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMyIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMDg4XzUzNjkzIi8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMDg4XzUzNjkzIiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PGltYWdlIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3QkRBQUlCQVFFQkFRSUJBUUVDQWdJQ0FnUURBZ0lDQWdVRUJBTUVCZ1VHQmdZRkJnWUdCd2tJQmdjSkJ3WUdDQXNJQ1FvS0Nnb0tCZ2dMREFzS0RBa0tDZ3IvMndCREFRSUNBZ0lDQWdVREF3VUtCd1lIQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnci93Z0FSQ0FDZkFNY0RBUkVBQWhFQkF4RUIvOFFBSFFBQUFnSURBUUVCQUFBQUFBQUFBQUFBQmdjRkNBTUVDUUlBQWYvRUFCc0JBQUlEQVFFQkFBQUFBQUFBQUFBQUFBTUVBUUlGQUFZSC85b0FEQU1CQUFJUUF4QUFBQUIzWTdOZHhhTldyc09EYTh3ejB6dCtzV05zT0tyYkdPNU1RSHFoaWtxZVR1L1pKNGlkcWVMNmszTzRMU1BVeWpjYmJ0V2UxNXFzNWxEbEhZU0IyZTBjMW9IRnlhcTAwMG1ISlhyR1NHSXFYd0taZTFadnVKU3I1Wmo2Tytuc3ZXSnFrMlJFRVZpMWRxeEJ6SHVlL1pyWEVrMW5OM1dUazRWbFk3WUZ6WXF3MGxUdU9LMktrZXBTMGFNbjVXMHhTNUNZZm1KdzB2NG1zMlFScmFKR0pGbFNWc0E5QTFKNUlPbWhySXRvRWhFOVFWUm1aSUt0Zko1cEJiWXdDVFFpUGNOOW1PbnloeGlMc2dabExqK29UVUdhTEVZdllVYnVubHpYY0tLbHFmbGVoZ0FQNUlKOTNlckI5V3FjNi9tV0FRTWZvcWM5cU1Ib3JHZExXQ2l1VG93MW1wakFoV0ozK0pGeDA4czFQTFBUN09mWkFpOWx5Q0ZseTFld2ZZUUMranFDYjI2U3VnUFJsRHZQMm55UXhtbWQ5T2pYSE1hek5CSllpdGMvZHVXcFhRa1VlMU0wc0ViWmdoUXV3YUJNN1NxdnRjdGdiMEdsVFZxODE3dlRFOW42MzdYb2RWdVBpKzM3SDVneXlnMFhWS1k4WXppUzRGMzZDMGQxaVZsWGx2cW9TQVd2dXM2aFcyUkdoWUlYUlF1cEYvTENIVlMxdDg1N3VQRTVzZ3RuamgwRCtwVzR0N0w1a1dPWjV1eXZUMlRsRlpPVkRQRUJNVThiUG84cnRCVW1BeEJFNDRDU2VFVGFpelpnWHc3WHZrY2NPeWxVYldQRjhLNk9SVjZOVmU5NUhvSUwxbnowOWJ6NHZRUXE2UWhURTdTakRXUWVIODMwcmY4QVRlRjVDNzJMcnh5L3RXdzJkb3FJSnB4MU45TE1sb3pXemdVc3FaZUtPbFBSOU02cXI0TGgrazUwaTlUMGc5RDhzWWp5SDJpaFZPRGtmZHZKTk9QTzBpUGh3Kzc1K2h6ZEU4MGpYOXRVMUVURmFONkpmeUdoYUhOMHJuTm9teTkwbm42V25hZENrNm5YaFBHL1E2OEIxck8vUnZpNC9FeWpDOWFKTVNWNXVsWHRPUUtVeTlGTm1HbEdSVmExTXBYbUFYQ05PVnREMm95VjJiYzVXeGY1dElpRHd5QStLMVJ1aFlaWTBRTXZPdWo5K21zbVFubGd3SktrNlc2SHh3N1NYRWk3MXFKYU15N2xmZEROUTdpUlNNc3ZTNHpjYlhVY3NqbjZyT1lTc2NHeG1LTWQ3REZDZ3k1K2ZUUGM3OUZMcmNpWE5YaGk4YnMzMnJRMzZEc2VPd0pGcTJaK25vYW1iVXJWeDFleXNXQlBPMXVGRkMwMVhMSTV1cGQ1aEZrQXREanRyWHVKaExXcnI4OUNNTVpac0QwOE1TSUZZdEI2SVoraEpSd2NLYlFLTW1UQzR0Z2VtUzNwUEwxZDFNaFVzTEZ3anlNY04zb3lsWExKWitwZnc2SlN0Y2JHZlN0QUl1ZEMzbmxucEwzdHptdVptL2p6aUdpUE1nNlNLM2xheUZNb1c5UWVVb1dZN0Y5R3NQVmVOMWlDNkZMTVhFZFVBeFh4OVBPeFJ4RE1BNjJ3UGVYdlQxRFVFeVVWTFFhb2J2bjFPcm9kVU1iUXI3cUlSNnhsWWFqL0FJTEswc0pNSTJWek5KUndTU1hhcmo2UHpJMWFuYUhPMHZ1bm0yUVpEZWliSlFtN3V2cVpQSUMwd1UwUXk5VnVjVlRmUWVkV3FtaDFFeU5HbG01alNPZTZlVnVVRXR0ZElsS3oremRKTXNpMG1WazAvblNsTDlUTTdTaTZFcEV5c2QxbE9HcElSTjExUzJtQjFLVTlRUXVGZE1ocS90NGFJTERweFBSczUvRkQyRm44czFFallsNHVGeXZaUkI1S05CSTZXcGZ0NFc5ZW94TlpnTEJaMEJNMU1LRS9ZbnBDZzdrcnlUUjB5VVYxc1lDRDFzcE9hQ0VmU2JYcHZPWVpweWFwU0x6ZFMxcFpRblZXSmE4eTFMVkoxOFhMM1MvUWYwTHRWbG9MdWIwV3JxNGlyWU0yMVdKcXZldWdWT0NLbW1XOUo0Qkw3bzZSZk1VYmNTYlFXNWVoS1phT1hFMXQrVFhhaVJzNnUzRVpaNHA2U3FoRzRvOE5sQ2tXVlZOVmpYRWU0aTlpdWt4OWVnN2lnSTRqUmMvL3hBQTRFQUFCQkFFREFnUURCQWtGQVFBQUFBQURBUUlFQlFBR0VSSVRJUWNVSWpFVkl5UXlORUZDRUJZWEpTWXpOVkZoUTBSU1UxUkYvOW9BQ0FFQkFBRU1BbzFuSW5UVWxUWXZSUDRwUmIrZDVOdEhFbUVXQlI2ckNaSkUycXNXaWhwOWU3TE52MUM1b1JlSXpaend6OHIxOWFya3Qyd2xYSUg4bE1iK2xjaUxzVmNZcTc0N0pydXk0Um5yZG5IL0FCaXQvd0FaM1R0dG1wTFJ0TkE2d21jajNvbGpScS9TZGVyVFRielYwYVBydzlNeEhPSjRUNnFnYXNqU0pzRUx4dDhSTzJqcDJ5SmtUNzZ2ZkxCZU1oMmFEUkZHWmMyVERLM0lPM0pWeVk3NVM1WG9ubDJyaWQ4N2ZqbXlaMndHM1h4anNjdmJKaWJzWEZNeEh1WmhKYkd1NDlzUnk4bFFqRVRGVWUyVHhNL1hjUmJNakdpRnE2cWdhbVBhMlJPSnBkSnB3VmpObnpKQ05mNFo2YzAvcDQxaDhFT2h3Ni9peEUwcExlZ1Zhc0VYMWk1YUo5Vy9OQXZDMEprZTlFWHF4LzhBc1RDRmc3ZDNKa1ZSdWU1UmZabXUrVmtLMGpDQTFqMDdwZHd2eFhQamtIL2t1TGZRdlpOOCtQUkZ5dm5nbW4rVmlJdStLM3RrckNTYThiM0lvZTVKVmN2c0x1U1VGaXE1UmJ1MWZYK0lWdllxNnBzVUJGZDRXNjd0bnE2eXVSdXgvZ3BPWHUrZVBuTmUyYklteDJPelEwSmE5aHdxNXJzOFFYZndoTVhLL3dDOTVjZmZ5WnBXdFdZUWgvTWpiaVVoUC9XTFBnem1wM21DeUF6b29vK1crV0QvQUplMlI2a0wyN3VrcGphWVMvNjJPcEFmK25GcFFwN1NzU3BiMU9wMXUxWUJrRXFxd3ZMQlBURjlzbDVJamZVbEp2MjhzbnU5KzJlV2ovakl6eTRQd2xZa2VMK2VZbUkyQ3ovZjlwRFJwWm45T1VIQkNsUmpWVE5mTC9DVXJLd2U4MU11azRUeU4zelJjZ0krc041RVJXekFwL3JKaFpBMWIySmtSZDkzWmY2dzFWRGxGWW1tZmt4dkZEVmtnU0dnMHJuTS9hVHI5M3RURnlUNG1hdGh4U1RMeURLR0NENGphaG14QnlvRmRKZUJOZmFrOW5WaE1rNjZ0b01MejlqQVVJcW0zMWZJa0RROUd4Z2Z3eVZraVV4REVId3hUYy95WngveGlOVGI3T1Q0a2swdEhCT1JqQ2lQSFRoNXA2NVlUZW5KT1JzZEZYVHh5RzVFY3paZGNyL0M4aE9XVnYzMW1YNmZ2WSthVGdpTVlwbnF1N0s4RzNaVngwVUtKMjN5T0Z3MmUzYlhWeDVTSStFRW5FbFl0bFZ4V3dZVnRJR0ZrcTVkL3dEYmxZY0VtZkhXTFBuSE9PdmpraXgyeFlza294aGpTeWR2UG13Mmc2NjEweWtoOWpNUG1rclo1R2ZEWlJWY1Q4dVNja3NiMW45c1JxZjJ4TnY3WnczL0FBeDQyS3E4a3lRRkhwMDJPMldiNnBadDhvRTJHN05hcW42cm55cVQ2MW1hZy9xeDgwY3greG5JbWRKemRsYWk0UXo0dnJlUElUa01ORkZBT21ham15cEdxSkxUdmNxRVJwUGNiY1lORVQ3T2FSclljK3hZS2FIbXkyakJpVHlCampSclJ1VnE5bHdjK2F2cFNRNUVwVG5XNkM5U1pzdkZNTzFkc2xKOVE5UDBOMnhNSjFPdXZCRndodmYxSnRxcTIrQ1E1MW4wK2E2QTFHeTBwVW5USk1aWDYyV08vU2huTmtONVZmM3RtMlh2OVRObWpYTlFaZVNwZ0pNZmZicnN5VWFNVU82U0dMbGFZWUt4WkpIcnRNOG5aWHhaZ2x5eldHTXZDQW5wWThpL2x5dG1UNGhVSkRjcU9rU0RGY3BaaStxSWdYdVRsN1M0OVFJWTN4Q3MzYStKR2tqa3huYjRLYkdmRllWT1d6aUFkM2VUYkprQ0R1OTZUZlV1MzZPV0VNMFBjamtUR0dqT2J2eWJqWEJlVlNMSkRsWTZNVWF0QVJyczFvakdhYU1palRlbjI4NlB2bC9zMjFQdG1tb1pwWFdleFV5RFJ0WTkvbitDdGRwZ3NjS0JnQ0V3SjFIQjBYSjh3OXJVcTVMQ09jakJFVkxHeGhBanVqQ3NuQWNrdXNjbkorcVpmS2kxZlF3NEh3NlRhbFYycTcydG1IVnRaSWU5UER5K2cwRHBiN0NhOTJIMWZwMDRURFdkS0lpV2NhRUZpZEdSd2pXbXFscXdGaTJmb1NmcmRZcm1FbEFhK0pMMW9TVnpzclVSQTdmNS9RNWY3WmRLdlNiL0FIcnRRM2RocXY1Qk9iUnZpRFoxWFRHTGxOYjZmZDZIMnczRzE3MjB3YmJLbmJ6bzgxQ3Y3M2s1cEd5RUFoWTZzSXFrczI5dDQ1c0M4azRQMGc5czF5QnkrR2RrSDgrbGE1eWlYdG1wWWpVODIxY1dGNlNLaXJrU0V4cEQ4OThiWGtZeGlGYnhkQ2hmYlJkODB3QnJqVGtjbUdDMXpmYmZOTVYvOE9SSFlRYld0VnZESHhpODNjUnJzc1kvL1V1S0V2OEF3WEZDYi9ndUhoR001UFJqOUYwOVJNK0lWMFFZekZzcktBNVlZTk1kWmkya3dmekM2WGtDeldNa2t6U0pqK1VVQ1ZHNnpoWnFMZGJTUm5oVXN4OGlVd2ZIaFkxWjVET1hzczJzT3gzVTJQbXBTdkpTU0FvS2U1S1I1QVJDdEV6ZDF2SmZJUElZL2tqK2tYWTN5a1hHZ2t2SWNZaHNSdGpFZkhsK1dBeHZBSWkrcnMxdVYxa3NPV1lTWXdxT0cxclNiNXBaVi9WeUp2a25iamdrK1VpNFg3T1B4ZmZKTXFMRlJGa1NHRHpXdmlSOEN2aTZjQkNocG54T1dHU3dUZUhHUlpzZUZXT0lITlcyTXQrbmxHNlMwZzZwclVuaVROUS8xV1QzendzV1I1R1E4Y2hHSUk4dGoxNnhlcG5pRmRtcDRJVERDMGJkWlg4MnJIOE1qWE1TeVRUd2RwR3p2ZlZ6VWJZeWRzVXF0YVJxTlRCSFhxa2J4Yms2UnZQM1JpSWlHOUtyMnlpaHhwY3lRNDRVZGcwYXhHb3h2YlROMnljbnd1L3V6MXd0TnpaVXV1S2g1U3lHaC9rcGhQYkNMdGxqUEJYd2pUNUMvTHFmZ3NxcC9hUjRpUFFwYkdSWFRaa21kTGFRN2g2cDA4aSttekkzUDFycDNkdmpVakxuVVlUeFZxWFdyek9xM3A1NE9haGR2WnlGM3p3MXVoUW9CNDVOa1FtcFlxSjl0RnkwMWJWSUJ3enVZNXBlTmdqbVYxWXdJSXV3amRkcnMxSFBiTGxuSnhWTVZCY0hZQkFvUTN0bHQwM1QzT1Z5YnM0OFVYZktDU3dCNU8rSTdkRXdVYUFmVGRlV1hEYWJDTllNZkViVVJBL3lVd2k5c083OE04UTN5RGFNdGh4YzFpVlRlRzFPd2J0MG90QmFLUFhDUE5qR1FsbkE4SW9CT2tLNG5HV1l6d3pERVU4R1ZiTEkwKytZZlVDUzVMM3Z5cWQ5WUxMNTM3eFBtbUxLaWRhcFRXVmlZUnBtbXRQeEhpWWVSTlZhcXQwckVkMVJRRWM2eEt6Z3F0WEs5d3lGYTBpYnBxU3ZqUjVjbFFBNDU4TnRDRGU1c0Fqc2oxMDlDRmM2dUpoNnVlOHF5QlJEa3dWUGNxaU5mQU9tVVZiMTVraExHSXU3a2FKcWROcU5Ta0Y1alRVTm5MWlRidEZ4M3dKaGRGTnlwaHBVYi92Wmt5WkhhanZuc3k3dXFkMVZJaG5uaVRQUEF0ZERWOUdzcjU5RlFXSVlUZk14aGNLN1RFMmFSekJ5SEprclJ0NURSU3lKUStHbXBjbGRSQ2h2TnVsUTVmTWp5ODM4K2JkYzhPYXF0c05mbk5NaXNJN1dsakFwM3dwYzQvVFpYNnBqVENJWWZOb1RXcGpXSGxTZWxHMzhLcnNFRE1rc0c4SllsaGZJN21oQjZYMFZadlVGeUZnbkJscDVXTVNSOEpBcWFjMUkzVURpK1RxUkR6VldtWjJvbWlSQ3h3NXFlQXROWVBpR2UxVkJxaURhV0xhdW1Na2cxVEhkQ3E0OFZ6dDFrL1lWY2xQMk8vRHY3ZStXYnQyWlltK1c0V1BIR2kvRDBIU3FCMUQ1QndlY3BmWFk2aXFRUkNCWkY0dkRFRGYwQ0pLdEJNYW1oOVBVY2x0bEZ1M21QVHI5U1BMdGZyamQ4OEx5b3p4Qm1acnhsZllKQmoyZ1hPR2x5TXNoS2l0WGloR1JXMmtOakJwMHRWeW9saE9MS2FKdWFPRlp2c1d5UmJPancyTjAvR1pFY25WT1JZMXMzeVVwdlRmY3pOUjJXcDAwVnArYXNSMGp3NnZZVVYwdWkxM1lQbFR0UVRyM1RRcHZVNko5RDJrYXBuc25xTkZXTkpGTWpEa3h5Y21TVitXdVMzZlBma2gzYkozZG1YT3pKRGtUSmxySmVzQ01iaTBXbEp0RWVwSEpESDZtQmxFWXliZUxEWHk5RlpSeVZQbk9BeDVMTjFvWE5Pc2pLbjd5UEx0ZHBoczhOSHFtdnBUZDJiZUlMWkx4eEVqOW5RSWNZUzhIZDgxUFlrZ2pBTURFeDhGc3dmcUs1dVVqaTBoZkxqazdqaGFzb3JPdldkTnN4Q08zVUZGRWhwWVFiWUpTU2kyTmZxdG1ycThiREwrMHlKYTllTlExQlhUUlU0YTZxWlR5cEhVSkJxNDBjdldGelZmRFdmSmNZMWM0MjRwSytoY2xkenZ3L3dDT1RQc3JsOGlkYmZMSmorS09HemZOUGVLbHpweXBGVGdwd09IUzNNV2Q4U1NRSmZJeTZnTFF4aWhzMnlNK09VUjlIa3JINlZucE1vSkE1Q0FJTmQwdDNmVkZ6dzQ2S2VJVXAvUDE2NWxJMWtORVRkWWt1UTkzcGhiSkpwMlhER2luSTFFa1NDQnNqeG9saTlCN3pGOTVpcmswQkNSbmo2MmFhaUY2VE5wSEZiZHBnazJkSzVaVXdqdWNVOE16a0lsWmU3UGM2MWNtTWl5a2xNWWUrSTdLdnhUOE1haEZheFh4WFRmSGp3N1Jla3lYSmRrTFhPbkx5ZDVXdW43a2tQWGJKait5NWJwdXpmamlzNUw3WjVaRlJjdHRESFBBZFBtU280UWVIdDFwM1RNdDBVY0h6U1duamFPaWx5bVNxQmMwOC9xOU1uQkdaYjlwSk1pcnFHTHFVbGxwN3A5UzBKck9XcVQ3STBabVZscHE2YkRiT3JQSTlQNHo0Z043dlNBbVNHZWNQMUpQZHpZUUdmbWRrWUVkVEl4Ujc0SHA3S3FJaVp3NnowSHZsZkViRUYwbTk4NnZGaEhPeW5lMGxzZVR4OVUrcU00WmJOZzJJS2dTMXNLNXZrQlU3R1I2UFVFVTdqUkw4QVhuZzZvTDJOcXlTL0NhZmx2N3lMZVF1TFJzQ3YzZ3k0eUtvdnNrTGdSTlJ2cjZtYVAwQkFmdEN0dm5KcVNyMDU0YTJDWFVablRUV1YzQzFWcU1ka0tQd1pTcWpPaWpQYTBYNXo4c3J1ZFYzQnZKeVhNeVRxaWRPQzRNbVdWeksvVWxoV2g2TVFqbXQvVzIwZXhmTUhlcU1meWJtNnY5c2FybXExMitCTTNiWnE0RW5FemVHK1I1UG8zZGg1L09JWGdtVXE5TVpENGVkTmRHV1A1aDNUcXJXZFR5MG0xNTFZK2wxQkN1Z09Pa0xpVWtscEFLc1VMZVRPcDBmcWhzUjVlRFc4ZHNjamQvYklveHZibi94QUJCRUFBQkFnTUZCQVVJQndnREFBQUFBQUFCQUFJREVSSVRJU0l4UVFRUVVXRVVJREp4Z1NOQ1VtSnlrYUd4SkROamM3TEIwUVVWTUhTQ2tzTFNOS0xoLzlvQUNBRUJBQTAvQW91ekIwYUVSMkhhaEFQdE9pakxMTk5paTBmRmhHa1hqTXpWZTZ2K0ZMcVZkU0s4UTlsaCtuRU9TN1phWFMwTlRqNGxRekV0QTBBekFGWEZORGZyUzJldkEzS2h0LzhBVUZYdXIvaFU5U3BGYVNPNkhzbjBDME1oYUU0dkdVa0hSQkVZV1ltWDB0WU9PRVQ4VkdMcW5VakQ0eVVkN0RoQXdtOVNiNXZyS3ZkWHU3bFBkTDBWN0s5bGR5N2tCMUtyelN0TUc2bVFoc2lEM3J6YlhhQzc0U0sxSmpPUCtLdG9rTXo3MDU3VGhWTGZ4aFZxYUYwbkx2WGVVRG4xTzVkeWJ5VGh3NmhjVjNMdUs5Z3IyU3VjTXEyZE9TdXpXSDhhclUxVk9XK2FoaytWRFhHNWFPYnNwSVB4WDhtUDFUZTA1a0JsM3hUMnpodmZDWmVQZXY1Y2Y3SnBIbEtQMEtKRlR1WHYzaDU2a3BPRFlrbHptZzkwc1NlUk1UdUZ5d2ZpVlkrYXJVNUljOTJoVVJwQkl6RTB6c3NiRXlYM3hUdTB5SkZKQlRMbXNaRklBWDN4UWY1ZUUrUGg1RlF4aGM3TnczMW5xVFhGV2grYXFXRDhTdEFyUlZvODBlS1BvT1RZMGcxeUhCdTZkNG1nNlFHN1VOdVZvTjlSNm53VFRJOTZnVmtOQW5NcDBTL290VkdsMkpUWUpWWjQxV3ExV3ZhV21JS0d5b21mQlJJaGRtcFpuZDNJNjdwWXdDaEk1cHpaakxjVE1DN3FIYzV4SjhweFF2ZGRjRWJPK1hyQldnVm9WUFVvakRKeVlKUTJWNUJNMk4xUkp1eVU4d3d5UWRqSmhPSzQ0cEsxbmFtRTdEeVJuZlE1Um9MYWFXRTBpZWFjNmw4NEp3Y2hJWElBQUd5S2V6eVE2TzNDRk9iWTBnWmpoVEphc0d6TmFlcE82YVpYRG9jZmo3MFBzMy9vb3pnMkhBWk1rcXVGK0lLMEN0U3M4TFY5MmdMNjdsMFdjdjZnaUpTOEVDVllzS3RBUWZGVGlOSUI0elIyT1h1VnF3LzlWSlVINTdwbGQyOGNWTTFQdGpybmNoNThQYldqNWhWQ2J4dE1NeXZSanc1UTNQbWMxYUJXcFFIRFZEZ3ZzZG9zL2VuQ21jVGJTV3lubkxWTnFMUjRKM2FhWVZONnNSZVNueFdWejRjVTJNLzVJN0hweUlVYVBCYVhUN09GWlZLZy9NOVp4azJ0OHBsUGZhUDIzYUJXQU9GSVJGNTZPNlFYT0M1UklqUVhzZzBnbWFyQ3RTcmFYMVUxNnJhVnRPMU5oUmRwaUNiWUE5SjBsdEVBeXNXU01LL1BEY2ptcTBJQjg3bW5PaER0ODBZNTg3MVYwVjJSNWhNc2kyZnNvYUJRVy9STWRrSXdubURxb1VaeklHMGtTTVZtaDZrQ0U2SS91QW10cmZWc2NHTE10MmVIUENHRGp6VG81c2cwMCtUMFhxeFNQelI0UnorcUVjVXc0bm1xMEN0U3JZa1hMWEpFWHRkZk5PN1Q2SkFwZ2NYZDBrKzhUVmc3NXFVTStNMTBtNysxZEhpZk1KMWlCZjZxMWtvTEdPYXdzQnZRRndBNm5RSWsvY3JPSFQvWW9rRmgvd0NRY1Z3UEJmWnh6L29nTUVOenNqN2s2SVhGNW5tcXdyUXFPN3lVTnJwTmNvanBTYnRCdlE4Nk5qSTk2cS9WT1k2NU5OMGtZUnlnbmlwUXkzeWVzMDZKcnM4cFllNVdMeDlWck5NYkNjMFBISk5Gd0NzRzNvQmQ2OXBTOUpSWUxtT3hjUW9PMkdFUU01WDNxeUZEeVh6T1drN2syNnF3a1BlaExGYUlSQ085VmhWbFFOaXJoVmVhYXMxVzhWZUNGekFJSkplZUtlWmR6dUNhUzF3THNsRWpYRUhPNU9lSFV4SXVZQ2hzTHBBajlGQ2xWVy9qNEtGUFVtYzFDbUM5dVJUK3pEWURlbVFnRHVyTzhGUGlsN3RwdHFyYk1UcDBWaXdERTg4SnFSR0xNSjhyd1JPNVY0WWVHVi9jcXdxeXYzYi9BSmhXN3NQSENtTjhwRTBaeTcxRHFuODFsT1dhaFJNZVYwNXF6QmpPZS9DeWVnQ2NLWU1lRVNMK2ZGQmhmdG0xRHpHcHQ5TzB4SnNlZUhKTWptSHRFdUtoSEhkOFU5czJrYTdxenZLMldLYVh0blZlakRBdE9qbStWM0JNd3h5WWZZT2srOUh0MC9zMmN2SFZPaVlRN1oyc0F2eXVWWVZhT3dlT2E2UVExM0REZXAzeXlVUXUweXVSMUNpUkJVUzFCdmxtdnVCbHFPS0xxb1VKbDgzVDE0SnpERDJ1QzU5TlFPb1BGTWJMNlM0Tll3L21ueERHalBZOENiaXNqVkVURzFNYnd2M1ZucUIyU2dpVFhSSE9ubXRxQlphdWRTMG5UUEpSTDNiTHNaYzZnNkxaelRFaU0yY3lVeGVxbDBHNFQ1b3huU3ZrT3l2dmYvRUwyMFpoTWlFTUR1RzZXU2RPK2tJYTBCVnlKbG91U3FhNThJUHZJbXNuQ3huOGlpTzBJRjN4S2ZNdFlSdjQ3NE8wVWhqb2tpNGFYSWkreUV5Q296SlFRKzR2UE5QaVZCbzB2VlNiQ3BOcHdLZ05KTitpY0xxbTNvTGpOZTBqeFhFYW9uVlp6NHF0QXlxOFZhbk9LSjVwbHp5ZjJjeDhRZTFNRStLT3NDQzFxNVBrdWNVcjcwcjIxM3AvcEZTRUxvOEZuYTVsTWhGczlUSlhLYUxSa25adExzL2d2UkRrN2daZmx2MDNETzlhSzlQaUVxZlkwUStQSXBsOGVHQmQzaFN3MXJXakxxZi94QUFtRUFBQ0FnSUNBUVFDQXdFQUFBQUFBQUFCRVFBaE1VRlJZWEdCa2FHeDBmREI0ZkVRLzlvQUNBRUJBQUUvSVRyak16TG8wUVpsK1E1VENoSDY0Y2dzQUEvaU1HNS9zd2dBTkNKbjZxQVZEb3hrM2hpQ2ZSakFKd1J3SERYditld2dBTXd4cDIrNFRnNHhDQVNJejJhOEVqNDJlaEZqZWNTQS9DWkY1aHZWVm1ZV2g0ZVp0eEU0K0J0TUZHSnM2U1JpbjlERWhuRS9SQ2hBTnl3WVpEWWNHNGNRVHBjd1k2SXovWUNPcHEvcHdRSm10RlFjR0dFTGI3Z29yRWhjcnZqc1FnelE5WWRZMnhuSkJoNUgyREQwc0tQQnhoajRrN1RrNHpWRWdHK1ljZFRlb2U2eTRNSTBueGNRWFIrVVdMaENhVVlrZFE5ZDQvcUVrMzMvQUpoekJvUWlTVVZDQVlDcjJKVzNJbERHd2EvZTRRc2Q3RUdRR1lKTUEySU0xWnpERG9YWVYyanFCdXJoOGNFUFc4bWpESzlnbUxjaGZYL29rREZDU0NCMDRJSG1CUFJnVHVKaHRyL1Vjak9sZ0NOeStXVFhVeFBiS0NkQUFZTkJneEN6Qk1XU1BhSEN4QWFCUnVCYSsvOEE1UXVONjJTT2ZlVlpwd2h0emdoQUFrOFpoUmMxbDBXWXcvTWlvQ2E1Y2FmTUF5U3BKMlNIQnVDUTVtUm00UVFtYTI1cEcxK0VPQXNqOXpLelV6QXBJanBRaFVLWXhOelpRWDRuTVI1TkdHTFBYcFdUSUd5K2gvY2g0ZldiRmFhQXhUSXg1TjRMdTZSL2poUU5ab3NsQVc1TWZ6bWFJUGVlcHhFVWd3NERvTjl3UkVmZVcvdkFaQzFnenJJNVN6M08wWUhNNTVoanVQeTNNTmlEQmhGcTdlMklLcEtZQ21VQmtpbEhVTVB0SXpZUG1Kd08xRmd3QmR3NlFGSDlrcmhaemFya2p3ZGVSZGh3WkMwZ2VBSEw2RWNuOHNLbkFBMGhiVjVtZEluWVBQWWhKRklXYWoybmc3aVA0UWNUSUN5eFAyRTVnSWh4QmFMcmhsNVVld2k4L3dCakFuSi9kRUJSeWhEdU5uMElRRHdoZEkyZUZBQ3pPYzRoZjFEUGQ1MEI3Z3dFVkVGQ0pBV01BR2toajd3dUkrSWxrMkZRSHlCbUh2Y3dycUUvaWpwaFF3aWtIM0Jna1RVY3ppcml3bHZCMXdxQk9rNmxhUUZhY09nUU1BaGtVa2paeEN0WUxSZEd2V0hYdy9jZDdkNE54clVUMEl5Q2pyT1dwSUdCam83QTZoQm8wVUo3d1ZKVWJXU1ovVElCQ3NFU0t1TENTRGhDSUZyUmhLb3JLVjd3U3dqVG9lSVhNbUhKZThIcUFkT0tOYXczM0xGUkhNRldENlRISWxIMHFKTVBDVS9tVjU2dzJ4Z2hHVkpobEs4NGhJd2g3bklGelUvWkZHMkkrc0NJb1hCREFHbzFIMEUvTC9DSkFpUGtPQXNNbUN4SXdIeTlIY01JR1R5T2JHSU1MV1V6WXBTNXBUTmtFdTNwR1ppTUJnWk44UUYreG5qWkR1WGRJR0REVTdRM3lOd0ZhekJ1c2x6SlFOWFVIYWFLb1pVank0REgxcHRSQ1lETjNHeEFDbUwzd05TdHMyb2JNVERLUUxScHA3UlNJU3NTSHNoQWk0Wm1yTlVQTXlYQW5OWVJmNUplSllSeGNhTmUrRkh3SkJKYWhOSmtGSUNQb041NUlXY0FoNEJDYXpHK29jcEQ0UUVzQVIwY01JSllGNWtBTFBpQ1o1MmcrUVVkMUdhT1FnMlZJVWNHeWpZbmk4VlJqaWNReVE3Z3N3NytxRlY4VUs4TnBueHRiWkNyY0FTUldTRGg4WGhRVm04VjE5a3Rja1ZCdUIvYkxEUi9sbWJnSG1hUzc4bmhCWUFNcSszS1E5Q1pKSmtTblV4R0tHYUlSNXVNc2pXTGhNNFlySnRFUmxQQXh1ZmxEd3VJS2FacU10ZXNLRFBnYjVnQWtnSmtRNUNlWmRHMUJCWnlnSWlCQndEZVZkUm9RQ2xOMGc4bWJqS0F3WTVqQ3pVTEF6bUpSMjh3K095RGhNWTNveEY2YWFDVmJPb1F6UmY3eGFLWno1Z2JHd1oyaHR4SFFrVVVCNkZ3M3FpSlFIeE5RN2FHT1NaRWJhY0tmSG1vZHFCMndUK0lzckZENVF4ajQzWDBaNmhDblNvL2lpZXM3RDdVVlMzQ0hhQWlqRUNhSDVtUTdJeE1YUmFlZVh2dmNVbDZoV2dtY1FkSVBhTDZoYjI1Q2kzZFBhRGJGcVlabmcycVV1QmZzS1NWVjRYUGVLeGw4T3JBbFBHRys4SGp4KytIallKWGdSZ0hqQVFpV1VsQTlFS3ZZbEh4QnVLcUJZT0VLNTE1WTVtdlcyK0tRMTJyZlBINk5qaVRBSUFjUTBKRndHcjZYQWt5RnlOUWVzZGdZRW85RW4wbFpyRlFFSUR3eEpNeS93Q1VVd01ya1pnZWpnb3RVR2tVTUx3RmJFd3JDM1hFQ1N3TGIvQ05FTHBkWk5HSEV0dkVqY3ZFV1pmN3dmUVB1aXFyR2FPTFZHRlZLdWhlT0lONHRPN1dRRXhOUVY1ZzR5d0lMTXlCakJQWWtYNklSQlp0NnlhekRNVWR0b21zUHhPUmp5TUhVRUFKTWd0eDZ6cmlHUk5naXE3bGl3UStZR04xV3hnUVh4SWRGZUdBeGdvWHlpQkpnRVdPTlBXZFNQc0VIY0h0Q2hvYlNLdGNvTERJMnQ0b3h3UG9nNk80QWMrWDFoRjJFZVlGTU10elhUdU5CN1lFMmV2aUEyRmVTWUFJRGdRUXVjUE1mY2lIRWRBamxpTFJqSG1XUXVoK3owNWtKSkxVSTQySEpWMUtHMXBqRHNrdWtCdnp1Y0E2SEVlSzVlQkdWUFhKaXRuVWVHSURtVUVPcCt6TXkrWWZqa0VUSDdpZUtxZ0tpZ1ZLZEdNeU9RQXBtaG5pYytRbG5LZzhiblVXWWJQVVZlOGxORDN0aUV0djhrVzJsSHYvQU1Lai9DUUJUNFBxSDJwUmhjb2VFSW9lOEkyNXhCTW8xQzAzNWh0cW1wVUFYTXVxZ1pvTzlybURaN3VKb0FPaGgzWm1saEtRWDdjclA2N3FPM3JIcWdPT2xITGxWdVlBMGRQbUJQZzljTWNEcUdwL1F5K2pDZEZGZ3JlS3lCeVdSeDFFL3dBMG14Rlp0WWdFU3pEUXk4bFZCb3RXdjJWY29yaXdyQ01vQ210eXViZ0FYK3AzS2h5KzV4WEU5UitJVjNLbkVreEc2aHVHN2l3SThwL2NXQ2hFNmt4RTRqbEUrSU9SVEN4SGxHNDlKdlFlWWFzSzlnc1FPREV0TzIzWjhBSmczZ1d5VEpCS3ZpRzBPYWxNdGdvY1ErckNLdmdUT3IvdHphNEwwSVpzL3dDa0lKb1ZEbEc0aHIxWkJ1VTJ2WkcxNW1BempneG9ZaXdFRnoxWTRKUFVkTm9oaDVxb3htZ0JCYmcvV3d5ek5HME9tNVhROUtpTmt5cnBiQkxmVU5aQTBmY01YeWlNSGhtTG13NUFuSkRqTXBFcEtKNzFEMmtnMHg4Q1Uzd1lnTmE5NE5vNXkrb09RaFJBQWRUUEVZQmwyYUVmRHgvQmhHSWF2eVJtRUVvVmw0NGd6WnVrZU9DeWN3NDg0UTF0KzNDaXZjelZSS3poTGlxK0RtWUxPQUp3aHM5b2tlSVVnVm1vNWdDUXZtbUZsYm9RdnBrMlJRckVJWnVaa2hWRFhoQ1RLeStUQXBqd1l4QnZBdFNrTjVFRGJGUWV5TGpzWXh1Z01wWmF6OC82VGQ3RmpaYUVHeS9NUjVLNDlFWWxMblhDRlFpV1hJL2pUYXp2KzFQZjNvbjJZZUcxaDVqbVFMQ0FZY1pyeUU1TWR6TEcvRTNFU0ZQQmpoeXlDQWh4Sy9pQjJzRS8waTFWblErUkNHalFpbUN3bkVLVEY2aDZaUDhBS0ZPQjFDM0Uyd1RwYVFLREZka1g1aHk1WklkU3dOY3ZCYlA2d093YkVTdUtBdmJ4MUNqdlJCOXFBazg1ejlvQmhlRUxZWDBJVVZqTS84UUFKQkFCQUFNQUFnSUNBd0VCQVFFQUFBQUFBUUFSSVRGQlVXRnhrWUdoc2RIQjRmRC8yZ0FJQVFFQUFUOFFDZmhOUnJyYkFwNGppVFF0NTVHRnBxQjh2WjVlc2dONURha1BVVXNUeEppQ0FLTUlucjlLeGJDOGlRdXdKWFQyQkFBTDRSVGViUnBINFJHckFJaCtadTNPWVlsckg5WXBLZkpHMWx0bURYdm1Bb211bnUwdHFxdlJITFQzbk1yQ1BaSEc0VXRkZzVxNzlKQ1lxajYvT21sYTdmQWwzZXpnWlZzZ1VBV1lIcWVsMjhxS0YzMGpVdlp3UWgxU3hhTWF0OHdRbkVLcjF6UEpYZ3lMVDFDYWRTdVNoM3FGWXVHcFExRWcvQitHYUVFcmlkUXpPSXVjL2NZYTZ2N1ZDd1BleDIvQk9ZcER3UTNxT2Q1YlF5VnNudXRTcmFyZGhQWjFMbDFITEZ6VjlqY0tvejJMaUpRUXVkcGloY1ZBWXVHdFFleW1hcldVVEtmaHJDenBzdnlobEZ6NXgzQjBDMkNzR3dkQUNzREl4VkpDdVU5TVR3UEM2d1JBMXFsYkNLNGpqb2lXVlhHMHNFNVhYT2tMdUQyakhjOWRCbG9ud1Qvc1BlUGtKdjZoM1JRVmlyV3UyT2xhc0tyY0t4aHQrU3FnTzlZallLUHpIb21vZGxzUnJJQmhLbW9mS3k3TmhDQ1hsYm5Da0pmaW1UbHRRYXZSaWMrZ1djS3orbExpcmpGWVdsNkhJakpUUkFIVE9UckNRM2NQQ25pZE84VHVzZzZwY1BZc3BaSzliUFArV1ZhbDRkUDFGNmFjdjRMZ0dGRkM3aHFSMklzVXU5cHVCM1ZkOVRmL0FCbitVUTB2am4va1dCM2dsdEFhR0RuaUNWWnJzQkY4azBXTHMwVHVUakxOL2FLaE9FdU9xczd1TnJDMDAvQVpHaHBuQ05mNHFjQUYxUWYwUXdCSEFhYzkxQjlUb0lmWVM2a0ZodEx1RGE2eUpaZWxMbFJTeHZJZFhyRHNhb0hiTjZKREVnUzdlcXdMbWhQRzdWU25wVHhjR3ZCM3pCNU9iTUtyblFDeTIzSmZBVlJMcEl4QjlwZVhINHdqc1VtM3FkYnAzRVVwVDh5MnBoYU9EN3Uvc3ZzVFdIRWJ2ZlZXRml0TFdaRlBBR2ljUThrZW5VME5wUXk2TTNuRnRWMDhlR3NYMmJERllZd1FBdHVTc0JuU0MzUXQyNHJrNDF3RVhvUUsyS3JLN2wxQjVKQU4zZTRpcllYZ0V5OE4yd2dnZlROS2dLd0F3TVljSVVnemlKeG1MenJGeXlhRnNYMmQrb242Q3dRSUZHbEZ2MU5odWd0SkI0dFZtOTRScHI2WUtFUmo2TGFPZ3owc1Vzb0tCQ1JkZFFUN2hJZTdaNjcxR0wzS2pKOUtDbDM1K3lISHdtYU9CRUZ2VVh5dEtvQm5qaEVpWGpNbGtQOEFsRElBOXdhWDVpaTNCMFZ1Rm1PRjJoc0czZmNNa3BzSzBPbUR4MmczM0d4ejloOVp1RFYxMmNQQllVUHVZQUo5RmcyOEpRNllxWXJ0TXZ4RDZCTjY5NUZZMm5ud0pGY3JmTVNnVUdEK09ZV0Jua2hoaGhVRStSbW1TMDZDaHFyRGFtZGt5dmRvR2NZRDhyamlMU2tXdDhQSkdJRjhJemRITW9zdkZYeXdHbTJOc2doRmhBTmRUUjkrSWw5ZWZNSWVRK0tsdENjYzBVTkxUWVhYVVllOWdLcG91N0FQUlliRDgveU5CdG9ncnZUajRrSURRSytkaXBWWUdOY21zRWZWMDQ5ZmNkQmsrUm9JeXp2bElMRjNpc25NZjVJcmFvOHpjRm9ONDQyNHVQQ3RSN3pOci9uUDFaRmVmcXFlVk9wUXo2K0FYem5VMk1yMmZsYUhIUkd6MXdtbEZGdS9QdUF1OHFqWGs0VGNqTmtmVzNVWVFCSlZYZ1hVZU5CK0Ryc2QwNTVrYlN5dVZKeFZHMkN3a3BXYWNmS0FwWXdTMUYxc29MVCtOQ3dMYzJpdENWMnEwK1d1T3EybitNM3NBMDdMUXdkRHVWMGVwUlJZVldsN1pveWxZNk4wV3NMMnp1S3F2Y1ZBV2hIQlpYTFlHaWg3TXJ1YkdjbmtwQSt4a3ZFa1N1YzJMQWxRdWQxS014UlN0bFBNSk9wVlU3QTNHekZMM2RMbGFFSFVmUFVjcFd5V0M3Y1ovWW1nQlpWclJzNTRnOFFLOGxBTmRndUwrVUVka2RvdmlZejErTlcxUXIwd3J2R2tTVWhkSW8yTldmS0dkNHBKVHdmaUJRSlErOXE4aEJETXhGWVc5akdzeHZqdVVHd09LOWFBRUJXd1lrZElrdlBPWlpSa3FhN016SnZQL2FlRG0rY2ovd0Q0ZkNZdjFTQXRtQzYrcnVQSFB0V3FLanBhNFd5MHRmbVc5YW1WeXVKanppQ2dCR3BTZTNGMyt2VnpGSWRPUUE1dEQ2dUl3KzlpVjRHZll1d1IvU1VXZ0tidDZKVHBWZk5iT0JBYTd6cHdyaTBsdUpZNFp5cVc1dDlxV2dvTmZXWW4xUytHdi9rdGZWVVd2d2F6K0piWFlRdW1DNWVaWFd6VCtnNVMyMGNkeE1IRlJGeHlyNW80b2dFRmVqd1psaUlpWnh5aFpsd2RybGhlaW8xdUU2dkRwbUYzeTFUbGdqOHNHY3F4QTFrd3lMVXVzWmVqRDJRU2tGK004RHFoOTNMNE1DS0NLMDFaNng3Z1hkcUNDQUM2bXVFUEVXK0hWbGlRb1VXN1RCS29NYldTYThmOFlYYzVKRGpkMzNjbzRsME5IQzB4V0lNVkJlbDVwMG5FdUU5YTYvTEI3bUNrQ2s5dlRCMVdpY2FMNVk0QmJMdXhtbDBhbDgyZGNRU0Z1aXFWWTBsb0VPM3FOc3FoeFFVMmNBYTlFUllXc0R6Y0VPVndsQitxWTlRaWdLV2xGR1ZUZ3VzWTA3NGowVnRkMlRPY0dMOHRiSUZ4QXVvQXF0YXhYQ0ZKWEQ1UVd5eUVPVGs4WXdpVlZLbmFsZFBCdCtvSHF1RTBHMXl4dUs4d3IzSmhTTTRkbmx5dmNJMGZHWkxQOFBEMzRKbnQ2N0hyeWlKTXlRTTlUR1A5SUVyNFlJMUEzVm5RWVFCcGlNcGNTQlJQK21BeUZUeTVRTUdXditXVWdyalkyOHFxaTJrV09mNDg4Q1VzbDhxc3M5NnNWbnh3eGJTb0xCVitGaGlibjF4YkJFNGJVWDNDT2JkdjNtWTZyWDUzRTB4U2xrTzRWREZVUGRNNWlDSE9oZUZZeGZjVjVtOE1MNXpWSG1JakQrVmlXbWNBUElGTkhCYTRpU3E0MVhhSWJJenh2SU5QZU1JZ1NMVGtiakU1MU5GUGVtUldyTURWS3JYcVhkRWxpQzhEbDFUTExwQzBFZ1ZqTVpqV0VicWc0SmZYUCsyQXo1SVkrdFhEUkdlYkJQekJGcXUvQkYwL0ZNVHoxUkRtMk5XU2hDNjRXckhxbHN1VmhlVWFTcmFtalEyeDZ5YnRLcWtjclNtQlIwb2ZyQ0NKaytBdFBicUJRMEpXbXRxVjRSR0FwckZWSFc0TXFidDVmQzZQZ0k3SDFsY1lnb25NMGhab2p4R2pyeVhFTXI1NmxCOVd3UU0rUnhMN2JyZVZxUElGMzNGUFcrZnJSWFpxM21EYXFsWENjQ3h6V0V0Mm9EUXhCelFrQ3lJakRnNElLMStvNHRsMDg1Wk5CTFkrYUhMRFd3d0FSdHZ1STNaZVhmOEEyTDJvclU2L2NIeGlURGRoZm1INmFJTHdXTEVBRnh4VWNUd1dwMnRVdzBReUdRdXVBeFo4N2NBb1lOVzBPQTM2NVdMTVVKU212dHhPWWIrVEdIRnBUTlBFRzV1MkNxOUFVdUM1VnNlVUtLeGR3ZS9UaVYwTEwxV0hjQU1Ea09Md3J1bHFFaktMakdUeHdkUUxDeW5qUWlleVY4WTlCYjJDdUxBcVFNQ0NnSEtpVUJFaldTamV1Y1VzaDllbFZRdFBjSU5BdTF2Y0JKV0I1UVZXZ0Z1NFBuTEYyWE9vcWtlbFFLTGovWENPWTc5dzNrdFM1VTdhNTcyZC93QWpBM0NoOTFjVXlvODJ3WU14UTNhaElGVkRQaVRVZGxCVmVtTHlmYzIrTVZ2aVhqRUZzS0Zzb0dta2JtZkh0ODVtWVZjVGpVM3cxbGVhU0EwdGZKM0ZXOTdMTzNiMHFLT0x4ZlMrWWUvM25DOFU4cXN2S3NTT3R0QUt1cDBPWmFLZGF0WWlyc3BGNnNoT0hRQWhMVzNEWElDaW1yUlZDcGV6aTM5QVNLRlZvSzFRV0VZNDFWQ0d3SGtLT0VxcHk2cWRJQVU4cExWU0NERkZLT3Z0T0hoMmVyN2llQmNPalA4QTBTbm0rNFExczhSbVNqKzQ1ZmdvWVJZZFdsTEkrTXFkS3lMRmwzdU9ieHY4dTh0UkEweHRZdWkyQTBBSjJKOThsYXk4c29hTnNNbHB1QnRwTDN1VzlMcUJGWnI2N1F2RWpodThjNTV3YkRxMEJPZ2E3ekc0N1BDM0wzWXZCeVlkWnovQ0YwU2x0TzRsQkJVSE44azV1ZUd4T0hWN01hTUJLSnNJa0ZHclRqcE1Sd3RQYWI0SVhxeG5QZnNkeGVaRzY1T1U2L2k2RFl6aTA1NXdJYUZXMTFDSnlUcUZ1d0RoTEkvQzBENnVYVDQ0SU4rbjZjd1FiZjY0QkZ3YkdyZFZoS0phckNKRVBSeFNrZjdGMnJpQ2lXaHl1b210WE10WjRUdHhjTFUwcEtSeEZVaGppUmZkdmhocmNkOWRWR2dhZE9SbXJiL2RsZUladGFWU21panNxTFhCZzZERVdxYXp0bGJ6ODdFeFUwSHF1TzRnWFVOOCtKUlhPYkhpQlNFOHEwanllUkpRdFplSCtTOWhHc0FGODFCTUQ2a0ZORkh3YWlkZHhMTnJvaXI3ZHgwMmI3RGdRR2lnc3FlVFMxeTVISmF3RU5nbU9keXEyYitlS2hvZk5NVXNaakg1VVJjRFFSTFhGbGgxY1JiMGVDSzQ2eHNGa29HL0JLcDI3UEFuRmN4T05qVHNxcUNEeXhZdjIxSzRQZ01WUjIweVpTNldtMFRjbmxrSDBHUlRFYTFHeDE0WW41TllSNFJhanU0cFFYNW9PVkZRWlVvVFFiZ3U5ZGxzcGRLeTRESDZLdzA1ZUw4Wm5nakFuS000SDF4RlAyRm5TZmxnMDNtdEdyeUFjSGxIeWJndjNXSmU1WDQ0K0FsT1ZaWnl5SjZtaTVTeXZxQnB4WFlVWGUzOHpmcmJ0cUdCMmcvZHRjcGFVS2VqSVBZeFd2Mkp6S2FORnNPREF0STE5TXhuK1I2bjU2Z1EvVzBXSG9VZ08wU0Y4KzVaTElhcUwwck45blppa29BN0NyaGRnQlQzV1J2WnJyNHVjalQ0M1ZidFEvMm5tbkZ0cVZHVkVUWmJVR3B1T0YvZzFwUnd1T1piTzBDUG1GaWs0TU1VTFZMdWpGNjVxTGV0VXZEOHhkc2VBV2ZaN2xaWVFnZFRsV05hc0E0MlFlVVFGNE1QNHh3c3ZNdGJxZHlsNEp2emZBUFdqRmpmbmdROW52cEUydkN3cmkycStKby9FdEtYbGN1SXV0QjZYY3hTSFFVUlBlWUFHbjNrLzhRQUtSRUFBd0FEQUFFREJBSUNBd0VBQUFBQUFRSURBQVFSRWhNaE1nVWlNVE1RRkNBakJoVkJKUC9hQUFnQkFnRUJCUUNqbG5kV2JOT1Ryc1UvU3Y0ZjkyRThXSDVyN0pIOWYrQUpHZS9UanR3cjhRTUhSL0NKNXNvOGplRkEwSW1CSTRLZnBRZEQvdHhuQXlKNmJmcmordjhBR2RIOEE4d1ozSFk4WWR5WiszeXdFNEd3c2ZRTXJ2RzVSbTE5a1hCSE1vM1pwOFc1Nm5WNTJPUzU1YkI1T1Y1cWcycFovYmdCL2RobjlxSUVicFppdUZPaGw4U0hrbWV0STU2ODFNOW5TQ0o5UjFFQStwY3pZK2YwNzdRUHhUNExsL3VJaWVlaWNnT1p0SDdFZ3BBMVZ4dFZlSFhVRDB6elVVUVBSL0ZQWW4zSVVEQ0pIQVpEQVk0dElBVi9icCtJSXloNmd5bnRYelRDNjhqOGZTMHJLOC9wK3ZUMS9wWEZPanNVcStoS2gyUHBYTlpkVzdCMC9pZ0dQY0F0WHVkenlHR3pER3Njb1I2a1QwcmpkOGZ3YWtzd21tRUp5U2tMS0hxMmVjS01ZNjJMNk0zb0pzeFdISmhKSVBZNVUrOWYyZngxTUlVNXdaUWRhQUF5WnhqN0g4MlBISlk0Q3laRStTeFJWZ2hJSlk0VC9zUGU1K0F4KzMveDhkR2FyZ3BoWXNNSjl5NHhBWDJtOFkwa29PTm4vdXdCNml1blhyUG1yd3lCb2tKQm14Z0FHQy95M09NdlFvSW42Sk9PZ2dvQzBENkVIei9yRDIyaExYRnE2VW5EcDV6SWJJbjdtdy9MWkk4ekNIVzA3dFhRSGpyT0JpS3hKUTVzNlZxblgwTmliWDE2dWs5VFlVbWJPVDVxSGV3VTBheUJjNWxmV1YvcnJ6YVczOVArbmEvMHBWQzVyK0FFMUFkdTV6cmJjM0Zic3lLaGVxZlRlS2R1dnZyTVNveGo3QmdjTGN6WllnQW5BZ00ySHNxbm5nK2NiQ09qL2tQMDdaM0NYMzc2Y3ZaSDZGaDBsK1lQbE1BYkZvZXFyUk1EclZEVXZ4bWtnVkFSblZBay9rblFNZVFkT2NLZkNtVCtCL0dIM0xKNUNYMGRkaUF1eWw3RXJyTTdGajNCOG1KUzZXb3JGUnM1cjZzamxUM05VandBeGg3VFRpZU9Ybzgwd0llUGt1bWVIMndBdTE2TkU2MVZDLzA2akRxMjhkU05UaHhmZHJPSjJOMHc3SzhPeVRiaGJOZVJTWTdqZDVIb25sMExybmVUYm5KZXlZeEdhLzd3UC9zdFd2WTcyOWRCdGJoeVZJK25UbkpqTi9XclYxMVVRaWMxSGY4QWMzMmpXbzdKNmtnV3JQaVZRQTFrTXZYeFQzT2M3SnZaWWtDWlplRnVtYzdGakVqYXNOWGxvejhFbEI4bnFTazFSd1MvRzMwdzkrTjBuWWl3b0lQV1pEcHJ5aTc0ZlpaVjlYTFJOTUNGVy9xdWlLUHNvUHRYOFRBN0g1UlE5SWMzb0tESTYxaTFybUY5YmN2ZHJaSWUyNlF1dkl0UXQ5bU9GYXVtalRUYk14TWY2aDMxTWhPY3BEYVJzaEJKN08wakhHSjVUMldmajRxdzdNKyt1ZXpPbWs5aXlYU2pxT1drV3RySjRWdCtOY0RuMUJldzF3L2taak5LQ1VZVUtaWlJkRHdMTmtLU1pYajZBUXJWbXBTenVHL05QZ254QU9TeldQdGRXS1YxS1Vlc3lxeVBNOUVpdXdwVTYzeCtva2pYMUZMczgvRVNzMFNnRG9FUVlWOW1WWnpod3F3bXA4bzUxQ0JxVTQranNPdE5QWWdpTUFKa2MxOEI5bmJpeTJsT1U5VmpPTkd6WkhCcmZDOG8xMTlaZExXeWcxQS9OSG85aC9CQjczd0RzWFBNWWRYdVVFZ3hFV1ZaNnE0cGpnZE9lb3E0OWp4OXA2VVkycWRPYnpTL3VzUGg0STZMcnhVdEdUa2E4Ui9pUjFlWXN5ci9BTU9pMFVvODg5d1E0TEE0VzRMT1FmL0VBQzBSQUFJQkF3RUdCZ01CQUFNQUFBQUFBQUFCRVFJUUlURURFaUJCVVhFaU1tR0JvYkVUa2RIQkJCVHcvOW9BQ0FFQ0FRWS9BSFU5YktwLyt3eGRyTHRaakcrSnhkOE1jdVpWdEhoRDJqMG43R25aV1hhN0h4Tzd2bXozZXVlM0lWTkNsWTdkV3lwVnN6bUNWZlBRMXRWRm80SkdxYndhWDhhbGtVVW1GOEZhS2wyNEZTYW1vN1pacWVZeFVQSTJuTjVIbkJsa3VvODQ1cit6emZES3U1Vkhwd0p2b2FtbzJKVjdTSDBIUlh0RW11VW1kcXYyL3dDQzJleHJUcWZjZEcwMmlUV3V2OEkvSjkvdzNkalZMSVYyb05MdEl5eXJ1T0RXekZUd1M5RU9xcWhOdm5CNUYraWFLVW4yUTZxcVZMOUR5TDlINU5sU2svUmNENEhKZ2ZBeFIwdExSaENhNWp6WlU4aDNidTBqSzRkeWRXT2xTVE4xMklrYWxFa01jMm02Z2FGekpaRE1vd29QTVR0Tm9sSnVyYUptcVBDSldZbDZHOUdTcXZHU21qbVpadk5TWXBNWVBGcDNGdTJlVklodEhpVjVUd1VLdDh6eHJkYmh5dnFleEtjajN1ZDJLcGFRWlRaNEVVSmtGSit4RGZZOXltT2xrTkdocGVqOGEwMVArdjhBOGpLVUw5RUpRTndTMUZtWTFqL2JOdEtCWVNGSW9FS1NlZUJkeFBvbjlqRndzWlZ0YXBlY0pjeUVOU3YwZWFWWmtweGc4VGtkTFg5WkxVUlpDRjdrSHVVcFBXZnV5ZXFIQXJ3dVorSFpmSE5pcGJoUjhtakhOUHdiOGVHTE04WE5Xd0xQT3lQY1h1ZTMrbnVVKy8zWlE3S3pLZTZLcDZzaWszcUVvOWNmWkZXN0IrT2hyVFNiTVZTWGhqVWpVbUJkL3dEUmlraVJSVWlHMSt6VkMzSHJOb1F4Wk5iSjAwc2RVWWFITGZ3WklTK0IxcFpnZHFWeWtnOUQ4cVhNM3FWcVBxa0t2a053T0ZaMGpxcnhaMnphU3RiOHJwMEhHbnQxSmJ3TlVVNkRwcXBoREVVOXh4eU4wcHA2RzZ4cDZzaEVQOWoydGVlU1JGZENnZE9xaVVRaERNa1hyMnllYW94MkdtL2twb25QSWRPdnVOT0o3c2RxV09IeU41amI1RUV0WlEzWHFoVjBqMmRmZEUxdkE5cFQwaEc2N08yTFFlRWRibVg2Rkc2L0VodHFIMVp2cXBhNnlPMU1GVWREemZCTkluVXNtaEJFRGNHVFFjVWt4TDdqUyt5YWxnaDJSbTNoVXQvWnY3UllKVFRuWDBHcktuYXpIb09taVhQVWROVXljeU9HYjdwRlVtNjZXMTZua01MZzhHRDhiYzh4cXJVcWRrcTFKS1dTV2lWVHd1eW0rN1ZsRVRoNkhpWjRkT0QveEFBbkVRQURBQU1BQVFNRUF3RUJBUUFBQUFBQkFnTUFCQkVTQlNFeEV4UWlJeEF5TXdaQlF2L2FBQWdCQXdFQkJRQlpvaStuMWhJZXBiV3BUUlQvQUZPYy9Ya3hsZmlIdWJEbE03blFBR0J3NXdjWEVIUm5SL0RPTUxjWCtvMTZ6TTdWRmhJL2xQOEEwT2ZFOFJUbE1ndjUyNlgrbm5nYzhEZ1E1enhVRHVJQmc0cEtFbjZSNTREZ1E0Z0gxRWFRcHIrWVcyczBETW55bXZIT0h2Z0ErS0xZL2Vhdy9ZK3ZZdDlyWTROTFpPRFEyY0duWTVmV3JCVVBRcHdIdUNGNkQ3UFpBbnEyY2ZZN2lsdEhZT0RSSkdzZUo2alQ2aFFmblA1T1NQZ1Bycmd1RGxUNUhUSGFQdFVHZmVOaWI5T0p2dnhkbnJiOVRzcUFRQmlaQmlpTFp5cTEyUUMyMGNJMmpqUTNXeVBSSGFKWkUrVUhEM29BSlR3T0tqQTB3VjNaTk03K3lnMVBWT0dPN0NjTlhmc2llbmVzc050ZGpXSEIvQzVEUm8wWmFoUUtuaU9jeU1wbEpSbGtVUDBySUFxODhobi9BTXA3SjVuQXo5YzlOS21jSnZlYUI5blBwMG9zL09TcTFrRldlemtZbzdpRHVhSVAyNEdjT2R5WllLaEl5SjVQWlBXNGVybnhOT2VDOHp4Vjhxb1ZuY20zQVFxQVkzdE9ZNnVGVnpnNE1UMkdyZVV0U2RaMVh1QVpKZjFxbnNlSnFjZWlkSVpjK1VsL20wYStFOWUzZGdFVzhKdlp5RkNNVG56aUh4SFFRcGJxbm1GaDVmVUdUbzFTanZObytxN2NnUFd3QnIrc2JHMW10UGV2SXE0bFZXR0hCN1lEK3VIc2k3bXo0UTlaMFo2dnFORWZjZ2NaazRwSE5UYmxETm4xQ0ZVaHRTUm4zTlpsRkVSRldiR2FRSkU1UWZtY3pXKzJlUDhBekV0bExlbVc5VTI5dDNkemRLRTkvSEIzdzFtUXBxUUZxVVZOZXZxUFh6U240cnNxTy84QWsvbnhJQ3FUbWtQZkhKRlo5NzVUSitvbUJ4aU1BZjhBay9WdFRSbE8rcERhcDdzbnk0Q2tZUGl4L1JDNGtWb0xpOFNKdzZFcXhaL2ZpaGpsRUtNb0pFcStEamdEL3dDa3orVGM2ZzRlbkZIQk9qSXkrc1BIWitncEU0Y2Fxb3BHRCtwQWVieVJsVi90OHRzMVhKamcydjc5OWd3Qm8zNWcrMnZOSGZnQUxnc2k4Tk9oMTc0alA2cnJhNXVkclV1aEd6QTRMdzdlc2xZWjhCSk5TWTEzSVRVYzRtbUZnUGJObHd6OEhFOGN0dzBIeHJPRWIvem5hVC9zNmtzTVJUbE9mU2dSOXZyVThHdDZWRFdyOWpBWmJYdWpML0dqdHBKVzI2MUJhellFSjFsOTgyWktyaVZTRWxYcGxRbFlXSTE0aDM0QXZseWtoMHVwODFWc1FjRFBGUlBaQTF0YXU0S2EyNVZHYmYyRUd6NmpmWWpJOVZ2alc5ckw4cU9EMCs4akU3RTVVRHBTOUtvb0hYeWt2cGlOQkxPOVg3cFdZKzdSL3NXNFNTY3JsbS9HSlU2c2pQdGRxU0pHSTJkYlkxb3hTSjluOWhwanRqeVpRK2VRSlNHMnlPMmdLbW5EWThNeFozcFE2cnJsTDBmVjFDbk1qOHQvYkh6WUFGTmZjYW1yQm90T1JKYUxxa3Rpbm5LT1YrTkEvdHI0ZUtQd2J0M21ETVVHc1RCcG96UGFWSlZvckxVWGR3WktzbzY4MFoxSGpISDlseWh6WVVGOUI1cFdGdFFKS2dkblQ4UllHV3E0ZExIUFR4KzdaUGdFb3paU0MzVjJaS2VkRGlzeXNyTmF0d3lNaHJSUlBZQUFwMDdNems5cVNHZTlDMzhVOXMyY1pPNGdLaXVvMlNFVlY2cU0wejBXK2RhbHAydWR2WXlUYmhUNm05d09SbmtjQnhTT2M4c2pQd3dOd0lUaWN4RFRpbW9ZMjJUaHBiUE55Q081T1l5ZW1rcHA5R0s3ZFVxZFhnRi9rRjBMVnZpVnJQQmF4em5QNUdBOE02Y3hxZVUrOHlZR0t6SWVwbm1yTDRFSndEQW95RXcyZi8vRUFEQVJBQUlDQVFJRUJRTURCUUVCQUFBQUFBQUJBaEVoRURFREVsRnhJQ0pCWWFFVGdaRXlzZEVFTThIaDhEQnkvOW9BQ0FFREFRWS9BT1dEdExZbjlUMkpRaHZpc0xyMzZEOEdkS0hyYTBXajhPTjlGRmJpaFdhRTB0dEg0cUgvQU9ONlllbFBwZ2JtNkZ5ckowc3BqMHdiYUs5OUcxcFNNb3BIbjhDYVpiTUdIOGo1bnBDWFlpMWpSNk9Sc2JNdlNsRTJQMG42UmVYSWsxVmVCVXVoaUJVZUZmNE04TDVSNWVGOG8vcy9LSTEwRmVqMGFSc1pXajVPSGNlcHo4UGh0cCt0R2VFL2orUjhUamNOcUs3ZnlMaWNQaHRwNVdZL3lXdUY4eC9rditvaTRyN0QxakpQZEptK3R5M28ydjhBQXV4blZEZW1kSFR5emxqeEdrdmMvdVA4alVwTnIzYk9WU2RkMlltMTkyZlQ0cmJYcGJmZ2oyWGdWRnNYZ1F4MlVtTk4yTlBaRzJqYUUvQTlJT1Q5RVhCM3JuVG5yS1J6T2lxMFF6bXAvZ3ZsZGRtTkZwbURKUmt3UG0wWlJTTU9tVTNhOXo5SHljdkI0RGsxMHljMCtFNHZvMFZUTGxveERmdWZUVGRkQ1BDbGRyMkp6OUd6WWNJeXBtWnNxVHM4cUh6RFQ3Yk0yZGRoM2d5ZVZqYjBhbkh6ZFRpUzRLZXkvY2ZHNGI1MUJPTFRmdnZYY3BxdndWSDB5Wi83SXhFbyt0bEtTWGQwTlRhZmJLSnVPdzJ5VEg5aDl5dlprcjZJbXZmUnJUTDF0SEVYRWVYUStOL1RPdWE3Uzk3ZlU1bTcwcS8rdlJEdnJweXAwTkoyTmRDVmordzY2bEwzSDJKTHExcExSbDY0WkdFWmN0TE1uc3NQb05zdW4rVENyYlJGTlhrd3ErUlBmOWtWRjNla2hqK3cyTit4SzF0V2pTM01qUm5Ybmwvb2JpcnRtNk1OZmtjRTgydGJqMTFsajA2YVNHUyt3KzU5aWYyMGxvOVpkaGRoOHh5Y1JPL2FuK3hjSXk5c0M0azROWnk2MWxHL05ldzlrYmovQVBuU1RTSGo0RytWbHBQOERWUFlsenJvVWhqWTZOaWg4MGtocGJseFMrVHl4TGY3aTRjbTZiV2pKTkZudXlYQmJ2RkRqSm1NcXh4S1RNc2Q1RXlvWkc5SzFhT2JselcvVVhYN2pRdWVWV0p4bmJ0YU1taGU0NU00a242amFGSmJMY2I5QzQ3SDBvT3ZWc3VFbllwTERUcGpUMWVyb1hBYXhGT24zRktLK0NYRXJEd3kvd0RCYXV1eTFtSzl5bHNSVWZVeVVuaGloRE43RDRVMVRXK1Q2a096S2dzbjA1UE83TFYva3ZSNnNmTzZWRktXTzV4TFhsZnFMTnJvc2ppNHVsaXEvd0FpYUtKa2I2bjZma3FZNHhlRGNzY3JvcTdQSzlodXpNaW02UDhBUnl4dSsybDZZMHk2Vi9BK0h3bmthYWFyYjNMNmpKUGg3bHpwVVdxSHNQMzhGRkVucGRubE9aTlgzTnpmd05UOHg5Vkt2U2pBaGpsRjBVNUdKRFRsNGNhU3JXNDRaZkwzSHlvOHl5VnIvOWs9IiBpZD0iZiIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiLz48L2RlZnM+PC9zdmc+", + "description": "Displays changes to efficiency data over time visualized with color ranges and background.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.rangeChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.rangeChartWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: false,\n defaultDataKeysFunction: function() {\n return [{ name: 'efficiency', label: 'Efficiency', type: 'timeseries' }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-range-chart-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-range-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Efficiency\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 30;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":2592000000,\"interval\":86400000,\"fixedTimewindow\":{\"startTimeMs\":1700577227898,\"endTimeMs\":1700663627898},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"dataZoom\":true,\"rangeColors\":[{\"from\":0,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":60,\"color\":\"#F89E0D\"},{\"from\":60,\"to\":80,\"color\":\"#3B911C\"},{\"from\":80,\"to\":100,\"color\":\"#2B54CE\"}],\"outOfRangeColor\":\"#ccc\",\"fillArea\":true,\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm\",\"lastUpdateAgo\":false,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:ZWZmaWNpZW5jeV9yYW5nZV9jaGFydF9iYWNrZ3JvdW5kXygxKS5wbmc=:ZWZmaWNpZW5jeV9yYW5nZV9jaGFydF9iYWNrZ3JvdW5kLnBuZw==;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Efficiency\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"%\",\"decimals\":0,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "productivity", + "effectiveness", + "performance", + "capability" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/entities_hierarchy.json b/application/src/main/data/json/system/widget_types/entities_hierarchy.json new file mode 100644 index 0000000000..48e9c3e632 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/entities_hierarchy.json @@ -0,0 +1,27 @@ +{ + "fqn": "cards.entities_hierarchy", + "name": "Entities hierarchy", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAr9SURBVHja7d17VFTHGQDwbx+siDxUFMEgKCm2JsZgFmNUVDAoRzERQfRgfTTa2Go18XlQo1WDmlVaS6I2XWuJgHIEHynVoMYiGuN7j5zjM0qQtw9wBURQll2+zty5CwtiT633biuZ+eNymbv38WNm7t35mNkFrC8rKnjJU1GZCaG+uMqML3kyVxXXQ1kVtoFUVQZF5rYAMRdBAbaJVMAhHMIhHMIhHMIhHMIhdoXc4BAO4RAOsU33/rI6UaJ73s4zEkNuNKUWfeKcuy13/cqxW7CPw7bWjro16/mu4ir0kxryjGJ5FAialOZ7nlHGWtA8R3WllaN6L3y+q1jsBQb7QLYBwCtoWffe/sas8b4msnzoEIffJZCVXN0DxOJ18zY9QIPObaiuAvHU0rk76xFv60qT568pbUibt/I63fH6yiXpJHtf2sl559ihTN02aufYE7IbwKHEmuXKTn3qMq7oTH5mQC4aOrwzw9ezKDO6fe/ou7hKETbRedhjPAe9Qie6+ET3ifF1JEdN07wb4xTRgBGejv32sENlqEoSOj22T9UaAJqduJVwrlpzYL11YyNkal8LGt0+YVXrnOILxBzNGgLZiJgNg+uxwmUFlrsuQzwOhzHC5Zr1AONC8Z46VWrIEpoam31TY7+HWB3musSaYYQ/PQWZ437Y3NhG5ruT+oOTBhDIScTHVINvTsNERTlZ6bEGI4ZZ97/rkIQYHmqXEsGiM1drm2U8ht8/BTFGa7r+5qwIGf823bbajUHqBfdbU3AFCGmmDSRefdxgWKUskB+SP6cHQEhZQ86XuuQia2aPSeyGk9IEQaxIDVfsZZBfC3fUBV1bQDZozhtIyreBvMZsq2WHXHElp3E2XtIK55tUyXLnOd4RngCJGK8hFSqdQOZmkJx+EwhkAeLnmmLEhn5jW0C+ge9peWIT5AxpMCR90NMiN2Qtvf4gHMP+cEJlp891r8Dvbx99w6+aNOS4kmO9CSTc51RVlmscYkBQ0ZMH3YMuF3+s/K4FxPTGq8cqD3fPaoLM8hIaVjb8U+4n+yp6+cEYLEJWidm5I5WgmUSr2gIldP2QQMrHq8BhZh1ikhou49WBAL7kudMcgiVhSnBebmmE1LgtEH5afH8p93utvKMkGdBwlKW8xg2VhfVspbZUzKnOf8K23BEKraShtXM+yK/j7345hEM4hEN+epAbHMIhHMIhTelbvX5HVk1rW2oT7rW6R5X+Jn2bqadbc/UVVXpr732P/j7bUX+avnXV03SenmLDH85KCHkSqgCbFCxmR2r8fJRum1qLwin3t3rceuflZLkbtpLlSqe6PFAIUSMsVEGOsLIE5pOlXtGJpLVoGt1+1GBFrHSQUxATa5P0Vgjp4T7a4rChlQM8ekYhjhpOFr9SjCPLd0dgHihZB2WlkkEM7RwpZJW/2MN3vIQYp/hBMkg2ZLf2qkihq77SkYTrMGvRquOkl0ULolRXeF9HejINBxYuFfYr/Wx+olgD45xM2OAZ5mJCs/NqAhnhTmNc9a+EChDTmx97U8iHIezV4aNoFYT9ckDm0rrlessGkgvJiIs00eGKeFzuQbqy8S411+EbxCntIserSOTrdEfttK79meQEnMWLYFCfIMtjBJKo3kly/65IESBrfasFSPjkQ9tPWrv1WXBdDkiwT2xsjPgrg9SrV2OW4iDiunb3r5Grw0HTkUK+hiOIOxR5lp9FWTDfKYFFlhw34fpf4JDluEVTSyD/GEd7wWNGZlPIdcdMFCD9FW6dYDjrW9YFjnmRNtK8Q28LCW76lUHQZQnO6CMcPwsDZ2OJ4lsBMqUvvXDdzdNCLGVkDNt/aBQOXYRxgTh5MLk7QXomXMMC5b7DBGIJIj15AZL8NzMmilHB37kXoMQlki2EJZ6CVJJYSwi7nX2FCZ7mzd3NAiQknO28k20bwn77xLNCnYUXlGW9llJIiqXXfFzhZcogkC1dykQII48S2nu74yg1ZKuqY8+apyHHSGzn/UAaiTOU41318RASsaOQqBFi2Boy6Daxoh+BTa4mtHgkQCaFJOH6ztXdV5B6mFPj4q7VajUeWjEuOC2ALFLU+1ByiPfXdYHbn4I8CfI347JOFSwUh2Mmqi4yyCfupH0boy/mwXZxG00P1X1IVA+nvq6qYpC7mgmqQgqpS6fJfUx69WWg/4PQRhK25q8oC0TbHOJvyN7SvzN5At9yGXnlwVYPUjVS4TVkkLwOkwsKIj0fYoT7PuP5178UjzWAxCsRd4EWGQQnwlikELaZVq2GwIFXyj6FQ3jBeTItzEsSPhBn6XSFtGrVBvvpdLOsEAB1z9n5wiv6AnTfRWNwLutFCGb7Awy4TAphmgNoPrA+IhcpaRysXHgSCpAsONACgoVhAB3jET9izctHMkjdKCVAttDYbZ8jzdLt4qfjuSXiPyOr82qf833hoxL+7pdDOIRDOIRDOIRDOOSnGqAjoTcacrEkpWKBvkzMSxPCccLqnRPWF2bp5YBIFqDDNGHI0UcOR0jH8YKY19WJhuPIWfaOVU8U83LbgxwQ6QJ0AkSnSMYmSJ0ija2Md57eU4Q0hHSQBSJdgI5CkhSfoQ2kAE6xlbOP8G0Rsq1LrNyQFwzQEcghh7loCzkNx3fsucPWRUipW0q83JAXDNClwQ7ndyzNIHvBwUvV/qAtZFwYSgWRK0CXBu06O5xuBvlxTT7eecv7cRMkrUO+VBCUK0CXBsOMAT3KbSFiuOt8I8TYjdwI5YRIEaBLg0uY6zba0hJyAw42QhZBgFbrDdoNckGkCNAJt999irU2kIj3yGI/XGuEnKOBuqmQflFGyAsH6NgDcYHqGIGk0IIqxP1K/f1zvYMabBo7ylW1pAvQMYhpkOftDNZ0fou4uRMoh+WjPSAyB+gaip8vfscH1XAIh3AIh3AIh3AIh/x/QiSa4vqEROISD9+VFvK/mOJqBHc/vw6Of5QW8oxikXOKqxE+J52Cucof7QERp7jGjW4aziLZFFcBgjmwz36QXaT31zh9T7IprgzyZ8Ulu1QtYYrrF7JMcTVCaGxslFO81G3k30xxrQp1WdRUs6Wa4mqEgOjo4c6LLfYoETmnuLKqdUqZKD9E3imuDII9pssOkXmKK4NUOS2THSLzFFcjLDZc2B/kdF32J7u8U1wJhCSPsLPSv9dqmV7aKa4vZeIQDuEQDmmzkBscwiEcwiFN6b8aQafXJ5+gHdCbev22AzaTK2p3rdlmjaf9oC+RByLlCDpvPx+NJxnxlwQ9/NqrYqwfyV/k5/X+z93Y5+yZ+sFReSASj6C7P/BVCslD8wnfoeJn8kf1qUZzyCBhPa6DXBBpR9DRYQ1VAoRMzlWI86eX7yaLdU509ZpjvB0gLz6Cju7oZREh2DfS5jxRdMaeZcj0HDtAXngE3czYhUHdjqEVEhFgPXLm5kh32gfe7HFfQoh8I+jCoiMDPFIbIVP9MYXcyGhZ93MKIRdY6JKK0kGwtRKRZgQdrVpx6jwrJHgITgoNncA6+MNIHHZ0OMoNkWYEHYXcgBQRYuoy1+Y86XArE/y02j7gHyUjRLIRdJhJ7AyyTsXGyTV0op+hkKioLKDD5zbCykPyQiQYQbfHcGa7d9/HBJJxNnmywhqYn9XxYMUZf7E2yla1pBxBB6DqOaOUNnaAbuGNF1wzWwPqmHKZIXaZ4lpf9J/G8nifnUM4hEM4hEM4hEM4hEM4hEM4hEM4hENkhrSZLwhuG1/ZXFkGprbxJdpmaBtfa27GfwEB0j8MtCzTjwAAAABJRU5ErkJggg==", + "description": "Displays the hierarchy of entities based on their relations. The root of the hierarchy is defined using entity alias. By default, displays entities related using \"Contains\" relation. You may change the behavior using advanced settings.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.entitiesHierarchyWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'nodeSelected': {\n name: 'widget-action.node-selected',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-entities-hierarchy-widget-settings", + "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"nodeRelationQueryFunction\":\"var entity = nodeCtx.entity;\\nvar query = {\\n parameters: {\\n rootId: entity.id.id,\\n rootType: entity.id.entityType,\\n direction: \\\"FROM\\\",\\n maxLevel: 1\\n },\\n filters: [{\\n relationType: \\\"Contains\\\",\\n entityTypes: []\\n }]\\n};\\nreturn query;\\n\\n/**\\n\\n// Function should return relations query object for current node used to fetch entity children.\\n// Function can return 'default' string value. In this case default relations query will be used.\\n\\n// The following example code will construct simple relations query that will fetch relations of type 'Contains'\\n// from the current entity.\\n\\nvar entity = nodeCtx.entity;\\nvar query = {\\n parameters: {\\n rootId: entity.id.id,\\n rootType: entity.id.entityType,\\n direction: \\\"FROM\\\",\\n maxLevel: 1\\n },\\n filters: [{\\n relationType: \\\"Contains\\\",\\n entityTypes: []\\n }]\\n};\\nreturn query;\\n\\n**/\\n\",\"nodeHasChildrenFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node has children (whether it can be expanded).\\n\\n// The following example code will restrict entities hierarchy expansion up to third level.\\n\\nreturn nodeCtx.level <= 2;\\n\\n// The next example code will restrict entities expansion according to the value of example 'nodeHasChildren' attribute.\\n\\nvar data = nodeCtx.data;\\nif (data.hasOwnProperty('nodeHasChildren') && data['nodeHasChildren'] !== null) {\\n return data['nodeHasChildren'] === 'true';\\n} else {\\n return true;\\n}\\n \\n**/\\n \",\"nodeOpenedFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node should be opened (expanded) when it first loaded.\\n\\n// The following example code will open by default nodes up to third level.\\n\\nreturn nodeCtx.level <= 2;\\n\\n**/\\n \",\"nodeDisabledFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node should be disabled (not selectable).\\n\\n// The following example code will disable current node according to the value of example 'nodeDisabled' attribute.\\n\\nvar data = nodeCtx.data;\\nif (data.hasOwnProperty('nodeDisabled') && data['nodeDisabled'] !== null) {\\n return data['nodeDisabled'] === 'true';\\n} else {\\n return false;\\n}\\n \\n**/\\n\",\"nodeIconFunction\":\"/** \\n\\n// Function should return node icon info object.\\n// Resulting object should contain either 'materialIcon' or 'iconUrl' property. \\n// Where:\\n - 'materialIcon' - name of the material icon to be used from the Material Icons Library (https://material.io/tools/icons);\\n - 'iconUrl' - url of the external image to be used as node icon.\\n// Function can return 'default' string value. In this case default icons according to entity type will be used.\\n\\n// The following example code shows how to use external image for devices which name starts with 'Test' and use \\n// default icons for the rest of entities.\\n\\nvar entity = nodeCtx.entity;\\nif (entity.id.entityType === 'DEVICE' && entity.name.startsWith('Test')) {\\n return {iconUrl: 'https://avatars1.githubusercontent.com/u/14793288?v=4&s=117'};\\n} else {\\n return 'default';\\n}\\n \\n**/\",\"nodeTextFunction\":\"/**\\n\\n// Function should return text (can be HTML code) for the current node.\\n\\n// The following example code will generate node text consisting of entity name and temperature if temperature value is present in entity attributes/timeseries.\\n\\nvar data = nodeCtx.data;\\nvar entity = nodeCtx.entity;\\nvar text = entity.name;\\nif (data.hasOwnProperty('temperature') && data['temperature'] !== null) {\\n text += \\\" \\\"+ data['temperature'] +\\\" °C\\\";\\n}\\nreturn text;\\n\\n**/\",\"nodesSortFunction\":\"/**\\n\\n// This function is used to sort nodes of the same level. Function should compare two nodes and return \\n// integer value: \\n// - less than 0 - sort nodeCtx1 to an index lower than nodeCtx2\\n// - 0 - leave nodeCtx1 and nodeCtx2 unchanged with respect to each other\\n// - greater than 0 - sort nodeCtx2 to an index lower than nodeCtx1\\n\\n// The following example code will sort entities first by entity type in alphabetical order then\\n// by entity name in alphabetical order.\\n\\nvar result = nodeCtx1.entity.id.entityType.localeCompare(nodeCtx2.entity.id.entityType);\\nif (result === 0) {\\n result = nodeCtx1.entity.name.localeCompare(nodeCtx2.entity.name);\\n}\\nreturn result;\\n \\n**/\"},\"title\":\"Entities hierarchy\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#4caf50\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.8926244886945558,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"widgetStyle\":{},\"actions\":{}}" + }, + "externalId": null, + "tags": [ + "administration", + "management", + "organization", + "structure" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/entities_table.json b/application/src/main/data/json/system/widget_types/entities_table.json new file mode 100644 index 0000000000..1ebf8d4fde --- /dev/null +++ b/application/src/main/data/json/system/widget_types/entities_table.json @@ -0,0 +1,28 @@ +{ + "fqn": "cards.entities_table", + "name": "Entities table", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a list of entities that match the selected alias and optional filter. Provides the ability for full-text search and pagination, except for columns with post-processing or cell content functions. Highly customizable using widget styles, data source keys, and widget actions.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.entitiesTableWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n hasDataPageLink: true,\n warnOnPageDataOverflow: false,\n dataKeysOptional: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'name', type: 'entityField' }];\n }\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true,\n hasShowCondition: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n },\n 'rowDoubleClick': {\n name: 'widget-action.row-double-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-entities-table-widget-settings", + "dataKeySettingsDirective": "tb-entities-table-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-entities-table-basic-config", + "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSearch\":true,\"enableSelectColumnDisplay\":true,\"enableStickyHeader\":true,\"enableStickyAction\":true,\"reserveSpaceForHiddenAction\":\"true\",\"displayEntityName\":false,\"displayEntityLabel\":false,\"displayEntityType\":false,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"name\",\"useRowStyleFunction\":false,\"entitiesTitle\":\"Entities\"},\"title\":\"Entities table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Entity name\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return 'Simulated';\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Entity type\",\"color\":\"#607d8b\",\"settings\":{},\"_hash\":0.782057645776538,\"funcBody\":\"return 'Device';\",\"decimals\":null,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.904797781901171,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\",\"decimals\":0},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.1961430898042078,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\",\"decimals\":0},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.7678057538205878,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"decimals\":2}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"displayTimewindow\":false,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"list\",\"iconColor\":null}" + }, + "externalId": null, + "tags": [ + "administration", + "management" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/entity_count.json b/application/src/main/data/json/system/widget_types/entity_count.json new file mode 100644 index 0000000000..cf3ad82e5c --- /dev/null +++ b/application/src/main/data/json/system/widget_types/entity_count.json @@ -0,0 +1,27 @@ +{ + "fqn": "entity_count", + "name": "Entity count", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the number of entities based on the selected alias and optional filter. The number of entities refreshes periodically and not in real-time.", + "descriptor": { + "type": "latest", + "sizeX": 3.5, + "sizeY": 1.5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.countWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.countWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '220px',\n previewHeight: '100px',\n embedTitlePanel: true,\n hideDataSettings: true\n };\n};\n\nself.actionSources = function() {\n return {\n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-entity-count-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-entity-count-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"count\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"return 150;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"],\"assignedToCurrentUser\":false,\"assigneeId\":null}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"showLabel\":true,\"label\":\"Devices\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.54)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":20,\"iconSizeUnit\":\"px\",\"icon\":\"devices\",\"iconColor\":{\"type\":\"constant\",\"color\":\"rgba(255, 255, 255, 1)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIconBackground\":true,\"iconBackgroundSize\":36,\"iconBackgroundSizeUnit\":\"px\",\"iconBackgroundColor\":{\"type\":\"constant\",\"color\":\"rgb(241, 141, 23)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":20,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"24px\"},\"valueColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showChevron\":false,\"chevronSize\":24,\"chevronSizeUnit\":\"px\",\"chevronColor\":\"rgba(0, 0, 0, 0.38)\",\"layout\":\"column\"},\"title\":\"Entity count\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"\",\"decimals\":null,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.54)\"}" + }, + "externalId": null, + "tags": [ + "total", + "tally" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/flooding_level_card.json b/application/src/main/data/json/system/widget_types/flooding_level_card.json new file mode 100644 index 0000000000..f31aceeece --- /dev/null +++ b/application/src/main/data/json/system/widget_types/flooding_level_card.json @@ -0,0 +1,37 @@ +{ + "fqn": "flooding_level_card", + "name": "Flooding level card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest flooding level telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'flooding', label: 'Flooding level', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flooding level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 2 - 1;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 5) {\\n\\tvalue = 5;\\n}\\nreturn value;\\n\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"flood\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"#234CC7\"},{\"from\":1,\"to\":3,\"color\":\"#F36900\"},{\"from\":3,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"#234CC7\"},{\"from\":1,\"to\":3,\"color\":\"#F36900\"},{\"from\":3,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Flooding level card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"m\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "flood", + "flooding", + "water height", + "flood depth", + "flood stage", + "inundation level", + "water rise", + "overflow level", + "flood peak", + "high water mark" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/flooding_level_card_with_background.json b/application/src/main/data/json/system/widget_types/flooding_level_card_with_background.json new file mode 100644 index 0000000000..0fc557f863 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/flooding_level_card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "flooding_level_card_with_background", + "name": "Flooding level card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest flooding level telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'flooding', label: 'Flooding level', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flooding level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 2 - 1;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 5) {\\n\\tvalue = 5;\\n}\\nreturn value;\\n\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"flood\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"#224AC2\"},{\"from\":1,\"to\":3,\"color\":\"#F77410\"},{\"from\":3,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"#224AC2\"},{\"from\":1,\"to\":3,\"color\":\"#F77410\"},{\"from\":3,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Flooding level card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"m\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "flood", + "flooding", + "water height", + "flood depth", + "flood stage", + "inundation level", + "water rise", + "overflow level", + "flood peak", + "high water mark" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/flooding_level_chart_card.json b/application/src/main/data/json/system/widget_types/flooding_level_chart_card.json new file mode 100644 index 0000000000..00804183c4 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/flooding_level_chart_card.json @@ -0,0 +1,32 @@ +{ + "fqn": "flooding_level_chart_card", + "name": "Flooding level chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAzFBMVEXg4ODf39/g4ODg4OAAAAD////g4OAjTMfs7Ozj4+PIyMjz8/PPz89YWFjn5+chISHCwsKsrKw9PT3x8fHa2tr5+fmQkJC2tra8vLx0dHQ7OzsvLy/V1dXk6fjU1NRaedW6urqCgoLI0vFKSko+Ys7b29tmZmby9Puesed1j9yenp4wV8uRpeODmt+tvOokJCSsvOpMbdJDQ0MZGRkRERHW3fViYmK6x+4/Ys4xV8pohNj39/ewsLChoaGsu+rm5uaDm+DAwMCTk5Nra2s4GVQ3AAAABXRSTlPvIL+vAC9A4IoAAAhzSURBVHja7Z2HetowFEbT8bvWsPHGKy6EMkKSrnTv8f7v1CsDJphVmmBM6sOwLNl8OlxdyR9JyMnjBw9PtGPn0YPHJw9OXBw9Lmk8ugceZELDCveCk/siojUiNaMRqRuNSN1oROpGI1I3Vol0X3/FIswtV2AJtqlmmQpEvr5pXSfvJHJSg2CGjUUc3jFKvW4t9btVpcmyiNZSXE/7yVuMMRj2LCpsEiCHw51Fis1F2EIMDyuivbi+fhNfv3FnIiBIJOrFAZjTc1J0EyPhtgHHMBzXdXq91kQkb+120Y2Y0XP4gUW+vnvzRrqt668zEcPokkgnBkuYESAw1Hs/EemgZ9sx0qlIFCGIuAOH2wPYxoFF8PPN9Tv5uvVucWgFA5UYDgenZyrmIlw1RGBTESNxnAESco6odGiRd69fvMnvXxaGFu+prg4CjA1XRaQQGRtFRLoRXIYgjihsVHHoiEhtci/lSNdJArC4l1BXHWcu4saOM8uR2HA6dEYKqkzGBxaJXr94kd+/YBG3mLVUcU7KOvHyekOlv6QuK7vbcxIbh+deX6IcJf+RiMBf4+tLL29iDVWLiHbfWqHyXccSnhASi5jcxzoqFvFM6Lqn+55u9b22noUZOOd+aFGbRaXQFyHXYVlctFPR5pk6h0tkoZLioaAC7QiUqVpEmvmTkGYGqaNthqINKTxVHcISmpfpCFURUkWkTfVt3fQtZHSI6UHXQ2FhJdVGRIP+PRfxIIXf7vf9QsSim+lJAQ7rpojeDwWnM3OR0OS1EPHbMhRmyCcikBkHiVBNIaJzKfNiJmcRyTgdSKVcRLZrIQIIpeMXYjdq19aJ0s56mnWkEak9/7OIX5oIUAu2iZghPdpQSL9Y7CdkUA1S9OvgslXEFrDa0KUHumXcJxGLC18jDQlq4KHQ/LxBygw7ULVIJgWJmH5bLXrC00hE8y0habdNt5Aq6Qhd87y+H2IHqhbxwoyGlkxzEXhePrTCQsSCVCIiv1SxsAOVi5hfVI812y9EpMb9UGtPImKGUxGTU9UOVCzi+xDQIUx6VlsfELqJeYWmqwPUcaZZ56H19/jSEvh76iuyG41II3IsNCJ1oxGpG41I3WhE6kYjUjcakbrRiKzlbHQ5OhuizOkNQNRdZHTxRHFxiRKvnsw5A1Fvkcuis+dY5OKoRF6qYFxdXj2j7Qg3GT45KpGn1Mu3KiHI5ONCnrxd6n+dRU6LITUq9/uMKm6d5KlLTzzdv8hL1f1iJF3hBs+pAv/A58+fxeephtFigBFxoGPvV+Rq/rZ/ePLkU2kW+Ih/wNV13UUOcx0Ge8AZ3HHAOvsUOSeR+Wz7rOT4dHh2eTl6i50wGWOmCgdnAIl04yB20Y2CvUZE5fqsWBL5RBGZzMBPd0oVrf/OUz20A56LRLa6w+6M2UFEaL/g4y5BSeW7rMhuNbQi1yGlIEBwGJELFYvR83O1wjwb4q+J7PjXAFO6LikYY5U66aEicvry/CzfKpP3u/zK5HXMsJrqRcoLyifsYBIxVC7yaasI8WHWUuOVfXH6fbU2bGpFqbXIjQXxgnL7eEWeF4l8SqWrUqyGhWIeqzqLDNUcW7rqQnEROSqS/Vxtz0ano5fYHc8yq7mMfzmdZJ/laldPf+TbC6o4zVumih/ORs/eX73/FxMTXHIZ7lPkbX4Ncnl+oYRmWXM5CQnxadJyDiWC03M8/4YdkZoSkbAgxR5FqL8zLovcuMJEacar4b+LENWI4IzmXeJiNN2l4ilyXk5bLoe4nYipQ0Mfmr9PEeL0/fPnb+d7Z8PVLaQ3xHBYz1lrF2o9/e5AI9KIHAuNSN1oROpGI1I3GpG6sSSScYljZElE9gWOkSURU3AfhBB1+GOK9VD/toggEyBCefDkYQF3sQ7J5WYRXfahsHBomNNLWokRdNLVIdksUnDwTGFOAKSdwIjJpttBGV8cx/SbkscUl48HcRJ1jnIdSZMAC/AoIZejE1EeS3SixIn4HYt0Ioa/gQ8GUTcY2+zfPZZd7lLEiR0bW2FGqxtEkWEkPfeWHgX2oEUudyUyjl077jFsxO22DDYtxoPtQTZmkMcmXOXC7kSEqXCwqBVgPW6QGHx+Rtzd+ppRMGVrrMnlbiISxVDYsbM2KLYTdxaHyxgb6Q2qv4xnLVYMnq6LFXAaHm7JrJViA0HM9iPi6uuJfxdFGcfjpXbS6PaXaqM409dityx9R26/stsLb17Qilg5xwcMKzCMzdcjVQ8t5owX93uOXc7xlbhxd0OCVC8SxMshMtismMR8w/pgr02Q6kVYq4MSxUzMtywB42RlhzstjupFomh13jhsaapapuu4qxNknyLcmDLoLMyiDMSqoKgc34Lbi1ZOAnsVYfaUKHbmE9P6dy/l2A5LxisTpKKhZRstwy4y/TYs5wOnin2I6HJSUapmk7CwxMYtCRx2Jwnim5tFOCworJVhMXq4NUaP32CHBNnpw4fiVwpCy1uR0HcxnN24dYN/fUUr3C5S70/m/jZHpNbGMbIkAv0oA1LXT1EakeOnEakb91ckwxzhl76iauEHDroHeKgLSyJteviagN8XvkVtxXcgwfQkTA2iLWCas+8Qmx2t6b6qrZTtItwMEWqWUCKTb9LSLF9qXOqyL/VQeDL1SESGuQjVgGvc9HiaoUq2i2TyCzTuQYqpiAVPs6jngktL7VphqiKUZkrEk206yzOpbKFKtouojmUIRS7CaSekIFnClJ4mLIQk6AslgtAC/NAP6RBpSl3USURK6fd5Cp336UEVqSQnmZJRavo81ZBqPs8VdQh1cp+rRm76dCZ2peLp18IWaHj9m0SzjjQidacRqRuNSN1oROpGI1I37pHICe4F2snD+/Evmx/dn3+i/fjBI+3YOXn44PEffvfx713DlFEAAAAASUVORK5CYII=", + "description": "Displays flooding level data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'flooding', label: 'Flooding level', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'm', decimals: 1 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'flooding', 'm', 1);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flooding level\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 2 - 1;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 5) {\\n\\tvalue = 5;\\n}\\nreturn value;\\n\",\"aggregationType\":null,\"units\":\"m\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"#234CC7\"},{\"from\":1,\"to\":3,\"color\":\"#F36900\"},{\"from\":3,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 2 - 1;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 5) {\\n\\tvalue = 5;\\n}\\nreturn value;\\n\",\"aggregationType\":null,\"units\":\"m\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 1 - 0.5;\\nif (value < -1.5) {\\n\\tvalue = -1.5;\\n} else if (value > 1.5) {\\n\\tvalue = 1.5;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Flooding level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"flood\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "flood", + "flooding" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/flooding_level_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/flooding_level_chart_card_with_background.json new file mode 100644 index 0000000000..4f6d71ce23 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/flooding_level_chart_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "flooding_level_chart_card_with_background", + "name": "Flooding level chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a flooding level data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'flooding', label: 'Flooding level', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'm', decimals: 1 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'flooding', 'm', 1);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flooding level\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 2 - 1;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 5) {\\n\\tvalue = 5;\\n}\\nreturn value;\\n\",\"aggregationType\":null,\"units\":\"m\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"#224AC2\"},{\"from\":1,\"to\":3,\"color\":\"#F77410\"},{\"from\":3,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 2 - 1;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 5) {\\n\\tvalue = 5;\\n}\\nreturn value;\\n\",\"aggregationType\":null,\"units\":\"m\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 1 - 0.5;\\nif (value < -1.5) {\\n\\tvalue = -1.5;\\n} else if (value > 1.5) {\\n\\tvalue = 1.5;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Flooding level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"flood\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "flood", + "flooding", + "water height", + "flood depth", + "flood stage", + "inundation level", + "water rise", + "overflow level", + "flood peak", + "high water mark" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/flooding_level_progress_bar.json b/application/src/main/data/json/system/widget_types/flooding_level_progress_bar.json new file mode 100644 index 0000000000..4bb5fbe32f --- /dev/null +++ b/application/src/main/data/json/system/widget_types/flooding_level_progress_bar.json @@ -0,0 +1,37 @@ +{ + "fqn": "flooding_level_progress_bar", + "name": "Flooding level progress bar", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAnFBMVEXg4ODf39/g4ODg4OAAAAD////g4OD19fUjTMchISGsrKw9PT3j4+PIyMh0dHSQkJDx8fEvLy+CgoLI0vGenp7k6PhaeNVYWFjy9Pu6urqtvOqRpeM+Ys7V1dUwV8vv7+9KSkqsu+qDmt/X3vS6x+5LbdF1j9yGhoZmZmb39/efseePj4+5ubmpqamXl5dohNkxV8rCwsLj6fjk6fgljp4SAAAABXRSTlPvIL+vAC9A4IoAAARWSURBVHja7M9JEQAwCAPAUI43DvDvsjYCk3WwCHuo7dwChsn1GgbvPGAeKk/AlUgpQkYRNoqwUYSNImwUYaMIG0U+u2WvKjkMQ+HqFEeWfzGYFIaUKfL+z7eRcwMhy+xslaS4H8ygWFKsD2zI2/gVeRu/Im/jHyKrDBSO+IjlfFB8IFLwmUB/h4jjQL6KLFxfLuJ1I30V0Y6Xi+gRYENCmWD4EkT3oKjl1qjosdvjWA5Faz2LWAe2EuuXuHdOD4mkmTkz2GwWOAWEdCT3eYRbAXO3/FaQ3UlkdMxI2w9qf8EW4o0iVUT8LlJs44UCpUuIbLbs0fMhYstWpCZZeRLRPVXRqBBWCBeg0d972dsuMjMBngsKZeSSPVhwiERgYkBlBXAWGR3KZaQWKho7zOjmy27BMRndz+aNOrFcRTyDjX4RCRw0pNyQm/UM4iN3JGdgnPLA9RAJ/ytSZGOyrpVynFnpj4g021PG0YmAZgdlA5CvIlaDdBapLAAU1p+pVj8B6HhExNPVamMkxyIzo7kFWXgRsXyQdhYZKzELkPKQ78xVAvsjIhBHOttXZzLXn6DNVxGoI8NZBNpIlgSrFGx4N15xl8gVVewkTUeAv0nQpJwxuHYA5853f/220n1jxBfeLxK5EfCN94v8YYcOUgCCwgAIr/56iZWUkA3W7n87CsUGy3k13w1moqyKMj5lEPKLIYbkwhAaQ2gMoTGExhAaQ2gMoTGExhAaQ2gMoTGExhAaQ2gMoTGE5iNkqusmHppLGyivIfOYdusjZUmnPlDeQrp0GO7zxwxDNvbroMVBGAjD8Gnwuw+GIYZAownUS5f2//+3TVdddNl1m5MjzXOp0Etfpk7UwTNJt/3NwM1+6UmVnZABSNMMPujbXd0o/g8JAM+fPS0YGEijnRADtJSNwGU9Jpa2JXX2Q+jJrkM6IHrAqft/lYYEzAzpUhpiox9FrAMsqVIYsuB5JLYdu4G46w6/a/ZD5K+QZSfDhQQTO+eEDrQfcpu3VvfbxnUAZc9vQo6Qw5fyC+dIWp0jj5TsZiKcQ83z4kIH2g9hIFDm4GghcwAD5jQh5ICRJEw9g3ftNJ5bS+wAe54QXj/9mukhSxwmgc4TQhwB+CSUjfA9ZffkAfiRzhRCJNzLcik06/nwU+N939nPo4ZoU0O0qSHa1BBtaog2NUSbNwh5GKzFvlGhPCRiyzQqXItD8FOjQ3GIwVZsVCifyD1izei4R65vvLVOpoZoU0M+27t7FIZhGAzDzs/nQEihlCwejEZhjA2+/+XqExS8fXH1nkAPGjSKLYOw9QPScg1gqqlqG4eICirVukoUSeOQeAOBaiX1EmAc8ipskKxZZQbIBZznDJCer+MQ+Ry4I3g6+kCtjEMQNFcwFTRnwTgEKYEs+e/L/qwMwpZB2DIIWwZhyyBsGYQtg7BlELYMwpZB2JoI4jBF3q0TvJ4G3ptb3ATPp6Uz9mXzT8+ty/4FsaeuueShPysAAAAASUVORK5CYII=", + "description": "Displays flooding level reading as a horizontal progress bar. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'flooding', label: 'Flooding level', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flooding level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 2 - 1;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 5) {\\n\\tvalue = 5;\\n}\\nreturn value;\\n\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"#234CC7\"},{\"from\":1,\"to\":3,\"color\":\"#F36900\"},{\"from\":3,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":5,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"#234CC7\"},{\"from\":1,\"to\":3,\"color\":\"#F36900\"},{\"from\":3,\"to\":null,\"color\":\"#D81838\"}]},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Flooding level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"m\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"flood\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "flood", + "flooding", + "water height", + "flood depth", + "flood stage", + "inundation level", + "water rise", + "overflow level", + "flood peak", + "high water mark" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/flooding_level_progress_bar_with_background.json b/application/src/main/data/json/system/widget_types/flooding_level_progress_bar_with_background.json new file mode 100644 index 0000000000..b5f7c0b41b --- /dev/null +++ b/application/src/main/data/json/system/widget_types/flooding_level_progress_bar_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "flooding_level_progress_bar_with_background", + "name": "Flooding level progress bar with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays flooding level reading as a horizontal progress bar with the background image. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'flooding', label: 'Flooding level', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flooding level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 2 - 1;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 5) {\\n\\tvalue = 5;\\n}\\nreturn value;\\n\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"#224AC2\"},{\"from\":1,\"to\":3,\"color\":\"#F77410\"},{\"from\":3,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":5,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"#224AC2\"},{\"from\":1,\"to\":3,\"color\":\"#F77410\"},{\"from\":3,\"to\":null,\"color\":\"#DE2343\"}]},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Flooding level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"m\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"flood\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "flood", + "flooding", + "water height", + "flood depth", + "flood stage", + "inundation level", + "water rise", + "overflow level", + "flood peak", + "high water mark" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/flow_rate_card.json b/application/src/main/data/json/system/widget_types/flow_rate_card.json new file mode 100644 index 0000000000..058fe8de9a --- /dev/null +++ b/application/src/main/data/json/system/widget_types/flow_rate_card.json @@ -0,0 +1,32 @@ +{ + "fqn": "flow_rate_card", + "name": "Flow rate card", + "deprecated": false, + "image": "tb-image:Zmxvd19yYXRlX2NhcmQuc3Zn:Zmxvd19yYXRlX2NhcmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxwYXRoIGZpbGw9IiMzRkE3MUEiIGQ9Ik00MS4yIDIzLjhhMi42IDIuNiAwIDAgMC0yLjUgMS44bC0xIDMuM2E0IDQgMCAwIDAtMS40LjRsLTIuNC0yLjYtMS44LTJhMi42IDIuNiAwIDAgMC0uMSAzLjhsMi4zIDIuNWE0IDQgMCAwIDAtLjQgMS40bC0zLjQuNy0yLjYuNmEyLjYgMi42IDAgMCAwIDMuMiAybDMuNC0uOGMuMi40LjYuOCAxIDEuMWwtMS4xIDMuMy0uOCAyLjVhMi42IDIuNiAwIDAgMCAzLjMtMS43bDEtMy4zYy41IDAgMS0uMSAxLjQtLjNsMi40IDIuNiAxLjggMS45YTIuNiAyLjYgMCAwIDAgLjEtMy43bC0yLjMtMi42LjItLjcuMi0uNyAzLjQtLjcgMi42LS42YTIuNiAyLjYgMCAwIDAtMy4yLTJsLTMuNC44YTQgNCAwIDAgMC0xLTFsMS4xLTMuNC44LTIuNS0uOC0uMVpNMzggMzAuOWEyIDIgMCAwIDEgMS42IDEgMiAyIDAgMSAxLTEuNi0xTTI5LjMgMzdhOS4xIDkuMSAwIDAgMS01LjIgMS44aC0yLjd2Mi42aDIuN2MxLjggMCAzLjUtLjUgNS4yLTEuMyAxIC41IDIgLjggMy4xIDFsLjctMi4yLjItLjNhOS4yIDkuMiAwIDAgMS00LTEuNlptMTYuMyAxLjhhNCA0IDAgMCAxLS43IDIuNmgyLjd2LTIuNmgtMlptLTE2LjMgMy40YTkuMSA5LjEgMCAwIDEtNS4yIDEuOGgtMi43djIuNmgyLjdjMS44IDAgMy41LS41IDUuMi0xLjMgMy4zIDEuNyA3LjIgMS43IDEwLjUgMCAxLjYuOCAzLjQgMS4zIDUuMiAxLjNoMi42VjQ0SDQ1Yy0xLjggMC0zLjYtLjYtNS4yLTEuOGE5IDkgMCAwIDEtMTAuNSAwWiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik01OSAyNHY4LjhoLTEuNlYyNEg1OVptMy42IDMuOFYyOWgtNHYtMS4yaDRaTTYzIDI0djEuMmgtNC41VjI0SDYzWm0yLjktLjV2OS40aC0xLjV2LTkuNEg2NlptMS41IDYuMnYtLjJjMC0uNCAwLS45LjItMS4zLjEtLjQuMy0uOC42LTEgLjMtLjQuNi0uNiAxLS44bDEuMy0uMmMuNSAwIC45IDAgMS4zLjJsMSAuNy41IDEuMWMuMi40LjMuOS4zIDEuM3YuMmMwIC41LS4xIDEtLjMgMS4zIDAgLjQtLjMuOC0uNiAxYTIuOCAyLjggMCAwIDEtMi4yIDFjLS41IDAtMSAwLTEuMy0uMmwtMS0uN2MtLjMtLjMtLjUtLjctLjYtMWwtLjItMS40Wm0xLjUtLjJ2MWwuMy43LjUuNS44LjJhMS40IDEuNCAwIDAgMCAxLjItLjdsLjMtLjd2LTEuOGMwLS4zLS4yLS41LS4zLS43YTEuNCAxLjQgMCAwIDAtMS4yLS42bC0uOC4xLS41LjUtLjIuNy0uMS44Wm03LjkgMiAxLjUtNS4yaDFsLS4zIDEuNS0xLjUgNS4xaC0uOWwuMi0xLjRabS0xLTUuMiAxLjMgNS4ydjEuNGgtLjlsLTEuOC02LjZINzZabTQuOSA1LjEgMS4yLTUuMWgxLjRsLTEuOCA2LjZoLTFsLjItMS41Wm0tMS4zLTUuMSAxLjUgNS4xLjIgMS41aC0uOGwtMS42LTUtLjItMS42aDFabTEwIDEuM3Y1LjNoLTEuNnYtNi42aDEuNGwuMSAxLjNabTItMS4zdjEuM2EyLjkgMi45IDAgMCAwLTEuMyAwIDEuMyAxLjMgMCAwIDAtLjguOWwtLjEuNmgtLjRMODkgMjhsLjQtMSAuNi0uNmExLjcgMS43IDAgMCAxIDEuMi0uMmguMlptNC42IDUuM3YtMy4ybC0uMS0uNi0uNC0uNC0uNy0uMS0uNi4xYTEgMSAwIDAgMC0uNC4zbC0uMi41aC0xLjRsLjEtLjcuNi0uNy45LS41IDEtLjFjLjYgMCAxIDAgMS40LjIuNC4yLjcuNCAxIC44LjIuMy4zLjcuMyAxLjNWMzJsLjIuNnYuMWgtMS41bC0uMi0uNnYtLjdabS4yLTIuN3YxaC0xYTMgMyAwIDAgMC0uOCAwbC0uNS4yYTEgMSAwIDAgMC0uNC44bC4xLjVjMCAuMi4yLjMuNC40aC41YTEuNiAxLjYgMCAwIDAgMS40LS42bC4yLS41LjUuNy0uMy41LS41LjZhMi4zIDIuMyAwIDAgMS0xLjUuNmMtLjUgMC0uOS0uMS0xLjItLjNhMiAyIDAgMCAxLS44LS43Yy0uMi0uMy0uMy0uNi0uMy0xczAtLjcuMi0xYy4xLS4yLjMtLjUuNi0uNmwxLS41IDEuMi0uMWgxLjJabTYtMi42djFoLTMuN3YtMWgzLjdabS0yLjYtMS42aDEuNXY2LjlsLjMuMmguNGEyLjMgMi4zIDAgMCAwIC41IDB2MS4xYTMuOCAzLjggMCAwIDEtMSAuMmwtLjktLjItLjYtLjYtLjItMS4xdi02LjVabTcgOC40Yy0uNiAwLTEtLjEtMS40LS4zYTMgMyAwIDAgMS0xLjctMS43bC0uMi0xLjN2LS4yYzAtLjUgMC0xIC4yLTEuNC4yLS40LjQtLjguNy0xIC4yLS40LjYtLjYgMS0uOGEzIDMgMCAwIDEgMS4xLS4yYy41IDAgMSAwIDEuMy4ybC45LjcuNSAxIC4yIDEuM3YuN0gxMDR2LTEuMWgzLjd2LS4xYzAtLjMgMC0uNS0uMi0uOCAwLS4yLS4yLS40LS40LS41LS4yLS4yLS41LS4yLS44LS4ybC0uNi4xLS41LjUtLjMuNy0uMS45di4ybC4xLjkuNC42LjUuNC44LjJhMiAyIDAgMCAwIDEuNy0uOGwuNy43YTIuOCAyLjggMCAwIDEtMS40IDFjLS4zLjItLjcuMy0xIC4zWiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjQiIGQ9Ik02MS40IDQ2di44SDU4VjQ2aDMuNFptLTMtNS45djYuN2gtMS4ydi02LjdoMS4xWm02LjggNS43di0yLjNMNjUgNDNsLS4zLS4zYTEgMSAwIDAgMC0uNS0uMWgtLjVsLS4zLjMtLjEuNGgtMS4xbC4xLS42LjQtLjUuNy0uMy44LS4xYy40IDAgLjcgMCAxIC4yLjMgMCAuNi4zLjcuNS4yLjMuMy42LjMgMVY0Ni4zbC4yLjVoLTEuMmwtLjEtLjR2LS42Wm0uMS0ydi43SDY0bC0uNC4yYS43LjcgMCAwIDAtLjMuNnYuNGwuMy4zaC41YTEuMiAxLjIgMCAwIDAgMS0uNGwuMS0uNC40LjUtLjIuNC0uMy40YTEuNyAxLjcgMCAwIDEtMS4yLjRjLS40IDAtLjcgMC0xLS4ybC0uNS0uNWMtLjItLjItLjItLjUtLjItLjhsLjEtLjcuNS0uNS43LS4zaDEuOFptNS4xIDEuN3YtLjNsLS40LS4zYTMgMyAwIDAgMC0uNy0uMmMtLjIgMC0uNSAwLS43LS4ybC0uNi0uMy0uMy0uNC0uMS0uNXYtLjZsLjUtLjUuNi0uMy44LS4xYy40IDAgLjcgMCAxIC4yLjMuMS42LjMuNy41LjIuMy4zLjUuMy44aC0xLjFsLS4xLS4zLS4zLS4zYTEgMSAwIDAgMC0uNS0uMSAxIDEgMCAwIDAtLjUgMGwtLjMuM2EuNi42IDAgMCAwIDAgLjVsLjEuMi40LjIuNS4xIDEgLjMuNi41LjIuN2ExLjMgMS4zIDAgMCAxLS41IDEuMSAyIDIgMCAwIDEtLjcuM2wtLjguMWMtLjUgMC0uOCAwLTEuMi0uMmwtLjctLjYtLjItLjhoMWwuMi41LjQuMi41LjFoLjVsLjMtLjMuMS0uM1ptNC41LTMuNnYuOGgtMi44di0uOEg3NVptLTItMS4zaDEuMnY1LjJsLjIuMmguM2ExLjcgMS43IDAgMCAwIC40IDB2LjhhMi44IDIuOCAwIDAgMS0uNy4xbC0uNy0uMWExIDEgMCAwIDEtLjUtLjVsLS4xLS44di00LjlabTguNiA1VjQyaDEuMXY1aC0xdi0xLjJabS4yLTFoLjRsLS4xIDEtLjQuNy0uNi41YTIgMiAwIDAgMS0uOC4xIDIgMiAwIDAgMS0uNyAwbC0uNS0uNC0uNC0uNnYtNGgxdjMuNmwuMy4zLjIuMmguM2MuMyAwIC42IDAgLjgtLjIuMiAwIC4zLS4yLjQtLjV2LS43Wm0zLjQtMS44djZIODR2LTdoMXYxWm0zLjIgMS41di4xbC0uMSAxYzAgLjMtLjIuNi0uNC44LS4xLjItLjMuNC0uNi41LS4yLjItLjUuMi0uOC4ybC0uOC0uMS0uNi0uNS0uNC0uOC0uMi0xdi0uM2wuMi0xIC40LS44YTEuNiAxLjYgMCAwIDEgMS40LS42Yy4zIDAgLjYgMCAuOC4yLjMgMCAuNS4yLjYuNWwuNC44LjEgMVptLTEgLjF2LS4xbC0uMS0uNi0uMi0uNmExIDEgMCAwIDAtMS0uNGgtLjRhMSAxIDAgMCAwLS40LjNsLS4yLjQtLjEuNXYxbC4yLjVjMCAuMi4yLjQuNC41bC42LjFoLjVsLjQtLjQuMi0uNnYtLjZabTUuMiAxLjR2LTZoMS4xdjdoLTF2LTFabS0zLjItMS40di0uMWwuMS0xYzAtLjMuMi0uNi40LS44YTEuNyAxLjcgMCAwIDEgMS40LS43bC44LjEuNi41LjQuOC4yIDF2LjNsLS4yIDEtLjQuOGExLjYgMS42IDAgMCAxLTEuNC42IDEuNyAxLjcgMCAwIDEtMS40LS43bC0uNC0uOC0uMS0xWm0xLS4xdi4xbC4xLjYuMi42LjQuMy41LjEuNi0uMS40LS41LjItLjZWNDRsLS4xLS42LS4zLS40YTEgMSAwIDAgMC0uMy0uMmwtLjUtLjFhMSAxIDAgMCAwLS41IDAgMSAxIDAgMCAwLS40LjRsLS4yLjZ2LjZabTcuNSAxLjV2LTIuM2wtLjEtLjUtLjMtLjNhMSAxIDAgMCAwLS41LS4xaC0uNWwtLjMuM3YuNGgtMS4yYzAtLjIgMC0uNC4yLS42IDAtLjIuMi0uMy40LS41bC42LS4zLjktLjFjLjMgMCAuNyAwIDEgLjIuMiAwIC41LjMuNi41LjIuMy4zLjYuMyAxVjQ2LjNsLjIuNWgtMS4ydi0uNGwtLjEtLjZabS4xLTJ2LjdIOTYuNmwtLjQuMmEuNy43IDAgMCAwLS4zLjZsLjEuNC4zLjNoLjRhMS4yIDEuMiAwIDAgMCAxLS40bC4yLS40LjMuNS0uMi40LS4zLjRhMS43IDEuNyAwIDAgMS0xLjIuNGMtLjMgMC0uNiAwLS45LS4ybC0uNi0uNS0uMi0uOC4xLS43LjUtLjUuNy0uM2gxLjhabTQuNi0ydi45aC0yLjh2LS44aDIuOFptLTItMS4yaDEuMXY0LjlsLjEuMy4yLjJoLjNhMS43IDEuNyAwIDAgMCAuNCAwdi44YTIuOCAyLjggMCAwIDEtLjguMWwtLjYtLjFhMSAxIDAgMCAxLS41LS41bC0uMi0uOHYtNC45Wm01LjMgNi4zYy0uNCAwLS43IDAtMS0uMmEyLjIgMi4yIDAgMCAxLTEuMy0xLjJsLS4xLTF2LS4ybC4xLTEgLjUtLjguNy0uNiAxLS4xLjkuMWMuMi4yLjUuMy42LjZsLjQuNy4yIDF2LjVoLTMuOXYtLjhoMi44di0uMWMwLS4yIDAtLjQtLjItLjZhMSAxIDAgMCAwLS4zLS40IDEgMSAwIDAgMC0uNi0uMiAxIDEgMCAwIDAtLjguNWwtLjIuNS0uMS43di44bC4zLjVjLjIuMS4zLjMuNS4zbC41LjFhMS41IDEuNSAwIDAgMCAxLjMtLjZsLjYuNi0uNC40LS43LjQtLjguMVptOC41LTYuOHY2LjdoLTF2LTUuNGwtMS43LjZ2LTFsMi42LS45aC4xWm01LjYgNS43di02aDEuMnY3aC0xbC0uMi0xWm0tMy4yLTEuNHYtLjFsLjEtMSAuNC0uOGMuMi0uMy40LS40LjYtLjUuMy0uMi42LS4yLjktLjJsLjguMS41LjUuNC44LjIgMXYuM2MwIC40IDAgLjctLjIgMSAwIC4zLS4yLjUtLjQuOGExLjYgMS42IDAgMCAxLTEuMy42IDEuNyAxLjcgMCAwIDEtMS41LS43bC0uNC0uOHYtMVptMS4xLS4xdi43bC4zLjYuMy4zLjUuMS43LS4xLjMtLjUuMi0uNnYtMS40bC0uMy0uNGExIDEgMCAwIDAtLjQtLjJsLS41LS4xYTEgMSAwIDAgMC0uNSAwIDEgMSAwIDAgMC0uMy40bC0uMi42di42Wm0xMCAxLjV2LTIuM2wtLjEtLjUtLjMtLjNhMSAxIDAgMCAwLS41LS4xaC0uNWwtLjMuMy0uMS40aC0xLjFsLjEtLjYuNC0uNS43LS4zLjgtLjFjLjQgMCAuNyAwIDEgLjIuMyAwIC42LjMuNy41LjIuMy4zLjYuMyAxVjQ2LjNsLjIuNWgtMS4ybC0uMS0uNHYtLjZabS4xLTJ2LjdoLTEuM2wtLjQuMmEuNy43IDAgMCAwLS4zLjZ2LjRsLjMuM2guNWExLjIgMS4yIDAgMCAwIDEtLjRsLjEtLjQuNC41LS4yLjQtLjMuNGExLjcgMS43IDAgMCAxLTEuMi40Yy0uNCAwLS43IDAtMS0uMmwtLjUtLjUtLjItLjguMS0uNy41LS41LjctLjNoMS44Wm01LjUtMmgxdjQuOWMwIC40IDAgLjgtLjMgMS4xLS4yLjMtLjQuNi0uOC43YTIuNyAyLjcgMCAwIDEtMS44LjJsLS42LS4zYy0uMyAwLS40LS4yLS42LS40bC41LS43LjYuNWExLjYgMS42IDAgMCAwIDEuMyAwbC41LS40LjEtLjdWNDNsLjEtMS4xWm0tMy40IDIuNnYtLjFjMC0uNCAwLS43LjItMSAwLS4zLjItLjYuNC0uOC4xLS4zLjMtLjQuNi0uNS4yLS4yLjUtLjIuOC0uMmwuOC4xLjYuNS40LjguMiAxdi4zbC0uMiAxLS40LjhhMS42IDEuNiAwIDAgMS0xLjQuNiAxLjggMS44IDAgMCAxLTEuNC0uN2wtLjQtLjgtLjItMVptMS4xLS4xdi4xbC4xLjYuMi42LjQuMy41LjEuNi0uMS40LS41LjItLjZWNDRsLS4xLS42LS4yLS40YTEgMSAwIDAgMC0uNC0uMmwtLjUtLjFhMSAxIDAgMCAwLS45LjRsLS4yLjZ2LjZabTQuNS4xdi0uMWwuMS0xIC41LS44LjctLjVjLjMtLjIuNi0uMiAxLS4yLjMgMCAuNyAwIDEgLjIuMiAwIC41LjMuNy41bC40LjguMiAxdi4xYzAgLjQgMCAuNy0uMiAxIDAgLjMtLjIuNi0uNC44YTIuMSAyLjEgMCAwIDEtMS43LjdjLS40IDAtLjcgMC0xLS4yLS4zIDAtLjUtLjMtLjctLjVsLS41LS44YTMgMyAwIDAgMS0uMS0xWm0xLS4xdi4xbC4xLjYuMi42LjQuMy42LjFhMSAxIDAgMCAwIC45LS40bC4yLS42VjQzLjdsLS4yLS42YTEgMSAwIDAgMC0xLS41IDEgMSAwIDAgMC0uNS4yIDEgMSAwIDAgMC0uNC4zbC0uMi42di42WiIvPjxwYXRoIGZpbGw9IiMzRkE3MUEiIGQ9Ik00OCAxMDQuNXYzLjNIMzFWMTA1bDguMy05IDIuMS0yLjdjLjYtLjggMS0xLjUgMS4xLTJhNS4yIDUuMiAwIDAgMCAwLTRjLS40LS42LS44LTEtMS40LTEuNC0uNi0uNC0xLjMtLjYtMi0uNi0xIDAtMS44LjMtMi41LjctLjYuNC0xLjEgMS0xLjQgMS43LS40LjctLjUgMS41LS41IDIuNGgtNC4zYzAtMS41LjQtMi44IDEtNGE3LjUgNy41IDAgMCAxIDMtMyA5LjIgOS4yIDAgMCAxIDQuNy0xLjFjMS43IDAgMy4xLjMgNC4zLjggMS4yLjYgMi4xIDEuNCAyLjggMi41YTcuNiA3LjYgMCAwIDEgLjYgNi4xbC0xLjIgMi40TDQ0IDk2YTQyIDQyIDAgMCAxLTIgMi40bC01LjYgNkg0OFptMjAuMi0xMS42Vjk3YzAgMi0uMiAzLjctLjYgNS4xYTkgOSAwIDAgMS0xLjcgMy40Yy0uOCAxLTEuNiAxLjYtMi43IDJhOS44IDkuOCAwIDAgMS02LjIuMiA2LjkgNi45IDAgMCAxLTQuMS0zLjNjLS41LTEtLjktMi0xLjEtMy4yLS4zLTEuMi0uNC0yLjYtLjQtNC4ydi00LjJjMC0yIC4yLTMuNy42LTUuMWE5IDkgMCAwIDEgMS43LTMuNGMuNy0uOSAxLjYtMS41IDIuNi0yIDEtLjMgMi4yLS41IDMuNS0uNSAxIDAgMiAuMSAyLjguNGE2LjYgNi42IDAgMCAxIDQgMy4zYy41LjggMSAxLjkgMS4yIDMgLjIgMS4zLjQgMi43LjQgNC4zWm0tNC4zIDQuOHYtNS40bC0uMS0yLjdhOCA4IDAgMCAwLS42LTIgNCA0IDAgMCAwLS44LTEuNCAzIDMgMCAwIDAtMS4yLS43IDMuOSAzLjkgMCAwIDAtMy4yLjFjLS41LjMtMSAuNy0xLjMgMS4yLS4zLjYtLjYgMS4zLS44IDIuMi0uMi45LS4zIDItLjMgMy4zdjUuNGMwIDEgMCAyIC4yIDIuN2wuNSAyIC45IDEuNCAxLjEuOCAxLjUuMmE0IDQgMCAwIDAgMS44LS40Yy41LS4yIDEtLjYgMS4zLTEuMi4zLS42LjYtMS4zLjgtMi4ybC4yLTMuM1ptMjEuNy01djE1LjFoLTQuM3YtMTloNGwuMyAzLjlabS0uNyA1LTEuNS0uMWMwLTEuMy4yLTIuNS41LTMuNi40LTEuMS44LTIuMSAxLjUtMyAuNi0uOCAxLjQtMS40IDIuMy0xLjkgMS0uNCAyLS42IDMuMy0uNmE3IDcgMCAwIDEgMi40LjNjLjcuMyAxLjQuNyAxLjkgMS4yLjUuNiAxIDEuMyAxLjIgMi4xLjMuOC40IDEuOS40IDN2MTIuN2gtNC4yVjk1LjVjMC0uOS0uMS0xLjYtLjQtMi4yLS4zLS41LS43LS45LTEuMi0xLS41LS4zLTEtLjQtMS44LS40cy0xLjUuMS0yIC40Yy0uNi4zLTEgLjgtMS40IDEuMy0uMy41LS42IDEuMS0uOCAxLjhsLS4yIDIuMlptMTEuOC0xLjItMiAuNGMwLTEuMS4yLTIuMi41LTMuMnMuOC0yIDEuNC0yLjdhNi43IDYuNyAwIDAgMSA1LjUtMi41YzEgMCAxLjkuMSAyLjYuNC44LjIgMS40LjYgMiAxLjIuNS42IDEgMS4zIDEuMiAyLjIuMy45LjQgMiAuNCAzLjJ2MTIuM2gtNC4yVjk1LjVjMC0xLS4yLTEuNy0uNC0yLjItLjMtLjUtLjctMS0xLjItMS4xLS41LS4yLTEuMS0uMy0xLjgtLjNhNCA0IDAgMCAwLTEuNy4zYy0uNS4zLTEgLjYtMS4zIDFsLS44IDEuNS0uMiAxLjhabTE4LjctOC42aDEuNWMuNSAwIDEgMCAxLjItLjIuNC0uMS42LS4zLjgtLjZhMS42IDEuNiAwIDAgMC0uMy0yYy0uMy0uMy0uOC0uNS0xLjYtLjUtLjUgMC0xIC4xLTEuNC40LS40LjItLjYuNS0uNiAxaC0zLjJjMC0uOS4yLTEuNi43LTIuMi41LS41IDEuMS0xIDEuOS0xLjNhOCA4IDAgMCAxIDUuNCAwIDQgNCAwIDAgMSAxLjggMS4zYy41LjYuNyAxLjMuNyAyLjIgMCAuNy0uMiAxLjMtLjYgMS44LS40LjUtMSAuOS0xLjcgMS4yLS43LjMtMS42LjQtMi42LjRoLTJWODhabTAgMi4ydi0xLjRoMmMxLjEgMCAyIC4xIDIuOC40LjguMiAxLjQuNiAxLjggMS4xLjQuNS42IDEuMi42IDIgMCAuOS0uMyAxLjYtLjggMi4yLS41LjYtMS4xIDEuMS0yIDEuNGE4IDggMCAwIDEtNS40IDBjLS44LS4yLTEuNS0uNy0yLTEuMy0uNi0uNi0uOC0xLjQtLjgtMi40aDMuMmMwIC40LjIuOC42IDEuMS40LjQgMSAuNSAxLjcuNS44IDAgMS40LS4xIDEuNy0uNS40LS4zLjYtLjcuNi0xLjJzLS4xLS44LS4zLTFjLS4yLS40LS41LS42LS45LS43YTQgNCAwIDAgMC0xLjMtLjJoLTEuNVptMjItNy45LTEwIDI3LjhIMTI0bDEwLTI3LjhoMy4zWm03LjEtMS40djI3aC00LjJ2LTI3aDQuMlptLS43IDE2LjhoLTEuNGMwLTEuMy4yLTIuNS42LTMuNi4zLTEuMS44LTIuMSAxLjUtM2E2LjcgNi43IDAgMCAxIDUuNC0yLjVjMSAwIDEuOCAwIDIuNS4zLjguMyAxLjQuNyAyIDEuMy41LjYgMSAxLjMgMS4yIDIuMi4zIDEgLjUgMiAuNSAzLjN2MTIuMmgtNC4zVjk1LjZhNSA1IDAgMCAwLS40LTIuMmMtLjItLjUtLjYtMS0xLjEtMS4yLS41LS4yLTEuMS0uMy0xLjktLjMtLjcgMC0xLjQuMS0yIC40LS42LjMtMSAuOC0xLjQgMS4zLS40LjUtLjcgMS4xLTEgMS44bC0uMiAyLjJabTIwLjktNS4ydjE1LjRoLTQuMnYtMTloNGwuMiAzLjZabTUuOC0zLjd2NGE4LjMgOC4zIDAgMCAwLTMuNy4xIDMuNiAzLjYgMCAwIDAtMi4yIDIuM2MtLjIuNS0uMyAxLjEtLjMgMS44aC0xYzAtMS4yLjEtMi4zLjMtMy4zLjMtMSAuNi0xLjkgMS4xLTIuNy41LS44IDEtMS40IDEuOC0xLjhhNC44IDQuOCAwIDAgMSAzLjMtLjZsLjcuMloiLz48L3N2Zz4=", + "description": "Displays the latest flow rate telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'flowRate', label: 'Flow rate', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flow rate\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"square\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:hydro-power\",\"iconColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#305AD7\"},{\"from\":10,\"to\":30,\"color\":\"#3FA71A\"},{\"from\":30,\"to\":50,\"color\":\"#F36900\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":28,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#305AD7\"},{\"from\":10,\"to\":30,\"color\":\"#3FA71A\"},{\"from\":30,\"to\":50,\"color\":\"#F36900\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Flow rate card\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"m³/hr\",\"decimals\":0,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "liquid", + "fluid", + "flow rate", + "fluid dynamics", + "velocity", + "mass flow", + "volume flow" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/flow_rate_card_with_background.json b/application/src/main/data/json/system/widget_types/flow_rate_card_with_background.json new file mode 100644 index 0000000000..effa45fe15 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/flow_rate_card_with_background.json @@ -0,0 +1,32 @@ +{ + "fqn": "flow_rate_card_with_background", + "name": "Flow rate card with background", + "deprecated": false, + "image": "tb-image:Zmxvd19yYXRlX2NhcmRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==:Zmxvd19yYXRlX2NhcmRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48L2c+PHBhdGggZmlsbD0iIzNCOTExQyIgZD0iTTMzLjQgMTkuNGEyLjUgMi41IDAgMCAwLTIuNCAxLjhsLTEgMy4yYy0uNSAwLTEgMC0xLjMuM2wtMi4zLTIuNS0xLjctMS44YTIuNSAyLjUgMCAwIDAtLjEgMy41bDIuMiAyLjVhMy44IDMuOCAwIDAgMC0uNCAxLjNsLTMuMy43LTIuNC41YTIuNSAyLjUgMCAwIDAgMyAyTDI3IDMwYy4yLjQuNS43LjkgMWwtMSAzLjItLjggMi40YTIuNSAyLjUgMCAwIDAgMy4xLTEuN2wxLTMuMWMuNSAwIDEtLjEgMS40LS4zbDIuMyAyLjQgMS42IDEuOWEyLjUgMi41IDAgMCAwIC4yLTMuNkwzMy40IDMwbC4zLS43LjItLjYgMy4yLS44IDIuNC0uNWEyLjUgMi41IDAgMCAwLTMtMS45bC0zLjIuN2MtLjItLjMtLjUtLjctMS0xbDEtMy4yLjgtMi4zLS43LS4yWm0tMy4yIDYuOGExLjkgMS45IDAgMSAxLTEuNyAyLjggMS45IDEuOSAwIDAgMSAxLjctMi44TTIyIDMyLjFhOC43IDguNyAwIDAgMS01IDEuNmgtMi41djIuNUgxN2ExMSAxMSAwIDAgMCA1LTEuMmMxIC41IDIgLjggMyAxbC43LTJ2LS40YTguOCA4LjggMCAwIDEtMy43LTEuNVptMTUuNiAxLjZjMCAxLS4yIDEuOC0uNyAyLjVoMi42di0yLjVoLTJaTTIyIDM3LjFhOC43IDguNyAwIDAgMS01IDEuNmgtMi41djIuNUgxN2ExMSAxMSAwIDAgMCA1LTEuMmMzLjEgMS42IDYuOSAxLjYgMTAgMCAxLjYuOCAzLjMgMS4yIDUgMS4yaDIuNXYtMi41SDM3Yy0xLjcgMC0zLjUtLjUtNS0xLjYtMyAyLjEtNyAyLjEtMTAgMFoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNNTAuNyAxOC41djEwSDQ5di0xMGgxLjdabTQgNC40djEuNGgtNC40di0xLjRoNC41Wm0uNy00LjRWMjBoLTUuMXYtMS40aDVabTMuMi0uNXYxMC41aC0xLjdWMThoMS43Wm0xLjcgNi45di0uMmMwLS41IDAtMSAuMi0xLjVsLjctMS4yYTMgMyAwIDAgMSAxLS43bDEuNS0uM2MuNiAwIDEgMCAxLjUuMy40LjEuOC40IDEgLjcuNC40LjYuOC44IDEuMmwuMiAxLjV2LjJjMCAuNSAwIDEtLjIgMS41bC0uNyAxLjJhMy4xIDMuMSAwIDAgMS0yLjUgMWMtLjYgMC0xIDAtMS41LS4yYTMuNiAzLjYgMCAwIDEtMS44LTJsLS4yLTEuNVptMS43LS4yVjI2bC40LjcuNS41Yy4zLjIuNS4yLjkuMmExLjYgMS42IDAgMCAwIDEuMy0uN2MuMi0uMi4zLS41LjMtLjhsLjEtMXYtMUw2NSAyM2ExLjYgMS42IDAgMCAwLTEuNC0uN2MtLjMgMC0uNSAwLS44LjJsLS41LjVjLS4yLjItLjMuNS0uMy44bC0uMSAxWm04LjggMi4yIDEuNy01LjhoMWwtLjIgMS43LTEuNyA1LjdoLTFsLjItMS42Wm0tMS01LjggMS40IDUuOHYxLjZoLTFsLTItNy40aDEuNlptNS40IDUuNyAxLjMtNS43SDc4bC0yIDcuNGgtMXYtMS43Wm0tMS40LTUuNyAxLjcgNS43LjIgMS43aC0xTDczIDIyLjhsLS4zLTEuN2gxWm0xMSAxLjR2NmgtMS42di03LjRoMS42djEuNFptMi4zLTEuNHYxLjVhMy4yIDMuMiAwIDAgMC0xLjQgMCAxLjQgMS40IDAgMCAwLS45IDFsLS4xLjZoLS40bC4xLTEuMi41LTFjLjEtLjQuNC0uNi42LS44YTEuOSAxLjkgMCAwIDEgMS4zLS4yaC4zWm01LjIgNnYtMy42bC0uMS0uN2ExIDEgMCAwIDAtLjUtLjRsLS43LS4yLS43LjEtLjQuNGMtLjIuMi0uMi4zLS4yLjVoLTEuNmMwLS4zIDAtLjUuMi0uOC4xLS4zLjMtLjUuNi0uN2EzIDMgMCAwIDEgMS0uNUE0IDQgMCAwIDEgOTEgMjFjLjYgMCAxIDAgMS41LjIuNC4yLjguNSAxIC45LjIuNC40LjguNCAxLjR2NC4yYzAgLjMuMi41LjMuN3YuMWgtMS43YTMgMyAwIDAgMS0uMi0uN1YyN1ptLjMtMy4xdjFoLTEuMmwtLjguMS0uNi4zYTEgMSAwIDAgMC0uNS45YzAgLjIgMCAuNC4yLjUgMCAuMi4yLjMuNC40bC42LjFhMS44IDEuOCAwIDAgMCAxLjUtLjdsLjItLjYuNi44LS4zLjZhMyAzIDAgMCAxLS41LjYgMi42IDIuNiAwIDAgMS0xLjguNmMtLjUgMC0xIDAtMS4zLS4zLS40LS4xLS43LS40LS45LS43YTIuNCAyLjQgMCAwIDEtLjEtMi4ybC43LS44IDEtLjQgMS41LS4yaDEuM1ptNi43LTIuOXYxLjJoLTQuMXYtMS4yaDQuMVptLTMtMS44SDk4djcuMmwuMS41LjMuMmguNGEyLjUgMi41IDAgMCAwIC42IDB2MS4zYTQuMiA0LjIgMCAwIDEtMSAuMWMtLjUgMC0uOCAwLTEuMS0uMi0uMy0uMS0uNS0uMy0uNy0uNmwtLjItMS4ydi03LjNabTcuOCA5LjNjLS41IDAtMSAwLTEuNS0uMmEzLjMgMy4zIDAgMCAxLTEuOC0yIDQgNCAwIDAgMS0uMi0xLjN2LS4zYzAtLjYgMC0xLjEuMi0xLjZzLjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEtLjhsMS40LS4yYy42IDAgMSAwIDEuNC4ybDEgLjhjLjMuMy41LjcuNiAxLjFsLjIgMS41di43aC01LjdWMjRoNFYyNGwtLjEtLjktLjUtLjYtLjktLjJjLS4zIDAtLjUgMC0uNy4yLS4yIDAtLjQuMi0uNS41bC0uNC43VjI2bC40LjcuNy41LjguMWEyLjMgMi4zIDAgMCAwIDEuOS0uOWwuOS45LS43LjZjLS4yLjItLjUuNC0xIC41LS4zLjItLjcuMi0xLjIuMloiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii40IiBkPSJNNTIuOSA0MS42di45aC0zLjR2LTFINTNabS0zLTUuOHY2LjdoLTEuMnYtNi43aDEuMVptNi44IDUuN3YtMi40bC0uMS0uNC0uMy0uM2ExIDEgMCAwIDAtLjUtLjFoLS41bC0uMy4zLS4xLjRoLTEuMWwuMS0uNi40LS41Yy4yLS4xLjQtLjMuNy0uM2wuOC0uMiAxIC4yYy4zLjEuNi4zLjcuNi4yLjIuMy42LjMgMVY0MmwuMi40di4xaC0xLjJsLS4xLS41di0uNVptLjEtMnYuN2gtMS4zbC0uNC4yYS43LjcgMCAwIDAtLjMuNnYuNGwuMy4yLjUuMWExLjIgMS4yIDAgMCAwIDEtLjVsLjEtLjQuNC41LS4yLjRjMCAuMi0uMi4zLS4zLjVhMS43IDEuNyAwIDAgMS0xLjIuNGMtLjQgMC0uNyAwLTEtLjJsLS41LS41Yy0uMi0uMy0uMi0uNS0uMi0uOGwuMS0uNy41LS41LjctLjMgMS0uMWguOFptNS4xIDEuN3YtLjNsLS40LS4zYTMgMyAwIDAgMC0uNy0uMmwtLjctLjItLjYtLjMtLjMtLjQtLjEtLjZ2LS41bC41LS41Yy4xLS4yLjMtLjMuNi0uM2wuOC0uMiAxIC4yLjcuNmMuMi4yLjMuNS4zLjhoLTEuMWwtLjEtLjQtLjMtLjJhMSAxIDAgMCAwLS41LS4xIDEgMSAwIDAgMC0uNSAwbC0uMy4zYS42LjYgMCAwIDAgMCAuNWwuMS4yLjQuMS41LjIgMSAuMy42LjUuMi43YTEuMyAxLjMgMCAwIDEtLjUgMSAyIDIgMCAwIDEtLjcuNEg2MWwtMS4yLS4xLS43LS43LS4yLS44aDFsLjIuNWMuMS4yLjMuMi40LjNoMWwuMy0uMi4xLS4zWm00LjUtMy43di44aC0yLjh2LS44aDIuOFptLTItMS4yaDEuMnY1LjJsLjIuMWguM2ExLjcgMS43IDAgMCAwIC40IDB2LjlhMi44IDIuOCAwIDAgMS0uNyAwSDY1YTEgMSAwIDAgMS0uNS0uNWwtLjEtLjh2LTQuOVptOC42IDV2LTMuOGgxLjF2NWgtMXYtMS4yWm0uMi0xaC40bC0uMSAxLS40LjZjLS4xLjItLjMuNC0uNi41YTIgMiAwIDAgMS0uOC4yIDIgMiAwIDAgMS0uNy0uMWMtLjIgMC0uNC0uMi0uNS0uM2wtLjQtLjZWMzcuNWgxdjMuN2wuMy4zLjIuMS4zLjFjLjMgMCAuNiAwIC44LS4ybC40LS41di0uN1ptMy40LTEuOHY2aC0xLjF2LTdoMXYxWm0zLjIgMS41LS4xIDFjMCAuNC0uMi42LS40LjktLjEuMi0uMy40LS42LjVsLS44LjJjLS4zIDAtLjYgMC0uOC0uMi0uMiAwLS40LS4zLS42LS41bC0uNC0uNy0uMi0xVjQwbC4yLTEgLjQtLjhhMS43IDEuNyAwIDAgMSAxLjQtLjdjLjMgMCAuNiAwIC44LjIuMy4xLjUuMy42LjVsLjQuOC4xIDFabS0xIDAtLjEtLjctLjItLjVhMSAxIDAgMCAwLTEtLjVsLS40LjFhMSAxIDAgMCAwLS40LjNsLS4yLjQtLjEuNXYuOWwuMi42YzAgLjIuMi4zLjQuNC4xLjIuMy4yLjYuMmwuNS0uMS40LS40LjItLjV2LS42Wm01LjIgMS41di02aDEuMXY3aC0xdi0xWk04MC44IDQwVjQwbC4xLTFjMC0uNC4yLS42LjQtLjlhMS43IDEuNyAwIDAgMSAxLjQtLjdjLjMgMCAuNiAwIC44LjJsLjYuNS40LjguMiAxdi4zbC0uMiAxLS40LjdhMS42IDEuNiAwIDAgMS0xLjQuNyAxLjcgMS43IDAgMCAxLTEuNC0uN2wtLjQtLjgtLjEtMVptMS0uMS4xLjcuMi41LjQuNC41LjFjLjIgMCAuNSAwIC42LS4yLjIgMCAuMy0uMi40LS40bC4yLS43di0uOGwtLjEtLjUtLjMtLjRhMSAxIDAgMCAwLS4zLS4zSDgzYTEgMSAwIDAgMC0uNSAwIDEgMSAwIDAgMC0uNC40bC0uMi41di43Wm03LjUgMS41di0yLjRsLS4xLS40YzAtLjItLjItLjMtLjMtLjNhMSAxIDAgMCAwLS41LS4xaC0uNWwtLjMuM3YuNGgtMS4yYzAtLjIgMC0uNC4yLS42IDAtLjIuMi0uNC40LS41bC42LS4zLjktLjIgMSAuMmMuMi4xLjUuMy42LjYuMi4yLjMuNi4zIDFWNDJsLjIuNHYuMWgtMS4yVjQybC0uMS0uNVptLjEtMnYuN0g4OC4xbC0uNC4yYS43LjcgMCAwIDAtLjMuNmwuMS40cy4xLjIuMy4ybC40LjFhMS4yIDEuMiAwIDAgMCAxLS41bC4yLS40LjMuNWMwIC4yIDAgLjMtLjIuNCAwIC4yLS4yLjMtLjMuNWExLjcgMS43IDAgMCAxLTEuMi40Yy0uMyAwLS42IDAtLjktLjJsLS42LS41LS4yLS44LjEtLjcuNS0uNS43LS4zIDEtLjFoLjhabTQuNi0ydi44aC0yLjh2LS44SDk0Wm0tMi0xLjJoMS4xdjQuOGwuMS40LjIuMWguM2ExLjcgMS43IDAgMCAwIC40IDB2LjlhMi44IDIuOCAwIDAgMS0uOCAwaC0uNmExIDEgMCAwIDEtLjUtLjVsLS4yLS44di00LjlabTUuMyA2LjNjLS40IDAtLjcgMC0xLS4yYTIuMiAyLjIgMCAwIDEtMS4zLTEuM2wtLjEtMVY0MGwuMS0xIC41LS45LjctLjUgMS0uMmMuMyAwIC42IDAgLjkuMmwuNi41LjQuOC4yIDF2LjRoLTMuOXYtLjhoMi44YzAtLjIgMC0uNC0uMi0uNmExIDEgMCAwIDAtLjMtLjQgMSAxIDAgMCAwLS42LS4yIDEgMSAwIDAgMC0uOC41bC0uMi41LS4xLjd2LjhsLjMuNS41LjMuNS4xYTEuNSAxLjUgMCAwIDAgMS4zLS42bC42LjYtLjQuNC0uNy40aC0uOFptOC41LTYuOHY2LjdoLTF2LTUuNGwtMS43LjZ2LTFsMi42LTFoLjFabTUuNiA1Ljd2LTZoMS4ydjdoLTFsLS4yLTFabS0zLjItMS40VjQwbC4xLTEgLjQtLjkuNi0uNS45LS4yYy4zIDAgLjUgMCAuOC4ybC41LjUuNC44LjIgMXYuM2MwIC4zIDAgLjctLjIgMSAwIC4zLS4yLjUtLjQuN2ExLjYgMS42IDAgMCAxLTEuMy43IDEuNyAxLjcgMCAwIDEtMS41LS43bC0uNC0uOHYtMVptMS4xLS4xdi43bC4zLjVjMCAuMi4yLjMuMy40bC41LjFjLjMgMCAuNSAwIC43LS4ybC4zLS40LjItLjd2LTEuM2wtLjMtLjRhMSAxIDAgMCAwLS40LS4zaC0uNWExIDEgMCAwIDAtLjUgMCAxIDEgMCAwIDAtLjMuNGwtLjIuNXYuN1ptMTAgMS41di0yLjRsLS4xLS40LS4zLS4zYTEgMSAwIDAgMC0uNS0uMWgtLjVsLS4zLjMtLjEuNGgtMS4xbC4xLS42LjQtLjVjLjItLjEuNC0uMy43LS4zbC44LS4yIDEgLjJjLjMuMS42LjMuNy42LjIuMi4zLjYuMyAxVjQybC4yLjR2LjFoLTEuMmwtLjEtLjV2LS41Wm0uMS0ydi43aC0xLjNsLS40LjJhLjcuNyAwIDAgMC0uMy42di40bC4zLjIuNS4xYTEuMiAxLjIgMCAwIDAgMS0uNWwuMS0uNC40LjUtLjIuNGMwIC4yLS4yLjMtLjMuNWExLjcgMS43IDAgMCAxLTEuMi40Yy0uNCAwLS43IDAtMS0uMmwtLjUtLjUtLjItLjguMS0uNy41LS41LjctLjMgMS0uMWguOFptNS41LTJoMXY0LjljMCAuNCAwIC44LS4zIDEuMS0uMi4zLS40LjYtLjguN2EyLjcgMi43IDAgMCAxLTEuOC4ybC0uNi0uM2MtLjMtLjEtLjQtLjMtLjYtLjVsLjUtLjYuNi41YTEuNiAxLjYgMCAwIDAgMS4zIDBsLjUtLjUuMS0uNnYtMy43bC4xLTEuMlptLTMuNCAyLjZWNDBjMC0uNCAwLS43LjItMSAwLS40LjItLjYuNC0uOS4xLS4yLjMtLjQuNi0uNWwuOC0uMmMuMyAwIC42IDAgLjguMi4zLjEuNS4zLjYuNWwuNC44LjIgMXYuM2wtLjIgMS0uNC43YTEuNiAxLjYgMCAwIDEtMS40LjcgMS44IDEuOCAwIDAgMS0xLjQtLjdsLS40LS44LS4yLTFabTEuMS0uMS4xLjcuMi41LjQuNC41LjFjLjMgMCAuNSAwIC42LS4yLjIgMCAuMy0uMi40LS40bC4yLS43di0uOGwtLjEtLjUtLjItLjRhMSAxIDAgMCAwLS40LS4zaC0uNWExIDEgMCAwIDAtLjkuNGwtLjIuNXYuN1ptNC41IDAgLjEtMSAuNS0uOC43LS42IDEtLjIgMSAuMi43LjYuNC44LjIgMWMwIC40IDAgLjgtLjIgMSAwIC40LS4yLjYtLjQuOWEyLjEgMi4xIDAgMCAxLTEuNy43Yy0uNCAwLS43IDAtMS0uMmwtLjctLjUtLjUtLjhhMyAzIDAgMCAxLS4xLTFabTEgMCAuMS43LjIuNS40LjQuNi4xYTEgMSAwIDAgMCAuOS0uNWwuMi0uNXYtMS40bC0uMi0uNWExIDEgMCAwIDAtMS0uNSAxIDEgMCAwIDAtLjUuMSAxIDEgMCAwIDAtLjQuNGwtLjIuNXYuN1oiLz48cGF0aCBmaWxsPSIjM0I5MTFDIiBkPSJNNDggMTA3LjZ2My40SDMxdi0yLjlsOC4zLTkgMi4xLTIuOGMuNi0uNyAxLTEuNCAxLjEtMmE1LjIgNS4yIDAgMCAwIDAtMy45Yy0uNC0uNi0uOC0xLTEuNC0xLjQtLjYtLjQtMS4zLS42LTItLjYtMSAwLTEuOC4yLTIuNS42LS42LjQtMS4xIDEtMS40IDEuNy0uNC43LS41IDEuNi0uNSAyLjVoLTQuM2MwLTEuNS40LTIuOSAxLTRhNy41IDcuNSAwIDAgMSAzLTMgOS4yIDkuMiAwIDAgMSA0LjctMS4xYzEuNyAwIDMuMS4yIDQuMy44IDEuMi42IDIuMSAxLjQgMi44IDIuNWE3LjYgNy42IDAgMCAxIC42IDYuMSAyMC4yIDIwLjIgMCAwIDEtMi44IDQuN2wtMi4yIDIuMy01LjUgNi4xSDQ4Wm0yMC4yLTExLjV2NC4xYzAgMi0uMiAzLjgtLjYgNS4yYTkgOSAwIDAgMS0xLjcgMy40Yy0uOC45LTEuNiAxLjUtMi43IDJhOS44IDkuOCAwIDAgMS02LjIuMiA2LjkgNi45IDAgMCAxLTQuMS0zLjRjLS41LS45LS45LTEuOS0xLjEtMy4xLS4zLTEuMi0uNC0yLjctLjQtNC4zdi00LjFjMC0yIC4yLTMuOC42LTUuMWE5IDkgMCAwIDEgMS43LTMuNGMuNy0xIDEuNi0xLjUgMi42LTIgMS0uMyAyLjItLjUgMy41LS41IDEgMCAyIDAgMi44LjNhNi42IDYuNiAwIDAgMSA0IDMuM2MuNi45IDEgMiAxLjIgMy4xLjIgMS4yLjQgMi43LjQgNC4zWm0tNC4zIDQuN3YtNS40bC0uMS0yLjdhOCA4IDAgMCAwLS42LTIgNCA0IDAgMCAwLS44LTEuMyAzIDMgMCAwIDAtMS4yLS43IDMuOSAzLjkgMCAwIDAtMy4yLjFjLS41LjItMSAuNi0xLjMgMS4yLS4zLjUtLjYgMS4zLS44IDIuMi0uMi45LS4zIDItLjMgMy4ydjUuNGMwIDEgMCAyIC4yIDIuOGwuNSAyIC45IDEuNGMuMy4zLjcuNiAxLjEuN2wxLjUuM2E0IDQgMCAwIDAgMS44LS40Yy41LS4zIDEtLjcgMS4zLTEuMi4zLS42LjYtMS40LjgtMi4zbC4yLTMuM1ptMjEuNy01VjExMWgtNC4zVjkyaDRsLjMgMy44Wm0tLjcgNWgtMS41YzAtMS4zLjItMi42LjUtMy43LjQtMSAuOC0yIDEuNS0yLjkuNi0uOCAxLjQtMS40IDIuMy0xLjkgMS0uNCAyLS43IDMuMy0uN2E3IDcgMCAwIDEgMi40LjRjLjcuMyAxLjQuNyAxLjkgMS4yczEgMS4yIDEuMiAyYy4zIDEgLjQgMiAuNCAzLjFWMTExaC00LjJWOTguN2MwLTEtLjEtMS43LS40LTIuMi0uMy0uNS0uNy0uOS0xLjItMS4xLS41LS4yLTEtLjQtMS44LS40cy0xLjUuMi0yIC41Yy0uNi4zLTEgLjctMS40IDEuMmwtLjggMS45LS4yIDIuMlptMTEuOC0xLjEtMiAuNGMwLTEuMS4yLTIuMi41LTMuMy4zLTEgLjgtMS45IDEuNC0yLjdhNi43IDYuNyAwIDAgMSA1LjUtMi41YzEgMCAxLjkuMiAyLjYuNC44LjMgMS40LjcgMiAxLjMuNS41IDEgMS4zIDEuMiAyLjIuMy45LjQgMiAuNCAzLjJWMTExaC00LjJWOTguN2MwLTEtLjItMS43LS40LTIuMy0uMy0uNS0uNy0uOC0xLjItMS0uNS0uMy0xLjEtLjQtMS44LS40YTQgNCAwIDAgMC0xLjcuNGMtLjUuMi0xIC42LTEuMyAxTDk3IDk4bC0uMiAxLjhabTE4LjctOC42aDEuNWMuNSAwIDEgMCAxLjItLjJsLjgtLjZhMS42IDEuNiAwIDAgMC0uMy0yYy0uMy0uNC0uOC0uNS0xLjYtLjUtLjUgMC0xIDAtMS40LjMtLjQuMy0uNi42LS42IDFoLTMuMmMwLS44LjItMS41LjctMiAuNS0uNyAxLjEtMS4xIDEuOS0xLjRhOCA4IDAgMCAxIDUuNCAwIDQgNCAwIDAgMSAxLjggMS4zYy41LjYuNyAxLjMuNyAyLjIgMCAuNi0uMiAxLjItLjYgMS43LS40LjYtMSAxLTEuNyAxLjMtLjcuMi0xLjYuNC0yLjYuNGgtMlY5MVptMCAyLjJ2LTEuNGgyYzEuMSAwIDIgMCAyLjguMy44LjMgMS40LjcgMS44IDEuMi40LjUuNiAxLjIuNiAyIDAgLjktLjMgMS42LS44IDIuMi0uNS42LTEuMSAxLTIgMS40YTggOCAwIDAgMS01LjQgMGMtLjgtLjItMS41LS43LTItMS4zLS42LS42LS44LTEuNS0uOC0yLjVoMy4yYzAgLjUuMi45LjYgMS4yLjQuMyAxIC41IDEuNy41LjggMCAxLjQtLjIgMS43LS41LjQtLjMuNi0uOC42LTEuMyAwLS40LS4xLS44LS4zLTEtLjItLjMtLjUtLjUtLjktLjZhNCA0IDAgMCAwLTEuMy0uMmgtMS41Wm0yMi03LjktMTAgMjcuOEgxMjRsMTAtMjcuOGgzLjNabTcuMS0xLjR2MjdoLTQuMlY4NGg0LjJabS0uNyAxNi44aC0xLjRjMC0xLjMuMi0yLjYuNi0zLjcuMy0xIC44LTIgMS41LTIuOWE2LjcgNi43IDAgMCAxIDUuNC0yLjZjMSAwIDEuOC4yIDIuNS40LjguMyAxLjQuNyAyIDEuMy41LjUgMSAxLjMgMS4yIDIuMi4zLjkuNSAyIC41IDMuM1YxMTFoLTQuM1Y5OC43YTUgNSAwIDAgMC0uNC0yLjFjLS4yLS42LS42LTEtMS4xLTEuMi0uNS0uMi0xLjEtLjQtMS45LS40YTQuMiA0LjIgMCAwIDAtMy41IDEuN2wtLjggMS45Yy0uMi43LS4zIDEuNC0uMyAyLjJabTIwLjktNS4yVjExMWgtNC4yVjkyaDRsLjIgMy42Wm01LjgtMy43djMuOWE4LjMgOC4zIDAgMCAwLTMuNy4yIDMuNiAzLjYgMCAwIDAtMi4yIDIuMmwtLjMgMS44LTEgLjFjMC0xLjIuMS0yLjMuMy0zLjMuMy0xIC42LTIgMS4xLTIuNy41LS44IDEtMS40IDEuOC0xLjhhNC44IDQuOCAwIDAgMSAzLjMtLjZsLjcuMloiLz48ZGVmcz48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2MiIHRyYW5zZm9ybT0ibWF0cml4KC4wMDUwMzE0NSAwIDAgLjAwNjI4OTMxIDAgMCkiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjE5Ny40IiBoZWlnaHQ9IjE1Ny40IiB4PSIxLjMiIHk9IjEuMyIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMi40Ii8+PGZlQ29tcG9zaXRlIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzE1MThfNDc5MzciLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzE1MThfNDc5MzciIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48aW1hZ2UgeGxpbms6aHJlZj0iZGF0YTppbWFnZS9qcGVnO2Jhc2U2NCwvOWovNEFBUVNrWkpSZ0FCQVFBQUFRQUJBQUQvMndCREFBSUNBZ0lDQVFJQ0FnSURBZ0lEQXdZRUF3TURBd2NGQlFRR0NBY0pDQWdIQ0FnSkNnMExDUW9NQ2dnSUN3OExEQTBPRGc4T0NRc1FFUkFPRVEwT0RnNy8yd0JEQVFJREF3TURBd2NFQkFjT0NRZ0pEZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc3L3dnQVJDQUNmQU1jREFSRUFBaEVCQXhFQi84UUFIUUFBQWdNQUF3RUJBQUFBQUFBQUFBQUFCZ2NFQlFnQ0F3a0JBUC9FQUJzQkFBSURBUUVCQUFBQUFBQUFBQUFBQUFFQ0FBTUVCUVlILzlvQURBTUJBQUlRQXhBQUFBQk9vU1V3cEpaUmh1eWs4RTloMXlkYkQ1Sm15dHNYMWh4cVhQVXhBWUhRTEFMc2kxSG5iVGwrTTV6V1BMb2FZc3dkWFlCS3BkYWJFaUVaWndjNUw0dytJT21sNloyUUJLcm1iUGNWQ1ZDd2daZFBOV240ZEtXVVR6RXZHcjZ5UGk3UlR0NThvVHRoV09iV0Nualg4V1NSeE11NERpU1Vwb0FBSldCNjJiTGphRjFPUVhyME5VeUV6MjZCMDBLdXA4OEJ0Q1cwQ3d2MEl0dm5KRXRnN2RleWNZQkkweUl4N2FvY0ZnSmZDSzFIQWthMFEwZXZQcE5ZWnNjdVJkMnFFclRZZDMwcjVZbzQrSkZiVmtmY2lOanl0b0ZiRk9zUjREWXo2Q2QyMUpIdGNZWjI1TTRaYjl2Y0h1T3RvcEtIVTExTHVScXdoVHNtaFRVTzU3MkRaWEZSazlUWkVodEdmUnhiQ3RMVUxqVU56WDcxMVNMRTEwWUo5djQ3T0d2TW91WjBOTU9yYjYzSjlIZkNlekIrTjE4OGFjc05iRGl1MlVzSmxCTldicGdNS2IyUUhFcm8ycmJHODlxVWpBN3AzYU5IYU1xSjE4enpWOWw1VEVtRHBISFQ1Z0ZrME5OREg3bkd1S0wvQUZiK1YvU0wxanpNajJUUE5DMVZVZVNXS2dJbGFYSWJBZE5EQmpxUm16Q3NGUXpCOXg1T1UxM1hweStVL1c0b1Z6K2pic3VwUFYrUElMTStkK2IxMS9ScTJlcWFWOGQ2czU4NzZHcHpYV0xHZTlFL1BkMTNaMTB0bDg3aUNvZHJZektueUIxK1ozNU5QYjlNOEt4T1IzbUZ1NTNqbjBPVmMzVld0bExtNjNDNUFpOUdoRmM3c3RQVGlQOEFrYjJUODArank4N0FLeG42YTNXektLMm9mcmJPUHZ2RDhOK0czOGQ3SnorRzluaEw2ajh2T3VyenR2ZExPMmZGZXZKTy93QXJ5QjJjMEt5NnJmUm0xUjZEemRoRlYvTjZpWDVmWWNIVTVWOTVqMEdqL0ErMkMwQW14ZVZpRk5adnlGN2JLbnJjdWxNaTVkVEc1VzlPVXBNN21Ka2ZWdm02ejVQU2FFdnlkcTUrWitmMElxTTAraHo0MkhYdzBWMW1mUnMveDNxQURoOWpUTUMzWkdDd3FxejBVMjk3VDZyZ050UEY2NjR0ZnBhVUl5QTZHVFIvdS9IZ1dta2R3Ylp0aTVPMjhtdkpET1QxTEhQb01ldnkzenh1aWMrSjlYZHM3aXVyL1o1YzNObmFnOUNTenNsNkdFMlU1WkdmWk5CTzRsVzNucm5iZVhUcFZ6YWMxWmFLVklzOVdQdDlmNWV3U3hpVlhhODUvUW92TWRjS1ZodHBhUFhCVWpKZXNyWmxQU1FOR0JLRncxaVBoN1NTU2g3Y3pnQ3l1a3hpbDZRejBHMVpSbFZoUFloUllsaUJ2aTlzYVpGVVl1VVFyempCcHdBM3RicHJMQWFxM2VMcFJXTWVEcU11bDhZMEtXT0ZiSEZsL2NaK1VQdWtLNE9wSkMwcXhHSHAxcG1MY2lsT3VlYVdVMThHbG5saXRzMVZNaldPeG1wWlRZa3FtdGxiVzFvMHR5YjBScjdtVXRja1psRjRqa1NLRXRZTkpoUG83cG1LNlZ4ZklIMHNKbU1GeTNBYmhJd0NvZTBldHRVYXRwS1dHelVvNE5GVXI0UHF2YmFMMVZmS2xQd3Nvd0ZoQnJXakZOMXUvbnZtaVVndklJTmxhNlczWUJyTW8xcEFjeUI2c3BJVTFYYStub1Y0WUprSFZlNzBDU3JTQkh1RTI0MVM0SlZpbEZHelFsa1JLVlBsaG9DMldVVW5FWHRUcCtzNXFWa2NTbndkbDZSMktBMmdybEdDQWJRUWwyVnRLQnZSWFZGcUFjMlhCM3NqdmN1aUtZVXZRS0UvVllpSzdNM2laNlZ2ekJUQUZEU1dScGU0djhqQU5CUjZ0ZG8wa0hVdlR5dFhSbjBNYW1lVDJWMk8zTmZTVk1HSkZtalp3cXZWVUN0azBjYWNiczQrRGRrQWtCWTB0VEhTODA1RDV6VkVWa2pxWXhuL3hBQXNFQUFDQXdBQ0FnSUNBUU1EQlFBQUFBQURCQUVDQlFBR0VSTVNGUlFoSmlJa0pRY1dOaU1uTlVGQy85b0FDQUVCQUFFRkFvcndkeVU1RHhSOEcrS2VETlMvSThjaXZKcHp4K29yelZXMVpaS0lnVG9MR0hGM21qbWpNb25GK3hpRHdLV2hzYWlPQWdueXNSSE82ei8yN3lmTWRYMVBGajNpUHc3S3ByYTloZGZyYUtjK0hDRC9BS2ZYejR6d1p6MDROd25Cc2ZMbnk4eFh6YTJ6UWtZWDVEREhMSkJVdFRhYk5JT3FNbnVxbXVnSkQ5OWc4Y3JXWjV2b2owK3NqMVVrMER0UE5ORmp4bkpUZWQ4enEvcXJYa1U0ZnhYa1Y4eDhPVFhnNjhIOFlpYjI1Wm1veU9zaXBRYitpN1hQNm9yN0FyaFhYMyt4WHgreFc4UkdaYW41bHA4YzhhcjNPMEpXenVtSXRaeXVJdzhGdlJQSDlnc3V2ZldLYktCV29LVHo4WTN4ZHhTL05QMitxQnhNV0JiZ3dYNVdsT042QVFWcFhaZW92aUtnWjY3NXRnQ3NXbktObmlPekJDMnlyZEFoODR1ZC91SWpDeE5RRE9oZFBheTI5SEdIVFV5VDVtZ3ErVm45cC9VNTZRNWZ4S3FBSDd6Qkg4U2VXWXFNeGZkZUFVVis5ektxZjdyelBPU0N0LzhBVUlHZU9PNWZIaDQ5ZHNBTWg2ckhpS3k0dlhqOHc0ZXFoNTVsakVKeThBKzBLMnBSUzIxbGpKOXFjdHY4clpoeVA3R01oZXZJRlFFQ29DeVgwQnZxNlpabHhtS1ljN1haYm5Qb2h0YTBURTJ4Q2VxNmUyM21YVTNNOTdJMGJWTG1mWk0xVHV5djdRbHRlN1ZIcXZHTVZPZlZZaUtvUFJNSjR3T1YxRXcwTDJNL2xWdzdlbm8rZnhveURHSmJJV2ppK2FaSzVmMTB4aHdNSTluMkMzbjk4VkpTbkhRV1dieVNlbFoyZkJzdDRpVHNDL01VRm1LMHFaTkFaakw5Y0xacG9LWnluMXBzK0l2d2pQTk52eTAvSXIydE1mYi9BQVNwb2ZZYWRabW91dm1teStBa2JmR1M0REEybjMrRW9YMU9rOTJ0SHpwU2c1a3d4RENLYi9FZWdyNnl4NW1jellkRm5YMGxTZ1N2TnVTc0VsTVZwUS9haE5maTlrMXgwa2V0YngxM0FQWVJ6dE1GQzJmNGY2akZiVXMxcUhpdktkay9hN2x2YTQvV2MvcjYvd0Flc3VpdFhNSWtXd3Z4UDcxQmV0RzVyV1ptSXRWeUsvVitBeEtueGlQYjZqQzBZeTVDMis0d1hPMVZPekt2dEc3RnVNUVBqZlk4N1RCbmFXZW0rVXZ0ZS9JajRVdXlaN1NHNTZVRHRNdFg4WDVkQWwwOE1nbCtzTWFGQ3JOVTllajdMU01MRUFjK1h1QmI5MGZORkZQL0FHblBoY0RZcGJwVlJ4WWJuWDEyQ01kZi9KeUcrdVM4ZDNJYVRHZnFrTGxwMVZnbDArcTJnV2IxTVZuRjg4SzlYQnN5cms1NjJyYkVWazUzNHBJWFpDMTY3YzNrL3dDdjVmdi9BT3JsSm5vUnBPa3JlZllPdGZuY1NzQ1RLcVVrWHQ2VVdaaTRnT0l2WnJrL0xzMVhFbU9hRWRkTUNGdXZFR0VHTFRyb1VzSXFLbVg3d1Y2NXJVaW1KMktHSjB0WDhxT3ZQT2F1cmg2S2R4NmI0Z0ZiRXlCMUc0THBDajJGdjdYS1YrVUMvY281MXhEQWRpdW9Jc2k2V0Q1VzRmUDhiNWZwQkkwdDUxOUF0RTlyTTlUcGpIelFIdFZRaW9UMS9GeWJaQzF4aTZnMk8rZDEyTjR2dzQ1bzZtWmlQdXVNZFkvYzlURUlvMHlQbG9sU2lybVVMckx0NUgxTFNtUTlWVFF4TkN0UzZjb1ZweTFWcUpDMWEvWnNiSjY5alJzS1dCMkxIYU5GS0dOUllNS2R2elc0cTdwd0NwdndsWUt1dXZZdDBtQTNvUFlyQkd1eENuWGRLNUZ0bVNLU3VWbGVtYXZUTUxtOVdFRU5ldlV6cDMySzB6OUhVWjNYL3dEaWhnQWQ3RTNtSWZRRHVuUkJ4a2tQS1EwMW9MMHFOd2RJblJZc2Y3TUZEVTJCTi9qYXJEZzJHSk9JdEtrc0s5RDIrSUx4NHIvWERlbVprcXpFVjZXdmNsK3BvNXRLcXRNU05tR3I4WE5jUXNXdjhrZnQvRVBQanNCbVJaWFd5VFl2VWNxYUw5bUE2U25iNkhrdXVPUExUaVpiQnovbFhhVHJGNDdPbFZSSnJyb1Z1ZXBrRTBjYnBXajQ2OCt5VHRYSjY0bTVEbU91a1VRQUFTTE56OEZtanJpR3lWQlhuUGowWkt2NHV1NSsrbmVmSGFkM1gvTzdFbmZ3bUtmOG1vWCtRQ212MmkvbXpKTk1rRFQ5MzNnck1DQnJNVHJBWGNscXNoVzlQd0VMUklPbkNWaWVZMnhLWkdZL0tZaFpHUmdTUStGOHhhYTN5azRqS0tvNDBPNk5OMXUzOFBia24zZGVxWHZmNmQybXU2aVVJRWtteTZ1ZDFiWXZiNmxaSVFVcmZQOEFGUEhDbXlFdU05bnBGVTMyTDZmem1Ra21aUGQwdm1XU1R5R2JWdDd5VHo1V211TmUwTTNpc0F1WVhscGZNWjUrQ3NHOWRLOSt1RkU5bzZtZjF6Yzl2MFBxNVZMRnBQdk9LcDVCZVdOek5TcTUyNmZGZE4zVE5XOXZ6KzZUL2xVOCtuMGh6ZUd6TS84QVJVYStHZFZ2endkWXRjU25zNERNcnhaR291YUxVakRRaENtVzZwc3ZjRjB0VWR3cFlLZFBzckJwT3NhSkxvS2c0MTJ0VWZHdTFORTRiVFlaT3ZZZFNzVDVKblQ0NE9QbHM5MEovS0UvK0Y2TFpKM1N1dGZNVnZDbGIvMUNETmVCdmZ5bm03Ny9BQy9VeUNHRFA2dUN3bkRob1k1QzhzeU1kV05pb3FzZG5yOGlhbW14VS9zbWlxaU5rVEQvQUJOd0ZmbS8rNjhMUGtxWDZTUi84NTIrM3k3YUg5ZE9iQVQ3YVVpMkxlc2p0RTgvLzhRQVNoQUFBZ0VEQVFRRkJ3Z0hCZ1FIQUFBQUFRSURBQVFSRWhNaElqRUZGRUZSWVNNeVFsSnhnYUVRSkdLUnNjSFI4QlV6WTNLQ2tySUdjM1Npd3VFZ05GUFNRMVJrZzRTVDhmL2FBQWdCQVFBR1B3S3VCeXRBbFEvRUIzVnhBcFhDeXQ3Njd2OEFoYzJrWGtqNmFITGZWV2lSU3I5dXFsdTVXRnBGNkx5ZHZzSGJTTEJ0R2tHNUpadU9YK0JPU1VibnBTNkZsbmVkVGE1My9DdXI5QVdHSFA4QTR6RFhJYWJydDJWYlBIcTNrVUNJdHRKNjhueVhuN3kvYlhSdVAvS3gvd0JJcXhPTisxNSs2aDNkWC9HcnllMDZRZEJjWU1qUEdDZmNCeW9tZWE1a1k4MjJlUGxYOTlmdCtYaGtOY1NnK3pkWGI3NjVWZ1ZKSEhjOVZtYkdKYzQwRE84MURESHI2Um5YelpwNC9zWHQ5cHJyUFRWOW9sOVFIVktmKzJqYS93Qm5PampDRHpseGx6NzYyL1M5MldjODBVNVAxMW90SVZnN3lQT1B2cTlQN2Y4QUg1Tnd6VTFsMXRMZlV3eStOV01WYjJrSHpwNG8xUUhHYzRHT1EvR3JUckVMUVFiVGdCM2RuZFEvdzM0MTB4R3NDU3gyc3FqSHBZSXBCQmN4cDZ5bUhhSDZ2bFFhUzNGbmQ0ZktQbDRSNzYwYjVaanlqUVpOSDlKM0FnVGQ4MmlPcVErMDlsR0RvUzBYbzYzTzR5bnpqN1RXMjZTbmE4bTlYMGEyZHZFc1VmY2d4VnBhOVdFOFVxYWp2d1J2clVkd3ErbDFZVXo3alFBNW44bWpzNTBndGZSYkdjKzZwNzBYVXR4ZEJnQXo4aG53cTB6TERITVlFTGhmT0owK0crclZJbGZna3lTNjQ3RFgvd0FYOGE2VkZ4UHM1VWxHMk0vcEh3eDUxRlZta2R2MmFCUHRyZ25SdmZpc3FtdjJHa2tGNGx5ekxsaHBLNlBDdEVvNGgyNXpuNU53elhMVFRIVmtMejhLSW1sMkE3STAvV04rRmFiU0FkRldqYzNmejIrK20ybWJtWlczdkorN25sVHZubk8xY01oSHlkRlN6SjVicklqMWpucDdxMDNrTFN3OTFYelRSRDlIOVlZYWNkblpVcTJ5dXNRdEpkT3NIMWFpanRiYkNoQU5Xajd6Z1ZMYjN2U1VOcm5lTmNoZjRDaDFpM2JwS3dRakppSlRjUEVmZlVYVmVqUllJcjhYQ2RSM0h0N2FBemo1citOVHozOTkxaE9hSUV4cDk1SXpXYlltMlhPNHJ6L3lnVnM0dDcvUzNBZDVQaFUzemphTHREczI4M2RYQkl4SGp2clMwRWIvQU1HS2FTUmRHbGM0VTBKWGtlRDZNeTZUVzdXdzhGcjlJSHBDSVcwazVjbzdGTjNkWFNOMjNsTUhFYkh4N1I4bHhLZVcwLzBWQ0dHQ1NUV1NjRHhvOGV2OTJ1amhqUmk1QlR0MUh1L0pyNXpkUjJ5L3RKZ3Z3RytwRU90dFVoT1lqZ21uRUVRaCtaUzZ2S2FpZmJTQ2VlUmxWUU1aMExSRU51djJrMTgwNk9say93RGEvSE5Xelg5cTl2RnI4bnFid3JHZFB6VWJ6MmM2bTZ3MHZTT1R3WWpNYWdlSk9NMXBROUgyUTlna2I3NmVDeXVJNTg1YTVrWHR4NlBzSHhxUzQyc2VtUGNSdnBKV2RkRDh0TFVoamlYcTR6dHA1V3hvOWc3YUVWditvVitIZGxwRDRVTHlBc1kzODhKaktOMmdzZVFyMFg5aGtrcVJqSnNSai9xdEZuK2NGRDdEVXR5cmVTTW1Oa1V3djFkbnUzVXR3QWl2a0JvL1NGU0czUXlCM2JRTTQ5Q2t0UkNZM1hDZ1JycTdPMnVNelRuMXBIRVNqNjhtc1d5eHMzL3ByY3pOL005ZEhSemljR2VmUXUzbjNIM0x5cUtHRzBXV2FRblIxYVBSdUhiazBXMnNkdkxubk55cVZwTHpyV3VGa0d4dHp3NXJNbG8wN0Qwcm01VWZBWnJGdEJiUkQ5bkFYK0p4V21PZGdQQmxUK2tHb3pLcDBnN21Zc2M3dnBVNmpjM1ZCWGwrbElRZjV6OTlmODY4L3dEbHFWNUxhMmlWb2lOY0NhYy9VYXZmR1g4S2dUUnRKSTg3dXlqQzBuQ0JtVEhZS2N5ZVRZS05zVjlBSGxHdmpVbHRjb3ZWaU1TcDZNUGNQRnFNVXI1OVV2SmdON0ZXcHNTYkptR0JpV1dQUDgyVlBzTlIyNjdoRU96ZHhIN1BzTkFvK2hUdXlPeXJTUGE3WGFGanJQRm5kVEpQY1BrZWNobDBBZTRWQ2tOdkUyMWliUmdibTNqbWFkSll4QTQ3VkpRL0NoQ2l6M0VxTnJoRE5xYVAzbmxTQTIyaldNcnRKUzI3M1ZIRkJjRzJmVG5XQm1uYTd1TGwwRURPcnljaVJXQSsxUGhsdnNGY0hSVWoreTJKKzJ2SmRGenhqeENwU0c5VFo3K0ViWFZVcWc0UFYxSHdyTTErMlBCZjk2dkxhWnBab29NQUhYcHp1OEtkb2swQjEwa0Z0VlhNS3V2VWtielZYZVdwMlpzWUZNNzcwMmpPMzdzZis5SzNPUkYybnRsazVmVlNSUmNUQjluRG4xdlRrTlFveDF1b3dIZm5SeTJGN2EyeWNjY2gvSTlocjcvejd2alZxa1UyaFV6amgzNytkWEFFOHN0eEpINmNlUHZxd1ZvOUlqajRTVDUyVG43cXZMMStjTHFGRlhyYllReVMyeUdJc28zNDUxWW9JNDVCTkNBZG9tY2NSTk0zL1RpMTd2Ymo3NjZNL3VEOTlTeHFRRjZ1V3dCeU5kR2xwblBrSkM1MWMrZEtrazVqZ0I0K1BoeFVJZ2tXWWliemxiczdCVTV6eWhYN0tjYW5iRFlIWWF1cCtzSmJ0SytlT1REVUJiNzVKSkFnMUhIT294bERLenN6OS9QL0FHcWM1OUNwT0pGSnRuRzl1OTZKTXNlT3N4bnp1d0xVRGJaSkdFVDdsN3kyODBDUm5IS3NFWkZTNnR3QXpUY1IzWjdQYlFDOHRacFc1OW04NHBITGk1MndNZ0d2R3o4UGpXMjZQdkJhbHBBREVPTUh4TlJZbGkydzN4dEc0MDEwV2x6ZGRZRXphNE1OblA1eFc4U3ZtTUJSRU9aem5mNFZZV2xwdFROc09SVDIxZENXNVdMRnRqZjM3czFJNlNhbzJQQ1FkMVlWMUUyYzZzOGRRaG1lZHpLT2N0WGNpd3VtSXUyYlBJVm8wYThMbmhGRFYyZU5GSjd5U1FaeXVOMksxbEJyV1VvekhPZThVOGZlTVVZVzVGcElUL0h2RmF3UEtOR2pnZlRqNWo2cVdWZDZSc1hHTzJKL3dOSThNbkNkK1IyMFJuRkMzenFZK2Q3UDk2M244L25OUmtuQkp6UTYxR3hoN294cU5kZWNIcXFqaVo0d3A5Z0FweEoxbTJpazdZSDMrM2ZSMFhOK3lBODNtNS81YWdFYjNRbHRvOHdNWmM0MGpjT1hLb3YwbTd3eHVQUW1LZC9oVFIyeUV5dGp5b2s0OTNaa0xYbDAwN3NGWkxqR2QvUHphd0p1SDFSZjdoL2xySVo4K0YrUCsya3ZPakJLZGxJTnF6VGh3QjdoVXFONlFwMXQ0WGlrQzd5a3hVNG9sWXdGOVV0VFJQR3diM1ViTlZjSkx1NTVIZlcrdHNOeXNNTWU3dU5GajVKdGVUK3lrL0Exd2d4bFRuQTV3bjcxTlJSUWNFcmNiNGoxS1BEd3JHc0QrN2dQMzBzdzNuazJOKy83elFVRG4zZm4zZlhVQTJNRTVVYnkwV3I0MWJLR2oweERpR1BPMzFiSjFaTHBOcitwZHlpbmQzamZSZGY3UDlIMjBtTU5NazdGc2Urdm0zOW1yVzJrMDQ2NGszbSs0aXA3ZzJpU2pRb2djU2FOR1BEdHp2RmJHNzZOV2ZrY2JUR25mdnhVUzJsclBZdUpRVzhvdU5QOE5YQ1N4QXNkUlNWVGsrRlhLM0VDbnBFUnBzT0RjVzlMTmRGTmNCTGVhUzVtVzh3U29WVkdWeDhLdUxXMDZRdGJTVmYxcVR5NkE5S1lyL281M0hkZkNtbUhVcEhJeHczYWZqVHhkVWJVdmJvM2ZYVWppT1dMUG9xbTRVcExTWkhJN0xGQkxsREtxN2dlVmJOZ04vWWFNaWI0OFl5M0xIcXQrTkdhVlNJb1JuRGN4NEE5b3A1WmVGMlBwUnNwK3NWdTQvNFdmK3JkUlU4U3R1Ty9QNTlncnJFdzMram50cUpOWlJHSjFZN2F0Sk1EV0pXYnpjOTlXR2lOTGlaaVdFVXd3aDNiNmhzOENYeURTT29iQUlDNTdLZ2c2SXpxV0VtY2hpVno0WnFPR1ozMXlybEY4QjIxc09ZRy9ESG5VVVV0Z3lBdUR0VzFjVzhERlhkb3lSeHVDVkR0ekh4cUtLS1ZaRHJVc3krZHU4YzEwZXNUMnNrdHEwemFKRGpWckdQaFU1NmJ0SWJnTXVFNFJKMjFxaDZNNFI2bHUyUGhTRVdtTFl3TnIxeHVBRzdLdEZFV0RzU1pDd3h2TGJ2aFY1Y2lYWTNxeDZ3TmoyZXcxcTZUNlNoNitWVmt3dU1kKzZycVdTUVRTZ3JvWURHTTVyWHdrcmphTmpWMkxWd3dJT0F1RlBLZ3VzV2l1MmNGT0UxODFqTW83NFMyUGhTYmUzZUpTd0dxVUhIeHFPN2tZM0Z6MWhVK2lCak5KQ1kyZGZWak9EUlpZeEFjYmpKT0tqdDViNjJqaFRzRW1TYWovUjA3ZVFYVEd5QWxtenpPNFYxcTYycGtXRTVMcVZKcGpETHJTUUU2ZlU4S1E4azJ1UGFLbm5kTXh4djU0Tyt1cjZ0WDZ0Z0FkdzdmcnJwYTFjQWg3Z01nTy9zMys2b0hTTlVZdGh0SzR6UmQ3ZENXSDVOQlBLSUR5MHpNS20yZHhkbUlmcXlrd1B3clVuU1UyZzcxQkhGanhGY01xM1ErTlhRVkdsMXhLRjBLeDVIeEFyUWVpNUI0SEgzMWN3TEYxYU9WdzJwM0J4ajJWY1JUOUpqajg1a2c4M2wzbndwNDdqcGU0azE0OHpRT1ZRd3cydDdjeHh1ZEphVWZIZFFqc0xVV1Nhc2NScTFGMUpHWURMeUhPb2Y4WVA2YUZ2UHFNV0NUcGJTZHdxOE1OdGlaUWNPV0xGYXRad2lJdU43bFBFaW4wT1J2OUUwc1dsaExzOE1EenhpZ3FnTHdubFcxYVJ0UW1DcXVkMkt1dG5laUpUSWN4dU4xV01rajI3Z25BYUhtY2V0VjNJVWs0MklKMDhKMzFDVllIUzIvRmFSdTBqNjZZUjhJUE9qbG1WZkNsOHFXeU56RVZwMUJ5S3VOaFptR0poaUxFckVyNG5OU1J5blhlTExIeFk0anhObjdxdlVaVHROUTA5L2JSZEh0WkVjaDNqeDVNY3QzYjNWSWtKdHhHRHVhQzFSUjlsR1hiczQxNDgzblE4b3dLT3hUZDMxYlNiODdUdDlsVy84QWkvOEFUV1Y5UnZzcVdlY0JuOVgxaWVTMUNxTHhPQmhWOFhhb0RPdTFFZWM1cThsUTdGdFB3MDBHSUM3anVGUXZubmNnWTk5ZGJYelpKaW9IZlZ1RzVnay9BMHZaL3dEdGRFUHV6TVh6OVFvU3FXeTZodHpZeFhCTTQvZlVNS09xenRMZ2R2Q1VQd3J5bGhjdytNVWdjVmhiM3E1N3BvU0ttSFdKVTRjZVlOMWJNU3l2NG10YWFnYytrYTJNV2NlcWdxSkxpSmRRSEZxNTFxaTJiUlp6alZ4Q2crVlNNOG1ac0NyWUFIWjVZaHM1QjUxWStOdzMyVjM4Sm8yOFp6Yld5Tmo2VGRwcTBHY1lRZjZxWmdlK3B0WEp1ZERSNXV6TlcwbWs0RjRCcTk5SkFGM1JNK0Q3VFNQSnZYUytDUDNUUWFXTFk1NXZJUXRXa2NhTkkxdHEwYklIQnozazBVdVlqQzZZVTc4MWhnRFJnUG1zTXJXNnQ5U2VSMndidmZGRjNsU0lmelVGbXVtZGU0WUZlUVovL3NyaWoxZTFqVzZDajBYZE1MTjFrT2tTZWpRVzJsZDlIYXlZQjNWMFg0eXQ5bFBzMjBuU1JVczluTU1hTUdPUTd4U0FXK0lzWURBN3R5MXRHWVkxWXdLWjB0NUdVOGpwb3pTUWkzaDArZEljVUltNlNFN0NmYTRoVFVkVlpnc1NUNjl5MlBoWGxyNFEvUnQxeDhhTFNGWGYxcFcxR3NXMEpZZWljWUZUUEtOTzFQSVZ1YmZXczdtWGtjVjYzdXJsOEtPR3JtYUdUV0NkMWFzMXpvbVlJWDdHN2ErYlRKREt1V2lWNVBQcXhSMk1pd3UydlFvT0R1M1pGZk1MS1JWSjlJY1I4S0JGdTBKSjdlVkE5SWRLd3dmUlRpYXRTV00vU0wrdE1kQzFpQkxibzVQMk1lVytzMXF1NTVMcHYycjBRSFJmQktPd2ovaWM0cVZXdXpFaW9XNEJXL2Z2N2Q5ZEN4N2dGcyt6MjFIZGVrWHAxOGFjZUhmUTNuNVBINWM0clNzaWcrMnRLczBySHNRWm9OMVJvVVBiTTJta2ErdjRJblVlaHhOV21LM212VDlJNEZZdDdhQzBYd0dUUjFYQk5FOENlTllXVGFudVNpSVVFUTcyTk1zdDA4bmdOd3F4Y3FPS1k2L0VZcjMxY245a2Z1cFIzeUNyQWVyYWorbzFaZUxtcG9MZE5vK3J1cG9wY0gzWXBSOG1hR2hNMTgyV09OZlcxQ3RmVEhUam92YWtLazE1R3dtNlRrOWE0a3dLMDJkcmE5SHAreWozMTg0dXBaUERWZ1Z3SlhFU1BZS0loVm5Qalc1aEV0RjVaV2xiUGFhMDlYR29qZWF1cmR6cXh2VTk0cTRLakFXUEordXJFZlNZL0Q1TG8vUisrb1A3MGZiVUgrSFg3VFhSb0E5WTFOY0w2VG1ra2NZRERoclFlZnlmLzhRQUpoQUJBQUlDQVFRQ0FnTUJBUUFBQUFBQUFRQVJJVEZCVVdGeGtZR2hzY0hSNGZBUThmL2FBQWdCQVFBQlB5RnlmVmt6S1Y1SFUxQ01uNHNqL3Bjb3E1OW9DWWJuS0VNOHNaZXN6VS80VVIwTFl3V2RWbkdQMmw0aUs0NFA4QnZjYit5Q1hMTVk4OG5TSEljbVMxeFV3WmZjK29KWDBUZ1ArSjZzQ2FzOXVKbFRTemE3d3E4N1FSNGd6eXJHZWZkUXI4Zjh6c3VGT0M0bFhFb1NyMGNrb0huWmRzZWhsZUMvVU5RdVo2U0JpQTJmWGU1YzFYTmM5VFA4Y1J1OXpWN2JvT3hNQjFBOCs4WloxZCsyTlB6S3RCd1AyN01EWFlKRHJkeDVzcHVqVWRGbjZ1czZ4N1NYdDhhanJhT09xWnlMYVpXL0d2eEtoL3VZTnFCNVdPRjVLK1ljbU9mNkpmTTdQL0ZaOEk3UXJXWlEwd3BETkpoSUw3SFljN29LYnpFZjhTNG0yb1ZMcmcyamxTdHE4cm1VNzlseVBiYkQ1TnFxbWFDYnpReHhNZzFscThRREJiSWNOL3pHcXJ1ZkFGcWNuZW1WSGJsNWlpUmQ1MTFPSS9RMWdXenVRR216SmQ2aHo5UnFodlQzRmVTOVlLWFRpNnhJZnhpdjJaNlJtejdpYTBXS244UUZDTXZib05teDRLUCtKWFhyeEptanZCQ0RZamVIV1kzRnllbGZGL2NlY3RXRDE2dnFZT0ozcnRRTFJLM1B4UFMzZUpsVlBjeEErcVhndnRiUzBZR3JpNzU2azV5dmNDalN2VW9ocnJBYWIzcUo1TFEzUi9rR1dCUUhsRGhNRDB4dURDNThXcTlkRXp0ZHRjOHprOGlndURMZFdhand2blVzUkxGM1VlYUgyd2FyWmRLSnNjRGxqQU83TStQdUhoa1lmdU50djVQcUlzdU8zZEYwWHpLMU4wWDhzMCs2S3pKV2wyVjBydzVxWkJvaUNHMzBiamhOU0t6MDU5dEoyWWxkZFVRdmdkTm52WDNEQjFaNGVCUlVQVzBkZmF2NVNsODQ4emhYQk5JRWJNbTFVd1JFaGlQaS9jVzVya1BvRCtZVS9RM0U5d2I2QlVSdHZlTG85RWRWU3Erd2hUSXhPV3d0WmM0ankyOWU0dUhybHhyZkttUDVtZWtjVm50Rm54N2drcU9aZU8wUFk2UkxpaTI5SncyaFloc1A1ZEVVNmRmMHhFVk1XdjdqN0tKd2hqUGpQcmo1bUppQ1p1dDVua29wS214U1huQjAzTjZhZEZ3UHVZT3FCWUZMUmZUaUNGYjM1QlA2RXlmR3ErdVJLZ0prMUswbGo0YmpRSUQwUmU1cTRVV2xRTDQzYzNLUjBkR0tLcjVoWE9JQXZtWDFLSEYxNzZjdmVpbDM4TSs1ZHJ0UWtybGZvbVRkQzNWTk02L2x0Z0h6UzZXUDFBc014Mjk3VDZpdUc2UHVDdWdaSFpiejRxSTNFOUw0bzdyZ21GNTVRZmM3bVkyRktIOHg1bmQ2ZkQxNDhVK0QxM3RBUm9sZGdFcGtjY3E0N0VYK1N3amx5RDdPbEpHTmRGRzlTODdpUXVlZ0hlbVB0Q1VIelppYkd1ZkJGcFNZSWNtc2IxVm83U2hacE5xckozM3hMc253dG5PSmg5WlR3aGQ0MzN4Qk8wQXZxaGJ1bHNkWmJyazM4VGN5M2owM3ppV0pncjB1TFdLS1QrU0w2V0ZZa09VUTU5NHFoZDh5dE5uN2o1ZW5hSk9YV0Q3elVHQ2NTMXJIblZmd2xkQzZaanYxQ2FJWFdveTN5NWw5eEcycG1aWXd1bHpsNk9qQmdOdjIxbjdjV0pscElCaGU2cXNST1NNOFN1YmRJdzNzRm9xUTRJUTF6NTNmYVpEY0lCYWgzemNEUng3VFlMMHgvVzRWcXppK0l0bGF3K2Y3d3pLcjRQOEFER2JUaCs0MHV1aFc0eUJ4eWdBWlNMeHZiR253YStaWkZOMDNpMERHSzZrNzZqS3RmYkZWcUVzQ2JGME0wenJUREhlWnVPTjVhaVRwRmQyUDFMMEc2M1l3OXgxVm1TMS9va0IwbGZaR0Y0Mk1lS3JnMXFteVY4enZSL1g2aVRWSVorWVlDWU5CT1RxeThMbGlvS0w1d3Jyd3dLbHloN3lHQURpZnhPT0E2K0lTQWcycXZLY2N1WVZZMTdtUmc3QithbFo3WGU4TU1Sbm8xdVIvWk1nNU9kSFdMZ2tBL2Fqd3NiWTY5WXl5bC9nM0JDekx5cjhFZEx4VlNIeUJvNXpXb0tybGkwdkpkMlpaVElwNEhDUEFRL1Bjci9uRk9wTUw2THVqVUtGdHdNMUlLMUpLczRsMFpoNjZ2NVdDVmx1Y0w5UDIrc29pUW92KzZ4WEpDcFc3MHBqdkVnOXhMZE1hRUd1dEdrVTBpM1ZuR3VtbStJcDBIY0xKVFc0MVIrRWI0S0xFd3NpWThWMDBjYWw3eDZTS3llVzNsdmdtNzhVRkI2RXV1K1d2M0plWDBqUVZHNkV6RHlKVE0vb1RoOFJ4aDBsZmNMZzNkby9NclJxV20xL2JpVmwzUGlaR0N1cGpmeHY1aWxTd284UFdkT09HYnR1NklzdVN0YngyN2JpN3VTRjk0a3VobHZkbmczOEY2STl2ZEdsYXgyNGZLWklVM0szWThKaWNwb0d6KzVXQktxak5EalZzOFJKUENTQnROOTh4S0pRTEN6bnJOZk1HbXZydHF1SEFaT3NJS3l0UzZYc2NLbU14RjVXczdXOXkvZ2dGbXBod2M1YlEyWHpkeHNyVnkweGF3bWszVjRYbmNzRFF3VGNkOHpRVTNmOEFaS0tmNXpIeUlwWEtVM0xxeTFWWkZ6bWJaNmpOcDc2aUF6NG8yRDl4Q2tESTB6T3lZSzErQjA0VEZjSnZPT1NhakR5d3N0MjB0UUNRVTUvaElXckQrOE1RTlpZc0QyLzNFSERPT0FRY1FBa09vY3pwM0hpUlpjbFMrS0d4aTM4RjVkQWVxQXRGeG1TNlcxWnVFWmtIK1MzbkVlamhMT21xTXZ4RDFCOUF6T0N0dldjaVRoRnZDbmlIVDFTMkFsSVZqZWZCQ213bVRiOTRlanhCT2NVcTVMZnhpWEZ2TWVBU01OeFV4d1U3Z1dEMHdsSExkVUowMWRrY052SXpwWStZVzdWMmxIeExERHRNR1FycmlVSmVLaUU3aDFmdVhKR0ZzVzVnWDNKeW1NN0RNek0rYldueVNaNkR0aFdqYjlUeXNuamNWSDRzcEJnSFZ4ZTVJdnBaVWVVbzBkeS96SG1HOURmQVRIRXdwbEk2c0ZOSVNzWktOWEY5WXgyeTA5SDNNQ2RGRlBCMmhDbkswelUvUmNIVjlXV3I3Q2o1bG1QNENIeENwUXZQWG81bGNHS1dUNmJyM0VHcFZSZXlqak1MWWFzWXBmQlRLQjJ4ajBVamRpaE0ydExGY2JOaUZQaTB2ajNHTTE0Uzh5dm5KYlZUL1gzbU1EbGV1eXh1V1dZcnNBTE8wR0JMYjdvN0dLZzJsYThqSXpOem42WnRKSUw1Qk1rdmtZQ1FjNVlmYVl4bHltV2JlSmVPKzA3OFZDenQvQk9nOWQyWUI3ejRoeDdsZEp4Rjg5L3Faem04NTJ3UVNibkNnWVpxQnVWU2hUMHpONnJtNnAvaUNiMkJ6S3pjYzdoNGxNMlpBeEsyUGNKZjg3dERJbk1mZFpyYXIrN1g0VFBhTzdSS3BMYjdaZFFoVnZPKzh5QzVNdjhBT2tRNllEbDFxNnJiS3p1U1hUemhMQ3g1NlJQNW1YN3ZxRGVyRXh6RG9pVmZ0djVQbUVFU3dZNEJETUJ3THlEbjRoRDhEZG1BcjVsZmM2Wk5lUEtwM1lCVzJicUlucWhTeDIyVlhkNVFibEt4eWhzOXlzQm0xa0xyY3pablJkOVExUmJFc3VxRHYwVTVoZTNxQmd2SUkzOGtzaWpiZXEraVplZXpRajhNRWIzeS9xZUdmQ3oxbGNNNG9QNVkrM0xhVTdrUU1CT1pTeHlvMWVOWjhFNGJ4dGFmaDBRWDZGNStVSE9HcU5RUDlSZ2d2Y0Z5cEdlOEJkWUhuY2ZOeXgxSnd3ZWdIbTZnemtySmZnZjRndTh5SmE4STQ0bFdXWGlLS2pkSzlaU1FkajEyZzFwTWFocnJLZklIcDcrSm0xZXNyOVRwcHN4ZXd3N3BGK3FtdFB2UDNGQ1krV2RNVlZ1VlJZOXVDVUdLdXFlTVFkMnpEM3o1ZWFkMTNsNi93b3E5ZmptTllDQmRSRGN2NWdKcytaeS95Vk1UZ3FZeUFPR0s2bkdNVjh6cy9oVDVwRExDNUMvZk1hMjViTDdsdk5HMjNYVHJCcjgwMUc5Q21FeEtuSThqcUVWVU9wR1ZJWWNsZk16WkZpYjh3UXpMbURzcFdaWkZaZ0hQUVB5Wmx1bE5vWjRhZUw3OUl5SmtXSjFDOFJvMnZWMGVqb2ZFM1JzWFNPOXl1REc1Z3NYL0FOM01NYXBnR2Q1aEFwNmpndnBabVRJY1VucVBDQ2xGMVdQdUpCa3BuTjl3UVlqQUZHWXJ2TmFrN1N2cXgxUnhtTTRxZEdXWkdWK1ppR1l3aDFFaFpGakZGZlJpc1ZjbHMvcUdmN1ZKdUVoVVZqVjUxT3VnMjcvRUVab2dPUDBBcUM1emF0eTdkNVNMZDVpZlVyeVJiSHNPejJ6UXFNeTZ6SHhmKzRJemcvWi94cUVENDVueEdHanNXcDRBNVlnbml4Y1RvQkE4WlIzOEZ3MUV6cTBUNS9ZcS9jLzlQMGZNVVozejQvcUN1bFl2dEw1OG5lZ21GaDNxTkt5QnVyUmhIYjFtTnNOWWdPS1lSS2pOdnd3bTNEMUprdVkzRzdGZ2lneFdMM0lzc2N6dmZYMG1UZFp2SHdpKzZGOVN6NW5iK0lVYlhFem1NcTZkSWxzLy84UUFKUkFCQVFBQ0FRUURBUUVCQUFNQUFBQUFBUkVBSVRGQlVXRnhnWkdoc2RIQjRmRHgvOW9BQ0FFQkFBRS9FQytFN1V4VytBZVBzNHh1bjlGUUUwNjc3WVBPNS84QWkveklqc2dCOUhOYmE4TXBpR0lYQVZKbEJUQUVwZEdIa3FQdVZ3eGQyeEM4bzd5MWgwQ2Q2UHhoUE9DdHJSOGVZVW51dm5sd3JGLzlmOFRHMW1hWjllUStBNGI1VGIxUmRENFVtSGppMlkvamdZUE1BSUhBTTBIWC9aanVPRHdhODllRkN1M00xaWtWR05RMk14VGozeWJCaUFYT2VNU2ZValk3UW93Z3crRUQxaVlDZjVjTnlIOHlqbjd6MnFQK1RrbzUzbGZXODBnOXAvcGwyQkhhT0JSazkwREg1TlNDMko0Sm9kUU1EMmZwQjFIRmR0R0tsMW8wOWY1ak5qcnYrWS84Wnpld3JlSG8rRENFaGJ5UFovcW9lTUc0UHRPbWN3aGtXd1hZanoyd1c0MnNxdWlLN0dKV1NhTndsWVYybUtTS0F0ajBBR3VxbnprQmdNUHJGYS9sTFFHdTlDeUhFblhZSkJBU2xpdGpDVXdUTytHdE42ZGFpOURCRWRBbnB5clpoS01Hc21zcW91R0p6c1YrRE5hdktqOUhEeXpLOUVGU1VkbTFNTTBSQ0NQZmJQZ3hqWTdtdnovaHdwbGdaOHM1ZkxodUZhNytlMnVEa3h4MnI4QktyaTE3eUF5Q3J4WXdPUSs5dmo2SWUweEVhaW9RNnNUc3NLMllTejk2QzZrRDJ4S3RYbGJqYVd1TUhEd2d5aXI3b01LdTJTTytuQ1RyTWR0MFVDSzRVQU1neWFva2ZYQTZ0OUZmVmc3VWNDcjhzWlBTakhhY3p0eGdpTllBVG90T3VvMk9JNkk1eHdCeXQvV2NjM3J6K3NJN0NtalpkT0pNWVhzYVVlQ2ZoYnlBdHZua1huOGd4T0oyK0duT0JzTXEzbXE5bWI3NXQranJFZ25PTmovTUNJbGx0SXFTN05Za0JvR2FORWllcjhjY2FDcFdoczl5REhaNEZvV2pMdFY5RmNBTXFKZ2xxSGZRNDJ3ZTQyZlpzSVpDb0RJcUlFVkp1WmgrTHBPT0EwQWRSYzB3QkwxS2k0M3N5bitGRmliVmRNRENERkVxazBLV2J5bGtyWmg5NGNPL2dLb0lNN0VJN0IzMEtJOEpsMUU5RmZnT0EzN2JmMU13OHJ5VzBnNVFhTVFuUGVrOVZnMm5uaVg4eG05RkJiRVFxRFhEQkRha3FqWnNHamxHOHVHa2ZzUCtzeEtNUGV6RS9NcEVoVWdmTHJKTmZsb3ZiaHdwYmZOU1JzRjNZZXlZVkRhL0habDZ2RHdBcndDQkVXbHEwSE5mOUdFTkJDSWVYWFhFR1lCQkUxTDQ4dFlycDFIZVlXZm5IeU5OQlEvOWVwa3VQZVAxRk5mYkJ5b2hwS05rckRuUmNDNzdVQjJPWGFBQjB3ZlJUUHk0Tytia25laGpXK09LbnQ2Z3Q5Sm1WUjZQSEtSakR0d05nNnBqc080S0Vpa3RRRURLUGFuS0tBODlsd2JFNUJDS3hRTHdZeXNMc0V2bC9wd0hVSmg5ZEJWZHdQWmtnSW1ocW9rVTZxa1NEbHNEc01BaWxBN1RneGk3REFOc3ZibnZwa2V0TlRLWUJ0TTZPdVVhamlxSzBFWFVLY09sQUpReW13ZEhJWnpDMTgxUnhIblk0d21ETUFzODJ3WnZpT0c2K1ZNNVZ3dk1PcmtjTjJCSFlCMzZqR1ljSTdQQlR4dGpvMzhqUGtmM2hPREFPZW4yQzhOZkdUSnV5dkJ4Z21RWUhna1c5TVNqUXFGZDk0ZFVnSUJON1Z3MU1NTk1oVEVWNFpjM0Y5OUZpTXVtVmtESFBZemdQZFhvbWNLTWdXR1Uvd0FyRExONENIVk9KYVM3U0ZnSWhSUmpzSXZ5d2RVSWp1a0MrNEY2T0YyREljNEExcEJ3TG81TytZS0F3Vk9MUXE2TXhSK2VBYVRJS2R5SGJHOW0waWNJSkVUbnZsS2lFSWhVcDZMdkpUY2tTN0xBWDBZdXRYVUkxSW14S1BKbUs1SVpVT2xRMEc4RWFQdW9Hc0UyaERaZ2tHRUY1QlpWMFllUW40OWFCOFl0SHJYL0FOMXcrc1BZa3QvMFg5d2NjQWFsenF1bzR5MURMM1NBL2JqWlhWLzJhUHpKOFN3Um5zTllZeWpBSzV3VWIwWSszQVBFTnI3QUYrT0dNUFFnT0p0VVI0VitKUzVTT25kVVh1clF5S3dkVENGMWpzeGVBVFQxNUJTdFpnVFpxdG55T25GVXNEZTc3MUh3NWhodkluQ2xSODMyWEFMTFVHd1ZtenFEWmdDM09TaWxJYUFUS0lXR3VUaHE4bTVLL1RuaEJYa3pXQVpRVFZqbmloQkZNR3R0NlRRd0pyYmtxbG5qZVlPdGNNZVVWQXU0R0FPTEc1YUpEa21acGFtd3J0ZHVsRnprTGZaSW9nZXpOYk9nNHhHYkVXR3VOS3RpTkVkTXQrMy9BQTNYTGhMYUsramNEYlFkc1VsdDRwdlFDczZPT3ZJRzFRN2JKcG9ITmxicGlYVGpvSUpOSEkrTks0QnE0SHJvUHU3QjJ4bWNxS1loNENMS29URDFkOEV3U0FvNUZEcThwSUhzTVJmQUNjZjhaK3VHVkdJajB6OHh0VHVpSU42ajV5RWZneTdMRFFEbTJKNnFmQ29Wd0JSY2NXR29hTnJ0Tk54Unh5dnUxRlUySUs2WXRlS05oTVU1NXVvdzM0Z1BMQWxUZUh6Rkx1SmRjaVVPVTlWUXZaRTZNamxoUlN5amFMWjhQR1h1UXFRZEZYVndnODVkWFJ4WU5jVERhQUxkRUJob0wrNGR6TElJN21BSm9qbE5EdCs0S0NFZFdWTmFBYmhUbzJIY1ROcGx4NExYMUl5bEIxRGZiaU1TMUxwVkZlN2RUQjBxbFJpN3dvYzNsZy82MGNBWStBRjFybEk3SkZ4VlU5Vitzbk1zcUtwL3F5UkY4WjhJanpUbTN2a2FMYk1FZmtoa2hzMUozczZkQXdDY0o2bDNOWTV6VHRKdEdrTkphM05zV0V4b3QrbFpsekpOZVE3WTBLakdKRlVzRTVIZWhBd01hQUlaZjQ1ME1XL2Y4K0VYRlo4djVvYXJWRmUyQUZtSzFDTW5ZNUhzdEhSUXk4VG9TYnkyVitNQm5NZ0I3OVV6aHBUbWhsdXh2TEJIQ2ZMRkZrd3pmL2NQNFdGMTRJd29vN1BYamVGekNIL296SkRpNFN3Y2FsNGl3OGVFVUgrOGM5N2pKNkVLQUlJSkRUdVhEWU9RMnJvK2dwMXVGMjZhNHFnbG5rQkpqdjY3Vzg3SWxpbGx4WFYvbXlSQkpJUEl4MnhZdThVS2RLMXhoaDBITlNhdTZiaTh5NHF5QkJRVFNYblF0bFo3S1ZNcUZ1R0lmVUlDclhoYlpvTU1TMmlvandnSTJqTGlza0ZXbnRRZVBJc3lMUnlMSGFLYWpWR0pTZkhQY0thUFVUSjRvSEtUMFk2TWNDeDV3YlY4VGtIemtrQnU4bmYxZTRPRmFxN2Q0b2EwSnVZSjNCRGo0bG1IRUdPUk9SSEE0SmxQYlhsUHQ2c2ZhTzhlKytQMEZ6WDVaWmRMMEJBY0lLYlJGOG9QYm01TnVmaEdXblo5dVRJNnl1elU2YUlHalE2cWcvTE9ZMEdwTytHU3RPaUpkVkJzeUF2T2RqYW5oRWNoREcxWTFGRkpnc0VncHdaTnB0ejh2YjlGbzBZYmloOUJvazZKUVBNY09lL2dlcUtSMDZodUNwWnVRdkZDZ2dGMmF1QWF6RWlBUmdnaERWWE95VGtxVE1OS0ZXeC9MVjkvczBSdVUzYVlpSE84clNPSWlzWUliaWgrYml4aE5KVFdpR2E1dWtxWUxLQmprdzlTWXFtUk5FZVJpQkUyUFVjQ1d4QkJveTQ3VGRrMEhNNERVZmQwazgyMlRhdy92eGpiQVBTYTV4Tk16eUdDZ0JTR3U5eWhGd3ZLNGRjWWxIeHFWRDVuREY1MmpJSFh2MFpVQjRtRkF0QUo5WURnUE1xVkNjRHhjZjJjUVhJOG0xTWpoZXpScnRmN0RrRncyMDV1TFVGQ0ZTVENlS1I1UDdVcmsxaXNuZ1dhaGRRaVhrdHhFSmNBaEJBNTZtNGRxYktpS0RCN2FVd0JRaTJUV29tNUx5c2NTTURGSGNGeGZxeUNBWVJDQWdtdHR3UHBVSUhrSWcrTWJvaFJzYlhOWExoSmhBRXlHdDNYbW9YR1Jib2hiM2hXMTJ4YUk5RWUySlRUQ0Rzc0llTUw3S3VVZHNYQ3ZVMFpMcURzNXdxb3BGbFZnVncwTlZnYTZpSGNadFg4YlB3emgyQmNJWWN6cjJCTGcwSWZrVXplSHZJZFhRWG5FbmYvQUFEd2M1bTVnV2RJQzFvaThoalEzYUt0Z056RnZ6Ymw0MktnN3IwY3BEUjBuU0wzeDRMeVBVL0p3SnY3RWpwU2RYdGpSM29raTBGZG83N1lOZkE3a0lMNG5Gd240SFpuVVNjT25BaHU4VzZwb1k0WElLT2h0UmJweEFNMnJJOXcwa3M2NWMwNkppTk5lOTR4SzVxTHkrOElDQzdId0tSdXBEQm1ac3g1REtMQzhKZ1N4bFRQaFBqRG9WV0VEU3prQ2l2QXhUaEpyOHFiSUhwWmhPZ0IzVk9RUVBCMDV3UUNZQTlEVWJZdnJwaTUwaExHMG1pckVPOVQ3REp6K1lpczYrOGNwTVRaL1pIOEN5VVlqRkloMi81bFBvME5hQ1BOY1l4d3hrQ1UwNkIzamF2d2tHc01zVU5iVjdQUk1QbHlubWRoMk91T3VmcWVEQkdHUm1Fb3VYN1lBaUp0MU1nQWRWdWRDUks5c1V1M0xEczBMOHJnUjNCVEh0VEg3SXdEZXdvUEdMQWZlNnZZcGhwNlJLaEk2UndyNkpZRUdWaGhwd29LNVlwTjVjYW0vZktiNUI1N1kyNDJaQVU4SWpIdmdzVHRyUm93Wmw5ek9pZW9LN2ZoeEVXWlN4QktpMTc1THcvZ3JBa0JvRDZESWVqTDJFOHpaeUJkZEZBMS93Qk0yME1oY3JPY3VWYkE3bi9neXJFRis1a1dBcGJkSDBxRk1CNm0yeXRKM0NobFhTblhhalJzZ0JjdmxKaGJlZ2FuYkIvTmx0WUpBQU1qQ2FFbVVTWHFHc1dvdEc0cWpwZVhmZ2xJZS9HUElwYU5nd0JLK3B2YzJzSmh5YlFWWHBqbGtwQy9lM05wYUI4NXlmaWQxWDl4NVBGUC91UndoT0UrVDJqaHhKV1Ric0pCcmxDam11SCtPTUxRZ25hQUJzT202Tk1idGUrQjE2MEVTUjg0MVo2Uk1TT05zMmJ5dGVCV2wzdzZUQnpyQUphY0x6aTZhcWF2V3NQM051RGFwSnNaVjV3RVVsUkJWV0cvdVVIcytxVFIvZUEyR3lOK1RnYlhtSzhsUEE4Wm8zVmNoTE40Y09Cb1RUM2hsOU5NRDYxbk94d2RITFp5OTFoeDJpWCtzSEtyaFluMzRuSk1xeVBibFdhYW5qa3lFYTJYdE5TSDNpelI0dGs1MXRxQnJRUWNhbkNJSW1naXRJRDN5azgxbVBDZ1FDc0xoK1Z3ZWpZbGc2UlovSG1jY015SjhkSHhrcUJBQVBJTGZXS2lqYWkvVlF3NDNJQmZ6ak9nUDZNNVpFekt0Y3lyZDllSVBNdVpWT1dzQm1uY3VXQmlWWWxTNlYvbU9VREdNdkJnVHFlaW5EbmhVbDd1YWh6ejkyd29Kc1BiSDZqZytvYlYxbkhoRmxEN3hmemFmM3dERVp0OEduZXNOdEQySnhDZytqa243MTFkNFIrT1FGZURxKzZrSEdrQTR2OEFtNEV5NVRLVlZWY21kZEFZOERGNU84WTRxMjdLaE83eml1d0VONUo3UngxTlZKOTRzckErM2djZEFTYzZzdDBKajV4bHdDKzdUeUJPeTJ6aEhCTndpcTkvMTVPemlqb1pBMHZMVGxIbU96L0dQd04wNlpQdHpodyszOFpvQWRSSHhvSDVoOTUrcE82VS9jQUFaQkpQTUEvV0JHUGIrSEdOeEhxcVhHemxjVE9CNkRSUHZPRTNJbzVwRWNLN3M0eFh2MlRoMjlEQWt0Q1YrT21jNHA2S0YwT0E5dUtUWXhlUlJ6V29Gejd3aU8zczluaXRiMk1MeVg5aHNJazU4bmhpV0JBV0lpL3hpNDhyVUVZOVY1RTNpWkJSd2hLNS84UUFOeEVBQVFNQ0JBSUlCQVlCQlFBQUFBQUFBUUFDRVFNaEJCSXhRUkFUQlNJeVVXRnhnWkd4d2VId0lETkNrcUhSRkJVd1VtS0MvOW9BQ0FFQ0FRRS9BT0E0aUZINERJUWdoRXlySUFtLzhvbW0zeFdhbysyZ1RhWUd0K0FXNktjT3NpUU4xbmRvQ2hwK0lIaFBDU2laV1VuVlMzUVhLeXZkcW10YTNSRHRjYkJHcXphNkRuT054Q2ZZb01hMjRDbVJxby8yWmpWRnhPcUR2K0FRcEU5b29BTkVCQUFqaElEZ2VHWjd4YXdRcGplNmdONFA3UzZzWEt6Z2FJT1VoUHBEVU9sTXpBUWVJbFJLSmhDVG9za1BBS2RxZ1NzeFFVQndnb05CTUZXQVBrbW15bUVITUtPWk83U2ZrWTJTYm9PYkpCMktCQ0V5VkFRRUpqWHVNTnVtZEZZeHdsd0RSNG1FT2h5UitZUFlxdDBSaUdBbGhEdkxYMlRtT2JWSWNJamdmekFuYThSS0FUQ1M5T3VDczRpNVdla1BGQ280OWxxSE1QYVI3U05LbjVxQzI5Z20zbGNzb05JVkREMWNRL0pURWtyRFU2UFJvdDFuR3hQZjROSHpXTHB2emN4aE1IdTE5WEhRSnBCT3g5WHVQOFdUM0dBUDcrRHY3VlREVWEyR0hQRTl4M0N4bURxNFI4RzdkaWorWUVjcmpZb3RlTnBSTGdnSEdVVGxWeVlDYUFBUVhLS0xWemFUZEF1ZWRoL0NEM083UVI3VUxLVHE1Y3RnVEdGczZLSmNxV0ZMdTBxVktuZzhOSXNTSko3bS93QmxDYnVkWWdYL0FPbzJhUEVyQ3ZaQm9WQjFlN1p2ZEozSlZacnFEOGxRKzVnZWpXcHJjN2cwYitmd2NxaEdjTUczeCs5dENxbEptSm9taTdRNmVCMlZXbTZuV05OK29KbEJuaWczdVFmc0NuVkhHd1F6dU5rMFpnVVcyTUZBTUd5Qk93WFg3a0E2YnB3NnlGS1AxSU1CY2gwVlJhMGt1S2Roc1BTQURidTNWRnJDOE5BV0lMWDFZT2trbnlaOVVBNW96RzVBemVibmRuMkM1YnMyVnR5REE4WGZxY2ZMWlU2WWFCbXVSdnVpR25WVmFSYTZSY0g3anlPM2NVRDkvZm9mZFB3ZUdyazFDd0VrTEhkR25EMERVQWp2dXFaRUJYdVpUSGRZd2haTUFoT1RMU3BQZXYxYXB6Mk5pNkZWcGZBVHFGWm9FTUprVG9VNm5WcG1YRExQZWl4d2E3TUNJRXFwUmN5c2MzMFdFYTNudDgxVXcxWjJhMnJTUFV1WCtPNDFadDJtblhZQllXZ1d2YVNRWUI5eWJsUUZBS3FnY29vaW1KdjkzVk9OdkZkTUNjQTVNYVcweS8wVUdiSU5JS2JUcWxwZkZnc0xocW1KY1dNaVltNVdJdzFhZzZLZ2hERDF3d3Z5bUN1alcweGcydWdhWHNtdG8xak9XZlFMcDlsTnBwaHJCb2RsaCtyWGFja0dSc3FXUHBVblpzVTlyRzk3aUdnbllYSUVyRllMQzQwQXYyN2xWcUI3T3lGV3gxSnZTNDZPTTVpMHZIY1FEQmp4Q29zTEhncXN3YzdLZERtYis2NFFlNHR6RFVnTy93RFRMRWV5cDFHMG41aG8yVDVzZHY2RlRtYUMwMjRZcXExak9YdWRmTDY2QkFrNi9mM0o5bFI3STkxMHJqNlRzTzZsSUpOb1IwUUtrSU9tMG9GekhTSjlGbVlBbTEzaHNOY1k4MVQ2VXJVcWZMWUJIa3FmU2Rhbm9Bc1ZqSzJMQXp4WkhXVlhiUnhyZVZpbU5xTkJtSE5EaEkzZ2czUTZReFlFWnloU3pOa0xGZEdZYkVZaW5pNmpaZlNuS2J5SnNkN2dvVkdiS3FCV281cDhENFJvVm1PczVUUDdYLzA1QTNnQ0k5MkU2MjNZVlZxdndsSU1wMk9wc1NCOHhLSFNHSmZZRWVqU2ZqWlZpS2pCV2I1R08vNW5ZYkJVeG1PVWZlMzBIcVYwaGk2bFhGRTAzRUFXRUp6M091U2k3U1Z6T3BsaUFzd0lpRTR0dW11RTNNcCtVNkpnQUZ5Z1dMS3l5RFFHU0RkWlhKdE40TW9VV21ubkx4ODFSZlF3d09URVQ1cHVLcE9CbXVCNktwV3dRazgwRXJDWStobnM0SHZDcTByY3ltWkduaEhjNzVGWVZndlVjTE4wblVlQU80S3JWTTFVdWRZK0lMVDdpeFFjSDZYL0FITy9vTEN2Qm1tN1EyKzRzUElMSFl4dUdZYUZNeTg2bnUrdndDSktGMm9OSklEVVdPejVkRTdMZktzdHBRZ1NvYVJvZ0dxeWE2d1RIVTJ0T2JWRGxrTExUVFNCTW9GMEdVQTl6Q2dJYVoxQ2pxTERZL0ZZVjhzZDZIUmY2eGdhek9XUVdqeUJIc2c3QjZzckFlcmgvRjArdGdXZHFwbThwUHhzc1Iway9LVzBSbEhmdjlQUkVxeUVRZzUwMlJMODBuVmZwS2JNSU5rU29JZEJUTkNuQzBvTWxxTElDdU4xMWh1Z1hvekVKeEEwS3p0RFNKWE1ibGhTRUlpQ2kvWUJOY1NVL3NsQUFtQ2cwRUdBdTZFMGxOYWMwSTZGTlRTWU4xNHBqb1RuaHdUS2dBaFpnVkE0UWdXM1F5NURhNkVaQ3BPV0ZCMVFKVXBwdW45bEJOQXBza3JlVTB5VUNXVkxJNkZOUUZrRXh2VlZRQUJGaEFRUUxsbVdZSGRDbUU1b0NQY3JBSVNvVUZOQkRrL3NsVXdNNnFHVEFUZ21DNko2NWxHSUthQ2diUWdFSFd1cWhMeFpNZm1GMWI4SE5NSXZuaENBVUttMEV3YktTRENkMlZKQ0Z3b1FiQ3l1SnNFS2JsRUNDVUxhSzUxVmxQZ210Z3FFQW80UW80UWh3bENGWXNpVUtiaUpoTm9rWEpoQnJGTFJvczRWeXBBV1pYSU1wbHlxdXF5akxLZHFwUktsQ1lRNFFneHgwUXcxWTNpRnlHdDdUa0dVOWhLTHcyMWdqVm5VeXVhU2lWekFzeml2TkNJNGJLbHFGVTFVamxxSlVCUXRreHBLYlFKMUtMYURMT0s1bE52WmI3bzRsK3hqeUNkVm01dXVZU3N4S2xjd2JJRjVRYUV3aFJkRWlGTnVBMEtwNnA1dWoyQXBSMFEwNGYvRUFEY1JBQUVEQWdRQ0NBUUdBUVVBQUFBQUFBRUFBaEVESVFRU01VRVFVUVVUSW1GeGdaSHdNcUd4MFJRZ0k1TEI0VUlWTUVOaWd2L2FBQWdCQXdFQlB3RGhLc281S0ZINU5Rb0t5V3VtdERmaFhaQXVobmRZV1FheGwwWG5nd2RwTkNab1ViQUlYTWtMS09YKzNBZFpORWFLUUZCSTVCQXNib2k1eEdxUHdxRlpNa0hSQ21ZdWdBQVlSMENtVEN2eEFuaU9BNEFFb1FGSE1yTUJvRkpjakk0RVdRQ2hqVTEwb0VrSm02T2dVT25rZ3diM1dYa1ZrY2dTMlJDTUUva2xBSWlOVUNNdGtPSitGR1FVVEYwSithSUpKaE5iQ3lIYTZhanNta3ZkQUNNalJORXEyeUQzRGRaMCtwVFlKZFpQNlR3Z0pEQ1hFY2hLL3dCV2ovalBxUHVxUFN1SGNZcUF0OGRQVUpsUnJtU3d5RHk0UjJTbTNIQ3dVOXlMdGlSOVUrTXZOTTAwaTZESFR1Z3gzY3NuTXBvQW1FNjBJT0lNb21VNzRZMitxMEVyTzJZVmF2U3c3QytvWUFWWjFicEU5cXpkUVA1Y1ZoSHNEVFRlQkk1NlIzQWFsRVFPWGsxdjF1cWJaa3g5RDgycGxlclJ4QjZneHo1SDM2ckI0eG1LWkkxR29VelRsTUQyQ0NFSE1PcGhBTjJSYnpXVzhUL0NNRFZTVG9GTHp5UWEvY3JJM2Nwb2FKaFAyV2J1UWNTRlZmUWMwZFcxd003bVI5QW5hQlBxNVpJVlNvL0dZbU5XdE1BYzNmMXFyUkRlMEp0LzJJMWNlNExFTWVTSzFJOXJudTduSElCVVhOck16c0hvSlBtNXlMc3JYSGw0ZlZ0MVJrdEx6L2w5QjlSMzZoVTZqOFBWRlZtbzEvbjM0Rk1xTnFVZzltaFFkWkYwcUdjbGxiSzdBVHJRZ1hiaFIzcUJ6VUJBUUNuYWpnQWgwbFdKQURSQ1pYeEZVRnpyRFlLdG5ESE9KMFdGQlpSa2F4ODNuN0tXdUphTEQ0UjNOYmQzcXVzYmxMM1dCRW51YnMwZU82cVZYVkhPTGJOT3cwVE13K0ZZZXJtYmxOaVBjK0kzNWhSM2UvY2owVGNWaUtINlllUUFWZ2VrUmlLblZtOXJJU21SQlRRTnlpWmtoSFZOVGlSRUtaWW1XWktZUytZRnJLcTB0aE54Tk1reTRDRHpDWlVaVW5JWlRDMHZibElNbUZTckIxRnVUKzFqWFA4QXc3L0JVc1JSYUJKL3lidHNBdnhET3JnVE9WMjI1S3hWWU9wUERRYmx1MndGZ2dTSlFKR2lvRTljSVRTODdlN0t0TnllUzZJTVl3QkFpWVVBb0VGcWxnTWJsVlhzWUpjcWIydkVnck93bUFicnBBMVB4VGhKUk5XbTBpZm1WMEE5NUZRdmNkdDFpWE1jd2pOTnVmOEFTeEdGaW5GSUVrOGxoc1hpY0lYWkl2elZCaFkremlxYll3UXhFYndmNFZlcUgwbk01ckR2Sm81aHFJUDdUQitTaG9jV25RRWovd0F2MFBxbjAzVmFaWWRYUVBCN2Z1RkdSeER4ZEF3Vmc2VG4xRFVpdyt2OWFsUU52ZnVCNnJFZkVSNUxvekF2WlhGVWdnRGZtZ3JvU29LSWxxQWVWazNJQ2YwZFRxVkM5OHo0cC9SMUorc3JDNFNuaHB5VGRPNXJxV3RHWmx2TWhmZ2NJZFdCZFpEdkJVTVhVYlRkUWJZT2hHaTlVcHc5Y3NONXVPL1lqekNEQm9MaVAzTSs3VkFpNW1SNVBBNzluQlU2VE1YVmM2cGNDd3ZCK3hoRG83RE11UWZNaVBsZFVSMWJ6UlBpTnJiK0E1N2xQT1VGeDkrOWZRTEFZVmxMRGdQQUpkY29NQWJBQ3l4TUpyQm1uZEZvMVZLQWJwemR3bWJ5RkxDTGkveVFickt1Sm51OUVETGtBQ0lsRUFpQVVTUS9MbEtkVHI0azl2RDVmTlB3MVJodFJKODFTYml6MlRTTUxGNE43NmR4SEk4bFNxbWVycUNEcmJuemIvSVdKY1l5Tk4zY3REM2tiRUtqVHkwd3h0d09SRGg2RzRXWEpyYjlyZnVWaW1FQVZHNmkvdWIrWldEd3h4TGhWZUlZTkJ6OS9NcG9CY2ludWExc3VOa3lzM3FnL1c2Yk9XK3FiY3dpQktadmRaK3hsaS9OTkxqWW96TjBQaTdvWDZlNkJaSVJpTGQ2SkFGa01QVnFFNUdrd25OTGJFUVVTQXNSZ3NOaW1FUGI1alZEb3ZHVWFtY09EajRrR1BGUml3WWZTSjhtbjUyVEtXTmY4TlBMNHdQcGRVdWptRHRWam1QeS92elRiQlR5VWxPRGN2YVRXc05PTnVBZ0p6NGRBVkloelpWUnZhQjdsUU15RVh3U0VIb09VaFNGSURsVTZyT2VyY1NQQ0ZjaFpaQWxRQUVTQm9nMElRaW0yQldseXJGRUNFMkMyeUc2RXAwVG9xY0dZQlRtNXQxU1psbEduZFpWb2hCNGFxRENNeGRGd3phSVBNYUFlU0NCSTBXeU1JYUltU3RvUkJJaE5zRk1vSTZwcWNibFVTWFNVMTVQQ0dyS0ZsUUppNnpHRm1NY0pLRXdzelJyWkJ6WEN4UlFzMU5UVktBa0tJUTFWa0hCV09pcGpJbk15OFpRdWcwSXdGZFNWSldaeUxNNGtYVFdBR1VVTkZvWlVnQk5lRFpCd0dxNjF1MTAxemp0Q01IVW9RTkF1MFZsalZPTXR0dHhFSUFJRlNnVTYvQ0VKRndnU2lzN0cybGRjMDJGMTJ6M0xJTnlnQnNGS0RTUWcwSVFOQWlTZ0lhRUxBb0N5RjBBb1FVcVZtUWpkZGRTYnV1dmNmZ2FwcW01TUlOQkZ5U2cxb3NBZ3hzSUFsZFc3VTJXVm83ME81YW0vQnFPNjJXeFFObGRBMlFLc0U2czBMckh1RmdoVHF2MWQ2SVllbnZkQnJXanNpRUpRQ0RDVjFKSHhMOU1kNnpGT0R0aW1teUhjaHJ3Q2l5MENpeDRDSlU4UC8vWiIgaWQ9ImMiIHdpZHRoPSIxOTkiIGhlaWdodD0iMTU5Ii8+PC9kZWZzPjwvc3ZnPg==", + "description": "Displays the latest flow rate telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'flowRate', label: 'Flow rate', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flow rate\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"square\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:hydro-power\",\"iconColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#2B54CE\"},{\"from\":10,\"to\":30,\"color\":\"#3B911C\"},{\"from\":30,\"to\":50,\"color\":\"#F77410\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"36px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#2B54CE\"},{\"from\":10,\"to\":30,\"color\":\"#3B911C\"},{\"from\":30,\"to\":50,\"color\":\"#F77410\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:Zmxvd19yYXRlX3ZhbHVlX2NhcmRfYmFja2dyb3VuZC5wbmc=:Zmxvd19yYXRlX3ZhbHVlX2NhcmRfYmFja2dyb3VuZC5wbmc=;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Flow rate card with background\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"m³/hr\",\"decimals\":0,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "liquid", + "fluid", + "flow rate", + "fluid dynamics", + "velocity", + "mass flow", + "volume flow" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/flow_rate_chart_card.json b/application/src/main/data/json/system/widget_types/flow_rate_chart_card.json new file mode 100644 index 0000000000..f36b6a61ba --- /dev/null +++ b/application/src/main/data/json/system/widget_types/flow_rate_chart_card.json @@ -0,0 +1,34 @@ +{ + "fqn": "flow_rate_chart_card", + "name": "Flow rate chart card", + "deprecated": false, + "image": "tb-image:Zmxvd19yYXRlX2NoYXJ0X2NhcmQuc3Zn:Zmxvd19yYXRlX2NoYXJ0X2NhcmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj4KICA8cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiIC8+CiAgPHJlY3Qgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiIHg9Ii41IiB5PSIuNSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjEiIHJ4PSIzLjUiIC8+CiAgPHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIKICAgIGQ9Ik0yMy40IDE0LjRhMS4zIDEuMyAwIDAgMC0xLjMuOWwtLjUgMS43YTIgMiAwIDAgMC0uNy4ybC0xLjItMS40LTEtMWExLjMgMS4zIDAgMCAwIDAgMmwxLjIgMS4zYTIgMiAwIDAgMC0uMi43bC0xLjguMy0xLjMuM2ExLjMgMS4zIDAgMCAwIDEuNiAxbDEuOC0uMy41LjUtLjYgMS43LS40IDEuM2ExLjMgMS4zIDAgMCAwIDEuNy0xbC41LTEuNmMuMyAwIC41IDAgLjgtLjJsMS4yIDEuNC45IDFhMS4zIDEuMyAwIDAgMCAwLTJMMjMuNCAyMGwuMi0uM3YtLjRsMS44LS4zIDEuMy0uM2ExLjMgMS4zIDAgMCAwLTEuNi0xbC0xLjcuM2EyIDIgMCAwIDAtLjUtLjVsLjUtMS43LjQtMS4zaC0uNFpNMjEuNyAxOGExIDEgMCAwIDEgLjkuNSAxIDEgMCAxIDEtLjktLjVtLTQuNCAzLjFjLS44LjYtMS43LjktMi42LjloLTEuNHYxLjNoMS40Yy45IDAgMS44LS4yIDIuNi0uNmwxLjYuNS40LTEuMXYtLjJjLS43IDAtMS4zLS40LTItLjhabTguMy45YzAgLjUgMCAxLS4zIDEuM2gxLjRWMjJoLTFabS04LjMgMS44Yy0uOC42LTEuNy45LTIuNi45aC0xLjRWMjZoMS40Yy45IDAgMS44LS4yIDIuNi0uNyAxLjcuOSAzLjcuOSA1LjQgMCAuOC41IDEuNy43IDIuNi43aDEuNHYtMS4zaC0xLjRjLS45IDAtMS44LS4zLTIuNi0xYTQuNiA0LjYgMCAwIDEtNS40IDBaTTM0LjcgMTV2MTBIMzNWMTVoMS43Wm00IDQuNHYxLjRoLTQuNHYtMS40aDQuNVptLjctNC40djEuNGgtNS4xVjE1aDVabTMuMi0uNVYyNWgtMS43VjE0LjVoMS43Wm0xLjcgNi45di0uMmMwLS41IDAtMSAuMi0xLjVsLjctMS4yYTMgMyAwIDAgMSAxLS43bDEuNS0uM2MuNiAwIDEgMCAxLjUuMy40LjEuOC40IDEgLjcuNC40LjYuOC44IDEuMmwuMiAxLjV2LjJjMCAuNSAwIDEtLjIgMS41bC0uNyAxLjJhMy4xIDMuMSAwIDAgMS0yLjUgMWMtLjYgMC0xIDAtMS41LS4yYTMuNiAzLjYgMCAwIDEtMS44LTJsLS4yLTEuNVptMS43LS4ydjEuMmwuNC43LjUuNWMuMy4yLjUuMi45LjJhMS42IDEuNiAwIDAgMCAxLjMtLjdjLjItLjIuMy0uNS4zLS44bC4xLTF2LTFsLS40LS44YTEuNiAxLjYgMCAwIDAtMS40LS43Yy0uMyAwLS41IDAtLjguMmwtLjUuNWMtLjIuMi0uMy41LS4zLjhsLS4xIDFabTguOCAyLjIgMS43LTUuOGgxbC0uMiAxLjctMS43IDUuN2gtMWwuMi0xLjZabS0xLTUuOCAxLjQgNS44VjI1aC0xbC0yLTcuNGgxLjZabTUuNCA1LjcgMS4zLTUuN0g2Mkw2MCAyNWgtMXYtMS43Wm0tMS40LTUuNyAxLjcgNS43LjIgMS43aC0xTDU3IDE5LjNsLS4zLTEuN2gxWm0xMSAxLjR2NmgtMS42di03LjRoMS42VjE5Wm0yLjMtMS40VjE5YTMuMiAzLjIgMCAwIDAtMS40IDAgMS40IDEuNCAwIDAgMC0uOSAxbC0uMS42aC0uNGwuMS0xLjIuNS0xYy4xLS40LjQtLjYuNi0uOGExLjkgMS45IDAgMCAxIDEuMy0uMmguM1ptNS4yIDZWMjBsLS4xLS43YTEgMSAwIDAgMC0uNS0uNGwtLjctLjItLjcuMS0uNC40Yy0uMi4yLS4yLjMtLjIuNWgtMS42YzAtLjMgMC0uNS4yLS44LjEtLjMuMy0uNS42LS43YTMgMyAwIDAgMSAxLS41IDQgNCAwIDAgMSAxLjItLjJjLjYgMCAxIDAgMS41LjIuNC4yLjguNSAxIC45LjIuNC40LjguNCAxLjR2NC4yYzAgLjMuMi41LjMuN3YuMWgtMS43YTMgMyAwIDAgMS0uMi0uN3YtLjhabS4zLTMuMXYxaC0xLjJsLS44LjEtLjYuM2ExIDEgMCAwIDAtLjUuOWMwIC4yIDAgLjQuMi41IDAgLjIuMi4zLjQuNGwuNi4xYTEuOCAxLjggMCAwIDAgMS41LS43bC4yLS42LjYuOC0uMy42YTMgMyAwIDAgMS0uNS42IDIuNiAyLjYgMCAwIDEtMS44LjZjLS41IDAtMSAwLTEuMy0uMy0uNC0uMS0uNy0uNC0uOS0uN2EyLjQgMi40IDAgMCAxLS4xLTIuMmwuNy0uOCAxLS40IDEuNS0uMmgxLjNabTYuNy0yLjl2MS4yaC00LjF2LTEuMmg0LjFabS0zLTEuOEg4MlYyM2wuMS41LjMuMmguNGEyLjUgMi41IDAgMCAwIC42IDBWMjVhNC4yIDQuMiAwIDAgMS0xIC4xYy0uNSAwLS44IDAtMS4xLS4yLS4zLS4xLS41LS4zLS43LS42bC0uMi0xLjJ2LTcuM1ptNy44IDkuM2MtLjUgMC0xIDAtMS41LS4yYTMuMyAzLjMgMCAwIDEtMS44LTIgNCA0IDAgMCAxLS4yLTEuM3YtLjNjMC0uNiAwLTEuMS4yLTEuNnMuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS0uOGwxLjQtLjJjLjYgMCAxIDAgMS40LjJsMSAuOGMuMy4zLjUuNy42IDEuMWwuMiAxLjV2LjdoLTUuN3YtMS4yaDR2LS4xbC0uMS0uOS0uNS0uNi0uOS0uMmMtLjMgMC0uNSAwLS43LjItLjIgMC0uNC4yLS41LjVsLS40Ljd2Mi4zbC40LjcuNy41LjguMWEyLjMgMi4zIDAgMCAwIDEuOS0uOWwuOS45LS43LjZjLS4yLjItLjUuNC0xIC41LS4zLjItLjcuMi0xLjIuMloiIC8+CiAgPHBhdGggZmlsbD0iIzNGQTcxQSIKICAgIGQ9Ik03MyA4MC42Vjg0SDU2di0yLjlsOC4zLTkgMi4xLTIuOGMuNi0uNyAxLTEuNCAxLjEtMmE1LjIgNS4yIDAgMCAwIDAtMy45Yy0uNC0uNi0uOC0xLTEuNC0xLjQtLjYtLjQtMS4zLS42LTItLjYtMSAwLTEuOC4yLTIuNS42LS42LjQtMS4xIDEtMS40IDEuNy0uNC43LS41IDEuNi0uNSAyLjVoLTQuM2MwLTEuNS40LTIuOSAxLTRhNy41IDcuNSAwIDAgMSAzLTMgOS4yIDkuMiAwIDAgMSA0LjctMS4xYzEuNyAwIDMuMS4yIDQuMy44IDEuMi42IDIuMSAxLjQgMi44IDIuNWE3LjYgNy42IDAgMCAxIC42IDYuMSAyMC4yIDIwLjIgMCAwIDEtMi44IDQuNyA0MiA0MiAwIDAgMS0yLjIgMi4zbC01LjUgNi4xSDczWm0yMC4yLTExLjV2NC4xYzAgMi0uMiAzLjgtLjYgNS4yYTkgOSAwIDAgMS0xLjcgMy40Yy0uOC45LTEuNiAxLjUtMi43IDJhOS44IDkuOCAwIDAgMS02LjIuMiA2LjkgNi45IDAgMCAxLTQuMS0zLjRjLS41LS45LS45LTEuOS0xLjEtMy4xLS4zLTEuMi0uNC0yLjctLjQtNC4zdi00LjFjMC0yIC4yLTMuOC42LTUuMWE5IDkgMCAwIDEgMS43LTMuNGMuNy0xIDEuNi0xLjUgMi42LTIgMS0uMyAyLjItLjUgMy41LS41IDEgMCAyIDAgMi44LjNhNi42IDYuNiAwIDAgMSA0IDMuM2MuNS45IDEgMiAxLjIgMy4xLjIgMS4yLjQgMi43LjQgNC4zWm0tNC4zIDQuN3YtNS40bC0uMS0yLjdhOCA4IDAgMCAwLS42LTIgNCA0IDAgMCAwLS44LTEuMyAzIDMgMCAwIDAtMS4yLS43IDMuOSAzLjkgMCAwIDAtMy4yLjFjLS41LjItMSAuNi0xLjMgMS4yLS4zLjUtLjYgMS4zLS44IDIuMi0uMi45LS4zIDItLjMgMy4ydjUuNGMwIDEgMCAyIC4yIDIuOGwuNSAyIC45IDEuNGMuMy4zLjcuNiAxLjEuN2wxLjUuM2E0IDQgMCAwIDAgMS44LS40Yy41LS4zIDEtLjcgMS4zLTEuMi4zLS42LjYtMS40LjgtMi4zbC4yLTMuM1pNMTAwLjIgNzh2Nkg5OXYtNy40aDEuMnYxLjVabS0uMiAyaC0uNmMwLS41IDAtMSAuMi0xLjRsLjUtMS4xIDEtLjhhMyAzIDAgMCAxIDEuMy0uMmwxIC4xYTIgMiAwIDAgMSAxLjIgMS4zbC4yIDEuMlY4NGgtMS4zdi00LjljMC0uNCAwLS43LS4yLS45bC0uNS0uNWEyIDIgMCAwIDAtLjktLjIgMiAyIDAgMCAwLTEgLjJjLS4yLjItLjQuMy0uNS42LS4yLjItLjMuNS0uMy44bC0uMSAxWm00LjgtLjctLjkuM2MwLS40IDAtLjguMi0xLjIuMS0uNC4zLS43LjYtMSAuMi0uMy41LS41LjktLjdsMS4yLS4yIDEuMS4xLjguNS41LjguMSAxLjJWODRoLTEuMnYtNC45bC0uMi0xYTEgMSAwIDAgMC0uNi0uNCAyLjIgMi4yIDAgMCAwLTEuNiAwYy0uMiAwLS40LjItLjUuNC0uMS4xLS4zLjMtLjMuNWwtLjEuN1ptNy41LTNoMS4xbC40LS40di0uM2MwLS4yIDAtLjQtLjItLjUtLjEtLjItLjMtLjMtLjYtLjNsLS43LjJjLS4yLjEtLjIuMy0uMi41SDExMWMwLS4zIDAtLjYuMi0uOGwuNy0uNWMuMy0uMi43LS4yIDEtLjJsMSAuMWMuNC4yLjYuMy44LjYuMi4yLjIuNS4yLjggMCAuMiAwIC41LS4yLjdsLS42LjUtMSAuMWgtLjh2LS41Wm0wIC44di0uNWguOGwxIC4xYy40LjEuNi4zLjcuNS4yLjIuMi40LjIuNyAwIC40IDAgLjctLjIuOWwtLjguNWEzIDMgMCAwIDEtMSAuMiAzIDMgMCAwIDEtMS0uMWMtLjMtLjItLjYtLjMtLjgtLjYtLjItLjItLjMtLjUtLjMtLjloMWMwIC4yLjIuNC40LjUuMS4yLjQuMi43LjIuMyAwIC41IDAgLjctLjIuMi0uMS4zLS4zLjMtLjVzMC0uNC0uMi0uNWwtLjQtLjItLjUtLjFoLS42Wm04LjktM0wxMTcgODQuOGgtMUwxMjAgNzRoMVptMi44LS42Vjg0aC0xLjJWNzMuNWgxLjJabS0uMyA2LjVoLS41YzAtLjUgMC0xIC4yLTEuNGwuNi0xLjFhMi43IDIuNyAwIDAgMSAyLjItMWwxIC4xLjcuNS41LjguMiAxLjNWODRoLTEuM3YtNC45YzAtLjMgMC0uNy0uMi0uOWExIDEgMCAwIDAtLjUtLjVsLS44LS4yYTEuOSAxLjkgMCAwIDAtMS41LjggMi44IDIuOCAwIDAgMC0uNiAxLjdabTguMi0yLjJWODRoLTEuMnYtNy40aDEuMnYxLjJabTIuNC0xLjJ2MS4xaC0uN2EyIDIgMCAwIDAtLjcuMWMtLjMuMS0uNS4yLS42LjRsLS40LjYtLjIuNy0uMy4yLjEtMS4yLjQtMWMuMi0uNC41LS42LjgtLjhhMiAyIDAgMCAxIDEuNi0uMVoiIC8+CiAgPHBhdGggZmlsbD0idXJsKCNhKSIgZmlsbC1vcGFjaXR5PSIuMyIgZD0iTTE0Ny40IDcxLjhoNy4ydjguOWwtMy42IDIuNi0zLjYtMi42di05WiIgLz4KICA8cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiCiAgICBkPSJNMTUwLjcgODMuOWMuMi4yLjQuMi42IDBsMy4zLTMuMi0uNy0uNy0xLjQgMS40Yy0uNC40LTEgLjEtMS0uNHYtMy45aC0xdjRjMCAuNC0uNi43LTEgLjNsLTEuNC0xLjQtLjcuNyAzLjMgMy4yWiIKICAgIGNsaXAtcnVsZT0iZXZlbm9kZCIgLz4KICA8cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IgogICAgZD0iTTE2Mi42IDc5Ljh2MS4xaC0zLjF2LTEuMWgzWm01LjItNC40Vjg0aC0xLjR2LTYuOWwtMiAuN3YtMS4xbDMuMy0xLjNoLjFabTcuNSAwaC4ydjEuMmgtLjFjLS41IDAtMSAwLTEuMy4ybC0xIC42LS40IDFjLS4yLjQtLjIuOC0uMiAxLjJ2MS4zbC4xIDEgLjMuNi41LjRoMS4xbC41LS40LjItLjZhMi41IDIuNSAwIDAgMC0uMi0ybC0uNS0uMy0uNi0uMWMtLjMgMC0uNSAwLS43LjItLjMuMS0uNC4zLS42LjVsLS4yLjctLjUtLjJjMC0uNCAwLS43LjItMWwuNS0uNy44LS41LjktLjJjLjQgMCAuOC4xIDEgLjMuNC4xLjYuMy44LjZsLjUgMWEzLjcgMy43IDAgMCAxIDAgMi4xbC0uNiAxLS45LjYtMS4xLjJjLS41IDAtLjkgMC0xLjMtLjJsLS45LS44LS41LTFjLS4yLS40LS4yLS45LS4yLTEuM3YtLjZjMC0uNyAwLTEuMy4yLTEuOC4yLS42LjQtMS4xLjgtMS42LjMtLjQuOC0uOCAxLjMtMSAuNS0uMyAxLjItLjQgMi0uNFpNMTc4LjYgNzcuNlY3N2MwLS4zIDAtLjYuMi0uOS4xLS4yLjMtLjUuNi0uNi4zLS4yLjYtLjMgMS0uM3MuNy4xIDEgLjNsLjYuNi4yIDF2LjRjMCAuMyAwIC42LS4yLjhsLS42LjctMSAuMmEyIDIgMCAwIDEtMS0uMmMtLjMtLjItLjUtLjQtLjYtLjdsLS4yLS44Wm0xLS41di45bC4zLjMuNS4yYy4yIDAgLjMgMCAuNS0uMmwuMi0uMy4xLS40di0uOWwtLjQtLjRhLjguOCAwIDAgMC0uNC0uMWwtLjUuMS0uMi40LS4xLjRabTMuMSA1LjN2LS41YzAtLjMgMC0uNi4yLS45bC42LS42Yy4zLS4yLjYtLjIgMS0uMnMuNyAwIDEgLjJsLjYuNi4yIDF2LjRjMCAuMyAwIC42LS4yLjgtLjEuMy0uMy41LS42LjdsLTEgLjJhMiAyIDAgMCAxLTEtLjJsLS42LS43LS4yLS44Wm0xLS41di45bC40LjRoLjlsLjMtLjR2LTEuM2EuOC44IDAgMCAwLS44LS41bC0uNS4xLS4yLjQtLjEuNFptMS4yLTUuMi00LjIgNi43LS43LS41IDQuMS02LjYuOC40WiIgLz4KICA8ZyBjbGlwLXBhdGg9InVybCgjYikiPgogICAgPHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuMSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTg4IDEyNUgxMnYtMWgxNzZ2MVoiIGNsaXAtcnVsZT0iZXZlbm9kZCIgLz4KICAgIDxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii45IiBzdHJva2Utd2lkdGg9IjEuNSIKICAgICAgZD0iTTEyIDEzMy44aDkuM2wxMy4zIDkuMkw0NSAxMTIuMmwyMS42IDIyLjYgMjMuNy0yMi42IDcuMiAxIDE0LjQgMjkuOGgxNC40bDEyLjQtMjYuN2g5LjNMMTU3IDEwMmwxMi40IDQuMSAxOC41IDEwLjMiIC8+CiAgPC9nPgogIDxkZWZzPgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJhIiB4MT0iMTUwLjciIHgyPSIxNTAuNyIgeTE9Ijg0IiB5Mj0iNzEuOCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgogICAgICA8c3RvcCBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmZmZmY7c3RvcC1vcGFjaXR5OjEiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIxIiBzdHlsZT0ic3RvcC1jb2xvcjojZmZmZmZmO3N0b3Atb3BhY2l0eTowIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGNsaXBQYXRoIGlkPSJiIj4KICAgICAgPHBhdGggZmlsbD0iI2ZmZiIgZD0iTTEyIDkwaDE3NnY2MEgxMnoiIC8+CiAgICA8L2NsaXBQYXRoPgogIDwvZGVmcz4KPC9zdmc+", + "description": "Displays flow rate data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'flowRate', label: 'Flow rate', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'm³/hr', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'flowRate', 'm³/hr', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flow rate\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m³/hr\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#305AD7\"},{\"from\":10,\"to\":30,\"color\":\"#3FA71A\"},{\"from\":30,\"to\":50,\"color\":\"#F36900\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m³/hr\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m³/hr\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Flow rate\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:hydro-power\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"margin\":\"0px\"}" + }, + "tags": [ + "liquid", + "fluid", + "flow rate", + "fluid dynamics", + "velocity", + "mass flow", + "volume flow" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/flow_rate_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/flow_rate_chart_card_with_background.json new file mode 100644 index 0000000000..58d56e2023 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/flow_rate_chart_card_with_background.json @@ -0,0 +1,34 @@ +{ + "fqn": "flow_rate_chart_card_with_background", + "name": "Flow rate chart card with background", + "deprecated": false, + "image": "tb-image:Zmxvd19yYXRlX2NoYXJ0X2NhcmRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==:Zmxvd19yYXRlX2NoYXJ0X2NhcmRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiIHRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDYgMTU0KSIvPjwvZz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNMjMuNCAxNC40YTEuMyAxLjMgMCAwIDAtMS4zLjlsLS41IDEuN2EyIDIgMCAwIDAtLjcuMmwtMS4yLTEuNC0xLTFhMS4zIDEuMyAwIDAgMCAwIDJsMS4yIDEuM2EyIDIgMCAwIDAtLjIuN2wtMS44LjMtMS4zLjNhMS4zIDEuMyAwIDAgMCAxLjYgMWwxLjgtLjMuNS41LS42IDEuNy0uNCAxLjNhMS4zIDEuMyAwIDAgMCAxLjctMWwuNS0xLjZjLjMgMCAuNSAwIC44LS4ybDEuMiAxLjQuOSAxYTEuMyAxLjMgMCAwIDAgMC0yTDIzLjQgMjBsLjItLjN2LS40bDEuOC0uMyAxLjMtLjNhMS4zIDEuMyAwIDAgMC0xLjYtMWwtMS43LjNhMiAyIDAgMCAwLS41LS41bC41LTEuNy40LTEuM2gtLjRaTTIxLjcgMThhMSAxIDAgMCAxIC45LjUgMSAxIDAgMSAxLS45LS41bS00LjQgMy4xYy0uOC42LTEuNy45LTIuNi45aC0xLjR2MS4zaDEuNGMuOSAwIDEuOC0uMiAyLjYtLjZsMS42LjUuNC0xLjF2LS4yYy0uNyAwLTEuMy0uNC0yLS44Wm04LjMuOWMwIC41IDAgMS0uMyAxLjNoMS40VjIyaC0xWm0tOC4zIDEuOGMtLjguNi0xLjcuOS0yLjYuOWgtMS40VjI2aDEuNGMuOSAwIDEuOC0uMiAyLjYtLjcgMS43LjkgMy43LjkgNS40IDAgLjguNSAxLjcuNyAyLjYuN2gxLjR2LTEuM2gtMS40Yy0uOSAwLTEuOC0uMy0yLjYtMWE0LjYgNC42IDAgMCAxLTUuNCAwWk0zNC43IDE1djEwSDMzVjE1aDEuN1ptNCA0LjR2MS40aC00LjR2LTEuNGg0LjVabS43LTQuNHYxLjRoLTUuMVYxNWg1Wm0zLjItLjVWMjVoLTEuN1YxNC41aDEuN1ptMS43IDYuOXYtLjJjMC0uNSAwLTEgLjItMS41bC43LTEuMmEzIDMgMCAwIDEgMS0uN2wxLjUtLjNjLjYgMCAxIDAgMS41LjMuNC4xLjguNCAxIC43LjQuNC42LjguOCAxLjJsLjIgMS41di4yYzAgLjUgMCAxLS4yIDEuNWwtLjcgMS4yYTMuMSAzLjEgMCAwIDEtMi41IDFjLS42IDAtMSAwLTEuNS0uMmEzLjYgMy42IDAgMCAxLTEuOC0ybC0uMi0xLjVabTEuNy0uMnYxLjJsLjQuNy41LjVjLjMuMi41LjIuOS4yYTEuNiAxLjYgMCAwIDAgMS4zLS43Yy4yLS4yLjMtLjUuMy0uOGwuMS0xdi0xbC0uNC0uOGExLjYgMS42IDAgMCAwLTEuNC0uN2MtLjMgMC0uNSAwLS44LjJsLS41LjVjLS4yLjItLjMuNS0uMy44bC0uMSAxWm04LjggMi4yIDEuNy01LjhoMWwtLjIgMS43LTEuNyA1LjdoLTFsLjItMS42Wm0tMS01LjggMS40IDUuOFYyNWgtMWwtMi03LjRoMS42Wm01LjQgNS43IDEuMy01LjdINjJMNjAgMjVoLTF2LTEuN1ptLTEuNC01LjcgMS43IDUuNy4yIDEuN2gtMUw1NyAxOS4zbC0uMy0xLjdoMVptMTEgMS40djZoLTEuNnYtNy40aDEuNlYxOVptMi4zLTEuNFYxOWEzLjIgMy4yIDAgMCAwLTEuNCAwIDEuNCAxLjQgMCAwIDAtLjkgMWwtLjEuNmgtLjRsLjEtMS4yLjUtMWMuMS0uNC40LS42LjYtLjhhMS45IDEuOSAwIDAgMSAxLjMtLjJoLjNabTUuMiA2VjIwbC0uMS0uN2ExIDEgMCAwIDAtLjUtLjRsLS43LS4yLS43LjEtLjQuNGMtLjIuMi0uMi4zLS4yLjVoLTEuNmMwLS4zIDAtLjUuMi0uOC4xLS4zLjMtLjUuNi0uN2EzIDMgMCAwIDEgMS0uNSA0IDQgMCAwIDEgMS4yLS4yYy42IDAgMSAwIDEuNS4yLjQuMi44LjUgMSAuOS4yLjQuNC44LjQgMS40djQuMmMwIC4zLjIuNS4zLjd2LjFoLTEuN2EzIDMgMCAwIDEtLjItLjd2LS44Wm0uMy0zLjF2MWgtMS4ybC0uOC4xLS42LjNhMSAxIDAgMCAwLS41LjljMCAuMiAwIC40LjIuNSAwIC4yLjIuMy40LjRsLjYuMWExLjggMS44IDAgMCAwIDEuNS0uN2wuMi0uNi42LjgtLjMuNmEzIDMgMCAwIDEtLjUuNiAyLjYgMi42IDAgMCAxLTEuOC42Yy0uNSAwLTEgMC0xLjMtLjMtLjQtLjEtLjctLjQtLjktLjdhMi40IDIuNCAwIDAgMS0uMS0yLjJsLjctLjggMS0uNCAxLjUtLjJoMS4zWm02LjctMi45djEuMmgtNC4xdi0xLjJoNC4xWm0tMy0xLjhIODJWMjNsLjEuNS4zLjJoLjRhMi41IDIuNSAwIDAgMCAuNiAwVjI1YTQuMiA0LjIgMCAwIDEtMSAuMWMtLjUgMC0uOCAwLTEuMS0uMi0uMy0uMS0uNS0uMy0uNy0uNmwtLjItMS4ydi03LjNabTcuOCA5LjNjLS41IDAtMSAwLTEuNS0uMmEzLjMgMy4zIDAgMCAxLTEuOC0yIDQgNCAwIDAgMS0uMi0xLjN2LS4zYzAtLjYgMC0xLjEuMi0xLjZzLjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEtLjhsMS40LS4yYy42IDAgMSAwIDEuNC4ybDEgLjhjLjMuMy41LjcuNiAxLjFsLjIgMS41di43aC01Ljd2LTEuMmg0di0uMWwtLjEtLjktLjUtLjYtLjktLjJjLS4zIDAtLjUgMC0uNy4yLS4yIDAtLjQuMi0uNS41bC0uNC43djIuM2wuNC43LjcuNS44LjFhMi4zIDIuMyAwIDAgMCAxLjktLjlsLjkuOS0uNy42Yy0uMi4yLS41LjQtMSAuNS0uMy4yLS43LjItMS4yLjJaIi8+PHBhdGggZmlsbD0iIzNCOTExQyIgZD0iTTczIDgwLjZWODRINTZ2LTIuOWw4LjMtOSAyLjEtMi44Yy42LS43IDEtMS40IDEuMS0yYTUuMiA1LjIgMCAwIDAgMC0zLjljLS40LS42LS44LTEtMS40LTEuNC0uNi0uNC0xLjMtLjYtMi0uNi0xIDAtMS44LjItMi41LjZhNCA0IDAgMCAwLTEuNCAxLjdjLS40LjctLjUgMS42LS41IDIuNWgtNC4zYzAtMS41LjQtMi45IDEtNGE3LjUgNy41IDAgMCAxIDMtMyA5LjIgOS4yIDAgMCAxIDQuNy0xLjFjMS43IDAgMy4xLjIgNC4zLjggMS4yLjYgMi4xIDEuNCAyLjggMi41YTcuNiA3LjYgMCAwIDEgLjYgNi4xIDIwLjIgMjAuMiAwIDAgMS0yLjggNC43IDQyIDQyIDAgMCAxLTIuMiAyLjNsLTUuNSA2LjFINzNabTIwLjItMTEuNXY0LjFjMCAyLS4yIDMuOC0uNiA1LjJhOSA5IDAgMCAxLTEuNyAzLjRjLS44LjktMS42IDEuNS0yLjcgMmE5LjggOS44IDAgMCAxLTYuMi4yIDYuOSA2LjkgMCAwIDEtNC4xLTMuNGMtLjUtLjktLjktMS45LTEuMS0zLjEtLjMtMS4yLS40LTIuNy0uNC00LjN2LTQuMWMwLTIgLjItMy44LjYtNS4xYTkgOSAwIDAgMSAxLjctMy40Yy43LTEgMS42LTEuNSAyLjYtMiAxLS4zIDIuMi0uNSAzLjUtLjUgMSAwIDIgMCAyLjguM2E2LjYgNi42IDAgMCAxIDQgMy4zYy41LjkgMSAyIDEuMiAzLjEuMiAxLjIuNCAyLjcuNCA0LjNabS00LjMgNC43di01LjRsLS4xLTIuN2E4IDggMCAwIDAtLjYtMiA0IDQgMCAwIDAtLjgtMS4zIDMgMyAwIDAgMC0xLjItLjcgMy45IDMuOSAwIDAgMC0zLjIuMWMtLjUuMi0xIC42LTEuMyAxLjItLjMuNS0uNiAxLjMtLjggMi4yLS4yLjktLjMgMi0uMyAzLjJ2NS40YzAgMSAwIDIgLjIgMi44bC41IDIgLjkgMS40Yy4zLjMuNy42IDEuMS43bDEuNS4zYTQgNCAwIDAgMCAxLjgtLjRjLjUtLjMgMS0uNyAxLjMtMS4yLjMtLjYuNi0xLjQuOC0yLjNsLjItMy4zWm0xMS4zIDQuMnY2SDk5di03LjRoMS4ydjEuNVptLS4yIDJoLS42YzAtLjUgMC0xIC4yLTEuNGwuNS0xLjEgMS0uOGEzIDMgMCAwIDEgMS4zLS4ybDEgLjFhMiAyIDAgMCAxIDEuMiAxLjNsLjIgMS4yVjg0aC0xLjN2LTQuOWMwLS40IDAtLjctLjItLjlsLS41LS41YTIgMiAwIDAgMC0uOS0uMiAyIDIgMCAwIDAtMSAuMmMtLjIuMi0uNC4zLS41LjYtLjIuMi0uMy41LS4zLjhsLS4xIDFabTQuOC0uNy0uOS4zYzAtLjQgMC0uOC4yLTEuMi4xLS40LjMtLjcuNi0xIC4yLS4zLjUtLjUuOS0uN2wxLjItLjIgMS4xLjEuOC41LjUuOC4xIDEuMlY4NGgtMS4ydi00LjlsLS4yLTFhMSAxIDAgMCAwLS42LS40IDIuMiAyLjIgMCAwIDAtMS42IDBjLS4yIDAtLjQuMi0uNS40LS4xLjEtLjMuMy0uMy41bC0uMS43Wm03LjUtM2gxLjFsLjQtLjR2LS4zYzAtLjIgMC0uNC0uMi0uNS0uMS0uMi0uMy0uMy0uNi0uM2wtLjcuMmMtLjIuMS0uMi4zLS4yLjVIMTExYzAtLjMgMC0uNi4yLS44bC43LS41Yy4zLS4yLjctLjIgMS0uMmwxIC4xYy40LjIuNi4zLjguNi4yLjIuMi41LjIuOCAwIC4yIDAgLjUtLjIuN2wtLjYuNS0xIC4xaC0uOHYtLjVabTAgLjh2LS41aC44bDEgLjFjLjQuMS42LjMuNy41LjIuMi4yLjQuMi43IDAgLjQgMCAuNy0uMi45bC0uOC41YTMgMyAwIDAgMS0xIC4yIDMgMyAwIDAgMS0xLS4xYy0uMy0uMi0uNi0uMy0uOC0uNi0uMi0uMi0uMy0uNS0uMy0uOWgxYzAgLjIuMi40LjQuNS4xLjIuNC4yLjcuMi4zIDAgLjUgMCAuNy0uMi4yLS4xLjMtLjMuMy0uNXMwLS40LS4yLS41bC0uNC0uMi0uNS0uMWgtLjZabTguOS0zTDExNyA4NC44aC0xbDQtMTAuOGgxWm0yLjgtLjZWODRoLTEuMlY3My41aDEuMlptLS4zIDYuNWgtLjVjMC0uNSAwLTEgLjItMS40bC42LTEuMWEyLjcgMi43IDAgMCAxIDIuMi0xbDEgLjEuNy41LjUuOC4yIDEuM1Y4NGgtMS4zdi00LjljMC0uMyAwLS43LS4yLS45YTEgMSAwIDAgMC0uNS0uNWwtLjgtLjJhMS45IDEuOSAwIDAgMC0xLjUuOCAyLjggMi44IDAgMCAwLS42IDEuN1ptOC4yLTIuMlY4NGgtMS4ydi03LjRoMS4ydjEuMlptMi40LTEuMnYxLjFoLS43YTIgMiAwIDAgMC0uNy4xYy0uMy4xLS41LjItLjYuNGwtLjQuNi0uMi43LS4zLjIuMS0xLjIuNC0xYy4yLS40LjUtLjYuOC0uOGEyIDIgMCAwIDEgMS42LS4xWiIvPjxwYXRoIGZpbGw9InVybCgjYykiIGZpbGwtb3BhY2l0eT0iLjMiIGQ9Ik0xNDcuNCA3MS44aDcuMnY4LjlsLTMuNiAyLjYtMy42LTIuNnYtOVoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNTAuNyA4My45Yy4yLjIuNC4yLjYgMGwzLjMtMy4yLS43LS43LTEuNCAxLjRjLS40LjQtMSAuMS0xLS40di0zLjloLTF2NGMwIC40LS42LjctMSAuM2wtMS40LTEuNC0uNy43IDMuMyAzLjJaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNMTYyLjYgNzkuOHYxLjFoLTMuMXYtMS4xaDNabTUuMi00LjRWODRoLTEuNHYtNi45bC0yIC43di0xLjFsMy4zLTEuM2guMVptNy41IDBoLjJ2MS4yaC0uMWMtLjUgMC0xIDAtMS4zLjJsLTEgLjYtLjQgMWMtLjIuNC0uMi44LS4yIDEuMnYxLjNsLjEgMSAuMy42LjUuNGgxLjFsLjUtLjQuMi0uNmEyLjUgMi41IDAgMCAwLS4yLTJsLS41LS4zLS42LS4xYy0uMyAwLS41IDAtLjcuMi0uMy4xLS40LjMtLjYuNWwtLjIuNy0uNS0uMmMwLS40IDAtLjcuMi0xbC41LS43LjgtLjUuOS0uMmMuNCAwIC44LjEgMSAuMy40LjEuNi4zLjguNmwuNSAxYTMuNyAzLjcgMCAwIDEgMCAyLjFsLS42IDEtLjkuNi0xLjEuMmMtLjUgMC0uOSAwLTEuMy0uMmwtLjktLjgtLjUtMWMtLjItLjQtLjItLjktLjItMS4zdi0uNmMwLS43IDAtMS4zLjItMS44LjItLjYuNC0xLjEuOC0xLjYuMy0uNC44LS44IDEuMy0xYTQgNCAwIDAgMSAyLS40Wm0zLjMgMi4yVjc3YzAtLjMgMC0uNi4yLS45LjEtLjIuMy0uNS42LS42LjMtLjIuNi0uMyAxLS4zcy43LjEgMSAuM2wuNi42LjIgMXYuNGMwIC4zIDAgLjYtLjIuOGwtLjYuNy0xIC4yYTIgMiAwIDAgMS0xLS4yYy0uMy0uMi0uNS0uNC0uNi0uN2wtLjItLjhabTEtLjV2LjlsLjMuMy41LjJjLjIgMCAuMyAwIC41LS4ybC4yLS4zLjEtLjR2LS45bC0uNC0uNGEuOC44IDAgMCAwLS40LS4xbC0uNS4xLS4yLjQtLjEuNFptMy4xIDUuM3YtLjVjMC0uMyAwLS42LjItLjlsLjYtLjZjLjMtLjIuNi0uMiAxLS4ycy43IDAgMSAuMmwuNi42LjIgMXYuNGMwIC4zIDAgLjYtLjIuOC0uMS4zLS4zLjUtLjYuN2wtMSAuMmEyIDIgMCAwIDEtMS0uMmwtLjYtLjctLjItLjhabTEtLjV2LjlsLjQuNGguOWwuMy0uNHYtMS4zYS44LjggMCAwIDAtLjgtLjVsLS41LjEtLjIuNC0uMS40Wm0xLjItNS4yLTQuMiA2LjctLjctLjUgNC4xLTYuNi44LjRaIi8+PGcgY2xpcC1wYXRoPSJ1cmwoI2QpIj48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii4xIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xODggMTI1SDEydi0xaDE3NnYxWiIgY2xpcC1ydWxlPSJldmVub2RkIi8+PHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjkiIHN0cm9rZS13aWR0aD0iMS41IiBkPSJNMTIgMTMzLjhoOS4zbDEzLjMgOS4yTDQ1IDExMi4ybDIxLjYgMjIuNiAyMy43LTIyLjYgNy4yIDEgMTQuNCAyOS44aDE0LjRsMTIuNC0yNi43aDkuM2w5LTE0LjMgMTIuNCA0LjEgMTguNSAxMC4zIi8+PC9nPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iYyIgeDE9IjE1MC43IiB4Mj0iMTUwLjciIHkxPSI4NCIgeTI9IjcxLjgiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmY7c3RvcC1vcGFjaXR5OjEiLz48c3RvcCBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmY7c3RvcC1vcGFjaXR5OjAiLz48L2xpbmVhckdyYWRpZW50PjxjbGlwUGF0aCBpZD0iZCI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTEyIDkwaDE3NnY2MEgxMnoiLz48L2NsaXBQYXRoPjxwYXR0ZXJuIGlkPSJhIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBwYXR0ZXJuQ29udGVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCI+PHVzZSB4bGluazpocmVmPSIjZSIgdHJhbnNmb3JtPSJzY2FsZSguMDA1MDMxNDUgLjAwNjI4OTMxKSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMTk1LjIiIGhlaWdodD0iMTU1LjIiIHg9IjIuNCIgeT0iMi40IiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIxLjgiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTUwN18yNzE1NiIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTUwN18yNzE1NiIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFJQ0FnSUNBUUlDQWdJREFnSURBd1lFQXdNREF3Y0ZCUVFHQ0FjSkNBZ0hDQWdKQ2cwTENRb01DZ2dJQ3c4TERBME9EZzhPQ1FzUUVSQU9FUTBPRGc3LzJ3QkRBUUlEQXdNREF3Y0VCQWNPQ1FnSkRnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzcvd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhRQUFBZ01BQXdFQkFBQUFBQUFBQUFBQUJnY0VCUWdDQXdrQkFQL0VBQnNCQUFJREFRRUJBQUFBQUFBQUFBQUFBQUVDQUFNRUJRWUgvOW9BREFNQkFBSVFBeEFBQUFCT29TVXdwSlpSaHV5azhFOWgxeWRiRDVKbXl0c1gxaHhxWFBVeEFZSFFMQUxzaTFIbmJUbCtNNXpXUExvYVlzd2RYWUJLcGRhYkVpRVpad2M1TDR3K0lPbWw2WjJRQktybWJQY1ZDVkN3Z1pkUE5XbjRkS1dVVHpFdkdyNnlQaTdSVHQ1OG9UdGhXT2JXQ25qWDhXU1J4TXU0RGlTVXBvQUFKV0I2MmJMamFGMU9RWHIwTlV5RXoyNkIwMEt1cDg4QnRDVzBDd3YwSXR2bkpFdGc3ZGV5Y1lCSTB5SXg3YW9jRmdKZkNLMUhBa2EwUTBldlBwTllac2N1UmQycUVyVFlkMzByNVlvNCtKRmJWa2ZjaU5qeXRvRmJGT3NSNERZejZDZDIxSkh0Y1laMjVNNFpiOXZjSHVPdG9wS0hVMTFMdVJxd2hUc21oVFVPNTcyRFpYRlJrOVRaRWh0R2ZSeGJDdExVTGpVTnpYNzExU0xFMTBZSjl2NDdPR3ZNb3VaME5NT3JiNjNKOUhmQ2V6QitOMTg4YWNzTmJEaXUyVXNKbEJOV2JwZ01LYjJRSEVybzJyYkc4OXFVakE3cDNhTkhhTXFKMTh6elY5bDVURW1EcEhIVDVnRmswTk5ESDduR3VLTC9BRmIrVi9TTDFqek1qMlRQTkMxVlVlU1dLZ0lsYVhJYkFkTkRCanFSbXpDc0ZRekI5eDVPVTEzWHB5K1UvVzRvVnoramJzdXBQVitQSUxNK2QrYjExL1JxMmVxYVY4ZDZzNTg3NkdwelhXTEdlOUUvUGQxM1oxMHRsODdpQ29kcll6S255QjErWjM1TlBiOU04S3hPUjNtRnU1M2puME9WYzNWV3RsTG02M0M1QWk5R2hGYzdzdFBUaVA4QWtiMlQ4MCtqeTg3QUt4bjZhM1d6S0syb2ZyYk9QdnZEOE4rRzM4ZDdKeitHOW5oTDZqOHZPdXJ6dHZkTE8yZkZldkpPL3dBcnlCMmMwS3k2cmZSbTFSNkR6ZGhGVi9ONmlYNWZZY0hVNVY5NWowR2ovQSsyQzBBbXhlVmlGTlp2eUY3YktucmN1bE1pNWRURzVXOU9VcE03bUprZlZ2bTZ6NVBTYUV2eWRxNStaK2YwSXFNMCtoejQySFh3MFYxbWZScy94M3FBRGg5alRNQzNaR0N3cXF6MFUyOTdUNnJnTnRQRjY2NHRmcGFVSXlBNkdUUi91L0hnV21rZHdiWnRpNU8yOG12SkRPVDFMSFBvTWV2eTN6eHVpYytKOVhkczdpdXIvWjVjM05uYWc5Q1N6c2w2R0UyVTVaR2ZaTkJPNGxXM25ybmJlWFRwVnphYzFaYUtWSXM5V1B0OWY1ZXdTeGlWWGE4NS9Rb3ZNZGNLVmh0cGFQWEJVakplc3JabFBTUU5HQktGdzFpUGg3U1NTaDdjemdDeXVreGlsNlF6MEcxWlJsVmhQWWhSWWxpQnZpOXNhWkZVWXVVUXJ6akJwd0EzdGJwckxBYXEzZUxwUldNZURxTXVsOFkwS1dPRmJIRmwvY1orVVB1a0s0T3BKQzBxeEdIcDFwbUxjaWxPdWVhV1UxOEdsbmxpdHMxVk1qV094bXBaVFlrcW10bGJXMW8wdHliMFJyN21VdGNrWmxGNGprU0tFdFlOSmhQbzdwbUs2VnhmSUgwc0ptTUZ5M0FiaEl3Q29lMGV0dFVhdHBLV0d6VW80TkZVcjRQcXZiYUwxVmZLbFB3c293RmhCcldqRk4xdS9udm1pVWd2SUlObGE2VzNZQnJNbzFwQWN5QjZzcElVMVhhK25vVjRZSmtIVmU3MENTclNCSHVFMjQxUzRKVmlsRkd6UWxrUktWUGxob0MyV1VVbkVYdFRwK3M1cVZrY1Nud2RsNlIyS0EyZ3JsR0NBYlFRbDJWdEtCdlJYVkZxQWMyWEIzc2p2Y3VpS1lVdlFLRS9WWWlLN00zaVo2VnZ6QlRBRkRTV1JwZTR2OGpBTkJSNnRkbzBrSFV2VHl0WFJuME1hbWVUMlYyTzNOZlNWTUdKRm1qWndxdlZVQ3RrMGNhY2JzNCtEZGtBa0JZMHRUSFM4MDVENXpWRVZranFZeG4veEFBc0VBQUNBd0FDQWdJQ0FRTURCUUFBQUFBREJBRUNCUUFHRVJNU0ZSUWhKaUlrSlFjV05pTW5OVUZDLzlvQUNBRUJBQUVGQW9yd2R5VTVEeFI4RytLZUROUy9JOGNpdkpwengrb3J6VlcxWlpLSWdUb0xHSEYzbWptak1vbkYreGlEd0tXaHNhaU9BZ255c1JITzZ6LzI3eWZNZFgxUEZqM2lQdzdLcHJhOWhkZnJhS2MrSENEL0FLZlh6NHp3WnowNE53bkJzZkxueTh4WHphMnpRa1lYNURESExKQlV0VGFiTklPcU1udXFtdWdKRDk5ZzhjcldaNXZvajArc2oxVWswRHRQTk5GanhuSlRlZDh6cS9xclhrVTRmeFhrVjh4OE9UWGc2OEg4WWliMjVabW95T3NpcFFiK2k3WFA2b3I3QXJoWFgzK3hYeCt4VzhSR1phbjVscDhjOGFyM08wSld6dW1JdFp5dUl3OEZ2UlBIOWdzdXZmV0tiS0JXb0tUejhZM3hkeFMvTlAyK3FCeE1XQmJnd1g1V2xPTjZBUVZwWFplb3ZpS2daNjc1dGdDc1duS05uaU96QkMyeXJkQWg4NHVkL3VJakN4TlFET2hkUGF5MjlIR0hUVXlUNW1ncStWbjlwL1U1NlE1ZnhLcUFIN3pCSDhTZVdZcU14ZmRlQVVWKzl6S3FmN3J6UE9TQ3QvOEFVSUdlT081ZkhoNDlkc0FNaDZySGlLeTR2WGo4dzRlcWg1NWxqRUp5OEErMEsycFJTMjFsako5cWN0djhyWmh5UDdHTWhldklGUUVDb0N5WDBCdnE2WlpseG1LWWM3WFpiblBvaHRhMFRFMnhDZXE2ZTIzbVhVM005N0kwYlZMbWZaTTFUdXl2N1FsdGU3VkhxdkdNVk9mVllpS29QUk1KNHdPVjFFdzBMMk0vbFZ3N2VubytmeG95REdKYklXamkrYVpLNWYxMHhod01JOW4yQzNuOThWSlNuSFFXV2J5U2VsWjJmQnN0NGlUc0MvTVVGbUswcVpOQVpqTDljTFpwb0taeW4xcHMrSXZ3alBOTnZ5MC9JcjJ0TWZiL0FBU3BvZllhZFptb3V2bW15K0FrYmZHUzREQTJuMytFb1gxT2s5MnRIenBTZzVrd3hEQ0tiL0VlZ3I2eXg1bWN6WWRGblgwbFNnU3ZOdVNzRWxNVnBRL2FoTmZpOWsxeDBrZXRieDEzQVBZUnp0TUZDMmY0ZjZqRmJVczFxSGl2S2RrL2E3bHZhNC9XYy9yNi93QWVzdWl0WE1Ja1d3dnhQNzFCZXRHNXJXWm1JdFZ5Sy9WK0F4S254aVBiNmpDMFl5NUMyKzR3WE8xVk96S3Z0RzdGdU1RUGpmWTg3VEJuYVdlbStVdnRlL0lqNFV1eVo3U0c1NlVEdE10WDhYNWRBbDA4TWdsK3NNYUZDck5VOWVqN0xTTUxFQWMrWHVCYjkwZk5GRlAvQUduUGhjRFlwYnBWUnhZYm5YMTJDTWRmL0p5Ryt1UzhkM0lhVEdmcWtMbHAxVmdsMCtxMmdXYjFNVm5GODhLOVhCc3lyazU2MnJiRVZrNTM0cElYWkMxNjdjM2svd0N2NWZ2L0FPcmxKbm9ScE9rcmVmWU90Zm5jU3NDVEtxVWtYdDZVV1ppNGdPSXZacmsvTHMxWEVtT2FFZGRNQ0Z1dkVHRUdMVHJvVXNJcUttWDd3VjY1clVpbUoyS0dKMHRYOHFPdlBPYXVyaDZLZHg2YjRnRmJFeUIxRzRMcENqMkZ2N1hLVitVQy9jbzUxeERBZGl1b0lzaTZXRDVXNGZQOGI1ZnBCSTB0NTE5QXRFOXJNOVRwakh6UUh0VlFpb1QxL0Z5YlpDMXhpNmcyTytkMTJONHZ3NDVvNm1aaVB1dU1kWS9jOVRFSW8weVBsb2xTaXJtVUxyTHQ1SDFMU21ROVZUUXhOQ3RTNmNvVnB5MVZxSkMxYS9ac2JKNjlqUnNLV0IyTEhhTkZLR05SWU1LZHZ6VzRxN3B3Q3B2d2xZS3V1dll0MG1BM29QWXJCR3V4Q25YZEs1RnRtU0tTdVZsZW1hdlRNTG05V0VFTmV2VXpwMzJLMHo5SFVaM1gvd0RpaGdBZDdFM21JZlFEdW5SQnhra1BLUTAxb0wwcU53ZEluUllzZjdNRkRVMkJOL2phckRnMkdKT0l0S2tzSzlEMitJTHg0ci9YRGVtWmtxekVWNld2Y2wrcG81dEtxdE1TTm1HcjhYTmNRc1d2OGtmdC9FUFBqc0JtUlpYV3lUWXZVY3FhTDltQTZTbmI2SGt1dU9QTFRpWmJCei9sWGFUckY0N09sVlJKcnJvVnVlcGtFMGNicFdqNDY4K3lUdFhKNjRtNURtT3VrVVFBQVNMTno4Rm1qcmlHeVZCWG5QajBaS3Y0dXU1KytuZWZIYWQzWC9PN0VuZndtS2Y4bW9YK1FDbXYyaS9tekpOTWtEVDkzM2dyTUNCck1UckFYY2xxc2hXOVB3RUxSSU9uQ1ZpZVkyeEtaR1kvS1loWkdSZ1NRK0Y4eGFhM3lrNGpLS280ME82Tk4xdTM4UGJrbjNkZXFYdmY2ZDJtdTZpVUlFa215NnVkMWJZdmI2bFpJUVVyZlA4QUZQSENteUV1TTlucEZVMzJMNmZ6bVFrbVpQZDB2bVdTVHlHYlZ0N3lUejVXbXVOZTBNM2lzQXVZWGxwZk1aNStDc0c5ZEs5K3VGRTlvNm1mMXpjOXYwUHE1VkxGcFB2T0twNUJlV056TlNxNTI2ZkZkTjNUTlc5dnorNlQvbFU4K24waHplR3pNLzhBUlVhK0dkVnZ6d2RZdGNTbnM0RE1yeFpHb3VhTFVqRFFoQ21XNnBzdmNGMHRVZHdwWUtkUHNyQnBPc2FKTG9LZzQxMnRVZkd1MU5FNGJUWVpPdllkU3NUNUpuVDQ0T1BsczkwSi9LRS8rRjZMWkozU3V0Zk1WdkNsYi8xQ0ROZUJ2ZnlubTc3L0FDL1V5Q0dEUDZ1Q3duRGhvWTVDOHN5TWRXTmlvcXNkbnI4aWFtbXhVL3NtaXFpTmtURC9BQk53RmZtLys2OExQa3FYNlNSLzg1MiszeTdhSDlkT2JBVDdhVWkyTGVzanRFOC8vOFFBU2hBQUFnRURBUVFGQndnSEJnUUhBQUFBQVFJREFBUVJFaE1oSWpFRkZFRlJZU015UWxKeGdhRVFKR0tSc2NIUjhCVXpZM0tDa3JJR2MzU2l3dUVnTkZQU1ExUmtnNFNUOGYvYUFBZ0JBUUFHUHdLdUJ5dEFsUS9FQjNWeEFwWEN5dDc2N3Y4QWhjMmtYa2o2YUhMZlZXaVJTcjl1cWx1NVdGcEY2THlkdnNIYlNMQnRHa0c1Slp1T1grQk9TVWJucFM2RmxuZWRUYTUzL0N1cjlBV0dIUDhBNHpEWElhYnJ0MlZiUEhxM2tVQ0l0dEo2OG55WG43eS9iWFJ1UC9LeC93QklxeE9OKzE1KzZoM2RYL0dyeWUwNlFkQmNZTWpQR0NmY0J5b21lYTVrWTgyMmVQbFg5OWZ0K1hoa05jU2cremRYYjc2NVZnVkpISGM5Vm1iR0pjNDBETzgxRERIcjZSblh6WnA0L3NYdDlwcnJQVFY5b2w5UUhWS2YrMmphL3dCbk9qakNEemx4bHo3NjIvUzkyV2M4MFU1UDExb3RJVmc3eVBPUHZxOVA3ZjhBSDVOd3pVMWwxdExmVXd5K05XTVZiMmtIenA0bzFRSEdjNEdPUS9HclRyRUxRUWJUZ0IzZG5kUS93MzQxMHhHc0NTeDJzcWpIcFlJcEJCY3hwNnltSGFINnZsUWFTM0ZuZDRmS1BsNFI3NjBiNVpqeWpRWk5IOUozQWdUZDgyaU9xUSswOWxHRG9TMFhvNjNPNHluemo3VFcyNlNuYThtOVgwYTJkdkVzVWZjZ3hWcGE5V0U4VXFhanZ3UnZyVWR3cStsMVlVejdqUUE1bjhtanM1MGd0ZlJiR2MrNnA3MFhVdHhkQmdBejhobndxMHpMREhNWUVMaGZPSjArRytyVklsZmdreVM2NDdEWC93QVg4YTZWRnhQczVVbEcyTS9wSHd4NTFGVm1rZHYyYUJQdHJnblJ2ZmlzcW12Mkdra0Y0bHl6TGxocEs2UEN0RW80aDI1em41Tnd6WExUVEhWa0x6OEtJbWwyQTdJMC9XTitGYWJTQWRGV2pjM2Z6MisrbTJtYm1aVzN2Sis3bmxUdm5uTzFjTWhIeWRGU3pKNWJySWoxam5wN3EwM2tMU3c5MVh6VFJEOUg5WVlhY2RuWlVxMnl1c1F0SmRPc0gxYWlqdGJiQ2hBTldqN3pnVkxiM3ZTVU5ybmVOY2hmNENoMWkzYnBLd1FqSmlKVGNQRWZmVVhWZWpSWUlyOFhDZFIzSHQ3YUF6ajVyK05UejM5OTFoT2FJRXhwOTVJeldiWW0yWE80cnoveWdWczR0Ny9TM0FkNVBoVTN6amFMdERzMjgzZFhCSXhIanZyUzBFYi9BTUdLYVNSZEdsYzRVMEpYa2VENk15NlRXN1d3OEZyOUlIcENJVzBrNWNvN0ZOM2RYU04yM2xNSEViSHg3UjhseEtlVzAvMFZDR0dDU1RXU2NEeG84ZXY5MnVqaGpSaTVCVHQxSHUvSnI1emRSMnkvdEpndndHK3BFT3R0VWhPWWpnbW5FRVFoK1pTNnZLYWlmYlNDZWVSbFZRTVowTFJFTnV2MmsxODA2T2xrL3dEYS9ITld6WDlxOXZGcjhucWJ3ckdkUHpVYnoyYzZtNncwdlNPVHdZak1hZ2VKT00xcFE5SDJROWdrYjc2ZUN5dUk1ODVhNWtYdHg2UHNIeHFTNDJzZW1QY1J2cEpXZGREOHRMVWhqaVhxNHp0cDVXeG85ZzdhRVZ2K29WK0hkbHBENFVMeUFzWTM4OEpqS04yZ3NlUXIwWDloa2txUmpKc1JqL3F0Rm4rY0ZEN0RVdHlyZVNNbU5rVXd2MWRudTNVdHdBaXZrQm8vU0ZTRzNReUIzYlFNNDlDa3RSQ1kzWENnUnJxN08ydU16VG4xcEhFU2o2OG1zV3l4czMvcHJjek4vTTlkSFJ6aWNHZWZRdTNuM0gzTHlxS0dHMFdXYVFuUjFhUFJ1SGJrMFcyc2R2TG5uTnlxVnBMenJXdUZrR3h0enc1ck1sbzA3RDBybTVVZkFackZ0QmJSRDluQVgrSnhXbU9kZ1BCbFQra0dvektwMGc3bVlzYzd2cFU2amMzVkJYbCtsSVFmNXo5OWY4Njgvd0RscVY1TGEyaVZvaU5jQ2FjL1VhdmZHWDhLZ1RSdEpJODd1eWpDMG5DQm1USFlLY3llVFlLTnNWOUFIbEd2alVsdGNvdlZpTVNwNk1QY1BGcU1VcjU5VXZKZ043Rldwc1NiSm1HQmlXV1BQODJWUHNOUjI2N2hFT3pkeEg3UHNOQW8raFR1eU95clNQYTdYYUZqclBGbmRUSlBjUGtlY2hsMEFlNFZDa052RTIxaWJSZ2JtM2ptYWRKWXhBNDdWSlEvQ2hDaXozRXFOcmhETnFhUDNubFNBMjJqV01ydEpTMjczVkhGQmNHMmZUbldCbW5hN3VMbDBFRE9yeWNpUldBKzFQaGx2c0ZjSFJVait5MkorMnZKZEZ6eGp4Q3BTRzlUWjcrRWJYVlVxZzRQVjFId3JNMSsyUEJmOTZ2TGFacFpvb01BSFhwenU4S2RvazBCMTBrRnRWWE1LdXZVa2J6VlhlV3AyWnNZRk03NzAyak8zN3NmKzlLM09SRjJudGxrNWZWU1JSY1RCOW5EbjF2VGtOUW94MXVvd0hmblJ5MkY3YTJ5Y2NjaC9JOWhyNy96N3ZqVnFrVTJoVXpqaDM3K2RYQUU4c3R4Skg2Y2VQdnF3Vm85SWpqNFNUNTJUbjdxdkwxK2NMcUZGWHJiWVF5UzJ5R0lzbzM0NTFZb0k0NUJOQ0Fkb21jY1JOTTMvVGkxN3Ziajc2Nk0vdUQ5OVN4cVFGNnVXd0J5TmRHbHBuUGtKQzUxYytkS2trNWpnQjQrUGh4VUlna1dZaWJ6bGJzN0JVNXp5aFg3S2NhbmJEWUhZYXVwK3NKYnRLK2VPVERVQmI3NUpKQWcxSEhPb3hsREt6c3o5L1AvQUdxYzU5Q3BPSkZKdG5HOXU5NkpNc2VPc3huenV3TFVEYlpKR0VUN2w3eTI4MENSbkhLc0VaRlM2dHdBelRjUjNaN1BiUUM4dFpwVzU5bTg0cEhMaTUyd01nR3ZHejhQalcyNlB2QmFscEFERU9NSHhOUllsaTJ3M3h0RzQwMTBXbHpkZFlFemE0TU5uUDV4VzhTdm1NQlJFT1p6bmY0VllXbHB0VE5zT1JUMjFkQ1c1V0xGdGpmMzdzMUk2U2FvMlBDUWQxWVYxRTJjNnM4ZFFobWVkektPY3RYY2l3dW1JdTJiUElWbzBhOExuaEZEVjJlTkZKN3lTUVp5dU4ySzFsQnJXVW96SE9lOFU4ZmVNVVlXNUZwSVQvSHZGYXdQS05HamdmVGo1ajZxV1ZkNlJzWEdPMkovd05JOE1uQ2QrUjIwUm5GQzN6cVkrZDdQOTYzbjgvbk5Sa25CSnpRNjFHeGg3b3hxTmRlY0hxcWppWjR3cDlnQXB4SjFtMmlrN1lIMyszZlIwWE4reUE4M201LzVhZ0ViM1FsdG84d01aYzQwamNPWEtvdjBtN3d4dVBRbUtkL2hUUjJ5RXl0anlvazQ5M1prTFhsMDA3c0ZaTGpHZC9QemF3SnVIMVJmN2gvbHJJWjgrRitQKzJrdk9qQktkbElOcXpUaHdCN2hVcU42UXAxdDRYaWtDN3lreFU0b2xZd0Y5VXRUUlBHd2IzVWJOVmNKTHU1NUhmVyt0c055c01NZTd1TkZqNUp0ZVQreWsvQTF3Z3hsVG5BNXduNzFOUlJRY0VyY2I0ajFLUER3ckdzRCs3Z1AzMHN3M25rMk4rLzd6UVVEbjNmbjNmWFVBMk1FNVVieTBXcjQxYktHajB4RGlHUE8zMWJKMVpMcE5yK3BkeWluZDNqZlJkZjdQOUgyMG1NTk1rN0ZzZSt2bTM5bXJXMmswNDY0azNtKzRpcDdnMmlTalFvZ2NTYU5HUER0enZGYkc3Nk5XZmtjYlRHbmZ2eFVTMmxyUFl1SlFXOG91TlA4TlhDU3hBc2RSU1ZUaytGWEszRUNucEVScHNPRGNXOUxOZEZOY0JMZWFTNW1XOHdTb1ZWR1Z4OEt1TFcwNlF0YlNWZjFxVHk2QTlLWXIvbzUzSGRmQ21tSFVwSEl4dzNhZmpUeGRVYlV2Ym8zZlhVamlPV0xQb3FtNFVwTFNaSEk3TEZCTGxES3E3Z2VWYk5nTi9ZYU1pYjQ4WXkzTEhxdCtOR2FWU0lvUm5EY3g0QTlvcDVaZUYyUHBSc3Arc1Z1NC80V2YrcmRSVThTdHVPL1A1OWdyckV3MytqbnRxSk5aUkdKMVk3YXRKTURXSldiemM5OVdHaU5MaVppV0VVd3doM2I2aHM4Q1h5RFNPb2JBSUM1N0tnZzZJenFXRW1jaGlWejRacU9HWjMxeXJsRjhCMjFzT1lHL0RIblVVVXRneUF1RHRXMWNXOERGWGRveVJ4dUNWRHR6SHhxS0tLVlpEclVzeStkdThjMTBlc1Qyc2t0cTB6YUpEalZyR1BoVTU2YnRJYmdNdUU0UkoyMXFoNk00UjZsdTJQaFNFV21MWXdOcjF4dUFHN0t0RkVXRHNTWkN3eHZMYnZoVjVjaVhZM3F4NndOajJldzFxNlQ2U2g2K1ZWa3d1TWQrNnJxV1NRVFNncm9ZREdNNXJYd2tyamFOalYyTFZ3d0lPQXVGUEtndXNXaXUyY0ZPRTE4MWpNbzc0UzJQaFNiZTNlSlN3R3FVSEh4cU83a1kzRnoxaFUraUJqTkpDWTJkZlZqT0RSWll4QWNiakpPS2p0NWI2MmpoVHNFbVNhai9SMDdlUVhUR3lBbG16ek80VjFxNjJwa1dFNUxxVkpwakRMclNRRTZmVThLUThrMnVQYUtubmRNeHh2NTRPK3VyNnRYNnRnQWR3N2ZycnBhMWNBaDdnTWdPL3MzKzZvSFNOVVl0aHRLNHpSZDdkQ1dINU5CUEtJRHkwek1LbTJkeGRtSWZxeWt3UHdyVW5TVTJnNzFCSEZqeEZjTXEzUStOWFFWR2wxeEtGMEt4NUh4QXJRZWk1QjRISDMxY3dMRjFhT1Z3MnAzQnhqMlZjUlQ5SmpqODVrZzgzbDNud3A0N2pwZTRrMTQ4elFPVlF3dzJ0N2N4eHVkSmFVZkhkUWpzTFVXU2FzY1JxMUYxSkdZREx5SE9vZjhZUDZhRnZQcU1XQ1RwYlNkd3E4TU50aVpRY09XTEZhdFp3aUl1TjdsUEVpbjBPUnY5RTBzV2xoTHM4TUR6eGlncWdMd25sVzFhUnRRbUNxdWQyS3V0bmVpSlRJY3h1TjFXTWtqMjdnbkFhSG1jZXRWM0lVazQySUowOEozMUNWWUhTMi9GYVJ1MGo2NllSOElQT2psbVZmQ2w4cVd5TnpFVnAxQnlLdU5oWm1HSmhpTEVyRXI0bk5TUnluWGVMTEh4WTRqeE5uN3F2VVpUdE5RMDkvYlJkSHRaRWNoM2p4NU1jdDNiM1ZJa0p0eEdEdWFDMVJSOWxHWGJzNDE0ODNuUThvd0tPeFRkMzFiU2I4N1R0OWxXLzhBaS84QVRXVjlSdnNxV2VjQm45WDFpZVMxQ3FMeE9CaFY4WGFvRE91MUVlYzVxOGxRN0Z0UHcwMEdJQzdqdUZRdm5uY2dZOTlkYlh6Wkppb0hmVnVHNWdrL0Ewdlovd0R0ZEVQdXpNWHo5UW9TcVd5Nmh0ell4WEJNNC9mVU1LT3F6dExnZHZDVVB3cnlsaGN3K01VZ2NWaGIzcTU3cG9TS21IV0pVNGNlWU4xYk1TeXY0bXRhYWdjK2thMk1XY2VxZ3FKTGlKZFFIRnE1MXFpMmJSWnpqVnhDZytWU004bVpzQ3JZQUhaNVloczVCNTFZK053MzJWMzhKbzI4WnpiV3lOajZUZHBxMEdjWVFmNnFaZ2UrcHRYSnVkRFI1dXpOVzBtazRGNEJxOTlKQUYzUk0rRDdUU1BKdlhTK0NQM1RRYVdMWTU1dklRdFdrY2FOSTF0cTBiSUhCejNrMFV1WWpDNllVNzgxaGdEUmdQbXNNclc2dDlTZVIyd2J2ZkZGM2xTSWZ6VUZtdW1kZTRZRmVRWi8vc3JpajFlMWpXNkNqMFhkTUxOMWtPa1NlalFXMmxkOUhheVlCM1YwWDR5dDlsUHMyMG5TUlVzOW5NTWFNR09RN3hTQVcrSXNZREE3dHkxdEdZWTFZd0taMHQ1R1U4anBvelNRaTNoMCtkSWNVSW02U0U3Q2ZhNGhUVWRWWmdzU1Q2OXkyUGhYbHI0US9SdDF4OGFMU0ZYZjFwVzFHc1cwSlllaWNZRlRQS05PMVBJVnViZldzN21Ya2NWNjN1cmw4S09Hcm1hR1RXQ2QxYXMxem9tWUlYN0c3YStiVEpES3VXaVY1UFBxeFIyTWl3dTJ2UW9PRHUzWkZmTUxLUlZKOUljUjhLQkZ1MEpKN2VWQTlJZEt3d2ZSVGlhdFNXTS9TTCt0TWRDMWlCTGJvNVAyTWVXK3MxcXU1NUxwdjJyMFFIUmZCS093ai9pYzRxVld1ekVpb1c0QlcvZnY3ZDlkQ3g3Z0ZzK3oyMUhkZWtYcDE4YWNlSGZRM241UEg1YzRyU3NpZysydEtzMHJIc1Fab04xUm9VUGJNMm1rYSt2NEluVWVoeE5XbUszbXZUOUk0Rll0N2FDMFh3R1RSMVhCTkU4Q2VOWVdUYW51U2lJVUVRNzJOTXN0MDhuZ053cXhjcU9LWTYvRVlyMzFjbjlrZnVwUjN5Q3JBZXJhaitvMVplTG1wb0xkTm8rcnVwb3BjSDNZcFI4bWFHaE0xODJXT05mVzFDdGZUSFRqb3Zha0trMTVHd202VGs5YTRrd0swMmRyYTlIcCt5ajMxODR1cFpQRFZnVndKWEVTUFlLSWhWblBqVzVoRXRGNVpXbGJQYWEwOVhHb2plYXVyZHpxeHZVOTRxNEtqQVdQSit1ckVmU1kvRDVMby9SKytvUDcwZmJVSCtIWDdUWFJvQTlZMU5jTDZUbWtrY1lERGhyUWVmeWYvOFFBSmhBQkFBSUNBUVFDQWdNQkFRQUFBQUFBQVFBUklURkJVV0Z4a1lHaHNjSFI0ZkFROGYvYUFBZ0JBUUFCUHlGeWZWa3pLVjVIVTFDTW40c2ovcGNvcTU5b0NZYm5LRU04c1plc3pVLzRVUjBMWXdXZFZuR1AybDRpSzQ0UDhCdmNiK3lDWExNWTg4blNISWNtUzF4VXdaZmMrb0pYMFRnUCtKNnNDYXM5dUpsVFN6YTd3cTg3UVI0Z3p5ckdlZmRRcjhmOHpzdUZPQzRsWEVvU3IwY2tvSG5aZHNlaGxlQy9VTlF1WjZTQmlBMmZYZTVjMVhOYzlUUDhjUnU5elY3Ym9PeE1CMUE4KzhaWjFkKzJOUHpLdEJ3UDI3TURYWUpEcmR4NXNwdWpVZEZuNnVzNng3U1h0OGFqcmFPT3FaeUxhWlcvR3Z4S2gvdVlOcUI1V09GNUsrWWNtT2Y2SmZNN1AvRlo4STdRcldaUTB3cEROSmhJTDdIWWM3b0tiekVmOFM0bTJvVkxyZzJqbFN0cThybVU3OWx5UGJiRDVOcXFtYUNielF4eE1nMWxxOFFEQmJJY04vekdxcnVmQUZxY25lbVZIYmw1aWlSZDUxMU9JL1ExZ1d6dVFHbXpKZDZoejlScWh2VDNGZVM5WUtYVGk2eElmeGl2Mlo2Um16N2lhMFdLbjhRRkNNdmJvTm14NEtQK0pYWHJ4Sm1qdkJDRFlqZUhXWTNGeWVsZkYvY2VjdFdEMTZ2cVlPSjNydFFMUkszUHhQUzNlSmxWUGN4QStxWGd2dGJTMFlHcmk3NTZrNXl2Y0NqU3ZVb2hyckFhYjNxSjVMUTNSL2tHV0JRSGxEaE1EMHh1REM1OFdxOWRFenRkdGM4ems4aWd1RExkV2Fqd3ZuVXNSTEYzVWVhSDJ3YXJaZEtKc2NEbGpBTzdNK1B1SGhrWWZ1TnR2NVBxSXN1TzNkRjBYeksxTjBYOHMwKzZLekpXbDJWMHJ3NXFaQm9pQ0czMGJqaE5TS3owNTl0SjJZbGRkVVF2Z2RObnZYM0RCMVo0ZUJSVVBXMGRmYXY1U2w4NDh6aFhCTklFYk1tMVV3UkVoaVBpL2NXNXJrUG9EK1lVL1EzRTl3YjZCVVJ0dmVMbzlFZFZTcSt3aFRJeE9Xd3RaYzRqeTI5ZTR1SHJseHJmS21QNW1la2NWbnRGbng3Z2txT1plTzBQWTZSTGlpMjlKdzJoWWhzUDVkRVU2ZGYweEVWTVd2N2o3S0p3aGpQalByajVtSmlDWnV0NW5rb3BLbXhTWG5CMDNONmFkRndQdVlPcUJZRkxSZlRpQ0ZiMzVCUDZFeWZHcSt1UktnSmsxSzBsajRialFJRDBSZTVxNFVXbFFMNDNjM0tSMGRHS0tyNWhYT0lBdm1YMUtIRjE3NmN2ZWlsMzhNKzVkcnRRa3JsZm9tVGRDM1ZOTTYvbHRnSHpTNldQMUFzTXgyOTdUNml1RzZQdUN1Z1pIWmJ6NHFJM0U5TDRvN3JnbUY1NVFmYzdtWTJGS0g4eDVuZDZmRDE0OFUrRDEzdEFSb2xkZ0Vwa2NjcTQ3RVgrU3dqbHlEN09sSkdOZEZHOVM4N2lRdWVnSGVtUHRDVUh6WmliR3VmQkZwU1lJY21zYjFWbzdTaFpwTnFySjMzeExzbnd0bk9KaDlaVHdoZDQzM3hCTzBBdnFoYnVsc2RaYnJrMzhUY3kzajAzemlXSmdyMHVMV0tLVCtTTDZXRllrT1VRNTk0cWhkOHl0Tm43ajVlbmFKT1hXRDd6VUdDY1MxckhuVmZ3bGRDNlpqdjFDYUlYV295M3k1bDl4RzJwbVpZd3Vsemw2T2pCZ052MjFuN2NXSmxwSUJoZTZxc1JPU004U3ViZEl3M3NGb3FRNElRMXo1M2ZhWkRjSUJhaDN6Y0RSeDdUWUwweC9XNFZxemkrSXRsYXcrZjd3ektyNFA4QURHYlRoKzQwdXVoVzR5Qnh5Z0FaU0x4dmJHbndhK1paRk4wM2kwREdLNms3NmpLdGZiRlZxRXNDYkYwTTB6clRESGVadU9ONWFpVHBGZDJQMUwwRzYzWXc5eDFWbVMxL29rQjBsZlpHRjQyTWVLcmcxcW15Vjh6dlIvWDZpVFZJWitZWUNZTkJPVHF5OExsaW9LTDV3cnJ3d0tseWg3eUdBRGlmeE9PQTYrSVNBZzJxdktjY3VZVlkxN21SZzdCK2FsWjdYZThNTVJubzF1Ui9aTWc1T2RIV0xna0EvYWp3c2JZNjlZeXlsL2czQkN6THlyOEVkTHhWU0h5Qm81eldvS3JsaTB2SmQyWlpUSXA0SENQQVEvUGNyL25GT3BNTDZMdWpVS0Z0d00xSUsxSktzNGwwWmg2NnY1V0NWbHVjTDlQMitzb2lRb3YrNnhYSkNwVzcwcGp2RWc5eExkTWFFR3V0R2tVMGkzVm5HdW1tK0lwMEhjTEpUVzQxUitFYjRLTEV3c2lZOFYwMGNhbDd4NlNLeWVXM2x2Z203OFVGQjZFdXUrV3YzSmVYMGpRVkc2RXpEeUpUTS9vVGg4UnhoMGxmY0xnM2RvL01yUnFXbTEvYmlWbDNQaVpHQ3VwamZ4djVpbFN3bzhQV2RPT0didHU2SXN1U3RieDI3Ymk3dVNGOTRrdWhsdmRuZzM4RjZJOXZkR2xheDI0ZktaSVUzSzNZOEppY3BvR3orNVdCS3FqTkRqVnM4UkpQQ1NCdE45OHhLSlFMQ3puck5mTUdtdnJ0cXVIQVpPc0lLeXRTNlhzY0ttTXhGNVdzN1c5eS9nZ0ZtcGh3YzViUTJYemR4c3JWeTB4YXdtazNWNFhuY3NEUXdUY2Q4elFVM2Y4QVpLS2Y1ekh5SXBYS1UzTHF5MVZaRnptYlo2ak5wNzZpQXo0bzJEOXhDa0RJMHpPeVlLMStCMDRURmNKdk9PU2FqRHl3c3QyMHRRQ1FVNS9oSVdyRCs4TVFOWllzRDIvM0VIRE9PQVFjUUFrT29jenAzSGlSWmNsUytLR3hpMzhGNWRBZXFBdEZ4bVM2VzFadUVaa0grUzNuRWVqaExPbXFNdnhEMUI5QXpPQ3R2V2NpVGhGdkNuaUhUMVMyQWxJVmplZkJDbXdtVGI5NGVqeEJPY1VxNUxmeGlYRnZNZUFTTU54VXh3VTdnV0Qwd2xITGRVSjAxZGtjTnZJenBZK1lXN1YybEh4TEREdE1HUXJyaVVKZUtpRTdoMWZ1WEpHRnNXNWdYM0p5bU03RE16TStiV255U1o2RHRoV2piOVR5c25qY1ZINHNwQmdIVnhlNUl2cFpVZVVvMGR5L3pIbUc5RGZBVEhFd3BsSTZzRk5JU3NaS05YRjlZeDJ5MDlIM01DZEZGUEIyaENuSzB6VS9SY0hWOVdXcjdDajVsbVA0Q0h4Q3BRdlBYbzVsY0dLV1Q2YnIzRUdwVlJleWpqTUxZYXNZcGZCVEtCMnhqMFVqZGloTTJ0TEZjYk5pRlBpMHZqM0dNMTRTOHl2bkpiVlQvWDNtTURsZXV5eHVXV1lyc0FMTzBHQkxiN283R0tnMmxhOGpJek56bjZadEpJTDVCTWt2a1lDUWM1WWZhWXhseW1XYmVKZU8rMDc4VkN6dC9CT2c5ZDJZQjd6NGh4N2xkSnhGODkvcVp6bTg1MndRU2JuQ2dZWnFCdVZTaFQwek42cm02cC9pQ2IyQnpLemNjN2g0bE0yWkF4SzJQY0pmODd0REluTWZkWnJhcis3WDRUUGFPN1JLcExiN1pkUWhWdk8rOHlDNU12OEFPa1E2WURsMXE2cmJLenVTWFR6aExDeDU2UlA1bVg3dnFEZXJFeHpEb2lWZnR2NVBtRUVTd1k0QkRNQndMeURuNGhEOERkbUFyNWxmYzZaTmVQS3AzWUJXMmJxSW5xaFN4MjJWWGQ1UWJsS3h5aHM5eXNCbTFrTHJjelpuUmQ5UTFSYkVzdXFEdjBVNWhlM3FCZ3ZJSTM4a3NpamJlcStpWmVlelFqOE1FYjN5L3FlR2ZDejFsY000b1A1WSszTGFVN2tRTUJPWlN4eW8xZU5aOEU0Ynh0YWZoMFFYNkY1K1VIT0dxTlFQOVJnZ3ZjRnlwR2U4QmRZSG5jZk55eDFKd3dlZ0htNmd6a3JKZmdmNGd1OHlKYThJNDRsV1dYaUtLamRLOVpTUWRqMTJnMXBNYWhycktmSUhwNytKbTFlc3I5VHBwc3hld3c3cEYrcW10UHZQM0ZDWStXZE1WVnVWUlk5dUNVR0t1cWVNUWQyekQzejVlYWQxM2w2L3dvcTlmam1OWUNCZFJEY3Y1Z0pzK1p5L3lWTVRncVl5QU9HSzZuR01WOHpzL2hUNXBETEM1Qy9mTWEyNWJMN2x2TkcyM1hUckJyODAxRzlDbUV4S25JOGpxRVZVT3BHVklZY2xmTXpaRmliOHdRekxtRHNwV1paRlpnSFBRUHlabHVsTm9aNGFlTDc5SXlKa1dKMUM4Um8ydlYwZWpvZkUzUnNYU085eXVERzVnc1gvQU4zTU1hcGdHZDVoQXA2amd2cFptVEljVW5xUENDbEYxV1B1SkJrcG5OOXdRWWpBRkdZcnZOYWs3U3ZxeDFSeG1NNHFkR1daR1YrWmlHWXdoMUVoWkZqRkZmUmlzVmNscy9xR2Y3Vkp1RWhVVmpWNTFPdWcyNy9FRVpvZ09QMEFxQzV6YXR5N2Q1U0xkNWlmVXJ5UmJIc096MnpRcU15NnpIeGYrNEl6Zy9aL3hxRUQ0NW54R0dqc1dwNEE1WWduaXhjVG9CQThaUjM4RncxRXpxMFQ1L1lxL2MvOVAwZk1VWjN6NC9xQ3VsWXZ0TDU4bmVnbUZoM3FOS3lCdXJSaEhiMW1Oc05ZZ09LWVJLak52d3dtM0QxSmt1WTNHN0ZnaWd4V0wzSXNzY3p2ZlgwbVRkWnZId2krNkY5U3o1bmIrSVViWEV6bU1xNmRJbHMvLzhRQUpSQUJBUUFDQVFRREFRRUJBQU1BQUFBQUFSRUFJVEZCVVdGeGdaR2hzZEhCNGZEeC85b0FDQUVCQUFFL0VDK0U3VXhXK0FlUHM0eHVuOUZRRTA2NzdZUE81LzhBaS96SWpzZ0I5SE5iYThNcGlHSVhBVkpsQlRBRXBkR0hrcVB1Vnd4ZDJ4QzhvN3kxaDBDZDZQeGhQT0N0clI4ZVlVbnV2bmx3ckYvOWY4VEcxbWFaOWVRK0E0YjVUYjFSZEQ0VW1IamkyWS9qZ1lQTUFJSEFNMEhYL1pqdU9Ed2E4OWVGQ3UzTTFpa1ZHTlEyTXhUajN5YkJpQVhPZU1TZlVqWTdRb3dndytFRDFpWUNmNWNOeUg4eWpuN3oycVArVGtvNTNsZlc4MGc5cC9wbDJCSGFPQlJrOTBESDVOU0MySjRKb2RRTUQyZnBCMUhGZHRHS2wxbzA5ZjVqTmpyditZLzhaemV3cmVIbytEQ0VoYnlQWi9xb2VNRzRQdE9tY3doa1d3WFlqejJ3VzQyc3F1aUs3R0pXU2FOd2xZVjJtS1NLQXRqMEFHdXFuemtCZ01QckZhL2xMUUd1OUN5SEVuWFlKQkFTbGl0akNVd1RPK0d0TjZkYWk5REJFZEFucHlyWmhLTUdzbXNxb3VHSnpzVitETmF2S2o5SER5eks5RUZTVWRtMU1NMFJDQ1BmYlBneGpZN212ei9od3BsZ1o4czVmTGh1RmE3K2UydURreHgycjhCS3JpMTd5QXlDcnhZd09RKzl2ajZJZTB4RWFpb1E2c1Rzc0syWVN6OTZDNmtEMnhLdFhsYmphV3VNSER3Z3lpcjdvTUt1MlNPK25DVHJNZHQwVUNLNFVBTWd5YW9rZlhBNnQ5RmZWZzdVY0NyOHNaUFNqSGFjenR4Z2lOWUFUb3RPdW8yT0k2STV4d0J5dC9XY2Mzcnorc0k3Q21qWmRPSk1ZWHNhVWVDZmhieUF0dm5rWG44Z3hPSjIrR25PQnNNcTNtcTltYjc1dCtqckVnbk9Oai9NQ0lsbHRJcVM3TllrQm9HYU5FaWVyOGNjYUNwV2hzOXlESFo0Rm9Xakx0VjlGY0FNcUpnbHFIZlE0MndlNDJmWnNJWkNvRElxSUVWSnVaaCtMcE9PQTBBZFJjMHdCTDFLaTQzc3luK0ZGaWJWZE1EQ0RGRXFrMEtXYnlsa3JaaDk0Y08vZ0tvSU03RUk3QjMwS0k4SmwxRTlGZmdPQTM3YmYxTXc4cnlXMGc1UWFNUW5QZWs5Vmcybm5pWDh4bTlGQmJFUXFEWERCRGFrcWpac0dqbEc4dUdrZnNQK3N4S01QZXpFL01wRWhVZ2ZMckpOZmxvdmJod3BiZk5TUnNGM1lleVlWRGEvSFpsNnZEd0Fyd0NCRVdscTBITmY5R0VOQkNJZVhYWEVHWUJCRTFMNDh0WXJwMUhlWVdmbkh5Tk5CUS85ZXBrdVBlUDFGTmZiQnlvaHBLTmtyRG5SY0M3N1VCMk9YYUFCMHdmUlRQeTRPK2JrbmVoalcrT0tudDZndDlKbVZSNlBIS1JqRHR3Tmc2cGpzTzRLRWlrdFFFREtQYW5LS0E4OWx3YkU1QkNLeFFMd1l5c0xzRXZsL3B3SFVKaDlkQlZkd1Baa2dJbWhxb2tVNnFrU0Rsc0RzTUFpbEE3VGd4aTdEQU5zdmJudnBrZXROVEtZQnRNNk91VWFqaXFLMEVYVUtjT2xBSlF5bXdkSElaekMxODFSeEhuWTR3bURNQXM4MndadmlPRzYrVk01Vnd2TU9ya2NOMkJIWUIzNmpHWWNJN1BCVHh0am8zOGpQa2YzaE9EQU9lbjJDOE5mR1RKdXl2QnhnbVFZSGdrVzlNU2pRcUZkOTRkVWdJQk43VncxTU1OTWhURVY0WmMzRjk5RmlNdW1Wa0RIUFl6Z1BkWG9tY0tNZ1dHVS93QXJETE40Q0hWT0phUzdTRmdJaFJSanNJdnl3ZFVJanVrQys0RjZPRjJESWM0QTFwQndMbzVPK1lLQXdWT0xRcTZNeFIrZUFhVElLZHlIYkc5bTBpY0lKRVRudmxLaUVJaFVwNkx2SlRja1M3TEFYMFl1dFhVSTFJbXhLUEptSzVJWlVPbFEwRzhFYVB1b0dzRTJoRFpna0dFRjVCWlYwWWVRbjQ5YUI4WXRIclgvQU4xdytzUFlrdC8wWDl3Y2NBYWx6cXVvNHkxREwzU0EvYmpaWFYvMmFQeko4U3dSbnNOWVl5akFLNXdVYjBZKzNBUEVOcjdBRitPR01QUWdPSnRVUjRWK0pTNVNPbmRVWHVyUXlLd2RUQ0YxanN4ZUFUVDE1QlN0WmdUWnF0bnlPbkZVc0RlNzcxSHc1aGh2SW5DbFI4MzJYQUxMVUd3Vm16cURaZ0MzT1NpbElhQVRLSVdHdVRocThtNUsvVG5oQlhreldBWlFUVmpuaWhCRk1HdHQ2VFF3SnJia3FsbmplWU90Y01lVVZBdTRHQU9MRzVhSkRrbVpwYW13cnRkdWxGemtMZlpJb2dlek5iT2c0eEdiRVdHdU5LdGlORWRNdCszL0FBM1hMaExhSytqY0RiUWRzVWx0NHB2UUNzNk9PdklHMVE3Ykpwb0hObGJwaVhUam9JSk5ISStOSzRCcTRIcm9QdTdCMnhtY3FLWWg0Q0xLb1REMWQ4RXdTQW81RkRxOHBJSHNNUmZBQ2NmOFordUdWR0lqMHo4eHRUdWlJTjZqNXlFZmd5N0xEUURtMko2cWZDb1Z3QlJjY1dHb2FOcnROTnhSeHl2dTFGVTJJSzZZdGVLTmhNVTU1dW93MzRnUExBbFRlSHpGTHVKZGNpVU9VOVZRdlpFNk1qbGhSU3lqYUxaOFBHWHVRcVFkRlhWd2c4NWRYUnhZTmNURGFBTGRFQmhvTCs0ZHpMSUk3bUFKb2psTkR0KzRLQ0VkV1ZOYUFiaFRvMkhjVE5wbHg0TFgxSXlsQjFEZmJpTVMxTHBWRmU3ZFRCMHFsUmk3d29jM2xnLzYwY0FZK0FGMXJsSTdKRnhWVTlWK3NuTXNxS3AvcXlSRjhaOElqelRtM3ZrYUxiTUVma2hraHMxSjNzNmRBd0NjSjZsM05ZNXpUdEp0R2tOSmEzTnNXRXhvdCtsWmx6Sk5lUTdZMEtqR0pGVXNFNUhlaEF3TWFBSVpmNDUwTVcvZjgrRVhGWjh2NW9hclZGZTJBRm1LMUNNblk1SHN0SFJReThUb1NieTJWK01Cbk1nQjc5VXpocFRtaGx1eHZMQkhDZkxGRmt3emYvY1A0V0YxNEl3b283UFhqZUZ6Q0gvb3pKRGk0U3djYWw0aXc4ZUVVSCs4Yzk3ako2RUtBSUlKRFR1WERZT1Eycm8rZ3AxdUYyNmE0cWdsbmtCSmp2NjdXODdJbGlsbHhYVi9teVJCSklQSXgyeFl1OFVLZEsxeGhoMEhOU2F1NmJpOHk0cXlCQlFUU1huUXRsWjdLVk1xRnVHSWZVSUNyWGhiWm9NTVMyaW9qd2dJMmpMaXNrRldudFFlUElzeUxSeUxIYUthalZHSlNmSFBjS2FQVVRKNG9IS1QwWTZNY0N4NXdiVjhUa0h6a2tCdThuZjFlNE9GYXE3ZDRvYTBKdVlKM0JEajRsbUhFR09ST1JIQTRKbFBiWGxQdDZzZmFPOGUrK1AwRnpYNVpaZEwwQkFjSUtiUkY4b1BibTVOdWZoR1duWjl1VEk2eXV6VTZhSUdqUTZxZy9MT1kwR3BPK0dTdE9pSmRWQnN5QXZPZGphbmhFY2hERzFZMUZGSmdzRWdwd1pOcHR6OHZiOUZvMFliaWg5Qm9rNkpRUE1jT2UvZ2VxS1IwNmh1Q3BadVF2RkNnZ0YyYXVBYXpFaUFSZ2doRFZYT3lUa3FUTU5LRld4L0xWOS9zMFJ1VTNhWWlITzhyU09JaXNZSWJpaCtiaXhoTkpUV2lHYTV1a3FZTEtCamt3OVNZcW1STkVlUmlCRTJQVWNDV3hCQm95NDdUZGswSE00RFVmZDBrODIyVGF3L3Z4amJBUFNhNXhOTXp5R0NnQlNHdTl5aEZ3dks0ZGNZbEh4cVZENW5ERjUyaklIWHYwWlVCNG1GQXRBSjlZRGdQTXFWQ2NEeGNmMmNRWEk4bTFNamhlelJydGY3RGtGdzIwNXVMVUZDRlNUQ2VLUjVQN1VyazFpc25nV2FoZFFpWGt0eEVKY0FoQkE1Nm00ZHFiS2lLREI3YVV3QlFpMlRXb201THlzY1NNREZIY0Z4ZnF5Q0FZUkNBZ210dHdQcFVJSGtJZytNYm9oUnNiWE5YTGhKaEFFeUd0M1htb1hHUmJvaGIzaFcxMnhhSTlFZTJKVFRDRHNzSWVNTDdLdVVkc1hDdlUwWkxxRHM1d3FvcEZsVmdWdzBOVmdhNmlIY1p0WDhiUHd6aDJCY0lZY3pyMkJMZzBJZmtVemVIdklkWFFYbkVuZi9BQUR3YzVtNWdXZElDMW9pOGhqUTNhS3RnTnpGdnpibDQyS2c3cjBjcERSMG5TTDN4NEx5UFUvSndKdjdFanBTZFh0alIzb2tpMEZkbzc3WU5mQTdrSUw0bkZ3bjRIWm5VU2NPbkFodThXNnBvWTRYSUtPaHRSYnB4QU0yckk5dzBrczY1YzA2SmlOTmU5NHhLNXFMeSs4SUNDN0h3S1J1cERCbVpzeDVES0xDOEpnU3hsVFBoUGpEb1ZXRURTemtDaXZBeFRoSnI4cWJJSHBaaE9nQjNWT1FRUEIwNXdRQ1lBOURVYll2cnBpNTBoTEcwbWlyRU85VDdESnorWWlzNis4Y3BNVFovWkg4Q3lVWWpGSWgyLzVsUG8wTmFDUE5jWXh3eGtDVTA2QjNqYXZ3a0dzTXNVTmJWN1BSTVBseW5tZGgyT3VPdWZxZURCR0dSbUVvdVg3WUFpSnQxTWdBZFZ1ZENSSzlzVXUzTERzMEw4cmdSM0JUSHRUSDdJd0Rld29QR0xBZmU2dllwaHA2UktoSTZSd3I2SllFR1ZoaHB3b0s1WXBONWNhbS9mS2I1QjU3WTI0MlpBVThJakh2Z3NUdHJSb3dabDl6T2llb0s3Zmh4RVdaU3hCS2kxNzVMdy9nckFrQm9ENkRJZWpMMkU4elp5QmRkRkExL3dCTTIwTWhjck9jdVZiQTduL2d5ckVGKzVrV0FwYmRIMHFGTUI2bTJ5dEozQ2hsWFNuWGFqUnNnQmN2bEpoYmVnYW5iQi9ObHRZSkFBTWpDYUVtVVNYcUdzV290RzRxanBlWGZnbEllL0dQSXBhTmd3QksrcHZjMnNKaHliUVZYcGpsa3BDL2UzTnBhQjg1eWZpZDFYOXg1UEZQL3VSd2hPRStUMmpoeEpXVGJzSkJybENqbXVIK09NTFFnbmFBQnNPbTZOTWJ0ZStCMTYwRVNSODQxWjZSTVNPTnMyYnl0ZUJXbDN3NlRCenJBSmFjTHppNmFxYXZXc1AzTnVEYXBKc1pWNXdFVWxSQlZXRy91VUhzK3FUUi9lQTJHeU4rVGdiWG1LOGxQQThabzNWY2hMTjRjT0JvVFQzaGw5Tk1ENjFuT3h3ZEhMWnk5MWh4MmlYK3NIS3JoWW4zNG5KTXF5UGJsV2FhbmpreUVhMlh0TlNIM2l6UjR0azUxdHFCclFRY2FuQ0lJbWdpdElEM3lrODFtUENnUUNzTGgrVndlallsZzZSWi9IbWNjTXlKOGRIeGtxQkFBUElMZldLaWphaS9WUXc0M0lCZnpqT2dQNk01WkV6S3RjeXJkOWVJUE11WlZPV3NCbW5jdVdCaVZZbFM2Vi9tT1VER012QmdUcWVpbkRuaFVsN3VhaHp6OTJ3b0pzUGJINmpnK29iVjFuSGhGbEQ3eGZ6YWYzd0RFWnQ4R25lc050RDJKeENnK2prbjcxMWQ0UitPUUZlRHErNmtIR2tBNHY4QW00RXk1VEtWVlZjbWRkQVk4REY1TzhZNHEyN0toTzd6aXV3RU41SjdSeDFOVko5NHNyQSszZ2NkQVNjNnN0MEpqNXhsd0MrN1R5Qk95MnpoSEJOd2lxOS8xNU96aWpvWkEwdkxUbEhtT3ovR1B3TjA2WlB0emh3KzM4Wm9BZFJIeG9INWg5NStwTzZVL2NBQVpCSlBNQS9XQkdQYitIR054SHFxWEd6bGNUT0I2RFJQdk9FM0lvNXBFY0s3czR4WHYyVGgyOURBa3RDVitPbWM0cDZLRjBPQTl1S1RZeGVSUnpXb0Z6N3dpTzNzOW5pdGIyTUx5WDloc0lrNThuaGlXQkFXSWkveGk0OHJVRVk5VjVFM2laQlJ3aEs1LzhRQU54RUFBUU1DQkFJSUJBWUJCUUFBQUFBQUFRQUNFUU1oQkJJeFFSQVRCU0l5VVdGeGdaR3h3ZUh3SUROQ2txSFJGQlV3VW1LQy85b0FDQUVDQVFFL0FPQTRpRkg0RElRZ2hFeXJJQW0vOG9tbTN4V2FvKzJnVGFZR3QrQVc2S2NPc2lRTjFuZG9DaHArSUhoUENTaVpXVW5WUzNRWEt5dmRxbXRhM1JEdGNiQkdxemE2RG5PTnhDZllvTWEyNENtUnFvLzJaalZGeE9xRHYrQVFwRTlvb0FORUJBQWpoSURnZUdaN3hhd1FwamU2Z040UDdTNnNYS3pnYUlPVWhQcERVT2xNekFRZUlsUktKaENUb3NrUEFLZHFnU3N4UVVCd2dvTkJNRldBUGttbXltRUhNS09aTzdTZmtZMlNib09iSkIyS0JDRXlWQVFFSmpYdU1OdW1kRll4d2x3RFI0bUVPaHlSK1lQWXF0MFJpR0FsaER2TFgyVG1PYlZJY0lqZ2Z6QW5hOFJLQVRDUzlPdUNzNGk1V2VrUEZDbzQ5bHFITVBhUjdTTktuNXFDMjlnbTNsY3NvTklWREQxY1EvSlRFa3JEVTZQUm90MW5HeFBmNE5IeldMcHZ6Y3hoTUh1MTlYSFFKcEJPeDlYdVA4V1QzR0FQNytEdjdWVERVYTJHSFBFOXgzQ3htRHE0UjhHN2RpaitZRWNyallvdGVOcFJMZ2dIR1VUbFZ5WUNhQUFRWEtLTFZ6YVRkQXVlZGgvQ0QzTzdRUjdVTEtUcTVjdGdUR0ZzNktKY3FXRkx1MHFWS25nOE5Jc1NKSjdtL3dCbENidWRZZ1gvQU9vMmFQRXJDdlpCb1ZCMWU3WnZkSjNKVlpycUQ4bFErNWdlaldwcmM3ZzBiK2Z3Y3FoR2NNRzN4Kzl0Q3FsSm1Kb21pN1E2ZUIyVldtNm5XTk4rb0psQm5pZzN1UWZzQ25WSEd3UXp1TmswWmdVVzJNRkFNR3lCT3dYWDdrQTZicHc2eUZLUDFJTUJjaDBWUmEwa3VLZGhzUFNBRGJ1M1ZGckM4TkFXSUxYMVlPa2tueVo5VUE1b3pHNUF6ZWJuZG4yQzViczJWdHlEQThYZnFjZkxaVTZZYUJtdVJ2dWlHblZWYVJhNlJjSDdqeU8zY1VEOS9mb2ZkUHdlR3JrMUN3RWtMSGRHbkQwRFVBanZ1cVpFQlh1WlRIZFl3aFpNQWhPVExTcFBldjFhcHoyTmk2RlZwZkFUcUZab0VNSmtUb1U2blZwbVhETFBlaXh3YTdNQ0lFcXBSY3lzYzMwV0VhM250ODFVdzFaMmEyclNQVXVYK080MVp0Mm1uWFlCWVdnV3ZhU1FZQjl5YmxRRkFLcWdjb29pbUp2OTNWT052RmRNQ2NBNU1hVzB5LzBVR2JJTklLYlRxbHBmRmdzTGhxbUpjV01pWW01V0l3MWFnNktnaEREMXd3dnltQ3VqVzB4ZzJ1Z2FYc210bzFqT1dmUUxwOWxOcHBockJvZGxoK3JYYWNrR1JzcVdQcFVuWnNVOXJHOTdpR2duWVhJRXJGWUxDNDBBdjI3bFZxQjdPeUZXeDFKdlM0Nk9NNWkwdkhjUURCanhDb3NMSGdxc3djN0tkRG1iKzY0UWU0dHpEVWdPL3dEVExFZXlwMUcwbjVobzJUNXNkdjZGVG1hQzAyNFlxcTFqT1h1ZGZMNjZCQWs2L2YzSjlsUjdJOTEwcmo2VHNPNmxJSk5vUjBRS2tJT20wb0Z6SFNKOUZtWUFtMTNoc05jWTgxVDZVclVxZkxZQkhrcWZTZGFub0FzVmpLMkxBenhaSFdWWGJSeHJlVmltTnFOQm1ITkRoSTNnZzNRNlF4WUVaeWhTek5rTEZkR1liRVlpbmk2alpmU25LYnlKc2Q3Z29WR2JLcUJXbzVwOEQ0Um9WbU9zNVRQN1gvMDVBM2dDSTkyRTYyM1lWVnF2d2xJTXAyT3BzU0I4eEtIU0dKZllFZWpTZmpaVmlLakJXYjVHTy81blliQlV4bU9VZmUzMEhxVjBoaTZsWEZFMDNFQVdFSnozT3VTaTdTVnpPcGxpQXN3SWlFNHR1bXVFM01wK1U2SmdBRnlnV0xLeXlEUUdTRGRaWEp0TjRNb1VXbW5uTHg4MVJmUXd3T1RFVDVwdUtwT0JtdUI2S3BXd1FrODBFckNZK2huczRIdkNxMHJjeW1aR25oSGM3NUZZVmd2VWNMTjBuVWVBTzRLclZNMVV1ZFkrSUxUN2l4UWNINlgvQUhPL29MQ3ZCbW03UTIrNHNQSUxIWXh1R1lhRk15ODZudSt2d0NKS0Yyb05KSURVV096NWRFN0xmS3N0cFFnU29hUm9nR3F5YTZ3VEhVMnRPYlZEbGtMTFRUU0JNb0YwR1VBOXpDZ0lhWjFDanFMRFkvRllWOHNkNkhSZjZ4Z2F6T1dRV2p5QkhzZzdCNnNyQWVyaC9GMCt0Z1dkcXBtOHBQeHNzUjBrL0tXMFJsSGZ2OVBSRXF5RVFnNTAyUkw4MG5WZnBLYk1JTmtTb0lkQlROQ25DMG9NbHFMSUN1TjExaHVnWG96RUp4QTBLenREU0pYTWJsaFNFSWlDaS9ZQk5jU1Uvc2xBQW1DZzBFR0F1NkUwbE5hYzBJNkZOVFNZTjE0cGpvVG5od1RLZ0FoWmdWQTRRZ1czUXk1RGE2RVpDcE9XRkIxUUpVcHB1bjlsQk5BcHNrcmVVMHlVQ1dWTEk2Rk5RRmtFeHZWVlFBQkZoQVFRTGxtV1lIZENtRTVvQ1BjckFJU29VRk5CRGsvc2xVd002cUdUQVRnbUM2SjY1bEdJS2FDZ2JRZ0VIV3VxaEx4Wk1mbUYxYjhITk1Jdm5oQ0FVS20wRXdiS1NEQ2QyVkpDRndvUWJDeXVKc0VLYmxFQ0NVTGFLNTFWbFBnbXRncUVBbzRRbzRRaHdsQ0ZZc2lVS2JpSmhOb2tYSmhCckZMUm9zNFZ5cEFXWlhJTXBseXF1cXlqTEtkcXBSS2xDWVE0UWd4eDBRdzFZM2lGeUd0N1RrR1U5aEtMdzIxZ2pWblV5dWFTaVZ6QXN6aXZOQ0k0YktscUZVMVVqbHFKVUJRdGt4cEtiUUoxS0xhRExPSzVsTnZaYjdvNGwreGp5Q2RWbTV1dVlTc3hLbGN3YklGNVFhRXdoUmRFaUZOdUEwS3A2cDV1ajJBcFIwUTA0Zi9FQURjUkFBRURBZ1FDQ0FRR0FRVUFBQUFBQUFFQUFoRURJUVFTTVVFUVVRVVRJbUZ4Z1pId01xR3gwUlFnSTVMQjRVSVZNRU5pZ3YvYUFBZ0JBd0VCUHdEaEtzbzVLRkg1TlFvS3lXdW10RGZoWFpBdWhuZFlXUWF4bDBYbmd3ZHBOQ1pvVWJBSVhNa0xLT1grM0FkWk5FYUtRRkJJNUJBc2JvaTV4R3FQd3FGWk1rSFJDbVl1Z0FBWVIwQ21UQ3Z4QW5pT0E0QUVvUUZITXJNQm9GSmNqSTRFV1FDaGpVMTBvRWtKbTZPZ1VPbmtnd2IzV1hrVmtjZ1MyUkNNRS9rbEFJaU5VQ010a09KK0ZHUVVURjBKK2FJSkpoTmJDeUhhNmFqc21rdmRBQ01qUk5FcTJ5RDNEZFowK3BUWUpkWlA2VHdnSkRDWEVjaEsvd0JXai9qUHFQdXFQU3VIY1lxQXQ4ZFBVSmxScm1Td3lEeTRSMlNtM0hDd1U5eUx0aVI5VStNdk5NMDBpNkRIVHVneDNjc25NcG9BbUU2MElPSU1vbVU3NFkyK3EwRXJPMllWYXZTdzdDK29ZQVZaMWJwRTlxemRRUDVjVmhIc0RUVGVCSTU2UjNBYWxFUU9YazF2MXVxYlpreDlEODJwbGVyUnhCNmd4ejVIMzZyQjR4bUtaSTFHb1V6VGxNRDJDQ0VITU9waEFOMlJieldXOFQvQ01EVlNUb0ZMenlRYS9jckkzY3BvYUpoUDJXYnVRY1NGVmZRYzBkVzF3TTdtUjlBbmFCUHE1WklWU28vR1ltTld0TUFjM2YxcXJSRGUwSnQvMkkxY2U0TEVNZVNLMUk5cm51N25ISUJVWE5yTXpzSG9KUG01eUxzclhIbDRmVnQxUmt0THovbDlCOVIzNmhVNmo4UFZGVm1vMS9uMzRGTXFOcVVnOW1oUWRaRjBxR2NsbGJLN0FUclFnWGJoUjNxQnpVQkFRQ25hamdBaDBsV0pBRFJDWlh4RlVGenJEWUt0bkRIT0owV0ZCWlJrYXg4M243S1d1SmFMRDRSM05iZDNxdXNibEwzV0JFbnViczBlTzZxVlhWSE9MYk5PdzBUTXcrRlllcm1ibE5pUGMrSTM1aFIzZS9jajBUY1ZpS0g2WWVRQVZnZWtSaUtuVm05cklTbVJCVFFOeWlaa2hIVk5UaVJFS1pZbVdaS1lTK1lGcktxMHRoTnhOTWt5NENEekNaVVpVbklaVEMwdmJsSU1tRlNyQjFGdVQrMWpYUDhBdzcvQlVzUlJhQkoveWJ0c0F2eERPcmdUT1YyMjVLeFZZT3BQRFFibHUyd0ZnZ1NKUUpHaW9FOWNJVFM4N2U3S3ROeWVTNklNWXdCQWlZVUFvRUZxbGdNYmxWWHNZSmNxYjJ2RWdyT3dtQWJycEExUHhUaEpSTldtMGlmbVYwQTk1RlF2Y2R0MWlYTWN3ak5OdWY4QVN4R0ZpbkZJRWs4bGhzWGljSVhaSXZ6VkJoWSt6aXFiWXdReEVid2Y0VmVxSDBuTTVyRHZKbzVocUlQN1RCK1Nob2NXblFFai93QXYwUHFuMDNWYVpZZFhRUEI3ZnVGR1J4RHhkQXdWZzZUbjFEVWl3K3Y5YWxRTnZmdUI2ckVmRVI1TG96QXZaWEZVZ2dEZm1ncm9Tb0tJbHFBZVZrM0lDZjBkVHFWQzk4ejRwL1IxSitzckM0U25ocHlUZE81cnFXdEdabHZNaGZnY0lkV0JkWkR2QlVNWFViVGRRYllPaEdpOVVwdzljc041dU8vWWp6Q0RCb0xpUDNNKzdWQWk1bVI1UEE3OW5CVTZUTVhWYzZwY0N3dkIreGhEbzdETXVRZk1pUGxkVVIxYnpSUGlOcmIrQTU3bFBPVUZ4OSs5ZlFMQVlWbExEZ1BBSmRjb01BYkFDeXhNSnJCbW5kRm8xVktBYnB6ZHdtYnlGTENMaS95UWJyS3VKbnU5RURMa0FDSWxFQWlBVVNRL0xsS2RUcjRrOXZENWZOUHcxUmh0Uko4MVNiaXoyVFNNTEY0Tjc2ZHhISThsU3FtZXJxQ0RyYm56Yi9JV0pjWXlOTjNjdEQza2JFS2pUeTB3eHR3T1JEaDZHNFdYSnJiOXJmdVZpbUVBVkc2aS91YitaV0R3eHhMaFZlSVlOQno5L01wb0JjaW51YTFzdU5reXMzcWcvVzZiT1crcWJjd2lCS1p2ZForeGxpL05OTGpZb3pOMFBpN29YNmU2QlpJUmlMZDZKQUZrTVBWcUU1R2t3bk5MYkVRVVNBc1Jnc05pbUVQYjVqVkRvdkdVYW1jT0RqNGtHUEZSaXdZZlNKOG1uNTJUS1dOZjhOUEw0d1BwZFV1am1EdFZqbVB5L3Z6VGJCVHlVbE9EY3ZhVFdzTk9OdUFnSno0ZEFWSWh6WlZSdmFCN2xRTXlFWHdTRUhvT1VoU0ZJRGxVNnJPZXJjU1BDRmNoWlpBbFFBRVNCb2cwSVFpbTJCV2x5ckZFQ0UyQzJ5RzZFcDBUb3FjR1lCVG01dDFTWmxsR25kWlZvaEI0YXFEQ014ZEZ3emFJUE1hQWVTQ0JJMFd5TUlhSW1TdG9SQkloTnNGTW9JNnBxY2JsVVNYU1UxNVBDR3JLRmxRSmk2ekdGbU1jSktFd3N6UnJaQnpYQ3hSUXMxTlRWS0FrS0lRMVZrSEJXT2lwakluTXk4WlF1ZzBJd0ZkU1ZKV1p5TE00a1hUV0FHVVVORm9aVWdCTmVEWkJ3R3E2MXUxMDF6anRDTUhVb1FOQXUwVmxqVk9NdHR0eEVJQUlGU2dVNi9DRUpGd2dTaXM3RzJsZGMwMkYxMnozTElOeWdCc0ZLRFNRZzBJUU5BaVNnSWFFTEFvQ3lGMEFvUVVxVm1RamRkZFNidXV2Y2ZnYXBxbTVNSU5CRnlTZzFvc0FneHNJQWxkVzdVMldWbzcwTzVhbS9CcU82Mld4UU5sZEEyUUtzRTZzMExySHVGZ2hUcXYxZDZJWWVudmRCcldqc2lFSlFDRENWMUpIeEw5TWQ2ekZPRHRpbW15SGNocndDaXkwQ2l4NENKVThQLy9aIiBpZD0iZSIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiLz48L2RlZnM+PC9zdmc+", + "description": "Displays flow rate data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'flowRate', label: 'Flow rate', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'm³/hr', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'flowRate', 'm³/hr', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flow rate\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m³/hr\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#2B54CE\"},{\"from\":10,\"to\":30,\"color\":\"#3B911C\"},{\"from\":30,\"to\":50,\"color\":\"#F77410\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m³/hr\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m³/hr\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:Zmxvd19yYXRlX2NoYXJ0X2NhcmRfYmFja2dyb3VuZC5wbmc=:Zmxvd19yYXRlX2NoYXJ0X2NhcmRfYmFja2dyb3VuZC5wbmc=;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Flow rate\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:hydro-power\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"margin\":\"0px\"}" + }, + "tags": [ + "liquid", + "fluid", + "flow rate", + "fluid dynamics", + "velocity", + "mass flow", + "volume flow" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/flow_rate_gauge.json b/application/src/main/data/json/system/widget_types/flow_rate_gauge.json new file mode 100644 index 0000000000..aa16d14af3 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/flow_rate_gauge.json @@ -0,0 +1,33 @@ +{ + "fqn": "flow_rate_gauge", + "name": "Flow rate gauge", + "deprecated": false, + "image": "tb-image:Zmxvd19yYXRlX2dhdWdlLnN2Zw==:Zmxvd19yYXRlX2dhdWdlLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48ZyBjbGlwLXBhdGg9InVybCgjYSkiPjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSIjZmZmIiByeD0iNCIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik0zMi4zIDE4LjVhMS42IDEuNiAwIDAgMC0xLjYgMWwtLjYgMmMtLjMgMC0uNi4xLS44LjNsLTEuNS0xLjYtMS0xLjFhMS42IDEuNiAwIDAgMCAwIDIuMmwxLjMgMS41YTIuNCAyLjQgMCAwIDAtLjIuOGwtMiAuNS0xLjYuM2ExLjYgMS42IDAgMCAwIDEuOSAxLjJsMi0uNC42LjYtLjYgMi0uNSAxLjVhMS42IDEuNiAwIDAgMCAyLTFsLjYtMiAuOC0uMiAxLjQgMS41IDEgMS4yYTEuNiAxLjYgMCAwIDAgLjItMi4yTDMyLjMgMjVsLjEtLjQuMS0uNCAyLS41IDEuNi0uM2ExLjYgMS42IDAgMCAwLTEuOS0xLjJsLTIgLjUtLjYtLjcuNi0yIC41LTEuNWgtLjRabS0yIDQuMmExLjIgMS4yIDAgMCAxIC40IDIuMyAxLjIgMS4yIDAgMSAxLS41LTIuM20tNS4xIDMuN2MtMSAuNy0yIDEtMy4xIDFoLTEuNlYyOUgyMmMxIDAgMi4xLS4zIDMuMS0uOC42LjMgMS4yLjYgMS45LjdsLjQtMS4zdi0uMmMtLjgtLjItMS42LS41LTIuMy0xWm05LjggMWMwIC42LS4xIDEuMi0uNSAxLjZoMS43di0xLjVoLTEuMlpNMjUgMjkuNmMtMSAuNy0yIDEtMy4xIDFoLTEuNnYxLjdIMjJjMSAwIDIuMS0uMyAzLjEtLjggMiAxIDQuMyAxIDYuMyAwIDEgLjUgMiAuOCAzLjEuOGgxLjZ2LTEuNmgtMS42Yy0xIDAtMi4yLS40LTMuMS0xYTUuNCA1LjQgMCAwIDEtNi4zIDBaTTQ2LjQgMjAuOHY4LjloLTEuNnYtOWgxLjZabTMuNiAzLjl2MS4yaC00di0xLjJoNFptLjUtNFYyMkg0NnYtMS4yaDQuNVptMi45LS40djkuNGgtMS41di05LjRoMS41Wm0xLjUgNi4xdi0uMWMwLS41IDAtMSAuMi0xLjMuMS0uNC4zLS44LjYtMS4xbDEtLjdhMy4xIDMuMSAwIDAgMSAyLjYgMGwxIC43LjYgMSAuMiAxLjR2LjFsLS4yIDEuNC0uNiAxYTIuOCAyLjggMCAwIDEtMi4zIDFjLS41IDAtMSAwLTEuMy0uMmwtMS0uOGMtLjItLjMtLjUtLjYtLjYtMWwtLjItMS40Wm0xLjUtLjF2MWwuNC43YzAgLjIuMy4zLjUuNWwuNy4xYTEuNCAxLjQgMCAwIDAgMS4yLS42bC4zLS43di0xLjhjMC0uMy0uMi0uNS0uMy0uN0ExLjQgMS40IDAgMCAwIDU4IDI0Yy0uMyAwLS41IDAtLjcuMmwtLjUuNWMtLjIuMi0uMy40LS4zLjdsLS4xLjhabTcuOSAyIDEuNS01LjJoMWwtLjMgMS41LTEuNSA1SDY0bC4yLTEuNFptLS45LTUuMiAxLjIgNS4xdjEuNWgtLjhMNjIgMjNoMS40Wm00LjggNSAxLjItNWgxLjRMNjkgMjkuN2gtMWwuMi0xLjVabS0xLjMtNSAxLjUgNSAuMiAxLjZoLS44bC0xLjYtNS4xTDY2IDIzaDFabTEwIDEuMnY1LjRoLTEuNlYyM2gxLjRsLjEgMS4yWm0yLTEuM3YxLjRhMi45IDIuOSAwIDAgMC0xLjMgMCAxLjMgMS4zIDAgMCAwLS44LjhsLS4xLjdoLS40bC4xLTEuMi40LTEgLjYtLjVhMS43IDEuNyAwIDAgMSAxLjItLjJoLjJabTQuNiA1LjR2LTMuMmwtLjEtLjYtLjQtLjQtLjctLjJjLS4yIDAtLjQgMC0uNi4yYTEgMSAwIDAgMC0uNC4zbC0uMi41aC0xLjRsLjEtLjguNi0uNmMuMi0uMi41LS40LjktLjVsMS0uMmMuNiAwIDEgLjEgMS40LjNsMSAuN2MuMi40LjMuOC4zIDEuM1YyOWwuMi42aC0xLjVsLS4yLS41di0uN1ptLjItMi43di45aC0xYTMgMyAwIDAgMC0uOCAwbC0uNS4zYTEgMSAwIDAgMC0uNC44bC4xLjUuNC4zLjUuMWExLjYgMS42IDAgMCAwIDEuNC0uNmwuMi0uNS41LjYtLjMuNi0uNS41YTIuMyAyLjMgMCAwIDEtMS41LjZjLS41IDAtLjkgMC0xLjItLjJhMiAyIDAgMCAxLS44LS44Yy0uMi0uMy0uMy0uNi0uMy0xIDAtLjMgMC0uNi4yLS45LjEtLjMuMy0uNS42LS43bDEtLjQgMS4yLS4xaDEuMlptNi0yLjZ2MUg4NnYtMWgzLjdabS0yLjYtMS43aDEuNXY2LjlsLjMuMi40LjFhMi4zIDIuMyAwIDAgMCAuNSAwdjFhMy44IDMuOCAwIDAgMS0xIC4yYy0uMyAwLS42IDAtLjktLjItLjIgMC0uNC0uMy0uNi0uNmwtLjItMXYtNi42Wm02LjkgOC40Yy0uNSAwLTEgMC0xLjMtLjJhMyAzIDAgMCAxLTEuNy0xLjdsLS4yLTEuM3YtLjJjMC0uNiAwLTEgLjItMS41bC43LTFjLjItLjMuNi0uNiAxLS43YTMgMyAwIDAgMSAxLjEtLjNjLjUgMCAxIC4xIDEuMy4zbC44LjZjLjMuMy41LjcuNiAxbC4yIDEuNHYuNmgtNS4ydi0xaDMuN3YtLjJjMC0uMiAwLS41LS4yLS43IDAtLjItLjItLjQtLjQtLjZsLS44LS4yYy0uMiAwLS40IDAtLjYuMi0uMiAwLS40LjItLjUuNGwtLjMuNy0uMSAxdi4ybC4xLjguNC43LjUuNC44LjFhMiAyIDAgMCAwIDEuNy0uOGwuNy44YTIuOCAyLjggMCAwIDEtMS40IDFsLTEuMS4yWiIvPjxnIGNsaXAtcGF0aD0idXJsKCNiKSI+PHJlY3Qgd2lkdGg9IjI2LjYiIGhlaWdodD0iMTIuOSIgeD0iODcuNiIgeT0iMTMwLjkiIGZpbGw9IiNGM0YzRjMiIHJ4PSIzLjIiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii41IiBkPSJNOTcuNSAxMzYuOXYxLjFsLS4xIDEuNC0uNSAxLS43LjQtMSAuMi0uNy0uMWExLjkgMS45IDAgMCAxLTEtLjlsLS40LS44VjEzNS41bC41LS45LjctLjUgMS0uMmMuMiAwIC41IDAgLjcuMmExLjggMS44IDAgMCAxIDEuMS44bC4zLjkuMSAxLjFabS0xLjEgMS4zVjEzNmwtLjItLjVhMSAxIDAgMCAwLS4yLS40bC0uNC0uMmgtLjNhMSAxIDAgMCAwLS41IDBsLS40LjQtLjIuNnYzbC4xLjYuMy4zLjMuMi40LjEuNC0uMWMuMiAwIC4zLS4yLjQtLjNsLjItLjZ2LTFabTcgMS44djFoLTQuN3YtLjlsMi4zLTIuNC41LS43LjMtLjYuMS0uNHYtLjZsLS40LS40YTEgMSAwIDAgMC0uNi0uMmwtLjYuMmExIDEgMCAwIDAtLjQuNWwtLjIuNmgtMS4xYzAtLjQgMC0uNy4zLTEgLjItLjQuNC0uNy44LS45LjMtLjIuNy0uMyAxLjItLjNzLjkuMSAxLjIuM2MuMy4xLjUuNC43LjYuMi4zLjMuNy4zIDFsLS4xLjctLjMuNi0uNS42LS42LjctMS40IDEuNmgzLjFabTUuNS0zLjF2MS4xYzAgLjYgMCAxLS4yIDEuNCAwIC40LS4yLjctLjQgMWwtLjcuNC0xIC4yLS43LS4xYTEuOSAxLjkgMCAwIDEtMS4xLS45bC0uMy0uOC0uMS0xLjJWMTM3bC4xLTEuNC41LS45LjctLjUgMS0uMmMuMiAwIC41IDAgLjcuMmExLjggMS44IDAgMCAxIDEgLjhsLjQuOXYxLjFabS0xLjEgMS4zVjEzNmwtLjItLjVhMSAxIDAgMCAwLS4zLS40bC0uMy0uMmgtLjRhMSAxIDAgMCAwLS40IDBsLS40LjQtLjIuNnYzbC4xLjYuMi4zLjQuMi4zLjEuNS0uMWMuMiAwIC4zLS4yLjQtLjNsLjItLjZ2LTFaIi8+PC9nPjxwYXRoIGZpbGw9IiMzMDVBRDciIGQ9Ik0xNDAuMyAxMzUuM2E1Ni4zIDU2LjMgMCAxIDAtNzkuNiAwbDYuOC02LjhhNDYuNyA0Ni43IDAgMSAxIDY2IDBsNi44IDYuOFoiLz48cGF0aCBmaWxsPSIjM0ZBNzFBIiBkPSJNMTQwLjMgMTM1LjNBNTYuMyA1Ni4zIDAgMSAwIDQ2IDExMGw5LjMtMi40YTQ2LjcgNDYuNyAwIDEgMSA3OC4yIDIxbDYuOCA2LjhaIi8+PHBhdGggZmlsbD0iI0YzNjkwMCIgZD0iTTE0MC4zIDEzNS4zYTU2LjMgNTYuMyAwIDAgMC03OS42LTc5LjZsNi44IDYuOGE0Ni43IDQ2LjcgMCAxIDEgNjYgNjZsNi44IDYuOFoiLz48cGF0aCBmaWxsPSIjRDgxODM4IiBkPSJNMTQwLjMgMTM1LjNhNTYuMyA1Ni4zIDAgMCAwLTI1LjItOTQuMmwtMi41IDkuM2E0Ni43IDQ2LjcgMCAwIDEgMjEgNzguMWw2LjcgNi44WiIvPjxwYXRoIGZpbGw9IiM4NDg0ODQiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0ibTE0MC41IDEzNS4xLS4yLjItMy4zLTMuMy4yLS4yIDEuNyAxLjcgMS42IDEuNlptMi41LTIuNy0uMi4zLTEuOC0xLjUtMS44LTEuNi4yLS4yIDEuOSAxLjUgMS43IDEuNVptMi40LTIuOC0uMy4zLTEuOC0xLjQtMi0xLjUuMy0uMyAyIDEuNSAxLjggMS40Wm0yLTMtLjEuMy0yLTEuMy0yLTEuMy4yLS4zIDIgMS4zIDIgMS4zWm0yLTMuMi0uMi40LTItMS4yLTItMS4yLjEtLjMgMi4xIDEuMiAyIDEuMVptMS43LTMuMi0uMS40LTIuMS0xLTIuMi0xLjIuMi0uMyAyLjIgMSAyIDFabTEuNS0zLjQtLjEuNC0yLjEtLjktMi4zLTEgLjItLjMgMi4yIDEgMi4xLjhabTEuMy0zLjQtLjEuNC0yLjItLjgtMi4zLS44LjEtLjMgMi4zLjcgMi4yLjhabTEtMy41di40bC0yLjMtLjctMi4zLS42LjEtLjQgMi4zLjcgMi4zLjZabS45LTMuNnYuNGwtMi4zLS41LTIuNC0uNXYtLjNsMi40LjQgMi4zLjVabS42LTMuN3YuNGwtMi4zLS4zLTIuNC0uM3YtLjRsMi40LjMgMi4zLjNabS4zLTMuNnYuNGwtMi4zLS4yLTIuNC0uMXYtLjRsMi40LjEgMi4zLjJabS4xLTMuN3YuNEgxNTIuMXYtLjRIMTU2LjhabS0uMS0zLjd2LjRsLTIuMy4yLTIuNC4xVjkybDIuNC0uMiAyLjMtLjJabS0uNC0zLjZ2LjRsLTIuMi4zLTIuNC4zdi0uNGwyLjMtLjMgMi4zLS4zWm0tLjYtMy43LjEuNC0yLjMuNS0yLjMuNC0uMS0uMyAyLjQtLjUgMi4yLS41Wm0tLjgtMy42di40bC0yLjIuNi0yLjMuN1Y4MmwyLjItLjYgMi4zLS43Wm0tMS4xLTMuNS4xLjQtMi4yLjctMi4zLjh2LS4zbDIuMi0uOCAyLjItLjhabS0xLjMtMy40di4xbC4xLjMtMi4xLjgtMi4yIDEtLjItLjQgMi4zLTEgMi0uOFptLTEuNS0zLjQuMS40LTIgMS0yLjIgMS0uMi0uMyAyLjItMSAyLTFabS0xLjgtMy4yLjIuNC0yIDEuMS0yIDEuMi0uMi0uMyAyLTEuMiAyLTEuMlptLTItMy4xLjMuMy0yIDEuMy0yIDEuM3MwLS4yLS4yLS4zbDItMS4zIDItMS4zWm0tMi0zYzAgLjEgMCAuMi4yLjNsLTEuOSAxLjQtMS45IDEuNS0uMi0uMyAxLjktMS41IDEuOC0xLjRabS0yLjQtMi44LjIuMy0xLjcgMS41LTEuOCAxLjYtLjMtLjMgMS44LTEuNiAxLjgtMS41Wm0tMi42LTIuNy4zLjMtMS42IDEuNi0xLjcgMS43LS4yLS4yIDEuNi0xLjggMS42LTEuNlptLTIuNi0yLjUuMy4yLTEuNiAxLjgtMS42IDEuOC0uMy0uMiAxLjYtMS45IDEuNi0xLjdabS0yLjktMi40LjMuMy0xLjQgMS44LTEuNSAyLS4zLS4zIDEuNS0xLjkgMS40LTEuOVptLTMtMi4xYTkuMiA5LjIgMCAwIDAgLjMuMmwtMS4zIDItMS4zIDItLjMtLjIgMS4zLTIgMS4zLTJabS0zLjEtMiAuMy4zLTEuMiAyLTEuMiAyLS4zLS4xIDEuMi0yIDEuMi0yLjFabS0zLjMtMS43LjQuMi0xIDIuMS0xLjEgMi4yLS40LS4yIDEtMi4xIDEuMS0yLjJabS0zLjMtMS41LjMuMi0uOSAyLjItMSAyLjItLjMtLjIgMS0yLjIuOS0yLjJabS0zLjUtMS4zLjQuMi0uOCAyLjItLjggMi4zLS4zLS4xLjgtMi4zLjctMi4zWm0tMy4xLTEtLjYgMi40LS43IDIuMy0uMy0uMS42LTIuMy42LTIuM2guNFptLTQtLjkuNC4xLS41IDIuMy0uNSAyLjRoLS4zbC40LTIuNC41LTIuNFptLTMuNi0uNmguNGwtLjQgMi41LS4zIDIuM2gtLjRsLjQtMi40LjMtMi40Wm0tMy43LS4zaC40bC0uMiAyLjQtLjEgMi40aC0uNGwuMS0yLjQuMi0yLjRabS0zLjctLjFoLjRWNDRoLS40di00LjhabS0zLjcgMGguNGwuMiAyLjUuMSAyLjRoLS4zbC0uMi0yLjQtLjItMi40Wk02NC4xIDEzMmwtMy40IDMuNHYtLjFsMS42LTEuNyAxLjctMS43aC4xWk05My44IDQybC0uMy0yLjRIOTNsLjMgMi41LjMgMi40LjQtLjEtLjMtMi40Wm0tMy41LjYtLjUtMi40aC0uNGwuNSAyLjUuNSAyLjNoLjRsLS41LTIuNFptLTMuNS44LS42LTIuNGE0OC43IDQ4LjcgMCAwIDAtLjQuMWwuNyAyLjQuNiAyLjNoLjJsLjItLjEtLjctMi4zWm0tMy4zIDEtLjgtMi4zLS40LjEuOCAyLjMuOCAyLjMuMy0uMS0uNy0yLjNaTTgwIDQ1LjZsLS45LTIuMi0uMy4xLjkgMi4zIDEgMi4yLjMtLjItMS0yLjJaTTc3IDQ3IDc2IDQ1bC0uNC4yIDEgMi4xIDEuMSAyLjIuNC0uMi0xLTIuMlptLTMgMS43LTEuMy0yLjEtLjMuMiAxLjIgMiAxLjIgMi4yLjMtLjItMS4yLTIuMVptLTMgMS44LTEuNC0yLS40LjMgMS40IDIgMS4zIDIgLjMtLjItMS4zLTJabS0zIDItMS40LTEuOC0uNC4yIDEuNSAyIDEuNSAxLjguMy0uMi0xLjUtMlptLTIuNyAyLjNMNjMuNiA1M2wtLjMuMyAxLjYgMS44IDEuNiAxLjguMy0uMy0xLjYtMS44Wm0tMi42IDIuNEw2MSA1NS41bC0uMy4zIDEuNyAxLjcgMS44IDEuNy4yLS4zLTEuNy0xLjdabS0yLjQgMi42LTEuOC0xLjYtLjMuMyAxLjggMS42IDEuOSAxLjYuMi0uMy0xLjgtMS42Wk01OCA2Mi41IDU2IDYxbC0uMi40IDIgMS40IDEuOCAxLjUuMy0uMy0yLTEuNVptLTIuMSAyLjktMi0xLjQtLjIuNCAyIDEuMyAyIDEuMy4yLS4zLTItMS4zWm0tMS45IDMtMi0xLjItLjMuMyAyLjEgMS4yTDU2IDcwbC4yLS4zLTItMS4yWm0tMS43IDMtMi4xLTEtLjIuNCAyLjIgMSAyLjEgMSAuMi0uMi0yLjItMS4xWk01MSA3NC44bC0yLjMtMS0uMS40IDIuMiAxIDIuMi45LjItLjQtMi4yLTFaTTQ5LjYgNzhsLTIuMy0uOC0uMS40IDIuMy44IDIuMy43di0uM2wtMi4yLS44Wm0tMSAzLjQtMi40LS43di40bDIuMy42IDIuMy43LjEtLjQtMi4zLS42Wm0tLjggMy40LTIuNC0uNXYuNGwyLjMuNSAyLjMuNC4xLS4zLTIuMy0uNVptLS42IDMuNS0yLjQtLjR2LjRsMi4zLjQgMi40LjN2LS40bC0yLjMtLjNabS0uNCAzLjUtMi40LS4ydi40bDIuNC4yIDIuNC4xVjkybC0yLjQtLjFabS0uMSAzLjVoLTIuNHYuNEg0OXYtLjRoLTIuNFptMCAzLjUtMi4zLjJ2LjRsMi40LS4yIDIuNC0uMnYtLjNsLTIuNC4xWm0uNCAzLjUtMi40LjN2LjVsMi41LS40IDIuNC0uM3YtLjRsLTIuNS4zWm0uNiAzLjUtMi40LjUuMS40IDIuNC0uNSAyLjMtLjV2LS4zbC0yLjQuNFptLTEuNSA0LjN2LS4ybDIuMy0uNiAyLjMtLjd2LjJsLjEuMi0yLjMuNi0yLjQuN3YtLjJabTMuMyAyLjYtMi4zLjcuMS40IDIuMy0uOCAyLjMtLjgtLjEtLjMtMi4zLjhabTEuMiAzLjMtMi4yLjkuMS40IDIuMy0xIDIuMi0xLS4xLS4zLTIuMyAxWm0xLjUgMy4yLTIuMiAxIC4yLjQgMi4xLTEgMi4yLTEuMS0uMi0uNC0yLjEgMVptMS42IDMuMS0yIDEuMi4xLjQgMi4xLTEuMyAyLTEuMi0uMS0uMy0yLjEgMS4yWm0xLjkgMy0yIDEuM2MwIC4yIDAgLjMuMi40bDItMS40IDItMS4zLS4yLS4zLTIgMS4zWm0yIDIuOS0yIDEuNGMuMi4xLjIuMy4zLjRsMi0xLjUgMS44LTEuNS0uMi0uMy0xLjkgMS41Wm0yLjIgMi43LTEuOCAxLjYuMy4zIDEuOC0xLjYgMS44LTEuNi0uMy0uM0w2MCAxMzFaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48cGF0aCBmaWxsPSIjNjM2MzYzIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Im0xNDAuNyAxMzUtLjQuMy02LjctNi43LjMtLjMgMy40IDMuMyAzLjQgMy40Wm0xNC4zLTI1LjQtLjIuOS00LjUtMS4zLTQuNy0xLjMuMi0uNyA0LjcgMS4yIDQuNSAxLjJabS0uMi0yOSAuMi44LTQuNSAxLjItNC43IDEuMnYtLjJsLS4xLS4yLS4xLS4zIDQuNy0xLjMgNC41LTEuM1ptLTE0LjctMjUuMi42LjYtMy40IDMuNC0zLjQgMy40LS41LS42IDMuMy0zLjQgMy40LTMuNFptLTI0LjUtMTQuMi0xLjMgNC42LTEuMyA0LjctLjctLjIgMS4yLTQuNyAxLjItNC42IDEgLjJabS00OC4zIDg3LjFzMCAuMi4yLjJsLTYuOCA2LjgtLjItLjMgMy40LTMuNCAzLjQtMy4zWm0tMTItMjEgLjIuNy00LjUgMS4yLTQuNyAxLjNhNjkuNiA2OS42IDAgMCAxLS4yLS45bDQuNy0xLjIgNC41LTEuMVptLjItMjQuMy0uMi43LTQuNi0xLjEtNC42LTEuMi4yLTEgNC43IDEuNCA0LjUgMS4yWm0xMi4zLTIwLjktLjUuNi0zLjQtMy4zLTMuNC0zLjQuNi0uNyAzLjQgMy41IDMuMyAzLjNabTIxLTExLjgtLjcuMUw4NyA0NmwtMS4zLTQuN2E2MCA2MCAwIDAgMSAuOS0uMmwxLjIgNC42IDEuMiA0LjdaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48cGF0aCBmaWxsPSIjNjM2MzYzIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNTUuOCAxMDYuNWMtMi4yIDExLTcuNiAyMS0xNS41IDI4LjhsLS41LS41YTU1LjUgNTUuNSAwIDEgMC03OC41IDBsLS42LjVhNTYuMyA1Ni4zIDAgMSAxIDk1LTI4LjhaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48cGF0aCBmaWxsPSIjNjM2MzYzIiBkPSJNNjAuNSA4NnYuOGgtMy44di0uNmwxLjgtMiAuNS0uNy4yLS40YTEuMSAxLjEgMCAwIDAgMC0uOUw1OSA4MmEuOS45IDAgMCAwLS40LS4xIDEgMSAwIDAgMC0uNi4xbC0uMy40LS4xLjVoLTFsLjMtLjkuNi0uNmEyIDIgMCAwIDEgMS0uM2MuNSAwIC44IDAgMSAuMi4zLjEuNS4zLjcuNmwuMi44LS4xLjVjMCAuMi0uMi40LS4zLjVsLS4zLjYtLjUuNS0xLjIgMS4zaDIuNlptNC40LTIuNXYuOWwtLjEgMS4xLS40LjgtLjYuNGEyLjIgMi4yIDAgMCAxLTEuNCAwIDEuNSAxLjUgMCAwIDEtLjktLjdsLS4yLS43LS4xLS45di0xbC4xLTEgLjQtLjguNi0uNC44LS4yLjYuMWExLjUgMS41IDAgMCAxIC45LjdsLjIuNy4xIDFabS0xIDFWODIuM2wtLjMtLjMtLjItLjJhMSAxIDAgMCAwLS4zIDBoLS40bC0uMy4zLS4yLjV2M2wuMy4zLjIuMmguOGwuMi0uMy4yLS41di0uOFpNNzEuNiAxMjQuNXYyYy0uMi40LS4zLjctLjUuOC0uMS4yLS4zLjQtLjYuNWEyLjIgMi4yIDAgMCAxLTEuNCAwIDEuNSAxLjUgMCAwIDEtLjktLjdsLS4yLS43di0zbC40LS44LjYtLjQuOC0uMWguNmExLjUgMS41IDAgMCAxIDEgLjhsLjIuN3YxWm0tLjkgMVYxMjMuOWwtLjItLjUtLjEtLjMtLjMtLjFhMSAxIDAgMCAwLS4zIDBoLS40bC0uMy4zLS4yLjV2Mi41bC4xLjRjMCAuMi4xLjMuMi4zbC4zLjJoLjdsLjMtLjMuMS0uNS4xLS43Wk01OS4yIDEwNC40djUuN2gtLjl2LTQuNmwtMS40LjV2LS44bDIuMi0uOGguMVptNS43IDIuNHYxbC0uMSAxYy0uMS40LS4yLjctLjQuOC0uMi4yLS4zLjQtLjYuNWEyLjIgMi4yIDAgMCAxLTEuNCAwIDEuNSAxLjUgMCAwIDEtLjktLjdsLS4yLS43LS4xLTF2LS45bC4xLTEuMS40LS44LjYtLjQuOC0uMWguNmExLjUgMS41IDAgMCAxIC45LjhsLjIuNy4xIDFabS0xIDF2LTIuMmwtLjMtLjMtLjItLjFhMSAxIDAgMCAwLS4zIDBoLS40Yy0uMiAwLS4yLjEtLjMuMy0uMSAwLS4yLjItLjIuNXYyLjlsLjMuMy4yLjJoLjhsLjItLjMuMi0uNXYtLjdaTTY5IDY1LjhoMS4xbC40LS40di0uOWMwLS4yLS4yLS4zLS4zLS4zYTEgMSAwIDAgMC0uNS0uMSAxIDEgMCAwIDAtLjUgMCAuOC44IDAgMCAwLS40LjhoLTFjMC0uMy4xLS42LjMtLjhsLjctLjZhMiAyIDAgMCAxIC45LS4yYy40IDAgLjcgMCAxIC4ybC42LjUuMiAxdi40bC0uNC41LS42LjMtLjguMUg2OXYtLjVabTAgLjhWNjZoLjdjLjQgMCAuNyAwIC45LjJsLjYuMy4zLjQuMS42LS4xLjctLjQuNS0uNy40YTIuNSAyLjUgMCAwIDEtMS40IDAgMiAyIDAgMCAxLS42LS4zbC0uNC0uNS0uMi0uN2gxdi40bC40LjMuNS4xaC41bC4zLS40LjItLjVjMC0uMiAwLS40LS4yLS41IDAtLjItLjItLjMtLjMtLjNsLS42LS4xSDY5Wm03LjItLjh2LjlsLS4xIDEuMS0uNC44LS42LjRhMi4yIDIuMiAwIDAgMS0xLjQgMCAxLjUgMS41IDAgMCAxLTEtLjdsLS4yLS43di0yLjlsLjUtLjhjLjEtLjIuMy0uNC42LS40bC43LS4yLjYuMWExLjUgMS41IDAgMCAxIDEgLjdsLjIuN3YxWm0tMSAxVjY1bC0uMS0uNC0uMi0uMy0uMy0uMmExIDEgMCAwIDAtLjMgMEg3NGwtLjMuMy0uMS41LS4xLjdWNjcuNGwuMi41LjEuMy4zLjJoLjdsLjMtLjMuMi0uNXYtLjhaTTg5LjYgNTYuNnYuOGgtNC4xdi0uNmwyLjQtMy45aC43bC0uOCAxLjQtMS40IDIuM2gzLjJabS0uOC0zLjd2NS43SDg4VjUzaDFabTUgMi40djJsLS40LjgtLjYuNWEyLjIgMi4yIDAgMCAxLTEuNCAwIDEuNSAxLjUgMCAwIDEtLjktLjdsLS4zLS43di0xLjlsLjEtMS4xYzAtLjMuMi0uNi40LS44LjEtLjIuMy0uMy42LS40bC43LS4xaC43YTEuNSAxLjUgMCAwIDEgLjkuOGwuMi43djFabS0uOCAxVjU0LjdsLS4yLS41LS4yLS4zLS4yLS4xYTEgMSAwIDAgMC0uNCAwaC0uNGwtLjMuMy0uMS40djNsLjMuMy4yLjJoLjdsLjMtLjMuMi0uNXYtLjdaTTEyNS43IDEyNy4xaC4xbDEtLjFhMS40IDEuNCAwIDAgMCAuOC0xdi0yLjRjMC0uMiAwLS4zLS4yLS40bC0uMy0uM2gtLjdsLS4zLjMtLjIuNGExLjggMS44IDAgMCAwIC4yIDEuM3MuMS4yLjMuMmwuNC4xYTEgMSAwIDAgMCAuNy0uM2wuMi0uM3YtLjRsLjQuMXYuNmExLjggMS44IDAgMCAxLTEgMWgtMS4zbC0uNS0uNS0uMy0uNmEyLjUgMi41IDAgMCAxIDAtMS41Yy4xLS4yLjItLjUuNC0uNmwuNi0uNS44LS4xYy4zIDAgLjUgMCAuOC4yLjIgMCAuNC4yLjYuNGwuMy44LjEuOXYxLjNsLS40LjhjMCAuMy0uMy41LS41LjdsLS44LjUtMSAuMmgtLjJ2LS44Wm03LjUtMi42djFsLS4xIDFjMCAuNC0uMi43LS40LjgtLjEuMi0uMy40LS42LjVhMi4yIDIuMiAwIDAgMS0xLjQgMCAxLjUgMS41IDAgMCAxLS45LS43bC0uMi0uNy0uMS0xdi0uOWwuMS0xLjEuNC0uOC42LS40LjgtLjFoLjZhMS41IDEuNSAwIDAgMSAuOS44bC4yLjcuMSAxWm0tMSAxdi0yLjJsLS4zLS4zLS4yLS4xYTEgMSAwIDAgMC0uMyAwaC0uNGwtLjMuMy0uMi41djIuNWwuMS40YzAgLjIuMS4zLjIuM2wuMi4yaC44bC4zLS4zLjEtLjV2LS43Wk0xMzkuMiAxMDguNmwtLjMuOS0uNy41Yy0uMi4yLS42LjItMSAuMi0uMyAwLS42IDAtLjktLjItLjMtLjEtLjUtLjMtLjYtLjUtLjItLjMtLjMtLjYtLjMtMWExLjUgMS41IDAgMCAxIC41LTFsLjYtLjRoLjhsMSAuMWMuMi4xLjUuMy42LjYuMi4yLjMuNS4zLjhabS0xIDAtLjEtLjYtLjMtLjNhMSAxIDAgMCAwLS41LS4xIDEgMSAwIDAgMC0uNS4xbC0uMy4zLS4xLjV2LjVsLjQuNGgxbC4zLS40LjEtLjVabS44LTIuN2MwIC4zIDAgLjYtLjIuOGwtLjYuNWEyIDIgMCAwIDEtMSAuMmMtLjMgMC0uNiAwLS44LS4yLS4zLS4xLS41LS4zLS42LS41LS4yLS4yLS4zLS41LS4zLS44IDAtLjMuMS0uNi4zLS44LjEtLjIuMy0uNC42LS41LjItLjIuNS0uMi45LS4yLjMgMCAuNiAwIC45LjJhMS40IDEuNCAwIDAgMSAuOCAxLjNabS0xIDB2LS40YS43LjcgMCAwIDAtLjctLjRsLS40LjFjLS4yIDAtLjMuMi0uMy4zbC0uMS41di40YS43LjcgMCAwIDAgLjguNGguNGwuMy0uNHYtLjRabTUuNyAxdi44bC0uMSAxLjItLjQuNy0uNi41YTIuMiAyLjIgMCAwIDEtMS40IDAgMS41IDEuNSAwIDAgMS0uOS0uN2wtLjMtLjd2LTEuOWwuMS0xLjFjMC0uMy4yLS42LjQtLjguMS0uMi4zLS4zLjYtLjRsLjctLjFoLjdhMS41IDEuNSAwIDAgMSAuOS44bC4yLjd2MVptLTEgMVYxMDZsLS4xLS41LS4yLS4zLS4yLS4xYTEgMSAwIDAgMC0uNCAwaC0uNGwtLjMuMy0uMS41djIuOWwuMy4zLjIuMmguN2wuMy0uMy4yLS41di0uN1pNMTQwIDgxLjF2LjVsLTIuMyA1LjJoLTFsMi4zLTVoLTN2LS43aDRabTQuNSAyLjR2LjlsLS4xIDEuMS0uNC44LS42LjRhMi4yIDIuMiAwIDAgMS0xLjQgMCAxLjUgMS41IDAgMCAxLS45LS43bC0uMi0uNy0uMS0uOXYtMWwuMS0xYzAtLjQuMi0uNi40LS44LjEtLjIuMy0uMy42LS40bC43LS4yLjcuMWExLjUgMS41IDAgMCAxIC45LjdsLjIuNy4xIDFabS0xIDFWODIuN2wtLjEtLjQtLjItLjMtLjItLjJhMSAxIDAgMCAwLS40IDBoLS40bC0uMi4zLS4yLjV2M2wuMy4zLjIuMmguN2wuMy0uMy4yLS41di0uOFpNMTI3LjggNjMuNGguMXYuN2MtLjQgMC0uNyAwLS45LjJsLS42LjRjLS4yLjItLjMuNC0uMy43bC0uMi43djFsLjEuNS4yLjUuMy4yLjQuMWguNGwuMy0uMy4yLS40YTEuNyAxLjcgMCAwIDAgMC0xbC0uMi0uMy0uMy0uMi0uNC0uMWExIDEgMCAwIDAtLjUuMSAxIDEgMCAwIDAtLjQuNGwtLjEuNC0uNC0uMWMwLS4zIDAtLjUuMi0uNyAwLS4yLjItLjMuMy0uNWwuNS0uM2gxLjRsLjUuNS4zLjZhMi41IDIuNSAwIDAgMSAwIDEuNWwtLjQuNi0uNi40YTIgMiAwIDAgMS0uOC4yYy0uMyAwLS42IDAtLjgtLjJsLS42LS41LS40LS43LS4xLS45di0uM2MwLS41IDAtLjkuMi0xLjMgMC0uNC4yLS43LjUtMWwuOC0uN2EzIDMgMCAwIDEgMS4zLS4yWm01LjQgMi40di45bC0uMSAxLjFjMCAuNC0uMi42LS40LjgtLjEuMi0uMy4zLS42LjRhMi4yIDIuMiAwIDAgMS0xLjQgMCAxLjUgMS41IDAgMCAxLS45LS43bC0uMi0uNy0uMS0uOXYtMWwuMS0xIC40LS44Yy4yLS4yLjQtLjQuNi0uNGwuOC0uMi42LjFhMS41IDEuNSAwIDAgMSAuOS43bC4yLjcuMSAxWm0tMSAxVjY0LjZsLS4zLS4zLS4yLS4yYTEgMSAwIDAgMC0uMyAwaC0uNGwtLjMuMy0uMi41djIuNWwuMS41LjIuMy4yLjJoLjhsLjMtLjMuMS0uNXYtLjhaTTExMC43IDU2bC0uOC0uMi4zLTIuOWgzLjF2LjhIMTExbC0uMSAxLjRhMS44IDEuOCAwIDAgMSAuOS0uMmwuNy4xLjUuNC40LjZhMi41IDIuNSAwIDAgMSAwIDEuNWwtLjQuNy0uNi40LS44LjEtLjctLjEtLjYtLjNhMS42IDEuNiAwIDAgMS0uNS0xLjJoLjlsLjEuNS4zLjNoLjlsLjMtLjMuMi0uM2ExLjggMS44IDAgMCAwIDAtMSAxIDEgMCAwIDAtLjItLjNsLS4zLS4zYTEgMSAwIDAgMC0uNSAwaC0uNWwtLjMuM1ptNy4yLS43djJsLS41LjhjLS4xLjItLjMuNC0uNi41YTIuMiAyLjIgMCAwIDEtMS40IDAgMS41IDEuNSAwIDAgMS0uOS0uN2wtLjItLjd2LTNsLjQtLjguNi0uNC44LS4xaC42YTEuNSAxLjUgMCAwIDEgMSAuOGwuMi43djFabS0uOSAxVjU0LjdsLS4yLS41LS4xLS4zLS4zLS4xYTEgMSAwIDAgMC0uMyAwaC0uNGwtLjMuMy0uMi40VjU3bC4xLjQuMi4zLjMuMmguN2wuMy0uMy4xLS41LjEtLjdaIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTg4LjQgMTE1LjV2NGgtMXYtNWgxdjFabS0uMSAxLjNoLS40bC4xLTEgLjQtLjguNi0uNSAxLS4yLjUuMS42LjMuMy42LjEuOHYzLjRoLTEuMXYtMy4zbC0uMS0uNi0uNC0uM2gtMWExIDEgMCAwIDAtLjQuNGwtLjIuNXYuNlptMy4xLS4zLS41LjEuMS0uOS40LS43YTEuOCAxLjggMCAwIDEgMS41LS43bC43LjEuNS40Yy4yLjEuMy4zLjMuNmwuMi44djMuM2gtMS4ydi0zLjNsLS4xLS42LS4zLS4zaC0uNWExIDEgMCAwIDAtLjUgMCAxIDEgMCAwIDAtLjMuM2wtLjIuNHYuNVptNS4yLTIuM2guOGwuMi0uMnYtLjNsLS4xLS4zLS40LS4xaC0uNGwtLjIuNGgtLjhjMC0uMyAwLS41LjItLjYgMC0uMi4yLS4zLjUtLjRoMS40bC41LjQuMi42YzAgLjEgMCAuMy0uMi40YTEgMSAwIDAgMS0uNC40SDk2LjZ2LS4zWm0wIC42di0uNEg5OGwuNS40LjIuNS0uMi42LS42LjRhMi4yIDIuMiAwIDAgMS0xLjQgMGMtLjMgMC0uNC0uMi0uNi0uNGExIDEgMCAwIDEtLjItLjZoLjlsLjEuMy41LjFoLjRsLjItLjR2LS4zbC0uMy0uMmExIDEgMCAwIDAtLjQgMGgtLjRabTYtMi4xTDEwMCAxMjBoLS45bDIuNy03LjRoLjlabTIuMi0uNHY3LjJoLTEuMXYtNy4yaDEuMVptLS4yIDQuNWgtLjRjMC0uNCAwLS43LjItMSAwLS4zLjItLjYuNC0uOGExLjggMS44IDAgMCAxIDEuNC0uN2wuNy4xLjUuNGMuMi4xLjMuMy40LjZ2NC4xaC0xdi0zLjNjMC0uMiAwLS40LS4yLS41IDAtLjItLjEtLjMtLjMtLjRoLTFsLS40LjQtLjIuNS0uMS42Wm01LjgtMS40djQuMWgtMS4ydi01aDEuMXYuOVptMS41LTF2MWEyLjMgMi4zIDAgMCAwLTEgMCAxIDEgMCAwIDAtLjUuN2wtLjEuNWgtLjNsLjEtLjljMC0uMy4yLS41LjMtLjdsLjUtLjVhMS4zIDEuMyAwIDAgMSAuOS0uMmguMVoiLz48cGF0aCBmaWxsPSIjRTY1MTAwIiBkPSJNMTAyLjMgOTUuMyA1MS4yIDgyLjZsNDkgMTcuMyAxMS42IDIuNWMuMiAwIC40IDAgLjUtLjJsLjgtMmMwLS4yIDAtLjQtLjItLjVsLTEwLjYtNC40WiIvPjxnIGZpbHRlcj0idXJsKCNjKSI+PGNpcmNsZSBjeD0iMTAwLjUiIGN5PSI5Ny4xIiByPSI0LjgiIGZpbGw9IiNmZmYiLz48L2c+PGcgZmlsdGVyPSJ1cmwoI2QpIj48Y2lyY2xlIGN4PSIxMDAuNSIgY3k9Ijk3LjEiIHI9IjMuMiIgZmlsbD0iI2ZmZiIvPjwvZz48L2c+PHJlY3Qgd2lkdGg9IjE5OS4yIiBoZWlnaHQ9IjE1OS4yIiB4PSIuNCIgeT0iLjQiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii4xIiBzdHJva2Utd2lkdGg9Ii44IiByeD0iMy42Ii8+PGRlZnM+PGNsaXBQYXRoIGlkPSJhIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48L2NsaXBQYXRoPjxjbGlwUGF0aCBpZD0iYiI+PHJlY3Qgd2lkdGg9IjI2LjYiIGhlaWdodD0iMTIuOSIgeD0iODcuNiIgeT0iMTMwLjkiIGZpbGw9IiNmZmYiIHJ4PSIzLjIiLz48L2NsaXBQYXRoPjxmaWx0ZXIgaWQ9ImMiIHdpZHRoPSIzOC42IiBoZWlnaHQ9IjM4LjYiIHg9IjgxLjIiIHk9Ijc3LjgiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUNvbG9yTWF0cml4IGluPSJTb3VyY2VBbHBoYSIgcmVzdWx0PSJoYXJkQWxwaGEiIHZhbHVlcz0iMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMTI3IDAiLz48ZmVNb3JwaG9sb2d5IGluPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImRpbGF0ZSIgcmFkaXVzPSIxLjYiIHJlc3VsdD0iZWZmZWN0MV9kcm9wU2hhZG93XzE0OTJfMzcwNSIvPjxmZU9mZnNldC8+PGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iNi40Ii8+PGZlQ29tcG9zaXRlIGluMj0iaGFyZEFscGhhIiBvcGVyYXRvcj0ib3V0Ii8+PGZlQ29sb3JNYXRyaXggdmFsdWVzPSIwIDAgMCAwIDEgMCAwIDAgMCAwLjI0MTY2NyAwIDAgMCAwIDAuMjQxNjY3IDAgMCAwIDAuMTUgMCIvPjxmZUJsZW5kIGluMj0iQmFja2dyb3VuZEltYWdlRml4IiByZXN1bHQ9ImVmZmVjdDFfZHJvcFNoYWRvd18xNDkyXzM3MDUiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2Ryb3BTaGFkb3dfMTQ5Ml8zNzA1IiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PGZpbHRlciBpZD0iZCIgd2lkdGg9IjEyLjkiIGhlaWdodD0iMTIuOSIgeD0iOTQuMSIgeT0iOTMuOSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlQ29sb3JNYXRyaXggaW49IlNvdXJjZUFscGhhIiByZXN1bHQ9ImhhcmRBbHBoYSIgdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAxMjcgMCIvPjxmZU9mZnNldCBkeT0iMy4yIi8+PGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iMS42Ii8+PGZlQ29tcG9zaXRlIGluMj0iaGFyZEFscGhhIiBvcGVyYXRvcj0ib3V0Ii8+PGZlQ29sb3JNYXRyaXggdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwLjI1IDAiLz48ZmVCbGVuZCBpbjI9IkJhY2tncm91bmRJbWFnZUZpeCIgcmVzdWx0PSJlZmZlY3QxX2Ryb3BTaGFkb3dfMTQ5Ml8zNzA1Ii8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9kcm9wU2hhZG93XzE0OTJfMzcwNSIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjwvZGVmcz48L3N2Zz4=", + "description": "Flow rate gauge.", + "descriptor": { + "type": "latest", + "sizeX": 6, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'flowRate', label: 'Flow rate', type: 'timeseries' }];\n }\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-analogue-radial-gauge-widget-settings", + "dataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-radial-gauge-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flow rate\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 90) {\\n\\tvalue = 90;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"startAngle\":45,\"ticksAngle\":270,\"needleCircleSize\":8,\"defaultColor\":\"#e65100\",\"minValue\":0,\"maxValue\":90,\"majorTicksCount\":9,\"colorMajorTicks\":\"#444\",\"minorTicks\":9,\"colorMinorTicks\":\"#666\",\"numbersFont\":{\"family\":\"Roboto\",\"size\":18,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"numbersColor\":\"#616161\",\"showUnitTitle\":false,\"unitTitle\":\"\",\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#888\"},\"titleColor\":\"#888\",\"unitsFont\":{\"size\":26,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"color\":\"#616161\"},\"unitsColor\":\"#616161\",\"valueBox\":true,\"valueInt\":3,\"valueFont\":{\"size\":27,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"color\":\"rgba(0, 0, 0, 0.54)\",\"shadowColor\":\"#FFFFFF01\"},\"valueColor\":\"rgba(0, 0, 0, 0.54)\",\"valueColorShadow\":\"#FFFFFF01\",\"colorValueBoxRect\":\"#88888800\",\"colorValueBoxRectEnd\":\"#66666600\",\"colorValueBoxBackground\":\"rgba(243, 243, 243, 0.54)\",\"colorValueBoxShadow\":\"rgba(0, 0, 0, 0)\",\"showBorder\":false,\"colorPlate\":\"#FFFFFF\",\"colorNeedle\":null,\"colorNeedleEnd\":null,\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"colorNeedleShadowDown\":\"rgba(188,143,143,0.45)\",\"highlightsWidth\":15,\"highlights\":[{\"from\":0,\"to\":10,\"color\":\"#305AD7\"},{\"from\":10,\"to\":30,\"color\":\"#3FA71A\"},{\"from\":30,\"to\":50,\"color\":\"#F36900\"},{\"from\":50,\"to\":90,\"color\":\"#D81838\"}],\"animation\":true,\"animationDuration\":500,\"animationRule\":\"cycle\"},\"title\":\"Flow rate\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"decimals\":0,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\",\"units\":\"m³/hr\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":null,\"lineHeight\":\"24px\"},\"showTitleIcon\":true,\"titleTooltip\":\"\",\"titleIcon\":\"mdi:hydro-power\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"actions\":{},\"margin\":\"0px\",\"borderRadius\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "liquid", + "fluid", + "flow rate", + "fluid dynamics", + "velocity", + "mass flow", + "volume flow" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/flow_rate_progress_bar.json b/application/src/main/data/json/system/widget_types/flow_rate_progress_bar.json new file mode 100644 index 0000000000..56bccf70e6 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/flow_rate_progress_bar.json @@ -0,0 +1,32 @@ +{ + "fqn": "flow_rate_progress_bar", + "name": "Flow rate progress bar", + "deprecated": false, + "image": "tb-image:Zmxvd19yYXRlX3Byb2dyZXNzX2Jhcl8oMSkuc3Zn:Zmxvd19yYXRlX3Byb2dyZXNzX2Jhci5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxnIGZpbHRlcj0idXJsKCNhKSI+PHJlY3Qgd2lkdGg9IjE4OCIgaGVpZ2h0PSIxNDgiIHg9IjYiIHk9IjYiIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iLjgiIHJ4PSI0Ii8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik0xOC43IDE1djEwSDE3VjE1aDEuN1ptNCA0LjR2MS40aC00LjR2LTEuNGg0LjVabS43LTQuNHYxLjRoLTUuMVYxNWg1Wm0zLjItLjVWMjVoLTEuN1YxNC41aDEuN1ptMS43IDYuOXYtLjJjMC0uNSAwLTEgLjItMS41bC43LTEuMmEzIDMgMCAwIDEgMS0uN2wxLjUtLjNjLjYgMCAxIDAgMS41LjMuNC4xLjguNCAxIC43LjQuNC42LjguOCAxLjJsLjIgMS41di4yYzAgLjUgMCAxLS4yIDEuNWwtLjcgMS4yYTMuMSAzLjEgMCAwIDEtMi41IDFjLS42IDAtMSAwLTEuNS0uMmEzLjYgMy42IDAgMCAxLTEuOC0ybC0uMi0xLjVabTEuNy0uMnYxLjJsLjQuNy41LjVjLjMuMi41LjIuOS4yYTEuNiAxLjYgMCAwIDAgMS4zLS43Yy4yLS4yLjMtLjUuMy0uOGwuMS0xdi0xbC0uNC0uOGExLjYgMS42IDAgMCAwLTEuNC0uN2MtLjMgMC0uNSAwLS44LjJsLS41LjVjLS4yLjItLjMuNS0uMy44bC0uMSAxWm04LjggMi4yIDEuNy01LjhoMWwtLjIgMS43LTEuNyA1LjdoLTFsLjItMS42Wm0tMS01LjggMS40IDUuOFYyNWgtMWwtMi03LjRoMS42Wm01LjQgNS43IDEuMy01LjdINDZMNDQgMjVoLTF2LTEuN1ptLTEuNC01LjcgMS43IDUuNy4yIDEuN2gtMUw0MSAxOS4zbC0uMy0xLjdoMVptMTEgMS40djZoLTEuNnYtNy40aDEuNlYxOVptMi4zLTEuNFYxOWEzLjIgMy4yIDAgMCAwLTEuNCAwIDEuNCAxLjQgMCAwIDAtLjkgMWwtLjEuNmgtLjRsLjEtMS4yLjUtMWMuMS0uNC40LS42LjYtLjhhMS45IDEuOSAwIDAgMSAxLjMtLjJoLjNabTUuMiA2VjIwbC0uMS0uN2ExIDEgMCAwIDAtLjUtLjRsLS43LS4yLS43LjEtLjQuNC0uMi41aC0xLjZjMC0uMyAwLS41LjItLjguMS0uMy4zLS41LjYtLjdhMyAzIDAgMCAxIDEtLjUgNCA0IDAgMCAxIDEuMi0uMmMuNiAwIDEgMCAxLjUuMi40LjIuOC41IDEgLjkuMi40LjQuOC40IDEuNHY0LjJjMCAuMy4yLjUuMy43di4xaC0xLjdhMyAzIDAgMCAxLS4yLS43di0uOFptLjMtMy4xdjFoLTEuMmwtLjguMS0uNi4zYTEgMSAwIDAgMC0uNS45YzAgLjIgMCAuNC4yLjUgMCAuMi4yLjMuNC40bC42LjFhMS44IDEuOCAwIDAgMCAxLjUtLjdsLjItLjYuNi44LS4zLjZhMyAzIDAgMCAxLS41LjYgMi42IDIuNiAwIDAgMS0xLjguNmMtLjUgMC0xIDAtMS4zLS4zLS40LS4xLS43LS40LS45LS43YTIuNCAyLjQgMCAwIDEtLjEtMi4ybC43LS44IDEtLjQgMS41LS4yaDEuM1ptNi43LTIuOXYxLjJoLTQuMXYtMS4yaDQuMVptLTMtMS44SDY2VjIzbC4xLjUuMy4yaC40YTIuNSAyLjUgMCAwIDAgLjYgMFYyNWE0LjIgNC4yIDAgMCAxLTEgLjFjLS41IDAtLjggMC0xLjEtLjItLjMtLjEtLjUtLjMtLjctLjZsLS4yLTEuMnYtNy4zWm03LjggOS4zYy0uNSAwLTEgMC0xLjUtLjJhMy4zIDMuMyAwIDAgMS0xLjgtMiA0IDQgMCAwIDEtLjItMS4zdi0uM2MwLS42IDAtMS4xLjItMS42cy40LS45LjctMS4yYTMgMyAwIDAgMSAxLS44bDEuNC0uMmMuNiAwIDEgMCAxLjQuMmwxIC44Yy4zLjMuNS43LjYgMS4xbC4yIDEuNXYuN2gtNS43di0xLjJoNHYtLjFsLS4xLS45LS41LS42LS45LS4yYy0uMyAwLS41IDAtLjcuMi0uMiAwLS40LjItLjUuNWwtLjQuN3YyLjNsLjQuNy43LjUuOC4xYTIuMyAyLjMgMCAwIDAgMS45LS45bC45LjktLjcuNmMtLjIuMi0uNS40LTEgLjUtLjMuMi0uNy4yLTEuMi4yWiIvPjxwYXRoIGZpbGw9IiMzQjkxMUMiIGQ9Ik0zNSAxMDIuNnYzLjRIMTh2LTIuOWw4LjMtOSAyLjEtMi44Yy42LS43IDEtMS40IDEuMS0yYTUuMiA1LjIgMCAwIDAgMC0zLjljLS40LS42LS44LTEtMS40LTEuNC0uNi0uNC0xLjMtLjYtMi0uNi0xIDAtMS44LjItMi41LjYtLjYuNC0xLjEgMS0xLjQgMS43LS40LjctLjUgMS42LS41IDIuNWgtNC4zYzAtMS41LjQtMi45IDEtNGE3LjUgNy41IDAgMCAxIDMtMyA5LjIgOS4yIDAgMCAxIDQuNy0xLjFjMS43IDAgMy4xLjIgNC4zLjggMS4yLjYgMi4xIDEuNCAyLjggMi41YTcuNiA3LjYgMCAwIDEgLjYgNi4xIDIwLjIgMjAuMiAwIDAgMS0yLjggNC43IDQyIDQyIDAgMCAxLTIuMiAyLjNsLTUuNSA2LjFIMzVabTIwLjItMTEuNXY0LjFjMCAyLS4yIDMuOC0uNiA1LjJhOSA5IDAgMCAxLTEuNyAzLjRjLS44LjktMS42IDEuNS0yLjcgMmE5LjggOS44IDAgMCAxLTYuMi4yIDYuOSA2LjkgMCAwIDEtNC4xLTMuNGMtLjUtLjktLjktMS45LTEuMS0zLjEtLjMtMS4yLS40LTIuNy0uNC00LjN2LTQuMWMwLTIgLjItMy44LjYtNS4xYTkgOSAwIDAgMSAxLjctMy40Yy43LTEgMS42LTEuNSAyLjYtMiAxLS4zIDIuMi0uNSAzLjUtLjUgMSAwIDIgMCAyLjguM2E2LjYgNi42IDAgMCAxIDQgMy4zYy41LjkgMSAyIDEuMiAzLjEuMiAxLjIuNCAyLjcuNCA0LjNabS00LjMgNC43di01LjRsLS4xLTIuN2E4IDggMCAwIDAtLjYtMiA0IDQgMCAwIDAtLjgtMS4zIDMgMyAwIDAgMC0xLjItLjcgMy45IDMuOSAwIDAgMC0zLjIuMWMtLjUuMi0xIC42LTEuMyAxLjItLjMuNS0uNiAxLjMtLjggMi4yLS4yLjktLjMgMi0uMyAzLjJ2NS40YzAgMSAwIDIgLjIgMi44bC41IDIgLjkgMS40Yy4zLjMuNy42IDEuMS43bDEuNS4zYTQgNCAwIDAgMCAxLjgtLjRjLjUtLjMgMS0uNyAxLjMtMS4yLjMtLjYuNi0xLjQuOC0yLjNsLjItMy4zWk02NS4zIDk1LjlWMTA2aC0yLjhWOTMuM0g2NWwuMiAyLjZabS0uNSAzLjNoLTFjMC0uOS4yLTEuNy40LTIuNC4yLS44LjUtMS40IDEtMiAuNC0uNS45LTEgMS41LTEuM2E1IDUgMCAwIDEgMi4yLS40Yy42IDAgMS4xIDAgMS42LjJzLjkuNSAxLjIuOGMuNC40LjcuOC45IDEuNC4yLjYuMyAxLjMuMyAydjguNUg3MHYtOC4yYzAtLjYgMC0xLjEtLjItMS41LS4yLS4zLS41LS42LS44LS43LS40LS4yLS44LS4yLTEuMi0uMi0uNiAwLTEgMC0xLjQuMy0uNC4yLS43LjQtLjkuOGwtLjUgMS4yLS4yIDEuNVptNy45LS44LTEuMy4zYzAtLjcgMC0xLjUuMy0yLjEuMi0uNy41LTEuMyAxLTEuOGE0LjUgNC41IDAgMCAxIDMuNi0xLjdjLjcgMCAxLjIgMCAxLjcuMy42LjEgMSAuNCAxLjQuOC4zLjQuNi44LjggMS40LjIuNi4zIDEuNC4zIDIuMnY4LjJoLTIuOXYtOC4yYzAtLjcgMC0xLjItLjMtMS41LS4xLS40LS40LS42LS43LS43LS40LS4yLS44LS4yLTEuMi0uMi0uNSAwLS45IDAtMS4yLjJsLS44LjdhMyAzIDAgMCAwLS42IDFsLS4xIDEuMVpNODUgOTIuN2gxbC44LS4xLjUtLjRhMSAxIDAgMCAwLS4xLTEuNGMtLjItLjItLjYtLjMtMS4xLS4zLS40IDAtLjcgMC0xIC4zLS4yLjEtLjQuMy0uNC42aC0yLjFjMC0uNS4yLTEgLjUtMS40YTMgMyAwIDAgMSAxLjItLjkgNS4zIDUuMyAwIDAgMSAzLjYgMGMuNi4yIDEgLjUgMS4zLjkuMy40LjQuOS40IDEuNCAwIC41LS4xLjktLjQgMS4yLS4zLjQtLjcuNi0xLjEuOC0uNS4yLTEuMS4zLTEuOC4zaC0xLjN2LTFabTAgMS41di0xaDEuM2E2IDYgMCAwIDEgMS45LjNjLjUuMiAxIC40IDEuMi44LjMuMy40LjcuNCAxLjMgMCAuNi0uMiAxLS41IDEuNWEzIDMgMCAwIDEtMS40LjkgNS40IDUuNCAwIDAgMS0zLjYgMGMtLjUtLjItMS0uNS0xLjMtLjktLjQtLjQtLjUtMS0uNS0xLjZoMi4xYzAgLjMuMS42LjQuOC4zLjIuNy4zIDEuMi4zcy44LS4xIDEtLjNjLjMtLjMuNS0uNS41LS45IDAtLjMtLjEtLjUtLjMtLjdhMSAxIDAgMCAwLS41LS40bC0xLS4xaC0xWm0xNC42LTUuM0w5MyAxMDcuNWgtMi4ybDYuNy0xOC42aDIuMlptNC43LS45djE4aC0yLjhWODhoMi44Wm0tLjUgMTEuMmgtLjljMC0uOS4xLTEuNy40LTIuNC4yLS44LjUtMS40IDEtMmE0LjUgNC41IDAgMCAxIDMuNi0xLjdjLjYgMCAxLjIgMCAxLjcuMnMuOS41IDEuMy45Yy4zLjQuNi44LjggMS41LjIuNi4zIDEuMy4zIDIuMnY4LjFoLTIuOHYtOC4yYzAtLjYtLjEtMS0uMy0xLjQtLjItLjQtLjQtLjYtLjgtLjgtLjMtLjItLjctLjItMS4yLS4ycy0xIDAtMS40LjNjLS4zLjItLjcuNC0xIC44bC0uNSAxLjItLjIgMS41Wm0xMy45LTMuNVYxMDZIMTE1VjkzLjNoMi43djIuNFptMy45LTIuNVY5NmE1LjUgNS41IDAgMCAwLTIuNSAwIDIuNCAyLjQgMCAwIDAtMS41IDEuNmwtLjIgMS4yaC0uNmMwLS44IDAtMS41LjItMi4yYTYgNiAwIDAgMSAuNy0xLjggMy4yIDMuMiAwIDAgMSAzLjQtMS42bC41LjFaIi8+PHJlY3Qgd2lkdGg9IjE2OCIgaGVpZ2h0PSI2IiB4PSIxNiIgeT0iMTIyIiBmaWxsPSJub25lIiByeD0iMyIvPjxyZWN0IHdpZHRoPSIzNiIgaGVpZ2h0PSI2IiBmaWxsPSIjM0ZBNzFBIiByeD0iMyIgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgNTIgMTIyKSIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Ik0yMS42IDEzOS41djEuMmwtLjIgMS42LS41IDFjLS4yLjMtLjUuNS0uOC42bC0xIC4yYTMgMyAwIDAgMS0uOC0uMSAyIDIgMCAwIDEtLjctLjRsLS41LS42LS40LTFhNyA3IDAgMCAxIDAtMS4zdi0yLjhsLjYtMWMuMi0uNC41LS41LjgtLjdsMS0uMWguOGExLjkgMS45IDAgMCAxIDEuMiAxYy4yLjMuMy43LjMgMWwuMiAxLjRabS0xIDEuM3YtMS41bC0uMS0xYTMgMyAwIDAgMC0uMi0uNmMwLS4yLS4yLS40LS4zLS41YTEgMSAwIDAgMC0uNC0uMmwtLjUtLjEtLjYuMS0uNS40LS4zLjh2My42YzAgLjIgMCAuNS4yLjYgMCAuMi4xLjQuMy41IDAgLjIuMi4yLjQuM2gxLjFsLjUtLjUuMy0uOHYtMVpNMTY4IDEzNi4xdjcuOWgtMXYtNi42bC0yIC43di0uOWwyLjgtMWguMVptOCAzLjR2MS4yYzAgLjYgMCAxLjEtLjIgMS42IDAgLjQtLjMuOC0uNSAxLS4yLjMtLjQuNS0uNy42bC0xIC4yYTMgMyAwIDAgMS0uOS0uMSAyIDIgMCAwIDEtLjctLjRsLS41LS42Yy0uMS0uMy0uMy0uNi0uMy0xYTcgNyAwIDAgMS0uMS0xLjN2LTEuMmwuMS0xLjZjLjEtLjUuMy0uOC41LTEgLjItLjQuNS0uNS44LS43bDEtLjFoLjlhMiAyIDAgMCAxIDEuMiAxbC4zIDEgLjEgMS40Wm0tMSAxLjN2LTIuNWwtLjItLjYtLjMtLjVhMSAxIDAgMCAwLS41LS4ybC0uNS0uMS0uNi4xLS41LjQtLjIuOC0uMSAxLjF2Mi41bC4yLjZjMCAuMi4yLjQuMy41LjEuMi4zLjIuNC4zaDEuMmwuNC0uNWMuMi0uMi4yLS40LjMtLjhsLjEtMVptNy40LTEuM3YxLjJsLS4xIDEuNi0uNSAxYy0uMi4zLS41LjUtLjguNmwtMSAuMmEzIDMgMCAwIDEtLjktLjEgMiAyIDAgMCAxLS43LS40bC0uNS0uNi0uMy0xYTcgNyAwIDAgMS0uMS0xLjN2LTEuMmMwLS43IDAtMS4yLjItMS42IDAtLjUuMi0uOC41LTEgLjItLjQuNC0uNS43LS43bDEtLjFoLjlhMS45IDEuOSAwIDAgMSAxLjIgMWwuMyAxIC4xIDEuNFptLTEgMS4zdi0yLjVhMyAzIDAgMCAwLS4yLS42YzAtLjItLjItLjQtLjMtLjVhMSAxIDAgMCAwLS40LS4ybC0uNS0uMS0uNy4xLS40LjRjLS4yLjItLjIuNS0uMy44bC0uMSAxLjF2Mi41bC4yLjYuMy41LjQuM2gxLjJsLjUtLjUuMi0uOC4xLTFaIi8+PGRlZnM+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIHg9IjAiIHk9IjAiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjMiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTUwN18yOTY3OCIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTUwN18yOTY3OCIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjwvZGVmcz48L3N2Zz4=", + "description": "Displays flow rate reading as a horizontal progress bar. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'flowRate', label: 'Flow Rate', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"flowRate\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#305AD7\"},{\"from\":10,\"to\":30,\"color\":\"#3FA71A\"},{\"from\":30,\"to\":50,\"color\":\"#F36900\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":100,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#305AD7\"},{\"from\":10,\"to\":30,\"color\":\"#3FA71A\"},{\"from\":30,\"to\":50,\"color\":\"#F36900\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Flow rate\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"m³/hr\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "liquid", + "fluid", + "flow rate", + "fluid dynamics", + "velocity", + "mass flow", + "volume flow" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/flow_rate_progress_bar_with_background.json b/application/src/main/data/json/system/widget_types/flow_rate_progress_bar_with_background.json new file mode 100644 index 0000000000..8a1a31fa73 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/flow_rate_progress_bar_with_background.json @@ -0,0 +1,32 @@ +{ + "fqn": "flow_rate_progress_bar_with_background", + "name": "Flow rate progress bar with background", + "deprecated": false, + "image": "tb-image:Zmxvd19yYXRlX3Byb2dyZXNzX2Jhcl93aXRoX2JhY2tncm91bmQuc3Zn:Zmxvd19yYXRlX3Byb2dyZXNzX2Jhcl93aXRoX2JhY2tncm91bmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48L2c+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZD0iTTE4LjcgMTV2MTBIMTdWMTVoMS43Wm00IDQuNHYxLjRoLTQuNHYtMS40aDQuNVptLjctNC40djEuNGgtNS4xVjE1aDVabTMuMi0uNVYyNWgtMS43VjE0LjVoMS43Wm0xLjcgNi45di0uMmMwLS41IDAtMSAuMi0xLjVsLjctMS4yYTMgMyAwIDAgMSAxLS43bDEuNS0uM2MuNiAwIDEgMCAxLjUuMy40LjEuOC40IDEgLjcuNC40LjYuOC44IDEuMmwuMiAxLjV2LjJjMCAuNSAwIDEtLjIgMS41bC0uNyAxLjJhMy4xIDMuMSAwIDAgMS0yLjUgMWMtLjYgMC0xIDAtMS41LS4yYTMuNiAzLjYgMCAwIDEtMS44LTJsLS4yLTEuNVptMS43LS4ydjEuMmwuNC43LjUuNWMuMy4yLjUuMi45LjJhMS42IDEuNiAwIDAgMCAxLjMtLjdjLjItLjIuMy0uNS4zLS44bC4xLTF2LTFsLS40LS44YTEuNiAxLjYgMCAwIDAtMS40LS43Yy0uMyAwLS41IDAtLjguMmwtLjUuNWMtLjIuMi0uMy41LS4zLjhsLS4xIDFabTguOCAyLjIgMS43LTUuOGgxbC0uMiAxLjctMS43IDUuN2gtMWwuMi0xLjZabS0xLTUuOCAxLjQgNS44VjI1aC0xbC0yLTcuNGgxLjZabTUuNCA1LjcgMS4zLTUuN0g0Nkw0NCAyNWgtMXYtMS43Wm0tMS40LTUuNyAxLjcgNS43LjIgMS43aC0xTDQxIDE5LjNsLS4zLTEuN2gxWm0xMSAxLjR2NmgtMS42di03LjRoMS42VjE5Wm0yLjMtMS40VjE5YTMuMiAzLjIgMCAwIDAtMS40IDAgMS40IDEuNCAwIDAgMC0uOSAxbC0uMS42aC0uNGwuMS0xLjIuNS0xYy4xLS40LjQtLjYuNi0uOGExLjkgMS45IDAgMCAxIDEuMy0uMmguM1ptNS4yIDZWMjBsLS4xLS43YTEgMSAwIDAgMC0uNS0uNGwtLjctLjItLjcuMS0uNC40LS4yLjVoLTEuNmMwLS4zIDAtLjUuMi0uOC4xLS4zLjMtLjUuNi0uN2EzIDMgMCAwIDEgMS0uNSA0IDQgMCAwIDEgMS4yLS4yYy42IDAgMSAwIDEuNS4yLjQuMi44LjUgMSAuOS4yLjQuNC44LjQgMS40djQuMmMwIC4zLjIuNS4zLjd2LjFoLTEuN2EzIDMgMCAwIDEtLjItLjd2LS44Wm0uMy0zLjF2MWgtMS4ybC0uOC4xLS42LjNhMSAxIDAgMCAwLS41LjljMCAuMiAwIC40LjIuNSAwIC4yLjIuMy40LjRsLjYuMWExLjggMS44IDAgMCAwIDEuNS0uN2wuMi0uNi42LjgtLjMuNmEzIDMgMCAwIDEtLjUuNiAyLjYgMi42IDAgMCAxLTEuOC42Yy0uNSAwLTEgMC0xLjMtLjMtLjQtLjEtLjctLjQtLjktLjdhMi40IDIuNCAwIDAgMS0uMS0yLjJsLjctLjggMS0uNCAxLjUtLjJoMS4zWm02LjctMi45djEuMmgtNC4xdi0xLjJoNC4xWm0tMy0xLjhINjZWMjNsLjEuNS4zLjJoLjRhMi41IDIuNSAwIDAgMCAuNiAwVjI1YTQuMiA0LjIgMCAwIDEtMSAuMWMtLjUgMC0uOCAwLTEuMS0uMi0uMy0uMS0uNS0uMy0uNy0uNmwtLjItMS4ydi03LjNabTcuOCA5LjNjLS41IDAtMSAwLTEuNS0uMmEzLjMgMy4zIDAgMCAxLTEuOC0yIDQgNCAwIDAgMS0uMi0xLjN2LS4zYzAtLjYgMC0xLjEuMi0xLjZzLjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEtLjhsMS40LS4yYy42IDAgMSAwIDEuNC4ybDEgLjhjLjMuMy41LjcuNiAxLjFsLjIgMS41di43aC01Ljd2LTEuMmg0di0uMWwtLjEtLjktLjUtLjYtLjktLjJjLS4zIDAtLjUgMC0uNy4yLS4yIDAtLjQuMi0uNS41bC0uNC43djIuM2wuNC43LjcuNS44LjFhMi4zIDIuMyAwIDAgMCAxLjktLjlsLjkuOS0uNy42Yy0uMi4yLS41LjQtMSAuNS0uMy4yLS43LjItMS4yLjJaIi8+PHBhdGggZmlsbD0iIzNCOTExQyIgZD0iTTM1IDEwMi42djMuNEgxOHYtMi45bDguMy05IDIuMS0yLjhjLjYtLjcgMS0xLjQgMS4xLTJhNS4yIDUuMiAwIDAgMCAwLTMuOWMtLjQtLjYtLjgtMS0xLjQtMS40LS42LS40LTEuMy0uNi0yLS42LTEgMC0xLjguMi0yLjUuNi0uNi40LTEuMSAxLTEuNCAxLjctLjQuNy0uNSAxLjYtLjUgMi41aC00LjNjMC0xLjUuNC0yLjkgMS00YTcuNSA3LjUgMCAwIDEgMy0zIDkuMiA5LjIgMCAwIDEgNC43LTEuMWMxLjcgMCAzLjEuMiA0LjMuOCAxLjIuNiAyLjEgMS40IDIuOCAyLjVhNy42IDcuNiAwIDAgMSAuNiA2LjEgMjAuMiAyMC4yIDAgMCAxLTIuOCA0LjcgNDIgNDIgMCAwIDEtMi4yIDIuM2wtNS41IDYuMUgzNVptMjAuMi0xMS41djQuMWMwIDItLjIgMy44LS42IDUuMmE5IDkgMCAwIDEtMS43IDMuNGMtLjguOS0xLjYgMS41LTIuNyAyYTkuOCA5LjggMCAwIDEtNi4yLjIgNi45IDYuOSAwIDAgMS00LjEtMy40Yy0uNS0uOS0uOS0xLjktMS4xLTMuMS0uMy0xLjItLjQtMi43LS40LTQuM3YtNC4xYzAtMiAuMi0zLjguNi01LjFhOSA5IDAgMCAxIDEuNy0zLjRjLjctMSAxLjYtMS41IDIuNi0yIDEtLjMgMi4yLS41IDMuNS0uNSAxIDAgMiAwIDIuOC4zYTYuNiA2LjYgMCAwIDEgNCAzLjNjLjUuOSAxIDIgMS4yIDMuMS4yIDEuMi40IDIuNy40IDQuM1ptLTQuMyA0Ljd2LTUuNGwtLjEtMi43YTggOCAwIDAgMC0uNi0yIDQgNCAwIDAgMC0uOC0xLjMgMyAzIDAgMCAwLTEuMi0uNyAzLjkgMy45IDAgMCAwLTMuMi4xYy0uNS4yLTEgLjYtMS4zIDEuMi0uMy41LS42IDEuMy0uOCAyLjItLjIuOS0uMyAyLS4zIDMuMnY1LjRjMCAxIDAgMiAuMiAyLjhsLjUgMiAuOSAxLjRjLjMuMy43LjYgMS4xLjdsMS41LjNhNCA0IDAgMCAwIDEuOC0uNGMuNS0uMyAxLS43IDEuMy0xLjIuMy0uNi42LTEuNC44LTIuM2wuMi0zLjNaTTY1LjMgOTUuOVYxMDZoLTIuOFY5My4zSDY1bC4yIDIuNlptLS41IDMuM2gtMWMwLS45LjItMS43LjQtMi40LjItLjguNS0xLjQgMS0yIC40LS41LjktMSAxLjUtMS4zYTUgNSAwIDAgMSAyLjItLjRjLjYgMCAxLjEgMCAxLjYuMnMuOS41IDEuMi44Yy40LjQuNy44LjkgMS40LjIuNi4zIDEuMy4zIDJ2OC41SDcwdi04LjJjMC0uNiAwLTEuMS0uMi0xLjUtLjItLjMtLjUtLjYtLjgtLjctLjQtLjItLjgtLjItMS4yLS4yLS42IDAtMSAwLTEuNC4zLS40LjItLjcuNC0uOS44bC0uNSAxLjItLjIgMS41Wm03LjktLjgtMS4zLjNjMC0uNyAwLTEuNS4zLTIuMS4yLS43LjUtMS4zIDEtMS44YTQuNSA0LjUgMCAwIDEgMy42LTEuN2MuNyAwIDEuMiAwIDEuNy4zLjYuMSAxIC40IDEuNC44LjMuNC42LjguOCAxLjQuMi42LjMgMS40LjMgMi4ydjguMmgtMi45di04LjJjMC0uNyAwLTEuMi0uMy0xLjUtLjEtLjQtLjQtLjYtLjctLjctLjQtLjItLjgtLjItMS4yLS4yLS41IDAtLjkgMC0xLjIuMmwtLjguN2EzIDMgMCAwIDAtLjYgMWwtLjEgMS4xWk04NSA5Mi43aDFsLjgtLjEuNS0uNGExIDEgMCAwIDAtLjEtMS40Yy0uMi0uMi0uNi0uMy0xLjEtLjMtLjQgMC0uNyAwLTEgLjMtLjIuMS0uNC4zLS40LjZoLTIuMWMwLS41LjItMSAuNS0xLjRhMyAzIDAgMCAxIDEuMi0uOSA1LjMgNS4zIDAgMCAxIDMuNiAwYy42LjIgMSAuNSAxLjMuOS4zLjQuNC45LjQgMS40IDAgLjUtLjEuOS0uNCAxLjItLjMuNC0uNy42LTEuMS44LS41LjItMS4xLjMtMS44LjNoLTEuM3YtMVptMCAxLjV2LTFoMS4zYTYgNiAwIDAgMSAxLjkuM2MuNS4yIDEgLjQgMS4yLjguMy4zLjQuNy40IDEuMyAwIC42LS4yIDEtLjUgMS41YTMgMyAwIDAgMS0xLjQuOSA1LjQgNS40IDAgMCAxLTMuNiAwYy0uNS0uMi0xLS41LTEuMy0uOS0uNC0uNC0uNS0xLS41LTEuNmgyLjFjMCAuMy4xLjYuNC44LjMuMi43LjMgMS4yLjNzLjgtLjEgMS0uM2MuMy0uMy41LS41LjUtLjkgMC0uMy0uMS0uNS0uMy0uN2ExIDEgMCAwIDAtLjUtLjRsLTEtLjFoLTFabTE0LjYtNS4zTDkzIDEwNy41aC0yLjJsNi43LTE4LjZoMi4yWm00LjctLjl2MThoLTIuOFY4OGgyLjhabS0uNSAxMS4yaC0uOWMwLS45LjEtMS43LjQtMi40LjItLjguNS0xLjQgMS0yYTQuNSA0LjUgMCAwIDEgMy42LTEuN2MuNiAwIDEuMiAwIDEuNy4ycy45LjUgMS4zLjljLjMuNC42LjguOCAxLjUuMi42LjMgMS4zLjMgMi4ydjguMWgtMi44di04LjJjMC0uNi0uMS0xLS4zLTEuNC0uMi0uNC0uNC0uNi0uOC0uOC0uMy0uMi0uNy0uMi0xLjItLjJzLTEgMC0xLjQuM2MtLjMuMi0uNy40LTEgLjhsLS41IDEuMi0uMiAxLjVabTEzLjktMy41VjEwNkgxMTVWOTMuM2gyLjd2Mi40Wm0zLjktMi41Vjk2YTUuNSA1LjUgMCAwIDAtMi41IDAgMi40IDIuNCAwIDAgMC0xLjUgMS42bC0uMiAxLjJoLS42YzAtLjggMC0xLjUuMi0yLjJhNiA2IDAgMCAxIC43LTEuOCAzLjIgMy4yIDAgMCAxIDMuNC0xLjZsLjUuMVoiLz48cmVjdCB3aWR0aD0iMTY4IiBoZWlnaHQ9IjYiIHg9IjE2IiB5PSIxMjIiIGZpbGw9Im5vbmUiIHJ4PSIzIi8+PHJlY3Qgd2lkdGg9IjM2IiBoZWlnaHQ9IjYiIGZpbGw9IiMzRkE3MUEiIHJ4PSIzIiB0cmFuc2Zvcm09Im1hdHJpeCgtMSAwIDAgMSA1MiAxMjIpIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTIxLjYgMTM5LjV2MS4ybC0uMiAxLjYtLjUgMWMtLjIuMy0uNS41LS44LjZsLTEgLjJhMyAzIDAgMCAxLS44LS4xIDIgMiAwIDAgMS0uNy0uNGwtLjUtLjYtLjQtMWE3IDcgMCAwIDEgMC0xLjN2LTIuOGwuNi0xYy4yLS40LjUtLjUuOC0uN2wxLS4xaC44YTEuOSAxLjkgMCAwIDEgMS4yIDFjLjIuMy4zLjcuMyAxbC4yIDEuNFptLTEgMS4zdi0xLjVsLS4xLTFhMyAzIDAgMCAwLS4yLS42YzAtLjItLjItLjQtLjMtLjVhMSAxIDAgMCAwLS40LS4ybC0uNS0uMS0uNi4xLS41LjQtLjMuOHYzLjZjMCAuMiAwIC41LjIuNiAwIC4yLjEuNC4zLjUgMCAuMi4yLjIuNC4zaDEuMWwuNS0uNS4zLS44di0xWk0xNjggMTM2LjF2Ny45aC0xdi02LjZsLTIgLjd2LS45bDIuOC0xaC4xWm04IDMuNHYxLjJjMCAuNiAwIDEuMS0uMiAxLjYgMCAuNC0uMy44LS41IDEtLjIuMy0uNC41LS43LjZsLTEgLjJhMyAzIDAgMCAxLS45LS4xIDIgMiAwIDAgMS0uNy0uNGwtLjUtLjZjLS4xLS4zLS4zLS42LS4zLTFhNyA3IDAgMCAxLS4xLTEuM3YtMS4ybC4xLTEuNmMuMS0uNS4zLS44LjUtMSAuMi0uNC41LS41LjgtLjdsMS0uMWguOWEyIDIgMCAwIDEgMS4yIDFsLjMgMSAuMSAxLjRabS0xIDEuM3YtMi41bC0uMi0uNi0uMy0uNWExIDEgMCAwIDAtLjUtLjJsLS41LS4xLS42LjEtLjUuNC0uMi44LS4xIDEuMXYyLjVsLjIuNmMwIC4yLjIuNC4zLjUuMS4yLjMuMi40LjNoMS4ybC40LS41Yy4yLS4yLjItLjQuMy0uOGwuMS0xWm03LjQtMS4zdjEuMmwtLjEgMS42LS41IDFjLS4yLjMtLjUuNS0uOC42bC0xIC4yYTMgMyAwIDAgMS0uOS0uMSAyIDIgMCAwIDEtLjctLjRsLS41LS42LS4zLTFhNyA3IDAgMCAxLS4xLTEuM3YtMS4yYzAtLjcgMC0xLjIuMi0xLjYgMC0uNS4yLS44LjUtMSAuMi0uNC40LS41LjctLjdsMS0uMWguOWExLjkgMS45IDAgMCAxIDEuMiAxbC4zIDEgLjEgMS40Wm0tMSAxLjN2LTIuNWEzIDMgMCAwIDAtLjItLjZjMC0uMi0uMi0uNC0uMy0uNWExIDEgMCAwIDAtLjQtLjJsLS41LS4xLS43LjEtLjQuNGMtLjIuMi0uMi41LS4zLjhsLS4xIDEuMXYyLjVsLjIuNi4zLjUuNC4zaDEuMmwuNS0uNS4yLS44LjEtMVoiLz48ZGVmcz48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2MiIHRyYW5zZm9ybT0ibWF0cml4KC4wMDUwMzE0NSAwIDAgLjAwNjI4OTMxIDAgMCkiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIHg9IjAiIHk9IjAiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjMiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTUwN18yOTM5NSIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTUwN18yOTM5NSIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFJQ0FnSUNBUUlDQWdJREFnSURBd1lFQXdNREF3Y0ZCUVFHQ0FjSkNBZ0hDQWdKQ2cwTENRb01DZ2dJQ3c4TERBME9EZzhPQ1FzUUVSQU9FUTBPRGc3LzJ3QkRBUUlEQXdNREF3Y0VCQWNPQ1FnSkRnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzcvd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhRQUFBZ01BQXdFQkFBQUFBQUFBQUFBQUJnY0VCUWdDQXdrQkFQL0VBQnNCQUFJREFRRUJBQUFBQUFBQUFBQUFBQUVDQUFNRUJRWUgvOW9BREFNQkFBSVFBeEFBQUFCT29TVXdwSlpSaHV5azhFOWgxeWRiRDVKbXl0c1gxaHhxWFBVeEFZSFFMQUxzaTFIbmJUbCtNNXpXUExvYVlzd2RYWUJLcGRhYkVpRVpad2M1TDR3K0lPbWw2WjJRQktybWJQY1ZDVkN3Z1pkUE5XbjRkS1dVVHpFdkdyNnlQaTdSVHQ1OG9UdGhXT2JXQ25qWDhXU1J4TXU0RGlTVXBvQUFKV0I2MmJMamFGMU9RWHIwTlV5RXoyNkIwMEt1cDg4QnRDVzBDd3YwSXR2bkpFdGc3ZGV5Y1lCSTB5SXg3YW9jRmdKZkNLMUhBa2EwUTBldlBwTllac2N1UmQycUVyVFlkMzByNVlvNCtKRmJWa2ZjaU5qeXRvRmJGT3NSNERZejZDZDIxSkh0Y1laMjVNNFpiOXZjSHVPdG9wS0hVMTFMdVJxd2hUc21oVFVPNTcyRFpYRlJrOVRaRWh0R2ZSeGJDdExVTGpVTnpYNzExU0xFMTBZSjl2NDdPR3ZNb3VaME5NT3JiNjNKOUhmQ2V6QitOMTg4YWNzTmJEaXUyVXNKbEJOV2JwZ01LYjJRSEVybzJyYkc4OXFVakE3cDNhTkhhTXFKMTh6elY5bDVURW1EcEhIVDVnRmswTk5ESDduR3VLTC9BRmIrVi9TTDFqek1qMlRQTkMxVlVlU1dLZ0lsYVhJYkFkTkRCanFSbXpDc0ZRekI5eDVPVTEzWHB5K1UvVzRvVnoramJzdXBQVitQSUxNK2QrYjExL1JxMmVxYVY4ZDZzNTg3NkdwelhXTEdlOUUvUGQxM1oxMHRsODdpQ29kcll6S255QjErWjM1TlBiOU04S3hPUjNtRnU1M2puME9WYzNWV3RsTG02M0M1QWk5R2hGYzdzdFBUaVA4QWtiMlQ4MCtqeTg3QUt4bjZhM1d6S0syb2ZyYk9QdnZEOE4rRzM4ZDdKeitHOW5oTDZqOHZPdXJ6dHZkTE8yZkZldkpPL3dBcnlCMmMwS3k2cmZSbTFSNkR6ZGhGVi9ONmlYNWZZY0hVNVY5NWowR2ovQSsyQzBBbXhlVmlGTlp2eUY3YktucmN1bE1pNWRURzVXOU9VcE03bUprZlZ2bTZ6NVBTYUV2eWRxNStaK2YwSXFNMCtoejQySFh3MFYxbWZScy94M3FBRGg5alRNQzNaR0N3cXF6MFUyOTdUNnJnTnRQRjY2NHRmcGFVSXlBNkdUUi91L0hnV21rZHdiWnRpNU8yOG12SkRPVDFMSFBvTWV2eTN6eHVpYytKOVhkczdpdXIvWjVjM05uYWc5Q1N6c2w2R0UyVTVaR2ZaTkJPNGxXM25ybmJlWFRwVnphYzFaYUtWSXM5V1B0OWY1ZXdTeGlWWGE4NS9Rb3ZNZGNLVmh0cGFQWEJVakplc3JabFBTUU5HQktGdzFpUGg3U1NTaDdjemdDeXVreGlsNlF6MEcxWlJsVmhQWWhSWWxpQnZpOXNhWkZVWXVVUXJ6akJwd0EzdGJwckxBYXEzZUxwUldNZURxTXVsOFkwS1dPRmJIRmwvY1orVVB1a0s0T3BKQzBxeEdIcDFwbUxjaWxPdWVhV1UxOEdsbmxpdHMxVk1qV094bXBaVFlrcW10bGJXMW8wdHliMFJyN21VdGNrWmxGNGprU0tFdFlOSmhQbzdwbUs2VnhmSUgwc0ptTUZ5M0FiaEl3Q29lMGV0dFVhdHBLV0d6VW80TkZVcjRQcXZiYUwxVmZLbFB3c293RmhCcldqRk4xdS9udm1pVWd2SUlObGE2VzNZQnJNbzFwQWN5QjZzcElVMVhhK25vVjRZSmtIVmU3MENTclNCSHVFMjQxUzRKVmlsRkd6UWxrUktWUGxob0MyV1VVbkVYdFRwK3M1cVZrY1Nud2RsNlIyS0EyZ3JsR0NBYlFRbDJWdEtCdlJYVkZxQWMyWEIzc2p2Y3VpS1lVdlFLRS9WWWlLN00zaVo2VnZ6QlRBRkRTV1JwZTR2OGpBTkJSNnRkbzBrSFV2VHl0WFJuME1hbWVUMlYyTzNOZlNWTUdKRm1qWndxdlZVQ3RrMGNhY2JzNCtEZGtBa0JZMHRUSFM4MDVENXpWRVZranFZeG4veEFBc0VBQUNBd0FDQWdJQ0FRTURCUUFBQUFBREJBRUNCUUFHRVJNU0ZSUWhKaUlrSlFjV05pTW5OVUZDLzlvQUNBRUJBQUVGQW9yd2R5VTVEeFI4RytLZUROUy9JOGNpdkpwengrb3J6VlcxWlpLSWdUb0xHSEYzbWptak1vbkYreGlEd0tXaHNhaU9BZ255c1JITzZ6LzI3eWZNZFgxUEZqM2lQdzdLcHJhOWhkZnJhS2MrSENEL0FLZlh6NHp3WnowNE53bkJzZkxueTh4WHphMnpRa1lYNURESExKQlV0VGFiTklPcU1udXFtdWdKRDk5ZzhjcldaNXZvajArc2oxVWswRHRQTk5GanhuSlRlZDh6cS9xclhrVTRmeFhrVjh4OE9UWGc2OEg4WWliMjVabW95T3NpcFFiK2k3WFA2b3I3QXJoWFgzK3hYeCt4VzhSR1phbjVscDhjOGFyM08wSld6dW1JdFp5dUl3OEZ2UlBIOWdzdXZmV0tiS0JXb0tUejhZM3hkeFMvTlAyK3FCeE1XQmJnd1g1V2xPTjZBUVZwWFplb3ZpS2daNjc1dGdDc1duS05uaU96QkMyeXJkQWg4NHVkL3VJakN4TlFET2hkUGF5MjlIR0hUVXlUNW1ncStWbjlwL1U1NlE1ZnhLcUFIN3pCSDhTZVdZcU14ZmRlQVVWKzl6S3FmN3J6UE9TQ3QvOEFVSUdlT081ZkhoNDlkc0FNaDZySGlLeTR2WGo4dzRlcWg1NWxqRUp5OEErMEsycFJTMjFsako5cWN0djhyWmh5UDdHTWhldklGUUVDb0N5WDBCdnE2WlpseG1LWWM3WFpiblBvaHRhMFRFMnhDZXE2ZTIzbVhVM005N0kwYlZMbWZaTTFUdXl2N1FsdGU3VkhxdkdNVk9mVllpS29QUk1KNHdPVjFFdzBMMk0vbFZ3N2VubytmeG95REdKYklXamkrYVpLNWYxMHhod01JOW4yQzNuOThWSlNuSFFXV2J5U2VsWjJmQnN0NGlUc0MvTVVGbUswcVpOQVpqTDljTFpwb0taeW4xcHMrSXZ3alBOTnZ5MC9JcjJ0TWZiL0FBU3BvZllhZFptb3V2bW15K0FrYmZHUzREQTJuMytFb1gxT2s5MnRIenBTZzVrd3hEQ0tiL0VlZ3I2eXg1bWN6WWRGblgwbFNnU3ZOdVNzRWxNVnBRL2FoTmZpOWsxeDBrZXRieDEzQVBZUnp0TUZDMmY0ZjZqRmJVczFxSGl2S2RrL2E3bHZhNC9XYy9yNi93QWVzdWl0WE1Ja1d3dnhQNzFCZXRHNXJXWm1JdFZ5Sy9WK0F4S254aVBiNmpDMFl5NUMyKzR3WE8xVk96S3Z0RzdGdU1RUGpmWTg3VEJuYVdlbStVdnRlL0lqNFV1eVo3U0c1NlVEdE10WDhYNWRBbDA4TWdsK3NNYUZDck5VOWVqN0xTTUxFQWMrWHVCYjkwZk5GRlAvQUduUGhjRFlwYnBWUnhZYm5YMTJDTWRmL0p5Ryt1UzhkM0lhVEdmcWtMbHAxVmdsMCtxMmdXYjFNVm5GODhLOVhCc3lyazU2MnJiRVZrNTM0cElYWkMxNjdjM2svd0N2NWZ2L0FPcmxKbm9ScE9rcmVmWU90Zm5jU3NDVEtxVWtYdDZVV1ppNGdPSXZacmsvTHMxWEVtT2FFZGRNQ0Z1dkVHRUdMVHJvVXNJcUttWDd3VjY1clVpbUoyS0dKMHRYOHFPdlBPYXVyaDZLZHg2YjRnRmJFeUIxRzRMcENqMkZ2N1hLVitVQy9jbzUxeERBZGl1b0lzaTZXRDVXNGZQOGI1ZnBCSTB0NTE5QXRFOXJNOVRwakh6UUh0VlFpb1QxL0Z5YlpDMXhpNmcyTytkMTJONHZ3NDVvNm1aaVB1dU1kWS9jOVRFSW8weVBsb2xTaXJtVUxyTHQ1SDFMU21ROVZUUXhOQ3RTNmNvVnB5MVZxSkMxYS9ac2JKNjlqUnNLV0IyTEhhTkZLR05SWU1LZHZ6VzRxN3B3Q3B2d2xZS3V1dll0MG1BM29QWXJCR3V4Q25YZEs1RnRtU0tTdVZsZW1hdlRNTG05V0VFTmV2VXpwMzJLMHo5SFVaM1gvd0RpaGdBZDdFM21JZlFEdW5SQnhra1BLUTAxb0wwcU53ZEluUllzZjdNRkRVMkJOL2phckRnMkdKT0l0S2tzSzlEMitJTHg0ci9YRGVtWmtxekVWNld2Y2wrcG81dEtxdE1TTm1HcjhYTmNRc1d2OGtmdC9FUFBqc0JtUlpYV3lUWXZVY3FhTDltQTZTbmI2SGt1dU9QTFRpWmJCei9sWGFUckY0N09sVlJKcnJvVnVlcGtFMGNicFdqNDY4K3lUdFhKNjRtNURtT3VrVVFBQVNMTno4Rm1qcmlHeVZCWG5QajBaS3Y0dXU1KytuZWZIYWQzWC9PN0VuZndtS2Y4bW9YK1FDbXYyaS9tekpOTWtEVDkzM2dyTUNCck1UckFYY2xxc2hXOVB3RUxSSU9uQ1ZpZVkyeEtaR1kvS1loWkdSZ1NRK0Y4eGFhM3lrNGpLS280ME82Tk4xdTM4UGJrbjNkZXFYdmY2ZDJtdTZpVUlFa215NnVkMWJZdmI2bFpJUVVyZlA4QUZQSENteUV1TTlucEZVMzJMNmZ6bVFrbVpQZDB2bVdTVHlHYlZ0N3lUejVXbXVOZTBNM2lzQXVZWGxwZk1aNStDc0c5ZEs5K3VGRTlvNm1mMXpjOXYwUHE1VkxGcFB2T0twNUJlV056TlNxNTI2ZkZkTjNUTlc5dnorNlQvbFU4K24waHplR3pNLzhBUlVhK0dkVnZ6d2RZdGNTbnM0RE1yeFpHb3VhTFVqRFFoQ21XNnBzdmNGMHRVZHdwWUtkUHNyQnBPc2FKTG9LZzQxMnRVZkd1MU5FNGJUWVpPdllkU3NUNUpuVDQ0T1BsczkwSi9LRS8rRjZMWkozU3V0Zk1WdkNsYi8xQ0ROZUJ2ZnlubTc3L0FDL1V5Q0dEUDZ1Q3duRGhvWTVDOHN5TWRXTmlvcXNkbnI4aWFtbXhVL3NtaXFpTmtURC9BQk53RmZtLys2OExQa3FYNlNSLzg1MiszeTdhSDlkT2JBVDdhVWkyTGVzanRFOC8vOFFBU2hBQUFnRURBUVFGQndnSEJnUUhBQUFBQVFJREFBUVJFaE1oSWpFRkZFRlJZU015UWxKeGdhRVFKR0tSc2NIUjhCVXpZM0tDa3JJR2MzU2l3dUVnTkZQU1ExUmtnNFNUOGYvYUFBZ0JBUUFHUHdLdUJ5dEFsUS9FQjNWeEFwWEN5dDc2N3Y4QWhjMmtYa2o2YUhMZlZXaVJTcjl1cWx1NVdGcEY2THlkdnNIYlNMQnRHa0c1Slp1T1grQk9TVWJucFM2RmxuZWRUYTUzL0N1cjlBV0dIUDhBNHpEWElhYnJ0MlZiUEhxM2tVQ0l0dEo2OG55WG43eS9iWFJ1UC9LeC93QklxeE9OKzE1KzZoM2RYL0dyeWUwNlFkQmNZTWpQR0NmY0J5b21lYTVrWTgyMmVQbFg5OWZ0K1hoa05jU2cremRYYjc2NVZnVkpISGM5Vm1iR0pjNDBETzgxRERIcjZSblh6WnA0L3NYdDlwcnJQVFY5b2w5UUhWS2YrMmphL3dCbk9qakNEemx4bHo3NjIvUzkyV2M4MFU1UDExb3RJVmc3eVBPUHZxOVA3ZjhBSDVOd3pVMWwxdExmVXd5K05XTVZiMmtIenA0bzFRSEdjNEdPUS9HclRyRUxRUWJUZ0IzZG5kUS93MzQxMHhHc0NTeDJzcWpIcFlJcEJCY3hwNnltSGFINnZsUWFTM0ZuZDRmS1BsNFI3NjBiNVpqeWpRWk5IOUozQWdUZDgyaU9xUSswOWxHRG9TMFhvNjNPNHluemo3VFcyNlNuYThtOVgwYTJkdkVzVWZjZ3hWcGE5V0U4VXFhanZ3UnZyVWR3cStsMVlVejdqUUE1bjhtanM1MGd0ZlJiR2MrNnA3MFhVdHhkQmdBejhobndxMHpMREhNWUVMaGZPSjArRytyVklsZmdreVM2NDdEWC93QVg4YTZWRnhQczVVbEcyTS9wSHd4NTFGVm1rZHYyYUJQdHJnblJ2ZmlzcW12Mkdra0Y0bHl6TGxocEs2UEN0RW80aDI1em41Tnd6WExUVEhWa0x6OEtJbWwyQTdJMC9XTitGYWJTQWRGV2pjM2Z6MisrbTJtYm1aVzN2Sis3bmxUdm5uTzFjTWhIeWRGU3pKNWJySWoxam5wN3EwM2tMU3c5MVh6VFJEOUg5WVlhY2RuWlVxMnl1c1F0SmRPc0gxYWlqdGJiQ2hBTldqN3pnVkxiM3ZTVU5ybmVOY2hmNENoMWkzYnBLd1FqSmlKVGNQRWZmVVhWZWpSWUlyOFhDZFIzSHQ3YUF6ajVyK05UejM5OTFoT2FJRXhwOTVJeldiWW0yWE80cnoveWdWczR0Ny9TM0FkNVBoVTN6amFMdERzMjgzZFhCSXhIanZyUzBFYi9BTUdLYVNSZEdsYzRVMEpYa2VENk15NlRXN1d3OEZyOUlIcENJVzBrNWNvN0ZOM2RYU04yM2xNSEViSHg3UjhseEtlVzAvMFZDR0dDU1RXU2NEeG84ZXY5MnVqaGpSaTVCVHQxSHUvSnI1emRSMnkvdEpndndHK3BFT3R0VWhPWWpnbW5FRVFoK1pTNnZLYWlmYlNDZWVSbFZRTVowTFJFTnV2MmsxODA2T2xrL3dEYS9ITld6WDlxOXZGcjhucWJ3ckdkUHpVYnoyYzZtNncwdlNPVHdZak1hZ2VKT00xcFE5SDJROWdrYjc2ZUN5dUk1ODVhNWtYdHg2UHNIeHFTNDJzZW1QY1J2cEpXZGREOHRMVWhqaVhxNHp0cDVXeG85ZzdhRVZ2K29WK0hkbHBENFVMeUFzWTM4OEpqS04yZ3NlUXIwWDloa2txUmpKc1JqL3F0Rm4rY0ZEN0RVdHlyZVNNbU5rVXd2MWRudTNVdHdBaXZrQm8vU0ZTRzNReUIzYlFNNDlDa3RSQ1kzWENnUnJxN08ydU16VG4xcEhFU2o2OG1zV3l4czMvcHJjek4vTTlkSFJ6aWNHZWZRdTNuM0gzTHlxS0dHMFdXYVFuUjFhUFJ1SGJrMFcyc2R2TG5uTnlxVnBMenJXdUZrR3h0enc1ck1sbzA3RDBybTVVZkFackZ0QmJSRDluQVgrSnhXbU9kZ1BCbFQra0dvektwMGc3bVlzYzd2cFU2amMzVkJYbCtsSVFmNXo5OWY4Njgvd0RscVY1TGEyaVZvaU5jQ2FjL1VhdmZHWDhLZ1RSdEpJODd1eWpDMG5DQm1USFlLY3llVFlLTnNWOUFIbEd2alVsdGNvdlZpTVNwNk1QY1BGcU1VcjU5VXZKZ043Rldwc1NiSm1HQmlXV1BQODJWUHNOUjI2N2hFT3pkeEg3UHNOQW8raFR1eU95clNQYTdYYUZqclBGbmRUSlBjUGtlY2hsMEFlNFZDa052RTIxaWJSZ2JtM2ptYWRKWXhBNDdWSlEvQ2hDaXozRXFOcmhETnFhUDNubFNBMjJqV01ydEpTMjczVkhGQmNHMmZUbldCbW5hN3VMbDBFRE9yeWNpUldBKzFQaGx2c0ZjSFJVait5MkorMnZKZEZ6eGp4Q3BTRzlUWjcrRWJYVlVxZzRQVjFId3JNMSsyUEJmOTZ2TGFacFpvb01BSFhwenU4S2RvazBCMTBrRnRWWE1LdXZVa2J6VlhlV3AyWnNZRk03NzAyak8zN3NmKzlLM09SRjJudGxrNWZWU1JSY1RCOW5EbjF2VGtOUW94MXVvd0hmblJ5MkY3YTJ5Y2NjaC9JOWhyNy96N3ZqVnFrVTJoVXpqaDM3K2RYQUU4c3R4Skg2Y2VQdnF3Vm85SWpqNFNUNTJUbjdxdkwxK2NMcUZGWHJiWVF5UzJ5R0lzbzM0NTFZb0k0NUJOQ0Fkb21jY1JOTTMvVGkxN3Ziajc2Nk0vdUQ5OVN4cVFGNnVXd0J5TmRHbHBuUGtKQzUxYytkS2trNWpnQjQrUGh4VUlna1dZaWJ6bGJzN0JVNXp5aFg3S2NhbmJEWUhZYXVwK3NKYnRLK2VPVERVQmI3NUpKQWcxSEhPb3hsREt6c3o5L1AvQUdxYzU5Q3BPSkZKdG5HOXU5NkpNc2VPc3huenV3TFVEYlpKR0VUN2w3eTI4MENSbkhLc0VaRlM2dHdBelRjUjNaN1BiUUM4dFpwVzU5bTg0cEhMaTUyd01nR3ZHejhQalcyNlB2QmFscEFERU9NSHhOUllsaTJ3M3h0RzQwMTBXbHpkZFlFemE0TU5uUDV4VzhTdm1NQlJFT1p6bmY0VllXbHB0VE5zT1JUMjFkQ1c1V0xGdGpmMzdzMUk2U2FvMlBDUWQxWVYxRTJjNnM4ZFFobWVkektPY3RYY2l3dW1JdTJiUElWbzBhOExuaEZEVjJlTkZKN3lTUVp5dU4ySzFsQnJXVW96SE9lOFU4ZmVNVVlXNUZwSVQvSHZGYXdQS05HamdmVGo1ajZxV1ZkNlJzWEdPMkovd05JOE1uQ2QrUjIwUm5GQzN6cVkrZDdQOTYzbjgvbk5Sa25CSnpRNjFHeGg3b3hxTmRlY0hxcWppWjR3cDlnQXB4SjFtMmlrN1lIMyszZlIwWE4reUE4M201LzVhZ0ViM1FsdG84d01aYzQwamNPWEtvdjBtN3d4dVBRbUtkL2hUUjJ5RXl0anlvazQ5M1prTFhsMDA3c0ZaTGpHZC9QemF3SnVIMVJmN2gvbHJJWjgrRitQKzJrdk9qQktkbElOcXpUaHdCN2hVcU42UXAxdDRYaWtDN3lreFU0b2xZd0Y5VXRUUlBHd2IzVWJOVmNKTHU1NUhmVyt0c055c01NZTd1TkZqNUp0ZVQreWsvQTF3Z3hsVG5BNXduNzFOUlJRY0VyY2I0ajFLUER3ckdzRCs3Z1AzMHN3M25rMk4rLzd6UVVEbjNmbjNmWFVBMk1FNVVieTBXcjQxYktHajB4RGlHUE8zMWJKMVpMcE5yK3BkeWluZDNqZlJkZjdQOUgyMG1NTk1rN0ZzZSt2bTM5bXJXMmswNDY0azNtKzRpcDdnMmlTalFvZ2NTYU5HUER0enZGYkc3Nk5XZmtjYlRHbmZ2eFVTMmxyUFl1SlFXOG91TlA4TlhDU3hBc2RSU1ZUaytGWEszRUNucEVScHNPRGNXOUxOZEZOY0JMZWFTNW1XOHdTb1ZWR1Z4OEt1TFcwNlF0YlNWZjFxVHk2QTlLWXIvbzUzSGRmQ21tSFVwSEl4dzNhZmpUeGRVYlV2Ym8zZlhVamlPV0xQb3FtNFVwTFNaSEk3TEZCTGxES3E3Z2VWYk5nTi9ZYU1pYjQ4WXkzTEhxdCtOR2FWU0lvUm5EY3g0QTlvcDVaZUYyUHBSc3Arc1Z1NC80V2YrcmRSVThTdHVPL1A1OWdyckV3MytqbnRxSk5aUkdKMVk3YXRKTURXSldiemM5OVdHaU5MaVppV0VVd3doM2I2aHM4Q1h5RFNPb2JBSUM1N0tnZzZJenFXRW1jaGlWejRacU9HWjMxeXJsRjhCMjFzT1lHL0RIblVVVXRneUF1RHRXMWNXOERGWGRveVJ4dUNWRHR6SHhxS0tLVlpEclVzeStkdThjMTBlc1Qyc2t0cTB6YUpEalZyR1BoVTU2YnRJYmdNdUU0UkoyMXFoNk00UjZsdTJQaFNFV21MWXdOcjF4dUFHN0t0RkVXRHNTWkN3eHZMYnZoVjVjaVhZM3F4NndOajJldzFxNlQ2U2g2K1ZWa3d1TWQrNnJxV1NRVFNncm9ZREdNNXJYd2tyamFOalYyTFZ3d0lPQXVGUEtndXNXaXUyY0ZPRTE4MWpNbzc0UzJQaFNiZTNlSlN3R3FVSEh4cU83a1kzRnoxaFUraUJqTkpDWTJkZlZqT0RSWll4QWNiakpPS2p0NWI2MmpoVHNFbVNhai9SMDdlUVhUR3lBbG16ek80VjFxNjJwa1dFNUxxVkpwakRMclNRRTZmVThLUThrMnVQYUtubmRNeHh2NTRPK3VyNnRYNnRnQWR3N2ZycnBhMWNBaDdnTWdPL3MzKzZvSFNOVVl0aHRLNHpSZDdkQ1dINU5CUEtJRHkwek1LbTJkeGRtSWZxeWt3UHdyVW5TVTJnNzFCSEZqeEZjTXEzUStOWFFWR2wxeEtGMEt4NUh4QXJRZWk1QjRISDMxY3dMRjFhT1Z3MnAzQnhqMlZjUlQ5SmpqODVrZzgzbDNud3A0N2pwZTRrMTQ4elFPVlF3dzJ0N2N4eHVkSmFVZkhkUWpzTFVXU2FzY1JxMUYxSkdZREx5SE9vZjhZUDZhRnZQcU1XQ1RwYlNkd3E4TU50aVpRY09XTEZhdFp3aUl1TjdsUEVpbjBPUnY5RTBzV2xoTHM4TUR6eGlncWdMd25sVzFhUnRRbUNxdWQyS3V0bmVpSlRJY3h1TjFXTWtqMjdnbkFhSG1jZXRWM0lVazQySUowOEozMUNWWUhTMi9GYVJ1MGo2NllSOElQT2psbVZmQ2w4cVd5TnpFVnAxQnlLdU5oWm1HSmhpTEVyRXI0bk5TUnluWGVMTEh4WTRqeE5uN3F2VVpUdE5RMDkvYlJkSHRaRWNoM2p4NU1jdDNiM1ZJa0p0eEdEdWFDMVJSOWxHWGJzNDE0ODNuUThvd0tPeFRkMzFiU2I4N1R0OWxXLzhBaS84QVRXVjlSdnNxV2VjQm45WDFpZVMxQ3FMeE9CaFY4WGFvRE91MUVlYzVxOGxRN0Z0UHcwMEdJQzdqdUZRdm5uY2dZOTlkYlh6Wkppb0hmVnVHNWdrL0Ewdlovd0R0ZEVQdXpNWHo5UW9TcVd5Nmh0ell4WEJNNC9mVU1LT3F6dExnZHZDVVB3cnlsaGN3K01VZ2NWaGIzcTU3cG9TS21IV0pVNGNlWU4xYk1TeXY0bXRhYWdjK2thMk1XY2VxZ3FKTGlKZFFIRnE1MXFpMmJSWnpqVnhDZytWU004bVpzQ3JZQUhaNVloczVCNTFZK053MzJWMzhKbzI4WnpiV3lOajZUZHBxMEdjWVFmNnFaZ2UrcHRYSnVkRFI1dXpOVzBtazRGNEJxOTlKQUYzUk0rRDdUU1BKdlhTK0NQM1RRYVdMWTU1dklRdFdrY2FOSTF0cTBiSUhCejNrMFV1WWpDNllVNzgxaGdEUmdQbXNNclc2dDlTZVIyd2J2ZkZGM2xTSWZ6VUZtdW1kZTRZRmVRWi8vc3JpajFlMWpXNkNqMFhkTUxOMWtPa1NlalFXMmxkOUhheVlCM1YwWDR5dDlsUHMyMG5TUlVzOW5NTWFNR09RN3hTQVcrSXNZREE3dHkxdEdZWTFZd0taMHQ1R1U4anBvelNRaTNoMCtkSWNVSW02U0U3Q2ZhNGhUVWRWWmdzU1Q2OXkyUGhYbHI0US9SdDF4OGFMU0ZYZjFwVzFHc1cwSlllaWNZRlRQS05PMVBJVnViZldzN21Ya2NWNjN1cmw4S09Hcm1hR1RXQ2QxYXMxem9tWUlYN0c3YStiVEpES3VXaVY1UFBxeFIyTWl3dTJ2UW9PRHUzWkZmTUxLUlZKOUljUjhLQkZ1MEpKN2VWQTlJZEt3d2ZSVGlhdFNXTS9TTCt0TWRDMWlCTGJvNVAyTWVXK3MxcXU1NUxwdjJyMFFIUmZCS093ai9pYzRxVld1ekVpb1c0QlcvZnY3ZDlkQ3g3Z0ZzK3oyMUhkZWtYcDE4YWNlSGZRM241UEg1YzRyU3NpZysydEtzMHJIc1Fab04xUm9VUGJNMm1rYSt2NEluVWVoeE5XbUszbXZUOUk0Rll0N2FDMFh3R1RSMVhCTkU4Q2VOWVdUYW51U2lJVUVRNzJOTXN0MDhuZ053cXhjcU9LWTYvRVlyMzFjbjlrZnVwUjN5Q3JBZXJhaitvMVplTG1wb0xkTm8rcnVwb3BjSDNZcFI4bWFHaE0xODJXT05mVzFDdGZUSFRqb3Zha0trMTVHd202VGs5YTRrd0swMmRyYTlIcCt5ajMxODR1cFpQRFZnVndKWEVTUFlLSWhWblBqVzVoRXRGNVpXbGJQYWEwOVhHb2plYXVyZHpxeHZVOTRxNEtqQVdQSit1ckVmU1kvRDVMby9SKytvUDcwZmJVSCtIWDdUWFJvQTlZMU5jTDZUbWtrY1lERGhyUWVmeWYvOFFBSmhBQkFBSUNBUVFDQWdNQkFRQUFBQUFBQVFBUklURkJVV0Z4a1lHaHNjSFI0ZkFROGYvYUFBZ0JBUUFCUHlGeWZWa3pLVjVIVTFDTW40c2ovcGNvcTU5b0NZYm5LRU04c1plc3pVLzRVUjBMWXdXZFZuR1AybDRpSzQ0UDhCdmNiK3lDWExNWTg4blNISWNtUzF4VXdaZmMrb0pYMFRnUCtKNnNDYXM5dUpsVFN6YTd3cTg3UVI0Z3p5ckdlZmRRcjhmOHpzdUZPQzRsWEVvU3IwY2tvSG5aZHNlaGxlQy9VTlF1WjZTQmlBMmZYZTVjMVhOYzlUUDhjUnU5elY3Ym9PeE1CMUE4KzhaWjFkKzJOUHpLdEJ3UDI3TURYWUpEcmR4NXNwdWpVZEZuNnVzNng3U1h0OGFqcmFPT3FaeUxhWlcvR3Z4S2gvdVlOcUI1V09GNUsrWWNtT2Y2SmZNN1AvRlo4STdRcldaUTB3cEROSmhJTDdIWWM3b0tiekVmOFM0bTJvVkxyZzJqbFN0cThybVU3OWx5UGJiRDVOcXFtYUNielF4eE1nMWxxOFFEQmJJY04vekdxcnVmQUZxY25lbVZIYmw1aWlSZDUxMU9JL1ExZ1d6dVFHbXpKZDZoejlScWh2VDNGZVM5WUtYVGk2eElmeGl2Mlo2Um16N2lhMFdLbjhRRkNNdmJvTm14NEtQK0pYWHJ4Sm1qdkJDRFlqZUhXWTNGeWVsZkYvY2VjdFdEMTZ2cVlPSjNydFFMUkszUHhQUzNlSmxWUGN4QStxWGd2dGJTMFlHcmk3NTZrNXl2Y0NqU3ZVb2hyckFhYjNxSjVMUTNSL2tHV0JRSGxEaE1EMHh1REM1OFdxOWRFenRkdGM4ems4aWd1RExkV2Fqd3ZuVXNSTEYzVWVhSDJ3YXJaZEtKc2NEbGpBTzdNK1B1SGhrWWZ1TnR2NVBxSXN1TzNkRjBYeksxTjBYOHMwKzZLekpXbDJWMHJ3NXFaQm9pQ0czMGJqaE5TS3owNTl0SjJZbGRkVVF2Z2RObnZYM0RCMVo0ZUJSVVBXMGRmYXY1U2w4NDh6aFhCTklFYk1tMVV3UkVoaVBpL2NXNXJrUG9EK1lVL1EzRTl3YjZCVVJ0dmVMbzlFZFZTcSt3aFRJeE9Xd3RaYzRqeTI5ZTR1SHJseHJmS21QNW1la2NWbnRGbng3Z2txT1plTzBQWTZSTGlpMjlKdzJoWWhzUDVkRVU2ZGYweEVWTVd2N2o3S0p3aGpQalByajVtSmlDWnV0NW5rb3BLbXhTWG5CMDNONmFkRndQdVlPcUJZRkxSZlRpQ0ZiMzVCUDZFeWZHcSt1UktnSmsxSzBsajRialFJRDBSZTVxNFVXbFFMNDNjM0tSMGRHS0tyNWhYT0lBdm1YMUtIRjE3NmN2ZWlsMzhNKzVkcnRRa3JsZm9tVGRDM1ZOTTYvbHRnSHpTNldQMUFzTXgyOTdUNml1RzZQdUN1Z1pIWmJ6NHFJM0U5TDRvN3JnbUY1NVFmYzdtWTJGS0g4eDVuZDZmRDE0OFUrRDEzdEFSb2xkZ0Vwa2NjcTQ3RVgrU3dqbHlEN09sSkdOZEZHOVM4N2lRdWVnSGVtUHRDVUh6WmliR3VmQkZwU1lJY21zYjFWbzdTaFpwTnFySjMzeExzbnd0bk9KaDlaVHdoZDQzM3hCTzBBdnFoYnVsc2RaYnJrMzhUY3kzajAzemlXSmdyMHVMV0tLVCtTTDZXRllrT1VRNTk0cWhkOHl0Tm43ajVlbmFKT1hXRDd6VUdDY1MxckhuVmZ3bGRDNlpqdjFDYUlYV295M3k1bDl4RzJwbVpZd3Vsemw2T2pCZ052MjFuN2NXSmxwSUJoZTZxc1JPU004U3ViZEl3M3NGb3FRNElRMXo1M2ZhWkRjSUJhaDN6Y0RSeDdUWUwweC9XNFZxemkrSXRsYXcrZjd3ektyNFA4QURHYlRoKzQwdXVoVzR5Qnh5Z0FaU0x4dmJHbndhK1paRk4wM2kwREdLNms3NmpLdGZiRlZxRXNDYkYwTTB6clRESGVadU9ONWFpVHBGZDJQMUwwRzYzWXc5eDFWbVMxL29rQjBsZlpHRjQyTWVLcmcxcW15Vjh6dlIvWDZpVFZJWitZWUNZTkJPVHF5OExsaW9LTDV3cnJ3d0tseWg3eUdBRGlmeE9PQTYrSVNBZzJxdktjY3VZVlkxN21SZzdCK2FsWjdYZThNTVJubzF1Ui9aTWc1T2RIV0xna0EvYWp3c2JZNjlZeXlsL2czQkN6THlyOEVkTHhWU0h5Qm81eldvS3JsaTB2SmQyWlpUSXA0SENQQVEvUGNyL25GT3BNTDZMdWpVS0Z0d00xSUsxSktzNGwwWmg2NnY1V0NWbHVjTDlQMitzb2lRb3YrNnhYSkNwVzcwcGp2RWc5eExkTWFFR3V0R2tVMGkzVm5HdW1tK0lwMEhjTEpUVzQxUitFYjRLTEV3c2lZOFYwMGNhbDd4NlNLeWVXM2x2Z203OFVGQjZFdXUrV3YzSmVYMGpRVkc2RXpEeUpUTS9vVGg4UnhoMGxmY0xnM2RvL01yUnFXbTEvYmlWbDNQaVpHQ3VwamZ4djVpbFN3bzhQV2RPT0didHU2SXN1U3RieDI3Ymk3dVNGOTRrdWhsdmRuZzM4RjZJOXZkR2xheDI0ZktaSVUzSzNZOEppY3BvR3orNVdCS3FqTkRqVnM4UkpQQ1NCdE45OHhLSlFMQ3puck5mTUdtdnJ0cXVIQVpPc0lLeXRTNlhzY0ttTXhGNVdzN1c5eS9nZ0ZtcGh3YzViUTJYemR4c3JWeTB4YXdtazNWNFhuY3NEUXdUY2Q4elFVM2Y4QVpLS2Y1ekh5SXBYS1UzTHF5MVZaRnptYlo2ak5wNzZpQXo0bzJEOXhDa0RJMHpPeVlLMStCMDRURmNKdk9PU2FqRHl3c3QyMHRRQ1FVNS9oSVdyRCs4TVFOWllzRDIvM0VIRE9PQVFjUUFrT29jenAzSGlSWmNsUytLR3hpMzhGNWRBZXFBdEZ4bVM2VzFadUVaa0grUzNuRWVqaExPbXFNdnhEMUI5QXpPQ3R2V2NpVGhGdkNuaUhUMVMyQWxJVmplZkJDbXdtVGI5NGVqeEJPY1VxNUxmeGlYRnZNZUFTTU54VXh3VTdnV0Qwd2xITGRVSjAxZGtjTnZJenBZK1lXN1YybEh4TEREdE1HUXJyaVVKZUtpRTdoMWZ1WEpHRnNXNWdYM0p5bU03RE16TStiV255U1o2RHRoV2piOVR5c25qY1ZINHNwQmdIVnhlNUl2cFpVZVVvMGR5L3pIbUc5RGZBVEhFd3BsSTZzRk5JU3NaS05YRjlZeDJ5MDlIM01DZEZGUEIyaENuSzB6VS9SY0hWOVdXcjdDajVsbVA0Q0h4Q3BRdlBYbzVsY0dLV1Q2YnIzRUdwVlJleWpqTUxZYXNZcGZCVEtCMnhqMFVqZGloTTJ0TEZjYk5pRlBpMHZqM0dNMTRTOHl2bkpiVlQvWDNtTURsZXV5eHVXV1lyc0FMTzBHQkxiN283R0tnMmxhOGpJek56bjZadEpJTDVCTWt2a1lDUWM1WWZhWXhseW1XYmVKZU8rMDc4VkN6dC9CT2c5ZDJZQjd6NGh4N2xkSnhGODkvcVp6bTg1MndRU2JuQ2dZWnFCdVZTaFQwek42cm02cC9pQ2IyQnpLemNjN2g0bE0yWkF4SzJQY0pmODd0REluTWZkWnJhcis3WDRUUGFPN1JLcExiN1pkUWhWdk8rOHlDNU12OEFPa1E2WURsMXE2cmJLenVTWFR6aExDeDU2UlA1bVg3dnFEZXJFeHpEb2lWZnR2NVBtRUVTd1k0QkRNQndMeURuNGhEOERkbUFyNWxmYzZaTmVQS3AzWUJXMmJxSW5xaFN4MjJWWGQ1UWJsS3h5aHM5eXNCbTFrTHJjelpuUmQ5UTFSYkVzdXFEdjBVNWhlM3FCZ3ZJSTM4a3NpamJlcStpWmVlelFqOE1FYjN5L3FlR2ZDejFsY000b1A1WSszTGFVN2tRTUJPWlN4eW8xZU5aOEU0Ynh0YWZoMFFYNkY1K1VIT0dxTlFQOVJnZ3ZjRnlwR2U4QmRZSG5jZk55eDFKd3dlZ0htNmd6a3JKZmdmNGd1OHlKYThJNDRsV1dYaUtLamRLOVpTUWRqMTJnMXBNYWhycktmSUhwNytKbTFlc3I5VHBwc3hld3c3cEYrcW10UHZQM0ZDWStXZE1WVnVWUlk5dUNVR0t1cWVNUWQyekQzejVlYWQxM2w2L3dvcTlmam1OWUNCZFJEY3Y1Z0pzK1p5L3lWTVRncVl5QU9HSzZuR01WOHpzL2hUNXBETEM1Qy9mTWEyNWJMN2x2TkcyM1hUckJyODAxRzlDbUV4S25JOGpxRVZVT3BHVklZY2xmTXpaRmliOHdRekxtRHNwV1paRlpnSFBRUHlabHVsTm9aNGFlTDc5SXlKa1dKMUM4Um8ydlYwZWpvZkUzUnNYU085eXVERzVnc1gvQU4zTU1hcGdHZDVoQXA2amd2cFptVEljVW5xUENDbEYxV1B1SkJrcG5OOXdRWWpBRkdZcnZOYWs3U3ZxeDFSeG1NNHFkR1daR1YrWmlHWXdoMUVoWkZqRkZmUmlzVmNscy9xR2Y3Vkp1RWhVVmpWNTFPdWcyNy9FRVpvZ09QMEFxQzV6YXR5N2Q1U0xkNWlmVXJ5UmJIc096MnpRcU15NnpIeGYrNEl6Zy9aL3hxRUQ0NW54R0dqc1dwNEE1WWduaXhjVG9CQThaUjM4RncxRXpxMFQ1L1lxL2MvOVAwZk1VWjN6NC9xQ3VsWXZ0TDU4bmVnbUZoM3FOS3lCdXJSaEhiMW1Oc05ZZ09LWVJLak52d3dtM0QxSmt1WTNHN0ZnaWd4V0wzSXNzY3p2ZlgwbVRkWnZId2krNkY5U3o1bmIrSVViWEV6bU1xNmRJbHMvLzhRQUpSQUJBUUFDQVFRREFRRUJBQU1BQUFBQUFSRUFJVEZCVVdGeGdaR2hzZEhCNGZEeC85b0FDQUVCQUFFL0VDK0U3VXhXK0FlUHM0eHVuOUZRRTA2NzdZUE81LzhBaS96SWpzZ0I5SE5iYThNcGlHSVhBVkpsQlRBRXBkR0hrcVB1Vnd4ZDJ4QzhvN3kxaDBDZDZQeGhQT0N0clI4ZVlVbnV2bmx3ckYvOWY4VEcxbWFaOWVRK0E0YjVUYjFSZEQ0VW1IamkyWS9qZ1lQTUFJSEFNMEhYL1pqdU9Ed2E4OWVGQ3UzTTFpa1ZHTlEyTXhUajN5YkJpQVhPZU1TZlVqWTdRb3dndytFRDFpWUNmNWNOeUg4eWpuN3oycVArVGtvNTNsZlc4MGc5cC9wbDJCSGFPQlJrOTBESDVOU0MySjRKb2RRTUQyZnBCMUhGZHRHS2wxbzA5ZjVqTmpyditZLzhaemV3cmVIbytEQ0VoYnlQWi9xb2VNRzRQdE9tY3doa1d3WFlqejJ3VzQyc3F1aUs3R0pXU2FOd2xZVjJtS1NLQXRqMEFHdXFuemtCZ01QckZhL2xMUUd1OUN5SEVuWFlKQkFTbGl0akNVd1RPK0d0TjZkYWk5REJFZEFucHlyWmhLTUdzbXNxb3VHSnpzVitETmF2S2o5SER5eks5RUZTVWRtMU1NMFJDQ1BmYlBneGpZN212ei9od3BsZ1o4czVmTGh1RmE3K2UydURreHgycjhCS3JpMTd5QXlDcnhZd09RKzl2ajZJZTB4RWFpb1E2c1Rzc0syWVN6OTZDNmtEMnhLdFhsYmphV3VNSER3Z3lpcjdvTUt1MlNPK25DVHJNZHQwVUNLNFVBTWd5YW9rZlhBNnQ5RmZWZzdVY0NyOHNaUFNqSGFjenR4Z2lOWUFUb3RPdW8yT0k2STV4d0J5dC9XY2Mzcnorc0k3Q21qWmRPSk1ZWHNhVWVDZmhieUF0dm5rWG44Z3hPSjIrR25PQnNNcTNtcTltYjc1dCtqckVnbk9Oai9NQ0lsbHRJcVM3TllrQm9HYU5FaWVyOGNjYUNwV2hzOXlESFo0Rm9Xakx0VjlGY0FNcUpnbHFIZlE0MndlNDJmWnNJWkNvRElxSUVWSnVaaCtMcE9PQTBBZFJjMHdCTDFLaTQzc3luK0ZGaWJWZE1EQ0RGRXFrMEtXYnlsa3JaaDk0Y08vZ0tvSU03RUk3QjMwS0k4SmwxRTlGZmdPQTM3YmYxTXc4cnlXMGc1UWFNUW5QZWs5Vmcybm5pWDh4bTlGQmJFUXFEWERCRGFrcWpac0dqbEc4dUdrZnNQK3N4S01QZXpFL01wRWhVZ2ZMckpOZmxvdmJod3BiZk5TUnNGM1lleVlWRGEvSFpsNnZEd0Fyd0NCRVdscTBITmY5R0VOQkNJZVhYWEVHWUJCRTFMNDh0WXJwMUhlWVdmbkh5Tk5CUS85ZXBrdVBlUDFGTmZiQnlvaHBLTmtyRG5SY0M3N1VCMk9YYUFCMHdmUlRQeTRPK2JrbmVoalcrT0tudDZndDlKbVZSNlBIS1JqRHR3Tmc2cGpzTzRLRWlrdFFFREtQYW5LS0E4OWx3YkU1QkNLeFFMd1l5c0xzRXZsL3B3SFVKaDlkQlZkd1Baa2dJbWhxb2tVNnFrU0Rsc0RzTUFpbEE3VGd4aTdEQU5zdmJudnBrZXROVEtZQnRNNk91VWFqaXFLMEVYVUtjT2xBSlF5bXdkSElaekMxODFSeEhuWTR3bURNQXM4MndadmlPRzYrVk01Vnd2TU9ya2NOMkJIWUIzNmpHWWNJN1BCVHh0am8zOGpQa2YzaE9EQU9lbjJDOE5mR1RKdXl2QnhnbVFZSGdrVzlNU2pRcUZkOTRkVWdJQk43VncxTU1OTWhURVY0WmMzRjk5RmlNdW1Wa0RIUFl6Z1BkWG9tY0tNZ1dHVS93QXJETE40Q0hWT0phUzdTRmdJaFJSanNJdnl3ZFVJanVrQys0RjZPRjJESWM0QTFwQndMbzVPK1lLQXdWT0xRcTZNeFIrZUFhVElLZHlIYkc5bTBpY0lKRVRudmxLaUVJaFVwNkx2SlRja1M3TEFYMFl1dFhVSTFJbXhLUEptSzVJWlVPbFEwRzhFYVB1b0dzRTJoRFpna0dFRjVCWlYwWWVRbjQ5YUI4WXRIclgvQU4xdytzUFlrdC8wWDl3Y2NBYWx6cXVvNHkxREwzU0EvYmpaWFYvMmFQeko4U3dSbnNOWVl5akFLNXdVYjBZKzNBUEVOcjdBRitPR01QUWdPSnRVUjRWK0pTNVNPbmRVWHVyUXlLd2RUQ0YxanN4ZUFUVDE1QlN0WmdUWnF0bnlPbkZVc0RlNzcxSHc1aGh2SW5DbFI4MzJYQUxMVUd3Vm16cURaZ0MzT1NpbElhQVRLSVdHdVRocThtNUsvVG5oQlhreldBWlFUVmpuaWhCRk1HdHQ2VFF3SnJia3FsbmplWU90Y01lVVZBdTRHQU9MRzVhSkRrbVpwYW13cnRkdWxGemtMZlpJb2dlek5iT2c0eEdiRVdHdU5LdGlORWRNdCszL0FBM1hMaExhSytqY0RiUWRzVWx0NHB2UUNzNk9PdklHMVE3Ykpwb0hObGJwaVhUam9JSk5ISStOSzRCcTRIcm9QdTdCMnhtY3FLWWg0Q0xLb1REMWQ4RXdTQW81RkRxOHBJSHNNUmZBQ2NmOFordUdWR0lqMHo4eHRUdWlJTjZqNXlFZmd5N0xEUURtMko2cWZDb1Z3QlJjY1dHb2FOcnROTnhSeHl2dTFGVTJJSzZZdGVLTmhNVTU1dW93MzRnUExBbFRlSHpGTHVKZGNpVU9VOVZRdlpFNk1qbGhSU3lqYUxaOFBHWHVRcVFkRlhWd2c4NWRYUnhZTmNURGFBTGRFQmhvTCs0ZHpMSUk3bUFKb2psTkR0KzRLQ0VkV1ZOYUFiaFRvMkhjVE5wbHg0TFgxSXlsQjFEZmJpTVMxTHBWRmU3ZFRCMHFsUmk3d29jM2xnLzYwY0FZK0FGMXJsSTdKRnhWVTlWK3NuTXNxS3AvcXlSRjhaOElqelRtM3ZrYUxiTUVma2hraHMxSjNzNmRBd0NjSjZsM05ZNXpUdEp0R2tOSmEzTnNXRXhvdCtsWmx6Sk5lUTdZMEtqR0pGVXNFNUhlaEF3TWFBSVpmNDUwTVcvZjgrRVhGWjh2NW9hclZGZTJBRm1LMUNNblk1SHN0SFJReThUb1NieTJWK01Cbk1nQjc5VXpocFRtaGx1eHZMQkhDZkxGRmt3emYvY1A0V0YxNEl3b283UFhqZUZ6Q0gvb3pKRGk0U3djYWw0aXc4ZUVVSCs4Yzk3ako2RUtBSUlKRFR1WERZT1Eycm8rZ3AxdUYyNmE0cWdsbmtCSmp2NjdXODdJbGlsbHhYVi9teVJCSklQSXgyeFl1OFVLZEsxeGhoMEhOU2F1NmJpOHk0cXlCQlFUU1huUXRsWjdLVk1xRnVHSWZVSUNyWGhiWm9NTVMyaW9qd2dJMmpMaXNrRldudFFlUElzeUxSeUxIYUthalZHSlNmSFBjS2FQVVRKNG9IS1QwWTZNY0N4NXdiVjhUa0h6a2tCdThuZjFlNE9GYXE3ZDRvYTBKdVlKM0JEajRsbUhFR09ST1JIQTRKbFBiWGxQdDZzZmFPOGUrK1AwRnpYNVpaZEwwQkFjSUtiUkY4b1BibTVOdWZoR1duWjl1VEk2eXV6VTZhSUdqUTZxZy9MT1kwR3BPK0dTdE9pSmRWQnN5QXZPZGphbmhFY2hERzFZMUZGSmdzRWdwd1pOcHR6OHZiOUZvMFliaWg5Qm9rNkpRUE1jT2UvZ2VxS1IwNmh1Q3BadVF2RkNnZ0YyYXVBYXpFaUFSZ2doRFZYT3lUa3FUTU5LRld4L0xWOS9zMFJ1VTNhWWlITzhyU09JaXNZSWJpaCtiaXhoTkpUV2lHYTV1a3FZTEtCamt3OVNZcW1STkVlUmlCRTJQVWNDV3hCQm95NDdUZGswSE00RFVmZDBrODIyVGF3L3Z4amJBUFNhNXhOTXp5R0NnQlNHdTl5aEZ3dks0ZGNZbEh4cVZENW5ERjUyaklIWHYwWlVCNG1GQXRBSjlZRGdQTXFWQ2NEeGNmMmNRWEk4bTFNamhlelJydGY3RGtGdzIwNXVMVUZDRlNUQ2VLUjVQN1VyazFpc25nV2FoZFFpWGt0eEVKY0FoQkE1Nm00ZHFiS2lLREI3YVV3QlFpMlRXb201THlzY1NNREZIY0Z4ZnF5Q0FZUkNBZ210dHdQcFVJSGtJZytNYm9oUnNiWE5YTGhKaEFFeUd0M1htb1hHUmJvaGIzaFcxMnhhSTlFZTJKVFRDRHNzSWVNTDdLdVVkc1hDdlUwWkxxRHM1d3FvcEZsVmdWdzBOVmdhNmlIY1p0WDhiUHd6aDJCY0lZY3pyMkJMZzBJZmtVemVIdklkWFFYbkVuZi9BQUR3YzVtNWdXZElDMW9pOGhqUTNhS3RnTnpGdnpibDQyS2c3cjBjcERSMG5TTDN4NEx5UFUvSndKdjdFanBTZFh0alIzb2tpMEZkbzc3WU5mQTdrSUw0bkZ3bjRIWm5VU2NPbkFodThXNnBvWTRYSUtPaHRSYnB4QU0yckk5dzBrczY1YzA2SmlOTmU5NHhLNXFMeSs4SUNDN0h3S1J1cERCbVpzeDVES0xDOEpnU3hsVFBoUGpEb1ZXRURTemtDaXZBeFRoSnI4cWJJSHBaaE9nQjNWT1FRUEIwNXdRQ1lBOURVYll2cnBpNTBoTEcwbWlyRU85VDdESnorWWlzNis4Y3BNVFovWkg4Q3lVWWpGSWgyLzVsUG8wTmFDUE5jWXh3eGtDVTA2QjNqYXZ3a0dzTXNVTmJWN1BSTVBseW5tZGgyT3VPdWZxZURCR0dSbUVvdVg3WUFpSnQxTWdBZFZ1ZENSSzlzVXUzTERzMEw4cmdSM0JUSHRUSDdJd0Rld29QR0xBZmU2dllwaHA2UktoSTZSd3I2SllFR1ZoaHB3b0s1WXBONWNhbS9mS2I1QjU3WTI0MlpBVThJakh2Z3NUdHJSb3dabDl6T2llb0s3Zmh4RVdaU3hCS2kxNzVMdy9nckFrQm9ENkRJZWpMMkU4elp5QmRkRkExL3dCTTIwTWhjck9jdVZiQTduL2d5ckVGKzVrV0FwYmRIMHFGTUI2bTJ5dEozQ2hsWFNuWGFqUnNnQmN2bEpoYmVnYW5iQi9ObHRZSkFBTWpDYUVtVVNYcUdzV290RzRxanBlWGZnbEllL0dQSXBhTmd3QksrcHZjMnNKaHliUVZYcGpsa3BDL2UzTnBhQjg1eWZpZDFYOXg1UEZQL3VSd2hPRStUMmpoeEpXVGJzSkJybENqbXVIK09NTFFnbmFBQnNPbTZOTWJ0ZStCMTYwRVNSODQxWjZSTVNPTnMyYnl0ZUJXbDN3NlRCenJBSmFjTHppNmFxYXZXc1AzTnVEYXBKc1pWNXdFVWxSQlZXRy91VUhzK3FUUi9lQTJHeU4rVGdiWG1LOGxQQThabzNWY2hMTjRjT0JvVFQzaGw5Tk1ENjFuT3h3ZEhMWnk5MWh4MmlYK3NIS3JoWW4zNG5KTXF5UGJsV2FhbmpreUVhMlh0TlNIM2l6UjR0azUxdHFCclFRY2FuQ0lJbWdpdElEM3lrODFtUENnUUNzTGgrVndlallsZzZSWi9IbWNjTXlKOGRIeGtxQkFBUElMZldLaWphaS9WUXc0M0lCZnpqT2dQNk01WkV6S3RjeXJkOWVJUE11WlZPV3NCbW5jdVdCaVZZbFM2Vi9tT1VER012QmdUcWVpbkRuaFVsN3VhaHp6OTJ3b0pzUGJINmpnK29iVjFuSGhGbEQ3eGZ6YWYzd0RFWnQ4R25lc050RDJKeENnK2prbjcxMWQ0UitPUUZlRHErNmtIR2tBNHY4QW00RXk1VEtWVlZjbWRkQVk4REY1TzhZNHEyN0toTzd6aXV3RU41SjdSeDFOVko5NHNyQSszZ2NkQVNjNnN0MEpqNXhsd0MrN1R5Qk95MnpoSEJOd2lxOS8xNU96aWpvWkEwdkxUbEhtT3ovR1B3TjA2WlB0emh3KzM4Wm9BZFJIeG9INWg5NStwTzZVL2NBQVpCSlBNQS9XQkdQYitIR054SHFxWEd6bGNUT0I2RFJQdk9FM0lvNXBFY0s3czR4WHYyVGgyOURBa3RDVitPbWM0cDZLRjBPQTl1S1RZeGVSUnpXb0Z6N3dpTzNzOW5pdGIyTUx5WDloc0lrNThuaGlXQkFXSWkveGk0OHJVRVk5VjVFM2laQlJ3aEs1LzhRQU54RUFBUU1DQkFJSUJBWUJCUUFBQUFBQUFRQUNFUU1oQkJJeFFSQVRCU0l5VVdGeGdaR3h3ZUh3SUROQ2txSFJGQlV3VW1LQy85b0FDQUVDQVFFL0FPQTRpRkg0RElRZ2hFeXJJQW0vOG9tbTN4V2FvKzJnVGFZR3QrQVc2S2NPc2lRTjFuZG9DaHArSUhoUENTaVpXVW5WUzNRWEt5dmRxbXRhM1JEdGNiQkdxemE2RG5PTnhDZllvTWEyNENtUnFvLzJaalZGeE9xRHYrQVFwRTlvb0FORUJBQWpoSURnZUdaN3hhd1FwamU2Z040UDdTNnNYS3pnYUlPVWhQcERVT2xNekFRZUlsUktKaENUb3NrUEFLZHFnU3N4UVVCd2dvTkJNRldBUGttbXltRUhNS09aTzdTZmtZMlNib09iSkIyS0JDRXlWQVFFSmpYdU1OdW1kRll4d2x3RFI0bUVPaHlSK1lQWXF0MFJpR0FsaER2TFgyVG1PYlZJY0lqZ2Z6QW5hOFJLQVRDUzlPdUNzNGk1V2VrUEZDbzQ5bHFITVBhUjdTTktuNXFDMjlnbTNsY3NvTklWREQxY1EvSlRFa3JEVTZQUm90MW5HeFBmNE5IeldMcHZ6Y3hoTUh1MTlYSFFKcEJPeDlYdVA4V1QzR0FQNytEdjdWVERVYTJHSFBFOXgzQ3htRHE0UjhHN2RpaitZRWNyallvdGVOcFJMZ2dIR1VUbFZ5WUNhQUFRWEtLTFZ6YVRkQXVlZGgvQ0QzTzdRUjdVTEtUcTVjdGdUR0ZzNktKY3FXRkx1MHFWS25nOE5Jc1NKSjdtL3dCbENidWRZZ1gvQU9vMmFQRXJDdlpCb1ZCMWU3WnZkSjNKVlpycUQ4bFErNWdlaldwcmM3ZzBiK2Z3Y3FoR2NNRzN4Kzl0Q3FsSm1Kb21pN1E2ZUIyVldtNm5XTk4rb0psQm5pZzN1UWZzQ25WSEd3UXp1TmswWmdVVzJNRkFNR3lCT3dYWDdrQTZicHc2eUZLUDFJTUJjaDBWUmEwa3VLZGhzUFNBRGJ1M1ZGckM4TkFXSUxYMVlPa2tueVo5VUE1b3pHNUF6ZWJuZG4yQzViczJWdHlEQThYZnFjZkxaVTZZYUJtdVJ2dWlHblZWYVJhNlJjSDdqeU8zY1VEOS9mb2ZkUHdlR3JrMUN3RWtMSGRHbkQwRFVBanZ1cVpFQlh1WlRIZFl3aFpNQWhPVExTcFBldjFhcHoyTmk2RlZwZkFUcUZab0VNSmtUb1U2blZwbVhETFBlaXh3YTdNQ0lFcXBSY3lzYzMwV0VhM250ODFVdzFaMmEyclNQVXVYK080MVp0Mm1uWFlCWVdnV3ZhU1FZQjl5YmxRRkFLcWdjb29pbUp2OTNWT052RmRNQ2NBNU1hVzB5LzBVR2JJTklLYlRxbHBmRmdzTGhxbUpjV01pWW01V0l3MWFnNktnaEREMXd3dnltQ3VqVzB4ZzJ1Z2FYc210bzFqT1dmUUxwOWxOcHBockJvZGxoK3JYYWNrR1JzcVdQcFVuWnNVOXJHOTdpR2duWVhJRXJGWUxDNDBBdjI3bFZxQjdPeUZXeDFKdlM0Nk9NNWkwdkhjUURCanhDb3NMSGdxc3djN0tkRG1iKzY0UWU0dHpEVWdPL3dEVExFZXlwMUcwbjVobzJUNXNkdjZGVG1hQzAyNFlxcTFqT1h1ZGZMNjZCQWs2L2YzSjlsUjdJOTEwcmo2VHNPNmxJSk5vUjBRS2tJT20wb0Z6SFNKOUZtWUFtMTNoc05jWTgxVDZVclVxZkxZQkhrcWZTZGFub0FzVmpLMkxBenhaSFdWWGJSeHJlVmltTnFOQm1ITkRoSTNnZzNRNlF4WUVaeWhTek5rTEZkR1liRVlpbmk2alpmU25LYnlKc2Q3Z29WR2JLcUJXbzVwOEQ0Um9WbU9zNVRQN1gvMDVBM2dDSTkyRTYyM1lWVnF2d2xJTXAyT3BzU0I4eEtIU0dKZllFZWpTZmpaVmlLakJXYjVHTy81blliQlV4bU9VZmUzMEhxVjBoaTZsWEZFMDNFQVdFSnozT3VTaTdTVnpPcGxpQXN3SWlFNHR1bXVFM01wK1U2SmdBRnlnV0xLeXlEUUdTRGRaWEp0TjRNb1VXbW5uTHg4MVJmUXd3T1RFVDVwdUtwT0JtdUI2S3BXd1FrODBFckNZK2huczRIdkNxMHJjeW1aR25oSGM3NUZZVmd2VWNMTjBuVWVBTzRLclZNMVV1ZFkrSUxUN2l4UWNINlgvQUhPL29MQ3ZCbW03UTIrNHNQSUxIWXh1R1lhRk15ODZudSt2d0NKS0Yyb05KSURVV096NWRFN0xmS3N0cFFnU29hUm9nR3F5YTZ3VEhVMnRPYlZEbGtMTFRUU0JNb0YwR1VBOXpDZ0lhWjFDanFMRFkvRllWOHNkNkhSZjZ4Z2F6T1dRV2p5QkhzZzdCNnNyQWVyaC9GMCt0Z1dkcXBtOHBQeHNzUjBrL0tXMFJsSGZ2OVBSRXF5RVFnNTAyUkw4MG5WZnBLYk1JTmtTb0lkQlROQ25DMG9NbHFMSUN1TjExaHVnWG96RUp4QTBLenREU0pYTWJsaFNFSWlDaS9ZQk5jU1Uvc2xBQW1DZzBFR0F1NkUwbE5hYzBJNkZOVFNZTjE0cGpvVG5od1RLZ0FoWmdWQTRRZ1czUXk1RGE2RVpDcE9XRkIxUUpVcHB1bjlsQk5BcHNrcmVVMHlVQ1dWTEk2Rk5RRmtFeHZWVlFBQkZoQVFRTGxtV1lIZENtRTVvQ1BjckFJU29VRk5CRGsvc2xVd002cUdUQVRnbUM2SjY1bEdJS2FDZ2JRZ0VIV3VxaEx4Wk1mbUYxYjhITk1Jdm5oQ0FVS20wRXdiS1NEQ2QyVkpDRndvUWJDeXVKc0VLYmxFQ0NVTGFLNTFWbFBnbXRncUVBbzRRbzRRaHdsQ0ZZc2lVS2JpSmhOb2tYSmhCckZMUm9zNFZ5cEFXWlhJTXBseXF1cXlqTEtkcXBSS2xDWVE0UWd4eDBRdzFZM2lGeUd0N1RrR1U5aEtMdzIxZ2pWblV5dWFTaVZ6QXN6aXZOQ0k0YktscUZVMVVqbHFKVUJRdGt4cEtiUUoxS0xhRExPSzVsTnZaYjdvNGwreGp5Q2RWbTV1dVlTc3hLbGN3YklGNVFhRXdoUmRFaUZOdUEwS3A2cDV1ajJBcFIwUTA0Zi9FQURjUkFBRURBZ1FDQ0FRR0FRVUFBQUFBQUFFQUFoRURJUVFTTVVFUVVRVVRJbUZ4Z1pId01xR3gwUlFnSTVMQjRVSVZNRU5pZ3YvYUFBZ0JBd0VCUHdEaEtzbzVLRkg1TlFvS3lXdW10RGZoWFpBdWhuZFlXUWF4bDBYbmd3ZHBOQ1pvVWJBSVhNa0xLT1grM0FkWk5FYUtRRkJJNUJBc2JvaTV4R3FQd3FGWk1rSFJDbVl1Z0FBWVIwQ21UQ3Z4QW5pT0E0QUVvUUZITXJNQm9GSmNqSTRFV1FDaGpVMTBvRWtKbTZPZ1VPbmtnd2IzV1hrVmtjZ1MyUkNNRS9rbEFJaU5VQ010a09KK0ZHUVVURjBKK2FJSkpoTmJDeUhhNmFqc21rdmRBQ01qUk5FcTJ5RDNEZFowK3BUWUpkWlA2VHdnSkRDWEVjaEsvd0JXai9qUHFQdXFQU3VIY1lxQXQ4ZFBVSmxScm1Td3lEeTRSMlNtM0hDd1U5eUx0aVI5VStNdk5NMDBpNkRIVHVneDNjc25NcG9BbUU2MElPSU1vbVU3NFkyK3EwRXJPMllWYXZTdzdDK29ZQVZaMWJwRTlxemRRUDVjVmhIc0RUVGVCSTU2UjNBYWxFUU9YazF2MXVxYlpreDlEODJwbGVyUnhCNmd4ejVIMzZyQjR4bUtaSTFHb1V6VGxNRDJDQ0VITU9waEFOMlJieldXOFQvQ01EVlNUb0ZMenlRYS9jckkzY3BvYUpoUDJXYnVRY1NGVmZRYzBkVzF3TTdtUjlBbmFCUHE1WklWU28vR1ltTld0TUFjM2YxcXJSRGUwSnQvMkkxY2U0TEVNZVNLMUk5cm51N25ISUJVWE5yTXpzSG9KUG01eUxzclhIbDRmVnQxUmt0THovbDlCOVIzNmhVNmo4UFZGVm1vMS9uMzRGTXFOcVVnOW1oUWRaRjBxR2NsbGJLN0FUclFnWGJoUjNxQnpVQkFRQ25hamdBaDBsV0pBRFJDWlh4RlVGenJEWUt0bkRIT0owV0ZCWlJrYXg4M243S1d1SmFMRDRSM05iZDNxdXNibEwzV0JFbnViczBlTzZxVlhWSE9MYk5PdzBUTXcrRlllcm1ibE5pUGMrSTM1aFIzZS9jajBUY1ZpS0g2WWVRQVZnZWtSaUtuVm05cklTbVJCVFFOeWlaa2hIVk5UaVJFS1pZbVdaS1lTK1lGcktxMHRoTnhOTWt5NENEekNaVVpVbklaVEMwdmJsSU1tRlNyQjFGdVQrMWpYUDhBdzcvQlVzUlJhQkoveWJ0c0F2eERPcmdUT1YyMjVLeFZZT3BQRFFibHUyd0ZnZ1NKUUpHaW9FOWNJVFM4N2U3S3ROeWVTNklNWXdCQWlZVUFvRUZxbGdNYmxWWHNZSmNxYjJ2RWdyT3dtQWJycEExUHhUaEpSTldtMGlmbVYwQTk1RlF2Y2R0MWlYTWN3ak5OdWY4QVN4R0ZpbkZJRWs4bGhzWGljSVhaSXZ6VkJoWSt6aXFiWXdReEVid2Y0VmVxSDBuTTVyRHZKbzVocUlQN1RCK1Nob2NXblFFai93QXYwUHFuMDNWYVpZZFhRUEI3ZnVGR1J4RHhkQXdWZzZUbjFEVWl3K3Y5YWxRTnZmdUI2ckVmRVI1TG96QXZaWEZVZ2dEZm1ncm9Tb0tJbHFBZVZrM0lDZjBkVHFWQzk4ejRwL1IxSitzckM0U25ocHlUZE81cnFXdEdabHZNaGZnY0lkV0JkWkR2QlVNWFViVGRRYllPaEdpOVVwdzljc041dU8vWWp6Q0RCb0xpUDNNKzdWQWk1bVI1UEE3OW5CVTZUTVhWYzZwY0N3dkIreGhEbzdETXVRZk1pUGxkVVIxYnpSUGlOcmIrQTU3bFBPVUZ4OSs5ZlFMQVlWbExEZ1BBSmRjb01BYkFDeXhNSnJCbW5kRm8xVktBYnB6ZHdtYnlGTENMaS95UWJyS3VKbnU5RURMa0FDSWxFQWlBVVNRL0xsS2RUcjRrOXZENWZOUHcxUmh0Uko4MVNiaXoyVFNNTEY0Tjc2ZHhISThsU3FtZXJxQ0RyYm56Yi9JV0pjWXlOTjNjdEQza2JFS2pUeTB3eHR3T1JEaDZHNFdYSnJiOXJmdVZpbUVBVkc2aS91YitaV0R3eHhMaFZlSVlOQno5L01wb0JjaW51YTFzdU5reXMzcWcvVzZiT1crcWJjd2lCS1p2ZForeGxpL05OTGpZb3pOMFBpN29YNmU2QlpJUmlMZDZKQUZrTVBWcUU1R2t3bk5MYkVRVVNBc1Jnc05pbUVQYjVqVkRvdkdVYW1jT0RqNGtHUEZSaXdZZlNKOG1uNTJUS1dOZjhOUEw0d1BwZFV1am1EdFZqbVB5L3Z6VGJCVHlVbE9EY3ZhVFdzTk9OdUFnSno0ZEFWSWh6WlZSdmFCN2xRTXlFWHdTRUhvT1VoU0ZJRGxVNnJPZXJjU1BDRmNoWlpBbFFBRVNCb2cwSVFpbTJCV2x5ckZFQ0UyQzJ5RzZFcDBUb3FjR1lCVG01dDFTWmxsR25kWlZvaEI0YXFEQ014ZEZ3emFJUE1hQWVTQ0JJMFd5TUlhSW1TdG9SQkloTnNGTW9JNnBxY2JsVVNYU1UxNVBDR3JLRmxRSmk2ekdGbU1jSktFd3N6UnJaQnpYQ3hSUXMxTlRWS0FrS0lRMVZrSEJXT2lwakluTXk4WlF1ZzBJd0ZkU1ZKV1p5TE00a1hUV0FHVVVORm9aVWdCTmVEWkJ3R3E2MXUxMDF6anRDTUhVb1FOQXUwVmxqVk9NdHR0eEVJQUlGU2dVNi9DRUpGd2dTaXM3RzJsZGMwMkYxMnozTElOeWdCc0ZLRFNRZzBJUU5BaVNnSWFFTEFvQ3lGMEFvUVVxVm1RamRkZFNidXV2Y2ZnYXBxbTVNSU5CRnlTZzFvc0FneHNJQWxkVzdVMldWbzcwTzVhbS9CcU82Mld4UU5sZEEyUUtzRTZzMExySHVGZ2hUcXYxZDZJWWVudmRCcldqc2lFSlFDRENWMUpIeEw5TWQ2ekZPRHRpbW15SGNocndDaXkwQ2l4NENKVThQLy9aIiBpZD0iYyIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiLz48L2RlZnM+PC9zdmc+", + "description": "Displays flow rate reading as a horizontal progress bar with background. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'flowRate', label: 'Flow Rate', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"flowRate\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#2B54CE\"},{\"from\":10,\"to\":30,\"color\":\"#3B911C\"},{\"from\":30,\"to\":50,\"color\":\"#F77410\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":100,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:Zmxvd19yYXRlX3Byb2dyZXNzX2Jhcl9iYWNrZ3JvdW5kLnBuZw==:Zmxvd19yYXRlX3Byb2dyZXNzX2Jhcl9iYWNrZ3JvdW5kLnBuZw==;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#2B54CE\"},{\"from\":10,\"to\":30,\"color\":\"#3B911C\"},{\"from\":30,\"to\":50,\"color\":\"#F77410\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Flow rate\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"m³/hr\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "liquid", + "fluid", + "flow rate", + "fluid dynamics", + "velocity", + "mass flow", + "volume flow" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/flow_rate_range_chart.json b/application/src/main/data/json/system/widget_types/flow_rate_range_chart.json new file mode 100644 index 0000000000..430e93f3fe --- /dev/null +++ b/application/src/main/data/json/system/widget_types/flow_rate_range_chart.json @@ -0,0 +1,35 @@ +{ + "fqn": "flow_rate_range_chart", + "name": "Flow rate range chart", + "deprecated": false, + "image": "tb-image:Zmxvd19yYXRlX3JhbmdlX2NoYXJ0LnN2Zw==:Zmxvd19yYXRlX3JhbmdlX2NoYXJ0LnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxnIGNsaXAtcGF0aD0idXJsKCNhKSI+PHBhdGggZmlsbD0idXJsKCNiKSIgZD0ibTc2IDUxLjMtMjEuOSA1Ni03LjYgMTd2MTkuM0gxOTR2LTI1LjNsLS41LTI2LjMtMTAuNSA0MC02LTI1LTIuNyA3LjMtMTEuMiAyNy41Yy03LjYtMjkuOS0xMy44LTQxLTIyLTcwLjRsLTguNCA0My45aC0xMC45TDExOS43IDQ0bC00LjItMjgtMjAuMSA3MC00LjgtMi4yLTE0LjUtMzIuNVoiLz48cGF0aCBzdHJva2U9InVybCgjYykiIHN0cm9rZS13aWR0aD0iLjkiIGQ9Im00Ni44IDEyNC40IDcuNi0xNy4yIDIyLTU2IDE0LjUgMzIuNiA0LjggMi4xIDIwLTcwIDQuNiAzNS4zIDEuOCA2NGgxMS40bDcuMi00NC43IDIyLjYgNzAuOSAxMS4zLTI3LjYgMi42LTcuNCA2LjEgMjUuNSAxMC0zOS45Ii8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Ik0xMi40IDI2LjloLjFsMS0uMmMuMy0uMS41LS4zLjYtLjUuMi0uMi4zLS40LjMtLjdsLjEtLjd2LTEuNmwtLjMtLjVhMSAxIDAgMCAwLS40LS4zLjkuOSAwIDAgMC0uNC0uMSAxIDEgMCAwIDAtLjguNGwtLjIuNGExLjkgMS45IDAgMCAwIC4yIDEuNGMwIC4yLjIuMy4zLjRoLjlhMS4zIDEuMyAwIDAgMCAuNy0uN2wuMS0uNGguM3YuNmEyIDIgMCAwIDEtMSAxbC0uNi4yYy0uMyAwLS42IDAtLjgtLjJMMTIgMjVhMiAyIDAgMCAxLS4zLS42IDIuNiAyLjYgMCAwIDEgMC0xLjVsLjQtLjYuNS0uNC44LS4yYy4zIDAgLjYgMCAuOC4yLjMuMS41LjMuNi41bC4zLjguMS44djEuMmMwIC4zLS4xLjYtLjMuOCAwIC4zLS4yLjYtLjQuOGwtLjguNS0xLjIuMnYtLjZabTcuNS0yLjd2LjhsLS4xIDEuMi0uNC44LS41LjRhMiAyIDAgMCAxLS44LjFoLS42Yy0uMiAwLS4zLS4yLS41LS4zbC0uNC0uNS0uMi0uN3YtM2wuNC0uOC42LS40LjctLjFoLjZhMS40IDEuNCAwIDAgMSAuOS44bC4yLjcuMSAxWm0tLjcgMVYyMy40bC0uMi0uNWExIDEgMCAwIDAtLjItLjRsLS4zLS4xYTEgMSAwIDAgMC0uNC0uMSAxIDEgMCAwIDAtLjQgMGwtLjQuNC0uMi41djIuNmwuMS41LjIuNC4zLjJoLjlsLjMtLjQuMi0uNXYtLjhabTIuNi0xLjF2My40SDIxdi00LjNoLjd2LjlabS0uMiAxaC0uM3YtLjdsLjQtLjcuNS0uNC44LS4xaC41YTEuMSAxLjEgMCAwIDEgLjguOHYzLjVoLS43di0zLjNsLS40LS4zLS41LS4xaC0uNWwtLjQuNC0uMS41di41Wm0yLjctLjMtLjQuMXYtLjZsLjQtLjYuNS0uNC43LS4xaC42bC41LjMuMy41djMuNWgtLjdWMjRsLS40LS4yYTEuMiAxLjIgMCAwIDAtLjkgMGMtLjEgMC0uMiAwLS4zLjJsLS4yLjN2LjRabTQuNC0xLjdoLjZsLjItLjJ2LS41bC0uNS0uMi0uMy4xLS4yLjNIMjhjMC0uMiAwLS4zLjItLjVsLjQtLjNoMS4xbC40LjMuMi41YzAgLjEgMCAuMy0uMi40bC0uMy4zaC0xdi0uMlptMCAuNHYtLjNoLjRsLjYuMWMuMiAwIC4zLjIuNC4zbC4xLjQtLjEuNWExIDEgMCAwIDEtLjUuM2wtLjYuMWgtLjVhMSAxIDAgMCAxLS41LS40LjguOCAwIDAgMS0uMS0uNWguNmwuMS4zLjQuMS41LS4xLjEtLjN2LS4zbC0uMy0uMWgtLjZabTUtMS43TDMxLjQgMjhoLS43bDIuNC02LjJoLjZabTEuNy0uM3Y2aC0uOHYtNmguOFptLS4yIDMuN2gtLjNsLjEtLjguNC0uN2ExLjYgMS42IDAgMCAxIDEuMi0uNWguNmwuNC4zLjMuNXYzLjVoLS43di0zLjNsLS4zLS4zYTEgMSAwIDAgMC0uNS0uMSAxIDEgMCAwIDAtLjkuNCAxLjYgMS42IDAgMCAwLS4zIDFabTQuNy0xLjN2My42aC0uN3YtNC4zaC43di43Wm0xLjMtLjd2LjdoLS44YTEgMSAwIDAgMC0uMy4zbC0uMi4zLS4xLjQtLjIuMXYtLjdsLjMtLjYuNC0uNGExLjEgMS4xIDAgMCAxIDEgMFpNMTUuNCA0OC4zdi40TDEzIDU0aC0uN2wyLjMtNS4xaC0zdi0uNmgzLjhabTQuNSAyLjR2LjhsLS4xIDEuMi0uNC44LS41LjRhMiAyIDAgMCAxLS44LjFoLS42Yy0uMiAwLS4zLS4yLS41LS4zbC0uNC0uNS0uMi0uN3YtM2wuNC0uOC42LS40LjctLjFoLjZhMS40IDEuNCAwIDAgMSAuOS44bC4yLjcuMSAxWm0tLjcgMVY0OS45bC0uMi0uNWExIDEgMCAwIDAtLjItLjRsLS4zLS4xYTEgMSAwIDAgMC0uNC0uMSAxIDEgMCAwIDAtLjQgMGwtLjQuNC0uMi41djIuNmwuMS41LjIuNC4zLjJoLjlsLjMtLjQuMi0uNXYtLjhabTIuNi0xLjFWNTRIMjF2LTQuM2guN3YuOVptLS4yIDFoLS4zVjUxbC40LS43LjUtLjQuOC0uMWguNWExLjEgMS4xIDAgMCAxIC44LjhWNTRoLS43di0zLjNsLS40LS4zLS41LS4xaC0uNWwtLjQuNC0uMS41di41Wm0yLjctLjMtLjQuMXYtLjZsLjQtLjYuNS0uNC43LS4xaC42bC41LjMuMy41VjU0aC0uN3YtMy40bC0uNC0uMmExLjIgMS4yIDAgMCAwLS45IDBjLS4xIDAtLjIgMC0uMy4ybC0uMi4zdi40Wm00LjQtMS43aC42bC4yLS4ydi0uNWwtLjUtLjItLjMuMS0uMi4zSDI4YzAtLjIgMC0uMy4yLS41bC40LS4zaDEuMWwuNC4zLjIuNWMwIC4xIDAgLjMtLjIuNGwtLjMuM2gtMXYtLjJabTAgLjR2LS4zaC40bC42LjFjLjIgMCAuMy4yLjQuM2wuMS40LS4xLjVhMSAxIDAgMCAxLS41LjNsLS42LjFoLS41YTEgMSAwIDAgMS0uNS0uNC44LjggMCAwIDEtLjEtLjVoLjZsLjEuMy40LjEuNS0uMS4xLS4zdi0uM2wtLjMtLjFoLS42Wm01LTEuNy0yLjMgNi4yaC0uN2wyLjQtNi4yaC42Wm0xLjctLjN2NmgtLjh2LTZoLjhabS0uMiAzLjdoLS4zbC4xLS44LjQtLjdhMS42IDEuNiAwIDAgMSAxLjItLjVoLjZsLjQuMy4zLjVWNTRoLS43di0zLjNsLS4zLS4zYTEgMSAwIDAgMC0uNS0uMSAxIDEgMCAwIDAtLjkuNCAxLjYgMS42IDAgMCAwLS4zIDFabTQuNy0xLjNWNTRoLS43di00LjNoLjd2LjdabTEuMy0uN3YuN2gtLjhhMSAxIDAgMCAwLS4zLjNsLS4yLjMtLjEuNC0uMi4xdi0uN2wuMy0uNi40LS40YTEuMSAxLjEgMCAwIDEgMSAwWk0xMi42IDc3LjhsLS41LS4yLjItMi44aDN2LjZIMTNsLS4yIDEuNi40LS4yaDEuMmwuNi41LjMuNi4xLjh2LjdsLS40LjYtLjYuNGEyLjIgMi4yIDAgMCAxLTEuNCAwbC0uNi0uMi0uNC0uNWEyIDIgMCAwIDEtLjEtLjdoLjZsLjIuNWMuMS4yLjIuMy40LjNsLjUuMi41LS4xLjMtLjMuMi0uNGExLjggMS44IDAgMCAwIDAtMWMwLS4yIDAtLjMtLjItLjRhMSAxIDAgMCAwLS4zLS4zbC0uNS0uMUgxM2wtLjQuNFptNy4zLS42di44bC0uMSAxLjItLjQuOC0uNS40YTIgMiAwIDAgMS0uOC4xaC0uNmMtLjIgMC0uMy0uMi0uNS0uM2wtLjQtLjUtLjItLjd2LTNsLjQtLjguNi0uNC43LS4xaC42YTEuNCAxLjQgMCAwIDEgLjkuOGwuMi43LjEgMVptLS43IDFWNzYuNGwtLjItLjVhMSAxIDAgMCAwLS4yLS40bC0uMy0uMWExIDEgMCAwIDAtLjQtLjEgMSAxIDAgMCAwLS40IDBsLS40LjQtLjIuNXYyLjZsLjEuNS4yLjQuMy4yaC45bC4zLS40LjItLjV2LS44Wm0yLjYtMS4xdjMuNEgyMXYtNC4zaC43di45Wm0tLjIgMWgtLjN2LS43bC40LS43LjUtLjQuOC0uMWguNWExLjEgMS4xIDAgMCAxIC44Ljh2My41aC0uN3YtMy4zbC0uNC0uMy0uNS0uMWgtLjVsLS40LjQtLjEuNXYuNVptMi43LS4zLS40LjF2LS42bC40LS42LjUtLjQuNy0uMWguNmwuNS4zLjMuNXYzLjVoLS43Vjc3bC0uNC0uMmExLjIgMS4yIDAgMCAwLS45IDBjLS4xIDAtLjIgMC0uMy4ybC0uMi4zdi40Wm00LjQtMS43aC42bC4yLS4ydi0uNWwtLjUtLjItLjMuMS0uMi4zSDI4YzAtLjIgMC0uMy4yLS41bC40LS4zaDEuMWwuNC4zLjIuNWMwIC4xIDAgLjMtLjIuNGwtLjMuM2gtMXYtLjJabTAgLjR2LS4zaC40bC42LjFjLjIgMCAuMy4yLjQuM2wuMS40LS4xLjVhMSAxIDAgMCAxLS41LjNsLS42LjFoLS41YTEgMSAwIDAgMS0uNS0uNC44LjggMCAwIDEtLjEtLjVoLjZsLjEuMy40LjEuNS0uMS4xLS4zdi0uM2wtLjMtLjFoLS42Wm01LTEuN0wzMS40IDgxaC0uN2wyLjQtNi4yaC42Wm0xLjctLjN2NmgtLjh2LTZoLjhabS0uMiAzLjdoLS4zbC4xLS44LjQtLjdhMS42IDEuNiAwIDAgMSAxLjItLjVoLjZsLjQuMy4zLjV2My41aC0uN3YtMy4zbC0uMy0uM2ExIDEgMCAwIDAtLjUtLjEgMSAxIDAgMCAwLS45LjQgMS42IDEuNiAwIDAgMC0uMyAxWm00LjctMS4zdjMuNmgtLjd2LTQuM2guN3YuN1ptMS4zLS43di43aC0uOGExIDEgMCAwIDAtLjMuM2wtLjIuMy0uMS40LS4yLjF2LS43bC4zLS42LjQtLjRhMS4xIDEuMSAwIDAgMSAxIDBaTTEyLjggMTAzLjhoLjVjLjIgMCAuNSAwIC42LS4yLjIgMCAuMy0uMi40LS4zbC4xLS41LS4xLS42LS4zLS4zLS42LS4xLS41LjFhLjkuOSAwIDAgMC0uNS45aC0uN2MwLS4zIDAtLjYuMi0uOC4xLS4zLjMtLjQuNi0uNmEyIDIgMCAwIDEgMS0uMmMuMyAwIC42IDAgLjguMi4zLjEuNS4zLjYuNS4yLjMuMi42LjIgMXYuNGwtLjQuNC0uNS40LS44LjFoLS42di0uNFptMCAuNnYtLjVoLjZjLjQgMCAuNyAwIC45LjJsLjYuMy4yLjUuMS41LS4xLjctLjQuNWMtLjIuMi0uMy4zLS42LjNsLS43LjFoLS43bC0uNS0uNGExLjQgMS40IDAgMCAxLS42LTEuMWguN2MwIC4yIDAgLjMuMi41bC4zLjMuNi4yYy4yIDAgLjQgMCAuNi0uMi4yIDAgLjMtLjIuNC0uM2wuMS0uNi0uMS0uNmEuOS45IDAgMCAwLS41LS4zbC0uNi0uMWgtLjVabTcuMS0uN3YuOGwtLjEgMS4yLS40LjgtLjUuNGEyIDIgMCAwIDEtLjguMWgtLjZjLS4yIDAtLjMtLjItLjUtLjNsLS40LS41LS4yLS43di0zbC40LS44LjYtLjQuNy0uMWguNmExLjQgMS40IDAgMCAxIC45LjhsLjIuNy4xIDFabS0uNyAxVjEwMi45bC0uMi0uNWExIDEgMCAwIDAtLjItLjRsLS4zLS4xYTEgMSAwIDAgMC0uNC0uMSAxIDEgMCAwIDAtLjQgMGwtLjQuNC0uMi41djIuNmwuMS41LjIuNC4zLjJoLjlsLjMtLjQuMi0uNXYtLjhabTIuNi0xLjF2My40SDIxdi00LjNoLjd2LjlabS0uMiAxaC0uM3YtLjdsLjQtLjcuNS0uNC44LS4xaC41YTEgMSAwIDAgMSAuOC44djMuNWgtLjd2LTMuM2wtLjQtLjMtLjUtLjFoLS41YTEgMSAwIDAgMC0uNC40bC0uMS41di41Wm0yLjctLjMtLjQuMXYtLjZsLjQtLjYuNS0uNC43LS4xaC42bC41LjMuMy41djMuNWgtLjd2LTMuNGwtLjQtLjJhMS4yIDEuMiAwIDAgMC0uOSAwYy0uMSAwLS4yIDAtLjMuMmwtLjIuM3YuNFptNC40LTEuN2guNmwuMi0uMnYtLjVsLS41LS4yLS4zLjEtLjIuM0gyOGMwLS4yIDAtLjMuMi0uNWwuNC0uM2gxLjFsLjQuMy4yLjVjMCAuMSAwIC4zLS4yLjRsLS4zLjNoLTF2LS4yWm0wIC40di0uM2guNGwuNi4xYy4yIDAgLjMuMi40LjNsLjEuNC0uMS41YTEgMSAwIDAgMS0uNS4zbC0uNi4xaC0uNWExIDEgMCAwIDEtLjUtLjQuOC44IDAgMCAxLS4xLS41aC42bC4xLjMuNC4xLjUtLjEuMS0uM3YtLjNsLS4zLS4xaC0uNlptNS0xLjctMi4zIDYuMmgtLjdsMi40LTYuMmguNlptMS43LS4zdjZoLS44di02aC44Wm0tLjIgMy43aC0uM2wuMS0uOC40LS43YTEuNiAxLjYgMCAwIDEgMS4yLS41aC42bC40LjMuMy41djMuNWgtLjd2LTMuM2wtLjMtLjNhMSAxIDAgMCAwLS41LS4xIDEgMSAwIDAgMC0uOS40IDEuNiAxLjYgMCAwIDAtLjMgMVptNC43LTEuM3YzLjZoLS43di00LjNoLjd2LjdabTEuMy0uN3YuN2gtLjhhMSAxIDAgMCAwLS4zLjNsLS4yLjMtLjEuNC0uMi4xdi0uN2wuMy0uNi40LS40YTEuMSAxLjEgMCAwIDEgMSAwWk0xNC4xIDEyNy43djUuOGgtLjd2LTQuOGwtMS41LjV2LS43bDItLjhoLjJabTUuOCAyLjV2LjhsLS4xIDEuMi0uNC44LS41LjRhMiAyIDAgMCAxLS44LjFoLS42Yy0uMiAwLS4zLS4yLS41LS4zbC0uNC0uNS0uMi0uN3YtM2wuNC0uOC42LS40LjctLjFoLjZhMS40IDEuNCAwIDAgMSAuOS44bC4yLjcuMSAxWm0tLjcgMVYxMjkuNGwtLjItLjVhMSAxIDAgMCAwLS4yLS40bC0uMy0uMWExIDEgMCAwIDAtLjQtLjEgMSAxIDAgMCAwLS40IDBsLS40LjQtLjIuNXYyLjZsLjEuNS4yLjQuMy4yaC45bC4zLS40LjItLjV2LS44Wm0yLjYtMS4xdjMuNEgyMXYtNC4zaC43di45Wm0tLjIgMWgtLjN2LS43bC40LS43LjUtLjQuOC0uMWguNWExIDEgMCAwIDEgLjguOHYzLjVoLS43di0zLjNsLS40LS4zLS41LS4xaC0uNWExIDEgMCAwIDAtLjQuNGwtLjEuNXYuNVptMi43LS4zLS40LjF2LS42bC40LS42LjUtLjQuNy0uMWguNmwuNS4zLjMuNXYzLjVoLS43VjEzMGwtLjQtLjJhMS4yIDEuMiAwIDAgMC0uOSAwYy0uMSAwLS4yIDAtLjMuMmwtLjIuM3YuNFptNC40LTEuN2guNmwuMi0uMnYtLjVsLS41LS4yLS4zLjEtLjIuM0gyOGMwLS4yIDAtLjMuMi0uNWwuNC0uM2gxLjFsLjQuMy4yLjVjMCAuMSAwIC4zLS4yLjRsLS4zLjNoLTF2LS4yWm0wIC40di0uM2guNGwuNi4xYy4yIDAgLjMuMi40LjNsLjEuNC0uMS41YTEgMSAwIDAgMS0uNS4zbC0uNi4xaC0uNWExIDEgMCAwIDEtLjUtLjQuOC44IDAgMCAxLS4xLS41aC42bC4xLjMuNC4xLjUtLjEuMS0uM3YtLjNsLS4zLS4xaC0uNlptNS0xLjctMi4zIDYuMmgtLjdsMi40LTYuMmguNlptMS43LS4zdjZoLS44di02aC44Wm0tLjIgMy43aC0uM2wuMS0uOC40LS43YTEuNiAxLjYgMCAwIDEgMS4yLS41aC42bC40LjMuMy41djMuNWgtLjd2LTMuM2wtLjMtLjNhMSAxIDAgMCAwLS41LS4xIDEgMSAwIDAgMC0uOS40IDEuNiAxLjYgMCAwIDAtLjMgMVptNC43LTEuM3YzLjZoLS43di00LjNoLjd2LjdabTEuMy0uN3YuN2gtLjhhMSAxIDAgMCAwLS4zLjNsLS4yLjMtLjEuNC0uMi4xdi0uN2wuMy0uNi40LS40YTEuMSAxLjEgMCAwIDEgMSAwWiIvPjxnIGNsaXAtcGF0aD0idXJsKCNkKSI+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNCIgZD0iTTQ1LjQgMjZhMSAxIDAgMSAwIDIuMiAwIDEgMSAwIDAgMC0yLjIgMFptMS4xLjJoMTQ3di0uNGgtMTQ3di40WiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Ik00NS40IDUyLjVhMSAxIDAgMSAwIDIuMiAwIDEgMSAwIDAgMC0yLjIgMFptMS4xLjJoMTQ3di0uNGgtMTQ3di40Wk00NS40IDc5YTEgMSAwIDEgMCAyLjIgMCAxIDEgMCAwIDAtMi4yIDBabTEuMS4yaDE0N3YtLjRoLTE0N3YuNFpNNDUuNCAxMDUuNWExIDEgMCAxIDAgMi4yIDAgMSAxIDAgMCAwLTIuMiAwWm0xLjEuMmgxNDd2LS40aC0xNDd2LjRaTTQ1LjQgMTMyYTEgMSAwIDEgMCAyLjIgMCAxIDEgMCAwIDAtMi4yIDBabTEuMS4yaDE0N3YtLjRoLTE0N3YuNFoiLz48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiIHN0cm9rZS1vcGFjaXR5PSIuNyIgc3Ryb2tlLXdpZHRoPSIuMyIgZD0iTTQ2LjYgMTQzLjloMTQ1LjUiLz48ZyBjbGlwLXBhdGg9InVybCgjZSkiPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLW9wYWNpdHk9Ii41IiBzdHJva2Utd2lkdGg9Ii4zIiBkPSJNNjEgMTQ1LjJ2LS44Ii8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTUxLjIgMTQ1LjR2NS44aC0uN3YtNC45bC0xLjQuNnYtLjdsMi0uOGguMVptNC42IDB2NS44aC0uN3YtNC45bC0xLjUuNnYtLjdsMi4xLS44aC4xWm04LjggMHY1LjhINjRsLTIuOS00LjR2NC40aC0uOHYtNS43aC44bDIuOSA0LjR2LTQuNGguN1ptMS4xIDMuN3YtLjFjMC0uMyAwLS42LjItLjggMC0uMy4yLS41LjMtLjdsLjYtLjUuOC0uMi44LjJjLjMuMS41LjMuNi41bC40LjcuMi44YzAgLjQgMCAuNy0uMiAxIDAgLjItLjIuNC0uNC42YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zdi0uOFptLjctLjEuMS43LjIuNS40LjMuNS4xaC42bC4zLS40LjMtLjVWMTQ4LjRjMC0uMi0uMi0uMy0uMy0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLS45LjVsLS4yLjV2LjZabTUuNCAxLjUgMS4xLTMuNmguOGwtMS42IDQuM2gtLjR2LS43Wm0tMS0zLjYgMS4yIDMuNnYuN2gtLjRsLTEuNS00LjNoLjdaIi8+PC9nPjxnIGNsaXAtcGF0aD0idXJsKCNmKSI+PHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0ic3F1YXJlIiBzdHJva2Utb3BhY2l0eT0iLjUiIHN0cm9rZS13aWR0aD0iLjMiIGQ9Ik05MC4yIDE0My42di0uOCIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Ik04MC40IDE0NS45djUuN2gtLjd2LTQuOGwtMS40LjV2LS43bDItLjdoLjFabTYgNXYuN2gtMy44di0uNWwxLjktMi4xLjUtLjdjLjItLjEuMy0uMy4zLS40di0xbC0uNC0uNGExIDEgMCAwIDAtLjUgMGgtLjZhMSAxIDAgMCAwLS40LjVsLS4xLjZoLS44bC4yLTEgLjctLjVhMiAyIDAgMCAxIDEtLjNjLjMgMCAuNyAwIDEgLjJsLjUuNS4yLjh2LjVsLS4zLjYtLjQuNS0uNC40LTEuNSAxLjdoMi45Wm03LjQtNXY1LjdoLS43bC0yLjktNC40djQuNGgtLjh2LTUuN2guOGwyLjkgNC40di00LjRoLjdabTEuMSAzLjZjMC0uNCAwLS43LjItMWwuMy0uNi42LS41LjgtLjEuOC4xLjYuNS40LjcuMi44di4xYzAgLjMgMCAuNi0uMi45IDAgLjItLjIuNS0uNC42YTEuOCAxLjggMCAwIDEtMS40LjcgMS44IDEuOCAwIDAgMS0xLjgtMS4zdi0uOVptLjcgMCAuMS42LjIuNWMuMS4yLjMuMy40LjNsLjUuMmMuMiAwIC40IDAgLjYtLjJsLjMtLjMuMy0uNVYxNDguOGMwLS4yLS4yLS4zLS4zLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjkuNWwtLjIuNXYuNlptNS40IDEuNCAxLjEtMy41aC44bC0xLjYgNC4yaC0uNHYtLjdabS0xLTMuNSAxLjIgMy42di42aC0uNGwtMS41LTQuMmguN1oiLz48L2c+PGcgY2xpcC1wYXRoPSJ1cmwoI2cpIj48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiIHN0cm9rZS1vcGFjaXR5PSIuNSIgc3Ryb2tlLXdpZHRoPSIuMyIgZD0iTTExOS40IDE0My42di0uOCIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Ik0xMDkuNiAxNDUuOXY1LjdoLS43di00LjhsLTEuNC41di0uN2wyLS43aC4xWm0zLjMgMi41aC41bC42LS4xLjQtLjQuMS0uNXYtLjVsLS40LS40SDExM2wtLjQuNHYuNWgtLjhjMC0uMyAwLS42LjItLjhsLjYtLjZhMiAyIDAgMCAxIDEtLjJjLjMgMCAuNiAwIC44LjIuMy4xLjUuMy42LjUuMi4zLjMuNi4zIDFsLS4xLjQtLjQuNGMwIC4yLS4zLjMtLjUuNGwtLjguMWgtLjZ2LS40Wm0wIC42di0uNWguNmMuNCAwIC43IDAgMSAuMmwuNS4zLjMuNXYuNWwtLjEuN2MwIC4yLS4yLjQtLjQuNWwtLjUuNGgtMS41bC0uNS0uM2ExLjQgMS40IDAgMCAxLS42LTEuMmguOHYuNWMuMi4xLjMuMy41LjNsLjUuMi42LS4xLjQtLjQuMS0uNi0uMS0uNmEuOS45IDAgMCAwLS41LS4zbC0uNi0uMWgtLjVabTEwLjEtMy4xdjUuN2gtLjdsLTIuOS00LjR2NC40aC0uOHYtNS43aC44bDIuOSA0LjR2LTQuNGguN1ptMS4xIDMuNmMwLS40IDAtLjcuMi0xbC4zLS42LjYtLjUuOC0uMS44LjEuNi41LjQuNy4yLjh2LjFjMCAuMyAwIC42LS4yLjkgMCAuMi0uMi41LS40LjZhMS44IDEuOCAwIDAgMS0xLjQuNyAxLjggMS44IDAgMCAxLTEuOC0xLjN2LS45Wm0uNyAwIC4xLjYuMi41Yy4xLjIuMy4zLjQuM2wuNS4yYy4yIDAgLjQgMCAuNi0uMmwuMy0uMy4zLS41VjE0OC44YzAtLjItLjItLjMtLjMtLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0uOS41bC0uMi41di42Wm01LjQgMS40IDEuMS0zLjVoLjhsLTEuNiA0LjJoLS40di0uN1ptLTEtMy41IDEuMiAzLjZ2LjZoLS40bC0xLjUtNC4yaC43WiIvPjwvZz48ZyBjbGlwLXBhdGg9InVybCgjaCkiPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLW9wYWNpdHk9Ii41IiBzdHJva2Utd2lkdGg9Ii4zIiBkPSJNMTQ4LjYgMTQzLjZ2LS44Ii8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTEzOC44IDE0NS45djUuN2gtLjd2LTQuOGwtMS40LjV2LS43bDItLjdoLjFabTYgMy44di42aC00di0uNWwyLjUtMy45aC42bC0uNiAxLjEtMS43IDIuN2gzLjNabS0uNy0zLjh2NS43aC0uN3YtNS43aC43Wm04LjEgMHY1LjdoLS43bC0yLjktNC40djQuNGgtLjh2LTUuN2guOGwyLjkgNC40di00LjRoLjdabTEuMSAzLjZjMC0uNCAwLS43LjItMWwuMy0uNi42LS41LjgtLjEuOC4xLjYuNS40LjcuMi44di4xYzAgLjMgMCAuNi0uMi45IDAgLjItLjIuNS0uNC42YTEuOCAxLjggMCAwIDEtMS40LjcgMS44IDEuOCAwIDAgMS0xLjgtMS4zdi0uOVptLjcgMCAuMS42LjIuNWMuMS4yLjMuMy40LjNsLjUuMmMuMiAwIC40IDAgLjYtLjJsLjMtLjMuMy0uNVYxNDguOGMwLS4yLS4yLS4zLS4zLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjkuNWwtLjIuNXYuNlptNS40IDEuNCAxLjEtMy41aC44bC0xLjYgNC4yaC0uNHYtLjdabS0xLTMuNSAxLjIgMy42di42aC0uNGwtMS41LTQuMmguN1oiLz48L2c+PGcgY2xpcC1wYXRoPSJ1cmwoI2kpIj48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiIHN0cm9rZS1vcGFjaXR5PSIuNSIgc3Ryb2tlLXdpZHRoPSIuMyIgZD0iTTE3Ny44IDE0My42di0uOCIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Ik0xNjggMTQ1Ljl2NS43aC0uN3YtNC44bC0xLjQuNXYtLjdsMi0uN2guMVptMy4yIDMtLjYtLjIuMy0yLjhoMi45di43aC0yLjNsLS4yIDEuNS40LS4yaC41bC44LjFjLjIgMCAuMy4yLjUuNGwuMy42LjEuOHYuN2wtLjQuNi0uNi40YTIgMiAwIDAgMS0uOC4ybC0uNi0uMS0uNi0uMy0uMy0uNWEyIDIgMCAwIDEtLjItLjdoLjdsLjEuNS40LjRoMWwuMy0uMy4yLS40YTEuOCAxLjggMCAwIDAgMC0xYzAtLjIgMC0uMy0uMi0uNGExIDEgMCAwIDAtLjMtLjNoLTEuMWwtLjMuM1ptMTAuMi0zdjUuN2gtLjdsLTIuOS00LjR2NC40aC0uOHYtNS43aC44bDIuOSA0LjR2LTQuNGguN1ptMS4xIDMuNmMwLS40IDAtLjcuMi0xbC4zLS42LjYtLjUuOC0uMS44LjEuNi41LjQuNy4yLjh2LjFjMCAuMyAwIC42LS4yLjkgMCAuMi0uMi41LS40LjZhMS44IDEuOCAwIDAgMS0xLjQuNyAxLjggMS44IDAgMCAxLTEuOC0xLjN2LS45Wm0uNyAwIC4xLjYuMi41Yy4xLjIuMy4zLjQuM2wuNS4yYy4yIDAgLjQgMCAuNi0uMmwuMy0uMy4zLS41VjE0OC44YzAtLjItLjItLjMtLjMtLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0uOS41bC0uMi41di42Wm01LjQgMS40IDEuMS0zLjVoLjhsLTEuNiA0LjJoLS40di0uN1ptLTEtMy41IDEuMiAzLjZ2LjZoLS40bC0xLjUtNC4yaC43WiIvPjwvZz48L2c+PGRlZnM+PGNsaXBQYXRoIGlkPSJhIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNNDYuNSAxMmgxNDd2MTMyaC0xNDd6Ii8+PC9jbGlwUGF0aD48Y2xpcFBhdGggaWQ9ImQiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik00NS4yIDExLjVoMTQ4djE0MmgtMTQ4eiIvPjwvY2xpcFBhdGg+PGNsaXBQYXRoIGlkPSJlIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNNDYuNSAxNDMuNmgyOS4ydjhINDYuNXoiLz48L2NsaXBQYXRoPjxjbGlwUGF0aCBpZD0iZiI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTc1LjcgMTQyLjdoMjkuMnY5LjlINzUuN3oiLz48L2NsaXBQYXRoPjxjbGlwUGF0aCBpZD0iZyI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTEwNC45IDE0Mi43aDI5LjJ2OS45aC0yOS4yeiIvPjwvY2xpcFBhdGg+PGNsaXBQYXRoIGlkPSJoIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTM0LjEgMTQyLjdoMjkuMnY5LjloLTI5LjJ6Ii8+PC9jbGlwUGF0aD48Y2xpcFBhdGggaWQ9ImkiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0xNjMuMyAxNDIuN2gyOS4ydjkuOWgtMjkuMnoiLz48L2NsaXBQYXRoPjxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjExNi43IiB4Mj0iMTE2LjciIHkxPSIxNTguMyIgeTI9Ii0xNy41IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIuMSIgc3RvcC1jb2xvcj0iIzQzNkFERiIvPjxzdG9wIG9mZnNldD0iLjEiIHN0b3AtY29sb3I9IiM1M0JEMkUiLz48c3RvcCBvZmZzZXQ9Ii4zIiBzdG9wLWNvbG9yPSIjNTNCRDJFIi8+PHN0b3Agb2Zmc2V0PSIuMyIgc3RvcC1jb2xvcj0iI0ZDOEYzQyIvPjxzdG9wIG9mZnNldD0iLjUiIHN0b3AtY29sb3I9IiNGQzhGM0MiLz48c3RvcCBvZmZzZXQ9Ii41IiBzdG9wLWNvbG9yPSIjRDMzRTU3Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImMiIHgxPSIxMTYuNSIgeDI9IjExNS42IiB5MT0iMTQxIiB5Mj0iLTE3LjUiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9Ii4xIiBzdG9wLWNvbG9yPSIjMzA1QUQ3Ii8+PHN0b3Agb2Zmc2V0PSIuMSIgc3RvcC1jb2xvcj0iIzNGQTcxQSIvPjxzdG9wIG9mZnNldD0iLjIiIHN0b3AtY29sb3I9IiMzRkE3MUEiLz48c3RvcCBvZmZzZXQ9Ii4yIiBzdG9wLWNvbG9yPSIjRjM2OTAwIi8+PHN0b3Agb2Zmc2V0PSIuNCIgc3RvcC1jb2xvcj0iI0YzNjkwMCIvPjxzdG9wIG9mZnNldD0iLjQiIHN0b3AtY29sb3I9IiNEODE4MzgiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48L3N2Zz4=", + "description": "Displays changes to flow rate data over time visualized with color ranges.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.rangeChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.rangeChartWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: false,\n defaultDataKeysFunction: function() {\n return [{ name: 'flowRate', label: 'Flow rate', type: 'timeseries' }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-range-chart-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-range-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flow rate\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 40;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":2592000000,\"interval\":86400000,\"fixedTimewindow\":{\"startTimeMs\":1700577227898,\"endTimeMs\":1700663627898},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"dataZoom\":true,\"rangeColors\":[{\"from\":0,\"to\":10,\"color\":\"#305AD7\"},{\"from\":10,\"to\":30,\"color\":\"#3FA71A\"},{\"from\":30,\"to\":50,\"color\":\"#F36900\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}],\"outOfRangeColor\":\"#ccc\",\"fillArea\":true,\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm\",\"lastUpdateAgo\":false,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Flow rate\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"m³/hr\",\"decimals\":0,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "liquid", + "fluid", + "flow rate", + "fluid dynamics", + "velocity", + "mass flow", + "volume flow" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/flow_rate_range_chart_with_background.json b/application/src/main/data/json/system/widget_types/flow_rate_range_chart_with_background.json new file mode 100644 index 0000000000..21ed0c71d1 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/flow_rate_range_chart_with_background.json @@ -0,0 +1,35 @@ +{ + "fqn": "flow_rate_range_chart_with_background", + "name": "Flow rate range chart with background", + "deprecated": false, + "image": "tb-image:Zmxvd19yYXRlX3JhbmdlX2NoYXJ0X3dpdGhfYmFja2dyb3VuZC5zdmc=:Zmxvd19yYXRlX3JhbmdlX2NoYXJ0X3dpdGhfYmFja2dyb3VuZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48L2c+PGcgY2xpcC1wYXRoPSJ1cmwoI2MpIj48cGF0aCBmaWxsPSJ1cmwoI2QpIiBkPSJtNzQuNSA0NS0yMS4yIDU1LjgtNy4zIDE3VjEzN2gxNDJWODUuNGwtMTAuNSA0MC01LjktMjQuOC0xMy40IDM0LjZjLTcuMi0yOS43LTEzLjItNDAuOC0yMS4xLTcwbC04LjEgNDMuNmgtMTAuNWwtMi03MC45LTQtMjcuOUw5MyA3OS42bC00LjYtMi4yLTE0LTMyLjNaIi8+PHBhdGggc3Ryb2tlPSJ1cmwoI2UpIiBzdHJva2Utd2lkdGg9Ii45IiBkPSJtNDYgMTE4IDcuMy0xN0w3NC41IDQ1bDE0IDMyLjUgNC43IDIuMkwxMTIuNSAxMGw0LjQgMzUuMSAxLjggNjQuOWgxMWw3LjMtNDUgMjEuNSA3MSAxMy40LTM1LjggNiAyNS44TDE4OCA4NiIvPjwvZz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii41IiBkPSJNMTMuMiAyMS40Yy41IDAgLjggMCAxLS4ybC43LS41LjMtLjZWMTcuOGwtLjMtLjZhMSAxIDAgMCAwLS4zLS4zLjkuOSAwIDAgMC0uNCAwIDEgMSAwIDAgMC0uOC4zbC0uMi40YTEuOSAxLjkgMCAwIDAgLjIgMS41bC4zLjMuNC4xLjUtLjFhMS4zIDEuMyAwIDAgMCAuNi0uNmwuMS0uNWguNGwtLjEuN2EyIDIgMCAwIDEtMSAxbC0uNi4xYy0uMyAwLS41IDAtLjctLjItLjIgMC0uNC0uMi0uNS0uNGEyIDIgMCAwIDEtLjMtLjYgMi42IDIuNiAwIDAgMSAwLTEuNWwuMy0uNi42LS40LjgtLjIuOC4yYy4yLjIuNC4zLjUuNmwuNC43djJsLS4yLjktLjUuNy0uNy41LTEuMi4yaC0uMXYtLjZabTcuMy0yLjd2MmwtLjQuOGMtLjIuMi0uNC40LS42LjRhMiAyIDAgMCAxLS44LjJsLS42LS4xLS41LS4zLS4zLS40LS4zLS44di0zbC40LS43Yy4yLS4yLjQtLjQuNi0uNGwuNy0uMi43LjFhMS40IDEuNCAwIDAgMSAuOC43bC4zLjd2MVptLS43IDF2LTEuOGwtLjItLjVhMSAxIDAgMCAwLS4yLS4zbC0uMy0uMmExIDEgMCAwIDAtLjQgMCAxIDEgMCAwIDAtLjQgMGwtLjQuMy0uMi42djIuNmwuMi41YzAgLjEgMCAuMi4yLjNsLjMuMmguOGwuMy0uMy4zLS42di0uOFptMi41LTFWMjJoLS44di00LjJoLjd2LjhabS0uMiAxaC0uM2wuMS0uOC4zLS42Yy4yLS4yLjMtLjQuNi0uNWwuNy0uMWguNWExLjEgMS4xIDAgMCAxIC44LjhWMjJoLS43di0zLjNsLS40LS4zLS41LS4xLS41LjEtLjMuMy0uMi41di41Wm0yLjgtLjQtLjUuMnYtLjdsLjQtLjYuNS0uNC44LS4xaC42bC40LjQuMy40VjIyaC0uN3YtMy4zYy0uMS0uMi0uMi0uMy0uNC0uM2ExLjIgMS4yIDAgMCAwLS45IDBsLS4zLjItLjIuM3YuNFptNC4yLTEuN2guNmwuMi0uMnYtLjVsLS41LS4xaC0uM2wtLjIuM2gtLjZjMC0uMSAwLS4zLjItLjQgMC0uMi4yLS4zLjQtLjNsLjUtLjFoLjZsLjQuNC4yLjRjMCAuMiAwIC4zLS4yLjRsLS4zLjMtLjYuMUgyOXYtLjNabTAgLjV2LS40aC40bC42LjEuNC4zLjEuNC0uMS41YTEgMSAwIDAgMS0uNS4zbC0uNi4xSDI5YTEgMSAwIDAgMS0uNS0uNC44LjggMCAwIDEtLjEtLjVoLjZsLjEuMy40LjFoLjVsLjEtLjR2LS4zbC0uMy0uMWgtLjZabTUtMS44LTIuNSA2LjJIMzFsMi40LTYuMmguNlptMS40LS4zdjZoLS43di02aC43Wm0tLjEgMy43SDM1VjE5bC40LS42YTEuNiAxLjYgMCAwIDEgMS4zLS42aC41bC41LjMuMi41LjEuN1YyMmgtLjd2LTMuM2wtLjQtLjNhMSAxIDAgMCAwLS40LS4xIDEgMSAwIDAgMC0xIC40IDEuNiAxLjYgMCAwIDAtLjIgMVptNC41LTEuM1YyMmgtLjd2LTQuMmguN3YuNlptMS40LS42di42aC0uOGExIDEgMCAwIDAtLjQuM2wtLjIuM3YuNGwtLjMuMi4xLS44LjItLjYuNC0uNGExLjEgMS4xIDAgMCAxIDEgMFpNMTYuMSA0M3YuNWwtMi4zIDUuM0gxM2wyLjQtNS4xaC0zLjFWNDNoMy44Wm00LjQgMi41djJsLS40LjgtLjYuNGEyIDIgMCAwIDEtLjguMWgtLjZsLS41LS4zLS4zLS41LS4zLS43di0zbC40LS44LjYtLjQuNy0uMWguN2ExLjQgMS40IDAgMCAxIC44LjhsLjMuN3YxWm0tLjcgMVY0NC42bC0uMi0uNWExIDEgMCAwIDAtLjItLjRsLS4zLS4yYTEgMSAwIDAgMC0uNCAwIDEgMSAwIDAgMC0uNCAwbC0uNC40LS4yLjV2Mi42bC4yLjVjMCAuMiAwIC4zLjIuNGwuMy4yaC44Yy4xLS4xLjMtLjIuMy0uNGwuMy0uNXYtLjlabTIuNS0xLjF2My40aC0uOHYtNC4zaC43di45Wm0tLjIgMWgtLjNsLjEtLjdjMC0uMy4yLS41LjMtLjdsLjYtLjQuNy0uMi41LjFhMS4xIDEuMSAwIDAgMSAuOC44djMuNWgtLjd2LTMuNGwtLjQtLjNoLTFjLS4xLjEtLjMuMi0uMy40bC0uMi40di42Wm0yLjgtLjMtLjUuMXYtLjZsLjQtLjYuNS0uNC44LS4yLjYuMS40LjMuMy41djMuNWgtLjd2LTMuNGwtLjQtLjNhMS4yIDEuMiAwIDAgMC0uOSAwbC0uMy4zLS4yLjN2LjRabTQuMi0xLjdoLjNsLjMtLjEuMi0uMnYtLjVIMjlsLS4yLjNoLS42YzAtLjIgMC0uMy4yLS41bC40LS4zSDMwYy4yIDAgLjMuMi40LjNsLjIuNWMwIC4xIDAgLjMtLjIuNGwtLjMuM2gtMXYtLjJabTAgLjR2LS4zaDFsLjQuNC4xLjQtLjEuNWExIDEgMCAwIDEtLjUuM0gyOWExIDEgMCAwIDEtLjUtLjMuOC44IDAgMCAxLS4xLS41aC42bC4xLjNoLjlsLjEtLjNWNDVsLS4zLS4yaC0uNlptNS0xLjctMi41IDYuMUgzMWwyLjQtNi4xaC42Wm0xLjQtLjR2NmgtLjd2LTZoLjdabS0uMSAzLjhIMzV2LS44bC40LS43YTEuNiAxLjYgMCAwIDEgMS4zLS42bC41LjFjLjIgMCAuMy4yLjUuM2wuMi41LjEuN3YyLjhoLS43di0zLjRjLS4yIDAtLjMtLjItLjQtLjNhMSAxIDAgMCAwLS40IDAgMSAxIDAgMCAwLTEgLjQgMS42IDEuNiAwIDAgMC0uMiAxWm00LjUtMS4zdjMuNWgtLjd2LTQuMmguN3YuN1ptMS40LS43di43aC0uOGExIDEgMCAwIDAtLjQuMmwtLjIuNHYuNGwtLjMuMS4xLS43LjItLjYuNC0uNGExLjEgMS4xIDAgMCAxIDEtLjFaTTEzLjQgNzIuOGwtLjYtLjIuMy0yLjhoM3YuN2gtMi40bC0uMiAxLjUuNC0uMS42LS4xLjcuMS41LjQuNC42djEuNWMtLjEuMy0uMi41LS40LjZsLS41LjRhMi4yIDIuMiAwIDAgMS0xLjQgMGMtLjMgMC0uNSAwLS42LS4ybC0uNC0uNWEyIDIgMCAwIDEtLjItLjdoLjdjMCAuMiAwIC40LjIuNSAwIC4yLjIuMy4zLjRoMWMuMiAwIC4zLS4yLjQtLjNsLjItLjRhMS44IDEuOCAwIDAgMCAwLTFsLS4yLS40YTEgMSAwIDAgMC0uNC0uM2gtMWMtLjIgMC0uMy4yLS40LjNabTcuMS0uNnYyLjFsLS40LjdjLS4yLjItLjQuNC0uNi40YTIgMiAwIDAgMS0uOC4ybC0uNi0uMS0uNS0uMy0uMy0uNC0uMy0uOHYtM2wuNC0uN2MuMi0uMi40LS40LjYtLjRsLjctLjIuNy4xYTEuNCAxLjQgMCAwIDEgLjguN2wuMy43djFabS0uNyAxdi0xLjhsLS4yLS41YTEgMSAwIDAgMC0uMi0uM2wtLjMtLjJhMSAxIDAgMCAwLS40IDAgMSAxIDAgMCAwLS40IDBsLS40LjMtLjIuNnYyLjZsLjIuNWMwIC4xIDAgLjIuMi4zbC4zLjJoLjhsLjMtLjMuMy0uNnYtLjhabTIuNS0xdjMuM2gtLjh2LTQuMmguN3YuOFptLS4yIDFoLS4zbC4xLS44LjMtLjZjLjItLjIuMy0uNC42LS41bC43LS4xaC41YTEuMSAxLjEgMCAwIDEgLjguOHYzLjVoLS43di0zLjNsLS40LS4zLS41LS4xLS41LjEtLjMuMy0uMi41di41Wm0yLjgtLjQtLjUuMnYtLjdsLjQtLjYuNS0uNC44LS4xaC42bC40LjQuMy40djMuNWgtLjd2LTMuM2MtLjEtLjItLjItLjMtLjQtLjNhMS4yIDEuMiAwIDAgMC0uOSAwbC0uMy4yLS4yLjN2LjRabTQuMi0xLjdoLjZsLjItLjJ2LS41bC0uNS0uMWgtLjNsLS4yLjNoLS42YzAtLjEgMC0uMy4yLS40IDAtLjIuMi0uMy40LS4zbC41LS4xaC42bC40LjQuMi40YzAgLjIgMCAuMy0uMi40bC0uMy4zLS42LjFIMjl2LS4zWm0wIC41di0uNGguNGwuNi4xLjQuMy4xLjQtLjEuNWExIDEgMCAwIDEtLjUuM2wtLjYuMUgyOWExIDEgMCAwIDEtLjUtLjQuOC44IDAgMCAxLS4xLS41aC42bC4xLjMuNC4xaC41bC4xLS40di0uM2wtLjMtLjFoLS42Wm01LTEuOEwzMS41IDc2SDMxbDIuNC02LjJoLjZabTEuNC0uM3Y2aC0uN3YtNmguN1ptLS4xIDMuN0gzNXYtLjhsLjQtLjZhMS42IDEuNiAwIDAgMSAxLjMtLjZoLjVsLjUuMy4yLjUuMS43djIuOGgtLjd2LTMuM2wtLjQtLjNhMSAxIDAgMCAwLS40LS4xIDEgMSAwIDAgMC0xIC40IDEuNiAxLjYgMCAwIDAtLjIgMVptNC41LTEuM3YzLjZoLS43di00LjJoLjd2LjZabTEuNC0uN3YuN2gtLjhhMSAxIDAgMCAwLS40LjNsLS4yLjN2LjRsLS4zLjIuMS0uOC4yLS41LjQtLjVhMS4xIDEuMSAwIDAgMSAxIDBaTTEzLjUgOTloMS4ybC4zLS40LjItLjUtLjEtLjYtLjQtLjMtLjUtLjEtLjYuMWEuOS45IDAgMCAwLS41LjloLS43YzAtLjMgMC0uNi4yLS44LjItLjMuNC0uNS43LS42YTIgMiAwIDAgMSAuOS0uMmMuMyAwIC42IDAgLjkuMi4yIDAgLjQuMy42LjVsLjIuOS0uMS41LS4zLjQtLjYuNGgtMS40di0uM1ptMCAuNnYtLjRoLjZsMSAuMS41LjQuMy41djEuMmwtLjQuNS0uNi4zLS43LjFoLS43bC0uNi0uNGExLjQgMS40IDAgMCAxLS41LTEuMmguN2wuMS42LjQuMy42LjFoLjVsLjQtLjQuMi0uNi0uMi0uNmEuOS45IDAgMCAwLS40LS4zbC0uNy0uMmgtLjVabTctLjZ2MmwtLjQuOC0uNi40YTIgMiAwIDAgMS0uOC4xaC0uNmwtLjUtLjMtLjMtLjUtLjMtLjd2LTNsLjQtLjguNi0uNC43LS4xaC43YTEuNCAxLjQgMCAwIDEgLjguOGwuMy43djFabS0uNyAxVjk4LjFsLS4yLS41YTEgMSAwIDAgMC0uMi0uNGwtLjMtLjJhMSAxIDAgMCAwLS40IDAgMSAxIDAgMCAwLS40IDBsLS40LjQtLjIuNXYyLjZsLjIuNWMwIC4yIDAgLjMuMi40bC4zLjJoLjhjLjEtLjEuMy0uMi4zLS40bC4zLS41di0uOVptMi41LTEuMXYzLjNoLS44Vjk4aC43di45Wm0tLjIgMWgtLjNsLjEtLjdjMC0uMy4yLS41LjMtLjdsLjYtLjQuNy0uMi41LjFhMS4xIDEuMSAwIDAgMSAuOC44djMuNGgtLjdWOTlsLS40LS4zaC0xYy0uMS4xLS4zLjItLjMuNGwtLjIuNHYuNlptMi44LS4zLS41LjF2LS42bC40LS42LjUtLjQuOC0uMi42LjEuNC4zLjMuNXYzLjVoLS43di0zLjRsLS40LS4zYTEuMiAxLjIgMCAwIDAtLjkgMGwtLjMuMy0uMi4zdi40Wm00LjItMS43aC4zbC4zLS4xLjItLjJ2LS41SDI5bC0uMi4zaC0uNmMwLS4yIDAtLjMuMi0uNWwuNC0uM0gzMGMuMiAwIC4zLjIuNC4zbC4yLjVjMCAuMSAwIC4zLS4yLjRsLS4zLjNoLTFWOThabTAgLjRWOThoMWwuNC40LjEuNC0uMS41YTEgMSAwIDAgMS0uNS4zSDI5YTEgMSAwIDAgMS0uNS0uMy44LjggMCAwIDEtLjEtLjVoLjZsLjEuM2guOWwuMS0uM3YtLjNsLS4zLS4yaC0uNlptNS0xLjctMi41IDYuMUgzMWwyLjQtNi4xaC42Wm0xLjQtLjN2NmgtLjd2LTZoLjdabS0uMSAzLjdIMzV2LS44bC40LS43YTEuNiAxLjYgMCAwIDEgMS4zLS42bC41LjFjLjIgMCAuMy4yLjUuM2wuMi41LjEuN3YyLjhoLS43di0zLjRjLS4yIDAtLjMtLjItLjQtLjNhMSAxIDAgMCAwLS40IDAgMSAxIDAgMCAwLTEgLjQgMS42IDEuNiAwIDAgMC0uMiAxWm00LjUtMS4zdjMuNWgtLjdWOThoLjd2LjdabTEuNC0uN3YuN2gtLjhhMSAxIDAgMCAwLS40LjJsLS4yLjR2LjRsLS4zLjEuMS0uNy4yLS42LjQtLjRhMS4xIDEuMSAwIDAgMSAxLS4xWk0xNC44IDEyMy4zdjUuN2gtLjd2LTQuOGwtMS40LjV2LS42bDItLjhoLjFabTUuNyAyLjR2Mi4xbC0uNC43Yy0uMi4yLS40LjQtLjYuNGEyIDIgMCAwIDEtLjguMmwtLjYtLjEtLjUtLjMtLjMtLjQtLjMtLjh2LTNsLjQtLjdjLjItLjIuNC0uNC42LS40bC43LS4yLjcuMWExLjQgMS40IDAgMCAxIC44LjdsLjMuN3YxWm0tLjcgMXYtMS44bC0uMi0uNWExIDEgMCAwIDAtLjItLjNsLS4zLS4yYTEgMSAwIDAgMC0uNCAwIDEgMSAwIDAgMC0uNCAwbC0uNC4zLS4yLjZ2Mi42bC4yLjVjMCAuMSAwIC4yLjIuM2wuMy4yaC44bC4zLS4zLjMtLjZ2LS44Wm0yLjUtMXYzLjNoLS44di00LjJoLjd2LjhabS0uMiAxaC0uM2wuMS0uOC4zLS42Yy4yLS4yLjMtLjQuNi0uNWwuNy0uMWguNWExLjEgMS4xIDAgMCAxIC44Ljh2My41aC0uN3YtMy4zbC0uNC0uM2gtMWwtLjMuMy0uMi41di41Wm0yLjgtLjQtLjUuMnYtLjdsLjQtLjYuNS0uNC44LS4xaC42YTEuMSAxLjEgMCAwIDEgLjcuOHYzLjVoLS43di0zLjNjLS4xLS4yLS4yLS4zLS40LS4zYTEuMiAxLjIgMCAwIDAtLjkgMGwtLjMuMi0uMi4zdi40Wm00LjItMS43aC42bC4yLS4ydi0uNWwtLjUtLjFoLS4zbC0uMi4zaC0uNmMwLS4xIDAtLjMuMi0uNCAwLS4yLjItLjMuNC0uM2wuNS0uMWguNmwuNC40LjIuNGMwIC4yIDAgLjMtLjIuNGwtLjMuMy0uNi4xSDI5di0uM1ptMCAuNXYtLjRoLjRsLjYuMS40LjMuMS40LS4xLjVhMSAxIDAgMCAxLS41LjNsLS42LjFIMjlhMSAxIDAgMCAxLS41LS40LjguOCAwIDAgMS0uMS0uNWguNmwuMS4zLjQuMWguNWwuMS0uNHYtLjNsLS4zLS4xaC0uNlptNS0xLjgtMi41IDYuMkgzMWwyLjQtNi4yaC42Wm0xLjQtLjN2NmgtLjd2LTZoLjdabS0uMSAzLjdIMzV2LS44bC40LS42YTEuNiAxLjYgMCAwIDEgMS4zLS42aC41bC41LjMuMi41LjEuN3YyLjhoLS43di0zLjNsLS40LS4zYTEgMSAwIDAgMC0uNCAwIDEgMSAwIDAgMC0xIC4zIDEuNiAxLjYgMCAwIDAtLjIgMVptNC41LTEuM3YzLjZoLS43di00LjJoLjd2LjZabTEuNC0uN3YuN2gtLjhhMSAxIDAgMCAwLS40LjNsLS4yLjN2LjRsLS4zLjIuMS0uOC4yLS41LjQtLjVhMS4xIDEuMSAwIDAgMSAxIDBaIi8+PHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjQiIHN0cm9rZS13aWR0aD0iLjMiIGQ9Ik00NiAyMGgxNDIiLz48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuNSIgc3Ryb2tlLXdpZHRoPSIuMyIgZD0iTTQ2IDQ2LjVoMTQyTTQ2IDczaDE0Mk00NiA5OS41aDE0MiIvPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii41IiBzdHJva2Utd2lkdGg9Ii40IiBkPSJNNDYgMTI2aDE0MiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Ik01MC44IDE0MS4zdjUuN2gtLjd2LTQuOGwtMS41LjV2LS42bDIuMS0uOGguMVptNC41IDB2NS43aC0uN3YtNC44bC0xLjUuNXYtLjZsMi4xLS44aC4xWm04LjcgMHY1LjdoLS44bC0yLjgtNC40djQuNGgtLjh2LTUuN2guOGwyLjggNC40di00LjRoLjhabTEgMy42YzAtLjQgMC0uNi4yLS45IDAtLjMuMi0uNS4zLS43bC42LS40Yy4zLS4yLjUtLjIuOC0uMi4zIDAgLjYgMCAuOC4yLjMgMCAuNS4yLjYuNGwuNC43LjIuOHYuMWMwIC4zIDAgLjYtLjIuOWwtLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM3YtLjlabS43IDAgLjEuNi4yLjUuNC40aDEuMWwuMy0uNC4zLS41di0xLjJjMC0uMi0uMi0uNC0uMy0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLS41LjFsLS40LjQtLjIuNXYuNVptNS4zIDEuNCAxLjItMy41aC43bC0xLjUgNC4yaC0uNWwuMS0uN1ptLTEtMy41IDEuMiAzLjYuMS42aC0uNWwtMS41LTQuMmguN1pNNzkgMTQxLjN2NS43aC0uN3YtNC44bC0xLjQuNXYtLjZsMi0uOGguMlptNiA1LjF2LjZoLTMuOHYtLjVsMS45LTIgLjUtLjcuMy0uNXYtMWwtLjQtLjNhMSAxIDAgMCAwLS42LS4yYy0uMiAwLS40IDAtLjYuMmExIDEgMCAwIDAtLjQuNGwtLjEuNmgtLjdjMC0uMyAwLS42LjItLjkuMS0uMy4zLS41LjYtLjZhMiAyIDAgMCAxIDEtLjNsMSAuMmMuMi4yLjQuMy41LjYuMi4yLjMuNC4zLjhsLS4xLjUtLjMuNWE3LjggNy44IDAgMCAxLS44IDFsLTEuNCAxLjZoMi44Wm03LjItNXY1LjZoLS43bC0yLjktNC40djQuNEg4OHYtNS43aC43bDIuOSA0LjR2LTQuNGguN1ptMSAzLjUuMi0uOWMwLS4zLjItLjUuNC0uN2wuNi0uNGMuMi0uMi41LS4yLjgtLjIuMyAwIC42IDAgLjguMi4yIDAgLjQuMi42LjRsLjQuNy4xLjh2LjFsLS4xLjktLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM2wtLjEtLjlabS44IDB2LjZsLjMuNWMwIC4yLjIuMy40LjRoMWwuNC0uNC4yLS41di0xLjJsLS4yLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41LS4xLjVabTUuMiAxLjQgMS4yLTMuNWguN2wtMS41IDQuMmgtLjVsLjEtLjdabS0xLTMuNSAxLjMgMy42di42aC0uNGwtMS42LTQuMmguOFpNMTA3LjMgMTQxLjN2NS43aC0uN3YtNC44bC0xLjUuNXYtLjZsMi4xLS44aC4xWm0zLjIgMi41aC41bC42LS4xLjQtLjQuMS0uNXYtLjVsLS40LS40aC0xLjFhLjkuOSAwIDAgMC0uNS45aC0uN2MwLS4zIDAtLjYuMi0uOGwuNi0uNmEyIDIgMCAwIDEgMS0uMmwuOC4yYy4zLjEuNS4zLjYuNi4yLjIuMy41LjMuOGwtLjEuNS0uNC41LS41LjMtLjguMWgtLjZ2LS40Wm0wIC42di0uNGgxLjZsLjUuNC4zLjV2MS4ybC0uNS41LS41LjRoLTEuNWwtLjUtLjNhMS40IDEuNCAwIDAgMS0uNi0xLjJoLjh2LjVsLjUuNGgxLjFsLjQtLjQuMS0uNi0uMS0uNmEuOS45IDAgMCAwLS41LS4zbC0uNi0uMWgtLjVabTEwLTN2NS42aC0uOGwtMi44LTQuNHY0LjRoLS44di01LjdoLjhsMi44IDQuNHYtNC40aC44Wm0xIDMuNWMwLS40IDAtLjYuMi0uOSAwLS4zLjItLjUuMy0uN2wuNi0uNGMuMy0uMi41LS4yLjgtLjIuMyAwIC42IDAgLjguMi4zIDAgLjUuMi42LjRsLjQuNy4yLjh2LjFjMCAuMyAwIC42LS4yLjlsLS40LjdhMS44IDEuOCAwIDAgMS0xLjQuNiAxLjggMS44IDAgMCAxLTEuOC0xLjN2LS45Wm0uNyAwIC4xLjYuMi41LjQuNGgxLjFsLjMtLjQuMy0uNXYtMS4yYzAtLjItLjItLjQtLjMtLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0uNS4xbC0uNC40LS4yLjV2LjVabTUuMyAxLjQgMS4yLTMuNWguN2wtMS41IDQuMmgtLjVsLjEtLjdabS0xLTMuNSAxLjIgMy42LjEuNmgtLjVsLTEuNS00LjJoLjdaTTEzNS42IDE0MS4zdjUuN2gtLjh2LTQuOGwtMS40LjV2LS42bDItLjhoLjJabTYgMy44di42aC00LjJ2LS40bDIuNi00aC42bC0uNyAxLjItMS43IDIuNmgzLjNabS0uOS0zLjh2NS43aC0uN3YtNS43aC43Wm04IDB2NS43aC0uN2wtMi45LTQuNHY0LjRoLS43di01LjdoLjdsMi45IDQuNHYtNC40aC43Wm0xIDMuNi4yLS45YzAtLjMuMi0uNS40LS43bC42LS40Yy4yLS4yLjUtLjIuOC0uMi4zIDAgLjYgMCAuOC4yLjIgMCAuNC4yLjYuNGwuNC43LjEuOHYuMWwtLjEuOS0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zbC0uMS0uOVptLjggMHYuNmwuMy41YzAgLjIuMi4zLjQuNGgxbC40LS40LjItLjV2LTEuMmwtLjItLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0uNS4xbC0uNC40LS4yLjUtLjEuNVptNS4yIDEuNCAxLjItMy41aC43bC0xLjUgNC4yaC0uNWwuMS0uN1ptLTEtMy41IDEuMyAzLjZ2LjZoLS40bC0xLjYtNC4yaC44Wk0xNjMuOCAxNDEuM3Y1LjdoLS43di00LjhsLTEuNS41di0uNmwyLjEtLjhoLjFabTMgMy0uNS0uMi4zLTIuOGgyLjl2LjdoLTIuM2wtLjIgMS41LjQtLjEuNS0uMS44LjEuNS40LjMuNmEyLjUgMi41IDAgMCAxIDAgMS41YzAgLjMtLjIuNS0uMy42LS4xLjItLjMuMy0uNi40YTIgMiAwIDAgMS0uOC4ybC0uNi0uMS0uNi0uMy0uMy0uNWEyIDIgMCAwIDEtLjItLjdoLjdsLjEuNS40LjRoMWwuMy0uMy4yLS40YTEuOCAxLjggMCAwIDAgMC0xYzAtLjIgMC0uMy0uMi0uNGExIDEgMCAwIDAtLjMtLjNoLTEuMWwtLjMuM1ptMTAuMi0zdjUuN2gtLjhsLTIuOC00LjR2NC40aC0uOHYtNS43aC44bDIuOCA0LjR2LTQuNGguOFptMSAzLjZjMC0uNCAwLS42LjItLjkgMC0uMy4yLS41LjMtLjdsLjYtLjRjLjMtLjIuNS0uMi44LS4yLjMgMCAuNiAwIC44LjIuMyAwIC41LjIuNi40bC40LjcuMi44di4xYzAgLjMgMCAuNi0uMi45bC0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zdi0uOVptLjcgMCAuMS42LjIuNS40LjRoMS4xbC4zLS40LjMtLjV2LTEuMmMwLS4yLS4yLS40LS4zLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41di41Wm01LjMgMS40IDEuMi0zLjVoLjdsLTEuNSA0LjJoLS41bC4xLS43Wm0tMS0zLjUgMS4yIDMuNi4xLjZoLS41bC0xLjUtNC4yaC43WiIvPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iZCIgeDE9IjExMy42IiB4Mj0iMTEzLjYiIHkxPSIxNTEuNiIgeTI9Ii0yMy4zIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIuMSIgc3RvcC1jb2xvcj0iIzQzNkFERiIvPjxzdG9wIG9mZnNldD0iLjIiIHN0b3AtY29sb3I9IiM1M0JEMkUiLz48c3RvcCBvZmZzZXQ9Ii4zIiBzdG9wLWNvbG9yPSIjNTNCRDJFIi8+PHN0b3Agb2Zmc2V0PSIuMyIgc3RvcC1jb2xvcj0iI0ZDOEYzQyIvPjxzdG9wIG9mZnNldD0iLjUiIHN0b3AtY29sb3I9IiNGQzhGM0MiLz48c3RvcCBvZmZzZXQ9Ii41IiBzdG9wLWNvbG9yPSIjRDMzRTU3Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImUiIHgxPSIxMTMuMiIgeDI9IjExMi4zIiB5MT0iMTM0LjciIHkyPSItMjMuNCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iLjEiIHN0b3AtY29sb3I9IiMzMDVBRDciLz48c3RvcCBvZmZzZXQ9Ii4xIiBzdG9wLWNvbG9yPSIjM0ZBNzFBIi8+PHN0b3Agb2Zmc2V0PSIuMiIgc3RvcC1jb2xvcj0iIzNGQTcxQSIvPjxzdG9wIG9mZnNldD0iLjIiIHN0b3AtY29sb3I9IiNGMzY5MDAiLz48c3RvcCBvZmZzZXQ9Ii40IiBzdG9wLWNvbG9yPSIjRjM2OTAwIi8+PHN0b3Agb2Zmc2V0PSIuNCIgc3RvcC1jb2xvcj0iI0Q4MTgzOCIvPjwvbGluZWFyR3JhZGllbnQ+PGNsaXBQYXRoIGlkPSJjIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNNDYgMTFoMTQydjEyNkg0NnoiLz48L2NsaXBQYXRoPjxwYXR0ZXJuIGlkPSJhIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBwYXR0ZXJuQ29udGVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCI+PHVzZSB4bGluazpocmVmPSIjZiIgdHJhbnNmb3JtPSJtYXRyaXgoLjAwNTAzMTQ1IDAgMCAuMDA2Mjg5MzEgMCAwKSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgeD0iMCIgeT0iMCIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMyIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMDc3XzI4MDc3Ii8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMDc3XzI4MDc3IiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PGltYWdlIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3QkRBQUlDQWdJQ0FRSUNBZ0lEQWdJREF3WUVBd01EQXdjRkJRUUdDQWNKQ0FnSENBZ0pDZzBMQ1FvTUNnZ0lDdzhMREEwT0RnOE9DUXNRRVJBT0VRME9EZzcvMndCREFRSURBd01EQXdjRUJBY09DUWdKRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNy93Z0FSQ0FDZkFNY0RBUkVBQWhFQkF4RUIvOFFBSFFBQUFnTUFBd0VCQUFBQUFBQUFBQUFBQmdjRUJRZ0NBd2tCQVAvRUFCc0JBQUlEQVFFQkFBQUFBQUFBQUFBQUFBRUNBQU1FQlFZSC85b0FEQU1CQUFJUUF4QUFBQUJPb1NVd3BKWlJodXlrOEU5aDF5ZGJENUpteXRzWDFoeHFYUFV4QVlIUUxBTHNpMUhuYlRsK001eldQTG9hWXN3ZFhZQktwZGFiRWlFWlp3YzVMNHcrSU9tbDZaMlFCS3JtYlBjVkNWQ3dnWmRQTlduNGRLV1VUekV2R3I2eVBpN1JUdDU4b1R0aFdPYldDbmpYOFdTUnhNdTREaVNVcG9BQUpXQjYyYkxqYUYxT1FYcjBOVXlFejI2QjAwS3VwODhCdENXMEN3djBJdHZuSkV0ZzdkZXljWUJJMHlJeDdhb2NGZ0pmQ0sxSEFrYTBRMGV2UHBOWVpzY3VSZDJxRXJUWWQzMHI1WW80K0pGYlZrZmNpTmp5dG9GYkZPc1I0RFl6NkNkMjFKSHRjWVoyNU00WmI5dmNIdU90b3BLSFUxMUx1UnF3aFRzbWhUVU81NzJEWlhGUms5VFpFaHRHZlJ4YkN0TFVMalVOelg3MTFTTEUxMFlKOXY0N09Hdk1vdVowTk1PcmI2M0o5SGZDZXpCK04xODhhY3NOYkRpdTJVc0psQk5XYnBnTUtiMlFIRXJvMnJiRzg5cVVqQTdwM2FOSGFNcUoxOHp6VjlsNVRFbURwSEhUNWdGazBOTkRIN25HdUtML0FGYitWL1NMMWp6TWoyVFBOQzFWVWVTV0tnSWxhWEliQWROREJqcVJtekNzRlF6Qjl4NU9VMTNYcHkrVS9XNG9WeitqYnN1cFBWK1BJTE0rZCtiMTEvUnEyZXFhVjhkNnM1ODc2R3B6WFdMR2U5RS9QZDEzWjEwdGw4N2lDb2RyWXpLbnlCMStaMzVOUGI5TThLeE9SM21GdTUzam4wT1ZjM1ZXdGxMbTYzQzVBaTlHaEZjN3N0UFRpUDhBa2IyVDgwK2p5ODdBS3huNmEzV3pLSzJvZnJiT1B2dkQ4TitHMzhkN0p6K0c5bmhMNmo4dk91cnp0dmRMTzJmRmV2Sk8vd0FyeUIyYzBLeTZyZlJtMVI2RHpkaEZWL042aVg1ZlljSFU1Vjk1ajBHai9BKzJDMEFteGVWaUZOWnZ5RjdiS25yY3VsTWk1ZFRHNVc5T1VwTTdtSmtmVnZtNno1UFNhRXZ5ZHE1K1orZjBJcU0wK2h6NDJIWHcwVjFtZlJzL3gzcUFEaDlqVE1DM1pHQ3dxcXowVTI5N1Q2cmdOdFBGNjY0dGZwYVVJeUE2R1RSL3UvSGdXbWtkd2JadGk1TzI4bXZKRE9UMUxIUG9NZXZ5M3p4dWljK0o5WGRzN2l1ci9aNWMzTm5hZzlDU3pzbDZHRTJVNVpHZlpOQk80bFczbnJuYmVYVHBWemFjMVphS1ZJczlXUHQ5ZjVld1N4aVZYYTg1L1Fvdk1kY0tWaHRwYVBYQlVqSmVzclpsUFNRTkdCS0Z3MWlQaDdTU1NoN2N6Z0N5dWt4aWw2UXowRzFaUmxWaFBZaFJZbGlCdmk5c2FaRlVZdVVRcnpqQnB3QTN0YnByTEFhcTNlTHBSV01lRHFNdWw4WTBLV09GYkhGbC9jWitVUHVrSzRPcEpDMHF4R0hwMXBtTGNpbE91ZWFXVTE4R2xubGl0czFWTWpXT3htcFpUWWtxbXRsYlcxbzB0eWIwUnI3bVV0Y2tabEY0amtTS0V0WU5KaFBvN3BtSzZWeGZJSDBzSm1NRnkzQWJoSXdDb2UwZXR0VWF0cEtXR3pVbzRORlVyNFBxdmJhTDFWZktsUHdzb3dGaEJyV2pGTjF1L252bWlVZ3ZJSU5sYTZXM1lCck1vMXBBY3lCNnNwSVUxWGErbm9WNFlKa0hWZTcwQ1NyU0JIdUUyNDFTNEpWaWxGR3pRbGtSS1ZQbGhvQzJXVVVuRVh0VHArczVxVmtjU253ZGw2UjJLQTJncmxHQ0FiUVFsMlZ0S0J2UlhWRnFBYzJYQjNzanZjdWlLWVV2UUtFL1ZZaUs3TTNpWjZWdnpCVEFGRFNXUnBlNHY4akFOQlI2dGRvMGtIVXZUeXRYUm4wTWFtZVQyVjJPM05mU1ZNR0pGbWpad3F2VlVDdGswY2FjYnM0K0Rka0FrQlkwdFRIUzgwNUQ1elZFVmtqcVl4bi94QUFzRUFBQ0F3QUNBZ0lDQVFNREJRQUFBQUFEQkFFQ0JRQUdFUk1TRlJRaEppSWtKUWNXTmlNbk5VRkMvOW9BQ0FFQkFBRUZBb3J3ZHlVNUR4UjhHK0tlRE5TL0k4Y2l2SnB6eCtvcnpWVzFaWktJZ1RvTEdIRjNtam1qTW9uRit4aUR3S1doc2FpT0FnbnlzUkhPNnovMjd5Zk1kWDFQRmozaVB3N0twcmE5aGRmcmFLYytIQ0QvQUtmWHo0endaejA0TnduQnNmTG55OHhYemEyelFrWVg1RERITEpCVXRUYWJOSU9xTW51cW11Z0pEOTlnOGNyV1o1dm9qMCtzajFVazBEdFBOTkZqeG5KVGVkOHpxL3FyWGtVNGZ4WGtWOHg4T1RYZzY4SDhZaWIyNVptb3lPc2lwUWIraTdYUDZvcjdBcmhYWDMreFh4K3hXOFJHWmFuNWxwOGM4YXIzTzBKV3p1bUl0Wnl1SXc4RnZSUEg5Z3N1dmZXS2JLQldvS1R6OFkzeGR4Uy9OUDIrcUJ4TVdCYmd3WDVXbE9ONkFRVnBYWmVvdmlLZ1o2NzV0Z0NzV25LTm5pT3pCQzJ5cmRBaDg0dWQvdUlqQ3hOUURPaGRQYXkyOUhHSFRVeVQ1bWdxK1ZuOXAvVTU2UTVmeEtxQUg3ekJIOFNlV1lxTXhmZGVBVVYrOXpLcWY3cnpQT1NDdC84QVVJR2VPTzVmSGg0OWRzQU1oNnJIaUt5NHZYajh3NGVxaDU1bGpFSnk4QSswSzJwUlMyMWxqSjlxY3R2OHJaaHlQN0dNaGV2SUZRRUNvQ3lYMEJ2cTZaWmx4bUtZYzdYWmJuUG9odGEwVEUyeENlcTZlMjNtWFUzTTk3STBiVkxtZlpNMVR1eXY3UWx0ZTdWSHF2R01WT2ZWWWlLb1BSTUo0d09WMUV3MEwyTS9sVnc3ZW5vK2Z4b3lER0piSVdqaSthWks1ZjEweGh3TUk5bjJDM245OFZKU25IUVdXYnlTZWxaMmZCc3Q0aVRzQy9NVUZtSzBxWk5BWmpMOWNMWnBvS1p5bjFwcytJdndqUE5OdnkwL0lyMnRNZmIvQUFTcG9mWWFkWm1vdXZtbXkrQWtiZkdTNERBMm4zK0VvWDFPazkydEh6cFNnNWt3eERDS2IvRWVncjZ5eDVtY3pZZEZuWDBsU2dTdk51U3NFbE1WcFEvYWhOZmk5azF4MGtldGJ4MTNBUFlSenRNRkMyZjRmNmpGYlVzMXFIaXZLZGsvYTdsdmE0L1djL3I2L3dBZXN1aXRYTUlrV3d2eFA3MUJldEc1cldabUl0VnlLL1YrQXhLbnhpUGI2akMwWXk1QzIrNHdYTzFWT3pLdnRHN0Z1TVFQamZZODdUQm5hV2VtK1V2dGUvSWo0VXV5WjdTRzU2VUR0TXRYOFg1ZEFsMDhNZ2wrc01hRkNyTlU5ZWo3TFNNTEVBYytYdUJiOTBmTkZGUC9BR25QaGNEWXBicFZSeFliblgxMkNNZGYvSnlHK3VTOGQzSWFUR2Zxa0xscDFWZ2wwK3EyZ1diMU1WbkY4OEs5WEJzeXJrNTYycmJFVms1MzRwSVhaQzE2N2Mzay93Q3Y1ZnYvQU9ybEpub1JwT2tyZWZZT3RmbmNTc0NUS3FVa1h0NlVXWmk0Z09Jdlpyay9MczFYRW1PYUVkZE1DRnV2RUdFR0xUcm9Vc0lxS21YN3dWNjVyVWltSjJLR0owdFg4cU92UE9hdXJoNktkeDZiNGdGYkV5QjFHNExwQ2oyRnY3WEtWK1VDL2NvNTF4REFkaXVvSXNpNldENVc0ZlA4YjVmcEJJMHQ1MTlBdEU5ck05VHBqSHpRSHRWUWlvVDEvRnliWkMxeGk2ZzJPK2QxMk40dnc0NW82bVppUHV1TWRZL2M5VEVJbzB5UGxvbFNpcm1VTHJMdDVIMUxTbVE5VlRReE5DdFM2Y29WcHkxVnFKQzFhL1pzYko2OWpSc0tXQjJMSGFORktHTlJZTUtkdnpXNHE3cHdDcHZ3bFlLdXV2WXQwbUEzb1BZckJHdXhDblhkSzVGdG1TS1N1VmxlbWF2VE1MbTlXRUVOZXZVenAzMkswejlIVVozWC93RGloZ0FkN0UzbUlmUUR1blJCeGtrUEtRMDFvTDBxTndkSW5SWXNmN01GRFUyQk4vamFyRGcyR0pPSXRLa3NLOUQyK0lMeDRyL1hEZW1aa3F6RVY2V3ZjbCtwbzV0S3F0TVNObUdyOFhOY1FzV3Y4a2Z0L0VQUGpzQm1SWlhXeVRZdlVjcWFMOW1BNlNuYjZIa3V1T1BMVGlaYkJ6L2xYYVRyRjQ3T2xWUkpycm9WdWVwa0UwY2JwV2o0NjgreVR0WEo2NG01RG1PdWtVUUFBU0xOejhGbWpyaUd5VkJYblBqMFpLdjR1dTUrK25lZkhhZDNYL083RW5md21LZjhtb1grUUNtdjJpL216Sk5Na0RUOTMzZ3JNQ0JyTVRyQVhjbHFzaFc5UHdFTFJJT25DVmllWTJ4S1pHWS9LWWhaR1JnU1ErRjh4YWEzeWs0aktLbzQwTzZOTjF1MzhQYmtuM2RlcVh2ZjZkMm11NmlVSUVrbXk2dWQxYll2YjZsWklRVXJmUDhBRlBIQ215RXVNOW5wRlUzMkw2ZnptUWttWlBkMHZtV1NUeUdiVnQ3eVR6NVdtdU5lME0zaXNBdVlYbHBmTVo1K0NzRzlkSzkrdUZFOW82bWYxemM5djBQcTVWTEZwUHZPS3A1QmVXTnpOU3E1MjZmRmROM1ROVzl2eis2VC9sVTgrbjBoemVHek0vOEFSVWErR2RWdnp3ZFl0Y1NuczRETXJ4WkdvdWFMVWpEUWhDbVc2cHN2Y0YwdFVkd3BZS2RQc3JCcE9zYUpMb0tnNDEydFVmR3UxTkU0YlRZWk92WWRTc1Q1Sm5UNDRPUGxzOTBKL0tFLytGNkxaSjNTdXRmTVZ2Q2xiLzFDRE5lQnZmeW5tNzcvQUMvVXlDR0RQNnVDd25EaG9ZNUM4c3lNZFdOaW9xc2RucjhpYW1teFUvc21pcWlOa1REL0FCTndGZm0vKzY4TFBrcVg2U1IvODUyKzN5N2FIOWRPYkFUN2FVaTJMZXNqdEU4Ly84UUFTaEFBQWdFREFRUUZCd2dIQmdRSEFBQUFBUUlEQUFRUkVoTWhJakVGRkVGUllTTXlRbEp4Z2FFUUpHS1JzY0hSOEJVelkzS0NrcklHYzNTaXd1RWdORlBTUTFSa2c0U1Q4Zi9hQUFnQkFRQUdQd0t1Qnl0QWxRL0VCM1Z4QXBYQ3l0NzY3djhBaGMya1hrajZhSExmVldpUlNyOXVxbHU1V0ZwRjZMeWR2c0hiU0xCdEdrRzVKWnVPWCtCT1NVYm5wUzZGbG5lZFRhNTMvQ3VyOUFXR0hQOEE0ekRYSWFicnQyVmJQSHEza1VDSXR0SjY4bnlYbjd5L2JYUnVQL0t4L3dCSXF4T04rMTUrNmgzZFgvR3J5ZTA2UWRCY1lNalBHQ2ZjQnlvbWVhNWtZODIyZVBsWDk5ZnQrWGhrTmNTZyt6ZFhiNzY1VmdWSkhIYzlWbWJHSmM0MERPODFEREhyNlJuWHpacDQvc1h0OXByclBUVjlvbDlRSFZLZisyamEvd0JuT2pqQ0R6bHhsejc2Mi9TOTJXYzgwVTVQMTFvdElWZzd5UE9QdnE5UDdmOEFINU53elUxbDF0TGZVd3krTldNVmIya0h6cDRvMVFIR2M0R09RL0dyVHJFTFFRYlRnQjNkbmRRL3czNDEweEdzQ1N4MnNxakhwWUlwQkJjeHA2eW1IYUg2dmxRYVMzRm5kNGZLUGw0Ujc2MGI1Wmp5alFaTkg5SjNBZ1RkODJpT3FRKzA5bEdEb1MwWG82M080eW56ajdUVzI2U25hOG05WDBhMmR2RXNVZmNneFZwYTlXRThVcWFqdndSdnJVZHdxK2wxWVV6N2pRQTVuOG1qczUwZ3RmUmJHYys2cDcwWFV0eGRCZ0F6OGhud3EwekxESE1ZRUxoZk9KMCtHK3JWSWxmZ2t5UzY0N0RYL3dBWDhhNlZGeFBzNVVsRzJNL3BId3g1MUZWbWtkdjJhQlB0cmduUnZmaXNxbXYyR2trRjRseXpMbGhwSzZQQ3RFbzRoMjV6bjVOd3pYTFRUSFZrTHo4S0ltbDJBN0kwL1dOK0ZhYlNBZEZXamMzZnoyKyttMm1ibVpXM3ZKKzdubFR2bm5PMWNNaEh5ZEZTeko1YnJJajFqbnA3cTAza0xTdzkxWHpUUkQ5SDlZWWFjZG5aVXEyeXVzUXRKZE9zSDFhaWp0YmJDaEFOV2o3emdWTGIzdlNVTnJuZU5jaGY0Q2gxaTNicEt3UWpKaUpUY1BFZmZVWFZlalJZSXI4WENkUjNIdDdhQXpqNXIrTlR6Mzk5MWhPYUlFeHA5NUl6V2JZbTJYTzRyei95Z1ZzNHQ3L1MzQWQ1UGhVM3pqYUx0RHMyODNkWEJJeEhqdnJTMEViL0FNR0thU1JkR2xjNFUwSlhrZUQ2TXk2VFc3V3c4RnI5SUhwQ0lXMGs1Y283Rk4zZFhTTjIzbE1IRWJIeDdSOGx4S2VXMC8wVkNHR0NTVFdTY0R4bzhldjkydWpoalJpNUJUdDFIdS9KcjV6ZFIyeS90Smd2d0crcEVPdHRVaE9ZamdtbkVFUWgrWlM2dkthaWZiU0NlZVJsVlFNWjBMUkVOdXYyazE4MDZPbGsvd0RhL0hOV3pYOXE5dkZyOG5xYndyR2RQelViejJjNm02dzB2U09Ud1lqTWFnZUpPTTFwUTlIMlE5Z2tiNzZlQ3l1STU4NWE1a1h0eDZQc0h4cVM0MnNlbVBjUnZwSldkZEQ4dExVaGppWHE0enRwNVd4bzlnN2FFVnYrb1YrSGRscEQ0VUx5QXNZMzg4SmpLTjJnc2VRcjBYOWhra3FSakpzUmovcXRGbitjRkQ3RFV0eXJlU01tTmtVd3YxZG51M1V0d0FpdmtCby9TRlNHM1F5QjNiUU00OUNrdFJDWTNYQ2dScnE3TzJ1TXpUbjFwSEVTajY4bXNXeXhzMy9wcmN6Ti9NOWRIUnppY0dlZlF1M24zSDNMeXFLR0cwV1dhUW5SMWFQUnVIYmswVzJzZHZMbm5OeXFWcEx6cld1RmtHeHR6dzVyTWxvMDdEMHJtNVVmQVpyRnRCYlJEOW5BWCtKeFdtT2RnUEJsVCtrR296S3AwZzdtWXNjN3ZwVTZqYzNWQlhsK2xJUWY1ejk5Zjg2OC93RGxxVjVMYTJpVm9pTmNDYWMvVWF2ZkdYOEtnVFJ0Skk4N3V5akMwbkNCbVRIWUtjeWVUWUtOc1Y5QUhsR3ZqVWx0Y292VmlNU3A2TVBjUEZxTVVyNTlVdkpnTjdGV3BzU2JKbUdCaVdXUFA4MlZQc05SMjY3aEVPemR4SDdQc05BbytoVHV5T3lyU1BhN1hhRmpyUEZuZFRKUGNQa2VjaGwwQWU0VkNrTnZFMjFpYlJnYm0zam1hZEpZeEE0N1ZKUS9DaENpejNFcU5yaEROcWFQM25sU0EyMmpXTXJ0SlMyNzNWSEZCY0cyZlRuV0JtbmE3dUxsMEVET3J5Y2lSV0ErMVBobHZzRmNIUlVqK3kySisydkpkRnp4anhDcFNHOVRaNytFYlhWVXFnNFBWMUh3ck0xKzJQQmY5NnZMYVpwWm9vTUFIWHB6dThLZG9rMEIxMGtGdFZYTUt1dlVrYnpWWGVXcDJac1lGTTc3MDJqTzM3c2YrOUszT1JGMm50bGs1ZlZTUlJjVEI5bkRuMXZUa05Rb3gxdW93SGZuUnkyRjdhMnljY2NoL0k5aHI3L3o3dmpWcWtVMmhVempoMzcrZFhBRThzdHhKSDZjZVB2cXdWbzlJamo0U1Q1MlRuN3F2TDErY0xxRkZYcmJZUXlTMnlHSXNvMzQ1MVlvSTQ1Qk5DQWRvbWNjUk5NMy9UaTE3dmJqNzY2TS91RDk5U3hxUUY2dVd3QnlOZEdscG5Qa0pDNTFjK2RLa2s1amdCNCtQaHhVSWdrV1lpYnpsYnM3QlU1enloWDdLY2FuYkRZSFlhdXArc0pidEsrZU9URFVCYjc1SkpBZzFISE9veGxES3pzejkvUC9BR3FjNTlDcE9KRkp0bkc5dTk2Sk1zZU9zeG56dXdMVURiWkpHRVQ3bDd5MjgwQ1JuSEtzRVpGUzZ0d0F6VGNSM1o3UGJRQzh0WnBXNTltODRwSExpNTJ3TWdHdkd6OFBqVzI2UHZCYWxwQURFT01IeE5SWWxpMnczeHRHNDAxMFdsemRkWUV6YTRNTm5QNXhXOFN2bU1CUkVPWnpuZjRWWVdscHRUTnNPUlQyMWRDVzVXTEZ0amYzN3MxSTZTYW8yUENRZDFZVjFFMmM2czhkUWhtZWR6S09jdFhjaXd1bUl1MmJQSVZvMGE4TG5oRkRWMmVORko3eVNRWnl1TjJLMWxCcldVb3pIT2U4VThmZU1VWVc1RnBJVC9IdkZhd1BLTkdqZ2ZUajVqNnFXVmQ2UnNYR08ySi93Tkk4TW5DZCtSMjBSbkZDM3pxWStkN1A5NjNuOC9uTlJrbkJKelE2MUd4aDdveHFOZGVjSHFxamlaNHdwOWdBcHhKMW0yaWs3WUgzKzNmUjBYTit5QTgzbTUvNWFnRWIzUWx0bzh3TVpjNDBqY09YS292MG03d3h1UFFtS2QvaFRSMnlFeXRqeW9rNDkzWmtMWGwwMDdzRlpMakdkL1B6YXdKdUgxUmY3aC9scklaOCtGK1ArMmt2T2pCS2RsSU5xelRod0I3aFVxTjZRcDF0NFhpa0M3eWt4VTRvbFl3RjlVdFRSUEd3YjNVYk5WY0pMdTU1SGZXK3RzTnlzTU1lN3VORmo1SnRlVCt5ay9BMXdneGxUbkE1d243MU5SUlFjRXJjYjRqMUtQRHdyR3NEKzdnUDMwc3czbmsyTisvN3pRVURuM2ZuM2ZYVUEyTUU1VWJ5MFdyNDFiS0dqMHhEaUdQTzMxYkoxWkxwTnIrcGR5aW5kM2pmUmRmN1A5SDIwbU1OTWs3RnNlK3ZtMzltclcyazA0NjRrM20rNGlwN2cyaVNqUW9nY1NhTkdQRHR6dkZiRzc2Tldma2NiVEduZnZ4VVMybHJQWXVKUVc4b3VOUDhOWENTeEFzZFJTVlRrK0ZYSzNFQ25wRVJwc09EY1c5TE5kRk5jQkxlYVM1bVc4d1NvVlZHVng4S3VMVzA2UXRiU1ZmMXFUeTZBOUtZci9vNTNIZGZDbW1IVXBISXh3M2FmalR4ZFViVXZibzNmWFVqaU9XTFBvcW00VXBMU1pISTdMRkJMbERLcTdnZVZiTmdOL1lhTWliNDhZeTNMSHF0K05HYVZTSW9SbkRjeDRBOW9wNVplRjJQcFJzcCtzVnU0LzRXZityZFJVOFN0dU8vUDU5Z3JyRXczK2pudHFKTlpSR0oxWTdhdEpNRFdKV2J6Yzk5V0dpTkxpWmlXRVV3d2gzYjZoczhDWHlEU09vYkFJQzU3S2dnNkl6cVdFbWNoaVZ6NFpxT0daMzF5cmxGOEIyMXNPWUcvREhuVVVVdGd5QXVEdFcxY1c4REZYZG95Unh1Q1ZEdHpIeHFLS0tWWkRyVXN5K2R1OGMxMGVzVDJza3RxMHphSkRqVnJHUGhVNTZidEliZ011RTRSSjIxcWg2TTRSNmx1MlBoU0VXbUxZd05yMXh1QUc3S3RGRVdEc1NaQ3d4dkxidmhWNWNpWFkzcXg2d05qMmV3MXE2VDZTaDYrVlZrd3VNZCs2cnFXU1FUU2dyb1lER001clh3a3JqYU5qVjJMVnd3SU9BdUZQS2d1c1dpdTJjRk9FMTgxak1vNzRTMlBoU2JlM2VKU3dHcVVISHhxTzdrWTNGejFoVStpQmpOSkNZMmRmVmpPRFJaWXhBY2JqSk9LanQ1YjYyamhUc0VtU2FqL1IwN2VRWFRHeUFsbXp6TzRWMXE2MnBrV0U1THFWSnBqRExyU1FFNmZVOEtROGsydVBhS25uZE14eHY1NE8rdXI2dFg2dGdBZHc3ZnJycGExY0FoN2dNZ08vczMrNm9IU05VWXRodEs0elJkN2RDV0g1TkJQS0lEeTB6TUttMmR4ZG1JZnF5a3dQd3JVblNVMmc3MUJIRmp4RmNNcTNRK05YUVZHbDF4S0YwS3g1SHhBclFlaTVCNEhIMzFjd0xGMWFPVncycDNCeGoyVmNSVDlKamo4NWtnODNsM253cDQ3anBlNGsxNDh6UU9WUXd3MnQ3Y3h4dWRKYVVmSGRRanNMVVdTYXNjUnExRjFKR1lETHlIT29mOFlQNmFGdlBxTVdDVHBiU2R3cThNTnRpWlFjT1dMRmF0WndpSXVON2xQRWluME9SdjlFMHNXbGhMczhNRHp4aWdxZ0x3bmxXMWFSdFFtQ3F1ZDJLdXRuZWlKVEljeHVOMVdNa2oyN2duQWFIbWNldFYzSVVrNDJJSjA4SjMxQ1ZZSFMyL0ZhUnUwajY2WVI4SVBPamxtVmZDbDhxV3lOekVWcDFCeUt1TmhabUdKaGlMRXJFcjRuTlNSeW5YZUxMSHhZNGp4Tm43cXZVWlR0TlEwOS9iUmRIdFpFY2gzang1TWN0M2IzVklrSnR4R0R1YUMxUlI5bEdYYnM0MTQ4M25ROG93S094VGQzMWJTYjg3VHQ5bFcvOEFpLzhBVFdWOVJ2c3FXZWNCbjlYMWllUzFDcUx4T0JoVjhYYW9ET3UxRWVjNXE4bFE3RnRQdzAwR0lDN2p1RlF2bm5jZ1k5OWRiWHpaSmlvSGZWdUc1Z2svQTB2Wi93RHRkRVB1ek1YejlRb1NxV3k2aHR6WXhYQk00L2ZVTUtPcXp0TGdkdkNVUHdyeWxoY3crTVVnY1ZoYjNxNTdwb1NLbUhXSlU0Y2VZTjFiTVN5djRtdGFhZ2Mra2EyTVdjZXFncUpMaUpkUUhGcTUxcWkyYlJaempWeENnK1ZTTThtWnNDcllBSFo1WWhzNUI1MVkrTnczMlYzOEpvMjhaemJXeU5qNlRkcHEwR2NZUWY2cVpnZStwdFhKdWREUjV1ek5XMG1rNEY0QnE5OUpBRjNSTStEN1RTUEp2WFMrQ1AzVFFhV0xZNTV2SVF0V2tjYU5JMXRxMGJJSEJ6M2swVXVZakM2WVU3ODFoZ0RSZ1Btc01yVzZ0OVNlUjJ3YnZmRkYzbFNJZnpVRm11bWRlNFlGZVFaLy9zcmlqMWUxalc2Q2owWGRNTE4xa09rU2VqUVcybGQ5SGF5WUIzVjBYNHl0OWxQczIwblNSVXM5bk1NYU1HT1E3eFNBVytJc1lEQTd0eTF0R1lZMVl3S1owdDVHVThqcG96U1FpM2gwK2RJY1VJbTZTRTdDZmE0aFRVZFZaZ3NTVDY5eTJQaFhscjRRL1J0MXg4YUxTRlhmMXBXMUdzVzBKWWVpY1lGVFBLTk8xUElWdWJmV3M3bVhrY1Y2M3VybDhLT0dybWFHVFdDZDFhczF6b21ZSVg3RzdhK2JUSkRLdVdpVjVQUHF4UjJNaXd1MnZRb09EdTNaRmZNTEtSVko5SWNSOEtCRnUwSko3ZVZBOUlkS3d3ZlJUaWF0U1dNL1NMK3RNZEMxaUJMYm81UDJNZVcrczFxdTU1THB2MnIwUUhSZkJLT3dqL2ljNHFWV3V6RWlvVzRCVy9mdjdkOWRDeDdnRnMrejIxSGRla1hwMThhY2VIZlEzbjVQSDVjNHJTc2lnKzJ0S3MwckhzUVpvTjFSb1VQYk0ybWthK3Y0SW5VZWh4TldtSzNtdlQ5STRGWXQ3YUMwWHdHVFIxWEJORThDZU5ZV1RhbnVTaUlVRVE3Mk5Nc3QwOG5nTndxeGNxT0tZNi9FWXIzMWNuOWtmdXBSM3lDckFlcmFqK28xWmVMbXBvTGRObytydXBvcGNIM1lwUjhtYUdoTTE4MldPTmZXMUN0ZlRIVGpvdmFrS2sxNUd3bTZUazlhNGt3SzAyZHJhOUhwK3lqMzE4NHVwWlBEVmdWd0pYRVNQWUtJaFZuUGpXNWhFdEY1WldsYlBhYTA5WEdvamVhdXJkenF4dlU5NHE0S2pBV1BKK3VyRWZTWS9ENUxvL1IrK29QNzBmYlVIK0hYN1RYUm9BOVkxTmNMNlRta2tjWUREaHJRZWZ5Zi84UUFKaEFCQUFJQ0FRUUNBZ01CQVFBQUFBQUFBUUFSSVRGQlVXRnhrWUdoc2NIUjRmQVE4Zi9hQUFnQkFRQUJQeUZ5ZlZrektWNUhVMUNNbjRzai9wY29xNTlvQ1libktFTThzWmVzelUvNFVSMExZd1dkVm5HUDJsNGlLNDRQOEJ2Y2IreUNYTE1ZODhuU0hJY21TMXhVd1pmYytvSlgwVGdQK0o2c0Nhczl1SmxUU3phN3dxODdRUjRnenlyR2VmZFFyOGY4enN1Rk9DNGxYRW9TcjBja29Iblpkc2VobGVDL1VOUXVaNlNCaUEyZlhlNWMxWE5jOVRQOGNSdTl6Vjdib094TUIxQTgrOFpaMWQrMk5Qekt0QndQMjdNRFhZSkRyZHg1c3B1alVkRm42dXM2eDdTWHQ4YWpyYU9PcVp5TGFaVy9HdnhLaC91WU5xQjVXT0Y1SytZY21PZjZKZk03UC9GWjhJN1FyV1pRMHdwRE5KaElMN0hZYzdvS2J6RWY4UzRtMm9WTHJnMmpsU3RxOHJtVTc5bHlQYmJENU5xcW1hQ2J6UXh4TWcxbHE4UURCYkljTi96R3FydWZBRnFjbmVtVkhibDVpaVJkNTExT0kvUTFnV3p1UUdtekpkNmh6OVJxaHZUM0ZlUzlZS1hUaTZ4SWZ4aXYyWjZSbXo3aWEwV0tuOFFGQ012Ym9ObXg0S1ArSlhYcnhKbWp2QkNEWWplSFdZM0Z5ZWxmRi9jZWN0V0QxNnZxWU9KM3J0UUxSSzNQeFBTM2VKbFZQY3hBK3FYZ3Z0YlMwWUdyaTc1Nms1eXZjQ2pTdlVvaHJyQWFiM3FKNUxRM1Iva0dXQlFIbERoTUQweHVEQzU4V3E5ZEV6dGR0Yzh6azhpZ3VETGRXYWp3dm5Vc1JMRjNVZWFIMndhclpkS0pzY0RsakFPN00rUHVIaGtZZnVOdHY1UHFJc3VPM2RGMFh6SzFOMFg4czArNkt6SldsMlYwcnc1cVpCb2lDRzMwYmpoTlNLejA1OXRKMllsZGRVUXZnZE5udlgzREIxWjRlQlJVUFcwZGZhdjVTbDg0OHpoWEJOSUViTW0xVXdSRWhpUGkvY1c1cmtQb0QrWVUvUTNFOXdiNkJVUnR2ZUxvOUVkVlNxK3doVEl4T1d3dFpjNGp5MjllNHVIcmx4cmZLbVA1bWVrY1ZudEZueDdna3FPWmVPMFBZNlJMaWkyOUp3MmhZaHNQNWRFVTZkZjB4RVZNV3Y3ajdLSndoalBqUHJqNW1KaUNadXQ1bmtvcEtteFNYbkIwM042YWRGd1B1WU9xQllGTFJmVGlDRmIzNUJQNkV5ZkdxK3VSS2dKazFLMGxqNGJqUUlEMFJlNXE0VVdsUUw0M2MzS1IwZEdLS3I1aFhPSUF2bVgxS0hGMTc2Y3ZlaWwzOE0rNWRydFFrcmxmb21UZEMzVk5NNi9sdGdIelM2V1AxQXNNeDI5N1Q2aXVHNlB1Q3VnWkhaYno0cUkzRTlMNG83cmdtRjU1UWZjN21ZMkZLSDh4NW5kNmZEMTQ4VStEMTN0QVJvbGRnRXBrY2NxNDdFWCtTd2pseUQ3T2xKR05kRkc5Uzg3aVF1ZWdIZW1QdENVSHpaaWJHdWZCRnBTWUljbXNiMVZvN1NoWnBOcXJKMzN4THNud3RuT0poOVpUd2hkNDMzeEJPMEF2cWhidWxzZFpicmszOFRjeTNqMDN6aVdKZ3IwdUxXS0tUK1NMNldGWWtPVVE1OTRxaGQ4eXRObjdqNWVuYUpPWFdEN3pVR0NjUzFySG5WZndsZEM2Wmp2MUNhSVhXb3kzeTVsOXhHMnBtWll3dWx6bDZPakJnTnYyMW43Y1dKbHBJQmhlNnFzUk9TTThTdWJkSXczc0ZvcVE0SVExejUzZmFaRGNJQmFoM3pjRFJ4N1RZTDB4L1c0VnF6aStJdGxhdytmN3d6S3I0UDhBREdiVGgrNDB1dWhXNHlCeHlnQVpTTHh2Ykdud2ErWlpGTjAzaTBER0s2azc2akt0ZmJGVnFFc0NiRjBNMHpyVERIZVp1T041YWlUcEZkMlAxTDBHNjNZdzl4MVZtUzEvb2tCMGxmWkdGNDJNZUtyZzFxbXlWOHp2Ui9YNmlUVklaK1lZQ1lOQk9UcXk4TGxpb0tMNXdycnd3S2x5aDd5R0FEaWZ4T09BNitJU0FnMnF2S2NjdVlWWTE3bVJnN0IrYWxaN1hlOE1NUm5vMXVSL1pNZzVPZEhXTGdrQS9handzYlk2OVl5eWwvZzNCQ3pMeXI4RWRMeFZTSHlCbzV6V29LcmxpMHZKZDJaWlRJcDRIQ1BBUS9QY3IvbkZPcE1MNkx1alVLRnR3TTFJSzFKS3M0bDBaaDY2djVXQ1ZsdWNMOVAyK3NvaVFvdis2eFhKQ3BXNzBwanZFZzl4TGRNYUVHdXRHa1UwaTNWbkd1bW0rSXAwSGNMSlRXNDFSK0ViNEtMRXdzaVk4VjAwY2FsN3g2U0t5ZVczbHZnbTc4VUZCNkV1dStXdjNKZVgwalFWRzZFekR5SlRNL29UaDhSeGgwbGZjTGczZG8vTXJScVdtMS9iaVZsM1BpWkdDdXBqZnh2NWlsU3dvOFBXZE9PR2J0dTZJc3VTdGJ4MjdiaTd1U0Y5NGt1aGx2ZG5nMzhGNkk5dmRHbGF4MjRmS1pJVTNLM1k4SmljcG9Heis1V0JLcWpORGpWczhSSlBDU0J0Tjk4eEtKUUxDem5yTmZNR212cnRxdUhBWk9zSUt5dFM2WHNjS21NeEY1V3M3Vzl5L2dnRm1waHdjNWJRMlh6ZHhzclZ5MHhhd21rM1Y0WG5jc0RRd1RjZDh6UVUzZjhBWktLZjV6SHlJcFhLVTNMcXkxVlpGem1iWjZqTnA3NmlBejRvMkQ5eENrREkwek95WUsxK0IwNFRGY0p2T09TYWpEeXdzdDIwdFFDUVU1L2hJV3JEKzhNUU5aWXNEMi8zRUhET09BUWNRQWtPb2N6cDNIaVJaY2xTK0tHeGkzOEY1ZEFlcUF0RnhtUzZXMVp1RVprSCtTM25FZWpoTE9tcU12eEQxQjlBek9DdHZXY2lUaEZ2Q25pSFQxUzJBbElWamVmQkNtd21UYjk0ZWp4Qk9jVXE1TGZ4aVhGdk1lQVNNTnhVeHdVN2dXRDB3bEhMZFVKMDFka2NOdkl6cFkrWVc3VjJsSHhMRER0TUdRcnJpVUplS2lFN2gxZnVYSkdGc1c1Z1gzSnltTTdETXpNK2JXbnlTWjZEdGhXamI5VHlzbmpjVkg0c3BCZ0hWeGU1SXZwWlVlVW8wZHkvekhtRzlEZkFUSEV3cGxJNnNGTklTc1pLTlhGOVl4MnkwOUgzTUNkRkZQQjJoQ25LMHpVL1JjSFY5V1dyN0NqNWxtUDRDSHhDcFF2UFhvNWxjR0tXVDZicjNFR3BWUmV5ampNTFlhc1lwZkJUS0IyeGowVWpkaWhNMnRMRmNiTmlGUGkwdmozR00xNFM4eXZuSmJWVC9YM21NRGxldXl4dVdXWXJzQUxPMEdCTGI3bzdHS2cybGE4akl6TnpuNlp0SklMNUJNa3ZrWUNRYzVZZmFZeGx5bVdiZUplTyswNzhWQ3p0L0JPZzlkMllCN3o0aHg3bGRKeEY4OS9xWnptODUyd1FTYm5DZ1lacUJ1VlNoVDB6TjZybTZwL2lDYjJCekt6Y2M3aDRsTTJaQXhLMlBjSmY4N3RESW5NZmRacmFyKzdYNFRQYU83UktwTGI3WmRRaFZ2Tys4eUM1TXY4QU9rUTZZRGwxcTZyYkt6dVNYVHpoTEN4NTZSUDVtWDd2cURlckV4ekRvaVZmdHY1UG1FRVN3WTRCRE1Cd0x5RG40aEQ4RGRtQXI1bGZjNlpOZVBLcDNZQlcyYnFJbnFoU3gyMlZYZDVRYmxLeHloczl5c0JtMWtMcmN6Wm5SZDlRMVJiRXN1cUR2MFU1aGUzcUJndklJMzhrc2lqYmVxK2laZWV6UWo4TUViM3kvcWVHZkN6MWxjTTRvUDVZKzNMYVU3a1FNQk9aU3h5bzFlTlo4RTRieHRhZmgwUVg2RjUrVUhPR3FOUVA5UmdndmNGeXBHZThCZFlIbmNmTnl4MUp3d2VnSG02Z3prckpmZ2Y0Z3U4eUphOEk0NGxXV1hpS0tqZEs5WlNRZGoxMmcxcE1haHJyS2ZJSHA3K0ptMWVzcjlUcHBzeGV3dzdwRitxbXRQdlAzRkNZK1dkTVZWdVZSWTl1Q1VHS3VxZU1RZDJ6RDN6NWVhZDEzbDYvd29xOWZqbU5ZQ0JkUkRjdjVnSnMrWnkveVZNVGdxWXlBT0dLNm5HTVY4enMvaFQ1cERMQzVDL2ZNYTI1Ykw3bHZORzIzWFRyQnI4MDFHOUNtRXhLbkk4anFFVlVPcEdWSVljbGZNelpGaWI4d1F6TG1Ec3BXWlpGWmdIUFFQeVpsdWxOb1o0YWVMNzlJeUprV0oxQzhSbzJ2VjBlam9mRTNSc1hTTzl5dURHNWdzWC9BTjNNTWFwZ0dkNWhBcDZqZ3ZwWm1USWNVbnFQQ0NsRjFXUHVKQmtwbk45d1FZakFGR1lydk5hazdTdnF4MVJ4bU00cWRHV1pHVitaaUdZd2gxRWhaRmpGRmZSaXNWY2xzL3FHZjdWSnVFaFVWalY1MU91ZzI3L0VFWm9nT1AwQXFDNXphdHk3ZDVTTGQ1aWZVcnlSYkhzT3oyelFxTXk2ekh4Zis0SXpnL1oveHFFRDQ1bnhHR2pzV3A0QTVZZ25peGNUb0JBOFpSMzhGdzFFenEwVDUvWXEvYy85UDBmTVVaM3o0L3FDdWxZdnRMNThuZWdtRmgzcU5LeUJ1clJoSGIxbU5zTllnT0tZUktqTnZ3d20zRDFKa3VZM0c3RmdpZ3hXTDNJc3NjenZmWDBtVGRadkh3aSs2RjlTejVuYitJVWJYRXptTXE2ZElscy8vOFFBSlJBQkFRQUNBUVFEQVFFQkFBTUFBQUFBQVJFQUlURkJVV0Z4Z1pHaHNkSEI0ZkR4LzlvQUNBRUJBQUUvRUMrRTdVeFcrQWVQczR4dW45RlFFMDY3N1lQTzUvOEFpL3pJanNnQjlITmJhOE1waUdJWEFWSmxCVEFFcGRHSGtxUHVWd3hkMnhDOG83eTFoMENkNlB4aFBPQ3RyUjhlWVVudXZubHdyRi85ZjhURzFtYVo5ZVErQTRiNVRiMVJkRDRVbUhqaTJZL2pnWVBNQUlIQU0wSFgvWmp1T0R3YTg5ZUZDdTNNMWlrVkdOUTJNeFRqM3liQmlBWE9lTVNmVWpZN1Fvd2d3K0VEMWlZQ2Y1Y055SDh5am43ejJxUCtUa281M2xmVzgwZzlwL3BsMkJIYU9CUms5MERINU5TQzJKNEpvZFFNRDJmcEIxSEZkdEdLbDFvMDlmNWpOanJ2K1kvOFp6ZXdyZUhvK0RDRWhieVBaL3FvZU1HNFB0T21jd2hrV3dYWWp6MndXNDJzcXVpSzdHSldTYU53bFlWMm1LU0tBdGowQUd1cW56a0JnTVByRmEvbExRR3U5Q3lIRW5YWUpCQVNsaXRqQ1V3VE8rR3RONmRhaTlEQkVkQW5weXJaaEtNR3Ntc3FvdUdKenNWK0ROYXZLajlIRHl6SzlFRlNVZG0xTU0wUkNDUGZiUGd4alk3bXZ6L2h3cGxnWjhzNWZMaHVGYTcrZTJ1RGt4eDJyOEJLcmkxN3lBeUNyeFl3T1ErOXZqNkllMHhFYWlvUTZzVHNzSzJZU3o5NkM2a0QyeEt0WGxiamFXdU1IRHdneWlyN29NS3UyU08rbkNUck1kdDBVQ0s0VUFNZ3lhb2tmWEE2dDlGZlZnN1VjQ3I4c1pQU2pIYWN6dHhnaU5ZQVRvdE91bzJPSTZJNXh3Qnl0L1djYzNyeitzSTdDbWpaZE9KTVlYc2FVZUNmaGJ5QXR2bmtYbjhneE9KMitHbk9Cc01xM21xOW1iNzV0K2pyRWduT05qL01DSWxsdElxUzdOWWtCb0dhTkVpZXI4Y2NhQ3BXaHM5eURIWjRGb1dqTHRWOUZjQU1xSmdscUhmUTQyd2U0MmZac0laQ29ESXFJRVZKdVpoK0xwT09BMEFkUmMwd0JMMUtpNDNzeW4rRkZpYlZkTURDREZFcWswS1dieWxrclpoOTRjTy9nS29JTTdFSTdCMzBLSThKbDFFOUZmZ09BMzdiZjFNdzhyeVcwZzVRYU1RblBlazlWZzJubmlYOHhtOUZCYkVRcURYREJEYWtxalpzR2psRzh1R2tmc1Arc3hLTVBlekUvTXBFaFVnZkxySk5mbG92Ymh3cGJmTlNSc0YzWWV5WVZEYS9IWmw2dkR3QXJ3Q0JFV2xxMEhOZjlHRU5CQ0llWFhYRUdZQkJFMUw0OHRZcnAxSGVZV2ZuSHlOTkJRLzllcGt1UGVQMUZOZmJCeW9ocEtOa3JEblJjQzc3VUIyT1hhQUIwd2ZSVFB5NE8rYmtuZWhqVytPS250Nmd0OUptVlI2UEhLUmpEdHdOZzZwanNPNEtFaWt0UUVES1BhbktLQTg5bHdiRTVCQ0t4UUx3WXlzTHNFdmwvcHdIVUpoOWRCVmR3UFprZ0ltaHFva1U2cWtTRGxzRHNNQWlsQTdUZ3hpN0RBTnN2Ym52cGtldE5US1lCdE02T3VVYWppcUswRVhVS2NPbEFKUXltd2RISVp6QzE4MVJ4SG5ZNHdtRE1Bczgyd1p2aU9HNitWTTVWd3ZNT3JrY04yQkhZQjM2akdZY0k3UEJUeHRqbzM4alBrZjNoT0RBT2VuMkM4TmZHVEp1eXZCeGdtUVlIZ2tXOU1TalFxRmQ5NGRVZ0lCTjdWdzFNTU5NaFRFVjRaYzNGOTlGaU11bVZrREhQWXpnUGRYb21jS01nV0dVL3dBckRMTjRDSFZPSmFTN1NGZ0loUlJqc0l2eXdkVUlqdWtDKzRGNk9GMkRJYzRBMXBCd0xvNU8rWUtBd1ZPTFFxNk14UitlQWFUSUtkeUhiRzltMGljSUpFVG52bEtpRUloVXA2THZKVGNrUzdMQVgwWXV0WFVJMUlteEtQSm1LNUlaVU9sUTBHOEVhUHVvR3NFMmhEWmdrR0VGNUJaVjBZZVFuNDlhQjhZdEhyWC9BTjF3K3NQWWt0LzBYOXdjY0FhbHpxdW80eTFETDNTQS9ialpYVi8yYVB6SjhTd1Juc05ZWXlqQUs1d1ViMFkrM0FQRU5yN0FGK09HTVBRZ09KdFVSNFYrSlM1U09uZFVYdXJReUt3ZFRDRjFqc3hlQVRUMTVCU3RaZ1RacXRueU9uRlVzRGU3NzFIdzVoaHZJbkNsUjgzMlhBTExVR3dWbXpxRFpnQzNPU2lsSWFBVEtJV0d1VGhxOG01Sy9UbmhCWGt6V0FaUVRWam5paEJGTUd0dDZUUXdKcmJrcWxuamVZT3RjTWVVVkF1NEdBT0xHNWFKRGttWnBhbXdydGR1bEZ6a0xmWklvZ2V6TmJPZzR4R2JFV0d1Tkt0aU5FZE10KzMvQUEzWExoTGFLK2pjRGJRZHNVbHQ0cHZRQ3M2T092SUcxUTdiSnBvSE5sYnBpWFRqb0lKTkhJK05LNEJxNEhyb1B1N0IyeG1jcUtZaDRDTEtvVEQxZDhFd1NBbzVGRHE4cElIc01SZkFDY2Y4Wit1R1ZHSWowejh4dFR1aUlONmo1eUVmZ3k3TERRRG0ySjZxZkNvVndCUmNjV0dvYU5ydE5OeFJ4eXZ1MUZVMklLNll0ZUtOaE1VNTV1b3czNGdQTEFsVGVIekZMdUpkY2lVT1U5VlF2WkU2TWpsaFJTeWphTFo4UEdYdVFxUWRGWFZ3Zzg1ZFhSeFlOY1REYUFMZEVCaG9MKzRkekxJSTdtQUpvamxORHQrNEtDRWRXVk5hQWJoVG8ySGNUTnBseDRMWDFJeWxCMURmYmlNUzFMcFZGZTdkVEIwcWxSaTd3b2MzbGcvNjBjQVkrQUYxcmxJN0pGeFZVOVYrc25Nc3FLcC9xeVJGOFo4SWp6VG0zdmthTGJNRWZraGtoczFKM3M2ZEF3Q2NKNmwzTlk1elR0SnRHa05KYTNOc1dFeG90K2xabHpKTmVRN1kwS2pHSkZVc0U1SGVoQXdNYUFJWmY0NTBNVy9mOCtFWEZaOHY1b2FyVkZlMkFGbUsxQ01uWTVIc3RIUlF5OFRvU2J5MlYrTUJuTWdCNzlVemhwVG1obHV4dkxCSENmTEZGa3d6Zi9jUDRXRjE0SXdvbzdQWGplRnpDSC9vekpEaTRTd2NhbDRpdzhlRVVIKzhjOTdqSjZFS0FJSUpEVHVYRFlPUTJybytncDF1RjI2YTRxZ2xua0JKanY2N1c4N0lsaWxseFhWL215UkJKSVBJeDJ4WXU4VUtkSzF4aGgwSE5TYXU2Ymk4eTRxeUJCUVRTWG5RdGxaN0tWTXFGdUdJZlVJQ3JYaGJab01NUzJpb2p3Z0kyakxpc2tGV250UWVQSXN5TFJ5TEhhS2FqVkdKU2ZIUGNLYVBVVEo0b0hLVDBZNk1jQ3g1d2JWOFRrSHpra0J1OG5mMWU0T0ZhcTdkNG9hMEp1WUozQkRqNGxtSEVHT1JPUkhBNEpsUGJYbFB0NnNmYU84ZSsrUDBGelg1WlpkTDBCQWNJS2JSRjhvUGJtNU51ZmhHV25aOXVUSTZ5dXpVNmFJR2pRNnFnL0xPWTBHcE8rR1N0T2lKZFZCc3lBdk9kamFuaEVjaERHMVkxRkZKZ3NFZ3B3Wk5wdHo4dmI5Rm8wWWJpaDlCb2s2SlFQTWNPZS9nZXFLUjA2aHVDcFp1UXZGQ2dnRjJhdUFhekVpQVJnZ2hEVlhPeVRrcVRNTktGV3gvTFY5L3MwUnVVM2FZaUhPOHJTT0lpc1lJYmloK2JpeGhOSlRXaUdhNXVrcVlMS0Jqa3c5U1lxbVJORWVSaUJFMlBVY0NXeEJCb3k0N1RkazBITTREVWZkMGs4MjJUYXcvdnhqYkFQU2E1eE5NenlHQ2dCU0d1OXloRnd2SzRkY1lsSHhxVkQ1bkRGNTJqSUhYdjBaVUI0bUZBdEFKOVlEZ1BNcVZDY0R4Y2YyY1FYSThtMU1qaGV6UnJ0ZjdEa0Z3MjA1dUxVRkNGU1RDZUtSNVA3VXJrMWlzbmdXYWhkUWlYa3R4RUpjQWhCQTU2bTRkcWJLaUtEQjdhVXdCUWkyVFdvbTVMeXNjU01ERkhjRnhmcXlDQVlSQ0FnbXR0d1BwVUlIa0lnK01ib2hSc2JYTlhMaEpoQUV5R3QzWG1vWEdSYm9oYjNoVzEyeGFJOUVlMkpUVENEc3NJZU1MN0t1VWRzWEN2VTBaTHFEczV3cW9wRmxWZ1Z3ME5WZ2E2aUhjWnRYOGJQd3poMkJjSVljenIyQkxnMElma1V6ZUh2SWRYUVhuRW5mL0FBRHdjNW01Z1dkSUMxb2k4aGpRM2FLdGdOekZ2emJsNDJLZzdyMGNwRFIwblNMM3g0THlQVS9Kd0p2N0VqcFNkWHRqUjNva2kwRmRvNzdZTmZBN2tJTDRuRnduNEhablVTY09uQWh1OFc2cG9ZNFhJS09odFJicHhBTTJySTl3MGtzNjVjMDZKaU5OZTk0eEs1cUx5KzhJQ0M3SHdLUnVwREJtWnN4NURLTEM4SmdTeGxUUGhQakRvVldFRFN6a0NpdkF4VGhKcjhxYklIcFpoT2dCM1ZPUVFQQjA1d1FDWUE5RFViWXZycGk1MGhMRzBtaXJFTzlUN0RKeitZaXM2KzhjcE1UWi9aSDhDeVVZakZJaDIvNWxQbzBOYUNQTmNZeHd4a0NVMDZCM2phdndrR3NNc1VOYlY3UFJNUGx5bm1kaDJPdU91ZnFlREJHR1JtRW91WDdZQWlKdDFNZ0FkVnVkQ1JLOXNVdTNMRHMwTDhyZ1IzQlRIdFRIN0l3RGV3b1BHTEFmZTZ2WXBocDZSS2hJNlJ3cjZKWUVHVmhocHdvSzVZcE41Y2FtL2ZLYjVCNTdZMjQyWkFVOElqSHZnc1R0clJvd1psOXpPaWVvSzdmaHhFV1pTeEJLaTE3NUx3L2dyQWtCb0Q2REllakwyRTh6WnlCZGRGQTEvd0JNMjBNaGNyT2N1VmJBN24vZ3lyRUYrNWtXQXBiZEgwcUZNQjZtMnl0SjNDaGxYU25YYWpSc2dCY3ZsSmhiZWdhbmJCL05sdFlKQUFNakNhRW1VU1hxR3NXb3RHNHFqcGVYZmdsSWUvR1BJcGFOZ3dCSytwdmMyc0poeWJRVlhwamxrcEMvZTNOcGFCODV5ZmlkMVg5eDVQRlAvdVJ3aE9FK1Qyamh4SldUYnNKQnJsQ2ptdUgrT01MUWduYUFCc09tNk5NYnRlK0IxNjBFU1I4NDFaNlJNU09OczJieXRlQldsM3c2VEJ6ckFKYWNMemk2YXFhdldzUDNOdURhcEpzWlY1d0VVbFJCVldHL3VVSHMrcVRSL2VBMkd5TitUZ2JYbUs4bFBBOFpvM1ZjaExONGNPQm9UVDNobDlOTUQ2MW5PeHdkSExaeTkxaHgyaVgrc0hLcmhZbjM0bkpNcXlQYmxXYWFuamt5RWEyWHROU0gzaXpSNHRrNTF0cUJyUVFjYW5DSUltZ2l0SUQzeWs4MW1QQ2dRQ3NMaCtWd2VqWWxnNlJaL0htY2NNeUo4ZEh4a3FCQUFQSUxmV0tpamFpL1ZRdzQzSUJmempPZ1A2TTVaRXpLdGN5cmQ5ZUlQTXVaVk9Xc0JtbmN1V0JpVllsUzZWL21PVURHTXZCZ1RxZWluRG5oVWw3dWFoeno5MndvSnNQYkg2amcrb2JWMW5IaEZsRDd4ZnphZjN3REVadDhHbmVzTnREMkp4Q2cramtuNzExZDRSK09RRmVEcSs2a0hHa0E0djhBbTRFeTVUS1ZWVmNtZGRBWThERjVPOFk0cTI3S2hPN3ppdXdFTjVKN1J4MU5WSjk0c3JBKzNnY2RBU2M2c3QwSmo1eGx3Qys3VHlCT3kyemhIQk53aXE5LzE1T3ppam9aQTB2TFRsSG1Pei9HUHdOMDZaUHR6aHcrMzhab0FkUkh4b0g1aDk1K3BPNlUvY0FBWkJKUE1BL1dCR1BiK0hHTnhIcXFYR3psY1RPQjZEUlB2T0UzSW81cEVjSzdzNHhYdjJUaDI5REFrdENWK09tYzRwNktGME9BOXVLVFl4ZVJSeldvRno3d2lPM3M5bml0YjJNTHlYOWhzSWs1OG5oaVdCQVdJaS94aTQ4clVFWTlWNUUzaVpCUndoSzUvOFFBTnhFQUFRTUNCQUlJQkFZQkJRQUFBQUFBQVFBQ0VRTWhCQkl4UVJBVEJTSXlVV0Z4Z1pHeHdlSHdJRE5Da3FIUkZCVXdVbUtDLzlvQUNBRUNBUUUvQU9BNGlGSDRESVFnaEV5cklBbS84b21tM3hXYW8rMmdUYVlHdCtBVzZLY09zaVFOMW5kb0NocCtJSGhQQ1NpWldVblZTM1FYS3l2ZHFtdGEzUkR0Y2JCR3F6YTZEbk9OeENmWW9NYTI0Q21ScW8vMlpqVkZ4T3FEditBUXBFOW9vQU5FQkFBamhJRGdlR1o3eGF3UXBqZTZnTjRQN1M2c1hLemdhSU9VaFBwRFVPbE16QVFlSWxSS0poQ1Rvc2tQQUtkcWdTc3hRVUJ3Z29OQk1GV0FQa21teW1FSE1LT1pPN1Nma1kyU2JvT2JKQjJLQkNFeVZBUUVKalh1TU51bWRGWXh3bHdEUjRtRU9oeVIrWVBZcXQwUmlHQWxoRHZMWDJUbU9iVkljSWpnZnpBbmE4UktBVENTOU91Q3M0aTVXZWtQRkNvNDlscUhNUGFSN1NOS241cUMyOWdtM2xjc29OSVZERDFjUS9KVEVrckRVNlBSb3Qxbkd4UGY0Tkh6V0xwdnpjeGhNSHUxOVhIUUpwQk94OVh1UDhXVDNHQVA3K0R2N1ZURFVhMkdIUEU5eDNDeG1EcTRSOEc3ZGlqK1lFY3JqWW90ZU5wUkxnZ0hHVVRsVnlZQ2FBQVFYS0tMVnphVGRBdWVkaC9DRDNPN1FSN1VMS1RxNWN0Z1RHRnM2S0pjcVdGTHUwcVZLbmc4TklzU0pKN20vd0JsQ2J1ZFlnWC9BT28yYVBFckN2WkJvVkIxZTdadmRKM0pWWnJxRDhsUSs1Z2VqV3ByYzdnMGIrZndjcWhHY01HM3grOXRDcWxKbUpvbWk3UTZlQjJWV202bldOTitvSmxCbmlnM3VRZnNDblZIR3dRenVOazBaZ1VXMk1GQU1HeUJPd1hYN2tBNmJwdzZ5RktQMUlNQmNoMFZSYTBrdUtkaHNQU0FEYnUzVkZyQzhOQVdJTFgxWU9ra255WjlVQTVvekc1QXplYm5kbjJDNWJzMlZ0eURBOFhmcWNmTFpVNllhQm11UnZ1aUduVlZhUmE2UmNIN2p5TzNjVUQ5L2ZvZmRQd2VHcmsxQ3dFa0xIZEduRDBEVUFqdnVxWkVCWHVaVEhkWXdoWk1BaE9UTFNwUGV2MWFwejJOaTZGVnBmQVRxRlpvRU1Ka1RvVTZuVnBtWERMUGVpeHdhN01DSUVxcFJjeXNjMzBXRWEzbnQ4MVV3MVoyYTJyU1BVdVgrTzQxWnQybW5YWUJZV2dXdmFTUVlCOXlibFFGQUtxZ2Nvb2ltSnY5M1ZPTnZGZE1DY0E1TWFXMHkvMFVHYklOSUtiVHFscGZGZ3NMaHFtSmNXTWlZbTVXSXcxYWc2S2doREQxd3d2eW1DdWpXMHhnMnVnYVhzbXRvMWpPV2ZRTHA5bE5wcGhyQm9kbGgrclhhY2tHUnNxV1BwVW5ac1U5ckc5N2lHZ25ZWElFckZZTEM0MEF2MjdsVnFCN095Rld4MUp2UzQ2T001aTB2SGNRREJqeENvc0xIZ3Fzd2M3S2REbWIrNjRRZTR0ekRVZ08vd0RUTEVleXAxRzBuNWhvMlQ1c2R2NkZUbWFDMDI0WXFxMWpPWHVkZkw2NkJBazYvZjNKOWxSN0k5MTByajZUc082bElKTm9SMFFLa0lPbTBvRnpIU0o5Rm1ZQW0xM2hzTmNZODFUNlVyVXFmTFlCSGtxZlNkYW5vQXNWaksyTEF6eFpIV1ZYYlJ4cmVWaW1OcU5CbUhORGhJM2dnM1E2UXhZRVp5aFN6TmtMRmRHWWJFWWluaTZqWmZTbktieUpzZDdnb1ZHYktxQldvNXA4RDRSb1ZtT3M1VFA3WC8wNUEzZ0NJOTJFNjIzWVZWcXZ3bElNcDJPcHNTQjh4S0hTR0pmWUVlalNmalpWaUtqQldiNUdPLzVuWWJCVXhtT1VmZTMwSHFWMGhpNmxYRkUwM0VBV0VKejNPdVNpN1NWek9wbGlBc3dJaUU0dHVtdUUzTXArVTZKZ0FGeWdXTEt5eURRR1NEZFpYSnRONE1vVVdtbm5MeDgxUmZRd3dPVEVUNXB1S3BPQm11QjZLcFd3UWs4MEVyQ1kraG5zNEh2Q3EwcmN5bVpHbmhIYzc1RllWZ3ZVY0xOMG5VZUFPNEtyVk0xVXVkWStJTFQ3aXhRY0g2WC9BSE8vb0xDdkJtbTdRMis0c1BJTEhZeHVHWWFGTXk4Nm51K3Z3Q0pLRjJvTkpJRFVXT3o1ZEU3TGZLc3RwUWdTb2FSb2dHcXlhNndUSFUydE9iVkRsa0xMVFRTQk1vRjBHVUE5ekNnSWFaMUNqcUxEWS9GWVY4c2Q2SFJmNnhnYXpPV1FXanlCSHNnN0I2c3JBZXJoL0YwK3RnV2RxcG04cFB4c3NSMGsvS1cwUmxIZnY5UFJFcXlFUWc1MDJSTDgwblZmcEtiTUlOa1NvSWRCVE5DbkMwb01scUxJQ3VOMTFodWdYb3pFSnhBMEt6dERTSlhNYmxoU0VJaUNpL1lCTmNTVS9zbEFBbUNnMEVHQXU2RTBsTmFjMEk2Rk5UU1lOMTRwam9Ubmh3VEtnQWhaZ1ZBNFFnVzNReTVEYTZFWkNwT1dGQjFRSlVwcHVuOWxCTkFwc2tyZVUweVVDV1ZMSTZGTlFGa0V4dlZWUUFCRmhBUVFMbG1XWUhkQ21FNW9DUGNyQUlTb1VGTkJEay9zbFV3TTZxR1RBVGdtQzZKNjVsR0lLYUNnYlFnRUhXdXFoTHhaTWZtRjFiOEhOTUl2bmhDQVVLbTBFd2JLU0RDZDJWSkNGd29RYkN5dUpzRUtibEVDQ1VMYUs1MVZsUGdtdGdxRUFvNFFvNFFod2xDRllzaVVLYmlKaE5va1hKaEJyRkxSb3M0VnlwQVdaWElNcGx5cXVxeWpMS2RxcFJLbENZUTRRZ3h4MFF3MVkzaUZ5R3Q3VGtHVTloS0x3MjFnalZuVXl1YVNpVnpBc3ppdk5DSTRiS2xxRlUxVWpscUpVQlF0a3hwS2JRSjFLTGFETE9LNWxOdlpiN280bCt4anlDZFZtNXV1WVNzeEtsY3diSUY1UWFFd2hSZEVpRk51QTBLcDZwNXVqMkFwUjBRMDRmL0VBRGNSQUFFREFnUUNDQVFHQVFVQUFBQUFBQUVBQWhFRElRUVNNVUVRVVFVVEltRnhnWkh3TXFHeDBSUWdJNUxCNFVJVk1FTmlndi9hQUFnQkF3RUJQd0RoS3NvNUtGSDVOUW9LeVd1bXREZmhYWkF1aG5kWVdRYXhsMFhuZ3dkcE5DWm9VYkFJWE1rTEtPWCszQWRaTkVhS1FGQkk1QkFzYm9pNXhHcVB3cUZaTWtIUkNtWXVnQUFZUjBDbVRDdnhBbmlPQTRBRW9RRkhNck1Cb0ZKY2pJNEVXUUNoalUxMG9Fa0ptNk9nVU9ua2d3YjNXWGtWa2NnUzJSQ01FL2tsQUlpTlVDTXRrT0orRkdRVVRGMEorYUlKSmhOYkN5SGE2YWpzbWt2ZEFDTWpSTkVxMnlEM0RkWjArcFRZSmRaUDZUd2dKRENYRWNoSy93QldqL2pQcVB1cVBTdUhjWXFBdDhkUFVKbFJybVN3eUR5NFIyU20zSEN3VTl5THRpUjlVK012Tk0wMGk2REhUdWd4M2Nzbk1wb0FtRTYwSU9JTW9tVTc0WTIrcTBFck8yWVZhdlN3N0Mrb1lBVloxYnBFOXF6ZFFQNWNWaEhzRFRUZUJJNTZSM0FhbEVRT1hrMXYxdXFiWmt4OUQ4MnBsZXJSeEI2Z3h6NUgzNnJCNHhtS1pJMUdvVXpUbE1EMkNDRUhNT3BoQU4yUmJ6V1c4VC9DTURWU1RvRkx6eVFhL2NySTNjcG9hSmhQMldidVFjU0ZWZlFjMGRXMXdNN21SOUFuYUJQcTVaSVZTby9HWW1OV3RNQWMzZjFxclJEZTBKdC8ySTFjZTRMRU1lU0sxSTlybnU3bkhJQlVYTnJNenNIb0pQbTV5THNyWEhsNGZWdDFSa3RMei9sOUI5UjM2aFU2ajhQVkZWbW8xL24zNEZNcU5xVWc5bWhRZFpGMHFHY2xsYks3QVRyUWdYYmhSM3FCelVCQVFDbmFqZ0FoMGxXSkFEUkNaWHhGVUZ6ckRZS3RuREhPSjBXRkJaUmtheDgzbjdLV3VKYUxENFIzTmJkM3F1c2JsTDNXQkVudWJzMGVPNnFWWFZIT0xiTk93MFRNdytGWWVybWJsTmlQYytJMzVoUjNlL2NqMFRjVmlLSDZZZVFBVmdla1JpS25WbTlySVNtUkJUUU55aVpraEhWTlRpUkVLWlltV1pLWVMrWUZyS3EwdGhOeE5Na3k0Q0R6Q1pVWlVuSVpUQzB2YmxJTW1GU3JCMUZ1VCsxalhQOEF3Ny9CVXNSUmFCSi95YnRzQXZ4RE9yZ1RPVjIyNUt4VllPcFBEUWJsdTJ3RmdnU0pRSkdpb0U5Y0lUUzg3ZTdLdE55ZVM2SU1Zd0JBaVlVQW9FRnFsZ01ibFZYc1lKY3FiMnZFZ3JPd21BYnJwQTFQeFRoSlJOV20waWZtVjBBOTVGUXZjZHQxaVhNY3dqTk51ZjhBU3hHRmluRklFazhsaHNYaWNJWFpJdnpWQmhZK3ppcWJZd1F4RWJ3ZjRWZXFIMG5NNXJEdkpvNWhxSVA3VEIrU2hvY1duUUVqL3dBdjBQcW4wM1ZhWllkWFFQQjdmdUZHUnhEeGRBd1ZnNlRuMURVaXcrdjlhbFFOdmZ1QjZyRWZFUjVMb3pBdlpYRlVnZ0RmbWdyb1NvS0lscUFlVmszSUNmMGRUcVZDOTh6NHAvUjFKK3NyQzRTbmhweVRkTzVycVd0R1psdk1oZmdjSWRXQmRaRHZCVU1YVWJUZFFiWU9oR2k5VXB3OWNzTjV1Ty9ZanpDREJvTGlQM00rN1ZBaTVtUjVQQTc5bkJVNlRNWFZjNnBjQ3d2Qit4aERvN0RNdVFmTWlQbGRVUjFielJQaU5yYitBNTdsUE9VRng5KzlmUUxBWVZsTERnUEFKZGNvTUFiQUN5eE1KckJtbmRGbzFWS0FicHpkd21ieUZMQ0xpL3lRYnJLdUpudTlFRExrQUNJbEVBaUFVU1EvTGxLZFRyNGs5dkQ1Zk5QdzFSaHRSSjgxU2JpejJUU01MRjRONzZkeEhJOGxTcW1lcnFDRHJibnpiL0lXSmNZeU5OM2N0RDNrYkVLalR5MHd4dHdPUkRoNkc0V1hKcmI5cmZ1VmltRUFWRzZpL3ViK1pXRHd4eExoVmVJWU5CejkvTXBvQmNpbnVhMXN1Tmt5czNxZy9XNmJPVytxYmN3aUJLWnZkWit4bGkvTk5Mallvek4wUGk3b1g2ZTZCWklSaUxkNkpBRmtNUFZxRTVHa3duTkxiRVFVU0FzUmdzTmltRVBiNWpWRG92R1VhbWNPRGo0a0dQRlJpd1lmU0o4bW41MlRLV05mOE5QTDR3UHBkVXVqbUR0VmptUHkvdnpUYkJUeVVsT0RjdmFUV3NOT051QWdKejRkQVZJaHpaVlJ2YUI3bFFNeUVYd1NFSG9PVWhTRklEbFU2ck9lcmNTUENGY2haWkFsUUFFU0JvZzBJUWltMkJXbHlyRkVDRTJDMnlHNkVwMFRvcWNHWUJUbTV0MVNabGxHbmRaVm9oQjRhcURDTXhkRnd6YUlQTWFBZVNDQkkwV3lNSWFJbVN0b1JCSWhOc0ZNb0k2cHFjYmxVU1hTVTE1UENHcktGbFFKaTZ6R0ZtTWNKS0V3c3pSclpCelhDeFJRczFOVFZLQWtLSVExVmtIQldPaXBqSW5NeThaUXVnMEl3RmRTVkpXWnlMTTRrWFRXQUdVVU5Gb1pVZ0JOZURaQndHcTYxdTEwMXpqdENNSFVvUU5BdTBWbGpWT010dHR4RUlBSUZTZ1U2L0NFSkZ3Z1NpczdHMmxkYzAyRjEyejNMSU55Z0JzRktEU1FnMElRTkFpU2dJYUVMQW9DeUYwQW9RVXFWbVFqZGRkU2J1dXZjZmdhcHFtNU1JTkJGeVNnMW9zQWd4c0lBbGRXN1UyV1ZvNzBPNWFtL0JxTzYyV3hRTmxkQTJRS3NFNnMwTHJIdUZnaFRxdjFkNklZZW52ZEJyV2pzaUVKUUNEQ1YxSkh4TDlNZDZ6Rk9EdGltbXlIY2hyd0NpeTBDaXg0Q0pVOFAvL1oiIGlkPSJmIiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIvPjwvZGVmcz48L3N2Zz4=", + "description": "Displays changes to flow rate data over time visualized with color ranges and background.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.rangeChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.rangeChartWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: false,\n defaultDataKeysFunction: function() {\n return [{ name: 'flowRate', label: 'Flow rate', type: 'timeseries' }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-range-chart-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-range-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flow rate\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 40;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":2592000000,\"interval\":86400000,\"fixedTimewindow\":{\"startTimeMs\":1700577227898,\"endTimeMs\":1700663627898},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"dataZoom\":true,\"rangeColors\":[{\"from\":0,\"to\":10,\"color\":\"#2B54CE\"},{\"from\":10,\"to\":30,\"color\":\"#3B911C\"},{\"from\":30,\"to\":50,\"color\":\"#F77410\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}],\"outOfRangeColor\":\"#ccc\",\"fillArea\":true,\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm\",\"lastUpdateAgo\":false,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:Zmxvd19yYXRlX3JhbmdlX2NoYXJ0X2JhY2tncm91bmRfKDEpLnBuZw==:Zmxvd19yYXRlX3JhbmdlX2NoYXJ0X2JhY2tncm91bmQucG5n;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Flow rate\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"m³/hr\",\"decimals\":0,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "liquid", + "fluid", + "flow rate", + "fluid dynamics", + "velocity", + "mass flow", + "volume flow" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/fluid_pressure_card.json b/application/src/main/data/json/system/widget_types/fluid_pressure_card.json new file mode 100644 index 0000000000..cd14091cb6 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/fluid_pressure_card.json @@ -0,0 +1,34 @@ +{ + "fqn": "fluid_pressure_card", + "name": "Pressure card", + "deprecated": false, + "image": "tb-image:cHJlc3N1cmVfY2FyZC5zdmc=:cHJlc3N1cmVfY2FyZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxnIGNsaXAtcGF0aD0idXJsKCNhKSI+PHBhdGggZmlsbD0iI0YzNjkwMCIgZD0iTTMzLjEgNDguMlY0NGwtMiAyLjEtMi0yIDUuNC01LjVMNDAgNDRsLTIgMi0yLTJ2NEgzM1ptLTktMTAuOXYtMi42SDQ1djIuNkgyNFptMC0zLjl2LTIuNkg0NXYyLjZIMjRabTEwLjQtMy45TDI5IDI0bDItMiAyLjEgMi4xVjIwSDM2VjI0bDItMi4xIDIgMi01LjUgNS41WiIvPjwvZz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNNjAuOCAyOS41aC0yLjN2LTEuMmgyLjNjLjQgMCAuNyAwIDEtLjIuMi0uMS40LS4zLjUtLjZsLjItLjdjMC0uMyAwLS42LS4yLS44IDAtLjMtLjMtLjUtLjUtLjYtLjMtLjItLjYtLjMtMS0uM2gtMS45djcuN2gtMS41VjI0aDMuNGMuNyAwIDEuMy4xIDEuOC40LjQuMi44LjYgMSAxIC4zLjQuNC45LjQgMS40IDAgLjYgMCAxLjEtLjMgMS41LS4zLjQtLjcuOC0xLjEgMS0uNS4yLTEuMS4zLTEuOC4zWm02LjItMnY1LjNoLTEuNXYtNi42aDEuNHYxLjNabTItMS4zdjEuM2EyLjkgMi45IDAgMCAwLTEuMyAwIDEuMyAxLjMgMCAwIDAtLjguOXYuNmgtLjRWMjhsLjUtMSAuNi0uNmExLjcgMS43IDAgMCAxIDEuMS0uMmguM1ptNCA2LjhjLS42IDAtMS0uMS0xLjQtLjNhMyAzIDAgMCAxLTEuNy0xLjdsLS4yLTEuM3YtLjJjMC0uNS4xLTEgLjMtMS40LjEtLjQuMy0uOC42LTEgLjMtLjQuNi0uNiAxLS44YTMgMyAwIDAgMSAxLjEtLjJjLjUgMCAxIDAgMS4zLjJsLjkuNy41IDEgLjIgMS4zdi43aC01LjJ2LTEuMWgzLjd2LS4xYzAtLjMgMC0uNS0uMi0uOCAwLS4yLS4yLS40LS40LS41LS4yLS4yLS40LS4yLS44LS4ybC0uNi4xLS41LjUtLjMuN3YybC40LjYuNS40LjguMmEyIDIgMCAwIDAgMS43LS44bC43LjdhMi44IDIuOCAwIDAgMS0xLjQgMWMtLjMuMi0uNy4zLTEgLjNabTcuNi0ydi0uNGExIDEgMCAwIDAtLjUtLjNsLS45LS4zLTEtLjJhMyAzIDAgMCAxLS43LS40bC0uNS0uNi0uMi0uN2MwLS4zIDAtLjUuMi0uOGwuNS0uNi44LS41IDEtLjFjLjYgMCAxLjEgMCAxLjUuMy40LjEuNy40IDEgLjdsLjIgMWgtMS40YzAtLjEgMC0uMy0uMi0uNGExIDEgMCAwIDAtLjQtLjRsLS42LS4xaC0uN2wtLjMuNGEuOC44IDAgMCAwIDAgLjdsLjEuM2MuMSAwIC4zIDAgLjUuMmwuNi4xIDEuMy40Yy40LjIuNy40LjkuNy4yLjIuMy42LjMgMWExLjcgMS43IDAgMCAxLS43IDEuNGwtLjkuNC0xLjEuMmMtLjYgMC0xLjEtLjItMS41LS40LS40LS4yLS44LS41LTEtLjhhMiAyIDAgMCAxLS4zLTFINzhsLjMuNi41LjRIODBsLjQtLjMuMS0uNVptNi43IDAtLjEtLjRhMSAxIDAgMCAwLS40LS4zbC0xLS4zLTEtLjJhMyAzIDAgMCAxLS43LS40bC0uNC0uNmMtLjItLjItLjItLjQtLjItLjcgMC0uMyAwLS41LjItLjggMC0uMi4yLS40LjUtLjZsLjgtLjUgMS0uMWMuNiAwIDEgMCAxLjUuMy40LjEuNy40LjkuNy4yLjMuMy43LjMgMWgtMS41bC0uMS0uNGExIDEgMCAwIDAtLjQtLjRsLS43LS4xaC0uNmwtLjQuNGEuOC44IDAgMCAwIDAgLjdsLjIuMy40LjIuNy4xIDEuMy40Yy40LjIuNy40LjguNy4yLjIuMy42LjMgMWExLjcgMS43IDAgMCAxLS43IDEuNGwtLjguNC0xLjEuMmMtLjYgMC0xLjItLjItMS42LS40bC0xLS44YTIgMiAwIDAgMS0uMi0xaDEuNGMwIC4yIDAgLjQuMi42bC41LjRIODYuN2wuNS0uM1YzMVptNyAuM3YtNS4xaDEuNHY2LjZoLTEuNHYtMS41Wm0uMS0xLjRoLjVsLS4xIDEuMi0uNSAxYy0uMi4zLS40LjUtLjguNi0uMy4yLS43LjMtMS4xLjMtLjMgMC0uNiAwLS45LS4yLS4zIDAtLjUtLjItLjctLjRhMiAyIDAgMCAxLS40LS44bC0uMi0xLjF2LTQuM2gxLjVWMzEuMWwuMy40LjMuMmguNWMuNCAwIC43IDAgMS0uMmwuNS0uNy4xLTFabTQuNS0yLjR2NS4zaC0xLjV2LTYuNmgxLjRsLjEgMS4zWm0yLTEuM3YxLjNhMyAzIDAgMCAwLTEuMyAwIDEuMyAxLjMgMCAwIDAtLjcuOWwtLjIuNmgtLjNsLjEtMS4xLjQtMSAuNi0uNmExLjcgMS43IDAgMCAxIDEuMi0uMmguMlptNCA2LjhjLS41IDAtMS0uMS0xLjQtLjNBMyAzIDAgMCAxIDEwMiAzMWMtLjItLjQtLjItLjgtLjItMS4zdi0uMmMwLS41IDAtMSAuMi0xLjQuMS0uNC40LS44LjYtMSAuMy0uNC42LS42IDEtLjhhMyAzIDAgMCAxIDEuMi0uMmMuNCAwIC45IDAgMS4yLjIuNC4yLjcuNC45LjdsLjUgMSAuMiAxLjN2LjdoLTUuMXYtMS4xaDMuNnYtLjFsLS4xLS44LS40LS41Yy0uMi0uMi0uNS0uMi0uOC0uMmwtLjcuMS0uNS41LS4zLjd2MmwuNC42LjYuNC43LjJhMiAyIDAgMCAwIDEuNy0uOGwuOC43YTIuOCAyLjggMCAwIDEtMS40IDFjLS4zLjItLjcuMy0xLjEuM1oiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii40IiBkPSJNNjEuNCA0NnYuOEg1OFY0NmgzLjRabS0zLTUuOXY2LjdoLTEuMnYtNi43aDEuMVptNi44IDUuN3YtMi4zTDY1IDQzbC0uMy0uM2ExIDEgMCAwIDAtLjUtLjFoLS41bC0uMy4zLS4xLjRoLTEuMWwuMS0uNi40LS41LjctLjMuOC0uMWMuNCAwIC43IDAgMSAuMi4zIDAgLjYuMy43LjUuMi4zLjMuNi4zIDFWNDYuM2wuMi41aC0xLjJsLS4xLS40di0uNlptLjEtMnYuN0g2NGwtLjQuMmEuNy43IDAgMCAwLS4zLjZ2LjRsLjMuM2guNWExLjIgMS4yIDAgMCAwIDEtLjRsLjEtLjQuNC41LS4yLjQtLjMuNGExLjcgMS43IDAgMCAxLTEuMi40Yy0uNCAwLS43IDAtMS0uMmwtLjUtLjVjLS4yLS4yLS4yLS41LS4yLS44bC4xLS43LjUtLjUuNy0uM2gxLjhabTUuMSAxLjd2LS4zbC0uNC0uM2EzIDMgMCAwIDAtLjctLjJjLS4yIDAtLjUgMC0uNy0uMmwtLjYtLjMtLjMtLjQtLjEtLjV2LS42bC41LS41LjYtLjMuOC0uMWMuNCAwIC43IDAgMSAuMi4zLjEuNi4zLjcuNS4yLjMuMy41LjMuOGgtMS4xbC0uMS0uMy0uMy0uM2ExIDEgMCAwIDAtLjUtLjEgMSAxIDAgMCAwLS41IDBsLS4zLjNhLjYuNiAwIDAgMCAwIC41bC4xLjIuNC4yLjUuMSAxIC4zLjYuNS4yLjdhMS4zIDEuMyAwIDAgMS0uNSAxLjEgMiAyIDAgMCAxLS43LjNsLS44LjFjLS41IDAtLjggMC0xLjItLjJsLS43LS42LS4yLS44aDFsLjIuNS40LjIuNS4xaC41bC4zLS4zLjEtLjNabTQuNS0zLjZ2LjhoLTIuOHYtLjhINzVabS0yLTEuM2gxLjJ2NS4ybC4yLjJoLjNhMS43IDEuNyAwIDAgMCAuNCAwdi44YTIuOCAyLjggMCAwIDEtLjcuMWwtLjctLjFhMSAxIDAgMCAxLS41LS41bC0uMS0uOHYtNC45Wm04LjYgNVY0MmgxLjF2NWgtMXYtMS4yWm0uMi0xaC40bC0uMSAxLS40LjctLjYuNWEyIDIgMCAwIDEtLjguMSAyIDIgMCAwIDEtLjcgMGwtLjUtLjQtLjQtLjZ2LTRoMXYzLjZsLjMuMy4yLjJoLjNjLjMgMCAuNiAwIC44LS4yLjIgMCAuMy0uMi40LS41di0uN1ptMy40LTEuOHY2SDg0di03aDF2MVptMy4yIDEuNXYuMWwtLjEgMWMwIC4zLS4yLjYtLjQuOC0uMS4yLS4zLjQtLjYuNS0uMi4yLS41LjItLjguMmwtLjgtLjEtLjYtLjUtLjQtLjgtLjItMXYtLjNsLjItMSAuNC0uOGExLjYgMS42IDAgMCAxIDEuNC0uNmMuMyAwIC42IDAgLjguMi4zIDAgLjUuMi42LjVsLjQuOC4xIDFabS0xIC4xdi0uMWwtLjEtLjYtLjItLjZhMSAxIDAgMCAwLTEtLjRoLS40YTEgMSAwIDAgMC0uNC4zbC0uMi40LS4xLjV2MWwuMi41YzAgLjIuMi40LjQuNWwuNi4xaC41bC40LS40LjItLjZ2LS42Wm01LjIgMS40di02aDEuMXY3aC0xdi0xWm0tMy4yLTEuNHYtLjFsLjEtMWMwLS4zLjItLjYuNC0uOC4yLS4zLjQtLjQuNi0uNS4yLS4yLjUtLjIuOC0uMmwuOC4xLjYuNS40LjguMiAxdi4zbC0uMiAxLS40LjhhMS42IDEuNiAwIDAgMS0xLjQuNiAxLjcgMS43IDAgMCAxLTEuNC0uN2wtLjQtLjgtLjEtMVptMS0uMXYuMWwuMS42LjIuNi40LjMuNS4xLjYtLjEuNC0uNS4yLS42VjQ0bC0uMS0uNi0uMy0uNGExIDEgMCAwIDAtLjMtLjJsLS41LS4xYTEgMSAwIDAgMC0uNSAwIDEgMSAwIDAgMC0uNC40bC0uMi42di42Wm03LjUgMS41di0yLjNsLS4xLS41LS4zLS4zYTEgMSAwIDAgMC0uNS0uMWgtLjVsLS4zLjN2LjRoLTEuMmMwLS4yIDAtLjQuMi0uNiAwLS4yLjItLjMuNC0uNWwuNi0uMy45LS4xYy4zIDAgLjcgMCAxIC4yLjIgMCAuNS4zLjYuNS4yLjMuMy42LjMgMVY0Ni4zbC4yLjVoLTEuMnYtLjRsLS4xLS42Wm0uMS0ydi43SDk2LjZsLS40LjJhLjcuNyAwIDAgMC0uMy42bC4xLjQuMy4zaC40YTEuMiAxLjIgMCAwIDAgMS0uNGwuMi0uNC4zLjUtLjIuNC0uMy40YTEuNyAxLjcgMCAwIDEtMS4yLjRjLS4zIDAtLjYgMC0uOS0uMmwtLjYtLjUtLjItLjguMS0uNy41LS41LjctLjNoMS44Wm00LjYtMnYuOWgtMi44di0uOGgyLjhabS0yLTEuMmgxLjF2NC45bC4xLjMuMi4yaC4zYTEuNyAxLjcgMCAwIDAgLjQgMHYuOGEyLjggMi44IDAgMCAxLS44LjFsLS42LS4xYTEgMSAwIDAgMS0uNS0uNWwtLjItLjh2LTQuOVptNS4zIDYuM2MtLjQgMC0uNyAwLTEtLjJhMi4yIDIuMiAwIDAgMS0xLjMtMS4ybC0uMS0xdi0uMmwuMS0xIC41LS44LjctLjYgMS0uMS45LjFjLjIuMi41LjMuNi42bC40LjcuMiAxdi41aC0zLjl2LS44aDIuOHYtLjFjMC0uMiAwLS40LS4yLS42YTEgMSAwIDAgMC0uMy0uNCAxIDEgMCAwIDAtLjYtLjIgMSAxIDAgMCAwLS44LjVsLS4yLjUtLjEuN3YuOGwuMy41Yy4yLjEuMy4zLjUuM2wuNS4xYTEuNSAxLjUgMCAwIDAgMS4zLS42bC42LjYtLjQuNC0uNy40LS44LjFabTguNS02Ljh2Ni43aC0xdi01LjRsLTEuNy42di0xbDIuNi0uOWguMVptNS42IDUuN3YtNmgxLjJ2N2gtMWwtLjItMVptLTMuMi0xLjR2LS4xbC4xLTEgLjQtLjhjLjItLjMuNC0uNC42LS41LjMtLjIuNi0uMi45LS4ybC44LjEuNS41LjQuOC4yIDF2LjNjMCAuNCAwIC43LS4yIDEgMCAuMy0uMi41LS40LjhhMS42IDEuNiAwIDAgMS0xLjMuNiAxLjcgMS43IDAgMCAxLTEuNS0uN2wtLjQtLjh2LTFabTEuMS0uMXYuN2wuMy42LjMuMy41LjEuNy0uMS4zLS41LjItLjZ2LTEuNGwtLjMtLjRhMSAxIDAgMCAwLS40LS4ybC0uNS0uMWExIDEgMCAwIDAtLjUgMCAxIDEgMCAwIDAtLjMuNGwtLjIuNnYuNlptMTAgMS41di0yLjNsLS4xLS41LS4zLS4zYTEgMSAwIDAgMC0uNS0uMWgtLjVsLS4zLjMtLjEuNGgtMS4xbC4xLS42LjQtLjUuNy0uMy44LS4xYy40IDAgLjcgMCAxIC4yLjMgMCAuNi4zLjcuNS4yLjMuMy42LjMgMVY0Ni4zbC4yLjVoLTEuMmwtLjEtLjR2LS42Wm0uMS0ydi43aC0xLjNsLS40LjJhLjcuNyAwIDAgMC0uMy42di40bC4zLjNoLjVhMS4yIDEuMiAwIDAgMCAxLS40bC4xLS40LjQuNS0uMi40LS4zLjRhMS43IDEuNyAwIDAgMS0xLjIuNGMtLjQgMC0uNyAwLTEtLjJsLS41LS41LS4yLS44LjEtLjcuNS0uNS43LS4zaDEuOFptNS41LTJoMXY0LjljMCAuNCAwIC44LS4zIDEuMS0uMi4zLS40LjYtLjguN2EyLjcgMi43IDAgMCAxLTEuOC4ybC0uNi0uM2MtLjMgMC0uNC0uMi0uNi0uNGwuNS0uNy42LjVhMS42IDEuNiAwIDAgMCAxLjMgMGwuNS0uNC4xLS43VjQzbC4xLTEuMVptLTMuNCAyLjZ2LS4xYzAtLjQgMC0uNy4yLTEgMC0uMy4yLS42LjQtLjguMS0uMy4zLS40LjYtLjUuMi0uMi41LS4yLjgtLjJsLjguMS42LjUuNC44LjIgMXYuM2wtLjIgMS0uNC44YTEuNiAxLjYgMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjQtLjdsLS40LS44LS4yLTFabTEuMS0uMXYuMWwuMS42LjIuNi40LjMuNS4xLjYtLjEuNC0uNS4yLS42VjQ0bC0uMS0uNi0uMi0uNGExIDEgMCAwIDAtLjQtLjJsLS41LS4xYTEgMSAwIDAgMC0uNSAwIDEgMSAwIDAgMC0uNC40bC0uMi42di42Wm00LjUuMXYtLjFsLjEtMSAuNS0uOC43LS41Yy4zLS4yLjYtLjIgMS0uMi4zIDAgLjcgMCAxIC4yLjIgMCAuNS4zLjcuNWwuNC44LjIgMXYuMWMwIC40IDAgLjctLjIgMSAwIC4zLS4yLjYtLjQuOGEyLjEgMi4xIDAgMCAxLTEuNy43Yy0uNCAwLS43IDAtMS0uMi0uMyAwLS41LS4zLS43LS41bC0uNS0uOGEzIDMgMCAwIDEtLjEtMVptMS0uMXYuMWwuMS42LjIuNi40LjMuNi4xYTEgMSAwIDAgMCAuOS0uNGwuMi0uNlY0My43bC0uMi0uNmExIDEgMCAwIDAtMS0uNSAxIDEgMCAwIDAtLjUuMiAxIDEgMCAwIDAtLjQuM2wtLjIuNnYuNloiLz48cGF0aCBmaWxsPSIjRjM2OTAwIiBkPSJNNjIuMSA4Mi4ydjI1LjZINThWODcuMmwtNi4zIDIuMXYtMy41bDEwLTMuNmguNVptMjYuMiAyMi4zdjMuM0g3MVYxMDVsOC40LTkgMi4xLTIuN2MuNS0uOCAxLTEuNSAxLjEtMmE1LjIgNS4yIDAgMCAwLS4xLTRjLS4zLS42LS43LTEtMS4zLTEuNC0uNi0uNC0xLjMtLjYtMi0uNi0xIDAtMS44LjMtMi41LjctLjYuNC0xLjEgMS0xLjQgMS43LS40LjctLjUgMS41LS41IDIuNGgtNC4zYzAtMS41LjQtMi44IDEtNGE3LjUgNy41IDAgMCAxIDMtMyA5LjIgOS4yIDAgMCAxIDQuNy0xLjFjMS43IDAgMy4xLjMgNC4zLjggMS4yLjYgMi4xIDEuNCAyLjcgMi41YTcuNiA3LjYgMCAwIDEgLjYgNi4xYy0uMi44LS42IDEuNi0xLjEgMi40TDg0LjEgOTZhNDIgNDIgMCAwIDEtMiAyLjRsLTUuNiA2aDExLjhaTTEwMSA4MC44aDQuMnYyM2wtLjQgNEgxMDF2LTI3Wm0xNi42IDE3LjN2LjRhMTUgMTUgMCAwIDEtLjQgMy45IDkgOSAwIDAgMS0xLjQgM2MtLjcgMS0xLjQgMS42LTIuNCAyLS45LjYtMiAuOC0zLjEuOC0xLjIgMC0yLjMtLjItMy4xLS43LTEtLjQtMS43LTEtMi4zLTEuOS0uNS0uOC0xLTEuOC0xLjQtMy0uMy0xLS42LTIuMy0uNy0zLjd2LTEuMmMuMS0xLjMuNC0yLjYuNy0zLjcuNC0xLjIuOS0yLjEgMS40LTNhNi4xIDYuMSAwIDAgMSA1LjMtMi41IDcgNyAwIDAgMSAzLjIuN2MxIC40IDEuOCAxLjEgMi40IDJhOSA5IDAgMCAxIDEuNCAzYy4zIDEuMi40IDIuNS40IDRabS00LjIuNHYtLjRjMC0uOCAwLTEuNi0uMi0yLjQtLjEtLjctLjQtMS40LS43LTJhMy43IDMuNyAwIDAgMC0zLjMtMS45Yy0uOCAwLTEuNC4yLTIgLjUtLjUuMi0xIC42LTEuMyAxLS40LjUtLjcgMS0uOSAxLjZsLS40IDEuOXYzLjFjLjEgMSAuMyAxLjcuNyAyLjUuMy43LjggMS4zIDEuNSAxLjcuNi41IDEuNC43IDIuNC43LjggMCAxLjUtLjIgMi0uNXMxLS44IDEuMy0xLjNsLjctMiAuMi0yLjVaTTEzMiAxMDR2LTljMC0uNyAwLTEuMy0uMy0xLjhzLS43LS45LTEuMi0xLjJjLS40LS4yLTEtLjQtMS44LS40LS43IDAtMS4zLjItMS44LjRzLTEgLjUtMS4yIDFjLS4zLjMtLjQuOC0uNCAxLjNIMTIxYzAtLjguMi0xLjUuNS0yLjIuNC0uNyAxLTEuMyAxLjYtMS45LjctLjUgMS41LTEgMi41LTEuMyAxLS4zIDItLjQgMy4yLS40IDEuNCAwIDIuNy4yIDMuOC43IDEuMS41IDIgMS4yIDIuNiAyLjIuNyAxIDEgMi4xIDEgMy42djguNGMwIDEgMCAxLjcuMiAyLjRsLjUgMS44di4yaC00LjNjLS4yLS40LS40LTEtLjUtMS43bC0uMi0yWm0uNy03Ljd2Mi42aC0zYy0uOCAwLTEuNSAwLTIuMS4ybC0xLjUuNmEyLjcgMi43IDAgMCAwLTEuMiAyLjRjMCAuNS4xIDEgLjMgMS40bDEgMWMuNS4yIDEgLjMgMS43LjNhNC43IDQuNyAwIDAgMCAzLjgtMS44Yy40LS41LjYtMSAuNi0xLjVsMS40IDEuOS0uNyAxLjUtMS40IDEuNmE2LjYgNi42IDAgMCAxLTQuNSAxLjdjLTEuMyAwLTIuNC0uMy0zLjMtLjhhNiA2IDAgMCAxLTIuMy0yYy0uNi0uOC0uOS0xLjgtLjktMi45IDAtMSAuMi0xLjkuNi0yLjYuNC0uOCAxLTEuNSAxLjctMnMxLjctMSAyLjctMS4yYzEuMS0uMyAyLjQtLjQgMy43LS40aDMuNFptMTIuNC0zLjl2MTUuNGgtNC4zdi0xOWg0bC4zIDMuNlptNS44LTMuN3Y0YTguMyA4LjMgMCAwIDAtMy43LjEgMy42IDMuNiAwIDAgMC0yLjIgMi4zbC0uNCAxLjhoLTFjMC0xLjIuMi0yLjMuNC0zLjNzLjYtMS45IDEtMi43Yy41LS44IDEuMS0xLjQgMS45LTEuOGE0LjggNC44IDAgMCAxIDMuMy0uNmwuNy4yWiIvPjxkZWZzPjxjbGlwUGF0aCBpZD0iYSI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTE4LjggMTkuMWgzMS40djMxLjRIMTguOHoiLz48L2NsaXBQYXRoPjwvZGVmcz48L3N2Zz4=", + "description": "Displays the latest fluid pressure telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pressure', label: 'Pressure', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 15 - 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"square\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"compress\",\"iconColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#305AD7\"},{\"from\":5,\"to\":10,\"color\":\"#3FA71A\"},{\"from\":10,\"to\":15,\"color\":\"#F36900\"},{\"from\":15,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":28,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#305AD7\"},{\"from\":5,\"to\":10,\"color\":\"#3FA71A\"},{\"from\":10,\"to\":15,\"color\":\"#F36900\"},{\"from\":15,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Pressure card\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"bar\",\"decimals\":0,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "fluid pressure", + "liquid pressure", + "pump pressure", + "hydraulic", + "pump", + "compressibility", + "compressive stress", + "pipe", + "pipeline" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/fluid_pressure_card_with_background.json b/application/src/main/data/json/system/widget_types/fluid_pressure_card_with_background.json new file mode 100644 index 0000000000..af2ffd2633 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/fluid_pressure_card_with_background.json @@ -0,0 +1,34 @@ +{ + "fqn": "fluid_pressure_card_with_background", + "name": "Pressure card with background", + "deprecated": false, + "image": "tb-image:cHJlc3N1cmVfY2FyZF93aXRoX2JhY2tncm91bmQuc3Zn:cHJlc3N1cmVfY2FyZF93aXRoX2JhY2tncm91bmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48L2c+PHBhdGggZmlsbD0iI0Y3NzQxMCIgZD0iTTI1LjYgNDIuN3YtNGwtMiAyLTEuOS0xLjggNS4zLTUuMyA1LjMgNS4zLTIgMS45LTItMnY0aC0yLjdaTTE3IDMyLjR2LTIuNWgyMHYyLjVIMTdabTAtMy44di0yLjVoMjB2Mi41SDE3Wk0yNyAyNWwtNS4zLTUuMiAyLTIgMiAydi00aDIuN3Y0bDItMiAxLjkgMi01LjMgNS4yWiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik01Mi44IDI0LjhoLTIuNnYtMS40aDIuNmMuNCAwIC44IDAgMS0uMi40LS4xLjYtLjMuNy0uNmwuMi0uOWMwLS4zIDAtLjYtLjItLjktLjEtLjItLjMtLjUtLjYtLjZhMiAyIDAgMCAwLTEuMS0uM2gtMnY4LjZINDl2LTEwaDMuOGMuOCAwIDEuNC4yIDIgLjUuNS4yIDEgLjYgMS4yIDEgLjMuNi40IDEgLjQgMS43IDAgLjYtLjEgMS4yLS40IDEuNy0uMy40LS43LjgtMS4yIDEtLjYuMy0xLjIuNC0yIC40Wm02LjktMi4zdjZINTh2LTcuNGgxLjZ2MS40Wm0yLjMtMS40djEuNWEzLjIgMy4yIDAgMCAwLTEuNSAwIDEuNCAxLjQgMCAwIDAtLjggMWwtLjIuNmgtLjRjMC0uNCAwLS44LjItMS4yIDAtLjQuMi0uOC40LTEgLjItLjQuNC0uNi43LS44YTEuOSAxLjkgMCAwIDEgMS4zLS4yaC4zWm00LjQgNy41Yy0uNiAwLTEgMC0xLjUtLjJhMy4zIDMuMyAwIDAgMS0xLjgtMiA0IDQgMCAwIDEtLjMtMS4zdi0uM2MwLS42IDAtMS4xLjMtMS42LjEtLjUuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS0uOGwxLjQtLjJjLjUgMCAxIDAgMS40LjJsMSAuOC41IDEuMWMuMi41LjIgMSAuMiAxLjV2LjdoLTUuN1YyNGg0LjFWMjRjMC0uMyAwLS42LS4yLS45IDAtLjItLjItLjQtLjUtLjZsLS44LS4yYy0uMyAwLS42IDAtLjguMi0uMiAwLS40LjItLjUuNS0uMS4yLS4zLjQtLjMuN2wtLjEgMVYyNmwuNS43LjYuNS45LjFhMi4zIDIuMyAwIDAgMCAxLjgtLjlsLjkuOS0uNi42LTEgLjVjLS4zLjItLjguMi0xLjIuMlptOC42LTIuMS0uMS0uNGExIDEgMCAwIDAtLjUtLjRsLTEtLjMtMS0uMy0xLS41LS41LS42LS4xLS44YzAtLjMgMC0uNi4yLS45IDAtLjIuMy0uNS41LS43bDEtLjUgMS4xLS4xYy42IDAgMS4yIDAgMS42LjMuNC4yLjguNSAxIC44LjMuNC40LjguNCAxLjJoLTEuN2wtLjEtLjVhMSAxIDAgMCAwLS41LS41bC0uNy0uMS0uNy4xYTEgMSAwIDAgMC0uNC40LjkuOSAwIDAgMCAwIC44bC4yLjIuNC4yLjguMiAxLjQuNWMuNS4yLjguNCAxIC43LjIuMy4zLjcuMyAxLjFhMiAyIDAgMCAxLS44IDEuNiAzIDMgMCAwIDEtMSAuNWwtMS4yLjFjLS42IDAtMS4yIDAtMS43LS4zLS40LS4zLS44LS42LTEtMS0uMy0uMy0uNC0uNy0uNC0xLjFoMS42YzAgLjMuMS41LjMuN2wuNS40LjcuMS44LS4xYy4yIDAgLjMtLjIuNC0uMy4yLS4yLjItLjMuMi0uNVptNy41IDBjMC0uMiAwLS4zLS4yLS40YTEgMSAwIDAgMC0uNC0uNGwtMS0uMy0xLjEtLjMtLjktLjVjLS4yLS4xLS40LS4zLS41LS42bC0uMi0uOGMwLS4zIDAtLjYuMi0uOWwuNi0uNy45LS41TDgxIDIxYy42IDAgMS4xIDAgMS42LjNsMSAuOC4zIDEuMmgtMS42YzAtLjIgMC0uNC0uMi0uNWExIDEgMCAwIDAtLjQtLjVsLS43LS4xLS43LjFhMSAxIDAgMCAwLS41LjQuOS45IDAgMCAwIDAgLjhsLjIuMi41LjIuNy4yIDEuNS41IDEgLjdjLjIuMy4zLjcuMyAxLjFhMiAyIDAgMCAxLS44IDEuNiAzIDMgMCAwIDEtMSAuNWwtMS4yLjFjLS43IDAtMS4zIDAtMS43LS4zLS41LS4zLS44LS42LTEtMS0uMy0uMy0uNC0uNy0uNC0xLjFoMS42YzAgLjMgMCAuNS4yLjdsLjYuNC43LjEuNy0uMWMuMiAwIC40LS4yLjUtLjNsLjItLjVabTcuNy4zVjIxSDkydjcuNGgtMS42di0xLjdabS4zLTEuNmguNWMwIC41IDAgMS0uMiAxLjQgMCAuNC0uMi44LS41IDEtLjIuNC0uNS42LS44LjhhMyAzIDAgMCAxLTEuMy4yIDMgMyAwIDAgMS0xLS4xIDIgMiAwIDAgMS0uOC0uNWMtLjItLjItLjQtLjUtLjUtLjlhNCA0IDAgMCAxLS4yLTEuMlYyMWgxLjd2NS41bC4zLjQuNC4yLjUuMWMuNSAwIC44IDAgMS0uMy40LS4yLjYtLjQuNy0uN2wuMi0xWm01LTIuN3Y2aC0xLjd2LTcuNGgxLjZ2MS40Wm0yLjItMS40djEuNWEzLjIgMy4yIDAgMCAwLTEuNCAwIDEuNCAxLjQgMCAwIDAtLjkgMWwtLjEuNmgtLjRjMC0uNCAwLS44LjItMS4yIDAtLjQuMi0uOC40LTEgLjItLjQuNC0uNi43LS44YTEuOSAxLjkgMCAwIDEgMS4zLS4yaC4yWm00LjQgNy41Yy0uNSAwLTEgMC0xLjQtLjJhMy4zIDMuMyAwIDAgMS0xLjktMiA0IDQgMCAwIDEtLjItMS4zdi0uM2MwLS42IDAtMS4xLjItMS42cy40LS45LjctMS4yYTMgMyAwIDAgMSAxLjEtLjhsMS4zLS4yYy42IDAgMSAwIDEuNC4ybDEgLjhjLjMuMy41LjcuNiAxLjFsLjIgMS41di43aC01LjdWMjRoNFYyNGwtLjEtLjktLjUtLjYtLjktLjJjLS4yIDAtLjUgMC0uNy4yLS4yIDAtLjQuMi0uNS41bC0uNC43VjI2bC41LjcuNi41LjguMWEyLjMgMi4zIDAgMCAwIDEuOS0uOWwuOS45LS43LjZjLS4yLjItLjUuNC0uOS41LS40LjItLjguMi0xLjMuMloiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii40IiBkPSJNNTIuOSA0MS42di45aC0zLjR2LTFINTNabS0zLTUuOHY2LjdoLTEuMnYtNi43aDEuMVptNi44IDUuN3YtMi40bC0uMS0uNC0uMy0uM2ExIDEgMCAwIDAtLjUtLjFoLS41bC0uMy4zLS4xLjRoLTEuMWwuMS0uNi40LS41Yy4yLS4xLjQtLjMuNy0uM2wuOC0uMiAxIC4yYy4zLjEuNi4zLjcuNi4yLjIuMy42LjMgMVY0MmwuMi40di4xaC0xLjJsLS4xLS41di0uNVptLjEtMnYuN2gtMS4zbC0uNC4yYS43LjcgMCAwIDAtLjMuNnYuNGwuMy4yLjUuMWExLjIgMS4yIDAgMCAwIDEtLjVsLjEtLjQuNC41LS4yLjRjMCAuMi0uMi4zLS4zLjVhMS43IDEuNyAwIDAgMS0xLjIuNGMtLjQgMC0uNyAwLTEtLjJsLS41LS41Yy0uMi0uMy0uMi0uNS0uMi0uOGwuMS0uNy41LS41LjctLjMgMS0uMWguOFptNS4xIDEuN3YtLjNsLS40LS4zYTMgMyAwIDAgMC0uNy0uMmwtLjctLjItLjYtLjMtLjMtLjQtLjEtLjZ2LS41bC41LS41Yy4xLS4yLjMtLjMuNi0uM2wuOC0uMiAxIC4yLjcuNmMuMi4yLjMuNS4zLjhoLTEuMWwtLjEtLjQtLjMtLjJhMSAxIDAgMCAwLS41LS4xIDEgMSAwIDAgMC0uNSAwbC0uMy4zYS42LjYgMCAwIDAgMCAuNWwuMS4yLjQuMS41LjIgMSAuMy42LjUuMi43YTEuMyAxLjMgMCAwIDEtLjUgMSAyIDIgMCAwIDEtLjcuNEg2MWwtMS4yLS4xLS43LS43LS4yLS44aDFsLjIuNWMuMS4yLjMuMi40LjNoMWwuMy0uMi4xLS4zWm00LjUtMy43di44aC0yLjh2LS44aDIuOFptLTItMS4yaDEuMnY1LjJsLjIuMWguM2ExLjcgMS43IDAgMCAwIC40IDB2LjlhMi44IDIuOCAwIDAgMS0uNyAwSDY1YTEgMSAwIDAgMS0uNS0uNWwtLjEtLjh2LTQuOVptOC42IDV2LTMuOGgxLjF2NWgtMXYtMS4yWm0uMi0xaC40bC0uMSAxLS40LjZjLS4xLjItLjMuNC0uNi41YTIgMiAwIDAgMS0uOC4yIDIgMiAwIDAgMS0uNy0uMWMtLjIgMC0uNC0uMi0uNS0uM2wtLjQtLjZWMzcuNWgxdjMuN2wuMy4zLjIuMS4zLjFjLjMgMCAuNiAwIC44LS4ybC40LS41di0uN1ptMy40LTEuOHY2aC0xLjF2LTdoMXYxWm0zLjIgMS41LS4xIDFjMCAuNC0uMi42LS40LjktLjEuMi0uMy40LS42LjVsLS44LjJjLS4zIDAtLjYgMC0uOC0uMi0uMiAwLS40LS4zLS42LS41bC0uNC0uNy0uMi0xVjQwbC4yLTEgLjQtLjhhMS43IDEuNyAwIDAgMSAxLjQtLjdjLjMgMCAuNiAwIC44LjIuMy4xLjUuMy42LjVsLjQuOC4xIDFabS0xIDAtLjEtLjctLjItLjVhMSAxIDAgMCAwLTEtLjVsLS40LjFhMSAxIDAgMCAwLS40LjNsLS4yLjQtLjEuNXYuOWwuMi42YzAgLjIuMi4zLjQuNC4xLjIuMy4yLjYuMmwuNS0uMS40LS40LjItLjV2LS42Wm01LjIgMS41di02aDEuMXY3aC0xdi0xWk04MC44IDQwVjQwbC4xLTFjMC0uNC4yLS42LjQtLjlhMS43IDEuNyAwIDAgMSAxLjQtLjdjLjMgMCAuNiAwIC44LjJsLjYuNS40LjguMiAxdi4zbC0uMiAxLS40LjdhMS42IDEuNiAwIDAgMS0xLjQuNyAxLjcgMS43IDAgMCAxLTEuNC0uN2wtLjQtLjgtLjEtMVptMS0uMS4xLjcuMi41LjQuNC41LjFjLjIgMCAuNSAwIC42LS4yLjIgMCAuMy0uMi40LS40bC4yLS43di0uOGwtLjEtLjUtLjMtLjRhMSAxIDAgMCAwLS4zLS4zSDgzYTEgMSAwIDAgMC0uNSAwIDEgMSAwIDAgMC0uNC40bC0uMi41di43Wm03LjUgMS41di0yLjRsLS4xLS40YzAtLjItLjItLjMtLjMtLjNhMSAxIDAgMCAwLS41LS4xaC0uNWwtLjMuM3YuNGgtMS4yYzAtLjIgMC0uNC4yLS42IDAtLjIuMi0uNC40LS41bC42LS4zLjktLjIgMSAuMmMuMi4xLjUuMy42LjYuMi4yLjMuNi4zIDFWNDJsLjIuNHYuMWgtMS4yVjQybC0uMS0uNVptLjEtMnYuN0g4OC4xbC0uNC4yYS43LjcgMCAwIDAtLjMuNmwuMS40cy4xLjIuMy4ybC40LjFhMS4yIDEuMiAwIDAgMCAxLS41bC4yLS40LjMuNWMwIC4yIDAgLjMtLjIuNCAwIC4yLS4yLjMtLjMuNWExLjcgMS43IDAgMCAxLTEuMi40Yy0uMyAwLS42IDAtLjktLjJsLS42LS41LS4yLS44LjEtLjcuNS0uNS43LS4zIDEtLjFoLjhabTQuNi0ydi44aC0yLjh2LS44SDk0Wm0tMi0xLjJoMS4xdjQuOGwuMS40LjIuMWguM2ExLjcgMS43IDAgMCAwIC40IDB2LjlhMi44IDIuOCAwIDAgMS0uOCAwaC0uNmExIDEgMCAwIDEtLjUtLjVsLS4yLS44di00LjlabTUuMyA2LjNjLS40IDAtLjcgMC0xLS4yYTIuMiAyLjIgMCAwIDEtMS4zLTEuM2wtLjEtMVY0MGwuMS0xIC41LS45LjctLjUgMS0uMmMuMyAwIC42IDAgLjkuMmwuNi41LjQuOC4yIDF2LjRoLTMuOXYtLjhoMi44YzAtLjIgMC0uNC0uMi0uNmExIDEgMCAwIDAtLjMtLjQgMSAxIDAgMCAwLS42LS4yIDEgMSAwIDAgMC0uOC41bC0uMi41LS4xLjd2LjhsLjMuNS41LjMuNS4xYTEuNSAxLjUgMCAwIDAgMS4zLS42bC42LjYtLjQuNC0uNy40aC0uOFptOC41LTYuOHY2LjdoLTF2LTUuNGwtMS43LjZ2LTFsMi42LTFoLjFabTUuNiA1Ljd2LTZoMS4ydjdoLTFsLS4yLTFabS0zLjItMS40VjQwbC4xLTEgLjQtLjkuNi0uNS45LS4yYy4zIDAgLjUgMCAuOC4ybC41LjUuNC44LjIgMXYuM2MwIC4zIDAgLjctLjIgMSAwIC4zLS4yLjUtLjQuN2ExLjYgMS42IDAgMCAxLTEuMy43IDEuNyAxLjcgMCAwIDEtMS41LS43bC0uNC0uOHYtMVptMS4xLS4xdi43bC4zLjVjMCAuMi4yLjMuMy40bC41LjFjLjMgMCAuNSAwIC43LS4ybC4zLS40LjItLjd2LTEuM2wtLjMtLjRhMSAxIDAgMCAwLS40LS4zaC0uNWExIDEgMCAwIDAtLjUgMCAxIDEgMCAwIDAtLjMuNGwtLjIuNXYuN1ptMTAgMS41di0yLjRsLS4xLS40LS4zLS4zYTEgMSAwIDAgMC0uNS0uMWgtLjVsLS4zLjMtLjEuNGgtMS4xbC4xLS42LjQtLjVjLjItLjEuNC0uMy43LS4zbC44LS4yIDEgLjJjLjMuMS42LjMuNy42LjIuMi4zLjYuMyAxVjQybC4yLjR2LjFoLTEuMmwtLjEtLjV2LS41Wm0uMS0ydi43aC0xLjNsLS40LjJhLjcuNyAwIDAgMC0uMy42di40bC4zLjIuNS4xYTEuMiAxLjIgMCAwIDAgMS0uNWwuMS0uNC40LjUtLjIuNGMwIC4yLS4yLjMtLjMuNWExLjcgMS43IDAgMCAxLTEuMi40Yy0uNCAwLS43IDAtMS0uMmwtLjUtLjUtLjItLjguMS0uNy41LS41LjctLjMgMS0uMWguOFptNS41LTJoMXY0LjljMCAuNCAwIC44LS4zIDEuMS0uMi4zLS40LjYtLjguN2EyLjcgMi43IDAgMCAxLTEuOC4ybC0uNi0uM2MtLjMtLjEtLjQtLjMtLjYtLjVsLjUtLjYuNi41YTEuNiAxLjYgMCAwIDAgMS4zIDBsLjUtLjUuMS0uNnYtMy43bC4xLTEuMlptLTMuNCAyLjZWNDBjMC0uNCAwLS43LjItMSAwLS40LjItLjYuNC0uOS4xLS4yLjMtLjQuNi0uNWwuOC0uMmMuMyAwIC42IDAgLjguMi4zLjEuNS4zLjYuNWwuNC44LjIgMXYuM2wtLjIgMS0uNC43YTEuNiAxLjYgMCAwIDEtMS40LjcgMS44IDEuOCAwIDAgMS0xLjQtLjdsLS40LS44LS4yLTFabTEuMS0uMS4xLjcuMi41LjQuNC41LjFjLjMgMCAuNSAwIC42LS4yLjIgMCAuMy0uMi40LS40bC4yLS43di0uOGwtLjEtLjUtLjItLjRhMSAxIDAgMCAwLS40LS4zaC0uNWExIDEgMCAwIDAtLjkuNGwtLjIuNXYuN1ptNC41IDAgLjEtMSAuNS0uOC43LS42IDEtLjIgMSAuMi43LjYuNC44LjIgMWMwIC40IDAgLjgtLjIgMSAwIC40LS4yLjYtLjQuOWEyLjEgMi4xIDAgMCAxLTEuNy43Yy0uNCAwLS43IDAtMS0uMmwtLjctLjUtLjUtLjhhMyAzIDAgMCAxLS4xLTFabTEgMCAuMS43LjIuNS40LjQuNi4xYTEgMSAwIDAgMCAuOS0uNWwuMi0uNXYtMS40bC0uMi0uNWExIDEgMCAwIDAtMS0uNSAxIDEgMCAwIDAtLjUuMSAxIDEgMCAwIDAtLjQuNGwtLjIuNXYuN1oiLz48cGF0aCBmaWxsPSIjRjc3NDEwIiBkPSJNNjIuMSA4NS4zVjExMUg1OFY5MC4zbC02LjMgMi4yVjg5bDEwLTMuN2guNVptMjYuMiAyMi4zdjMuNEg3MXYtMi45bDguNC05IDIuMS0yLjhjLjUtLjcgMS0xLjQgMS4xLTJhNS4yIDUuMiAwIDAgMC0uMS0zLjljLS4zLS42LS43LTEtMS4zLTEuNC0uNi0uNC0xLjMtLjYtMi0uNi0xIDAtMS44LjItMi41LjYtLjYuNC0xLjEgMS0xLjQgMS43LS40LjctLjUgMS42LS41IDIuNWgtNC4zYzAtMS41LjQtMi45IDEtNGE3LjUgNy41IDAgMCAxIDMtMyA5LjIgOS4yIDAgMCAxIDQuNy0xLjFjMS43IDAgMy4xLjIgNC4zLjggMS4yLjYgMi4xIDEuNCAyLjcgMi41YTcuNiA3LjYgMCAwIDEgLjYgNi4xYy0uMi44LS42IDEuNS0xLjEgMi4zYTQxLjYgNDEuNiAwIDAgMS0zLjggNC43bC01LjUgNi4xaDExLjhaTTEwMSA4NGg0LjJ2MjNsLS40IDRIMTAxVjg0Wm0xNi42IDE3LjN2LjRhMTUgMTUgMCAwIDEtLjQgMy45IDkgOSAwIDAgMS0xLjQgMyA2LjkgNi45IDAgMCAxLTUuNSAyLjdjLTEuMiAwLTIuMy0uMi0zLjEtLjYtMS0uNS0xLjctMS0yLjMtMi0uNS0uNy0xLTEuNy0xLjQtMi45LS4zLTEtLjYtMi4zLS43LTMuN3YtMS4yYy4xLTEuNC40LTIuNi43LTMuOGwxLjQtMi45YTYuMSA2LjEgMCAwIDEgNS4zLTIuNiA3IDcgMCAwIDEgMy4yLjdjMSAuNSAxLjggMS4yIDIuNCAyYTkgOSAwIDAgMSAxLjQgM2MuMyAxLjMuNCAyLjYuNCA0Wm0tNC4yLjR2LS40YzAtLjggMC0xLjYtLjItMi40LS4xLS44LS40LTEuNC0uNy0yLS4zLS42LS44LTEtMS4zLTEuNC0uNS0uMy0xLjItLjUtMi0uNXMtMS40LjItMiAuNGMtLjUuMy0xIC42LTEuMyAxLS40LjUtLjcgMS0uOSAxLjZsLS40IDJ2My4xYy4xLjkuMyAxLjcuNyAyLjQuMy44LjggMS4zIDEuNSAxLjguNi40IDEuNC42IDIuNC42LjggMCAxLjUtLjEgMi0uNC41LS4zIDEtLjggMS4zLTEuNC4zLS41LjUtMS4yLjctMmwuMi0yLjRabTE4LjYgNS41di05YzAtLjggMC0xLjQtLjMtMS44LS4zLS41LS43LTEtMS4yLTEuMi0uNC0uMy0xLS40LTEuOC0uNC0uNyAwLTEuMy4xLTEuOC40LS41LjItMSAuNS0xLjIgMS0uMy4zLS40LjgtLjQgMS4zSDEyMWMwLS44LjItMS41LjUtMi4yLjQtLjcgMS0xLjMgMS42LTEuOS43LS41IDEuNS0xIDIuNS0xLjMgMS0uMyAyLS41IDMuMi0uNSAxLjQgMCAyLjcuMyAzLjguNyAxLjEuNSAyIDEuMyAyLjYgMi4yLjcgMSAxIDIuMiAxIDMuN3Y4LjRjMCAuOSAwIDEuNy4yIDIuM2wuNSAxLjh2LjNoLTQuM2MtLjItLjUtLjQtMS0uNS0xLjdsLS4yLTIuMVptLjctNy44djIuN2gtM2MtLjggMC0xLjUgMC0yLjEuMi0uNi4xLTEuMS4zLTEuNS42YTIuNyAyLjcgMCAwIDAtMS4yIDIuNGMwIC41LjEgMSAuMyAxLjRsMSAxYy41LjIgMSAuMyAxLjcuM2E0LjcgNC43IDAgMCAwIDMuOC0xLjljLjQtLjUuNi0xIC42LTEuNGwxLjQgMS44Yy0uMS41LS40IDEtLjcgMS42bC0xLjQgMS42YTYuNiA2LjYgMCAwIDEtNC41IDEuN2MtMS4zIDAtMi40LS4zLTMuMy0uOC0xLS41LTEuOC0xLjItMi4zLTJhNiA2IDAgMCAxLS4zLTUuNmMuNC0uOCAxLTEuNCAxLjctMiAuNy0uNSAxLjctLjkgMi43LTEuMSAxLjEtLjMgMi4zLS41IDMuNy0uNWgzLjRabTEyLjQtMy44VjExMWgtNC4zVjkyaDRsLjMgMy42Wm01LjgtMy43djMuOWE4LjMgOC4zIDAgMCAwLTMuNy4yIDMuNiAzLjYgMCAwIDAtMi4yIDIuMmwtLjQgMS44LTEgLjFjMC0xLjIuMi0yLjMuNC0zLjNzLjYtMiAxLTIuN2MuNS0uOCAxLjEtMS40IDEuOS0xLjhhNC44IDQuOCAwIDAgMSAzLjMtLjZsLjcuMloiLz48ZGVmcz48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2MiIHRyYW5zZm9ybT0ibWF0cml4KC4wMDUwMzE0NSAwIDAgLjAwNjI4OTMxIDAgMCkiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjE5Ny40IiBoZWlnaHQ9IjE1Ny40IiB4PSIxLjMiIHk9IjEuMyIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMi40Ii8+PGZlQ29tcG9zaXRlIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzE1MDdfMjk5ODciLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzE1MDdfMjk5ODciIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48aW1hZ2UgeGxpbms6aHJlZj0iZGF0YTppbWFnZS9qcGVnO2Jhc2U2NCwvOWovNEFBUVNrWkpSZ0FCQVFBQUFRQUJBQUQvMndCREFBTUNBZ0lDQWdNQ0FnSURBd01EQkFZRUJBUUVCQWdHQmdVR0NRZ0tDZ2tJQ1FrS0RBOE1DZ3NPQ3drSkRSRU5EZzhRRUJFUUNnd1NFeElRRXc4UUVCRC8yd0JEQVFNREF3UURCQWdFQkFnUUN3a0xFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRUJEL3dnQVJDQUNmQU1jREFSRUFBaEVCQXhFQi84UUFIUUFBQWdNQkFRRUJBUUFBQUFBQUFBQUFCZ2NFQlFnREFnRUpBUC9FQUJzQkFBSURBUUVCQUFBQUFBQUFBQUFBQUFJREFBRUVCUVlILzlvQURBTUJBQUlRQXhBQUFBQjc2Z3pabkpqRk5ON0Y5NU8wbnFUeEpXeVVWWFFMTG5JUXNBaHVXMG5xVG5ZOUt0ZW9iaGJuUDJQMGM2VnlNZk8wRmVsd3ZtMU1yWHphU1JKODdadDNzNFNPNUlrOVZJbHlqa293SWVXVWVpdURBbFlKSmRUcFBzbk9SRDgvWGxiTVdvTitZQ1FlT2gwb1NHU3hmNldOeW1yMUp6bmJORTlQS3lHaktxZjBrQzRPMUJ4YklJWDdrNjNMVXhJakMva2xqSWhSQ1l0SytGWnF5RWJhUVNXWWd6N05LTE5yUDV6TjFwRHNqM0hzV2Z1Qy9xUnFsTklOQ1ZZTi93Qkw5WGZxVHBCdGpHL0tybXBVU0xGTEtFbExiRHBmTzlOcGRZZDVYZkRrZGQzNi9IdkxZQStnMnB1Ukp1RWtuZ1lESWIzYU1LcTh5K2duRkM3Rm9mYnExTVo4b1pRMzIwSzRsNTM1RzNWWFZ5S2JQdnhwaTlmVmoxTm03dm1FMUxsOW1jMk55R1ZyUjF1b2ltS2puYk9yMHNIWWp6VUNjanh4TEdmdXkyYkI0U3dERm9uZ1ovMDhVZUFtT2ZwMER2UXVrYU1WOG4yWXBmWTFOMVBsemhqTTU0ZEN6U3hnNjgrc2R5Uk5EVi9oMEkxSk9qZG1kR3RLOXp0cEVQc2pCb2JNNlF5TkFjN25JMFRYYm12bnFRZk9mcWZwSXd2eitqa0lHZjBWK3BlckhaMFNndzZWUlZtckt0V29JU3RnbE1UWVhNZVc4ZDJlT3hWZ1ZjVWFXUzVLSXl0VDZXYWIxSlN1WmhJRGJwNnRIOUxKK1IzTzFKQWx6Q0g5Z29XYitic3N3WTlleHpaN2xFWjM4a3FCcXdTNFFDempXZ2h1YzZrNnhFd1ltc0doc1BBZkc3cGcrSkJOSmxKbWxvakE2V2ZMbjY1NmxaNDUyc09rMU4xTVBLNjdpVTR4dTd2K3FWcW1RV2p5RVJSWm5qZ3RicEM4elk5OXFLMlhkRldja2s4R1VLcXNpY3Y4aGNUUVpvZnFYYXBLTmE4QTloOWZuM1Z3YkF3Z2FiN1JEMXRWMko3bjM1cldWRW91dDE5cVloNUcvVy9ReTN6UlJtZHJNTVNwcXc5WlRybjViWlR6cXdOZjRhTEU5QXh1OVhkYm4yR2xKQmRjcXZqTHl4eWQ0RG1kdHZzODY4MEttU3E0U3k1aDAzSlV3THExTWJpN2xGUS9WMWFpVU9SK0YxbW1OMkxmSFAwVWlkR205cXgwYk5kdVJvNlUxNm1LakhvQThXbUtKQVd6RzZvZjBxOUxKSWpOYzlITGVHQ2R3YW50dHoySlY4RXNyOC9Ua2ZPOFZZQ1kzNGYxZFV6eG0wV0d6T1VzRElQTDJYamwzcTJRN21sK2prZ0FWQXBpanlPcWdKLzlESlpsWlk5SXdONTk1TzlzNjg1anBTTWhlS3Vmc2lYQTFxeUZUTnE3Y2c1bmNzRldTbUhNVEJRTjhPQkhwTm1OVjlxNHdzQ0FaN3FHeFhKTkhPVlFWZDFaVUs3clNHcXF4VXFxcm93QzQwdmZmYjU2aXhQRVFtbGVqbmpnUWdobE9saUt6czlTNDQzTGtLN25PNnBxSzhLclM2V2d4cm5SS3hkMmNnQVhLU3FXUWlzbHNzdFRkVEVvY2J6N2JuMGJzUnprQWttck1PbE5vWk9FcDFGL1NvWmpiWElhajdTZWF0VkZUNU1lVjBQb1pFa0RLblM3L3BVdTYxSDA4YUN5TzBydnorNUlZRkJFa0hsYlZnVGFPdk5GOWtVZ1M4TVh2clZQTVoxMVJCZWFNR2w3NmtzelN1RU1oVVZNc3hsQmpTVGF2UnlwM014em1Va3EreVF4SVl0Uy93QXpybFRyVzdnRFkweEJHMFdPNnJ0Z1R5cnRKbkxFeHE2QWFPbFh5NVdEWTBod3VrcVVZMWVqalEyRFJzRHE1UGQxOWtxNnZOL0wxeUhxYnVwZFdCZTVGVGlkMm9uZDFNZGhLbFM1RXJKUEoydHpVcHc3TS9HU29sMTlRWVN3VVNmL3hBQXNFQUFCQkFJQkJBSUJBd1VCQVFBQUFBQUVBUUlEQlFBR0VRY1NFeFFWSVNJV0l6RVFKQ1V5UVRORC85b0FDQUVCQUFFSUFkQ2N5U2xpWW5XMnVuRW5IdTRPbVc3TnRRWTRYeHllUnZPY1kxbWR1UFJNbFZFeVoyU3lxbVJUcXE0UE5rTW5PTnhWL29tYlpiL0dBelQ1RmV4N2J0c0VCTnMyTWVoV0xOVzFlQU8yOTZDd1BEcllmS1phZFRkUnJvMThtazdaVTdOYWt1RDZabXRscm1zVHJCRWsxUEpEbWlqTnFDR05Tc0lTU0ZPRzRuOUpNbVhKbi96a3YzamVVWElKRndlWElIOHBuQ1p4aXIycHpuVUdkcndaSWMxYW45UGJCeXMzYWRmZ25OYm8vc3hRRUZUOVQrb3gyMG55QnhQdURwbStQTldJMlJ0Z253UFRlYU5DM1JSZFEyUlN3TVk2c0FiQk0xTW81ZTFpTnlKZTVPY1JNVmVNa2RoRC93Q2NsZG5iem5aamZySUpPTWdseUovS2YwbmQyc1hOdEc5dkl4WWhqQmxiZG13SkZFMmJYaFJaSUZXTGN1aDlUWnpTbTA5eFFUMFZ2TlNyMFhFaFErV2FmU0dsMUpzVXMyNk1uT1NCSVFBNW95V3BNQkVzYUlxQlRjcHhuS2NZOWNuY3FaUEppcHpuSEdjWWpjYjlaQkprTTJNaytzTmsvRlVTeWo3K2U0Nm1iNEdsSWI3RnF4RWgxY1pSNjVqSEh6UkRRdm5sMzNhbEltWllVdEJhVzhmSldIMnNvbXcvRzRLSklhTDd6MFlyVGtXZUNEOW42R2M2TjJSejhwazVEWW1keS9NeXVJZEdROXZlbmMxV3EzRXgwSk0wYm1pQXRKWkNyVCszR0txTGtMK01lWjJNK2d6elRDWkl6anhrZEFxNFFITStyZDIxcFpJaGp4bjFxY0JvNWVxYzVNR3BsU2lucStTaGJHdE81ZVhSNXROR0cyMlU2YXYybXVIcDJ3eTMyemdTSXh3dXE3WkNjS3pJcEdUOTNZMTZ0WExBK0FXSnNwRmpiQXp1ZjRkWTJGaGtYZ2NpbzlPN0hOUkU1eHU1VmdCc29aRSs4Vkxub3pLOHhwbzdaazR5VXBzRGVWbDJvUDNXNVliQlZMRk1pU044bFl4NlFGS2xhNXFnamhrSC9nT3pzR2FtYnlIQWJyUnNCRmdUVEUxZnBoQnFHRzd1a3F5bmJYcmxOYkYyTmFKRkV5TWJxSlZBa0E4c283eVRYcXYwbmRQK3Bzb0pFd0Y3RGNCRVF0bnpZSFYxbzJNYVkvWEtJMkIzTUU3dFhZZTRiVCtvTUp3a2NCbnZpbENxNkNDS3NIZTZaMHJkZU9uWHpnejNkQkh5UUdiQ2JFa2tQVWJZM1ZVb3NMS0dycU54dTNPUHM5UTE1OENpTXJib0g0NUs5QjV4WDE2cXV1UVBuMnVlWEZUc2hUT3RYVWVZVC9BVlRBb1M0bXFRbFhSTS93RFZCNHRZb2Flamltc0VlNW9wVzFCUVR1UnJwK2wzeWRqQVdGRytDbWtrck5ERXI5cGU3ekd4SmZBT1J3MFR3ZHJGZlhFYkxwbXo2enNNZnZhaG9kcGVXVGx5eThjTWE2MXJvV2pBV2JQSkpOb0ZZRkE0bklXV2RETzJjZXVyS3lVZGJhbDZydGlTc1JHZFBkTjJPdnNoYmNuYkp5YTFHd3cvSDdWUElrcU5PM0VTTHNacmV4MWJMVHN1Q0c5OFN0VHEvWHhWMjFseHRWWElJK0NRT3E4aWN5YnRLY0dVL3dCQnN4dFdXNjN1S3VhV3dpVHV0Njl0UlVDNnRXVU9xaVFRTWpXa3J5WTJNRk1zSzR3aWR3VU5yUk5IVkp4N2NPTFp0WG1oSTBlcExvTkVaSWxEU3d3TWE5SWFqd0hQTnd1ckF1WkVJZWNIQ1JHcU9wMG1Bc3BJTTNPbW1KMjBSU0tlSmEycW11U1FLaWEwbmVTWlNCeHUvc2liVUdhWW40K0M4MXRyUDNJOWF1SFNDdURNNmg5TEtTelBkdE11empORXRDWTR3V2VkcldwdWRCQmJLMTBoSFRjVjA3dmE2YzBJTE9xYldRMjBaY2RpWEtGQnROMExNc1ZpQjFBMXU3bVN2Q1p2dW9RenhWenl5N0I1TWdpVWpYZUtkcjZtZEM5UUVtWTRtMVJyZlROMkRldmM3SzI2TjZtR3dlUFdxYWxOUG9nRjJiMDBnc280d09vRjh3UGFoaHN2a1NNRWVGb1Z2QkZNeXZ3bTJybWV4QTQvZk5vYjFDR3BxeXpzQ0drcUMybVlyTGRHUHNvZmFvSHd2M1d6Smt1NTRwR215OHRSS2JjaXE0ZzZ1bHMrb0ZON1VubDE2eDcrbzBHMFZ0akI2OW8yZFBhZ2JQR0l0bFRnZUwxaDM5TTlQblBFdkY5aUo0L25nUEtaU1VwVnNSMFl2R1d0T1pRa0VXVEtnR1Z4UlJrRUZTNHl3YjZWWDRKWlBiTmhMN1N2TkNERlBjRkFndTZpN0JZV09DbXBmYTB3dVAzaDRnL2JJdUIzN1FWT1JUVXRjRXlOWTUya0R6bzVJYTlzZnl6eTVSSDkxWkZLN3JiRUkvYjV5ZzI4L3dESVBPVFppcUs2M3NxWUd2SG5xOXhJcjYvMUFOUHZBOXAxNGVKMElVdGNmSWJLMk1oRG5GdVNDYnVKOW9VRnNNY25mMVgzUDM1ZmhRTkZNL1RpdGxkV1gxWHNvYkRFZ1JGYTVramxUamxacGhXTzVsNnY3akpOUUV4RGRMcXlPcW9SMHlyczRhTFppS3dyWXRlbk5aSERqbkV6aXcvRFRzT2ZZanpReGdldFAvanJxY2NPQlEyN1YxQmJHMVJLM2NlWlh5enl4OXZQR1U5SU9sbklQbG5SR2tTRXZOMWZWMlI2Mkg1TnJXdzA0VDUrbzBucXRTYlRENFpaYlVYK1dTN1hUaHh1a0l0ZCsvVVJhMDJ2THFwSDNJNXRET3hlSEVGN0ZyTzVnUjFCM1VTenBuZUkybjZzdXRaNGd4YjJ5MmkwamRDemFOVTJPMHJjMXlsbUZyaG9aZGpvQmJDbW1qbTFIcTRHMWpRTGNUWTljc1UvdDNTZ0luZGlNc0QvQU1LbnFkYkQwSlh4bzRWbThxYVJwRjFIQStWNllxS2oxVE5GbVEvWUpremNsakFyRFZYcDV0TWhHcmkrOXNSSU41VUVWOGxld3JWcGtZTnZPNEZXbFcxdFJyZXJ4bjZNUVhZNnROQnFtNFJDcytmQS93Q3pYbGVxTDJwWTFKSkxaNXJDWkx2YmpVbkpqam9DYUtjRDU4SkYrNi9Zd083N1hhd1dOelpkNmpycUEwc2ZSOVhCczVLeWVlbXN0ZEhjUlhud2JiUmd2ZE9MZjlXT3dWNlEzR3hlNlZNWWRxUHEzRm05RjJLcUtDdUpzcWFPRkl1OXVqMTFvSHN0bzNPb1lFL2tJU0dySkxpQ2lXR0EwNlJQeWZFNmRWYzQ3bUNWU29LZ214aDFPTUJGbUx0ZHU3cEdpT2MzbFlna1ZmektFZXo4b3hLbzR1NU1mRGMwaFlzMWZMTTRNcDM4TXJpR001Y3RjWC90bDRFVExVRXN6U2JYWXg0UTVjbkV2RFR5aVNaSzYyalh0V2Ftc0hweSs3MDB3Z1JYdzZwcmR0V3NmTThrWStaMzduaHQ0V2NSU3JKQjVIaTc3RkkrbE9KVFFGanN0aWtBYTNXQVZ4TlJCamVyMjJXdVY0SkRPVGg0RWpZd2UrSC9BRXlkRmJWcnQxSDdFNzAzUWRNWnViQ2Y3ZUduRGdNa21KazJldGlpQVlZd0hjcFJZL0VhemN3NVA5azIydWN6akx2Y0ZzU1g2Mk5WYSsydUNpSGppN2ExSHpLbHBSUCsxSXQ2QnIrMVhYbXVKK0t5N0JyamVVU3gyV2lpVDlvM2JSdi9BSmxSWFRyU2VGZHcrYm5ETFpKMHIxS0VJRWZabFJFUk1WMmJUN1E0ajdNY2F4T3NBSXJCMnhGV1YwU0dCREZxYldqc2JPelZSTWRxY0hIN090Zk5sU0hKbTBRM2FWVTAyQ2FzM3dvOGo5TWpZdXRqb21YMUVYVFdMZGhoQ3ZEakVqVENyeXhsbWRWNThKRWljTEpyMEw4c2RaVnN2NE8xeHJvOGgxeUJmeHgydFFKL0owQzJ4eW9QdDlQUFV4eCtldlBCclFvSzRHYTlrVC9WdDlML0FOc0xUM0E1eHBnOVczVXV0SGxpMU1LMmJlbnczbmtienhubFltZXpFMXZPUTFGdVZzcG85RFBRYklJUUJCWlJEaWR2Q1JnQ1B4S1VSeVpzR3BCMjFHWFhyckpXNzJyWTBHMCtvS09PTUx2SktJRmMrQUR5V25EVDZ5Y1FLSkZUSkl4SStYWVFSSDNjTmdFY0VlUTErK1J5bHoxWXF4YXk1anU3UGdKRitzVFg1UDRVM1g1V2l6T1NyTGlqcHhXSnFJYjdXN3ZwNFRLR3ppSSttVUJUbzhuMW81VStxWWVlcjJnb2FmWVh6em1VNHpJYWlaY2pyWlkwK21RVHR4NDB6NFhvdWdsUVJCT2dYVVoySFdGc283QjMvd0RYUXJrc0tJaTVQWHVsVlZ3eXBWZnBQUWpoL3dCN0ppL0xsUFMyYzRuYktBWi9qYmlNYml0WmxrNWtkY1UvSzBsL29EOGRLNVcrTzdWNWpvWDVHUTJQNnh4YkhmU0h2YTdjSGQxdEwva2FWbVJweGpjYTFNN0U3VnpVWW05eG5HbGVObHRhd3g4WS9qakpleEZ5V2VOcVlRVXpDWld2ei8vRUFFQVFBQUlCQWdNRkJRUUhCZ1VGQUFBQUFBRUNBd0FSQkJJaEV5SXhRVkVGTWtKaGNSU0JrYUVRSUNOU2NySEJCaVJBWXBLaUZUTTB3dkJEVTFTQzBmL2FBQWdCQVFBSlB3SGtscVgvQUU3YjF1bGFTUmdBL3dBQjRGdlY5MXI1TXZDMUhMZExYcDJZV3RyV0lTSkFPTEcxZHB4dnB3UTMvS3BObVFNd0Q2WEZIaFVkODVBclQrQVBmRnE0TmNWeElBcDNmSndCcERIaGNOSVFGKytSek5XMDAwcFpwTVVWTzRndnB6cTF2TGhYaVA4QUJEVU5SRmkxdGFBS1B4dFQreXp5T1hmN3JlNnNLMG1KZ2J3amlLd0RSVDdLMjh2RVVsbzVqOEsxMXZYdy9nVzdtOVJGb2p4UFdocUZvMlZCYzEyU2lobXllMXVPTmRvQlc2SWxRQ01Jd04rdFNXSEJSVGk5N2ZVMXJEN0pPVFpxNEg2U2l1QnhhcDBlWGxsRnIvVTFQSVZoaGhveDNXWitQMGNjdWxJSHpTMnRRNGlwQ2pEaWZLcjVZM0RDdUZxajF0YTk3V3FmTTJia09WWXBOSkFYNnFLZk9wNTF5bzFJcW9HNG1zWkdUMHZVd1oxNWZUTWRxVjBSUmNtcDJna1BjRWkyelVlSStuRUFiS1RLNEp0WUhuV1BoTFd2M3FON29LSGRGS3Q5b1dQWDZHdEdVTnpVaEVpV1ZpM0ExakkvNnFTY1lkc1NVdkkxMmtoRFd1YmN1WG9Ld2tTUkFhYVZoa1NiV3pycGM5S2x2aVc2Y3FNOHpTeUhadUZ2cDBwbVhOL0l4L1NpWkZSczl0VjE5OVF2Q0I0d2JXckZGOHJsY08xOVdxT1JabDBkbUdsUG5MRGRWZFNUNUFWSGhEaTEvd0F4dHBtSXZ5T1VHMVRRNWdkM05CSUZCL0ZscnMvYjRNdHBpY080a1JSNTI0VTRQcFJjWm0zcmRLeGJDTkl3VVZIeTdRK2RZZHpkYk5ZOHF4S1pzTU5ucTNTblFramthN2lrNlZ5RlRLTnJ1ek4wcnRSNDI4VmpYYUx1ZngxZ0h4SncwVU9GeVJnRTU4dDJZMzVYdldSbmZnVjVWTG1DU1pTb3J0UDJUQnhEYVl1ZVhWWTBINjlCWFozMnlqTFBqNVFHblA0bWJkaS9DQVQ2VjI4N0c5MkN4bVQrNWpXTzJ5YzQ4UWhVTjhOUGxRT0J4NWp0bGF4VmdPWXRvdzlOYU8xd3p0WlhIY0ZZL1pZWG5rKzd6cEJCaG9mc3A1dThTZWEvek44aDVtc0Y3U0ZZb2RzN0d4R2hHVWFMOEs3SncwV3lUT1NoWUVhYTJzYWprYUlydndTSGVaZkp1WjhtcXlKTU16eEFXRitlbmhQVVVpN2R0RXBibDMwZ0huVW0zbm1Ha1NYVVc1M3R2SDRnVjIwMkRTK2JaWVpWaitPVWEvR3NldUl0NFpiUGYzT1AxcnNZZG1ZdVR2WW1HK3hQNDBQYy9FTjJtRmlOQ0t4SmxrZDlwWW5WYjFoMjJyTmNQZmdLdlFWbmRlQmZMV050SU5Jc09yYVZNcjR2RnphSmZtVFUzN3hQZDU1aHhWUnhmMUowV29ja1NkMk1jUFU5VFhaNnBISEYzMmE1TFpyQWYwMjFyQktNTElZd1pVYXpCZkh6OUtESmxiTU12aFBVZEtqVTRtT01uLzJIRzNxS2xac2JpaVZWejRjekhMOEJyU0N5aXlubldPbHlHUXk3THc2cGxQcjFxV1liTmRudTd0eG1EY3gxVVVRRHh2VjhrN2I2Mi82ZzhYdkZOZkNRWGNKNThxaXU1Rm9JejU4UGorVlRGaTV1L1Z2L0FJSzdPbXVpeUJwbkZneFY4bzk1R3RkbXZzV2VFYmRlU0crZjRXQTk5WmhsdVZJNHhucUQrbExhVERiajVSb1BQMHRyOFJ5cTZ5cFl5YTZNQlEzQTEwOUtZTHB4cDVJM0s1VlpEYXNiUElCMU90U3laTUdqeWhXYTl5RjByQ0ppc1JuV0ZZMmt5QmxVY00zTFVtdjJNN1JqalF2ZDhQYVlXSERtTDg2bXh2dE1nSldMWVBFNXR4MU9uTHJYYjhBeFRTYkRaUEplUVNYdGxienZYWkRTeDV5b2U5cnJrdmU1MDQ2VUxaZUlORzZ3eU5GL1RjVkRqNWt5RVpSRXFjd2JnNWgwUHhyOWkxTUdRNzArTFZkNzNWRVloayszZkdScWtnazVpTGxienA1ZjhSVERDTEVFUDN0NitwNjZDa0Nra0Zia2tacVlXRnRwYitiU3U0bWk5TkJZVmc4UkcvMlMvYXJsSnpYNWVWcTdSV09XQmJ5QmRXVC9BSit0WVdWK3pJU3NHSk9MUVJiVm0zaVFlcXJ3dDUxMlROT3BLQU9uTU1HdWVtbGdPUGlwREh0NHBNUEtwMTRieS9xUGZWM095S0h6STByREdBcHVGR0ZqcFVoQVdzWS9hR0J3czR4Q0Y3dklNTzR6YUU2bktUV0V4NmhCMzlob1JXRGxIWkxSYktlWEpZS1R6UHlyVU9SS1BlS2I3V1JTNmkzSWNhN0NSMWxjeXNZZnNpc24zcmpXL0hXdXlmOEFEOFRncHcyNjFoSVFkTTNYV3BGZE5iTURwcFQ1UWN4QTYwNER5U05QQmM4eWJuNTFNa2NrWUlqRXJXQmUyaTFON01teEJsY2VDNDVWanBtRTdaRnpITnRHYmhVTnNNYnFycXQ5N051MzZDM3pvaEVpUWxiOWFrWlluWWlGdlRRVm1FMkVrMlU2ODFjYU44NmxXS1BTN09kQnlxQ2ZEOWw3UlRpc1poeWRyaldVOElSd0MzQXU0MU50S3dlSmFZSGJtWEY3N2tuUyticlljT2xUbytXMmJLMTdYRng4cUZsR2R2N2JVTENRRjllaEpOV3RsQ3ZicjlFcmtrU2UwUnd0dkN5a0JQV3B5VEptaGxENm5KeVBxSzdHYkVRYWd5QTJ6KzZnWThSaHhzOHNoM2tQM1QraHA4WmpIZTZvbTZWZ1UyMFhuYlNzWTJ3TVFRWWZLTEJ2dlhyRUhFUlRPcGpqdGJaRDE5ZGFkNE1ER2MxcFBEMXNlWXZTTmxYZHlEd0w1K2RMSm1WczQxckk3SnhiTDNXOHh5UC9BQVZQdDFid3VvL0tod3FSaGE3K3Z1cDlsRVUyVVlCN3hPbDY3eFFFbWsvZHUxcnp3NmJybnh4L2lCM2g2MWpRZXlHYmFUUmlQTjdRbzF5WDVmclVxNFVSeXJuRXNKMWpIRUFjcXh3andzWVlUUVpMN1FuZ2I4clVzOFcyRnBGZmVVZ0N5NitFai9uV3A5bThvczdmZFR4SDF0b0sweWpMK0VVK2RwdDc2TUJMaDhabEdhWlpMcEw1MjVHcDFQc2hBVzNNZEJVWUxPbWZYenBRclEvNWllR1JPaHFOc1BpaHEwY280K2FzT05PQ281NXhXT3dxWmZ2eUEvSVhOVGlSd3VaNWZ1RCtWUnc5K3RSWm5PcGJtYWhhcFhSY1VwRWljbXJzKzdiUGFYamJMcCtYeXJzcDNlY0ZselRrTFllZ0ZiT0dOdThrUzJCOWVaOTlLODhtZGJLUFdyZ3FpZzBtOUd1MWpZSGVSeHdZSGthbjMrQmNyY1ArTmY4QWNLbjFQL2FjT1BoeCtWVHVWNTNzdjUxaDlsSHp4TW5EM0huN3FuYWFiSm5sYzhTYXZtYzFOY2dIUy8wTGNSUjJiU28wenZpaWU3NVZNREt0MTlBRHBSRnBFSXBRMGtMNk4xRkpKSGppQU4xeURVMDhzN3dsczhoekZUenRTN1NQRjRmWmtuVExiV21XaXQ2alF5UmQxanlxMDJHZ2dDUnFlQXVOYVZZYk1FY3J6VThSUkZFVkt0WlpYU0k1Vko0bW1senNaV1B3dlhZV0RaOEhKa1dSb2dXY2RUWFo4TVQ4THFnck9YdDFweVhjM3VhMFVkRFNzVUxhR3NDajV0YnVLeng0YVNOVG1Yam1yR1l2N0liVTVuMHZVTW04dDZTVWU2b1dZK2xZY3MwWEZTS3dFSzdTSzI5THFiNjFoUkY3S3BCeXZlOVgrTkUvR2xQeHJ0RjhQR0Fva0E0bHJWMnRKaUk5cUV5c09CNVZFSzBOUFRDMlM5WUhBaFlnMlc3RzUwcFlvcFpIN3NaTnJjcWRqOGF2cjVWR2MzUUNzSzZOZXNMbkk4cXc1VWVsS29MVzVjYVRLK1VFVzlhTHRodlpnK1IrdWxRaldvUUtoM1pRYjZjVFNrS0J3cUhOTGltWlpWazFVK2RZYksvTVdxRDVWRmtjK0xMVWRwVGJNZXRMdnhTS3k2NkExSHRHNjJxSzN1cEI4S3dzU1JZdTBMVEhpTTNRVmlwV1dFWlFUenBFa0hGczYzcjJmNVVjUDhxZkNuNFY3TVBoU3dOK0cxWWNIMHJ0SDkzdytYSVVqdGU0NTFqNU5qQUN4VUlCbUhuV0paNXNmaGhkT1NnL1RIQkt1R2habWprTnIraHJCd1I3V1BhQ1BNZUZZS0FabWJMdkc0MG9CcEIzclZIUjJiY210WGFLUlI0T2ZZQkJBRG04NjdVUmtpc2RuN09MSDFwdG96Qzk4dHJWR0tqcU5IdzhEbzVXOXVGZG1SeG1RWnQ2YlN1eTQ5cThSTzBFdTdiaFVZdnpxT2swOHFpUHdxSFgwcUt1MWNaaEdTTVp0aTI2ZmNhL2FQdFdlSEVTaEhqTWk3d0oxcFFrTUNCRVVIZ0tCcFRRT3prakt0WThxL2E4eG9MN0pkbU4xT25EcFhhSXhUNEMyeEtxQU5lTjZZVTFOWGJUWUhEdWdtbUhldkllWXI5clpjWGc1cGJTd2xBTXc2WHExaFFGQVZKczlwSDNnTGtFVjJUMmFrY1J5YlNSanFCcFVlRWpudzMyQ0poOVZ5blc1SjUxR3RSaWtGS0t0UzFvVEd0dGE0U1lxTkQ3MitpOUNod2piOHFFaElqSEFVamdLNkp2RG5XcTBUVG1vM0x6WWRIWDNWaDJHZkVjYWMwVFY2NXFhazMwbGNIK28wOXdycGY0ZldGY0ZqUmFOd2NhcHQ2ZlU4TUxuNUdyYXhEbDVWcisvZjdhV2hTMXl3YVcvcU5mK1VQMStxb1cyTW1IOTVvYVdqWSt1djFSOUgvL3hBQWpFQUVBQXdBQ0FnSURBUUVCQUFBQUFBQUJBQkVoTVVGUmNXR0JFSkdod2JIUi85b0FDQUVCQUFFL0VPS3dQMW5XdDl4WDVpUHhVUWx4SVgvQVJSMkNKRTFqaVRZTW83bGJtT3k1WU0wY2pZMjYzVU80SzdvdlNXWTNDVUI2NHFLMG16elZ1eUhCS3NJL3NYM3JWWXYxYUtkVDFUUElUYVlCVDNDcUxOOGx6REdwK21YN3V3L0Z4SmdZNWV6T0ZkcC9IRWR5VmhjR3E0TnBFUzRodGFvUjZOcFNFU2FUVUoyWDZwRzkvd0FkVWxIandRblFCb2lMUjIyMDF0VkZXUEduMFlEUXBrVTlYZDNIYWNDQUkvR0loMHpvTWRXQ2F3cHJEYU9xblFNNXlBTUwwb051Y1pWZk5iWE1xaGRZV0RDT2FyR054SXB3QzBlYmkzYmh0MDBZaDNKcmdEMHNBVVFDT3BPeWE5eThIeE1Ta0xoNm1vWk5nR2Z3Q0lNUlV1RTRTTHp5d1hzbEp6eE9NR0c3anNBd3pZOHdMcThwdEl0OW5oS3A1aGhMbEVqU1lnQUVYRHRsdjJiWkM3eldFL2dONmw0ZFF3Y0FBWHZHd3RsVS9NYWlid21oZzgwUVg5QU9WaUVPdGdmMlFydXdzU0tSTWZ5Qk5nK3BlN2VxdzlSVXdVV2lzSnQwNGNyR09iUkMreDFDaHc5d25LM1dnbEh3Vk54d0FRaDZBQU9iTk1YOUNpM2oyU3R0VmlIS3Y4Y3lqa3ZHYURrNUhITFFPNWhuQUdNSkQzcVVDS0dTRmFJT2h4U0xmSnFHMGxDUlc2QUlvMG0vb2QwUkc3cDIzd1FLVFVzb1drdVNZUUxLeWh3Yy9Na1ZGMlFuSEU1RDZOd2RaYVBSdUI4TEhWTXZFRVA4UGhBZDdMUkxWYUpSVE5WS0xORklodHNHRDBtZ0luMUZnbmhDNkZDUXk1ZTV2d3ZRZlFiS2FZMkgrUlE0cm1lQnhkQzR0ODBwdjgzRGJ1b2IrSDl5dXJGRHRBejJ3cjQ2d3N5dTZ0cnJhbzd1b1FGcjJOZUxEbnpSRzRuTFpqN243SUNaUVcwYm1YcmdubU52NElyTktPMU9ZL1FaN1NvT0ZoaVU3dnVvNXBIQ3dZSlpCZUxjaENjTEdITDYvTVQ0SnpGTGhHTElNODBncWVBd05FQUZpdzc3a1ZSUm9qYm5OSEs4K1NPYkxNUEtIeXZnT1dVRi9LWExTUTcrVXhTMlB0NkgvaUErSVAxSzVpVXhhRnFydHlTaEJlUUpSd3NjNEJxTUR6ZUhhVnhEeEZXQ3l5bENYaHMzYWJDY0syNjdMNmt3RWg2R285RWd3NGhBUWFnZXhObGwrSVZUd21zNXQ5R3c4V1hML2pPcWVNZWk1RkVkN0htMG5BSXVhSEQ3bEkvckFlemhiejlwZ0s2d1BYaStqVGx1OHNvenhnOVhGSThuQjVoRXdnRVAxY1czU1Bra0JHVTFXSjVtYlAyc0srSkZmUXFFWjdPMk45OXJEcEtzVG4xRWcxTjBTNm84V3g2WEZSWEVaN1k4UjVUTjBWOE0rTFdZRFFXZ0hLbkxkcnErTFNNVEdZVWFkVk52SmxqbHVnM2NlQ1VDaTZXb3RJMmpuby9LSC96ZUF0Y3RyUTdYZ2JCT1RIY1J3UE92dUUxeGFOQmZQdDVYekZMeFB5MUE4MG9CeFNSQkVydWxQTExFQ1M4dGdLdWJ6bVhhQ0YraFlEMmpod1pBdy9Pcm5iMUFzQkdjOGFkang5SHpCTkdVeEhndmdUQU1EK0ZnMXBsRUhDK1hNbEZCeHVvc3RLTkR1bkhnN1dvWmZ5amRXTnBUZ0ZwcERTWUZIdzFUd1NPNDJNOXVKVnVIaFZTbDFsck5FdmFRYXI4UnFNS3FlMWNFVnNMZWx1RndOYTYxc0V1clNwbkZZMkl1SllYb2NHUzJUVEdickRLbHJpdXZoMXM0dE9LVWdxcXNLSGd1WUwzcWJPYkV2dk1nSlhJUEwvempPMHFwRk1HeEFCUzBoUVgwRngwdHZYb0cvSkZZd1RVTml0L01Qd3l2VENWbzFBN0paRUllb0FsdFZWN1YyVWFxZDNDTlFZU29JUXNMYjdYN2pDWVFkSktpMTFYeU5rSEFranFTcUtSV3dvdHNaS1AzeUsyWUhCUU5oWW1lNFBoWHNVczYraUpPMEdZTkNpaW5NUlUzVkJ0MS91bzNyY3lXcTVUMUR3eDZsUGdzWExFYzhrYUI0NW56aElLYms5QUorMXRLWmp3ZUdrVy9oSUpyVGhJMDJ2ZzVpeWpLd0RqR0ZlRE41eDlSeDhWWWk2d2VxYmtCU0pmc1NQZ2R4clV1Y2xQT2JoL1VhU3NJRThXcVFaZTdMY3JhVzR1UlVQTmtGQzZCbncvTXNCRjcyVnFLcGxVcnlManh6b2pRZ3gyY2o2RnJmV0RLNHlQc0ZDejRRR0g2QWlLRGJyV0Q0NE5sS1EwNldNV0MzTXVQN01mV1NsVlNLTDhoZGdWeGFFK0k4RThTNzFRQjc3ZURCbkMwUDRNL2lUMDYyd3lxMDNMTWYzVWJTb3NCcEI5NmR0NEpaOE53SlRKSmUyTEJnY2l3UXExNXVyN0paUkhWZ3R3V1BaWTRHNE5OYjJYMVV2bzljMUt3R3VDM3hOdlEzS3RwNWtTNjQyRmpVSUhTcGllVDV2SFJGRmoxTlc4Z3hXYTBZV0hGL3dEVDdKT0xtQUFEeGcvcEQwYU1UQnA4a1JWcFlLaURDbkovSXY4QTVYd0wzUGFaaEZoUjVXS1N0M2JHaWlpOWdPVWNLWlhlYUtVSTdOTDhIQ2tYTG9OOEpORlV4VjFZTDdsMy9wQnBHcmlpa0UyQW9ERXNkb0pFcGZ6ZFh3SzNzWHVOVUF6NEFvSldVdlhMeTdOTVhIZ2s2UGJEWTdGM0RHNXpXYjNCd090RzFvdjVxV1pjWERzMHpyc2VSMGpzdmlWRjNaQTlnekJUaFFoK3hoaGl5Mmg4V3AvSVRDNUNZZDJCL0tmU1B1bTJtMDgzRiszM2JGcXJoYWovQUl3ZUs0bU5MUzBhaGVZVzE1R2tNOFFUbGVqL0FFbUlxOUgrS1FrbjVxTDhWVTRkZWFDUDNhNFhIZG04ZVh5UW8xbC83OGgreERSOFkvclVTNnV4Vm1wM1lhOVgzSFM5WTdzdlBqNEltNVEzekw4Njg4RENWeUxBQnpzRHpkUjIwVXBib2xRejV0c3RBUkRQOXRKZlZCNHZwK3BkODZnbDV1bW1LaHNoRWFDWXVRRXNiNU1nQlV6SXFrNGR0UXYwbmt1RWxHa1MrOHcyYVJROU9XR2Y1Z1dnOEZ4aHl1bEVLbEh2SFpRMnhGQmE1QlFSTHVPR2dBOWpiSGdadHQ4a3VBZ0F1MEYvTW9SOFNGeUVXb3RkYlpibTZ5Q0swRDZpYll0a2J5MDRnSmdDS2J2dGpHMzQ1UThrMWRNRmd1ZVpJY2M1Z0EvQWpSQ2Vvb3pKWUVUY0ViVWdqeDVnTFdSaWpGTHdWQXRFNUxUYUoxQzdkZm01Y2dialhKWFFFOXNjWmxKVnF5TzJoUTROYnNKY3hJckhWbzJPTWJNaTRWRnVLNGlQQ0tKUkg3RzJGRnY0bHA5d2hrRHBHTGk2bkdoa0RDbTYwYVo5ckxzRGk1a1FNR3hBVnU3WGdpcjlHN0NzbFdhU1VsWFR3eEZld1ZKQmxrdjZ1RjJlS28yTWlxZzY3cUw3SG0zeUpTMExoTG4xTmZkektvVHVXVXNwTFdRbkoxdDA3WUZZZ3JsUjRlcFRha09XUXB2amF3NVljSmNnS0wzS3RlZTEvaUFYN0FSY1FGT3ZaUWFOUUpVSWNJdlJFM1FJYUYwZmdoTEF5VmN1cGVGMDUyVFJ6cVJPVldjeTlpeWhJV1ZXSGZqWEM1U3BINmo2aUtJV2pldll0b1haVzduRkcvRFd0WFpFRWpxZ1FuRkViS3lqSkMxR3VHQklWdys0Wmk4TnBVdm91VVpCZGszMVZYR3lhaDdNNUkwdzB0UnNBN0I1Y2xXMm1nVUJML2pGK3dWNlZhc0hmbUJOZkhnWUZyaEpZam41V1lTNXZUb3o3blhDVVd3M1U3NmpvVmFUQTZPazliRlFMWWhaRnkxK28yZzNLS0MvMVVvVHdRZjE2dzJtRjFCREtBZWlQYXNzMHRydVF1Z0duNUlyY3BQdXVnNHVvbG9TM1VCN0NtV05TUXRpRXJMd1QrSlNOa0Y4MUZJZVBSQzhiSzVkeXNqM1JSc1VrMkxtc0VURE9kRUQ5b04xV2x2aVBJM3FqaFVndFNyRFNYVEx4aXFVMDRSSXh5K050S0FHRjdEZU9BVU1LZTR2Mk1PblQvb28yM2paTnQ1Z2xzcERqVVJIbjhZREZhK3A1ZytvZ0FBMzZWLzdFS295NDBzUERCNmtGMUJ4eUJuWkx5NFRpRGpXZjhJS3FQMUxLbXZ3SXBQalB0QndKcVlnR0pGU0ZTSzVKZ1VjUkZ0TE1uVDZ4K2NJYzRMdGxncXhyak5CM2lLQlAvL0VBQ3dSQUFJQ0FRUUNBZ0lCQXdVQkFBQUFBQUVDQUFNUkJCSWhNUkFUSWtFZ1VTTXlNM0VVUXFIQjBlSC8yZ0FJQVFJQkFUOEEwMzlzVFVyOHMvdWFhNy9ZMzRqbndUTXdHQS9sZlo2MUppN3JHM0dXY0pLYS9sbUZ3ZzVoMUNmWE0wNkUyRnBwV3ltSnF1RnpLMy9reVlweVBQZmttSHdwZ1BqUG5YdGhNVFRFOEF5OC9DVWNBbU1TNXlZRUE4YUk5aWFnQXBnd1ZnR1ZIQThHRHdUQ2ZLd1FUSGhacXEvWkVyQzRsakFEbVZBRmVJOUJIS3pQN2lQL0FDNEhNMHBLc0NaZUN3NGdRN3VZZ2dtSVBCL0pUNE0rbzRqVmpFWWw1U3UxUkdiQXlaYVJhY3oxaGVWaEpWOFRjU014V3kzTUE0Z0dQQjRndDV3ZndiT09KV3hJK1hmZ1JZVGdSTEN4SUl4R1dFWldCeXJiWU9wcWM3SU9vVEw2d0czVDNxcTRqMkFqaWFiVTdoaG9HRGRUTXNZS01tUFlyRTRNMDErNGJUQno0YlVxcElNWFVxYzRpTUhHUkJDMjJOcVJ1ajNweUFZUDZZRHhGVldlQVRVRE5aaWxoRGt5eGZZQVRIUmNkUzBLRGtRRWdraVVYK3J2cUN3TU15L2JZTVEwSjJJRGc4U25VZ0RhMDNoaHhBcUR2dU1sWk1yWjZSK3hFWU9NaWFwam5hSlNnc1lobytscUhRbE5vSzQvVVVncktGemNUNHRzOWpGQjFBdVBxZkw5UjM5YWhjWmhjSGd5L0FsYWtnajZpREhDRC8zL0FPUVZzZXpQV3k5R0ZSWU1IZ3l5djFncXdsTkp0Z1FBYkVpVWpFTkNZNm5vOWJiMWlJbzVXYXZIRVFFc0NvbHpiSnVmNmdOcS9jb3ZWU2Q0d2YzQmdpS3UxaUpnNXo0MUxNdjlNeVZPWVNXL3lUQWdSUldzU29BUmNnWWpaTWFzUyt2ZldRZXhOT3VLK096RVVJSmlZejQyN1RrVFVxUzQvVXIrQ2J6RXJMbmMwUlIxaU12MWlXMEQ2bW50Sy9BelVvUUN5eWs1U08yMFMrZ1dqbnVQcGlwN21sK1ZnQmdIWkVEdDlpZTVJTEZQM01uT01RelRrTWdoekMxbjBzemNSeEZCd04zZmpVMllzeEhHQkZZRGlGZ004d1c4OEdGdWNZaWo1eHZuWGlVcnRXRVppYWpibjdIL0FGTE5TakhxVU1BKzZMNEtqR01RVnIzMUFjeTF3aUVtYUt6dFRHWUlNbUZncTdtbkM4NW5PWTdCVkpNQ0crd3dOdlhNQkdNbVA4ODdlcFdCakdJQ0RGSHl6RVB3ekVZTVRqeGpDemNUbUpxZldOczBseTJwa1RiZzdvRk9jNWdVZ25KbVFvbXF2M25hT3BWZHNiZEtiVnNYSW1QQklYdUczM1dCUHFVWXlXL2NCOWJrSG94bExmZkVPNWdQV2NRcXhZRUhpQmNkUzF3bzJ5N1ZaK0tUVHNjNDhGV1B4aEJXVjZNTW1UMlo2VzB6YjBsZXFCNFBjRjRFYlVJbzdsbW9hdzdVamFWekYwN0NVMWxLbWI3R0ovcW5Uc1FheG1PQVArWlkxcjhUVFZ1dG01cFZUdEV0clYwSWxXcXdNUEV0UnVqQ3doY3R3azFRMk1GQjVqTGpxVlpIaWpsOHkvZ0gvTTA5b0M3U2VwWVZkU0p1S3RtRzBXbjVRSmxTdytwcG4yUGt6M0pEYW1JdHFrOHk1dDdaaUhhMlo3bGxkNndYcVB1V2FnQkRpVnA3SHdaUTlRWGE2OVFYMHIwSSt0R01LSjhuT1RHVWdZTVFqYkhkaWU1cGtKWWlhaE1PUkZCSFVER0VFd1FGZ20wUlI0QWpERTI3aEFtM21jd0tjVGFSRG1JV1E1V0FOem1jK0VPMHgyM0h5VHQ2bW9YSUxUU0x1ZkI2bnBTRFRxT1JMS0VVeXhBT3B0SU0zbWJ6TjVpRGRIWGFNeGJTSUxwN3hqR0lIM0NCUHVEK1BtZTFERGFrOXlUMnArb2JBZWhNa3l6MmJ5TThDV05ZY2duaWFTbllOK2UvTjJRTndoZGlNNG0vY1lVSm5yZ3JNUUhIRWZKSE1GWklucmhyaWNERUZwUEFFYTBuakUyelpNRVFBd0F6YVk2ZTF1R0lscUd2czVpMnFvd0liNE5SSHUzS1JCU3pnTkZBei9pYnB1RTNpSU4vQWpJVXdDWW9TQkVucVF4NlYybkVWU3h3czA2WllsdnFHdEQ5UVZKK3BzVDlSZ2doWlJDNE1yVFlUTDhzd0VYVEVUMEdEVG1QUXdFUndFQWlWdFpuRWZUMmd3YVcySFMyeWxXck9HbHVYSVVDTHAyZ29JZ1JoQ2pFR1VNRllneWo1c3hFQ0diVE5rYWdtTnB6QlR0N2pENUdETFhBSDhEMFlybkUwWDlKallnSWhhV2YzWVQvQUNMK0tBRnpOSnd6Z2ZnU0JDWXhuLy9FQUNvUkFBSUNBZ0VEQkFFRkFBTUFBQUFBQUFBQkFoRURFaUVRTVVFRUV5QWlVUVVVTWtKeEZTTmgvOW9BQ0FFREFRRS9BSi95WmpKeDhyNXJwWHhvUmlqdTZSS2t0UmR5Ykl4MmZCSDAyUitEMUtjTVNUSjhNeDhzMFdsZk5LaGRhK1BwRmNyTTZWdWlQY2trMlkvK3BXdTUrNG5MZzl5UzdtZXI0SU9tUm5hSkw0b1h4Znd3VDBKenRrVVB2eVF5a0hGTGhHU0NsaTJya3lxMnhFWHdQbjRMb2kvZytxRTZMSS9VazdaRk51a2VoL1RzaytHZjhWU3FUTzZJeFRZNDB2Z2xaN2RLK3FFU2pyMVlsWTRVclFuMGlyalkxUituUmpMUEZTUFQvWEtOY0VIZG9oQnZrakcyWmNPcjRHcUVZNHRzVVhGR1RIWEtLRVJ4Tms4YlhCS092Ukt5T0trUnh5b2ZmcGRRNmVsbEtPVk5IbzhHZmZhZlk3SWphSU4yUmI2VHhPUW9OR0s0OGl5TnFtZUNlSytVS0xRbklpcDBTaXBQL3dCSkpwMHpDaWN0VndRelRmY25CcGxVU2RZMTA5TENPS1B1eTcrQ2ZyczB2N3MvY3k4dXlFWExrU3JzWTA1RXFST1VZcmw4bnUvZ1dSZVVKcGY0UWQ4b2xQVjhqbmJ0am14Wkg0WkgxVW5EMjVPME50dW1ZYjVHdUhiSVhJU2liUWZneVlYTmZUa3FtU3liUVMvQmRScnBnVmxKa0tWa3B1VDJZNU1sWGRNanJWdnVSa3pEUFdSa2EyZGpiWnRhcWhOdzRFSjJxTUw0SnU1YUlsTFhpSS85STlyc2hrY1RORlRqdXUva3hVM3laRlRNY2RqSGtjQ0diWXk4UWRGMzNIRlZ3eU9PVFk0Tk5tc2F1eFBrektwdEMxWGNVWVZ5eVh0K0JUMXVoV3pESDYyUjVabzZzVVgrQnhhRkhpeTdRdUpFNWJNUWtZOGJpWkZhcnBYRmtKTmMyYk5sVTZNY2RuUjZtUDhBWWpGellsOXFSYm5hb3BPUEhjaEZ5bFNISlk0b3JTUlQ3SWorR3lUZmV5bWg4SWt2c1NpMHJmUk5PVkVJclZXUEM1Y21mRTRTWXBXdFMxVlVYK0J0eS8wOVBpMVZrOFZxaWVPVUhYUklpbXh3OXJHNWVTZDBrTmJ4dGVDRXE3OXhVcjI1RTQ2dE5jamxmY3h4Y3VURmhybVJtaXRIMGdveCt4R2FsMlAzT3JwRHl4elIxWkxDeDRySTRaTWhqakRtUkhQQkR5eFpreVhrakh3ejJveTdEd1JTc3hxRVRQS0x4dElsa3NoTnhkazhDZktIamtoUmsrRFZRL21ZWmJwc1V2Qk5KOUpjSXhJeVE1c2dxWWtuR2pWd3VoUzhHV054TkdRaXlVUEtNUzFSTldoUVpPRG8wWkRHMnh5MGlaRk83aXpUSSs3STRIZkpGS0NGSk1mY3h4aXZCa2FNVS9xTnJ5Y0NsWFJOWFkzYUs2WTBtK1J0UWZBcHFYQnJCRGFzdENWRnFTNUpOQzZQbEVVa3VpZENWOXpFNjRNcnBXS2JGa2RFTWpaQ1g1TmhLMEtKcjBpN2RHbG50aXhqVk12Z2YyRkJpaXhRWnF4UktTTWVsY2tGR3VETFBiZ3JwRHZRb29jQ01xVkd4dDBpcUU2TnhTSHp5YUNna2JHNWRteFpaRjZMbFdRbHNQRzJMRWUwUnhVejNGSGdWdnVhbE1VV1Nlb25mWXFROWpaa1p1eHVseVpHNjRFNUZ5L0lySXFRb3NVQ2NsSXhjS3g1b3NXU0o3c1NPV0pWdGl5UmozSTVzYlBmZ0xQak1zbE4yakhVYlk4OFQza09jUlRqWkpOcmdrOUVyTjBLWnVSekpDekk5eXlMSHhqWW1XSmlOVVp1NUd5aWlIOFQrajZMb21KMUZHWjhKL0JGQ1AvWiIgaWQ9ImMiIHdpZHRoPSIxOTkiIGhlaWdodD0iMTU5Ii8+PC9kZWZzPjwvc3ZnPg==", + "description": "Displays the latest fluid pressure telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pressure', label: 'Pressure', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 15 - 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"square\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"compress\",\"iconColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#2B54CE\"},{\"from\":5,\"to\":10,\"color\":\"#3B911C\"},{\"from\":10,\"to\":15,\"color\":\"#F77410\"},{\"from\":15,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"36px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#2B54CE\"},{\"from\":5,\"to\":10,\"color\":\"#3B911C\"},{\"from\":10,\"to\":15,\"color\":\"#F77410\"},{\"from\":15,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:cHJlc3N1cmVfY2FyZF9iYWNrZ3JvdW5kLnBuZw==:cHJlc3N1cmVfY2FyZF9iYWNrZ3JvdW5kLnBuZw==;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Pressure card with background\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"bar\",\"decimals\":0,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "fluid pressure", + "liquid pressure", + "pump pressure", + "hydraulic", + "pump", + "compressibility", + "compressive stress", + "pipe", + "pipeline" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/fluid_pressure_chart_card.json b/application/src/main/data/json/system/widget_types/fluid_pressure_chart_card.json new file mode 100644 index 0000000000..7f79439ca4 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/fluid_pressure_chart_card.json @@ -0,0 +1,36 @@ +{ + "fqn": "fluid_pressure_chart_card", + "name": "Pressure chart card", + "deprecated": false, + "image": "tb-image:cHJlc3N1cmVfY2hhcnRfY2FyZC5zdmc=:cHJlc3N1cmVfY2hhcnRfY2FyZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxnIGNsaXAtcGF0aD0idXJsKCNhKSI+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZD0iTTE5LjMgMjYuOHYtMi4xbC0xIDEtMS4xLTFMMjAgMjJsMi44IDIuOC0xIDEtMS0xdjJoLTEuNVptLTQuNi01LjV2LTEuNGgxMC42djEuNEgxNC43Wm0wLTJ2LTEuNGgxMC42djEuNEgxNC43Wm01LjMtMi0yLjgtMi44IDEtMSAxIDF2LTJoMS41djJsMS0xIDEuMSAxLTIuOCAyLjhaIi8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik0zNi44IDIxLjNoLTIuNnYtMS40aDIuNmMuNCAwIC44IDAgMS0uMi40LS4xLjYtLjMuNy0uNmwuMi0uOWMwLS4zIDAtLjYtLjItLjktLjEtLjItLjMtLjUtLjYtLjZhMiAyIDAgMCAwLTEuMS0uM2gtMlYyNUgzM1YxNWgzLjhjLjggMCAxLjQuMiAyIC41LjUuMiAxIC42IDEuMiAxIC4zLjYuNCAxIC40IDEuNyAwIC42LS4xIDEuMi0uNCAxLjdhMyAzIDAgMCAxLTEuMiAxYy0uNi4zLTEuMi40LTIgLjRabTYuOS0yLjN2Nkg0MnYtNy40aDEuNlYxOVptMi4zLTEuNFYxOWEzLjIgMy4yIDAgMCAwLTEuNSAwIDEuNCAxLjQgMCAwIDAtLjggMWwtLjIuNmgtLjRjMC0uNCAwLS44LjItMS4yIDAtLjQuMi0uOC40LTEgLjItLjQuNC0uNi43LS44YTEuOSAxLjkgMCAwIDEgMS4zLS4yaC4zWm00LjQgNy41Yy0uNiAwLTEgMC0xLjUtLjJhMy4zIDMuMyAwIDAgMS0xLjgtMiA0IDQgMCAwIDEtLjMtMS4zdi0uM2MwLS42IDAtMS4xLjMtMS42LjEtLjUuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS0uOGwxLjQtLjJjLjUgMCAxIDAgMS40LjJsMSAuOC41IDEuMWMuMi41LjIgMSAuMiAxLjV2LjdoLTUuN3YtMS4yaDQuMXYtLjFjMC0uMyAwLS42LS4yLS45IDAtLjItLjItLjQtLjUtLjZsLS44LS4yYy0uMyAwLS42IDAtLjguMi0uMiAwLS40LjItLjUuNS0uMS4yLS4zLjQtLjMuN2wtLjEgMXYxLjNsLjUuNy42LjUuOS4xYTIuMyAyLjMgMCAwIDAgMS44LS45bC45LjktLjYuNi0xIC41Yy0uMy4yLS44LjItMS4yLjJaTTU5IDIzbC0uMS0uNGExIDEgMCAwIDAtLjUtLjRsLTEtLjMtMS0uMy0xLS41LS41LS42LS4xLS44YzAtLjMgMC0uNi4yLS45IDAtLjIuMy0uNS41LS43bDEtLjUgMS4xLS4xYy42IDAgMS4yIDAgMS42LjMuNC4yLjguNSAxIC44LjMuNC40LjguNCAxLjJoLTEuN2wtLjEtLjVhMSAxIDAgMCAwLS41LS41bC0uNy0uMS0uNy4xYTEgMSAwIDAgMC0uNC40LjkuOSAwIDAgMCAwIC44bC4yLjIuNC4yLjguMiAxLjQuNWMuNS4yLjguNCAxIC43LjIuMy4zLjcuMyAxLjFhMiAyIDAgMCAxLS44IDEuNiAzIDMgMCAwIDEtMSAuNWwtMS4yLjFjLS42IDAtMS4yIDAtMS43LS4zLS40LS4zLS44LS42LTEtMS0uMy0uMy0uNC0uNy0uNC0xLjFoMS42YzAgLjMuMS41LjMuN2wuNS40LjcuMS44LS4xYy4yIDAgLjMtLjIuNC0uMy4yLS4yLjItLjMuMi0uNVptNy41IDBjMC0uMiAwLS4zLS4yLS40YTEgMSAwIDAgMC0uNC0uNGwtMS0uMy0xLjEtLjMtLjktLjVhMSAxIDAgMCAxLS41LS42bC0uMi0uOGMwLS4zIDAtLjYuMi0uOWwuNi0uNy45LS41IDEuMi0uMWMuNiAwIDEuMSAwIDEuNi4zbDEgLjguMyAxLjJoLTEuNmMwLS4yIDAtLjQtLjItLjVhMSAxIDAgMCAwLS40LS41bC0uNy0uMS0uNy4xYTEgMSAwIDAgMC0uNS40LjkuOSAwIDAgMCAwIC44bC4yLjIuNS4yLjcuMiAxLjUuNSAxIC43Yy4yLjMuMy43LjMgMS4xYTIgMiAwIDAgMS0uOCAxLjYgMyAzIDAgMCAxLTEgLjVsLTEuMi4xYy0uNyAwLTEuMyAwLTEuNy0uMy0uNS0uMy0uOC0uNi0xLTEtLjMtLjMtLjQtLjctLjQtMS4xaDEuNmMwIC4zIDAgLjUuMi43bC42LjQuNy4xLjctLjFjLjIgMCAuNC0uMi41LS4zbC4yLS41Wm03LjcuM3YtNS43SDc2VjI1aC0xLjZ2LTEuN1ptLjMtMS42aC41YzAgLjUgMCAxLS4yIDEuNCAwIC40LS4yLjgtLjUgMS0uMi40LS41LjYtLjguOGEzIDMgMCAwIDEtMS4zLjIgMyAzIDAgMCAxLTEtLjEgMiAyIDAgMCAxLS44LS41IDIgMiAwIDAgMS0uNS0uOSA0IDQgMCAwIDEtLjItMS4ydi00LjhoMS43djUuNWwuMy40LjQuMi41LjFjLjUgMCAuOCAwIDEtLjMuNC0uMi42LS40LjctLjdsLjItMVptNS0yLjd2NmgtMS43di03LjRoMS42VjE5Wm0yLjItMS40VjE5YTMuMiAzLjIgMCAwIDAtMS40IDAgMS40IDEuNCAwIDAgMC0uOSAxbC0uMS42aC0uNGMwLS40IDAtLjguMi0xLjIgMC0uNC4yLS44LjQtMSAuMi0uNC40LS42LjctLjhhMS45IDEuOSAwIDAgMSAxLjMtLjJoLjJabTQuNCA3LjVjLS41IDAtMSAwLTEuNC0uMmEzLjMgMy4zIDAgMCAxLTEuOS0yIDQgNCAwIDAgMS0uMi0xLjN2LS4zYzAtLjYgMC0xLjEuMi0xLjZzLjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEuMS0uOGwxLjMtLjJjLjYgMCAxIDAgMS40LjJsMSAuOGMuMy4zLjUuNy42IDEuMWwuMiAxLjV2LjdoLTUuN3YtMS4yaDR2LS4xbC0uMS0uOS0uNS0uNi0uOS0uMmMtLjIgMC0uNSAwLS43LjItLjIgMC0uNC4yLS41LjVsLS40Ljd2Mi4zbC41LjcuNi41LjguMWEyLjMgMi4zIDAgMCAwIDEuOS0uOWwuOS45LS43LjZhMiAyIDAgMCAxLS45LjVjLS40LjItLjguMi0xLjMuMloiLz48cGF0aCBmaWxsPSIjRjc3NDEwIiBkPSJNODMuNSA1OC4zVjg0aC00LjJWNjMuM0w3MyA2NS41VjYybDEwLTMuN2guNVptMjYuMSAyMi4zVjg0SDkyLjV2LTIuOWw4LjMtOWMxLTEuMSAxLjctMiAyLjItMi44LjUtLjcuOS0xLjQgMS0yYTUuMiA1LjIgMCAwIDAgMC0zLjljLS4zLS42LS44LTEtMS4zLTEuNC0uNi0uNC0xLjMtLjYtMi4xLS42LTEgMC0xLjguMi0yLjQuNi0uNy40LTEuMiAxLTEuNSAxLjctLjMuNy0uNSAxLjYtLjUgMi41SDkyYzAtMS41LjMtMi45IDEtNGE3LjUgNy41IDAgMCAxIDMtMyA5LjIgOS4yIDAgMCAxIDQuNi0xLjFjMS43IDAgMy4yLjIgNC40LjggMS4yLjYgMiAxLjQgMi43IDIuNWE3LjYgNy42IDAgMCAxIC42IDYuMWwtMS4xIDIuM2E0MS41IDQxLjUgMCAwIDEtMy44IDQuN2wtNS42IDYuMWgxMS44Wm01LjQtNy4xaDEuMlY4NEgxMTVWNzMuNVptNi4yIDYuN3YuMmMwIC41IDAgMS0uMiAxLjUgMCAuNC0uMy44LS41IDEuMmwtMSAuOC0xLjIuMmEzIDMgMCAwIDEtMS4yLS4ybC0uOS0uNy0uNi0xLjEtLjMtMS41VjgwbC4zLTEuNS42LTFjLjMtLjQuNi0uNiAxLS44LjMtLjIuNy0uMiAxLjEtLjIuNSAwIDEgMCAxLjMuMmwuOS44LjUgMS4yYy4yLjUuMiAxIC4yIDEuNVptLTEuMi4ydi0uMmwtLjEtMS0uNC0uOWExLjYgMS42IDAgMCAwLTEuNS0uOCAxLjggMS44IDAgMCAwLTEuNC42bC0uNC43LS4yLjd2MS42YzAgLjQuMi43LjMgMWwuNy43Yy4zLjIuNi4zIDEgLjNsMS0uMi41LS42LjMtLjkuMi0xWm03LjQgMi4zVjc5YzAtLjMgMC0uNS0uMi0uN2wtLjUtLjVjLS4yLS4yLS41LS4yLS45LS4yYTIgMiAwIDAgMC0uOC4xbC0uNi41YTEgMSAwIDAgMC0uMi42SDEyM2MwLS4zIDAtLjYuMi0uOC4xLS4zLjMtLjUuNi0uN2EzIDMgMCAwIDEgLjktLjVjLjQtLjIuOC0uMiAxLjItLjIuNSAwIDEgMCAxLjQuMi40LjIuOC41IDEgLjkuMi4zLjQuOC40IDEuM3Y0LjNsLjIuN3YuMWgtMS4zbC0uMi0uNnYtLjdabS4yLTMuMnYuOWgtMi4zbC0uNi4zLS41LjUtLjEuNi4xLjYuNS41LjguMWEyIDIgMCAwIDAgMS43LS44bC4zLS43LjYuNi0uMy42YTMgMyAwIDAgMS0xLjQgMS4yYy0uMy4yLS43LjItMSAuMi0uNiAwLTEgMC0xLjQtLjMtLjQtLjItLjctLjQtLjktLjdhMiAyIDAgMCAxLS4zLTEuMmMwLS40IDAtLjcuMi0xbC43LS44IDEtLjQgMS40LS4yaDEuNVptNC41LTEuN1Y4NGgtMS4zdi03LjRoMS4zdjEuMlptMi4zLTEuMnYxLjFoLS42YTIgMiAwIDAgMC0uOC4xbC0uNS40LS40LjYtLjIuNy0uMy4ydi0xLjJsLjUtMWMuMi0uNC40LS42LjctLjhhMiAyIDAgMCAxIDEuNi0uMVoiLz48cGF0aCBmaWxsPSJ1cmwoI2IpIiBmaWxsLW9wYWNpdHk9Ii4zIiBkPSJNMTQ3LjQgNzEuOGg3LjJ2OC45bC0zLjYgMi42LTMuNi0yLjZ2LTlaIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTUwLjcgODMuOWMuMi4yLjQuMi42IDBsMy4zLTMuMi0uNy0uNy0xLjQgMS40Yy0uNC40LTEgLjEtMS0uNHYtMy45aC0xdjRjMCAuNC0uNi43LTEgLjNsLTEuNC0xLjQtLjcuNyAzLjMgMy4yWiIgY2xpcC1ydWxlPSJldmVub2RkIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZD0iTTE2Mi42IDc5Ljh2MS4xaC0zLjF2LTEuMWgzWm01LjItNC40Vjg0aC0xLjR2LTYuOWwtMiAuN3YtMS4xbDMuMy0xLjNoLjFabTcuNSAwaC4ydjEuMmgtLjFjLS41IDAtMSAwLTEuMy4ybC0xIC42LS40IDFjLS4yLjQtLjIuOC0uMiAxLjJ2MS4zbC4xIDEgLjMuNi41LjRoMS4xbC41LS40LjItLjZhMi41IDIuNSAwIDAgMC0uMi0ybC0uNS0uMy0uNi0uMWMtLjMgMC0uNSAwLS43LjItLjMuMS0uNC4zLS42LjVsLS4yLjctLjUtLjJjMC0uNCAwLS43LjItMWwuNS0uNy44LS41LjktLjJjLjQgMCAuOC4xIDEgLjMuNC4xLjYuMy44LjZsLjUgMWEzLjcgMy43IDAgMCAxIDAgMi4xbC0uNiAxLS45LjYtMS4xLjJjLS41IDAtLjkgMC0xLjMtLjJsLS45LS44LS41LTFjLS4yLS40LS4yLS45LS4yLTEuM3YtLjZjMC0uNyAwLTEuMy4yLTEuOC4yLS42LjQtMS4xLjgtMS42LjMtLjQuOC0uOCAxLjMtMWE0IDQgMCAwIDEgMi0uNFptMy4zIDIuMlY3N2MwLS4zIDAtLjYuMi0uOS4xLS4yLjMtLjUuNi0uNi4zLS4yLjYtLjMgMS0uM3MuNy4xIDEgLjNsLjYuNi4yIDF2LjRjMCAuMyAwIC42LS4yLjhsLS42LjctMSAuMmEyIDIgMCAwIDEtMS0uMmMtLjMtLjItLjUtLjQtLjYtLjdsLS4yLS44Wm0xLS41di45bC4zLjMuNS4yYy4yIDAgLjMgMCAuNS0uMmwuMi0uMy4xLS40di0uOWwtLjQtLjRhLjguOCAwIDAgMC0uNC0uMWwtLjUuMS0uMi40LS4xLjRabTMuMSA1LjN2LS41YzAtLjMgMC0uNi4yLS45bC42LS42Yy4zLS4yLjYtLjIgMS0uMnMuNyAwIDEgLjJsLjYuNi4yIDF2LjRjMCAuMyAwIC42LS4yLjgtLjEuMy0uMy41LS42LjdsLTEgLjJhMiAyIDAgMCAxLTEtLjJsLS42LS43LS4yLS44Wm0xLS41di45bC40LjRoLjlsLjMtLjR2LTEuM2EuOC44IDAgMCAwLS44LS41bC0uNS4xLS4yLjQtLjEuNFptMS4yLTUuMi00LjIgNi43LS43LS41IDQuMS02LjYuOC40WiIvPjxnIGNsaXAtcGF0aD0idXJsKCNjKSI+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuMSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTg4IDEyNUgxMnYtMWgxNzZ2MVoiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii45IiBzdHJva2Utd2lkdGg9IjEuNSIgZD0iTTEyIDEzMy44aDkuM2wxMy4zIDkuMkw0NSAxMTIuMmwyMS42IDIyLjYgMjMuNy0yMi42IDcuMiAxIDE0LjQgMjkuOGgxNC40bDEyLjQtMjYuN2g5LjNsOS0xNC4zIDEyLjQgNC4xIDE4LjUgMTAuMyIvPjwvZz48ZGVmcz48Y2xpcFBhdGggaWQ9ImEiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0xMiAxMmgxNnYxNkgxMnoiLz48L2NsaXBQYXRoPjxjbGlwUGF0aCBpZD0iYyI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTEyIDkwaDE3NnY2MEgxMnoiLz48L2NsaXBQYXRoPjxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjE1MC43IiB4Mj0iMTUwLjciIHkxPSI4NCIgeTI9IjcxLjgiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmY7c3RvcC1vcGFjaXR5OjEiLz48c3RvcCBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmY7c3RvcC1vcGFjaXR5OjAiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48L3N2Zz4=", + "description": "Displays fluid pressure data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pressure', label: 'Pressure', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'bar', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'pressure', 'bar', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 15 - 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"bar\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#305AD7\"},{\"from\":5,\"to\":10,\"color\":\"#3FA71A\"},{\"from\":10,\"to\":15,\"color\":\"#F36900\"},{\"from\":15,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 15 - 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"bar\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"bar\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Pressure\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"compress\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"margin\":\"0px\"}" + }, + "tags": [ + "fluid pressure", + "liquid pressure", + "pump pressure", + "hydraulic", + "pump", + "compressibility", + "compressive stress", + "pipe", + "pipeline" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/fluid_pressure_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/fluid_pressure_chart_card_with_background.json new file mode 100644 index 0000000000..0937db7590 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/fluid_pressure_chart_card_with_background.json @@ -0,0 +1,36 @@ +{ + "fqn": "fluid_pressure_chart_card_with_background", + "name": "Pressure chart card with background", + "deprecated": false, + "image": "tb-image:cHJlc3N1cmVfY2hhcnRfY2FyZF93aXRoX2JhY2tncm91bmQuc3Zn:cHJlc3N1cmVfY2hhcnRfY2FyZF93aXRoX2JhY2tncm91bmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+CiAgPHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0IiAvPgogIDxnIGZpbHRlcj0idXJsKCNiKSI+CiAgICA8cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiIHRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDYgMTU0KSIgLz4KICA8L2c+CiAgPGcgY2xpcC1wYXRoPSJ1cmwoI2MpIj4KICAgIDxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiCiAgICAgIGQ9Ik0xOS4zIDI2Ljh2LTIuMWwtMSAxLTEuMS0xTDIwIDIybDIuOCAyLjgtMSAxLTEtMXYyaC0xLjVabS00LjYtNS41di0xLjRoMTAuNnYxLjRIMTQuN1ptMC0ydi0xLjRoMTAuNnYxLjRIMTQuN1ptNS4zLTItMi44LTIuOCAxLTEgMSAxdi0yaDEuNXYybDEtMSAxLjEgMS0yLjggMi44WiIgLz4KICA8L2c+CiAgPHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIKICAgIGQ9Ik0zNi44IDIxLjNoLTIuNnYtMS40aDIuNmMuNCAwIC44IDAgMS0uMi40LS4xLjYtLjMuNy0uNmwuMi0uOWMwLS4zIDAtLjYtLjItLjktLjEtLjItLjMtLjUtLjYtLjZhMiAyIDAgMCAwLTEuMS0uM2gtMlYyNUgzM1YxNWgzLjhjLjggMCAxLjQuMiAyIC41LjUuMiAxIC42IDEuMiAxIC4zLjYuNCAxIC40IDEuNyAwIC42LS4xIDEuMi0uNCAxLjctLjMuNC0uNy44LTEuMiAxLS42LjMtMS4yLjQtMiAuNFptNi45LTIuM3Y2SDQydi03LjRoMS42VjE5Wm0yLjMtMS40VjE5YTMuMiAzLjIgMCAwIDAtMS41IDAgMS40IDEuNCAwIDAgMC0uOCAxbC0uMi42aC0uNGMwLS40IDAtLjguMi0xLjIgMC0uNC4yLS44LjQtMSAuMi0uNC40LS42LjctLjhhMS45IDEuOSAwIDAgMSAxLjMtLjJoLjNabTQuNCA3LjVjLS42IDAtMSAwLTEuNS0uMmEzLjMgMy4zIDAgMCAxLTEuOC0yIDQgNCAwIDAgMS0uMy0xLjN2LS4zYzAtLjYgMC0xLjEuMy0xLjYuMS0uNS40LS45LjctMS4yYTMgMyAwIDAgMSAxLS44bDEuNC0uMmMuNSAwIDEgMCAxLjQuMmwxIC44LjUgMS4xYy4yLjUuMiAxIC4yIDEuNXYuN2gtNS43di0xLjJoNC4xdi0uMWMwLS4zIDAtLjYtLjItLjkgMC0uMi0uMi0uNC0uNS0uNmwtLjgtLjJjLS4zIDAtLjYgMC0uOC4yLS4yIDAtLjQuMi0uNS41LS4xLjItLjMuNC0uMy43bC0uMSAxdjEuM2wuNS43LjYuNS45LjFhMi4zIDIuMyAwIDAgMCAxLjgtLjlsLjkuOS0uNi42LTEgLjVjLS4zLjItLjguMi0xLjIuMlpNNTkgMjNsLS4xLS40YTEgMSAwIDAgMC0uNS0uNGwtMS0uMy0xLS4zLTEtLjUtLjUtLjYtLjEtLjhjMC0uMyAwLS42LjItLjkgMC0uMi4zLS41LjUtLjdsMS0uNSAxLjEtLjFjLjYgMCAxLjIgMCAxLjYuMy40LjIuOC41IDEgLjguMy40LjQuOC40IDEuMmgtMS43bC0uMS0uNWExIDEgMCAwIDAtLjUtLjVsLS43LS4xLS43LjFhMSAxIDAgMCAwLS40LjQuOS45IDAgMCAwIDAgLjhsLjIuMi40LjIuOC4yIDEuNC41Yy41LjIuOC40IDEgLjcuMi4zLjMuNy4zIDEuMWEyIDIgMCAwIDEtLjggMS42IDMgMyAwIDAgMS0xIC41bC0xLjIuMWMtLjYgMC0xLjIgMC0xLjctLjMtLjQtLjMtLjgtLjYtMS0xLS4zLS4zLS40LS43LS40LTEuMWgxLjZjMCAuMy4xLjUuMy43bC41LjQuNy4xLjgtLjFjLjIgMCAuMy0uMi40LS4zLjItLjIuMi0uMy4yLS41Wm03LjUgMGMwLS4yIDAtLjMtLjItLjRhMSAxIDAgMCAwLS40LS40bC0xLS4zLTEuMS0uMy0uOS0uNWMtLjItLjEtLjQtLjMtLjUtLjZsLS4yLS44YzAtLjMgMC0uNi4yLS45bC42LS43LjktLjUgMS4yLS4xYy42IDAgMS4xIDAgMS42LjNsMSAuOC4zIDEuMmgtMS42YzAtLjIgMC0uNC0uMi0uNWExIDEgMCAwIDAtLjQtLjVsLS43LS4xLS43LjFhMSAxIDAgMCAwLS41LjQuOS45IDAgMCAwIDAgLjhsLjIuMi41LjIuNy4yIDEuNS41IDEgLjdjLjIuMy4zLjcuMyAxLjFhMiAyIDAgMCAxLS44IDEuNiAzIDMgMCAwIDEtMSAuNWwtMS4yLjFjLS43IDAtMS4zIDAtMS43LS4zLS41LS4zLS44LS42LTEtMS0uMy0uMy0uNC0uNy0uNC0xLjFoMS42YzAgLjMgMCAuNS4yLjdsLjYuNC43LjEuNy0uMWMuMiAwIC40LS4yLjUtLjNsLjItLjVabTcuNy4zdi01LjdINzZWMjVoLTEuNnYtMS43Wm0uMy0xLjZoLjVjMCAuNSAwIDEtLjIgMS40IDAgLjQtLjIuOC0uNSAxLS4yLjQtLjUuNi0uOC44YTMgMyAwIDAgMS0xLjMuMiAzIDMgMCAwIDEtMS0uMSAyIDIgMCAwIDEtLjgtLjVjLS4yLS4yLS40LS41LS41LS45YTQgNCAwIDAgMS0uMi0xLjJ2LTQuOGgxLjd2NS41bC4zLjQuNC4yLjUuMWMuNSAwIC44IDAgMS0uMy40LS4yLjYtLjQuNy0uN2wuMi0xWm01LTIuN3Y2aC0xLjd2LTcuNGgxLjZWMTlabTIuMi0xLjRWMTlhMy4yIDMuMiAwIDAgMC0xLjQgMCAxLjQgMS40IDAgMCAwLS45IDFsLS4xLjZoLS40YzAtLjQgMC0uOC4yLTEuMiAwLS40LjItLjguNC0xIC4yLS40LjQtLjYuNy0uOGExLjkgMS45IDAgMCAxIDEuMy0uMmguMlptNC40IDcuNWMtLjUgMC0xIDAtMS40LS4yYTMuMyAzLjMgMCAwIDEtMS45LTIgNCA0IDAgMCAxLS4yLTEuM3YtLjNjMC0uNiAwLTEuMS4yLTEuNnMuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS4xLS44bDEuMy0uMmMuNiAwIDEgMCAxLjQuMmwxIC44Yy4zLjMuNS43LjYgMS4xbC4yIDEuNXYuN2gtNS43di0xLjJoNHYtLjFsLS4xLS45LS41LS42LS45LS4yYy0uMiAwLS41IDAtLjcuMi0uMiAwLS40LjItLjUuNWwtLjQuN3YyLjNsLjUuNy42LjUuOC4xYTIuMyAyLjMgMCAwIDAgMS45LS45bC45LjktLjcuNmMtLjIuMi0uNS40LS45LjUtLjQuMi0uOC4yLTEuMy4yWiIgLz4KICA8cGF0aCBmaWxsPSIjRjc3NDEwIgogICAgZD0iTTgzLjUgNTguM1Y4NGgtNC4yVjYzLjNMNzMgNjUuNVY2MmwxMC0zLjdoLjVabTI2LjEgMjIuM1Y4NEg5Mi41di0yLjlsOC4zLTljMS0xLjEgMS43LTIgMi4yLTIuOC41LS43LjktMS40IDEtMmE1LjIgNS4yIDAgMCAwIDAtMy45Yy0uMy0uNi0uOC0xLTEuMy0xLjQtLjYtLjQtMS4zLS42LTIuMS0uNi0xIDAtMS44LjItMi40LjYtLjcuNC0xLjIgMS0xLjUgMS43LS4zLjctLjUgMS42LS41IDIuNUg5MmMwLTEuNS4zLTIuOSAxLTRhNy41IDcuNSAwIDAgMSAzLTMgOS4yIDkuMiAwIDAgMSA0LjYtMS4xYzEuNyAwIDMuMi4yIDQuNC44IDEuMi42IDIgMS40IDIuNyAyLjVhNy42IDcuNiAwIDAgMSAuNiA2LjFsLTEuMSAyLjNhNDEuNSA0MS41IDAgMCAxLTMuOCA0LjdsLTUuNiA2LjFoMTEuOFpNMTE1IDczLjVoMS4yVjg0SDExNVY3My41Wm02LjIgNi43di4yYzAgLjUgMCAxLS4yIDEuNSAwIC40LS4zLjgtLjUgMS4ybC0xIC44LTEuMi4yYTMgMyAwIDAgMS0xLjItLjJsLS45LS43LS42LTEuMS0uMy0xLjVWODBsLjMtMS41LjYtMWMuMy0uNC42LS42IDEtLjguMy0uMi43LS4yIDEuMS0uMi41IDAgMSAwIDEuMy4ybC45LjguNSAxLjJjLjIuNS4yIDEgLjIgMS41Wm0tMS4yLjJ2LS4ybC0uMS0xLS40LS45YTEuNiAxLjYgMCAwIDAtMS41LS44IDEuOCAxLjggMCAwIDAtMS40LjZsLS40LjctLjIuN3YxLjZjMCAuNC4yLjcuMyAxbC43LjdjLjMuMi42LjMgMSAuM2wxLS4yLjUtLjYuMy0uOS4yLTFabTcuNCAyLjNWNzljMC0uMyAwLS41LS4yLS43bC0uNS0uNWMtLjItLjItLjUtLjItLjktLjJhMiAyIDAgMCAwLS44LjFsLS42LjVhMSAxIDAgMCAwLS4yLjZIMTIzYzAtLjMgMC0uNi4yLS44LjEtLjMuMy0uNS42LS43YTMgMyAwIDAgMSAuOS0uNWMuNC0uMi44LS4yIDEuMi0uMi41IDAgMSAwIDEuNC4yLjQuMi44LjUgMSAuOS4yLjMuNC44LjQgMS4zdjQuM2wuMi43di4xaC0xLjNsLS4yLS42di0uN1ptLjItMy4ydi45aC0yLjNsLS42LjMtLjUuNS0uMS42LjEuNi41LjUuOC4xYTIgMiAwIDAgMCAxLjctLjhsLjMtLjcuNi42LS4zLjZhMyAzIDAgMCAxLTEuNCAxLjJjLS4zLjItLjcuMi0xIC4yLS42IDAtMSAwLTEuNC0uMy0uNC0uMi0uNy0uNC0uOS0uN2EyIDIgMCAwIDEtLjMtMS4yYzAtLjQgMC0uNy4yLTFsLjctLjggMS0uNCAxLjQtLjJoMS41Wm00LjUtMS43Vjg0aC0xLjN2LTcuNGgxLjN2MS4yWm0yLjMtMS4ydjEuMWgtLjZhMiAyIDAgMCAwLS44LjFsLS41LjQtLjQuNi0uMi43LS4zLjJ2LTEuMmwuNS0xYy4yLS40LjQtLjYuNy0uOGEyIDIgMCAwIDEgMS42LS4xWiIgLz4KICA8cGF0aCBmaWxsPSJ1cmwoI2QpIiBmaWxsLW9wYWNpdHk9Ii4zIiBkPSJNMTQ3LjQgNzEuOGg3LjJ2OC45bC0zLjYgMi42LTMuNi0yLjZ2LTlaIiAvPgogIDxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGZpbGwtcnVsZT0iZXZlbm9kZCIKICAgIGQ9Ik0xNTAuNyA4My45Yy4yLjIuNC4yLjYgMGwzLjMtMy4yLS43LS43LTEuNCAxLjRjLS40LjQtMSAuMS0xLS40di0zLjloLTF2NGMwIC40LS42LjctMSAuM2wtMS40LTEuNC0uNy43IDMuMyAzLjJaIgogICAgY2xpcC1ydWxlPSJldmVub2RkIiAvPgogIDxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiCiAgICBkPSJNMTYyLjYgNzkuOHYxLjFoLTMuMXYtMS4xaDNabTUuMi00LjRWODRoLTEuNHYtNi45bC0yIC43di0xLjFsMy4zLTEuM2guMVptNy41IDBoLjJ2MS4yaC0uMWMtLjUgMC0xIDAtMS4zLjJsLTEgLjYtLjQgMWMtLjIuNC0uMi44LS4yIDEuMnYxLjNsLjEgMSAuMy42LjUuNGgxLjFsLjUtLjQuMi0uNmEyLjUgMi41IDAgMCAwLS4yLTJsLS41LS4zLS42LS4xYy0uMyAwLS41IDAtLjcuMi0uMy4xLS40LjMtLjYuNWwtLjIuNy0uNS0uMmMwLS40IDAtLjcuMi0xbC41LS43LjgtLjUuOS0uMmMuNCAwIC44LjEgMSAuMy40LjEuNi4zLjguNmwuNSAxYTMuNyAzLjcgMCAwIDEgMCAyLjFsLS42IDEtLjkuNi0xLjEuMmMtLjUgMC0uOSAwLTEuMy0uMmwtLjktLjgtLjUtMWMtLjItLjQtLjItLjktLjItMS4zdi0uNmMwLS43IDAtMS4zLjItMS44LjItLjYuNC0xLjEuOC0xLjYuMy0uNC44LS44IDEuMy0xIC41LS4zIDEuMi0uNCAyLS40Wk0xNzguNiA3Ny42Vjc3YzAtLjMgMC0uNi4yLS45LjEtLjIuMy0uNS42LS42LjMtLjIuNi0uMyAxLS4zcy43LjEgMSAuM2wuNi42LjIgMXYuNGMwIC4zIDAgLjYtLjIuOGwtLjYuNy0xIC4yYTIgMiAwIDAgMS0xLS4yYy0uMy0uMi0uNS0uNC0uNi0uN2wtLjItLjhabTEtLjV2LjlsLjMuMy41LjJjLjIgMCAuMyAwIC41LS4ybC4yLS4zLjEtLjR2LS45bC0uNC0uNGEuOC44IDAgMCAwLS40LS4xbC0uNS4xLS4yLjQtLjEuNFptMy4xIDUuM3YtLjVjMC0uMyAwLS42LjItLjlsLjYtLjZjLjMtLjIuNi0uMiAxLS4ycy43IDAgMSAuMmwuNi42LjIgMXYuNGMwIC4zIDAgLjYtLjIuOC0uMS4zLS4zLjUtLjYuN2wtMSAuMmEyIDIgMCAwIDEtMS0uMmwtLjYtLjctLjItLjhabTEtLjV2LjlsLjQuNGguOWwuMy0uNHYtMS4zYS44LjggMCAwIDAtLjgtLjVsLS41LjEtLjIuNC0uMS40Wm0xLjItNS4yLTQuMiA2LjctLjctLjUgNC4xLTYuNi44LjRaIiAvPgogIDxnIGNsaXAtcGF0aD0idXJsKCNlKSI+CiAgICA8cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii4xIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xODggMTI1SDEydi0xaDE3NnYxWiIgY2xpcC1ydWxlPSJldmVub2RkIiAvPgogICAgPHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjkiIHN0cm9rZS13aWR0aD0iMS41IgogICAgICBkPSJNMTIgMTMzLjhoOS4zbDEzLjMgOS4yTDQ1IDExMi4ybDIxLjYgMjIuNiAyMy43LTIyLjYgNy4yIDEgMTQuNCAyOS44aDE0LjRsMTIuNC0yNi43aDkuM0wxNTcgMTAybDEyLjQgNC4xIDE4LjUgMTAuMyIgLz4KICA8L2c+CiAgPGRlZnM+CiAgICA8Y2xpcFBhdGggaWQ9ImMiPgogICAgICA8cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTIgMTJoMTZ2MTZIMTJ6IiAvPgogICAgPC9jbGlwUGF0aD4KICAgIDxjbGlwUGF0aCBpZD0iZSI+CiAgICAgIDxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0xMiA5MGgxNzZ2NjBIMTJ6IiAvPgogICAgPC9jbGlwUGF0aD4KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iZCIgeDE9IjE1MC43IiB4Mj0iMTUwLjciIHkxPSI4NCIgeTI9IjcxLjgiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KICAgICAgPHN0b3Agb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojZmZmZmZmO3N0b3Atb3BhY2l0eToxIi8+CiAgICAgIDxzdG9wIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6I2ZmZmZmZjtzdG9wLW9wYWNpdHk6MCIvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxwYXR0ZXJuIGlkPSJhIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBwYXR0ZXJuQ29udGVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCI+CiAgICAgIDx1c2UgeGxpbms6aHJlZj0iI2YiIHRyYW5zZm9ybT0ibWF0cml4KC4wMDUwMzE0NSAwIDAgLjAwNjI4OTMxIDAgMCkiIC8+CiAgICA8L3BhdHRlcm4+CiAgICA8ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMTk1LjIiIGhlaWdodD0iMTU1LjIiIHg9IjIuNCIgeT0iMi40IiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiCiAgICAgIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CiAgICAgIDxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4IiAvPgogICAgICA8ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIxLjgiIC8+CiAgICAgIDxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xNTA3XzMwNjU0IiAvPgogICAgICA8ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzE1MDdfMzA2NTQiIHJlc3VsdD0ic2hhcGUiIC8+CiAgICA8L2ZpbHRlcj4KICAgIDxpbWFnZQogICAgICB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFNQ0FnSUNBZ01DQWdJREF3TURCQVlFQkFRRUJBZ0dCZ1VHQ1FnS0Nna0lDUWtLREE4TUNnc09Dd2tKRFJFTkRnOFFFQkVRQ2d3U0V4SVFFdzhRRUJELzJ3QkRBUU1EQXdRREJBZ0VCQWdRQ3drTEVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkQvd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhRQUFBZ01CQVFFQkFRQUFBQUFBQUFBQUJnY0VCUWdEQWdFSkFQL0VBQnNCQUFJREFRRUJBQUFBQUFBQUFBQUFBQUlEQUFFRUJRWUgvOW9BREFNQkFBSVFBeEFBQUFCNzZnelpuSmpGTk43Rjk1TzBucVR4Sld5VVZYUUxMbklRc0FodVcwbnFUblk5S3Rlb2JoYm5QMlAwYzZWeU1mTzBGZWx3dm0xTXJYemFTUko4N1p0M3M0U081SWs5VklseWprb3dJZVdVZWl1REFsWUpKZFRwUHNuT1JEOC9YbGJNV29OK1lDUWVPaDBvU0dTeGY2V055bXIxSnpuYk5FOVBLeUdqS3FmMGtDNE8xQnhiSUlYN2s2M0xVeElqQy9rbGpJaFJDWXRLK0ZacXlFYmFRU1dZZ3o3TktMTnJQNXpOMXBEc2ozSHNXZnVDL3FScWxOSU5DVllOL3dCTDlYZnFUcEJ0akcvS3JtcFVTTEZMS0VsTGJEcGZPOU5wZFlkNVhmRGtkZDM2L0h2TFlBK2cycHVSSnVFa25nWURJYjNhTUtxOHkrZ25GQzdGb2ZicTFNWjhvWlEzMjBLNGw1MzVHM1ZYVnlLYlB2eHBpOWZWajFObTd2bUUxTGw5bWMyTnlHVnJSMXVvaW1Lam5iT3Iwc0hZanpVQ2NqeHhMR2Z1eTJiQjRTd0RGb25nWi8wOFVlQW1PZnAwRHZRdWthTVY4bjJZcGZZMU4xUGx6aGpNNTRkQ3pTeGc2OCtzZHlSTkRWL2gwSTFKT2pkbWRHdEs5enRwRVBzakJvYk02UXlOQWM3bkkwVFhibXZucVFmT2ZxZnBJd3Z6K2prSUdmMFYrcGVySFowU2d3NlZSVm1yS3RXb0lTdGdsTVRZWE1lVzhkMmVPeFZnVmNVYVdTNUtJeXRUNldhYjFKU3VaaElEYnA2dEg5TEorUjNPMUpBbHpDSDlnb1diK2Jzc3dZOWV4elo3bEVaMzhrcUJxd1M0UUN6aldnaHVjNms2eEV3WW1zR2hzUEFmRzdwZytKQk5KbEptbG9qQTZXZkxuNjU2bFo0NTJzT2sxTjFNUEs2N2lVNHh1N3YrcVZxbVFXanlFUlJabmpndGJwQzh6WTk5cUsyWGRGV2NrazhHVUtxc2ljdjhoY1RRWm9mcVhhcEtOYThBOWg5Zm4zVndiQXdnYWI3UkQxdFYySjduMzVyV1ZFb3V0MTlxWWg1Ry9XL1F5M3pSUm1kck1NU3BxdzlaVHJuNWJaVHpxd05mNGFMRTlBeHU5WGRibjJHbEpCZGNxdmpMeXh5ZDREbWR0dnM4NjgwS21TcTRTeTVoMDNKVXdMcTFNYmk3bEZRL1YxYWlVT1IrRjFtbU4yTGZIUDBVaWRHbTlxeDBiTmR1Um82VTE2bUtqSG9BOFdtS0pBV3pHNm9mMHE5TEpJak5jOUhMZUdDZHdhbnR0ejJKVjhFc3I4L1RrZk84VllDWTM0ZjFkVXp4bTBXR3pPVXNESVBMMlhqbDNxMlE3bWwramtnQVZBcGlqeU9xZ0ovOURKWmxaWTlJd041OTVPOXM2ODVqcFNNaGVLdWZzaVhBMXF5RlROcTdjZzVuY3NGV1NtSE1UQlFOOE9CSHBObU5WOXE0d3NDQVo3cUd4WEpOSE9WUVZkMVpVSzdyU0dxcXhVcXFyb3dDNDB2ZmZiNTZpeFBFUW1sZWpuamdRZ2hsT2xpS3pzOVM0NDNMa0s3bk82cHFLOEtyUzZXZ3hyblJLeGQyY2dBWEtTcVdRaXNsc3N0VGRURW9jYno3Ym4wYnNSemtBa21yTU9sTm9aT0VwMUYvU29aamJYSWFqN1NlYXRWRlQ1TWVWMFBvWkVrREtuUzcvcFV1NjFIMDhhQ3lPMHJ2eis1SVlGQkVrSGxiVmdUYU92TkY5a1VnUzhNWHZyVlBNWjExUkJlYU1HbDc2a3N6U3VFTWhVVk1zeGxCalNUYXZSeXAzTXh6bVVrcSt5UXhJWXRTL3dBenJsVHJXN2dEWTB4QkcwV082cnRnVHlydEpuTEV4cTZBYU9sWHk1V0RZMGh3dWtxVVkxZWpqUTJEUnNEcTVQZDE5a3E2dk4vTDF5SHFidXBkV0JlNUZUaWQyb25kMU1kaEtsUzVFckpQSjJ0elVwdzdNL0dTb2wxOVFZU3dVU2YveEFBc0VBQUJCQUlCQkFJQkF3VUJBUUFBQUFBRUFRSURCUUFHRVFjU0V4UVZJU0lXSXpFUUpDVXlRVE5ELzlvQUNBRUJBQUVJQWRDY3lTbGlZblcydW5Fbkh1NE9tVzdOdFFZNFh4eWVSdk9jWTFtZHVQUk1sVkV5WjJTeXFtUlRxcTRQTmtNbk9OeFYvb21iWmIvR0F6VDVGZXg3YnRzRUJOczJNZWhXTE5XMWVBTzI5NkN3UERyWWZLWmFkVGRScm8xOG1rN1pVN05ha3VENlptdGxybXNUckJFazFQSkRtaWpOcUNHTlNzSVNTRk9HNG45Sk1tWEpuL3prdjNqZVVYSUpGd2VYSUg4cG5DWnhpcjJwem5VR2Ryd1pJYzFhbjlQYkJ5czNhZGZnbk5iby9zeFFFRlQ5VCtveDIwbnlCeFB1RHBtK1BOV0kyUnRnbndQVGVhTkMzUlJkUTJSU3dNWTZzQWJCTTFNbzVlMWlOeUplNU9jUk1WZU1rZGhEL3dDY2xkbmJ6blpqZnJJSk9NZ2x5Si9LZjBuZDJzWE50Rzl2SXhZaGpCbGJkbXdKRkUyYlhoUlpJRldMY3VoOVRaelNtMDl4UVQwVnZOU3IwWEVoUStXYWZTR2wxSnNVczI2TW5PU0JJUUE1b3lXcE1CRXNhSXFCVGNweG5LY1k5Y25jcVpQSmlwem5IR2NZamNiOVpCSmtNMk1rK3NOay9GVVN5ajcrZTQ2bWI0R2xJYjdGcXhFaDFjWlI2NWpISHpSRFF2bmwzM2FsSW1aWVV0QmFXOGZKV0gyc29tdy9HNEtKSWFMN3owWXJUa1dlQ0Q5bjZHYzZOMlJ6OHBrNURZbWR5L015dUlkR1E5dmVuYzFXcTNFeDBKTTBibWlBdEpaQ3JUKzNHS3FMa0wrTWVaMk0rZ3p6VENaSXpqeGtkQXE0UUhNK3JkMjFwWkloanhuMXFjQm81ZXFjNU1HcGxTaW5xK1NoYkd0TzVlWFI1dE5HRzIyVTZhdjJtdUhwMnd5MzJ6Z1NJeHd1cTdaQ2NLeklwR1Q5M1kxNnRYTEErQVdKc3BGamJBenVmNGRZMkZoa1hnY2lvOU83SE5SRTV4dTVWZ0Jzb1pFKzhWTG5veks4eHBvN1prNHlVcHNEZVZsMm9QM1c1WWJCVkxGTWlTTjhsWXg2UUZLbGE1cWdqaGtIL2dPenNHYW1ieUhBYnJSc0JGZ1RURTFmcGhCcUdHN3VrcXluYlhybE5iRjJOYUpGRXlNYnFKVkFrQThzbzd5VFhxdjBuZFArcHNvSkV3RjdEY0JFUXRuellIVjFvMk1hWS9YS0kyQjNNRTd0WFllNGJUK29NSndrY0JudmlsQ3E2Q0NLc0hlNlowcmRlT25Yemd6M2RCSHlRR2JDYkVra1BVYlkzVlVvc0xLR3JxTnh1M09QczlRMTU4Q2lNcmJvSDQ1SzlCNXhYMTZxdXVRUG4ydWVYRlRzaFRPdFhVZVlUL0FWVEFvUzRtcVFsWFJNL3dEVkI0dFlvYWVqaW1zRWU1b3BXMUJRVHVScnArbDN5ZGpBV0ZHK0Nta2tyTkRFcjlwZTd6R3hKZkFPUncwVHdkckZmWEViTHBtejZ6c01mdmFob2RwZVdUbHl5OGNNYTYxcm9XakFXYlBKSk5vRllGQTRuSVdXZERPMmNldXJLeVVkYmFsNnJ0aVNzUkdkUGROMk92c2hiY25iSnlhMUd3dy9IN1ZQSWtxTk8zRVNMc1pyZXgxYkxUc3VDRzk4U3RUcS9YeFYyMWx4dFZYSUkrQ1FPcThpY3lidEtjR1Uvd0JCc3h0V1c2M3VLdWFXd2lUdXQ2OXRSVUM2dFdVT3FpUVFNaldrcnlZMk1GTXNLNHdpZHdVTnJSTkhWSng3Y09MWnRYbWhJMGVwTG9ORVpJbERTd3dNYTlJYWp3SFBOd3VyQXVaRUllY0hDUkdxT3AwbUFzcElNM09tbUoyMFJTS2VKYTJxbXVTUUtpYTBuZVNaU0J4dS9zaWJVR2FZbjQrQzgxdHJQM0k5YXVIU0N1RE02aDlMS1N6UGR0TXV6ak5FdENZNHdXZWRyV3B1ZEJCYksxMGhIVGNWMDd2YTZjMElMT3FiV1EyMFpjZGlYS0ZCdE4wTE1zVmlCMUExdTdtU3ZDWnZ1b1F6eFZ6eXk3QjVNZ2lValhlS2RyNm1kQzlRRW1ZNG0xUnJmVE4yRGV2YzdLMjZONm1Hd2VQV3FhbE5Qb2dGMmIwMGdzbzR3T29GOHdQYWhoc3ZrU01FZUZvVnZCRk15dndtMnJtZXhBNC9mTm9iMUNHcHF5enNDR2txQzJtWXJMZEdQc29mYW9Id3YzV3pKa3U1NHBHbXk4dFJLYmNpcTRnNnVscytvRk43VW5sMTZ4NytvMEcwVnRqQjY5bzJkUGFnYlBHSXRsVGdlTDFoMzlNOVBuUEV2RjlpSjQvbmdQS1pTVXBWc1IwWXZHV3RPWlFrRVdUS2dHVnhSUmtFRlM0eXdiNlZYNEpaUGJOaEw3U3ZOQ0RGUGNGQWd1Nmk3QllXT0NtcGZhMHd1UDNoNGcvYkl1QjM3UVZPUlRVdGNFeU5ZNTJrRHpvNUlhOXNmeXp5NVJIOTFaRks3cmJFSS9iNXlnMjgvd0RJUE9UWmlxSzYzc3FZR3ZIbnE5eElyNi8xQU5QdkE5cDE0ZUowSVV0Y2ZJYksyTWhEbkZ1U0NidUo5b1VGc01jbmYxWDNQMzVmaFFORk0vVGl0bGRXWDFYc29iREVnUkZhNWtqbFRqbFpwaFdPNWw2djdqSk5RRXhEZExxeU9xb1IweXJzNGFMWmlLd3JZdGVuTlpIRGpuRXppdy9EVHNPZllqelF4Z2V0UC9qcnFjY09CUTI3VjFCYkcxUkszY2VaWHl6eXg5dlBHVTlJT2xuSVBsblJHa1NFdk4xZlYyUjYySDVOcld3MDRUNStvMG5xdFNiVEQ0WlpiVVgrV1M3WFRoeHVrSXRkKy9VUmEwMnZMcXBIM0k1dERPeGVIRUY3RnJPNWdSMUIzVVN6cG5lSTJuNnN1dFo0Z3hiMnkyaTBqZEN6YU5VMk8wcmMxeWxtRnJob1pkam9CYkNtbWptMUhxNEcxalFMY1RZOWNzVS90M1NnSW5kaU1zRC9BTUtucWRiRDBKWHhvNFZtOHFhUnBGMUhBK1Y2WXFLajFUTkZtUS9ZSmt6Y2xqQXJEVlhwNXRNaEdyaSs5c1JJTjVVRVY4bGV3clZwa1lOdk80RldsVzF0UnJlcnhuNk1RWFk2dE5CcW00UkNzK2ZBL3dDelhsZXFMMnBZMUpKTFo1ckNaTHZialVuSmpqb0NhS2NENThKRis2L1l3Tzc3WGF3V056WmQ2anJxQTBzZlI5WEJzNUt5ZWVtc3RkSGNSWG53YmJSZ3ZkT0xmOVdPd1Y2UTNHeGU2Vk1ZZHFQcTNGbTlGMktxS0N1SnNxYU9GSXU5dWoxMW9Ic3RvM09vWUUva0lTR3JKTGlDaVdHQTA2UlB5ZkU2ZFZjNDdtQ1ZTb0tnbXhoMU9NQkZtTHRkdTdwR2lPYzNsWWdrVmZ6S0VlejhveEtvNHU1TWZEYzBoWXMxZkxNNE1wMzhNcmlHTTVjdGNYL3RsNEVUTFVFc3pTYlhZeDRRNWNuRXZEVHlpU1pLNjJqWHRXYW1zSHB5KzcwMHdnUlh3NnByZHRXc2ZNOGtZK1ozN25odDRXY1JTckpCNUhpNzdGSStsT0pUUUZqc3Rpa0FhM1dBVnhOUkJqZXIyMld1VjRKRE9UaDRFall3ZStIL0FFeWRGYlZydDFIN0U3MDNRZE1adWJDZjdlR25EZ01rbUprMmV0aWlBWVl3SGNwUlkvRWF6Y3c1UDlrMjJ1Y3pqTHZjRnNTWDYyTlZhKzJ1Q2lIamk3YTFIektscFJQKzFJdDZCcisxWFhtdUorS3k3QnJqZVVTeDJXaWlUOW8zYlJ2L0FKbFJYVHJTZUZkdytibkRMWkowcjFLRUlFZlpsUkVSTVYyYlQ3UTRqN01jYXhPc0FJckIyeEZXVjBTR0JERnFiV2pzYk96VlJNZHFjSEg3T3RmTmxTSEptMFEzYVZVMDJDYXMzd284ajlNall1dGpvbVgxRVhUV0xkaGhDdkRqRWpUQ3J5eGxtZFY1OEpFaWNMSnIwTDhzZFpWc3Y0TzF4cm84aDF5QmZ4eDJ0UUovSjBDMnh5b1B0OVBQVXh4K2V2UEJyUW9LNEdhOWtUL1Z0OUwvQU5zTFQzQTV4cGc5VzNVdXRIbGkxTUsyYmVudzNua2J6eG5sWW1lekUxdk9RMUZ1VnNwbzlEUFFiSUlRQkJaUkRpZHZDUmdDUHhLVVJ5WnNHcEIyMUdYWHJySlc3MnJZMEcwK29LT09NTHZKS0lGYytBRHlXbkRUNnljUUtKRlRKSXhJK1hZUVJIM2NOZ0VjRWVRMSsrUnlsejFZcXhheTVqdTdQZ0pGK3NUWDVQNFUzWDVXaXpPU3JMaWpweFdKcUliN1c3dnA0VEtHemlJK21VQlRvOG4xbzVVK3FZZWVyMmdvYWZZWHp6bVU0eklhaVpjanJaWTArbVFUdHg0MHo0WG91Z2xRUkJPZ1hVWjJIV0ZzbzdCMy93RFhRcmtzS0lpNVBYdWxWVnd5cFZmcFBRamgvd0I3SmkvTGxQUzJjNG5iS0FaL2piaU1iaXRabGs1a2RjVS9LMGwvb0Q4ZEs1VytPN1Y1am9YNUdRMlA2eHhiSGZTSHZhN2NIZDF0TC9rYVZtUnB4amNhMU03RTdWelVZbTl4bkdsZU5sdGF3eDhZL2pqSmV4RnlXZU5xWVFVekNaV3Z6Ly9FQUVBUUFBSUJBZ01GQlFRSEJnVUZBQUFBQUFFQ0F3QVJCQkloRXlJeFFWRUZNa0poY1JTQmthRVFJQ05TY3JIQkJpUkFZcEtpRlRNMHd2QkRVMVNDMGYvYUFBZ0JBUUFKUHdIa2xxWC9BRTdiMXVsYVNSZ0Evd0FCNEZ2VjkxcjVNdkMxSExkTFhwMllXdHJXSVNKQU9MRzFkcHh2cHdRMy9LcE5tUU13RDZYRkhoVWQ4NUFyVCtBUGZGcTROY1Z4SUFwM2ZKd0JwREhoY05JUUYrK1J6TlcwMDBwWnBNVVZPNGd2cHpxMXZMaFhpUDhBQkRVTlJGaTF0YUFLUHh0VCt5enlPWGY3cmU2c0swbUpnYndqaUt3RFJUN0syOHZFVWxvNWo4SzExdlh3L2dXN205UkZvanhQV2hxRm8yVkJjMTJTaWhteWUxdU9OZG9CVzZJbFFDTUl3Tit0U1dIQlJUaTk3ZlUxckQ3Sk9UWnE0SDZTaXVCeGFwMGVYbGxGci9VMVBJVmhoaG94M1daK1AwY2N1bElIelMydFE0aXBDakRpZktyNVkzREN1RnFqMXRhOTdXcWZNMmJrT1ZZcE5KQVg2cUtmT3A1MXlvMUlxb0c0bXNaR1QwdlV3WjE1ZlRNZHFWMFJSY21wMmdrUGNFaTJ6VWVJK25FQWJLVEs0SnRZSG5XUGhMV3YzcU43b0tIZEZLdDlvV1BYNkd0R1VOelVoRWlXVmkzQTFqSS82cVNjWWRzU1V2STEya2hEV3ViY3VYb0t3a1NSQWFhVmhrU2JXenJwYzlLbHZpVzZjcU04elN5SFp1RnZwMHBtWE4vSXgvU2laRlJzOXRWMTk5UXZDQjR3YldyRkY4cmxjTzE5V3FPUlpsMGRtR2xQbkxEZFZkU1Q1QVZIaERpMS93QXh0cG1JdnlPVUcxVFE1Z2QzTkJJRkIvRmxycy9iNE10cGljTzRrUlI1MjRVNFBwUmNabTNyZEt4YkNOSXdVVkh5N1ErZFlkemRiTlk4cXhLWnNNTm5xM1NuUWtqa2E3aWs2VnlGVEtOcnV6TjBydFI0MjhWalhhTHVmeDFnSHhKdzBVT0Z5UmdFNTh0MlkzNVh2V1JuZmdWNVZMbUNTWlNvcnRQMlRCeERhWXVlWFZZMEg2OUJYWjMyeWpMUGo1UUduUDRtYmRpL0NBVDZWMjg3RzkyQ3htVCs1aldPMnljNDhRaFVOOE5QbFFPQng1anRsYXhWZ09ZdG93OU5hTzF3enRaWEhjRlkvWllYbmsrN3pwQkJob2ZzcDV1OFNlYS96TjhoNW1zRjdTRllvZHM3R3hHaEdVYUw4SzdKdzBXeVRPU2hZRWFhMnNhamthSXJ2d1NIZVpmSnVaOG1xeUpNTXp4QVdGK2VuaFBVVWk3ZHRFcGJsMzBnSG5VbTNubUdrU1hVVzUzdHZINGdWMjAyRFMrYlpZWlZqK09VYS9Hc2V1SXQ0WmJQZjNPUDFyc1lkbVl1VHZZbUcreFA0MFBjL0VOMm1GaU5DS3hKbGtkOXBZblZiMWgyMnJOY1BmZ0t2UVZuZGVCZkxXTnRJTklzT3JhVk1yNHZGemFKZm1UVTM3eFBkNTVoeFZSeGYxSjBXb2NrU2QyTWNQVTlUWFo2cEhIRjMyYTVMWnJBZjAyMXJCS01MSVl3WlVhekJmSHo5S0RKbGJNTXZoUFVkS2pVNG1PTW4vMkhHM3FLbFpzYmlpVlZ6NGN6SEw4QnJTQ3lpeW5uV09seUdReTdMdzZwbFByMXFXWWJOZG51N3R4bURjeDFVVVFEeHZWOGs3YjYyLzZnOFh2Rk5mQ1FYY0o1OHFpdTVGb0l6NThQaitWVEZpNXUvVnYvQUlLN09tdWl5QnBuRmd4VjhvOTVHdGRtdnNXZUViZGVTRytmNFdBOTlaaGx1Vkk0eG5xRCtsTGFURGJqNVJvUFAwdHI4UnlxNnlwWXlhNk1CUTNBMTA5S1lMcHhwNUkzSzVWWkRhc2JQSUIxT3RTeVpNR2p5aFdhOXlGMHJDSmlzUm5XRlkya3lCbFVjTTNMVW12Mk03UmpqUXZkOFBhWVdIRG1MODZteHZ0TWdKV0xZUEU1dHgxT25MclhiOEF4VFNiRFpQSmVRU1h0bGJ6dlhaRFN4NXlvZTlycmt2ZTUwNDZVTFplSU5HNnd5TkYvVGNWRGo1a3lFWlJFcWN3Ymc1aDBQeHI5aTFNR1E3MCtMVmQ3M1ZFWWhrKzNmR1Jxa2drNWlMbGJ6cDVmOFJURENMRUVQM3Q2K3A2NkNrQ2trRmJra1pxWVdGdHBiK2JTdTRtaTlOQllWZzhSRy8yUy9hcmxKelg1ZVZxN1JXT1dCYnlCZFdUL0FKK3RZV1YreklTc0dKT0xRUmJWbTNpUWVxcnd0NTEyVE5PcEtBT25NTUd1ZW1sZ09QaXBESHQ0cE1QS3AxNGJ5L3FQZlYzT3lLSHpJMHJER0FwdUZHRmpwVWhBV3NZL2FHQndzNHhDRjd2SU1PNHphRTZuS1RXRXg2aEIzOWhvUldEbEhaTFJiS2VYSllLVHpQeXJVT1JLUGVLYjdXUlM2aTNJY2E3Q1IxbGN5c1lmc2lzbjNyalcvSFd1eWY4QUQ4VGdwdzI2MWhJUWRNM1hXcEZkTmJNRHBwVDVRY3hBNjA0RHlTTlBCYzh5Ym41MU1rY2tZSWpFcldCZTJpMU43TW14QmxjZUM0NVZqcG1FN1pGekhOdEdiaFVOc01icXJxdDk3TnUzNkMzem9oRWlRbGI5YWtaWW5ZaUZ2VFFWbUUyRWsyVTY4MWNhTjg2bFdLUFM3T2RCeXFDZkQ5bDdSVGlzWmh5ZHJqV1U4SVJ3QzNBdTQxTnRLd2VKYVlIYm1YRjc3a25TK2JyWWNPbFRvK1cyYksxN1hGeDhxRmxHZHY3YlVMQ1FGOWVoSk5XdGxDdmJyOUVya2tTZTBSd3R2Q3lrQlBXcHlUSm1obEQ2bkp5UHFLN0diRVFhZ3lBMnorNmdZOFJoeHM4c2gza1AzVCtocDhaakhlNm9tNlZnVTIwWG5iU3NZMndNUVFZZktMQnZ2WHJFSEVSVE9wamp0YlpEMTlkYWQ0TURHYzFwUEQxc2VZdlNObFhkeUR3TDUrZExKbVZzNDFySTdKeGJMM1c4eHlQL0FBVlB0MWJ3dW8vS2h3cVJoYTcrdnVwOWxFVTJVWUI3eE9sNjd4UUVtay9kdTFyenc2YnJueHgvaUIzaDYxalFleUdiYVRSaVBON1FvMXlYNWZyVXE0VVJ5cm5Fc0oxakhFQWNxeHdqd3NZWVRRWkw3UW5nYjhyVXM4VzJGcEZmZVVnQ3k2K0VqL25XcDltOG9zN2ZkVHhIMXRvSzB5akwrRVUrZHB0NzZNQkxoOFpsR2FaWkxwTDUyNUdwMVBzaEFXM01kQlVZTE9tZlh6cFFyUS81aWVHUk9ocU5zUGlocTBjbzQrYXNPTk9DbzU1eFdPd3FaZnZ5QS9JWE5UaVJ3dVo1ZnVEK1ZSdzkrdFJabk9wYm1haGFwWFJjVXBFaWNtcnMrN2JQYVhqYkxwK1h5cnNwM2VjRmx6VGtMWWVnRmJPR051OGtTMkI5ZVo5OUs4OG1kYktQV3JncWlnMG05R3UxallIZVJ4d1lIa2FuMytCY3JjUCtOZjhBY0tuMVAvYWNPUGh4K1ZUdVY1M3N2NTFoOWxIenhNbkQzSG43cW5hYWJKbmxjOFNhdm1jMU5jZ0hTLzBMY1JSMmJTbzB6dmlpZTc1Vk1ES3QxOUFEcFJGcEVJcFEwa0w2TjFGSkpIamlBTjF5RFUwOHM3d2xzOGh6RlR6dFM3U1BGNGZaa25UTGJXbVdpdDZqUXlSZDFqeXEwMkdnZ0NScWVBdU5hVlliTUVjcnpVOFJSRkVWS3RaWlhTSTVWSjRtbWx6c1pXUHd2WFlXRFo4SEprV1JvZ1djZFRYWjhNVDhMcWdyT1h0MXB5WGMzdWEwVWREU3NVTGFHc0NqNXRidUt6eDRhU05UbVhqbXJHWXY3SWJVNW4wdlVNbTh0NlNVZTZvV1krbFljczBYRlNLd0VLN1NLMjlMcWI2MWhSRjdLcEJ5dmU5WCtORS9HbFB4cnRGOFBHQW9rQTRsclYydEppSTlxRXlzT0I1VkVLME5QVEMyUzlZSEFoWWcyVzdHNTBwWW9wWkg3c1pOcmNxZGo4YXZyNVZHYzNRQ3NLNk5lc0xuSThxdzVVZWxLb0xXNWNhVEsrVUVXOWFMdGh2WmcrUit1bFFqV29RS2gzWlFiNmNUU2tLQndxSE5MaW1aWlZrMVUrZFliSy9NV3FENVZGa2MrTExVZHBUYk1ldEx2eFNLeTY2QTFIdEc2MnFLM3VwQjhLd3NTUll1MExUSGlNM1FWaXBXV0VaUVR6cEVrSEZzNjNyMmY1VWNQOHFmQ240VjdNUGhTd04rRzFZY0gwcnRIOTN3K1hJVWp0ZTQ1MWo1TmpBQ3hVSUJtSG5XSlo1c2ZoaGRPU2cvVEhCS3VHaFptamtOcitockJ3UjdXUGFDUE1lRllLQVptYkx2RzQwb0JwQjNyVkhSMmJjbXRYYUtSUjRPZllCQkFEbTg2N1VSa2lzZG43T0xIMXB0b3pDOTh0clZHS2pxTkh3OERvNVc5dUZkbVJ4bVFadDZiU3V5NDlxOFJPMEV1N2JoVVl2enFPazA4cWlQd3FIWDBxS3UxY1poR1NNWnRpMjZmY2EvYVB0V2VIRVNoSGpNaTd3SjFwUWtNQ0JFVUhnS0JwVFFPemtqS3RZOHEvYTh4b0w3SmRtTjFPbkRwWGFJeFQ0QzJ4S3FBTmVONllVMU5YYlRZSER1Z21tSGV2SWVZcjlyWmNYZzVwYlN3bEFNdzZYcTFoUUZBVkpzOXBIM2dMa0VWMlQyYWtjUnliU1JqcUJwVWVFam53MzJDSmg5VnluVzVKNTFHdFJpa0ZLS3RTMW9UR3R0YTRTWXFORDcyK2k5Q2h3amI4cUVoSWpIQVVqZ0s2SnZEbldxMFRUbW8zTHpZZEhYM1ZoMkdmRWNhYzBUVjY1cWFrMzBsY0grbzA5d3JwZjRmV0ZjRmpSYU53Y2FwdDZmVThNTG41R3JheERsNVZyKy9mN2FXaFMxeXdhVy9xTmYrVVAxK3FvVzJNbUg5NW9hV2pZK3V2MVI5SC8veEFBakVBRUFBd0FDQWdJREFRRUJBQUFBQUFBQkFCRWhNVUZSY1dHQkVKR2h3YkhSLzlvQUNBRUJBQUUvRU9Ld1Axbld0OXhYNWlQeFVRbHhJWC9BUlIyQ0pFMWppVFlNbzdsYm1PeTVZTTBjalkyNjNVTzRLN292U1dZM0NVQjY0cUswbXp6VnV5SEJLc0kvc1gzclZZdjFhS2RUMVRQSVRhWUJUM0NxTE44bHpER3ArbVg3dXcvRnhKZ1k1ZXpPRmRwL0hFZHlWaGNHcTROcEVTNGh0YW9SNk5wU0VTYVRVSjJYNnBHOS93QWRVbEhqd1FuUUJvaUxSMjIwMXRWRldQR24wWURRcGtVOVhkM0hhY0NBSS9HSWgwem9NZFdDYXdwckRhT3FuUU01eUFNTDBvTnVjWlZmTmJYTXFoZFlXRENPYXJHTnhJcHdDMGViaTNiaHQwMFloM0pyZ0Qwc0FVUUNPcE95YTl5OEh4TVNrTGg2bW9aTmdHZndDSU1SVXVFNFNMenl3WHNsSnp4T01HRzdqc0F3elk4d0xxOHB0SXQ5bmhLcDVoaExsRWpTWWdBRVhEdGx2MmJaQzd6V0UvZ042bDRkUXdjQUFYdkd3dGxVL01haWJ3bWhnODBRWDlBT1ZpRU90Z2YyUXJ1d3NTS1JNZnlCTmcrcGU3ZXF3OVJVd1VXaXNKdDA0Y3JHT2JSQyt4MUNodzl3bkszV2dsSHdWTnh3QVFoNkFBT2JOTVg5Q2kzajJTdHRWaUhLdjhjeWprdkdhRGs1SEhMUU81aG5BR01KRDNxVUNLR1NGYUlPaHhTTGZKcUcwbENSVzZBSW8wbS9vZDBSRzdwMjN3UUtUVXNvV2t1U1lRTEt5aHdjL01rVkYyUW5IRTVENk53ZFphUFJ1QjhMSFZNdkVFUDhQaEFkN0xSTFZhSlJUTlZLTE5GSWh0c0dEMG1nSW4xRmduaEM2RkNReTVlNXZ3dlFmUWJLYVkySCtSUTRybWVCeGRDNHQ4MHB2ODNEYnVvYitIOXl1ckZEdEF6MndyNDZ3c3l1NnRycmFvN3VvUUZyMk5lTERuelJHNG5MWmo3bjdJQ1pRVzBibVhyZ25tTnY0SXJOS08xT1kvUVo3U29PRmhpVTd2dW81cEhDd1lKWkJlTGNoQ2NMR0hMNi9NVDRKekZMaEdMSU04MGdxZUF3TkVBRml3NzdrVlJSb2pibk5ISzgrU09iTE1QS0h5dmdPV1VGL0tYTFNRNytVeFMyUHQ2SC9pQStJUDFLNWlVeGFGcXJ0eVNoQmVRSlJ3c2M0QnFNRHplSGFWeER4RldDeXlsQ1hoczNhYkNjSzI2N0w2a3dFaDZHbzlFZ3c0aEFRYWdleE5sbCtJVlR3bXM1dDlHdzhXWEwvak9xZU1laTVGRWQ3SG0wbkFJdWFIRDdsSS9yQWV6aGJ6OXBnSzZ3UFhpK2pUbHU4c296eGc5WEZJOG5CNWhFd2dFUDFjVzNTUGtrQkdVMVdKNW1iUDJzSytKRmZRcUVaN08yTjk5ckRwS3NUbjFFZzFOMFM2bzhXeDZYRlJYRVo3WThSNVROMFY4TStMV1lEUVdnSEtuTGRycStMU01UR1lVYWRWTnZKbGpsdWczY2VDVUNpNldvdEkyam5vL0tIL3plQXRjdHJRN1hnYkJPVEhjUndQT3Z1RTF4YU5CZlB0NVh6Rkx4UHkxQTgwb0J4U1JCRXJ1bFBMTEVDUzh0Z0t1YnptWGFDRitoWUQyamh3WkF3L09ybmIxQXNCR2M4YWRqeDlIekJOR1V4SGd2Z1RBTUQrRmcxcGxFSEMrWE1sRkJ4dW9zdEtORHVuSGc3V29aZnlqZFdOcFRnRnBwRFNZRkh3MVR3U080Mk05dUpWdUhoVlNsMWxyTkV2YVFhcjhScU1LcWUxY0VWc0xlbHVGd05hNjFzRXVyU3BuRlkySXVKWVhvY0dTMlRUR2JyREtscml1dmgxczR0T0tVZ3Fxc0tIZ3VZTDNxYk9iRXZ2TWdKWElQTC96ak8wcXBGTUd4QUJTMGhRWDBGeDB0dlhvRy9KRll3VFVOaXQvTVB3eXZUQ1ZvMUE3SlpFSWVvQWx0VlY3VjJVYXFkM0NOUVlTb0lRc0xiN1g3akNZUWRKS2kxMVh5TmtIQWtqcVNxS1JXd290c1pLUDN5SzJZSEJRTmhZbWU0UGhYc1VzNitpSk8wR1lOQ2lpbk1SVTNWQnQxL3VvM3JjeVdxNVQxRHd4NmxQZ3NYTEVjOGthQjQ1bnpoSUtiazlBSisxdEtaandlR2tXL2hJSnJUaEkwMnZnNWl5akt3RGpHRmVETjV4OVJ4OFZZaTZ3ZXFia0JTSmZzU1BnZHhyVXVjbFBPYmgvVWFTc0lFOFdxUVplN0xjcmFXNHVSVVBOa0ZDNkJudy9Nc0JGNzJWcUtwbFVyeUxqeHpvalFneDJjajZGcmZXREs0eVBzRkN6NFFHSDZBaUtEYnJXRDQ0TmxLUTA2V01XQzNNdVA3TWZXU2xWU0tMOGhkZ1Z4YUUrSThFOFM3MVFCNzdlREJuQzBQNE0vaVQwNjJ3eXEwM0xNZjNVYlNvc0JwQjk2ZHQ0Slo4TndKVEpKZTJMQmdjaXdRcTE1dXI3SlpSSFZndHdXUFpZNEc0Tk5iMlgxVXZvOWMxS3dHdUMzeE52UTNLdHA1a1M2NDJGalVJSFNwaWVUNXZIUkZGajFOVzhneFdhMFlXSEYvd0RUN0pPTG1BQUR4Zy9wRDBhTVRCcDhrUlZwWUtpRENuSi9JdjhBNVh3TDNQYVpoRmhSNVdLU3QzYkdpaWk5Z09VY0taWGVhS1VJN05MOEhDa1hMb044Sk5GVXhWMVlMN2wzL3BCcEdyaWlrRTJBb0RFc2RvSkVwZnpkWHdLM3NYdU5VQXo0QW9KV1V2WEx5N05NWEhnazZQYkRZN0YzREc1eldiM0J3T3RHMW92NXFXWmNYRHMwenJzZVIwanN2aVZGM1pBOWd6QlRoUWgreGhoaXkyaDhXcC9JVEM1Q1lkMkIvS2ZTUHVtMm0wODNGKzMzYkZxcmhhai9BSXdlSzRtTkxTMGFoZVlXMTVHa004UVRsZWovQUVtSXE5SCtLUWtuNXFMOFZVNGRlYUNQM2E0WEhkbThlWHlRbzFsLzc4aCt4RFI4WS9yVVM2dXhWbXAzWWE5WDNIUzlZN3N2UGo0SW01UTN6TDg2ODhEQ1Z5TEFCenNEemRSMjBVcGJvbFF6NXRzdEFSRFA5dEpmVkI0dnArcGQ4NmdsNXVtbUtoc2hFYUNZdVFFc2I1TWdCVXpJcWs0ZHRRdjBua3VFbEdrUys4dzJhUlE5T1dHZjVnV2c4RnhoeXVsRUtsSHZIWlEyeEZCYTVCUVJMdU9HZ0E5amJIZ1p0dDhrdUFnQXUwRi9Nb1I4U0Z5RVdvdGRiWmJtNnlDSzBENmliWXRrYnkwNGdKZ0NLYnZ0akczNDVROGsxZE1GZ3VlWkljYzVnQS9BalJDZW9vekpZRVRjRWJVZ2p4NWdMV1JpakZMd1ZBdEU1TFRhSjFDN2RmbTVjZ2JqWEpYUUU5c2NabEpWcXlPMmhRNE5ic0pjeElySFZvMk9NYk1pNFZGdUs0aVBDS0pSSDdHMkZGdjRscDl3aGtEcEdMaTZuR2hrRENtNjBhWjlyTHNEaTVrUU1HeEFWdTdYZ2lyOUc3Q3NsV2FTVWxYVHd4RmV3VkpCbGt2NnVGMmVLbzJNaXFnNjdxTDdIbTN5SlMwTGhMbjFOZmR6S29UdVdVc3BMV1FuSjF0MDdZRllncmxSNGVwVGFrT1dRcHZqYXc1WWNKY2dLTDNLdGVlMS9pQVg3QVJjUUZPdlpRYU5RSlVJY0l2UkUzUUlhRjBmZ2hMQXlWY3VwZUYwNTJUUnpxUk9WV2N5OWl5aElXVldIZmpYQzVTcEg2ajZpS0lXamV2WXRvWFpXN25GRy9EV3RYWkVFanFnUW5GRWJLeWpKQzFHdUdCSVZ3KzRaaThOcFV2b3VVWkJkazMxVlhHeWFoN001STB3MHRSc0E3QjVjbFcybWdVQkwvakYrd1Y2VmFzSGZtQk5mSGdZRnJoSllqbjVXWVM1dlRvejduWENVV3czVTc2am9WYVRBNk9rOWJGUUxZaFpGeTErbzJnM0tLQy8xVW9Ud1FmMTZ3Mm1GMUJES0FlaVBhc3MwdHJ1UXVnR241SXJjcFB1dWc0dW9sb1MzVUI3Q21XTlNRdGlFckx3VCtKU05rRjgxRkllUFJDOGJLNWR5c2ozUlJzVWsyTG1zRVRET2RFRDlvTjFXbHZpUEkzcWpoVWd0U3JEU1hUTHhpcVUwNFJJeHkrTnRLQUdGN0RlT0FVTUtlNHYyTU9uVC9vbzIzalpOdDVnbHNwRGpVUkhuOFlERmErcDVnK29nQUEzNlYvN0VLb3k0MHNQREI2a0YxQnh5Qm5aTHk0VGlEaldmOElLcVAxTEttdndJcFBqUHRCd0pxWWdHSkZTRlNLNUpnVWNSRnRMTW5UNngrY0ljNEx0bGdxeHJqTkIzaUtCUC8vRUFDd1JBQUlDQVFRQ0FnSUJBd1VCQUFBQUFBRUNBQU1SQkJJaE1SQVRJa0VnVVNNeU0zRVVRcUhCMGVILzJnQUlBUUlCQVQ4QTAzOXNUVXI4cy91YWE3L1kzNGpud1RNd0dBL2xmWjYxSmk3ckczR1djSkthL2xtRndnNWgxQ2ZYTTA2RTJGcHBXeW1KcXVGekszL2t5WXB5UFBma21Id3BnUGpQblh0aE1UVEU4QXk4L0NVY0FtTVM1eVlFQThhSTlpYWdBcGd3VmdHVkhBOEdEd1RDZkt3UVRIaFpxcS9aRXJDNGxqQURtVkFGZUk5QkhLelA3aVAvQUM0SE0wcEtzQ1plQ3c0Z1E3dVlnZ21JUEIvSlQ0TStvNGpWakVZbDVTdTFSR2JBeVphUmFjejFoZVZoSlY4VGNTTXhXeTNNQTRnR1BCNGd0NXdmd2JPT0pXeEkrWGZnUllUZ1JMQ3hJSXhHV0VaV0J5cmJZT3BxYzdJT29UTDZ3RzNUM3FxNGoyQWppYWJVN2hob0dEZFRNc1lLTW1QWXJFNE0wMSs0YlRCejRiVXFwSU1YVXFjNGlNSEdSQkMyMk5xUnVqM3B5QVlQNllEeEZWV2VBVFVETlppbGhEa3l4ZllBVEhSY2RTMEtEa1FFZ2tpVVgrcnZxQ3dNTXkvYllNUTBKMklEZzhTblVnRGEwM2hoeEFxRHZ1TWxaTXJaNlIreEVZT01pYXBqbmFKU2dzWWhvK2xxSFFsTm9LNC9VVWdyS0Z6Y1Q0dHM5akZCMUF1UHFmTDlSMzlhaGNaaGNIZ3kvQWxha2dqNmlESENELzMvQU9RVnNlelBXeTlHRlJZTUhneXl2MWdxd2xOSnRnUUFiRWlVakVOQ1k2bm85YmIxaUlvNVdhdkhFUUVzQ29semJKdWY2Z05xL2NvdlZTZDR3ZjNCZ2lLdTFpSmc1ejQxTE12OU15Vk9ZU1cveVRBZ1JSV3NTb0FSY2dZalpNYXNTK3ZmV1FleE5PdUsrT3pFVUlKaVl6NDI3VGtUVXFTNC9VcitDYnpFckxuYzBSUjFpTXYxaVcwRDZtbnRLL0F6VW9RQ3l5azVTTzIwUytnV2pudVBwaXA3bWwrVmdCZ0haRUR0OWllNUlMRlAzTW5PTVF6VGtNZ2h6QzFuMHN6Y1J4RkJ3TjNmalUyWXN4SEdCRllEaUZnTTh3Vzg4R0Z1Y1lpajV4dm5YaVVydFdFWmlhamJuN0gvQUZMTlNqSHFVTUErNkw0S2pHTVFWcjMxQWN5MXdpRW1hS3p0VEdZSU1tRmdxN21uQzg1bk9ZN0JWSk1DRyt3d052WE1CR01tUDg4N2VwV0JqR0lDREZIeXpFUHd6RVlNVGp4akN6Y1RtSnFmV05zMGx5MnBrVGJnN29GT2M1Z1VnbkptUW9tcXYzbmFPcFZkc2JkS2JWc1hJbVBCSVh1RzMzV0JQcVVZeVcvY0I5YmtIb3hsTGZmRU81Z1BXY1FxeFlFSGlCY2RTMXdvMnk3VlorS1RUc2M0OEZXUHhoQldWNk1NbVQyWjZXMHpiMGxlcUI0UGNGNEViVUlvN2xtb2F3N1VqYVZ6RjA3Q1UxbEttYjdHSi9xblRzUWF4bU9BUCtaWTFyOFRUVnV0bTVwVlR0RXRyVjBJbFdxd01QRXRSdWpDd2hjdHdrMVEyTUZCNWpManFWWkhpamw4eS9nSC9NMDlvQzdTZXBZVmRTSnVLdG1HMFduNVFKbFN3K3BwbjJQa3ozSkRhbUl0cWs4eTV0N1ppSGEyWjdsbGQ2d1hxUHVXYWdCRGlWcDdId1pROVFYYTY5UVgwcjBJK3RHTUtKOG5PVEdVZ1lNUWpiSGRpZTVwa0pZaWFoTU9SRkJIVURHRUV3UUZnbTBSUjRBakRFMjdoQW0zbWN3S2NUYVJEbUlXUTVXQU56bWMrRU8weDIzSHlUdDZtb1hJTFRTTHVmQjZucFNEVHFPUkxLRVV5eEFPcHRJTTNtYnpONWlEZEhYYU14YlNJTHA3eGpHSUgzQ0JQdUQrUG1lMUREYWs5eVQycCtvYkFlaE1reXoyYnlNOENXTlljZ25pYVNuWU4rZS9OMlFOd2hkaU00bS9jWVVKbnJnck1RSEhFZkpITUZaSW5yaHJpY0RFRnBQQUVhMG5qRTJ6Wk1FUUF3QXphWTZlMXVHSWxxR3ZzNWkycW93SWI0TlJIdTNLUkJTemdORkF6L2licHVFM2lJTi9BaklVd0NZb1NCRW5xUXg2VjJuRVZTeHdzMDZaWWx2cUd0RDlRVkorcHNUOVJnZ2haUkM0TXJUWVRMOHN3RVhURVQwR0RUbVBRd0VSd0VBaVZ0Wm5FZlQyZ3dhVzJIUzJ5bFdyT0dsdVhJVUNMcDJnb0lnUmhDakVHVU1GWWd5ajVzeEVDR2JUTmthZ21OcHpCVHQ3akQ1R0RMWEFIOEQwWXJuRTBYOUpqWWdJaGFXZjNZVC9BQ0wrS0FGek5Kd3pnZmdTQkNZeG4vL0VBQ29SQUFJQ0FnRURCQUVGQUFNQUFBQUFBQUFCQWhFREVpRVFNVUVFRXlBaVVRVVVNa0p4RlNOaC85b0FDQUVEQVFFL0FKL3laakp4OHI1cnBYeG9SaWp1NlJLa3RSZHliSXgyZkJIMDJSK0QxS2NNU1RKOE14OHMwV2xmTktoZGErUHBGY3JNNlZ1aVBja2syWS8rcFd1NSs0bkxnOXlTN21lcjRJT21SbmFKTDRvWHhmd3dUMEp6dGtVUHZ5UXlrSEZMaEdTQ2xpMnJreXEyeEVYd1BuNExvaS9nK3FFNkxJL1VrN1pGTnVrZWgvVHNrK0dmOFZTcVRPNkl4VFk0MHZnbFo3ZEsrcUVTanIxWWxZNFVyUW4waXJqWTFSK25SakxQRlNQVC9YS05jRUhkb2hCdmtqRzJaY09yNEdxRVk0dHNVWEZHVEhYS0tFUnhOazhiWEJLT3ZSS3lPS2tSeHlvZmZwZFE2ZWxsS09WTkhvOEdmZmFmWTdJamFJTjJSYjZUeE9Rb05HSzQ4aXlOcW1lQ2VLK1VLTFFuSWlwMFNpcFAvd0JKSnAwekNpY3RWd1F6VGZjbkJwbFVTZFkxMDlMQ09LUHV5NytDZnJzMHY3cy9jeTh1eUVYTGtTcnNZMDVFcVJPVVlybDhudS9nV1JlVUpwZjRRZDhvbFBWOGpuYnRqbXhaSDRaSDFVbkQyNU8wTnR1bVliNUd1SGJJWElTaWJRZmd5WVhOZlRrcW1TeWJRUy9CZFJycGdWbEprS1ZrcHVUMlk1TWxYZE1qclZ2dVJrekRQV1JrYTJkamJadGFxaE53NEVKMnFNTDRKdTVhSWxMWGlJLzlJOXJzaGtjVE5GVGp1dS9reFUzeVpGVE1jZGpIa2NDR2JZeThRZEYzM0hGVnd5T09UWTROTm1zYXV4UGt6S3B0QzFYY1VZVnl5WHQrQlQxdWhXekRINjJSNVpvNnNVWCtCeGFGSGl5N1F1SkU1Yk1Ra1k4YmlaRmFycFhGa0pOYzJiTmxVNk1jZG5SNm1QOEFZakZ6WWw5cVJibmFvcE9QSGNoRnlsU0hKWTRvclNSVDdJaitHeVRmZXltaDhJa3ZzU2kwcmZSTk9WRUlyVldQQzVjbWZFNFNZcFd0UzFWVVgrQnR5LzA5UGkxVms4VnFpZU9VSFhSSWlteHc5ckc1ZVNkMGtOYnh0ZUNFcTc5eFVyMjVFNDZ0TmNqbGZjeHhjdVRGaHJtUm1pdEgwZ294K3hHYWwyUDNPcnBEeXh6UjFaTEN4NHJJNFpNaGpqRG1SSFBCRHl4Wmt5WGtqSHd6Mm95N0R3UlNzeHFFVFBLTHh0SWxrc2hOeGRrOENmS0hqa2hSaytEVlEvbVlaYnBzVXZCTko5SmNJeEl5UTVzZ3FZa25HalZ3dWhTOEdXTnhOR1FpeVVQS01TMVJOV2hRWk9EbzBaREcyeHkwaVpGTzdpelRJKzdJNEhmSkZLQ0ZKTWZjeHhpdkJrYU1VL3FOcnljQ2xYUk5YWTNhSzZZMG0rUnRRZkFwcVhCckJEYXN0Q1ZGcVM1Sk5DNlBsRVVrdWlkQ1Y5ekU2NE1ycFdLYkZrZEVNalpDWDVOaEswS0pyMGk3ZEdsbnRpeGpWTXZnZjJGQmlpeFFacXhSS1NNZWxja0ZHdURMUGJncnBEdlFvb2NDTXFWR3h0MGlxRTZOeFNIenlhQ2drYkc1ZG14WlpGNkxsV1Fsc1BHMkxFZTBSeFV6M0ZIZ1Z2dWFsTVVXU2VvbmZZcVE5alprWnV4dWx5Wkc2NEU1RnkvSXJJcVFvc1VDY2xJeGNLeDVvc1dTSjdzU09XSlZ0aXlSajNJNXNiUGZnTFBqTXNsTjJqSFViWTg4VDNrT2NSVGpaSk5yZ2s5RXJOMEtadVJ6SkN6STl5eUxIeGpZbVdKaU5VWnU1R3lpaUg4VCtqNkxvbUoxRkdaOEovQkZDUC9aIgogICAgICBpZD0iZiIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiIC8+CiAgPC9kZWZzPgo8L3N2Zz4=", + "description": "Displays fluid pressure data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pressure', label: 'Pressure', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'bar', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'pressure', 'bar', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 15 - 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"bar\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#2B54CE\"},{\"from\":5,\"to\":10,\"color\":\"#3B911C\"},{\"from\":10,\"to\":15,\"color\":\"#F77410\"},{\"from\":15,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 15 - 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"bar\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"bar\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:cHJlc3N1cmVfY2hhcnRfY2FyZF9iYWNrZ3JvdW5kLnBuZw==:cHJlc3N1cmVfY2hhcnRfY2FyZF9iYWNrZ3JvdW5kLnBuZw==;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Pressure\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"compress\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"margin\":\"0px\"}" + }, + "tags": [ + "fluid pressure", + "liquid pressure", + "pump pressure", + "hydraulic", + "pump", + "compressibility", + "compressive stress", + "pipe", + "pipeline" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/fluid_pressure_gauge.json b/application/src/main/data/json/system/widget_types/fluid_pressure_gauge.json new file mode 100644 index 0000000000..7b7a5a4936 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/fluid_pressure_gauge.json @@ -0,0 +1,35 @@ +{ + "fqn": "fluid_pressure_gauge", + "name": "Pressure gauge", + "deprecated": false, + "image": "tb-image:cHJlc3N1cmVfZ2F1Z2Uuc3Zn:cHJlc3N1cmVfZ2F1Z2Uuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5LjIiIGhlaWdodD0iMTU5LjIiIHg9Ii40IiB5PSIuNCIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjEiIHN0cm9rZS13aWR0aD0iLjgiIHJ4PSIzLjYiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNMjcuNCAzM3YtMi40TDI2IDMxLjkgMjUgMzAuN2wzLjMtMy4zIDMuMyAzLjMtMS4yIDEuMi0xLjItMS4zdjIuNWgtMS43Wk0yMiAyNi43VjI1aDEyLjV2MS42SDIyWm0wLTIuNHYtMS41aDEyLjV2MS41SDIyWm02LjItMi4zTDI1IDE4LjZsMS4yLTEuMiAxLjMgMS4zdi0yLjVIMjl2Mi41bDEuMi0xLjMgMS4yIDEuMi0zLjMgMy4zWk00OC4yIDI2LjRINDZWMjVoMi4zYy40IDAgLjcgMCAxLS4yLjIgMCAuNC0uMy41LS41LjItLjIuMi0uNS4yLS44IDAtLjMgMC0uNS0uMi0uOCAwLS4yLS4zLS40LS41LS42bC0xLS4yaC0xLjh2Ny43aC0xLjZ2LTloMy40Yy43IDAgMS4zLjIgMS44LjQuNS4zLjguNiAxIDEgLjMuNS41IDEgLjUgMS41IDAgLjYtLjIgMS0uNCAxLjUtLjMuNC0uNi43LTEuMSAxLS41LjEtMSAuMy0xLjguM1ptNi4yLTJ2NS4zaC0xLjVWMjNoMS40djEuMlptMi0xLjR2MS40YTIuOSAyLjkgMCAwIDAtMS4zIDAgMS4zIDEuMyAwIDAgMC0uOC44di43aC0uNGwuMS0xLjJjMC0uMy4yLS42LjQtMWwuNi0uNWExLjcgMS43IDAgMCAxIDEuMi0uMmguMlptNCA2LjhjLS42IDAtMSAwLTEuNC0uMmEzIDMgMCAwIDEtMS42LTEuN2MtLjItLjQtLjMtLjgtLjMtMS4zdi0uMmMwLS42LjEtMSAuMy0xLjUuMS0uNC4zLS43LjYtMSAuMy0uMy42LS42IDEtLjdhMyAzIDAgMCAxIDEuMS0uM2MuNSAwIDEgLjEgMS4zLjNsLjkuNi41IDEgLjIgMS40di42aC01LjF2LTFoMy42di0uMmwtLjEtLjctLjUtLjYtLjgtLjJjLS4yIDAtLjQgMC0uNi4yLS4yIDAtLjQuMi0uNS40bC0uMy43djJsLjQuNy42LjQuNy4xYTIgMiAwIDAgMCAxLjctLjhsLjguOGEyLjggMi44IDAgMCAxLTEuNCAxbC0xLjIuMlptNy42LTJ2LS4zYTEgMSAwIDAgMC0uNS0uM2wtLjktLjMtMS0uM2EzIDMgMCAwIDEtLjctLjRsLS41LS41LS4yLS44YzAtLjIgMC0uNS4yLS43bC41LS43LjgtLjQgMS0uMmMuNiAwIDEuMS4xIDEuNS4zbDEgLjhjLjEuMy4zLjYuMyAxSDY4YzAtLjEgMC0uMy0uMi0uNWExIDEgMCAwIDAtLjMtLjNsLS43LS4yLS42LjEtLjQuNGEuOC44IDAgMCAwIDAgLjdsLjIuMi40LjIuNi4yYy42IDAgMSAuMiAxLjQuNGwuOC42Yy4yLjMuMy42LjMgMWExLjcgMS43IDAgMCAxLS43IDEuNWwtLjkuNC0xIC4xYy0uNyAwLTEuMi0uMS0xLjYtLjMtLjQtLjItLjgtLjUtMS0uOGEyIDIgMCAwIDEtLjMtMWgxLjRsLjMuNi41LjMuNi4xaC43bC40LS40LjEtLjRabTcgLjN2LTVoMS41djYuNkg3NUw3NSAyOFptLjItMS40aC41YzAgLjUgMCAuOS0uMiAxLjNhMi4xIDIuMSAwIDAgMS0xLjIgMS42bC0xLjIuMi0uOC0uMS0uNy0uNWEyIDIgMCAwIDEtLjUtLjdsLS4xLTEuMnYtNC4yaDEuNHY0LjNsLjEuNi4yLjMuNC4zaC40Yy40IDAgLjggMCAxLS4ybC41LS43LjItMVptNC40LTIuNHY1LjRoLTEuNFYyM2gxLjR2MS4yWm0yLTEuM3YxLjRhMi45IDIuOSAwIDAgMC0xLjIgMCAxLjMgMS4zIDAgMCAwLS44LjhsLS4xLjdoLS4zdi0xLjJsLjQtMSAuNy0uNWExLjcgMS43IDAgMCAxIDEuMS0uMmguM1ptNCA2LjhjLS41IDAtMSAwLTEuMy0uMmEzIDMgMCAwIDEtMS43LTEuN2wtLjItMS4zdi0uMmMwLS42IDAtMSAuMi0xLjVsLjctMWMuMi0uMy42LS42IDEtLjdhMyAzIDAgMCAxIDEuMS0uM2MuNSAwIDEgLjEgMS4zLjNsLjguNmMuMy4zLjUuNy42IDFsLjEgMS40di42aC01di0xaDMuNnYtLjJjMC0uMiAwLS41LS4yLS43IDAtLjItLjItLjQtLjQtLjZsLS44LS4yYy0uMiAwLS41IDAtLjYuMi0uMiAwLS40LjItLjUuNGwtLjMuNy0uMSAxdi4ybC4xLjhjMCAuMy4yLjUuNC43bC41LjQuOC4xYTIgMiAwIDAgMCAxLjctLjhsLjcuOGEyLjggMi44IDAgMCAxLTEuNCAxbC0xLjEuMloiLz48ZyBjbGlwLXBhdGg9InVybCgjYSkiPjxyZWN0IHdpZHRoPSIyNi4yIiBoZWlnaHQ9IjEyLjciIHg9Ijg2LjkiIHk9IjEzMCIgZmlsbD0iI0YzRjNGMyIgcng9IjMuMiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Ik05Ny42IDEzNnYxLjJjMCAuNSAwIDEtLjIgMS4zIDAgLjQtLjIuNy0uNCAxbC0uNy41LTEgLjFoLS43YTEuOCAxLjggMCAwIDEtMS0xbC0uNC0uOFYxMzZsLjEtMS40LjQtLjkuNy0uNSAxLS4yYy4yIDAgLjUgMCAuNy4yYTEuOCAxLjggMCAwIDEgMSAuOGwuNC44djEuMlptLTEuMSAxLjNWMTM1LjJsLS4yLS41YTEgMSAwIDAgMC0uMi0uNGwtLjQtLjJhMSAxIDAgMCAwLS4zIDAgMSAxIDAgMCAwLS41IDBjLS4xLjEtLjMuMi0uMy40bC0uMy41VjEzOC4xYzAgLjIgMCAuMy4yLjUgMCAuMSAwIC4zLjIuNGwuMy4yaC44bC40LS40LjItLjZ2LS45Wm01LjQtNHY2LjdoLTEuMnYtNS40bC0xLjYuNXYtMWwyLjYtLjloLjJabTcgNS44djFoLTQuNXYtLjhsMi4yLTIuNS42LS43LjMtLjVhMS40IDEuNCAwIDAgMCAwLTFsLS40LS40YTEgMSAwIDAgMC0uNi0uMmMtLjIgMC0uNCAwLS42LjJhMSAxIDAgMCAwLS40LjVsLS4xLjZoLTEuMWMwLS40IDAtLjguMi0xIC4yLS40LjUtLjcuOC0uOS40LS4yLjgtLjMgMS4zLS4zLjQgMCAuOC4xIDEuMS4zLjMuMS42LjMuNy42LjIuMy4zLjYuMyAxbC0uMS42YzAgLjMtLjIuNS0uMy43bC0uNC42LS42LjYtMS41IDEuNmgzLjJaIi8+PC9nPjxwYXRoIGZpbGw9IiMzMDVBRDciIGQ9Ik0xNDMgMTMxLjRhNTUuNiA1NS42IDAgMSAwLTg2LjcuMmw3LjQtNmE0Ni4xIDQ2LjEgMCAxIDEgNzItLjFsNy40IDZaIi8+PHBhdGggZmlsbD0iIzNGQTcxQSIgZD0iTTE0My4yIDEzMS4yYTU1LjYgNTUuNiAwIDEgMC05Ny4zLTQ5bDkuMiAyLjVhNDYuMSA0Ni4xIDAgMSAxIDgwLjcgNDAuNmw3LjQgNloiLz48cGF0aCBmaWxsPSIjRjM2OTAwIiBkPSJNMTQzLjIgMTMxLjJhNTUuNiA1NS42IDAgMCAwLTY1LTg1LjlsMy42IDguOGE0Ni4xIDQ2LjEgMCAwIDEgNTQgNzEuMmw3LjQgNloiLz48cGF0aCBmaWxsPSIjRDgxODM4IiBkPSJNMTQzLjEgMTMxLjRhNTUuNiA1NS42IDAgMCAwLTE0LTgybC01IDhhNDYuMiA0Ni4yIDAgMCAxIDExLjYgNjhsNy40IDZaIi8+PHBhdGggZmlsbD0iIzYzNjM2MyIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJtMTM1LjYgMTI1LjYuNC0uNCAzLjcgMi45IDMuNyAyLjktLjQuNS03LjQtNS45Wk0xNTQuOSAxMDMuM2wtNC42LS42LTQuOC0uNXYtLjdsNC44LjUgNC43LjUtLjEuOFpNMTUwIDczbC0uNC0uOC00LjIgMi00LjIgMi4yLjIuNiA0LjMtMiA0LjMtMlpNMTI5LjcgNDkuOGwtMi42IDQtMi42IDQtLjUtLjQgMi41LTQgMi41LTQgLjcuNFpNMTAwLjEgNDUuOFY0MWE1Ni43IDU2LjcgMCAwIDAtLjcgMHY5LjZhNTAuNyA1MC43IDAgMCAxIC43IDB2LTQuOFpNNzMgNTMuNGwtMi41LTRjLS4zIDAtLjUuMi0uNy40bDIuNiA0IDIuNSA0IC42LS40LTIuNS00Wk01NCA3NC4zbC00LjItMi4xLS40LjcgNC4zIDIgNC40IDIgLjItLjUtNC4yLTIuMVpNNDkuMSAxMDJsLTQuNy41di44bDQuOC0uNiA0LjgtLjUtLjEtLjctNC44LjVaTTU5LjcgMTI4LjFsLTMuNyAzIC41LjYgMy43LTMgMy43LTNhNDcgNDcgMCAwIDEtLjQtLjVsLTMuOCAyLjlaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48cGF0aCBmaWxsPSIjNjM2MzYzIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNTUgMTAzYTU1LjYgNTUuNiAwIDAgMS0xMiAyOC41bC0uNi0uNGE1NC44IDU0LjggMCAxIDAtODUuNCAwbC0uNS41YTU1LjYgNTUuNiAwIDEgMSA5OC40LTI4LjZaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii41IiBkPSJNNTYuNyAxMDAuOGguNWwuNi0uMS4zLS4zLjEtLjUtLjEtLjUtLjMtLjNhMSAxIDAgMCAwLS41IDAgMSAxIDAgMCAwLS40IDAgLjguOCAwIDAgMC0uNS43aC0xYzAtLjMuMi0uNS4zLS44bC43LS41YTIgMiAwIDAgMSAxLS4yYy4zIDAgLjYgMCAuOS4yLjIgMCAuNS4zLjYuNS4yLjIuMi41LjIuOXYuNWwtLjQuNC0uNi40LS43LjFoLS43di0uNVptMCAuN3YtLjVoLjdsLjguMWMuMyAwIC41LjIuNi4zLjEuMi4zLjMuMy41bC4xLjYtLjEuNy0uNC41LS42LjNhMi41IDIuNSAwIDAgMS0xLjUgMCAyIDIgMCAwIDEtLjYtLjNsLS40LS41LS4yLS43aDF2LjVjLjIgMCAuMy4yLjQuM2gxbC40LS4zdi0xLjFsLS40LS4zaC0xLjFaTTY4IDEyMi4ydjFsLS4xIDEtLjQuOC0uNi41YTIuMiAyLjIgMCAwIDEtMS40IDAgMS41IDEuNSAwIDAgMS0uOS0uN2wtLjItLjctLjEtMXYtMWwuMS0xIC40LS44LjYtLjQuNy0uMi43LjFhMS41IDEuNSAwIDAgMSAuOS44bC4yLjcuMS45Wm0tMSAxVjEyMWwtLjMtLjMtLjItLjJoLS43bC0uMy4zLS4yLjV2M2wuMy4zLjIuMmguN2wuMy0uMy4yLS41di0uN1pNNjIuNCA3NnYuOGMtLjMgMC0uNiAwLS44LjItLjMuMS0uNS4yLS42LjRsLS40LjctLjEuN3YxLjVsLjMuNS4zLjIuNC4xaC4zbC4zLS4zLjItLjRhMS43IDEuNyAwIDAgMCAwLS45YzAtLjEgMC0uMy0uMi0uNEw2MiA3OWwtLjQtLjFhMSAxIDAgMCAwLS42LjEgMSAxIDAgMCAwLS4zLjRsLS4yLjQtLjMtLjEuMS0uNy40LS41LjUtLjNINjIuNGwuNS41LjMuNmEyLjUgMi41IDAgMCAxIDAgMS41YzAgLjItLjIuNC0uNC42bC0uNS40YTIgMiAwIDAgMS0uOC4yYy0uMyAwLS42IDAtLjgtLjItLjMtLjEtLjUtLjMtLjYtLjVsLS40LS43LS4xLS45di0uM2wuMS0xLjMuNS0xYy4yLS4zLjUtLjUuOS0uN2EzIDMgMCAwIDEgMS4zLS4yWk03NC42IDYzLjZjLjQgMCAuNyAwIDEtLjJhMS40IDEuNCAwIDAgMCAuOS0xdi0yLjNsLS4yLS40LS4zLS4zLS40LS4xaC0uNGMwIC4yLS4yLjItLjMuNGwtLjEuM2ExLjggMS44IDAgMCAwIDAgMWwuMS4zLjMuMy40LjFhMSAxIDAgMCAwIC43LS4zIDEuMSAxLjEgMCAwIDAgLjMtLjdsLjQuMS0uMS42YTEuOCAxLjggMCAwIDEtLjkuOWwtLjYuMS0uNy0uMS0uNS0uNC0uMy0uNmEyLjUgMi41IDAgMCAxIDAtMS41YzAtLjMuMi0uNS40LS43bC41LS40LjgtLjIuOC4yYy4zLjEuNS4zLjYuNWwuNC44djIuMmMwIC4zLS4yLjYtLjMuOGwtLjUuNy0uOC41LTEuMS4xaC0uMXYtLjdaTTEyOS4yIDEyNC44di43aC0zLjh2LS42bDEuOS0yIC40LS42LjMtLjV2LS45bC0uMy0uM2EuOS45IDAgMCAwLS41LS4xIDEgMSAwIDAgMC0uNS4xYy0uMi4xLS4zLjItLjMuNGwtLjIuNmgtLjljMC0uNCAwLS43LjItMWwuNy0uNmEyIDIgMCAwIDEgMS0uM2wxIC4yYy4zLjIuNS4zLjYuNmwuMi44di41bC0uMy42LS40LjUtLjUuNS0xLjIgMS40aDIuNlptNC43LTEuM3YuN2gtNC4xdi0uNWwyLjQtMy45aC43bC0uOCAxLjQtMS40IDIuM2gzLjJabS0uNy0zLjd2NS43aC0xdi01LjdoMVpNMTM5LjYgMTAzLjN2LjdoLTMuOXYtLjZsMS45LTIgLjUtLjYuMi0uNWExLjEgMS4xIDAgMCAwIDAtLjhjMC0uMi0uMi0uMy0uMy0uM2EuOS45IDAgMCAwLS41LS4yIDEgMSAwIDAgMC0uNS4ybC0uMy4zLS4xLjZoLTFsLjMtMSAuNi0uNmEyIDIgMCAwIDEgMS0uMmMuNCAwIC44IDAgMSAuMi4zLjEuNS4zLjYuNS4yLjMuMi41LjIuOXYuNWwtLjMuNS0uNC41LS40LjUtMS4zIDEuNGgyLjdabTMuMy01djUuN2gtMXYtNC41bC0xLjQuNHYtLjdsMi4yLS45aC4yWk0xMzQuMyA3NnY1LjhoLS45di00LjZsLTEuNC41di0uOGwyLjItLjhoLjFabTUuNyA0LjJjMCAuNCAwIC43LS4yIDFsLS43LjUtMSAuMmMtLjMgMC0uNiAwLS45LS4yLS4zLS4xLS41LS4zLS43LS42bC0uMi0uOWExLjUgMS41IDAgMCAxIC41LTFsLjYtLjQuOC0uMWMuMyAwIC42IDAgMSAuMmwuNi41Yy4yLjMuMi41LjIuOFptLTEgMHYtLjVjLS4xLS4xLS4yLS4zLS40LS4zYTEgMSAwIDAgMC0uNC0uMiAxIDEgMCAwIDAtLjUuMmMtLjIgMC0uMy4yLS40LjN2MWwuNC4zLjUuMWguNWMwLS4yLjItLjMuMy0uNHYtLjVabTEtMi42YzAgLjMtLjIuNS0uMy44bC0uNi41YTIgMiAwIDAgMS0xIC4yYy0uMyAwLS42IDAtLjgtLjJsLS43LS41LS4yLS44YzAtLjMgMC0uNi4yLS45bC43LS41LjktLjJjLjMgMCAuNiAwIC45LjJhMS40IDEuNCAwIDAgMSAuOCAxLjRabS0xIDAtLjEtLjQtLjMtLjNhLjguOCAwIDAgMC0uNC0uMWgtLjVsLS4yLjQtLjEuNHYuNWEuNy43IDAgMCAwIC44LjRsLjQtLjEuMy0uM3YtLjVaTTExNy42IDU4LjZ2NS43aC0uOXYtNC42bC0xLjQuNXYtLjhsMi4yLS44aC4xWm0zIDMtLjctLjEuMy0yLjloM3YuOEgxMjFsLS4yIDEuNGExLjggMS44IDAgMCAxIC45LS4ybC43LjEuNi40LjMuNmEyLjUgMi41IDAgMCAxIDAgMS41YzAgLjMtLjIuNS0uMy42bC0uNi40LS45LjItLjYtLjFjLS4zIDAtLjQtLjItLjYtLjNhMS42IDEuNiAwIDAgMS0uNi0xLjJoMXYuNGMuMS4yLjMuMy40LjNsLjQuMWguNWwuMi0uMy4yLS40YTEuOCAxLjggMCAwIDAgMC0uOSAxIDEgMCAwIDAtLjItLjRsLS4zLS4yYTEgMSAwIDAgMC0uNCAwaC0uNmwtLjMuM1pNOTguMiA1M3Y1LjdoLTF2LTQuNWwtMS40LjR2LS44bDIuMy0uOFptNS44IDV2LjdoLTMuOHYtLjZsMS44LTIgLjUtLjYuMi0uNWExLjEgMS4xIDAgMCAwIDAtLjhjMC0uMi0uMi0uMy0uMy0uNGEuOS45IDAgMCAwLS41IDAgMSAxIDAgMCAwLS41IDBsLS4zLjQtLjEuNmgtMWwuMy0xIC42LS42YTIgMiAwIDAgMSAxLS4yYy40IDAgLjggMCAxIC4yLjMuMS41LjMuNi41LjIuMi4yLjUuMi44di42bC0uMy41YTQuNiA0LjYgMCAwIDEtLjggMWwtMS4zIDEuNGgyLjdaTTkzIDExMy4zaDEuMnY2bC0uMSAxLjFoLTF2LTcuMVptNC41IDQuNXYuMWwtLjEgMS0uNC45LS42LjUtLjkuMmMtLjMgMC0uNSAwLS44LS4yLS4yIDAtLjQtLjMtLjYtLjUtLjEtLjItLjMtLjUtLjMtLjhsLS4zLTF2LS4zbC4zLTFjMC0uMy4yLS41LjMtLjdhMS42IDEuNiAwIDAgMSAxLjQtLjdjLjQgMCAuNiAwIC45LjIuMiAwIC40LjMuNi41bC40Ljh2MVptLTEuMS4xdi0uN2wtLjMtLjVhMSAxIDAgMCAwLS4zLS40IDEgMSAwIDAgMC0uNi0uMWgtLjVhMSAxIDAgMCAwLS4zLjRsLS4zLjR2MS4zbC4xLjcuNC40LjcuMi41LS4xLjMtLjQuMi0uNXYtLjdabTUgMS41di0yLjlsLS4zLS4zYTEgMSAwIDAgMC0uNSAwaC0uNWwtLjMuMy0uMS4zaC0xLjFsLjEtLjYuNC0uNS43LS4zLjktLjFjLjMgMCAuNyAwIDEgLjIuMyAwIC41LjMuNy41bC4yIDFWMTE5LjlsLjIuNHYuMWgtMS4xbC0uMi0uNXYtLjVabS4yLTJ2LjZoLS44bC0uNS4xYy0uMiAwLS4zIDAtLjQuMmEuNy43IDAgMCAwLS4zLjZ2LjRsLjQuMi40LjFhMS4yIDEuMiAwIDAgMCAxLS41bC4yLS40LjMuNWMwIC4yIDAgLjMtLjIuNCAwIC4yLS4yLjMtLjMuNWExLjcgMS43IDAgMCAxLTEuMi40IDIgMiAwIDAgMS0xLS4ybC0uNS0uNWMtLjItLjMtLjItLjUtLjItLjhsLjEtLjcuNS0uNWMuMi0uMi40LS4zLjctLjNhNCA0IDAgMCAxIDEtLjJoLjhabTMuNS0xdjRIMTA0di01aDFsLjEgMVptMS42LTF2MWEyLjYgMi42IDAgMCAwLS41IDBoLS41YTEgMSAwIDAgMC0uNi42bC0uMS41aC0uM2wuMS0uOS4zLS43LjUtLjVhMS4zIDEuMyAwIDAgMSAuOS0uMWguMloiLz48cGF0aCBmaWxsPSIjRTY1MTAwIiBkPSJtMTAyLjcgOTguOS0yLjktNTEuNUw5NyA5OC42bDEuMSAxMS42YzAgLjIuMi40LjQuNGgyLjZjLjIgMCAuNC0uMS40LS4zbDEuMy0xMS40WiIvPjxnIGZpbHRlcj0idXJsKCNiKSI+PGNpcmNsZSBjeD0iOTkuNiIgY3k9Ijk4LjIiIHI9IjQuOCIgZmlsbD0iI2ZmZiIvPjwvZz48Y2lyY2xlIGN4PSI5OS42IiBjeT0iOTguMiIgcj0iMy4yIiBmaWxsPSIjZmZmIi8+PGRlZnM+PGNsaXBQYXRoIGlkPSJhIj48cmVjdCB3aWR0aD0iMjYuMiIgaGVpZ2h0PSIxMi43IiB4PSI4Ni45IiB5PSIxMzAiIGZpbGw9IiNmZmYiIHJ4PSIzLjIiLz48L2NsaXBQYXRoPjxmaWx0ZXIgaWQ9ImIiIHdpZHRoPSIzOC4yIiBoZWlnaHQ9IjM4LjIiIHg9IjgwLjYiIHk9Ijc5LjIiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUNvbG9yTWF0cml4IGluPSJTb3VyY2VBbHBoYSIgcmVzdWx0PSJoYXJkQWxwaGEiIHZhbHVlcz0iMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMTI3IDAiLz48ZmVNb3JwaG9sb2d5IGluPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImRpbGF0ZSIgcmFkaXVzPSIxLjYiIHJlc3VsdD0iZWZmZWN0MV9kcm9wU2hhZG93XzE1MDFfNzc0NiIvPjxmZU9mZnNldC8+PGZlR2F1c3NpYW5CbHVyIHN0ZERldmlhdGlvbj0iNi40Ii8+PGZlQ29tcG9zaXRlIGluMj0iaGFyZEFscGhhIiBvcGVyYXRvcj0ib3V0Ii8+PGZlQ29sb3JNYXRyaXggdmFsdWVzPSIwIDAgMCAwIDEgMCAwIDAgMCAwLjI0MTY2NyAwIDAgMCAwIDAuMjQxNjY3IDAgMCAwIDAuMTUgMCIvPjxmZUJsZW5kIGluMj0iQmFja2dyb3VuZEltYWdlRml4IiByZXN1bHQ9ImVmZmVjdDFfZHJvcFNoYWRvd18xNTAxXzc3NDYiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2Ryb3BTaGFkb3dfMTUwMV83NzQ2IiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PC9kZWZzPjwvc3ZnPg==", + "description": "Fluid pressure gauge.", + "descriptor": { + "type": "latest", + "sizeX": 6, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pressure', label: 'Pressure', type: 'timeseries' }];\n }\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-analogue-radial-gauge-widget-settings", + "dataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-radial-gauge-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flow rate\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 15 - 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 24) {\\n\\tvalue = 24;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"startAngle\":45,\"ticksAngle\":270,\"needleCircleSize\":8,\"defaultColor\":\"#e65100\",\"minValue\":0,\"maxValue\":24,\"majorTicksCount\":7,\"colorMajorTicks\":\"#444\",\"minorTicks\":5,\"colorMinorTicks\":\"#666\",\"numbersFont\":{\"family\":\"Roboto\",\"size\":18,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"numbersColor\":\"#616161\",\"showUnitTitle\":false,\"unitTitle\":\"\",\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#888\"},\"titleColor\":\"#888\",\"unitsFont\":{\"size\":26,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"color\":\"#616161\"},\"unitsColor\":\"#616161\",\"valueBox\":true,\"valueInt\":3,\"valueFont\":{\"size\":27,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"color\":\"rgba(0, 0, 0, 0.54)\",\"shadowColor\":\"#FFFFFF01\"},\"valueColor\":\"rgba(0, 0, 0, 0.54)\",\"valueColorShadow\":\"#FFFFFF01\",\"colorValueBoxRect\":\"#88888800\",\"colorValueBoxRectEnd\":\"#66666600\",\"colorValueBoxBackground\":\"rgba(243, 243, 243, 0.54)\",\"colorValueBoxShadow\":\"rgba(0, 0, 0, 0)\",\"showBorder\":false,\"colorPlate\":\"#FFFFFF\",\"colorNeedle\":null,\"colorNeedleEnd\":null,\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"colorNeedleShadowDown\":\"rgba(188,143,143,0.45)\",\"highlightsWidth\":15,\"highlights\":[{\"from\":0,\"to\":5,\"color\":\"#305AD7\"},{\"from\":5,\"to\":10,\"color\":\"#3FA71A\"},{\"from\":10,\"to\":15,\"color\":\"#F36900\"},{\"from\":15,\"to\":24,\"color\":\"#D81838\"}],\"animation\":true,\"animationDuration\":500,\"animationRule\":\"cycle\"},\"title\":\"Pressure\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"decimals\":0,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\",\"units\":\"bar\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":null,\"lineHeight\":\"24px\"},\"showTitleIcon\":true,\"titleTooltip\":\"\",\"titleIcon\":\"compress\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"actions\":{},\"margin\":\"0px\",\"borderRadius\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "fluid pressure", + "liquid pressure", + "pump pressure", + "hydraulic", + "pump", + "compressibility", + "compressive stress", + "pipe", + "pipeline" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/fluid_pressure_progress_bar.json b/application/src/main/data/json/system/widget_types/fluid_pressure_progress_bar.json new file mode 100644 index 0000000000..0e7c38f50f --- /dev/null +++ b/application/src/main/data/json/system/widget_types/fluid_pressure_progress_bar.json @@ -0,0 +1,34 @@ +{ + "fqn": "fluid_pressure_progress_bar", + "name": "Pressure progress bar", + "deprecated": false, + "image": "tb-image:cHJlc3N1cmVfcHJvZ3Jlc3NfYmFyXygxKS5zdmc=:cHJlc3N1cmVfcHJvZ3Jlc3NfYmFyLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxnIGZpbHRlcj0idXJsKCNhKSI+PHJlY3Qgd2lkdGg9IjE4OCIgaGVpZ2h0PSIxNDgiIHg9IjYiIHk9IjYiIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iLjgiIHJ4PSI0Ii8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik0yMC44IDIxLjNoLTIuNnYtMS40aDIuNmMuNCAwIC44IDAgMS0uMi40LS4xLjYtLjMuNy0uNmwuMi0uOWMwLS4zIDAtLjYtLjItLjktLjEtLjItLjMtLjUtLjYtLjZhMiAyIDAgMCAwLTEuMS0uM2gtMlYyNUgxN1YxNWgzLjhjLjggMCAxLjQuMiAyIC41LjUuMiAxIC42IDEuMiAxIC4zLjYuNCAxIC40IDEuNyAwIC42LS4xIDEuMi0uNCAxLjctLjMuNC0uNy44LTEuMiAxLS42LjMtMS4yLjQtMiAuNFptNi45LTIuM3Y2SDI2di03LjRoMS42VjE5Wm0yLjMtMS40VjE5YTMuMiAzLjIgMCAwIDAtMS41IDAgMS40IDEuNCAwIDAgMC0uOCAxbC0uMi42aC0uNGMwLS40IDAtLjguMi0xLjIgMC0uNC4yLS44LjQtMSAuMi0uNC40LS42LjctLjhhMS45IDEuOSAwIDAgMSAxLjMtLjJoLjNabTQuNCA3LjVjLS42IDAtMSAwLTEuNS0uMmEzLjMgMy4zIDAgMCAxLTEuOC0yIDQgNCAwIDAgMS0uMy0xLjN2LS4zYzAtLjYgMC0xLjEuMy0xLjYuMS0uNS40LS45LjctMS4yYTMgMyAwIDAgMSAxLS44bDEuNC0uMmMuNSAwIDEgMCAxLjQuMmwxIC44LjUgMS4xYy4yLjUuMiAxIC4yIDEuNXYuN2gtNS43di0xLjJoNC4xdi0uMWMwLS4zIDAtLjYtLjItLjkgMC0uMi0uMi0uNC0uNS0uNmwtLjgtLjJjLS4zIDAtLjYgMC0uOC4yLS4yIDAtLjQuMi0uNS41LS4xLjItLjMuNC0uMy43bC0uMSAxdjEuM2wuNS43LjYuNS45LjFhMi4zIDIuMyAwIDAgMCAxLjgtLjlsLjkuOS0uNi42LTEgLjVjLS4zLjItLjguMi0xLjIuMlpNNDMgMjNsLS4xLS40YTEgMSAwIDAgMC0uNS0uNGwtMS0uMy0xLS4zLTEtLjUtLjUtLjYtLjEtLjhjMC0uMyAwLS42LjItLjkgMC0uMi4zLS41LjUtLjdsMS0uNSAxLjEtLjFjLjYgMCAxLjIgMCAxLjYuMy40LjIuOC41IDEgLjguMy40LjQuOC40IDEuMmgtMS43bC0uMS0uNWExIDEgMCAwIDAtLjUtLjVsLS43LS4xLS43LjFhMSAxIDAgMCAwLS40LjQuOS45IDAgMCAwIDAgLjhsLjIuMi40LjIuOC4yIDEuNC41Yy41LjIuOC40IDEgLjcuMi4zLjMuNy4zIDEuMWEyIDIgMCAwIDEtLjggMS42IDMgMyAwIDAgMS0xIC41bC0xLjIuMWMtLjYgMC0xLjIgMC0xLjctLjMtLjQtLjMtLjgtLjYtMS0xLS4zLS4zLS40LS43LS40LTEuMWgxLjZjMCAuMy4xLjUuMy43bC41LjQuNy4xLjgtLjFjLjIgMCAuMy0uMi40LS4zLjItLjIuMi0uMy4yLS41Wm03LjUgMGMwLS4yIDAtLjMtLjItLjRhMSAxIDAgMCAwLS40LS40bC0xLS4zLTEuMS0uMy0uOS0uNWMtLjItLjEtLjQtLjMtLjUtLjZsLS4yLS44YzAtLjMgMC0uNi4yLS45bC42LS43LjktLjUgMS4yLS4xYy42IDAgMS4xIDAgMS42LjNsMSAuOC4zIDEuMmgtMS42YzAtLjIgMC0uNC0uMi0uNWExIDEgMCAwIDAtLjQtLjVsLS43LS4xLS43LjFhMSAxIDAgMCAwLS41LjQuOS45IDAgMCAwIDAgLjhsLjIuMi41LjIuNy4yIDEuNS41IDEgLjdjLjIuMy4zLjcuMyAxLjFhMiAyIDAgMCAxLS44IDEuNiAzIDMgMCAwIDEtMSAuNWwtMS4yLjFjLS43IDAtMS4zIDAtMS43LS4zLS41LS4zLS44LS42LTEtMS0uMy0uMy0uNC0uNy0uNC0xLjFoMS42YzAgLjMgMCAuNS4yLjdsLjYuNC43LjEuNy0uMWMuMiAwIC40LS4yLjUtLjNsLjItLjVabTcuNy4zdi01LjdINjBWMjVoLTEuNnYtMS43Wm0uMy0xLjZoLjVjMCAuNSAwIDEtLjIgMS40IDAgLjQtLjIuOC0uNSAxLS4yLjQtLjUuNi0uOC44YTMgMyAwIDAgMS0xLjMuMiAzIDMgMCAwIDEtMS0uMSAyIDIgMCAwIDEtLjgtLjVjLS4yLS4yLS40LS41LS41LS45YTQgNCAwIDAgMS0uMi0xLjJ2LTQuOGgxLjd2NS41bC4zLjQuNC4yLjUuMWMuNSAwIC44IDAgMS0uMy40LS4yLjYtLjQuNy0uN2wuMi0xWm01LTIuN3Y2aC0xLjd2LTcuNGgxLjZWMTlabTIuMi0xLjRWMTlhMy4yIDMuMiAwIDAgMC0xLjQgMCAxLjQgMS40IDAgMCAwLS45IDFsLS4xLjZoLS40YzAtLjQgMC0uOC4yLTEuMiAwLS40LjItLjguNC0xIC4yLS40LjQtLjYuNy0uOGExLjkgMS45IDAgMCAxIDEuMy0uMmguMlptNC40IDcuNWMtLjUgMC0xIDAtMS40LS4yYTMuMyAzLjMgMCAwIDEtMS45LTIgNCA0IDAgMCAxLS4yLTEuM3YtLjNjMC0uNiAwLTEuMS4yLTEuNnMuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS4xLS44bDEuMy0uMmMuNiAwIDEgMCAxLjQuMmwxIC44Yy4zLjMuNS43LjYgMS4xbC4yIDEuNXYuN2gtNS43di0xLjJoNHYtLjFsLS4xLS45LS41LS42LS45LS4yYy0uMiAwLS41IDAtLjcuMi0uMiAwLS40LjItLjUuNWwtLjQuN3YyLjNsLjUuNy42LjUuOC4xYTIuMyAyLjMgMCAwIDAgMS45LS45bC45LjktLjcuNmMtLjIuMi0uNS40LS45LjUtLjQuMi0uOC4yLTEuMy4yWiIvPjxwYXRoIGZpbGw9IiNGNzc0MTAiIGQ9Ik0yOS41IDgwLjNWMTA2aC00LjJWODUuM0wxOSA4Ny41Vjg0bDEwLTMuN2guNVptMjYuMSAyMi4zdjMuNEgzOC41di0yLjlsOC4zLTljMS0xLjEgMS43LTIgMi4yLTIuOC41LS43LjktMS40IDEtMmE1LjIgNS4yIDAgMCAwIDAtMy45Yy0uMy0uNi0uOC0xLTEuMy0xLjQtLjYtLjQtMS4zLS42LTIuMS0uNi0xIDAtMS44LjItMi40LjYtLjcuNC0xLjIgMS0xLjUgMS43LS4zLjctLjUgMS42LS41IDIuNUgzOGMwLTEuNS4zLTIuOSAxLTRhNy41IDcuNSAwIDAgMSAzLTMgOS4yIDkuMiAwIDAgMSA0LjYtMS4xYzEuNyAwIDMuMi4yIDQuNC44IDEuMi42IDIgMS40IDIuNyAyLjVhNy42IDcuNiAwIDAgMSAuNiA2LjFsLTEuMSAyLjNhNDIgNDIgMCAwIDEtMy44IDQuN2wtNS42IDYuMWgxMS44Wk02Mi41IDg4aDIuOHYxNS4zTDY1IDEwNmgtMi41Vjg4Wm0xMSAxMS41di4zYzAgMSAwIDEuOC0uMyAyLjZhNiA2IDAgMCAxLS45IDJjLS40LjYtMSAxLTEuNSAxLjQtLjYuMy0xLjQuNC0yLjIuNC0uNyAwLTEuNC0uMS0yLS40LS42LS4zLTEuMS0uNy0xLjUtMS4zYTcgNyAwIDAgMS0xLTIgMTMgMTMgMCAwIDEtLjUtMi40di0uOGMuMS0xIC4zLTEuOC42LTIuNWE3IDcgMCAwIDEgLjktMiA0IDQgMCAwIDEgMy41LTEuN2MuOCAwIDEuNS4xIDIuMi41LjYuMyAxLjEuNyAxLjUgMS4zYTYgNiAwIDAgMSAxIDJjLjIuOC4zIDEuNy4zIDIuNlptLTIuOC4zdi0uM2wtLjEtMS42LS41LTEuM2MtLjItLjQtLjUtLjctLjgtLjktLjQtLjItLjgtLjMtMS40LS4zLS41IDAtMSAwLTEuMy4ybC0uOS43LS42IDEtLjIgMS4zdjIuMWMwIC42LjIgMS4yLjQgMS43bDEgMS4xYy40LjMgMSAuNSAxLjYuNS42IDAgMS0uMSAxLjQtLjRsLjgtLjguNS0xLjQuMS0xLjZabTEyLjQgMy43di02YzAtLjUgMC0xLS4yLTEuM2wtLjgtLjdjLS4zLS4yLS43LS4zLTEuMi0uM3MtLjkgMC0xLjIuMmEyIDIgMCAwIDAtLjguN2MtLjIuMi0uMy41LS4zLjloLTIuOGMwLS41LjEtMSAuNC0xLjUuMi0uNC41LS45IDEtMS4yLjUtLjQgMS0uNyAxLjctLjkuNi0uMiAxLjMtLjMgMi4xLS4zIDEgMCAxLjguMSAyLjUuNS44LjMgMS40LjggMS44IDEuNC40LjcuNiAxLjUuNiAyLjR2NS43YzAgLjUgMCAxIC4yIDEuNSAwIC41LjIuOS4zIDEuMnYuMmgtMi45YTUgNSAwIDAgMS0uMy0xLjJsLS4xLTEuM1ptLjQtNS4ydjEuN2gtMmMtLjUgMC0xIDAtMS40LjItLjQgMC0uNy4yLTEgLjRhMS44IDEuOCAwIDAgMC0uOCAxLjZsLjMgMSAuNi42IDEuMS4yYTMuMSAzLjEgMCAwIDAgMi42LTEuMmMuMi0uNC40LS43LjQtMWwuOSAxLjJjMCAuMy0uMy43LS41IDFhNSA1IDAgMCAxLS45IDEuMSA0LjQgNC40IDAgMCAxLTMgMS4xYy0uOCAwLTEuNi0uMS0yLjItLjVhNCA0IDAgMCAxLTEuNS0xLjMgNCA0IDAgMCAxLS4yLTMuN2MuMi0uNS42LTEgMS4xLTEuMy41LS40IDEuMS0uNyAxLjgtLjguOC0uMiAxLjYtLjMgMi41LS4zaDIuMlptOC4yLTIuNlYxMDZIODlWOTMuM2gyLjdsLjEgMi40Wm00LTIuNS0uMSAyLjdhNS41IDUuNSAwIDAgMC0yLjQgMCAyLjQgMi40IDAgMCAwLTEuNSAxLjYgNCA0IDAgMCAwLS4zIDEuMmgtLjZjMC0uOCAwLTEuNS4yLTIuMmE2IDYgMCAwIDEgLjctMS44IDMuMiAzLjIgMCAwIDEgMy40LTEuNmwuNS4xWiIvPjxyZWN0IHdpZHRoPSIxNjgiIGhlaWdodD0iNiIgeD0iMTYiIHk9IjEyMiIgZmlsbD0ibm9uZSIgcng9IjMiLz48cmVjdCB3aWR0aD0iNzYiIGhlaWdodD0iNiIgZmlsbD0iI0YzNjkwMCIgcng9IjMiIHRyYW5zZm9ybT0ibWF0cml4KC0xIDAgMCAxIDkyIDEyMikiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii41IiBkPSJNMjEuNiAxMzkuNXYxLjJsLS4yIDEuNi0uNSAxYy0uMi4zLS41LjUtLjguNmwtMSAuMmEzIDMgMCAwIDEtLjgtLjEgMiAyIDAgMCAxLS43LS40bC0uNS0uNi0uNC0xYTcgNyAwIDAgMSAwLTEuM3YtMi44bC42LTFjLjItLjQuNS0uNS44LS43bDEtLjFoLjhhMS45IDEuOSAwIDAgMSAxLjIgMWMuMi4zLjMuNy4zIDFsLjIgMS40Wm0tMSAxLjN2LTEuNWwtLjEtMWEzIDMgMCAwIDAtLjItLjZjMC0uMi0uMi0uNC0uMy0uNWExIDEgMCAwIDAtLjQtLjJsLS41LS4xLS42LjEtLjUuNC0uMy44djMuNmMwIC4yIDAgLjUuMi42IDAgLjIuMS40LjMuNSAwIC4yLjIuMi40LjNoMS4xbC41LS41LjMtLjh2LTFaTTE3Ni44IDE0My4ydi44aC01LjF2LS43bDIuNS0yLjljLjMtLjMuNi0uNi43LS45bC40LS42LjEtLjYtLjEtLjctLjUtLjUtLjctLjJjLS40IDAtLjcgMC0xIC4ybC0uNC42YTIgMiAwIDAgMC0uMi44aC0xYzAtLjUuMS0uOS4zLTEuMi4yLS40LjUtLjcuOS0uOS4zLS4yLjgtLjMgMS40LS4zLjUgMCAuOSAwIDEuMi4yLjQuMi42LjUuOC44LjIuMy4zLjYuMyAxbC0uMS44LS4zLjctLjUuNi0uNi43LTIgMi4zaDMuOVptMi41LTMtLjgtLjEuNC00aDR2MWgtMy4xbC0uMyAyLjEuNi0uMi44LS4xYy4zIDAgLjYgMCAxIC4ybC43LjUuNC44LjIgMS4xYzAgLjQgMCAuNy0uMiAxIDAgLjQtLjIuNi0uNC45YTIgMiAwIDAgMS0uOC41bC0xIC4yYTMgMyAwIDAgMS0xLS4xbC0uNy0uNC0uNi0uNy0uMi0xaDFjMCAuMyAwIC42LjIuOGwuNS40LjcuMi42LS4xLjUtLjQuMy0uNWEyLjQgMi40IDAgMCAwIDAtMS41YzAtLjItLjItLjQtLjMtLjVsLS41LS40LS43LS4xLS43LjEtLjYuNFoiLz48ZGVmcz48ZmlsdGVyIGlkPSJhIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgeD0iMCIgeT0iMCIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMyIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xNTA3XzMzMDA0Ii8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xNTA3XzMzMDA0IiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PC9kZWZzPjwvc3ZnPg==", + "description": "Displays fluid pressure reading as a horizontal progress bar. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pressure', label: 'Pressure', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 15 - 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#305AD7\"},{\"from\":5,\"to\":10,\"color\":\"#3FA71A\"},{\"from\":10,\"to\":15,\"color\":\"#F36900\"},{\"from\":15,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":25,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#305AD7\"},{\"from\":5,\"to\":10,\"color\":\"#3FA71A\"},{\"from\":10,\"to\":15,\"color\":\"#F36900\"},{\"from\":15,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Pressure\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"bar\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "fluid pressure", + "liquid pressure", + "pump pressure", + "hydraulic", + "pump", + "compressibility", + "compressive stress", + "pipe", + "pipeline" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/fluid_pressure_progress_bar_with_background.json b/application/src/main/data/json/system/widget_types/fluid_pressure_progress_bar_with_background.json new file mode 100644 index 0000000000..1e884ea4ee --- /dev/null +++ b/application/src/main/data/json/system/widget_types/fluid_pressure_progress_bar_with_background.json @@ -0,0 +1,34 @@ +{ + "fqn": "fluid_pressure_progress_bar_with_background", + "name": "Pressure progress bar with background", + "deprecated": false, + "image": "tb-image:cHJvZ3Jlc3NfYmFyX3dpdGhfYmFja2dyb3VuZC5zdmc=:cHJvZ3Jlc3NfYmFyX3dpdGhfYmFja2dyb3VuZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48L2c+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZD0iTTIwLjggMjEuM2gtMi42di0xLjRoMi42Yy40IDAgLjggMCAxLS4yLjQtLjEuNi0uMy43LS42bC4yLS45YzAtLjMgMC0uNi0uMi0uOS0uMS0uMi0uMy0uNS0uNi0uNmEyIDIgMCAwIDAtMS4xLS4zaC0yVjI1SDE3VjE1aDMuOGMuOCAwIDEuNC4yIDIgLjUuNS4yIDEgLjYgMS4yIDEgLjMuNi40IDEgLjQgMS43IDAgLjYtLjEgMS4yLS40IDEuNy0uMy40LS43LjgtMS4yIDEtLjYuMy0xLjIuNC0yIC40Wm02LjktMi4zdjZIMjZ2LTcuNGgxLjZWMTlabTIuMy0xLjRWMTlhMy4yIDMuMiAwIDAgMC0xLjUgMCAxLjQgMS40IDAgMCAwLS44IDFsLS4yLjZoLS40YzAtLjQgMC0uOC4yLTEuMiAwLS40LjItLjguNC0xIC4yLS40LjQtLjYuNy0uOGExLjkgMS45IDAgMCAxIDEuMy0uMmguM1ptNC40IDcuNWMtLjYgMC0xIDAtMS41LS4yYTMuMyAzLjMgMCAwIDEtMS44LTIgNCA0IDAgMCAxLS4zLTEuM3YtLjNjMC0uNiAwLTEuMS4zLTEuNi4xLS41LjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEtLjhsMS40LS4yYy41IDAgMSAwIDEuNC4ybDEgLjguNSAxLjFjLjIuNS4yIDEgLjIgMS41di43aC01Ljd2LTEuMmg0LjF2LS4xYzAtLjMgMC0uNi0uMi0uOSAwLS4yLS4yLS40LS41LS42bC0uOC0uMmMtLjMgMC0uNiAwLS44LjItLjIgMC0uNC4yLS41LjUtLjEuMi0uMy40LS4zLjdsLS4xIDF2MS4zbC41LjcuNi41LjkuMWEyLjMgMi4zIDAgMCAwIDEuOC0uOWwuOS45LS42LjYtMSAuNWMtLjMuMi0uOC4yLTEuMi4yWk00MyAyM2wtLjEtLjRhMSAxIDAgMCAwLS41LS40bC0xLS4zLTEtLjMtMS0uNS0uNS0uNi0uMS0uOGMwLS4zIDAtLjYuMi0uOSAwLS4yLjMtLjUuNS0uN2wxLS41IDEuMS0uMWMuNiAwIDEuMiAwIDEuNi4zLjQuMi44LjUgMSAuOC4zLjQuNC44LjQgMS4yaC0xLjdsLS4xLS41YTEgMSAwIDAgMC0uNS0uNWwtLjctLjEtLjcuMWExIDEgMCAwIDAtLjQuNC45LjkgMCAwIDAgMCAuOGwuMi4yLjQuMi44LjIgMS40LjVjLjUuMi44LjQgMSAuNy4yLjMuMy43LjMgMS4xYTIgMiAwIDAgMS0uOCAxLjYgMyAzIDAgMCAxLTEgLjVsLTEuMi4xYy0uNiAwLTEuMiAwLTEuNy0uMy0uNC0uMy0uOC0uNi0xLTEtLjMtLjMtLjQtLjctLjQtMS4xaDEuNmMwIC4zLjEuNS4zLjdsLjUuNC43LjEuOC0uMWMuMiAwIC4zLS4yLjQtLjMuMi0uMi4yLS4zLjItLjVabTcuNSAwYzAtLjIgMC0uMy0uMi0uNGExIDEgMCAwIDAtLjQtLjRsLTEtLjMtMS4xLS4zLS45LS41Yy0uMi0uMS0uNC0uMy0uNS0uNmwtLjItLjhjMC0uMyAwLS42LjItLjlsLjYtLjcuOS0uNSAxLjItLjFjLjYgMCAxLjEgMCAxLjYuM2wxIC44LjMgMS4yaC0xLjZjMC0uMiAwLS40LS4yLS41YTEgMSAwIDAgMC0uNC0uNWwtLjctLjEtLjcuMWExIDEgMCAwIDAtLjUuNC45LjkgMCAwIDAgMCAuOGwuMi4yLjUuMi43LjIgMS41LjUgMSAuN2MuMi4zLjMuNy4zIDEuMWEyIDIgMCAwIDEtLjggMS42IDMgMyAwIDAgMS0xIC41bC0xLjIuMWMtLjcgMC0xLjMgMC0xLjctLjMtLjUtLjMtLjgtLjYtMS0xLS4zLS4zLS40LS43LS40LTEuMWgxLjZjMCAuMyAwIC41LjIuN2wuNi40LjcuMS43LS4xYy4yIDAgLjQtLjIuNS0uM2wuMi0uNVptNy43LjN2LTUuN0g2MFYyNWgtMS42di0xLjdabS4zLTEuNmguNWMwIC41IDAgMS0uMiAxLjQgMCAuNC0uMi44LS41IDEtLjIuNC0uNS42LS44LjhhMyAzIDAgMCAxLTEuMy4yIDMgMyAwIDAgMS0xLS4xIDIgMiAwIDAgMS0uOC0uNWMtLjItLjItLjQtLjUtLjUtLjlhNCA0IDAgMCAxLS4yLTEuMnYtNC44aDEuN3Y1LjVsLjMuNC40LjIuNS4xYy41IDAgLjggMCAxLS4zLjQtLjIuNi0uNC43LS43bC4yLTFabTUtMi43djZoLTEuN3YtNy40aDEuNlYxOVptMi4yLTEuNFYxOWEzLjIgMy4yIDAgMCAwLTEuNCAwIDEuNCAxLjQgMCAwIDAtLjkgMWwtLjEuNmgtLjRjMC0uNCAwLS44LjItMS4yIDAtLjQuMi0uOC40LTEgLjItLjQuNC0uNi43LS44YTEuOSAxLjkgMCAwIDEgMS4zLS4yaC4yWm00LjQgNy41Yy0uNSAwLTEgMC0xLjQtLjJhMy4zIDMuMyAwIDAgMS0xLjktMiA0IDQgMCAwIDEtLjItMS4zdi0uM2MwLS42IDAtMS4xLjItMS42cy40LS45LjctMS4yYTMgMyAwIDAgMSAxLjEtLjhsMS4zLS4yYy42IDAgMSAwIDEuNC4ybDEgLjhjLjMuMy41LjcuNiAxLjFsLjIgMS41di43aC01Ljd2LTEuMmg0di0uMWwtLjEtLjktLjUtLjYtLjktLjJjLS4yIDAtLjUgMC0uNy4yLS4yIDAtLjQuMi0uNS41bC0uNC43djIuM2wuNS43LjYuNS44LjFhMi4zIDIuMyAwIDAgMCAxLjktLjlsLjkuOS0uNy42Yy0uMi4yLS41LjQtLjkuNS0uNC4yLS44LjItMS4zLjJaIi8+PHBhdGggZmlsbD0iI0Y3NzQxMCIgZD0iTTI5LjUgODAuM1YxMDZoLTQuMlY4NS4zTDE5IDg3LjVWODRsMTAtMy43aC41Wm0yNi4xIDIyLjN2My40SDM4LjV2LTIuOWw4LjMtOWMxLTEuMSAxLjctMiAyLjItMi44LjUtLjcuOS0xLjQgMS0yYTUuMiA1LjIgMCAwIDAgMC0zLjljLS4zLS42LS44LTEtMS4zLTEuNC0uNi0uNC0xLjMtLjYtMi4xLS42LTEgMC0xLjguMi0yLjQuNi0uNy40LTEuMiAxLTEuNSAxLjctLjMuNy0uNSAxLjYtLjUgMi41SDM4YzAtMS41LjMtMi45IDEtNGE3LjUgNy41IDAgMCAxIDMtMyA5LjIgOS4yIDAgMCAxIDQuNi0xLjFjMS43IDAgMy4yLjIgNC40LjggMS4yLjYgMiAxLjQgMi43IDIuNWE3LjYgNy42IDAgMCAxIC42IDYuMWwtMS4xIDIuM2E0MiA0MiAwIDAgMS0zLjggNC43bC01LjYgNi4xaDExLjhaTTYyLjUgODhoMi44djE1LjNMNjUgMTA2aC0yLjVWODhabTExIDExLjV2LjNjMCAxIDAgMS44LS4zIDIuNmE2IDYgMCAwIDEtLjkgMmMtLjQuNi0xIDEtMS41IDEuNC0uNi4zLTEuNC40LTIuMi40LS43IDAtMS40LS4xLTItLjQtLjYtLjMtMS4xLS43LTEuNS0xLjNhNyA3IDAgMCAxLTEtMiAxMyAxMyAwIDAgMS0uNS0yLjR2LS44Yy4xLTEgLjMtMS44LjYtMi41YTcgNyAwIDAgMSAuOS0yIDQgNCAwIDAgMSAzLjUtMS43Yy44IDAgMS41LjEgMi4yLjUuNi4zIDEuMS43IDEuNSAxLjNhNiA2IDAgMCAxIDEgMmMuMi44LjMgMS43LjMgMi42Wm0tMi44LjN2LS4zbC0uMS0xLjYtLjUtMS4zYy0uMi0uNC0uNS0uNy0uOC0uOS0uNC0uMi0uOC0uMy0xLjQtLjMtLjUgMC0xIDAtMS4zLjJsLS45LjctLjYgMS0uMiAxLjN2Mi4xYzAgLjYuMiAxLjIuNCAxLjdsMSAxLjFjLjQuMyAxIC41IDEuNi41LjYgMCAxLS4xIDEuNC0uNGwuOC0uOC41LTEuNC4xLTEuNlptMTIuNCAzLjd2LTZjMC0uNSAwLTEtLjItMS4zbC0uOC0uN2MtLjMtLjItLjctLjMtMS4yLS4zcy0uOSAwLTEuMi4yYTIgMiAwIDAgMC0uOC43Yy0uMi4yLS4zLjUtLjMuOWgtMi44YzAtLjUuMS0xIC40LTEuNS4yLS40LjUtLjkgMS0xLjIuNS0uNCAxLS43IDEuNy0uOS42LS4yIDEuMy0uMyAyLjEtLjMgMSAwIDEuOC4xIDIuNS41LjguMyAxLjQuOCAxLjggMS40LjQuNy42IDEuNS42IDIuNHY1LjdjMCAuNSAwIDEgLjIgMS41IDAgLjUuMi45LjMgMS4ydi4yaC0yLjlhNSA1IDAgMCAxLS4zLTEuMmwtLjEtMS4zWm0uNC01LjJ2MS43aC0yYy0uNSAwLTEgMC0xLjQuMi0uNCAwLS43LjItMSAuNGExLjggMS44IDAgMCAwLS44IDEuNmwuMyAxIC42LjYgMS4xLjJhMy4xIDMuMSAwIDAgMCAyLjYtMS4yYy4yLS40LjQtLjcuNC0xbC45IDEuMmMwIC4zLS4zLjctLjUgMWE1IDUgMCAwIDEtLjkgMS4xIDQuNCA0LjQgMCAwIDEtMyAxLjFjLS44IDAtMS42LS4xLTIuMi0uNWE0IDQgMCAwIDEtMS41LTEuMyA0IDQgMCAwIDEtLjItMy43Yy4yLS41LjYtMSAxLjEtMS4zLjUtLjQgMS4xLS43IDEuOC0uOC44LS4yIDEuNi0uMyAyLjUtLjNoMi4yWm04LjItMi42VjEwNkg4OVY5My4zaDIuN2wuMSAyLjRabTQtMi41LS4xIDIuN2E1LjUgNS41IDAgMCAwLTIuNCAwIDIuNCAyLjQgMCAwIDAtMS41IDEuNiA0IDQgMCAwIDAtLjMgMS4yaC0uNmMwLS44IDAtMS41LjItMi4yYTYgNiAwIDAgMSAuNy0xLjggMy4yIDMuMiAwIDAgMSAzLjQtMS42bC41LjFaIi8+PHJlY3Qgd2lkdGg9IjE2OCIgaGVpZ2h0PSI2IiB4PSIxNiIgeT0iMTIyIiBmaWxsPSJub25lIiByeD0iMyIvPjxyZWN0IHdpZHRoPSI3NiIgaGVpZ2h0PSI2IiBmaWxsPSIjRjM2OTAwIiByeD0iMyIgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgOTIgMTIyKSIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Ik0yMS42IDEzOS41djEuMmwtLjIgMS42LS41IDFjLS4yLjMtLjUuNS0uOC42bC0xIC4yYTMgMyAwIDAgMS0uOC0uMSAyIDIgMCAwIDEtLjctLjRsLS41LS42LS40LTFhNyA3IDAgMCAxIDAtMS4zdi0yLjhsLjYtMWMuMi0uNC41LS41LjgtLjdsMS0uMWguOGExLjkgMS45IDAgMCAxIDEuMiAxYy4yLjMuMy43LjMgMWwuMiAxLjRabS0xIDEuM3YtMS41bC0uMS0xYTMgMyAwIDAgMC0uMi0uNmMwLS4yLS4yLS40LS4zLS41YTEgMSAwIDAgMC0uNC0uMmwtLjUtLjEtLjYuMS0uNS40LS4zLjh2My42YzAgLjIgMCAuNS4yLjYgMCAuMi4xLjQuMy41IDAgLjIuMi4yLjQuM2gxLjFsLjUtLjUuMy0uOHYtMVpNMTc2LjggMTQzLjJ2LjhoLTUuMXYtLjdsMi41LTIuOWMuMy0uMy42LS42LjctLjlsLjQtLjYuMS0uNi0uMS0uNy0uNS0uNS0uNy0uMmMtLjQgMC0uNyAwLTEgLjJsLS40LjZhMiAyIDAgMCAwLS4yLjhoLTFjMC0uNS4xLS45LjMtMS4yLjItLjQuNS0uNy45LS45LjMtLjIuOC0uMyAxLjQtLjMuNSAwIC45IDAgMS4yLjIuNC4yLjYuNS44LjguMi4zLjMuNi4zIDFsLS4xLjgtLjMuNy0uNS42LS42LjctMiAyLjNoMy45Wm0yLjUtMy0uOC0uMS40LTRoNHYxaC0zLjFsLS4zIDIuMS42LS4yLjgtLjFjLjMgMCAuNiAwIDEgLjJsLjcuNS40LjguMiAxLjFjMCAuNCAwIC43LS4yIDEgMCAuNC0uMi42LS40LjlhMiAyIDAgMCAxLS44LjVsLTEgLjJhMyAzIDAgMCAxLTEtLjFsLS43LS40LS42LS43LS4yLTFoMWMwIC4zIDAgLjYuMi44bC41LjQuNy4yLjYtLjEuNS0uNC4zLS41YTIuNCAyLjQgMCAwIDAgMC0xLjVjMC0uMi0uMi0uNC0uMy0uNWwtLjUtLjQtLjctLjEtLjcuMS0uNi40WiIvPjxkZWZzPjxwYXR0ZXJuIGlkPSJhIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBwYXR0ZXJuQ29udGVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCI+PHVzZSB4bGluazpocmVmPSIjYyIgdHJhbnNmb3JtPSJtYXRyaXgoLjAwNTAzMTQ1IDAgMCAuMDA2Mjg5MzEgMCAwKSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgeD0iMCIgeT0iMCIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMyIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xNTA3XzMxODE2Ii8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xNTA3XzMxODE2IiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PGltYWdlIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3QkRBQU1DQWdJQ0FnTUNBZ0lEQXdNREJBWUVCQVFFQkFnR0JnVUdDUWdLQ2drSUNRa0tEQThNQ2dzT0N3a0pEUkVORGc4UUVCRVFDZ3dTRXhJUUV3OFFFQkQvMndCREFRTURBd1FEQkFnRUJBZ1FDd2tMRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCRC93Z0FSQ0FDZkFNY0RBUkVBQWhFQkF4RUIvOFFBSFFBQUFnTUJBUUVCQVFBQUFBQUFBQUFBQmdjRUJRZ0RBZ0VKQVAvRUFCc0JBQUlEQVFFQkFBQUFBQUFBQUFBQUFBSURBQUVFQlFZSC85b0FEQU1CQUFJUUF4QUFBQUI3Nmd6Wm5KakZOTjdGOTVPMG5xVHhKV3lVVlhRTExuSVFzQWh1VzBucVRuWTlLdGVvYmhiblAyUDBjNlZ5TWZPMEZlbHd2bTFNclh6YVNSSjg3WnQzczRTTzVJazlWSWx5amtvd0llV1VlaXVEQWxZSkpkVHBQc25PUkQ4L1hsYk1Xb04rWUNRZU9oMG9TR1N4ZjZXTnltcjFKem5iTkU5UEt5R2pLcWYwa0M0TzFCeGJJSVg3azYzTFV4SWpDL2tsakloUkNZdEsrRlpxeUViYVFTV1lnejdOS0xOclA1ek4xcERzajNIc1dmdUMvcVJxbE5JTkNWWU4vd0JMOVhmcVRwQnRqRy9Lcm1wVVNMRkxLRWxMYkRwZk85TnBkWWQ1WGZEa2RkMzYvSHZMWUErZzJwdVJKdUVrbmdZREliM2FNS3E4eStnbkZDN0ZvZmJxMU1aOG9aUTMyMEs0bDUzNUczVlhWeUtiUHZ4cGk5ZlZqMU5tN3ZtRTFMbDltYzJOeUdWclIxdW9pbUtqbmJPcjBzSFlqelVDY2p4eExHZnV5MmJCNFN3REZvbmdaLzA4VWVBbU9mcDBEdlF1a2FNVjhuMllwZlkxTjFQbHpoak01NGRDelN4ZzY4K3NkeVJORFYvaDBJMUpPamRtZEd0Szl6dHBFUHNqQm9iTTZReU5BYzduSTBUWGJtdm5xUWZPZnFmcEl3dnoramtJR2YwVitwZXJIWjBTZ3c2VlJWbXJLdFdvSVN0Z2xNVFlYTWVXOGQyZU94VmdWY1VhV1M1S0l5dFQ2V2FiMUpTdVpoSURicDZ0SDlMSitSM08xSkFsekNIOWdvV2IrYnNzd1k5ZXh6WjdsRVozOGtxQnF3UzRRQ3pqV2dodWM2azZ4RXdZbXNHaHNQQWZHN3BnK0pCTkpsSm1sb2pBNldmTG42NTZsWjQ1MnNPazFOMU1QSzY3aVU0eHU3ditxVnFtUVdqeUVSUlpuamd0YnBDOHpZOTlxSzJYZEZXY2trOEdVS3FzaWN2OGhjVFFab2ZxWGFwS05hOEE5aDlmbjNWd2JBd2dhYjdSRDF0VjJKN24zNXJXVkVvdXQxOXFZaDVHL1cvUXkzelJSbWRyTU1TcHF3OVpUcm41YlpUenF3TmY0YUxFOUF4dTlYZGJuMkdsSkJkY3F2akx5eHlkNERtZHR2czg2ODBLbVNxNFN5NWgwM0pVd0xxMU1iaTdsRlEvVjFhaVVPUitGMW1tTjJMZkhQMFVpZEdtOXF4MGJOZHVSbzZVMTZtS2pIb0E4V21LSkFXekc2b2YwcTlMSklqTmM5SExlR0Nkd2FudHR6MkpWOEVzcjgvVGtmTzhWWUNZMzRmMWRVenhtMFdHek9Vc0RJUEwyWGpsM3EyUTdtbCtqa2dBVkFwaWp5T3FnSi85REpabFpZOUl3TjU5NU85czY4NWpwU01oZUt1ZnNpWEExcXlGVE5xN2NnNW5jc0ZXU21ITVRCUU44T0JIcE5tTlY5cTR3c0NBWjdxR3hYSk5IT1ZRVmQxWlVLN3JTR3FxeFVxcXJvd0M0MHZmZmI1Nml4UEVRbWxlam5qZ1FnaGxPbGlLenM5UzQ0M0xrSzduTzZwcUs4S3JTNldneHJuUkt4ZDJjZ0FYS1NxV1Fpc2xzc3RUZFRFb2NiejdibjBic1J6a0FrbXJNT2xOb1pPRXAxRi9Tb1pqYlhJYWo3U2VhdFZGVDVNZVYwUG9aRWtES25TNy9wVXU2MUgwOGFDeU8wcnZ6KzVJWUZCRWtIbGJWZ1RhT3ZORjlrVWdTOE1YdnJWUE1aMTFSQmVhTUdsNzZrc3pTdUVNaFVWTXN4bEJqU1RhdlJ5cDNNeHptVWtxK3lReElZdFMvd0F6cmxUclc3Z0RZMHhCRzBXTzZydGdUeXJ0Sm5MRXhxNkFhT2xYeTVXRFkwaHd1a3FVWTFlampRMkRSc0RxNVBkMTlrcTZ2Ti9MMXlIcWJ1cGRXQmU1RlRpZDJvbmQxTWRoS2xTNUVySlBKMnR6VXB3N00vR1NvbDE5UVlTd1VTZi94QUFzRUFBQkJBSUJCQUlCQXdVQkFRQUFBQUFFQVFJREJRQUdFUWNTRXhRVklTSVdJekVRSkNVeVFUTkQvOW9BQ0FFQkFBRUlBZENjeVNsaVluVzJ1bkVuSHU0T21XN050UVk0WHh5ZVJ2T2NZMW1kdVBSTWxWRXlaMlN5cW1SVHFxNFBOa01uT054Vi9vbWJaYi9HQXpUNUZleDdidHNFQk5zMk1laFdMTlcxZUFPMjk2Q3dQRHJZZktaYWRUZFJybzE4bWs3WlU3TmFrdUQ2Wm10bHJtc1RyQkVrMVBKRG1pak5xQ0dOU3NJU1NGT0c0bjlKTW1YSm4vemt2M2plVVhJSkZ3ZVhJSDhwbkNaeGlyMnB6blVHZHJ3WkljMWFuOVBiQnlzM2FkZmduTmJvL3N4UUVGVDlUK294MjBueUJ4UHVEcG0rUE5XSTJSdGdud1BUZWFOQzNSUmRRMlJTd01ZNnNBYkJNMU1vNWUxaU55SmU1T2NSTVZlTWtkaEQvd0NjbGRuYnpuWmpmcklKT01nbHlKL0tmMG5kMnNYTnRHOXZJeFloakJsYmRtd0pGRTJiWGhSWklGV0xjdWg5VFp6U20wOXhRVDBWdk5TcjBYRWhRK1dhZlNHbDFKc1VzMjZNbk9TQklRQTVveVdwTUJFc2FJcUJUY3B4bktjWTljbmNxWlBKaXB6bkhHY1lqY2I5WkJKa00yTWsrc05rL0ZVU3lqNytlNDZtYjRHbEliN0ZxeEVoMWNaUjY1akhIelJEUXZubDMzYWxJbVpZVXRCYVc4ZkpXSDJzb213L0c0S0pJYUw3ejBZclRrV2VDRDluNkdjNk4yUno4cGs1RFltZHkvTXl1SWRHUTl2ZW5jMVdxM0V4MEpNMGJtaUF0SlpDclQrM0dLcUxrTCtNZVoyTStnenpUQ1pJemp4a2RBcTRRSE0rcmQyMXBaSWhqeG4xcWNCbzVlcWM1TUdwbFNpbnErU2hiR3RPNWVYUjV0TkdHMjJVNmF2Mm11SHAyd3kzMnpnU0l4d3VxN1pDY0t6SXBHVDkzWTE2dFhMQStBV0pzcEZqYkF6dWY0ZFkyRmhrWGdjaW85TzdITlJFNXh1NVZnQnNvWkUrOFZMbm96Szh4cG83Wms0eVVwc0RlVmwyb1AzVzVZYkJWTEZNaVNOOGxZeDZRRktsYTVxZ2poa0gvZ096c0dhbWJ5SEFiclJzQkZnVFRFMWZwaEJxR0c3dWtxeW5iWHJsTmJGMk5hSkZFeU1icUpWQWtBOHNvN3lUWHF2MG5kUCtwc29KRXdGN0RjQkVRdG56WUhWMW8yTWFZL1hLSTJCM01FN3RYWWU0YlQrb01Kd2tjQm52aWxDcTZDQ0tzSGU2WjByZGVPblh6Z3ozZEJIeVFHYkNiRWtrUFViWTNWVW9zTEtHcnFOeHUzT1BzOVExNThDaU1yYm9INDVLOUI1eFgxNnF1dVFQbjJ1ZVhGVHNoVE90WFVlWVQvQVZUQW9TNG1xUWxYUk0vd0RWQjR0WW9hZWppbXNFZTVvcFcxQlFUdVJycCtsM3lkakFXRkcrQ21ra3JOREVyOXBlN3pHeEpmQU9SdzBUd2RyRmZYRWJMcG16NnpzTWZ2YWhvZHBlV1RseXk4Y01hNjFyb1dqQVdiUEpKTm9GWUZBNG5JV1dkRE8yY2V1ckt5VWRiYWw2cnRpU3NSR2RQZE4yT3ZzaGJjbmJKeWExR3d3L0g3VlBJa3FOTzNFU0xzWnJleDFiTFRzdUNHOThTdFRxL1h4VjIxbHh0VlhJSStDUU9xOGljeWJ0S2NHVS93QkJzeHRXVzYzdUt1YVd3aVR1dDY5dFJVQzZ0V1VPcWlRUU1qV2tyeVkyTUZNc0s0d2lkd1VOclJOSFZKeDdjT0xadFhtaEkwZXBMb05FWklsRFN3d01hOUlhandIUE53dXJBdVpFSWVjSENSR3FPcDBtQXNwSU0zT21tSjIwUlNLZUphMnFtdVNRS2lhMG5lU1pTQnh1L3NpYlVHYVluNCtDODF0clAzSTlhdUhTQ3VETTZoOUxLU3pQZHRNdXpqTkV0Q1k0d1dlZHJXcHVkQkJiSzEwaEhUY1YwN3ZhNmMwSUxPcWJXUTIwWmNkaVhLRkJ0TjBMTXNWaUIxQTF1N21TdkNadnVvUXp4Vnp5eTdCNU1naVVqWGVLZHI2bWRDOVFFbVk0bTFScmZUTjJEZXZjN0syNk42bUd3ZVBXcWFsTlBvZ0YyYjAwZ3NvNHdPb0Y4d1BhaGhzdmtTTUVlRm9WdkJGTXl2d20ycm1leEE0L2ZOb2IxQ0dwcXl6c0NHa3FDMm1ZckxkR1Bzb2Zhb0h3djNXekprdTU0cEdteTh0UktiY2lxNGc2dWxzK29GTjdVbmwxNng3K28wRzBWdGpCNjlvMmRQYWdiUEdJdGxUZ2VMMWgzOU05UG5QRXZGOWlKNC9uZ1BLWlNVcFZzUjBZdkdXdE9aUWtFV1RLZ0dWeFJSa0VGUzR5d2I2Vlg0SlpQYk5oTDdTdk5DREZQY0ZBZ3U2aTdCWVdPQ21wZmEwd3VQM2g0Zy9iSXVCMzdRVk9SVFV0Y0V5Tlk1MmtEem81SWE5c2Z5enk1Ukg5MVpGSzdyYkVJL2I1eWcyOC93RElQT1RaaXFLNjNzcVlHdkhucTl4SXI2LzFBTlB2QTlwMTRlSjBJVXRjZkliSzJNaERuRnVTQ2J1SjlvVUZzTWNuZjFYM1AzNWZoUU5GTS9UaXRsZFdYMVhzb2JERWdSRmE1a2psVGpsWnBoV081bDZ2N2pKTlFFeERkTHF5T3FvUjB5cnM0YUxaaUt3cll0ZW5OWkhEam5Feml3L0RUc09mWWp6UXhnZXRQL2pycWNjT0JRMjdWMUJiRzFSSzNjZVpYeXp5eDl2UEdVOUlPbG5JUGxuUkdrU0V2TjFmVjJSNjJINU5yV3cwNFQ1K28wbnF0U2JURDRaWmJVWCtXUzdYVGh4dWtJdGQrL1VSYTAydkxxcEgzSTV0RE94ZUhFRjdGck81Z1IxQjNVU3pwbmVJMm42c3V0WjRneGIyeTJpMGpkQ3phTlUyTzByYzF5bG1GcmhvWmRqb0JiQ21tam0xSHE0RzFqUUxjVFk5Y3NVL3QzU2dJbmRpTXNEL0FNS25xZGJEMEpYeG80Vm04cWFScEYxSEErVjZZcUtqMVRORm1RL1lKa3pjbGpBckRWWHA1dE1oR3JpKzlzUklONVVFVjhsZXdyVnBrWU52TzRGV2xXMXRScmVyeG42TVFYWTZ0TkJxbTRSQ3MrZkEvd0N6WGxlcUwycFkxSkpMWjVyQ1pMdmJqVW5KampvQ2FLY0Q1OEpGKzYvWXdPNzdYYXdXTnpaZDZqcnFBMHNmUjlYQnM1S3llZW1zdGRIY1JYbndiYlJndmRPTGY5V093VjZRM0d4ZTZWTVlkcVBxM0ZtOUYyS3FLQ3VKc3FhT0ZJdTl1ajExb0hzdG8zT29ZRS9rSVNHckpMaUNpV0dBMDZSUHlmRTZkVmM0N21DVlNvS2dteGgxT01CRm1MdGR1N3BHaU9jM2xZZ2tWZnpLRWV6OG94S280dTVNZkRjMGhZczFmTE00TXAzOE1yaUdNNWN0Y1gvdGw0RVRMVUVzelNiWFl4NFE1Y25FdkRUeWlTWks2MmpYdFdhbXNIcHkrNzAwd2dSWHc2cHJkdFdzZk04a1krWjM3bmh0NFdjUlNySkI1SGk3N0ZJK2xPSlRRRmpzdGlrQWEzV0FWeE5SQmplcjIyV3VWNEpET1RoNEVqWXdlK0gvQUV5ZEZiVnJ0MUg3RTcwM1FkTVp1YkNmN2VHbkRnTWttSmsyZXRpaUFZWXdIY3BSWS9FYXpjdzVQOWsyMnVjempMdmNGc1NYNjJOVmErMnVDaUhqaTdhMUh6S2xwUlArMUl0NkJyKzFYWG11SitLeTdCcmplVVN4MldpaVQ5bzNiUnYvQUpsUlhUclNlRmR3K2JuRExaSjByMUtFSUVmWmxSRVJNVjJiVDdRNGo3TWNheE9zQUlyQjJ4RldWMFNHQkRGcWJXanNiT3pWUk1kcWNISDdPdGZObFNISm0wUTNhVlUwMkNhczN3bzhqOU1qWXV0am9tWDFFWFRXTGRoaEN2RGpFalRDcnl4bG1kVjU4SkVpY0xKcjBMOHNkWlZzdjRPMXhybzhoMXlCZnh4MnRRSi9KMEMyeHlvUHQ5UFBVeHgrZXZQQnJRb0s0R2E5a1QvVnQ5TC9BTnNMVDNBNXhwZzlXM1V1dEhsaTFNSzJiZW53M25rYnp4bmxZbWV6RTF2T1ExRnVWc3BvOURQUWJJSVFCQlpSRGlkdkNSZ0NQeEtVUnlac0dwQjIxR1hYcnJKVzcyclkwRzArb0tPT01MdkpLSUZjK0FEeVduRFQ2eWNRS0pGVEpJeEkrWFlRUkgzY05nRWNFZVExKytSeWx6MVlxeGF5NWp1N1BnSkYrc1RYNVA0VTNYNVdpek9TckxpanB4V0pxSWI3Vzd2cDRUS0d6aUkrbVVCVG84bjFvNVUrcVllZXIyZ29hZllYenptVTR6SWFpWmNqclpZMCttUVR0eDQwejRYb3VnbFFSQk9nWFVaMkhXRnNvN0IzL3dEWFFya3NLSWk1UFh1bFZWd3lwVmZwUFFqaC93QjdKaS9MbFBTMmM0bmJLQVovamJpTWJpdFpsazVrZGNVL0swbC9vRDhkSzVXK083VjVqb1g1R1EyUDZ4eGJIZlNIdmE3Y0hkMXRML2thVm1ScHhqY2ExTTdFN1Z6VVltOXhuR2xlTmx0YXd4OFkvampKZXhGeVdlTnFZUVV6Q1pXdnovL0VBRUFRQUFJQkFnTUZCUVFIQmdVRkFBQUFBQUVDQXdBUkJCSWhFeUl4UVZFRk1rSmhjUlNCa2FFUUlDTlNjckhCQmlSQVlwS2lGVE0wd3ZCRFUxU0MwZi9hQUFnQkFRQUpQd0hrbHFYL0FFN2IxdWxhU1JnQS93QUI0RnZWOTFyNU12QzFITGRMWHAyWVd0cldJU0pBT0xHMWRweHZwd1EzL0twTm1RTXdENlhGSGhVZDg1QXJUK0FQZkZxNE5jVnhJQXAzZkp3QnBESGhjTklRRisrUnpOVzAwMHBacE1VVk80Z3ZwenExdkxoWGlQOEFCRFVOUkZpMXRhQUtQeHRUK3l6eU9YZjdyZTZzSzBtSmdid2ppS3dEUlQ3SzI4dkVVbG81ajhLMTF2WHcvZ1c3bTlSRm9qeFBXaHFGbzJWQmMxMlNpaG15ZTF1T05kb0JXNklsUUNNSXdOK3RTV0hCUlRpOTdmVTFyRDdKT1RacTRINlNpdUJ4YXAwZVhsbEZyL1UxUElWaGhob3gzV1orUDBjY3VsSUh6UzJ0UTRpcENqRGlmS3I1WTNEQ3VGcWoxdGE5N1dxZk0yYmtPVllwTkpBWDZxS2ZPcDUxeW8xSXFvRzRtc1pHVDB2VXdaMTVmVE1kcVYwUlJjbXAyZ2tQY0VpMnpVZUkrbkVBYktUSzRKdFlIbldQaExXdjNxTjdvS0hkRkt0OW9XUFg2R3RHVU56VWhFaVdWaTNBMWpJLzZxU2NZZHNTVXZJMTJraERXdWJjdVhvS3drU1JBYWFWaGtTYld6cnBjOUtsdmlXNmNxTTh6U3lIWnVGdnAwcG1YTi9JeC9TaVpGUnM5dFYxOTlRdkNCNHdiV3JGRjhybGNPMTlXcU9SWmwwZG1HbFBuTERkVmRTVDVBVkhoRGkxL3dBeHRwbUl2eU9VRzFUUTVnZDNOQklGQi9GbHJzL2I0TXRwaWNPNGtSUjUyNFU0UHBSY1ptM3JkS3hiQ05Jd1VWSHk3UStkWWR6ZGJOWThxeEtac01ObnEzU25Ra2prYTdpazZWeUZUS05ydXpOMHJ0UjQyOFZqWGFMdWZ4MWdIeEp3MFVPRnlSZ0U1OHQyWTM1WHZXUm5mZ1Y1VkxtQ1NaU29ydFAyVEJ4RGFZdWVYVlkwSDY5QlhaMzJ5akxQajVRR25QNG1iZGkvQ0FUNlYyODdHOTJDeG1UKzVqV08yeWM0OFFoVU44TlBsUU9CeDVqdGxheFZnT1l0b3c5TmFPMXd6dFpYSGNGWS9aWVhuays3enBCQmhvZnNwNXU4U2VhL3pOOGg1bXNGN1NGWW9kczdHeEdoR1VhTDhLN0p3MFd5VE9TaFlFYWEyc2Fqa2FJcnZ3U0hlWmZKdVo4bXF5Sk1NenhBV0YrZW5oUFVVaTdkdEVwYmwzMGdIblVtM25tR2tTWFVXNTN0dkg0Z1YyMDJEUytiWllaVmorT1VhL0dzZXVJdDRaYlBmM09QMXJzWWRtWXVUdlltRyt4UDQwUGMvRU4ybUZpTkNLeEpsa2Q5cFluVmIxaDIyck5jUGZnS3ZRVm5kZUJmTFdOdElOSXNPcmFWTXI0dkZ6YUpmbVRVMzd4UGQ1NWh4VlJ4ZjFKMFdvY2tTZDJNY1BVOVRYWjZwSEhGMzJhNUxackFmMDIxckJLTUxJWXdaVWF6QmZIejlLREpsYk1NdmhQVWRLalU0bU9Nbi8ySEczcUtsWnNiaWlWVno0Y3pITDhCclNDeWl5bm5XT2x5R1F5N0x3NnBsUHIxcVdZYk5kbnU3dHhtRGN4MVVVUUR4dlY4azdiNjIvNmc4WHZGTmZDUVhjSjU4cWl1NUZvSXo1OFBqK1ZURmk1dS9Wdi9BSUs3T211aXlCcG5GZ3hWOG85NUd0ZG12c1dlRWJkZVNHK2Y0V0E5OVpobHVWSTR4bnFEK2xMYVREYmo1Um9QUDB0cjhSeXE2eXBZeWE2TUJRM0ExMDlLWUxweHA1STNLNVZaRGFzYlBJQjFPdFN5Wk1HanloV2E5eUYwckNKaXNSbldGWTJreUJsVWNNM0xVbXYyTTdSampRdmQ4UGFZV0hEbUw4Nm14dnRNZ0pXTFlQRTV0eDFPbkxyWGI4QXhUU2JEWlBKZVFTWHRsYnp2WFpEU3g1eW9lOXJya3ZlNTA0NlVMWmVJTkc2d3lORi9UY1ZEajVreUVaUkVxY3diZzVoMFB4cjlpMU1HUTcwK0xWZDczVkVZaGsrM2ZHUnFrZ2s1aUxsYnpwNWY4UlREQ0xFRVAzdDYrcDY2Q2tDa2tGYmtrWnFZV0Z0cGIrYlN1NG1pOU5CWVZnOFJHLzJTL2FybEp6WDVlVnE3UldPV0JieUJkV1QvQUordFlXVit6SVNzR0pPTFFSYlZtM2lRZXFyd3Q1MTJUTk9wS0FPbk1NR3VlbWxnT1BpcERIdDRwTVBLcDE0YnkvcVBmVjNPeUtIekkwckRHQXB1RkdGanBVaEFXc1kvYUdCd3M0eENGN3ZJTU80emFFNm5LVFdFeDZoQjM5aG9SV0RsSFpMUmJLZVhKWUtUelB5clVPUktQZUtiN1dSUzZpM0ljYTdDUjFsY3lzWWZzaXNuM3JqVy9IV3V5ZjhBRDhUZ3B3MjYxaElRZE0zWFdwRmROYk1EcHBUNVFjeEE2MDREeVNOUEJjOHlibjUxTWtja1lJakVyV0JlMmkxTjdNbXhCbGNlQzQ1VmpwbUU3WkZ6SE50R2JoVU5zTWJxcnF0OTdOdTM2QzN6b2hFaVFsYjlha1pZbllpRnZUUVZtRTJFazJVNjgxY2FOODZsV0tQUzdPZEJ5cUNmRDlsN1JUaXNaaHlkcmpXVThJUndDM0F1NDFOdEt3ZUphWUhibVhGNzdrblMrYnJZY09sVG8rVzJiSzE3WEZ4OHFGbEdkdjdiVUxDUUY5ZWhKTld0bEN2YnI5RXJra1NlMFJ3dHZDeWtCUFdweVRKbWhsRDZuSnlQcUs3R2JFUWFneUEyeis2Z1k4Umh4czhzaDNrUDNUK2hwOFpqSGU2b202VmdVMjBYbmJTc1kyd01RUVlmS0xCdnZYckVIRVJUT3BqanRiWkQxOWRhZDRNREdjMXBQRDFzZVl2U05sWGR5RHdMNStkTEptVnM0MXJJN0p4YkwzVzh4eVAvQUFWUHQxYnd1by9LaHdxUmhhNyt2dXA5bEVVMlVZQjd4T2w2N3hRRW1rL2R1MXJ6dzZicm54eC9pQjNoNjFqUWV5R2JhVFJpUE43UW8xeVg1ZnJVcTRVUnlybkVzSjFqSEVBY3F4d2p3c1lZVFFaTDdRbmdiOHJVczhXMkZwRmZlVWdDeTYrRWovbldwOW04b3M3ZmRUeEgxdG9LMHlqTCtFVStkcHQ3Nk1CTGg4WmxHYVpaTHBMNTI1R3AxUHNoQVczTWRCVVlMT21mWHpwUXJRLzVpZUdST2hxTnNQaWhxMGNvNCthc09OT0NvNTV4V093cVpmdnlBL0lYTlRpUnd1WjVmdUQrVlJ3OSt0UlpuT3BibWFoYXBYUmNVcEVpY21ycys3YlBhWGpiTHArWHlyc3AzZWNGbHpUa0xZZWdGYk9HTnU4a1MyQjllWjk5Szg4bWRiS1BXcmdxaWcwbTlHdTFqWUhlUnh3WUhrYW4zK0JjcmNQK05mOEFjS24xUC9hY09QaHgrVlR1VjUzc3Y1MWg5bEh6eE1uRDNIbjdxbmFhYkpubGM4U2F2bWMxTmNnSFMvMExjUlIyYlNvMHp2aWllNzVWTURLdDE5QURwUkZwRUlwUTBrTDZOMUZKSkhqaUFOMXlEVTA4czd3bHM4aHpGVHp0UzdTUEY0ZlprblRMYldtV2l0NmpReVJkMWp5cTAyR2dnQ1JxZUF1TmFWWWJNRWNyelU4UlJGRVZLdFpaWFNJNVZKNG1tbHpzWldQd3ZYWVdEWjhISmtXUm9nV2NkVFhaOE1UOExxZ3JPWHQxcHlYYzN1YTBVZERTc1VMYUdzQ2o1dGJ1S3p4NGFTTlRtWGptckdZdjdJYlU1bjB2VU1tOHQ2U1VlNm9XWStsWWNzMFhGU0t3RUs3U0syOUxxYjYxaFJGN0twQnl2ZTlYK05FL0dsUHhydEY4UEdBb2tBNGxyVjJ0SmlJOXFFeXNPQjVWRUswTlBUQzJTOVlIQWhZZzJXN0c1MHBZb3BaSDdzWk5yY3FkajhhdnI1VkdjM1FDc0s2TmVzTG5JOHF3NVVlbEtvTFc1Y2FUSytVRVc5YUx0aHZaZytSK3VsUWpXb1FLaDNaUWI2Y1RTa0tCd3FITkxpbVpaVmsxVStkWWJLL01XcUQ1VkZrYytMTFVkcFRiTWV0THZ4U0t5NjZBMUh0RzYycUszdXBCOEt3c1NSWXUwTFRIaU0zUVZpcFdXRVpRVHpwRWtIRnM2M3IyZjVVY1A4cWZDbjRWN01QaFN3TitHMVljSDBydEg5M3crWElVanRlNDUxajVOakFDeFVJQm1IbldKWjVzZmhoZE9TZy9USEJLdUdoWm1qa05yK2hyQndSN1dQYUNQTWVGWUtBWm1iTHZHNDBvQnBCM3JWSFIyYmNtdFhhS1JSNE9mWUJCQURtODY3VVJraXNkbjdPTEgxcHRvekM5OHRyVkdLanFOSHc4RG81Vzl1RmRtUnhtUVp0NmJTdXk0OXE4Uk8wRXU3YmhVWXZ6cU9rMDhxaVB3cUhYMHFLdTFjWmhHU01adGkyNmZjYS9hUHRXZUhFU2hIak1pN3dKMXBRa01DQkVVSGdLQnBUUU96a2pLdFk4cS9hOHhvTDdKZG1OMU9uRHBYYUl4VDRDMnhLcUFOZU42WVUxTlhiVFlIRHVnbW1IZXZJZVlyOXJaY1hnNXBiU3dsQU13NlhxMWhRRkFWSnM5cEgzZ0xrRVYyVDJha2NSeWJTUmpxQnBVZUVqbnczMkNKaDlWeW5XNUo1MUd0UmlrRktLdFMxb1RHdHRhNFNZcU5ENzIraTlDaHdqYjhxRWhJakhBVWpnSzZKdkRuV3EwVFRtbzNMellkSFgzVmgyR2ZFY2FjMFRWNjVxYWszMGxjSCtvMDl3cnBmNGZXRmNGalJhTndjYXB0NmZVOE1MbjVHcmF4RGw1VnIrL2Y3YVdoUzF5d2FXL3FOZitVUDErcW9XMk1tSDk1b2FXalkrdXYxUjlILy94QUFqRUFFQUF3QUNBZ0lEQVFFQkFBQUFBQUFCQUJFaE1VRlJjV0dCRUpHaHdiSFIvOW9BQ0FFQkFBRS9FT0t3UDFuV3Q5eFg1aVB4VVFseElYL0FSUjJDSkUxamlUWU1vN2xibU95NVlNMGNqWTI2M1VPNEs3b3ZTV1kzQ1VCNjRxSzBtenpWdXlIQktzSS9zWDNyVll2MWFLZFQxVFBJVGFZQlQzQ3FMTjhsekRHcCttWDd1dy9GeEpnWTVlek9GZHAvSEVkeVZoY0dxNE5wRVM0aHRhb1I2TnBTRVNhVFVKMlg2cEc5L3dBZFVsSGp3UW5RQm9pTFIyMjAxdFZGV1BHbjBZRFFwa1U5WGQzSGFjQ0FJL0dJaDB6b01kV0Nhd3ByRGFPcW5RTTV5QU1MMG9OdWNaVmZOYlhNcWhkWVdEQ09hckdOeElwd0MwZWJpM2JodDAwWWgzSnJnRDBzQVVRQ09wT3lhOXk4SHhNU2tMaDZtb1pOZ0dmd0NJTVJVdUU0U0x6eXdYc2xKenhPTUdHN2pzQXd6WTh3THE4cHRJdDluaEtwNWhoTGxFalNZZ0FFWER0bHYyYlpDN3pXRS9nTjZsNGRRd2NBQVh2R3d0bFUvTWFpYndtaGc4MFFYOUFPVmlFT3RnZjJRcnV3c1NLUk1meUJOZytwZTdlcXc5UlV3VVdpc0p0MDRjckdPYlJDK3gxQ2h3OXduSzNXZ2xId1ZOeHdBUWg2QUFPYk5NWDlDaTNqMlN0dFZpSEt2OGN5amt2R2FEazVISExRTzVobkFHTUpEM3FVQ0tHU0ZhSU9oeFNMZkpxRzBsQ1JXNkFJbzBtL29kMFJHN3AyM3dRS1RVc29Xa3VTWVFMS3lod2MvTWtWRjJRbkhFNUQ2TndkWmFQUnVCOExIVk12RUVQOFBoQWQ3TFJMVmFKUlROVktMTkZJaHRzR0QwbWdJbjFGZ25oQzZGQ1F5NWU1dnd2UWZRYkthWTJIK1JRNHJtZUJ4ZEM0dDgwcHY4M0RidW9iK0g5eXVyRkR0QXoyd3I0NndzeXU2dHJyYW83dW9RRnIyTmVMRG56Ukc0bkxaajduN0lDWlFXMGJtWHJnbm1OdjRJck5LTzFPWS9RWjdTb09GaGlVN3Z1bzVwSEN3WUpaQmVMY2hDY0xHSEw2L01UNEp6RkxoR0xJTTgwZ3FlQXdORUFGaXc3N2tWUlJvamJuTkhLOCtTT2JMTVBLSHl2Z09XVUYvS1hMU1E3K1V4UzJQdDZIL2lBK0lQMUs1aVV4YUZxcnR5U2hCZVFKUndzYzRCcU1EemVIYVZ4RHhGV0N5eWxDWGhzM2FiQ2NLMjY3TDZrd0VoNkdvOUVndzRoQVFhZ2V4TmxsK0lWVHdtczV0OUd3OFdYTC9qT3FlTWVpNUZFZDdIbTBuQUl1YUhEN2xJL3JBZXpoYno5cGdLNndQWGkralRsdThzb3p4ZzlYRkk4bkI1aEV3Z0VQMWNXM1NQa2tCR1UxV0o1bWJQMnNLK0pGZlFxRVo3TzJOOTlyRHBLc1RuMUVnMU4wUzZvOFd4NlhGUlhFWjdZOFI1VE4wVjhNK0xXWURRV2dIS25MZHJxK0xTTVRHWVVhZFZOdkpsamx1ZzNjZUNVQ2k2V290STJqbm8vS0gvemVBdGN0clE3WGdiQk9USGNSd1BPdnVFMXhhTkJmUHQ1WHpGTHhQeTFBODBvQnhTUkJFcnVsUExMRUNTOHRnS3Viem1YYUNGK2hZRDJqaHdaQXcvT3JuYjFBc0JHYzhhZGp4OUh6Qk5HVXhIZ3ZnVEFNRCtGZzFwbEVIQytYTWxGQnh1b3N0S05EdW5IZzdXb1pmeWpkV05wVGdGcHBEU1lGSHcxVHdTTzQyTTl1SlZ1SGhWU2wxbHJORXZhUWFyOFJxTUtxZTFjRVZzTGVsdUZ3TmE2MXNFdXJTcG5GWTJJdUpZWG9jR1MyVFRHYnJES2xyaXV2aDFzNHRPS1VncXFzS0hndVlMM3FiT2JFdnZNZ0pYSVBML3pqTzBxcEZNR3hBQlMwaFFYMEZ4MHR2WG9HL0pGWXdUVU5pdC9NUHd5dlRDVm8xQTdKWkVJZW9BbHRWVjdWMlVhcWQzQ05RWVNvSVFzTGI3WDdqQ1lRZEpLaTExWHlOa0hBa2pxU3FLUld3b3RzWktQM3lLMllIQlFOaFltZTRQaFhzVXM2K2lKTzBHWU5DaWluTVJVM1ZCdDEvdW8zcmN5V3E1VDFEd3g2bFBnc1hMRWM4a2FCNDVuemhJS2JrOUFKKzF0S1pqd2VHa1cvaElKclRoSTAydmc1aXlqS3dEakdGZURONXg5Ung4VllpNndlcWJrQlNKZnNTUGdkeHJVdWNsUE9iaC9VYVNzSUU4V3FRWmU3TGNyYVc0dVJVUE5rRkM2Qm53L01zQkY3MlZxS3BsVXJ5TGp4em9qUWd4MmNqNkZyZldESzR5UHNGQ3o0UUdINkFpS0RicldENDRObEtRMDZXTVdDM011UDdNZldTbFZTS0w4aGRnVnhhRStJOEU4UzcxUUI3N2VEQm5DMFA0TS9pVDA2Mnd5cTAzTE1mM1ViU29zQnBCOTZkdDRKWjhOd0pUSkplMkxCZ2Npd1FxMTV1cjdKWlJIVmd0d1dQWlk0RzROTmIyWDFVdm85YzFLd0d1QzN4TnZRM0t0cDVrUzY0MkZqVUlIU3BpZVQ1dkhSRkZqMU5XOGd4V2EwWVdIRi93RFQ3Sk9MbUFBRHhnL3BEMGFNVEJwOGtSVnBZS2lEQ25KL0l2OEE1WHdMM1BhWmhGaFI1V0tTdDNiR2lpaTlnT1VjS1pYZWFLVUk3Tkw4SENrWExvTjhKTkZVeFYxWUw3bDMvcEJwR3JpaWtFMkFvREVzZG9KRXBmemRYd0szc1h1TlVBejRBb0pXVXZYTHk3Tk1YSGdrNlBiRFk3RjNERzV6V2IzQndPdEcxb3Y1cVdaY1hEczB6cnNlUjBqc3ZpVkYzWkE5Z3pCVGhRaCt4aGhpeTJoOFdwL0lUQzVDWWQyQi9LZlNQdW0ybTA4M0YrMzNiRnFyaGFqL0FJd2VLNG1OTFMwYWhlWVcxNUdrTThRVGxlai9BRW1JcTlIK0tRa241cUw4VlU0ZGVhQ1AzYTRYSGRtOGVYeVFvMWwvNzhoK3hEUjhZL3JVUzZ1eFZtcDNZYTlYM0hTOVk3c3ZQajRJbTVRM3pMODY4OERDVnlMQUJ6c0R6ZFIyMFVwYm9sUXo1dHN0QVJEUDl0SmZWQjR2cCtwZDg2Z2w1dW1tS2hzaEVhQ1l1UUVzYjVNZ0JVeklxazRkdFF2MG5rdUVsR2tTKzh3MmFSUTlPV0dmNWdXZzhGeGh5dWxFS2xIdkhaUTJ4RkJhNUJRUkx1T0dnQTlqYkhnWnR0OGt1QWdBdTBGL01vUjhTRnlFV290ZGJaYm02eUNLMEQ2aWJZdGtieTA0Z0pnQ0tidnRqRzM0NVE4azFkTUZndWVaSWNjNWdBL0FqUkNlb296SllFVGNFYlVnang1Z0xXUmlqRkx3VkF0RTVMVGFKMUM3ZGZtNWNnYmpYSlhRRTlzY1psSlZxeU8yaFE0TmJzSmN4SXJIVm8yT01iTWk0VkZ1SzRpUENLSlJIN0cyRkZ2NGxwOXdoa0RwR0xpNm5HaGtEQ202MGFaOXJMc0RpNWtRTUd4QVZ1N1hnaXI5RzdDc2xXYVNVbFhUd3hGZXdWSkJsa3Y2dUYyZUtvMk1pcWc2N3FMN0htM3lKUzBMaExuMU5mZHpLb1R1V1VzcExXUW5KMXQwN1lGWWdybFI0ZXBUYWtPV1FwdmphdzVZY0pjZ0tMM0t0ZWUxL2lBWDdBUmNRRk92WlFhTlFKVUljSXZSRTNRSWFGMGZnaExBeVZjdXBlRjA1MlRSenFST1ZXY3k5aXloSVdWV0hmalhDNVNwSDZqNmlLSVdqZXZZdG9YWlc3bkZHL0RXdFhaRUVqcWdRbkZFYkt5akpDMUd1R0JJVncrNFppOE5wVXZvdVVaQmRrMzFWWEd5YWg3TTVJMHcwdFJzQTdCNWNsVzJtZ1VCTC9qRit3VjZWYXNIZm1CTmZIZ1lGcmhKWWpuNVdZUzV2VG96N25YQ1VXdzNVNzZqb1ZhVEE2T2s5YkZRTFloWkZ5MStvMmczS0tDLzFVb1R3UWYxNncybUYxQkRLQWVpUGFzczB0cnVRdWdHbjVJcmNwUHV1ZzR1b2xvUzNVQjdDbVdOU1F0aUVyTHdUK0pTTmtGODFGSWVQUkM4Yks1ZHlzajNSUnNVazJMbXNFVERPZEVEOW9OMVdsdmlQSTNxamhVZ3RTckRTWFRMeGlxVTA0Ukl4eStOdEtBR0Y3RGVPQVVNS2U0djJNT25UL29vMjNqWk50NWdsc3BEalVSSG44WURGYStwNWcrb2dBQTM2Vi83RUtveTQwc1BEQjZrRjFCeHlCblpMeTRUaURqV2Y4SUtxUDFMS212d0lwUGpQdEJ3SnFZZ0dKRlNGU0s1SmdVY1JGdExNblQ2eCtjSWM0THRsZ3F4cmpOQjNpS0JQLy9FQUN3UkFBSUNBUVFDQWdJQkF3VUJBQUFBQUFFQ0FBTVJCQkloTVJBVElrRWdVU015TTNFVVFxSEIwZUgvMmdBSUFRSUJBVDhBMDM5c1RVcjhzL3VhYTcvWTM0am53VE13R0EvbGZaNjFKaTdyRzNHV2NKS2EvbG1Gd2c1aDFDZlhNMDZFMkZwcFd5bUpxdUZ6SzMva3lZcHlQUGZrbUh3cGdQalBuWHRoTVRURThBeTgvQ1VjQW1NUzV5WUVBOGFJOWlhZ0FwZ3dWZ0dWSEE4R0R3VENmS3dRVEhoWnFxL1pFckM0bGpBRG1WQUZlSTlCSEt6UDdpUC9BQzRITTBwS3NDWmVDdzRnUTd1WWdnbUlQQi9KVDRNK280alZqRVlsNVN1MVJHYkF5WmFSYWN6MWhlVmhKVjhUY1NNeFd5M01BNGdHUEI0Z3Q1d2Z3Yk9PSld4SStYZmdSWVRnUkxDeElJeEdXRVpXQnlyYllPcHFjN0lPb1RMNndHM1QzcXE0ajJBamlhYlU3aGhvR0RkVE1zWUtNbVBZckU0TTAxKzRiVEJ6NGJVcXBJTVhVcWM0aU1IR1JCQzIyTnFSdWozcHlBWVA2WUR4RlZXZUFUVUROWmlsaERreXhmWUFUSFJjZFMwS0RrUUVna2lVWCtydnFDd01NeS9iWU1RMEoySURnOFNuVWdEYTAzaGh4QXFEdnVNbFpNclo2Uit4RVlPTWlhcGpuYUpTZ3NZaG8rbHFIUWxOb0s0L1VVZ3JLRnpjVDR0czlqRkIxQXVQcWZMOVIzOWFoY1poY0hneS9BbGFrZ2o2aURIQ0QvMy9BT1FWc2V6UFd5OUdGUllNSGd5eXYxZ3F3bE5KdGdRQWJFaVVqRU5DWTZubzliYjFpSW81V2F2SEVRRXNDb2x6Ykp1ZjZnTnEvY292VlNkNHdmM0JnaUt1MWlKZzV6NDFMTXY5TXlWT1lTVy95VEFnUlJXc1NvQVJjZ1lqWk1hc1MrdmZXUWV4Tk91SytPekVVSUppWXo0MjdUa1RVcVM0L1VyK0NiekVyTG5jMFJSMWlNdjFpVzBENm1udEsvQXpVb1FDeXlrNVNPMjBTK2dXam51UHBpcDdtbCtWZ0JnSFpFRHQ5aWU1SUxGUDNNbk9NUXpUa01naHpDMW4wc3pjUnhGQndOM2ZqVTJZc3hIR0JGWURpRmdNOHdXODhHRnVjWWlqNXh2blhpVXJ0V0VaaWFqYm43SC9BRkxOU2pIcVVNQSs2TDRLakdNUVZyMzFBY3kxd2lFbWFLenRUR1lJTW1GZ3E3bW5DODVuT1k3QlZKTUNHK3d3TnZYTUJHTW1QODg3ZXBXQmpHSUNERkh5ekVQd3pFWU1UanhqQ3pjVG1KcWZXTnMwbHkycGtUYmc3b0ZPYzVnVWduSm1Rb21xdjNuYU9wVmRzYmRLYlZzWEltUEJJWHVHMzNXQlBxVVl5Vy9jQjlia0hveGxMZmZFTzVnUFdjUXF4WUVIaUJjZFMxd28yeTdWWitLVFRzYzQ4RldQeGhCV1Y2TU1tVDJaNlcwemIwbGVxQjRQY0Y0RWJVSW83bG1vYXc3VWphVnpGMDdDVTFsS21iN0dKL3FuVHNRYXhtT0FQK1pZMXI4VFRWdXRtNXBWVHRFdHJWMElsV3F3TVBFdFJ1akN3aGN0d2sxUTJNRkI1akxqcVZaSGlqbDh5L2dIL00wOW9DN1NlcFlWZFNKdUt0bUcwV241UUpsU3crcHBuMlBrejNKRGFtSXRxazh5NXQ3WmlIYTJaN2xsZDZ3WHFQdVdhZ0JEaVZwN0h3WlE5UVhhNjlRWDByMEkrdEdNS0o4bk9UR1VnWU1RamJIZGllNXBrSllpYWhNT1JGQkhVREdFRXdRRmdtMFJSNEFqREUyN2hBbTNtY3dLY1RhUkRtSVdRNVdBTnptYytFTzB4MjNIeVR0Nm1vWElMVFNMdWZCNm5wU0RUcU9STEtFVXl4QU9wdElNM21iek41aURkSFhhTXhiU0lMcDd4akdJSDNDQlB1RCtQbWUxRERhazl5VDJwK29iQWVoTWt5ejJieU04Q1dOWWNnbmlhU25ZTitlL04yUU53aGRpTTRtL2NZVUpucmdyTVFISEVmSkhNRlpJbnJocmljREVGcFBBRWEwbmpFMnpaTUVRQXdBemFZNmUxdUdJbHFHdnM1aTJxb3dJYjROUkh1M0tSQlN6Z05GQXovaWJwdUUzaUlOL0FqSVV3Q1lvU0JFbnFReDZWMm5FVlN4d3MwNlpZbHZxR3REOVFWSitwc1Q5UmdnaFpSQzRNclRZVEw4c3dFWFRFVDBHRFRtUFF3RVJ3RUFpVnRabkVmVDJnd2FXMkhTMnlsV3JPR2x1WElVQ0xwMmdvSWdSaENqRUdVTUZZZ3lqNXN4RUNHYlROa2FnbU5wekJUdDdqRDVHRExYQUg4RDBZcm5FMFg5SmpZZ0loYVdmM1lUL0FDTCtLQUZ6Tkp3emdmZ1NCQ1l4bi8vRUFDb1JBQUlDQWdFREJBRUZBQU1BQUFBQUFBQUJBaEVERWlFUU1VRUVFeUFpVVFVVU1rSnhGU05oLzlvQUNBRURBUUUvQUoveVpqSng4cjVycFh4b1JpanU2UktrdFJkeWJJeDJmQkgwMlIrRDFLY01TVEo4TXg4czBXbGZOS2hkYStQcEZjck02VnVpUGNrazJZLytwV3U1KzRuTGc5eVM3bWVyNElPbVJuYUpMNG9YeGZ3d1QwSnp0a1VQdnlReWtIRkxoR1NDbGkycmt5cTJ4RVh3UG40TG9pL2crcUU2TEkvVWs3WkZOdWtlaC9Uc2srR2Y4VlNxVE82SXhUWTQwdmdsWjdkSytxRVNqcjFZbFk0VXJRbjBpcmpZMVIrblJqTFBGU1BUL1hLTmNFSGRvaEJ2a2pHMlpjT3I0R3FFWTR0c1VYRkdUSFhLS0VSeE5rOGJYQktPdlJLeU9La1J4eW9mZnBkUTZlbGxLT1ZOSG84R2ZmYWZZN0lqYUlOMlJiNlR4T1FvTkdLNDhpeU5xbWVDZUsrVUtMUW5JaXAwU2lwUC93QkpKcDB6Q2ljdFZ3UXpUZmNuQnBsVVNkWTEwOUxDT0tQdXk3K0NmcnMwdjdzL2N5OHV5RVhMa1Nyc1kwNUVxUk9VWXJsOG51L2dXUmVVSnBmNFFkOG9sUFY4am5idGpteFpINFpIMVVuRDI1TzBOdHVtWWI1R3VIYklYSVNpYlFmZ3lZWE5mVGtxbVN5YlFTL0JkUnJwZ1ZsSmtLVmtwdVQyWTVNbFhkTWpyVnZ1Umt6RFBXUmthMmRqYlp0YXFoTnc0RUoycU1MNEp1NWFJbExYaUkvOUk5cnNoa2NUTkZUanV1L2t4VTN5WkZUTWNkakhrY0NHYll5OFFkRjMzSEZWd3lPT1RZNE5ObXNhdXhQa3pLcHRDMVhjVVlWeXlYdCtCVDF1aFd6REg2MlI1Wm82c1VYK0J4YUZIaXk3UXVKRTViTVFrWThiaVpGYXJwWEZrSk5jMmJObFU2TWNkblI2bVA4QVlqRnpZbDlxUmJuYW9wT1BIY2hGeWxTSEpZNG9yU1JUN0lqK0d5VGZleW1oOElrdnNTaTByZlJOT1ZFSXJWV1BDNWNtZkU0U1lwV3RTMVZVWCtCdHkvMDlQaTFWazhWcWllT1VIWFJJaW14dzlyRzVlU2Qwa05ieHRlQ0VxNzl4VXIyNUU0NnROY2psZmN4eGN1VEZocm1SbWl0SDBnb3greEdhbDJQM09ycER5eHpSMVpMQ3g0ckk0Wk1oampEbVJIUEJEeXhaa3lYa2pId3oyb3k3RHdSU3N4cUVUUEtMeHRJbGtzaE54ZGs4Q2ZLSGpraFJrK0RWUS9tWVpicHNVdkJOSjlKY0l4SXlRNXNncVlrbkdqVnd1aFM4R1dOeE5HUWl5VVBLTVMxUk5XaFFaT0RvMFpERzJ4eTBpWkZPN2l6VEkrN0k0SGZKRktDRkpNZmN4eGl2QmthTVUvcU5yeWNDbFhSTlhZM2FLNlkwbStSdFFmQXBxWEJyQkRhc3RDVkZxUzVKTkM2UGxFVWt1aWRDVjl6RTY0TXJwV0tiRmtkRU1qWkNYNU5oSzBLSnIwaTdkR2xudGl4alZNdmdmMkZCaWl4UVpxeFJLU01lbGNrRkd1RExQYmdycER2UW9vY0NNcVZHeHQwaXFFNk54U0h6eWFDZ2tiRzVkbXhaWkY2TGxXUWxzUEcyTEVlMFJ4VXozRkhnVnZ1YWxNVVdTZW9uZllxUTlqWmtadXh1bHlaRzY0RTVGeS9JcklxUW9zVUNjbEl4Y0t4NW9zV1NKN3NTT1dKVnRpeVJqM0k1c2JQZmdMUGpNc2xOMmpIVWJZODhUM2tPY1JUalpKTnJnazlFck4wS1p1UnpKQ3pJOXl5TEh4alltV0ppTlVadTVHeWlpSDhUK2o2TG9tSjFGR1o4Si9CRkNQL1oiIGlkPSJjIiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIvPjwvZGVmcz48L3N2Zz4=", + "description": "Displays fluid pressure reading as a horizontal progress bar with background. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pressure', label: 'Pressure', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 15 - 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#2B54CE\"},{\"from\":5,\"to\":10,\"color\":\"#3B911C\"},{\"from\":10,\"to\":15,\"color\":\"#F77410\"},{\"from\":15,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":25,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:cHJlc3N1cmVfcHJvZ3Jlc3NfYmFyX2JhY2tncm91bmQucG5n:cHJlc3N1cmVfcHJvZ3Jlc3NfYmFyX2JhY2tncm91bmQucG5n;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#2B54CE\"},{\"from\":5,\"to\":10,\"color\":\"#3B911C\"},{\"from\":10,\"to\":15,\"color\":\"#F77410\"},{\"from\":15,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Pressure\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"bar\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "fluid pressure", + "liquid pressure", + "pump pressure", + "hydraulic", + "pump", + "compressibility", + "compressive stress", + "pipe", + "pipeline" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/fluid_pressure_range_chart.json b/application/src/main/data/json/system/widget_types/fluid_pressure_range_chart.json new file mode 100644 index 0000000000..ce1d777dac --- /dev/null +++ b/application/src/main/data/json/system/widget_types/fluid_pressure_range_chart.json @@ -0,0 +1,37 @@ +{ + "fqn": "fluid_pressure_range_chart", + "name": "Pressure range chart", + "deprecated": false, + "image": "tb-image:cHJlc3N1cmVfcmFuZ2VfY2hhcnRfKDEpLnN2Zw==:cHJlc3N1cmVfcmFuZ2VfY2hhcnQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxnIGNsaXAtcGF0aD0idXJsKCNhKSI+PHBhdGggZmlsbD0idXJsKCNiKSIgZD0ibTY3LjMgNTAuMy0yMi4xIDU2LTcuNyAxN3YxOS4zSDE4NnYtMjUuM2wtLjUtMjYuMy0xMC41IDQwLTYuMi0yNS0yLjYgNy4zLTExLjQgMjcuNWMtNy41LTI5LjktMTMuOC00MS0yMi03MC40bC04LjYgNDMuOWgtMTAuOUwxMTEuMiA0M2wtNC4zLTI4LTIwLjIgNzAtNC44LTIuMi0xNC42LTMyLjVaIi8+PHBhdGggc3Ryb2tlPSJ1cmwoI2MpIiBzdHJva2Utd2lkdGg9Ii45IiBkPSJtMzcuOCAxMjMuNCA3LjctMTcuMiAyMi01NiAxNC43IDMyLjYgNC44IDIuMSAyMC4yLTcwIDQuNiAzNS4zIDEuOCA2NGgxMS41bDcuMi00NC43IDIyLjggNzAuOSAxMS40LTI3LjYgMi42LTcuNCA2LjIgMjUuNSAxMC0zOS45Ii8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Ik0xNS40IDE3LjJ2LjZoLTMuN3YtLjVsMS45LTIuMS41LS43LjMtLjR2LTFsLS40LS40YTEgMSAwIDAgMC0uNSAwaC0uN2ExIDEgMCAwIDAtLjMuNWwtLjIuNmgtLjdjMC0uNCAwLS42LjItMWwuNy0uNWEyIDIgMCAwIDEgMS0uM2MuMyAwIC42IDAgLjkuMmwuNi41LjIuOC0uMS41LS4yLjZhNy44IDcuOCAwIDAgMS0uOCAxbC0xLjUgMS42aDIuOFptMS45LTIuMS0uNi0uMi4zLTIuOGgyLjl2LjdoLTIuM2wtLjIgMS41LjQtLjJoLjZsLjcuMWMuMiAwIC4zLjIuNS40bC4zLjYuMS44di43Yy0uMS4zLS4yLjUtLjQuNi0uMS4yLS4zLjMtLjYuNGEyLjIgMi4yIDAgMCAxLTEuNCAwYy0uMiAwLS40IDAtLjYtLjJsLS4zLS41YTIgMiAwIDAgMS0uMi0uN2guN2wuMS41LjQuNGgxbC4zLS4zLjItLjRhMS44IDEuOCAwIDAgMCAwLTFjMC0uMiAwLS4zLS4yLS40YTEgMSAwIDAgMC0uMy0uM2gtMS4xbC0uMy4zWm0zLjctMy4zaC44VjE3bC0uMS44SDIxdi02Wm0zLjYgMy44djFsLS40LjctLjUuNC0uNy4yYy0uMyAwLS41IDAtLjctLjItLjIgMC0uNC0uMi0uNS0uNGEyIDIgMCAwIDEtLjQtLjYgNCA0IDAgMCAxLS4xLS44di0uNGwuMS0uOC40LS43YzAtLjIuMy0uMy41LS40bC43LS4xLjcuMS41LjUuMy43LjEuOFptLS43LjFWMTVsLS4zLS41YTEgMSAwIDAgMC0uOC0uNGgtLjVhMSAxIDAgMCAwLS4zLjNsLS4zLjR2MS40bC4xLjUuNC40LjYuMWguNWwuMy0uNC4yLS41di0uNlptNC4yIDEuNHYtMi42Yy0uMS0uMi0uMi0uMy0uNC0uM2ExIDEgMCAwIDAtLjUtLjFoLS40bC0uNC4zdi4zaC0uOGwuMS0uNC40LS40LjUtLjMuNy0uMS44LjEuNi41LjIuOHYyLjRsLjEuNGgtLjdsLS4xLS4yVjE3Wm0uMi0xLjl2LjVIMjdsLS40LjItLjMuM3YuN2wuMy4yLjUuMWExLjIgMS4yIDAgMCAwIDEtLjVsLjEtLjMuMy4zLS4xLjRhMS43IDEuNyAwIDAgMS0uOC42bC0uNi4yYy0uMyAwLS42IDAtLjgtLjJsLS41LS41LS4yLS42YzAtLjIgMC0uNC4yLS42IDAtLjIuMi0uMy4zLS40bC42LS4zSDI4LjNabTIuNS0xdjMuNmgtLjd2LTQuMmguN3YuNlptMS40LS43di43aC0uOGExIDEgMCAwIDAtLjQuM2wtLjIuM3YuNGwtLjMuMS4xLS43LjItLjYuNC0uNGExLjEgMS4xIDAgMCAxIDEgMFpNMTUuNCA0Mi45di42aC0zLjdWNDNsMS45LTIuMS41LS43LjMtLjR2LTFsLS40LS40YTEgMSAwIDAgMC0uNSAwaC0uN2ExIDEgMCAwIDAtLjMuNWwtLjIuNmgtLjdjMC0uNCAwLS43LjItMWwuNy0uNWEyIDIgMCAwIDEgMS0uM2MuMyAwIC42IDAgLjkuMmwuNi41LjIuOC0uMS41LS4yLjVhNy44IDcuOCAwIDAgMS0uOCAxTDEyLjYgNDNoMi44Wm00LjUtMi43di44bC0uMSAxLjItLjQuOC0uNS40YTIgMiAwIDAgMS0uOC4ybC0uNi0uMWMtLjIgMC0uMy0uMi0uNS0uM2wtLjQtLjUtLjItLjd2LTNjLjEtLjMuMi0uNi40LS43LjItLjIuMy0uNC42LS41SDE4LjdhMS40IDEuNCAwIDAgMSAuOS43bC4yLjcuMSAxWm0tLjcgMXYtMS44bC0uMi0uNWExIDEgMCAwIDAtLjItLjNsLS4zLS4yYTEgMSAwIDAgMC0uNC0uMSAxIDEgMCAwIDAtLjQuMWMtLjIgMC0uMy4yLS40LjNsLS4yLjVWNDEuOGwuMS41LjIuNC4zLjJoLjlsLjMtLjQuMi0uNXYtLjhabTEuOC0zLjdoLjh2NS4ybC0uMS44SDIxdi02Wm0zLjYgMy44djFsLS40LjYtLjUuNS0uNy4yYy0uMyAwLS41IDAtLjctLjItLjIgMC0uNC0uMi0uNS0uNGEyIDIgMCAwIDEtLjQtLjYgNCA0IDAgMCAxLS4xLS45di0uM2wuMS0uOS40LS42YzAtLjIuMy0uMy41LS40bC43LS4xLjcuMS41LjUuMy43LjEuOFptLS43LjF2LS43bC0uMy0uNWExIDEgMCAwIDAtLjgtLjRoLS41YTEgMSAwIDAgMC0uMy4zbC0uMy40djEuNGwuMS41LjQuNC42LjFoLjVsLjMtLjQuMi0uNXYtLjZabTQuMiAxLjRWNDBsLS40LS4yYTEgMSAwIDAgMC0uNS0uMWgtLjRsLS40LjN2LjNoLS44bC4xLS40LjQtLjRjLjEtLjIuMy0uMi41LS4zbC43LS4xLjguMS42LjUuMi44VjQzbC4xLjRoLS43bC0uMS0uM3YtLjNabS4yLTEuOXYuNUgyN2wtLjQuMi0uMy4zdi43bC4zLjIuNS4xYTEuMiAxLjIgMCAwIDAgMS0uNWwuMS0uNC4zLjQtLjEuNGExLjcgMS43IDAgMCAxLS44LjZsLS42LjItLjgtLjItLjUtLjUtLjItLjZjMC0uMiAwLS40LjItLjYgMC0uMi4yLS4zLjMtLjRsLjYtLjNIMjguM1ptMi41LTF2My42aC0uN3YtNC4yaC43di42Wm0xLjQtLjd2LjdoLS44YTEgMSAwIDAgMC0uNC4zbC0uMi4zdi40bC0uMy4xLjEtLjcuMi0uNi40LS40YTEuMSAxLjEgMCAwIDEgMSAwWk0xNC4xIDYzLjV2NS43aC0uN3YtNC44bC0xLjUuNXYtLjdsMi0uN2guMlptMy4yIDMtLjYtLjIuMy0yLjhoMi45di43aC0yLjNsLS4yIDEuNS40LS4ySDE5LjFsLjUuNWMuMS4xLjMuMy4zLjZsLjEuOHYuN2wtLjQuNmMtLjEuMi0uMy4zLS42LjRhMiAyIDAgMCAxLS44LjJsLS42LS4xLS42LS4zLS4zLS41YTIgMiAwIDAgMS0uMi0uN2guN2wuMS41Yy4xLjIuMy4zLjQuM2wuNS4yLjUtLjEuMy0uMy4yLS40YTEuOCAxLjggMCAwIDAgMC0xYzAtLjIgMC0uMy0uMi0uNGExIDEgMCAwIDAtLjMtLjNsLS41LS4xLS42LjEtLjMuM1ptMy43LTMuM2guOHY1LjJsLS4xLjhIMjF2LTZabTMuNiAzLjh2MWwtLjQuNi0uNS41LS43LjItLjctLjJjLS4yIDAtLjQtLjItLjUtLjRhMiAyIDAgMCAxLS40LS42IDQgNCAwIDAgMS0uMS0uOVY2N2wuMS0uOS40LS42YzAtLjIuMy0uMy41LS40bC43LS4xLjcuMS41LjUuMy42LjEgMVptLS43LjF2LS43bC0uMy0uNWExIDEgMCAwIDAtLjgtLjRoLS41YTEgMSAwIDAgMC0uMy4zbC0uMy40djEuNGwuMS41LjQuNC42LjFoLjVsLjMtLjQuMi0uNXYtLjZabTQuMiAxLjN2LTIuNmwtLjQtLjJhMSAxIDAgMCAwLS41LS4ybC0uNC4xLS40LjN2LjNoLS44bC4xLS40LjQtLjRjLjEtLjIuMy0uMy41LS4zbC43LS4xLjguMS42LjUuMi44djIuNGwuMS40aC0uN2wtLjEtLjN2LS40Wm0uMi0xLjh2LjVIMjdsLS40LjItLjMuM3YuN2wuMy4yLjUuMWExLjIgMS4yIDAgMCAwIDEtLjVsLjEtLjQuMy40LS4xLjNhMS43IDEuNyAwIDAgMS0uOC43bC0uNi4yLS44LS4yLS41LS41LS4yLS42YzAtLjIgMC0uNC4yLS42IDAtLjIuMi0uMy4zLS40bC42LS4zSDI4LjNabTIuNS0xdjMuNmgtLjd2LTQuM2guN3YuN1ptMS40LS43di43aC0uOGExIDEgMCAwIDAtLjQuM2wtLjIuM3YuNGwtLjMuMS4xLS43LjItLjYuNC0uNGExLjEgMS4xIDAgMCAxIDEgMFpNMTQuMSA4OS4xVjk1aC0uN1Y5MGwtMS41LjZ2LS43bDItLjhoLjJabTUuOCAyLjV2LjhsLS4xIDEuMi0uNC44LS41LjRhMiAyIDAgMCAxLS44LjFoLS42Yy0uMiAwLS4zLS4yLS41LS4zbC0uNC0uNS0uMi0uN3YtM2wuNC0uOC42LS40LjctLjFoLjZhMS40IDEuNCAwIDAgMSAuOS44bC4yLjcuMSAxWm0tLjcgMVY5MC44bC0uMi0uNWExIDEgMCAwIDAtLjItLjRsLS4zLS4xYTEgMSAwIDAgMC0uNC0uMSAxIDEgMCAwIDAtLjQgMGwtLjQuNC0uMi41djIuNmwuMS41LjIuNC4zLjJoLjlsLjMtLjQuMi0uNXYtLjhabTEuOC0zLjdoLjhWOTRsLS4xLjlIMjF2LTZabTMuNiAzLjh2MWwtLjQuNi0uNS41LS43LjFoLS43bC0uNS0uNWEyIDIgMCAwIDEtLjQtLjYgNCA0IDAgMCAxLS4xLS45di0uM2wuMS0uOS40LS42YzAtLjIuMy0uMy41LS40bC43LS4xLjcuMS41LjUuMy42LjEgMVptLS43LjFWOTJsLS4zLS41YTEgMSAwIDAgMC0uOC0uNGgtLjVhMSAxIDAgMCAwLS4zLjNsLS4zLjR2MS40bC4xLjUuNC40LjYuMWguNWwuMy0uNC4yLS41di0uNlptNC4yIDEuM3YtMi42bC0uNC0uM2ExIDEgMCAwIDAtLjUgMGgtLjRsLS40LjN2LjNoLS44bC4xLS40LjQtLjRjLjEtLjIuMy0uMy41LS4zbC43LS4xLjguMS42LjUuMi44djIuNGwuMS40aC0uN2wtLjEtLjN2LS40Wm0uMi0xLjh2LjVIMjdsLS40LjItLjMuM3YuN2wuMy4yLjUuMWExLjIgMS4yIDAgMCAwIDEtLjVsLjEtLjQuMy40LS4xLjNhMS43IDEuNyAwIDAgMS0uOC43bC0uNi4xLS44LS4xLS41LS41LS4yLS42YzAtLjIgMC0uNC4yLS42IDAtLjIuMi0uMy4zLS40bC42LS4zLjgtLjFoLjlabTIuNS0xdjMuNmgtLjd2LTQuM2guN3YuN1ptMS40LS43di43aC0uOGExIDEgMCAwIDAtLjQuM2wtLjIuM3YuNGwtLjMuMS4xLS43LjItLjYuNC0uNGExLjEgMS4xIDAgMCAxIDEgMFpNMTcuNiAxMTcuOWwtLjUtLjIuMi0yLjhoM3YuNkgxOGwtLjIgMS42LjQtLjJoMS4ybC42LjUuMy42LjEuN3YuOGwtLjQuNi0uNi40YTIgMiAwIDAgMS0uOC4xSDE4bC0uNi0uMy0uNC0uNWEyIDIgMCAwIDEtLjEtLjdoLjZsLjIuNWMuMS4xLjIuMy40LjNsLjUuMWguNWwuMy0uMy4yLS40YTEuOCAxLjggMCAwIDAgMC0xYzAtLjIgMC0uMy0uMi0uNGExIDEgMCAwIDAtLjMtLjNsLS41LS4xSDE4bC0uNC40Wm0zLjgtMy4zaC43djZoLS43di02Wm0zLjYgMy44LS4xIDEtLjMuNi0uNS41LS43LjEtLjgtLjFjLS4yIDAtLjMtLjItLjUtLjRhMiAyIDAgMCAxLS4zLS42IDQgNCAwIDAgMS0uMi0uOXYtLjNsLjItLjkuMy0uNi41LS40LjctLjEuOC4xLjUuNC4zLjcuMSAxWm0tLjcgMHYtLjZsLS4zLS41YTEgMSAwIDAgMC0uMy0uMyAxIDEgMCAwIDAtLjUtLjFoLS41YTEgMSAwIDAgMC0uNC4zbC0uMi40LS4xLjR2MWwuMi41LjQuNC42LjFoLjVsLjMtLjQuMi0uNXYtLjZabTQuMiAxLjR2LTIuNmwtLjQtLjNhMSAxIDAgMCAwLS41IDBoLS41bC0uMy4zdi4zSDI2bC4xLS40YzAtLjIuMi0uMy4zLS40bC42LS4zLjctLjEuOC4xLjUuNWMuMi4yLjIuNS4yLjh2Mi40bC4yLjRoLS44di0uN1ptLjEtMS44di41aC0xLjJsLS40LjItLjMuMnYuOGwuMy4yLjQuMWExLjIgMS4yIDAgMCAwIDEtLjVsLjItLjQuMy40LS4xLjNhMS43IDEuNyAwIDAgMS0uOC43bC0uNy4xLS43LS4xLS41LS41LS4yLS42LjEtLjYuNC0uNC42LS4zLjgtLjFoLjhabTIuNi0xdjMuNmgtLjd2LTQuM2guN3YuN1ptMS4zLS43di43aC0uOGExIDEgMCAwIDAtLjMuMmwtLjIuNHYuNGwtLjMuMXYtLjdsLjMtLjYuNC0uNGExLjEgMS4xIDAgMCAxIDEgMFpNMjAuMyAxNDN2LjhsLS4xIDEuMi0uNC44Yy0uMS4yLS4zLjMtLjYuNGEyIDIgMCAwIDEtLjcuMWgtLjZsLS41LS4zLS40LS41LS4yLS43LS4xLTF2LS44bC4xLTEuMi40LS44Yy4xLS4yLjMtLjMuNi0uNGwuNy0uMWguNmExLjQgMS40IDAgMCAxIC45LjhsLjIuN3YxWm0tLjcgMVYxNDIuMWwtLjItLjVhMSAxIDAgMCAwLS4yLS40bC0uMy0uMmExIDEgMCAwIDAtLjQgMCAxIDEgMCAwIDAtLjUgMGwtLjMuNC0uMi41djIuNmwuMS41LjIuNC4zLjJoLjlsLjMtLjQuMi0uNXYtLjlabTEuOC0zLjdoLjd2NmgtLjd2LTZaTTI1IDE0NGwtLjEgMS0uMy42LS41LjUtLjcuMS0uOC0uMS0uNS0uNGEyIDIgMCAwIDEtLjMtLjYgNCA0IDAgMCAxLS4yLS45di0uM2wuMi0uOS4zLS42LjUtLjQuNy0uMi44LjIuNS40LjMuNy4xIDFabS0uNyAwdi0uNmwtLjMtLjVhMSAxIDAgMCAwLS44LS40aC0uNWExIDEgMCAwIDAtLjQuM2wtLjIuNC0uMS40djFsLjIuNGMwIC4yLjIuNC40LjVsLjYuMS41LS4xLjMtLjMuMi0uNXYtLjZabTQuMiAxLjRWMTQzbC0uNC0uM2ExIDEgMCAwIDAtLjUgMGgtLjVsLS4zLjN2LjNIMjZsLjEtLjRjMC0uMi4yLS4zLjMtLjVsLjYtLjIuNy0uMi44LjIuNS41Yy4yLjIuMi40LjIuOHYyLjRsLjIuNGgtLjh2LS43Wm0uMS0xLjh2LjVoLTEuMmwtLjQuMi0uMy4ydi43YzAgLjIuMi4yLjMuM2wuNC4xYTEuMiAxLjIgMCAwIDAgMS0uNWwuMi0uNC4zLjQtLjEuM2ExLjcgMS43IDAgMCAxLS44LjdsLS43LjEtLjctLjEtLjUtLjUtLjItLjYuMS0uNi40LS41LjYtLjIuOC0uMWguOFptMi42LTF2My42aC0uN1YxNDJoLjd2LjdabTEuMy0uN3YuN2gtLjhhMSAxIDAgMCAwLS4zLjJsLS4yLjR2LjRsLS4zLjF2LS43bC4zLS42LjQtLjRhMS4xIDEuMSAwIDAgMSAxLS4xWiIvPjxnIGNsaXAtcGF0aD0idXJsKCNkKSI+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTM2LjQgMTVhMSAxIDAgMSAwIDIuMiAwIDEgMSAwIDAgMC0yLjIgMFptMS4xLjJoMTQ4di0uNGgtMTQ4di40Wk0zNi40IDQwLjhhMSAxIDAgMSAwIDIuMiAwIDEgMSAwIDAgMC0yLjIgMFptMS4xLjJoMTQ4di0uNWgtMTQ4di41Wk0zNi40IDY2LjVhMSAxIDAgMSAwIDIuMiAwIDEgMSAwIDAgMC0yLjIgMFptMS4xLjJoMTQ4di0uNGgtMTQ4di40Wk0zNi40IDkyLjNhMSAxIDAgMSAwIDIuMiAwIDEgMSAwIDAgMC0yLjIgMFptMS4xLjJoMTQ4VjkyaC0xNDh2LjVaTTM2LjQgMTE4YTEgMSAwIDEgMCAyLjIgMCAxIDEgMCAwIDAtMi4yIDBabTEuMS4yaDE0OHYtLjRoLTE0OHYuNFoiLz48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiIHN0cm9rZS1vcGFjaXR5PSIuNyIgc3Ryb2tlLXdpZHRoPSIuMyIgZD0iTTM3LjYgMTQyLjloMTQ3LjgiLz48ZyBjbGlwLXBhdGg9InVybCgjZSkiPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLW9wYWNpdHk9Ii41IiBzdHJva2Utd2lkdGg9Ii4zIiBkPSJNNTIuMiAxNDQuMnYtLjgiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii41IiBkPSJNNDIuNCAxNDQuNHY1LjhoLS43di00LjlsLTEuNC42di0uN2wyLS44aC4xWm00LjYgMHY1LjhoLS43di00LjlsLTEuNS42di0uN2wyLjEtLjhoLjFabTguOCAwdjUuOGgtLjdsLTIuOS00LjR2NC40aC0uOHYtNS43aC44bDIuOSA0LjR2LTQuNGguN1ptMS4xIDMuN3YtLjFjMC0uMyAwLS42LjItLjggMC0uMy4yLS41LjMtLjdsLjYtLjUuOC0uMi44LjJjLjMuMS41LjMuNi41bC40LjcuMi44YzAgLjQgMCAuNy0uMiAxIDAgLjItLjIuNC0uNC42YTEuOCAxLjggMCAwIDEtMS40LjZBMS44IDEuOCAwIDAgMSA1NyAxNDl2LS44Wm0uNy0uMS4xLjcuMi41LjQuMy41LjFoLjZsLjMtLjQuMy0uNVYxNDcuNGMwLS4yLS4yLS4zLS4zLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjkuNWwtLjIuNXYuNlptNS40IDEuNSAxLjEtMy42aC44bC0xLjYgNC4zSDYzdi0uN1ptLTEtMy42IDEuMiAzLjZ2LjdoLS40bC0xLjUtNC4zaC43WiIvPjwvZz48ZyBjbGlwLXBhdGg9InVybCgjZikiPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLW9wYWNpdHk9Ii41IiBzdHJva2Utd2lkdGg9Ii4zIiBkPSJNODEuOCAxNDIuNnYtLjgiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii41IiBkPSJNNzIgMTQ0Ljl2NS43aC0uN3YtNC44bC0xLjQuNXYtLjdsMi0uN2guMVptNiA1di43aC0zLjh2LS41bDEuOS0yLjEuNS0uN2MuMi0uMS4zLS4zLjMtLjR2LTFsLS40LS40YTEgMSAwIDAgMC0uNSAwaC0uNmExIDEgMCAwIDAtLjQuNWwtLjEuNkg3NGwuMi0xIC43LS41YTIgMiAwIDAgMSAxLS4zYy4zIDAgLjcgMCAxIC4ybC41LjUuMi44di41bC0uMy42LS40LjUtLjQuNC0xLjUgMS43SDc4Wm03LjQtNXY1LjdoLS43bC0yLjktNC40djQuNEg4MXYtNS43aC44bDIuOSA0LjR2LTQuNGguN1ptMS4xIDMuNmMwLS40IDAtLjcuMi0xbC4zLS42LjYtLjUuOC0uMS44LjEuNi41LjQuNy4yLjh2LjFjMCAuMyAwIC42LS4yLjkgMCAuMi0uMi41LS40LjZhMS44IDEuOCAwIDAgMS0xLjQuNyAxLjggMS44IDAgMCAxLTEuOC0xLjN2LS45Wm0uNyAwIC4xLjYuMi41Yy4xLjIuMy4zLjQuM2wuNS4yYy4yIDAgLjQgMCAuNi0uMmwuMy0uMy4zLS41VjE0Ny44YzAtLjItLjItLjMtLjMtLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0uOS41bC0uMi41di42Wm01LjQgMS40IDEuMS0zLjVoLjhsLTEuNiA0LjJoLS40di0uN1ptLTEtMy41IDEuMiAzLjZ2LjZoLS40bC0xLjUtNC4yaC43WiIvPjwvZz48ZyBjbGlwLXBhdGg9InVybCgjZykiPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLW9wYWNpdHk9Ii41IiBzdHJva2Utd2lkdGg9Ii4zIiBkPSJNMTExLjQgMTQyLjZ2LS44Ii8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTEwMS42IDE0NC45djUuN2gtLjd2LTQuOGwtMS40LjV2LS43bDItLjdoLjFabTMuMyAyLjVoLjVsLjYtLjEuNC0uNC4xLS41di0uNWwtLjQtLjRIMTA1YS45LjkgMCAwIDAtLjUuOWgtLjdhMS41IDEuNSAwIDAgMSAuOC0xLjQgMiAyIDAgMCAxIDEtLjJjLjMgMCAuNiAwIC44LjIuMy4xLjUuMy42LjUuMi4zLjMuNi4zIDFsLS4xLjQtLjQuNGMwIC4yLS4zLjMtLjUuNGwtLjguMWgtLjZ2LS40Wm0wIC42di0uNWguNmMuNCAwIC43IDAgMSAuMmwuNS4zLjMuNXYxLjJsLS41LjUtLjUuNGgtMS41bC0uNS0uM2ExLjQgMS40IDAgMCAxLS42LTEuMmguOHYuNWMuMi4xLjMuMy41LjNsLjUuMi42LS4xLjQtLjQuMS0uNi0uMS0uNmEuOS45IDAgMCAwLS41LS4zbC0uNi0uMWgtLjVabTEwLjEtMy4xdjUuN2gtLjdsLTIuOS00LjR2NC40aC0uOHYtNS43aC44bDIuOSA0LjR2LTQuNGguN1ptMS4xIDMuNmMwLS40IDAtLjcuMi0xbC4zLS42LjYtLjUuOC0uMS44LjEuNi41LjQuNy4yLjh2LjFjMCAuMyAwIC42LS4yLjkgMCAuMi0uMi41LS40LjZhMS44IDEuOCAwIDAgMS0xLjQuNyAxLjggMS44IDAgMCAxLTEuOC0xLjN2LS45Wm0uNyAwIC4xLjYuMi41Yy4xLjIuMy4zLjQuM2wuNS4yYy4yIDAgLjQgMCAuNi0uMmwuMy0uMy4zLS41VjE0Ny44YzAtLjItLjItLjMtLjMtLjVhMSAxIDAgMCAwLS45LS40IDEgMSAwIDAgMC0uOS40bC0uMi41di42Wm01LjQgMS40IDEuMS0zLjVoLjhsLTEuNiA0LjJoLS40di0uN1ptLTEtMy41IDEuMiAzLjZ2LjZoLS40bC0xLjUtNC4yaC43WiIvPjwvZz48ZyBjbGlwLXBhdGg9InVybCgjaCkiPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLW9wYWNpdHk9Ii41IiBzdHJva2Utd2lkdGg9Ii4zIiBkPSJNMTQxIDE0Mi42di0uOCIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Ik0xMzEuMiAxNDQuOXY1LjdoLS43di00LjhsLTEuNC41di0uN2wyLS43aC4xWm02IDMuOHYuNmgtNHYtLjVsMi41LTMuOWguNmwtLjYgMS4xLTEuNyAyLjdoMy4zWm0tLjctMy44djUuN2gtLjd2LTUuN2guN1ptOC4xIDB2NS43aC0uN2wtMi45LTQuNHY0LjRoLS44di01LjdoLjhsMi45IDQuNHYtNC40aC43Wm0xLjEgMy42YzAtLjQgMC0uNy4yLTFsLjMtLjYuNi0uNS44LS4xLjguMS42LjUuNC43LjIuOHYuMWMwIC4zIDAgLjYtLjIuOSAwIC4yLS4yLjUtLjQuNmExLjggMS44IDAgMCAxLTEuNC43IDEuOCAxLjggMCAwIDEtMS44LTEuM3YtLjlabS43IDAgLjEuNi4yLjVjLjEuMi4zLjMuNC4zbC41LjJjLjIgMCAuNCAwIC42LS4ybC4zLS4zLjMtLjVWMTQ3LjhjMC0uMi0uMi0uMy0uMy0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLS45LjVsLS4yLjV2LjZabTUuNCAxLjQgMS4xLTMuNWguOGwtMS42IDQuMmgtLjR2LS43Wm0tMS0zLjUgMS4yIDMuNnYuNmgtLjRsLTEuNS00LjJoLjdaIi8+PC9nPjxnIGNsaXAtcGF0aD0idXJsKCNpKSI+PHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0ic3F1YXJlIiBzdHJva2Utb3BhY2l0eT0iLjUiIHN0cm9rZS13aWR0aD0iLjMiIGQ9Ik0xNzAuNiAxNDIuNnYtLjgiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii41IiBkPSJNMTYwLjggMTQ0Ljl2NS43aC0uN3YtNC44bC0xLjQuNXYtLjdsMi0uN2guMVptMy4yIDMtLjYtLjIuMy0yLjhoMi45di43aC0yLjNsLS4yIDEuNS40LS4yaC41bC44LjFjLjIgMCAuMy4yLjUuNGwuMy42LjEuOHYuN2wtLjQuNi0uNi40YTIgMiAwIDAgMS0uOC4ybC0uNi0uMS0uNi0uMy0uMy0uNWEyIDIgMCAwIDEtLjItLjdoLjdsLjEuNS40LjRoMWwuMy0uMy4yLS40YTEuOCAxLjggMCAwIDAgMC0xYzAtLjIgMC0uMy0uMi0uNGExIDEgMCAwIDAtLjMtLjNoLTEuMWwtLjMuM1ptMTAuMi0zdjUuN2gtLjdsLTIuOS00LjR2NC40aC0uOHYtNS43aC44bDIuOSA0LjR2LTQuNGguN1ptMS4xIDMuNmMwLS40IDAtLjcuMi0xbC4zLS42LjYtLjUuOC0uMS44LjEuNi41LjQuNy4yLjh2LjFjMCAuMyAwIC42LS4yLjkgMCAuMi0uMi41LS40LjZhMS44IDEuOCAwIDAgMS0xLjQuNyAxLjggMS44IDAgMCAxLTEuOC0xLjN2LS45Wm0uNyAwIC4xLjYuMi41Yy4xLjIuMy4zLjQuM2wuNS4yYy4yIDAgLjQgMCAuNi0uMmwuMy0uMy4zLS41VjE0Ny44YzAtLjItLjItLjMtLjMtLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0uOS41bC0uMi41di42Wm01LjQgMS40IDEuMS0zLjVoLjhsLTEuNiA0LjJoLS40di0uN1ptLTEtMy41IDEuMiAzLjZ2LjZoLS40bC0xLjUtNC4yaC43WiIvPjwvZz48L2c+PGRlZnM+PGNsaXBQYXRoIGlkPSJhIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMzcuNSA2aDE0OHYxMzdoLTE0OHoiLz48L2NsaXBQYXRoPjxjbGlwUGF0aCBpZD0iZCI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTM2LjIgMTAuOGgxNTJ2MTQzaC0xNTJ6Ii8+PC9jbGlwUGF0aD48Y2xpcFBhdGggaWQ9ImUiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0zNy41IDE0Mi42aDI5LjZ2OEgzNy41eiIvPjwvY2xpcFBhdGg+PGNsaXBQYXRoIGlkPSJmIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNNjcuMSAxNDEuN2gyOS42djkuOUg2Ny4xeiIvPjwvY2xpcFBhdGg+PGNsaXBQYXRoIGlkPSJnIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNOTYuNyAxNDEuN2gyOS42djkuOUg5Ni43eiIvPjwvY2xpcFBhdGg+PGNsaXBQYXRoIGlkPSJoIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTI2LjMgMTQxLjdoMjkuNnY5LjloLTI5LjZ6Ii8+PC9jbGlwUGF0aD48Y2xpcFBhdGggaWQ9ImkiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0xNTUuOSAxNDEuN2gyOS42djkuOWgtMjkuNnoiLz48L2NsaXBQYXRoPjxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjEwOC4yIiB4Mj0iMTA4LjIiIHkxPSIxNTcuMyIgeTI9Ii0xOC41IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIuMiIgc3RvcC1jb2xvcj0iIzQzNkFERiIvPjxzdG9wIG9mZnNldD0iLjIiIHN0b3AtY29sb3I9IiM1M0JEMkQiLz48c3RvcCBvZmZzZXQ9Ii40IiBzdG9wLWNvbG9yPSIjNTNCRDJEIi8+PHN0b3Agb2Zmc2V0PSIuNCIgc3RvcC1jb2xvcj0iI0ZEOEYzQyIvPjxzdG9wIG9mZnNldD0iLjUiIHN0b3AtY29sb3I9IiNGRDhGM0MiLz48c3RvcCBvZmZzZXQ9Ii41IiBzdG9wLWNvbG9yPSIjRDMzRTU3Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImMiIHgxPSIxMDcuMSIgeDI9IjEwNy4xIiB5MT0iMTI3LjEiIHkyPSItMTguNSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iLjEiIHN0b3AtY29sb3I9IiMzMDVBRDciLz48c3RvcCBvZmZzZXQ9Ii4xIiBzdG9wLWNvbG9yPSIjM0ZBNzFBIi8+PHN0b3Agb2Zmc2V0PSIuMiIgc3RvcC1jb2xvcj0iIzNGQTcxQSIvPjxzdG9wIG9mZnNldD0iLjIiIHN0b3AtY29sb3I9IiNGMzY5MDAiLz48c3RvcCBvZmZzZXQ9Ii40IiBzdG9wLWNvbG9yPSIjRjM2OTAwIi8+PHN0b3Agb2Zmc2V0PSIuNCIgc3RvcC1jb2xvcj0iI0Q4MTgzOCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjwvc3ZnPg==", + "description": "Displays changes to fluid pressure data over time visualized with color ranges.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.rangeChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.rangeChartWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: false,\n defaultDataKeysFunction: function() {\n return [{ name: 'pressure', label: 'Pressure', type: 'timeseries' }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-range-chart-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-range-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 15 - 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":2592000000,\"interval\":86400000,\"fixedTimewindow\":{\"startTimeMs\":1700577227898,\"endTimeMs\":1700663627898},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"dataZoom\":true,\"rangeColors\":[{\"from\":0,\"to\":5,\"color\":\"#305AD7\"},{\"from\":5,\"to\":10,\"color\":\"#3FA71A\"},{\"from\":10,\"to\":15,\"color\":\"#F36900\"},{\"from\":15,\"to\":null,\"color\":\"#D81838\"}],\"outOfRangeColor\":\"#ccc\",\"fillArea\":true,\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm\",\"lastUpdateAgo\":false,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Pressure\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"bar\",\"decimals\":0,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "fluid pressure", + "liquid pressure", + "pump pressure", + "hydraulic", + "pump", + "compressibility", + "compressive stress", + "pipe", + "pipeline" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/fluid_pressure_range_chart_with_background.json b/application/src/main/data/json/system/widget_types/fluid_pressure_range_chart_with_background.json new file mode 100644 index 0000000000..171256b88b --- /dev/null +++ b/application/src/main/data/json/system/widget_types/fluid_pressure_range_chart_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "fluid_pressure_range_chart_with_background", + "name": "Pressure range chart with background", + "deprecated": false, + "image": "tb-image:cHJlc3N1cmVfcmFuZ2VfY2hhcnRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==:cHJlc3N1cmVfcmFuZ2VfY2hhcnRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48L2c+PGcgY2xpcC1wYXRoPSJ1cmwoI2MpIj48cGF0aCBmaWxsPSJ1cmwoI2QpIiBkPSJtNjguOSA0NS0yMi4yIDU1LjgtNy43IDE3VjEzN2gxNDlWODUuNGwtMTEgNDAtNi4yLTI0LjgtMTQgMzQuNmMtNy42LTI5LjctMTQtNDAuOC0yMi4yLTcwbC04LjUgNDMuNmgtMTFMMTEzIDM4IDEwOC43IDEwIDg4LjQgNzkuNmwtNC44LTIuMi0xNC43LTMyLjNaIi8+PHBhdGggc3Ryb2tlPSJ1cmwoI2UpIiBzdHJva2Utd2lkdGg9Ii45IiBkPSJtMzkgMTE4IDcuNy0xN0w2OSA0NWwxNC44IDMyLjUgNC44IDIuMkwxMDguOCAxMGw0LjYgMzUuMSAxLjggNjMuOWgxMS42bDcuNy00NCAyMi41IDcxIDE0LjEtMzUuOCA2LjIgMjUuOEwxODggODYiLz48L2c+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTE2LjIgMTguNHYuNmgtMy43di0uNWwxLjgtMiAuNi0uNy4yLS41LjEtLjUtLjEtLjUtLjMtLjNhMSAxIDAgMCAwLS42LS4yYy0uMiAwLS40IDAtLjYuMmExIDEgMCAwIDAtLjQuNGwtLjEuNmgtLjdjMC0uMyAwLS42LjItLjkuMS0uMy4zLS41LjYtLjZhMiAyIDAgMCAxIDEtLjNsMSAuMmMuMi4yLjQuMy41LjYuMi4yLjIuNC4yLjh2LjVsLS4zLjVhNy44IDcuOCAwIDAgMS0uOCAxbC0xLjUgMS42aDIuOVptMS44LTIuMS0uNi0uMi4zLTIuOGgzdi43aC0yLjRsLS4xIDEuNS4zLS4xLjYtLjEuNy4xLjUuNC40LjYuMS44LS4xLjdjMCAuMy0uMi41LS4zLjZsLS42LjRhMi4yIDIuMiAwIDAgMS0xLjQgMGMtLjMgMC0uNCAwLS42LS4ybC0uNC0uNWEyIDIgMCAwIDEtLjItLjdoLjdsLjIuNWMwIC4yLjIuMy40LjRoMWwuMy0uMy4yLS40YTEuOCAxLjggMCAwIDAgMC0xbC0uMi0uNGExIDEgMCAwIDAtLjQtLjNoLTFsLS40LjNabTMuOC0zLjNoLjd2NmgtLjd2LTZabTMuNiAzLjktLjEuOS0uNC43LS41LjQtLjcuMmMtLjIgMC0uNSAwLS43LS4yLS4yIDAtLjQtLjItLjUtLjRhMiAyIDAgMCAxLS4zLS42IDQgNCAwIDAgMS0uMi0uOHYtLjRjMC0uMyAwLS42LjItLjggMC0uMy4yLS41LjMtLjdsLjUtLjQuNy0uMWMuMyAwIC41IDAgLjcuMi4yIDAgLjQuMi41LjRsLjQuN3YuOVptLS44IDB2LS42bC0uMi0uNWExIDEgMCAwIDAtLjktLjVsLS40LjFhMSAxIDAgMCAwLS40LjNsLS4yLjMtLjEuNHYxYzAgLjEgMCAuMy4yLjUgMCAuMi4yLjMuMy40bC42LjJjLjIgMCAuNCAwIC41LS4yLjIgMCAuMy0uMS40LS4zbC4yLS41VjE3Wm00LjMgMS40VjE2bC0uMS0uNGMwLS4yLS4yLS4zLS4zLS4zYTEgMSAwIDAgMC0uNS0uMWgtLjVsLS4zLjMtLjEuM2gtLjd2LS40bC40LS40LjUtLjMuNy0uMWMuMyAwIC42IDAgLjkuMi4yIDAgLjQuMi41LjRsLjIuOHYyLjRsLjIuNHYuMUgyOXYtLjNsLS4xLS40Wm0uMS0xLjl2LjVoLS43bC0uNi4xLS40LjEtLjIuMy0uMS4zdi40bC40LjNoLjRhMS4yIDEuMiAwIDAgMCAxLS40bC4yLS40LjMuMy0uMi40YTEuNyAxLjcgMCAwIDEtLjguN0gyN2wtLjUtLjUtLjEtLjcuMS0uNi40LS40LjYtLjNIMjlabTIuNi0xVjE5aC0uN3YtNC4yaC43di42Wm0xLjMtLjd2LjdoLS44YTEgMSAwIDAgMC0uMy4zbC0uMi4zLS4xLjQtLjIuMnYtLjhsLjMtLjYuNC0uNGExLjEgMS4xIDAgMCAxIDEgMFpNMTYuMiA0My4xdi42aC0zLjd2LS41bDEuOC0yLjEuNi0uNi4yLS41LjEtLjUtLjEtLjUtLjMtLjNhMSAxIDAgMCAwLS42LS4yYy0uMiAwLS40IDAtLjYuMmExIDEgMCAwIDAtLjQuNGwtLjEuNmgtLjdjMC0uMyAwLS42LjItLjkuMS0uMy4zLS41LjYtLjZhMiAyIDAgMCAxIDEtLjNjLjQgMCAuNyAwIDEgLjJsLjUuNWMuMi4zLjIuNS4yLjh2LjZsLS4zLjVhNy44IDcuOCAwIDAgMS0uOCAxTDEzLjQgNDNoMi45Wm00LjUtMi43di45YzAgLjQgMCAuOC0uMiAxLjEgMCAuNC0uMi42LS4zLjgtLjIuMi0uNC40LS42LjRhMiAyIDAgMCAxLS43LjJsLS42LS4xYy0uMiAwLS40LS4yLS41LS4zbC0uNC0uNC0uMy0uOHYtM2wuNS0uN2MuMS0uMi4zLS40LjUtLjRsLjgtLjIuNi4xYTEuNCAxLjQgMCAwIDEgLjguN2wuMy43djFabS0uOCAxdi0xLjhsLS4xLS41YTEgMSAwIDAgMC0uMy0uM2MwLS4xLS4xLS4yLS4zLS4yYTEgMSAwIDAgMC0uMyAwIDEgMSAwIDAgMC0uNSAwbC0uMy4zLS4yLjYtLjEuOFY0MmwuMi41LjIuMy4zLjJoLjhjLjIgMCAuMy0uMi40LS4zbC4yLS42di0uOFptMS45LTMuN2guN3Y2aC0uN3YtNlptMy42IDMuOHYuMWwtLjEuOS0uNC43LS41LjQtLjcuMmMtLjIgMC0uNSAwLS43LS4yLS4yIDAtLjQtLjItLjUtLjRhMiAyIDAgMCAxLS4zLS42IDQgNCAwIDAgMS0uMi0uOHYtLjRjMC0uMyAwLS42LjItLjggMC0uMy4yLS41LjMtLjdsLjUtLjQuNy0uMS43LjEuNS41LjQuN3YuOFptLS44LjFWNDFsLS4yLS41YTEgMSAwIDAgMC0uOS0uNWgtLjRhMSAxIDAgMCAwLS40LjNsLS4yLjQtLjEuNHYxYzAgLjEgMCAuMy4yLjUgMCAuMi4yLjMuMy40bC42LjJjLjIgMCAuNCAwIC41LS4yLjIgMCAuMy0uMi40LS4zbC4yLS41di0uNlpNMjkgNDN2LTIuMmwtLjEtLjRjMC0uMi0uMi0uMy0uMy0uM2ExIDEgMCAwIDAtLjUtLjFoLS41bC0uMy4zLS4xLjNoLS43di0uNGwuNC0uNC41LS4zLjctLjEuOS4xLjUuNS4yLjh2Mi40bC4yLjRIMjl2LS4ybC0uMS0uNFpNMjkgNDF2LjVIMjcuN2wtLjQuMi0uMi4zLS4xLjN2LjRsLjQuMi40LjFhMS4yIDEuMiAwIDAgMCAxLS40bC4yLS40LjMuMy0uMi40YTEuNyAxLjcgMCAwIDEtLjguN2gtLjZsLS44LS4xLS41LS40LS4xLS43LjEtLjZjMC0uMi4yLS4zLjQtLjRsLjYtLjNIMjlabTIuNi0xdjMuNmgtLjd2LTQuMmguN3YuNlptMS4zLS43di43aC0uOGExIDEgMCAwIDAtLjMuM2wtLjIuMy0uMS40LS4yLjJ2LS44bC4zLS42LjQtLjRhMS4xIDEuMSAwIDAgMSAxIDBaTTE0LjggNjIuN3Y1LjdoLS43di00LjhsLTEuNC41di0uN2wyLS43aC4xWm0zLjIgMy0uNi0uMi4zLTIuOGgzdi43aC0yLjRsLS4xIDEuNS4zLS4yaC42bC43LjFjLjIgMCAuNC4yLjUuNGwuNC42LjEuOC0uMS43YzAgLjMtLjIuNS0uMy42bC0uNi40YTIuMiAyLjIgMCAwIDEtMS40IDBjLS4zIDAtLjQgMC0uNi0uMmwtLjQtLjVhMiAyIDAgMCAxLS4yLS43aC43bC4yLjVjMCAuMi4yLjMuNC40aDFsLjMtLjMuMi0uNGExLjggMS44IDAgMCAwIDAtMWwtLjItLjRhMSAxIDAgMCAwLS40LS4zaC0xbC0uNC4zWm0zLjgtMy4zaC43djZoLS43di02Wm0zLjYgMy44di4xbC0uMS45LS40LjctLjUuNC0uNy4yYy0uMiAwLS41IDAtLjctLjItLjIgMC0uNC0uMi0uNS0uNGEyIDIgMCAwIDEtLjMtLjYgNCA0IDAgMCAxLS4yLS44VjY2YzAtLjMgMC0uNi4yLS44IDAtLjMuMi0uNS4zLS43bC41LS40LjctLjEuNy4xLjUuNS40Ljd2LjhabS0uOC4xdi0uN2wtLjItLjRhMSAxIDAgMCAwLS45LS41aC0uNGExIDEgMCAwIDAtLjQuM2wtLjIuNC0uMS40djFjMCAuMSAwIC4zLjIuNWwuMy40LjYuMmMuMiAwIC40IDAgLjUtLjIuMiAwIC4zLS4yLjQtLjNsLjItLjV2LS42Wm00LjMgMS40di0yLjJsLS4xLS40YzAtLjItLjItLjMtLjMtLjNhMSAxIDAgMCAwLS41LS4xaC0uNWwtLjMuMy0uMS4zaC0uN1Y2NWwuNC0uNC41LS4zLjctLjEuOS4xLjUuNS4yLjh2Mi40bC4yLjRIMjl2LS4ybC0uMS0uNFptLjEtMS45di41SDI3LjdsLS40LjItLjIuMy0uMS4zdi40bC40LjIuNC4xYTEuMiAxLjIgMCAwIDAgMS0uNWwuMi0uMy4zLjMtLjIuNGExLjcgMS43IDAgMCAxLS44LjZsLS42LjJjLS4zIDAtLjUgMC0uOC0uMmwtLjUtLjUtLjEtLjYuMS0uNmMwLS4yLjItLjMuNC0uNGwuNi0uM0gyOVptMi42LTF2My42aC0uN3YtNC4yaC43di42Wm0xLjMtLjd2LjdoLS44YTEgMSAwIDAgMC0uMy4zbC0uMi4zLS4xLjQtLjIuMXYtLjdsLjMtLjYuNC0uNGExLjEgMS4xIDAgMCAxIDEgMFpNMTQuOCA4Ny40VjkzaC0uN3YtNC44bC0xLjQuNVY4OGwyLS43aC4xWm01LjkgMi40di44YzAgLjUgMCAxLS4yIDEuMiAwIC4zLS4yLjYtLjMuOGwtLjYuNGEyIDIgMCAwIDEtLjcuMmwtLjYtLjFjLS4yIDAtLjQtLjItLjUtLjNsLS40LS41LS4zLS43di0zYy4yLS4zLjMtLjYuNS0uN2wuNS0uNWgxLjRhMS40IDEuNCAwIDAgMSAuOC43bC4zLjd2MVptLS44IDFWODlsLS4xLS41YTEgMSAwIDAgMC0uMy0uM2MwLS4xLS4xLS4yLS4zLS4yYTEgMSAwIDAgMC0uMy0uMSAxIDEgMCAwIDAtLjUuMWwtLjMuMy0uMi41LS4xLjl2MS43bC4yLjUuMi40LjMuMmguOGwuNC0uNC4yLS41di0uOFptMS45LTMuN2guN3Y2aC0uN3YtNlptMy42IDMuOHYuMWwtLjEuOWMtLjEuMi0uMi41LS40LjZsLS41LjUtLjcuMmMtLjIgMC0uNSAwLS43LS4yLS4yIDAtLjQtLjItLjUtLjRhMiAyIDAgMCAxLS4zLS42IDQgNCAwIDAgMS0uMi0uOXYtLjNjMC0uMyAwLS42LjItLjlsLjMtLjYuNS0uNC43LS4xLjcuMS41LjUuNC43di44Wm0tLjguMXYtLjdsLS4yLS41YTEgMSAwIDAgMC0uOS0uNGgtLjRhMSAxIDAgMCAwLS40LjNsLS4yLjQtLjEuNHYxYzAgLjEgMCAuMy4yLjVsLjMuNC42LjFoLjVsLjQtLjQuMi0uNVY5MVptNC4zIDEuNHYtMi4ybC0uMS0uNS0uMy0uMmExIDEgMCAwIDAtLjUtLjFoLS41bC0uMy4zLS4xLjNoLS43di0uNGwuNC0uNC41LS4zLjctLjEuOS4xLjUuNS4yLjh2Mi40bC4yLjRIMjl2LS4zbC0uMS0uM1ptLjEtMS45di41SDI3LjdsLS40LjItLjIuMy0uMS4zdi40bC40LjIuNC4xYTEuMiAxLjIgMCAwIDAgMS0uNWwuMi0uNC4zLjQtLjIuNGExLjcgMS43IDAgMCAxLS44LjZsLS42LjItLjgtLjItLjUtLjUtLjEtLjYuMS0uNmMwLS4yLjItLjMuNC0uNGwuNi0uM0gyOVptMi42LTF2My42aC0uN3YtNC4yaC43di42Wm0xLjMtLjd2LjdoLS44YTEgMSAwIDAgMC0uMy4zbC0uMi4zLS4xLjQtLjIuMVY5MGwuMy0uNi40LS40YTEuMSAxLjEgMCAwIDEgMSAwWk0xOC40IDExNWgtLjZsLjMtM2gzdi44aC0yLjRsLS4yIDEuNS40LS4ySDIwLjJsLjUuNWMuMi4xLjMuMy40LjZ2MS41bC0uNC42LS41LjRhMi4yIDIuMiAwIDAgMS0xLjQgMGwtLjYtLjItLjQtLjVhMiAyIDAgMCAxLS4yLS43aC43YzAgLjIgMCAuNC4yLjUgMCAuMi4yLjMuMy4zbC42LjIuNC0uMWMuMiAwIC4zLS4yLjQtLjNsLjItLjRhMS44IDEuOCAwIDAgMCAwLTFsLS4yLS40YTEgMSAwIDAgMC0uNC0uM2wtLjUtLjEtLjUuMWEyIDIgMCAwIDAtLjQuM1ptMy44LTMuMmguN3Y2aC0uN3YtNlptMy41IDMuOHYxbC0uNC42LS41LjUtLjcuMWgtLjdsLS41LS41YTIgMiAwIDAgMS0uMy0uNiA0IDQgMCAwIDEtLjItLjl2LS4zYzAtLjMgMC0uNi4yLS45bC4zLS42LjUtLjQuNy0uMS43LjEuNS41LjMuNi4xIDFabS0uNy4xdi0uN2wtLjItLjVhMSAxIDAgMCAwLS45LS40aC0uNWExIDEgMCAwIDAtLjMuM2wtLjIuNC0uMi40djFsLjIuNS40LjQuNi4xaC41bC40LS40LjItLjV2LS42Wm00LjMgMS4zVjExNWwtLjEtLjUtLjMtLjNhMSAxIDAgMCAwLS41IDBoLS41bC0uMy4zLS4xLjNoLS44YzAtLjEgMC0uMy4yLS40IDAtLjIuMi0uMy4zLS40LjEtLjIuMy0uMy41LS4zbC43LS4xLjguMWMuMy4xLjUuMy42LjVsLjIuOHYyLjRsLjEuNGgtLjdsLS4xLS4zdi0uNFptLjEtMS44di41SDI4LjFsLS40LjItLjIuMy0uMS4zdi40bC4zLjIuNS4xYTEuMiAxLjIgMCAwIDAgMS0uNWwuMS0uNC4zLjQtLjEuM2ExLjcgMS43IDAgMCAxLS44LjdsLS42LjEtLjgtLjEtLjUtLjUtLjEtLjZ2LS42bC41LS40Yy4xLS4yLjMtLjIuNi0uM2gxLjZabTIuNi0xdjMuNmgtLjh2LTQuM2guOHYuN1ptMS4zLS43di43aC0uOGExIDEgMCAwIDAtLjMuM2wtLjIuMy0uMS40LS4yLjF2LS43bC4zLS42YzAtLjIuMi0uMy40LS40YTEuMSAxLjEgMCAwIDEgLjkgMFpNMjEgMTM5LjJ2MmwtLjQuOC0uNi40YTIgMiAwIDAgMS0uOC4xaC0uNmwtLjUtLjMtLjMtLjUtLjMtLjd2LTNsLjQtLjguNi0uNC43LS4xaC43YTEuNCAxLjQgMCAwIDEgLjguOGwuMy43djFabS0uNyAxVjEzOC40bC0uMi0uNWExIDEgMCAwIDAtLjItLjRsLS4zLS4yYTEgMSAwIDAgMC0uNCAwIDEgMSAwIDAgMC0uNCAwbC0uNC40LS4yLjV2Mi42bC4yLjVjMCAuMiAwIC4zLjIuNGwuMy4yaC44Yy4xLS4xLjMtLjIuMy0uNGwuMy0uNXYtLjhabTEuOS0zLjdoLjd2NmgtLjd2LTZabTMuNSAzLjh2MWwtLjQuNi0uNS41LS43LjFoLS43bC0uNS0uNWEyIDIgMCAwIDEtLjMtLjYgNCA0IDAgMCAxLS4yLS45di0uM2MwLS4zIDAtLjYuMi0uOWwuMy0uNi41LS40LjctLjEuNy4xLjUuNWMuMi4xLjMuNC4zLjZsLjEgMVptLS43LjF2LS43bC0uMi0uNWExIDEgMCAwIDAtLjktLjRoLS41YTEgMSAwIDAgMC0uMy4zbC0uMi40LS4yLjR2MWwuMi41LjQuNC42LjFoLjVsLjQtLjQuMi0uNXYtLjZabTQuMyAxLjN2LTIuMWwtLjEtLjUtLjMtLjNhMSAxIDAgMCAwLS41IDBoLS41bC0uMy4zLS4xLjNoLS44YzAtLjEgMC0uMy4yLS40IDAtLjIuMi0uMy4zLS40LjEtLjIuMy0uMy41LS4zbC43LS4xLjguMWMuMy4xLjUuMy42LjVsLjIuOHYyLjRsLjEuNGgtLjdsLS4xLS4zdi0uNFptLjEtMS44di41SDI4LjFsLS40LjItLjIuMy0uMS4zdi40bC4zLjIuNS4xYTEuMiAxLjIgMCAwIDAgMS0uNWwuMS0uNC4zLjQtLjEuM2ExLjcgMS43IDAgMCAxLS44LjdsLS42LjEtLjgtLjEtLjUtLjUtLjEtLjZ2LS42bC41LS40Yy4xLS4yLjMtLjMuNi0uM2wuOC0uMWguOFptMi42LTF2My42aC0uOHYtNC4zaC44di43Wm0xLjMtLjd2LjdoLS44YTEgMSAwIDAgMC0uMy4zbC0uMi4zLS4xLjQtLjIuMXYtLjdsLjMtLjZjMC0uMi4yLS4zLjQtLjRhMS4xIDEuMSAwIDAgMSAuOSAwWiIvPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii41IiBzdHJva2Utd2lkdGg9Ii40IiBkPSJNMzkgMTdoMTQ5TTM5IDQyLjhoMTQ5TTM5IDY4LjVoMTQ5TTM5IDk0LjNoMTQ5TTM5IDEyMGgxNDkiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii41IiBkPSJNNDIuOCAxNDEuM3Y1LjdoLS43di00LjhsLTEuNS41di0uNmwyLjEtLjhoLjFabTQuNSAwdjUuN2gtLjd2LTQuOGwtMS41LjV2LS42bDIuMS0uOGguMVptOC43IDB2NS43aC0uOGwtMi44LTQuNHY0LjRoLS44di01LjdoLjhsMi44IDQuNHYtNC40aC44Wm0xIDMuNmMwLS40IDAtLjYuMi0uOSAwLS4zLjItLjUuMy0uN2wuNi0uNGMuMy0uMi41LS4yLjgtLjIuMyAwIC42IDAgLjguMi4zIDAgLjUuMi42LjRsLjQuNy4yLjh2LjFjMCAuMyAwIC42LS4yLjlsLS40LjdhMS44IDEuOCAwIDAgMS0xLjQuNiAxLjggMS44IDAgMCAxLTEuOC0xLjN2LS45Wm0uNyAwIC4xLjYuMi41LjQuNGgxLjFsLjMtLjQuMy0uNXYtMS4yYzAtLjItLjItLjQtLjMtLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0uNS4xbC0uNC40LS4yLjV2LjVabTUuMyAxLjQgMS4yLTMuNWguN2wtMS41IDQuMmgtLjVsLjEtLjdabS0xLTMuNSAxLjIgMy42LjEuNmgtLjVsLTEuNS00LjJoLjdaTTczIDE0MS4zdjUuN2gtLjd2LTQuOGwtMS40LjV2LS42bDItLjhoLjJabTYgNS4xdi42aC0zLjh2LS41bDEuOS0yIC41LS43LjMtLjV2LTFsLS40LS4zYTEgMSAwIDAgMC0uNi0uMmMtLjIgMC0uNCAwLS42LjJhMSAxIDAgMCAwLS40LjRsLS4xLjZoLS43YzAtLjMgMC0uNi4yLS45LjEtLjMuMy0uNS42LS42YTIgMiAwIDAgMSAxLS4zbDEgLjJjLjIuMi40LjMuNS42LjIuMi4zLjQuMy44bC0uMS41LS4zLjVhNy44IDcuOCAwIDAgMS0uOCAxbC0xLjQgMS42aDIuOFptNy4yLTV2NS42aC0uN2wtMi45LTQuNHY0LjRIODJ2LTUuN2guN2wyLjkgNC40di00LjRoLjdabTEgMy41LjItLjljMC0uMy4yLS41LjQtLjdsLjYtLjRjLjItLjIuNS0uMi44LS4yLjMgMCAuNiAwIC44LjIuMiAwIC40LjIuNi40bC40LjcuMS44di4xbC0uMS45LS40LjdhMS44IDEuOCAwIDAgMS0xLjQuNiAxLjggMS44IDAgMCAxLTEuOC0xLjNsLS4xLS45Wm0uOCAwdi42bC4zLjVjMCAuMi4yLjMuNC40aDFsLjQtLjQuMi0uNXYtMS4ybC0uMi0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLS41LjFsLS40LjQtLjIuNS0uMS41Wm01LjIgMS40IDEuMi0zLjVoLjdsLTEuNSA0LjJoLS41bC4xLS43Wm0tMS0zLjUgMS4zIDMuNnYuNmgtLjRsLTEuNi00LjJoLjhaTTEwMy4zIDE0MS4zdjUuN2gtLjd2LTQuOGwtMS41LjV2LS42bDIuMS0uOGguMVptMy4yIDIuNWguNWwuNi0uMS40LS40LjEtLjV2LS41bC0uNC0uNGgtMS4xYS45LjkgMCAwIDAtLjUuOWgtLjdjMC0uMyAwLS42LjItLjhsLjYtLjZhMiAyIDAgMCAxIDEtLjJsLjguMmMuMy4xLjUuMy42LjYuMi4yLjMuNS4zLjhsLS4xLjUtLjQuNS0uNS4zLS44LjFoLS42di0uNFptMCAuNnYtLjRoMS42bC41LjQuMy41djEuMmwtLjUuNS0uNS40aC0xLjVsLS41LS4zYTEuNCAxLjQgMCAwIDEtLjYtMS4yaC44di41bC41LjRoMS4xbC40LS40LjEtLjYtLjEtLjZhLjkuOSAwIDAgMC0uNS0uM2wtLjYtLjFoLS41Wm0xMC0zdjUuNmgtLjhsLTIuOC00LjR2NC40aC0uOHYtNS43aC44bDIuOCA0LjR2LTQuNGguOFptMSAzLjVjMC0uNCAwLS42LjItLjkgMC0uMy4yLS41LjMtLjdsLjYtLjRjLjMtLjIuNS0uMi44LS4yLjMgMCAuNiAwIC44LjIuMyAwIC41LjIuNi40bC40LjcuMi44di4xYzAgLjMgMCAuNi0uMi45bC0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zdi0uOVptLjcgMCAuMS42LjIuNS40LjRoMS4xbC4zLS40LjMtLjV2LTEuMmMwLS4yLS4yLS40LS4zLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41di41Wm01LjMgMS40IDEuMi0zLjVoLjdsLTEuNSA0LjJoLS41bC4xLS43Wm0tMS0zLjUgMS4yIDMuNi4xLjZoLS41bC0xLjUtNC4yaC43Wk0xMzMuNiAxNDEuM3Y1LjdoLS44di00LjhsLTEuNC41di0uNmwyLS44aC4yWm02IDMuOHYuNmgtNC4ydi0uNGwyLjYtNGguNmwtLjcgMS4yLTEuNyAyLjZoMy4zWm0tLjktMy44djUuN2gtLjd2LTUuN2guN1ptOCAwdjUuN2gtLjdsLTIuOS00LjR2NC40aC0uN3YtNS43aC43bDIuOSA0LjR2LTQuNGguN1ptMSAzLjYuMi0uOWMwLS4zLjItLjUuNC0uN2wuNi0uNGMuMi0uMi41LS4yLjgtLjIuMyAwIC42IDAgLjguMi4yIDAgLjQuMi42LjRsLjQuNy4xLjh2LjFsLS4xLjktLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM2wtLjEtLjlabS44IDB2LjZsLjMuNWMwIC4yLjIuMy40LjRoMWwuNC0uNC4yLS41di0xLjJsLS4yLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41LS4xLjVabTUuMiAxLjQgMS4yLTMuNWguN2wtMS41IDQuMmgtLjVsLjEtLjdabS0xLTMuNSAxLjMgMy42di42aC0uNGwtMS42LTQuMmguOFpNMTYzLjggMTQxLjN2NS43aC0uN3YtNC44bC0xLjUuNXYtLjZsMi4xLS44aC4xWm0zIDMtLjUtLjIuMy0yLjhoMi45di43aC0yLjNsLS4yIDEuNS40LS4xLjUtLjEuOC4xLjUuNC4zLjZhMi41IDIuNSAwIDAgMSAwIDEuNWMwIC4zLS4yLjUtLjMuNi0uMS4yLS4zLjMtLjYuNGEyIDIgMCAwIDEtLjguMmwtLjYtLjEtLjYtLjMtLjMtLjVhMiAyIDAgMCAxLS4yLS43aC43bC4xLjUuNC40aDFsLjMtLjMuMi0uNGExLjggMS44IDAgMCAwIDAtMWMwLS4yIDAtLjMtLjItLjRhMSAxIDAgMCAwLS4zLS4zaC0xLjFsLS4zLjNabTEwLjItM3Y1LjdoLS44bC0yLjgtNC40djQuNGgtLjh2LTUuN2guOGwyLjggNC40di00LjRoLjhabTEgMy42YzAtLjQgMC0uNi4yLS45IDAtLjMuMi0uNS4zLS43bC42LS40Yy4zLS4yLjUtLjIuOC0uMi4zIDAgLjYgMCAuOC4yLjMgMCAuNS4yLjYuNGwuNC43LjIuOHYuMWMwIC4zIDAgLjYtLjIuOWwtLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM3YtLjlabS43IDAgLjEuNi4yLjUuNC40aDEuMWwuMy0uNC4zLS41di0xLjJjMC0uMi0uMi0uNC0uMy0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLS41LjFsLS40LjQtLjIuNXYuNVptNS4zIDEuNCAxLjItMy41aC43bC0xLjUgNC4yaC0uNWwuMS0uN1ptLTEtMy41IDEuMiAzLjYuMS42aC0uNWwtMS41LTQuMmguN1oiLz48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImQiIHgxPSIxMDkuOSIgeDI9IjEwOS45IiB5MT0iMTUxLjYiIHkyPSItMjMuMyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iLjIiIHN0b3AtY29sb3I9IiM0MzZBREYiLz48c3RvcCBvZmZzZXQ9Ii4yIiBzdG9wLWNvbG9yPSIjNTNCRDJFIi8+PHN0b3Agb2Zmc2V0PSIuMyIgc3RvcC1jb2xvcj0iIzUzQkQyRSIvPjxzdG9wIG9mZnNldD0iLjMiIHN0b3AtY29sb3I9IiNGQzhGM0MiLz48c3RvcCBvZmZzZXQ9Ii41IiBzdG9wLWNvbG9yPSIjRkM4RjNDIi8+PHN0b3Agb2Zmc2V0PSIuNSIgc3RvcC1jb2xvcj0iI0QzM0U1NyIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJlIiB4MT0iMTA5LjUiIHgyPSIxMDguNyIgeTE9IjEzNC43IiB5Mj0iLTIzLjQiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9Ii4xIiBzdG9wLWNvbG9yPSIjMzA1QUQ3Ii8+PHN0b3Agb2Zmc2V0PSIuMSIgc3RvcC1jb2xvcj0iIzNGQTcxQSIvPjxzdG9wIG9mZnNldD0iLjMiIHN0b3AtY29sb3I9IiMzRkE3MUEiLz48c3RvcCBvZmZzZXQ9Ii4zIiBzdG9wLWNvbG9yPSIjRjM2OTAwIi8+PHN0b3Agb2Zmc2V0PSIuNCIgc3RvcC1jb2xvcj0iI0YzNjkwMCIvPjxzdG9wIG9mZnNldD0iLjQiIHN0b3AtY29sb3I9IiNEODE4MzgiLz48L2xpbmVhckdyYWRpZW50PjxjbGlwUGF0aCBpZD0iYyI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTM5IDExaDE0OXYxMjZIMzl6Ii8+PC9jbGlwUGF0aD48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2YiIHRyYW5zZm9ybT0ibWF0cml4KC4wMDUwMzE0NSAwIDAgLjAwNjI4OTMxIDAgMCkiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIHg9IjAiIHk9IjAiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjMiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA3N180Njk0NiIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA3N180Njk0NiIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFNQ0FnSUNBZ01DQWdJREF3TURCQVlFQkFRRUJBZ0dCZ1VHQ1FnS0Nna0lDUWtLREE4TUNnc09Dd2tKRFJFTkRnOFFFQkVRQ2d3U0V4SVFFdzhRRUJELzJ3QkRBUU1EQXdRREJBZ0VCQWdRQ3drTEVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkQvd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhRQUFBZ01CQVFFQkFRQUFBQUFBQUFBQUJnY0VCUWdEQWdFSkFQL0VBQnNCQUFJREFRRUJBQUFBQUFBQUFBQUFBQUlEQUFFRUJRWUgvOW9BREFNQkFBSVFBeEFBQUFCNzZnelpuSmpGTk43Rjk1TzBucVR4Sld5VVZYUUxMbklRc0FodVcwbnFUblk5S3Rlb2JoYm5QMlAwYzZWeU1mTzBGZWx3dm0xTXJYemFTUko4N1p0M3M0U081SWs5VklseWprb3dJZVdVZWl1REFsWUpKZFRwUHNuT1JEOC9YbGJNV29OK1lDUWVPaDBvU0dTeGY2V055bXIxSnpuYk5FOVBLeUdqS3FmMGtDNE8xQnhiSUlYN2s2M0xVeElqQy9rbGpJaFJDWXRLK0ZacXlFYmFRU1dZZ3o3TktMTnJQNXpOMXBEc2ozSHNXZnVDL3FScWxOSU5DVllOL3dCTDlYZnFUcEJ0akcvS3JtcFVTTEZMS0VsTGJEcGZPOU5wZFlkNVhmRGtkZDM2L0h2TFlBK2cycHVSSnVFa25nWURJYjNhTUtxOHkrZ25GQzdGb2ZicTFNWjhvWlEzMjBLNGw1MzVHM1ZYVnlLYlB2eHBpOWZWajFObTd2bUUxTGw5bWMyTnlHVnJSMXVvaW1Lam5iT3Iwc0hZanpVQ2NqeHhMR2Z1eTJiQjRTd0RGb25nWi8wOFVlQW1PZnAwRHZRdWthTVY4bjJZcGZZMU4xUGx6aGpNNTRkQ3pTeGc2OCtzZHlSTkRWL2gwSTFKT2pkbWRHdEs5enRwRVBzakJvYk02UXlOQWM3bkkwVFhibXZucVFmT2ZxZnBJd3Z6K2prSUdmMFYrcGVySFowU2d3NlZSVm1yS3RXb0lTdGdsTVRZWE1lVzhkMmVPeFZnVmNVYVdTNUtJeXRUNldhYjFKU3VaaElEYnA2dEg5TEorUjNPMUpBbHpDSDlnb1diK2Jzc3dZOWV4elo3bEVaMzhrcUJxd1M0UUN6aldnaHVjNms2eEV3WW1zR2hzUEFmRzdwZytKQk5KbEptbG9qQTZXZkxuNjU2bFo0NTJzT2sxTjFNUEs2N2lVNHh1N3YrcVZxbVFXanlFUlJabmpndGJwQzh6WTk5cUsyWGRGV2NrazhHVUtxc2ljdjhoY1RRWm9mcVhhcEtOYThBOWg5Zm4zVndiQXdnYWI3UkQxdFYySjduMzVyV1ZFb3V0MTlxWWg1Ry9XL1F5M3pSUm1kck1NU3BxdzlaVHJuNWJaVHpxd05mNGFMRTlBeHU5WGRibjJHbEpCZGNxdmpMeXh5ZDREbWR0dnM4NjgwS21TcTRTeTVoMDNKVXdMcTFNYmk3bEZRL1YxYWlVT1IrRjFtbU4yTGZIUDBVaWRHbTlxeDBiTmR1Um82VTE2bUtqSG9BOFdtS0pBV3pHNm9mMHE5TEpJak5jOUhMZUdDZHdhbnR0ejJKVjhFc3I4L1RrZk84VllDWTM0ZjFkVXp4bTBXR3pPVXNESVBMMlhqbDNxMlE3bWwramtnQVZBcGlqeU9xZ0ovOURKWmxaWTlJd041OTVPOXM2ODVqcFNNaGVLdWZzaVhBMXF5RlROcTdjZzVuY3NGV1NtSE1UQlFOOE9CSHBObU5WOXE0d3NDQVo3cUd4WEpOSE9WUVZkMVpVSzdyU0dxcXhVcXFyb3dDNDB2ZmZiNTZpeFBFUW1sZWpuamdRZ2hsT2xpS3pzOVM0NDNMa0s3bk82cHFLOEtyUzZXZ3hyblJLeGQyY2dBWEtTcVdRaXNsc3N0VGRURW9jYno3Ym4wYnNSemtBa21yTU9sTm9aT0VwMUYvU29aamJYSWFqN1NlYXRWRlQ1TWVWMFBvWkVrREtuUzcvcFV1NjFIMDhhQ3lPMHJ2eis1SVlGQkVrSGxiVmdUYU92TkY5a1VnUzhNWHZyVlBNWjExUkJlYU1HbDc2a3N6U3VFTWhVVk1zeGxCalNUYXZSeXAzTXh6bVVrcSt5UXhJWXRTL3dBenJsVHJXN2dEWTB4QkcwV082cnRnVHlydEpuTEV4cTZBYU9sWHk1V0RZMGh3dWtxVVkxZWpqUTJEUnNEcTVQZDE5a3E2dk4vTDF5SHFidXBkV0JlNUZUaWQyb25kMU1kaEtsUzVFckpQSjJ0elVwdzdNL0dTb2wxOVFZU3dVU2YveEFBc0VBQUJCQUlCQkFJQkF3VUJBUUFBQUFBRUFRSURCUUFHRVFjU0V4UVZJU0lXSXpFUUpDVXlRVE5ELzlvQUNBRUJBQUVJQWRDY3lTbGlZblcydW5Fbkh1NE9tVzdOdFFZNFh4eWVSdk9jWTFtZHVQUk1sVkV5WjJTeXFtUlRxcTRQTmtNbk9OeFYvb21iWmIvR0F6VDVGZXg3YnRzRUJOczJNZWhXTE5XMWVBTzI5NkN3UERyWWZLWmFkVGRScm8xOG1rN1pVN05ha3VENlptdGxybXNUckJFazFQSkRtaWpOcUNHTlNzSVNTRk9HNG45Sk1tWEpuL3prdjNqZVVYSUpGd2VYSUg4cG5DWnhpcjJwem5VR2Ryd1pJYzFhbjlQYkJ5czNhZGZnbk5iby9zeFFFRlQ5VCtveDIwbnlCeFB1RHBtK1BOV0kyUnRnbndQVGVhTkMzUlJkUTJSU3dNWTZzQWJCTTFNbzVlMWlOeUplNU9jUk1WZU1rZGhEL3dDY2xkbmJ6blpqZnJJSk9NZ2x5Si9LZjBuZDJzWE50Rzl2SXhZaGpCbGJkbXdKRkUyYlhoUlpJRldMY3VoOVRaelNtMDl4UVQwVnZOU3IwWEVoUStXYWZTR2wxSnNVczI2TW5PU0JJUUE1b3lXcE1CRXNhSXFCVGNweG5LY1k5Y25jcVpQSmlwem5IR2NZamNiOVpCSmtNMk1rK3NOay9GVVN5ajcrZTQ2bWI0R2xJYjdGcXhFaDFjWlI2NWpISHpSRFF2bmwzM2FsSW1aWVV0QmFXOGZKV0gyc29tdy9HNEtKSWFMN3owWXJUa1dlQ0Q5bjZHYzZOMlJ6OHBrNURZbWR5L015dUlkR1E5dmVuYzFXcTNFeDBKTTBibWlBdEpaQ3JUKzNHS3FMa0wrTWVaMk0rZ3p6VENaSXpqeGtkQXE0UUhNK3JkMjFwWkloanhuMXFjQm81ZXFjNU1HcGxTaW5xK1NoYkd0TzVlWFI1dE5HRzIyVTZhdjJtdUhwMnd5MzJ6Z1NJeHd1cTdaQ2NLeklwR1Q5M1kxNnRYTEErQVdKc3BGamJBenVmNGRZMkZoa1hnY2lvOU83SE5SRTV4dTVWZ0Jzb1pFKzhWTG5veks4eHBvN1prNHlVcHNEZVZsMm9QM1c1WWJCVkxGTWlTTjhsWXg2UUZLbGE1cWdqaGtIL2dPenNHYW1ieUhBYnJSc0JGZ1RURTFmcGhCcUdHN3VrcXluYlhybE5iRjJOYUpGRXlNYnFKVkFrQThzbzd5VFhxdjBuZFArcHNvSkV3RjdEY0JFUXRuellIVjFvMk1hWS9YS0kyQjNNRTd0WFllNGJUK29NSndrY0JudmlsQ3E2Q0NLc0hlNlowcmRlT25Yemd6M2RCSHlRR2JDYkVra1BVYlkzVlVvc0xLR3JxTnh1M09QczlRMTU4Q2lNcmJvSDQ1SzlCNXhYMTZxdXVRUG4ydWVYRlRzaFRPdFhVZVlUL0FWVEFvUzRtcVFsWFJNL3dEVkI0dFlvYWVqaW1zRWU1b3BXMUJRVHVScnArbDN5ZGpBV0ZHK0Nta2tyTkRFcjlwZTd6R3hKZkFPUncwVHdkckZmWEViTHBtejZ6c01mdmFob2RwZVdUbHl5OGNNYTYxcm9XakFXYlBKSk5vRllGQTRuSVdXZERPMmNldXJLeVVkYmFsNnJ0aVNzUkdkUGROMk92c2hiY25iSnlhMUd3dy9IN1ZQSWtxTk8zRVNMc1pyZXgxYkxUc3VDRzk4U3RUcS9YeFYyMWx4dFZYSUkrQ1FPcThpY3lidEtjR1Uvd0JCc3h0V1c2M3VLdWFXd2lUdXQ2OXRSVUM2dFdVT3FpUVFNaldrcnlZMk1GTXNLNHdpZHdVTnJSTkhWSng3Y09MWnRYbWhJMGVwTG9ORVpJbERTd3dNYTlJYWp3SFBOd3VyQXVaRUllY0hDUkdxT3AwbUFzcElNM09tbUoyMFJTS2VKYTJxbXVTUUtpYTBuZVNaU0J4dS9zaWJVR2FZbjQrQzgxdHJQM0k5YXVIU0N1RE02aDlMS1N6UGR0TXV6ak5FdENZNHdXZWRyV3B1ZEJCYksxMGhIVGNWMDd2YTZjMElMT3FiV1EyMFpjZGlYS0ZCdE4wTE1zVmlCMUExdTdtU3ZDWnZ1b1F6eFZ6eXk3QjVNZ2lValhlS2RyNm1kQzlRRW1ZNG0xUnJmVE4yRGV2YzdLMjZONm1Hd2VQV3FhbE5Qb2dGMmIwMGdzbzR3T29GOHdQYWhoc3ZrU01FZUZvVnZCRk15dndtMnJtZXhBNC9mTm9iMUNHcHF5enNDR2txQzJtWXJMZEdQc29mYW9Id3YzV3pKa3U1NHBHbXk4dFJLYmNpcTRnNnVscytvRk43VW5sMTZ4NytvMEcwVnRqQjY5bzJkUGFnYlBHSXRsVGdlTDFoMzlNOVBuUEV2RjlpSjQvbmdQS1pTVXBWc1IwWXZHV3RPWlFrRVdUS2dHVnhSUmtFRlM0eXdiNlZYNEpaUGJOaEw3U3ZOQ0RGUGNGQWd1Nmk3QllXT0NtcGZhMHd1UDNoNGcvYkl1QjM3UVZPUlRVdGNFeU5ZNTJrRHpvNUlhOXNmeXp5NVJIOTFaRks3cmJFSS9iNXlnMjgvd0RJUE9UWmlxSzYzc3FZR3ZIbnE5eElyNi8xQU5QdkE5cDE0ZUowSVV0Y2ZJYksyTWhEbkZ1U0NidUo5b1VGc01jbmYxWDNQMzVmaFFORk0vVGl0bGRXWDFYc29iREVnUkZhNWtqbFRqbFpwaFdPNWw2djdqSk5RRXhEZExxeU9xb1IweXJzNGFMWmlLd3JZdGVuTlpIRGpuRXppdy9EVHNPZllqelF4Z2V0UC9qcnFjY09CUTI3VjFCYkcxUkszY2VaWHl6eXg5dlBHVTlJT2xuSVBsblJHa1NFdk4xZlYyUjYySDVOcld3MDRUNStvMG5xdFNiVEQ0WlpiVVgrV1M3WFRoeHVrSXRkKy9VUmEwMnZMcXBIM0k1dERPeGVIRUY3RnJPNWdSMUIzVVN6cG5lSTJuNnN1dFo0Z3hiMnkyaTBqZEN6YU5VMk8wcmMxeWxtRnJob1pkam9CYkNtbWptMUhxNEcxalFMY1RZOWNzVS90M1NnSW5kaU1zRC9BTUtucWRiRDBKWHhvNFZtOHFhUnBGMUhBK1Y2WXFLajFUTkZtUS9ZSmt6Y2xqQXJEVlhwNXRNaEdyaSs5c1JJTjVVRVY4bGV3clZwa1lOdk80RldsVzF0UnJlcnhuNk1RWFk2dE5CcW00UkNzK2ZBL3dDelhsZXFMMnBZMUpKTFo1ckNaTHZialVuSmpqb0NhS2NENThKRis2L1l3Tzc3WGF3V056WmQ2anJxQTBzZlI5WEJzNUt5ZWVtc3RkSGNSWG53YmJSZ3ZkT0xmOVdPd1Y2UTNHeGU2Vk1ZZHFQcTNGbTlGMktxS0N1SnNxYU9GSXU5dWoxMW9Ic3RvM09vWUUva0lTR3JKTGlDaVdHQTA2UlB5ZkU2ZFZjNDdtQ1ZTb0tnbXhoMU9NQkZtTHRkdTdwR2lPYzNsWWdrVmZ6S0VlejhveEtvNHU1TWZEYzBoWXMxZkxNNE1wMzhNcmlHTTVjdGNYL3RsNEVUTFVFc3pTYlhZeDRRNWNuRXZEVHlpU1pLNjJqWHRXYW1zSHB5KzcwMHdnUlh3NnByZHRXc2ZNOGtZK1ozN25odDRXY1JTckpCNUhpNzdGSStsT0pUUUZqc3Rpa0FhM1dBVnhOUkJqZXIyMld1VjRKRE9UaDRFall3ZStIL0FFeWRGYlZydDFIN0U3MDNRZE1adWJDZjdlR25EZ01rbUprMmV0aWlBWVl3SGNwUlkvRWF6Y3c1UDlrMjJ1Y3pqTHZjRnNTWDYyTlZhKzJ1Q2lIamk3YTFIektscFJQKzFJdDZCcisxWFhtdUorS3k3QnJqZVVTeDJXaWlUOW8zYlJ2L0FKbFJYVHJTZUZkdytibkRMWkowcjFLRUlFZlpsUkVSTVYyYlQ3UTRqN01jYXhPc0FJckIyeEZXVjBTR0JERnFiV2pzYk96VlJNZHFjSEg3T3RmTmxTSEptMFEzYVZVMDJDYXMzd284ajlNall1dGpvbVgxRVhUV0xkaGhDdkRqRWpUQ3J5eGxtZFY1OEpFaWNMSnIwTDhzZFpWc3Y0TzF4cm84aDF5QmZ4eDJ0UUovSjBDMnh5b1B0OVBQVXh4K2V2UEJyUW9LNEdhOWtUL1Z0OUwvQU5zTFQzQTV4cGc5VzNVdXRIbGkxTUsyYmVudzNua2J6eG5sWW1lekUxdk9RMUZ1VnNwbzlEUFFiSUlRQkJaUkRpZHZDUmdDUHhLVVJ5WnNHcEIyMUdYWHJySlc3MnJZMEcwK29LT09NTHZKS0lGYytBRHlXbkRUNnljUUtKRlRKSXhJK1hZUVJIM2NOZ0VjRWVRMSsrUnlsejFZcXhheTVqdTdQZ0pGK3NUWDVQNFUzWDVXaXpPU3JMaWpweFdKcUliN1c3dnA0VEtHemlJK21VQlRvOG4xbzVVK3FZZWVyMmdvYWZZWHp6bVU0eklhaVpjanJaWTArbVFUdHg0MHo0WG91Z2xRUkJPZ1hVWjJIV0ZzbzdCMy93RFhRcmtzS0lpNVBYdWxWVnd5cFZmcFBRamgvd0I3SmkvTGxQUzJjNG5iS0FaL2piaU1iaXRabGs1a2RjVS9LMGwvb0Q4ZEs1VytPN1Y1am9YNUdRMlA2eHhiSGZTSHZhN2NIZDF0TC9rYVZtUnB4amNhMU03RTdWelVZbTl4bkdsZU5sdGF3eDhZL2pqSmV4RnlXZU5xWVFVekNaV3Z6Ly9FQUVBUUFBSUJBZ01GQlFRSEJnVUZBQUFBQUFFQ0F3QVJCQkloRXlJeFFWRUZNa0poY1JTQmthRVFJQ05TY3JIQkJpUkFZcEtpRlRNMHd2QkRVMVNDMGYvYUFBZ0JBUUFKUHdIa2xxWC9BRTdiMXVsYVNSZ0Evd0FCNEZ2VjkxcjVNdkMxSExkTFhwMllXdHJXSVNKQU9MRzFkcHh2cHdRMy9LcE5tUU13RDZYRkhoVWQ4NUFyVCtBUGZGcTROY1Z4SUFwM2ZKd0JwREhoY05JUUYrK1J6TlcwMDBwWnBNVVZPNGd2cHpxMXZMaFhpUDhBQkRVTlJGaTF0YUFLUHh0VCt5enlPWGY3cmU2c0swbUpnYndqaUt3RFJUN0syOHZFVWxvNWo4SzExdlh3L2dXN205UkZvanhQV2hxRm8yVkJjMTJTaWhteWUxdU9OZG9CVzZJbFFDTUl3Tit0U1dIQlJUaTk3ZlUxckQ3Sk9UWnE0SDZTaXVCeGFwMGVYbGxGci9VMVBJVmhoaG94M1daK1AwY2N1bElIelMydFE0aXBDakRpZktyNVkzREN1RnFqMXRhOTdXcWZNMmJrT1ZZcE5KQVg2cUtmT3A1MXlvMUlxb0c0bXNaR1QwdlV3WjE1ZlRNZHFWMFJSY21wMmdrUGNFaTJ6VWVJK25FQWJLVEs0SnRZSG5XUGhMV3YzcU43b0tIZEZLdDlvV1BYNkd0R1VOelVoRWlXVmkzQTFqSS82cVNjWWRzU1V2STEya2hEV3ViY3VYb0t3a1NSQWFhVmhrU2JXenJwYzlLbHZpVzZjcU04elN5SFp1RnZwMHBtWE4vSXgvU2laRlJzOXRWMTk5UXZDQjR3YldyRkY4cmxjTzE5V3FPUlpsMGRtR2xQbkxEZFZkU1Q1QVZIaERpMS93QXh0cG1JdnlPVUcxVFE1Z2QzTkJJRkIvRmxycy9iNE10cGljTzRrUlI1MjRVNFBwUmNabTNyZEt4YkNOSXdVVkh5N1ErZFlkemRiTlk4cXhLWnNNTm5xM1NuUWtqa2E3aWs2VnlGVEtOcnV6TjBydFI0MjhWalhhTHVmeDFnSHhKdzBVT0Z5UmdFNTh0MlkzNVh2V1JuZmdWNVZMbUNTWlNvcnRQMlRCeERhWXVlWFZZMEg2OUJYWjMyeWpMUGo1UUduUDRtYmRpL0NBVDZWMjg3RzkyQ3htVCs1aldPMnljNDhRaFVOOE5QbFFPQng1anRsYXhWZ09ZdG93OU5hTzF3enRaWEhjRlkvWllYbmsrN3pwQkJob2ZzcDV1OFNlYS96TjhoNW1zRjdTRllvZHM3R3hHaEdVYUw4SzdKdzBXeVRPU2hZRWFhMnNhamthSXJ2d1NIZVpmSnVaOG1xeUpNTXp4QVdGK2VuaFBVVWk3ZHRFcGJsMzBnSG5VbTNubUdrU1hVVzUzdHZINGdWMjAyRFMrYlpZWlZqK09VYS9Hc2V1SXQ0WmJQZjNPUDFyc1lkbVl1VHZZbUcreFA0MFBjL0VOMm1GaU5DS3hKbGtkOXBZblZiMWgyMnJOY1BmZ0t2UVZuZGVCZkxXTnRJTklzT3JhVk1yNHZGemFKZm1UVTM3eFBkNTVoeFZSeGYxSjBXb2NrU2QyTWNQVTlUWFo2cEhIRjMyYTVMWnJBZjAyMXJCS01MSVl3WlVhekJmSHo5S0RKbGJNTXZoUFVkS2pVNG1PTW4vMkhHM3FLbFpzYmlpVlZ6NGN6SEw4QnJTQ3lpeW5uV09seUdReTdMdzZwbFByMXFXWWJOZG51N3R4bURjeDFVVVFEeHZWOGs3YjYyLzZnOFh2Rk5mQ1FYY0o1OHFpdTVGb0l6NThQaitWVEZpNXUvVnYvQUlLN09tdWl5QnBuRmd4VjhvOTVHdGRtdnNXZUViZGVTRytmNFdBOTlaaGx1Vkk0eG5xRCtsTGFURGJqNVJvUFAwdHI4UnlxNnlwWXlhNk1CUTNBMTA5S1lMcHhwNUkzSzVWWkRhc2JQSUIxT3RTeVpNR2p5aFdhOXlGMHJDSmlzUm5XRlkya3lCbFVjTTNMVW12Mk03UmpqUXZkOFBhWVdIRG1MODZteHZ0TWdKV0xZUEU1dHgxT25MclhiOEF4VFNiRFpQSmVRU1h0bGJ6dlhaRFN4NXlvZTlycmt2ZTUwNDZVTFplSU5HNnd5TkYvVGNWRGo1a3lFWlJFcWN3Ymc1aDBQeHI5aTFNR1E3MCtMVmQ3M1ZFWWhrKzNmR1Jxa2drNWlMbGJ6cDVmOFJURENMRUVQM3Q2K3A2NkNrQ2trRmJra1pxWVdGdHBiK2JTdTRtaTlOQllWZzhSRy8yUy9hcmxKelg1ZVZxN1JXT1dCYnlCZFdUL0FKK3RZV1YreklTc0dKT0xRUmJWbTNpUWVxcnd0NTEyVE5PcEtBT25NTUd1ZW1sZ09QaXBESHQ0cE1QS3AxNGJ5L3FQZlYzT3lLSHpJMHJER0FwdUZHRmpwVWhBV3NZL2FHQndzNHhDRjd2SU1PNHphRTZuS1RXRXg2aEIzOWhvUldEbEhaTFJiS2VYSllLVHpQeXJVT1JLUGVLYjdXUlM2aTNJY2E3Q1IxbGN5c1lmc2lzbjNyalcvSFd1eWY4QUQ4VGdwdzI2MWhJUWRNM1hXcEZkTmJNRHBwVDVRY3hBNjA0RHlTTlBCYzh5Ym41MU1rY2tZSWpFcldCZTJpMU43TW14QmxjZUM0NVZqcG1FN1pGekhOdEdiaFVOc01icXJxdDk3TnUzNkMzem9oRWlRbGI5YWtaWW5ZaUZ2VFFWbUUyRWsyVTY4MWNhTjg2bFdLUFM3T2RCeXFDZkQ5bDdSVGlzWmh5ZHJqV1U4SVJ3QzNBdTQxTnRLd2VKYVlIYm1YRjc3a25TK2JyWWNPbFRvK1cyYksxN1hGeDhxRmxHZHY3YlVMQ1FGOWVoSk5XdGxDdmJyOUVya2tTZTBSd3R2Q3lrQlBXcHlUSm1obEQ2bkp5UHFLN0diRVFhZ3lBMnorNmdZOFJoeHM4c2gza1AzVCtocDhaakhlNm9tNlZnVTIwWG5iU3NZMndNUVFZZktMQnZ2WHJFSEVSVE9wamp0YlpEMTlkYWQ0TURHYzFwUEQxc2VZdlNObFhkeUR3TDUrZExKbVZzNDFySTdKeGJMM1c4eHlQL0FBVlB0MWJ3dW8vS2h3cVJoYTcrdnVwOWxFVTJVWUI3eE9sNjd4UUVtay9kdTFyenc2YnJueHgvaUIzaDYxalFleUdiYVRSaVBON1FvMXlYNWZyVXE0VVJ5cm5Fc0oxakhFQWNxeHdqd3NZWVRRWkw3UW5nYjhyVXM4VzJGcEZmZVVnQ3k2K0VqL25XcDltOG9zN2ZkVHhIMXRvSzB5akwrRVUrZHB0NzZNQkxoOFpsR2FaWkxwTDUyNUdwMVBzaEFXM01kQlVZTE9tZlh6cFFyUS81aWVHUk9ocU5zUGlocTBjbzQrYXNPTk9DbzU1eFdPd3FaZnZ5QS9JWE5UaVJ3dVo1ZnVEK1ZSdzkrdFJabk9wYm1haGFwWFJjVXBFaWNtcnMrN2JQYVhqYkxwK1h5cnNwM2VjRmx6VGtMWWVnRmJPR051OGtTMkI5ZVo5OUs4OG1kYktQV3JncWlnMG05R3UxallIZVJ4d1lIa2FuMytCY3JjUCtOZjhBY0tuMVAvYWNPUGh4K1ZUdVY1M3N2NTFoOWxIenhNbkQzSG43cW5hYWJKbmxjOFNhdm1jMU5jZ0hTLzBMY1JSMmJTbzB6dmlpZTc1Vk1ES3QxOUFEcFJGcEVJcFEwa0w2TjFGSkpIamlBTjF5RFUwOHM3d2xzOGh6RlR6dFM3U1BGNGZaa25UTGJXbVdpdDZqUXlSZDFqeXEwMkdnZ0NScWVBdU5hVlliTUVjcnpVOFJSRkVWS3RaWlhTSTVWSjRtbWx6c1pXUHd2WFlXRFo4SEprV1JvZ1djZFRYWjhNVDhMcWdyT1h0MXB5WGMzdWEwVWREU3NVTGFHc0NqNXRidUt6eDRhU05UbVhqbXJHWXY3SWJVNW4wdlVNbTh0NlNVZTZvV1krbFljczBYRlNLd0VLN1NLMjlMcWI2MWhSRjdLcEJ5dmU5WCtORS9HbFB4cnRGOFBHQW9rQTRsclYydEppSTlxRXlzT0I1VkVLME5QVEMyUzlZSEFoWWcyVzdHNTBwWW9wWkg3c1pOcmNxZGo4YXZyNVZHYzNRQ3NLNk5lc0xuSThxdzVVZWxLb0xXNWNhVEsrVUVXOWFMdGh2WmcrUit1bFFqV29RS2gzWlFiNmNUU2tLQndxSE5MaW1aWlZrMVUrZFliSy9NV3FENVZGa2MrTExVZHBUYk1ldEx2eFNLeTY2QTFIdEc2MnFLM3VwQjhLd3NTUll1MExUSGlNM1FWaXBXV0VaUVR6cEVrSEZzNjNyMmY1VWNQOHFmQ240VjdNUGhTd04rRzFZY0gwcnRIOTN3K1hJVWp0ZTQ1MWo1TmpBQ3hVSUJtSG5XSlo1c2ZoaGRPU2cvVEhCS3VHaFptamtOcitockJ3UjdXUGFDUE1lRllLQVptYkx2RzQwb0JwQjNyVkhSMmJjbXRYYUtSUjRPZllCQkFEbTg2N1VSa2lzZG43T0xIMXB0b3pDOTh0clZHS2pxTkh3OERvNVc5dUZkbVJ4bVFadDZiU3V5NDlxOFJPMEV1N2JoVVl2enFPazA4cWlQd3FIWDBxS3UxY1poR1NNWnRpMjZmY2EvYVB0V2VIRVNoSGpNaTd3SjFwUWtNQ0JFVUhnS0JwVFFPemtqS3RZOHEvYTh4b0w3SmRtTjFPbkRwWGFJeFQ0QzJ4S3FBTmVONllVMU5YYlRZSER1Z21tSGV2SWVZcjlyWmNYZzVwYlN3bEFNdzZYcTFoUUZBVkpzOXBIM2dMa0VWMlQyYWtjUnliU1JqcUJwVWVFam53MzJDSmg5VnluVzVKNTFHdFJpa0ZLS3RTMW9UR3R0YTRTWXFORDcyK2k5Q2h3amI4cUVoSWpIQVVqZ0s2SnZEbldxMFRUbW8zTHpZZEhYM1ZoMkdmRWNhYzBUVjY1cWFrMzBsY0grbzA5d3JwZjRmV0ZjRmpSYU53Y2FwdDZmVThNTG41R3JheERsNVZyKy9mN2FXaFMxeXdhVy9xTmYrVVAxK3FvVzJNbUg5NW9hV2pZK3V2MVI5SC8veEFBakVBRUFBd0FDQWdJREFRRUJBQUFBQUFBQkFCRWhNVUZSY1dHQkVKR2h3YkhSLzlvQUNBRUJBQUUvRU9Ld1Axbld0OXhYNWlQeFVRbHhJWC9BUlIyQ0pFMWppVFlNbzdsYm1PeTVZTTBjalkyNjNVTzRLN292U1dZM0NVQjY0cUswbXp6VnV5SEJLc0kvc1gzclZZdjFhS2RUMVRQSVRhWUJUM0NxTE44bHpER3ArbVg3dXcvRnhKZ1k1ZXpPRmRwL0hFZHlWaGNHcTROcEVTNGh0YW9SNk5wU0VTYVRVSjJYNnBHOS93QWRVbEhqd1FuUUJvaUxSMjIwMXRWRldQR24wWURRcGtVOVhkM0hhY0NBSS9HSWgwem9NZFdDYXdwckRhT3FuUU01eUFNTDBvTnVjWlZmTmJYTXFoZFlXRENPYXJHTnhJcHdDMGViaTNiaHQwMFloM0pyZ0Qwc0FVUUNPcE95YTl5OEh4TVNrTGg2bW9aTmdHZndDSU1SVXVFNFNMenl3WHNsSnp4T01HRzdqc0F3elk4d0xxOHB0SXQ5bmhLcDVoaExsRWpTWWdBRVhEdGx2MmJaQzd6V0UvZ042bDRkUXdjQUFYdkd3dGxVL01haWJ3bWhnODBRWDlBT1ZpRU90Z2YyUXJ1d3NTS1JNZnlCTmcrcGU3ZXF3OVJVd1VXaXNKdDA0Y3JHT2JSQyt4MUNodzl3bkszV2dsSHdWTnh3QVFoNkFBT2JOTVg5Q2kzajJTdHRWaUhLdjhjeWprdkdhRGs1SEhMUU81aG5BR01KRDNxVUNLR1NGYUlPaHhTTGZKcUcwbENSVzZBSW8wbS9vZDBSRzdwMjN3UUtUVXNvV2t1U1lRTEt5aHdjL01rVkYyUW5IRTVENk53ZFphUFJ1QjhMSFZNdkVFUDhQaEFkN0xSTFZhSlJUTlZLTE5GSWh0c0dEMG1nSW4xRmduaEM2RkNReTVlNXZ3dlFmUWJLYVkySCtSUTRybWVCeGRDNHQ4MHB2ODNEYnVvYitIOXl1ckZEdEF6MndyNDZ3c3l1NnRycmFvN3VvUUZyMk5lTERuelJHNG5MWmo3bjdJQ1pRVzBibVhyZ25tTnY0SXJOS08xT1kvUVo3U29PRmhpVTd2dW81cEhDd1lKWkJlTGNoQ2NMR0hMNi9NVDRKekZMaEdMSU04MGdxZUF3TkVBRml3NzdrVlJSb2pibk5ISzgrU09iTE1QS0h5dmdPV1VGL0tYTFNRNytVeFMyUHQ2SC9pQStJUDFLNWlVeGFGcXJ0eVNoQmVRSlJ3c2M0QnFNRHplSGFWeER4RldDeXlsQ1hoczNhYkNjSzI2N0w2a3dFaDZHbzlFZ3c0aEFRYWdleE5sbCtJVlR3bXM1dDlHdzhXWEwvak9xZU1laTVGRWQ3SG0wbkFJdWFIRDdsSS9yQWV6aGJ6OXBnSzZ3UFhpK2pUbHU4c296eGc5WEZJOG5CNWhFd2dFUDFjVzNTUGtrQkdVMVdKNW1iUDJzSytKRmZRcUVaN08yTjk5ckRwS3NUbjFFZzFOMFM2bzhXeDZYRlJYRVo3WThSNVROMFY4TStMV1lEUVdnSEtuTGRycStMU01UR1lVYWRWTnZKbGpsdWczY2VDVUNpNldvdEkyam5vL0tIL3plQXRjdHJRN1hnYkJPVEhjUndQT3Z1RTF4YU5CZlB0NVh6Rkx4UHkxQTgwb0J4U1JCRXJ1bFBMTEVDUzh0Z0t1YnptWGFDRitoWUQyamh3WkF3L09ybmIxQXNCR2M4YWRqeDlIekJOR1V4SGd2Z1RBTUQrRmcxcGxFSEMrWE1sRkJ4dW9zdEtORHVuSGc3V29aZnlqZFdOcFRnRnBwRFNZRkh3MVR3U080Mk05dUpWdUhoVlNsMWxyTkV2YVFhcjhScU1LcWUxY0VWc0xlbHVGd05hNjFzRXVyU3BuRlkySXVKWVhvY0dTMlRUR2JyREtscml1dmgxczR0T0tVZ3Fxc0tIZ3VZTDNxYk9iRXZ2TWdKWElQTC96ak8wcXBGTUd4QUJTMGhRWDBGeDB0dlhvRy9KRll3VFVOaXQvTVB3eXZUQ1ZvMUE3SlpFSWVvQWx0VlY3VjJVYXFkM0NOUVlTb0lRc0xiN1g3akNZUWRKS2kxMVh5TmtIQWtqcVNxS1JXd290c1pLUDN5SzJZSEJRTmhZbWU0UGhYc1VzNitpSk8wR1lOQ2lpbk1SVTNWQnQxL3VvM3JjeVdxNVQxRHd4NmxQZ3NYTEVjOGthQjQ1bnpoSUtiazlBSisxdEtaandlR2tXL2hJSnJUaEkwMnZnNWl5akt3RGpHRmVETjV4OVJ4OFZZaTZ3ZXFia0JTSmZzU1BnZHhyVXVjbFBPYmgvVWFTc0lFOFdxUVplN0xjcmFXNHVSVVBOa0ZDNkJudy9Nc0JGNzJWcUtwbFVyeUxqeHpvalFneDJjajZGcmZXREs0eVBzRkN6NFFHSDZBaUtEYnJXRDQ0TmxLUTA2V01XQzNNdVA3TWZXU2xWU0tMOGhkZ1Z4YUUrSThFOFM3MVFCNzdlREJuQzBQNE0vaVQwNjJ3eXEwM0xNZjNVYlNvc0JwQjk2ZHQ0Slo4TndKVEpKZTJMQmdjaXdRcTE1dXI3SlpSSFZndHdXUFpZNEc0Tk5iMlgxVXZvOWMxS3dHdUMzeE52UTNLdHA1a1M2NDJGalVJSFNwaWVUNXZIUkZGajFOVzhneFdhMFlXSEYvd0RUN0pPTG1BQUR4Zy9wRDBhTVRCcDhrUlZwWUtpRENuSi9JdjhBNVh3TDNQYVpoRmhSNVdLU3QzYkdpaWk5Z09VY0taWGVhS1VJN05MOEhDa1hMb044Sk5GVXhWMVlMN2wzL3BCcEdyaWlrRTJBb0RFc2RvSkVwZnpkWHdLM3NYdU5VQXo0QW9KV1V2WEx5N05NWEhnazZQYkRZN0YzREc1eldiM0J3T3RHMW92NXFXWmNYRHMwenJzZVIwanN2aVZGM1pBOWd6QlRoUWgreGhoaXkyaDhXcC9JVEM1Q1lkMkIvS2ZTUHVtMm0wODNGKzMzYkZxcmhhai9BSXdlSzRtTkxTMGFoZVlXMTVHa004UVRsZWovQUVtSXE5SCtLUWtuNXFMOFZVNGRlYUNQM2E0WEhkbThlWHlRbzFsLzc4aCt4RFI4WS9yVVM2dXhWbXAzWWE5WDNIUzlZN3N2UGo0SW01UTN6TDg2ODhEQ1Z5TEFCenNEemRSMjBVcGJvbFF6NXRzdEFSRFA5dEpmVkI0dnArcGQ4NmdsNXVtbUtoc2hFYUNZdVFFc2I1TWdCVXpJcWs0ZHRRdjBua3VFbEdrUys4dzJhUlE5T1dHZjVnV2c4RnhoeXVsRUtsSHZIWlEyeEZCYTVCUVJMdU9HZ0E5amJIZ1p0dDhrdUFnQXUwRi9Nb1I4U0Z5RVdvdGRiWmJtNnlDSzBENmliWXRrYnkwNGdKZ0NLYnZ0akczNDVROGsxZE1GZ3VlWkljYzVnQS9BalJDZW9vekpZRVRjRWJVZ2p4NWdMV1JpakZMd1ZBdEU1TFRhSjFDN2RmbTVjZ2JqWEpYUUU5c2NabEpWcXlPMmhRNE5ic0pjeElySFZvMk9NYk1pNFZGdUs0aVBDS0pSSDdHMkZGdjRscDl3aGtEcEdMaTZuR2hrRENtNjBhWjlyTHNEaTVrUU1HeEFWdTdYZ2lyOUc3Q3NsV2FTVWxYVHd4RmV3VkpCbGt2NnVGMmVLbzJNaXFnNjdxTDdIbTN5SlMwTGhMbjFOZmR6S29UdVdVc3BMV1FuSjF0MDdZRllncmxSNGVwVGFrT1dRcHZqYXc1WWNKY2dLTDNLdGVlMS9pQVg3QVJjUUZPdlpRYU5RSlVJY0l2UkUzUUlhRjBmZ2hMQXlWY3VwZUYwNTJUUnpxUk9WV2N5OWl5aElXVldIZmpYQzVTcEg2ajZpS0lXamV2WXRvWFpXN25GRy9EV3RYWkVFanFnUW5GRWJLeWpKQzFHdUdCSVZ3KzRaaThOcFV2b3VVWkJkazMxVlhHeWFoN001STB3MHRSc0E3QjVjbFcybWdVQkwvakYrd1Y2VmFzSGZtQk5mSGdZRnJoSllqbjVXWVM1dlRvejduWENVV3czVTc2am9WYVRBNk9rOWJGUUxZaFpGeTErbzJnM0tLQy8xVW9Ud1FmMTZ3Mm1GMUJES0FlaVBhc3MwdHJ1UXVnR241SXJjcFB1dWc0dW9sb1MzVUI3Q21XTlNRdGlFckx3VCtKU05rRjgxRkllUFJDOGJLNWR5c2ozUlJzVWsyTG1zRVRET2RFRDlvTjFXbHZpUEkzcWpoVWd0U3JEU1hUTHhpcVUwNFJJeHkrTnRLQUdGN0RlT0FVTUtlNHYyTU9uVC9vbzIzalpOdDVnbHNwRGpVUkhuOFlERmErcDVnK29nQUEzNlYvN0VLb3k0MHNQREI2a0YxQnh5Qm5aTHk0VGlEaldmOElLcVAxTEttdndJcFBqUHRCd0pxWWdHSkZTRlNLNUpnVWNSRnRMTW5UNngrY0ljNEx0bGdxeHJqTkIzaUtCUC8vRUFDd1JBQUlDQVFRQ0FnSUJBd1VCQUFBQUFBRUNBQU1SQkJJaE1SQVRJa0VnVVNNeU0zRVVRcUhCMGVILzJnQUlBUUlCQVQ4QTAzOXNUVXI4cy91YWE3L1kzNGpud1RNd0dBL2xmWjYxSmk3ckczR1djSkthL2xtRndnNWgxQ2ZYTTA2RTJGcHBXeW1KcXVGekszL2t5WXB5UFBma21Id3BnUGpQblh0aE1UVEU4QXk4L0NVY0FtTVM1eVlFQThhSTlpYWdBcGd3VmdHVkhBOEdEd1RDZkt3UVRIaFpxcS9aRXJDNGxqQURtVkFGZUk5QkhLelA3aVAvQUM0SE0wcEtzQ1plQ3c0Z1E3dVlnZ21JUEIvSlQ0TStvNGpWakVZbDVTdTFSR2JBeVphUmFjejFoZVZoSlY4VGNTTXhXeTNNQTRnR1BCNGd0NXdmd2JPT0pXeEkrWGZnUllUZ1JMQ3hJSXhHV0VaV0J5cmJZT3BxYzdJT29UTDZ3RzNUM3FxNGoyQWppYWJVN2hob0dEZFRNc1lLTW1QWXJFNE0wMSs0YlRCejRiVXFwSU1YVXFjNGlNSEdSQkMyMk5xUnVqM3B5QVlQNllEeEZWV2VBVFVETlppbGhEa3l4ZllBVEhSY2RTMEtEa1FFZ2tpVVgrcnZxQ3dNTXkvYllNUTBKMklEZzhTblVnRGEwM2hoeEFxRHZ1TWxaTXJaNlIreEVZT01pYXBqbmFKU2dzWWhvK2xxSFFsTm9LNC9VVWdyS0Z6Y1Q0dHM5akZCMUF1UHFmTDlSMzlhaGNaaGNIZ3kvQWxha2dqNmlESENELzMvQU9RVnNlelBXeTlHRlJZTUhneXl2MWdxd2xOSnRnUUFiRWlVakVOQ1k2bm85YmIxaUlvNVdhdkhFUUVzQ29semJKdWY2Z05xL2NvdlZTZDR3ZjNCZ2lLdTFpSmc1ejQxTE12OU15Vk9ZU1cveVRBZ1JSV3NTb0FSY2dZalpNYXNTK3ZmV1FleE5PdUsrT3pFVUlKaVl6NDI3VGtUVXFTNC9VcitDYnpFckxuYzBSUjFpTXYxaVcwRDZtbnRLL0F6VW9RQ3l5azVTTzIwUytnV2pudVBwaXA3bWwrVmdCZ0haRUR0OWllNUlMRlAzTW5PTVF6VGtNZ2h6QzFuMHN6Y1J4RkJ3TjNmalUyWXN4SEdCRllEaUZnTTh3Vzg4R0Z1Y1lpajV4dm5YaVVydFdFWmlhamJuN0gvQUZMTlNqSHFVTUErNkw0S2pHTVFWcjMxQWN5MXdpRW1hS3p0VEdZSU1tRmdxN21uQzg1bk9ZN0JWSk1DRyt3d052WE1CR01tUDg4N2VwV0JqR0lDREZIeXpFUHd6RVlNVGp4akN6Y1RtSnFmV05zMGx5MnBrVGJnN29GT2M1Z1VnbkptUW9tcXYzbmFPcFZkc2JkS2JWc1hJbVBCSVh1RzMzV0JQcVVZeVcvY0I5YmtIb3hsTGZmRU81Z1BXY1FxeFlFSGlCY2RTMXdvMnk3VlorS1RUc2M0OEZXUHhoQldWNk1NbVQyWjZXMHpiMGxlcUI0UGNGNEViVUlvN2xtb2F3N1VqYVZ6RjA3Q1UxbEttYjdHSi9xblRzUWF4bU9BUCtaWTFyOFRUVnV0bTVwVlR0RXRyVjBJbFdxd01QRXRSdWpDd2hjdHdrMVEyTUZCNWpManFWWkhpamw4eS9nSC9NMDlvQzdTZXBZVmRTSnVLdG1HMFduNVFKbFN3K3BwbjJQa3ozSkRhbUl0cWs4eTV0N1ppSGEyWjdsbGQ2d1hxUHVXYWdCRGlWcDdId1pROVFYYTY5UVgwcjBJK3RHTUtKOG5PVEdVZ1lNUWpiSGRpZTVwa0pZaWFoTU9SRkJIVURHRUV3UUZnbTBSUjRBakRFMjdoQW0zbWN3S2NUYVJEbUlXUTVXQU56bWMrRU8weDIzSHlUdDZtb1hJTFRTTHVmQjZucFNEVHFPUkxLRVV5eEFPcHRJTTNtYnpONWlEZEhYYU14YlNJTHA3eGpHSUgzQ0JQdUQrUG1lMUREYWs5eVQycCtvYkFlaE1reXoyYnlNOENXTlljZ25pYVNuWU4rZS9OMlFOd2hkaU00bS9jWVVKbnJnck1RSEhFZkpITUZaSW5yaHJpY0RFRnBQQUVhMG5qRTJ6Wk1FUUF3QXphWTZlMXVHSWxxR3ZzNWkycW93SWI0TlJIdTNLUkJTemdORkF6L2licHVFM2lJTi9BaklVd0NZb1NCRW5xUXg2VjJuRVZTeHdzMDZaWWx2cUd0RDlRVkorcHNUOVJnZ2haUkM0TXJUWVRMOHN3RVhURVQwR0RUbVBRd0VSd0VBaVZ0Wm5FZlQyZ3dhVzJIUzJ5bFdyT0dsdVhJVUNMcDJnb0lnUmhDakVHVU1GWWd5ajVzeEVDR2JUTmthZ21OcHpCVHQ3akQ1R0RMWEFIOEQwWXJuRTBYOUpqWWdJaGFXZjNZVC9BQ0wrS0FGek5Kd3pnZmdTQkNZeG4vL0VBQ29SQUFJQ0FnRURCQUVGQUFNQUFBQUFBQUFCQWhFREVpRVFNVUVFRXlBaVVRVVVNa0p4RlNOaC85b0FDQUVEQVFFL0FKL3laakp4OHI1cnBYeG9SaWp1NlJLa3RSZHliSXgyZkJIMDJSK0QxS2NNU1RKOE14OHMwV2xmTktoZGErUHBGY3JNNlZ1aVBja2syWS8rcFd1NSs0bkxnOXlTN21lcjRJT21SbmFKTDRvWHhmd3dUMEp6dGtVUHZ5UXlrSEZMaEdTQ2xpMnJreXEyeEVYd1BuNExvaS9nK3FFNkxJL1VrN1pGTnVrZWgvVHNrK0dmOFZTcVRPNkl4VFk0MHZnbFo3ZEsrcUVTanIxWWxZNFVyUW4waXJqWTFSK25SakxQRlNQVC9YS05jRUhkb2hCdmtqRzJaY09yNEdxRVk0dHNVWEZHVEhYS0tFUnhOazhiWEJLT3ZSS3lPS2tSeHlvZmZwZFE2ZWxsS09WTkhvOEdmZmFmWTdJamFJTjJSYjZUeE9Rb05HSzQ4aXlOcW1lQ2VLK1VLTFFuSWlwMFNpcFAvd0JKSnAwekNpY3RWd1F6VGZjbkJwbFVTZFkxMDlMQ09LUHV5NytDZnJzMHY3cy9jeTh1eUVYTGtTcnNZMDVFcVJPVVlybDhudS9nV1JlVUpwZjRRZDhvbFBWOGpuYnRqbXhaSDRaSDFVbkQyNU8wTnR1bVliNUd1SGJJWElTaWJRZmd5WVhOZlRrcW1TeWJRUy9CZFJycGdWbEprS1ZrcHVUMlk1TWxYZE1qclZ2dVJrekRQV1JrYTJkamJadGFxaE53NEVKMnFNTDRKdTVhSWxMWGlJLzlJOXJzaGtjVE5GVGp1dS9reFUzeVpGVE1jZGpIa2NDR2JZeThRZEYzM0hGVnd5T09UWTROTm1zYXV4UGt6S3B0QzFYY1VZVnl5WHQrQlQxdWhXekRINjJSNVpvNnNVWCtCeGFGSGl5N1F1SkU1Yk1Ra1k4YmlaRmFycFhGa0pOYzJiTmxVNk1jZG5SNm1QOEFZakZ6WWw5cVJibmFvcE9QSGNoRnlsU0hKWTRvclNSVDdJaitHeVRmZXltaDhJa3ZzU2kwcmZSTk9WRUlyVldQQzVjbWZFNFNZcFd0UzFWVVgrQnR5LzA5UGkxVms4VnFpZU9VSFhSSWlteHc5ckc1ZVNkMGtOYnh0ZUNFcTc5eFVyMjVFNDZ0TmNqbGZjeHhjdVRGaHJtUm1pdEgwZ294K3hHYWwyUDNPcnBEeXh6UjFaTEN4NHJJNFpNaGpqRG1SSFBCRHl4Wmt5WGtqSHd6Mm95N0R3UlNzeHFFVFBLTHh0SWxrc2hOeGRrOENmS0hqa2hSaytEVlEvbVlaYnBzVXZCTko5SmNJeEl5UTVzZ3FZa25HalZ3dWhTOEdXTnhOR1FpeVVQS01TMVJOV2hRWk9EbzBaREcyeHkwaVpGTzdpelRJKzdJNEhmSkZLQ0ZKTWZjeHhpdkJrYU1VL3FOcnljQ2xYUk5YWTNhSzZZMG0rUnRRZkFwcVhCckJEYXN0Q1ZGcVM1Sk5DNlBsRVVrdWlkQ1Y5ekU2NE1ycFdLYkZrZEVNalpDWDVOaEswS0pyMGk3ZEdsbnRpeGpWTXZnZjJGQmlpeFFacXhSS1NNZWxja0ZHdURMUGJncnBEdlFvb2NDTXFWR3h0MGlxRTZOeFNIenlhQ2drYkc1ZG14WlpGNkxsV1Fsc1BHMkxFZTBSeFV6M0ZIZ1Z2dWFsTVVXU2VvbmZZcVE5alprWnV4dWx5Wkc2NEU1RnkvSXJJcVFvc1VDY2xJeGNLeDVvc1dTSjdzU09XSlZ0aXlSajNJNXNiUGZnTFBqTXNsTjJqSFViWTg4VDNrT2NSVGpaSk5yZ2s5RXJOMEtadVJ6SkN6STl5eUxIeGpZbVdKaU5VWnU1R3lpaUg4VCtqNkxvbUoxRkdaOEovQkZDUC9aIiBpZD0iZiIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiLz48L2RlZnM+PC9zdmc+", + "description": "Displays changes to fluid pressure data over time visualized with color ranges and background.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.rangeChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.rangeChartWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: false,\n defaultDataKeysFunction: function() {\n return [{ name: 'pressure', label: 'Pressure', type: 'timeseries' }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-range-chart-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-range-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 15 - 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":2592000000,\"interval\":86400000,\"fixedTimewindow\":{\"startTimeMs\":1700577227898,\"endTimeMs\":1700663627898},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"dataZoom\":true,\"rangeColors\":[{\"from\":0,\"to\":5,\"color\":\"#2B54CE\"},{\"from\":5,\"to\":10,\"color\":\"#3B911C\"},{\"from\":10,\"to\":15,\"color\":\"#F77410\"},{\"from\":15,\"to\":null,\"color\":\"#DE2343\"}],\"outOfRangeColor\":\"#ccc\",\"fillArea\":true,\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm\",\"lastUpdateAgo\":false,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:cHJlc3N1cmVfcmFuZ2VfY2hhcnRfd2l0aF9iYWNrZ3JvdW5kLnBuZw==:cHJlc3N1cmVfcmFuZ2VfY2hhcnRfd2l0aF9iYWNrZ3JvdW5kLnBuZw==;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Pressure\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"bar\",\"decimals\":0,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "fluid pressure", + "liquid pressure", + "pump pressure", + "hydraulic", + "pump", + "compressibility", + "compressive stress", + "pipe", + "pipeline" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/gateway_configuration.json b/application/src/main/data/json/system/widget_types/gateway_configuration.json new file mode 100644 index 0000000000..985426ef94 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/gateway_configuration.json @@ -0,0 +1,40 @@ +{ + "fqn": "gateway_widgets.gateway_configuration", + "name": "Gateway Configuration", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Allows to define configuration for a single gateway.", + "descriptor": { + "type": "static", + "sizeX": 8, + "sizeY": 6.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-gateway-config-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"widgetTitle\":\"Gateway Configuration\",\"archiveFileName\":\"configurationGateway\"},\"title\":\"Gateway Configuration\",\"dropShadow\":true,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null, + "tags": [ + "router", + "bridge", + "hub", + "access point", + "relay", + "opc ua", + "opc-ua", + "modbus", + "bacnet", + "odbc", + "ftp", + "snmp", + "mqtt", + "xmpp", + "ocpp", + "ble", + "bluetooth" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/gateway_configuration__single_device_.json b/application/src/main/data/json/system/widget_types/gateway_configuration__single_device_.json new file mode 100644 index 0000000000..a7b3291666 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/gateway_configuration__single_device_.json @@ -0,0 +1,40 @@ +{ + "fqn": "gateway_widgets.config_form_latest", + "name": "Gateway configuration (Single device)", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Allows to create or choose the gateway and edit its configuration.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 9, + "resources": [], + "templateHtml": "\n", + "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", + "controllerScript": "self.onInit = function() {\n}\n\n\nself.onDestroy = function() {\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\t\t\t\n dataKeysOptional: true,\n singleEntity: true\n };\n}\n\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-gateway-config-single-device-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"gatewayTitle\":\"Gateway configuration (Single device)\"},\"title\":\"Gateway configuration (Single device)\"}" + }, + "externalId": null, + "tags": [ + "router", + "bridge", + "hub", + "access point", + "relay", + "opc ua", + "opc-ua", + "modbus", + "bacnet", + "odbc", + "ftp", + "snmp", + "mqtt", + "xmpp", + "ocpp", + "ble", + "bluetooth" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/gateway_connectors.json b/application/src/main/data/json/system/widget_types/gateway_connectors.json new file mode 100644 index 0000000000..765d9043ad --- /dev/null +++ b/application/src/main/data/json/system/widget_types/gateway_connectors.json @@ -0,0 +1,38 @@ +{ + "fqn": "gateway_widgets.gateway_connectors", + "name": "Gateway connectors", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Allows to create and manage gateway connectors.", + "descriptor": { + "type": "latest", + "sizeX": 11, + "sizeY": 8, + "resources": [], + "templateHtml": "", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n if (self.ctx.datasources && self.ctx.datasources.length) {\n self.ctx.$scope.entityId = self.ctx.datasources[0].entity.id;\n }\n};\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n singleEntity: true\n };\n}", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Gateway connectors\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":500},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false}" + }, + "tags": [ + "router", + "bridge", + "hub", + "access point", + "relay", + "opc ua", + "opc-ua", + "modbus", + "bacnet", + "odbc", + "ftp", + "snmp", + "mqtt", + "xmpp", + "ocpp", + "ble", + "bluetooth" + ] +} diff --git a/application/src/main/data/json/system/widget_types/gateway_custom_statistics.json b/application/src/main/data/json/system/widget_types/gateway_custom_statistics.json new file mode 100644 index 0000000000..7090490ea6 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/gateway_custom_statistics.json @@ -0,0 +1,42 @@ +{ + "fqn": "gateway_widgets.gateway_custom_statistics", + "name": "Gateway custom statistics", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Allows users to create and display custom statistic data for the gateway in real-time.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() { \n};\n\nself.onDataUpdated = function() {\n};\n\nself.onLatestDataUpdated = function() {\n};\n\nself.onResize = function() {\n};\n\nself.onEditModeChanged = function() {\n};\n\nself.onDestroy = function() {\n};\n\nself.typeParameters = function() {\n return {\n hasAdditionalLatestDataKeys: false,\n dataKeysOptional: true\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-flot-line-widget-settings", + "dataKeySettingsDirective": "tb-flot-line-key-settings", + "latestDataKeySettingsDirective": "tb-flot-latest-key-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"legend\":{\"show\":true,\"position\":\"nw\",\"backgroundColor\":\"#f0f0f0\",\"backgroundOpacity\":0.85,\"labelBoxBorderColor\":\"rgba(1, 1, 1, 0.45)\"},\"decimals\":1,\"stack\":false,\"tooltipIndividual\":false},\"title\":\"Gateway custom statistics \",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}" + }, + "tags": [ + "router", + "bridge", + "hub", + "access point", + "relay", + "opc ua", + "opc-ua", + "modbus", + "bacnet", + "odbc", + "ftp", + "snmp", + "mqtt", + "xmpp", + "ocpp", + "ble", + "bluetooth" + ] +} diff --git a/application/src/main/data/json/system/widget_types/gateway_events.json b/application/src/main/data/json/system/widget_types/gateway_events.json new file mode 100644 index 0000000000..4cbbfb8d7a --- /dev/null +++ b/application/src/main/data/json/system/widget_types/gateway_events.json @@ -0,0 +1,40 @@ +{ + "fqn": "gateway_widgets.attributes_card", + "name": "Gateway events", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Allows to browse events reported by the gateway.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 8, + "resources": [], + "templateHtml": "", + "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", + "controllerScript": "let types;\nlet eventsReg = \"eventsReg\";\n\nself.onInit = function() {\n \n self.ctx.datasourceTitleCells = [];\n self.ctx.valueCells = [];\n self.ctx.labelCells = [];\n\n if (self.ctx.datasources.length && self.ctx.datasources[0].type === 'entity') {\n getDatasourceKeys(self.ctx.datasources[0]);\n } else {\n processDatasources(self.ctx.datasources);\n }\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.valueCells.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData && cellData.data && cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n var textValue;\n //toDo -> + IsNumber\n \n if (isNumber(value)) {\n var decimals = self.ctx.decimals;\n var units = self.ctx.units;\n if (cellData.dataKey.decimals || cellData.dataKey.decimals === 0) {\n decimals = cellData.dataKey.decimals;\n }\n if (cellData.dataKey.units) {\n units = cellData.dataKey.units;\n }\n txtValue = self.ctx.utils.formatValue(value, decimals, units, false);\n }\n else {\n txtValue = value;\n }\n self.ctx.valueCells[i].html(txtValue);\n }\n }\n \n function isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n }\n}\n\nself.onResize = function() {\n var datasourceTitleFontSize = self.ctx.height/8;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n datasourceTitleFontSize = self.ctx.width/12;\n }\n datasourceTitleFontSize = Math.min(datasourceTitleFontSize, 20);\n for (var i = 0; i < self.ctx.datasourceTitleCells.length; i++) {\n self.ctx.datasourceTitleCells[i].css('font-size', datasourceTitleFontSize+'px');\n }\n var valueFontSize = self.ctx.height/9;\n var labelFontSize = self.ctx.height/9;\n if (self.ctx.width/self.ctx.height <= 1.5) {\n valueFontSize = self.ctx.width/15;\n labelFontSize = self.ctx.width/15;\n }\n valueFontSize = Math.min(valueFontSize, 18);\n labelFontSize = Math.min(labelFontSize, 18);\n\n for (i = 0; i < self.ctx.valueCells; i++) {\n self.ctx.valueCells[i].css('font-size', valueFontSize+'px');\n self.ctx.valueCells[i].css('height', valueFontSize*2.5+'px');\n self.ctx.valueCells[i].css('padding', '0px ' + valueFontSize + 'px');\n self.ctx.labelCells[i].css('font-size', labelFontSize+'px');\n self.ctx.labelCells[i].css('height', labelFontSize*2.5+'px');\n self.ctx.labelCells[i].css('padding', '0px ' + labelFontSize + 'px');\n } \n}\n\nfunction processDatasources(datasources) {\n var i = 0;\n var tbDatasource = datasources[i];\n var datasourceId = 'tbDatasource' + i;\n self.ctx.$container.append(\n \"
\"\n );\n\n var datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n\n datasourceContainer.append(\n \"
\" +\n tbDatasource.name + \"
\"\n );\n \n var datasourceTitleCell = $('.tbDatasource-title', datasourceContainer);\n self.ctx.datasourceTitleCells.push(datasourceTitleCell);\n \n var tableId = 'table' + i;\n datasourceContainer.append(\n \"
\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n\n for (var a = 0; a < tbDatasource.dataKeys.length; a++) {\n var dataKey = tbDatasource.dataKeys[a];\n var labelCellId = 'labelCell' + a;\n var cellId = 'cell' + a;\n table.append(\"\" + dataKey.label +\n \"\");\n var labelCell = $('#' + labelCellId, table);\n self.ctx.labelCells.push(labelCell);\n var valueCell = $('#' + cellId, table);\n self.ctx.valueCells.push(valueCell);\n }\n self.onResize();\n}\n\nfunction getDatasourceKeys (datasource) {\n let entityService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('entityService'));\n if (datasource.entityId && datasource.entityType) {\n entityService.getEntityKeys({entityType: datasource.entityType, id: datasource.entityId}, '', 'timeseries').subscribe(\n function(data){\n if (data.length) {\n subscribeForKeys (datasource, data);\n }\n });\n }\n}\n\nfunction subscribeForKeys (datasource, data) {\n let eventsRegVals = self.ctx.settings[eventsReg];\n if (eventsRegVals && eventsRegVals.length > 0) {\n var dataKeys = [];\n data.sort();\n data.forEach(dataValue => {eventsRegVals.forEach(event => {\n if (dataValue.toLowerCase().includes(event.toLowerCase())) {\n var dataKey = {\n type: 'timeseries',\n name: dataValue,\n label: dataValue,\n settings: {},\n _hash: Math.random()\n };\n dataKeys.push(dataKey);\n }\n })});\n\n if (dataKeys.length) {\n updateSubscription (datasource, dataKeys);\n }\n }\n}\n\nfunction updateSubscription (datasource, dataKeys) {\n var datasources = [\n {\n type: 'entity',\n name: datasource.aliasName,\n aliasName: datasource.aliasName,\n entityAliasId: datasource.entityAliasId,\n dataKeys: dataKeys\n }\n ];\n \n var subscriptionOptions = {\n datasources: datasources,\n useDashboardTimewindow: false,\n type: 'latest',\n callbacks: {\n onDataUpdated: (subscription) => {\n self.ctx.data = subscription.data;\n self.onDataUpdated();\n }\n }\n };\n \n processDatasources(datasources);\n self.ctx.subscriptionApi.createSubscription(subscriptionOptions, true).subscribe(\n (subscription) => {\n self.ctx.defaultSubscription = subscription;\n }\n );\n}\n\nself.onDestroy = function() {\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\t\n dataKeysOptional: true,\n singleEntity: true\n };\n}\n\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-gateway-events-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Function Math.round\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.826503672916844,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"eventsTitle\":\"Gateway Events Form\",\"eventsReg\":[]},\"title\":\"Gateway events\",\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null, + "tags": [ + "router", + "bridge", + "hub", + "access point", + "relay", + "opc ua", + "opc-ua", + "modbus", + "bacnet", + "odbc", + "ftp", + "snmp", + "mqtt", + "xmpp", + "ocpp", + "ble", + "bluetooth" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/gateway_general_chart_statistics.json b/application/src/main/data/json/system/widget_types/gateway_general_chart_statistics.json new file mode 100644 index 0000000000..7e1974d5b7 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/gateway_general_chart_statistics.json @@ -0,0 +1,42 @@ +{ + "fqn": "gateway_widgets.gateway_general_chart_statistics", + "name": "Gateway general chart statistics", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Provides a real-time chart with statistical data. Displays one of the following: bytes sent to the platform, bytes received from the platform, CPU, RAM, Disk, etc.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() { \n};\n\nself.onDataUpdated = function() {\n};\n\nself.onLatestDataUpdated = function() {\n};\n\nself.onResize = function() {\n};\n\nself.onEditModeChanged = function() {\n};\n\nself.onDestroy = function() {\n};\n\nself.typeParameters = function() {\n return {\n hasAdditionalLatestDataKeys: false\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-flot-line-widget-settings", + "dataKeySettingsDirective": "tb-flot-line-key-settings", + "latestDataKeySettingsDirective": "tb-flot-latest-key-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"legend\":{\"show\":true,\"position\":\"nw\",\"backgroundColor\":\"#f0f0f0\",\"backgroundOpacity\":0.85,\"labelBoxBorderColor\":\"rgba(1, 1, 1, 0.45)\"},\"decimals\":1,\"stack\":false,\"tooltipIndividual\":false},\"title\":\"Gateway general chart statistics\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}" + }, + "tags": [ + "router", + "bridge", + "hub", + "access point", + "relay", + "opc ua", + "opc-ua", + "modbus", + "bacnet", + "odbc", + "ftp", + "snmp", + "mqtt", + "xmpp", + "ocpp", + "ble", + "bluetooth" + ] +} diff --git a/application/src/main/data/json/system/widget_types/gateway_general_configuration.json b/application/src/main/data/json/system/widget_types/gateway_general_configuration.json new file mode 100644 index 0000000000..e2129c0271 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/gateway_general_configuration.json @@ -0,0 +1,38 @@ +{ + "fqn": "gateway_widgets.gateway_general_configuration", + "name": "Gateway general configuration", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Allows to create or edit general gateway configuration.", + "descriptor": { + "type": "latest", + "sizeX": 11, + "sizeY": 8, + "resources": [], + "templateHtml": "", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n if (self.ctx.datasources && self.ctx.datasources.length) {\n self.ctx.$scope.entityId = self.ctx.datasources[0].entity.id;\n }\n};\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n singleEntity: true\n };\n}", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Gateway configuration\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":500},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false}" + }, + "tags": [ + "router", + "bridge", + "hub", + "access point", + "relay", + "opc ua", + "opc-ua", + "modbus", + "bacnet", + "odbc", + "ftp", + "snmp", + "mqtt", + "xmpp", + "ocpp", + "ble", + "bluetooth" + ] +} diff --git a/application/src/main/data/json/system/widget_types/gateway_logs.json b/application/src/main/data/json/system/widget_types/gateway_logs.json new file mode 100644 index 0000000000..45b380e544 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/gateway_logs.json @@ -0,0 +1,39 @@ +{ + "fqn": "gateway_widgets.gateway_logs", + "name": "Gateway logs", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Allows to monitor and analyze the gateway logs.", + "descriptor": { + "type": "timeseries", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "\n \n", + "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", + "controllerScript": "self.onInit = function() {\n};\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n singleEntity: true\n };\n}", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-gateway-logs-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":0,\"realtime\":{\"realtimeType\":0,\"timewindowMs\":86400000,\"quickInterval\":\"CURRENT_DAY\",\"interval\":300000},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Gateway logs\",\"showTitleIcon\":false,\"dropShadow\":false,\"enableFullscreen\":true,\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false,\"useDashboardTimewindow\":false,\"displayTimewindow\":true}" + }, + "tags": [ + "router", + "bridge", + "hub", + "access point", + "relay", + "opc ua", + "opc-ua", + "modbus", + "bacnet", + "odbc", + "ftp", + "snmp", + "mqtt", + "xmpp", + "ocpp", + "ble", + "bluetooth" + ] +} diff --git a/application/src/main/data/json/system/widget_types/gauge.json b/application/src/main/data/json/system/widget_types/gauge.json new file mode 100644 index 0000000000..f5494f8b4a --- /dev/null +++ b/application/src/main/data/json/system/widget_types/gauge.json @@ -0,0 +1,28 @@ +{ + "fqn": "digital_gauges.gauge_justgage", + "name": "Gauge", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Preconfigured gauge to display any value reading. Allows to configure value range, gradient colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 4, + "sizeY": 3, + "resources": [], + "templateHtml": "", + "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-digital-gauge-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#999999\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":36,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#666666\"},\"neonGlowBrightness\":0,\"decimals\":0,\"dashThickness\":0,\"gaugeColor\":\"#eeeeee\",\"showTitle\":true,\"gaugeType\":\"arc\"},\"title\":\"Gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" + }, + "externalId": null, + "tags": [ + "measure", + "indicator", + "dial", + "scale", + "instrument" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/getting_started.json b/application/src/main/data/json/system/widget_types/getting_started.json new file mode 100644 index 0000000000..f845b948fa --- /dev/null +++ b/application/src/main/data/json/system/widget_types/getting_started.json @@ -0,0 +1,27 @@ +{ + "fqn": "home_page_widgets.getting_started", + "name": "Getting started", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the getting started instructions.", + "descriptor": { + "type": "static", + "sizeX": 7.5, + "sizeY": 6.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "", + "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Getting started\",\"dropShadow\":true}" + }, + "externalId": null, + "tags": [ + "hello world", + "beginning", + "kickoff", + "tutorial" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/google_map.json b/application/src/main/data/json/system/widget_types/google_map.json new file mode 100644 index 0000000000..8b27b9b679 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/google_map.json @@ -0,0 +1,29 @@ +{ + "fqn": "maps_v2.google_maps", + "name": "Google Map", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the location of the entities on Google Maps. Requires the Google map key to work properly. Highly customizable via custom markers, marker tooltips, and widget actions.", + "descriptor": { + "type": "latest", + "sizeX": 8.5, + "sizeY": 6, + "resources": [], + "templateHtml": "", + "templateCss": ".error {\n color: red;\n}\n.tb-labels {\n color: #222;\n font: 12px/1.5 \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n text-align: center;\n width: 200px;\n white-space: nowrap;\n}", + "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('google-map', false, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-map-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.9430343126300238,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.1784452363910778,\"funcBody\":\"return \\\"colorpin\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]},{\"type\":\"function\",\"name\":\"Second point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.773875863339494,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.405822538899673,\"funcBody\":\"return \\\"thermometer\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"google-map\",\"gmApiKey\":\"AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q\",\"gmDefaultMapType\":\"roadmap\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#fe7568\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'thermometer') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"useClusterMarkers\":false,\"zoomOnClick\":true,\"maxClusterRadius\":80,\"animate\":true,\"spiderfyOnMaxZoom\":false,\"showCoverageOnHover\":true,\"chunkedLoading\":false,\"removeOutsideVisibleBounds\":true,\"useIconCreateFunction\":false},\"title\":\"Google Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" + }, + "externalId": null, + "tags": [ + "mapping", + "gps", + "navigation", + "geolocation", + "satellite", + "directions" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/ground_temperature_card.json b/application/src/main/data/json/system/widget_types/ground_temperature_card.json new file mode 100644 index 0000000000..56b1b6ad1c --- /dev/null +++ b/application/src/main/data/json/system/widget_types/ground_temperature_card.json @@ -0,0 +1,37 @@ +{ + "fqn": "ground_temperature_card", + "name": "Ground temperature card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest ground temperature telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Ground temperature', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Ground temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"thermostat\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#234CC7\"},{\"from\":-20,\"to\":0,\"color\":\"#305AD7\"},{\"from\":0,\"to\":10,\"color\":\"#7191EF\"},{\"from\":10,\"to\":20,\"color\":\"#FFA600\"},{\"from\":20,\"to\":30,\"color\":\"#F36900\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#234CC7\"},{\"from\":-20,\"to\":0,\"color\":\"#305AD7\"},{\"from\":0,\"to\":10,\"color\":\"#7191EF\"},{\"from\":10,\"to\":20,\"color\":\"#FFA600\"},{\"from\":20,\"to\":30,\"color\":\"#F36900\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Ground temperature card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "soil temperature", + "terrestrial temperature", + "subsurface temperature", + "earth temperature", + "below surface temperature", + "surface temp", + "soil warmth", + "land temperature", + "geothermal reading", + "ground warmth" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/ground_temperature_card_with_background.json b/application/src/main/data/json/system/widget_types/ground_temperature_card_with_background.json new file mode 100644 index 0000000000..c4e87a4c74 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/ground_temperature_card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "ground_temperature_card_with_background", + "name": "Ground temperature card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest ground temperature telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Ground temperature', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Ground temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"thermostat\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#224AC2\"},{\"from\":-20,\"to\":0,\"color\":\"#2B54CE\"},{\"from\":0,\"to\":10,\"color\":\"#6083EC\"},{\"from\":10,\"to\":20,\"color\":\"#F89E0D\"},{\"from\":20,\"to\":30,\"color\":\"#F77410\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#224AC2\"},{\"from\":-20,\"to\":0,\"color\":\"#2B54CE\"},{\"from\":0,\"to\":10,\"color\":\"#6083EC\"},{\"from\":10,\"to\":20,\"color\":\"#F89E0D\"},{\"from\":20,\"to\":30,\"color\":\"#F77410\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Ground temperature card with background\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "soil temperature", + "terrestrial temperature", + "subsurface temperature", + "earth temperature", + "below surface temperature", + "surface temp", + "soil warmth", + "land temperature", + "geothermal reading", + "ground warmth" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/ground_temperature_chart_card.json b/application/src/main/data/json/system/widget_types/ground_temperature_chart_card.json new file mode 100644 index 0000000000..897c92a9b0 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/ground_temperature_chart_card.json @@ -0,0 +1,40 @@ +{ + "fqn": "ground_temperature_chart_card", + "name": "Ground temperature chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a ground temperature data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'temperature', label: 'Ground temperature', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: '°C', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'temperature', '°C', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Ground temperature\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"°C\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#234CC7\"},{\"from\":-20,\"to\":0,\"color\":\"#305AD7\"},{\"from\":0,\"to\":10,\"color\":\"#7191EF\"},{\"from\":10,\"to\":20,\"color\":\"#FFA600\"},{\"from\":20,\"to\":30,\"color\":\"#F36900\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"°C\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"°C\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Ground temperature\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"device_thermostat\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "soil temperature", + "terrestrial temperature", + "subsurface temperature", + "earth temperature", + "below surface temperature", + "surface temp", + "soil warmth", + "land temperature", + "geothermal reading", + "ground warmth" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/ground_temperature_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/ground_temperature_chart_card_with_background.json new file mode 100644 index 0000000000..aa853de8a5 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/ground_temperature_chart_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "ground_temperature_chart_card_with_background", + "name": "Ground temperature chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a ground temperature data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'temperature', label: 'Ground temperature', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: '°C', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'temperature', '°C', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Ground temperature\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"°C\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#224AC2\"},{\"from\":-20,\"to\":0,\"color\":\"#2B54CE\"},{\"from\":0,\"to\":10,\"color\":\"#6083EC\"},{\"from\":10,\"to\":20,\"color\":\"#F89E0D\"},{\"from\":20,\"to\":30,\"color\":\"#F77410\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"°C\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"°C\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Ground temperature\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"device_thermostat\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "soil temperature", + "terrestrial temperature", + "subsurface temperature", + "earth temperature", + "below surface temperature", + "surface temp", + "soil warmth", + "land temperature", + "geothermal reading", + "ground warmth" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/here_map.json b/application/src/main/data/json/system/widget_types/here_map.json new file mode 100644 index 0000000000..1d60a8ca11 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/here_map.json @@ -0,0 +1,29 @@ +{ + "fqn": "maps_v2.here_map", + "name": "HERE Map", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the location of the entities on HERE Maps. Requires the HERE map key to work properly. Highly customizable via custom markers, marker tooltips, and widget actions.", + "descriptor": { + "type": "latest", + "sizeX": 9.5, + "sizeY": 6, + "resources": [], + "templateHtml": "", + "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('here', false, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-map-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.9430343126300238,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.1784452363910778,\"funcBody\":\"return \\\"colorpin\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]},{\"type\":\"function\",\"name\":\"Second point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.773875863339494,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.405822538899673,\"funcBody\":\"return \\\"thermometer\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"here\",\"gmApiKey\":\"AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q\",\"gmDefaultMapType\":\"roadmap\",\"mapProvider\":\"HERE.normalDay\",\"useCustomProvider\":false,\"customProviderTileUrl\":\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\"mapProviderHere\":\"HERE.normalDay\",\"credentials\":{\"useV3\":true,\"apiKey\":\"kVXykxAfZ6LS4EbCTO02soFVfjA7HoBzNVVH9u7nzoE\"},\"mapImageUrl\":\"data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg==\",\"tmApiKey\":\"84d6d83e0e51e481e50454ccbe8986b\",\"tmDefaultMapType\":\"roadmap\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#fe7569\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'thermometer') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"coordinates\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.5,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"useClusterMarkers\":false,\"zoomOnClick\":true,\"maxClusterRadius\":80,\"animate\":true,\"spiderfyOnMaxZoom\":false,\"showCoverageOnHover\":true,\"chunkedLoading\":false,\"removeOutsideVisibleBounds\":true,\"useIconCreateFunction\":false},\"title\":\"HERE Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" + }, + "externalId": null, + "tags": [ + "mapping", + "gps", + "navigation", + "geolocation", + "satellite", + "directions" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_2_1_elliptical_tank.json b/application/src/main/data/json/system/widget_types/horizontal_2_1_elliptical_tank.json new file mode 100644 index 0000000000..93087939ac --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_2_1_elliptical_tank.json @@ -0,0 +1,36 @@ +{ + "fqn": "horizontal_2_1_elliptical_tank", + "name": "Horizontal 2:1 elliptical tank", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Widget indicates the level of liquid in Horizontal 2:1 elliptical tank.", + "descriptor": { + "type": "latest", + "sizeX": 4, + "sizeY": 4, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.liquidLevelWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.liquidLevelWidget.update();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n}\n\nself.actionSources = function() { \n return { \n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false \n } \n };\n}", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-liquid-level-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-liquid-level-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.floor(Math.random() * 101);\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"tankSelectionType\":\"static\",\"selectedShape\":\"Horizontal 2:1 Elliptical\",\"shapeAttributeName\":\"tankShape\",\"tankColor\":{\"type\":\"range\",\"color\":\"#242770\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E73535DE\"},{\"from\":20,\"to\":null,\"color\":\"#242770\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E73535DE';\\n }\\n}\\nreturn '#242770';\"},\"datasourceUnits\":\"%\",\"layout\":\"percentage\",\"volumeSource\":\"static\",\"volumeConstant\":500,\"volumeAttributeName\":\"volume\",\"volumeUnits\":\"L\",\"volumeFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"volumeColor\":\"rgba(0, 0, 0, 0.18)\",\"units\":\"%\",\"widgetUnitsSource\":\"static\",\"widgetUnitsAttributeName\":\"units\",\"liquidColor\":{\"type\":\"range\",\"color\":\"#7A8BFF\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E27C7CDE\"},{\"from\":20,\"to\":null,\"color\":\"#7A8BFF\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FF0000DE\"},{\"from\":20,\"to\":null,\"color\":\"rgba(0,0,0,0.87)\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FF0000DE';\\n }\\n}\\nreturn '#000000DE';\"},\"showBackgroundOverlay\":true,\"backgroundOverlayColor\":{\"type\":\"range\",\"color\":\"rgba(255, 255, 255, 0.76)\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FFEFEFDE\"},{\"from\":20,\"to\":null,\"color\":\"#FFFFFFC2\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FFEFEFDE';\\n }\\n}\\nreturn '#FFFFFFC2';\"},\"showTooltip\":true,\"showTooltipLevel\":true,\"tooltipUnits\":\"%\",\"tooltipLevelDecimals\":0,\"tooltipLevelFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipLevelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.76)\",\"rangeList\":[],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"showTooltipDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":3,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Liquid level\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"titleFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1.5\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"showTitleIcon\":false,\"titleIcon\":\"water_drop\",\"iconColor\":\"#5469FF\",\"decimals\":0,\"enableDataExport\":false,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" + }, + "externalId": null, + "tags": [ + "reservoir", + "container", + "vessel", + "storage unit", + "cistern", + "canister", + "vat", + "basin", + "repository", + "bin", + "hopper" + ] +} diff --git a/application/src/main/data/json/system/widget_types/horizontal_air_quality_index_card.json b/application/src/main/data/json/system/widget_types/horizontal_air_quality_index_card.json new file mode 100644 index 0000000000..60bd64f347 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_air_quality_index_card.json @@ -0,0 +1,32 @@ +{ + "fqn": "horizontal_air_quality_card", + "name": "Horizontal air quality index card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAmVBMVEUAAADf39/f39/g4ODg4ODf39/////k5OSAwyzg4OCg0mHy8vLQ6a+srKyQkJCQy0bf8Mrv9+Xj4+POzs5YWFiv2Xv5+fn3+/KIxznb29u2trbA4ZXV1dXHx8fCwsK8vLzn5+fIyMi43Yio1m47Ozt0dHTn9NjH5aNmZmZKSkrY7L2enp6YzlOCgoJLS0vt7e2dnZ3U1NQhISGmKTEOAAAABnRSTlMAIEDfv1C6kOEmAAADrUlEQVR42u3aa3OaQBiG4bRJn7yynEEOAh7xrEna///jykKrtjZK2iZDMs/1QWGXD3sP4OjIDREREREREREREREREREREREREREREdFb+vTu3Wifb+/fO//2S9VxH+HdU/efb+4UPoDo7qaHD6HHkI5hSNcwpGsY0jUM6Zo/hmSJGB5O+FvAylErbAsHlpVbUGgU+c/REa57g5CFGDNxYhzNS4U8V0oBVlisttWGX+Up2HZeFGvlQ0/ZllK+zrYtvV8f1IpnujgRuxcOcJfZJj6OXQzBAjDFxFH4tK2WaZdPwKpAHhYhQn8/Lwu7GrXLQan2lg6xwlWIh9Xa8h9W2yK05q0yDBEDB+4sMfCbmSSHYyvOBnqFRpt7xD0NGc1VqEMs3VTAX9chxTa0dIhfzW0f/DrExje9a833Vol9NXid54hhLvCDm8gh68ATEReaI7uN26/2roZ4bm2zEw8LD435OiyLHyF7G9Y81yEP+b4JWcN/CHEIWesQuygwL9uE9CXDkWksl2chpsxkWm80V/xUjKsh8tMShkgGzS+Bp70OqXfC0lfrVemvVuutDlFrG6F1CMF8FVp5OK9qihBXeeLA83DCPAvZySJJdIEhS1RikfhayMZsxIh3mSEezqknu3r5ZcQv1dlRaEWvZyfiLC6ELGSHaZ3gHK4w90LIuUxctPK0xd/KRIxsJkn8fEhfTLhivCzES+SEg1fXl35zzTwf4ugLIxHvRSHxtH+UxXhVx3s9k+mzIaYk1VocyXTABlrShHTqK8pSpldCZtJIgGlz2EIE10JmxonNpRtXPf4+oPAX3PoTdScm4P0xxBNZeBV9FlxJvDqtfz3EOUpkgdqgiVF4TFGLIkS9QA/0EAEYW2NEadpMRE15W31xMkOcemXnIcfdTGb6RWZTRxzvQsi5jZiHkDwP0skkqLaGGKbDUTQYBnk+CUaPwSgCgrEajVKooJ4IJiO0Fk9FxKhXNvtTiPNjFW79yWY6UpniBSE7xxD3EPI1GKhJMA6akPHXtNquxsZpMJzUIb00rUImg/FEVblDhfZiL27e0YbnuY7M4va/R7IkWaJhBekwGOSTodJnJBilw4EaTIJq7HE0ngTQIZE+I+ORpSfy4RCvyXOm/+eHVTrG875OUryNfw+JokuTCm10IqSLGNI1DOkahnQNQ7qmd3P3Af5mB9Rd9cDAB/ijPaoeGLj5cuvfv3e3nz/SQzVEREREREREREREREREREREREREREREb+Q79h0E444zJWUAAAAASUVORK5CYII=", + "description": "Displays the latest air quality index telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'air', label: 'Air Quality Index', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Air Quality Index\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 320) {\\n\\tvalue = 320;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:weather-windy\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#80C32C\"},{\"from\":50,\"to\":100,\"color\":\"#FFA600\"},{\"from\":100,\"to\":150,\"color\":\"#F36900\"},{\"from\":150,\"to\":200,\"color\":\"#D81838\"},{\"from\":200,\"to\":300,\"color\":\"#8D268C\"},{\"from\":300,\"to\":null,\"color\":\"#6F113A\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#80C32C\"},{\"from\":50,\"to\":100,\"color\":\"#FFA600\"},{\"from\":100,\"to\":150,\"color\":\"#F36900\"},{\"from\":150,\"to\":200,\"color\":\"#D81838\"},{\"from\":200,\"to\":300,\"color\":\"#8D268C\"},{\"from\":300,\"to\":null,\"color\":\"#6F113A\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal air quality card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"AQI\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "air", + "aqi", + "pollution", + "emission", + "smog" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_air_quality_index_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_air_quality_index_card_with_background.json new file mode 100644 index 0000000000..0947ac3772 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_air_quality_index_card_with_background.json @@ -0,0 +1,32 @@ +{ + "fqn": "horizontal_air_quality_card_with_background", + "name": "Horizontal air quality index card with background", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAACE1BMVEUAAABoaWifoZuvr6+AgYCXnJdzd3FZXVWtr6OKjIinqaNrb2iNkIuvsaqZnZZgYVutsKtgYVvm5+Xk5eTh4eDd3t3j5OLl5uTe393f4N/p6eji4+Lg4N/q6+nc3dzo6Ofi4uF8wyLb3Nro6eisrqmusKqbnpfa29mcn5iYm5Sgo52rraeZnJWXmpOdoJmEhoGFh4PDxMDa29uChICipZ+anZatr6qHiYR5fHZtcGqkpqCZm5h4enV3eXSeoZqqrKeJjIZ1eHNpa2fd3dxwc25rb2uIioW0trG715mGiINrbWiAgn+3ubSWmZKVy1G1t7RucWxmamWJxzq6u7iPkYxobWmvsauKjYhydXFqbmmhpJ18fnmrramfopuCg4JhY12TlpGMj4qmqaOSlI57fXdscWxmbGioq6V9f36vsK6ur6ukpKTb3Nylp6KbnJuXmpeQk42w1IKWzFKpqqieoJ2cnpqCxS5dXlrIycjAwb+Xl5dudG/Pz86XmZRkZl9bXFdPT0/T1NO0tLOwsq90d3F/gnxydG6iz2pCQkLI27FmaGFfYFrV1tTW4Mq+2pyVl5PU3cjb4NW+v71pcW2k0GtXWlTZ2djFxsSPyUbE26dRUlA3Nze4ubbN3rW21o6p0XWfo6FnaGeXzFPa487K3bPh5trd49fMzMvDxMLT4MHO3Lydzl81NTXB16SbzV0gICCz1YmQMTWOAAAAEnRSTlMAICAgICAgICDKQEBQ39/fv78M9TdhAAAOO0lEQVR42uzBgQAAAACAoP2pF6kCAAAAAAAAAJg9OlZtGAbCABxcAiW0pQ9hGcuSPUkaPDidumUrLoga0mQoCEynPkdfuf+dXIG7eemkT9KdThlyh7Msy7Isy7Isy/7TzXa3HAgSK4piT4dCgWqL/brA2mhHDvdjGIUYQ6AURDKKkd6T+EBLCFeW2PBWlkqp2fih66phqLXs+36+zKSfcU8MeO+bXxMbom7oVupFxYFZOrbS1moitfyUEhc6D3eYA90FDICVUOVizTPyVIlzcQxVqqNil4n+x8r2dL5+N8as+sfGi8EIyxDLABO6hz/tU+Ac++dg2ZKWIaRsW2y6UKUPu8fYd3AuUHAltuIUBJLAjvCjcGkQfIcj1gczX5jhdH55fXp+v8qp8WC4feYB7aN1Uusfzsvup2koDOO99Xar3Wan7aERjaLidyKJAhcyg2PJXJwx0Di8EBTnBmyMZYTOXVAThh8XCgxRQFGU6L/o855z1jIFRX60b99TBjy/nnOWEW/faYDMEulANA1wonaQADng4NGlB0Dlw0MKciEYRaNnPIiKgpb3PldaO8wGTEhiGNzveJDLpDvtfttOp9Inr1/Hc79PXyRB7Sk8/sLZdoqBSXvYIePL2BRaXKlF9VcTN+iAgZwGIATiMzNnZuJcRZgoFHwQoQZvIPuNG6gAD5sKP0mKDnElB/EaafH02qVTzzLpc/bFEeJmd+dMR3vhOhgaau4GPP6O+MuHRC6XSuVmLsvIKD7+SKwjfzMgPJ1yFiAgeBknE4lCDqeRP3H+f5kGb968uZOoDQ4Pf5z8yJkYnLjyuNaXuL2ycsdnZaWvBkZH+fzfxbclaHYj4dPXh2NvTkmPuIKHD4/j1aP/zRFgGQHNVCM+qqqapq5rwUDA8AgEQkFN101TVfEKE98NBQ5CSFQfDErtZ+LwgAiYeFQ5elCRkN4qAkwZ1QgIDeEBCzUaAQc1gYGUoIvXnY8DEqEte2Vnvn1bACMQ1OHRKoKkOk9KDkJDM4VFOByORMlECwYRYx/hcQDPYBcu4w3zwQOFduyjlZ3hRAMhKrt44V4TCyJmtEXEmxI/C6ZDVSEBDh8+DBOuKlRC//DYB9ceEMowcZvS/w0hhNqCxUUigFJKEYJEfA/dxGyQhSAsXsZNBLstoT0kgqEgCIkTIzD8kIAIPkwkjhwAePC9zj0if4iE/PmQHr4IECZBIeKvn73iy1N4tMJFcgo0DiZiAeNvIjJWkL8bSI9jJELQnsdGAUi4N0GR/688zQGIXAN9rQGPWFT/rQGPkGZGd19azSi0z6XIMXhIEdopeBGHkvrR5cFv74trKULhn+kSIhkKnfkiW9/gY0C1krKswld0OBJ2AZ5SJJud7jLnI5zw6B0hEi0/gYnMGOQ7xBMhleaehwhMfMQE7AtNwptLmUwqk1HI41KftYMttv6dJT9bPr2NNuvr17a2NsvKLiemcmgqhlE5Ydj2dK3mVt9FqtVIZKRcrZYgUhopq/Pz2tu3wS/0N3VVisBDmoitQvPmqdQXPqF6bO4caUuxTU2wGcvnP1GDe3VUIQLSCv8PoWbwB2zxi7VlWAtsoXkHLKdSlp21GznLmkpY55cTjrFc6eltPLftbH//9pPt6r0yiZSdWUd1Z91yyZ3NPHe61jyRcFNkDCKAdKIQAVxjnLFxP/mn70U50og6Y3lNpC8ykCSFARbTNP4L7qcJBRpDEPmN92zBH0z0ti0bEMlCbTlhVVwukkg5WRIpOZGRtIupgMiI+rPkoFm719UI9rhyRqL+woLIMdHytSVN6kk2vrDlaVDacc0nxlixafRhaSnJFoWILhjq7u6+2a3AY2jSE9l4z1laZBvG1oYh6HWdRoJE0PfYRrZ32jGcijvdI0TcSMl1op6ISyL9tZq21oCIRh/FvOkAQoQuEIEJMcfyYrnzSYiNr656IjpYZAPsBa4In0fdTJKDL3L9JqFM4kO2L8KarAbWMaEGUWkYRqpHiGDgNCon3KntytSUm7Ltrv55dyTslNWmiLo265TvOGv9XWvPHUpCIqTRBi5cuEBrC4MxEjGFSJ0ltXpd20FMiuhEnQ28YHNoXjNW18EqG+AipqmboHDr1q2rV5XJs2cLBU9kaUHw2fi8mF9nmJMAYQBUfj2Rso3AiYAA776RcPXddjWqekTRm/N4vDhIJEoeY8JjTIigJxGYAIq9iJX/rUVEFxqIOsdir4vF17r+giX5HbqaEMHPq6D9XGdn5y0F/6UVCqM8piiokjx7H5Aj30M4ASlCzzudDkcRH+++noiuSXSYwgMW8CCgcAwtNklUiuQZG8j/YMXNFhHAPdQke6XOsVWTBEyAKzNVEhF0XTwHfrFn9r9JA2Ecr4nRGE2Ms0iJjbGLEd+SGd8AGSrYGodRYUgENW6+oNZG54S9ILplyLY4pptTNzXGJb7Et2j8E/0+d1dOCL7+zKfdvbTX7T597roeKFs2D2y+CRHOW0w0SXfbn6B3X/7W4YrAyAcT9E+IwJR57PILERpmfmQaNSMmjcl1NAFeI0UVHMXgAeyGfzVOqOoD48RGlURwCCKDqiuiq/rW8WPHdu9WNofDe8PX2wTvF59K+t//jYhGIjAR6PSjy5CQqQiInwERFNvbPSSC+0si/eh1v7GItCayEajEN+P55OSkYXxWP0MAQOg5F9EZx8cJha2MN7X9H14pIkFFhmS9K8JiQuwiEdKhoQVo8i7WiWxkIipn1hD0q48HjQcqQASZCGJP9J4F48oO+lToXJvgy+03b97cFrwQB9ubObRPw+Nnkelp4eEungB/aGGucw8pgtSNCBv7jzdufE7P04e4DEcgooqBg+Is+GoM6mo/OzwLH12HiE9j9AbBZYU+nuiTIt2SQeMTP+gwGezTC/yAqrapH1Ledu/Y8Pp2iAzPDGv+YtHn8/t9/nlNGxPzGDARFhK/8BBDi4twlUmju/8ETQA2pKSIDuDnI6j3j7uN7sXJQaMfdYhonFuRSKTzsnIPHxHNHJTrSrnAfGE8qokU51ILJ0+mULLb7AU7rzt2qjhXTuXHTjrzHk95eMxxir7psjNfyJfLtuM+WqUIjwef7JCqE3m8aBi48STwjU0NJqKTyGfDeMxEFo0Tuj47aYDBz4g7RDycU5VKJRJRZoi8XMy7PO++bbyjAol4iymnvZy6m2rz2l574e7cAspzKefux1d5e96jlYcXinNF35hdGLbHygUtP+2GZCNENDG2OBtqEYEtg1RmMbgoV2vonNpwRUbV2Vk8rb9rgGsc9lyYyGQyUWWmF1xdK5CrnP7BwdeiWEgt2CmnWLb9jtdrp/ILtuN3yin7lTOd/3C/rGkQmXecOW3YCU8X7PKrfJ6tyxkqRFhIOPRuchhaJIJGLmhWjy7gFgIxCb92H3UtDm/YcPi0OWFmKkovcXFtE+pXaUhcPt51m7C5zG6O5pKy51CpiVCf+KsWJNB9eLDKhsNcpDkNwZDwqrCABnEolDAnMsqlXqx4b6z9JV43k/h8UkTVG0Tmx1jNJ+8zWpCJiwdQRiK/MuEaUsRTr1OzaCeOhEKJhKlcugVIZN3af4X/uxM3qPbXWFnebbkaEYiRzUX038WDBwB4BLICC/JwRaCi3DoF9q8ToHcuf9aQIvV/CEpSRHSJGkkPwESamjTODY8UoURGQxCIWzCBCFa8B9Y15U8a4h1dwjtMHu6wEV0Stg0iZAKah4MjJwTLRKVm4QddsXg8nlVOXTh94cIt/BNmW4MIdmxA5jJq8BAiEk12sm6+NmukSRMdAg0eQkLGABuviGj4XRJTyWQsrmDdfuj06bn1Luhe0+A05M1FxN/hfZQeP58UPeMi+i9ocIcGQEpbvce8/+IUSEIEC97AkVPF9Q3A50+sJxH5gRV+eJkKWk2Ed6kRIeKqqPUeviYe3MUV8bsi5+JTI1NTI0nl9JEjgUBX185LeeKg4Ny5g9joW7hzBEoogn0ACatup+8j9gwM3Az3CcLhgUKY5TcL20ABe6EQlvTRPjDAyjcHCtSIvp+TnKEjdNUA/V4GfuE9cJySvr6tfXfuzcz04hGFz4CQmKPVkZ4p7BAJBHaCDkq6ApAKANhhQX+IoExyGlDCznR1dVyLZjK5XClXMp88eVIqlaKmZVmJUqYzeDlIdHZGSyXTNBMJliLPZSoTCTz4zUoEjcDZOuhIsDMSzUyYExMmkQgBzOeQSzyWHOnpGar2jIxUn40ih8iIcigAiY7du7HI6oAM2RCBrp07A3CAEUEpK+EADjK/ANrsPov3HNN6YqWvpNOkEs1lrbQVMqOdnUFs6FGllMtZIZI1zRwI4YJQDiYZahOEC7O5TA5U4fZRsoBvSFhkaQ9Z2LLZWHIKIqPVoZ6e6uizZ0M9I6gqK3Z27IYGIBWCfHhKne9ibqTHk4CAxiMuDZZKOYvIQQUimdwVKFmJaKSTiEQqGXY+m6WEpWYmgTyXmIhGI0G4NMKuqkAEFgymEYvFslkUqASPoeroKETggQyRWaOs7IDF+Pg12qHCOM8zqEBJIEpwwc69OnBpsITbnAZXAImkkyikrShMIgAi2SvJJE5n05aVBtmEmYvH06EQRIQuix4LIL8oGq1UMhh/uPvCgUhCKIucBlZ1qPpsaJREEJEqIrNSUVatgAPiivF57RhDxAe7EBIp5UKLGaI+HilZWfQSnUwmoZAz0WuQzlAvsWEK4XSSTpINRGIJ04rF0lkrUUETQDIsJQWy4B4YWFY8Rha4WCRxiPCBNTQEkWp19OVLCslqeIClYBlY+h8sW9KcZRLUft+mOUt+w/IGlBYtWrRo0aJFixYtWrRo0eJHe3BIAAAAACDo/2tfmAAAAAAAAAAAGAUsOGduMAlN0AAAAABJRU5ErkJggg==", + "description": "Displays the latest air quality index telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'air', label: 'Air Quality Index', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Air Quality Index\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 320) {\\n\\tvalue = 320;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:weather-windy\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#7CC322\"},{\"from\":50,\"to\":100,\"color\":\"#F89E0D\"},{\"from\":100,\"to\":150,\"color\":\"#F77410\"},{\"from\":150,\"to\":200,\"color\":\"#DE2343\"},{\"from\":200,\"to\":300,\"color\":\"#7B287A\"},{\"from\":300,\"to\":null,\"color\":\"#791541\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#7CC322\"},{\"from\":50,\"to\":100,\"color\":\"#F89E0D\"},{\"from\":100,\"to\":150,\"color\":\"#F77410\"},{\"from\":150,\"to\":200,\"color\":\"#DE2343\"},{\"from\":200,\"to\":300,\"color\":\"#7B287A\"},{\"from\":300,\"to\":null,\"color\":\"#791541\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal air quality card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"AQI\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "air", + "aqi", + "pollution", + "emission", + "smog" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_bar.json b/application/src/main/data/json/system/widget_types/horizontal_bar.json new file mode 100644 index 0000000000..27ec8d2367 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_bar.json @@ -0,0 +1,21 @@ +{ + "fqn": "digital_gauges.horizontal_bar_justgage", + "name": "Horizontal bar", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Preconfigured gauge to display any value reading as a horizontal bar. Allows to configure value range, gradient colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 7, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-digital-gauge-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#999999\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":18,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#666666\"},\"neonGlowBrightness\":0,\"decimals\":0,\"dashThickness\":0,\"gaugeColor\":\"#eeeeee\",\"showTitle\":true,\"gaugeType\":\"horizontalBar\"},\"title\":\"Horizontal bar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_capsule_tank.json b/application/src/main/data/json/system/widget_types/horizontal_capsule_tank.json new file mode 100644 index 0000000000..426bbdc744 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_capsule_tank.json @@ -0,0 +1,36 @@ +{ + "fqn": "horizontal_capsule_tank", + "name": "Horizontal capsule tank", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Widget indicates the level of liquid in Horizontal capsule tank.", + "descriptor": { + "type": "latest", + "sizeX": 4, + "sizeY": 4, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.liquidLevelWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.liquidLevelWidget.update();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n}\n\nself.actionSources = function() { \n return { \n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false \n } \n };\n}", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-liquid-level-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-liquid-level-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.floor(Math.random() * 101);\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"tankSelectionType\":\"static\",\"selectedShape\":\"Horizontal Capsule\",\"shapeAttributeName\":\"tankShape\",\"tankColor\":{\"type\":\"range\",\"color\":\"#242770\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E73535DE\"},{\"from\":20,\"to\":null,\"color\":\"#242770\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E73535DE';\\n }\\n}\\nreturn '#242770';\"},\"datasourceUnits\":\"%\",\"layout\":\"percentage\",\"volumeSource\":\"static\",\"volumeConstant\":500,\"volumeAttributeName\":\"volume\",\"volumeUnits\":\"L\",\"volumeFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"volumeColor\":\"rgba(0, 0, 0, 0.18)\",\"units\":\"%\",\"widgetUnitsSource\":\"static\",\"widgetUnitsAttributeName\":\"units\",\"liquidColor\":{\"type\":\"range\",\"color\":\"#7A8BFF\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E27C7CDE\"},{\"from\":20,\"to\":null,\"color\":\"#7A8BFF\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FF0000DE\"},{\"from\":20,\"to\":null,\"color\":\"rgba(0,0,0,0.87)\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FF0000DE';\\n }\\n}\\nreturn '#000000DE';\"},\"showBackgroundOverlay\":true,\"backgroundOverlayColor\":{\"type\":\"range\",\"color\":\"#FFFFFFC2\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FFEFEFDE\"},{\"from\":20,\"to\":null,\"color\":\"#FFFFFFC2\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FFEFEFDE';\\n }\\n}\\nreturn '#FFFFFFC2';\"},\"showTooltip\":true,\"showTooltipLevel\":true,\"tooltipUnits\":\"%\",\"tooltipLevelDecimals\":0,\"tooltipLevelFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipLevelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.76)\",\"rangeList\":[],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"showTooltipDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":3,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Liquid level\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"titleFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1.5\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"showTitleIcon\":false,\"titleIcon\":\"water_drop\",\"iconColor\":\"#5469FF\",\"decimals\":0,\"enableDataExport\":false,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" + }, + "externalId": null, + "tags": [ + "reservoir", + "container", + "vessel", + "storage unit", + "cistern", + "canister", + "vat", + "basin", + "repository", + "bin", + "hopper" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_carbon_monoxide__co__card.json b/application/src/main/data/json/system/widget_types/horizontal_carbon_monoxide__co__card.json new file mode 100644 index 0000000000..fe9131c7ae --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_carbon_monoxide__co__card.json @@ -0,0 +1,37 @@ +{ + "fqn": "horizontal_carbon_monoxide_co_card", + "name": "Horizontal carbon monoxide (CO) card", + "deprecated": false, + "image": "tb-image:Q08tdmFsdWUtY2FyZC1ob3Jpem9udGFsLnN2Zw==:Q08tdmFsdWUtY2FyZC1ob3Jpem9udGFsLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjU2IiB5PSI1MiIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjU1IiB4PSIuNSIgeT0iNTIuNSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjEyIiByeD0iMy41Ii8+PGcgZmlsdGVyPSJ1cmwoI2EpIj48cmVjdCB3aWR0aD0iMTg2IiBoZWlnaHQ9IjQyIiB4PSI3IiB5PSI1OSIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuNzYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iI0Q4MTgzOCIgZD0iTTE5IDc1Yy0xLjEgMC0yIC45LTIgMnY2YTIgMiAwIDAgMCAyIDJoM3YtMmgtM3YtNmgzdi0yaC0zWm02IDBjLTEuMSAwLTIgLjktMiAydjZhMiAyIDAgMCAwIDIgMmgyYzEuMTEgMCAyLS44OSAyLTJ2LTZhMiAyIDAgMCAwLTItMmgtMlptMCAyaDJ2NmgtMnYtNloiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii44NyIgZD0iTTQ0LjM5OCA3Mi45OTFoMS41ODdhMy42NjIgMy42NjIgMCAwIDEtLjUwOCAxLjYxOWMtLjI4Ny40Ny0uNjkxLjg0LTEuMjEyIDEuMTEtLjUyLjI3Mi0xLjE1My40MDctMS44OTguNDA3YTMuNzYgMy43NiAwIDAgMS0xLjU0Mi0uMzA1IDMuMzgyIDMuMzgyIDAgMCAxLTEuMTc1LS44NzYgMy45NjIgMy45NjIgMCAwIDEtLjc0OS0xLjM3NyA1Ljk4NCA1Ljk4NCAwIDAgMS0uMjU0LTEuODAzdi0uNzY4YzAtLjY2NC4wODctMS4yNjUuMjYtMS44MDMuMTc4LS41MzcuNDMyLS45OTYuNzYyLTEuMzc3LjMzLS4zODUuNzI2LS42OCAxLjE4Ny0uODgyYTMuODgyIDMuODgyIDAgMCAxIDEuNTY4LS4zMDVjLjczNyAwIDEuMzU5LjEzNSAxLjg2Ny40MDYuNTA3LjI3LjkuNjQ1IDEuMTggMS4xMjRhMy45OCAzLjk4IDAgMCAxIC41MiAxLjY0NGgtMS41ODZhMi45MzQgMi45MzQgMCAwIDAtLjI4LTEuMDIyIDEuNDQgMS40NCAwIDAgMC0uNjIyLS42NDhjLS4yNzUtLjE1Mi0uNjM0LS4yMjgtMS4wNzktLjIyOC0uMzY0IDAtLjY4MS4wNjctLjk1Mi4yMDMtLjI3LjEzNS0uNDk3LjMzNC0uNjc5LjU5Ny0uMTgyLjI2Mi0uMzIuNTg2LS40MTMuOTctLjA4OS4zODItLjEzMy44MTctLjEzMyAxLjMwOHYuNzgxYzAgLjQ2Ni4wNC44ODkuMTIgMS4yNy4wODUuMzc2LjIxMi43LjM4Mi45Ny4xNzMuMjcyLjM5My40ODEuNjYuNjMuMjY2LjE0Ny41ODYuMjIxLjk1OC4yMjEuNDUzIDAgLjgxOS0uMDcyIDEuMDk4LS4yMTUuMjg0LS4xNDQuNDk4LS4zNTQuNjQxLS42MjkuMTQ5LS4yOC4yNDYtLjYyLjI5Mi0xLjAyMlptNy4wMDQgMS42MzJ2LTMuMjc2YzAtLjI0NS0uMDQ0LS40NTctLjEzMy0uNjM1YS45MzMuOTMzIDAgMCAwLS40MDYtLjQxMmMtLjE3OC0uMDk3LS40MDItLjE0Ni0uNjczLS4xNDYtLjI1IDAtLjQ2Ni4wNDItLjY0OC4xMjdhMS4wNDcgMS4wNDcgMCAwIDAtLjQyNS4zNDMuODI3LjgyNyAwIDAgMC0uMTUyLjQ4OEg0Ny40NGMwLS4yNy4wNjYtLjUzMy4xOTctLjc4N3MuMzIyLS40OC41NzItLjY3OWEyLjg1IDIuODUgMCAwIDEgLjg5NS0uNDcgMy43MjIgMy43MjIgMCAwIDEgMS4xNjgtLjE3MWMuNTE2IDAgLjk3My4wODcgMS4zNy4yNi40MDMuMTc0LjcxOC40MzYuOTQ2Ljc4Ny4yMzMuMzQ3LjM1Ljc4My4zNSAxLjMwOHYzLjA1M2MwIC4zMTMuMDIuNTk1LjA2My44NDQuMDQ2LjI0Ni4xMTIuNDYuMTk3LjY0MVY3Nkg1MS42M2EyLjcwNSAyLjcwNSAwIDAgMS0uMTcyLS42MjggNS4xMyA1LjEzIDAgMCAxLS4wNTctLjc1Wm0uMjIyLTIuOC4wMTMuOTQ2SDUwLjU0Yy0uMjg0IDAtLjUzMy4wMjctLjc0OS4wODMtLjIxNi4wNS0uMzk2LjEyNi0uNTQuMjI4YS45OTIuOTkyIDAgMCAwLS40MzEuODU3YzAgLjE4Mi4wNDIuMzUuMTI3LjUwMmEuODkuODkgMCAwIDAgLjM2OC4zNDljLjE2NS4wODQuMzY0LjEyNy41OTcuMTI3LjMxMyAwIC41ODYtLjA2NC44MTktLjE5LjIzNy0uMTMyLjQyMy0uMjkuNTU4LS40NzcuMTM2LS4xOS4yMDctLjM3LjIxNi0uNTRsLjQ5NS42OGMtLjA1LjE3My0uMTM4LjM2LS4yNi41NThhMi43MSAyLjcxIDAgMCAxLS40ODMuNTcyIDIuNDAyIDIuNDAyIDAgMCAxLTEuNjQ0LjYxIDIuNjIgMi42MiAwIDAgMS0xLjItLjI2OCAyLjEzNCAyLjEzNCAwIDAgMS0uODI1LS43MyAxLjg5MyAxLjg5MyAwIDAgMS0uMjk4LTEuMDQ3YzAtLjM2NC4wNjgtLjY4Ni4yMDMtLjk2NS4xNC0uMjgzLjM0My0uNTIuNjEtLjcxLjI3LS4xOTEuNi0uMzM1Ljk5LS40MzIuMzktLjEwMi44MzQtLjE1MyAxLjMzMy0uMTUzaDEuMlptNC43LTEuMzgzVjc2aC0xLjUzdi02Ljg2OGgxLjQ2bC4wNyAxLjMwN1ptMi4xMDEtMS4zNTMtLjAxMiAxLjQyMmEzLjk2MiAzLjk2MiAwIDAgMC0uNjIyLS4wNWMtLjI2MyAwLS40OTMuMDM4LS42OTIuMTE0YTEuMzA0IDEuMzA0IDAgMCAwLS41MDIuMzE3Yy0uMTMxLjEzNi0uMjMzLjMtLjMwNS40OTVhMi4yMTMgMi4yMTMgMCAwIDAtLjEyNy42NTRsLS4zNDkuMDI1YzAtLjQzMS4wNDMtLjgzMS4xMjctMS4yLjA4NS0uMzY3LjIxMi0uNjkxLjM4MS0uOTcuMTc0LS4yOC4zOS0uNDk4LjY0OC0uNjU0LjI2Mi0uMTU3LjU2NS0uMjM1LjkwNy0uMjM1LjA5MyAwIC4xOTMuMDA4LjI5OS4wMjUuMTEuMDE3LjE5Mi4wMzYuMjQ3LjA1N1ptMS4yMTUtMi44MzdoMS41M3Y4LjI4NEw2MS4wMjQgNzZINTkuNjR2LTkuNzVabTYuMDExIDYuMjUydi4xMzRjMCAuNTA4LS4wNTcuOTc1LS4xNzEgMS40MDMtLjExLjQyMy0uMjguNzktLjUwOCAxLjEwNGEyLjM1MiAyLjM1MiAwIDAgMS0uODM4LjczYy0uMzMuMTctLjcxMy4yNTQtMS4xNDkuMjU0LS40MjcgMC0uOC0uMDgtMS4xMTctLjI0MWEyLjIyMSAyLjIyMSAwIDAgMS0uOC0uNjg2IDMuODAyIDMuODAyIDAgMCAxLS41MTQtMS4wNiA2Ljk5OCA2Ljk5OCAwIDAgMS0uMjgtMS4zNTh2LS40MjZjLjA1Ni0uNDk5LjE0OS0uOTUyLjI4LTEuMzU4LjEzMS0uNDEuMzAzLS43NjQuNTE0LTEuMDYuMjE2LS4zLjQ4LS41MzEuNzk0LS42OTIuMzE3LS4xNi42ODgtLjI0MSAxLjExLS4yNDEuNDQgMCAuODI4LjA4NSAxLjE2Mi4yNTQuMzM5LjE3LjYyLjQxLjg0NC43MjMuMjI1LjMxLjM5Mi42NzcuNTAyIDEuMTA1LjExNC40MjcuMTcxLjkuMTcxIDEuNDE1Wm0tMS41My4xMzR2LS4xMzRjMC0uMzA4LS4wMjUtLjU5OC0uMDc2LS44N2EyLjI4MyAyLjI4MyAwIDAgMC0uMjU0LS43MjMgMS4yNzIgMS4yNzIgMCAwIDAtLjQ3LS40ODljLS4xOTQtLjEyMi0uNDM3LS4xODQtLjczLS4xODQtLjI3IDAtLjUwMy4wNDctLjY5Ny4xNC0uMTk1LjA5My0uMzU4LjIyLS40OS4zOC0uMTMuMTYyLS4yMzQuMzQ4LS4zMS41Ni0uMDcyLjIxMS0uMTIuNDQtLjE0Ni42ODV2MS4xNDljLjAzOC4zMTcuMTE4LjYxLjI0MS44NzYuMTI3LjI2Mi4zMDUuNDc0LjUzMy42MzUuMjI5LjE1Ni41MjMuMjM0Ljg4My4yMzQuMjgzIDAgLjUyMi0uMDU3LjcxNy0uMTcuMTk0LS4xMTUuMzQ5LS4yNzQuNDYzLS40NzcuMTE5LS4yMDcuMjAzLS40NDkuMjU0LS43MjQuMDU1LS4yNzUuMDgzLS41Ny4wODMtLjg4OFptMi43ODMuMDA2di0uMTQ2YzAtLjQ5NS4wNzItLjk1NC4yMTYtMS4zNzcuMTQ0LS40MjguMzUxLS43OTguNjIyLTEuMTExLjI3NS0uMzE4LjYxLS41NjMgMS4wMDMtLjczNmEzLjI2NSAzLjI2NSAwIDAgMSAxLjM0Ni0uMjY3Yy41MDMgMCAuOTUyLjA4OSAxLjM0Ni4yNjcuMzk3LjE3My43MzQuNDE4IDEuMDA5LjczNi4yNzUuMzEzLjQ4NC42ODMuNjI4IDEuMTEuMTQ0LjQyNC4yMTYuODgzLjIxNiAxLjM3OHYuMTQ2YzAgLjQ5NS0uMDcyLjk1NC0uMjE2IDEuMzc3YTMuMjk2IDMuMjk2IDAgMCAxLS42MjggMS4xMTFjLS4yNzUuMzE0LS42MS41NTktMS4wMDMuNzM3LS4zOTQuMTczLS44NC4yNi0xLjM0LjI2YTMuMzUgMy4zNSAwIDAgMS0xLjM1Mi0uMjYgMi45MTIgMi45MTIgMCAwIDEtMS4wMDItLjczNyAzLjI5NyAzLjI5NyAwIDAgMS0uNjI5LTEuMTEgNC4yNTIgNC4yNTIgMCAwIDEtLjIxNi0xLjM3OFptMS41My0uMTQ2di4xNDZjMCAuMzA5LjAzMi42MDEuMDk1Ljg3Ni4wNjQuMjc1LjE2My41MTYuMjk5LjcyNC4xMzUuMjA3LjMwOS4zNy41Mi40ODkuMjEyLjExOC40NjMuMTc3Ljc1NS4xNzcuMjg0IDAgLjUzLS4wNTkuNzM3LS4xNzcuMjExLS4xMTkuMzg1LS4yODIuNTItLjQ5LjEzNi0uMjA3LjIzNS0uNDQ4LjI5OS0uNzIzLjA2Ny0uMjc1LjEwMS0uNTY3LjEwMS0uODc2di0uMTQ2YTMuNTUgMy41NSAwIDAgMC0uMTAxLS44NjMgMi4yMTcgMi4yMTcgMCAwIDAtLjMwNS0uNzMgMS40NTYgMS40NTYgMCAwIDAtLjUyLS40OTUgMS40MzMgMS40MzMgMCAwIDAtLjc0My0uMTg0Yy0uMjg4IDAtLjUzOC4wNjEtLjc1LjE4NGExLjQ3NCAxLjQ3NCAwIDAgMC0uNTEzLjQ5NSAyLjMwNCAyLjMwNCAwIDAgMC0uMjk5LjczYy0uMDYzLjI3LS4wOTUuNTU4LS4wOTUuODYzWm03LjkwNS0xLjg5OFY3NmgtMS41M3YtNi44NjhoMS40NDJsLjA4OCAxLjQ2NlptLS4yNzMgMS43MTQtLjQ5NS0uMDA2Yy4wMDUtLjQ4Ny4wNzItLjkzMy4yMDMtMS4zNC4xMzYtLjQwNi4zMjItLjc1NS41NTktMS4wNDcuMjQxLS4yOTIuNTI5LS41MTYuODYzLS42NzMuMzM1LS4xNi43MDctLjI0MSAxLjExOC0uMjQxLjMzIDAgLjYyOC4wNDYuODk1LjE0LjI3LjA4OC41MDEuMjM0LjY5MS40MzguMTk1LjIwMy4zNDMuNDY3LjQ0NS43OTMuMTAxLjMyMi4xNTIuNzE3LjE1MiAxLjE4N1Y3NmgtMS41MzZ2LTQuNDQzYzAtLjMzLS4wNDktLjU5LS4xNDYtLjc4MWEuODE2LjgxNiAwIDAgMC0uNDEzLS40MTMgMS41NDYgMS41NDYgMCAwIDAtLjY2Ni0uMTI3IDEuNDggMS40OCAwIDAgMC0uNzA1LjE2NWMtLjIwNy4xMS0uMzgzLjI2LS41MjcuNDUxLS4xNC4xOS0uMjQ3LjQxLS4zMjMuNjYtLjA3Ni4yNS0uMTE1LjUxNi0uMTE1LjhabS0zNS43NSAxNC4yMTZWOTJoLTEuNTI5di02Ljg2OGgxLjQ0MWwuMDg5IDEuMzk2Wm0tLjI0NyAxLjc4NC0uNTItLjAwNmMwLS40NzQuMDYtLjkxMi4xNzgtMS4zMTRzLjI5Mi0uNzUxLjUyLTEuMDQ4Yy4yMjktLjMuNTEyLS41My44NS0uNjkyYTIuNzA1IDIuNzA1IDAgMCAxIDEuMTg4LS4yNDdjLjMxMyAwIC41OTkuMDQ2Ljg1Ny4xNC4yNjIuMDg4LjQ4OC4yMy42NzkuNDI1LjE5NS4xOTUuMzQzLjQ0NC40NDQuNzQ5LjEwNi4zMDUuMTU5LjY3My4xNTkgMS4xMDRWOTJoLTEuNTN2LTQuNDQzYzAtLjMzNS0uMDUtLjU5Ny0uMTUyLS43ODhhLjg0NC44NDQgMCAwIDAtLjQyNi0uNDA2IDEuNTQgMS41NCAwIDAgMC0uNjUzLS4xMjdjLS4yODggMC0uNTM0LjA1NS0uNzM3LjE2NS0uMTk5LjExLS4zNjEuMjYtLjQ4OS40NTEtLjEyNy4xOS0uMjIuNDEtLjI3OS42NmEzLjQ2IDMuNDYgMCAwIDAtLjA4OS44Wm00LjI2LS40MDYtLjcxOC4xNThjMC0uNDE0LjA1OC0uODA2LjE3Mi0xLjE3NC4xMTgtLjM3Mi4yOS0uNjk4LjUxNC0uOTc3LjIyOC0uMjg0LjUxLS41MDYuODQ0LS42NjdhMi42MiAyLjYyIDAgMCAxIDEuMTQ5LS4yNDFjLjM1MSAwIC42NjQuMDQ5Ljk0LjE0Ni4yNzkuMDkzLjUxNi4yNDEuNzEuNDQ0LjE5NS4yMDMuMzQzLjQ2OC40NDUuNzk0LjEwMS4zMjEuMTUyLjcxLjE1MiAxLjE2OFY5MmgtMS41MzZ2LTQuNDVjMC0uMzQ3LS4wNS0uNjE1LS4xNTItLjgwNmEuNzkuNzkgMCAwIDAtLjQyLS4zOTMgMS42ODkgMS42ODkgMCAwIDAtLjY1My0uMTE1Yy0uMjM3IDAtLjQ0Ny4wNDUtLjYyOS4xMzRhMS4yNDcgMS4yNDcgMCAwIDAtLjQ1LjM2MSAxLjYxNCAxLjYxNCAwIDAgMC0uMjguNTI3Yy0uMDU5LjItLjA4OC40MTUtLjA4OC42NDhabTUuNzQuNzM2di0uMTQ2YzAtLjQ5NS4wNzItLjk1NC4yMTYtMS4zNzcuMTQ0LS40MjguMzUxLS43OTguNjIyLTEuMTExLjI3NS0uMzE4LjYxLS41NjMgMS4wMDMtLjczNmEzLjI2NSAzLjI2NSAwIDAgMSAxLjM0Ni0uMjY3Yy41MDMgMCAuOTUyLjA4OSAxLjM0Ni4yNjcuMzk3LjE3My43MzQuNDE4IDEuMDA5LjczNi4yNzUuMzEzLjQ4NC42ODMuNjI4IDEuMTEuMTQ0LjQyNC4yMTYuODgzLjIxNiAxLjM3OHYuMTQ2YzAgLjQ5NS0uMDcyLjk1NC0uMjE2IDEuMzc3YTMuMjk2IDMuMjk2IDAgMCAxLS42MjggMS4xMTEgMi45MSAyLjkxIDAgMCAxLTEuMDAzLjczN2MtLjM5NC4xNzMtLjg0LjI2LTEuMzQuMjZhMy4zNSAzLjM1IDAgMCAxLTEuMzUyLS4yNiAyLjkxIDIuOTEgMCAwIDEtMS4wMDItLjczNyAzLjI5NCAzLjI5NCAwIDAgMS0uNjI5LTEuMTEgNC4yNTIgNC4yNTIgMCAwIDEtLjIxNi0xLjM3OFptMS41My0uMTQ2di4xNDZjMCAuMzA5LjAzMi42MDEuMDk1Ljg3Ni4wNjQuMjc1LjE2My41MTYuMjk5LjcyNC4xMzUuMjA3LjMwOS4zNy41Mi40ODkuMjEyLjExOC40NjQuMTc3Ljc1Ni4xNzcuMjgzIDAgLjUyOS0uMDU5LjczNi0uMTc3LjIxMi0uMTE5LjM4NS0uMjgyLjUyLS40OS4xMzYtLjIwNy4yMzUtLjQ0OC4yOTktLjcyMy4wNjctLjI3NS4xMDEtLjU2Ny4xMDEtLjg3NnYtLjE0NmEzLjU1IDMuNTUgMCAwIDAtLjEwMS0uODYzIDIuMjE1IDIuMjE1IDAgMCAwLS4zMDUtLjczIDEuNDU2IDEuNDU2IDAgMCAwLS41Mi0uNDk1IDEuNDMyIDEuNDMyIDAgMCAwLS43NDMtLjE4NGMtLjI4OCAwLS41MzguMDYxLS43NS4xODRhMS40NzQgMS40NzQgMCAwIDAtLjUxMy40OTUgMi4zMDQgMi4zMDQgMCAwIDAtLjI5OS43M2MtLjA2My4yNy0uMDk1LjU1OC0uMDk1Ljg2M1ptNy45MDUtMS44OThWOTJoLTEuNTN2LTYuODY4aDEuNDQybC4wODggMS40NjZabS0uMjczIDEuNzE0LS40OTUtLjAwNmE0LjQ3IDQuNDcgMCAwIDEgLjIwNC0xLjM0IDMuMyAzLjMgMCAwIDEgLjU1OC0xLjA0N2MuMjQxLS4yOTIuNTI5LS41MTYuODYzLS42NzMuMzM1LS4xNi43MDctLjI0MSAxLjExNy0uMjQxLjMzIDAgLjYyOS4wNDYuODk1LjE0LjI3MS4wODguNTAyLjIzNC42OTIuNDM4LjE5NS4yMDMuMzQzLjQ2Ny40NDUuNzkzLjEwMS4zMjIuMTUyLjcxNy4xNTIgMS4xODdWOTJoLTEuNTM2di00LjQ0M2MwLS4zMy0uMDQ5LS41OS0uMTQ2LS43ODFhLjgxNy44MTcgMCAwIDAtLjQxMy0uNDEzIDEuNTQ3IDEuNTQ3IDAgMCAwLS42NjYtLjEyN2MtLjI2MyAwLS40OTcuMDU1LS43MDUuMTY1LS4yMDcuMTEtLjM4My4yNi0uNTI3LjQ1MS0uMTQuMTktLjI0Ny40MS0uMzIzLjY2LS4wNzYuMjUtLjExNS41MTYtLjExNS44Wm01Ljk1LjMzdi0uMTQ2YzAtLjQ5NS4wNzMtLjk1NC4yMTYtMS4zNzcuMTQ0LS40MjguMzUyLS43OTguNjIyLTEuMTExLjI3Ni0uMzE4LjYxLS41NjMgMS4wMDMtLjczNmEzLjI2NSAzLjI2NSAwIDAgMSAxLjM0Ni0uMjY3Yy41MDQgMCAuOTUyLjA4OSAxLjM0Ni4yNjcuMzk4LjE3My43MzQuNDE4IDEuMDEuNzM2LjI3NC4zMTMuNDg0LjY4My42MjggMS4xMS4xNDMuNDI0LjIxNS44ODMuMjE1IDEuMzc4di4xNDZjMCAuNDk1LS4wNzIuOTU0LS4yMTUgMS4zNzdhMy4yOTYgMy4yOTYgMCAwIDEtLjYyOSAxLjExMSAyLjkxIDIuOTEgMCAwIDEtMS4wMDMuNzM3Yy0uMzkzLjE3My0uODQuMjYtMS4zNC4yNmEzLjM1IDMuMzUgMCAwIDEtMS4zNTEtLjI2IDIuOTEyIDIuOTEyIDAgMCAxLTEuMDAzLS43MzcgMy4yOTQgMy4yOTQgMCAwIDEtLjYyOS0xLjExIDQuMjUyIDQuMjUyIDAgMCAxLS4yMTUtMS4zNzhabTEuNTMtLjE0NnYuMTQ2YzAgLjMwOS4wMzIuNjAxLjA5Ni44NzYuMDYzLjI3NS4xNjMuNTE2LjI5OC43MjQuMTM1LjIwNy4zMDkuMzcuNTIuNDg5LjIxMi4xMTguNDY0LjE3Ny43NTYuMTc3LjI4MyAwIC41MjktLjA1OS43MzYtLjE3Ny4yMTItLjExOS4zODUtLjI4Mi41Mi0uNDkuMTM2LS4yMDcuMjM2LS40NDguMjk5LS43MjMuMDY4LS4yNzUuMTAyLS41NjcuMTAyLS44NzZ2LS4xNDZhMy41NSAzLjU1IDAgMCAwLS4xMDItLjg2MyAyLjIxNSAyLjIxNSAwIDAgMC0uMzA1LS43MyAxLjQ1NiAxLjQ1NiAwIDAgMC0uNTItLjQ5NSAxLjQzMiAxLjQzMiAwIDAgMC0uNzQzLS4xODRjLS4yODggMC0uNTM3LjA2MS0uNzQ5LjE4NGExLjQ3NCAxLjQ3NCAwIDAgMC0uNTE0LjQ5NSAyLjMwNCAyLjMwNCAwIDAgMC0uMjk4LjczYy0uMDY0LjI3LS4wOTYuNTU4LS4wOTYuODYzWm03LjQxNy0zLjM2NCAxLjMwOCAyLjI3OSAxLjMzMy0yLjI4aDEuNjgybC0yLjA4MiAzLjM3MUw3OC41MzMgOTJoLTEuNjgybC0xLjM5Ni0yLjM3NEw3NC4wNTggOTJINzIuMzdsMi4xNTgtMy40OTgtMi4wNzYtMy4zN2gxLjY3NlptNy4yNTggMFY5Mkg3OS44NXYtNi44NjhoMS41MzZabS0xLjYzOC0xLjgwM2MwLS4yMzMuMDc2LS40MjUuMjI5LS41NzguMTU2LS4xNTYuMzcyLS4yMzQuNjQ3LS4yMzQuMjcgMCAuNDg1LjA3OC42NDEuMjM0YS43NzIuNzcyIDAgMCAxIC4yMzUuNTc4Ljc2NC43NjQgMCAwIDEtLjIzNS41NzFjLS4xNTYuMTUzLS4zNy4yMjktLjY0MS4yMjktLjI3NSAwLS40OS0uMDc2LS42NDctLjIyOWEuNzc1Ljc3NSAwIDAgMS0uMjI5LS41N1ptNy43NTMgNy4yNVY4Mi4yNWgxLjUzNlY5MmgtMS4zOWwtLjE0Ni0xLjQyMlptLTQuNDY5LTEuOTM3di0uMTMzYzAtLjUyLjA2Mi0uOTk1LjE4NC0xLjQyMi4xMjMtLjQzMi4zLS44MDIuNTM0LTEuMTEuMjMyLS4zMTQuNTE2LS41NTMuODUtLjcxOC4zMzQtLjE3LjcxMS0uMjU0IDEuMTMtLjI1NC40MTUgMCAuNzc5LjA4IDEuMDkyLjI0MS4zMTMuMTYuNTguMzkyLjguNjkyLjIyLjI5Ni4zOTUuNjUyLjUyNiAxLjA2Ni4xMzIuNDEuMjI1Ljg2OC4yOCAxLjM3MXYuNDI2Yy0uMDU1LjQ5LS4xNDguOTQtLjI4IDEuMzQ2YTMuNjEzIDMuNjEzIDAgMCAxLS41MjYgMS4wNTNjLS4yMi4yOTYtLjQ5LjUyNS0uODA3LjY4Ni0uMzEzLjE2LS42NzkuMjQxLTEuMDk4LjI0MS0uNDE0IDAtLjc4OS0uMDg3LTEuMTIzLS4yNmEyLjQ4NCAyLjQ4NCAwIDAgMS0uODQ0LS43MyAzLjQzNyAzLjQzNyAwIDAgMS0uNTM0LTEuMTA1IDUuMDIgNS4wMiAwIDAgMS0uMTg0LTEuMzlabTEuNTMtLjEzM3YuMTMzYzAgLjMxMy4wMjguNjA1LjA4Mi44NzYuMDYuMjcuMTUuNTEuMjczLjcxNy4xMjMuMjA0LjI4Mi4zNjQuNDc3LjQ4My4xOTguMTE0LjQzNS4xNzEuNzEuMTcxLjM0NyAwIC42MzMtLjA3Ni44NTctLjIyOC4yMjUtLjE1My40LS4zNTguNTI3LS42MTYuMTMyLS4yNjIuMjItLjU1NC4yNjctLjg3NlY4OC4wMmEyLjgwNSAyLjgwNSAwIDAgMC0uMTU5LS42OTggMS44NzEgMS44NzEgMCAwIDAtLjMxLS41NjUgMS4zMSAxLjMxIDAgMCAwLS40OS0uMzgxIDEuNTMyIDEuNTMyIDAgMCAwLS42NzktLjE0Yy0uMjggMC0uNTE2LjA2LS43MS4xNzhhMS40NDMgMS40NDMgMCAwIDAtLjQ4My40ODkgMi40MDYgMi40MDYgMCAwIDAtLjI3My43MjMgNC4xOCA0LjE4IDAgMCAwLS4wODkuODgzWm05LjM1MyAzLjYxOGMtLjUwOCAwLS45NjctLjA4My0xLjM3OC0uMjQ4YTMuMDQyIDMuMDQyIDAgMCAxLTEuMDQtLjcwNCAzLjEyMyAzLjEyMyAwIDAgMS0uNjU1LTEuMDYgMy43MSAzLjcxIDAgMCAxLS4yMjgtMS4zMTR2LS4yNTRjMC0uNTM4LjA3OC0xLjAyNC4yMzUtMS40Ni4xNTYtLjQzNi4zNzQtLjgwOC42NTMtMS4xMTcuMjgtLjMxMy42MS0uNTUyLjk5LS43MThhMy4wOCAzLjA4IDAgMCAxIDEuMjM5LS4yNDdjLjQ5IDAgLjkyLjA4MiAxLjI4OC4yNDcuMzY4LjE2Ni42NzMuMzk4LjkxNC42OTkuMjQ1LjI5Ni40MjcuNjUuNTQ2IDEuMDYuMTIzLjQxLjE4NC44NjMuMTg0IDEuMzU4di42NTRoLTUuMzA3di0xLjA5OGgzLjc5NnYtLjEyYTIuMTQ2IDIuMTQ2IDAgMCAwLS4xNjUtLjc3NSAxLjMxNiAxLjMxNiAwIDAgMC0uNDUtLjU4NGMtLjIwNC0uMTQ4LS40NzQtLjIyMi0uODEzLS4yMjItLjI1NCAwLS40OC4wNTUtLjY4LjE2NS0uMTk0LjEwNi0uMzU3LjI2LS40ODguNDYzYTIuNDM4IDIuNDM4IDAgMCAwLS4zMDUuNzM2IDQuMTM4IDQuMTM4IDAgMCAwLS4xMDEuOTU5di4yNTRjMCAuMy4wNC41OC4xMi44MzguMDg1LjI1NC4yMDguNDc2LjM2OS42NjYuMTYuMTkuMzU1LjM0LjU4NC40NS4yMjguMTA3LjQ4OC4xNi43OC4xNi4zNjkgMCAuNjk3LS4wNzUuOTg0LS4yMjMuMjg4LS4xNDguNTM4LS4zNTcuNzUtLjYyOGwuODA2Ljc4Yy0uMTQ5LjIxNy0uMzQxLjQyNC0uNTc4LjYyM2EyLjg5MiAyLjg5MiAwIDAgMS0uODcuNDc2IDMuNDYgMy40NiAwIDAgMS0xLjE4LjE4NFoiLz48cGF0aCBmaWxsPSIjRDgxODM4IiBkPSJNMTExLjM2NSA3My4xMjRoLjI2NHYxLjczMmgtLjE1Yy0uNzU1IDAtMS40MDkuMTE3LTEuOTU5LjM1MWEzLjYyIDMuNjIgMCAwIDAtMS4zNDUuOTY3Yy0uMzUyLjQxLS42MTUuODktLjc5MSAxLjQ0MWE1LjgxMiA1LjgxMiAwIDAgMC0uMjU1IDEuNzR2Mi4wMTNjMCAuNTEuMDU2Ljk2MS4xNjcgMS4zNTQuMTExLjM4Ni4yNjYuNzEyLjQ2Ni45NzUuMjA1LjI1OC40MzkuNDU0LjcwMy41OS4yNjQuMTM0LjU0OC4yMDEuODUyLjIwMWExLjkgMS45IDAgMCAwIC44NjItLjE5M2MuMjU4LS4xMzUuNDc3LS4zMi42NTktLjU1NC4xODItLjIzNC4zMTktLjUxMy40MTMtLjgzNS4wOTQtLjMyMi4xNDEtLjY3NC4xNDEtMS4wNTQgMC0uMzY0LS4wNDctLjcwNC0uMTQxLTEuMDJhMi41NDggMi41NDggMCAwIDAtLjM5Ni0uODQ0IDEuODA3IDEuODA3IDAgMCAwLS42NTktLjU3MSAxLjg0MSAxLjg0MSAwIDAgMC0uODk2LS4yMWMtLjQyMiAwLS44MDYuMDk5LTEuMTUyLjI5OC0uMzM5LjItLjYxNS40Ni0uODI2Ljc4MmEyLjAxOSAyLjAxOSAwIDAgMC0uMzM0IDEuMDFsLS44MDgtLjI2M2E0LjU1IDQuNTUgMCAwIDEgLjM2LTEuNDY4Yy4xOTktLjQzMy40Ni0uODAyLjc4Mi0xLjEwN2EzLjMwNCAzLjMwNCAwIDAgMSAxLjExNi0uNjk0IDMuOTAxIDMuOTAxIDAgMCAxIDEuNDA3LS4yNDZjLjYyMSAwIDEuMTYuMTE3IDEuNjE3LjM1MS40NTcuMjM0LjgzNS41NTQgMS4xMzQuOTU4LjMwNC4zOTkuNTMuODU2LjY3NiAxLjM3MS4xNTMuNTEuMjI5IDEuMDQzLjIyOSAxLjZhNS4xNyA1LjE3IDAgMCAxLS4yODEgMS43MjJjLS4xODguNTI4LS40NjMuOTktLjgyNiAxLjM5YTMuNzEzIDMuNzEzIDAgMCAxLTEuMzEuOTNjLS41MS4yMjQtMS4wOS4zMzUtMS43NC4zMzUtLjY4NiAwLTEuMjk4LS4xMzItMS44MzctLjM5NmE0LjAzNyA0LjAzNyAwIDAgMS0xLjM2Mi0xLjA5IDQuOTQ4IDQuOTQ4IDAgMCAxLS44NDQtMS42IDYuMjIzIDYuMjIzIDAgMCAxLS4yOS0xLjkxNXYtLjg4YzAtLjk3Mi4xMjMtMS44ODkuMzY5LTIuNzVhNi43MjIgNi43MjIgMCAwIDEgMS4xMzQtMi4yOTQgNS4zIDUuMyAwIDAgMSAxLjk3Ny0xLjU1NmMuODAzLS4zOCAxLjc2MS0uNTcxIDIuODc0LS41NzFabTEyLjI3MyA1LjQwNXYyLjA5MmMwIDEuMDAyLS4xIDEuODU4LS4yOTkgMi41NjctLjE5My43MDMtLjQ3NCAxLjI3NC0uODQzIDEuNzEzLS4zNy40NC0uODEyLjc2Mi0xLjMyOC45NjdhNC41NTUgNC41NTUgMCAwIDEtMS43MTQuMzA4IDQuOTIgNC45MiAwIDAgMS0xLjQwNi0uMTk0IDMuNDQ0IDMuNDQ0IDAgMCAxLTEuMTYtLjYwNiAzLjYyMiAzLjYyMiAwIDAgMS0uODg4LTEuMDY0Yy0uMjQtLjQzOS0uNDI3LS45NjMtLjU2Mi0xLjU3My0uMTI5LS42MS0uMTkzLTEuMzE1LS4xOTMtMi4xMThWNzguNTNjMC0xLjAwNy4wOTktMS44NTcuMjk4LTIuNTQ4LjItLjY5OC40ODQtMS4yNjMuODUzLTEuNjk3LjM2OS0uNDQuODA4LS43NTkgMS4zMTgtLjk1OC41MTYtLjE5OSAxLjA5LS4yOTkgMS43MjMtLjI5OS41MSAwIC45NzguMDY1IDEuNDA2LjE5NC40MzQuMTIzLjgyLjMxOSAxLjE2LjU4OS4zNDYuMjcuNjM5LjYyLjg3OSAxLjA1NC4yNDYuNDI4LjQzNC45NDcuNTYzIDEuNTU2LjEyOS42MDMuMTkzIDEuMzA3LjE5MyAyLjExWm0tMi4xMTggMi4zOXYtMi43MDZjMC0uNTEtLjAyOS0uOTU4LS4wODgtMS4zNDVhMy45NjYgMy45NjYgMCAwIDAtLjI2NC0uOTkzIDEuOTc0IDEuOTc0IDAgMCAwLS40MjItLjY2OCAxLjQ3NyAxLjQ3NyAwIDAgMC0uNTgtLjM3OCAyLjA0NSAyLjA0NSAwIDAgMC0uNzI5LS4xMjMgMS45NCAxLjk0IDAgMCAwLS44NzkuMTkzIDEuNTc0IDEuNTc0IDAgMCAwLS42NS41OThjLS4xNzYuMjc1LS4zMTEuNjM5LS40MDUgMS4wOS0uMDg4LjQ0NS0uMTMyLjk4Ny0uMTMyIDEuNjI2djIuNzA3YzAgLjUxNi4wMy45Ny4wODggMS4zNjIuMDU5LjM5My4xNDcuNzMuMjY0IDEuMDExLjExNy4yNzUuMjU4LjUwNC40MjIuNjg1LjE3LjE3Ni4zNjMuMzA1LjU4LjM4Ny4yMjMuMDgyLjQ2Ni4xMjMuNzI5LjEyMy4zMzQgMCAuNjMtLjA2NC44ODgtLjE5M2ExLjYzIDEuNjMgMCAwIDAgLjY1MS0uNjE1Yy4xNzUtLjI4Ny4zMDctLjY2LjM5NS0xLjExN2E4LjgyNyA4LjgyNyAwIDAgMCAuMTMyLTEuNjQzWm0xMC45MzEtMi40OTVWODZoLTIuMTE4di05LjUxaDEuOTk1bC4xMjMgMS45MzRabS0uMzQyIDIuNDctLjcyMS0uMDFjMC0uNjU2LjA4Mi0xLjI2Mi4yNDYtMS44MTkuMTY0LS41NTYuNDA0LTEuMDQuNzIxLTEuNDVhMy4xNjggMy4xNjggMCAwIDEgMS4xNzctLjk1OGMuNDc1LS4yMjggMS4wMjMtLjM0MyAxLjY0NC0uMzQzLjQzNCAwIC44MjkuMDY1IDEuMTg2LjE5NC4zNjQuMTIzLjY3Ny4zMi45NDEuNTg5YTIuNiAyLjYgMCAwIDEgLjYxNSAxLjAzN2MuMTQ3LjQyMi4yMi45MzEuMjIgMS41M1Y4NmgtMi4xMTh2LTYuMTUyYzAtLjQ2My0uMDcxLS44MjYtLjIxMS0xLjA5YTEuMTcgMS4xNyAwIDAgMC0uNTg5LS41NjMgMi4xMzMgMi4xMzMgMCAwIDAtLjkwNS0uMTc1Yy0uMzk5IDAtLjczOS4wNzYtMS4wMi4yMjgtLjI3NS4xNTItLjUwMS4zNi0uNjc3LjYyNGEyLjgxIDIuODEgMCAwIDAtLjM4Ni45MTQgNC43ODggNC43ODggMCAwIDAtLjEyMyAxLjEwOFptNS44OTctLjU2My0uOTkzLjIyYzAtLjU3NC4wNzktMS4xMTYuMjM3LTEuNjI2LjE2NC0uNTE2LjQwMi0uOTY3LjcxMi0xLjM1NC4zMTctLjM5Mi43MDYtLjcgMS4xNjktLjkyMy40NjMtLjIyMi45OTMtLjMzNCAxLjU5MS0uMzM0LjQ4NiAwIC45Mi4wNjggMS4zMDEuMjAzLjM4Ni4xMjkuNzE0LjMzNC45ODQuNjE1LjI3LjI4MS40NzUuNjQ3LjYxNSAxLjA5OS4xNDEuNDQ1LjIxMS45ODQuMjExIDEuNjE3Vjg2aC0yLjEyN3YtNi4xNjFjMC0uNDgtLjA3LS44NTMtLjIxMS0xLjExNmExLjA5IDEuMDkgMCAwIDAtLjU4LS41NDUgMi4zMzcgMi4zMzcgMCAwIDAtLjkwNS0uMTU5IDEuOTYgMS45NiAwIDAgMC0uODcuMTg1IDEuNzI4IDEuNzI4IDAgMCAwLS42MjQuNTAxYy0uMTcuMjExLS4yOTkuNDU0LS4zODcuNzNhMy4xMyAzLjEzIDAgMCAwLS4xMjMuODk2Wm0xNC4xOTgtMy44NGgxLjkyNXY5LjI0NWMwIC44NTYtLjE4MiAxLjU4Mi0uNTQ1IDIuMThhMy41MSAzLjUxIDAgMCAxLTEuNTIxIDEuMzYyYy0uNjUuMzE3LTEuNDAzLjQ3NS0yLjI1OS40NzUtLjM2MyAwLS43NjctLjA1My0xLjIxMy0uMTU4YTQuOTc1IDQuOTc1IDAgMCAxLTEuMjgzLS41MSAzLjE3OSAzLjE3OSAwIDAgMS0xLjAyOC0uOTA1bC45OTMtMS4yNDhjLjM0LjQwNC43MTUuNyAxLjEyNS44ODcuNDEuMTg4Ljg0MS4yODIgMS4yOTIuMjgyLjQ4NiAwIC44OTktLjA5MSAxLjIzOS0uMjczLjM0Ni0uMTc2LjYxMy0uNDM2LjgtLjc4Mi4xODgtLjM0Ni4yODEtLjc2OC4yODEtMS4yNjZ2LTcuMTM2bC4xOTQtMi4xNTRabS02LjQ2IDQuODZ2LS4xODVjMC0uNzIuMDg4LTEuMzc3LjI2My0xLjk2OS4xNzYtLjU5Ny40MjgtMS4xMS43NTYtMS41MzhhMy4yOTYgMy4yOTYgMCAwIDEgMS4xOTYtLjk5M2MuNDY4LS4yMzQuOTk5LS4zNTIgMS41OS0uMzUyLjYxNiAwIDEuMTQuMTEyIDEuNTc0LjMzNC40MzkuMjIzLjgwNS41NDIgMS4wOTguOTU4LjI5My40MS41MjIuOTAzLjY4NiAxLjQ3Ny4xNy41NjguMjk2IDEuMjAxLjM3OCAxLjg5OHYuNTlhOS40NyA5LjQ3IDAgMCAxLS4zODcgMS44NjMgNS4xNDcgNS4xNDcgMCAwIDEtLjcyMSAxLjQ1OWMtLjI5OC40MS0uNjY4LjcyNi0xLjEwNy45NDktLjQzNC4yMjItLjk0Ni4zMzQtMS41MzguMzM0LS41OCAwLTEuMTA1LS4xMi0xLjU3My0uMzZhMy41ODEgMy41ODEgMCAwIDEtMS4xOTYtMS4wMTEgNC43OTUgNC43OTUgMCAwIDEtLjc1Ni0xLjUzIDYuNzYxIDYuNzYxIDAgMCAxLS4yNjMtMS45MjRabTIuMTE4LS4xODV2LjE4NWMwIC40MzMuMDQxLjgzOC4xMjMgMS4yMTMuMDg4LjM3NS4yMi43MDYuMzk1Ljk5My4xODIuMjguNDExLjUwNC42ODYuNjY4LjI4MS4xNTguNjEyLjIzNy45OTMuMjM3LjQ5OCAwIC45MDUtLjEwNiAxLjIyMi0uMzE3YTIuMDUgMi4wNSAwIDAgMCAuNzM4LS44NTJjLjE3Ni0uMzYzLjI5OS0uNzY4LjM2OS0xLjIxM3YtMS41OWEzLjg5MSAzLjg5MSAwIDAgMC0uMjE5LS45NjggMi41OTEgMi41OTEgMCAwIDAtLjQzMS0uNzgyIDEuNzk3IDEuNzk3IDAgMCAwLS42ODYtLjUyNyAyLjI3OCAyLjI3OCAwIDAgMC0uOTc1LS4xOTRjLS4zODEgMC0uNzEyLjA4My0uOTkzLjI0Ny0uMjgyLjE2NC0uNTEzLjM5LS42OTUuNjc2YTMuMzQzIDMuMzQzIDAgMCAwLS4zOTUgMS4wMDJjLS4wODguMzgxLS4xMzIuNzg4LS4xMzIgMS4yMjJabTE0LjEyNy03Ljk2M0wxNTcuMDA2IDg3LjFoLTEuNjYxbDQuOTkyLTEzLjg5NmgxLjY1MlptMy42ODYgNS4yMlY4NmgtMi4xMTh2LTkuNTFoMS45OTVsLjEyMyAxLjkzNFptLS4zNDMgMi40Ny0uNzItLjAwOGMwLS42NTcuMDgyLTEuMjYzLjI0Ni0xLjgyLjE2NC0uNTU2LjQwNC0xLjA0LjcyMS0xLjQ1YTMuMTYgMy4xNiAwIDAgMSAxLjE3Ny0uOTU4Yy40NzUtLjIyOCAxLjAyMy0uMzQzIDEuNjQ0LS4zNDMuNDMzIDAgLjgyOS4wNjUgMS4xODYuMTk0LjM2NC4xMjMuNjc3LjMyLjk0MS41ODkuMjY5LjI3LjQ3NC42MTUuNjE1IDEuMDM3LjE0Ni40MjIuMjIuOTMxLjIyIDEuNTNWODZoLTIuMTE4di02LjE1MmMwLS40NjMtLjA3MS0uODI2LS4yMTEtMS4wOWExLjE3IDEuMTcgMCAwIDAtLjU4OS0uNTYzIDIuMTM0IDIuMTM0IDAgMCAwLS45MDYtLjE3NWMtLjM5OCAwLS43MzguMDc2LTEuMDE5LjIyOC0uMjc1LjE1Mi0uNTAxLjM2LS42NzcuNjI0YTIuNzkgMi43OSAwIDAgMC0uMzg3LjkxNCA0Ljc4OCA0Ljc4OCAwIDAgMC0uMTIzIDEuMTA4Wm01Ljg5OC0uNTYyLS45OTMuMjJjMC0uNTc0LjA3OS0xLjExNi4yMzctMS42MjYuMTY0LS41MTYuNDAxLS45NjcuNzEyLTEuMzU0LjMxNi0uMzkyLjcwNi0uNyAxLjE2OS0uOTIzLjQ2My0uMjIyLjk5My0uMzM0IDEuNTkxLS4zMzQuNDg2IDAgLjkyLjA2OCAxLjMuMjAzLjM4Ny4xMjkuNzE1LjMzNC45ODUuNjE1LjI2OS4yODEuNDc0LjY0Ny42MTUgMS4wOTkuMTQxLjQ0NS4yMTEuOTg0LjIxMSAxLjYxN1Y4NmgtMi4xMjd2LTYuMTYxYzAtLjQ4LS4wNy0uODUzLS4yMTEtMS4xMTZhMS4wOTQgMS4wOTQgMCAwIDAtLjU4LS41NDUgMi4zNCAyLjM0IDAgMCAwLS45MDUtLjE1OSAxLjk2IDEuOTYgMCAwIDAtLjg3LjE4NSAxLjcyOCAxLjcyOCAwIDAgMC0uNjI0LjUwMWMtLjE3LjIxMS0uMjk5LjQ1NC0uMzg3LjczYTMuMTMgMy4xMyAwIDAgMC0uMTIzLjg5NlptOS4zOS00LjI4aC43NjVjLjI1MiAwIC40Ni0uMDM1LjYyNC0uMTA2YS44NDMuODQzIDAgMCAwIC4zNjktLjMwNy43OS43OSAwIDAgMCAuMTMyLS40NTcuNzQ0Ljc0NCAwIDAgMC0uMjQ3LS41NTRjLS4xNTgtLjE2NC0uNDI0LS4yNDYtLjc5OS0uMjQ2LS4yODcgMC0uNTMxLjA2MS0uNzMuMTg0YS41NTYuNTU2IDAgMCAwLS4yOS41MDFoLTEuNTk5YzAtLjQxNi4xMTctLjc3LjM1MS0xLjA2My4yMzQtLjI5My41NDgtLjUxNi45NDEtLjY2OC4zOTItLjE1Mi44Mi0uMjI5IDEuMjgzLS4yMjkuNTMzIDAgMS4wMDIuMDc3IDEuNDA2LjIyOS40MDQuMTQ2LjcxOC4zNjYuOTQuNjYuMjI5LjI5Mi4zNDMuNjU1LjM0MyAxLjA4OSAwIC4zMzQtLjEwNS42My0uMzE2Ljg4OC0uMjA1LjI1OC0uNDkyLjQ2LS44NjIuNjA2LS4zNjMuMTQtLjc5MS4yMTEtMS4yODMuMjExaC0xLjAyOHYtLjczOFptMCAxLjA5OHYtLjcyaDEuMDI4Yy41NDUgMCAxLjAxMS4wNjQgMS4zOTguMTkzLjM4Ni4xMjMuNjc5LjMxNC44NzkuNTcxLjIwNS4yNTguMzA3LjU5Mi4zMDcgMS4wMDIgMCAuNDM0LS4xMjMuODAzLS4zNjkgMS4xMDgtLjI0Ni4zMDQtLjU4My41MzYtMS4wMTEuNjk0YTQuMDI5IDQuMDI5IDAgMCAxLTEuNDIzLjIzN2MtLjQ0IDAtLjg2OC0uMDctMS4yODQtLjIxYTIuMzggMi4zOCAwIDAgMS0xLjAxOS0uNjg2Yy0uMjY0LS4zMS0uMzk2LS43MTgtLjM5Ni0xLjIyMmgxLjYwOWMwIC4yMjkuMTA1LjQyNS4zMTYuNTkuMjExLjE2My40OTUuMjQ1Ljg1My4yNDUuMzggMCAuNjYyLS4wODIuODQzLS4yNDZhLjgxOS44MTkgMCAwIDAgLjI4Mi0uNjMzYzAtLjIyMi0uMDUzLS40MDEtLjE1OS0uNTM2YS44MTguODE4IDAgMCAwLS40My0uMjkgMi4wMyAyLjAzIDAgMCAwLS42NTktLjA5N2gtLjc2NVoiLz48L2c+PGRlZnM+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjE5OCIgaGVpZ2h0PSI1NCIgeD0iMSIgeT0iNTMiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjMiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA3M18zOTYwIi8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMDczXzM5NjAiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48L2RlZnM+PC9zdmc+", + "description": "A colorless and odorless gas resulting from incomplete combustion, particularly in motor vehicles.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'carbon', label: 'Carbon monoxide', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Carbon monoxide\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:molecule-co\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#3FA71A\"},{\"from\":5,\"to\":10,\"color\":\"#80C32C\"},{\"from\":10,\"to\":25,\"color\":\"#FFA600\"},{\"from\":25,\"to\":50,\"color\":\"#F36900\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#3FA71A\"},{\"from\":5,\"to\":10,\"color\":\"#80C32C\"},{\"from\":10,\"to\":25,\"color\":\"#FFA600\"},{\"from\":25,\"to\":50,\"color\":\"#F36900\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Carbon monoxide\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"mg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "carbon monoxide", + "CO" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_carbon_monoxide__co__card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_carbon_monoxide__co__card_with_background.json new file mode 100644 index 0000000000..f48476eb16 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_carbon_monoxide__co__card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "horizontal_carbon_monoxide_co_card_with_background", + "name": "Horizontal carbon monoxide (CO) card with background", + "deprecated": false, + "image": "tb-image:Q08taG9yaXpvbnRhbC12YWx1ZS1jYXJkLXdpdGgtYmFja2dyb3VuZC5zdmc=:Q08taG9yaXpvbnRhbC12YWx1ZS1jYXJkLXdpdGgtYmFja2dyb3VuZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSI1NiIgeT0iNTIiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjQ0IiB4PSI2IiB5PSI1OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuNzYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iI0RFMjM0MyIgZD0iTTE4IDc1Yy0xLjEgMC0yIC45LTIgMnY2YTIgMiAwIDAgMCAyIDJoM3YtMmgtM3YtNmgzdi0yaC0zWm02IDBjLTEuMSAwLTIgLjktMiAydjZhMiAyIDAgMCAwIDIgMmgyYzEuMTEgMCAyLS44OSAyLTJ2LTZhMiAyIDAgMCAwLTItMmgtMlptMCAyaDJ2NmgtMnYtNloiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii44NyIgZD0iTTQyLjg5IDczLjc2aDEuNzFhMy45NDQgMy45NDQgMCAwIDEtLjU0NyAxLjc0M2MtLjMxLjUwNi0uNzQ1LjkwNS0xLjMwNiAxLjE5Ni0uNTYuMjkyLTEuMjQyLjQzOC0yLjA0NC40MzgtLjYxNSAwLTEuMTY5LS4xMS0xLjY2MS0uMzI4YTMuNjQzIDMuNjQzIDAgMCAxLTEuMjY1LS45NDRjLS4zNS0uNDEtLjYyLS45MDQtLjgwNi0xLjQ4M2E2LjQ0NSA2LjQ0NSAwIDAgMS0uMjc0LTEuOTQydi0uODI3YzAtLjcxNS4wOTQtMS4zNjIuMjgtMS45NDFhNC4yOSA0LjI5IDAgMCAxIC44Mi0xLjQ4MyAzLjU0NSAzLjU0NSAwIDAgMSAxLjI4LS45NSA0LjE3OSA0LjE3OSAwIDAgMSAxLjY4OC0uMzI5Yy43OTMgMCAxLjQ2Mi4xNDYgMi4wMS40MzguNTQ2LjI5MS45Ny42OTUgMS4yNyAxLjIxLjMwNi41MTUuNDkzIDEuMTA1LjU2MSAxLjc3aC0xLjcwOWEzLjE2MyAzLjE2MyAwIDAgMC0uMy0xLjEgMS41NSAxLjU1IDAgMCAwLS42Ny0uNjk4Yy0uMjk3LS4xNjQtLjY4NC0uMjQ2LTEuMTYyLS4yNDYtLjM5MiAwLS43MzQuMDczLTEuMDI2LjIxOS0uMjkxLjE0Ni0uNTM1LjM2LS43MzEuNjQyYTMuMDk1IDMuMDk1IDAgMCAwLS40NDUgMS4wNDZjLS4wOTUuNDEtLjE0My44OC0uMTQzIDEuNDA5di44NGMwIC41MDIuMDQzLjk1Ny4xMyAxLjM2OC4wOTEuNDA1LjIyOC43NTQuNDEgMS4wNDUuMTg3LjI5Mi40MjQuNTE4LjcxLjY3Ny4yODguMTYuNjMyLjI0IDEuMDMzLjI0LjQ4OCAwIC44ODItLjA3OCAxLjE4My0uMjMzYTEuNTUgMS41NSAwIDAgMCAuNjktLjY3N2MuMTYtLjMuMjY0LS42NjcuMzE1LTEuMVptNy41MjQgMS43NTd2LTMuNTI4YzAtLjI2NC0uMDQ4LS40OTItLjE0NC0uNjgzYTEuMDA1IDEuMDA1IDAgMCAwLS40MzctLjQ0NWMtLjE5MS0uMTA1LS40MzMtLjE1Ny0uNzI1LS4xNTctLjI2OCAwLS41LjA0Ni0uNjk3LjEzN2ExLjEyOCAxLjEyOCAwIDAgMC0uNDU4LjM2OS44OS44OSAwIDAgMC0uMTY0LjUyNmgtMS42NGMwLS4yOTEuMDctLjU3NC4yMTEtLjg0Ny4xNDItLjI3NC4zNDctLjUxOC42MTYtLjczMi4yNjktLjIxNC41OS0uMzgzLjk2NC0uNTA2YTQuMDA3IDQuMDA3IDAgMCAxIDEuMjU3LS4xODRjLjU1NiAwIDEuMDQ4LjA5MyAxLjQ3Ny4yOC40MzMuMTg3Ljc3Mi40NyAxLjAxOC44NDguMjUxLjM3My4zNzYuODQzLjM3NiAxLjQwOHYzLjI4OGMwIC4zMzcuMDIzLjY0LjA2OS45MS4wNS4yNjQuMTIuNDk0LjIxMi42OVY3N0g1MC42NmEyLjkxNyAyLjkxNyAwIDAgMS0uMTg0LS42NzcgNS41MjggNS41MjggMCAwIDEtLjA2Mi0uODA2Wm0uMjQtMy4wMTUuMDEzIDEuMDE5aC0xLjE4M2MtLjMwNSAwLS41NzQuMDMtLjgwNi4wODhhMS42NTYgMS42NTYgMCAwIDAtLjU4MS4yNDdjLS4xNTUuMTA5LS4yNzIuMjQxLS4zNDkuMzk2LS4wNzcuMTU1LS4xMTYuMzMtLjExNi41MjZzLjA0NS4zNzYuMTM3LjU0Yy4wOS4xNi4yMjMuMjg1LjM5Ni4zNzYuMTc4LjA5MS4zOTIuMTM3LjY0My4xMzcuMzM3IDAgLjYzLS4wNjguODgyLS4yMDUuMjU1LS4xNDEuNDU1LS4zMTIuNjAxLS41MTMuMTQ2LS4yMDUuMjIzLS4zOTguMjMyLS41OGwuNTM0LjczYTIuNTAyIDIuNTAyIDAgMCAxLS4yOC42MDJjLS4xMzMuMjE0LS4zMDYuNDItLjUyLjYxNmEyLjU4NCAyLjU4NCAwIDAgMS0xLjc3LjY1NmMtLjQ4NCAwLS45MTQtLjA5Ni0xLjI5My0uMjg3YTIuMjk4IDIuMjk4IDAgMCAxLS44ODgtLjc4NiAyLjAzOSAyLjAzOSAwIDAgMS0uMzIyLTEuMTI4YzAtLjM5Mi4wNzMtLjczOS4yMi0xLjA0LjE1LS4zMDUuMzY4LS41Ni42NTUtLjc2NS4yOTItLjIwNS42NDgtLjM2IDEuMDY3LS40NjUuNDItLjExLjg5OC0uMTY0IDEuNDM1LS4xNjRoMS4yOTJabTUuMDQxLTEuNDlWNzdoLTEuNjQ3di03LjM5N2gxLjU3MmwuMDc1IDEuNDA5Wm0yLjI2My0xLjQ1Ni0uMDE0IDEuNTNhNC4yOTEgNC4yOTEgMCAwIDAtLjY3LS4wNTVjLS4yODIuMDAxLS41My4wNDItLjc0NS4xMjRhMS40MDMgMS40MDMgMCAwIDAtLjU0LjM0MmMtLjE0MS4xNDYtLjI1LjMyNC0uMzI4LjUzMy0uMDc3LjIxLS4xMjMuNDQ1LS4xMzcuNzA0bC0uMzc2LjAyOGMwLS40NjUuMDQ2LS44OTYuMTM3LTEuMjkyLjA5MS0uMzk3LjIyOC0uNzQ1LjQxLTEuMDQ2LjE4Ny0uMzAxLjQyLS41MzYuNjk4LS43MDRhMS44NyAxLjg3IDAgMCAxIC45NzctLjI1M2MuMSAwIC4yMDguMDA5LjMyMS4wMjcuMTE5LjAxOC4yMDguMDM5LjI2Ny4wNjJabTEuMjktMy4wNTZoMS42NDd2OC45Mkw2MC43MzYgNzdoLTEuNDlWNjYuNVptNi40NzMgNi43MzN2LjE0NGMwIC41NDctLjA2MiAxLjA1LS4xODUgMS41MWEzLjQ4IDMuNDggMCAwIDEtLjU0NyAxLjE5Yy0uMjQxLjMzNy0uNTQyLjYtLjkwMi43ODYtLjM1Ni4xODMtLjc2OC4yNzQtMS4yMzcuMjc0LS40NiAwLS44NjItLjA4Ny0xLjIwMy0uMjZhMi4zOTIgMi4zOTIgMCAwIDEtLjg2Mi0uNzM4IDQuMDk1IDQuMDk1IDAgMCAxLS41NTQtMS4xNDIgNy41NDQgNy41NDQgMCAwIDEtLjMtMS40NjN2LS40NThjLjA1OS0uNTM4LjE2LTEuMDI1LjMtMS40NjMuMTQyLS40NDIuMzI2LS44MjIuNTU0LTEuMTQxLjIzMy0uMzI0LjUxNy0uNTcyLjg1NS0uNzQ1LjM0MS0uMTc0Ljc0LS4yNiAxLjE5Ni0uMjYuNDc0IDAgLjg5LjA5IDEuMjUuMjczLjM2NS4xODMuNjY5LjQ0Mi45MS43OC4yNDIuMzMyLjQyMi43MjkuNTQgMS4xODkuMTIzLjQ2LjE4NS45NjguMTg1IDEuNTI0Wm0tMS42NDguMTQ0di0uMTQ0YTUuMDggNS4wOCAwIDAgMC0uMDgyLS45MzYgMi40NiAyLjQ2IDAgMCAwLS4yNzMtLjc4IDEuMzcgMS4zNyAwIDAgMC0uNTA2LS41MjZjLS4yMS0uMTMyLS40NzItLjE5OC0uNzg2LS4xOTgtLjI5MiAwLS41NDMuMDUtLjc1Mi4xNS0uMjEuMS0uMzg1LjIzNy0uNTI2LjQxYTIuMDMgMi4wMyAwIDAgMC0uMzM2LjYwMmMtLjA3Ny4yMjgtLjEzLjQ3NC0uMTU3LjczOHYxLjIzOGMuMDQxLjM0MS4xMjguNjU2LjI2Ljk0My4xMzcuMjgzLjMyOC41MS41NzQuNjg0LjI0Ni4xNjguNTYzLjI1Mi45NS4yNTIuMzA2IDAgLjU2My0uMDYxLjc3My0uMTg0YTEuMzUgMS4zNSAwIDAgMCAuNDk5LS41MTMgMi40NiAyLjQ2IDAgMCAwIC4yNzMtLjc3OWMuMDYtLjI5Ni4wOS0uNjE1LjA5LS45NTdabTIuOTc4LjAwN3YtLjE1N2MwLS41MzQuMDc3LTEuMDI4LjIzMi0xLjQ4NC4xNTUtLjQ2LjM3OS0uODU5LjY3LTEuMTk2YTMuMDI0IDMuMDI0IDAgMCAxIDEuMDgtLjc5MyAzLjUxNiAzLjUxNiAwIDAgMSAxLjQ1LS4yODdjLjU0MiAwIDEuMDI1LjA5NiAxLjQ0OS4yODcuNDI4LjE4Ny43OS40NTEgMS4wODcuNzkzLjI5Ni4zMzcuNTIxLjczNi42NzYgMS4xOTYuMTU1LjQ1Ni4yMzMuOTUuMjMzIDEuNDg0di4xNTdjMCAuNTMzLS4wNzggMS4wMjgtLjIzMyAxLjQ4M2EzLjU1IDMuNTUgMCAwIDEtLjY3NiAxLjE5NyAzLjEzNyAzLjEzNyAwIDAgMS0xLjA4Ljc5MiAzLjU0IDMuNTQgMCAwIDEtMS40NDMuMjggMy42MDcgMy42MDcgMCAwIDEtMS40NTYtLjI4IDMuMTM2IDMuMTM2IDAgMCAxLTEuMDgtLjc5MiAzLjU1IDMuNTUgMCAwIDEtLjY3Ny0xLjE5NyA0LjU4MiA0LjU4MiAwIDAgMS0uMjMyLTEuNDgzWm0xLjY0Ny0uMTU3di4xNTdjMCAuMzMyLjAzNC42NDcuMTAzLjk0My4wNjguMjk2LjE3NS41NTYuMzIxLjc4LjE0Ni4yMjMuMzMzLjM5OC41Ni41MjYuMjI4LjEyNy41LjE5MS44MTQuMTkxLjMwNSAwIC41Ny0uMDY0Ljc5My0uMTkxLjIyOC0uMTI4LjQxNS0uMzAzLjU2LS41MjdhMi40IDIuNCAwIDAgMCAuMzIyLS43NzljLjA3My0uMjk2LjExLS42MS4xMS0uOTQzdi0uMTU3YzAtLjMyOS0uMDM3LS42MzktLjExLS45M2EyLjM5IDIuMzkgMCAwIDAtLjMyOC0uNzg2IDEuNTcgMS41NyAwIDAgMC0uNTYtLjUzMyAxLjU0MyAxLjU0MyAwIDAgMC0uOC0uMTk5Yy0uMzEgMC0uNTguMDY2LS44MDcuMTk5YTEuNTg3IDEuNTg3IDAgMCAwLS41NTQuNTMzYy0uMTQ2LjIyOC0uMjUzLjQ5LS4zMjEuNzg2YTQuMDYyIDQuMDYyIDAgMCAwLS4xMDMuOTNabTguNDk0LTIuMDQ0Vjc3aC0xLjY0N3YtNy4zOTdoMS41NTJsLjA5NSAxLjU4Wm0tLjI5NCAxLjg0NS0uNTMzLS4wMDdhNC44MTIgNC44MTIgMCAwIDEgLjIxOS0xLjQ0MmMuMTQ2LS40MzcuMzQ2LS44MTMuNjAyLTEuMTI4LjI2LS4zMTQuNTctLjU1Ni45My0uNzI0YTIuNzQgMi43NCAwIDAgMSAxLjIwMi0uMjZjLjM1NiAwIC42NzcuMDUuOTY0LjE1LjI5Mi4wOTYuNTQuMjUzLjc0NS40NzIuMjEuMjE5LjM3LjUwMy40NzkuODU0LjExLjM0Ny4xNjQuNzczLjE2NCAxLjI3OVY3N2gtMS42NTR2LTQuNzg1YzAtLjM1Ni0uMDUzLS42MzYtLjE1OC0uODQxYS44OC44OCAwIDAgMC0uNDQ0LS40NDRjLS4xOTEtLjA5Mi0uNDMtLjEzNy0uNzE4LS4xMzctLjI4MiAwLS41MzUuMDYtLjc1OC4xNzgtLjIyNC4xMTgtLjQxMy4yOC0uNTY4LjQ4NS0uMTUuMjA1LS4yNjcuNDQyLS4zNDkuNzExYTIuOTM4IDIuOTM4IDAgMCAwLS4xMjMuODYxWm0tMzguNDAzIDE0LjA4VjkzaC0xLjY0N3YtNy4zOTdoMS41NTFsLjA5NiAxLjUwNFptLS4yNjcgMS45Mi0uNTYtLjAwN2MwLS41MS4wNjQtLjk4Mi4xOTEtMS40MTUuMTI4LS40MzIuMzE1LS44MDguNTYtMS4xMjcuMjQ3LS4zMjQuNTUyLS41NzIuOTE3LS43NDYuMzctLjE3Ny43OTUtLjI2NiAxLjI3OC0uMjY2LjMzOCAwIC42NDUuMDUuOTIzLjE1LjI4My4wOTYuNTI2LjI0OS43MzIuNDU4LjIxLjIxLjM2OS40NzkuNDc4LjgwNy4xMTQuMzI4LjE3MS43MjQuMTcxIDEuMTlWOTNINDEuMjd2LTQuNzg1YzAtLjM2LS4wNTQtLjY0My0uMTY0LS44NDhhLjkwOS45MDkgMCAwIDAtLjQ1OC0uNDM3IDEuNjU3IDEuNjU3IDAgMCAwLS43MDQtLjEzN2MtLjMxIDAtLjU3NC4wNi0uNzkzLjE3OC0uMjE0LjExOC0uMzkuMjgtLjUyNi40ODVhMi4xNzMgMi4xNzMgMCAwIDAtLjMuNzExIDMuNzIzIDMuNzIzIDAgMCAwLS4wOTcuODYxWm00LjU4Ny0uNDM3LS43NzIuMTdjMC0uNDQ2LjA2Mi0uODY3LjE4NC0xLjI2NC4xMjgtLjQwMS4zMTMtLjc1Mi41NTQtMS4wNTMuMjQ2LS4zMDUuNTUtLjU0NC45MS0uNzE3YTIuODIgMi44MiAwIDAgMSAxLjIzNy0uMjZjLjM3OCAwIC43MTUuMDUyIDEuMDEyLjE1Ny4zLjEuNTU1LjI2Ljc2NS40NzkuMjEuMjE4LjM3LjUwMy40NzkuODU0LjEwOS4zNDYuMTY0Ljc2Ni4xNjQgMS4yNThWOTNoLTEuNjU1di00Ljc5MmMwLS4zNzQtLjA1NC0uNjYzLS4xNjQtLjg2OGEuODUuODUgMCAwIDAtLjQ1LS40MjQgMS44MiAxLjgyIDAgMCAwLS43MDUtLjEyM2MtLjI1NSAwLS40OC4wNDgtLjY3Ny4xNDNhMS4zNDQgMS4zNDQgMCAwIDAtLjQ4NS4zOSAxLjczNyAxLjczNyAwIDAgMC0uMy41NjggMi40MzIgMi40MzIgMCAwIDAtLjA5Ny42OTdabTYuMTYzLjc5M3YtLjE1N2MwLS41MzQuMDc4LTEuMDI4LjIzMy0xLjQ4NC4xNTUtLjQ2LjM3OC0uODU5LjY3LTEuMTk2YTMuMDI0IDMuMDI0IDAgMCAxIDEuMDgtLjc5MyAzLjUxNiAzLjUxNiAwIDAgMSAxLjQ1LS4yODdjLjU0MSAwIDEuMDI0LjA5NiAxLjQ0OC4yODcuNDI5LjE4Ny43OTEuNDUxIDEuMDg3Ljc5My4yOTcuMzM3LjUyMi43MzYuNjc3IDEuMTk2LjE1NS40NTYuMjMzLjk1LjIzMyAxLjQ4NHYuMTU3YzAgLjUzMy0uMDc4IDEuMDI4LS4yMzMgMS40ODNhMy41NSAzLjU1IDAgMCAxLS42NzcgMS4xOTcgMy4xMzYgMy4xMzYgMCAwIDEtMS4wOC43OTIgMy41NCAzLjU0IDAgMCAxLTEuNDQyLjI4IDMuNjA3IDMuNjA3IDAgMCAxLTEuNDU2LS4yOCAzLjEzNSAzLjEzNSAwIDAgMS0xLjA4LS43OTIgMy41NTEgMy41NTEgMCAwIDEtLjY3Ny0xLjE5NyA0LjU4MiA0LjU4MiAwIDAgMS0uMjMzLTEuNDgzWm0xLjY0OC0uMTU3di4xNTdjMCAuMzMyLjAzNC42NDcuMTAyLjk0M2EyLjQgMi40IDAgMCAwIC4zMjIuNzhjLjE0Ni4yMjMuMzMzLjM5OC41Ni41MjYuMjI4LjEyNy41LjE5MS44MTQuMTkxLjMwNSAwIC41Ny0uMDY0Ljc5My0uMTkxLjIyOC0uMTI4LjQxNS0uMzAzLjU2LS41MjcuMTQ2LS4yMjMuMjUzLS40ODMuMzIyLS43NzkuMDczLS4yOTYuMTA5LS42MS4xMDktLjk0M3YtLjE1N2MwLS4zMjktLjAzNi0uNjM5LS4xMS0uOTNhMi4zODcgMi4zODcgMCAwIDAtLjMyOC0uNzg2IDEuNTcgMS41NyAwIDAgMC0uNTYtLjUzMyAxLjU0MyAxLjU0MyAwIDAgMC0uOC0uMTk5Yy0uMzEgMC0uNTc5LjA2Ni0uODA2LjE5OWExLjU4NyAxLjU4NyAwIDAgMC0uNTU0LjUzM2MtLjE0Ni4yMjgtLjI1My40OS0uMzIyLjc4NmE0LjA2NyA0LjA2NyAwIDAgMC0uMTAyLjkzWm04LjQ5NC0yLjA0NFY5M2gtMS42NDd2LTcuMzk3aDEuNTUxbC4wOTYgMS41OFptLS4yOTQgMS44NDUtLjUzMy0uMDA3YTQuODA4IDQuODA4IDAgMCAxIC4yMTktMS40NDJjLjE0Ni0uNDM3LjM0Ni0uODEzLjYwMS0xLjEyOC4yNi0uMzE0LjU3LS41NTYuOTMtLjcyNC4zNi0uMTc0Ljc2MS0uMjYgMS4yMDMtLjI2LjM1NiAwIC42NzcuMDUuOTY0LjE1LjI5Mi4wOTYuNTQuMjUzLjc0NS40NzIuMjEuMjE5LjM3LjUwMy40NzkuODU0LjEwOS4zNDcuMTY0Ljc3My4xNjQgMS4yNzlWOTNoLTEuNjU1di00Ljc4NWMwLS4zNTYtLjA1Mi0uNjM2LS4xNTctLjg0MWEuODguODggMCAwIDAtLjQ0NC0uNDQ0Yy0uMTkyLS4wOTItLjQzLS4xMzctLjcxOC0uMTM3LS4yODIgMC0uNTM1LjA2LS43NTkuMTc4LS4yMjMuMTE4LS40MTIuMjgtLjU2Ny40ODUtLjE1LjIwNS0uMjY3LjQ0Mi0uMzQ5LjcxMWEyLjkzOCAyLjkzOCAwIDAgMC0uMTIzLjg2MVptNi4zODkuMzU2di0uMTU3YzAtLjUzNC4wNzctMS4wMjguMjMyLTEuNDg0LjE1NS0uNDYuMzc5LS44NTkuNjctMS4xOTZhMy4wMjMgMy4wMjMgMCAwIDEgMS4wOC0uNzkzIDMuNTE2IDMuNTE2IDAgMCAxIDEuNDUtLjI4N2MuNTQyIDAgMS4wMjUuMDk2IDEuNDQ5LjI4Ny40MjguMTg3Ljc5LjQ1MSAxLjA4Ny43OTMuMjk2LjMzNy41MjEuNzM2LjY3NiAxLjE5Ni4xNTUuNDU2LjIzMy45NS4yMzMgMS40ODR2LjE1N2MwIC41MzMtLjA3OCAxLjAyOC0uMjMzIDEuNDgzLS4xNTUuNDU2LS4zOC44NTUtLjY3NiAxLjE5N2EzLjEzNSAzLjEzNSAwIDAgMS0xLjA4Ljc5MiAzLjU0IDMuNTQgMCAwIDEtMS40NDMuMjggMy42MDcgMy42MDcgMCAwIDEtMS40NTYtLjI4IDMuMTM2IDMuMTM2IDAgMCAxLTEuMDgtLjc5MiAzLjU1IDMuNTUgMCAwIDEtLjY3Ny0xLjE5NyA0LjU3OSA0LjU3OSAwIDAgMS0uMjMyLTEuNDgzWm0xLjY0Ny0uMTU3di4xNTdjMCAuMzMyLjAzNS42NDcuMTAzLjk0M2EyLjQgMi40IDAgMCAwIC4zMjEuNzhjLjE0Ni4yMjMuMzMzLjM5OC41Ni41MjYuMjI4LjEyNy41LjE5MS44MTQuMTkxLjMwNiAwIC41Ny0uMDY0Ljc5My0uMTkxLjIyOC0uMTI4LjQxNS0uMzAzLjU2LS41MjdhMi40IDIuNCAwIDAgMCAuMzIyLS43NzljLjA3My0uMjk2LjExLS42MS4xMS0uOTQzdi0uMTU3YzAtLjMyOS0uMDM3LS42MzktLjExLS45M2EyLjM5IDIuMzkgMCAwIDAtLjMyOC0uNzg2IDEuNTY4IDEuNTY4IDAgMCAwLS41Ni0uNTMzIDEuNTQzIDEuNTQzIDAgMCAwLS44LS4xOTljLS4zMSAwLS41OC4wNjYtLjgwNy4xOTlhMS41ODggMS41ODggMCAwIDAtLjU1NC41MzNjLS4xNDYuMjI4LS4yNTMuNDktLjMyMS43ODZhNC4wNjcgNC4wNjcgMCAwIDAtLjEwMy45M1ptNy45NjgtMy42MjQgMS40MDggMi40NTUgMS40MzYtMi40NTVoMS44MTFsLTIuMjQyIDMuNjNMNzkuNTc0IDkzaC0xLjgxMWwtMS41MDQtMi41NTdMNzQuNzU1IDkzaC0xLjgxOWwyLjMyNS0zLjc2Ny0yLjIzNi0zLjYzaDEuODA1Wm03Ljc5NyAwVjkzaC0xLjY1NHYtNy4zOTdoMS42NTRabS0xLjc2NC0xLjk0YzAtLjI1MS4wODItLjQ1OS4yNDYtLjYyMy4xNjktLjE2OS40MDEtLjI1My42OTgtLjI1My4yOTEgMCAuNTIyLjA4NC42OS4yNTNhLjgzMS44MzEgMCAwIDEgLjI1My42MjIuODIzLjgyMyAwIDAgMS0uMjUzLjYxNWMtLjE2OC4xNjQtLjM5OS4yNDYtLjY5LjI0Ni0uMjk3IDAtLjUyOS0uMDgyLS42OTgtLjI0NmEuODM0LjgzNCAwIDAgMS0uMjQ2LS42MTVabTguMzMgNy44MDZWODIuNWgxLjY1NVY5M0g4OS4zNWwtLjE1OC0xLjUzMVptLTQuODEyLTIuMDg1di0uMTQ0YzAtLjU2LjA2Ni0xLjA3LjE5OC0xLjUzMS4xMzItLjQ2NS4zMjQtLjg2NC41NzQtMS4xOTYuMjUxLS4zMzguNTU2LS41OTUuOTE2LS43NzMuMzYtLjE4Mi43NjYtLjI3MyAxLjIxNy0uMjczLjQ0NyAwIC44MzkuMDg2IDEuMTc2LjI2LjMzNy4xNzMuNjI0LjQyMS44NjEuNzQ1LjIzNy4zMTkuNDI2LjcwMi41NjggMS4xNDguMTQuNDQyLjI0MS45MzQuMyAxLjQ3N3YuNDU4YTcuMzkyIDcuMzkyIDAgMCAxLS4zIDEuNDQ5Yy0uMTQyLjQzNy0uMzMuODE2LS41NjggMS4xMzVhMi40NTEgMi40NTEgMCAwIDEtLjg2OC43MzhjLS4zMzcuMTczLS43MzEuMjYtMS4xODMuMjYtLjQ0NiAwLS44NS0uMDk0LTEuMjEtLjI4YTIuNjc0IDIuNjc0IDAgMCAxLS45MDktLjc4NyAzLjcwMyAzLjcwMyAwIDAgMS0uNTc0LTEuMTkgNS40MDUgNS40MDUgMCAwIDEtLjE5OC0xLjQ5NlptMS42NDctLjE0NHYuMTQ0YzAgLjMzNy4wMy42NTEuMDkuOTQzLjA2My4yOTIuMTYxLjU1LjI5My43NzMuMTMyLjIxOC4zMDMuMzkyLjUxMy41Mi4yMTQuMTIyLjQ3LjE4NC43NjUuMTg0LjM3NCAwIC42ODItLjA4Mi45MjMtLjI0NmExLjY4IDEuNjggMCAwIDAgLjU2OC0uNjY0IDMuMDggMy4wOCAwIDAgMCAuMjg3LS45NDN2LTEuMjM3YTMuMDE4IDMuMDE4IDAgMCAwLS4xNzEtLjc1MiAyLjAxMSAyLjAxMSAwIDAgMC0uMzM1LS42MDkgMS40MSAxLjQxIDAgMCAwLS41MjYtLjQxIDEuNjUgMS42NSAwIDAgMC0uNzMyLS4xNWMtLjMgMC0uNTU2LjA2NC0uNzY2LjE5MS0uMjEuMTI4LS4zODIuMzAzLS41MTkuNTI3YTIuNTkgMi41OSAwIDAgMC0uMjk0Ljc3OSA0LjUwOCA0LjUwOCAwIDAgMC0uMDk2Ljk1Wm0xMC4wNTMgMy44OTdhMy45NDMgMy45NDMgMCAwIDEtMS40ODMtLjI2NyAzLjI3NSAzLjI3NSAwIDAgMS0xLjEyMS0uNzU5IDMuMzYxIDMuMzYxIDAgMCAxLS43MDUtMS4xNDEgMy45OTcgMy45OTcgMCAwIDEtLjI0Ni0xLjQxNXYtLjI3NGMwLS41NzkuMDg1LTEuMTAzLjI1My0xLjU3Mi4xNjktLjQ3LjQwNC0uODcuNzA0LTEuMjAzLjMwMS0uMzM3LjY1Ny0uNTk1IDEuMDY3LS43NzMuNDEtLjE3Ny44NTQtLjI2NiAxLjMzMy0uMjY2LjUyOSAwIC45OTEuMDg5IDEuMzg4LjI2Ni4zOTYuMTc4LjcyNC40MjkuOTg0Ljc1Mi4yNjQuMzIuNDYuNy41ODggMS4xNDIuMTMyLjQ0Mi4xOTguOTMuMTk4IDEuNDYzdi43MDRoLTUuNzE1Vjg4LjYxaDQuMDg4di0uMTNhMi4zMTQgMi4zMTQgMCAwIDAtLjE3OC0uODM0IDEuNDE3IDEuNDE3IDAgMCAwLS40ODUtLjYyOGMtLjIxOS0uMTYtLjUxLS4yNC0uODc1LS4yNC0uMjczIDAtLjUxNy4wNi0uNzMyLjE3OC0uMjEuMTE0LS4zODUuMjgtLjUyNi41LS4xNDEuMjE4LS4yNS40ODItLjMyOC43OTItLjA3My4zMDUtLjExLjY1LS4xMSAxLjAzMnYuMjc0YzAgLjMyMy4wNDQuNjI0LjEzLjkwMi4wOTIuMjc0LjIyNC41MTMuMzk3LjcxOC4xNzMuMjA1LjM4My4zNjcuNjI5LjQ4NS4yNDYuMTE0LjUyNi4xNzEuODQuMTcxLjM5NyAwIC43NS0uMDggMS4wNi0uMjQuMzEtLjE1OS41OC0uMzg0LjgwNy0uNjc2bC44NjguODRjLS4xNi4yMzMtLjM2Ny40NTctLjYyMi42Ny0uMjU1LjIxLS41NjcuMzgxLS45MzYuNTE0YTMuNzI2IDMuNzI2IDAgMCAxLTEuMjcyLjE5OFoiLz48cGF0aCBmaWxsPSIjREUyMzQzIiBkPSJNMTEyLjM2NSA3My4xMjRoLjI2NHYxLjczMmgtLjE1Yy0uNzU1IDAtMS40MDkuMTE3LTEuOTU5LjM1MWEzLjYyIDMuNjIgMCAwIDAtMS4zNDUuOTY3Yy0uMzUyLjQxLS42MTUuODktLjc5MSAxLjQ0MWE1LjgxMiA1LjgxMiAwIDAgMC0uMjU1IDEuNzR2Mi4wMTNjMCAuNTEuMDU2Ljk2MS4xNjcgMS4zNTQuMTExLjM4Ni4yNjYuNzEyLjQ2Ni45NzUuMjA1LjI1OC40MzkuNDU0LjcwMy41OS4yNjQuMTM0LjU0OC4yMDEuODUyLjIwMWExLjkgMS45IDAgMCAwIC44NjItLjE5M2MuMjU4LS4xMzUuNDc3LS4zMi42NTktLjU1NC4xODItLjIzNC4zMTktLjUxMy40MTMtLjgzNS4wOTQtLjMyMi4xNDEtLjY3NC4xNDEtMS4wNTQgMC0uMzY0LS4wNDctLjcwNC0uMTQxLTEuMDJhMi41NDggMi41NDggMCAwIDAtLjM5Ni0uODQ0IDEuODA3IDEuODA3IDAgMCAwLS42NTktLjU3MSAxLjg0MSAxLjg0MSAwIDAgMC0uODk2LS4yMWMtLjQyMiAwLS44MDYuMDk5LTEuMTUyLjI5OC0uMzM5LjItLjYxNS40Ni0uODI2Ljc4MmEyLjAxOSAyLjAxOSAwIDAgMC0uMzM0IDEuMDFsLS44MDgtLjI2M2E0LjU1IDQuNTUgMCAwIDEgLjM2LTEuNDY4Yy4xOTktLjQzMy40Ni0uODAyLjc4Mi0xLjEwN2EzLjMwNCAzLjMwNCAwIDAgMSAxLjExNi0uNjk0IDMuOTAxIDMuOTAxIDAgMCAxIDEuNDA3LS4yNDZjLjYyMSAwIDEuMTYuMTE3IDEuNjE3LjM1MS40NTcuMjM0LjgzNS41NTQgMS4xMzQuOTU4LjMwNC4zOTkuNTMuODU2LjY3NiAxLjM3MS4xNTMuNTEuMjI5IDEuMDQzLjIyOSAxLjZhNS4xNyA1LjE3IDAgMCAxLS4yODEgMS43MjJjLS4xODguNTI4LS40NjMuOTktLjgyNiAxLjM5YTMuNzEzIDMuNzEzIDAgMCAxLTEuMzEuOTNjLS41MS4yMjQtMS4wOS4zMzUtMS43NC4zMzUtLjY4NiAwLTEuMjk4LS4xMzItMS44MzctLjM5NmE0LjAzNyA0LjAzNyAwIDAgMS0xLjM2Mi0xLjA5IDQuOTQ4IDQuOTQ4IDAgMCAxLS44NDQtMS42IDYuMjIzIDYuMjIzIDAgMCAxLS4yOS0xLjkxNXYtLjg4YzAtLjk3Mi4xMjMtMS44ODkuMzY5LTIuNzVhNi43MjIgNi43MjIgMCAwIDEgMS4xMzQtMi4yOTQgNS4zIDUuMyAwIDAgMSAxLjk3Ny0xLjU1NmMuODAzLS4zOCAxLjc2MS0uNTcxIDIuODc0LS41NzFabTEyLjI3MyA1LjQwNXYyLjA5MmMwIDEuMDAyLS4xIDEuODU4LS4yOTkgMi41NjctLjE5My43MDMtLjQ3NCAxLjI3NC0uODQzIDEuNzEzLS4zNy40NC0uODEyLjc2Mi0xLjMyOC45NjdhNC41NTUgNC41NTUgMCAwIDEtMS43MTQuMzA4IDQuOTIgNC45MiAwIDAgMS0xLjQwNi0uMTk0IDMuNDQ0IDMuNDQ0IDAgMCAxLTEuMTYtLjYwNiAzLjYyMiAzLjYyMiAwIDAgMS0uODg4LTEuMDY0Yy0uMjQtLjQzOS0uNDI3LS45NjMtLjU2Mi0xLjU3My0uMTI5LS42MS0uMTkzLTEuMzE1LS4xOTMtMi4xMThWNzguNTNjMC0xLjAwNy4wOTktMS44NTcuMjk4LTIuNTQ4LjItLjY5OC40ODQtMS4yNjMuODUzLTEuNjk3LjM2OS0uNDQuODA4LS43NTkgMS4zMTgtLjk1OC41MTYtLjE5OSAxLjA5LS4yOTkgMS43MjMtLjI5OS41MSAwIC45NzguMDY1IDEuNDA2LjE5NC40MzQuMTIzLjgyLjMxOSAxLjE2LjU4OS4zNDYuMjcuNjM5LjYyLjg3OSAxLjA1NC4yNDYuNDI4LjQzNC45NDcuNTYzIDEuNTU2LjEyOS42MDMuMTkzIDEuMzA3LjE5MyAyLjExWm0tMi4xMTggMi4zOXYtMi43MDZjMC0uNTEtLjAyOS0uOTU4LS4wODgtMS4zNDVhMy45NjYgMy45NjYgMCAwIDAtLjI2NC0uOTkzIDEuOTc0IDEuOTc0IDAgMCAwLS40MjItLjY2OCAxLjQ3NyAxLjQ3NyAwIDAgMC0uNTgtLjM3OCAyLjA0NSAyLjA0NSAwIDAgMC0uNzI5LS4xMjMgMS45NCAxLjk0IDAgMCAwLS44NzkuMTkzIDEuNTc0IDEuNTc0IDAgMCAwLS42NS41OThjLS4xNzYuMjc1LS4zMTEuNjM5LS40MDUgMS4wOS0uMDg4LjQ0NS0uMTMyLjk4Ny0uMTMyIDEuNjI2djIuNzA3YzAgLjUxNi4wMy45Ny4wODggMS4zNjIuMDU5LjM5My4xNDcuNzMuMjY0IDEuMDExLjExNy4yNzUuMjU4LjUwNC40MjIuNjg1LjE3LjE3Ni4zNjMuMzA1LjU4LjM4Ny4yMjMuMDgyLjQ2Ni4xMjMuNzI5LjEyMy4zMzQgMCAuNjMtLjA2NC44ODgtLjE5M2ExLjYzIDEuNjMgMCAwIDAgLjY1MS0uNjE1Yy4xNzUtLjI4Ny4zMDctLjY2LjM5NS0xLjExN2E4LjgyNyA4LjgyNyAwIDAgMCAuMTMyLTEuNjQzWm0xMC45MzEtMi40OTVWODZoLTIuMTE4di05LjUxaDEuOTk1bC4xMjMgMS45MzRabS0uMzQyIDIuNDctLjcyMS0uMDFjMC0uNjU2LjA4Mi0xLjI2Mi4yNDYtMS44MTkuMTY0LS41NTYuNDA0LTEuMDQuNzIxLTEuNDVhMy4xNjggMy4xNjggMCAwIDEgMS4xNzctLjk1OGMuNDc1LS4yMjggMS4wMjMtLjM0MyAxLjY0NC0uMzQzLjQzNCAwIC44MjkuMDY1IDEuMTg2LjE5NC4zNjQuMTIzLjY3Ny4zMi45NDEuNTg5YTIuNiAyLjYgMCAwIDEgLjYxNSAxLjAzN2MuMTQ3LjQyMi4yMi45MzEuMjIgMS41M1Y4NmgtMi4xMTh2LTYuMTUyYzAtLjQ2My0uMDcxLS44MjYtLjIxMS0xLjA5YTEuMTcgMS4xNyAwIDAgMC0uNTg5LS41NjMgMi4xMzMgMi4xMzMgMCAwIDAtLjkwNS0uMTc1Yy0uMzk5IDAtLjczOS4wNzYtMS4wMi4yMjgtLjI3NS4xNTItLjUwMS4zNi0uNjc3LjYyNGEyLjgxIDIuODEgMCAwIDAtLjM4Ni45MTQgNC43ODggNC43ODggMCAwIDAtLjEyMyAxLjEwOFptNS44OTctLjU2My0uOTkzLjIyYzAtLjU3NC4wNzktMS4xMTYuMjM3LTEuNjI2LjE2NC0uNTE2LjQwMi0uOTY3LjcxMi0xLjM1NC4zMTctLjM5Mi43MDYtLjcgMS4xNjktLjkyMy40NjMtLjIyMi45OTMtLjMzNCAxLjU5MS0uMzM0LjQ4NiAwIC45Mi4wNjggMS4zMDEuMjAzLjM4Ni4xMjkuNzE0LjMzNC45ODQuNjE1LjI3LjI4MS40NzUuNjQ3LjYxNSAxLjA5OS4xNDEuNDQ1LjIxMS45ODQuMjExIDEuNjE3Vjg2aC0yLjEyN3YtNi4xNjFjMC0uNDgtLjA3LS44NTMtLjIxMS0xLjExNmExLjA5IDEuMDkgMCAwIDAtLjU4LS41NDUgMi4zMzcgMi4zMzcgMCAwIDAtLjkwNS0uMTU5IDEuOTYgMS45NiAwIDAgMC0uODcuMTg1IDEuNzI4IDEuNzI4IDAgMCAwLS42MjQuNTAxYy0uMTcuMjExLS4yOTkuNDU0LS4zODcuNzNhMy4xMyAzLjEzIDAgMCAwLS4xMjMuODk2Wm0xNC4xOTgtMy44NGgxLjkyNXY5LjI0NWMwIC44NTYtLjE4MiAxLjU4Mi0uNTQ1IDIuMThhMy41MSAzLjUxIDAgMCAxLTEuNTIxIDEuMzYyYy0uNjUuMzE3LTEuNDAzLjQ3NS0yLjI1OS40NzUtLjM2MyAwLS43NjctLjA1My0xLjIxMy0uMTU4YTQuOTc1IDQuOTc1IDAgMCAxLTEuMjgzLS41MSAzLjE3OSAzLjE3OSAwIDAgMS0xLjAyOC0uOTA1bC45OTMtMS4yNDhjLjM0LjQwNC43MTUuNyAxLjEyNS44ODcuNDEuMTg4Ljg0MS4yODIgMS4yOTIuMjgyLjQ4NiAwIC44OTktLjA5MSAxLjIzOS0uMjczLjM0Ni0uMTc2LjYxMy0uNDM2LjgtLjc4Mi4xODgtLjM0Ni4yODEtLjc2OC4yODEtMS4yNjZ2LTcuMTM2bC4xOTQtMi4xNTRabS02LjQ2IDQuODZ2LS4xODVjMC0uNzIuMDg4LTEuMzc3LjI2My0xLjk2OS4xNzYtLjU5Ny40MjgtMS4xMS43NTYtMS41MzhhMy4yOTYgMy4yOTYgMCAwIDEgMS4xOTYtLjk5M2MuNDY4LS4yMzQuOTk5LS4zNTIgMS41OS0uMzUyLjYxNiAwIDEuMTQuMTEyIDEuNTc0LjMzNC40MzkuMjIzLjgwNS41NDIgMS4wOTguOTU4LjI5My40MS41MjIuOTAzLjY4NiAxLjQ3Ny4xNy41NjguMjk2IDEuMjAxLjM3OCAxLjg5OHYuNTlhOS40NyA5LjQ3IDAgMCAxLS4zODcgMS44NjMgNS4xNDcgNS4xNDcgMCAwIDEtLjcyMSAxLjQ1OWMtLjI5OC40MS0uNjY4LjcyNi0xLjEwNy45NDktLjQzNC4yMjItLjk0Ni4zMzQtMS41MzguMzM0LS41OCAwLTEuMTA1LS4xMi0xLjU3My0uMzZhMy41ODEgMy41ODEgMCAwIDEtMS4xOTYtMS4wMTEgNC43OTUgNC43OTUgMCAwIDEtLjc1Ni0xLjUzIDYuNzYxIDYuNzYxIDAgMCAxLS4yNjMtMS45MjRabTIuMTE4LS4xODV2LjE4NWMwIC40MzMuMDQxLjgzOC4xMjMgMS4yMTMuMDg4LjM3NS4yMi43MDYuMzk1Ljk5My4xODIuMjguNDExLjUwNC42ODYuNjY4LjI4MS4xNTguNjEyLjIzNy45OTMuMjM3LjQ5OCAwIC45MDUtLjEwNiAxLjIyMi0uMzE3YTIuMDUgMi4wNSAwIDAgMCAuNzM4LS44NTJjLjE3Ni0uMzYzLjI5OS0uNzY4LjM2OS0xLjIxM3YtMS41OWEzLjg5MSAzLjg5MSAwIDAgMC0uMjE5LS45NjggMi41OTEgMi41OTEgMCAwIDAtLjQzMS0uNzgyIDEuNzk3IDEuNzk3IDAgMCAwLS42ODYtLjUyNyAyLjI3OCAyLjI3OCAwIDAgMC0uOTc1LS4xOTRjLS4zODEgMC0uNzEyLjA4My0uOTkzLjI0Ny0uMjgyLjE2NC0uNTEzLjM5LS42OTUuNjc2YTMuMzQzIDMuMzQzIDAgMCAwLS4zOTUgMS4wMDJjLS4wODguMzgxLS4xMzIuNzg4LS4xMzIgMS4yMjJabTE0LjEyNy03Ljk2M0wxNTguMDA2IDg3LjFoLTEuNjYxbDQuOTkyLTEzLjg5NmgxLjY1MlptMy42ODYgNS4yMlY4NmgtMi4xMTh2LTkuNTFoMS45OTVsLjEyMyAxLjkzNFptLS4zNDMgMi40Ny0uNzItLjAwOGMwLS42NTcuMDgyLTEuMjYzLjI0Ni0xLjgyLjE2NC0uNTU2LjQwNC0xLjA0LjcyMS0xLjQ1YTMuMTYgMy4xNiAwIDAgMSAxLjE3Ny0uOTU4Yy40NzUtLjIyOCAxLjAyMy0uMzQzIDEuNjQ0LS4zNDMuNDMzIDAgLjgyOS4wNjUgMS4xODYuMTk0LjM2NC4xMjMuNjc3LjMyLjk0MS41ODkuMjY5LjI3LjQ3NC42MTUuNjE1IDEuMDM3LjE0Ni40MjIuMjIuOTMxLjIyIDEuNTNWODZoLTIuMTE4di02LjE1MmMwLS40NjMtLjA3MS0uODI2LS4yMTEtMS4wOWExLjE3IDEuMTcgMCAwIDAtLjU4OS0uNTYzIDIuMTM0IDIuMTM0IDAgMCAwLS45MDYtLjE3NWMtLjM5OCAwLS43MzguMDc2LTEuMDE5LjIyOC0uMjc1LjE1Mi0uNTAxLjM2LS42NzcuNjI0YTIuNzkgMi43OSAwIDAgMC0uMzg3LjkxNCA0Ljc4OCA0Ljc4OCAwIDAgMC0uMTIzIDEuMTA4Wm01Ljg5OC0uNTYyLS45OTMuMjJjMC0uNTc0LjA3OS0xLjExNi4yMzctMS42MjYuMTY0LS41MTYuNDAxLS45NjcuNzEyLTEuMzU0LjMxNi0uMzkyLjcwNi0uNyAxLjE2OS0uOTIzLjQ2My0uMjIyLjk5My0uMzM0IDEuNTkxLS4zMzQuNDg2IDAgLjkyLjA2OCAxLjMuMjAzLjM4Ny4xMjkuNzE1LjMzNC45ODUuNjE1LjI2OS4yODEuNDc0LjY0Ny42MTUgMS4wOTkuMTQxLjQ0NS4yMTEuOTg0LjIxMSAxLjYxN1Y4NmgtMi4xMjd2LTYuMTYxYzAtLjQ4LS4wNy0uODUzLS4yMTEtMS4xMTZhMS4wOTQgMS4wOTQgMCAwIDAtLjU4LS41NDUgMi4zNCAyLjM0IDAgMCAwLS45MDUtLjE1OSAxLjk2IDEuOTYgMCAwIDAtLjg3LjE4NSAxLjcyOCAxLjcyOCAwIDAgMC0uNjI0LjUwMWMtLjE3LjIxMS0uMjk5LjQ1NC0uMzg3LjczYTMuMTMgMy4xMyAwIDAgMC0uMTIzLjg5NlptOS4zOS00LjI4aC43NjVjLjI1MiAwIC40Ni0uMDM1LjYyNC0uMTA2YS44NDMuODQzIDAgMCAwIC4zNjktLjMwNy43OS43OSAwIDAgMCAuMTMyLS40NTcuNzQ0Ljc0NCAwIDAgMC0uMjQ3LS41NTRjLS4xNTgtLjE2NC0uNDI0LS4yNDYtLjc5OS0uMjQ2LS4yODcgMC0uNTMxLjA2MS0uNzMuMTg0YS41NTYuNTU2IDAgMCAwLS4yOS41MDFoLTEuNTk5YzAtLjQxNi4xMTctLjc3LjM1MS0xLjA2My4yMzQtLjI5My41NDgtLjUxNi45NDEtLjY2OC4zOTItLjE1Mi44Mi0uMjI5IDEuMjgzLS4yMjkuNTMzIDAgMS4wMDIuMDc3IDEuNDA2LjIyOS40MDQuMTQ2LjcxOC4zNjYuOTQuNjYuMjI5LjI5Mi4zNDMuNjU1LjM0MyAxLjA4OSAwIC4zMzQtLjEwNS42My0uMzE2Ljg4OC0uMjA1LjI1OC0uNDkyLjQ2LS44NjIuNjA2LS4zNjMuMTQtLjc5MS4yMTEtMS4yODMuMjExaC0xLjAyOHYtLjczOFptMCAxLjA5OHYtLjcyaDEuMDI4Yy41NDUgMCAxLjAxMS4wNjQgMS4zOTguMTkzLjM4Ni4xMjMuNjc5LjMxNC44NzkuNTcxLjIwNS4yNTguMzA3LjU5Mi4zMDcgMS4wMDIgMCAuNDM0LS4xMjMuODAzLS4zNjkgMS4xMDgtLjI0Ni4zMDQtLjU4My41MzYtMS4wMTEuNjk0YTQuMDI5IDQuMDI5IDAgMCAxLTEuNDIzLjIzN2MtLjQ0IDAtLjg2OC0uMDctMS4yODQtLjIxYTIuMzggMi4zOCAwIDAgMS0xLjAxOS0uNjg2Yy0uMjY0LS4zMS0uMzk2LS43MTgtLjM5Ni0xLjIyMmgxLjYwOWMwIC4yMjkuMTA1LjQyNS4zMTYuNTkuMjExLjE2My40OTUuMjQ1Ljg1My4yNDUuMzggMCAuNjYyLS4wODIuODQzLS4yNDZhLjgxOS44MTkgMCAwIDAgLjI4Mi0uNjMzYzAtLjIyMi0uMDUzLS40MDEtLjE1OS0uNTM2YS44MTguODE4IDAgMCAwLS40My0uMjkgMi4wMyAyLjAzIDAgMCAwLS42NTktLjA5N2gtLjc2NVoiLz48L2c+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHBhdHRlcm5Db250ZW50VW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48dXNlIHhsaW5rOmhyZWY9IiNjIiB0cmFuc2Zvcm09Im1hdHJpeCguMDA1MDMgMCAwIC4wMTc5NSAwIC0uOTI3KSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjU2IiB4PSIwIiB5PSI1MiIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMyIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMTA5XzU1MzgiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzExMDlfNTUzOCIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFJQkFRRUJBUUlCQVFFQ0FnSUNBZ1FEQWdJQ0FnVUVCQU1FQmdVR0JnWUZCZ1lHQndrSUJnY0pCd1lHQ0FzSUNRb0tDZ29LQmdnTERBc0tEQWtLQ2dyLzJ3QkRBUUlDQWdJQ0FnVURBd1VLQndZSENnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ3Ivd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhRQUJBUUFEQVFFQkFRRUFBQUFBQUFBQUF3UUNCUVlCQndBSUNmL0VBQnNCQUFNQkFRRUJBUUFBQUFBQUFBQUFBQUVDQXdBRUJnVUgvOW9BREFNQkFBSVFBeEFBQUFEL0FIMDRxWWJlWTRBSHNPd2tURUJnR0FIRVFXQXVRZkN3SjFGa0dpQzA4Q3BrR1ZNaWVqZmZmTi9ad0RHU0FJbkJzTGFmVERBQ0FLenVvNVRJQnlMNDJKc0RLQTZrNllPdURLUkJaUHZYbVB1am1BR2NrTUp5Z1lBY0IwN2dLZ25tQlF6c2cyT3dVWEFrSFRBZ3lDZFJxaHVoTW4zL0FNajk0UTAyY3d5RFRocFRwWEVUVEY4RExpV0JreURZc0twV3lCMWZSenkxa1Ridzc5aEhXWG1QaEgyanpIMU1RWnhRbGNTM294WWcybGNRVVhFNk9xQTZmc2ZOcDNIcFg4VGlRWkdKeGxSWllMUWt0ei9iUEdlaU1QTVNXUHUwN0hIWW0wdFptMm1vb05qWUJSRFlDd05sRjB4SndPSmx4T0JwelZVS0o5VDhkOTZaOFJ4Z2sySmg0Q0ZGd1lFNG5vaE1uakVhREJrSmw4WVlIRFJBZERJQ2t5WlJaQWVmMVh4dnBjR3hrZzRDaWt5QTJFNG1BVmtUZ1hCdGdkQ2VSdE1iSWJnTWhGU3BJV21GVUNxZzYvYi9BQW5yeWJBK0JzQldaMUFxVHpuZkM0QnNEeUZrSjFGMUtxRVVOcERTWlVRcUFYVUhXZDFuZFB2WGhQWGdkT1FEQUdRU3M5Qk00bWRBWUN5QlZDZFFjRVprMHl0RWFUbnFKM1VHV2FpQldZMVNlaWYwSitlZTBuSUIxRmxuT0IxbWJUbFFaUnBJYUlEcE9aenVnUE9heURVVE9KM0UxWkJhWTBRVE1YbVR3L29md1B1QU9BaVlnR1dkbEZrblpKbVFLTE15elVTWjFDcUEyaHZMWGRFWGxUVDluRythU2tjR1NSNTQwaDR5L3dCQ2VCOXJOUkp5SjNTUjFtSW5ZQ1JPd0YxK2RmVCtkMFhQMTJyVGh2cS9LM2ZOMUZSSlhsK08rZC9UK080MjFuYjU5My9OMXRvV0hidWRQNmIvQUQvM2ZNOW5MK3o3bm42WW1USEV5ZGZTTTdKckx6K0JmYitMMk1xOUhHbXB2enhPS1FmY3BrY1oyOE1GWTlESG80YnI1TlAwY3V0cEhySnYvU2ZodmVjWDE4YkI3UVZaZGR0NFYxVENWMWdaT0Y2bzdvQzVUSFJKMkJzbGVFbEphcXFhUy9Mc0pXRW5WZFBIcUs4OWViNzk0ejJlcmRjTU1XV1lpS3N0V1JleGhlWXNOUlJOU3Jlc0lubTdUeFk1bE5VOGxKMXQrWTUyQU5qYU94dHlRNC9VZkgrdUIxMWo3VU1zekxuaEl5Yk84Qk8wN1BnMGJWdHVMUml3NTVrMTBMYmRrMWFEVmRVRzZlZUpIeXduWmJxdzhPNnZ5bnEvSDNaUGdaZVR5YW9IVnFOUmFTc05tdzBKZkphNFc1K3JXT21OcmJjZkowbkRJbGFZTlBVRTRaZWd4cHlkRFRuNlh6bnJmZXJsNkxwNU9SNHUvbmtmVnNQeEduZWU2R3lwUHIzM3paQnJOWHRhOG5QVjBiUzU5UzJTSnBidVQwOW5HdlR4emMvUjBuemZ0ZG4xOCtQeC90TzJndnhSenJvd1NacDFyT3E1MmprVjNuVHgxWGtZdG9NM044blJRTDlUV1ByL0FEK0FFb21UbkdQMStzL1JUVTgvV0ZoLy84UUFJaEFBQWdNQUFnSURBUUVCQUFBQUFBQUFBUVFDQXdVR0ZCSVRBQWNWRUJFVy85b0FDQUVCQUFFQ0FmNmZoSitINUtSSm1abVJrWm16Mm1Ya1Q1K1htWkg0WmY3NUdabWJUY2JqY2JUYWJEWWJqYVordzJld3pNalB6TmhuN0RhYlpYbTgzRzZWeHRsY2J6ZWJUWjV4ak9yeGxVZjU3RFlaR1JrYit4YlAvZktYd21jcDE5WHF5VmtyQlEwVmZKaXlpYTVVZ3JKU1ZIVkZQYkxKc2w4OTN2bGI1MldTa1p6bjd2YVpuNUc4Tmxuc2htVGszcE5UWTdSdm5jYmkwVzUzZHFiYzNaWGtrZnp6UHp5ODVXZTQzeXZsY2JwTmR1VEhhTG5jTHBkN3BlTDM2QmY3M2U3NWU3cGVrOGRIdjk4dlRlOTV0TnNyamFiZmQ3emViemVXQzBXZXlXU3lXdXlXaXlXaTBXdTJiemVXQ3pKbVRaWkxKYkxSYkxVbUN5V2UwV2kwVzVPRjB2RnlUa25TekpvdEZrM2xpVFVtWk5Gb3Nsb3RGc3Rkb3VGeVRoZExzblM4WEpPU2FMUlpMY215MFdpMFdKTVNaTFpiazVKMlRzblpQRjJUcGNMWmNrMldpMld5M0pzdHliazRYWk9sMHV5ZGs2VzVOdE8xTzJQSzdWajFXaFcrOXJ5MEMvTFFPakxUbHBTMVpha3RPV2tkSHZiMzJKajhqc2ZHNmQyM1NpOWRwVzhoaHpGelR1NWhaeS9QNStkdjlOemwrTnQyNmRtdWQwYi9BUDFGbks3dVVhYm5IZEdqY0RNdGVXN0hVbHJzTnlkL1MxN2M2MzJwYWt0ZGxuOVJmVmsxUFJzMEo2Y3RVNmpqT1pxL3ZXYjFXM0RaR3VkWjNYbnlCWGRhdlVQbXUyTjIvVnR1RHNOVjdsRXVUcjYxYnptMjFPUm5DNXF1enVSMlg5R2IwV2xOKzNrSjVGWFpQU1l1N1RPb05mOEFTdllyYjB0dFBjYTBxK1JJYUczcjVlcHkza1dYekZUUXVhYjFMdDZ2a1k1WDN0WGJXMHYwWU4xNjlqNGZZMEpheldpbnlKdmVTZlkxYTlTVjRTNldmeXFlNjF1Nld2WnIvc1V1NUYrbFRCNmpkeitUYWUydnU5L1VmcmJjME1qNDFndXF3ZHpPY3k1TXh1MWhodWVoMVVEZXhwWm4xNVR5bDdCVzVVMmx3MW5pTnZGbzVMeXFleGs4ZDJjOC9adVp5MDVtM0dISlV1VDhUaHp2QUd4cDZhckNmUGJ2dFJqN2MyK1Uvd0QveEFCRkVBQUJBd0lEQlFNS0FRa0dCd0FBQUFBQkFBSURCQkVTSVRFRkUwRlJZUkFpTWdZVU0wSlNZbkdCa2FFakJ5QkRZNUt4d2RIaEZTUWxNRlB3VkhKemdvT2k4Zi9hQUFnQkFRQURQd0gvQUM3L0FKbDh2endoejdRTzNyMmRlMGR0dVBaMS9QUDVvN2IvQU9SMVhWU1NlRDk2bWJxUG9WZjF3bnR6eHQrcVBNZlhzUFQ2SzQvTWwxM2FrenRDRktkWWg5RWZkVXRzbk0raW05cHFxaDRjS3FqNDQybEczZ2FQbXBIRFZuN2Ftdm5hM1BFdjFpOW1WQWp2ek4rWVRHSEtRL0pNUEg5b0tKd3NXaE92M1NWTWRYSjJwZWZvbXZ5RHltREs3bG5rNjN4VjlaRkV6VnBQeFdWN3BoR2loZG5mN3BsdkNtdDFiODBMK0ZBOTBXVVdtN3o2S2w0dFZOZndPc3FNanhPVWZDVnl0K25QN0t1UFRmWk50NmI3TGxPbnMwbFQvd0RVWDYxeVBGN3ZvbURpbkRSMzBYZHRkcWx6c0dmRlZCOVlvSE95YWMxQmV4VWJUaVk3N29jOGtIT3RmSkRVdXVvdjlUanhYZXlIM1RENG83S0YraElUU1BTaFpYeGhlK0UrMXc1U0hTUUtVZXNFL3N0eFR1YWQvc3A1enVwUGFVdkFvMjlJVkljc1pVdXVNcDR2K0o5MGRONGlQMGlOa2RBbkQvNG5XSXhLYTl0OHBSckw5VnIzbW8rcVFuTjBrUHlWejRqOGd2MTVSc2JTcm05RG12OEFka0Z5S2Q3UVR1WTdSd0tIdEllMGg3UlhKeCtxUHRMNGRudkwzdXhxSHRMMzBQYVh2THIyZFVlenFyY1YxUjVycWp6WHZMcWp6WFhzNm9jMWZpdXE2bzgwZWE5NWRVUnhYVmRWMVhWZTkyOWUzcW04ME9hNjluWHM2bzgxMVhWZGV6cXVia09hQTlaZFYxWFZkVjd5SFBzS3Z4UTVycXVxNnJxaHpRWFZkVjFYVmRVVHhSenU1ZFVUeFZ1SzVGSG4yZFYxWFZkZXdjME9hNnJrNWRWMVhWSG1qelE5cEVSOTEvMFR0N205QmpDNitRQzg0eDQ4ckZBTkphNUY3Q1hIUkNRRTNWUFF4YjJwbXd0T1NEb3hKREpkcmhjRWNVUGF6NVhROXBOOXRNdDQwMzJrT0RsMVI1cnFqelRpb3FGaFpzNXVPVnN0bkNWdVZzMUZ0bWtOWFQ2WTdaaGJ2eG15cXp0RUIxVy9EdmJXdnd1b1h1d1J5Qy9KT2N6QzVBT3ZpVjRuOTcxVlVRT2ZGRTF1dGpkU0J1R1dsQnl6SWN2OE9tM1V0bkZtUkMydHNmWjBYbWpHUzRua2ZpTjArNnI5dGZnMWd3Z1JrNFFQVzVwdEhSTnA2bUIwcGJrM0JsM1Y1M0JGdEtERXplRHdrcU4yczJYUlVJanRTMURybjFuc3RaTXFIdjhBUGF3NGJYYmt0bGdHMVM4OVZzOWc5TVZzb093YnQ1NnFoYURlbmRybFpOYTRoMUt3Y3MxWW5EQXpQcXFrdnhOSWJaUGxxS2w3bmZwZjRsVFE3S3d4U0VmaW0vMGFuaktkeGVtdG44Nk1yYkI5N1hVYmFqR3lVZUpXOWZFRklZdDYzVDRxSXg0UTZ6clozS0puZmJQdkxrUW15UVBaZjFTaTZnaC81M0lpbzE5UjNEcDhGeVp4NWYwVXpLQ09FdXlhTWdtRjRiREpmMnlGbG4xVzRZRzhjSVVkVzNDSk4yVzZ1THNrMXd3dDJoR254SERiL3VjYmZSVHRpRnBBTDhuNnB3dnZaYmtEbWkwV0pOK0F1aExLZSs3bVNVQytheDFmcjlWdWFVNEtpMWpjdEp5dFpVd09jandQZE9hcHNGN2NmQ1hLbm11UlNITHhXVk05OW80bWNpWHlXK2lCbzVab0F3bU53eTFBdmY4QWtvSW1ZcG1ndUp0aGpHWVRHVk1iV3c0bXlTTnNUZFRHcGUwd3R5ZU5CelZQREs5OGoydGUwV0RYTnlQTkRhTVFiUjA1Y0d5SElEb3BJYXN0ZkFXV1k2L2Q2ZkJTWDlHZjJQNktWelREaThQQWxSd3NMQ0hGdzlIaWQvQlNiOWdobkJ4YWtqSUZWVDNZak96VFFYVWtVYjQ5NHczNDVwdExJSkcxRVdQbGV5cTRKaHZaY1ZtOTJ6amtuVFJGa3BOdVpmZk5Pb3BzYldOZGxiQzUzRlN6VG1xcUtyUHhEQ2JsUmVMZVRBbDJaT0U4RlRPakVrQWVYT3o3emhucC9OVllMWFJ4T09JMnd0NzJmeVcwYWFRdWtwWGdjaGtWUExJSUlZbll1WjFzalR5Mk5XNjloNGhZbTZaR3lWa01HOWUrVVdOcjRiWHVuVUVkUlMxa2JtUG13dmp1TXNJdW9xaWNZSnpDVCtrZmNOMFdBVVRwNTI2Z2szK0tZK3BsZTJXNEwyNS9KUlJiUmZUVmpCdThiZ0JoMU4xVzB0U1h3MWhZTG5DMGFLZXBlMHNxYnp1ZUJrYkIxOHMxUEZOTERYVHdzbGpkbTNlYTgxU3dNSWt3RjloaExaTlFqSGFXU2RtZW1hYTUyRjFYR3dFM3hFbitDcUlyNGF2R0NiWW1IVlN0OWZJTEc3TWxQWUc0cDJtNHlzYnFxbmZndTRnWjJBVkhITThlZEM0ZG0wSXp4U2lsZ2tjNFJrV2EyNjJoRkhpcTZNc3dQc2VCdnlSbk1iSTRqaVpsbTRBRlRiT3E0ZjhBRW1IRTRkMXVvK2ZCU1JUVlZUU1NBdUxqWjRJNkRWVFN4UnlWSllYTmxCeHVzUzN2RGlvWXR1U3VrNzdoa0w1aXlwbzVERFVVakJESWZ4QTF4R25KVWRkVHorYjNMV3g5MHVBeXVkTWxGZkJjSHZaZ3Z1blJWR0dId2hvOGRyM1VFRC83ekxoYzZ6aFlMMDlTMFdMbmZodndqTUE2NXB0UysxYzV0UTIrYlhNMXlXeS9QVzFGSSsyQnpYR01FRFRYVXFscWRxeXpSemdpVEZodjFBV3hJNHdOb1NSWWdMV0xibFRUZDEwcmlPRjFDeVVGNUpaNjFqWXFLUmhFT3lvSkFOY05NMDJWT3lMZXo3RmhZMzNxTnVTcDZxZVNjTURJcjZNYUJiNEtqWSs4VG5CaDAzbXYyVDJFTzNodnpDYmZHMXhPZWxsTlNSN3BqemJGZkoxa2E3WmJvekk4dkZuWGMva3BRZDQ2TUVBMzcyaURudURhVWtYMXhKOGJDSUd1akp5YzV6eG90bm5ZbFB2cG5PRXNIcEwrQTR0QUxLbGViUjFraEZ2K0RKL2N0aVU4RHpGSE9aSkFjbnVhQVA0cnplbWRGRFZ5d0QxV3NPcVpESzE3WFpPTjNkZWFnbG1NbWx4eFZGVVRSeU5BZmFQbW9ITndBV3RvQW9qNE9mSmJCYnNWdFZVVkR6TzZjdGZDMXdHRnRzam9xRGFkYkhzbWgySnY1SHZ1MjlRR3VkN292a2VnVkZzWHppamoyUXlZU3lOdkVTWFRRT2JyY2dhSG9WREpRTm5lL3BoNm9VZE1QN09sTFg3Mno4WXViS0Y5VytpYkRUNU5OM3NiWS92dDlrUE41WVgwMFF4dXUyWE82YlROTEhSTmQ4VkpWVldKN2hZdjcxaGtGVHNud2JPbGVZaG1IbGxpU3AzdnRHWEhtSExaTEtJTmhMOTl4YmdHRnZ6NHJaKzBYYnA4aGplMjE4UXl6VU1GTEl6OGRyMkRJYnNaRzNLOTdmSzYycFRWRG13UVR5TjRQWkEreCt5cXBNWDRSUERSUXhiQmtvcXVkckoyTURJck12a0ZXenhqY1V6NUR4ZGdObHRvNVJiUExQOEF4bFNSdi92Rk5JRzg4QkZscytLSU83enJISlF5U05aR3h4WTYxeWNsRkZsYmdPV2k4bSsrZHR4VkEwRWJvckw4bkRKR3hpYmFHWXZpQWJrdkplMGMreHErV1JydGQ2TTc4TFpacnpIOG9Pem9ab1ptRXoydkpFV2c5UENxaGxOVk9obmxCMzV0dTVIZytrUHNpNnBCUlMxWXhsOE00M2QrV1dvMFQ2amFMWXJpNVpsbFpvK1FXMmFtaGwyeHM0NzV3ZFprYmJNRHVlWmNMTHlsY0RpMk82Ly9BRjJaZit5Mi9ENmVnZG4rdFovTmJUZ1phU2lkcnJ2Ry93QTFVYlBMUld3dlpqMDc0UDdsUzBrVDRxbWxFdUwxemtRRlB0a0hhZTlFZE9IV3RmdlBQSWYxVHRpYk9jSUtrNDVYZ2VHMXJLcXFhbWFSKytwdzl1TEtVOFBndHExWlpOSHRDcWJ2ZTluVU9Oc3VWMVNUMSs1bWlaM3k1KzlMTG05c1J5V3o5azdLWnRHVFpUSk44Nnd2Yk1LcjJtNXRCVHh4VWpINlBhM01OVkZHUE5ObitleUROenpXbGh0OE1LZDVUYlNrMlpXMUw0aHVpK1BCRTEzeSs2OG12SmhqRzFtMEo2cUo4bHZ3NGNHQjlsNUpNYUkyeTFmd3dCYkhxZ0hRek9adXNtRXdqdkM2cEl0b3h6a0NhTVNCejJ2Wms0WDBXellXTnBJUEpuWTV3aklPb1NmdVNWUjBEKzc1SjdGeHR6YnU2TENma2VDMnpYVmtUNUtlT1BjMWJKUTZLOThqcG1xaXRtbWlsa0c3ZTdFV0Z2VzYvOFFBSkJBQkFBSUNBZ0lEQUFNQkFRQUFBQUFBQVFBUklURkJVV0Z4Z1pHaEVMSEIwZUgvMmdBSUFRRUFBVDhRV0xOUkRLTk1RM2NaTExqZy93QVFFRzJZVlNxVjJSc3kxTDl4RUh1UDRpRW1HR0lpVmtPUzBTUDhJbHR3Z2Fpb1M1dVV3cVpkYk15MkVYTWJMRk1DM2FRVzZoank2aVVXL3dDSXk4d2U0eUdZTEdlVTNaaHNYL0FYZXB4dEViVGNWOU1mdU9XL2szVXhrUmZtTTNUTWJjclJIU0hORDJrZ3EvT0xMN205TUFYWDNDQWwzcUZRMmVMWWxhSHZTT2cxUnFOQUxwbnN1VVZERVZBMi9Fd3hQVWc4ajRsRklUekQxUWhkRHF5QW9MN0k5TGIrMHVFSU9HRlVnOEFJTlhoOGtZd0MvREYxRHhraUw2MG1uOTRQV2EwaEdPbDRpQ1lmVUdGTnpVcktLZW9qZjhwbkFqdkZnUmUzcVhsN0RSQUQ5eExZczIxVU1yYnM1eEJKOW1DNnFzNVlnTlFhMlZGVzI5T0l1M3RUUWJsZEZQQ1FxWHlEbVlBTDRsaERlVWlEaDJCTmJwM2NsZUUxTWcrS1NYRnRSYWkwaVBYV2pNUk1vNFV5clJqMmpNUjVWQ1IxWEh0Z1NteGtIbVo0SThGU3FCSXROa2FXME9CNXVZQXA4MlhBcS9OS1pWR3ZaRHBZdXlEVmd2Tk9TVXRtTGhrcDh3eFdQcTQvc1dWMTJGV3l3VmZWeEFJUHBnckh4eHN5elU0QjZUOWdsNUViSk56Q3c2a1V0RXZ6TitEQTR2MU5sUy9NR05FdUN5SjZnMVRQdVVhZWtZcHBLc0xBN2pKVkMrN2plRnNXelpLcW5QaVpGSk5XSHM5eFdVK3BtUHVwY1JwZGNSVUFQZVltVHlMV2ZUSzJ2c3BsbHRQVmtLNU5Zc202djZKckJnOTFpT3RqdFRuK05DdUNNMGtJeXdkdFBwbEpwVjdsSnAvY3JOd2FXUUhOZHZiS3hBKzBJdjhBN0J5ZjNtMWdLNFdNRlFPMFloYVA3ZzZqTzhZWnIxanZPeFVnbHQ0YlN4MjkrWVNPY1NydkVxb2RjdG5EeEVseXVtRWJKZXBOUzJwMU03b3pzNGRpQmt3bk82Um94Q3QzSExPb2hlTTRzSTJUMWZLTGFTSVdNSUdDYWk2d1k3Y3pOd0tCRkVxMnNwa0dJcThaNUp0ak1oS3EwM2tKdGtDNlpZc3FXZXlWTVNRM0hDVGlYYVBtZnBhbTBaY01tWUZ4QmFRSXRqZWM3UDhBajdxamJOSTFJYmtpMHduTllBSVFaWktxeUpjS05SbUliUXJOeEI3VGpUNUpsWGVLY3NNR1NuZlVqU3NrRFRCWE9qSlVwR3lGVi90bTRoSEJyRldrdUc1QXl5c0d6Szl4UXZiYzI3M0ZFbFNoRFpBY1RlS2g3VkQzRG10dHoreEhjR0NZaFJWYVhqd1Fmb0dYQTZZc0FEUXdGekxVL2M0NWxoY08xT2JBY3pvZjM5eE1rTVdIYldGZnV1RmFGS2IyUU1HcXJRM1E2dDdoOTh4SXdQaFBCTDZUYXZZa1hLVGVLb0hsQitNQjBNaTF0YTVKZkF0S2UzdXFqbTdoRnBvWVBad1VhZ1VZRXB4OHphamZnVmd6RnFXdlNvME43ZlVSOWF0SlF5WnFaU3hSVnBnejVqdDBFS0JISVp1SjRLVU4yL01UQ2pTQVArekprVUtNWS9aU05EQzNFV2xXbGdiT0o0RUJZU1V3ZUZOMzNNeXgwR1AvQUdLd2JzZkxSUFpNUmtaZmx4cFh4U3l4UGsyU1hMTkFOTGU3aDUwbzBadmZNUUNXR3lBaEtOOGEyNU05UUtlUy93QnpNR1I1R0E0ZEZEQjhUYzRIZzhNMmwzUG04WU9yUlhSa0xjcTA0TXMyb0FCb3kxODVqZHZyZkh6TE9LMk1ZQ1hsQXRxN2YzY2VBOGl0eEtXMk5Vam5lRWJPR09XM0ZjUzF2U1cxTnVCakErb3ZuVHBuZ0J3YUY3WUNXMDBOTk1aT0hEbFVHT200bzVkYjVXYW1adFREaFhoeDYxTlFUZHQ0YnBpOXhFRDJ2QUM4NWhjWWJMWVh3cG4vQUZGVnpDaDZpM0VJK09ySEtsNTlPSGNvTnowSU5MWEJOY2t4bFFBbW11NC9uRUVoZ3RYWkdOZ0VnckxrSDl3dFcvcFRoZ0h4dnlvTVdSUnRKbkZxM0hMbzAxU1VUREtOYTlCVXFwdDI2WnNoWWdQbGRQMUE0Tm9CdFkvY1lIWEk5bFI0L3dCWEEzL3FYSWlWRUlMVG5DY1ppd0w4TlIyd0VKcGFnZ3FTNDFCUEhNRk8xVmxIczNMY0J4WnVBMU9yUzZIZ3ZNYW1oYXlRMWl4SUJoYmNvOWpHYitLaktNS0NRenhkR1lMSjJiVVVyT09iUGtnRzFyMWNDcnErTVhCYnNPWjZNYWNGUXEyQTR1b0hoaXNCYUsxQzBwbUxMRFlxOHh3MGMrOGxSeGpIMlFRaUlzUGt2Y09YQzNiY2N3M0VBZnMva011UEJsVjk1cWNsazRIbWMzRWRHN0pIaHFDY29xeFJlV3BiOGNXTFBHU2JHRENyYXZENXVJdGg5YkloU1ZYcmpwRHRBMUQxdHJGZWpaUmNKa1plVXdlVnF6Y0krZURRU0s2MzNHQ3gzUWd1ajBRWlZsWVJYU2JwYTNqY1ZhMkdZN0JGcWQvRTFaZ0w1ZDlPbU5HcGFyOHJtdzZ4QWZPQU5ncDh3YXBiWGExc2VHSlowaWpGL0xEdUJkMVM0R21EeUZxeE1aSk16Z0ZlWWpHV3pvdDNtc2Y2d2NEQjJVZEVabmtSZEhOYmxOZWgrdmF6SFdwVkZadmk0VkVyT1VGMEhCNjRKV3BOcGorQVRjVk5ZYjhZaldoVXE1ZDNlWXpCYWF5dXEyY2FpYjg1MHBPZkRkUTdWNEwyZkpHK1RSUWZqaVh1bnFhOVdCOThRaklpc0pZeXZEbnVBVmRMWWZWcUNmcFdhQnJDZ3UrSVIwMnhuWEMyWll0ZERVN1lzM3l0eWtUUkVXcXVXdzJFVmh2V0V1R1FuSENLVlhheFJSRk1oQnk2bUR6WHhUVFpjMmZFWGFzNThHMmlOWkM3MTFBRGhncDBWQmx2UjJXUTc5NUEyQ3hRajBuSm5aS1hKVmV0UVYrNGdxRG5Gemd0aGRXazZpY1UzbTg3dUJXTTdVNTVncVdhdUd2T1BVWGtFN0YyTGFsWHl2VXIzRWVtalJiQktOcldZMURJTmxsc2xXN2VtUHRmQ1hGeUN5Wk5nNm1LbXE5Wm5rdlpGcVliZStIa1l0VGRVWmdmZFl1T2I1YkVSYnBNeFoxNHRaMWtxdjN4QnhXYVRxTjZ6RzZuU1IrWnV6dTZObjVGNDNsaXlxaXhIcU5EKzhDcFRhbVhxUDRaMUw3aVg5UXZGeDVsb1UyWFZkeXR1eDVGT1N2MGxkcHN6ci9pSFJkOUk4dHMzZTJ3VzNEazEwaHFxNksvTHpBdjQxQ3VoWmNqVU55V3Vyenh6bE0yb0dQL0FBbFlBSW9ldlBMTVI5cERubzVOdXlIcUxGMlM0S1JmT0lsM2tYdGlnS1F0NXBNUGxFMlY1UmJ4WkNscVM5ZGNDTXloQ0ptRktXRldXUEVya0I1THF5TzBlY1JQSGJBREMzUVdhSUJNZHFMeVpDbDBYTDlDRjlBVlRGMTdpM3dsb0VHaW5GcnVNOUMweFlhQmJmZmlGUkFBTk9zWFhkeGpNQzVMRzZOMGtYRE9RSlpZSW9uU1FZMW8vUGFmc3hQT1dJdDRRTXB5cDY2M0RVMmZzZWh5cTVWdHZiUC94QUFzRVFBREFBSUNBZ0VEQXdRQ0F3QUFBQUFBQVFJREVSSWhCREZCRUJOaEZDSlJNa0p4Z1FVanNjSGgvOW9BQ0FFQ0FRRS9BRGFOb1ZDWnRDZjBUK3ZJNUV0Q2JFeE5vVnY1RmV4TVRRdnJOSEkyYzBjem10SElURTBLeFdLaGZSSmlla0pvVEYzOUZiUXFRbm9UMzlkbS9wczJiRStqWWhNbm8ydnBzZHpQc1dTR0s5ZkFzaUZaelFrSnRFc2xpUHVzNTdRc2g5eG5NNW9WeXhYQytTY3EvaysvSXZJalF2SUY1S0g1T3ZRczdwZWgyMFRsdE1qTjEyTHlKWHdMeWwvQitxcEwwTHlxYUo4Z1hsdEg2bW12Wjl3KzRmY1B1bkk1b1ZmSno2RTZGVkNxeWFyWG9UL0J2OENwL3dBRTMrRDJhUXVsNitxVWt3aVZqZnl5WnhyMGNqbXhVem05ZG14UFNaTjlhRlZhSmUxN0pUK0dMa2hYcjRKeXRNV1VXVmVtaFpKMTZGbGtWSTJKaTBMUXRFdkdMUWpvWEUwS2V2UncvQW83SnhpaE1VTFlvalhvVVQvQXNiL2dXUHJzVUppeHZRb2V2UXNmNEpqOEU0MkxDejdTUHRvV0phSmpvU0VMWkloQ1FraFNLV1NoU0pDUWsyS2Z3UW12Z1gwU0ZMRkd4WXlZUW9TRkFwUXBGSXBGSXBRcFFwRklwRWhDa21Qb2laRkFzWk9NVUlVaWtVTVNFaEltZGlRa1RERk9pWkVoTFlzWW9XaFN2Z1NKbGs0eFkwVENGQXAyVGpiRmlGQ1NGSW9vV0toWW1MR0tFaFNraElTRW1LU1ZvU0VoU3lZSmpRcEZJbHYwVGlaTUpDVEVoSVNFaEk0aWtTRklvRkFvRklvSnhrd2hTaEl3eHVpb1hCNlJHUGt5c0NoTFJNampUNk9IRXhZNnQ2UkVhMm1LR0xFeFlXTERvV0w4Q3hDeEN4Q3hvV0pFd1lQK09xKzdmV3ZneWVPOEZjYUp4Y3ZSK254L1o5ZkF2RmFXMlJpVXZhT0pFYW9uRE5wTm42TmZESXg3eUxZdkd4NXNqMzBZL0ZqQytVbCtIenQxTDBZOFhGMUZmQXNWR0x3cjN1L1I1R0J3a29ST0x5SDdST0RNeGVONUh2WitselAwejlEMS9VeGVBdmxzbnc4U1d2WmpYR0VqSkNxK3g0Vi9hYTNPamczR2hZbG80SVdOa0pjRUpDampTWmk2eXY4QXd2OEF5eCtoSENlVFlvMnR0RWpsYk9BbzBoSVVuQW1ObkRTSVhRNTc5Q2s0djBjZjVHbm9Vcm5wam45MnZnbGJnaUZVbHczMFErTnZiK0RmWDA1Sk1VRWlhRTBiMkpwL0FscERpbXRhWjl2VWs0cFNPUDdSY2ZrWEIrbWFKUS9lMnlVdVhJVDJ4UFNhTUpsbW5MMFlzYWM5K3hLay93QUV2YU5VaUxYcENleEplamo3RW1rZEkvQ0ZVcEcxU2FSTGFYb1ZmZzRyYWJPTTZla1NrKzJpdTI5RlJUWDdmWnh5TlA4QTlDVDNwK3hSdzNwa2FuMmU2TWlibDhYbzdjZXlFOUpiSEx2YTJ6V2wwanJScUY3WnFWM3RpWEpNNGt6VGV5VjBka3Q2ZXgwMThrNkUzclNZN3B2V3hLdmhFTnRkb2kzTyt5ZSsyVmFPVzMwSlY3TlBXdEV1K1RUWFJXU2NYN3FmUkhLbW12OEE0ejkvK2pCVXl1dlpqV0c4amxGYVNleE4vQXRlMmExNkpOdVdRS09Xeld1bVRxU0Y4bzVJNUk1YVRGZHZvVHRKdGl6Wkgya2VQa3h0djd1LzlHL0MzN296NXNjTmZhMjErVHo4bFY0TjlhUEdmL1RQK0VYenhaT0taaWJwYlpHSzJ1VXNjNVgvQUdpaTllaUkzdE5Hc1NYb3gzOGFKVGFKZnZaR1ZTdXV0RTAzMm14ZU5PVysyWlBHNFFtbXh4YTN0aWpQUDliWCt0ajNyb243djIrVFJ6ZWlMWGFIYTJTNWZTTGFYUmVPY3NPSDhtTktWeFh3Zi8vRUFETVJBQUlCQkFBRkF3SUVCQWNBQUFBQUFBQUJBZ01SRWlFRUV6RkJVUkFpWVJReUJYR1JvU0JTZ2JFakpETkN3ZEhoLzlvQUNBRURBUUUvQUN6TE1zS0xMRmpFcy80TGlaWXhNVVlHTmh4TE5GaFJNZEZoS3drV1Fpd2tpeGlZZFJ4RWtXR2hMK0Jvc09DTVdXTVN4YXdvTVVTeXQ2UG9XTGVxUTRqVFIxTVJVcHkyaDBhaVhRVWJqZzBXTVRwNk5HS01iaXB3N001Y2ZKR0tGRVVSUmR0R014d3FOYlJ5V2ZUelBwNmcrR1o5TUxoVTBmVHhnUXBxdzZNYkV1RzhNZkN5OG4wYjhrZUZUN24wa1VQaHJDNFQ1UHBvSytoVU93cU1reU1KZEJSTVBCaVlNVVN5UmlqQldIQmVSSmVSSmQyWXg4bUtTMC9WUnYzRXZrU1ZodVhrYzVkckdWWGRraVhOWXFJcWJGVE1FS0JnWWFGQWEzMEx4N290RXdIU3VqbEk1T2psTTVNakJpZ09EUloyTVpEVXh3YVJhVml6TEZtTFJvVFM3bDlEYlBjV2xaM1BjdTRuSy9VelNSbDNSZHBkUnpYY3lYa1RYWm1TdjFITTVqTXhTMXBqcU1kV2FmWVN1WWlSWXhNREczY3N4dElidUkxNk54RzRqZnlhTkRZNUpGeHN5OUhwQ2JFMzZJdVpzYzVNdXhHU00wU2todjBmb3h5OERabVptUmtaR2FMb3lYWXpISm1Sa3k1bWh5WmV4a3pJeU0wT3FPc09yY2N5N0xtU1E1am1aanFKR2NUTkhNTXk1Y3Y1TWtPYUhVUTVvemJNeHpISXViZm8ybHNkUWNoeXNPZGh6SFVOaUxvelNPYWMwZFVkUXpiTWh6U0hObVRMbHliYWlRbTFOYkoxYkpzcGNUemIzSlRTaTJtVTYwcEozSTFWTzVVclFweHU5SWRTNnZGaXFJNTV6eDFtYzNYVTVwekRtTTVyT1l5N1pXNCtNTHFIVW9WbzE0WlJISlI2bjFGVG5kZFhIeGNYZElsWGJqWVZSSms2aXhZNjg2ZDBoY2ZLMW1qbXVOS1ZuMlBxcXRDanJ5Vk9McVZvNHk2RkhqdVhTeGtyMktsWEtNYWtkWE1rVk9MZ2xhSncxV003dWJIUGhsZmJIVm9MdU92d3ExWVhFVVBBdU1TLzJvZkcrSWk0cVVwYjBUZDV0L0pUbktGTFQ3bFBpWkplN1luYWQva3phbGM1ejJLbytxT2FyRlNXMzZScVpRYStDdC9vcjh5TWVwYlJuTEJMc2hWTzF6dVJsaWptYTBaTXpNdmt6UnpMTFFwdHM3aSszcUpwSnRzeUZMSk1VbGRqazFTY3ZsZjhpbDdma2sza1RuS0x1bVVwcGU0cUxPbGRlUmF2NlJoT2EwS2Z6NnE3OU54N21SR1VQNWwrL3dEMEthNWxoMVpOc1UyMjdscE5YUktGU0t1MEpON1NIcE1YMnRKWGVpVG1vT0RYZS84QWMxR1B5V1pXWGtvU2pHU3lXaXBWOTd0MEZnNHRkMnlTeGJzUmFaYXdteStyMk1rZGZUb1lOa0lOUzJ5Y0lMN1pYTWJKdHNoS1VrNHJvTzZXTGZZVXBwV1QwN2k5aUlWNDlKYmkrcEtkTHNKNkpPNVYyUmYrR1VjY2xrcmtjRTNkRG5HN2RpRldOSjNhWDl4TVFxai9BSkYrNHF1N1lMOXg2WkVreDlXSjJPcFNvenFkdEgrWlMwbVRoSnR1YS9VU3BxU2t0SE84eS9Za2xGKzE2R3FjdXFITEV1cjdZNVUyOW5OcEtGa2M2Q2Z0SnpwY3RTaTl2cXZCU3pyUGx3amRsZXJEaG0xSlh2NFYydjFFaXJHVW52cFluS3RDamsyVTJ4cE10SnUwUnhtdnVMTnV5RzlXc05rT0xuU2RvOURuVCs1T3hueE00UHVtU25lTmlNSnpUYVRzT2xWN1JZcUV1cnZiOGlVS01WZlkzRSttK1AzUlg0WGlMSjBsK3JIUjQyS2YybkQwNXVMNXZYNFB3ZWxHUDRoQjdPS1Y2OC96S1NoT2xrVlVvYVJPdENQdG1RcTByYmx2K29wMCtyWkdxNGJnejZuaWIvY1RsVW5LN1kwbkluQ3k2a3FieHZMZHh3aHRXUkhqblFvMnhUdDVLWDRqR2RWcWRPTEZ4Ym1zRnBmQlM0bWg5c0x2emwvNFJtb3QzYmEvTXI0U3F1TkwrcVlvVm10RTF4RCsyekl3cTQrOVdZNzlTbENVdmNrVXEwNk0xUHhzMVViays1Ly8yUT09IiBpZD0iYyIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiLz48L2RlZnM+PC9zdmc+", + "description": "A colorless and odorless gas resulting from incomplete combustion, particularly in motor vehicles.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'carbon', label: 'Carbon monoxide', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Carbon monoxide\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:molecule-co\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#3B911C\"},{\"from\":5,\"to\":10,\"color\":\"#7CC322\"},{\"from\":10,\"to\":25,\"color\":\"#F89E0D\"},{\"from\":25,\"to\":50,\"color\":\"#F77410\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#3B911C\"},{\"from\":5,\"to\":10,\"color\":\"#7CC322\"},{\"from\":10,\"to\":25,\"color\":\"#F89E0D\"},{\"from\":25,\"to\":50,\"color\":\"#F77410\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:Q08tdmFsdWUtY2FyZC1ob3Jpem9udGFsLWJhY2tncm91bmQucG5n:Q08tdmFsdWUtY2FyZC1ob3Jpem9udGFsLWJhY2tncm91bmQucG5n;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Carbon monoxide\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"mg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "carbon monoxide", + "CO" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_co2_card.json b/application/src/main/data/json/system/widget_types/horizontal_co2_card.json new file mode 100644 index 0000000000..83c280e62c --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_co2_card.json @@ -0,0 +1,40 @@ +{ + "fqn": "horizontal_co2_card", + "name": "Horizontal CO2 card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAllBMVEUAAADf39/f39/g4ODg4ODf39/////k5OSAwyzg4OCQy0bQ6a/v9+Wv2XvIyMjf8Mqg0mD3+/LOzs6/4ZX09PT5+flYWFiIxznY7L3b29u3t7fV1dXCwsK8vLzx8fHn5+fn9NiQkJDH5aN0dHS43Yitra2YzlOenp6CgoJmZmY5OTnU1NS33Yio1m7t7e1KSkqn1m243YlPJUzgAAAABnRSTlMAIEDfv1C6kOEmAAADiElEQVR42u3a647aMBAFYNpuJyYX554QkkAId3ZZtu//cp1xUHoVpbS0gM4nYYaMf/jIGITIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOBfenf3BuL9B+veDT985BxWSncvsd4PnhJ6AOnTYEgPYYggNwZBbg2C3BoEuTUIcmvOC7KelUR6tpShltclfaXc0P93VpDNRh/0cqu3S70tdxOiuabJhCwiS551RhcJ29hxSfhtPKK+ul6Qw4RKK9Okt7zsLkidZevNWM+tw3Z9YZBYMbvpK7evwmsF2RHLSiozotmMJEg2Wx/0fDOeb5a7y4K4SvlNrGypbL95UQGRL9WbWlwrSKZppjdrWs4lhwmynWlNu4zm/HxZEEe98Zgrl15k5WGuQvKUw5XKyQjdMByNXFNR6Pgm/rEKfW6cCuI7js9THefraWWWZRNrlx0svcuyWoKU2XZNszm3OMqFQQITJKRAybFYqYYfslpbuccZ+1zxHhEH3Js3n/Q8qRqbx/ZUELUKchrlgf3t9k76oTfpny8T2qp1PymPOIhPZuwi8Ogfg6h45Kk8JB5iZ6UC6dmOwykW0jgZxCWXnIBij66tUWwVngryZq47HKQ1b76m67XHzXTPCxIodcU0siNvsa3iU0E8IjlB3dmhPY/SOzbs00GCVU6OeWuFbhvQ9bRmNbnyfxEk5rEL4v1OkJE57Dy69MkOWrqefb84eZjDLsfehAu/2ZG4myHdM4L8KPA4k5HQTySv319I6HwSYWFWM6JW7c2XCVEs1xqpjp9axxmeimWGci8KsgiCFzLGXZiEXgsy0pTSYSQXhpQS0XQ5pbQoukYqM3/NV8qLA2Wb0+J1pyXMTeUcg/QzPKl4Fl0URPRByjIqtI64qqgqqjodV1FZ6qh+jeqUKJomdV1QEplGpGv6tZF8FwSyGJerfEGskar98k3DL1euBHnhKgj/QpDn53Gio2nUBZk+F1zztWkRVdoEGRYFB9H1VCcct0roDKEb/lC5fSXr5ZfUnSSpTjgvyDIqqmhc6iqRHYnqohonYx3xtVdeeEQSJJUdmdZLaZRVRX/OBBEmyFV+WBXTE81nXfB4H0HS9FQzob+k6X+ajOIGP3XvDYLcGgS5NQhyax4oyNMD/M1OlDzxDQMP8Ed7yjcMDD5+GFr37sP7R7qpBgAAAAAAAAAAAAAAAAAAAAAAAAAAAOAf+QwerQBC9f0WmwAAAABJRU5ErkJggg==", + "description": "Displays the latest CO2 level telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'co2', label: 'CO2 level', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"CO2 level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"co2\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3FA71A\"},{\"from\":600,\"to\":1000,\"color\":\"#80C32C\"},{\"from\":1000,\"to\":1500,\"color\":\"#F36900\"},{\"from\":1500,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3FA71A\"},{\"from\":600,\"to\":1000,\"color\":\"#80C32C\"},{\"from\":1000,\"to\":1500,\"color\":\"#F36900\"},{\"from\":1500,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal CO2 card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"ppm\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "co2", + "carbon dioxide", + "carbon emission", + "carbon footprint", + "carbon output", + "carbon pollution", + "carbon capture", + "carbon offset", + "carbon reduction", + "carbon neutral", + "climate gas", + "fossil fuel emission", + "carbon cycle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_co2_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_co2_card_with_background.json new file mode 100644 index 0000000000..d2baacdade --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_co2_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "horizontal_co2_card_with_background", + "name": "Horizontal CO2 card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest CO2 level telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'co2', label: 'CO2 level', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"CO2 level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"co2\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3B911C\"},{\"from\":600,\"to\":1000,\"color\":\"#7CC322\"},{\"from\":1000,\"to\":1500,\"color\":\"#F77410\"},{\"from\":1500,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3B911C\"},{\"from\":600,\"to\":1000,\"color\":\"#7CC322\"},{\"from\":1000,\"to\":1500,\"color\":\"#F77410\"},{\"from\":1500,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal CO2 card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"ppm\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "co2", + "carbon dioxide", + "carbon emission", + "carbon footprint", + "carbon output", + "carbon pollution", + "carbon capture", + "carbon offset", + "carbon reduction", + "carbon neutral", + "climate gas", + "fossil fuel emission", + "carbon cycle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_cylinder_tank.json b/application/src/main/data/json/system/widget_types/horizontal_cylinder_tank.json new file mode 100644 index 0000000000..02cf7724d3 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_cylinder_tank.json @@ -0,0 +1,36 @@ +{ + "fqn": "horizontal_cylinder_tank", + "name": "Horizontal cylinder tank", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Widget indicates the level of liquid in Horizontal cylinder tank.", + "descriptor": { + "type": "latest", + "sizeX": 4, + "sizeY": 4, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.liquidLevelWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.liquidLevelWidget.update();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n}\n\nself.actionSources = function() { \n return { \n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false \n } \n };\n}", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-liquid-level-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-liquid-level-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.floor(Math.random() * 101);\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"tankSelectionType\":\"static\",\"selectedShape\":\"Horizontal Cylinder\",\"shapeAttributeName\":\"tankShape\",\"tankColor\":{\"type\":\"range\",\"color\":\"#242770\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E73535DE\"},{\"from\":20,\"to\":null,\"color\":\"#242770\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E73535DE';\\n }\\n}\\nreturn '#242770';\"},\"datasourceUnits\":\"%\",\"layout\":\"percentage\",\"volumeSource\":\"static\",\"volumeConstant\":500,\"volumeAttributeName\":\"volume\",\"volumeUnits\":\"L\",\"volumeFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"volumeColor\":\"rgba(0, 0, 0, 0.18)\",\"units\":\"%\",\"widgetUnitsSource\":\"static\",\"widgetUnitsAttributeName\":\"units\",\"liquidColor\":{\"type\":\"range\",\"color\":\"#7A8BFF\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E27C7CDE\"},{\"from\":20,\"to\":null,\"color\":\"#7A8BFF\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FF0000DE\"},{\"from\":20,\"to\":null,\"color\":\"rgba(0,0,0,0.87)\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FF0000DE';\\n }\\n}\\nreturn '#000000DE';\"},\"showBackgroundOverlay\":true,\"backgroundOverlayColor\":{\"type\":\"range\",\"color\":\"#FFFFFFC2\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FFEFEFDE\"},{\"from\":20,\"to\":null,\"color\":\"#FFFFFFC2\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FFEFEFDE';\\n }\\n}\\nreturn '#FFFFFFC2';\"},\"showTooltip\":true,\"showTooltipLevel\":true,\"tooltipUnits\":\"%\",\"tooltipLevelDecimals\":0,\"tooltipLevelFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipLevelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.76)\",\"rangeList\":[],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"showTooltipDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":3,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Liquid level\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"titleFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1.5\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"showTitleIcon\":false,\"titleIcon\":\"water_drop\",\"iconColor\":\"#5469FF\",\"decimals\":0,\"enableDataExport\":false,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" + }, + "externalId": null, + "tags": [ + "reservoir", + "container", + "vessel", + "storage unit", + "cistern", + "canister", + "vat", + "basin", + "repository", + "bin", + "hopper" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_dish_ends_tank.json b/application/src/main/data/json/system/widget_types/horizontal_dish_ends_tank.json new file mode 100644 index 0000000000..6ba607edaf --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_dish_ends_tank.json @@ -0,0 +1,36 @@ +{ + "fqn": "horizontal_dish_ends_tank", + "name": "Horizontal dish ends tank", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAABWVBMVEXg4ODf39/g4ODg4OAAAAD////7+v96i//g4OD+/v/g5P8kJ3Dj5O0/QYKRk7cgICHGxdt1dqaAkP+QkJBkctv9+v88PD39/f/j4+OsrKyHlv92eKV0dHRYWFjs7v8vNILx8fEuNIIxNHnz9P+QkLfk5//IydvV1dVoap1bXZTIyMitrsmEhq66u9PHx8efocBaXJSrq8lZWVmDg67v8PHh4O2cnJ0vLy/k6P/y8vaCgoKbp/+Tof+6urplZmYuLi7R0uR1dqWenp5NT4syNXn08//x8fbW1+SdncBLS0vN0f+stv+Ckv/v7fZKSkrU1/+zu//c3v+7w/+Uov+Lmf/m5vXX2fCvs8iEhq+io6xMT4tNUIpubnRQUVlGRko2Nz3Dyf+jrv/c3eNpdtulqLqXmqx4eYI3OoKUof/e3/bEx9W5useOkJ2Bgo9OUIt3eYKBgYFdX2YqKy4q4f5pAAAABXRSTlPvIL+vAC9A4IoAAAX9SURBVHja7NZBi4MwEIZhu9sPesshMJ1AhNwCHjxIURBU8ObF37L//7RjXbBbCr2Vsc2LmgRBfFDB7Hj4yk577/twzA7Z5Yyddw7C+A54gy7yWu3+eSyds+yEt+iUIMpKEG0liLY+FlLwNjyIC7y4xxBXrcMUcdfPtI7GbsODrMHr2yD39xE94y437hHCLgBlXcTuOg2OUXYAuppvIMHVLIcocLeu1EGcKWRKNBBKmbJx8ATkZmo2CFOTmxp5hWWPQ0NN1AihHphvITIdwRvEN4y2CdaE5TxNi0chhM0I5P8g1gRgg8xkbW84GOeWY27tTCoh7gmEyEsBue8rsKlk0SqEhMH/QTrEFeIW0wbpG3HJVjeDmIdKFqwIUklx/dh9bwhhoJZWSCDy8waJA9l8AoIxwhlN35KeJ1LaJY6WAedtTgB739mIbgS49UVdruLyurRCQO0gda0flwvg9T3/RRHIbvoMSIzYTR/7G6+2BNFWgmgrQbSVINpKEG0liLYS5Jc9eld1GAaiKFp5mtEIpVYnjJCQhByMC9c2Tpr0l+T//+OaNHlUbmaIwbs4/eL8Wgfk1xKAqGdL89WuIF4HQnRE6+IjpOatXUEcTrqoZm1RyTrUzVt7glhEInzmyOFcmld7giwTPkLWSetatY17fuTPrwIbY7BWJ4WqebUvyOcFB+SAHJAD8tEiBclWb2v6gmS9KZtlIBXDaVsBgy7+opTyJeUHnbbVYZWAKHQGNjZc++5O5IjuoTc32JbBWQlAqCUDrBlqIz8kEfBDgCo3RFGRgJh5ZIboE0hAoNO8EEWDDGS9hBWSOpCBQKdZIWSkIGbmhBQCKQjcPSMknOUgbeSDKBzkIDcc2SCpAzkIdJUNEs+SkGvkgiw4SEKGeWSC+AkkIUCeCZJ7WUifmSCxyEJKZILgIAu54cgC8QSyEKALC6QGaUioLJDcSkP6zAKJRRpyjiwQZ7ZB/tu725+0gTiA4/hwxUqPM6AtDIquQIWAA0ZxOJwrcwwGbOCzJj4/7nnZ/v8Xu1LjXrEdxt9ZSL+E43rvPmlpQqFhjiE2iIlBIL4PTBCRJYUJQk9bAJC6JrBA5kSmmCCCJgFA9HX+EKwDQJby/CH5DAAkl+IPqeYAIP7qI0D8EJDEoJCQYY3GonFvSCILAIkMCgkTlY5BQkj03pCIAyAhmaiW5mUoSILDDInK8nM6kpAoyu/vCWmAQAoDQZ6RsEz3iPpCtAerxdCAkMJjQhRFFOlTjoo9iGxBSG8t+uaj+IpOrMegEP6HlqD0CsohRVYVRZXpBoVYrRlqKKjYOf89YndO1HD4tRw2VJluqUSwWlxbVg3BbgggcasDYnd+SHbi8ePjuN1y8PDAnj0ixF9sBuzmLwL9q8TuOjmKxQ7IdewrObQXzOufX36ZvWkl0L+L+dtJs+iHgFQ7Xruns97+bSWTyXKZPpPlkyM6HJHf5MSk23T90vz+7Xi3bM23vP2bnb+dbFQhIJnUBhNk+q69pd54dbX7d+1yz35dZYKkMhCQ9RgL5NM0Q4yQZH4JAFLHNRbI6gNCaiAfrCSf0OYLaQs+CeTiQ2mbAbL9cJDVkg9BQLB5xgDxtlgcLS8D5GzmBwgkVeiyQLyBJ/8t4GWBdBtpEEi22GSAsMUE6QBdMs3kk3wh9OwLApE0oc0VImh1EAjSShWekIr5Fuqrt0aXJ6RbSANBctUOT0gnlQOC6FqNJ6SG60AQpM1U+EEqMxhBQbKJU36QZjECBtFxjR8khnUgiH1s8YHYRxYcJFs85QVpViOAEF3bafOBbO9gCRCC0o1NPpBWIY0gITqu8YHEcAYUgtYLLR6QVgEjWIiOP7fhIdYOAYagdGITHrKZwAgaomu7N9CQm5ImgUPQfh5DQ3BqH8FD3mEf9K+DfHiFAwRJmg84TUI8IGhFAm7FaTdU/quhujO0fy7EhQxLLsRpuRCn5UKclgtxWi7EabkQp+VCnJYLcVouxGm5EKflQpzWCEE8o/EHwVOecQONQAsTnjHPwtDvE8WgjMmxialhzzM+NvkHPLFdf1u4AJwAAAAASUVORK5CYII=", + "description": "Widget indicates the level of liquid in Horizontal dish ends tank.", + "descriptor": { + "type": "latest", + "sizeX": 4, + "sizeY": 4, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.liquidLevelWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.liquidLevelWidget.update();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n}\n\nself.actionSources = function() { \n return { \n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false \n } \n };\n}", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-liquid-level-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-liquid-level-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.floor(Math.random() * 101);\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"tankSelectionType\":\"static\",\"selectedShape\":\"Horizontal Dish Ends\",\"shapeAttributeName\":\"tankShape\",\"tankColor\":{\"type\":\"range\",\"color\":\"#242770\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E73535DE\"},{\"from\":20,\"to\":null,\"color\":\"#242770\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E73535DE';\\n }\\n}\\nreturn '#242770';\"},\"datasourceUnits\":\"%\",\"layout\":\"percentage\",\"volumeSource\":\"static\",\"volumeConstant\":500,\"volumeAttributeName\":\"volume\",\"volumeUnits\":\"L\",\"volumeFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"volumeColor\":\"rgba(0, 0, 0, 0.18)\",\"units\":\"%\",\"widgetUnitsSource\":\"static\",\"widgetUnitsAttributeName\":\"units\",\"liquidColor\":{\"type\":\"range\",\"color\":\"#7A8BFF\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E27C7CDE\"},{\"from\":20,\"to\":null,\"color\":\"#7A8BFF\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FF0000DE\"},{\"from\":20,\"to\":null,\"color\":\"rgba(0,0,0,0.87)\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FF0000DE';\\n }\\n}\\nreturn '#000000DE';\"},\"showBackgroundOverlay\":true,\"backgroundOverlayColor\":{\"type\":\"range\",\"color\":\"rgba(255, 255, 255, 0.76)\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FFEFEFDE\"},{\"from\":20,\"to\":null,\"color\":\"#FFFFFFC2\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FFEFEFDE';\\n }\\n}\\nreturn '#FFFFFFC2';\"},\"showTooltip\":true,\"showTooltipLevel\":true,\"tooltipUnits\":\"%\",\"tooltipLevelDecimals\":0,\"tooltipLevelFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipLevelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.76)\",\"rangeList\":[],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"showTooltipDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":3,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Liquid level\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"titleFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1.5\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"showTitleIcon\":false,\"titleIcon\":\"water_drop\",\"iconColor\":\"#5469FF\",\"decimals\":0,\"enableDataExport\":false,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" + }, + "externalId": null, + "tags": [ + "reservoir", + "container", + "vessel", + "storage unit", + "cistern", + "canister", + "vat", + "basin", + "repository", + "bin", + "hopper" + ] +} diff --git a/application/src/main/data/json/system/widget_types/horizontal_doughnut.json b/application/src/main/data/json/system/widget_types/horizontal_doughnut.json new file mode 100644 index 0000000000..f76bf50e48 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_doughnut.json @@ -0,0 +1,29 @@ +{ + "fqn": "horizontal_doughnut", + "name": "Horizontal doughnut", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest values of the attributes or time-series data in a doughnut chart using horizontal layout. Supports numeric values only.", + "descriptor": { + "type": "latest", + "sizeX": 4, + "sizeY": 2.5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.doughnutWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.doughnutWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n previewWidth: '380px',\n previewHeight: '220px',\n embedTitlePanel: true,\n horizontal: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'windPower', label: 'Wind power', type: 'timeseries', color: '#08872B' },\n { name: 'solarPower', label: 'Solar power', type: 'timeseries', color: '#FF4D5A' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-doughnut-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-doughnut-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind power\",\"color\":\"#08872B\",\"settings\":{},\"_hash\":0.7227918773301678,\"funcBody\":\"if (prevValue === 0) {\\n prevValue = Math.random() * 200;\\n}\\nvar value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Solar power\",\"color\":\"#FF4D5A\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"if (prevValue === 0) {\\n prevValue = Math.random() * 200;\\n}\\nvar value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"totalValueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1\"},\"totalValueColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showLegend\":true,\"legendPosition\":\"right\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.38)\",\"legendValueFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"20px\"},\"legendValueColor\":\"rgba(0, 0, 0, 0.87)\",\"showTooltip\":true,\"tooltipValueType\":\"percentage\",\"tooltipValueDecimals\":0,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Doughnut\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"headerButton\":[]},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"donut_large\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "ring", + "circle", + "pie chart", + "donut" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_efficiency_card.json b/application/src/main/data/json/system/widget_types/horizontal_efficiency_card.json new file mode 100644 index 0000000000..ef311b2828 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_efficiency_card.json @@ -0,0 +1,29 @@ +{ + "fqn": "horizontal_efficiency_card", + "name": "Horizontal efficiency card", + "deprecated": false, + "image": "tb-image:aG9yaXpvbnRhbF9lZmZpY2llbmN5X2NhcmQuc3Zn:aG9yaXpvbnRhbF9lZmZpY2llbmN5X2NhcmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjU2IiB5PSI1MiIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjU1IiB4PSIuNSIgeT0iNTIuNSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjEiIHJ4PSIzLjUiLz48ZyBmaWx0ZXI9InVybCgjYSkiPjxwYXRoIGZpbGw9IiMzRkE3MUEiIGQ9Im0yNiA3NCAyLjMgMi4zLTQuOSA0LjktNC00LTcuNCA3LjQgMS40IDEuNCA2LTYgNCA0IDYuMy02LjNMMzIgODB2LTZoLTZaIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZD0iTTQ1LjUgODMuNlY4NWgtNS4zdi0xLjRoNS4zWk00MC43IDc1djEwSDM5Vjc1aDEuN1ptNC4xIDQuMnYxLjNoLTQuNnYtMS4zaDQuNlptLjctNC4ydjEuNGgtNS4zVjc1aDUuM1ptMy43IDEwaC0xLjZ2LTguMWMwLS42LjEtMSAuMy0xLjQuMi0uNC41LS43LjktLjhhMy40IDMuNCAwIDAgMSAyLjMtLjJ2MS4zbC0uMy0uMWE0IDQgMCAwIDAtLjQgMGwtLjYuMWExIDEgMCAwIDAtLjQuNGwtLjIuN1Y4NVptMS42LTcuNHYxLjJoLTQuM3YtMS4yaDQuM1ptMi43IDcuNGgtMS43di03LjljMC0uNi4yLTEgLjQtMS41LjItLjQuNi0uNyAxLTFsMS42LS4yaDFsMS4xLjQtLjIgMS4zLS44LS4yLTEtLjFjLS41IDAtLjggMC0xIC4zLS4zLjItLjQuNi0uNCAxVjg1Wm0xLjUtNy40djEuMmgtNC4zdi0xLjJINTVabTIuNyAwVjg1aC0xLjZ2LTcuNGgxLjZabTUuMSA2LjIuNy0uMWMuMy0uMS40LS4zLjYtLjVsLjItLjZoMS41YzAgLjQtLjEuOS0uNCAxLjNhMyAzIDAgMCAxLTEgLjljLS41LjItMSAuMy0xLjYuMy0uNSAwLTEgMC0xLjQtLjNhMyAzIDAgMCAxLTEtLjhjLS40LS4zLS42LS43LS43LTEuMS0uMi0uNS0uMi0xLS4yLTEuNXYtLjJjMC0uNSAwLTEgLjItMS41LjEtLjQuMy0uOC42LTEuMS4zLS40LjYtLjYgMS0uOGwxLjUtLjNjLjYgMCAxLjEgMCAxLjYuMy40LjMuOC42IDEgMSAuMy40LjQuOS40IDEuNGgtMS41bC0uMi0uNy0uNS0uNWMtLjItLjItLjUtLjItLjgtLjItLjMgMC0uNiAwLS44LjJsLS41LjVjLS4yLjMtLjMuNS0uMy44djIuMWMwIC4yLjEuNS4zLjcgMCAuMi4zLjQuNS41LjIuMi41LjIuOC4yWm02LjMtNi4yVjg1aC0xLjZ2LTcuNEg2OVptLTEuNy0yYzAtLjIgMC0uNC4yLS42bC43LS4yYy4zIDAgLjUgMCAuNy4yLjIuMi4yLjQuMi43IDAgLjIgMCAuNC0uMi42YTEgMSAwIDAgMS0uNy4yIDEgMSAwIDAgMS0uNy0uMi44LjggMCAwIDEtLjItLjZabTcgOS41Yy0uNSAwLTEgMC0xLjUtLjJhMy4zIDMuMyAwIDAgMS0xLjgtMiA0IDQgMCAwIDEtLjItMS4zdi0uM2MwLS42IDAtMS4xLjItMS42cy40LS45LjctMS4yYTMgMyAwIDAgMSAxLjEtLjhsMS4zLS4yYy42IDAgMSAwIDEuNC4ybDEgLjhjLjMuMy41LjcuNiAxLjFsLjIgMS41di43aC01Ljd2LTEuMmg0di0uMWwtLjEtLjktLjUtLjYtLjktLjJjLS4yIDAtLjUgMC0uNy4yLS4yIDAtLjQuMi0uNS41bC0uNC43djIuM2wuNS43LjYuNS44LjFhMi4zIDIuMyAwIDAgMCAxLjktLjlsLjkuOS0uNy42Yy0uMi4yLS41LjQtLjkuNS0uNC4yLS44LjItMS4zLjJabTYuMi02Vjg1aC0xLjd2LTcuNGgxLjZ2MS42Wm0tLjMgMmgtLjZjMC0uNi4xLTEgLjMtMS41LjEtLjUuMy0uOC42LTEuMWEyLjcgMi43IDAgMCAxIDItMWwxIC4xLjguNS41LjguMSAxLjNWODVoLTEuNnYtNC44YzAtLjMgMC0uNi0uMi0uOGEuOS45IDAgMCAwLS40LS41bC0uNy0uMWExLjYgMS42IDAgMCAwLTEuNC43bC0uMy43LS4xLjhabTkuNyAyLjcuNy0uMS41LS41LjMtLjZIOTNjMCAuNC0uMS45LS40IDEuM2EzIDMgMCAwIDEtMSAuOWMtLjUuMi0xIC4zLTEuNi4zLS42IDAtMSAwLTEuNS0uM2EzIDMgMCAwIDEtMS0uOGMtLjMtLjMtLjUtLjctLjYtMS4xLS4yLS41LS4yLTEtLjItMS41di0uMmMwLS41IDAtMSAuMi0xLjUuMS0uNC4zLS44LjYtMS4xLjMtLjQuNi0uNiAxLS44bDEuNS0uM2MuNiAwIDEuMSAwIDEuNi4zLjQuMy44LjYgMSAxIC4zLjQuNC45LjQgMS40aC0xLjVsLS4yLS43LS41LS41Yy0uMy0uMi0uNS0uMi0uOC0uMi0uMyAwLS42IDAtLjguMi0uMy4xLS40LjMtLjYuNWwtLjIuOC0uMS45djEuMmwuMy43Yy4yLjIuMy40LjYuNS4yLjIuNS4yLjguMlptNi43LjQgMi02LjZoMS44bC0zIDguNS0uMy42YzAgLjItLjIuNC0uNC42YTIgMiAwIDAgMS0uNy41bC0uOS4yaC0uNGwtLjQtLjF2LTEuM2E2LjMgNi4zIDAgMCAwIC4zIDBoLjdsLjQtLjRjLjItLjEuMy0uMy4zLS42bC42LTEuNFptLTEuMi02LjYgMS44IDUuNS4zIDEuOC0xLjEuMy0yLjctNy42aDEuN1oiLz48cGF0aCBmaWxsPSIjM0ZBNzFBIiBkPSJNMTUzLjUgNzIuOFY3NEwxNDcuOCA4N2gtMi40bDUuNi0xMi4zaC03LjN2LTJoOS44Wm0xMS4zIDUuOVY4MWMwIDEuMS0uMSAyLS4zIDIuOWE1IDUgMCAwIDEtMSAxLjljLS40LjUtLjkuOC0xLjQgMWE1LjUgNS41IDAgMCAxLTMuNS4xIDMuOCAzLjggMCAwIDEtMi4zLTEuOGMtLjItLjUtLjUtMS0uNi0xLjdsLS4yLTIuNHYtMi4zYzAtMS4xIDAtMiAuMy0yLjhhNSA1IDAgMCAxIDEtMmMuNC0uNC45LS44IDEuNC0xIC42LS4yIDEuMi0uMyAyLS4zLjUgMCAxIDAgMS41LjJhMy43IDMuNyAwIDAgMSAyLjMgMS44Yy4yLjUuNSAxIC42IDEuOGwuMiAyLjNabS0yLjMgMi43di0zbC0uMS0xLjUtLjMtMS4yLS41LS43LS42LS40LS45LS4xYy0uMyAwLS42IDAtMSAuMmwtLjcuNi0uNCAxLjItLjIgMS44djNsLjEgMS42LjMgMSAuNS45LjcuNC44LjFjLjMgMCAuNyAwIDEtLjJsLjctLjcuNC0xLjIuMi0xLjhabTkuNS01LjF2LS44bC4zLTEuNWMuMy0uNC42LS44IDEtMSAuNS0uMyAxLS40IDEuNy0uNC42IDAgMS4yLjEgMS42LjQuNS4yLjguNiAxIDEgLjMuNS40IDEgLjQgMS41di44YzAgLjUtLjEgMS0uMyAxLjQtLjMuNS0uNi44LTEgMS0uNS40LTEgLjUtMS43LjUtLjYgMC0xLjItLjEtMS42LS40LS41LS4zLS44LS42LTEtMWEzIDMgMCAwIDEtLjQtMS41Wm0xLjYtLjh2LjhjMCAuMiAwIC41LjIuNyAwIC4yLjIuNC40LjZsLjguMmMuMyAwIC42IDAgLjgtLjJsLjQtLjYuMi0uN3YtLjhjMC0uMyAwLS41LS4yLS43IDAtLjMtLjItLjUtLjQtLjYtLjItLjItLjUtLjItLjgtLjItLjMgMC0uNiAwLS44LjItLjIuMS0uMy4zLS40LjZhMiAyIDAgMCAwLS4yLjdabTUuMiA4Ljh2LS44YzAtLjUuMi0xIC40LTEuNS4yLS40LjYtLjcgMS0xIC41LS4zIDEtLjQgMS43LS40LjYgMCAxLjIuMSAxLjYuNC40LjMuOC42IDEgMSAuMi41LjQgMSAuNCAxLjV2LjhjMCAuNS0uMiAxLS40IDEuNC0uMi41LS42LjgtMSAxLjFhMyAzIDAgMCAxLTEuNi40Yy0uNyAwLTEuMi0uMS0xLjctLjQtLjQtLjMtLjgtLjYtMS0xYTMgMyAwIDAgMS0uNC0xLjVabTEuNy0uOHYuOGwuMS43LjUuNi44LjJjLjMgMCAuNiAwIC44LS4ybC40LS42LjEtLjd2LS44bC0uMS0uN2ExLjMgMS4zIDAgMCAwLTEuMi0uOGMtLjQgMC0uNiAwLS44LjItLjIuMS0uNC4zLS41LjZhMiAyIDAgMCAwLS4xLjdabTItOC43LTcgMTEuMS0xLjItLjcgNy0xMSAxLjEuNloiLz48L2c+PGRlZnM+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjIwMCIgaGVpZ2h0PSI1NiIgeD0iMCIgeT0iNTIiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjMiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTUxOF85MzMyIi8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xNTE4XzkzMzIiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48L2RlZnM+PC9zdmc+", + "description": "Displays the latest efficiency telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'efficiency', label: 'Efficiency', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Efficiency\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 30;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"horizontal\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"trending_up\",\"iconColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":60,\"color\":\"#FFA600\"},{\"from\":60,\"to\":80,\"color\":\"#3FA71A\"},{\"from\":80,\"to\":null,\"color\":\"#305AD7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"36px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":60,\"color\":\"#FFA600\"},{\"from\":60,\"to\":80,\"color\":\"#3FA71A\"},{\"from\":80,\"to\":null,\"color\":\"#305AD7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Horizontal efficiency card\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"%\",\"decimals\":0,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "productivity", + "effectiveness", + "performance", + "capability" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_efficiency_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_efficiency_card_with_background.json new file mode 100644 index 0000000000..032bb4ca50 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_efficiency_card_with_background.json @@ -0,0 +1,29 @@ +{ + "fqn": "horizontal_efficiency_card_with_background", + "name": "Horizontal efficiency card with background", + "deprecated": false, + "image": "tb-image:aG9yaXpvbnRhbF9lZmZpY2llbmN5X2NhcmRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==:aG9yaXpvbnRhbF9lZmZpY2llbmN5X2NhcmRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSI1NiIgeT0iNTIiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjQ0IiB4PSI2IiB5PSI1OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48cGF0aCBmaWxsPSIjM0I5MTFDIiBkPSJtMjYgNzQgMi4zIDIuMy00LjkgNC45LTQtNC03LjQgNy40IDEuNCAxLjQgNi02IDQgNCA2LjMtNi4zTDMyIDgwdi02aC02WiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik00NS41IDgzLjZWODVoLTUuM3YtMS40aDUuM1pNNDAuNyA3NXYxMEgzOVY3NWgxLjdabTQuMSA0LjJ2MS4zaC00LjZ2LTEuM2g0LjZabS43LTQuMnYxLjRoLTUuM1Y3NWg1LjNabTMuNyAxMGgtMS42di04LjFjMC0uNi4xLTEgLjMtMS40LjItLjQuNS0uNy45LS44YTMuNCAzLjQgMCAwIDEgMi4zLS4ydjEuM2wtLjMtLjFhNCA0IDAgMCAwLS40IDBsLS42LjFhMSAxIDAgMCAwLS40LjRsLS4yLjdWODVabTEuNi03LjR2MS4yaC00LjN2LTEuMmg0LjNabTIuNyA3LjRoLTEuN3YtNy45YzAtLjYuMi0xIC40LTEuNS4yLS40LjYtLjcgMS0xbDEuNi0uMmgxbDEuMS40LS4yIDEuMy0uOC0uMi0xLS4xYy0uNSAwLS44IDAtMSAuMy0uMy4yLS40LjYtLjQgMVY4NVptMS41LTcuNHYxLjJoLTQuM3YtMS4ySDU1Wm0yLjcgMFY4NWgtMS42di03LjRoMS42Wm01LjEgNi4yLjctLjFjLjMtLjEuNC0uMy42LS41bC4yLS42aDEuNWMwIC40LS4xLjktLjQgMS4zYTMgMyAwIDAgMS0xIC45Yy0uNS4yLTEgLjMtMS42LjMtLjUgMC0xIDAtMS40LS4zYTMgMyAwIDAgMS0xLS44Yy0uNC0uMy0uNi0uNy0uNy0xLjEtLjItLjUtLjItMS0uMi0xLjV2LS4yYzAtLjUgMC0xIC4yLTEuNS4xLS40LjMtLjguNi0xLjEuMy0uNC42LS42IDEtLjhsMS41LS4zYy42IDAgMS4xIDAgMS42LjMuNC4zLjguNiAxIDEgLjMuNC40LjkuNCAxLjRoLTEuNWwtLjItLjctLjUtLjVjLS4yLS4yLS41LS4yLS44LS4yLS4zIDAtLjYgMC0uOC4ybC0uNS41Yy0uMi4zLS4zLjUtLjMuOHYyLjFjMCAuMi4xLjUuMy43IDAgLjIuMy40LjUuNS4yLjIuNS4yLjguMlptNi4zLTYuMlY4NWgtMS42di03LjRINjlabS0xLjctMmMwLS4yIDAtLjQuMi0uNmwuNy0uMmMuMyAwIC41IDAgLjcuMi4yLjIuMi40LjIuNyAwIC4yIDAgLjQtLjIuNmExIDEgMCAwIDEtLjcuMiAxIDEgMCAwIDEtLjctLjIuOC44IDAgMCAxLS4yLS42Wm03IDkuNWMtLjUgMC0xIDAtMS41LS4yYTMuMyAzLjMgMCAwIDEtMS44LTIgNCA0IDAgMCAxLS4yLTEuM3YtLjNjMC0uNiAwLTEuMS4yLTEuNnMuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS4xLS44bDEuMy0uMmMuNiAwIDEgMCAxLjQuMmwxIC44Yy4zLjMuNS43LjYgMS4xbC4yIDEuNXYuN2gtNS43di0xLjJoNHYtLjFsLS4xLS45LS41LS42LS45LS4yYy0uMiAwLS41IDAtLjcuMi0uMiAwLS40LjItLjUuNWwtLjQuN3YyLjNsLjUuNy42LjUuOC4xYTIuMyAyLjMgMCAwIDAgMS45LS45bC45LjktLjcuNmMtLjIuMi0uNS40LS45LjUtLjQuMi0uOC4yLTEuMy4yWm02LjItNlY4NWgtMS43di03LjRoMS42djEuNlptLS4zIDJoLS42YzAtLjYuMS0xIC4zLTEuNS4xLS41LjMtLjguNi0xLjFhMi43IDIuNyAwIDAgMSAyLTFsMSAuMS44LjUuNS44LjEgMS4zVjg1aC0xLjZ2LTQuOGMwLS4zIDAtLjYtLjItLjhhLjkuOSAwIDAgMC0uNC0uNWwtLjctLjFhMS42IDEuNiAwIDAgMC0xLjQuN2wtLjMuNy0uMS44Wm05LjcgMi43LjctLjEuNS0uNS4zLS42SDkzYzAgLjQtLjEuOS0uNCAxLjNhMyAzIDAgMCAxLTEgLjljLS41LjItMSAuMy0xLjYuMy0uNiAwLTEgMC0xLjUtLjNhMyAzIDAgMCAxLTEtLjhjLS4zLS4zLS41LS43LS42LTEuMS0uMi0uNS0uMi0xLS4yLTEuNXYtLjJjMC0uNSAwLTEgLjItMS41LjEtLjQuMy0uOC42LTEuMS4zLS40LjYtLjYgMS0uOGwxLjUtLjNjLjYgMCAxLjEgMCAxLjYuMy40LjMuOC42IDEgMSAuMy40LjQuOS40IDEuNGgtMS41bC0uMi0uNy0uNS0uNWMtLjMtLjItLjUtLjItLjgtLjItLjMgMC0uNiAwLS44LjItLjMuMS0uNC4zLS42LjVsLS4yLjgtLjEuOXYxLjJsLjMuN2MuMi4yLjMuNC42LjUuMi4yLjUuMi44LjJabTYuNy40IDItNi42aDEuOGwtMyA4LjUtLjMuNmMwIC4yLS4yLjQtLjQuNmEyIDIgMCAwIDEtLjcuNWwtLjkuMmgtLjRsLS40LS4xdi0xLjNhNi4zIDYuMyAwIDAgMCAuMyAwaC43bC40LS40Yy4yLS4xLjMtLjMuMy0uNmwuNi0xLjRabS0xLjItNi42IDEuOCA1LjUuMyAxLjgtMS4xLjMtMi43LTcuNmgxLjdaIi8+PHBhdGggZmlsbD0iIzNCOTExQyIgZD0iTTE1My41IDcyLjhWNzRMMTQ3LjggODdoLTIuNGw1LjYtMTIuM2gtNy4zdi0yaDkuOFptMTEuMyA1LjlWODFjMCAxLjEtLjEgMi0uMyAyLjlhNSA1IDAgMCAxLTEgMS45Yy0uNC41LS45LjgtMS40IDFhNS41IDUuNSAwIDAgMS0zLjUuMSAzLjggMy44IDAgMCAxLTIuMy0xLjhjLS4yLS41LS41LTEtLjYtMS43bC0uMi0yLjR2LTIuM2MwLTEuMSAwLTIgLjMtMi44YTUgNSAwIDAgMSAxLTJjLjQtLjQuOS0uOCAxLjQtMSAuNi0uMiAxLjItLjMgMi0uMy41IDAgMSAwIDEuNS4yYTMuNyAzLjcgMCAwIDEgMi4zIDEuOGMuMi41LjUgMSAuNiAxLjhsLjIgMi4zWm0tMi4zIDIuN3YtM2wtLjEtMS41LS4zLTEuMi0uNS0uNy0uNi0uNC0uOS0uMWMtLjMgMC0uNiAwLTEgLjJsLS43LjYtLjQgMS4yLS4yIDEuOHYzbC4xIDEuNi4zIDEgLjUuOS43LjQuOC4xYy4zIDAgLjcgMCAxLS4ybC43LS43LjQtMS4yLjItMS44Wm05LjUtNS4xdi0uOGwuMy0xLjVjLjMtLjQuNi0uOCAxLTEgLjUtLjMgMS0uNCAxLjctLjQuNiAwIDEuMi4xIDEuNi40LjUuMi44LjYgMSAxIC4zLjUuNCAxIC40IDEuNXYuOGMwIC41LS4xIDEtLjMgMS40LS4zLjUtLjYuOC0xIDEtLjUuNC0xIC41LTEuNy41LS42IDAtMS4yLS4xLTEuNi0uNC0uNS0uMy0uOC0uNi0xLTFhMyAzIDAgMCAxLS40LTEuNVptMS42LS44di44YzAgLjIgMCAuNS4yLjcgMCAuMi4yLjQuNC42bC44LjJjLjMgMCAuNiAwIC44LS4ybC40LS42LjItLjd2LS44YzAtLjMgMC0uNS0uMi0uNyAwLS4zLS4yLS41LS40LS42LS4yLS4yLS41LS4yLS44LS4yLS4zIDAtLjYgMC0uOC4yLS4yLjEtLjMuMy0uNC42YTIgMiAwIDAgMC0uMi43Wm01LjIgOC44di0uOGMwLS41LjItMSAuNC0xLjUuMi0uNC42LS43IDEtMSAuNS0uMyAxLS40IDEuNy0uNC42IDAgMS4yLjEgMS42LjQuNC4zLjguNiAxIDEgLjIuNS40IDEgLjQgMS41di44YzAgLjUtLjIgMS0uNCAxLjQtLjIuNS0uNi44LTEgMS4xYTMgMyAwIDAgMS0xLjYuNGMtLjcgMC0xLjItLjEtMS43LS40LS40LS4zLS44LS42LTEtMWEzIDMgMCAwIDEtLjQtMS41Wm0xLjctLjh2LjhsLjEuNy41LjYuOC4yYy4zIDAgLjYgMCAuOC0uMmwuNC0uNi4xLS43di0uOGwtLjEtLjdhMS4zIDEuMyAwIDAgMC0xLjItLjhjLS40IDAtLjYgMC0uOC4yLS4yLjEtLjQuMy0uNS42YTIgMiAwIDAgMC0uMS43Wm0yLTguNy03IDExLjEtMS4yLS43IDctMTEgMS4xLjZaIi8+PC9nPjxkZWZzPjxwYXR0ZXJuIGlkPSJhIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBwYXR0ZXJuQ29udGVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCI+PHVzZSB4bGluazpocmVmPSIjYyIgdHJhbnNmb3JtPSJtYXRyaXgoLjAwNTAyNTEzIDAgMCAuMDE3OTQ2OSAwIC0xKSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjU2IiB4PSIwIiB5PSI1MiIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMyIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMDg4XzI2MTM3Ii8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMDg4XzI2MTM3IiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PGltYWdlIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3QkRBQUlCQVFFQkFRSUJBUUVDQWdJQ0FnUURBZ0lDQWdVRUJBTUVCZ1VHQmdZRkJnWUdCd2tJQmdjSkJ3WUdDQXNJQ1FvS0Nnb0tCZ2dMREFzS0RBa0tDZ3IvMndCREFRSUNBZ0lDQWdVREF3VUtCd1lIQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnci93Z0FSQ0FDZkFNY0RBUkVBQWhFQkF4RUIvOFFBSFFBQUFnSURBUUVCQUFBQUFBQUFBQUFBQmdjRkNBTUVDUUlBQWYvRUFCc0JBQUlEQVFFQkFBQUFBQUFBQUFBQUFBTUVBUUlGQUFZSC85b0FEQU1CQUFJUUF4QUFBQUIzWTdOZHhhTldyc09EYTh3ejB6dCtzV05zT0tyYkdPNU1RSHFoaWtxZVR1L1pKNGlkcWVMNmszTzRMU1BVeWpjYmJ0V2UxNXFzNWxEbEhZU0IyZTBjMW9IRnlhcTAwMG1ISlhyR1NHSXFYd0taZTFadnVKU3I1Wmo2Tytuc3ZXSnFrMlJFRVZpMWRxeEJ6SHVlL1pyWEVrMW5OM1dUazRWbFk3WUZ6WXF3MGxUdU9LMktrZXBTMGFNbjVXMHhTNUNZZm1KdzB2NG1zMlFScmFKR0pGbFNWc0E5QTFKNUlPbWhySXRvRWhFOVFWUm1aSUt0Zko1cEJiWXdDVFFpUGNOOW1PbnloeGlMc2dabExqK29UVUdhTEVZdllVYnVubHpYY0tLbHFmbGVoZ0FQNUlKOTNlckI5V3FjNi9tV0FRTWZvcWM5cU1Ib3JHZExXQ2l1VG93MW1wakFoV0ozK0pGeDA4czFQTFBUN09mWkFpOWx5Q0ZseTFld2ZZUUMranFDYjI2U3VnUFJsRHZQMm55UXhtbWQ5T2pYSE1hek5CSllpdGMvZHVXcFhRa1VlMU0wc0ViWmdoUXV3YUJNN1NxdnRjdGdiMEdsVFZxODE3dlRFOW42MzdYb2RWdVBpKzM3SDVneXlnMFhWS1k4WXppUzRGMzZDMGQxaVZsWGx2cW9TQVd2dXM2aFcyUkdoWUlYUlF1cEYvTENIVlMxdDg1N3VQRTVzZ3RuamgwRCtwVzR0N0w1a1dPWjV1eXZUMlRsRlpPVkRQRUJNVThiUG84cnRCVW1BeEJFNDRDU2VFVGFpelpnWHc3WHZrY2NPeWxVYldQRjhLNk9SVjZOVmU5NUhvSUwxbnowOWJ6NHZRUXE2UWhURTdTakRXUWVIODMwcmY4QVRlRjVDNzJMcnh5L3RXdzJkb3FJSnB4MU45TE1sb3pXemdVc3FaZUtPbFBSOU02cXI0TGgrazUwaTlUMGc5RDhzWWp5SDJpaFZPRGtmZHZKTk9QTzBpUGh3Kzc1K2h6ZEU4MGpYOXRVMUVURmFONkpmeUdoYUhOMHJuTm9teTkwbm42V25hZENrNm5YaFBHL1E2OEIxck8vUnZpNC9FeWpDOWFKTVNWNXVsWHRPUUtVeTlGTm1HbEdSVmExTXBYbUFYQ05PVnREMm95VjJiYzVXeGY1dElpRHd5QStLMVJ1aFlaWTBRTXZPdWo5K21zbVFubGd3SktrNlc2SHh3N1NYRWk3MXFKYU15N2xmZEROUTdpUlNNc3ZTNHpjYlhVY3NqbjZyT1lTc2NHeG1LTWQ3REZDZ3k1K2ZUUGM3OUZMcmNpWE5YaGk4YnMzMnJRMzZEc2VPd0pGcTJaK25vYW1iVXJWeDFleXNXQlBPMXVGRkMwMVhMSTV1cGQ1aEZrQXREanRyWHVKaExXcnI4OUNNTVpac0QwOE1TSUZZdEI2SVoraEpSd2NLYlFLTW1UQzR0Z2VtUzNwUEwxZDFNaFVzTEZ3anlNY04zb3lsWExKWitwZnc2SlN0Y2JHZlN0QUl1ZEMzbmxucEwzdHptdVptL2p6aUdpUE1nNlNLM2xheUZNb1c5UWVVb1dZN0Y5R3NQVmVOMWlDNkZMTVhFZFVBeFh4OVBPeFJ4RE1BNjJ3UGVYdlQxRFVFeVVWTFFhb2J2bjFPcm9kVU1iUXI3cUlSNnhsWWFqL0FJTEswc0pNSTJWek5KUndTU1hhcmo2UHpJMWFuYUhPMHZ1bm0yUVpEZWliSlFtN3V2cVpQSUMwd1UwUXk5VnVjVlRmUWVkV3FtaDFFeU5HbG01alNPZTZlVnVVRXR0ZElsS3oremRKTXNpMG1WazAvblNsTDlUTTdTaTZFcEV5c2QxbE9HcElSTjExUzJtQjFLVTlRUXVGZE1ocS90NGFJTERweFBSczUvRkQyRm44czFFallsNHVGeXZaUkI1S05CSTZXcGZ0NFc5ZW94TlpnTEJaMEJNMU1LRS9ZbnBDZzdrcnlUUjB5VVYxc1lDRDFzcE9hQ0VmU2JYcHZPWVpweWFwU0x6ZFMxcFpRblZXSmE4eTFMVkoxOFhMM1MvUWYwTHRWbG9MdWIwV3JxNGlyWU0yMVdKcXZldWdWT0NLbW1XOUo0Qkw3bzZSZk1VYmNTYlFXNWVoS1phT1hFMXQrVFhhaVJzNnUzRVpaNHA2U3FoRzRvOE5sQ2tXVlZOVmpYRWU0aTlpdWt4OWVnN2lnSTRqUmMvL3hBQTRFQUFCQkFFREFnUURCQWtGQVFBQUFBQURBUUlFQlFBR0VSSVRJUWNVSWpFVkl5UXlORUZDRUJZWEpTWXpOVkZoUTBSU1UxUkYvOW9BQ0FFQkFBRU1BbzFuSW5UVWxUWXZSUDRwUmIrZDVOdEhFbUVXQlI2ckNaSkUycXNXaWhwOWU3TE52MUM1b1JlSXpaend6OHIxOWFya3Qyd2xYSUg4bE1iK2xjaUxzVmNZcTc0N0pydXk0Um5yZG5IL0FCaXQvd0FaM1R0dG1wTFJ0TkE2d21jajNvbGpScS9TZGVyVFRielYwYVBydzlNeEhPSjRUNnFnYXNqU0pzRUx4dDhSTzJqcDJ5SmtUNzZ2ZkxCZU1oMmFEUkZHWmMyVERLM0lPM0pWeVk3NVM1WG9ubDJyaWQ4N2ZqbXlaMndHM1h4anNjdmJKaWJzWEZNeEh1WmhKYkd1NDlzUnk4bFFqRVRGVWUyVHhNL1hjUmJNakdpRnE2cWdhbVBhMlJPSnBkSnB3VmpObnpKQ05mNFo2YzAvcDQxaDhFT2h3Ni9peEUwcExlZ1Zhc0VYMWk1YUo5Vy9OQXZDMEprZTlFWHF4LzhBc1RDRmc3ZDNKa1ZSdWU1UmZabXUrVmtLMGpDQTFqMDdwZHd2eFhQamtIL2t1TGZRdlpOOCtQUkZ5dm5nbW4rVmlJdStLM3RrckNTYThiM0lvZTVKVmN2c0x1U1VGaXE1UmJ1MWZYK0lWdllxNnBzVUJGZDRXNjd0bnE2eXVSdXgvZ3BPWHUrZVBuTmUyYklteDJPelEwSmE5aHdxNXJzOFFYZndoTVhLL3dDOTVjZmZ5WnBXdFdZUWgvTWpiaVVoUC9XTFBnem1wM21DeUF6b29vK1crV0QvQUplMlI2a0wyN3VrcGphWVMvNjJPcEFmK25GcFFwN1NzU3BiMU9wMXUxWUJrRXFxd3ZMQlBURjlzbDVJamZVbEp2MjhzbnU5KzJlV2ovakl6eTRQd2xZa2VMK2VZbUkyQ3ovZjlwRFJwWm45T1VIQkNsUmpWVE5mTC9DVXJLd2U4MU11azRUeU4zelJjZ0krc041RVJXekFwL3JKaFpBMWIySmtSZDkzWmY2dzFWRGxGWW1tZmt4dkZEVmtnU0dnMHJuTS9hVHI5M3RURnlUNG1hdGh4U1RMeURLR0NENGphaG14QnlvRmRKZUJOZmFrOW5WaE1rNjZ0b01MejlqQVVJcW0zMWZJa0RROUd4Z2Z3eVZraVV4REVId3hUYy95WngveGlOVGI3T1Q0a2swdEhCT1JqQ2lQSFRoNXA2NVlUZW5KT1JzZEZYVHh5RzVFY3paZGNyL0M4aE9XVnYzMW1YNmZ2WSthVGdpTVlwbnF1N0s4RzNaVngwVUtKMjN5T0Z3MmUzYlhWeDVTSStFRW5FbFl0bFZ4V3dZVnRJR0ZrcTVkL3dEYmxZY0VtZkhXTFBuSE9PdmpraXgyeFlza294aGpTeWR2UG13Mmc2NjEweWtoOWpNUG1rclo1R2ZEWlJWY1Q4dVNja3NiMW45c1JxZjJ4TnY3WnczL0FBeDQyS3E4a3lRRkhwMDJPMldiNnBadDhvRTJHN05hcW42cm55cVQ2MW1hZy9xeDgwY3greG5JbWRKemRsYWk0UXo0dnJlUElUa01ORkZBT21ham15cEdxSkxUdmNxRVJwUGNiY1lORVQ3T2FSclljK3hZS2FIbXkyakJpVHlCampSclJ1VnE5bHdjK2F2cFNRNUVwVG5XNkM5U1pzdkZNTzFkc2xKOVE5UDBOMnhNSjFPdXZCRndodmYxSnRxcTIrQ1E1MW4wK2E2QTFHeTBwVW5USk1aWDYyV08vU2huTmtONVZmM3RtMlh2OVRObWpYTlFaZVNwZ0pNZmZicnN5VWFNVU82U0dMbGFZWUt4WkpIcnRNOG5aWHhaZ2x5eldHTXZDQW5wWThpL2x5dG1UNGhVSkRjcU9rU0RGY3BaaStxSWdYdVRsN1M0OVFJWTN4Q3MzYStKR2tqa3huYjRLYkdmRllWT1d6aUFkM2VUYkprQ0R1OTZUZlV1MzZPV0VNMFBjamtUR0dqT2J2eWJqWEJlVlNMSkRsWTZNVWF0QVJyczFvakdhYU1palRlbjI4NlB2bC9zMjFQdG1tb1pwWFdleFV5RFJ0WTkvbitDdGRwZ3NjS0JnQ0V3SjFIQjBYSjh3OXJVcTVMQ09jakJFVkxHeGhBanVqQ3NuQWNrdXNjbkorcVpmS2kxZlF3NEh3NlRhbFYycTcydG1IVnRaSWU5UER5K2cwRHBiN0NhOTJIMWZwMDRURFdkS0lpV2NhRUZpZEdSd2pXbXFscXdGaTJmb1NmcmRZcm1FbEFhK0pMMW9TVnpzclVSQTdmNS9RNWY3WmRLdlNiL0FIcnRRM2RocXY1Qk9iUnZpRFoxWFRHTGxOYjZmZDZIMnczRzE3MjB3YmJLbmJ6bzgxQ3Y3M2s1cEd5RUFoWTZzSXFrczI5dDQ1c0M4azRQMGc5czF5QnkrR2RrSDgrbGE1eWlYdG1wWWpVODIxY1dGNlNLaXJrU0V4cEQ4OThiWGtZeGlGYnhkQ2hmYlJkODB3QnJqVGtjbUdDMXpmYmZOTVYvOE9SSFlRYld0VnZESHhpODNjUnJzc1kvL1V1S0V2OEF3WEZDYi9ndUhoR001UFJqOUYwOVJNK0lWMFFZekZzcktBNVlZTk1kWmkya3dmekM2WGtDeldNa2t6U0pqK1VVQ1ZHNnpoWnFMZGJTUm5oVXN4OGlVd2ZIaFkxWjVET1hzczJzT3gzVTJQbXBTdkpTU0FvS2U1S1I1QVJDdEV6ZDF2SmZJUElZL2tqK2tYWTN5a1hHZ2t2SWNZaHNSdGpFZkhsK1dBeHZBSWkrcnMxdVYxa3NPV1lTWXdxT0cxclNiNXBaVi9WeUp2a25iamdrK1VpNFg3T1B4ZmZKTXFMRlJGa1NHRHpXdmlSOEN2aTZjQkNocG54T1dHU3dUZUhHUlpzZUZXT0lITlcyTXQrbmxHNlMwZzZwclVuaVROUS8xV1QzendzV1I1R1E4Y2hHSUk4dGoxNnhlcG5pRmRtcDRJVERDMGJkWlg4MnJIOE1qWE1TeVRUd2RwR3p2ZlZ6VWJZeWRzVXF0YVJxTlRCSFhxa2J4Yms2UnZQM1JpSWlHOUtyMnlpaHhwY3lRNDRVZGcwYXhHb3h2YlROMnljbnd1L3V6MXd0TnpaVXV1S2g1U3lHaC9rcGhQYkNMdGxqUEJYd2pUNUMvTHFmZ3NxcC9hUjRpUFFwYkdSWFRaa21kTGFRN2g2cDA4aSttekkzUDFycDNkdmpVakxuVVlUeFZxWFdyek9xM3A1NE9haGR2WnlGM3p3MXVoUW9CNDVOa1FtcFlxSjl0RnkwMWJWSUJ3enVZNXBlTmdqbVYxWXdJSXV3amRkcnMxSFBiTGxuSnhWTVZCY0hZQkFvUTN0bHQwM1QzT1Z5YnM0OFVYZktDU3dCNU8rSTdkRXdVYUFmVGRlV1hEYWJDTllNZkViVVJBL3lVd2k5c083OE04UTN5RGFNdGh4YzFpVlRlRzFPd2J0MG90QmFLUFhDUE5qR1FsbkE4SW9CT2tLNG5HV1l6d3pERVU4R1ZiTEkwKytZZlVDUzVMM3Z5cWQ5WUxMNTM3eFBtbUxLaWRhcFRXVmlZUnBtbXRQeEhpWWVSTlZhcXQwckVkMVJRRWM2eEt6Z3F0WEs5d3lGYTBpYnBxU3ZqUjVjbFFBNDU4TnRDRGU1c0Fqc2oxMDlDRmM2dUpoNnVlOHF5QlJEa3dWUGNxaU5mQU9tVVZiMTVraExHSXU3a2FKcWROcU5Ta0Y1alRVTm5MWlRidEZ4M3dKaGRGTnlwaHBVYi92Wmt5WkhhanZuc3k3dXFkMVZJaG5uaVRQUEF0ZERWOUdzcjU5RlFXSVlUZk14aGNLN1RFMmFSekJ5SEprclJ0NURSU3lKUStHbXBjbGRSQ2h2TnVsUTVmTWp5ODM4K2JkYzhPYXF0c05mbk5NaXNJN1dsakFwM3dwYzQvVFpYNnBqVENJWWZOb1RXcGpXSGxTZWxHMzhLcnNFRE1rc0c4SllsaGZJN21oQjZYMFZadlVGeUZnbkJscDVXTVNSOEpBcWFjMUkzVURpK1RxUkR6VldtWjJvbWlSQ3h3NXFlQXROWVBpR2UxVkJxaURhV0xhdW1Na2cxVEhkQ3E0OFZ6dDFrL1lWY2xQMk8vRHY3ZStXYnQyWlltK1c0V1BIR2kvRDBIU3FCMUQ1QndlY3BmWFk2aXFRUkNCWkY0dkRFRGYwQ0pLdEJNYW1oOVBVY2x0bEZ1M21QVHI5U1BMdGZyamQ4OEx5b3p4Qm1acnhsZllKQmoyZ1hPR2x5TXNoS2l0WGloR1JXMmtOakJwMHRWeW9saE9MS2FKdWFPRlp2c1d5UmJPancyTjAvR1pFY25WT1JZMXMzeVVwdlRmY3pOUjJXcDAwVnArYXNSMGp3NnZZVVYwdWkxM1lQbFR0UVRyM1RRcHZVNko5RDJrYXBuc25xTkZXTkpGTWpEa3h5Y21TVitXdVMzZlBma2gzYkozZG1YT3pKRGtUSmxySmVzQ01iaTBXbEp0RWVwSEpESDZtQmxFWXliZUxEWHk5RlpSeVZQbk9BeDVMTjFvWE5Pc2pLbjd5UEx0ZHBoczhOSHFtdnBUZDJiZUlMWkx4eEVqOW5RSWNZUzhIZDgxUFlrZ2pBTURFeDhGc3dmcUs1dVVqaTBoZkxqazdqaGFzb3JPdldkTnN4Q08zVUZGRWhwWVFiWUpTU2kyTmZxdG1ycThiREwrMHlKYTllTlExQlhUUlU0YTZxWlR5cEhVSkJxNDBjdldGelZmRFdmSmNZMWM0MjRwSytoY2xkenZ3L3dDT1RQc3JsOGlkYmZMSmorS09HemZOUGVLbHpweXBGVGdwd09IUzNNV2Q4U1NRSmZJeTZnTFF4aWhzMnlNK09VUjlIa3JINlZucE1vSkE1Q0FJTmQwdDNmVkZ6dzQ2S2VJVXAvUDE2NWxJMWtORVRkWWt1UTkzcGhiSkpwMlhER2luSTFFa1NDQnNqeG9saTlCN3pGOTVpcmswQkNSbmo2MmFhaUY2VE5wSEZiZHBnazJkSzVaVXdqdWNVOE16a0lsWmU3UGM2MWNtTWl5a2xNWWUrSTdLdnhUOE1haEZheFh4WFRmSGp3N1Jla3lYSmRrTFhPbkx5ZDVXdW43a2tQWGJKait5NWJwdXpmamlzNUw3WjVaRlJjdHRESFBBZFBtU280UWVIdDFwM1RNdDBVY0h6U1duamFPaWx5bVNxQmMwOC9xOU1uQkdaYjlwSk1pcnFHTHFVbGxwN3A5UzBKck9XcVQ3STBabVZscHE2YkRiT3JQSTlQNHo0Z043dlNBbVNHZWNQMUpQZHpZUUdmbWRrWUVkVEl4Ujc0SHA3S3FJaVp3NnowSHZsZkViRUYwbTk4NnZGaEhPeW5lMGxzZVR4OVUrcU00WmJOZzJJS2dTMXNLNXZrQlU3R1I2UFVFVTdqUkw4QVhuZzZvTDJOcXlTL0NhZmx2N3lMZVF1TFJzQ3YzZ3k0eUtvdnNrTGdSTlJ2cjZtYVAwQkFmdEN0dm5KcVNyMDU0YTJDWFVablRUV1YzQzFWcU1ka0tQd1pTcWpPaWpQYTBYNXo4c3J1ZFYzQnZKeVhNeVRxaWRPQzRNbVdWeksvVWxoV2g2TVFqbXQvVzIwZXhmTUhlcU1meWJtNnY5c2FybXExMitCTTNiWnE0RW5FemVHK1I1UG8zZGg1L09JWGdtVXE5TVpENGVkTmRHV1A1aDNUcXJXZFR5MG0xNTFZK2wxQkN1Z09Pa0xpVWtscEFLc1VMZVRPcDBmcWhzUjVlRFc4ZHNjamQvYklveHZibi94QUJCRUFBQkFnTUZCQVVJQndnREFBQUFBQUFCQUFJREVSSVRJU0l4UVFRUVVXRVVJREp4Z1NOQ1VtSnlrYUd4SkROamM3TEIwUVVWTUhTQ2tzTFNOS0xoLzlvQUNBRUJBQTAvQW91ekIwYUVSMkhhaEFQdE9pakxMTk5paTBmRmhHa1hqTXpWZTZ2K0ZMcVZkU0s4UTlsaCtuRU9TN1phWFMwTlRqNGxRekV0QTBBekFGWEZORGZyUzJldkEzS2h0LzhBVUZYdXIvaFU5U3BGYVNPNkhzbjBDME1oYUU0dkdVa0hSQkVZV1ltWDB0WU9PRVQ4VkdMcW5VakQ0eVVkN0RoQXdtOVNiNXZyS3ZkWHU3bFBkTDBWN0s5bGR5N2tCMUtyelN0TUc2bVFoc2lEM3J6YlhhQzc0U0sxSmpPUCtLdG9rTXo3MDU3VGhWTGZ4aFZxYUYwbkx2WGVVRG4xTzVkeWJ5VGh3NmhjVjNMdUs5Z3IyU3VjTXEyZE9TdXpXSDhhclUxVk9XK2FoaytWRFhHNWFPYnNwSVB4WDhtUDFUZTA1a0JsM3hUMnpodmZDWmVQZXY1Y2Y3SnBIbEtQMEtKRlR1WHYzaDU2a3BPRFlrbHptZzkwc1NlUk1UdUZ5d2ZpVlkrYXJVNUljOTJoVVJwQkl6RTB6c3NiRXlYM3hUdTB5SkZKQlRMbXNaRklBWDN4UWY1ZUUrUGg1RlF4aGM3TnczMW5xVFhGV2grYXFXRDhTdEFyUlZvODBlS1BvT1RZMGcxeUhCdTZkNG1nNlFHN1VOdVZvTjlSNm53VFRJOTZnVmtOQW5NcDBTL290VkdsMkpUWUpWWjQxV3ExV3ZhV21JS0d5b21mQlJJaGRtcFpuZDNJNjdwWXdDaEk1cHpaakxjVE1DN3FIYzV4SjhweFF2ZGRjRWJPK1hyQldnVm9WUFVvakRKeVlKUTJWNUJNMk4xUkp1eVU4d3d5UWRqSmhPSzQ0cEsxbmFtRTdEeVJuZlE1Um9MYWFXRTBpZWFjNmw4NEp3Y2hJWElBQUd5S2V6eVE2TzNDRk9iWTBnWmpoVEphc0d6TmFlcE82YVpYRG9jZmo3MFBzMy9vb3pnMkhBWk1rcXVGK0lLMEN0U3M4TFY5MmdMNjdsMFdjdjZnaUpTOEVDVllzS3RBUWZGVGlOSUI0elIyT1h1VnF3LzlWSlVINTdwbGQyOGNWTTFQdGpybmNoNThQYldqNWhWQ2J4dE1NeXZSanc1UTNQbWMxYUJXcFFIRFZEZ3ZzZG9zL2VuQ21jVGJTV3lubkxWTnFMUjRKM2FhWVZONnNSZVNueFdWejRjVTJNLzVJN0hweUlVYVBCYVhUN09GWlZLZy9NOVp4azJ0OHBsUGZhUDIzYUJXQU9GSVJGNTZPNlFYT0M1UklqUVhzZzBnbWFyQ3RTcmFYMVUxNnJhVnRPMU5oUmRwaUNiWUE5SjBsdEVBeXNXU01LL1BEY2ptcTBJQjg3bW5PaER0ODBZNTg3MVYwVjJSNWhNc2kyZnNvYUJRVy9STWRrSXdubURxb1VaeklHMGtTTVZtaDZrQ0U2SS91QW10cmZWc2NHTE10MmVIUENHRGp6VG81c2cwMCtUMFhxeFNQelI0UnorcUVjVXc0bm1xMEN0U3JZa1hMWEpFWHRkZk5PN1Q2SkFwZ2NYZDBrKzhUVmc3NXFVTStNMTBtNysxZEhpZk1KMWlCZjZxMWtvTEdPYXdzQnZRRndBNm5RSWsvY3JPSFQvWW9rRmgvd0NRY1Z3UEJmWnh6L29nTUVOenNqN2s2SVhGNW5tcXdyUXFPN3lVTnJwTmNvanBTYnRCdlE4Nk5qSTk2cS9WT1k2NU5OMGtZUnlnbmlwUXkzeWVzMDZKcnM4cFllNVdMeDlWck5NYkNjMFBISk5Gd0NzRzNvQmQ2OXBTOUpSWUxtT3hjUW9PMkdFUU01WDNxeUZEeVh6T1drN2syNnF3a1BlaExGYUlSQ085VmhWbFFOaXJoVmVhYXMxVzhWZUNGekFJSkplZUtlWmR6dUNhUzF3THNsRWpYRUhPNU9lSFV4SXVZQ2hzTHBBajlGQ2xWVy9qNEtGUFVtYzFDbUM5dVJUK3pEWURlbVFnRHVyTzhGUGlsN3RwdHFyYk1UcDBWaXdERTg4SnFSR0xNSjhyd1JPNVY0WWVHVi9jcXdxeXYzYi9BSmhXN3NQSENtTjhwRTBaeTcxRHFuODFsT1dhaFJNZVYwNXF6QmpPZS9DeWVnQ2NLWU1lRVNMK2ZGQmhmdG0xRHpHcHQ5TzB4SnNlZUhKTWptSHRFdUtoSEhkOFU5czJrYTdxenZLMldLYVh0blZlakRBdE9qbStWM0JNd3h5WWZZT2srOUh0MC9zMmN2SFZPaVlRN1oyc0F2eXVWWVZhT3dlT2E2UVExM0REZXAzeXlVUXUweXVSMUNpUkJVUzFCdmxtdnVCbHFPS0xxb1VKbDgzVDE0SnpERDJ1QzU5TlFPb1BGTWJMNlM0Tll3L21ueERHalBZOENiaXNqVkVURzFNYnd2M1ZucUIyU2dpVFhSSE9ubXRxQlphdWRTMG5UUEpSTDNiTHNaYzZnNkxaelRFaU0yY3lVeGVxbDBHNFQ1b3huU3ZrT3l2dmYvRUwyMFpoTWlFTUR1RzZXU2RPK2tJYTBCVnlKbG91U3FhNThJUHZJbXNuQ3huOGlpTzBJRjN4S2ZNdFlSdjQ3NE8wVWhqb2tpNGFYSWkreUV5Q296SlFRKzR2UE5QaVZCbzB2VlNiQ3BOcHdLZ05KTitpY0xxbTNvTGpOZTBqeFhFYW9uVlp6NHF0QXlxOFZhbk9LSjVwbHp5ZjJjeDhRZTFNRStLT3NDQzFxNVBrdWNVcjcwcjIxM3AvcEZTRUxvOEZuYTVsTWhGczlUSlhLYUxSa25adExzL2d2UkRrN2daZmx2MDNETzlhSzlQaUVxZlkwUStQSXBsOGVHQmQzaFN3MXJXakxxZi94QUFtRUFBQ0FnSUNBUVFDQXdFQUFBQUFBQUFCRVFBaE1VRlJZWEdCa2FHeDBmREI0ZkVRLzlvQUNBRUJBQUUvSVRyak16TG8wUVpsK1E1VENoSDY0Y2dzQUEvaU1HNS9zd2dBTkNKbjZxQVZEb3hrM2hpQ2ZSakFKd1J3SERYditld2dBTXd4cDIrNFRnNHhDQVNJejJhOEVqNDJlaEZqZWNTQS9DWkY1aHZWVm1ZV2g0ZVp0eEU0K0J0TUZHSnM2U1JpbjlERWhuRS9SQ2hBTnl3WVpEWWNHNGNRVHBjd1k2SXovWUNPcHEvcHdRSm10RlFjR0dFTGI3Z29yRWhjcnZqc1FnelE5WWRZMnhuSkJoNUgyREQwc0tQQnhoajRrN1RrNHpWRWdHK1ljZFRlb2U2eTRNSTBueGNRWFIrVVdMaENhVVlrZFE5ZDQvcUVrMzMvQUpoekJvUWlTVVZDQVlDcjJKVzNJbERHd2EvZTRRc2Q3RUdRR1lKTUEySU0xWnpERG9YWVYyanFCdXJoOGNFUFc4bWpESzlnbUxjaGZYL29rREZDU0NCMDRJSG1CUFJnVHVKaHRyL1Vjak9sZ0NOeStXVFhVeFBiS0NkQUFZTkJneEN6Qk1XU1BhSEN4QWFCUnVCYSsvOEE1UXVONjJTT2ZlVlpwd2h0emdoQUFrOFpoUmMxbDBXWXcvTWlvQ2E1Y2FmTUF5U3BKMlNIQnVDUTVtUm00UVFtYTI1cEcxK0VPQXNqOXpLelV6QXBJanBRaFVLWXhOelpRWDRuTVI1TkdHTFBYcFdUSUd5K2gvY2g0ZldiRmFhQXhUSXg1TjRMdTZSL2poUU5ab3NsQVc1TWZ6bWFJUGVlcHhFVWd3NERvTjl3UkVmZVcvdkFaQzFnenJJNVN6M08wWUhNNTVoanVQeTNNTmlEQmhGcTdlMklLcEtZQ21VQmtpbEhVTVB0SXpZUG1Kd08xRmd3QmR3NlFGSDlrcmhaemFya2p3ZGVSZGh3WkMwZ2VBSEw2RWNuOHNLbkFBMGhiVjVtZEluWVBQWWhKRklXYWoybmc3aVA0UWNUSUN5eFAyRTVnSWh4QmFMcmhsNVVld2k4L3dCakFuSi9kRUJSeWhEdU5uMElRRHdoZEkyZUZBQ3pPYzRoZjFEUGQ1MEI3Z3dFVkVGQ0pBV01BR2toajd3dUkrSWxrMkZRSHlCbUh2Y3dycUUvaWpwaFF3aWtIM0Jna1RVY3ppcml3bHZCMXdxQk9rNmxhUUZhY09nUU1BaGtVa2paeEN0WUxSZEd2V0hYdy9jZDdkNE54clVUMEl5Q2pyT1dwSUdCam83QTZoQm8wVUo3d1ZKVWJXU1ovVElCQ3NFU0t1TENTRGhDSUZyUmhLb3JLVjd3U3dqVG9lSVhNbUhKZThIcUFkT0tOYXczM0xGUkhNRldENlRISWxIMHFKTVBDVS9tVjU2dzJ4Z2hHVkpobEs4NGhJd2g3bklGelUvWkZHMkkrc0NJb1hCREFHbzFIMEUvTC9DSkFpUGtPQXNNbUN4SXdIeTlIY01JR1R5T2JHSU1MV1V6WXBTNXBUTmtFdTNwR1ppTUJnWk44UUYreG5qWkR1WGRJR0REVTdRM3lOd0ZhekJ1c2x6SlFOWFVIYWFLb1pVank0REgxcHRSQ1lETjNHeEFDbUwzd05TdHMyb2JNVERLUUxScHA3UlNJU3NTSHNoQWk0Wm1yTlVQTXlYQW5OWVJmNUplSllSeGNhTmUrRkh3SkJKYWhOSmtGSUNQb041NUlXY0FoNEJDYXpHK29jcEQ0UUVzQVIwY01JSllGNWtBTFBpQ1o1MmcrUVVkMUdhT1FnMlZJVWNHeWpZbmk4VlJqaWNReVE3Z3N3NytxRlY4VUs4TnBueHRiWkNyY0FTUldTRGg4WGhRVm04VjE5a3Rja1ZCdUIvYkxEUi9sbWJnSG1hUzc4bmhCWUFNcSszS1E5Q1pKSmtTblV4R0tHYUlSNXVNc2pXTGhNNFlySnRFUmxQQXh1ZmxEd3VJS2FacU10ZXNLRFBnYjVnQWtnSmtRNUNlWmRHMUJCWnlnSWlCQndEZVZkUm9RQ2xOMGc4bWJqS0F3WTVqQ3pVTEF6bUpSMjh3K095RGhNWTNveEY2YWFDVmJPb1F6UmY3eGFLWno1Z2JHd1oyaHR4SFFrVVVCNkZ3M3FpSlFIeE5RN2FHT1NaRWJhY0tmSG1vZHFCMndUK0lzckZENVF4ajQzWDBaNmhDblNvL2lpZXM3RDdVVlMzQ0hhQWlqRUNhSDVtUTdJeE1YUmFlZVh2dmNVbDZoV2dtY1FkSVBhTDZoYjI1Q2kzZFBhRGJGcVlabmcycVV1QmZzS1NWVjRYUGVLeGw4T3JBbFBHRys4SGp4KytIallKWGdSZ0hqQVFpV1VsQTlFS3ZZbEh4QnVLcUJZT0VLNTE1WTVtdlcyK0tRMTJyZlBINk5qaVRBSUFjUTBKRndHcjZYQWt5RnlOUWVzZGdZRW85RW4wbFpyRlFFSUR3eEpNeS93Q1VVd01ya1pnZWpnb3RVR2tVTUx3RmJFd3JDM1hFQ1N3TGIvQ05FTHBkWk5HSEV0dkVqY3ZFV1pmN3dmUVB1aXFyR2FPTFZHRlZLdWhlT0lONHRPN1dRRXhOUVY1ZzR5d0lMTXlCakJQWWtYNklSQlp0NnlhekRNVWR0b21zUHhPUmp5TUhVRUFKTWd0eDZ6cmlHUk5naXE3bGl3UStZR04xV3hnUVh4SWRGZUdBeGdvWHlpQkpnRVdPTlBXZFNQc0VIY0h0Q2hvYlNLdGNvTERJMnQ0b3h3UG9nNk80QWMrWDFoRjJFZVlGTU10elhUdU5CN1lFMmV2aUEyRmVTWUFJRGdRUXVjUE1mY2lIRWRBamxpTFJqSG1XUXVoK3owNWtKSkxVSTQySEpWMUtHMXBqRHNrdWtCdnp1Y0E2SEVlSzVlQkdWUFhKaXRuVWVHSURtVUVPcCt6TXkrWWZqa0VUSDdpZUtxZ0tpZ1ZLZEdNeU9RQXBtaG5pYytRbG5LZzhiblVXWWJQVVZlOGxORDN0aUV0djhrVzJsSHYvQU1Lai9DUUJUNFBxSDJwUmhjb2VFSW9lOEkyNXhCTW8xQzAzNWh0cW1wVUFYTXVxZ1pvTzlybURaN3VKb0FPaGgzWm1saEtRWDdjclA2N3FPM3JIcWdPT2xITGxWdVlBMGRQbUJQZzljTWNEcUdwL1F5K2pDZEZGZ3JlS3lCeVdSeDFFL3dBMG14Rlp0WWdFU3pEUXk4bFZCb3RXdjJWY29yaXdyQ01vQ210eXViZ0FYK3AzS2h5KzV4WEU5UitJVjNLbkVreEc2aHVHN2l3SThwL2NXQ2hFNmt4RTRqbEUrSU9SVEN4SGxHNDlKdlFlWWFzSzlnc1FPREV0TzIzWjhBSmczZ1d5VEpCS3ZpRzBPYWxNdGdvY1ErckNLdmdUT3IvdHphNEwwSVpzL3dDa0lKb1ZEbEc0aHIxWkJ1VTJ2WkcxNW1BempneG9ZaXdFRnoxWTRKUFVkTm9oaDVxb3htZ0JCYmcvV3d5ek5HME9tNVhROUtpTmt5cnBiQkxmVU5aQTBmY01YeWlNSGhtTG13NUFuSkRqTXBFcEtKNzFEMmtnMHg4Q1Uzd1lnTmE5NE5vNXkrb09RaFJBQWRUUEVZQmwyYUVmRHgvQmhHSWF2eVJtRUVvVmw0NGd6WnVrZU9DeWN3NDg0UTF0KzNDaXZjelZSS3poTGlxK0RtWUxPQUp3aHM5b2tlSVVnVm1vNWdDUXZtbUZsYm9RdnBrMlJRckVJWnVaa2hWRFhoQ1RLeStUQXBqd1l4QnZBdFNrTjVFRGJGUWV5TGpzWXh1Z01wWmF6OC82VGQ3RmpaYUVHeS9NUjVLNDlFWWxMblhDRlFpV1hJL2pUYXp2KzFQZjNvbjJZZUcxaDVqbVFMQ0FZY1pyeUU1TWR6TEcvRTNFU0ZQQmpoeXlDQWh4Sy9pQjJzRS8waTFWblErUkNHalFpbUN3bkVLVEY2aDZaUDhBS0ZPQjFDM0Uyd1RwYVFLREZka1g1aHk1WklkU3dOY3ZCYlA2d093YkVTdUtBdmJ4MUNqdlJCOXFBazg1ejlvQmhlRUxZWDBJVVZqTS84UUFKQkFCQUFNQUFnSUNBd0VCQVFFQUFBQUFBUUFSSVRGQlVXRnhrWUdoc2RIQjRmRC8yZ0FJQVFFQUFUOFFDZmhOUnJyYkFwNGppVFF0NTVHRnBxQjh2WjVlc2dONURha1BVVXNUeEppQ0FLTUlucjlLeGJDOGlRdXdKWFQyQkFBTDRSVGViUnBINFJHckFJaCtadTNPWVlsckg5WXBLZkpHMWx0bURYdm1Bb211bnUwdHFxdlJITFQzbk1yQ1BaSEc0VXRkZzVxNzlKQ1lxajYvT21sYTdmQWwzZXpnWlZzZ1VBV1lIcWVsMjhxS0YzMGpVdlp3UWgxU3hhTWF0OHdRbkVLcjF6UEpYZ3lMVDFDYWRTdVNoM3FGWXVHcFExRWcvQitHYUVFcmlkUXpPSXVjL2NZYTZ2N1ZDd1BleDIvQk9ZcER3UTNxT2Q1YlF5VnNudXRTcmFyZGhQWjFMbDFITEZ6VjlqY0tvejJMaUpRUXVkcGloY1ZBWXVHdFFleW1hcldVVEtmaHJDenBzdnlobEZ6NXgzQjBDMkNzR3dkQUNzREl4VkpDdVU5TVR3UEM2d1JBMXFsYkNLNGpqb2lXVlhHMHNFNVhYT2tMdUQyakhjOWRCbG9ud1Qvc1BlUGtKdjZoM1JRVmlyV3UyT2xhc0tyY0t4aHQrU3FnTzlZallLUHpIb21vZGxzUnJJQmhLbW9mS3k3TmhDQ1hsYm5Da0pmaW1UbHRRYXZSaWMrZ1djS3orbExpcmpGWVdsNkhJakpUUkFIVE9UckNRM2NQQ25pZE84VHVzZzZwY1BZc3BaSzliUFArV1ZhbDRkUDFGNmFjdjRMZ0dGRkM3aHFSMklzVXU5cHVCM1ZkOVRmL0FCbitVUTB2am4va1dCM2dsdEFhR0RuaUNWWnJzQkY4azBXTHMwVHVUakxOL2FLaE9FdU9xczd1TnJDMDAvQVpHaHBuQ05mNHFjQUYxUWYwUXdCSEFhYzkxQjlUb0lmWVM2a0ZodEx1RGE2eUpaZWxMbFJTeHZJZFhyRHNhb0hiTjZKREVnUzdlcXdMbWhQRzdWU25wVHhjR3ZCM3pCNU9iTUtyblFDeTIzSmZBVlJMcEl4QjlwZVhINHdqc1VtM3FkYnAzRVVwVDh5MnBoYU9EN3Uvc3ZzVFdIRWJ2ZlZXRml0TFdaRlBBR2ljUThrZW5VME5wUXk2TTNuRnRWMDhlR3NYMmJERllZd1FBdHVTc0JuU0MzUXQyNHJrNDF3RVhvUUsyS3JLN2wxQjVKQU4zZTRpcllYZ0V5OE4yd2dnZlROS2dLd0F3TVljSVVnemlKeG1MenJGeXlhRnNYMmQrb242Q3dRSUZHbEZ2MU5odWd0SkI0dFZtOTRScHI2WUtFUmo2TGFPZ3owc1Vzb0tCQ1JkZFFUN2hJZTdaNjcxR0wzS2pKOUtDbDM1K3lISHdtYU9CRUZ2VVh5dEtvQm5qaEVpWGpNbGtQOEFsRElBOXdhWDVpaTNCMFZ1Rm1PRjJoc0czZmNNa3BzSzBPbUR4MmczM0d4ejloOVp1RFYxMmNQQllVUHVZQUo5RmcyOEpRNllxWXJ0TXZ4RDZCTjY5NUZZMm5ud0pGY3JmTVNnVUdEK09ZV0Jua2hoaGhVRStSbW1TMDZDaHFyRGFtZGt5dmRvR2NZRDhyamlMU2tXdDhQSkdJRjhJemRITW9zdkZYeXdHbTJOc2doRmhBTmRUUjkrSWw5ZWZNSWVRK0tsdENjYzBVTkxUWVhYVVllOWdLcG91N0FQUlliRDgveU5CdG9ncnZUajRrSURRSytkaXBWWUdOY21zRWZWMDQ5ZmNkQmsrUm9JeXp2bElMRjNpc25NZjVJcmFvOHpjRm9ONDQyNHVQQ3RSN3pOci9uUDFaRmVmcXFlVk9wUXo2K0FYem5VMk1yMmZsYUhIUkd6MXdtbEZGdS9QdUF1OHFqWGs0VGNqTmtmVzNVWVFCSlZYZ1hVZU5CK0Ryc2QwNTVrYlN5dVZKeFZHMkN3a3BXYWNmS0FwWXdTMUYxc29MVCtOQ3dMYzJpdENWMnEwK1d1T3EybitNM3NBMDdMUXdkRHVWMGVwUlJZVldsN1pveWxZNk4wV3NMMnp1S3F2Y1ZBV2hIQlpYTFlHaWg3TXJ1YkdjbmtwQSt4a3ZFa1N1YzJMQWxRdWQxS014UlN0bFBNSk9wVlU3QTNHekZMM2RMbGFFSFVmUFVjcFd5V0M3Y1ovWW1nQlpWclJzNTRnOFFLOGxBTmRndUwrVUVka2RvdmlZejErTlcxUXIwd3J2R2tTVWhkSW8yTldmS0dkNHBKVHdmaUJRSlErOXE4aEJETXhGWVc5akdzeHZqdVVHd09LOWFBRUJXd1lrZElrdlBPWlpSa3FhN016SnZQL2FlRG0rY2ovd0Q0ZkNZdjFTQXRtQzYrcnVQSFB0V3FLanBhNFd5MHRmbVc5YW1WeXVKanppQ2dCR3BTZTNGMyt2VnpGSWRPUUE1dEQ2dUl3KzlpVjRHZll1d1IvU1VXZ0tidDZKVHBWZk5iT0JBYTd6cHdyaTBsdUpZNFp5cVc1dDlxV2dvTmZXWW4xUytHdi9rdGZWVVd2d2F6K0piWFlRdW1DNWVaWFd6VCtnNVMyMGNkeE1IRlJGeHlyNW80b2dFRmVqd1psaUlpWnh5aFpsd2RybGhlaW8xdUU2dkRwbUYzeTFUbGdqOHNHY3F4QTFrd3lMVXVzWmVqRDJRU2tGK004RHFoOTNMNE1DS0NLMDFaNng3Z1hkcUNDQUM2bXVFUEVXK0hWbGlRb1VXN1RCS29NYldTYThmOFlYYzVKRGpkMzNjbzRsME5IQzB4V0lNVkJlbDVwMG5FdUU5YTYvTEI3bUNrQ2s5dlRCMVdpY2FMNVk0QmJMdXhtbDBhbDgyZGNRU0Z1aXFWWTBsb0VPM3FOc3FoeFFVMmNBYTlFUllXc0R6Y0VPVndsQitxWTlRaWdLV2xGR1ZUZ3VzWTA3NGowVnRkMlRPY0dMOHRiSUZ4QXVvQXF0YXhYQ0ZKWEQ1UVd5eUVPVGs4WXdpVlZLbmFsZFBCdCtvSHF1RTBHMXl4dUs4d3IzSmhTTTRkbmx5dmNJMGZHWkxQOFBEMzRKbnQ2N0hyeWlKTXlRTTlUR1A5SUVyNFlJMUEzVm5RWVFCcGlNcGNTQlJQK21BeUZUeTVRTUdXditXVWdyalkyOHFxaTJrV09mNDg4Q1VzbDhxc3M5NnNWbnh3eGJTb0xCVitGaGlibjF4YkJFNGJVWDNDT2JkdjNtWTZyWDUzRTB4U2xrTzRWREZVUGRNNWlDSE9oZUZZeGZjVjVtOE1MNXpWSG1JakQrVmlXbWNBUElGTkhCYTRpU3E0MVhhSWJJenh2SU5QZU1JZ1NMVGtiakU1MU5GUGVtUldyTURWS3JYcVhkRWxpQzhEbDFUTExwQzBFZ1ZqTVpqV0VicWc0SmZYUCsyQXo1SVkrdFhEUkdlYkJQekJGcXUvQkYwL0ZNVHoxUkRtMk5XU2hDNjRXckhxbHN1VmhlVWFTcmFtalEyeDZ5YnRLcWtjclNtQlIwb2ZyQ0NKaytBdFBicUJRMEpXbXRxVjRSR0FwckZWSFc0TXFidDVmQzZQZ0k3SDFsY1lnb25NMGhab2p4R2pyeVhFTXI1NmxCOVd3UU0rUnhMN2JyZVZxUElGMzNGUFcrZnJSWFpxM21EYXFsWENjQ3h6V0V0Mm9EUXhCelFrQ3lJakRnNElLMStvNHRsMDg1Wk5CTFkrYUhMRFd3d0FSdHZ1STNaZVhmOEEyTDJvclU2L2NIeGlURGRoZm1INmFJTHdXTEVBRnh4VWNUd1dwMnRVdzBReUdRdXVBeFo4N2NBb1lOVzBPQTM2NVdMTVVKU212dHhPWWIrVEdIRnBUTlBFRzV1MkNxOUFVdUM1VnNlVUtLeGR3ZS9UaVYwTEwxV0hjQU1Ea09Md3J1bHFFaktMakdUeHdkUUxDeW5qUWlleVY4WTlCYjJDdUxBcVFNQ0NnSEtpVUJFaldTamV1Y1VzaDllbFZRdFBjSU5BdTF2Y0JKV0I1UVZXZ0Z1NFBuTEYyWE9vcWtlbFFLTGovWENPWTc5dzNrdFM1VTdhNTcyZC93QWpBM0NoOTFjVXlvODJ3WU14UTNhaElGVkRQaVRVZGxCVmVtTHlmYzIrTVZ2aVhqRUZzS0Zzb0dta2JtZkh0ODVtWVZjVGpVM3cxbGVhU0EwdGZKM0ZXOTdMTzNiMHFLT0x4ZlMrWWUvM25DOFU4cXN2S3NTT3R0QUt1cDBPWmFLZGF0WWlyc3BGNnNoT0hRQWhMVzNEWElDaW1yUlZDcGV6aTM5QVNLRlZvSzFRV0VZNDFWQ0d3SGtLT0VxcHk2cWRJQVU4cExWU0NERkZLT3Z0T0hoMmVyN2llQmNPalA4QTBTbm0rNFExczhSbVNqKzQ1ZmdvWVJZZFdsTEkrTXFkS3lMRmwzdU9ieHY4dTh0UkEweHRZdWkyQTBBSjJKOThsYXk4c29hTnNNbHB1QnRwTDN1VzlMcUJGWnI2N1F2RWpodThjNTV3YkRxMEJPZ2E3ekc0N1BDM0wzWXZCeVlkWnovQ0YwU2x0TzRsQkJVSE44azV1ZUd4T0hWN01hTUJLSnNJa0ZHclRqcE1Sd3RQYWI0SVhxeG5QZnNkeGVaRzY1T1U2L2k2RFl6aTA1NXdJYUZXMTFDSnlUcUZ1d0RoTEkvQzBENnVYVDQ0SU4rbjZjd1FiZjY0QkZ3YkdyZFZoS0phckNKRVBSeFNrZjdGMnJpQ2lXaHl1b210WE10WjRUdHhjTFUwcEtSeEZVaGppUmZkdmhocmNkOWRWR2dhZE9SbXJiL2RsZUladGFWU21panNxTFhCZzZERVdxYXp0bGJ6ODdFeFUwSHF1TzRnWFVOOCtKUlhPYkhpQlNFOHEwanllUkpRdFplSCtTOWhHc0FGODFCTUQ2a0ZORkh3YWlkZHhMTnJvaXI3ZHgwMmI3RGdRR2lnc3FlVFMxeTVISmF3RU5nbU9keXEyYitlS2hvZk5NVXNaakg1VVJjRFFSTFhGbGgxY1JiMGVDSzQ2eHNGa29HL0JLcDI3UEFuRmN4T05qVHNxcUNEeXhZdjIxSzRQZ01WUjIweVpTNldtMFRjbmxrSDBHUlRFYTFHeDE0WW41TllSNFJhanU0cFFYNW9PVkZRWlVvVFFiZ3U5ZGxzcGRLeTRESDZLdzA1ZUw4Wm5nakFuS000SDF4RlAyRm5TZmxnMDNtdEdyeUFjSGxIeWJndjNXSmU1WDQ0K0FsT1ZaWnl5SjZtaTVTeXZxQnB4WFlVWGUzOHpmcmJ0cUdCMmcvZHRjcGFVS2VqSVBZeFd2Mkp6S2FORnNPREF0STE5TXhuK1I2bjU2Z1EvVzBXSG9VZ08wU0Y4KzVaTElhcUwwck45blppa29BN0NyaGRnQlQzV1J2WnJyNHVjalQ0M1ZidFEvMm5tbkZ0cVZHVkVUWmJVR3B1T0YvZzFwUnd1T1piTzBDUG1GaWs0TU1VTFZMdWpGNjVxTGV0VXZEOHhkc2VBV2ZaN2xaWVFnZFRsV05hc0E0MlFlVVFGNE1QNHh3c3ZNdGJxZHlsNEp2emZBUFdqRmpmbmdROW52cEUydkN3cmkycStKby9FdEtYbGN1SXV0QjZYY3hTSFFVUlBlWUFHbjNrLzhRQUtSRUFBd0FEQUFFREJBSUNBd0VBQUFBQUFRSURBQVFSRWhNaE1nVWlNVE1RRkNBakJoVkJKUC9hQUFnQkFnRUJCUUNqbG5kV2JOT1Ryc1UvU3Y0ZjkyRThXSDVyN0pIOWYrQUpHZS9UanR3cjhRTUhSL0NKNXNvOGplRkEwSW1CSTRLZnBRZEQvdHhuQXlKNmJmcmordjhBR2RIOEE4d1ozSFk4WWR5WiszeXdFNEd3c2ZRTXJ2RzVSbTE5a1hCSE1vM1pwOFc1Nm5WNTJPUzU1YkI1T1Y1cWcycFovYmdCL2RobjlxSUVicFppdUZPaGw4U0hrbWV0STU2ODFNOW5TQ0o5UjFFQStwY3pZK2YwNzdRUHhUNExsL3VJaWVlaWNnT1p0SDdFZ3BBMVZ4dFZlSFhVRDB6elVVUVBSL0ZQWW4zSVVEQ0pIQVpEQVk0dElBVi9icCtJSXloNmd5bnRYelRDNjhqOGZTMHJLOC9wK3ZUMS9wWEZPanNVcStoS2gyUHBYTlpkVzdCMC9pZ0dQY0F0WHVkenlHR3pER3Njb1I2a1QwcmpkOGZ3YWtzd21tRUp5U2tMS0hxMmVjS01ZNjJMNk0zb0pzeFdISmhKSVBZNVUrOWYyZngxTUlVNXdaUWRhQUF5WnhqN0g4MlBISlk0Q3laRStTeFJWZ2hJSlk0VC9zUGU1K0F4KzMveDhkR2FyZ3BoWXNNSjl5NHhBWDJtOFkwa29PTm4vdXdCNml1blhyUG1yd3lCb2tKQm14Z0FHQy95M09NdlFvSW42Sk9PZ2dvQzBENkVIei9yRDIyaExYRnE2VW5EcDV6SWJJbjdtdy9MWkk4ekNIVzA3dFhRSGpyT0JpS3hKUTVzNlZxblgwTmliWDE2dWs5VFlVbWJPVDVxSGV3VTBheUJjNWxmV1YvcnJ6YVczOVArbmEvMHBWQzVyK0FFMUFkdTV6cmJjM0Zic3lLaGVxZlRlS2R1dnZyTVNveGo3QmdjTGN6WllnQW5BZ00ySHNxbm5nK2NiQ09qL2tQMDdaM0NYMzc2Y3ZaSDZGaDBsK1lQbE1BYkZvZXFyUk1EclZEVXZ4bWtnVkFSblZBay9rblFNZVFkT2NLZkNtVCtCL0dIM0xKNUNYMGRkaUF1eWw3RXJyTTdGajNCOG1KUzZXb3JGUnM1cjZzamxUM05VandBeGg3VFRpZU9Ybzgwd0llUGt1bWVIMndBdTE2TkU2MVZDLzA2akRxMjhkU05UaHhmZHJPSjJOMHc3SzhPeVRiaGJOZVJTWTdqZDVIb25sMExybmVUYm5KZXlZeEdhLzd3UC9zdFd2WTcyOWRCdGJoeVZJK25UbkpqTi9XclYxMVVRaWMxSGY4QWMzMmpXbzdKNmtnV3JQaVZRQTFrTXZYeFQzT2M3SnZaWWtDWlplRnVtYzdGakVqYXNOWGxvejhFbEI4bnFTazFSd1MvRzMwdzkrTjBuWWl3b0lQV1pEcHJ5aTc0ZlpaVjlYTFJOTUNGVy9xdWlLUHNvUHRYOFRBN0g1UlE5SWMzb0tESTYxaTFybUY5YmN2ZHJaSWUyNlF1dkl0UXQ5bU9GYXVtalRUYk14TWY2aDMxTWhPY3BEYVJzaEJKN08wakhHSjVUMldmajRxdzdNKyt1ZXpPbWs5aXlYU2pxT1drV3RySjRWdCtOY0RuMUJldzF3L2taak5LQ1VZVUtaWlJkRHdMTmtLU1pYajZBUXJWbXBTenVHL05QZ254QU9TeldQdGRXS1YxS1Vlc3lxeVBNOUVpdXdwVTYzeCtva2pYMUZMczgvRVNzMFNnRG9FUVlWOW1WWnpod3F3bXA4bzUxQ0JxVTQranNPdE5QWWdpTUFKa2MxOEI5bmJpeTJsT1U5VmpPTkd6WkhCcmZDOG8xMTlaZExXeWcxQS9OSG85aC9CQjczd0RzWFBNWWRYdVVFZ3hFV1ZaNnE0cGpnZE9lb3E0OWp4OXA2VVkycWRPYnpTL3VzUGg0STZMcnhVdEdUa2E4Ui9pUjFlWXN5ci9BTU9pMFVvODg5d1E0TEE0VzRMT1FmL0VBQzBSQUFJQkF3RUdCZ01CQUFNQUFBQUFBQUFCRVFJUUlURURFaUJCVVhFaU1tR0JvYkVUa2RIQkJCVHcvOW9BQ0FFQ0FRWS9BSFU5YktwLyt3eGRyTHRaakcrSnhkOE1jdVpWdEhoRDJqMG43R25aV1hhN0h4Tzd2bXozZXVlM0lWTkNsWTdkV3lwVnN6bUNWZlBRMXRWRm80SkdxYndhWDhhbGtVVW1GOEZhS2wyNEZTYW1vN1pacWVZeFVQSTJuTjVIbkJsa3VvODQ1cit6emZES3U1Vkhwd0p2b2FtbzJKVjdTSDBIUlh0RW11VW1kcXYyL3dDQzJleHJUcWZjZEcwMmlUV3V2OEkvSjkvdzNkalZMSVYyb05MdEl5eXJ1T0RXekZUd1M5RU9xcWhOdm5CNUYraWFLVW4yUTZxcVZMOUR5TDlINU5sU2svUmNENEhKZ2ZBeFIwdExSaENhNWp6WlU4aDNidTBqSzRkeWRXT2xTVE4xMklrYWxFa01jMm02Z2FGekpaRE1vd29QTVR0Tm9sSnVyYUptcVBDSldZbDZHOUdTcXZHU21qbVpadk5TWXBNWVBGcDNGdTJlVklodEhpVjVUd1VLdDh6eHJkYmh5dnFleEtjajN1ZDJLcGFRWlRaNEVVSmtGSit4RGZZOXltT2xrTkdocGVqOGEwMVArdjhBOGpLVUw5RUpRTndTMUZtWTFqL2JOdEtCWVNGSW9FS1NlZUJkeFBvbjlqRndzWlZ0YXBlY0pjeUVOU3YwZWFWWmtweGc4VGtkTFg5WkxVUlpDRjdrSHVVcFBXZnV5ZXFIQXJ3dVorSFpmSE5pcGJoUjhtakhOUHdiOGVHTE04WE5Xd0xQT3lQY1h1ZTMrbnVVKy8zWlE3S3pLZTZLcDZzaWszcUVvOWNmWkZXN0IrT2hyVFNiTVZTWGhqVWpVbUJkL3dEUmlraVJSVWlHMSt6VkMzSHJOb1F4Wk5iSjAwc2RVWWFITGZ3WklTK0IxcFpnZHFWeWtnOUQ4cVhNM3FWcVBxa0t2a053T0ZaMGpxcnhaMnphU3RiOHJwMEhHbnQxSmJ3TlVVNkRwcXBoREVVOXh4eU4wcHA2RzZ4cDZzaEVQOWoydGVlU1JGZENnZE9xaVVRaERNa1hyMnllYW94MkdtL2twb25QSWRPdnVOT0o3c2RxV09IeU41amI1RUV0WlEzWHFoVjBqMmRmZEUxdkE5cFQwaEc2N08yTFFlRWRibVg2Rkc2L0VodHFIMVp2cXBhNnlPMU1GVWREemZCTkluVXNtaEJFRGNHVFFjVWt4TDdqUyt5YWxnaDJSbTNoVXQvWnY3UllKVFRuWDBHcktuYXpIb09taVhQVWROVXljeU9HYjdwRlVtNjZXMTZua01MZzhHRDhiYzh4cXJVcWRrcTFKS1dTV2lWVHd1eW0rN1ZsRVRoNkhpWjRkT0QveEFBbkVRQURBQU1BQVFNRUF3RUJBUUFBQUFBQkFnTUFCQkVTQlNFeEV4UWlJeEF5TXdaQlF2L2FBQWdCQXdFQkJRQlpvaStuMWhJZXBiV3BUUlQvQUZPYy9Ya3hsZmlIdWJEbE03blFBR0J3NXdjWEVIUm5SL0RPTUxjWCtvMTZ6TTdWRmhJL2xQOEEwT2ZFOFJUbE1ndjUyNlgrbm5nYzhEZ1E1enhVRHVJQmc0cEtFbjZSNTREZ1E0Z0gxRWFRcHIrWVcyczBETW55bXZIT0h2Z0ErS0xZL2Vhdy9ZK3ZZdDlyWTROTFpPRFEyY0duWTVmV3JCVVBRcHdIdUNGNkQ3UFpBbnEyY2ZZN2lsdEhZT0RSSkdzZUo2alQ2aFFmblA1T1NQZ1Bycmd1RGxUNUhUSGFQdFVHZmVOaWI5T0p2dnhkbnJiOVRzcUFRQmlaQmlpTFp5cTEyUUMyMGNJMmpqUTNXeVBSSGFKWkUrVUhEM29BSlR3T0tqQTB3VjNaTk03K3lnMVBWT0dPN0NjTlhmc2llbmVzc050ZGpXSEIvQzVEUm8wWmFoUUtuaU9jeU1wbEpSbGtVUDBySUFxODhobi9BTXA3SjVuQXo5YzlOS21jSnZlYUI5blBwMG9zL09TcTFrRldlemtZbzdpRHVhSVAyNEdjT2R5WllLaEl5SjVQWlBXNGVybnhOT2VDOHp4Vjhxb1ZuY20zQVFxQVkzdE9ZNnVGVnpnNE1UMkdyZVV0U2RaMVh1QVpKZjFxbnNlSnFjZWlkSVpjK1VsL20wYStFOWUzZGdFVzhKdlp5RkNNVG56aUh4SFFRcGJxbm1GaDVmVUdUbzFTanZObytxN2NnUFd3QnIrc2JHMW10UGV2SXE0bFZXR0hCN1lEK3VIc2k3bXo0UTlaMFo2dnFORWZjZ2NaazRwSE5UYmxETm4xQ0ZVaHRTUm4zTlpsRkVSRldiR2FRSkU1UWZtY3pXKzJlUDhBekV0bExlbVc5VTI5dDNkemRLRTkvSEIzdzFtUXBxUUZxVVZOZXZxUFh6U240cnNxTy84QWsvbnhJQ3FUbWtQZkhKRlo5NzVUSitvbUJ4aU1BZjhBay9WdFRSbE8rcERhcDdzbnk0Q2tZUGl4L1JDNGtWb0xpOFNKdzZFcXhaL2ZpaGpsRUtNb0pFcStEamdEL3dDa3orVGM2ZzRlbkZIQk9qSXkrc1BIWitncEU0Y2Fxb3BHRCtwQWVieVJsVi90OHRzMVhKamcydjc5OWd3Qm8zNWcrMnZOSGZnQUxnc2k4Tk9oMTc0alA2cnJhNXVkclV1aEd6QTRMdzdlc2xZWjhCSk5TWTEzSVRVYzRtbUZnUGJObHd6OEhFOGN0dzBIeHJPRWIvem5hVC9zNmtzTVJUbE9mU2dSOXZyVThHdDZWRFdyOWpBWmJYdWpML0dqdHBKVzI2MUJhellFSjFsOTgyWktyaVZTRWxYcGxRbFlXSTE0aDM0QXZseWtoMHVwODFWc1FjRFBGUlBaQTF0YXU0S2EyNVZHYmYyRUd6NmpmWWpJOVZ2alc5ckw4cU9EMCs4akU3RTVVRHBTOUtvb0hYeWt2cGlOQkxPOVg3cFdZKzdSL3NXNFNTY3JsbS9HSlU2c2pQdGRxU0pHSTJkYlkxb3hTSjluOWhwanRqeVpRK2VRSlNHMnlPMmdLbW5EWThNeFozcFE2cnJsTDBmVjFDbk1qOHQvYkh6WUFGTmZjYW1yQm90T1JKYUxxa3Rpbm5LT1YrTkEvdHI0ZUtQd2J0M21ETVVHc1RCcG96UGFWSlZvckxVWGR3WktzbzY4MFoxSGpISDlseWh6WVVGOUI1cFdGdFFKS2dkblQ4UllHV3E0ZExIUFR4KzdaUGdFb3paU0MzVjJaS2VkRGlzeXNyTmF0d3lNaHJSUlBZQUFwMDdNems5cVNHZTlDMzhVOXMyY1pPNGdLaXVvMlNFVlY2cU0wejBXK2RhbHAydWR2WXlUYmhUNm05d09SbmtjQnhTT2M4c2pQd3dOd0lUaWN4RFRpbW9ZMjJUaHBiUE55Q081T1l5ZW1rcHA5R0s3ZFVxZFhnRi9rRjBMVnZpVnJQQmF4em5QNUdBOE02Y3hxZVUrOHlZR0t6SWVwbm1yTDRFSndEQW95RXcyZi8vRUFEQVJBQUlDQVFJRUJRTURCUUVCQUFBQUFBQUJBaEVoRURFREVsRnhJQ0pCWWFFVGdaRXlzZEVFTThIaDhEQnkvOW9BQ0FFREFRWS9BT1dEdExZbjlUMkpRaHZpc0xyMzZEOEdkS0hyYTBXajhPTjlGRmJpaFdhRTB0dEg0cUgvQU9ONlllbFBwZ2JtNkZ5ckowc3BqMHdiYUs5OUcxcFNNb3BIbjhDYVpiTUdIOGo1bnBDWFlpMWpSNk9Sc2JNdlNsRTJQMG42UmVYSWsxVmVCVXVoaUJVZUZmNE04TDVSNWVGOG8vcy9LSTEwRmVqMGFSc1pXajVPSGNlcHo4UGh0cCt0R2VFL2orUjhUamNOcUs3ZnlMaWNQaHRwNVdZL3lXdUY4eC9rditvaTRyN0QxakpQZEptK3R5M28ydjhBQXV4blZEZW1kSFR5emxqeEdrdmMvdVA4alVwTnIzYk9WU2RkMlltMTkyZlQ0cmJYcGJmZ2oyWGdWRnNYZ1F4MlVtTk4yTlBaRzJqYUUvQTlJT1Q5RVhCM3JuVG5yS1J6T2lxMFF6bXAvZ3ZsZGRtTkZwbURKUmt3UG0wWlJTTU9tVTNhOXo5SHljdkI0RGsxMHljMCtFNHZvMFZUTGxveERmdWZUVGRkQ1BDbGRyMkp6OUd6WWNJeXBtWnNxVHM4cUh6RFQ3Yk0yZGRoM2d5ZVZqYjBhbkh6ZFRpUzRLZXkvY2ZHNGI1MUJPTFRmdnZYY3BxdndWSDB5Wi83SXhFbyt0bEtTWGQwTlRhZmJLSnVPdzJ5VEg5aDl5dlprcjZJbXZmUnJUTDF0SEVYRWVYUStOL1RPdWE3Uzk3ZlU1bTcwcS8rdlJEdnJweXAwTkoyTmRDVmordzY2bEwzSDJKTHExcExSbDY0WkdFWmN0TE1uc3NQb05zdW4rVENyYlJGTlhrd3ErUlBmOWtWRjNla2hqK3cyTit4SzF0V2pTM01qUm5Ybmwvb2JpcnRtNk1OZmtjRTgydGJqMTFsajA2YVNHUyt3KzU5aWYyMGxvOVpkaGRoOHh5Y1JPL2FuK3hjSXk5c0M0azROWnk2MWxHL05ldzlrYmovQVBuU1RTSGo0RytWbHBQOERWUFlsenJvVWhqWTZOaWg4MGtocGJseFMrVHl4TGY3aTRjbTZiV2pKTkZudXlYQmJ2RkRqSm1NcXh4S1RNc2Q1RXlvWkc5SzFhT2JselcvVVhYN2pRdWVWV0p4bmJ0YU1taGU0NU00a242amFGSmJMY2I5QzQ3SDBvT3ZWc3VFbllwTERUcGpUMWVyb1hBYXhGT24zRktLK0NYRXJEd3kvd0RCYXV1eTFtSzl5bHNSVWZVeVVuaGloRE43RDRVMVRXK1Q2a096S2dzbjA1UE83TFYva3ZSNnNmTzZWRktXTzV4TFhsZnFMTnJvc2ppNHVsaXEvd0FpYUtKa2I2bjZma3FZNHhlRGNzY3JvcTdQSzlodXpNaW02UDhBUnl4dSsybDZZMHk2Vi9BK0h3bmthYWFyYjNMNmpKUGg3bHpwVVdxSHNQMzhGRkVucGRubE9aTlgzTnpmd05UOHg5Vkt2U2pBaGpsRjBVNUdKRFRsNGNhU3JXNDRaZkwzSHlvOHl5VnIvOWs9IiBpZD0iYyIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiLz48L2RlZnM+PC9zdmc+", + "description": "Displays the latest efficiency telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'efficiency', label: 'Efficiency', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Efficiency\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 30;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"horizontal\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"trending_up\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":60,\"color\":\"#F89E0D\"},{\"from\":60,\"to\":80,\"color\":\"#3B911C\"},{\"from\":80,\"to\":null,\"color\":\"#2B54CE\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"36px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":60,\"color\":\"#F89E0D\"},{\"from\":60,\"to\":80,\"color\":\"#3B911C\"},{\"from\":80,\"to\":null,\"color\":\"#2B54CE\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:aG9yaXpvbnRhbF9lZmZpY2llbmN5X2NhcmRfYmFja2dyb3VuZC5wbmc=:aG9yaXpvbnRhbF9lZmZpY2llbmN5X2NhcmRfYmFja2dyb3VuZC5wbmc=;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Horizontal efficiency card with background\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"%\",\"decimals\":0,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "productivity", + "effectiveness", + "performance", + "capability" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_ellipse_tank.json b/application/src/main/data/json/system/widget_types/horizontal_ellipse_tank.json new file mode 100644 index 0000000000..35df7fd221 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_ellipse_tank.json @@ -0,0 +1,36 @@ +{ + "fqn": "horizontal_ellipse_tank", + "name": "Horizontal ellipse tank", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Widget indicates the level of liquid in Horizontal ellipse tank.", + "descriptor": { + "type": "latest", + "sizeX": 4, + "sizeY": 4, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.liquidLevelWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.liquidLevelWidget.update();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n}\n\nself.actionSources = function() { \n return { \n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false \n } \n };\n}", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-liquid-level-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-liquid-level-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.floor(Math.random() * 101);\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"tankSelectionType\":\"Static\",\"selectedShape\":\"Horizontal Ellipse\",\"shapeAttributeName\":\"tankShape\",\"tankColor\":{\"type\":\"range\",\"color\":\"#242770\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E73535DE\"},{\"from\":20,\"to\":null,\"color\":\"#242770\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E73535DE';\\n }\\n}\\nreturn '#242770';\"},\"datasourceUnits\":\"%\",\"layout\":\"percentage\",\"volumeSource\":\"static\",\"volumeConstant\":500,\"volumeAttributeName\":\"volume\",\"volumeUnits\":\"L\",\"volumeFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"volumeColor\":\"rgba(0, 0, 0, 0.18)\",\"units\":\"%\",\"widgetUnitsSource\":\"static\",\"widgetUnitsAttributeName\":\"units\",\"liquidColor\":{\"type\":\"range\",\"color\":\"#7A8BFF\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E27C7CDE\"},{\"from\":20,\"to\":null,\"color\":\"#7A8BFF\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FF0000DE\"},{\"from\":20,\"to\":null,\"color\":\"rgba(0,0,0,0.87)\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FF0000DE';\\n }\\n}\\nreturn '#000000DE';\"},\"showBackgroundOverlay\":true,\"backgroundOverlayColor\":{\"type\":\"range\",\"color\":\"#FFFFFFC2\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#FFEFEFDE\"},{\"from\":20,\"to\":null,\"color\":\"#FFFFFFC2\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FFEFEFDE';\\n }\\n}\\nreturn '#FFFFFFC2';\"},\"showTooltip\":true,\"showTooltipLevel\":true,\"tooltipUnits\":\"%\",\"tooltipLevelDecimals\":0,\"tooltipLevelFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipLevelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.76)\",\"rangeList\":[],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"showTooltipDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":3,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Liquid level\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"titleFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1.5\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"showTitleIcon\":false,\"titleIcon\":\"water_drop\",\"iconColor\":\"#5469FF\",\"decimals\":0,\"enableDataExport\":false,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" + }, + "externalId": null, + "tags": [ + "reservoir", + "container", + "vessel", + "storage unit", + "cistern", + "canister", + "vat", + "basin", + "repository", + "bin", + "hopper" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_flooding_level_card.json b/application/src/main/data/json/system/widget_types/horizontal_flooding_level_card.json new file mode 100644 index 0000000000..8f35d8ad76 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_flooding_level_card.json @@ -0,0 +1,37 @@ +{ + "fqn": "horizontal_flooding_level_card", + "name": "Horizontal flooding level card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAn1BMVEUAAADf39/f39/g4ODg4ODf39/////k5OQjTMfg4OCRpePk6fisrKzx8fHIyMg9PT11j9zOzs7y9PuQkJD09PT5+fmtvOqsvOpYWFjI0vHV1dXb29u3t7c+Ys4wV8vCwsK8vLy6x+5aedXn5+dohNlmZmaDmt+enp5aeNUrKyt0dHSfsedMbtGdnZ0/Ys6CgoJLS0vW3fTW3fUxV8pKSkpA7yNPAAAABnRSTlMAIEDfv1C6kOEmAAADcUlEQVR42u3a53KbQBSGYUV2DivKUgQSRTR1F9lOuf9ry9kVUkjGQyyT8WDN91iGBVx4h93xD3kEAAAAAAAAAAAAAAAAAAAAAAAAAAAA8JG+fHo6Y3xjfHaTm6/cYUT06YXGeHQb0hWIbkcTugoThAwMQoYGIUODkKFByNC8FmI9egm12dOpLW1quaep0z40qEV/aX/9Q3IhRD636CzeydSekqNu1+AAx3HpwCM11CddQ503+Ihf3E29BA9m67fXM+0dIVtxtJ6fQ2zDsadGvIlpunGl8bK5I5fce9dWQx0yjV0ZGwfbjn9WPUPmguXnkqXQLg8xxVn+0IRs7iVPrx3F6R3JDQ9c/WFPn3moQ+7s5/vDLjZeprtNvxBLiOS7KZbUeBLveyKJOFvu6yZEqnVyUCHuXyH2KWQhDeclJnehZmG/B+LxthAWHYmcfgusIEhqvvRtlnSHWHkrhKgdYvCD4an1M+XBXROihjrk4G4kxTbZbrzrF+IJNQ/WYk5aIPZktabLWt1grebbY1dI0HQUa978COhPjv7Um4ZRSfd0sbXvFzJvtto38aMQwgvO896crTli9lCIuiNkK/bNSit4n9C/OPZzSp36hxTbbS6W5yeiHpKw1K2aHSHW3BKM+z3ebamv/iEUBETWacmYegEJoYadIexJzUGimV4kvfUPYXrJXBaSLAuhrWvB5gF9uK1e7EuRnP4eWvqwvizkSazVItmrtV54efPTQnpF6Px9IqT/IRFLPZfUjNJd5numVh0QJbOEyJrNeFhbpCx0DL+cjLQoomjiqxMTivh4Za8oyrLjBT7RKykXnvmo1ue8SVKHHl0WQsGsJm6prSboFJKmfialz6OSyqysokXpp6n0K8evIiJ/FVZVRqGvL/iyovez1JzYBipkzYffczXTgwtDtoUQuae/dalWu/U7ZBFKf+UfQ1ZpxmM+t8r8UuqQSZZxiKxWMuTcMqQeAis47luHr+laI/t6novCTDxRbBOzSEix/az0F6ksQ/VE/CorF+FC+nzO4Rv3SYVE6omsKltdSMuSPpgOuVi26riYyozeYBAhUdR1MaS3GETIECFkaBAyNAgZGoQMzWR0ewVvsxOFt6OxcQVvtEfGeDT6ejMxPrub8TX9Uw0AAAAAAAAAAAAAAAAAAAAAAAAAAADAB/kFV7oA4xEmmqQAAAAASUVORK5CYII=", + "description": "Displays the latest flooding level telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'flooding', label: 'Flooding level', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flooding level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 2 - 1;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 5) {\\n\\tvalue = 5;\\n}\\nreturn value;\\n\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"flood\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"#234CC7\"},{\"from\":1,\"to\":3,\"color\":\"#F36900\"},{\"from\":3,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"#234CC7\"},{\"from\":1,\"to\":3,\"color\":\"#F36900\"},{\"from\":3,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal flooding level card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"m\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "flood", + "flooding", + "water height", + "flood depth", + "flood stage", + "inundation level", + "water rise", + "overflow level", + "flood peak", + "high water mark" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_flooding_level_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_flooding_level_card_with_background.json new file mode 100644 index 0000000000..589b3cd7e5 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_flooding_level_card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "horizontal_flooding_level_card_with_background", + "name": "Horizontal flooding level card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest flooding level telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'flooding', label: 'Flooding level', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flooding level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 2 - 1;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 5) {\\n\\tvalue = 5;\\n}\\nreturn value;\\n\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"flood\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"#224AC2\"},{\"from\":1,\"to\":3,\"color\":\"#F77410\"},{\"from\":3,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"#224AC2\"},{\"from\":1,\"to\":3,\"color\":\"#F77410\"},{\"from\":3,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal flooding level card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"m\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "flood", + "flooding", + "water height", + "flood depth", + "flood stage", + "inundation level", + "water rise", + "overflow level", + "flood peak", + "high water mark" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_flow_rate_card.json b/application/src/main/data/json/system/widget_types/horizontal_flow_rate_card.json new file mode 100644 index 0000000000..5c1f4d6a65 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_flow_rate_card.json @@ -0,0 +1,32 @@ +{ + "fqn": "horizontal_flow_rate_card", + "name": "Horizontal flow rate card", + "deprecated": false, + "image": "tb-image:aG9yaXpvbnRhbF9mbG93X3JhdGVfY2FyZC5zdmc=:aG9yaXpvbnRhbF9mbG93X3JhdGVfY2FyZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjU2IiB5PSI1MiIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjU1IiB4PSIuNSIgeT0iNTIuNSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjEiIHJ4PSIzLjUiLz48ZyBmaWx0ZXI9InVybCgjYSkiPjxyZWN0IHdpZHRoPSIxODQiIGhlaWdodD0iNDAiIHg9IjgiIHk9IjYwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii44IiByeD0iNCIvPjxwYXRoIGZpbGw9IiMzRkE3MUEiIGQ9Ik0yOS4xIDcxLjVhMiAyIDAgMCAwLTEuOSAxLjVsLS44IDIuNWEzIDMgMCAwIDAtMSAuMmwtMS45LTItMS4zLTEuNEEyIDIgMCAwIDAgMjIgNzVsMS44IDJhMyAzIDAgMCAwLS40IDFsLTIuNi42LTIgLjVhMiAyIDAgMCAwIDIuNSAxLjVMMjQgODBsLjcuOC0uOCAyLjUtLjYgMmEyIDIgMCAwIDAgMi41LTEuNGwuOC0yLjVjLjQgMCAuNyAwIDEtLjJsMS45IDIgMS4zIDEuNGEyIDIgMCAwIDAgLjItMi44bC0xLjktMiAuMy0uNS4xLS41IDIuNi0uNiAyLS41YTIgMiAwIDAgMC0yLjQtMS41TDI5IDc3YTMgMyAwIDAgMC0uOC0uOGwuOC0yLjUuNi0yYTIgMiAwIDAgMC0uNiAwWk0yNi42IDc3YTEuNSAxLjUgMCAwIDEgMS4yLjggMS41IDEuNSAwIDEgMS0xLjMtLjhNMjAgODEuN2E3IDcgMCAwIDEtNCAxLjNoLTJ2MmgyYzEuNCAwIDIuNy0uMyA0LTEgLjguNCAxLjYuNyAyLjQuOGwuNS0xLjcuMS0uMmE3IDcgMCAwIDEtMy0xLjJaTTMyLjUgODNhMyAzIDAgMCAxLS42IDJIMzR2LTJoLTEuNVpNMjAgODUuN2E3IDcgMCAwIDEtNCAxLjNoLTJ2MmgyYzEuNCAwIDIuNy0uMyA0LTFhOC43IDguNyAwIDAgMCA4IDBjMS4zLjcgMi42IDEgNCAxaDJ2LTJoLTJhNyA3IDAgMCAxLTQtMS4zIDYuOSA2LjkgMCAwIDEtOCAwWiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik00MS43IDc1djEwSDQwVjc1aDEuN1ptNCA0LjR2MS40aC00LjR2LTEuNGg0LjVabS43LTQuNHYxLjRoLTUuMVY3NWg1Wm0zLjItLjVWODVoLTEuN1Y3NC41aDEuN1ptMS43IDYuOXYtLjJjMC0uNSAwLTEgLjItMS41bC43LTEuMmEzIDMgMCAwIDEgMS0uN2wxLjUtLjNjLjYgMCAxIDAgMS41LjMuNC4xLjguNCAxIC43LjQuNC42LjguOCAxLjJsLjIgMS41di4yYzAgLjUgMCAxLS4yIDEuNWwtLjcgMS4yYTMuMSAzLjEgMCAwIDEtMi41IDFjLS42IDAtMSAwLTEuNS0uMmEzLjYgMy42IDAgMCAxLTEuOC0ybC0uMi0xLjVabTEuNy0uMnYxLjJsLjQuNy41LjVjLjMuMi41LjIuOS4yYTEuNiAxLjYgMCAwIDAgMS4zLS43Yy4yLS4yLjMtLjUuMy0uOGwuMS0xdi0xbC0uNC0uOGExLjYgMS42IDAgMCAwLTEuNC0uN2MtLjMgMC0uNSAwLS44LjJsLS41LjVjLS4yLjItLjMuNS0uMy44bC0uMSAxWm04LjggMi4yIDEuNy01LjhoMWwtLjIgMS43LTEuNyA1LjdoLTFsLjItMS42Wm0tMS01LjggMS40IDUuOFY4NWgtMWwtMi03LjRoMS42Wm01LjQgNS43IDEuMy01LjdINjlMNjcgODVoLTF2LTEuN1ptLTEuNC01LjcgMS43IDUuNy4yIDEuN2gtMUw2NCA3OS4zbC0uMy0xLjdoMVptMTEgMS40djZoLTEuNnYtNy40aDEuNlY3OVptMi4zLTEuNFY3OWEzLjIgMy4yIDAgMCAwLTEuNCAwIDEuNCAxLjQgMCAwIDAtLjkgMWwtLjEuNmgtLjRsLjEtMS4yLjUtMWMuMS0uNC40LS42LjYtLjhhMS45IDEuOSAwIDAgMSAxLjMtLjJoLjNabTUuMiA2VjgwbC0uMS0uN2ExIDEgMCAwIDAtLjUtLjRsLS43LS4yLS43LjEtLjQuNGMtLjIuMi0uMi4zLS4yLjVoLTEuNmMwLS4zIDAtLjUuMi0uOC4xLS4zLjMtLjUuNi0uN2EzIDMgMCAwIDEgMS0uNSA0IDQgMCAwIDEgMS4yLS4yYy42IDAgMSAwIDEuNS4yLjQuMi44LjUgMSAuOS4yLjQuNC44LjQgMS40djQuMmMwIC4zLjIuNS4zLjd2LjFoLTEuN2EzIDMgMCAwIDEtLjItLjd2LS44Wm0uMy0zLjF2MWgtMS4ybC0uOC4xLS42LjNhMSAxIDAgMCAwLS41LjljMCAuMiAwIC40LjIuNSAwIC4yLjIuMy40LjRsLjYuMWExLjggMS44IDAgMCAwIDEuNS0uN2wuMi0uNi42LjgtLjMuNmEzIDMgMCAwIDEtLjUuNiAyLjYgMi42IDAgMCAxLTEuOC42Yy0uNSAwLTEgMC0xLjMtLjMtLjQtLjEtLjctLjQtLjktLjdhMi40IDIuNCAwIDAgMS0uMS0yLjJsLjctLjggMS0uNCAxLjUtLjJoMS4zWm02LjctMi45djEuMmgtNC4xdi0xLjJoNC4xWm0tMy0xLjhIODlWODNsLjEuNS4zLjJoLjRhMi41IDIuNSAwIDAgMCAuNiAwVjg1YTQuMiA0LjIgMCAwIDEtMSAuMWMtLjUgMC0uOCAwLTEuMS0uMi0uMy0uMS0uNS0uMy0uNy0uNmwtLjItMS4ydi03LjNabTcuOCA5LjNjLS41IDAtMSAwLTEuNS0uMmEzLjMgMy4zIDAgMCAxLTEuOC0yIDQgNCAwIDAgMS0uMi0xLjN2LS4zYzAtLjYgMC0xLjEuMi0xLjZzLjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEtLjhsMS40LS4yYy42IDAgMSAwIDEuNC4ybDEgLjhjLjMuMy41LjcuNiAxLjFsLjIgMS41di43aC01Ljd2LTEuMmg0di0uMWwtLjEtLjktLjUtLjYtLjktLjJjLS4zIDAtLjUgMC0uNy4yLS4yIDAtLjQuMi0uNS41bC0uNC43djIuM2wuNC43LjcuNS44LjFhMi4zIDIuMyAwIDAgMCAxLjktLjlsLjkuOS0uNy42Yy0uMi4yLS41LjQtMSAuNS0uMy4yLS43LjItMS4yLjJaIi8+PHBhdGggZmlsbD0iIzNGQTcxQSIgZD0iTTExNC42IDg1LjFWODdoLTkuNXYtMS42bDQuNi01IDEuMi0xLjUuNi0xLjJhMi45IDIuOSAwIDAgMCAwLTIuMWMtLjItLjQtLjUtLjctLjgtLjgtLjMtLjItLjctLjMtMS4xLS4zLS42IDAtMSAwLTEuNC4zLS4zLjItLjYuNS0uOCAxLS4yLjMtLjIuOC0uMiAxLjNoLTIuNGMwLS44LjItMS42LjYtMi4zLjMtLjYuOS0xLjIgMS42LTEuNi43LS40IDEuNi0uNiAyLjYtLjZzMS44LjEgMi40LjVjLjcuMyAxLjIuNyAxLjUgMS4zLjQuNi42IDEuMy42IDIuMSAwIC40LS4xLjktLjMgMS4zLS4xLjUtLjMuOS0uNiAxLjNhMjMuMiAyMy4yIDAgMCAxLTIuMSAyLjZsLTMgMy40aDYuNVptMTEuMi02LjRWODFjMCAxLjEtLjEgMi0uMyAyLjlhNSA1IDAgMCAxLTEgMS45Yy0uNC41LS45LjgtMS40IDFhNS41IDUuNSAwIDAgMS0zLjUuMSAzLjggMy44IDAgMCAxLTIuMy0xLjhjLS4yLS41LS41LTEtLjYtMS43bC0uMi0yLjR2LTIuM2MwLTEuMSAwLTIgLjMtMi44YTUgNSAwIDAgMSAxLTJjLjQtLjQuOS0uOCAxLjQtMSAuNi0uMiAxLjItLjMgMi0uMy41IDAgMSAwIDEuNS4yYTMuNyAzLjcgMCAwIDEgMi4zIDEuOGMuMi41LjUgMSAuNiAxLjhsLjIgMi4zWm0tMi4zIDIuN3YtM2wtLjEtMS41LS4zLTEuMi0uNS0uNy0uNi0uNC0uOS0uMWMtLjMgMC0uNiAwLTEgLjJsLS43LjYtLjQgMS4yLS4yIDEuOHYzbC4xIDEuNi4zIDEgLjUuOS42LjQuOS4xYy4zIDAgLjcgMCAxLS4ybC43LS43LjQtMS4yLjItMS44Wm0xMi0yLjhWODdoLTIuM1Y3Ni40aDIuMmwuMiAyLjJabS0uMyAyLjdoLS44YzAtLjcgMC0xLjQuMy0yIC4yLS42LjQtMS4yLjgtMS42LjMtLjUuOC0uOCAxLjMtMSAuNS0uMyAxLjEtLjUgMS44LS41LjUgMCAxIC4xIDEuMy4zLjQgMCAuOC4zIDEgLjYuNC4zLjYuNy44IDEuMmwuMiAxLjd2N2gtMi40di02LjhjMC0uNiAwLTEtLjItMS4yLS4xLS4zLS40LS41LS42LS43bC0xLS4yYy0uNSAwLS45LjEtMS4yLjNsLS43LjctLjUgMS0uMSAxLjJabTYuNi0uNi0xLjIuMi4zLTEuOC44LTEuNWEzLjcgMy43IDAgMCAxIDMtMS40Yy42IDAgMSAuMSAxLjUuMy40LjEuOC4zIDEuMS42bC43IDEuMy4yIDEuOFY4N0gxNDZ2LTYuOGMwLS42LS4xLTEtLjMtMS4zLS4xLS4zLS4zLS41LS42LS42bC0xLS4yLTEgLjJhMiAyIDAgMCAwLS43LjZsLS40LjgtLjEgMVptMTAuNC00LjhoMS41bC40LS40YS45LjkgMCAwIDAtLjEtMS4xYy0uMi0uMi0uNS0uMy0uOS0uMy0uMyAwLS42IDAtLjguMi0uMi4xLS4zLjMtLjMuNmgtMS44YzAtLjUuMS0uOS40LTEuMi4zLS40LjYtLjYgMS0uOGE0LjQgNC40IDAgMCAxIDMgMGMuNS4yLjguNCAxIC44LjMuMy41LjcuNSAxLjIgMCAuMy0uMi43LS40IDEtLjIuMi0uNS41LTEgLjZhNCA0IDAgMCAxLTEuNC4zaC0xLjF2LS45Wm0wIDEuM3YtLjhoMS4xYTUgNSAwIDAgMSAxLjYuMmMuNC4xLjcuMyAxIC42LjIuMy4zLjcuMyAxLjEgMCAuNS0uMSAxLS40IDEuMy0uMy4zLS43LjYtMS4xLjdhNC41IDQuNSAwIDAgMS0zIDBjLS41LS4xLS45LS40LTEuMi0uN2EyIDIgMCAwIDEtLjQtMS40aDEuOGMwIC4zIDAgLjUuMy43LjIuMi42LjMgMSAuM3MuNy0uMS45LS4zYy4yLS4yLjMtLjQuMy0uNyAwLS4zIDAtLjUtLjItLjZhMSAxIDAgMCAwLS40LS4zbC0uOC0uMWgtLjhabTEyLjItNC40TDE1OSA4OC4ySDE1N2w1LjYtMTUuNGgxLjhabTQtLjh2MTVoLTIuM1Y3MmgyLjRabS0uMyA5LjNoLS44YzAtLjcuMS0xLjQuMy0yIC4yLS42LjUtMS4yLjktMS42YTMuNyAzLjcgMCAwIDEgMy0xLjVjLjUgMCAxIC4xIDEuNC4zLjQuMS43LjMgMSAuNi40LjQuNi44LjcgMS4zLjIuNS4zIDEgLjMgMS44Vjg3aC0yLjR2LTYuOGMwLS41IDAtMS0uMi0xLjItLjEtLjMtLjQtLjUtLjYtLjdsLTEtLjJjLS41IDAtLjkuMS0xLjIuM2wtLjguNy0uNSAxLS4xIDEuMlptMTEuNi0yLjlWODdoLTIuM1Y3Ni40aDIuMmwuMSAyWm0zLjMtMnYyLjJhNC42IDQuNiAwIDAgMC0yIDAgMiAyIDAgMCAwLTEuMyAxLjNsLS4yIDFoLS41YzAtLjYgMC0xLjIuMi0xLjggMC0uNi4zLTEgLjUtMS41YTIuNyAyLjcgMCAwIDEgMi45LTEuM2guNFoiLz48L2c+PGRlZnM+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjE5NiIgaGVpZ2h0PSI1MiIgeD0iMiIgeT0iNTQiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjMiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTQ3Nl8zMTk1Ii8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xNDc2XzMxOTUiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48L2RlZnM+PC9zdmc+", + "description": "Displays the latest flow rate telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'flowRate', label: 'Flow rate', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flow rate\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"horizontal\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:hydro-power\",\"iconColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#305AD7\"},{\"from\":10,\"to\":30,\"color\":\"#3FA71A\"},{\"from\":30,\"to\":50,\"color\":\"#F36900\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#305AD7\"},{\"from\":10,\"to\":30,\"color\":\"#3FA71A\"},{\"from\":30,\"to\":50,\"color\":\"#F36900\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Horizontal flow rate card\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"m³/hr\",\"decimals\":0,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "liquid", + "fluid", + "flow rate", + "fluid dynamics", + "velocity", + "mass flow", + "volume flow" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_flow_rate_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_flow_rate_card_with_background.json new file mode 100644 index 0000000000..d7e8549216 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_flow_rate_card_with_background.json @@ -0,0 +1,32 @@ +{ + "fqn": "horizontal_flow_rate_card_with_background", + "name": "Horizontal flow rate card with background", + "deprecated": false, + "image": "tb-image:aG9yaXpvbnRhbF9mbG93X3JhdGVfY2FyZF93aXRoX2JhY2tncm91bmQuc3Zn:aG9yaXpvbnRhbF9mbG93X3JhdGVfY2FyZF93aXRoX2JhY2tncm91bmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSI1NiIgeT0iNTIiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjQ0IiB4PSI2IiB5PSI1OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48cGF0aCBmaWxsPSIjM0I5MTFDIiBkPSJNMjcuMSA3MS41YTIgMiAwIDAgMC0xLjkgMS41bC0uOCAyLjVhMyAzIDAgMCAwLTEgLjJsLTEuOS0yLTEuMy0xLjRBMiAyIDAgMCAwIDIwIDc1bDEuOCAyYTMgMyAwIDAgMC0uNCAxbC0yLjYuNi0yIC41YTIgMiAwIDAgMCAyLjUgMS41TDIyIDgwbC43LjgtLjggMi41LS42IDJhMiAyIDAgMCAwIDIuNS0xLjRsLjgtMi41Yy40IDAgLjcgMCAxLS4ybDEuOSAyIDEuMyAxLjRhMiAyIDAgMCAwIC4yLTIuOGwtMS45LTIgLjMtLjUuMS0uNSAyLjYtLjYgMi0uNWEyIDIgMCAwIDAtMi40LTEuNUwyNyA3N2EzIDMgMCAwIDAtLjgtLjhsLjgtMi41LjYtMmEyIDIgMCAwIDAtLjYgMFpNMjQuNiA3N2ExLjUgMS41IDAgMSAxLTEuNCAyLjIgMS41IDEuNSAwIDAgMSAxLjQtMi4yTTE4IDgxLjdhNyA3IDAgMCAxLTQgMS4zaC0ydjJoMmMxLjQgMCAyLjctLjMgNC0xIC44LjQgMS42LjcgMi40LjhsLjUtMS43LjEtLjJhNyA3IDAgMCAxLTMtMS4yWk0zMC40IDgzYTMgMyAwIDAgMS0uNSAySDMydi0yaC0xLjZaTTE4IDg1LjdhNyA3IDAgMCAxLTQgMS4zaC0ydjJoMmMxLjQgMCAyLjctLjMgNC0xYTguNyA4LjcgMCAwIDAgOCAwYzEuMy43IDIuNiAxIDQgMWgydi0yaC0yYTcgNyAwIDAgMS00LTEuMyA2LjkgNi45IDAgMCAxLTggMFoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNNDAuNyA3NXYxMEgzOVY3NWgxLjdabTQgNC40djEuNGgtNC40di0xLjRoNC41Wm0uNy00LjR2MS40aC01LjFWNzVoNVptMy4yLS41Vjg1aC0xLjdWNzQuNWgxLjdabTEuNyA2Ljl2LS4yYzAtLjUgMC0xIC4yLTEuNWwuNy0xLjJhMyAzIDAgMCAxIDEtLjdsMS41LS4zYy42IDAgMSAwIDEuNS4zLjQuMS44LjQgMSAuNy40LjQuNi44LjggMS4ybC4yIDEuNXYuMmMwIC41IDAgMS0uMiAxLjVsLS43IDEuMmEzLjEgMy4xIDAgMCAxLTIuNSAxYy0uNiAwLTEgMC0xLjUtLjJhMy42IDMuNiAwIDAgMS0xLjgtMmwtLjItMS41Wm0xLjctLjJ2MS4ybC40LjcuNS41Yy4zLjIuNS4yLjkuMmExLjYgMS42IDAgMCAwIDEuMy0uN2MuMi0uMi4zLS41LjMtLjhsLjEtMXYtMWwtLjQtLjhhMS42IDEuNiAwIDAgMC0xLjQtLjdjLS4zIDAtLjUgMC0uOC4ybC0uNS41Yy0uMi4yLS4zLjUtLjMuOGwtLjEgMVptOC44IDIuMiAxLjctNS44aDFsLS4yIDEuNy0xLjcgNS43aC0xbC4yLTEuNlptLTEtNS44IDEuNCA1LjhWODVoLTFsLTItNy40aDEuNlptNS40IDUuNyAxLjMtNS43SDY4TDY2IDg1aC0xdi0xLjdabS0xLjQtNS43IDEuNyA1LjcuMiAxLjdoLTFMNjMgNzkuM2wtLjMtMS43aDFabTExIDEuNHY2aC0xLjZ2LTcuNGgxLjZWNzlabTIuMy0xLjRWNzlhMy4yIDMuMiAwIDAgMC0xLjQgMCAxLjQgMS40IDAgMCAwLS45IDFsLS4xLjZoLS40bC4xLTEuMi41LTFjLjEtLjQuNC0uNi42LS44YTEuOSAxLjkgMCAwIDEgMS4zLS4yaC4zWm01LjIgNlY4MGwtLjEtLjdhMSAxIDAgMCAwLS41LS40bC0uNy0uMi0uNy4xLS40LjRjLS4yLjItLjIuMy0uMi41aC0xLjZjMC0uMyAwLS41LjItLjguMS0uMy4zLS41LjYtLjdhMyAzIDAgMCAxIDEtLjUgNCA0IDAgMCAxIDEuMi0uMmMuNiAwIDEgMCAxLjUuMi40LjIuOC41IDEgLjkuMi40LjQuOC40IDEuNHY0LjJjMCAuMy4yLjUuMy43di4xaC0xLjdhMyAzIDAgMCAxLS4yLS43di0uOFptLjMtMy4xdjFoLTEuMmwtLjguMS0uNi4zYTEgMSAwIDAgMC0uNS45YzAgLjIgMCAuNC4yLjUgMCAuMi4yLjMuNC40bC42LjFhMS44IDEuOCAwIDAgMCAxLjUtLjdsLjItLjYuNi44LS4zLjZhMyAzIDAgMCAxLS41LjYgMi42IDIuNiAwIDAgMS0xLjguNmMtLjUgMC0xIDAtMS4zLS4zLS40LS4xLS43LS40LS45LS43YTIuNCAyLjQgMCAwIDEtLjEtMi4ybC43LS44IDEtLjQgMS41LS4yaDEuM1ptNi43LTIuOXYxLjJoLTQuMXYtMS4yaDQuMVptLTMtMS44SDg4VjgzbC4xLjUuMy4yaC40YTIuNSAyLjUgMCAwIDAgLjYgMFY4NWE0LjIgNC4yIDAgMCAxLTEgLjFjLS41IDAtLjggMC0xLjEtLjItLjMtLjEtLjUtLjMtLjctLjZsLS4yLTEuMnYtNy4zWm03LjggOS4zYy0uNSAwLTEgMC0xLjUtLjJhMy4zIDMuMyAwIDAgMS0xLjgtMiA0IDQgMCAwIDEtLjItMS4zdi0uM2MwLS42IDAtMS4xLjItMS42cy40LS45LjctMS4yYTMgMyAwIDAgMSAxLS44bDEuNC0uMmMuNiAwIDEgMCAxLjQuMmwxIC44Yy4zLjMuNS43LjYgMS4xbC4yIDEuNXYuN2gtNS43di0xLjJoNHYtLjFsLS4xLS45LS41LS42LS45LS4yYy0uMyAwLS41IDAtLjcuMi0uMiAwLS40LjItLjUuNWwtLjQuN3YyLjNsLjQuNy43LjUuOC4xYTIuMyAyLjMgMCAwIDAgMS45LS45bC45LjktLjcuNmMtLjIuMi0uNS40LTEgLjUtLjMuMi0uNy4yLTEuMi4yWiIvPjxwYXRoIGZpbGw9IiMzQjkxMUMiIGQ9Ik0xMTYuNiA4NS4xVjg3aC05LjV2LTEuNmw0LjYtNSAxLjItMS41LjYtMS4yYTIuOSAyLjkgMCAwIDAgMC0yLjFjLS4yLS40LS41LS43LS44LS44LS4zLS4yLS43LS4zLTEuMS0uMy0uNiAwLTEgMC0xLjQuMy0uMy4yLS42LjUtLjggMS0uMi4zLS4yLjgtLjIgMS4zaC0yLjRjMC0uOC4yLTEuNi42LTIuMy4zLS42LjktMS4yIDEuNi0xLjYuNy0uNCAxLjYtLjYgMi42LS42czEuOC4xIDIuNC41Yy43LjMgMS4yLjcgMS41IDEuMy40LjYuNiAxLjMuNiAyLjEgMCAuNC0uMS45LS4zIDEuMy0uMS41LS4zLjktLjYgMS4zYTIzLjIgMjMuMiAwIDAgMS0yLjEgMi42bC0zIDMuNGg2LjVabTExLjItNi40VjgxYzAgMS4xLS4xIDItLjMgMi45YTUgNSAwIDAgMS0xIDEuOWMtLjQuNS0uOS44LTEuNCAxYTUuNSA1LjUgMCAwIDEtMy41LjEgMy44IDMuOCAwIDAgMS0yLjMtMS44Yy0uMi0uNS0uNS0xLS42LTEuN2wtLjItMi40di0yLjNjMC0xLjEgMC0yIC4zLTIuOGE1IDUgMCAwIDEgMS0yYy40LS40LjktLjggMS40LTEgLjYtLjIgMS4yLS4zIDItLjMuNSAwIDEgMCAxLjUuMmEzLjcgMy43IDAgMCAxIDIuMyAxLjhjLjIuNS41IDEgLjYgMS44bC4yIDIuM1ptLTIuMyAyLjd2LTNsLS4xLTEuNS0uMy0xLjItLjUtLjctLjYtLjQtLjktLjFjLS4zIDAtLjYgMC0xIC4ybC0uNy42LS40IDEuMi0uMiAxLjh2M2wuMSAxLjYuMyAxIC41LjkuNi40LjkuMWMuMyAwIC43IDAgMS0uMmwuNy0uNy40LTEuMi4yLTEuOFptMTItMi44Vjg3aC0yLjNWNzYuNGgyLjJsLjIgMi4yWm0tLjMgMi43aC0uOGMwLS43IDAtMS40LjMtMiAuMi0uNi40LTEuMi44LTEuNi4zLS41LjgtLjggMS4zLTEgLjUtLjMgMS4xLS41IDEuOC0uNS41IDAgMSAuMSAxLjMuMy40IDAgLjguMyAxIC42LjQuMy42LjcuOCAxLjJsLjIgMS43djdoLTIuNHYtNi44YzAtLjYgMC0xLS4yLTEuMi0uMS0uMy0uNC0uNS0uNi0uN2wtMS0uMmMtLjUgMC0uOS4xLTEuMi4zbC0uNy43LS41IDEtLjEgMS4yWm02LjYtLjYtMS4yLjIuMy0xLjguOC0xLjVhMy43IDMuNyAwIDAgMSAzLTEuNGMuNiAwIDEgLjEgMS41LjMuNC4xLjguMyAxLjEuNmwuNyAxLjMuMiAxLjhWODdIMTQ4di02LjhjMC0uNi0uMS0xLS4zLTEuMy0uMS0uMy0uMy0uNS0uNi0uNmwtMS0uMi0xIC4yYTIgMiAwIDAgMC0uNy42bC0uNC44LS4xIDFabTEwLjQtNC44aDEuNWwuNC0uNGEuOS45IDAgMCAwLS4xLTEuMWMtLjItLjItLjUtLjMtLjktLjMtLjMgMC0uNiAwLS44LjItLjIuMS0uMy4zLS4zLjZoLTEuOGMwLS41LjEtLjkuNC0xLjIuMy0uNC42LS42IDEtLjhhNC40IDQuNCAwIDAgMSAzIDBjLjUuMi44LjQgMSAuOC4zLjMuNS43LjUgMS4yIDAgLjMtLjIuNy0uNCAxLS4yLjItLjUuNS0xIC42YTQgNCAwIDAgMS0xLjQuM2gtMS4xdi0uOVptMCAxLjN2LS44aDEuMWE1IDUgMCAwIDEgMS42LjJjLjQuMS43LjMgMSAuNi4yLjMuMy43LjMgMS4xIDAgLjUtLjEgMS0uNCAxLjMtLjMuMy0uNy42LTEuMS43YTQuNSA0LjUgMCAwIDEtMyAwYy0uNS0uMS0uOS0uNC0xLjItLjdhMiAyIDAgMCAxLS40LTEuNGgxLjhjMCAuMyAwIC41LjMuNy4yLjIuNi4zIDEgLjNzLjctLjEuOS0uM2MuMi0uMi4zLS40LjMtLjcgMC0uMyAwLS41LS4yLS42YTEgMSAwIDAgMC0uNC0uM2wtLjgtLjFoLS44Wm0xMi4yLTQuNEwxNjEgODguMkgxNTlsNS42LTE1LjRoMS44Wm00LS44djE1aC0yLjNWNzJoMi40Wm0tLjMgOS4zaC0uOGMwLS43LjEtMS40LjMtMiAuMi0uNi41LTEuMi45LTEuNmEzLjcgMy43IDAgMCAxIDMtMS41Yy41IDAgMSAuMSAxLjQuMy40LjEuNy4zIDEgLjYuNC40LjYuOC43IDEuMy4yLjUuMyAxIC4zIDEuOFY4N2gtMi40di02LjhjMC0uNSAwLTEtLjItMS4yLS4xLS4zLS40LS41LS42LS43bC0xLS4yYy0uNSAwLS45LjEtMS4yLjNsLS44LjctLjUgMS0uMSAxLjJabTExLjYtMi45Vjg3aC0yLjNWNzYuNGgyLjJsLjEgMlptMy4zLTJ2Mi4yYTQuNiA0LjYgMCAwIDAtMiAwIDIgMiAwIDAgMC0xLjMgMS4zbC0uMiAxaC0uNWMwLS42IDAtMS4yLjItMS44IDAtLjYuMy0xIC41LTEuNWEyLjcgMi43IDAgMCAxIDIuOS0xLjNoLjRaIi8+PC9nPjxkZWZzPjxwYXR0ZXJuIGlkPSJhIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBwYXR0ZXJuQ29udGVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCI+PHVzZSB4bGluazpocmVmPSIjYyIgdHJhbnNmb3JtPSJtYXRyaXgoLjAwNTAyNTEzIDAgMCAuMDE3OTQ2OSAwIC0xKSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjU2IiB4PSIwIiB5PSI1MiIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMyIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMDc3XzI2NTg3Ii8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMDc3XzI2NTg3IiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PGltYWdlIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3QkRBQUlDQWdJQ0FRSUNBZ0lEQWdJREF3WUVBd01EQXdjRkJRUUdDQWNKQ0FnSENBZ0pDZzBMQ1FvTUNnZ0lDdzhMREEwT0RnOE9DUXNRRVJBT0VRME9EZzcvMndCREFRSURBd01EQXdjRUJBY09DUWdKRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNy93Z0FSQ0FDZkFNY0RBUkVBQWhFQkF4RUIvOFFBSFFBQUFnTUFBd0VCQUFBQUFBQUFBQUFBQmdjRUJRZ0NBd2tCQVAvRUFCc0JBQUlEQVFFQkFBQUFBQUFBQUFBQUFBRUNBQU1FQlFZSC85b0FEQU1CQUFJUUF4QUFBQUJPb1NVd3BKWlJodXlrOEU5aDF5ZGJENUpteXRzWDFoeHFYUFV4QVlIUUxBTHNpMUhuYlRsK001eldQTG9hWXN3ZFhZQktwZGFiRWlFWlp3YzVMNHcrSU9tbDZaMlFCS3JtYlBjVkNWQ3dnWmRQTlduNGRLV1VUekV2R3I2eVBpN1JUdDU4b1R0aFdPYldDbmpYOFdTUnhNdTREaVNVcG9BQUpXQjYyYkxqYUYxT1FYcjBOVXlFejI2QjAwS3VwODhCdENXMEN3djBJdHZuSkV0ZzdkZXljWUJJMHlJeDdhb2NGZ0pmQ0sxSEFrYTBRMGV2UHBOWVpzY3VSZDJxRXJUWWQzMHI1WW80K0pGYlZrZmNpTmp5dG9GYkZPc1I0RFl6NkNkMjFKSHRjWVoyNU00WmI5dmNIdU90b3BLSFUxMUx1UnF3aFRzbWhUVU81NzJEWlhGUms5VFpFaHRHZlJ4YkN0TFVMalVOelg3MTFTTEUxMFlKOXY0N09Hdk1vdVowTk1PcmI2M0o5SGZDZXpCK04xODhhY3NOYkRpdTJVc0psQk5XYnBnTUtiMlFIRXJvMnJiRzg5cVVqQTdwM2FOSGFNcUoxOHp6VjlsNVRFbURwSEhUNWdGazBOTkRIN25HdUtML0FGYitWL1NMMWp6TWoyVFBOQzFWVWVTV0tnSWxhWEliQWROREJqcVJtekNzRlF6Qjl4NU9VMTNYcHkrVS9XNG9WeitqYnN1cFBWK1BJTE0rZCtiMTEvUnEyZXFhVjhkNnM1ODc2R3B6WFdMR2U5RS9QZDEzWjEwdGw4N2lDb2RyWXpLbnlCMStaMzVOUGI5TThLeE9SM21GdTUzam4wT1ZjM1ZXdGxMbTYzQzVBaTlHaEZjN3N0UFRpUDhBa2IyVDgwK2p5ODdBS3huNmEzV3pLSzJvZnJiT1B2dkQ4TitHMzhkN0p6K0c5bmhMNmo4dk91cnp0dmRMTzJmRmV2Sk8vd0FyeUIyYzBLeTZyZlJtMVI2RHpkaEZWL042aVg1ZlljSFU1Vjk1ajBHai9BKzJDMEFteGVWaUZOWnZ5RjdiS25yY3VsTWk1ZFRHNVc5T1VwTTdtSmtmVnZtNno1UFNhRXZ5ZHE1K1orZjBJcU0wK2h6NDJIWHcwVjFtZlJzL3gzcUFEaDlqVE1DM1pHQ3dxcXowVTI5N1Q2cmdOdFBGNjY0dGZwYVVJeUE2R1RSL3UvSGdXbWtkd2JadGk1TzI4bXZKRE9UMUxIUG9NZXZ5M3p4dWljK0o5WGRzN2l1ci9aNWMzTm5hZzlDU3pzbDZHRTJVNVpHZlpOQk80bFczbnJuYmVYVHBWemFjMVphS1ZJczlXUHQ5ZjVld1N4aVZYYTg1L1Fvdk1kY0tWaHRwYVBYQlVqSmVzclpsUFNRTkdCS0Z3MWlQaDdTU1NoN2N6Z0N5dWt4aWw2UXowRzFaUmxWaFBZaFJZbGlCdmk5c2FaRlVZdVVRcnpqQnB3QTN0YnByTEFhcTNlTHBSV01lRHFNdWw4WTBLV09GYkhGbC9jWitVUHVrSzRPcEpDMHF4R0hwMXBtTGNpbE91ZWFXVTE4R2xubGl0czFWTWpXT3htcFpUWWtxbXRsYlcxbzB0eWIwUnI3bVV0Y2tabEY0amtTS0V0WU5KaFBvN3BtSzZWeGZJSDBzSm1NRnkzQWJoSXdDb2UwZXR0VWF0cEtXR3pVbzRORlVyNFBxdmJhTDFWZktsUHdzb3dGaEJyV2pGTjF1L252bWlVZ3ZJSU5sYTZXM1lCck1vMXBBY3lCNnNwSVUxWGErbm9WNFlKa0hWZTcwQ1NyU0JIdUUyNDFTNEpWaWxGR3pRbGtSS1ZQbGhvQzJXVVVuRVh0VHArczVxVmtjU253ZGw2UjJLQTJncmxHQ0FiUVFsMlZ0S0J2UlhWRnFBYzJYQjNzanZjdWlLWVV2UUtFL1ZZaUs3TTNpWjZWdnpCVEFGRFNXUnBlNHY4akFOQlI2dGRvMGtIVXZUeXRYUm4wTWFtZVQyVjJPM05mU1ZNR0pGbWpad3F2VlVDdGswY2FjYnM0K0Rka0FrQlkwdFRIUzgwNUQ1elZFVmtqcVl4bi94QUFzRUFBQ0F3QUNBZ0lDQVFNREJRQUFBQUFEQkFFQ0JRQUdFUk1TRlJRaEppSWtKUWNXTmlNbk5VRkMvOW9BQ0FFQkFBRUZBb3J3ZHlVNUR4UjhHK0tlRE5TL0k4Y2l2SnB6eCtvcnpWVzFaWktJZ1RvTEdIRjNtam1qTW9uRit4aUR3S1doc2FpT0FnbnlzUkhPNnovMjd5Zk1kWDFQRmozaVB3N0twcmE5aGRmcmFLYytIQ0QvQUtmWHo0endaejA0TnduQnNmTG55OHhYemEyelFrWVg1RERITEpCVXRUYWJOSU9xTW51cW11Z0pEOTlnOGNyV1o1dm9qMCtzajFVazBEdFBOTkZqeG5KVGVkOHpxL3FyWGtVNGZ4WGtWOHg4T1RYZzY4SDhZaWIyNVptb3lPc2lwUWIraTdYUDZvcjdBcmhYWDMreFh4K3hXOFJHWmFuNWxwOGM4YXIzTzBKV3p1bUl0Wnl1SXc4RnZSUEg5Z3N1dmZXS2JLQldvS1R6OFkzeGR4Uy9OUDIrcUJ4TVdCYmd3WDVXbE9ONkFRVnBYWmVvdmlLZ1o2NzV0Z0NzV25LTm5pT3pCQzJ5cmRBaDg0dWQvdUlqQ3hOUURPaGRQYXkyOUhHSFRVeVQ1bWdxK1ZuOXAvVTU2UTVmeEtxQUg3ekJIOFNlV1lxTXhmZGVBVVYrOXpLcWY3cnpQT1NDdC84QVVJR2VPTzVmSGg0OWRzQU1oNnJIaUt5NHZYajh3NGVxaDU1bGpFSnk4QSswSzJwUlMyMWxqSjlxY3R2OHJaaHlQN0dNaGV2SUZRRUNvQ3lYMEJ2cTZaWmx4bUtZYzdYWmJuUG9odGEwVEUyeENlcTZlMjNtWFUzTTk3STBiVkxtZlpNMVR1eXY3UWx0ZTdWSHF2R01WT2ZWWWlLb1BSTUo0d09WMUV3MEwyTS9sVnc3ZW5vK2Z4b3lER0piSVdqaSthWks1ZjEweGh3TUk5bjJDM245OFZKU25IUVdXYnlTZWxaMmZCc3Q0aVRzQy9NVUZtSzBxWk5BWmpMOWNMWnBvS1p5bjFwcytJdndqUE5OdnkwL0lyMnRNZmIvQUFTcG9mWWFkWm1vdXZtbXkrQWtiZkdTNERBMm4zK0VvWDFPazkydEh6cFNnNWt3eERDS2IvRWVncjZ5eDVtY3pZZEZuWDBsU2dTdk51U3NFbE1WcFEvYWhOZmk5azF4MGtldGJ4MTNBUFlSenRNRkMyZjRmNmpGYlVzMXFIaXZLZGsvYTdsdmE0L1djL3I2L3dBZXN1aXRYTUlrV3d2eFA3MUJldEc1cldabUl0VnlLL1YrQXhLbnhpUGI2akMwWXk1QzIrNHdYTzFWT3pLdnRHN0Z1TVFQamZZODdUQm5hV2VtK1V2dGUvSWo0VXV5WjdTRzU2VUR0TXRYOFg1ZEFsMDhNZ2wrc01hRkNyTlU5ZWo3TFNNTEVBYytYdUJiOTBmTkZGUC9BR25QaGNEWXBicFZSeFliblgxMkNNZGYvSnlHK3VTOGQzSWFUR2Zxa0xscDFWZ2wwK3EyZ1diMU1WbkY4OEs5WEJzeXJrNTYycmJFVms1MzRwSVhaQzE2N2Mzay93Q3Y1ZnYvQU9ybEpub1JwT2tyZWZZT3RmbmNTc0NUS3FVa1h0NlVXWmk0Z09Jdlpyay9MczFYRW1PYUVkZE1DRnV2RUdFR0xUcm9Vc0lxS21YN3dWNjVyVWltSjJLR0owdFg4cU92UE9hdXJoNktkeDZiNGdGYkV5QjFHNExwQ2oyRnY3WEtWK1VDL2NvNTF4REFkaXVvSXNpNldENVc0ZlA4YjVmcEJJMHQ1MTlBdEU5ck05VHBqSHpRSHRWUWlvVDEvRnliWkMxeGk2ZzJPK2QxMk40dnc0NW82bVppUHV1TWRZL2M5VEVJbzB5UGxvbFNpcm1VTHJMdDVIMUxTbVE5VlRReE5DdFM2Y29WcHkxVnFKQzFhL1pzYko2OWpSc0tXQjJMSGFORktHTlJZTUtkdnpXNHE3cHdDcHZ3bFlLdXV2WXQwbUEzb1BZckJHdXhDblhkSzVGdG1TS1N1VmxlbWF2VE1MbTlXRUVOZXZVenAzMkswejlIVVozWC93RGloZ0FkN0UzbUlmUUR1blJCeGtrUEtRMDFvTDBxTndkSW5SWXNmN01GRFUyQk4vamFyRGcyR0pPSXRLa3NLOUQyK0lMeDRyL1hEZW1aa3F6RVY2V3ZjbCtwbzV0S3F0TVNObUdyOFhOY1FzV3Y4a2Z0L0VQUGpzQm1SWlhXeVRZdlVjcWFMOW1BNlNuYjZIa3V1T1BMVGlaYkJ6L2xYYVRyRjQ3T2xWUkpycm9WdWVwa0UwY2JwV2o0NjgreVR0WEo2NG01RG1PdWtVUUFBU0xOejhGbWpyaUd5VkJYblBqMFpLdjR1dTUrK25lZkhhZDNYL083RW5md21LZjhtb1grUUNtdjJpL216Sk5Na0RUOTMzZ3JNQ0JyTVRyQVhjbHFzaFc5UHdFTFJJT25DVmllWTJ4S1pHWS9LWWhaR1JnU1ErRjh4YWEzeWs0aktLbzQwTzZOTjF1MzhQYmtuM2RlcVh2ZjZkMm11NmlVSUVrbXk2dWQxYll2YjZsWklRVXJmUDhBRlBIQ215RXVNOW5wRlUzMkw2ZnptUWttWlBkMHZtV1NUeUdiVnQ3eVR6NVdtdU5lME0zaXNBdVlYbHBmTVo1K0NzRzlkSzkrdUZFOW82bWYxemM5djBQcTVWTEZwUHZPS3A1QmVXTnpOU3E1MjZmRmROM1ROVzl2eis2VC9sVTgrbjBoemVHek0vOEFSVWErR2RWdnp3ZFl0Y1NuczRETXJ4WkdvdWFMVWpEUWhDbVc2cHN2Y0YwdFVkd3BZS2RQc3JCcE9zYUpMb0tnNDEydFVmR3UxTkU0YlRZWk92WWRTc1Q1Sm5UNDRPUGxzOTBKL0tFLytGNkxaSjNTdXRmTVZ2Q2xiLzFDRE5lQnZmeW5tNzcvQUMvVXlDR0RQNnVDd25EaG9ZNUM4c3lNZFdOaW9xc2RucjhpYW1teFUvc21pcWlOa1REL0FCTndGZm0vKzY4TFBrcVg2U1IvODUyKzN5N2FIOWRPYkFUN2FVaTJMZXNqdEU4Ly84UUFTaEFBQWdFREFRUUZCd2dIQmdRSEFBQUFBUUlEQUFRUkVoTWhJakVGRkVGUllTTXlRbEp4Z2FFUUpHS1JzY0hSOEJVelkzS0NrcklHYzNTaXd1RWdORlBTUTFSa2c0U1Q4Zi9hQUFnQkFRQUdQd0t1Qnl0QWxRL0VCM1Z4QXBYQ3l0NzY3djhBaGMya1hrajZhSExmVldpUlNyOXVxbHU1V0ZwRjZMeWR2c0hiU0xCdEdrRzVKWnVPWCtCT1NVYm5wUzZGbG5lZFRhNTMvQ3VyOUFXR0hQOEE0ekRYSWFicnQyVmJQSHEza1VDSXR0SjY4bnlYbjd5L2JYUnVQL0t4L3dCSXF4T04rMTUrNmgzZFgvR3J5ZTA2UWRCY1lNalBHQ2ZjQnlvbWVhNWtZODIyZVBsWDk5ZnQrWGhrTmNTZyt6ZFhiNzY1VmdWSkhIYzlWbWJHSmM0MERPODFEREhyNlJuWHpacDQvc1h0OXByclBUVjlvbDlRSFZLZisyamEvd0JuT2pqQ0R6bHhsejc2Mi9TOTJXYzgwVTVQMTFvdElWZzd5UE9QdnE5UDdmOEFINU53elUxbDF0TGZVd3krTldNVmIya0h6cDRvMVFIR2M0R09RL0dyVHJFTFFRYlRnQjNkbmRRL3czNDEweEdzQ1N4MnNxakhwWUlwQkJjeHA2eW1IYUg2dmxRYVMzRm5kNGZLUGw0Ujc2MGI1Wmp5alFaTkg5SjNBZ1RkODJpT3FRKzA5bEdEb1MwWG82M080eW56ajdUVzI2U25hOG05WDBhMmR2RXNVZmNneFZwYTlXRThVcWFqdndSdnJVZHdxK2wxWVV6N2pRQTVuOG1qczUwZ3RmUmJHYys2cDcwWFV0eGRCZ0F6OGhud3EwekxESE1ZRUxoZk9KMCtHK3JWSWxmZ2t5UzY0N0RYL3dBWDhhNlZGeFBzNVVsRzJNL3BId3g1MUZWbWtkdjJhQlB0cmduUnZmaXNxbXYyR2trRjRseXpMbGhwSzZQQ3RFbzRoMjV6bjVOd3pYTFRUSFZrTHo4S0ltbDJBN0kwL1dOK0ZhYlNBZEZXamMzZnoyKyttMm1ibVpXM3ZKKzdubFR2bm5PMWNNaEh5ZEZTeko1YnJJajFqbnA3cTAza0xTdzkxWHpUUkQ5SDlZWWFjZG5aVXEyeXVzUXRKZE9zSDFhaWp0YmJDaEFOV2o3emdWTGIzdlNVTnJuZU5jaGY0Q2gxaTNicEt3UWpKaUpUY1BFZmZVWFZlalJZSXI4WENkUjNIdDdhQXpqNXIrTlR6Mzk5MWhPYUlFeHA5NUl6V2JZbTJYTzRyei95Z1ZzNHQ3L1MzQWQ1UGhVM3pqYUx0RHMyODNkWEJJeEhqdnJTMEViL0FNR0thU1JkR2xjNFUwSlhrZUQ2TXk2VFc3V3c4RnI5SUhwQ0lXMGs1Y283Rk4zZFhTTjIzbE1IRWJIeDdSOGx4S2VXMC8wVkNHR0NTVFdTY0R4bzhldjkydWpoalJpNUJUdDFIdS9KcjV6ZFIyeS90Smd2d0crcEVPdHRVaE9ZamdtbkVFUWgrWlM2dkthaWZiU0NlZVJsVlFNWjBMUkVOdXYyazE4MDZPbGsvd0RhL0hOV3pYOXE5dkZyOG5xYndyR2RQelViejJjNm02dzB2U09Ud1lqTWFnZUpPTTFwUTlIMlE5Z2tiNzZlQ3l1STU4NWE1a1h0eDZQc0h4cVM0MnNlbVBjUnZwSldkZEQ4dExVaGppWHE0enRwNVd4bzlnN2FFVnYrb1YrSGRscEQ0VUx5QXNZMzg4SmpLTjJnc2VRcjBYOWhra3FSakpzUmovcXRGbitjRkQ3RFV0eXJlU01tTmtVd3YxZG51M1V0d0FpdmtCby9TRlNHM1F5QjNiUU00OUNrdFJDWTNYQ2dScnE3TzJ1TXpUbjFwSEVTajY4bXNXeXhzMy9wcmN6Ti9NOWRIUnppY0dlZlF1M24zSDNMeXFLR0cwV1dhUW5SMWFQUnVIYmswVzJzZHZMbm5OeXFWcEx6cld1RmtHeHR6dzVyTWxvMDdEMHJtNVVmQVpyRnRCYlJEOW5BWCtKeFdtT2RnUEJsVCtrR296S3AwZzdtWXNjN3ZwVTZqYzNWQlhsK2xJUWY1ejk5Zjg2OC93RGxxVjVMYTJpVm9pTmNDYWMvVWF2ZkdYOEtnVFJ0Skk4N3V5akMwbkNCbVRIWUtjeWVUWUtOc1Y5QUhsR3ZqVWx0Y292VmlNU3A2TVBjUEZxTVVyNTlVdkpnTjdGV3BzU2JKbUdCaVdXUFA4MlZQc05SMjY3aEVPemR4SDdQc05BbytoVHV5T3lyU1BhN1hhRmpyUEZuZFRKUGNQa2VjaGwwQWU0VkNrTnZFMjFpYlJnYm0zam1hZEpZeEE0N1ZKUS9DaENpejNFcU5yaEROcWFQM25sU0EyMmpXTXJ0SlMyNzNWSEZCY0cyZlRuV0JtbmE3dUxsMEVET3J5Y2lSV0ErMVBobHZzRmNIUlVqK3kySisydkpkRnp4anhDcFNHOVRaNytFYlhWVXFnNFBWMUh3ck0xKzJQQmY5NnZMYVpwWm9vTUFIWHB6dThLZG9rMEIxMGtGdFZYTUt1dlVrYnpWWGVXcDJac1lGTTc3MDJqTzM3c2YrOUszT1JGMm50bGs1ZlZTUlJjVEI5bkRuMXZUa05Rb3gxdW93SGZuUnkyRjdhMnljY2NoL0k5aHI3L3o3dmpWcWtVMmhVempoMzcrZFhBRThzdHhKSDZjZVB2cXdWbzlJamo0U1Q1MlRuN3F2TDErY0xxRkZYcmJZUXlTMnlHSXNvMzQ1MVlvSTQ1Qk5DQWRvbWNjUk5NMy9UaTE3dmJqNzY2TS91RDk5U3hxUUY2dVd3QnlOZEdscG5Qa0pDNTFjK2RLa2s1amdCNCtQaHhVSWdrV1lpYnpsYnM3QlU1enloWDdLY2FuYkRZSFlhdXArc0pidEsrZU9URFVCYjc1SkpBZzFISE9veGxES3pzejkvUC9BR3FjNTlDcE9KRkp0bkc5dTk2Sk1zZU9zeG56dXdMVURiWkpHRVQ3bDd5MjgwQ1JuSEtzRVpGUzZ0d0F6VGNSM1o3UGJRQzh0WnBXNTltODRwSExpNTJ3TWdHdkd6OFBqVzI2UHZCYWxwQURFT01IeE5SWWxpMnczeHRHNDAxMFdsemRkWUV6YTRNTm5QNXhXOFN2bU1CUkVPWnpuZjRWWVdscHRUTnNPUlQyMWRDVzVXTEZ0amYzN3MxSTZTYW8yUENRZDFZVjFFMmM2czhkUWhtZWR6S09jdFhjaXd1bUl1MmJQSVZvMGE4TG5oRkRWMmVORko3eVNRWnl1TjJLMWxCcldVb3pIT2U4VThmZU1VWVc1RnBJVC9IdkZhd1BLTkdqZ2ZUajVqNnFXVmQ2UnNYR08ySi93Tkk4TW5DZCtSMjBSbkZDM3pxWStkN1A5NjNuOC9uTlJrbkJKelE2MUd4aDdveHFOZGVjSHFxamlaNHdwOWdBcHhKMW0yaWs3WUgzKzNmUjBYTit5QTgzbTUvNWFnRWIzUWx0bzh3TVpjNDBqY09YS292MG03d3h1UFFtS2QvaFRSMnlFeXRqeW9rNDkzWmtMWGwwMDdzRlpMakdkL1B6YXdKdUgxUmY3aC9scklaOCtGK1ArMmt2T2pCS2RsSU5xelRod0I3aFVxTjZRcDF0NFhpa0M3eWt4VTRvbFl3RjlVdFRSUEd3YjNVYk5WY0pMdTU1SGZXK3RzTnlzTU1lN3VORmo1SnRlVCt5ay9BMXdneGxUbkE1d243MU5SUlFjRXJjYjRqMUtQRHdyR3NEKzdnUDMwc3czbmsyTisvN3pRVURuM2ZuM2ZYVUEyTUU1VWJ5MFdyNDFiS0dqMHhEaUdQTzMxYkoxWkxwTnIrcGR5aW5kM2pmUmRmN1A5SDIwbU1OTWs3RnNlK3ZtMzltclcyazA0NjRrM20rNGlwN2cyaVNqUW9nY1NhTkdQRHR6dkZiRzc2Tldma2NiVEduZnZ4VVMybHJQWXVKUVc4b3VOUDhOWENTeEFzZFJTVlRrK0ZYSzNFQ25wRVJwc09EY1c5TE5kRk5jQkxlYVM1bVc4d1NvVlZHVng4S3VMVzA2UXRiU1ZmMXFUeTZBOUtZci9vNTNIZGZDbW1IVXBISXh3M2FmalR4ZFViVXZibzNmWFVqaU9XTFBvcW00VXBMU1pISTdMRkJMbERLcTdnZVZiTmdOL1lhTWliNDhZeTNMSHF0K05HYVZTSW9SbkRjeDRBOW9wNVplRjJQcFJzcCtzVnU0LzRXZityZFJVOFN0dU8vUDU5Z3JyRXczK2pudHFKTlpSR0oxWTdhdEpNRFdKV2J6Yzk5V0dpTkxpWmlXRVV3d2gzYjZoczhDWHlEU09vYkFJQzU3S2dnNkl6cVdFbWNoaVZ6NFpxT0daMzF5cmxGOEIyMXNPWUcvREhuVVVVdGd5QXVEdFcxY1c4REZYZG95Unh1Q1ZEdHpIeHFLS0tWWkRyVXN5K2R1OGMxMGVzVDJza3RxMHphSkRqVnJHUGhVNTZidEliZ011RTRSSjIxcWg2TTRSNmx1MlBoU0VXbUxZd05yMXh1QUc3S3RGRVdEc1NaQ3d4dkxidmhWNWNpWFkzcXg2d05qMmV3MXE2VDZTaDYrVlZrd3VNZCs2cnFXU1FUU2dyb1lER001clh3a3JqYU5qVjJMVnd3SU9BdUZQS2d1c1dpdTJjRk9FMTgxak1vNzRTMlBoU2JlM2VKU3dHcVVISHhxTzdrWTNGejFoVStpQmpOSkNZMmRmVmpPRFJaWXhBY2JqSk9LanQ1YjYyamhUc0VtU2FqL1IwN2VRWFRHeUFsbXp6TzRWMXE2MnBrV0U1THFWSnBqRExyU1FFNmZVOEtROGsydVBhS25uZE14eHY1NE8rdXI2dFg2dGdBZHc3ZnJycGExY0FoN2dNZ08vczMrNm9IU05VWXRodEs0elJkN2RDV0g1TkJQS0lEeTB6TUttMmR4ZG1JZnF5a3dQd3JVblNVMmc3MUJIRmp4RmNNcTNRK05YUVZHbDF4S0YwS3g1SHhBclFlaTVCNEhIMzFjd0xGMWFPVncycDNCeGoyVmNSVDlKamo4NWtnODNsM253cDQ3anBlNGsxNDh6UU9WUXd3MnQ3Y3h4dWRKYVVmSGRRanNMVVdTYXNjUnExRjFKR1lETHlIT29mOFlQNmFGdlBxTVdDVHBiU2R3cThNTnRpWlFjT1dMRmF0WndpSXVON2xQRWluME9SdjlFMHNXbGhMczhNRHp4aWdxZ0x3bmxXMWFSdFFtQ3F1ZDJLdXRuZWlKVEljeHVOMVdNa2oyN2duQWFIbWNldFYzSVVrNDJJSjA4SjMxQ1ZZSFMyL0ZhUnUwajY2WVI4SVBPamxtVmZDbDhxV3lOekVWcDFCeUt1TmhabUdKaGlMRXJFcjRuTlNSeW5YZUxMSHhZNGp4Tm43cXZVWlR0TlEwOS9iUmRIdFpFY2gzang1TWN0M2IzVklrSnR4R0R1YUMxUlI5bEdYYnM0MTQ4M25ROG93S094VGQzMWJTYjg3VHQ5bFcvOEFpLzhBVFdWOVJ2c3FXZWNCbjlYMWllUzFDcUx4T0JoVjhYYW9ET3UxRWVjNXE4bFE3RnRQdzAwR0lDN2p1RlF2bm5jZ1k5OWRiWHpaSmlvSGZWdUc1Z2svQTB2Wi93RHRkRVB1ek1YejlRb1NxV3k2aHR6WXhYQk00L2ZVTUtPcXp0TGdkdkNVUHdyeWxoY3crTVVnY1ZoYjNxNTdwb1NLbUhXSlU0Y2VZTjFiTVN5djRtdGFhZ2Mra2EyTVdjZXFncUpMaUpkUUhGcTUxcWkyYlJaempWeENnK1ZTTThtWnNDcllBSFo1WWhzNUI1MVkrTnczMlYzOEpvMjhaemJXeU5qNlRkcHEwR2NZUWY2cVpnZStwdFhKdWREUjV1ek5XMG1rNEY0QnE5OUpBRjNSTStEN1RTUEp2WFMrQ1AzVFFhV0xZNTV2SVF0V2tjYU5JMXRxMGJJSEJ6M2swVXVZakM2WVU3ODFoZ0RSZ1Btc01yVzZ0OVNlUjJ3YnZmRkYzbFNJZnpVRm11bWRlNFlGZVFaLy9zcmlqMWUxalc2Q2owWGRNTE4xa09rU2VqUVcybGQ5SGF5WUIzVjBYNHl0OWxQczIwblNSVXM5bk1NYU1HT1E3eFNBVytJc1lEQTd0eTF0R1lZMVl3S1owdDVHVThqcG96U1FpM2gwK2RJY1VJbTZTRTdDZmE0aFRVZFZaZ3NTVDY5eTJQaFhscjRRL1J0MXg4YUxTRlhmMXBXMUdzVzBKWWVpY1lGVFBLTk8xUElWdWJmV3M3bVhrY1Y2M3VybDhLT0dybWFHVFdDZDFhczF6b21ZSVg3RzdhK2JUSkRLdVdpVjVQUHF4UjJNaXd1MnZRb09EdTNaRmZNTEtSVko5SWNSOEtCRnUwSko3ZVZBOUlkS3d3ZlJUaWF0U1dNL1NMK3RNZEMxaUJMYm81UDJNZVcrczFxdTU1THB2MnIwUUhSZkJLT3dqL2ljNHFWV3V6RWlvVzRCVy9mdjdkOWRDeDdnRnMrejIxSGRla1hwMThhY2VIZlEzbjVQSDVjNHJTc2lnKzJ0S3MwckhzUVpvTjFSb1VQYk0ybWthK3Y0SW5VZWh4TldtSzNtdlQ5STRGWXQ3YUMwWHdHVFIxWEJORThDZU5ZV1RhbnVTaUlVRVE3Mk5Nc3QwOG5nTndxeGNxT0tZNi9FWXIzMWNuOWtmdXBSM3lDckFlcmFqK28xWmVMbXBvTGRObytydXBvcGNIM1lwUjhtYUdoTTE4MldPTmZXMUN0ZlRIVGpvdmFrS2sxNUd3bTZUazlhNGt3SzAyZHJhOUhwK3lqMzE4NHVwWlBEVmdWd0pYRVNQWUtJaFZuUGpXNWhFdEY1WldsYlBhYTA5WEdvamVhdXJkenF4dlU5NHE0S2pBV1BKK3VyRWZTWS9ENUxvL1IrK29QNzBmYlVIK0hYN1RYUm9BOVkxTmNMNlRta2tjWUREaHJRZWZ5Zi84UUFKaEFCQUFJQ0FRUUNBZ01CQVFBQUFBQUFBUUFSSVRGQlVXRnhrWUdoc2NIUjRmQVE4Zi9hQUFnQkFRQUJQeUZ5ZlZrektWNUhVMUNNbjRzai9wY29xNTlvQ1libktFTThzWmVzelUvNFVSMExZd1dkVm5HUDJsNGlLNDRQOEJ2Y2IreUNYTE1ZODhuU0hJY21TMXhVd1pmYytvSlgwVGdQK0o2c0Nhczl1SmxUU3phN3dxODdRUjRnenlyR2VmZFFyOGY4enN1Rk9DNGxYRW9TcjBja29Iblpkc2VobGVDL1VOUXVaNlNCaUEyZlhlNWMxWE5jOVRQOGNSdTl6Vjdib094TUIxQTgrOFpaMWQrMk5Qekt0QndQMjdNRFhZSkRyZHg1c3B1alVkRm42dXM2eDdTWHQ4YWpyYU9PcVp5TGFaVy9HdnhLaC91WU5xQjVXT0Y1SytZY21PZjZKZk03UC9GWjhJN1FyV1pRMHdwRE5KaElMN0hZYzdvS2J6RWY4UzRtMm9WTHJnMmpsU3RxOHJtVTc5bHlQYmJENU5xcW1hQ2J6UXh4TWcxbHE4UURCYkljTi96R3FydWZBRnFjbmVtVkhibDVpaVJkNTExT0kvUTFnV3p1UUdtekpkNmh6OVJxaHZUM0ZlUzlZS1hUaTZ4SWZ4aXYyWjZSbXo3aWEwV0tuOFFGQ012Ym9ObXg0S1ArSlhYcnhKbWp2QkNEWWplSFdZM0Z5ZWxmRi9jZWN0V0QxNnZxWU9KM3J0UUxSSzNQeFBTM2VKbFZQY3hBK3FYZ3Z0YlMwWUdyaTc1Nms1eXZjQ2pTdlVvaHJyQWFiM3FKNUxRM1Iva0dXQlFIbERoTUQweHVEQzU4V3E5ZEV6dGR0Yzh6azhpZ3VETGRXYWp3dm5Vc1JMRjNVZWFIMndhclpkS0pzY0RsakFPN00rUHVIaGtZZnVOdHY1UHFJc3VPM2RGMFh6SzFOMFg4czArNkt6SldsMlYwcnc1cVpCb2lDRzMwYmpoTlNLejA1OXRKMllsZGRVUXZnZE5udlgzREIxWjRlQlJVUFcwZGZhdjVTbDg0OHpoWEJOSUViTW0xVXdSRWhpUGkvY1c1cmtQb0QrWVUvUTNFOXdiNkJVUnR2ZUxvOUVkVlNxK3doVEl4T1d3dFpjNGp5MjllNHVIcmx4cmZLbVA1bWVrY1ZudEZueDdna3FPWmVPMFBZNlJMaWkyOUp3MmhZaHNQNWRFVTZkZjB4RVZNV3Y3ajdLSndoalBqUHJqNW1KaUNadXQ1bmtvcEtteFNYbkIwM042YWRGd1B1WU9xQllGTFJmVGlDRmIzNUJQNkV5ZkdxK3VSS2dKazFLMGxqNGJqUUlEMFJlNXE0VVdsUUw0M2MzS1IwZEdLS3I1aFhPSUF2bVgxS0hGMTc2Y3ZlaWwzOE0rNWRydFFrcmxmb21UZEMzVk5NNi9sdGdIelM2V1AxQXNNeDI5N1Q2aXVHNlB1Q3VnWkhaYno0cUkzRTlMNG83cmdtRjU1UWZjN21ZMkZLSDh4NW5kNmZEMTQ4VStEMTN0QVJvbGRnRXBrY2NxNDdFWCtTd2pseUQ3T2xKR05kRkc5Uzg3aVF1ZWdIZW1QdENVSHpaaWJHdWZCRnBTWUljbXNiMVZvN1NoWnBOcXJKMzN4THNud3RuT0poOVpUd2hkNDMzeEJPMEF2cWhidWxzZFpicmszOFRjeTNqMDN6aVdKZ3IwdUxXS0tUK1NMNldGWWtPVVE1OTRxaGQ4eXRObjdqNWVuYUpPWFdEN3pVR0NjUzFySG5WZndsZEM2Wmp2MUNhSVhXb3kzeTVsOXhHMnBtWll3dWx6bDZPakJnTnYyMW43Y1dKbHBJQmhlNnFzUk9TTThTdWJkSXczc0ZvcVE0SVExejUzZmFaRGNJQmFoM3pjRFJ4N1RZTDB4L1c0VnF6aStJdGxhdytmN3d6S3I0UDhBREdiVGgrNDB1dWhXNHlCeHlnQVpTTHh2Ykdud2ErWlpGTjAzaTBER0s2azc2akt0ZmJGVnFFc0NiRjBNMHpyVERIZVp1T041YWlUcEZkMlAxTDBHNjNZdzl4MVZtUzEvb2tCMGxmWkdGNDJNZUtyZzFxbXlWOHp2Ui9YNmlUVklaK1lZQ1lOQk9UcXk4TGxpb0tMNXdycnd3S2x5aDd5R0FEaWZ4T09BNitJU0FnMnF2S2NjdVlWWTE3bVJnN0IrYWxaN1hlOE1NUm5vMXVSL1pNZzVPZEhXTGdrQS9handzYlk2OVl5eWwvZzNCQ3pMeXI4RWRMeFZTSHlCbzV6V29LcmxpMHZKZDJaWlRJcDRIQ1BBUS9QY3IvbkZPcE1MNkx1alVLRnR3TTFJSzFKS3M0bDBaaDY2djVXQ1ZsdWNMOVAyK3NvaVFvdis2eFhKQ3BXNzBwanZFZzl4TGRNYUVHdXRHa1UwaTNWbkd1bW0rSXAwSGNMSlRXNDFSK0ViNEtMRXdzaVk4VjAwY2FsN3g2U0t5ZVczbHZnbTc4VUZCNkV1dStXdjNKZVgwalFWRzZFekR5SlRNL29UaDhSeGgwbGZjTGczZG8vTXJScVdtMS9iaVZsM1BpWkdDdXBqZnh2NWlsU3dvOFBXZE9PR2J0dTZJc3VTdGJ4MjdiaTd1U0Y5NGt1aGx2ZG5nMzhGNkk5dmRHbGF4MjRmS1pJVTNLM1k4SmljcG9Heis1V0JLcWpORGpWczhSSlBDU0J0Tjk4eEtKUUxDem5yTmZNR212cnRxdUhBWk9zSUt5dFM2WHNjS21NeEY1V3M3Vzl5L2dnRm1waHdjNWJRMlh6ZHhzclZ5MHhhd21rM1Y0WG5jc0RRd1RjZDh6UVUzZjhBWktLZjV6SHlJcFhLVTNMcXkxVlpGem1iWjZqTnA3NmlBejRvMkQ5eENrREkwek95WUsxK0IwNFRGY0p2T09TYWpEeXdzdDIwdFFDUVU1L2hJV3JEKzhNUU5aWXNEMi8zRUhET09BUWNRQWtPb2N6cDNIaVJaY2xTK0tHeGkzOEY1ZEFlcUF0RnhtUzZXMVp1RVprSCtTM25FZWpoTE9tcU12eEQxQjlBek9DdHZXY2lUaEZ2Q25pSFQxUzJBbElWamVmQkNtd21UYjk0ZWp4Qk9jVXE1TGZ4aVhGdk1lQVNNTnhVeHdVN2dXRDB3bEhMZFVKMDFka2NOdkl6cFkrWVc3VjJsSHhMRER0TUdRcnJpVUplS2lFN2gxZnVYSkdGc1c1Z1gzSnltTTdETXpNK2JXbnlTWjZEdGhXamI5VHlzbmpjVkg0c3BCZ0hWeGU1SXZwWlVlVW8wZHkvekhtRzlEZkFUSEV3cGxJNnNGTklTc1pLTlhGOVl4MnkwOUgzTUNkRkZQQjJoQ25LMHpVL1JjSFY5V1dyN0NqNWxtUDRDSHhDcFF2UFhvNWxjR0tXVDZicjNFR3BWUmV5ampNTFlhc1lwZkJUS0IyeGowVWpkaWhNMnRMRmNiTmlGUGkwdmozR00xNFM4eXZuSmJWVC9YM21NRGxldXl4dVdXWXJzQUxPMEdCTGI3bzdHS2cybGE4akl6TnpuNlp0SklMNUJNa3ZrWUNRYzVZZmFZeGx5bVdiZUplTyswNzhWQ3p0L0JPZzlkMllCN3o0aHg3bGRKeEY4OS9xWnptODUyd1FTYm5DZ1lacUJ1VlNoVDB6TjZybTZwL2lDYjJCekt6Y2M3aDRsTTJaQXhLMlBjSmY4N3RESW5NZmRacmFyKzdYNFRQYU83UktwTGI3WmRRaFZ2Tys4eUM1TXY4QU9rUTZZRGwxcTZyYkt6dVNYVHpoTEN4NTZSUDVtWDd2cURlckV4ekRvaVZmdHY1UG1FRVN3WTRCRE1Cd0x5RG40aEQ4RGRtQXI1bGZjNlpOZVBLcDNZQlcyYnFJbnFoU3gyMlZYZDVRYmxLeHloczl5c0JtMWtMcmN6Wm5SZDlRMVJiRXN1cUR2MFU1aGUzcUJndklJMzhrc2lqYmVxK2laZWV6UWo4TUViM3kvcWVHZkN6MWxjTTRvUDVZKzNMYVU3a1FNQk9aU3h5bzFlTlo4RTRieHRhZmgwUVg2RjUrVUhPR3FOUVA5UmdndmNGeXBHZThCZFlIbmNmTnl4MUp3d2VnSG02Z3prckpmZ2Y0Z3U4eUphOEk0NGxXV1hpS0tqZEs5WlNRZGoxMmcxcE1haHJyS2ZJSHA3K0ptMWVzcjlUcHBzeGV3dzdwRitxbXRQdlAzRkNZK1dkTVZWdVZSWTl1Q1VHS3VxZU1RZDJ6RDN6NWVhZDEzbDYvd29xOWZqbU5ZQ0JkUkRjdjVnSnMrWnkveVZNVGdxWXlBT0dLNm5HTVY4enMvaFQ1cERMQzVDL2ZNYTI1Ykw3bHZORzIzWFRyQnI4MDFHOUNtRXhLbkk4anFFVlVPcEdWSVljbGZNelpGaWI4d1F6TG1Ec3BXWlpGWmdIUFFQeVpsdWxOb1o0YWVMNzlJeUprV0oxQzhSbzJ2VjBlam9mRTNSc1hTTzl5dURHNWdzWC9BTjNNTWFwZ0dkNWhBcDZqZ3ZwWm1USWNVbnFQQ0NsRjFXUHVKQmtwbk45d1FZakFGR1lydk5hazdTdnF4MVJ4bU00cWRHV1pHVitaaUdZd2gxRWhaRmpGRmZSaXNWY2xzL3FHZjdWSnVFaFVWalY1MU91ZzI3L0VFWm9nT1AwQXFDNXphdHk3ZDVTTGQ1aWZVcnlSYkhzT3oyelFxTXk2ekh4Zis0SXpnL1oveHFFRDQ1bnhHR2pzV3A0QTVZZ25peGNUb0JBOFpSMzhGdzFFenEwVDUvWXEvYy85UDBmTVVaM3o0L3FDdWxZdnRMNThuZWdtRmgzcU5LeUJ1clJoSGIxbU5zTllnT0tZUktqTnZ3d20zRDFKa3VZM0c3RmdpZ3hXTDNJc3NjenZmWDBtVGRadkh3aSs2RjlTejVuYitJVWJYRXptTXE2ZElscy8vOFFBSlJBQkFRQUNBUVFEQVFFQkFBTUFBQUFBQVJFQUlURkJVV0Z4Z1pHaHNkSEI0ZkR4LzlvQUNBRUJBQUUvRUMrRTdVeFcrQWVQczR4dW45RlFFMDY3N1lQTzUvOEFpL3pJanNnQjlITmJhOE1waUdJWEFWSmxCVEFFcGRHSGtxUHVWd3hkMnhDOG83eTFoMENkNlB4aFBPQ3RyUjhlWVVudXZubHdyRi85ZjhURzFtYVo5ZVErQTRiNVRiMVJkRDRVbUhqaTJZL2pnWVBNQUlIQU0wSFgvWmp1T0R3YTg5ZUZDdTNNMWlrVkdOUTJNeFRqM3liQmlBWE9lTVNmVWpZN1Fvd2d3K0VEMWlZQ2Y1Y055SDh5am43ejJxUCtUa281M2xmVzgwZzlwL3BsMkJIYU9CUms5MERINU5TQzJKNEpvZFFNRDJmcEIxSEZkdEdLbDFvMDlmNWpOanJ2K1kvOFp6ZXdyZUhvK0RDRWhieVBaL3FvZU1HNFB0T21jd2hrV3dYWWp6MndXNDJzcXVpSzdHSldTYU53bFlWMm1LU0tBdGowQUd1cW56a0JnTVByRmEvbExRR3U5Q3lIRW5YWUpCQVNsaXRqQ1V3VE8rR3RONmRhaTlEQkVkQW5weXJaaEtNR3Ntc3FvdUdKenNWK0ROYXZLajlIRHl6SzlFRlNVZG0xTU0wUkNDUGZiUGd4alk3bXZ6L2h3cGxnWjhzNWZMaHVGYTcrZTJ1RGt4eDJyOEJLcmkxN3lBeUNyeFl3T1ErOXZqNkllMHhFYWlvUTZzVHNzSzJZU3o5NkM2a0QyeEt0WGxiamFXdU1IRHdneWlyN29NS3UyU08rbkNUck1kdDBVQ0s0VUFNZ3lhb2tmWEE2dDlGZlZnN1VjQ3I4c1pQU2pIYWN6dHhnaU5ZQVRvdE91bzJPSTZJNXh3Qnl0L1djYzNyeitzSTdDbWpaZE9KTVlYc2FVZUNmaGJ5QXR2bmtYbjhneE9KMitHbk9Cc01xM21xOW1iNzV0K2pyRWduT05qL01DSWxsdElxUzdOWWtCb0dhTkVpZXI4Y2NhQ3BXaHM5eURIWjRGb1dqTHRWOUZjQU1xSmdscUhmUTQyd2U0MmZac0laQ29ESXFJRVZKdVpoK0xwT09BMEFkUmMwd0JMMUtpNDNzeW4rRkZpYlZkTURDREZFcWswS1dieWxrclpoOTRjTy9nS29JTTdFSTdCMzBLSThKbDFFOUZmZ09BMzdiZjFNdzhyeVcwZzVRYU1RblBlazlWZzJubmlYOHhtOUZCYkVRcURYREJEYWtxalpzR2psRzh1R2tmc1Arc3hLTVBlekUvTXBFaFVnZkxySk5mbG92Ymh3cGJmTlNSc0YzWWV5WVZEYS9IWmw2dkR3QXJ3Q0JFV2xxMEhOZjlHRU5CQ0llWFhYRUdZQkJFMUw0OHRZcnAxSGVZV2ZuSHlOTkJRLzllcGt1UGVQMUZOZmJCeW9ocEtOa3JEblJjQzc3VUIyT1hhQUIwd2ZSVFB5NE8rYmtuZWhqVytPS250Nmd0OUptVlI2UEhLUmpEdHdOZzZwanNPNEtFaWt0UUVES1BhbktLQTg5bHdiRTVCQ0t4UUx3WXlzTHNFdmwvcHdIVUpoOWRCVmR3UFprZ0ltaHFva1U2cWtTRGxzRHNNQWlsQTdUZ3hpN0RBTnN2Ym52cGtldE5US1lCdE02T3VVYWppcUswRVhVS2NPbEFKUXltd2RISVp6QzE4MVJ4SG5ZNHdtRE1Bczgyd1p2aU9HNitWTTVWd3ZNT3JrY04yQkhZQjM2akdZY0k3UEJUeHRqbzM4alBrZjNoT0RBT2VuMkM4TmZHVEp1eXZCeGdtUVlIZ2tXOU1TalFxRmQ5NGRVZ0lCTjdWdzFNTU5NaFRFVjRaYzNGOTlGaU11bVZrREhQWXpnUGRYb21jS01nV0dVL3dBckRMTjRDSFZPSmFTN1NGZ0loUlJqc0l2eXdkVUlqdWtDKzRGNk9GMkRJYzRBMXBCd0xvNU8rWUtBd1ZPTFFxNk14UitlQWFUSUtkeUhiRzltMGljSUpFVG52bEtpRUloVXA2THZKVGNrUzdMQVgwWXV0WFVJMUlteEtQSm1LNUlaVU9sUTBHOEVhUHVvR3NFMmhEWmdrR0VGNUJaVjBZZVFuNDlhQjhZdEhyWC9BTjF3K3NQWWt0LzBYOXdjY0FhbHpxdW80eTFETDNTQS9ialpYVi8yYVB6SjhTd1Juc05ZWXlqQUs1d1ViMFkrM0FQRU5yN0FGK09HTVBRZ09KdFVSNFYrSlM1U09uZFVYdXJReUt3ZFRDRjFqc3hlQVRUMTVCU3RaZ1RacXRueU9uRlVzRGU3NzFIdzVoaHZJbkNsUjgzMlhBTExVR3dWbXpxRFpnQzNPU2lsSWFBVEtJV0d1VGhxOG01Sy9UbmhCWGt6V0FaUVRWam5paEJGTUd0dDZUUXdKcmJrcWxuamVZT3RjTWVVVkF1NEdBT0xHNWFKRGttWnBhbXdydGR1bEZ6a0xmWklvZ2V6TmJPZzR4R2JFV0d1Tkt0aU5FZE10KzMvQUEzWExoTGFLK2pjRGJRZHNVbHQ0cHZRQ3M2T092SUcxUTdiSnBvSE5sYnBpWFRqb0lKTkhJK05LNEJxNEhyb1B1N0IyeG1jcUtZaDRDTEtvVEQxZDhFd1NBbzVGRHE4cElIc01SZkFDY2Y4Wit1R1ZHSWowejh4dFR1aUlONmo1eUVmZ3k3TERRRG0ySjZxZkNvVndCUmNjV0dvYU5ydE5OeFJ4eXZ1MUZVMklLNll0ZUtOaE1VNTV1b3czNGdQTEFsVGVIekZMdUpkY2lVT1U5VlF2WkU2TWpsaFJTeWphTFo4UEdYdVFxUWRGWFZ3Zzg1ZFhSeFlOY1REYUFMZEVCaG9MKzRkekxJSTdtQUpvamxORHQrNEtDRWRXVk5hQWJoVG8ySGNUTnBseDRMWDFJeWxCMURmYmlNUzFMcFZGZTdkVEIwcWxSaTd3b2MzbGcvNjBjQVkrQUYxcmxJN0pGeFZVOVYrc25Nc3FLcC9xeVJGOFo4SWp6VG0zdmthTGJNRWZraGtoczFKM3M2ZEF3Q2NKNmwzTlk1elR0SnRHa05KYTNOc1dFeG90K2xabHpKTmVRN1kwS2pHSkZVc0U1SGVoQXdNYUFJWmY0NTBNVy9mOCtFWEZaOHY1b2FyVkZlMkFGbUsxQ01uWTVIc3RIUlF5OFRvU2J5MlYrTUJuTWdCNzlVemhwVG1obHV4dkxCSENmTEZGa3d6Zi9jUDRXRjE0SXdvbzdQWGplRnpDSC9vekpEaTRTd2NhbDRpdzhlRVVIKzhjOTdqSjZFS0FJSUpEVHVYRFlPUTJybytncDF1RjI2YTRxZ2xua0JKanY2N1c4N0lsaWxseFhWL215UkJKSVBJeDJ4WXU4VUtkSzF4aGgwSE5TYXU2Ymk4eTRxeUJCUVRTWG5RdGxaN0tWTXFGdUdJZlVJQ3JYaGJab01NUzJpb2p3Z0kyakxpc2tGV250UWVQSXN5TFJ5TEhhS2FqVkdKU2ZIUGNLYVBVVEo0b0hLVDBZNk1jQ3g1d2JWOFRrSHpra0J1OG5mMWU0T0ZhcTdkNG9hMEp1WUozQkRqNGxtSEVHT1JPUkhBNEpsUGJYbFB0NnNmYU84ZSsrUDBGelg1WlpkTDBCQWNJS2JSRjhvUGJtNU51ZmhHV25aOXVUSTZ5dXpVNmFJR2pRNnFnL0xPWTBHcE8rR1N0T2lKZFZCc3lBdk9kamFuaEVjaERHMVkxRkZKZ3NFZ3B3Wk5wdHo4dmI5Rm8wWWJpaDlCb2s2SlFQTWNPZS9nZXFLUjA2aHVDcFp1UXZGQ2dnRjJhdUFhekVpQVJnZ2hEVlhPeVRrcVRNTktGV3gvTFY5L3MwUnVVM2FZaUhPOHJTT0lpc1lJYmloK2JpeGhOSlRXaUdhNXVrcVlMS0Jqa3c5U1lxbVJORWVSaUJFMlBVY0NXeEJCb3k0N1RkazBITTREVWZkMGs4MjJUYXcvdnhqYkFQU2E1eE5NenlHQ2dCU0d1OXloRnd2SzRkY1lsSHhxVkQ1bkRGNTJqSUhYdjBaVUI0bUZBdEFKOVlEZ1BNcVZDY0R4Y2YyY1FYSThtMU1qaGV6UnJ0ZjdEa0Z3MjA1dUxVRkNGU1RDZUtSNVA3VXJrMWlzbmdXYWhkUWlYa3R4RUpjQWhCQTU2bTRkcWJLaUtEQjdhVXdCUWkyVFdvbTVMeXNjU01ERkhjRnhmcXlDQVlSQ0FnbXR0d1BwVUlIa0lnK01ib2hSc2JYTlhMaEpoQUV5R3QzWG1vWEdSYm9oYjNoVzEyeGFJOUVlMkpUVENEc3NJZU1MN0t1VWRzWEN2VTBaTHFEczV3cW9wRmxWZ1Z3ME5WZ2E2aUhjWnRYOGJQd3poMkJjSVljenIyQkxnMElma1V6ZUh2SWRYUVhuRW5mL0FBRHdjNW01Z1dkSUMxb2k4aGpRM2FLdGdOekZ2emJsNDJLZzdyMGNwRFIwblNMM3g0THlQVS9Kd0p2N0VqcFNkWHRqUjNva2kwRmRvNzdZTmZBN2tJTDRuRnduNEhablVTY09uQWh1OFc2cG9ZNFhJS09odFJicHhBTTJySTl3MGtzNjVjMDZKaU5OZTk0eEs1cUx5KzhJQ0M3SHdLUnVwREJtWnN4NURLTEM4SmdTeGxUUGhQakRvVldFRFN6a0NpdkF4VGhKcjhxYklIcFpoT2dCM1ZPUVFQQjA1d1FDWUE5RFViWXZycGk1MGhMRzBtaXJFTzlUN0RKeitZaXM2KzhjcE1UWi9aSDhDeVVZakZJaDIvNWxQbzBOYUNQTmNZeHd4a0NVMDZCM2phdndrR3NNc1VOYlY3UFJNUGx5bm1kaDJPdU91ZnFlREJHR1JtRW91WDdZQWlKdDFNZ0FkVnVkQ1JLOXNVdTNMRHMwTDhyZ1IzQlRIdFRIN0l3RGV3b1BHTEFmZTZ2WXBocDZSS2hJNlJ3cjZKWUVHVmhocHdvSzVZcE41Y2FtL2ZLYjVCNTdZMjQyWkFVOElqSHZnc1R0clJvd1psOXpPaWVvSzdmaHhFV1pTeEJLaTE3NUx3L2dyQWtCb0Q2REllakwyRTh6WnlCZGRGQTEvd0JNMjBNaGNyT2N1VmJBN24vZ3lyRUYrNWtXQXBiZEgwcUZNQjZtMnl0SjNDaGxYU25YYWpSc2dCY3ZsSmhiZWdhbmJCL05sdFlKQUFNakNhRW1VU1hxR3NXb3RHNHFqcGVYZmdsSWUvR1BJcGFOZ3dCSytwdmMyc0poeWJRVlhwamxrcEMvZTNOcGFCODV5ZmlkMVg5eDVQRlAvdVJ3aE9FK1Qyamh4SldUYnNKQnJsQ2ptdUgrT01MUWduYUFCc09tNk5NYnRlK0IxNjBFU1I4NDFaNlJNU09OczJieXRlQldsM3c2VEJ6ckFKYWNMemk2YXFhdldzUDNOdURhcEpzWlY1d0VVbFJCVldHL3VVSHMrcVRSL2VBMkd5TitUZ2JYbUs4bFBBOFpvM1ZjaExONGNPQm9UVDNobDlOTUQ2MW5PeHdkSExaeTkxaHgyaVgrc0hLcmhZbjM0bkpNcXlQYmxXYWFuamt5RWEyWHROU0gzaXpSNHRrNTF0cUJyUVFjYW5DSUltZ2l0SUQzeWs4MW1QQ2dRQ3NMaCtWd2VqWWxnNlJaL0htY2NNeUo4ZEh4a3FCQUFQSUxmV0tpamFpL1ZRdzQzSUJmempPZ1A2TTVaRXpLdGN5cmQ5ZUlQTXVaVk9Xc0JtbmN1V0JpVllsUzZWL21PVURHTXZCZ1RxZWluRG5oVWw3dWFoeno5MndvSnNQYkg2amcrb2JWMW5IaEZsRDd4ZnphZjN3REVadDhHbmVzTnREMkp4Q2cramtuNzExZDRSK09RRmVEcSs2a0hHa0E0djhBbTRFeTVUS1ZWVmNtZGRBWThERjVPOFk0cTI3S2hPN3ppdXdFTjVKN1J4MU5WSjk0c3JBKzNnY2RBU2M2c3QwSmo1eGx3Qys3VHlCT3kyemhIQk53aXE5LzE1T3ppam9aQTB2TFRsSG1Pei9HUHdOMDZaUHR6aHcrMzhab0FkUkh4b0g1aDk1K3BPNlUvY0FBWkJKUE1BL1dCR1BiK0hHTnhIcXFYR3psY1RPQjZEUlB2T0UzSW81cEVjSzdzNHhYdjJUaDI5REFrdENWK09tYzRwNktGME9BOXVLVFl4ZVJSeldvRno3d2lPM3M5bml0YjJNTHlYOWhzSWs1OG5oaVdCQVdJaS94aTQ4clVFWTlWNUUzaVpCUndoSzUvOFFBTnhFQUFRTUNCQUlJQkFZQkJRQUFBQUFBQVFBQ0VRTWhCQkl4UVJBVEJTSXlVV0Z4Z1pHeHdlSHdJRE5Da3FIUkZCVXdVbUtDLzlvQUNBRUNBUUUvQU9BNGlGSDRESVFnaEV5cklBbS84b21tM3hXYW8rMmdUYVlHdCtBVzZLY09zaVFOMW5kb0NocCtJSGhQQ1NpWldVblZTM1FYS3l2ZHFtdGEzUkR0Y2JCR3F6YTZEbk9OeENmWW9NYTI0Q21ScW8vMlpqVkZ4T3FEditBUXBFOW9vQU5FQkFBamhJRGdlR1o3eGF3UXBqZTZnTjRQN1M2c1hLemdhSU9VaFBwRFVPbE16QVFlSWxSS0poQ1Rvc2tQQUtkcWdTc3hRVUJ3Z29OQk1GV0FQa21teW1FSE1LT1pPN1Nma1kyU2JvT2JKQjJLQkNFeVZBUUVKalh1TU51bWRGWXh3bHdEUjRtRU9oeVIrWVBZcXQwUmlHQWxoRHZMWDJUbU9iVkljSWpnZnpBbmE4UktBVENTOU91Q3M0aTVXZWtQRkNvNDlscUhNUGFSN1NOS241cUMyOWdtM2xjc29OSVZERDFjUS9KVEVrckRVNlBSb3Qxbkd4UGY0Tkh6V0xwdnpjeGhNSHUxOVhIUUpwQk94OVh1UDhXVDNHQVA3K0R2N1ZURFVhMkdIUEU5eDNDeG1EcTRSOEc3ZGlqK1lFY3JqWW90ZU5wUkxnZ0hHVVRsVnlZQ2FBQVFYS0tMVnphVGRBdWVkaC9DRDNPN1FSN1VMS1RxNWN0Z1RHRnM2S0pjcVdGTHUwcVZLbmc4TklzU0pKN20vd0JsQ2J1ZFlnWC9BT28yYVBFckN2WkJvVkIxZTdadmRKM0pWWnJxRDhsUSs1Z2VqV3ByYzdnMGIrZndjcWhHY01HM3grOXRDcWxKbUpvbWk3UTZlQjJWV202bldOTitvSmxCbmlnM3VRZnNDblZIR3dRenVOazBaZ1VXMk1GQU1HeUJPd1hYN2tBNmJwdzZ5RktQMUlNQmNoMFZSYTBrdUtkaHNQU0FEYnUzVkZyQzhOQVdJTFgxWU9ra255WjlVQTVvekc1QXplYm5kbjJDNWJzMlZ0eURBOFhmcWNmTFpVNllhQm11UnZ1aUduVlZhUmE2UmNIN2p5TzNjVUQ5L2ZvZmRQd2VHcmsxQ3dFa0xIZEduRDBEVUFqdnVxWkVCWHVaVEhkWXdoWk1BaE9UTFNwUGV2MWFwejJOaTZGVnBmQVRxRlpvRU1Ka1RvVTZuVnBtWERMUGVpeHdhN01DSUVxcFJjeXNjMzBXRWEzbnQ4MVV3MVoyYTJyU1BVdVgrTzQxWnQybW5YWUJZV2dXdmFTUVlCOXlibFFGQUtxZ2Nvb2ltSnY5M1ZPTnZGZE1DY0E1TWFXMHkvMFVHYklOSUtiVHFscGZGZ3NMaHFtSmNXTWlZbTVXSXcxYWc2S2doREQxd3d2eW1DdWpXMHhnMnVnYVhzbXRvMWpPV2ZRTHA5bE5wcGhyQm9kbGgrclhhY2tHUnNxV1BwVW5ac1U5ckc5N2lHZ25ZWElFckZZTEM0MEF2MjdsVnFCN095Rld4MUp2UzQ2T001aTB2SGNRREJqeENvc0xIZ3Fzd2M3S2REbWIrNjRRZTR0ekRVZ08vd0RUTEVleXAxRzBuNWhvMlQ1c2R2NkZUbWFDMDI0WXFxMWpPWHVkZkw2NkJBazYvZjNKOWxSN0k5MTByajZUc082bElKTm9SMFFLa0lPbTBvRnpIU0o5Rm1ZQW0xM2hzTmNZODFUNlVyVXFmTFlCSGtxZlNkYW5vQXNWaksyTEF6eFpIV1ZYYlJ4cmVWaW1OcU5CbUhORGhJM2dnM1E2UXhZRVp5aFN6TmtMRmRHWWJFWWluaTZqWmZTbktieUpzZDdnb1ZHYktxQldvNXA4RDRSb1ZtT3M1VFA3WC8wNUEzZ0NJOTJFNjIzWVZWcXZ3bElNcDJPcHNTQjh4S0hTR0pmWUVlalNmalpWaUtqQldiNUdPLzVuWWJCVXhtT1VmZTMwSHFWMGhpNmxYRkUwM0VBV0VKejNPdVNpN1NWek9wbGlBc3dJaUU0dHVtdUUzTXArVTZKZ0FGeWdXTEt5eURRR1NEZFpYSnRONE1vVVdtbm5MeDgxUmZRd3dPVEVUNXB1S3BPQm11QjZLcFd3UWs4MEVyQ1kraG5zNEh2Q3EwcmN5bVpHbmhIYzc1RllWZ3ZVY0xOMG5VZUFPNEtyVk0xVXVkWStJTFQ3aXhRY0g2WC9BSE8vb0xDdkJtbTdRMis0c1BJTEhZeHVHWWFGTXk4Nm51K3Z3Q0pLRjJvTkpJRFVXT3o1ZEU3TGZLc3RwUWdTb2FSb2dHcXlhNndUSFUydE9iVkRsa0xMVFRTQk1vRjBHVUE5ekNnSWFaMUNqcUxEWS9GWVY4c2Q2SFJmNnhnYXpPV1FXanlCSHNnN0I2c3JBZXJoL0YwK3RnV2RxcG04cFB4c3NSMGsvS1cwUmxIZnY5UFJFcXlFUWc1MDJSTDgwblZmcEtiTUlOa1NvSWRCVE5DbkMwb01scUxJQ3VOMTFodWdYb3pFSnhBMEt6dERTSlhNYmxoU0VJaUNpL1lCTmNTVS9zbEFBbUNnMEVHQXU2RTBsTmFjMEk2Rk5UU1lOMTRwam9Ubmh3VEtnQWhaZ1ZBNFFnVzNReTVEYTZFWkNwT1dGQjFRSlVwcHVuOWxCTkFwc2tyZVUweVVDV1ZMSTZGTlFGa0V4dlZWUUFCRmhBUVFMbG1XWUhkQ21FNW9DUGNyQUlTb1VGTkJEay9zbFV3TTZxR1RBVGdtQzZKNjVsR0lLYUNnYlFnRUhXdXFoTHhaTWZtRjFiOEhOTUl2bmhDQVVLbTBFd2JLU0RDZDJWSkNGd29RYkN5dUpzRUtibEVDQ1VMYUs1MVZsUGdtdGdxRUFvNFFvNFFod2xDRllzaVVLYmlKaE5va1hKaEJyRkxSb3M0VnlwQVdaWElNcGx5cXVxeWpMS2RxcFJLbENZUTRRZ3h4MFF3MVkzaUZ5R3Q3VGtHVTloS0x3MjFnalZuVXl1YVNpVnpBc3ppdk5DSTRiS2xxRlUxVWpscUpVQlF0a3hwS2JRSjFLTGFETE9LNWxOdlpiN280bCt4anlDZFZtNXV1WVNzeEtsY3diSUY1UWFFd2hSZEVpRk51QTBLcDZwNXVqMkFwUjBRMDRmL0VBRGNSQUFFREFnUUNDQVFHQVFVQUFBQUFBQUVBQWhFRElRUVNNVUVRVVFVVEltRnhnWkh3TXFHeDBSUWdJNUxCNFVJVk1FTmlndi9hQUFnQkF3RUJQd0RoS3NvNUtGSDVOUW9LeVd1bXREZmhYWkF1aG5kWVdRYXhsMFhuZ3dkcE5DWm9VYkFJWE1rTEtPWCszQWRaTkVhS1FGQkk1QkFzYm9pNXhHcVB3cUZaTWtIUkNtWXVnQUFZUjBDbVRDdnhBbmlPQTRBRW9RRkhNck1Cb0ZKY2pJNEVXUUNoalUxMG9Fa0ptNk9nVU9ua2d3YjNXWGtWa2NnUzJSQ01FL2tsQUlpTlVDTXRrT0orRkdRVVRGMEorYUlKSmhOYkN5SGE2YWpzbWt2ZEFDTWpSTkVxMnlEM0RkWjArcFRZSmRaUDZUd2dKRENYRWNoSy93QldqL2pQcVB1cVBTdUhjWXFBdDhkUFVKbFJybVN3eUR5NFIyU20zSEN3VTl5THRpUjlVK012Tk0wMGk2REhUdWd4M2Nzbk1wb0FtRTYwSU9JTW9tVTc0WTIrcTBFck8yWVZhdlN3N0Mrb1lBVloxYnBFOXF6ZFFQNWNWaEhzRFRUZUJJNTZSM0FhbEVRT1hrMXYxdXFiWmt4OUQ4MnBsZXJSeEI2Z3h6NUgzNnJCNHhtS1pJMUdvVXpUbE1EMkNDRUhNT3BoQU4yUmJ6V1c4VC9DTURWU1RvRkx6eVFhL2NySTNjcG9hSmhQMldidVFjU0ZWZlFjMGRXMXdNN21SOUFuYUJQcTVaSVZTby9HWW1OV3RNQWMzZjFxclJEZTBKdC8ySTFjZTRMRU1lU0sxSTlybnU3bkhJQlVYTnJNenNIb0pQbTV5THNyWEhsNGZWdDFSa3RMei9sOUI5UjM2aFU2ajhQVkZWbW8xL24zNEZNcU5xVWc5bWhRZFpGMHFHY2xsYks3QVRyUWdYYmhSM3FCelVCQVFDbmFqZ0FoMGxXSkFEUkNaWHhGVUZ6ckRZS3RuREhPSjBXRkJaUmtheDgzbjdLV3VKYUxENFIzTmJkM3F1c2JsTDNXQkVudWJzMGVPNnFWWFZIT0xiTk93MFRNdytGWWVybWJsTmlQYytJMzVoUjNlL2NqMFRjVmlLSDZZZVFBVmdla1JpS25WbTlySVNtUkJUUU55aVpraEhWTlRpUkVLWlltV1pLWVMrWUZyS3EwdGhOeE5Na3k0Q0R6Q1pVWlVuSVpUQzB2YmxJTW1GU3JCMUZ1VCsxalhQOEF3Ny9CVXNSUmFCSi95YnRzQXZ4RE9yZ1RPVjIyNUt4VllPcFBEUWJsdTJ3RmdnU0pRSkdpb0U5Y0lUUzg3ZTdLdE55ZVM2SU1Zd0JBaVlVQW9FRnFsZ01ibFZYc1lKY3FiMnZFZ3JPd21BYnJwQTFQeFRoSlJOV20waWZtVjBBOTVGUXZjZHQxaVhNY3dqTk51ZjhBU3hHRmluRklFazhsaHNYaWNJWFpJdnpWQmhZK3ppcWJZd1F4RWJ3ZjRWZXFIMG5NNXJEdkpvNWhxSVA3VEIrU2hvY1duUUVqL3dBdjBQcW4wM1ZhWllkWFFQQjdmdUZHUnhEeGRBd1ZnNlRuMURVaXcrdjlhbFFOdmZ1QjZyRWZFUjVMb3pBdlpYRlVnZ0RmbWdyb1NvS0lscUFlVmszSUNmMGRUcVZDOTh6NHAvUjFKK3NyQzRTbmhweVRkTzVycVd0R1psdk1oZmdjSWRXQmRaRHZCVU1YVWJUZFFiWU9oR2k5VXB3OWNzTjV1Ty9ZanpDREJvTGlQM00rN1ZBaTVtUjVQQTc5bkJVNlRNWFZjNnBjQ3d2Qit4aERvN0RNdVFmTWlQbGRVUjFielJQaU5yYitBNTdsUE9VRng5KzlmUUxBWVZsTERnUEFKZGNvTUFiQUN5eE1KckJtbmRGbzFWS0FicHpkd21ieUZMQ0xpL3lRYnJLdUpudTlFRExrQUNJbEVBaUFVU1EvTGxLZFRyNGs5dkQ1Zk5QdzFSaHRSSjgxU2JpejJUU01MRjRONzZkeEhJOGxTcW1lcnFDRHJibnpiL0lXSmNZeU5OM2N0RDNrYkVLalR5MHd4dHdPUkRoNkc0V1hKcmI5cmZ1VmltRUFWRzZpL3ViK1pXRHd4eExoVmVJWU5CejkvTXBvQmNpbnVhMXN1Tmt5czNxZy9XNmJPVytxYmN3aUJLWnZkWit4bGkvTk5Mallvek4wUGk3b1g2ZTZCWklSaUxkNkpBRmtNUFZxRTVHa3duTkxiRVFVU0FzUmdzTmltRVBiNWpWRG92R1VhbWNPRGo0a0dQRlJpd1lmU0o4bW41MlRLV05mOE5QTDR3UHBkVXVqbUR0VmptUHkvdnpUYkJUeVVsT0RjdmFUV3NOT051QWdKejRkQVZJaHpaVlJ2YUI3bFFNeUVYd1NFSG9PVWhTRklEbFU2ck9lcmNTUENGY2haWkFsUUFFU0JvZzBJUWltMkJXbHlyRkVDRTJDMnlHNkVwMFRvcWNHWUJUbTV0MVNabGxHbmRaVm9oQjRhcURDTXhkRnd6YUlQTWFBZVNDQkkwV3lNSWFJbVN0b1JCSWhOc0ZNb0k2cHFjYmxVU1hTVTE1UENHcktGbFFKaTZ6R0ZtTWNKS0V3c3pSclpCelhDeFJRczFOVFZLQWtLSVExVmtIQldPaXBqSW5NeThaUXVnMEl3RmRTVkpXWnlMTTRrWFRXQUdVVU5Gb1pVZ0JOZURaQndHcTYxdTEwMXpqdENNSFVvUU5BdTBWbGpWT010dHR4RUlBSUZTZ1U2L0NFSkZ3Z1NpczdHMmxkYzAyRjEyejNMSU55Z0JzRktEU1FnMElRTkFpU2dJYUVMQW9DeUYwQW9RVXFWbVFqZGRkU2J1dXZjZmdhcHFtNU1JTkJGeVNnMW9zQWd4c0lBbGRXN1UyV1ZvNzBPNWFtL0JxTzYyV3hRTmxkQTJRS3NFNnMwTHJIdUZnaFRxdjFkNklZZW52ZEJyV2pzaUVKUUNEQ1YxSkh4TDlNZDZ6Rk9EdGltbXlIY2hyd0NpeTBDaXg0Q0pVOFAvL1oiIGlkPSJjIiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIvPjwvZGVmcz48L3N2Zz4=", + "description": "Displays the latest flow rate telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'flowRate', label: 'Flow rate', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flow rate\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"horizontal\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:hydro-power\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#2B54CE\"},{\"from\":10,\"to\":30,\"color\":\"#3B911C\"},{\"from\":30,\"to\":50,\"color\":\"#F77410\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":28,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#2B54CE\"},{\"from\":10,\"to\":30,\"color\":\"#3B911C\"},{\"from\":30,\"to\":50,\"color\":\"#F77410\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:aG9yaXpvbnRhbF9mbG93X3JhdGVfY2FyZF9iYWNrZ3JvdW5kXygxKS5wbmc=:aG9yaXpvbnRhbF9mbG93X3JhdGVfY2FyZF9iYWNrZ3JvdW5kLnBuZw==;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAaAAAABcCAMAAAAfz5G9AAADAFBMVEVGdZ5GdqBLSEQ/PDh2dHBGcZhFQDuUlJBIRUB4d3RjYV6QkI9paGY7ODNEc51bWFRMS0gQFh9pZmGdnZlfXlyAf36NjIgjPVt1cWxsa2hPTkstU3h5eXhWVVNwbGhcW1kyV3xxcW98fHxyb2o2NDAXJz2srKs3W35WU06hoJ4mQmJJdqCamZWHiIeAfXk2V3iPj4ukpKGYlZNubm5lZGMvWYJjX1mBgoKFhYCJhoKysrKmpaWFhYUSGiZRUVBCbJWLiYYyUnQpS299e3cMERhUUEtgW1YgOFaFgX9IeKIrT3Q3YosgNU7///9nY108aJHEv8CDgXtuaWQ0XoaNjI1yc3MsSWmvr60/YoV6dXCUl5anpqs/Zoyqqqc7XoETHy9aVlAzT24mRmmZmJqJiosZLEMUIzf08vZwiqi3s7N8eXQsOk4cJjQbMUp9n8HAvbxohaaioKQ8VnSUj4zp4+e6uLm2uLVAb5mQkpX7+vwyMC0/Pz7Bw8XNy8zg3ODMztIvQVkgLD2dm55pa2zY1tjHxslEao8jMUQsVX1lZ2i0wNeJo7/Iw8NSS0RERUXx7PC+vsL29ft4jKh0dXiHmK/f19mQnrHR09a9ubwcISvm3uKxtrtPfKavrLKfpqbY291ahK1WWFnTztCrpKHu5+vZ0dMtNUOeq8JRdJqhqbtfYWLt8fLn7O87TmaZo7WxqqWUm50mLDa1rq3f5edMZ4aQqMSDnLilrK0ySGI5OTt2mLtbboXBubV7lbJFXnuSiIOtutKaoaKBkqtwkLJjirGnr8G3v79Tbo5kc4o4Qk+IjpN+hpCrsbKhmJLQxsZcfaGis8x7gYZ0eIBWZnt5h55cd5ZDVmwrKSi5wcvIwbxsf5skIyConpfE1OC+xte4uMA5SFuvuceckYyFeXVpcHpTW2jCzM5bZG++sLKLk6GOf3pGTVhsXFhfUE1se5F2al58a2nR3eackICJdGR7YE6ri4i9pZygfXBvQV7Xym21sCHIt6Tcz5nDvnuyqnpmdz8FmWDnAABydUlEQVR42myaeVyTdRzHlw3HjkA2NobMybZ4YLjNbVy7CGRcAXE4YGMXwjiaRoSwiblBEUPKmWghpBC2SUGoFF4dKB2GZYKVpWKYZWXaffdHvXr1/T3Dsld9tue3ZxMU9t7n+/18f4+E8+dffPHFLF/brpZ99WOTG+8Cbazft2/fwYND7Z6ey6pBDDOZ1OqmJnVT40GXydHX5+n31cMXbW83qU2nd09fuHDhkzdGD+x8/PHXXnttx30DdxZV121eaY0Iy3ziBcPKgrCw588ZogtSM+/5+LghIyYzbPMqcxFhgBCfmhl2z+aI8owopO5oXdeZI4cP7/h8XXxe0Z0rYgsq6yKSonLqCqK6C+CsOiYnKa+66MzOy3a/pdgrUzS7XD0Oh1Pl9HpAbjfckdrbe1KEg01qzKtXNTodPS4Pes3n69BOWEQ1ZcTE8LiloCWLFgUvylqTTJQyRKTGycnJuXESiSR0TZ+WCVs1HDZrNS3F6XJMVayVrF1bopyaar7SPKWooCeviYNv3pq1JruwMDk7NGtR8OLFi4MXbc2KC02G52uW0VnNuTJM7fH66nddw/ROR6NTJrLoMb1epbLb7Y7+/sbiYq/360SKYt7v9cKTm9SIhFXcunTTpqwl+I+4NFxCePp5APSNbxiXe7iz7WDLvrF6HBSiMHZwqK8dfvCjJlOT92CPqR+e9Q/DnwcAHR1945PdgOfATgAEhO7eMxCS1HDPC9a8usywdcetEQiMAT08AXyqgc/z+w353bqBE+aCBx+oWhkdlREf0VAbE1PbkPPCoR2H7319xGBN26BL6o6vq4vojiqoy+nOq42pToqAozoq6L5Rld8y1ygsUXpcPdP9DqcD4bmhdpCDR2pqbXL1qfUqhoJhU9mBkqufL7PLOQ8lLl8Wt3TxrcFbs9nK5lyulpNQxuGnNE7aJ73jJCrV9uRzNRIyjydgU1cniKddTiV7LbBRarmU0DVZWxGYNWtC16yJWxK8+NZbEZlFW9dkZ2eHLk/kEsnMZoYM84vkCJA7AEiFAKkZGE7HgegAEeAzl0yhKOf9xV7QzYDQMbX8tjuCBc2Fi5ZuBURLlhBePL9p04uPMmuMTSZVv9eNSHV2dva2bKs/uHfbWP32jQFSG8f2nV4A1BMAVH8VOQjHA3xACNANA8XmZGY+v96QgTABmLrMJ946bigHPk/vN1jLc6pD9uwxr4yNLs+JqQzLxBWWWllXG3F8ZEXQyEBRfEEemAYRaohB9mmAEziq80deOz1u8eKAgFDPtGMaHnFEAaf4+lSRpA6b2gOA9HKJiIjZ7RN6jJTeKNSUEQVrguO4RG3JlEzu6FMLJADoMQ4Amrw2O06VUkmlpaxmxrx/ft6/mkNyFTuV9NDsuKVLgoMXAZy4rKWLgjctRmCCkYeSk5PpREkuSy60TNgnJibsdpVerwZAar3aPbvgoEZwEANDePoRHgCECDVmUygsAPRvAwWejk9J1OMTjY3jXxYmx20NDiYseREB4vD5HPJjZY+lpIhLja+aVA5LcfFsL6itt20XVL999fXbd3sOTgcAdQKg7QCoafQNwIPzCRDagQxUcM8LOjBQ6gOG2AKEyRxVm5kKmPJqEZ9D1viYmOr8gT0D5oyGyhts7nlw8+bn76msrM3JMw8MpBWk1mbghBbsgxe5+KTqvA1ndgKgfn9u7nBfp8/ndqnEamOHCcyMGZucnr6+PizS2AEv+NQYRtI2EzuMpRpmCtOit6VriVmPhpKZZTxys1/u7JMJJEx+iphqmwNAbj+plCpsnNSPWyzjs77GGbnQAw5KXrRkESg4+FbQ4sWLsrLp7LVaiULZbGRYVP1gAY+3uN/hcOAlbEKv9zMQIIvb2weA9CockFAFX4DjQQjQMQmASP5xeOH/FUA1OXGlIjmLcH5TOBidKBDwiEQyn5kr1tSINWKxWKMRnzJ2PDm4u8fj9oGveneZAJAaAXKAgzZur/cMmt4YBT43AXp9z50bMmICHejB9YbqStSHdDmpYU+vN0c3hCE+sRGVYTHl+Wl3DqTFI/M8sTknvipWl2/uOnno3FdvRVQnbRgoyoipBELltQAF2ae7ISavu6A2KioeLIRZvHZZSUknUtuQQ4vZhCZ3U4dRomAx+zr7hJE2oa3J7UaA1pJ4CQk8XplG6bTIScrli7O4HDK5jM+SyVXtjEjJk0hHAdDkrF9OJVHF6rVGkVq1r97t0svFRgC0GAcTvHUNnV3CEgktervK3uj14PKCYAVC/Y4JVQCQGgHCANBwAJDTAYDsjhv+WfDQtTUUCmNi4n8BwYvwRT1wwPc4LemE+++//Y63Pk6kRfKIfL6WmatUKhW5ivRS6oJKa0ClpRrboNp9cDcOqL93+/aN27dfHTwKfG4GdOzEwC35OXUPGKJicAM1ZGY+fdwMmO7Zb7YCJug/sREAbV2sWWce2GN98InND1RZizaE3HnnnQN79py4774zJ63mkKKkpLyCygbkIcCE7JMRk9OdgYpdXtB9B0xe6C5TOKChTqdEb+sw+ZqELK1CCoA6W7mMjo4mt0utBweRiMCj7DExQ4+pSRV3LEpm2UwOz/CURaR3C1eXXL58elBlRw7y+UVGo2KtmkgiPakaA0DwzQqVgp6VFQpoRBONXlT9fSA3CMdS7PLBG4m/8/0OVQAQhgDJLGoc0ASGCKmEyEH9IFgWME2EAqDxmx0EVAJIHM4ecKQT/jpTR2trB2Yn3P7ii9lPfUWj0RITE2F9KAE1TmaKGAiR5CASdE9pM4spyW1yDwGg9vbL/W3b6+vrxz4ZBR0Y/cc/KCJsiKp9cKU1PhUZqBxY7DdH1yJYOCZU3ypROzJHV2cQ9gxYV1nTCHcirViBGA2cOHFmZGAguiAeBbiGvKT4uoYoaEMZSTlw3LCQya2yvfvJwR/a26HK6bUWIwKkFpNLpExX5xC1wmJramovBgdJJc3woeMzNXK7WtXDIMatSdm7bVt9/bapcRHmYazOtfvHJyYnkYMAkNwooTdViEjq3WNj7h6MJBE7FQIynT7jHy8GOJ1Q7ztBwz6PqbXmOXXpq7tb1FQxh5dQxrQNIj52DMPUM/8C5HQgQHh+Q+pf4ORfTkkU+cfRE5DTrlINDg5igxCXOx4qO9pBFadIJJxIMT+3Rm4nQIb7VAGtB4dRo2FyOJwyXhncoX9y+O/Cn9SUUuWi5mb5AiA3Dmj72KXR0YCDgA0I8TkBESG+bp01ugExsTaghJAfHwZ9qCuqFsAY8gNxwbyqoDYetaEQ4AKC9RYECe4D8OqqysqIAKHuHGQfKHJ5MQXwCAWvO2jknR9+uPT4zkuPjF5/9ehRNRMAYT6hXrFWoUw52jdUI1HZmtSdLgDEkjSTORytJF3uwBweETmbbmpBodQzpWfoAVBp277td921bQ4IDfsZDNHqYCGbRFKrECCLvESqUhClkZQZf7PMhLVv73T1OAc9vb39dq6AJ7eIGbvHhOm5RHp2drJEjACpLBY8xanVAKgTAVLhgBjYhMoOcjphUYEGZ7IRIBnMLh22VmqN9iEemSfg0uh0eiGFQkkGZS/PvpXDYTJLVQBo0ww2gfkn7BBFEGqnc9rjdjlUmKzDWMPnP/YY0EpIeChB2eQZOt2EHOToHDt46ZGdj48G3IPYoGMHGIigK7in3JABbqky5+EGisINpIsIQ30oLybApyG1rloXMjCAg4EFVgQogGhFfsTfERvZB0VsZCHoQt0wCwXd/coj79zdNXLs8OGdr4qpCggJgz6hmqlVKFhY+5CYabeZTEMueLekJc3kiopPP22Wq9ROJ4mcTLf0NCJN2YVOD4PWiifU+huAGIJkAEQ1qca2uRstJIVUX0KWsRNnlFyx1OiqdzstJDant3fSzuXyGHqxbXpMxlJUACDKVLodGchvuTYvEuXKEKDhbb0TegRIL6SWllKNNhuaJBERm9GYm5xIE81LFUw+h8jmcWk0emEyJTk8GamwkF5Ih2JGP8+DSEDVE1589NFZ3zBKAUiuyVloenY7eM/V3jl0cAjm1T53I8ZgiUkwXJg67K4Lo6PPHLv77osXdy7geQ0EKwypUOHyYjbHWoHGOms+BIXNVjNuIDOkuuf3d+kKANPxLjwurO8iABkQ+AdxugUeVtyCIBEI+fEx0HDyGgIZAZIcslAOnFd3x+uCgrq6Rrq6gkaOHTuyEzT6aofd1yHjkEsUpaq+oVJNj23Q1NaD6TGWQqqYn5m5Mi+Xqfvt8rXJ2Y5AlGKp1A6PPJJ14Wr7pUvtOKBxGUNYoRaxjaUYOCgACJOQU2j0mYrVYlara8yLAPE7Ox12LptssaczLmwTspQAKDScJXX2F/f7GdNtQ/aZXJlM7S72/Q2IIU4RM1NqalqNrVQQ0KJKKLREkVyarszNzS3RaslEAZgnkRKO2yc8e3k4zEmhH/KI2pQFQMNIMPyA2lr2trTt6h1Gwc03O1vscMC0sXv37tFXr799fef1U9dNOx8/8voHh9a/+doN9yDhBjqxByocRIToWghv5sAaC3UODJQDQduQD33o+XNdsTnA51zXir/LGw4KHhEr4AMqKo8BOBmQEaDIoZgNFoIgV5uDhlUglJaWFhRkPvTBxR3Hjm058pzdJxSSyRIF9XLnQbGyx4aZ2vr1Kku6Usoa9/t/nSCphXa7lLgsfKErN9vVPW4SV6qpMWJX2+Ya5+Y6/TIhQ+glsamlmLN+r7tRTS0hWbRE+HTPrKWJFaWusWKVmkpMaeudxLg8DgNK3HSLUJpORg6SyyHozolSPrrrrt6ZEoZQ7WkEB42Pq+xOAETqUGtqSgGQUG0ahFDisE9RaDSZy4mpbTAE8IkJqwtx/wCgwkQ6jU6LFAi4yz9M4DBTqCoAtHQ2QAfn0+nubLuhXbjaPzl85NiOu+/eAcfrxwDFzmP3GaLL37/Re3A+uIMgw+ly6qrMGZUQEYrwFc5xA8FaZe4GZB8bDPEoznUBC0BygxECdAsgCzhoBRDCAxzaRwhYCA9xEbVReeVpyEII0KH3A9Hx2OP9bqGaxymR2NxDB/msHuPgYJsDU1mUrHT/8OzsfKsckzmLSRXZ9Fm3exbCcbPd5OqTRkojmVS1a2hubnYWARI5vM1sUo3JWd+CA5LKyET26sj5CpqUKb66t1Elo7IB0DWhgM2x6NNtu1uELAWZvjycLjf6ABCj48kLV01XPmUwZN7J4W3D4xY9DkguxACQ+FRN66tHIQ3AloyEFkmDDQuBAEgkcLlwgk85PDaZDLFTy5GkKJWFH9I4Wj7VTtj06Dcte/e2tAAKYNI5ee3aJMymC3DQiwdeAziPP37sdeDzOkJx8eShrpHj1xcAITy4UMgO6a59MBZiQRjYJSKwZv6zApl1x6HaAZ9DN9nnhoUQIPRAQJDM8YH5B9kn0IEaasFSsN+D17guAHTy8KWAHnF5hDKBVrHW2L7zOlnaYzQNttktKrWSpZyA0jDeOq/uMDWStOEUN/xCvb3DUyqTx83iSlfzAVDnHGAb9ssYAIjFZeWeVtXvcxerSQoWo4JH5Ar8FTRWifjqrn69TMoT79p1TQgl7qhebJzehwMKXb76SisCpFapMBPjylqRSOZtvBmQTa0Rv8uErYCysrIEiMkJkVwunbdWq9VKmCW5TKYiPV2pgNGTVSqVwl3KokJyppxPJJM5rROEXyYmIHlu34ir3jsJFXlu7zZABsyQhg4g5wR07BgiMnLo5HHDt9CAUDjAtRMXAErLiCkw6AoyU18wW1GF60qCtaorOgZCA9Q8CNpd1oKwJz4+NIKsgvyDDtxAwAdnBIBw6XICmzy4hTKgA8VDXGjI0WV0AyAzAtR19zOvPPIIIvTRdEcHT6sg8jWfLyWSelpNqiHMopelpCvsvcPDFs28SG6zsyQUir1lL6iXpTL1tdcI0ukcqaxn2Ds7O7sASCkgsU6rxg66G2VURbqIzUsQ8MahB0nEV1vsmIwlEA91qq5w2Ry9XmO80MJQEiPDQ0MDDvKaIJ9hjOZPjXLkoL3DfgjxdrteBO+75uy6z1OYfDKvjIhMQhTwIksBAgmJWooaEwtWNNeIRAyhzGJR61efT4QU3aon/FoP2la/DVc92soea6vfuKDtfRcuHHnp2IJefwkBOkY4dPLQy9fxeA03JMjakOiOnMBbkBnH0RVVBxUuLa8SgkJROYoL+dUoOhRVV0JOCFpoO/8I0KCDcAsYiIA8FBLdEOhACzNQd1RMhC6iNikqIw0AmYuCgtJGzpw5c9998Lk5cuA5AFRC1IhFMjLJQTUNdqotmFDCyu0HQH6mTJkrln1ZkbxG+Q8gD4unpHBKARAqe8N+oYjRWMyOLNWYjo61IUBKhZHIowEgIl3B1yBAQpZA4/ZiCNBpu8Z2qYWRTuZSloXSmo0wvParj0LqZ1z5lEoVzgGgWQCkB1OJxAoNZ334OnG6UlkikaxlC1ZHEslshgwEmw6YHnYh9LCN57Dr9XpsEMVwFdbUlH2eBiOccZBwrX77P6rfiFsJ5lAcV/0jJ+4+cATSEq6XXkKA7jsUe/K+i+8DHxwNYoOOZ5555sgevAVB8wl7MDatOjWswFCEqp05PwdZSteQeQ8KDWFgoxUBIrASAjkbJWzcRSEbQhAhsFFaNb59UJCE26cuQ1fQAKd5SRC0uwwI0MjImYGBkZEg6EcXW20JEgWRJFOrtDZna5OqUybDhExWibett9fPG48UVDRPVRTGUdyomPua9Vinq6ZMHE5OF053znrnvD7ICAyXlxvJSnkSq0eApOJcEpsXySb62YkpZPLVXXaLkMUT9/djM1wi/7RKbPsBACmIyctCI+WvHsR16er0/JUKFosxeQ0AzfvHQRZRCXv1sxGff/xUXBxcnsB39hK1kpKenn774KBe3YRPQ8ZWI0xEpQGJNSl8TvCHND4nxeYnzNUjbccxtexqgWKHbmjZvrH+8InHDxy5SVDUPjh0/OzZc28fWNAoeAfRGX3m8Et74ErDgyvN8YDGbI4IS32gK78gs7K8SFeLQkNUXdjmWAgNsG9KQLH6FuCyoaioKARZCdggSIQgs063AeGBc10Bmn0gvDXg9oHs3Z0UE68r1wWlmQ3mIKSRFXBL6+r64BQpQaJkiywYxuwAQP0IEIMpVfh2tfXKBOPZkQLJlIAetyYXdn+Hi8mtp/c5ODR+OFHcMd3nnQsAErb1VnBLNZhj4y4EKB0AEXkV5HkuTcNJae9VYcJ0wal2n30eAKnsua1X9zLSlcTwZaEJDA3k21PvwlQjIf1YoVTOAKCWuXlEyCIjcbO2Ln1w//64uNBQGGphyOFx3n3vXSOVCntoYg0zF6YhzmNkKH08CA4JCdxIGp0SvvzWDx8CQMZ5wmwAECLUgmahfwy18a59W07sPLAFwGzZAuu99x5BLeji8Qc2f3UdccHhAJoAoy0v7dkQFQOTTw6OpiGzsipNF5MZsz4tqhI3U+oTD4CZAgYCGEAopEgXlVR0C77Rg+wDttFFRQEhBAgsVAexui4C7APdpwF2yDPyGwryM6IDgAiEIAKChDLDyVNUnkQhgGtAQkWH3tjU42uyYAyJVOGG30jI8xdSBFoltzAuK5kF+2hNROqFMQuPVracrOmY9s01Qk8HAzGG3GwBteb07o1t7mkZCwARyTzy2vnQ5Rou3zV7FAHC6se8CJDDzqRe2MtQKtjZccsSGER4R5fF3bF06ermqe8VXwIg367fZ2bm/X6/7Ioyl5mwLj7+LOg46OU333z55Ze/ZWpRM2ILuPTEQkp4+PLs0NBly5bdcUdWFlwSBJfd/iGPQ2YaZwizYwFCoLFt6OQm/XDk9dHDW3ABJBzQjjf3r1v31XvABIcTeMC15fWBDRl1BWZDQ1hleVosoFmZFl0ZttkALSgVoYHOlJZUG7Z5ZRDAuQUd4Jek7vwNC9UNmIQUxcbmxaYRcIEfa6O6axuS8qD7xMdEJ4GLImp1UVUbAg4aCcIPlBlOniqFHhTp7OvbrRGajB0un9CCibQshdPlctkk44JQ7toKYmjc0rjV0tMup0XVPqTWlvEBkG23bxLmoGEGyKUSRFJrsNMb2zwOIYvFlMIYRCZKkguVPGa7T28BQOT33tOPcyv4jgmyZrpFpCjhhmbFRcrZdDqXu5orKCROffmd9tMrv/862/v7jz/Og2aaP1u/alUVCBY4ORTQy09lIRQAAwRlD7QYtGnxpk234rr/POzzMuVXCBuRbngG3W+cgy4dOfbGlgVA+PL4xZfPnjt39s3rozf0BlwRemP0mTfeeObw6wNF8HkvAjR1q4JWVmbWWtMyUsNyuooiMuvATIDGCv0JWAEQuIFpcEC6DWhAhddAhCJrdJQVB4SexMdU6yJi8pB9MsA+BQ355cAJJiGz1YDsM4LuBGhJh07V8PgltFKSqEYjkwEgdxOGybVSRaPTCVc1i+XZyxVcbvaSJUvXrJX1wJYnXCTuaceWkSVUva9x0js7zBCBgzzsSJbYdLo+AIhPquAKiGw2JVESCSUOpTheAuXhU29/8cXZb988+/LdR948+zlcIXnrqy8+O+31zcK/9Bj/y5++/u67L3/95fc//vztt99+BH35xSqk8qqVK6vK168/vt5qtRoOGb4CIkgBLItxKLAu4LnttvO3c8BBJCXhrv8V0MEB7Rzd8o8A0I4dgGj/59cBzE16Bt22nBiAEPdCWnRdWEx02irUiOCKT2p8Wn7DgplyzBAawEYrgA+esYOKklZF54fAE+QfgLJiQ350rCEE8QEHQcXMATLxkLijo5F9YnTRdavyyyElWK1pQTeEAL2t4TEVNMiwHIW+ydbhcasx5KCU09MuT3tnX1vJEu2X2cnwUV2aLNXD1sh0+9DQkDMRakiPbw5dSmCIRKJPnpTwqFRVz9+AyJEPE9mJFIo2IaXdA5uT6UTOy8dXrYqOtcbCYbDGRhkOobp1bj1salw8aTZbv/ju561bt/7820+//Prbz6Dvfvrp+y/WrwTtr4o9vv6FlbGxVbGx1uMGw1ebbsXhwIqEyGy6yUC33b9JS+TxqV8SblzVHttb/x9MPxwZvQnQ4S2oA539uGDzZ4OXL1/+5I3dN/ggHQZA+RFglZWVYeCd8tRU3DuVGRsMMeiFqtTUeAgNYbWxQWCWBaUZrAbghQseUHYzQ3fBtSIkSFdQq4vGM9wqHXSf8rpoHVzre8EMSKxpIagDheCArAhQrpJGSS4UaFQdAMgDAZYkYaEdVCVJeHpaFhrKgha0adOSbI3t1dYULVxPadXQySmtl72Ns3PeWdjMpvIiS2CzVO8a6/Q4helSPomYeAeR+Gk2RUvjXy3GMKGy7DNUp6IAEPABQNYPznzwwcUPVgYUvXLluexvbl28qFBbMXXl+2++yf4aUkHy5wAItB7JGguyggzngARuHBzPzVqM+Nx+/xImjcsh5RLsfbsP+CAiDg11wn/l2Te2b6EjbUSAtoweOAxgAnwObzn2/tkXCgrWfdXkc3sA0D90EKB7T9wJF+tWpVVVhjUYkHci0ooKULWLrUNmKg+rzAjS1aYWGFB8wwUPG4IgDqBzPCPgMxCsAYWEmCNionSwg5BXV52fU5u/qq4qvzaiqFwHgGKLEBvCTQ7KTactWbSEluuw2WyQwvQWqUTKCV0eTqFzmWIWe1EoN2vR4tuC15D5KSlMGBWJZAGdyC9VeRvnABA4SMplS+CCncoz1ul1CpU4oFCi9ko4F/az272YhaHgfYE3EgCECMXGnvwAdObkSlAsfpyjLAteekdCTa6849PISEFCJC0x/PNz+/eXR1Str2p4IT6AB62xjyIywbgQodsAy4sA5jZct99+26JECieFnvgXG+cC12QVhnHC4dglLjI2xrgMCAYT2LhMQJCxoY7YYsJgIg6Q4SgcZEIyUgoG6jSLLCujCDOLTCVEsSCNUiq5aKEkFpVhSmSY3evX7VfP+Tag+vX4fWcfCyv3533Pe97zHJ1YuV6vnb8wdOTsrvo9DxxGvY0Lq6DDRJePv3wcXGYBnR4fwBTUN/BZPZxWM2zmAN2CwlgCQIQHjyQ3Q+XSMqELSW7b8EaZwiIqW8IzgA+okLRmz3UQleEWUnzmCLm5yPUIGJ69PBCHFolC9QaVRlsiogBRRdwsoAQA4oc/7MxP2ojW/tbyGCSkbGmeM5FHYKC7vzucH+Rn1ZnDYadHp69iMEJyaOnerI0rTpJmXCR2Jku9sxmsgsVfHNq7tcYrSupN92GzaennOPxEftTeh8KSIwFIUlVSIilCiiOfseEipW6FAOFDJJn0ueG75sZVdNB//v7q1atfZWevy2luKS4za3g9PSaxXiSSgY5QhO+tat7cjItocNquyd52ot7e9i5UDh4eBJ3T1prCtqbUQCZzOYcTf5AZF7S2cfVnW/cAz6N3fPcSADkQ4aV//J7HHruJKagRlcEMmHf+DUhEkhsBhAWQi8G0NFTkIllCgkmFmckiLC4WawGIYMEwgwcvwAMu1DUrN7miUq/WVwp1GpW6xIQXvVasMiiEFKB/z0F5cdlpbPwQ8hMLcwtK718cszI5JZvOxob+rfNcfcPx52NS6QOY4DZjop+/IIuzKtqbVbPi5Hr0T+ksaekDR6NpLK/F9YceAKCkJD+pD5u/Kvsckx/Fzt1DAGUETYkkCCEyBwkRCd3Qtu4z+QIHIIFwMirAedmNr3766eeff/kBExCmoG9cW1oqGjRms9lq1QPNnETkhjAnIechZXbbGjRWq62iq6trMAtt7ZwQH4bTnS+sX/38/St+/hmr3v2xb2b7+PgE5DCxJxHP8fvQDmgmgC6eOXNirEFTOfaKI3owjoyMvESul14CIFllqGP2kas8AUiNWrvITbGkmGQ7z1CRBXulesvtYEBRASCMAIMRgOxQ5vjc4mYpEuu1Ip5ArRerRTyhVo8ZrkcrogAZkOJwzQIKSkxcsCDQN86bnsIqrVsdsTI5MZHOX/aw88POrph8mwFm87xb54evWbYMS45wFLmBy/mM4NynHkKOe48AKqhPTg9KKQjb+sieusVe2dl+9Bw2n5F4LoufGL92y4ow9AQQQRKJQiJyVAmYQaFukUAIITAAyOfr+Q/f+OEXAghsKG1uqSi2thRXwLSkFwq2w6YphLYLBRjsoGbUZ+6w2azWrpauisGDBzlkXRTudOR6ctu7u3Y9s2vPsRcOEb8bacc98sAD9Vu/2HX5ZWoZ5AD06oH8Hk2lzTx47Z0rV94ZwS/QuTLi0P8Acsu3epry3RTFZXqXbeYWW76bxGqrcmkFF0p4BRo7J0TOf4R052JQKLT5SqFaojSolTqtQiEvUsqLJEAiMbjMSp6fD0DZ2bTIxto4Zvw+vyD/VN+DOzhBLLRqVvlkuS9YlbMcfRZn/2XOiKFwdnZ6ljtZ2DPTCaD1J7FLyZLmej3RmUFLi1xZ/8iWhwAoIziSH8/P5n6+KCSRmbt3RWGMV6IPASTAXUTKBIfy7eEjolLcjYfnO3+dc/Xqm18RPgHgk7W5paW4uBhDS7FCKPiH7F/gd4E4LqGoxNwARNbB6faudoQ7NRc5Tad+vvbavn0czsGDTfDx7b6QMDxUe+RI22uvvVt/GcUzlqhEINQvy9SLO/pGT1yH2Rd6i/yya+StkZGdAKSxFrnoEdByeUOLrcrJYO5q2OYkajdXHXDpGEQHobsHGw2zgObwOOhQu3VzqNxcDBKB1qDcrhYq1WqlTKvIlBcZtUUKOyDEj9MMoI5Nm+Y558RsqD1Vk3IpiM/093Cdhz+dtPxohJS/Lp0dz/YBEqT1ZUhvgeH+2esC4939A/381j614uTJ9VtP0qVcr+c7o+MSuRF1aMUVxkZn+6Xw44PSpNXu7Gwma8vG5JjIJNok4gcihfasihyA8BFLppfNR0r9+gYWQze+/vrrNajk1vQWL1kCPiBUoRAIMjMFmcDiGDBCEiLg6tGYNBNm6+TktLWr117guTrtPf+mX/m78Pa2tR05Uls7NHz+/IVrU1Mnbo6Pn75y5aXHT39AdP3JJ3feg6Q7cH08v+qDDy8/+NZ/RAHSNZgNLhJzQ9UBp6oGoHCp6uhzWbitqq+7daGhr/tAq4thm8vtrY4ZaCEapXMltiNqKM0BUhrVOrFSncnT6XiZWqNYK1HlF5UAkPCfEVSUP3oQk0xwQWNBW1uD54V4Dqo3DoxKeblbauLYcYXwuXklMZjY6o9jhmf5+GQFx0b5+dHcgxhrCx4CoIdO0ulS78nR0Ymx6dSm2OA4NjMwLyGKzaFVc2nhzHVs1t6NMRH0bACiJISJL7+vm+jMme4qAHNoAoDmP/zjD9CPDi3ajACyC4AABdccIAiveB9DJgFkM09MNk11tU+j7eOb6p/qdMexIdp7h0+tqL/3yru7tsysU4+NvN7/9tv9p5+8Pj5wYuDm8NDw8PXTF890D7z2/nbh6cuXHwShBynhiaJFAGklkgMLt4n6trW2bsvfdqB1ITbWFrY6od3c2mqRWxa23mKxWEiN4DSDBcOMEYG8CSj/hOSi5qnUskqxTlwpk5n0apXGUGIqEhFAEsxBbv8AFJcHg2JwaUHb9Yau8/FobjH58CwyEpIZjLjsc3v27qmLSVzgvsadL2WdS6pel3Y/Wg9J7vy43PUAdPKhkyjignkqo1JgXNIctmrTxx9jpzkhhADyWcCkxUfVl8d4SaODAEgIQGRe755oqJJ0d4+eODFK1XSUpm987bom66vvoV8Ipa++/+qHh1uApriixWbT2PEqCA0FqFABpYAEZMjMnLCZgKijafdUe/tUoK9HKn7M0Ek4Esdv3N25+IkLIZyvHIAu73xl9ZfPfvDccxfPnzhw4ABKyfGbj/VrBcqBZ96XyU6Dz79FAB1/+3aLFjzctHJLa6uLVmuBCQvj7W5qtXbh7XAjym+RbxfAzEMIzcBxdBH+Zw5CkeBClqcKuBNUoSIBKm5NpUEfWiTUE0DquQAigHb7MYLTs70KCodVXecXcOKZC2gM9BWSorwZtHOddcewxtsaQ1uwgFZQU1OdGMLdupbRMKHR2E7tWXGy/OhD5XR65AWjOHOywVimqRI3tHx822aPqCAmO3twcqJhwnzbQRbHw8M/dd+ofcEDROMdPNEY9WFL8qmKIR/32Dc3li0KeZP04D4nlGBZedO9pYKSdQlPUtJHAM2KgKHYUEPJhJloCklssP1CkG97c2/zlNMd9x359s4vgvavvj+I7+8AdOz4Oy+iWDj0+D33DJxpXUh04P277pFliq9dIYBGXnlphAqguRgCoH4AUltul8tkOrfbtQKgcJMZlWo3rZGnlFskGpXQDctNoxyAQGVOhBcVQP+p4tDrEZo06pIysUwTKhOU6dWVBJCOACqS5P+zSCjqi+fDBMPwyk2ZNk+6u5MWpC/cgUHclDS/6M6VMTWweG7IDUJPM6HtC7rnkqbkXD+VymydeAatuKPlW8sjAah7fGR1hziU12csafl4U29zbdKCg3k88ZRKwyueyvPw9Lx0a689x4kw5VSNlVQ16MVio9GIMg4RBEB9HQturMmiSfd37v/kHOCcQ4OCy+mqsEI2s5U3NjYxNlaSqbT/KlES4cUh/aA11BZqm8I2xODgcNyasalNcakUoPu+i9u/YcXU5PS3hyhAIw8+cR9JdJ8e7794oBV6DnqjH2bCay+/b+g+vfPiPS9d+SchO6BWrQBuNzUyhQH7oZVGOTYMTAI3dWWxRm1RFpcpLerKJSq1o5FAIM2BmoWCe1ZyNOPQ4NHLiB8BgIBLDEBUipsDJAegqt5N0PKYQI/BsR14unTp0iaiwFxpgt/KjcmlXqdKpYkMtJz5QSnBSz2D6wtZZs1E+/T9W9bDUrjiaCQ99tSRnnvGeQ2hKqPK1HLptt595XU1kXkNqo6mLl6ouEHT/vGlTb3Xqki5BRWV9IyNVfIqKzUalUSACIIAiLl8TZZPmgNQLAFUze5yzEBlt/5G1KPX65X2y4hHo96Ii7rNNlNlqG3y/NDw4OBQ3HDtEY+hYKfVxwigvLaBMQ2hPHb9hTvu2znyCDUTfXf5jQOtb/e/Dt199/HXdaKOgVfGLw6cftXl4vG3/g+Q0VRiEZmKK4vcJMXFKi0aPsVGC1mu6iwK9OLIasgkQ8gAyCwbe+zgdoSQmxv1Qm456ZSS3SCTCRtDJToA4oUWKUpcDLMRZHFE0D7X+c3uKZFpjOZN4e7oHLgvc0UV5MwpaExuWhvJ9UtISYAlgB/A5vOD4pZ+vHrX7uDJwebUfXu2rD+6snPjUfRKpd7XR8bj9znvS9qE8ElNCONEFSYGNqg0oxOqUJxishJAFxRFEqISPc9kMlWWlZXBwkfap5QkHQfXhDODkiLC9ntFQucw0mldSxzaZAdkFCPuxP+jBvMgVrNjTVEX2geH8qaCD8anxjkdOlvqs/fKFHbhrES26dFn7zv+6mE7oE9fd4FfGnTA5/jOA1iojo71NAzsHH/sJQD6d5I7/joAhYrJvkKo0A0NuEq1i3KmIyewiNDw0Wp5nogjssU9C8fiRq1TMeBNN+wIGQxyJwckrbhCj1a2QhCqIQ1TIZrbKpNOoQAgkdplTvn5oztCkmp2ZUQ2BruH+HAYQRwmWnD+Hps9Sr2i+XEMPuxm8RyIyYlnB/CXblpcFxjnRzvo7Fy35anFnYU1nfgkk2jrNl/q9fHm+64LDHf2q4/1jYr0YacnZTB8u3qn25H1buv1Gy4hc4hErKo0Wa2hJgKJV4LSGzGFG4DCl3FCEhrD9p+LheABiWVFb54B1PIXUQNQ8P4rccfk6LUL15+pbZqeHqo9D0BxTX5xO1J3ON1xaHh0dKDHam2gAJnB6PpI/4N3UoBG3lcvhFkaCQ4l3fsihZGn4qlUA1ihzqK5PBtCr7fK4f/UGio9y0pc8kNJ50BCNh1IT9soR1fBpNMq0eshOW5GmLXUFnvsYAAYuUEo1AEaAglFnKpCgegRZmJbVVUmFJTxdJXYE/oPIMu2ovwT+3x9Nu6KjZxKoK0LCZifs4C23JnjPn+za246jIBsD1f7nhi6+Ztdm+ct7d2QfDAuKOCg+6WzezbCU11eiP06Gr5h0yWOlJXRmVbzUWfE7n2siMTs3cmdF0cTubFvum6+zePaiRJlpn1a12s0KMs0moYSfGWfmICuZzLcN5AdHJtceA5u9lh6bCSdnpTaQtGBeA2z4jmee3pUKh5v7Nr54eHh3LVDQ0O1KSkp+9oHL8Bw3RS0m+F036HhgZtVNhygAptQmyYUhEZ39h+vf+TR7x6/2KfX4qOyEC08IMtE0szv7vvgwcuzAqGZKoFseZsID89isYtB41mW6aIrAxW50tOTp9WqlpZlareHLqmUWW4HDCqI3OQ6oRCEZhs9eKNIgC1VipCL0GQTIXqERlInmGT6Yr0MWU4vpABZ5lpxRfkDQ5GRyRGJNL9gBjcnKyY2gMHgh9CWh9+6gx3PjPclJ7BIhxg3xkWXbmtG0zmPH57qXPBQ+eLO/YsJoJB57lm9fOLrTPfacu/hT7w9/Pnpi3Z/dK7rki//NvaqkIwLfUhtegVAYIpX8TSYf8RKBSVHAS0GIH+2Hyum0IsrJS4qAOIymFiB4Ypn79jBb2pq4rP5TTs4+y7s3rEjbnfgQZWq4xrogNBaqBa93ty81MFruyHY4p2ufDo08IG5HT0gc0dVVQlIAVX3+28/h7M6BlHVpEEN6XQ69S3PvX+m+wwa7AMffAgrGjSb3yA0e+4mphHTTK8HmIxUOzsTLdKllTqtEelOrQY3pZbkNCqAsMWtE+jkMz1skuF0QoFIi0dcFiVymh5wVGXK7SaS5ZTEPNJT5GIQFant9YF8mwF8DCcueDOCAtDg94um0/3SaRnSGOJiKpTm0Gi05WSTf/6taOjjQoefu/xWP59FIcmMHBqXjmMJK2NWdsKTkJRVzeWnpKVJCyJWwuF4f1qid5RXZPJH1Yv8OfRVtIzauq1tsBPAU3CTvEydGJ2cgs/gxJwGToxec/cNj6ellcZ4paXB5+YFQNIMH2IiDYCF1MeHxlgXnU6LY4QwU+NoQXlx8c0HpyeBB6X1+ai2rY21UGlpbUpwOg3eLBQ1Tt/VDw2Po/rWmPuq+nBpCKGOvu5t4+Pd3TdfOd0t3E4kkz/3dv9jd4139411TL48A2e2jQBR7WxeqEKuwI6d2kIwadHUXiLWqk0keATYJ5KpjXgbe6iUYLIyiGTY4qaKODdHBAklOov9Wa0K1ctUFfrtlVYBleVC8aLXidWoCorUchewwQPqJwMApVOuGG9WFE4L0rgfrViJmCAuwGNb6rcSbdmLowAYoAeOQYcPHzp056FjW/bgVE/Myo+gBw79V4cP33vvMSI8wHW7ZS+59uAg7Ja9eMQTDpeTU0p7tpCv9tTjbNfGdI9UJv43vCKTMrj0SAoQlzoVxyXOxMiImJU1ySwcxAr04DTl7W7i4IBjUx6O+UBrYzaGFbJYtbXBu1kFLL88eFBD2E6Pj/Zd6zZDY/l9fbhKGszmhh6Qeu++Y5c/PX7xgF3PoZaDs/SuA/hMTryClDbTgqPa2TM7dnAR6OVFmH4kZBcI2U3v6alRawkmNXxx8BVsN3maBJZbZlKcFksI2AucZjIcQkidL8cTni0Cq02IW6KkagQ9nIxClAoisVaeL8pXo7qW2De/JIrRoYiIxtLYAvrKxRn8EHgXYTHA8YHEjKTEjcfug3BafdfZ1yJKG0uhSC+cOni3rq5+76H77nyeTi+IXVkYE1N4/16QADcIL+RYRz0ErBDA2OkeA13AgQjow47vB2tKhw59tMDDl8Nn4LhVUnRSNaYgKfrk1dVJGUlRONdQW3DkbE0yOdWDjz+PHMUKcG86vzsuLyElJTFD+lBNWCkrtoAVHJTQ2Mjyxtlab2+nT8dHL1zs6OmZGIOTAfWq3owUB0BVJ+949LtPR4DFXmZDMDCCUHffietolY6gme1gAzjQzrvfbiVJyWDQEFuPjoQNDApLQwVa5RISPGIEj0zHW4IywW22SDDowAOiYsguF9wElZpX0SATV/Bwi0Wo5RRWFWkpKPTybUUOQIpMpR4Fa8fQM7vqKNXHBoSkS71gn40BoKQ0nNo+u3F9Hf4egVJWLukqBDOCg4KlXhFejTGNrz1Tf/iOF6q5UtbZSEznXsmFZxd/Vod/09a6d2vOwoOwIaatrbDtVEwMYrGgNBcf7yk0kXEEjlVwhMXl5uZKWQWnTuG91cloZUIbGhNzHvbwjw/y5lZnrMvmVoMOLL7V1TAv5oSQM8AJCRkZKQmwAXt7Z2AEo+BgvLs2F3NPxPrO6jSctq/FqQZY7GOj1nK5XKc7Pn3m+l2DDeZ2c3dVX35Vnw1CiuvpOUnK7JG776boQDsBCK6RuzAJ1X70x++vOfbpKDZ23xx8V8JKzDaEhEELOylPS+xxRrXMRIJHgN0hMi4xZVJuRQIIE43DggAidkIznORKq00hqLSWwLyokNgqZfoKvcxm02FTyFBUZCCASngqFKhG/dg18gdNwI8hnUaLloJORKQUTlsYEhLTuZGFG+9fsZqeFMVFpuFKq4kfGo5ob++1jWe/OPYAGkLeYZ9gyRKLf4AZuhGu4UKkRzgZI7wocFhsSvF7uGlRaxEFlNsQP9zkc40OhvAQHO2NjxzvpXsHLXNOZQb4ZSRlrGIkpsEKT0eSkwJQVo4PIx1cgoOBBpZ5/BZynJHhx0jH2wkphHcUY1VSMtBs2LD6tbDkWPzX6E6PHrvyweOkCWQzkzmIZ8Oeq3liomHyeXQSLr8FMmBE8QGgx9+45+KZvirvP/74o/MVO5kngcau1/ufc1NjEtJmIruJUBQsrZQR72+lDicdSPCoyCjjYcd1u8WxCpqT278EcOQ4v8hYYRYpKzRCPQIJNbeAOOSEcoMIgLRyg6RHrFcqBMKO5mZsbMNk5s6nedMjCmHSzcDaJz4rngk/oXu8dy4rYTdWQGx+QByN+NcZ+GDwGFxw9otngpriGMnn4DI8ip1vHB0oP1oIA2QNOeuF5ZEUxZjX/s7y9ZikVqw/iTOMRzsLYUeMScYdE0EYegFttbSahWMJ3LS1CYtcPTg5NL/odB+f9CQQokIoaVVIzjdXk9IgLi4pQgOPGHDh5laTQw1pKd7pq3y4EcmLy+EHDlsZUYt/p9MLX7xy82a3mYjUB5iBsCNX2zb8TlPEoRlA0EyKu2fhNp1g4Pc//vjzScoLTIY3HMIhfLJUNYhCYebRwj1f5hjVmcUIHl0mno267TA2qmQgBCr/2v35FySLjldhliCA9EJVBblLtptsQiOCSKwjU5CWAML+s4L4ZQY33Upp3oIAWm5MIewI67LC17i7rwlf5o99IRRu2FZ1yNkhAF1ECy59JqJpBztoA8ItLSckke4Vs78QCa0UORABFFmdloEWbBrdKwJhRQ5rb8Qx7Y010FMYF9cshs4SzzullWGF+KHPc3Z1j4/LI54Hn+hEwAAg6ZtXv8n55ts3q6tZAIlTDdUQYnlOsUiGXPiAk7Kv7ocddkUyfGPJrFykuLrXTp2o6uuwoTSwWdsxDk5MHr3jw81fR2S99+lbD87A2UkC6Q0Aer+7r2r0k19//fPI6TfeOE24PPbGY/2P9ffjjAp1yFujgyPbU6Ump1B41KjSyTCS4EE4ZepwkBiHhC1uBM4cGPvjLB+1uMJWIhRXmIXIdAIcQREqK3hCVSiWGlpMQQatDqWCoqRjYnpycJ/vvHmu8zGE8/OCI8JQCa1jhq/B3wWyIGDRMix9UFqjzMYG5ZzmETUvX5XSyNqxI57dlo7Dxms8fJcBK2c5h+ke7h/u7x4IcZhZbNTHsOjSVq1LXwfXezY+ReCklLsWR+eionCRG6sYJET6ovkoE/hYB+fkMPC9aVIASrv6zaKsb76ikIASiwVqeLXDwTMZpMAUEXb088hjj7yAFu7J8kbY6FOcUhJOTfV0VPXAr2CztZsHp5vK656+o275t4uZ7z34zr8i6HUQQaU9fvMm/c9ff/3kMbvu6r/LoX6c4EJnJlOtX7I0VIjR07RdrUTwZGIkwZOJ0KKSHCGEechOx0EG11x+U+utXT0ipdVaguARC3m4EUSgL9LrSeTkq4VquahnYqJ3M6wG2MbmOIfPm5fj55eLv00iJhsWOA/nNQiSzbfdtom6iJ0JniY8w0cCUZjmZwXnJmD1uGPDKkZenit5k6yTPPxhLPFdthymDVin8bcmwcLwMNwmHo7DCc4QvsK3QOGQv787peXsDavDwjKcXf2ZWO/mZGUFMNKzMRERQDfgm79aTViAF4QZBwM3l3yFAaJjURBWvvHcV/c//cB760+uLw/yRu39N1nnAtV0HcVxGgu2Mdscgz3YAzaQjWDKGOM1F48Ya9BwG48GmkNm6oISyUJXyKMQSqAkM8MsRD2FlIqVUhlZlikaQb4yrQQqIk17n06nTt/ff8uoLmMQiCYf7v197/3dew0Ygx3DmXPwYAE5fY5JnsE9w/6vhOb7edsfffUSPOe6D5FBoZXPLzy6fOM3f/7225+nlv/HjmAGEjFOkB8ZQ6QBBrYWxzmSq+FIyclwqurWZOxPQN7ZmgdCea2FQAP7Hx/iP0tiqhrmWgqqGmKJVMha2phlaUSEAyccQXdWxKEQ3OrIAqCvdSjdMHhRUca10R30FIV6xZzSEGEuR69UsfzX+qhtgwoo/WP4BIEUKBaaFVEiAohezGQQvwIgrljFMVntzqEfcKXjsbqI9+ELCL2/fc9/HY0HwYWH3zh1UPFuThBXziPFA5NVRpfYIEwoQCa9k4Q7hLgiHDu+0yghE7NbCW54YngRwQQPHFIueuihp8trMhJ1TAUAfXfmDBru0W79xsGGhi++OGacJpXsr8y1K2TbH33tPQC6bkiEACg5efnG5ROIcSHXyawkr3g5gk0w6HaPJEP3BURRA0o+JrZiLI5sKq61YnkPRrIiCmZThBDmKD7/AfRAYVbjFhxAeeQUglSAAzVExgEO5FyEIL+lAoWPAkFFycGXvv1iskYnFqPxrSNQ3Pe1OcyQvhpzQSxTWqqNFa0Xo/xVtauHQzPyOCqXSsXFHbLcLqRLKtWGJCmNFkizpqVmpoWFZdg0Hg1c8e/4R1XuAAAPoPHBmWmzfLj+Y/i6kDLMcssYcCFSmAVnkZCpMYSHpwGQS+8MJ46D18w0QwIxAzFzSoLPMjPxlOa0h2Lsq0lbWckGIEPAWI9Uoe4u0276xthBKzbS2q+ROun+Ie1q0YePvv7ivwG9izzozrfOj5z+0udCoPKPARAOIei4kvzsKr+uhgu1woV8cQ0TpmShUgwhNH/2XQIQIlh8gc7/BDyon1rW7wIKcgoRqWBpbMyKIE4UAzlXUrgQfQPcY99++fWXXxu/Ha1J0vEZQdGzOrxtimBJyILSRVohR+Rhsjk86S7Cpy2NxTJyjB3cDm8go4MmzZxXd2D/cyeKWOJcRi5nAmdCmnkOkVEyNn8tg8/WiY00hQ0LKoKCcu1Kp5AKl2DlA+OPlDMM/3ndgrrCYEyOl8vRm/gmnIMsnpKJ5UtpE1aTy+RMS6BOqswEQ6oBr2ZzChHc5lSYOdVHS2O3h5KZLvhilIQAapZ0TO14YrJH2MVt+/n2+uM9P98O25wWulr09NvvPgVA/xgFaOFRTBG/VfvHH38eX/lfex4xzrK0ABFtti8pRdnAARfC+ROB5QhLLZiRI4RaIwR3xeRZ8pGpPvAv/7nxgUIyIeSoqACfxoORS3AK+R1IgLclrYLIWza+/2Dwnj2Jc7TNfNXXxyt1PA6NMSuo7di3RfFdIfcmJmaycTstZ+tS6ka/PjNcHhIuV9pSkIF+lT6JdqQDaW73if3rtil6VGIs6LsaGhqcaS6txSHd3KdzJdXmRMmTysNFHC5t1qdeu6iYRWMk5LD5KrlOrKOJ2UqmjcmGepdjeIRyM3gZl9tOA0JiLLLwQMenMUjzndyVK+WYWCImlIUTgPRCggWI4DnAQQAZCJgUAwXITAClMK3ORSfKa5qa1E0hEuxOCkgyDKL1hC29NsYZHVq27Hjbz2v2719zbjrxafbUR0cOAdDr/3gQtNrILQ7sPJp7am9Z2anzK8//i88REuMceVDUJYCSTYkCgSM5j6BxAM38gghcvIFQXGvrkjyB5QEsL0vOLwQkX2iD80QsEWDhFd4KqnAKZa/HUwlOIThQhQU6Ozsuv+XUgYy02nHtwB6RSmVODOHrXe2MQEav2amtKy5arQ618abqn05fvXnNms6HH3+k/lmt3Hmy5plObFC7iB+9eiwhO/Dc+2VJxlxWd5M9qDYRFYd0XN2E9u8IN4diMvvpBzcsmHDxgqI/bf/1px+vtgv3lO7BrNzQlehAPpNZef/9dYYEdOBfvC8jOFOBW0A2Fc7kcpYdeiIXZjSifA71IFXlQkhI+Wyh03nVRM6gMLgMIQM3S4EhPU0J81uORiHBDYMI4bkGB1BNRnlTCFvP0gW0pUSDvLJnepI1Frj259G+n7//2jj02UTvvc3PfnTk3ddeRynhb0IUoBfuOD0yknV+5QhF5/x/Xeimwmzsm4iEC/lOoRjCCf4EhbAYB09EKwghvFkwDRx729bY6qV5SyyRDqpkHmlZggV/MQK0KOaXVPlUAgLceuoUEsCJ4EjZLefHcOdjUnHGp9i0XP5QkZxBZgFoIqEm3t1VNEerzeFt8k3Q3L6s/uHn6p9NM9JPlj/z9xLJDVEpXRmb0xNYYlfz/ts3tWt71bWZK3B1o16zRqtZ8CZZwpdo1Txz4aera2W//3Bh3Fq7YgDj9AMT7Spx9AF9lLj5vs4d8ey+5uN86eS5rx4ZkhrlxsN6hljqsQqZ7VwaZUEMsRhDqZTa4+h41iu5LpVJFkVMKRKxKdPp8NDhLsKKVx47qRjFbVc7swbzMyfSu59tkojx1QE9iujLl02apCtBlycH10bTei7cfnFozdsTiSvayj86cuTQ638bAXRk+cqRjYeWH/3g6Pnzp+FIMw8gvFAxDiWAxhLHkioqri0Fp1ZH3OLZRCEI5vsIxUAiYKWio+U2XEqQdX4x1QWw6pjFuNbad3dFbLLDYSm4OyuroGoXZDaVC+EUWo9TSOBoeWcsjI28RFkbGmWyss1uE7d91tmzXpZdOD5PUfdsZXwO70GUMDcPv7hm93NY/VCu4XtmABpQphSXb84Ik4tZALTzSuiF8aG0AZRjEtesqbRtvr0TQ6ybnrh9Te/EUCDzzdBF8TL1gg8vTC1YseBNJXdtt/OwdXLHsh2bgoyD0sC1k/2d/eOB3Lb3d093KMX4nqe2+weyyJZGXxSk4R34OEJgOxQ5vIsyrhiqHTD9hg4rNpZXNXNyc+agu6UG8z01mTok2rkBOl0fUrW+Pjkfr3jSaQ5Pso4Xe9SL+mo+wq3Quz461DMAAcrplafPH3rtMQi3f+y6nkOMw5YQcgrNn19NnT8+XQ1cRBqgiGBpXbJ0NolyhTffkC/ARszrNpvgObhqbkQ26FVUZBdQuSqeAKnBdxKVFLZ8jYDCMhFVxvVGf2qtZBm9UNKBHClvPFhSVKoNZuq4XhgSUa+3g8vFd0TOS3JXlnd//vgj6zZXKuYlZWxXSzhiPv+b94quXBj68dq1PUNDQ/O634+3PXx9gu3CdG3Q6CfqRVolAP08tb2+/k1RbnR3zjbe2I5lnZuCcE/gjR4jgIIYPf27pznMXLvi62E2xB+10ZSkTXgwfENAVIbly6JmlDTw2s7NRRol10EQFItYrKiijDkZZsPxjO6ahDCPxMPMCTD2DLL6vh0MGlxL4w765iG4+nVPXQtV8xVPPn/zyHIQum5k5d7z+TfeknzH0dMjYOWDM9OIC6H0vMSRXUW8JhlosOaAKASi4Qih6uxWS17M0rjkm297AJt8BdVVW7ZQF8L7UAN8adXc7Li8PHIKZRdsaTyIAwhSIQ6RDvkQHCj2lg+GEzSYN0WeziOcWDI57kVxKPOx8CNEUjQnPl7Do/m+KYh8eERjcs2fWpLnJEVw0onHg0XQrGKxVHZ1GgmKdfzHiQlnjiZBsxnbr57o70eL+vi0lmYAoGBZ/L0bfICiVNGrwxLto50EUFsHmzE4Clcd6mDwAagvXsVLmRz+5ppP7WFejgYDKyoTi54FPKQXDA+Y0WiU9hAzcjuMSc1tXRKR0i6LKk7BfUimwY39CJVdbJQRUc4I4ALIt+IOn4nxDlfaI5/8Er+BUdp25sx3G0dGtmKL28gIaUwYAZWthfm34AbPYlk48i88G/2EbiOnUHVEsoBCY0GQq4Z4a5w9f+mSVhC6FW8jIAWWLNx6W1ZeXLYFNwYHqct5XOjOzSKnUCNOoXzUevYdBJZ9DZH+ggJAlSw8OlxWaw7L8XiYHibTKUQJRma38uwwnnA8RFFUDg+yuhAbcBgQcRcIHYw+BD8fxJmkhJDmE0+XARCMyxJa2+RS6bXLVy+b8FXtHUY+v7mvzV2m9kxoaQmfxPdmWjO1xb8OPLSpfkoGQCnbeAmdy3bsZDRzRVzvMO6Dpjtobbt3TycluuwA9OSm6StX2tspTccgfyAkOejQSBEJU0pGshGaTAGhagNrrJo9exZLVzyKOyHsvCyWQMalHK8L6wqLMlS6iy4z6+oC0rHkgqy3wFuq6peO3X/pWPTssznpr5EKDuSVIxnmWHgLrpkdGP21CPIsL1B0/AUfvPiCHFzIgT4yHxqi2mYvJuKtykcob/Hs+ZAIrRGxN25Fe8n8mGrBEkDKglWUxOUtXUydQpGRyQ7L3ZSKg0oooGR2QVa2ILblg+OVIWaNhwmjO4mRcRlq1lc4cTI8rKi3Npiut9OFMiX5qCbHoxQJMexOV4KjnqzwFWrMSSfeb4qSQmlhUkhI6zDKdQZM2+hdqN/hRx/RR6zS05XT8fqyT+L3hAs9GhtRwWlmuowXRk9huldv2LCHlyRli71jmEic4NKad++eUGRI7QmTw091dpJdGTjItmNBxr1TA72kyXR8aHri2rWrl9FQD8W5LyaGdGuB0fp9Zz9l8GWKUZRzqN2Q7uPE3F2Kps8+u3Sp3X3pXEBzc5uOD2eTGmeaGJGbeqt76tA9ryMB3XobtkygyZqsX7klGQ6Eb+vRjTNsuR/RSrjQLRHVZGsvCXKtyThmUDVojQMhKgHCDw2kgqPw5puyYiARyDpmqn0p5q7F86lTCGEOv7vDMTerBHxWZUElZMGJkBmVFB4drqwMSc3xAaITREohMQrQeG1mbW945tXLTtQy08y2NDNKlXhX49SLablyHt0cHoK69Bz1HDSJBKPkT5puUnOEvDAD297WVTpQej/1U7ooUZ0qs1rtMro5Htd4arIXlNyfZ9o8SmzbY/GoiXtszKatffG5eqeYm9Tf7xztVbEzx4Zf7vzHdhDDejfK/OuqsMrpB29MDPjENA5yjw2e7eDTJXoWhkoMbty47q3D/MJoSvq5xy9+dukcBcjIRRuKqEcnkXLJgDHqVAjbQ8Ncna5jVvuBD6Qvv/v6PShTo/v3RiQqFKAXWhytZA4JgP6LyO9CaAAtiIBqg0LwVw38hASIbkvnLy7IRo5aGCFYWjV/9uyZGmHXMXIKCarzljjyHdko+GRFCiDloOeQDwkchW8Ma0MMGiZlTsJI6FT6QNExC6ct0s4Jrr0GQDCzLRWY0vCsEaIqQJMqnZnBWGHUBDLoDMnUwGx40XiuuiCIVUbXZSHdyYQ+tJPdlHorFlOiII6rUCzIFjE9QpnIjnjKs+NzGA3mmHi89rOSkL1WGq1v22F58UmVi9VWfOCh7ehQ2ER2uREoO6jHdUQUpp853mPHdjU27uoR6drPnuVbZRxjSIqErJRrqsEEdI32xP6LFCDG8XPnAkRcLkdblqR9PKnHOzgYffZsUMfEtWWfyeas6ONeqT8R9tTGxx4jgKgeGhhW5GD5Sj6ciAB6B+vI/OaPdNB5t91EdAJ2Xl+XBiAU0RoXM3s20LSS77/lxq03QaxZSlBDb6zaAqvyaYRVWVh2vqWxwQJAcUAzFwWfl/AEJxJkFb4yyWRH5SDVRPUf27ycCF7wHjwIIM14mVtbGh/v4QFQqtlmRpktFfcrGqHdFeSlqcJsmfO06vjK8MzgNDCie6A1cJuJKHj5iomUZa7S6R4mCtgUIBhZUIl3AYiFXFOITxBlQj6MrPMKigZXZnVt22tiMIwsHXdMK20fnOVtDks1UHcPvhYEFATKsSBp9eMLHqEW05I1b9Ms3SOXczva9mqEMuE1mUijMR9e1//m95vSx+vOPZ2+efuKc88A0LlL54LqACjMKJX2Hmb3PtwshzZFi3M7f6j8xe/spQv6aFeW7XVvxErDd0duvhEbcShALxBrWeiIbLW8An/5ey8mQPkdCS6EZpCsaqDJBhLEOks1CCEBgu/gIIqLiMguib15a+zSuwqIRrCg6nnw4EsvVRCNUBJXfRfqZw0YRUO1uqEiIq4KUg5SGwHuIJp3upAsFCu2vb/twImmWk2ulW0S4mdaJiRehGK+IWROfOiElW6G/+DvDTcy28BBpoKmcnk04bXaxNCm8PB4VPrTfFQ9HnAWXiUmIw5El4lkM/gQk2H0iC2D81AORH0CiFz4qNWUJClWeb1iqVw8quWrcHwNiuVSTLmYCFtyg6v0/zG4xCWmQCTj8diLLquM7L1mD4+nmWZD7ux9BIDu6/ykv37D+88BUP3Fz4kHBe0991kAahRymYglVMjl0H65Yliu1N5jH9jQx7183/73lh96Det53mpBc1OLHxBsoSM20nIHNN07mCcGopmAKEK40ME2MSINquEz630JUHY1CN2VZ0lObrntRipHvas6Dz0F2RUwdH/kVVctBh4qzMVlJztiLQJIOb/UFtwJB+piSopFEkld2eEDuGkOsnfQViQ2ZTBVPKGTLRwqSqldFB/q0dNR2NJosCHehio13eMUupCXuOiKefO0NfGhqWnheGgIVTyYUIREaShhwii60k7RYenBwU/IrscrBcgOPuRD+BUmK1H5phPPbb7i9bYtUquGa1m4mQjidoiN4ITJcpQJSAGIgzRHBQkHYPhN8Gc48aV6jqfdiyluvovhZWHbb7GhJvxwenfvVw8OlH61YOBBAIIHfR6097PPAuSU4bZJSukCFYwLHaNPkRjFqqknXl5+6NAkO/qlO7GEAUvAWgig0/CgOyMjLbGnR15Y+dqL72G1HwlxpzaewgMvy0ewVQxKDmuVoRAoQtUkAYKIE4AYFB46E/PxLoyIBKiEGEgE1BHIKURyoZICVIBIkPPx2YcDKC9rYcvHXcxidMCzE+bNcyfUxgcz2F7xCixdS+U4gxPDMsOLDMGloQDUPDoahaMpxww/oiM4yXIDvQyVLMygMYQUVdpQurSlCe2IjuTnG3zgfnBBCpLffcCGOoL8OPC9RYAj3FAD1fNMyJNVfEQ7e3f/jiuBgaO7OznucJYmzdM2ltCVhK5RKe7wkPS3oQwgV2AtA0+uIlUFSnvD91yBmnYvn0PTBZ415rbnskQiOC55YlPFn7ak4qQxOBsn5XB5ALVjiZJwvgKE2Jgr5noH21ll3CDGRPmpje98Y6Anfdnw0ksglN/S4gd0R2xEdvadWNm28h34D7znFAzPG2GnVo5gQjsyD2EtYgYhkIFEqL4LOepN13NUiAQiEwioLZRGOLNqVUlBVdX6g1nJjmRBI9CsBx9LHmoIr0xKiql6YlqxRy62aWsZSrl0BcqKaXJh6P3qjLQEtCtp4+mm7WvWdNcukDgnnEryDdXr28mgQ47bYDZrcrSaMKdQY7PzZISIENEHbkSHkZxKxGPpAcEEBBiQIHFKr3dmhv+oTVxk1osDvz1ZrHad6LzsDfQaeTyW3X6if0f74ODw7n5VyBDLHB8y+p5PZe98aMPT6RlNde4UdMAr3MFag4IpEpXFB7tTc1xyuWboZDtNwpZGMaKJIa0l5MiVu1ROWvzZiI64NWfjfycgVwU0fjjExPhVHL4IKV9dZVnK8PB3wzXPltVN7mr4omIVmVIi/8bCK3fEzkV+GRdXsuqVkZHlp/xGIPmcaIQcQxEFIITE59a/EyAcRKgixAnwT2vcFikQEI0QJyhYv49Y465dx+A7aD6OW1/l19oRjqwSSmpnZQni8gvBJ0eSwyyO4julPLt3WlvEELI4K9Q1NZlyZwKaMzQpCndNcBmdsx1DT5i2qxz/YdrK4aQiE0kxaGv2XBgKQa90jTkM4oDTjrhDDJdqJiCha9i5XGn5ilCbSKI63rlsA3tIzX8cizJzhAPbP/z5w/rt83ji6J5eZqKre9lVhjeohy6y6ov399/HmDXrm907cstsfNuForH3fOqaCDhCyieud977rFqbyXbvfQa26QrHVTb1S3tHV5u0OMTsiWLzc1EVCqKuC2fcMRFmuN0NMMKB/DU8PyCdG2PelXVuA0mfuibH6mpCur7ddww/3PjpPnMGi03Q59gAuxvWcOaVj4+ePzXTzp86f54EuUJLNc4bH6E4XwKEd7AoIPLGrS158+ffVSAosSA/pWwVMWgEOBUJc8ATR+oJsSWNJBUSNMQW3jGpyEEzM7+HLrejmL12QpvGYHOvDaD7BoCScC5rwiTu8pBgOmf0/XXpdFbRxVAA0uuHftnz9LOfd2LJ/KZ5WKGv1kD90VkqDjdKkjBvSFN7b3CwRpiahPOirSgqE9VqfVf35hWi8UT5g3sS1Tb6wIY3f516OFPebGP0mKPi2w9vvmw0cprpIpZ+9L7+Z2iBs7bt3kSr81CALv0rC7qeACFz3TCxrZiIuJ0ApB640M4t1kklpGl454cDc7rv6/1qQUZCaZFTlqR3GcW0oEA/L5xBcCCyTI7rM7ERyvoU7Lvvvvvgg7HJycmuugNfduACk7IvffYt7BixQbyeGVl+HlRm2ukXAkCoYikhJFhM7hcQ3RDmSEbkWBhw89ylJP2Zvxj/rhPpPITFxeUVxCBV3bLv2BcY/WxYvzQPgCILthD/EcQWtnw8FibRsVGhEsmlRq4+cLrWTGMH6qdCa5oy5UKRy6o0F6VkpodonSZhYnppWU9P3w9D0zg4hvbsWX3iEqlm19ei3VSdo1EKc0Qc7IaJYmoA6BNtsE2ZYECLRjFbVBzlVrvdx4u7lONq/iMUoF7n5V+n1KGJtpzieB49nhYVYsIqZoWQrQ9qPnxgjk3lHX1woOM4Zsbj3WOPYo/OP4xA6R9O49ui6mEfXnFJMwZ+DmI061SSh4l9uKL7wc7V/fdtWt1ZP7X6uc971z20ruZSXV0KrUshCeBL+TA5+Uuj/Y/bwRWPPXno9dcOvXvk+edHKDsNO/qPvfHGK69gcSCCEayEGALdUdD87rp9gK94gfR+ZPsIVfkToLwqKkcNuLkwAoFsPmQcDEsEYBAIeG/LWXIKUWGukRR8HMlxd1dkNdwdWdjyRrOEydTxWVJjW5SKn+sKHAr30PjR+ins3A+XC5n8PqksjKk5WVsaLnTaU7Un55SWIcTJ7PrwC9oDh7sJoE1pwbXhi2w5SllOklxahoZBzUlN7YZ4AMLZQzZWImXlsJM0CcGVnpMnWQ8NZCTanBK2nF7p5KMDKyknijnPo0iFgpYZhGwrO6kpY4Kt5EusLNpxJ0cqMUx+1glCfjrUA4ZoRwEKb96PNwTQoqkfAr1yDrdr3c7tD20HoM8JoJ2rO5+bWv19d+/FzQ93X3zxUk3QAag4VFVxyyAHITGtg1SzuQD0Gso7APRvO4KaAtVNik+uRDkB9blYss2EGipfhQUXH/jtNJY+Hj1KESpZD0IRSFFJmMM7UGeWW7beFEnOH0EBlaKCDNAQibBvF5wH4BvWV/mi3BKLIzKrQnD33MKFbyjoXW0iXV8SlkswEQFctPFwT65xrQmAmnCpncNqayt+vO6Z8kq12m3icfmS9x+8d+iHaWiysIS0oiIK0M7M2nnhiTaP0MSEGyaGsaM8J3OKPgzV2ux6F/YxclRGFY2qcdK4vAta1rqBRWqNJ/REN+zE4TJcVStSQkLdCrbMrgyj21lYPKvQ8Jl8nZRtjbLrePYuAJphIDTTg1KayZtPcl3S0jenA72oZef0YCMDs6m0+/MdK/qf2Xn/DgLoWfXFdQD08qUaWjnyoL+6OvOgxMswjjtI3A4MV0AcAWZQQlwBCrEUQXSMLSQV0BYl1Ug1Ddk6pLYUrBUVZlFZETqu1RQdkjVjdjl0WLl2uN3TYaet3cfU9Gff90dW01Ob3bV+eJ73uR/KuVbx4RgqJpBKWBqdOPDcc0//BxC+Ul8In5tardgoDL354CFo7iAZVCzZACE8JdjECAEc/EJU7RBC6JTLLr3shMPOvP64E6FEFxC34Jij7z37tCNIjIp/5inyklHtXnARAAfKc9q1rYzP8UedRozcYeBzzjlPfuXHpRm4RFoescmpoJP/up8dVHSGBm2FASk/qhcXS7HxgT2FblGBGxJq3NLesZWhL74NhWSJZl3eIIBuTfpxaCcryag5bIeKz6bbnerXA0NYRRaJhoIGAY3uNkU4DAU/pVAA0FB9amGXNWB2ypUYDUmIEByjpZR0yetRG3DH1cJEl7l5s0zuEPvIZhl1HIBevn8v0jxwEVry33TcsrRC3qNngyn+wrO/dYyqMBX40RJSRjJuC9C+swBoeAaAxgmgqq08jUwC6YGFZ0C5ctAgvEGVF1qAXmqB+R+gtwAIf5UAggq1+AAQqRoA0X/GmaBDh5AOqtMugmcA6wYlQiru2MPPObkNFVZyse6ko85E6+4V24J0NpSq5WwTX/vx6685HoCeOuac6575KiClK+EicNCvwRP7BKw+51xYX1a09+1mGgsWjRprH1TiMHs+HHZn5LruaaYI8yXLv37J50tmnmi6k3iDrsxW/Oi4NUbi6ijOQU47hA46pUFGAOIHsU0Od+qwQoaWSsHHjlr/BqQnMQgViag09aLSjUsmJIEehqVkmqx7mUwMUpAFdHKtzFFJoC9OVDBmk8laL+qD+/bh5AK1xR8O3ZdDEuLXHQymeK+rU0gOGgTqzhEfHZFydfJKALp6+Mqpg4ME0CSlQbngIgChaqWCgUM6m48fROKNLez6xSkAigsAvdTiQwGi5OkXYeJ2nH4ObFxr9xkUiErYoBEXQ2eQFqLrDkEfyOGHYg76lFOOuvA4quPq1EOORqkHdo04CRddeNlp8BKOOgqTr6fBvYb8jeeka4mVIwmfM8Bn3WUmgOgxhAdiuT0lCKpS8y51WcESDnNFBQs/qqIF6V6OZA6JZ6dzZWxlbHPzk5mhL+IqlS7/xEqikd/jEfP7EKfaPBK2Wq7luAqjcGXVr2ekFKAyDfVpAdXeAIGjy9m17CCAMtGeITILxvVjekUuVqHVwKdFxOSiIwDmVEyLuD4yNr7z8uGzPMaEJSH1k/EF5GEl/gp1bw5DjTgRVhucvPnniAu09vxBC65/rEKcqgzworJwVJYBoBUA2vNG9cqpwd6pReYn1b2ff3rn5/0UIIejRG4hVrSYnbSbdWhRCQwksrvHVhaTzeZj1NZYpNu2sMP8oc8/JYKO+pcfuv02uAKY+Hv7GyKlYqlYJ58zIrMtRk9SSgRCO4486tqLLrvglBMOvfS8I/6JUU+79LjtJDaRVqT6/nakesm15PeOOf+aQw8/9fTrXt3PdZmxLxRzCQSQQ6nHJ9ywIImWfbS+wf4BkZTfp6JNCLnaRBLjTyF+9YMPwOeXX5aX4/yULj/TkHhNXDGe/4DJb7Oa2TKMgTftHR+93wFAlAaRRI7QKexLKUjD4ih8XOFcj3xqvssaDyGOnAhq5Bm/TTTXddYg9sahnit36+EgsmV8sv8nhkrbALrru6wiLKzHG0fSrTx7QOLlmizSnkiAHAOI8dxanDTMfsmilb4UlzuC7B6ZXGb9GUkkenXlnl7k4XqvnFqoTSWH8rW91RvvrH5rSKLcoJOYbEacTWlgvbp0eFopd5hz3ELX8HAN/fq1jbE8LlaN4SImLixeeTElt2DmbLOGE2v9JhdGxSrYkoM+FTEf3eb4oUrxtxlRZg41ClSvL4USwS046ahz7733yEOPomLUS65/nLgIJJfw0UfvUy4C6FCv0OOIVBEVXXIJjuK+8uNqNxOAZAG2Qy5HalEY0JLs1nygLxikAOENEvrZfJ5UWxj3u818zecARGR5WaZKuaZmNitKgVyVctiVCFitbC1WX7pWVB991KmQ/Q1I1v5RB6goVHyFRmvyWkwS9Vy4uHe+yxjnIxrZVqt2BrUckI6fsPen6Pdo3lZxsNBEDZOnRXClx3eBxL5OjhDjr7smr8Y3b2x4rWsXjjFYRTYbpl4jZpnBN5vAm8cq8wV67OPhp0qiFqCd1XumFs7KJ7vzyftf/nyr9mVq8fONtlxhcHzfHff8sph2meZnpiVsfTjXP5AklzxgSHHmKQ9A0OJtQHAin9gzk68CUFpCrqmxSfYFD6UG2SdQUiFAx//C7NfgQzy6B1tm7tpLLzvqlONPOWbHvTtOQhvPaXh+IFTAewnxEMCm5SNccu2ljyMUIs7cJU9hiuHJ/Q0jAYTd1WakY+RyfDpTQTy080pnWREUzqcHbNCgkmoiYLNzazj2GOI8VMUIIC4k+5f1KX7i4Ydn7I5RbLfsYaOXlulBZidCb3gM7Z0KR+ALOAlWUUSmoI2MdACQRiUumvobm3nZXERLAXKq7EWHSrAdORJSMa3a3gOPHJutGAoBuUOYQfcoeq1xnCGA4wxyksjWK9OJZFfv2NVTpLeIOpQ1tvssgMLqBK6/B38jXRhJ8WW4NDEHQIOP3PHsyj1752v5pChf2PfQy/tXvzfkkIvL1u6455aLr3pkLW3CwlsTUpFmAPLgzk02uw1ocnJjA4DuowCRbX/QoCzyTLoYAJE8OnSohB35Tr6BErI8XVWvf/PVqwiO3oUS7TiDKBHS2KdSMSqEPD8nEUrw0I8nHsLxoHUoqQ21nG3gueb8sxH+7G92WbtR5Xbb7SRPRh2pwqY3Tmg+4KT5VMI50wDTz3eKBYq41J5GrjTs1P2C5bjkskE6rA45LdiR3aiPivXdUQ47IOn3ZNCLS+cIsS3OLs8MBYZuRjQq4/g6SDrMwOGgLVUaq8TU1kzsDgDKRA2LU1CE3umESUnSMjCCMWiMH14fWFGK1WriMYQ4enomgsZ4kJJaqHqun/T4pk2kWFpI9g6Tq2Z74ebhqBlmyzF+5I7gycKEnXcovFiwNW+etCbHst3jufxDD63v/yHVn2i0Je+6B5px1S/TuOTFbMRQ/+a5yFli8MEsBQBNEUDV6mQ+j8MORC5+YmbPeDbXj+UqGOXHyUgoEtbbcmahPXiIgmJnnFtwZIoqTan01T9KdOSZp1126OGHHL3jhkOvJzlS6vm5GwduT7vkpEsuQdnucfgIoEOy2QTPJY9CfdCCMI1xEhHeILeah0BSE6KEw9GI5zJ8Gt+pnvfauD0wKiG1Xucq9HpEEmc3hnvJIPBUv1sd6gtgqnRaN6pq1wQ5MbbOhEwCctmckMqHvTCZ5cDQswQQT8OC0Ay8DJ3D98GX189lKnd0AVAfq/HE1OYmlm2v4V/U5HrZ5AmSS0Nw+TpJ41DLAFLLgTpaaU8oFVwZOuodRKswykyqHy43xOQlraX9NmsXzrGSSwx777965+C8iLlsjsuQyxY6YMb1rspAelVn+j6EEmubUtJPxKUtOdC9Cl+OJMrlYW7ORs1r7sYh5kloEABNXYkoGXzIee/dWZKsk+AFIgoEI4dNKwrWKM05KwhuFc229PdrLl3KGQzN1sHoFYLonCPPP/NsxKhkrdM1l1zbYkR5B5SD8BFchNYrdP6ZwAPn+vRznnx1FdOIBJDE7JbznOTCG1Eici3ROcc2lENO/YLLJnWnUH9W8+xpDzwpPd/u4jaawNLsIRqkTUBMNA2NH3RgjNcrykQibpk4pFJAI9U9Aduz1u6MjMcx0MpYmBnUB9jsUHtQrbdmXHfgxg27b2R9ZgZa2AI0LWJK3TAzPKk+GjWpCBk7mklCMHcgRRnBbSG00JeiwhwZplPDqIb4veg9AbEIGZ51kXFVcwQ+omfh2Zv3EVr3v3H5wsdDXyLCcvKJaJDXblPhC0SDrhE0+dEQDaHN0aceQKNfc72IBRk9NUhueayWHJzcXTt4sLZQq2W9xslcEWlWNKRpNEVdLptWFlWjnYx4XRBswJsTlNd0XrFTAHdfbK+8/eR1WFWCiwuHnHzOaZeij+d8GLRrEKA+DnkfP6A4/0SqoAM8Z+OkyTNfpXH7l/KN8D2V21EeQBKelCnV+PIxO0jjOGRn6fD2prCaTyhX4kpcwc/h47PrSmPdAAwjDp7Ic2SgMcSicVJCOl0Z7iaAtGKnSsUYMeh7zLZnjbYMXQtCLEVKwytiZDU0qgnLrHQX0aB4X6f9iSnwWVnrWvRMN6cHvEqwEUph5mI8LzvagfK2EmVEFKDkKEGg2EMDrH9wUd5Fq20xyOdEtQEJWuZxYwsmUCJBXd6sDETCYKVkZ3Bk3GacW4BuvYGTRru7RP5M1NnGk+i6m5VYkXTRqRx1MUAhKNPi/dn7wYHZic6R6CR8hP7XJ9cwh15fwicjpTXm6gtXNmeXWBNLnaxUUKwrjK1zNIL2EYaaAqTRKPC/A5MkMLSzEClPfPfe8+fsAKO2o884FKHopdeT54dKZD9KCRwESIsa3p5zYdze2Xh5vw6mgADqBqCIHFXjAGKMSCSM77ArYkTGh202zwe47ghfzOP06SW2XV02Jb8PfxvpDHCHAciZYi8gmumO0tC96IixAYi0iuj5ToN4dMQgkwKQiJmJ41wZnm0cJFF6LTo+i+PWi/REg0QZIUv1QX5zGIP5FJ/paaYOPjlHqtfzijzcUFviSt1KSaxeAlq720RO0QbgaPehN48Ev1SjKUhtu4IdqB2WKbWig5Wrx9+PSVR3GP30gIWF3lBgqjIli+OOOzL1bcmpRc9ibTzfWMd7YuK6SiRa9clwsHDmgwMaAKKFlcq4t9+MdvtkcRT/AVXKmuAsPDItXsIIVCfLoBC7svl1sZgA6qsrfphG0UnAUhlwmhxziQx+38+d74+89t452L5I5heOORP+NfYPYgbrKCRaz28JmhcBB3QePYYUbl/Zmnn4l4QEgEgXVLcFWHhMK0aqW5eRuUw0u8d8gu5dPctyeLdYYiUPMLlGbO/vM7jxHSNj7vjJZvqcKSEaEIYsGmxWEsjpLUABhFBlgyM1ItD7Jd0HRcx4PMK280rYoqm0iMx6g8Dhl3VHvQRQPGoQLEJ/hnux1pWShEsb5fVZZGqhXevSCwUmtzJG1oKw4XxjaYjXgqFGrpttUBVNsQR0gAxQBpGkgJRpBNa/rFrPlUNPir94mtBn4ifpvQD1WaEq8vq2zbHc6kBtbGXVi3ArzVU6oEIABBd7z6cEUOeSgt8XU36xbyyZXTtAACkYxoTj4JWL9VEa2bUfFHC8c/l1jQa0AMj3Q3YWGhSUaGgKPg21Zr4TgD56/zPM/Z6K1AJhdOT5T13ywLWPQ8jemQdaAjhPnX8k6Oy47t13tiZvvWrPAADtAiCjzYJPPc+68+adl+MAr1GE4esAPS7H9C/QkFZtntJkHRZhV7ZFawjhEdejWYMdh4hThujQEHYfYgGJ1idHqGsigMzO9lGFQzPCEEYkogWjDQ802XUkxw4XrilANyjszHg3zwsT1y2LCmjTw9XhNeoFIirUcOnRmyCV6XkOjLfWizEJvFheCXPdMTuPVyq6mcgGJSwwtVzetE3aY2b7e4QMGmM2paApyvhkEFIQBmB1QrYtoAJuIAylEgkIMrnaT+a8wua2yYJOx60l07p1cpU8XanDxBkIoN6/AbF6crakrXD/WNLTW8FQblnR8WVCeXCmpsT3RkVDEo9DNEijmmjvbA9xfD8UoEE+g1nDEqQAqD3VAvQabh2c/yqcbsJox+lnn3sMsWh/y2ePwnV7HtXa03fseOXdrYeq1fuvAqDwNiBpwJwR9mCjihmtpLhSRce2A9LvgR/QCWLLzBGp6CwPM8MPCtVyVLJl8UgG7aR8Q1m9vCwUG3oicYlDTi7j2zLuSEBYDqZCcML6zDoKkJrio2bnGt0Ws1LIYXfHbX2m+wmgPhbDO7yGqJ34cEQaLjXaFCzIvoFPLFa0a3FxH8P8RbsccwhFdoykvBNM8ahCKx+Q9oQDGUuPmEGb0IW1DjgHSrk4SB3VgNNYDlJRSfmfIfTOf9VKHZOYsDCr7a5cPc4W0nFjHb+6TJX6rA9C90CDPqjMAlAQ3yHcwhvuTSarB/DHQ/jauwHPO5vWVeScuKsIEzd+AGOiox0sgcP3Q3NW7EBtZHaClmIFOwigkfc/+mjktUc/mz2wvv8dFPN27CCQdpyOY4FnQ7AM6TqgIR13sG13Njby43f9B5DHyJQGMnGHnPTdUEIPwBkyB0CNzMWHvX4LDi94dmUTErGAL5TrEY+6TcyEK+Q00GjQIOfEiFliYtJhwuwu4iSYnQzE8AYccMyYjfPG7rgeGS8tDqebEs1pK1Mu1nBkTE56HwDpOe2j6yubK9Zdi9ONZqMBQF7SPtIDj6VYAp2YvQTzht9Q6qT06hB46NIiZrCjY7TMIRP9PBdXqtZoXGG63JIxSaXuAN1B3ZEgjcYafpmQKhOlCraaulsjzn+zatu5Vop/8cYTn25VikUAskOD4Mlps55eACIaxDcWqO0oXUlPlbxB2Spu0yByrSVz2C8tjHt0Yklho6LRCBi0+UbJx5/WcLQOlV3FKhtYZQAKUYDIPkmkG0rrq1tb7/z47is7WgJOrYZItKO8i9zDgcmNrvGp8bvuIIAiLUAi3CNHeALnTUYhisdJKIPWqSGyqMVqtGLfh424apIQS4N6fXM6iR3KdDmyb0EGQyCU8Wkj/SJLnLr1H7YBUCDVWQ6Rs2bCjMSIKITkotdzCaVD7lpcrK7JxRxNfMi5ug/nAPShdp+yOrYrJsKGiwaRhAsK5HRHAYhq08O/tFjR1UstqXhNJPPXLUoIU5oU6q5iTUrlSnBNORFbFrUI5RKMCuN2KptTJnrEyu/Z28UMTy9Hw06YQRJGgRYRWsu7aJucKn2LSPWTra+gnjpJcdZH2kdk2WxthgK0pEpzBxBfFLqShYHSUieDzNlAf+BrA5BGHOrWaXS2KgE0yppPF32haY3DVFTxMN9hYAg62g2hKAH0PgjxFBMsrBpS5pqNrf0/IlNHBPd3ULAlW9hwem/dfPXkwnieAjQDDTK2APnhF6E2EAcXKRO+jdWD7JbHaAQbiBW/ZXYzpbIgg+WkGtdCSAZhyyJeZhZLoxUr2jGzStxgOc/utZndbrOw/JOYj8BCyG4Boq+vNqcbyMWyp1c+2XSgQBYf4q++0YXpk5CCp5v0yGOLwyLcakygfVqHMEwcQB8jlEWntBeR7SrVNXV0WdVRdy0iZW5KW5jYsRloejL6Pk55dLafrOxx03kIRyNSKXJyAZfUHxBiE8rinvsXRLapZz133NWzcvNcOBHFhpSO9paxg7S9MVX59r6Lb/1kP2ZoMfddnEWaJhU1FbK1vZMV8cRS+/fG5OBdk810MlnIQoPKXbUajlDbbP3hn7/HoEW7sCJWAtDsLI0YXbvvh0WxvGH3EUBBBq2jI5iKjkCBgKjTwRcwlhhoBndIcp7pBpYMomvoR/xmFW0qsC3WpDdMARrbTWlQTzc0iIRBJgASovCCq6PQZQLH2hKjCGDwDtOjfaEyUmNOoMEnkxpxY6HIBUuiMGDdooAY+xSPB0BM9DOao6mogTTJCOmS7By8OAmlGhJsqW9sPjElRLNhhsnPvbHLM6QW4qxzQl7MDVspQECkRIQbCqj1bBfFh7xElWJ9tk6UCNFKyUt2NeH9QL99RAsjkxLYE0wm/klyYB3NlD1AFNZJYOy8bJ53/P6FgS+mDmbv2Dn0wc0LQ9VlCbOgKukEKlWHAArVdvXMWP8bdzzyyf5S3eGw62Kz4jhThBxCtmsKdkuw1L58JXJ9M40D2MK0UVka+d5TGPry5z//FsafDNTQXANEgyYYqhIAhapi9qLd51CMsgQMbO8I/iB8vwWoAy0FE0vlpfdZOD2i9w+kudNdydrw5eO4H44sBbmEbAvnKQ3afT80iAACDWyu6gmY6UIbIeKh2MCkIdKLsPUIuYPl7SH51lKRDhZN9Q1WgK5u7Xc4FDDwgnYqBxPs60MK024aghcXkBuQi4MxByDPx90WuqvRgH8GQtrcB8PsWTTRBJipxM0EUFTokItLruquBBA2wGc1xoF1NKPpEamuGOybHU9RsQgdKtm1pVlxUaLjcqUmvzQtJTtlpYjauAmmxZRGbICVdmCEDBCch4gXy7NQP/IOfLGcP5i8Y9/c5hsHh6r9aYuR9tpXKk2lXTxSHGub2hzfHE70L66XxHWOPMId6IKCeApIxE1VY7OCpdHf78Np771bFKADS53fdtWgS7kWo59/VosxBestVL+Cmz2hwZlqxffVenglpuIICCAsMCj/0LcNCElvH+0Hh4AlYAWFflNCtBOFaGC55T78F66++fLB3eme/F29xMT9A6iLAHJnImwhl0RB+Ill6Oq+kKFMqDC2BXQQAS5h5lFRdy1uIhmHmkhTHoWR0w+5J95/v70DlITQIL/UHMkE0LaI81vKolBGAEn1rmlKuGx2biXB46BuYh7iNz7c5bGgdV4zy1EudiXgO+dymN7ZXxSjrScg02sPoNYCRhXKw6qjjago6S8WKzGSn3S5dCYT1rtgDY8f9XLEZhIXVAuq1E0Y+S1SvKL9ZMWIhTsUzi8k78jPbd6884uNX+7q76qtPnag5Jsojny31XblBzunrLmY60BJ7GAzeyc3kDi4qzcLQPsAaGKJsbxn7569eQpQ1b70Q7JQw9Kn3sHeWnIgZ/KSTxMAbcTEGhpLg0BA8f2aw08Bamchjh7poP0g3gYU8CqLgvaQMMWiqbncAevazj37bsb2KIzltGTNu5yf/AdQggLUZYSvmonEHVGkBbZn1yB/g/nb5VFo5Po+DZnKGh2lQPl45r5oFOeChEwmRzOi0SD7DFdaLaUACY1sMQ3eLJo/dkGD9K7mdJMKcdiSaaYcTdV8s8XQ/NBqlUadqgmVtroGE0j49K+avDwOAJnh7q+TchhUKAY7V6+DkK9usqTdSgREMbbLj25SF9cNp6DHi75KE1aVebHRyiIawAcNVg6EqC0xXm/atDGXvX/89c2dd32x8enBodqnn659/ukvHyQ/bTQB6K6rRVwHz2VK4PkfQ3FuDCk3LFJI5jdiMHGMb3EOe9/Y6gE8ytn60s81VG/x94Di7mS/16vkwSX1ZscO1MUCGj/GLvq+XytJV7QqMQ3fRgpQSrMNKCNR2gXk7Qua+3PWta5sAeEF7l0PE9kNUGe5AGjw6vz4zj1X3brXtm3ipG5UcDjUTpwyjYKzPfrZyeIINByEoyIJXe1MBVmMv4l1CPQZJ4+HGIMujTsdo+hc4tlJR23/kNlMzwjZamGcrufgoe/6WGRR60TNRQBquulsnB+0I6ec6VdNP+ux+qMGuDWxNZyGETVyq+k0+jl5YqTVw3q1fd21jkQxcjwUHJ+vDinCR3NJtLFSMWaCQ6dFwbWiJMv/TC4JzF4Cmxq5CZEIakSqEhQhjDGZLLnxwYH87oNDG/3+7lplf/+PWy//8sEj+Zm2K1d2Tw242VgjhzYH+Gd3VTENiVpDIXn/RqWuaGd8ezWoVfdXsp5CYnbpy16kTlGGB0UsnLb5vVoxJggLk8SLY6nsWmjQYsmyYlfxaaNkGTIAGf4BxA67YgrUXZxSrpX0MWV7a7sAiKgjdq15rMaCZHnfXYO4XL7zHhx6z/0NCNnQuJnOEfyz1AgNF2KVM9jJ0KrpRodWhk083QG9MBQUABwhB3PGUusxNwBAcbaDpBtIlMpzCOVSC8m4CTHt+P4Igx+N6s56vVsaJc3wAey3bKRjykZ1pcnG2hb+4kGM2kWDPmQq0k0oEDX+htQojyy2cssIIJcEvQJUAFQCI9g4bQzIiAoR1wGTMm67g4c/yYbR88LKmQDFi0wuMXRARBGiBIaO6d/IZr9IfunvTrr2w5ZWawObj9z6F14pMWKB3hbTAAAAAElFTkSuQmCC\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Horizontal flow rate card with background\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"m³/hr\",\"decimals\":0,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "liquid", + "fluid", + "flow rate", + "fluid dynamics", + "velocity", + "mass flow", + "volume flow" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_fluid_pressure_card.json b/application/src/main/data/json/system/widget_types/horizontal_fluid_pressure_card.json new file mode 100644 index 0000000000..822825e286 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_fluid_pressure_card.json @@ -0,0 +1,34 @@ +{ + "fqn": "horizontal_fluid_pressure_card", + "name": "Horizontal pressure card", + "deprecated": false, + "image": "tb-image:aG9yaXpvbnRhbF9wcmVzc3VyZV9jYXJkLnN2Zw==:aG9yaXpvbnRhbF9wcmVzc3VyZV9jYXJkLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjU2IiB5PSI1MiIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjU1IiB4PSIuNSIgeT0iNTIuNSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjEiIHJ4PSIzLjUiLz48ZyBmaWx0ZXI9InVybCgjYSkiPjxyZWN0IHdpZHRoPSIxODQiIGhlaWdodD0iNDAiIHg9IjgiIHk9IjYwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii44IiByeD0iNCIvPjxwYXRoIGZpbGw9IiNGMzY5MDAiIGQ9Ik0yMyA5MC4yVjg3bC0xLjcgMS42LTEuNS0xLjUgNC4yLTQuMiA0LjIgNC4yLTEuNSAxLjUtMS42LTEuNXYzaC0yLjJabS03LTguM3YtMmgxNnYySDE2Wm0wLTN2LTJoMTZ2MkgxNlptOC0zLTQuMi00LjIgMS41LTEuNSAxLjYgMS42di0zLjJoMi4ydjMuMmwxLjYtMS42IDEuNSAxLjVMMjQgNzZaIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZD0iTTQzLjggODEuM2gtMi42di0xLjRoMi42Yy40IDAgLjggMCAxLS4yLjQtLjEuNi0uMy43LS42bC4yLS45YzAtLjMgMC0uNi0uMi0uOS0uMS0uMi0uMy0uNS0uNi0uNmEyIDIgMCAwIDAtMS4xLS4zaC0yVjg1SDQwVjc1aDMuOGMuOCAwIDEuNC4yIDIgLjUuNS4yIDEgLjYgMS4yIDEgLjMuNi40IDEgLjQgMS43IDAgLjYtLjEgMS4yLS40IDEuNy0uMy40LS43LjgtMS4yIDEtLjYuMy0xLjIuNC0yIC40Wm02LjktMi4zdjZINDl2LTcuNGgxLjZWNzlabTIuMy0xLjRWNzlhMy4yIDMuMiAwIDAgMC0xLjUgMCAxLjQgMS40IDAgMCAwLS44IDFsLS4yLjZoLS40YzAtLjQgMC0uOC4yLTEuMiAwLS40LjItLjguNC0xIC4yLS40LjQtLjYuNy0uOGExLjkgMS45IDAgMCAxIDEuMy0uMmguM1ptNC40IDcuNWMtLjYgMC0xIDAtMS41LS4yYTMuMyAzLjMgMCAwIDEtMS44LTIgNCA0IDAgMCAxLS4zLTEuM3YtLjNjMC0uNiAwLTEuMS4zLTEuNi4xLS41LjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEtLjhsMS40LS4yYy41IDAgMSAwIDEuNC4ybDEgLjguNSAxLjFjLjIuNS4yIDEgLjIgMS41di43aC01Ljd2LTEuMmg0LjF2LS4xYzAtLjMgMC0uNi0uMi0uOSAwLS4yLS4yLS40LS41LS42bC0uOC0uMmMtLjMgMC0uNiAwLS44LjItLjIgMC0uNC4yLS41LjUtLjEuMi0uMy40LS4zLjdsLS4xIDF2MS4zbC41LjcuNi41LjkuMWEyLjMgMi4zIDAgMCAwIDEuOC0uOWwuOS45LS42LjYtMSAuNWMtLjMuMi0uOC4yLTEuMi4yWk02NiA4M2wtLjEtLjRhMSAxIDAgMCAwLS41LS40bC0xLS4zLTEtLjMtMS0uNS0uNS0uNi0uMS0uOGMwLS4zIDAtLjYuMi0uOSAwLS4yLjMtLjUuNS0uN2wxLS41IDEuMS0uMWMuNiAwIDEuMiAwIDEuNi4zLjQuMi44LjUgMSAuOC4zLjQuNC44LjQgMS4yaC0xLjdsLS4xLS41YTEgMSAwIDAgMC0uNS0uNWwtLjctLjEtLjcuMWExIDEgMCAwIDAtLjQuNC45LjkgMCAwIDAgMCAuOGwuMi4yLjQuMi44LjIgMS40LjVjLjUuMi44LjQgMSAuNy4yLjMuMy43LjMgMS4xYTIgMiAwIDAgMS0uOCAxLjYgMyAzIDAgMCAxLTEgLjVsLTEuMi4xYy0uNiAwLTEuMiAwLTEuNy0uMy0uNC0uMy0uOC0uNi0xLTEtLjMtLjMtLjQtLjctLjQtMS4xaDEuNmMwIC4zLjEuNS4zLjdsLjUuNC43LjEuOC0uMWMuMiAwIC4zLS4yLjQtLjMuMi0uMi4yLS4zLjItLjVabTcuNSAwYzAtLjIgMC0uMy0uMi0uNGExIDEgMCAwIDAtLjQtLjRsLTEtLjMtMS4xLS4zLS45LS41Yy0uMi0uMS0uNC0uMy0uNS0uNmwtLjItLjhjMC0uMyAwLS42LjItLjlsLjYtLjcuOS0uNSAxLjItLjFjLjYgMCAxLjEgMCAxLjYuM2wxIC44LjMgMS4yaC0xLjZjMC0uMiAwLS40LS4yLS41YTEgMSAwIDAgMC0uNC0uNWwtLjctLjEtLjcuMWExIDEgMCAwIDAtLjUuNC45LjkgMCAwIDAgMCAuOGwuMi4yLjUuMi43LjIgMS41LjUgMSAuN2MuMi4zLjMuNy4zIDEuMWEyIDIgMCAwIDEtLjggMS42IDMgMyAwIDAgMS0xIC41bC0xLjIuMWMtLjcgMC0xLjMgMC0xLjctLjMtLjUtLjMtLjgtLjYtMS0xLS4zLS4zLS40LS43LS40LTEuMWgxLjZjMCAuMyAwIC41LjIuN2wuNi40LjcuMS43LS4xYy4yIDAgLjQtLjIuNS0uM2wuMi0uNVptNy43LjN2LTUuN0g4M1Y4NWgtMS42di0xLjdabS4zLTEuNmguNWMwIC41IDAgMS0uMiAxLjQgMCAuNC0uMi44LS41IDEtLjIuNC0uNS42LS44LjhhMyAzIDAgMCAxLTEuMy4yIDMgMyAwIDAgMS0xLS4xIDIgMiAwIDAgMS0uOC0uNWMtLjItLjItLjQtLjUtLjUtLjlhNCA0IDAgMCAxLS4yLTEuMnYtNC44aDEuN3Y1LjVsLjMuNC40LjIuNS4xYy41IDAgLjggMCAxLS4zLjQtLjIuNi0uNC43LS43bC4yLTFabTUtMi43djZoLTEuN3YtNy40aDEuNlY3OVptMi4yLTEuNFY3OWEzLjIgMy4yIDAgMCAwLTEuNCAwIDEuNCAxLjQgMCAwIDAtLjkgMWwtLjEuNmgtLjRjMC0uNCAwLS44LjItMS4yIDAtLjQuMi0uOC40LTEgLjItLjQuNC0uNi43LS44YTEuOSAxLjkgMCAwIDEgMS4zLS4yaC4yWm00LjQgNy41Yy0uNSAwLTEgMC0xLjQtLjJhMy4zIDMuMyAwIDAgMS0xLjktMiA0IDQgMCAwIDEtLjItMS4zdi0uM2MwLS42IDAtMS4xLjItMS42cy40LS45LjctMS4yYTMgMyAwIDAgMSAxLjEtLjhsMS4zLS4yYy42IDAgMSAwIDEuNC4ybDEgLjhjLjMuMy41LjcuNiAxLjFsLjIgMS41di43aC01Ljd2LTEuMmg0di0uMWwtLjEtLjktLjUtLjYtLjktLjJjLS4yIDAtLjUgMC0uNy4yLS4yIDAtLjQuMi0uNS41bC0uNC43djIuM2wuNS43LjYuNS44LjFhMi4zIDIuMyAwIDAgMCAxLjktLjlsLjkuOS0uNy42Yy0uMi4yLS41LjQtLjkuNS0uNC4yLS44LjItMS4zLjJaIi8+PHBhdGggZmlsbD0iI0YzNjkwMCIgZD0iTTEzMy41IDcyLjdWODdoLTIuNFY3NS41bC0zLjQgMS4ydi0ybDUuNS0yaC4zWm0xNC42IDEyLjRWODdoLTkuNnYtMS42bDQuNy01IDEuMi0xLjUuNi0xLjJhMi45IDIuOSAwIDAgMCAwLTIuMWMtLjItLjQtLjUtLjctLjgtLjgtLjMtLjItLjctLjMtMS4yLS4zcy0xIDAtMS4zLjNjLS40LjItLjYuNS0uOCAxLS4yLjMtLjMuOC0uMyAxLjNoLTIuM2MwLS44LjItMS42LjUtMi4zLjQtLjYgMS0xLjIgMS43LTEuNi43LS40IDEuNi0uNiAyLjYtLjZzMS43LjEgMi40LjVjLjYuMyAxLjIuNyAxLjUgMS4zLjMuNi41IDEuMy41IDIuMSAwIC40IDAgLjktLjIgMS4zLS4xLjUtLjMuOS0uNiAxLjNsLTEgMS4zLTEuMSAxLjMtMyAzLjRoNi41Wm03LjEtMTMuMWgyLjR2MTIuN2wtLjIgMi4zaC0yLjJWNzJabTkuMyA5LjZ2LjJjMCAuOC0uMSAxLjUtLjMgMi4yYTUgNSAwIDAgMS0uOCAxLjdjLS4zLjUtLjcuOC0xLjMgMS4xLS41LjMtMSAuNC0xLjcuNHMtMS4yLS4xLTEuNy0uNGMtLjUtLjItMS0uNi0xLjMtMS0uMy0uNS0uNi0xLS44LTEuN2wtLjQtMnYtLjdsLjQtMi4xLjgtMS42YTMuNCAzLjQgMCAwIDEgMy0xLjUgNCA0IDAgMCAxIDEuOC40Yy41LjMuOS43IDEuMiAxLjEuNC41LjcgMSAuOCAxLjcuMi43LjMgMS40LjMgMi4yWm0tMi40LjJ2LTEuNWwtLjUtMS4xYTIgMiAwIDAgMC0uNy0uOCAyIDIgMCAwIDAtMS4xLS4zbC0xLjEuMi0uOC42YTMgMyAwIDAgMC0uNC45bC0uMyAxdjEuOGwuNCAxLjQuOCAxYy40LjIuOC4zIDEuNC4zLjQgMCAuOCAwIDEuMS0uMy4zLS4xLjUtLjQuNy0uN2wuNC0xLjEuMS0xLjRabTEwLjQgM3YtNWMwLS4zIDAtLjctLjItMWwtLjYtLjYtMS0uMmMtLjQgMC0uNyAwLTEgLjJsLS43LjUtLjIuOGgtMi40bC4zLTEuMmMuMi0uNC41LS44IDEtMSAuMy0uNC44LS42IDEuMy0uOC41LS4yIDEuMS0uMyAxLjgtLjMuOCAwIDEuNS4yIDIuMS40LjYuMyAxLjEuNyAxLjUgMS4yLjMuNi41IDEuMy41IDJWODZsLjQgMXYuMWgtMi40bC0uMy0xVjg1Wm0uNC00LjJWODJoLTEuN2MtLjQgMC0uOCAwLTEuMS4yLS40IDAtLjcuMi0uOS4zYTEuNSAxLjUgMCAwIDAtLjYgMS4zYzAgLjMgMCAuNi4yLjggMCAuMi4zLjQuNS41LjMuMi42LjIgMSAuMmEyLjYgMi42IDAgMCAwIDItMWMuMy0uMy40LS42LjQtLjhsLjggMS0uNC45LS44LjlhMy43IDMuNyAwIDAgMS0yLjUuOSA0IDQgMCAwIDEtMS45LS40Yy0uNS0uMy0xLS43LTEuMi0xLjFhMy40IDMuNCAwIDAgMS0uMS0zLjEgMyAzIDAgMCAxIC45LTEuMWMuNC0uMy45LS41IDEuNS0uN2wyLS4yaDEuOVptNy0yLjJWODdoLTIuNFY3Ni40aDIuMmwuMSAyWm0zLjItMnYyLjJhNC42IDQuNiAwIDAgMC0yIDAgMiAyIDAgMCAwLTEuMyAxLjNsLS4yIDFoLS42bC4yLTEuOC42LTEuNWEyLjcgMi43IDAgMCAxIDIuOS0xLjNoLjRaIi8+PC9nPjxkZWZzPjxmaWx0ZXIgaWQ9ImEiIHdpZHRoPSIxOTYiIGhlaWdodD0iNTIiIHg9IjIiIHk9IjU0IiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIzIi8+PGZlQ29tcG9zaXRlIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzE1MDFfNzcwOSIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTUwMV83NzA5IiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PC9kZWZzPjwvc3ZnPg==", + "description": "Displays the latest fluid pressure telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pressure', label: 'Pressure', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 15 - 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"horizontal\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"compress\",\"iconColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#305AD7\"},{\"from\":5,\"to\":10,\"color\":\"#3FA71A\"},{\"from\":10,\"to\":15,\"color\":\"#F36900\"},{\"from\":15,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#305AD7\"},{\"from\":5,\"to\":10,\"color\":\"#3FA71A\"},{\"from\":10,\"to\":15,\"color\":\"#F36900\"},{\"from\":15,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Horizontal pressure card\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"bar\",\"decimals\":0,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "fluid pressure", + "liquid pressure", + "pump pressure", + "hydraulic", + "pump", + "compressibility", + "compressive stress", + "pipe", + "pipeline" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_fluid_pressure_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_fluid_pressure_card_with_background.json new file mode 100644 index 0000000000..7cf97b4caa --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_fluid_pressure_card_with_background.json @@ -0,0 +1,34 @@ +{ + "fqn": "horizontal_fluid_pressure_card_with_background", + "name": "Horizontal pressure card with background", + "deprecated": false, + "image": "tb-image:aG9yaXpvbnRhbF9wcmVzc3VyZV9jYXJkX3dpdGhfYmFja2dyb3VuZC5zdmc=:aG9yaXpvbnRhbF9wcmVzc3VyZV9jYXJkX3dpdGhfYmFja2dyb3VuZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSI1NiIgeT0iNTIiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjQ0IiB4PSI2IiB5PSI1OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48cGF0aCBmaWxsPSIjRjc3NDEwIiBkPSJNMjEgOTAuMlY4N2wtMS43IDEuNi0xLjUtMS41IDQuMi00LjIgNC4yIDQuMi0xLjUgMS41LTEuNi0xLjV2M2gtMi4yWm0tNy04LjN2LTJoMTZ2MkgxNFptMC0zdi0yaDE2djJIMTRabTgtMy00LjItNC4yIDEuNS0xLjUgMS42IDEuNnYtMy4yaDIuMnYzLjJsMS42LTEuNiAxLjUgMS41TDIyIDc2WiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik00Mi44IDgxLjNoLTIuNnYtMS40aDIuNmMuNCAwIC44IDAgMS0uMi40LS4xLjYtLjMuNy0uNmwuMi0uOWMwLS4zIDAtLjYtLjItLjktLjEtLjItLjMtLjUtLjYtLjZhMiAyIDAgMCAwLTEuMS0uM2gtMlY4NUgzOVY3NWgzLjhjLjggMCAxLjQuMiAyIC41LjUuMiAxIC42IDEuMiAxIC4zLjYuNCAxIC40IDEuNyAwIC42LS4xIDEuMi0uNCAxLjctLjMuNC0uNy44LTEuMiAxLS42LjMtMS4yLjQtMiAuNFptNi45LTIuM3Y2SDQ4di03LjRoMS42Vjc5Wm0yLjMtMS40Vjc5YTMuMiAzLjIgMCAwIDAtMS41IDAgMS40IDEuNCAwIDAgMC0uOCAxbC0uMi42aC0uNGMwLS40IDAtLjguMi0xLjIgMC0uNC4yLS44LjQtMSAuMi0uNC40LS42LjctLjhhMS45IDEuOSAwIDAgMSAxLjMtLjJoLjNabTQuNCA3LjVjLS42IDAtMSAwLTEuNS0uMmEzLjMgMy4zIDAgMCAxLTEuOC0yIDQgNCAwIDAgMS0uMy0xLjN2LS4zYzAtLjYgMC0xLjEuMy0xLjYuMS0uNS40LS45LjctMS4yYTMgMyAwIDAgMSAxLS44bDEuNC0uMmMuNSAwIDEgMCAxLjQuMmwxIC44LjUgMS4xYy4yLjUuMiAxIC4yIDEuNXYuN2gtNS43di0xLjJoNC4xdi0uMWMwLS4zIDAtLjYtLjItLjkgMC0uMi0uMi0uNC0uNS0uNmwtLjgtLjJjLS4zIDAtLjYgMC0uOC4yLS4yIDAtLjQuMi0uNS41LS4xLjItLjMuNC0uMy43bC0uMSAxdjEuM2wuNS43LjYuNS45LjFhMi4zIDIuMyAwIDAgMCAxLjgtLjlsLjkuOS0uNi42LTEgLjVjLS4zLjItLjguMi0xLjIuMlpNNjUgODNsLS4xLS40YTEgMSAwIDAgMC0uNS0uNGwtMS0uMy0xLS4zLTEtLjUtLjUtLjYtLjEtLjhjMC0uMyAwLS42LjItLjkgMC0uMi4zLS41LjUtLjdsMS0uNSAxLjEtLjFjLjYgMCAxLjIgMCAxLjYuMy40LjIuOC41IDEgLjguMy40LjQuOC40IDEuMmgtMS43bC0uMS0uNWExIDEgMCAwIDAtLjUtLjVsLS43LS4xLS43LjFhMSAxIDAgMCAwLS40LjQuOS45IDAgMCAwIDAgLjhsLjIuMi40LjIuOC4yIDEuNC41Yy41LjIuOC40IDEgLjcuMi4zLjMuNy4zIDEuMWEyIDIgMCAwIDEtLjggMS42IDMgMyAwIDAgMS0xIC41bC0xLjIuMWMtLjYgMC0xLjIgMC0xLjctLjMtLjQtLjMtLjgtLjYtMS0xLS4zLS4zLS40LS43LS40LTEuMWgxLjZjMCAuMy4xLjUuMy43bC41LjQuNy4xLjgtLjFjLjIgMCAuMy0uMi40LS4zLjItLjIuMi0uMy4yLS41Wm03LjUgMGMwLS4yIDAtLjMtLjItLjRhMSAxIDAgMCAwLS40LS40bC0xLS4zLTEuMS0uMy0uOS0uNWMtLjItLjEtLjQtLjMtLjUtLjZsLS4yLS44YzAtLjMgMC0uNi4yLS45bC42LS43LjktLjUgMS4yLS4xYy42IDAgMS4xIDAgMS42LjNsMSAuOC4zIDEuMmgtMS42YzAtLjIgMC0uNC0uMi0uNWExIDEgMCAwIDAtLjQtLjVsLS43LS4xLS43LjFhMSAxIDAgMCAwLS41LjQuOS45IDAgMCAwIDAgLjhsLjIuMi41LjIuNy4yIDEuNS41IDEgLjdjLjIuMy4zLjcuMyAxLjFhMiAyIDAgMCAxLS44IDEuNiAzIDMgMCAwIDEtMSAuNWwtMS4yLjFjLS43IDAtMS4zIDAtMS43LS4zLS41LS4zLS44LS42LTEtMS0uMy0uMy0uNC0uNy0uNC0xLjFoMS42YzAgLjMgMCAuNS4yLjdsLjYuNC43LjEuNy0uMWMuMiAwIC40LS4yLjUtLjNsLjItLjVabTcuNy4zdi01LjdIODJWODVoLTEuNnYtMS43Wm0uMy0xLjZoLjVjMCAuNSAwIDEtLjIgMS40IDAgLjQtLjIuOC0uNSAxLS4yLjQtLjUuNi0uOC44YTMgMyAwIDAgMS0xLjMuMiAzIDMgMCAwIDEtMS0uMSAyIDIgMCAwIDEtLjgtLjVjLS4yLS4yLS40LS41LS41LS45YTQgNCAwIDAgMS0uMi0xLjJ2LTQuOGgxLjd2NS41bC4zLjQuNC4yLjUuMWMuNSAwIC44IDAgMS0uMy40LS4yLjYtLjQuNy0uN2wuMi0xWm01LTIuN3Y2aC0xLjd2LTcuNGgxLjZWNzlabTIuMi0xLjRWNzlhMy4yIDMuMiAwIDAgMC0xLjQgMCAxLjQgMS40IDAgMCAwLS45IDFsLS4xLjZoLS40YzAtLjQgMC0uOC4yLTEuMiAwLS40LjItLjguNC0xIC4yLS40LjQtLjYuNy0uOGExLjkgMS45IDAgMCAxIDEuMy0uMmguMlptNC40IDcuNWMtLjUgMC0xIDAtMS40LS4yYTMuMyAzLjMgMCAwIDEtMS45LTIgNCA0IDAgMCAxLS4yLTEuM3YtLjNjMC0uNiAwLTEuMS4yLTEuNnMuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS4xLS44bDEuMy0uMmMuNiAwIDEgMCAxLjQuMmwxIC44Yy4zLjMuNS43LjYgMS4xbC4yIDEuNXYuN2gtNS43di0xLjJoNHYtLjFsLS4xLS45LS41LS42LS45LS4yYy0uMiAwLS41IDAtLjcuMi0uMiAwLS40LjItLjUuNWwtLjQuN3YyLjNsLjUuNy42LjUuOC4xYTIuMyAyLjMgMCAwIDAgMS45LS45bC45LjktLjcuNmMtLjIuMi0uNS40LS45LjUtLjQuMi0uOC4yLTEuMy4yWiIvPjxwYXRoIGZpbGw9IiNGNzc0MTAiIGQ9Ik0xMzUuNSA3Mi43Vjg3aC0yLjRWNzUuNWwtMy40IDEuMnYtMmw1LjUtMmguM1ptMTQuNiAxMi40Vjg3aC05LjZ2LTEuNmw0LjctNSAxLjItMS41LjYtMS4yYTIuOSAyLjkgMCAwIDAgMC0yLjFjLS4yLS40LS41LS43LS44LS44LS4zLS4yLS43LS4zLTEuMi0uM3MtMSAwLTEuMy4zYy0uNC4yLS42LjUtLjggMS0uMi4zLS4zLjgtLjMgMS4zaC0yLjNjMC0uOC4yLTEuNi41LTIuMy40LS42IDEtMS4yIDEuNy0xLjYuNy0uNCAxLjYtLjYgMi42LS42czEuNy4xIDIuNC41Yy42LjMgMS4yLjcgMS41IDEuMy4zLjYuNSAxLjMuNSAyLjEgMCAuNCAwIC45LS4yIDEuMy0uMS41LS4zLjktLjYgMS4zbC0xIDEuMy0xLjEgMS4zLTMgMy40aDYuNVptNy4xLTEzLjFoMi40djEyLjdsLS4yIDIuM2gtMi4yVjcyWm05LjMgOS42di4yYzAgLjgtLjEgMS41LS4zIDIuMmE1IDUgMCAwIDEtLjggMS43Yy0uMy41LS43LjgtMS4zIDEuMS0uNS4zLTEgLjQtMS43LjRzLTEuMi0uMS0xLjctLjRjLS41LS4yLTEtLjYtMS4zLTEtLjMtLjUtLjYtMS0uOC0xLjdsLS40LTJ2LS43bC40LTIuMS44LTEuNmEzLjQgMy40IDAgMCAxIDMtMS41IDQgNCAwIDAgMSAxLjguNGMuNS4zLjkuNyAxLjIgMS4xLjQuNS43IDEgLjggMS43LjIuNy4zIDEuNC4zIDIuMlptLTIuNC4ydi0xLjVsLS41LTEuMWEyIDIgMCAwIDAtLjctLjggMiAyIDAgMCAwLTEuMS0uM2wtMS4xLjItLjguNmEzIDMgMCAwIDAtLjQuOWwtLjMgMXYxLjhsLjQgMS40LjggMWMuNC4yLjguMyAxLjQuMy40IDAgLjggMCAxLjEtLjMuMy0uMS41LS40LjctLjdsLjQtMS4xLjEtMS40Wm0xMC40IDN2LTVjMC0uMyAwLS43LS4yLTFsLS42LS42LTEtLjJjLS40IDAtLjcgMC0xIC4ybC0uNy41LS4yLjhoLTIuNGwuMy0xLjJjLjItLjQuNS0uOCAxLTEgLjMtLjQuOC0uNiAxLjMtLjguNS0uMiAxLjEtLjMgMS44LS4zLjggMCAxLjUuMiAyLjEuNC42LjMgMS4xLjcgMS41IDEuMi4zLjYuNSAxLjMuNSAyVjg2bC40IDF2LjFoLTIuNGwtLjMtMVY4NVptLjQtNC4yVjgyaC0xLjdjLS40IDAtLjggMC0xLjEuMi0uNCAwLS43LjItLjkuM2ExLjUgMS41IDAgMCAwLS42IDEuM2MwIC4zIDAgLjYuMi44IDAgLjIuMy40LjUuNS4zLjIuNi4yIDEgLjJhMi42IDIuNiAwIDAgMCAyLTFjLjMtLjMuNC0uNi40LS44bC44IDEtLjQuOS0uOC45YTMuNyAzLjcgMCAwIDEtMi41LjkgNCA0IDAgMCAxLTEuOS0uNGMtLjUtLjMtMS0uNy0xLjItMS4xYTMuNCAzLjQgMCAwIDEtLjEtMy4xIDMgMyAwIDAgMSAuOS0xLjFjLjQtLjMuOS0uNSAxLjUtLjdsMi0uMmgxLjlabTctMi4yVjg3aC0yLjRWNzYuNGgyLjJsLjEgMlptMy4yLTJ2Mi4yYTQuNiA0LjYgMCAwIDAtMiAwIDIgMiAwIDAgMC0xLjMgMS4zbC0uMiAxaC0uNmwuMi0xLjguNi0xLjVhMi43IDIuNyAwIDAgMSAyLjktMS4zaC40WiIvPjwvZz48ZGVmcz48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2MiIHRyYW5zZm9ybT0ibWF0cml4KC4wMDUwMjUxMyAwIDAgLjAxNzk0NjkgMCAtMSkiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjIwMCIgaGVpZ2h0PSI1NiIgeD0iMCIgeT0iNTIiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjMiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA3N180NjI5NCIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA3N180NjI5NCIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFNQ0FnSUNBZ01DQWdJREF3TURCQVlFQkFRRUJBZ0dCZ1VHQ1FnS0Nna0lDUWtLREE4TUNnc09Dd2tKRFJFTkRnOFFFQkVRQ2d3U0V4SVFFdzhRRUJELzJ3QkRBUU1EQXdRREJBZ0VCQWdRQ3drTEVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkQvd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhRQUFBZ01CQVFFQkFRQUFBQUFBQUFBQUJnY0VCUWdEQWdFSkFQL0VBQnNCQUFJREFRRUJBQUFBQUFBQUFBQUFBQUlEQUFFRUJRWUgvOW9BREFNQkFBSVFBeEFBQUFCNzZnelpuSmpGTk43Rjk1TzBucVR4Sld5VVZYUUxMbklRc0FodVcwbnFUblk5S3Rlb2JoYm5QMlAwYzZWeU1mTzBGZWx3dm0xTXJYemFTUko4N1p0M3M0U081SWs5VklseWprb3dJZVdVZWl1REFsWUpKZFRwUHNuT1JEOC9YbGJNV29OK1lDUWVPaDBvU0dTeGY2V055bXIxSnpuYk5FOVBLeUdqS3FmMGtDNE8xQnhiSUlYN2s2M0xVeElqQy9rbGpJaFJDWXRLK0ZacXlFYmFRU1dZZ3o3TktMTnJQNXpOMXBEc2ozSHNXZnVDL3FScWxOSU5DVllOL3dCTDlYZnFUcEJ0akcvS3JtcFVTTEZMS0VsTGJEcGZPOU5wZFlkNVhmRGtkZDM2L0h2TFlBK2cycHVSSnVFa25nWURJYjNhTUtxOHkrZ25GQzdGb2ZicTFNWjhvWlEzMjBLNGw1MzVHM1ZYVnlLYlB2eHBpOWZWajFObTd2bUUxTGw5bWMyTnlHVnJSMXVvaW1Lam5iT3Iwc0hZanpVQ2NqeHhMR2Z1eTJiQjRTd0RGb25nWi8wOFVlQW1PZnAwRHZRdWthTVY4bjJZcGZZMU4xUGx6aGpNNTRkQ3pTeGc2OCtzZHlSTkRWL2gwSTFKT2pkbWRHdEs5enRwRVBzakJvYk02UXlOQWM3bkkwVFhibXZucVFmT2ZxZnBJd3Z6K2prSUdmMFYrcGVySFowU2d3NlZSVm1yS3RXb0lTdGdsTVRZWE1lVzhkMmVPeFZnVmNVYVdTNUtJeXRUNldhYjFKU3VaaElEYnA2dEg5TEorUjNPMUpBbHpDSDlnb1diK2Jzc3dZOWV4elo3bEVaMzhrcUJxd1M0UUN6aldnaHVjNms2eEV3WW1zR2hzUEFmRzdwZytKQk5KbEptbG9qQTZXZkxuNjU2bFo0NTJzT2sxTjFNUEs2N2lVNHh1N3YrcVZxbVFXanlFUlJabmpndGJwQzh6WTk5cUsyWGRGV2NrazhHVUtxc2ljdjhoY1RRWm9mcVhhcEtOYThBOWg5Zm4zVndiQXdnYWI3UkQxdFYySjduMzVyV1ZFb3V0MTlxWWg1Ry9XL1F5M3pSUm1kck1NU3BxdzlaVHJuNWJaVHpxd05mNGFMRTlBeHU5WGRibjJHbEpCZGNxdmpMeXh5ZDREbWR0dnM4NjgwS21TcTRTeTVoMDNKVXdMcTFNYmk3bEZRL1YxYWlVT1IrRjFtbU4yTGZIUDBVaWRHbTlxeDBiTmR1Um82VTE2bUtqSG9BOFdtS0pBV3pHNm9mMHE5TEpJak5jOUhMZUdDZHdhbnR0ejJKVjhFc3I4L1RrZk84VllDWTM0ZjFkVXp4bTBXR3pPVXNESVBMMlhqbDNxMlE3bWwramtnQVZBcGlqeU9xZ0ovOURKWmxaWTlJd041OTVPOXM2ODVqcFNNaGVLdWZzaVhBMXF5RlROcTdjZzVuY3NGV1NtSE1UQlFOOE9CSHBObU5WOXE0d3NDQVo3cUd4WEpOSE9WUVZkMVpVSzdyU0dxcXhVcXFyb3dDNDB2ZmZiNTZpeFBFUW1sZWpuamdRZ2hsT2xpS3pzOVM0NDNMa0s3bk82cHFLOEtyUzZXZ3hyblJLeGQyY2dBWEtTcVdRaXNsc3N0VGRURW9jYno3Ym4wYnNSemtBa21yTU9sTm9aT0VwMUYvU29aamJYSWFqN1NlYXRWRlQ1TWVWMFBvWkVrREtuUzcvcFV1NjFIMDhhQ3lPMHJ2eis1SVlGQkVrSGxiVmdUYU92TkY5a1VnUzhNWHZyVlBNWjExUkJlYU1HbDc2a3N6U3VFTWhVVk1zeGxCalNUYXZSeXAzTXh6bVVrcSt5UXhJWXRTL3dBenJsVHJXN2dEWTB4QkcwV082cnRnVHlydEpuTEV4cTZBYU9sWHk1V0RZMGh3dWtxVVkxZWpqUTJEUnNEcTVQZDE5a3E2dk4vTDF5SHFidXBkV0JlNUZUaWQyb25kMU1kaEtsUzVFckpQSjJ0elVwdzdNL0dTb2wxOVFZU3dVU2YveEFBc0VBQUJCQUlCQkFJQkF3VUJBUUFBQUFBRUFRSURCUUFHRVFjU0V4UVZJU0lXSXpFUUpDVXlRVE5ELzlvQUNBRUJBQUVJQWRDY3lTbGlZblcydW5Fbkh1NE9tVzdOdFFZNFh4eWVSdk9jWTFtZHVQUk1sVkV5WjJTeXFtUlRxcTRQTmtNbk9OeFYvb21iWmIvR0F6VDVGZXg3YnRzRUJOczJNZWhXTE5XMWVBTzI5NkN3UERyWWZLWmFkVGRScm8xOG1rN1pVN05ha3VENlptdGxybXNUckJFazFQSkRtaWpOcUNHTlNzSVNTRk9HNG45Sk1tWEpuL3prdjNqZVVYSUpGd2VYSUg4cG5DWnhpcjJwem5VR2Ryd1pJYzFhbjlQYkJ5czNhZGZnbk5iby9zeFFFRlQ5VCtveDIwbnlCeFB1RHBtK1BOV0kyUnRnbndQVGVhTkMzUlJkUTJSU3dNWTZzQWJCTTFNbzVlMWlOeUplNU9jUk1WZU1rZGhEL3dDY2xkbmJ6blpqZnJJSk9NZ2x5Si9LZjBuZDJzWE50Rzl2SXhZaGpCbGJkbXdKRkUyYlhoUlpJRldMY3VoOVRaelNtMDl4UVQwVnZOU3IwWEVoUStXYWZTR2wxSnNVczI2TW5PU0JJUUE1b3lXcE1CRXNhSXFCVGNweG5LY1k5Y25jcVpQSmlwem5IR2NZamNiOVpCSmtNMk1rK3NOay9GVVN5ajcrZTQ2bWI0R2xJYjdGcXhFaDFjWlI2NWpISHpSRFF2bmwzM2FsSW1aWVV0QmFXOGZKV0gyc29tdy9HNEtKSWFMN3owWXJUa1dlQ0Q5bjZHYzZOMlJ6OHBrNURZbWR5L015dUlkR1E5dmVuYzFXcTNFeDBKTTBibWlBdEpaQ3JUKzNHS3FMa0wrTWVaMk0rZ3p6VENaSXpqeGtkQXE0UUhNK3JkMjFwWkloanhuMXFjQm81ZXFjNU1HcGxTaW5xK1NoYkd0TzVlWFI1dE5HRzIyVTZhdjJtdUhwMnd5MzJ6Z1NJeHd1cTdaQ2NLeklwR1Q5M1kxNnRYTEErQVdKc3BGamJBenVmNGRZMkZoa1hnY2lvOU83SE5SRTV4dTVWZ0Jzb1pFKzhWTG5veks4eHBvN1prNHlVcHNEZVZsMm9QM1c1WWJCVkxGTWlTTjhsWXg2UUZLbGE1cWdqaGtIL2dPenNHYW1ieUhBYnJSc0JGZ1RURTFmcGhCcUdHN3VrcXluYlhybE5iRjJOYUpGRXlNYnFKVkFrQThzbzd5VFhxdjBuZFArcHNvSkV3RjdEY0JFUXRuellIVjFvMk1hWS9YS0kyQjNNRTd0WFllNGJUK29NSndrY0JudmlsQ3E2Q0NLc0hlNlowcmRlT25Yemd6M2RCSHlRR2JDYkVra1BVYlkzVlVvc0xLR3JxTnh1M09QczlRMTU4Q2lNcmJvSDQ1SzlCNXhYMTZxdXVRUG4ydWVYRlRzaFRPdFhVZVlUL0FWVEFvUzRtcVFsWFJNL3dEVkI0dFlvYWVqaW1zRWU1b3BXMUJRVHVScnArbDN5ZGpBV0ZHK0Nta2tyTkRFcjlwZTd6R3hKZkFPUncwVHdkckZmWEViTHBtejZ6c01mdmFob2RwZVdUbHl5OGNNYTYxcm9XakFXYlBKSk5vRllGQTRuSVdXZERPMmNldXJLeVVkYmFsNnJ0aVNzUkdkUGROMk92c2hiY25iSnlhMUd3dy9IN1ZQSWtxTk8zRVNMc1pyZXgxYkxUc3VDRzk4U3RUcS9YeFYyMWx4dFZYSUkrQ1FPcThpY3lidEtjR1Uvd0JCc3h0V1c2M3VLdWFXd2lUdXQ2OXRSVUM2dFdVT3FpUVFNaldrcnlZMk1GTXNLNHdpZHdVTnJSTkhWSng3Y09MWnRYbWhJMGVwTG9ORVpJbERTd3dNYTlJYWp3SFBOd3VyQXVaRUllY0hDUkdxT3AwbUFzcElNM09tbUoyMFJTS2VKYTJxbXVTUUtpYTBuZVNaU0J4dS9zaWJVR2FZbjQrQzgxdHJQM0k5YXVIU0N1RE02aDlMS1N6UGR0TXV6ak5FdENZNHdXZWRyV3B1ZEJCYksxMGhIVGNWMDd2YTZjMElMT3FiV1EyMFpjZGlYS0ZCdE4wTE1zVmlCMUExdTdtU3ZDWnZ1b1F6eFZ6eXk3QjVNZ2lValhlS2RyNm1kQzlRRW1ZNG0xUnJmVE4yRGV2YzdLMjZONm1Hd2VQV3FhbE5Qb2dGMmIwMGdzbzR3T29GOHdQYWhoc3ZrU01FZUZvVnZCRk15dndtMnJtZXhBNC9mTm9iMUNHcHF5enNDR2txQzJtWXJMZEdQc29mYW9Id3YzV3pKa3U1NHBHbXk4dFJLYmNpcTRnNnVscytvRk43VW5sMTZ4NytvMEcwVnRqQjY5bzJkUGFnYlBHSXRsVGdlTDFoMzlNOVBuUEV2RjlpSjQvbmdQS1pTVXBWc1IwWXZHV3RPWlFrRVdUS2dHVnhSUmtFRlM0eXdiNlZYNEpaUGJOaEw3U3ZOQ0RGUGNGQWd1Nmk3QllXT0NtcGZhMHd1UDNoNGcvYkl1QjM3UVZPUlRVdGNFeU5ZNTJrRHpvNUlhOXNmeXp5NVJIOTFaRks3cmJFSS9iNXlnMjgvd0RJUE9UWmlxSzYzc3FZR3ZIbnE5eElyNi8xQU5QdkE5cDE0ZUowSVV0Y2ZJYksyTWhEbkZ1U0NidUo5b1VGc01jbmYxWDNQMzVmaFFORk0vVGl0bGRXWDFYc29iREVnUkZhNWtqbFRqbFpwaFdPNWw2djdqSk5RRXhEZExxeU9xb1IweXJzNGFMWmlLd3JZdGVuTlpIRGpuRXppdy9EVHNPZllqelF4Z2V0UC9qcnFjY09CUTI3VjFCYkcxUkszY2VaWHl6eXg5dlBHVTlJT2xuSVBsblJHa1NFdk4xZlYyUjYySDVOcld3MDRUNStvMG5xdFNiVEQ0WlpiVVgrV1M3WFRoeHVrSXRkKy9VUmEwMnZMcXBIM0k1dERPeGVIRUY3RnJPNWdSMUIzVVN6cG5lSTJuNnN1dFo0Z3hiMnkyaTBqZEN6YU5VMk8wcmMxeWxtRnJob1pkam9CYkNtbWptMUhxNEcxalFMY1RZOWNzVS90M1NnSW5kaU1zRC9BTUtucWRiRDBKWHhvNFZtOHFhUnBGMUhBK1Y2WXFLajFUTkZtUS9ZSmt6Y2xqQXJEVlhwNXRNaEdyaSs5c1JJTjVVRVY4bGV3clZwa1lOdk80RldsVzF0UnJlcnhuNk1RWFk2dE5CcW00UkNzK2ZBL3dDelhsZXFMMnBZMUpKTFo1ckNaTHZialVuSmpqb0NhS2NENThKRis2L1l3Tzc3WGF3V056WmQ2anJxQTBzZlI5WEJzNUt5ZWVtc3RkSGNSWG53YmJSZ3ZkT0xmOVdPd1Y2UTNHeGU2Vk1ZZHFQcTNGbTlGMktxS0N1SnNxYU9GSXU5dWoxMW9Ic3RvM09vWUUva0lTR3JKTGlDaVdHQTA2UlB5ZkU2ZFZjNDdtQ1ZTb0tnbXhoMU9NQkZtTHRkdTdwR2lPYzNsWWdrVmZ6S0VlejhveEtvNHU1TWZEYzBoWXMxZkxNNE1wMzhNcmlHTTVjdGNYL3RsNEVUTFVFc3pTYlhZeDRRNWNuRXZEVHlpU1pLNjJqWHRXYW1zSHB5KzcwMHdnUlh3NnByZHRXc2ZNOGtZK1ozN25odDRXY1JTckpCNUhpNzdGSStsT0pUUUZqc3Rpa0FhM1dBVnhOUkJqZXIyMld1VjRKRE9UaDRFall3ZStIL0FFeWRGYlZydDFIN0U3MDNRZE1adWJDZjdlR25EZ01rbUprMmV0aWlBWVl3SGNwUlkvRWF6Y3c1UDlrMjJ1Y3pqTHZjRnNTWDYyTlZhKzJ1Q2lIamk3YTFIektscFJQKzFJdDZCcisxWFhtdUorS3k3QnJqZVVTeDJXaWlUOW8zYlJ2L0FKbFJYVHJTZUZkdytibkRMWkowcjFLRUlFZlpsUkVSTVYyYlQ3UTRqN01jYXhPc0FJckIyeEZXVjBTR0JERnFiV2pzYk96VlJNZHFjSEg3T3RmTmxTSEptMFEzYVZVMDJDYXMzd284ajlNall1dGpvbVgxRVhUV0xkaGhDdkRqRWpUQ3J5eGxtZFY1OEpFaWNMSnIwTDhzZFpWc3Y0TzF4cm84aDF5QmZ4eDJ0UUovSjBDMnh5b1B0OVBQVXh4K2V2UEJyUW9LNEdhOWtUL1Z0OUwvQU5zTFQzQTV4cGc5VzNVdXRIbGkxTUsyYmVudzNua2J6eG5sWW1lekUxdk9RMUZ1VnNwbzlEUFFiSUlRQkJaUkRpZHZDUmdDUHhLVVJ5WnNHcEIyMUdYWHJySlc3MnJZMEcwK29LT09NTHZKS0lGYytBRHlXbkRUNnljUUtKRlRKSXhJK1hZUVJIM2NOZ0VjRWVRMSsrUnlsejFZcXhheTVqdTdQZ0pGK3NUWDVQNFUzWDVXaXpPU3JMaWpweFdKcUliN1c3dnA0VEtHemlJK21VQlRvOG4xbzVVK3FZZWVyMmdvYWZZWHp6bVU0eklhaVpjanJaWTArbVFUdHg0MHo0WG91Z2xRUkJPZ1hVWjJIV0ZzbzdCMy93RFhRcmtzS0lpNVBYdWxWVnd5cFZmcFBRamgvd0I3SmkvTGxQUzJjNG5iS0FaL2piaU1iaXRabGs1a2RjVS9LMGwvb0Q4ZEs1VytPN1Y1am9YNUdRMlA2eHhiSGZTSHZhN2NIZDF0TC9rYVZtUnB4amNhMU03RTdWelVZbTl4bkdsZU5sdGF3eDhZL2pqSmV4RnlXZU5xWVFVekNaV3Z6Ly9FQUVBUUFBSUJBZ01GQlFRSEJnVUZBQUFBQUFFQ0F3QVJCQkloRXlJeFFWRUZNa0poY1JTQmthRVFJQ05TY3JIQkJpUkFZcEtpRlRNMHd2QkRVMVNDMGYvYUFBZ0JBUUFKUHdIa2xxWC9BRTdiMXVsYVNSZ0Evd0FCNEZ2VjkxcjVNdkMxSExkTFhwMllXdHJXSVNKQU9MRzFkcHh2cHdRMy9LcE5tUU13RDZYRkhoVWQ4NUFyVCtBUGZGcTROY1Z4SUFwM2ZKd0JwREhoY05JUUYrK1J6TlcwMDBwWnBNVVZPNGd2cHpxMXZMaFhpUDhBQkRVTlJGaTF0YUFLUHh0VCt5enlPWGY3cmU2c0swbUpnYndqaUt3RFJUN0syOHZFVWxvNWo4SzExdlh3L2dXN205UkZvanhQV2hxRm8yVkJjMTJTaWhteWUxdU9OZG9CVzZJbFFDTUl3Tit0U1dIQlJUaTk3ZlUxckQ3Sk9UWnE0SDZTaXVCeGFwMGVYbGxGci9VMVBJVmhoaG94M1daK1AwY2N1bElIelMydFE0aXBDakRpZktyNVkzREN1RnFqMXRhOTdXcWZNMmJrT1ZZcE5KQVg2cUtmT3A1MXlvMUlxb0c0bXNaR1QwdlV3WjE1ZlRNZHFWMFJSY21wMmdrUGNFaTJ6VWVJK25FQWJLVEs0SnRZSG5XUGhMV3YzcU43b0tIZEZLdDlvV1BYNkd0R1VOelVoRWlXVmkzQTFqSS82cVNjWWRzU1V2STEya2hEV3ViY3VYb0t3a1NSQWFhVmhrU2JXenJwYzlLbHZpVzZjcU04elN5SFp1RnZwMHBtWE4vSXgvU2laRlJzOXRWMTk5UXZDQjR3YldyRkY4cmxjTzE5V3FPUlpsMGRtR2xQbkxEZFZkU1Q1QVZIaERpMS93QXh0cG1JdnlPVUcxVFE1Z2QzTkJJRkIvRmxycy9iNE10cGljTzRrUlI1MjRVNFBwUmNabTNyZEt4YkNOSXdVVkh5N1ErZFlkemRiTlk4cXhLWnNNTm5xM1NuUWtqa2E3aWs2VnlGVEtOcnV6TjBydFI0MjhWalhhTHVmeDFnSHhKdzBVT0Z5UmdFNTh0MlkzNVh2V1JuZmdWNVZMbUNTWlNvcnRQMlRCeERhWXVlWFZZMEg2OUJYWjMyeWpMUGo1UUduUDRtYmRpL0NBVDZWMjg3RzkyQ3htVCs1aldPMnljNDhRaFVOOE5QbFFPQng1anRsYXhWZ09ZdG93OU5hTzF3enRaWEhjRlkvWllYbmsrN3pwQkJob2ZzcDV1OFNlYS96TjhoNW1zRjdTRllvZHM3R3hHaEdVYUw4SzdKdzBXeVRPU2hZRWFhMnNhamthSXJ2d1NIZVpmSnVaOG1xeUpNTXp4QVdGK2VuaFBVVWk3ZHRFcGJsMzBnSG5VbTNubUdrU1hVVzUzdHZINGdWMjAyRFMrYlpZWlZqK09VYS9Hc2V1SXQ0WmJQZjNPUDFyc1lkbVl1VHZZbUcreFA0MFBjL0VOMm1GaU5DS3hKbGtkOXBZblZiMWgyMnJOY1BmZ0t2UVZuZGVCZkxXTnRJTklzT3JhVk1yNHZGemFKZm1UVTM3eFBkNTVoeFZSeGYxSjBXb2NrU2QyTWNQVTlUWFo2cEhIRjMyYTVMWnJBZjAyMXJCS01MSVl3WlVhekJmSHo5S0RKbGJNTXZoUFVkS2pVNG1PTW4vMkhHM3FLbFpzYmlpVlZ6NGN6SEw4QnJTQ3lpeW5uV09seUdReTdMdzZwbFByMXFXWWJOZG51N3R4bURjeDFVVVFEeHZWOGs3YjYyLzZnOFh2Rk5mQ1FYY0o1OHFpdTVGb0l6NThQaitWVEZpNXUvVnYvQUlLN09tdWl5QnBuRmd4VjhvOTVHdGRtdnNXZUViZGVTRytmNFdBOTlaaGx1Vkk0eG5xRCtsTGFURGJqNVJvUFAwdHI4UnlxNnlwWXlhNk1CUTNBMTA5S1lMcHhwNUkzSzVWWkRhc2JQSUIxT3RTeVpNR2p5aFdhOXlGMHJDSmlzUm5XRlkya3lCbFVjTTNMVW12Mk03UmpqUXZkOFBhWVdIRG1MODZteHZ0TWdKV0xZUEU1dHgxT25MclhiOEF4VFNiRFpQSmVRU1h0bGJ6dlhaRFN4NXlvZTlycmt2ZTUwNDZVTFplSU5HNnd5TkYvVGNWRGo1a3lFWlJFcWN3Ymc1aDBQeHI5aTFNR1E3MCtMVmQ3M1ZFWWhrKzNmR1Jxa2drNWlMbGJ6cDVmOFJURENMRUVQM3Q2K3A2NkNrQ2trRmJra1pxWVdGdHBiK2JTdTRtaTlOQllWZzhSRy8yUy9hcmxKelg1ZVZxN1JXT1dCYnlCZFdUL0FKK3RZV1YreklTc0dKT0xRUmJWbTNpUWVxcnd0NTEyVE5PcEtBT25NTUd1ZW1sZ09QaXBESHQ0cE1QS3AxNGJ5L3FQZlYzT3lLSHpJMHJER0FwdUZHRmpwVWhBV3NZL2FHQndzNHhDRjd2SU1PNHphRTZuS1RXRXg2aEIzOWhvUldEbEhaTFJiS2VYSllLVHpQeXJVT1JLUGVLYjdXUlM2aTNJY2E3Q1IxbGN5c1lmc2lzbjNyalcvSFd1eWY4QUQ4VGdwdzI2MWhJUWRNM1hXcEZkTmJNRHBwVDVRY3hBNjA0RHlTTlBCYzh5Ym41MU1rY2tZSWpFcldCZTJpMU43TW14QmxjZUM0NVZqcG1FN1pGekhOdEdiaFVOc01icXJxdDk3TnUzNkMzem9oRWlRbGI5YWtaWW5ZaUZ2VFFWbUUyRWsyVTY4MWNhTjg2bFdLUFM3T2RCeXFDZkQ5bDdSVGlzWmh5ZHJqV1U4SVJ3QzNBdTQxTnRLd2VKYVlIYm1YRjc3a25TK2JyWWNPbFRvK1cyYksxN1hGeDhxRmxHZHY3YlVMQ1FGOWVoSk5XdGxDdmJyOUVya2tTZTBSd3R2Q3lrQlBXcHlUSm1obEQ2bkp5UHFLN0diRVFhZ3lBMnorNmdZOFJoeHM4c2gza1AzVCtocDhaakhlNm9tNlZnVTIwWG5iU3NZMndNUVFZZktMQnZ2WHJFSEVSVE9wamp0YlpEMTlkYWQ0TURHYzFwUEQxc2VZdlNObFhkeUR3TDUrZExKbVZzNDFySTdKeGJMM1c4eHlQL0FBVlB0MWJ3dW8vS2h3cVJoYTcrdnVwOWxFVTJVWUI3eE9sNjd4UUVtay9kdTFyenc2YnJueHgvaUIzaDYxalFleUdiYVRSaVBON1FvMXlYNWZyVXE0VVJ5cm5Fc0oxakhFQWNxeHdqd3NZWVRRWkw3UW5nYjhyVXM4VzJGcEZmZVVnQ3k2K0VqL25XcDltOG9zN2ZkVHhIMXRvSzB5akwrRVUrZHB0NzZNQkxoOFpsR2FaWkxwTDUyNUdwMVBzaEFXM01kQlVZTE9tZlh6cFFyUS81aWVHUk9ocU5zUGlocTBjbzQrYXNPTk9DbzU1eFdPd3FaZnZ5QS9JWE5UaVJ3dVo1ZnVEK1ZSdzkrdFJabk9wYm1haGFwWFJjVXBFaWNtcnMrN2JQYVhqYkxwK1h5cnNwM2VjRmx6VGtMWWVnRmJPR051OGtTMkI5ZVo5OUs4OG1kYktQV3JncWlnMG05R3UxallIZVJ4d1lIa2FuMytCY3JjUCtOZjhBY0tuMVAvYWNPUGh4K1ZUdVY1M3N2NTFoOWxIenhNbkQzSG43cW5hYWJKbmxjOFNhdm1jMU5jZ0hTLzBMY1JSMmJTbzB6dmlpZTc1Vk1ES3QxOUFEcFJGcEVJcFEwa0w2TjFGSkpIamlBTjF5RFUwOHM3d2xzOGh6RlR6dFM3U1BGNGZaa25UTGJXbVdpdDZqUXlSZDFqeXEwMkdnZ0NScWVBdU5hVlliTUVjcnpVOFJSRkVWS3RaWlhTSTVWSjRtbWx6c1pXUHd2WFlXRFo4SEprV1JvZ1djZFRYWjhNVDhMcWdyT1h0MXB5WGMzdWEwVWREU3NVTGFHc0NqNXRidUt6eDRhU05UbVhqbXJHWXY3SWJVNW4wdlVNbTh0NlNVZTZvV1krbFljczBYRlNLd0VLN1NLMjlMcWI2MWhSRjdLcEJ5dmU5WCtORS9HbFB4cnRGOFBHQW9rQTRsclYydEppSTlxRXlzT0I1VkVLME5QVEMyUzlZSEFoWWcyVzdHNTBwWW9wWkg3c1pOcmNxZGo4YXZyNVZHYzNRQ3NLNk5lc0xuSThxdzVVZWxLb0xXNWNhVEsrVUVXOWFMdGh2WmcrUit1bFFqV29RS2gzWlFiNmNUU2tLQndxSE5MaW1aWlZrMVUrZFliSy9NV3FENVZGa2MrTExVZHBUYk1ldEx2eFNLeTY2QTFIdEc2MnFLM3VwQjhLd3NTUll1MExUSGlNM1FWaXBXV0VaUVR6cEVrSEZzNjNyMmY1VWNQOHFmQ240VjdNUGhTd04rRzFZY0gwcnRIOTN3K1hJVWp0ZTQ1MWo1TmpBQ3hVSUJtSG5XSlo1c2ZoaGRPU2cvVEhCS3VHaFptamtOcitockJ3UjdXUGFDUE1lRllLQVptYkx2RzQwb0JwQjNyVkhSMmJjbXRYYUtSUjRPZllCQkFEbTg2N1VSa2lzZG43T0xIMXB0b3pDOTh0clZHS2pxTkh3OERvNVc5dUZkbVJ4bVFadDZiU3V5NDlxOFJPMEV1N2JoVVl2enFPazA4cWlQd3FIWDBxS3UxY1poR1NNWnRpMjZmY2EvYVB0V2VIRVNoSGpNaTd3SjFwUWtNQ0JFVUhnS0JwVFFPemtqS3RZOHEvYTh4b0w3SmRtTjFPbkRwWGFJeFQ0QzJ4S3FBTmVONllVMU5YYlRZSER1Z21tSGV2SWVZcjlyWmNYZzVwYlN3bEFNdzZYcTFoUUZBVkpzOXBIM2dMa0VWMlQyYWtjUnliU1JqcUJwVWVFam53MzJDSmg5VnluVzVKNTFHdFJpa0ZLS3RTMW9UR3R0YTRTWXFORDcyK2k5Q2h3amI4cUVoSWpIQVVqZ0s2SnZEbldxMFRUbW8zTHpZZEhYM1ZoMkdmRWNhYzBUVjY1cWFrMzBsY0grbzA5d3JwZjRmV0ZjRmpSYU53Y2FwdDZmVThNTG41R3JheERsNVZyKy9mN2FXaFMxeXdhVy9xTmYrVVAxK3FvVzJNbUg5NW9hV2pZK3V2MVI5SC8veEFBakVBRUFBd0FDQWdJREFRRUJBQUFBQUFBQkFCRWhNVUZSY1dHQkVKR2h3YkhSLzlvQUNBRUJBQUUvRU9Ld1Axbld0OXhYNWlQeFVRbHhJWC9BUlIyQ0pFMWppVFlNbzdsYm1PeTVZTTBjalkyNjNVTzRLN292U1dZM0NVQjY0cUswbXp6VnV5SEJLc0kvc1gzclZZdjFhS2RUMVRQSVRhWUJUM0NxTE44bHpER3ArbVg3dXcvRnhKZ1k1ZXpPRmRwL0hFZHlWaGNHcTROcEVTNGh0YW9SNk5wU0VTYVRVSjJYNnBHOS93QWRVbEhqd1FuUUJvaUxSMjIwMXRWRldQR24wWURRcGtVOVhkM0hhY0NBSS9HSWgwem9NZFdDYXdwckRhT3FuUU01eUFNTDBvTnVjWlZmTmJYTXFoZFlXRENPYXJHTnhJcHdDMGViaTNiaHQwMFloM0pyZ0Qwc0FVUUNPcE95YTl5OEh4TVNrTGg2bW9aTmdHZndDSU1SVXVFNFNMenl3WHNsSnp4T01HRzdqc0F3elk4d0xxOHB0SXQ5bmhLcDVoaExsRWpTWWdBRVhEdGx2MmJaQzd6V0UvZ042bDRkUXdjQUFYdkd3dGxVL01haWJ3bWhnODBRWDlBT1ZpRU90Z2YyUXJ1d3NTS1JNZnlCTmcrcGU3ZXF3OVJVd1VXaXNKdDA0Y3JHT2JSQyt4MUNodzl3bkszV2dsSHdWTnh3QVFoNkFBT2JOTVg5Q2kzajJTdHRWaUhLdjhjeWprdkdhRGs1SEhMUU81aG5BR01KRDNxVUNLR1NGYUlPaHhTTGZKcUcwbENSVzZBSW8wbS9vZDBSRzdwMjN3UUtUVXNvV2t1U1lRTEt5aHdjL01rVkYyUW5IRTVENk53ZFphUFJ1QjhMSFZNdkVFUDhQaEFkN0xSTFZhSlJUTlZLTE5GSWh0c0dEMG1nSW4xRmduaEM2RkNReTVlNXZ3dlFmUWJLYVkySCtSUTRybWVCeGRDNHQ4MHB2ODNEYnVvYitIOXl1ckZEdEF6MndyNDZ3c3l1NnRycmFvN3VvUUZyMk5lTERuelJHNG5MWmo3bjdJQ1pRVzBibVhyZ25tTnY0SXJOS08xT1kvUVo3U29PRmhpVTd2dW81cEhDd1lKWkJlTGNoQ2NMR0hMNi9NVDRKekZMaEdMSU04MGdxZUF3TkVBRml3NzdrVlJSb2pibk5ISzgrU09iTE1QS0h5dmdPV1VGL0tYTFNRNytVeFMyUHQ2SC9pQStJUDFLNWlVeGFGcXJ0eVNoQmVRSlJ3c2M0QnFNRHplSGFWeER4RldDeXlsQ1hoczNhYkNjSzI2N0w2a3dFaDZHbzlFZ3c0aEFRYWdleE5sbCtJVlR3bXM1dDlHdzhXWEwvak9xZU1laTVGRWQ3SG0wbkFJdWFIRDdsSS9yQWV6aGJ6OXBnSzZ3UFhpK2pUbHU4c296eGc5WEZJOG5CNWhFd2dFUDFjVzNTUGtrQkdVMVdKNW1iUDJzSytKRmZRcUVaN08yTjk5ckRwS3NUbjFFZzFOMFM2bzhXeDZYRlJYRVo3WThSNVROMFY4TStMV1lEUVdnSEtuTGRycStMU01UR1lVYWRWTnZKbGpsdWczY2VDVUNpNldvdEkyam5vL0tIL3plQXRjdHJRN1hnYkJPVEhjUndQT3Z1RTF4YU5CZlB0NVh6Rkx4UHkxQTgwb0J4U1JCRXJ1bFBMTEVDUzh0Z0t1YnptWGFDRitoWUQyamh3WkF3L09ybmIxQXNCR2M4YWRqeDlIekJOR1V4SGd2Z1RBTUQrRmcxcGxFSEMrWE1sRkJ4dW9zdEtORHVuSGc3V29aZnlqZFdOcFRnRnBwRFNZRkh3MVR3U080Mk05dUpWdUhoVlNsMWxyTkV2YVFhcjhScU1LcWUxY0VWc0xlbHVGd05hNjFzRXVyU3BuRlkySXVKWVhvY0dTMlRUR2JyREtscml1dmgxczR0T0tVZ3Fxc0tIZ3VZTDNxYk9iRXZ2TWdKWElQTC96ak8wcXBGTUd4QUJTMGhRWDBGeDB0dlhvRy9KRll3VFVOaXQvTVB3eXZUQ1ZvMUE3SlpFSWVvQWx0VlY3VjJVYXFkM0NOUVlTb0lRc0xiN1g3akNZUWRKS2kxMVh5TmtIQWtqcVNxS1JXd290c1pLUDN5SzJZSEJRTmhZbWU0UGhYc1VzNitpSk8wR1lOQ2lpbk1SVTNWQnQxL3VvM3JjeVdxNVQxRHd4NmxQZ3NYTEVjOGthQjQ1bnpoSUtiazlBSisxdEtaandlR2tXL2hJSnJUaEkwMnZnNWl5akt3RGpHRmVETjV4OVJ4OFZZaTZ3ZXFia0JTSmZzU1BnZHhyVXVjbFBPYmgvVWFTc0lFOFdxUVplN0xjcmFXNHVSVVBOa0ZDNkJudy9Nc0JGNzJWcUtwbFVyeUxqeHpvalFneDJjajZGcmZXREs0eVBzRkN6NFFHSDZBaUtEYnJXRDQ0TmxLUTA2V01XQzNNdVA3TWZXU2xWU0tMOGhkZ1Z4YUUrSThFOFM3MVFCNzdlREJuQzBQNE0vaVQwNjJ3eXEwM0xNZjNVYlNvc0JwQjk2ZHQ0Slo4TndKVEpKZTJMQmdjaXdRcTE1dXI3SlpSSFZndHdXUFpZNEc0Tk5iMlgxVXZvOWMxS3dHdUMzeE52UTNLdHA1a1M2NDJGalVJSFNwaWVUNXZIUkZGajFOVzhneFdhMFlXSEYvd0RUN0pPTG1BQUR4Zy9wRDBhTVRCcDhrUlZwWUtpRENuSi9JdjhBNVh3TDNQYVpoRmhSNVdLU3QzYkdpaWk5Z09VY0taWGVhS1VJN05MOEhDa1hMb044Sk5GVXhWMVlMN2wzL3BCcEdyaWlrRTJBb0RFc2RvSkVwZnpkWHdLM3NYdU5VQXo0QW9KV1V2WEx5N05NWEhnazZQYkRZN0YzREc1eldiM0J3T3RHMW92NXFXWmNYRHMwenJzZVIwanN2aVZGM1pBOWd6QlRoUWgreGhoaXkyaDhXcC9JVEM1Q1lkMkIvS2ZTUHVtMm0wODNGKzMzYkZxcmhhai9BSXdlSzRtTkxTMGFoZVlXMTVHa004UVRsZWovQUVtSXE5SCtLUWtuNXFMOFZVNGRlYUNQM2E0WEhkbThlWHlRbzFsLzc4aCt4RFI4WS9yVVM2dXhWbXAzWWE5WDNIUzlZN3N2UGo0SW01UTN6TDg2ODhEQ1Z5TEFCenNEemRSMjBVcGJvbFF6NXRzdEFSRFA5dEpmVkI0dnArcGQ4NmdsNXVtbUtoc2hFYUNZdVFFc2I1TWdCVXpJcWs0ZHRRdjBua3VFbEdrUys4dzJhUlE5T1dHZjVnV2c4RnhoeXVsRUtsSHZIWlEyeEZCYTVCUVJMdU9HZ0E5amJIZ1p0dDhrdUFnQXUwRi9Nb1I4U0Z5RVdvdGRiWmJtNnlDSzBENmliWXRrYnkwNGdKZ0NLYnZ0akczNDVROGsxZE1GZ3VlWkljYzVnQS9BalJDZW9vekpZRVRjRWJVZ2p4NWdMV1JpakZMd1ZBdEU1TFRhSjFDN2RmbTVjZ2JqWEpYUUU5c2NabEpWcXlPMmhRNE5ic0pjeElySFZvMk9NYk1pNFZGdUs0aVBDS0pSSDdHMkZGdjRscDl3aGtEcEdMaTZuR2hrRENtNjBhWjlyTHNEaTVrUU1HeEFWdTdYZ2lyOUc3Q3NsV2FTVWxYVHd4RmV3VkpCbGt2NnVGMmVLbzJNaXFnNjdxTDdIbTN5SlMwTGhMbjFOZmR6S29UdVdVc3BMV1FuSjF0MDdZRllncmxSNGVwVGFrT1dRcHZqYXc1WWNKY2dLTDNLdGVlMS9pQVg3QVJjUUZPdlpRYU5RSlVJY0l2UkUzUUlhRjBmZ2hMQXlWY3VwZUYwNTJUUnpxUk9WV2N5OWl5aElXVldIZmpYQzVTcEg2ajZpS0lXamV2WXRvWFpXN25GRy9EV3RYWkVFanFnUW5GRWJLeWpKQzFHdUdCSVZ3KzRaaThOcFV2b3VVWkJkazMxVlhHeWFoN001STB3MHRSc0E3QjVjbFcybWdVQkwvakYrd1Y2VmFzSGZtQk5mSGdZRnJoSllqbjVXWVM1dlRvejduWENVV3czVTc2am9WYVRBNk9rOWJGUUxZaFpGeTErbzJnM0tLQy8xVW9Ud1FmMTZ3Mm1GMUJES0FlaVBhc3MwdHJ1UXVnR241SXJjcFB1dWc0dW9sb1MzVUI3Q21XTlNRdGlFckx3VCtKU05rRjgxRkllUFJDOGJLNWR5c2ozUlJzVWsyTG1zRVRET2RFRDlvTjFXbHZpUEkzcWpoVWd0U3JEU1hUTHhpcVUwNFJJeHkrTnRLQUdGN0RlT0FVTUtlNHYyTU9uVC9vbzIzalpOdDVnbHNwRGpVUkhuOFlERmErcDVnK29nQUEzNlYvN0VLb3k0MHNQREI2a0YxQnh5Qm5aTHk0VGlEaldmOElLcVAxTEttdndJcFBqUHRCd0pxWWdHSkZTRlNLNUpnVWNSRnRMTW5UNngrY0ljNEx0bGdxeHJqTkIzaUtCUC8vRUFDd1JBQUlDQVFRQ0FnSUJBd1VCQUFBQUFBRUNBQU1SQkJJaE1SQVRJa0VnVVNNeU0zRVVRcUhCMGVILzJnQUlBUUlCQVQ4QTAzOXNUVXI4cy91YWE3L1kzNGpud1RNd0dBL2xmWjYxSmk3ckczR1djSkthL2xtRndnNWgxQ2ZYTTA2RTJGcHBXeW1KcXVGekszL2t5WXB5UFBma21Id3BnUGpQblh0aE1UVEU4QXk4L0NVY0FtTVM1eVlFQThhSTlpYWdBcGd3VmdHVkhBOEdEd1RDZkt3UVRIaFpxcS9aRXJDNGxqQURtVkFGZUk5QkhLelA3aVAvQUM0SE0wcEtzQ1plQ3c0Z1E3dVlnZ21JUEIvSlQ0TStvNGpWakVZbDVTdTFSR2JBeVphUmFjejFoZVZoSlY4VGNTTXhXeTNNQTRnR1BCNGd0NXdmd2JPT0pXeEkrWGZnUllUZ1JMQ3hJSXhHV0VaV0J5cmJZT3BxYzdJT29UTDZ3RzNUM3FxNGoyQWppYWJVN2hob0dEZFRNc1lLTW1QWXJFNE0wMSs0YlRCejRiVXFwSU1YVXFjNGlNSEdSQkMyMk5xUnVqM3B5QVlQNllEeEZWV2VBVFVETlppbGhEa3l4ZllBVEhSY2RTMEtEa1FFZ2tpVVgrcnZxQ3dNTXkvYllNUTBKMklEZzhTblVnRGEwM2hoeEFxRHZ1TWxaTXJaNlIreEVZT01pYXBqbmFKU2dzWWhvK2xxSFFsTm9LNC9VVWdyS0Z6Y1Q0dHM5akZCMUF1UHFmTDlSMzlhaGNaaGNIZ3kvQWxha2dqNmlESENELzMvQU9RVnNlelBXeTlHRlJZTUhneXl2MWdxd2xOSnRnUUFiRWlVakVOQ1k2bm85YmIxaUlvNVdhdkhFUUVzQ29semJKdWY2Z05xL2NvdlZTZDR3ZjNCZ2lLdTFpSmc1ejQxTE12OU15Vk9ZU1cveVRBZ1JSV3NTb0FSY2dZalpNYXNTK3ZmV1FleE5PdUsrT3pFVUlKaVl6NDI3VGtUVXFTNC9VcitDYnpFckxuYzBSUjFpTXYxaVcwRDZtbnRLL0F6VW9RQ3l5azVTTzIwUytnV2pudVBwaXA3bWwrVmdCZ0haRUR0OWllNUlMRlAzTW5PTVF6VGtNZ2h6QzFuMHN6Y1J4RkJ3TjNmalUyWXN4SEdCRllEaUZnTTh3Vzg4R0Z1Y1lpajV4dm5YaVVydFdFWmlhamJuN0gvQUZMTlNqSHFVTUErNkw0S2pHTVFWcjMxQWN5MXdpRW1hS3p0VEdZSU1tRmdxN21uQzg1bk9ZN0JWSk1DRyt3d052WE1CR01tUDg4N2VwV0JqR0lDREZIeXpFUHd6RVlNVGp4akN6Y1RtSnFmV05zMGx5MnBrVGJnN29GT2M1Z1VnbkptUW9tcXYzbmFPcFZkc2JkS2JWc1hJbVBCSVh1RzMzV0JQcVVZeVcvY0I5YmtIb3hsTGZmRU81Z1BXY1FxeFlFSGlCY2RTMXdvMnk3VlorS1RUc2M0OEZXUHhoQldWNk1NbVQyWjZXMHpiMGxlcUI0UGNGNEViVUlvN2xtb2F3N1VqYVZ6RjA3Q1UxbEttYjdHSi9xblRzUWF4bU9BUCtaWTFyOFRUVnV0bTVwVlR0RXRyVjBJbFdxd01QRXRSdWpDd2hjdHdrMVEyTUZCNWpManFWWkhpamw4eS9nSC9NMDlvQzdTZXBZVmRTSnVLdG1HMFduNVFKbFN3K3BwbjJQa3ozSkRhbUl0cWs4eTV0N1ppSGEyWjdsbGQ2d1hxUHVXYWdCRGlWcDdId1pROVFYYTY5UVgwcjBJK3RHTUtKOG5PVEdVZ1lNUWpiSGRpZTVwa0pZaWFoTU9SRkJIVURHRUV3UUZnbTBSUjRBakRFMjdoQW0zbWN3S2NUYVJEbUlXUTVXQU56bWMrRU8weDIzSHlUdDZtb1hJTFRTTHVmQjZucFNEVHFPUkxLRVV5eEFPcHRJTTNtYnpONWlEZEhYYU14YlNJTHA3eGpHSUgzQ0JQdUQrUG1lMUREYWs5eVQycCtvYkFlaE1reXoyYnlNOENXTlljZ25pYVNuWU4rZS9OMlFOd2hkaU00bS9jWVVKbnJnck1RSEhFZkpITUZaSW5yaHJpY0RFRnBQQUVhMG5qRTJ6Wk1FUUF3QXphWTZlMXVHSWxxR3ZzNWkycW93SWI0TlJIdTNLUkJTemdORkF6L2licHVFM2lJTi9BaklVd0NZb1NCRW5xUXg2VjJuRVZTeHdzMDZaWWx2cUd0RDlRVkorcHNUOVJnZ2haUkM0TXJUWVRMOHN3RVhURVQwR0RUbVBRd0VSd0VBaVZ0Wm5FZlQyZ3dhVzJIUzJ5bFdyT0dsdVhJVUNMcDJnb0lnUmhDakVHVU1GWWd5ajVzeEVDR2JUTmthZ21OcHpCVHQ3akQ1R0RMWEFIOEQwWXJuRTBYOUpqWWdJaGFXZjNZVC9BQ0wrS0FGek5Kd3pnZmdTQkNZeG4vL0VBQ29SQUFJQ0FnRURCQUVGQUFNQUFBQUFBQUFCQWhFREVpRVFNVUVFRXlBaVVRVVVNa0p4RlNOaC85b0FDQUVEQVFFL0FKL3laakp4OHI1cnBYeG9SaWp1NlJLa3RSZHliSXgyZkJIMDJSK0QxS2NNU1RKOE14OHMwV2xmTktoZGErUHBGY3JNNlZ1aVBja2syWS8rcFd1NSs0bkxnOXlTN21lcjRJT21SbmFKTDRvWHhmd3dUMEp6dGtVUHZ5UXlrSEZMaEdTQ2xpMnJreXEyeEVYd1BuNExvaS9nK3FFNkxJL1VrN1pGTnVrZWgvVHNrK0dmOFZTcVRPNkl4VFk0MHZnbFo3ZEsrcUVTanIxWWxZNFVyUW4waXJqWTFSK25SakxQRlNQVC9YS05jRUhkb2hCdmtqRzJaY09yNEdxRVk0dHNVWEZHVEhYS0tFUnhOazhiWEJLT3ZSS3lPS2tSeHlvZmZwZFE2ZWxsS09WTkhvOEdmZmFmWTdJamFJTjJSYjZUeE9Rb05HSzQ4aXlOcW1lQ2VLK1VLTFFuSWlwMFNpcFAvd0JKSnAwekNpY3RWd1F6VGZjbkJwbFVTZFkxMDlMQ09LUHV5NytDZnJzMHY3cy9jeTh1eUVYTGtTcnNZMDVFcVJPVVlybDhudS9nV1JlVUpwZjRRZDhvbFBWOGpuYnRqbXhaSDRaSDFVbkQyNU8wTnR1bVliNUd1SGJJWElTaWJRZmd5WVhOZlRrcW1TeWJRUy9CZFJycGdWbEprS1ZrcHVUMlk1TWxYZE1qclZ2dVJrekRQV1JrYTJkamJadGFxaE53NEVKMnFNTDRKdTVhSWxMWGlJLzlJOXJzaGtjVE5GVGp1dS9reFUzeVpGVE1jZGpIa2NDR2JZeThRZEYzM0hGVnd5T09UWTROTm1zYXV4UGt6S3B0QzFYY1VZVnl5WHQrQlQxdWhXekRINjJSNVpvNnNVWCtCeGFGSGl5N1F1SkU1Yk1Ra1k4YmlaRmFycFhGa0pOYzJiTmxVNk1jZG5SNm1QOEFZakZ6WWw5cVJibmFvcE9QSGNoRnlsU0hKWTRvclNSVDdJaitHeVRmZXltaDhJa3ZzU2kwcmZSTk9WRUlyVldQQzVjbWZFNFNZcFd0UzFWVVgrQnR5LzA5UGkxVms4VnFpZU9VSFhSSWlteHc5ckc1ZVNkMGtOYnh0ZUNFcTc5eFVyMjVFNDZ0TmNqbGZjeHhjdVRGaHJtUm1pdEgwZ294K3hHYWwyUDNPcnBEeXh6UjFaTEN4NHJJNFpNaGpqRG1SSFBCRHl4Wmt5WGtqSHd6Mm95N0R3UlNzeHFFVFBLTHh0SWxrc2hOeGRrOENmS0hqa2hSaytEVlEvbVlaYnBzVXZCTko5SmNJeEl5UTVzZ3FZa25HalZ3dWhTOEdXTnhOR1FpeVVQS01TMVJOV2hRWk9EbzBaREcyeHkwaVpGTzdpelRJKzdJNEhmSkZLQ0ZKTWZjeHhpdkJrYU1VL3FOcnljQ2xYUk5YWTNhSzZZMG0rUnRRZkFwcVhCckJEYXN0Q1ZGcVM1Sk5DNlBsRVVrdWlkQ1Y5ekU2NE1ycFdLYkZrZEVNalpDWDVOaEswS0pyMGk3ZEdsbnRpeGpWTXZnZjJGQmlpeFFacXhSS1NNZWxja0ZHdURMUGJncnBEdlFvb2NDTXFWR3h0MGlxRTZOeFNIenlhQ2drYkc1ZG14WlpGNkxsV1Fsc1BHMkxFZTBSeFV6M0ZIZ1Z2dWFsTVVXU2VvbmZZcVE5alprWnV4dWx5Wkc2NEU1RnkvSXJJcVFvc1VDY2xJeGNLeDVvc1dTSjdzU09XSlZ0aXlSajNJNXNiUGZnTFBqTXNsTjJqSFViWTg4VDNrT2NSVGpaSk5yZ2s5RXJOMEtadVJ6SkN6STl5eUxIeGpZbVdKaU5VWnU1R3lpaUg4VCtqNkxvbUoxRkdaOEovQkZDUC9aIiBpZD0iYyIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiLz48L2RlZnM+PC9zdmc+", + "description": "Displays the latest fluid pressure telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pressure', label: 'Pressure', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 15 - 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"horizontal\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"compress\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#2B54CE\"},{\"from\":5,\"to\":10,\"color\":\"#3B911C\"},{\"from\":10,\"to\":15,\"color\":\"#F77410\"},{\"from\":15,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":28,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#2B54CE\"},{\"from\":5,\"to\":10,\"color\":\"#3B911C\"},{\"from\":10,\"to\":15,\"color\":\"#F77410\"},{\"from\":15,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:aG9yaXpvbnRhbF9wcmVzc3VyZV9jYXJkX2JhY2tncm91bmQucG5n:aG9yaXpvbnRhbF9wcmVzc3VyZV9jYXJkX2JhY2tncm91bmQucG5n;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Horizontal pressure card with background\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"bar\",\"decimals\":0,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "fluid pressure", + "liquid pressure", + "pump pressure", + "hydraulic", + "pump", + "compressibility", + "compressive stress", + "pipe", + "pipeline" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_ground_temperature_card.json b/application/src/main/data/json/system/widget_types/horizontal_ground_temperature_card.json new file mode 100644 index 0000000000..8294dc23d3 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_ground_temperature_card.json @@ -0,0 +1,37 @@ +{ + "fqn": "horizontal_ground_temperature_card", + "name": "Horizontal ground temperature card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAqFBMVEUAAADf39/f39/g4ODg4ODf39/////k5OQjTMfg4ODy8vLIyMjI0vFYWFh1j9yQkJDOzs7k6fg+Ys50dHT5+fmsrKxKSkpmZmaRpePb29vy9PvCwsK2trasvOrV1dXn5+efsedaedU9PT28vLy6x+6enp4/Ys66urowV8taeNVMbtI8PDzt7e2dnZ2CgoIhISExV8ovLy/W3fXW3fSRpuODmuBog9nW3vRsY+KEAAAABnRSTlMAIEDfv1C6kOEmAAADiUlEQVR42u3ai1LaQBiGYar232WXzflACIRzOSnVVtv7v7P+2bQijoVYHRqZ7xkIm92M7jtmBmegBQAAAAAAAAAAAAAAAAAAAAAAAAAAAHBKnz48m3FxKT669uVn7hARfXhKXLSuFJ2B6KrVprPQRkjDIKRpENI0CGkahDTNiyGDpXygp+ZuTC8I+anogAOrpwjpyeCHHNGOu9WJVkpR9RAk+MijCR9SThT8sPOkqgX7KhTpRIlybI9UX2fUnfmPo93GRoO99WMhHTnoSod2koKEcJPtjTFzHZIRJt3EIkm/csAmpSRM6C7cpKkRX0MjFomZx8nWDTc63CyM0KGehHz1nOpyJAu+8+jejnyy/GAVjMvXsZ0d1Anp7YVMVJzGrkuJUJMqRMShO6cJr4V6kcSm+EpuLCbCkDs37pyvJjFPXT63ISml23hCNQ1k0PnmyDXvpBo5ZM3W5MseUSBXs05Xyu81Qr7vhRhNLuMgkbwQYrRWv0MS4oobrTnELG4eQ0IKXa2pprXslPc379mRo3J026v2ddu5D2wnMb5paoSM9q4qTDpZcEg8MQuVpHc2RJj0jtdcwzPJn5C7NBELY+YccpMmW7VZ3CSmDClM6FJNs0G58TJkXCbRF+mT1V0u+XzMdayzqhNC+7kkqKKqJ3txIIzanav9RUWvMpArTihDbM6OnawcD1mPOeQfKJfexGe9ahRI/x1CmEP/wRf7i6uOEb01hH9IaUanN+p27dtGL5Bd+nvIrF7ISFq39P+sqw76Yfe8lD494VSLM7k+EjKWlZ+H/9FQ188nFL2Tx3eObjnwZfDszr/1beTDq0P6VYyi64ysKKKo7ZUTbYr4fBgPKcqyaoEn3pZ0L6VTGlBvKZ1uIEfPOwPnYSwD/0hIV1oBPQ0pCi/T2uNRTnmWT6N+7hWF9qbX3jQi8oZqOs1IeXbB01N6g7GsOETfAimXI3qG26Rc+XQkpBfI0mA/xOsr7Q29KmRYZDzmuWHm5dqGtLOMQ3R/qBXn5oreie/3DsweDCHfWcpVh3ZiL8u9fqFzxUm5N83yvuprj+eup0PtURkSlX+R4TQuF4o8p9OqQl4tG9LfFTqjOpoQEkWHFhXV0YiQJkJI0yCkaRDSNAhpmnbr6gw+ZidSV60LcQYftEfiotX6fNkWH93lxTl9qQYAAAAAAAAAAAAAAAAAAAAAAAAAAADgRH4BUVMBmbp0nFsAAAAASUVORK5CYII=", + "description": "Displays the latest ground temperature telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Ground temperature', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Ground temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"thermostat\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#234CC7\"},{\"from\":-20,\"to\":0,\"color\":\"#305AD7\"},{\"from\":0,\"to\":10,\"color\":\"#7191EF\"},{\"from\":10,\"to\":20,\"color\":\"#FFA600\"},{\"from\":20,\"to\":30,\"color\":\"#F36900\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#234CC7\"},{\"from\":-20,\"to\":0,\"color\":\"#305AD7\"},{\"from\":0,\"to\":10,\"color\":\"#7191EF\"},{\"from\":10,\"to\":20,\"color\":\"#FFA600\"},{\"from\":20,\"to\":30,\"color\":\"#F36900\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal temperature card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "soil temperature", + "terrestrial temperature", + "subsurface temperature", + "earth temperature", + "below surface temperature", + "surface temp", + "soil warmth", + "land temperature", + "geothermal reading", + "ground warmth" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_ground_temperature_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_ground_temperature_card_with_background.json new file mode 100644 index 0000000000..004b66759c --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_ground_temperature_card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "horizontal_ground_temperature_card_with_background", + "name": "Horizontal ground temperature card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest ground temperature telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Ground temperature', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Ground temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"thermostat\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#224AC2\"},{\"from\":-20,\"to\":0,\"color\":\"#2B54CE\"},{\"from\":0,\"to\":10,\"color\":\"#6083EC\"},{\"from\":10,\"to\":20,\"color\":\"#F89E0D\"},{\"from\":20,\"to\":30,\"color\":\"#F77410\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#224AC2\"},{\"from\":-20,\"to\":0,\"color\":\"#2B54CE\"},{\"from\":0,\"to\":10,\"color\":\"#6083EC\"},{\"from\":10,\"to\":20,\"color\":\"#F89E0D\"},{\"from\":20,\"to\":30,\"color\":\"#F77410\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal temperature card with background\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "soil temperature", + "terrestrial temperature", + "subsurface temperature", + "earth temperature", + "below surface temperature", + "surface temp", + "soil warmth", + "land temperature", + "geothermal reading", + "ground warmth" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_humidity_card.json b/application/src/main/data/json/system/widget_types/horizontal_humidity_card.json new file mode 100644 index 0000000000..133214dc6e --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_humidity_card.json @@ -0,0 +1,37 @@ +{ + "fqn": "horizontal_humidity_card", + "name": "Horizontal humidity card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAnFBMVEUAAADf39/f39/g4ODg4OD////k5OQjTMfg4ODz8/M+Ys7k6fjOzs51j9zIyMjy9PusrKz5+fm2trbb29vCwsKQkJCRpeMwV8usvOq8vLw9PT3n5+eesefW3fWRpuODm+DV1dVYWFhMbdHI0vF0dHS6x+1aedVmZmZohNnW3fTt7e2CgoJKSkpaeNUxV8ovLy8hISG6x+6enp6dnZ2UGhvnAAAABXRSTlMAIETfv0i0+14AAANBSURBVHja7dp5b9owHMZxxvrkh+OcJOSA0HCUY4WtO97/e5sdygCpg2orbVo9nz+Qa0uVvzKOUEuHiIiIiIiIiIiIiIiIiIiIiIiIiIiI6DV9evc6Vvez8971PndNhxPj3VNOt3Oj8AHEN50ePoQeQ1qGIW3DkLZhSNswpG2eDqlmIY5EDiYljmwABFEQoT2f054MuRVxNQ6mAaI7peDAaV4RAU40mATBVjlQ16oJq6zQsOZVttBH0/psyHHHacn0Z7C5C+4wdYab4Wo1xRCb1XAQDQbD/oPaRHhhx9v4AsDfjyztujP58pyQW2nMwkPI3WD1GGIOAt9NyAMiE+JMMfg5dHANWmQ+zsS1G3LnY1+WaGTfQri+6ZlfCBnLo/rkrXUaoob7kGC4wlUUsgaQi8ZM5kAoEu5CTNrSR+1eOhFX9uand2S7ejicyGC1NSFqW2Ia4SoKWTYhIYrCJoigUYnWbq1FXwgpRPwql3wt4uKUeuIH5WwVriJ0pdK1+Pt97UfhzF5g18eFENeW+/kYM3skl/3q40rGxzdV54cjGI9RufpCyBcRuV3Mxs2Vr/GGQlfWmSu3uw5XKhzovBhnmT4XMhcjtx2GjzdUid/ckfkuKsMR39f5V/MQPhOiHzvWVf7GJ7KWwm7Zvoaz045CdP0NcOszITD7X5jzsL9Cfpxe7FPqHqeUwkvypW6u7A/bVJ++6TL4zUP4XEgm4tb2nriHp1Z/F6Nwn6ARx4h7np3oIQYwikaIk2S3YCZeImku4mdLu4najKwFdhZmrvqmdV6cCwld2SuOQiYTLwkCz4xSpElaxv3Um0wCr7z3yhjwRqosEyivWfCCEv9vYXfyVQNL2cmOt7k0a+G5EOh9SYXjEK+vAm/k7UJGk8SMzdwo8dKgCekliQkJ+qNAmdxU4QWEOsRfaX3ps1ZYzERyf4w/Ii9Jvf4kSJU9Ea9M0r7qB56Zuy9HgQcbEtsTGZWRXZikKV7F+ZDLkhH+bhIkeI42hMTxuUWF52hFSBsxpG0Y0jYMaRuGtE2vc/MB/s0OqJtO12nPH3D/Wex07Vc4es5797n7kb5UQ0RERERERERERERERERERERERERERPRKfgPNbdZin3BhXgAAAABJRU5ErkJggg==", + "description": "Displays the latest humidity telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'humidity', label: 'Humidity', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:water-percent\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#D81838\"},{\"from\":20,\"to\":40,\"color\":\"#FFA600\"},{\"from\":40,\"to\":60,\"color\":\"#5B7EE6\"},{\"from\":60,\"to\":80,\"color\":\"#305AD7\"},{\"from\":80,\"to\":100,\"color\":\"#234CC7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#D81838\"},{\"from\":20,\"to\":40,\"color\":\"#FFA600\"},{\"from\":40,\"to\":60,\"color\":\"#5B7EE6\"},{\"from\":60,\"to\":80,\"color\":\"#305AD7\"},{\"from\":80,\"to\":100,\"color\":\"#234CC7\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal humidity card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "moisture", + "dampness", + "wetness", + "humidness", + "moistness", + "dew", + "water vapor", + "condensation", + "dew point", + "steaminess" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_humidity_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_humidity_card_with_background.json new file mode 100644 index 0000000000..deb4c48ad6 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_humidity_card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "horizontal_humidity_card_with_background", + "name": "Horizontal humidity card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest humidity telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'humidity', label: 'Humidity', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:water-percent\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#DE2343\"},{\"from\":20,\"to\":40,\"color\":\"#F89E0D\"},{\"from\":40,\"to\":60,\"color\":\"#5579E5\"},{\"from\":60,\"to\":80,\"color\":\"#2B54CE\"},{\"from\":80,\"to\":100,\"color\":\"#224AC2\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#DE2343\"},{\"from\":20,\"to\":40,\"color\":\"#F89E0D\"},{\"from\":40,\"to\":60,\"color\":\"#5579E5\"},{\"from\":60,\"to\":80,\"color\":\"#2B54CE\"},{\"from\":80,\"to\":100,\"color\":\"#224AC2\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal humidity card with background\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "moisture", + "dampness", + "wetness", + "humidness", + "moistness", + "dew", + "water vapor", + "condensation", + "dew point", + "steaminess" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_illuminance_card.json b/application/src/main/data/json/system/widget_types/horizontal_illuminance_card.json new file mode 100644 index 0000000000..166d6583ee --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_illuminance_card.json @@ -0,0 +1,36 @@ +{ + "fqn": "horizontal_illuminance_card", + "name": "Horizontal illuminance card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAolBMVEUAAADg4ODf39/g4ODg4ODf39/////g4ODYGDjdNVH1xc2srKzx8fHOzs6QkJDiUmn98fM9PT364ubIyMjz8/P5+fnb29u2trZ0dHTj4+PCwsLnb4K8vLzn5+fwqLTri5vpfY/409pmZmbaJkXV1dXumqjzt8Hu7u7sjJzkYHbU1NRYWFjfQ12dnZ1LS0vgRF0vLy8hISHwqbSCgoLpfZDofY9vEfvyAAAABnRSTlMA7yC/r7BI7FImAAADHklEQVR42u3a53LaUBCGYcDJsmqoC9QLiGJip9//rWUlOWAcTMyECTLzPeD10dEf3jEyzMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM42uhuO37sPI+kYrujd04ejwQedbsDqbjCmmzC8lZAxQnoGIX2DkL5BSN8cD/mp0HtzPCRmem4aapqr0Z4+ob55GWLFlswsJlGX1Jm5k4k20XUKKWwnae2CdF3uckhhd3BRVp1/c6ih1vmSnqisvCkk488WddZsPg+RG9n0ZWZXVdUt3IdtpWmPlU3fq2042douXdCahVLuVs55IZTnZK03n+OSStP6M+QrTbTQpuY208JJNdNmZOs2LcJH7fuMLsdhVsuclWalqGXB5nkhwlG4sSZxOmSbPOxD7Kkb0uVkvJE5Z4cKLoisOVu7EMtp1o7MkyEKd9QXIYvH6sthyMN224bQpHoMH+xKu2iI2YZYZPJSVp+43IU4PHdkKM7JkIyfmPSS/uqG3o0LshSunYJjkhCV2rkLoZxjKavp1ZDaLKjg30g1TbqaksVn62iIVMby+/WQXELifcjyiiHyWDe5wuujITKZHaIznlpXU3PcXiPq0RA58Yn+EmIp3PlG17ThTGYsU34OL/anl5b6jf9+c9rx6Ahv9XLDowuK+YdMhZdU86Z9MaF9iMPzJc+tkyFiyWJDe9M2Ru6rgFq+T/7YaDbG5MtxqqXkB0F3QjYukKQyx7n5+8rOFc6fhWzkqOD4byEli/gwZLEwAtc1ZBVRFESJP42MxcI1kpWR+ERG6iVJQJ7RnjDchP7ZUmFm03l6iswL2odkbZ9cPydDxDLLMucw5P5+6rlGanQh6X0ga9lLAyNy25BxEEiIm6SuJ7mRR//OcqyD1flv41XTLOgZzQgiY7pwI0+SIiMJoqk3dQ3ZW8kDN6gJ8Zu/SJpozYlFFNH/cjrkk1LQCUFKr7t3A7qCJuRsvn/qpEdv0YuQPkJI3yCkbxDSNzcUMqSbMB7c3cTH0/rHwWh4AyX6sPnqw4fxeze8Gw0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA4Fy/AL5F9/papj6AAAAAAElFTkSuQmCC", + "description": "Displays the latest illuminance telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'illuminance', label: 'Illuminance', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Illuminance\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:lightbulb-on\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":1,\"to\":5,\"color\":\"#FFA600\"},{\"from\":5,\"to\":20,\"color\":\"#F36900\"},{\"from\":20,\"to\":50,\"color\":\"#F04022\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":1,\"to\":5,\"color\":\"#FFA600\"},{\"from\":5,\"to\":20,\"color\":\"#F36900\"},{\"from\":20,\"to\":50,\"color\":\"#F04022\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal illuminance card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"lx\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "brightness", + "luminance", + "luminosity", + "light", + "light level", + "light intensity", + "lux", + "candela", + "foot-candle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_illuminance_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_illuminance_card_with_background.json new file mode 100644 index 0000000000..c896876dab --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_illuminance_card_with_background.json @@ -0,0 +1,36 @@ +{ + "fqn": "horizontal_illuminance_card_with_background", + "name": "Horizontal illuminance card with background", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC/VBMVEUAAAAWHB8VGB0ZHSAWHB4fISIgICAfICHIyckXHB/OzMrMy8rSzsvRzcveI0PJycnVz8vm2tDQzMrZ0czOzMvUzsvk2c8WGx4ZHR8YHB/X0MsbHSDOy8ri187k2c7LysrIycr///7e1Mzb0cxDR0no3NHa083d08zY0czW0Mzg1s7KysodHyDd1M0hIiJER0kfICEmISArJiM0KSIvKCTY0s09LSTb0802LCXi2NAzKiQyJyIqJCD//frdOFQ5KyM6LST45dXg1c1AMSctJSEnJSP87uAvJyJGMiY7LyZONidxcXI3KSL25NPd1c5XUE1ISUpENClTOChDMCX/+vT559YtJyMjIR/q3dH+9ez15tjz4dG9vb2MXDJJNypALiT+8+j769vp3tXg08zFxcVhVU5dU03RoahnWE9xSSptRypmQyr86djl29OHh4dwYFOYZTeUYTWtbDKjo6P56drz49VRTUuQYDV9UCx3Syr+8eX97N3v4tbBv729trKenp5bUU0wMDDs4tpqW1GeajuOXjT78efIwMCQkJDVdobcTmX/+O+lnZmZmZnYTGTKfTh4Ty1gQClLMyb+8eGtrazWo6moqKjwuodaPSnv39Dt3tDMtbmys7OtpqGlfltbW1tSUVF1XlBNS0veLUvfjUWGWTGkZy/UuLr91q3fj5eMionWYXX4sG/aiEG3cjWCVC7UysK2trbPipfbQlyObVPSgz07PD2hbTzAdjacZDJrRioiJylYPCj27eX06uHx5tzt39LMxsPQwcO2rafde4jUa37dZHbuqGhfXVxtWlBOOiv/6M3az8fYxcT+3bf+y5mVlZXUgY/bb3/vsHZnaGnmnl72o1nRkVaGaVN5YFG6fkencT7ZrK/9z6KelpHYnmjGjViVclXnlEoyMTDevrzMqa/ZmqHQlqD8xpGAfn37uXrZV23KmGu8j2a3i2Kcd1d9Y1GudUIxMTDjzcXgtbT3yZvlrHffW2+xh2Gge1rTzMfRs5beqHfLhUfRipd2EeMNAAAACHRSTlMA7yC/sO8gr7sVXXAAABGASURBVHja7MGBAAAAAICg/akXqQIAAAAAAAAAmD06VnEbBgM43qS36Aki41cwwbGMEHLABp9RBF5CIJ563kuHxsu9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", + "description": "Displays the latest illuminance telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'illuminance', label: 'Illuminance', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Illuminance\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:lightbulb-on\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":1,\"to\":5,\"color\":\"#F89E0D\"},{\"from\":5,\"to\":20,\"color\":\"#F77410\"},{\"from\":20,\"to\":50,\"color\":\"#F04022\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":1,\"to\":5,\"color\":\"#F89E0D\"},{\"from\":5,\"to\":20,\"color\":\"#F77410\"},{\"from\":20,\"to\":50,\"color\":\"#F04022\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal illuminance card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"lx\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "brightness", + "luminance", + "luminosity", + "light", + "light level", + "light intensity", + "lux", + "candela", + "foot-candle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_individual_allergy_index__iai__card.json b/application/src/main/data/json/system/widget_types/horizontal_individual_allergy_index__iai__card.json new file mode 100644 index 0000000000..cd75058597 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_individual_allergy_index__iai__card.json @@ -0,0 +1,32 @@ +{ + "fqn": "horizontal_individual_allergy_index_iai_card", + "name": "Horizontal individual allergy index (IAI) card", + "deprecated": false, + "image": "tb-image:SUFJLXZhbHVlLWNhcmQtaG9yaXpvbnRhbC5zdmc=:SUFJLXZhbHVlLWNhcmQtaG9yaXpvbnRhbC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjU2IiB5PSI1MiIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjU1IiB4PSIuNSIgeT0iNTIuNSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjEyIiByeD0iMy41Ii8+PGcgZmlsdGVyPSJ1cmwoI2EpIj48cmVjdCB3aWR0aD0iMTg0IiBoZWlnaHQ9IjQwIiB4PSI4IiB5PSI2MCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuNzYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iIzgwQzQyQyIgZD0iTTMwLjQgODAuNzVhMi41IDIuNSAwIDAgMC0yLjUtMi41Yy0uNTMgMC0xLjAyLjE2LTEuNC40NHYtLjE5YTIuNSAyLjUgMCAwIDAtNSAwdi4xOWMtLjM4LS4yOC0uODctLjQ0LTEuNC0uNDRhMi41IDIuNSAwIDAgMC0yLjUgMi41YzAgLjk5LjU5IDEuODUgMS40MyAyLjI1YTIuNDkxIDIuNDkxIDAgMCAwLS42OTggNC4wMTggMi41IDIuNSAwIDAgMCAxLjc2OC43MzJjLjUzIDAgMS4wMi0uMTcgMS40LS40NHYuMTlhMi41IDIuNSAwIDAgMCA1IDB2LS4xOWMuMzguMjcuODcuNDQgMS40LjQ0YTIuNSAyLjUgMCAwIDAgMi41LTIuNWMwLTEtLjU5LTEuODUtMS40My0yLjI1Ljg0LS40IDEuNDMtMS4yNiAxLjQzLTIuMjVaTTI0IDg1LjVhMi41IDIuNSAwIDEgMSAwLTUgMi41IDIuNSAwIDAgMSAwIDVaTTIzIDc0YzAtLjU1LjQ1LTEgMS0xczEgLjQ1IDEgMS0uNDUgMS0xIDEtMS0uNDUtMS0xWm0tNCAyYzAtLjU1LjQ1LTEgMS0xczEgLjQ1IDEgMS0uNDUgMS0xIDEtMS0uNDUtMS0xWm0tMi0yYy0uNTUgMC0xLS40NS0xLTFzLjQ1LTEgMS0xIDEgLjQ1IDEgMS0uNDUgMS0xIDFabTMtM2MwLS41NS40NS0xIDEtMXMxIC40NSAxIDEtLjQ1IDEtMSAxLTEtLjQ1LTEtMVptNiAwYzAtLjU1LjQ1LTEgMS0xczEgLjQ1IDEgMS0uNDUgMS0xIDEtMS0uNDUtMS0xWm02IDJjMCAuNTUtLjQ1IDEtMSAxcy0xLS40NS0xLTEgLjQ1LTEgMS0xIDEgLjQ1IDEgMVptLTQgMmMuNTUgMCAxIC40NSAxIDFzLS40NSAxLTEgMS0xLS40NS0xLTEgLjQ1LTEgMS0xWiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjg3IiBkPSJNNDEuODQ0IDc1LjA0N1Y4NWgtMS43MTZ2LTkuOTUzaDEuNzE2Wm02LjM4MiAxLjMyNkw0NS4yNTIgODVoLTEuNzk4bDMuNzQ2LTkuOTUzaDEuMTQ5bC0uMTIzIDEuMzI2Wk01MC43MTQgODVsLTIuOTgtOC42MjctLjEzLTEuMzI2aDEuMTU1TDUyLjUxOSA4NWgtMS44MDVabS0uMTQ0LTMuNjkxdjEuMzZoLTUuNDE0di0xLjM2aDUuNDE0Wm01LjE0NS02LjI2MlY4NWgtMS43MTZ2LTkuOTUzaDEuNzE2WiIvPjxwYXRoIGZpbGw9IiM4MEM0MkMiIGQ9Im0xNzUuNTQ1IDgwLjM4OS0xLjg3NS0uNDYuNzcxLTcuMTQ4aDcuNjY2djEuOTkyaC01LjcxMmwtLjM5MSAzLjQ4N2MuMjIxLS4xMy41MjctLjI1Ny45MTgtLjM4MWE0LjIyIDQuMjIgMCAwIDEgMS4zMzgtLjE5NWMuNjcgMCAxLjI2OS4xMSAxLjc5Ny4zMzIuNTMzLjIxNS45ODYuNTMgMS4zNTcuOTQ3LjM3MS40MS42NTQuOTExLjg1IDEuNTA0LjE5NS41ODYuMjkzIDEuMjQ2LjI5MyAxLjk4MmE1LjY5IDUuNjkgMCAwIDEtLjI5MyAxLjg0NiA0LjMwNiA0LjMwNiAwIDAgMS0uODYgMS41MTQgMy45NSAzLjk1IDAgMCAxLTEuNDU1IDEuMDI1Yy0uNTc5LjI0LTEuMjY2LjM2MS0yLjA2LjM2MWE1Ljc0NCA1Ljc0NCAwIDAgMS0xLjcwOS0uMjU0IDQuNjQyIDQuNjQyIDAgMCAxLTEuNDU1LS43NzEgMy45MzggMy45MzggMCAwIDEtMS4wMzYtMS4yN2MtLjI2LS41MDctLjQxLTEuMDktLjQ0OS0xLjc0OGgyLjMwNWMuMDU5LjQ2My4xODUuODU2LjM4MSAxLjE4Mi4yMDIuMzE5LjQ2OS41NjMuODAxLjczMi4zMzIuMTcuNzE2LjI1NCAxLjE1Mi4yNTQuMzk3IDAgLjczOS0uMDY4IDEuMDI1LS4yMDUuMjg3LS4xNDMuNTI0LS4zNDUuNzEzLS42MDVhMi43IDIuNyAwIDAgMCAuNDMtLjkzOCA0LjUxIDQuNTEgMCAwIDAgLjE0Ni0xLjE5MWMwLS40MTctLjA1NS0uNzk4LS4xNjYtMS4xNDNhMi40NzggMi40NzggMCAwIDAtLjQ3OC0uODk4IDIuMDUzIDIuMDUzIDAgMCAwLS43OTEtLjU4NiAyLjY3MiAyLjY3MiAwIDAgMC0xLjEwNC0uMjE1Yy0uNTYgMC0uOTg5LjA4MS0xLjI4OS4yNDRhNC4wNDMgNC4wNDMgMCAwIDAtLjgyLjYwNloiLz48L2c+PGRlZnM+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjE5NiIgaGVpZ2h0PSI1MiIgeD0iMiIgeT0iNTQiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjMiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA0OV8zMjcxIi8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMDQ5XzMyNzEiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48L2RlZnM+PC9zdmc+", + "description": "Indicates the concentration of airborne allergens, including pollen and mold spores, which can trigger allergic reactions in sensitive individuals.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'IAI_level', label: 'IAI', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"IAI\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 12) {\\n\\tvalue = 12;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:flower-pollen\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#3FA71A\"},{\"from\":2,\"to\":6,\"color\":\"#80C32C\"},{\"from\":6,\"to\":9,\"color\":\"#F36900\"},{\"from\":9,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#3FA71A\"},{\"from\":2,\"to\":6,\"color\":\"#80C32C\"},{\"from\":6,\"to\":9,\"color\":\"#F36900\"},{\"from\":9,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"IAI\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "air", + "aqi", + "pollution", + "emission", + "smog" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_individual_allergy_index__iai__card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_individual_allergy_index__iai__card_with_background.json new file mode 100644 index 0000000000..777c041100 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_individual_allergy_index__iai__card_with_background.json @@ -0,0 +1,32 @@ +{ + "fqn": "horizontal_individual_allergy_index_iai_card_with_background", + "name": "Horizontal individual allergy index (IAI) card with background", + "deprecated": false, + "image": "tb-image:SUFJLXZhbHVlLWNhcmQtaG9yaXpvbnRhbC13aXRoLWJhY2tncm91bmQuc3Zn:SUFJLXZhbHVlLWNhcmQtaG9yaXpvbnRhbC13aXRoLWJhY2tncm91bmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSI1NiIgeT0iNTIiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjQ0IiB4PSI2IiB5PSI1OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuNzYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iIzdDQzMyMiIgZD0iTTI4LjQgODAuNzVhMi41IDIuNSAwIDAgMC0yLjUtMi41Yy0uNTMgMC0xLjAyLjE2LTEuNC40NHYtLjE5YTIuNSAyLjUgMCAwIDAtNSAwdi4xOWMtLjM4LS4yOC0uODctLjQ0LTEuNC0uNDRhMi41IDIuNSAwIDAgMC0yLjUgMi41YzAgLjk5LjU5IDEuODUgMS40MyAyLjI1YTIuNDkxIDIuNDkxIDAgMCAwLS42OTggNC4wMTggMi41IDIuNSAwIDAgMCAxLjc2OC43MzJjLjUzIDAgMS4wMi0uMTcgMS40LS40NHYuMTlhMi41IDIuNSAwIDAgMCA1IDB2LS4xOWMuMzguMjcuODcuNDQgMS40LjQ0YTIuNSAyLjUgMCAwIDAgMi41LTIuNWMwLTEtLjU5LTEuODUtMS40My0yLjI1Ljg0LS40IDEuNDMtMS4yNiAxLjQzLTIuMjVaTTIyIDg1LjVhMi41IDIuNSAwIDEgMSAwLTUgMi41IDIuNSAwIDAgMSAwIDVaTTIxIDc0YzAtLjU1LjQ1LTEgMS0xczEgLjQ1IDEgMS0uNDUgMS0xIDEtMS0uNDUtMS0xWm0tNCAyYzAtLjU1LjQ1LTEgMS0xczEgLjQ1IDEgMS0uNDUgMS0xIDEtMS0uNDUtMS0xWm0tMi0yYy0uNTUgMC0xLS40NS0xLTFzLjQ1LTEgMS0xIDEgLjQ1IDEgMS0uNDUgMS0xIDFabTMtM2MwLS41NS40NS0xIDEtMXMxIC40NSAxIDEtLjQ1IDEtMSAxLTEtLjQ1LTEtMVptNiAwYzAtLjU1LjQ1LTEgMS0xczEgLjQ1IDEgMS0uNDUgMS0xIDEtMS0uNDUtMS0xWm02IDJjMCAuNTUtLjQ1IDEtMSAxcy0xLS40NS0xLTEgLjQ1LTEgMS0xIDEgLjQ1IDEgMVptLTQgMmMuNTUgMCAxIC40NSAxIDFzLS40NSAxLTEgMS0xLS40NS0xLTEgLjQ1LTEgMS0xWiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjg3IiBkPSJNNDAuODQ0IDc1LjA0N1Y4NWgtMS43MTZ2LTkuOTUzaDEuNzE2Wm02LjM4MiAxLjMyNkw0NC4yNTIgODVoLTEuNzk4bDMuNzQ2LTkuOTUzaDEuMTQ5bC0uMTIzIDEuMzI2Wk00OS43MTQgODVsLTIuOTgtOC42MjctLjEzLTEuMzI2aDEuMTU1TDUxLjUxOSA4NWgtMS44MDVabS0uMTQ0LTMuNjkxdjEuMzZoLTUuNDE0di0xLjM2aDUuNDE0Wm01LjE0NS02LjI2MlY4NWgtMS43MTZ2LTkuOTUzaDEuNzE2WiIvPjxwYXRoIGZpbGw9IiM3Q0MzMjIiIGQ9Im0xNzcuNTQ1IDgwLjM4OS0xLjg3NS0uNDYuNzcxLTcuMTQ4aDcuNjY2djEuOTkyaC01LjcxMmwtLjM5MSAzLjQ4N2MuMjIxLS4xMy41MjctLjI1Ny45MTgtLjM4MWE0LjIyIDQuMjIgMCAwIDEgMS4zMzgtLjE5NWMuNjcgMCAxLjI2OS4xMSAxLjc5Ny4zMzIuNTMzLjIxNS45ODYuNTMgMS4zNTcuOTQ3LjM3MS40MS42NTQuOTExLjg1IDEuNTA0LjE5NS41ODYuMjkzIDEuMjQ2LjI5MyAxLjk4MmE1LjY5IDUuNjkgMCAwIDEtLjI5MyAxLjg0NiA0LjMwNiA0LjMwNiAwIDAgMS0uODYgMS41MTQgMy45NSAzLjk1IDAgMCAxLTEuNDU1IDEuMDI1Yy0uNTc5LjI0LTEuMjY2LjM2MS0yLjA2LjM2MWE1Ljc0NCA1Ljc0NCAwIDAgMS0xLjcwOS0uMjU0IDQuNjQyIDQuNjQyIDAgMCAxLTEuNDU1LS43NzEgMy45MzggMy45MzggMCAwIDEtMS4wMzYtMS4yN2MtLjI2LS41MDctLjQxLTEuMDktLjQ0OS0xLjc0OGgyLjMwNWMuMDU5LjQ2My4xODUuODU2LjM4MSAxLjE4Mi4yMDIuMzE5LjQ2OS41NjMuODAxLjczMi4zMzIuMTcuNzE2LjI1NCAxLjE1Mi4yNTQuMzk3IDAgLjczOS0uMDY4IDEuMDI1LS4yMDUuMjg3LS4xNDMuNTI0LS4zNDUuNzEzLS42MDVhMi43IDIuNyAwIDAgMCAuNDMtLjkzOCA0LjUxIDQuNTEgMCAwIDAgLjE0Ni0xLjE5MWMwLS40MTctLjA1NS0uNzk4LS4xNjYtMS4xNDNhMi40NzggMi40NzggMCAwIDAtLjQ3OC0uODk4IDIuMDUzIDIuMDUzIDAgMCAwLS43OTEtLjU4NiAyLjY3MiAyLjY3MiAwIDAgMC0xLjEwNC0uMjE1Yy0uNTYgMC0uOTg5LjA4MS0xLjI4OS4yNDRhNC4wNDMgNC4wNDMgMCAwIDAtLjgyLjYwNloiLz48L2c+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHBhdHRlcm5Db250ZW50VW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48dXNlIHhsaW5rOmhyZWY9IiNjIiB0cmFuc2Zvcm09Im1hdHJpeCguMDA1MDMgMCAwIC4wMTc5NSAwIC0uOTI3KSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjU2IiB4PSIwIiB5PSI1MiIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMyIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMTA0XzM0NTAiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzExMDRfMzQ1MCIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFJQkFRRUJBUUlCQVFFQ0FnSUNBZ1FEQWdJQ0FnVUVCQU1FQmdVR0JnWUZCZ1lHQndrSUJnY0pCd1lHQ0FzSUNRb0tDZ29LQmdnTERBc0tEQWtLQ2dyLzJ3QkRBUUlDQWdJQ0FnVURBd1VLQndZSENnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ3Ivd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhRQUFBZ01CQVFFQkFRQUFBQUFBQUFBQUJBWURCUWNDQVFnQUNmL0VBQnNCQUFNQkFRRUJBUUFBQUFBQUFBQUFBQUVDQXdBRUJRY0cvOW9BREFNQkFBSVFBeEFBQUFENG4vUGVQQTI0TzViUmJlN1RiRUtzNjRsRktWU1VIZTNPTVJNRE1PMkVkZzNJVHRDVDFzY284ekR2cjdpakMralljNXVpc3E0a0taTUZLaGFLY2lteVgwYm5HRm1IcGo2R2xwUmZvNGRHRko2WXpEREZ4SExWNS9QeTJoWWZnSmhpQUNsUXhFTm1EVUZqSlRVSDdHQW1KdGQxRGIzRTNvT0s4NysxMUNYRFp4ODFVYVFVZHk4bms2eWp2b21NMnhLVE1SUzBCMDhhb3ZrU3gyNk9xczlqYmFaNmlGOXphZjZUZk5ubkd5b3VkY3pCb1ZOYkxwdDVSbkw4OXh5NVE2WVNwbHdLbXBjOGFxMmVCNkJnWlcvcFh1cHFpN3Q2VE92b21ydHRkOUtpNnVTT2RjeDVCUXkyWWMxQnk4RHRiL2orYjNhdnJncm5uQXllSlhHNWI3S3lWUXR5NGQydGVweEhmVXZYZTU2MnRxNmdLdU5MVitDQkZNNjUwb3A1aDZObnNzai9BREtjYjZ2bzFmZlJQaTBFZ05vUm8zUkowNnphZHJ6ZEJhdXQyVHByb0hWWjN0VlgwMHZTYUhjN05hQ3F5WlVEeVVNbVV3bGlQeU5CcUN2czhOQVZWYmdpRE5kMTJtZDZXdmMrMCt4VzJyU0lzNzA2ZEsxMStrb20xNERSRlV0cFg4N24wVUtrNlBMa1FuOGQvSGVRV2hFcWU2STY5YzNIcEZmcVdIUVd2dExuNkZOQTc3YXpib3RSVzFuWmR5MTFJM0paek5ZRnlEWG5WNXV0VjUzeDJKenFtbi9OMzVEd0ROb0tFMm8xTDBPZlNQVmNtMUdicWUvNldacTJlSzlEeTFnaXFiTEwwemFYbmNQamczS1BuTk9abkZxTzBTc1RDeXdWL25MOGw0WUgzam0xb21uZWxMU3ZXZHk3bk5velRXbHE5dFNicElMS2VuWEZYNlBibjF1VE54emp1V3RLMUR6S1N4TjVzZWY4eHJLUy9tVjhxNWUyQmRBN2RDYS83STBEMFhlK21pMEIxbWZxVzJ1blpuS3pURlJwcHBJZGVmUHpLTzVUR1p6UFF6V1dOV1F6TnFlaXpsVFdsOFMvTitheGNGUHRGN1YxejFpOTlyUDlyZmd3UXoyZWxRUUtXam4vQURCaXV0d2J0NTYrdW5rSVZpTlJDbk40bzk2elpDOE14RXFpVmZuWDhWeFh0MG5zMjIra3V3K2hSb2Exd1g2MjlKVmtVVkhzTStXODhuYm9LeVUyYmVvejlFV1JIVWFUUVpLamlhM2xXNnpSbGVkTFlwK1k4OXo2MHV1bmJyM25YK2pvczUxcjFCTFpwcnFkU3NUWGxYV29PMDlLVUxUdUpkamF6aHRPdXdWRUt3ME1QUmJOMkZGc2owMW53dVZ3NjU2NTNwdFBUWFRhV3FJTlN5RHpXalBYVTRFWU1RQlVyQ3NtTXBDM0w2ZkwwNk9zVldHWHlNK2VPYWhYSnFxdFo0cW1vUEdqcFhmSFl1eE5HczJrMWRFZ25VenVoN0svWUJwcWNrQ3kxY2FKMEtzL1FzYjVwNC9WbFpsV0xDMG5sbFlXSzFpN09YRXp6cVNsTDhsZEg3WmZRdmZGK3BTMURKMFpNVDRDV3Fablc3V2pSMDZTS2lyQ1hlbjZHUjZMWEYyMVBKMytZMXJ6UXhnNlJ5M3Q4d1dzVmhXVy9QanFIYW4wQjNMb2xUbmZMbFdKVnVkN3BrOEthdjBiWDYyeEhrenBSMXpFc3U4anNyWVd5VGc3UWc2NFNzQ2E1ZmhTVzVyT29zZWlLUHh3MGZ0bnAxeElyVTY1WGhxK2J6cTF0dHR2V3RGRjFoR2I2dTV2YWpqWVFQMUd5YkpsdEdWak5WYWNaQlJTcXRDcWFQOEEvOFFBSlJBQUFnSUNBZ01CQUFNQkFRRUFBQUFBQXdRQ0JRQUJCaElSRXhRaEJ4VWlKQllqLzlvQUNBRUJBQUVGQWR4emNjNjV1T2RNMFB6a1JaQUdSQnZJTFpCWElLZm55NThtYlV5U3ViVjNrd2VNbUxKRHpwa0lmc1llTTNrczNEZWJoblRPbWFGa0E0TUdRQmtGOEd0ZzFNaXJyV3ZtMTQrYkpLNUpiRmFXUjQydFVST2MxOTd5U3U4a3R2V0RYL2ZYa28vazQ1NnM5T2VueG1nNUVPUkJrQTRNR0RCZ2dZRlhQbC96cGY4QUpoMXJlbHRrbHVrbjYrSzBrakI1aHc3MzFLVlJvc1M4Zm5DVGxYdUdEUi95ZGJwcVEvMlFNMERQbXdpL2pJaXlJc0dIQmh5QWNHREJMNzF0Tlh0bTYveGtLLzhBSDBKQzNTSWJJVlNuK21ISHc2VnR5MUdtVm1lTnRJWERWSktJYml0NjVHa2tHdmFXL3dBL052MmJXMzVnRFBSaEFaNnZHNGp3WXNnUEFod0svYlVWcFF5ckhvbWxVSXloT3E2Q0tnTm9WUnhuN2tKS1JWcnFPbkpOd0F4Zk0zeGtMb3JTbjlVVytPN2FiNVdwL1hxdEo3M0dGRVl1ZjFQcm5BR2JEK0VIa2g1Q09RamtkZU1EckVsdHkySktmVlJMMXlTN2ExTWZtS3RQT2JQSGF6NUMzTktYUmY2cU5lQmwrWUMxK3BTRGFWUW1kRW9DS3o1SHgrZGdjUEF2TWljWUFGRzU0enRVVUIvaEkvazRaT0dRSGtCYjhSakxLOEhZcU5acnFsVGU4ZXVQRUlLcXJpd0xPazlrYXpqMmpxVW5IWisxZW5CUExWYmNaRG9ac25Da1JNUUJBUG45UXNjTGZFVjU3WjQ4TVE3TkdTMGIyUHUxcldHeVdkUE93SjlzaFhkbzdyNTZsVm82MFJOUGN3VXBYNnR5cUNuUEk4WkhxVVVHTjc0dlVtOWtrUmpDV2NnWkJTRGU0VXdZSnQxL3QwOXZhZUpPejljN0JmZU5xZTdkaFQrNkYxeENQYlVzTEx6bTk0Q1BlZFhYKzJDMUYyZ3hVRUdjTlRPT1ZpMHh5V1Y5dVZxWHVVcUsvd0Q0OVVnWTZCSlpFYi9KeGFrV3hsaUE1eEFsT0pnMk5YQmlOalVqR3hZa21ESlc1UU5WZHBBc1pzUkpsdU9CczluNUluNTIvVTkvN290eDFCWHJ1RVVJRm1sWEQ4THFLaXhWSlV1cVd1MFBVT29Jc3UrWVBXWkR5a0lwRFVvakJucTc2UHUzOEFhcWVaZjNLSXVidE1zUXRBRUFYMU9NakNkT1pXdkFtekVuR1J2ejI1cVc5elJnU1JLU00vRlFJeHliUmxIRmU0NUNFVFkxQnpES25FVFVHbWR3MjliRDN1dnJubUdVT0xRRUhsZGlzR0p1WCt3Q3pMY2JDdDNJd0VraDhoYkkyU01xdy95YkZaUTZRMEtXN1VBWEl5bHZlUWhnUmQ5MHdCNjF4YXJDek5kUDVYaHF4T0Y0RTRGVTJYZTZMN3luZ1FLaWRweU9HcEc1Q0h1bnpJSWlwY3BOYkJzUFlNODBGcTl4Vk9HM0FiN1pYRlROUGtWYk9DMUlzYVl2akNDRnhiK2NGeXFZR0Z4K3pJaS8rZGFEWkRxMS93RHljRlhLTlZjTXpsVVNuODI2T1pEcTFBU3lSUFYwd2J1OHNXMmJUY1NhTFp6NzE3VEFybWd0RkpIYkFURjZiYnFxdEl6TWp3Z2dCSld5RmxEelNWUmo1S1pxdHRyRHhsdGExUk5xckIzdXBIcWUyRnVrS05iY1dLYXZqTlBqbFlJUWxGaHdsRWdmWWhZaFFmdTY4bEpNOGlQU2lhUUpPSmphcjdWU0tqd3FTVHJsZldFZzVUSSt4YXNJQ3NtOXlpdGlxMjhFMlhCaDU3QUtyMlBNdDFyL0FDYmtMa0JvV29yWmk1ZFNnT3ROMEp2d2NWT2x2MjBTR3BDcmc2QW9DTUk2a3hEUlRwYWR4SnFldzJOSENEWlI2SnM4NW9udGtJMmhlUGdrampOellKTWc1REl1N1NQU3V0NXloVUJZN0g1UlpSZzFjV0t5RmRkWHlaYkdpSHRsVjhYL0FKcVg4cUUrZS9ybWZPSm5uTGZHOVI4VU1vYTJHV3RMdFdlaGFKWkQzbkR5eWRLOVA0WnN0QzNvZXZvbTNVL1hLdC9qNXlCYmNpeXBlV0syWnlWVzF3ek82TTFFK1haYU9ndHRFTmNrOS9KZjVCc3U4NHBlOTlSYlNpdk5MMVpxNC9rQmoyMnRRejEzVUdIUGRMcndLbmIzc3pObDhxYkhKQkdKWEx1dEhvVG9jVXI1M2tMa3gzSEJ4RmFXUTIxZVR2NkkzL0pyYXBXSHBWOXBOZUZpdlZmY29DdnZONnJHclRRd2hsRkN4S2ZyYVhQdHNiR3ZDdlBYT2J3VkZ4NjRJU2VYRTlXcUNwWlIxeGNwQ2xxOVNrdngxUTBKM1RVdGpVNnhLTGw2dE1OL2xHN2hhaDVNNnMxV1hpbTVXVXB2Z3N4eFdCQVZZMVBheDFUcEM4amx1TzhYQ0VPMmJLZjJ0TVIzRnA2TzV0dTYwZHUva0tQTmVTYnNwUmhvdjhmMUVwYXdPNHkxdzJBeFpSbmhISVhNUWk1QmRtSVZxL2tHWkRsZE9qOTBnZ3VyaFhPSVFuWk1jNFRsVjFsZHk1ZEkwcTFRK2FGTlhFTE1JNXhzVldCM2l2d2dQY0VFWnprR2h6YzVMcVdtTG1POTZ0Q1RhdWRHS1Y5dUNNS2hoVjJhZ3BhMVF6OU1VK1F4Q00vSUR6eDUvYmczOXNvNHhaT0EwanlRdW9WYkJIeVZmSXRjRFVsekcvZEZhY3JyYkp2aDkwZXR3cjNtWjN6ajNLK04yWTVFZGlESlpTbmFiSnZUSk5qMDQxTEZKUzZEMkV6ZDg5V2xjQTJrakgvL3hBQThFQUFCQXdJREJRUUlCQVVGQVFBQUFBQUJBQUlERVNFRUVqRVRJakpCVVFVUVlYRVVJQ05DZ1pHaHNWSml3ZkF6UTNMaDhSVWxNRlBSZ3YvYUFBZ0JBUUFHUHdIMWRQVTA3dE8vVHUwOVRUL2pwVHVzdEZvdE82bmRZS3pGdk5zVnAveWFLdzc5RmNMUmFkMlZyZFZ3ZlJYYndtaWUrT1BlWnZOc3J0ODBkeXk0VndvMlZGWWV2cDNVcDNhTFJjS3EwTE01bkNzeFp1alVwK0ZvTjhWVG8zc3NXNktTSXhISVhtaFdZc1Rzb3Q1TGJ2SEpHeU5sU2kwN3RPK3ZmUlhIeFI2aFVlRTU3QWlLWHFnN0RVenNLMkFieXVqMmlXY1IzZkpYQXpVVzJETGhFWkxKc1RSdTEza01Ld2FxdEZ0R3hXUE5GcnU2cVBxMjd0NXRVNXpLZzlGbDFzaXc4d21sd3NkUEZOWlRqUWJHMm9lZ01sd3dCQ01pN3lnUjAwUkxXWFRubUw0MFhCdzIwVko3dFRvSW90NFhiWmJjMHJuOVhUdkZRZzZud1dkZ3FlaU1zUjVhVVJxeXZSWmpGUXByWTJWYzByUE1QbXFTdHJUU3FjNU94TThkaG9oSktOZVhSVldWN0FpZGxUb1VXT2JkWjlseXVxRTI2ZXBSVm9xVVdVTlZIQ2l5QUIxT1lWSFJrc1hwd2JUTlFGcWRqb0dpMXgwWHRHK0pXZkpaSEtLRkhtTkZYVmJmWlZ5V2FPdmlzenpTamExVllXNm1pckpvaFJ3cXRwR3o1ckpsMVIzYkErcm91QlV5NnJNMWhXWnpMb1AyWnFFR3hzdm1UWTN0MTFSYzVvb3QyaXlrODZKcmVLdWhYcFR4YjdyWnpHemhjSjBRZlFVVzhNeUxReWxPcWJHUjhrYTNJVmVxbzByWDFCVldWWDgwQlJiOGYwWHNnZ0ExRjZKYWFVUWZXbVd6Z1B1aTk3alFpLzZKMDNhSnBEV3pUelh0VGFtNUdEYWlrZmhaYXV5YnplWVQzNGMrMGhyWTh3bnRsczVraGFXbmt2UzhRMFUwdjFSa2phMXBXMHIvQUZMYzFJMDZMTTYzbjZndG9nelZiTEttT3BxaXdsYXNjUE5EUEdhZFFzNTBwWkZydEJxaTJoQS9LaTJsUTY3RFQ5L0pPeE9OZUFOY3BSUHBHVmpMRU5Yb2NFVkdIK2I3eTlLdzA5WFpSV3BzOGRDbmRxOWpzMmVLamlKbndydWRUU3YwVTJOZ2oyVDlKV25rZkZIQ3VpY0NHK3paVFR4VHBKSFZrSjYxeXJhU25pS004cG8xVmhJSjZlb0hQQzIyVFRSTmV6M2RVVFR4QzNRZkZVRDNCTWkycGUwblEzUkQzRHFpQkJWR1FzaUJCMDFVYkl3NFo2Q29GbXVSZ2ZqRG1KcGR1blR6VXBmRG5acFBDL2wvWmJIWVNnSGVaSXgxcitCUmxaaUhNSlpTOGFpdzBZbzg3NGxoOTBja1k4SzZOOHpUN1dXT2xENGxmNnQyZkZtY1A0MG9DSWRFNjVXMnhrZ2JsRzZ5cXpRdXNQZ0ZRejV2QnQrNG1taURVNXpOV2haNUdvK2FCYUJieFRaM05xMVo4NEhVQlppNFY2bENiQ1NPREdHN0FiVVIyanMxUlZnekl4Ung1ZkFLR1pyc3NoTVpFZko0b015ZkZUWnVFN1RrTHFBVnJ3bjlDdlRtNytUcHJYbCs5RUh4VUxuYjJVamgvd0E5RVdpQWFVSUxkT3FmRkhoOTkvOEFFZEZ4TzhCMEMzTU9jT3pOV2puai9KWG9QYWVYRU5sM1MxN2FLUi9aRTdSTEhKUjBiYldSa3hHSWtvUCtxTVVUdjkyYzV4NFc1RVozWWw1Yi9UcXJvbGl6MVdSNTRyTEtORnVCQmtobzNRbWlFR0lscUJKa2UwdDFDOUl3SU1rYm5VRHVuZ3FWM2hxT3EyTW5ENHBzMGRuZ21ua2hpbmJyWDZlSlVmYU9FdStPSndvRHl5RUQ2cjJremNveExHTkFyWGRCcWc5cjZSTTk1OWQrcUUrT3hJazNpUlNNTnI4a0pEZ0FCT1NPVnFJdHdyOTA4cUg5Q25oa2tRdnZaYkorT3lrdTBieXFkRkJCaG5NWTJZZTBvTGtuUkhGNERFRmxkQ09hcDJ4MlMwa2Z6b1JsS2JCZ2UwQTNubGNRRFJWQlZrd0VJV1ZCMFZUMFJFZ3Exd29vNTJHcjh0R3lEMytueFUySHhjVzBidEcxYWViU0UvRVlDVE15dGFjMitDYzJkcEJVZUdrSHM5bUc1dVhVcG41SlJVK0N4R05GUTk1T0hpRmZqWDUyUjdOWmN4d2d6elA0V2RkT3FqMlRnR05PV3d1YkM2aGxicTExWkZGSm10SGlTRDhRbzNzZHpGVk5FOXJYMzBjRkZCZGhkeUhnUDdnS2JhdzVtNUd0YVFMZzYxK1NraXh6QVlJMkF4dnJySWRQTlJGemcrcER3YzFLai9DZmhjTDdrY1dRam1Nb1FlRDVvWlRiUk5kS0xpeUFDK3l5dFBtcW1UeVVtQUxnNk41emYwK0tjOThSMnNOTm8wZSszcWppb3NTWXlOYzhkajArQ2F5VFp1RG5mMU4rQjFDbWZFMDBlYVpUdkFwak1WaVlvbzMzRDN2L0FBM29wZXpNRUh6djlITFdTUWpkTkwwQi9YeFdGN2FubTJNTGg3U0FIUndzZm9vOEd3RytRMUk1Qm92OGsxN24xdWMzeGNWam9CcTJmTXdlUVFqZWJaZ0FwSVJjQ2xQblJNaHdwcVhUYk9JZFJ3ajY1ai84cDB6bVZ3bzRQRy83Q2o3SW5PN0kvYVp2d3RCMVVuWWQzc2plQzV2Tm5sLzRvZTFtU2s3YUhLL3dMSEZ0UGtHcmZLYVc2ODFtVFEwb1Y1TnZST2lrbHQrTUQ3clpPaTR1RndObkoySXhFd0w2TDA1a2xIRUZ1dXJVMjdTMytISWZ5blEvQS9kUGVIN01RTmNYRzlxZnNJMHg4ejhuRlEwelBPZ3I1WFhvK0htejFPL252bnJiNUtQSHdUUDlFZklDQUhYaFBUeVdLN01iSU5kdEVCeXJvNExEdng3blo0NEhBU1YxSXpEN1BDampxRG15Q256SlFuanFXeXpIYWcrN29nWWp1T2xCWjVMRll5dTlXalB0K3FZWXBjb2lEbXRmK0hrWGZBZlZ5SFovOE9GdEJXbDIrWDVsc1kyTWJKaUp0eDR2czJONFFGaWUzbXV5UE9GWk1YZmhjYU5wWHJVRXFRWWR0VERpUzVuaUgzK3RlNXFvd0xheU4wMFZIU1phYUl5azM1dkdxSXFQdlg0SW5EWW9OQTBhNDIvc3ZRWnc2TjVPNVgzazdDNGwyYmFOT1VBNmRRZnVwZHBkczJ6WTV1bWhPYjdCYlhCUDlsR2Q4L25LL3dCendqaHZWQmlONmU2ZkpQeHNHSVppc0hMSnZsdnVlZlJSWW5CUDlwQmRyZW9PcmYzNHFQMmVhS1ZoTEIwUE1JSENFNVFLN01qUk5iRytnWnVtdWg2cHdqTjI3elIwOEVaQTZ6cnJKbXlRdHVUK0lyWTRUY3FLTkk0cjJVV0ZnWnV0RGpFLzhBcnk2bTJwWG9lM0JmTGpTWXI4Vk5mTlpacEN3aG1TVStBNFhmY2ZKYm9XYVJDeXlFaHBwcXF4dkJ5NjFLeTBMYTZGRXVuZHUzTlZuejZYOFZzMjRBbGhPL283TisvQlJNd21ka2ozQXRpbDBxb1g0VHRLTnUzYTUyM2FhaHRUZjQvK0wwSjNhT0RrZ0p5N0U1aFh6SjFLWjJqZ0tGalJUSjA4RVo4SVRHYThoZnlQVlZlTmk3bUdjSitISlJWbWIxdDFycXBjUmg1bU9PZWd5T3FXckk3Nm92cXNyTER6UmNaTDlVK1pzckhiT0l1YlYxTDhrR1BjYU5Zd1pRS0RSWUxBR3VXTENWa0EvRTV4TmZzakt4N1E0ZThPZnc1SUZDZytLeVV1clBLRXdHOE5hcHNyTVMvSktMYjNDUXFPZUhFdW9TUXFIQ3htdm1uNU1QQzFvZGNsbGI5RnRJbU5kakFSa09TMGJqb21kcTRUSHVJWWNwYkp6NW80VHRmc1p1MS83SXpRL01MWXc0cDh1RmYvQUM1YmxxMmtaMU9sTlVYTWNNdkxkVE40OFA2cHdtM21IaXNuRnJ6VG9zelZkSEtiSDZKN1hIV2dvcVNNSjN1cW1kdlZxSXhia0xKc2pMbWxlSHF2LzhRQUpCQUJBQU1BQWdJREFBTUJBUUVBQUFBQUFRQVJJVEZCVVdGeGdaRVFvY0hSc2VILzJnQUlBUUVBQVQ4UU9tS1JEY1haWm02cmZ3NFlSMTVIWElkSndvWmN1U3E2aFhrbXlWdE9kamxvVG0xQ3NNVzVZaS9rTmRJc3hKWHlSYlNwYnFEZXJsK0RpVTNCVmZQK01idGtSWGxNR0tpd3NkMWtCTG1NWlhqNXFlTy93S2xraXdjaGkvZ1pjeDZoL2syK3Y4UmF1ZndVMkVZWGpVeGJjdjFEQWgyeFlDTCtPRTVZOUNKbWxiMlRxVVBicXk3YkhSZ2Jjb3BocUVKZGRFVFlPdzVSQW54QldkZFRSaUFwR2ZUNG1DSUROWXNCMVZMSTFneEdiQjVxRm4wa0g1UllKeXhuVURkTUJkWWdPT1pkd29LSUM1a1dKZVNzN0NJVXRDaW9ZOERVdkNGeHVVelBYb25La1pGYWhOOW9KSUZyQTM4VEpzZ01PZ0pMZHo2aStqWHVNN2R1TEdjYkhpT0RNbnhDb0RPZVE1SVlPRGpHVVlGOHkxa1JVSFJHeDI1Y2p1and6aDJHMXZiNjlRYnR2UytwWU9Vb2FnZHFucmx4UkRmTXgwbFMrbWsxeEtSZzNiMTZsbHd3MHdITk1zaTE0alVVdkozRE40clJrSjBGdW1rRHdwcnh6S3dwcWlINDJyeno1aWFYY1ZFT3RXQTQ3aUZVQm5VQ0wyVXI4ZU5SNlc4SHRCNEpneGR0RUtoWXVrZ0xRQ0VEcHVEWmhDQWhVc3ZzQ3hpTmdxZ1haVS90UnZPRG1DQUJkRTFrcUZoMTdsNXhwYTNBNmxZQWhwa3R5bU1lc3pOSXZnUW9JU3IxMnZTRzhIWTFPd1YvaEVtaTNqdzZpbGFzZzBxcEJvRmRnUXBPc283blBnS0w5aXF6Z25TRWxlZUdVcHBsOWpNUjFoMzZIeEZQQ1lBdHJPU1c2QnpsSllNWFJPb1g0WWVOOXlyUXpCcGpNc1ZzU0kyam9xR0FKamxUWGpnQ1dyVnVrMkxXTFBaZFJhWTArSWN3NW9KczhNQWNEbHNMRmcxN1k0K0ZhT29iVW9MVmwrZlVrRUpyOVFwVmdjeFJkZHNBV3dodFNuQ2kxaEI2eXppSFNJb2VUSTFqM0JMakFjVHFHWENsbVdkZzRxVldob3FOYXZQUnd4d3REWkdTNUFiOXErSURNQlg2THlVUmVwWGR5NWlGL1NNc0JaRkZHeTZPOHdlVkFvWmNWSVlxRlA3RUNoMmkvRVMxWlFFWU1nMENONitzbE45d0RDMWFCV2tkSG43a1daWkwvZE50a0ZFQUNHN2diTE9XeGJLY1BoTzEvTHB1bCt4S0FVNWFIZ2pWR0VmdVh5ek50NitUbXFqVnVWSDBMbHpQZGcyb1EyLzFIOFF2MDlCQU5sRFlQTUp4cFlZUFdRTkRnNTl4RDBtbnFwOHdJV0dGVjFDQ1Q0UVdLVzl3NEhRcGZjVkY5NVlmcW85R09ZVzREVVBZOWlmZDFKVDNpcjVmTlIvR3FIMjVMNUh1QjhjRnhEdjlSYWU2bjAvNUJKQUxPYUxyR2NJNWJ0SDIrR2ZMTUlHRHppaTl4MWFRYkR5NHg4U2dIWjlMeVBMNGhYQkMxUXYrdzdDbEpDNTJhWHovQU1sUXdvM1JWOHl6OTVHcTJYSFNWRWJFM2Ewb0JMTlVJcEtrWUlpeGRSNGZGTHlHc2dXRkF1QUxvWTRwZzM2dExUWHpDYUVDYmg4WEVUQXA3MHJQRDVnL1NXMWY0Qys0K1VWcG80VVg4UFRWeDFqQkZRdEtNckxMNUdHSm9GZ1hRVUZIejFGR1dyVEt5NWlGYjhzN1hJWVRvL3lYMU9UUVR1dkh6eXgvRlp3VmR5QTNCbjRWMis1YmJIYzRQbmVZNk9rYjRuUExMbFBlSHpGT2I1d3ltTWNNb0ZOVG1OWU1uZmZ3UXVWNlRMWDNEU002V2ZrRlh4WE9ZY0laVWFob3RGWjJXaTR1cjd4dU1EOGdMdVhZVEJLS0tuS3JwNzRpL0p2MkJXY1h6eGU0NmxxYkVWNUJ6UW5KcWdWcjNxTit3Nk8yakJQQW43NkNiYjhHV0ZqZHRHL1M5OXhXajlBbCszc2xhbE5iaVB5Mi9jYlNEakhJcWVEKzFHdk5OL3RRRUt5WXYvV0hOelJRUG8zQTRqRXhqMHhGbHZZYUJZbW9QL3N3eVZrRzQrYm40aG1EMXFCYlZzVm1aMndxTVdzUytTS1BKWDJSSGVkeGtOa1U5ZEdLb0xCNlpMQTFGSzNnUSsrNHNndWVNajFVSlZnUDByclBqam00S1Nld1VJV2dORnI2d0k5YUNWTVhsblBscUMvQzFxczI2MjcwWXJibGVYZThKKzZtKzI1YTdIMmpFZnVnUlFsRFRldk02emhOcldmWUxzdDNNS1NQaHp6Nll6dC9TRVMxdXN2MGwrNWJ0d1FVdE9jZjJBMXdRdVoxZFBNWHlCdUdMdzZqZ2FhN0VkTmtXS3hwM0NLMjY2UzllS1o5VkFQRlFjc0gySS8yUzZmUHkzbDU5UUtyN0RUNjdKejlJNWgxTDlMUGNtSjRUbGZLVk12YUQwU1QxSnJpNWVXSzNkS0RBNktPWXBPTWNWZlpWcFJ2bkRKWjJ4Yzl0dEF2ZFpCdmRXSHdmOGdjUWZOUE1zNG1iWGZrcC91Q3NvVVN3c1ZSNnVTcEpoMENHbjQxSHVDYTFpcVgwRy82aEtBeEJJOUx6eXgrSXBZVTJMT0JJYjY2aEcrNndyMG1XRkZtTHdGdVJRcjF3akhnWW9iY0VTeDZnSWhDalY0VGdxZ3QycUh5MzlxSldnTGlicVE3SDRqQnFCQ20yM3AvQmozS1pjRXI0MHlPSEtyQ09DSGFEbFlndWNRQ25naW9uYUNlbk5ud2hEbGV6c091dUhPQUY4bXo0WUN4S0UzUlY5VVN3TG4zb2l2eTRqbDlnNW5CYUljNmdINnpKcDZWb05uMzlXQVZnVWpVdExQYTFUd011RXJaY3N0bmx6MmtRRmdnemE1ZWFvZkpJckxvUmhXYjdueXlrdDB5WU0yeDVqV1JKNDJNZFRYUUxZcElUVGRMOU92bUR4QXJ2QjhqRUt6ek9keVBRMHFKR3NySG1CNGsyZEY5L2wvSlJRdmRMQnI1MXhEQllqbmExOUJhcm81WmxxY0tRQW9iVnNXUjR4S0wyMER2Ui9lVGJpNDVZQVg1WGhEdFkxK2Y5cnRxOWwzNmhCNU04UDhBOWt2ako1SEJUNm81bGt4aXZzeDFMbjdBSWYyOFB0OXQrWjhyVXoyQ3dHUktFUEs0RG9WYW5LQVdpQW5rWDRqVEM0dDI3bmI5VjZJTThZcG9pLzB0OVNrcEVZYkNyZ20xblVUc0V3bVoycmdwQjZKZlhFcWhDN1JaZXVueVI3NFU0L1o1WHpudU52Vm1JNkV4SDFOWWVZV0VkNU5BZUdOWEFtWVVrK2F2dUt0N0JlYkxIeFJhL256Y0ZEeStWUjVGWDRZM200M1Z4UWVSNzVoWG9qMnJTWGtiVVBMVGFzczBiM3lJSHJScng4VGVYL1VxNFAyRlJZQmNyZjZaVTNYWHp1MzROWktVZ255YWhjMnRBMjEwK09uNWVZQUd5RGN1VDFycS93QnhoMWNDNzZpSGtBdFJ1UzVXYkZ3bXd2a3RoNXY3TUFvQnlJdnV4WDVnQ1NNVzFUSVlha2ZqbkwzTjN4akZQaUxFQzVZZ1AzakVxa0s3NGhRQ3JwVi9HcTMrNmpGOHRLK3daWDRmbVdCYm8wTGxMQi9TTmFwVXJ5eTNBaHE1YUFBdW9hUjd2NHlFbVVCcGR2eW5qeWNSZ3FYUWd1aURoK2VZbkQxR3R2Ti9xeGlVaHQrYmgrMzhzc05oRGNGbkhETEVodVBKSUxOMWlNb1VIcHlsVnJ5YksxalIxbEFDYzJqS28rTmtTemZuWHVGT0tWWitoQWFna3AxckxYanlYZEpURlRBUHRkc1hXd0hNTjV3V01OYzQxYzgzRWlEZ0N0SFp3NUxuMWN2RGppb3FzbWxzNFBpNGpFNHY5ckFEMi9rQlpsTTlhd3RPYjRJUm1Bb2lib25IRnVWREYyQ1psTEUwL1dZT1QvQXJ5ZW8xVkNoU1N4ZFAxb3p2S0NBTmZrTUhBVjZ1MUt1YU5NTmwwNUhZQThvWDdPWWZWckxLVWFrOHYrUTFwTGkzaHVWd1lNUG9ZeDFoTExrSHdmVThxLzN0VWgzNG4vL0VBQ1FSQUFJQ0FnSUNBd0VCQVFFQUFBQUFBQUFCQWhFREVoQWhCREVUSWtGUklHR0IvOW9BQ0FFQ0FRRS9BSXZoUC9ObXh1aHpOelppbUtkaW1LUW1KaVphNHNqd2hQbTJXYkRtV2JVT1p1S1lwaWtSbVJZblJGaVluZks0VExMTml6WWNqWWNoeUhJc3NUSXNqR3pCaWMreWVCcU5vaEZpaTBKRFl2UWxmQ2tLWW5aWlpZNUljaHN2aWhRTkdqRGp0bUx4N1I0MktwdEVjQ2FINDJrMlBCOVI0bW1hT3JFaElTdEd3cGtaQ2xTTFpZMlh3MHlFYlJESDBMRVJ3N0l3ZUlwUjZJNGRZVWVOaDd0a1kydXg0VkllSWVHMlpjV3NhUjhSSENmSFJac1JrUlkyWHdoSnNVYlJqeFZaaWdLQmp3ZG5pNHRTV0VqQlJSNkY2SEcwUEVUd3VUSStML1JZRlQ2SllTeUxJa1h3K0lxeUdOZWpGNDdrakg0dExveFlLWXZISVlFbzBZNHBNVHJoSzJMaXhVeWtVWkkydUlFU0pIMEpYWnFRZ1ljVm93UmxqbDJZb1I5aXdwT3hSL29sUlloc1FtM3pCY3BJcGtlbVJSRXhRdEVjRFBnZGtQSE1XTnhNY0RHbHIxeFNHNkxRdWRoY1Evd29rVUpFRjJZSXBtS0NJNGt5R0pDeHhSR0tvalNMUWlTWWlDZjd6MDdvVXZyWWlQb1RFNzRTRkZpUmppN1BIaVlvVzZGRHFrUVRRclpGUGxTR3JSR3RoOUh5SXY4QVVmdG9xNGlkQzljUklvakVVQlFNY0dlTGhUN0lZOUdKZEVrS3lGaUxvMlZrWnA5RDkySXBJU3ArejI2RTFRMTFhRmY1enVrdXlNQlJSQ0Z1akZpNlBGaFNFaUs2TmJaR0tIU1FwdTZKTTJNY251T25aaWJSVm9TSDdHNUppbXFwbUZ5Y2JaMmRmMG00L2pNY0V6NGpEaTdNT0cxUkNDU0VXSjB4cG85alExOVJxbVJoYnRDaGZzUzFaQ1hYWkRxYnRqZjhHS2tMSm82UThqcTBQSW11MFV2d3h4STRrMFlzUmp4ME5VdWE2c2krcUhEOVhEVmREaTVNd3hTdGluVG95dC9oQjlEOWlkTWNxYkUwaVRpWS9Uc3FtWm9WS2tZNGtJc3h4cEVGVEdpUWlGOHJzMHNrb1kyazM3Rzlla1NVbTFNbWxxUmxVUzNURmsrNkZLNXNiZmRIN2I5SDVRNlV0VHlPcEtTTWFveHBNZ2lKNkc0dDB6U1YwT0xpckl6Yk5oTnBzak4vMHlaZHVpTGFsMmErNGl2UkptelNQbWFYUTUxa3RFWjFiRlBxeU0zTjErQytrTC9vMDBuTC9oT3B3YVg0UlJnUkVVYVE1VWZVV1NNRVRudWlEYWRNalJLRHBqYmd1aGF5OW8rSnRXUnYvd0JQWFJOSytpVGV4Vng2NGMzVkl3c2xOdWtMdng2djlNYklvd1ExUkN1SnV4eWFKV3lMa0xKSkwwUXFYWnRETGl1TEZVZXI2SGpwWEVUY1MwMXhQVjNxTkVSR3BoVmRrcHMzcEtKaWFsMlFpWTFTSXVoeVpKMnJKcHhWanZzVEl4YllwTEJCaWs5YlE1OTBZc212WDRTYVJ1U2xTczNyMk5jUlJTUFNJTDdEVWRtUnFDUC94QUFuRVFBQ0FnSUNBUVFDQWdNQUFBQUFBQUFBQVFJUkF4SVFJVEVUSUNKQkJIRXlZU016VWYvYUFBZ0JBd0VCUHdEMzdHek5pemRDbWJDYVpaYk5sd21Yd256dFNObVd6Wkd4YkxSWnNqWTJMWW1XSmlrL3M5UWpKTkNkQ2tKb3ZoTy9aYUxOaXpiL0FJV1h4WlpZdU5oZVdRZE1jNjhDeUtpTTFRbVJZaE8rTmtXaXkxUTVNdCt6d2hPMExzaTdReEM1VjJSTnJrUlltS1M0c1Q0dm14T2p5UGhLMExpdWloR2hWY1JYWWplaFNMNVQ5bEYwajlIYlFvaVhRa1JpeDQyT0ZDb2owT0lvTW90OEpqNFhLNFpib1VSSVNGRjJRZ3lPT3V4UUhFMHRpalNFa3hRSlFORU5kRmNvWmRDa2l5eElSRkppeGZZb014UVpxcUtvOG1sS3h4SC9BRVJkRW1obzBIRHNTZGxjUHlOaUVtVUpDVmtJV2lFZWhSRWh6Sk1pTHdPQ2FIRlhSWFJOOWtHV2hpWEw0aVJYQ1JHSkNJbFEyS1ZrbzBWWGtVcU5rUW5jUlNibTBMd05iU0ZHdXhqOENWbEgxeCt5S0VJaVFWTWloanFpQ2RucDFDMlRkQ21YOWtQNG1OWE4zd3F0a2VKY3NaR0lrSk91SW1OTnNWSmRqbWtiaXlKR0xKdkdqSXFiUnJVaEVlb2tmdXVJb1Nvc1h5WjQ0WWhJU0lScENnS0taamNZazIzMmg5OGRuNDhxZEdXTnEwYWlUS1NpS0xSQjB6SjZhZnhHeVQ4bUtIVmt1aGNKRUlXUmdhS3lMcDlqV2o2NGpPdWh4VWtWMnpXMTBRZFNNZnlpU2lrU2t2VFhRcW9kSGF0bmxEc3g0M2RqZEtoK09WRk1oR2tMaHhzcDAweWtob2k2ZEZXaFdpNUg0MHJSbFhYUlAvV2lDc21pZmdpblFvTnM2aW1SZHF4b2FSUkJJanhaaitYSDZFVVljTTVwdjZSVVY0Skg0M1RvMXREamNXakRDNHRrb1dUWFpHRGw0SXdVVTJOdXJNS3ROTWtob29pdU5oZG1HVVlLbVZHVHMxa2wwZG16SVpaUUdsVzBmQlZvZ25HUkJKbzlKdDBZOFZRWThaNld6WXNYcHhzbjhwVWhWNEkzR1hmRTBhc2hGVU9CbzBoV2kySnVLNkk1QkRnbWFha0o2Uy9vVUUxY1JZMFl2QmppcUZGS3hZaU9GUk05VVB5eXY4aEpjTkNWUGl5UHlOVU9JOFpDRGJveTQ1NFpVeU1pS1VqMGtZb3VERWt5RUpRZlpCM3dtaVU2UmtkaXhpZ2licGV4SW9pUlJxZkVpblpjMCsyUlVaSzJpT01oQVdNZ2tWOFNEbzJZNXNjbXlLVEdOMGZ5NlAvWiIgaWQ9ImMiIHdpZHRoPSIxOTkiIGhlaWdodD0iMTU5Ii8+PC9kZWZzPjwvc3ZnPg==", + "description": "Indicates the concentration of airborne allergens, including pollen and mold spores, which can trigger allergic reactions in sensitive individuals.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'IAI_level', label: 'IAI', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"IAI\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 12) {\\n\\tvalue = 12;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:flower-pollen\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#3B911C\"},{\"from\":2,\"to\":6,\"color\":\"#7CC322\"},{\"from\":6,\"to\":9,\"color\":\"#F77410\"},{\"from\":9,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#3B911C\"},{\"from\":2,\"to\":6,\"color\":\"#7CC322\"},{\"from\":6,\"to\":9,\"color\":\"#F77410\"},{\"from\":9,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:SUFJLWhvcml6b250YWwtdmFsdWUtY2FyZC1iYWNrZ3JvdW5kLnBuZw==:SUFJLWhvcml6b250YWwtdmFsdWUtY2FyZC1iYWNrZ3JvdW5kLnBuZw==;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAaAAAABcCAMAAAAfz5G9AAADAFBMVEVWcUhXc0lVb0dadkpTbEZUbka6wspbeEuuuMJZdElUb0pRaUa8xMu3wMdOY0PByM9SakfAx86wusRTbUlSakRRaEPCydCqtL1VcEynsbqut7+yvMazvMO3v8Wbq6u4wchLX0LHzdS1vsWwusCQo6Cst7zEy9K1vsSgr7FPZkOstr+1v8iaqamTp6Sstbm5wcens7yqs7mosriOoZ6MoJyxu8K/xsumr7mlsracq6xSakmfrq9XckyJnpejsbSeqK28xc1UbE2nsbaUoKqSpKOgqa+lr7aYqKidp6qVqKaXpqZYck+apah+jZW+xs68w8mps7yjrbaVpKSptbmlrrOiq7Glsrqyu8CUnqGBmYutuL2cqbOEmpCRnp54iJCgrLdgfFqdrq6PnKeLoJlVbU/Jz9aTnaaGm5Oqtrxng2WirbN/lollgWJadlJQZkeqtr9deVeToaCfrLSjrbB1j3lcd1Svt7yjr7misbKwub6hr7eZo6eCmY2QmqSLnpqZprGbpa2BkJhvi2+Ilp+Voq1Xb1Fyinehq7SRpaBZdFCfq61hfl23wMpdeky1vMKntLiMm5yfqaqQnqKRn6lphWedqrWXqqiYoqt7koSGk5yfqrOnsLOXpK97ipNjgF+qsrSdq7CIl5bHzNKUoaaXoaKPnJ+OopxviHRzg4h9lIZwgYWJnpR+l4WDkpt5kIBzj3Vbc1fM0dmiq61+h4pthnBrhmqNmaB1hYydqLFZcVSyub94knyQmpx3jn5siWycpq+tucCMmaSVo6maqLGXoaZca2u4wsuKlpmGlZGYpqyLl51xjXGAj4lIWkCCi47Fyc6KmKJ7lYBjcnRndXhnf2emrrDP09uDko14houSoKWMmpeCkpKusrRsfIGEjZKFlJZ1f4Jgbm9pgWtoeX10i3tje2Ffd1qGnZB6g4Zrgm/N0NV8ipBFVz5ZZ2eytrhvf4GHkJZveXvCx8y3ur19joyqra/T1t29wca6vsJDUjylqamepKVVY2LBw8VmfGl8jIVTa2F0AABYP0lEQVR42lSaXWyTZRiGtzFqN9qUbriytmuzdt3m/hiD4paNZUEi2YgDNx3ZIEJdCIVJhhN0LIsENGQIyIG6IEGzeDB/hhgSiAaCqSGaGA/8S9ATDoyExRhj4oFHHnjdz1uY3t9va+fBd3k/z/0+nwWPoIcffnjVwytWrVpVvLxoeVHR8oKCwsLCooLCksJlJWzLlj207CFkFz6UcC5cxk8KCgsKioo4Cjj4u/LS0tLy8uLyleUrylesKF+5YsWKlVJ5OV9xlopLkX7GybTaHV1dW08dPHLw0J7Tz6E3P//88xMndJz45pNPbtz4/vtLly59f+MGl+nh4eHFxcXhS+c//PD8+cuXuYxNT+/at7BxYePo/Px8zfz60fXrX355/UzzllhHZ2dnhnP95GT3psGWkWTU7497PP64XXz9gWCwx+OPlIUbUqnU4NBLUxNzc7fmbt2am5gaGmqdQu3nwuloagi1tla2hkKhXM5bVlYWSXNE/CjOvygejycS0XRZtMxrGm/l5/zJS2jqrVs7d966+Oqrj73x7BffvfLK2x9//PHxJ9555pmff/7go4/+/PNPne58dOfXH3/48ss/0D//U4HoGCDwFC9HPHHYsEEAQA+VlCw7Cxl2SXhgpm9LhKjAIAnT8oLlpWhlafHK4uLi8hUrgYS4gMsJKo5LHpLxYZO6tgrQEQF687k3AQQa9Mkn33xy2PEZHr70/eVLgrO4sLCwOD02NnwefTg8PL1ret++hdHR9fMz0jq0fv3zNb1N8KlHB7o3tW9qa3kxmUwn/J6eHk/cIzweX7CiNuADUKQsNzKSDVVCaG5iAkgT3W3ZEM93qDWbS0ciudahyqGhysrtra2t446QICUMkEeK+6sSiUg0GilD3tz4+HgrAtHU1MRbOyF07OJFCBmg4x8fP/7E78/8/owA3QEQZxj9+sMvX4IIQo4Rd0gOWiVASIQMkZnn//5BxomP7lxSUiI+zkblBUWFy4sKcAf+gU558QpOK1eshJOwcAKHGWx1OUS0OzjlnFHXanPQoUOHTp+GzjUBEh7kDIRxphchBCDw4JWN+3YND4+NfTgm++yCj8wzM9Pc3Dwzs7lmZl3N85v7muY6cU77ucG2wcGW1NEdO3bUGSCfBwlQP4B64v5EJJIcGUllKyt5ngcOHJiYenwwG87imVA2GeGhe1uHWuEjG/Hoc15vpPEBHzkI3niysbExgsr4fZl3PCdCBmj37gkgHbv4qiwkQMcF6Pff5SC8Y4Bw0o8/ipDDYngcIMHRXkx9W2V8sAM1qyQPyJBI3Jw9ywU0DwkbV1lItRAtt2JXWlwEB/lnBaeVEMqXNiiwGxh3w0n3jlLX1i7jc/DInj173t2v+nbNipsIOQNR1iRcA6BRND+6gHGGx4aFR8RmNjc3D8wM9PYODPQNPL/5+YGBgaa5+sn2wZaWkZEXR168Lj5Vazw+APnYodQTqKigxBmgZC6b3V451W5qDYW9XvCEvOnGSCOuCLWiRwHEl+PjYa8YpBsdH+gg1bkzjY2JiDA5F4VkIaokkHbvxkZfi9B9QO/IQbChxHERIStykiPDje4f9CDxAZDZR76AUAF1zNzC+SxX8HA9y0fhQcAplACkMgfb0tJiJAthInmoHESrSwFk6uLKR91xGCqsUwqeU1u3wucQFe70c9fUfqz/gAdAh29Q36ZlnNFFeCwuwodiNr9xcRoXyT0b4TM727Slb2BLX98WLoI0gIMmu8+lRkaS6R3o9der/Gu2wccDJDYABQPBHt9XZ3ii6WQylc2uhcC5wXPZXNJbFgphFR63pzEdSY/jpVAlJY8f8A+8ES9lLlFVBSCHyAlC4DEj5QGhl7Ttntr91q1jVuQEyDWhTx2hPKc/f5WFMNH/RA8SH6tvxUWuwFlGAI4s5Ai57uMIaXe1r7BEP4QnuxmoeHmxQoI2841OS/7Zunorwi6rIcLHLnasI3GrFkQH2nP63Xetvn1+QmcJQDhoF3jmsQ18dEct650ZXdin6rYRPjWzs5lYrG8LghOUenv7tjR11G8aHDm6oy5aV1fFvmbbtg0b4GOEBKgnyKevvuKJRtPpHISyPP1wLpmORrxhby4tO/gSBIJx+KRCPG8RApC3AYtFEom8h0w+ACH+BOJYTG3IooKrdC/tnthJlRMhdNwB+kBwdEaOkLQE6RcBchGh2AgZHwHCQAZBdCSHxvUj14NU4lyVowGxKc2VF5VKxUaHrdTigegABEAQEYmtZAJMBB55iau+U4ETHyIc1Y3idsJ09erJT07eoMQtYJvmmfnRRdW4mebZ2dlY8/zCAnzQ+ubZjs6OpgHxMUB2EwMQrSeq57gGbfNtMED2SA2QaOmhqtHoqadS4XAyWke/L5NFKFo4iHrVEMpmw2SzR4kNla25soZQqCFcRkfzr8mnBJ8BivPvcpRECAvJcK2hIRABCQ99DaG3387nuGdECD5cREmApF+ECS+ZAAQfpPpGyjY+EEIqY8ZnSWahs8ZJFjOj5WtcVwEqLVitJqQuhHQCl+Bok+g0FDMJMobM0AHoCBEOPqf3PLf/2ueWD0Dk9P77J0lvwjLTC6IFAlvzbAbFeudHoUPFm+ltynR29DXjGgA16QQeAWo5Gq2rint82+g6G4LBDRssIeip8iXyxM+AQUTSIEoeTaejVfBMRKOJRtx1xu+LlqW9ItQQbqislCdaG7x66uF0GkKWs9coFCLc+BV4OIlQQ8P27a35PGe5W1Xu4mN4SIjIcRD6+edPP/3gZ+ggi9p5iZEdCgmuwK1apQonH6hkmT2WHCT3gOYsJyt3Cg9iIzlAOsoJ2lQ55ADBCj6SrKM2c+oU+ykoOUjuxGeVtyNH7p1+9/Rz+0lxxucwEfvEN1jofSM0LUDNvc29o0SCZvDUQ6i5ZnR04/r5dc2zsUzHls3NvRjHAYrFOl7reO0FSlyU0iY4KBAIbthmgARBzxROyl8JCOGiNErEff3KZHEfBhPJaCSKhbLZhvGGEAIQ1yHydpIfLxU5nwRRBCYBYj3UMG7ir8SIPsSKCELffff2K8efeAIXERU+hROU0J1ff/3RJE7OSz8sOYgKZwWOHTwYyCQuVtUcmft8VP3oUFYJC7oKC7v4O6W/rnKinLA484DHJApAkE/IaqcAxcnAGJ+D8IHQvXvgka4R4CwcHP7mm6uO0OHLWGh+pre3d7aXQN2b6chMTmbqm5pnampq4Dab6Wjq20zfEZ6+GPaZm5t77WkBeh1A8AlUkNkCWGibL7+wzC+JBMgQ1bHRqXzB2h7qVU9tsN9HwvMkGhON0Vwq1RDOlQEJQNQ61kitI2GaVQIZIsPto2DSg+Qji3LekCzkpE60GxMpy33x3XeUueMgsjiHj2hIshASJA4HSQ4CkfhoioAsMYOnsEB8LFdDSGcCHCZy6yGEw+BoQcFcJ7K6WQ4aEFnvcekAl8DiIDUMQKBAAiRiCEDcHMQ/VDgZ6Nq1a85B0sn3379qhO6eH16kCc1KMzOzsVh9ezcmim2BGHxiJISBZvERIgDNzbFIfeGpTamR18UnUIFqa42QT1QS0TrcgYPEB8kKFss8Pf0VgZ5gsLY2EPTxlQd4/kSaYKcFqhfv4J8plrNrUyPJZJRSWGdpDkDCakEOCwGKKDfuVrZ5QEZo561XH3v2WfI2gVuMRIhNkD64c+fOr4LkOEk4CD6IDmR89KiVs63FCJDRuZ/e8sJAIiT/CI82M1ApZ5cU1H/y/qHLOI9gkrwMkixljLjw3aFD906T4Chw1xSxWQLJQiJ09TcrcjfHdu3b6MIBwkHd7d3MCTJNsUymKYOBBsjY1DYA0XxkIAY83YMvXgcQfGrRe++Zh3w+OYMkEKetq0I1Ir+/iucsH/T0B2v5eXV1RTCeqIv3+FXH6pJ16bS3LOLdzrynVXyeHMpmSRTJ6yAysE4GSEWOGxU5AHlzXIkMAsSSaAJCb4CIqQKBzird70+8844K3Z8QYkHEkghSIOJwC1Wrbxoj5COCSRiQ0WFD9z205CABcgZytZF/werSYgz03/qGACQIgnMIFoeOyFB8qX/Erb6SfxjzaMqjgH0YPgCC0Mmr6OTJu5fHdrn4Fuvsnsx0sAY9Z4Q6J9snOzMZWwE1mTrg04mDCNmp6zvq1jDSec+pFkIb8NC2eFWiyo9Un76KE77i+qTxHApW70XVAQ+/6QlUJdSzoh5/2ktaUDdhZLfzwlRbNkvmO3o9nVYM8ZETsZF8E4GPdSGrcZo86KZBf0llhBCTuce0JGKuIEKSC3XK2hzixGE2ogetcioqLkI84UIzEHgsrWky6rhIqnNIgCzAiZDw0IO6rMAJkKRzvv3gE+cd0OzRsQdA+oiN5KZT3CLVN+NzwoY8xocNQK7I3b17fnrXy7ShWapbe2cs030OQoxyGBYMUu6gA5kYfBydufr67raWEfhgIANUXS0LVQSDKnLEhHhVVZSH2xj3PJAPOv39gerbt6/srfbFsVR/wGar9Cu/skIYJ1DgLuzcObFpbSiUSjGiuB6FsuMdPxORsNCZPCAvfHAef2pZYYjZAnH71kUY4SIitw1P3crVFq5Od9ghJAdpFkcLQtQ31TgzBYBM6j1wuT9FQNaUkC2U9EtNvqEEoeVdTMJtXmB0uKOOySICBBakpQ6H3UOM3e7u2RJVgBgjiM6NG7DR4frQSRx08/KH0wvrFRQyk+31TfiIRX+3Rm2p7OC5+phyNYRiqm2m7vbBkR2vV23bEKz91gGqNgsFAAQhNaK01qQQsgZCbOsPBABUsff231f2VvhsRVvr4cGfYbAab2Tx2uAtyw1NXfjs1s4DbWvXroXQ9WQdoJUWqHQaSqBII3K3XlQGM3kIjW/HRSxZjx07ZuPtV5BBosi9I0JLC1dH6H7MVoGTfzRTo2apeuEdI2S+ecAHWHKWi+EYyDyUF5SobrQiGJXaoAA8LqI5k2ATjCJC3MKFzXSPgwZEhrumIdzhwzaA0+uFGzdv3jh8Fz4GaGzXy+tqZnoV4GIy0mB2sP1cNsugc7CdioZxXpubwzqTTx04MAm/VJIIp5m18RGgitpvsRA4lLv8AFKwhhVwwNNPEA/w8yt/C5B9Fazw+I1dhZ+lUoMAvTT12a3PPnv6cRHSOrhuRxIRF+rIGl5I5BFFELfuk0hJDZQ6LMh82yY/CnSsiwgMlDoRYrPIzYSOMvdfQMWaxFkHYuPpL3Wg+ynb6AhWiZuYImBiIIJ2vgd1AacUgQdASKlafAwPfEwOEeVOZ2PG6Z4RosJZfTNAvAJy73zu3r2JeLUwvXHdupnmzGR9BwudzsnubHYwO5IbSeYGu+snqXf1nWzcIZ5faiSqAldBwxcecxCA6EK2GooDSITqEnKIaBDF2aqrAXR7bwV26u8JBDx+WaunP4IaqHG51qnPds7NHdjUJj7XE9FkmLAgpQkRyGoaScHZSHJ2WiJEE7swYWHhMRCJkSNETkCOkCyECiwjILcKchlumQEyiY7I6OAkWWuCH3Ilrgswy4p0BhN1TuXNTXCocOoy+dpmfMDA/i6IHBvNr/mWL8WHkO3mPJq/6SXQGANrIOm9HJexn/bN46BYfQZAHZn67nO4J5fmP95Ue17dVDZpE/3nKP4hYTs4TrXvAShISljjk4OizODIyf4zRkhrpQA0996+jYVqA7VBXw+HGlNP0Bcpa2xkhEAvGbpwYW5CBkrxMi95PYxG2FOUMFKbDVkjdCGpkT1ip7ybIDQuQrttNAciZD6CkCwkOUiykANkWi4HFUjOQA6QCpprQZIjZYMeNwZyLxy0WDUVdYGoFECu/cg+h8w8QiESTkIhQtph5r6gAdkq1QF6/6QIMYGbRsPTY+fPj+nFD+/lRms293ZkOokCnOuHBsWnLhEdyQ7y1gdt0rmNrYUhKeM3Alz13mrFMvAoxlHDBIjhgi2GtDa1eYACAoSw294rtyF0pbq2wseiNajU0O8LerzhxjNkMcaprRMXLhx4vC0UknGEJ0UvSjFP3a53RvCxmCBxcaC+MjO5nkQjqnx0So0IQuS5x4SIRAchohxsnmGzdmQOWvUIeKAjyUHq+todIGchk9nIrsZGW34dhHfYXNhWdSuVf04dRHk8eZsAQXp3/3P7YeK6kTkHPEh4GJQy3lEqOPz9pfNjw8N636P3Ptza4Hp9TV9TRyzT1Pv11xlywKD4+P3pXI4+lEq1tJB9U7x7SzHEtgkCfKAjwUf2oQUBSPL4gROFkEPk69cwSHyu3L5ymxpX26/2VcGCNcDrCX/OC6CwpWWS2OMEhLDpxVRqrdS21vgw67amIz5nAORk1e5/hJ6kDwFIiOQjBYaPKXL3JQdJOAjlKxw7j1iMSAkPXjUYFCCZl9jyJU7TBvHRyRC5+ggh+QcDsR905uEAhEkcHAvVOUfHuUdiDSQ+RALS9c3zl3DNLgBx4B7xGZ1fVzPQkemI9fX2NcX0Om5EfPwqVcnc0RF0NJncsePoDuIbLsESAuTkAAX+2iD7sNxREwINM55oFEI+ShxA+CGEVOMwD7hqUdBHyvY2UOLCYb325tVQ2/ZU0vEJtYSEpw1AIQCxMBUhTOTGcj0IF8EHuVak1VTlS7t37jzm+JiPjNBxrVidVOTwUIGlbBKCTXo43GN2gFCJyBgisZFsurD0QhXJR85A4iT7OAed0qrUNR5YgEEshOFNwXDG0dkRU4JjzAMg6BxGpDYDJEJ2Fp+amubmDs2xGYvGOgnSuahf406qnBixpXnaUR55lV7+BL5VA1oy0LeBvwJ/bbP83N/jYy3EayIBIiJ7rAtV8GMjhKoZJ1hlrA16GJuGGxKNeraVlDYqHV1HGmlpWZsHVLl9+3b3ylUxARryjyMkQNDR7jwUcmGb5dB9uZd5AMojUhuSgx5M4lxI0CAOYRDYLL3+MfMgA6SPJY6PNjOQ/OPOXRAyPsgMJMFBZITmGmudN/e/uV+VzsT32m3Ic+3qVdW3wzdvXr5BghuDy+KuxX3I/LO+pmYz/68O44PZLb1cp4ayubQASWQyrTvrNHipWmMD0r++tXQtPPIPxYoAh4F4N2Qpzg8caGqa5gbS/TbjAZAQXal2YMlzDLgTyVzjmTSPNxzS2zrYHD0qA7WsRZvwT1slFW5qiDdGDEiNkOPjk4OM0P8BTb3Faog2dBGxKjJCb2MhADlI9proXyrONbbZMYzjItRpw5DV6qlFqysTnZYZcdhSa/MSupZ2iw1NCKrIopXN5nyIskidModVUnGMOMWCyASLCnn5IBKTCIkvEokPPpDgo9//uvu0cfV5nvXd3u3Dfvlf9/++ruseKQ5ANo9gYc0Gy3AED9ej88N0dBiECKuT8jgQPubgXNgmFfcmQMiHxYcWguSDQQMQIoEDMMTK8Jh47EsU4VQyUIL7+BN8m/FRB5W2nOubwofCKJ2G887dv5+C6IWPL0ZNQf5gjRdxYzZz8EEr344Bphuktz//ZPGZICi7TUBHEUdtk5EwHkF8cmNjmDcfkAUlheEcJm7U8778MqJ1JBQMqXiDrV5Y8wFZZKfWNzdr66vzpiH+65cKJyDx8WdKVKMjxW0uQUh0LOi2Pq1liABST0L+Psjx8ROcs2bGQc++fPBzNhvn2qluekHFHi4I8TgQQH1CKAg+ymIEv3+ubkDEMpu0o8T2KTVsVXQAJEIo6DsAiQ/9ORpAN74pg3DmybOzp5+IezvlXGoGL9aU4SBC8PslqM24eSonH/Tj2GDIvmWDCh7UA55mHJGFI6RD4fHA6s/JwUeA+ogMEDbhbAoKMzO21quccAJkSamhRCxIektns8UqsdXpdGrldeuKm1WAj0J8HB0DBCEfkKLDxYvijyRk1VOiS8hsNisQAR1CqujWsg8Rh+64FWFP4XLYlOQUvnZoNAgQNQRGDhwgDVFByLKYFXEIq7Q9ABBbiexL7gvv6379YS6qBuS455AQFo78piEr4sYL7jnr5NPJcFSpqy8StfVzKtEIcLhmCDGSDHjL6oM/8PmMfatWKnxIfFJPkwES8UEDRJQeN6XTURp1eIQhliGgQEhOjuD9S2Nsn3J8mbqcuWUBCnvwicUq8AFPsVoqlT76aP+9FOlqELISqcy2bHaPD2x4WIUuP0VJfKkXpDsI4RNcB0LhZhb8YmlvaJF6nOUuMRAJjYjo1atlu9FFv9eth1QnSnhA/i0XZw4BQFqBDILapLThgKNONq3Sb0CkhQhhWXkUa4B2JB8AqXDwDnUDmQR/KoSxxHvOOsuWICoFp2gksZadr0wOYpetUpMDjNVmeJ199jA1NxOQeYM/VcA+GziEAYKQfr+7UfnzKClOOQ4TN5aboZb9Xs/J/W2ATnJ11jEN0Y26XukxJEe5OASUzhZL4MlMZz7q7CdhLW06De2aT0A95g986fC0RYiaeLm2aWw2NzvczDYioT4hZ+R6a9BxtgipWKpRqv7UIojMVysgIwMHHW7C7BtBKZt3MHI+zpls6/HIu3XpiA8BH+vFgclU9D3KQjtQIYyOBAQe+DCVeKXZtzdFiHlepTi2qadceOEdL1LSyZ7z+G4EOOplCgwxNAQZtqLIh6QGIcW3AALOxFwTMjs7wCE89phUIaK7sTUAKUfqm3P8rKMMEHwcoN/5l4VVWmdomZoQALSDgJLGJ5Phmp7e6nz+0EMQsiyHiMxrmy9w8lEYoHEUpM0UWQ40ZW7CJCQn5wOyJAegI/oC6mc5JTkA+UuOniYkLoIECD4nIZOP+WynIVUS2APZDIjyG3i0/iOTHp2PAYSKjJB8G/KxrY/89a9cv/r6YSyxL6HXAIRLuORceql3aJ6XOikWTnQICmeQsb6cPhIAEhzwwCcMnPDCykoikVgBUTiOfrSn3YVR1IsEECCVONVQB4YxFr6CIES++/339977/T357ZOOwpFZusL+LSAfzBv5raTIbBU+YzuDK+ssYRR8Qtxm6SwzulA9In/GyFS5DKFO2UKEkJAqCmo+AEghQBBCRFqBQGRG2Z8Y4eWsNmGM7OpJCEAHg8jVEFwVge/3U5wG4QEkPqLjmjygIWgl8OZdPvE+zgBbjYC6Wx/YEO/IwkFH1QP44BLuQUBnwgdAl5+rats6N4U4D0DyseS1YW36ZZKtsS0dGRwFzrq5swaYRLvVaiUSO6xA3k4slbqiEqtAaDIcmFFDPBfAK+SGISE+PiDgAOgH+W057hkfkOeFAISBYwEyEWU/e/blH5/+/PMXXljCyoGILOfWHCNjb2T/1BuCjwpyTjtlu9x0sCQkQL6GbGjkaPnsgyBkO5leLY6LIKn54nGfUPbDiDurpzaQGFm153CNjbg6j5vjJcM5PmryODCK73ipFffN++9/gCngpW0pjQXBQT3wAZDkY4AYVLzH1CMB3SYFnZPNnkOvoTIZd50CkhpIRAc+WDZqNobnKOBA559//lmQHW629+qEZuUi4WgwRVSoPUQjgzOqeDsBDfFOKc4hUt8BQNIRQV1heOZLa5GT4uKTK+awW8Vio5QppT6671l6cE9f9JAAUQ1dPWMZHEJiYOyje45TjmDTxCpUZv2RfriIDoQ0HOwmFYyQKegICIHIdqo2haiQfnzh8BIqpbbesAiEFCBSHKQPx9MKohdE4BFsk+pmDFyLFDpwoYugm3eIyFpx0g50IEPg3RQUrt2pBZu7lnzOhM3s7OUMHpxbzc5ns2o4Rz3Hh+RESD6Ey3FnIyD4QEd4/gnvSDzJRH17Y2N7jy4BDjt0BYBiu7vR+GAA66Z6gVazoxAKlLtGW30HpThbiQSfuqnmdh6jChGJkDNX8NhFeYR09bOPPgcQfO7tLBHl8vy8CHH5yvFjGT7zI1OSkFYhAeKW7bbxbX+u0QFCQr2xRQj5IhIG4eHhM/J3QDa1DUCWHx8QD24UpEIp4drYzIDIRAuQdXg0Be9CiJTv4AOd262bYFR4IZ3+6nPjPfg3O7Ywe/klFkyLqFEXs7VjMGCADE8PkJuuUtFtADb/Evg2AO21Vurb2/W9Vjq5skZRKBRTMynqhUfZ/8AnZ3tVUIFhzLw2lP7+4ffrQcQLQCRRW4RY+ilDUMprhpTmsggoO9LIbj39NPpBQOXN2matpiQ33gdkt5VKleHcMLCf4yxk5HRG5UElOfVXDRB4REj9OkL9nJ5L8DerImMPn5C+CkPggIe8yIPbSGkfRK9OgNwKxP60y0fqcYSu1AMNfQIjvoB2lNCuVEqz0oHSmibidYbhLOjMXn4xE4sMxWsohAxHj3sRDah+Zs1OpTcf0JjExJLERkgbn3iz+VcTg7AmQHvtJIDqrSLNopCqqjFsgk0q6phDAL+BgCRAjSWIB5VTNkP4t9+tZYF8aOGNnmaFUCPk7WDlEsk8zcFsMNuhboN8XIIrb66vUpazeQQXcnDgYTYhnx9Zranh0EE/CKdH6N7e9DYSMkJWi1P0bZzpAQZUc1RyM0AoiYfvG8xDSD+8jApX71tVKXVzVBrSUYH6G51S6PO52VfROyrofGeOQKfmLN60uE5x5slnchqLpKbC6DPPuJEdRhF0oiRmmxfoaH865Ls3bpMPaYh9Ka+5f5s7eIOdhbWVRKu+sb29xr3XYlFPJ1OVELsg+Qx+gA1shwOauEJJ/EsrETxIdRhtWXVzCDMDziMc4+a0qNFqn0u3bsWLhQ5dZeepjCWTTfICkwrfh54m1fSWoWAQPHkVs8s1AOn0nQC5wMj5Oc6SHEU5ALnReeg4RK5nZyKBhQHyJ0nd/lRPfV0+wfTji4fXscc7QL6Hu0UrEHwkIOHxA0jPCc07dLTl2DhdcpWRQTUSDnHy6ZxgeIZRUndigVk4uqgq8zDEQ1MBf9AtG2j/74f143IqKuh041xYZFaInxPt4vaTT26k06S4+l61XoVRBRXuetLP0Gg8yrtATrONZLqw3innDQ29dD2Avh371oraoxRvbKOJi4uEKctGvJ0o7Q2P3uxpoby2nlvwwcIpfc3DZ+qMcWcLuJ3BHjnjDPiMTDVUR7jXQutVmW9hqlEVObqrEHKArukBMkbkKltNACCRcIuN4DiT4GrcRsdNXLlwfAirxFmz+1brM5jJNkAICD5GiCUGPm+89R1o1Mg2w3aVAAmNBauO+goURjn1ozFrLt3qohbTsgdrXkR7S4Xyk+Rj4fgECIzCRHMtkWyz80m02+1W/cl9+57EJtQVpenpYlIyxLYNMwAXSqdCg/wowzKorMdnGboK5MYo9bx01Ny3R509NOfll0/YZYhev24qeJM6mOrxhkLtl5ToJAtVS8swInPVVgvl1ZEgZGyih4+uVTc1wkmjQmNLgyOqlVrxgQNERD/HYeS6gCCkhpAZ7YNkxnSZgMzJCQkhNr6B4+qGliyrDpmKUB+A3Gkf6jx4OAHSEiRAvfXnSvY3YiQ4mAJnCH77Dbvm84EO6plFPs9AhTn587hP3KC8Y0fmYou7oQXNCiIeGWrbl/oKoqBgQ1UTAQS0swIZCAFob29vG0CtpHxCfXvfpZdmssl8dHCGhMhMYiybDoXV3yOv5QYnYHP2AC0mEt/Q2HsvvTT379kD4UA8NLK+zGx2Pk9HiD0O8yYqSLBbBpFHp2idIHUpyoyFrNcaDhCELEhwIwCaAiSAPur4xezOJiE+DpCctvXulOKOVDGuWzC1lp3JoSuSLh5BMUTOODCBIHpg7Ern4MOlnmO5NW4FImbhNLIDINWv+woyPqIjSG9JO35Tm/SGKzBALDyz8DE6gsOsjl4KxhRtTmQy6rECAUhzhpTQuojMHpDetHwP0iKNrwGo1UJBe7IIktD2TroOn419+6Yz1WzM4xRkjvG4WCoZ8vgO9McnbKI+MBiNrcW9wRn97D//HcBqLE7xWz+jVlifQgVBO0s0SYrzRkm33iRtUs32wkeI1qcK6+tbhdWR5eVxScg6SONB6gfYa6RVRkGYti4gq8mVUZSNy1FNMA35gIju9DyEbCNkDs3NL3IBh8chfvVNLz0It7O1cUeK2Jq6wmb7501pNXSb2G6MSgEhsXmDhzD5DdOeMdBBYMEhHB9qolBRnMMHpnVSHJlT29RluCEXrsqjMhzzN2rEhRmmXotPLMTQDoAQkGLjyY16Yg1AG09eCqB6NqZM9uUJoQoJLq4uLJ3TgeHhgdHIKI28WDo5SUsiPAD3ubmVcLKY2d/pzHc6W41qqSBEGlsMRQf5P6S9YH5+3YLlpFZenSLhCVAwz6E9ETI+RqdQKLMAbS3d25Hvc4TuJSeyGIHMEXreeneXCRASssk4BGRB1oKNyUhgFM4gqADXTW2ycE483OTFg23Sl5unmQSbt7ZZXgDJZdsm6A34ECagu/QOOspubjPaO6ftADGaSGtO427AueMc1XYef/wKti6TdjAHU0x68vGgIvm3nAQ0SnhRZkjiYcxba6+daO+14NNCOfXkQkuA9k1vbJTS7IByjz12aD6dqjB4GOWODEiRAZrg0VAwG41Vq8noYG6M/lJipZ559sfP99fu3d/JlKroL8V4Siga8xbggzXTqA4lHizCFBQ0Y7/aKKxO5UfywSAaAhB8Rlh8Go2CDEIDAZHRrLzKu817URSuwfpC/altAQKPL6GDzC67PraZNalGYNwbuHXRaaUyOvYN8CG1iY+OBBOumypAH/gpzvigINExPKw8eDez1jeqnWCLT58PBkGnGBAQjBAPAZ9KIrZrYwdqsGmvIzAQUsMnBx7x0RLk7VZSyZVmM9EGDDKCE5Dq2612eqG+cd7G9EamntIJodHIMaGRx5OcG9bhx/HxwSFlyTiwQtFCdfq+6WpqIXBUIDzUbG5c+urXT/140dZFn0/TXBChIH6fvnc8aiM9BSbj5+e5VCfIB8eDq3wGQJzLW5aCgnq72miAiOHuMh5BOFAPlECFkiwkIRF6EA3db4COk4SOcGdUNUHfW4MIM3OGh+g/WG0O4C8jOI99tPIbM7+g4e5WEh6l1GN9bucStAa98Y4AEQDSsw+IZqlLb1i3WRHinr1YBxnk20hz8DFAqp1FGTzQeQJmdhwggo4nXVQioLCh60XGBBILa20Ek0i0QLQHpXq7VYymS/WSamceo1aPaSaX+VQKp+mYd8x4aHJ0JodpDoVOixS/2HfnF3dO15PxIRzCRPPJO1/95esPr63d/fKd9+2bnqY6mgzmY14qGk2l0iwthQJw5qnh5LHSwXEcdS+CIRXg9E72bauxBaWPANTpcFzoBaoPjpED1CVEmmPDesCpLEIqJphJwMaZgnyngGTEh5urG76p1ke3EB1/MKe12AJBSSkOTu6wCcdN3TS8kxB4BMhlNncpzB2YfpCP1h8uPWZnQUWigxASEh4JKBbTFkgntsXHdX60AA0N0MsenUA+gjQYpxFXyTKcu7ACoPbPAGqbhhIUfBbUH0jHWGW+JK2xF4ouVrLVarZiwyA470hoMuwFd1NPXfLjq0/dOZ1ZY2YkEJ6oAwhC+5/98eU7ATSdyaSTiwuxmJfNFrLcDfY/SIbkll+mnqOmgnZCU2CRnpYBBMWG/PVWo8GetqPiG2Se1tgVixGMTEHshUQIM/fg2yhIgGg5CJB7dSfc/h/mr513MFtg/8v4HHz4QXZcGEIHaSVyHgFAluJu+vQBOgo6TAKhN7p8uMDj4sY+H/C4BGf3rMSk8TcIsQSJDw57kZE38WGP6vhAB0R05Bga4ci1LyCq1KkiVbemdj5/7LV/FqJWeqVdL3qLxVJ6Z9CLDnpM6BxK1xuYpfu2+M2G+Ks9lV06OMFKLBW774tXn3ji5TsvTbEM5cKR5L6nfvnpl6+fWnrqqR8vvdQ0lGalihczpVK12miYPxANcECaE6ohnAOApvJaooDFWwhh3zbBtElDVbM8ukDEDSsFtLrjwOS5A44UIcLN/3ZL2ubluozO9/c9yAj99P4yjyARyO7YgwGkY3UISPpxgG7tr0EPPOwAMUj1llls6DBt0APkDNwjtgCZv9ZLlOBD/e3EDeU4+CSkn6hOLKAf+FgM62GzVGFGQdw4lTfJmVLy1oXFdKLZrm+zCP0MIRk6/EJyIs2vPA6fxStSFRGKPZ7O3LdFD2NkPIofKxQww5V8Nr/xxFdfXf3yK8XsYmAoEI0Vv/jlp59++XDzhlefetkAZZJryUgwo3elBtuepVrBFLQsUyD44yEATckmjCAkPAL+AULlVdJhmV2qzjdAhhd9iqeREXz8aWAQcdz4P6rONbbBMYrjPogIxlxns7lsagydljHESumrKDZaa8biLlO3rBhVl8owQabqMkrodOWDuERYZialGtMlLh/cilgiSJCIGCFE4vc/z9yOvm0xPuznf855zjnPASCdhATIzqr2a7fszNk/jm3zHT6YjWcZnm3kEgHEDhjgiI+T0KaCbJgHBVmfjsKOTIg0J3qOXtYvFSH6PRyAxMduOR500kPURk/GGFBEQ1q54z/T9DMr/yYHR+hxfHSfpF/mlhupRMZil7MY1I7HH/WPPvrio/5HP/9ciKamniCeZ+iu9i7vRSKh2ENfyXdWKk3ymyZnG9ijrVooVNvbqYDODJfz8yvjC4+GvJ4ddvNlJiJI6NRn728UKsm6CCWmvI5MIhGK5EKRmTQBRf+WdiVtAmQeE7/m+Mj4jg8cEcX2GduABYQ3FXGwCy4AkAxSbqReKhIgJyEAKU8w+RgC82e82WN5tf0lY6Nc4u/NJLpv7wQk05IXAFk5+3FikPio60MMehVA8HGAACM+biCRkUQAUbY+wRREfYcEDkIGSKUeyxPODODiekZ7W+Djrm0DyDSk2wqYCp5dGgTuHfD57h08ayJMN+1RX8+jH079Ig197h+dCviozqnK3d9C05tmDrpR2oURhoJB3NDc6lqlQaQf9sJecnVlJRnPhgJb7dcbGPPKlc9+/n66WoAQYSgSTAVm40wjYJFi7rEGM1czN7S3addSRwfHHxXh5N3k6OAiQjzXMTo8MkO0MT633fLGtOx9CKlZIQeH/a2hLfY2QvBpVZ7tLqHopegiQIbGGWREj86RiUcf6tQpAO3SChvd68YECBfnsjh1GwCEgKyRrU6pCLleD4/4YDerJef4gEdw9sTclW0DpPNQOGyA8HACtJvEY3ncbnZbAbdH9ZQpbTqdXL+eGFRXLxiWXxvlJGSApjamfFNcKz798NmW/oGMPxhOJdJpGoDDr+PZJiaCpVqjlk4tfraar53VdsiwN1gqJFcWssl6pOvYvXwB31Jz7bOf1xuVQiFJohD3Uhkl3CpLRHK1Wg1CKGjzWHqVIlGnzqd98OFw5DSEw9v6kBE15+Tg7uchWeMG1/tCBBhMfP6vIAHiBZ9/CG3xHxMjG6mHm7S15RaKVfpZ+7CVFc5sMBszQN8IkCREW/t5WgtuEsQUBB8zC0GyC22sV7mb1XhgpBKp7pzKzr+rZIAoIyhHECEBAo4AEYGYJAXa6Vudvh3X3VjAN3GVb5DE7IZ7zwyEHw37R/3+D0nk/Bsby8uzs2iNH9XhJRyPlBq5UuqGCapqAxNeqVYoFxrV5upn65VamsGO9CG5ej2aXEnG9uIUG7zXaxQWP/usEltJJpPZUDjC4dUElEjkasffX2tUizPXwYfhYClINVVTEWdYZwpEW+/Ud9bIjPrbBsOGRED0yPT7ENpEZIDMttgeCQmQTfdIQiYQB8it9nOM9JdoRoid3vlJQ4Sja/3Xx6EhRaFTbPWbGkKSkBRE31RX5NwolQSkWV4eMwN0s7ra4JGIEA1xR4T0nC9QBkg7EyGkJAEPByCDI/kAiGMQv/b+/tkpKmT3Dk5MpPcvlYbGMgHITJFt//LE537o/Nb163b9Ldvt1j/qDz4av+uuRiMXKg1yiaVnor1YLgCo1igU1r9YLVTSGqBui8RC4yvj2a3CE50ZDpi1QmV9NbSWrJez8WgwSM2VgR4gzWWPv78sQFfBo02OjVxbdARJJVIZ74f0UfbGxVlhlK0jnEepF4DoEXm5fxDpuQBGcnGY+LAdcXOjEuZSNLjAyNVE9WciY5CMjREiArVu2drq8mxsc3mifJwtPpCEEJAbRlRrTiLiIpaKB4bIeTheRCGCEKbc2jk2TB+qyOlO8FA4qJP7skmIljYtOR7n3rRn7PTTtyIGdY1STPXtnxocKkF2sKeHQZ4nMrM/PPH5Dz9s/Pbbr6fzD+xIR2Iq4F0duSvWKOVK3lkkCmfekFoCUK1QKxeaydX11bVmWleyBzz/wvhKvSN2dXhguPFmqdwsVGorK/XY1aG7pqLZbCjhhUOxbKxWO/74WnWmraONrLpt2E6nEpKLQ+14OUEa7ttjp6vwedeNaJr0MToLAmSIIGSRyCR0yfFzx1/yN6C9zccdsO2urdvyO9fLSchpyDW0FX7skXiMEzCRjxI48LAVTtsRHCHbLYaANvNsABGD/hmnEqBzwKJ53nPOgRFfDdKhh2mqal8Qnazs+iGD5EQEIfgoz6aQkJnt7e13i5F4beYHKEicentHRzs7xzKdQ2hu/4NOPrE0MZAJEIZmZ3/ww+dXhrB2IGht1X3sWDiOgCKpNDbkealSgyDSbJYL5fLaYqGQX12vNBqUBtq6A7GF8Vg8mQsd8XqaMFMr13PRxXIkEQvAJ5uLB7xYvV4HUK1RHLZ82h5dUwER2ZzjZQfWPs1la2pEo9kAgo9GeEQIL/fs9DQSMh93iSRkgCQhA9S67f+dnCu18RgfJynTjzUmyLD5WczgGCYr+MDH1iMxt/iNxuadgjSNaITgYx7Owo4JyL7xAhBHITGiyEP2xl4XQ8SDkpDQ0L1+fyYzmlnuBBBwTjt8O/CIT5ch4hM+y8vcux4byECoBKGQN5aZomE39dtvGxu//umGGWkoUM0LePGrvWAqXcIaDeDg32AEoMW1tQKeLH/i4v3pYcJTPJZ9NLkyTqH0kFKz0WjM5YLlWDwWTMCFSORFotlyudlszqXbKOVYwsZDdk2bzsrcjg76Uf7dJzNCbzKMLUCYEVKy8P4FoLlEBiWnIANkPg5CrXJdDo817gAjNFKNxMML23wjNXCE2FDKd9TDAyAmS62j6q4FISHuLFzz1WtGyC2BBQu1AyTEF961pveYw4hCwqM0AR+HgnQQAs9B3KhTvUdreyUg5+EUfRyfLtnfeGhv+/w+NDThVUupwVKUdQbLs1OZqY1ff/3zJ80CX7zPezuQ7GW6OjwuaqdKR5aOrJXLhUqlkqdCUKuVa+VFrFmuVAqfUTZ4vSUzEI5ksp+urjTLb17XINDk5iJeYiERVKYQTZAkxMp1ha/iEUeoD4R5vOPpMGCo0A0xKgx98ne0VXFyjI08SB7HGNytNBUkIkcIQGY3idA/gFwUshW9dL7lvKyubUkbpm8ut3O5m0UqIg/S0Q5mF4KO0po46YdEgRgkDycBkSVokcvfE/HcBlYGJz4s55Nn0yFVfBSE8HDQUaINFwSEKRDBR4t3lGaTIyz3dpFl63gqQuJCCQHrnR3VdVHdIQ5mOl4/E2l4ZwbDw4FM1+zyb7/9iXi+wy6+WDcauwc6uoP+YGr/UoP/9iv5tfXV/Gq+gnxQ0RoKKteajeZn66uFlNoQgdFs8tP5fH6xka7V3mQMricS8y/Uk/VoJFaPCRASKo2MFIvMZVMIHfF4qRU0DJV2DD7qrYJLSR2TPXQjmEB4TDdO7L6JopC83PuQMQPQJZsuzhhdeQD1OCDpvIpe9JiS7IMXUkI+slYQYlodS9zR9stdLU0wD9cKIfFxAvpAHbt/kgQ3cn0ZdNCNKjtwgRDp21NPsejajV4zI6IW99F0uS3HJv4Q67XYZf84xTgNVvf2A8gOqQBS3OkFkkaroAORYHgodfS9PR2DN6RTQbU9l/v7TT4MiGoYfp/3uOvT3dHRmSHNO+jkQgHtzK9/Nj+/Oj+fb6IhUum1PJ+l2vr6Z6shL7B17179idjCp/Pr6+/flm40UsGML9hTT1JOiGSlolgzWa6R4hWLS0s0elSrLqIgVx0dbvcsP7CKNtpBPYcgrrPcBSHyOHefwVI5Uu3Fm26CEW96FyBMPu7KA0Ro21Z8nDuxiorla9aTgw5m2rE5VPBo5TLvvMEHOw5EvMBjgLjXLQGRIwiQ/NtX8MHD2e7RC2lr3wmYm2++mfRae66dgk6AEDyux0ACJd3Wsv0hWi7G3XoKPeJj1xjcwgPzceIzxYTIE0GqB/Gr7woN9qhMMKgDTi9XT96Tfj7aBHR6iy5fTWRCJ548WUA4+fz8ZxTZ+P3PVyrNcqMJtEKzXCo18+s/Mr3gde64XeDqWHKVHyq8eV013dHp6/ayC+inXkku1GPZcrI+MwKgamNpaamofoIBsuL1SPvMiPqvKm+zI4GhRZKFq+iPa84KQjexsAdELtdeXHzjP4BUSRAfTIwsDBHsEZFpRID0bp/24q+3ChBEJB3+wGAjAR13nBXjjttZeK6w7W+KQF9KQJQR7A+SOJaFMHWAgKi7gegwCQjlQAhAdlaltgMX1+nWm5ZTMC5SSiQofVoAwrVJPzZA2i+zKz9T6m+jn3h8iONj6szOjEptPTQguAtp3o1BXt6f/On0WW45BIa8QULPp7BZz+epgs4DiG8VMu1Cgah005EkdvOr0fHknLffDi3BVC65+vOp0/en272ejpZej+pBPJYcr6imUK6nRqqNdHGusVTlD5RUnPGsuQAsD0jk133yeHJ20GrX8lMGrUD02PE34VK1rJkYNL140yJkzISJSoIU5PgYoS1BhGhESC+MrzzIiQ/IbKvWHmEHkiaeTUJHtR61s5wbL9VJ3daXry+99IOXvwSQOwNhyuGOYacYEYhgw7pEqNxJ5DFAAiVEbhpBXk65m5bxsM6Kxnd8yACd7sYQrBZHCUHXTfFwpAc2gBCOp8y4vtrdReO7h0kQUgPACJBm3A7/tWvUH4978cgRDfm2n2GTz0tB86BCT03S7HKDGERCUE5GKcZFWPq3VzARTa6rxDDc4mW28/lGgoEICXiyrLqpN1OdmyMHX6oCSH/g5nBsxSW+j3h8hY0pivikYjloWDOLnVusHn/TohBh03Jwlh7MSUMoCED/EtrWwhC/bRvVNj76sMeotW4rLwgj8CA2sTE4em17lM0j3H47gLS76uvH7/4aBX3wwZffvkT8ER9Grx/mkMqK3oOpXFvPBzL4NQwpQUgmQicd5AbhNEfKWjgQ+eimkiNkFH/QjvUYrMZDjt0lPst4OGttx69OMTk6FPR19HbRZuviPomGq/+2907/jVwvMEQJOhSL59/Gt/388/p8fv7nzxSEKmskcxxSC6Td1RqpQuno5MpK3B8e7trKK1Wb09OL5Y6O0S6vY6B3yovWCUQ0jFITI9U5zlC1RikCEa5C5gSIrmkVYDOuiDB8VTvjCEhLkGxcnisq9jNzaGXxpvdxb2+YenRQFSCnIDMBMgPQrtKQekN2FJW542gr3o3DEh6OSKWXPBzCOeqoo9DSUa3En1N2f+X2K5x7s0v2D3z55ZfEIEsOMHcGOkZsBOghEXJt7hP2BZAlCocRkTiqnqFqgo5A15/pV0NA7R3b/qblLnbXze100SlV+ZsSbADBh6EdADGcpdnT7TTXyE0FoUE88m+/LjMnFz46hEVzifm3BQg4eDrEU0nqTU6PY82bOc5GQ+ncQjIQj0WCXS3B4puNmy4JFoPbjcXb9jq2JxWKZiGUjbdRZb1ksYLuSiHT0BxYRvS7z+WqS97wER5nIGZIinJ/QCKV0IgwMwwa8YmEcpQNzCgguK8Awv5VkOUJ0hB7zoVAVEBkKtIXVdxgIwnx9/UFPkACzJagAtHORx133O6noJ8rXvjmm/O+lnou/fLLb7/89tuX7vhK6rmM/xsGbwASG7W16fucRPENw7EdJhMgExJeT3dNBImMAQUxrKh5XzqpLK+yCyLGSIUEAZoFERKy2zr0TZlG4FJJl27na1hUN0x1mYtM7k+uO9C0ezGajRFDQuHkvAChodV1Obrx2GQFd/fzu+ucgnR6bQxV05HoVDyZjXgD3WOls88emUl1BnwaDe8NMJ/KWTXBNchcYU2OsQYgkOTmqkUy7SXw5CxbUE/ourOXhAuAczYgcraGhEeO8EYSIvSPmXh4ySyLc4Qw8DgRcV51NQWnIqOFfFqNj4wPHpMR4jnqKJORi0GvvMLy3q+//vryDxgqJT/4lhzuK7sv50bhVObZ907IuK4CmFTAhhDy4SVziNyFIO34ZzcPA9m+MdVJyaoPt31UAoSRJdjF+lnTkB8+guNXS4HpUCZLuPpjm2Qpf//0569//vnrxg9dUx/e90c2uRAL0akOZ8fnP/75E0kI2RB/ksmFPMg+y68WCEO1FP99pzr9iQWK16nXuyfSbR3tXcGlQMtWHVPdneFEKBojmqUisYoBovKKaODDzM8I3wwUCYLKo0X4YKjFGnO6bpwmOAX0j4dApxzd/qaj838Fmf3t4iwOGRgFI9OQc3BEKH4ADfGGeMzPCY4EdBzpGwJ65QUEhH5cEY4qHPp5TvKxQQQpiCwO8UBILQV9qskgH0eeYIjgw0uVuUkjJCEdWaLSQ4awAxkzCtLeEHNxpqAWl2YrCvn89HsyyxmuCncxdDCgvQncj+znDPvbn79SKv1t4/MnlgMvZjm+nJGNlq4OBk+eNCDrAFrNz1fylcnyOInDKk2FZvPkQdoJ4b3IBlR1K7YPtHS3dXWOHNHRQ6moYy8IJfwevaBY3QDV5kJYFdmkPG+Gz+qMxyEIQO0zxSrykFIgcL/seBA9CCGPQiD1cOJTEd/4H0KLtFmnAfR/RCgDfQBIZkLZpVUmv2aARMg0hHDMJCMgHXfR7RyArv3mm2/IDkjfNgE9TQDSuoNnbOGOKegwbYY9mF3kOos+dBATPCByZyHKPpgBMhlNio/aqubpfKPsEOEKHEFFhNRucALirCpIYEFG/nvDujrXSQ73+kDvXi29DE7j1Z74AfVsAOiXDzPh+xYmxycXFrJHpoKJEyYLlTy2ThDij9UKlofXGc18oTYYvSvU2TP1IkMi0WyuesNb3N1qb+vuOaK9p7Oth+nggUAiS0WuXnlEgFBQSGkCJ4K4ghHygY8qqEW5u5yLMK7BfQFTImeT4RmgmcSMxabHzMk5PtOaADZA/8vkrgSAYFAesHxa4tl1yy13tczNCALHwg/qwaQes9svuv12BAQgreFxm0O4f4p+7B4jV7LOufEyW/kGIJQDIOyhPfnqCKEf13YQIMODhBSJ3OTI/oMB7U/kEqkuaklCVopzRYQMq8gCQYoIceZG7yJPCPboJjf7KvfqznT2ZJ64774nNn774fONjR9++f3DZQjR5JlMNhulYCFfOKHEZZ5SWZzm89BZXys3V/fkT6nihEKjQX84Ho1lo7nqVSPd2+3Rt0dnB0lZR2fHwGlb9Xi5ZHITULOWy4XUHcJ3ejisotRjlbgR/B2CcgFmcZFxHVe3rs4k4u6WOPohpzCEDpDGf6lv/09BVzIkd+Uu25InSEEKP/JikOFx4Qd2mPhIQRKPPsgPLrroIgC98or4fL25uwr/xhHIim9uWQgdVFsqtq98HIBU08HPnXii/JwybZnl23YkmixMOgVRNVXJZ8ivBbGakjc+O5p2ZhHNmF/pgSVwjIxyh+G++16M+zspnvoCmc6Wnsy9oaufePHDqR82ROjz36MfZuLRyZWV8fHxcmz/lJJqZnpp7w3uf6QKPyQO+fJBQwDas1nPBuLhSILyxF3ZOdLlrbbuu2crWqZ9gbatdT2lJ0WWV683FxWCsgKEcQ6Dzwx81LtT+VT+S7966ExPc+bBQHT8UhGcAOWPJazqPOAmoEd0et3inf8J6IDtd3XnIaVsepxU7NtmBuf+IlTs4f04+JyyO3gsApHBWfx5QAKynRQ3nqPJeKo7MHIzCIeeZDm2W7qzpzZXQYg8AUJgNEQCtJklnCgBnahEYYwcmxQBPgaIQqkKPD5GC3R3IcJs/H2yaCx619FXPzq2PDZG0jDFVQdf6C4uofzwOT07vNwvf/xxtf/R7CSAINQYyuYLa6cy70YN4U0GexKlk1c5GI2HQmcUDsons6ORODMH8WCAEYfiHu19B/b17dSpsvQeOxzOhi1P5bhycvGRtQI05xgJxmMVxYfkoK1juW94ZEYNiCXnuuCjXzyAaM6RVOdiuWgURogHjYmPCMHHVjA6QI6P09BfdZ1rbKRjFMd9anwgbokyqo3WqEtNzbC0Gh3KTMfkdensjA4xErfVzAcyJmnU0FRcVibSmw+LRmr0rWtIDBbNJFhlZje1EtTYUbuuiWsVbdBi/f7P27GWOJ13OsW67M//nPOcc57nuRE8yuOcNNopzgGIzMGkbVVEjnAMJ8rYRB+82908r5gU+30MRhCSgG5jON5MxhNiaACpOqq5A831dlDDobfN/kbyBBQEIPOCkPhIPJhpCD3CzrreY02K4NiR7NgxIYfqG8rJZwFjtShWhFokonSbt8sb41cnm469IjuWZxvKzysgIgytrNkht5dEbjoxXUhZneFtpAk53FtxlPhDrnyZb8SXTIe2t3XE41kWtHYmpCHF5toDH7r5wA21PT19jIcjn4MOhlN0iLWQAE1PaVI4BuIApZ4eUgO2ELvUBmI5ioDul3tTVUd9n9fl5agb8Ev5uxPgqB7wUoqtKIUXdDpFB7y9PyGqptU4IyKCpAQOLwebG290tKSVj6MgfTvvvLMhBCIUhIsjxTaEPnuRJdDjbA9muveGa7X+FCGjkMcuABANhQ4ZcahlHRCEqmYqp+YsefBk+eqgzkMj9RDxcU6rQEAzXrk28Ixl+W+k8G9ZoNEtAdpeciIt0/HpVLinqc+KWFZ+cmbx54WFX8kVxuJ2yOvNjEZ2JQqFVHLjLuoJyuFyoyyDcHLF4vjUI0z7NPW5Z5oDUX4Pbdvvu8JVd+BltcGgyxWMXio+jcdotLsvZsXjm9TkA9CWQOBm9PByz9Hg0eSICUEISsFFDm6Hos9WzV1THV16sLijUCCEZYBiFj9iCDYdSm9O9wPQvwlBARbAWX8knWoIuhE4AoPb4/088PCcRwQSG2TkJNlI6LPPeGmN+gDHxTsn8QmQGkE61pLMGv2Ymd5b1FcgCjl53KBybUnJpAgREaLcY6zTN3Bs65lEIJl2Yl/SzJRbNBpNSzuycAbfxhaTcES/1GpvzwYCYRafQ5e5w4lKIb62uLqyuLJAIjcf4n/bqMsanY7Y8XH/FdspyanSg2NLICLaQ6NUtXdtO3F8NtnqSSfiIIp5g273aTpQtsfl8rprSA8PP/yUgxqOcR3tz1B13bp1nBBEoYAZn4foLWD0upmI27xOR45LFR0MH6dbBncvYcUdcYwkvWpMZAujEZAAYYbPPkInCZIyhZMQjglHPMIGIAwkR5yHZzN0CECY4+MwhSABwqjBfa016uXio7HR4x8bJIMjkVbRGkBmFoRTdwDU0nKhlkJQkQHK8Dk+ZVycOHacOMG0u1IEbTbRm/Tj7vOls4H2FgtA3d3IrB3BRS5IyLjBLjLb4u+O5Ea33XxZ2I4jlcn0yurCAsncSj7EyjP5mj9BeyETSHaNU8rGxwkQM28JqMbDkan6R8anev1pf95vJ/JdAbev6+btr2rMvrexpm7GxZnM7KA8qPFAH2Fo6/TWXayASAYCPWR4ZiOr5uF6Yut45LoYAyEIae/ctx9++OG3O3fv3r1UlIiKS0sPynbw4pE9A579FSRCCkIkc9V6jl6OdgQI9agoih3lCEd49GYUJPnIw0EIOrxURGDS6vIrQXKv7Hgtc8ypIUjoQvDQVOiEkFpy3adeCKB9BkQyhDBGsScLH8YPj3W6DKx+LmZ9emyd22cGp7L1Y6E0DOtRToLkGeM3OBVuIUu3eK9EkrW5qbidSORDKwurKwShxdWxPKJI9vmnp+NWINTZOb6jWBwlbUulElThpqdtu3sq0rFrfHYmlLXlEPtj7mCMVt6u7UzxzFxSM9Nf6xqoaziXfcv3HRgcmh3fNT70yKuvJll5wgZj6pfUgPWpox6FmG33377jwT0XIZ6rPuTured37twpQtju3TslJxmo7sB0UoJMgKqE/hGGYPQfQztVk2oMHB69KT+AD4aCACTDx71wD0We225gzzZ48GEKLet9BQ621MUl2j+HPK7WYihygVJrXqenTtd3SqYyYhBCm6RV1ypAlG0uVp+70TkNaaPHOzLSmQ1FUiqiVWQJKPEesRLTCd6zkchDJxf3ZFJ2fG1+bXVxNb+Ik1sdG8Ox+JvStp3tCA9dlhynicpCKBKeomgAvFmqddHxXb5APmPz13i7fmkKbtpxx4M7qMq5+ryu5pl+18AGDvkhHjYc2OPfNhRgBdRDW+FkJeGq7QiPKR3wmAiE91oyeMQHQCLkcNm583lYyZZ4aN/JqoD+E4VECJOMZHw62+BBPLKqW3MgGUTXCZDwoCBMcHh9ZlycA0jtOao5j1X1waWAdEo1TNWpzml7S/eF4cgF3H6qKzZT1VqpHBwTPWmGQzkGW426nyShc+kiNDYrBPm60vWWlYLJIGhyldxgocBPZBmjkVCigiK629Mdyb5d5ZISiHkbQisLuLlF8j40FXAn/bGQ+gWB6XErHKZ9nfGH/DDiWz42vr0/lOZz3hudmUkOWRFKovGsl2lftoj3NuuYLJ3jfEKN6yytTX1Hg+ZSbrvzHk0NFDxam8q2zTp0+N3nfluMW+ukIMww2fk85vzI8y1w2D/MuwB9J0D713twchAi7AhRlZOCj2Og4Ut8ZM4iVeEHczIEx8MpSVCdh7vn8HB0T7XOeQzXpRNEiEFaBF0tBWkL9yPtjMpHsG4SBvOpWsxW/7tTfJp/OdOcLMbZb+pm64D5aGiMKx/tSOqMwYoAVQghmGCl7EhotFQZLebCj7jr+raXPyplOCXBXltbWVxcWWHMdHIsT7P05bqm2NRsZii5cdsj28OPtIT8WpT4uVwtHfCGZoOuCfYbp/s53CdqT9NW27Ut2uX3I7HZoWQtpznfd9hxHLp0XA3/Sm5Xk5RzsgoHSUo8qo6Kz5S6PTuKclwXwUeAdNYl34CyGyQYhKomfFxtx/nz5kCyA777L6EbAeQgMrUd4o9J3G7c596oGpx3N2CERyZAxr8ZQFIPlCBEDHryyqc5atQAIjcjQZB+4EPoN3MHup1JhFouZEEaCbd30gUSIZlyOPm3CennD/gASKdTBLOh+myyrS3qt0ItLRnihvAMJlJYojCoHxLhMStXKuV4Tm+/763tET7Y8/35tbVVCC2srK6tePL5fCKebDjZmqX30NZ36fiUPwscDrhknjHq9dLvafZwuk+0d6Z1OGqx2qEVrvq1TT6fYZgnyA7vGu6y4djMM/G6MzM4PdelPcwkROlOxVh7opxxUmsC3JLxW/CRenQFzVeYRCM4evgJ+8rYEyL4kjkvDkD/JQQjNYaqyQI5Hf5NbIx+HED6Zj7DSTUe8FQXqWoDvf/n+3/++Rmnx1NHIEegknPv6c760yQAF3KpGeMGAKKVDR9uOkM4WPtIfbe2NUAHPMoPdE+gxrEdPpSkeyezFiiz0XkybM3NhcN2YhA8EUNKlsvlEqFsrgwhXmfcdPFN7Qmg2cda80hoYVWE7DWfN+S3/YFa1xBTH35XX2jK8oOHukvIn45GPZ7N3kuu6Ir5NG7vpTeXsTLJ5JRFPlggS2dF7I96Nhx88XHP6Zh6dZp+aW3ecPJZmrry+SajojMtOlvJwXfABzxVPp888cknn4iPgs/zQiMu5p0/Ya49eeoJc4mDAP1XQQBSXQ4TH96q4uFlgGBoSL4O2wfI8Pma/O2zP8HzpxREg44QpI6COj7HY1IQOQKeS2cftBGFkkShegmHvkKTs69Bzk3ujVuc3M4tGWeCR3yG+yfGbKTSnaW8Y1kqA4UzKalHeHLA4SmVS5FsolQu52Rth293RUp7UpbHM7aiPGF1YXFhLW7n+2PRGPM4GwKZqU0ut88vo3QZCPljnVF3wD/gqvUNtDL87SHs+P1psrXZRMGxBIlEJnCWq/HcE3Qk1jt//PEHR2/WnWxyNz/iwa9JOls30YooGjw7L4IP4uB26CogqQZAVULi88OPH3MNJIR0zQaAZPvnCTLhQUbO6kdwZMatOSFHhDAnHCmJM3wECDx8/YkBSKXSczTCYwDJwUGIGfkL6+s1tjPSuW4MWEEIy7a2YyyNmEhAPn/fckbDDTzw+ZljQ0I2iQCE0ukQztBIj8yiAB4EI78GmXKifjRRmstVzih91HfrR/wvkKhM5wNpz8ICgFZXFhbX4nHL09sVik89dEzUivqDvoA/5rfSsWjW8mc5bGkosCEYCw60/nImFQtqSgNRgs90Yd0qjJoSiWJHH37fIY9e/807f/xKw/aQgxpdlwYf2mKmDGTx2U3jRbID+TeiDResiw8SWhdQlU8Vkfh8/u67770LIQBJQb/tIwQYnn1xSIxQEOYAEh5MgMAj0x/gAdC+BOEF7GsIoSEUBCDmrFCQPJzowEjthnaNg7SBxgxW4eSy2fZ6XNqx9eLDKBycqI+inzrxkeFDWMJ4OBVJziwRCaXT7XwKh+vHshGpR3wcK8/NjXbj50qVRGqufPIbyxecc0FnOmLn4xkP1VIArawsjNnxaNDrjc5OJRv6NviH2oIhKxYKZNnTiFq6YlbUHQt4uaTDy3bhiX7mhQhAoRDxx5bFeYhDtQc2NrZyiDoKeueb6w87+LVLVXgjfRsaCiSTAVDBSiU41qMkCsbPPfztzm/J3pZYn5q8Wukdqdwn8AGQbvR+Fw0RhQD0mwD9N9de50P40VvVvYmI8WgQEiNHUI6LEx9n9aOrT//8+k9CkQNIMUhTIVLQ+mBVi7PtFEIIyfnAuvWWjvYTG9ulHOUNziV0TWYSTrMh4IEPB1Pkx9ZsAQpTBAqnEpH6LAOIBB4HDYaDA1ALnyusXeeWa7cvD+bmRju7ohOFgj3/yy8L4IHS/NhEfOrl2q5XZ2MXHxwbeqTPR1jxpaMxPB/JWowA5Gmqu8SbT094Z3B8rJOKcm7ycqr+hGJdviDYfK5huukC9MFxNYeftfll+KiZ2qNONja0PhQye7sZf9P5FKKCgUUKkpJgY+zjzx0+T5lE+z+AxIY3FESyjR2qN4Wgak8OItKLEEk9mBS0j8/7FOB0caMExOWnWqciHABJQSQIfFYOVy8+DFS1tYFBn3Tlpn5oPBHl8MdIo0dGzFWy2syg0Wt6Pz97OBaWhsIaXFQryHa0W1w8THoQGc3dUIIM0pkDEd/OqIdSKdIiQFcv53JzpVLENzyfiK8trNAUWlAmNzMZL0wPHd0UDJxb05j09wwE/bEJjy9Kc8HtCzT1Jz11wwf7opPemd5oLDQbnx4tFAtFrAImK+il1ZDRDrtYcNgA4mzgcw8/rech5LN585ZNJH04O2oIW7Zv2YaQqPJofhRIqsHJROhbrX5wf9Uk+4fPueyWO24loYcNoP+RkOqmsrN5GT7SDibB3C1Cr4AGYpgDyCkfCNAX97zwNaYQxMWNCMcA4gOfVChVIU50RoAg+eiIsT4yhqZbOhtvkXza2MjAnnsPD11R2NCY09CB9KOOz1qYPIFF0y1t6VAEEfEgIIEpVwENtpTmyoOJhJVbvqy+VJ4WvXgDfTuyuFX6dhAi056w2D0XbLq4tufgg7pi3kb1qplO9fr6PcFWt7uh9dgu1DPs6YLPVNxEIFNOykz0+/IhVGQFrCn2osz8irx/ffS54y4+5tKentOO3qw9LJu27GJ5SmfumvX2qEkSPtRKyBQOTHYt1Sg9wLfJ5N/exBwn9/BLAPobkcHDa79cTnwEyJi6PnpnzYOIDBsIwUyAxEcpHF3UL754XICMgM55zJzoT0cbUMa/dRP/ybI39rqBsHEEUuBgbGrjyBVtI41w0qmxboy1qIeDEXRmXu/AFW7Nvc07LTkQ2alwvYoQVndnVhlcRViMOYjOyJbKlVSuNGofP9Ly0d5U+aPl8mhXpn9+bRE2BtFifo04ZsU3BQ9r7O855lyXr7kpyFUzXDDd20t24KLh0+XrH57xdeVpeTOlCCM7MzbGeQtRi49mL76ZoV9gcTXva7z4kHO5fOOyzXg1/NrWBynb7N7tlAQIPcKjGtyHAFGCoKTtR7QCl08+AY25Gxo83xtC77338VNXOYD+L5MDzbop/uhL6RsOzdRFDSFeogYgox8JaB8gXR58w6BkIwXBJ5Wq8ulgYFQ3YxxstmQPDBBqmpvc9F8auM/RbKcDjsdsqQPRQFP/RrqmtLSlH4dRKJulQUdikUoNprBBFPQPQKXTs6VKpDI3lxu94Ir65b2R0vJyuZjOVUI//7LCYpUo9PMv86E1ajrJbGOw7+BTvIef23RyDRtYPF7m3npnLmk+86BmH3vzPN4o/1ilB6H8fHqyf6DX55+1IcUJgQyeFBLTdig/OTE2a511IHcLHn4pY8BDTBhs2bR1z9JugVFtx9yn9cMTAMKMfqSgT4Czbrr7vopHDxIygGTiA6F1Pno5EgKNHgcQBhoQyaNhAKquiZCUPBx41APSGdjycV+QItxAimD2nQJIBWoBAo8SgDbnaBd0AgMROhZCGxtGOq7OKr82f9rdj25g5OnqNKfw/c2HY3csm7Zce7vCUcJI6B+AcqXc6R2VSGJu7949ldM3di5/lFrGculSrhKfmF/4GTe3yJ67SQ75szO9G7qSR9fVbGi6+JS6GjbkgaB5ppW2Rp23iTnurqhswsdk+PAwTcIo/U+bzmA+PxtP2IlcsRi3xlbz8cLs5qPZMXwaW+4JQAyJDm3ZNE2YER2yM92K+t7HPzgeTYAweTgZ336syseY4+Q+fuKq/RRUJSQ+IsRXNQhJPgIkLE5hhyhk9MPj5HCSjxycAD2gI8u/2Fu+DQE5px9oQN50EJTAUeGpno6Ei9MpIgy0EWsGejc2bLylA0DkEPx5CHnQEepJUk+1wpbl4Jn04OrW+ClL+9ReBzT4D0CVSimVjkTKACpPn9qZmI6MmvnRjjKJ9wT1HuZHFlU2JWuPRYcDPm7JaK45qOHimsPOPJabA4YvGeY82UYk7vJ4GYWcmSFRaa1zMQRnuoPIiSUT+/MrhWK5UKTGms9bdmF8S+wsbazbPHS/mqObtt11/qatD97x7Ye6Qf1zABh3tm8JhIeTAQ0j/gDICUB6vfvu50gIQPtHof0ThapVKzoSjpI4TIgcAb2Cgr4kaRMf5ki/0Hb7Z++Bz3K5dM6gdpQ4QyEXmB6200cFkMkRyNU69YM+NpGvXXHwiNZA2Sx/DamD4tDIZCdN7XoV6MJoJsSZb3lMTmdMowiWkRC2T0Klyg2lSOdYZQ5Ac6P1V6TmSqGcAGWp/iTmbVsVuV9nVPEZm/S2Nrnh4+o9sEat2uMO0Z7+w8488npOK6ubYUiVneEHXXJwr6uWM+aRMSND2VgAHgm7uPRRoVgqFEJ5f97ekytundqy+dJL2cD96vnqXdO8Zh/Y7Q9e9O0TLD+lkHcFSAqSiY90o0ffwCNDPu99iuHiWKwKkGx/DfHaP4+Ti1PKZrhUPZwThNZDkADJwcHHAOI7fPBwAsSECLNvwmP0IyIw0YOxSjWA2jayd6Ghpo/Az1oIFU1wOg98fBOmqd0dkcm1yAAkE6lsxBCySeTKEJFoSiyKrIlUoSxAlfYr5vaWRnM4uVJorixAKmr/TCQiU/BMLnpitZpM7XfVNZygG7c4J5udkscxE1K3ofkS9ha98wunLzBYjNryeaqok2maRHEybuJIIbOGwyMVTOypTNOc1R47Bqi2sKekyKjc0g6NKN550YdPfPK5+DiAKI+ashuBZ91E5M3vf//99++//12EeD5FQh8/JUD/zROqiPZLEkBhOj5V+VQVJGEZQE5mbe7cfnLvF188awA9pj1zmk7UAdjKD8Sn0yHT6SBijconfWxqPeSwDbQfrm7RJAJT8PABYJaeOPpxLGwQ5WHDu9KGfEtKhOwUKZsBBARqCtmOApnd3r17B7O+vXsTueW5SG5PBHiFfBxAKviYgsLiO72c3jN8SWsdDexzdUu0Y7oIQIcAN5upfB/qifp+8vQveqJ5SgmsVZeKTHhnrNweDiJJFPjk7+kZmppiPnv70BYATT+4Z4l+HCufO6+5/3X83FNKpMXHKEhhB9GsG7qBz40QwkD0LoAQ0ec//AUTG1RG2dsUygAAAABJRU5ErkJggg==\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal individual allergy index (IAI) card with background\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "air", + "aqi", + "pollution", + "emission", + "smog" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_leaf_wetness_card.json b/application/src/main/data/json/system/widget_types/horizontal_leaf_wetness_card.json new file mode 100644 index 0000000000..62c8faf373 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_leaf_wetness_card.json @@ -0,0 +1,32 @@ +{ + "fqn": "horizontal_leaf_wetness_card", + "name": "Horizontal leaf wetness card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAk1BMVEUAAADf39/f39/g4ODg4ODf39/////k5ORLcN3g4ODy8vLIyMhYWFjOzs5hguHo7fv5+fm7yfK7u7v09v2Opurb29utra3CwsK2trZmZmbV1dWlt+7n5+c9PT2QkJB0dHTd5PmaruzS2/d4lOWwwPBWed+Dned4k+WCgoJKSkrt7e1ti+MsLCzH0vSenp6dnZ2DnOgFpIuiAAAABnRSTlMAIEDfv1C6kOEmAAADEklEQVR42u3aaW+iQBzHcde2f8Y5GOSUm3rbc9//q9sB7Wq3DfqAbUbz+6QYHEIyXwZsEzsCAAAAAAAAAAAAAAAAAAAAAAAAAAAA+Em/rl6XMb5zrt3k7sF0OB5dPe6MR/ecboB3P5rQTZggxDIIsQ1CbIMQ2yDENheFxA4d+buMTjhkh4tCpDAvnJvNIS7jbp+3Ce1bwc2o2eXHbWDNtBNQJ1jm+tKQZvkYGvn8JETI9M3Z1VK8pET+Vki+c3ap9F/qeJeWtE2lU9WleKtN6MBy1tnPRrurDZtfFDJ12UF4EpJu30oeV2X3hsut9LdxGpeOdOKKSv4S129V7fP31Oc0sDXLp4bez45pWvwmcl/PhYSMfRtSCRHXzj6EpIxL8Z4KQR8hQjiZSONM1BUNbMH06fIQhRvK3eBMyCP7JmQnt5msK1Gmh5Bqy1+Il2nN07oNoUqmWWXutHcpfRqYy+g461c2D9xQsyn1h8zZ15ADTl/w4zD/2DgNja2eGVvo45V2dRjSmZA16ywOITYIGFvnLvt7LzUNTVf6XMiKGavD8/5IVgga8/LMlvRBu0u9zJveEGa4et9hTrVHfnJZQzPD5zWbn1uRJmd7c7JBM20+h2imlyyg57AvZMFY/rEgbs9jbgaf/h3g9F9M2aK7taZ04D5S7pqpbvo/tVxzZud45qyLMT9PEXU8j7yJagcm5BFRERfkRdH+gBkYNClwzcO+OF7WZhPQK3vVpqf398jSrEpnQychWaYiIZTZSyiJEt+bJSrLhPKflO8RqYL7fkRcdQeU8GlAet3ORlPn9EO4N4QCYp0w+BSiZlyoQu1Diiwy+2asiFQiupBJFJkQMSsEN7kJpyEFOvg6dPZvrWC9YqvNnI5iFSVqlomEtyui/CiZ8ZlQZuzJL4SiNsRrV6Tw4/ZAliT0w05DLhcVPQczEdElbAjxvL6DnC5hRYiNEGIbhNgGIbZBiG0mo/sb+JqdiN+Pxs4NfNHuOePR6OFu4ly7u/Et/VMNAAAAAAAAAAAAAAAAAAAAAAAAAAAAwA/5A5Ee9c9zue74AAAAAElFTkSuQmCC", + "description": "Displays the latest leaf wetness telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'leaf', label: 'Leaf wetness', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Leaf wetness\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:leaf\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#4B70DD\"},{\"from\":10,\"to\":50,\"color\":\"#FFA600\"},{\"from\":50,\"to\":90,\"color\":\"#F36900\"},{\"from\":90,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#4B70DD\"},{\"from\":10,\"to\":50,\"color\":\"#FFA600\"},{\"from\":50,\"to\":90,\"color\":\"#F36900\"},{\"from\":90,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal humidity card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "dew", + "leaf moisture", + "foliage dampness", + "leaf humidity", + "foliar moisture" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_leaf_wetness_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_leaf_wetness_card_with_background.json new file mode 100644 index 0000000000..9b498729f9 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_leaf_wetness_card_with_background.json @@ -0,0 +1,32 @@ +{ + "fqn": "horizontal_leaf_wetness_card_with_background", + "name": "Horizontal leaf wetness card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest leaf wetness telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'leaf', label: 'Leaf wetness', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Leaf wetness\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:leaf\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#4369DD\"},{\"from\":10,\"to\":50,\"color\":\"#F89E0D\"},{\"from\":50,\"to\":90,\"color\":\"#F77410\"},{\"from\":90,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#4369DD\"},{\"from\":10,\"to\":50,\"color\":\"#F89E0D\"},{\"from\":50,\"to\":90,\"color\":\"#F77410\"},{\"from\":90,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal humidity card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "dew", + "leaf moisture", + "foliage dampness", + "leaf humidity", + "foliar moisture" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_nitrogen_dioxide__no2__card.json b/application/src/main/data/json/system/widget_types/horizontal_nitrogen_dioxide__no2__card.json new file mode 100644 index 0000000000..6279232e93 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_nitrogen_dioxide__no2__card.json @@ -0,0 +1,36 @@ +{ + "fqn": "horizontal_nitrogen_dioxide_no2_card", + "name": "Horizontal nitrogen dioxide (NO2) card", + "deprecated": false, + "image": "tb-image:Tk8yLXZhbHVlLWNhcmQtaG9yaXpvbnRhbC5zdmc=:Tk8yLXZhbHVlLWNhcmQtaG9yaXpvbnRhbC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjU2IiB5PSI1MiIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjU1IiB4PSIuNSIgeT0iNTIuNSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjEyIiByeD0iMy41Ii8+PGcgZmlsdGVyPSJ1cmwoI2EpIj48cmVjdCB3aWR0aD0iMTg2IiBoZWlnaHQ9IjQyIiB4PSI3IiB5PSI1OSIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuNzYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iIzNGQTcxQSIgZD0iTTIzIDcwYy01LjUyIDAtMTAgNC40OC0xMCAxMHM0LjQ4IDEwIDEwIDEwIDEwLTQuNDggMTAtMTAtNC40OC0xMC0xMC0xMFptLTggMTBjMC0uNjEuMDgtMS4yMS4yMS0xLjc4TDE5Ljk5IDgzdjFjMCAxLjEuOSAyIDIgMnYxLjkzQzE4LjA2IDg3LjQzIDE1IDg0LjA3IDE1IDgwWm0xMy44OSA1LjRjLS4yNi0uODEtMS0xLjQtMS45LTEuNGgtMXYtM2MwLS41NS0uNDUtMS0xLTFoLTZ2LTJoMmMuNTUgMCAxLS40NSAxLTF2LTJoMmMxLjEgMCAyLS45IDItMnYtLjQxQzI4LjkyIDczLjc3IDMxIDc2LjY1IDMxIDgwYzAgMi4wOC0uODEgMy45OC0yLjExIDUuNFoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii44NyIgZD0iTTQ2LjkwNyA2Ny4wNDdWNzdoLTEuNzE2bC00LjQ2NC03LjEzVjc3aC0xLjcxNXYtOS45NTNoMS43MTVsNC40NzggNy4xNDN2LTcuMTQzaDEuNzAyWm0zLjg5NCAyLjU1NlY3N2gtMS42NTR2LTcuMzk3SDUwLjhabS0xLjc2NC0xLjk0YzAtLjI1MS4wODItLjQ1OS4yNDYtLjYyMy4xNjktLjE2OS40MDEtLjI1My42OTctLjI1My4yOTIgMCAuNTIyLjA4NC42OS4yNTNhLjgzMS44MzEgMCAwIDEgLjI1NC42MjIuODIzLjgyMyAwIDAgMS0uMjUzLjYxNWMtLjE2OS4xNjQtLjM5OS4yNDYtLjY5LjI0Ni0uMjk3IDAtLjUzLS4wODItLjY5OC0uMjQ2YS44MzQuODM0IDAgMCAxLS4yNDYtLjYxNVptNy4yMSAxLjk0djEuMjA0aC00LjE3di0xLjIwNGg0LjE3Wm0tMi45NjgtMS44MTFoMS42NDh2Ny4xNjRjMCAuMjI4LjAzMi40MDMuMDk2LjUyNmEuNTExLjUxMSAwIDAgMCAuMjguMjRjLjExOC4wNC4yNTcuMDYxLjQxNy4wNjEuMTE0IDAgLjIyMy0uMDA3LjMyOC0uMDJhMy4yOSAzLjI5IDAgMCAwIC4yNTMtLjA0MWwuMDA3IDEuMjU4YTQuMjA1IDQuMjA1IDAgMCAxLS40NzkuMTA5IDMuNTE0IDMuNTE0IDAgMCAxLS42MTUuMDQ4Yy0uMzc4IDAtLjcxMy0uMDY2LTEuMDA1LS4xOThhMS40OCAxLjQ4IDAgMCAxLS42ODQtLjY2NGMtLjE2NC0uMzA1LS4yNDYtLjcxLS4yNDYtMS4yMTZ2LTcuMjY3Wm02LjE0MyAzLjIyVjc3aC0xLjY0OHYtNy4zOTdoMS41NzNsLjA3NSAxLjQwOVptMi4yNjMtMS40NTYtLjAxNCAxLjUzYTQuMjkzIDQuMjkzIDAgMCAwLS42Ny0uMDU1Yy0uMjgzLjAwMS0uNTMuMDQyLS43NDUuMTI0YTEuNDA0IDEuNDA0IDAgMCAwLS41NC4zNDJjLS4xNDIuMTQ2LS4yNS4zMjQtLjMyOC41MzMtLjA3OC4yMS0uMTIzLjQ0NS0uMTM3LjcwNGwtLjM3Ni4wMjhjMC0uNDY1LjA0Ni0uODk2LjEzNy0xLjI5MmEzLjUzIDMuNTMgMCAwIDEgLjQxLTEuMDQ2Yy4xODctLjMwMS40Mi0uNTM2LjY5Ny0uNzA0LjI4My0uMTY5LjYwOC0uMjUzLjk3OC0uMjUzLjEgMCAuMjA3LjAwOS4zMjEuMDI3LjExOC4wMTguMjA3LjAzOS4yNjcuMDYyWm0uNzkgMy44Mjh2LS4xNTdjMC0uNTM0LjA3Ny0xLjAyOC4yMzItMS40ODQuMTU1LS40Ni4zNzgtLjg1OS42Ny0xLjE5NmEzLjAyNCAzLjAyNCAwIDAgMSAxLjA4LS43OTMgMy41MTYgMy41MTYgMCAwIDEgMS40NS0uMjg3Yy41NDIgMCAxLjAyNS4wOTYgMS40NDkuMjg3LjQyOC4xODcuNzkuNDUxIDEuMDg2Ljc5My4yOTcuMzM3LjUyMi43MzYuNjc3IDEuMTk2LjE1NS40NTYuMjMzLjk1LjIzMyAxLjQ4NHYuMTU3YzAgLjUzMy0uMDc4IDEuMDI4LS4yMzMgMS40ODNhMy41NSAzLjU1IDAgMCAxLS42NzcgMS4xOTcgMy4xMzYgMy4xMzYgMCAwIDEtMS4wOC43OTIgMy41NCAzLjU0IDAgMCAxLTEuNDQyLjI4IDMuNjA3IDMuNjA3IDAgMCAxLTEuNDU2LS4yOCAzLjEzNSAzLjEzNSAwIDAgMS0xLjA4LS43OTIgMy41NTEgMy41NTEgMCAwIDEtLjY3Ny0xLjE5NyA0LjU4MiA0LjU4MiAwIDAgMS0uMjMyLTEuNDgzWm0xLjY0Ny0uMTU3di4xNTdjMCAuMzMyLjAzNC42NDcuMTAzLjk0M2EyLjQgMi40IDAgMCAwIC4zMi43OGMuMTQ3LjIyMy4zMzQuMzk4LjU2MS41MjYuMjI4LjEyNy41LjE5MS44MTQuMTkxLjMwNSAwIC41Ny0uMDY0Ljc5My0uMTkxLjIyOC0uMTI4LjQxNS0uMzAzLjU2LS41MjcuMTQ2LS4yMjMuMjUzLS40ODMuMzIyLS43NzkuMDczLS4yOTYuMTEtLjYxLjExLS45NDN2LS4xNTdjMC0uMzI5LS4wMzctLjYzOS0uMTEtLjkzYTIuMzg3IDIuMzg3IDAgMCAwLS4zMjgtLjc4NiAxLjU3IDEuNTcgMCAwIDAtLjU2LS41MzMgMS41NDQgMS41NDQgMCAwIDAtLjgtLjE5OWMtLjMxIDAtLjU4LjA2Ni0uODA3LjE5OWExLjU4NyAxLjU4NyAwIDAgMC0uNTU0LjUzM2MtLjE0Ni4yMjgtLjI1My40OS0uMzIxLjc4NmE0LjA2MiA0LjA2MiAwIDAgMC0uMTAzLjkzWm0xMS41OTgtMy42MjRoMS40OTd2Ny4xOTJjMCAuNjY1LS4xNDEgMS4yMy0uNDI0IDEuNjk1YTIuNzI0IDIuNzI0IDAgMCAxLTEuMTgzIDEuMDZjLS41MDUuMjQ2LTEuMDkxLjM2OS0xLjc1Ny4zNjktLjI4MiAwLS41OTYtLjA0MS0uOTQzLS4xMjNhMy44NjUgMy44NjUgMCAwIDEtLjk5OC0uMzk3IDIuNDc0IDIuNDc0IDAgMCAxLS44LS43MDRsLjc3My0uOTdjLjI2NC4zMTQuNTU2LjU0NC44NzUuNjkuMzE5LjE0Ni42NTQuMjE5IDEuMDA1LjIxOS4zNzggMCAuNy0uMDcuOTYzLS4yMTIuMjctLjEzNy40NzctLjM0LjYyMy0uNjA4LjE0NS0uMjcuMjE4LS41OTcuMjE4LS45ODV2LTUuNTVsLjE1LTEuNjc1Wm0tNS4wMjUgMy43OHYtLjE0M2MwLS41Ni4wNjktMS4wNy4yMDUtMS41MzEuMTM3LS40NjUuMzMzLS44NjQuNTg4LTEuMTk2LjI1NS0uMzM4LjU2NS0uNTk1LjkzLS43NzNhMi43MjkgMi43MjkgMCAwIDEgMS4yMzctLjI3M2MuNDc5IDAgLjg4Ny4wODYgMS4yMjQuMjYuMzQyLjE3My42MjYuNDIxLjg1NC43NDUuMjI4LjMxOS40MDYuNzAyLjUzNCAxLjE0OC4xMzIuNDQyLjIzLjkzNC4yOTQgMS40Nzd2LjQ1OGE3LjQgNy40IDAgMCAxLS4zMDEgMS40NDkgNCA0IDAgMCAxLS41NiAxLjEzNWMtLjIzMy4zMTktLjUyLjU2NS0uODYyLjczOC0uMzM3LjE3My0uNzM2LjI2LTEuMTk2LjI2LS40NTIgMC0uODYtLjA5NC0xLjIyNC0uMjhhMi43ODIgMi43ODIgMCAwIDEtLjkzLS43ODcgMy43MjMgMy43MjMgMCAwIDEtLjU4OC0xLjE5IDUuMjM4IDUuMjM4IDAgMCAxLS4yMDUtMS40OTZabTEuNjQ4LS4xNDN2LjE0NGMwIC4zMzcuMDMyLjY1MS4wOTYuOTQzLjA2OC4yOTIuMTcuNTUuMzA3Ljc3My4xNDEuMjE4LjMyLjM5Mi41MzMuNTIuMjE5LjEyMi40NzcuMTg0Ljc3My4xODQuMzg3IDAgLjcwNC0uMDgyLjk1LS4yNDYuMjUtLjE2NC40NDItLjM4Ni41NzQtLjY2NC4xMzctLjI4Mi4yMzMtLjU5Ny4yODctLjk0M3YtMS4yMzdhMy4wMjMgMy4wMjMgMCAwIDAtLjE3LS43NTIgMi4wMTEgMi4wMTEgMCAwIDAtLjMzNi0uNjA5IDEuMzk5IDEuMzk5IDAgMCAwLS41MzMtLjQxYy0uMjE0LS4xLS40NjctLjE1LS43NTktLjE1LS4yOTYgMC0uNTUzLjA2NC0uNzcyLjE5MWExLjU2OSAxLjU2OSAwIDAgMC0uNTQuNTI3Yy0uMTM3LjIyMy0uMjQuNDgzLS4zMDguNzc5YTQuMjIyIDQuMjIyIDAgMCAwLS4xMDIuOTVabTEwLjA5MyAzLjg5N2EzLjk0NCAzLjk0NCAwIDAgMS0xLjQ4My0uMjY3IDMuMjc1IDMuMjc1IDAgMCAxLTEuMTIxLS43NTkgMy4zNjQgMy4zNjQgMCAwIDEtLjcwNC0xLjE0MSAzLjk5OSAzLjk5OSAwIDAgMS0uMjQ2LTEuNDE1di0uMjc0YzAtLjU3OS4wODQtMS4xMDMuMjUzLTEuNTcyLjE2OC0uNDcuNDAzLS44Ny43MDQtMS4yMDMuMy0uMzM3LjY1Ni0uNTk1IDEuMDY2LS43NzMuNDEtLjE3Ny44NTUtLjI2NiAxLjMzMy0uMjY2LjUyOSAwIC45OTIuMDg5IDEuMzg4LjI2Ni4zOTYuMTc4LjcyNS40MjkuOTg0Ljc1Mi4yNjUuMzIuNDYuNy41ODggMS4xNDIuMTMyLjQ0Mi4xOTkuOTMuMTk5IDEuNDYzdi43MDRoLTUuNzE1VjcyLjYxaDQuMDg3di0uMTNhMi4zMTIgMi4zMTIgMCAwIDAtLjE3Ny0uODM0IDEuNDE3IDEuNDE3IDAgMCAwLS40ODYtLjYyOGMtLjIxOC0uMTYtLjUxLS4yNC0uODc1LS4yNC0uMjczIDAtLjUxNy4wNi0uNzMxLjE3OC0uMjEuMTE0LS4zODUuMjgtLjUyNi41YTIuNjMgMi42MyAwIDAgMC0uMzI5Ljc5MmMtLjA3Mi4zMDUtLjEwOS42NS0uMTA5IDEuMDMydi4yNzRjMCAuMzIzLjA0My42MjQuMTMuOTAyLjA5MS4yNzQuMjIzLjUxMy4zOTcuNzE4LjE3My4yMDUuMzgyLjM2Ny42MjguNDg1LjI0Ni4xMTQuNTI3LjE3MS44NDEuMTcxLjM5NyAwIC43NS0uMDggMS4wNi0uMjQuMzEtLjE1OS41NzktLjM4NC44MDYtLjY3NmwuODY5Ljg0Yy0uMTYuMjMzLS4zNjcuNDU3LS42MjMuNjctLjI1NS4yMS0uNTY3LjM4MS0uOTM2LjUxNGEzLjcyNyAzLjcyNyAwIDAgMS0xLjI3Mi4xOThabTYuMTMtNS45NTRWNzdoLTEuNjQ4di03LjM5N2gxLjU1MmwuMDk1IDEuNThabS0uMjk0IDEuODQ1LS41MzQtLjAwN2E0LjgxMiA0LjgxMiAwIDAgMSAuMjE5LTEuNDQyYy4xNDYtLjQzNy4zNDYtLjgxMy42MDItMS4xMjguMjYtLjMxNC41Ny0uNTU2LjkzLS43MjQuMzYtLjE3NC43Ni0uMjYgMS4yMDItLjI2LjM1NiAwIC42NzcuMDUuOTY0LjE1LjI5Mi4wOTYuNTQuMjUzLjc0NS40NzIuMjEuMjE5LjM3LjUwMy40NzkuODU0LjExLjM0Ny4xNjQuNzczLjE2NCAxLjI3OVY3N2gtMS42NTR2LTQuNzg1YzAtLjM1Ni0uMDUzLS42MzYtLjE1OC0uODQxYS44OC44OCAwIDAgMC0uNDQ0LS40NDRjLS4xOTEtLjA5Mi0uNDMtLjEzNy0uNzE4LS4xMzctLjI4MiAwLS41MzUuMDYtLjc1OC4xNzgtLjIyNC4xMTgtLjQxMy4yOC0uNTY4LjQ4NS0uMTUuMjA1LS4yNjYuNDQyLS4zNDkuNzExYTIuOTM4IDIuOTM4IDAgMCAwLS4xMjMuODYxWm0tNDQuOTEzIDE4LjQ0VjgyLjVoMS42NTVWOTNoLTEuNDk3bC0uMTU4LTEuNTMxWm0tNC44MTItMi4wODR2LS4xNDRjMC0uNTYuMDY2LTEuMDcuMTk4LTEuNTMxLjEzMi0uNDY1LjMyNC0uODY0LjU3NC0xLjE5Ni4yNTEtLjMzOC41NTYtLjU5NS45MTYtLjc3My4zNi0uMTgyLjc2Ni0uMjczIDEuMjE3LS4yNzMuNDQ3IDAgLjgzOS4wODYgMS4xNzYuMjYuMzM3LjE3My42MjQuNDIxLjg2MS43NDUuMjM3LjMxOS40MjYuNzAyLjU2OCAxLjE0OC4xNC40NDIuMjQxLjkzNC4zIDEuNDc3di40NThhNy4zOTIgNy4zOTIgMCAwIDEtLjMgMS40NDljLS4xNDIuNDM3LS4zMy44MTYtLjU2OCAxLjEzNWEyLjQ1MyAyLjQ1MyAwIDAgMS0uODY4LjczOGMtLjMzNy4xNzMtLjczMS4yNi0xLjE4My4yNi0uNDQ2IDAtLjg1LS4wOTQtMS4yMS0uMjhhMi42NzQgMi42NzQgMCAwIDEtLjkwOS0uNzg3IDMuNzAzIDMuNzAzIDAgMCAxLS41NzQtMS4xOSA1LjQwOSA1LjQwOSAwIDAgMS0uMTk4LTEuNDk2Wm0xLjY0Ny0uMTQ0di4xNDRjMCAuMzM3LjAzLjY1MS4wOS45NDMuMDYzLjI5Mi4xNjEuNTUuMjkzLjc3My4xMzIuMjE4LjMwMy4zOTIuNTEzLjUyLjIxNC4xMjIuNDcuMTg0Ljc2NS4xODQuMzc0IDAgLjY4Mi0uMDgyLjkyMy0uMjQ2LjI0Mi0uMTY0LjQzMS0uMzg2LjU2OC0uNjY0YTMuMDggMy4wOCAwIDAgMCAuMjg3LS45NDN2LTEuMjM3YTMuMDE4IDMuMDE4IDAgMCAwLS4xNzEtLjc1MiAyLjAxMyAyLjAxMyAwIDAgMC0uMzM1LS42MDkgMS40MTIgMS40MTIgMCAwIDAtLjUyNi0uNDEgMS42NSAxLjY1IDAgMCAwLS43MzItLjE1Yy0uMyAwLS41NTYuMDY0LS43NjYuMTkxLS4yMS4xMjgtLjM4Mi4zMDMtLjUxOS41MjdhMi41OSAyLjU5IDAgMCAwLS4yOTQuNzc5IDQuNTA4IDQuNTA4IDAgMCAwLS4wOTYuOTVabTguNTU2LTMuNjM2VjkzaC0xLjY1NHYtNy4zOTdoMS42NTRabS0xLjc2NC0xLjk0MmMwLS4yNS4wODItLjQ1OC4yNDYtLjYyMi4xNjktLjE2OS40MDEtLjI1My42OTgtLjI1My4yOTEgMCAuNTIyLjA4NC42OS4yNTNhLjgzMS44MzEgMCAwIDEgLjI1My42MjIuODIzLjgyMyAwIDAgMS0uMjUzLjYxNWMtLjE2OC4xNjQtLjM5OS4yNDYtLjY5LjI0Ni0uMjk3IDAtLjUyOS0uMDgyLS42OTgtLjI0NmEuODM1LjgzNSAwIDAgMS0uMjQ2LS42MTVabTMuNTA0IDUuNzIydi0uMTU3YzAtLjUzNC4wNzgtMS4wMjguMjMzLTEuNDg0LjE1NS0uNDYuMzc4LS44NTkuNjctMS4xOTZhMy4wMjMgMy4wMjMgMCAwIDEgMS4wOC0uNzkzIDMuNTE1IDMuNTE1IDAgMCAxIDEuNDQ5LS4yODdjLjU0MiAwIDEuMDI1LjA5NiAxLjQ1LjI4Ny40MjguMTg3Ljc5LjQ1MSAxLjA4Ni43OTMuMjk2LjMzNy41MjIuNzM2LjY3NyAxLjE5Ni4xNTUuNDU2LjIzMi45NS4yMzIgMS40ODR2LjE1N2MwIC41MzMtLjA3NyAxLjAyOC0uMjMyIDEuNDgzLS4xNTUuNDU2LS4zOC44NTUtLjY3NyAxLjE5N2EzLjEzNSAzLjEzNSAwIDAgMS0xLjA4Ljc5MiAzLjU0IDMuNTQgMCAwIDEtMS40NDMuMjggMy42MDcgMy42MDcgMCAwIDEtMS40NTYtLjI4IDMuMTM2IDMuMTM2IDAgMCAxLTEuMDgtLjc5MiAzLjU1IDMuNTUgMCAwIDEtLjY3Ni0xLjE5NyA0LjU3OSA0LjU3OSAwIDAgMS0uMjMzLTEuNDgzWm0xLjY0OC0uMTU3di4xNTdjMCAuMzMyLjAzNC42NDcuMTAyLjk0M2EyLjQgMi40IDAgMCAwIC4zMjIuNzhjLjE0NS4yMjMuMzMyLjM5OC41Ni41MjYuMjI4LjEyNy41LjE5MS44MTMuMTkxLjMwNiAwIC41Ny0uMDY0Ljc5My0uMTkxLjIyOC0uMTI4LjQxNS0uMzAzLjU2MS0uNTI3YTIuNCAyLjQgMCAwIDAgLjMyMS0uNzc5Yy4wNzMtLjI5Ni4xMS0uNjEuMTEtLjk0M3YtLjE1N2MwLS4zMjktLjAzNy0uNjM5LS4xMS0uOTNhMi4zOSAyLjM5IDAgMCAwLS4zMjgtLjc4NiAxLjU3IDEuNTcgMCAwIDAtLjU2LS41MzMgMS41NDMgMS41NDMgMCAwIDAtLjgtLjE5OWMtLjMxIDAtLjU3OS4wNjYtLjgwNy4xOTlhMS41ODggMS41ODggMCAwIDAtLjU1My41MzNjLS4xNDYuMjI4LS4yNTMuNDktLjMyMi43ODZhNC4wNjcgNC4wNjcgMCAwIDAtLjEwMi45M1ptNy45NjgtMy42MjQgMS40MDggMi40NTUgMS40MzUtMi40NTVoMS44MTJsLTIuMjQyIDMuNjNMNjQuODQ5IDkzaC0xLjgxbC0xLjUwNS0yLjU1N0w2MC4wMyA5M2gtMS44MThsMi4zMjQtMy43NjctMi4yMzUtMy42M2gxLjgwNFptNy43OTYgMFY5M2gtMS42NTR2LTcuMzk3aDEuNjU0Wm0tMS43NjMtMS45NGMwLS4yNTEuMDgyLS40NTkuMjQ2LS42MjMuMTY4LS4xNjkuNC0uMjUzLjY5Ny0uMjUzLjI5MiAwIC41MjIuMDg0LjY5LjI1M2EuODMxLjgzMSAwIDAgMSAuMjUzLjYyMi44MjMuODIzIDAgMCAxLS4yNTMuNjE1Yy0uMTY4LjE2NC0uMzk4LjI0Ni0uNjkuMjQ2LS4yOTYgMC0uNTI5LS4wODItLjY5Ny0uMjQ2YS44MzQuODM0IDAgMCAxLS4yNDYtLjYxNVptOC4zMyA3LjgwNlY4Mi41aDEuNjU0VjkzaC0xLjQ5N2wtLjE1Ny0xLjUzMVptLTQuODEzLTIuMDg1di0uMTQ0YzAtLjU2LjA2Ni0xLjA3LjE5OC0xLjUzMS4xMzMtLjQ2NS4zMjQtLjg2NC41NzUtMS4xOTYuMjUtLjMzOC41NTYtLjU5NS45MTYtLjc3My4zNi0uMTgyLjc2NS0uMjczIDEuMjE2LS4yNzMuNDQ3IDAgLjg0LjA4NiAxLjE3Ni4yNi4zMzguMTczLjYyNS40MjEuODYyLjc0NS4yMzcuMzE5LjQyNi43MDIuNTY3IDEuMTQ4LjE0MS40NDIuMjQyLjkzNC4zIDEuNDc3di40NThhNy4zOTIgNy4zOTIgMCAwIDEtLjMgMS40NDljLS4xNDEuNDM3LS4zMy44MTYtLjU2NyAxLjEzNWEyLjQ1MSAyLjQ1MSAwIDAgMS0uODY4LjczOGMtLjMzOC4xNzMtLjczMi4yNi0xLjE4My4yNi0uNDQ3IDAtLjg1LS4wOTQtMS4yMS0uMjhhMi42NzQgMi42NzQgMCAwIDEtLjkxLS43ODcgMy43MDMgMy43MDMgMCAwIDEtLjU3My0xLjE5IDUuNDA1IDUuNDA1IDAgMCAxLS4xOTktMS40OTZabTEuNjQ4LS4xNDR2LjE0NGMwIC4zMzcuMDMuNjUxLjA4OS45NDMuMDYzLjI5Mi4xNjEuNTUuMjkzLjc3My4xMzMuMjE4LjMwNC4zOTIuNTEzLjUyLjIxNC4xMjIuNDcuMTg0Ljc2Ni4xODQuMzczIDAgLjY4MS0uMDgyLjkyMy0uMjQ2YTEuNjggMS42OCAwIDAgMCAuNTY3LS42NjQgMy4wOCAzLjA4IDAgMCAwIC4yODctLjk0M3YtMS4yMzdhMy4wMjMgMy4wMjMgMCAwIDAtLjE3LS43NTIgMi4wMTEgMi4wMTEgMCAwIDAtLjMzNi0uNjA5IDEuNDEgMS40MSAwIDAgMC0uNTI2LS40MSAxLjY1IDEuNjUgMCAwIDAtLjczMS0uMTVjLS4zMDEgMC0uNTU2LjA2NC0uNzY2LjE5MS0uMjEuMTI4LS4zODMuMzAzLS41Mi41MjdhMi41OSAyLjU5IDAgMCAwLS4yOTQuNzc5IDQuNTA4IDQuNTA4IDAgMCAwLS4wOTUuOTVabTEwLjA1MiAzLjg5N2EzLjk0NCAzLjk0NCAwIDAgMS0xLjQ4My0uMjY3IDMuMjczIDMuMjczIDAgMCAxLTEuMTIxLS43NTkgMy4zNjEgMy4zNjEgMCAwIDEtLjcwNC0xLjE0MSAzLjk5NyAzLjk5NyAwIDAgMS0uMjQ2LTEuNDE1di0uMjc0YzAtLjU3OS4wODQtMS4xMDMuMjUzLTEuNTcyLjE2OC0uNDcuNDAzLS44Ny43MDQtMS4yMDMuMy0uMzM3LjY1Ni0uNTk1IDEuMDY2LS43NzMuNDEtLjE3Ny44NTUtLjI2NiAxLjMzMy0uMjY2LjUyOSAwIC45OTEuMDg5IDEuMzg4LjI2Ni4zOTYuMTc4LjcyNC40MjkuOTg0Ljc1Mi4yNjUuMzIuNDYuNy41ODggMS4xNDIuMTMyLjQ0Mi4xOTguOTMuMTk4IDEuNDYzdi43MDRoLTUuNzE0Vjg4LjYxaDQuMDg3di0uMTNhMi4zMTQgMi4zMTQgMCAwIDAtLjE3Ny0uODM0IDEuNDE3IDEuNDE3IDAgMCAwLS40ODYtLjYyOGMtLjIxOC0uMTYtLjUxLS4yNC0uODc1LS4yNC0uMjczIDAtLjUxNy4wNi0uNzMxLjE3OC0uMjEuMTE0LS4zODUuMjgtLjUyNi41LS4xNDIuMjE4LS4yNTEuNDgyLS4zMjkuNzkyLS4wNzIuMzA1LS4xMDkuNjUtLjEwOSAxLjAzMnYuMjc0YzAgLjMyMy4wNDMuNjI0LjEzLjkwMi4wOTEuMjc0LjIyMy41MTMuMzk2LjcxOC4xNzQuMjA1LjM4My4zNjcuNjMuNDg1LjI0NS4xMTQuNTI2LjE3MS44NC4xNzEuMzk3IDAgLjc1LS4wOCAxLjA2LS4yNC4zMS0uMTU5LjU3OS0uMzg0LjgwNi0uNjc2bC44NjguODRjLS4xNTkuMjMzLS4zNjYuNDU3LS42MjIuNjctLjI1NS4yMS0uNTY3LjM4MS0uOTM2LjUxNGEzLjcyNyAzLjcyNyAwIDAgMS0xLjI3Mi4xOThaIi8+PHBhdGggZmlsbD0iIzNGQTcxQSIgZD0iTTExMS4xMjUgNzIuNDQ2Vjg2aC0yLjIzNlY3NS4xbC0zLjMxMiAxLjEyMnYtMS44NDZsNS4yNzktMS45M2guMjY5Wm0xMy41OTkgNS42Njh2Mi4yMDhjMCAxLjA1OC0uMTA2IDEuOTYtLjMxNiAyLjcxLS4yMDQuNzQxLS41MDEgMS4zNDQtLjg5MSAxLjgwOGEzLjQ4OSAzLjQ4OSAwIDAgMS0xLjQgMS4wMmMtLjUzOC4yMTctMS4xNDIuMzI2LTEuODA5LjMyNmE1LjE5MiA1LjE5MiAwIDAgMS0xLjQ4NS0uMjA1IDMuNjE4IDMuNjE4IDAgMCAxLTEuMjI0LS42NCAzLjgxNiAzLjgxNiAwIDAgMS0uOTM3LTEuMTIyYy0uMjU0LS40NjQtLjQ1Mi0xLjAxOC0uNTk0LTEuNjYtLjEzNi0uNjQ0LS4yMDQtMS4zOS0uMjA0LTIuMjM3di0yLjIwOGMwLTEuMDY0LjEwNS0xLjk2LjMxNS0yLjY5LjIxLS43MzYuNTEtMS4zMzMuOS0xLjc5LjM5LS40NjUuODU0LS44MDIgMS4zOTItMS4wMTJhNS4wMDYgNS4wMDYgMCAwIDEgMS44MTgtLjMxNWMuNTM4IDAgMS4wMzMuMDY4IDEuNDg0LjIwNC40NTguMTMuODY2LjMzNyAxLjIyNS42MjEuMzY1LjI4NS42NzQuNjU2LjkyOCAxLjExNC4yNTkuNDUxLjQ1Ny45OTkuNTkzIDEuNjQyLjEzNy42MzcuMjA1IDEuMzc5LjIwNSAyLjIyNlptLTIuMjM2IDIuNTI0Vjc3Ljc4YzAtLjUzOC0uMDMxLTEuMDExLS4wOTMtMS40MmE0LjI0NCA0LjI0NCAwIDAgMC0uMjc4LTEuMDQ3IDIuMDg4IDIuMDg4IDAgMCAwLS40NDYtLjcwNiAxLjU2MyAxLjU2MyAwIDAgMC0uNjEyLS4zOTggMi4xNjUgMi4xNjUgMCAwIDAtLjc3LS4xM2MtLjM0NiAwLS42NTYuMDY4LS45MjguMjA0LS4yNzIuMTMtLjUwMS4zNC0uNjg2LjYzLS4xODYuMjkxLS4zMjguNjc1LS40MjcgMS4xNTEtLjA5My40Ny0uMTM5IDEuMDQyLS4xMzkgMS43MTZ2Mi44NThjMCAuNTQ0LjAzMSAxLjAyMy4wOTMgMS40MzguMDYxLjQxNC4xNTQuNzcuMjc4IDEuMDY3LjEyNC4yOS4yNzIuNTMxLjQ0NS43MjMuMTguMTg2LjM4NC4zMjIuNjEzLjQwOC4yMzUuMDg3LjQ5MS4xMy43Ny4xMy4zNTIgMCAuNjY0LS4wNjguOTM3LS4yMDQuMjcyLS4xMzYuNTAxLS4zNTIuNjg2LS42NS4xODYtLjMwMi4zMjUtLjY5NS40MTgtMS4xNzcuMDkyLS40ODMuMTM5LTEuMDYxLjEzOS0xLjczNVptMTUuNzMtNC42NzZoMi4yNDVWODZoLTIuMDY5bC0uMTc2LTIuMzJ2LTcuNzE4Wm0uMzI1IDUuMzcxLjg4MS0uMDE4YzAgLjcwNS0uMDY4IDEuMzU0LS4yMDQgMS45NDhhNC43ODYgNC43ODYgMCAwIDEtLjYxMiAxLjU1IDIuOTQgMi45NCAwIDAgMS0xLjAzOSAxLjAyYy0uNDIxLjI0MS0uOTE5LjM2Mi0xLjQ5NC4zNjItLjQ1OCAwLS44NjktLjA2NS0xLjIzNC0uMTk1YTIuNDUgMi40NSAwIDAgMS0uOTQ2LS42M2MtLjI2Ni0uMjg1LS40ODYtLjY1Ny0uNjU5LTEuMTE0bC0uMjg4LTIuNTMzaDEuMjcxYzAgLjU1Ny4wNDcgMS4wMTEuMTQgMS4zNjQuMDkyLjM1Mi4yMjIuNjI1LjM4OS44MTYuMTY3LjE4Ni4zNjIuMzE2LjU4NS4zOS4yMjkuMDc0LjQ3Ni4xMTEuNzQyLjExMS40ODkgMCAuODkxLS4wNzQgMS4yMDYtLjIyMi4zMjItLjE1NS41NzItLjM2OC43NTEtLjY0LjE4Ni0uMjc5LjMxNi0uNjAzLjM5LS45NzUuMDgtLjM3Ny4xMjEtLjc4OC4xMjEtMS4yMzNabS02LjU1LTUuMzcxaDIuMjI2djEzLjg5N2gtMi4yMjZWNzUuOTYyWm0xNy42MTUgMGgyLjAzMnY5Ljc2YzAgLjkwMy0uMTkxIDEuNjctLjU3NSAyLjNhMy42OTIgMy42OTIgMCAwIDEtMS42MDUgMS40MzhjLS42ODYuMzM0LTEuNDgxLjUwMS0yLjM4NC41MDEtLjM4NCAwLS44MS0uMDU1LTEuMjgtLjE2N2E1LjI0NSA1LjI0NSAwIDAgMS0xLjM1NS0uNTM4IDMuMzQ5IDMuMzQ5IDAgMCAxLTEuMDg1LS45NTVsMS4wNDgtMS4zMThjLjM1OS40MjcuNzU0Ljc0IDEuMTg3LjkzNy40MzMuMTk4Ljg4OC4yOTcgMS4zNjQuMjk3LjUxNCAwIC45NS0uMDk2IDEuMzA4LS4yODcuMzY1LS4xODYuNjQ3LS40NjEuODQ1LS44MjYuMTk3LS4zNjUuMjk2LS44MS4yOTYtMS4zMzZ2LTcuNTMzbC4yMDQtMi4yNzNabS02LjgxOCA1LjEzdi0uMTk1YzAtLjc2LjA5Mi0xLjQ1My4yNzgtMi4wNzguMTg2LS42My40NTEtMS4xNzIuNzk4LTEuNjIzYTMuNDc2IDMuNDc2IDAgMCAxIDEuMjYyLTEuMDQ5Yy40OTQtLjI0NyAxLjA1NC0uMzcgMS42NzktLjM3LjY0OSAwIDEuMjAzLjExNyAxLjY2LjM1Mi40NjQuMjM1Ljg1MS41NzIgMS4xNiAxLjAxMS4zMDkuNDMzLjU1MS45NTMuNzI0IDEuNTU5LjE3OS42LjMxMiAxLjI2OC4zOTkgMi4wMDR2LjYyMWExMC4wNiAxMC4wNiAwIDAgMS0uNDA5IDEuOTY3IDUuNDE4IDUuNDE4IDAgMCAxLS43NiAxLjU0IDMuMjUgMy4yNSAwIDAgMS0xLjE2OSAxLjAwMmMtLjQ1OC4yMzUtLjk5OS4zNTMtMS42MjQuMzUzYTMuNTgzIDMuNTgzIDAgMCAxLTEuNjYtLjM4IDMuNzcxIDMuNzcxIDAgMCAxLTEuMjYyLTEuMDY4IDUuMDUgNS4wNSAwIDAgMS0uNzk4LTEuNjE0IDcuMDkyIDcuMDkyIDAgMCAxLS4yNzgtMi4wMzJabTIuMjM1LS4xOTV2LjE5NWMwIC40NTguMDQ0Ljg4NS4xMyAxLjI4LjA5My4zOTYuMjMyLjc0Ni40MTggMS4wNDkuMTkyLjI5Ny40MzMuNTMyLjcyMy43MDUuMjk3LjE2Ny42NDcuMjUgMS4wNDkuMjUuNTI2IDAgLjk1NS0uMTEgMS4yODktLjMzNC4zNDEtLjIyMi42LS41MjIuNzgtLjkuMTg1LS4zODMuMzE1LS44MS4zODktMS4yOHYtMS42NzlhNC4wNzMgNC4wNzMgMCAwIDAtLjIzMi0xLjAyIDIuNzE2IDIuNzE2IDAgMCAwLS40NTQtLjgyNiAxLjg5OSAxLjg5OSAwIDAgMC0uNzI0LS41NTdjLS4yOTEtLjEzNi0uNjM0LS4yMDQtMS4wMy0uMjA0LS40MDIgMC0uNzUxLjA4Ny0xLjA0OC4yNmEyLjEzIDIuMTMgMCAwIDAtLjczMy43MTQgMy41MjYgMy41MjYgMCAwIDAtLjQxNyAxLjA1OCA1LjcxIDUuNzEgMCAwIDAtLjE0IDEuMjlabTE0LjkwNy04LjQwNS01LjI2IDE0LjY2OGgtMS43NTRsNS4yNy0xNC42NjhoMS43NDRabTMuODg1IDUuNTFWODZoLTIuMjM2Vjc1Ljk2MmgyLjEwNmwuMTMgMi4wNFptLS4zNjIgMi42MDgtLjc2LS4wMWMwLS42OTIuMDg2LTEuMzMyLjI1OS0xLjkyYTQuNjI5IDQuNjI5IDAgMCAxIC43NjEtMS41MyAzLjM1IDMuMzUgMCAwIDEgMS4yNDMtMS4wMTJjLjUwMS0uMjQxIDEuMDgtLjM2MiAxLjczNS0uMzYyLjQ1OCAwIC44NzUuMDY4IDEuMjUzLjIwNS4zODMuMTMuNzE0LjMzNy45OTIuNjIxLjI4NS4yODUuNTAxLjY1LjY1IDEuMDk1LjE1NC40NDUuMjMyLjk4My4yMzIgMS42MTRWODZoLTIuMjM2di02LjQ5NGMwLS40ODktLjA3NC0uODcyLS4yMjMtMS4xNWExLjIzMiAxLjIzMiAwIDAgMC0uNjIxLS41OTRjLS4yNjYtLjEyNC0uNTg1LS4xODYtLjk1Ni0uMTg2LS40MiAwLS43NzkuMDgtMS4wNzYuMjQxLS4yOTEuMTYxLS41MjkuMzgtLjcxNC42NmEyLjk1IDIuOTUgMCAwIDAtLjQwOS45NjRjLS4wODYuMzY1LS4xMy43NTQtLjEzIDEuMTY5Wm02LjIyNi0uNTk0LTEuMDQ5LjIzMmMwLS42MDYuMDg0LTEuMTc4LjI1MS0xLjcxNi4xNzMtLjU0NC40MjMtMS4wMi43NTEtMS40MjkuMzM0LS40MTQuNzQ1LS43NCAxLjIzNC0uOTc0LjQ4OS0uMjM1IDEuMDQ4LS4zNTMgMS42NzktLjM1M2E0LjEgNC4xIDAgMCAxIDEuMzczLjIxNGMuNDA5LjEzNi43NTUuMzUyIDEuMDM5LjY1LjI4NS4yOTYuNTAxLjY4My42NSAxLjE1OS4xNDguNDcuMjIyIDEuMDM5LjIyMiAxLjcwN1Y4NmgtMi4yNDV2LTYuNTAzYzAtLjUwOC0uMDc0LS45LS4yMjItMS4xNzlhMS4xNTggMS4xNTggMCAwIDAtLjYxMy0uNTc1IDIuNDY1IDIuNDY1IDAgMCAwLS45NTUtLjE2N2MtLjM0NyAwLS42NTMuMDY1LS45MTkuMTk1LS4yNTkuMTI0LS40NzkuMy0uNjU4LjUyOWEyLjM1IDIuMzUgMCAwIDAtLjQwOS43N2MtLjA4Ni4yOS0uMTI5LjYwNi0uMTI5Ljk0NlptOS45MDYtNC41MThoLjgwN2MuMjY2IDAgLjQ4NS0uMDM3LjY1OS0uMTExYS44ODUuODg1IDAgMCAwIC4zODktLjMyNS44MzQuODM0IDAgMCAwIC4xMzktLjQ4Mi43ODcuNzg3IDAgMCAwLS4yNTktLjU4NWMtLjE2Ny0uMTczLS40NDktLjI2LS44NDUtLjI2YTEuNDQgMS40NCAwIDAgMC0uNzcuMTk1LjU4Ni41ODYgMCAwIDAtLjMwNi41MjloLTEuNjg4YzAtLjQ0LjEyMy0uODEzLjM3MS0xLjEyMy4yNDctLjMwOS41NzgtLjU0NC45OTItLjcwNS40MTUtLjE2Ljg2Ni0uMjQgMS4zNTUtLjI0LjU2MyAwIDEuMDU4LjA4IDEuNDg0LjI0LjQyNy4xNTUuNzU4LjM4Ny45OTMuNjk2LjI0MS4zMS4zNjIuNjkzLjM2MiAxLjE1IDAgLjM1My0uMTEyLjY2NS0uMzM0LjkzOC0uMjE3LjI3Mi0uNTIuNDg1LS45MDkuNjQtLjM4NC4xNDgtLjgzNS4yMjItMS4zNTUuMjIyaC0xLjA4NXYtLjc3OVptMCAxLjE2di0uNzYxaDEuMDg1Yy41NzUgMCAxLjA2Ny4wNjggMS40NzUuMjA0LjQwOC4xMy43MTguMzMxLjkyOC42MDMuMjE2LjI3Mi4zMjUuNjI1LjMyNSAxLjA1OCAwIC40NTctLjEzLjg0Ny0uMzkgMS4xNjktLjI2LjMyMS0uNjE1LjU2Ni0xLjA2Ny43MzMtLjQ0NS4xNjctLjk0Ni4yNS0xLjUwMy4yNS0uNDY0IDAtLjkxNS0uMDc0LTEuMzU0LS4yMjNhMi41MTIgMi41MTIgMCAwIDEtMS4wNzYtLjcyM2MtLjI3OS0uMzI4LS40MTgtLjc1OC0uNDE4LTEuMjloMS42OThjMCAuMjQxLjExMS40NDkuMzM0LjYyMi4yMjIuMTczLjUyMi4yNi45LjI2LjQwMiAwIC42OTktLjA4Ny44OS0uMjZhLjg2NS44NjUgMCAwIDAgLjI5Ny0uNjY4YzAtLjIzNS0uMDU1LS40MjQtLjE2Ny0uNTY2YS44NjMuODYzIDAgMCAwLS40NTQtLjMwNiAyLjE0MiAyLjE0MiAwIDAgMC0uNjk2LS4xMDJoLS44MDdaIi8+PC9nPjxkZWZzPjxmaWx0ZXIgaWQ9ImEiIHdpZHRoPSIxOTgiIGhlaWdodD0iNTQiIHg9IjEiIHk9IjUzIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIzIi8+PGZlQ29tcG9zaXRlIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzEwNjJfMjU2MiIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA2Ml8yNTYyIiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PC9kZWZzPjwvc3ZnPg==", + "description": "A gas produced from vehicle emissions and the burning of fossil fuels.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'nitrogen', label: 'Nitrogen dioxide', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Nitrogen dioxide\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"public\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#3FA71A\"},{\"from\":40,\"to\":90,\"color\":\"#80C32C\"},{\"from\":90,\"to\":120,\"color\":\"#FFA600\"},{\"from\":120,\"to\":230,\"color\":\"#F36900\"},{\"from\":230,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#3FA71A\"},{\"from\":40,\"to\":90,\"color\":\"#80C32C\"},{\"from\":90,\"to\":120,\"color\":\"#FFA600\"},{\"from\":120,\"to\":230,\"color\":\"#F36900\"},{\"from\":230,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal nitrogen dioxide (NO2) card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "nitrogen" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_nitrogen_dioxide__no2__card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_nitrogen_dioxide__no2__card_with_background.json new file mode 100644 index 0000000000..96cda090d9 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_nitrogen_dioxide__no2__card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "horizontal_nitrogen_dioxide_no2_card_with_background", + "name": "Horizontal nitrogen dioxide (NO2) card with background", + "deprecated": false, + "image": "tb-image:Tk8yLWhvcml6b250YWwtdmFsdWUtY2FyZC13aXRoLWJhY2tncm91bmQuc3Zn:Tk8yLWhvcml6b250YWwtdmFsdWUtY2FyZC13aXRoLWJhY2tncm91bmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSI1NiIgeT0iNTIiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjQ0IiB4PSI2IiB5PSI1OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuNzYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iIzNCOTExQyIgZD0iTTIyIDcwYy01LjUyIDAtMTAgNC40OC0xMCAxMHM0LjQ4IDEwIDEwIDEwIDEwLTQuNDggMTAtMTAtNC40OC0xMC0xMC0xMFptLTggMTBjMC0uNjEuMDgtMS4yMS4yMS0xLjc4TDE4Ljk5IDgzdjFjMCAxLjEuOSAyIDIgMnYxLjkzQzE3LjA2IDg3LjQzIDE0IDg0LjA3IDE0IDgwWm0xMy44OSA1LjRjLS4yNi0uODEtMS0xLjQtMS45LTEuNGgtMXYtM2MwLS41NS0uNDUtMS0xLTFoLTZ2LTJoMmMuNTUgMCAxLS40NSAxLTF2LTJoMmMxLjEgMCAyLS45IDItMnYtLjQxQzI3LjkyIDczLjc3IDMwIDc2LjY1IDMwIDgwYzAgMi4wOC0uODEgMy45OC0yLjExIDUuNFoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii44NyIgZD0iTTQ4LjkwNyA2Ny4wNDdWNzdoLTEuNzE2bC00LjQ2NC03LjEzVjc3aC0xLjcxNXYtOS45NTNoMS43MTVsNC40NzggNy4xNDN2LTcuMTQzaDEuNzAyWm0zLjg5NCAyLjU1NlY3N2gtMS42NTR2LTcuMzk3SDUyLjhabS0xLjc2NC0xLjk0YzAtLjI1MS4wODItLjQ1OS4yNDYtLjYyMy4xNjktLjE2OS40MDEtLjI1My42OTctLjI1My4yOTIgMCAuNTIyLjA4NC42OS4yNTNhLjgzMS44MzEgMCAwIDEgLjI1NC42MjIuODIzLjgyMyAwIDAgMS0uMjUzLjYxNWMtLjE2OS4xNjQtLjM5OS4yNDYtLjY5LjI0Ni0uMjk3IDAtLjUzLS4wODItLjY5OC0uMjQ2YS44MzQuODM0IDAgMCAxLS4yNDYtLjYxNVptNy4yMSAxLjk0djEuMjA0aC00LjE3di0xLjIwNGg0LjE3Wm0tMi45NjgtMS44MTFoMS42NDh2Ny4xNjRjMCAuMjI4LjAzMi40MDMuMDk2LjUyNmEuNTExLjUxMSAwIDAgMCAuMjguMjRjLjExOC4wNC4yNTcuMDYxLjQxNy4wNjEuMTE0IDAgLjIyMy0uMDA3LjMyOC0uMDJhMy4yOSAzLjI5IDAgMCAwIC4yNTMtLjA0MWwuMDA3IDEuMjU4YTQuMjA1IDQuMjA1IDAgMCAxLS40NzkuMTA5IDMuNTE0IDMuNTE0IDAgMCAxLS42MTUuMDQ4Yy0uMzc4IDAtLjcxMy0uMDY2LTEuMDA1LS4xOThhMS40OCAxLjQ4IDAgMCAxLS42ODQtLjY2NGMtLjE2NC0uMzA1LS4yNDYtLjcxLS4yNDYtMS4yMTZ2LTcuMjY3Wm02LjE0MyAzLjIyVjc3aC0xLjY0OHYtNy4zOTdoMS41NzNsLjA3NSAxLjQwOVptMi4yNjMtMS40NTYtLjAxNCAxLjUzYTQuMjkzIDQuMjkzIDAgMCAwLS42Ny0uMDU1Yy0uMjgzLjAwMS0uNTMuMDQyLS43NDUuMTI0YTEuNDA0IDEuNDA0IDAgMCAwLS41NC4zNDJjLS4xNDIuMTQ2LS4yNS4zMjQtLjMyOC41MzMtLjA3OC4yMS0uMTIzLjQ0NS0uMTM3LjcwNGwtLjM3Ni4wMjhjMC0uNDY1LjA0Ni0uODk2LjEzNy0xLjI5MmEzLjUzIDMuNTMgMCAwIDEgLjQxLTEuMDQ2Yy4xODctLjMwMS40Mi0uNTM2LjY5Ny0uNzA0LjI4My0uMTY5LjYwOC0uMjUzLjk3OC0uMjUzLjEgMCAuMjA3LjAwOS4zMjEuMDI3LjExOC4wMTguMjA3LjAzOS4yNjcuMDYyWm0uNzkgMy44Mjh2LS4xNTdjMC0uNTM0LjA3Ny0xLjAyOC4yMzItMS40ODQuMTU1LS40Ni4zNzgtLjg1OS42Ny0xLjE5NmEzLjAyNCAzLjAyNCAwIDAgMSAxLjA4LS43OTMgMy41MTYgMy41MTYgMCAwIDEgMS40NS0uMjg3Yy41NDIgMCAxLjAyNS4wOTYgMS40NDkuMjg3LjQyOC4xODcuNzkuNDUxIDEuMDg2Ljc5My4yOTcuMzM3LjUyMi43MzYuNjc3IDEuMTk2LjE1NS40NTYuMjMzLjk1LjIzMyAxLjQ4NHYuMTU3YzAgLjUzMy0uMDc4IDEuMDI4LS4yMzMgMS40ODNhMy41NSAzLjU1IDAgMCAxLS42NzcgMS4xOTcgMy4xMzYgMy4xMzYgMCAwIDEtMS4wOC43OTIgMy41NCAzLjU0IDAgMCAxLTEuNDQyLjI4IDMuNjA3IDMuNjA3IDAgMCAxLTEuNDU2LS4yOCAzLjEzNSAzLjEzNSAwIDAgMS0xLjA4LS43OTIgMy41NTEgMy41NTEgMCAwIDEtLjY3Ny0xLjE5NyA0LjU4MiA0LjU4MiAwIDAgMS0uMjMyLTEuNDgzWm0xLjY0Ny0uMTU3di4xNTdjMCAuMzMyLjAzNC42NDcuMTAzLjk0M2EyLjQgMi40IDAgMCAwIC4zMi43OGMuMTQ3LjIyMy4zMzQuMzk4LjU2MS41MjYuMjI4LjEyNy41LjE5MS44MTQuMTkxLjMwNSAwIC41Ny0uMDY0Ljc5My0uMTkxLjIyOC0uMTI4LjQxNS0uMzAzLjU2LS41MjcuMTQ2LS4yMjMuMjUzLS40ODMuMzIyLS43NzkuMDczLS4yOTYuMTEtLjYxLjExLS45NDN2LS4xNTdjMC0uMzI5LS4wMzctLjYzOS0uMTEtLjkzYTIuMzg3IDIuMzg3IDAgMCAwLS4zMjgtLjc4NiAxLjU3IDEuNTcgMCAwIDAtLjU2LS41MzMgMS41NDQgMS41NDQgMCAwIDAtLjgtLjE5OWMtLjMxIDAtLjU4LjA2Ni0uODA3LjE5OWExLjU4NyAxLjU4NyAwIDAgMC0uNTU0LjUzM2MtLjE0Ni4yMjgtLjI1My40OS0uMzIxLjc4NmE0LjA2MiA0LjA2MiAwIDAgMC0uMTAzLjkzWm0xMS41OTgtMy42MjRoMS40OTd2Ny4xOTJjMCAuNjY1LS4xNDEgMS4yMy0uNDI0IDEuNjk1YTIuNzI0IDIuNzI0IDAgMCAxLTEuMTgzIDEuMDZjLS41MDUuMjQ2LTEuMDkxLjM2OS0xLjc1Ny4zNjktLjI4MiAwLS41OTYtLjA0MS0uOTQzLS4xMjNhMy44NjUgMy44NjUgMCAwIDEtLjk5OC0uMzk3IDIuNDc0IDIuNDc0IDAgMCAxLS44LS43MDRsLjc3My0uOTdjLjI2NC4zMTQuNTU2LjU0NC44NzUuNjkuMzE5LjE0Ni42NTQuMjE5IDEuMDA1LjIxOS4zNzggMCAuNy0uMDcuOTYzLS4yMTIuMjctLjEzNy40NzctLjM0LjYyMy0uNjA4LjE0NS0uMjcuMjE4LS41OTcuMjE4LS45ODV2LTUuNTVsLjE1LTEuNjc1Wm0tNS4wMjUgMy43OHYtLjE0M2MwLS41Ni4wNjktMS4wNy4yMDUtMS41MzEuMTM3LS40NjUuMzMzLS44NjQuNTg4LTEuMTk2LjI1NS0uMzM4LjU2NS0uNTk1LjkzLS43NzNhMi43MjkgMi43MjkgMCAwIDEgMS4yMzctLjI3M2MuNDc5IDAgLjg4Ny4wODYgMS4yMjQuMjYuMzQyLjE3My42MjYuNDIxLjg1NC43NDUuMjI4LjMxOS40MDYuNzAyLjUzNCAxLjE0OC4xMzIuNDQyLjIzLjkzNC4yOTQgMS40Nzd2LjQ1OGE3LjQgNy40IDAgMCAxLS4zMDEgMS40NDkgNCA0IDAgMCAxLS41NiAxLjEzNWMtLjIzMy4zMTktLjUyLjU2NS0uODYyLjczOC0uMzM3LjE3My0uNzM2LjI2LTEuMTk2LjI2LS40NTIgMC0uODYtLjA5NC0xLjIyNC0uMjhhMi43ODIgMi43ODIgMCAwIDEtLjkzLS43ODcgMy43MjMgMy43MjMgMCAwIDEtLjU4OC0xLjE5IDUuMjM4IDUuMjM4IDAgMCAxLS4yMDUtMS40OTZabTEuNjQ4LS4xNDN2LjE0NGMwIC4zMzcuMDMyLjY1MS4wOTYuOTQzLjA2OC4yOTIuMTcuNTUuMzA3Ljc3My4xNDEuMjE4LjMyLjM5Mi41MzMuNTIuMjE5LjEyMi40NzcuMTg0Ljc3My4xODQuMzg3IDAgLjcwNC0uMDgyLjk1LS4yNDYuMjUtLjE2NC40NDItLjM4Ni41NzQtLjY2NC4xMzctLjI4Mi4yMzMtLjU5Ny4yODctLjk0M3YtMS4yMzdhMy4wMjMgMy4wMjMgMCAwIDAtLjE3LS43NTIgMi4wMTEgMi4wMTEgMCAwIDAtLjMzNi0uNjA5IDEuMzk5IDEuMzk5IDAgMCAwLS41MzMtLjQxYy0uMjE0LS4xLS40NjctLjE1LS43NTktLjE1LS4yOTYgMC0uNTUzLjA2NC0uNzcyLjE5MWExLjU2OSAxLjU2OSAwIDAgMC0uNTQuNTI3Yy0uMTM3LjIyMy0uMjQuNDgzLS4zMDguNzc5YTQuMjIyIDQuMjIyIDAgMCAwLS4xMDIuOTVabTEwLjA5MyAzLjg5N2EzLjk0NCAzLjk0NCAwIDAgMS0xLjQ4My0uMjY3IDMuMjc1IDMuMjc1IDAgMCAxLTEuMTIxLS43NTkgMy4zNjQgMy4zNjQgMCAwIDEtLjcwNC0xLjE0MSAzLjk5OSAzLjk5OSAwIDAgMS0uMjQ2LTEuNDE1di0uMjc0YzAtLjU3OS4wODQtMS4xMDMuMjUzLTEuNTcyLjE2OC0uNDcuNDAzLS44Ny43MDQtMS4yMDMuMy0uMzM3LjY1Ni0uNTk1IDEuMDY2LS43NzMuNDEtLjE3Ny44NTUtLjI2NiAxLjMzMy0uMjY2LjUyOSAwIC45OTIuMDg5IDEuMzg4LjI2Ni4zOTYuMTc4LjcyNS40MjkuOTg0Ljc1Mi4yNjUuMzIuNDYuNy41ODggMS4xNDIuMTMyLjQ0Mi4xOTkuOTMuMTk5IDEuNDYzdi43MDRoLTUuNzE1VjcyLjYxaDQuMDg3di0uMTNhMi4zMTIgMi4zMTIgMCAwIDAtLjE3Ny0uODM0IDEuNDE3IDEuNDE3IDAgMCAwLS40ODYtLjYyOGMtLjIxOC0uMTYtLjUxLS4yNC0uODc1LS4yNC0uMjczIDAtLjUxNy4wNi0uNzMxLjE3OC0uMjEuMTE0LS4zODUuMjgtLjUyNi41YTIuNjMgMi42MyAwIDAgMC0uMzI5Ljc5MmMtLjA3Mi4zMDUtLjEwOS42NS0uMTA5IDEuMDMydi4yNzRjMCAuMzIzLjA0My42MjQuMTMuOTAyLjA5MS4yNzQuMjIzLjUxMy4zOTcuNzE4LjE3My4yMDUuMzgyLjM2Ny42MjguNDg1LjI0Ni4xMTQuNTI3LjE3MS44NDEuMTcxLjM5NyAwIC43NS0uMDggMS4wNi0uMjQuMzEtLjE1OS41NzktLjM4NC44MDYtLjY3NmwuODY5Ljg0Yy0uMTYuMjMzLS4zNjcuNDU3LS42MjMuNjctLjI1NS4yMS0uNTY3LjM4MS0uOTM2LjUxNGEzLjcyNyAzLjcyNyAwIDAgMS0xLjI3Mi4xOThabTYuMTMtNS45NTRWNzdoLTEuNjQ4di03LjM5N2gxLjU1MmwuMDk1IDEuNThabS0uMjk0IDEuODQ1LS41MzQtLjAwN2E0LjgxMiA0LjgxMiAwIDAgMSAuMjE5LTEuNDQyYy4xNDYtLjQzNy4zNDYtLjgxMy42MDItMS4xMjguMjYtLjMxNC41Ny0uNTU2LjkzLS43MjQuMzYtLjE3NC43Ni0uMjYgMS4yMDItLjI2LjM1NiAwIC42NzcuMDUuOTY0LjE1LjI5Mi4wOTYuNTQuMjUzLjc0NS40NzIuMjEuMjE5LjM3LjUwMy40NzkuODU0LjExLjM0Ny4xNjQuNzczLjE2NCAxLjI3OVY3N2gtMS42NTR2LTQuNzg1YzAtLjM1Ni0uMDUzLS42MzYtLjE1OC0uODQxYS44OC44OCAwIDAgMC0uNDQ0LS40NDRjLS4xOTEtLjA5Mi0uNDMtLjEzNy0uNzE4LS4xMzctLjI4MiAwLS41MzUuMDYtLjc1OC4xNzgtLjIyNC4xMTgtLjQxMy4yOC0uNTY4LjQ4NS0uMTUuMjA1LS4yNjYuNDQyLS4zNDkuNzExYTIuOTM4IDIuOTM4IDAgMCAwLS4xMjMuODYxWm0tNDQuOTEzIDE4LjQ0VjgyLjVoMS42NTVWOTNoLTEuNDk3bC0uMTU4LTEuNTMxWm0tNC44MTItMi4wODR2LS4xNDRjMC0uNTYuMDY2LTEuMDcuMTk4LTEuNTMxLjEzMi0uNDY1LjMyNC0uODY0LjU3NC0xLjE5Ni4yNTEtLjMzOC41NTYtLjU5NS45MTYtLjc3My4zNi0uMTgyLjc2Ni0uMjczIDEuMjE3LS4yNzMuNDQ3IDAgLjgzOS4wODYgMS4xNzYuMjYuMzM3LjE3My42MjQuNDIxLjg2MS43NDUuMjM3LjMxOS40MjYuNzAyLjU2OCAxLjE0OC4xNC40NDIuMjQxLjkzNC4zIDEuNDc3di40NThhNy4zOTIgNy4zOTIgMCAwIDEtLjMgMS40NDljLS4xNDIuNDM3LS4zMy44MTYtLjU2OCAxLjEzNWEyLjQ1MyAyLjQ1MyAwIDAgMS0uODY4LjczOGMtLjMzNy4xNzMtLjczMS4yNi0xLjE4My4yNi0uNDQ2IDAtLjg1LS4wOTQtMS4yMS0uMjhhMi42NzQgMi42NzQgMCAwIDEtLjkwOS0uNzg3IDMuNzAzIDMuNzAzIDAgMCAxLS41NzQtMS4xOSA1LjQwOSA1LjQwOSAwIDAgMS0uMTk4LTEuNDk2Wm0xLjY0Ny0uMTQ0di4xNDRjMCAuMzM3LjAzLjY1MS4wOS45NDMuMDYzLjI5Mi4xNjEuNTUuMjkzLjc3My4xMzIuMjE4LjMwMy4zOTIuNTEzLjUyLjIxNC4xMjIuNDcuMTg0Ljc2NS4xODQuMzc0IDAgLjY4Mi0uMDgyLjkyMy0uMjQ2LjI0Mi0uMTY0LjQzMS0uMzg2LjU2OC0uNjY0YTMuMDggMy4wOCAwIDAgMCAuMjg3LS45NDN2LTEuMjM3YTMuMDE4IDMuMDE4IDAgMCAwLS4xNzEtLjc1MiAyLjAxMyAyLjAxMyAwIDAgMC0uMzM1LS42MDkgMS40MTIgMS40MTIgMCAwIDAtLjUyNi0uNDEgMS42NSAxLjY1IDAgMCAwLS43MzItLjE1Yy0uMyAwLS41NTYuMDY0LS43NjYuMTkxLS4yMS4xMjgtLjM4Mi4zMDMtLjUxOS41MjdhMi41OSAyLjU5IDAgMCAwLS4yOTQuNzc5IDQuNTA4IDQuNTA4IDAgMCAwLS4wOTYuOTVabTguNTU2LTMuNjM2VjkzaC0xLjY1NHYtNy4zOTdoMS42NTRabS0xLjc2NC0xLjk0MmMwLS4yNS4wODItLjQ1OC4yNDYtLjYyMi4xNjktLjE2OS40MDEtLjI1My42OTgtLjI1My4yOTEgMCAuNTIyLjA4NC42OS4yNTNhLjgzMS44MzEgMCAwIDEgLjI1My42MjIuODIzLjgyMyAwIDAgMS0uMjUzLjYxNWMtLjE2OC4xNjQtLjM5OS4yNDYtLjY5LjI0Ni0uMjk3IDAtLjUyOS0uMDgyLS42OTgtLjI0NmEuODM1LjgzNSAwIDAgMS0uMjQ2LS42MTVabTMuNTA0IDUuNzIydi0uMTU3YzAtLjUzNC4wNzgtMS4wMjguMjMzLTEuNDg0LjE1NS0uNDYuMzc4LS44NTkuNjctMS4xOTZhMy4wMjMgMy4wMjMgMCAwIDEgMS4wOC0uNzkzIDMuNTE1IDMuNTE1IDAgMCAxIDEuNDQ5LS4yODdjLjU0MiAwIDEuMDI1LjA5NiAxLjQ1LjI4Ny40MjguMTg3Ljc5LjQ1MSAxLjA4Ni43OTMuMjk2LjMzNy41MjIuNzM2LjY3NyAxLjE5Ni4xNTUuNDU2LjIzMi45NS4yMzIgMS40ODR2LjE1N2MwIC41MzMtLjA3NyAxLjAyOC0uMjMyIDEuNDgzLS4xNTUuNDU2LS4zOC44NTUtLjY3NyAxLjE5N2EzLjEzNSAzLjEzNSAwIDAgMS0xLjA4Ljc5MiAzLjU0IDMuNTQgMCAwIDEtMS40NDMuMjggMy42MDcgMy42MDcgMCAwIDEtMS40NTYtLjI4IDMuMTM2IDMuMTM2IDAgMCAxLTEuMDgtLjc5MiAzLjU1IDMuNTUgMCAwIDEtLjY3Ni0xLjE5NyA0LjU3OSA0LjU3OSAwIDAgMS0uMjMzLTEuNDgzWm0xLjY0OC0uMTU3di4xNTdjMCAuMzMyLjAzNC42NDcuMTAyLjk0M2EyLjQgMi40IDAgMCAwIC4zMjIuNzhjLjE0NS4yMjMuMzMyLjM5OC41Ni41MjYuMjI4LjEyNy41LjE5MS44MTMuMTkxLjMwNiAwIC41Ny0uMDY0Ljc5My0uMTkxLjIyOC0uMTI4LjQxNS0uMzAzLjU2MS0uNTI3YTIuNCAyLjQgMCAwIDAgLjMyMS0uNzc5Yy4wNzMtLjI5Ni4xMS0uNjEuMTEtLjk0M3YtLjE1N2MwLS4zMjktLjAzNy0uNjM5LS4xMS0uOTNhMi4zOSAyLjM5IDAgMCAwLS4zMjgtLjc4NiAxLjU3IDEuNTcgMCAwIDAtLjU2LS41MzMgMS41NDMgMS41NDMgMCAwIDAtLjgtLjE5OWMtLjMxIDAtLjU3OS4wNjYtLjgwNy4xOTlhMS41ODggMS41ODggMCAwIDAtLjU1My41MzNjLS4xNDYuMjI4LS4yNTMuNDktLjMyMi43ODZhNC4wNjcgNC4wNjcgMCAwIDAtLjEwMi45M1ptNy45NjgtMy42MjQgMS40MDggMi40NTUgMS40MzUtMi40NTVoMS44MTJsLTIuMjQyIDMuNjNMNjYuODQ5IDkzaC0xLjgxbC0xLjUwNS0yLjU1N0w2Mi4wMyA5M2gtMS44MThsMi4zMjQtMy43NjctMi4yMzUtMy42M2gxLjgwNFptNy43OTYgMFY5M2gtMS42NTR2LTcuMzk3aDEuNjU0Wm0tMS43NjMtMS45NGMwLS4yNTEuMDgyLS40NTkuMjQ2LS42MjMuMTY4LS4xNjkuNC0uMjUzLjY5Ny0uMjUzLjI5MiAwIC41MjIuMDg0LjY5LjI1M2EuODMxLjgzMSAwIDAgMSAuMjUzLjYyMi44MjMuODIzIDAgMCAxLS4yNTMuNjE1Yy0uMTY4LjE2NC0uMzk4LjI0Ni0uNjkuMjQ2LS4yOTYgMC0uNTI5LS4wODItLjY5Ny0uMjQ2YS44MzQuODM0IDAgMCAxLS4yNDYtLjYxNVptOC4zMyA3LjgwNlY4Mi41aDEuNjU0VjkzaC0xLjQ5N2wtLjE1Ny0xLjUzMVptLTQuODEzLTIuMDg1di0uMTQ0YzAtLjU2LjA2Ni0xLjA3LjE5OC0xLjUzMS4xMzMtLjQ2NS4zMjQtLjg2NC41NzUtMS4xOTYuMjUtLjMzOC41NTYtLjU5NS45MTYtLjc3My4zNi0uMTgyLjc2NS0uMjczIDEuMjE2LS4yNzMuNDQ3IDAgLjg0LjA4NiAxLjE3Ni4yNi4zMzguMTczLjYyNS40MjEuODYyLjc0NS4yMzcuMzE5LjQyNi43MDIuNTY3IDEuMTQ4LjE0MS40NDIuMjQyLjkzNC4zIDEuNDc3di40NThhNy4zOTIgNy4zOTIgMCAwIDEtLjMgMS40NDljLS4xNDEuNDM3LS4zMy44MTYtLjU2NyAxLjEzNWEyLjQ1MSAyLjQ1MSAwIDAgMS0uODY4LjczOGMtLjMzOC4xNzMtLjczMi4yNi0xLjE4My4yNi0uNDQ3IDAtLjg1LS4wOTQtMS4yMS0uMjhhMi42NzQgMi42NzQgMCAwIDEtLjkxLS43ODcgMy43MDMgMy43MDMgMCAwIDEtLjU3My0xLjE5IDUuNDA1IDUuNDA1IDAgMCAxLS4xOTktMS40OTZabTEuNjQ4LS4xNDR2LjE0NGMwIC4zMzcuMDMuNjUxLjA4OS45NDMuMDYzLjI5Mi4xNjEuNTUuMjkzLjc3My4xMzMuMjE4LjMwNC4zOTIuNTEzLjUyLjIxNC4xMjIuNDcuMTg0Ljc2Ni4xODQuMzczIDAgLjY4MS0uMDgyLjkyMy0uMjQ2YTEuNjggMS42OCAwIDAgMCAuNTY3LS42NjQgMy4wOCAzLjA4IDAgMCAwIC4yODctLjk0M3YtMS4yMzdhMy4wMjMgMy4wMjMgMCAwIDAtLjE3LS43NTIgMi4wMTEgMi4wMTEgMCAwIDAtLjMzNi0uNjA5IDEuNDEgMS40MSAwIDAgMC0uNTI2LS40MSAxLjY1IDEuNjUgMCAwIDAtLjczMS0uMTVjLS4zMDEgMC0uNTU2LjA2NC0uNzY2LjE5MS0uMjEuMTI4LS4zODMuMzAzLS41Mi41MjdhMi41OSAyLjU5IDAgMCAwLS4yOTQuNzc5IDQuNTA4IDQuNTA4IDAgMCAwLS4wOTUuOTVabTEwLjA1MiAzLjg5N2EzLjk0NCAzLjk0NCAwIDAgMS0xLjQ4My0uMjY3IDMuMjczIDMuMjczIDAgMCAxLTEuMTIxLS43NTkgMy4zNjEgMy4zNjEgMCAwIDEtLjcwNC0xLjE0MSAzLjk5NyAzLjk5NyAwIDAgMS0uMjQ2LTEuNDE1di0uMjc0YzAtLjU3OS4wODQtMS4xMDMuMjUzLTEuNTcyLjE2OC0uNDcuNDAzLS44Ny43MDQtMS4yMDMuMy0uMzM3LjY1Ni0uNTk1IDEuMDY2LS43NzMuNDEtLjE3Ny44NTUtLjI2NiAxLjMzMy0uMjY2LjUyOSAwIC45OTEuMDg5IDEuMzg4LjI2Ni4zOTYuMTc4LjcyNC40MjkuOTg0Ljc1Mi4yNjUuMzIuNDYuNy41ODggMS4xNDIuMTMyLjQ0Mi4xOTguOTMuMTk4IDEuNDYzdi43MDRoLTUuNzE0Vjg4LjYxaDQuMDg3di0uMTNhMi4zMTQgMi4zMTQgMCAwIDAtLjE3Ny0uODM0IDEuNDE3IDEuNDE3IDAgMCAwLS40ODYtLjYyOGMtLjIxOC0uMTYtLjUxLS4yNC0uODc1LS4yNC0uMjczIDAtLjUxNy4wNi0uNzMxLjE3OC0uMjEuMTE0LS4zODUuMjgtLjUyNi41LS4xNDIuMjE4LS4yNTEuNDgyLS4zMjkuNzkyLS4wNzIuMzA1LS4xMDkuNjUtLjEwOSAxLjAzMnYuMjc0YzAgLjMyMy4wNDMuNjI0LjEzLjkwMi4wOTEuMjc0LjIyMy41MTMuMzk2LjcxOC4xNzQuMjA1LjM4My4zNjcuNjMuNDg1LjI0NS4xMTQuNTI2LjE3MS44NC4xNzEuMzk3IDAgLjc1LS4wOCAxLjA2LS4yNC4zMS0uMTU5LjU3OS0uMzg0LjgwNi0uNjc2bC44NjguODRjLS4xNTkuMjMzLS4zNjYuNDU3LS42MjIuNjctLjI1NS4yMS0uNTY3LjM4MS0uOTM2LjUxNGEzLjcyNyAzLjcyNyAwIDAgMS0xLjI3Mi4xOThaIi8+PHBhdGggZmlsbD0iIzNCOTExQyIgZD0iTTEwOC41IDcyLjczMlY4N2gtMi4zNTRWNzUuNTI1bC0zLjQ4NiAxLjE4MnYtMS45NDNsNS41NTctMi4wMzJoLjI4M1ptMTQuMzA5IDUuOTY3djIuMzI0YzAgMS4xMTQtLjExMSAyLjA2NC0uMzMyIDIuODUyLS4yMTUuNzgxLS41MjcgMS40MTYtLjkzOCAxLjkwNC0uNDEuNDg5LS45MDEuODQ3LTEuNDc0IDEuMDc1LS41NjcuMjI3LTEuMjAxLjM0MS0xLjkwNC4zNDEtLjU2IDAtMS4wODEtLjA3MS0xLjU2My0uMjE0YTMuODIgMy44MiAwIDAgMS0xLjI4OS0uNjc0IDQuMDIyIDQuMDIyIDAgMCAxLS45ODYtMS4xODJjLS4yNjctLjQ4OC0uNDc2LTEuMDcxLS42MjUtMS43NDgtLjE0NC0uNjc3LS4yMTUtMS40NjItLjIxNS0yLjM1NFY3OC43YzAtMS4xMi4xMS0yLjA2NC4zMzItMi44MzIuMjIxLS43NzUuNTM3LTEuNDAzLjk0Ny0xLjg4NWEzLjYxIDMuNjEgMCAwIDEgMS40NjUtMS4wNjRjLjU3My0uMjIxIDEuMjExLS4zMzIgMS45MTQtLjMzMi41NjYgMCAxLjA4Ny4wNzIgMS41NjMuMjE1LjQ4MS4xMzYuOTExLjM1NSAxLjI4OS42NTQuMzg0LjMuNzA5LjY5Ljk3NiAxLjE3Mi4yNzQuNDc1LjQ4MiAxLjA1MS42MjUgMS43MjkuMTQzLjY3LjIxNSAxLjQ1MS4yMTUgMi4zNDNabS0yLjM1NCAyLjY1N3YtMy4wMDhjMC0uNTY3LS4wMzItMS4wNjUtLjA5Ny0xLjQ5NWE0LjQzMSA0LjQzMSAwIDAgMC0uMjkzLTEuMTAzIDIuMjAxIDIuMjAxIDAgMCAwLS40NjktLjc0MiAxLjY1NiAxLjY1NiAwIDAgMC0uNjQ0LS40MiAyLjI4IDIuMjggMCAwIDAtLjgxMS0uMTM3Yy0uMzY1IDAtLjY5LjA3Mi0uOTc3LjIxNWExLjc1NSAxLjc1NSAwIDAgMC0uNzIyLjY2NGMtLjE5Ni4zMDYtLjM0NS43MS0uNDQ5IDEuMjExLS4wOTguNDk1LS4xNDcgMS4wOTctLjE0NyAxLjgwN3YzLjAwOGMwIC41NzIuMDMzIDEuMDc3LjA5OCAxLjUxMy4wNjUuNDM2LjE2My44MS4yOTMgMS4xMjMuMTMuMzA2LjI4Ni41Ni40NjguNzYyLjE4OS4xOTUuNDA0LjMzOC42NDUuNDMuMjQ3LjA5LjUxOC4xMzYuODExLjEzNi4zNzEgMCAuNjk5LS4wNzEuOTg2LS4yMTQuMjg2LS4xNDQuNTI3LS4zNzIuNzIzLS42ODQuMTk1LS4zMi4zNDEtLjczMy40MzktMS4yNC4wOTgtLjUwOC4xNDYtMS4xMTcuMTQ2LTEuODI2Wm0xNi41NDgtNC45MjJoMi4zNjNWODdoLTIuMTc3bC0uMTg2LTIuNDQxdi04LjEyNVptLjM0MiA1LjY1NC45MjgtLjAyYzAgLjc0My0uMDcyIDEuNDI2LS4yMTUgMi4wNTFhNS4wMzMgNS4wMzMgMCAwIDEtLjY0NSAxLjYzMWMtLjI4Ni40NjItLjY1MS44Mi0xLjA5MyAxLjA3NC0uNDQzLjI1NC0uOTY3LjM4MS0xLjU3My4zODFhMy44NiAzLjg2IDAgMCAxLTEuMjk5LS4yMDUgMi41OCAyLjU4IDAgMCAxLS45OTYtLjY2NGMtLjI4LS4zLS41MTEtLjY5LS42OTMtMS4xNzJsLS4zMDMtMi42NjZoMS4zMzhjMCAuNTg2LjA0OSAxLjA2NC4xNDcgMS40MzYuMDk3LjM3LjIzNC42NTcuNDEuODU5LjE3Ni4xOTUuMzgxLjMzMi42MTUuNDEuMjQxLjA3OC41MDEuMTE3Ljc4MS4xMTcuNTE1IDAgLjkzOC0uMDc4IDEuMjctLjIzNGExLjkyIDEuOTIgMCAwIDAgLjc5MS0uNjc0IDIuODMgMi44MyAwIDAgMCAuNDEtMS4wMjVjLjA4NS0uMzk3LjEyNy0uODMuMTI3LTEuM1ptLTYuODk1LTUuNjU0aDIuMzQ0djE0LjYyOWgtMi4zNDR2LTE0LjYzWm0xOC41MzggMGgyLjEzOXYxMC4yNzNjMCAuOTUtLjIwMiAxLjc1OC0uNjA2IDIuNDIyYTMuODk2IDMuODk2IDAgMCAxLTEuNjg5IDEuNTE0Yy0uNzIzLjM1MS0xLjU2LjUyNy0yLjUxLjUyNy0uNDA0IDAtLjg1My0uMDU5LTEuMzQ4LS4xNzZhNS41MzMgNS41MzMgMCAwIDEtMS40MjYtLjU2NiAzLjU0MSAzLjU0MSAwIDAgMS0xLjE0Mi0xLjAwNmwxLjEwMy0xLjM4N2MuMzc4LjQ1Ljc5NS43NzggMS4yNS45ODcuNDU2LjIwOC45MzUuMzEyIDEuNDM2LjMxMi41NCAwIC45OTktLjEgMS4zNzctLjMwM2EyLjA1IDIuMDUgMCAwIDAgLjg4OS0uODY5Yy4yMDgtLjM4NC4zMTItLjg1My4zMTItMS40MDZ2LTcuOTNsLjIxNS0yLjM5MlptLTcuMTc4IDUuNHYtLjIwNWMwLS44LjA5OC0xLjUzLjI5My0yLjE4OC4xOTUtLjY2NC40NzUtMS4yMzMuODQtMS43MDkuMzY1LS40ODEuODA3LS44NSAxLjMyOC0xLjEwMy41MjEtLjI2IDEuMTEtLjM5IDEuNzY4LS4zOS42ODMgMCAxLjI2Ni4xMjMgMS43NDguMzcuNDg4LjI0OC44OTUuNjAzIDEuMjIgMS4wNjUuMzI2LjQ1Ni41OCAxLjAwMi43NjIgMS42NC4xODkuNjMyLjMyOSAxLjMzNS40MiAyLjExdi42NTRhMTAuNTggMTAuNTggMCAwIDEtLjQzIDIuMDcgNS42OSA1LjY5IDAgMCAxLS44IDEuNjIxIDMuNDIgMy40MiAwIDAgMS0xLjIzMSAxLjA1NWMtLjQ4Mi4yNDgtMS4wNTEuMzcxLTEuNzA5LjM3MWEzLjc3OCAzLjc3OCAwIDAgMS0xLjc0OC0uNCAzLjk4IDMuOTggMCAwIDEtMS4zMjgtMS4xMjMgNS4zMDUgNS4zMDUgMCAwIDEtLjg0LTEuNyA3LjQ5MyA3LjQ5MyAwIDAgMS0uMjkzLTIuMTM4Wm0yLjM1NC0uMjA1di4yMDVjMCAuNDgyLjA0NS45MzEuMTM2IDEuMzQ4LjA5OC40MTYuMjQ1Ljc4NC40NCAxLjEwMy4yMDIuMzEzLjQ1Ni41Ni43NjIuNzQyLjMxMi4xNzYuNjguMjY0IDEuMTAzLjI2NC41NTMgMCAxLjAwNi0uMTE3IDEuMzU4LS4zNTIuMzU4LS4yMzQuNjMxLS41NS44Mi0uOTQ3YTQuOCA0LjggMCAwIDAgLjQxLTEuMzQ4di0xLjc2N2E0LjMgNC4zIDAgMCAwLS4yNDQtMS4wNzQgMi44NjcgMi44NjcgMCAwIDAtLjQ3OS0uODcgMS45OTYgMS45OTYgMCAwIDAtLjc2MS0uNTg1Yy0uMzA2LS4xNDQtLjY2OC0uMjE1LTEuMDg0LS4yMTUtLjQyNCAwLS43OTEuMDkxLTEuMTA0LjI3My0uMzEyLjE4Mi0uNTcuNDMzLS43NzEuNzUyYTMuNzEgMy43MSAwIDAgMC0uNDQgMS4xMTQgNi4wNjQgNi4wNjQgMCAwIDAtLjE0NiAxLjM1N1ptMTUuNjg2LTguODQ4LTUuNTM4IDE1LjQ0aC0xLjg0NWw1LjU0Ny0xNS40NGgxLjgzNlptNC4wODQgNS44MDFWODdoLTIuMzU0Vjc2LjQzNGgyLjIxN2wuMTM3IDIuMTQ4Wm0tLjM4MSAyLjc0NC0uODAxLS4wMWE3LjEgNy4xIDAgMCAxIC4yNzQtMi4wMjEgNC44NDEgNC44NDEgMCAwIDEgLjgwMS0xLjYxMSAzLjUxMiAzLjUxMiAwIDAgMSAxLjMwOC0xLjA2NWMuNTI3LS4yNTQgMS4xMzYtLjM4IDEuODI2LS4zOC40ODIgMCAuOTIyLjA3IDEuMzE5LjIxNC40MDMuMTM3Ljc1Mi4zNTUgMS4wNDUuNjU0LjI5OS4zLjUyNy42ODQuNjgzIDEuMTUzLjE2My40NjguMjQ0IDEuMDM1LjI0NCAxLjY5OVY4N2gtMi4zNTN2LTYuODM2YzAtLjUxNC0uMDc4LS45MTgtLjIzNS0xLjIxYTEuMjk0IDEuMjk0IDAgMCAwLS42NTQtLjYyNmMtLjI4LS4xMy0uNjE1LS4xOTUtMS4wMDYtLjE5NS0uNDQyIDAtLjgyLjA4NC0xLjEzMy4yNTQtLjMwNi4xNjktLjU1Ni40LS43NTEuNjkzYTMuMDg4IDMuMDg4IDAgMCAwLS40MyAxLjAxNiA1LjMyNiA1LjMyNiAwIDAgMC0uMTM3IDEuMjNabTYuNTUzLS42MjUtMS4xMDQuMjQ0YzAtLjYzOC4wODgtMS4yNC4yNjQtMS44MDZhNC42IDQuNiAwIDAgMSAuNzkxLTEuNTA0IDMuNzIgMy43MiAwIDAgMSAxLjI5OS0xLjAyNmMuNTE0LS4yNDcgMS4xMDMtLjM3IDEuNzY3LS4zN2E0LjMyIDQuMzIgMCAwIDEgMS40NDYuMjI0Yy40MjkuMTQzLjc5NC4zNzEgMS4wOTMuNjg0LjMuMzEyLjUyOC43MTkuNjg0IDEuMjIuMTU2LjQ5NS4yMzQgMS4wOTQuMjM0IDEuNzk3Vjg3aC0yLjM2M3YtNi44NDZjMC0uNTM0LS4wNzgtLjk0Ny0uMjM0LTEuMjRhMS4yMTcgMS4yMTcgMCAwIDAtLjY0NS0uNjA1Yy0uMjgtLjExOC0uNjE1LS4xNzYtMS4wMDYtLjE3Ni0uMzY0IDAtLjY4Ni4wNjgtLjk2Ni4yMDUtLjI3NC4xMy0uNTA1LjMxNi0uNjk0LjU1NmEyLjQ2MyAyLjQ2MyAwIDAgMC0uNDI5LjgxMSAzLjQ0NiAzLjQ0NiAwIDAgMC0uMTM3Ljk5NlptMTAuNDIyLTQuNzU2aC44NWMuMjggMCAuNTExLS4wMzkuNjkzLS4xMTdhLjkzNi45MzYgMCAwIDAgLjQxLS4zNDIuODc3Ljg3NyAwIDAgMCAuMTQ3LS41MDguODI3LjgyNyAwIDAgMC0uMjc0LS42MTVjLS4xNzUtLjE4Mi0uNDcyLS4yNzMtLjg4OC0uMjczLS4zMTkgMC0uNTkuMDY4LS44MTEuMjA1YS42MTYuNjE2IDAgMCAwLS4zMjIuNTU3aC0xLjc3OGMwLS40NjMuMTMxLS44NTcuMzkxLTEuMTgyYTIuNDM1IDIuNDM1IDAgMCAxIDEuMDQ1LS43NDJjLjQzNi0uMTcuOTExLS4yNTQgMS40MjYtLjI1NC41OTIgMCAxLjExMy4wODQgMS41NjIuMjU0LjQ1LjE2My43OTguNDA3IDEuMDQ1LjczMi4yNTQuMzI2LjM4MS43My4zODEgMS4yMTEgMCAuMzcxLS4xMTcuNy0uMzUxLjk4Ni0uMjI4LjI4Ny0uNTQ3LjUxMS0uOTU4LjY3NC0uNDAzLjE1Ni0uODc4LjIzNS0xLjQyNS4yMzVoLTEuMTQzdi0uODJabTAgMS4yMjF2LS44aDEuMTQzYy42MDUgMCAxLjEyMy4wNyAxLjU1Mi4yMTQuNDMuMTM3Ljc1Ni4zNDguOTc3LjYzNS4yMjguMjg2LjM0Mi42NTcuMzQyIDEuMTEzIDAgLjQ4Mi0uMTM3Ljg5Mi0uNDEgMS4yMy0uMjc0LjM0LS42NDguNTk2LTEuMTIzLjc3MmE0LjQ4NCA0LjQ4NCAwIDAgMS0xLjU4Mi4yNjRjLS40ODkgMC0uOTY0LS4wNzgtMS40MjYtLjIzNWEyLjY0NiAyLjY0NiAwIDAgMS0xLjEzMy0uNzYxYy0uMjkzLS4zNDUtLjQzOS0uNzk4LS40MzktMS4zNThoMS43ODdjMCAuMjU0LjExNy40NzIuMzUxLjY1NC4yMzUuMTgzLjU1LjI3NC45NDcuMjc0LjQyNCAwIC43MzYtLjA5MS45MzgtLjI3NGEuOTExLjkxMSAwIDAgMCAuMzEyLS43MDNjMC0uMjQ3LS4wNTgtLjQ0Ni0uMTc1LS41OTVhLjkxNS45MTUgMCAwIDAtLjQ3OS0uMzIzIDIuMjUxIDIuMjUxIDAgMCAwLS43MzItLjEwN2gtLjg1WiIvPjwvZz48ZGVmcz48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2MiIHRyYW5zZm9ybT0ibWF0cml4KC4wMDUwMyAwIDAgLjAxNzk1IDAgLS45MjcpIi8+PC9wYXR0ZXJuPjxmaWx0ZXIgaWQ9ImIiIHdpZHRoPSIyMDAiIGhlaWdodD0iNTYiIHg9IjAiIHk9IjUyIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIzIi8+PGZlQ29tcG9zaXRlIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzExMDdfMzM3OCIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTEwN18zMzc4IiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PGltYWdlIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3QkRBQUlCQVFFQkFRSUJBUUVDQWdJQ0FnUURBZ0lDQWdVRUJBTUVCZ1VHQmdZRkJnWUdCd2tJQmdjSkJ3WUdDQXNJQ1FvS0Nnb0tCZ2dMREFzS0RBa0tDZ3IvMndCREFRSUNBZ0lDQWdVREF3VUtCd1lIQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnci93Z0FSQ0FDZkFNY0RBUkVBQWhFQkF4RUIvOFFBSFFBQUFnSURBUUVCQUFBQUFBQUFBQUFBQkFVREJnSUhDQUVKQVAvRUFCb0JBQUlEQVFFQUFBQUFBQUFBQUFBQUFBRUNBQU1FQlFiLzJnQU1Bd0VBQWhBREVBQUFBUHBWM09SRVlwWUtTdVprNEpzSlFqWkdaUS9wSlNTUUdGY2tKL0dBT29yckF5NEZXS096cmNxRW9NY2sxaFl2NEFPQ3V1RlZnOWthcVc2Rm1HWlE0U1NTR0xKMWtobjZCYmFCbmtaV1NLMnJkZ3RqTlpNQjZEcTJ4QzQyQUFFU3Z2QXBHUmh5bGdKN0pKSlBHTUJsTUdLcmJRc3NxRFpKcEg5ZDlqb2V5VTJOMGNoR3hZYzg2c21JaHdNaEF3STdRSUQyR09DS0E4a3VTY3pGNE02cm5RRjVMQm5GZEs5aXFzdE5GbGhxc1ByYWVQeVYxT1VlR2pCUHJmTVFKMGtESXI2MGNRcFhaQW0wUE9wejBDS3dCTXJCRGtBVURNWllsZXhVMjJtbDJDdkpCb3ZSbS9DZWtDT29UcUpaWFdiRVJBVHErQmtSQktzMHB0Wm1ZMkFkbEJhSFZ4aENRWllGTnh6NkxEVTVCaGlyVGthVWdTQVNDRmdNNHAybW1xVzAzSEhzUVc1OVM3TTBVTGRMTElsaDRPSml3U1c1Q2xqZVN3bzlrU09VWnVsaGFuM05va0E5SXdCWHNzQkZPMVVlaU9zbW5SM1I1Mm45bVFGYlNBMWtnczYySFYyWXFZbVgyRms0c0VSNHJNVkxKWFlyTGR6dG1VYk9UT0RJejBGUXlGbUs2cmVRKy94TmJNSXdwU3U2dEZucmV4MVc0b3dzT1pScUdkV0tRcjRnRlJYd2Zabk0yZW1aU1NDRmllQ1JtUkdLQWVaZXh5dFB1Z2pSZ0M5dlJpam5CY0ljRlpwVFkvU3cySDh5NEZER2pwRzNYemQ0TGhSYlZHVUtEZUtlZHFkSFJGK1hpdlB2dkhaNE5NVVZ5amJXVmZkZXpua3hrN0tHeU4wdEVXUHl6cXBqa3NKTUNzck9FNkI1ZlM4YUFPa1JRZ01IRjVPeDlIdEsvRnhGaTZlMCs5NTFOYlVuNS9RMDZXM2YwZWZhcVdjQWd5RjFXNnIxNXMzbHRvWi9UZGtWeEpoSTJMbXZmVlhSRWVSU0M4SVBLdVBkMjliazRPeGRIYjNvUE4reVU3SjBkWmdkRTdNQ2V3UFZaNmpIVldVKzZsYTZ1cTJzVlY4TUF6MXdFWVIyZGRqV3ByeFRkR3dUbWN5NHQvWmwrUGdITDBOSmRQanltZHo0OXVzYjZkNWJNTXNpOWcyUXNhM1dNQTdLeVEwTW40VDhaSUNaUmY4QUpUbjc2R3RuMEgxWXVzTDZocW4wSGg2WFgxbWZpSE50K1dlckRjRnQrMDFaK0sxeUt0R1Q3OTJWY0V3L0x1cTc2emFjL1VGK2I1TDQ5OXlldnRQdGNUVWVYVncxeStyMW5zeGR5Y3JxL0ZEUG9vVmxaQzJiVnVxMWJUYmRpRUtXN0FLd05YWGdlN3RtSDV0Wk5zZGxmYm5vL05wc1cvaDdsOVRxbnM4Vmh2NTNPZmx2V1dWNjNmcXZKRGMvZVo1djFEcnJjajZVK1Q5RHJsYk5iby9oV3lLR1JhbUl3MWlWZ2wrNDByMnVYcEhSbnFPUGVRMCt1blU0L0M5ZHZGMmZWdC9UaXl1eTFEa2VnbnNxWmRmZ1dMQjFMVnlld2gyNGVoNnBxQjF1TlJWRTNWVzFveVdpRkdVNlp6MjZ4cmZTSFR6WFdxMm93ODE2TSs2MHRnSTBEWW5ZVmE3M3dYY3Y3SzZsZEtMWmpzT1BwV3VuUlpiVTUxbUw4Q3pEVlFxZUJYeXVMU25zbHhyZnd5YUcyNTduRjFlYXJqZlZzaFdUS0xYaTBhWDMwUlFERVdLMVFLMldHVjlvL3dETWRiR0NJbDFWSkZrWmtaR2NFSkphR0d5UmhmekdZTmxJTzZwbWFtMnBzYW1Zc1Q2M3dkWnlLWmVvVnMvL3hBQWlFQUFDQVFRQ0F3RUJBUUFBQUFBQUFBQUJBZ01BQkFVU0JoRVFFeFVVSUJiLzJnQUlBUUVBQVFJRHJwbDhEeVBQUUE4YWxldkFvZUJYZGRFTW5nVUZWZkk4N0Y5dXhRb0FMcjFzRFJveDZDZzIyL1lQZmV4Y3liaDFLRmZQVys0YitPeSs0ZmYyZXd2dDRGS3lzamc5OXF4b1A3REo3MWxlWDJDUU1IMjJMZGg5aFhhbGFYeDM2OVBVWXRQWEttMUZmWUpGZnZ1dGd3Y01qS3l2c0cxMEs2OWF6ajFwRGRVV0VpeUI5aTFBaGxkSFZsYmJiejBWQ1RvbHFZNzJVdHNIU1JXL2hhVXEyNGszNy9sZ0thcm9FMG9Xa1lHdXhTMTJHM0Rxd3J2dmJ5ZkY2ckNveUtGYit6ZFNwNzdGRm8yRk5XNGs5bnV3R1Q5bUR2bUxKZHdZV0pWWlNtb1RkSHJvTExVWlZ6V3Voak1QRG8vejhaUzlpUVgwT0pTSmRGajBrak5vSWtyVUs2SXF0dDNzREllSVRBOFRrdUdTTEx4NEdCSVpGaUhxQWR0VlFMMDRTaE1KVWRBVmxyaUtCT0hDVGxjZkxzcmI0QWdQSEdnSVpyZFU5ZGQ5dFF4bC9mV2Q1eFhJU1NPL0RuRjV4VzRtdTdOdVJHOHZvc2xFT2ZLSDRjVGtYNU5qK1NmUXptVSszeHUrZzVjOGtFN1N0ZWw4ZlNDRVJSekRLY2x1WkxkNGM5eWxSV092L3Jkd0ZieDVsaGFVTFBJYUk5amlwS2E1anVwY2JHV1dPRTV6bEY3M2JTR25NVWtrc0xWY09jL1BuamZmYnVyMlhrbHpsSWVSSzhkeFkzc3VTdXNNYjJ3dGNoQjN4bDhobmNrUmo3N0g0eVg5K3k1bjYzMkh2NU12K21lNEN3WFZ2UDhBdFc4bHl1VHkrSXkvK3JiUDIrZXVzdmFaV1hML0FIL3Qvcm5uTm4rTDVueHhpVGlmampEL0FCeGh6aC9pL0YrT01OOGFiRVdlTUdOK2RIaXphQmZ6QzMvL3hBQkVFQUFCQXdJREJBVUlCd01OQUFBQUFBQUJBQUlEQkJFU0lURUZFeUpCRUJReVVaRWdJMEpUWVhHQm9UQXpRRkppY3BJVlErRVdKVFExUkVWUVk0T1RvckhSLzlvQUNBRUJBQU0vQS90aDEveGUzMEI2RDVadXJLeXNzdWdjL3NZNkI1QllicS9RVy9ZejBrNUlKZ0NBRi9zM0ZheTU5QUpMUWRQcGJmUUJBbEFLNFdDUXQ4aTMyVXVrYzRoWnJDTGtvT0YybS91NmJmUVc2TCtSWldGMXRDczMzWG1PRmlNR0pnSFJYVGJRcVdUVis5YTBjSXZwbWk2K0pPQjBXT2plQ0hhZWdNMEdVV2p1MmUyTEhvc242cC9jaUJtRmJ5d3JvZEFLQWFjK1NnZXlvTWU2N2JmcW5rcHQxdmRxMW1NazJIT0xEelZxaDJGWHlLdFJTRERmTFRIaCthY0tFWEI3UjFkZm9DWlpNYWVGWTJIQzNOU0FZM0l0S3NyOUYyK1NVVWQyNzhxRWtjOXBuTzQyOXFIQnk2QTdhRlljRWd5SGJmZm1pNlRFaTdNcU03T2tFcm1BWlp5ZG40b0dnR0RCWXVQWTBXRTJUNHptc1dxdm9yY05rTWRpRmMzc2dWa3MwZWdkRjB3aFdSM1RyZmRPaUlpbjRiZWNIOW8zbkx2UlRUVzF1RGQ4dXg3eXFoanlPb04xOWIvQlZEalk3UEgrNm54N05mUHZYTk5oMkk4VnZoelEvWnJMdnZtY3pIaCtTQk4xdk1naGJpUUhORDBrNTdzWEpEbWdyS3lCVFFMb3F0ZVRHNnNrSXVjc1NyUzBqcjA5bW5odEtjbFdWVy9GVFV1ZUdPYUc0dmNzSTFUQlN2TXIyanpaN1NpNnJVUFlZN2J3Wnh3NEJvcVhsT3o5U3d6Vmtzc2ppTXM1R0JxaXhsMWphL2NvM3VhNEJQOEEyUEp1QysrWDFac2RWdEdCeldOclpodzNzWHJhcGVHamFjK2YrWVZIRTBXMXRxaDFLS2FOMWp2czdIMktUMXAvVW1zMlJqbGZjdWxPcFZOZkRqelcyUk05b3E5SEg5MkZ0ZWFzWkZMVWpDWGNYbXdvajZhZEZzMHVwWjhMOFF6YXRxbis4SnYxcWFyZkwxMnNlNEFDMklyWWRRQzJDdEFmYmhEeGE2RXRXLzNsWU1XZk5PM3B6eTVxb2lNSWJVdlkwV3ZZcVIyTEhmUDBrSThXOHFIY2JTQUFmWW5PSk51ZmNtbW1tWTk1dTVsZ1BpaTZMQ1E0WjhsMWV4Rjh6eld6Ni9aL1ZHUXZjUzBYdmtGSExJTE53aHJMV1FiTGNIUmJiclg0R1R0YjNjQ3IyMFVQV3FyR0hQMEkwTmxuYTRVbEpUNE1XV2VTZG5aM0pjV0x2UmFkNHpVSjF1S1h3Um1HN3gzUXRvalRNdUNNMURneWc1ZXFDZ1pBOHViK0cyQUN5cEd0MDhRMVFidmVobkQzZ05VY1p0LzFaRnh1R2srQ0wzV2lZVC9xTlRJbllIaXh0NndKb2JaenMvWkwvQmJOcVlYN1BueENRM0lLcVdRTnFXamVSM0dKOFRzUUh2N2tiOFlHbmVudmRaclI4RktYWE1hb3VwUlRCckxtUENUOXdwOVM2Rmp6b0NWeGRwWGhzSnVmY3U2WmUxRUVFT1ZNSDRpN0VteTZSa0w4S01kUHZkM2V6dWFscUtSODBSZFpwRjd1NzAyVURpZmU0dW91cnRxMzA5Mms4MVJXL282aXBNQmtnTGNZdTJ5YUFUWjJXcXBLV1hESkJaNUYxczk4NGJidEcySWhiSXFHUzlYcVlYdmlqeHlXOW50VkxVeHNtaWtGM3huSytZeTBWVFIrZHA1bk1JN2xzbXJkL09VZlYzOHA2Y2EvbWFxZ05kTkRMdm9mV1V1WnQ3UnFtc0dDbGphd1g5SWFyYWxSR2FqckxkMk80S1JrVyttbkxyWlpvTEMyWGlBMDFaZE9oODFKc3Ryb2NyVE5aWXFocjZ0OVRITnVnNTJUY0hDUGlGVUQ2cHpIL2xmL0FPcmEyejI3Nm9vM3NqY2JOZGJWTzNWeWVhaU1tN2FibE1taWRDVzVGVlRJOTF3MlBLeWxkZHp6R010TUNsa2gzYjNnZDNDcGNWNDV4My9WMlZiTWJ2cVNSM1lVSGdoNysxMmpaZFpjWkh5T0pQTHVWdWFOTGkzYnlNVGJIM0lVOHU5amtjTGFGU09uZFBKTWJ2MXRrb1hSUEV6TDRyWVNxZUNRVGJNM3JEWWZoeitDWnRGckpIUlhlRytjZTd2VlBzeUdSc2xMdkhPNTN5VVdIK3FJTCswTFpoZVgvd0FuYWYyWnFnYnhuWnpHdE9yWWxVMVROMFhXWjNZVStuWmhiR3cydGs1dDdxWitHTWNER205aHJxampra3A0Y0prNzNISlZMaGhuaGlsSDRtTFpnek95amNqMFp5QUZUNzh2bzNTUnNPalQ2S3BqKzYrYXBqNkR2RlF1T0hqOFVCblkrS0RlL3dBVXpuZnhVZXQzK0tqL0FCK0lUVDZUdmttK2s1NThFemxqOFF2dlNQOEFrby9XUFRSbzkveVRQdnYrU2l2Ykc5RGRuZHlPdnlCQ21rT0tZV2Jia1ZEb01YaW9OTHZ2NzFDek1sL3lWRXdYYzl3Ris1Yk41Ty80S25FbUprNStJVGVWVDhsLy84UUFKaEFBQXdBQ0FnSUNBZ0lEQVFBQUFBQUFBQUVSSVRGQlVSQmhjWUdSb2JIUklNSGg4UC9hQUFnQkFRQUJQekpJZmk2Y0U0TUppOFpnZ3ZJUkQ4MjBJb1lRd2hQOFZDWmJLa09UQlJNWW9vUHpMOS9CY0RuL0FJQm9KZzJoMDhHYW9xN0ZnMUNNQzhSZU56NVQwTFB5TG9VSXZHR1F1ZkJhT2pwV2hOdC80SnJ6TUk2TXhWTHdjWkh5YUtMMFFqbU5DVVBWSXJVR1JzU0ZubGxCcDdQRmVCOHhFRVdvc2hCNmp1RkVJUENKWStnNTBYbkF1eG13NUJLNlJ1UlR6NGFjbnFORGd4NUwrQmxIZWN6TUhoQTNnYVBGb09LMFpJbUlGUjgwSmVETnM0NklZSlJzYXZDa2lWSzhpUWdnaVVhVHc1UldIWTdSeGxLUzhoUHdWRTc0b21ONVAySHZGUXY4akwwUXhHWWt6UFhQZ3BUUGtGUWI4R0dGaDRINytMSDVFVWhVVW95RzFtRVVydWh1QTR4SkpiYkl3cDJ3bFJpZXdaVlB3cUh4T2xNOXNoaGRFb1lxbEdTTXJZZENaeG91cVMwRjNDbS9zendXSEZ2ZjBqbEd6NGd2enV0TnVEa0s3aVhSRmhDeXd3SmpnZmdXZ3NiMEowc25NWXhqa3NFa0kyR2E1enhyWS9aRHZjbld2ZDVPWWFBUlBjL2tUMXJLaW10Mm4yUHdKU3I5NzdHbmNKNXN6UGdxNCtxdlJLSmk5Qm0xWU9BSHBhSnZJa2FHVjRBZ3oyNHZBNFhLMEVmRHdibmErYS9BN3p0aWljSkxYVll5dUdZTWRtWjV2bHhSZEJ6aDAwTFdoSmdkMTloSnRIaGpIYjVDRmdjTUlvaEpqekZMNVhoZlpPUmV4blFsaVVmWmxleHovc0VveHNzc0Z2WThmOEVVTHkzSkJsQS8zaU1nRUY4OUZqUm1rRHVtQ2lxRFFEZ1lXSmkzUXNOa0ZtbGVTZThqeExmb2pXZ1JTRFJWcXNEU1UraWdhWTAzcDdSYkhuTmZWWTR0dGNTVjlteU52b3oxMTBkNVJvZjZYOWEweElMZVIxWmY5Q3V5b1MyVFJXMnhaK3gyaFZZbnI1Nkc2NmU2WVNNWlY5RWFtaVh5ajU1VExVUzVTdjJQeG9tek1vc29tR0ViemlCVXllMzJTQlNzUyt6cGdReUtXU1piOFNyVjdHclVxSnN1bVR1anBtcHIyZTlmN2tmUk5ycFgvbzJPQjNBYlhidERMeDljYXlPbWNXZnRpSm1zbWlsNC9zTnBxWTdvTSs0RVptcDREbTc3UEVHbFB0cVVUZ3hSMXBYMkVob1BJdm8ySUN5b3hHMXA4MGc2L3dBek5WeitobEZPZCswSUx3MUlVY3BrWlZuTmVPall0TmY4QjYxeEdoODUwV2k2UTlpVjIxRzhjWlFxai8wa0Q3REh4UnNWcFhRMFF6TmVpNTZJSlBIcjFURVJRdEx2QktwYURYT0tLNDZGVGJhNTdhMEpXVnc3R2xFMHhvWmU1bW4rQjFsRnVLVTA0bSt4NFhtL2tUT3lzWDhqbDdHZHFYMmNkS3Z2Wi9KbnAybnRUL29tMHpDOEo5OU1YOTM3VjFzU2xsZUc1d0ozRFZnWjhzWXVPUGVtbkRMTDVlMXlHUTBwZmI3OUdiVFpvS1c5UDRnaHhXMitncUgraGhZR1N1MjdiZkpJbHpxL29WMkZvYWNlQmU2QThCdGl0dVZtaUpXU3ErRCtrZFZIeE5OL1VNcXhOakNPQ2p2SER0SFlsNk50L1pLTFltL1RvcFdPQ1ZYY2o2VUczbjhDNnRLdEVoOUh4S2dpK1B3UGYveGJtTUE4a3BHRUtlNU5kUFF2YWpUMjJFVnJURkt2eVZjMGVITGowaGlONE9qMHVmOEEzSnRNdzNJK3d3V1ViVGIvQUEvazRndGZONmFQYk84aS9BcmRlemZ3aXl4OWI5VGtlMGE0aFVzLzZGNUlhdE00K3pUMmVTMmxKOURqWDFqV2ZrZWI5NGIrVmtWK2dEZEh4a3FHbWZmalU4aUtVdjhBejJaaUxJWExqRTZUUzVVZC9yaVJWTitGR3IvUURPbnFGZldGTS9hWUR0YnFHazBnbVkvZ0Uxak9DRHNZenY0SDlsazV3akwvQUNYOGZzcXNhVlBuSW5jbHB4Z3lGTHBocDlPSjJrTjA4dHhoL2dRN2VidCtqLy9FQUNZUUFRQUNBZ0lCQXdRREFRQUFBQUFBQUFFQUVTRXhRVkZoRUhHQmthR3h3ZEhoOFBILzJnQUlBUUVBQVQ4aGpjUk1wNmdXd282aHNoMVFyeDZCd0dwYzFVQ3NFNUxsKzVUdjAxY3hxbmhITWVtSGZvdk1FQllKcVpvY0pPY3pteGZiMTRNSVl6Y01JWGNyM0I0aGl4NDRDS0VLa0tTaFNMMm5EZ0xGYmJFMmpDVWR4Q21ZSjVKYno2SUVmT1Zkenp3TzVkY28yUm5sZ1JHYWpVS0pTb0JseXB1THU0bVVJQ1J0emFJWEtGWGM4a3E1bU1iY3gzZWExeE12RXhJWmpEYkhJWGFXVm1YY3d2UXpDbWRkdXFtTyswMm5vSWFKQ0p2TDFVWVVPR3pqc2VvQzh6aVdHWWdMdU1EQnppVUx1SHltTm1MdE5nd3B6UExDRzN0ZWdLK0dJbG9ad0pVbHh6TEpVMTdQTXBFTTFZRGxoTzJLT1VSSE1jSmwzUkhjbFdMaUl1VkNETUFKZGI5QWNLaFhxWjBCYW1Sa2huRVExWXhreUpjMXpEVFBTWEdtVWMrazQ1RzV1SW9lakEzS2VmUWNJbmVsNm9ObTRkMElwSytoTGVKWk5RakxkUmJCYzh3Q05OYy9NcHN1WDRpTGNidUtNeFlZemR6NkRHWjVZRllnM0RIYWdNc2xrdjJsamlvRUdTMnZIcFJTdkZNTUplN2U1bWJZZWtMUmNSOFY4eDg0UGZvVlNKQXVvYnVJMnVMek1XbGdjTTVrSzd1ZStVdTMwV1l5cmlBQWJrbGdDY2d5S2duaGZvVDdUTlBFdnE2Z3hGblBveE9SblpuYWdhcEFYY0NLTlF4c1NaUVpqd3hZSFlWR0UyTEhieE9McHRYS3VCUm5LTjJYTXZQdDJ5d1lvd01SMm1VUlplOHhlTmhhV3J0bXRhbmhuRXcwckJaUlRZb1ZXOFM2Q3liblpFNVF2c2xoNXk0emJoUnpYNllZcEhlUlN4RHVoUmg0WFVZMnEraDN4N3pFSEFwTjgvOEFGQ2xzWEtRZFFXVCtoKzVUaWpSRFVPSVFvRU5uSEg3WXhhVldvTXFkZmNyOEdwTGI2ZzR4V0hxU290bU1NR1g3WVhoVTNTT296K2Zac1BvNE9KcEVWRzdvMjFiY2MrMDNtQUZGdU96ekd4Y3ZMeXQvdHV1b3Mxblp5RnRzQ3BFZkpKblhqOFdRT3JFSmNPWVB6RlpycSsyVStjNWhGUXJBUmV3NmhmTjVsTk9vWDFZeXJpSmtJMG5WNk1zZDNCeXBBbUY4V0MxdFBEUENyUDhBbFBpRzY3SXZzQzV4Zmdmek5vdkhDYWlxcVFVOG1aVVkzTjFML054Yjh0TWMvaEc2TTRHc2hMdUVSVmVKaGdnbTZtSUg2VThzcnRWakRiVksrc2s3RE5zTUdERXQ1TFBtVmZLV3dES1FGbUlnaW82NU5BK0p5bEljVnJrQy9tSG5sTEtTbEVHZEIzRjFLSnpYdHczK2FNQ2tXNWYwdWNiTGpnOW15OThRWjF5c0VsU0tTa3pDOG5Ha01sbXNRU1c5MmdyRU04QXc1R3U0VkdoTXRLeTNNbDFQMk9ueVE3NkYvUEtoTWl0UlNMOW91ZUxWMlVZQVoxQXh0REp2bk5ZbXRzSEpmNmxvbjNuN0NadnFuOHN0cDlJNzB6ai9BR2dSVzROODlUVnIzNWdWb0hOZmVVdGxWTFdNYTVsT1pyY1ZrZ3pNdjBaY3Rtcmx0ZWxxcXVQd0V3RzFtemZxTXk4eEh4VGY0NHFFQ3dvYlhQZFRQS0k1ZEh0T2diTmcyRzlrM2VMdTNaZjNoV2NrWDNESlp1Z0tjMVF6Z2F0U2JIMnFPc29Pc0RnY0VlZVlDMjYzOHd6bXEyKzdGKzdMNkxqQVlhRWIrc1dQcXZhZmNVbTcweXBkZkVWakdTZlM4SGpVTzJ1U3B2V2NWa0tGc2pENDVsOXdDZEI3MUFLbjNhZlB4RFk1WlY3OEU5LzlVM2xiNEdLK2lDR05yU3V1ejYveEVhcjEyNzJyRFhQNmk0MXhNaFYwR0JNbGZXVm5CMElkNGZaRXIrQzdDOG5YVXQrS0FmWGpjUkVNQ0hMNGxrTENJWjRaaHJJbHBjUDBsS3p5aXpYY0lWeTdyeE1McVdzSVpxWkJwd0ZCTDdSeTE1ekVCYmkyeFd5M2RIMWMrM2MzQ1ZqdEI0WWh5QXRuSG1wZVVzcGpaTkxxTVBleFpyVGVVYnNEYTFEdFM5STNmK0ZLWDhJZmVSN0FETkNvYll2Q3Z5SXRsU3h2dFM0LzF6VEFsUUwzT0RuNW12QWxHYndWRllLWFV2bi9BTjhPUTVJMkVuTjFLWDRZWDEzVXpRakhuTCtjRE5jaUNyZlFiWjdwaHdNMHprSDdNU3A0TGMwNkFObjNnUmtrUnRlTWZNNUxhbHJiUnVVUGtxRDFMdjhBRHNwaGpyWE02NGUxQmpZUDNncG5iSHNCZmV3TXBMSDl6RjB0SjAxR01yYldhTlM1U2w0UWNlc1BGdjFFS3RxWnVvOXJibmk4TjRhd1ZyZlV2a1NocmlwV3Y2U3NMbEtMeFlSdkh4bUZVak50YVlIQmhqbXBZRnpnRDhkYjUrMHhMd2NGS0I2RHhLb0dSZjRrYm1XR0dBVEk4ZFJjUEtyVFBTcStJcTVSRG9jS291dEdva2ViRERXVytOMXltSHB5cWk3YzF4d042Uk42b05WV3kzS052eVlPT3RRbDN1WFV0d0lkbmgxQUNEeHBubG9IN1N5QlRYZURlNjVjeHpvWFBDbWZjSHhjZlVzRlZ0bnh5aHVtYnVIMkVyRWJQaFd1aXIzRGJrUUZsQmluZlI5MGJlTUx3Tzc1SGl5WEZ0V1dHK3NtcnEyRGlyaWxBaWthckg5NWhIUjVVM3piSDZVTWlXeXBiOVJKYkhMSS9FNHYwVUh0eTE4c3lkODlvSW9mVkVYalpETTF1UUNLaEMyemk5NGtMSmIvQU9vVTlEblorc1NxUExReUUwWDJCT2k0dU90VFNYMWNSVUptMlQ3UzgvM0ZZbGxSTzFFWWFqei9BQmd1QkF0RzcyOThUd1gwVDQ2VExOdmRXSC8veEFBakVBRUJBUUVCQVFBQ0F3RUFBd0VBQUFBQkVRQWhNVUZSWVJDQmtYR2hzZkRSLzlvQUNBRUJBQUUvRUJtcm1oK0ROWFFmRzZ0T0dRWUZCeDM5MGtPdDR5NTMzZWp6UTFtL3hDWmFLcnZQWU92dVM4cGZjZ0duY3p1ODdWVzV4Zm1LUVpSZVlYUmN6dzh0STZNRERPVHpkYmZ2bGdEbmpPQVdiNkdsMHl0WXVMNXFGY2JNN3kvMmFDSFA0SzZvSGZtUmpnYmtmNDVXKy93ckdvTXdUamdvYTlOUHpXSVU2UFhjSE9yUzU4eWpENXl1TW83bHJJNnZadlV2K1hLRFdmd2hOUksvaHBjam44RXhCbG02c1UxYzlPNTZEN04yTSsvVDh6eklMNDV3RlQ1cUdJL2U0VXA1dk53bnpFdDBqN3U2WjFIRzYwU0dzaisxMUJIZWhPT1RBeE14VnBZMkp6dXdndDNUNGVMNU81SjBYeGlhYUgzQkxiY3FXZ1h1NkIvdHdlVDRYSW9pT3J5bndkV0l6M09QVTlNTmpuZGpkS080dERuV3dNMjRTRDE1anhtTitzd2lubXZCeWZuTUhwalBmVzRRMVh6UHZOOHpCMy82emdDRnoycTFYcitPc1AxUDFuLzRxNS9jSjhhVUplNEEvd0RMS1haeVBYWGo4NmlHdnNmdDFUZ3pqdkdWUnhycS9jTXEvd0JZT0wrTThnV1lWUnd0Ui9XRnlNWXhZcTBSNWZ2Y2UxclFLVis0Qi9CTXhTRlJaWE9pd1BETEt3OGNxYnQ0RU9GTWNiOWR5YVpLeDNOZjhPWGtla0JQdzZpYkR2eDlNT2VBais4STUvVndHNU55SlBES1JqdEl3TUpuVXN1ZUhXVnFYVTBZOW9yVHg3c3hvZWNWcTF5SzVzRzAxSmhtVUgrR3B2OEFEOUY4TkI3aTF3TFF6UWNFTjlMSDU1aE9Eb1BNTGxXOUVEQ3JIU3YrTlZMbjB6SXlabVU0U1B6VnlsSjc4M09YdWQvRzlseEdQY3hLWEFtWVFvRFdYTDR1amhYL0FKalN3Zld6K0xJTm5lUzJIV1pWUlo2aWFSWmdxUzZ0RnpWTmg0Zlh1U2tkNEtIblk1ditBMFpqb1lmcXJ3TU9VbEMxSHBVbWVYNnlDcG5DVHpJRzYyQk5WZGVzRmZGeG4vWTFYT3ZNaHVMMkdDZy9zd2NLL2RYbWVMRk9lWkZ2YnZkL3FPZVEwQmF3VS90Smt6OHpJMWd6eFdwaWxZLzk1ak1pSXFUVWc0TXcrMVZmdGNTTUh6R2huUmJlZmlyNE9oeUZ0OWlLVkFoeCs5MUQ0NW5WaSs1SHBBYTBRQjF6Q01PNlFYcDNBZ2U4K1NVNmlvNVlYWmdjcUlYOVorcmllWWloY0tQanZDMzlZTHlCcXJqOCtad3FKUjdBdi90NTdsa29kL1RQSC9nNXdubVB4OTFQQ1Job0hRNWdqOU1OREFINU9ZSUlzVnkybG1SKzZuKzVxSjhSRndTYmVIbWJTK0J5WjVoQ2VycFcxVndFL2dvWEo0TFoyRGdDbmJiL0FDektWUTBVOTBzRGVOd0FpRDlNWkxRMTUzU2MzRk9IS3pDNWtwODl1SVBkK09PVFRGOVc1RFNpclFQYzlQOEE1ZWF6QlZac3dIL211bm1jSmRFWWR1T0NpRDg4eGhUNmZFSHpON0prTFM4TUJoQlFZdUxFUkVBdzFxdUllNTd1eFRpSlJRQU9lR0ZFL09vZDBaU1hnL2gxdWVmSlpycUxmSURLYVNDWjFMcGo1YkZ2MTJETU9IMmNHR1d2VFhNRHRMa2hBUFZYcnVsQ2VzM3k3bERrQjZhRzFja2ZudHhzTm1QQklIazdjcDR2QmpvbjNVQ2s2VUtmbTIya09EL2VMZjhBTjkwNmRVMi9tczIwMUxVWDR4dDBSOXgvZWwrWXlaQWVJcWVlWEw1NXhWdDQ0UDM5M3NsZEhlKzZlUWMzbnNCai9jWTFET2hFaEhIK3Y3eUFQQlQvQUl2Zm03WU1IK3FZSU1QeWFqNEcvdGpMRVgzN2dBTVg3cndDZzNHNWhIMzZrenV6SlhGd0FISDhHSnErZUNRNUlBbUpkZUFpM1htc2JUbFBTeGFoM3FmRENCeEpOYXdLZlFLbERYVFdORS9JVjFuaXBtWlhxc3hqMk9Yd3J4OEFXQUJuSFVIb0ZVSHlHVXM1UHRDVStPTDgxazd5Y2ZzcjdNSzg5NkM2d2VyL0FIZklXOC9BelZsRS9BRVJkcTljNnZzeG80akI0NzJLdlBBeEVLcCtFSTFEcGhwcGFCMFhzdzRwT2NGVy9pR0orRGkrU0VLbDZZWWFES09uMmJqc2NWb3E3eE1kVDFlbVdnckFEOGNIWmp4djVvdGVIQWU1bG9TWUh0RDY2ZjhBYzgwQXM0SFgzL3VQakNtOWRGUndDQzltYll3MnBFRklWZlQ5WmxuNG5OZEtyV3A1dnhVd3B1MTdVb2dLUEhYNW5xUHl5OEQ5eU9KVDFUZ2dwT05FcGxxeEdBR0F0Y2I1aVhOQUhsUUdKVXVZK0RDaGEwMjhRam1PVUFzTDcrOUdVVXVGQTkvVkRTSEZ6VFluUk1yeDR6a3VIZTNMM3VWSWhQdWZNc0RhMEZuQUhQQjFyNkhBTjg2SDM4WlRxK202dXZmdlhHTEhEK0JWZHl1eTcxNzRuNCs1WjFsVWZoK0xyTXQzK0JhMTFPb3ZWL0lIeFg3clIyTXR1NXdiUHhZWXJOVzYxK01zZ0ZqUlQxQndwQXBFYjA5Y2MrUE96RE9VMUtBcUMyc1FFcnpRaDF5NVhsRDQxZm56VzVRVlQrNHBLZndLUFhPWnY2SlUwUThuNHlmNWdzYTZBRVVLc0N2Q2M2bzBCcTRKeVVsaUg2ZTBySmh1TEJpQXAyaC9aWHpSMTRUNGorVHZ4eSt6Mk1peEFzUmV6NXFvb0FyV25WeDU2Nk9FZTFEOVVwOVkvakUrVmNvczZxSDhYanI5WTVZZ3A2RTV6TUtwYjIxd3JlazhlbWdLQWpqOWNCcnhPZ1lDKzN6K2d1R1FJWDByL21CMFEvQzNoSStlTkhTeXFTcnM0ZzcrUW56STEwR09lQkQvQU4yQ1JOMlNhZ2NFL1pKL2Vtdm1YUFBUancvdlR4SGtnMG9WRy9PekVzNXhreFIrVm9QMWU1RzhhRm1oRjZIdkF2NnhZZGtlYVJ6eVR4aFltU1FFbEQvVjVrL1pseVFEQ2lxQTNucms2R25SMExVblMyRmNrVnNtN1pXSU1NaXV3OE03Rng1djBhbENRUWNRNkozbkw0a2sxZ0VMM2h2bnRNUkFENEhUby9Sd25HWlJ6V0VvRkNpNFYwWkU1aGRESFVXMllhSWpuc2w0SHNucERNVU5MVlh5OG0vRUxoalZsQ290U0RYbjEzQm12cXdNb1hJUW9lQjVTOU8zQ2FDSzFHQXJGMzNXWG9YR1FCVWZ5MHI4TUwyWDRmUlFINGgrc01mOGM2b2VTTFZUWGI0WkVhQy90Y2RUdGJDZW9Nak5DRkx1cDVFL0RnZjdhRmxINFAxZlU2WVpNaTFkWkZsNlNRSUQ0c2dyemtDbWdnL2hYbzJ1UEVjUDNSTlAwbFhFZEo3cFdBR29XNXdrZEU1dXVtZDJOa0lHRGtBK3JKZFJoZzVkdndnQXYzMXRKWjZ5dVFJVXpDMU9La3FFdnVXME5MT0tXYUVFeW5CTlkxQndKcm9CblhHUThMc0FKRVFmUStucEFIWGp5S1l0K2haM1cvOEE2OURtcENIclBreWx6VzRsRGFrQVNWbm1MK3RLNzVpL1phKzYrZXhFaXIrSHlBNUZOWWltZUt2U0xqNWQ1ZHpCRUNTcjF6L1pkYlBucUtOOGd3SGZPNGJ4K0VIdkR3NEFjZzVueGN1Q0Q4Y0ZUSjVndW9wTlBSemY2bHpjN0lUcjhURWsrL2t5N0xqVlJOU0dHQWFtQUVnVDJWOWJndlY2SldjaDVNbnNUSktxMS9vZDNmajViM3YwNHd0MFlIb3gvYjg2dTFDRjB0YmxRRzI4ZXNjLzdqZkVvdjhBeUM1MlBWbEtIM2k1bjY5Njc4ZnN6WEJaTG4zMzg4N2dzNUx5UVY4VzRueDBqY0N5L1BIRGxCR0poOTU2SE5MNm5nQlJJZlRpeDF5Q0IvME9uSEg0UjZEbE1jZ1l5a2dlSFI5eVJ0Ty9QN0ZTWGR1SUVmMDA1MWRTeWR2TjgrVytaYy9ERUZYbGtOelgrbkxDRmE4cGpLT2ljUFlFVDlocFVnZ2lwSnhuZ2MzL3hBQXhFUUFDQWdFQ0JBUUZBd1FEQUFBQUFBQUFBUUlSQXhJaEJCQXhRUVVUVWFFVUZTSnhnU0Frc1dHUjhQRWpNc0gvMmdBSUFRSUJBVDhBTFF1U3Q5RHB6NmM3SExsYlFwTkVabDJKbTFDcnZ6YS9SMEZMc1JaSm5RdjlGT2l1YVRJd2RDanNKYmJqTDVYK3FPeHFMNVZ5UW5YT3FJcE4wTEd1d29sRklva3V3L1FwaVlwbE1VVzk2L1N0eXJGR2JFcGN0SW9XTEVSeHBkUlJYWVRTTE5oeDJIRWNSeE5Lb1VTclJHNUhseFo1SkhEdTdGZ1hxU3d2c1J4MGltS0RIalZXVFJCaUVueWNUU3VVVTJVZmNaV3dsUjVqYUZOb1dXUXN5N2ltalhTMklQVWhLaTZJdlVVTldqUTRzV3dtNkxSMTVVSkQ3amcyN05KUnBMTGQ4azBJVXBVWWFVTEhiSjVIYk9IYmFhTWR0a2xRa21VeE9pOXVXemlhRWFQUXJzT0k0bWswaWQ4aytVWjBqV1lXOU5qenFtWDlSZ2c5SWhwMFFTYkdyUThhT25KT2kyUlkwdVZEaVUvMHF1VVc0OXlteUNxYlpoZDQwMGJEL3dDck1TdGpTNklxaVNkbENOcUkyaHZZVEtaVHJrMHhRTkROTEtmTldRalRaaGNZd28yZlFlMFhaaUtZazNkaWc2M05HMW84dXh3cGlaWkZXVzBLenAxSFpCYW1hRUtNVFJFOHRIRC9BUEpkOWp5NG5EUzg3STRzU2lpbFp4c3E0YVgyL0o0YzE1Rm0rNG4xM0xzVmprcTZpV3Brc1cxbWxVSXZzUVRZMGhxeGJHdG1zVTJoNUdjUGtsRlNIblp3bVNVWnlhZitXWUozalRZMmpqNWZ0SnRIaFR2aHZ5eVZhUk5rcFRiRTNWTWhLVUo3dllqbGc1TkRuSFRRM2FGSXRFV3JKU1E0MnVTVmlpVlE2bzRWZlRLL1VhT0ZUMXlJUlVZMFhUT1BkY0pObmhzdjJ2NVpxc2c0TldOVXh5U1ZzZHlXcE1oRzQ3R3lSS1RYUXZjdFVSYUpJMG5sSWNIRWJhWkdXb2ZRNFJyUkw3anlSUncwMHJQaTBsVkM0cFBzY1p4S253MG9vOE9UK0cvTE4xc1JhZ1hmUTB0amcreEhKQ0tvdjBOV3dvS1RzZVAwTkZMWXhxYmxRdU5qMU0yZks0dTJMSlBRa21ZVzV3M0l4c20xMk9HVW5GamkwY0pGeVROTjl5Nk04V3NMYlJ3czVRd1VuWFhvTEpPdXJHcFRROVVZVXkyekRHYzR0b2h3V1JqejVZdXJNRTU1Y3FpMzFaOE1vczR2RExIaWMwZWJrWGM0S1dXV3J1UGhNc2QyaktxZ3pUOUtLYTdqbTFGTGxLVUZGc3VQcUtVTkQzRjViZEZLOWtjVmhmd2pkOWFNTnh4aW0wUWpoUzZISFpNYWdsSDFMUEQ4a1Z3LzVQTzJzeVpMbTMvVTRUSW84VEJ2MUhsM1BFTW43Vi9kSG1NOEh5YXBUWDIvOUpUbTNUZnV4UWIyditSTGYvWkZhM3BYWDhrZC93REdhZGo2VS84QVpDR3BYMit4cFNmVDJNWER6Y2xsZzBoUmpxM00yVnk0TlJSQlV1cFMwOVRESlZlbzRxYWxVVWhMWXdaRmlnMHg4VDlESXBzaEZxZXIwRnhNdWh4M0VOd1NMYjduQTVKUXl0SjlVZkNZWXU2UkhoWXJzanlZVzBra1E0ZlQySVljYzNhUStIeHpmUkVjR0tmWWZEd1VIOUtOR0piU2c2SkxRM0dLMnNUaStvb3JKYWo3OUNDbEZkUDdpeHV2cWRsUnVraUtLWmxpcGRUNEpTbGV0cCtsbUhHOFNycUpwbDlqaWZxa1F3YWx1TEZvZXBHcFB1S2txUnRiTm10eEpKVWtKNlZzaE9sMFBOSGMzdWkzS05ORVhTcEllVnhuOUtNVHpLMDYva2hQSkdUUnJVdG12YzB4cm8vN3Nua2xvMHJiK3RrT0p5ckxva3R2VWprY1hkRTVTbVJlOW1SNm4wb3VKYmE2MFJuSlJwa2ZFTTNxUHhITWxkbnpmSWxib1hqcWEyL2dmamFraVBqdlZDOGV0MVM5ejU5WFd2Y1hqMFVyb1hqc2IyU1g5eVBqOEgxcjNQbjJQc2w3bnoydDB2NVBuc1pLNlh1ZlBvcDFTOXhlUHF1M3VMeG5ITjFKYmZremVKNE1DcUx0L1puemVTNjE3bnphZjlDWGpMNkt2Y2g0bG1tNlN0bnhmR2VudVI0N000MUtJK09jVnZFLy84UUFMQkVBQWdJQkFnVUVBZ0lDQXdBQUFBQUFBQUVDRVFNRUlSQVNNVUZSQlJNVWdTQXljYUVpc1RCaGtmL2FBQWdCQXdFQlB3QkZJYTI0ZEJicmlpaEpMZ3VORGpZb0RqWENOc2lLMXdTRkVwL2dsVjhFckVxLzRHNkxOMm1VUVd3a1VVeWl5TC9CSmxDdEZzdGxzVDQyTmpsWFJqYkl5YU94SG9SSTFYRGxRaHRGYmNLcmpaWnpMOE5oeThGMlZzTDlSS1QzWXA5aFNkRUdtaXhQaEdUdG82UFlXUml5SWM5aDVrUXlKcmNsa2RFWDVPWWpKM1Q0TFpjTFF0ckxMRXl4TkpDcVNJOExRc2NVY2lQYlE0ZUNtaFFHbkF1eFJkRDJPWkVKK1JTVExiUTFZclhCYjhLc2owM0l0TFlSekNaUlNLUlJSeXF5ZHVURkZFWUpSTXlHb3BFZWhja2hPeE44YnBuTXlNMEtZbnVjenNzdERvb29yZ2taRi9rUnduTHRabmU3b1RJY0U2WXNqRmIvQUFwVVIyWXRpUE14RnNzc1RMUnNKRFNiNFQvU2ljV3Bzb1Zwa2hDTWI3Q2Q4Tm5ac2xSR0tUczZqa3hQbDZNN1grYTI0TjNFeVc1dDhFbTViRTAwNmFIQnJvVzBqbW9XUVRaR2FsMU5sdStEYlhDWFNpSzVvVUlicUxZc2pvNWpuWXNoblN4VlhjOTFtc3d4d1lZeWltcjgvd0FIZDhQVDQzcklYWFh2MCt6MU5WcU5xNkxwOWtkMFBaRlNhM0lwWFFrVWlNcEpVUmxSY2ZKR203VE1oQ1Zib1c1VzFISWhZbVBFbWhZVjVOWmpjbkcvSGM5ZzF1R0x4UlczL3Q5ak5CUXlOSVI2YXExa04vNnM5V1Y2djZYYWlLS1RSR01hcGxKTWxGU1hUY2xpa2xZbFNvWFVVZVZiSExLOXhwMHlFYjNvaExmY3RjRktWVVJkb3VYZzFsWEd2SG16bWFScnB4ZUtPLzhBVmRpYmJrMlJqWjZkRi9NaHkzOWRUMVdEZXJhMzZMcUtGRDJJMHhRdm9SMjJhSk81YmxFVkh1TG9MWjBTM3NoWW52c0tUN2tXbUpScmpyNzU0L3g0b1dPVE5kQ1RpdG45L3dBQzByNjJmRmw1TkJnY2RURnV2OWVlNTZrNHJWTmJkRjBMWGtxK2hGVlluUkdhcW1lMDV1eFJGRkhNb3hvVTQwS1VMSnpndDBmRWtqRGh4eDNTRkRHNU8wWnVUSE9raHpWV1F0bXVkVGpmanpaR1NhTmE0d1VWRkw2RTBrS0Z4czBrNHZVTGRmZTVxNFFubmIyZlRzZTNDLzFRbkNEMlJCUmMyMEtKbWxHTXR4NnpGVkprY1dLVWVobWhERmlsT3VpRnFsSk0wZVNHVEp5dmM5ckY0UnFsamdsMkk2akZMb3pIUW5jbUorVUtGdHVoTjFRbEpzVjdsU2Nsc05aVkd5SE5XN05IbmNkYlVlcXN5M1BKY240UGJRNVpYTHFhQ0UzTnR2c2NwNmhpa3RScyt3OGE2R0hGVUZacThONldkZUJRakE5TXh0NnBmd3lPSkhyR1BraEJyL3N4NDQxc2h5U1E3U3NjNVJRK2F0eFhHOXlwcGRUbm1rMVlwekphcUNnOFUwMmU0M0habUhDMXJuSm5jVjN1ak5Cd2JUUm9vVXBTdmhxOGZ1U1RURnBybWkwaWJVb05EMDhMYmU1NmZoaXNqZkxWSWlqMUhFbnAxSks2WkhVemFwRDFEOHNqbGtsYmJvZWZtN3NlYWNlNUhVU2pkTm52elc3Wjc4cnZtSTVzdHVwRVorNHVhVDNaTEcrdzV6eGJ5My8yZTVDZDI2U0huaW5jSXBmMnhaY3JqY21uOUN5SjdjT2QyTFV1RWYwVFI3NmwxUjdzT3pLdStWN0dpWExqWTlUQzZUSlpGbGc0U0Z6K0RteU4yeFNuUmVUeVAzSGJFcGIyTzJKRUpPQkZiMmh1U2JkaXlYQjJUeVFudlc1SnhsSFpDNTErckZreng3LzBRYytlM3Y4QVE2NUxKNzdFTmhwMDlpRUdoUElsVmllMjZQNEZpeE1XREc5aGFhTDJQalVoYVpEMDZmYytMQ205eGFPSGF4YVhIZEh4SW54SVM4aTBLdmRpMFVQTEk2Q0M3aTBNR2ZCaGZVV2loQldtUTBzcDllaDhPQXREQStGR3VwOExGM2JQZzR1MG1QUks5bWZCWGsvLzJRPT0iIGlkPSJjIiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIvPjwvZGVmcz48L3N2Zz4=", + "description": "A gas produced from vehicle emissions and the burning of fossil fuels.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'nitrogen', label: 'Nitrogen dioxide', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Nitrogen dioxide\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"public\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#3B911C\"},{\"from\":40,\"to\":90,\"color\":\"#7CC322\"},{\"from\":90,\"to\":120,\"color\":\"#F89E0D\"},{\"from\":120,\"to\":230,\"color\":\"#F77410\"},{\"from\":230,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#3B911C\"},{\"from\":40,\"to\":90,\"color\":\"#7CC322\"},{\"from\":90,\"to\":120,\"color\":\"#F89E0D\"},{\"from\":120,\"to\":230,\"color\":\"#F77410\"},{\"from\":230,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:Tk8yLXZhbHVlLWNhcmQtaG9yaXpvbnRhbC1iYWNrZ3JvdW5kLnBuZw==:Tk8yLXZhbHVlLWNhcmQtaG9yaXpvbnRhbC1iYWNrZ3JvdW5kLnBuZw==;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAaAAAABcCAMAAAAfz5G9AAADAFBMVEW6x9Szwc/O1t23xNLGztjO19/K09vJ0tmyv82ntcXHz9nE0N21w9DJ0dvEzdfM1NvM1d3I1OC5xdPL1eG9ytejs8SruMektcW8ydasusq+ydObrL3G0t6uvMywvcudq7umt8els8PP2uWouMjAy9e5xNDCy9Wwvc2bqbnQ1980Q1HAzdqAkqXAytSptsa8x9KhssKjscHH0NvCztzV3ujCzNfT3Oc8SVjQ2+afrL2El6yfsMDT2eGZqryTpbfQ2OA5RlTK1OCHma3N1+KuusmJm69CQkfQ1dvT2N2Yqbu9y9mPobORo7adrcDEztmLnbK4ws61wc2Wp7q0wtGqusk+P0Xf4eXW2t7X3OB9kKMxNDzm5+iwvszc3+O8xtA2PESsvMwxQE61w9I8QkqNn7Kyv885P0czOEDN09lLSk6uv8+dr76UprlMUVigsMK3xdWruMlWV1xSUVQ7PEKXqLiDlqhGSlC9zNyhr79aW2CRorRERUvM1+S6x9Y/RU7a3uGBlak6VWvF0t9HRkpQb4heXmKInbE3UGZ/kKBSVFnCzdiLn7ROTVHK0NYjNUA5OT/i4+bK092grr5KTVNTbIEtRFcfMT3r6+tLWWhcWFqwwtJlYGFYVFaEmq9CUWFCSVGbs8mMnq9ac4pBWnB7jZ+hnJNBTltRXmpJZHtfYmlFTVVWYm1UdI9kZWorLzi0v8uZprYxSV6UjYRJVWOkssSWpbUpOEKcl46XkYqTrcWOorZQaHxEX3VmhqFfgJw1RldsjKdMaoIvPkvx8e59d3NybWuOiIFsZ2dCVmiNqMBocnthW1ssO0VTQkR1cnNaZ3OmoZk6TV5ZepWKg3xQV1/19PK5ydl9nbhgeY9pa3Gmu89ab4NCNTvY4eqDfnh3mLOjtsm8t6+qpqGlq7KEjJZja3VveINPY3VngJdKX3IoP1CEobp5g46HhYZ5lKuts7pwka2PjY98fYOFpcC0rqYcKjXGy9DBvbabnJ5LOz6TlJeaoKmPl6FbTE6Wqr+4vcJpeCj5AABTNklEQVR42nyazU/jRRzGW1taqLRFllKgtoIFumblVWuE7kJkaVzkTYEETdBsTDfZyFo5QCREo/FAzJI9wNGEwyaePHlgL/wdxpuJf4Bnz36e78zQxbdnZn5v1JLsh+c7z8zPSKwzFot0RxDHvr6evoGxgTFptJrJxOPVeKY6moln6KlUPOWURIlE0lo2m010JbJdWTqHF154geHUhXQK96/RUVtbG0cudTRluxDfIyW7Xmhr60qORh/09xY7R8Yyo6lkOpHOZvmdT1AmkxnouLn/7NnRcn1hYXb2W6fXkR2+/55hdwcHBwsLC/Wny43lo5W5RxsbG0sbSzdfkWZmZl6Z4dzSzZbsnp+fHR5OTV1c5PKDg4Plcm/v3t5ef3///fufffbZDz9wqFQePKA/2Nzc/PSLr7+mI/4Be3rek245vSG9iL4zvWS6ceOrf+jVV7+iobfffvutt970eisSi8Y6I7GRkW6aOE0MIAGqjtL45xjNcBKrTBxESfAAiA4cYYIQYCSjBBUIXTFqg49ODo8B4g7pyt20PmjfkRDUbDI1FqsUd2IT4EkkutoSfGsCPH8+4W+ldGf/6OhouVGHz6znEwSdgAhA/Pz4+OlTAZoToKWlOzc9HyEIcByefYaTfeLs7OywCaB8Pv97GY339vZuCRCITMVixeQQxb74whBF+oAkRi1IAJKu+Lzj+dy9e/err26Ahn4XMgg20Lniw1UkGu2MxmKd3bFYbOQTEHX3IRCNjo6OVTM06fFaXHLeSaWT6WQwkcgEROCh4wWRoMGpLTiojcbRnNPmvTP/muc03xYY4SUj2ZWMR7o7o5HhsUw63SWgfPUTKZWamHp2hBrH5p+D14Xno4+uEYKPB3R+fvx0efnoaA5CQnTnzh39819HFNgEGUQchIUupnJ5WUiAZKJAqFKsFBcrntGDzahcJAUbeR8ZnlsvQkh6+bvvXn4H+3hAIPLeCXjgY02I3vKAHlQg1LnZ2d3ZOTJiHuqjj40NS3jImkgZoJSnlE7LQvqDD3DcWVA4/KuulTVBAtX8a/PwQYIFBvucTsn4457u7r6xzFritGtehLIPn5jG3j87Wl5ZMQPBABLwkQIeE3cHjlD9KYRWqHGPzEMtQNeL2/7+zZ9/tiscJUBGiAqXG3Qa7x2/qnL9i8XFxcX+vaJwQQkXCVGLUcQD+tIQSUAKHrrxkrF51de1AIeOhMcUHFSplHai0fYokEaYjrpjxkhVbnTYpiJkZlKJS0NI9c0jkof83IG4ECInj4Hh/cMjCMzTxAkIonJbnWcMCNnJ/ASORCozMDqaysRP8eG82D2UkslPSlMrJyfrJ+v1Bc8HGSB6AMQUhLNU4up1CDWwkCP0X3xQ4ISMkE1CkznmIKk8Pu7nIcQJQFS8rS0DVvGINmNAMhP1fI2JAPTlrTcAJJmFYAQhyOAhoWEEQHRPiO4FoQi/rIJ22qOdKAafWGQiAiDBYWAlLGQzEtMQDgIPkDRxp+GTNTJJWQlZLGgx8o7gonWtQcc2DPig+dcEyvnILqza6W8B5hQ49Nq8A/QkmZxYnJpbvjxZp8K5uQcykp1gEwBJACImQOjoSBZSiStQ11qErrNhIG+hwszM4WHzCtA4Ku9ByPHplcrj5fLWFpj6PSJM9Nxk9N6vKnJfQsj4vCFCLwPIVzjoBO94OKLEwcshiizyK4tF1dVodBMfxWDU3T0xMQGjvoGeYWw0IExjw9XH1TUXFLwghIdasinemyhQcnFAaDjDidugF/CKETE2AZGAGcckBdW+rM0ItWXFhzI3UZpcWsFD9fNZ4wMZwHz0I3js4AkJj0SQe6oiByFzkAg5OUB0o/PzviocHfHc+YwYN0VKEKAOERIVCBme8fHBjg5+IEaqekUIhUIXIh2A8JDxoSMXE0JskwxJkDPSNUaR8mB5Czz8DexEKXUM+QhECt2a64YHmPLwkZSRlLOlhMpcCxEXWXrSnCRCHDSvtOkktYIcnG57SvPqQqQOHQYSoBReTQQ+qAs8lLgnn5RyuxuXjfVjSpz4MEQm6HkHged8dqFOkBOhuQ1ZCA95Pi08gPGShZ4PchS5fK6jAzpDHeOc0B42Ao+e6FFvuewYMRW1i1BrJvJxzgCFlOAynOIbFAKhK/292OEgAJlN7/eXdnbaSzuldjHSXBSRHCS68PQMkxXIc6nplPGRAqswQIRU6ELAhhByVwwygLjQTFbWAEJ3Bc5RgmUqkwK0KEsQB5BKXGakf2/j8uT4uI6DJMeFgUD1o/E5+HaWrhRXZykkQMtYaOkRSdtbyMfr64Tg05qEEIAI2iIxPgQQQwWdXi6GiHc8M2pbVulU5qIQ+sIQBQsZIKNDgXsZQO/cuCuBwfMQCoaIBbknCgoRvntwq1zeKxb7KyXC4w6EMBEeEh5GT6SPTr3DTLdI3BkYwaWlwMoDQl1+PjJEJqUCmvQ8HC1a59VutzkDwcvzScRT8g8BQe55h/CuFJdMp+IDH4wvXTbq58xBYQKSg7jiLPv8iH9mTQsSiJ42fNR+JAsZobAAEhzD8+yKT0A0c0ZOABAQBASJhgZ3AJqczHG2h4Rw/sityqnIBUC/9liKC8sgrYE0Azk+AuOPbwnHdT4IPr+9Gdnbk4fKveV+RH3lt+AiEHVrh0GMTCyQEcG7CqC1VOo6F9cZrs6pNAmQHOAZuRMTvo8PynKCgxw18xVGEp95PpkWIP7zti7ptCubNkDU19FPAESGE6BgG9pzFY6E7QEJUf28Xl9vNBrK2i5q2xKowHTkrMMwQM+ewef5Grd7BqCpfD7wQfCg54aGpqYm0dRQB8/ygx38E2IhDygWZqEeAbKI/Z0DBJ6XfIADg5pYaBgjjyfw+Q1Fikx5ZSzkRClFTEftnbGQuaVuFsjiI0JEBQhNp9OE7nRgFPDIQgp1lu8UGryMVDCVsxLHluaDs8h0wpbVt1mZND6np4oIKEWU/GRoo8EENGsh25U1BgcBwkM8nQ2AjFB9YX29cWI5Yc6inOwDoKVWbYMOnYNnpE+c7e6eud0eMEAEPgzponkxhQ6bpLy8j+EdvXtaEP0QhY8AtQzkVkDUN8pbAASia2lNkGj2lGF8HKDYJssuRUY0uNXbr8nOVzkhuiLUF+nToqhaHbOdBWQr1+nrlY6DO0POzUeMLkTZ46hhkcHz+TsoTBTWsvZhCJl7Ekh4+JWklLGOOQzENsKBrXp8RIAQg3ubgb49n6UtnJ8vSGahZWVtIbLl6s2bAFpaCt4JApG3ECYzB11Q4y5A42V8pi6a6Oys1px0fPDQYHlvsVL57AdtKUDH+AiQpWvZ52X8884N5CucRxRWPE5X7oGPA5Tpi3VG20ul/j3baCLQt/sshzyibuNEUlDcrg4PP0YGKRW/ZqJwCR78xRk6f9sMMjcJkkcT0oNdSPPig9yukaqiDESgjz/5888nf2YEqPRsHVeYg4THy11YxD5QQDhnm4eBjQRofV1R2xCxabq/BKV96t2jR4/mTCtHgZDythwEoNUalSwHoqGpySZm8rqYOqzVVpuroiQ+5fzv4tS7WKywkepmoMBHhBwiBQQP6AaAQpwONDgwgrjxgN7NUjWqEyMj2vBpZykElE6YOQMxjBGAiHIgQu85QhkjNB2n0LXyXDoQMoWSZ3Q4U/HkCoOEOBoja352urIVKK0YykmnCX1ZBkIe0E0WqnUtVLFQiw+sQobDQrZGFSBcVBehk8YRiFZWYGJgwBPgrIAOPQOSywpGqFA4azbxDOUMBzWbh9wgmelwFcGoNmkRIi8NsqK8L0Cej3YSwIN8hGMKAk/YgrMQ5/j8h8ADoO0Pt+/de8jGyij1a3g0U41XWZoGOmLVzcEqHYQCpepaFUSZOMqkwKQpaTrQyQpQuE8gnKSGOAUnGQFP4/rBYMHSeYizlUpeNkAonmJ/vbS7ASEspBgHIS/VulDiHKBzZFaCFYSWG5cihIvgs2J0AAMdjsvLnF2Ne+ZiXEEVTrZx4+xQmU7CPdLubo0SJz6D+RwbDkrgRQiR4vwEBCB2Sr0ECAOFjWzLCbe9g2Dx73wA9PE333yzrYKSkAe0cn83Hc+MDUx0I9DE5KWwKAoLo2HlOQU6GEn2pig9zUY3SgkPVyjwMR/ZQKcJAULOIi2Zq+wimMyu7Abo4qOQEB8YL8ytkOM8oBaha8tUZQTnnoVj249rnJw0Li8vnVkMlAPDcOKpAZKLXIwTH9HxmtHtJHwK4JnZrUFocugC/0zmaPmO8t4ehJiFBAg8ZqCAB4nQVzfuClDYwA7lDB7/DejDe/fuZbMPaYnkw3ffzcYzo2MDA33gwT80F7lN3QrctkAefixAhohQF1+Lp1z+xksGR6tZSXy4FSf1Ls/JmUkYlLUDJqtpbcxRIe1xS7Gz/whACMeOjkwubTxiqTrrCWEczwdC8AqAUP24rolIewnr2mE1EFBakWM4L5MdaI3GU50vHSIlbwBpJ8EA8WbI3j0wmuS2WrM2UygUXjk7hE+OfYbcBQ9ztXyuTJDDQtFNhbhfxQdCwUBW4mz+Qa/SJWeg63S4D3wE6JsPP9zevg0iyAAonY5X4QMc6ESdOkPk1kB9RO73sNBwlbLIp+Ns0VHy4hzX1qadodamg4E4CBn+FCIbSuGWwU9V59wbVsMUVkyCwoVJ8xeA/PtUlkEje4Wl/X1ZaMEDgg94TFdb2bNmoXPBEZ46hBpMQ6xYcdLJJZ2Gp9bx1roEQDxE/ROh/eAgEYLM2cyhaBHbUK024+jVWAk18/mLiyauwkxsMiwSEyzIUeO8g1qE3gm7CPiHboCU3/53DvoQ3d7e3jZCiUR6jS3sCd6VSe07tGj0g6i8JHUjQfKrVsvd2lzQjLRGu8XEFEciJR9ZjBOVeHp6GmSJNDrlnh9xIyuBoWUfsDAEB0L+xtGh8zr1CU4duF9bKizd2Zhr1O11gyzj6QRA5qADA3Rsgk/9RIIQPhKaY3e/zk/r63UBUgUkRXgLhe1SYUHiAanVWq122DzzP6lN5ScnO3KTxAXUHOoAUNEREqI+/8Lu5ecY4SE039ozgA49iGu1UPIsJGxjIQSfdx9m07yF+YQMVyFtt5cqaIcuFwVpQoq5wKDQPQYhQh391vDw2uPhW/byNQOgtClriIQnzqD+wUkZYtoRSyrYiZGPdUjuoWMbMNHEB6W0AvozPh3PTBQH87t3lvaX61jI7fX4I7Q0MJDnM2v5un7sHQIYg7IOJHFjw9X46VMMLNTQpmooclDYlXU4S2dqsNAUVJhhAmpaAu/I8QCt1poX4+x1F++7xVA3Gwl+GjK5nOAthIkcpNbLU39opewACPsAySz0MJnIDIx0tu/wplArVrbQWRyXSiIkSBqxkBkEif99YViqPoaRE6iEp8q8BAU3+QADOF4k8xSAiH4IWBbDT5HgcIl82ONGeC1tpAQIw8Y5TezkC68s7c81FnhlJyyqafBBLjCEKchlN2VsJDKeD7Y5MTYymHZTXQ7nRwBScrCYIBPtFnbdzqoaZ8aqcGCoWm2XBH6Rv8itFu7QCqvNXAeA+ossVx9smoV6btk85N41eEJ3baXKkKzIBRmc1trVIYrcu719Dz4eUKY6AJ/+sl5CbOmXSaVSpaTVKwrvjKh0AgQhDsNMRbY+6lHjyioddqER6YiHKVIEztJkhQSJSWoNwYhKh0Qo+0LiNMu+gaAk9Uj5wklc9AYxM0rK/mCrViAmzD0Nr1Rt3WNvHv74/Jeffvn8AP8YH0Pj/NPQYdlhAgvbC6QHk22p4jI+IUJaLM2FKtcScDRmsI+5adfvA0FohorL3sTq6uTUODmOIseOnBEiTIlQ632dCFnYlpMCImxEmvPi7hqjyF9MnEtMY2UUx1s7aqv4ti1tbaVCQWJsiwYTrdSo9VEt2CJGY0hjDCZqsaKpL3zEONEoYgy6UnTBwpXRuhg3rMdMogFNdENCXCi6khiIMSMw6O9/vntnPLe9fUxhmO/H/5zznXPu3O3sQY6zAJRsNhrh8CP9fidKZpCmw/J6tCTCAMKUM3ipd4Glsw2SrBdEC/g5BEImZyICE4TM9ekPOMj2FLYMkTA6CtCAjBQnEzd9MQdfAiDkqpg3mKy0U9VZphLuoBznCHF29v1LG88/9dQfFEs/BZCCy1vCoVqpYQIYhTmcmtIHj4+dBUgJw71qTGg/S0HBNe5SV7vaHbUfAyX1gEhuToCwK6uq70GofQwJVSpDTGM96VV8jJDjI0CUE/yCj863W2Xba6ti/28KUZmTqATIN5KE865t1rtw6O+zGtNUfgqbewRG0yqipjPBcANApN2ny0C9ycLlhSScDBCgpCFAmF9jjXlkfUWHbhIScpxG0dk1C9wmZMgJTjK5M3oKfAyufKmxPcLXDaxs/bF1kGVqL9M+7MyWazOowOfDgeHXfn/2k8UPn3/7OYkCQOx/ZlZwZvARIpGx2inmE1JJj4+/hXruA5DSbTdkMq/9qip2neoYJlrigxkdtkI3fjBSKgIo1SmXIcRbxWg+BiAVtf2mw+XOyTnzS6ZQkgmTJyO7Gxo2STK/qB0gN1B+AB9lCRcQg+rdYHjokX4IxSnRqhdhQkrrlKlMW0PPN9c0wgqWfttJCR5LjhqW3AYViYiK+UFl5zzhI2ovQZLsgvv11xC4MDUDSdR//WP3AEjEKd6amFhgqqj77VOv/fTUViEZipPB1agE+GM9guTxuW33n382jz4hBhyylfsYXCSy4LkgRNy5ydh5plCF2ixWQVC9PT6s+6obpaOwnVJptZryHZ6FIfZBAqRdERoan6yVpSFFoTiACEKuLcTyqGn3AoTORCEYAUlKsvYqBhHgiMqZYazT26TARRcBRoxcFndk8Npmrh4MpmNzqtFKRnlFJLO5WLqShpCSOd0sGnk7WD2+GvAJjRJmFP11O/c6AcI3wQQ/yJyDPXGMFqyDASlhkpyEo/vbyy8/FRs1dhRL1MztrTz18jvPP//7pYGhIvIxm3ES8qYWbY70j1OnHnjg2RnyMm6cpAhMqw2hFRAAByhnAPHiNgESRQBJctzNy6ms2qnCh86Ea+2pB3GlHBx1OtLt9osfqHTarlIzqlZhxsa1L8ZM1mOYZnxCjwLINOTPjbwhQmDCeCZEl6AiIHG6nUfY6IVeOUYB+iwwevxxAWIxLwRQs0B9O52g10qFKQokTOd+VdTTEAqTz0lJBigkPCp4W5lBnk7rfgRv5VdJLckmG78ceHZHamYO0j06QQ1BOevd31hf/2Lfio3kHvKcfHhjbXNt7eDSUL5art27yurVUISNJXgOTmO+b29tbbz5u5wZhAg+Jh4FfdoMAGLDiucTGRzb684EigikHex9NgGkTZP7Mq81YdOOGvpxs1gu5X5RHbsSpVMk1OI3ZlKASLxZpznzcizPo7YdCvhNByECCmCckuyVX+Lm7D3h4CxWBojqDsdF54oT+daRa5PNQqHZEw4PkWXnvTotEQlWnPuVMWiLlAGTAfI0JFAcXltiFCEQQoTIRXqyNq08TSVaf6a4kN/DUAWW96AgE97ktxsb6xsHkLSXenCAPjp6EArHxmq1VQ0YzNL3RkIGiA7DbZ8il4OdvfXdP1jqt2SkbTgr1hk8k2WKQ85MOg6Ql+7xSYhATxHKr32vqvDdAZAUJErGh4oCZ1XpsliUTDsycuXqKk5OLu4YtR9WCURpNynHP9IShf/thYhEztnZyWtCKCwpweNmzyzXw9kByKuYPW51GDoPyVwhUMipR5SOxbIGqF+AOOK4OpJvEGXEyUeEfJQ0cLZqkAYYFo5M4LAuWGIfaoiUV19z+asmNehA0urk+vGNl0zkgMUf//7lw19++W3AN/dVezvrz2zs9wTT4yhohtrzbO1eSQgTH/akpMi7a/9s/vPJ/AxLjIMzVwVK6WCytsqOiL0p6lLwgpGEpACkt9WKWAGOM5I/1RtWrf+KNMQHQOCh8GOI4ONbpF2d5bdAMagUZdYeU6pQse1QMPR0oNcH5JcT7NE7OxwmIUPjYpO9bRlEQI0c39iw0GxIClAuGARCLBKNFk1D4oPh5wSIMJjG2B3BxWXcIScJl38XBulbLFDt0VbTJMQ3HrA/Nd24lhOxDEAoRLxCurtg1vP50cXFJw54Q2T8zsfO9sbx9X2iIzGZig3jv1ISeyGz22x+tAOg95/45OsZcaA0gNJqbqC0U56dtxrCveWagXX2KWpRdW51nsYDgsTopfO29rLShU3SkSfobMVspyBICU0E/USi42Oz+lsEqNhGQSxTTIkVzU/iEIBIh/zJHg8QYYibDC44NSKPpyIkJUYcDlHgiDJZDfXCiUeSrWQy0GQBg9OZuXxEro3Ix74oEo9k1TbMz+H7xGcunUkHe+qa6JZyQCUAGOucpPHKXtV8HHgAxKx1r9YZOgEexcdGwgNyYHptR8iA7O/ubO9+xSuZ5SKwPLH2z9raPleltFO1+xQyWFOCM4EDQz96o7q1vf3Shxu4uBUyNomgJjwdFPB1pyyk8zVWnCIE/k8uDwy16mQZm+wYIXiT10EILwcgfTmdcQGiPTTCIE/LWTGajboREko9Y7RlIaQY5FwcjqZfgJTLCZC3V7UEbukSR0kyWcKTYQJh4UeArPdqU9wmpAD5r+wIN92t1YCzuRRAVBT46wDUzmbz1jjM8pKSbYLtMu1Dds1DQQFiDpVfdRbUNSjglYQQgEjklsi2iUG4OEAo8oBIJHtwoqqTKwoFhOsxgPGG/uCr7Z2d3aulHAOHQfPE2uaza/uPPZboK5bVILA8C1M3B39GsCl3qgcnT65v79y7sqK5ubfoJ7DALDFOanK20yEYwYdaDYk6z1dV0y6nskVej+m9eXlMtkWW2d3xrnp40p+5uVapKCtR4bFGkD/kg4iiKRDjRlWOUxxQ1qvtYzo89CRpgk3HOf04Ls5u9SMN+nHZ2ysggYlvIgQgnJFscFRXmqiQMphsko0VCsoT+iMRflMwglCe62Tc9pVpShjFEjEAhRu5XBNARI8CKvKCkgDB/IKli8/hRhpnLs6LNnKE0LRPQkw5QCh0f/DjIJAcpYPNzc339x91hKRNqW1v+4vFL37lM+lECd2gIQDRGR2bHUtNWqzpjKX+WP7u1Ecb98qZoRElCOLTIohIB4eHh7xqHyulWqlxhIMDOyxlY7ERq0anZmt3sG/1UzskZBtWdcbho56c9FF0jSF+VbNxpn3MRsA/6Vyc8QGP7R2npzXGqESHLG5ClTjMuJhfu527Kyg4Q0Xyan4sMlwAgs0gJRTuKk1zSub4PW8266QAiVi+T8EHNiQK/UoVYIar048Qc1GoHmqin1yB0BXAZzXqIRAw+kPJ7Rzw0H8zJ3chafarygE4LIuTmvSS2eKkJBQOh8VHkem3o08cfWC/x9/7Ogd6sLe+vv5rT+jRUDBR9RU0aVWW1lgZ66RabSUJix+paKBUbKaGsbraVXLX6XAs1e4zv63QP05Yz1cysQgzIaXU7Luac0BC3tS90gQgoo2x8VKWxjaWVZagECRWcRtghJJqg1UV49T4jrlLHrDp6cckIDfcQ1FLDs7Y6A4OZ4Dh0KOdzNVBypNRADJmydNWyNVzdQCxVpkEeUJcyT1HnLtNVZJ7awwsoViEm3pUl+c1k9cWeuW5MILRwACA8HAQcvVopR+DbDipqPZyKGXztkz3LAwOyPkJDUcI2zp58uTOnwHqeqhLGyXZ8eXFTz76s3cA60nNs9u3BABCh6w061OutqLZrfVvjn7yxcrKW2pzz8ysug0KLmm8JGsDqRgnoMKoJHdF0MjGut1MpZIfSZXfvQMw3tAjYwxS6XxNfNiVjkTybnxRgKiTRiEkBWVx/H3RVGpStEeieXUcHtHBFQmsDhuhXm1VF66H0MSEfJshwqQhR4UHPdoTaYrzaRkFIMMNLA4OllN4uL9e15KFiTWRkXhEKpKf46d0gCL6QYYECCK5QhO/lQxYzhVEGRaCyDw0Bayap/k4yqUXnulKwMhGvhf4ufF+FCHcnkr2697ezva/atCeTxFIH6GGekIb1T/5jhdfcGlrliTOdqvVDsuHMjRzzSQuMWhjeZcdqUoJ91mOTXhXa9qOYpuEZ0qmWdo4b7DMeRHqdmPkHsoRAASbO1yrYhVAfGdTmsawcWdYsaU5nzi0eMd28WqCk9jZ8PZUopLBqLk8piFGdy2XP9+Dkbhh5t7O9kXj3c5I6IyL8/EUPDyX5nL1ngbuTdWCYE84Hcvj0uIIWv7VhsVN2BBiMwYgfBVhKMfFrddKCOxu+C7UnVlftU+XjM511gfClIhocgghoRDVHM5DXhMTFOVGjR511MF9xaB/QaGG95J6fBedNbx9/NnFj/61QYWBrKoJ8/4VJagjpTVE6Hs//rh8Yhc+bn0tBqEaMHCzU38i3M1kwow3J6bks+WUKpVGvVG5knFIuTb1Yd8tz5AD1lbtGxigki2/iSiq1dBIKWuBcW1XpDgu/Zi/08Uo9NC0lVcEctWve9QY8jdDrucAH9OM7EwVQUrCIOMDAotjA6MmCy3/RgLHd8fCdL3nBAQ1FyXmOBbJGjKyOY3aUznVV1EfSg4kVfGBjzUGIISXszKCTSkIEoC4k4kMLKi2Q9GAohCA9D6Gm7Y907cG6CwbrfdmfIbv3tvb+OZE/axhJrcH+uk3EIIApOw2JW+FGIjbu2sk48sAshBkQYqlQzdCAUDSKwCFg4p4lampPr0XoVPdrTfypVl0c4eNkKxyHUSZHFDZc9WacRph5N/tjojFZQHi11QX3uWPlVqqnEaJVFNk15RhXGOGSo8AYf/rDLmVPx2ChMaFJVOVqctyPZG8IlBoNpvCwykXQAyyBj8/S4+GGo1MbA5AIxQ10LT2QjpEjGw/PdSAYqMOVIzrvGBMQAI1hDStIERu/R0aDxBdHZvCH1XqqPYq/KQTmQLWFi5ut3nWWTe76+8gxAVF28c/OrHc1HD9Xcn+UqrTIVPWDQVpUxKJalOy+92pO59dnlHngGlsVQJYXfFxxSrxeYSVa2iWlmqjvcsix+SS8in8ZjUVyXdma4fjh7OEuHLVbVSdPrKKwDHxFCVXQHaXdPUp/4aQphI0Qg8hAJHQ0PkmdTJE9yCjATcnZ4sPC9OPX3uzRtGSvVQAsrI350AuB6KCMBmbel33elAW6gFQo5LPAygaHYl7ezNJ3dwvW1ZA3q+dS4/cHBk634esOwcgbvJzEoaYOEYTuvMKNu4d69jhBY84OpwVsP50gIBhdvfNdw8PP7isGPT38N28TmbbLm8mAyYHJu5HbCvdF9n/65flj/Y6q5IBxTQv9y32yQyQrlYjdBLBE8ShuL2pwgxho1sZYXdTaVbGbNPpXxTOd8DYBkWwGHDExL/ajnsF6++Tz6MbZHM9YUzBmYj6NC5OxgPZnFQEIq+3erptp7TbfBoPZnqJwQgXBxHzbNzrvnnPcAQAysjFFS0EFRGRI5SHWp4fMG2u1hESErQYorCgvVESTULICzwamjMu+DcedC0l5o+RUKvTeQlCS3A6uYnVz0FBZg/eTD/+oeMnjr65+PcwVxHd3cTlE3fUp1GIoA3TR+hXanbw1y8/H9+5mqrBqnIIJWAt9QAgoyU0DWEJZQn9fB6z0mamkmnUu5W5fKaZy45PdsjBVT4w95bSDCm1A4Vd2VTCHhwbfalywKz8C28YaxGypplNTHMXIbsOzmKREZKE/BqcTmdSA4NDucEhCgSDAMEcl0a3G+7as566et8wEqDIsWP88zAFo4gFR25UfZQnDMkdhgBUuBZA0EF5ZHZNDK/nI+JOfU4ta8yAEZoAJAOO8bpAsDi+cIAutivuHhIjHk7+sHHim/ekquHRKC4/Jbsy1bFxjbiuoIHQ7ili0PqNY6TXSiHEp6htNvqxLE6EgMVJOK3AOJVgTdnQ5Rr1MJhymWIJr9huCVGKHS10XHyJop+MPgsRD0/GWTjTb6ntlGgJjzyPBCRAGBKyvpA0NGB8IOBFIcNjcOwFdBwYXVCkjkSA1ZVOtAtpdBvdaRQBGzTR5RV/V3qKKhwOTiFYI67aDEnJJiFMtVN+xmADIigoYCN1/Ih1PF3TxSLgWPIGHd29/yBjgfjEIYMOwQleeoKmfqfSs968bomBRvTinNxD2z8uf7P83i0CNJAvlVi58RJbEuPUirp21Ysba2ubTyxTd5uvaQfTuZoAVDShFLF2SYQw/5oeo6VecSbcbPZ0wxkkgNTixUq+JEKIBzLFdltfrho1LIxARqTsGacgMxy2BUnoLStgNQjL4uO1SxCPe/QnFFylR0m1n7Z5gHhi7SLouIJ3QLtN5zMBRQLKeAhFTNZYpkkR+CCcUlb/SGWY2T7lPcrtkJBc8pwxCluqgHyCwaFpTWqp+5BrIqsLfQ05PsjIjlHDNsGj2YRL8ez06y+f7awnj5xDFWKYoCMmww99Qgz65z1dKnl2I1ZsoRtmCFuafmqlSlEnjCt/euf44p0fPld2hro+KI1ov49DZI4Ng5DyY6vHcMjo5k83koOFekOpXaRPXrAyhUjl3ITUy9H7EQ1OP8wtKE7Q8myahJ1CC75SuII9pE2hUCGAybHRkJTdI/NSOTk4FHS2zR5wMue2ZF5O2gERH8G4MC8wR8nzMfV3KulHkE8i465Sld/iNo2C8K9Z9nQcx5iRAJbybAko0q8wZCriHxlG1DmsJzgtROEemgm4PYggHoMhPhDCyXFlP7UGY6X/hkFxSb1uPmcfPDj1xBNHmyQOjNJLRRxn3/zdqc3lNQG65bpwn9xQi9ig8fXSiyVlLCxrPr+188vx5e2O0ZntpKASLUb6p9oW6tvKuEeUUUzRMFE1JjyUIFmYygRzhWtzDXJtHLobOJuKU7HDvdE+UDd5ykskGkrR8TeSjmCQr0GL2GUuP0F9P+y27ujH7/+/eg/iMT4+Ia6JvGLp1stuuf2qm2+4CkKWup2OPwgHQAiIGwr6j6yzj4m6juP4XYhcdFJZgkeBMKKzNSDsYTkt1mKKlezYITIDvOFOBcPEymQt2DK2cLhE/mhKG/VHK0iiNV32R62W5NJJrejJtSJQ15ZUUDNAqV7vz/cOenjf04+7i+j3+n0evp/v5/uNq9/ugR2ZJDIyoAxflXkp5YqAy7fhsw267alw+yIl2kiZHHxQIEB2yT9ms3aMnTQM0BCWlNuiUDI3BBIxIR03QlZg0joJMXMdCMQmnF8PIWhDXgq1og4QWeN20crpkW9aB7cWofTi7EXbblizhAoChApBlFaesUM7HZVPUCydmSADfxzrET9snvdvo+xGYUfZMj66PKDGKCoxXIcRKQggFbgKQr7yfH0/UBxIo+pgzaOLlJtnOjz+3FAZw4qQnR8D5JU1BbTsm96NADGKAmnIiituZS9SqQpAjg53I4T90AC8cqUMKDYEMj4CZJKZGSCW3ivMswhfgGS4GVWyWqxHKnbhDxNiXQyz8LSI72bIZkUpVRQYF5gVsf6YX8Mfb3MD9s9jQxi68gT2NDMbQhiQASpA6fYU3xbISnWYEwT7xo4PtIJVWTeETCtHRiY+Ol5C3rDAU36EGM4An/Xa20guF125Q9NTus4GT09NnR64R7Xl5UQflh5wdr3eK12yDFH+fnyaWgbUGcX8LGuuMqLeXM58bnp6KBNrke9TUoE/dIkffFzs8WuM6PciqCCvH2PCwTFYzUJkh/YtHHuCa6IxC3J3wUHK4hyia61Du0h8OrgpDLnswOjc/uSTT2JDuDgNLfmNNZbDKQhhp+QH2I9BS1X4sxFQIc3iB7atWF7IsrMjsws2Ywk35i1XjnljRSJkzhgjMkJ5lMpnxc/KxnNFh/oQMmOy9WMcaAOnS6OT4xMgJadTOVy6pZLwPzW1GmvK86bdtoLmbGK4fBiDjwBsuOiLg5ljA1117RPbyJCpnObksPg6PxD01pZvX6vaNUGFqkJWTiSqKln40dpwOBqt93mDUZ3pUEF6VY6mVqwCxJIFbMgBilg6gA+HI5VKn9yaLmWtK+CIawNAgBUhQpDYGCDXwg4eNEeI8SodgVfdeF1R0f2JicJD/OkAjyQ80HG6HUIeJb22XZIGmaGQLyMDQKR12HImyijPyeLPxGCgg2g6kidHhdtXaH0gjBAmpMKczwiJTJmODRfJnZDg2YDAkYmaX4GHaQode+jvVnKXQiEOQmj6RFfXQSxInUEdibbZVdFWJhJenNp6S9F8fyRLgO5RzxozDDmBIMpQ5TaQOfnDyMDA5O4lK3YvYm5eoh7FZeIN3AZRK6sdyYpEvQlUyRIY6NeGExYW5Nbiqurr8XM7QA8i1iwIDU2JbhF3uaZVFHswICGyEITP88tf+HAzmQQyyztIHMoo8FBAiLs47Ag6NuXgpN1hNPtw47VkB9QOLOzoRlItOLN4vhSi2z0svGKAAiUIqWkRZ9uPINRPOGJejhBkuwIcYEyAcBZoLVIJX+4OAciGRPx1xsddYzzLG0MI5UlWmdWfru4eRMdIOpdYniXeKZYoaF1Y47copVTlBRlQEbfEXzqb6p5aN5pYdEUuTZXEIOrYqhLkZPDvgxAT9GxqNP32sePHJxcVUr8h9aFgoF52v6bNs9YyZyNAiwCEcxMdb23YX5Byc4GfHjZvMOz3R8jdyDuO4COU7bn+AiV6VWUa26nKUpPrhoiZTMdp+Oh35kRdghv5OoRCmuly8ynWshQ3ILT3JraPEx9FISvAMSIyMno8IzazeL78Uog8SVYcUx6lhUFlWC6Cj9xdACmftkmqJaLj5ufpVnbDRE3+FiokqcSBb9QoAPGsDLCfQ1cEcnUfMx13w3Y0CYtH473F7GBiC15tGEQqN7Ru3WNbLt0JILKEjsREQVo5ONHU2iZWBWyWk5OmLHgbJ1tXRJVgBLU32InTBKHjtmFLFZ8EDVCUYBjF7NbeY82fmBwtayCppdrM1CJ5TAE78iWEsaXMwrUIPuQSVj5SOMJ1qYCHP9NovsAqjxDC5ekAp0E0KI53sVfZ25bAwUdJ9tzui9BxoZ9Xm14l9JBXW+SRc/sHnwdAhB540qNekZQUnRb1i4TKqmS7Vf24OABlEP9jfJYvZVaM/gvTPY/DCkBLGIPAyFVByH2IWoABj0BxmfmtlIoNgSju3FA6eMyaiDj0Ad+JT3bFHwEC0+XxscGeFOPToVX4lm2fOnV6Zkar8v2pZCYMHrWiTeHeJ7fsjaqPr3hwhgx9orycDwiAkEPBqKoutAOlbVfrJ4BI26LWA8F8dN5NBek3J3vCalbhLwqQugHRTVzb6gFsw2ZHoVqGRMef6y+TG1c27de4p4p4HbTudf1m5vwLLH3Luz5WOzA88W39JLwatQRX5InxUe0A9zYnAZIF2dqBJJ7glF6TS5qA+i3RV+MVWRyTiMuXQITZS4YXR9XCYpjU9Q+mbRAqJAxpUKRIpKkQt3OWinSq9YkQI6IYIA5dbgecFKRRKuJFcGTQ04MEofkKQTIht3RoNbbx4ulEjvz5R0ixVqTtCCjyIYI0fs7c2aXfJ/tOTKqjJahabzAGyPWzlC86km0uyzbVCWNXj6r/CNsuvZMXUq/0dG95VrYK9WKjBFyEAsGISRXqUIg8jqtXsoG5H2l0JEfHhckvVku2q8ApQyAjQKJjT4o2TgASHpdei47A4f9iiqPyuF3YkLk6RaFMu2FA2lSQGET+pviDd7sXNupyvhcdFSE2g6DcC6LdrgcpVcNe7AhCMiKjZJW+GnFBAOLVrSOx9QwAMZnp2Ouy0pvbvkXz51lt29YJafHQiYFV7Y1FAsSiQ4YzTJO5NTIq+gd8VcUakl36YeTt70bEBUAL/czL8HeEXWOdj+wsP5+kmUq2WRVvEwZtrzWrcdDsUuCntEX+Vx6BS31GfT1DuigZBKsJ5OiiSscVZym1heTIDJBVykKCRgy2lmzrbyZHsOi61wGyp7j5iI8MR2yclFPfbvn1v+VZvZXb6q0IV0dHD6mcy7fLMuW0igF02wEsZamWTFHiogOTuyhJ7MtBtqumpEJKDUDSmEgJN0YoV0dolUIYkc1rzNJhGkgL665Ock4NQUYH2FLe0MCJUwe1uii2PlyPpImx1r5viohIZYXbbtDoB+GwEA4pw+sLkM6kDsygcR8hSeXkMM6PA78Bqo3kXJmflU/npRV4Yz2xChacznTySLbzoL/IV56GO8CAYr27XhTmSC0GGV6uNWhgkoxWCxiG8zseTYAQVoU5KcFjUYM/d2GNmdD1e5XzWFZtgo8YwcKeuImME3CIQf+Xp2R1TBAiT7CJHP4KDVllDxTb0lYcYGmMAD10Lz1oTPhbR41akrAnmdJSHN1uB8hmHa3mYfMi5KA4abvkqBaHzJasfkoZx23tp3YS3SVXsyvde3l0ZHxsvnk4rAc+6OKJgRcPdhURjnI1wtdmBS6fJO5vTwv4fapmBLrU+jsmyw1yqqIOlN+avfwAyk7LZ5rRx0nEwblGVolZT5pckli+5sHOFmUFqwLMizJU0gaX+i6colHx8pO2wcz2YbZkocZaLcmE4EP+wD+BZJpYJj3qzOPbFJAFfIv9DzzwJXdFGH5AvMdns2JJkb3wcPKsl0oQiJQuaFBUU+OGrFbHoXPyAOuY5OKOas8H+NiaAbdFh7yekgb8nPXIMPWqeGQpg8rczt3JmJR3iBFWJEDzNR/HTNzVbpZbvi6uO6fHPj8+aNstEH7EiGT7IjGofectAKo5e+H8yZMnz507eY6X8xcunH3u5WyGh4tLO+aPHzve3jVpzjWsUQ6JPmeQYKBGr2BmQFXSjFq1Ssbtx7UjQ2je1Ukp1yd4gzlpkVqPNxChY8p1HteqNU+/DSz+BMcnypN1iFEVxWT8kPIUoAShCx/SsgZbAYnZUInTeT90yDb64+B/qj905gyPWbFMvJ6Hk6e6ugVVGicozd+q/XbzAKRcPyOQmpVVKAtas5Sk7fE3jwKIXR/YpVW2ZFMuyuzWIOZmtiFKXjZ43ZHKDUhggpPk0/CqHwdaw0TevGUdV5QwFbfyGu3Wg5XQuRBXusZB7bxD7AEId0rYF7u69rQ1acx6eUtcG0wcbGxOXWB7aY78MfLKW6MqNFkwUJbvccKPyTyoC9Q/ytrexZx73QUugSNS/lIcSC7hJiP89E2eCBaYACEh4kswpPmvFhknnBwlCAIZnKSE0EILsbg88CCeoKTNEr58mkMUP/l6Gn7pJe7SOzzs9R3deejZ6YyTZz+qqKhuMVCVzpa2zstjcElK5ytOZSqSJHvpGp36pUfhoq32Zw3oEfzMdnJtxNgIiGQTmn2kg8FS79hm4ApLSNMQPpIfEtt5jEFtbkeDUOttTLbGLLWO9NcdPPViF/4NPBLfW1+5b3x8Z+vgSo5HG3obenu7UbPU3d3b23CeX8V+D5XfDXSdIIvjXxPV9W7TmhZmpOs9tZmkcEG/ai9ybnzGA2mrY8yMEOTlQw+swvW+MPzmZNOiGBJ24PVCKRqOBhGUgsCCkUcDvIUJXPySAIVrRUZIjEqchfTxx79JvLijn3jYq735jsl92/Ow5ChVAMntmWBZQzLLITGj4uxC8jhNvBxgixzM5nHCzlFFH/JsFRNcs9huii+22hlOWgFt41cgZdvUfYbNDStjrWLS0ggl4+Q0k2A7WMxaEH5tXsr4yKfjY0nscaF9GO3Ut1Tv++74wa7BSo7/GhpqaGjo7ezd1f1Ut+FpGLrAZg+VfO0UE6pTY7QOVtkqRADFVzoDhPKF98odQV/4UYtJ1qyvBwdmReK0eK+awJKTE1g6kmAtBYslGRpA/fJgOukMbMGi9BsYsc1F8JgFC2v5OYLO8AHfGx4Wl9i5thPvOHyFeJK+/+p7k47jr8bLQP7keWFOD4tSS3UL/6ktLdUlq7U1TnpNsUIKECip717zCHCW7F6yTXs8sBxDfUeEG9tmNRs7spVoaMny3eiAVepsEygKDdwpNjAG7lccYhpvwfwr5vqq1NlDBIQP47LJyWNjY+wBGINTWcmVc3FGDVVYesVoQxuAOjs7d0mdnQ0NbU1nWwCEjg8crJsZzyGOBFSmDqo5Wkaidn2vZ1nHQvLKaJigIhPQSEChH/llT4SrvezlmbSMFR8Led/GSY6QnJyCDVgOuQARrqeZHDgZrLnnRaalJpH6M8Nnhod1+Q+f4UD28rEJ4/jqK8zDQZnTr0hP77//Ps880CwwPvZ8OCsIgSiuhyvMlLamhIpTmREmlOSnZm9fS7omXqmalwcOdbti66IEAHZEnCLh00Y3JtvOK+bq8hGIXKWPOJR+Z0ryfNgQY8jRuGydAWm1ZE0rMegg5qV+EeiY/jp1at2erv3VFRUXjc8soF2dDUM909XVlbjo6l/+mJx+e9R64FyPFefPyoNhBpGL2V443c9lH8RLUXmw6z9SHw1W1fsR2V6p7VRHW0RpAp7L71b3uIIalIk+0IGPhBVxHPVyENWPtcQcUoSITOZPeSkXUsxe4mbCC4cC8v5/9W5M7ifRir3p+eKLL+YQPTwrDivkNkpW55UVc8mHanB3NnQtrgppOEPlLqQzHSI70ws/iNEKNhZg0PT40jVGaNtaqlqLbFfj/At71q0aBeTy0VFSbjKFvAXaNpakhN0uREdVBfWN/LVq5vTprlscnkqio5zvvt+nu7qmK9DoED4OPkLEAwfX1McH1friyMj4sWOj/AnExLXLs5kpQIEqb2ZqprfgGhpQivLoqTu3afNGUgvSi82bm5s7h4ZevUBZqN5zq2KeBmdJpf6ggou1hkoEJhDF2ERySLSIMbYgI/aW3uDjcviYyZh/Qj9BByZ2vmUfMhmd+Pd48MTjvff00Iu7If0Uf8/zIYTQPwHtf/gFBKD1+LuSfek0vKlUly4xkr0p+QoSiZJbVu9TEZxl4TRYUWolp6DaUQ4j2gQ4Q2uWUqljpcaRI5dZ2nu+p2HXzlV1GzY8T971xmNNF0Z/yeSfSmLVZQo3PaUwawQrdHaUGGR7CDk+0sWR8e8+msYH7x9tampqawAMlsRTW2NTa9+l/TDkm6dOo4NDvRDYtHnDG8agd+j1Jb6FpR2iTR/k3sDa1zeiLXwDbdq8CV2g5bh0mSUlKi0tK/W6zeSDGvv43apAf+2hiMHAd4lQ+GXy/fMnz31yzqSE//wTFmscH+iAR6YDnVkDgU+cwGef6WlWcSgcHj58+LPDvBogBCDH59+A8PfEpPWUgtiJZPXqpOR9UnLJ+mrOE5+0yMTIK/goyUZRbARYUxbIx9fRbwMoZvlQNuewE1dUt7Nuz4Z1z7e3tdfRcrBry5bL6XnaXuxmtZWoll6D30vSDliTI59+NF4pzQKq/mtmampqoALnW2OAuEmNjU1NrT3fxADtI1C1n27v7ebcGwMJI+lf0GFZ+F0AWnC99/wWxAcmfd79JniUNUp0511168lPXkc9PD755JxGW2eXBOsjwxGJyE++HE640NBAMrnLEsnuXai797ntJNCWginAE+2dcxOUmMnAykERg39oDlT8bYjJxQmG6FiSEAck8Z9cYS8VlRX7K5TelbiCQ2WFAYzJYlcFgYLkgo/3Jd9c4MtUlOKJ0ISKe3r6ehob23buXFf3xqotrX09rT1dPa2bN46ylhXjoe6DKeaGivtD/P+caJlP75r69tsTwP8noF/op5oZqJAFDTU24uIESFbU2NbU980lS0GrQztPDB7c81T3JjwYeIAgEM29d4sOiFDiNTfeenbLxg2CFwO0qbl51PVCdYCng0rZ7bdvQs3k8XH1Nlw+IzCRCF5MB5jSZaWQpPxSdy/WPPTqg3yDhFrxx/H5+WcL9u+/CyIZDq9xE3n22cPPusccJfuJ+2dfgwf9+J6HUxCzGKLO/lmJDHxcasdPvFTbKGn9+gq+j9HNSbbnTI6ThBvZyrRFvxqUQrk8ykJc4Q5QXd2GVRtb+/pg1Ne3ccsocehObGgeRe283MzU4rKClHnzmZurI0no0gm1dNLFoIsjYwe7xitWFlUTg9o6kbOiRmyo55tJGRDfnf70o2PHJ3o3GyAAmAV1n1tZdH9REQ0AKLGj46rLxo0HX9gkQL39LHOBEVUNJs5Y8bK4obnZEhA99XZzG5L3AgxBxnQmJ+c5wPFR7A6foVdvI2eI8A0DJClnJkv4Fd8mQM6piQUYTB/EHo7Mvy3q6/e+/tFDpbQERmDgPMypGu03E4mTEircGuYDLgM0p3iOYZghuXVBnk4+07Q69yARoPade9o3rHqstbUVp9TTum7jL6qc3ly6TNlBQQhAucSgBVck7htjHDQOICQTQgI0/d3n40mJ8Gtsa4jl150AQj19k3zjjjtaWk7NMGk0oRxAZDYhokzvSQChxPtVlbivY9klGc+mjdiIi0LNvSnMpmn3oFJVna9/4OnaIcyqe1d3N65LTqxh6PWX3/kzPsxfi4lsHx4G0Kx63XgsQi4nQC4SuTjE4ytLDWJR5/BhUHyAXnvttQ/sgGcD5OTwGaQfP/v6b6rOPrRxOozjQ07nC76CCiqioOKhpyC+gIKCaDzQExWjc+IqFNpFrJIGiiE4LbGOYEfVVFjZrNCKWzc6nftH7fR24LRO3DkcujpQodu6wm7VoUPdufl5flkFv21DmqS9u+eT7/M8+TXJdXC7/c5eGD0ubaoEg75AijMLgIEgBzrMJZQACQOx0P9M9F8dg6PKeIdPUbdjlsl9vaXyvoNcQwCVUjkQpfRoS8ZHDl0lp5je8j5Z8Z4b5cZYHP7Uf//2o98OnxoQApGoRf3/Z6vz1PPOWRl7JxJ1YhOh7omQDyDPpEkQQF03d1GD/vnmBIAi8hRCUIht3cWla3L6ply49sZlN9XVKiaoTzZYvExdy8f5uc8/LwMAP/4yHoxThPtgxCbvxBbvVc3ZLlGHFFe7HvkYQGH5OCIbjqHFp59m5OCXI2wS4ASOfOyHzV9nxDvKGDgGOLz2p233tAWcglpQWFpaKnQcokZT+nuBRAaTo8LDzCGZUFqkTZDEBi/YYDOWCDkYBWgCLAGhdqrkqISiJfE9hY7PxkE2fIZxkAJkC6BwtHU1478guuXBQzTwHFadA1M67wFVgw4C6NTOa87kv3PoQnVOLP3n887Trjm0Q+KaiCb+mOCR8L24dHF/Aqer69aDs7NxN/6b0GkbiBDHVjg3Q4qL0k03PdgS26iVAIr0veMvnsXwKV0y/Rn2QLvHIwqig4H6FKCR2yVbBXoVW2CNd8OB+pRUPTpCFpTDoF1e0GSrt9Hm5szM+mdUmHZWE/eomUKhnesUKF6IpdmCkgC6pEfyzDVACu56xQNWaJ8ThQdArIFeMF4Hu5eUpcBCVpMUKMlQAEHnKRwnUlO2xgC2LSkOQBqAIgoQCkXPYOz+ahlVuBH3cH3O5Qc61S3q6y41qMztgA9c/f5DPafcpYZ7Wh/Ol2dPnHbgwMGVUHRiojvyB5pwXcM3U6ny38GlRKf8+e2Jn4b+pL7wDJPEmFLfV86n/lNb0INyakArpAQByXI4KPbiM49eyN3jdwno5uZrn3y1ezyIvBxoCaPE4vjm5q+/bv4cTJRm1tU27WZCFapXP3l2l6v7FRUestWn65+uo6WlgAIMAjIFbJLNFl7JsjArs8E6qIgKbXXI6DIxuuaAuiMzRqJRQ0IK4wRsDh4+5VyWqLeImZdEtOFSurrA+BIZUBo6qhQiPQa1nfYPEUEPxYcNwwqHxD7IDkWvPOODeuue969++KIXbjidGtTq2dmio92aSwwNpWoDhw8e2tnYmouZpRrXd9eGyuXZ+frZK6avaYaRTqdd19U01zBIcUO/nceZzeedf85P3305+/XfIdUjRPo40omGCdtJfgujtqgT0zjP8/s3gw5PPCJmwx/PXPEcelbt7Jtv44/jgfnUUAXyvNLMDC0Y4UZMeC0VIAMfISNiDHfk3c3NJ2ASQIEKXAoEO/CIqCDKLhWyQBEqWajAqJ3vKseyy3wAQnyIORzEbzQy8KIAKbUJIYgwhQyuGuBmS8BSUuyUpG0TaqpgSfWh2VamAx7dntJjvukZvicOMnQ9ZCMQmVbGyVhWptvy1lZWNjb+Pvm+LvEm8I6maazxSn+WzRQshvlobng4Z6fi5bqWSccN0y4L45ysGU7TKWwxHP7GWdf1yEk9f51QowQh/NMXCdH/Lu4ImO/l/6INfpa5FjxI+DDBY4uPvPYJl9S9uokABKaRsKxwhI+iFCut01XR/rKHqwexa8hQbXjMj4mkm1wcX0KsFC0T4gq+yFayxyr0A5XRY5Usi7OwoTtgCUyyo8egw/y0SBaxCU+FkU9XRpWD1CW/A8IkCLwCgYck1Pt8OgNmTMlCvafIUl5ti6n8J4BwkOov4PW66v1Ytm3bJoGPi4NCkajJWx6eabjcHtHVvNrWxlrJ80tlDmBdM8dyrIFce971PE1PDw8b6SHblD6jBiDfd2isywlHpDlmnDXzb5zN4f8lD9Ek5L45AZ5uaaL500LYwDv5/Yv8UEbpv/CO3Wd3jxy5W9HhFQ6KUSK2DpZX36ZUcCTJ7KeFqQjWgI8SiPzx9cIxiStRk8jJtMnyGOw8ZMpOZ5cCNoJBASHwfIDtmSzzXFaAKpVjwkLWVwRXBTQsEEzFUbUhb4VZsVJYokkAzjnbA3RzAR6yU5dAQgKrzUAJI4k6oSSnMgiz4ImFpBAFTZ80g6Q8DKTUM1fyjACQq1tRL5Bm6ZYlSFKra2Xf0XXTcVxHN7y4MRHISf2pp9OGlvaGNcvwKDeaVuvUMmYswaFp2VGHOCEr5bh+vHwqfcCDPdf89uHs0PzfERwEnj6CHCK6iyuX3v/yE2ScIL2TdwRMGBcpAxHlsaUlEG1KpVgie80sZfPYR/kH+czFxpeJWBBF4liU+eZYu9NHdsosl2sNzIFPsoEheAUBR8vQYQ5wFcVtFDJZ2CQhCYppXsVisiiTymgyWUzykgUdvaJzB5gor/Q+1PVUUEdApQC1bYQgIuc5DgRmIuuhNiBVkMREiDklZiFW3zLTZC4vnnPdTKg7FosjT/JYhgoST62u2hGqvllLOJpugCTaPSFdQMncM/lkRmORwwBROp7w1+5JWXEcROdeW10tlWg+SgBSP7XeddOD2ye++PLz2d8iGCgc1lWXRoAXB5tNIoAI7HK20RikRuEg0qAkQifsx9jz19+VgkEYYZStvEd6Q9IkGD4yF5tCpTgqez0zSZ5VGQmM2Uj8Y9oAErOwSTGJ+vv7k7zd5wOhrLgCHJLvpsVToyCYHmVDNk621X7Hi9X9HbTV+25RqWmvro5SEZBYxlOxYiWchMqBTgzUe/Dwdm+n3HKZvgIrCSFp7dqjeSQ4kSDaXpmLG6Q46ojhsmv7PnaKxchdVkbXjDg9QMq3MhlzLUHhsZygBpG6at6eHaev0Iw0eM1hRuDsrXrKiCccM1WrlcvwkWNeXXPS5iEGOW/p6Jmfz81+vYW3LE3XdD1shRJsPdUsVqvJKv/iZvb219Yrg9LjWZIFuyHZZzljRLGAw8hAoqXs9JiDEg6jhn6cXYMUNqmUBDJPMVFxUCU36CAz5QmgKpEFHgYo9hebyenlSiObLTYqlSZsyG3La8vNZLHCnPJNG4mSQpPk24Uw4ptY1AEHEQZQZForZCeZ4yWMgoaZ/7inZ6DVqu/ttXZOrsm4baPx1vHqSr3Vau216ityQ976zk5dbdBqPbk9oPgoQq2VUtpxXR9ArsZQNhFzZMfUM9HujG64sVLN9lxdt+ccaRkcn7riCJ+y38rFHUcchOVo0+1UbaNlx3EQUIVOSdqNsqY7hl+/+oLTL71zJ/V1Op72I1YIwnwLWZQsZcaq1Wp/sUGgCk8cefST61etEPCi0YwejYY1nRpUqUgOQuzx7PPF6jvQSRiu42JyxWfxrcn+yX6+pdGoFFW5SObpfkxP/g7qwRWaNbX7TzJhy2KTikLY1QJ5NIrJxnKyf3oZZM2i8EBt94CjHyBiumVFD/egqjppRPwCiNcHel+fO37yJcrH673bvT1CgxH14+ODU1O3vTV59K23FvJ5/q35PLOIt4P5/NRtt00NDk4NopGRcR7yLn9yuzWwty3DeHsbdpoYxv3c166e6Z5wROR9nZ8dui1Xi5VXvYSh6d6ak4Gf5Yt9aGvXSv5OOWcAyDehgwvN8tzctamcF+tjcMeGEMOuDCPpuuYm1t7dWG4Wp+ycMWFR3nAV4ot0J23YsXx+oVrFIu9e/9oDFz968VpEj4SdjG7JrxIaecyUSs5+3ag0mpigWsxT/6FjKLGD4JKqUG42KtmGRJ9H8j2qj+AZKvOa/XJodnZ+sNHEZoRZzNFoFJv9SErLNBkSSEUoNBuYCGgCgz2iWayQdoFTUV/KH16UVQBcqA5Wqx0tdnq0sdbIDy7wnGwO3rYw0swfRQu3QQQFRNo6OjnJdB/R0YWFo0fzU1NT4BlHIyMjkBJEoMvP7ex1ba+Y7Ml6AkCaRXnRRE7C03CQReE3V+0YwdAtm9662zIMXeUsc7Xk2l/mDC2jGbaME6Vdg9aAS+hMr482EDIISGXXojuPVVF+KvdvWecW2jwZxvGJCooI4oWiFx5QEYKICBoQleJFRQRBoQhVHGWw0YsKa3U4C0MZMpTSovPQ0NDhoSnrUFiLbcGLQWnS0oQeaG/GoLDpVivbhbIxEUF/z5vV4z/pmzen7tvzy/95ny7ZPpe6MJmQ7BmiySSWkqlkfMQ3+mV78gX/B8c7L91xw4OdEOKrkQUTydAK+bP33QQ0X04HcqVPccnWyvpKEEQ4aF0Beq/Y3mi/iwgfzXTa3tUVIOr9HHMuZ7RsG4YbECJCxIguE2lx0sYeu7gPIBsHB50D8pxCSKabTmkm00EbKsCbfvxxb1KcFiWQEyJ6PJe3Bnt/x97Hog90WdL7e9dPPzFLQ/9v5StXLBAGUmgQjWy1LPb/cLqSSYYvwstGLhha+iYcCi2GlhLJWCgR5vrOJDaPO8vRIMGMBTOZ7ZX1DLEL8hIH5FKEeT2eWo6T5QgWucYwo/Hk8iqw9gUQeT+awUIx8LQnPRdAJNL5TJjRDnHPjndaGwzaIDr46s2nn7vj1mtuwHShDF+RWiFBNqVO700G+Y0BRhsMdieTdnsQhE0QsQR/ikOK08GuLwk72rXgAxojh8wWc6ubFTSid5lZcBR+A5JKee1J5+Tg5ORAeYztSu0sNUzveKIyEJd4tqhuQR2rh/5OOnO6DyWvW6qng8fKM1nENwIR5Enj0/EFmau+VUF5S9eZtT3EeZyPLD0QCOilIl+FBP1+PGjG3SAWCkkJ8E0ongpmokZwPRg19/ffM03DdHMGj+2Yhuvmci4PT+XcVstusWx17Zbd7HabTWl8jR1nuA8dUTxJBDfzA0KcN1xuCwa/yWS+yYSBkFEEopuTkzc/n3z34/Mv3ffgtffd/khsKZMJwzBMsbgUpJZfJiZomgcNmWWwq0l2k2qFMoZ3x2PvWXuSOHx7+NqKL2MfzMO/0WgBqtXNq90yb3zyybtfZjHdd5PdfFttJ/VMDw562fagPW1PuZyVKqqFyo6ohxQb/9nMXm8uomkEUmngNwHdYgkiXfvppwj7I4iFtscLQVFX3QhdOXxPD+xB42onu5izv/56hApH/Vq/0O/3j47Oz8fj4dAZKo0lyDSyxSmX64UCh3BYoSxyyo7DdnocylmINSXHP92pF+qGXL8yOhsUUbFefrqxp5kmPwYKLgUT4VA4QQ7DQ5hx8665R2+696s37nlY/nrfI88tkNwyHMAgiKMTACBYk0G2nc9m8wNe7TzWAQ5TKCk93mS/08tf5Y+fdge7e+1sLJ7iVJMmbsTpxw3DIuVvbAw+kat4l9Jxp9Pr+UMz2Z9Oluyi0ovM4lfhUwSKYiPWUXQAI5uKnDmn60IoIBQk5LTgAhnzX7KupP8tOMDpLwX8Vk5lD+9TPDw8vLw8ROdI8ByeE1+BUa/VaVGhRtjrZdZrPiBUo4vKSI4ZOpyDykNFpw4fMU+zXOjX1kPJzRSBeZ/P8psrFt+uHjNN8uB6JhjKMIVkLJIstXnz9R9ce+ujj1//6n38VYGP3g6rBAgd5D+amj056WnZtj4YRD6RYVUPqiHMF6Qhah1kZ94ZTBmuP9kD0PoyVFxMZLhm3HSNUZ7AUzntFJFqrtK9BE+u6UajEYk0Il4j8pPnVdNVLZJOWwoOL5qZ6FIFQXTOx6EEppldAkiBU/v+2v83ELVvtiKTL+loQnnHByRT/7zQP2pCamyPh+VxbVhoYouaXW+V7VaXO6CFbqHZPcsdjWut8Xi18J5j2+O4s+64djNezpW7qbo9dt9rRp2U3TWHqaZtlM/l7cbR95SFVsLbo6SV52IcGS7IAAQhRiY+DIVlCIm/9uELN1z72JPX3/jU6/wF+lOYwAddEZqfD+Tbe5+0ewNNYyAmA8yHAAtiVUgkQ7K28AkXvFjI6lUG+e3tvM4nh7hhGgYvaUwz566O8EpxNBIoCCoEw0tX0//Vp0p0GpFAllOUwXypDr6KVSo72eM5ggkPFVh6rPktaqgWFMyK3Wyx5/eZET2fl5zWUHRZajv22dGvl2eXP18eugUoCKCzLm4pF4a1MT6oObUh5iBXMSnjHMmErZgk2RVqeE29CuKw+lA6/b44rlyD++FRIapuzW5vzu+vJktEpLLqulFSXJjLnleCNkyJl4p/z2/WPHDXB3c+9Qx/z/eJk8xSgnoSKUBACK1pkb29CLldLTVtkYoEvlK7yO4QrwXyOd/6YCAWOcEflThgXMOHwxyPu7lVnEI0fK9gkX8A+b/SVU/T8woH43gjrVlFy/MinofXvEAxqwUs74c5wulbRcI+Y+P3IPTPVdng82Hhs0F+ryGNonWlY/xz5aAjspxkOOKuJASuJNFXRMDDMMWrUPPxsK8OibIcUnPKsGkO6w4r5aGDC8HNu0ZXt9HXm4v7q5lFropIyYzz0Ufck0FBUhyAqJR/uP/DWx5464Ybnzn9iv/ybkfinZAspwzEWnCheKxrjUZF13SL71WnLsSECaQICaM1P/OoYAYaXqUX8wExm1Q4Lj3KmioSHv/2yax7wXwhWxqNgFWx9IZV2apEGl5DPAavQETOrlblqDQHarERDpqpIYLITMLHJ+BnN3rKav4+pHAKGF3tEClEcs4xeHxEhBKJgdRQo6jQgYoPAT+AoV+oHbFeGLccuzxujm0nN3a7LaOWc8buOOW0crngOGTbbjNjGwmnfimEUj6g2OLxajAZ0SNeyTDWw+EgUSf6SorAysltH1x34+lDdz9z+v3vX3VK4CPyHOMPQYKg05EhWqUWGb+TAohjOFCmEAqSctQ4rC7NStZawTeuafjmkSbluvb/zMIGIu753GgjnPypZjUCIw8i1slI87S05/nHfhqpAtHzLpCw1Dq9uQZglGZ+CcxKBrJjJCAFQkDP04Lg37KuBieOj3Cg9DmaVQG43fzt/NfDs8vm5Xnu3HDqR4c8EWWeNcs2g0+umWvVcuX40My1nLpdaxUYbsy+Xcckzbpdd8bDrmM7jEh2ueuUu7VunbrPduxh02Yga3Zb7+93f/3+j++//60rgBa2l4ObXrWxtWlSTmWiYf/al1JgKbGSjK323nzxtbs7J5//fsozlD8sJGT/zEAQ4r6DQlMcWaKAFcN2IKLYUG/FkEaKi7FX10o6QV5saFo6aQKF21E502WZ4hOtYbS8K0ACSqzwqRdjkfb0yg+/aOlqozIqaUQ+MO9VP63iG8yTjjQ6O96nFxwHSc+DiLRAbWi6Puch4IgfQBRYWFMjPbLkLjs1iMARDL6T/qXZsBTxqkIJTn72hfkxDx+eXxYuudSHh8N+7fyyPnZITtRiY2dIUVZTA0yzNS7ImDJUxV1BleS1fl8KNuwlPhNR/PkvKDVF3UTsuIN++eOVZ3/bB9BqMEoK6i2bRmo9JDcxQty7SBAzftjJDb33Th9+8duTnY56AvEHwKG/qwTW52Nb1pZVKlkj0fH7goQcR4oMsz+jHuEa7VQ0jWArAER3U4zDPRQzFTd9B/FJaI3oq93x1lJsdWX+/VZrxSt58AqUit5FQ9cj1R6DEwkr1u1+U9pfqHqYY3Eroth41QtvvhHgXtRWWiuViO2cWEcN9JZoi9uLFrU6GsnTXpWiAFIGmg1JgAj8c5xiBr+6HPCwKM389dmvhaNLh+m8edg9s4/Ox3zalOpg2HSwgO2o8Hf37Vq53qxjlly/3B+TAWHUbFFOM9b0RYVWPBdtmWYrWHb5mOOuu+vrdvAinBv+us3v2P3x2ctnC1+vRuMxLqikGee6B1AqGeUHMDJ45yj0XH5k98Pdp5RHPfgcL2UkAfLMCZpV2lv+rxyt8VqwioxjOJApLCTBM5/gMa0qxkC+P6rzpqGGHkxrYCKXoShn20ZYZaf02n74YnG05lVji9ikKtuqxdKaPN+4sCbifu3CIjaS5KeKCclwynZKajPM0n8CH5YlDGXT5kYAAAAASUVORK5CYII=\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal nitrogen dioxide (NO2) card with background\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "nitrogen", + "nitrogen dioxide" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_noise_level_card.json b/application/src/main/data/json/system/widget_types/horizontal_noise_level_card.json new file mode 100644 index 0000000000..4b82b62bb7 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_noise_level_card.json @@ -0,0 +1,35 @@ +{ + "fqn": "horizontal_noise_level_card", + "name": "Horizontal noise level card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAnFBMVEUAAADf39/f39/g4ODg4ODf39/////k5OSAwyzg4ODP6a/f8MqQy0bIyMhYWFig0mDOzs67u7v09PTx8fHv9+X5+fn3+/LV1dXb29vi4uLCwsK2trav2Xt0dHTn5+e43Yi/4ZWIxznQ6a+rq6tmZmaYzlNKSkrH5aPn9NiCgoKQkJDt7e3X7L2enp7Y7L2w2Xuo1m48PDytra0hISE2LSZJAAAABnRSTlMAIEDfv1C6kOEmAAADL0lEQVR42u3ai1LaQBiGYar294fshs05hCQEEo6e2/u/t+4unVSoVaiMRuZ71GXdOGNeFobMQA8AAAAAAAAAAAAAAAAAAAAAAAAAAADgI3378mzGxeX1Vze6/K47riP68vzri96VT2cguur16Sz0EdIxCOkahHQNQroGIV1zUEidUOKRcTeiVrKm59YjOspnhLgu1QWRPlMZ0CggCvQPSVcvmWlgD7knC3GW1X3czmjHkB0KZ9r96pCQSe7shtzVxWjRLIJCboon6ekFG7IoFmYcucUiOFnIA2vCnGdlZ+F+yIAtEb4dIniwE+ItNoW3pnVtQrygWd896RC9XLuJ/pL61jtVyICFs6pYEIVmNuHh3yHz2Ww5Z3F8iJQ/i3pNhVfIRDZ1s5HShugDwcatE71JJ9uRXNzqccohTXhCFE85pq343tmG2LaYOXwtJHacF0KoKYKnxqVC3jWul7jNxoQErn60BT9HtG5cE3JC5vSHbJLmvCJrMGUW8zaEmFevhQyY25A9QTvaYff2hOIwNJth7/3taITMk1nONmQemj95pINCPo/DzEPaD1lyrsftjljz+KAQ+xj7HGE1EVzthzzycufJnrOIDwnRE0GfJp5yuBcy5EEbMvy90OmQ21lMZJ/oOQ+ePdnz/ZCcq2NDfHqBf7O/4NMpzO0OCD0u+dG+mJC1tKcv2pBQ8O2xIWMbo79vUrKiiKK+Mgt9ioiorEuK0nR7QC+8J2nJ04dqaP55LPihEly1jzazbkOmef44feMF0clze4miJ5NnIUmiUimVnmWUpZkXjTOVJFJ5N8qLiFTpe15KvrIHlPTo/w0E6zON9Sw0s/YsVvq34Z9LFDGJj71otCFq7EtVqm1ImaR6rtfKVGXShvTTVIfIcSl9nZv59A5hGLcvKfHO+juvfn+oNFPjRGa+2RHlpdnYH0ul1268UioyIZHZkdL7YQ4kWUYfzIYcLS3p3xKZ0gE6ERJFrx306RCdCOkihHQNQroGIV2DkK7p967O4G12Iv9Kf2DgDN5oj/QHBnrfL0fXX93lxTl9qAYAAAAAAAAAAAAAAAAAAAAAAAAAAADgg/wCTYn9BVfiGNYAAAAASUVORK5CYII=", + "description": "Displays the latest noise level telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'noise', label: 'Noise level', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Noise level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value;\\nif (!prevValue) {\\n value = Math.random() * 120;\\n} else {\\n value = prevValue + Math.random() * 40 - 20;\\n}\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"bar_chart\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":50,\"color\":\"#80C32C\"},{\"from\":50,\"to\":70,\"color\":\"#FFA600\"},{\"from\":70,\"to\":90,\"color\":\"#F36900\"},{\"from\":90,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":50,\"color\":\"#80C32C\"},{\"from\":50,\"to\":70,\"color\":\"#FFA600\"},{\"from\":70,\"to\":90,\"color\":\"#F36900\"},{\"from\":90,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal noise level card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"dB\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "sound level", + "acoustic level", + "decibel level", + "volume", + "loudness", + "ambient noise", + "sound intensity", + "acoustic intensity" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_noise_level_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_noise_level_card_with_background.json new file mode 100644 index 0000000000..dd4a79a5d5 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_noise_level_card_with_background.json @@ -0,0 +1,35 @@ +{ + "fqn": "horizontal_noise_level_card_with_background", + "name": "Horizontal noise level card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest noise level telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'noise', label: 'Noise level', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Noise level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value;\\nif (!prevValue) {\\n value = Math.random() * 120;\\n} else {\\n value = prevValue + Math.random() * 40 - 20;\\n}\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"bar_chart\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":50,\"color\":\"#7CC322\"},{\"from\":50,\"to\":70,\"color\":\"#F89E0D\"},{\"from\":70,\"to\":90,\"color\":\"#F77410\"},{\"from\":90,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":50,\"color\":\"#7CC322\"},{\"from\":50,\"to\":70,\"color\":\"#F89E0D\"},{\"from\":70,\"to\":90,\"color\":\"#F77410\"},{\"from\":90,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal noise level card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"dB\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "sound level", + "acoustic level", + "decibel level", + "volume", + "loudness", + "ambient noise", + "sound intensity", + "acoustic intensity" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_oval_tank.json b/application/src/main/data/json/system/widget_types/horizontal_oval_tank.json new file mode 100644 index 0000000000..32fa8954f4 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_oval_tank.json @@ -0,0 +1,36 @@ +{ + "fqn": "horizontal_oval_tank", + "name": "Horizontal oval tank", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Widget indicates the level of liquid in Horizontal oval tank.", + "descriptor": { + "type": "latest", + "sizeX": 4, + "sizeY": 4, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.liquidLevelWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.liquidLevelWidget.update();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n}\n\nself.actionSources = function() { \n return { \n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false \n } \n };\n}", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-liquid-level-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-liquid-level-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.floor(Math.random() * 101);\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"tankSelectionType\":\"static\",\"selectedShape\":\"Horizontal Oval\",\"shapeAttributeName\":\"tankShape\",\"tankColor\":{\"type\":\"range\",\"color\":\"#242770\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E73535DE\"},{\"from\":20,\"to\":null,\"color\":\"#242770\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E73535DE';\\n }\\n}\\nreturn '#242770';\"},\"datasourceUnits\":\"%\",\"layout\":\"percentage\",\"volumeSource\":\"static\",\"volumeConstant\":500,\"volumeAttributeName\":\"volume\",\"volumeUnits\":\"L\",\"volumeFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"volumeColor\":\"rgba(0, 0, 0, 0.18)\",\"units\":\"%\",\"widgetUnitsSource\":\"static\",\"widgetUnitsAttributeName\":\"units\",\"liquidColor\":{\"type\":\"range\",\"color\":\"#7A8BFF\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E27C7CDE\"},{\"from\":20,\"to\":null,\"color\":\"#7A8BFF\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FF0000DE\"},{\"from\":20,\"to\":null,\"color\":\"rgba(0,0,0,0.87)\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FF0000DE';\\n }\\n}\\nreturn '#000000DE';\"},\"showBackgroundOverlay\":true,\"backgroundOverlayColor\":{\"type\":\"range\",\"color\":\"rgba(255, 255, 255, 0.76)\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FFEFEFDE\"},{\"from\":20,\"to\":null,\"color\":\"#FFFFFFC2\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FFEFEFDE';\\n }\\n}\\nreturn '#FFFFFFC2';\"},\"showTooltip\":true,\"showTooltipLevel\":true,\"tooltipUnits\":\"%\",\"tooltipLevelDecimals\":0,\"tooltipLevelFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipLevelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.76)\",\"rangeList\":[],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"showTooltipDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":3,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Liquid level\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"titleFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1.5\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"showTitleIcon\":false,\"titleIcon\":\"water_drop\",\"iconColor\":\"#5469FF\",\"decimals\":0,\"enableDataExport\":false,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" + }, + "externalId": null, + "tags": [ + "reservoir", + "container", + "vessel", + "storage unit", + "cistern", + "canister", + "vat", + "basin", + "repository", + "bin", + "hopper" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_ozone__o3__card.json b/application/src/main/data/json/system/widget_types/horizontal_ozone__o3__card.json new file mode 100644 index 0000000000..7668aa908f --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_ozone__o3__card.json @@ -0,0 +1,37 @@ +{ + "fqn": "horizontal_ozone_o3_card", + "name": "Horizontal ozone (O3) card", + "deprecated": false, + "image": "tb-image:b3pvbmUtdmFsdWUtY2FyZC1ob3Jpem9udGFsLnN2Zw==:b3pvbmUtdmFsdWUtY2FyZC1ob3Jpem9udGFsLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjU2IiB5PSI1MiIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjU1IiB4PSIuNSIgeT0iNTIuNSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjEyIiByeD0iMy41Ii8+PGcgZmlsdGVyPSJ1cmwoI2EpIj48cmVjdCB3aWR0aD0iMTg0IiBoZWlnaHQ9IjQwIiB4PSI4IiB5PSI2MCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuNzYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iI0ZGQTYwMCIgZD0iTTI0IDcwYy01LjUyIDAtMTAgNC40OC0xMCAxMHM0LjQ4IDEwIDEwIDEwIDEwLTQuNDggMTAtMTAtNC40OC0xMC0xMC0xMFptLTggMTBjMC0uNjEuMDgtMS4yMS4yMS0xLjc4TDIwLjk5IDgzdjFjMCAxLjEuOSAyIDIgMnYxLjkzQzE5LjA2IDg3LjQzIDE2IDg0LjA3IDE2IDgwWm0xMy44OSA1LjRjLS4yNi0uODEtMS0xLjQtMS45LTEuNGgtMXYtM2MwLS41NS0uNDUtMS0xLTFoLTZ2LTJoMmMuNTUgMCAxLS40NSAxLTF2LTJoMmMxLjEgMCAyLS45IDItMnYtLjQxQzI5LjkyIDczLjc3IDMyIDc2LjY1IDMyIDgwYzAgMi4wOC0uODEgMy45OC0yLjExIDUuNFoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii44NyIgZD0iTTQ4Ljk0OCA3OS43NXYuNTQ3YzAgLjc1Mi0uMDk4IDEuNDI2LS4yOTQgMi4wMjNhNC4zNjYgNC4zNjYgMCAwIDEtLjg0IDEuNTI1Yy0uMzYuNDE5LS43OTQuNzQtMS4zLjk2NGE0LjE5NCA0LjE5NCAwIDAgMS0xLjY4MS4zMjhjLS42MSAwLTEuMTY5LS4xMS0xLjY3NS0uMzI4YTMuNzYyIDMuNzYyIDAgMCAxLTEuMzA2LS45NjQgNC40NzcgNC40NzcgMCAwIDEtLjg2LTEuNTI1Yy0uMjAxLS41OTctLjMwMi0xLjI3MS0uMzAyLTIuMDIzdi0uNTQ3YzAtLjc1Mi4xLTEuNDI0LjMwMS0yLjAxNy4yLS41OTcuNDgzLTEuMTA1Ljg0OC0xLjUyNC4zNjktLjQyNC44MDQtLjc0NSAxLjMwNi0uOTY0YTQuMTAyIDQuMTAyIDAgMCAxIDEuNjc0LS4zMzUgNC4xMiA0LjEyIDAgMCAxIDEuNjgyLjMzNWMuNTA2LjIxOS45NDEuNTQgMS4zMDYuOTY0LjM2NC40Mi42NDUuOTI3Ljg0IDEuNTI0LjIwMS41OTMuMzAxIDEuMjY1LjMwMSAyLjAxN1ptLTEuNzE2LjU0N3YtLjU2YzAtLjU1Ny0uMDU0LTEuMDQ3LS4xNjQtMS40Ny0uMTA0LS40MjktLjI2Mi0uNzg2LS40NzEtMS4wNzRhMS45NzIgMS45NzIgMCAwIDAtLjc2LS42NTZjLS4zLS4xNS0uNjQtLjIyNi0xLjAxOC0uMjI2cy0uNzE1LjA3Ni0xLjAxMS4yMjZhMi4wNCAyLjA0IDAgMCAwLS43Ni42NTYgMy4xOSAzLjE5IDAgMCAwLS40NyAxLjA3NGMtLjExLjQyMy0uMTY1LjkxMy0uMTY1IDEuNDd2LjU2YzAgLjU1Ni4wNTUgMS4wNDguMTY0IDEuNDc2LjExLjQyOS4yNjkuNzkxLjQ3OSAxLjA4Ny4yMTQuMjkyLjQ3LjUxMy43NjUuNjYzYTIuMjYgMi4yNiAwIDAgMCAxLjAxMi4yMmMuMzgzIDAgLjcyMi0uMDc0IDEuMDE5LS4yMi4yOTYtLjE1LjU0Ni0uMzcxLjc1Mi0uNjYzLjIwNS0uMjk2LjM2LS42NTguNDY0LTEuMDg3LjExLS40MjguMTY0LS45Mi4xNjQtMS40NzZabTkuMjQgMy4zOVY4NWgtNS41MXYtMS4zMTNoNS41MVptLS4xNS01LjA1OEw1MS41MjEgODVoLTEuMDY2di0xLjA2bDQuNzcxLTYuMzM3aDEuMDk0djEuMDI2Wm0tLjYzNy0xLjAyNnYxLjMxM2gtNS4xNDd2LTEuMzEzaDUuMTQ4Wm0xLjkzMiAzLjc4di0uMTU2YzAtLjUzNC4wNzgtMS4wMjguMjMzLTEuNDg0LjE1NS0uNDYuMzc4LS44NTkuNjctMS4xOTZhMy4wMjUgMy4wMjUgMCAwIDEgMS4wOC0uNzkzIDMuNTE2IDMuNTE2IDAgMCAxIDEuNDQ5LS4yODdjLjU0MiAwIDEuMDI1LjA5NiAxLjQ0OS4yODcuNDI4LjE4Ny43OS40NTEgMS4wODcuNzkzLjI5Ni4zMzcuNTIyLjczNi42NzcgMS4xOTYuMTU1LjQ1Ni4yMzIuOTUuMjMyIDEuNDg0di4xNTdjMCAuNTMzLS4wNzcgMS4wMjgtLjIzMiAxLjQ4M2EzLjU1IDMuNTUgMCAwIDEtLjY3NyAxLjE5NyAzLjEzNyAzLjEzNyAwIDAgMS0xLjA4Ljc5MiAzLjU0IDMuNTQgMCAwIDEtMS40NDMuMjggMy42MDcgMy42MDcgMCAwIDEtMS40NTYtLjI4IDMuMTM2IDMuMTM2IDAgMCAxLTEuMDgtLjc5MiAzLjU1IDMuNTUgMCAwIDEtLjY3Ni0xLjE5NyA0LjU4MiA0LjU4MiAwIDAgMS0uMjMzLTEuNDgzWm0xLjY0OC0uMTU2di4xNTdjMCAuMzMyLjAzNC42NDcuMTAyLjk0My4wNjkuMjk2LjE3Ni41NTYuMzIyLjc4LjE0NS4yMjMuMzMyLjM5OC41Ni41MjYuMjI4LjEyNy40OTkuMTkxLjgxMy4xOTEuMzA2IDAgLjU3LS4wNjQuNzk0LS4xOTEuMjI3LS4xMjguNDE0LS4zMDMuNTYtLjUyN2EyLjQgMi40IDAgMCAwIC4zMjEtLjc3OWMuMDczLS4yOTYuMTEtLjYxLjExLS45NDN2LS4xNTdjMC0uMzI5LS4wMzctLjYzOS0uMTEtLjkzYTIuMzkgMi4zOSAwIDAgMC0uMzI4LS43ODYgMS41NyAxLjU3IDAgMCAwLS41Ni0uNTMzIDEuNTQzIDEuNTQzIDAgMCAwLS44LS4xOTljLS4zMSAwLS41NzkuMDY2LS44MDcuMTk5YTEuNTg3IDEuNTg3IDAgMCAwLS41NTMuNTMzYy0uMTQ2LjIyOC0uMjU0LjQ5LS4zMjIuNzg2YTQuMDYyIDQuMDYyIDAgMCAwLS4xMDIuOTNabTguNDk0LTIuMDQ0Vjg1SDY2LjExdi03LjM5N2gxLjU1MmwuMDk2IDEuNThabS0uMjk0IDEuODQ1LS41MzMtLjAwN2E0LjgxMiA0LjgxMiAwIDAgMSAuMjE4LTEuNDQyIDMuNTUgMy41NSAwIDAgMSAuNjAyLTEuMTI4Yy4yNi0uMzE0LjU3LS41NTYuOTMtLjcyNGEyLjc0IDIuNzQgMCAwIDEgMS4yMDMtLjI2Yy4zNTUgMCAuNjc2LjA1Ljk2NC4xNS4yOTEuMDk2LjU0LjI1My43NDUuNDcyLjIxLjIxOS4zNjkuNTAzLjQ3OC44NTQuMTEuMzQ3LjE2NC43NzMuMTY0IDEuMjc5Vjg1aC0xLjY1NHYtNC43ODVjMC0uMzU2LS4wNTItLjYzNi0uMTU3LS44NDFhLjg4Ljg4IDAgMCAwLS40NDQtLjQ0NGMtLjE5Mi0uMDkyLS40MzEtLjEzNy0uNzE4LS4xMzctLjI4MyAwLS41MzYuMDYtLjc2LjE3OC0uMjIyLjExOC0uNDEyLjI4LS41NjcuNDg1LS4xNS4yMDUtLjI2Ni40NDItLjM0OC43MTFhMi45MzggMi45MzggMCAwIDAtLjEyMy44NjFabTkuOTY0IDQuMTA5YTMuOTQ0IDMuOTQ0IDAgMCAxLTEuNDg0LS4yNjcgMy4yNzUgMy4yNzUgMCAwIDEtMS4xMi0uNzU5IDMuMzY0IDMuMzY0IDAgMCAxLS43MDUtMS4xNDEgMy45OTkgMy45OTkgMCAwIDEtLjI0Ni0xLjQxNXYtLjI3NGMwLS41NzkuMDg0LTEuMTAzLjI1My0xLjU3Mi4xNjktLjQ3LjQwMy0uODcuNzA0LTEuMjAzLjMtLjMzNy42NTYtLjU5NSAxLjA2Ni0uNzczLjQxLS4xNzcuODU1LS4yNjYgMS4zMzQtLjI2Ni41MjggMCAuOTkuMDg5IDEuMzg3LjI2Ni4zOTcuMTc4LjcyNS40MjkuOTg1Ljc1Mi4yNjQuMzIuNDYuNy41ODcgMS4xNDIuMTMzLjQ0Mi4xOTkuOTMuMTk5IDEuNDYzdi43MDRoLTUuNzE1VjgwLjYxaDQuMDg4di0uMTNhMi4zMTIgMi4zMTIgMCAwIDAtLjE3OC0uODM0IDEuNDE3IDEuNDE3IDAgMCAwLS40ODUtLjYyOGMtLjIyLS4xNi0uNTEtLjI0LS44NzUtLjI0LS4yNzQgMC0uNTE4LjA2LS43MzIuMTc4LS4yMS4xMTQtLjM4NS4yOC0uNTI2LjUtLjE0MS4yMTgtLjI1LjQ4Mi0uMzI4Ljc5Mi0uMDczLjMwNS0uMTEuNjUtLjExIDEuMDMydi4yNzRjMCAuMzIzLjA0NC42MjQuMTMuOTAyLjA5MS4yNzQuMjI0LjUxMy4zOTcuNzE4LjE3My4yMDUuMzgyLjM2Ny42MjkuNDg1LjI0Ni4xMTQuNTI2LjE3MS44NC4xNzEuMzk3IDAgLjc1LS4wOCAxLjA2LS4yNC4zMS0uMTU5LjU3OS0uMzg0LjgwNy0uNjc2bC44NjguODRjLS4xNi4yMzMtLjM2Ny40NTctLjYyMi42Ny0uMjU1LjIxLS41NjguMzgxLS45MzcuNTE0YTMuNzI3IDMuNzI3IDAgMCAxLTEuMjcxLjE5OFoiLz48cGF0aCBmaWxsPSIjRkZBNjAwIiBkPSJNOTUuNSA3Mi43MzJWODdoLTIuMzUzVjc1LjUyNWwtMy40ODcgMS4xODJ2LTEuOTQzbDUuNTU3LTIuMDMyaC4yODNabTExLjQ2NyAwVjg3aC0yLjM1M1Y3NS41MjVsLTMuNDg3IDEuMTgydi0xLjk0M2w1LjU1Ny0yLjAzMmguMjgzWm0xNC4zMDkgNS45Njd2Mi4zMjRjMCAxLjExNC0uMTExIDIuMDY0LS4zMzIgMi44NTItLjIxNS43ODEtLjUyNyAxLjQxNi0uOTM3IDEuOTA0YTMuNjg1IDMuNjg1IDAgMCAxLTEuNDc1IDEuMDc1Yy0uNTY2LjIyNy0xLjIwMS4zNDEtMS45MDQuMzQxLS41NiAwLTEuMDgxLS4wNzEtMS41NjMtLjIxNGEzLjgyIDMuODIgMCAwIDEtMS4yODktLjY3NCA0LjAyMiA0LjAyMiAwIDAgMS0uOTg2LTEuMTgyYy0uMjY3LS40ODgtLjQ3NS0xLjA3MS0uNjI1LTEuNzQ4LS4xNDMtLjY3Ny0uMjE1LTEuNDYyLS4yMTUtMi4zNTRWNzguN2MwLTEuMTIuMTExLTIuMDY0LjMzMi0yLjgzMi4yMjEtLjc3NS41MzctMS40MDMuOTQ3LTEuODg1YTMuNjEgMy42MSAwIDAgMSAxLjQ2NS0xLjA2NGMuNTczLS4yMjEgMS4yMTEtLjMzMiAxLjkxNC0uMzMyLjU2NyAwIDEuMDg3LjA3MiAxLjU2My4yMTUuNDgxLjEzNi45MTEuMzU1IDEuMjg5LjY1NC4zODQuMy43MDkuNjkuOTc2IDEuMTcyLjI3NC40NzUuNDgyIDEuMDUxLjYyNSAxLjcyOS4xNDQuNjcuMjE1IDEuNDUxLjIxNSAyLjM0M1ptLTIuMzUzIDIuNjU3di0zLjAwOGMwLS41NjctLjAzMy0xLjA2NS0uMDk4LTEuNDk1YTQuNDMxIDQuNDMxIDAgMCAwLS4yOTMtMS4xMDMgMi4yMDEgMi4yMDEgMCAwIDAtLjQ2OS0uNzQyIDEuNjQ5IDEuNjQ5IDAgMCAwLS42NDQtLjQyIDIuMjc2IDIuMjc2IDAgMCAwLS44MTEtLjEzN2MtLjM2NCAwLS42OS4wNzItLjk3Ni4yMTVhMS43NTIgMS43NTIgMCAwIDAtLjcyMy42NjRjLS4xOTUuMzA2LS4zNDUuNzEtLjQ0OSAxLjIxMS0uMDk4LjQ5NS0uMTQ3IDEuMDk3LS4xNDcgMS44MDd2My4wMDhjMCAuNTcyLjAzMyAxLjA3Ny4wOTggMS41MTMuMDY1LjQzNi4xNjMuODEuMjkzIDEuMTIzLjEzLjMwNi4yODYuNTYuNDY5Ljc2Mi4xODguMTk1LjQwMy4zMzguNjQ0LjQzLjI0OC4wOS41MTguMTM2LjgxMS4xMzYuMzcxIDAgLjctLjA3MS45ODYtLjIxNC4yODctLjE0NC41MjctLjM3Mi43MjMtLjY4NC4xOTUtLjMyLjM0Mi0uNzMzLjQzOS0xLjI0LjA5OC0uNTA4LjE0Ny0xLjExNy4xNDctMS44MjZabTE2LjU0Ny00LjkyMmgyLjM2NFY4N2gtMi4xNzhsLS4xODYtMi40NDF2LTguMTI1Wm0uMzQyIDUuNjU0LjkyOC0uMDJjMCAuNzQzLS4wNzIgMS40MjYtLjIxNSAyLjA1MWE1LjAzMyA1LjAzMyAwIDAgMS0uNjQ1IDEuNjMxYy0uMjg2LjQ2Mi0uNjUxLjgyLTEuMDkzIDEuMDc0LS40NDMuMjU0LS45NjcuMzgxLTEuNTczLjM4MS0uNDgxIDAtLjkxNC0uMDY4LTEuMjk4LS4yMDVhMi41NzIgMi41NzIgMCAwIDEtLjk5Ni0uNjY0Yy0uMjgtLjMtLjUxMi0uNjktLjY5NC0xLjE3MmwtLjMwMy0yLjY2NmgxLjMzOGMwIC41ODYuMDQ5IDEuMDY0LjE0NyAxLjQzNi4wOTcuMzcuMjM0LjY1Ny40MS44NTkuMTc2LjE5NS4zODEuMzMyLjYxNS40MS4yNDEuMDc4LjUwMi4xMTcuNzgxLjExNy41MTUgMCAuOTM4LS4wNzggMS4yNy0uMjM0LjMzOS0uMTYzLjYwMi0uMzg3Ljc5MS0uNjc0YTIuODMgMi44MyAwIDAgMCAuNDEtMS4wMjVjLjA4NS0uMzk3LjEyNy0uODMuMTI3LTEuM1ptLTYuODk0LTUuNjU0aDIuMzQzdjE0LjYyOWgtMi4zNDN2LTE0LjYzWm0xOC41MzcgMGgyLjEzOXYxMC4yNzNjMCAuOTUtLjIwMiAxLjc1OC0uNjA2IDIuNDIyYTMuODkgMy44OSAwIDAgMS0xLjY4OSAxLjUxNGMtLjcyMy4zNTEtMS41NTkuNTI3LTIuNTEuNTI3LS40MDQgMC0uODUzLS4wNTktMS4zNDgtLjE3NmE1LjUxMiA1LjUxMiAwIDAgMS0xLjQyNS0uNTY2IDMuNTM0IDMuNTM0IDAgMCAxLTEuMTQzLTEuMDA2bDEuMTA0LTEuMzg3Yy4zNzcuNDUuNzk0Ljc3OCAxLjI1Ljk4N2EzLjQxIDMuNDEgMCAwIDAgMS40MzUuMzEyYy41NCAwIC45OTktLjEgMS4zNzctLjMwM2EyLjA1IDIuMDUgMCAwIDAgLjg4OS0uODY5Yy4yMDgtLjM4NC4zMTItLjg1My4zMTItMS40MDZ2LTcuOTNsLjIxNS0yLjM5MlptLTcuMTc4IDUuNHYtLjIwNWMwLS44LjA5OC0xLjUzLjI5My0yLjE4OC4xOTYtLjY2NC40NzYtMS4yMzMuODQtMS43MDkuMzY1LS40ODEuODA3LS44NSAxLjMyOC0xLjEwMy41MjEtLjI2IDEuMTEtLjM5IDEuNzY4LS4zOS42ODMgMCAxLjI2Ni4xMjMgMS43NDguMzcuNDg4LjI0OC44OTUuNjAzIDEuMjIxIDEuMDY1LjMyNS40NTYuNTc5IDEuMDAyLjc2MSAxLjY0LjE4OS42MzIuMzI5IDEuMzM1LjQyIDIuMTF2LjY1NGExMC41ODEgMTAuNTgxIDAgMCAxLS40MjkgMi4wNyA1LjcxNyA1LjcxNyAwIDAgMS0uODAxIDEuNjIxIDMuNDEyIDMuNDEyIDAgMCAxLTEuMjMxIDEuMDU1Yy0uNDgxLjI0OC0xLjA1MS4zNzEtMS43MDkuMzcxYTMuNzc4IDMuNzc4IDAgMCAxLTEuNzQ4LS40IDMuOTggMy45OCAwIDAgMS0xLjMyOC0xLjEyMyA1LjM0NiA1LjM0NiAwIDAgMS0uODQtMS43IDcuNDkzIDcuNDkzIDAgMCAxLS4yOTMtMi4xMzhabTIuMzU0LS4yMDV2LjIwNWMwIC40ODIuMDQ1LjkzMS4xMzcgMS4zNDguMDk3LjQxNi4yNDQuNzg0LjQzOSAxLjEwMy4yMDIuMzEzLjQ1Ni41Ni43NjIuNzQyLjMxMi4xNzYuNjguMjY0IDEuMTAzLjI2NC41NTQgMCAxLjAwNi0uMTE3IDEuMzU4LS4zNTIuMzU4LS4yMzQuNjMxLS41NS44Mi0uOTQ3YTQuOCA0LjggMCAwIDAgLjQxLTEuMzQ4di0xLjc2N2E0LjMgNC4zIDAgMCAwLS4yNDQtMS4wNzQgMi44ODcgMi44ODcgMCAwIDAtLjQ3OC0uODcgMiAyIDAgMCAwLS43NjItLjU4NWMtLjMwNi0uMTQ0LS42NjgtLjIxNS0xLjA4NC0uMjE1LS40MjMgMC0uNzkxLjA5MS0xLjEwNC4yNzNhMi4yNDYgMi4yNDYgMCAwIDAtLjc3MS43NTIgMy43MSAzLjcxIDAgMCAwLS40NCAxLjExNCA2LjA2NCA2LjA2NCAwIDAgMC0uMTQ2IDEuMzU3Wm0xNS42ODYtOC44NDgtNS41MzcgMTUuNDRoLTEuODQ2bDUuNTQ3LTE1LjQ0aDEuODM2Wm00LjA4NCA1LjgwMVY4N2gtMi4zNTNWNzYuNDM0aDIuMjE2bC4xMzcgMi4xNDhabS0uMzgxIDIuNzQ0LS44LS4wMWMwLS43MjkuMDkxLTEuNDAzLjI3My0yLjAyMWE0Ljg0MSA0Ljg0MSAwIDAgMSAuODAxLTEuNjExIDMuNTIgMy41MiAwIDAgMSAxLjMwOC0xLjA2NWMuNTI4LS4yNTQgMS4xMzYtLjM4IDEuODI3LS4zOC40ODEgMCAuOTIxLjA3IDEuMzE4LjIxNGEyLjYxIDIuNjEgMCAwIDEgMS4wNDUuNjU0Yy4yOTkuMy41MjcuNjg0LjY4MyAxLjE1My4xNjMuNDY4LjI0NSAxLjAzNS4yNDUgMS42OTlWODdoLTIuMzU0di02LjgzNmMwLS41MTQtLjA3OC0uOTE4LS4yMzQtMS4yMWExLjMgMS4zIDAgMCAwLS42NTUtLjYyNmMtLjI4LS4xMy0uNjE1LS4xOTUtMS4wMDYtLjE5NS0uNDQyIDAtLjgyLjA4NC0xLjEzMi4yNTQtLjMwNi4xNjktLjU1Ny40LS43NTIuNjkzYTMuMDg4IDMuMDg4IDAgMCAwLS40MyAxLjAxNiA1LjMyNiA1LjMyNiAwIDAgMC0uMTM3IDEuMjNabTYuNTUzLS42MjUtMS4xMDMuMjQ0YzAtLjYzOC4wODctMS4yNC4yNjMtMS44MDZhNC42IDQuNiAwIDAgMSAuNzkxLTEuNTA0IDMuNzIgMy43MiAwIDAgMSAxLjI5OS0xLjAyNmMuNTE0LS4yNDcgMS4xMDQtLjM3IDEuNzY4LS4zNy41NCAwIDEuMDIyLjA3NCAxLjQ0NS4yMjQuNDMuMTQzLjc5NC4zNzEgMS4wOTQuNjg0LjI5OS4zMTIuNTI3LjcxOS42ODMgMS4yMi4xNTcuNDk1LjIzNSAxLjA5NC4yMzUgMS43OTdWODdoLTIuMzY0di02Ljg0NmMwLS41MzQtLjA3OC0uOTQ3LS4yMzQtMS4yNGExLjIxNyAxLjIxNyAwIDAgMC0uNjQ1LS42MDVjLS4yNzktLjExOC0uNjE1LS4xNzYtMS4wMDUtLjE3Ni0uMzY1IDAtLjY4Ny4wNjgtLjk2Ny4yMDUtLjI3NC4xMy0uNTA1LjMxNi0uNjk0LjU1NmEyLjQ4MiAyLjQ4MiAwIDAgMC0uNDI5LjgxMSAzLjQ4IDMuNDggMCAwIDAtLjEzNy45OTZabTEwLjQyMi00Ljc1NmguODVjLjI4IDAgLjUxMS0uMDM5LjY5My0uMTE3YS45My45MyAwIDAgMCAuNDEtLjM0Mi44NzcuODc3IDAgMCAwIC4xNDctLjUwOC44MjcuODI3IDAgMCAwLS4yNzQtLjYxNWMtLjE3NS0uMTgyLS40NzItLjI3My0uODg4LS4yNzMtLjMxOSAwLS41ODkuMDY4LS44MTEuMjA1YS42MTYuNjE2IDAgMCAwLS4zMjIuNTU3aC0xLjc3N2MwLS40NjMuMTMtLjg1Ny4zOS0xLjE4MmEyLjQzNSAyLjQzNSAwIDAgMSAxLjA0NS0uNzQyYy40MzYtLjE3LjkxMi0uMjU0IDEuNDI2LS4yNTQuNTkyIDAgMS4xMTMuMDg0IDEuNTYyLjI1NC40NS4xNjMuNzk4LjQwNyAxLjA0NS43MzIuMjU0LjMyNi4zODEuNzMuMzgxIDEuMjExIDAgLjM3MS0uMTE3LjctLjM1MS45ODYtLjIyOC4yODctLjU0Ny41MTEtLjk1Ny42NzQtLjQwNC4xNTYtLjg3OS4yMzUtMS40MjYuMjM1aC0xLjE0M3YtLjgyWm0wIDEuMjIxdi0uOGgxLjE0M2MuNjA1IDAgMS4xMjMuMDcgMS41NTMuMjE0LjQyOS4xMzcuNzU1LjM0OC45NzYuNjM1LjIyOC4yODYuMzQyLjY1Ny4zNDIgMS4xMTMgMCAuNDgyLS4xMzcuODkyLS40MSAxLjIzLS4yNzQuMzQtLjY0OC41OTYtMS4xMjMuNzcyYTQuNDggNC40OCAwIDAgMS0xLjU4Mi4yNjRjLS40ODkgMC0uOTY0LS4wNzgtMS40MjYtLjIzNWEyLjY1MiAyLjY1MiAwIDAgMS0xLjEzMy0uNzYxYy0uMjkzLS4zNDUtLjQzOS0uNzk4LS40MzktMS4zNThoMS43ODdjMCAuMjU0LjExNy40NzIuMzUxLjY1NC4yMzUuMTgzLjU1MS4yNzQuOTQ4LjI3NC40MjMgMCAuNzM1LS4wOTEuOTM3LS4yNzRhLjkxMi45MTIgMCAwIDAgLjMxMy0uNzAzYzAtLjI0Ny0uMDU5LS40NDYtLjE3Ni0uNTk1YS45MTEuOTExIDAgMCAwLS40NzktLjMyMyAyLjI1MSAyLjI1MSAwIDAgMC0uNzMyLS4xMDdoLS44NVoiLz48L2c+PGRlZnM+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjE5NiIgaGVpZ2h0PSI1MiIgeD0iMiIgeT0iNTQiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjMiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA1N18yMzgiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzEwNTdfMjM4IiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PC9kZWZzPjwvc3ZnPg==", + "description": "A beneficial layer in the upper atmosphere, but harmful when present near ground level. Results mainly from vehicle exhaust and industrial emissions.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'ozone', label: 'Ozone', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Ozone\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"public\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#3FA71A\"},{\"from\":50,\"to\":100,\"color\":\"#80C32C\"},{\"from\":100,\"to\":130,\"color\":\"#FFA600\"},{\"from\":130,\"to\":240,\"color\":\"#F36900\"},{\"from\":240,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#3FA71A\"},{\"from\":50,\"to\":100,\"color\":\"#80C32C\"},{\"from\":100,\"to\":130,\"color\":\"#FFA600\"},{\"from\":130,\"to\":240,\"color\":\"#F36900\"},{\"from\":240,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Ozone\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "O3", + "ozone" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_ozone__o3__card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_ozone__o3__card_with_background.json new file mode 100644 index 0000000000..220b5248ce --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_ozone__o3__card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "horizontal_ozone_o3_card_with_background", + "name": "Horizontal ozone (O3) card with background", + "deprecated": false, + "image": "tb-image:b3pvbmUtdmFsdWUtY2FyZC1ob3Jpem9udGFsLXdpdGgtYmFja2dyb3VuZC5zdmc=:b3pvbmUtdmFsdWUtY2FyZC1ob3Jpem9udGFsLXdpdGgtYmFja2dyb3VuZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSI1NiIgeT0iNTIiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjQ0IiB4PSI2IiB5PSI1OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuNzYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iI0Y4OUUwRCIgZD0iTTIyIDcwYy01LjUyIDAtMTAgNC40OC0xMCAxMHM0LjQ4IDEwIDEwIDEwIDEwLTQuNDggMTAtMTAtNC40OC0xMC0xMC0xMFptLTggMTBjMC0uNjEuMDgtMS4yMS4yMS0xLjc4TDE4Ljk5IDgzdjFjMCAxLjEuOSAyIDIgMnYxLjkzQzE3LjA2IDg3LjQzIDE0IDg0LjA3IDE0IDgwWm0xMy44OSA1LjRjLS4yNi0uODEtMS0xLjQtMS45LTEuNGgtMXYtM2MwLS41NS0uNDUtMS0xLTFoLTZ2LTJoMmMuNTUgMCAxLS40NSAxLTF2LTJoMmMxLjEgMCAyLS45IDItMnYtLjQxQzI3LjkyIDczLjc3IDMwIDc2LjY1IDMwIDgwYzAgMi4wOC0uODEgMy45OC0yLjExIDUuNFoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii44NyIgZD0iTTQ2Ljk0OCA3OS43NXYuNTQ3YzAgLjc1Mi0uMDk4IDEuNDI2LS4yOTQgMi4wMjNhNC4zNjYgNC4zNjYgMCAwIDEtLjg0IDEuNTI1Yy0uMzYuNDE5LS43OTQuNzQtMS4zLjk2NGE0LjE5NCA0LjE5NCAwIDAgMS0xLjY4MS4zMjhjLS42MSAwLTEuMTY5LS4xMS0xLjY3NS0uMzI4YTMuNzYyIDMuNzYyIDAgMCAxLTEuMzA2LS45NjQgNC40NzcgNC40NzcgMCAwIDEtLjg2LTEuNTI1Yy0uMjAxLS41OTctLjMwMi0xLjI3MS0uMzAyLTIuMDIzdi0uNTQ3YzAtLjc1Mi4xLTEuNDI0LjMwMS0yLjAxNy4yLS41OTcuNDgzLTEuMTA1Ljg0OC0xLjUyNC4zNjktLjQyNC44MDQtLjc0NSAxLjMwNi0uOTY0YTQuMTAyIDQuMTAyIDAgMCAxIDEuNjc0LS4zMzUgNC4xMiA0LjEyIDAgMCAxIDEuNjgyLjMzNWMuNTA2LjIxOS45NDEuNTQgMS4zMDYuOTY0LjM2NC40Mi42NDUuOTI3Ljg0IDEuNTI0LjIwMS41OTMuMzAxIDEuMjY1LjMwMSAyLjAxN1ptLTEuNzE2LjU0N3YtLjU2YzAtLjU1Ny0uMDU0LTEuMDQ3LS4xNjQtMS40Ny0uMTA0LS40MjktLjI2Mi0uNzg2LS40NzEtMS4wNzRhMS45NzIgMS45NzIgMCAwIDAtLjc2LS42NTZjLS4zLS4xNS0uNjQtLjIyNi0xLjAxOC0uMjI2cy0uNzE1LjA3Ni0xLjAxMS4yMjZhMi4wNCAyLjA0IDAgMCAwLS43Ni42NTYgMy4xOSAzLjE5IDAgMCAwLS40NyAxLjA3NGMtLjExLjQyMy0uMTY1LjkxMy0uMTY1IDEuNDd2LjU2YzAgLjU1Ni4wNTUgMS4wNDguMTY0IDEuNDc2LjExLjQyOS4yNjkuNzkxLjQ3OSAxLjA4Ny4yMTQuMjkyLjQ3LjUxMy43NjUuNjYzYTIuMjYgMi4yNiAwIDAgMCAxLjAxMi4yMmMuMzgzIDAgLjcyMi0uMDc0IDEuMDE5LS4yMi4yOTYtLjE1LjU0Ni0uMzcxLjc1Mi0uNjYzLjIwNS0uMjk2LjM2LS42NTguNDY0LTEuMDg3LjExLS40MjguMTY0LS45Mi4xNjQtMS40NzZabTkuMjQgMy4zOVY4NWgtNS41MXYtMS4zMTNoNS41MVptLS4xNS01LjA1OEw0OS41MjEgODVoLTEuMDY2di0xLjA2bDQuNzcxLTYuMzM3aDEuMDk0djEuMDI2Wm0tLjYzNy0xLjAyNnYxLjMxM2gtNS4xNDd2LTEuMzEzaDUuMTQ4Wm0xLjkzMiAzLjc4di0uMTU2YzAtLjUzNC4wNzgtMS4wMjguMjMzLTEuNDg0LjE1NS0uNDYuMzc4LS44NTkuNjctMS4xOTZhMy4wMjUgMy4wMjUgMCAwIDEgMS4wOC0uNzkzIDMuNTE2IDMuNTE2IDAgMCAxIDEuNDQ5LS4yODdjLjU0MiAwIDEuMDI1LjA5NiAxLjQ0OS4yODcuNDI4LjE4Ny43OS40NTEgMS4wODcuNzkzLjI5Ni4zMzcuNTIyLjczNi42NzcgMS4xOTYuMTU1LjQ1Ni4yMzIuOTUuMjMyIDEuNDg0di4xNTdjMCAuNTMzLS4wNzcgMS4wMjgtLjIzMiAxLjQ4M2EzLjU1IDMuNTUgMCAwIDEtLjY3NyAxLjE5NyAzLjEzNyAzLjEzNyAwIDAgMS0xLjA4Ljc5MiAzLjU0IDMuNTQgMCAwIDEtMS40NDMuMjggMy42MDcgMy42MDcgMCAwIDEtMS40NTYtLjI4IDMuMTM2IDMuMTM2IDAgMCAxLTEuMDgtLjc5MiAzLjU1IDMuNTUgMCAwIDEtLjY3Ni0xLjE5NyA0LjU4MiA0LjU4MiAwIDAgMS0uMjMzLTEuNDgzWm0xLjY0OC0uMTU2di4xNTdjMCAuMzMyLjAzNC42NDcuMTAyLjk0My4wNjkuMjk2LjE3Ni41NTYuMzIyLjc4LjE0NS4yMjMuMzMyLjM5OC41Ni41MjYuMjI4LjEyNy40OTkuMTkxLjgxMy4xOTEuMzA2IDAgLjU3LS4wNjQuNzk0LS4xOTEuMjI3LS4xMjguNDE0LS4zMDMuNTYtLjUyN2EyLjQgMi40IDAgMCAwIC4zMjEtLjc3OWMuMDczLS4yOTYuMTEtLjYxLjExLS45NDN2LS4xNTdjMC0uMzI5LS4wMzctLjYzOS0uMTEtLjkzYTIuMzkgMi4zOSAwIDAgMC0uMzI4LS43ODYgMS41NyAxLjU3IDAgMCAwLS41Ni0uNTMzIDEuNTQzIDEuNTQzIDAgMCAwLS44LS4xOTljLS4zMSAwLS41NzkuMDY2LS44MDcuMTk5YTEuNTg3IDEuNTg3IDAgMCAwLS41NTMuNTMzYy0uMTQ2LjIyOC0uMjU0LjQ5LS4zMjIuNzg2YTQuMDYyIDQuMDYyIDAgMCAwLS4xMDIuOTNabTguNDk0LTIuMDQ0Vjg1SDY0LjExdi03LjM5N2gxLjU1MmwuMDk2IDEuNThabS0uMjk0IDEuODQ1LS41MzMtLjAwN2E0LjgxMiA0LjgxMiAwIDAgMSAuMjE4LTEuNDQyIDMuNTUgMy41NSAwIDAgMSAuNjAyLTEuMTI4Yy4yNi0uMzE0LjU3LS41NTYuOTMtLjcyNGEyLjc0IDIuNzQgMCAwIDEgMS4yMDMtLjI2Yy4zNTUgMCAuNjc2LjA1Ljk2NC4xNS4yOTEuMDk2LjU0LjI1My43NDUuNDcyLjIxLjIxOS4zNjkuNTAzLjQ3OC44NTQuMTEuMzQ3LjE2NC43NzMuMTY0IDEuMjc5Vjg1aC0xLjY1NHYtNC43ODVjMC0uMzU2LS4wNTItLjYzNi0uMTU3LS44NDFhLjg4Ljg4IDAgMCAwLS40NDQtLjQ0NGMtLjE5Mi0uMDkyLS40MzEtLjEzNy0uNzE4LS4xMzctLjI4MyAwLS41MzYuMDYtLjc2LjE3OC0uMjIyLjExOC0uNDEyLjI4LS41NjcuNDg1LS4xNS4yMDUtLjI2Ni40NDItLjM0OC43MTFhMi45MzggMi45MzggMCAwIDAtLjEyMy44NjFabTkuOTY0IDQuMTA5YTMuOTQ0IDMuOTQ0IDAgMCAxLTEuNDg0LS4yNjcgMy4yNzUgMy4yNzUgMCAwIDEtMS4xMi0uNzU5IDMuMzY0IDMuMzY0IDAgMCAxLS43MDUtMS4xNDEgMy45OTkgMy45OTkgMCAwIDEtLjI0Ni0xLjQxNXYtLjI3NGMwLS41NzkuMDg0LTEuMTAzLjI1My0xLjU3Mi4xNjktLjQ3LjQwMy0uODcuNzA0LTEuMjAzLjMtLjMzNy42NTYtLjU5NSAxLjA2Ni0uNzczLjQxLS4xNzcuODU1LS4yNjYgMS4zMzQtLjI2Ni41MjggMCAuOTkuMDg5IDEuMzg3LjI2Ni4zOTcuMTc4LjcyNS40MjkuOTg1Ljc1Mi4yNjQuMzIuNDYuNy41ODcgMS4xNDIuMTMzLjQ0Mi4xOTkuOTMuMTk5IDEuNDYzdi43MDRoLTUuNzE1VjgwLjYxaDQuMDg4di0uMTNhMi4zMTIgMi4zMTIgMCAwIDAtLjE3OC0uODM0IDEuNDE3IDEuNDE3IDAgMCAwLS40ODUtLjYyOGMtLjIyLS4xNi0uNTEtLjI0LS44NzUtLjI0LS4yNzQgMC0uNTE4LjA2LS43MzIuMTc4LS4yMS4xMTQtLjM4NS4yOC0uNTI2LjUtLjE0MS4yMTgtLjI1LjQ4Mi0uMzI4Ljc5Mi0uMDczLjMwNS0uMTEuNjUtLjExIDEuMDMydi4yNzRjMCAuMzIzLjA0NC42MjQuMTMuOTAyLjA5MS4yNzQuMjI0LjUxMy4zOTcuNzE4LjE3My4yMDUuMzgyLjM2Ny42MjkuNDg1LjI0Ni4xMTQuNTI2LjE3MS44NC4xNzEuMzk3IDAgLjc1LS4wOCAxLjA2LS4yNC4zMS0uMTU5LjU3OS0uMzg0LjgwNy0uNjc2bC44NjguODRjLS4xNi4yMzMtLjM2Ny40NTctLjYyMi42Ny0uMjU1LjIxLS41NjguMzgxLS45MzcuNTE0YTMuNzI3IDMuNzI3IDAgMCAxLTEuMjcxLjE5OFoiLz48cGF0aCBmaWxsPSIjRjg5RTBEIiBkPSJNOTcuNSA3Mi43MzJWODdoLTIuMzUzVjc1LjUyNWwtMy40ODcgMS4xODJ2LTEuOTQzbDUuNTU3LTIuMDMyaC4yODNabTExLjQ2NyAwVjg3aC0yLjM1M1Y3NS41MjVsLTMuNDg3IDEuMTgydi0xLjk0M2w1LjU1Ny0yLjAzMmguMjgzWm0xNC4zMDkgNS45Njd2Mi4zMjRjMCAxLjExNC0uMTExIDIuMDY0LS4zMzIgMi44NTItLjIxNS43ODEtLjUyNyAxLjQxNi0uOTM3IDEuOTA0YTMuNjg1IDMuNjg1IDAgMCAxLTEuNDc1IDEuMDc1Yy0uNTY2LjIyNy0xLjIwMS4zNDEtMS45MDQuMzQxLS41NiAwLTEuMDgxLS4wNzEtMS41NjMtLjIxNGEzLjgyIDMuODIgMCAwIDEtMS4yODktLjY3NCA0LjAyMiA0LjAyMiAwIDAgMS0uOTg2LTEuMTgyYy0uMjY3LS40ODgtLjQ3NS0xLjA3MS0uNjI1LTEuNzQ4LS4xNDMtLjY3Ny0uMjE1LTEuNDYyLS4yMTUtMi4zNTRWNzguN2MwLTEuMTIuMTExLTIuMDY0LjMzMi0yLjgzMi4yMjEtLjc3NS41MzctMS40MDMuOTQ3LTEuODg1YTMuNjEgMy42MSAwIDAgMSAxLjQ2NS0xLjA2NGMuNTczLS4yMjEgMS4yMTEtLjMzMiAxLjkxNC0uMzMyLjU2NyAwIDEuMDg3LjA3MiAxLjU2My4yMTUuNDgxLjEzNi45MTEuMzU1IDEuMjg5LjY1NC4zODQuMy43MDkuNjkuOTc2IDEuMTcyLjI3NC40NzUuNDgyIDEuMDUxLjYyNSAxLjcyOS4xNDQuNjcuMjE1IDEuNDUxLjIxNSAyLjM0M1ptLTIuMzUzIDIuNjU3di0zLjAwOGMwLS41NjctLjAzMy0xLjA2NS0uMDk4LTEuNDk1YTQuNDMxIDQuNDMxIDAgMCAwLS4yOTMtMS4xMDMgMi4yMDEgMi4yMDEgMCAwIDAtLjQ2OS0uNzQyIDEuNjQ5IDEuNjQ5IDAgMCAwLS42NDQtLjQyIDIuMjc2IDIuMjc2IDAgMCAwLS44MTEtLjEzN2MtLjM2NCAwLS42OS4wNzItLjk3Ni4yMTVhMS43NTIgMS43NTIgMCAwIDAtLjcyMy42NjRjLS4xOTUuMzA2LS4zNDUuNzEtLjQ0OSAxLjIxMS0uMDk4LjQ5NS0uMTQ3IDEuMDk3LS4xNDcgMS44MDd2My4wMDhjMCAuNTcyLjAzMyAxLjA3Ny4wOTggMS41MTMuMDY1LjQzNi4xNjMuODEuMjkzIDEuMTIzLjEzLjMwNi4yODYuNTYuNDY5Ljc2Mi4xODguMTk1LjQwMy4zMzguNjQ0LjQzLjI0OC4wOS41MTguMTM2LjgxMS4xMzYuMzcxIDAgLjctLjA3MS45ODYtLjIxNC4yODctLjE0NC41MjctLjM3Mi43MjMtLjY4NC4xOTUtLjMyLjM0Mi0uNzMzLjQzOS0xLjI0LjA5OC0uNTA4LjE0Ny0xLjExNy4xNDctMS44MjZabTE2LjU0Ny00LjkyMmgyLjM2NFY4N2gtMi4xNzhsLS4xODYtMi40NDF2LTguMTI1Wm0uMzQyIDUuNjU0LjkyOC0uMDJjMCAuNzQzLS4wNzIgMS40MjYtLjIxNSAyLjA1MWE1LjAzMyA1LjAzMyAwIDAgMS0uNjQ1IDEuNjMxYy0uMjg2LjQ2Mi0uNjUxLjgyLTEuMDkzIDEuMDc0LS40NDMuMjU0LS45NjcuMzgxLTEuNTczLjM4MS0uNDgxIDAtLjkxNC0uMDY4LTEuMjk4LS4yMDVhMi41NzIgMi41NzIgMCAwIDEtLjk5Ni0uNjY0Yy0uMjgtLjMtLjUxMi0uNjktLjY5NC0xLjE3MmwtLjMwMy0yLjY2NmgxLjMzOGMwIC41ODYuMDQ5IDEuMDY0LjE0NyAxLjQzNi4wOTcuMzcuMjM0LjY1Ny40MS44NTkuMTc2LjE5NS4zODEuMzMyLjYxNS40MS4yNDEuMDc4LjUwMi4xMTcuNzgxLjExNy41MTUgMCAuOTM4LS4wNzggMS4yNy0uMjM0LjMzOS0uMTYzLjYwMi0uMzg3Ljc5MS0uNjc0YTIuODMgMi44MyAwIDAgMCAuNDEtMS4wMjVjLjA4NS0uMzk3LjEyNy0uODMuMTI3LTEuM1ptLTYuODk0LTUuNjU0aDIuMzQzdjE0LjYyOWgtMi4zNDN2LTE0LjYzWm0xOC41MzcgMGgyLjEzOXYxMC4yNzNjMCAuOTUtLjIwMiAxLjc1OC0uNjA2IDIuNDIyYTMuODkgMy44OSAwIDAgMS0xLjY4OSAxLjUxNGMtLjcyMy4zNTEtMS41NTkuNTI3LTIuNTEuNTI3LS40MDQgMC0uODUzLS4wNTktMS4zNDgtLjE3NmE1LjUxMiA1LjUxMiAwIDAgMS0xLjQyNS0uNTY2IDMuNTM0IDMuNTM0IDAgMCAxLTEuMTQzLTEuMDA2bDEuMTA0LTEuMzg3Yy4zNzcuNDUuNzk0Ljc3OCAxLjI1Ljk4N2EzLjQxIDMuNDEgMCAwIDAgMS40MzUuMzEyYy41NCAwIC45OTktLjEgMS4zNzctLjMwM2EyLjA1IDIuMDUgMCAwIDAgLjg4OS0uODY5Yy4yMDgtLjM4NC4zMTItLjg1My4zMTItMS40MDZ2LTcuOTNsLjIxNS0yLjM5MlptLTcuMTc4IDUuNHYtLjIwNWMwLS44LjA5OC0xLjUzLjI5My0yLjE4OC4xOTYtLjY2NC40NzYtMS4yMzMuODQtMS43MDkuMzY1LS40ODEuODA3LS44NSAxLjMyOC0xLjEwMy41MjEtLjI2IDEuMTEtLjM5IDEuNzY4LS4zOS42ODMgMCAxLjI2Ni4xMjMgMS43NDguMzcuNDg4LjI0OC44OTUuNjAzIDEuMjIxIDEuMDY1LjMyNS40NTYuNTc5IDEuMDAyLjc2MSAxLjY0LjE4OS42MzIuMzI5IDEuMzM1LjQyIDIuMTF2LjY1NGExMC41ODEgMTAuNTgxIDAgMCAxLS40MjkgMi4wNyA1LjcxNyA1LjcxNyAwIDAgMS0uODAxIDEuNjIxIDMuNDEyIDMuNDEyIDAgMCAxLTEuMjMxIDEuMDU1Yy0uNDgxLjI0OC0xLjA1MS4zNzEtMS43MDkuMzcxYTMuNzc4IDMuNzc4IDAgMCAxLTEuNzQ4LS40IDMuOTggMy45OCAwIDAgMS0xLjMyOC0xLjEyMyA1LjM0NiA1LjM0NiAwIDAgMS0uODQtMS43IDcuNDkzIDcuNDkzIDAgMCAxLS4yOTMtMi4xMzhabTIuMzU0LS4yMDV2LjIwNWMwIC40ODIuMDQ1LjkzMS4xMzcgMS4zNDguMDk3LjQxNi4yNDQuNzg0LjQzOSAxLjEwMy4yMDIuMzEzLjQ1Ni41Ni43NjIuNzQyLjMxMi4xNzYuNjguMjY0IDEuMTAzLjI2NC41NTQgMCAxLjAwNi0uMTE3IDEuMzU4LS4zNTIuMzU4LS4yMzQuNjMxLS41NS44Mi0uOTQ3YTQuOCA0LjggMCAwIDAgLjQxLTEuMzQ4di0xLjc2N2E0LjMgNC4zIDAgMCAwLS4yNDQtMS4wNzQgMi44ODcgMi44ODcgMCAwIDAtLjQ3OC0uODcgMiAyIDAgMCAwLS43NjItLjU4NWMtLjMwNi0uMTQ0LS42NjgtLjIxNS0xLjA4NC0uMjE1LS40MjMgMC0uNzkxLjA5MS0xLjEwNC4yNzNhMi4yNDYgMi4yNDYgMCAwIDAtLjc3MS43NTIgMy43MSAzLjcxIDAgMCAwLS40NCAxLjExNCA2LjA2NCA2LjA2NCAwIDAgMC0uMTQ2IDEuMzU3Wm0xNS42ODYtOC44NDgtNS41MzcgMTUuNDRoLTEuODQ2bDUuNTQ3LTE1LjQ0aDEuODM2Wm00LjA4NCA1LjgwMVY4N2gtMi4zNTNWNzYuNDM0aDIuMjE2bC4xMzcgMi4xNDhabS0uMzgxIDIuNzQ0LS44LS4wMWMwLS43MjkuMDkxLTEuNDAzLjI3My0yLjAyMWE0Ljg0MSA0Ljg0MSAwIDAgMSAuODAxLTEuNjExIDMuNTIgMy41MiAwIDAgMSAxLjMwOC0xLjA2NWMuNTI4LS4yNTQgMS4xMzYtLjM4IDEuODI3LS4zOC40ODEgMCAuOTIxLjA3IDEuMzE4LjIxNGEyLjYxIDIuNjEgMCAwIDEgMS4wNDUuNjU0Yy4yOTkuMy41MjcuNjg0LjY4MyAxLjE1My4xNjMuNDY4LjI0NSAxLjAzNS4yNDUgMS42OTlWODdoLTIuMzU0di02LjgzNmMwLS41MTQtLjA3OC0uOTE4LS4yMzQtMS4yMWExLjMgMS4zIDAgMCAwLS42NTUtLjYyNmMtLjI4LS4xMy0uNjE1LS4xOTUtMS4wMDYtLjE5NS0uNDQyIDAtLjgyLjA4NC0xLjEzMi4yNTQtLjMwNi4xNjktLjU1Ny40LS43NTIuNjkzYTMuMDg4IDMuMDg4IDAgMCAwLS40MyAxLjAxNiA1LjMyNiA1LjMyNiAwIDAgMC0uMTM3IDEuMjNabTYuNTUzLS42MjUtMS4xMDMuMjQ0YzAtLjYzOC4wODctMS4yNC4yNjMtMS44MDZhNC42IDQuNiAwIDAgMSAuNzkxLTEuNTA0IDMuNzIgMy43MiAwIDAgMSAxLjI5OS0xLjAyNmMuNTE0LS4yNDcgMS4xMDQtLjM3IDEuNzY4LS4zNy41NCAwIDEuMDIyLjA3NCAxLjQ0NS4yMjQuNDMuMTQzLjc5NC4zNzEgMS4wOTQuNjg0LjI5OS4zMTIuNTI3LjcxOS42ODMgMS4yMi4xNTcuNDk1LjIzNSAxLjA5NC4yMzUgMS43OTdWODdoLTIuMzY0di02Ljg0NmMwLS41MzQtLjA3OC0uOTQ3LS4yMzQtMS4yNGExLjIxNyAxLjIxNyAwIDAgMC0uNjQ1LS42MDVjLS4yNzktLjExOC0uNjE1LS4xNzYtMS4wMDUtLjE3Ni0uMzY1IDAtLjY4Ny4wNjgtLjk2Ny4yMDUtLjI3NC4xMy0uNTA1LjMxNi0uNjk0LjU1NmEyLjQ4MiAyLjQ4MiAwIDAgMC0uNDI5LjgxMSAzLjQ4IDMuNDggMCAwIDAtLjEzNy45OTZabTEwLjQyMi00Ljc1NmguODVjLjI4IDAgLjUxMS0uMDM5LjY5My0uMTE3YS45My45MyAwIDAgMCAuNDEtLjM0Mi44NzcuODc3IDAgMCAwIC4xNDctLjUwOC44MjcuODI3IDAgMCAwLS4yNzQtLjYxNWMtLjE3NS0uMTgyLS40NzItLjI3My0uODg4LS4yNzMtLjMxOSAwLS41ODkuMDY4LS44MTEuMjA1YS42MTYuNjE2IDAgMCAwLS4zMjIuNTU3aC0xLjc3N2MwLS40NjMuMTMtLjg1Ny4zOS0xLjE4MmEyLjQzNSAyLjQzNSAwIDAgMSAxLjA0NS0uNzQyYy40MzYtLjE3LjkxMi0uMjU0IDEuNDI2LS4yNTQuNTkyIDAgMS4xMTMuMDg0IDEuNTYyLjI1NC40NS4xNjMuNzk4LjQwNyAxLjA0NS43MzIuMjU0LjMyNi4zODEuNzMuMzgxIDEuMjExIDAgLjM3MS0uMTE3LjctLjM1MS45ODYtLjIyOC4yODctLjU0Ny41MTEtLjk1Ny42NzQtLjQwNC4xNTYtLjg3OS4yMzUtMS40MjYuMjM1aC0xLjE0M3YtLjgyWm0wIDEuMjIxdi0uOGgxLjE0M2MuNjA1IDAgMS4xMjMuMDcgMS41NTMuMjE0LjQyOS4xMzcuNzU1LjM0OC45NzYuNjM1LjIyOC4yODYuMzQyLjY1Ny4zNDIgMS4xMTMgMCAuNDgyLS4xMzcuODkyLS40MSAxLjIzLS4yNzQuMzQtLjY0OC41OTYtMS4xMjMuNzcyYTQuNDggNC40OCAwIDAgMS0xLjU4Mi4yNjRjLS40ODkgMC0uOTY0LS4wNzgtMS40MjYtLjIzNWEyLjY1MiAyLjY1MiAwIDAgMS0xLjEzMy0uNzYxYy0uMjkzLS4zNDUtLjQzOS0uNzk4LS40MzktMS4zNThoMS43ODdjMCAuMjU0LjExNy40NzIuMzUxLjY1NC4yMzUuMTgzLjU1MS4yNzQuOTQ4LjI3NC40MjMgMCAuNzM1LS4wOTEuOTM3LS4yNzRhLjkxMi45MTIgMCAwIDAgLjMxMy0uNzAzYzAtLjI0Ny0uMDU5LS40NDYtLjE3Ni0uNTk1YS45MTEuOTExIDAgMCAwLS40NzktLjMyMyAyLjI1MSAyLjI1MSAwIDAgMC0uNzMyLS4xMDdoLS44NVoiLz48L2c+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHBhdHRlcm5Db250ZW50VW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48dXNlIHhsaW5rOmhyZWY9IiNjIiB0cmFuc2Zvcm09Im1hdHJpeCguMDA1MDMgMCAwIC4wMTc5NSAwIC0uOTI3KSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjU2IiB4PSIwIiB5PSI1MiIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMyIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMTAzXzI1NjMiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzExMDNfMjU2MyIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFJQkFRRUJBUUlCQVFFQ0FnSUNBZ1FEQWdJQ0FnVUVCQU1FQmdVR0JnWUZCZ1lHQndrSUJnY0pCd1lHQ0FzSUNRb0tDZ29LQmdnTERBc0tEQWtLQ2dyLzJ3QkRBUUlDQWdJQ0FnVURBd1VLQndZSENnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ3Ivd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhnQUFBUVFEQVFFQkFBQUFBQUFBQUFBQUF3SUVCUVlCQndnQUNRci94QUFjQVFBREFRRUJBUUVCQUFBQUFBQUFBQUFCQWdNRUFBVUdCd2ovMmdBTUF3RUFBaEFERUFBQUFQcVY5TDgwWmVPaE9wY0JqcnhsTGhYTXBPcGNxVEs1VUxnTTVESFJqS3gxWXlzVldjS1NxeFF4QVM4ZVpmYjhzeWs4Mk9DNFZqSXhsTGhXTXBjSVRLeDBkeE56b1hJSmxKVkp1SlF4MTQ0SlR4dTVaNHhITWZyWnpveFpzZENaV09oUE5qcXhGYzgyT2hPanVFSjFMZ0Z3cE1wT0FYZ1FBaEN5Q0JWa0tjYzIrMXhsWXMyTk56STVwdVpHTk56emM4MmNSWndyT0pzZE9Lb1dxNVhsSXVSeXdwQ3BPVXhWWEFsRTU0K3lZc25OR2hVY3FVTkNocDBNbEhFWGN4bzRTaFpoU0twZVVnVWlxbUZCVmpsU1JYREtnaUtRcVFyb245UXlsUnl6b1NkU1RvV1ZqUW9WS25qVThxR1NpNVVWTUxpcXdxMEdaaks5NVFxUVV2WVVLbUZoU2N1Z1A2SC9BQzV1NEVCS1pkVHlHaGxiT3gxWmlCanJSekdqM0hzZDU5VVhxeCtuUXlQR2JNS1NsbjhuMDIxWUxLb0lUTjNtYlMyMFp4c05jZmFmTVNlUFpJNU5IaXE3d2lQVThtQzlieWE3NmZtaGRaWEhzczNqK3hUUEk5YldHUysvSFMrNnNiQ3M2bjZYbXVWcEVYbkgxbEZhWXNxU0tyekdUWlBlZnZxNjJjWjlNeGowdmM5V2dmQzg3ZWREMjR1WmFRNVkwdzU3Vmw4WHZOWTB0Mk9SMlM4cm9VcWU3UFFmYzhPSzM0ZlBONHBuOG1wMTVucTFYRnIybjVmcnQrNHFneW1BcExXMnJMb3JaaTBEY1VtTm9GQzRkWHhENWEyU0ZycjVubzdnOFAyK2g4MWVrNzQzSHFlWFRmZDhXb2VuNTNKWWZrbnkvUzFaajJ1dWY3ZUIwZ1lJalQxZktVbW1mVSszTHoxdWpUSjlZS3pLeG15cytxdGtxM3hiTjkvTWZUYjg4RDErY051YnAzMGZIcTIzei9uSFpkVGwyUGNwdU96ZmRXVkU4QUVOaUtxeTZyZUduZFVkUGFPcWxVQ1p5YnBhbm5NTDA1bnA2RjVyenZUNkYrYzkvYm5uYXVTTitQUnZvZWZYdG1iVFJuQVB6WmlTcWZlQ2J1MForalBKc2dOVHFKcjlHb0FycmE4b2paa2pMNVkrc2tWamgweEtqTExxay9POVBlZmllcnRQSm81ODFZK05OL25NTkVhOXFuQkpaRGQrZ2ZIcDhyTG1YRTZPdTVnODZ0UVV4dTErNHBscHcrbk0xMDRvUnAxeCtqR1RNTkRmRHZsL1AzYkd4WGFsZE02c25OL1RnU1UyVnJxUDZHL0gzblZpS3hCMlJ6YWlSRlpRbWlWUnRPaDFTbVVteGVjMXl5SE1pVmF2SzBQbnVmTHBaUk1PVnFGRTFDWWFuck9BZmhPUDBCK2Y2WmxLa09EeW1Ed2RJOHFtV0Uwd3B1dlBBV1NPSE5rSzBNb0drWlBtVjYvQ3REbTlDRDExYVJYQ3JzS1cwNkpvei9hUExyTWpZVXQrQVNHeko2aXZtbk1EcE1yRjZZd041TVNvMU9KMHdyWm5SMHJTY25ZSWRmcFNnaW1zZXBTbkppdjBMTW5reVlGS2xuM05tUnF5b29wV1Y4VWt5SHRKNVpJOGlQN2hjWWhHMUxtclRKUFNwblFFZTFmSnFjajBPWlJ4L1FwNzB6VGM2ZzZzdnVEd2FrTlNvbTV5NitNNkxTT3FRbERrZFl6YlFlVzNPVUhGTnZEc04za2J4R1VmREJZUDZHUG9VTk1rVGxna0hLSEpLa0hVNHB5cEh1QTVMUXRtZWUxWm9QRHB5R2hzMVJJeFpWOGhHeTRYc2NjbmtrS1Uvd0QveEFBakVBQUJCUUFCQkFNQkFRQUFBQUFBQUFBQ0FRTUVCUVlBQndnVEZCRVNGUllKLzlvQUNBRUJBQUVDQVJRVUFRRVJFUlFFRkFRRUhnb0hBNEtDZ0lJaUlpS0R3VkhnOEJSVVVCQkVVQkI0Q2dvOEhnOERnOFRnOEhpY0ZFUVVIbjJGUklGRWhVQ0V4TkRGUk1YQk1TQlI0S2lhT0k2anlQSStMNHZDUW1KaWFHRGlHSmlRa0tpb0tMaVBKSVNUN0hzZXlNbEpLU1VmUjhYRU1URjFIaGVCNFh3ZkIwWDBrKzE3WHRKSlNTa3IyaGxwTDlwSlF5eGxnNmpvdUlhT2c2TG91QTZqd3lQT2p5UEk4TDNtODNtUjd6K3dra1pTU2lkL2JLOGF0VXMvMm50VkgxeGFkblFKb1AxaXZrMTM5Uzd0STIzYjBSM1kycTNmOVNHdWUwUzZWbldOWHd5RWlGV3ZWa3BsMThMV0xOYTBGeDNQc2QvV0o2NC9yekx6Mm5OTTcxRmQ2cm4xRi91bTl6RjI4YldJREp4NUsyQzZkTjQzMVMyUGREcysvVFg5MlV1elpWdGlqdGNoM2FZYXdmNmYyR0RtWTM4QWFsbWloVUUyOVk3aGZsZUJJOXV3UzRoNnV2dU14TXh4NWxySUJrNDlEQm82M0ZVbVV6WFhyTWEyZG5KbUNzOHJ0TzVmV2RkUEtCcUNLUXZsTG5XZHRvYlhRdkJvV1o0VmtHUUVSNkEyblMvWTJlUHZxUHB2M05kVHU3dnFqMXRhYzhyVDZTaEpXVmFObXpxN2l0dnF5NGdGQUtCRW1RNUxQRzN2RFZQWXZYeklYVkhvKzFzYjNwN1BxWkpKSURnSTJUUWd3L1YyTkxaWnV5eDlqaEZ4NytSV2w4Q0tmRnM2N1I1clZ0YXpxZDA5a0xLMWs4akJIZnovQUVoakJ3VGtqTWcyTlZZeDViYXE0M0twcE5KNmFRL3lJTm5DMU5wYTZrcm1xS1I4SXJjbEpDU0VMd093SkVOOW14cWJIUHlxWnFzalZMZGE5bFpXUldzR05KeXB3TGlpdk1yWlpsWVM4RWtMNTh3ekdyRnVjY1dabkp0RzVUdVpYK2NDdVlndFFuTTVNNmMybUZtUnBGZTlYenFDeHlNM0hnUGpWb3VLMnZCZGFteDdKSHBWSzVVbHo0OVA4OUl6UmdVeW1zK25FL0MzRktkWi9QQndGVGhNa3dyUk5JZ3F5K3pLQjFZajFBdFA2dG5QdE80SSs3dlM5OE9wNzVYdTZhODZ5U3JWWm9tTGdPSTU5aUVoSnZ4dHBOc2JUcmhQNzI3Zi9RdXcvd0JDOWwzVnkzL3R4T0R4QlFmaEVSUklYQmRGL3dBM2tEbXA2aWFudnQ2cWRiSytRL1R0VXo5R29mUkIrdjBWUHFwaTU1UC94QUJJRUFBQkF3SUNCZ2NEQ0FjRkNRQUFBQUFCQUFJREJCRUZFZ1lUSVRGUllRY1FJakpCY1lFVVlxRVZJek5DVW5LQ2tTQWtOWkt4d2RFV0pUUkRjekJGVTRPaXNzTGg4UC9hQUFnQkFRQURQd0VjRnlSL1RIVndRLzJGMEVBaDFEcUpUdUNQRmMxejZnRUVPb0lMZ2lqeVJSdHZSdDFiRUVFRUZicThicm1nZ3VINkFRUTVxNkhGRGltOFVPSzVybXVhSEZBcng2dWZWejZod1hORGloeFFQaWh4WEE5UUNISDlMM2w3eUhVT0t2MURqMWpxSGgxYzFaRHE1b2NWd1BVQWh2UTRybWh4WE5EaXVhNW9mYVE0cjNselhOSDdTUEZlOGlOemtmRXEvd0JaYzF6WE5jRVZ6WE5jVnY2dWE1b3JHNmZ0UFlIajNXS3J2WndEZk9JcXNjNnduamFQZEcxVEZuWm5iZjN3cG14M2t5K2JSY0xFN0V3VTdKZnVrWC9JckdJVGI1THQ5L1lzUWNmbnFWak9aY3E1NXREVjB6ZnczV0tYczZycG5lb1dJM3RsWis3ZitDeFo3UDFka2JqdzNmeFdsa2NoL3VpVzNQSlpZNHc1WmNKbkhOa04xaXVyMndWQTUremk2N0JpcWFpcGJ4MkFMTklJMllrY28zTnlIK3FyNUdacWVWai9BTDJ4YVZTdXRBSUFQdXJTVmd6VlZWQ1ArV1FzY20rZ3hLRDhIL3RxcnFML0FCVWxTNDhmWmJoR2R1eVNScDUweEN4VS93Q0duYy83cldqK0lXa2NZL1pOYS9uSHFqL0JUTzJGeWRMM3orYXA2aHZiaFlWaGp6dGh5OHdpOXRxU3Z5K2Ewa1o5RlhaMXBTQVErRXU4cGxpbE9mbmFGOS9OVE83L0FHZlJVb09aOVlCK0JZZlF4NjJweGRrVFFOcnBTR2o0cm9XMFdMb01XNlFxV1dSbStLamlkVU8vNkxqNHJvbmZpb29YNk40MktRbXpxNHdSN09lVFBtWFJMMGxWRDZQUS9US0NTcGJ0OWxxSTN3eUg3b2VCbTlMcWdoZDg1WFEramxvN093dHJLcUlqL3dDNHJSTms1ZFMxemp5WkhzV0NVemYyaWZMVmdyRGFjL3E4VDVEeGRzV0pib05WRVBKWXhJZjJqYjdvc3NhZi92aVg5NVkxZjlxU0g4U3hFN0phbDVUbjk1NzB6Z3NtNXl0NDJVa1F2dkNwWWpaN2kxWVpTN1pLN1o1TFFwclM2czBrcElyYjljL0wvRmRER2h0SVpKY1liaWM5dXpTNFl6T1Q1dU5tajgxbzc4bUZ1aEhSTm1ySGY1dU1UTjFUUFNQYTQrb1hTN3BhejJhamx3N0JtSGY4a1VPUjUvRy9NZnlzc1R4T1owK0s0aFBVeU9kZDdwNTNQSlBIYW5XeWdLZDNkYXNXd0N0anhHZ2ZxNW9YaDBiL0FMSjRoWTY4TnB0T05FNmJFbTdBYWlrZnFKZlB4RGorUzZPZWsybU5Ub3Jpa3JwQUx2b3FyNXVWbnA0am1GMmNzYnNvODFXNVMyR1Q0TEhJbmtPZTBEelZYSGZQS1Q2S3J2WnBWYzdmSzBlcWFIRFhWRitRQzBOMGFiYkc5STZPbHNMMnFhbHJEYnlLNkZZNm4yWCsyTktUOXZVeVpQenlvRkZwMkl0M3JadlVjNE54dFR3MTJXUXFXUnJ0WTY0NXFHY2ttSkczemQ3SnpIV2N0WWJzY3FpRTltTzZuQitjakk1cUo1QWZLUHhCVUU1RG56UnM5VmkrQjZuRXNNcVhqVjdZNTRuYnZVTFNQQTVCRHBoVEd2cGZyVFJNQW1aL0ovOEFGYUthYzRmOG82T1lzeWRnK2thTmo0andjMDdXb1ZkOHRSOEZXa2t4UzV2VllsaGRQSlcxVFkyUXhOelNUU1NCcldEaVNkeTBVMGFNbEpvN0FjV25iL214T3l3QS9lM3U5RjBsYVlUMnFOSVpLU0hlMmx3NG1KbzlRY3g5U3BwbnVra2NYT0oydmNibE9IZWVuQmNVQ0UrUGFFQmU1c3NvT2Zjb25zZGF4Q3l5bDlOTVdIeldKVXIvQUo0WENvc1RoK2NZTCtTZEF6WDB6cjhFN2RVTVZMVWJ0NkxQb21xZUoxODdsUFF6ZXdWa1lkQS93Y3NLMGdwVFdZU1ExOXU2RnBIb2ppbnlyZ0dJMUdIMWpOMDlNOHR1T0I0amtWb3ZqZEo4bDZlU3R3bkZvMjJPejVtcTV4OEQ3aDlGVGFJaDFOb2hvaFBYU2VGVGlEOVRGKzZMdVB3WFNoMHJWQmRwZmpyelRNTjQ4UHBScTZlUDhBM25tNjVSY3h6WGI3YkxxenU2T0NiWWc4VXkxazEyOVg4RThiazV3c1dJeU1PVUtyaXpEYVZVRzdtQjNsWlZaQmErbk8vZXFuY1lGTEVjekdGdHVDcTRleTVwS2ptRnBHMktERDJTYks3TE9VTDczS2hvcGM5eVBKTWpBQkxyZmFDd0xTcWlMWmd4OXh2RzlUVTBVczlFelhROEFOclZxRy9JdW0xUHJxY055TXJXc0plMzc0K3Q1NzFSVHhIRmRHTVFqcUlUdEdyZmV5ckthVHR4MkkzaE96QTVNcEE3WE5PSUlPM21ua1hCc3BicCs1WnQ2ajRLQ1ZwczFNc1ErUE1GUVMzT3AycWdrQkdvVkkxNWMwSHlWTGF6ZC9OTWpPWmpGcWZwSXJlU0VQTUlmVmZaZUxWUFR2c3B0YmYyaDdEN3BWYXlNUE5SK052ODFEV3hhcXQyRS9XOEZoZU9Oa3FzTGEyS29jTDJIZGVzYjBNeEo3SU5aQThIdHQ4SGYxVHNRZmFyZ1kxM2k5bjlFT3pJOWwydjdyZ2dMNWR4Nm5oU2NGSTN3UmFnZzlNa3ZkcWVBU3dKekwzWW04RTlwdGE2RXV6WW55ZHFNZkJWOGU2RStheEpuWjFaVTc5a3NDbmE4bUNJK3F4UERIQmhwWDI1S2txSWNranNwS3E0SUhQYS9XeEg0TEQ4Y2lkSFdzMitEcmJRbllkS2NycnRCN3lJTnc2NDRMV0F1alA0Vlp4emJFVDFCUk8zaFJPM0ZTTzdyMVVzdmRtemtnUVE0SnNvUFpXL3NLYU8vWlQzbXhDbFlOb3VvbmJIeHFscVczajJIa3FxRWx6STh3VXNMaURFNEtKNHlUd3FHY0dXbG15bmtxNmp2Ry91Zld0NHFuckduVmd0ZnhDeEZqWFhpekRqWlZrTHM4Y0J0eUNxSVNidElLLzRtM3pSNmluTlJhdWFqZjNsUlZJMmdKcnhlRnlxSWI1NGJoUXkzenNVYjc2c0txaEhaMnFWaHRKRW1XN2lnM09qQ3d5cTcwUXVvSmdYVXJzcFdPVUYzd1FFZ2VMVmlVUkxLbUJ3UE5xcXp0Ykdxd3RPZW11bzM5cDlPUXFkOXl5UGFvNy9BRVNIVTRkV3pZbk5SQjJGU004VmZZU29KMjJjcVdjRnpOaFU5UDNSZEZuZVlvMzcycU4yN3FjdzlseWV6WVV4NDJyRDY1bHBxWmp2UllmTmQ5TGVNOGxpVkptMWVXUWZGVkVUclRVOGpPZVZWRFhuT0x0NGxSMUhmcHg2ZFFzbXBybGwzTGlod1Z1cHpQRmMwSGphcWVmdk1DamZ0aU5sVVE4MU1ObVZZUmd6RExqT0swdEkwQzVOVlV0ajJmaUs2RXNHemUwZEptR3ZMZnEwcjNURStXUUc2NkM0M2xuOXBLdzIrdTNDWmJMbzZ3dUlqUmZBOFR4V2Erek93VTBmNXV1Zmd1bFBGQnFkRjhCd3pDVy9iY0RVdlA3MW0vQmRQbFhzbTA2eS93Q25SUU4vOFYwcGFTc2EzR3RQTVFrRERkcllwZFgvQU5nQ3hDdW5kVTF1TDFVcjNkOTB0UTRrL21WVlh2N2ZOYi9WUDlldm11YUJUZHR1cmduaGNWaG1EMHByc1d4R0NraGJzTTFUTUdOdjVsZEVPQWxqTVM2Uk1NYVhzek4xVStzMmZndlpkQ09GMVBzK3V4V2NYK2tob1A1RjExb1BUR1JtQWFBWXJWMitqa3FhbU9FTzlCbUswOG5CR0VkSGVDMC9PZW9sbS9tMWROK21iblJWV21qNkNFbTRwOEhqRk9OMXU4TzBmVXFTdHFYVkZiTEpPOXpydWtua0wzRStaVmhzM2VhQTNBZnZKMXV5emN2RnpFN2FqOW15Y1BCTnYzN0szaittSExRclFxbjlwMG54cjJacExnUDFhUjk4dS91dEs2TjhNZkpUNkk2TllqaTcyTzdFejdVOFR4YmYycnUzN082dWtIcGl4UmxacEpYc1pUVTBqblVPSDBveXhRWCtMbmU4VlZUU2hzc1dlNTJrV0I4MWhZcEh5elJCcmcyK1lYdUR6OENuUHZxS3FNK1lJL2tzVHBRV3lSRDBlRUdPN2JsMnljNDVKbmpkUjhGN3h2NXB0OXNqdnpRRzVNRGJCbytLN1ZnZk5OdG5LLy9FQUNVUUFRQUNBUVFDQWdJREFRQUFBQUFBQUFFQUVTRXhRVkZoY1lHUm9SQ3h3ZEhoOFAvYUFBZ0JBUUFCUHhEWFE2TlpSTzVNYVJ5YTB2eGY0YnJSR1VJR3RRTGRDQ201Q1dyOFRNVlhMTGxVK1lUaUNNNC9DV0s3Z0JtUm1veU9HZFUwZ2pMc0JORTZJMjhjVWRsU3BWeWcvVi9nMXFkQkxyb2dIRVZYUVJNd3dxdE9adW9LaGFoQ2NNS3FacUlXM0JTcmhOSUVhcWZnSHl5cmQvQTR3eHJjQk1QNW8vRE02bFFlYzNwdkkwaEJqL2FJUVlrOUNhOUpEcE5oRVd6RE1YTnVOUnFCcDlJUk5oQjVobWtoeU9CYjVrS0ttYUZHYkpjd2VnaUc5eElRS0NMZzVzUURTTjlEWTR5U1ZvWmdabExJRy80YzZDWEFxd0xEMXBEZ21yT3FRT2tERjNqWmpmWmxpU1FNYlk0aTNFRHBCWGJPK1FUVzBDbG5VdU9KcVVwTTBwRGNKbXJpVGJpR3pWcUx1SVpBb3hOU1VISmZ4WHJNb1dQTUpqa2pYWXBlTG1yRTJoRzFrSkEzWlJWSzlLd3Z0emcvdUNBczBXM3RNUVVMNXFmWVI0MXRtL1ZMaThiR1hzR1dEYnVFWDdaUytqZFFQelczOXRKZFhQU2dZUzBObC91UytlTFMzeEdidnVQclRjVzhhQ2ZtT2QxRDk2LzFISGRvRi93Vk5nV1ViK3hLSjEyemZaL01jbm5OL3dDWlU4R3RIUFlSUkZnNklYNFNJdzRXL1hDWmhvMS9aREZhSzJ1WlphSG0rbGZ4THBjMFNiNFhLc0wwVEpwY3FWOFIyeEJneWZ1WHFaTkxRL1VOR25kOU1mTlE2dHFqS0U1alorNERWOWYxWTh0R0ZSemJJZkRtbUR4WkJlNHE4Sm1yMXBFR3FJcmE4b3J2cFJiTWJrWnlWTTUvU0RqQTNWUjd3UkZRQWFXZkl4ZHhOUWZVWGI3ZWlzZG8xeWNKbFE4dis0TUNQdWJQM2VXRGVPMkNGVGU5UzFaUFVyVmFrUXJGR2lvNEpaR0JxWFlUYXJ0ZndtY2JnUDhBMkJVOVJUNUdhZzVQbmdSOEIwSWVTdyswZHdtVmhMdVlyU2pZV2lOVy93QXlnNEQzWnRpR1I3SlVkYXFaZVZzbXlRUFYyTlhPYjVsSlVoSHNObFlTODJoYTF1cGdkTWNDaWJudGNSSkMrNXRhZFpVeEVyc2QrTExqa1pJM3d2cWpxMkppTk55RFpSZHFrV29YRVhEbXlUME0rYnY1eVN5V05WUkV0WnJKVXNnNTJXSnFlMHdocjBNY0NEVWpmQW5EakMxaGpzYWNaQ21VUE9IWUhzSFRYYU9LR256OFk4bE95d3ZDdmFDVGNCVWFub1NYS3VBN1dEMzlDak9nc284YTdoMjkzeUZ2YVVqZEh4bE03cm1DS2gzQUd2aVg0MXhpd0pURUpoQzhZYndIU0RKRXNpdURDVUhQNmtYVjk2VVl3Q2R0MzlRclZQR2tEc0ViTzltR1VOWWFXb3dPNUN4dW5tNHJEWGRCOVJVVHFkYnJ3WWVzMkRBWUJaYXZNWGR2azVKQnNrUk1mSHlsbThPMWZhRVR1dXlaZTh4eERPOEVxRWdxbVVsekdCaTFzYVFPaE14SUQzeHl1UHVXWUpCNmxCOTdJdHEyY0plc29iSkRDZFlwRXd3YjFCSzBOVTFoZ1JzYWlZV0FhUFVEblBMY2lhQUlNS2V5TVhLTHlqTUVXVnhJM2xJYkRsYkFadEJwOHJtTXR3WlByZnBpVVRBUEV1SlZDMnljMUNGQnBDVVFkd0ZxOE1NMmdZdUtXeXlwQVlmMEpsdERva2M0aGs1ZVJLb2tEbVdWdU0zMm9iQUpCdVBDWUVGNjdIRVpaNjR2ZUFLQ1RORXVVMWczL0NGeUFVYTFMSkVHSCt0bU9SRmxucGswRVJOT3BUUEt2NGdhdXh5TlNPVWFEVXJzMk5vWDBCbFhVbWVrNjl2NEE3S0lCTG5CNGp3MHVvNjdReGUwTkxxV1Z2QmxpczhZU3dWOEFneFdPV3pVM25yRWxxVkVsTGdpM0NjVW5rZ3hrZ09yM09KWXVJMC9jbDAyQ1hHZFlMS2w4eVpJRGhVNitJd0xyZXlIeEkvQ1RUVmdHNjRBNTViTDl6Qzl6cGpJQlBFV3FVN0k0aGE4UjdVVFJDV0VmQWpINk1lbjJDUGhMaUtST3p6R0l2TlJjU1o0ZkVxVUtBb0U3VXg5VllBZ2cxRGhZU2FuQmtTVW43cVBWSFQ4TjI2U29peVl4Y3NJVzVNcWtSbW0xaW10d01ldGNoS2NQc2xvVThSUk1maVBuL0UxU0dIeWpEMm11b0pQWU5wcVZaYmYxQUtoeXNSRks5a0dxLzNFVmJldFkzbGJCdXhyQ096WEZSOXdLWWdLRUFMQ1VyTXVydEhsb3gzTGh4bUpEY0NFYmlYelY2bWVOanFDbXVJbkVZQXR1b25tMC9US3JYaHNqSzhjbU9HZTJiZmlBSUJQRUtXUTdpU3QxNm82L2NwVXpxTjRNTlRuUkdYQ0pFMUkxMkhxREt3cVNFTE53eE5XR3BWaDZJOVJCZ1BLQ0NYTHQ2NWo2Y2hzRytDR0FzK0lGRjhwVHhyVlY5WEQzR2xZZHJLYlVYaG16bXk4bUM2bE9iSk91ZGxIMlM4czRvaTRWelBkeEpMMm11ZGRTVmhUazMrMkJpNkxCSVBlRU5heFJBMTBsOXNHUm1iY0orSUVkWFJhRnVhTldWMFoyeGlMU3hzb2FXSXpyb3RiNXBVOVRSYnZzdWFJanQzYkd2VWFHR1E4eURhWFVYa3N0SU1xcXIzS2lackZZVkxLbm1qUzdndCt3NFpnM0RzaGhaT0t6SDBBRHZhZy9Nc3ROR1ZsNmUwZkNWMFdJQ3h6ZUxXcmlXUkZoZkZ5OVVDRXBpcEJTR2tEZXBSdFN2YkMwY3NFTXAvcHBxK2tBcXZGRUNsbUp0R09peDdUUTFObXdYNGJQTU5Ed1dHQytVcEE2c1VZMytZNkdIaUYya2JpOWI4UURyTzMzTURTQSthekN5aFVNTjRPdWxxai9NdEZRRXJWOXlza0dpZjZtWG5ob3M3NWlBd2ZNLy9FQUNJUkFBSUNBUVFEQVFFQkFBQUFBQUFBQUFFQ0F4TUFCQVlSRWdVUUZCVVdJUC9hQUFnQkFnRUJBZ0FTMjNYWFczVzIzWFcyV1dXWFgzMzNXV2QrOWx0dGx0dHQvd0JIMFhuVVgzWFhYMzMyM0NRU2lXMjB6R2EzdlpiY1pqTmJkZGRkZGNKN3JoT0o3N3hQejJzTXZjeUdXd3lHVzR6bWEyNjIyNis0VGllOFRDY2c0VGhCR2NrbjBTZlJ3bm5rdFpaWmNKUk9ZK2hqcXJhTXdtTXgxR0dzeFZHTXcxVjlESFZWVUl6RU5GK092aUc4UWZERHdVVzFuMmt1MTVOdUhiZjRBMjMvQUNZMnZGdG1YYkRiYlRiQTI3L05uYXAyZEZ0YitYZlJ2Q1hHdGkxOE91MDhxS2ROTnBadkh6YUw0VWdIall2RkRSSjRsTnZMdGhmQmZqdDRsL0Z5ZVBNakI0akI4bnlwQkhBTVdmT3RiSW1yRzRsM0pCdURUK1ZFM0phUjVHSjljQlZWVmlRd3RvL2pHaStNNlo0Wmtsa0FRNmJVd2VVaDhsTjVLVFZZZjhEQUZNQkJFb3hCeVNjbVh5T203UXpST2txdTJGd1FNNXdFRkdSMWxSamlQeWNPTms4V28wdlhUVEt5Smd3SDBTWE1pekNYdXM4V3BYVXJLa3F5aG1ENU9rc1ZjTG82NmdIcURjWmJDd1lNSDRWRVdOZ0FSTGE3U0dSREZHaStrS3lqQzNQUElaWkVkTVVCZ1JMZDNMR1Y1R05va0Vna0JRcm5QWU1ENkdCNDlTa29IUGJudVdPSEMzcnRZanJLU1R5Q0NHVWdjS1k1aFBoR0U4NGNLbk9RUWV4QjljREJnSUlPREFjRGQreFp0UjlSMVoxbjFmVFlHNDRJNDR3WU1CR0RBclMvVWRTMDV5cXFxc2poVDZJL3h3TTVPcE9vMDJwZlQvTTJsd2tudjM3ZHdBcUwvOFFBTlJFQUFnRUNCQU1HQXdZSEFBQUFBQUFBQUFFQ0VTRURCQkF4QlNCaEVqSkJVWUdSSWpCeEV6T0NvYkhSQmlOQ1EzS1M4UC9hQUFnQkFnRURQd0JDMVk5T3VpMFpJNm5VaWlJdEdTSHR5TGxSQkNFV0dQejA2aVF1WmpvUFdnL0FZeW5qcGI1blU2blU2ajh5dWk1TGI2TXNXMWZucTBkZFh5STY2V0hxeGpvUFJqMHBxOUdoakpESFFhSDVEMWZrSmpHVzJIcFRSakdTSG94a3hra000ZGliVzlUS1UzYjlVWk5LdEczMU11MjI0dW5Rd0hLMWZjeUQ3OG1qaHM3L0FHcmYwTWpGZkRKc3lNSTFsR2I5YUhEZXpYc1NSdzZqZC9lbjZuREl0OXR5WC9kRGdNNGZlWC9GVTRUdkhFOTIwY0xVdDEvczZHVGhMdFljSXYzZjYxTW0wNVN3bFgwL1k0WldrNHRmUS9oNksrUHRlNXdGdW1IaE4rdFRna08vZ3YxZjdNNGJqcDlpRVV2OHFNeVVKVjdOZnhuQ0ZIK1pocjFiZjZNNEUvR0MrdmJJb1VkaWNIWm1QRmJrSXY0NFZPRlM3MkhRNFUrN2IwTXROZkN5RkxFNUpxaEs3YUlwMlJMZEdJck9OVEdtclFabll1c0lzNGoyS1NpbDlXWnFlOGZ6SnpYeHVoa2wzazM2bVNpdTRqS1IvdHIyTWxUdUwyTXF0b293dHhOQ2tJcVRkMFl5OERNTGFMTTMxWDFabW9QN3htYWpiN1JrcGJzcWlUSDRtYXkvM2MvZTVtNFZVL3lKSjN1VjNSZzRxSVNFSkRhZEJMdk5HWGwvWHlMUkVHaFMzUW5zUjh5TFc1UW9Tb3lVRTZSRTVOQ3I1RTRrOExaRFNvMFFrdDZHRkMwYnN4OFhkMFhUbnNTcU5ibHJNYXMwUmtVclFZcUN1S2duOFNKd2RIcEhkRUdoTFNxSXRNcXE4eWlKYkVYR2paRitJbGRNZXpFeGtxREZKTkZDV0d4TjBrUTdWeFNWUmxHV3N4YUlmZ01lazByTW1tVEpsYmpHSm9RaE5PeFJ1eFIxUTFhUktONHNyM2tSbHNVVDBRaFU1UE1UMGNTbTRub2x1UWFJZVl2TVVpanNJVktQUnhaUzBoUG5pK1N3NG9US29hcUpEbDRsRTZEVytpWXE2V2RTaXQ4aG9rdHlFdkVhUlRWamU1WWxvcWFVMGFUTFg1bUlXalJPSlVURU1mSWhwYStSSkxrYUh6MjBwb2hKWE1HRzhrWmQrSmdwV1loM3VZc2liSk5WK1l6RHc5MlFIVFl4NTdPaE43czY2UjhXTHpJZVpId1luVkk2ZkpvcW1HaWZlcVlrNVVrcW1WZUUzU2pKcGZESmZtWmlGYW9veDFKRW1MeUpMd0pNYlphcEpXUC94QUFpRVFBQ0FnRUZBUUVCQVFFQUFBQUFBQUFCQXdJVEFBUUZCaEVTRUJVZ0ZCYi8yZ0FJQVFNQkFRSUFDd3FvS3FwQ1FvTENxZ29LQ2FxUW1rSnFFS3hBUThCUVVFaFFUUlFFVUJJVUVoTk5OSVZWVUYxK1BQZ1JDUWdKQ2dxa0lDQWdJb0NLQWdKcHBxcnI4R0hueDVDd2tKQ1FrSkNBZ0lDUWdKcHFycnJxcUtxcWFxNjRyQ29yRUlyaXFLZ3FLUW1LYUthYWFhcXFhYWFLS0tCZ0lNU0RFQVJqRVJBQVhWWFZWVlZWVlRUVFJRTmVONk85UTNtTytIa00rWHE1aWVXTDVRdms0NUtlVURsLy9WTjVLbmtnNUZQa3Y3NDVBT1NqbFUrUkRrQzlWQ1lXZEZQYjM3ZTVNc2k5T3FSdUtkV05YTnY2TE4wT3QvVU8reTVDZDQvVmh1U3Rlcld4WERJTUR4cWY5RW5UWVJKQXlKN2d5ZWpQSFR4MXV3YWpiQ25JQldLRVYvTys1R2VTbXQwWEI1MUVYaGtHSmtoYzRTelVLZG9XYU5PM3Ewb3lQOEUrams0eGlWOUdSaURIRlMwR29HT1U1TTlPRlF3UTZ3ZnhLSmdZQlpoNEsvSXlPUU9sY2pVZHZWSUdXRFBHQ1BueUllUEVZQmNrVDB4Z1ZsWFVDckVUV3dUWkNjU3FPUmtZQllXRmlQUkFqaFlaTWljNnJDNEJXTGtHU25QQmdIV0NBSFhrUnJLejhJeXVnS2lJcmd1SUt5b29waU1BRVFPdXZwakpKaDBBSWlJaEdNWkNRSXlKODFTUkxUREJuWFhub2o0Y2tzckdBaVlJd0RBWXpCR2RkRDRNQi9qcnljNnp3SUNJeUlBRVFvS0NoRG9ZQ1BnUGY4RWxrVDVoRUxHQmxsZ21ERTRNSDBZUG95enZ3MU1YQm9hSXhpSWlBaDE1N0JKLy84UUFOUkVBQWdFQ0JBTUdCQU1KQUFBQUFBQUFBQUVDRVNFREJCQXhCU0JCRWlKUllZR1JNREp4Z2tLeDhRWVRVcEtob3RIaDhQL2FBQWdCQXdFRFB3QmoxZEJqS0RLaTA4aCtBeDZvUzBYSXhqR09uSWhMVitBOUZRUWhDRnl2blhVVk5IcDVEOE5HTVl4bE9aYzFPZDZQbHNNWjVjaTBYSXRIMEdNOGp5MVl4OHFFSVJCQ0VSWXRGVFJqV3FGb2hDRUxUaVdIdmYwTTV0UkwwWm5aVzdTUzhrWmxSdEplcU15b3V0UFk0aXJ4Z3BVLzd4UnhLRnYzU1gxL1U0akxlRVVjVG5hTTRMMHFjVzI3Y1g2STRxck9uc2NXY2U3MmEvUS9hQ09JMzJMZmJRNHh0TEQvQUtIRnV6dC9halBUaTQ0azVMMlg1VU0vWHN4eFhUMS95Y1NTN3NrL3FjZms2UVVWNkhHbGVlSWw2SEY1L0xpcjIvMFovQ1hmazM5cG1aUnBXbjJuRXBTN2s2K2lYNW80dlM4WlArVWs5eXRtWWNsZEdETG9TYXBDZERpa2E5bkVxY1VpcVNkVE14K1pFcTNJUlpIWk5rNUxka2RtUnBWTXdZTzgwWldhcEtTTWlwVmpKdjBNckJXYjlpTVBrVmZxWng3Tkl6ci9BQk16Yi9HL2N6ZjhiOXpNdmVUTWJhNHlpR2hpTVBxekFhZmVSbHZxWmFWZTRqS3QxN0M5aEw1YkNWUlhFdGpLWmo1NFg4ckdWbGVLcVlSRlJzekV3V3ljQmphRW5jazEzRXpOUlZlenl1aEltbU5Sdm82RFFoVXJVUmh6ZDJNc0loTWd4UjJNV1Y1VVNNREMyVldlaFY4akVLOUMxVHlFeVVTcXZwSklsdXp2YkQ3TkdKM0U3a2syWWgyS3VtblpxU3FQbHFUWXlneHZSclJDanNPSlZDYUxOaktNVjZGVXhkQ2cyTXErU0hVZzBRb0pIUXU2Nk5GdEhGMUxXWW1ya1duUXJabG4yV1N1UHFOb2VqNVdrTVJadUpJWktXeGlKa3lTUk9LMkUwTkZhNlZSUXRibFdrbHBYUmlsWFNndW9uZGFLZ21WSFVtaVNXbS93RXhkQ1VkK1h5R2hvV3F1VkZRVEwyMXNMblRHaGpvSjZKcldtdzB4VXRwVFNueEU5V1UzWko3SWt1aFB3TVVsNGlXN0lKQ1Z2aHBLNWgrSTViSW5RbStwaHJlcklMWkZlZ3ZBbFRZZmdUOENaSmZDaGNrOWtLVm1LRWFwbU5DZEsxUkhacG1ITFpsbXhVM0VpS09xWW10eUtMZnJvdHovMlE9PSIgaWQ9ImMiIHdpZHRoPSIxOTkiIGhlaWdodD0iMTU5Ii8+PC9kZWZzPjwvc3ZnPg==", + "description": "A beneficial layer in the upper atmosphere, but harmful when present near ground level. Results mainly from vehicle exhaust and industrial emissions.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'ozone', label: 'Ozone', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Ozone\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"public\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#3B911C\"},{\"from\":50,\"to\":100,\"color\":\"#7CC322\"},{\"from\":100,\"to\":130,\"color\":\"#F89E0D\"},{\"from\":130,\"to\":240,\"color\":\"#F77410\"},{\"from\":240,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#3B911C\"},{\"from\":50,\"to\":100,\"color\":\"#7CC322\"},{\"from\":100,\"to\":130,\"color\":\"#F89E0D\"},{\"from\":130,\"to\":240,\"color\":\"#F77410\"},{\"from\":240,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:b3pvbmUtaG9yaXpvbnRhbC12YWx1ZS1jYXJkLWJhY2tncm91bmQucG5n:b3pvbmUtaG9yaXpvbnRhbC12YWx1ZS1jYXJkLWJhY2tncm91bmQucG5n;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Ozone\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "O3", + "ozone" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_pm10_card.json b/application/src/main/data/json/system/widget_types/horizontal_pm10_card.json new file mode 100644 index 0000000000..0548e0cafa --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_pm10_card.json @@ -0,0 +1,37 @@ +{ + "fqn": "horizontal_pm10_card", + "name": "Horizontal PM10 card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAllBMVEUAAADf39/f39/g4ODg4ODf39/////k5OT/pgDg4OD/9N//vED/sSD/+u//3p//0oDOzs7z8/P/6L//x2D5+fnIyMj/6b//qxD/46/b29u2trbCwsL/2JBYWFjV1dW8vLzn5+erq6uQkJD/tzD/78//zW//zXD/x187Ozuenp7t7e1mZmb/wlBKSkp0dHT9/f2tra2CgoIEnlQuAAAABnRSTlMAIEDfv1C6kOEmAAADg0lEQVR42u3aiXLaMBSFYdqkh4ssb9jGNjY2OyFLl/d/uUqihTYt0IHSIfR8kzCynMnojwyBgQ4RERERERERERERERERERERERERERHRv/TuzetY7++6b13v7oPp6KZ48+Lu+859jBuQ3nd6uAWfejcSAoZcG4ZcG4ZcG4Zcm/8rZDKddjFpgWYNZI2d6OLK7AvpB4XCd6N19tCMHoCPL/gymqJ9al5wnn4dFv52hCNU4OMV3y7weEgiIrrYhmR4Go6esq6NmUwxXePlvC1JxNBzMwrdSOGglcYrhWidz4+GLMXK1feQl9EIo+nHyeRbyASjs0IC0YN5KBpQdrQUDwflyS8hIZSER0NyccLdjtibp4d4E7Je4yE+a0PcbueisJQl4Ofiw1EK5lC5o36hYEe2u28n3QxU0Ie18LQ6FqJkI/k5ZP0Rm5D45ekLzmeX74lNmskclhLtrrbEDGd2AZutejazA5nZmdBeitrm1Svp/+mO1Ngnxpl8pZaygAnpw92+DlnJLKi1eG42sSGyKBYmoi60hKjNRkpyKMQPkwC1WFrhUuy6xMP+ECWigML9TGD3a+CGWgJ7/Gy+dS7FoZCZ2wm3gXNcjgoXWsL9IW7d3269mR3uugM7VHXg40DIXAwNqMEcF+bnovaGBLsQJcEvIcf/ISoxZriwwv413R09keC3d/ZCttsQijohBCsR+7trrzhy144fX0/E+FMztwPa3Nby7Na//TP6dgmJ265vS9YeTglBEA4AXyTH1tDFmK/HEk6aIu1FdqKH1ByPJ2OkZbk5kR5/UKslT0LPLt/XkoRawm2IDhfucX9pR7l4piA4JWR3/eqfQpomKrMsMqMKVVm16bCKmiaL2seoTYFoHLdtiThyJ6KsxWGBFtPi27Xb0WJ3YeeSezYESzNeiWf2xz85ZPc0bRcSDeMsGkebkHFTmrGZG5dRlbmQXlmakGw4zmKTW8U4QikfG/52ZK8x/8d5+0Crk7/3NP5zVFbRsMmq2O5I1JbVMB5mkZl7bMdZBBuS2h0Zt5/tiaaqcBK1uw7qZ4W+luCiL6zKMfZrshLG2SFirXDRkDQ9dDLGyfyw3kWFyWLAl7pvF0OuDUOuDUOujQn5hFvQ69zfwNvsQHxvPjBwA2+0p+YDA50Pd73uW3f3/pY+VENERERERERERERERERERERERERERET0j3wFxKH86witj0MAAAAASUVORK5CYII=", + "description": "Displays the latest fine and coarse particulate matter (PM10) telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pm10', label: 'PM10', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM10\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"bubble_chart\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#80C32C\"},{\"from\":20,\"to\":50,\"color\":\"#FFA600\"},{\"from\":50,\"to\":150,\"color\":\"#F36900\"},{\"from\":150,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#80C32C\"},{\"from\":20,\"to\":50,\"color\":\"#FFA600\"},{\"from\":50,\"to\":150,\"color\":\"#F36900\"},{\"from\":150,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal temperature card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "pm10", + "coarse particulates", + "coarse particles", + "particulate matter 10", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_pm10_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_pm10_card_with_background.json new file mode 100644 index 0000000000..823b419fdb --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_pm10_card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "horizontal_pm10_card_with_background", + "name": "Horizontal PM10 card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest fine and coarse particulate matter (PM10) telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pm10', label: 'PM10', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM10\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"bubble_chart\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#7CC322\"},{\"from\":20,\"to\":50,\"color\":\"#F89E0D\"},{\"from\":50,\"to\":150,\"color\":\"#F77410\"},{\"from\":150,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#7CC322\"},{\"from\":20,\"to\":50,\"color\":\"#F89E0D\"},{\"from\":50,\"to\":150,\"color\":\"#F77410\"},{\"from\":150,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal temperature card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "pm10", + "coarse particulates", + "coarse particles", + "particulate matter 10", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_pm2_5_card.json b/application/src/main/data/json/system/widget_types/horizontal_pm2_5_card.json new file mode 100644 index 0000000000..b64711d5c3 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_pm2_5_card.json @@ -0,0 +1,38 @@ +{ + "fqn": "horizontal_pm2_5_card", + "name": "Horizontal PM2.5 card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAkFBMVEUAAADf39/f39/g4ODg4ODf39/////k5OSAwyzg4ODv9+XQ6bCQy0bz8/POzs7f8Mqv2XuQykb3+/LIyMj5+fm3t7fA4ZWg0mDb29vH5aOIxznCwsK8vLxYWFjn9Njn5+fV1dXY7L2tra243YiYzlM9PT2QkJCn1m5LS0t0dHTt7e2enp5mZmaCgoKdnZ0vLy8j+senAAAABnRSTlMAIEDfv1C6kOEmAAADWUlEQVR42u3aa1PaQBiGYar2yctmc2gSciaEk4Da9v//u+6uiFMcEVt0kHmuDzG8Ycbcs4TDwICIiIiIiIiIiIiIiIiIiIiIiIiIiIjoM3378lzG1bX31Q2vv5sOL8OXF3hXg5sAFyC7GQxxEYYMOTMMOTcMOTcMOTcMOTdHhcxubz3MWqC/h3c7243OyWshkT9VeDK+T5f9+DewWWOdbu7taJQGOImkyn2Fw5SfYE/i++qIkFhE9HQXkuJhNH5IvfESHjZ2SZY/ZziJXAzd4aC5qP20utYSvRkyF6tWTyHr8Rjj281stgTSNYyR99DiBJRI1OWicZBeYU+Uq0Tmb4bU4uS7FXGbh2WwRL8OAAT3uL3HCfiycv9Pbbvs30QlMLppZ28bkfiAStwEiR/Bygut3gpR8ij+O+TXBkss1+Of7QY/H9beaUIKF5LAEXGzGEgKewLyA0Zc23Na2ElemY0r8GOpjl2RCq871aWupVJzibEfEov2ff0YomMbIvE0F6mr6ULmJiNBvToUkuSxD5vtwj9eJ8YieREiooCpC/ElsiE1gIVUdrpAJFqLfyhk4VYif/up5GQrssq15PshkU3YblcaJsTtFuJvp6o6/PTbiWHvF3X4DJV7VGmJXoQsnkKUxC9DnEMhyq00Ps3KPT5i8fdC1POK+NK9M8SJReydq2L6xoUd3O0PArxfLHO3ItOnkG67SrVE22uk0HhviOPnEZCI1NgZPcYEuCvhZBmyYWgHQ2QAJrMJsrJ8PGAGxydFInFeiN5dIzqf1zYkFz03ez9MQfX+kJ2kFv1XSN+HZZqGZq9BUzZtNmrCvk/D9i5sMyCcBG1bIgjdgTBtcaypFpFCPYeYWzYEc5F6ZU65EvUfIbu3abuQcBSk4SR8DJn0pdk3s0kZNqkLGZalCUlHkzQwuU2Ao7kX8l0IVPI8j6SALk74Nj4sm3DUp01gVyRsy2YUjNLQzO7aSRrChmR2RSbtzB7omwbvtQvZihYdOi3Vh36wKid4XZ+WMP43xBerwIeGZNmhgwH+XZ7jiariecSPul8XQ84NQ84NQ87NBYXcXMDX7EBwM7jyLuCL9sy7Ggy+Xw+9r+766pJ+VENERERERERERERERERERERERERERET0Sf4AN2T4354zjDoAAAAASUVORK5CYII=", + "description": "Displays the latest fine particulate matter (PM2.5) telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pm2.5', label: 'PM2.5', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM2.5\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 120 - 60;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"bubble_chart\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#80C32C\"},{\"from\":10,\"to\":35,\"color\":\"#FFA600\"},{\"from\":35,\"to\":75,\"color\":\"#F36900\"},{\"from\":75,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#80C32C\"},{\"from\":10,\"to\":35,\"color\":\"#FFA600\"},{\"from\":35,\"to\":75,\"color\":\"#F36900\"},{\"from\":75,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal temperature card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "pm2.5", + "particulate", + "matter", + "air", + "fine particulates", + "fine particles", + "particulate matter 2.5", + "airborne particles", + "microscopic particles", + "respirable particles", + "aerosol particles", + "dust particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_pm2_5_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_pm2_5_card_with_background.json new file mode 100644 index 0000000000..0d5690cd8f --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_pm2_5_card_with_background.json @@ -0,0 +1,38 @@ +{ + "fqn": "horizontal_pm2_5_card_with_background", + "name": "Horizontal PM2.5 card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest fine particulate matter (PM2.5) telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pm2.5', label: 'PM2.5', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM2.5\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 120 - 60;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"bubble_chart\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#7CC322\"},{\"from\":10,\"to\":35,\"color\":\"#F89E0D\"},{\"from\":35,\"to\":75,\"color\":\"#F77410\"},{\"from\":75,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#7CC322\"},{\"from\":10,\"to\":35,\"color\":\"#F89E0D\"},{\"from\":35,\"to\":75,\"color\":\"#F77410\"},{\"from\":75,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAaAAAABcCAMAAAAfz5G9AAAA51BMVEXi4+To6err7O3l5efg4eI5Oj0pKi0vMDMyMzY0NTg2ODsmKCs7PD8+P0IfISRAQkUtLjErLC9GR0pJSk1OT1JZWl1RUlUiIycaGx9naGsdHiFWV1okJSlDRUdTVVeQkZN3eHqNjpBkZWeTlJZ+f4KKi40XGRyHiIpsbXBhYmV6fH6EhYhpa22Wl5mBgoVLTU9eX2JbXV9vcHKZmpxxc3V0dXfd3t+en6Gqq6zHyMrY2dq7vL64ubqwsbMUFRmtrrCbnJ6kpafDw8WhoqTPz9G/wMLa29y0tbfT09XV1tjLy82mp6kQERVcMhA+AAA1IUlEQVR42rSaiVLiaBSFnQVZhIqGkKlUisUgBWFL2Bp7scS9W9//fea7xzDopLW7ppljFmkWp/Jx7j3/zRz8+ZYOXuqPt/Tbz+j3/0N87u4/4+Dgw4f7b4/Xnx8urtbd0ajf7/XOVoP5cjmfL+eDwTgIovFgEIw70/60w0Oe6gxHp92JabO+uXo6v7y4OOdweX51s94sJpNut9s+PW1Lpz0+k9+7egNPtHu9/mjKnwjiKIpnjbDVChuNhu2tlu979aZTLZfLlVLx+ITt5LhcbXqzNI54dZrO7A2el3i+7+vlnr07Rbx/ls7scziEvOggDyb72cHhh+3fZF785Ilkx93Pdnv98x+lN6NnNDr+8eeHD5++frz+8vC0aY+mEBoNl4NOp7Oar5aDQZyaZmmwPJt2lkEUjceD5bCny73Y3NxcPQO6tF2AJl3DACB2AwSOvvhk4IzP2VlnHkRpHKdbPgLV8r3EcWqFUhEdnxwesp8Ua3W/EQRjvhtRHBsCqMAH8fK6F/JeBJQG9DJA9lE/5yAh0TFH6A0JRqYtDW37kBBlfBBHAP354dO32493X24mvenZdDSadubjVWe1xEHRzC5f4rpesBwO53MAYaHOsNfuosnmBj2dYx90frVeLxZQEB8BEg1zkCwkPv1pb3QG/DGG2ALi0DA8Sd11qvA5PkGHR0dH5p9aEqbB3OwcRPAJufQChNeQ5/P+kEcZZxHiNaA7+LGB9JN30GtMHHZwdHhtoe3pV+2T4dHR+PzzTQLQ/aevtx8f1u3+9AwNBwFXZBBQ2Ga+x5VwnKY/7gzhFsVBZCWu39M1X6zX6yv4PD2xX1l5g4F0ai8Qn+lUgCT44NHpEGcGUQAiu96+sbGj5zq1WqFQLh4i+ACoWKnVwzjAy2gcpTPDkyT1JOHkunWVOjEyPAJkD+ADoP/Wgn57z0E7ROglkL04aAt8B4jtgAoHoG9fHx82bYrPcNhZRjGFLEqDaOa7CVWnUKs3BtkTsFt1zqxVtY3QBkCwodJdmX221e01ING0x/Dh8ZD+I0WxLrgvQPzm1KqFSrl0fJjppFgWnwF+Xq2WATxxhsnY1JvwkZkwz04tBKAfO0je0em9DpQDpRq0s84W0n4MlH3SvwChT1gIQL3RkNIWzWaBXYy44TWdpO6UKm4rWOGsTszFspAw6gPIWHQ3VLgnAGGlhQCBrXeKdoBG2K2tovf8aLiiWFmpxI8NM4THJTUnJQCqFIsUN9hQ3Yol8080WK5wb4d3zVoeaFz2ptPkgH8SFTniQTozwceKnwlAP/SPHJTno7P0ChFcdueXjPbXgzI+O0AIPLLQ48Wme3q2otrPGmkUW9P1nEqlWSieOK14OYTPmCfoBauzXq93KhiTNd65QetJVznAOEgGxRoQXW3a74FLD6yELq3/0PTnAzqcShQOSMMsvz2zQcVSueb6jQjD8rfhE6WterPZdKSa02y6hiHxM/uElhbkHUzG7r7hoNf9550KJwdp/zcWnXKhbS8d6LetgYgGGaD7+3sjBCAc1BmQ0WbE1dBvzcKkVqg5xcMSV6lD8BqHXkiaWw2nXPAucY0SJ0BZtp5gIaBJGaD+cw8SH+AAiE6GdWTEgRmohQCkvk6Aq5SMjalUqjT9WTzmq8HfJkCmYdKs1ZyaVK05WAkQitkoNMw4zORiL9f5CQflIQmLTu9JqHJB4dclOsjC2xbQBwG6J2gDaDQmRs8B1PC8MA7rfFErJ8dNu0jT4XzmugTeJR2o12tPFjLQGveID4AWAqSYYEbSisdCnADR3tB0yFdgbKEZpwKo5W/5AKhuPahcKpbEp2z1LQ0GHYstS15sfAqCY8JCdUSVEyGFDfhAjd2MVjt420B5Njs8GZ9dhnshcdjhybY9OohdeOBj4vQC0OW6PWw04mBsaw3PawQNao5zdFJNLRSMVnFSYdUxxkD9PutUA7Gh9yy08kGLzWLByUofJyOkutZHPVBhnqGtfyhvBmgAqFTO8UNOHiGbq2ohrgwj+BSceiuNBiy/4IPpZq1mtVIuVKXCDpDnh8reJkpggn8cfdL3HbTFk4ckQNn23hI17yCk0148JEDqQJKxEaDb6/P16aoxiwN9sWes3BvUuOOTWmsJkf500KoVkjCer8w/bboNLtpsJtQ1gEzaPFhjIB5DKAMkPvyiSmf5kEpFvoYPgDBS2gAQeKhPBshtYiGqnABVmtRTEuOQ1KdVbSupAa9QAJEdHKyCEh/ZCkqyCAEchBcP3jbQH9rfGyEgjuz5BPeDHoR+2UF/mnMyQAS4T/cY6PHz1aIftBrxmGLicxoHceo7xYIfDW260InqVbcer3gAGdzSPu1aScM9OKXLg7UBWqCuSUHBmlAWD8w+Z1pKgQc6NhcIrbgJUMtStus4VsDoQ/inBp9osBpioA4LZmtAZq5KAVUqAGq6yBykWOCipsHJmhQQD95rQNk6nXPePzp8F8+WkTDt6puOe4txFuCU3bYGQjZJeLiaTGOPqhLNGrOIlep4kPrNQj2dn9nactCqu62UgoMzzCxdThkKQNi/qRdBDmJIeJD6kPxjfJY4J1OgDgQg85B6EA5y7NICokp9w8zzDu/iTRRDr1nDWiXYwEeAwAEhz4MOZW1rHKmAvusg9KaDdmh2BsrjyTkI6bCHOifgO0BI8Q0+LFPvLm9oQXV/FqVpQAvvMDC1GU8rmg8pTvM0SRoxA1OmcDY/mFjOprDBp2+hG2YA2izYFLXBIz5aCYmPsvLA0jUhwQxEUvS1/uEYaq6m8mQWqjbrPpF+zMzCoAZx6LniUzIToYLFbAEy98AqgwM9wRGg9xJcNonknI9wgqOjtvwS1TCw5xere6hvrwHdo2/G5+vHu8sNbYa+HASUfQoLcctvNv2QOsOFjVosSQYsTo3PDXxGbc114GM1r6s0h8xAwDFpCsdJH9bpGKEVXIgItgoKNMgGjOZqmt8g0rF9/WuuF8pAHZva8crEdSqlMnxsFx+VNHfbd3Ce+FTglyGqHrxlIOHRljdQtonOW3o9PtjzmDRbo4pNVt3YvgHo4aq/igkJg3l7QkshVofUDt+3K0WPpux1LLu1F5BYsMzRWBQYBLxed6O0vVYbUnWTc3gKUdqQ1TfNWhnzZHysxAkQu5ddagEiYPshBgKQ2QerNQkIcg82AoA6kBlIEiDVtYoh/C4gPXi5AMqPsXPL01zCzv2w7bEF7e4zGB+xAY4EoOvP5+3pajxYnfVvbogA3LDxqPAwolszguOZqSbSVDE1oHUGqH/aP52s5R6eEZ8doP5U6Q1A6j8AihSx5Z/Ql3kSDwkQFQ4+ZhCXZghLGqHmdQn/DBkzB7utgtRzmsbGJD7Ak8fsBQL0Co8c9KMetMsH+ZCQo7X/Gw3CI0D4R74B0DYjXN+dL9ojbsV115fnN+s+k9E6sxRqh5PM1A2mVDKaDjFafDbgUTkjwy3MPeARH/4lA6R0beH6mQ/1bQsosoRtbNgTJTEzUA2REAxQ0iKoKElqAGfcBAhly1RnB0h8KG+SAP28g/Il7sezg1cO2uMqSH/fZA5S5xEhfjVA15dPN5NRb3H+5eHifE27CF3Pr1M0Cm7Kcr4/ggRA+hO7adBdr+UmTa1ZCj3nA9C9BDTqbQEpv3GxY5uGm4Gob7YYFpgERs+dpMplrZDUqjWXkBBZZWW8Rv9RdCjDYJvRqHFyEJLtKqjEO6H0hoPYX0eE7y5S5ZndOccI7ayzZwf9truTqhYkQBQ5+ADo8SP3vC+vNk8Pd9d3Fzdtkm3di8Pa0XGp4A/OSAfYZs3wZqIlKF5BGve0oTPBWJubTXarwQApwGV80NyqWxyn5go1IC68NZ/EBY9vfOrwMYOUDk8g5BAnUyganib+sPiNEGwkywUGSOzMWyWmD5oQVfIpLkO0k/igPCFhyXegXbjONSE5aF8NSIS2t4BEKOtBAPr85eLp4svdRwLdpDOPW24jTcqHpao765CtxYMrL4dotQMk46HZDsg2G6OliMAm/cNnOcYNKLb6xqmhWzaegcE9BGXjU6tYSONGd5Exj+vb4kgTbt1cEAesgcRHDoJPdgsJFZmy2h0kAaII1t50EGw4vH2jm+MbQ558A2Lfo4N2PUiEXgO6u8NDn68/Pt7ePZ2Ox0S4NHT4RnIvaNSeyDHdXh934I0FcAAjM2WjBJjBJytxRkgBW4BWy7HB2QKytqJY7QEGQMBBltOKXGVuowqQTVG9xNhofSQ+GaCaALHzLvHZ/v8lh0cZoGoG6HVMeKk8ntcO0i95RC9nohmmX3PQwVYCtHUQeDJAEDKpxl2D6PHx9vb2y2Iaz1zHnWGgJqOejlxCpyFSIyDBBNGLBKit4TZ+ApAIbReq8KH5cP+H4pamMbJVULq9LwogWcdEGynLA38dHRZLDEqJD9gHPvUMUGEHyOTAybApup0cHR2eoMPjUtn4oFwPeu8mwxsDnjfncPkx6cGv6oWDFOIECH01AQgu5IS/aTnXpUSuKApbqSiig8hd5CIgCt1gcxEqJjPDXGJqLJ33f558a3EIznSR+SM72t3iJJXqL2ufdfbeJ38+6v6lPxyXKIa1s0eZVrExHlxp+0PVemDfzLbnSk/KYZBwHxtA8nZhYGRT6Om7BqGaTmUGHEJtboQBF9c5ARU2mcgnA6DCUa3pSYSWbUO+KJ8XBOQ3zxeoiJxuglrQ/EKzyZU+39pl58ztRwW9BrQzwXELiPy1JbORUICTTm8HbxDBxQmQw4SeHvnridvj47MAPT9//dhd1Outw3y11cwUy3To0AnmAIWgHmBQeFNzZySNSEboSkbuOqxHXojMxx2G5RBAbHaBo2D9kXxCm61RLSlLyYSRppylkAIKQh1FxUROAorvvAFy+E9DgfAAg6ieoiC+gkcIJYUdCrJ+dliEXyooPX1g+bxJuA4XXJzCVtuAHp8QkBX0lTz37e94Pq5jkSbnp7lKA4s9AIGqCy4MJCNwKXcJkDsKSXJzfW8D59k3L0RhDXL5GihT8XGw7cSxTTRF4LZTyeLIUsaxgrgcWwMAUhL0ZCJ/KLBZ35CJaSm7SUCnCGgNSGXw8KufFfRj7CwipKtw5pIaWHT46a0AEZutquUeCK0BPT0TLEF2cteL5SLCwuYvT9pUNMfxIIlH95qZQzdWzkiFtcEgNErjEflPtNY9bwXYREidgjFoXBu1fnr0apXcGuKDS5uoJ6j8ZiPGm/cC4iUGjOqHC1AxLEBcvOEJNQVEh3iEp1YToILrdFkFT1tAB7YIKQXtENCO2DELZzxvCCgQ2npt8Fg9SOcj8fDw5fOAGZpKflI8qZWZXauMO4MxVZ4BBc9kyYqvUqoHrxKJCT59Epxmhq/Mx1nuhkRImA/GwBVst4A0OFVtg4dwiUCASFpBFCjHRWk94Q5CsbuUU9ryx37/0tiJ+QiPlqBarekhraxSJToy7v9RULqIsF2CUgZuS0aXVA1OgN5YQo6N1RYe6Hz49umFes315z/u+oO4Ti2slC1FCKi+XNYrzCTQqh52ehBb0jWIY7UQEjCQxViIyHnJwBbB3dUuiVDqijXcPWdqqsM+FUAUBlh1NMMzrRBVWW1MwiFLuhsI3ni6ZceP9gceIDUzf2x1uFzqtmvhFO0oms0mU1pACe1yYcocpFqpOwvZZmM8O6o8jjQi03lrBfEQpq7cC3pippQdEGPVlHES7WqS0SKa9NqlvIfNpvN5uTIe0sWbRuoP9OYeU1N/FCfnyfgbyWjQBc52fJRPGeUGj1s6VpBK4pp8B41G4C2OMoByevkoRrOIJQBJJtQSAOT5+FKLBGdZBUC6htWnWTtzKMVp/dK3Z07k2A9+0QpKCyhl4ByvFZQqIrw9IT8EPk9PtglfWXioozFRuMSIkcxoYC4bkyovtDGbjXu5xnRWnZQbynuTaA6fGFEI0M2A7+6gHyqpYVAEPnw66gzhs3RPNLRQZ1Chr6TQE8/y0GHjGZqjOTAcIqAWgLwGlfSJt6ncwyqDVphv1Npzdkmc4c1lsmFWgJFu/PQ/CtpRJU2lN9MJ375u2bw9HftsAP1ui0AwyPPIPvXxn/cfKbJhiqOoM+IND+Jx1ItnlJNn6tJVStnqhH4mruGYPlmlQ3ZLGMWNE2SSACgZxcsYIiw9OAWbtwGU1D1dqAiukwzqogpQOwDaDsOV1nOiHnEDEAVTO4WcLbYnFbSncbXNJR3fMqaAdMQHAQmQY3MTqJSCUojSu1RiFyKuPxbi9kFondvCSMLTn++fn+ilfnj43OVdzueVKEmuENByNh0s6XwX2/jgaZEdUW59YOcdu9d5XyZBAwbatWqmN0l4tnjsrbU4wQl7PVx00I/4jBmC037VQ6RWDk/ISDdDKoUHpbOTkN8a3iflD6Gy7vPYIQhQxnuf5qUiALLPdgCLZzZTB2kBbfE40nh83a2grYPbA53gEezebBBIbSrsvH+47yYYgP60x8tHQP15fd5d9maUmXmbJfJLXlMCslqtasSuiJQmSjeYA215wqbVW1PX30Qu7sF8PXNIbpPPVmtHmlmfHgkKEir91Kjyc1t2wB7bG1kWKj6xLVMIkBEhIOFBPMTFBYSMCDb63qhJCkoLaIvoYMc0aWoPFBClOkICtAdCm34341acCPqKvf501x3FY8apZnTlElXQmAUYzaNqnrfUaFPGZ0nAVOVYx8vRnD8BkUQe2x27K5hoMQohv8ClM6RhbZ0t65TgwvGraUhq2uEwbmWjEMQkYmpsu9ODhwQPOCfmA6HQ73aSU3ZrsvhcmM+GUNMBtxoC8oD36zXoBzy790C+pE9rBU578m9pCWEPiCdOPX74559vd3hiKWIUkYfihAm42Zz/7ButbENVzaMCL+hQM9HF6nS2SPBsSSIfrbrBZ7VUndlQDZjk5wAEn2iIgMSnRxnOTTrnuDLRMgESmPmEoz1OcDm5Aw3qeB+LfnLHhUJGTMATAIHnFDwXF7e3F+FimyBCNeIMFbEEAfG1glK97jSeQGcHonQf9WA/sZ6LU5OODPfp4dvHzx2M1oIdJ6+QXSlvt9eo46pzJ7nKtJ0/bp5njgToXXE6r99g0hKc9bV6dsS6FXRNlpN4QJXE2h1p28OBqyX5rd5o+MjKMHIZruQOATlMYAzILGBUxWHjpekuIJ+o55pQLotTOw/TPFwEiM0p4oFMiCChAIgHlU5P8XIpBYEmpaCUyU7R2SoowNknHhfkNjMJcgcvD3/0p0pG7Pp1Dre9uLnpVDgR2qieZybTSS5bOyNt0OPMl2ZzXBu7UAIslHUYQ1DlTfKRY4Ab2U58dEAYg71cxCipwhyXT5fyxifuLoAhbHIkG5sBFqOJqtp5/aIx1RjRTOtPhjJOYYOHm5af09qWDxEyHCFCpDqbOBVedyoobbK3EoLRDkR7Wn/SiAyIPSomjimEm3q50lv0sdbVBiWY5Go0LGPeWu9OM8XSu+Pzs9vLmho0xcYwuUIxEo0mSj2+6BKp8GAMkgQ05sPAlndAxDyqVHoCNGNN0dkQjDQccOsy1ZuBEbcdEJYH4svYezXFmQs/kRfAj0EmY0g+3EV6I14BOnOYEpfmZgk62qGgtIlLDYuk8exVP+kU5yLC49f3H1/u4kZRw9f9eqPEAHv96iqeFkk2x6SWQ72Q2uq2qSGOci++8lzivSj59KP4dOETL2zqHJ4S7jAupQrPEv3wut0DArrtWrkIhnWpekK/FFCbhikhcIyF4yiiCuotyJF5DQKQzAG85A4CIF0CHxxdSHQ23I7THQqCyY5pkXBLHRn2V5DOvvVDrHMc0/IU4L4MGH5rD/v9ebtYYqx0eJ3UmbQ6yRRWZ6eF2tH5+enq7DzfYt40HtxzCpVhKzW3tetBR12VEvrDIYsNf3n+eoSDmxOLzpL6ToUVRvpRPvM57HYxl0VArlWXNHxIsdStuCx0sIk4ewaPAVvOw4P8emQ0XISnZhY/AeJnrg7kAxjCdYUwiLBLQeklKBXp48L7stc/b1YPlOJocn9JesVWFI/mswkna9r1fjJuszNkC/h9dXtxe5Q9rl0WTnJlTm1177+8MFJ6ZzIsN7g31bNpadcpzmm2F7cRBuRNiE6351F1skd+HVac2QOHdzrqcpuJuzr0D/Qx7k58xj35a9rfWOYjWewtHvu0S8isVisbBKPRbf07CJmPYqug9DhcOsOl9GM46TOPewZEbAD9BaGHm0Vj2r/qzmccHshXhkmnV81na7er2+/fv68uzk+OL08PW8Ue3YXrv184aH99k+DkRihFFQMCHIsFcBa6er6XmsGYGHKPiHqPAkKDmEVTRuBh425P6GELjqqbJuQE1xtDlIPlGbsx19bc9nHVzdsdAVopJCAHhFh8FPzefIRpq6BfD5T6svu8/X71g3VLlXsM6PH5010yjO/+uF5G7dK7FgdNlr12PnOx+r76TlyeZzMX58xJdxKa2jppf097QWUCUCRUSAkK1R0KbqiGK7zA4tkDdKBBRR4wB/IfWOdpVc0EXJwoUZggcbnszPexm0ESEH+7Tr0cF3jd4UCx8QQ+p2xF4cO/H4S2gPQrt+42gHg8SAvoFZy0idt+pRS0/doLIP7BP61BcnHYhOcPL/fdl2+f7pM5i3I7GnKUgFlF0SFWZ4VC85JB9kiHTj7rNPfVCBeATIaxx6lw0lpssAMoZtHHHyxkqHsSDvrBuEPF872ahY8aRdetDwnwnB8pYBAAwa2oHRAtoyL+5Ag1rOGwL/1viSECIPAI0LYeBxrCV/09ApSuwxE7BBTYpHqo+9ePjdtvr38gwTHwq1Xo/cdP3/788/2X606FemUjwh+3jy9uN4BqF6vVca46omWq/+vLfRfpYKLpIVAI1amUuU5Y6a5aXqIjK2IiRux1I+1AqRuozdOoUBmgtSMz7XKnHLS2lUpiEGKcjRVpopP0VeU36BWAF7RjN3DWRE/+QCsQfAivPQ7bN4Uh+aeDtICg84uJbN+3bHzfOyB5y397O/e+xKooDHfjooLcITxyGaQGELmopDWaEVNWfv/v0/O++xyxGKg/Ou0aVKrpF09rr3ddD7/5NgyCzW+09CDkHj78/PGPDy+9foPbp7sY0zGfO8WCfL7V3Z4tdPvf9DzU0JtJnclophrEgpDrdigE3iIAkkNSP3UkSN4jQrSpKmlNJYYQ7KhWDR/sw9k0AwKRl1ZU44y2zEsHRElUimRBVuo93kkAbVV2UnAQnACoBaAdD2QftKuxd7e/vIGjk2p4is0EaRB3EVtjq1GEQzvpz398xIIA1CkUyV5eN0rlU9OxSjjFDXSWqx5HfaUYDgoNLsrdXHv+DUBIg7AcZsKNh3Rr1BsAcv1HCRwFO2gEf1dSsQfng4C2+DKgiv7EhgBEr6JbTCXbYmkgPNCwAYkZGiFouMSCIJ3wwSqDHennT1sQfA5MPe4ietsjkhIgNFw8exE6esznDx9y2d9/JCe3vhg2SsfVKKq358fZluD4fJ2lClRvT27CYrHhVHWDFUgmKGuVEzyXNcOQNJqP5OYw2FprkKtxBTVurm42+Or5U3fk5LIONs2H/wP8K5vT3GMNhqrPKSlqZW3BhuQ/U0SU5EmFx+eNugY0R3B8OerLHgva01K63wNxUo1OZTShCORSqvTBR9DQpkiv7y/ccL88PG0ulwT2pSolmUxWUYZ88Bkhaq1QnI+HNwzSq04UdYlSJmEmy30JeuFmw358w6Eg+u1urbnARXk0zvvFmDfVV48Hq/GjACAf6TScRcvf5KUSgvqOmz7KJ4les/2g+coQE7LEfmQ5ifEYkLt8XPRG/e2quN2x1G38Y0R79EHKhIwoEPLmMfgASI1WAsQ1R0fPO6rWg2Z3PGh0yme5TKZ8dk6fc6cbdardaY+meeyjHc3JqLFVCamN4eBx0BSmFI/YK80zZWZEQyY0ARH2ODXKmJFypOg0Pn+NMJZ1BSWXkQ7fVvKZcHS/2YKykmuGAQg+dfKkKDgl4mLrsba28VgL6sSgeHXJe8eEDvTD7SJ664LSze7Y9ch0ZD3q8oWPAdGo+MvPP9Ot+KSGtsmUnXDTWj4fDZaFsop0zWWjM19eu9kAJV6r08E7ncDBa8tW6tS5tdzuD2VSSpMOwONOxcECBzTX8NW8WQdQUaW4Ytzq3opjSbsPUtBk3NzSSz4uaa2qJM4GTwg/1Q+CoosJmY8o23TgI5uDjY8vun0WdOCC27MWIe0EaYhVbTzGQ47nZwDB5+EBRALEQr7NO/Ue9rvF7uz9qqg1ZIjjLkXUvse2h9OI1Yf4HSo/AJp5e1y7TUqHILXvNh/JbGa0RnZA7D7yMkCvmtBNJ/txZSenJtCy/Il03DlRzulJmQydAPHiujY0QkbnVV9Lv71NxCksSpp5cuGENm0D0vnUergDQdAn8zzp289WysEHPOYjQEwNPwDoQZSeHx+1sfRu87K+bC9mm4t+gV1Xta5EWP36Uv06sz4lA/q0aVpQ/zwdWsMBt5lXuqC30dz2SKulUgikSaW21RbiFVT2QLrfXLz2Jjji1HMJZgNqZQlUg4M6cvdoXmLNIPCEMp8km7AFpBjq3J4p9Cxs7YcvlnO7Km7/SIO+HOiWT5/Q55+Bx3DCgY8AGc/j4zOgaF1kL+y6N7u821xOa3y+1bm6bMZUuREJs9sRnn/GIJAAqZatlM7I7VREqm3uOm8sc22bt3FBy4Vn7SOuOQ/6asLEHYfyMeVzZ6ZDEENZHUKcpPvN9mMOCD19/m8BhffhIxiG42IRgOyAzIdjFbc7VnewFrRrQdBJE8/b0Ac69juyHt9ydkEfHp7ho/nU+xcAvVDqueuNltHtuFtyzpltvxdqtu6rhK0ZB4m40eoWy6HkczsFyUpy4Rab4s1rI6NgQKbUM8CRJZzVmT5JX2BBj1kiixHCC8uyiHCOzt5GJIQBI+HAR0T9phsR1BtS1klasXXDYZsGpCMVtxsFidO+WtCnWuHSjH+2GtvKDTgwgQdTjhAKGu7DL0g4+DyIz5r1yi/afjBb4HgGdcaq6eOhcV51U4vp98wIzbRcdjzV/mwlsVU5tU7gTKaoN/GhJDrQ16VbfV1FLQDGfGwhZQjIiRgTEsG5OMp1HpkjAEJ6Jxm2E8U/Tgy9BeTSj3wPhEzHfHwMSKph14IgcqgU9MmeuPTrp1/9KuMRG0+XPJLd0bHG5o8H+Gjw/mXz093L0/3dxfvZdbO+Gi6q+tSq7fbs4kptIQyf6H6b3U61u21AImGl7ZYSB6oC4X76XtlMHs6rXlQWDbuTIVTiY1froedMYz4WYWc2BUbqSkRLUZMsD2V2e5BWkh5w3ClA1nA+SQrilY14+GYTmkQ2fPav9iuaznYD2e7ivjQBOff21W+62gRH59FbyO11kNgaHH7SwYI2NyzwXWvzL5/51aR+XCkUaov+7IJKN6W5iYp09JQuVOVp0lXfH7KG93oydG1IhaDpyo1BY6/0VZha1wh3J+iDjHtzpKLtT4IB8SJPUlaWhwux20FHyFzMRAGOO+BEy/bzquJsdiIRxhnKsbS2qjMcuAHo7w1xu/ZzuOU3fXltQN99tOvh6PVBONb3j8+62fjZl9uLtpJv1lTk2HVAB9Y3Vz9Na9w4xag/vLijjsogyXCm4HTV1ia5hSZOtfxAaYWZW+iVKaWI6jmtKS+Ldt2zJKX5XH2ptORYI+BgnFBTCoA45+tWhZP37mV6vDScJVuQaosLo0A89QGKOw8CH0lvQlfR0N8PTR9bjxFtLWjv5FZC51MeaFtETV9hA4i5xkdwWBgA5B5vAyFsKNxua+2O5xeotNr/qkdDIgtkSf0MZu+1G643m0hFU2sYLaIxd9cABaBi0GjojVa0xMmGeGMaBNyAbh6NaGfUhwigjJveXaHzHCp8AMRHfSLNXahRLSKFV3BpO8ScFa4wIEFRPPhDX3nR8e0ox1U2EF4BJJw2Ho75yAf9Y63OL/tb5lOqn/6lPAcgbbJaPz3/8r3uOl1pgiFAD9YH3hzPGxqw41kMXHWbn3oIgfpxc/XO3b3qBkEQrL6ZjZuRutwk0fT8jREXH0eDXPCB0O1EnQiaLSmRN/BYlu63QiwO7O9DYU0fvTx9JV+YR9T15lpXytXmfczCiBk52WMiAU4CyOIBkgmgbLxnW6ND/hfZL322O9OwryFul43xpEXoy7fa4zt1KL6w/3X9ZK8DlWeIyIakGmxC8OGnZwCt1ReifXC94bRZn15eaGFf2GV5PXl/0ac5y23WxDjqPdD4D9ZDczaB0chJudUgikgdHFexGy2GlchWfBMsKKNeRDkMxahK82S537TvuqobMCu3I1vjk8eI4GNAJhTDcQ3VfDiyGH212PYAcdIfLMKHLWiHzg6llPAE6ebyj1hJwrF7h41HrNy7f5bbsd9ZE/UASCrBgFAOencNnyvv5mN53Gj4TvvgvJDvmkTdJQZUX2rtdZeSHPsPBt5ehsLDA3EwNCXjmlWtqbAu4IKreaTEJR5HnFn35p4mbQR5cqlRd146kmnJtZRJyGVUBM+dnIasqI8B2ZoMyDeaM3TyWLAJo/nOl4uQ3JMt6FBT9nbw8VMTdSkqOPeFqIpKggcJx622vrqgUffm5UHKOgykbu4fYwvC9fjw3h0uiO4DnYv3Q02dwsfNvNMFCYNRt7tsX2uZ5Xgaeg6mGBCAxAc6xEM87qGmGnddN1umA5+qXJG0loQ2Ic65+ICH9hyuqWONPlYLOctoqqblfMG7s11s2JblxCUBFQS6RYTYmE6Y0g/bZGM+5X8UcXsMCDThpNogLwWjUeGP2tQnBHoCxpPdEFpBM6kAsvKWatD88IZI6EYryu8QdPf3/EAfKZhmlmjtLg1uUQ0+0/FC++BXftbSaOaRrYknUeEz0ph9pJbejtbpSGfrirN4U5kHgRDq2KdwOkUiZPyRlltBQJ9XCI4LRxYJ/BwXfqApLMmkiQBxxCeYDlYTdp3HmQjzqQQL2p49lSC/7CDyHykSgowLdEEhvAR/gwv6QztFHBYRD0kloLYfpbFZ18u9Bia+4evmnjfRdDc9Df9AgKXMkTKng2s2JNPcTnZnQbq60YceF5wEAgqB518s6FLkIqx3tGO8M/fGnZKH4o4UAumcYy2WcbrjgFZW8xAw6AKv5LGC43KW6+5rsYGQATlGtfd5PS3xUY3COXAbjwHJ/IKO2xms21sJMp2dk+oIQ1Ki+0EWJD4/k39jkRVbeZRX4E0jQxeYxFrb41Hb1tn8gXlJ0t1wewkQc/l1rdurLkd0WA3QcYSlU8TaACjU6SYWcOR93Eu6bEZgkeUwce+pEg/GWZpJc7kWBB7rMnE6Rayprg0fbZZHRyhGEhofeyve8TGcuISk9J1PAgdjDIPGdkk7FnRot8iu9aRJyIzcXeUtVir7AIhjQL85chUf00G5bbyLT1qbH/zMJtINmFVPs3Ra/zKKSqro1MhZr7Q6fqR4VM2iZN9uEXC8TdwKn7FXIeD1vZSXekMXQGFPCIBCkBKWt8TZUFd9hIceIvLWurAAFL8Lo2T4B4wcI/IAg/DY6xhM0YyCPtAxoL9Z0MFE9m5T3P9TRLW9OC+K6xGg37QX4Q9Xg7Af6NhowqDPy3rNTxDaSNThl3oWcDM8zLhZ1NxVDdfj1tE2Q0XjOr1uehaHctlkSvtt+Iw1Rie7caO1vJQntzmS0Spr2w9hNyAQH7cdhBailj90kgmKfwIgW084wpQAyobsqvBwzIYj6+EuNSDg2YL2uCDAHJBwqXmg3TRcMCEdv2pt0m9ccNqqKD6YzhpA3tWLeNPdxtmsAfSkBE9/4p3xoyVPeiLMz5TU2K6HzzXp0G02ATGm5jCC0ogwaDCFjy6+hWbjjjsA9PMZozqCIeM500KH6LUSFFuoubV0uxmQFNwxmuHIqdTXdoRzd4k6NtXbAuSVf7AJKzGBw6+wWkmAjjhSdW8t6EAme4/9pI8nMaKwTFFpHt1y2soj81GpAa39qESPd11fbe437HC5Qo2L2hPv9nS/DWe3RDb4+SxNPsyMRNdTqWzNgDNrtZDcbnDlTa4XjQVFBi2xaqi6UKpHfItLGo/nhXzGm2LDptEjhT8tNS0qmYOvMZ4fT7PYD+dIM47g4c6L8eBuqODFBT5Uellz+l48z0sJq+EISrIoDjxOyv7Ngg5N3+/6oPSvuG2tDityoU54uN44IbONfuPlKc4erJ/YNqL+0TurOQIi6tp69uKYkigGgDLOHh3VliNVSfU4xnpdKw9Iyy1pQ4DPsk0qbjEmTtXqlrDoQJsWu1VEQv44rIDV6jELBMqoEgznif2cYT9hTpgMApIu0QYc99DLjqwN4Oi12XpOkGdYfMI2RvXkxwYkQIct6O1+nv/dAwXfJzHnnjgRApD42CGpzP0ogR1EtowIQGtxwoDg5YW+73noVYPbrSrxmkNwIV1rywF8NIIwZiKLzVWMngJoKtOhjooEh4+mhP1srKVHgpB/qpYKUEeCzsXR84r7qE5jQCi4nH2H2xotD+Aj84Gk60EhNBWfsE0OQJ0SR55IhASpKP46tqnPDqzB3JrPnuVWqRLC+8i6BQk+HPI9gIGQCnfO7kDlkRfwWGhvsBte8EE6SDrt7Jm1G1TavIiFm6OVPcIy8PvdaDH1kGKXWy7iRiMsGkS8DoCFakPsJQ9eWqjg3bGX0H1UDW1XLsApsfmmM/HM0/527jkZkJvhQOILTuUJjkt0CPEwoQciqUPx0VcRSTb5+VjFbc+O/Ryaq0vfgD6HkBtJwwOccEOsXUZsc3S3SVzLgkLSVJecbjYFqAakfXzqVJw2ase4Bj42DKJUbyzDcxZUEVKPvJZQjBcc7jutOpDAnvPR1RuR5oW97K2mj7JUcsKHeMiD2voN1aEgPrEWwB+hzNwblz85k/0AyBeaj4frvNDKnQteKCursduRFRmQ8EpmJ4AOdiOAZne71bYSlD6hZPclhBjVshN6IF6FCgcu8FGWFMEWolPOXRgSRnPTB0dswzKLUuXbE2QRHYwDqAwizfp0ZTAR4ShQeHugzgOx42c/Q24OGFe63a/oZ2DMQ0+82fhzJPbPe5fYaw+i7y41JLS44L5WkBRK3sF8zkNXfNY6D9RcoprPMx77oYSQUwgxo7fX2yEX9H8akH/vsOHXfHz8fC3VvJ8tDDgvG8WknLWzB/Ejar2Zoqf9vSy8WjBTGnXyp/TPZwpkeBT/4F8Y8o14dlpR09m4HbWGUCLCUyHwmg095gxtBx89K1jtVo5Xa+YjPI7xvbnqqBJkWcgVkCWVb8nQjeXw1Qk3Y3E3gl79vfV1R66mIOsRH/0JnnhJWainJoAOJ+J2G37Tb+T5ItQZ/L/Ha9deeJj6LzofAqF73WU4HANyXlQrXZgw8Y5yUtjDdgbdlSm3zs6p19THZAr8kKUOyo2mhFqez4YwZ6ANIvVaV3ESXqehukGtCaZkuN6PyYKVindeau2tpF6W6Fr2dvinwvsZ9WNvFx+0UOOm5No3iAwIKBZtSRqBV5RbsmxRN6UTFga0q+EOD6am74Ekq7/cLuQhX6rjxS8OVkEEo+cnPeyH0PRFeCziuNW8fAdQ7sOuE0eqJeokXyXMuV1pqAQd1sRWBoNuAXPIEKWKTzPToUJXrOKdNE/HQqzIW8s7SZDf4XsSPfRjCxDZHK2Xb9lGXODBCdmA0G8YlY/Y2e2Iknvc/BJ3zxXNB1A6RdFy+jpes1SRBelsAR3Yz7O73/evpe7/HJX29elfmSy8/M504IRQcOuIreheTTy9d+oHISYlVqWuoAkTP3ymx76r20gFGvd0RqvZ7HYcOWavaQfjFOHd5N7q4Iym43mx0EBJy/PU65qxr8aPLbOnwGKKUCplKsyYqEjqkKaiUNT1a1tPWG+gEkMrToWeobF5CVN17ug1Gyff/Is/QW86vE16h2Js0lAKoFDF2/FBe0aH0zWgLWR9cWQqRCRKOTTL+wEApLVjQsGEqDQ83uNubngS91NIJkhZ+1EzVzTGUf7h2RrHOf23jxmaWxILaS1IsyE5HSkAwsMs8D8aQV1ypTWX9HLziZGBsdtWtSFTzir4VIW0jB/R/pxsiDcT1yNCwiMl7ZgnHLX76GgJDUfXm3yPB/d1+AKauLYgq3THb7zfHDQw1TnogxJCu2m4FHoVkwUl/pYktgtB4FEmzvMMmNJX3wlQ6Pd9Vlvc052e9AygRwG6As6V1DVdigCarJg2KR6p0EKgw80FHD0vDQ2ntZYMNHK0s9nzp8opaDcIn6FllYwGe8u3+IDjPoTAx1k4J9p8XODhIw1KWnh4wy9xWzaAg1+pHBFJCVBoPjlO0gZBtVVCe0I8HcQ34Xfc35VtNG84xXhS80DQEfskAacUqTzPxx+YpeO4zvArXkjlbrVeqV3+wwcIvVchfK2CKlRmP7GlYoYd+fHO71bRUa7Ec7BIDMy9k2rOJacOEO8RnTfceu3HM2NQCwa/LKv8aCxr3wxxThYiWdLXrTKfoPFwKM55OMFeqHwcAqKQs3YJIuHTsjXgVBSJOQsR8CSpHF51wwlOvmy7sTzwJIQtdb8F7cljp9iJIEBGn6RIWecLHqeug36DjiyHtl95IfeWPm30JJkrh6gXl5Pry6urb4YzzntRuq5nszx665qAZtl12obEDrk0hFmpgMtZTNuMoEqwMcLNJN5xPqP0S9GPcdb9Bg0MR2DoM5AX9wdn8wlZUODlNC3kEVNw+C9yEvOJn7bBP10Kz68RFY8Xc8r5uH/HgHg7REBxududwvu7so3m9Zu/n3RWVXCssIOpfkYS+3ecUHjqzGtu9B5C+g5AL/eEQ1d0HPbQ2Nq+s9JDHt/NNC7nXtHVGN2sZxFXgYOg7o/IunHX1bVPDKvhr0VNpF2He4+noNNdPcczdZQQw4mTsHFV+ySoKieckxlGffoKOx2F2vPwY+hkfDu8UM4ZhR8mVAN5R5lqYfAlVsnHNdok7sknjIxVwdMhFbe923ZFXGrbLl//7bIhraog/DGgB9e2w4CJ4iC7nTXy+oZ2awFCu/Eo6NtLBPZqRH/VkDIQ49rk1pqlIoB45DkNb5R7Ij9JQX3uXvlK6wF/2f9vlwu1RkN50WIYRM2eYhLWVDq+/bjr5Gkk0RyCOkvwKqpbYeLev+Khx7ISqIpMY0CZ+BFDcjcBEHwMJTkg5VcM6LAFbSGlHwMZUJI+cJbUUaoN6CPpUWUQ3AUSEjtEp7wo9XZD7KMEAs083wwZJGHKlIb4hR54r0bEazYrZfQ0OW+/1nNmvJscodClObshX6TbJ+N11rS/R6xI1ExRUeNzpxWjUZeoCwRKxZUNKGzcgVC8OZ63dMLV5tAoGaqrJKlrTq0qzaZ3ffd5CZbZCKK/A5kNjJP9Fxa0uxzhizQJGVIyzk0QpFffcOr25TzqioONO+TX5N7UhM1DH7nWaMXWs4E060PjYZvJU1BpWmG8kJVEg/6l9omMGEjl71loSSwZUmXgKPXUOnbLuQ5tInqEPTmFjGVVJd5znTspZzrJBKTTnlZyp8lDF0xnOxh89nq9aZJVZb6iHwjt5iD4ZCXYXfKWTIj3ANuwYgdUSUYdDlrQvlpDingSue3r7TcEAspaey85GtKSFTngUS3uho5rjkTBZQ9GpN+QBeqOF4bRKExn3bZHlOtqbGQElVLWYy+BayDfyPtEvKPpuSJSuHJ+QmRSzhbriwF7aD0jrNSNkJyeE6vypxKhYZbEPVRJruCEH17Xi8HJfCphZOsEyAbkhzp4Q0zFdyOywC2kysF5ts4nxzGfGNFfLejfrvBLf5wbhQAgYlSXGtQiIgFHwoAy3cvGq0b9tDknSIcTOg/JwhGkMigsQ2FAAR5qjl8xdjqKMplSFw54GFLYgJsuvEQkVFUbqLecRrK9ZqLEEEmjmkFTI4+9wfe40jrHAWXiwSB3i4RINO6A148uOQhR0tarlIMHILLyQBkA+QkP4YKzWzsqJGGrhUKyKMHf+peU3KGu0t0dV+kb0FYsOA1HLESoqjype3iYCcKMnl50s5E38Ope/A+iQH1VWp7IXBAKmwcDuOugXm+ohWdczR05Zi8hGGqdqM1MEGaktl/9WkYl3H/sbEqkFhqKf3KKL1WXLuWztI5SBVJ1TuH+diG5QCDBLbuTBlKneSTuxEf8XD4tyIJ8QcpIkt1/pQAIo7K5mIntBzQcwfoTdOCas/81FIkAAAAASUVORK5CYII=\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal temperature card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "pm2.5", + "particulate", + "matter", + "air", + "fine particulates", + "fine particles", + "particulate matter 2.5", + "airborne particles", + "microscopic particles", + "respirable particles", + "aerosol particles", + "dust particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_power_consumption_card.json b/application/src/main/data/json/system/widget_types/horizontal_power_consumption_card.json new file mode 100644 index 0000000000..42f2b1b0f4 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_power_consumption_card.json @@ -0,0 +1,32 @@ +{ + "fqn": "horizontal_power_consumption_card", + "name": "Horizontal power consumption card", + "deprecated": false, + "image": "tb-image:aG9yaXpvbnRhbF9wb3dlcl9jb25zdW1wdGlvbl9jYXJkLnN2Zw==:aG9yaXpvbnRhbF9wb3dlcl9jb25zdW1wdGlvbl9jYXJkLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjU2IiB5PSI1MiIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjU1IiB4PSIuNSIgeT0iNTIuNSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjEiIHJ4PSIzLjUiLz48ZyBmaWx0ZXI9InVybCgjYSkiPjxyZWN0IHdpZHRoPSIxODQiIGhlaWdodD0iNDgiIHg9IjgiIHk9IjU2IiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii44IiByeD0iNCIvPjxwYXRoIGZpbGw9IiMzRkE3MUEiIGQ9Ik0yMyA4OWgtMWwxLTdoLTMuNWMtLjYgMC0uNi0uMy0uNC0uNy4yLS4zIDAgMCAwIDBMMjUgNzFoMWwtMSA3aDMuNWMuNSAwIC42LjMuNS41bC0uMS4yTDIzIDg5WiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik00My44IDczLjNoLTIuNnYtMS40aDIuNmMuNCAwIC44IDAgMS0uMi40LS4xLjYtLjMuNy0uNmwuMi0uOWMwLS4zIDAtLjYtLjItLjktLjEtLjItLjMtLjUtLjYtLjZhMiAyIDAgMCAwLTEuMS0uM2gtMlY3N0g0MFY2N2gzLjhjLjggMCAxLjQuMiAyIC41LjUuMiAxIC42IDEuMiAxIC4zLjYuNCAxIC40IDEuNyAwIC42LS4xIDEuMi0uNCAxLjctLjMuNC0uNy44LTEuMiAxLS42LjMtMS4yLjQtMiAuNFptNC44IDBjMC0uNi4xLTEuMS4zLTEuNmwuNi0xLjJhMyAzIDAgMCAxIDEuMS0uN2MuNC0uMiAxLS4zIDEuNS0uM3MxIDAgMS40LjNjLjQuMS44LjQgMS4xLjdsLjcgMS4yLjIgMS41di4yYzAgLjUgMCAxLS4yIDEuNWwtLjcgMS4yYTMuMSAzLjEgMCAwIDEtMi41IDFjLS42IDAtMSAwLTEuNS0uMmEzLjYgMy42IDAgMCAxLTEuOC0ybC0uMi0xLjVabTEuNyAwdjFsLjQuOGMuMS4yLjMuNC42LjUuMi4yLjUuMi44LjJhMS42IDEuNiAwIDAgMCAxLjMtLjdjLjItLjIuMy0uNS4zLS44bC4yLTFjMC0uNCAwLS43LS4yLTEgMC0uMy0uMS0uNi0uMy0uOGExLjYgMS42IDAgMCAwLTEuMy0uN2MtLjMgMC0uNiAwLS44LjItLjMuMS0uNS4zLS42LjUtLjEuMi0uMy41LS4zLjhsLS4xIDFabTguOCAyIDEuOC01LjdoMWwtLjMgMS43TDYwIDc3aC0xbC4yLTEuNlptLTEtNS43IDEuNCA1LjhWNzdoLTFsLTItNy40aDEuNlptNS40IDUuNyAxLjMtNS43aDEuNmwtMiA3LjRoLTFsLjEtMS43Wm0tMS40LTUuNyAxLjcgNS43LjIgMS43aC0xbC0xLjctNS43LS4zLTEuN2gxWm05IDcuNWMtLjYgMC0xLjEgMC0xLjUtLjJhMy4zIDMuMyAwIDAgMS0xLjktMiA0IDQgMCAwIDEtLjItMS4zdi0uM2MwLS42IDAtMS4xLjItMS42cy40LS45LjctMS4yYTMgMyAwIDAgMSAxLjEtLjhsMS4zLS4yYy42IDAgMSAwIDEuNC4yYTMuMSAzLjEgMCAwIDEgMS42IDEuOWwuMiAxLjV2LjdoLTUuN3YtMS4yaDR2LS4xbC0uMS0uOS0uNS0uNi0uOS0uMmMtLjIgMC0uNSAwLS43LjItLjIgMC0uNC4yLS41LjVsLS40Ljd2Mi4zbC41LjcuNi41LjguMUEyLjMgMi4zIDAgMCAwIDczIDc1bC45LjktLjcuNmMtLjIuMi0uNS40LS45LjUtLjQuMi0uOC4yLTEuMy4yWm02LTZWNzdoLTEuNnYtNy40aDEuNlY3MVptMi40LTEuNVY3MWEzLjIgMy4yIDAgMCAwLTEuNSAwIDEuNCAxLjQgMCAwIDAtLjggMWwtLjIuNmgtLjRjMC0uNCAwLS44LjItMS4yIDAtLjQuMi0uOC40LTEgLjItLjQuNC0uNi43LS44YTEuOSAxLjkgMCAwIDEgMS4zLS4yaC4zWk00Mi45IDkxLjhsLjctLjEuNS0uNWMuMi0uMi4yLS40LjItLjZINDZjMCAuNC0uMi45LS40IDEuM2EzIDMgMCAwIDEtMS4xLjlsLTEuNS4zYy0uNiAwLTEgMC0xLjUtLjNhMyAzIDAgMCAxLTEtLjhsLS43LTEuMS0uMi0xLjV2LS4ybC4yLTEuNS43LTEuMWMuMi0uNC42LS42IDEtLjguNC0uMiAxLS4zIDEuNS0uMy42IDAgMSAwIDEuNS4zcy44LjYgMSAxYy40LjQuNS45LjUgMS40aC0xLjZjMC0uMiAwLS41LS4yLS43IDAtLjItLjItLjQtLjUtLjUtLjItLjItLjQtLjItLjctLjItLjQgMC0uNiAwLS45LjJsLS41LjUtLjIuOC0uMS45djEuMmwuMy43LjUuNWMuMy4yLjUuMi45LjJabTQuMi0yLjR2LS4yYzAtLjUgMC0xIC4yLTEuNWwuNy0xLjJhMyAzIDAgMCAxIDEtLjdsMS41LS4zYy42IDAgMSAwIDEuNS4zLjQuMS44LjQgMSAuNy40LjQuNi44LjcgMS4ybC4zIDEuNXYuMmwtLjMgMS41LS42IDEuMmEzLjEgMy4xIDAgMCAxLTIuNiAxYy0uNSAwLTEgMC0xLjQtLjJhMy41IDMuNSAwIDAgMS0xLjgtMmwtLjItMS41Wm0xLjYtLjJ2LjJsLjEgMSAuNC43LjUuNWMuMy4yLjUuMi44LjJhMS42IDEuNiAwIDAgMCAxLjQtLjdsLjMtLjguMS0xdi0xbC0uNC0uOGExLjYgMS42IDAgMCAwLTEuNC0uN2MtLjMgMC0uNSAwLS44LjJsLS41LjUtLjQuOHYxWm04LjUtMlY5M2gtMS42di03LjRINTdsLjEgMS42Wk01NyA4OWgtLjVjMC0uNSAwLTEgLjItMS40bC42LTEuMWEyLjcgMi43IDAgMCAxIDIuMi0xbDEgLjEuNy41LjUuOC4xIDEuM1Y5M2gtMS42di00LjhjMC0uMyAwLS42LS4yLS44YS45LjkgMCAwIDAtLjQtLjVsLS44LS4xYTEuNiAxLjYgMCAwIDAtMS4zLjdsLS4zLjctLjIuOFptMTAuOSAyLS4xLS40YTEgMSAwIDAgMC0uNS0uNGwtMS0uMy0xLS4zLTEtLjUtLjUtLjYtLjItLjguMi0uOS42LS43IDEtLjUgMS4xLS4xYy42IDAgMS4xIDAgMS42LjMuNC4yLjguNSAxIC44LjMuNC40LjguNCAxLjJoLTEuN2wtLjEtLjVhMSAxIDAgMCAwLS41LS41bC0uNy0uMS0uNy4xYTEgMSAwIDAgMC0uNC40LjkuOSAwIDAgMCAwIC44bC4yLjIuNC4yLjguMiAxLjQuNWMuNC4yLjguNCAxIC43LjIuMy4zLjcuMyAxLjFhMiAyIDAgMCAxLS44IDEuNiAzIDMgMCAwIDEtMSAuNWwtMS4yLjFjLS43IDAtMS4yIDAtMS43LS4zcy0uOC0uNi0xLTFjLS4zLS4zLS40LS43LS40LTEuMWgxLjZsLjIuNy42LjQuNy4xLjgtLjFjLjIgMCAuMy0uMi40LS4zbC4yLS41Wm03LjguM3YtNS43aDEuNlY5M2gtMS42di0xLjdabS4yLTEuNmguNWwtLjEgMS40LS41IDFjLS4zLjQtLjUuNi0xIC44YTMgMyAwIDAgMS0xLjIuMiAzIDMgMCAwIDEtMS0uMSAyIDIgMCAwIDEtLjgtLjVsLS41LS45YTQgNCAwIDAgMS0uMS0xLjJ2LTQuOGgxLjZ2NC44bC4xLjdjMCAuMi4yLjMuMy40bC40LjIuNC4xYy41IDAgLjkgMCAxLjEtLjMuMy0uMi41LS40LjYtLjdsLjItMVptNS0yLjZWOTNINzl2LTcuNGgxLjZ2MS41Wm0tLjMgMkg4MGMwLS42IDAtMSAuMi0xLjUgMC0uNC4zLS44LjUtMS4xbDEtLjhhMyAzIDAgMCAxIDEuMi0uMmwxIC4xLjcuNS40LjhjLjIuMy4yLjcuMiAxLjJWOTNoLTEuNnYtNC44YzAtLjMgMC0uNi0uMi0uOGExIDEgMCAwIDAtLjUtLjVsLS43LS4xYy0uMyAwLS41IDAtLjcuMi0uMyAwLS40LjMtLjYuNWwtLjMuN3YuOFptNC42LS41LS44LjIuMi0xLjNjLjItLjQuMy0uOC42LTFhMi42IDIuNiAwIDAgMSAyLjEtMWwxIC4xLjguNWMuMi4yLjQuNS41LjlsLjEgMS4yVjkzSDg4di00LjhjMC0uNCAwLS43LS4yLS45YS45LjkgMCAwIDAtLjQtLjRsLS43LS4xLS43LjEtLjUuNC0uMy42di43Wm04LjEtMS42djguOGgtMS42Vjg1LjZoMS41bC4xIDEuNFptNC45IDIuMnYuMmwtLjIgMS41LS42IDEuMmMtLjIuMy0uNS42LS45LjhsLTEuMi4yYy0uNSAwLS45IDAtMS4yLS4ybC0uOS0uN2E0IDQgMCAwIDEtLjYtMS4ybC0uMy0xLjR2LS41bC4zLTEuNS42LTEuMWEyLjUgMi41IDAgMCAxIDItMWMuNSAwIDEgMCAxLjMuMmwxIC44LjUgMS4yLjIgMS41Wm0tMS43LjJ2LTEuMmwtLjQtLjdhMS41IDEuNSAwIDAgMC0xLjMtLjdsLS43LjEtLjUuNGEyIDIgMCAwIDAtLjQuN2wtLjEuN1Y5MGwuMiAxIC42LjYgMSAuMmMuMiAwIC41IDAgLjctLjIuMiAwIC40LS4zLjUtLjUuMi0uMi4zLS41LjMtLjhsLjEtMVptNi43LTMuOHYxLjJIOTl2LTEuMmg0LjJabS0zLTEuOGgxLjd2Ny43bC40LjJoLjRhMi41IDIuNSAwIDAgMCAuNiAwVjkzYTQuMiA0LjIgMCAwIDEtMS4xLjFjLS40IDAtLjcgMC0xLS4yLS4zLS4xLS41LS4zLS43LS42LS4yLS4zLS4zLS43LS4zLTEuMnYtNy4zWm02LjMgMS44VjkzaC0xLjd2LTcuNGgxLjdabS0xLjgtMiAuMy0uNi43LS4yYy4zIDAgLjUgMCAuNy4ybC4yLjdjMCAuMiAwIC40LS4yLjZhMSAxIDAgMCAxLS43LjIgMSAxIDAgMCAxLS43LS4yLjguOCAwIDAgMS0uMy0uNlptMy41IDUuOHYtLjJsLjMtMS41LjYtMS4yYTMgMyAwIDAgMSAxLjEtLjdjLjQtLjIgMS0uMyAxLjUtLjNzMSAwIDEuNC4zYy40LjEuOC40IDEuMS43bC43IDEuMi4yIDEuNXYuMmMwIC41IDAgMS0uMiAxLjVMMTE0IDkyYTMuMiAzLjIgMCAwIDEtMi41IDFjLS42IDAtMSAwLTEuNS0uMmEzLjYgMy42IDAgMCAxLTEuOC0ybC0uMi0xLjVabTEuNy0uMnYxLjJsLjQuN2MuMS4yLjMuNC42LjUuMi4yLjUuMi44LjJhMS42IDEuNiAwIDAgMCAxLjMtLjdsLjQtLjh2LTJsLS40LS44YTEuNiAxLjYgMCAwIDAtMS4zLS43Yy0uMyAwLS42IDAtLjguMi0uMy4xLS41LjMtLjYuNWwtLjMuOC0uMSAxWm04LjUtMlY5M2gtMS43di03LjRoMS42djEuNlpNMTE4IDg5aC0uNmMwLS41LjEtMSAuMy0xLjQuMS0uNS4zLS44LjYtMS4xYTIuNiAyLjYgMCAwIDEgMi4xLTFsMSAuMS43LjUuNS44LjIgMS4zVjkzSDEyMXYtNC44YzAtLjMgMC0uNi0uMi0uOGEuOS45IDAgMCAwLS40LS41bC0uNy0uMWExLjYgMS42IDAgMCAwLTEuMy43bC0uNC43LS4xLjhaIi8+PHBhdGggZmlsbD0iIzNGQTcxQSIgZD0iTTE0Ni41IDcyLjdWODdoLTIuNFY3NS41bC0zLjQgMS4ydi0ybDUuNS0yaC4zWm0xMi42LS43djE1aC0yLjNWNzJoMi4zWm02LjYgNC40LTQuNiA1LjEtMi41IDIuNi0uNi0yIDEuOS0yLjMgMy0zLjRoMi44Wk0xNjMuNCA4N2wtMy41LTUgMS41LTEuNiA0LjcgNi42aC0yLjdabTcuNy0zLjIgMi44LTExaDEuNWwtLjIgMi44LTMgMTEuNGgtMS41bC40LTMuMlptLTIuMS0xMSAyLjMgMTEgLjIgMy4yaC0xLjdsLTMuMy0xNC4yaDIuNVptOS42IDEwLjkgMi4zLTExaDIuNEwxODAgODdoLTEuNmwuMi0zLjNabS0yLjYtMTEgMi44IDExLjEuNCAzLjJoLTEuNWwtMy0xMS40LS4yLTIuOGgxLjVaIi8+PC9nPjxkZWZzPjxmaWx0ZXIgaWQ9ImEiIHdpZHRoPSIxOTYiIGhlaWdodD0iNjAiIHg9IjIiIHk9IjUwIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIzIi8+PGZlQ29tcG9zaXRlIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzE1MTFfNDM5NTgiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzE1MTFfNDM5NTgiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48L2RlZnM+PC9zdmc+", + "description": "Displays the latest power consumption telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'powerConsumption', label: 'Power consumption', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Power consumption\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"horizontal\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"bolt\",\"iconColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":5,\"color\":\"#3FA71A\"},{\"from\":5,\"to\":15,\"color\":\"#F36900\"},{\"from\":15,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":5,\"color\":\"#3FA71A\"},{\"from\":5,\"to\":15,\"color\":\"#F36900\"},{\"from\":15,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Horizontal power consumption card\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"kW\",\"decimals\":0,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "power", + "energy", + "energy usage", + "electric load", + "electricity", + "power efficiency", + "load profile" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_power_consumption_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_power_consumption_card_with_background.json new file mode 100644 index 0000000000..e275255fb9 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_power_consumption_card_with_background.json @@ -0,0 +1,32 @@ +{ + "fqn": "horizontal_power_consumption_card_with_background", + "name": "Horizontal power consumption card with background", + "deprecated": false, + "image": "tb-image:aG9yaXpvbnRhbF9wb3dlcl9jb25zdW1wdGlvbl9jYXJkX3dpdGhfYmFja2dyb3VuZC5zdmc=:aG9yaXpvbnRhbF9wb3dlcl9jb25zdW1wdGlvbl9jYXJkX3dpdGhfYmFja2dyb3VuZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSI1NiIgeT0iNTIiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjQ0IiB4PSI2IiB5PSI1OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48cGF0aCBmaWxsPSIjM0I5MTFDIiBkPSJNMjEgODloLTFsMS03aC0zLjVjLS42IDAtLjYtLjMtLjQtLjcuMi0uMyAwIDAgMCAwTDIzIDcxaDFsLTEgN2gzLjVjLjUgMCAuNi4zLjUuNWwtLjEuMkwyMSA4OVoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNNDIuOCA3My4zaC0yLjZ2LTEuNGgyLjZjLjQgMCAuOCAwIDEtLjIuNC0uMS42LS4zLjctLjZsLjItLjljMC0uMyAwLS42LS4yLS45LS4xLS4yLS4zLS41LS42LS42YTIgMiAwIDAgMC0xLjEtLjNoLTJWNzdIMzlWNjdoMy44Yy44IDAgMS40LjIgMiAuNS41LjIgMSAuNiAxLjIgMSAuMy42LjQgMSAuNCAxLjcgMCAuNi0uMSAxLjItLjQgMS43LS4zLjQtLjcuOC0xLjIgMS0uNi4zLTEuMi40LTIgLjRabTQuOCAwYzAtLjYuMS0xLjEuMy0xLjZsLjYtMS4yYTMgMyAwIDAgMSAxLjEtLjdjLjQtLjIgMS0uMyAxLjUtLjNzMSAwIDEuNC4zYy40LjEuOC40IDEuMS43bC43IDEuMi4yIDEuNXYuMmMwIC41IDAgMS0uMiAxLjVsLS43IDEuMmEzLjEgMy4xIDAgMCAxLTIuNSAxYy0uNiAwLTEgMC0xLjUtLjJhMy42IDMuNiAwIDAgMS0xLjgtMmwtLjItMS41Wm0xLjcgMHYxbC40LjhjLjEuMi4zLjQuNi41LjIuMi41LjIuOC4yYTEuNiAxLjYgMCAwIDAgMS4zLS43Yy4yLS4yLjMtLjUuMy0uOGwuMi0xYzAtLjQgMC0uNy0uMi0xIDAtLjMtLjEtLjYtLjMtLjhhMS42IDEuNiAwIDAgMC0xLjMtLjdjLS4zIDAtLjYgMC0uOC4yLS4zLjEtLjUuMy0uNi41LS4xLjItLjMuNS0uMy44bC0uMSAxWm04LjggMiAxLjgtNS43aDFsLS4zIDEuN0w1OSA3N2gtMWwuMi0xLjZabS0xLTUuNyAxLjQgNS44Vjc3aC0xbC0yLTcuNGgxLjZabTUuNCA1LjcgMS4zLTUuN2gxLjZsLTIgNy40aC0xbC4xLTEuN1ptLTEuNC01LjcgMS43IDUuNy4yIDEuN2gtMWwtMS43LTUuNy0uMy0xLjdoMVptOSA3LjVjLS42IDAtMS4xIDAtMS41LS4yYTMuMyAzLjMgMCAwIDEtMS45LTIgNCA0IDAgMCAxLS4yLTEuM3YtLjNjMC0uNiAwLTEuMS4yLTEuNnMuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS4xLS44bDEuMy0uMmMuNiAwIDEgMCAxLjQuMmEzLjEgMy4xIDAgMCAxIDEuNiAxLjlsLjIgMS41di43aC01Ljd2LTEuMmg0di0uMWwtLjEtLjktLjUtLjYtLjktLjJjLS4yIDAtLjUgMC0uNy4yLS4yIDAtLjQuMi0uNS41bC0uNC43djIuM2wuNS43LjYuNS44LjFBMi4zIDIuMyAwIDAgMCA3MiA3NWwuOS45LS43LjZjLS4yLjItLjUuNC0uOS41LS40LjItLjguMi0xLjMuMlptNi02Vjc3aC0xLjZ2LTcuNGgxLjZWNzFabTIuNC0xLjVWNzFhMy4yIDMuMiAwIDAgMC0xLjUgMCAxLjQgMS40IDAgMCAwLS44IDFsLS4yLjZoLS40YzAtLjQgMC0uOC4yLTEuMiAwLS40LjItLjguNC0xIC4yLS40LjQtLjYuNy0uOGExLjkgMS45IDAgMCAxIDEuMy0uMmguM1pNNDEuOSA5MS44bC43LS4xLjUtLjVjLjItLjIuMi0uNC4yLS42SDQ1YzAgLjQtLjIuOS0uNCAxLjNhMyAzIDAgMCAxLTEuMS45bC0xLjUuM2MtLjYgMC0xIDAtMS41LS4zYTMgMyAwIDAgMS0xLS44bC0uNy0xLjEtLjItMS41di0uMmwuMi0xLjUuNy0xLjFjLjItLjQuNi0uNiAxLS44LjQtLjIgMS0uMyAxLjUtLjMuNiAwIDEgMCAxLjUuM3MuOC42IDEgMWMuNC40LjUuOS41IDEuNGgtMS42YzAtLjIgMC0uNS0uMi0uNyAwLS4yLS4yLS40LS41LS41LS4yLS4yLS40LS4yLS43LS4yLS40IDAtLjYgMC0uOS4ybC0uNS41LS4yLjgtLjEuOXYxLjJsLjMuNy41LjVjLjMuMi41LjIuOS4yWm00LjItMi40di0uMmMwLS41IDAtMSAuMi0xLjVsLjctMS4yYTMgMyAwIDAgMSAxLS43bDEuNS0uM2MuNiAwIDEgMCAxLjUuMy40LjEuOC40IDEgLjcuNC40LjYuOC43IDEuMmwuMyAxLjV2LjJsLS4zIDEuNS0uNiAxLjJhMy4xIDMuMSAwIDAgMS0yLjYgMWMtLjUgMC0xIDAtMS40LS4yYTMuNSAzLjUgMCAwIDEtMS44LTJsLS4yLTEuNVptMS42LS4ydi4ybC4xIDEgLjQuNy41LjVjLjMuMi41LjIuOC4yYTEuNiAxLjYgMCAwIDAgMS40LS43bC4zLS44LjEtMXYtMWwtLjQtLjhhMS42IDEuNiAwIDAgMC0xLjQtLjdjLS4zIDAtLjUgMC0uOC4ybC0uNS41LS40Ljh2MVptOC41LTJWOTNoLTEuNnYtNy40SDU2bC4xIDEuNlpNNTYgODloLS41YzAtLjUgMC0xIC4yLTEuNGwuNi0xLjFhMi43IDIuNyAwIDAgMSAyLjItMWwxIC4xLjcuNS41LjguMSAxLjNWOTNoLTEuNnYtNC44YzAtLjMgMC0uNi0uMi0uOGEuOS45IDAgMCAwLS40LS41bC0uOC0uMWExLjYgMS42IDAgMCAwLTEuMy43bC0uMy43LS4yLjhabTEwLjkgMi0uMS0uNGExIDEgMCAwIDAtLjUtLjRsLTEtLjMtMS0uMy0xLS41LS41LS42LS4yLS44LjItLjkuNi0uNyAxLS41IDEuMS0uMWMuNiAwIDEuMSAwIDEuNi4zLjQuMi44LjUgMSAuOC4zLjQuNC44LjQgMS4yaC0xLjdsLS4xLS41YTEgMSAwIDAgMC0uNS0uNWwtLjctLjEtLjcuMWExIDEgMCAwIDAtLjQuNC45LjkgMCAwIDAgMCAuOGwuMi4yLjQuMi44LjIgMS40LjVjLjQuMi44LjQgMSAuNy4yLjMuMy43LjMgMS4xYTIgMiAwIDAgMS0uOCAxLjYgMyAzIDAgMCAxLTEgLjVsLTEuMi4xYy0uNyAwLTEuMiAwLTEuNy0uM3MtLjgtLjYtMS0xYy0uMy0uMy0uNC0uNy0uNC0xLjFoMS42bC4yLjcuNi40LjcuMS44LS4xYy4yIDAgLjMtLjIuNC0uM2wuMi0uNVptNy44LjN2LTUuN2gxLjZWOTNoLTEuNnYtMS43Wm0uMi0xLjZoLjVsLS4xIDEuNC0uNSAxYy0uMy40LS41LjYtMSAuOGEzIDMgMCAwIDEtMS4yLjIgMyAzIDAgMCAxLTEtLjEgMiAyIDAgMCAxLS44LS41bC0uNS0uOWE0IDQgMCAwIDEtLjEtMS4ydi00LjhoMS42djQuOGwuMS43YzAgLjIuMi4zLjMuNGwuNC4yLjQuMWMuNSAwIC45IDAgMS4xLS4zLjMtLjIuNS0uNC42LS43bC4yLTFabTUtMi42VjkzSDc4di03LjRoMS42djEuNVptLS4zIDJINzljMC0uNiAwLTEgLjItMS41IDAtLjQuMy0uOC41LTEuMWwxLS44YTMgMyAwIDAgMSAxLjItLjJsMSAuMS43LjUuNC44Yy4yLjMuMi43LjIgMS4yVjkzaC0xLjZ2LTQuOGMwLS4zIDAtLjYtLjItLjhhMSAxIDAgMCAwLS41LS41bC0uNy0uMWMtLjMgMC0uNSAwLS43LjItLjMgMC0uNC4zLS42LjVsLS4zLjd2LjhabTQuNi0uNS0uOC4yLjItMS4zYy4yLS40LjMtLjguNi0xYTIuNiAyLjYgMCAwIDEgMi4xLTFsMSAuMS44LjVjLjIuMi40LjUuNS45bC4xIDEuMlY5M0g4N3YtNC44YzAtLjQgMC0uNy0uMi0uOWEuOS45IDAgMCAwLS40LS40bC0uNy0uMS0uNy4xLS41LjQtLjMuNnYuN1ptOC4xLTEuNnY4LjhoLTEuNlY4NS42aDEuNWwuMSAxLjRabTQuOSAyLjJ2LjJsLS4yIDEuNS0uNiAxLjJjLS4yLjMtLjUuNi0uOS44bC0xLjIuMmMtLjUgMC0uOSAwLTEuMi0uMmwtLjktLjdhNCA0IDAgMCAxLS42LTEuMmwtLjMtMS40di0uNWwuMy0xLjUuNi0xLjFhMi41IDIuNSAwIDAgMSAyLTFjLjUgMCAxIDAgMS4zLjJsMSAuOC41IDEuMi4yIDEuNVptLTEuNy4ydi0xLjJsLS40LS43YTEuNSAxLjUgMCAwIDAtMS4zLS43bC0uNy4xLS41LjRhMiAyIDAgMCAwLS40LjdsLS4xLjdWOTBsLjIgMSAuNi42IDEgLjJjLjIgMCAuNSAwIC43LS4yLjIgMCAuNC0uMy41LS41LjItLjIuMy0uNS4zLS44bC4xLTFabTYuNy0zLjh2MS4ySDk4di0xLjJoNC4yWm0tMy0xLjhoMS43djcuN2wuNC4yaC40YTIuNSAyLjUgMCAwIDAgLjYgMFY5M2E0LjIgNC4yIDAgMCAxLTEuMS4xYy0uNCAwLS43IDAtMS0uMi0uMy0uMS0uNS0uMy0uNy0uNi0uMi0uMy0uMy0uNy0uMy0xLjJ2LTcuM1ptNi4zIDEuOFY5M2gtMS43di03LjRoMS43Wm0tMS44LTIgLjMtLjYuNy0uMmMuMyAwIC41IDAgLjcuMmwuMi43YzAgLjIgMCAuNC0uMi42YTEgMSAwIDAgMS0uNy4yIDEgMSAwIDAgMS0uNy0uMi44LjggMCAwIDEtLjMtLjZabTMuNSA1Ljh2LS4ybC4zLTEuNS42LTEuMmEzIDMgMCAwIDEgMS4xLS43Yy40LS4yIDEtLjMgMS41LS4zczEgMCAxLjQuM2MuNC4xLjguNCAxLjEuN2wuNyAxLjIuMiAxLjV2LjJjMCAuNSAwIDEtLjIgMS41TDExMyA5MmEzLjIgMy4yIDAgMCAxLTIuNSAxYy0uNiAwLTEgMC0xLjUtLjJhMy42IDMuNiAwIDAgMS0xLjgtMmwtLjItMS41Wm0xLjctLjJ2MS4ybC40LjdjLjEuMi4zLjQuNi41LjIuMi41LjIuOC4yYTEuNiAxLjYgMCAwIDAgMS4zLS43bC40LS44di0ybC0uNC0uOGExLjYgMS42IDAgMCAwLTEuMy0uN2MtLjMgMC0uNiAwLS44LjItLjMuMS0uNS4zLS42LjVsLS4zLjgtLjEgMVptOC41LTJWOTNoLTEuN3YtNy40aDEuNnYxLjZaTTExNyA4OWgtLjZjMC0uNS4xLTEgLjMtMS40LjEtLjUuMy0uOC42LTEuMWEyLjYgMi42IDAgMCAxIDIuMS0xbDEgLjEuNy41LjUuOC4yIDEuM1Y5M0gxMjB2LTQuOGMwLS4zIDAtLjYtLjItLjhhLjkuOSAwIDAgMC0uNC0uNWwtLjctLjFhMS42IDEuNiAwIDAgMC0xLjMuN2wtLjQuNy0uMS44WiIvPjxwYXRoIGZpbGw9IiMzQjkxMUMiIGQ9Ik0xNDguNSA3Mi43Vjg3aC0yLjRWNzUuNWwtMy40IDEuMnYtMmw1LjUtMmguM1ptMTIuNi0uN3YxNWgtMi4zVjcyaDIuM1ptNi42IDQuNC00LjYgNS4xLTIuNSAyLjYtLjYtMiAxLjktMi4zIDMtMy40aDIuOFpNMTY1LjQgODdsLTMuNS01IDEuNS0xLjYgNC43IDYuNmgtMi43Wm03LjctMy4yIDIuOC0xMWgxLjVsLS4yIDIuOC0zIDExLjRoLTEuNWwuNC0zLjJabS0yLjEtMTEgMi4zIDExIC4yIDMuMmgtMS43bC0zLjMtMTQuMmgyLjVabTkuNiAxMC45IDIuMy0xMWgyLjRMMTgyIDg3aC0xLjZsLjItMy4zWm0tMi42LTExIDIuOCAxMS4xLjQgMy4yaC0xLjVsLTMtMTEuNC0uMi0yLjhoMS41WiIvPjwvZz48ZGVmcz48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2MiIHRyYW5zZm9ybT0ibWF0cml4KC4wMDUwMjUxMyAwIDAgLjAxNzk0NjkgMCAtMSkiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjIwMCIgaGVpZ2h0PSI1NiIgeD0iMCIgeT0iNTIiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjMiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA4NV82ODQ5OSIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA4NV82ODQ5OSIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFJQkFRRUJBUUlCQVFFQ0FnSUNBZ1FEQWdJQ0FnVUVCQU1FQmdVR0JnWUZCZ1lHQndrSUJnY0pCd1lHQ0FzSUNRb0tDZ29LQmdnTERBc0tEQWtLQ2dyLzJ3QkRBUUlDQWdJQ0FnVURBd1VLQndZSENnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ3Ivd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhRQUFBUVFEQVFFQUFBQUFBQUFBQUFBQUJ3TUZCZ2dCQWdRQUNmL0VBQnNCQUFFRkFRRUFBQUFBQUFBQUFBQUFBQUlBQVFNRUJRWUgvOW9BREFNQkFBSVFBeEFBQUFENk0rL2NlbVFhRUtaaWlUY2tnamFyTlQ3bXRYbWtyM2Y2ckprMXdFaUZNbTFKa3lTWkJvYmF1eVR0NTJ3bThuOGw1TEtjcjhkdkpFMnB4Nk95RGp6U3RYdkl1MWR6WjJHamJ0RHZacGoyYU12dlFwdXlSaW03Sm1LUng2dStpZkR0bE41UDVMeVdFNVc1RGJUSnRYSFFtNHpCWmxWeWhPSUtkdUExWk9wTllmWHFUVzlETExjTXJzMWtqV2hNbVE2RzJIV0UyRTNrdkovSmVTS2ZJN1dISFFsekcyVU9XWVZSR0xLZGdkUkdWYmxlR1F5RENHUmdZN203K1pOcnRmcWxEUjFxNjFkWWRZWnZKWVM1WFpzRnpmeXV4b1NUZGtTRlZQaG1Ga0NJZk9UVncxSWJHN0loSW9BakNRU3pORTczNFpuWWpJbDJDY1dZVlNXcVRTN0pNT2lmVWhYU2NTRXBjbnQ2b2VVa3VsaDFoT0RhN2RPUFhaN09vZEloRjNXWXBER2NQaTBQcnl3R09YZ1pSOTN0SHBaOHFtVHNhODdJa09YYlZMTElvOHRzODdzc3k4NjFUYUpWMFl3dEVmUUEyNXUxM3FjTW1JYWhNcVdCR3B4Q1dONmlaRit6TjZBcjNZU2RQRTR1d3JGRm1SdDJmZGtRZWExZk10M2Z5V2pyVW1CYkVMQ1RIQTl1cnRYeUxzSVlGSExRdkcwcmpiZVdSSlJpTTFXTkJMVkNsTXpzZHk5T3JnbU5DRG5Sb3BFbm50RlJMWk5xbHE2UmR3UEltRm5SQmpYTUR5azJrdUJtb3BuMmI5Yk9lelF6QitLZDFzMHlKTUZXNmNvdGluRktmNm9YcWorejl6dkxjUzlsTHhEcWx6cGNib1ZHNDFUemNGTHA0cGdJckdtdG0rYm1YZStqT3hub01VY0NTV3lSMW1vMnkzYWdaSFlPU0RhaVZwUURLTVVueTdIbVd3cmdkYk91cElFdVFkTTNsQ1h5WXBGQmhLRVJ5VVV4ZDM2TjczT3F1VFM0N09xWFVMTnVMMElNdHBnemJFODFBQWxHUXlzTnNxUzFTNVdYVXk4NkZReWh1Y0JJYXRERUQwaWs2YVFvSU5IUFVmTDZDL3V4ek9EVE1seGtOTm9KclFYNEtsRXF1UnpIS3pGWmNYSVZJeW5VUHRKK2xtWVdUTUxoTUQ3YlFBSWp0VFVVUk16dUlQN2pDM2VvZExvTDE2UE5WNGM2ZUl6ZFlpaGRleWZyY1VuME0zNTA1Vmt5Mkd2Vk5EUWFPZTNtZGN5N1JjZ2tVRmlTRkRVMnRvRmllb0RKVlpHb1FBYWE5N1YvTTQ0TXEyMWRxNmVuelVCMUt2emVyeWZTemRvaDdBMWVJYlFPbHBWa2lleDB0bTJQVGMxUXlPYjY2Y2wwQzhhNFJXMGN6S2NWQWF1Z1c3OUtIUkZjY0ErYkVGcjZRSFZmcFkvbkd4dVRhOWxlazVSeW1qK1lPUmIrbnVoWEdsSzQvd0JTNVRiVnhoN3I1M1puYlRKdVlMa1ovWmZqT3pVQXRXSGVJbDNqb1RCSVJ6aEVaUGFZRjg4YWsvME11MVpSTkRUV0tRWVZOdjZDVzhWclpmSkNrZjJXMXFndG1FRlFXeE1GYXJTbUtMV1YwRWxpc2ZSN3VNcXYrWWN4TTBVM1BFcXlZRm8wMmFBRWFleGRsVjh3ZEc0MjFUbE1vQXVnNEJ6OXI2VmIrRkJRVk5jMnhkblRxaWFBb0UwNEVyeHcrcmROK3BteGZSeHJLNnVmU0dlb1Z0R2hGSjY4Y3hOazI5ZnlOV1BLdlVldld5WjlHL3R6RmIrSjdXdy9vL24rOUsyRHNUYjE1dnM3QytwZU94REUyeEJ6dlNFdnFlWDF5OUdPUmFOZXNlMUxjbmZsL1Y4bndQRWRPazU0TWMxcnlUTHZ0a2xoR1JLNnVDRU9IN3VVNkVFWmhHWjYyVjBZdTR2MGZOT3pCR3FXZzZaZlJyZEx3akhpN2tSbWxaekdUMTRXWldoRWNaVnh0dUs2dURFaWtmSUE3S084NlJHMHBuU1Y4d0dzTHdPSzBVTE1JS2lobThVdlJGTU1MTU82VmdZYlZhcHFlQ2VYTkgwMTVSM0lFbUdTTUZDU1ZJSldEZ0pjc2dmL3hBQXdFQUFDQWdJQkF3TUVBUUlHQXdBQUFBQUNBd0VFQUFVUkJoSVRGQ0VpRUNNeE1oVkJVUWNXSkNVek5DQW1ZZi9hQUFnQkFRQUJDQVBqT000empPTUwrMGJPNGlsVU9UdUw1Q0hYdFFld2l2TnZZVkdWSEk3NlhHY1p4bkdjWnhuR2NaeG5HY1p4bkdjWnhuR2NaeG5IMGdmNnoxRlJYYVVtWjhSN0xhTXREV3Z4WVlLckd2N3FyV1Z4RzJIZDQ3SEdjWnhuR2NmVGpPTTQrdkdjWnhuR2NaeG5HY2N6bkdiL0FGU2JSTGVmeVhaN0w4VjZsK2xKU2x6ZGJUbVFSWVZ0dUpXazdDaGlXTHVJUG1KNHpqT000empPTTR6ak9NNHpqT000empPTW4rMGNaeG0wVEQ0U3FSTHpXNWFhZGFwZFFiWXJDM3IzRTA3U1lzV2djYnp2YWxzbmRxcGk3ZVZzcnNQc0E0UllKQ2Y2OFp4bkdjWnhuR2NaeGhFSWZ0NWhuOU9NNCtrUi9YT000elpyWWZoQlZiVkF5WnZWNnl5R3BGVWhIZ0l6WjFLcGNHTnlqYVRXOVc1ZEV0dTMrWG0weTVVNEI0a3QzYmVxK3JsVFBIWWlSS080ZU00enlxalBKTS9ydytjOEhQN2loUS9qajZjWnh6bkdjWnhteXZWbFdGMTVTOWlMWjE0MTdsSFNFYkd5c0xXc1NxMUsvay8xTGVNT2dxMmRnU3MwcktnR1paU1Zac2crcjZwbXNpYTE4TmxkdEpDMXJoTVNPUWJGVkVlK2R2SDQ0ejIrbnRudG5HVC9BR3pqT000empOMlZhdSt2YnNMbllyWWM1VnRkbXFXM0JEN1VTUS9ybkdSZVJYdHRVNFpFeDdnMkNLVEo1YUdwdEpJckJ4VXQyWWh0U05qWXBNbU5uWGQ0MEI0VnVCbnRrL2puTlQvd2tvdTNPTTQra1JuSDE0elowMTNybGRUUCsxWWFDZFJUWXVzdTVaVEhkWEhGVCtRd3BnWTk2YXZteGg5SVRjdkx2R1NGMnFuM0hKc0lzZXlyTk92Y3M5ajJVSEJZODhkdnB5R2RaNnNHY3EzUHFUaFI5MUtZOG53NHp1Q1B6NVZaeG5IMTR5ZmJMY3krOTZiREFWM1d4R3VMbWlxZ2RJZUt3amhLZ3A3czdCR2ZkWVRMZm4wRXJ0L2xCeGY2OFpZcW9mSExabXpWcy9ZQzE2b29DeVZHcVl3Sk42YkFEV2diU1RwbFBvbGN1a0hXVTFsZUllWVVzZnh4L3dDTXpBNTJ5WHVUZUJjODg3WGc4KzZsRU0xaXJBNjh2OU44dmtXUU1SK0FqNzJmNGRjZWZjREJSSUYzajk0OGlna2JIZmtMRTErSm0rdC81ZHJMdDFaZTFObVBWN1ZGRFlWbG0xTmFWd0F0cWR2cFY5dWNmWGpKbjM3UmdQNnppZVlzT3Zac0xDL1Z1Wml4c2RzT3JhdnVsWitUak9NZTd3ZVJzZERJU3Z6dlZBUkg0NHdvKzRPVEVpWGNQWDN6NmY3bzdTODR0bmJLWEVlVU5iVnMyRDdFb3YwMEs3SFcrbzdNTzdhOVk5NCtGak9majgrNVpFY1ovd0RjYnRWUllWVVRyMHZ1TDdjMmlVVW0yaGdIc1NzYURhQ29UWWVBUHUxNi9zZlpNL3RiV1BZM2pvZ1E5TEJaeG5HTWo1RGpiTlZCZHJ2OFJKQXVrTFdBNENycGJuVkIydlNFNnBwZHJONUhxVzlMcmhoWDB1OUJQbTdvVWRXandEUWFMUjdrd1A4QVdjZTd4UkhIcDViNzJkdE1CdTlmSlZpaXRXUWtOMDNzTjBzanNhbnZUVUI1N05xTHZoV3VPeFVlOGM1YS9CNTBmMndnWWozeTV2bHJiQVVuZFhYcnQxZFdnclh2MkljMnR4Uk9scUpWaXdhZFZSRmFVNzBaQm5ScXZUZFBXWVowMDlQODV1RUQzTGdlUjYzdEdOWkRJR3M4Y0k3U1I1d0xPd05oZVZkejdrSGg3VVErTWJ1Nlo3Q0hIcnJWTXJFSmR0djlROWhpMnBLblRacDlQYkd6dGI2cldSTm52d0hIQS9LeGJxTk0xNTBrd0lURXdWbjQ5dytPbDNTNXQ0UC9BRzRiSUw4aFI3OVR1U1duT1luYnBxMEZwSGM5WU9TSGhucGxGKzZtN1ducHlKaTY4U2JEUHpIV2dRM1RHdXZ6eDdaSDk4NStSUm43SE9NcFVwajMzUzB4dlBEbnBlKzEyTjNNTUswcE9lc0dFem5SUHdzbzRzM0dVRlMreDV5WEplUk8yWEhXbGhwOU92V2lsM3RIWWErZjFKME9qN1c1dFcwZFREYVAwYlJqdVBmMXlqVk41MTNwNTFjZC9YSFkyaXJOSnNWcTIxOUdWV0ZGaTFDdHBiMmF6RlpYYWpWVmZJY0ZHZVQrd2w3ek9DZnpMQ0tKakx2ejZtRWNXK2FETTJuZW5iMTNPdUxNcTdMeDlMUXluc2tJbnFBLzl1amhWbGx6eXhsSnpoNnlzVm9xY0hwN0s1aWxRSUJrb29hMlQ3ZzZyckJYM0pFTjY0NnJWT3lqZCtzYnBuRVhaWWQwdlpsTzRSVkhtYkdwSXE3ZzJJVGJNN0pPRzBWNTZ2VnZzYXpkK0h5TzlURVJ6aXRwdTJiRWE3QTJsdDF0bE5hYnpsa2ZtMlBVRDZuL0FBczJiVTdhYnRvZDNjVXYxQ3JWOHJHM3J6VnY3UHRxR29xN1BIZDhrYmtwZFRBR1VILzltY3FXcTQ5VnNTR3VPVDF6NHhUNGhZUmxHMUhxTEk1dXlFdDZEY3V1U1d2Yng1b2IwNFluckpobWhzSnk2WHdyTlhwUVVtMm1KMmp2OTNXeFp2cjJlbVZDdXRzcC9qbHBmcjlvMmJVK2RWbTQ3Wm5LbDNMWTdocXhxWGJIckVyaTFhTnlUWEcwZTRMaCtRM1d6Smg1YXJHbllwQU5sQmR2WXVyMlF5U0M1ZDd2R0VEZjdGSHdteFA4cXpZaHJwa2FMc204NFRFSXIzSGNNZEQyZWJZeE12Y2h5dkRrRFdtak1UUWVzZGNjdFVVQldXSzllenRlRTViYzJiaVNQV3ZBdGU1V1UycktvdVNpb3NFRW9EbVJoZ2d3UTU5VFB0NWxFdnovQUNJSkxteTZZSUdWNSsxbnFROW1qeUhwaEdYVEEyRG1JMk5kbllBRFpRQ0lNYTd3ajVUV1pIb1c5cmRwWEd4TVQvSUtVbUp6MWNsZURJY21xMXp5ZHVxNlB0WVZqeDYveFpMWXFySWgxYisrRnN4ZTNLOVpHQ08wZEVXZUU5NVpxckFWbHUxbEh4TGRWREdJa3R2WE91S2NqWUxlVStCT3lON1pXcDNkU0h6c1hzZVdqd3VqWWFxQ24xc3BzY21WL3dEa0h6NG0xbTF5aHEzeVlUQzIrcHJlaW10TU5DTldjWklVem52WjR0WmlYMHdjTGl0V0tqcElzbGRTZjBLeVJyZ01kM04vZWhaWFdEdGJOZHBGeU02OHluMy9BSXM4LzhRQVFSQUFBUU1EQVFRSEF3Z0pCQU1BQUFBQUFRQUNFUU1TSVRFRUlrRlJFQk1qTW1GeGdVSlNrUVVnTUdLaHNjSFJGRE5BUTFOVWNwTGhGU1JqZzZLejhQL2FBQWdCQVFBSlB3UDUxVUF4Z1NyUjFadTZ1Y2p3bm54VkUxS1JBTFd0NzdSejhVNXBaOVQ5bVlMaFdFRTh1S04remlLbEVWbTRkNHA3cktQZG9lOEoxOFlWQ3B2UHVacG5pbTlXN0hlL1krSFJ0VDJiK1FYYmh4eENjS1BaM041YTZlQVZCZ1kxeGtrNU9kQithK1VHYlFXUGdOZlUzMkVjdVlXMXNQV01zcU1zeXppcW9PL1k5cEdoVHJDREJhL0g3SG9hMlk4aXRTekxnTk00UGt1eTNPMXRHN01uZ3RpNnh0Vm9sOVBnZkpiSURkVHRZOXRmTWpJZ2oxV3pOckJ4YUdDbDN2WDdsU1lYWFFLTlBBcGNnZkZVdHlwM001bEhUWDZGd0NhNTNrUG45NDFmd0tnTnR0c2pYbW0zVXkxejhCY2tUVHJETGFsTXhIaVZUcjFpeHdkMlozajR4elZUYUtMVzFRS2JHVGVCUHRUOXkyU3BXRis1VlpyNUZzL2NtaUIzaTErbmdnUDYybEdSejZYajBWRjNyaFd0KzFWWEg3RXdmUU83VFcwK1dxZjNHTm1yL2hOTjBrdWprT1NwekluUEJjY2dINytoem1rdkcvVGRCMENGV3ZGUUc1bFhQd1ZBMDVhNjV3clpPbXFwTU5vRHYwaWxtT1VqMFJGTmhmYWFyaE4wbmdQeFZTcVBzQlZPZlBLSDBnWWJiZzFwR1htTzZxbE5yM013eDJmUlU1TmxuZ1IrYU02ZEljTjZiNHdFNEVjd3FRTlQyU05VR1BtTUNrTGtIN00rZTBKcFFabjNmeFd5dWdOeFZaTnBiekk0S28ycGYzWU1nclgzZWh1bFEvUlJ1WFZHazgwYlh2cEMwZTdDcWZ1WWc5MXBQLzJxOTFham9iRG5udkxiS2wxSGJYTlk0SGdxQXJmWHBhajBLcVNScTNpUFJNbnMrZmlyYWpMTFNMZDVQTlBhUTd0R3h1UWM1Q3FHbTV0VFZqK3orUE5aWmkweGxhUnZEb2VQaW4vRDUrR3ZZR2wzcWh1c29pNGpYam56Q0dPckJlN21FNll4S01IbUViaXZoNm90enQ3dTY2VndWT1NOSGNSNnFyMS9aNlBPZGVhSm9Ub3grTHZWVUJqUXhsVFdaN1hXbk5zNkovVk1wMUxPcmMwa09KMHh3VG5peGtsN2VHWTFIaW1oMjczbzFWTWZENkJsOTF0TU00NkxmRGRsa3g3ZVRsT0hXV050UExHaXhEeUNzZFA4L3dEbXM4MnBsdm1wa2pNSFZOQkdoQlFkRDlvYlRMSjNjcWhHNGQrbnZEL0NEYWpSVkVPQjBYYTc5V1ZwWUkraHl3VlNIRDdMazhOTDlrbWw5cW8yMDJzRXVjM1E4U0J4VllWSENwbDQ0OUlrc1lYUjZLWjJ0NXFWTTZaL3o4ejFDeFp0TkoyOWoya2ZaT0ZUZzNDYWxQelQyeFRyazVPWmhWV3NzeHZPaWZMbXRrcnZaellMVy8zRlZhTkc5c2k1cGM3cHdPbmVkV0pGTng3dU1sYlM3dk9OUUFSTW5Db3hic01pMzJUS1p2MjZzOTNtb3RKYTRCdkRDcVo5MGE5QS9jbjdrTjdPZW5tcTdXbjZ4UjBzUC9rRVlCYU0raVpVZGFaY1J3Q3ExWEIxWE5qcmRWc25XZFh0eERTNGFDQWlRZVR1STgwd1VKeHZIWDFSdUhQcGJMbkdHaEcvNnZzajA0cjJhZTBPSDlpYi91ZXFCYjlkTXVQNkgyZzkwM29HZTkxdkpDMHVvaHhGUEExVEFQSWRIdWZnVWM1NkhVYXY4U2FrUWhNdXdLYk5maXQvZHlMaElUS3BCMmdIdHFsM3A1SjNvbmdOT0RIbXFXYU8wT0RkM2lEcW5HM3I2YmcyZE4zb1k0ZFZ0UGVQOUJXelk0V1YxUzJuMGh5clZLWUE0MEZVcHVJYnV5WUtwWG4vQUkzZ3BsblUwSE5ZQ2ZmMVZZRWtXMDhkNGZtbnkxMUpnejd2V0twVUZGbzF1bVBUaUZVYTF6OWp1ZHg5bzRUR094d2RDMlovcEJWVU5lMGFPMTBReG5RS21mTndWQ1RFZnErQzJiSDZXMDZjRFFJL0JPQWJyQ09SVWI5NnAzMVhBV0NNTFpHc2RydkVxcDNkdGRMQzlNazR6b3RwcHNIOVVsVlRVcmx6YmMrUFNlU0hBYXJaYWZlOXhDR0dyU0JoUHFkWEpuZXkxVlIrcnBCdG50aTlOakVlWG55VTVwbnZlU3BnZzFHdDNEek1Lanh4QlZMZFB5Y0I2M0x1Z21TUWlmU1FxM3hNcWhodGVnN2QvcGUxVXlUeWJ2TFFSanF6elZXMDI4U3F6WkY0UFovVS93QUxxMnk1cjJ6b1phakV1eGFmRlZIUTdsL1VCK0tyU1FST1BuUkhYQ1pISnFvdmRUNGZWVFJKNnQxckJwdm93NFVqdWppT1JWU2JNTytDL21hWC9zQ1lPenFsbXVzSVMzL1R3YmVSblZNL2R2MThsczhibXJRUXFyaG1EdjhBNXF2dTlnNjZSd2U2VU9zaGhjTVlUQUIxVW5LcUVXN082STV3ZzUxektVQW4zcWJ3ck42a05XNjRoVzN2R1EwS2s4c2NKRCtxd2VLMmFxV2NkOUZVdG5wVXJTN0JMaVJQMksxaHBnRnp6blZWSFZJcndGOGwxS2dHcjd3MXFwMmx0MTRwR1l4d1ZvRk9sdU5PWS95cUR5OWpodWtSbVRwS21rWE5JSWJrUDhreUhNYzBnZkJSK3Zwem54bGZ6TDQreFVBSEhZQVhQOVZ4WTVwK0Nkb3hPMDJnb1R1alVlSlVmcVRwNUk3M1V1NHB4M3FMaHI0RlZKSTJXZ1FkY2dIODFGck9KYm92NEJHZlZVdDVySGpYbUNGVEpkMVl2TitwVlYzVmlrRGNjKzJSUDRLaTRpblF5ZUdxZGg5dHhIQ0Fxem9yVjZ0MXhpWTBXb2NCanpSaDhtN2Q4VnRUN0ErMDQxVlpycnY0ZkFaVzBQTVJKbEVpUGVjalBhRGlqSmttMEpndnNETzl3NlNKdS9GZUgzcGpNVXlSYTdSQ0REamgwSWFONXAxMXRMM3VVZEZOcmRZdE15b1E5Z0oyOGVyYUNlUWRLYzRPcUdIR2RST2lNRFJXbHpLZUo4VStYVGNXbzNPYzRieFFPVzRsVlR1akJpTUlpU1pjdmFjUGluNXB2emhWTldZK0ovTk94ZWp3VG5kbmhTYitSUTczaXFwemMyejFUS2I5M1dVd082dzJpVjNpTXp3NWRER3R0UEJZYzdFcXF5TEJpRlVMQ3F6eVl6TGRVODRNNkxPNW5DdHVJUkFONFZkazZkMHA3ZkRHRnRrMnU5eFZZYmRkM2NyYTVKOWx3VzJ4QXdJSzJ2TjgzV2xWWW1uckMrVXpKMTdGYmM4LzlhMnh6cmVCWXFqWU00ZzgxVnBmMnVXMjB0MHlPeUsrVVdIL0FLaXRxRHQ2UnVsYmZ4eHF0c2FmaXRvcC9Bci94QUFwRUFFQUFnSUJBd01FQWdNQkFBQUFBQUFCQUJFaE1VRlJZWEdCa2FFUXNjSHdJTkV3NGZGQS85b0FDQUVCQUFFL012NGdIbVpoeWUxRzVsVTBubFlLVzR2cW1Cc3MyaUlLcmpXNGErUFQwZWovQU9UQUFBZ0ZzVFJsbkJJZHF5ZmlTODlBYW9lK25qcnVwbzhNc1pHNXhjZVpYaXV3REl3YyszckRkZ2lLYnVzK244Z1YvQVZLL3dBQUQwWDNmVFdJYzlSRmowS3hHaFlGc0dOcy9RektqSnhDNmRQOUVvcjRkY3RlQTg1cHFaRFEyWlZNTFprY3pNY0ZMQ0hUMy9NMzd1SXIvWCtJZUgrTitQV01LbEgwV2c1SE1BbGs0MTNOYkt2dThzMUZYVTNTWDhMTWxrSTFlZUFSV2RncXV1b25YZXJuUUxvaXUwemdPQ0RsSEdteThPbXBacW44c2gzSkxQR0RZZHZYRXV2Q2NuK0I5YUJYNWgvaHN4d1d6bmJmcXMzYkFWeVpzOFlsNzZBNHBlWDd4S2l4bXlyYUh4OXA0UVplbm1ZK1BjT08rb25LSEhRSEszd3ZCZFFOZko5UmI0L2hjcW9BUU9kTmRVNit5WWVZdVlHQzJPTjVoZ3Q3a01YU1FtWk5CK2hwbGl5cm5wbDlweXA0ZmxQMXlsdmFBMCtKa2ZPcjZLK2pMZkJyK0FONjNSZ3E2RitscHFZOUZZc3BYYm9tTStaaVNIalg5RDd6ZStiVU5jLzFFMlZYSzMxZmc0K2pVaXdCays4dTUrb1BOc2ZFWUo4VG1tTGQxZW1PTzI2UGtYZFZ3Z0JxMlgzUGw3UnF3YVYvMEloaFhWL2xBR2dQQjlHV21VZFo1VHkrZ1hqNnpzL2dHNHZoZUwrbytEbktvWnk2cEJ5Tkk1QXYrMDM4VW9iUGo2UVVEaVh3Qm10ZWRRODVhV3lBaHZzTjk1a3VnVnlqVlhUNlRBdHl5SFFQZlYvU1lrWVducU52NGl0Mm9PQnRhZTBTcTExdHc1clFRb0pMcDN0dCtaVHBQSDZIRXBQb3FWOUJ4ZGtJS0Q5NlhESS9QT3pheSsvaGlJMjFmNXgrSzdGaEU2a2dOamx5Uy9leUlHcnV1dzFWOU1RTThpQWRpYVp0MjcvYXZETG9leFk4aXlUR1BxaUlSTVd5ZW5XWFhmaHYwbVdvbkUyRnJ3Y2xhbUk0YXFFYzZaNk16SmpvR2g2ZFlBTmVvZDB2N1REZjBSZEFQdWh6Zm9xVjlGQmJCRDJvYXlmeDd6V00vd0NPZG42RXFDUWswdlBlMzJpUFllVG1wVUNHR1lhTEczTEJXekNnR205MXFjemxUSzF4WU1lSVpKamdDN2hUZ0RmcktRWFdXOE5HT0ErQWVzWGt3NHp2SXhYcnViRHo1UlNLZG11a3pYTGE3ZGc0UGlKWitsbzhOeldYeDlLcFVxVkNNd1o3Skw1MUE4bVBWamdyTnZYNU43SU5oNVVvQzdkWWVpeFR2ZkhhVnJ3bWxUSTgvd0JUUEpoWWptS1BKSFNNY3dDSU1yUjZvM0NzSFl4cmJhYmk5WFRyMG1ONmJ6YVo2d25YM0xkNHdMdEU2eGVxMXJNckd2R0ZHb1V6TCtEU0xaVTJXeW9UTitGclI0Q3ZlRnp0VHBuM1diOHhkWmJocU9vOTk0NW1hU0tXT1dQcVVhZURxcTM0anFDWXRySENOUyttcHBYYXhrTzdPeGxkYkxUK2dIZW1YVTMyeTlEMWw1eXhWQmNqNzFLUUhPbUVLVmI3SldPbk5tdTliYXJMNWw3ZTZsaDEwYzcraWd0U3BnbWdqUVdhRGJBOXA1bzY3T2NkUGVNbVd2QUxSZG83OFI4N1VkWFZVN3A3OVpxdVBnZnYxbWJJall0dzdZaVFhNkJrczlweTNvUUdNc2ZuSDYwRTZqSDFLSGcwN0ZoVGlWRGUyQ1M0dE1UVzRIMFFwRGtRdjNaUWRqbm5CMW1XblY5NnJBTGtpZ1d4TEs3RmMvZUJKR0pzS252bFNpYU9wUTIyL1N2YjVOWC9BS2wvdlpTdjJPWTFxb0NzWUVVTmNGY1c0WHA5cWczOGEyZXFsMDFxVFYrenpYek56RU0xSWlLVnBhMlRBNmlBcXMzeEdveTM0a09xQnBnZDE4Y3dNSFhKVXRCbWtkS09UZWw2dUh4TzZWN095QTJzcXJhbXZtVU9vSmFJTTNjaFRuN0hBdm1lODJEWFVpUHl6eWpYek1NWWpiUHZPTTdoaDQ3d2NLelZQenl3N3RLaDB0enZ4RnZENGw4SHdvR2s5ZEZUWjRQZ0w5M0FrVnc5cVZmNWxDc29hV2NpMC9IRWF3ZVZVZVYzMHdLdXNESjl4S0pqWS83STRxdzFiSFhyTUtsOGVhcG5aUzZ6UFNZY2VJWlhMMExWb0htS0xETUdHRWpYZThPQVhLN1o2azBsUktBcHVOTHJpeW9QN21YV2czQ3gxZVpuZm91NlIrc1RUZlZYcE1GWU0yM0VnTW9BTFN1QjVnc1ZpNmJ1VlZVbjNWY3luTTRXMVl2K3RRcEU2TVhIMlluOHdxVlY2MStoS3FKQzF0ZFdBV2VYWGlPKzdLdXdaVVdPSUFnUFcrZUlXeDFoUEVjK01zOW9mZjEzV3BZb2JQUSsxenVjTk5kRlB0SGl4ZHNEcnpxT3VOTEQ4U2oyOVBjcDNBcHEwNklOdnZIcUlWOXJyM21HdHM0TUppYkZIOW9hRit1QmNiek1oNGxRZXNZNGdvckFJMGZBcXhGOG5wTGpvNWlIbzZ2ZVcvTUNXRVgzU0xlc3ZYZlhIOVM0alN4K3pqUHpOTEZldUQ4SlFzbHdEWFhMaFpIWWgwamJNK1RoT0lvc2JrR3ZzR1k4MnVxV3hlNTkrcnFNR3lGRWwxdVFIdng5VzEzbDFIUkhHb3VLRlZsbmRjaDZRVTJCcUFaSmpPcjlKWm4zSXZQUzVuT0hWNngrK3gxd040TnU4NWxwYTVWajBtYWVNWVVBUDVsdFdsb0EzVmRmaUt4dE9OQmsxY1lqZ2ovVHVoUkt3QnlOMVZLamxRV3RlSGQ0aWJJeDEwVktVbHNTMnZ6RXA4bDM0akN3UzJkUThUR2M5eU11Y2ZRZkU0VklEeVIzblJwaFM3TWJIZEMrbEdybmk0N0MwM1VVdDFVR0RrWTZWNDREdE10WFlFVXNPa3V1bEh3MWo2VFhSY3QxR0pCQUN0bEZUeFVWTmg5MWRscFNwU1B5TndUOHJoQWlMY2p5ZlNKcTFCYlV2UkxFU1JhVUhES3BzdGF0RnZ4SFBoRkF0NEtKaytCWTdUakdYckZPUU5nY1hEenJZcHl2T3VHV3psVjNlbnBkeGRRNXhLdzgvYVZzRWxLN2lQVGhxNmM1RXNiQWpHcjZHSWRmSUxpNTMxZ3RFWHozTCtzaStXRW5ZSHExeExrb0RWT3JaR3NwdnJyQ1N3RTdMaUFjSXIvNnZ6SzFlRmk0SXg1UXRiWEdJTnNiZFFObjdRS0tRa2RSN3hHODd5d0duUVA3d1V2akRXTkd2aUNWMWd2YkR3MEE2R3N5ckRLQ2k5eThQbThDQzhaVEtpRlRkMFExTlZyckwyZ2tWcDBZemVZSy9RQzZiOUljQWl5NE04elloUysvRFBzQUt4ZUlacVlIWHFIU1hMdS83bWZRZWJNRjBiZUJtLzhBTWhaTVJndVFXQlRSTzVYY3JqM1cxdmpFNlprVFhRUWtGa1haK0p3Mmxkb2UwWjd0SnY4QU0wNCtXcjBsRHBKQmVHQVl3d1c3TnpSVjJqa21xaXRVZWtDVFdwaU9tc3o0N3htdWxxMi9lVW0xNlptREtvRVp5SnJiY1RRRDE1VktMWE9NR1F6SDZWTDRRc0dyMmpVSVg3TXp6bnFmMWpzL1k4VC94QUFvRUFFQUFnSUNBZ0VFQWdNQkFRQUFBQUFCRVNFQU1VRlJZWEdCRUpHaHNjSHdJT0h4TU5ILzJnQUlBUUVBQVQ4aHh2amg4TWVPTFhzT2p2RENxUFVXb09jMkYwWFFZQlZZR0ljQmxkTnoza2NsRnBkNFp4dG1ZS0J5QnZzRzh0OVYrazVUOVB2OU04bms4bmg5WS9RWjZ6TDNjdkIxamRNMkJSMm56NENaYmtXNFRMNEZKZVZIemFKUXFLd1I0MG1NdDdINkFRSkdtVnVQREVTaHBkTVFDbHFyNmRkWS9Ubmovd0NmWUw0dk5maysvd0Rwamphc0xqdXp5U1NKRjNreDFRTEZ3TG91Q0FqSFpCakpWSm9tOUtEWlFlNGQ2RDB4eHlBc25JKythb3lBOFlFTHJlZHUybXlKRktIaXVKbnhuUlNJdUdZUkhabk1qTk1jZXVReEhySVpISTVEL0ErbjBoL29IbkRNcU5mVFd2aUVZbUR5UWZuS0ZPdDFpTnFSbzZpZWNKT2xhN0JLUnJoU0U3eVRGdk9xMFMrT0V5VzhDWDU1eXlBejFoMkt5aEhwRHdFTUxOQ1dVb3h6MWFSYWhibFFaRllRUmdZWmNVUUZTeEFRcHpYT2JmcWhhWHBObVQrcFRmOEFnZkJrRVo2ODBQWXNmZCtvUjBveGJIK0R4OU1NcGZZcUdpL0p2MW1pZnNoTUhuY1h6a1NJbGduTUUzTGpvZVlZSi93NTVsQ1hhaUVzYkN5aGcwdVdZN095cEFLM2tqRG5DQWM0R292STVwUDhtMGxIQlBaVEZ6OEdudWlSbkk1RFdVUmRSa1NHeE1FeVZoTTltYUg2Q0drN2M5MGh6KzBzL0xzQi9YckhySHovQUFHT0x3RUwvdkorN2xpUHB0aFovd0FoN3crc3VIVFNOdkdDTFluSEJ6YkFXaVpZdDZua1dGUjBwVk9UYUk1bmd0b05xelR6SDdtSXlCSG1NT0huK0ErYnhpVVlHS1FvSUhZMG14eVFHYUdDWlNFNjMxeGtGWW5mQUxtMGRUaDJNd2pDdEV2SkZETUdicnlGWEFHNGxwOEl5THEwdnhxZ05xaDV5ZHUweS9KeVBJNml5bHhnMmo1blBEKytNTm43NGRaZXZvL2JIUmdTaEgrRldHVGFxVUlibmlEZDV6aHZvL1AvQUo0RmM1TVJxMzVBU0Jreng3enR3Qk95Sjl4emtwOFRIQi9ocGZuYWZjZTJXNXpOVDVNQ2U4SERTNE5QU005WU03T21CeWk1R09UYzVEMW1aeHAwU1ZSYjJDYzFQZlpOMldkQ3JQbGs5SUZXRVhNRmlwTlo1aGNlZzEzU2E3eUNuUXVOczJOdGl4NlNZZFA3WURqQStUQkNBbGRZUGM4dmVlbVIrc1FHa0hPbXZNeXlZVzFNbTNTTmZWNW9Fd3JYWTlKQkE1Z09RSWh4UFRGaHZVWkhiOFduTjd1cGJ5V3dVSU5hWVRDeFBMTVlDNHY0bndmdmo0enV3MU4yNVMvS3k5Y1ozc2VQaGo1REtSMlRDMExKTUgwaEhrS3JZNmc4TW0raHEvVVQxMnEyemwzd1c3WHZscEJ4WVpPNTBJdVhyaEUvdFk0SmdobTNHR3dtL0dSNmZlTW1GN09jUG96K21OZTEvR0gwZlQ2Qk00RER0NThrMkhoU0E5ZzNocjFlSGlZUEtsZTVlc2o3b1ZicVVhSmNjRVlweHlrbVZMSWt2MndRZklOOGRNN01sZURoeDE2WWEvcWNnZjhBVEs2eVc5NEplNU1OdnN1L0ZId09OQmxkaUdqaDRmT0hEWC8yeVdoZldSdHF6Yk80MnZJK09ZTXp5N01hVVc1eWZRTW9hcUJXazBWTU9XVmVCc1dBRnlXS3dyTVdkVm5iaE5hdmRaK0ptTTFqSTVENkw1TUhmQm1uWnc0alhGc3BJQ1BGcnFJbmpMWW0yanBiaytDL2JLaVk1bjVZaHA1WGpGTUxoa2c1OHVSNFRQMkc3Y1BqNWU4anE1L3Q0TnkwRHFjekZsQitNanc0bVBsQXhtelJJQ1ZUc1l3Q0VEQTFJMXJON0xmYndmdmoxaFN3aThGcUFpOWlYdkE3R1BaWkUyb25GeEJqdGs5SFRIU0JyUGlqT0lhT00yREFhWkdSOUgra0V6NTZMOVBLWUxZaUw1SU9TV3k2OWMxMmdGRGxTT2gxT1NMbUlnRTBwR2tKU0xtUkY3c0RQRWgxQ2ZSQXlPR1Fta2NQR0JNOTJDaUZaQ09mV0RjdmZmMGZLUmtkZ1k1WHBQUGpKMkJ5dlNOMFpzdGNXN0UvQmpsWFBJckFKeWwyT05sU1lRZmpxWW82aHkzaEdoVW9NRWo3TWlwbkY2cUVuY0NCb2F6UzNscVZpWW1FY3NqTlhtZjBFdVVWR0o0d1NqQUdUOHlFRFNTWTI0WWE4V0RrK0NORG8zamN3VW81YVVzZEtOT1Y0OWMyUlBoMVBDczVYc014aXFUMmZ0a1ZBMHpKQlRWOTVBMDVxOGh0SGY4QXp4TnM5NUZmMVFWd2hmWnFwaWI4b1pmMEIyMWc1Y01TZ3VEcnRzTkVMY2xuRStNbEpCZzI0YzZBUXlUSHZBdGNDZFEyTjA3WnllVmtLNk51WHVSbXlKTHlqQkxRbW43WUcyeWY5dUQ3QnZIMFRpN3RMYXJ3QVZmNWNESndaVFo0MlhtZm95RG5yT3YrTjRWZG94ZVhqYzc3aWVhRzA3U0V3bVh3NmlFckZPWWpraFFvVEZTUWtrNXlQaUZHT1ZqTEl5bElrZExLQzdoU0NwN3Q1eVBXampyNDczWVk0U1RsYnN3Y0pqZ0g1Y0hVQWtQZ3g1S3BEdndCSUMzSGVzbHgzR296SVM1RTdkWTJZYkhDSUhTRUlNTmorV3RSd2ZsOG1TVHkrY0lUOTgxeXFYODdDTmRHUVNJbjFUcDRobmp4MnJPUXFyUkFmZHRGd1dUenN2aU5QR0Q0aUovZmllMzRuSjVIYUNHZUl2b29QT1RmSUliSXEwMHpXZkNpaWRLelg3eFVjb0E2T2JmR3ZETDdaaUVUOGlSTWtHSEEyQU5vYlpTdThyTllpZzBnQXRvc0w3WElLdDhabVltS0doMlFtS001SE5YeWVPY0ppN2xhV09KWWhmejQwbWtsMWNUWm9ESzEwYmNnZXJKTVhxRzhZYzhYSWlpcmxOVVpMeGpBV1JlTlhHR3FDcGV5b3c2OXFwNEg5NU5xSUZMQWt3MDR1WENDVXVnT3NrQjRiSGNFeEJNWU5SeWJnUzlYcDhZRTdkRHdUZnEzb2NyQURzWW9uTnpuOU1qS3FIeGVMeW9MS3Z5emxQVDlwcmVYSjhSNVZYV0tIUExJNWRkOFcreVp4VXhlaE1nRFZYcm5XbnJCdWlPanVQbWVpemlvdVliNVM3NHdZR0hDTWxCMFRnRU8yTElKNXE4SnB2U3BrNk5QNk1hWWd3QVE0YXlkZ0ZvOTg0Lzl5cHFFNm5nZ0xLZUtnR0RNV2kyV0lJQkFRcFlZR1ZaVTJ3a2Rrc09qOGdqRkVNTjVKdXdsVGcydCs4bDVIZGhZNVNBZU1iTEFpaFNWdU1GU3BIa0ZPanEvZkRQOE9ZWFV6Z1duMnVBTCszR1dCSWZHV0hUWDdZTXZJNXMrK3V4VWl6K3VRTUljR1lCQnRvUnlWdUtra1JSbmI4QXZ6Nnk2OTN3Z2lyckRUY3VzaEI2TlJzWWZPUDZmNFEvT1J4ZTJNdkJvNlpiek1yRTVVZUVaRUdpTEVISHdpYzNScnNjS0ZJREZEYldoMUo2eTl2ZUlqTzRaNVpHeFpLbllIU2VNcWN3SnlsQ0x5eDdJQ0ZyWlp4bW1MaVlvVmRQeGtyVVJyQ1FPb0lhdzVLcXVRZ013MHM2eXQxK0tpQUtScGdzZUJCSTBOdk9OaHBSd2VHVUJBYnVlVlhyZ0JCY3VtbGVGTjVSamJDU0ZVVkxQUmxBbHQ1QUpaVEthV0F1YWVYR241SUhJSlJZYUZzazVYeTNIbkxWa2lqTVBZeXhUR1hFaTZnVVIwK1RTOTUzVDIzQkdZdW5EV05ockVJZlRoRnFtYW9HeXVKSDdZV1lqc25VMUp5anU4a0g1elIrNWZqRlRKVGU0QmsxK01BUlZ3QzVBNm5GZ0FnR0JUNHZPU0VVNVVyenVJeVlyZ2J0dGltUXBnSVJrdmY4QStaSFdqaWtBSWZUT2ZQb2dVbjc0bldsMkU3UGVNTEZZeENFZFBLVDg1M1AzL3ZGS2RsWU1CNmlLZm9RQlYwWTQ0bTZKYTNIMk1ta216SkErS2t4WUNZYnN2bngrc3VzbWtDcDRVWUV4U2VLZ2NQYzhtU3ZWa3d6TmV6ZkdHQlkyNFJpb3E4VnJCdmd6bnV3Q0ZpTUNaTy8xa3JieUppK0psN09NSUY3S0oyUUhrYzREWXBaMDd3ckhLR2EwWSthd1V2OEFnUlQ5YkNKU3ExZVduMWl1VWFZRkZ0UEt5bkcyT1FHOW9KOTJRK0VTdERFWUJabTBCekVrMWpUb0lUK1Y0MWhISks4aU5CL09JQjg2S3Ricnlac3lKTDFsUVczS1d4ZG0ydHR0WXRCMWk4eDlRUGpyQzFRZzdFTGZIZkdNdWUyTUZGMHNDWTlaZmJkYVpPT2p2RktnRmpNU3ZkUDZ6UVRGeXdOejVRUDN3VDNZR3BSMHgrN2tKTmhqUklSNEl3VUw3NkNIc25HNVVPYm83SWoxZzF3ekFzM1gzeEd5Z0N1Zm5mOEF2STBsOGJvdis4ZVp1VnpPZStUNXc0MitseW80QTVDVmpNSUo5dE9NUzNsdWdpRzczcXNqQmRBSm5iaDBYODVlNmtnVDJDTGF3anFiSzBMd05QbktOeTRPT1VtQldvTzlheUdDeUNtcVJvdjhPRUpTRXpNK2ZHQ2xVTmpmWmsxSkNJUjNPWU1tMW9vdGo0RVBPWEJvNDBISnFUV3NTb1M4SU4vWUJrOFNzVWRiRkozaE1LaTR1SjVieVZsZ0hvckI0dzRBaUh0bUtDY1BYeW5qRk9HSTZrdExOaFM2eHdaaHV4V1BrdVJuWG9ZSEhlUVJrVTJLZkxqR3UvazZyYmVXM0U3Zy9ia2hKcDhJZjVnd29aeERSQWxPTWpqa1NmM25Oang2UUk1OFpBMjRDcXBpdmJLei9EZW41WTMzSUlHM2d4L0tWcnBjRC9NemYvL0VBQ2NRQVFFQUFnTUFBZ0lDQWdNQkFRQUFBQUVSQUNFeFFWRmhjWUdSRUxHaHdTRFI0ZkR4LzlvQUNBRUJBQUUvRUpSaGxmdzYyWXBXTXdBbC93RGRmOVBuRUVpSG1wVTFTdWh3MWJvWGZKVmRuS0J5d2svS1BQTXdZcGF5UHYwb0RiQkFGSXd2SmpER0xuQndJcGprQjZ5QnhFeFdGQnAvNHFxVGJqeXhRNEdKbU5FQTFjQUZhVWVWbjRmMnVBQzgxNGQ0dDIwanZKcHRnVTZSekZRdVlNUzUvc0JhSXFrN1IxQm9HQWVON29uYVNGVElNTXAzTnZrQ21pVE9DWXNVeUV4MVpqS3d4WkVNcUxyR2ZNa3lmK0VTOUdhamk4VjJNMkJNaE9YazBIK3Y3ZldReHUwZHV6eXFTeUMwcG9NLzJzMGhhSzBTaGdMeTFIaVdKRnFjcTg1aXRBSEdybi9QaEJzTWlPanlKQk1CdENVT0lHUWRPQU1BM0JGRWd5dnE4Q0ozUTJwTmxCWmp6RGlCWW1KN3dreEdYM0dmRWZ5dmtaT1Q4ZnJGbVQ0eElPYVRkUXZYdjhQN1RJSmpnZWNLTW5EN0c3QlJtMVdhRGlvOXBXQk1zRFZwR3FNQ1IzdEVjQkNkeUdndzdtWnR0dWluV1p3YkNVdDM5YVVOcExYZ1pIVUE0ckMyR2dZbGo2NnJtYWh2WUNHS3NlTkNwSVNKZ3N1SjRadlBvT0o5bU9TNjZ4ZU95UDRVR0Y1c2tZODUrVXY4VnlaZ21ML1V3NDRjdkZoWFBDSFI2ZEQrSHdZcGtTNXVldDNHTzNEYzZHdjhkMWFpN0JWUmN0OENIcU5UTFVWcmlHQlhTQW53Y1ZUUHJRTkRFa2QyMTBSV3RLa1VRMEpob0pjSjBUeXI1bTVTcHBVZ1VOR016WUNwb0dsT2RyVnA1WEkwTks3V0VHbEtwVkZEWEE0Sm5RRWYwVFRrcndaVDB1ZEorM0dzOHgvd0VXTmg1ZjdqUTRSWS9ELzkxKzhHdnZEVDhBZjg1TVJkMnQ5Mk9Id0oxanJnY0ZyTHp3UFAwT3ZtdUF1aTRtWU1zTUV0TmE0NHdGU0Nwc0xIT1RaQW5EQUVhMHNzUlBFVHZiUlVoekhVM0VWeUFwRU51aEp5RVBUVFhsd0VJRXBxczY3VmJnYmJQY1hLM0czRmtZUVM0OHhEb1k3SkRTM0tISDMxVjFEQUxsdDhMakxlcXJleTJaRGRnd1E4L1hwVnhOTEo0eGh5cWg3bzFJaDBnVkJjL3dEMU9hMlR6bndCK2pFaFFoaDc1SVVSOFIyWlBuOVBCby9UNVlvL1N2OEFYOEhXNG9KZm8rM0I5Y2FBTTdZdVA4Tm9JVGpvQ3JhQnk2SENXL3BkbXNqMUtNWVVvR3ZXYWxqZDk2clVGQTRPdEVpa0JJN2EzK0FCaHlpakowaEUvWmlJM0F3ZkFjSU9XcG9mWGNtSGs2QlgwUkhGSDRqemN1aEJ4dURtUUlpZmJFUTdRWVlJWVFTL1laNHFneUhMYUJoYk1UV2dKelo2YUpPU2c0SzRnZ2o4QVpYTGVrREtQNnJSYWdMTWU1SlF4UUY1Nnd0UUFZdThMK0VKOUZEditqaGpod0ZFeHRvMEhyLzBkNUlEUnJFVmtWY01sam0zdVhtdVlTUTc2V0hzTVk5QzEycWlsbnVLWFl4UXZBdDBqYTIwUmNKZ3VtK1doODNlZXpOKytTenhWbndtTXdFMllHT2VkQWNxNk1ZY2gvOEFMMFZKYzFoZzlING5idHhVRnVQR0RxVFhBaHNwZjlLb1k0L29JdDRUOEl6bnF6Z2tJQkpPVHBFd3dxZm91d2IwNWVVamNMbWhNaG1ZTnI0RjBjdWVnQmlXR2sxNFNYTm0zOHdoUmVtR2pWSzRId0lzRmpqeWplVFM3Y2tWRWZUSGcyU2hVL0M1UlZoLzhKY25HUGxzSGgvQ1Rsb244Q3Z1cGNJV292UXY2NVhEbGhpOC9lQU5BV2hHN0NZTVZEdWhEQUJZb0ZMVVQvY1NpcWRxVXVVNEpZRnRvQ2ZFbXVzV1c2YXVrMlVOYTVEcmtPODROREowNTd3bGs4VElnajhlRmNNcWVvcVl1ZXY3M20rclFaUDUxNE1rS2VyM1RXQ2M5S21lbHYzTTRsaXVleHhPSmlYY2V4eDNxWXkzcVU5Y1dtb3d5Vm5lbjZJdksrWUUxa1lza2ZhM1ZQb3lqOXhhSk96WFFHOVlBRThoL3JqRGtMaGtGbjB3ZFlnM2hyNWMzK0h4US8xL2I4Y1kvYkFpQmpGd1RuUkRZdzVZYTdjMnd2S2tTaGdsNVF1YlplNkx3VGxCREVCc0lZMEFJdFFCWm13Rlg2UDQ4eDN6T1Z0K3ptbDgvd0Q0SDNnMlk1LzBOeDRFSXNGajhNblRwK01TRzNwMTc0WWZkZnB5VXJ2ZzJqbVpmOVlRczVMTGdsR2lVeml4TDd3dm9FM09ZZEhJTTQ0YVR2aEdEcXE3eE9KdllqcURUSXgrMlRNSkNycUh3QmRNZkRTYXlFaDV5S2dueGhGeE9UaGk2VkxiTmQ3ZU1GN3RyeEgxaTVNcytObGNOZHlnUE9aREZjYmxlVGtyVVVyYkhJdW5GVS9hRUUyb1ZHUnRXaDhBY1FnQ1VacGNoWElIdzV3ckNtd0kyZ29XRnpXK3ZXV29RaHl5bmtMbm0vbGpnQU8zSWtkQkdUNWFoQW9ya2syYkV1MFRqQzY0NXpVdzRoaWowZTd6TENLb29BNlFLOC9UWExRU2JtZzVMRW4yeUZPejZRQWdDZ2wwM2t2cEp2Z2Fnck9JTXQyTG9YVkM3OEUyT3E0V054Y0FIYmswQlQ5ZitqUCsyYys4WlJFRUZ5cTZENWNlYnB1Zm95RzZMNGh5ZmpSQlBTVG9raEFMTWxvTFFUZlZwUlZMTGJSdHpJRXhzUE9sWXdtRElkc20ydXNoU1FyQUdKa05JbEh0YVFXREN1NXQ0UHd1WGp2bkF0c3NmbG5WNzJpS0hTaWF4T0tqblFBejl6S2lqb3dIY0FRSzdXR1RaUHJ4TEM2WkJKcXdONWR6Slo1TTlOWlZRS1lOanN5NEVCdkZiaGxLUU8ra2FrbzBtZTBaYjRGaWxVaWtFcmFpVnNSdnhHUTVMRkNpQXpIWUZrRjlQNGNZTVI0VVBvZFpjaEtKWmtyOFpaaFlRRUJMYVgwMWlxZzZmNFlIZEJzcHZpa2hycHd2OW5WeVBlZFFKSFN3bVY4a0ZRbFZVTm9SQ1VnMUVONjVxTU9oaWF4eDVOYU96bm1iT2hnZ2hSeFEwamJnMUxqT0dqZGRKYzBDaW9YemZuUm1yNzBraXRCNWNEVXYwNTY3Z1l0V2hxcHZCdzE4WlcyTHJvTERFOVZjaWVCQkhrQ0ZoTU91TTg4QUFSVnNTNFFIV1dnb0E2eEpQaXdXdlJDVzRKSzZhc3FYR0RzKzZTa2x1QkYyWVNGVzhaZ2RBb1RGNW1ZL0hYbitjaFBsMG01b2w3RXphTXg0SlVZRFYyMDhzMWpucHdzMmxXcmdKZ0xUeWJWbzZvQ2crUmdBY00xZU5FUVdnQlBjV3BnS05SRUFWTlBZZzQzWEFCREEwQTFEYlljRDhhSGVMVnR4OEdRRUFxSzloZThpSUExUzBxc2Mvd0ErZ09SU3FPbTRUK0xkN05aRm02ZE5NVE9NT1ZLMEd3d1g5cEROUmZpZVpWWmpiREsvaFFtbjNHV0Y3VFRNMlRyR2RLZ0NweHlqOGF4Z1I4bW9NRlEwU0tyRjNndU9LZkNwRlRYYkZRZ1JOemRURjhBdzRDTGE4WS9HRFBVcXpxUUF3ZW1jWjBTOG1rUkZMeGdCNE5ETzhLRVdLUXVNMlZlRTlSMjlIaVNHQ1hVZDJmUUlmb3lVbmlQM3dRVGRHSHFtUmRuNWNtaDhZOWZUQkZIVGZFcFg5NDN0SkFIZ0NIMjd3MjdGdVdPdjdNUU1DUjBUYlpQMmJ4VzJrS1pobG9kMkh0NDY1em5xWkREb1d4RXdsY1AwaDNkVW9yUUl6SytLYmNXRkhyVGppWnF4NXNMNGtMRjBPUFUyeUxXSUIwcHhpUUhHQmtOYk5GSDdjTGZxUWZ1MDQwSC9BTjhPclRMSWgweGVndVNPYlRhUGQvR0JrUlBlUEMyUEFNWGdCV0VCVWx0UmdDeHdOajFaU3BlRHNoaXB1VWR2b2RqM2N4V210VFZOdmxBSGJFQmR1VUUyMEVVcHBnQlFzNFVnZkRSVVppZWRtZ1VwNmRNMUNzWDdqTmhXdFJXWWorNUczNXhNQjBTK252dkMxenU2ZzZNNHRBTHZNZC92RUVFMTFPR21OeHhWVEtZQ25PdnBnRWRmeVdJV2xSSjRsaEl2ZWtxWlc3dW9WSUdEQ2xJTk5rQ2xWc2RyRmd4R2swZTJSRkVDaExqb1NKVC9BSFJKQy9GeDdTNlRYRTdVaUp1c29QZ2lwZFBvb1lFZGZaYkthaWpmU1kzbmt5U2lMcFI2NndyVHBuV29FYWs3S3hvdDYreEFSTGFsR1M0UEhCekZRRm90WWxmQ1RDbEE2QThtRkJKY1NwY1NNd3FMaTY0N0FxS2RLVEhTTWVwNXNZQkZiUlV3N1Y4QUxnQVZoeVV4eFpCWWFhQ2sxaFZjc2NCYVlhT3RZM1lRS05RcGNsZjNsT0NjN1BaS0RCa0hKTFZHdG11RHRLS1kzbW9yS3RGUlhnbUVxdWJrQXZLdGJCMW1jZXZMU0xCYkZDNDlxOHVoU1ZWRDdkSW8xT0hJUnJFcWtGakxqS0p5eGdQWlNTVW96M3JnNHFoWUF1cHlEVTFqRjN1OElXUUc4YkNZV2N0VE5BSnlnWG5iTC93bmhxb0NDbXRaZ3MyWWl6STBpbThjQVcxTWxYdnpNZFJNZElldnVHRUpEV0EyalRTeDNqbTMxUkNJaUVqSDJBYng4M1dnZlJnZldTaHRHaTk5WTIyNTA1UzVnaEhTT0hsTFNYbnBKUXNtR3N0blhlcmFYZ2ExMFk4eS9PNmFVVWdsY1JBcENWVU10MzN3RCtYR3BFaUprc0Y0WEMwVGRNcnJwZW9NYWhGak9LVVNIVUxON3dzbGkzYmYxblF2R09Ta0s3V0UrcW5NV1FaRUtXdVFlczJaSnFNbmx3Z1JYUEpNcE82VG1vQUtpem8xbThUeWdYekNFMVlobEM5d3M4bFdDVHhwa05hZVFRSTByeG96bG1IMC93Q2dRSUJlS200WVNXQSt6YThwcjRtQnBNaG9CMmkxL3dBWjUvcUpjQTJnNTBpT09RNGFLdkNQMnhsb1BKQ2JQQkFuT0o3UkloNjBXbElnbU80ekV4NWtyVktIc3hzT01sMUFOQzF3dDk3eldteFlEcml4SUlRYWZJcjZwaXRUMzlnUklxc0dramNKL2dnSmhOQ2krVnlyakNSWWZNSHZjTmJEV3FDSnU0REl2ZzZDU29zUnNjdGVyMUlDSVMzTlR4K1RFMGlSU1BtUWszQTFGVjBDSlplcU9QQlZpWXpKT1NuSURubGNIaHhoQXBaT29JNjV3c2w0d2puVjArdElXOENVSUt6SnZERDZNSVpUTVNUQ2FZR2R5NDdnN2UxUlFRRlZUaTBtRnNnWnJpTjdtTVV6TzN2SlY3UlBRWTgwdXdpQ0hmSEM4emVQOGlJaU9CYUdpODRKRFhYSWFNbXR4RHR3Zy9BMDBtaEZuTUxoRldoOVpVTnNOSzB3dDhoQjFZWFJiVGw0VENxOFdzQnNCQmExZmNsQy93QUFDSXR5ZWxwM2o2U0s4Q3Bucmo1ajZyTkdHTjYvZ0x0b1JIbXg0SFdHelozVUIzVWZqaS9BL3dBcFMwUUtlZFRIaG5JZ2pSUUtCY2JkcGJUMDFRMDBNaXdsVDhZaG82TittU0lEZEVoQ3FjQlV3cythcVltQVdGZmd3U2UvN28yZjVISXFWRklFTk4zT3lMQXlKN0c0TjJBRkJMZ1RiWEM2aWVUZGhUQ2I1ck9DQjRmcHd2OEFkMkE4ZzdsZDd5SSt0U0FYdkVjb09zQUk1TnJyRGNjWmVWRUt2dUVkY3FvdStnVExnc0pqZ3JDT2c0YVhwdmI1TktEUXpMVERNWVhJTnlmT3lFNUo3ajZTQWhxUnJ3c09CdVBnbzZ4TUozU3N4YWZLRGh6Y0pValYzMnhXNVdDK2JILy94QUFsRVFBQ0FnSUNBZ0lEQVFFQkFBQUFBQUFCQWdBREJCSUZFUk1oQmhRaUl6RVFGVEwvMmdBSUFRSUJBUWdBREdienViRVJXaU5Ndk5wb3FZTnlPTlU5aVpGdURibTBZdm15Y0xJeHI2TjZBeEVWekE1Z2N3T1p2QTgzZ2ViR2JHYkdiR2JHZG1CcHVZSGdjeFhoZnIrY25qVlpJUWsxdmtaeHVtSG5ybXQ0Yk9PNnczYXF0TTFGT3RnZUI0TElIbTVnZWJtYm1ibWJtYm1ibWJtYm1CeE5wdUlIZ2ZydUIrcG1ZdGQ3Q3cyT1V2SGsweHNyRTJDbkt4Y2I5ZFdUWG5qb1VYM0FBeXJOcGZzUVBBOEZnbmtFRGdqdWJUZWJUYWJ6YWJRUE40SGl0TjV2MUxTSEhSZXp1NGQxMUpWVHZFTmxMa3k2aEw3Z1M3NWVBK3pvSzhyS1M2NFpGdFZnVmt1VnY1dE5vSDZnc25rRThnZ2VienlnZjN6emJxYlJXTTNtODNsMWhBN0dkemRGTElxWU9WNU1RQjZuWFhvZldTODlqTFpxMURGOFlaekxlMXR0OUtqeUxjbG9GdFl5U0c2WlhCSFkyZ2NDQ3dUeUdCbm14SWk5ZHphYlRhYmFpQjRHbThlNnN2cE1yaDhLekwrd3oyMlU0SmF2Z2NqTXNyM3lUY0NPbERTeXRiRTZqaTFRSTZKWmFDb3ZhZ0ZiRnlyYmtWNlJiNzZJUFVEQXpzUU9CTjV0QzVteGdhYm1CcHRONSt2YnMzQzRIdEZKWEhEeFFPdlFib1JiSmZuSlFWRFYzMTJyc2kwcFllNFQ0dHRTaHRYZW9aVmxMRTJwYWRBVUZvbThSd0FRUElaNUp2QTgzTTJNM01EVGVFYkRzTWZ3OVl4SnFVdHQxQlpOekI3VTdjWlNqbzdBNWVaV29EMVpsTjNZVmxEcDdJWVB0QWpWanREZDJPc2hiUjBZbHFrZXR6QlpOak5wdE5wdEEvVURkeEhOZnVXUCtuczFFQ3NBN0h1YlFPUkE1Q21jWCtEV0NLNE10cnB1WHB0c2loZWtUa0tFQTJaL0tlMmVuOERPL0dTVVA1OUZ4YTNVMkUzRTJtMDJtMDJnY0NBaGdBWGR4WE8rd0FDL2NEZTRHSFVMZmdaeGJkV1c5QnVvTEdNQ0p2dE93UjBiM09JbTFhNVZncU82T2x2NUtQWTlxRHI2T3dNM003TTJuWWdiMU81MllXNlVDTVNhZElyNkwwRUxCQjN0Tm8xblNHY1prdjhBYWRJSDZtNW04MlBVemp0VjJGYnYxUG85cnRNL0xQSEtHZXE1ZWdySExKc1lRWkRIc0QvTzV0TzRXNjdFMkE5RnRQcWVnd1A4QjZYcUYxVmU1dExQYW1ZWGFaSkI3bTBEVHlIK1RPcHRUR1pqajEyZGRpeXl3b2RjUkh6c1BlN2k4UER0TjR0eWE2YUcvU2NpdFA3L0FBKy82SjBmOFhxTTJyanF3YmQyRHZ2SDZsdlpJQjNHZ20zU0dCK3hBU1I3cklHYk94RlZuL2dlc0gwdHRaSFRaN2o2aFJlSHFCdzFOejNjWFU1SzhXdDVvS3JoMFpOT1hjQ0JrUGJxYzNEVHhoZ2VvTlNmYkNrSDhTQjJSQlV4WGFXa3JrSW9aeUYxZ2J2RmlmclVGaXhzVHNBdjFFYzlkSHlBZW92cksybE5OMTltaVZjVmw5NksvR3BUbmhiRytsUW42cy9EeXFhL3p4S1dmRUJML1hvQUJ3Q2JCWldjVEl2cDVNc21IZmszdHEzeVBET1B4aFlIL1A1RC93Q3AyMFBmMmtKYlZsN0NNUlJBTng2cUI4alFici9hY2ZKdFA0clE1eVdRNDYwZmRIbFRqcXJVSGp4YWZwdVZIS1YwNG5KRUQvdHZTdXRPYnpGbVJpc2h3bmQwQU9mam8yT0NlR0ZRNUhJcFY2N2Eva2hSZVVITThjZnk1akJwcTRreHFpQkJVMEZYZjk4WDVtQ3N6eDkzeGlvaFhaQUkxWkFtQ3h0czZsaWRBR1lsajFudU0vbHlXN1lEemR3R2Q5bVhyWVhsR01qMHM3Wkp3SzhSMVRHdnhxdU9lczM1MXVRR0MwWmVUUy9tcnZ6c3E3Sjg5bDJkblpLOU9OOGwrbVBGcVgxamNaeVZ0M2pDY2RsUGFhNVh4cjF1NWJKeDdxUWV1N1Y1QXJaWnh0ZHl2WkhvWVdvbGR5T0tUNWVQUUc4Qk9Tdzdhc2JjWStDWFYra3dzcHVUZElmWVl5akFzc3FWcFh4N2wzVVoxVFkxNFV0eDF2MTJjSmhHM0E4b3c4VTNZNzlIekxZVmVtczJYcWd6T055TVBJUmJLK0d6TXZpUHMxOGY4U2ZPdzB2TlZocHpmdDE1M0w0bkg4c1Z2TkdiYnpGbFZBcDN5Y1N3NTJMalY0VnIzTmJWZnpGbGxWMk1LYjdSVmRiaHJ5ZEhuNTNpTU1WV0d2NC9VcHpuMytVVjRTOFlvcjRSOE43TXlKbDJINUorbXVvK2R3T054TFd3Y2JyaEtySDVQUFdmS2NjMTU2eW5GeDh6anc4K1A0V05rL0dpOW54MUtEaDNtdzEwWDRwZGNUb1pTay9JTWQ2TDZpZmpwc2ZpTWlrZkhzaktiaWtDNWVTK0ZuVXNmazFkZkxjczk5U1pQSTQrVmQ5dkF1UjhqR2FjMXlsVi9IMzF0V0Z4N2VsLzdQRktXcEYvSlU4eFYwRjVLdC9qejR6Y2RrMEpZZHVSK1hZZkw4VUtNYmhPZitxTC9zNFBMdFJsL1p0ZkpXdTgyTGgvS3FLMHh5TUQ1RXVIeU9RN2M1a3ZsMmgyd00ybmo5clp3L3lHdmpzRjZEaThrTUtwcXlIeHlHSzBzVXMyR2J6MlZ5ckR6NC9OVTBoL0RUOGg1WEJ4eFhpUG01TEVzRGxaanQyLzJzbFNTcVgzanN5KysrNGRRNU5WcmFLTWVwU0dnZkV4c1psWHlvdFJJcTB4d0dzcHZ4OHdsS2VrclpwNVFCMExzNUE2SUVlb0wwUEtBZlZtVGJhd0xCbjcyYnlqcUN4ZSt6NWxiM0JaMFo1RVBxQjZoK0lVei8vRUFETVJBQUVEQXdJREJnUUdBd0VBQUFBQUFBRUFBaEVoTVVFU1VRTmhjUkFnSW9HeHdUQ1JvZEVUTWtCQ1V2QUVjdUdDLzlvQUNBRUNBUWsvQU84NEFxaFpVMU14eU8rVU5YQ2dHbjVnTEFrQy9xaUMzbCttSGlCRmZxZm9FWlpFZ0d4aTlOaXBET0grMjJxcGduZUFoQUpKRnZNR3E4SnBmbitsY1JIT2g2aEVDQlRhOXVRVFJGZVI2RDdwL3dDSkZnVDRnUk1SdUl3Y0ZHcEVFUkJIa2lMd1J6SDM5MFlJcEIvUkhzd0krdENocGtWVFpCQXFQc20zb0RNR2s1dW02Mm1MRHhVbStDb0ptMzhUaWVaVmo4MGZnbjRCc2dTL05hUWJDTndyRVU2SHNrSGNicVhhWXRQb2lXd1JFWDZuMWpDQmVNRVhubVBzc1greC9RbXRFenhSRXlteTVvTURtRUNKM3VyZGhqb3BNRUtoZ3pIOXI1b1RFZUllNFJnV25mb0ZUNDEwUjBLQnQzQVlpWkZZNjVUcEhKWXlxanBWU3c3eGM5UGREekZvM0l3cXoySDRkd3JyUGNxVVNIYWpVVS80VUE0RGFoOHhaR294WS9KYnEwUWpwZHRneldvUmpwWldSK0hTdmVPVDdkZ1R0UW14djgwQ0hjN2Y5VlNxMXlqSExFcWhIOXVqOEU4NVcvZTNQYmZzTVNRSXhVcHZtRlg0ZkpGR1VlNEtHdmR3UjZoV1FJTzRRa0V4U2h5akZBZWRVMG1JUStBYzk3YnV0SUErNnFxdUZnS1Ixd25TQWVWeXVJS09wS2ZQSW9RZTl1UFZHaXRQdDJIbDNOdTB5VTI2ZVNKeXAwK3FZU2VxaTVvcnRpNG5DRW42SWlockhROXRlMHlEMkRLcVlSN0IyaXFjMzVncDRjM1VBVC81SjlvVENYV25DSGhUb0ZRQldzYzdCVkpxbWsrSTJGa0NaQkJtdHNubXVDNG5rejNYRERIRWlKL01hOTByQUt1cnoyYkQxUVRaQlZJRXFvaEhTWXc2eTQxZWducmxQL0VhQzA3WG1iYkprRGJWS2JFcW9HT3E0WmJmTWcwSlV0L0s1dFFMdHluUVM2L1VMakhSRjJuRWdXODF4dGZFa1hNbS9lMjdjTGIwVzZGdklxMGU2MjdDdVgwSzRnYnBGamM5SVR0Wk5xRVF1Q0hQTXc0bTNRTGhodGg0UkdDUHFEWGRPMHVJQW5NQVVUcGZ2bFBrSTFpYW5DdnROZmtnR1ozSkFNY29RQTBnU2Vxa3lZK1M0Wkkzb0UyMTQ2SnNUQm10MDJxYkJqK3dxbU4rYUVWSHF2MmtqelJwcEJpUGRZQitnS0ZDQWZtaFlvS3dDTWdpVUpScHBhVnVJUWpVS0p3MENaM3BJSzRnRWpaY1BVQTBDU1pjV25pRURTRDc0QzRyT0hEUUFYWkpkWUFWbUJNSndjQ0cxa05INVo1bE4xT2R4T0xRbW9paWUxdWdqT0pGVEs0Z2N4eE1PRmlJeHlYRklicU9CV0NCS2NXTW1DU0EzSkFQT1UrUnc3bVJjMkFqbFVwd2hyWjYrSVdWU1hzdDFUQ1k0amlMUlVDbmxDNFFPb2FZTjQ5RU1QOEFSeUFxeHZuUlJMWEEyNmhaSHVRdE0vaGt3RG15STFEWG1EU1lRbzBBK3F1R0VYL2lRdHg2b0NETVFab25FQUUwNUZPb0UzVXdONFdxSjFDWFNLWnpKVFFCcThCT0hOSXJzaU9NV2l2N1p2QW9SSmlndEpRRXQ0am9iSkZ5SW5rdE90M0VGQi9FZXhRRUFHMUFLNENCY1hnNlp2RkRqa0Q1bE5qUVMwbTlkeGlRZ1E4T0ZTUlhZcThVUFBVUFFCVWN4MnhGalEyajVGT0FjOHlZQnlJa2NwaTZmWUVUQU9hZlJHaG1QT1U5OE1iRHJWSUg3UVZxSjRzUVFiR1NmRnVGbVlyT1poR3JtbHNkY3IvSER5U2FubnRUQzRZZCtKQS8xNWhYSU4raTNCVEd0RGEwbnp1blE1d2p5Q2NOUE1UVk9nbUs1RVdqb3FpOGttU2NtMXpsVUJ4Mk9nQ01UWlBreEJwQ0FrQ0FZc2pCTmFiMnYwQ3pDbVJzbWx1OFFoVW9VUXVoQXdnc0ozWVNqMkZEcy8vRUFDa1JBQUlDQWdJQ0FnTUFBZ0lEQUFBQUFBRUNBQU1FRVFVU0V5RUdNUkFVSWlCQkZUSWpKRUwvMmdBSUFRTUJBUWdBSU02ejNQY0t6ck1iSHNzY2F4clhGYlZqSng4Zkp0OFZkbEQwTVVjQ2RaMU0wWm82bWorUGMwVDk2TTBab3pSbWpBRCtOQ2FFMFpxRkpoV3RRV01VaXZGNlN6RzhCOGk1UmE4ZDM4REViU2FtdndGbldEODZtaE5DYUUrdndBZnhvUWdiL0dQYTZxVUNCVFFlaStXbTBDYXJ2dS90cW54L3AwUXNSTE1heGZZMU92NDFPczZRcm9UUW1oTkNkWjFNMU5HYW1vRmhXVi93MjRGSXBPdksxbG5XUDFzSHF0dWlFQU5qNUM5Rkl0eDZXVlBHanFURFc2L2ZXZFoxbldkWjFFNmlkUitPamEzT3M2aWFFQSs0RjlRckFBSWxCZWhyRnAyd0ZnS25abG1TMUFJR01GTEdMYTJNcHJXdEtiUjZOYjFnMXNhZXk3WHFRZEhyT3NLK3AwTTZRejdINTZtQVRyT3BtcDRYSTNFeGFpL2ttQldsZVVBK2RYV0NRbGVNUXhaNVNTakV4QXAzRWRrVWlMV0xXN0R4Q3BpdHB4dTIyaW9GM09zNm1GVE9zNndMTkNFZXZYV2RacWFNL3JYcXZ4NjBSdHI5UTcyZDZoU1krSzl5c3kyVXZTM1YydUZRS2l0ZStpeVcrUDhBbHZBbGczVzY3YlQ2bWpDTnpyQWs2enFKMUUwSm9UUW1vakZkaUlnK3pjb1c1dXVwMTlibW8yaG9KbDVEMUJFZ3hzRjJKV3pHdXA5dUdLdVp0ZGFuY09kTUtocnRVS3lUR1RyOTZFVmRqMTBNNnpyT3BuVXpxWjFnWCtTWXZzNkRqK3lRUnZmNDZpRVROT3drMUtycktqL0pYSHVPN0h3TDNKNlZwNHhvSStuaTZiL3NEb0VLYUs1MTE5QVRRbWhOR2FNMFpvd0lSN2xhamZvNkxOR1RSMFB4L3FaMndsWmdHL3NJczhqK1BwTkVIWXBQbmJyY2FLbU8wTlRvZEVqWDQwSUJxYU0wZjhTTmpVb1QzQXFuY3VJNy93QTYyWjFsZGZaaE9Ud2tUQ1N3ZFJPczZ6cUppS1BMQUJvN3J5aWg2eXFxcklCQXlNQzZoaEJpQURVS1VBRXIvaHFMVnRDeGIxclZMV084Q2paakRhZ2hLM2M2QUVxSHNUa2wzeG9hYW5XZFlLMi8xaU92N0M2Y3A3QnFGUVB1MjFNYTRpdk95Y2dHdGxxdDhxNmNVTysrb21wb1RxVHN6US8waUUxdG9BNzBhMTZuY1ZSb3pycXJjVFFiY0lBTVQ3RXpBQnhjNm1Dc2dIc2xZLzhBcGlWOXJndDVjenMzTGJHZTRRVjVML2VYMThuWTNQVytQVzAycURZeEx2N0lPb1JvUkZMamJGTjFtYUc5VEhwUnNWMklRa2JnSFJqTzVaZXBmU0tRU0svOU5XaGJZU2x2c1pnM3h4ak5XaTdaOHhBTmxNaXkyamFxTG5QOThmZFNieHJQNmZ2RXpvUzVNdElSMVlaRlZWdkZqZDYxVjdNNGUveVpvMTBncWkxd3BwQkFDUHBQV0cwRy9xTWc3bWFFdS9yRkVDSS8vVnpVQU5yNnBCWEw3bmpHNkhLc3JKNzJXRzVOekFMM1ltMkdCVlllejRPQ2xPU3JEa2FSNTl5aDI4ckFaNWNZMWRqWTcxUDhkYXhzQzNqTTcwbkY1bVJmeUlFQ2dDYUUxT244Q2RKMjFSMW0xRUEweGdRRXk1T2xSRXE5Tkxha2NSVTYwNkZtL3dCTXJGUnRRVnVZcWdEcWJmOEF4TUFNV3JLT1NyblBvdnR5a3NXdkdGU2dGc0hIdVRxOUdKalU0eHBTdkV3c2NqcVRWU213T1FUWGFEa2NZRFpPZmppc1BQOEFrRlBvTGxxeDFPd2JIREFPbmJVUjFJSmxUMUYvNXltNm9TK005RmxoRWU1RTF2elZpa0dNVmVucVRsVVZzeWxzdWtLRE1TeEwvYS91MG16cEJsTFhrK001dDYxbGRqeHRVR0JQangyWTRQSjQrWFZZMVZuTjRPUHlJeFh6Zm1TWVdhK096NUp0eFRqMjRhWnVYeGl2UU9ib3grRlMvSWZsSFNuTEV4UGtPWmRrMHJSNWI2T0hUeVU4aGZmalY5cTM1RitKdk5QQmMxeU50bGZsNTI5NnNOZkQ4Vnp1VnU1TnhmenQvSTAxWW12RFNueG8yM1cyL3dEb0J6eVhMVnBtNUlQTjUxZVB4UEgyVDRsbERKd1hNdXpic0xsR3JIUDhsZmlmSlJVbnliSXVUSXgxcjczWStRS3BhZFliR2ZITStyTHJ0VmVkTkZYSjBXdDhpbzQ4Y3U1dHg4UmN6QnZWUGpWZC9GOFZYUmYrdmhOeDlQNm5LWTcvQUsyVXA0Ymg3c2ZQeDdBYTJ5YS82VGd1WnNVWG1uak1qaUFPNjhYWW55Qk1sZVJ4ckxTQW5HL0RNbmdPYWUvSzV2NDZNbndDaks0ZXZJNDQ0MVlvTFlJck9WOFZ0eVh5KzJmOGViTTQvRnJUZ3NKZU1wOFE1VEJQSW9xTHpueCs3a3M5YjF6dVBzekdyY01saTZsU216RzZIaXVEcjRndDF5ZUplNEw1Y3o0NWljamY1TGw0L0hHdGpqOFpQYU5pVk1CdDZBK3BUaTFodXBYQ3R4OTJOKzFjeU1rc3g4M0l5RmF3MWhYOTIxZnNnaERYbFVxRGF2a2NBVHByM0VvSnFZd0l4SjJhaVpWaXBXMng0YXo3QW9UL0FGNGY0Nnc0MjExRXI2alUvWGNRMHVUc25IYy9YLy9FQURBUkFBRURBd0VHQlFNRUF3QUFBQUFBQUFFQUFoRWhNVUZSQXhBU0lHRnhJb0dSb2JFd1FNRVRNdEh3Y29MeC85b0FDQUVEQVFrL0FPWVVSbWFXcG4yUmgzc1QwMFRZSSsydEJYN3Bnd3YzT3pwLzFHU0tIM1ZmdFJmMVF5TzlqVkVwcGJPbGpQd1VLQXlEcWgxQlFrZFB0YXdhSXhHRWZMQ2R3a2VpMHZlUm1GUSt5SDJSdCtMNzRNNEtnVHI4cXRET2lQRHFEYnlPNzArd3NyanY4SzNzb25rb3FoR0p3ZndVT3Y4QVNqNy9BRnhjbzhoRXpZMEo3WVRZS3JPRlE5NlhYaUdpTmREK0RsVUkrcmxXbmwvcFFscEZqVkhoUFdvOGpjSVVPUlVldStyZFY3M1ErcWM4dW00cHZDZFIrUWlITkdsL1M0VkZSQldRK2xxT2JUZlpYVmV1ZlgrVTd5UDgyUStwcXJvVTVEVUdPY2dqUXFrRHVGV2RFNElreEhyelk1OVVjODVvbXdTRURVWTdvZWlNL1cxNURWTkVnSWYyRTVhS3huY0R6VU84SnRMS1FpckxCQ0tCanNtMWo4bzA5MWREQW0xS0lvaVl5Z0JCQnBSUEE3bE9MaEIveDNIa3p5YTduVlZhb3daRUtvN0pnOTAzaHVOYldUcTlvUmxhQ3FmTnNSbFFSVUdoTlpUWkFGdXhXeDhVeDRobUNSOExaRnJJT0k1dGVUQldtN1hsWVRPUllKdkNCMUJXMTRXZ1ZFWFR5NjVxZXNwb0lHRTJHNkpzSWVnVnR4ditOeEN6dU5FVUlxUGhGVUJXcXpDZFl3alE3cm1pdk1icnlRc0JHZUc5RTAvcUdJMHJhcTJaUENZbVZ0SUpjYUFBTkRnd0V5UXRpL2FjVGpJYkZJYmVUZ2xNSUlMcVFYRWVMaDZEQ01OWnM5bVpBdnhTYUFyWlBkeGdrVXZBTkJDWnd2RFJMVGNISUsyWGlMUWFUU1FiK2tKbkZ0QUFRQVNacEpIU0FtUWRyWVFhQVhKbnJBQ2FTWE9qdFExS0JEUXg5d2RFOERpMmJRWnZNbXVZbGJZZ3Q4VWlJbkFqOFhSeXo1Q2VmQzl3N1ZLbUh0Y090S3FhT3oyQlhFUU5vQUhHeENhUzN3V0JpcW54T0lvcWppYVFZMUJRd2ZnckVUUG1tU1NCWHFDbVNUQnZHRTR0Zng3V0NiSGhidytXSVR5NGhzdUdvTXFkaUhPaUtFOVQ0Z1lFMUttRHMyUzRYb0RNV3FpLzlOdXlOWEd2RVRuRWdJeTR4VTNQZE9EV3NqaUFtQ2ZFRGVzU1JUb25mdUFjTGluekJSSEFXMkFzYTBsV0pxUDlYQkVrYlJoaW9JZ2lJdkthU0dOZ0VrV0RySHFtVUxnU0NTTUdWV09IMmhiSmt2ZExiekV6VWpKVFd4c3BrRVRBaUR3NlZVWW55RUl4d3VEdlFyYmxnQWJRR2xKT3FlVy9wdW52aUNwdVBsWkJIcW5GM0VtekJrS2hnWlRSQW1tRE41NzVWS1JHSXdPd1Z4TUlwcytjSVFKcFdVNHc0eWVwMVVGb0VWMHZidXJCRzZmeDk1VHFCR3FLS084N2p2S0svOWs9IiBpZD0iYyIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiLz48L2RlZnM+PC9zdmc+", + "description": "Displays the latest power consumption telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'powerConsumption', label: 'Power consumption', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Power consumption\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"horizontal\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"bolt\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":5,\"color\":\"#3B911C\"},{\"from\":5,\"to\":15,\"color\":\"#F77410\"},{\"from\":15,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":5,\"color\":\"#3B911C\"},{\"from\":5,\"to\":15,\"color\":\"#F77410\"},{\"from\":15,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:aG9yaXpvbnRhbF9wb3dlcl9jb25zdW1wdGlvbl9jYXJkX2JhY2tncm91bmQucG5n:aG9yaXpvbnRhbF9wb3dlcl9jb25zdW1wdGlvbl9jYXJkX2JhY2tncm91bmQucG5n;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Horizontal power consumption card with background\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"kW\",\"decimals\":0,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "power", + "energy", + "energy usage", + "electric load", + "electricity", + "power efficiency", + "load profile" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_pressure_card.json b/application/src/main/data/json/system/widget_types/horizontal_pressure_card.json new file mode 100644 index 0000000000..7a2a3da436 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_pressure_card.json @@ -0,0 +1,28 @@ +{ + "fqn": "horizontal_pressure_card", + "name": "Horizontal pressure card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAolBMVEUAAADf39/f39/g4ODg4ODf39/////k5OTYGDjg4ODri5v64ub1xc3nboLdNVHwqLT98fPOzs5YWFjz8/O3t7faJkXx8fH5+fnb29vCwsK8vLziUmnV1dXHx8fkYHbn5+f409rIyMjzt8HpfY9mZmarq6t0dHTiUmrfQ13umqiQkJCCgoJKSko6Ojrt7e3tmajsi5z39/faJkTgRF3pfY4hISGGGYADAAAABnRSTlMAIEDfv1C6kOEmAAADqUlEQVR42u3ai1LaQBiGYar2z89uzgdIYgKEcBJBrW3v/9a6WUCgUiTMlAnM90hCTHCyr7Aggy0AAAAAAAAAAAAAAAAAAAAAAAAAAACAS/p29XTG3b1x7dr331WH8YOuXmDctR4CugE/HlptuglthDQMQpoGIU2DkKY5HOJKujaHQyy+upJDIc6bxaagrfF8blDDHQqxOVJfQ/rgj/NlZnQ6VOUYHb2otVoZ1eUs5vrqVXycaOCYq31CmLQmWGx+wFGGgzoh0mZmQVt+TqPQX+TPI7/zPFlk88kyn+SGb/xeGP7o+YyKMmKqvLFiS50xZbY2Z2fb/TvEYs2Wp4RsSwTt8Be+T35mLMejsT9JO3O16BCf5qPxkmpyZ6yQItk2ByVPq46ILaca5CNbcjBj8Tmk5zivM5VYY7LLJ9rl59XKMJZ52MlzP83y0XgS6pDnSZ5TTdIWziqk5FJ1RazGZrNDml2NdMj2R4g7HEodopMidlaPQnk8RIodw/0Q0g+t0cKYj/xwvBxVIcZiNKH65CrkUc/EGQ/UyC2S27GZPN2E9Gzmd3MToq/klJW3oyEm77DoL529Zbvv/BBzvS65F20mgCvNn+xsQlg4PZ5+hDzyE/W4NJ2I5bEQ19whqZ7zQ9TClrB5uh68Gu0m5JcaFHMVYpnm0OJIkhAuqRrnjHvkAiFCz5aBfvq1ondzd7KvQrSouoX7Wlo2O3XniHKBkGrL2gzO5NnnkJ7jOLrviaPZowqp+6yl/KcQPXA92fUDRa2HrjMkhflziKAVl1luo794HXmjHQEdELz8vSOoHfLKvWrbrram+rzS5Xc1ygHb/w6R/K6jnVNe2fdKQh2jLi8xad0uddtetaNNXfV9f9ynbhyvDqgdwYkhrq2nuFhv/WSrGqAtyojF4RCtuu2UvwxxV39rOXshWebFReGprYSSOEm7YeJlWeGlL16qRu71gzSNKfD0Aa9ITwvRv7Oo1Fs9Zi5ddXpVwdETHQkZ2MxTi8ujIYoQFps9dz/EC4PC63urkH4Wq221rx97SaFD2nGsQoqwXwQqNwnoRK50P21JSV+Q8qz3I2MvTrwwK5IgVCFeGidhEBae2veS9gsVp0K61T3ST8fVgSxJ6AyXf4cY9+nfsiKmy9MhtXW7xw4GdIpGhDQRQpoGIU2DkKZBSNO0Ww838DE7UfDQujNu4IP2H8Zdq/X9vm1cu/u7W/qnGgAAAAAAAAAAAAAAAAAAAAAAAAAAAIAL+QMW1wDljmkjjQAAAABJRU5ErkJggg==", + "description": "Displays the latest pressure telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pressure', label: 'Pressure', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 80 - 40;\\nif (value < 980) {\\n\\tvalue = 980;\\n} else if (value > 1040) {\\n\\tvalue = 1040;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"compress\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1000,\"color\":\"#D81838\"},{\"from\":1000,\"to\":1020,\"color\":\"#80C32C\"},{\"from\":1020,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":1000,\"color\":\"#D81838\"},{\"from\":1000,\"to\":1020,\"color\":\"#80C32C\"},{\"from\":1020,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal pressure card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"hPa\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "barometry" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_pressure_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_pressure_card_with_background.json new file mode 100644 index 0000000000..d0551423ef --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_pressure_card_with_background.json @@ -0,0 +1,28 @@ +{ + "fqn": "horizontal_pressure_card_with_background", + "name": "Horizontal pressure card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest pressure telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pressure', label: 'Pressure', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 80 - 40;\\nif (value < 980) {\\n\\tvalue = 980;\\n} else if (value > 1040) {\\n\\tvalue = 1040;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"compress\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1000,\"color\":\"#DE2343\"},{\"from\":1000,\"to\":1020,\"color\":\"#7CC322\"},{\"from\":1020,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":1000,\"color\":\"#DE2343\"},{\"from\":1000,\"to\":1020,\"color\":\"#7CC322\"},{\"from\":1020,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal pressure card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"hPa\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "barometry" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_pump_vibration_card.json b/application/src/main/data/json/system/widget_types/horizontal_pump_vibration_card.json new file mode 100644 index 0000000000..e74de98113 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_pump_vibration_card.json @@ -0,0 +1,33 @@ +{ + "fqn": "horizontal_pump_vibration_card", + "name": "Horizontal vibration card", + "deprecated": false, + "image": "tb-image:aG9yaXpvbnRhbF92aWJyYXRpb25fY2FyZC5zdmc=:aG9yaXpvbnRhbF92aWJyYXRpb25fY2FyZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjU2IiB5PSI1MiIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjU1IiB4PSIuNSIgeT0iNTIuNSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjEiIHJ4PSIzLjUiLz48ZyBmaWx0ZXI9InVybCgjYSkiPjxyZWN0IHdpZHRoPSIxODQiIGhlaWdodD0iNDAiIHg9IjgiIHk9IjYwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii44IiByeD0iNCIvPjxwYXRoIGZpbGw9IiNGRkE2MDAiIGQ9Ik0yOSA4NWE2IDYgMCAwIDAtMyAuOGMtLjYuMy0xLjEuNi0yIC42LS45IDAtMS40LS4zLTItLjYtLjgtLjQtMS42LS44LTMtLjhzLTIuMi40LTMgLjhjLS42LjMtMS4xLjYtMiAuNnYyYTYgNiAwIDAgMCAzLS45Yy42LS4zIDEuMS0uNiAyLS42LjkgMCAxLjQuMyAyIC42LjguNCAxLjYuOCAzIC44czIuMi0uNCAzLS44Yy42LS4zIDEuMS0uNiAyLS42LjkgMCAxLjQuMyAyIC42LjguNCAxLjYuOCAzIC44di0yYy0uOSAwLTEuNC0uMi0yLS41YTYgNiAwIDAgMC0zLS44Wm0wLTQuNWMtMS40IDAtMi4yLjUtMyAuOC0uNi40LTEuMS42LTIgLjYtLjkgMC0xLjQtLjItMi0uNi0uOC0uMy0xLjYtLjgtMy0uOHMtMi4yLjUtMyAuOGMtLjYuNC0xLjEuNi0yIC42djJjMS4zIDAgMi4yLS40IDMtLjhhMy44IDMuOCAwIDAgMSA0IDBjLjguNCAxLjYuOCAzIC44czIuMi0uNCAzLS44YTMuOCAzLjggMCAwIDEgNCAwYy44LjQgMS42LjggMyAuOHYtMmMtLjkgMC0xLjQtLjItMi0uNmE2IDYgMCAwIDAtMy0uOFptMy04YTUuOSA1LjkgMCAwIDAtNiAwYy0uNi4zLTEuMS42LTIgLjYtLjkgMC0xLjQtLjMtMi0uNi0uOC0uNC0xLjYtLjgtMy0uOHMtMi4yLjQtMyAuOGMtLjYuMy0xLjEuNi0yIC42Vjc1YzEuMyAwIDIuMi0uNCAzLS44LjYtLjMgMS4xLS42IDItLjYuOSAwIDEuNC4yIDIgLjYuOC40IDEuNi44IDMgLjhzMi4yLS40IDMtLjhjLjYtLjMgMS4xLS42IDItLjYuOSAwIDEuNC4yIDIgLjYuOC40IDEuNi44IDMgLjh2LTJhNCA0IDAgMCAxLTItLjVaTTI5IDc2Yy0xLjQgMC0yLjIuNC0zIC44YTMuOCAzLjggMCAwIDEtNCAwYy0uOC0uNC0xLjYtLjgtMy0uOHMtMi4yLjQtMyAuOGMtLjYuMy0xLjEuNi0yIC42djJjMS4zIDAgMi4yLS41IDMtLjkuNi0uMyAxLjEtLjYgMi0uNi45IDAgMS40LjMgMiAuNi44LjQgMS42LjggMyAuOHMyLjItLjQgMy0uOGMuNi0uMyAxLjEtLjYgMi0uNi45IDAgMS40LjMgMiAuNi44LjQgMS42LjggMyAuOHYtMmMtLjkgMC0xLjQtLjItMi0uNWE2IDYgMCAwIDAtMy0uOFoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNNDMuNCA4My4yIDQ2IDc1aDJsLTMuNiAxMEg0M2wuMy0xLjhaTTQxIDc1bDIuNiA4LjIuNCAxLjhoLTEuM0wzOSA3NUg0MVptOS45IDIuNlY4NWgtMS43di03LjRINTFabS0xLjgtMiAuMy0uNi43LS4yYy4zIDAgLjUgMCAuNy4ybC4yLjdjMCAuMiAwIC40LS4yLjZhMSAxIDAgMCAxLS43LjIgMSAxIDAgMCAxLS43LS4yLjguOCAwIDAgMS0uMy0uNlptMy45LTEuMWgxLjZ2OWwtLjEgMS41SDUzVjc0LjVabTYuNCA2Ljd2LjJjMCAuNSAwIDEtLjIgMS41IDAgLjQtLjMuOC0uNSAxLjItLjIuMy0uNS42LS45LjhsLTEuMi4yYy0uNSAwLS45IDAtMS4yLS4yLS40LS4yLS43LS40LS45LS44bC0uNi0xLjEtLjMtMS41di0uNGwuMy0xLjUuNi0xLjFhMi40IDIuNCAwIDAgMSAyLTFjLjUgMCAxIDAgMS4zLjJsMSAuOC40IDEuMmMuMi41LjIgMSAuMiAxLjVabS0xLjYuMnYtLjJsLS4xLS45YzAtLjMtLjEtLjYtLjMtLjggMC0uMi0uMy0uNC0uNS0uNWwtLjgtLjItLjcuMS0uNS41YTIgMiAwIDAgMC0uNC42bC0uMS43djEuMmwuMiAxIC42LjcgMSAuMmMuMyAwIC41IDAgLjctLjJsLjUtLjVjLjItLjIuMi0uNS4zLS44di0xWm01LTIuNHY2SDYxdi03LjRoMS42Vjc5Wm0yLjItMS40Vjc5YTMuMiAzLjIgMCAwIDAtMS40IDAgMS40IDEuNCAwIDAgMC0uOSAxbC0uMS42aC0uNGwuMS0xLjIuNC0xYy4yLS40LjUtLjYuNy0uOGExLjkgMS45IDAgMCAxIDEuMy0uMmguM1ptNS4yIDZWODBsLS4xLS43YTEgMSAwIDAgMC0uNS0uNGwtLjctLjItLjcuMS0uNS40LS4xLjVoLTEuN2wuMi0uOC43LS43YTMgMyAwIDAgMSAxLS41IDQgNCAwIDAgMSAxLjItLjJjLjUgMCAxIDAgMS41LjIuNC4yLjcuNSAxIC45LjIuNC40LjguNCAxLjR2NC4ybC4yLjd2LjFoLTEuN2EzIDMgMCAwIDEtLjEtLjdsLS4xLS44Wm0uMi0zLjF2MWgtMS4xbC0uOC4xYy0uMyAwLS41LjEtLjYuM2ExIDEgMCAwIDAtLjUuOWMwIC4yIDAgLjQuMi41IDAgLjIuMi4zLjQuNGwuNi4xYTEuOCAxLjggMCAwIDAgMS41LS43bC4yLS42LjUuOC0uMi42YTMgMyAwIDAgMS0uNi42IDIuNiAyLjYgMCAwIDEtMS43LjZjLS41IDAtMSAwLTEuMy0uMy0uNC0uMS0uNy0uNC0xLS43YTIuNCAyLjQgMCAwIDEtLjEtMi4ybC43LS44IDEuMS0uNCAxLjQtLjJoMS4zWm02LjgtMi45djEuMkg3M3YtMS4yaDQuMlptLTMtMS44SDc2VjgzbC4xLjUuMy4yaC40YTIuNSAyLjUgMCAwIDAgLjYgMFY4NWE0LjIgNC4yIDAgMCAxLTEuMS4xYy0uNCAwLS43IDAtMS0uMi0uMy0uMS0uNS0uMy0uNy0uNi0uMi0uMy0uMi0uNy0uMi0xLjJ2LTcuM1ptNi4zIDEuOFY4NWgtMS43di03LjRoMS43Wm0tMS44LTIgLjMtLjYuNy0uMmMuMyAwIC41IDAgLjcuMmwuMi43YzAgLjIgMCAuNC0uMi42YTEgMSAwIDAgMS0uNy4yIDEgMSAwIDAgMS0uNy0uMi44LjggMCAwIDEtLjMtLjZabTMuNSA1Ljh2LS4ybC4zLTEuNS42LTEuMmEzIDMgMCAwIDEgMS4xLS43bDEuNS0uM2MuNSAwIDEgMCAxLjQuMy41LjEuOC40IDEuMS43bC43IDEuMi4yIDEuNXYuMmMwIC41IDAgMS0uMiAxLjVsLS43IDEuMmEzLjEgMy4xIDAgMCAxLTIuNSAxYy0uNiAwLTEgMC0xLjUtLjJhMy41IDMuNSAwIDAgMS0xLjgtMmwtLjItMS41Wm0xLjctLjJ2MS4ybC40LjdjLjIuMi4zLjQuNi41LjIuMi41LjIuOC4yYTEuNiAxLjYgMCAwIDAgMS4zLS43bC40LS44di0ybC0uNC0uOGExLjYgMS42IDAgMCAwLTEuMy0uN2MtLjMgMC0uNiAwLS44LjItLjMuMS0uNC4zLS42LjVsLS4zLjgtLjEgMVptOC41LTJWODVoLTEuN3YtNy40aDEuNnYxLjZaTTkyIDgxaC0uNWMwLS41IDAtMSAuMi0xLjQuMS0uNS4zLS44LjYtMS4xYTIuNyAyLjcgMCAwIDEgMi4xLTFsMSAuMS43LjUuNS44LjIgMS4zVjg1aC0xLjd2LTQuOGwtLjEtLjhhLjkuOSAwIDAgMC0uNS0uNWwtLjctLjFhMS42IDEuNiAwIDAgMC0xLjMuN2wtLjQuNy0uMS44WiIvPjxwYXRoIGZpbGw9IiNGRkE2MDAiIGQ9Ik0xMjQuOSA4MnYxLjhoLTEwLjN2LTEuNGw2LTkuNmgybC0yIDMuNS0zLjYgNS42aDcuOVptLTEuOC05LjJWODdoLTIuNFY3Mi44aDIuNFptMTEgNS44Vjg3aC0yLjNWNzYuNGgyLjJsLjEgMi4yWm0tLjQgMi43aC0uOGMwLS43LjEtMS40LjMtMmwuOC0xLjZjLjQtLjUuOC0uOCAxLjMtMSAuNS0uMyAxLjEtLjUgMS44LS41LjUgMCAxIC4xIDEuNC4zLjQgMCAuNy4zIDEgLjYuMy4zLjUuNy43IDEuMmwuMiAxLjd2N2gtMi4zdi02LjhjMC0uNi0uMS0xLS4zLTEuMi0uMS0uMy0uMy0uNS0uNi0uN2wtMS0uMmMtLjUgMC0uOC4xLTEuMS4zLS40LjItLjYuNC0uOC43YTMgMyAwIDAgMC0uNCAxbC0uMiAxLjJabTYuNi0uNi0xLjEuMmMwLS42IDAtMS4yLjItMS44bC44LTEuNWEzLjcgMy43IDAgMCAxIDMuMS0xLjRjLjUgMCAxIC4xIDEuNC4zLjUuMS44LjMgMS4xLjYuMy40LjYuOC43IDEuMy4yLjUuMyAxIC4zIDEuOFY4N2gtMi40di02LjhjMC0uNiAwLTEtLjItMS4zLS4yLS4zLS40LS41LS43LS42bC0xLS4yLTEgLjJhMiAyIDAgMCAwLS43LjZsLS40LjgtLjEgMVptMTEuMy0yLjFWODdoLTIuM1Y3Ni40aDIuMmwuMSAyLjJabS0uMyAyLjdoLS44YzAtLjcgMC0xLjQuMi0ybC44LTEuNmMuNC0uNS44LS44IDEuMy0xIC42LS4zIDEuMi0uNSAxLjktLjUuNCAwIC45LjEgMS4zLjMuNCAwIC43LjMgMSAuNi4zLjMuNi43LjcgMS4yLjIuNC4zIDEgLjMgMS43djdoLTIuNHYtNi44YzAtLjYgMC0xLS4yLTEuMmwtLjctLjctMS0uMmMtLjQgMC0uOC4xLTEuMS4zbC0uOC43YTMgMyAwIDAgMC0uNCAxbC0uMSAxLjJabTYuNS0uNi0xLjEuMmMwLS42IDAtMS4yLjMtMS44LjEtLjUuNC0xIC44LTEuNWEzLjcgMy43IDAgMCAxIDMtMS40Yy42IDAgMSAuMSAxLjUuMy40LjEuOC4zIDEgLjYuNC40LjYuOC43IDEuMy4yLjUuMyAxIC4zIDEuOFY4N2gtMi40di02LjhjMC0uNiAwLTEtLjItMS4zLS4yLS4zLS40LS41LS43LS42bC0xLS4yLTEgLjJhMiAyIDAgMCAwLS42LjZsLS41Ljh2MVptMTUuMi04LTUuNiAxNS41aC0xLjhsNS41LTE1LjRoMS45Wm03LjYgMTEuNC0uMS0uNi0uNy0uNS0xLjUtLjVjLS41IDAtMS0uMi0xLjUtLjRsLTEuMi0uNi0uOC0xLS4yLTFjMC0uNSAwLTEgLjMtMS4zLjEtLjQuNC0uNy44LTFhNCA0IDAgMCAxIDEuMi0uN2MuNS0uMiAxLjEtLjMgMS43LS4zIDEgMCAxLjcuMiAyLjMuNS43LjMgMS4xLjcgMS41IDEuMi4zLjUuNSAxIC41IDEuN2gtMi40YzAtLjMgMC0uNi0uMi0uOGwtLjYtLjYtMS0uMmMtLjUgMC0uOCAwLTEgLjJsLS43LjVhMS4yIDEuMiAwIDAgMCAwIDEuMWwuMy40LjcuMyAxIC4zYy45LjEgMS42LjQgMi4xLjYuNi4zIDEgLjYgMS40IDEgLjMuNS40IDEgLjQgMS42YTIuOCAyLjggMCAwIDEtMS4xIDIuM2wtMS40LjctMS43LjJjLTEgMC0xLjgtLjItMi41LS41LS42LS40LTEuMS0uOC0xLjUtMS4zLS4zLS42LS41LTEuMS0uNS0xLjdoMi4zYzAgLjQuMS44LjQgMSAuMi4zLjQuNS44LjZsMSAuMiAxLS4yYy40LS4xLjYtLjMuNy0uNS4yLS4yLjItLjQuMi0uN1oiLz48L2c+PGRlZnM+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjE5NiIgaGVpZ2h0PSI1MiIgeD0iMiIgeT0iNTQiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjMiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTUwOV8xMjI5NiIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTUwOV8xMjI5NiIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjwvZGVmcz48L3N2Zz4=", + "description": "Displays the latest vibration telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'vibration', label: 'Vibration', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Vibration\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 3.3 - 1.7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"horizontal\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"waves\",\"iconColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":2.8,\"color\":\"#3FA71A\"},{\"from\":2.8,\"to\":4.5,\"color\":\"#FFA600\"},{\"from\":4.5,\"to\":7.1,\"color\":\"#F36900\"},{\"from\":7.1,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":2.8,\"color\":\"#3FA71A\"},{\"from\":2.8,\"to\":4.5,\"color\":\"#FFA600\"},{\"from\":4.5,\"to\":7.1,\"color\":\"#F36900\"},{\"from\":7.1,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Horizontal vibration card\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"mm/s\",\"decimals\":1,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "vibration", + "pump vibration", + "motion", + "resonance", + "dynamic balancing", + "mechanical integrity", + "pulsation", + "frequency" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_pump_vibration_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_pump_vibration_card_with_background.json new file mode 100644 index 0000000000..c36c1ff9fd --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_pump_vibration_card_with_background.json @@ -0,0 +1,33 @@ +{ + "fqn": "horizontal_pump_vibration_card_with_background", + "name": "Horizontal vibration card with background", + "deprecated": false, + "image": "tb-image:aG9yaXpvbnRhbF92aWJyYXRpb25fY2FyZF93aXRoX2JhY2tncm91bmQuc3Zn:aG9yaXpvbnRhbF92aWJyYXRpb25fY2FyZF93aXRoX2JhY2tncm91bmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSI1NiIgeT0iNTIiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjQ0IiB4PSI2IiB5PSI1OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48cGF0aCBmaWxsPSIjRjg5RTBEIiBkPSJNMjcgODVhNiA2IDAgMCAwLTMgLjhjLS42LjMtMS4xLjYtMiAuNi0uOSAwLTEuNC0uMy0yLS42LS44LS40LTEuNi0uOC0zLS44cy0yLjIuNC0zIC44Yy0uNi4zLTEuMS42LTIgLjZ2MmE2IDYgMCAwIDAgMy0uOWMuNi0uMyAxLjEtLjYgMi0uNi45IDAgMS40LjMgMiAuNi44LjQgMS42LjggMyAuOHMyLjItLjQgMy0uOGMuNi0uMyAxLjEtLjYgMi0uNi45IDAgMS40LjMgMiAuNi44LjQgMS42LjggMyAuOHYtMmMtLjkgMC0xLjQtLjItMi0uNWE2IDYgMCAwIDAtMy0uOFptMC00LjVjLTEuNCAwLTIuMi41LTMgLjgtLjYuNC0xLjEuNi0yIC42LS45IDAtMS40LS4yLTItLjYtLjgtLjMtMS42LS44LTMtLjhzLTIuMi41LTMgLjhjLS42LjQtMS4xLjYtMiAuNnYyYzEuMyAwIDIuMi0uNCAzLS44YTMuOCAzLjggMCAwIDEgNCAwYy44LjQgMS42LjggMyAuOHMyLjItLjQgMy0uOGEzLjggMy44IDAgMCAxIDQgMGMuOC40IDEuNi44IDMgLjh2LTJjLS45IDAtMS40LS4yLTItLjZhNiA2IDAgMCAwLTMtLjhabTMtOGE1LjkgNS45IDAgMCAwLTYgMGMtLjYuMy0xLjEuNi0yIC42LS45IDAtMS40LS4zLTItLjYtLjgtLjQtMS42LS44LTMtLjhzLTIuMi40LTMgLjhjLS42LjMtMS4xLjYtMiAuNlY3NWMxLjMgMCAyLjItLjQgMy0uOC42LS4zIDEuMS0uNiAyLS42LjkgMCAxLjQuMiAyIC42LjguNCAxLjYuOCAzIC44czIuMi0uNCAzLS44Yy42LS4zIDEuMS0uNiAyLS42LjkgMCAxLjQuMiAyIC42LjguNCAxLjYuOCAzIC44di0yYTQgNCAwIDAgMS0yLS41Wk0yNyA3NmMtMS40IDAtMi4yLjQtMyAuOGEzLjggMy44IDAgMCAxLTQgMGMtLjgtLjQtMS42LS44LTMtLjhzLTIuMi40LTMgLjhjLS42LjMtMS4xLjYtMiAuNnYyYzEuMyAwIDIuMi0uNSAzLS45LjYtLjMgMS4xLS42IDItLjYuOSAwIDEuNC4zIDIgLjYuOC40IDEuNi44IDMgLjhzMi4yLS40IDMtLjhjLjYtLjMgMS4xLS42IDItLjYuOSAwIDEuNC4zIDIgLjYuOC40IDEuNi44IDMgLjh2LTJjLS45IDAtMS40LS4yLTItLjVhNiA2IDAgMCAwLTMtLjhaIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZD0iTTQyLjQgODMuMiA0NSA3NWgybC0zLjYgMTBINDJsLjMtMS44Wk00MCA3NWwyLjYgOC4yLjQgMS44aC0xLjNMMzggNzVINDBabTkuOSAyLjZWODVoLTEuN3YtNy40SDUwWm0tMS44LTIgLjMtLjYuNy0uMmMuMyAwIC41IDAgLjcuMmwuMi43YzAgLjIgMCAuNC0uMi42YTEgMSAwIDAgMS0uNy4yIDEgMSAwIDAgMS0uNy0uMi44LjggMCAwIDEtLjMtLjZabTMuOS0xLjFoMS42djlsLS4xIDEuNUg1MlY3NC41Wm02LjQgNi43di4yYzAgLjUgMCAxLS4yIDEuNSAwIC40LS4zLjgtLjUgMS4yLS4yLjMtLjUuNi0uOS44bC0xLjIuMmMtLjUgMC0uOSAwLTEuMi0uMi0uNC0uMi0uNy0uNC0uOS0uOGwtLjYtMS4xLS4zLTEuNXYtLjRsLjMtMS41LjYtMS4xYTIuNCAyLjQgMCAwIDEgMi0xYy41IDAgMSAwIDEuMy4ybDEgLjguNCAxLjJjLjIuNS4yIDEgLjIgMS41Wm0tMS42LjJ2LS4ybC0uMS0uOWMwLS4zLS4xLS42LS4zLS44IDAtLjItLjMtLjQtLjUtLjVsLS44LS4yLS43LjEtLjUuNWEyIDIgMCAwIDAtLjQuNmwtLjEuN3YxLjJsLjIgMSAuNi43IDEgLjJjLjMgMCAuNSAwIC43LS4ybC41LS41Yy4yLS4yLjItLjUuMy0uOHYtMVptNS0yLjR2Nkg2MHYtNy40aDEuNlY3OVptMi4yLTEuNFY3OWEzLjIgMy4yIDAgMCAwLTEuNCAwIDEuNCAxLjQgMCAwIDAtLjkgMWwtLjEuNmgtLjRsLjEtMS4yLjQtMWMuMi0uNC41LS42LjctLjhhMS45IDEuOSAwIDAgMSAxLjMtLjJoLjNabTUuMiA2VjgwbC0uMS0uN2ExIDEgMCAwIDAtLjUtLjRsLS43LS4yLS43LjEtLjUuNC0uMS41aC0xLjdsLjItLjguNy0uN2EzIDMgMCAwIDEgMS0uNSA0IDQgMCAwIDEgMS4yLS4yYy41IDAgMSAwIDEuNS4yLjQuMi43LjUgMSAuOS4yLjQuNC44LjQgMS40djQuMmwuMi43di4xaC0xLjdhMyAzIDAgMCAxLS4xLS43bC0uMS0uOFptLjItMy4xdjFoLTEuMWwtLjguMWMtLjMgMC0uNS4xLS42LjNhMSAxIDAgMCAwLS41LjljMCAuMiAwIC40LjIuNSAwIC4yLjIuMy40LjRsLjYuMWExLjggMS44IDAgMCAwIDEuNS0uN2wuMi0uNi41LjgtLjIuNmEzIDMgMCAwIDEtLjYuNiAyLjYgMi42IDAgMCAxLTEuNy42Yy0uNSAwLTEgMC0xLjMtLjMtLjQtLjEtLjctLjQtMS0uN2EyLjQgMi40IDAgMCAxLS4xLTIuMmwuNy0uOCAxLjEtLjQgMS40LS4yaDEuM1ptNi44LTIuOXYxLjJINzJ2LTEuMmg0LjJabS0zLTEuOEg3NVY4M2wuMS41LjMuMmguNGEyLjUgMi41IDAgMCAwIC42IDBWODVhNC4yIDQuMiAwIDAgMS0xLjEuMWMtLjQgMC0uNyAwLTEtLjItLjMtLjEtLjUtLjMtLjctLjYtLjItLjMtLjItLjctLjItMS4ydi03LjNabTYuMyAxLjhWODVoLTEuN3YtNy40aDEuN1ptLTEuOC0yIC4zLS42LjctLjJjLjMgMCAuNSAwIC43LjJsLjIuN2MwIC4yIDAgLjQtLjIuNmExIDEgMCAwIDEtLjcuMiAxIDEgMCAwIDEtLjctLjIuOC44IDAgMCAxLS4zLS42Wm0zLjUgNS44di0uMmwuMy0xLjUuNi0xLjJhMyAzIDAgMCAxIDEuMS0uN2wxLjUtLjNjLjUgMCAxIDAgMS40LjMuNS4xLjguNCAxLjEuN2wuNyAxLjIuMiAxLjV2LjJjMCAuNSAwIDEtLjIgMS41bC0uNyAxLjJhMy4xIDMuMSAwIDAgMS0yLjUgMWMtLjYgMC0xIDAtMS41LS4yYTMuNSAzLjUgMCAwIDEtMS44LTJsLS4yLTEuNVptMS43LS4ydjEuMmwuNC43Yy4yLjIuMy40LjYuNS4yLjIuNS4yLjguMmExLjYgMS42IDAgMCAwIDEuMy0uN2wuNC0uOHYtMmwtLjQtLjhhMS42IDEuNiAwIDAgMC0xLjMtLjdjLS4zIDAtLjYgMC0uOC4yLS4zLjEtLjQuMy0uNi41bC0uMy44LS4xIDFabTguNS0yVjg1aC0xLjd2LTcuNGgxLjZ2MS42Wk05MSA4MWgtLjVjMC0uNSAwLTEgLjItMS40LjEtLjUuMy0uOC42LTEuMWEyLjcgMi43IDAgMCAxIDIuMS0xbDEgLjEuNy41LjUuOC4yIDEuM1Y4NWgtMS43di00LjhsLS4xLS44YS45LjkgMCAwIDAtLjUtLjVsLS43LS4xYTEuNiAxLjYgMCAwIDAtMS4zLjdsLS40LjctLjEuOFoiLz48cGF0aCBmaWxsPSIjRjg5RTBEIiBkPSJNMTI2LjkgODJ2MS44aC0xMC4zdi0xLjRsNi05LjZoMmwtMiAzLjUtMy42IDUuNmg3LjlabS0xLjgtOS4yVjg3aC0yLjRWNzIuOGgyLjRabTExIDUuOFY4N2gtMi4zVjc2LjRoMi4ybC4xIDIuMlptLS40IDIuN2gtLjhjMC0uNy4xLTEuNC4zLTJsLjgtMS42Yy40LS41LjgtLjggMS4zLTEgLjUtLjMgMS4xLS41IDEuOC0uNS41IDAgMSAuMSAxLjQuMy40IDAgLjcuMyAxIC42LjMuMy41LjcuNyAxLjJsLjIgMS43djdoLTIuM3YtNi44YzAtLjYtLjEtMS0uMy0xLjItLjEtLjMtLjMtLjUtLjYtLjdsLTEtLjJjLS41IDAtLjguMS0xLjEuMy0uNC4yLS42LjQtLjguN2EzIDMgMCAwIDAtLjQgMWwtLjIgMS4yWm02LjYtLjYtMS4xLjJjMC0uNiAwLTEuMi4yLTEuOGwuOC0xLjVhMy43IDMuNyAwIDAgMSAzLjEtMS40Yy41IDAgMSAuMSAxLjQuMy41LjEuOC4zIDEuMS42LjMuNC42LjguNyAxLjMuMi41LjMgMSAuMyAxLjhWODdoLTIuNHYtNi44YzAtLjYgMC0xLS4yLTEuMy0uMi0uMy0uNC0uNS0uNy0uNmwtMS0uMi0xIC4yYTIgMiAwIDAgMC0uNy42bC0uNC44LS4xIDFabTExLjMtMi4xVjg3aC0yLjNWNzYuNGgyLjJsLjEgMi4yWm0tLjMgMi43aC0uOGMwLS43IDAtMS40LjItMmwuOC0xLjZjLjQtLjUuOC0uOCAxLjMtMSAuNi0uMyAxLjItLjUgMS45LS41LjQgMCAuOS4xIDEuMy4zLjQgMCAuNy4zIDEgLjYuMy4zLjYuNy43IDEuMi4yLjQuMyAxIC4zIDEuN3Y3aC0yLjR2LTYuOGMwLS42IDAtMS0uMi0xLjJsLS43LS43LTEtLjJjLS40IDAtLjguMS0xLjEuM2wtLjguN2EzIDMgMCAwIDAtLjQgMWwtLjEgMS4yWm02LjUtLjYtMS4xLjJjMC0uNiAwLTEuMi4zLTEuOC4xLS41LjQtMSAuOC0xLjVhMy43IDMuNyAwIDAgMSAzLTEuNGMuNiAwIDEgLjEgMS41LjMuNC4xLjguMyAxIC42LjQuNC42LjguNyAxLjMuMi41LjMgMSAuMyAxLjhWODdoLTIuNHYtNi44YzAtLjYgMC0xLS4yLTEuMy0uMi0uMy0uNC0uNS0uNy0uNmwtMS0uMi0xIC4yYTIgMiAwIDAgMC0uNi42bC0uNS44djFabTE1LjItOC01LjYgMTUuNWgtMS44bDUuNS0xNS40aDEuOVptNy42IDExLjQtLjEtLjYtLjctLjUtMS41LS41Yy0uNSAwLTEtLjItMS41LS40bC0xLjItLjYtLjgtMS0uMi0xYzAtLjUgMC0xIC4zLTEuMy4xLS40LjQtLjcuOC0xYTQgNCAwIDAgMSAxLjItLjdjLjUtLjIgMS4xLS4zIDEuNy0uMyAxIDAgMS43LjIgMi4zLjUuNy4zIDEuMS43IDEuNSAxLjIuMy41LjUgMSAuNSAxLjdoLTIuNGMwLS4zIDAtLjYtLjItLjhsLS42LS42LTEtLjJjLS41IDAtLjggMC0xIC4ybC0uNy41YTEuMiAxLjIgMCAwIDAgMCAxLjFsLjMuNC43LjMgMSAuM2MuOS4xIDEuNi40IDIuMS42LjYuMyAxIC42IDEuNCAxIC4zLjUuNCAxIC40IDEuNmEyLjggMi44IDAgMCAxLTEuMSAyLjNsLTEuNC43LTEuNy4yYy0xIDAtMS44LS4yLTIuNS0uNS0uNi0uNC0xLjEtLjgtMS41LTEuMy0uMy0uNi0uNS0xLjEtLjUtMS43aDIuM2MwIC40LjEuOC40IDEgLjIuMy40LjUuOC42bDEgLjIgMS0uMmMuNC0uMS42LS4zLjctLjUuMi0uMi4yLS40LjItLjdaIi8+PC9nPjxkZWZzPjxwYXR0ZXJuIGlkPSJhIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBwYXR0ZXJuQ29udGVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCI+PHVzZSB4bGluazpocmVmPSIjYyIgdHJhbnNmb3JtPSJtYXRyaXgoLjAwNTAyNTEzIDAgMCAuMDE3OTQ2OSAwIC0xKSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjU2IiB4PSIwIiB5PSI1MiIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMyIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMDg1XzYyOTIwIi8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMDg1XzYyOTIwIiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PGltYWdlIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3QkRBQUlDQWdJQ0FRSUNBZ0lEQWdJREF3WUVBd01EQXdjRkJRUUdDQWNKQ0FnSENBZ0pDZzBMQ1FvTUNnZ0lDdzhMREEwT0RnOE9DUXNRRVJBT0VRME9EZzcvMndCREFRSURBd01EQXdjRUJBY09DUWdKRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNy93Z0FSQ0FDZkFNY0RBUkVBQWhFQkF4RUIvOFFBSGdBQUFnSUNBd0VCQUFBQUFBQUFBQUFBQmdjRkNBTUVBQUVDQ1FyL3hBQWFBUUFEQVFFQkFRQUFBQUFBQUFBQUFBQUNBd1FCQUFVRy85b0FEQU1CQUFJUUF4QUFBQUQ3eFpudmg3N09aM043dmU2N3VaM003bmIxM2RkbmZienN4OW5yTjczc09aaDdPdTd2czUzWU15S0ZNUUNXQlI2SHJPN0x1aDdydTUzZDd2bnU1M2M3c1E1bjNvN0F3NW1iZGpoQlpvOCtHd2NmQnBpcjN3eDJKZ2x6NlFwejkxeVBVKzM1MitlenZjOVp2bnU4OTNmZDZ6b3ZGYzd2TzhKcWpCSjRGOG56NC9FblRyTUhDTGlBOTNBK0FPWm5XN29aMGVDdnEzNjMyL2ZkNjd1KzcxdmM3Y0dCb2lPVGV5ZHNPTTRrcUlIUjUyUEZRNHBnZ1RIWUlFcEk1MkxQbXJabFlTMnBZazZJMGlVVi9vNTlINlRCbWFHSzBCR1NJODNFTUJLVk1xbWpjQko4OVlJOHZEaTlMQmd3bUtTcFhhb2tyTDUrTGlUU2dnT0pmTXBWOUY0MVRmdE1wWXZaOTlXMUNLcEYxUDVjb1drektjRzhOQ3FCN1BIZE04V0hoV3lvbkcvMTFjdVBXeGNjS1NmVFg4OFJhVFFSYVpJM2paYkF2OUtBZGJxTXIrbEFSd0NvZE1WQ1M1UmZBQU8xVWdMQTNUMHprdUxUeGNpVEY5bWo1R3ErWVRsczEydkxoS3NDWXppSmxHVFRBRnpBemFWV2RkalpQQ2xpTVhXcFdBQ0Z6VkpwN0dGdUxRNk9TZWJ4NFpEZzVpWTJ1K01BMzJDaHVPTk40WXR1Z2drek9MbDhpakFSckE2RkkyMjd5WUExYUkvQlVPRlhvNmx6clR0YVhBRTUvdWJBb2J6bnJoTTJuMndKdHErZGkrb3ZEVzF2L2hPZ1N4VlJRMDhlQUV4bmJvdG9UOVBvV2toOFhQZ0N1dGlXMEJickNvbnZvWnlwSG1jNDJjK2dFVWpVQ2RhcVdNNnhPSFNJVVhWdnA5Um9FVmx1R0xSSmxuZ0tGU2VNN1ZhNVkwVzJCaDhaZWlmdGpOSjFrZyswb0xyOG05ZFRlSkg1cDlSZlhxZnp6Y1VnS2x3WTh1dTJab3QrZHRIclBWejJaeE1NUXNlcVVhbjgwSldwSUVhVGZkZEdYeUdrYlVPUFdHczlaTG05YVlpOVRLa0FqeVlMRGJySElWYW1rcUZYSVFjZGd0bVZFZDZRYzZzQm92Wi9kOVJORnBxbVZxa1JxWWFzazVPTm92UlA1dGc2TE9pZHE2UWEvd0JDandTM0JiZFQ1VUNBNXp3R2JEZ04wRk01YzlSSW9uY3pWT3lxcVQvUVoxTmlNWGw5bUhlZFVUbFpWWEtUeXFzWnljZGE4b2ZFZTdUVnV0c0krbGRNdXFZZS9WU24ydmpHbnpVZHJ4QUpQcFBQNWRVaXAreW93L0syS2NuNEM5ejZLTTlDTmJSSXNhMFVTUFlVMmdWQ1dFeXkxdnMvTmxVdlBQOEFISXpQS1cyUHI5U1ZOOVJpMjNWSHFVV1hMVllWcjdTK2lzZmhhbTZRSWljbVo4eUZOUEQydWRmcnJRM3plODFFeVdiWEd4a3p2NXJaNTl4dFQ2VmIvTCtWWCtNdkpaNkVWM2E1RldmbDZCMHVwdHlpWEdod1czMVEyZnhTTlFnVVJIaVpWU3AzcGE1dFFiN3lIY2ZLWkdMTDV0cmRXd21PV2pLWWw5czlCNFV3UnVkcjFvQTJGcTlLS0o5ZFVlTTdLUFYwc1VDSWdpUVU1ZE9XSXE5eWVkVERhWmhqNjRVK25IbS9RUk5aQUk3V2pNdDU1MzdSUkNQdkEyM09HVHdLK0tLempPSE8xbU5vamNBYVhLeG5Vc1ovb2ppb2dORVc1cEdqS2RVaXJyTkQ4MXFQVjNUY2I0Tmc4V3Q0Zk1mZXFmYm5yTG1MUmxFNCs0amk4SmVDY0gybk85dDl0dnEvUVlMcjYvUWZNbjc3aU50UXNxSUtUTHZtL3dCTW9EVGZWY0ZwZkRmUXp0TVZNckJFbHFpOHdLWlJrZmRwRVl2QjQ4d2JGMWhXS01IYzJoZEF0aEUwMGE4R1I1dWx6cGhqUjBKZ29jMkhXaWhtWk1xaGxUQ0NwVFZTQ2pjQ3Nab0ZzQ2RHbzZwSUtWLy94QUEyRUFBQkJRRUFBUVFCQWdNR0JBY0FBQUFGQVFJREJBWUhFUUFJRWhNVUZpRVZGekVRSWlNeVFVSUpJQ1V6SkNaUlVtSnhnZi9hQUFnQkFRQUJEQUJHL3dCaUoveWVFOGY2SjZUOS93Q25sM3J3cmY2K0cray9mLzNQOWVGUlA4cU45STlQNkpKNTlJMVhKLzJudTlMNFl4Zm0rS0wwazhQKzJXU1pVK2JrWDQxWlhldmpLaWZ1eUNIMDZ6REg1K3doRXoxK2ZVWHo4WkxkbjBsbHpsWDZoRnlYMTVKTy93QzBFaVo2U00ycUw0cWpJUFQza29rOHpGQlZWSjlGVHFvdjVXMEV3ZXB1aFpTdjVTYm9neHFvbnIvOTlJaS92NFJWL3MvK2thMVBLZjZ2OCtrUmYzK0xmSHBWYW4rZVJ6dlRFZXFMOElQVGs4SXYyMkVhalZqWHlrVlo5aFVaYitLcXYxVldUMnhkZnlsd21rcjQ3MEwvQUN0QU5MWjlKWkxTS3JVU3RWUzlmcEQ0SFNHVGpLVEMvWnVWaFh1Wk1makl6dzl1c0VHZWNseXpTRzJTZEw2clA1LzZCbGNwR213M3RwL2l4dktueVc5cloyK1piK3R2K3BveWt5TDk4UHF5TUhPVnkzWGdZdlRJTWpGTjhIa2dySHNJWWlKVmFwbUwxNFgvQU9LZXZQOEE2T1JQWGxuK3JuTzlOVHluOTJKVjlmc24rWnFla1dYL0FHTmF4UGlubFZsbitYcHIyLzBoVHlyb1ZScXVuc2ZXMlVzR3FQVnJFZGNtWmNOMzBWS3RKS0VNZ0JrcUxJVkpUMlVMYkhCWkJqbG10UU1tczlmMEphR1JjaGg3ZHFDK2MyeCtaOEpqb0xhVEtuT1IxNmRaNVFWNHk4Wm1JQTBTdXJXQldlYVFNWXFxNXlsU3RvN1BMMC9QMVhMRUF6a0UwdC9xTzBzSTVsYXE2ZzBsc1RDUGM0enA2d3huNnp5c2lxbG5TWHk3MTZEaDZiVWNtV01YMHNkMkEwbGZGVzV5cXlzOXdSaTB4NmpNQ01aRjhxN1AyVjdwRlNSRi9wRjZSMHYreUgwa04yVC9BRE9TTnFWVVluOStmMStQQTcrczYrcEhpcXJWZFBPMzBoK3BJNVlxTVN5cjlVZGxGZmRWVlJsb1pSWi9nd05qOVhOWTVyMWlvMDMySmpOaTB0Ulo5UVhqQ1ZJdmpidXF6SjVtR3MreUFwelAvSTB4aVk1UFByczhNWSt1S3IvblN5YURhSFZWZ3VnMnBGYXlWNld1KzFxZEpKOVYvVWMyRFN2Z0h4TzBkeTdzOUJkSHZrclZCdVdGRk5sa1dQZkVSMVo3WTJTL1ZHQWxjbWQ1T0ZDUHVibnUrbHJQa3JrMmh4eGpTeGpMVWlhanB3MjNlaTZTU2c4c0FXRXJSVSttYWl6NGNUN0hkR0s2ZWhXUmZMbSt2NHRCKy8xTTgrck9raXJzVnFKODNzdTZFbXYrRFdmWGhpR1cwYjVzV0cvS1NtcVFyOHI4Y1RMTTJWcTJ2RjRzbHVZUkxRbmhSYVZMNFF2Z2hjaStVUkUwZW9BQVkxYkxYa0lXcE5Sc1QwN3E0bUJvU3JHSUZCNVgzVDkxYk4yWFJsRFZLNUdFclEwUThPY0lIcGZrc1Ywc3lIT0FnZE5KVFVzTHBqM1JTTnBYMGNnUGorQk1iZlBsMVlWdlg5WVNKWXdtQkNyZTBaTVR6d1dTS2dMajVyR2F6VjdZT1VacmRCV2tSazkrOVVJVnY0Q1FmVnAzeFFRcHBzelBlZXRqVXpUUVJNRmlSVlR5SXlCdlFLSURkRDA2dlRMNUhOaUtvcm1PaFJrMHBub01FbGlkb2VoNWVVTE5lOHoxSEZnNEZZeXZNUW1sNjFzU2RsWTh4azZ3MWxTTHNwWlB1dTMwSHdWQnM5Vm5rMXVJWHlrUDAvTEVySnRJUnN0UTVqODRyNXFveU9TY2oyNmRWK3FFMURXWlE3UVY4SWtKMWxocWRKUFgvd0IycldldG83cUx0SnlQc3ZraXYzU2xpNnRkME5tVnVLMEtqY05jQlZoVUFkK2syV3FVL1lIUndXU0UxeTNRR2MrbElIbzdOeTBMUkMxTnhmUXp0RWhhbTNrSjFiOFdVcjB3UXdmejJudGRTaFcrOVM4QzVIR1ZMVXp4QUpkaVp0Y3YwdlNETDRqQjZXeUtvY0F6OUZrdEJ0eVdWaEhrczhkK01kbUF3YkZMWjlzVjJFeTVZOHZkMnA3WWNqRDVHVmpleGRVWDgvRThPS2I4ak5lWm05Wno3RVdjem5LdnljWUxwSkpKSGdxVWl5VmdLWDVYYko4Ylh3Q0s4VlZMMmhJV1puZk4vd0E1TGw4dXZsRTh4S2V2MnE5Wnp5MmthSWdrTzgzdFhuUnlHN3Vuc1oraUxsUkpSZk5MSHdib3FZaUZFdkVjeG5FZzNJdXpGOGF1d1M2KzFmS0UwYzJBcnI0dlVUaVk2a2w5K25KWEhtWVo5VGY4dmlOQmh4TW83TzU5ekt0a3ZiVWJ0Q1k0L1oyYkxXaFc4SzJ6Wk5OK3FOQzR5UHE1L3N1SVBXN2V6UTNXZnp6TGFJSHBhSDVSUWhSZ3pJcUNxK2hjb01yTW9nS21tSHlESlIxYTB0R3FOSUFac3cralJLenE2eFR5WTJnNjJNaXVqU1pDTmpDZFUzSWRTM1pPN2ZWUWFSREZkOVFXN3NXeTVuMDRuVHM5THhabzQxbG1XMU81cVdGbGtZSWwrbFpiY2RlakVYMTJPRnNXdlBlc243QjNwbTNscVBaaytmTXAxajVMcWhoNzJIdWpRZ2F4V2huODQvOEFPTHVweVdodW8xUktyLzVmUDZLWWRUZm5ieE5JTlBjVXpQbmJHTkFvazRybVU3NXErcUxsbFNPb0VvMG1CaGQxWTNXaVZTaTJUVEVLOXFabzc4RjkrTjQ3TmhjdE1RTVl3Y05ZZEVOM0o5a3Q4TlJYTk9vVlQrd3FReE1xTkE2c25ydEJxWThnT2lvT0VZb2xxdEQxRDhtc0lvNllHM2VWZHR0NGd3M25xWndmVEprTFI2UE01SE4waUFLMzFrZUtIT282M25HakVTMFQzS3REU2IrblN0S3dha3VvRnMrSU5aWUd5MmRUbzBwT210Z2ErbUhsZGpnTDl0OWFhOVl5WkxSNTBqRXJyUXk3U05RTTFkdTB6OGJNQzJ3UW1USUNzVlN0cGRCTWVMM2pFdzBWOXJvQmVGSGFVL2Zrb3lXeHdxMFRhWVpwekVGbTFlMWRsQmdhc0hsTnpwaTZrcHkrTnlzaDB1eENEQWx1K016dDBheHRPeVVLMTQ2d2JZb1BnY0ttc0RLWXlNT1N2cFNNOUZ0WEk3VkVLRnlWbDR5aFplMFBVSUV6dDBuT2hLcFczeE9lVGFETWlOdlBpWGJ6dHhuRzdOaGwrMDhubUozeFpIWTYrekJJbHZNRHFGSEdhVFRUWjFhaG5sKzBTRGp4TWhRdVRmZnljNjRIN2FETzdtKzJHM3k0MXprcmhURjNLOVhONXJRaWQ3MFllWWZZbTZMbjl1bXIxNkhieVhkZGxxWXVPc1hKVTg4dGtOZk9EQWRXNCs1RE1SRUNJU3ptNnJVSEtscXNNSTUzc2xlOXJPYzFkTElLbEttK0g2Y3BxK2c3S2d5eFp0Snl2RFVibERJNUdTNE9kQmlRYmRuZnZ4U0U4a0ppRkJyNVdtNFRLY3VYVXNWcnc4Zm9hSDhlSU9nc2l4V1VtTDVWaGlMTldOTm5zanlPSFNnUHZPOVNJWHdnN0taTE4wNXF1YUJ6Mko3K1dxdXkwRmk2RXlzY2VSb1Z4OFRYVjdFUGpvMXhXUHBRdWpIcjZ3WU90Qm13c1NWYWJmUXVxbWw5d1ZxMWFscjJmWHVNYTZNSURHdGUyclVLWlpnbjJUQ2hrRGFqRzNzcllGZXpuUU1aV3JSVGNtRDJadUNoSW1KWWlud1U3SVArSFR6TzNZTGloWXJtSXkxcnVCYm5JbmVhOUtONlBFWlRWZzhMTkpGbHVpNEFGbk0rWHFHYnhNR0NzeTE0ODNldlNTYUVZSlRWU1VLUUxaSDFxRTdjT3FMSGdHeXJXZnZFMlJmYWg1UFg1ZmVVSjhxOFJBYUoxOC9xYWJsTFhZMDN1OW5yc3M5TS9IbVgrYi9UeThoS2ZVclFvVlY2WHV4ZUtvWHRST1oxQnh1LzFJbXhvdEoxOTRIaytJVHBtOEZ5ekdlNVJKYU9QOXhIUjdVR2dGOHFvVjlMRlJKejZRaFRwQlBxdFhIUnhXN2I0Y3RoSUs0K3FOZUlzeUxsRDBycmpiWHJwaEYwWkk1OEs4Q2VzTFpXY0lOZXJJMFhtTnhrZlZHUlRXRTgrNUdvK0FOUnZSeUxGNkpMSVM5b2creTM0VFR2b0Q5THhkMUNOWVZyOExLUWdhVjRZUnB5TGI1NFBQcDdWdWw4N25LaG1udU9JQ2Y3a09oWU0vMVFtYkhjeW8rM1hCYThOY005UDFHWTIyajQ1eFUzc3J1aFRSbE1CbzdkRFU1MTlNamNNUWROem5UaDdpdVJyRjlaalllaVpXU3JYME9Wam12bUU2bHo3cWVhUERSRmI5YkE1dTdZSFhtdFYwamEzc1hnWkhqQVdRdzJVeDNOckE4UEI5VkcwaDNxWnkyRng2UzVUbWNQTzhSekRsTWRBMEdXMFlvaXRuMFBPSDh6bDFnNXh3NEZNYjArcmd6dUFmTGlzUHFNdTNuUEo4ZU5HV1d6QnR6c3F3MFM1SzZMWXU2SGFmaDRyajF1N2RTU3RtOWhWc1pnN0UyUkU5ZGJNTWIwY1JVZk9xTjRlWGlOY2RGRUlGWDFzekQrZGU0aVNlYnhEUTZlUXJidjI5elhLa3JYcnpTdWg3aGM5YUNMeStsMFovS3ZjbW1KMDA3NHNmcUE5L0plL0czSkRCRXl0MFl5UTVoMlVCMlVaK1pQbXFqSVJmV1Jab2RjKzgrdk0rYzYzZjVuZkR2NFlYNWxXd1FCT1gzY1hTbGxUUjN1TTZBQUJrMnZIRlpGZEY3Mi9lNTFZMEdRcU11ekdZQmUwMFpuZjhQc3Z3blZyWlBTN0VSUHBlUlgvd0JIYnZjbm1MMFhNOFY1Ny8wdTAvSjU4aGpSMlYrU3doODJZekFyWFduVmFOWkV5VEtCZnF4WFk2bC81OUwzRjdTQ2YyNTVIQjVxQ0lmQlpNM0JaUm9zSk84V3ZTb3J1VjlsUEtIdXRUV1UxZWthdWJubitYbDJzM0Z1MTdOTW9YU1pWOWNyNmJWdmlBbG1XZjhBOEwzNnlXVDI2aXRaUVJmNDk3VU5rTXN5bFJOVlVlTjl6SE9uSCtWVzdOWmZObmlYUW8zdnU0M1Z6dXFEZVZtcHN4MVV6akQ3dndKL2VKeU4rbHhNbWxDVjJRazhucGY1d2UyOWMxZXMvaDd5RW1KNjl4Y2xidkRsZWI1bkxicEV2NUc2QTdNUFBlMm5zMURLTVBjbTZiVWpBWXdpS1BWZE9LSVFtWTZHeXg1MmtmeHhGMUNGNG81MFdZYVAzVmJwUWVKYXJPOWM5UEJ0RlY2MXp1TklkcjAwR1IwbU56bnVHNDlWZk1kNFJZbE9kYTZaMFc1Tjk5am5IUXB0YmxkdnBvSGZXempPbm5QYWZxR2xlOXkxaTh6d25Dc2RUWjVTYk5VMzYvM3I1WVBkWjg2SFNvWGh2ZEJyeHFwOWJDZVBHOUg5a0lVYzd4SkhvbjNHVnlBeWZ5aytUcVdkS3pxL0kvNjNQYjdySkxOcWNIUEl2d0RVb05saEw0VXI0ZFg0d1lNY2U3M05uVEtQaG5xM0JtbzU0eEp2aGFxZHg1cmZ3UFl5TnFCRi9DQ0hGNlZ5NWhtS3c2cnZxZmFSZGhpNTNhb2xLVHVHSEo4WTd0RjBqTXExQUI0b1ZZVHFkZjVtakg2SFJqd1BiZUtRYnpGejJZcnZLTmVON09LWmtkbkxWQmRneEhiOXh4Z2l6bnZTS05rdnp2Ukg3NVBudFEzaUQ3V1dzREhzTjN6bWN1WUFmRFdqY0JsclFNWFNzV3BhTWVSMG96SGIvVTVQRXFvZURoVEZaN05OblpnL2EzN1cvQkgydzYybDgvRXZBTXpmSFlMVUNMOEQ2OW5RZURQdDY0bm9ZZjNod0F1bkJuc1AwS05pSmU5MW1UV3A3a3Eyb0dvajZudEw2SkdmNVBMZ1NrdmdyMTdDeWhQY2pyYVAxZUl0bGtEZVoyQXJaNXg2MURXb3k5UE5lNStodWN0VS9CeHVCcXR0Q0swckhwRlg3VnltWFNVS0hTOC9SYitxT0k5QW1GamErYnR6U1NWOTNuUmZUT2N6am14TnNsU3pkSnlMcjFiVWhueU5uNkhqcy8xN2pETmZpSkZvUHduVTVIMFNQSGVyUXhNVDRTOFg2eXVjT3JKTHpzb04xWEhPcXhkRXhiRnVENXNmbXV1WlNwMG5rVTdxdWo1RjFmSzlPb0M4UDFzVXo5VkVjcHNPTjZxZTF6bUNZMWwrY2Qwcmt1ZDJpSUd0YVdMQkNPNWJtaHFxVzRucldJN0dSNUhsbU02Qm9RdExWbnVQNGR3SEk3YkZyTDk3L2JuTitsL2MxdnVaa1ZXdEZkRDBIMTVMWTZOa0JiSm9NbXBIdWZrWmZwem9CbGdHWjBlTU1LZzZwbzV5SnRac3ZwVVdNeG5heHJJYklkb3dMbGdKZE9waXQ1QUo2V01yK0t4Zm5rQkxQU3h0alNWdGZtQ1g0YjNPcjhhUnM0NVZrYUVlRElSdWlrSFZYd3BMSk14SlYzdk1HQ0NzaFFSNWdGUmJFc05qa2xsWk5WSzY4VUE2N2hyZWdDZlhFYkYzRDNIK20yWFZXeE1FZHE1d0Q2ZGtvdGZqSm5pTlBtRE1YUU1mZTUvcTRJcW1oNVR0clBKOWphNXAxR2s4dnprLzdmZFZrTk4vTXZobDlDd1RNbmNqMHA3NE5DUFprK2hjNDAvU2hPcEdoZHFIakxacmM4NUw3dExOdkpIWWN1U0wzZGZ4M2pFR2k2dDFPdGFOWHQ5eHNEeFhWSno0NWYwT08yV2dzOHA2MEMwajRuejViY2Mvak1iL0FEUFVjVGFSOSszSkhzK1p6dXBXSktOclBiUjVjM2V5dXlyTUdhd0VvalowbVpyVnpKQWUzdkVrMitYaU5DN3NFdTJHd2toOTJTcGZwT1NYbTlJQmUyRW8yMDlzTkNmSDI4OXFpSXFPUmI5UFhZZUs3V1dka1NzZkNMa3ZYWk5MQjRZZEVPbHQwSWJNRG1KWmZSWThYSkc2SkhqTnh6dWFuS2s5TnlSTHIzN0RtbXlidGN3eXhUakozc2wyem0xc2tFV01Yb3M4VHRZamF0QjZPOUZFenFQR2IrcEpRYkRFMVp4NTdFR3M3MGVPdmcreDBKTXhyZVlYTnI3ZGpiZ3o2TSt2d0Z6a2ZNKzA1Qm10RjJ2d3JUcis3NUhBOFJwWUxtd3cvT2lPQUZySHJnNVc0ZzVMeWRQUDlJeGQ3aHhJdEJ2K2NkRzB2dHF6MUxqZVN5L014dHFvTjErTHY0ZlV0ai9OdyszTzhBNlEvbm0rL0pzNEk4dDhiU2JzTWI5WmFwU000UHNJbUthcGNTcm9BZ1FyWkh6VUpwSUxkdzZPMU1lcVV1RVBXOHRzSmRTWGRxL3AzZ3A0bzRPcWlDTmRqbzFhc2lEck9nRFBjeWR5Nkw1ckxVV0szRDZ1MG5DOURHVUd2amJOUmlaV2pVbUlrVkJWWkozUXVtaG1pbGpKMEdJS2Fzcy8zaXQvaks3eHIwYmFqYXV3NTlwZWJkVWZvQUZtYXJVT1B6WFM4VkZKTFdobU5jMDM1SEgzaCtUdEFpWklWcnZhamwraVpuK05FcnlpcklVanFlR2VCV2x0eGJURzVLdmIxbWVuMFhMTlhCbkh4YllHT3pZMEtUSTEzbk5weG1TK1VyYXJOOUJueEJzejIyWEtkL0E0SW9jSlZKc0hWNWJrdEdVNTRuNnFLMlNUcUhTZVlWdFh6OGt5TytNMytSNmhUczh3NndOYUExZzZUcUh0Mk92ckpITHQrWUVjZmpPc1RNM1BNVDY1TGNoZXZhdkRrNEFuWVFVd3hjMTBJUWJ6OVZDU1ZkTUd1RE12ZjVyWWdyNTZIVGc0Z1k2cVRrUWZOYUd3aFZOVUxrRnlpU1krVzVWdXppWmpZNktOR1c1SXJGVnlXeGY3cGJwaDdseFdSTmZVckpWaHpzWmlvVGRJSGtJajYwVXNzWldPV0xUTUFSMUpyNG1aNU1GdXFlUjBReGxLVkxGK2xXdzFibVd6dG9FeU04dHNObnRMdklLeFpiN00yVkNaRFNCZDR5cVozUDRRcDIwNW5mb3Z6MmRVWVdzN0RrbTFzaGExck1uNkdSdUdPaDVmbldJZ0ZieTRHbU1iY3IwM2NBTXBMenpTWmlITzducDlZWDFjRmtLZXNFRjlVekdWdWUrNFF2MUl2MmM2SlpvT2RhemlXNG4xT0RKTmxBa0JHRjl3ZVJlczFSNEhYVStwNzNoZWlneUhVSW02N0taN05ZZlcwS212eFUwd2oxUXk1ZS9uSzljdXltV0hYZVFVc2Z0NTJaeTFjNSthelovWTV1MjZ4Zkd3a0cyTlJ6UFYwbldtV3JvVWxiR2svdGM3T2FPT1QxVzJ2WGNoTFA4QWVHR25SMloySmJXSEhqQkFGSUN0M2N3VzQ1WTdZQld2dGJPbFdGVGozVTVaUmVqa3pHZHhlV0wySUwwTmtmdXM2TjFOQXRRems4OWlHOVkyMjl0cGhBMUdzU3BaN29VK3RyeEdoUWdjREo0THU1Ty9QV3pleUUwQllISGdzWm5ZTE83S1ZkYnBjNHlwK3NMTnpMNENrcVVCWHVRNnQwTmFWOW9NU00zT0h3UkhxTmJQRzhYUzFHcjZaenpyY3ZiaG1lRlI1Y0puZTg3OEJ4TzVRQkd3STZ5WjFuTit6ZGs3bFQwNVBZaXdtZjhBLzhRQVRSQUFBZ0VDQXdRSEJRUUZDUU1OQUFBQUFRSURCQkVBQlJJVElURkJCaUl5UWxGaGNSUlNZb0dSSXpOeWdnY1ZrcUd4RUNSRFJGTmpvc0hoSUxMUkZpVXdORFZGWkdWemRJT1R0UC9hQUFnQkFRQU5Qd0QvQUtINFJqNHpqd1FZOFdhMlBNM09QQ05NZUx0Ykh4TmZIZ2k0L3Vvamo0MkNqOTV4NHpWZi9BSEh4RjIveUdQL0FFditMREE0OWFGZjR2anluaS95di9KNWZ5K0p4NURIaWNlQzQ4WE9QZFRIaWY1UGNSdjhoamxKSXVsZnFjZTZsNUcvY01EaVo2aElCOUNiNEJzSTZTT1NvWS93R09WUTlFS2FIOXBoYjkrUC9OYytFMGcvSkVXT1A3TEllak1zdjBlVXFNZTlMSlQwS2ZRQmpqbWEzcEhQS2ZtSTlPUENTR1dZL1dTVS93QU1lRVdWMHBQNzFKeC9kVU1DL3dBSXY5bndHUEU0OEIvSjRYd09TRFVjY3BKdVAweHhLN1hacGdjRXBvdHBJZm1jRCt2NWlkbkN2bVdheWo2NFBITCtqRk8xVTQ4akl1bU1mdG5IRnFqUHMyWWcrWmpnMGdlaGM0SEg5VTVaRWovL0FHV1p2cStGNGlwcm1mOEF3Z3RnY0REUmJWdnFkWDF0Z213V1NWWWY4SU53Znk0Sjc4Mmsvd0NMUWI0QnNSU1FTUzM0N2pZTU1XN1VzUVJiand1YllUZXdxcWxRUUJ4SkRHNStXQncyY3duYjVvdThmN2Znelk4UXRoajNjZUlYZmc4QUFUaHV6VHEycWVUeUNEZmppY3p6T0VWRlFSN3lvZXFucWNKM1o1dHBHbmtCY1JyOHNSN3RuU3hpUUw2c2JJdU9VamZha2ZtYXlEOHQ4THZZYllCVjlXYXlqOW5DYnROTEVha0ErYnZhTmZwZ2NLak1KdHRiNUFwRU1jcUhJb21paUo4UHNnb0krWnZqZ2xYMG1yMU14ODltTHVmNGNyNEtjY3B5TktTRmZHMHRTVittbS9NRTRBQU1FL1NDV3VrREh4aXBrQWRmSW00d0gzVkVQUlJZbFJyOW9TMWNvc0RnRXExUFRaakVIQjhBbExBMzFKSXQvTHlWUmZCN3pDMlBpZkZ1T3JIS05HdmprN0QrUnV6REh3UHFjTjNLQ0hYTVI1dmJkamkwS3piV2R6OGJtNFQ5NXhSMjlwbmtKV25odnd2YTdTT2VRM2s0djk1UEdWaStVU20zN2JIMHdpM2pvbzNSbUZ0NU5oMUl3T1p3ZXFLb2dtUDhwNHlmSUVlZUNlclEwYW1WVlBocEIwTCtaajZZVWJoSXkxVlo2QWthRVA0RXhIdWZQdWsxVVlxUkQ0Z3VkL29MWVBhcGVqZEdLQ2lBK0tvY0FzUE94R0hPK255U2tiT00wWThOOHIzVlQ1ai9BRXhiN3pwYm5MenlzQjd0SEFRQnlPazJ0NGpoZ0Jnc3BpanlTaUozOExBT3dQUGlDT0lISENBQTFGSGxScnpHbmdaNXlGQThtTzQzc3h4dUJpbzh3TGhCeVZrbzR5Q0J3R3NxdytMQTRwR2NkMk9QZVd3ZXpMTW0wbCtnd2UvTUJFbytweHpXRWwvNFk1clR3bGIvQURZakEzKzBablZhejY2UllmdndPQ1V5S3FqQjk0UnZnOTcySlNmOTA0UGNCa1ZmMlZRREFhekxTMDUvM24wZ2ZURWltUksydXJGbEcxOFpMZ0tMZ1dGaHV4RzJsNWtacFlSNk51UWowSkdIZFFZR3U0blBFWDBpMWdiV1Roek9vOElpSGJMZG02Sk1vNVRTamVWK0VXQnhFaGFmTVJUTEJGWURnbTRYR0JKOXJtZVprU2tEbjdQVEVnTitKdXI0aHNVbzZ0Vm0wd2VDazhBcUFCRitRd2h1MUxSUyt3WlpBQjNXbEF2SzN3b0NmdzRSTHlaWDBacC9aMTArTTFRYnVmcmM4cjRxSDBSVENqOXZ6V29Qd0x2SVBtVDZnY2d3RFpoMHFyMWlvb1grR0syemEza2pENHNDTU5TOURlZ3VTbXNxZ0R3QmVRU0xDRDVLbmtSaUlIWVp0bmxmUjVjOHZ1Z1BVQ3FuYS9reURITkZiVWNEaEpWTlpmcGdmMGVXMHU4ZXJBRS92d2VJZVF6eWZzcHF0OHlNZjN6aEQrd3V0djRZQU4ya2tXQWZXVnJuOW5BYmZIUWJhb0h5S0JSNjJ1TUEzRTJZSkhIOHplNXdPN1BYeGxyK1ExQTRCLzdweXg2aGZxcWY1NE50SWo2UG1NTnY1RnlMWWppTVQwOWVrRUxxdS9jTDZSODczR0l3Q1phTHBCVHlSU254SXVSOHI0aUIyVkZEbTlKdHA3QTdsM2c0b1hNRlowWXppampXc0paZXhUOWI3UW01M3J3RmpiQkRUUlpGV1pkTmFsWDNXSzJFamVvM25saWdjeDEyVzFWMGxpazRBVkVKUkZzeHNVM3RpYWlNaVUwVTRRaTYzVytpN0FnMkI0RDFHQ29lTnFRTWlWSTQzRW5lVS9oc2ZIRWU0Q3RqRWtZOGRLc0JwUG1NTkplcDJPaVBiZVN0dkNnZWx6aTFucTBVMVR2NUZnMXdQU3d4U3lpV0tscVUyTEJod2F6bXorbDhHWVN5em5Mb2IxRmp3Wnd2UDhWOFU2Qlk2WExPa3p4VXdQdkNKa0szeGZmRlFydDJ2NXZjUmo2dGdieTliSVozK201Qit5Y0x3cGFWRElQa2lkWEE0VkdhMUFpUUR4SUJBK3JZZmNhSG81U2FwRDVhK3FPSGdXd2JIMi9wcm5odSsvaUlFc1Q2Qy9saGU5ME02R3haZFNBZis5clNvdDVqRnhycHM3L1NGVTV6VUh5OW15OWRBSjljRWZmUjVMRGxvUHhDV3JZU0VmWEM4SUp1bGRYTko2YUtXSmhmOHd4eFZJMnpIYUlvNUI1M0FPSkRzOUFYSzVXQUpOMUlubTFFbjFHSTRkVnEzTHN1b1htYjRaSWk2aytaM1l1U1RtTkprdVkwRVNDOTFGUkRJSm9pUndhM0hHVWtRWkttWVpkbCtaMGRMOGZ0VUZWSE5Gd3VOYUhDeWh5N3dWbEhTMWtxbS8yR1kwMGtpQnJLMWtiSFJ2TDRqRERYNW1sUG1ES29BTThrVWtUeHlxUVFBeFlOcEFOaGMzVFMxZFhSVW9yS0YrN3M1VVNSWGhERWkwbGluZ09ZcEVEdFFqTW94TVg1YkFrUHFUZGJTV0I4UmhPcDdZdEM5VFR4L2pOTnJGdlZWd3h0SkJRVkdpWHlPa2xYQjhpdU9JanphSTZmMmlNRCtzWmRPcjJIcHZ3KzdSVkJxYTUvRURwYjVuRG02czFrYmVlT3VNcmY1ZzQ3SXFKUUFQbC9wOU1OMmNxb0NYYS9tRjRmdXd3Nm50QUV0WEo2S09lQnd6WHBIVyt4VW84MVh0bjBBR0FMeVI5SG9WeW1oQUh2MWsxNUdGdWEyd2grM3FPaWVUKzN5cFlrRXk1cFduWngrWkJ3RFlwbVZmVjlNY3dVK0d5aktVaUh5dWNBYjZWcW1teWlNQWVGSlFwdENQSmppYnFvYVNncHFlVFNQR1d0a0wyOEdaQmdTQUNrZnAwN2tIZ1N5VVNJZzlCZkZrVVBKVm1yWFdleVM5VTlyYnh4dVJpQnpUVTIxeWZMcWhCcDdkOUs2N0FnZ2NOM0xDTWlWUzBHYlFVbG5kUzcvWlNxeWRWQVJwNnh1UnVHS3NOR2p5VDA5SFdMR2czZ3RHZ2plekVBTWJBK2VJNVBZS2FvYWx5Mkd1alhTMHMxcFlFQ1ZDRkk5UzZ0NExEZXVBcGhxSzRkRzRLU2JRNXV3a3Axa2VLWlFpU01rZ0JKNEFHMktqTW5vNmFnT1kxODJXUUdXWlZGNEpXQmllTlhkaVUwalNMa2pFa2NweW12elE1WUtrd3huUWdpcktZSDJvYWVycGtBSUpRS1d4REswMGRCUzlJM3JaMkpmckNyeStxaCt3bVhtSWdFNUxpbXVrQnpScS9KSlpFdi9SenVHcDVRZndnRTQ1VDlJK2pmdDlBL25IbVZBeE1mcVJqblBsRmFuUzNJN2ZGRS93QnZDdUV1Wk01L1JmbkxRVmNKSEZwY3RuSVBxQmlLNHFNa3oyay9VMmV4ZUsyWUJYT09NdVRkSTBhQ0Z3T0ppY25adVBOV3hNTFI5L01xOWZCRkhXc2Z5cmdnN2JQYzJrRWxkVmtjZEYrSjhsQnRnL2VkTmVtRnkwM2lhYUE5Wno0VzNlZUVIMm5TZnBXMW96NTA5UGUyRUd0Yys2YXVmWmxYM3FlZ1d3SThDNEFQSnNRc0ZqNlRmcEl6SC9rLzBXcHovd0NIb0l0TDFIa01JdStpNks1RTNSM0lrK0JOQUU5UitackhGck5Ia0ZJREs5djdTcGtKQTlTK0pwdG50czBubHphc3A1bXN6S1FiTEs0VURtVVcrSUtkNUZDNVBIVHhHd3RZaFFOSU43ZXRoaWxwcHF1MVU3TXptT05tQUtnV1Qxd0VRdkxSRjB1V2E3RWc3emNLY1NQVXlMSFY1ZXJFYlNvRUtrRzNWR21MRnFkZEU4QWxpRFBMSkxhM1BkR1BrRGlxOW9sa1JLY2luSmRxV1BkYmVGc3hzUFBDNWZVekxGQkk1RVpXZ21ZYk4yMzIrMFkzTy9pTU81Y3lUc0pRcFU1cm8xcGUwcUJrQkNYQWF4VTRiTlJPWGg2UEUwUUVOUkEzODRpTW5XbFpoRWpnSFo3RW8zdkE1WG50WE5WMEhUQ2doRmZSaDVqcmpwNnhGYU9zWnIvQ1ZJVWdXWXRpV2VTT092aXJrNlI1SE5Za2FhckxwbDIxTElBTE1xTGIzU1JoV08zejM5RStkNk5KOGFqS3BpVjlWQXhFOTU4MjZKaHVqL1NDbEkvdDZVMmprYnh1TitLY2R0NlU1SDBzeTgrSkNhUlVXOFZ1Y1UyNDVablJXZzZSMG85MktxQUcwWWU3S04rS05nMVYwTDZiVXlVK1lVaEI3U2lVRkpGSHZvUml0M3BMTXhHWFVQNGIvZVc4UjFUNWpnb3V3a3QrcHNqVWN5RDFDRjhUMWZBSENyZWFwcVBzOG15Z0RtU2R4QytmN0p4UnZaNXBsdGx1VnVPNURFZXFDUEZyc1BDTzRKb3BmNTNuR2JpMlVaQTN2U3UzMmNiRGpvR3FjODJYRkxMc0tucERWaGw2UFpTNE5qVFpmQXRnNFU3cnhoUWViTmlZaEhOUFJReHdVRitFS0FjR1BFbmVjVzZ0SlNkVy9yencyWHlWSXFKZC93QmxKTTJrVzV2MVNXUG1CeXd0TkVHWFFCeGxBMWowL3dBOERvL093MW5mSlo0ci9RZGI1WUt4V2VJL0JJTVdsVW8vaWxkSnFQOEFpR0lEU01iaTRBU2Q0bSttMEdLWkoxWkxkVFZzWXB4Lytkd1BQRlhRN0J3aHVORGlXbFkrbHBZV1Byam90bjd6VHhiUTYyRUZRdFlxRUhrWTNyQUZQT054ajlHZlNyMm5McEtPbUNlM05WU0xzSmpFUmE0YW5wblE5a3JJVU9PbFBRMm02ZTBmUlY1VFVwRm1kWkR0S3Y4QVY4OGhCZFkzMndLKzZDdmJpeG5lWnpVbGZtend0U1pWVlZZT3BLRE5FWm5nTlF5V2VPb2NJWm96cUVqRUVpamdhZHEya2MwMVpFaWpVMHNjcVhFeUR0TWpCeHAza2M4U0lIb2VsM1IyTUptS3huZzhrVVpLemp4YUVsdkdQR2phVTNTVG80TmptOUFCL1NCVnRyMDh5aEJITkJqZkhTOUpxU1pxYnBEa0w4bG5kTE13SGl3dmlFZzVibjJYRVE5SmNyRGRoR2RQdm8yN0pZSEVCMDlJdWs3b2RNYWM0MDRFc1JZQkJ2UGtOMklUZXZyWldEUExMekx1UHZKajVYV1BzcUMyL0ZKVWJMTU00cEk5TTJaU0EyMkZOYmlibXhjWHRld0pZbHNUMDRqeXpvOVNTTW9vbGM2VmtsWk91ODdrMld4dmRqcHNidmpJMWtUcFZubEdncHFhb0NmZVpmUmFMQm93YmlhWmJtVjdycTBDN3BJdE5UR20reXJhOWIyQloxKzZRK0M4c1JBaW9uTXVwcGF4eGQzUHlGaDRERGMrSkdLbm85TVZLdC9TcldTaVZEK0c2REV1VmJaRGZqc3BFY2o5bStNN3l1cnkrRTNzQThsTyt6K3JoUjg4ZXlwSlkrTWJCaVAyZFdKTTNJMm5oRFdKZFQ2Q1dJL044YTNwcHQvWTlvVzhaSjhGcUkxR0txRVR3UkVjS2hDMGdqL04vT0k4VjJxR25ySFAzTUVnME0zNVVOUE41YkI4ZE03R05Xc0lobWNMa0dKdVdtUm5taS9EV0kyS3VPSElPbjlBbHc5VlFNQXROVWtjcFZTSUpmdXowYWUvam85Ty9Tem9GbWRGL1hJVnRObXVYUis4c3NKVE1ZSS9FVGdkczQ2ZjVNbFBtdERLZ2FtcVkzKzBpc2VDdkRMMTQyN1NYSUdPaVVscVhWTDl1c0FKTWJLZUpWZTZlVnlPR0o3elovMEZsT2pMODJmdnkwZzRVMVFlT2tkUmpoWkdYTWNrelFHR1F5cHVrZ201eHpMeWs0amRxTElReTVZU09rUFJiTUVDUjF3Rjd4MUVRNnJBMk5wRi93QWlCbDFRYWZwTjBIelFrMHlTazJkMEhGTjVEWFhjM01jR3hYMUg4OHFZK3JKVHhqclRUTVIzN1gzOGlXUEZCZzBwK3lRMlpLSmJoNUQ4Y3hCQThGUG5qSmFJSEpzb2dBRVVGNURCRzVBOTV3eUo1SksrTXNEbUV5ZG1XcGRTclRIOEtrcWc3b083SFNTb1p4VDBxQkVob0lEdkFVY21hd3hBZE1sUkdkTWdQQTI4RGdzODFYTks1Wm1ra1hjU1Q5TWFUYkhSdnA1WDViVitNY2RiVHhWTUh5TFFUZ1lMR2tyRHE0UlNxWW5QeUQzK1dPaW1hcVpCNFNReVhIeTFKZ1RKbVdXSjc5RlZMdFVIeVYyalBtaHhUUW1rbG0rQmlIZ205RmNSdDZFNHptT1NockRLYkdrbUpDbC9JeHpCWDlDNXhOVXlNajhvS3BHQmxBOHRRU2RmRlhZWXBuYVZCYTZ3VG9TWGpQaWwyUHJIS2NkSEFocEphdDdTM2hPeWhra1BpTjFKTWZLQ1hBUjhwNmNaQklSSE1sU3gwczlqMk51Nks2dndTcWo4SlJmTEprekw5R3ZTcUZOTWppT1JuZ0tBOEpZWk5ZMlI4Wm9EM2NWMWVoTUtFbUhvbG1jNTNiTHh5MnFhOGxOSndXN3dHenhyZkxZZ21VNXhMSURTWnpUSHNRVHR3M2pjc2gzTURwYXhBT0taMkZma1ZSdXFLR29YZTZnSGV5bmlENFlyWm9xRHByUlFBRTNhNHBxOVJ3SnZkQzNBbnF0MnpqSkl2YVpZS1lrUjV0U0JkYnhmRXJSaldoNGdEeGlzWjh1aU9aWmRBWGpPWTBrdjJZSkVmV0VzRXJCVGJlQXpMd0J4SEd1V1VOUVRmU1pwQXJPRDUzMWZNNExMRFNlRWNkeXNDK2dVUUhBekNaNmNIdVFVZE9rRk9ueUU1UHJqTmNxbnpXb1B2V0EwL3ZjWXlqOUgrV3RUZzhDWlltbmMvTm54RlZsa1h5SXZqOVRvWVdIZGJaM0JHSUpIaWNmRXBJUDhNZExNZ05ia0Nubm0rV0ZxeW5RZWNzUXFvUjV5REV5WFFOanBCUXRTVlJQY3JJbEM2dnpyb2YxTFk2TlZqd3VIM2JiTEo1cjM4OWpPNVA0S2p3WEV0THNhaGVJa3AzM0tmeWxySHlJeFZ6S1RJZXp0Q0NJNVQ4TWlqUzN4QSs5am8xREdtYmM1Wm9JanBnclFPODhST3lsOFVOK2VKWWhCWG9ScXNFRzZkUGZNWTMyNzhKUHVZcVpnYTh3RGF3eDYxc0pyRDd5R1NNNlhIZmpJUGFUQ1Uyd3p6STVuMmtPZDB1Z0Y2ZVMzYmNSaTZ1TjhrWVIxT3RNVWN2dFUwY1EybVlaVFZLb0xTcUYzeU9BbzJxTDkvR29sUzBpRVl5dWphR2t6UFpDcHBzNHBaaGNoMDRWVk5PQmQ0dTkyNHlrcTRRckhDdFJON1hWWkZ0VFpObk8xaFZVa25DT1JyYXg5bStpWmJzSTBiSjg5V1pyd3FEWklaMzdUVTVQVVdZOWVCN0xKMWIyb0ozZ3ovSko2ZlNLcEcrOE95NXhURGVRdllrR3REWXJhS1ZKc3RwNndnVlZLZFlab0NUdWtRbmtlWlBpY1pCMGx6RktXT3JXNlVjZFRPODhDa0h1RldySS9Kb1JqOWNMdkhHdzBCZjhBRTJCbmRNaitTRktjQS80VGl1bnpPaUdzVyswMFFrRDZ4U2ZzNGJKSmNscmY3dVl4Q3dQL0FNbFBJdU11b2prR2JubmFGMkVSUDVDTVpuVGphNmVCWmY4QVRHV3c2SUEvR1dIdWtlbkE0a3JtbWovQy9XeGxOZEZYMEU2ajd1YUp3NkgwdUJjY3dUanBnZ3p6S1lGN05HMGprVmRGNndWQWxqdDdtelBQRmVxRlpEd2huWDd0L0lYSlUrVG53eGxONHN6cEhYZFVwWXBKSElQQmxMSTJOa1pNcGxxdTFMRHZWb1pQalEzamNlSXZ6R0tlQnpSSkliTlZROStCajc2MkZqNGhXeFJQcmtab1NSVVFDNnNYVHZXRjBsajVqekNZQlJoRFR6WG55ZXE3YVJoaHhRazZvWk9CVTZUaUZXaHBKNmthWW9WWnV4NVV6dHZCNDA4aDl3N3N4bk1XWDEwdDFiTG4xYTloS1YzcUZZNmdSdlFuV2wxSkJyM1ZxK2ppM1E1cXA2KzRKdVNwQTY0Q20wbmJpTjdyaUZ6VlRaUFI2VW5TWW5WSTlLTGdDUm1GM3BpUWtwR3FNeHk4WWhKQlE1dm9BZ3pBTjFIUmhJQm9MZG1SSkJZdUxTcWpkZkVyR1dwNk5WQWVVV1VXZG9MM2tqa1VkcVBlMXVHMVFFQ2tqMFZlV0dQN2VqdngyVEQ3dGgybEgzTDJzTkJ1b29pajlGczFOVXFETmFlWU5yQTNsd0YweFNBc0xvMnRjWnpTakxNNnFNdXFVbG9xK2RHV1Iya1hnSk5VWWIxdWVaeFVLZG14NzdLU0ViMFlDRS9ueG04QmtvTDdydkhlU093OFRFN0QxUzJNeW5XdXBvaFlBNWpBRzI4QTg1VWVRangxakhTS1g5WmRIYXc4S0tzSjF0SDVYY0J4NWx4ak8wMlRPM1lwYXdDMGNvUHVOdUY4Wk5VdEJORE9MT2pEZGJ6QjRnK0dLS1FPakwyWEhORzhRUml0UmFQT0tidjBOV3ZCVzhtSEE4RGhsOE1WVldhM294VXlibG84eXRwTVpQS09wVlJHM0lPc1RjamlNbEdpbVd6SVFiRlNEd0lOd1JoVUNWcUVYRTBYQVMrbzNLM3lQamlwbTI4Y3lBbjJLZmdKQmJ1a0FLNDVpeDRxTVVEcWF1S01YWUZlRTBmSnQzRURjeW4wT0lFRTJhMGRQdlpUYXkxa0E3eWtDekx6RndiRWJwbEsxVkxKZHFWNG5hNUREdlV6bTV1QmVGN2tidFM0b1dHbFppQlBUeWtmY1RIZ1F3M0xKdlNSU0wrSm9ZdGhKUjF4WlVLcGNCU2UxczFQWWNkZUErOG5DZHhUaHN4aEVrdVZhemRFbEE0eE1kNnNwMG50eHNHdU1WU2UwVkZISE9KRm5qNG5VZUQrVTFyOHBSZnI0UW9YcVpvbWphYVVDeTdkUWJscmJsblE3UURuS3VOamFtenAwVjZxalZPRE15N3BsWGt3RmlPVWJiZ3NTdkgwbXlxcTNCcjMyY3JSRVNiTitJTERVclc3UXVNRE1vMWt6ZW1vWmFvRm9pVEd3V0lCbGtNUXRJcktGa3RxRmlXdm1kZlE1alJWMVhNQkFnbFJwRUZNaEFZcUJLeUZuM2pUcDd1SjlOSFd5THVLUkhkRElmbGVJK0RKSGltdFV4U1UzOWJnSkxuVDhjYlhrVWN3WlZ4T3FNNXBUOXBTVlNkYU9hTGYybE4yWDMwTEp4RnhDRFUxQXB3UkRXS0Qvd0JvMG5pakVYbGpIV2phNXRiVUZXTC9BSnN6T094RlluRUFIZ3pXMzI1OFJqS29SQ3RkQTQwNWxDbkNLYndkZUNrK2h4RTJpV0oxc3lrY2NaclRtbXJxT1hkSFVBOWtFY21CM3F3M2c0Z25aSTJrN2VtKzYvbmJIRzRGaU1VeXIrdklndHR2M1JXS1BQY0pCeWJyY0dObytCTmpma1FSekJHNGpFemJPU0Y5NXAzUGR2ekI0cWZVY1JpUC9xTlVlQ2pmOWk1OTA4aWV5ZDNBN3FPY3RtTkhER1dlallucnVJKy9FM2ZpK1kzOGFlUDJpc3kybklkNlptL3JWTC9hd1AzbC9nd3hKRVJRMVJqTWxPOEpZZ3FCeGxweWJobzdhNG1Od0FicTBFWXFhZXRwWE1vVUx3SmtXKzJoNUNVZGRPREFqaFJ4R0dsclZqUVBvZmpZTjFKSW00bU1reHR4VW8yS2lVUFR5VWJ5UG8rT25ZN3d3SEdKL3RGSEF5TGlSTmNkYlJxcU5BeDk0QVdzVHgzYUw4UWh4dGdJc3pvbFl5MHJqZ1R2dWpnY0dEQy9KbTRETUNXcnFRcjluTnJHbHkwSkFOajN3aEJCMzZBZUtaZVpkZWRUVHpSUm1DUzBUSVZDaGxLeXlNdWtsbkhQSFE3TlRsc0ZiWDVUUzA5UTFFMFYwVkJVNnRJRXhrdVNOVGE4Q0ZrRFRHNGtCWHRYSEVFV0p0OExqRlZWRTVWbUp1WG9YdmNFRWZJa0Q4YTRuakVrOUxTbThWWWg2MnFQVHpQYTBqbjFrMzNYRkJPSjBLVkhzOVpSVGcyRWlPQmVLUzR0ckFLUGF6cmZjSURxa1FwN01KRHlrTVl2N05KZmZxakxRc2Q0TVp3cWphUFVsa2lyZ09HM0M4VzVDZEF3UGVCN1dIQUprZU5RbFNQZlIxNmtnUHZBNDNXS216REZGRmRaRzQxc0k1TjR1dmp6R0NMWDA0akozTXQxZFNDR1JoelZnU0NQQTRad2tzRE5xZWljOEkyUE5UWTZYNThEWWpFaWFab1g3TWkrQi9pQ040T0ttNmphV0ppY2NZMlBqekI1ZzN3RjAwOVd3dW9YK3psK0VjbTVjRHU0UXptV2VocDdDYWhadDdTMC9kS3R4YU1uUTQ4RFpzYlRhbW1veVJEV1NnV00xTElBVEJQWWRhTmg1TXBHS3VYVlIxNlU1Z2FtbTdMTElkK3luSEJsSUtTQ3hGeFlpZTh0SkxCQUtkcWFmaWZFUnVlOHU5SkJpU1lVa3JOQnJNYkRlRWRDU0FiYngxdk5HR0dxTCt5bUl1N2tiNUludllzYkVFSHRqNHhpcmpaQzZVcEVOUVJiVURwRjFaU2Q5eGNnamR6S29KNXZZSTFrb2FwYm0waGlPNXZCaHBQRGdNVmtpS2N4Z29wSHBhaFpsdHN4TEd3T3pkVzNNVkpRTnhOc2RJVFpNdmluamxxNk42Y3RLVEloUUV2WU9wWnVST0lVMnRQczJJTURBM0tIdkJiL0FKa0o0RUd4akd5aU5Vb1JaajNTamVQTVdQbWg1WWxZdG83YjBxRTd5ZkFlTEFhRDNnaHhHTmJsRHMyZGp5bFhuZmhjbmVPODR4VHVCVGRJYVFQc0FmZTFxTlVKK1dueUF4T0EwRlVnalovVVdPaVQxUWhzWExHQ09ZNjZaanhJVit0R2ZRNDE5U09WZzFoNEhFUnV1b1hCOGlNSS93RFBxSTc5aVQza1BORDU3eGkzYVFBakZYQ1lLdW1rdW9sUW0vSHVrRUFnOGlNUFVlenNaV3RKRExiVm9ia2JqZUdISHl4SXRwcWVZNm9waDRNdC93Qi9FY2lNU0lHbFNOekxKUkh2UnlnZFlBSGc1RmlPZUl3VWhtcEl5MVJUSVNUcEE0U1Jna2tJU0NMblNSd05XcXU4a01qR256R1B0Unp4c2JCVHdLdUJxVTNCNWpGT29ScTZXbkczVWNBdFFvNmtxSGhxc1J5SUdLc3FzT1czQ3dGajNJbjRPcFBaV1RlTGdYUEV2R3NWZFN3MDRTUGFMY0ZtWUtVOVJ5TjhROVNyb2FXbUVrTG9EZERjQW9vc2Q0QXhzUTBxVW04T2JYRW91R0drajN2a2NTMHlWTmZISGVwZ3BWZGlWbEw2R1d6eDZEb1UzdmZFV1dVZ2l6Qk1wZEtMTHdZVjdPbUlwb2E1YjBZaXd4VWt4eTA1cU5wRlJNNjIyTUlNb0JqQlBWTnV6YkVoRTlWbDgweENGT0FZYy9LL2FIeGpGSSt6RlJISFpvWlNDYmFoY0VIajRId1U0a2N4MGxkSEtHblVEeUp2Y0QvUnNUZ3lSelUwUmlJOTY2R3dQbmExK1liRTQyY05USXQ0bjVXWlRaMDlWQkh3NGw2OGxMUXlwTlJWTitCYUFneE1QeW8ySUI5dFhaSFZlenZwK09ubkpVK2l5WVlYa0lvZW94OFNxa2o2SEhGQkpFNmcvVWJzVEpvbmpOVUZMcjZteEJ3SW1rRkRXenBad291UXNxa2cvbVZjS1NyYVpWTmlEWTRrbTJ6VWM0RFJpUUMydFNDR1ZyYnJnakdkVTcxQ1phOG9sMFFockxLSkJ1c3hCNmg2dzU0cHAwa0ZxblpNd0RYSzZnYmkvREZmTzhxWlpta2F3TUdZbGlxeVJCMFBsY0ppR3VDNW5UbVgyaXlCN1NsRkZnSHRlektRYjg4Uk81OXJncEFKUkZjKytMbnE4ZDJJNXhLbFY3SEtMQUMvVlhzQjFZQWcyR0pra2tscUs1NG9DWkNqRkNCRmZlVzA3OU9JNUZXdFdta1ZZeW5BNmlEcmZkNEtMNFFwQlI1Y2tDd1VqNk9xb2xjdDFodTRibEhoaWoyY01HUzBFR2xOa3FpOGJQWWRRSFZZQzVBeEZUUSt3SWtHMHA2WU1vSURzWTJkbERFMkFKd2FCSktDaGdwZ29wb3Rub1hnak4xaUNRdW82UWJYeC8vRUFDY1JBQUlDQWdJQ0FRUURBUUVBQUFBQUFBRUNBQkVTSVFNeElrRVFFekpDVVNCaGNTTXcvOW9BQ0FFQ0FRRUlBUDRBL3dBci93REN4OGJNMzhaQWQvVlQrTmZGeTRQbmNzUy8xdVdCMmViaUVITmYyL1U1Sm01bHRLYVlpVWt0QkI4WExsaUQ1cVdCQVNaaisyNU9OQlByczMyRm5ic2NZTUMxQ1U5amxVZFpzWlo5Nm5qTEFtZnh2NHI1c1N4QUpvVE9Fbjh1L3N4djdzMUdoazdkWUd0NWNZbVpNREw3RENBa3pHWXdneXdKY3lnTEdBSDQ4QkJSK0daVm1UdDBGQTdzbnJBdEFpcjIzSWVsSUpPOENvM1k5Q3pGTVV3UUNFQ2VJN1BNaXo2N243Ujlkb0FSMlFzR0M5ZlZnNVRQcUV6SmpObUlhV2dXYVVBdXdMMmNqK0k0OHpNVTlCR2M3SEdCcUJkMEZWdW9LR2l1NWdvN3JqRStwNkdSTUdVbysvR0RIMWN6RXlKZ0pHNWR5NE5HNS9ac1hjRkhzRDFNaDFGSzQwS1FkRHU1azNjTEF5N21NTG9KbTNvWkhzQUNYQ1RCak1vb0oyUzF3VU5sVGU0dTl3YmdFTzVXcUJmVkgvbWVyeGdZKzFJUFFKbWZwU1YvS3lQZ0F5djBaVXBxMEs2SkFBMExKakhkUmppdFM4VWdOTDhmNFFJU1JEWjcvd0FYK2wzMmxub3N4ZzBOQ0NBU3JFeEJsRDFqU3hCVWVJb3FEYnpsajlSdWhQMUJDTm1NdFFqOVZjRzR0UWIzS1BzZHdkNlFEMGd2bzdNSUlUYkRVclFFVWFNWWJNV0pwaE9VUnRxRER0QVlEYUJvT29ZUXBGa29qUWdpZjJmOUJBT3lTeElDcUFJUEtCUW90bHNpQVgweTBLRENFYUVIUmhGbUxIR0p1TVE2eE44YzRmSkNzNFRZS2xQMFJzRVFBWENncWdlTWtaSUdJRmdFVVdBcEJjWS9nb1ZTQXM0eW9Oemo4bkxGbXNBQVFqeGxUSHhsVkJDS2FNSW9xMVBHYWFqWjR1VEtPTUd6SFl1RWxUa0NSVncyWWpCZ1k1RjV3ZjhBTnJnOENaeDdKTVJpMlJpSHhMUWVQRUlteUlORXpzU29JWURSamJGeXdSSEdKdU1OV0VQMUZvOExkOFRKNEhFZ2k2SnZoYU5wY2xKeUdsRE1MaW9wRlFHMDF4ZlpPSDdET01Fb1JPK0pUT0w3UVkybWlIVlE5enFFeGhMTlhMeE1OT0p4c0ZPSklQRTFobFhsWE5WSmRkMEhGUVAzeDhodmpORWl1azVLV3dnNUhRaGxDcUxuR0t0WnhhWXJLSHJqTldoWHhKVWhyMFFhaDN1ZXBmcVhlb0k2d01WTWRiRmppY01NV1VIaGFHZ2JqRFdTbkhrRkd5UEZsTEF3b1dOcTEwTEpIR3BBYTBOaGx5OGxzT3NCeTBVSWZ4WnVJc0xDdisrTWdtb2RFaU1KZDdnczlWcVB4MU9KdnhMY1pVMkVOaUNocUV0eEdFZytTME9SWnRkRk1WT1VRQnliOG1FRk1NU3BQR2FMQS9jcWxYRUFzUTVnd3ViOGxveWl3bDYzMGJBL1lGd3JxTnh6amJWTlZTNkU5UTJodUR5MkE2aGFQMDY4b3JXYUM0cnFFWkMxVmdkTlRjZlF4ZllETXYzTHlBaW1wQ2hvS0IwdVE2SUpGZzdFQkN3RFZnVUpTK3FFTmlYQ0RjTHAwRzRtN0FZS0tOT2FoWWV0aUVIak5qVHdGa2dWSDJBalZDbUpxS1hVM011SjVpZnhEOHlYRlpuME13ZXd3cVVvQU1EQ1hrZGJ2WlYvUUFVYldzdEFjM0thaFZRMUFxK1ZFakhVTE1ldi84UUFLUkVBQWdFREFnUUdBd0VBQUFBQUFBQUFBQUVRSUNFeEVVRUNNRkdSZ2FHeHdkSGhZWEh3UVAvYUFBZ0JBZ0VKUHdEL0FCb1VzNHVjb1l6aFpvdkVmWkdwNm1nMTJIekhIQ2NYWVd2N0xGeFF4aUVKY3g2Q0hyTEx3eGN0MFdqRkZ5eGV0d280aGlxV2xGb1dvL2c4aXdod3hVT0ZETlIwNGpFT0hUY1dnNkZIdWV3cWtJeU9sbGp6cWZuSHR5SEh5ZVJjdVdSYWx4cHpQcyt6N1BzdVhaZHgzaThxT25KWXg2cnFkenVXZldjSFl4eXRxY015alBTRmJkRitHUDVHMkRibDd4aHpobVZqOG1laDQvSmd1cC90STZWN1J0R1l4RjB5NjlCMzJZcjdxTm82bjVPaCt1Vmh6bWpzWVBBdkc1djZtSGczTnE4VVluTVhSM0hlblA4QVhNbVZYaXJGUENLTVVQUmxuR2FjVllHTVk5S01GbkZ4YXFIQ2gySHlNbUJqcXM1WmRDRkc5T1JRcjBmL3hBQXFFUUFDQWdJQ0FnSUNBQWNCQVFBQUFBQUJBZ0FERVJJaE1RUVRJa0VqTWdVUUZDQXpRbEV3VXYvYUFBZ0JBd0VCQ0FEKzhmM2dpY24rV1FPOWdaa3o1ekZreXdodVJlejVmamp1RC94eUo4b1dRZGhzL3JsNHpvbjd0NS9pSkI1ek4valBsK1ZQNmk4d3ZjWWR6MlVIMytJVGVvZjNEK2VPT2QwSFFOamRlc2R0WjVYaTBEaytmZFlQd3ZkZlova1dsQ2NoUVVFTmxIK3c4bXRmMFBrM05NdWU5Vit3dFlueCtpVDlmeitVeE1DRXFPL1lEMWdIdlpGamVSOUxZV3htME5zY1VOV0dPYmZkV25DZXk2em8xTjI1YXBZTENlbFAvVUt4U3NGWmJyMGdkbXYvQUpsUk54RGFCTTJ2QWpmZXNMVUtmbFdWYnJBbDExVlVOL2tXOElLa1RMV0Y3TEZJUVVOWVlQSFNzWmQ3MjZyMVpqeWFpbmNVeERLM3hGeWV3cXpWVDErTlAyczg3eHFoRC9FN1dPS1ZQOFNzNUtxeS93Q1J2U2V3OUZYS3Q1dHA2WHpMaFA2cTFvYkxXRUpKT0Q0OWdXc290ajJrNGhVQkNXQ1orVGUxamtJbFJzYkpLMWpJU3V1eTFvdFAwQUdEYWhFc0IxZzByNHNUTHhxcVZINUdQaHIxL1ZOMVcxanQyUFlacTMzaEJBVitnMko3UkM1TUR1Qm1aM2dPQnd1UTJaOTVuVDVpZXR2M1ZBQVZndVhYUVZ2U2E5UVI0MWE1UUViQndMcnR0b2Jsc1B5em1CUHN0WlV2QkZya2ZFWlBZRUFBakV3RlIzdXNVRThuYUFxUDJRanVKeU14QU1jaFJqSWNCaGtGQ2ZpcHNHdUdJb0s4WjBQQXNiL2FzNTVSSFA4QXNMVDFXeFh0OGtRQUh0UU1UQmprRGlBWm56UFFiSjFqNmdjZHRHSkI0SjFHSVcxUUNCaUVBaHljQ01OanhZb1h1d2tISklKelBxS0FJb0JITlljaktsaVJCbnNLQU9Tb2lxUk5Sak0xUWpnODhRcUZxektGd2N5ejloS2tFL1pnSloyWlkzempOeXNPTjFtQnNKWXBEbkYxZjJHR2Y1Y2prSm8zYXF3RzB4Qm1JT1RFNEhGU014NHRKWnNHMWJVcHc5b0FYbGxJcnJBckI5YlN4ZVdsVXJ3SFFtNGR4K2NHWjJyUm94elVqdzlIRndCNUpXdDZneit1bDh4NjhIakI3SStKQktNb09TY3NUQW93WW9VREpDQ3BkclVGaklZQm5oYnF2WFdBSFVSME9pUk9GWVRUWjRxNEpsaWtLY0Z2Wmd4TW11VUlMYUdVZUsvc3JLR241S1VaTXZXVWlvcXVNUFFvUXFub2F5dmRRektJcDFYTUFXb2NuLzRRVm9WQ0NnMXF4c1Bpc0xibXRzdnRCcUNxdkhWaWtVcUpyUFh0Vk1hbVZua1N4UlhkR1hSc0Vwakt4RHErVFU1OGEvYVhJS0x2WXA3M2xoYXNpNWJDakp1RzJaaEtYRG93VzJ4SC9NS3o2WDFhdjhMbENuM2hHWmk3UXRpb3NBRFQ0cXlrYjJLSjFZUk5RMWVKaVZqc1M1Y2lCc1BMdnlWYkRJZE9TU0RtV0lGNWxZTnlGVDRsZ2RUNDlsV2F5YW5WaFcvcmR4WjRkbVZzVkdyM3JMYjRES3Rqc1hTcng2U2dVN0Y2ZzZKd0JLdjFFeG1nckxRTFBEcmNlS1I2MHNsM3h0elBIc0RMckgrTGtRV2FuTXNJSWxnNWlXa0RNYyt0OGpPdzRvZFQrTmw5bmkyeTJwYmtGMVN0NzY1aGZJVFFyZVZ6UmU2djQ3NUhCeUJYZTFTNVd1dTd5S1NyVnBYVW16SmtGbGlja3BOVllFanhiRnczanZTVFM3Vk43R1B4YXEwcWNpMWc0RmdKaTJiZkNFN2NFWkJKampYZ3FTallMS1J6S25XNVBYWlNYOFY4RnNJM3NXNU4xOWxSdHI4aE5iU1hyQlJsSUJHQWpPNVlQOHRTTENsQ0ZGSUtOc0hVc3VVRExZbVl3M0hOREo1QTlWMXZodFlteUpZZW04WmxMNmx3YTNLeXdmOEFDeHNHNFVsdVFWK01zcUk0UGpPRCtOMnBhazVDTUhUbXRndnhadC9HYmF1d3BjZDBVN0tSUHJtdGtxZmMwSUhMUlRiWXV4VnMvRmxPa1pXQjNyVndSa0xYN1J3V3RWOGw3SDNQdFFxWUExNkhHMlJ5U2EyMlVESHpSZHV3MVkxalZZbE51VjlkZ1UxSGh1RnpCWXlyR0hPUU1uSmd0VUpnaXIxamFJU3paV2tWZ21zc21Sa0FuN3dWR1ZESzNNNVhtVitVTWEzTXREVUVMb29iNDFHMUcyVjFaMU5pTVFSeXJDc21Lb0NiaENvNk5kWjVVQlk3TU9Edjloc2w4Szl0V3VBMURxTXdZQTFnRDRHek5ralVzeW1ITmNHRzVBQkhJMVIrWWxObUFaWXJWTm85VHZXZGxlM3dieG1HczUrRmQvbFVabFJlNDZvYlFleFlBdXNaSzBSV2xicXBCR1JhNTA1RFlZcllTZFFBdmFjMnhFOG55M3hDcFN3cXIxV0I5WGVvb2RRYlNmMS8vOFFBTHhFQUFnQURCQWtFQWdNQkFBQUFBQUFBQUFFUklURUNFRUZSWVhHQmthR3h3ZEh3RWlBeTRRTWlNRUR4Z3YvYUFBZ0JBd0VKUHdEK2k3a0lnTkZ0TGNmbFhEK1ZsbTYxRGdXbzZwbjRtK0FyTm5XejhtNnozUFU5eUZ2dFBvZW5pK28xdVJhNEx0L0N5WW9EaVBjZmpsbXo4dXl6UDZMRGVtMCszY3RLenFRM2EybGxYTVlpeWhMM3YyS0phOUs0bGphNXN0UjgzQ2pjeVl2WXJsN1dNY1JYVFl2U3RBNHZpU3NyZDlpYjE5amQ1SkNKa3ZhNzJUTEJKYmo4cUxiWlpucEdNYTNEdVVOTEppS1pFaHk4b2hSMWxPQTQ2aGRXSjhrTlJ5cXlNTnhhTE1YcEZEVWg5WGN4aUpGb2IzRnE5M1V5R29DZ3RHTzBjRnBtV255SXA1MUxVWWJDUW5zWTkwL29TV3VZNGlnT0lyNW5OblVmSVhDSEtQdFVCZWFDd1ZMVzhVUmlGQWNSZWxjVGpmNXNWM05EZ3RmWTc4ekRWZmlPdDhpMTE0M3lGdFEvVXVKSkcrNzd1bndXOGNGb1V2c25xSWMybjE1SWROQmhGM1l2cmRndTNzalhBWFI3anM3OTY2b21zMFR1M2tsbmlJL1o4Qkp4MEtXZ2V3VUpjMk9OT1lzR2FPcDVNeVhOb3hYUTh3TURBa29SbG1QYjNMVGxnNnJVeTFISTNrbm1oK2w1NE83elJyNUZDZHAwV1gyU1dPV3BYVTYzWmRXWkdLUElHRDVtUFg3SzJmTzQ2VFBIZFZIeHRZRC9BR3NiNEZjVm5wV1RNZU9oNVBTS05sMTBQdUwxV0dlYVdPSFJaNjJLaWt1QlJjeWo1ZXpCM1ZSUTFkbmRqSjZ5aDhiVjIwK0w0UFB1T0Z0VWVEV1RKV2xWWXBpMFdseXRFMCtLK2lkbXBxTVhCZWJqUGdwSXhVVEN5dmRqektvOFJWY1Q0c3FqWjJLWU1veWRsazdPV1dyUm9IQnFscnZvNUMvWmZKWng2UG1PR1VhclFWc3RyWVptZll3YlJsQXdVSHNNZmJpYlNqUDlXUlIwZDFUNUlxaW1ZNjQ0UFdMWjJ5WTQyZUs3Y21PTHdieFJOdXNLUk1ER2hXMVAvcFZXMHBhbW5weUpLMXdaVmV6WmR0N2xId3UyOTBieWFaUXFTZVpORTF4WGMzcnFPRnJlWW1IRkZWTkZIWFE4MTB6Uld2MnVxSCsyRHpIRzB1T3ZTU1k1TytxcjM3M1VmQTJPNm5sTHFFMFZ5OHhLY2g3VGJreXhwcTNRc3FSVkZPUnRXWjUyZW1qSzd2OEFId0c3TnJQUFgzUXA4N3ZrdUt2cHl1Y21iKzkxQ2hSM09ZNTU1alhRY0llYmhPWmg3TU1mS0M5UzgyUFlXUFZaNWRpS0diVUlvNU5EbFFaTmNybU1jSW5ZaEhGQ21TV1JhV0JhaDdKTTMrVFFvTVc2WEJ5NGpkbDZpMFFhWXA1UHVXUlN5RlhBUlY0Rk1TWlVzNzdsT2drSlNGTS8vOWs9IiBpZD0iYyIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiLz48L2RlZnM+PC9zdmc+", + "description": "Displays the latest vibration telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'vibration', label: 'Vibration', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Vibration\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 3.3 - 1.7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"horizontal\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"waves\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2.8,\"color\":\"#3B911C\"},{\"from\":2.8,\"to\":4.5,\"color\":\"#F89E0D\"},{\"from\":4.5,\"to\":7.1,\"color\":\"#F77410\"},{\"from\":7.1,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2.8,\"color\":\"#3B911C\"},{\"from\":2.8,\"to\":4.5,\"color\":\"#F89E0D\"},{\"from\":4.5,\"to\":7.1,\"color\":\"#F77410\"},{\"from\":7.1,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:aG9yaXpvbnRhbF92aWJyYXRpb25fY2FyZF9iYWNrZ3JvdW5kLnBuZw==:aG9yaXpvbnRhbF92aWJyYXRpb25fY2FyZF9iYWNrZ3JvdW5kLnBuZw==;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Horizontal vibration card with background\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"mm/s\",\"decimals\":1,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "vibration", + "pump vibration", + "motion", + "resonance", + "dynamic balancing", + "mechanical integrity", + "pulsation", + "frequency" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_radon_level_card.json b/application/src/main/data/json/system/widget_types/horizontal_radon_level_card.json new file mode 100644 index 0000000000..36ea0ef3f3 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_radon_level_card.json @@ -0,0 +1,29 @@ +{ + "fqn": "horizontal_radon_level_card", + "name": "Horizontal radon level card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAApVBMVEUAAADf39/f39/g4ODg4OD////k5OSAwyzg4ODv9+Xf8Mqv2XuQy0bP6bDOzs5YWFj3+/Lx8fH09PT5+fnA4ZWg0mDHx8eYzlOIxznb29u2trbQ6a/CwsJ0dHTV1dU8PDy43Yjn5+fIyMjn9NjX7L28vLytra1mZmbH5aPY7L1KSkqenp6CgoKo1m6Qykbt7e3U1NSQkJDH5aKdnZ2n1m4vLy+qqqrWFLAGAAAABXRSTlMAIETfv0i0+14AAAPtSURBVHja7dqLVtpAEAZgSzsZkmyuBEi4BAhXsdba2/s/WmeWUiJW5dDSA5z/O4rJBOP87G6EAzcAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/9O7i3ejGu+dS9d835AcTosuXuo0bj6kdAVaH26adBWaCHJmEOTcIMi5QZBzgyDn5g9B3JiUt6ssHh8d2kiWtJN8pLq7ko5wsiArzmwcNu62FN11Rgk5ibRejogklOyRbES6kySU2P1hhw7lrbLg9+n7/ZisuC9uPXpm9rzm9W9fDzJm7pIIeJck6tAo+Thcl4v1cETRMEqGUfSoQWRn7Uip/LiU4sFBMhZmun3k2CPLZcvc0lNx7tOeMXe5+1qQjIWe1+gZY7Ki9fdH6txFi6h0Rp0lfb0bduxoRJ3RQqujZLRYLg4OErDxphkbUq6pB5Eh8TmP9+/v0Z4skFbdF4PoWcU9kccq247I1yGty4+LZccZlbUgEqHTSxbRneTrOAcHafNMbvNNI9J3LQiJLgc6DDLHYpdU25BsSuVWEnq3UxJT32SvjEjAIveI3C6L7m5qOdFyveiMZGoNl2tnG4R0SiXfHfm5PHxqzfquDRLbv5iZvSA2Z5AzmzZ7tjzWvPdamWlfmRYzY+KXg9yzCCgYu3HOon7XRL+3G/Vq/efhAu7aKWCoHsR13TEbuzme+WyPBOzagVtpivYsY9n3A1rx9OUgXdv9jPmb/KaY0qm4xp7cl1uzv9g9Xau+bcfT26694xed8LkdsUC+u7l58x+iJ8HptGJjJ8hU+zVPFvvK18vlvfStLXtaDWyQQDc/bTdnctF+M4hOLTqpuCs5hMTp93Mee/U14vNYH/VtkDG7z4NYbwaJ9euktFfb+S/tehCdVv5uREybjg1ijE7OnZT+IH3YL6R0qLGsQRV7yvBq+vTyO6aVzaZteBwcGyRjUb+29bRN/XooyGq1qNUMtdCkluxPfkyoVRSbA1J4I9KK+d4Xtp+na8T35SqbuxTn7GdtliM+x0cGcdkaPwlSlmFRVaFsDWhQDOat3iAsyyqcP4Rz6TycpPN5QWloD4TV/I2JteE/C6Lyb7ompkYmnB4xPh0ZxPDGtB7kc9hLq3ASboJMPheyLbVJEQ4qG6RZFBKk6k2qVOIOUvprrqtrZMazo5/G+13mvF1/Gh8Wg7BXVoNURyScF4Ne2qtCqT3MJ1VIGqSlIzKZL/RAORjQv6BBTvrCqpjQyz5XupAuI0ir9drBlP6ZIHPxUvdSIci5QZBzgyDn5oqCfLiCt9mJ0g83DecK3mhvOQ39CEfTuXTvG9f0oRoAAAAAAAAAAAAAAAAAAAAAAAAAAACA/+QnXMPkgka4f5kAAAAASUVORK5CYII=", + "description": "Displays the latest radon level telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'radon', label: 'Radon level', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Radon level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 75 - 37.5;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 300) {\\n\\tvalue = 300;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:radioactive\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#80C32C\"},{\"from\":100,\"to\":200,\"color\":\"#FFA600\"},{\"from\":200,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#80C32C\"},{\"from\":100,\"to\":200,\"color\":\"#FFA600\"},{\"from\":200,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal temperature card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"Bq/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "indoor", + "air", + "radon" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_radon_level_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_radon_level_card_with_background.json new file mode 100644 index 0000000000..27694748ce --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_radon_level_card_with_background.json @@ -0,0 +1,29 @@ +{ + "fqn": "horizontal_radon_level_card_with_background", + "name": "Horizontal radon level card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest radon level telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'radon', label: 'Radon level', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Radon level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 75 - 37.5;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 300) {\\n\\tvalue = 300;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:radioactive\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#7CC322\"},{\"from\":100,\"to\":200,\"color\":\"#F89E0D\"},{\"from\":200,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#80C32C\"},{\"from\":100,\"to\":200,\"color\":\"#FFA600\"},{\"from\":200,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal temperature card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"Bq/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "indoor", + "air", + "radon" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_rainfall_card.json b/application/src/main/data/json/system/widget_types/horizontal_rainfall_card.json new file mode 100644 index 0000000000..9f94eb4b46 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_rainfall_card.json @@ -0,0 +1,35 @@ +{ + "fqn": "horizontal_rainfall_card", + "name": "Horizontal rainfall card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAqFBMVEUAAADf39/f39/g4ODg4ODf39/////k5OQjTMfg4OB1j9yRpePk6fjOzs7I0vGtvOry9Pvk6Pj09PT5+flYWFjIyMg+Ys7b29vV1dUwV8vCwsK2trbw8PC6x+6fseetra3n5+eDmuBohNi8vLyQkJCsvOo9PT1aeNVMbtE/Ys7W3fXW3vRohNkxV8q6urp0dHRmZmaenp6CgoJKSkovLy+su+pLS0shISHWoRcHAAAABnRSTlMAIEDfv1C6kOEmAAADUUlEQVR42u3aDW+aQBzHcWe7P3/BA+QZBETQ4aytfdr2/t/ZDgStjS7baBtqfp+m5Q5Mel8P0xg7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAPhIXz69OmN4pXx2o6uvskOx6dOzlOHg2qILYF8PRnQRRgjpGYT0DUL6BiF9g5C+ORWilyF7Gh3MFguFKnfB7vBUT7f0qFBfnAjROFTVkj2TWs6D+y0mJaYgiGOFYueO5IG+kdMxRJffptFY789qWvIGITrPq4RlWB5CXPoWP26fgsVs4dw77vO94mydriH6PGR5GHNDo9p4xZJndgxZ5uGKailP9iFPzwtyH5zZQn7t1n+3+NUtxKzXWw30Wtj+ug2rk/WK591CNA7LdltDbbLfkYct/QweDyF3W6VjyPi7ZsiQls55E8i3RJSw10wnppkkciE3yU0z1eupof8pROeNSa0Vc27uby3Fuf95CAmc++e4U4g0fhGSs96s3EjqhZRUU1negHyrl8zVJhm88eQoVeWPlXk+ZM6TF1WGyjkdxESnZ91D5NCjl1RetoNQMzZy1UYayla5iVoiKzxj6XF6PkRlOpLf0juSIUcLP0j3XSpr9QMn1fAHGfVTy3xTPenz8yFpVTkOE/JS2uQy5CjsHUJOb8g6DCevQpqhwaocfa+6NFbPh5hyv2SNrssD568f+34h6tGzOw55Td1CaJwn1f2k8Y0uN1srTXo/h5Cx17w229mSuoS0wpxK7+h1b9EJVvz6hPW/Ict2TZNdh0ZvESK3wpSPKVe0N93FWBT7VLNtskeiOjEiW86zWUa27+8uyBPWv4W0G5KyWk1u1YreeUcSM5F7m6yPQoJA+K4r5CiiyI8KexqJIHBFEYvCJhKZVRQ+WaK+INziX0KWXFITMifixo+OIYc/J8chYmq5IhO7kCzw5Viey3wRuXXIyPdliDvNXEvmRhb9D/ON34+kczoyE34kpoEbWdWOiMKPptbUFfJcXGSuoCrErnYkK2bVhSCKqIOPe2PlZ3Re4Pr0N/oQYtt/umjR3+hFSB8hpG8Q0jcI6RuE9M1ocH0BH7MTWdeDoXIBH7TbynAw+Ho1Uj67q+El/VMNAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAf5Damp/4Ir985nAAAAAElFTkSuQmCC", + "description": "Displays the latest rainfall telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'rainfall', label: 'Rainfall', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Rainfall\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 8) {\\n\\tvalue = 8;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:weather-pouring\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#7191EF\"},{\"from\":0,\"to\":2.5,\"color\":\"#4B70DD\"},{\"from\":2.5,\"to\":7.6,\"color\":\"#305AD7\"},{\"from\":7.6,\"to\":null,\"color\":\"#234CC7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#7191EF\"},{\"from\":0,\"to\":2.5,\"color\":\"#4B70DD\"},{\"from\":2.5,\"to\":7.6,\"color\":\"#305AD7\"},{\"from\":7.6,\"to\":null,\"color\":\"#234CC7\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal rainfall card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"mm\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "rain", + "precipitation", + "downpour", + "rain shower", + "drizzle", + "raindrop", + "cloudburst", + "rainwater" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_rainfall_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_rainfall_card_with_background.json new file mode 100644 index 0000000000..ed2e536310 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_rainfall_card_with_background.json @@ -0,0 +1,35 @@ +{ + "fqn": "horizontal_rainfall_card_with_background", + "name": "Horizontal rainfall card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest rainfall telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'rainfall', label: 'Rainfall', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Rainfall\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 8) {\\n\\tvalue = 8;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:weather-pouring\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#6083EC\"},{\"from\":0,\"to\":2.5,\"color\":\"#4369DD\"},{\"from\":2.5,\"to\":7.6,\"color\":\"#2B54CE\"},{\"from\":7.6,\"to\":null,\"color\":\"#224AC2\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#6083EC\"},{\"from\":0,\"to\":2.5,\"color\":\"#4369DD\"},{\"from\":2.5,\"to\":7.6,\"color\":\"#2B54CE\"},{\"from\":7.6,\"to\":null,\"color\":\"#224AC2\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAaAAAABcCAMAAAAfz5G9AAACl1BMVEUVGxsWHR2qtLSlsLAfKCifqqoSFxcXHh4QExUOEBETGRkVGRqtt7cdJyeosrEUFxirtrYmMjIlMDAbJCQcJSYhKyuwurqnsbCrtbQqNzcZHh8fKiqjrq4YISEjLy+os7MaISGps7Kkrq2vubgnNDStuLgfJSYaIyOlr6+hrKwQEhObpaUhLS0YICAQFRayvLwjLS0rNTWiraycp6cnLy8tOjqfqKghKCgYGxwkKysSFRa0vb0dIyMOEhMuPz61v784S0qttrWSnJyxu7s9UVAsPDyhqqozQEBQZGM6TU0qOjoqMTE1R0YyQ0IvOzsqMzOeqakzRUSmsbIcISJmenlidnUvNzcsNzeQmpqUnp4mLS2apKMwQUBAUlF5jYyYoqE9S0s3SUgxOTqXo6NJXVxEU1Jbb26VoaE2RUUzPDxEWVhEVlV5g4MwPT2Ak5J/kJCIk5JdcnEnNjYlMzOdp6Z1iIdvg4Jhc3M1QkJJWVmClJRYbWw9Tk4xPz5pfn1ugYA6SUg5QkIMDQ86RUWjrKuMl5eFj5B2iolkd3ZWamlpe3tGW1uYpaWcqKhQYGCirK18j49NYmFJU1KWoJ+LlZU2Pj5BVlWAioqDjYx2gICPmJhzhoZxhYRRZmU/SEhUZmY9RkZrgH9KX19GT09eb29IVlZBTk55iopAVFOHmZhUaWhYZ2Ztd3Y3QECLmppzfX1MXFtNX15RXFwtNDS3wcB8jYxPWVmElpZtfn1BS0tdbWyQnp1pd3ddZmZVY2JxenqSoKB9hoZaamlqc3NkbW1NVlZ+iYhjeXiIkJB5hoZmc3NtenqMnJxzhINVX19ocHBicHBaZGNETEyIlpVedHNygYBfamkICgq6wsJiampYYGG+x8bEzc3DU1oUAACFUklEQVR42hxZjV/T+B1mCdlrLkvZTNKZD1kT4pJPtgUSW+enN2sbLfgGtVNprVqkYIUrZ71VRZEJs+3J2drDVgv1UM8XTgVFWcepqMChCL47T4fn/TH7spRPS9M0bb/P7/k+z/dJSXdLty+ypfr6Fmv15fa26suUMWZqa7Pef9R2dz7yE+v6tg1tazfs/Gzntp3rV61suPfJ9rU31+xcter0hU9aW3ubbj/dVnNhzQFf145Pv5m7++XVN4UkSkqUJWhoFo5lMQqbHyaFaXOjjOOINDebV6l4/iVKexkqMNXvz4miYKfUot1OpVzRYrE4j9gFu8NRVBG9aKCsg3IJRR0z5guq2u8w0HTR7sBTqXEKDlTtRdgyCfXuAcZRLPzooyjGmGe9GkvbGQJOKTrgCDtutxfnHSJFFUmvQ34/TKpOpqiKnF017PaASop2R4p7ySYMh8pF5hXVrvqcAp6gQllCwBlKihc4MUmTaS+Ke7A5EkfCHo7yIziF4yLeJws44kR4xmkJ4vz7cY9EOy0EYeE4klOahzg6hqEITSMoinBhLztJEN40yw0xRZcic2yelrtXv0NZLINlaFQKZ1hez9JZNBCUCryk8BIhuTMlp1q2XG9sbB4xtVkHzb/7uH3oYnmVqcJqrbZ2jmQrzVVtXYPr1z8yn964YfX2tsuftG5ftWoNQLW96cqVK4dvnT54+FZv65XTX3/+7MzXj5/vufrkepj1S2KOwhCvxnGUJ4zi+HTqRB6ZVZ2iPE4xQjApqEIMzSUO4oTdUDnKnhAdhMOnh8lwPqFSfT7EXqQor9eTVTnNTuKyUHT4XQ6HQ3WouBeFh6KjCFB5KUdR0/0SGrU7XIrEppSQioZIwTflwEmXw26HP8ooOuaLatHhiGRUg8t5AFjnwgsYfK0g7qANzHCQEVuBoigVyaqCQAmMSlHvTrBOwWkIgsAQeFzkcBIXEUSxiC+tR19IJJkfcqG4kKWUO+3NNgSLsymGeRlMuF0PA4gSlceGiRHMLg//sb7PIvEWGpNID4Z5vRiGonL6BufSaALN6DNv9z2TJS/CptAkmmZpW8DG83E3z7t5NyHxEj3nKQHubGmvqrfW1taO5euXV1vrqkxV5rrartr6bRXm3eajVmvttm0rzGvN27qqVzfs3rnxYNfNg5+sWXl81bmVa9Z89offmC8cr/nHjs+fH9m06cznqf7JMEoqlEiiXhFH4IeJqDf/4WFANlhymhBEQUQZEhEM3O8jDMNQi0+HHRRidxASZberKE447SjOFWH5qypupEWSRRZWucFSfT8AQAbsB3SgwnLS4Uj60BBe/EAV7TnRXgQIOcphjwzdm8jnLNLoGCbCiWGtO+waGkgBVYEmdrslyfgddoZyhOOqQXOciADbMJEiRVwFQHiAR/TNwOcASWYlyokvgCYgiAtHE4YlMecA+uIzkpfhLS5MmXUUfc6ozlsUJ7yXCEZlS5p5iZAoFiHZi5cScwISRxheJr2pGxyKhtJpDh1nWW36ZlRG/ZH5eY6WWDbpVaTJWV5TeA1g4QGbON/n9sfcUknjo6qq2truamt1++X5+3Vmq9m0pb6qqqqsu63CbDZ3mmBP7ah5bf56Q2fbmkjdhjVDzabVaw7f3bZh5faeg/ceVP5zRe+qjiOf37791dz92/u/+RZDFUG6TyOkBMuO8iYLLl9uisNFkuQtnBiIoQWOc0LxVUENMh5yNi8LDhYReMfCNtvnCVyccNihqA7KQaqqOq9xjnkHnuH8oaLqd3CAw7zhSOBeh0tMu/24UZy3F/9PE/sCQg7HxPyH/mCSGr3x7KHqoP7/kt2wY2mG4pwIQ6m0EbElcFEVGYHCKYqd1H24QKgCTva5mNhF67BT9MQCH1ACZwTcqbkFp9Mg87qO4SSjZV0+H0qgPpYTcYKJZ52uiE+dIiTWF8Pw4SwjpVFNs+lZL+tF9Mn7dodKWwrxqej9EHf10ndv/LqM6tOPdDry5BDrdbPJmTgrsQEJkySpIEmom+bjPOBD81GfK56Bf0q2HK2qq69bWm892thWWw/0qbfW1VfUXh9etwBNhbm+rHZ3dYXpt6uHunZXdyUnGlZsDHX95K8bD685sWHjzgfz9miV9fCqpr2bd321583Vq19++szlpaWsJUnh2X4k4+X80YKSQzOUKJKiyMBa5HDDxdqNQp8hMIwg2BnOcBhAH0dfxDmffEr3zT+dFz0IarezFImKXofXEkFUyh9Ti0VjfoE+YubB93dVh0N0CJwBClb0esLJDw5j1hAdKXvB55MN3THx4cMMAC950w4mR3Jipmxt24hh0LqDtRdI0BoBtAYR7KrGkiqlBkRB9dDIRLao8Abu5B2C4FRwYmxGpIBWFj+Bk6gFQWJu0ecScY1JofiIP4BaMMVCs32T4/eiOmOzIB5e1lOeVALlGEXWn/b7CQ1hEEswwHE39LQ3hcl62EVJsf7gB5qVJAx7kyroqCSPRNwZgMUtvWvG3LF4LP8cixO8xc+XbFlX1djS/vFfPt5aW13xekldvcnUvKXKeqcFqFTedgoQulPX+FHJn//5B/ODts7BO2O125z4k8pfrmjbuHrntm1dw1Pn16+p6b22d9+/34Qm/aHJkOItTKE2Tpi+WLuV1cm8kMZlhMRJkkzjYvKliooUtD5MkCxzgIsyFaYAI0pUVS0virlsMZDNQUtT7QinelLQ3zhOVVQHUlAcDiOf7HPgmKEmilOaCMeQIYdhXxAnFthTDJGoA3jnmElKdopP+guwgA2voyhyqh1/t/7ApoaIQYkGg+C4Co99PIELMgUfjYKJ8OdoG0VI/bykCgbiUjDc4xFjfXFBBIiA/iQoPU4DpFnN6UQsHOKMyiyvSW6aoLHws9CbMDETkKRMYvRyaoST2bQUDRU0Pkz73DZQHLcXFeEUbDSnz9roOJ0PZDKedBAN8VKGxjw+LuN38zG+L2BovNsdL8QZd8bWl1NKusfurGtPXqz8W2ltbf2ji3UmU3ld+8nq6uvNW8ErzFZUtpwtq3r3sO5n/119efu23dt3d2279+MH10jDu9Vrtg1tO3xlw7ZVp88Nbb/3jyf/TvtS4Yk3k6nEm7fpG3rSMV4VEiiMJfGYhyNFkFkBd4MOg6osVMVAbSRtQNOxvTQYA1cNFTEEp4NSVcEOPUklMVRTZYVVScNPxZCQF6yengBtsDuknIMnFxRHn2fALRTtADmrAoCAVdGjJnCcgt0qKI4RsM+QqmNcV7s4jtH2OGiVGpnOiF6E7I/2MQEBN/CkSIGZoDQQG8mAO/iesytaH/A8wmgcLpBpC0qyOmUxnJjwVFKoglOEn0ASmCbrmDuOarRL/ubQ1dDDOQuL6T9sqPki5MmHPel8OpRA2QCKxXiClr26l814NB00RgITkGGD2MQLmzvOZzI6lsnIGZ5PRvg+XzAGh7hlmcWkTMHWX1J1/nxLZ8HS31LVUllXUV9VZz3b3tV2p75z0e/LTOX11h8aq8q75w3fxb+aaq3N61c3dK7feHl3RfPGNSumrd+t37hmw+7t9TtXto7efHo1kdbHv9lzZN/z/ccOHZrM22Z9pOydfSQiDIEKsbEXJMnQ0PYl5YezfgERoBQgv4Y962dUMgsqD/WExpLTVRXDENGbdiGsoXr90SyQJWxn7CQpArFAhOwiBTYhI7hHNEolWZIElTGgKeUw3a7iRRxIBUip6si9PGuwcKiXFO/8OP/hw4QKttvvVvyCSrltRUqjCwzLYQJOCRaARrQQaJQRnPpDsviBA7ZwBPbwIYZrHCmQzhlJc9qdzhyL0LKT8xJxT+jZ1WdBnSXQ/sinz7+ckxXf5LNR2XvszKFDoZSeSqfCgbMNWdAsLQE4JBTdTwc8bHg4xvuzNO06URP2x9CELVqQMiNPvEostqBBNB23xV1JwFHyyNGS39U1nl9aP3bKVFe2zrRu+Z1TlXUtZWPnq6vdW6pMZWXWyrOP7ph/yBbjy0wnK7vAS3RubBi8OVTbuWLFwYaG7o07W08cfLWypqZv7KtvbqTG+/ff3bt384Ga78/sm9szLrNe3Z9Lo5wTt+SKb1+KgkUUnPiUvqcaPBNuEVQ10g/OV8Tu/xAH1wBKpI1eMtsWSoOCKJB4EMPItMPgDAosHwPVBZ4Ad1R4e/+0GqB8xZejbvATYj6iGjnexxUCIjAHTAFpqGrOyAKScCtS4vTEjz8SPGOA3vCIT4BXKZhzwG5RBhnGKejBTkXEhbFXM3TUH5yIKgyRZtHA4DepWRSHDkA6ERolnLwFReJEVOemZD41vmf/matKnLblLF9/8c3nh+ZCz76967SHNj3+9tvLg3OTz0J9/XaM8IA3I1BOx/NZGx9xR1VaVoAfrwVES8Z4TA4pEpqXMVmOS8GAlHZj05dXrHTZMD3j9pT8bt26dXX1S02VW7eWmazltS8W1Y6eNbWfstZ1d7Y1lz36T1nVdVOtqXKR2Rw839X20GTtNC9ee797Z9fhrjU3mzesvNl2a+XKnSd6e/ZuOnTj2f6BazUXWnt7bu/ddevIkZDuDefZ4VPvGURAfb5+EbfYcFzz5dSHjNqfw3Ej5cpPjRTJLR3rWwxjoXs58/Nv+kgGV8FUIJwX9aoyAACe2wBs5qHW8ypYClX1GH7JYXdb+tyO/qKdiSXneMqRRUNhDeDToqoMYNuLGHRQHberN+yi6poohJw0dNbCMDgEShWhoSLuqJdKzqHRnGATYMOp3AfXlC3HoYKTl2KIUHj6owUVcc4LfCKdBKjQncsKI8+cfOJn6NSnO75+vH/PayWCHXqwqWfzgT1Xv923b2j+i13Hhj4fclGTzyYnIzGZlmh/3C+HvKwS5mltwaPxNN3HJ2IWLUjwvGyTMlImhOqhcc1Ns3yyek5PsSibCdnccsnSdUu31leXnV+67uTSk1sultWVmRZdrK2vbasYtLZVlf7+d9evb/lNeUWZ+SNTbaf5aLW5zdw81tVZ3VZRsaJ5zYadg4cb1q45bV0/cO7ars/3ffOPVz3Z9r9fudLU2nHg7udX86EEweZ+HMUtIiWJpMDAIASqixd45/Dz3d4URZLeQgKZ8BihOlANQVEs7uQMoCFQBk6BuGOTJBdmRHAC0BSLCTbw8j1pV4MuTqUcIvAJWCIF7KLGzELDokgDh4wAwV/seW9w4oICBR2wizUQFhMF4GAkKWCES8ecQQGOxnEcLByFOWRXCmFIrT/gxPMJNx4NjihMEtE1xsK4RmIW4BJDwsYxASlZ+BBwaiMfMFzSUoe+3Lx515k9k898SVfH3Y63z7889PmXtzdfe1Kzd/+ZTw+NTz57diPsIWwEkUOheUZlHnWDiUZ5sNIYGo8pUhwwyEUURZL9SgBDwUV4sOTumN0PmNkCfjlTsnRhMjVtra9YVtlcf77u7Kktg40tnS8WVVRVLFr8x+Hp0l/Uvhj+WYWpIXmqYnXFk8a1bbsrrFtW338UaPnBtHqos2vV5bsr1kesrdt7L/xj35nnHZsOog0/u7D51vGmywcOjDVOpxRlysY4cV1hnE4neFtbkqQEwtPnjECVWSrEUcYI/6MwysluGGP9/TbBm3vnghAiiiIkNw7NSAZquTzRIqmSiCsI1g8Jg4vQSTsomAP03e6kBNXAIZbwT/sBSzHyo5/rM4BCMEnxWRKsHS7MsoagumhA3gkT0II36zdvvAedDRFxmWVwnCGCDIIwAmGx5TXMRsYUhelDbGnGxk16OcDHI1swRumbdUpaKMlK8fTkU/PdS+c6Hj+9cePfzzvOnbj74MGOL451tA7cujkwsGnHvm9uvP3m08lIwtY/O5sbkTEbLUs2CcCR+CkweHxMi87wPMH6PdlMQs542Ewmw42ksRtTIza32xZPukPxknVblq7bWl55qr68u8zcuKW7pf1OdZm1qspkrbtT1TboG66orzNXVprKyyrM1WZzNXjvk20TS63tDTcHK3bv3n2xbfvlbRsudy0uPXytY/+RIevuJsRoO378dM3AyNdfPX1QfjU850ecXhLBZJFhKCKWgKc4SRkBGpcBKs4L3aYwMsIgol3kqAnsCYmyLlQFOaf0hMsroF4HO2wUJS+kMZwBVg0BKqiGaGgeiGcgBYDyqxDmqKAurhi7QCsiQAb9IukQRdY+4dJkEs4GHOEw2UUhTkwUUREhQ4PjOOVxAYnDScXGCrQAKCXOjo5YnFM+5yxKuwmE7rNgGEajeIbk0mHJQ6d5BmWwQoGhUX3uy5E00rF387G9Z97ePvJdzXcn+/RLh/bWDDV13OzoHbh158E3O57/+01Kk2BcShAIj3I0q2hvXShPB23BgFvm835MSWiZEOZN+dDJE5MymrrnZ/tYzANem4ehVSpZt3TpsmVn35VXLemuXH62sbG7qsraWVZmbjFbP6uwNtb/qbbibP3w0ubG7j9a2821E921P5g6K2pXd9UOftZ/qn3jhu0rN+5uvhyRRxf3DmzquOe92PTqxOma49eu1Vz6agcmDH47jvpQDYV+whE2BRdsIxba4kREgfSSUdQ77iUpDtFCbygVwiElk/P5SCc4ggTiMyByICGMMBw4S4FBgJ4m+8CliZ6cMT1iUOFEmArc61cNg6Kh3VEqwGVQ+WHVSRpIlFVlL4OpEM4EETuwSUsbmGZ4VczLTc8QIoUNGx+ELCeKUU6ZwsIYwTjlkDiannda8rko4/LLfszpThAMRhAQgPZ7SRr1WjiwcAHGxWFPQ+E9O64+C33f9P3djmPHns+9vXTiZGTowLEDuzpqbp1u+qS1bOXirs2bv76qRDGP5vNH83HUn/RtfKJLmIYuCFGobzgRTsLomn7/Ws9jmQSCuTC2A2V9MibJ8TgPt3jJ1mXrWrbULz47fcp0+eTSwYqLpyqtdWVnK+u7Kxa3119c+svlpkWLy9oqyupOnfppm7UCnF3ZoKndevlRg/VO8+ALa/vGhuoVG07OvNh+rXfz456nD5pamy7s6hjYdq3j+Q7nq2+/vRqSZdc7HB0WFxaFM+jyxU4efc/grIKyXi+VwaYohBIoVtS9eBogSeMGGmL1kEqSVE6VCj5gDmTelKMQKYbDYs7HsbDwI1TAGHmJJItRylB9B1+TkDZSYPvETIACCYtOKjOCwYhcH+tS7AlstmGERGEiJT2GMx6npYRXk9/PFig2jWKi9/7itSfjiFMm8Jc/5nhUy0GzIxjSYmEZmpYwG8pIHg6RCdg8WlZy0jFG198cOvPV10ceRHftMpc2fHFp13dDQ68ObDrWcenJ4famoeOt5mQoefvx6MNsjA6id7qDBEFrcnJt+WDeHZ6T+YDMRkO+JCYFMvduv1VYD+uV0x45T8m+gwqLzWmuuCLx/pK65d2LGpc/XHZqrLb2/Zb6soazg0tblpf8bJHJbOo0PbS8+HPl4KPSRS1Vldcn6qpq2yvvN3ZusbaMjVU3j+5e2zZY8eSz5rHVh9d8cqD1Vc3e798ePdeaGNv1uOPp3bLvv94/9OWnTw9NptPjGDid93VLhjncGXQOf3rko6SoQPQjyuASZDw/IUqcDFE+QnoAFpIhxSLhn/VATHnv9G6gBmUHrUmGEQCNvYFLEdGPkbIWYafoIX+aU7Zf7enziAJLsvcjpJqxq0bwfMPGKchJi5C5kVQajcz7ipQhEH45RjALbo3CMVZdMNmkiD1dtX98R54hSJxx+3iLBWc0IkZYLDAVIQ/HIrKFQVF9osCi8ZkHM0w+yBAIEwjvaTgGFuHYib177/uilw5cOnG35tzdY28fnKi+mzjdevrWhfUT8wd67iZCCkTUEXtQAv+m5Z5uFuKsnstGkzG9r6E5PaX0294G5qWwh2VBg/wJSkmKhNyXVHyB2P0ZuWTJssrzH9ed31Jdv6hqWUlleWXZsnrT2Mlf/rRiUV3l4rL3LxeX1Y6VVV6/eL+qxVR+Z8x0tOLvr2vLO9u7zC27dzdcfrDis4qNK8yH3w12bb92aaDmcM+J46cHOjqGsg8eP378dgdcKBq/8QwS7rDxhL13ikRAnnO24tw0yUgaQ8oQiuAQm1AI7kVRKATC4qjIsjCTplMChEIPqScq7obaQjIAE4scwg3x9ZHhBQ9GFXKKX5YMymf7QAEaokq9TkeCgpcnjLN7aPQlBVyicI7kYGr15xAhQcQtfIp0QkCN609FnIGwfSFLOstBoBp0eVCGtpAa7QyQTgswh8Foy8S9kMj0R2gLw0dBkp6SlKbRmE33Jz1dD05srhnqOLCrZ6wQrDl2qaamZvOrV7dv94yqB49faG1tPd4717vr2G1XMhtxKy53fKGtsS8F3ie5PckQhkWPNgUzHjcm6S4f6/Gj4KxlFvUo4xk5nQnruYAtKeklw0uWPdy6vHRJ45Ytpu4lP//92c+6TWU/WF7+q6qs7PdHW1rKq8s6z1ZYTdbKijs/dI9Z4QpRbVvZ2b/faRuKNHY2/H3j6ur2bbdHKlY1TzT0NvU2bb/SCmNQTUdPb8f3j/9x5syOT2E6Gg/pEJVOGR9ey1Abgeybfalo5NzwQpwcHYHlDFTivKwIXIE9cAeNmYbdQhKLfMjjhoUxaAKsGsmReFg18g6fIKoc3peiQK1yJD6TiCCK0d+Hx/wuCJ1Zxnj94Ud0VqQUUhY1ew6dcSZ9Nl4Bl8/4RIxwMkSUk+Gzwl4sRWvjo7lk3gDhQRgcDcQmoqLF95/RHEbwxHSRcBEBVSIgDKE5VHIwU094BKGVlD5cDH3R0zvQ0/P62olb5767e+BV83ev3jz/YlfvmpqmTw4fvNJ6Zf3xrj+dvKEknYybVmZzssQTL4ssKqF9ft7rG02TdjeNQWIqZzMZlpULCRZDWT+rZ5IoGsjDUNHXVvK3ZUuWLF2+7jdLljZ2ExPLf72opW5ZeWn94mUwmk5VVZXfqT7ZXd9ZZW4rqzhqKqs8WX30zqK2o/V//8mf/9T+ruLvtWvX3q9oNjccPbxmZXNVxeHjVy5vWLNr/fHejksnvn+8b/9Xn+849O23z3Qv5MmkP2JzgsEVGIIWEwGdU9LeMBtiwUwXXKQ9htOpfhHzgLPDOZhmMIQSEV+s4EIUCt4g2TGY9DgxipEcRgKmFFCO9KJC6AZwRBKEjK9gUIhXBPuBWwqRZAFaWTgMeQ9bIFNMFhOhw6EQTUM6wSOyHIDz62lGYCWnQMY8OI45PYibDTOxOK8jEeSDn5UtNB9x8S5UsdgkBLlzH9cIZK4YoD2EE437Zs58tbmnZuDcdzdvXbv75NZEc3//7dvffLWppmfXJ63HBgaatsUGxlzFyMzEfQymnyycWJN8Wc8km33XFw/E8jPzWAZF+bjOouBSJvX3eZTVMIwPhEMeFs2ysvtFtORfjXXLti75eOmydd3rXhRG/7Lo8vLSk0tMyxdVLrpeWV1VWvbHj/5Qa77YVl9f+huzedHWisW7raXWz4ZeLfr5xeqW+7VwSWLt2stdDQ0Ngwfd1y9fMZeWlB6/MHDheNO1a99vL//DPz498tWOq5Ms54Vh3OKE8I1mg7AEYxyLkj52nMNZDyFqvgkC17w6yBKDU5xX9D66PCKlw7rI4WCKBcoi4kGR0xIUBZkEIiopFXocwvrAb/dhKIy0mSTmxQFSkYKIOsbgpCDicCYBV0ICCi4MCIlwBOWkeBqk36MB0TgNpXkMhlEPTVgQJIyT7jhLYDyoD4Zmg1A6CyrRNEagKKH0OaN9GClZCMuCgYD7tNu158t/dDwYqOqquXXv5qVzT4cenNt8bMe+J+dqjl+50DRQU9N76/jhwr0vnk8kIGSLSTRb4KU4C3ZgOjmnsKwtJ0CiDVlp2uP2LHAnz4QgTpXjHo8/4vGMNLPuYazkb3/73fLl/UuWfPzxktLSZUsql3103XT91PItpWVLGysb60wflV//6c9+u+hoZcXiX5b+sby86o/WztHq5pOOyE+qyuprq8zVa3dXVHXvbm9v79zWfOfKffncn1etP72zp3fziU3fcd3H9u34/MurqYevIz6UIAWFYfwW2yx0eFj/Xg+XQeWXCdeUosSzSAJGklDoxUkPxei2rDqFAx9whhEoEBxAVpYoTqNmnr4jcHaO4iBgLhSoXI541xxljDAjM7johXwNGIpbaKeA3xC8SthNu6STo34EIIbdIpZmJsZiBIKyhM4gYYtTYggGsSC0zYmkIMjJ+1KoBZHBV2MWAnBNWOg4HwPxyPMWp09HYUv7FSLL04RE5HxvvjzW21C43XPw6N1LHZt7dn1x6Yv9x/ad621d1XrhQhMs0tbjPXtvf/E8ES3MxnmahWukMgpmQOlzaZm4TQ5k4Fkm6sfiiovFIPqDPMgts7KW4jyFuWeSpoyU/O53f9m69dSSJaW/+/ijytJFdaYtVfV19ct/U7p07GT10tLlje3Cfz6qMJWWdd78yU8qTX/4k9lsMleUW0fuWE2/rTCZL9Z/NjbcUm+t79rdMDjYtfqogz51pfX0zle3BmoGvigMndm/f9ORPeGR7Pmn6XTByTC4MssEFM0TOZljMzAxotFYDnPSnIUh58BGRbVD9xkEzQRzBlAALAXcCAjGnLRICaiOpTx+mQRSKaSXHRkmOTn3Vh+ldMnm5IQMXqDgKFFUWbhiE4K0lWZ4YUpXI3wh7xNUCyHwwsSHgoiLZCgIjmChzxG0T9KiBYTQCR1UAhsO6Mx02zTiDXl5CxOWIkQ+KBFsHyYRXJZwo7IvUACOxWw25Ub42ed3T4zcO3hz+knNve9fvLp0be+uB7sGano7Wi/0Xhhoqtl7815rz6UH/TM+yUVL0RxPYzTGYrrsVnjeJucy4BX8rlwSJlIsjLI3CkGNxzz5ePgtG3UF9QCmlPSNLWtZ9vESINKiZaXLfvXznwFWdYt+u/y3H58qqVzeXFpW/eJsVWn9R/V/vPnbv/7MWmgxfVb+uvT8qYqKP078x/xZdXdb+2eJbOnqkXXt/zn58fr1Gx+0H75y/FrT8eMXjvcMPN7/9Zkj//h05MabqeEvn41j4USUcFsESzoUer+n0ceAiQLhh2RBRHAE9WiupBaZyoiIk+Q8iIFQHKxvkYoFJFzDBXmaQvE0wy3EM7gzV/D6ZMgTLFNCHhdxV9ZCcgIzPU0KFLxNoH19McwXFhkD5k6LMHrrckygAJmYO6BAC+OmkiJi04edRCid9jA0cAbhgNU0x+rE7IhBunRwUjzBW2QPjfJI0K9JBO+SCELTZQrJollPOpwKjO85srnj4IGDTbvAJTwZunbt3LmhT65c6+ltbd286lbTkwu92wd6Do7ln5585JpV/Lyk8XFMkjCdlTI6a3Pn51gsFXJFgzYf5pLZcFqG8Riz2dx6SAdZsmny/zi2GnemFz2+7efXzvGyMNrMmC2xPcu5bJqc47izO/O6zGzazAybTZvYakiEDlteZtI0m1oiCjmorshLiIpQSnXveflj7tf9PfE89fTw+H19v9/P2xdTeSktrQgmXIonLtlv7s8U7PkffvsRm5cc6fe9mBifHEWMT0sOCol+HJbVEoL9NSix4HJEh9/VCKCrZ3+PEFedi70YfuFiy9qFh9eubc2txbYI+WPusZyBnF7fM/glsuj3lt6X9TyZun77XtntxakNbR/LgZL6Ngwr3sGZYW7wcVwG7CIbQQqmK1CTYRxOylapMmxoIPAhnNQQfKQ6nUHOGDp8TVcMnjoed/0rwbtesJasAmixrc9nTvPgvQNEzzCwTymWZ8wA1XAwFwnBBPrpDOov4FvTO0/N/cXcPHNsch9/nIEZxzZQZ1gGBKWyNWwSAUcYfDoklXoycAQ6iUDzssw4hxehkkgEtpUtQnHAUFFEi85ZAdhxSLxAEkNiOKqd3a9bkOnHs0ecz3TNI93T+hHdmO7V86Xe7jG3cazC3Wuc1q2afN2mpQnbv13H30hjdVFnXf1MpmFXxOrU8tj9qIhaI5DwGJx+CRflomi/3cGwij7PoY6bV1o6eSpM0YuStJK4izdjwl/kXVz7Iy31TjLm199i4uPDk2Piw/ziQtOIBS/9w0uCEieaYrFRIdGJ4aE3oiP8fg2LjohIiI+NfbtFvFzaXnWtMORCVfq58pZ5cbk7Z7Liarope2xAb5GNNMq+wU9y//5ow/17UKMV7izKXdmfrd3Xon2gu9BoKMR/vB9Jx+LyKQ9KPR0o4YiAzNCADB3rmLbNY6n1F3Mw9MkpqoClAJdohUaTiqwkzulBAeivyNrh+umMM+COev/iiaB7znSip8+AdY0jw+YBfkQ+7fLgbCSSCgE1lJTBpO/QOEq2S0lACLCDUDKZpHltcGygWhoCpg+UBbQYVEtlkuykIb8OD4fJQmiIMmvdxaHTB9fZEg2Ha/j29kND2ZOGNv20/lmTqVfnm27smlCb1H941semu30DA0Jjdq/R8lX+dFrfurBltikNKJerYrk6wVtgesxMxeAK2idB2CiD18l0zPWjKJWFortVq1aN6B21v8bO/RdbqcGUVMYVlxTlvaCEJxYVhBfF5NFd5WH/KSISX6QVJRBPnAhLC0qbuxveUXRxLkNw+cTFaL/oB8kRN08XxF4Omdm5e/lh6J2siRvtzpF85QWnqV2tTozXmdSNrCE/nbB3AJp9Sd/4pG7q+v2l1lZZw/37Zav7G0dDewdTdW2Lsxtcu1YipUFGgD2opJmpCA13ZgvBMRkZx25esOQMWxoYXPPOnkHvFEilUC6cVELl4taPteVZOoMRyJYAiqAzBFZBhitQBTqzZ4fNO0UiM8kcngs3aMOdIWTgTnPoOLqSHogSOJwMMhStkycIPD0HGI7OxAXO7NBJHpdEYOMhUi6XM6yko1IDzYBC2uVodoXrlzv/0uxaoeOYM/9kd+J4JEEgl6NhCo5WrUvd+vGv8kb52LOnOp1pWi6feDQz7Rt4BvjAONA7LXwKRfNNt1p8zT1Plna/vROo2IJOh9fVvztM7UfYcx4YZFoN5OU0CoYZ3WSDZddPRW1sGHDbNqb2s5LdKXJgUop/T6HkpVGKUtJSkpMS04gPNa4HJ2LiYm7GFROTE35LqySmUi6Fxt6M87tYU5MYdLYyNuth+aXHF5Iuh8TCnLscW535uD2rPL/l8uPCx1U3ohLi84XNwiZ1xWRO6Vn3eGNzs1xWVrdwa7RVPtr4Xta6d/1oljU7W3e//lbt4uLi9TotSqD3H4dKOP0QPMCB9pwBUTLIOJFxNPDKgod2rGd40EN9jmAWGkzt00odDhzSF0wAwGFFvWsqesYpEeQ8373bhGAGkw7hGs0OmYzQM3hakt1FJoA0i6OCOZdxGlqRzKQPMnjg/6OEd2wzgQ5eGkLfAamUhVL7WQLP/yUzJnhsBImGROcom2pdn2xWhAPhKJdWxRhS0RES7w8FZ2el9v5B43izrPHr126A1cIx0/R8Dc/XrXPruo3uAWO3W+3OHmv9arGMNC51jT973fjEPqhgq3giAZWpVKkkIhvL/JaNspC15yhbY0eBEXqqlrVUrZLXqXGhEpBKJTwOpqSEUhRzqTj54YvzycmJ4SkUv4QYLAYbE5MQlxrXufnDzZLUteSrQWfDz4aFB3WEXErICgnD/IpJOvc88Wpk9IXLEdHhHbnq3IgrV85FdGQuq1aqovJNzoGxnF5d7+UszNi0vHF86eCgp17W9lUuf/Ox51bd7Oz1qdsNbbIGoLG3/3i5uyFBpFSgn+wP3GAQuKGNADHzmHQq4AcVqAeACXCBNKYB4VKBN3kNwap/sIIhpgbhqGBkbXaNDAWAV88joUD3XSQ6zzvhGyLjMjLYOLPq1BmQzXAZvDMi9hlNBs1M6OSRRTSe3aXA0QkswvadGk4fAT3Wq2lSnMD8TxxLQCcNWwkERGBVeV3Ip1/+WYOjqrgkjsrGIdkEKyoNqePDDFVi37/dINd3z2epBwC2GY1V3m9dI2/G10xVOe6BgeneMYjfDkwP+PSgb1sG5pucTv1q/betIy17dVg7E35O4GVIakDjY/XVkPtZSolVIek/ZCtR0bBI9XnIi2omZkSdIjMmpbKoqCgNf+JXkBLupoUGUWL8KqO+JObhKfiEqDAK9ktxWFSYPzEo6KHf5ZCipJcFD4K2PXd/67gAEvh2CWQbv68FhYiD8JH5amd106uqvzPO8UHkGDBVGwfWGe2ZvmZLckhPj9zS1tXapX/6dPT+4pO6W2X37oNS19PwTXawe3tqRUsdAsQsBSk0MLB/M5iGo7PoqjOIQUNV1khZZDKTSe2flbK0tEAWh8sdZCMojQSjirlpDqz51ybYbKhIoQRECDWDB1fz9192egbhDIkMnDSDhEPpdDLKpqEkhndop1NkpeFwIo0VLO0+jvcvAQIpAzpobzMfNcdiHJVktqqgPgj9ude1gg4q/6aRYCsxAGrYOID5NJKVz3/BUu9bWQAEl4WPj7/WdNjrHpsw6Z69adabcgEf9OaLx3otqzkjRp3vmU//ZnliXmia7/ZlG3cXhoZZAtJLj8EBYR8Ot78f6acC82GZ36FKrsPg4KhcEskQwmN7NAivk1ODSSuKCaCk3Y0M+C0g/FIq9mdYPiUlMX5RxanJRRS/mAQ8Pj6o2Jzi50ckhj8oLkj7wS/6MW8TH/YoIiS8oyDswYObF2NDLuwmEatzHlVGVDe9dKYX8vludcve08KqwcBrlmk18ny8Ud/Y2CMbrW+DQZcU+gbgwld5z3uZvOv9eGv9rbp9rUO5VVEDrh4CuBsaBvUScCA1c864UBoXJTOl7whalIawhoek+zSuxBBMAioJMgDKwgmOF/8OnUcA/A3ONt0gwKm8Q4AcVDgBLyNj59XQGRwJp8FxBJ/IZIWKQJ9T0YDJolt0EAjsmnc8eo2CwQC4MMzmcggE+y6sCkhKdQpYgyISEFMvzDzgPVBDpUtSw9XCm1UpOoc5c/vPXy21B0WMvOucaOk1qY1Nbt10ti7J69aNFT6a0w0I1XwIRxt93dPzI4dzLeXZq3xTk7H5+gcrD93z/FPEnbNyeCwpSlV+9gyj2n72J6UZVaAMlEe1i0Q7tqFOlVLEwZwPoASkxQ0Pv40LwKYF3C05SYlPfFGS15H3IO5h3IuUi6EpJSnRxORrlQXFkSEhd7x5/tFJiUnRoWFffgoJuRj26lVxOCb6ArMmJDci81V6Vqa4hV9RUSFsyXRPPpuedHbpxrpXnYfNzeM9PV1tPV2tPfP/VHV0jcrfy953yWR62dfW1g+3aldQ6s4/Pp6iqd4G4yQ4tk1hPr2ya0McyDtDIERwESWN5gjuN0CYxjzxFCUHZpyZ+WQDfwAuB6g4qwBBM1BuBg3MHRKHbDcEkjUuDwMHCgEHZz4NpaOTAgkqOiwtVwaOw+YiZIJobS6YgAybcQIGnUeCGtCBKg+qhpWKtYl3TBFcGED5NVzG9wcGEuiZCIEOuo+hU4Aaao7ss6gEZRuOPvAOrpiHM35Z1/W6e3snnRdCng3c6dzL8emryRqhOwew7J9bQsBzRnfWNWOLSasbMGaPyGU1Ko2KwGJJRJ12oD/H1IfL7hdAzpuL7kaYpP0b3FklVyQSqYAPiTA//xyApRTdfPvlfFjQWlraq/MUSkrqTWJRAoWSQCnCR2J/v/lTODEhL434siM15GbNHWLBxReUsKDI7QchIcmRiUmV3x/6xZ6NyL9y4VpERPqrkPak9nI1f8L5UmdUt+gGnJbp8expi+W9XNbVZXna0bh2uLY6Ogo+sVx/zv/Z19aDeVlbwzcDSt8ZpJOVWjOOtmVg02w0lmEOCQ7W0Mg0EU7LxeFYIOfRHFQNpETIJOmu3bAMbUCYI7igDgDVvAIFE8yi4E021bHXiXBhB50RmGG1sHl2+B8IT5nBI2to4AjCjqNBLkejoRJwVjod8dgYMAtROgMgPBPZHPrFxRSwa5Qw1aysvt3ADQ4J+BCCMCUMJgRvFmufPNndslnZGtvsk7LHNb/891RVr8mkFj6bfGTXCb+Tjyn6WI6wJV+ta8l//Nht6nWahHOD+QPGPtMIaD/TTsMOvHmepHOXx+JxqUsOKgqPBH2nEaHrEqkInT2qUnusPAZoFSIr5uQP/gVFRTEvEi4VFyeep0QlpGJO/PhDHCUGfzfveA1FEm8m+/nhywvCQiNf3CX6+d0Mi1M8iAs/65fol5gYHfIwckuTFFGa3n5N3Z6VVVjVXlitVueXl/PPCdW9A73GgYHs7pGe7sePSkwy2fjTmq4l2f33bW1dcv177+k5tbynbg6uIuqm9h1MRAFrJViAowlonGDqJkKjsQQ4hMkdlEpZLBa81kEpnIAMQ+iC3MfzMM04sojRx4E3C0on0yUhKVyBGf1aqChL0Q8rirzmPKQD68mwArbGmQNtWjq0CdeBgxpJwXrqQ0V0goZHfl2DQ2DFIIY/H9y0IuwhD5vUhyhRDZUrsC3sam2CThDecAySlolSh7Z3D1pHXw/XHXhtKxtHC3Xlz83z3cbq/IgOo4lvyhFOGqE6k8LJdP5AzlhOBb98nj+pmzRVtVSo25eyQfzRNel6PDU81juXqJ8zZIYkFor0Kz4OelGnp9OqeUcwcFGB1wAK4LCLZ9VsY87/SIlLLUpLjMlLxkfFYSlY7NzbtP9gsCn4hJLEpN/j8JQwYvjv/vEJ+Jj4SmLipR+C4sKLIpPjUpNCXvndjPkRH3v2RuiNgih8+I0LyeLo3PzqG+XqinI3/7VbnXNsWumnp/Xd48uu9arXy93jenkXPG2jjXf0stVDZ1ejTA5w7qBscUXCOBbQQPyh4YbYUqVosNPOBtlKA0yWjEhrNoMhWCH58w7YnBkkKAmc3eDoCKo57h8ygiNIhmkGDRfHodIAsAMM5Jlx7/5ig9FGJgNEgC9shQ/6ULmShnNoHSy0D8JPYC8I2ATFP9gGFocg2ZrYs3sZHNKmBu2EicfYDn082jZaViNAuWz4Bztkcg17w/d6VmGhth0u7F4/qq0tuycbGch2v5n4SJ4sFJa6jd97cyZzcnLyKypyhEJTFSRv3fwm/rLJncMXC00Twvmqp5MWuwblidBOhmRGhUrYqIRq+weXhWo4bB6bq+HxzCqvSvl5V6W0q5T9mJTzFJCxwxLCUwu+FONP/nwSe0fhBZ81JiAmxi8l4WYq9u6D+Bcn/pOMjQmNzyv5XlyUEJRXVFJJTE6M98taC/ktqOBsZGjBo8j/hDzOKt8OK2x5Xq4W5vBzTEIhqLnu6jGfz+lr7vKNNL/xjTcuN8pljTKZrOtbl8zSKJdb5PKu1rIPe7f27SgHJg3utEoa/O9TBho9wxaMeBHILtp32JvBLAlcORnYO2YGDjLE27BuzGA800nwJnEiBV2pIUhwZNB/UAQqLIVPiAgViRAc6lESFCgs+WER+KPI6sYqQA3HxqzDweUwECaTwKEhHOoR20VVbJH++182z8MZ/ryuISEcwkfyaZleL6tdPLJtODgEOtXz9vBe25s3bywmWfaupXV1yXKv9b3cMj9SXlrRIs4RPpqbVDeVlhv5wuP102t05ohLxWJ1llOnuyx0u3XPSicvVDSpq5s+CIYGPZ1sCarhof08CSoR9bNYUCoQ4QyqmhqeeU0A2ghvfUfl6sQkEYtiSqLiCvBYCj6OAkLcz6mUorgYPzw2PtzvUkqCHzEKm3p6J7UIj4fQjx/xUcqjyLuff0+OvHj3ZXxYDelFfFh5eOyj/76Lfxh/ufxjdXp7YXq+OF9cKIRG73XHYMPcoSD6TI9bLPrmpcbDD3K9fNXSaOlqbLS87tI3dvXI2t58LrlfN7vCcUF7zIIhESwxOKgCJYmBDHregXgQ7MBpmdS+ftAy4ZZgu6y2+hTwUAIYo7D/CTwzHFP0oce+QTD8AfTHwuGg7zhazxkcapvdvvZAQ8cNgtiJcjx/oXat41vdxtT+ikHbx0VJBBZ8U5JXgBBIm2Y7wkAIa2YuKM+IxFvjrX2vt9xbWHhSpxgE7jpj/fD0gnO5sVvevDzu9I18tfi+Tk/7jAPOqqc6E79CLM4fEzq9z3OE4skWtSlfzL9zWJqvfvp0TF1TmFghzC6NiC7PqfjyacjO2xxio2gnD2VDhpuLsiQskcTGE2wOdYpc2h1QFrZ+CVRpXGylFeNXFBOV7Ie9VJRcgI97G4n58Uf8zQAKFo/1x+IxXwIefMfEpSZgY/yL8L8nxD8owUfGY4iRxBBKUHx4MagLj/ISL0YGVT7ePhvbXhWdCU+EOGsiAhvvF9tSMfksoicivH7N91btyzZ1+7o/THTr9c1QJ4ulsbnLMj4On1q7Gg47158AG+L2aezAb1guOIXkKgA9K+GWRkrG9Wm5/Q4tQtOiZA2KML1//RegLwQEAI0rtgM9W4Mw0WjDNCkKrAf6hwDloXJoUq0EkneAwr8c3ftARwDvEczDDBsq4Wp7ytoaFuprp44MGywtxwMclAMKHEIgMFAcnVPjUQIeAMF9qr5N1jrv7G6Uty2swCrfv7508emqc96ZPTJS1dJsGjG98Y0Zu6ugYUxCiD/n94pz8qvE+aWZmTmv80uvtORXlWd+FxaOuHubMlXqan7FZatKyJ94nbXrUpkNKtBGj8OKQ3/ucVEt6mEpeDye1T5jZprZIp6Sy1DZrCoRJo6YFhNTcAlDiUkr8mN6H5xILUopSnuBScHnpaRg4rAxaccjMAGfHHqz4EF4EjFhPSGoMunOi6jKvPigF9FJQakdd0NjY69e6bh8NaIjujQ+KCIzvUozc7NDDMN576/PkyNVY8vuMcu0r7l73gl2sMWZFN7R3KyXN1q+ftXLZbKe+p77EJSd3TjaM3NILpp0A+cwmD2B8NJYQF9ZZKnB3AdkByGvV9lw9DODHijDkIdk2JFyHHQBSUOARhLYCDTcdg0ZWC6CkyLwSKWA1rSswM2/T3mYVpuAx8wgMI/TUosL2bL7o/ch3V97fUMCtNdFhWAUxwyiNsIBP4Yu4ih5jI2psrKGnvmq5WX5kiz17DuW5KjO8pkwM9G42mwU37jzsqV3TJxjdAuzhaVXHhtbKj7mQ5FMfHXSM+ekmz/RxM8R6gB+89X81zdyC9XiqvJCsXeiI7ti8ttBneLoSOFQsHe5/bMVxiHQr9B+LqA2nnUQWBZjWKRhC1ydnSIeD1MQTjn5U0BRcQA+EZ9wd2cmBhtHSX1RiUnNw8YXpPn7BxQU4QMwlIdpxBS/SjCKktKwN09QiiuLk8OSw/yIkZXEoMro6PD0kOho8FuvXtU8jD1X+Nq6+WUexi//kCHsHT8YGMseGZjWN11o9418sDwt/pz50zXopK7l8fmk0ObmHvmorGxpvHb/usfBWf8E2r/mkQYOAug7ChCtFRwFVapFpDQOC7HaIb1BAt4Y/Hm1Azl2ZcFUI3CA6TP+bOIS6AobbZZ1/FCRtVcG8ESDzXaSamt9RQHpTgRgRX/f4kHZvdGetvdtH3tG78sO6ve5kDNZMUg4fUwedQaCECRw8Ng2ki0xtw142t48/Eq1Tswo122KoW+H403ObNBHs7MGBYNL2e5Jdz4AoWiMX/idEb574KO6Iqewnf46vTCHX5hv1LmNPqFwskqd3lKaXiFOLyzkT+Y8rXC7Jx/v9fQ0HCwYDBIbe0/zC0HElfSr2IPgsyhcVrsCGocnUoHRwRDxMClhDzooP6Zgk4gFScnxxf7YSn9MSsyLVzExPwek+cdR0tJiLsWklsSV5KWmECPjOyhBeD9sFBFLjMEnVCanEol+HXDPig09e/VzCvFKbvQfE+Xtwojq6qr2x4UtpnK4AxcajZNjY76BiNT4X6tGRrqfdf6DV1llmZY368efKystctnhm9GlD7bthf3rRxtSAYNFQmY/gXymcfTNuaibSlj1tA8ChAb7BdYFTDeAATtsLw70eceuHYFXr7WRt2v/IHFQqE0f6CXcvq2qpu8Inc5gWUlKRDm3PhdMH7aTGTW7K1MNDe9l17JlS6OtXev1y54VOOtfARdkA8bdsbRA6qQTlMPMz9/3l0feNFU1jejlljfKwLlv35YPx5/pql43W7pHst3GgZGxAbiLgkPqSx/NhBqTbwAO35teQ3amPbO0EHA2vyq7SVfBz63OLM0trRanX04Iq9Cpe9NzK/i90/qBgZGGJ9saxeDGp0GWaHhQxOpT8VSDIo2qk8eD7dPpYX8CNdCFCQt7ODjjX4k9T3lBiYl7mIr19085j4k56Z+S+usJzM0EvN95/5iwVGJxXspPWCy2Mg+bEBUOkjUxcvutPzCk8LMXz8auXfULjXxZ45cbfS4i//GDnNLL1c/b85t0pvyBCVN+03P3mHtAuEUayssacY40r3mHHmd3W5rH4QBi7rUFLJOuY65aNzW1UTe7IfF4rBBtcQF4Ru3mptn56hqWjav1sJUCKUJgqmwEEokm5Wj6JH3cvg3OLFXDMkBU1n5K5ZCg2vX1lf1awGh/4mifOCKNje1SaRgEFWOQDOI2Had0aG+3td5qtR+M9oy+f99wr75hdna/9lZD2eLC/gYXXZ9ZJ3h5Vg6dtP5vTZus582Isx2f5Lx3+GG5xtl8+EFv2RsH3Dnic2brBprVd5y9QmHO3X/9d8jo843pdE6hu6KwKj2Xn575/Q6IC02T86V7paW51REhV3KjVif4bpO6NH1SLITcXMWkrM7O5oognd0vYolQ1zBb5HVZBZ0iAc/KE7mUM7vflIc5mLwXMcUpMeB4p6SkUpIoAQElJ/z9z1O+nIh79FNA3I//OXHifIo/Nibxpx8eruUlYrH+FDyQJD98FPFSUHhkSNBZv/DIoDshIX7RF85dbhdfzsyciLjQcSO26kZpR+ErU46Yn1NodOdeGXMP7gzemRgYydaPNC5nAyPSd/ksMn3jxLhlyyTrWm2ob9s/WLqu5ZhdHCouA9QvMpXlMVNRjhdH1e5T+3ECVGvoc0AyCeEiK4a+/dkNOO8wbDgcDq32yCHo4zgcXy6kX5qaWjE4/vjbpWKyOQYGm0onMY9VH14nDnA2W1tX74zotrTJ3o++H711v+3WQV1d7URoU8Ot24tHGwI7G+ICnQS4lSOBHmWRjzsjy5ee17e1Tb9+MyJraJWP+Frv3e/56jOOXauSb672Tgrzcy+VZ2fr9ePGbHdOYX56LL8qMza9pbylRTxRPiHOr7iRL77yNLNQfEexkCMsLXx8OJnd/bXZ0tw6tQI3WlwWj6UViETDVu3gsOjdZ7YHCmSF4waNZ6V+W4QBLTuVQkxLjS7BBp3EBFAolygn8fbUlFR//59/mnvx66/P7/xYFOC/VoPBxmHisVEYYtRvsJMSiDGpIaHR4TeLw/2CIsOCwvNOnA2JyLwShj2XlVV6I0Iszs09l34ts+JKvjOf/8z7UNw05pvXVWXrnPpn893NPt9I47RP77PARP+6rB8ZHy+7DkbR9b0PCtYG4Jfg7ec8DrxXmtTmIWxKBXMshIP2rV2+6mES2MgKVMbB1T6482Sq9vhKamUW6rSx4lh5dLRxd6psccMgmLP3AUriwELhDDOBr7LMg1sMFgfVHtwf/+tJl3xbLv/aOg2dW7tw7/6aanBJtvSkDG4EtaBIgF0zJJn13mqEAl3b+scvu10j3atPpy09DfWt4/qR+rqFVtnAmPB29vvmMSO/sKWwcMIHqgkfCCq/UPxYfS5kVRyPr37zLF0ciY+6Is7KvdKenpMvNPKNwsmcfN371ltTq/dvd4R9ALjGBXleANa3V8Flc61DDkByKlQhYe/bFtruCZ2YGEpRKiYFC6eQBZfiSvx/9T95MqDy9/P4n37APzrxSPTiB/+4E18C/PFRmBRwibBR/pURP2IgCwS0NYqIj0oIC/ILDQsPjXVtPbwa+3K7A4MXnzt349yDtxfSYRTHBv0UVigszN9rUYMAwgfJt+lZ03R7VW+zLnsa5Pe9a/EjwFYtH1afgE1UU7+0aK6hOqg2FAGigNgNx/objQ5Cstm8O7iCzJwyeAl9fXuzG1OQclh89252CggNHIrWzVyHSs3Ofvz335+m6ldX9h2GFTv0FnqM8AQCQQ0iAJ7EY/1p1tbWNyzp21oXet6De9gz2lbsrLu1PLTc2Pj0j4b6RVhGs+sKhM0cXj9oW97tznZem9meyDaZTDKfpXG053VHVtTrpf0PDc29Y+Brj4FCwM+fL1erdTAshBV8Ply/81v4EVfWBG8qWtJzw1ftgoqI8ur09MwWWMbuihyjTpddVrtyZP8CCTY2V8DsH7TN9PMMgn7Vd+CqUB2lhm3br6u99z7d3ZuNARSdQgkowWDD8OfxM5d+fotJiIOxlkz57STmhP+lX08EnDzxKi4VW0QJwWCOnby3njQKNgpoa9RNSkJUQkJ8ZEdHUFg0MT7vwtWCL798CrrQ8SmkJeJibESsuPQPxp2zmTn5LS35+T51k1s97xyYmH+9edGYrf8on/Z1y1lvfLLxccu3rra663VP2hamjlAp8nyIPSOg0XBa5ayWSkV5bFgjCKdPMzS8oxrkajcWpqYW66ZuX58qq60vm1pcPFiou12///3l1NTC1szC9do2KFZdzezsNygRCbCqBBUcR+HYDK9iUDtbO9r63tIjA7tD3yVrbdsULMAE65I1tzU2tB7UL9Y9WVkxs7nDtp4P3QPZv7+dmG+abDKp53UwxrKdL0NDire1fUcHzQNut9HIn5xwtlQ0FVa3T+ZXiFuEWcADr2Sm5+bGPviUmy9uORe/VvO4pSo3tzS21MQ39goLhe4xIXFk+frKyjXSEVOlGmLbh4dE/SIenNhxzSyuxrUX3bRx69Yt/VjO+tLYGBQIf55yPuDkz+cDzp/EwIw7mYfJCzjx/Y+fU+N+OnEi+QGGmPTbSUrLnThsACYAdlBMfAyAuNjksPgXKVHx8fGY+MSwsKCzkZWJoREzFy9GR8fCXdGViaybWRGZN+CgXwz2EGhSWRPX1DfC1aa98memN1XG6QFL8/R0t3xang03x6Ojt+49eVJXX1d36EC4K52aPhUDAXrywYDQUBVIBlaFYBBuJCBOJzHMXr8+tVpTW7t4fXFp9foivNFvZaOLtxbfmSGJUjtVOwXPk92hhamFqY0VFLFDyBDEUCq4DQTboGKr7vbo02OzYxQaV97T0NbQVn/rPpAx+Mvh4eHevXt1Rxsb9oXZewf67mcDj5Qvr5kmJ9V/PM5RGyeFIy/fmQmM+o39bxdy+PnqirNEfn4hX1woviwuzD10miYyz1U/IF4NjQ3JzEyPuHI14lqFsDBXHJFbCi0UH9RSWOFW868N1b5vra9b+q4UuDSApRWQJpGIvDa2QtLHZrPXaH099ZbmkUnjM/Vk0/84uBavNPIrDN0pIHTKmWGmKUyZ0u0c2ZqBwlZWY5WyqSyKguUYJbFIxFUXokCEBZWI+NgEH62HuNUki/ERH8QYE2OKrxizMTGbx2oSsyabbf+YXgrneMDgCfDNvfe73/3uj5OfD7hki8ViDtwLRCIeR6w9JuZyXmcXKTh5Nep8hH+5kiciVdVcDkfI4QFGnB4RhBJXJVPKCxVZRVkVSxSB5GIkRbF2rB0nJyo0UzjbE4R1VotOLx0p73aUW6iR2o5/dTfPNXY0XBsbOFPScKO14dxASdMP0MC2tQ52hWLhUMiZPL919Mjfj1z46oMPTp8+/uaLJx+9HTn4+G/nj//8VqbSgMp/9otvFyJrybQz4vPtKS0kAOVPLPhSTp8PrCnJSMLni6QXfMnvBw9T91Lff/Gn34CXreX0xz+HZd2jR08/OfomEm7cbuubr+ofGJgc2FkPh5LOWNdgV2tbf9V3/eFk/5Yf0Em+qlocgDkBTOLmu880DEVLm7eLS6GGjHz67wuxRHq+Zc+mN5hGXtjgs9XC7rte53ZbrzvMbKD+rK6ObW+H8QuzGnRLIaKCAaPUYcRefjzM5ZpLS2vfTp6ZvFPSthc5/gTowNevT3399fF/bZ380/mzR/9+6/v+E1fPDcFQ+gyE6I0zHFG+SATwFMAtJwdCRazOysrmc7NEIiFXJhwV1wAgslFEUiEUclRCwAiiTAyzPYqrkCtzWWSiR6AlSJLCSRSlNDRC4ySJ4yNPcqvpxr5oANjcvl5vwJVcXnu5Y66ks3bm4tiZkuWVzouw5mWSNv/wQ1tTF6SarnAyHAJGder42Q9/97cPn37w4W+eXDp65IOnv3/w8WctT259vju1evnJvX+8ubXmjDh95yIA0M6zf25v+SKDh5E1/+FOxBdpCT1r80EdSYb9t9LphTXf59/e+xN06D/75OOTR1pgj+zUrcPkeGyr0d42Mz5Q0rBcXDIJ/3XXeHigbbCqq6v1ZtXgdHLc5zsxPjj8fHsZQ+Zd5VLd9aFigwkUgWWbu/jM3KeL19r6G27eOWMz0l+3uGylxfoRxhpkvHN1DqnucRDvNJpZmgkyjtKRDr1unqGNntdmi0OPMZKO9hWbxWYrtzR2RndKBpMt5y99cvyz44tHP/nDyb/tsujIm3Ry+mKp5cZ1N4h3rqGBMU4+VJRKAEjAKcjJeidWCDfKssSFMFkdleVli7LzZWW5wLvBPscRciG8eEIeT8SDQbinUI5SEqWy55fVIgSVlAmUKELzEQ1pWuKRFPRHfWwACxJyrVHXaHRsne1Q8RuXO+ZqGxobdx52ljxfbm0o6Xyyg/zQdK5pYLEfhP1Q2heJXDrymw8+/OXPr8AF/8tHr58e+fD4Nx/8/Pw3N7+99+THL99+DoxgLZEY9zkT4Ddx+l/++zZEDCAG2HwCkCUj/gTQh0gkBW4h3xowiM9BEf0YzmsBLfv0B0AEz8ZDgyf62//479ezN5qL3S5Xd0PT880TN9qalmEdq+3t7Im23Kl4oiU83bbd0TnyY7dLji3eudagRCW1pcXubUupYfNyh6GuobRUb9ZbjEGjxWHQ2xz6ulqTNxCMOmgpS2IYqCoTtF7faDZavHRH0GGSms0zq2777/98ymEMmPWwk7gcsDUPxs9+eP6zT748nhGyWx6fPP6LWNfVodKdb0vHXn3SZejodhVnIqjybn6BuFAmFuf0lolqph5VitUzOcLcvJ58bp6MB76fbE6hUMFTq4UKUZEwiyeUc/gSuYTK1ijlHO7l93yckDwaRQmKr8gi7NX7NL0btUMalioEyiJK57YZLTN//tU+bnK56jotnY87yofOlFxsG2poeHup8WJJ6/B022zXiS6n33frT6c//uj2+aOXT90+8vHps0+/Ah/Az0+d+vbbCEhXt5/dugd2oITP6UzEnX7nXty5tpXwrSVS/rVIAgggRMwCPIIQ8kUWbkXm6ut2IxFg4Qsg7H/0mwON9pvP7kVCg7NdbSf2V3eGx5b1M6b594bihhKYvP9w+xWQ/dZzq5MPW8IhXyo523zVUK4fca385fcdY6Ujx89KDc1e8/vy6+XSYLOpHNpP3OEoB4qqN+j13Y47UlNA6maBHtDLVpwUYhjLBlghlw6w4GQ3uY3uvnlcN0GbaHIsUFMW1Bttta6hkvVPPvzyy6MZP/LJ6fbnLSXXrhtKOw2WyeuGzjNnmq/EbByFSJRXIMrOtheKCiDDCcUFPKFYVt8nysvKE+ceE2YXbtQXQNioBTxQTtVCwbsioUgslyv5fXyK4Mq5Ei4fkcB8gpJ4Zj5ZErIbmimMwAI4HqSm2tGsoNFscpU76h5Hu8vNnXXl3YbhnbqhVerqtWs3LgLVXlypPdc/qJvfC43v+X23v/roD787+fHpzz6G+fLxX3754d8/+sPRexAgvpYn927dAn7gB3ySccAotOBM+OLTAKvzwabfvxBKxkM+/9raeggYOJwosUO/ePY2shDxL3x7Cyyct5cOn908e2sv1tbWNHhie3FxsRT6fa85al/xdjY27/wwvDFcsj1QEuvvr5rt8if754Ybyuv0jnnbfjekuKWHI3qHLtDodcCQZ1HfaDEaoxduGyzlmeeWUkcQNtxmAlKpl0vSeBCfOv6TBqFovG/fWmdprzPaV/v6zNJgnTRqnH8RoHpaOuaMlnLDTuzFvafHvzn997+fTqfDP1y9BmWudqzUBS3VGHRVFtN1iCCR2AMVSFwhy+/NKQBqLa45Jhzl8Dgbhe+yN7iQ0AAdHl9dyREIirJndNx3Ym49R1hdw1FxZURBdh9HpKhEVXKlakpLjNzn23UoUkb0aqE27v741WtOQGrSd7uNFqM0MyqyOTptI83Xh26+PVN8cax4bOj6RdjbaBv+ZLmrK+R7vfLm6KkHb28fOfLxByc/Og1um1+ePfX1o63NhTdPHq1fTr+JpP1+pw+SW6x92ekcjy+OJJ0gBKyP+xKRvXRXeG3t5ltf/+tXId/3sdWf/eebZ4lDyHfrkXtfnLzy9Y+fXvkinVp8Mbv8anB2tq2ktNHitumhige7Kb1hs2Sx8erAuarW7VbpZH94tna5rrgYIgSknLGMBzMYlNp0eqzb1jjiMHe7y1mpNMpmJl9RlxGSlul2sA4SHD7V0tPYVysltAiNUqTk8ul2Xb2bRufpKBF0t3u9WppuNzYapcGoudt2fai1603Lfc/lvx+PhL47VzxWbhiy2DoNhtLGhk6Hobj7zZnrwOJkUPTFlROVBVCIFJW5lcIyNQck7ccP7qrFUG94OZ48aF6FagH3nfr2JQ3oCO1BTg1XwM3lu+v4YCvpA/fPsVENhmhQj52k2Pq/nlQwLB3EP3i6GwgYmaCjrwdatWGHzWFxOIrnSxuKAZud4TP3iye3b5TcmAW/XNvOYPLxTU/LvZ9/dPLDI7dvfvAHMOaCbHrq6M9vHV5I+8++OZteS/tfxNLTcYicrl9fSYTnki/OJ8Ee/Wo2ATAlIhFId+lb4/6nsRAcyeJ//GXLwtoFJ1jc1948u//to+NXLqwlxqtiVZsnms7NzhbbwOpSgpkCDI1fAeWje3nsIra7DUNEubqGYgwu23atobi83OAyWNpd+o2fgsaAFNPrbXpjgNHTdyc6beZuu1tav2QOBNsDDMbSLL5bP0PBIqLdnslwOIujFNu+P4PdlNIIBYoCY6KhcpkD5UazPhgwl9suTnu4BHP+ZPq7prEhgwvISLGrc9Jd3thZWw5PvAjDEYuBZwMAuYU5+ZDqROJ8MYyECniybEElhA9XwBOIAB0Vh7f0pEaY+1VLEcipJEf405RQyAfmjYg4HEk2juUSmJbHxYDEsWiGyFmlNA0/cKubkbbbNmrbLQZDdF9q6Kx16Qgb8FeH4XrtxTvDHW0X2zqGmwbaZvsvH3/kuwfuoyMfnvrmEzjo68svLkGN/8e9Nfju0+mzaQDhxcTmuDMdj4cXBp0nTq0nIJji8UvxFICWedUzyHGhtf14Iv0ccl3EDwkx9Xx0aeEWVK/0AtCHt+PgVmkDubO1cUYfZYJSjMFwiqobCexeqW02rN7c7pgsUeNM8E5Dp6sUGIHRZjOBz8JoDJqk3WaGZQAmNxMIBF+erWO5XtMG9mgGnlWIZPCZGYZkpeC3xUicomkpXs9SKIVpkLsMexgN0BRtgsuWwWBgxjBmQmkJBIoH8t68+LU/FW4dgkiF1spS2nm9ePNOsanOVTfXMLq4CREEDSq0qRqZoEAMI29ZJQwZBLyiHI5AzRWoBUUgvKkzcoJAOAovLnxUxs3mCd9B00rKuCJ1EaG6f0xQ6UFwumfiijygw1lGJ5Xidm0ALiGm3WqN6szmulVIdQzI7rZSW9CxfBQfG2ueLb56dXMYbAvzw1tzFzuaqqrCscH11K2T977+8m9/+OLzU19cgHHwF+ff3AOdwO/3p16lneujm1gbgOJMODfHx8PhcDKVTsT9iWQsAinO7/OngUX4bq3vvThMQOHa2kq9iNz/8HZ6bSFyK1PI0utVw03Dk1U7O7O1G1IaNc1YWUxjl+oqVvrcruJyI9CYAQ9HJXXe6wAJp8FoLnfUNmZUxX17X9CMdkuntnGcsxyFj2LWT1y5jXlmvDra1F79zae6YJDGMsAEyXbTzJSUsmoRKY1PmBjsPYvPmxCrFQeKB/8e0NOB7uD+BZdDqm8IvPzzz6pam64Vu2xgJ7QsG4F56GpttkbdfKfB+7vLGYDulokFIihDZTIOSAqjBepjQnguEBZA8RFA5enNUmdDNAkVMG6AksQpyH0vFvOyeGW5HELwTlV2oVqRXYhI6gkSseNTWqsEZ7R97fBWvJg3aA7QZhy74g3+xLYfuGwG/WNvJxyh6SqGTDtkmJy8erh84471zsDkuarvNjfb1tNvzn79+T++ePa9f8G3/uDz79+kfIn19ufpW+mt9dTew/+c7ffF/U5nHG4hwCcc2oLmaQ+AWgRit+aDNjUV2UqBoHB4uPXqMHHZt9dy6VYEEFrwb6VS4dmWyZ3ayeWmneHaPm80aDKSUq0HpeTohrG53GyUDm2e4wabL3/2tcHicgUgGxnN3Q44nI0OBBj8ZR9DjzBIX19QF5BWeO2PRyfYkSmmR9NNevK8LC2l2CgDiW1qI+OUlhJWK4JnhpobjEmHaAmMIXEMc5frgzZA7sFtCCW3bexaw9a1G2euu1w2EB7K9S4waDV6ZxbL4Sy+YYOljgPZTQZEIVskKACylpNVKcspEOYLst5lCYQCNU9Q9O5dFi8LqpEY0BGLMgJdHi+bI9u4K8rnfgMOLaEc2IKCkFRrMC0wFwpFX9ZgVlOfRsrYtCbrBhOEdxOQ6jG9w1yu8wK9NIDAeGPoIgwbO8bGOsDKPDLRcePiHOxBtFZ1Ta8DpX7jD0eSvtT3/2fOED2/fvZ8NpVKxVJ7i+sATua2Hk+EYuuv2mKDyZQzmd7cXh92+uBRKgTqdCKxeRgCtCIwXH7hPw8R6AP6/SaeGhxYBjelu7Gj3a3DAtZGdzQYtC6NHtw0I6xNv68LWK5dzD389I8/PjTbXEa9t05qrG/UewMMw8iFBGsndbRl7rf7ZinWtz/BsF6Mzsa0NEVJAxCJWtqqaWcZTAftn5sGFdm6hCJLxAZL4RiuRGiKRVHtzTkmSONmvTTIMmajQ4/rXcXXjQaXuX2k3QLuLMeiwWi0RfXzbmPdcG1jM6cnU4CAxuUXFPAKdo8V9ObklBUUHFRmvRMIOBy1QC2r5GT1intAbhCCDHQsr1fAF3J6xbxejojz4NMWAA4FU4lEWT01hSAHEm0NWkGgtJ1hKTlJsgxixQ54CqnezEgDUenjeqOjvLg4c6ozMMri4fKhxqHuM5deb3c0rG/emWxtbT0BSw+QvF4v7vWvL6zd8gGxTieSe3tbqUQiFfI5fYkMQs7xZHx8vV4uUakG48nEq/upC9+FnIkFYHOJdMKXCm+F1l4c+lL+lO9w4QW0RanUK5BXnXONJWfasXmmbsMllRqDShqVI5hS0mNFuBRurLMz5tJa6sc///g7mIAqlfAKRl+376V5chKtsGNSHdSQ9qDJi7mj0Oc9RilcnZ35lEGgBLTVPh89gK8fkhv1yE4eWO0eWoMcq0EmllAW66YwimRprIdiAmYjEwgyNHja2UJ8ty5qNur1lqgrfcNoNtqMd7oNrnlbX7fLXVdarOPIxAVZvZmboCBfcPfpfk0OyKYFj/5Vk5OTk1XEW+Kos3rfveMBh1ML1WpB9YMytbr+UVHB7VGxSCCcmuLyYQ9CWS8/Rkr4NQR0RAeE9soUbBrZ8ZtKq5Uh6aV/PVJapBgtxSDZm736Olf0jq3ZYCi2DXWeua5r2F9uvNax2HDnzts7k4vn+sEzF3u+CNa51CBwg4gznYonk5ttzkR6a349DuAknOPOeDgUD5VJHI6gevJ5whfrT4Xg94k1/1rCF1kPJdKpFAxc/Ikt55s1WFNcW2iJpcKRVP/ss05H2X6F1VYnt9KU16ph8NUAXT/DX71U3QMpKIjpjeaWH/8zY9HrV07OM+6+YICtQyZebJCjX52KAj6slGXN9j4ry2oYiiUPbrZg9e+h+udV2xnc2qOtr0eQgBfvZj3a0QqSKrz8UJmtQkmURRG+QAhUt89tCdBzUgtjDGBS7OCzp9KAjjI0OlhLgzGASx1BvZRBdUyjqdhVW9qpgxpUKZNBCPX2FvTJRDzxsUzxEQtHswsh6VWrW6ZQTkEv5DuBgFcJGY8n5k3kikfzCu/W8GDNS8xRzsg4oJkq5DhZSVRqCYQi7IhHglAsoiXhmsFpxi6FZnqDpl9vSE04rR92QTf+2FFqszjo8kk4OGa5uWP7Ysnk+vydkuGV58vf3dlpAqYFlo50BGSdUMgfS599mh7vev18OxlOQJMairfFnKGQcDl0a1INSyxbMedsqn91NJXwwW3Q6dxKrUEnG/H5XyUmd569CT1f9Cc7Esmq1mt17UbTk5GZ7sDrp1orS5I67QSjBYfz/ffKURS2bHD8vb7bdB86/0CtW2fKSGpBOlB/+S1N4Y+3aZJwm0jzlB2wYSq8QK3JK/d1dbs6hKh4oOXiqETTp6VQhCrz2styqxGGkFAoLpFIUDml5CrICTlFs2xw3qI3oygRlAbpuiv4PHnwmGGsdyCm8ZeHenOQwF8sdtosxaUWS0cfR1ZTmQ8ZDnS4ArhV8gQ5OZycrHyoSTw10Oynp1doWVEGIDGoCEsg9/CKQPL2iEWFebI8Li8/m8vh1wj5hdkyWPiiwDHn8ZA0plWq5AgxUY0ENBTTx8DlRmLAUHXBxzoLLnOU6rU2g6P5zENHce0YTBkXV+dBWl6c6y8pab5oz+VMgrzdFLsDx6RvVI9WOf2hRGhvMHb7Zngc4HHGnM7UeGI8MQpnxnyxFJuFfOcMOWdXYFaQ9iViD/wwtwCiB9nRl3p56nkahkzra+nLgzv9Z4oxGtYwSI11QzeaK9Gy6FIfxeY+6kEp1Dsheb+qpBnGaNQtN863u00OxsgGgzMsA/wMSGpPQIpi9+EQeBytw6NzUnsFjgZRBEFyJ8j6VxhKPOiZsZNyjBilcSVO2ZEyFFEpCUIikSM0U8Fwwa0jR1Eyc/KHheGpuASILTa6HYgf2+0ChgIJVWp2Bwnrv1aM5YZOixfcqlZOZctUoSI/H6JEkCPIOVbAA/eIIKdAwKsRC6A9zf3ody/LhO8KoBipiwAlYZSb1SsAqlAtyoNZEges2ypO/ai8kJ+9pFJU12eXEfV0NVs/hezWYEK+DmG07XQAp0joERBprVRvLD2sMbtcW+W2lYuG7VJXZ+kQ8IU7Q9eamwcebVcNPB8e+cP77tlO9rvvzrfGCFRKCXZAxIkdLkLgxMNQcJyh7RMhgGl87sp//zNyGD/RsTweh6PzrtyGMhXyh0LpNRCNOuxpH+S7FOzHpmD4mkqmw5dvdJgvMVrSOmNnUHoCRsJaJb+Mjyo9GtSOyuV4z8woCY2Km1B1g3yGA5hM1PsAWZmAS2x3aqTd2zNl1WkryPuEXJmZItM02t5zn8Sk9Go9AgoxZUXRRxtKmtiQ0ASGZKAprJBjFagdowkF99iIB17ETkAXFOXOmWvXM5CAgOcOdjuMODRO3ije/bxm71mLuRmKkqPDUO6ycfIPvrzvqe+r780RZGdlgfE3Ky+Tx2AyJAIZoajXc/rTK5wstUDw/ywnFPz0UluUqxZnCQVZ0BzxtDKF6m5lLjhRy6qPVWYrCUWNjkTBQQdqneSnHsqDoD12gq5A+IRpZCOIK83MzRXQF2vhcGdwUOiHri83Nzdeuz7SrLt47UbrwJ2OkuXbj3f60yd/qIIFd+ow1MFtDDlD06HpxHg4HgrF1/unH26Nx5OgyM3u7m5Cwlv7ftAJxcmfci744pDm0uk1X+L+L+772vZ8IGvffp9eWwP/7mBbyUjxxIH7ids6z0x4dRT5klCt9tEH3BqlPC/7EamR4LmodsZNtE+wnr68aqxChyNzgAKhra7WkmVuNopQJEmh1SiLazzKCiHabsflElxCEEVu1iRhEUJKoQQiR+X1pISUywoVSjuBkDVMNY5U3GUpYl+CWw+iAMbEpydPW920jgb9oV0fDO6zo9uBAFwRVs+nvyu2BCzltW7vyGKnl5PPXzl6cl9UKChQg1+nV8YDbiC+KxIpIGQEM9VZgoKleiDbAogfwEjMK8o94AlhQYV7O5/D5+WKBJmxkaIw9y5MZok8SfYxBYF4CEi5uTi8NRzr9lCjUD+x09lYkA0uvWWkQDHLy/VzLqCXxSWwsHF9qLHhTPPAs6slDZMlh3Pn2tqq5oabJoev/jB97vFf/nJkfnh8Njk9HhtPdoVgTTwJMZLsise32qbjoVcPEl3JVognGIQ64yF/fDq5uxOOOVOptbOxxFrI54N0d7jXkQLTQ7yra7a5cV6qM+oxFidQlJVjHpKQePgrkIoQCfeuRJlN4/UB/JdXdCMj2AGJV78msRndUvUEBInOWt9HaSmyHUNRyFsogXouIBA1Gy9obY/kybxSAmWGIhACpZSE1aOVyMtyj93lUIgSZmEaUCtXUE0f/B3drgXZ5+FHj3SNwNP1NErzJSyLjbgrvBoTdPWahw/cDAPrcNLoq3Z3I0cmyqu8C4ob1KGy9zIRhEruXd4xDic7g8exVbGgoAza1UzPqs4HmNTQEXHVZT1FMp6wksfJE/fwMLVYmDErFCrkimxNZS6q1CoxFN4KuBsrKvC+Ci0+MVJjx3RQcdsrjGapGYim2aYfM4x1ZhZUxkqbrzcPd9xpgCOYrpYMl7QNz24tDjTFrn7XFd5+DF6tJMAzHg9VQWN6Yjo1ngqdWIQTgMKxrmR8LyMqxDoW48C8U7FY8rlzEApWeC0SSjuBbjtfHe7DQuzehfVkGqrXzmprR7HNAJpnRaC6TopocETTo5TL3y6pPIVcef0EeRdBrRRD0aSGwXanDkgE39Wyml0gYaTdjjIUo2NZqucBfM2U0m7PrQdyNqX14pRkFEUkqGp3AgmiCkiAqFJBKAo5vUKZSiXMzt2AZKdSQBKVK2mEvTszE9VppKA7eOtY1iSfex0FNYLRaaVSqclrrTPij7YDegcYtVzlzQDQsUIYngpB7hEDSRMBoS6sKRAJFAIYZucAXqD4yGQCiKGiTBUCsAC6vNVCsVjNFymAR9Tk1nBlvF6eTOHJzMFVPEFWL7gWMIn89YGWmYCPUr2kfV2hAYa5inlNpmB5FPRCTA/CtssGaypgPXeVwk54+Zlr+7t3zoHTdrsVoqjph6rJgenp8dlX4ckwjKXHYeYaGw8PdnVttg1e6gq3RU50Jae7QnUd8Y3++aQzHksmNt/uAH0IjYf3d+J+SHmD27tv00mfc21vZTLc3z84/bzh4nXtjIXGKeUMWJ1RhNxQYQTJlyshmUmoCi2fpeVsu1WLZRNYkIa1ag+G4ZjGCgqCBvioRMLSdhwEEwl5QLZLNFMsy1RLCNBEVUo5TC9H9kepCpWVqhDx+BRgsnrl6IwKufTNTQq3CnlCvkqGIhrJwxmapjEayUQbRCJZPxNAodbSLxsJJKrra9TZ55lumEbMswFzJwdqDVQcwIcDRjgZCHM5vbxjguycIqAMRVlFOUUCwClHrMiBRzyx4C6iFmSpRWpFjkAA6ABMYhj5cV/+VMTh1qsUKo6iwi7p7VVx5VoTVkExVisJYU8u5co05P1jOEJsWHoQs7l7AoRtYNtDjmK4TIY6ijs6V2tLlpdhC7dkspGpGxi80Xr1xtuuwcHYiVePmk7EBrvCg4ddsektOGJ3fT0W6n/1Xa17OhRuOr0Vf/vVc6hNVeHp9WSbE0Iq/eRyCKhEZPb51pW92Ho8OYhsjjYt7273b567em3zgduMsjUjJKLlzrAcPv+pSS5B4ItaUcqhbJRp3SQ5Q71GaVB6gyjLwmsRsgwYAQJRAckNr9COypUkAYkMReAHpSJUhFKpEshZO2gFhFyiwXh5S3c10NzWr04gSr5ILudykKKXD2dUChMJ/Qe0YCiFIHiUVaIUEYSqxdPocJSlqlem5uYZbcXKK53HYtH5o7UWTmUGIc7dAkhlImBtPCa/CMS3DDyFRTk5QL9zsrJ47yCcAKmisgxIHHgClBuCqbKAU1ijLurlKFT1INSJ+OCX43761aVctVxBeDT1kEKAVjJUwN7z+i2JlWHS+1OOwNwqjEMcZr3Dpq+FuWGpLRp1zWUWCBsGlsEzdtVERYU3muAYmaZWsN7AeQrnQl3jMfCw1ZnGv+vq6p8GJLoOt5y/+G04PN3xw3R8OpEA1rCXaV67EqHws1gi6VzZj6em44Obodhm2Ll387P7Azu/Bk/e5PBArcVN4QSpvSsXqibKSKUEI5SMhqVv90CGVhA4XNgo3UMQb1eiFRQrKVvSTIBWM0MhOPucT8g5qAbZ/0kiL+IIJaRECXcIHTkik3g8BEESGr6csBKir38/hcgUEkSBICoEggt5gHPLduUqvoThyKFyoRkoM3/btyTp2aD46JaLnvNqv9klsfkohkOiG3leTuul5iiMvEHpEYoFvN4CIaQpAYd3bEbcm5WV0REEoCbAo9yfZJlAUhfBLQtWwT1C6IvU+Rlm18sTz4xkiUV8GVwkoG7DlZK7W8ZX8WUKisNVaXq4eVYlkZHoMA+mwfuw6gmPx+o1GfUOt9TdOe+wsebun1bAmVXrqr06BNt4N27ozv8DAyv6wDnQfcBZCJDE2mKL/eMrp+93dcXCXe7uxbmdWCw0PT08HA/DdDWWOQaoKw6/GDyMr8yGkrEqZ+qwa9C5idetjoeSkB+rZp/Pbu+82O5oHJ63Bc31JKXkX3kPGpUEGBgr0RCoXANfYz00lnKVZINSwp1ErRiC1sxYtZ6VEVJF8QmWT+TO3FRmZ/MRzsF+i0ROQsAouLmgOJFK+U9QeSDZURt8MDxNoYRCJiEVNXw5H74IrorPHeGDg7qFevAaVZIwWkOhsVdKKkUKj4ZevLKK4ZD0NMxyd59VY9KZ5xl9zyuzdNnwv5quxqltv4x/cxeTNjHm2jRq0wsXlUtPbHLtGUyDLU4FeSlQZ6HVCloqTDrG6TYdILDx4gsw5U600/nCythwbqsOenKDc9ttg01/nsNxc7j5z/jJ1LSwXulYl6fP83yez/N5noBJgOPoAUWEpCfBwloece0AkPod0zM/r8CFUCB5RY/XC89JaYRnK48dryfJJvGsecxpqyIhGtFMIiVIgBhxYqiapNiErRiBcumBIQWylkVnJZ3TM0YO2PVgYSySb5tuHoZw+fzTxoNZKPw6pgdbJi9jpPX23vVffX9ja+rJ1GBsq3fvQi/Y0N7l8Zu/ufib95a/den5+uKDv/959NqvURK1/+3edwG3L3wXoPvCj37jUnjPL2zvw1Q/WnwImvva9le//xqU0O9u7F1cHH/y5OHKvcnhoVtd3Z1+GsPR+2lZCOUQpQS/ZbApHWpM77EiCQ+jAAKS5PcHMFggCOk5ISrJYcuxjXDZmPvltkAHIrRUuvpAkPBqoWJtqIh+QgYOQxdVNcKyWUlLo8cM+MEmWIrmArIaVM1AUKWFSFQopwKmGWINIcDqgrNW2I4Iv3o5kg7KdshXhxTnjy3Ux8499EkjbQODgNlzYiKTeLyzGm9Ax8EDJgGHB3fRm9i3571N8CGQdUnXoeBBJguokM14kritHe6kPElofTjC8dUSn6LVLOAcG1Y1XorpqWg0vZZ1wtWowGbZmrX6N233gW9HHpHBOq6CyR0bPN89UNhsHixgs8KJlmCUa7yOVQLXt++1Q1h7KjY72wsB6G96l5cPX4xPLS/Dl35zsfd7N3725b+DUf3ary89u/gbwO7e/UvPgQyA9C5ewBNfvnQJVr108eL9Jxfu37j/m98s/vrO4vJK342Hd25O3hzFiqNCsCKvRjBKE8j7DQrxKUKnaRmc4jyRcwe/uBqNCrSqBkICqJlEYyUqSfkoMr5HloiE4lNP0TSpxUKITzStl9KCViwHMk46FJBUiq1YDqOq7kIdEEjcwlxYDsfYwJFg0jGM60ihmiSVbb0oZwNobAqy/ImxuvrQRp1cJ0cadwW6jFbNV27GGut3D+7Ojo7MTBDdsRM6qc0lXGECMJubdXB/92U9zuPBPB4fN8x75k2vm4lcQ5lACDDX28PDrIdjGcJYDIAcccKOxuAJpVSs0loCNbpLQwlRlBtn8tncgzVsEl7omdiOAWeD2ejyH5zr6uhuRcEM0TkrR2Tv9d9unb4y2j7prk35wRmsYbrYu7h8oe/P4+OY3ocs6uL6+kVYbX35wsVlt2O3DPv8aHEZP/zN+mz0uWuw3ov3/3zJfcGlfZA7i5d+/WT50nJ1+86NrdF75XsYbR4EPKp72YaysRo1fWm6+FJgLZxNg+KEzT984I2dksIG1quMTUi16Fw64CspdkVS1L/pBgsvklYVmk0dzQjhcADT19DNqKVHP5TyZYkKKBRjS246ZlVK4xMOdsSHewhqIYplzvxFLdfS0Q0unNLzP/vBxw5p1LWhSHTMF9L9PayVS9cJwbqYv74RKWCicWNseCY30gg2O64niMgDj8WbGmCeJhMCRtzdA1EN7lOpZbhjMe/x5sGXAj+YSQS4+SRsxHHvCDsQCjbLP1V1Np2o/fIgSTsKC09SAG4SukEDzEIzly76hTkiRBpLwdy5sTHQtv3d52ZOngRYOAFlzEmuMNxGk8tXWma6rgy1T27dbv/D0ixGRPp6nyxc6N17uPzdS8uT4zjvMNEeZO/r7m4zWGrd3XK23ru+XnjvD+1wnfW/jV9cef2blcX7y+O9z6f2nizeXfnthfd+ubW0NUxT1uhpjBe8oX3puhAVknLPvvPWFwikkCQoiJgPvvnZz+wb4awaPgqALJCKWRBtC05o7W6WJQaRjDmWZWMJQzCsMyVJDdSc8GzEp6oCzCHpbPZunJUwbH2w/R4bqhMoQLuQlQ9Rbx4HKP6mEyUOG44V//iPgOQpvalIqJ4i+FCisHXQdC3Rsjwh+5/UT9TnstlGX9iK9VTezPa4s6ki36TMNSHjg7Vucp0IES5huhYSvbDF0afulj28mUF/mzlQ3IBnKiJejGaE4fGgiEWPlSdmGHpUBkDuKM9qCOY4vKxmVF/qeQ14P+vsJIprUFSE6POQZPkim2OFrs1z/d0TzbhW4XRX96OPfvjL3OCt6wvv1Q+Mjl5Z6b59p33v1eKpqbO9kOCMw05Plv/55OyNveXJvr4+GOhdTeTig3UIrZ+vT/3ih09+tL7e++fnF1YW/3z/W719F+7c6PvH2cnhvgcPTx1MDtyb1MKb0uVbWx3dzT3BtlD0wUKkEgUhFcuWCSEBVtKsg8NVnGHFr7aBqdpV9YARtm2LUhdSLMXKFl3KH2jgvrVAOCNUatWoqh6xC3MxOZK11TKKP/LoO6uCI7+egxot8DrQQ9CNCbEUGP6IkQ5Rgi8dtQ6LobAmYUNVIFgXoQ+EhUC6JyaF6ZAvLfsE+WGjvzq3m8ttvFydya/19BA7Dt8xeUaJUxmt4Rg0qa3Hm5K6mBGRimAhNFg9Dd75JIxiG0kThGrWSwVI0kts24N2EqMlbCZTx8GTODgcGyc1zVR0W+PmjwmdVySlIiMf0bZdTIXGIiD5g/66yJgBDYwPXdZz/a2u2ufEtz/8i92Oze4T089Gb05ODwzcO31laGBpqr39zh20H8YhnJ5a/vX4Xt/F3r/1naq8utT7GwS79ZX2i719t1+tX1p+/qfFi5h1PbWy2v50efwG4uD9/b3VyfHKizuYKx8bHd66Ln/lw4cdg7PBpz2drWudkXDIF0MeyKssS2UDFIvQjFkNlxEJkICRDcPB5LykhildTXD8AbExXxgOgLjaNignlWX9VC6GRB9eiApy5SWRgTIopGNZpSiC8sqoCxwB3cbgerCuUlHD25lASUZHw3gwEcOMog78mFGiABw5uU7P4EO8QO/IIDGKsTFIOaK+trbtiU1XhdCQUvh4KhFHCkLfzoUI4Ao8JJE5Ej1mKomgFt8WGxSWSenJJOMV9aYkq3FcEpR30sMxGsPrxEcrytzjjTxBeZRkmc0ZWWmAQyYMKUXntJiWLx85QEZIhgge9f7Q0w10P+pPtmIwo7W7pRV5qHP6xMmB0emBltPTXYOzMWx86A+PDnXQvq3Ja0vQECw931zrfa+vDx29V9+6sby+fupi76l//PLVaFotPH+OObDFU8/Xl947+7d2bC8Zh5ON33jyj9tnH/78ybXbp/+GC360DOx+6Bdd04XJh51dBV/3yAs5QjgUI6oE2EtYQXIECRwnKpf4RtUA7hYChqqSF1RIW+M4iZFSnI0iKB2IhqgiW+cj/jDbE6B6ogFDQUTnkqyssDCOaqcNdjfqMwSTpWNFKkVbdwVqVz1ay89h0WsJdRfSl1bCtU+jB1oorASQyzbWfMZmFAv4/LnQy8Z6R2/bKWyOTUzMEh44u0Ek/OGDmK6gAGqoNnn4BqVJTPKA3vAIeAUoH2LqSUVJuAHuGNHNizwETsfFfIwpKhpPMZZIHbwtowhIVYzK9796dzfPHBONLVJsIvuADksyACZNtyHqhqCnWejxd54PnusJNqPd23Wy/2TL5CSgwsDgcNfk6OjgvV9MtPZHZ4wBdVqNjYyuLG3fGz/7uz9jDmLp1P3lV32jhVtLz9df9U39to8rtITaLi6+6v3H/ee3z+6NY84f8sPhaNtZzJIu7k3ee9h+6+He1tCVlqHp6dHLo/0T05MnRmabq/JE+GerHp2KyihSdrMpQmxB04pkTuFJgjUpI19lKQQoQYJPOLpDU5pe0xNzMafIpgQqHbOjgAnZomq9JIRmEi/vqoRQYlg1LIoY+Ek48asdd+NEmc8QasOf6QnTdCQgBcILjWl5thot53RfjuN5VRLomaC8EwG0r4/W7DqrrfTexMTIGC7S9Jrwc6UMBAmideYLj8oZILa4m4HsOCnGPa5tlCbH5X0aYCQAN8C4AMscw0AuVacjYcFidpOYBeFD4g4ByPYkE87av/71rw8cermSom2/qWUUGp86KRQCiyWD5BBiPlmQDTDv5/z1UPBhFG8Qq0n6IXTq6GrsuDcwcuVWc//s+U9+aqKw9pEz8kHh3tYf79059XAc6WhvfG/xxjUIfKCBX8cU49m//utfP3nY14vlWb2Lp9qn9l6BgTh7cfvrP8AU7L0bt++81740M3n6+tDo8PDWlVujLdObwwXfRKEnHcytSeHdzz2mAY61MGtxvF0G30Xx8dWrlhhLgSTL/HG4XkppqsGliilLl0uqVtUciprT2Bj6kbFcNcxmA4QlWvnMmYOUFGUJlw/lPSZbZHOZmAGgvUtSjpWNbXM0C/4rZNKBsEbokj8kSAFrQWVTqxsB4TyNJmeU3p8oz1h/2U5vt/k3JnJNYn5hto3Ea06TaB/ZYry2/dUUcJztKcc9gHEet/R5h+MacPvvQxyppzXueF5Meue9nImnjhLHDEVsM6M4plPb5uIHjpI3UhDcszZLQ+/DWjamxIu2lKdpnww9yUakMVQXIFTS6+sf6Zjt6Jwd7mgePNk/PNJ9YjDnP9FS6O44ebK1p/F1obm1fu4x1mhNbmH5/qn7AAvjS9sri6d6P3nmxnOko+ev1l+t/2P/F69eoTWB5aCnzi72nS3Mrdyeuv/H93671H5j78619oHhPw+PXh4IBruGB6a72vq7Bjs3O+vbxtI9m5JUm3MshCWOKh3hG5dMIkhVH9lO8QtFtWKoWUdISSzP6HSYYTNoryiwacKaIwDc1IZsk1iaFkoBg1XLrEKpmqasop66+zqssvKcnzX8ElsG9E4TIw039BLRr6rRzEZOSy9gbZjEs5Z+cBAOgxsHKSEIY4BPscaYfyR4MyZi+1i2XAWToLvKK1tkSmiiig5czkVxyC7zDaBJ8cg1j5uWXCMhR7392QN9/t1D12aeg6/XTBN/J8MdepgEw2eKiUraqVlRm2VJklFY1tYTWRsOgz6K7qjQcQq+o8a5BUuaJ/7ceT9mmpqbsffhZHOhf7Rw5+6JgSc7Lf3T3TudrSPN59v60RcpjP70RgFE5dLinZtn/7bU92r87KnFwtJzbGxdet77CvuyMMt/aqrvdi827N7MLYz2Ld0eb18ppG/+tL19lq7L3bt52Vh4+LKrq17UjOaRkclWZJ1QKGQQk1vTNcx1shxFSYbpr2gZkrXyRMqnFOdByV24whDyskaRg7UUHVIZFk5W0mr5ig54Dek6mDvKDW3ECocMQ6fKCUdPYajKQ1S1SofnDHTPCJ0tEVYC1984XReQUlelcpaW9bCyWzR6MIYQHsnOCNmCHJWj6bqxtpmbsc1ZsepznNTCLNHdCS6gbIYzRTauNzXBS1AHOTX++J3nNCG8ufZxXWgePxPnUztljxvsTIQ5x/YkmzQCTZYCOM3u215M5LHoJCpamIq+yXmLBqLerqMpimJrMWV+Hoxwuj4U3f/A13cUMzRc//BnqxHoawe70eVtnoa6vquu/yTULJiKCLZCxnB+p7FzZKRj1P7j33wDZ9f/eersuHtbBAqYHZi6Bo/BrtpTt9sxZtTb+9sReWjyS4/br93D+okn2++bfLi1NRNcvo9lOi/f/7S1e8XUuuVQ80jb+fCzM341HSb77omDdQhDU3Ra+dw+o9gclTpK5CUpzBI1T2om4Z0qSWQdycimWF1PvFTyLHghimS9pgrT8PiieM0SCGSdNluhGI3afkoZKENUNFkOH//ROqoAysX3e76toiOrApDrtBQQlJJOlAWpbIFbrpalRMIf85ViPWM91cYx8fHjwshm5YAoNnAc7hzLlUAlpCqkwZUksFc3vAkEOvDa8QYXGbzzGe//DpKBuVAHAe3pSVGP8yDxqh6xxh6KhHI4YoQV1qgSbFYAt4r6CXwhGEZFJJHAMRWR69D/Wv36v+8SDBqfr76obx1bhfp5ZmN2tq3joPnuYGs3UhQGO9ED9vtD/u5NgO79rz5eQQuifSs20bc0hUTTl14Rzp56BUFqHxbSAUpPvVqKzNz+bfuNaz+9c23p5cr19stYdvTTn378X1cvt0w+OXGyY3Aw/+Xv/wAYxecL2WHVTEfpo4QRUvO0rjCgMrUawXlWiiD1iaPHpE0qDJBKsqZCFC5fVG20dLwWKxBDyWhUlmXon/+CqpqE5VOZjRrFvLxa5mo0sd4CVCQqJPsUnELeoErhANEoIqS/+qS2W4W7aWgd8YxqqDGyqsjhsiVrQlRIHD4Ym5AFJ5BL6z2b+cr3P/uxMzPIQTgycUtUwCVQKHxQePIuVep+ecUG94gz8/xxhged6voQrhwA/4FrvVNiKaaZbYg7b5Um05YYE6SccqRUaiynUogEIpexsuRNikoRM6lp+e2ccWz6YgKNRlGgst8WK0Uax6A5P4cGb1frZnBnAnVrc2drePD0zdP3Cud6/GPnH+4Gn8xOYqFj4fL127d/Onr15Y2pKSyCOLX/S1xGCqGtz8Vtt9unzj4ZmPz9+367cn147Nq12aGh29d2hrDPaWgIG3RG6k+0znR3NftTTx/V7Zyj5ZmAsrZKyZZM9BDqIMcWKUBOglhAEm7AMrKGqhAqTypzxGxa5TQjmahgsC2a/QvqJi6VZzkiriUgo2F1KyBxBi9whBAOAZBkaxyFFKyEWcYgBoUneS7E0QGNBdqgejRN0Fk2daaE4ksKp3skSw4jfsdknx1L19NvD33579xdC/rsjbcjIxPE1c7HQWLjnyg2oSsEw+AA64M7bg2wiuPdqc03wFgwS0aPu2Zys0/czHgY3GpMqqkh0eDlOI4BCIoTImaITRA1PO/eL0noPK8/KxvE+c6Xq15DDrh9L/SKdmuhulh9hMYHWtDq1JAPF7jp3C7gItSbvwg2d2FFx0wlG4xs7Pb01xVeD+91DQzcwlrWlSFgBgxgT92bvN2+d6r39tRgGywHTPDTL35xb3R/ZeVG+0+fDQ1BwtU+df3K5HTH6xOF/eaBJ83nzr13syco+3J1whpNP16TaFXW9u/Cv4smw3tMiJXck6mQqqXjs181Aau1hLMBXM2bh1k9QVGmwxrIViQK8TMU6aYnxRFT0zlOlMpR9AW0t6uIiyx+JcexmiWF+RR/V4dHsiwYPie8RijjWRVZQDFUf4ZA9hMOqDonwlRQ3lOJqBxK5fLYVVWo4rLcPa3vPAgdO9gImoSECC6Oz8S972zjdXk5b7HJm49bj+yGJrf74MWRxOG4+t+E1uRSp26e8rJrqxxQNuIZpegbIgHBYGrzeSCXhJePcwqF2gL46LN/8agoosPuEE0k4s5yytt00M/IdNh63dP2tnFWjhUWzv3t2fkTJ/o7XwRzYwvDGPwIDjd3TwyoXKjl9NZS+/Vr7VtYUT2yNnIbE3pbG1N//vOrzeHLt6Z+uzXZ1tEyMXB5FJnn4fTWyK0rP701OHSipWW6Hzq81q7p5uaZwZ1oIBJQqUAWTRqWdx6zTpgHVSXOvSboLPOiVaMlywTwokuo7gSJZBwTo7wWZZesQDpqg1Rjkfnd1JNJMKkFhjcZnYGUnavVRDFPRLOcAAcdoLI6z0DdyTF86g0rGJmEDbZCVeGxGkIKH6ahGTCADQ3DWi3qciaSuPrHat72p9I9bcXG1MxEW9vwAolDV4oySJwTeT5P8RBpw3FRDCVBK7zD2EoD78UBRPcgi+aqDv9BOcvwDXF4EfFA4Hgspo4haHA13kRhCBSokDgwidgqQRjYyTd4gUQIqwM3aBBwWqFAmEVHkWOESB1I9lg0GHrwzWelMDQTc5GJ9EwuB3zlwwwoZthiJf/uxJi/lJ1p7g/4x6j+K7PBJye3toe2hi7vnbmHjbV1A+PXsFsG+8N/i4t53Ly7PftidvBE/+Gd00Oz+5hyHGg5OdI9AB3gwMzL5nObXdOtQX/FBwuh14ZEb/CIS0ye7O/iv2LbTCqvJRgqzx7Fayz27ZYMLskRO+WkLMImooZMq6zN1TjxcM7hDLcVrXKrHKQ0XuAM1cpUJFaTKOJwpXQmayUokSde6HHjHCXBMy1CULhaxHDACUkahZ3lCXVBrtMyL/4YpTXbXj2KWXkrXYyNFPOPZ2dXg07O9SDdgRzb3WXxv8PzLvEcVpT5/3cePG4tBCSnoZPqhrdj01TiJnyKYSqWdpw5amrSTBOcgiCDbFAYhtXxlhQEOG3/52tA+w7H5nU6gHBroHBTZWg0r1pIRRFfKRqNRh59e06OhXrON/aEtagUgtgG6vuZXCSIbSkRXLBobSOAgdgz39iODPzjweiVSb+8iRA29NM7Q5VnY+2ABEO/vTL62+s3JyenuyaGT3ad6Jgcvn568q8jJ7f6T57rPtcMlHiy89z0iUJnT+f5TiB9mEdTwoZEWOQIOMCbPMc4qAzyFDh6n+oQHFxpB1H78IHmqpZq2XAeETCuqQoD6CMm6ASwhLgLAAHwVrr6yzMqekAW4Q1KZxg85QE8h/ck8uW3h3loPqDMBWGUVcHe5S1h9e6jat2bB430Ebj/DP7DUeMRFo06fke3HcvJz6XtVHo435YmCjBCBfaJgxeFGRCP3uUh5CCL98a9Nu+GOnxj4shGeZf8AcGWPHYR3JxL92z8Mg9o3oRomIEvEzqalvV5+NM8ISzHsDyfBMVgzwHVAXpzJuICRWsqy0loQfj9czKdrfr8NHRq6ZKcw37ASPooFNMEdCoIZMyzwShq7MZg+J9HEX/56oe7OjowJTi2/8Od0xgSRyi7s40/cKW/e5HNldOXbw4U2mbqZ0baToDX6xhoGZ1pvTmy2par7+/ClGwnaKVOLLarYyHbk1ScyWQGUJozOD7rIGUysJRpWpiHVjzuKc7zXJ4z3BqiiWIoUUng1GtMMp/nX8eNkgv1RGg1sxyzwdRMlOmcyBqeJpJMcLCQXmYIp8GDMm9e6JBKEYoxOZ7jSZCIlEPQoCixWhYQWpPKJRsI29ESqQy4c2vjjfVgG5NhqyMzb3/wjCiKHncPDDUAqPFxm4d9XETtYgLiuk68ySPaSWXeSXlhI6SgPz47LM3zlpKB0sctj7IlZCu8B1gJINALxpXbDR9TCYMzOaiEGngWpEK4XC47b0E6EFDt7IuoKoTSoYWcj842Cn67ko/W6tSdvMBQ5VQOBOajXz1Ih+RoRJDqir6xtcjTLGS4/s7Bky0dEGv9ZXL09PVZXEBp9Pbo0FT75ZXrXcNXt/aGJgea24IzE7lg/+pNRLburumdc4Orrf/48YnCdDPQodB5/u4Ea9ICx4Si6XAyViZFa7tIMY6Y4TggZgphhyNQ/iVipA7xgaQYLkVxzNsUpm8g2+QMmXIcV8epqVwdy9cslrNMUq4TGHPBVObSZslkk6ZoiTUL1B1+KXEngM2sxqZ1ymREVKsB8pesXqTrM9UoS5O7Vx1B1zO6TVd30i8XnFQqn86WS9Xso43gZmF1lcTfHTCMrWcUvgm9IXRWddYzbyPJwEg47EzGFFN4gAOmwwReMkXcUjXBzyveCl5vok3hvpanYCDUTM7jX8WJ4jhcgs+/BlplAEI4NhO3XT5eqcsLG2/VGETLZQzQROmYlEiBYSym8gF9384m8keqs7MDYVIGE5bgFyNjbmsiXfAVRgY6uqOdjadHb7Vcnt4ZGW1vwYUS2nH51umuF+1DW9GA3tY4lksHYzOF1iBR25pbheaTbW2tM/1tjZWxTmk3jWsizqvqxkAsGpWt1PdfgIDhTIIIxBDTjUxIvSzJEMeKkuIDksCnflVHyEaWaRLBEjssl9QE2oP8j+aXwiV5otseYAaFM5k5MaNxlKd6NIdfBYcDNnMlU5SoFUWkaxECap6ncAooEphPElrQbPQ28rJks48ep/Sczy/n9GKtFqsW5dRadWGisFpYgAAe9sm71nF/GZo5yHzx/QQCm5IES8pgNL8BeR5B7r9Hik/+t6OKJ7JVEAueJq+9yyNTVePxsZgheuaPkxnO1h10Wi280WKKz1gHVFkjts1iFoxzZQuSIOejwgI6eXo2Rft0qo5WOSBQUHQVfY6Nofcva1xjjEnRjbENDEzQAgbAgp2D13yzPmN69go2frWtBYZGfzp0erhltfBy68+jpye04Wh0pm02mI6hh8Zmb5bozrvLzYMjned6Yjk2HJx4Onz+fIS9+qkPfv5TtJ4ySI2l0E7eeEojkSdEnFZwcfjuUagMQ4GEwgfV5MOsvo9mDONlpTmeESQavmZtN5n6RJjdFjn7CIW4XoURURgiK3lWf+HwDKxAWQwFdtuCi3HMQVn3GtoLjrAooTQqw5cpLqFJFV9CoK28HXXiSsJx8vpctZRxsrntheLc5sjI2kKFYGg4rgBEyhkFLbqM+A4rvHgab4LFYJUSsFkq3pAEYHjnT7gbHi+wY8N/w+BaGSVtLS6CW0UD9nDTpwBFvBOo8uA9dNaLik0TCUE54Srwkja8TKMlOpY/sOWqL3BVq8ayQkJDh2UhOk/RAmRMCvoXWCm886sPntlYsLSiRKcbGyd2caXWE/2F2b02szA8XSg0fuKPwM+jmOgfWCoMDE+Pjk6//fRT/0QsulHyV4py7urPvnIYnJ1xF4TVz9T5X79uW+j0NWIOXV17Wy3Skh2tIfTyrMhpROWoIk88aB+jUFeaNAsni5dgL55H9YB+G5C05/Wnvkyj0nPdSQtkK0hZkBokFih9l4ZpK7aZdLE2nMXCd87D0UaaIRlK88xtguIUM2++AvhHdEDal4b48ukLWpcTgu3QElqeofLdkpP12y82rN2jrJWuFJEShqsHlf8AmD0SuE5gg44AAAAASUVORK5CYII=\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal rainfall card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"mm\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "rain", + "precipitation", + "downpour", + "rain shower", + "drizzle", + "raindrop", + "cloudburst", + "rainwater" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_rotational_speed_card.json b/application/src/main/data/json/system/widget_types/horizontal_rotational_speed_card.json new file mode 100644 index 0000000000..e1bc6fab1f --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_rotational_speed_card.json @@ -0,0 +1,30 @@ +{ + "fqn": "horizontal_rotational_speed_card", + "name": "Horizontal rotational speed card", + "deprecated": false, + "image": "tb-image:aG9yaXpvbnRhbF9yb3RhdGlvbmFsX3NwZWVkX2NhcmQuc3Zn:aG9yaXpvbnRhbF9yb3RhdGlvbmFsX3NwZWVkX2NhcmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjU2IiB5PSI1MiIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjU1IiB4PSIuNSIgeT0iNTIuNSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjEiIHJ4PSIzLjUiLz48ZyBmaWx0ZXI9InVybCgjYSkiPjxwYXRoIGZpbGw9IiMyQjU0Q0UiIGQ9Ik0yMiA3NWMtNS41IDAtMTAgMi4yLTEwIDUgMCAyLjIgMyA0LjEgNyA0LjhWODhsNC00LTQtNHYyLjdjLTMuMi0uNS01LTEuOS01LTIuNyAwLTEgMy0zIDgtM3M4IDIgOCAzYzAgLjctMS41IDEuOS00IDIuNXYyYzMuNS0uNyA2LTIuNSA2LTQuNSAwLTIuOC00LjUtNS0xMC01WiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik0zOSA2NWgzLjVhNSA1IDAgMCAxIDIgLjRjLjUuMiAxIC42IDEuMiAxIC4zLjQuNCAxIC40IDEuNiAwIC41IDAgMS0uMyAxLjRsLS43IDEtMS4yLjUtLjUuM2gtMy4ydi0xLjRoMi40Yy40IDAgLjcgMCAxLS4ybC42LS42LjItLjljMC0uMyAwLS42LS4yLS45bC0uNi0uNi0xLS4yaC0xLjlWNzVIMzlWNjVabTUuNyAxMC0yLjMtNC41aDEuOGwyLjMgNC40di4xaC0xLjhabTIuOS0zLjZ2LS4yYzAtLjUgMC0xIC4yLTEuNWwuNy0xLjJhMyAzIDAgMCAxIDEtLjdsMS41LS4zYy42IDAgMSAwIDEuNS4zLjQuMS44LjQgMSAuNy40LjQuNi44LjcgMS4ybC4zIDEuNXYuMmwtLjMgMS41LS42IDEuMmEzLjEgMy4xIDAgMCAxLTIuNiAxYy0uNSAwLTEgMC0xLjQtLjJhMy41IDMuNSAwIDAgMS0xLjgtMmwtLjItMS41Wm0xLjYtLjJ2LjJjMCAuMyAwIC42LjIgMSAwIC4yLjEuNS4zLjdsLjUuNWMuMy4yLjUuMi44LjJhMS42IDEuNiAwIDAgMCAxLjQtLjdjLjEtLjIuMy0uNS4zLS44bC4xLTF2LTFsLS40LS44YTEuNiAxLjYgMCAwIDAtMS40LS43Yy0uMyAwLS41IDAtLjguMmwtLjUuNWMtLjIuMi0uMy41LS4zLjhsLS4yIDFabTEwLjMtMy42djEuMmgtNC4ydi0xLjJoNC4yWm0tMy0xLjhoMS43djcuN2wuNC4yaC40YTIuNSAyLjUgMCAwIDAgLjYgMFY3NWE0LjIgNC4yIDAgMCAxLTEuMS4xYy0uNCAwLS43IDAtMS0uMi0uMy0uMS0uNi0uMy0uNy0uNi0uMi0uMy0uMy0uNy0uMy0xLjJ2LTcuM1ptOC43IDcuN1Y3MGMwLS4zIDAtLjUtLjItLjdhMSAxIDAgMCAwLS40LS40bC0uNy0uMi0uNy4xLS41LjQtLjEuNWgtMS43bC4yLS44LjctLjdhMyAzIDAgMCAxIDEtLjUgNCA0IDAgMCAxIDEuMi0uMmMuNSAwIDEgMCAxLjUuMi40LjIuNy41IDEgLjkuMi40LjMuOC4zIDEuNHYzLjNsLjEuOS4yLjd2LjFoLTEuN2EzIDMgMCAwIDEtLjEtLjdsLS4xLS44Wm0uMi0zdjFoLTEuMWwtLjguMWMtLjMgMC0uNS4xLS42LjNhMSAxIDAgMCAwLS41LjlsLjEuNS40LjQuNy4xYTEuOCAxLjggMCAwIDAgMS41LS43bC4yLS42LjUuOC0uMi42YTMgMyAwIDAgMS0uNi42IDIuNiAyLjYgMCAwIDEtMS43LjZjLS41IDAtMSAwLTEuMy0uMy0uNC0uMS0uNy0uNC0xLS43YTIuNCAyLjQgMCAwIDEtLjEtMi4ybC43LS44IDEuMS0uNCAxLjQtLjJoMS4zWm02LjgtMi45djEuMkg2OHYtMS4yaDQuMlptLTMtMS44SDcxdjcuN2wuNC4yaC40YTIuNSAyLjUgMCAwIDAgLjYgMFY3NWE0LjIgNC4yIDAgMCAxLTEuMS4xYy0uNCAwLS43IDAtMS0uMi0uMy0uMS0uNS0uMy0uNy0uNi0uMi0uMy0uMy0uNy0uMy0xLjJ2LTcuM1ptNi4zIDEuOFY3NWgtMS43di03LjRoMS43Wm0tMS44LTIgLjMtLjYuNy0uMmMuMyAwIC41IDAgLjcuMmwuMi43YzAgLjIgMCAuNC0uMi42YTEgMSAwIDAgMS0uNy4yIDEgMSAwIDAgMS0uNy0uMi44LjggMCAwIDEtLjMtLjZabTMuNSA1Ljh2LS4ybC4zLTEuNS42LTEuMmEzIDMgMCAwIDEgMS4xLS43Yy40LS4yIDEtLjMgMS41LS4zczEgMCAxLjQuM2MuNC4xLjguNCAxLjEuN2wuNyAxLjIuMiAxLjV2LjJjMCAuNSAwIDEtLjIgMS41bC0uNyAxLjJhMy4xIDMuMSAwIDAgMS0yLjUgMWMtLjYgMC0xIDAtMS41LS4yYTMuNSAzLjUgMCAwIDEtMS44LTJsLS4yLTEuNVptMS43LS4ydjEuMmwuNC43Yy4yLjIuMy40LjYuNS4yLjIuNS4yLjguMmExLjYgMS42IDAgMCAwIDEuMy0uN2wuNC0uOHYtMmwtLjQtLjhhMS42IDEuNiAwIDAgMC0xLjMtLjdjLS4zIDAtLjYgMC0uOC4yLS4zLjEtLjQuMy0uNi41bC0uMy44LS4xIDFabTguNS0yVjc1aC0xLjd2LTcuNGgxLjZ2MS42Wk04NyA3MWgtLjZjMC0uNS4xLTEgLjMtMS40LjEtLjUuMy0uOC42LTEuMWEyLjcgMi43IDAgMCAxIDIuMS0xbDEgLjEuNy41LjUuOC4yIDEuM1Y3NWgtMS43di00LjhjMC0uMyAwLS42LS4yLS44YS45LjkgMCAwIDAtLjQtLjVsLS43LS4xYTEuNiAxLjYgMCAwIDAtMS4zLjdsLS40LjctLjEuOFpNOTggNzMuNVY3MGMwLS4zIDAtLjUtLjItLjdhMSAxIDAgMCAwLS40LS40bC0uOC0uMi0uNi4xLS41LjQtLjIuNWgtMS42YzAtLjMgMC0uNS4yLS44LjEtLjMuMy0uNS42LS43YTMgMyAwIDAgMSAxLS41IDQgNCAwIDAgMSAxLjItLjJjLjYgMCAxIDAgMS41LjIuNC4yLjguNSAxIC45LjMuNC40LjguNCAxLjR2NC4ybC4zLjd2LjFoLTEuN2EzIDMgMCAwIDEtLjItLjd2LS44Wm0uMi0zdjFIOTdsLS44LjEtLjYuM2ExIDEgMCAwIDAtLjQuOWwuMS41LjQuNC42LjFhMS44IDEuOCAwIDAgMCAxLjUtLjdsLjMtLjYuNS44LS4zLjZhMyAzIDAgMCAxLS41LjYgMi42IDIuNiAwIDAgMS0xLjguNmMtLjUgMC0uOSAwLTEuMy0uMy0uMy0uMS0uNi0uNC0uOS0uN2EyLjQgMi40IDAgMCAxLS4xLTIuMmwuNy0uOCAxLS40IDEuNS0uMmgxLjNabTUuMS02Vjc1aC0xLjZWNjQuNWgxLjZaTTQzIDkzbC0uMS0uNGExIDEgMCAwIDAtLjUtLjRsLTEtLjMtMS0uMy0xLS41LS41LS42LS4yLS44LjItLjkuNi0uNyAxLS41IDEuMS0uMWMuNiAwIDEuMSAwIDEuNi4zLjQuMi44LjUgMSAuOC4zLjQuNC44LjQgMS4yaC0xLjdsLS4xLS41YTEgMSAwIDAgMC0uNS0uNWwtLjctLjEtLjcuMWExIDEgMCAwIDAtLjQuNC45LjkgMCAwIDAgMCAuOGwuMi4yLjQuMi44LjIgMS40LjVjLjQuMi44LjQgMSAuNy4yLjMuMy43LjMgMS4xYTIgMiAwIDAgMS0uOCAxLjYgMyAzIDAgMCAxLTEgLjVsLTEuMi4xYy0uNyAwLTEuMiAwLTEuNy0uM3MtLjgtLjYtMS0xYy0uMy0uMy0uNC0uNy0uNC0xLjFoMS42YzAgLjMgMCAuNS4yLjdsLjYuNC43LjEuOC0uMWMuMiAwIC4zLS4yLjQtLjNsLjItLjVabTUtNHY4LjhoLTEuN1Y4Ny42aDEuNUw0OCA4OVptNC44IDIuMnYuMmMwIC41IDAgMS0uMiAxLjVMNTIgOTRjLS4yLjMtLjUuNi0uOS44TDUwIDk1Yy0uNSAwLS45IDAtMS4yLS4ybC0uOS0uN2E0IDQgMCAwIDEtLjUtMS4yYy0uMi0uNC0uMy0uOS0uMy0xLjR2LS41YzAtLjYuMS0xIC4zLTEuNWwuNS0xLjFhMi41IDIuNSAwIDAgMSAyLTFjLjYgMCAxIDAgMS4zLjJsMSAuOC41IDEuMi4yIDEuNVptLTEuNy4ydi0xLjJsLS40LS43YTEuNSAxLjUgMCAwIDAtMS4zLS43bC0uNy4xLS41LjRhMiAyIDAgMCAwLS40LjdsLS4xLjdWOTJjMCAuNC4xLjcuMyAxIDAgLjIuMy40LjUuNmwxIC4yYy4zIDAgLjUgMCAuNy0uMi4yIDAgLjQtLjMuNS0uNS4yLS4yLjMtLjUuMy0uOGwuMS0xWm02LjYgMy43Yy0uNiAwLTEgMC0xLjUtLjJhMy4zIDMuMyAwIDAgMS0xLjgtMiA0IDQgMCAwIDEtLjMtMS4zdi0uM2MwLS42LjEtMS4xLjMtMS42LjItLjUuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS0uOGwxLjQtLjJjLjUgMCAxIDAgMS40LjJsMSAuOC42IDEuMS4xIDEuNXYuN0g1NXYtMS4ySDU5di0uMWMwLS4zIDAtLjYtLjItLjkgMC0uMi0uMi0uNC0uNC0uNmwtMS0uMmMtLjIgMC0uNCAwLS42LjItLjMgMC0uNC4yLS42LjVsLS4zLjctLjEgMXYuNGwuMS45LjQuNy42LjUuOS4xYTIuMyAyLjMgMCAwIDAgMS45LS45bC44LjktLjYuNi0xIC41Yy0uMy4yLS43LjItMS4yLjJabTcuNyAwYy0uNSAwLTEgMC0xLjQtLjJhMy4zIDMuMyAwIDAgMS0xLjktMiA0IDQgMCAwIDEtLjItMS4zdi0uM2MwLS42IDAtMS4xLjItMS42cy40LS45LjctMS4yYTMgMyAwIDAgMSAxLjEtLjhsMS4zLS4yYy42IDAgMSAwIDEuNC4yYTMuMSAzLjEgMCAwIDEgMS42IDEuOWwuMiAxLjV2LjdoLTUuN3YtMS4yaDR2LS4xbC0uMS0uOS0uNS0uNi0uOS0uMmMtLjIgMC0uNSAwLS43LjItLjIgMC0uNC4yLS41LjUtLjIuMi0uMy40LS4zLjdsLS4yIDF2LjRjMCAuMyAwIC42LjIuOWwuNC43LjYuNS44LjFhMi4zIDIuMyAwIDAgMCAyLS45bC44LjktLjYuNi0xIC41Yy0uMy4yLS44LjItMS4zLjJabTktMS42di05aDEuN1Y5NWgtMS41bC0uMS0xLjVabS00LjgtMi4xdi0uMmwuMi0xLjUuNi0xLjJhMi41IDIuNSAwIDAgMSAyLjEtMWMuNSAwIC45IDAgMS4yLjIuNC4yLjYuNC45LjhsLjUgMS4xLjMgMS41di41YzAgLjUtLjEgMS0uMyAxLjQgMCAuNC0uMy44LS41IDEuMWEyLjUgMi41IDAgMCAxLTIgMSAyLjYgMi42IDAgMCAxLTIuMi0xbC0uNi0xLjItLjItMS41Wm0xLjctLjJ2MS4ybC40LjcuNS41LjcuMmMuNCAwIC43IDAgMS0uMmwuNS0uN2MuMi0uMy4zLS42LjMtMXYtMS4ybC0uMS0uN2EyIDIgMCAwIDAtLjQtLjZsLS41LS41LS43LS4xYy0uMyAwLS42IDAtLjguMmwtLjUuNWMtLjIuMi0uMy41LS4zLjhsLS4xIDFaIi8+PHBhdGggZmlsbD0iIzJCNTRDRSIgZD0iTTExNC41IDc4LjZoMS4yYy41IDAgMSAwIDEuMi0uMi40LS4yLjYtLjQuOC0uN2wuMi0xYzAtLjQgMC0uOC0uMi0xLS4yLS40LS40LS42LS43LS44bC0xLS4yYy0uNCAwLS44IDAtMSAuMmExLjcgMS43IDAgMCAwLTEgMS42aC0yLjJjMC0uNi4yLTEuMi42LTEuOC4zLS41LjgtMSAxLjQtMS4yYTUuMyA1LjMgMCAwIDEgNC4zIDBjLjYuMiAxIC42IDEuNCAxLjIuNC41LjUgMS4yLjUgMiAwIC4zIDAgLjctLjIgMS0uMi40LS40LjgtLjggMWE0IDQgMCAwIDEtMS4yLjhjLS41LjItMS4xLjMtMS44LjNoLTEuNXYtMS4yWm0wIDEuN3YtMS4yaDEuNWMuOCAwIDEuNC4xIDIgLjMuNS4yIDEgLjQgMS4zLjdsLjcgMSAuMiAxLjNjMCAuNiAwIDEuMi0uMyAxLjYtLjIuNS0uNS45LS45IDEuMmE0IDQgMCAwIDEtMS40LjcgNS43IDUuNyAwIDAgMS0zLjMgMGMtLjUtLjEtMS0uMy0xLjMtLjYtLjQtLjMtLjctLjctMS0xLjItLjItLjQtLjMtMS0uMy0xLjVoMi4xYzAgLjMgMCAuNy4zIDEgLjEuMy40LjUuNy42LjQuMi43LjMgMS4xLjMuNSAwIC45IDAgMS4yLS4yLjMtLjIuNi0uNC43LS44LjItLjMuMy0uNi4zLTEgMC0uNiAwLTEtLjMtMS4zbC0uOC0uNy0xLjMtLjJoLTEuMlptMTYuMS0xLjh2Mi4xYzAgMSAwIDEuOS0uMyAyLjYtLjIuNy0uNCAxLjMtLjggMS43LS40LjQtLjguOC0xLjMgMWE1IDUgMCAwIDEtMy4xLjEgMy40IDMuNCAwIDAgMS0yLjEtMS43IDYgNiAwIDAgMS0uNi0xLjZsLS4yLTJ2LTIuMmMwLTEgLjEtMS44LjMtMi41LjItLjcuNS0xLjMuOS0xLjcuNC0uNS44LS44IDEuMy0xYTQuOSA0LjkgMCAwIDEgMy4xLS4xIDMuMyAzLjMgMCAwIDEgMiAxLjdjLjMuNC41IDEgLjYgMS41LjIuNi4yIDEuMy4yIDIuMVptLTIgMi40di0yLjdsLS4yLTEuM2E0IDQgMCAwIDAtLjItMSAyIDIgMCAwIDAtLjUtLjdsLS41LS40YTIgMiAwIDAgMC0uOCAwIDIgMiAwIDAgMC0uOC4xYy0uMy4xLS41LjMtLjcuNmwtLjQgMS0uMSAxLjd2NC4xbC4zIDEgLjQuNy42LjRoLjhsLjgtLjFjLjMtLjEuNS0uMy43LS42bC40LTEuMS4xLTEuN1ptMTIuNC0yLjR2Mi4xYzAgMS0uMSAxLjktLjMgMi42LS4yLjctLjUgMS4zLS45IDEuNy0uMy40LS44LjgtMS4zIDFhNSA1IDAgMCAxLTMuMS4xIDMuNCAzLjQgMCAwIDEtMi0xLjcgNiA2IDAgMCAxLS42LTEuNmMtLjItLjYtLjItMS4zLS4yLTJ2LTIuMmMwLTEgMC0xLjguMy0yLjUuMi0uNy41LTEuMy44LTEuNy40LS41LjgtLjggMS4zLTEgLjYtLjIgMS4xLS4zIDEuOC0uMy41IDAgMSAwIDEuNC4yYTMuMyAzLjMgMCAwIDEgMiAxLjdjLjMuNC40IDEgLjYgMS41bC4yIDIuMVptLTIuMiAyLjR2LTRhNCA0IDAgMCAwLS4zLTEgMiAyIDAgMCAwLS40LS43bC0uNi0uNGEyIDIgMCAwIDAtLjcgMCAyIDIgMCAwIDAtMSAuMWwtLjYuNi0uNCAxLS4xIDEuN3Y0LjFsLjQgMWMwIC4zLjIuNS40LjcuMS4yLjMuMy42LjRoLjdsLjktLjEuNi0uNi40LTEuMS4yLTEuN1ptOS03LjdoNC42YzEgMCAxLjguMSAyLjUuNC43LjMgMS4yLjggMS42IDEuMy4zLjYuNSAxLjMuNSAyLjIgMCAuNi0uMSAxLjItLjMgMS42LS4zLjUtLjYgMS0xIDEuMy0uNS4zLTEgLjYtMS42LjdsLS42LjRoLTQuMXYtMS44aDNjLjYgMCAxIDAgMS40LS4zLjMtLjIuNi0uNC43LS43YTIuNCAyLjQgMCAwIDAgMC0yLjNjLS4xLS4zLS40LS42LS43LS44YTMgMyAwIDAgMC0xLjQtLjJoLTIuM3YxMWgtMi4yVjczLjJabTcuNCAxMi44LTMtNS43aDIuM2wzIDUuNnYuMWgtMi4zWm05LTQuOGgtMy40di0xLjdoMy40Yy42IDAgMS0uMSAxLjQtLjMuMy0uMi42LS40LjgtLjhsLjItMS4xYzAtLjQgMC0uOC0uMi0xLjJhMiAyIDAgMCAwLS44LS44Yy0uNC0uMi0uOC0uMy0xLjQtLjNoLTIuN3YxMWgtMi4yVjczLjJoNC45YzEgMCAxLjguMiAyLjUuNS43LjQgMS4yLjkgMS42IDEuNS4zLjYuNSAxLjMuNSAyYTQgNCAwIDAgMS0uNSAyLjJjLS40LjYtMSAxLTEuNiAxLjRhNiA2IDAgMCAxLTIuNS40Wm03LjYtOGgybDMuNyA5LjkgMy43LTkuOWgybC01IDEyLjhoLTEuNWwtNC45LTEyLjhabS0uOSAwaDEuOWwuMyA4LjVWODZIMTcxVjczLjJabTExLjMgMGgxLjhWODZoLTIuMnYtNC4zbC40LTguNVoiLz48L2c+PGRlZnM+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjIwMCIgaGVpZ2h0PSI1NiIgeD0iMCIgeT0iNTIiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjMiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTUxOF80OTgzIi8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xNTE4XzQ5ODMiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48L2RlZnM+PC9zdmc+", + "description": "Displays the latest rotational speed telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'rotationalSpeed', label: 'Rotational speed', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Rotational speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 4000 - 2000;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 4000) {\\n\\tvalue = 4000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"horizontal\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"360\",\"iconColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":500,\"color\":\"#305AD7\"},{\"from\":500,\"to\":1500,\"color\":\"#3FA71A\"},{\"from\":1500,\"to\":3000,\"color\":\"#FFA600\"},{\"from\":3000,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":500,\"color\":\"#305AD7\"},{\"from\":500,\"to\":1500,\"color\":\"#3FA71A\"},{\"from\":1500,\"to\":3000,\"color\":\"#FFA600\"},{\"from\":3000,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Horizontal rotational speed card\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"RPM\",\"decimals\":0,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "angular speed", + "spin rate", + "revolutions", + "rotational frequency", + "spin motion" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_rotational_speed_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_rotational_speed_card_with_background.json new file mode 100644 index 0000000000..57d105b4ae --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_rotational_speed_card_with_background.json @@ -0,0 +1,30 @@ +{ + "fqn": "horizontal_rotational_speed_card_with_background", + "name": "Horizontal rotational speed card with background", + "deprecated": false, + "image": "tb-image:aG9yaXpvbnRhbF9yb3RhdGlvbmFsX3NwZWVkX2NhcmRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==:aG9yaXpvbnRhbF9yb3RhdGlvbmFsX3NwZWVkX2NhcmRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSI1NiIgeT0iNTIiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjQ0IiB4PSI2IiB5PSI1OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48cGF0aCBmaWxsPSIjMkI1NENFIiBkPSJNMjIgNzVjLTUuNSAwLTEwIDIuMi0xMCA1IDAgMi4yIDMgNC4xIDcgNC44Vjg4bDQtNC00LTR2Mi43Yy0zLjItLjUtNS0xLjktNS0yLjcgMC0xIDMtMyA4LTNzOCAyIDggM2MwIC43LTEuNSAxLjktNCAyLjV2MmMzLjUtLjcgNi0yLjUgNi00LjUgMC0yLjgtNC41LTUtMTAtNVoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNMzkgNjdoMy41YTUgNSAwIDAgMSAyIC40Yy41LjIgMSAuNiAxLjIgMSAuMy40LjQgMSAuNCAxLjYgMCAuNSAwIDEtLjMgMS40bC0uNyAxLTEuMi41LS41LjNoLTMuMnYtMS40aDIuNGMuNCAwIC43IDAgMS0uMmwuNi0uNi4yLS45YzAtLjMgMC0uNi0uMi0uOWwtLjYtLjYtMS0uMmgtMS45Vjc3SDM5VjY3Wm01LjcgMTAtMi4zLTQuNWgxLjhsMi4zIDQuNHYuMWgtMS44Wm0yLjktMy42di0uMmMwLS41IDAtMSAuMi0xLjVsLjctMS4yYTMgMyAwIDAgMSAxLS43bDEuNS0uM2MuNiAwIDEgMCAxLjUuMy40LjEuOC40IDEgLjcuNC40LjYuOC43IDEuMmwuMyAxLjV2LjJsLS4zIDEuNS0uNiAxLjJhMy4xIDMuMSAwIDAgMS0yLjYgMWMtLjUgMC0xIDAtMS40LS4yYTMuNSAzLjUgMCAwIDEtMS44LTJsLS4yLTEuNVptMS42LS4ydi4yYzAgLjMgMCAuNi4yIDEgMCAuMi4xLjUuMy43bC41LjVjLjMuMi41LjIuOC4yYTEuNiAxLjYgMCAwIDAgMS40LS43Yy4xLS4yLjMtLjUuMy0uOGwuMS0xdi0xbC0uNC0uOGExLjYgMS42IDAgMCAwLTEuNC0uN2MtLjMgMC0uNSAwLS44LjJsLS41LjVjLS4yLjItLjMuNS0uMy44bC0uMiAxWm0xMC4zLTMuNnYxLjJoLTQuMnYtMS4yaDQuMlptLTMtMS44aDEuN3Y3LjdsLjQuMmguNGEyLjUgMi41IDAgMCAwIC42IDBWNzdhNC4yIDQuMiAwIDAgMS0xLjEuMWMtLjQgMC0uNyAwLTEtLjItLjMtLjEtLjYtLjMtLjctLjYtLjItLjMtLjMtLjctLjMtMS4ydi03LjNabTguNyA3LjdWNzJjMC0uMyAwLS41LS4yLS43YTEgMSAwIDAgMC0uNC0uNGwtLjctLjItLjcuMS0uNS40LS4xLjVoLTEuN2wuMi0uOC43LS43YTMgMyAwIDAgMSAxLS41IDQgNCAwIDAgMSAxLjItLjJjLjUgMCAxIDAgMS41LjIuNC4yLjcuNSAxIC45LjIuNC4zLjguMyAxLjR2My4zbC4xLjkuMi43di4xaC0xLjdhMyAzIDAgMCAxLS4xLS43bC0uMS0uOFptLjItM3YxaC0xLjFsLS44LjFjLS4zIDAtLjUuMS0uNi4zYTEgMSAwIDAgMC0uNS45bC4xLjUuNC40LjcuMWExLjggMS44IDAgMCAwIDEuNS0uN2wuMi0uNi41LjgtLjIuNmEzIDMgMCAwIDEtLjYuNiAyLjYgMi42IDAgMCAxLTEuNy42Yy0uNSAwLTEgMC0xLjMtLjMtLjQtLjEtLjctLjQtMS0uN2EyLjQgMi40IDAgMCAxLS4xLTIuMmwuNy0uOCAxLjEtLjQgMS40LS4yaDEuM1ptNi44LTIuOXYxLjJINjh2LTEuMmg0LjJabS0zLTEuOEg3MXY3LjdsLjQuMmguNGEyLjUgMi41IDAgMCAwIC42IDBWNzdhNC4yIDQuMiAwIDAgMS0xLjEuMWMtLjQgMC0uNyAwLTEtLjItLjMtLjEtLjUtLjMtLjctLjYtLjItLjMtLjMtLjctLjMtMS4ydi03LjNabTYuMyAxLjhWNzdoLTEuN3YtNy40aDEuN1ptLTEuOC0yIC4zLS42LjctLjJjLjMgMCAuNSAwIC43LjJsLjIuN2MwIC4yIDAgLjQtLjIuNmExIDEgMCAwIDEtLjcuMiAxIDEgMCAwIDEtLjctLjIuOC44IDAgMCAxLS4zLS42Wm0zLjUgNS44di0uMmwuMy0xLjUuNi0xLjJhMyAzIDAgMCAxIDEuMS0uN2MuNC0uMiAxLS4zIDEuNS0uM3MxIDAgMS40LjNjLjQuMS44LjQgMS4xLjdsLjcgMS4yLjIgMS41di4yYzAgLjUgMCAxLS4yIDEuNWwtLjcgMS4yYTMuMSAzLjEgMCAwIDEtMi41IDFjLS42IDAtMSAwLTEuNS0uMmEzLjUgMy41IDAgMCAxLTEuOC0ybC0uMi0xLjVabTEuNy0uMnYxLjJsLjQuN2MuMi4yLjMuNC42LjUuMi4yLjUuMi44LjJhMS42IDEuNiAwIDAgMCAxLjMtLjdsLjQtLjh2LTJsLS40LS44YTEuNiAxLjYgMCAwIDAtMS4zLS43Yy0uMyAwLS42IDAtLjguMi0uMy4xLS40LjMtLjYuNWwtLjMuOC0uMSAxWm04LjUtMlY3N2gtMS43di03LjRoMS42djEuNlpNODcgNzNoLS42YzAtLjUuMS0xIC4zLTEuNC4xLS41LjMtLjguNi0xLjFhMi43IDIuNyAwIDAgMSAyLjEtMWwxIC4xLjcuNS41LjguMiAxLjNWNzdoLTEuN3YtNC44YzAtLjMgMC0uNi0uMi0uOGEuOS45IDAgMCAwLS40LS41bC0uNy0uMWExLjYgMS42IDAgMCAwLTEuMy43bC0uNC43LS4xLjhaTTk4IDc1LjVWNzJjMC0uMyAwLS41LS4yLS43YTEgMSAwIDAgMC0uNC0uNGwtLjgtLjItLjYuMS0uNS40LS4yLjVoLTEuNmMwLS4zIDAtLjUuMi0uOC4xLS4zLjMtLjUuNi0uN2EzIDMgMCAwIDEgMS0uNSA0IDQgMCAwIDEgMS4yLS4yYy42IDAgMSAwIDEuNS4yLjQuMi44LjUgMSAuOS4zLjQuNC44LjQgMS40djQuMmwuMy43di4xaC0xLjdhMyAzIDAgMCAxLS4yLS43di0uOFptLjItM3YxSDk3bC0uOC4xLS42LjNhMSAxIDAgMCAwLS40LjlsLjEuNS40LjQuNi4xYTEuOCAxLjggMCAwIDAgMS41LS43bC4zLS42LjUuOC0uMy42YTMgMyAwIDAgMS0uNS42IDIuNiAyLjYgMCAwIDEtMS44LjZjLS41IDAtLjkgMC0xLjMtLjMtLjMtLjEtLjYtLjQtLjktLjdhMi40IDIuNCAwIDAgMS0uMS0yLjJsLjctLjggMS0uNCAxLjUtLjJoMS4zWm01LjEtNlY3N2gtMS42VjY2LjVoMS42Wk00MyA5MWwtLjEtLjRhMSAxIDAgMCAwLS41LS40bC0xLS4zLTEtLjMtMS0uNS0uNS0uNi0uMi0uOC4yLS45LjYtLjcgMS0uNSAxLjEtLjFjLjYgMCAxLjEgMCAxLjYuMy40LjIuOC41IDEgLjguMy40LjQuOC40IDEuMmgtMS43bC0uMS0uNWExIDEgMCAwIDAtLjUtLjVsLS43LS4xLS43LjFhMSAxIDAgMCAwLS40LjQuOS45IDAgMCAwIDAgLjhsLjIuMi40LjIuOC4yIDEuNC41Yy40LjIuOC40IDEgLjcuMi4zLjMuNy4zIDEuMWEyIDIgMCAwIDEtLjggMS42IDMgMyAwIDAgMS0xIC41bC0xLjIuMWMtLjcgMC0xLjIgMC0xLjctLjNzLS44LS42LTEtMWMtLjMtLjMtLjQtLjctLjQtMS4xaDEuNmMwIC4zIDAgLjUuMi43bC42LjQuNy4xLjgtLjFjLjIgMCAuMy0uMi40LS4zbC4yLS41Wm01LTR2OC44aC0xLjdWODUuNmgxLjVMNDggODdabTQuOCAyLjJ2LjJjMCAuNSAwIDEtLjIgMS41TDUyIDkyYy0uMi4zLS41LjYtLjkuOEw1MCA5M2MtLjUgMC0uOSAwLTEuMi0uMmwtLjktLjdhNCA0IDAgMCAxLS41LTEuMmMtLjItLjQtLjMtLjktLjMtMS40di0uNWMwLS42LjEtMSAuMy0xLjVsLjUtMS4xYTIuNSAyLjUgMCAwIDEgMi0xYy42IDAgMSAwIDEuMy4ybDEgLjguNSAxLjIuMiAxLjVabS0xLjcuMnYtMS4ybC0uNC0uN2ExLjUgMS41IDAgMCAwLTEuMy0uN2wtLjcuMS0uNS40YTIgMiAwIDAgMC0uNC43bC0uMS43VjkwYzAgLjQuMS43LjMgMSAwIC4yLjMuNC41LjZsMSAuMmMuMyAwIC41IDAgLjctLjIuMiAwIC40LS4zLjUtLjUuMi0uMi4zLS41LjMtLjhsLjEtMVptNi42IDMuN2MtLjYgMC0xIDAtMS41LS4yYTMuMyAzLjMgMCAwIDEtMS44LTIgNCA0IDAgMCAxLS4zLTEuM3YtLjNjMC0uNi4xLTEuMS4zLTEuNi4yLS41LjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEtLjhsMS40LS4yYy41IDAgMSAwIDEuNC4ybDEgLjguNiAxLjEuMSAxLjV2LjdINTV2LTEuMkg1OXYtLjFjMC0uMyAwLS42LS4yLS45IDAtLjItLjItLjQtLjQtLjZsLTEtLjJjLS4yIDAtLjQgMC0uNi4yLS4zIDAtLjQuMi0uNi41bC0uMy43LS4xIDF2LjRsLjEuOS40LjcuNi41LjkuMWEyLjMgMi4zIDAgMCAwIDEuOS0uOWwuOC45LS42LjYtMSAuNWMtLjMuMi0uNy4yLTEuMi4yWm03LjcgMGMtLjUgMC0xIDAtMS40LS4yYTMuMyAzLjMgMCAwIDEtMS45LTIgNCA0IDAgMCAxLS4yLTEuM3YtLjNjMC0uNiAwLTEuMS4yLTEuNnMuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS4xLS44bDEuMy0uMmMuNiAwIDEgMCAxLjQuMmEzLjEgMy4xIDAgMCAxIDEuNiAxLjlsLjIgMS41di43aC01Ljd2LTEuMmg0di0uMWwtLjEtLjktLjUtLjYtLjktLjJjLS4yIDAtLjUgMC0uNy4yLS4yIDAtLjQuMi0uNS41LS4yLjItLjMuNC0uMy43bC0uMiAxdi40YzAgLjMgMCAuNi4yLjlsLjQuNy42LjUuOC4xYTIuMyAyLjMgMCAwIDAgMi0uOWwuOC45LS42LjYtMSAuNWMtLjMuMi0uOC4yLTEuMy4yWm05LTEuNnYtOWgxLjdWOTNoLTEuNWwtLjEtMS41Wm0tNC44LTIuMXYtLjJsLjItMS41LjYtMS4yYTIuNSAyLjUgMCAwIDEgMi4xLTFjLjUgMCAuOSAwIDEuMi4yLjQuMi42LjQuOS44bC41IDEuMS4zIDEuNXYuNWMwIC41LS4xIDEtLjMgMS40IDAgLjQtLjMuOC0uNSAxLjFhMi41IDIuNSAwIDAgMS0yIDEgMi42IDIuNiAwIDAgMS0yLjItMWwtLjYtMS4yLS4yLTEuNVptMS43LS4ydjEuMmwuNC43LjUuNS43LjJjLjQgMCAuNyAwIDEtLjJsLjUtLjdjLjItLjMuMy0uNi4zLTF2LTEuMmwtLjEtLjdhMiAyIDAgMCAwLS40LS42bC0uNS0uNS0uNy0uMWMtLjMgMC0uNiAwLS44LjJsLS41LjVjLS4yLjItLjMuNS0uMy44bC0uMSAxWiIvPjxwYXRoIGZpbGw9IiMyQjU0Q0UiIGQ9Ik0xMTQuNSA3OC42aDEuMmMuNSAwIDEgMCAxLjItLjIuNC0uMi42LS40LjgtLjdsLjItMWMwLS40IDAtLjgtLjItMS0uMi0uNC0uNC0uNi0uNy0uOGwtMS0uMmMtLjQgMC0uOCAwLTEgLjJhMS43IDEuNyAwIDAgMC0xIDEuNmgtMi4yYzAtLjYuMi0xLjIuNi0xLjguMy0uNS44LTEgMS40LTEuMmE1LjMgNS4zIDAgMCAxIDQuMyAwYy42LjIgMSAuNiAxLjQgMS4yLjQuNS41IDEuMi41IDIgMCAuMyAwIC43LS4yIDEtLjIuNC0uNC44LS44IDFhNCA0IDAgMCAxLTEuMi44Yy0uNS4yLTEuMS4zLTEuOC4zaC0xLjV2LTEuMlptMCAxLjd2LTEuMmgxLjVjLjggMCAxLjQuMSAyIC4zLjUuMiAxIC40IDEuMy43bC43IDEgLjIgMS4zYzAgLjYgMCAxLjItLjMgMS42LS4yLjUtLjUuOS0uOSAxLjJhNCA0IDAgMCAxLTEuNC43IDUuNyA1LjcgMCAwIDEtMy4zIDBjLS41LS4xLTEtLjMtMS4zLS42LS40LS4zLS43LS43LTEtMS4yLS4yLS40LS4zLTEtLjMtMS41aDIuMWMwIC4zIDAgLjcuMyAxIC4xLjMuNC41LjcuNi40LjIuNy4zIDEuMS4zLjUgMCAuOSAwIDEuMi0uMi4zLS4yLjYtLjQuNy0uOC4yLS4zLjMtLjYuMy0xIDAtLjYgMC0xLS4zLTEuM2wtLjgtLjctMS4zLS4yaC0xLjJabTE2LjEtMS44djIuMWMwIDEgMCAxLjktLjMgMi42LS4yLjctLjQgMS4zLS44IDEuNy0uNC40LS44LjgtMS4zIDFhNSA1IDAgMCAxLTMuMS4xIDMuNCAzLjQgMCAwIDEtMi4xLTEuNyA2IDYgMCAwIDEtLjYtMS42bC0uMi0ydi0yLjJjMC0xIC4xLTEuOC4zLTIuNS4yLS43LjUtMS4zLjktMS43LjQtLjUuOC0uOCAxLjMtMWE0LjkgNC45IDAgMCAxIDMuMS0uMSAzLjMgMy4zIDAgMCAxIDIgMS43Yy4zLjQuNSAxIC42IDEuNS4yLjYuMiAxLjMuMiAyLjFabS0yIDIuNHYtMi43bC0uMi0xLjNhNCA0IDAgMCAwLS4yLTEgMiAyIDAgMCAwLS41LS43bC0uNS0uNGEyIDIgMCAwIDAtLjggMCAyIDIgMCAwIDAtLjguMWMtLjMuMS0uNS4zLS43LjZsLS40IDEtLjEgMS43djQuMWwuMyAxIC40LjcuNi40aC44bC44LS4xYy4zLS4xLjUtLjMuNy0uNmwuNC0xLjEuMS0xLjdabTEyLjQtMi40djIuMWMwIDEtLjEgMS45LS4zIDIuNi0uMi43LS41IDEuMy0uOSAxLjctLjMuNC0uOC44LTEuMyAxYTUgNSAwIDAgMS0zLjEuMSAzLjQgMy40IDAgMCAxLTItMS43IDYgNiAwIDAgMS0uNi0xLjZjLS4yLS42LS4yLTEuMy0uMi0ydi0yLjJjMC0xIDAtMS44LjMtMi41LjItLjcuNS0xLjMuOC0xLjcuNC0uNS44LS44IDEuMy0xIC42LS4yIDEuMS0uMyAxLjgtLjMuNSAwIDEgMCAxLjQuMmEzLjMgMy4zIDAgMCAxIDIgMS43Yy4zLjQuNCAxIC42IDEuNWwuMiAyLjFabS0yLjIgMi40di00YTQgNCAwIDAgMC0uMy0xIDIgMiAwIDAgMC0uNC0uN2wtLjYtLjRhMiAyIDAgMCAwLS43IDAgMiAyIDAgMCAwLTEgLjFsLS42LjYtLjQgMS0uMSAxLjd2NC4xbC40IDFjMCAuMy4yLjUuNC43LjEuMi4zLjMuNi40aC43bC45LS4xLjYtLjYuNC0xLjEuMi0xLjdabTktNy43aDQuNmMxIDAgMS44LjEgMi41LjQuNy4zIDEuMi44IDEuNiAxLjMuMy42LjUgMS4zLjUgMi4yIDAgLjYtLjEgMS4yLS4zIDEuNi0uMy41LS42IDEtMSAxLjMtLjUuMy0xIC42LTEuNi43bC0uNi40aC00LjF2LTEuOGgzYy42IDAgMSAwIDEuNC0uMy4zLS4yLjYtLjQuNy0uN2EyLjQgMi40IDAgMCAwIDAtMi4zYy0uMS0uMy0uNC0uNi0uNy0uOGEzIDMgMCAwIDAtMS40LS4yaC0yLjN2MTFoLTIuMlY3My4yWm03LjQgMTIuOC0zLTUuN2gyLjNsMyA1LjZ2LjFoLTIuM1ptOS00LjhoLTMuNHYtMS43aDMuNGMuNiAwIDEtLjEgMS40LS4zLjMtLjIuNi0uNC44LS44bC4yLTEuMWMwLS40IDAtLjgtLjItMS4yYTIgMiAwIDAgMC0uOC0uOGMtLjQtLjItLjgtLjMtMS40LS4zaC0yLjd2MTFoLTIuMlY3My4yaDQuOWMxIDAgMS44LjIgMi41LjUuNy40IDEuMi45IDEuNiAxLjUuMy42LjUgMS4zLjUgMmE0IDQgMCAwIDEtLjUgMi4yYy0uNC42LTEgMS0xLjYgMS40YTYgNiAwIDAgMS0yLjUuNFptNy42LThoMmwzLjcgOS45IDMuNy05LjloMmwtNSAxMi44aC0xLjVsLTQuOS0xMi44Wm0tLjkgMGgxLjlsLjMgOC41Vjg2SDE3MVY3My4yWm0xMS4zIDBoMS44Vjg2aC0yLjJ2LTQuM2wuNC04LjVaIi8+PC9nPjxkZWZzPjxwYXR0ZXJuIGlkPSJhIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBwYXR0ZXJuQ29udGVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCI+PHVzZSB4bGluazpocmVmPSIjYyIgdHJhbnNmb3JtPSJtYXRyaXgoLjAwNTAyNTEzIDAgMCAuMDE3OTQ2OSAwIC0xKSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjU2IiB4PSIwIiB5PSI1MiIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMyIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMDg3Xzc0NjQzIi8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMDg3Xzc0NjQzIiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PGltYWdlIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3QkRBQUlCQVFFQkFRSUJBUUVDQWdJQ0FnUURBZ0lDQWdVRUJBTUVCZ1VHQmdZRkJnWUdCd2tJQmdjSkJ3WUdDQXNJQ1FvS0Nnb0tCZ2dMREFzS0RBa0tDZ3IvMndCREFRSUNBZ0lDQWdVREF3VUtCd1lIQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnci93Z0FSQ0FDZkFNY0RBUkVBQWhFQkF4RUIvOFFBSGdBQUFnTUJBUUVCQVFFQUFBQUFBQUFBQndnRkJna0VBd0VDQ2dEL3hBQWFBUUFEQVFFQkFRQUFBQUFBQUFBQUFBQUJBZ01FQUFVRy85b0FEQU1CQUFJUUF4QUFBQURHUDUvZDdBLzRnamNRdnFsS1JhKzU2UXRGcWJMb1ZoMXViQ3ZYd1F2UkVUVVV2emJSUE5ZdnEyUFcvTW0xbzk0T2hlVFNZVmFPNEd5YkY5VC9BRExleDUzM2ordUhoUll1NDlPNTNQSjFzVEtpamFJcy9uc3hFNkxMYVNhWGlZWlZkNkZkUTg5Yjl4RUJYQVgwY1lPcE43c21reEsrYk92SzhPYlFjcG5EUDFNUG9PanRDZVo2MlRmYVR5OW9DNGNuRjFaVWVLRktFd3dPMzVJRThWRWJSekxiUm1WTzd1cFBjalZwWXdlbGpaelBadXM5YnFReThhV3NkL05GNitFcEF4Wk5tbVQ5Q3BJSENEaFF5Q0h4ck5rRjVXN3dxWEVidDdpM050RW8xdnFtaXNBZ3k1V2JzeThXbTV1UFRPQVhwV1RHOGJnclF6Y1dBYXFRUVVaQlBSeVdUTlRpQnBka3MwelpsWnJJV05DUGExNWtwdlRtQnNScW9WR3BHZSt1Q3hhb2tqTlZlclM5dTV1NFdmdlBiQ3IyTUZiSTFIODNZWVl0Vlc1YnF6SktNdU5VdXluMzdtNWhXWTQ4QjRYTW52M1FKNWw0MG0rNUhORWMyUFR4dDVoMDBmZ1E2cm9uZzFJYnN6K0tIaElhWE5jSDFucXhqMExMVmFreWhXaXczQ29zSGFoV3ZkeFJVclJXWVhkR2tsVTh5Zmw3c3dOdVlkMFI0YzlpVko0ODhJcUlpT2lKUUJJNExlWk5CRFVrQlRGbnMrTkVRblZBWmFNVXdmdkpwZTdMY3RLYitqZmpqWGlvdmRhV3dVRFJKcXM5THlDV0ZhbzhGZnF1YWV2TEUzVS95Y21acWhSME9TUDRIbGEyUUQ5cDJiUFFrbzNIVmRkdkwyR3BXSnlOQ2QwT2VIN0t1TlVGaktYVWQ4b3VUZ3duSVR1cVo4YXN5WCtsbXRDSFJiek5rOE82QXkvMmlGYlRkUE5vUEVYWFNpNXllaGpiaVZ0Ti9PMUdSRDA4WUVCTzlNNkFlaCtETlNvZTVrd0t3eGRRc1JrdDZ1SlU5VVNGQ2pvNDdhdFl0UUNkY2IvVHhEclJQUmJ6ZGRDSExiZVFyMHk1Q0dXelcwTHgzbnVLdjJtckY1UGRpMGtWWGllQldVdHRKcXUzTCs0dWZEK2ZQMnZPbmM3dkZrMFBmbnNuMm1PZnVtTEJ4cEd1b3Z2T1Jtd25xa1NndmlPUzUwcjNkOVBFZEM0TUxlZzZodU5FYzlZamhFbnJrcHkzMXdIVjVoR3M5SnZQMTFObGJhTk1zdHVaY3F6ZzdMV0t6ZER6dFpNbXlGYk16YjV0QS9JNkFmejNXMGMvR2U5ZUtqaHc3Y0tCbGxPYU9JWFRIRDBNaERVL0tCNjhHcG01dktqazh2TFBIVkQzN2hEYVJDbFF1eWNtcTFlUFU3aDlQZk9QWUJxM2swVlZpTUNrU1FzRnAxczlPcjBTd29sazlIWFEzejlha1hsTXFaNEZMTm1lb0ZZeWk4ZGxtVWJsSU0yU3h1bTVRbFFvellCMVJtNDBZV2JoSjErOTF4QnJYQmZhTEFNb0N0Sm9wMGtwdWNVS3RWU29NRlIwdzYzRkd2T0FkWi9OUzZJelRaclBCbnRFT3EzVm1wMUVMODZhVFpkRmNQUmhCYW1lUThHSFVOMFNaVXlYZGZBV0FtNi91cUY2SUFMUkgxRFJWRVlTRlhDelc0S0t2ZHBwOWFYSnc4VzdrWmVpWmVUSnFlakxlMkFrd0dFNENKK3oyMDVpZ2o4WEFjT0FGcGp6VVdsSjNxUklrbkxMWWlIbHV2S211SVNxYzVIdzkrMTZTbVpDYis1NW5NOVh3eTZHampUbVBDRmt6azB4TXF1Rkt5b0RBQjdJUU4xc0dWdnZjYzQySWNITmlVTU0yVTNSRlF0a08zdXFOVTRuNDdlYm83TzZLWmZ5ZXNrMkxLUEdPQlhTWlZqUmlwVkE5b3E1cmtOOU12SnVtb05iRVl4NUxGT2RDdWpQckNsQWJzMXRlWUZWa09OTTdWSnBpVGsrVlJyV1VVUmNWY2N2UGdxczZqZi94QUFyRUFBQkJRQUJCQUlCQXdVQkFRQUFBQUFFQVFJREJRWUhBQkVTRXhRaEZTSWpKQWdRRmpFeU14Zi8yZ0FJQVFFQUFRZ0NqbVkxbmJyM002OXpQOTlEMytXREhWSm4yMHNscitXNnVMcWE2TithVlY1ODI1ZytReStvcDZTSnNrdFlWNmpJMzlaQVNHYVFWM1VVVEd0K3lIMXdNTHJFeldia3E4SmNLTUhkVE4vYXFheCtvbGQ1cGl6ZVFvSSs3YmlobzlnSVVEUHlaeHlUa0RaelkzS28vZGVuVFNwOXk4ZVA5MVlPM3E5dURNeFdQdWdNWnQ5cHRyQjRYVnBUY29oMzROSyswNDF0cWtOVHJ4SEwyNjhuZEo1OSs2eVA3LzJhMWZORTZ4bW5pbzRmeGt0L1NWV2tpUmhOdG5LZG8zdG9zc01ZQ3hzMDdYVzVUdjQzSTF2Y2l1WlR5K3YzZjkwMEtlU2Q4eFZmS2VpZFp5blFRSkU2SkJIS2c5QlY1V1BPR2VGTnM4TlBRVzB3dFZhM3BKclBobWNXZDNWY1VxYkI0MytObE5zajdiTVBIV0lETWJJVTlBS2dhK3d2TXppVWdqUjdldmF2U3ZWMzI3djFTQ0RrSHcvUHJPTk1qZERRV0Z4eUptcVNrMTBRZFZFZkhERDYwNGNsamRaR1M5RmRsbmpWTHZRcFh4TERIcCt4OWxMTXlLbG1kMmN0U0ZKRkkxRXdqZzFlMWlpZGtnVHA3bXUra05qWk5DbzcrUzgxRHJLeEpTdGRqN2VwaS9KZGNNbmVZL3d1dFJYUldsQ1pYTy9wMHhXR0lwU3pUT1pNcmFuNTlsOWsrTStWNlBlMDdKSGpjZjJwSk1MT2pPSldFaC9Kb0J1SDdCMzJYSHhSWER2VDVORnhabEd3dWpueXZKK0xwNmtRSS9jM1ExL3BXblZkaEtzRGtnZlM4blI0QWQxa3k0L3FQdnJGcU5HRzVLdmRhWTJzdFhWNmsyTDQwWlZvblJBYWpxallzMDRpc0tTYVdqMTFQWmlkb0dsTWVubWxnVDlMMkxsUjg3a2R5SlJaY0dZaUluak1xeWFjNFd0SVhRTUk5YjZNN1Q1VS93REs1czdrVFV3QndXUTllU0pxdFNTZGZhWWw4QUsra0RUR0d5UndqRjZ6T2pKKzhOdGFBMGhzQTBHMWI2MGxBc3NGcWtta3NvNzU5cENzTWdRUU9qUGIrMU5uYndtd1VLWk1wWHdKMktvVzFsTGF4a0IxeEkwVTZIU0RPZ243T1RPVkRqN0tRK1VvME8ycjN4RGhZSWtNbjhuQlYxNzYrQ1dGU3JGRlZVU1FueW1haWNrM1JRaGN3M1hIT21BenBzN3BMN2tPOExNbFBESXRyTTZYM2x5R0hFckZFbUFyTlVCQXBCVzh3TUZsUXBQVlg5eGJsUGRWSDF3Ynp5R3hNbzh6VzVpdWI3czdmVjJqU2F2ajJlUmpMRUlzQWFLbXNEQWZqM2JLTXVTWjN4clN0c2hMbVVLTUxER1djSHp3aHNsTTB4SGxRWm1FbXZZT1BBQTBSRTZXZXhxblIzZGNGeWNPTmFIMkkxaHkvYmoxc0lFRUcyc3pMaVFlT3RuSmRFMThySHVVaHJsNU1JdGh6WGVzbjJKT3NybU1IdGN3UFl3S0FrckZaR0x4RnlsY1NNSTZ4K0p1Nk5QbjM5L3luYW02dXZ1SU5yUkRNdjMrUEgxVU0yN1U5S1NzdExsdjV3YWtyeThydnhwOVBaaXd6em9jQlpPclFTbmxUVTF6VGozMWhsenR6ZWdXYzBNczJUM0lGUThtQ0Vya1dZa3YrTmlMMzg1U3pveXBJa0psZEU1YU1naG5tSGQ4V2FnL3pOR0I0aTIweERpN1RMY2N1cTNLVVUyTDF4OXVtcy9lUjNXL25HYmVQZTZxNDgwR2ptN0RaRGlzdXJycHgrdi9BSlY2SXA1aHk1OU5tSjNOWmU4c2FlR0tKeURXL3BqVklkM1pGVHhDbEp3NVZ6M1B5NFJuMngrU24vQk9HdjROSlhKVzZJQ2NlckNiWFRFUjFGMFBIRlBiNFRVQldyL2pYeGpYZXNFSTZXWHU5cVJOYkNxTmw0ZHVKNHJaL3FTbEhGc1hrUURSSTFVVkd0YTlQMExBeE8vc0pqYXp5Ykc5aVRQOGVyd3Y4ZldrR056MkpTeXUyMlU2NlBJWWhzTlhCWG5jcVgvNzQ0MUp5SWpmSjEwbW9FZ1dPN01BcVRqNUxzR256NkZzUkdibDBhSFJ4TjRNc2hvTE1nUit1MVd0T3ZKQndTakwrT1ZJN2VnMGRWUG43S3l1QmR0TXVUa0lpTjVKdXA2dU9zWDVWNWZHT1FhdnAzd1NmeXJ6TWoxWWpMYXV3bC9QUjJ6SW5KZElPMW52Q2IyN2VjQS82T2lHTDVmYzZlVFBxVmpZVy9WdEwrVE5aQ3k3dkNLMk9MR1pURlp5dHp5STVhejJUTTd5UXhNUk90QzFPeXRiWWhSeXY4NUE2ai9FdzVTN3F6TWRZSHlGT3k5eTJpdVlTeU54aTcrNG5icHdSc0JyOUpUSXpWSng2QlhRSkF6ZEh4MVlpZ2kxL0h0N2JVRE5FdkhOT1JrS2lHM0cyUTlEcGdKNzdxaU5waWFNMFNYK0lOWXUrSHh0djRnMk1vamFWeEFNYXk1K0xVMHpXZXNxVFFaNTBmZFNMUnIyZnc5cHM0cThkWW9ZTEZnTkxMY3daT3FraFl0akpBVXdOVTdVMXY1cDJjeXlWRTZzQzNFdmNpRnAyWDc1Sm9OVFdHTy9Jd09oams3ekJBd1hTeXNxK011VW1aa1I5VmJNbnJaUkhqQmFhN3JhMlA0c2V1MGtseVlyb3ZLd2hnYW5YSFJJKzRwUDhZTjFvSzVLR1NxaUZmWlFRU3k5VHpPa21WN28zUGJJa3NlWDVKdmMxRDZZZ09jZ2lYK3A2OHVWSGt4SU5IeXpaV1FibWhDV0p0dHBVbm11bU9JbkhEWXh5UXFyR3ZPKy9MclBUU1BrUmVnWFF5cDYzRkJLajE5bG1za1QxYXRsblF0Wng2MlFtNnE1cVd5bHF5cTQ2ZFUrS05pY1VNTEV5N3RzOFRSUHI0cTZIbGZBV2JScGJxQWFBV2RXUkhBWHdtVFlqeE5SdWFEUTF5eXc1UGYzR1ZsL2pGMjF2dEpFN24xQnRYK3VhRnFPWDdnci9obFJQc3JoSzBpeWxtcC9RdlN4ZDArOG1QSkJjTldEc2toajVGbG1Ycy9zc3NqcFd4TXlHZUJIRVdjcTRGSHNucFhpVVZiWitMZ0pZcUNSNDNjckQzR25xN1NUdHkxWHNIT2lMandvY0I5OHdlV05zRnJjdWhmczcrY1JrQTB1Wk50YlBLUlRhSGtIUFBydEM4cUMxK1E3d1NXU3VtK0srWmZ2dDI2NDdyd3BDbk9sT1duc1pJL2tEdGRHVjFzTkltanFBaHhReDFTRnFPOEZSZW5NNzlaeGtuNU52Z1BXU1RSeUZNY0oyVlVYNDZzc1lrNk5NRnRLUUpHVk9yZmNWUkloVnB5WlMxOVZHc1Y3eVBLUWJPUDBta3NKQ1kxNjBFNms1Z1l0M0ZxK1YrcUxsMWFBR3NsaE8vRDd5dVdybXJ4TE1XaWdGdE9UaFFDS3hmbE9qR2lFV1hxc045N3A0eUhveEhkVnBzZnFVYnFvai9JMlF6WEY4VXVLbmNiUVNZYlQxbmZwL3dBbUIzcUk5MHFmNythTC9wNGJ4Zm1NOHNraW1RZkVuSmc4SkZhNHFOWXlFbGFkSldrQjlOc1M2OUNJT2pOQkpZMkRlbzV5VEhPTm1KeURLZXljMTJ4Vmc0c1lEZUxwdlRxR292SVdzcWhwb1NiUXZrQzFMN2ZqTERrR3pzbnkxN1p5NXZ4M3lGdEh4eVE5M1FTckVpOW45TisxK2tGTUlqVjZRT21qY25vQTNHdXF1ekJoT1k3bVp2eHJXdm14K25mNHl5Y1ZlY1h2cmorTjduLzFEd3o1YXA2QWw2UlAzUGtwS3oyczc5Q2ppbC90RkNZNm5uVjZrMkdCcVlZM1BZUlVzamY0UjNPalVxTjN2aURTNktXenM4L3gxVFhvMFpsRkxsamIreWh0N1RUOFdJUlhvVFF3MWgyYnBacUpTQ2lxK2k4VFBGQ1dPVXFFeWdFWjdCckd4bHNadmE1aW9pOTFpUXFSZXc0V1l0QzErOHp3eGMzaTk0UmVITXBrQlVPM0pmTm5FbVFaNmNuYi93QlMvSWxtam1WSCtWMzBrS3Rrek8zUGlLOU5rR1Voa1B3aXBnQ0I1SElyWVVrWHUydFVpS1BzNnhXV2R2ajBVRjI2MmxYaktuVEVWOFZaaTZlNGJGSVM3VldXRXRFL0hXdCtWWFBUVENEYVNuT0NlZ042YlhETmNTdXR1dmM5WUdRK0pUM05kNHhOR2Q0QjFUN0J2czZvT05EYkNackl4K09xRE1NOXUwbTVjNHF5bjZNem9PZitUTkRHb29aRTB4a3lrMkhzaVoveTR2djE3NVhkUXE5cjBlbUMxa1FnM3dpQkxGcllFaWxpRmhtN0VJUEIyaS9RV2s2cDJReUY2cjl0aUx2cmwxdEphYWo4RzFQZ1dtNnJJUVRCZ1FyMlM4ejYxc24rUVdRVVBwZ0sxOXdVOVg5TVZ6bTkzeU9kSC81d25lVXpHR1U5cFp4dmduRk4wUElaME1za1JtZk9HbWU4eWFOdzNUNW52LzY4bDY3ZjJaQS8vYXRiRkY5dStmNHIrak04Z20rOVk3SFBiR0IwZnVBZzFVQ3NSSnlkTlRJejZNMTg3UDAxK2J6ZG9VUTJTbzJlTXM3RXByU0QrS05ORUgzbWtybTAxS3NSSmNqdkgxLzJWZXc3T294M21QVnNkRmxVYi9JSy9QVmdUL2oxOURwcjFqdkY3V1VsbE15TzAySEhKZEM5VkVtcnUwdmoxRlZFRVF1ZEFRTktNL3drYjI4azhxK1ZrOFhwNmRYaE9UOXhjOEZOOVF5WkswSFQydHE3UW1wbWhrNnJkcTJyakNhNlBrdVNENGlUdTNObXo0aEVWNVpsWnMxd1ZYRmM2SFYwNFJGL2NiNHV0Sy9GbFhGOFNmTCtweXE3N1hxVC93QUkrczZGSVdjajB0eVpaWHNvUUswRWVxZ1NNZXRra2JNMTYwMDdaQWZGOTRQR0JGN1FOcG5SYStIOG9BdzIwR3IvQURBTDkwa25zS3FLUkxWa2lSZmpTNjFzWmF0aVY4S3lLZktyVjdJS2RNeDNsR3RtUlllVmZNMTZmN1R3THRDL0dJTUVaNnU3L3dEL3hBQkZFQUFDQVFNQkJRUUdCd1VHQlFVQUFBQUJBZ01BQkJFU0JSTWhNVUVpTWxGaEVDTkNjWUdSQmhSU2NxSEIwVE5EWXJIaEIxTnprcUx4WTdMQzR2QWdKRFNDbFAvYUFBZ0JBUUFKUHdMMEd0ZzI4OG9qNE9WYmkvbjI2Z2hSdzRZSWlkZ0VlVkxIdkQvZFI2UlRMZ2NOT2VWRUhXK25oUTVTS2Z4b2MrT0t3S2tDUlJMcWtjOUJWenViV0oyQU1Ua2J4YzgyelZvci93REZsNEwvQUZyYkVVWCtIYUEvenFhd3Y0L2Fqa2hNTGZNWkg0Vlp2c3JhbDNZZlZDN1I1SWgxYXNKN0pIdXExU3lzcGIwMit6N2RpWkpwbEM4WlBkK3RSdUFlNnpMaWxZWjQ4UnpycGJubDc2eWswR0NueE9QenI2WjJOaElFQmpGNU1WM251cjZVd1NSWGpBUGV3WktRbnpyKzFJVzY1d0htaDBMbjNsdlRqNDBFK0hwc2xjUExxTWg2VmNBb3Jhd1lSMXhSbmtsUzYzVFE0MUgzOEJXejdoQW5EVThEQ3RsM0Q4T2tEVkJkV3BaY25VY0xLaDRVZXgwWDdYblFwZndvVkhrQTVCSE5UNGlualc5RU1pN08yZzhJWXBxR0NmZld6ZHAzc1ZxNFI3eVlhVk1nSEhIbFVHbG9uNEQ3UERsUjVRRmY5VmFoQ0V6SVU1OERWdFB2ZllkbnI2TzNGemZTYUk5UVBCbjVjNitpY0U2Y1NyaThFZy8xR2xvME0rSEdnS1prZ01nRHlMN05iTUYzZE5FcW1VeU55WGdPQU9LMlhGREQ5VFZ0QUhQaWVkTGdVZy8rTXY4QXpVZkgrVldyU3k3c3NtVGhNOU10MHF5K3JmV0pXWm9Wa0xoUEhqNzZYbjBwZXRkNFVNZWpoMVJ2c21yVzZ1SmJRSEZyYjNBVEo4ZU5mUXk4MmZhTHdNdHhKckRFK2RPTzdJUHpxWHRUVzdLT0hYRmJFdGJuYU1kNTJqY1JCOTBuc2FRZmpTRmI3WmVYV09GUDJrWE1qM2pBWWU2cnVLQy9SY1hWcTdnSFAybDhSV1k0MlRNMGtxNDNaOFBQK3RiWDNoVTRrM3k5bjVpdHN3SjVKR1RXMTVtKzdHQlMzTDVSamhwOGRyQnh5cmI2TE1rSWpsaUViRWcrSEtqSThLMnlybDB3YzVOSU5mRDJzMGtNelhYcXQyY25UcDQ5S2ozR0QzNElSbi9VVFcwN3hvM1BaRE1DcFBtQXRKN1NSaitacGV1S0hIcWEyVmR5ZWFSZnJWMEE2RHR4dU5MTFRaSGw2QVNINWhXd2ErajMwbE4zcGJjOXNHSlQwSjFaWWlyV1NXWFZsUkdtZkkxcUo2Z2Z5clJCTzZhSFdRZG1VZTQxY29MaU9adDJadFRGRzY4MnEvaXNwNTh2dkxhM1ZBemRlWEttSUdlMWlvakZBZ1QxeXphVXdPZVY4NjIzYkErQW1CL2xVMHM1ZVZZeHVvVGpVZVF6V3pKSDlRMHVXa1ZleUcwSDhhc1E0M3VvcWpkM3IxcTczbHRlMm9lSU12ekh6cUM0WStFY1ZRemx4eEtzT1ZTT1c3UEJISERJeWZsVzBYRWNnN1VlNnpxSG1hN3IzRFl3UDRSajBCbWp0LzJVVWVOYm5xUm1wRGNXc1lIMW0wbC9hSi9FUHNrZkkxT3JSTDArMm4rMWJRekdqa0pxNTQ2VTNvL3REbWhRbmpaMnE5ejcyS25JTE4ySkQ3UXFiZGFud203WEhaOC9PcjJWaUdHRG5sVjJUaHV3dUdaaTN3NjFZVzhVVFNIVUpWOWNmRDRWc3BQcmtjQlcwQWszVWV0c0RXL2pwSEd0cXBMSGJTRlFMZGV3U0RqSThhMTZWNHlibUhMYUJ6YkZXUDEyNUVZOVhicHdjSTVaSlNEeUk2bXRuYkttT2p0V3F5OW9nbk9PNkI1MVBOQzBieVQzVU9qVklXMDhGR2VsS1VGdkxyMGJ2MTBNbkRzZkVjYWxudEpKRlpMWDZ5eHdXUFUvbFZ4Zlh1NG0wYjRRQlVaaHo1OU0xYjdtWlcwM0VERHVueEhrYWxhTWtEZXN5K3IwRDJmZlRLZERMMi8vQUtuOUt4eThhdG9IakRhWnA1SnRKdHg3dkRyVTRkWjRqMnl2QW1yc0EvVjFCd091S3ZuSzRYUGE2Z2NhYy9HdnQxRFlXMGJPMmlYR1htKzlSR1MyY3FPRlI5dzRsQUhJMUc3RSt6R2VMZVZiSGpzWXdjeGZXcnhJd253em12cFZhdmNvdWxQcVlsWWZFNEZYZHkwY1p4TkFac28wZW9qaW80Wks4NjJsWVdNTEw2bTN0TGNrb2c1RnNLZUpxNnVMbFlUd3UxWGRxaEhOU0R6eURVMkxtNm4xeGhsenBpVTlnRHcvN3FNMXBHSi8ya2NCQVpQTHlxWlpOVWVVa1RrNjAwU1FTUHFrK3N2b1ovZDRuMzFkdE5EdW1kREorN0k0NHJiVTVHNzdOckM1WDNzZFBQUG5WZ3F4cGJqSVBpQ1BPazBJZ1BIbnA4L09tWFV5OW5UNGp0RDhNMXp6Vnp1WmNZNHhoMWNlREJnUlZ1bHhKN0kzcUlQZ28vbHdxRllRaDVTU2pQNFVRejRycFJyWVAxU1ZtWXlTVFM1MThlRENyZmN4TkFabzVweGdPbzU0OGFtdUwyTzZzQmNJRmgwSVgrd0NldGJDbWtDYk5Fa1N0Y2NYdU9zZkNyQ01SMit6RXVycVdWVzBwNHhqanhOUEZZUlNvSklSTnAxT3A1SEhHb2xYdEx5WDMxRmJJSkl4cWxWL1dPY2UwS3RtV1dWREd0MUpKaUkvdzQ4YTJOYzcyMlJZbkNZdzJCekI2aXRnN3RYUERMNXgvRVBBMWVvclFOd2J5T2YwcTR0SnBHdThXU1BiYXN5WVBkOEQ3K0ZiUHZOOU9XM2x6Y0tGSEhyd0pyYU5tME1hZG1UZG5lOE9CQitOWG9rendCMDQ0VVZHVHhKUGNxVVRyZ2J4Z3VBRzVyK254b2VvbDdjWHVQbzdQalRqblErTmVOVzBzMmlNNGloN3plNm9yZ215dUU5WHRSdFlrZ0s5T2ZIblViM2wzRHFGdkJFQThxQmp5ejdJcll0aHN5RTkwM1phUi9sd3I2VWJNUGlwMmVjZmcxYkp0N3FGaDJtczVNOFB1UDhBclVNZjF6Y0xDR21YSWpVSGxwNlU0SlBQUEExQkIyVXdKNDN5V0E0WVBoZzAwNGx3SkluUWRoY2VQaFRjWUV4Tkk0MGRyajQxdFprUFBTRmJJcVptbWxsTFJTNlNSRnA1WjZITmJYT3pyaU9kdDFkdGI1REFqdTQ4NnVIS3gyY01LSFdlRFJ0a1NlK281WlpaRzFNTGVQaWZsU1l3ZTJPb3E5VzVpY0RUNmpNanVUeXg1VWwyMGJOaUcwang2eVJpQnhyU2JRdGd5cCs2YjlLSUlZZGtqa2ZRUG42VTFCRzdESk5qeWI1Vks3VEJkRHpGc3N2eDhmT290N2VQeGtsYXBLYXBhNE1QM2ljR3E5MXo3c0JSSHgzY2VkT3J6eHdvUjZtUGFNYVlESDdYeHFlNVdFT0duVzFmRFNBY1F2enFEZGljUnIycHNidG1ISi9BOGw4SzJ3WVdpd2lwdWh4VWVkRFhwNW5IZXE4Z2pjeDlxQ1dITzhROE9IbUtWdnFjallXYUhEWU9jWVlEaXRXVHpDVzRaWnBJKzlrY01lUXEwaml1N2FQVVM0d3pxT1lhci9jdWtaZU40UmxrNlpIblgxalJxT21XYnN2NzZpWFFBa0Z2YlJwbHAzZCswNXo3NmtqdnJQZUZYc3BKZTZ3T0RvZm9mS3BadG55ZjNWOUNRUDhBTU9GYmNzbWIrQ1RVZndxMmRoMGVWTkErWE0vaFcwVlNlVkhNRjBWMVJhMDVwdzYrVkNFNzgvOEFzUkVtT0RmMXlheVpweWNTSHc4ZmpReTFTWXJqOEtYR0tGUnNJenF1SGpRZnNGZDkyTlRmeGFBZmpVVzhIMmRlSzJZeXRCYnROSURMbnNMelBHa1JyVXh5eTNHcjFrbDA3RlFGNDhPWFNyL1FrYmxYU1FaMFk1Z0U4Umo0MWRxODd0b0JkdUd2VHFWVGp1NUZYRW01enJXT1RHWTJJN1NnK0ZYN3FBMnBJUkllQjhjVjlJWk5uWEZ0OWpnMXl2UFBIcjQxdEY3dTR1VklLaml3WHhPS2paUTY2UjR0MU9LbWQvTjZZcXk5MHJ6Rk9XRUZuTkhhQlR3RWp0bmVOOXFrUmsrc1cwS2F1eVdERDFzaEg4SnExU1BlU1hVZW95NENtTHVrNEhKNjF3dExad1NSN3JnWUxnTjIxYjdTNHJhaVdDdmROUEdvWE1hU0h3SG5YN3ROUjkvZC9PdVNEQzRvMXpxK3RsZjdCblhORUE5TzFYUTB5N1JqaGJWb0V1TXV1ZEt2MXg3Nm5pYVdJNGtGdnhVSHd6NVVyeGwxTVozYjZTNm5oZzFMdWhBeXZtZmd5RmRTdWgvZ05TWEVsdWlCRk1zVE1HSHZJNDU1VnRCVGFpRnA3NmNuQ05wYlRER0VIVUtjWnF6STNZeHJZYWRWV0thdkRBSCs5Yk9TM3V4N1NRZ0UrOGluSlF1N3k0UGFrSlFxTW5xQnp4UzlyUkhxZCtKMXFNRXJqeHBGWkR3RG9lSG90WDNXVmQxSXhxU3RuN2kzZVhWQ29ZOWxjZDM1K201dDF5QnEzNDd3enlIblhqR1B4Si9MMERKTFlGWm1jTDIyNGhQNjFhcFpuSHFveWk2WnZ1c0J6cVJ0SDdsMi9kdCtsY2ZmekI4S3YzV0c2ajBYSWM4RzhEODZ1cktKSlZ5SWJaZld1VHhabStOQ0o4ODFuT05ZNUVEejYvQ3VObnM3U2l4NTRQTGppVDQ0NVZzdHQ3SEpxaWxqY2FwUmo4Qnl4VmpwbFlkdUtVZDhkQ2MwdDVmVHpzWjU1WFhFYUZtNERoV00rQ3Rta0lhTGk0UGg2TnJKRHVyY3ZHN3AyR2Y3SjhxMDQybEN5eS9lOWx2Zi9TbHlFUEdvR2N3Smd6TkhqVC9ENTF6b2VpMGhsUFAxcHhwNDh4NTBPQkNuL0srUCtxdXREUHJBT0ZDTzFBVW1HWkorMThSOE9WVDZmcWhMSTJPSXgxK2RXckdXZTJWaU9nSkdhd295RzBEekZUbmhJRHc5OVhFRzhKYldKZjJuZjlueXFlMzVmczVGN2JmY3BkRGI2UjVGbDRjU3g4YXhIZEl1WUxnRGw3bUhQM1Zjb1RiU2J0WkMzTk9sYlR1QkVyYXR4YURPOGJvRDVWWnZIanVucUtrMWE0SFVsdXZEK25vWnNkTkpvckRiMjR4dkpPRmJidExtS1FraVBmWVllWEROYkp1c0QycmQxbEZPcWtjMXViY3htclFrZU1Fb2FuMC80aWtWQjlZR3I5bEhMeGFvakc3REdodloxcmovQUpzVW1PdGNEbmdhTnZhN1JSVDZ5Nm1ZSkp4OW5uVW96Y2RtVjlPQUIxeFJPNmlBQ2p5SEt1ODcxYjY5MmV6eG93NUdDVTBkcGV1UjVVOENhbHdyemQ3VjAwK2RiS1I3bUwxT2RYUEZXUWdSV3lwUmF1Wk8xREdHVk9Xb1lyYVp0eUUvYnNNNmFubGxJUEJ1bGVGYy9SbDFUbmxxZGtPZUJRNHJiODJrZXpNZDUvT3RqVzErdjNNZnJYMEhudFpEN1VIL0FHbjhxbjJoR3Y4QXhZdDR2NjA5bGM0NmoxYlZzKzZnUlJoM3VKdGVvbm1RZkkwT01uRTQ1YXZhSDUvSDBJUGpXeklKZzNWMXpWbEhGam9xMHZLcDBZbnZ1WXdEenhrNDZaNTFjR0cxakliVkkzOHZFVmR3dmNRTkNFRnpGM1VFaFpueDdSd2Z3cWEzdUlZZ04vdW9tVmludE5qL0FNNFZjMnNQWTdoaC9NVnM1WnBwSDE3OU9aYng4dUZTeFFTNmpvM3E2bFBYRlhVcW9xNm5ZUmZsV3dwTG81NFBlemRuL0tuNjFCQkVNWVdPM2hDS1BsNkkyNCtGSVJWaExJUHRZNFY5SkxLd1grN2VRYXZsV3dwOXBUQWZ0V1hRbnpQNlZCWjdPUThqRkJxWWZGcTJpMjhKNHpJY1B6enpxNGx1dFEwd1JrakpZK2RQMlQxK3dSeWI4alM4UnpIajVpdUJvVVRqd29Zb0lQV25UQW5IVG5CUE0rTlIzTzR0UnEwd0VZWHp3ZThmS3AvcjloSW9hTXQzSlU4dXFuK1ZXazhzRERVckNZS3FuSzR6bXJrWUVqb1ZpNHBxVTRiQTZmRGhSSklVbkxEQUE5M1dwbVJmYUdRVlplYXVQT3B4R2pERGFzc1dxUHNvT1hoVVRycVBBNDRHclNTUm01THByYjlsc3hkT2RFOG8zbitRWmF2bzFjN1puWGxQZStwaS93QXZFbXRvcnMyMlA3alpzZTdHUHZjNnYzbWtQTjJZdWZtYVFlOXVKcmo3NlB5cHprY2pXN2doZ2plV2VlU1RMTzVQU2tMdzR5bWp2Uis3eEhsVGEwUEthUGw4ZkNwS3hSejVDcnVPYTVlWFhLVUFCSkwvQUtWZmJ2Y3ZvdVZYSUUyZU9DRFZyNnR5R1RWMDFqdGZqVm9rNGlBWVF5UGdFOFJuNVZkSXFpT1dOSTFIQlJKM3NlZkNwUXJieldDdk1IUm9QekZFK1ZOMXBnWWgzbFhyVmdra2FOd2dtSkNkcmg3K2RiYUZoQ1lDenc3TUc1R2dOcE9jY1R4ODZtYlVKQ3NqUHg3WGhtb0I5L3ZVZm4vNkJnZWRHa0I4MnFUZXpYTjJtV2M0U0pNWU9QQ3JyU3MweGo2S1pHWHcrMVZwWnY4QWZSbzIvd0JQQ3RtMjMvNjNQL1RWdWtmK0JGcC9FNU5XZWNTQlJwY0FrK1hqVU9pY2QwM011bnRlSHZxZTNXWFZ4dDk1MnNlT2VXUGpXamVTTm5kTzJNcVAvTTBmaHB3UFIxbzhobXU4RGtIdzZnanhxME53L0wxUTRmT3ZvZnZGUFRmWUorWXJZczFpUzdzalhVZloxc3VuT2U2ZUhqV3FXTmR6SEVOT3BwM1BNK1ZLUmp2cG5MVnNXZHRQN3hUbkZMZzF5enhvOWoyZFMxYmZGRFcwRlFucE11UHhwQkluMjRtRGo4S0p6YnVXaVJ1UUo4cWVUZDJ6eUdWTmZHWFZXK2tNVzgrdFpJOWFEM2ZpS1ByN1ZYVGVPMmRhbnhvcExieTlxMnVIajdlUHlJcThWNXJlVEFsa0dkUUhGU2ZHcmczRzZWUVpTbkZqckpiL0FFa2ZLczlNNVBQejh1SHA4S3lBbnRqSEE5SzdDY254L0trN1hWK3A5Q2hnUjJsWVpCcGNSRHZ3ZEZIOFBoN3Ewd1dxSU9XUzhybHVwcmFFc2VYd1UxY3hVeFpqenE0UkpVWFVzVEE1Y2RlTlBtTitBNCttUmwrNmFsN1RSK3Jaa3ovdFF6U0ROQXpzTzkydENqODYvOFFBSmhBQkFBSUNBZ0lDQWdNQkFRRUFBQUFBQVFBUklURkJVV0Z4Z2FHUnNjSFI4QkRoOGYvYUFBZ0JBUUFCUHlIQU5FRG5Xek1USnBxdzZLdm9oN0VIV29yMXhNQXRwT2FYWHpEZzZ1b1ZtU2lDempGeXl0OHowSlgwOWpqTXB4RCtKVnFWU2xtOFpsVHdIVFVMd1B1V1FHMjdyMTJHNE9OTHdEZmFGVFV5WDlmY1FFUEk0Z3F0dFp6bG5KTGxoeG40VTdPZjBsUTFJVHZLR2lhWEZPeVZpdVpVV2VEYnlaUDFGVllueTdySEtkUmlSMnFsQXVpcjRnOEtDMXgxeW9PbC93RE9nRjZQREtEUlh3WWt4TTVTVzdrMjRmSHhCRGVvWGh2eGRleVd0dTFMNDNCZmNhL2xiQTFnTWRTb0I0RnYrSThDdUZwWVE2VG1hZEY5enQ0NmlDdThjRXF1dFZxQ3dpek13SkpoZ2FUaDhrZi9BRlhHa2RVM1c5ekxqS201VlZaUVNTOXV1amtQZTVSOVRSR1B1ekxJS1Y5UnRYREZCbnI4d2NXWDRUVmpobm1XOGtML0FNUFA4U3BkUmxPaWVpYUdPMmsycmc0LzRVbFlQZmU4QVhBcVVFdDBEYnhGYmNzeVBBWS9VOC9jck51ODd3MFg3VGpZa2Y2Tml6R1ZxQ0kzWkZsTXZ6R3RjdndKYjF1bW9CVW1CeFl4Qk9hUC9hKzJPbVZWRTVlZE9IOHlrbmc5UVl5RDZsQnNwSGltY3BzclBrK1p0MkdzUWNyUjIzWk1KOVJMcUdISjdoQmRYb1k3czI3OGFaU0hsY1NqVytJSXdER0dmSTZIRDRueTQzL2RTbE52TkgvTW9ZaXJ3TnFPWm85dEZWcEZnVXIyQ1lITHNocElMVVFDV1VuaUN2Yk9TN0pFVjhzdkw3Z0hXdC9xd0src3VuQUVWNVowdGc0MzlSSFBpWWdrZ2ZDdzV1RjYxZmhjeFpxK2VBeWl1akNwVlowNHFFQkcxRzR3bW56RnJiWmg4cEhxNExxaHRTekpVc1VUYWJQU1piSkJMbXdjVjNyekVXZHZTSTFHdSttdzRxZkJSZ2IyRnkyemJDZk9IT25Vdk9zSXY5N0NybGozeW9INHRMZ3pEN05YS0M0SzhSem90SExWWXpwdG9KeWNDdUpUSE9OYzROTTJEM0QyVWVqK0kvMFZmYnE4OVgzTmNMN05ZTzZOSHpBYnd6cVY0UU1YeUVCWlgxQ1hRd3RrRFBEOUsxeCtlcGw1b2s5cm5wRUJyNFVTK3BpczVSZmwrak53RHpLcTdVdVcxUEt0OE9WOXNzL0FzbnhIUFB6S0h2ZTJoVnZsYi9FQ0hvdXFyeFIzTWVIK01DbWxXNnVMYjBkMTFRTHIyOHc2N01pSEVjUkE0M0EveDB2V0FGTVlZYWRMU0NhbTZHWDMzSFZaMHJiZFZ6RkNPdTFZczduYXJOeE55Qkx1R3dMbnJOemdsc2x2QWNVL0tZNlg2NDN3OURVYkIycUNSeldMR2VwV0dsU3VpNTFmaEdJdzV0UXlVZUV0M00yRmdiTFN2KzhHTGRVWXZSK0thWkcxS2djTEs3T3VpOS8rekJuYXQzMG5zM1RpaS9NU01YTERvYUdPOXpCYlk4Vmp5akpKbUd0ZkVBUitEbS9aOWtwRWdsVnpSZk9vN0EvMk9aTGVhdWFpR0E3d3RFdGRZbWc3bWhGWFZ2RE5ES3FFNm9BRERkTXNxOGhuYUIxT0l0S2RVN0hLdFZxK1VGK1RkbGRoQy9EcURteXpsbTYvMm1Yc1krd2RURmdPbFRQSFJ6U0tGMWpOSE1yOU9iN3M4SWNvalFPeU5GZHF1OSs0cFQzdlMrMFo0UU56WlA4QTlOR0FOaGpiS2plQXFhU0ZPUnJFeTdtN1Y5RU1lamx1V3NOWENlNnRHYWl6V1lCVjZTbXZxNzlSbEFiMmZCOFh1SnlJemNKUVY3MFFmazVXdHNMR2Qxb1lyL1dMY0EzOXc2SzNQY3RQdFlNUmNCYmNyVDVxYmp2eEdULzZuQTNZSzhXYUt3eEMwL0EycThGYnZxNWRtcEU2cXpsd3k0bzdUdjQ1QzU3aHp5ajZjYS84NGc3ekVsYVduR0xmZ2RrNDNjWG5tZ0ErYlkrUkllaG8xWG91U1ZTRlIycHpWY1pqWTdzNFBKek4wUlBHZVQ1VUVaZEIrRDJPSW1HTzRjM2NKWW1YUk81UmtEdnRNcFZ0ZzRnT2grWkwyNStKWFRTYzdPV0ZGdzlNejRKWEFQRjR0MU9kUjA3dnFpZUZEdCtRTWFKc3FyNWJ2aFJNcXhxMk1wblZrVzRWbTNndzZ4UFRqMlhma0RFaHk2TEg4aXB2RFVWMEdxYkpJY2s4Uk8venlKbmtOeHVlR3Rvd1hzVlZYbTVVcytKa3FqMkw0eEN3VDg3OGw4OVBkRXZOTXRiSExSbERiR3hic2xkK0lQc1BkQmFXcVRzalVleHBRR1R5VE0zVk01YVZyazc2WmpyU0tzZXhsMWcwV1FpelZtTUhyM0dsQUg3bG5kblhseTdYVjkzQzNSVGw2dDEzd0pRNkczYjhYbjU1bEl2MGY4aVE2MXZxWFU3WDhGa2U3N0lJeGhkTFc2aHc0aW1vckIwMHY1bTdmWVlCNFJKZnVKdnZRc3RwVlBtRFY1WmtJZnFtb3NaeEtyemQrNG11aXN2eE96TVFHWFdRK1FEdVY3Z1JzN1puZ3h4RHBURjJGR0hLWFl2VTJVS0JSYTByYlhnbEZzQU5VN0JwdnpNNUpKL2JHR3NpYW5Vd1ZEeFNiU3FsNytFZ1VlWWZwclgwZ1ZsZjVEL3d2OVk4d00yNDdndHQ1aHc5eGl2bEtsR0cvYzlSVzE3SlMrZlpUSFI4dmNISTFZQ1lHdCtvY3hDNm9TK1lvNTVUR1I1TlRxNHRINlg3d21FMERrRjNiVWNScERoaS93Q2NGN1c2aHNjdnJtMUlBVGtCelV4Y3J5V05MQWhoZDNGU25uUU5RWnFtSnV0RVoyWW1qak1NK0FTMW9MVlI5bzl6UXV4TFNZdittZ3Y5Q1c3Snh1U0t3UVZVaHpmY0JWaStIcXJoaWN4cmt2Ym5jTEF0eDFIS2Vzd1RVUEYvTVQ3a0R4cWNvSTRycWU5MnY1VEtoZGpvdlF4N1RLY1N2OEF3UUxQMG0xaTBWRGV0d3kvcTRzdCtHMHNjcWxZSXZUZVBsRU9UMmhFUkkzUmI1clUrcGFzMGJBbTVPeG5JMUJUV0RDQ1poTUR6RnV4MDVVOVRBMURGYjdVc3pjQnA1NWF2QXdaVTV3Q2UwZnlsU0F2OC9ISkIranF3N2E3VkZjekg5bkJ4M2pnM1NOREd5YS9UMCtITWJyMHlwbTFHMWJzdmhPWVZ1eHlIMERtdGk4a3ZiUHFPMXZjUWxpRmxndU5DRmhmL0FOdUVKemR4R2dRSExjRFRoSWNaQk0rMFphVllLczRUNE1ZbW83UEFXOG5IaWFGRlZyVjNLUHpHSjBlendkbnlxNTR5MU1HUmswdTRkV0RicGREeEdrTExjeURMZUVvUE5zV2xxS0lXRkRCeVozT1NGaG90aTVjM3pMaXZiNEUwWndSamxlVWxIODRtSWRBS3phdjRmM0JEdHFaZmp1OFhudDJSUHVyZHNvWVhvbFh5UVF2OGcweXFDSERDYkw4c1lnazRrdjd3bG5WTXBRS2h3b2tKbnVyNUgxQ1ZuR28yWHNvVmMxQ1NjSThhM0k4bjFBRkxrWXJCOEJocnR4Z0grMFNlZjlsZ3VmZHhJNDRNZTBRQ3I3cUlENXN6eHB1VkE1NlZ6NFllYTYyK0NzeTFEUWRueGY4QVFXK1BVY21USDB1bDlXWTl6eFg0R1Y0aDU0bGJzaTErUnhVdnB2ZjBmN2htUUY1NVpZTmdyQlE2bVJaSnFodm5YLzJiSCtVVnZLaDl3aWxKL0ZBMzlSLytCc0JxR1UrMzlSY3pZcDNwVS9aSDArTUNGY0FjN2gxU0xwYm9ENkVyWnpnbW1ZM1d3R21jZnFnWnRWVEozV09waXVkRnRjRG4rV1Y3VHQxWFErbzhxTy9IK3FVQ1NscUY5eWtTVzNlM1o3R0dYUDJKS2NENFMxNUpuc0RZeE41N3pyM09HSkwwKzR2MVRCMjZ2NFl0bXR5Tyt2OEFiaVBIYWxIeTN4TFp1VWZNVk5yNlF2YW0wMkRaWHd5cU9XWFI4a3ZWY0xEOFhuWW1odjhBdzBoYndKUy9paTRWaXdFdm9nZ1dGeWg1NC9jTk5zNXRSMXhrSmVUaG9ham8rY1BFUFU0ZnhNcnlPS1ptSmFHVys5VGpUaW5GOUVXUEUydDRwSDFyUkdmRWNJTFNqNVVRamQxRkJvajZvUEhNSzVXTDBOQzMyMW1uTlFpeUJGVWxZUktlN3JNb3FCWmJPQlljd3Q1dVlIdXJrTnZzdUx3MHdhcjBXTGxCRXlVK2xmek5DaW9KUFczdHRsYkcvRTBMTk9lU1lRcndaaEc3ZVg3NEpRb2Q1cCszNEdOcXd0bTcydnhIOXQ2dStmVWJnVlZrdEt4UnRncDBGaEVBTkxuSFNHZHVOOEg0UFVvSmFsd2ZzR1dpb2o5eXhwYStDb3QxQWllVTJsVjltT0lIa2l5NzliMVdQRTVuamszbmlOY0tNeDNoSHVSSFpTdDV5VnpBMHFibTNDR2p1em5namVrTlVpMW9yUWo2anA0U1kySzQ5VGl1T2lvM0dNNmtIQ2YrQ3J6R3I5c3EvQkNXM25INFAxaENrSVZ1dlo5bFU2Tk5BNld2N1RmQWlUODBNa0wvQUt1UHFibVgzTDFKQmxqV0RrZTVrK0U0SnlPZDVNeFhGc1pFNXVWV041WmVPend4aXpsYVlvd0szY3U5cjJ6T1BOQ1FyUnJyM0E0NC9mcDRWa1Q1Z0luRHltbFE0c0RLWnc2QlF0NFgxTFBRT3NsK2lvMzRsZ09vVjZRdmdNa3R4V21UQmRUZ3NldkF5Z0ptblBMTGZ1cU11bG9sS0d1cFZUQlkweGZsRlpLNmxCc2d2eU9lWG1MS1ZGWlBMNmlGcXhkekR5Z0JFQmF0TTVZczJuQnMwZmlYNjlIZlJOdnFXbGk5MlptaloyOVFtNVdYOHV0OUk3VlBuNlFQM045blg5cmZxWFJUMXVjQlIrRXhQUVVuVWFQN1I4b3RPMUpqRnE2WHhMeVZyZWZ5YmNKVHFnNXJKemlFL3BBM21aY0xyd0Y2clhBVzU2aFZxNEdCbTZ0NTZoSVdPMWcvQk9hQzRmN0dYQ3NORXd0aXpLdEpyakF6cTNuQTV4eEN0YXk4ZTVWVE5HR25pWnJPaUZMS0FvU2ZoaVFxOVA2WWtjaG5YNFJlZkYvMncrby9ha214cVcycmp6QlB2dDV2cGNUVVhkQm4yaHI1Tzl2dkh4MzRod3VxUm1yUGFLc3hpeUJJWGNlcHUyRXZjZUw2cWxjaDBEekRUYzVkZ3NBSEtsaEhGOHIvQU0rMi9jZHBzK3hoMllaeHdHaGQvcDNCQ3RNNWttYmZQY3dLbjlNRVppSXQyK1EzL3A2cVdnSWFLYXRUM05ob0g1di9BSlA5akxxZWg5TlZBSUlkMWNMbHVyd1RaWHpLUWFOeTkyRGNiSmZJVE1OY0F5UE84UGNOVnlkeTV4L1ZnQVRuKzFzL05LK0NmLy9FQUNVUUFRRUFBd0FDQXdBREFRRUFBd0FBQUFFUkFDRXhRVkZoY1lHUm9kR3h3UkRoOGYvYUFBZ0JBUUFCUHhBL0FGZ2VONXgzNnBnVHlDOWNWTG4zWkRyNUFjU3ZOZjRnbDJTcDVyaWwyYnEwaEd0VUh3QmxNNFV6YlRqZTRHa1Raa0x5UE9DOGd3RlloL295RmVVTGJzWDVtRUROWC9XSlRlTUZEQVYwNFk1NkpoMUNHbmdoS01PS29LMndqNy9tTmZZZnJsV1VNRDlaRmVicnYrR084ZmI1TnpJekFlV0JuVFNoS1NIZGtSQkdLK0w0S1hqL0FMaFJDY1k4RmdvKzhSRGdwK05YT29leVJPSHRNQndVTGtseUhkb2NIK2htbUtTcVJOZ3BqYjdRVTN1RnRoazBYMjRRNnVOVGdicVlJc1BSU29mRnp6bUVlVEd6MjRiY0NZZ0JGd1ErUjNEckd3QXRxTkVOQ2FReXNIYW5JM1dMUXFvamluVThnWURhYk9zZE9vV1p2ZGRUQmJxTkxVS3hnaVBOTUdpcUJaa1Avd0FUMWkzM1FBYVBveFY4S0U3aVowZklYSmtlc2E1ZG40ZzR6UElEWWhuTkNJQWVUTlVnWFZydWh4WFpuaHJsdkF3WFNMMjdjSm1Lakp3Lzc4Y1RKZ0c4UWNuWlVZOGJPK3NDUU8xSUdRQUNPN3h4bUhYNWdGbXp3WEdVb2VoaEt4R2tUdkk0Uk1PenBhTUxGWTZ1Q0ExcWhXd1BHUngwdmU2WFRXZ0EzQ3l0bWtBRFE5SC9BSml1MWdBZzJCQ3E0MTUyWC9GakNpak9CZGRRQytNb3ZEVXozdUZIb1FLWWxFa0kxNGpFNXZ1NEN0d2tycTdERTJoR3VHdXA4ZjhBekErdEtuUUQ5L3BUTnVITTN4SUtCTEdPR2FkbHlUSmo3RUlURFgxa2R5aHgxWnAwQW5ENkFieGFneFNGUUE2VzZzWlI2aWFTK0NGT3FCaS8rZW91bWFycHQwTW4zY0pJYXE2RXdRREVjV2Z1c1RXM0NoMWFjY2hESHY4QUo4Y2JmSWpYOHVXRHB1RjhUbUl5eHdXWGJydDdZTlBEamU1RUU4MHFTOTVEdWtJQ3lVYWp6cloweHZhQWxmSXZLa3dMbzZjdFVCSVI4VmdYKzU5Y2VsNFNEZ3N0U3ZDUS9pWkNqc2crR1FDa0NnSjRMNXdTbnYycHFINHVSRFF1ajg2YWZJNVE3QkFTK0VmU1lkRzdrdjNEdno4elQxTVRUWTR5ZzFCeFVxTXE5akVtalE2WUFvUXdpRW9uVWxVQ0pTaUQ4WVBTK1dWazFBOXRKTjJHTDBhNWlKMU1RaHhKam02dnBEQ3dYdHRYRnRJSU5QOEE0ZVR6aGVUa3JjZ25uTnBZRncxR3NmNWpGbTV1RE95RFd4bFRqbHNaM0xuUGtNVHpnOTQxdzdLQ1FubGFrTVF5UTZ4NnIwenNVeVNpUXBPRVh0MDl4bzJFYnBNbkFBWHNGeW44anlUUGJZODNHRkIzUTNpSjlpYWpxNXNwNEZxYWpKcGNNWjJsSk0xZmZmRTZPL3dQS0lxN01WZzVKYnZXSzBRRWc0NE5jcFVyOGlzWm5yaGtCMWZWbkFPZmJaaHVtelQrL3dEbUdaMk1FUTRha0dEbWd0NlBlQzBRN0J2dkpiUlU2Z2cyR0x6ckZvdlNkaFc4dG1FeG1PbGE2QTJ2WTVkS1B3MmhwM285N0Jnd01hSXlsaVNGV3NWd2lHVVdzRkdVcDRhQmd5WTdwNG1Hem5mb3g2YlVwMnNNVVZTcHhpYzd0RTNja2RrclRiQkdSckVsTXE0bFF3NUJpaFNnbUxuMUFBSENSWGxHNEdhQ1VYQnhRVGNRUGdoSWxKajFCV2krMHdMRUlnQlNaOFlPSU1xUGVvSEJLVDc3K0NLOE9xaVNFNTNLZFZjSjZJT0pwdGU0MUlkaUVGT2doczhZQk5XbzhXL3NzeEcyTVduZHJ6V0I1TkpOK00zRjJBZVpnMWFZN1VTUGdmVGpVRG9MVTBHZ3ZqTnFzbEFuNHZnL01pN2dHTXFweTVoQTBodnFoVGEyMXhwdXZ1SGJyVVNxM2huYjM4dDZieUJVNHJtaUt6YzVuZ1dxWExHdEsxVGY0bzRUQ0tzc2hCU0dtd1hFS1FKYU5WZVVJcVlVK00xUDRhaVdEc0l6ZU1QQ3RjeTdZQkFxZ1lNYkFxcXFXekNTQnd6NGZEb3FpMzJIeGpIOGthUTQxVlhkUGNVNFNFOUJjaWVFcFdKaVZHNm9vd0R3RS9reGphdHFlcnZEeFlSZWhRUTdBbDJkY1ZscWJiK3VkQ2czYUZqNU1Fd3ZnTVdhUUxHOTJ1UjVVK0V2MWptUUpDY3p1Sk1TUXBBTmdKUmNSTU9uVGE4bWhEVHZHMTFpaWx5M2pURXYyaHJZZFFrQmZGb0dIeldTMlFwTGhQVFZBeDAwUjJVSWpKaUt6czVtd3ZzdjA0ZitteDFjNENHcHZ2RjZ4ZFJtVWVvSXQ0R016UllCTmNJaHM3c2dvQVl6cGw1c3REUXVjM2J0Z2dmQ2FQOEFMQXZ6RDV3aXl5ZEFtd3JKQXJjM0VBcjBpQUJpbVNkMUtUUTVBZE1TMkptWlFwSGZ4OURqcnJpNGwySHEyY2JNYTNJSkZRSURacWVNSU1HdnZXdnRJK01yemNRZUhIcUM1TEg1eFpWcVFNSEJPSmRmTGsvMUplS2s3NHVhMklYU2FCcUVDamV1TTJLQ1VMQU1QbEpUUXVHTEp2QmE4Wm9GdzQ0YVZBc09vb3htZVdKSDU5Rk1jOWhONjJTaWRNQXZtQXh2WE5zVGZqV0RYTHZTeDlKVk9qYmN1RWNCNWhzRy9hSmkrNk12UWFXUENJVGdZdjhBNmxCSWdTN0dTNTVKSXc2WURZOHdBc3poT0JnRU54azRSS1EwM1lGRWFKWWVYdHV2Z1E2Q2grRmx4ZkZsL1dmVUxncVQxcmp0Tm9qcXh4NmN5QnZTVHFQTVVTWXUrS3Z5QW1FWFBycSs1blYreW1xWVRIL2NEemdjWkVteERlRWhYNk5QbHpWWGNmbi9BUFdialpWV3YrUy81akJWUnlvWVVmR2FHUUhJeGplcjV1M3MzOHlUMkVEYnRSNkM5NnJlTnZLWFlCanVPcEwwTDhZRUhFQUJGdGlBVC9NTXZ0QlNGVDRQc2Zab3h4QUE3MVhWTWhZOXJyYm15SGwrUUg1V2pPTlF6SjhVQUlmQ3hlVU5vbzlvWVVEWW9Yd0kxNk9LSmdDdVBMMjUrKzc1TVpZRVVRTkpJSk1hQmNQOTlpOFMxU09lTnc5N1h1Sk1vS0d0TndrYytGcENzZGx6VXgzWm1pQVBKUVpiVktaclJtWWlLSnVzUGtNeW5qYS9IODRvN0hCc3dXWDJ6VXd2QkpYRWF4SmsrUHZHR1BMSU51K0Q2NG1YdHdlNjlaeSsxZWNIdWdIdWNheWFSYXd6ZGNqcHlsRklEaENxTG03cUlEditRdjFNZmk2TzdHd3c5T0dyUitaR0VBYW5Nb0dEdm1NVkp2Z080czEwb1JDUzdXc0ZoSFZwMzduL0FEQXdxRDFLSUsyZVQ0eXUyUzI0aWlCU2hTb21GK3VLUjNub2JhYThESytMaGZsQkZPb2kzWk9vbEduZkwxYzh6TlFsdjlJTnNLTFNzTUxLM0FRWFQ5aEJRRGpjd0cyZWtoMFZobGU3U0lRaFlzRHN3Sm1vNmNoUyt0WXY3UDdLaERRSmFaRWNwV3J2TDBuRzRsU2U4YUx3QUZHSDhvT2l0d1ZnNlRnNFpDY1ZaZTVZVGRJUFdRanYyMkJPSGxTWTh5ZEYwMmg1cmpFNVBhQ0tIOVlNbGFsUGQ1b1FrNmtQR05uUjliQjJqOE9KRGxJMWxZa3kzQm5oRDZ3THhQUGFzRFBFSVRDbSthaHBFRlJkdHJibE1WcG1UU1IrYUNPM0gwUDVFU2hzRUxXQ1pIVnVDdG9hb3Jad1NjRTJxcGF1ZVpXSWVRMVdhSFNoWVhZZWNXdXdDZGROOVp4R1JMNmJuZG14K21TcVB0U0tza0FTY0g0Rm5qUjAyYVBjZVlLc2dvNmhOMGdCMG1LQUFDK3JnbXkxN01FS1pnMCtNNzBiNVdJdjlJSE03NVBNWjQ4SHJCT3ZJZndCK2pUS2hoQjJWZnk0TlBCME03VWFIU0EvbHhXUHpZOUJ5Qng1N0RGQ1RSQUlHQkk2eGNaNzFmc3M3anNEMnVEM3VxeWN3Y2VuZHd3dTV0QXJMdURaMEdUWmJ6UVUrVWhZVTQrWkFxcDJlNGZER3dwK2lpVzJEVnBmWXhhY1N5UW1sMWhvZTBkYVg0d3BCS0tBRWFHVm9EblVxQkREVXhMU0xzd2VRU3Z2Yms1RGJVelora2NUcWkwcHZBYkV4UmxxZ0c5MHcwbzI0dlBPWFBZdDhaR0J0SHFiL2FIODVwN1hHOHZLQkdMdlpqWTRmdFBuQ3dVTS92QWRBaEdtY2QwZUN4UjhQQkZ4aU5XeWFtbDZ3Y2FpS3B4RS93QzRuZDRMTjVPRXo5UURidkVFdGRUZVdJMSttWmZvZ253cDV3Q3BIQ0liVlVFekZhMlBJNnN2ZUs4dmtvQUhXT0wzRGg5Zk1VWDNyRTN1RGVnTHpiZWJYazRiWEZ4MG5HckNhUkpoRCtyMVpyQkY4dFNCdGxvK2dObHZVd0hrWXR5SE5ZZ1ZBb2RwaGhVZUNDeFNBODVmc3lCQ0RTcm9MOEJmSmprdUIwTGp5eW5SYTNSczdoeTVCVjJpc0JkQjdVNGp1cHdXVTRpeWt3M1B1aHA3WGZ0R1BUdEZMK2hZS2EzV0tQVFcrcmxUU0cvdnlZT3NIcWVsaEswMU1jWUxhcEc0dDlNV3l6RTN1UHhwait3QkNsc1FkS09EUjJUMkVNRlZVb1ZyQ0lGckN0b1JvZ0pSV0J2SEpkNjZCODFRWEhmY0R5VHcrRFRESzh4b1VGL2x4S0lqYVNjTUVQY3gyd3hJOHJPR1IxTXVESHpWUkJTVnRzSndvWGtZbHJhNnpGUHl2cmx4Wk9tYkhzSkRJVjh4cy9sam1mRHFMNVZWNDBURUVUTVBZbi9MaE1kVzVhbjM2eU5iVWlNYmh6QU9rb0t0UFhySGlyRE95dFJPR0dsc1Q0UlFZZnBnRlpnc3g4ZjhzeFJhNldYMmcvY0QrMnVJZy8zbkJpTlIzRFpXZzZ5NjJwbFdxeU10OFoxc3NqK0FGZlE0MElVazhxeW9XenlIMlBUQ0wyTngvclg4WUJuNW8vZ1pUVGdEY2ZPTEwyVXBsV3BFUVZQT0dRekNkUDVyVVZnd004Mkw3aWltR1RmSGcrV1h1M0lrNk1DTXJubHRpaE9Mb2d4RWVMS0sySkNlaElUUmlCSHZXOEFKb0l2bDFrbC9rdFk5a0lGUU02eWJ4eitlOGlZd000YmRkQlZiMmllWEk0d2tzTC9PT1hXSWxUMjh3bFdqWFgrR1RsNklZZmJqL09CRVJmNGYwZmpKQllxSng2c3ZvelVHTTc2cmk1VU4yc0FnMFZOeGx4c2RpajFIUW1pWGVWOThEa29JOUtuNWx3TzlkdGVJaG9HekwxOFNNUjlQK1pvb1NMUW51WnA4azFRUG9NcERFaUV6UzMwbzlTYWo1d00zRkw3dW9Gdm9EK0NyaG1ZaHpXRzJ6cmRxQTVVb0FlUVFKcG9WUUE4RUpjc2dMMFhTeFFYWk03bktRME1GbkQwNEttb3pWcU5FWWdVRENabE5WTys0WHZnTVN3NXNzcXl1eXI3Y2MxWXZBR0toSWRNNWc1Z1MrdmtyK0dCQ0FFNiszWDRjMjlWaG40Z1JZVytHQWZGSlA2ZU82dXBHcjFYL0FGd0tSY1NuODZmbUtMRDhUUjllc1dZdlJwZ29IdHdEUUpzUktKajFpYWRBSnJSQWljQWNueDV4dllhOW5qRG5kR3hneUhRODRPM1p3LzNEMitJR0tCVzBOSDdpeTJCK3VzQXRKZ2djVXhhRUdscGk2TFFVRDZIdk43aDJxNmhIb3VNd2Z5N20vUEI1MHdjVTZhRVVpTFBsRGo4R0ZLQkZ0aERLTHZMNW9BajhaS3d0MXpUcmViMTR0U1JEYUFOc0hVeDZsR2I3bWp1c21pNHNhNzV5NFdHYVlseDBSdW93M2FUQytBT04yYmtZZUM4djQ1b1B6UXRIMEdqRWNYREhhLzhBd0tSTzAwcDdEemlROE4xMGZ4aXdPWWVoR2ljUDdjSU9tSE5vUk9hQWV6bERaVUxKMElDaGxjRkRCR1ErVmRseDRiMWt2N3o5UmwxMnRxVUh5MzVrK3h3MWF3Tk9tTG5nT0h6b2RWVkNDMkE5OHVLc3QzcnlnSjhXS3RNRVRXTTBPaUd5ZUExNkI2ZDRTN3hVdFo1REt2NDVyWnpMYVdRUExoOHI1UjA3S1FwV0VkeWlYWmU5UzFnUWNIRTFUdDRpWW1QMndYT0tlU1NyWUZERzd1dlowUWJOb1lsWXg2RXpCRWIrczB2OC9HM1I0RDR3WTZ5OTkrT2JqaEllcm5qQVNCR2dGQXk3NmVjLzdCeGY0OEQvQUNUK3N0UjZFRDVxUDNEc0tKSTY0VUpYRVpUQnNJRG8zUGdlSmtHdmo4a1g0dUFzTlpiNlU1eURXTHBLVlE0cGNBcDczZGZOS2hJNk4vSjhkQnVOcTRDNUtSbzlaOFFMRW1LdVNDT25PS2FVSXBjVnEwTG5IRS94K05xRk5WVlgxOGtNVjhOdXA3QjlFYjdVT0c0Y01DdjArRDBZN2dFb0twdk5NakVtZWhJbUZySGEwbFZLazYxNWJPbG14WHVIQ1FNVjRUQzcxbXJVSWsxMVE5RGVGTC9xZHMvb2I4WWV1U1FKZE1hc2tPTndoeUlibzIrRVJ6VGMxSnNvNk9GdE0vV0R6T2x1NzR4eUM3M25NTktRQzByeE1HMDVWVDArOEQ1Z1ZlWnplejRGeGFrN2FoSnVqOVFQZWYvRUFEQVJBQUlDQVFNQ0JRTURBd1VBQUFBQUFBRUNBQkVERWlFeEJFRVFFeUpSY1RKaG9ZR1I0U094MFJRZ1FsTEIvOW9BQ0FFQ0FRRS9BUEVZYzdIN1E0aDVlZ1JNTElwSGFNeW9hbUVqSVNJNlVzZnZPWUw0RVRHRjVtZ2R6RDVZSEVjWTRyTWg5eE1PUU9LN2ptYkdCUk1uMUdJdXMxR3hvaThYRk9NcVRVVjFQQ3loS0VBV0lOKzhVd24wR091cmNSR1pPSXJ0ZnFqRys4MkV4S09ZSVl4cVh2NEthTXhaQTQzSUZ4Y1lUY0dadG1NeGdsd0ZnUnhkeGtJczN0RmJGWE1LbjNnVXdLRUZBd0NoTXpsUVF2TUhVWmtCVlRRbUoyWkNUS21Xd0lCVVZiaWJDYXhDWTl3VGZ3eE9VTXhaRmNWcUJQdE9wV211WTJLT0dIYVo4bVFubmFZV0FhbTRNeVltUnAvcUZDbnVaajZ3cmVzUTlhdGJBeHVzZGhzSS9VWldObUhwYzdnc0YybU5kS0VHTHZGd1BtTkR0RTZBOTQvU2pFcFlWTG9UVkFZYUloU3BYZ0JPbkxudUoxU2lnWUtxRUl3b3dZMXVvYlZLRzhRV2Q0VUNpTGd6UHdwaDZmS2dzaXA1WjdtWXVwd2FOQlBhSUVGZ2pjR0VvSU1xS3UxVHpuN1J5N3JUU3UzZ3hvVkJ0QyswSmdXQVZNQzMvd0FMK1oxV04zVVJNS3dLb0hFQ2dYTXB4M3NaaHkwL3FPM2VZa1JOMVhuM01ZNmJKRi9KajVXeU1hMi9udEdCWGZlWTNvMUhJRGVuK0pxRlJTQ3ZZUTVLMk04eTFxYXU4dTVRT3hua21nSU1JSm5sZ0xEWGhoQzk3TXhGU0tqS2NlUXJGTzg4M3BNWXJWZndJN294OUkyKzh4ZE1weE1DYW1KclhpL2t6TzNvcWdQdENRTm9TR3hlbUw3UVdkb1ZiVHFFeEtSZENQall4Y1ZDT3BSdkFHb3VWZThPZEtwWTJTL0hDTFRtNE02NGpabWJxQmtJTlZVODJLRVljOTZpZE9ISkEzaFM1Z1ZkK1RPcXBLL3QzZ1VOdmNyUnVET2Q0Q3lrMUJrUXJFV3JKQmlCZmFBRTNVNm5FVUcrMHV4L3NIZ2cxTlViSlNVRHo3UUk3N3lzSTczTHgrMFhUMk1Ec3E2ZTBkNm5UQWxEek9zUTZicUlpQkxNQVhzSXlFTUFJdVArcFJGd2RPak9XNDNQNWk0OFdKYlA1ajVOVm1ZM0xuU2R2MTJtWUxrVGtYNy9BR0VxNWZpSUlvb1JSWkx0SFl0NGlMTDgxcVVSRkNyVy93QzhiR2NxRUN2MS92TWJxdHFaNW1OV3RSUE5KblRvV0pOWERuWEU1QUc4ejVEbWMzRTFLYWhzT0RWeHl4WGNqNEU2akFYOVkrVDl2dEQ5NW9Qek5MZTBBbVBHV1BHMG82cWpudEtKaEhnQjRkTStLdlR6eDg5L3hOMkg4Unk2Q3kwellkZTQ1OEVVdHZNR0lWVlJXU3lBTCsvOHpPZ3hQcUFCQi9FeG56TjY0bnBKcVVLb1ZDYVdvK1BIa08vdUwvaUhwelU4aGgzdmo4LzRpNE1hTnZ2dis0bXk0eUt2YXYwaUdyUGlaUWdNVVJNajRzdHJzWWpCMDFBYmZlT29CdTdtWEpkZ1E2ckpuVDVWK252Mi93RFlXZEJzWVEyUTdtSmd5STMybVRBbVJab3g0TzhSa3lDaERMc0dvdDFSOEJVWTJrSEhnQUFJelJUVUpFdU9FSW5Tc05KRlg4OFRPVDVkejZWK1pqWG5lTUFHTlRFNGJIUm9mM21PZ1ppeUFOcGxBbVppYjRpNmw0N1MvVE1hYUdOdytBamZUTGdsZ2lBRUV3cFJpNHlURng3UVl4Um1KZjZoRlRxVkF4Y1J0enRCcnhtNWUrMHdFcTJ3bTVhcmxGZUlwYXRveXNEWm5DbUROUXBoUE1ROXg0VVBlVVpScUdEd1d4OFNoQWxMTmh0QTVaWjAvSk02cWppbUpDUWFneEFjbUxpQUZ3QVh4TVFOd2dHSUxIMmpHbDNneW9QdEM0RzUzaFRHKzVYOXRvZW1Ua0dvd2RPR3VETjd3WlIzMmowZDdpZUZrVFdZSE1CSmlKVXZRS0hNODlsc0g4UU9GQkFpWmQ2TXNPMTNGQVpvdGhxVVJsY0VobUErUDhtSWlwOS9uZU10aUVJdk1PWkJ4RzZrZ2N6elh5YktMaTlMbWY2alVIUzRrNTNNOHRQYVBqSGFIM2dJOFY4RU9Wa3VheUlFRHFiNWlvRzlKaFJsTzhVRTdURW9pa29iSE10bWJlTmtiRjZidVpPcEpITUR1K3lpNHZTNW0zWTFFNlRCajUzK1lLVVVCQUdiWVJjSm5sb0kxVEpqSk5pVkw4UkxDclVWQzBYR3hJSmhUUThSQTUvYjhRZE9nRVlBR2hFQUpOdzQvU1NvaFZMT293ZVF2QzM4N3daMVphcUlkWTVpNHhVMGdTNVpJak1KWmFCUGVaY0lIMDlvMk1qbWFJRk1DUm1GYnpGa0NneGM2QnJxYWk3M01LMlNZT0lWdklZcEdJV1psNmhtMkVDTVJaaFJQKzBGZ2JHNG1VTkZ6SFRQTjIzWVRHNnV0enNhalFNUkJsWWN3WlZhTXVvR0hIZHdZTHVERGRpSW9jV1pTb3gwekhnRGl4TVdCUU9Zb3FDRDYybWRsWEhVUUFXeGhZc2Q0ZUlSWml6RTk3SG1BWXkvcUV4RUFIU0krWmh6QXdheDRJSlFNQ2dEVjRMb1JMTVoyN2JULzhRQUtCRUFBZ0lDQWdFREJBTUJBUUFBQUFBQUFBRUNFUU1oRWpFUUV5SkJCREpSWVNCQ2NTT0IvOW9BQ0FFREFRRS9BRTZSYU9TT2NFaFNmTGtPZk5rWU9TSnhjRVFleUM4V2tTbFpHWDRJcWY1STgvOEFUaXBYOE1uRGhmd2pvc3hPME4wckl6bEw1SHpGSDlpOHZ6amtvcWlVWXkweHdpbG9ob3RrNVBvUkVpaGVHclJPSEY2VFk1WDJZYjRrcVVIWnloV2lFN1ZGUy9BbWl5L0VGYjJMRkJ1MnRrb3BTMTR4K0xKZGlqUWt5SDhKeFVrVGc0cTZwR0IrMGtsS0xSaWhDckpwMWFJVFVrTEU3SGd2N1dMNlovTEZnUzdaRERDS0k1Y2EwMk4yL0hxcUNzZjFYNEk1bk4weXRpWGlLb1RUL2hrakg4TStuYlZwR3hjbzdSeVpHbTJQb2pLeDVJTHRpeVFiMExJU3c1TGJIeTdUN0VwTWNKTjB4WWt1eUtqRjZFS21KVzJ6VkNpSkYrTWpyK3hnbW90anlQdEZ5ZlpiTVhPcmFKd2JqcnNsS1QwMlJWNklRVUZ2WkZxUTRrRTJ0bEZTVHJzVUcxb1VIWWtxRXFMYWRvV1JiUFVhT1RzWGlmSkRGVW9XTmFQU3pTZDFSQ01sOXpKWlh6VFJPS1V1ekV2ZFpHM3NpbkdkdnhwQ2F0cGtxZnlRbWxkRHlXOUVHcElpTldQR3hZcHQyeU1LOHpwUzZGaWxNeDR1Q2V6aCtpNVJKWkgvQUlLWmtiUHA5Mlh4VlVMZllqVFJ3bFkzZlRIWjEyWVpXK3hMeHIrRGRLeU1MZGx4anI1SXViS24rUjhxMlZGdS9rakF5dFdmVE5XMFNsSnVrZTc1WkdkeGJaejluZEhxdFJvdkpMU0lRNGtvY1ZmWmhrNFMrU3hJU0VQcnc5bDE3WWtZcUl1aWgrRXVDYmJHN2RrSmNIWktEZnVRb1RjZmN4WTBaSlVxRmpjNDJ6RkRndERwcXlOT0wyS2syWXNxKzFpL1FwUm8xUlpPYVNFNlZrSTB2Q1loK01rWnA3STZJcUx1a1lzbkJVeGRNY2tqSk8yVkpMc3hQbkhpK3lkUVZKaWJTWmV4ZDJReVRnaU9WT3haVVBKSm9UYm4zUS93SXNXeEZlSEZTaHNsSGkybVJiNk1lTkpXeU5WUmxnNnNTaSswSnh4clJMSkJ4SVpIQXVXVWNaUkVLTlBZKzlGRk1ncWtLcjh4V3ZDVEtJT1NNeTMyWVVuTVc1ZjRUazBSYmNkazQ4WmEyeVE0T3JFWWtxS2kvd0QwVjJUbHlpSlVoZUk5aVZsSFRQZ1VyVEhKSkRteFRkazIrRm1ENyt5SDI3RnhrcUVuUmtweDJVcUl1N1hpRHRVUjJ6MFU3cG5DU04rTFFxc2o0YW9aZFdqbGI4ZW54WmswcU1DcVpPU1haNmpmUTUzYUwwVHJpSmpvUnhrMEw5RVp6V2t4WnBmS3NpNHl2VkhwYUhpWkQycW1Qd2ppamlpcUhKTkNYTGJGamkxYU9ON1k0YTBLTGpHaDZqczdXMkp4cTBySk50aUZiNkk0cE1oZ3NXT01GYlo2MktIU3NmMUdSOWFQVWxWV1k1dE9tTHd2TmJHb0ppZ21pK0EyMXNUVFdpVFJLUjJVa25SR0ttUndvVVl3WHVZOCtLUFNzZWZMTG83MjJXamtXeUppblM4cnlrM0t4ejRqeUtuUXBjb1VPYml0RHlTWXZDbHVtUmJQK3JUdG5wdSt6cjRISi93UzhjakhrZTcrU01rMEo2TE9UK0NFWDhFb2NoNFp0RktFQnQ5Q0ZxSlRsMFF4SmJaeVMwaU01ZmdwUHNlUGlPR3poK2hweFlpTk5IQkhwbzlOcENkQ25SNnRIcVZUSlNjWFNFNVNqYkpaWEYwU20vUDlURkZ0amY4QVZFWXFLRjRldWljZU95NUtPaVYzc2hCTVVYSHcyS1JkNjhWS2JwRVlwMzhuLzlrPSIgaWQ9ImMiIHdpZHRoPSIxOTkiIGhlaWdodD0iMTU5Ii8+PC9kZWZzPjwvc3ZnPg==", + "description": "Displays the latest rotational speed telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'rotationalSpeed', label: 'Rotational speed', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Rotational speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 4000 - 2000;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 4000) {\\n\\tvalue = 4000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"horizontal\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"360\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":500,\"color\":\"#2B54CE\"},{\"from\":500,\"to\":1500,\"color\":\"#3B911C\"},{\"from\":1500,\"to\":3000,\"color\":\"#F89E0D\"},{\"from\":3000,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":500,\"color\":\"#2B54CE\"},{\"from\":500,\"to\":1500,\"color\":\"#3B911C\"},{\"from\":1500,\"to\":3000,\"color\":\"#F89E0D\"},{\"from\":3000,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:aG9yaXpvbnRhbF9yb3RhdGlvbmFsX3NwZWVkX2NhcmRfYmFja2dyb3VuZC5wbmc=:aG9yaXpvbnRhbF9yb3RhdGlvbmFsX3NwZWVkX2NhcmRfYmFja2dyb3VuZC5wbmc=;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Horizontal rotational speed card with background\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"RPM\",\"decimals\":0,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "angular speed", + "spin rate", + "revolutions", + "rotational frequency", + "spin motion" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_snow_depth_card.json b/application/src/main/data/json/system/widget_types/horizontal_snow_depth_card.json new file mode 100644 index 0000000000..e489b2f721 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_snow_depth_card.json @@ -0,0 +1,36 @@ +{ + "fqn": "horizontal_snow_depth_card", + "name": "Horizontal snow depth card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAxlBMVEUAAADf39/f39/g4ODg4ODf39/////k5OQwWtfg4OCXrOsjTMfl6/qxwfDx8fFKb9zOzs5+mObz8/O7u7tYWFjy9f3k6fjj4+PIyMisrKw+Ys48PDz5+fnL1vXb29vV1dVaedXCwsKQkJA9ZNkwV8u2trZkg+Hn5+eKoul2j9zY4Pe+zPPW3fWCgoKkt+51j9x0dHSRpuMvLy9wjuRXed9mZmbt7e1KSkr39/dMbdGenp7I0vG6x+2fsebJyclLS0udnZ0hISFSqBZZAAAABnRSTlMAIEDfv1C6kOEmAAADuklEQVR42u3aeXOaQBjHcdukT1ZuOQKogIBXtRrrEdv0fv9vqvuANU7GsZVOMsT5fSZRZP1jv1k2OqMNAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJf05tVrsLdXN6+df/VOdtx8olfPvnnbuLbpAny6bjTpIjQRUjMIqRuE1A1C6gYhdXMkxCO6U4luac94yHw6YvZ41qcgpAqeMeS2/ZFDPvS/0R/uQ/CDDMMgeVP8kE987Pp/HhmdJOTYSqa6RlI0V6Ye7UWKMq0ewt6rH+/UD/327WNI5vu0XHQSZ+s+OLPANbZE/naz9DO3E2TbzcJZzkJ341IVuhA6ESlCMjUqaevioVc55PaWS/pq0fGVSkbYyahDi9BNqGO4M9eZEclf1++Es0UW0pI6fshPqUAzi5BI5NFkLIZUWomWNlmLceWQz+2iRC3vvlBhQXKiHLKRIeRuwk5AFHLI0gn8/wwZC5NDFNEi8oQgiQ9yvuaESTvedKrxneZ5ER9N9Oh0SHlJ9dSi4yOVsh/ugjgkcN2Mspnxi5dp6y79n52Nk7mubFpUDJmI8ZBD5mJ8GKJPeZvsF2ieCyHGRa4pj/SVvFmfDClL7tSDDmY8PWBGeZOF5X01pqkVIV6e69qYJ/uoxQNlbT6fmkLhjaRwhanLtOmJkLter622e32191nt93q9D/R3TkDVKUInDuHJSkM6MN9fWSt+hiYfKmJVrJvGl6RyImQoQ2RBm0PUIuR5abwzOIT3fEsxDyc3yXONSrmY8JO1cieREMWfQKlwaT0XOXNdX4vWlFrFHsmFt2/k2e/wErBTIac3+zPTxI5JQxHRbnEYr5D+2Hs85Ox/vzYdYd8/PWHT2SK2FuMJrYp5mZyjlR3K4a5XuHr9JOTsF8RBGWPTfUyFbpe6TYtPNKlLRKNwRN04LgfkifOShuULotwjQ2HyHm9xUd5i0W67lBvoSci5b1E4JEms+Pt3Sx6llMap0x2kVpIElnNvOV0ia2Q7Tky2VQxYgXN2COkm/9fSdq8oYkenQiQH8zmdFeIdvGk8DBnYgTWyypBREstjeW4UW2lQhDTjWIYEziiwZW5q0/k0zSPmHR+s+jbeo73QilNrkASpzStiOXE6sAeBJc/dy4lbxCFdXpGRE/JAkqb00jjkbPHoxGASxPQv6hDS7Z4atOlf1CKkjhBSNwipG4TUDULqptm4voCP2Ynsa/mFgQv4oP2T/MJA492Vf/PaXb29pC/VAAAAAAAAAAAAAAAAAAAAAAAAAAAAALyQ32C4FBab7K0VAAAAAElFTkSuQmCC", + "description": "Displays the latest snow depth telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'snow', label: 'Snow depth', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Snow depth\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 120;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"ac_unit\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#7191EF\"},{\"from\":1,\"to\":10,\"color\":\"#4B70DD\"},{\"from\":10,\"to\":30,\"color\":\"#305AD7\"},{\"from\":30,\"to\":60,\"color\":\"#234CC7\"},{\"from\":60,\"to\":90,\"color\":\"#F36900\"},{\"from\":90,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#7191EF\"},{\"from\":1,\"to\":10,\"color\":\"#4B70DD\"},{\"from\":10,\"to\":30,\"color\":\"#305AD7\"},{\"from\":30,\"to\":60,\"color\":\"#234CC7\"},{\"from\":60,\"to\":90,\"color\":\"#F36900\"},{\"from\":90,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal snow depth card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"cm\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "snow", + "snowfall", + "flurry", + "blizzard", + "snowstorm", + "snowflake", + "sleet", + "whiteout", + "snowdrift" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_snow_depth_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_snow_depth_card_with_background.json new file mode 100644 index 0000000000..c79740b596 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_snow_depth_card_with_background.json @@ -0,0 +1,36 @@ +{ + "fqn": "horizontal_snow_depth_card_with_background", + "name": "Horizontal snow depth card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest snow depth telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'snow', label: 'Snow depth', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Snow depth\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 120;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"ac_unit\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#6083EC\"},{\"from\":1,\"to\":10,\"color\":\"#4369DD\"},{\"from\":10,\"to\":30,\"color\":\"#2B54CE\"},{\"from\":30,\"to\":60,\"color\":\"#224AC2\"},{\"from\":60,\"to\":90,\"color\":\"#F77410\"},{\"from\":90,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#6083EC\"},{\"from\":1,\"to\":10,\"color\":\"#4369DD\"},{\"from\":10,\"to\":30,\"color\":\"#2B54CE\"},{\"from\":30,\"to\":60,\"color\":\"#224AC2\"},{\"from\":60,\"to\":90,\"color\":\"#F77410\"},{\"from\":90,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal snow depth card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"cm\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "snow", + "snowfall", + "flurry", + "blizzard", + "snowstorm", + "snowflake", + "sleet", + "whiteout", + "snowdrift" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_soil_moisture_card.json b/application/src/main/data/json/system/widget_types/horizontal_soil_moisture_card.json new file mode 100644 index 0000000000..0e42ee38a9 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_soil_moisture_card.json @@ -0,0 +1,29 @@ +{ + "fqn": "horizontal_soil_moisture_card", + "name": "Horizontal soil moisture card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAolBMVEUAAADf39/f39/g4ODg4ODf39/////k5OQjTMfg4ODy8vLk6fg+Ys7IyMjOzs5YWFjj4+Py9Pv5+fmRpeN1j9zV1dWsrKyQkJDb29u7u7uDmuDCwsK2trZaedUwV8t0dHTn5+eesefW3fVKSkrI0vFMbtE8PDyRpuNmZmasvOqCgoK6x+6rq6uenp7t7e2tvOoxV8rW3fRohNkhISGwsLAvLy8aKS4pAAAABnRSTlMAIEDfv1C6kOEmAAADj0lEQVR42u3aCW/aMBjGcbZ2b42P3CEXCYSrQKHXtu//1faaQQPTFCYNlbR6fhVSaquS/3UcoZYeAAAAAAAAAAAAAAAAAAAAAAAAAAAAwHv68uH1rK83dx+dd/ONO+4C+vDk3dferaRPILjt9elT6COkYxDSNQjpGoR0DUK65u8h9dynI3KbeGRlxgyIvEQSJTFZI0lMXv/92l9D7oXQihru1kyJuCWOBwmR+Z7xa0CSRzZ2XC63JPnLk/ZFl6OeZmTN6vCBDvw6VGdCmo7TEjfxPHJHrpcMdiFLl0buwJsu17Sg0dqNF27MQ8ZdbNZL17tYRilEaVdeHi9IaT0XL+0hTQc7urvkZpHwSgfrfcjIlW4yWGc09RY03cZyNPB2IUZ+H4y2l+rQonya8EUu5rNJZJus8NknHfHs7EzIROzldDAiWpgpbZN9iJtNN6NdiFyQMdOYQ6a0tCE/jfHoMiIR0s6rXbMvxD5EE5UR5frcjmhx8JacTN0RrZdT7xAif3p2E9zE3lp8M214a5YLDqHEXcZ0EUpoUqr59hBSC6V0roQ6E7ISIqorUb0KcdQs969T8nhONmMXsRLlnNfwQsxX6m1/fDuqdERnQrQtj6oJze2WXFHIJz18Fc/2qD6d3OmTCdVanQl54R+5f5hP6L752euIhP2ll6K2Sw9zzRdvVLWahKFqC5kJVtkOFtGVNGc9FPlh7c9+MxepquSHcEuI2ne81tWVd6QW+T7k6YmYFrPm+Kj8mUfylhDi9T/wfnC1vWg5wPLxzwFJlzQT2ieaixVVNkFVb88pX4cU7R7CbSGhEDq350Q3T63h4bn0mNJOEFDQd+xAnwIiGm/GFKTp7wkekJe5t3RY2kXUvKJQNzf6gx17VnxQ2kJ8LQ5WRyFx7KTGOHxVUJEWWTAsnDg2TvboZAGRM5ZZlpJ0dhOOyej/+TmvobTbwBWiCv2TyZLn/LYQUoeSmo5DnKE0ztj5HTKOU77msXHqFGYX0k9TDjHDsZGcW0i6AF8dlqrs1Smlzr3X8ldzIapoQm9+OGnhDGNTSLsjTpYWQzk0Do89ZmPjkA0J7I6Msx92Ii4KehftIeel45bJ2KT0DzoREgRtk5L+RSdCugghXYOQrkFI1yCka/q920/wb3YiecsfGLj+H27/W8AfGOh9u/HuPrqbr5/pQzUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7+QX7yoAidBTT3IAAAAASUVORK5CYII=", + "description": "Displays the latest soil moisture telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'soilMoisture', label: 'Soil Moisture', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Soil Moisture\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:water-percent\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#D81838\"},{\"from\":20,\"to\":40,\"color\":\"#F36900\"},{\"from\":40,\"to\":60,\"color\":\"#4B70DD\"},{\"from\":60,\"to\":100,\"color\":\"#234CC7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#D81838\"},{\"from\":20,\"to\":40,\"color\":\"#F36900\"},{\"from\":40,\"to\":60,\"color\":\"#4B70DD\"},{\"from\":60,\"to\":100,\"color\":\"#234CC7\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal soil moisture card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "soil", + "moisture" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_soil_moisture_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_soil_moisture_card_with_background.json new file mode 100644 index 0000000000..2a4d3245f2 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_soil_moisture_card_with_background.json @@ -0,0 +1,29 @@ +{ + "fqn": "horizontal_soil_moisture_card_with_background", + "name": "Horizontal soil moisture card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest soil moisture telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'soilMoisture', label: 'Soil Moisture', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Soil Moisture\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:water-percent\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#DE2343\"},{\"from\":20,\"to\":40,\"color\":\"#F77410\"},{\"from\":40,\"to\":60,\"color\":\"#4369DD\"},{\"from\":60,\"to\":100,\"color\":\"#224AC2\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#DE2343\"},{\"from\":20,\"to\":40,\"color\":\"#F77410\"},{\"from\":40,\"to\":60,\"color\":\"#4369DD\"},{\"from\":60,\"to\":100,\"color\":\"#224AC2\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal soil moisture card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "soil", + "moisture" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_solar_radiation_card.json b/application/src/main/data/json/system/widget_types/horizontal_solar_radiation_card.json new file mode 100644 index 0000000000..e29000b5a3 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_solar_radiation_card.json @@ -0,0 +1,30 @@ +{ + "fqn": "horizontal_solar_radiation_card", + "name": "Horizontal solar radiation card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAk1BMVEUAAADf39/f39/g4ODg4ODf39/////k5OSAwyzg4OCQy0bf8Mrz8/Pv9+XQ6bCQkJD3+/LOzs6srKxYWFjIyMig0mGIxzm43Yj5+fmYzlPb29vV1dXCwsK2tra/4ZVKSkrY7L08PDzn5+fn9NjH5aO8vLyw2Xuenp6CgoJ0dHS6urqo1m5mZmbt7e2rq6svLy8hISGYzLGRAAAABnRSTlMAIEDfv1C6kOEmAAAEBElEQVR42u3aiW7aQBSFYZqk18N4X/AKGLNDSNL3f7reO9i1ukAbpCInOp/SaDLjkvnrcamUjgAAAAAAAAAAAAAAAAAAAAAAAAAAAADu6cuHNxIPj9ZHN378yh1WTh+eYz2Mnhz6BPKn0Zg+hTFCBgYhQ4OQoUHI0CBkaP4Q4gYk7H7G2fkWiehIHcv3IxJm+lidJ+mY0P1cD1mruclR2qVWvEtq2SXx3h2Hw3iY1Mnel5E0RVUlC07jyMDiKf7qCptaQTuy1/NFN+V5xBZeYD6vzGbki0532cK9GpIpVRDzVF8S+5ZF8SHmHR/j0876VnNITMnJqg8Hag5xxAv1aff69ur7Vn3a09uhSS5WZKn6MdIk5orpdrOpks1rJWWFMqVb/duxSVOt3Ssh5iVt80L9SzuvjV/FFO39yNrtYysmDnmLG8uJ/IYX/IgX/ENMDfn+/ki19U1u3h8FhWLEVql8CxOktL16VhkZW7XmRSVfBkrJFoJ0S7/ICv5d3uUQVyv2LK8t5mQciBo+XDvf55ZjG8LbtqKD1Vi1CdnvojYkoti5HOLquXcOsSc80uete7LptDvbW3MgCr6kOx02Bfzo2nKaXM/u/syv3BFPsdTmywvFCjL8Oj7Q/lTz0do1cd2FHOOkObxRfKojXqjjmhputWI+WhdDhGtC2ltBPw6Qbje2km/7rAoVcFNGbKLl0olsaT3vTspcra88I8+KeeRlbpAqFtCZI7/a0a/6hf6T+NeQLkFLjkhVQGmxVgvezsJcn5kjki0yjvAWWk5Kltp0JaQwu1/I6doqtqKb3R4yUfZKZbL9wix4/FnuSPdXgMe7W6tU6+xyiDD1E+rdOyRT6zWPUx20J6+gPsTmEB6uPGb/LUSOFvXuGyKz22cV8JFYqGdzufd7SOd6SCAfvf8eMlGL9tEw+EakE9mvVmtzg9xbQ7Ru34jYxafXefl1wrk1ZC5P84rH/ZM65572GdUTui2kf5/tzGSb8vFSkpHnlI9DmRhTTkTL1yXlZXle4AnnfSFBqrZz3b+/Zd17cmqu8W4McZWR/RRSVWGZJCGPpjQtp5t8Ng2rKgk3L+GGdx4unc2mJCc0C2GyeVcIrbRS6Zo6HhewzGx4mwY3hmh1tvopJJw5SbgMzyHLquQxzy3LcJqYkHFZckgyWyYO504deh/XvfRU6u3N/4zf8hlNJ5zeicJyGs6qZOrIHQk35XTmzJKQ5142yyQkCcnljiw3kSxU0yndm4S8W7mky6qkpH8xhJA8v7bo0L8YRMgQIWRoEDI0CBkahAzNePT0CX7MTuQ8jR6sT/CD9tx6GI2+Po6tj+7x4TP9pxoAAAAAAAAAAAAAAAAAAAAAAAAAAACAO/kOdioE5akwBJsAAAAASUVORK5CYII=", + "description": "Displays the latest solar radiation telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'radiation', label: 'Solar Radiation', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Solar Radiation\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 1100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:radioactive\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#5B7EE6\"},{\"from\":0,\"to\":250,\"color\":\"#80C32C\"},{\"from\":250,\"to\":500,\"color\":\"#FFA600\"},{\"from\":500,\"to\":1000,\"color\":\"#F36900\"},{\"from\":1000,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#5B7EE6\"},{\"from\":0,\"to\":250,\"color\":\"#80C32C\"},{\"from\":250,\"to\":500,\"color\":\"#FFA600\"},{\"from\":500,\"to\":1000,\"color\":\"#F36900\"},{\"from\":1000,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal solar radiation card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"W/m²\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "solar", + "radiation", + "sunlight" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_solar_radiation_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_solar_radiation_card_with_background.json new file mode 100644 index 0000000000..6482e381c3 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_solar_radiation_card_with_background.json @@ -0,0 +1,30 @@ +{ + "fqn": "horizontal_solar_radiation_card_with_background", + "name": "Horizontal solar radiation card with background", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC8VBMVEUAAADPfgjntTTCagSpWgHx0kb///rgoCDckg7/+H+hVgGhVgKtYAGaTwCfVgD////z38Pt2sJ8wyL14sPw3ML04MPy3sPv2sL46sv25MT57M7679Dy3cL358f57s/46cnr2ML568z78dH25cawYAH///r78tT89NT++dj358X358n//ufx3MTNegLQfgH///bUhQT//eP++tvKdgH999X//N7rvTutXAH//+/89dWyYgGjVwKzYwHBbAKmWQGtXwK9agH///PEgTH//eDSgQO1ZQH//uv78dOvXQGoWwGrWgHXjQe4ZwLnszPHcwKqXQHtwkGLxzbhnyX89tflri/ioyjFcALwy0zgnCDblA7ZkQu6aALV2JvuxkXpuTjclxP14sX02E7otjaMyTjmsDChVAGeUgH13VLy1EzWiQXKaQ3G1YbkqCvGZQZVUEaDxSzNcBP67F6ay0qx0WnuwzvBcAT248jwyUbkqyzgox7EuaGrpI734lWTyUDVigWaTwHy48XKv6bxz03emBjReRf89trb0bW+133451i3ZAHq2rrVgRvfnRXiqCTflyPYiR6yZASkm4jhq07xzkk8OTT36srtxmzXmkzbn0tJRTzr5rnPxqza46WxqZTP2ZKOiXf99XWKgG93cmTqvGDepEzquDK5ei+/YQLl3cDl3K/i2a/++568spqQkI7++ouq0F/ckCHw9uLy7s7f173ay6/d6qq+t565tZ3I2Ynxz3W30nP88mjv5b3j1LjVzrT//rHk4q7Z3aDmtlihzVXRlEqlVwK4XgHy8vD//87ksFFcWlEoJyTjtxThrhHx6sXs4sW/v7lqZVz36y7OkAuXTQGVTAHLy8X//8Curqv354bz3CTYoRDFfA7j4+P78Y/13oDz133y0lWTSwH//dnx89ng8cTLyrS3rZaCgoKZjnvarHToxRzhxKLR45jG34/rzCf5+OnV1dOXi3j25m+c0FDDjlDAh0TEfCWVSwH3+/Hj49mNwMzJAAAAD3RSTlMAICAgICAgICAgQL/f31DNJ4eVAAAOsklEQVR42uzBgQAAAACAoP2pF6kCAAAAAAAAAJg9eldNGAzDOF5P9VCpN2GnEhsRQhFjlcZB4iGIRpOiQRGM4OIgWhAHXSyY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", + "description": "Displays the latest solar radiation telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'radiation', label: 'Solar Radiation', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Solar Radiation\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 1100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:radioactive\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#5579E5\"},{\"from\":0,\"to\":250,\"color\":\"#7CC322\"},{\"from\":250,\"to\":500,\"color\":\"#F89E0D\"},{\"from\":500,\"to\":1000,\"color\":\"#F77410\"},{\"from\":1000,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#5579E5\"},{\"from\":0,\"to\":250,\"color\":\"#7CC322\"},{\"from\":250,\"to\":500,\"color\":\"#F89E0D\"},{\"from\":500,\"to\":1000,\"color\":\"#F77410\"},{\"from\":1000,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAaAAAABcCAMAAAAfz5G9AAADAFBMVEX////UhgXOewLOfgPLdwPWjAbHcgKzYwGwYAHThAS/awHKdgHRgQe3ZAHQfQGyYgG1ZQHQgAPNeQLmrzDAbQLNeAbDcALquzzViQitXAG9agG7aAHJdQKlWAKuXwHTgwWxYQHBaQbalA3FcALIcQfkrC/MeQHclxDrvj/RgwLSgAKnWwHxz0vemhe7ZQK5ZwG+ZwPgnhzCbALfnBrIdAHotjPmsjbXjQvmsTLotDe3ZwLgoyDkqizpuDrTiga8aQGqWgHGbgbkqCvipCjakgntw0X///nViAPotjndmBbhoSTBbgHrvzrnszKrXQHEbgLjqiXwy07ZkQrQfQfwyknZjwqwXgLPfAbgmx/twkH88GfhpB+jVwLYjQfKcwjhnSTjpinuxkHgoCDfnRP//+nemhHXigXux0XYjwbz10/+/+3sv0PPegbuxUn77WHAaQLuxD3//+TvyEzx00rmrTL45FjquTXipyG0YQHclRT12lDfoRzhpCShVQHakA+fUwHEawj551vbkxGpXALEcwK5ZQH6613yzkXswD7z1E7ipiX+//P+/rr23lXtwjr33k/pvDjblgz983Lxz0/88GL13FPgnxeYTgHwykTLdQnmsCr34lP//9r34VfrujjmrC7clArVhQP//+CcUQHXjgP//8v010rkqyn88GzjoSbhohzy0lD//8X++5f011Pnsy3lrSr+/af+/q3//rP+9nW3YQLZixzxyj/elxzvxzr++o3//aH++5KTSgH+94L66lf551OrYAP120vXhxn+/sDflyPVgRv+/Jz++obsvjTGYwf982vcmAzFbAX+9nqzZgr//9HdkSHMbhKvWgHOcxX//9Xpty/ckRrKcgH871rSfBjKaRH99on++X7JZwr99HzLbAb000bReBPVgBDPdAf36jGnWALfohL99m/44k799GTCYgP03yfkuBTWnBG8YQHowBy0XALsxDHlrCXqyh3frwvy22b363rFfQv475X59UDx1jniqRj04XXv0if58akeqTMtAAA390lEQVR42rzc7U+XZRQH8CLkIUCCJDFICAWkQEOMhyQoQxEKSBQNCTOlBLMHTSuVtdJ0iLpKo2WpOFf5mJXa1sxpNTfdmuYbt9pyvmv1pv6Fvt9z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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal solar radiation card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"W/m²\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "solar", + "radiation", + "sunlight" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_sulfur_dioxide__so2__card.json b/application/src/main/data/json/system/widget_types/horizontal_sulfur_dioxide__so2__card.json new file mode 100644 index 0000000000..241c75db0a --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_sulfur_dioxide__so2__card.json @@ -0,0 +1,38 @@ +{ + "fqn": "horizontal_sulfur_dioxide_so2_card", + "name": "Horizontal sulfur dioxide (SO2) card", + "deprecated": false, + "image": "tb-image:U08yLXZhbHVlLWNhcmQtaG9yaXpvbnRhbC5zdmc=:U08yLXZhbHVlLWNhcmQtaG9yaXpvbnRhbC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjU2IiB5PSI1MiIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjU1IiB4PSIuNSIgeT0iNTIuNSIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjEyIiByeD0iMy41Ii8+PGcgZmlsdGVyPSJ1cmwoI2EpIj48cmVjdCB3aWR0aD0iMTg2IiBoZWlnaHQ9IjQyIiB4PSI3IiB5PSI1OSIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuNzYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iIzNGQTcxQSIgZD0iTTIzIDcwYy01LjUyIDAtMTAgNC40OC0xMCAxMHM0LjQ4IDEwIDEwIDEwIDEwLTQuNDggMTAtMTAtNC40OC0xMC0xMC0xMFptLTggMTBjMC0uNjEuMDgtMS4yMS4yMS0xLjc4TDE5Ljk5IDgzdjFjMCAxLjEuOSAyIDIgMnYxLjkzQzE4LjA2IDg3LjQzIDE1IDg0LjA3IDE1IDgwWm0xMy44OSA1LjRjLS4yNi0uODEtMS0xLjQtMS45LTEuNGgtMXYtM2MwLS41NS0uNDUtMS0xLTFoLTZ2LTJoMmMuNTUgMCAxLS40NSAxLTF2LTJoMmMxLjEgMCAyLS45IDItMnYtLjQxQzI4LjkyIDczLjc3IDMxIDc2LjY1IDMxIDgwYzAgMi4wOC0uODEgMy45OC0yLjExIDUuNFoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii44NyIgZD0iTTQ0LjI0OCA3NC40MzZjMC0uMjA1LS4wMzItLjM4Ny0uMDk2LS41NDZhMS4wNjggMS4wNjggMCAwIDAtLjMyLS40MzggMi42NTUgMi42NTUgMCAwIDAtLjY1Ny0uMzgzIDguNzA4IDguNzA4IDAgMCAwLTEuMDY3LS4zOSAxMS43OTYgMTEuNzk2IDAgMCAxLTEuMzEyLS40ODUgNC45OTYgNC45OTYgMCAwIDEtMS4wNC0uNjI5IDIuNjkgMi42OSAwIDAgMS0uNjktLjgyNyAyLjMxNiAyLjMxNiAwIDAgMS0uMjQ2LTEuMDg3YzAtLjQwNS4wODUtLjc3NC4yNTMtMS4xMDcuMTczLS4zMzMuNDE3LS42Mi43MzItLjg2MWEzLjUgMy41IDAgMCAxIDEuMTI4LS41NjggNC43NTEgNC43NTEgMCAwIDEgMS40MzUtLjIwNWMuNzM4IDAgMS4zNzQuMTM3IDEuOTA3LjQxLjUzOC4yNzQuOTUuNjQgMS4yMzggMS4xLjI5MS40NjEuNDM3Ljk3LjQzNyAxLjUyNWgtMS43MDJjMC0uMzI4LS4wNy0uNjE3LS4yMTItLjg2OGExLjQzNiAxLjQzNiAwIDAgMC0uNjI5LS42MDFjLS4yNzgtLjE0Ni0uNjMxLS4yMi0xLjA2LS4yMi0uNDA1IDAtLjc0Mi4wNjItMS4wMTEuMTg1YTEuMzUgMS4zNSAwIDAgMC0uNjAyLjVjLS4xMzIuMjEtLjE5OC40NDYtLjE5OC43MSAwIC4xODcuMDQzLjM1OC4xMy41MTMuMDg3LjE1LjIxOS4yOTIuMzk3LjQyNC4xNzcuMTI4LjQuMjQ4LjY3LjM2Mi4yNjguMTE0LjU4NS4yMjQuOTUuMzI4YTkuNDE2IDkuNDE2IDAgMCAxIDEuNDQyLjU0N2MuNDEuMTk2Ljc1Mi40MiAxLjAyNS42Ny4yNzQuMjUuNDc5LjUzNi42MTYuODU1LjEzNi4zMTQuMjA1LjY3Mi4yMDUgMS4wNzMgMCAuNDItLjA4NS43OTctLjI1MyAxLjEzNWEyLjQ1IDIuNDUgMCAwIDEtLjcyNS44NTRjLS4zMS4yMzItLjY4My40MTItMS4xMi41NGE1LjI5NCA1LjI5NCAwIDAgMS0xLjQ1LjE4NWMtLjQ3OSAwLS45NS0uMDY0LTEuNDE1LS4xOTJhNC4xNzUgNC4xNzUgMCAwIDEtMS4yNTgtLjU4IDMuMDAzIDMuMDAzIDAgMCAxLS45MDItLjk4NWMtLjIyNC0uMzk3LS4zMzUtLjg2LS4zMzUtMS4zODhoMS43MTVjMCAuMzI0LjA1NS42LjE2NS44MjcuMTE0LjIyOC4yNy40MTUuNDcxLjU2LjIuMTQyLjQzMy4yNDcuNjk4LjMxNS4yNjguMDY5LjU1Ni4xMDMuODYuMTAzLjQwMiAwIC43MzctLjA1NyAxLjAwNi0uMTcxLjI3My0uMTE0LjQ3OC0uMjczLjYxNS0uNDc5LjEzNy0uMjA1LjIwNS0uNDQyLjIwNS0uNzFabTcuNzc2Ljgydi01LjY1M2gxLjY1NVY3N0g1Mi4xMmwtLjA5Ni0xLjc0M1ptLjIzMy0xLjUzNy41NTMtLjAxNGMwIC40OTctLjA1NC45NTUtLjE2MyAxLjM3NC0uMTEuNDE1LS4yNzkuNzc3LS41MDYgMS4wODctLjIyOC4zMDUtLjUyLjU0NS0uODc1LjcxOC0uMzU2LjE2OC0uNzgyLjI1My0xLjI3OS4yNTMtLjM2IDAtLjY5LS4wNTMtLjk5LS4xNTdhMi4wMyAyLjAzIDAgMCAxLS43OC0uNDg2IDIuMjA4IDIuMjA4IDAgMCAxLS41LS44NTRjLS4xMTgtLjM1MS0uMTc3LS43Ny0uMTc3LTEuMjU4di00Ljc3OWgxLjY0OHY0Ljc5M2MwIC4yNjguMDMxLjQ5NC4wOTUuNjc2LjA2NC4xNzguMTUuMzIyLjI2LjQzLjExLjExLjIzNy4xODguMzgzLjIzMy4xNDYuMDQ2LjMuMDY5LjQ2NS4wNjkuNDY5IDAgLjgzOC0uMDkxIDEuMTA3LS4yNzQuMjc0LS4xODcuNDY3LS40MzcuNTgxLS43NTIuMTE5LS4zMTQuMTc4LS42NjcuMTc4LTEuMDZaTTU3LjM4IDY2LjVWNzdoLTEuNjU0VjY2LjVoMS42NTRaTTYxLjY2NCA3N2gtMS42NDd2LTguMTA3YzAtLjU1Mi4xMDItMS4wMTQuMzA3LTEuMzg4YTIuMDMgMi4wMyAwIDAgMSAuODk2LS44NTVjLjM4Ny0uMTk2Ljg0NS0uMjk0IDEuMzc0LS4yOTQuMTY0IDAgLjMyNi4wMTIuNDg1LjAzNS4xNi4wMTguMzE1LjA0Ny40NjUuMDg4bC0uMDQxIDEuMjcyYTEuODg1IDEuODg1IDAgMCAwLS4zLS4wNDggMy45NjcgMy45NjcgMCAwIDAtLjM0My0uMDE0Yy0uMjUgMC0uNDY3LjA0OC0uNjUuMTQ0YS45NDguOTQ4IDAgMCAwLS40MS40MDMgMS40MjcgMS40MjcgMCAwIDAtLjEzNi42NTdWNzdabTEuNTI0LTcuMzk3djEuMjA0aC00LjMwNnYtMS4yMDRoNC4zMDZabTUuODk3IDUuNjU0di01LjY1NGgxLjY1NFY3N2gtMS41NThsLS4wOTYtMS43NDNabS4yMzItMS41MzguNTU0LS4wMTRjMCAuNDk3LS4wNTUuOTU1LS4xNjQgMS4zNzQtLjExLjQxNS0uMjc4Ljc3Ny0uNTA2IDEuMDg3LS4yMjguMzA1LS41Mi41NDUtLjg3NS43MTgtLjM1NS4xNjgtLjc4MS4yNTMtMS4yNzguMjUzLS4zNiAwLS42OS0uMDUzLS45OTEtLjE1Ny0uMzAxLS4xMDUtLjU2LS4yNjctLjc4LS40ODZhMi4yMSAyLjIxIDAgMCAxLS40OTktLjg1NGMtLjExOC0uMzUxLS4xNzctLjc3LS4xNzctMS4yNTh2LTQuNzc5aDEuNjQ3djQuNzkzYzAgLjI2OC4wMzIuNDk0LjA5Ni42NzYuMDY0LjE3OC4xNS4zMjIuMjYuNDMuMTA5LjExLjIzNy4xODguMzgyLjIzMy4xNDYuMDQ2LjMwMS4wNjkuNDY1LjA2OS40NyAwIC44MzktLjA5MSAxLjEwOC0uMjc0LjI3My0uMTg3LjQ2Ny0uNDM3LjU4LS43NTIuMTItLjMxNC4xNzgtLjY2Ny4xNzgtMS4wNlptNS4wMTUtMi43MDdWNzdoLTEuNjQ3di03LjM5N2gxLjU3MmwuMDc1IDEuNDA5Wm0yLjI2My0xLjQ1Ni0uMDE0IDEuNTNhNC4yOSA0LjI5IDAgMCAwLS42Ny0uMDU1Yy0uMjgyLjAwMS0uNTMuMDQyLS43NDUuMTI0YTEuNDAzIDEuNDAzIDAgMCAwLS41NC4zNDJjLS4xNDEuMTQ2LS4yNS4zMjQtLjMyOC41MzMtLjA3OC4yMS0uMTIzLjQ0NS0uMTM3LjcwNGwtLjM3Ni4wMjhjMC0uNDY1LjA0Ni0uODk2LjEzNy0xLjI5Mi4wOTEtLjM5Ny4yMjgtLjc0NS40MS0xLjA0Ni4xODctLjMwMS40Mi0uNTM2LjY5Ny0uNzA0YTEuODcgMS44NyAwIDAgMSAuOTc4LS4yNTNjLjEgMCAuMjA3LjAwOS4zMjEuMDI3LjExOS4wMTguMjA3LjAzOS4yNjcuMDYyWk00My4zNTkgOTEuNDY5VjgyLjVoMS42NTVWOTNoLTEuNDk3bC0uMTU4LTEuNTMxWm0tNC44MTItMi4wODV2LS4xNDRjMC0uNTYuMDY2LTEuMDcuMTk4LTEuNTMxLjEzMi0uNDY1LjMyNC0uODY0LjU3NC0xLjE5Ni4yNTEtLjMzOC41NTYtLjU5NS45MTYtLjc3My4zNi0uMTgyLjc2Ni0uMjczIDEuMjE3LS4yNzMuNDQ3IDAgLjgzOS4wODYgMS4xNzYuMjYuMzM3LjE3My42MjQuNDIxLjg2MS43NDUuMjM3LjMxOS40MjYuNzAyLjU2OCAxLjE0OC4xNC40NDIuMjQxLjkzNC4zIDEuNDc3di40NThhNy4zOTIgNy4zOTIgMCAwIDEtLjMgMS40NDljLS4xNDIuNDM3LS4zMy44MTYtLjU2OCAxLjEzNWEyLjQ1MyAyLjQ1MyAwIDAgMS0uODY4LjczOGMtLjMzNy4xNzMtLjczMS4yNi0xLjE4My4yNi0uNDQ2IDAtLjg1LS4wOTQtMS4yMS0uMjhhMi42NzQgMi42NzQgMCAwIDEtLjkwOS0uNzg3IDMuNzAzIDMuNzAzIDAgMCAxLS41NzQtMS4xOSA1LjQwOSA1LjQwOSAwIDAgMS0uMTk4LTEuNDk2Wm0xLjY0Ny0uMTQ0di4xNDRjMCAuMzM3LjAzLjY1MS4wOS45NDMuMDYzLjI5Mi4xNjEuNTUuMjkzLjc3My4xMzIuMjE4LjMwMy4zOTIuNTEzLjUyLjIxNC4xMjIuNDcuMTg0Ljc2NS4xODQuMzc0IDAgLjY4Mi0uMDgyLjkyMy0uMjQ2LjI0Mi0uMTY0LjQzMS0uMzg2LjU2OC0uNjY0YTMuMDggMy4wOCAwIDAgMCAuMjg3LS45NDN2LTEuMjM3YTMuMDE4IDMuMDE4IDAgMCAwLS4xNzEtLjc1MiAyLjAxMyAyLjAxMyAwIDAgMC0uMzM1LS42MDkgMS40MTIgMS40MTIgMCAwIDAtLjUyNi0uNDEgMS42NSAxLjY1IDAgMCAwLS43MzItLjE1Yy0uMyAwLS41NTYuMDY0LS43NjYuMTkxLS4yMS4xMjgtLjM4Mi4zMDMtLjUxOS41MjdhMi41OSAyLjU5IDAgMCAwLS4yOTQuNzc5IDQuNTA4IDQuNTA4IDAgMCAwLS4wOTYuOTVabTguNTU2LTMuNjM2VjkzaC0xLjY1NHYtNy4zOTdoMS42NTRabS0xLjc2NC0xLjk0MmMwLS4yNS4wODItLjQ1OC4yNDYtLjYyMi4xNjktLjE2OS40MDEtLjI1My42OTgtLjI1My4yOTEgMCAuNTIyLjA4NC42OS4yNTNhLjgzMS44MzEgMCAwIDEgLjI1My42MjIuODIzLjgyMyAwIDAgMS0uMjUzLjYxNWMtLjE2OC4xNjQtLjM5OS4yNDYtLjY5LjI0Ni0uMjk3IDAtLjUyOS0uMDgyLS42OTgtLjI0NmEuODM1LjgzNSAwIDAgMS0uMjQ2LS42MTVabTMuNTA0IDUuNzIydi0uMTU3YzAtLjUzNC4wNzgtMS4wMjguMjMzLTEuNDg0LjE1NS0uNDYuMzc4LS44NTkuNjctMS4xOTZhMy4wMjMgMy4wMjMgMCAwIDEgMS4wOC0uNzkzIDMuNTE1IDMuNTE1IDAgMCAxIDEuNDQ5LS4yODdjLjU0MiAwIDEuMDI1LjA5NiAxLjQ1LjI4Ny40MjguMTg3Ljc5LjQ1MSAxLjA4Ni43OTMuMjk2LjMzNy41MjIuNzM2LjY3NyAxLjE5Ni4xNTUuNDU2LjIzMi45NS4yMzIgMS40ODR2LjE1N2MwIC41MzMtLjA3NyAxLjAyOC0uMjMyIDEuNDgzLS4xNTUuNDU2LS4zOC44NTUtLjY3NyAxLjE5N2EzLjEzNSAzLjEzNSAwIDAgMS0xLjA4Ljc5MiAzLjU0IDMuNTQgMCAwIDEtMS40NDMuMjggMy42MDcgMy42MDcgMCAwIDEtMS40NTYtLjI4IDMuMTM2IDMuMTM2IDAgMCAxLTEuMDgtLjc5MiAzLjU1IDMuNTUgMCAwIDEtLjY3Ni0xLjE5NyA0LjU3OSA0LjU3OSAwIDAgMS0uMjMzLTEuNDgzWm0xLjY0OC0uMTU3di4xNTdjMCAuMzMyLjAzNC42NDcuMTAyLjk0M2EyLjQgMi40IDAgMCAwIC4zMjIuNzhjLjE0NS4yMjMuMzMyLjM5OC41Ni41MjYuMjI4LjEyNy41LjE5MS44MTMuMTkxLjMwNiAwIC41Ny0uMDY0Ljc5My0uMTkxLjIyOC0uMTI4LjQxNS0uMzAzLjU2MS0uNTI3YTIuNCAyLjQgMCAwIDAgLjMyMS0uNzc5Yy4wNzMtLjI5Ni4xMS0uNjEuMTEtLjk0M3YtLjE1N2MwLS4zMjktLjAzNy0uNjM5LS4xMS0uOTNhMi4zOSAyLjM5IDAgMCAwLS4zMjgtLjc4NiAxLjU3IDEuNTcgMCAwIDAtLjU2LS41MzMgMS41NDMgMS41NDMgMCAwIDAtLjgtLjE5OWMtLjMxIDAtLjU3OS4wNjYtLjgwNy4xOTlhMS41ODggMS41ODggMCAwIDAtLjU1My41MzNjLS4xNDYuMjI4LS4yNTMuNDktLjMyMi43ODZhNC4wNjcgNC4wNjcgMCAwIDAtLjEwMi45M1ptNy45NjgtMy42MjQgMS40MDggMi40NTUgMS40MzUtMi40NTVoMS44MTJsLTIuMjQyIDMuNjNMNjQuODQ5IDkzaC0xLjgxbC0xLjUwNS0yLjU1N0w2MC4wMyA5M2gtMS44MThsMi4zMjQtMy43NjctMi4yMzUtMy42M2gxLjgwNFptNy43OTYgMFY5M2gtMS42NTR2LTcuMzk3aDEuNjU0Wm0tMS43NjMtMS45NGMwLS4yNTEuMDgyLS40NTkuMjQ2LS42MjMuMTY4LS4xNjkuNC0uMjUzLjY5Ny0uMjUzLjI5MiAwIC41MjIuMDg0LjY5LjI1M2EuODMxLjgzMSAwIDAgMSAuMjUzLjYyMi44MjMuODIzIDAgMCAxLS4yNTMuNjE1Yy0uMTY4LjE2NC0uMzk4LjI0Ni0uNjkuMjQ2LS4yOTYgMC0uNTI5LS4wODItLjY5Ny0uMjQ2YS44MzQuODM0IDAgMCAxLS4yNDYtLjYxNVptOC4zMyA3LjgwNlY4Mi41aDEuNjU0VjkzaC0xLjQ5N2wtLjE1Ny0xLjUzMVptLTQuODEzLTIuMDg1di0uMTQ0YzAtLjU2LjA2Ni0xLjA3LjE5OC0xLjUzMS4xMzMtLjQ2NS4zMjQtLjg2NC41NzUtMS4xOTYuMjUtLjMzOC41NTYtLjU5NS45MTYtLjc3My4zNi0uMTgyLjc2NS0uMjczIDEuMjE2LS4yNzMuNDQ3IDAgLjg0LjA4NiAxLjE3Ni4yNi4zMzguMTczLjYyNS40MjEuODYyLjc0NS4yMzcuMzE5LjQyNi43MDIuNTY3IDEuMTQ4LjE0MS40NDIuMjQyLjkzNC4zIDEuNDc3di40NThhNy4zOTIgNy4zOTIgMCAwIDEtLjMgMS40NDljLS4xNDEuNDM3LS4zMy44MTYtLjU2NyAxLjEzNWEyLjQ1MSAyLjQ1MSAwIDAgMS0uODY4LjczOGMtLjMzOC4xNzMtLjczMi4yNi0xLjE4My4yNi0uNDQ3IDAtLjg1LS4wOTQtMS4yMS0uMjhhMi42NzQgMi42NzQgMCAwIDEtLjkxLS43ODcgMy43MDMgMy43MDMgMCAwIDEtLjU3My0xLjE5IDUuNDA1IDUuNDA1IDAgMCAxLS4xOTktMS40OTZabTEuNjQ4LS4xNDR2LjE0NGMwIC4zMzcuMDMuNjUxLjA4OS45NDMuMDYzLjI5Mi4xNjEuNTUuMjkzLjc3My4xMzMuMjE4LjMwNC4zOTIuNTEzLjUyLjIxNC4xMjIuNDcuMTg0Ljc2Ni4xODQuMzczIDAgLjY4MS0uMDgyLjkyMy0uMjQ2YTEuNjggMS42OCAwIDAgMCAuNTY3LS42NjQgMy4wOCAzLjA4IDAgMCAwIC4yODctLjk0M3YtMS4yMzdhMy4wMjMgMy4wMjMgMCAwIDAtLjE3LS43NTIgMi4wMTEgMi4wMTEgMCAwIDAtLjMzNi0uNjA5IDEuNDEgMS40MSAwIDAgMC0uNTI2LS40MSAxLjY1IDEuNjUgMCAwIDAtLjczMS0uMTVjLS4zMDEgMC0uNTU2LjA2NC0uNzY2LjE5MS0uMjEuMTI4LS4zODMuMzAzLS41Mi41MjdhMi41OSAyLjU5IDAgMCAwLS4yOTQuNzc5IDQuNTA4IDQuNTA4IDAgMCAwLS4wOTUuOTVabTEwLjA1MiAzLjg5N2EzLjk0NCAzLjk0NCAwIDAgMS0xLjQ4My0uMjY3IDMuMjczIDMuMjczIDAgMCAxLTEuMTIxLS43NTkgMy4zNjEgMy4zNjEgMCAwIDEtLjcwNC0xLjE0MSAzLjk5NyAzLjk5NyAwIDAgMS0uMjQ2LTEuNDE1di0uMjc0YzAtLjU3OS4wODQtMS4xMDMuMjUzLTEuNTcyLjE2OC0uNDcuNDAzLS44Ny43MDQtMS4yMDMuMy0uMzM3LjY1Ni0uNTk1IDEuMDY2LS43NzMuNDEtLjE3Ny44NTUtLjI2NiAxLjMzMy0uMjY2LjUyOSAwIC45OTEuMDg5IDEuMzg4LjI2Ni4zOTYuMTc4LjcyNC40MjkuOTg0Ljc1Mi4yNjUuMzIuNDYuNy41ODggMS4xNDIuMTMyLjQ0Mi4xOTguOTMuMTk4IDEuNDYzdi43MDRoLTUuNzE0Vjg4LjYxaDQuMDg3di0uMTNhMi4zMTQgMi4zMTQgMCAwIDAtLjE3Ny0uODM0IDEuNDE3IDEuNDE3IDAgMCAwLS40ODYtLjYyOGMtLjIxOC0uMTYtLjUxLS4yNC0uODc1LS4yNC0uMjczIDAtLjUxNy4wNi0uNzMxLjE3OC0uMjEuMTE0LS4zODUuMjgtLjUyNi41LS4xNDIuMjE4LS4yNTEuNDgyLS4zMjkuNzkyLS4wNzIuMzA1LS4xMDkuNjUtLjEwOSAxLjAzMnYuMjc0YzAgLjMyMy4wNDMuNjI0LjEzLjkwMi4wOTEuMjc0LjIyMy41MTMuMzk2LjcxOC4xNzQuMjA1LjM4My4zNjcuNjMuNDg1LjI0NS4xMTQuNTI2LjE3MS44NC4xNzEuMzk3IDAgLjc1LS4wOCAxLjA2LS4yNC4zMS0uMTU5LjU3OS0uMzg0LjgwNi0uNjc2bC44NjguODRjLS4xNTkuMjMzLS4zNjYuNDU3LS42MjIuNjctLjI1NS4yMS0uNTY3LjM4MS0uOTM2LjUxNGEzLjcyNyAzLjcyNyAwIDAgMS0xLjI3Mi4xOThaIi8+PHBhdGggZmlsbD0iIzNGQTcxQSIgZD0ibTEwMy41NDUgODAuMzg5LTEuODc1LS40Ni43NzEtNy4xNDhoNy42NjZ2MS45OTJoLTUuNzEybC0uMzkxIDMuNDg3Yy4yMjEtLjEzLjUyNy0uMjU3LjkxOC0uMzgxYTQuMjIgNC4yMiAwIDAgMSAxLjMzOC0uMTk1Yy42NyAwIDEuMjY5LjExIDEuNzk3LjMzMi41MzMuMjE1Ljk4Ni41MyAxLjM1Ny45NDcuMzcxLjQxLjY1NC45MTEuODUgMS41MDQuMTk1LjU4Ni4yOTMgMS4yNDYuMjkzIDEuOTgyYTUuNjkgNS42OSAwIDAgMS0uMjkzIDEuODQ2IDQuMzA2IDQuMzA2IDAgMCAxLS44NiAxLjUxNCAzLjk1IDMuOTUgMCAwIDEtMS40NTUgMS4wMjVjLS41NzkuMjQtMS4yNjYuMzYxLTIuMDYuMzYxYTUuNzQ0IDUuNzQ0IDAgMCAxLTEuNzA5LS4yNTQgNC42NDIgNC42NDIgMCAwIDEtMS40NTUtLjc3MSAzLjkzOCAzLjkzOCAwIDAgMS0xLjAzNi0xLjI3Yy0uMjYtLjUwNy0uNDEtMS4wOS0uNDQ5LTEuNzQ4aDIuMzA1Yy4wNTkuNDYzLjE4NS44NTYuMzgxIDEuMTgyLjIwMi4zMTkuNDY5LjU2My44MDEuNzMyLjMzMi4xNy43MTYuMjU0IDEuMTUyLjI1NC4zOTcgMCAuNzM5LS4wNjggMS4wMjUtLjIwNS4yODctLjE0My41MjQtLjM0NS43MTMtLjYwNWEyLjcgMi43IDAgMCAwIC40My0uOTM4IDQuNTEgNC41MSAwIDAgMCAuMTQ2LTEuMTkxYzAtLjQxNy0uMDU1LS43OTgtLjE2Ni0xLjE0M2EyLjQ3OCAyLjQ3OCAwIDAgMC0uNDc4LS44OTggMi4wNTMgMi4wNTMgMCAwIDAtLjc5MS0uNTg2IDIuNjcyIDIuNjcyIDAgMCAwLTEuMTA0LS4yMTVjLS41NiAwLS45ODkuMDgxLTEuMjg5LjI0NGE0LjA0MyA0LjA0MyAwIDAgMC0uODIuNjA2Wm0xOC4yNjQtMS42OXYyLjMyNGMwIDEuMTE0LS4xMTEgMi4wNjQtLjMzMiAyLjg1Mi0uMjE1Ljc4MS0uNTI3IDEuNDE2LS45MzggMS45MDQtLjQxLjQ4OS0uOTAxLjg0Ny0xLjQ3NCAxLjA3NS0uNTY3LjIyNy0xLjIwMS4zNDEtMS45MDQuMzQxLS41NiAwLTEuMDgxLS4wNzEtMS41NjMtLjIxNGEzLjgyIDMuODIgMCAwIDEtMS4yODktLjY3NCA0LjAyMiA0LjAyMiAwIDAgMS0uOTg2LTEuMTgyYy0uMjY3LS40ODgtLjQ3Ni0xLjA3MS0uNjI1LTEuNzQ4LS4xNDQtLjY3Ny0uMjE1LTEuNDYyLS4yMTUtMi4zNTRWNzguN2MwLTEuMTIuMTEtMi4wNjQuMzMyLTIuODMyLjIyMS0uNzc1LjUzNy0xLjQwMy45NDctMS44ODVhMy42MSAzLjYxIDAgMCAxIDEuNDY1LTEuMDY0Yy41NzMtLjIyMSAxLjIxMS0uMzMyIDEuOTE0LS4zMzIuNTY2IDAgMS4wODcuMDcyIDEuNTYzLjIxNS40ODEuMTM2LjkxMS4zNTUgMS4yODkuNjU0LjM4NC4zLjcwOS42OS45NzYgMS4xNzIuMjc0LjQ3NS40ODIgMS4wNTEuNjI1IDEuNzI5LjE0My42Ny4yMTUgMS40NTEuMjE1IDIuMzQzWm0tMi4zNTQgMi42NTd2LTMuMDA4YzAtLjU2Ny0uMDMyLTEuMDY1LS4wOTctMS40OTVhNC40MzEgNC40MzEgMCAwIDAtLjI5My0xLjEwMyAyLjIwMSAyLjIwMSAwIDAgMC0uNDY5LS43NDIgMS42NTYgMS42NTYgMCAwIDAtLjY0NC0uNDIgMi4yOCAyLjI4IDAgMCAwLS44MTEtLjEzN2MtLjM2NSAwLS42OS4wNzItLjk3Ny4yMTVhMS43NTUgMS43NTUgMCAwIDAtLjcyMi42NjRjLS4xOTYuMzA2LS4zNDUuNzEtLjQ0OSAxLjIxMS0uMDk4LjQ5NS0uMTQ3IDEuMDk3LS4xNDcgMS44MDd2My4wMDhjMCAuNTcyLjAzMyAxLjA3Ny4wOTggMS41MTMuMDY1LjQzNi4xNjMuODEuMjkzIDEuMTIzLjEzLjMwNi4yODYuNTYuNDY4Ljc2Mi4xODkuMTk1LjQwNC4zMzguNjQ1LjQzLjI0Ny4wOS41MTguMTM2LjgxMS4xMzYuMzcxIDAgLjY5OS0uMDcxLjk4Ni0uMjE0LjI4Ni0uMTQ0LjUyNy0uMzcyLjcyMy0uNjg0LjE5NS0uMzIuMzQxLS43MzMuNDM5LTEuMjQuMDk4LS41MDguMTQ2LTEuMTE3LjE0Ni0xLjgyNlptMTYuNTQ4LTQuOTIyaDIuMzYzVjg3aC0yLjE3N2wtLjE4Ni0yLjQ0MXYtOC4xMjVabS4zNDIgNS42NTQuOTI4LS4wMmMwIC43NDMtLjA3MiAxLjQyNi0uMjE1IDIuMDUxYTUuMDMzIDUuMDMzIDAgMCAxLS42NDUgMS42MzFjLS4yODYuNDYyLS42NTEuODItMS4wOTMgMS4wNzQtLjQ0My4yNTQtLjk2Ny4zODEtMS41NzMuMzgxYTMuODYgMy44NiAwIDAgMS0xLjI5OS0uMjA1IDIuNTggMi41OCAwIDAgMS0uOTk2LS42NjRjLS4yOC0uMy0uNTExLS42OS0uNjkzLTEuMTcybC0uMzAzLTIuNjY2aDEuMzM4YzAgLjU4Ni4wNDkgMS4wNjQuMTQ3IDEuNDM2LjA5Ny4zNy4yMzQuNjU3LjQxLjg1OS4xNzYuMTk1LjM4MS4zMzIuNjE1LjQxLjI0MS4wNzguNTAxLjExNy43ODEuMTE3LjUxNSAwIC45MzgtLjA3OCAxLjI3LS4yMzRhMS45MiAxLjkyIDAgMCAwIC43OTEtLjY3NCAyLjgzIDIuODMgMCAwIDAgLjQxLTEuMDI1Yy4wODUtLjM5Ny4xMjctLjgzLjEyNy0xLjNabS02Ljg5NS01LjY1NGgyLjM0NHYxNC42MjloLTIuMzQ0di0xNC42M1ptMTguNTM4IDBoMi4xMzl2MTAuMjczYzAgLjk1LS4yMDIgMS43NTgtLjYwNiAyLjQyMmEzLjg5NiAzLjg5NiAwIDAgMS0xLjY4OSAxLjUxNGMtLjcyMy4zNTEtMS41Ni41MjctMi41MS41MjctLjQwNCAwLS44NTMtLjA1OS0xLjM0OC0uMTc2YTUuNTMzIDUuNTMzIDAgMCAxLTEuNDI2LS41NjYgMy41NDEgMy41NDEgMCAwIDEtMS4xNDItMS4wMDZsMS4xMDMtMS4zODdjLjM3OC40NS43OTUuNzc4IDEuMjUuOTg3LjQ1Ni4yMDguOTM1LjMxMiAxLjQzNi4zMTIuNTQgMCAuOTk5LS4xIDEuMzc3LS4zMDNhMi4wNSAyLjA1IDAgMCAwIC44ODktLjg2OWMuMjA4LS4zODQuMzEyLS44NTMuMzEyLTEuNDA2di03LjkzbC4yMTUtMi4zOTJabS03LjE3OCA1LjR2LS4yMDVjMC0uOC4wOTgtMS41My4yOTMtMi4xODguMTk1LS42NjQuNDc1LTEuMjMzLjg0LTEuNzA5LjM2NS0uNDgxLjgwNy0uODUgMS4zMjgtMS4xMDMuNTIxLS4yNiAxLjExLS4zOSAxLjc2OC0uMzkuNjgzIDAgMS4yNjYuMTIzIDEuNzQ4LjM3LjQ4OC4yNDguODk1LjYwMyAxLjIyIDEuMDY1LjMyNi40NTYuNTggMS4wMDIuNzYyIDEuNjQuMTg5LjYzMi4zMjkgMS4zMzUuNDIgMi4xMXYuNjU0YTEwLjU4IDEwLjU4IDAgMCAxLS40MyAyLjA3IDUuNjkgNS42OSAwIDAgMS0uOCAxLjYyMSAzLjQyIDMuNDIgMCAwIDEtMS4yMzEgMS4wNTVjLS40ODIuMjQ4LTEuMDUxLjM3MS0xLjcwOS4zNzFhMy43NzggMy43NzggMCAwIDEtMS43NDgtLjQgMy45OCAzLjk4IDAgMCAxLTEuMzI4LTEuMTIzIDUuMzA1IDUuMzA1IDAgMCAxLS44NC0xLjcgNy40OTMgNy40OTMgMCAwIDEtLjI5My0yLjEzOFptMi4zNTQtLjIwNXYuMjA1YzAgLjQ4Mi4wNDUuOTMxLjEzNiAxLjM0OC4wOTguNDE2LjI0NS43ODQuNDQgMS4xMDMuMjAyLjMxMy40NTYuNTYuNzYyLjc0Mi4zMTIuMTc2LjY4LjI2NCAxLjEwMy4yNjQuNTUzIDAgMS4wMDYtLjExNyAxLjM1OC0uMzUyLjM1OC0uMjM0LjYzMS0uNTUuODItLjk0N2E0LjggNC44IDAgMCAwIC40MS0xLjM0OHYtMS43NjdhNC4zIDQuMyAwIDAgMC0uMjQ0LTEuMDc0IDIuODY3IDIuODY3IDAgMCAwLS40NzktLjg3IDEuOTk2IDEuOTk2IDAgMCAwLS43NjEtLjU4NWMtLjMwNi0uMTQ0LS42NjgtLjIxNS0xLjA4NC0uMjE1LS40MjQgMC0uNzkxLjA5MS0xLjEwNC4yNzMtLjMxMi4xODItLjU3LjQzMy0uNzcxLjc1MmEzLjcxIDMuNzEgMCAwIDAtLjQ0IDEuMTE0IDYuMDY0IDYuMDY0IDAgMCAwLS4xNDYgMS4zNTdabTE1LjY4Ni04Ljg0OC01LjUzOCAxNS40NGgtMS44NDVsNS41NDctMTUuNDRoMS44MzZabTQuMDg0IDUuODAxVjg3aC0yLjM1NFY3Ni40MzRoMi4yMTdsLjEzNyAyLjE0OFptLS4zODEgMi43NDQtLjgwMS0uMDFhNy4xIDcuMSAwIDAgMSAuMjc0LTIuMDIxIDQuODQxIDQuODQxIDAgMCAxIC44MDEtMS42MTEgMy41MTIgMy41MTIgMCAwIDEgMS4zMDgtMS4wNjVjLjUyNy0uMjU0IDEuMTM2LS4zOCAxLjgyNi0uMzguNDgyIDAgLjkyMi4wNyAxLjMxOS4yMTQuNDAzLjEzNy43NTIuMzU1IDEuMDQ1LjY1NC4yOTkuMy41MjcuNjg0LjY4MyAxLjE1My4xNjMuNDY4LjI0NCAxLjAzNS4yNDQgMS42OTlWODdoLTIuMzUzdi02LjgzNmMwLS41MTQtLjA3OC0uOTE4LS4yMzUtMS4yMWExLjI5NCAxLjI5NCAwIDAgMC0uNjU0LS42MjZjLS4yOC0uMTMtLjYxNS0uMTk1LTEuMDA2LS4xOTUtLjQ0MiAwLS44Mi4wODQtMS4xMzMuMjU0LS4zMDYuMTY5LS41NTYuNC0uNzUxLjY5M2EzLjA4OCAzLjA4OCAwIDAgMC0uNDMgMS4wMTYgNS4zMjYgNS4zMjYgMCAwIDAtLjEzNyAxLjIzWm02LjU1My0uNjI1LTEuMTA0LjI0NGMwLS42MzguMDg4LTEuMjQuMjY0LTEuODA2YTQuNiA0LjYgMCAwIDEgLjc5MS0xLjUwNCAzLjcyIDMuNzIgMCAwIDEgMS4yOTktMS4wMjZjLjUxNC0uMjQ3IDEuMTAzLS4zNyAxLjc2Ny0uMzdhNC4zMiA0LjMyIDAgMCAxIDEuNDQ2LjIyNGMuNDI5LjE0My43OTQuMzcxIDEuMDkzLjY4NC4zLjMxMi41MjguNzE5LjY4NCAxLjIyLjE1Ni40OTUuMjM0IDEuMDk0LjIzNCAxLjc5N1Y4N2gtMi4zNjN2LTYuODQ2YzAtLjUzNC0uMDc4LS45NDctLjIzNC0xLjI0YTEuMjE3IDEuMjE3IDAgMCAwLS42NDUtLjYwNWMtLjI4LS4xMTgtLjYxNS0uMTc2LTEuMDA2LS4xNzYtLjM2NCAwLS42ODYuMDY4LS45NjYuMjA1LS4yNzQuMTMtLjUwNS4zMTYtLjY5NC41NTZhMi40NjMgMi40NjMgMCAwIDAtLjQyOS44MTEgMy40NDYgMy40NDYgMCAwIDAtLjEzNy45OTZabTEwLjQyMi00Ljc1NmguODVjLjI4IDAgLjUxMS0uMDM5LjY5My0uMTE3YS45MzYuOTM2IDAgMCAwIC40MS0uMzQyLjg3Ny44NzcgMCAwIDAgLjE0Ny0uNTA4LjgyNy44MjcgMCAwIDAtLjI3NC0uNjE1Yy0uMTc1LS4xODItLjQ3Mi0uMjczLS44ODgtLjI3My0uMzE5IDAtLjU5LjA2OC0uODExLjIwNWEuNjE2LjYxNiAwIDAgMC0uMzIyLjU1N2gtMS43NzhjMC0uNDYzLjEzMS0uODU3LjM5MS0xLjE4MmEyLjQzNSAyLjQzNSAwIDAgMSAxLjA0NS0uNzQyYy40MzYtLjE3LjkxMS0uMjU0IDEuNDI2LS4yNTQuNTkyIDAgMS4xMTMuMDg0IDEuNTYyLjI1NC40NS4xNjMuNzk4LjQwNyAxLjA0NS43MzIuMjU0LjMyNi4zODEuNzMuMzgxIDEuMjExIDAgLjM3MS0uMTE3LjctLjM1MS45ODYtLjIyOC4yODctLjU0Ny41MTEtLjk1OC42NzQtLjQwMy4xNTYtLjg3OC4yMzUtMS40MjUuMjM1aC0xLjE0M3YtLjgyWm0wIDEuMjIxdi0uOGgxLjE0M2MuNjA1IDAgMS4xMjMuMDcgMS41NTIuMjE0LjQzLjEzNy43NTYuMzQ4Ljk3Ny42MzUuMjI4LjI4Ni4zNDIuNjU3LjM0MiAxLjExMyAwIC40ODItLjEzNy44OTItLjQxIDEuMjMtLjI3NC4zNC0uNjQ4LjU5Ni0xLjEyMy43NzJhNC40ODQgNC40ODQgMCAwIDEtMS41ODIuMjY0Yy0uNDg5IDAtLjk2NC0uMDc4LTEuNDI2LS4yMzVhMi42NDYgMi42NDYgMCAwIDEtMS4xMzMtLjc2MWMtLjI5My0uMzQ1LS40MzktLjc5OC0uNDM5LTEuMzU4aDEuNzg3YzAgLjI1NC4xMTcuNDcyLjM1MS42NTQuMjM1LjE4My41NS4yNzQuOTQ3LjI3NC40MjQgMCAuNzM2LS4wOTEuOTM4LS4yNzRhLjkxMS45MTEgMCAwIDAgLjMxMi0uNzAzYzAtLjI0Ny0uMDU4LS40NDYtLjE3NS0uNTk1YS45MTUuOTE1IDAgMCAwLS40NzktLjMyMyAyLjI1MSAyLjI1MSAwIDAgMC0uNzMyLS4xMDdoLS44NVoiLz48L2c+PGRlZnM+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjE5OCIgaGVpZ2h0PSI1NCIgeD0iMSIgeT0iNTMiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjMiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA3MV8zNzA0Ii8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMDcxXzM3MDQiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48L2RlZnM+PC9zdmc+", + "description": "Produced by volcanoes and industrial processes, particularly the burning of coal and oil.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'sulfur', label: 'Sulfur dioxide', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sulfur dioxide\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 600) {\\n\\tvalue = 600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"public\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#3FA71A\"},{\"from\":100,\"to\":200,\"color\":\"#80C32C\"},{\"from\":200,\"to\":350,\"color\":\"#FFA600\"},{\"from\":350,\"to\":500,\"color\":\"#F36900\"},{\"from\":500,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#3FA71A\"},{\"from\":100,\"to\":200,\"color\":\"#80C32C\"},{\"from\":200,\"to\":350,\"color\":\"#FFA600\"},{\"from\":350,\"to\":500,\"color\":\"#F36900\"},{\"from\":500,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Sulfur dioxide\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "nitrogen", + "sulfur", + "sulfur dioxide" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_sulfur_dioxide__so2__card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_sulfur_dioxide__so2__card_with_background.json new file mode 100644 index 0000000000..9c600b66ce --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_sulfur_dioxide__so2__card_with_background.json @@ -0,0 +1,38 @@ +{ + "fqn": "horizontal_sulfur_dioxide_so2_card_with_background", + "name": "Horizontal sulfur dioxide (SO2) card with background", + "deprecated": false, + "image": "tb-image:U08yLWhvcml6b250YWwtdmFsdWUtY2FyZC13aXRoLWJhY2tncm91bmQuc3Zn:U08yLWhvcml6b250YWwtdmFsdWUtY2FyZC13aXRoLWJhY2tncm91bmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSI1NiIgeT0iNTIiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjQ0IiB4PSI2IiB5PSI1OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuNzYiIHJ4PSI0Ii8+PHBhdGggZmlsbD0iIzNCOTExQyIgZD0iTTIyIDcwYy01LjUyIDAtMTAgNC40OC0xMCAxMHM0LjQ4IDEwIDEwIDEwIDEwLTQuNDggMTAtMTAtNC40OC0xMC0xMC0xMFptLTggMTBjMC0uNjEuMDgtMS4yMS4yMS0xLjc4TDE4Ljk5IDgzdjFjMCAxLjEuOSAyIDIgMnYxLjkzQzE3LjA2IDg3LjQzIDE0IDg0LjA3IDE0IDgwWm0xMy44OSA1LjRjLS4yNi0uODEtMS0xLjQtMS45LTEuNGgtMXYtM2MwLS41NS0uNDUtMS0xLTFoLTZ2LTJoMmMuNTUgMCAxLS40NSAxLTF2LTJoMmMxLjEgMCAyLS45IDItMnYtLjQxQzI3LjkyIDczLjc3IDMwIDc2LjY1IDMwIDgwYzAgMi4wOC0uODEgMy45OC0yLjExIDUuNFoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii44NyIgZD0iTTQ2LjI0OCA3NC40MzZjMC0uMjA1LS4wMzItLjM4Ny0uMDk2LS41NDZhMS4wNjggMS4wNjggMCAwIDAtLjMyLS40MzggMi42NTUgMi42NTUgMCAwIDAtLjY1Ny0uMzgzIDguNzA4IDguNzA4IDAgMCAwLTEuMDY3LS4zOSAxMS43OTYgMTEuNzk2IDAgMCAxLTEuMzEyLS40ODUgNC45OTYgNC45OTYgMCAwIDEtMS4wNC0uNjI5IDIuNjkgMi42OSAwIDAgMS0uNjktLjgyNyAyLjMxNiAyLjMxNiAwIDAgMS0uMjQ2LTEuMDg3YzAtLjQwNS4wODUtLjc3NC4yNTMtMS4xMDcuMTczLS4zMzMuNDE3LS42Mi43MzItLjg2MWEzLjUgMy41IDAgMCAxIDEuMTI4LS41NjggNC43NTEgNC43NTEgMCAwIDEgMS40MzUtLjIwNWMuNzM4IDAgMS4zNzQuMTM3IDEuOTA3LjQxLjUzOC4yNzQuOTUuNjQgMS4yMzggMS4xLjI5MS40NjEuNDM3Ljk3LjQzNyAxLjUyNWgtMS43MDJjMC0uMzI4LS4wNy0uNjE3LS4yMTItLjg2OGExLjQzNiAxLjQzNiAwIDAgMC0uNjI5LS42MDFjLS4yNzgtLjE0Ni0uNjMxLS4yMi0xLjA2LS4yMi0uNDA1IDAtLjc0Mi4wNjItMS4wMTEuMTg1YTEuMzUgMS4zNSAwIDAgMC0uNjAyLjVjLS4xMzIuMjEtLjE5OC40NDYtLjE5OC43MSAwIC4xODcuMDQzLjM1OC4xMy41MTMuMDg3LjE1LjIxOS4yOTIuMzk3LjQyNC4xNzcuMTI4LjQuMjQ4LjY3LjM2Mi4yNjguMTE0LjU4NS4yMjQuOTUuMzI4YTkuNDE2IDkuNDE2IDAgMCAxIDEuNDQyLjU0N2MuNDEuMTk2Ljc1Mi40MiAxLjAyNS42Ny4yNzQuMjUuNDc5LjUzNi42MTYuODU1LjEzNi4zMTQuMjA1LjY3Mi4yMDUgMS4wNzMgMCAuNDItLjA4NS43OTctLjI1MyAxLjEzNWEyLjQ1IDIuNDUgMCAwIDEtLjcyNS44NTRjLS4zMS4yMzItLjY4My40MTItMS4xMi41NGE1LjI5NCA1LjI5NCAwIDAgMS0xLjQ1LjE4NWMtLjQ3OSAwLS45NS0uMDY0LTEuNDE1LS4xOTJhNC4xNzUgNC4xNzUgMCAwIDEtMS4yNTgtLjU4IDMuMDAzIDMuMDAzIDAgMCAxLS45MDItLjk4NWMtLjIyNC0uMzk3LS4zMzUtLjg2LS4zMzUtMS4zODhoMS43MTVjMCAuMzI0LjA1NS42LjE2NS44MjcuMTE0LjIyOC4yNy40MTUuNDcxLjU2LjIuMTQyLjQzMy4yNDcuNjk4LjMxNS4yNjguMDY5LjU1Ni4xMDMuODYuMTAzLjQwMiAwIC43MzctLjA1NyAxLjAwNi0uMTcxLjI3My0uMTE0LjQ3OC0uMjczLjYxNS0uNDc5LjEzNy0uMjA1LjIwNS0uNDQyLjIwNS0uNzFabTcuNzc2Ljgydi01LjY1M2gxLjY1NVY3N0g1NC4xMmwtLjA5Ni0xLjc0M1ptLjIzMy0xLjUzNy41NTMtLjAxNGMwIC40OTctLjA1NC45NTUtLjE2MyAxLjM3NC0uMTEuNDE1LS4yNzkuNzc3LS41MDYgMS4wODctLjIyOC4zMDUtLjUyLjU0NS0uODc1LjcxOC0uMzU2LjE2OC0uNzgyLjI1My0xLjI3OS4yNTMtLjM2IDAtLjY5LS4wNTMtLjk5LS4xNTdhMi4wMyAyLjAzIDAgMCAxLS43OC0uNDg2IDIuMjA4IDIuMjA4IDAgMCAxLS41LS44NTRjLS4xMTgtLjM1MS0uMTc3LS43Ny0uMTc3LTEuMjU4di00Ljc3OWgxLjY0OHY0Ljc5M2MwIC4yNjguMDMxLjQ5NC4wOTUuNjc2LjA2NC4xNzguMTUuMzIyLjI2LjQzLjExLjExLjIzNy4xODguMzgzLjIzMy4xNDYuMDQ2LjMuMDY5LjQ2NS4wNjkuNDY5IDAgLjgzOC0uMDkxIDEuMTA3LS4yNzQuMjc0LS4xODcuNDY3LS40MzcuNTgxLS43NTIuMTE5LS4zMTQuMTc4LS42NjcuMTc4LTEuMDZaTTU5LjM4IDY2LjVWNzdoLTEuNjU0VjY2LjVoMS42NTRaTTYzLjY2NCA3N2gtMS42NDd2LTguMTA3YzAtLjU1Mi4xMDItMS4wMTQuMzA3LTEuMzg4YTIuMDMgMi4wMyAwIDAgMSAuODk2LS44NTVjLjM4Ny0uMTk2Ljg0NS0uMjk0IDEuMzc0LS4yOTQuMTY0IDAgLjMyNi4wMTIuNDg1LjAzNS4xNi4wMTguMzE1LjA0Ny40NjUuMDg4bC0uMDQxIDEuMjcyYTEuODg1IDEuODg1IDAgMCAwLS4zLS4wNDggMy45NjcgMy45NjcgMCAwIDAtLjM0My0uMDE0Yy0uMjUgMC0uNDY3LjA0OC0uNjUuMTQ0YS45NDguOTQ4IDAgMCAwLS40MS40MDMgMS40MjcgMS40MjcgMCAwIDAtLjEzNi42NTdWNzdabTEuNTI1LTcuMzk3djEuMjA0aC00LjMwN3YtMS4yMDRoNC4zMDdabTUuODk2IDUuNjU0di01LjY1NGgxLjY1NFY3N2gtMS41NThsLS4wOTYtMS43NDNabS4yMzItMS41MzguNTU0LS4wMTRjMCAuNDk3LS4wNTUuOTU1LS4xNjQgMS4zNzQtLjExLjQxNS0uMjc4Ljc3Ny0uNTA2IDEuMDg3LS4yMjguMzA1LS41Mi41NDUtLjg3NS43MTgtLjM1NS4xNjgtLjc4MS4yNTMtMS4yNzguMjUzLS4zNiAwLS42OS0uMDUzLS45OTEtLjE1Ny0uMzAxLS4xMDUtLjU2LS4yNjctLjc4LS40ODZhMi4yMSAyLjIxIDAgMCAxLS40OTktLjg1NGMtLjExOC0uMzUxLS4xNzctLjc3LS4xNzctMS4yNTh2LTQuNzc5aDEuNjQ3djQuNzkzYzAgLjI2OC4wMzIuNDk0LjA5Ni42NzYuMDY0LjE3OC4xNS4zMjIuMjYuNDMuMTA5LjExLjIzNy4xODguMzgyLjIzMy4xNDYuMDQ2LjMwMS4wNjkuNDY1LjA2OS40NyAwIC44MzktLjA5MSAxLjEwOC0uMjc0LjI3My0uMTg3LjQ2Ny0uNDM3LjU4LS43NTIuMTItLjMxNC4xNzgtLjY2Ny4xNzgtMS4wNlptNS4wMTUtMi43MDdWNzdoLTEuNjQ3di03LjM5N2gxLjU3MmwuMDc1IDEuNDA5Wm0yLjI2My0xLjQ1Ni0uMDE0IDEuNTNhNC4yOSA0LjI5IDAgMCAwLS42Ny0uMDU1Yy0uMjgyLjAwMS0uNTMuMDQyLS43NDUuMTI0YTEuNDAzIDEuNDAzIDAgMCAwLS41NC4zNDJjLS4xNDEuMTQ2LS4yNS4zMjQtLjMyOC41MzMtLjA3OC4yMS0uMTIzLjQ0NS0uMTM3LjcwNGwtLjM3Ni4wMjhjMC0uNDY1LjA0Ni0uODk2LjEzNy0xLjI5Mi4wOTEtLjM5Ny4yMjgtLjc0NS40MS0xLjA0Ni4xODctLjMwMS40Mi0uNTM2LjY5Ny0uNzA0YTEuODcgMS44NyAwIDAgMSAuOTc4LS4yNTNjLjEgMCAuMjA3LjAwOS4zMjEuMDI3LjExOS4wMTguMjA3LjAzOS4yNjcuMDYyWk00NS4zNTkgOTEuNDY5VjgyLjVoMS42NTVWOTNoLTEuNDk3bC0uMTU4LTEuNTMxWm0tNC44MTItMi4wODV2LS4xNDRjMC0uNTYuMDY2LTEuMDcuMTk4LTEuNTMxLjEzMi0uNDY1LjMyNC0uODY0LjU3NC0xLjE5Ni4yNTEtLjMzOC41NTYtLjU5NS45MTYtLjc3My4zNi0uMTgyLjc2Ni0uMjczIDEuMjE3LS4yNzMuNDQ3IDAgLjgzOS4wODYgMS4xNzYuMjYuMzM3LjE3My42MjQuNDIxLjg2MS43NDUuMjM3LjMxOS40MjYuNzAyLjU2OCAxLjE0OC4xNC40NDIuMjQxLjkzNC4zIDEuNDc3di40NThhNy4zOTIgNy4zOTIgMCAwIDEtLjMgMS40NDljLS4xNDIuNDM3LS4zMy44MTYtLjU2OCAxLjEzNWEyLjQ1MyAyLjQ1MyAwIDAgMS0uODY4LjczOGMtLjMzNy4xNzMtLjczMS4yNi0xLjE4My4yNi0uNDQ2IDAtLjg1LS4wOTQtMS4yMS0uMjhhMi42NzQgMi42NzQgMCAwIDEtLjkwOS0uNzg3IDMuNzAzIDMuNzAzIDAgMCAxLS41NzQtMS4xOSA1LjQwOSA1LjQwOSAwIDAgMS0uMTk4LTEuNDk2Wm0xLjY0Ny0uMTQ0di4xNDRjMCAuMzM3LjAzLjY1MS4wOS45NDMuMDYzLjI5Mi4xNjEuNTUuMjkzLjc3My4xMzIuMjE4LjMwMy4zOTIuNTEzLjUyLjIxNC4xMjIuNDcuMTg0Ljc2NS4xODQuMzc0IDAgLjY4Mi0uMDgyLjkyMy0uMjQ2LjI0Mi0uMTY0LjQzMS0uMzg2LjU2OC0uNjY0YTMuMDggMy4wOCAwIDAgMCAuMjg3LS45NDN2LTEuMjM3YTMuMDE4IDMuMDE4IDAgMCAwLS4xNzEtLjc1MiAyLjAxMyAyLjAxMyAwIDAgMC0uMzM1LS42MDkgMS40MTIgMS40MTIgMCAwIDAtLjUyNi0uNDEgMS42NSAxLjY1IDAgMCAwLS43MzItLjE1Yy0uMyAwLS41NTYuMDY0LS43NjYuMTkxLS4yMS4xMjgtLjM4Mi4zMDMtLjUxOS41MjdhMi41OSAyLjU5IDAgMCAwLS4yOTQuNzc5IDQuNTA4IDQuNTA4IDAgMCAwLS4wOTYuOTVabTguNTU2LTMuNjM2VjkzaC0xLjY1NHYtNy4zOTdoMS42NTRabS0xLjc2NC0xLjk0MmMwLS4yNS4wODItLjQ1OC4yNDYtLjYyMi4xNjktLjE2OS40MDEtLjI1My42OTgtLjI1My4yOTEgMCAuNTIyLjA4NC42OS4yNTNhLjgzMS44MzEgMCAwIDEgLjI1My42MjIuODIzLjgyMyAwIDAgMS0uMjUzLjYxNWMtLjE2OC4xNjQtLjM5OS4yNDYtLjY5LjI0Ni0uMjk3IDAtLjUyOS0uMDgyLS42OTgtLjI0NmEuODM1LjgzNSAwIDAgMS0uMjQ2LS42MTVabTMuNTA0IDUuNzIydi0uMTU3YzAtLjUzNC4wNzgtMS4wMjguMjMzLTEuNDg0LjE1NS0uNDYuMzc4LS44NTkuNjctMS4xOTZhMy4wMjMgMy4wMjMgMCAwIDEgMS4wOC0uNzkzIDMuNTE1IDMuNTE1IDAgMCAxIDEuNDQ5LS4yODdjLjU0MiAwIDEuMDI1LjA5NiAxLjQ1LjI4Ny40MjguMTg3Ljc5LjQ1MSAxLjA4Ni43OTMuMjk2LjMzNy41MjIuNzM2LjY3NyAxLjE5Ni4xNTUuNDU2LjIzMi45NS4yMzIgMS40ODR2LjE1N2MwIC41MzMtLjA3NyAxLjAyOC0uMjMyIDEuNDgzLS4xNTUuNDU2LS4zOC44NTUtLjY3NyAxLjE5N2EzLjEzNSAzLjEzNSAwIDAgMS0xLjA4Ljc5MiAzLjU0IDMuNTQgMCAwIDEtMS40NDMuMjggMy42MDcgMy42MDcgMCAwIDEtMS40NTYtLjI4IDMuMTM2IDMuMTM2IDAgMCAxLTEuMDgtLjc5MiAzLjU1IDMuNTUgMCAwIDEtLjY3Ni0xLjE5NyA0LjU3OSA0LjU3OSAwIDAgMS0uMjMzLTEuNDgzWm0xLjY0OC0uMTU3di4xNTdjMCAuMzMyLjAzNC42NDcuMTAyLjk0M2EyLjQgMi40IDAgMCAwIC4zMjIuNzhjLjE0NS4yMjMuMzMyLjM5OC41Ni41MjYuMjI4LjEyNy41LjE5MS44MTMuMTkxLjMwNiAwIC41Ny0uMDY0Ljc5My0uMTkxLjIyOC0uMTI4LjQxNS0uMzAzLjU2MS0uNTI3YTIuNCAyLjQgMCAwIDAgLjMyMS0uNzc5Yy4wNzMtLjI5Ni4xMS0uNjEuMTEtLjk0M3YtLjE1N2MwLS4zMjktLjAzNy0uNjM5LS4xMS0uOTNhMi4zOSAyLjM5IDAgMCAwLS4zMjgtLjc4NiAxLjU3IDEuNTcgMCAwIDAtLjU2LS41MzMgMS41NDMgMS41NDMgMCAwIDAtLjgtLjE5OWMtLjMxIDAtLjU3OS4wNjYtLjgwNy4xOTlhMS41ODggMS41ODggMCAwIDAtLjU1My41MzNjLS4xNDYuMjI4LS4yNTMuNDktLjMyMi43ODZhNC4wNjcgNC4wNjcgMCAwIDAtLjEwMi45M1ptNy45NjgtMy42MjQgMS40MDggMi40NTUgMS40MzUtMi40NTVoMS44MTJsLTIuMjQyIDMuNjNMNjYuODQ5IDkzaC0xLjgxbC0xLjUwNS0yLjU1N0w2Mi4wMyA5M2gtMS44MThsMi4zMjQtMy43NjctMi4yMzUtMy42M2gxLjgwNFptNy43OTYgMFY5M2gtMS42NTR2LTcuMzk3aDEuNjU0Wm0tMS43NjMtMS45NGMwLS4yNTEuMDgyLS40NTkuMjQ2LS42MjMuMTY4LS4xNjkuNC0uMjUzLjY5Ny0uMjUzLjI5MiAwIC41MjIuMDg0LjY5LjI1M2EuODMxLjgzMSAwIDAgMSAuMjUzLjYyMi44MjMuODIzIDAgMCAxLS4yNTMuNjE1Yy0uMTY4LjE2NC0uMzk4LjI0Ni0uNjkuMjQ2LS4yOTYgMC0uNTI5LS4wODItLjY5Ny0uMjQ2YS44MzQuODM0IDAgMCAxLS4yNDYtLjYxNVptOC4zMyA3LjgwNlY4Mi41aDEuNjU0VjkzaC0xLjQ5N2wtLjE1Ny0xLjUzMVptLTQuODEzLTIuMDg1di0uMTQ0YzAtLjU2LjA2Ni0xLjA3LjE5OC0xLjUzMS4xMzMtLjQ2NS4zMjQtLjg2NC41NzUtMS4xOTYuMjUtLjMzOC41NTYtLjU5NS45MTYtLjc3My4zNi0uMTgyLjc2NS0uMjczIDEuMjE2LS4yNzMuNDQ3IDAgLjg0LjA4NiAxLjE3Ni4yNi4zMzguMTczLjYyNS40MjEuODYyLjc0NS4yMzcuMzE5LjQyNi43MDIuNTY3IDEuMTQ4LjE0MS40NDIuMjQyLjkzNC4zIDEuNDc3di40NThhNy4zOTIgNy4zOTIgMCAwIDEtLjMgMS40NDljLS4xNDEuNDM3LS4zMy44MTYtLjU2NyAxLjEzNWEyLjQ1MSAyLjQ1MSAwIDAgMS0uODY4LjczOGMtLjMzOC4xNzMtLjczMi4yNi0xLjE4My4yNi0uNDQ3IDAtLjg1LS4wOTQtMS4yMS0uMjhhMi42NzQgMi42NzQgMCAwIDEtLjkxLS43ODcgMy43MDMgMy43MDMgMCAwIDEtLjU3My0xLjE5IDUuNDA1IDUuNDA1IDAgMCAxLS4xOTktMS40OTZabTEuNjQ4LS4xNDR2LjE0NGMwIC4zMzcuMDMuNjUxLjA4OS45NDMuMDYzLjI5Mi4xNjEuNTUuMjkzLjc3My4xMzMuMjE4LjMwNC4zOTIuNTEzLjUyLjIxNC4xMjIuNDcuMTg0Ljc2Ni4xODQuMzczIDAgLjY4MS0uMDgyLjkyMy0uMjQ2YTEuNjggMS42OCAwIDAgMCAuNTY3LS42NjQgMy4wOCAzLjA4IDAgMCAwIC4yODctLjk0M3YtMS4yMzdhMy4wMjMgMy4wMjMgMCAwIDAtLjE3LS43NTIgMi4wMTEgMi4wMTEgMCAwIDAtLjMzNi0uNjA5IDEuNDEgMS40MSAwIDAgMC0uNTI2LS40MSAxLjY1IDEuNjUgMCAwIDAtLjczMS0uMTVjLS4zMDEgMC0uNTU2LjA2NC0uNzY2LjE5MS0uMjEuMTI4LS4zODMuMzAzLS41Mi41MjdhMi41OSAyLjU5IDAgMCAwLS4yOTQuNzc5IDQuNTA4IDQuNTA4IDAgMCAwLS4wOTUuOTVabTEwLjA1MiAzLjg5N2EzLjk0NCAzLjk0NCAwIDAgMS0xLjQ4My0uMjY3IDMuMjczIDMuMjczIDAgMCAxLTEuMTIxLS43NTkgMy4zNjEgMy4zNjEgMCAwIDEtLjcwNC0xLjE0MSAzLjk5NyAzLjk5NyAwIDAgMS0uMjQ2LTEuNDE1di0uMjc0YzAtLjU3OS4wODQtMS4xMDMuMjUzLTEuNTcyLjE2OC0uNDcuNDAzLS44Ny43MDQtMS4yMDMuMy0uMzM3LjY1Ni0uNTk1IDEuMDY2LS43NzMuNDEtLjE3Ny44NTUtLjI2NiAxLjMzMy0uMjY2LjUyOSAwIC45OTEuMDg5IDEuMzg4LjI2Ni4zOTYuMTc4LjcyNC40MjkuOTg0Ljc1Mi4yNjUuMzIuNDYuNy41ODggMS4xNDIuMTMyLjQ0Mi4xOTguOTMuMTk4IDEuNDYzdi43MDRoLTUuNzE0Vjg4LjYxaDQuMDg3di0uMTNhMi4zMTQgMi4zMTQgMCAwIDAtLjE3Ny0uODM0IDEuNDE3IDEuNDE3IDAgMCAwLS40ODYtLjYyOGMtLjIxOC0uMTYtLjUxLS4yNC0uODc1LS4yNC0uMjczIDAtLjUxNy4wNi0uNzMxLjE3OC0uMjEuMTE0LS4zODUuMjgtLjUyNi41LS4xNDIuMjE4LS4yNTEuNDgyLS4zMjkuNzkyLS4wNzIuMzA1LS4xMDkuNjUtLjEwOSAxLjAzMnYuMjc0YzAgLjMyMy4wNDMuNjI0LjEzLjkwMi4wOTEuMjc0LjIyMy41MTMuMzk2LjcxOC4xNzQuMjA1LjM4My4zNjcuNjMuNDg1LjI0NS4xMTQuNTI2LjE3MS44NC4xNzEuMzk3IDAgLjc1LS4wOCAxLjA2LS4yNC4zMS0uMTU5LjU3OS0uMzg0LjgwNi0uNjc2bC44NjguODRjLS4xNTkuMjMzLS4zNjYuNDU3LS42MjIuNjctLjI1NS4yMS0uNTY3LjM4MS0uOTM2LjUxNGEzLjcyNyAzLjcyNyAwIDAgMS0xLjI3Mi4xOThaIi8+PHBhdGggZmlsbD0iIzNCOTExQyIgZD0ibTEwNC41NDUgODAuMzg5LTEuODc1LS40Ni43NzEtNy4xNDhoNy42NjZ2MS45OTJoLTUuNzEybC0uMzkxIDMuNDg3Yy4yMjEtLjEzLjUyNy0uMjU3LjkxOC0uMzgxYTQuMjIgNC4yMiAwIDAgMSAxLjMzOC0uMTk1Yy42NyAwIDEuMjY5LjExIDEuNzk3LjMzMi41MzMuMjE1Ljk4Ni41MyAxLjM1Ny45NDcuMzcxLjQxLjY1NC45MTEuODUgMS41MDQuMTk1LjU4Ni4yOTMgMS4yNDYuMjkzIDEuOTgyYTUuNjkgNS42OSAwIDAgMS0uMjkzIDEuODQ2IDQuMzA2IDQuMzA2IDAgMCAxLS44NiAxLjUxNCAzLjk1IDMuOTUgMCAwIDEtMS40NTUgMS4wMjVjLS41NzkuMjQtMS4yNjYuMzYxLTIuMDYuMzYxYTUuNzQ0IDUuNzQ0IDAgMCAxLTEuNzA5LS4yNTQgNC42NDIgNC42NDIgMCAwIDEtMS40NTUtLjc3MSAzLjkzOCAzLjkzOCAwIDAgMS0xLjAzNi0xLjI3Yy0uMjYtLjUwNy0uNDEtMS4wOS0uNDQ5LTEuNzQ4aDIuMzA1Yy4wNTkuNDYzLjE4NS44NTYuMzgxIDEuMTgyLjIwMi4zMTkuNDY5LjU2My44MDEuNzMyLjMzMi4xNy43MTYuMjU0IDEuMTUyLjI1NC4zOTcgMCAuNzM5LS4wNjggMS4wMjUtLjIwNS4yODctLjE0My41MjQtLjM0NS43MTMtLjYwNWEyLjcgMi43IDAgMCAwIC40My0uOTM4IDQuNTEgNC41MSAwIDAgMCAuMTQ2LTEuMTkxYzAtLjQxNy0uMDU1LS43OTgtLjE2Ni0xLjE0M2EyLjQ3OCAyLjQ3OCAwIDAgMC0uNDc4LS44OTggMi4wNTMgMi4wNTMgMCAwIDAtLjc5MS0uNTg2IDIuNjcyIDIuNjcyIDAgMCAwLTEuMTA0LS4yMTVjLS41NiAwLS45ODkuMDgxLTEuMjg5LjI0NGE0LjA0MyA0LjA0MyAwIDAgMC0uODIuNjA2Wm0xOC4yNjQtMS42OXYyLjMyNGMwIDEuMTE0LS4xMTEgMi4wNjQtLjMzMiAyLjg1Mi0uMjE1Ljc4MS0uNTI3IDEuNDE2LS45MzggMS45MDQtLjQxLjQ4OS0uOTAxLjg0Ny0xLjQ3NCAxLjA3NS0uNTY3LjIyNy0xLjIwMS4zNDEtMS45MDQuMzQxLS41NiAwLTEuMDgxLS4wNzEtMS41NjMtLjIxNGEzLjgyIDMuODIgMCAwIDEtMS4yODktLjY3NCA0LjAyMiA0LjAyMiAwIDAgMS0uOTg2LTEuMTgyYy0uMjY3LS40ODgtLjQ3Ni0xLjA3MS0uNjI1LTEuNzQ4LS4xNDQtLjY3Ny0uMjE1LTEuNDYyLS4yMTUtMi4zNTRWNzguN2MwLTEuMTIuMTEtMi4wNjQuMzMyLTIuODMyLjIyMS0uNzc1LjUzNy0xLjQwMy45NDctMS44ODVhMy42MSAzLjYxIDAgMCAxIDEuNDY1LTEuMDY0Yy41NzMtLjIyMSAxLjIxMS0uMzMyIDEuOTE0LS4zMzIuNTY2IDAgMS4wODcuMDcyIDEuNTYzLjIxNS40ODEuMTM2LjkxMS4zNTUgMS4yODkuNjU0LjM4NC4zLjcwOS42OS45NzYgMS4xNzIuMjc0LjQ3NS40ODIgMS4wNTEuNjI1IDEuNzI5LjE0My42Ny4yMTUgMS40NTEuMjE1IDIuMzQzWm0tMi4zNTQgMi42NTd2LTMuMDA4YzAtLjU2Ny0uMDMyLTEuMDY1LS4wOTctMS40OTVhNC40MzEgNC40MzEgMCAwIDAtLjI5My0xLjEwMyAyLjIwMSAyLjIwMSAwIDAgMC0uNDY5LS43NDIgMS42NTYgMS42NTYgMCAwIDAtLjY0NC0uNDIgMi4yOCAyLjI4IDAgMCAwLS44MTEtLjEzN2MtLjM2NSAwLS42OS4wNzItLjk3Ny4yMTVhMS43NTUgMS43NTUgMCAwIDAtLjcyMi42NjRjLS4xOTYuMzA2LS4zNDUuNzEtLjQ0OSAxLjIxMS0uMDk4LjQ5NS0uMTQ3IDEuMDk3LS4xNDcgMS44MDd2My4wMDhjMCAuNTcyLjAzMyAxLjA3Ny4wOTggMS41MTMuMDY1LjQzNi4xNjMuODEuMjkzIDEuMTIzLjEzLjMwNi4yODYuNTYuNDY4Ljc2Mi4xODkuMTk1LjQwNC4zMzguNjQ1LjQzLjI0Ny4wOS41MTguMTM2LjgxMS4xMzYuMzcxIDAgLjY5OS0uMDcxLjk4Ni0uMjE0LjI4Ni0uMTQ0LjUyNy0uMzcyLjcyMy0uNjg0LjE5NS0uMzIuMzQxLS43MzMuNDM5LTEuMjQuMDk4LS41MDguMTQ2LTEuMTE3LjE0Ni0xLjgyNlptMTYuNTQ4LTQuOTIyaDIuMzYzVjg3aC0yLjE3N2wtLjE4Ni0yLjQ0MXYtOC4xMjVabS4zNDIgNS42NTQuOTI4LS4wMmMwIC43NDMtLjA3MiAxLjQyNi0uMjE1IDIuMDUxYTUuMDMzIDUuMDMzIDAgMCAxLS42NDUgMS42MzFjLS4yODYuNDYyLS42NTEuODItMS4wOTMgMS4wNzQtLjQ0My4yNTQtLjk2Ny4zODEtMS41NzMuMzgxYTMuODYgMy44NiAwIDAgMS0xLjI5OS0uMjA1IDIuNTggMi41OCAwIDAgMS0uOTk2LS42NjRjLS4yOC0uMy0uNTExLS42OS0uNjkzLTEuMTcybC0uMzAzLTIuNjY2aDEuMzM4YzAgLjU4Ni4wNDkgMS4wNjQuMTQ3IDEuNDM2LjA5Ny4zNy4yMzQuNjU3LjQxLjg1OS4xNzYuMTk1LjM4MS4zMzIuNjE1LjQxLjI0MS4wNzguNTAxLjExNy43ODEuMTE3LjUxNSAwIC45MzgtLjA3OCAxLjI3LS4yMzRhMS45MiAxLjkyIDAgMCAwIC43OTEtLjY3NCAyLjgzIDIuODMgMCAwIDAgLjQxLTEuMDI1Yy4wODUtLjM5Ny4xMjctLjgzLjEyNy0xLjNabS02Ljg5NS01LjY1NGgyLjM0NHYxNC42MjloLTIuMzQ0di0xNC42M1ptMTguNTM4IDBoMi4xMzl2MTAuMjczYzAgLjk1LS4yMDIgMS43NTgtLjYwNiAyLjQyMmEzLjg5NiAzLjg5NiAwIDAgMS0xLjY4OSAxLjUxNGMtLjcyMy4zNTEtMS41Ni41MjctMi41MS41MjctLjQwNCAwLS44NTMtLjA1OS0xLjM0OC0uMTc2YTUuNTMzIDUuNTMzIDAgMCAxLTEuNDI2LS41NjYgMy41NDEgMy41NDEgMCAwIDEtMS4xNDItMS4wMDZsMS4xMDMtMS4zODdjLjM3OC40NS43OTUuNzc4IDEuMjUuOTg3LjQ1Ni4yMDguOTM1LjMxMiAxLjQzNi4zMTIuNTQgMCAuOTk5LS4xIDEuMzc3LS4zMDNhMi4wNSAyLjA1IDAgMCAwIC44ODktLjg2OWMuMjA4LS4zODQuMzEyLS44NTMuMzEyLTEuNDA2di03LjkzbC4yMTUtMi4zOTJabS03LjE3OCA1LjR2LS4yMDVjMC0uOC4wOTgtMS41My4yOTMtMi4xODguMTk1LS42NjQuNDc1LTEuMjMzLjg0LTEuNzA5LjM2NS0uNDgxLjgwNy0uODUgMS4zMjgtMS4xMDMuNTIxLS4yNiAxLjExLS4zOSAxLjc2OC0uMzkuNjgzIDAgMS4yNjYuMTIzIDEuNzQ4LjM3LjQ4OC4yNDguODk1LjYwMyAxLjIyIDEuMDY1LjMyNi40NTYuNTggMS4wMDIuNzYyIDEuNjQuMTg5LjYzMi4zMjkgMS4zMzUuNDIgMi4xMXYuNjU0YTEwLjU4IDEwLjU4IDAgMCAxLS40MyAyLjA3IDUuNjkgNS42OSAwIDAgMS0uOCAxLjYyMSAzLjQyIDMuNDIgMCAwIDEtMS4yMzEgMS4wNTVjLS40ODIuMjQ4LTEuMDUxLjM3MS0xLjcwOS4zNzFhMy43NzggMy43NzggMCAwIDEtMS43NDgtLjQgMy45OCAzLjk4IDAgMCAxLTEuMzI4LTEuMTIzIDUuMzA1IDUuMzA1IDAgMCAxLS44NC0xLjcgNy40OTMgNy40OTMgMCAwIDEtLjI5My0yLjEzOFptMi4zNTQtLjIwNXYuMjA1YzAgLjQ4Mi4wNDUuOTMxLjEzNiAxLjM0OC4wOTguNDE2LjI0NS43ODQuNDQgMS4xMDMuMjAyLjMxMy40NTYuNTYuNzYyLjc0Mi4zMTIuMTc2LjY4LjI2NCAxLjEwMy4yNjQuNTUzIDAgMS4wMDYtLjExNyAxLjM1OC0uMzUyLjM1OC0uMjM0LjYzMS0uNTUuODItLjk0N2E0LjggNC44IDAgMCAwIC40MS0xLjM0OHYtMS43NjdhNC4zIDQuMyAwIDAgMC0uMjQ0LTEuMDc0IDIuODY3IDIuODY3IDAgMCAwLS40NzktLjg3IDEuOTk2IDEuOTk2IDAgMCAwLS43NjEtLjU4NWMtLjMwNi0uMTQ0LS42NjgtLjIxNS0xLjA4NC0uMjE1LS40MjQgMC0uNzkxLjA5MS0xLjEwNC4yNzMtLjMxMi4xODItLjU3LjQzMy0uNzcxLjc1MmEzLjcxIDMuNzEgMCAwIDAtLjQ0IDEuMTE0IDYuMDY0IDYuMDY0IDAgMCAwLS4xNDYgMS4zNTdabTE1LjY4Ni04Ljg0OC01LjUzOCAxNS40NGgtMS44NDVsNS41NDctMTUuNDRoMS44MzZabTQuMDg0IDUuODAxVjg3aC0yLjM1NFY3Ni40MzRoMi4yMTdsLjEzNyAyLjE0OFptLS4zODEgMi43NDQtLjgwMS0uMDFhNy4xIDcuMSAwIDAgMSAuMjc0LTIuMDIxIDQuODQxIDQuODQxIDAgMCAxIC44MDEtMS42MTEgMy41MTIgMy41MTIgMCAwIDEgMS4zMDgtMS4wNjVjLjUyNy0uMjU0IDEuMTM2LS4zOCAxLjgyNi0uMzguNDgyIDAgLjkyMi4wNyAxLjMxOS4yMTQuNDAzLjEzNy43NTIuMzU1IDEuMDQ1LjY1NC4yOTkuMy41MjcuNjg0LjY4MyAxLjE1My4xNjMuNDY4LjI0NCAxLjAzNS4yNDQgMS42OTlWODdoLTIuMzUzdi02LjgzNmMwLS41MTQtLjA3OC0uOTE4LS4yMzUtMS4yMWExLjI5NCAxLjI5NCAwIDAgMC0uNjU0LS42MjZjLS4yOC0uMTMtLjYxNS0uMTk1LTEuMDA2LS4xOTUtLjQ0MiAwLS44Mi4wODQtMS4xMzMuMjU0LS4zMDYuMTY5LS41NTYuNC0uNzUxLjY5M2EzLjA4OCAzLjA4OCAwIDAgMC0uNDMgMS4wMTYgNS4zMjYgNS4zMjYgMCAwIDAtLjEzNyAxLjIzWm02LjU1My0uNjI1LTEuMTA0LjI0NGMwLS42MzguMDg4LTEuMjQuMjY0LTEuODA2YTQuNiA0LjYgMCAwIDEgLjc5MS0xLjUwNCAzLjcyIDMuNzIgMCAwIDEgMS4yOTktMS4wMjZjLjUxNC0uMjQ3IDEuMTAzLS4zNyAxLjc2Ny0uMzdhNC4zMiA0LjMyIDAgMCAxIDEuNDQ2LjIyNGMuNDI5LjE0My43OTQuMzcxIDEuMDkzLjY4NC4zLjMxMi41MjguNzE5LjY4NCAxLjIyLjE1Ni40OTUuMjM0IDEuMDk0LjIzNCAxLjc5N1Y4N2gtMi4zNjN2LTYuODQ2YzAtLjUzNC0uMDc4LS45NDctLjIzNC0xLjI0YTEuMjE3IDEuMjE3IDAgMCAwLS42NDUtLjYwNWMtLjI4LS4xMTgtLjYxNS0uMTc2LTEuMDA2LS4xNzYtLjM2NCAwLS42ODYuMDY4LS45NjYuMjA1LS4yNzQuMTMtLjUwNS4zMTYtLjY5NC41NTZhMi40NjMgMi40NjMgMCAwIDAtLjQyOS44MTEgMy40NDYgMy40NDYgMCAwIDAtLjEzNy45OTZabTEwLjQyMi00Ljc1NmguODVjLjI4IDAgLjUxMS0uMDM5LjY5My0uMTE3YS45MzYuOTM2IDAgMCAwIC40MS0uMzQyLjg3Ny44NzcgMCAwIDAgLjE0Ny0uNTA4LjgyNy44MjcgMCAwIDAtLjI3NC0uNjE1Yy0uMTc1LS4xODItLjQ3Mi0uMjczLS44ODgtLjI3My0uMzE5IDAtLjU5LjA2OC0uODExLjIwNWEuNjE2LjYxNiAwIDAgMC0uMzIyLjU1N2gtMS43NzhjMC0uNDYzLjEzMS0uODU3LjM5MS0xLjE4MmEyLjQzNSAyLjQzNSAwIDAgMSAxLjA0NS0uNzQyYy40MzYtLjE3LjkxMS0uMjU0IDEuNDI2LS4yNTQuNTkyIDAgMS4xMTMuMDg0IDEuNTYyLjI1NC40NS4xNjMuNzk4LjQwNyAxLjA0NS43MzIuMjU0LjMyNi4zODEuNzMuMzgxIDEuMjExIDAgLjM3MS0uMTE3LjctLjM1MS45ODYtLjIyOC4yODctLjU0Ny41MTEtLjk1OC42NzQtLjQwMy4xNTYtLjg3OC4yMzUtMS40MjUuMjM1aC0xLjE0M3YtLjgyWm0wIDEuMjIxdi0uOGgxLjE0M2MuNjA1IDAgMS4xMjMuMDcgMS41NTIuMjE0LjQzLjEzNy43NTYuMzQ4Ljk3Ny42MzUuMjI4LjI4Ni4zNDIuNjU3LjM0MiAxLjExMyAwIC40ODItLjEzNy44OTItLjQxIDEuMjMtLjI3NC4zNC0uNjQ4LjU5Ni0xLjEyMy43NzJhNC40ODQgNC40ODQgMCAwIDEtMS41ODIuMjY0Yy0uNDg5IDAtLjk2NC0uMDc4LTEuNDI2LS4yMzVhMi42NDYgMi42NDYgMCAwIDEtMS4xMzMtLjc2MWMtLjI5My0uMzQ1LS40MzktLjc5OC0uNDM5LTEuMzU4aDEuNzg3YzAgLjI1NC4xMTcuNDcyLjM1MS42NTQuMjM1LjE4My41NS4yNzQuOTQ3LjI3NC40MjQgMCAuNzM2LS4wOTEuOTM4LS4yNzRhLjkxMS45MTEgMCAwIDAgLjMxMi0uNzAzYzAtLjI0Ny0uMDU4LS40NDYtLjE3NS0uNTk1YS45MTUuOTE1IDAgMCAwLS40NzktLjMyMyAyLjI1MSAyLjI1MSAwIDAgMC0uNzMyLS4xMDdoLS44NVoiLz48L2c+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHBhdHRlcm5Db250ZW50VW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48dXNlIHhsaW5rOmhyZWY9IiNjIiB0cmFuc2Zvcm09Im1hdHJpeCguMDA1MDMgMCAwIC4wMTc5NSAwIC0uOTI3KSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjU2IiB4PSIwIiB5PSI1MiIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMyIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMTA5XzI1ODkiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzExMDlfMjU4OSIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFJQkFRRUJBUUlCQVFFQ0FnSUNBZ1FEQWdJQ0FnVUVCQU1FQmdVR0JnWUZCZ1lHQndrSUJnY0pCd1lHQ0FzSUNRb0tDZ29LQmdnTERBc0tEQWtLQ2dyLzJ3QkRBUUlDQWdJQ0FnVURBd1VLQndZSENnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ3Ivd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhRQUFBZ0lEQVFFQkFBQUFBQUFBQUFBQUJBVURCZ0lIQ0FFQUNmL0VBQnNCQUFJREFRRUJBQUFBQUFBQUFBQUFBQUlEQVFRRkFBWUgvOW9BREFNQkFBSVFBeEFBQUFDRDMzem5QZ3hpUllJTG1ROXhFQkhNK1IzMGRGTWdpeGJCclJZSjB3Q1lZa0hCeGhPSXpoeGV4eFVjeUZaSFJ0YmR4V2tybWdXMHBZUUVQZDVNWTlBc0VEeHpqR0hDRERWUXVTUllWQXdHQ2dnOEk2QVdRcmtVR0RDWGduSkhieDlGNXlib2xrRHBXMWhPMHFqTE1rbGhMTVhaM3pqK2cyQlR0OEY3bURSTEtFcnh3ck9YQTJwdGtKcTRSbUVURkV4d1lNTElnTEZaZGU3L0FKRVpveThKMEM2QSsvUEVmUWpVbFJMT2UrVGFJRXFTeWxvTzlScExJV1QxVmFOT3Nwcmwrb3ZNRjhNRkF3bHNIV1k0TmlGc0lIK2lWL3pNL0NlSkhBZlJ2blBaYll5ZDZxV0tWTWRSMWpab2FiMWNtdHdkYmdxbEpMWU93MktkOHNab0JpSndqekNTZXE4c1dyYUVwb0lzOVUvOUFKUlB3TTAyZDk0UHIzcUxTNlZhMnQ1V3JybWVuc1ZhazlFaHJJR1daSkdtVlpMWG1DN2hLRXNJRWZoQjdsVUVqbVZZR3BXN3Joc0piRlc2NSt2MHY1NzJVSEJSYkdmekxyWW5ycXR6NFlDaEhNYW9samlSdjgxSUpVdklWTXdKeEFjYUkxVmhkZytVaXFkbURFNHpmQ3ZJRVBFUS9xNnZhaEpmVUdadTZjME1YblRXeFc4VHJ4eXE0VVI4U0pWaXAxN0ZsWlZHNHBVdnF5N0NoYjY2dHV0VjNyUTJudWUzbGJNV2FlVjgwSjFuWURNQkluTTJ3MnAzSlVhTElmbTgxNmVZTkZZc0VmazNDTXU4cGNOT3VwRkloTEFYQjFXQTUrNHZlZ3RjVm8zTUtGallTQVI3ZE10UlZZaWdZTCtRL1YzTTBkRVhjZm1MbmFYNXAxYzdsak5wdGhaS25GY0FUcHBsZ202K3J6NWpjSnpnam1mTzdLSW44OXBVak0wLzBoOVo0N1dYbE56YTN0dlArekZlaWF5VVF4S3lEcm1UWkN5Ykd0dEt2UEhWK3c2MTFVcFhHb2ZJMHRzVmNhcFluMHlzVFVFc3JzSlNjbWFuZ2JuZVdRL1IyblU3UDNzamZ1aFRKQW9waE1NY29ZV2h5UFF2YTYwaEFjdXdwR3ZQWStRaXJXbkdjQ1kzMjZ1cEEwd1NMQ1pra2J4Y3BPV0RXZkxiblE5S1ZBanV6U29kU2Vwd0IrNnZjR3RRN1RGWjFJRnV2SWFDa2tVdVlxbFJCckdOeTZQWjRjaVBoZnhjY3lNQjRZaUxxWFRzVy8xZFZvVUR5M290MWVsd2Q5ZlF2SnNtTCtqbFVjdjZLY0pleEJrY3FFN1YwZlRDV1lWRUE4eEZQWENTY2pIblRTRVc0cyszd3Y1YjNOYTg5bzJlM2hlVWJDM1RyZm94Nzd5VzBOWE95N2lJNE9SYlFSVVNIMFlkMmNkak1TUktjaEdnY083NlpnN3VaOEgxc1pVaHFrODQ0dnBkWTVGcHZWSERYcHJyQ2V0OTNHN0c5Rml1elVaRXp6d2RkcmFoY0VhckM3VitNSW81ZE1DWk45VnM1a0hkQkVyT25VZVZ0YUQ4NzZEN0h1NkxadFZ4VUQzZlBpU2N3VGY3ZFR2cll6M0VpMnNWOXZhMk11ejcxRStWZSswcGw3T2ZzTUYvNkx6OXQyTVozWnE2WitYKzQyYjdMellIcE1XalorbkZJOC9lUzkxb25NMDFMQXIybFJhVTdhRFV3dk9KWFo2M1ZEM1JWZFphZXRmNzNtM1VaZFhmRmMrY2ZSR3U5NW10N1hLcWx3WE5RallyMGJGNTloNTU3Ylpxdk0zZGZEME5QV0Vpa0xZVk9zOGVpczYrYk10cjJxVVVUT1RMb2E3Y2l0VHRmUFUwTlRkdmpkY0hYclVhM08xdkZ0b1BvNnRXMGtySWZZVVVhb2QrdDZBS3JxckJtWFNFaXF0akpDODRXazBVTmFqMk5abVBDVXpnTDlYRWxXakV2THR5aHZQeWVqcTdVQmYzVDUxMFVxNmIwVkJFQmtvT0ZoanM3SUpNUklGcGVCU2JYTDBlLzhRQUxoQUFBZ0lCQXdNREJBRUZBQU1BQUFBQUFnTUJCQVVBRVJNR0VoUWhJakVIRlNOQk1oWWtRbEZTRUJjMS85b0FDQUVCQUFFSUFvcmY2NFkxNDMrcFFVYWxNVDgrS0U2aWh2cWFPMnZGbU5jRytwcWFLbm9xazZKRTZsT3VIUkkxd2E0dGNldXlkUnZvWjBFaEh6dXVkTGE0ZFEvZjVFOTlCdlBvWGp4UDhmSDIxd3hyaGpVb2pYWnRvVWwyOSt1TFVxMGF4MGF4MFN4MUs0MXhhNG8xeERxVlJxVmE0cDF4bHFJTFVET3VQVUwwQVRHbHhPc0xpOGpsamxkSzFnYlZJdTI1T0dOaEN1RDZQdXhZaXRMZWg4TTZrcW91N2o4ZjRIaDErbzhHTkI4QlViam5oSHNpcGFNdHRPbzl1blVCZ084VFZJL0hIT3V6WEhxWW4veE8ydHRSdnFjVDIvSGdGR29vRm9jZHZxdGhiTmt1eXQwN2hneE9KQmFiZEhtWE1uS0s5UW9NVlhCYTN2WXhxaUtaQzNZbmFaREszRnVLUk0yVmV6MElVN2J4Y0RZTzZDUDAyMGF5T2ZhU0hSNnpLajFLaTFLNTF4enJqMXhGcmlMWGhUL2tOQU5EajFhWGp2OEFucG5GaFJYTEdHeUlqYUdYTnZiRmpqWnVSMzdvcjNoZG5JM1NpZU5HVnVKWElUWnNHNHZjMU5oZnYxRGJYSnRLaUowOGVsNGxKQnRKNEd0RTYrMVZSOU5IaWt6cHVOUUVlcDFhMGFtblgwZElQMTRZejZSOXUvNmpoMFBIcXVnSHRoZWtWMWVnYWpaWHBEM3h0cTNmVXRtMFB5ZmsyL0VVL0huTWJhKzFzRTl5c1lubGpZRjRSYTFSb0t5K3oxYlRRMytmMnVxUDhHWStZL2lOWXhuWFpJL01tUDdMakxYOXNHallyOU1rWjBVam93aWRXN3FwOWlRc1dQOEFISE5jdDhHNnMwQ1ZCYU4wTW50SE1lVXBNOE5xMWt4MkZ1TjUwdEt5N3lwblhMUDd0WkFhNFRzN3F2SWdXeTZQVlBsSHhXZkkvd0J5Mk5HVTZMdW5SQStJN3BjbXdvUllaR2VyT1ZxVkc4Rm1EN3g3b2t0dm1BWXdKTlpheW5VdFBHaVBhdnJ0UForTmZXOXlYZHJjYjF4Uk9yL2NCMWxRKzRqTmFqZHFaSmZLbnFuRzdMbXlxdGJSWjlFc0Mwb09kejgrZTg5amN4SmJUTVpoS2hrakxxcXo4TC9xOGpuM2oxYlFXdUoxL1dQSVdyUFZlT0NaaEI5V1dJS0lsdldPUUxjVXR6K2FzYmlNZzl4UzV1T3YyOFRQZWdPcXNiWWp0YmozWGI5ZWZ0eHJzUk1oWUdROVlsVWtjYnNjM2puZW9icjhGeU93ejZOTGl2WmZEL1VPaWJvclV2cURtNzJOeEFXYWxYRDVQTUdiMGt5eFVPVldmdVl5TzJpc2N4N1FMRjEveHVoeVNuVmhCL2JVNzI5aW5rQ0lCbFhtRVgycTdKRWtaSlRBN1RPeW52a3dHMEE3dEFyQ2paekxBbHZUTzB2SU54MGx6bHM4dEVkU1pyampTRmtVY2RleWxrZTFKRmFWODdtZnloUmhPK3ZwMWd1bmVuS1FacTE5Ujd1TnRrcVl0WjFWZFpEVXRPYzlzazZqVnNXbWJMMnBWRlNLT2I4czd2RHRzaFFYZHNseHkzVGxpZFg4Y3dTbzhoY2hGMmhGa2UwV2U0UloramlOL2piVVRJL0V5WmwzU01TYStJWE9PdS90Q3owVjRxSmE2S0ZBMlJSQ245T3JMaDVWRjlOc3YraituWFVBSDNhSDZkOVJOTXVRL3B0MUh3aW9HL1RIcW9aOUc0RHhyc3JmYnRwQ3YySmJZSjB6MjFJbFRwdFBwbXg5dVptOTJ4Uk1OZVRLQldhMmxNRHRwVEpybnVHU3FCRVJlcGpBN2JsQmtvdngrWFZiSDV1M21MY0JyZHY4WnhodUR1U0dJZEFTK3prY21aWEc0cEN3clgzQkdRNk55REdWL3R5NGtkUjJhS3drZmwrUnJLR1NMckRyQlVLbEZaTGU5dmtXTGx5TExEMkVpQU9KZDdqclk5U1p4VUZONkpCaDh2S0dwanVxQ1dtUUpSMlRNYi9HTmVxUm12WVlNVjJ4eGRrenZxUGJwZmVINVZWODJZZWpZeTVNOW8zR1dyTEpDRlFGa3Z0UVVzTWx3K2Zmd1RqYllVa1N5QkQ4L2RnbjR0WmhReHZxOWxwc3hNYVpscFNCY2ZtcHN6MjY4Y1FLV1FkWUI5eXBxODhiblhGS21jTllaRVRLUUVTOFk5TjM1ZlZpU01ZY3M2N2V5SDZJVGZYOURxdWNVUnBlRnloakpSR0l5WHJ2R0p5SmV1dkJzSVp3UFhXQ3pUYXpRT0d0WUw3WlJ5emJtUTNYaE16VW9QZW0wbG9XVURZUUF2a2RoKzNoNjkvMmVsdHRwdUNxc0hqMUhSTkh2N3hIb3FsRE9XWTZLeHM3OS84QTYzd3NNN3dWMFJoRmZ3am96Q0pMdlhaNlF3aFJ1UmRNWXI5QmhLb2JxUXJwK2g4YXFZV3BXaiszOGRJL0pWZ21KWk9LeU9Nekp0WGpzNWJxZFAxQ3NXYk9SdTNjbXBsdHRzeWgxNDhYVlEwVEVVVzd6WFJJNHgzSzYzYVBwckpjOUVhbG9XTi94aVdGNlR3bnJZeG5TKzc5bkIvcnRQOEFaQTM5Y1RKMXdmOEFVclhNYmFZb0I5UUZnanFaSGZlTzdmWGRNYTZxNm15T0t6QUxyOVBaV29qL0FPaDlSUEZ1NHRkblNLbFNhRVg4dzY5Tkl2N2VqYm1rdnZCZDJxcDB2MVZzVlJxOFRPa2NnM0dVZkpkanNnZHV2RG1jMmdjVTY5WjlkVFpXb3hBM05TbFhLMmJDZ0xqS2JLdVRoMXo2bCtpZHZwbGhhMGtUQ0xiVU1qOW15QjlZbTNycWZGUm1xc2tBcUtrdG9VWXo1L2J2SE85bnJzcGxEZTZTclJMQ1oySU9HMkVPcUxDYk5aYXJqWVVqRjJxQzRsVEtlVXRVNlFXZGYxS1ZmR3N0UHhmVWh0S0s5Nkc2dEZRbE0rZmxzc284U0N0WjdxYkpOVERhMk55MmRTUFlCZFI1Tk55SUlPcWwzTW9HTnFYY3FpcFhONVc4OTk5dXN3cWl1bWt5VllPOE1lazIrcHFsYTROQnRuTkJYbjh1WTYxdWNwTHg3OG5ZWXlIc2Jaa3ZmSHF6NElEbXRFaVVqSjc2T3gzUlBiM25PKytNYXhMaHN4aGVxYkN1Vmg1U3hiYXNDc25ZN0JWV3IydXBzclJEeFVYZXM3N0RKV1FwM0NUaVczZ3llU2xkYXRMNm1kdDVQSWV6TDVHLzVoaXFnL0ozdDY5YXl2eFVieGc4a1kzWTd5ejlOTmxxd3hmVXRkbEVvZG5zMVF2eXV6WGJuYmpnNHpLK0RYUzAxUDdCbGh1YThmZktHeXMrWFV5dVBjdWQ5dDlNVzFPL0pTcmgyOHhJVkRvWXd3YWMyWmxSWnNPUHhBRFlXN203TlBPOUoxWXMzd1B6YldPaTJXSHN3ZVVVaktadDd4ZWxLTEl1SERxUmR4ZHdiV012WDZTeFZqc2xacjI0OGlhdVY5NjFGYnVwVUhJSVRaR1pjeGt4QndrbDd3ellSSHVDQzFZZHllNG9zUUl6RUQvSFpwQkFSdnFkb25ZWlp5SWhXb0JhazgycTlnWkFxNHdhUS9KRGUwUjc5UmFhZXdOb3ZBTGtyRzFOTnN6Mi9lVTQvQ3ppcXVSdVIyeStwVXMrVjNReTVZQ3dxZkRyZ3gwbXFGNUVWTWp4M3pzWHRXN2VDM2l5Y0JFVExqTDNFSC9lbEV5UHhBRzV6MjY0cDMyMHNwM2tKWnRHOGEvL3hBQS9FQUFDQVFJRUF3VUZCUVFLQXdBQUFBQUJBZ0FERVJJaE1VRUVJbEVRRXpKaGNRVWpRb0dSSUZLaHNjRWtZbkxSRkRBelEwUlRzdUh3OFlLU292L2FBQWdCQVFBSlB3SzhIYU8weG9lMGRvZy9xQkRGaGdnaXdRUWRnTnV0b1phTEIvVUhzRUhhT3loM21FYzNsT0V3bThvMkwrSHpsTTUyNWhwRXc0V0JkL2lhVWtDNGNJWERIdjF5ajNsTHNQWVB0dEZNUXhPeWl6bm9CRXN4Vzcrc0dMMWxKYnJwQm9KVWo5bG8vd0JoWUlPMFFRUTloTWFMenZ2OWd5c1lkOTRZcGlIMGlSaytrcS9TTGVVWlNhVTIra3BNWndwbEl6S0NMUG5FRnV3OWptN2RJWXg5SXRyd2N3TjRrVWZTWlNyS2tQWVlvUHlpajZmWW9BZVloN1RBVEJ6MjB0S1p1MlhTMC9PTEJkdWs0UDhBV2NJMUkvZXdrai9hVyt3aHQxaStMVHM0cW1qblptaHVEb1JCRUpBMUlFVXppMTRndm9LTmphY05XSjh3c1JjL0NxYlJoVElXN1ozajRzd0h6Mmh4QmRjdDQzaDNXZTNLWmNlS24zZ3VEUGFlRlI4UmFWNmpXMEp5dks1dWRRZHBYYnl3aVZQL0FMaXNSZldjVFZ4ZmN0K3NwOG5reHZPL2MvdkhES2V1bk1aU3NSNXppeXBBdmRJU3phc1RyS25KdW16Q0JxSjJ4QzRQem50QlNvK0duVUVyT1BuRGMvZEV3aFI0VUVHSjl6ZWNRZzZKS2JPRnE4OUNuYXpMYVVybzdoVUZLbmNremkrNGVvV3RpQTIyemxHL056TytRdktKVmwyeFdqVy9pTWJFZktMaWR0RVV4YjJGM2JyNWZXWlB4QnhHVTdZMUJXM1dOWmc5bS9RdytvTS82TVBwR3M2dmRSYlVSc1A2UlZGVEZtbXpRWlgwTzByNGJiaHJFVDJvdFJiWmQ2QitjVDV6blB4YlJXWDVTNWw3ZWs0L2hSeDFlbHpJT0pXMU1IYTNXZTFlRjRqQldiQ25lajNZTXFKaUl3OHMxSDRRZ0MzTzdhQVMrZngvRTM4aENEY2E5ZlNWTG5FRFZZYnQvc0psM2RaVnByUDhLK0VucURuQmVtK1REOUlmZlVjbTgxbVIzOHZzbUd6WDE4b1d5RmpQYUlDS2JHeUhMNkNjZFNkYWpLRnJVaUNCZlRGT0pMcHMwNHhOWlVvc0JvTWVzbzA4TGE4Kzg0SVlsYko4WTA2VGc2VnJmNXdsVk1zaXRGNzMrYzkxUTA4T3N1RnR6RW5XSmlWRXVCK1VZbG1CTi9PQUFNNCtjUGlTenpKRCtjdm5yTTBZYzRId21ITGZ5anlrUTI1WFF5eEhVUk1RbE96ZVlpaEZUSVltMW5HUFFUU3ZadzYwVDhMRnpvSjdSUjJ2aERWYVpSbjlMWmJieXdUaHpvVVVjdTNoSkJnZ0VZU29MQ1ZIY2ZkcDZmTXhiRENSWmNyQXp3amxvb05oUEZjRDV3c1h4SEViK0tkRGVXL3NTUjYzbnd0TkRadlNhNlIyVVd6dHZMMzh4RnQ1ZGoySTZSUG1vbFp2L1dMank1VzJsR25RZDc4dkVzMVRQWDR0UFhTY2E0UkNBdEE0NzExNjMzM3RQWi83S3RYOW1xOENXWHVqdmlHOXovMUZQWWw1VEpBR2dNNEMvd0Q0NVRoTEhWcFRXMFA4WnRLdUZkaEI4TjJjN3ovTHdpODNjYlRZV3ptZ2luUHlnT0pkY3BUZTVYVERPQ3FHMzdrNFJobDkyY0kvMGd3dHNwbkdwU05PMk1QcWM3VGdLSEFyd2QvNlJWVUt3ZHRoaXZGNGltYkNuVTRjVnI5NHcxdGY1ZExTcncxQjM5NGFORERoVHl2YldWa3FvNHVHUnJneW1CS0l6MW5EZ2VrVGw2UWZLOG9oaU5MNlNrTEhZUm1IVVNpUHBLR2M5bnJyY2tiemdsUHluQXFQUlp3bWZuT0ZWZXVVb3JsNVFaQVp4UlVGSzNPVnlNN3RXWWU2RnRUT0o3bGx1L0RIeFlUcUpXWEdiTFVaUm1jNXhkWG1vKzhWVjhiMy93QlhTVmNKcGNRYXBGUmdQcjFsVGZrb3JhNXFYL0FlWWxTa2VJUkx0VHBVeXVGZG9oZ1BZcGc3VEdqbnNNSFllemlWVUl0MG9rMkZTLzU1eXIrMkRpTUJjWmhpUitWdGVoaVZHcVVxb0hJZkRmcktOU21CZGFSdGh4ZWVtZnJLTklyYXpkNG1zZENGcTNGMXlOdW92RVN3NVJ5bkM1c1RjL2dKN1BUeFoxS1JzeW5mckVxaHdBS2RTdHhJWmFpWXRBTnJTajNkOXNlTDhmc05ZdWJMZmN5b0ZGc3l4aHpMWVY4ekdHUERpdytYMkd0NTloamRqbnZhWTkyR2V5K2M0d1hvMVFPN3hlOEQrcDFIbktuZVZzQkFybnAwYVZXcUtiRUIzdmhtUWMzUWJTZ0RjRG5Kc1IvT0JWVTVxcVB0SEM4MmZmV3luQ0ExbHFEdStWK2EzWERPTC9wYmNRUWJWL2RzblhMcC93QXppMG5xVXF3U29GYXlpODRkYWRUSzVGVVd6LzUrUFlWRlBjc2JXbFJhMTZ5NExmRm5kQ2ZJemlpUWJwVHJLZGV2MU1HTGlxM05XcTF4WS91Z0Rwa1k2dFNvb01lSCs4T1dWK3Q1dzdNTEUxSzJ5NVhqZUViQzhvbkFuZHN4VnMyQmhRZE0reHlIY1hYbHloL3V5NTlCUEJoM0d4aWpNM0xMSENmZFZ0WmsrbVkxaFBMbGc2UVllYk9NMnQrWnB1Y3pPSnNjVm16MEU5b2xhcnZ5T3kzeGVYNVQyaGpxc0Z0YjgvUFhXY1N5MVVwSmFvcmpDbVcvUXoyMVJKd1dIZGpHY3BRWmtaMHlDZUd5bS8xTXEzUmFHQlhEZUFrMnNCK01xVXJjSFNWYVFBdytZM3psYnU3Z1k2anQvcGxJVWFiMnhXNmRaeHZkSTVKcXNtVnZNeHF0bUo5N1U2aWNiZ0pYRDNnOU5aUXJjU2pMejkrbWJkYzlwWFB1c2tOUWN6RCtjeHJVVUVZdnlqbGlUcmU4VjFHRzNMQjQwNWZXZkZFNXRyNlFOZmN0dkJlTFlrYUdjMThzRXNNSkJJNjV4QmhYVW9OQkIvRlV0YVpMZ0JHRThyM0dSaUxsNGRoNjVUMmhvK1NoNVVTcWpWcWRTdzY1N3k2MGpYMSs0TGZ6bkZobEl4ZDNpNmRaUUZTNjJTdDRpcld5ODVSbzBxMVEvd0JyYkVUOVk5VnFnSHZRY2hpOHJSQmxTZEJkZDlvQVRWVG5XM2h5MUJnN3pIbDUrc09XV0xPQzh6Nm1VeGtMV25US2MzU0l4R21Jdy9oQjRjd0ROOVl2T3g1VDBsOE5yRzBIbFlpVkRoUmJBZEJFSGR0VDk0RzBIbktUY29KeFlzN2ZyS2VKM1VOVWMvQ2RSNngyeE9CM3BPK1VTNHY0UnBQZHI4U2pLNWxVM1RtTnVta28yenk4cHBxSVRmQkdQS0xMQVBNekx6alpPTEhzSW0xclNxd0Iybi8veEFBbkVBRUFBZ0lDQWdJQkJRRUJBUUFBQUFBQkFCRWhNVUZSWVhHQmthRVFzY0hSOE9IeElQL2FBQWdCQVFBQlB5RVF1VFJTNHgyeGN4RWVneXJZd1ZpZVRGOExNSGRRT1Q4UWhaQStHSWJKbnFWbDhKaittbUVibEcvL0FJOHFuaTRJandCTzVYc0l2YTg2WldLWERtSWVvaW1LdTdLKzQyNHlybFNwa2ZVSHFlbXpqek9QUFU5VTRKQWp4UlIrdUFlTWY5S0UwcEQwcTVLQjlweFNEQ0pHK0N4anI5S3dsWUVUL0xiQk42eENpT1F2NG9GelV5TWVINUxOVXk4ejZ6NTN6eFVlV0U5Und3UVVSZzdUMmxFUHVwZWxxL3RqYkg0amtCTGJVclA4NjVXWnVLQnQzU1crMWxVcnVNQlVNSWZOUVZ4cmJjSEVVNWdLSWU0Mmh2b1lDQmR5bDFpMS9aRHJCT1pIWVRyTVlMMURWTjBDYlB1TDcvUlEzbmx2elpUUjFGNFVUUENaaUlHTVZOb1B6TVd6ZU5jVjFSQktybkR1WUZBNUc0VGZMbVpzaVpaU3RueGhMdG44b0ZoVDltYU9UYkJKL0w4czVHSnUxRk1CeG9oZjRSNFJVeGlkSU9JWHJuMkNabWNzOEFKMlA1bW9LOUpxV3lnR25ZTUJRaEhWVGY2T0orSkM3Q1c1ZzFOOVFicEh1RXV5L2VpN2orb1UxSFdUYmx3cFpYS3pTL1VYaldzVERTNi9UNno1SzRpV1RaYkplbFBTaDlvbTcrRVgyZmNZRkRWNi9NVVVUdk5vOXJta2kvRVBkYUs1L0UzQnVMVW4zSzJTMVNWMHczbVgvd0FuUmd3T1dWbXY0Z2tNbG9zWW0yVG5FN05SM3VOZnZXbWgydWoxT2IxVkFMOXh6a1d3dnM4d3ZYZ05ScmlHb3NxTUxlZnpNQVlyb2hNMWkraGwzaDB4ZGFsbU1IYU5mOWdhdnhIQW50Vi8vVVlBYXNjbnpMU0k4Wk1VM0ZEcEdNL0ppajdUQityL0FKc1hQRjBEUDJ5NnFjaVQzUEp0UXZBcU5XZCtwZDR0dmE4c010YkM3ZnFmeVR2aXo0OElVNzd0UG96Rm9oN1M2dHhqbC9xWkFybGMrV1lPUDFTR1U0VUQvVWJIdHBkWVYrZFJWN3VpMnNiY1VTNE8zYWNqMmcwTXQ4UnQvUHhIWlJteU44anlzclVyd1ZpUlZCMlBsMEJLU0lNNmpBd09yb0JEV2xETjRvL3VNZGVlR3FaL0kzTHRxakhWL3dEaEVNS2ViT0NXNmQ4Q1E2cldWVnpkZkhFcGw0SkUyZmN5MjF0L3pyOW85a2RuNURwOGt2Tkx0MjVlNS9ISFptQXVyT2FUd3FZNzNsL3pLTndEQWFENDhSQ3ZVUUl3YnVvVGJUU2kvd0J1WTZDR1FaWWJZcnVQMDBFaUF6aHprbUVrUVcvNmp2TmxVc1BjTmgvTWpwd1UxL3lQRXZsN3kvWHcvZUw1cEFES2VrcjJzd1lGMnlnY2ZqUHVPcUlkbVFoK0l4OVFTM2Y5Snh3eDU0bjRtYldXbmJ2MUZQM0lvdjhBQ2Y3NWpWdXFkbkVZWDEyem5UcHgyc21GMWxNM1hKNGlxVEVvVk4wbGZNbzRzRkE1R3EwLzhsbElhU2pYVWVGZXRVMlRuZWliUE9JSjdBZzdkWWlWcGphRnVVWTZ5M2lQbGdzL01oelk0R1BjSDRDNi9Zd0hyUy80QkdRcGpjV0g0WFdKWktKTnV6RmFNWlJsNVdjSnNWN1IzVXhvcHo2NFdXZ0FLeXpHWDJzdnQrR2QvdFUzRFRSZUU1aGNjU0dUMXhBSDZZNmx5N0RnTG1pMDFnZnVOSWJUMmVKb2dLd2hWV1M4cjZoVEZtSlRYSExiaGQvR3VjQkRiaFNLclhMbnlUTERPUVBpWVFrQXQ1R0xkR0hHL3dDTHhFb0hkR01mTlg5M0NyRlpSdnBVSGx0aXNLWnJnOEhIOHk5N0h3S2gyRkQ0V2pYd2ZVelRsalVVMkdRZkJqNGdvMk8vTXRPQWRUcXA3RWhnZkV0VUo3bW9EYW0vR29sdmZUUWplclA4YUhHNFpyYXEwb1V2TjlzU0xMUXZlSzhiMEI3bUtjTUdWNDczMFVzTjJNT0hkVllBMjNYeWxtaU80Wkt2MU1RNnpJeURTZzU2VkFmaVdaU3Q5OHhHRmRyRjVqMUIvRzZtblpmTlJjVzFiWFNqUjBUYWFZREZjeWlHS0VFQ3FoZkZpRmdvVmRPYTRqRkVyNWVZQVZOb3U0ZFNCdHRaZFRrTHk4NWJUWkMydU1ETDZnckl1bEkyRVNHOWdCbmJFWi9TRWFIRnFwcFhGc0dxV0ZpNURGRk1LRUxmMWJHSXRBWGx1NmVKOUtKTG04a3JTdWdnSWtzbHNPaEs3ZVNXU3oyd0VBcTRIeEtzWGFkRXhQbHRzM0I2RmlscmRTalU3eXZmN2c4eGVCRFYyamc3MUhaNCtseWgydlhtWFJ1YU5jVE85aW5xVk5tT2F0NnZPS2x2ZU52RDZ3ZVl5TmRpSk5sY2Y2b2ZUWWEzVzFveWZpWisreUlnenllSU9za1JRR1NYVmFZVFZKK053eFRLTEZCQUMreXRGTVlQc0QxeGc3OW1KMEJIb0dDd2Z2OEFWRGFIR0YrWkZUQlpwRE8xZUptREhVMGI4RXluMmozVm1JZzhRK1NCL2d5bzB6Mk9tck1TL3IzMzhXWVAzaHl5b1NOUCtneVlqT056bjBYSVBEMUJodEttNTM4eXE5RmZjUDhBTHVKa1Zwb1RoZVZ0QThHb0ZycWdyVGhuTFdPU2FkSm9qWXVlSHFXc0NTcERUc0VXYVlETVdVMnk1M0JkZlVJVWF3Z2IvcUU2U3c2aTJ2VStSNFp5cS9WeDZTamlkbUUwSldmRXprWlhyOUlrMnA3bTI0WHcybUxtTjhTdlRSSENkdkNZMXJuSnlGRGtQOXFXWk1HR1BIVFhQVXBOZUFvUVlVNmxwZVdwZ2Njcmp3Umd1R1NiNGE1dnVZa015Z3NaZlA4QUc1UzE4M1VWN3MyMWpCVjVtUnZNOHU4aHc0UHpIeHJOTHJtanptZEtaY1JkNXB2UlJlUkRUY1BMd2ZZZzhNQ2lqVzNmQVBzOHdxejFzMTN4NERIRlp0allacWVQYTZKaEx6bVBocTIyR1NtQmE3eFdjUjZ4WmNXSXJmQmZIeVJDWEFybG1ycWZJYWtLck5WVFh6R29nbkVwWE54Mno4emE2M01NOC9FMFZDazQvZWlrYmRMQ3FLejV1Wmc3VXlHcVlFMEVMeWZZL3dBd1d6ZUorQkZHY09EeTE1bGhWWFZlU1BubHpXWDJ2bVhVeGJFT1ptUk1WbURrNUdGZm5vQXpneER3RGpwUW5Mamc2VWtvampTK1lPUjNkWkx5UlNWWmZuVmh4ZDV6QXV1YXVvNWNvUG9sRlQydTM3R0lBTjJiR0VUS2p4NDRtQ1ROb01nSHdDdWkrMk5Od0wvZS91ci9BSXhDcnJVcUhLL1A3UzRwT2JQVFErZWQvRVlMdFYrRFNuZ3Y2aWpvdTFMcGJiaS9VdTZXb0RnMzRhODFHWmJpL3dBeDJlWXRmWE5ZZGVvc0k1Q0M0ODhTbFZzSzhkb281MFhqSUgrSWswMjFvR1VOcTdQbnFNcDBMV1pTaktBdUVzU1pmdjZHZVdKUzdEeDRIcUozNU4vUG5qaVU5Wkt3Y0ROMFFuSmZ2dk1tOXVPTXhJS25zYXR5bWJKM1hrYjFNVkxaNVRpcXVhWWM5eVRYaDArMnVXTTU0TEE0Ri92L0FETEhlaWNuci80bXBNV3dCaGNzN29EOTRPeXdrN21DdTN1VjBPNUtyRC9udVlndG5WUjZUTi9YbUZRb2JYc21QTHhERVZLRGxFeEZMb21aOTc0QzNWVFdvTkZyL0hNOElRVnFNVzFLYVBjenpPQ1RZZm5NYnVPUllEeDE2aFVGNEJ1czlFT3hzTi84V2swS1FCSllXVytZeUM5NkszNWhiZTBuNFB6RW04OE5IV3NpZUp5cFJjam50T0VHcUJOdmxkUWxacGdpQmRIdGxkTUxZaEszWHk3Z2tPbDA3UEw1ZkVJUU9uOEI0NmpaWXpUV0NWWDVoVnZLcUN2eENCbEdlL0VxSDZ3NGgxeDhFeVcrTldzeEZxKzRqbVc0ZGR3ZSt4TVA4UXU0QXkxRzQwT28vOFFBSXhBQkFRQUNBZ01CQVFFQkFRRUJBQUFBQVJFQUlURkJVV0Z4Z1pHaHNjSGg4UC9hQUFnQkFRQUJQeERSSWVEZUJ5d0l0ZjNEVlVZdzR6VlAyd2xpeEFxSTRBUkhHN01qaDd3bEgrNGJMVExXcktBK0pMWmMyNUdLVzQ1TXNzWEZ0SEtRc0I2Tzgyd0VUaVkzb2ZXYWdjOE9CeVQ1SEJXQXZObTY1bkZ6WTVqWW1ldU1YL1d4OHp4MGg2aE41d1Jxc3IrSmhGbVlYUFVUTHpmNU1WSVpzLzhBTEVPeTRXNC9yZ3RMTEYzZzVLNTRzeC9CaGpEcHYxTVp5WEhUcmg5aGlTVWZNYjNHRDlBRlRjcXVYRXd1eGt5NlRCNTJlT2FsUFdXMExYb1BMK2J5dUx4OEhIazMvbUdKam95Y2FtV2NuY0FIb2h4aW1ZS09OOWVzUUF4aUlNWmhBU1gvQUhFYTNVa3hYZ2prWWIxWVozcjh5ZFpNcGhLT1VPVmRaVnVPSWZ4U1pTREpMVCt1TDlENHNxdTJOQmhkSGpIYkNmRG9PZ3hwOTFXemI3WjdSN2w0L01hUG9ZMG5uRSs2QTRHWk5nM2tQK0d2T1hxTmI1RGtocytSbDJwQmNYYzhPQ3M0TUtBWHN6Wkh3cTNJTnVVcnJHclc0RkdYaXhsZlE5TE9lZjd4UnNmM0ZTa2ZKY0JNVXR3ZU1DZUEwQndZTVM5TnpoT21RcWU4L2NFVkZqelo2TWViZXpwdmpOZmx6V3hCUjJRaHdJa0NwSXhwVVFiUU45NXBuU3BYOHVBUHlRQmlxbjFGMmNSVDR3UWV5c1poemQ1YlUzZ0poTkIxYldLUjFRQWNIQlBqbHhtaVZ6aFVmbUZKU2hYRHR2Q3JBUUhveXhDUExGUWl1WExsTjhyeGl3YW82QnRmQmk0RlZ5Y0thQUtrRXJ6a0tvY09WeHlTcEpmbnpEbHdqb2YvQUhBa2E2Z0orNEMwdjdZazFQV3h4RUVmYlRJQ09kNGdFUHJnVG9PSXcwOWs0cFBoVVRGS2g4WnBCL21lSFhHczNEMTBDZXNTVE5nQUc0Y1BYU0JmT1BaSUNQWVljSTB2NWxDUnMvNU1CMDdYWWE3MWtvNDFCWGRxNHdncjAyTXJ2ZW04Si9CdmhmWUVNUHVMc0wvSEgvTTExSWVOUW92NjE3em9yZUJNMEo5ekQ3KzR6Qm5BSjg0Q0IrT1d4Y05FSGFDdnBaYzJpaE1rTERYbWJSNGZaY0pKY0NEeUpwTUVYWVEyTi8zQ3h6VUFmYVlvVnA1MkdIYURsejBMZGpvNWVzQmI0R2g5QnU0eVFPUlJZUEhUclFaTUN3bnFxaURxenFtSHdISTc4WGFDSERGVFNDZG0yRCtJNHpwU3luNm5mbkduajh6QTZLUHJITmNxdFBnQlZlZ1hDUGxwajROb2ZjVTdKb3I3UjVZVTY4UlBRVmF3RGYzL0FQSXc2aTNxUG9yaTFXeG5MLzhBSEZ3QldHZ3BSNXNFL1NZVWNqdFo2ZEdKbTJXYy9NcFd5SzVGZEYzUVptZ091V25GNE5aN2I3bzFmRjZ4S1BiL0FHbXRCTmRwMlpSK0FuVWRCLzZHS1hWR1FKYXRQMllzeHFoSjN6SmlTZ1RaUHZldGZySzhyMDN3dmV6QkRsK1JldjhBNWpJRU81SFhBVDlYR2RiekJ5VmFyb1BEWmNmNXRrZUlPMEZDNzlHQm1WUmhzQmFOTzlZdXNYYkVLTDJleEM1RWh3Mkcwb0ZFOW1JVEFnNjdmZUU0REJnNnNQekhTNzVWMlZzQVJYZS9pNFZCb1hRSklHSTROdGJDS016VkowOWM2ZXpBbVdVQUlSNW9QWk1aaEN1MUZIMWFqdlhadUtQK2NnN0YzeC9tTldXNzQ1L1ZROUp6bHlWQ05HMDlhMnZVY1ZYQWxTNzZ4cERwWE5VMnBkMTY5dEoycWNaSUw3bTVYVzFqczMzZ0EyVnpYdjhBSC9jVWdEYjMrQ0E1MCtPYzM4ZFpNcm9CWlBlOERKWG1mMVhSbXJYSllIdHovRGtNeDcwUDZZaTlNTmNua3cwTnNSUitwYmdyWEhJeGxyQXM4cGxHNHJ0SlZMZkduam9pZEY1Z0RjNDNpb2JHNWRuUzh1Kys4ZGkwM1lxOEdzNERlVy9Kc0pGV1RoaHcybks0azZpQjEybk5vdmw3QWNZbVduMGhndllCODNzeDdTUVZ1M3hBMjhrR1FyQ25nSmg3WTdNS3MxQ3A2dGZDR0VDaWM0WFg1R3ZZT0dpa0VLRkhUM2Y0dVdHK25ES1VRVC8wWThvYy93QStNb3FxR3BmL0FKamt2M0gzSElKWElLQWk4T3hMclhKM1R1QnkwVG9xVnBKcm5DajV2QlJXazJMMzdjZW5LL0dVVzNjVGVzRWJER0NKRk5hZTdqMXlveSsrTVBtTmx2cEdIQldITGhSUmRqRDBjYVhsV2VjMU5DWDljQVMzZDU3Q0hIdnJCYmRxZGdZY1NMRkNnWUliYWIrU0FqV3pudmJpSm5HcVRSczBGMEIrM0JISmFCcU5pSXBPMHU4SWhWVlZiSDJ0Y3JXbEZiVXFxQWVPYVdheEp3dERiWU52SU1PcDFsSWlaT0lhT2dYV3M0Smlzb3A0NDR3TElRVHN3TTQvaGNuTVYvMGNPL21NTHgxeERwM3hrMlVVQjhqaFh6RDVnbCtqc1gvSGZ2R0s4TGVoOWQ1dU5GcUp4b2xHRnNsU2VvaGlvZThtZmtndUFTUlFjb1JkNGNPUVlOMncyRFdkT25EWTk0aEpybkVMQ0NDUnd5Mit4d1o5MmNGdmpnb1lKUktwTUtJanR3ZG51TFl4b01scnBUanV0RTI3SFZ4WmsyZ2hxT3ZMMjh1TFFSTHRIWWg1R0JlTjRIdTNxcndIQTVyVUp3WVdRRW9aR2ovYk1VTThlaUlmYlQxaHpGOGlyVGY4L3VEMXRCM1lMUEM3L2NOSlhCL0FleHp6YUVIYnljeDY3dUxGZzZKSjBvZWZHOGJ1bHJxejd4aTZNTlVhZmFkL01xbkFybzdUOU1SREdDSUwxZUJyeGxTcFFzTUJWWGlBVmNHNHJRSG9IbXNqU0Y1dVB1WkVZT3FVbm9hbFhCUW9QMWpHbVRZT3MyR3o1YVdxUkZsNU5nQ3FISGxHQUxTTTF5SXM3VFp6dDhZWjJvVWZYM0NUaG9yeWg4bjI1dDFJZFFETnplSVFRT1VMamV1TUt1Qm9SZkErWERBckE5U2IvTHJuR1lwaEMzVHFvN2NFZllleWdHbittSzV0ZzZLRHd2SFRsU2ZDT1lwbmpXSWM2N2xMWDhSd2JORFNOTEJyMW1rYjg1VGh3YVZ4UmRBWUgxTzhrOEVFYmZack8wUUxDZlFHTHdTQnNYQnVwVWx2N0c1NW1zaDN6MldOR2ZSd2pnUVU0a2svVXFLUVRJbkVKNmlFUnp5RnN3cnI2UUhnYjVKSXZRRTdmQnB2SjltQ2ZYYXc3QVM1dkpMMnpvLzNLNndpSXhJZitSUERNa1ppRlF3S0xYR0RuVVo1L0lYakdpTnUxZGcxckd2bFc4MmtUakQ0VUZIUnc0dEdiUTFneHVLa3VUeVVhYXo2dU9UN3NBUDNURU5YQTIvVXdHVVN1QUt2NFl6bXkwM0lrU29WQXJwY1ZJTGRBMGdvdmNpWTRIWTNLcTBHc1R4dkJyUVIxalZBU25HdGNFNDgwTzZwcFlnK0F1YXI3Z29xMlF4MmNodFFjS0RKb091ellUYkhrR3ZJcUNIRkhuWWdYTVZuWTd0NjRNQ3d0KzVyUDlNUTZGamsvbnhLQ2NZdWFwMWpnSW54bHQ0bzhoeFJsSE9Eek84TTg1dmliWE5vVzRvanN3TzVDUjR0bWxDdExXNmxjU0RHcTBtdmNHZ200TWMyTi9OWUVLRXVFWStzQTZMb3dSRUZRMnlaaXRpT3d2UEx3d2NQNzU1SmdUYXJ6K2g3bUlPWWVkcnVNWlRsamNZTnlZQklzMEVFOExsanhnck1ZRW15cUxUWkNxaXhvUUpJak93VFltT1pUaGxUeWVNRXVsRkFFejdoZllNd3VaUWRpNlYrdGM2Y3Vid2pvM3lqVDRkWnNBYUJPdmcreVhKYXdJVVk0NnBMeGljcVZ3SUcxK1pNQjRReXZrSHFQM0QxUCs1SGwybmVZbGMyakNuak5QOEFPU3JhbXN5MFhTNk9NVWtrRUJWVmkxR2JNSWVFN2NSRGJSTnQ2dWE1OW9nOWszNGRQSjdkVHc4a0RCczNab0UxVExUcXFPT1FycGRycDZ3ZnpWNjVoR2NoUFFwMjQxWVFsV0VSVUJ4MUJtQk14ZGNLQ3ZGb0VCbUMzcUFLQWlvek1vNE56ZkpIYlkwTlJaVkNGYkd5RkRMUzBiK3VxUkIySmp4NEFVU2plY25pTlhGdUJuSTRVQUxnbG1zSmlpM255MmFhVUd6dDVVclVVQ1FqcUhpa0tBaHdlMENzT0FvNnNrcEJMUkRnZUM2b0RnVTJxWWhLTGFrSEVWM1F0R2FTT3NvbFlYSXMxcHBpQUFuSmkza0t0UGRlSnovcGpSVnB2Y05RN0tnQnV1QjMzV21CYldoZFNWSlRBVkRZSCtTUzgwOFV4WFFadm5vSnhIYjhPQ3hTeVdOR0VFcnh4M2dsTzBjVFgrQWZxbUxYZ2hTRDUrU3d4R1VqWmlrRTlnRFdCdXFMcnN4dlJ2QVo4NDZIQWlBa1BSYzRrcWJrQ3NRZFk0QkFzb3VUYkVINkNXQWlydE5ZbDJleElGa2h5aUR6Q0Z4d1NZQkNpMjR3SXVsdWtlOVlobUxJMVNHYisrWDZvRlZVTjVpQml6SVRVL3VMdDZwNFpVeTk0VFc1bzBrdnpXWDZ3ckFtRzZLZ2JHOTNCY0kxcTFoQnBCQXEwSG9FMUlvc0dIQXZSb0ZzdzhZUEdnaEJ0d0Z0RUU5d2IwQVVOOUNZbnFYdFRxZDZ0aGJKT2MwdGZWODZiUjdJWENzVXF4Y1Vqb3F1aXF0eFN6VlFtc0tCSnZ5ZUhCUGl4cWtuWGdwTjNuS2QzcnFqS2RIYktvYjVrR2Q2ME45YXlBaXFrTEZFQUFCL2NaY0phb0lGZFA1ZkRnZDBFcnVGK0dEUWRjemVGRjJkY0o3M01BK1pqZWdQQUxReFFrNi9Zb0lhUllETjMzUlNxOUZlR2lQckM4M09vZ09kbFVSQ3F1cURMQnQ2NkJYUVhORkNsTVdvVUFwWTg4dVJaTjQzVVNBV3NTbW5PS3VMVGlqVVFFcmsxSWw1ZHo1MUlJYmFnY2FuQkNRa1ZaZGdOeDNITlpqbkNSQVJUSVRWSUxwTUppQ0swRG9BREVVZWpoVXJpUldZZ0kxUzliYnladkpFSU80clkzcjVZZ3ZHbWlVRjhXVjh3dTdxamxyUTAvM05MSXFPNWQ2Mzd3NmVpQTVXTHRmZWpBZWxjMWRoMTZhWXY2dTRONkgvQUwvY1o2am1ncFpQUTd1Q2E0bFlvMkhqSXBCMjNEWFE5amczQmxVOEJYYnl1MFhlUE1DRVdpMkJxeVhtTGt5RGpoUERBMjVDaUJaRnVKbDRkaEVIdFM2ZExpM0pxV29VWXkwMTdVc3NlcW5GOWY2aU9CWWlIYjFtZ3pwQ0VwVVhnOEczZUJhbWQwMFJMekNyNDRERjZEWEZZK3NsdDJ0WVp2NERsSEd3cHpvbnRlTUhVQ0FxZWxJVm0xRFdKT2RsSFd0KzJoYXN1SHdCWHlnUUluSExsMXhNdHlEb0FkNzVSNnk3Y0Jld1Q5Qk1ha0kwUVZ5NENwRFpTOHpubGN2Y3pST1ZUZ3dJaW5VSk9IcGtlcGdZOUY3V1U3dXB3Wnlyb0Y5aDlZZ3FNWmQyS2ZoTXFpSUpZbHQzenJrei84UUFLeEVBQWdJQ0FnSUJBd1FCQlFBQUFBQUFBUUlBQXdRUkJSSVRJVEVRSWtFR0ZDTlJNaFVXSUROQy85b0FDQUVDQVFFSUFBa0N6b0owblNkSjBuUXpvWUZuU2RaMUU2UXBQR0o0eE9oblF6ckFvL0hTYTE5QkFwZ0UxcUJET2tDVG9KMEU2enhHYUc5VG9aMW5TZEowbXBxZEowRUtDZEIrT2htak9zNmlkUkF1dmpwT2t5YjZNWWJzcnlxTGh0QmtWZ0VrY2hTYXkwWGtzaGJTeHJ0dDhuWTQyVTJ0c21VckU3TjFRRzRMdDdpM0hmdEg3blFLR2RKMW1vVi9zb1pvR2RJR0JnNm1iVVRZRXN5S3FSdDg3TE9Ua3NTbHZVNkhkM0JFS2tEUUFiVVFlL2RRNmlCaVlPMjVYdmNHUDJsTklyV2RRWjFoUVFwT3NLa1RRblgraGFKNWpQTzBiSTlUa2NrM3RwVldDdUpzU3BOL05ORlpYY2JGREhjOEd2VUZaRTZuY3BVRDU4dW9NZ21lWm9MVEE1UDBQeE4rb2ZRM0MzcUR1WWU0bHR4clFtTTdlelBtS3NyckpXVlVnRFpDckVmUTlDMkMwR2R0UU5BNUVXMGZFTENkdjYyWU93K0QzSWc3YWgrSWZpSDRsTlREMnhWSmxLakpwWFVoaUlxa0QzajlHUHRhcTJIMmlzQWFnVklBdXZTcUoxV2FXQkJPc1VmUUZENmkyVnN4V2RSR3RxUTlXOWEyRDhRc0JERGVpUnNwSW1TekU3TlBkaVFjZmRaVXVqMUhSd3IvQVB6UElueEF5TjZDMDZFVlFzSU92Yk5RaTdZWjJJRHFETm9Qd011cUhNSDQvZG44ZnViRFBOY2ZwVmZiVWZ0R1VqajI1cExiblVFVGMveDNBVDhSUFU5bXNrV1l5c3ZZNDZKOXdoY0Erd2Z5Q1FUc2xrUWJsK2JaYU5VdWJ0N05WZ2JJY1NvYUdpR1piZXNTMDZpc3JmQUgxOWd3QUdabkk0dURZbGRnNTJvWkpvWGNMZ0RaQkErUTRJbXhMclh1Ym9LL2FlZ0IrZGt6SnlxOFZPek5kazVSKzRKcXYxVWU2RzFxS3dHMUsyS3Y3R20rMHFmRmNWZzJJbG45L1RRSWcxcWM1anFRTDNwb0daU1daOHlqbzdSc3pLSE1nbERUWXZaZWl3SnVCQ0owblRRbVZ5VlZTa1ZWcFpiWVdkS3dnOTJmZW5SWENKVm9VN053Z3JERmdWQWhVUEtMRzlvL3ZYcFdZQ0MwR0FnaWFKK0xjZjhBY1ZHdGtaTWZJNk92SW0zbTdxVjhGZlJtdHdVQ1ZpdXZYcjZhaFBYNXpyTHN6K0txMm9pdm90RkpxUWJLQW5acDI5N05MeUJTWld2VUF3YjdrUllKY3JiN0tQdlgzQkFTSlZrcjhNMTlTQWI1UGpNM0w1RHlyaGNhaTVmZHVVNzE4UzE3OEhsVzVXSWpQMG5TYUErVG8vT3dmZ3FtcDFHcFl1eEs2dENYcWhFNkVxSnI3OXdEMUYvS3dBK3hBckJ0QmEyK0lLTGZ3YW5FV3BwZlowUFVaM0pWV1lvdm9ITFptS0xiYjhuS2ZNemFRbkdmcVZPbDFnb2NQVUducWJFMEo2bmpyZ1JOdzFJWjQ2eE9xQ2FYVUNJZmtJZytPaWZNQ2dDRDFBWjJBR3pqNVZXVHMxOHp5WXdjZnBYajQzZ3NKTnRKUzVNZXU2M3N5VnRrNFl4TVBvY1hEdzZzV3V1N2lNbzVXR3BiUSttL3FOVFltdHpRbWhxYWdtb1BybjVtUmo1SDJZTjFZMkg1K21wNmx0bVExV0hqVkZqaEptZjltV3JaRmpWSEdkMzZKZm1kOGdnTHhMVjQrTUdpT3pEWkJucWVvY2xGeUJTMWZLNC83eXpHdG81TER5TFdyVHpWcllLL3Jvek10dXgrUngyLzRiRTVIQ0dUWDNHVmtKaTFzVnl2MUcrVGd2UTJJR3RvNnN4VVpCVVVyV2ZTWTJUajVWcnJUbFhXWWRCWjhGMFJPOXVYeVZtTUZTbCtXZkZ3VFkvSFo5dVVtN0p5WnhrdzJlN211ZTdQVmxLYnVTTmxXVlpnWkhOM0JxVng4dkpXM3UxR2M5OS9WTHNoYXFpMC9VZk9adkpQVW1QL0FMa3hzZTFhY2p1Q053OGxVTXc0eHllU294TitUTzVYTXliMkNqQ1d6dXNweGdwQ25wNDRMVS9jc0dSVzhmcXZIQ3Q3Q0lxNkdVbFQxZFlLbm92TGpKd0JlL2QxejhZWUsvdDZ1VzVGdjR4bVBsM1k1VzNPeFd5K2ZXaCtIZkw1Kyt5NjdNVk1XbFdzeWY1bVp4bFoxL0hJRnV4L0xrNmM4angxUXFBRlhUSDJLeHlhVVloRXZ5LzNHU0xhN2Nhekp0RDJIRHRXdnF0dEJzY1ZyanBRZnNXNGVST2tRV0VGWFhSOUNtK3Evd0R3ejdiZXVsdnY4UGpyVFJyeEFXcFZteHdwNCtwYzdzU3RkTkIwdHZRdDBITTBWWGNuVmtMK21hdjlOL1QxZmZQdHYyNlRPdXo4UGthUEJiaVkvTVhrWFkySlRpZlpqNVdDVUZseVVtekpzSUZweGlCU2xLbm9iVmZYajIxdGhEbFpoNDVxMkZPR3p1R0xBaDkxMTJseVZpZ3NObXF2d1pMV0ZycmNqSWFpWldNeU9MampCM1krV3RmNlR4WXdKVGs2TDNvRnBxUGVsZTJSalc1VzdEaVZONTFRNWxmanRGa3FyZE1rSEp0dHF4MVN3MDEyb21teGgzQjNaU1YwUWNXdnV6Qk1ldXM5VmM5ZnNOcVZ2L0l6NnJUYzhxaGR5NnNhRFNoU3dCUC94QUF5RVFBQkF3TUJCZ1FHQWdJREFBQUFBQUFCQUFJUklURkJVUkFTSUNKaGNRT0JrYUV3TWtLeHdmQVQ0VURSVUZKaS85b0FDQUVDQVFrL0FQOEFqUjhJd25JMEd3M1IyQ0VkbytHZGhoSHRzUDhBaldIQ09FYkJ4SGlQeEI4SS9EQ1BDVWJiSENkcDJnb0NPSnNvYlNFVVVVRTFRRTdZYWFiRFBHVmphVlpVQXVUK0FqZTM3MENzMmlNd3RGVWNOdGtndXRDQmtFelRUVFdmdHRHMndWTnRUb0xsVTZDdy93QjdCMjdmMlZrRWxmVitGY0t4Ky9HSlkwUWRiMGhDNWtJbUc1alMrS0oyOTRadEJNQUdLRVJjVk1sT2tJb29vb2hIZU91UDdSM25LNk1FbFdDMEt4YmJmNzhRa0h5UmgyUURNREJJME5nVVNHT0FiUXlONEdBUWROVTdkbkpwZThZQTdvOG90YUNPa2NQeTVPUDcreTZYNksrVDFWbGJacndDZUVlaUowc3JDeEVWSGRjdmlFRm9CcUoxaVpxQm15Y0J1L05SeGtBbXhwbXhYZzdyY1I5UDduVGdDRUljT3ZDTmcydTV0RnpPWkhMcUQ3VVFhMXJUUU01aVp0MzVSZ3F2aG04T05RVG1iQ05aQlFnV2JvWERTbWJERUlnOXJjSVFRUVRVRUVPSXlCbFBBOFYzeTBuTVRDZERnUGVaVWtHWFJNRHJVVlFoa2l4QVBZZy9TY3FqUzNkRUN0ZXRiQk9JNWFFVFhTVEhxU3I0SFN3dFNlMytEVzBEV1VJZk1IcW0xRkNmL0pCbjNUQzV6aERRQkFJdG45S2M0RzlEYjIwd2dSSUdhaXVEQlRaM1JQV2hnVHJxVTRBQ3NSZnYwOWxTQnJUdEdPRVhFZzZ4Y2Q4b2hybXVBYlg1cGJ2QWowUG9uVkRpM3VSZU5Zc2pVOERvOE03NGVDYVJBSVBjR25ud25tYmFUUlBnakdacmU4aGVHWnRPc2xFa3pjeWZTYkl5V3hQbnJncStQWDBDa3VGQ1NNK2VJMFRTVEIrVUdzV3FqZUlrVzZZS084OTlZSU5CYWFZN3FDNXBBTTBtY2dJREZSbnlPeDI2QlVPeURnaFIvSzNlYThnV2N3RXRjQWZwYzF4VEN3dkFpNFBVNHZjcDI0SUJEdnFkM2Nab0FNRE1yeFE1clJyNnpyQnltOHNYL3dCQkd5YVA0ZzhBMWdrekVBMnFWY21IT2JWalRHVDNPTmdNaUs0cWpZVDd4NmtwKzc0WnBHVDNUcEppOWlieWh2VGNpQ1BkQ1c5RUFKenFxMFVlUVZndkpFN3hFU1B6TXB4Sk41S2NYYmxKRWdVN0FUVkV3S1VFZTVrb2pkNjZ6UkNKZk83Z3RhMlpucmFFN2s4TWJyQlRVanpKQW1kQ3NSVVhnVktkSWQ4c1dPZ29uT0lnUUpnR2NBQ3A4MUFIL1VhSFdZOWhSTTVkNXBqVG1xZ0hOaXMzNndWZHNST1IzdE1KaEJzZXVpZFFDeWdtWmd6Q1B5a1QxR2lORzA2cDNMMHZJVVFOTWFYeWlqSUUxNmd4Q29CVW5QWUlUdlNKMHBjOTA0ZzJnbS85bTZhQURvaERBWTZod01FSDcrYUNFbnJYN3FXdkljMGs0cFF4NmhEbXQzSkpBOWRkRTNTb0UzdkNKRFJkdEFDSnFieDZGT1A4YkJKczN2VVlFVktiRFlGVHpFbnVhd0tVVjNGcFBheDlxOXdnUUdtNXNaeENPN0VIcFExSFRxcjFpbXltUFFKeE1tWmo5cmhhcWxwVGdEZU5salR6NjY5MUFpb0srVUN2WCtnZ0RINTAyQ040eWVwL1FuRVBhNldrWHJOTHdoelFKN293QWFmbnRsQ2pCSTh5UVBhVVkvVDlrQTQ0T2dUYU9wNTNxUGRHdVNzRWhDa29DWEdTakNxaFZzRWVWUGZLQ3NzekthQ1IrMFgveEFBcUVRQUNBZ0lDQWdJQ0FRTUZBQUFBQUFBQkFnQURCQkVGRWhNaEZERVFRU0lHSTFFVklESkNZZi9hQUFnQkF3RUJDQUEzSDkrVWlDNy9BQ0xsZ3NubjZ3Wk1HVlBQQmRyM0JrUmJ3UkZ0M0JaQThGa0ZrRCtvR0UzQVoySDRZTWZvZDRlcG5XRVF0b3p5R2VVenlIVThoRUZwZ2ZZaExhM0JacUN5STVpMndPWjVEQlo2M0JaTzVnZlVGbWhCWkE0bllUeVR5aUcyRndZekNZMkxrWmpFVlhZbVRqblRqSHVKQWg0N0tGb1NIaThjb3FMZlRUNGVxUGpJR0t4c2ZYMHFIZW9GOVJWamZ3SHZ5d1dDQ3lLOERpQXdHYm5rZjN2eW45K1dHeHBVbCtRU3RmRzRYeGNNQmJjWXV1ejBTbHRoTFF6N1l2V1NTSGYzNko3TnVEMStONkUrU0Y5U3pJN0dDNFFXaUN3UVdRV1FXUVdEVUZvblF3VWdtZUJZdU52NjRUQ1hGckxNempyR3YreExPcmJKdHMwTksrUzRZZ3JrOVJvQzJDMVQ2Z1pPc3lHT3ZRUS9ycVpwcHQvYzhqQ0xhOEZ6aUxjMzdXNHhMaVJCNHhQVXhxQmtXYWxkRmZwWW9GY3R0R3RTMjVRL3EyOXZZQmR0UnY1VG9RZlJWakFQd3dEZmZqMEowTTZhbm9RNmloUitCN2k3RURhM0w3MFBwVmR6T1BZcFlXTkRxVTNIc0RuUXkvSWc5TmJZRC9ON0N4OUUyR2FlQlhhSlhIcWNEWUxrZmZrRThrN3RCNU5iTFZXcWdZNy9BTTFZMlZkV1hyQk95Q0ZCUHBVSjNvTnFWNGpXbUp4enBQaHFxN0NaWFFkU21RVXVEaW0ycklYc09ReHV1eVBGYW83VFRnYlBsY21hY3hVY25VU2k1dm84WGtrYko0cS9leU9MMEpYeGxnSHNjZDZpY2ZTUCtTWW1JbzlxeW9PcTVPSGo1by9tT0p0b08xUkxFQkJOcFU2Z0IxQ252MHRXeG8rS3JyNklBdDZpbkp1cmJvTG1kM1dBQlYxUEVqVDR3QjlMVXc5Q25GUUhkaW1yV2dWS29JUHVkTkNHbXR4dUdteW82aW94KytoMXFCQ0FRZmFOQWdNcHhMYjYyS25oVmVyeXQxMmZhcCtnTmFuMUc5L2VQU21PbmNnT0xmWVVuN1FCUm9JZ2M5WnF0UWRkem94bUNyMWlzem1Ba3J1S2UzOFo2VTladnJDazNBM3FhQkVVS0JxWUZ4REZFTCtCdEtQTU9zU3BUZ0hiWFdwc044a2lESlVEMzhtdjlESldmS1NVMXZaOWtBRFFnWmRnUzRmMjl5ZytvUGV3RTBUQ08vM1d5a2RUdlJJbWd5K3pVLzhBMUxGQm8rYnQ5MFpocHQzRFlicWRvdUtmOUpyc2dkL1FxeVdaM0x1Q0I5alVMaWRnWmgxaWhqWlpVNmIyWDJXMk5LVzdGQUhiWXZiVkpFb0doc2c5YmlJbTE5eERyZTNHajJDRTJLZXdZQVQ3K202dC9Gck1NSDJsZUF6NzF4MlppNFdHYTJ5TTBuRDZKZ3Z2UEZWZkkwcGozdk84RFF0QnVMV2Q3SVZ4QzdhMFVzSkdpTCtub2QzWk96Z25VMlBJSWhCV0k2cVNwUzJ2dVVnWmFyU0lsMWFBeHN2SERhSHlxUDBjcWhmVXhzWnI2VGRLZUpldmtQajMxNGRONFZGb3JyeGNlNlpQRU01clNXcDBzWVFiblFUck92dmNCdDFQN3V0RUs4RFc2MU5NdzlnTUlXc1g2RFB1RjNnSm10L2VnUHBVTG5RemVQdndHQ1c4TnhoenNqZGxTaS9qVG9kRVl1ZE1WWXlpOHZrZVFaRm1RN3M2Y2pqaWk5Z28zUGM2L2dmWHVEYzl6Um12ODZFMFA5dkRjZGc1ZkhNYmVWb2N0M3I0QzZ3V1BUTUZjek50dG9xZG1SakUwbGEyQzZzS2ptcW9CQ1RPUVZycmlBeUJHMEpxYWkwczFKc0Q4ZGY4Vkw2N3NESm9yVjNGYkZDNC9IdVlsZGR1RGVDUkFQd0ZuQzhtY0N3bzFkTnVUWXB1d2VCK055Q1hqSlJFdUYxUW8xaml4aUc3YkxWc2krNmFmS3hFdnF0c09xOEhpNmNudmJlbkRwbGNnS3F1UjQyakVzSXFuRzA1T1JtTFZSeHZFdlZYZGkyWW80cWlpMnM1U2YwelJXdDV5Y1RFYW5vbHVGWFJqRjNyb3NzY0xPQjRqR29SemJid3RyOW5vRlpFVGlMMndQbGpENHZJem5DVllIOU80R1BnaHJUbU10S0l0eGExQ3pWK1QzQ1crS0ZIMlovNUNBWldYUmlSM3J2eGhXL0hjb2NXb3BWbjBabE9mWWNsOFNuc2JYdzZVOG5hbFZPTHd3dlRPcHA0K2xhcXVIcXRzdUtWME11TFV0YnZYVmE1ZXNPL1lnNG1UZFZZeFFZejNhWjA0ZXpJeXhySDQ1OFBGYkh1eHJLY1d2clVNNmh6N0dSNDAyYmJyaU52VlpjSERBbWdFc3VvOUwwK25vNmduZFNCK3pzdlI4alV1VkV5V1plUTcwTm8rQVdwdDZxeXRmZGNMT3RIRnRpbmxFR2Z6VmlWOE5qMGtLNXlMeG05alk5ZndhTzVMaHFSM295UE1pVVdWMWVGQVppK1ZOM1N4dE1FYW5hMmVrWlNnSXZ0ODUyVXZSS3lJcHJDbnNlMWZzeG5OMUlTR211dkhXMFVPQ2hTWEJBbTA5L3V3VzNuVFk5dmpScWxDMkt4bUk2VVc2THQ0NldsTHF1S2FSNGEzeHQ0N3JheEt0WWlOc3h3VVBxcy9jcXMwaDMzc0k3bnUzdDRwZFFRRUxIWUhpWWlWbEJzTllBckhyLy94QUF5RVFBQkJBRUNCQU1IQkFJREFBQUFBQUFCQUFJUklURkJVUkFTWVhFZ0lvRURNa0NSb2JIQk1OSGg4UlB3TTBLUy85b0FDQUVEQVFrL0FQMEFoOENmalJLYWhuQ0dkVnBrN3JId2JTVDBRczU3cTBNY0Q4R1VMUGhLUEFvb29vbzhTaWlqNE1JZnBGSDlOcUtQNkxsZmhDRkhnd2tiZ0xLTW9IZzJPNmo1SThCWEVVakNQR1VVMElYNktBRTVPSlRaNzJxQ0Y3NnEreWJDSEhIQVNFMUNuVFBFSUswS0hoeHd3Z3NjQjdxQWd4RjcvYndoR1hFSzU4WHIzNDVDMThaaHhpTnRWVzZBazlma21jcnh1TWtUWU02MElUWUthZ2dVRUNnUjR0Rm53bmdVY0lTTjRpZWdPSjFJVUZ6Q1NKRUdEWkJHK3liNkE3V0puOFdoWjd6UFdmRG5RYS94d3dzSWNkVjJXT0puZ09BRkNjb1dUWU8vWmViMmJZY1NLT3RURVZQcW15WFl0b2duY2RzNnIycEw0dVk4dzIvYndsRkhnSjQ3Y1NqUlJDY25TaXFaR1RyMEhxbkFCMCthNkkwT3RvdWM1MGp6UzBDTS9YZFJ6YVMwR0NOdDc5VmtXNG5JYVQzOVNNb1IzOEpSUlRrNUZFb2xGSGdFTXFuSFFYSGM0dE5uMmJmZXVQU2QwSlk1MWpCTVZTeUtrMlZiczMrSTF5aEpHZjZRQnU1Vmo4NTcvTkQ0QVptWGFqbE9CdEk2WGRyL0FJeTJRTVFKeEc4ekc0VG9CZ2dia1Y5azlyV3RJTHljZ3pnZDl0RkI2cklKUmlhL3JaU1NyazM1ZGQ1MVI4R2hnK3VDZ1hOTFpNRDNZZHkzNng4MDJpME83QTRuYWNyQThEWmVDemxQcVFSNmo3ZUVlUjJZRWs3ZWdOd3ZaeUhDWi82eHBBR0hkTGhPRVRJRnhNSm9BNVlnQUQ1eFo2SVFETWRZNGE0UldrNi8yRTNsOWt5cGFSWjBGbk81QTd3Z2VWd0pBRmtSb1RqMTdJbldRUmc3U0srY2NHY3pqcHVOVUR5eUNKTkVPSTVtbU5RV2hPRHkwbXFJdkc4Y3VHcHYrUnhKNW0zeXNIUm9peVRxZElYc2kxempxTUM0SXVnUmx1NmY1NWdBWXprbjdRUFZES2NROHRKbUNRMEM1Z1hwS0I1Y3REcWNSSkdOTVRCNEVjc3hHc3pDRWtrTjlUZDdBREpUQzcydVNab0VFajVZcE1BaXBHUU1SK1U2RG9EUktNSHFuZTdVZUFVZzNsYVo1U0QzcVAzRkZNRFFNUUl1Y1IzRTJ2Wnc1NW1EQk4yTEpNVnBrSm9FbWVpTU92RXAzTnpOQW5YbUpJSTlOMDN6UHR4QlArd29KZElneEVtaEtiRG1tNUZqZlAwVFJPOFduV082TmtFZlFwOE8wSWs5cVdIWmc0UGJNU3ZhQnpaa1JOYnBubW5QOElrQ0luVzBLSXJnTWI0VXoxamhSMitzcTFwRmIyZ0ltYUZDL3dBSnhPZ243L3dqTGpmU0RkZE51RkNLaXM5a0phd2h3RWFhajdGR0c1eFFBdDN5MjNUaFVubEpnMWlUZVVKdWprLytvdE5seHhyOUVaZVNhRkFOSDNKS3cwR0tBdkl2dlY3bzM2MGhJeDJKd1ZqWGgvc2xEQXh3dERnZmQrM1Q5bE5vV2NLcFJSbUJIb2g1WEFUM0dzNVJwQ1pSdDVnOWhmM2haSjZVTS9Yb3BFWlJ4ZjRXRnJ3Mi9LMTE0WUtQQWJRalMvL1oiIGlkPSJjIiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIvPjwvZGVmcz48L3N2Zz4=", + "description": "Produced by volcanoes and industrial processes, particularly the burning of coal and oil.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'sulfur', label: 'Sulfur dioxide', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sulfur dioxide\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 600) {\\n\\tvalue = 600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"public\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#3FA71A\"},{\"from\":100,\"to\":200,\"color\":\"#80C32C\"},{\"from\":200,\"to\":350,\"color\":\"#FFA600\"},{\"from\":350,\"to\":500,\"color\":\"#F36900\"},{\"from\":500,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#3FA71A\"},{\"from\":100,\"to\":200,\"color\":\"#80C32C\"},{\"from\":200,\"to\":350,\"color\":\"#FFA600\"},{\"from\":350,\"to\":500,\"color\":\"#F36900\"},{\"from\":500,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:U08yLXZhbHVlLWNhcmQtaG9yaXpvbnRhbC1iYWNrZ3JvdW5kLnBuZw==:U08yLXZhbHVlLWNhcmQtaG9yaXpvbnRhbC1iYWNrZ3JvdW5kLnBuZw==;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Sulfur dioxide\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "nitrogen", + "sulfur", + "sulfur dioxide" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_temperature_card.json b/application/src/main/data/json/system/widget_types/horizontal_temperature_card.json new file mode 100644 index 0000000000..93804c104e --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_temperature_card.json @@ -0,0 +1,28 @@ +{ + "fqn": "horizontal_temperature_card", + "name": "Horizontal temperature card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAsVBMVEUAAADf39/u7u7x8fHr6+vl5eXf39/f39/r6+vq6ur////z8/MjTMc+Ys7IyMjj4+Pk6fitvOqRpePPz8/x8fGtra2QkJDCwsLy9Pv5+fnI0vHV1dW2trZYWFg9PT3n5+cwV8vt7e1aeNXW3fV1j9zb29tog9laedW8vLxmZma6x+5MbtGCgoIvLy88PDyfsOeDmt+enp50dHRKSkrU1NSRpuNMbtIhISHd3d0xV8qdnZ2QKY5sAAAACnRSTlMAIN/vz6AQML+/B+y1FwAAA4lJREFUeNrt2glzmkAYxnHTptfLsssNCgoqGPCsR8/v/8H6LjTVaqsZ66TGeX4ZDbDMZP+yDpPRFgAAAAAAAAAAAAAAAAAAAAAAAAAAAMBzenv3wr1tae8+GC+dd3/Xat29phvw+q71hm7Cm9Y93YRXrVd0ExBybRBybRBybRBybW47ZGiKPKL/IurY9pAalm13dofs8a8te2zRrr+EmOZIOLTVZT4d4cV0GXYm2Mgi9lEwk6salpmJSZ3BA81Jp0Is4TjZgLaU31UexYqUkiqWPG/eJk8SxbHnSUX+WilFkmJ+5vGzdYQYOLkQD7z9SYiPjim+RdTIzcgS+ppM+KSPI26MnhASmQPaIbtE3elaBvNpb9VdynUwl/F82qVeFfe+z+V0LgOfvvCunK6qJZ3roU5whLCITL0oooyfG6MRUWYT2ULYepJ65HTIMNsPUbOgmgYB9cgPeDdYrqpg5vV4bNrz+UATomgWrCo6lxCL+u+Ljr44Q72+OK1hi9FDVgfmxHjEfELI4jCkp5S3DVktp0uleM+bedVvIXp5ncuyomaBWTQWgthiO117MOK0SPD60obOU64IHS6tYF3Jx5BZtVZqXa10yLya+d7cl71qpkO+97pL+je5GNQrqLkO5sHbqENbJ0LMBzGgfd5Ol7dzwGse3va8f8PvcusyIUPBPtPfqYAuzBSsQ0yvKYcuEzIR2oKe0cBkHWLDjDuOhAyfHmKL2reIHvXpj/oG7Wi3+ZHSP7JM7tgJcfaXRvT4Eo/z/ETISDTG9ChpZs3T3Gx4tvqH2/puyr/aZTP92NcnuFQPGf12nXlOR/5zq3nlR2L0x3uNHvl8IiQXjc5uSOom7cRVUhKFRmj4sfwqwzRWYT8xEtclfdjw9YarSqOUxUaW7XM6JtsFntc59uHd/1NzxewTIQvRiHZDvrqJUci0KJqQhEI3LVJ3UxJXuXEd0ncNHbJJiiItSnLTM+7sIjO1vH7Tmw+ZMGmfowdMwcknQqKJ0BzahoSyKH0jCeM0JCpC3/C5IyzT0k0oMUpX1SHtRHIIH2uXYRGHCZ0T0hjwjm3qDYv2NQOZQ6dCKMozMbHpmOOTbBdS0QVYw+hvA9x3GHKGPh1l9Ol53fp/iC8RQq4NQq4NQq4NQq7N/e18PH0zXxho3d17xkv3/t0tfakGAAAAAAAAAAAAAAAAAAAAAAAAAAAA4Jn8AJ01hSMevW95AAAAAElFTkSuQmCC", + "description": "Displays the latest temperature telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"thermostat\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#234CC7\"},{\"from\":-20,\"to\":0,\"color\":\"#305AD7\"},{\"from\":0,\"to\":10,\"color\":\"#7191EF\"},{\"from\":10,\"to\":20,\"color\":\"#FFA600\"},{\"from\":20,\"to\":30,\"color\":\"#F36900\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#234CC7\"},{\"from\":-20,\"to\":0,\"color\":\"#305AD7\"},{\"from\":0,\"to\":10,\"color\":\"#7191EF\"},{\"from\":10,\"to\":20,\"color\":\"#FFA600\"},{\"from\":20,\"to\":30,\"color\":\"#F36900\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal temperature card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "temperature", + "weather", + "environment" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_temperature_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_temperature_card_with_background.json new file mode 100644 index 0000000000..25168535f2 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_temperature_card_with_background.json @@ -0,0 +1,28 @@ +{ + "fqn": "horizontal_temperature_card_with_background", + "name": "Horizontal temperature card with background", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC91BMVEUAAADx8Ozp5uaalZKJhnyzrpXDs5dwenqsoYlaaGnLt6D39/CwqZfv7euamp/08u7n4eW9sJH29PDo5OTDt5vl4uL8/Pv7+/r5+Pn39vf49/j29fb6+fn09PX19Pbz8+8jTMf39ffx8fT6+vry8u7z8vXz8vPx8fPt7fHw7+vv7/L19fH9/fzv7/H09PDw7/P39/Py8e3v7vL08/Do5OXv7uro4uPs6ejr5+Xr7O3u7Ont6+js7e/29vLq5uXh4eDj5ujKvKHl6Onv7OjFwc/h5ebw7u3n6evFuJvn4+XIup7f4+S9vb3t7O3p6+xyc3Lt6ebHw9HCtZfv7erX0NjPydI9Yc2ysbKjnJzk5uapqamdmZysrb62rI88PDzy7+7SzNTT09KYlpm8sJGmteXJycnFxcSxscCrqK7IuqOjnqHKvZ7u7u7m4eXCv866tcCip7jDtpzf4+aMoN/V1dU9Ycy4t7jNv6G5rpCOjo5YWFfp6Orm5+fMx9LKxdLAvsumqrvMvaXEt6CfmZm6sJe+spVSc3n49/Ts6u3Z09pXddPIxM/Dv8q6uMa2tcGpqbadm6K/tJlZdn3Z3e8wVsqioKmrpZqWl5B+gH/g3eByi9nTztjEw8SyqKWtpKDFvJ+wq5yon5uTlJva3/C6sZ+0rZV3hIV/hYRqf4Nwen6Mhna+yOnc2dt+ldu4srmeo7OlpbGdnqiooZ+TjICXi3Tq6/Tc4PHP1u7By+vn4uXPz8++u8ezsLytrLm2ra+oo6akn5WimZWLi4tLbXZlZWXr5+ezvuequuearOJKa9C9vMuVmaKMj5ScmpOYkYiLi4aBf3bG0O50jtrIwsu+uL6vq7LBtaOuoZSyqY6SkI6hlo2klolreXxKSkrj5fHBu8OFjZulpJiGkYx+iYuVj4efkoaKiH+bjnovLy/n6fO1wug+Yc22rYqAe2whISFkgNZhe4WklHhkdHhZd9QwV8psg4u6sqVmbWuCmd5ng9fVzb/QxrZ2fHJ/vX40AAAAFnRSTlMAICAgICAgICAgICBEQM/f39+/v79QWvZnwAAAD0RJREFUeNrswYEAAAAAgKD9qRepAgAAAAAAAACY/bFHjRgGonDidlkSAqmnEQY1A0b+w4KkMBjcCXyAPYFP4FwkkIv4AL5X3hPOsptNlUAqfdK8N9otzJdIJBKJRCKRSCQS/8nh/hK8rp+HjMk6cDIEfvkrGSfDZSO5sn5F1Dg+iEqei7gc6bCQ3DBVCouqlat9n6z3Qzl1Q2F2xPxEYQqABWkt7hVVxFZdV+F8pefxvuv7EDxpGtytAahlWd5eSBuzjIzlWI/jWM7z4zM8FAhwDsNEwcLgIIHIq/dIPTWVtfOHEYeHEfLdRCTKWSoQKEHnRoUybAKPqYcKmXoSenq0oSEB1bbNtm0tLHjLs0dkXde5no93T07VGVEYEN2ViJG9NTh8qRjGqT1p742J4mqUf55tLta9qBHbFhwLbowo8kk7GaU0DARh2DsITZCCDwFBUkJC0ihKfBECeRNygJ4g5+gZPEoO0Hv5/dMhDbbYrdV/d2a23ULn45+FpH9hr99xBQ6BvH7sjOMZIAoVwVHtoKlqlsGgoRrq7vYmzTJ2hgvlHoBkDlFU7ZvsMRNqUeqrdUV789+5dYR7xD4kXGG5JgIvrBeG7B2SXoZwAMUmS7M1gRjGk4HYZFGMRBzDMHb1CIhkfZYqal/ZmTiIaP2YcUujLrfNgQ/e+UCavAAiAkFYnfnhLwYQODRWHDAFFDg+JDimDQ4kxJtxuIAZh64DBBKZkub5lkUmHasgTqrRJqZz0xCS1VQ1TVOSq+QfVTKW6Z5gHgAvN0VRyr9NsdEmKbdtS7Bn6vu2J/W7Do11VwNiWq4WM/HhLxVZRBERriRaRsulhee54nif4ocKW3gkDrJdoECQ1eXA3pudwiWSs4pbgQzmCJMV0Nr1phCXUCQ/csSWYgJLRoE0gOSL/5VmymuIkiQ53TzLTzM918gdyQMG6WrTxBKE8b35M3pyEE1WHtzt6tcMUsgL9/4DdMdGAhk+BdLw1ldo1ut0tov5pd9fPlkBfhxTqNdz+uK87HnThqIwzK+4FrV8dQylS6LuGagXlA1ZSMSDI1myhDyxWUKypaRjBGsY+MjGmDJC+AFWgLHZvVOQyNAM7db3GoOR41RRHq6NsZf38TnHwLf+j3a7DRFwcwqyb7y4gIVX9OnDc/6OwThupCR/Xmz/ow+NY5E4JPbzF/oTS4CkJtHuA7x7uJF/Np/PYoNJo7GeJHknjXVeVVVxtG40HtQdsqr26+16vZ4ri2m/OWVJyAJt/tIiKci5D1p7zSya9tvFeDN84pBU4mFDYDPNA5eAsVZjpkaXjIkqy7Lbja5MZZnL2Hi/PhzWh7lyOWktLPCT5i49HTWZdhlo501HY5rtYCG3rTGGA+bYTXEe1zWNOQznsR3m+y0DZMcC6bGedmnpDrq0yefVBtHAXdIL8ka4xnTUXSD3mKgzfjbIGHGucNC+urq4uEBF0l/sEClA5AhTZ8zqWY7n91Z62HJCz2qaoe6zVeD4ptXUQ1s3NYtZvtPTfRMKLFMAZD6UPsUTMSADpZgRoXEMGiB+h8Z8x3jJueFyDsGFovBRl8aKUhLUr6+ua7XcSZbI7SsRx/L8S89kW+Z5js56pq97W2ZJrAVBnBAikiVVt16gp/sfiCJk5cduDxSW5KIWE6KG2M1w+xdkxCL39NyhkcI71FFAyaVuacewBo9a1FrltMjilYgdanbzICIFrcDs95HcDpv+3Z0u7UVWdr+aOAiBtAHCx/ETh5jpdCKmQTigLCMe3X+FA0R/7Dzf4w0FKQkeXPdXqfTlS6EwrMDjGj9RMOxpEbYTkeKJhQjr+T5EpK0EkVVgabYf9ETy0F+17JVpWrpVtIpFzwpakUBmC6EGmQpAFchimNFgMj8SSUD+W6LHSKRQwIJGLFLJfcV/gc8nyVNGiCx/05OUpno4utOrhxNFvGErFs/i/jnDQdoAHCvk06h7ZPCIqHwnouxFSgcgch95JAy/VyoQ+UeHGbO2DURx3N+isrBcNWqkIjCBdAoUtJhA4FTIUIuEQlqIGg+uF8ta7MQZXTfJYi9xvRVKIc7Qobg4Q7aQeOsXMIVM6dJC273/d6dIF8v55Y47WRneT+/es/BDc+vx1lO5PL/rACL3crxJzdOQgIDoonymBSQJVQo/iz+eh44OtkkjP+rpNV4SehcHCiKLkobIyAKQRaBRLWa2ViuV1S0pzCl6OfiZavnYpMil25BsMUM6CdFx6tfBRR4eH0u8T2kkgkyQiIzZhTAOVYK18GGdi1RcsJpEbFzqnL8GeMBHw5hnYDgVWcHEdJ17CzqLkZaARkweTYo8+hoxIpFFWcQUC9ouFzjp96ewsCzrVbFYbbUyrttuuwUj4Y8uGMWfeJiNiqE5uTdBTtGUSk5r5pSm75hNpRkqJoXvn8Ml8FWVrppNU8WYm4lYQwSPJc93gHtc3WjEjSjqU/0q0oAHjYWOXuIifb1uCfYm1Vb1PRdpV+aJoHFychBxfM8Ix044NgymeErZ94OB5/hDNvRMzw/w+JnihgOIDILBMWPBIPCstIYoCQxBfoabK70ODw6+L1ARJf2SS5gEb1I4czWxnEJiCexWWweHh5k2UVASasKjRPscx1OMoe8pAUMSFAUiNH3XdwZjz2UuiagqU53AgYj/mo2HQcBU1uYKsgeef+SRJ4tERBML3j56dQIOiLT0q6fXuxS+XBU13OjUdb0+XRK8aE0mk1amjZ9V225Wjfla0oltNP2IMhsHrOx6jOJTx+xc3WTITuiEftlERgaOSiKWxyASDsIN5gVjxmYyQTMpiASNT43o6xElHKaLOjad7kKKbbrRq3EPe8m29w6qrbNWZm2NTDQNDyhLoHHgta3zDVfqHLIcHqQ6F9wojNnGXYlI4VYkSQIMZqCTFNEdTe8qWBjE9KRrcQ3bXl5eLp4dgMy73WHjbcOyTELT6CRadLWocTMhB253/HWhgZvik6gh0UzYWJEsRF1IxKlIOwCTkGKfS6xBIutHhyDz4/pL6B09K3DwL7Q8KRRsi7wisEXHENsYjWQeZVMkWcDCibsrEMt8AUEcPBLAY05jCyBCFHd2Xu7vZz79+/35evP5ysrKMwF2dAEZC5gWYdvWLcIYotyTIkP1iuAlC5lUf5IlZAdZA+H+J7SOUSIGojAA72k0BETICbKBYUAhncVA+hRbiGksTJNFtpFNkSaIvaTwBh7BQwjeYRtt/Od/4/BwFvbP7O6EbPG+vJkQfuniI0I5cNdf3vdj266maVkOH9baB1v7WKSytiaMNWf0ZTJX0GiB5Ej0plAGDBXNiEspFnvJI6RQAFHkrCh7GuEAZLebpkOPVJLSp4IFKtZcSwQgUlzzlHPVFokHUKFDSbq3aeCLuB9niuHLKxCpOknuG4EhjmyPldU0q/lrnl+XZ+dc3ztXlsaYruuMAQZhfySWEShOhZKnFP/BSBzp0+noviYjpPBHIb8cSJheREfVtON4+7nabB/vhuHbhRhzLaFFAhtPiACTzoqrDxJScK8VJs2/dvBtgxLNCBAUx7V8In+QmxHB0rpfb662w/rnjQ5hRAuDwmVOUxcuBUkWeqIhfEQlEP5JbQ22xAu0A5BfwstfN20oCuM8QNWpD9GlD2HJg+UtVqwiGaTIHuI/EjZdnA5Q7ECkyCQ2kWyGCjCWISGKDDhsYEKTrZB2AFJoK0FIUpYMmTr3mBfo53OP7rUH66fv3KN73204/k+y6a5vPyRieu84CDIRAsNwHJdlQUYQGcE4HEEUDuG4O9u6t92/d649uL8trupnJycHEDcHNzc3B2GC6cnLy8lZfVSO8jRtmCbL9o7GqEbubIPGk74neuQ2+fnw4uJie3sHAgRfDg93yHAKazKlbaFwwYMjhobC0e8Tmg/Mpjlst/W20x46ThuSYbJB/31vd/d4NxYq3BLQcSUpk9FjeiyWaeqSJEUIIpdjCILAVBUXhIog47KiIBVElvFsrfbjR83lOEVRNmSWbdvW7eoMdABNAgawnNXrp6v9Unmfpx0H/mqYU09cLMz+Fcuy3ji1Q6IkGcJ9JtOwINNpVNtKa6kUcIzHZApNJBJ5gNhCv3a/Jo711nC4dEbXzogegRyapvfn/K+fVXbar/KtVkvSpWYzk8lAkkCx3SBEkiLM83N8Lx6PP+9lQ+XAIayCIBACpyCcWoB3WDKUqqrgF86p6p3rgl/F1cPDqnh7b9sDa1CsWiWfj0Ypnt+PUjRNUZRhUBQ/FUUxiPW6KZLU0un8RETTWgJFQ5CUNl4svMVkMul+6naPjsQjT8x7vDFcLulGcX59fVp/GM2L3+ePf57W6/Xjl19PTz+rV0a71Ww2W7EMEOl6C55e0FwuI28K4AmQzOLxWq02m8VB4FASx3CMq1QwKDl8gwU1h0ARYkQhWyiAfVCSqltwXRXnOK5w51qD24+nvF8u+2UfYELRIIo2jLZpBqI4EcUp2xPhhg2GoJpGonmvXypZVqnv9cVe771XKvX9LxT90PjeAIxG43f99PHxz+VlZ93pnJ9frs87nfW3uWPw0avplGWvDJ4H7nYr0KXXkVcMk9uDkY3nskACQLUZwzB7TI5gQsJZIZkEkwgMF3BBERABq1QQRdlQAVZSBbAkJsuwrQDGrlrlcrnk+9GB71dDGAr0j93y900QiOK4CXdwGtOk/5UbCZNpt7qYdjBu7NTRNAxWFrU2rQSmJjBXsbNA0h9EYTFRpzLAUJcO/aJN0z/iPuHe+97d48fjjhe0oiE33Lb3dNPtFn95rdbr22AwnnkeXvMwmjanw0E0HPvB3NiZVj4xjUmSJMbODne2nbl2GMppaDvOJggu4iyO463j9/3Zylt4Z8/N92qpVD1VP1W1eHZYVWnoj9d6p6N3FAXLhA2nqliiTht9RW8reqM2qmGrwRQOn9UVCsXoUCuQ3sds9nD3NVgP14HZvx2PkYy20LTzel1DJp7m9bFGl1OUhd59r77oL00ryU1zHswX3iqKonjlL4N58pLklmXl1sSw7W85zTI33cvyXkY2jmGErpumoeM6G993Vlt7558gD8AYoyKlIqDs2BEroogGGKWCAPMbAcEqmAdwjFbgIXDAFArgBIESQnAlAbqAMpgj5E8QiUhSWSqXJUkiRCBMYAiDKmMQM3AYRxSDRe8wIByCocg/ShwOh8PhcDgcDofD4fy0B4cEAAAAAIL+v/aFCQAAAAAAAACAUTszAUz2y7WeAAAAAElFTkSuQmCC", + "description": "Displays the latest temperature telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"thermostat\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#224AC2\"},{\"from\":-20,\"to\":0,\"color\":\"#2B54CE\"},{\"from\":0,\"to\":10,\"color\":\"#6083EC\"},{\"from\":10,\"to\":20,\"color\":\"#F89E0D\"},{\"from\":20,\"to\":30,\"color\":\"#F77410\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#224AC2\"},{\"from\":-20,\"to\":0,\"color\":\"#2B54CE\"},{\"from\":0,\"to\":10,\"color\":\"#6083EC\"},{\"from\":10,\"to\":20,\"color\":\"#F89E0D\"},{\"from\":20,\"to\":30,\"color\":\"#F77410\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal temperature card with background\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "temperature", + "weather", + "environment" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_uv_index_card.json b/application/src/main/data/json/system/widget_types/horizontal_uv_index_card.json new file mode 100644 index 0000000000..8fb6714399 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_uv_index_card.json @@ -0,0 +1,30 @@ +{ + "fqn": "horizontal_uv_index_card", + "name": "Horizontal UV Index card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAArlBMVEUAAADg4ODf39/g4ODg4ODg4ODf39/g4ODg4ODf39/////39/eAwyzg4ODCwsKQkJD6+vrn5+fz8/O2trbt7e3b29vV1dXOzs50dHSQy0bx8fHP6K88PDzv9+XHx8e7u7v3+/Lj4+PU1NTH5aPA4Zatra1YWFjY7L2enp7IyMiqqqqCgoKYzlMhISHf8Mqv2Xun1m5mZmZKSkqIxzmo1m6g0mDn9Ne43YgvLy/H5aK3Tah+AAAACnRSTlMAvxDv36eQgG8gyT+wCQAAAqtJREFUeNrt2OtuokAAhmFqtYevODDAgFIExPOhtrXHvf8b20HcqNntYv9QNN9jgs44MXkRBTWIiIiIiIiIiIiIiIiIiIiIiIiIiIiI6Nsa162LU3fTMIzbpufcnTqv2TBaIc6Ad2Vc2jgHl0YbZ6HNkJphSN0wpG7OPiRdDnBSvgrpmY84EIZAP+xDb4vRVhyjFo4O6VjoP4QPIeIoH42wZVmo3uppUh7S6w4BpB+T/PFycBCC8RSLzibEt6xFiGlHzyc6a+r3F6jG6uPFNO/LQ9LublXXHB6GJGNE8SYkjpLONIn8Z8uP/HESrztTVGJimseFoPc2QToZDnrA0y8choSfcYQiZAxLyzfROLLQ+UQ17t8GaWnI7vAytSH2LBbwI30XWXsh0+diE6K/XseoTHnIriM3wE5f7/c5ED/090LC/M0In8dRv2PFa5SoPmRoFl5S7AnxD2GxKVF9SLrsdld4M7fe8dTt1vDMeGzIq7m1OtmQvw6tejo2JN1+2IeoqW98/da648iQ3usE0CfEd+B116Js/OEdvEB7f0rmo8xDiUpCegeXKANsCQeQgWNnypl6gIR02vkwUE4mnblnZx7gBAJ2JuRmKbJMzyoUqg9Jl0Noj5NN1H6I7WezQPnO3AZcuFK0hfJnKpOurW9eYgOOgJ6VgK/0c0Eg5EyiRMWX8TrEcX3hBa49RxESwBWOUjN/roejIChCEjuQsPXSQMoggVIoUXVIIjxXCF/ona8ANxhJVyiZJGqWzO2k7QuBTYgvRnnITMi5G+jlDkr88E9dGzLAfzlqplDu5/98sL2y15b4Wo1C6oUhdcOQumFI3bSNS5wD+9K48nAGvJbRaHp3p87xmreG0bi5OHWt64ZBRERERERERERERERERERERERERERERN/1G1Juby0y7njrAAAAAElFTkSuQmCC", + "description": "Displays the latest UV index telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'uv', label: 'UV Index', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"UV Index\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.ceil(Math.random() * 4 - 2);\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 14) {\\n\\tvalue = 14;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"light_mode\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#80C32C\"},{\"from\":2,\"to\":5,\"color\":\"#FFA600\"},{\"from\":5,\"to\":7,\"color\":\"#F36900\"},{\"from\":7,\"to\":10,\"color\":\"#F04022\"},{\"from\":10,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#80C32C\"},{\"from\":2,\"to\":5,\"color\":\"#FFA600\"},{\"from\":5,\"to\":7,\"color\":\"#F36900\"},{\"from\":7,\"to\":10,\"color\":\"#F04022\"},{\"from\":10,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal UV Index card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "uv", + "ultraviolet", + "sunburn" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_uv_index_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_uv_index_card_with_background.json new file mode 100644 index 0000000000..535d2c493d --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_uv_index_card_with_background.json @@ -0,0 +1,30 @@ +{ + "fqn": "horizontal_uv_index_card_with_background", + "name": "Horizontal UV Index card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest UV index telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'uv', label: 'UV Index', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"UV Index\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.ceil(Math.random() * 4 - 2);\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 14) {\\n\\tvalue = 14;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"light_mode\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#7CC322\"},{\"from\":2,\"to\":5,\"color\":\"#F89E0D\"},{\"from\":5,\"to\":7,\"color\":\"#F77410\"},{\"from\":7,\"to\":10,\"color\":\"#F04022\"},{\"from\":10,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#7CC322\"},{\"from\":2,\"to\":5,\"color\":\"#F89E0D\"},{\"from\":5,\"to\":7,\"color\":\"#F77410\"},{\"from\":7,\"to\":10,\"color\":\"#F04022\"},{\"from\":10,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal UV Index card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "uv", + "ultraviolet", + "sunburn" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_value_card.json b/application/src/main/data/json/system/widget_types/horizontal_value_card.json new file mode 100644 index 0000000000..c6919027f9 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_value_card.json @@ -0,0 +1,24 @@ +{ + "fqn": "cards.horizontal_value_card", + "name": "Horizontal value card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a single entity attribute or the latest telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"thermostat\",\"iconColor\":{\"type\":\"constant\",\"color\":\"#5469FF\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal value card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_vibration_card.json b/application/src/main/data/json/system/widget_types/horizontal_vibration_card.json new file mode 100644 index 0000000000..9974087967 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_vibration_card.json @@ -0,0 +1,35 @@ +{ + "fqn": "horizontal_vibration_card", + "name": "Horizontal vibration card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAn1BMVEUAAADf39/f39/g4ODg4OD////k5OTYGDjg4OD64ubwqLTdNVH1xc3nboLIyMjiUmnOzs7aJkXx8fHsi5v09PT98fL5+fm3t7f41Nrb29vCwsLzt8GQkJDV1dW8vLxYWFjn5+fumqjpfY90dHTgQ1398fPiUmqsrKw8PDyenp5KSkqCgoLkYHYvLy9mZmZLS0shISGwsLCdnZ3pfZDpfY64MVbEAAAABXRSTlMAIETfv0i0+14AAAO7SURBVHja7doNb6IwHMfxnbc/tS30eBZQQHyYD9vu+f2/tmsL7LadOpZcFrb8PomsApp+04kmegUAAAAAAAAAAAAAAAAAAAAAAAAAAADwlj69ezZj8tl576afJ7rDSendi53J1XVMH0B6fTWlD2GKkJFByNggZGwQMjYIGZsTIZnb4tQJD0RK/XQOJT1wqFI0Jv+GNKwjqXcfUq1UuJ45FIbmRuE+LEvSY3L0vaHc7k8TzZ+MOnNhuHTO3KXHJ7svhHgeWZxRb3Fw9rRW62N9UPfH6vj1sLvfLRbOt69rul/v1bCKVcDIiJgm+cMooU7AjBs6gzOPHmwCyZpLIfxxCE/IUvVubUJUuFc1OYfbmva0WNzO6JvznRY7etl2w7T2eaWbrcx8XTO6Y6v+FCaFNqczBMuol0lBG3kphPFHIV8Eteq6NCGz8KhDbg+zLmRHdTgwhMtItCErM/NtwBLymDDTD/rJPVmMhFMydxOdO+dkfZH2LOEm/QKdDUmy5yGJS8ZiTybkW73QIT/39ZGOC/2vVX+9pYEhGm9DvrC53m5Ypm/m2SXrJurqQk4dXekFTC+ZWcmofXijNzesu5/JTXI2RNw8DxGSHgu77ZNNZ3iI2267BKlHlmCbgAXRQ0jQzHXF3Txi9rzGbD224iLQj8iCFdHrQi76nyERk7/1KkR9SGMWKbCrJ8x5N/1yCs/d6sWSko8zhHhip96HCHthsOfqYaY3NnalU2grjGSkIVbA+MkQr93fSL0W0UtviDx7HsKz/x6iZ9i+2LuXfcCS/i0uuRAi76iViaBPf/XlN6YT4vD5jnhQSMPuuhlG7Jeel50r7xbJHjgV4tr8rdzYC3hE1uA3xCQha9bGxBTmZKUppVPf7JhSSkTL3ZLSPG8P6B3xhZCtZF4kWaRHgR2JbmpzFtgDJ0M8PbDDO9H0i/b6jyg6pCz9XClfjwoq8qJKZ4VflsqvQr9KifxlXFU5xb494KvqfAhxyVh78czMqLGvYbMV5m5EJ0MCj4ytx/Q5goaERKwjn4T4s1j5S78NWZa5Hut9y9wvlA2Z5rkOUbOlinVuEdMFCU/6uG7E/949STD34bF86Md4t5NQ74efF/6sVEVsVsSv8mIWz5Sv94XVUvlkQlKzIsvqhzlQFgW9qS7k1fIlnVeqnIYYQ0iaXjoY0xCjCBkjhIwNQsYGIWODkLGZXl1/gK/ZieLrq4nzAb5oT52J+QnH1HnvPk8+0o9qAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN7IH7CH4Te59eAaAAAAAElFTkSuQmCC", + "description": "Displays the latest vibration telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'vibration', label: 'Vibration', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Vibration\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"let factor = 1000;\\nif (prevValue < 1) {\\n factor = 1;\\n} else if (prevValue < 10) {\\n factor = 10;\\n} else if (prevValue < 100) {\\n factor = 100;\\n}\\nlet value = prevValue + Math.random() * factor;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"vibration\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":0.1,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0.1,\"to\":1,\"color\":\"#FFA600\"},{\"from\":1,\"to\":10,\"color\":\"#F36900\"},{\"from\":10,\"to\":100,\"color\":\"#F04022\"},{\"from\":100,\"to\":1000,\"color\":\"#D81838\"},{\"from\":1000,\"to\":null,\"color\":\"#6F113A\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":0.1,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0.1,\"to\":1,\"color\":\"#FFA600\"},{\"from\":1,\"to\":10,\"color\":\"#F36900\"},{\"from\":10,\"to\":100,\"color\":\"#F04022\"},{\"from\":100,\"to\":1000,\"color\":\"#D81838\"},{\"from\":1000,\"to\":null,\"color\":\"#6F113A\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal vibration card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"m/s²\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "vibration", + "tremor", + "shake", + "quiver", + "jolt", + "oscillation", + "pulsation", + "resonance" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_vibration_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_vibration_card_with_background.json new file mode 100644 index 0000000000..52ddac2cf3 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_vibration_card_with_background.json @@ -0,0 +1,35 @@ +{ + "fqn": "horizontal_vibration_card_with_background", + "name": "Horizontal vibration card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest vibration telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'vibration', label: 'Vibration', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Vibration\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"let factor = 1000;\\nif (prevValue < 1) {\\n factor = 1;\\n} else if (prevValue < 10) {\\n factor = 10;\\n} else if (prevValue < 100) {\\n factor = 100;\\n}\\nlet value = prevValue + Math.random() * factor;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"vibration\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":0.1,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0.1,\"to\":1,\"color\":\"#F89E0D\"},{\"from\":1,\"to\":10,\"color\":\"#F77410\"},{\"from\":10,\"to\":100,\"color\":\"#F04022\"},{\"from\":100,\"to\":1000,\"color\":\"#DE2343\"},{\"from\":1000,\"to\":null,\"color\":\"#791541\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":0.1,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0.1,\"to\":1,\"color\":\"#F89E0D\"},{\"from\":1,\"to\":10,\"color\":\"#F77410\"},{\"from\":10,\"to\":100,\"color\":\"#F04022\"},{\"from\":100,\"to\":1000,\"color\":\"#DE2343\"},{\"from\":1000,\"to\":null,\"color\":\"#791541\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal vibration card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"m/s²\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "vibration", + "tremor", + "shake", + "quiver", + "jolt", + "oscillation", + "pulsation", + "resonance" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_visibility_card.json b/application/src/main/data/json/system/widget_types/horizontal_visibility_card.json new file mode 100644 index 0000000000..48a6bf0bf7 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_visibility_card.json @@ -0,0 +1,36 @@ +{ + "fqn": "horizontal_visibility_card", + "name": "Horizontal visibility card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAolBMVEUAAADf39/f39/g4ODg4ODf39/////k5OSAwyzg4OCg0mHf8Mrz8/PP6bCQy0bv9+XOzs75+fn3+/K3t7erq6vCwsLA4Za8vLyQkJDa2trV1dXHx8ew2Xvn5+fn9NjIyMi43YhYWFg8PDx0dHTY7L2YzlPH5aOIxzmenp6Ixzrt7e2CgoKn1m5mZmYhISHb29u43Ymo1m6tra3H5aJKSkovLy/b7uh5AAAABnRSTlMAIEDfv1C6kOEmAAADOUlEQVR42u3aCU/bMBjG8a6wN27jxM7dXE3Tm2uw6/t/tb02ZWEbdFWpqoCenxCx7EjwT0xboAMAAAAAAAAAAAAAAAAAAAAAAAAAAACAc/r07tmM4YXz3o0uPnOHE9C7J53h4FLSBxBcDkb0IYwQ0jMI6RuE9A1C+gYhfXNIiNwSleXWIZaVccaHG1rQVsoevUw7JIR+SmrLUpIjqSgmMRfFNKdY3nALOXRK7jr11NNoSTueGB8R4la+EJuxp+jJZOvMaVFOFvNyEk/au5a+0JzbfrTRggcnlArmT3+P1PEhaix2fI92yjZe2JBMcgjfDWlDHDPklNPxhO9OU+ETKTOaifHRIdMNF6xDmt5yyox22rbgkKK8i23Izy7k5kdEpzMWZjddCUUz88XDKxE+C1EqDBV/a0vFS567N0SZDkUun1s9K5nMiUO2d23EIXftDT2GxHOnnNMJLT1lQ8Jd0kpMu5AHsQpdsTI7Jk3tvtsT4vMJ3+nBnBUK9me27A5PI95zp+aJFXGIS/bz75DUXmG+uMuZ2TRLX6Svh3iCuVPBZrQS4r9bUy4cOjHli+kLIbaDQ8b2Rnlm7vb1kLUNUV2IT+cWmiv9b8hGmDwbYqft8PWQ0DcbkCrB+TbHozMLV6bj3xA2OziETa+E2ChaeiFVtuncKjHbHb0/ftg3yyvhHhbSlfic4Y6fPWhJeoG8/ntC0ps9PG38tfhqn0y655Gl8MODQ5haiZ3Nd9qJbAx/XOdkBQEFI20mRhQQUR3XFOT54wJPHJu0FuJrxTyzyavUF2kXQrfcdngIc6sVV4zXIT0LKQqdN43mUUJJnmRBlOiiaHR2rbOASNcyy3KS2i7oJjt2Yz2q7IOX2MyoCzFt6/0he3QhOpKNrvVjSF3c85jn6lwnjQ0Z5TmHNFHdSM5NJL1ZqMJTv/r9pu8THRVNIs0d0VmeRDJqNM9dZ3WjyYQE5o7U2TezUCQJHeH8v1jlNb2uaO7pEH0ICYJ9i5IO0YuQPkJI3yCkbxDSNwjpm9Hg8gP8m51IXg6GTo/+gnuswBkOBp8vRs57dzH8SG+qAQAAAAAAAAAAAAAAAAAAAAAAAAAAADiTXxBy+4qYs+NTAAAAAElFTkSuQmCC", + "description": "Displays the latest visibility telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'visibility', label: 'Visibility', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Visibility\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"visibility\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#D81838\"},{\"from\":1,\"to\":4,\"color\":\"#FFA600\"},{\"from\":4,\"to\":null,\"color\":\"#80C32C\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#D81838\"},{\"from\":1,\"to\":4,\"color\":\"#FFA600\"},{\"from\":4,\"to\":null,\"color\":\"#80C32C\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal visibility card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"km\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "visibility", + "sight", + "view", + "clarity", + "transparency", + "perceptibility", + "discernibility", + "range of view", + "clearness" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_visibility_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_visibility_card_with_background.json new file mode 100644 index 0000000000..3570933e52 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_visibility_card_with_background.json @@ -0,0 +1,36 @@ +{ + "fqn": "horizontal_visibility_card_with_background", + "name": "Horizontal visibility card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest visibility telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'visibility', label: 'Visibility', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Visibility\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"visibility\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#DE2343\"},{\"from\":1,\"to\":4,\"color\":\"#F89E0D\"},{\"from\":4,\"to\":null,\"color\":\"#7CC322\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#DE2343\"},{\"from\":1,\"to\":4,\"color\":\"#F89E0D\"},{\"from\":4,\"to\":null,\"color\":\"#7CC322\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal visibility card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"km\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "visibility", + "sight", + "view", + "clarity", + "transparency", + "perceptibility", + "discernibility", + "range of view", + "clearness" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_volatile_organic_compounds_card.json b/application/src/main/data/json/system/widget_types/horizontal_volatile_organic_compounds_card.json new file mode 100644 index 0000000000..6de0c82cca --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_volatile_organic_compounds_card.json @@ -0,0 +1,37 @@ +{ + "fqn": "horizontal_volatile_organic_compounds_card", + "name": "Horizontal volatile organic compounds card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAmVBMVEUAAADf39/f39/g4ODg4ODf39/////k5OTYGDjg4ODdNVH64ub1xc3ri5v98fPOzs7iUmnwqLTaJkX09PT5+fnnb4K7u7v41NrCwsK2trba2trumqjw8PDn5+fIyMhYWFjHx8fpfY/gQ13V1dXzt8Gtra3kYHaQkJB0dHRLS0s6OjrU1NSCgoLb29uenp7pfZDkYHflYXZmZmaiBA8wAAAABnRSTlMAIEDfv1C6kOEmAAADj0lEQVR42u3aiW6bQBSF4bRJL9csAwyrsQ1e4iXp3vd/uN6ZIcitWtrIaWS751PijIEg/2YGKYpvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF7Tm4tnM97eepducvtOOrycLl7mvb25y+gK5Hc3E7oKE4ScGYScG4ScG4ScG4Scm9+HqIp6809EyyVt9yXRZr+d0zn6fYjPIfUe59Qt9x+WD0vvofzU0dyj0w1nV/0onEX3R6MRPgd/HxJoHk62/+Q90sOGykP5gaj0ukN3asVK8zDyyYhY+EpGBYu1Oi1kliiyCp6ptD9+2W0P9OjR8rDdy1PvYbukU6g1CxKVloFvg9gPqx2viFIzWnN0WgjzjCI21rTihJyu28jkIpld38g7zJfbBzpF4EepCwkTGfnujUulkDVRYqZCJVstFSh1H5IIAlJp2IdUMhoNSbUv5yiKIuGwGN6V/SOR13WdRx+6RzO1DnSiwIW4S0FizaF7iebbvqOqn3FfNbNfmcAvLCPZ6dtRokbXyIy1PZHSmgv6wfzo4YVDXIK7w3A/Cp6WTpTu2Dch/uze58Ts89PU52g0RI4vXJGJH/FqISt51LI94dT+npJ9lf298ZCQP5ORslY04tVCInfrsSFuCg6TbzSEdhzaBamlaPCKIdqO9FGIe3QhyVFIMB4SaLfY04RnNPjHIe5eRZrV8DLpKGT3yytC4yG062+/FXNFRka/kM1/3pCdEhLx5/6muzIvPeXkKaQw08OukdmwRkJzxPoPIYE980oOLhKypjZGvuYNWXlO+SQ2GyaUy/PFdkF507gdsiF7fojSXEQ+p6aGi5WWUR8i29furqXNESt711qZI8ZDAm2vg2KTPYRsNnHTtrGMaqqbusyndbzZtHE5j8ucKF5kZdlQFtsdcVs+O4Qqn1nPSNwPIxuyk6dJICF2tFMmRPI4ovEQpXXgVt/9Ucj7eJq18SJ2IYv3H2Us2xZNXLc2ZNI0EtJOF20muXVGzxcEahiR5daIe2rXyHCEcqPxqaXcj4qebOOPdTzdtHVmrkhcNvU0m7axbJuXizYmE5KbK7Iot2bHpq7ppbjF3oe89B9WzWJk5/v2ozxeRkiej+3M6CWFUUhOGlX/95+6FwYh5wYh5wYh5+aKQu6u4N/sRNmdfGDgCv7RnssHBm7e3U68S3f79po+VAMAAAAAAAAAAAAAAAAAAAAAAAAAAADwSr4DXzz6NDozanMAAAAASUVORK5CYII=", + "description": "Displays the latest volatile organic compounds (VOCs) telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'voc', label: 'VOCs', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"VOCs\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 2000) {\\n\\tvalue = 2000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:molecule\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":500,\"color\":\"#80C32C\"},{\"from\":500,\"to\":1000,\"color\":\"#FFA600\"},{\"from\":1000,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":500,\"color\":\"#80C32C\"},{\"from\":500,\"to\":1000,\"color\":\"#FFA600\"},{\"from\":1000,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal temperature card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"ppb\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "indoor", + "air", + "vocs", + "voc", + "organic solvents", + "hydrocarbons", + "emissions", + "fumes", + "gaseous organics", + "contaminants", + "air pollutants" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_volatile_organic_compounds_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_volatile_organic_compounds_card_with_background.json new file mode 100644 index 0000000000..fbe4390d1d --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_volatile_organic_compounds_card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "horizontal_volatile_organic_compounds_card_with_background", + "name": "Horizontal volatile organic compounds card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest volatile organic compounds (VOCs) telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'voc', label: 'VOCs', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"VOCs\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 2000) {\\n\\tvalue = 2000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:molecule\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":500,\"color\":\"#7CC322\"},{\"from\":500,\"to\":1000,\"color\":\"#F89E0D\"},{\"from\":1000,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":500,\"color\":\"#7CC322\"},{\"from\":500,\"to\":1000,\"color\":\"#F89E0D\"},{\"from\":1000,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal temperature card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"ppb\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "indoor", + "air", + "vocs", + "voc", + "organic solvents", + "hydrocarbons", + "emissions", + "fumes", + "gaseous organics", + "contaminants", + "air pollutants" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_wind_speed_card.json b/application/src/main/data/json/system/widget_types/horizontal_wind_speed_card.json new file mode 100644 index 0000000000..b5e1f8b583 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_wind_speed_card.json @@ -0,0 +1,32 @@ +{ + "fqn": "horizontal_wind_speed_card", + "name": "Horizontal wind speed card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAn1BMVEUAAADg4ODf39/g4ODg4OD////g4OAjTMd1j9zk6fjx8fGtvOrOzs7IyMiRpePy9Pvj4+O3t7fI0vFaedU+Ys7z8/NYWFj5+fnb29swV8vCwsI8PDy8vLzn5+eesefV1dWenp6QkJCDmuCCgoJmZma6x+2rq6tohNkvLy90dHRLS0shISHW3fS6x+7W3fXU1NSRpuNMbtFMbdGsu+qtra1mE7QcAAAABXRSTlMA7yC/r1EOHTEAAAN3SURBVHja7dprU5tAGIbhJPpmOW2AcAgg4ZRzGrXa/v/f1neJLbY2FJypg5nnMgSy5MPeLMw4oyMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6G1yM55+drcT7hgb9OkZ48no9go6uORmNKWrML6WkClCBgYhQ4OQoUHI0FxtyIneMgpyUnKd0qCzkDenKB36Q5HTB2oPERW9tTdWIYUO9xgGdxlfeOwo3Tsy1Od6c9T+IKmfOSkPZs1uxu05XbYxO4VolGWbk02vHWQYGnfEP4e7rXNcq5CwcBzny+FopMew4LGVwSdlr4oqFqRooqb/qvB4Fpf5WqeQ+N4TLPZ2M3OuU60ow7JcqxBnW662pEKc7V3phLTahmWxXq23e7mmPitiPwlGiieyVyuyiYVoC9mI+44r4vH2IITnPQsxJyXfF3Jf/BZirIi+OEdauWEh89VBSnmgdY8Q3Z+ZLyHPvGvMvCxrQmzdtjcbnei0OVFN84npfJW7hJAQ30hXIbV9zq+XkDxc73moDMOVs18fHTfkWytUt9Z6L6kP/SVExGTTa2YTookqFiKeq9ukqsvUOdsT7Gu3EG22UyF/YfzcOaFRHzTbe0JsEfNcd/qFkHhm8kSezCyuZ2Oq969i9zDnz20hPj8jvi9E7O+qbGNTG6OkFh1DeBdrlS/8CyGz+ou6OvzGA5763k49J6a2aV+RSpuZDzr9Z00I2TpvfH3bQppDfqNM+Jq6yO0hH6IJaWbcJcQUOjHTV8/IwELmpppb1S3k2aMznVvMviEB/W3Q+HMgeF9IJnb87os5j/wr5HSe/JNvE83ErG+IW8fwy4iotljQYmqpgSktiGi5XdIiis4neCDoE2L7Qpt5wldJWnsIb3VrJTwz88X9O0Ly3IqktPgooSRK0oWbWHkurdSwUp65tQzSNKLAqk9YMu0RQronhPB0FVL9I8TfkWJX6pePjN4R8mi5gbSW1jlk+RjxMY8tIyuRdcg0ijhEuksZcG4SUB+2bp/31G7ePBY6h7eFzHV667sVJZabyyRQK2KlUeIGrrR4zEiX0iIVslArsky/qxN5ktBHUyG9RUu67FFG1MUQQhaLtpMBdTGIkCFCyNAgZGgQMjRXFDKmqzAd3VzHn6dvr+cfBkaT2+lnN76ZjAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgrx9YOdsEpKcg1gAAAABJRU5ErkJggg==", + "description": "Displays the latest wind speed telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'speed', label: 'Wind Speed', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 16 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 26) {\\n\\tvalue = 26;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:windsock\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0.2,\"color\":\"#7191EF\"},{\"from\":0.2,\"to\":3.4,\"color\":\"#5B7EE6\"},{\"from\":3.4,\"to\":8,\"color\":\"#4B70DD\"},{\"from\":8,\"to\":10.8,\"color\":\"#305AD7\"},{\"from\":10.8,\"to\":17.2,\"color\":\"#234CC7\"},{\"from\":17.2,\"to\":24.5,\"color\":\"#F04022\"},{\"from\":24.5,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":0.2,\"color\":\"#7191EF\"},{\"from\":0.2,\"to\":3.4,\"color\":\"#5B7EE6\"},{\"from\":3.4,\"to\":8,\"color\":\"#4B70DD\"},{\"from\":8,\"to\":10.8,\"color\":\"#305AD7\"},{\"from\":10.8,\"to\":17.2,\"color\":\"#234CC7\"},{\"from\":17.2,\"to\":24.5,\"color\":\"#F04022\"},{\"from\":24.5,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal wind speed card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"m/s\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "wind", + "speed", + "airspeed", + "flow", + "gust" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/horizontal_wind_speed_card_with_background.json b/application/src/main/data/json/system/widget_types/horizontal_wind_speed_card_with_background.json new file mode 100644 index 0000000000..c48436a368 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/horizontal_wind_speed_card_with_background.json @@ -0,0 +1,32 @@ +{ + "fqn": "horizontal_wind_speed_card_with_background", + "name": "Horizontal wind speed card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest wind speed telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'speed', label: 'Wind Speed', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 16 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 26) {\\n\\tvalue = 26;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:windsock\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0.2,\"color\":\"#6083EC\"},{\"from\":0.2,\"to\":3.4,\"color\":\"#5579E5\"},{\"from\":3.4,\"to\":8,\"color\":\"#4369DD\"},{\"from\":8,\"to\":10.8,\"color\":\"#2B54CE\"},{\"from\":10.8,\"to\":17.2,\"color\":\"#224AC2\"},{\"from\":17.2,\"to\":24.5,\"color\":\"#F04022\"},{\"from\":24.5,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":0.2,\"color\":\"#6083EC\"},{\"from\":0.2,\"to\":3.4,\"color\":\"#5579E5\"},{\"from\":3.4,\"to\":8,\"color\":\"#4369DD\"},{\"from\":8,\"to\":10.8,\"color\":\"#2B54CE\"},{\"from\":10.8,\"to\":17.2,\"color\":\"#224AC2\"},{\"from\":17.2,\"to\":24.5,\"color\":\"#F04022\"},{\"from\":24.5,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal wind speed card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"m/s\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "wind", + "speed", + "airspeed", + "flow", + "gust" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/html_card.json b/application/src/main/data/json/system/widget_types/html_card.json new file mode 100644 index 0000000000..6c63a70177 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/html_card.json @@ -0,0 +1,25 @@ +{ + "fqn": "cards.html_card", + "name": "HTML Card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAATFSURBVHja7d3tUxNXFMdx/u7vEkAJsKQK6bilKaBV1Eh5GGNaSyq0WodhRuqMtQJqW5UHCwKGMRJiQpL99cWGEGaaTjtjgaTnvNpz7mYnn8neu/duXtwWFbdWlxs8Vrf21VJcy5TU4FHKrBVbtjJqgshstayWmgFSWm1ZVlPEskEMYhCDGMQgBjGIQQxiEIMYxCAGMYhBDGIQgxjEIAYxiEEMYhCDGMQgBjGIQQxiEIMYxCAG+T9ACrlcLjjyc7lcWflcbfhBfnByOZfL5cpSKZfL+f/2G+WAJ/8dJAHh4GgFeK4BaiOjMYDtyskzAIvSQ+BtI0JmKif3NRBk3PM8Lwwhz/M8LxtAzgf30RoNBJEkjUOk0j4GwJok6XaDQ8LwtSSVXVrP1of4v1w978bu5YNh4fHlfnfwfpDo15Hevm/eHkCyP8Tc/rGV44d8FaLbl/QCRrrqQopXg251bkdSdjBIIpuSNAtAbwXyqgsA595Hh3Sm0+l0Or1QDzJxLWiYhCftdSFJoKMHiPnyvwTOhIHeD9KGA3SfIYBkOsGJtIPz/GNDauIvIfEFGJdKnZwtOPUgOw4kfT0E1vQMmPb12IEZaRJCz1RKBpBJCG+ocAVixw25XArTUdACTBbqdvY56MhLisK8xisDXRyiUi+MStoDnqjUCT9Keg3Oh48MCcXj8Xg8PlwPMqQE/Kzr8HK3LiQBFyozhaI8uClJ8+AUi8CD6qi1DYymUqkksH7Mnf0zLcNIvo2In64LGYXhauLCHUlaAnb3gIUq5FXN7//qmCH98s8RmoXv9KYuZBI+ryZ9cFsKzsvngUdVyAowcDGIjWOGRKRpaINNrdWF3IUeX9LS/PymLsMlSZqCLikM01XIe+CnE3ogRqRtAE9arQtZCVqynbCoWXBWpfddMCaNgLsnLQSj1gVwM5Je3/RPAKIBYPYIJOp5nud5dw8uFIPQxO1eCOf1oRs6Jm71QOtG0FMiydFQAHkKnJlIjTgkTwIyB87uEUglbh5c6G13UGhdkvSyPUicB5J0HYBQe/Bkv+tUPnr/JCDZVq7obyF6d6MNnC+COdTmSCs4A78Hs5dkG3zyYqAy13oec8CJ/XZ6l7qFzfW9apJ/80dNsr5V2yNyGxt7tmY3iEEMYhCDGOTEIJnD6YtBDGKQpocsDrvR5K4kyX96PeoOzu5Lysbj8b2Zvv7SkepphgwBcD4vqRAPVlXR91IauASUVAhWgny6e7ohtEdCwcpdCaDTBYYDCED5sHrxdEOuFZRx4Ya06cC3ZS05sKI04EzM3demA1NlLTqweuo7ewI86U7lHdYQzCgNzEnSHXB9SYPw/amHpKBPGoFzqVQqFYUxpYEX0pHqeINAav4wvXoI+eyweq1BIDHorry+nT6E1FRnGgQyCtFqUxVyo7baGJBHlVG4mFivgRxU9xMbjQIpR4HBqYRLz84hpBwFhqYSLu5Og0C07VZ6dXTvEHKk2iAQZZOdQHcqX3NrSdlb1WrjLHXL26/T/j+s2prdIAYxiEEMYhCDGMQgBjGIQQxiEIMYxCAGMYhBDGIQgxjEIAYxiEEMYhCDGMQgBjGIQQxiEIMY5PRAmmaD4ObYsvndVst+c2yiXWppjm3NS/oTe0OjFEeU1MMAAAAASUVORK5CYII=", + "description": "Useful to inject custom HTML code. Designed to display static information only.", + "descriptor": { + "type": "static", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n var $injector = self.ctx.$scope.$injector;\n var utils = $injector.get(self.ctx.servicesMap.get('utils'));\n\n var cssParser = new cssjs();\n cssParser.testMode = false;\n var namespace = 'html-card-' + hashCode(self.ctx.settings.cardCss);\n cssParser.cssPreviewNamespace = namespace;\n cssParser.createStyleElement(namespace, self.ctx.settings.cardCss);\n self.ctx.$container.addClass(namespace);\n var evtFnPrefix = 'htmlCard_' + Math.abs(hashCode(self.ctx.settings.cardCss + self.ctx.settings.cardHtml + self.ctx.widget.id));\n cardHtml = '
' + \n self.ctx.settings.cardHtml + \n '
';\n cardHtml = replaceCustomTranslations(cardHtml);\n self.ctx.$container.html(cardHtml);\n\n window[evtFnPrefix + '_onClickFn'] = function (event) {\n self.ctx.actionsApi.elementClick(event);\n }\n\n function hashCode(str) {\n var hash = 0;\n var i, char;\n if (str.length === 0) return hash;\n for (i = 0; i < str.length; i++) {\n char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n return hash;\n }\n \n function replaceCustomTranslations (pattern) {\n var customTranslationRegex = new RegExp('{i18n:[^{}]+}', 'g');\n pattern = pattern.replace(customTranslationRegex, getTranslationText);\n return pattern;\n }\n \n function getTranslationText (variable) {\n return utils.customTranslation(variable, variable);\n \n }\n}\n\nself.actionSources = function() {\n return {\n 'elementClick': {\n name: 'widget-action.element-click',\n multiple: true\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-html-card-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"cardHtml\":\"
HTML code here
\",\"cardCss\":\".card {\\n font-weight: bold;\\n font-size: 32px;\\n color: #999;\\n width: 100%;\\n height: 100%;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n}\"},\"title\":\"HTML Card\",\"dropShadow\":true}" + }, + "externalId": null, + "tags": [ + "web", + "markup" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/html_value_card.json b/application/src/main/data/json/system/widget_types/html_value_card.json new file mode 100644 index 0000000000..008e9423dc --- /dev/null +++ b/application/src/main/data/json/system/widget_types/html_value_card.json @@ -0,0 +1,25 @@ +{ + "fqn": "cards.html_value_card", + "name": "HTML Value Card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays configurable HTML with the ability to inject values from the selected data source. For example, display single or multiple attribute values.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.varsRegex = /\\$\\{([^\\}]*)\\}/g;\n self.ctx.htmlSet = false;\n \n var cssParser = new cssjs();\n cssParser.testMode = false;\n var namespace = 'html-value-card-' + hashCode(self.ctx.settings.cardCss);\n cssParser.cssPreviewNamespace = namespace;\n cssParser.createStyleElement(namespace, self.ctx.settings.cardCss);\n self.ctx.$container.addClass(namespace);\n var evtFnPrefix = 'htmlValueCard_' + Math.abs(hashCode(self.ctx.settings.cardCss + self.ctx.settings.cardHtml + self.ctx.widget.id));\n self.ctx.html = '
' + \n self.ctx.settings.cardHtml + \n '
';\n\n self.ctx.replaceInfo = processHtmlPattern(self.ctx.html, self.ctx.data);\n \n updateHtml();\n \n window[evtFnPrefix + '_onClickFn'] = function (event) {\n self.ctx.actionsApi.elementClick(event);\n }\n\n function hashCode(str) {\n var hash = 0;\n var i, char;\n if (str.length === 0) return hash;\n for (i = 0; i < str.length; i++) {\n char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n return hash;\n }\n \n function processHtmlPattern(pattern, data) {\n var match = self.ctx.varsRegex.exec(pattern);\n var replaceInfo = {};\n replaceInfo.variables = [];\n while (match !== null) {\n var variableInfo = {};\n variableInfo.dataKeyIndex = -1;\n var variable = match[0];\n var label = match[1];\n var valDec = 2;\n var splitVals = label.split(':');\n if (splitVals.length > 1) {\n label = splitVals[0];\n valDec = parseFloat(splitVals[1]);\n }\n variableInfo.variable = variable;\n variableInfo.valDec = valDec;\n if (label == 'entityName') {\n variableInfo.isEntityName = true;\n } else if (label == 'entityLabel') {\n variableInfo.isEntityLabel = true;\n } else if (label.startsWith('#')) {\n var keyIndexStr = label.substring(1);\n var n = Math.floor(Number(keyIndexStr));\n if (String(n) === keyIndexStr && n >= 0) {\n variableInfo.dataKeyIndex = n;\n }\n }\n if (!variableInfo.isEntityName && !variableInfo.isEntityLabel && variableInfo.dataKeyIndex === -1) {\n for (var i = 0; i < data.length; i++) {\n var datasourceData = data[i];\n var dataKey = datasourceData.dataKey;\n if (dataKey.label === label) {\n variableInfo.dataKeyIndex = i;\n break;\n }\n }\n }\n replaceInfo.variables.push(variableInfo);\n match = self.ctx.varsRegex.exec(pattern);\n }\n return replaceInfo;\n } \n}\n\nself.onDataUpdated = function() {\n updateHtml();\n}\n\nself.actionSources = function() {\n return {\n 'elementClick': {\n name: 'widget-action.element-click',\n multiple: true\n }\n };\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n singleEntity: true,\n dataKeysOptional: true\n };\n}\n\n\nself.onDestroy = function() {\n}\n\nfunction isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n}\n\nfunction padValue(val, dec, int) {\n var i = 0;\n var s, strVal, n;\n\n val = parseFloat(val);\n n = (val < 0);\n val = Math.abs(val);\n\n if (dec > 0) {\n strVal = val.toFixed(dec).toString().split('.');\n s = int - strVal[0].length;\n\n for (; i < s; ++i) {\n strVal[0] = '0' + strVal[0];\n }\n\n strVal = (n ? '-' : '') + strVal[0] + '.' + strVal[1];\n }\n\n else {\n strVal = Math.round(val).toString();\n s = int - strVal.length;\n\n for (; i < s; ++i) {\n strVal = '0' + strVal;\n }\n\n strVal = (n ? '-' : '') + strVal;\n }\n\n return strVal;\n}\n\nfunction updateHtml() {\n var $injector = self.ctx.$scope.$injector;\n var utils = $injector.get(self.ctx.servicesMap.get('utils'));\n var text = self.ctx.html;\n var updated = false;\n for (var v in self.ctx.replaceInfo.variables) {\n var variableInfo = self.ctx.replaceInfo.variables[v];\n var txtVal = '';\n if (variableInfo.dataKeyIndex > -1) {\n var varData = self.ctx.data[variableInfo.dataKeyIndex].data;\n if (varData.length > 0) {\n var val = varData[varData.length-1][1];\n if (isNumber(val)) {\n txtVal = padValue(val, variableInfo.valDec, 0);\n } else {\n txtVal = val;\n }\n }\n } else if (variableInfo.isEntityName) {\n if (self.ctx.defaultSubscription.datasources.length) {\n txtVal = self.ctx.defaultSubscription.datasources[0].entityName;\n } else {\n txtVal = 'Unknown';\n }\n } else if (variableInfo.isEntityLabel) {\n if (self.ctx.defaultSubscription.datasources.length) {\n txtVal = self.ctx.defaultSubscription.datasources[0].entityLabel || self.ctx.defaultSubscription.datasources[0].entityName;\n } else {\n txtVal = 'Unknown';\n }\n }\n if (typeof variableInfo.lastVal === undefined ||\n variableInfo.lastVal !== txtVal) {\n updated = true;\n variableInfo.lastVal = txtVal;\n }\n text = text.split(variableInfo.variable).join(txtVal);\n }\n if (updated || !self.ctx.htmlSet) {\n text = replaceCustomTranslations(text);\n self.ctx.$container.html(text);\n if (!self.ctx.htmlSet) {\n self.ctx.htmlSet = true;\n }\n }\n \n function replaceCustomTranslations (pattern) {\n var customTranslationRegex = new RegExp('{i18n:[^{}]+}', 'g');\n pattern = pattern.replace(customTranslationRegex, getTranslationText);\n return pattern;\n }\n \n function getTranslationText (variable) {\n return utils.customTranslation(variable, variable);\n \n }\n}\n\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-html-card-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"My value\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.random() * 5.45;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"cardCss\":\".card {\\n width: 100%;\\n height: 100%;\\n border: 2px solid #ccc;\\n box-sizing: border-box;\\n}\\n\\n.card .content {\\n padding: 20px;\\n display: flex;\\n flex-direction: row;\\n align-items: center;\\n justify-content: space-around;\\n height: 100%;\\n box-sizing: border-box;\\n}\\n\\n.card .content .column {\\n display: flex;\\n flex-direction: column; \\n justify-content: space-around;\\n height: 100%;\\n}\\n\\n.card h1 {\\n text-transform: uppercase;\\n color: #999;\\n font-size: 20px;\\n font-weight: bold;\\n margin: 0;\\n padding-bottom: 10px;\\n line-height: 32px;\\n}\\n\\n.card .value {\\n font-size: 38px;\\n font-weight: 200;\\n}\\n\\n.card .description {\\n font-size: 20px;\\n color: #999;\\n}\\n\",\"cardHtml\":\"
\\n
\\n
\\n

Value title

\\n
\\n ${My value:2} units.\\n
\\n
\\n Value description text\\n
\\n
\\n \\n
\\n
\"},\"title\":\"HTML Value Card\",\"dropShadow\":false,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null, + "tags": [ + "web", + "markup" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/humidity_card.json b/application/src/main/data/json/system/widget_types/humidity_card.json new file mode 100644 index 0000000000..ea1f3cd17c --- /dev/null +++ b/application/src/main/data/json/system/widget_types/humidity_card.json @@ -0,0 +1,37 @@ +{ + "fqn": "humidity_card", + "name": "Humidity card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest humidity telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'humidity', label: 'Humidity', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:water-percent\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#D81838\"},{\"from\":20,\"to\":40,\"color\":\"#FFA600\"},{\"from\":40,\"to\":60,\"color\":\"#5B7EE6\"},{\"from\":60,\"to\":80,\"color\":\"#305AD7\"},{\"from\":80,\"to\":100,\"color\":\"#234CC7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#D81838\"},{\"from\":20,\"to\":40,\"color\":\"#FFA600\"},{\"from\":40,\"to\":60,\"color\":\"#5B7EE6\"},{\"from\":60,\"to\":80,\"color\":\"#305AD7\"},{\"from\":80,\"to\":100,\"color\":\"#234CC7\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Humidity card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "moisture", + "dampness", + "wetness", + "humidness", + "moistness", + "dew", + "water vapor", + "condensation", + "dew point", + "steaminess" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/humidity_card_with_background.json b/application/src/main/data/json/system/widget_types/humidity_card_with_background.json new file mode 100644 index 0000000000..31f0ca2444 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/humidity_card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "humidity_card_with_background", + "name": "Humidity card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest humidity telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'humidity', label: 'Humidity', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:water-percent\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#DE2343\"},{\"from\":20,\"to\":40,\"color\":\"#F89E0D\"},{\"from\":40,\"to\":60,\"color\":\"#5579E5\"},{\"from\":60,\"to\":80,\"color\":\"#2B54CE\"},{\"from\":80,\"to\":100,\"color\":\"#224AC2\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#DE2343\"},{\"from\":20,\"to\":40,\"color\":\"#F89E0D\"},{\"from\":40,\"to\":60,\"color\":\"#5579E5\"},{\"from\":60,\"to\":80,\"color\":\"#2B54CE\"},{\"from\":80,\"to\":100,\"color\":\"#224AC2\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Humidity card with background\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "moisture", + "dampness", + "wetness", + "humidness", + "moistness", + "dew", + "water vapor", + "condensation", + "dew point", + "steaminess" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/humidity_chart_card.json b/application/src/main/data/json/system/widget_types/humidity_chart_card.json new file mode 100644 index 0000000000..0a352cd2ff --- /dev/null +++ b/application/src/main/data/json/system/widget_types/humidity_chart_card.json @@ -0,0 +1,40 @@ +{ + "fqn": "humidity_chart_card", + "name": "Humidity chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a humidity data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'humidity', label: 'Humidity', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: '%', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'humidity', '%', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#D81838\"},{\"from\":20,\"to\":40,\"color\":\"#FFA600\"},{\"from\":40,\"to\":60,\"color\":\"#5B7EE6\"},{\"from\":60,\"to\":80,\"color\":\"#305AD7\"},{\"from\":80,\"to\":100,\"color\":\"#234CC7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Humidity\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "moisture", + "dampness", + "wetness", + "humidness", + "moistness", + "dew", + "water vapor", + "condensation", + "dew point", + "steaminess" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/humidity_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/humidity_chart_card_with_background.json new file mode 100644 index 0000000000..e5e3435d58 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/humidity_chart_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "humidity_chart_card_with_background", + "name": "Humidity chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a humidity data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'humidity', label: 'Humidity', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: '%', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'humidity', '%', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#DE2343\"},{\"from\":20,\"to\":40,\"color\":\"#F89E0D\"},{\"from\":40,\"to\":60,\"color\":\"#5579E5\"},{\"from\":60,\"to\":80,\"color\":\"#2B54CE\"},{\"from\":80,\"to\":100,\"color\":\"#224AC2\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Humidity\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "moisture", + "dampness", + "wetness", + "humidness", + "moistness", + "dew", + "water vapor", + "condensation", + "dew point", + "steaminess" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/humidity_progress_bar.json b/application/src/main/data/json/system/widget_types/humidity_progress_bar.json new file mode 100644 index 0000000000..cdb98533ed --- /dev/null +++ b/application/src/main/data/json/system/widget_types/humidity_progress_bar.json @@ -0,0 +1,37 @@ +{ + "fqn": "humidity_progress_bar", + "name": "Humidity progress bar", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAn1BMVEXg4ODf39/g4ODg4OAAAAD///8jTMfg4OD29vYhISHHx8fj4+M9PT10dHSQkJDx8fHk6fitra3I0vG6urpYWFjy9Pvv7++svOo+Ys5aedWGhoZ1j9y6x+7V1dWpqamenp6Xl5eCgoIvLy+Dmt9LbdEwV8vX3vSRpeOesefLy8tmZmbCwsJohNmOjo6RpuPY2Nh+fn7l5eVXdtJKSkrm5uZgoBxmAAAABXRSTlPvIL+vAC9A4IoAAAQhSURBVHja7M/BEQBABAPAOIynn/5LvTbCZDtYhD3Udm4Bw+R6DYN3HjAPlSfgSqQUIaMIG0XYKMJGETaKsFGEjSKf3TrWcRAGoihaveJ5xjNCoWUtu3Sb//+3JVgrlA2KqYBIuUg2yNURU/hqvYHcBl+37czQigyQGHBCfYhSMDe2bSsnfYUk5pltgn1dCYJpwgpB8Payq3MgwSZgsgCxEHKesCyAJQCS8xAZlnO7M/5MdgOQzNHpeIhxAAYaApWFrI8lAFRACh9PWM61UKuzAEJFp+MgNc7pP4ggtyU2yEiDKBukjda4fGV0Og6ij/gMiUBgBYRjg7AAiE+QRFt+ZK8zR6tB4gqZ9xeIU513vO9qEKe+QDAy8QedzoAkJiBtQqB0oD5DbizdyToFEqgp6TbEqINxhdQb4IUjOp0CgZGlbkOQSc1/kEkXQjx0slZIPxd/cwasiS8Qwc6ufPuVwIpenwAZWQS9PgESBke3T4Ds6Av5Qn7Zn2MUAGEgAIJVUocDwWBrxMr/P09DsI3tCjs/mL8wQmOExgiNERojNEZojNAYoTFCY4TGCI0RGiM0RmiM0BihMUJjhOYjckSU9FoiYklQ08jZ8qOOSqm526CVWeTKQyv9seZhT0iTSLnZL3sVh2EgCFczpMtiEJKwO8s/hau8/8Od8JokBG6PwIE3RB8ujCt/jGYlkewQRjIDyOQYMFedCI8YIgPZAwgTJ2BWHf3oEUMkkhGaxS6wHDFleMQQ6cgRlcQEBJFw/+gRQ6QaTMNe+QV3erLDjrPpZYlIIqdJS/HozXYYlTzDEZZI0Pk7xWe1SXTdZXQbxE9fLJFSw4hrIteniAaoSI85ox/gBXtqLQBCTSFoQup0vA/jGB1NYkMkk4LKeozhUNRMkWWVLHCDLRI0Gc1hU48HfewWN2vLENmOSdtrMcbXHUSKpFi8HFjsjiQ9a6WwbyelUwQ7SeKCwUtNDBH0PBgA4QMNYV0QR0Qv+7wlgphYyR1eRQ5C2pKXvv95Q5wDDMTNQeVb7uyfQxPxRhPxRhPxRhPxRhPxRhPxxheISOYZlPli8r5I4TncLjZvi/AsLpXrr8/7IpnnUOrv/msiUngGN+2IylxfXipfPLU+jCbijSby094dqzgMwwAYdtr+1MtxBIMGQVKtwYSkcO//bFdK5kK6WE31g3d9eNAobwXEWy8gdbZC43IdATEz2QbaDxEVbKFpWXsFxspkLJpllf2Q4Qal+ZeIAirkldrDOO2HTKMbyM/zlRIQLxAVWKlvQmTN3AaatkFsYTAWA/3bD6HobLRtg9zV9A79PBfegJAzXpJtoO/e7J9VQLwVEG8FxFsB8VZAvBUQbwXEWwHxVkC8FRBvHQiSOETXdDrA6Wn4PacuHeD4tDwYl+58/fTSqbv8A8IwqxFwwnsYAAAAAElFTkSuQmCC", + "description": "Displays humidity reading as a horizontal progress bar. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'humidity', label: 'humidity', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#D81838\"},{\"from\":20,\"to\":40,\"color\":\"#FFA600\"},{\"from\":40,\"to\":60,\"color\":\"#5B7EE6\"},{\"from\":60,\"to\":80,\"color\":\"#305AD7\"},{\"from\":80,\"to\":100,\"color\":\"#234CC7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":100,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#D81838\"},{\"from\":20,\"to\":40,\"color\":\"#FFA600\"},{\"from\":40,\"to\":60,\"color\":\"#5B7EE6\"},{\"from\":60,\"to\":80,\"color\":\"#305AD7\"},{\"from\":80,\"to\":100,\"color\":\"#234CC7\"}]},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Humidity\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "moisture", + "dampness", + "wetness", + "humidness", + "moistness", + "dew", + "water vapor", + "condensation", + "dew point", + "steaminess" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/humidity_progress_bar_with_background.json b/application/src/main/data/json/system/widget_types/humidity_progress_bar_with_background.json new file mode 100644 index 0000000000..4eaf46e7ee --- /dev/null +++ b/application/src/main/data/json/system/widget_types/humidity_progress_bar_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "humidity_progress_bar_with_background", + "name": "Humidity progress bar with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays humidity reading as a horizontal progress bar with background. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'humidity', label: 'humidity', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#DE2343\"},{\"from\":20,\"to\":40,\"color\":\"#F89E0D\"},{\"from\":40,\"to\":60,\"color\":\"#5579E5\"},{\"from\":60,\"to\":80,\"color\":\"#2B54CE\"},{\"from\":80,\"to\":100,\"color\":\"#224AC2\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":100,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#DE2343\"},{\"from\":20,\"to\":40,\"color\":\"#F89E0D\"},{\"from\":40,\"to\":60,\"color\":\"#5579E5\"},{\"from\":60,\"to\":80,\"color\":\"#2B54CE\"},{\"from\":80,\"to\":100,\"color\":\"#224AC2\"}]},\"barBackground\":\"rgba(0, 0, 0, 0.1)\"},\"title\":\"Humidity\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "moisture", + "dampness", + "wetness", + "humidness", + "moistness", + "dew", + "water vapor", + "condensation", + "dew point", + "steaminess" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/illuminance_card.json b/application/src/main/data/json/system/widget_types/illuminance_card.json new file mode 100644 index 0000000000..a728e235bc --- /dev/null +++ b/application/src/main/data/json/system/widget_types/illuminance_card.json @@ -0,0 +1,36 @@ +{ + "fqn": "illuminance_card", + "name": "Illuminance card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest illuminance telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'illuminance', label: 'Illuminance', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Illuminance\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:lightbulb-on\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":1,\"to\":5,\"color\":\"#FFA600\"},{\"from\":5,\"to\":20,\"color\":\"#F36900\"},{\"from\":20,\"to\":50,\"color\":\"#F04022\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":1,\"to\":5,\"color\":\"#FFA600\"},{\"from\":5,\"to\":20,\"color\":\"#F36900\"},{\"from\":20,\"to\":50,\"color\":\"#F04022\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Illuminance card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"lx\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "brightness", + "luminance", + "luminosity", + "light", + "light level", + "light intensity", + "lux", + "candela", + "foot-candle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/illuminance_card_with_background.json b/application/src/main/data/json/system/widget_types/illuminance_card_with_background.json new file mode 100644 index 0000000000..2048c150f9 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/illuminance_card_with_background.json @@ -0,0 +1,36 @@ +{ + "fqn": "illuminance_card_with_background", + "name": "Illuminance card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest illuminance telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'illuminance', label: 'Illuminance', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Illuminance\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:lightbulb-on\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":1,\"to\":5,\"color\":\"#F89E0D\"},{\"from\":5,\"to\":20,\"color\":\"#F77410\"},{\"from\":20,\"to\":50,\"color\":\"#F04022\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":1,\"to\":5,\"color\":\"#F89E0D\"},{\"from\":5,\"to\":20,\"color\":\"#F77410\"},{\"from\":20,\"to\":50,\"color\":\"#F04022\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Illuminance card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"lx\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "brightness", + "luminance", + "luminosity", + "light", + "light level", + "light intensity", + "lux", + "candela", + "foot-candle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/illuminance_chart_card.json b/application/src/main/data/json/system/widget_types/illuminance_chart_card.json new file mode 100644 index 0000000000..8970889f94 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/illuminance_chart_card.json @@ -0,0 +1,39 @@ +{ + "fqn": "illuminance_chart_card", + "name": "Illuminance chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a illuminance data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'illuminance', label: 'Illuminance', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'lx', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'illuminance', 'lx', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Illuminance\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"lx\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":1,\"to\":5,\"color\":\"#FFA600\"},{\"from\":5,\"to\":20,\"color\":\"#F36900\"},{\"from\":20,\"to\":50,\"color\":\"#F04022\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"lx\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"lx\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Illuminance\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:lightbulb-on\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "brightness", + "luminance", + "luminosity", + "light", + "light level", + "light intensity", + "lux", + "candela", + "foot-candle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/illuminance_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/illuminance_chart_card_with_background.json new file mode 100644 index 0000000000..fb2c9a741a --- /dev/null +++ b/application/src/main/data/json/system/widget_types/illuminance_chart_card_with_background.json @@ -0,0 +1,39 @@ +{ + "fqn": "illuminance_chart_card_with_background", + "name": "Illuminance chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a illuminance data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'illuminance', label: 'Illuminance', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'lx', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'illuminance', 'lx', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Illuminance\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"lx\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":1,\"to\":5,\"color\":\"#F89E0D\"},{\"from\":5,\"to\":20,\"color\":\"#F77410\"},{\"from\":20,\"to\":50,\"color\":\"#F04022\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"lx\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"lx\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAU8AAAELCAMAAABu2vS6AAADAFBMVEUYHB8WGx4aHSAaHB8dHyA2KiMzKSMdHh85KyMwJyI8LiU1KyQ7LCMnIiA5LSU3KyQyKCIsJSIhICAjISAuJSEpJCH8+/kwKSUpIyA0KCElIiEzKiU+LyUlIR82KCIyJyFAMSYrJCA4KSIwJiEtKCRALiQfHx4fHyA+LST+/v0iHx79/ftDLyRMNidtRil0SShKNSd2SypEMiZxSCh4TCpvRihoQylHMiVCMSZlQylhQCiRYDVHNCeMXTNFMydMMyWTYjZjQSiAUCtrRCdGMCVxSStaPShQOShRNiaHWjP++/WVZDhWOidTOilONyZeQClXPCk9LCOYZTiocT+lbz6DUyxdPierc0GPXjRTOCZvSCr++fBzSytpRSl7TSpJMiWNXzV9TiqEWDKgaztdOieAVjGaZznUhD2KWzJqRithPCdPNCWWYzaudkJhQyudaDqKVSqRYjejbTxZOyd1Ti6JXTX9zp2UXS99UCx4SihrSS3+0qOnaDBbPyrNfjh6Tyz+9edrQSlkPievbTOOVyt8VDGIVy5ZOCedazz+8uH9y5Z4UTChbj6OYDdnPyiHUyplRiy7dDT+37u1cDSeYzGyeET92K/+1airajF1RipwQymiZjH+yI7llEyOWi7+8Nv+6tHbhz1vSy38uHaSWiv9vX6dYCxmQSb+9+tVNiaaaTz7v4b97Nb8s27ejETkjkNxTjD+6Mz+xIjCdzT85MiaYTB9Sir0mkz6olS/eDvEezn+27T2qGLKeDH+48LUikjwol37plzrmVHJfz7zt36YXCv4rmr9rmXrlEi4dTvMhUb738DpnVrckE3xn1X1z6rgmViCTSvopmjTfjS4bi70snTqjT3wqmrqrnbCciuiYivjhzjywZLvu4jEilXvkkOpbDjAgUf6xJC3fUnYpnfdoWmwcTuyaizwx53agTTju5bhsYGrZSrSkVX4kzz32LjHnnjZml+leFOXc1O9kWq1hFvOmGb8nkrkx6rs1b6GZ055W0HNrY5jSTWoiG2RZ0Y/5tOCAADCVUlEQVR42nSY7W6jRhSGHYaFYoRtwMgWJFKqWiDlIvq3t9tr2/zbarV9n8OMj6DpgflgAjg8fs8HPmCZNhswJlmassgBA2ZjzizPM9s0ckT72pZFXfefVe36S4f13Tj2sjHaELcH80FN1oxmfWy29miG9sdnyMclO4SQ52WeHbCXl/Uff1GTMQ/hEMqAZSErZfrYWbdpdee2fbS9runq2zQdT6dTXden+lhPk/bpZvaY1NjMNKdj3dq6ZsNha9mGYpqw02KXMwoljTko4/RrovzVrcwS6MR06fpRm3WjW8+eGh2WjgzyMPz9mS3jkge7Z2Y0aex0tgDPg4hnWSmwoczLMM9dp7twh2v7uA0dPEXvJJIQnWo6gZ1uIgnMlWfrUBk4MLpPzjuYu8PsC90aTQPKjD4yhihtC+85CyvEzJfVgVQmol1vBsMEMhFeu27uIEqDhCkXnmEZu3THA2YsN48T2LKQlzDNQ5A+F7yiR53DY3g8ypeX0IldDUeAiqZ2JCpmUwSq5uxYwXxlp8+XHUzv2H2+KpKBFqkeIs2405JyDbF1YWUqj9PzG02RYAAohmpk3bgKliU77hntiBGoasbznx9BZy+h7BaUf3g6vE2jMtULpzX9H4Zz0e3GRjdo2/b2aHPjWQgfMNXk+DVQYQkz61J7qjLJNiq1hScf7kC3XDMfMnoIpWVwcuRRlXWGFAMi0yTSuFbyDQQmoWQ1hYROUCFqyoGaccT6hWXm87xEqvNsqh1+/MjGoS/D+uWEsNdEyLTj6KgyQJbJnJf2TQ3i+Wjlxbkeuq8xaNbWauInzXzeMdoEeDc1Ixl5YjHeYEzXtme8N8Po0xRgg3YOzIIBDiu7EOAdEItWNSIW0JaRNczN8VdbQJkUK5qLANrBXLJitI06PDXm8uVFRCXFF7JQTEirKg/iyBygHId5KUs+4W0Y2/vQCgo8Qy948DSqJlNI1qDUxICtCN3Bo2y1loSrz4/R2xly9H8sXZx0qWdP0QB2WDxMwRVtCCyUcboVaNCWcJaQXcpuY6USR7nMXV/m3QxSTdXQKAbP0Ct+ZqXlN7AZSI0mVViiUQ5yMjzBRmciT8tpJPfq1sJzjN5en09HRs3QJ1DVkmPTW7dSTIsrz7ZNJA2ry5QhIXbKxsiVuYkBNjrCdCp9OinqN9jDMZpiS8QFDxHlUVFppNrz4BLeLEVFmyNXgOLvn/9In4tCh67CDwIoLQOtrhPM+JwYXEp23YFk17w1v7ft7+LR6UmHpEuonutIsz7dPMd7XodjsinqlHaI3GyLZku2fx1W/ShLzfNVRLtVc/Bw5oscBna0C1UkKnYAWxh4biSqDdZqzJeFPwNUSOCp/M9fCcDcKOX0PKPmRJSynEhDyNYK9zF9N61ZVVXt8hLCUExV/Q7QIwLF9W/TCV61muEyrG0kCmRUqsG1G2EmkWIRLccu3T1K72Kd6izXNT/YW0Zz1Dw2ezCHNzAocu4AVwodJGkYRCl2jLoiaPv5j/L7KGykMRISCjUvkOblEFwWtJwjYc0WK3xnvL1R7BwG4RQmvoWBDGSOTg/RMwF19fVasFomrZEjpKqDpEbH+YgsaS7QSNfNQ+xOtMzintA5qq/k7Of5xZFnyFETiQkBidySS6aSksM0gdLD80Mgb58/c+lzKXP02pU5nyJ+QCylSaOp6zlaFs00V7abhZNiXuK8tlUxiWd5aGsMmkc1sMrfz6IJUyRogoQ+JA2m65YRsE9t2vBfjr74dPu9XL0q8BrBz3bzha3mAzt1AIY/wkuNRDTnC14PiGWlqiX9iY2y/vH5s3wMI3EXRHmguuxCTtW+kORi4A0WTihTJXt8fXwbG/z9WhTFrWq7l3C41fj4a30GJgoV3HMxwZIknzhWPo8o2Y2njCdLLu7+7RKN6nVL53lhFQdWGFx4mauZLU7S6dt8R86gN22BTE69gI9dRhaBTmS9WCUlnr/g2RujESVz/cFqWxmFhBKVdmiSsJZeG+QpPSXO4lpVxeVyLQ+z/P0ync5ninmjOQnnGVIVST4RpEtM8fq0PCV/jyw22mJ3lFuiUZC+mqjuVexs9+/U/pHyTm4TkYoCmgKBOqIk5MSPw1wbU4E0h7ey/vb95zwMcvSZ7LSUcGMjEVn1JdRUm7P9TmCvBeQxcI5K7e0f1/oiGVbm74TP4yvV0vlVM2Rqaf5WPGX5iHqsTLbWWIj1lPF0JtsnT0S9kvLB4cQTE6F0TNuiiyelP/vgIgUo3k4DHFLs8llmSOyttFT5CdX4cnr6/NkNA5wg1XExYTJjoilfCgXBuBAgxu75O0HTDI0Eeq+U0y9FfS1fyoPyeIEw1RiUjMR6YiPDm1CjSqvK2NIjTHCy4/LOSrbO4yR1EarLcX8Qz2NwS/J2aL7kFpcxvD3+qpatQGeKecKnZAZcGX03J5pj09Xffy7IUziRq4XJVHmJKOtzzzdR6vRFPEfeq95I7u3Q3K/tRdVSXRTloQs3CfJ8TDylVfFFsuqEXWYoRY72jKbo0+XZekSjbYLhrrB3TlvKe1QMCaUvPMlvT/FAg4GTjeQjiGowlEhNo52M+kmmFGV++3H6/lc5tKNOQZ4dmXwlGRQd2CmNPqBtvEeakOpVE2evWsGsLpfij/AyS5/KRPXr6+n42+lsaGuZ5Amv61Q90Gh1jTF0dXWSv4UAE3CKn9g2PWxyiz/4XqW7POUzP3On2HTj7XWsZO7wAJGBzpQoiWmYezqwdMbmo/sYX7+/z+NAvQROtBlIY9SuCwzzTsFSLi+mfS8hy9H7rlGtJHVSLF0vstO5Coc5PKzwPKLQ4yuJiXwknuebPP4Ctcq83DimLgZROnu79+ImDZ6fvRR6QtyodKO3r2F7eN7Wr8w2mIMGsrtX7bjovOpKIpOBUmKLPFepvX++l2PblfJojMQumEROYiVfxKLLpUehHIexb+jGUSyH+/33or1Wl3NRo88+FGeC56s4vp5eV30WJ5VTiqNFIliL59X83CvPiQjLRsHkZaAT3ZWZSamObSNLHx16op3ApVv4xM/3z4UoHmvSBN8H+kSUK0WlETWcloKn6Zth/PN7NY+juC1GeMkoOXOFXJJQqXU4Nm+97EModc3bvfnQcMfjK/E8fzt9K67h0ITaIqf8XVi1YTU+X4lVoZRUkdQhSAJ6soQxrFnX4orLKW7Mn1btS8H5mvuym1M77P08nenvVFSgFjtxV/9xGRvZ2OPv8m9NIzZNMzbvv9pZbqxIqdOGTjAzyv0+L8cPLuH8Xs5NzJQiG8Xa+9AIaiuU10tFdi++narDYQyTFPn+Loc/qhNVDI1Wl6kgHxVy90t1EbV6IteD+UKXEhKSdUy0DUssznZVv/P60t39VWBfLvj9ZJ6S1lwETuOJMi1vgGTsRjMRJOoNKnT40bJ5G+46+OvXWKLPzgp6itYl9PZtCPI4Am8Ybm0z3BtNuGachFTWCKjq+XPx7dsf5wJ9gk+efmQ7/YZANR6V461kKiax00But3z/eNQmUPVRnPTOjX7/lrhP1c7Mndn5bhzbuq1o4yLNccaFELTHwNnPs+QESHstHKkVwYG1LZ2YqhfQn7+GUvWnsg9fAK9CykB8GR2a5kvgxNv93lZNexseyuqPgRfNeytTZv+XbnOBbaKO4ziZuVvveutdXZeBPaBlQxZDWa3oGtM0isIMAoIGjeIjvoKJiiTqpo7HIgu4JTotc0ORbbCH6DbGeMhguLExxxAYPvCNPMZQfEUzGMZH0O/v35afd8F/73r/di1tP/f9Pf+HZOi6MWH8KOKpq6pO8chH5q5Rz04VKT15UNRO2MBSqFGhBc94hCeo2JL5KJc42GwOlNkmgPOLrYB5YmlXMTl+Dyf8uPFnUBMjwZPy7vQMpIg0cBAgR19BjXHAxE0MPKLnl45kTMrISIcuRbseWKkKpYCF00Dc/XCShuJR3H6oCp1jATLD7/YrUCfie5auS7LLIJ6qQ4cifSiO1LAPAR4hXtUR4qFOHeI0ZChQFigTJL1cJwmgCkze6gi5sBYTFqzVnhmq2OwhHDsLlDXLbxQHBkuhPdlQpopwbGJBWJCEqYqldtgT3UTSjLkHqSPIBIYVwY+s3e2m3OiqdLhWeNhsd4YCZTo98HuSPm78OHhHyZ0JjF43+UF5PKw9y5AcskPXFeLphBx1FZLUTFOlgdjkcEm6CwIVi8eKMHkxyPpFLg/V4s80FOVifcQ+kr2n9RHmlwjj/KQtUluSUJY4dmtaldxojWcSAZ1EqRFFX7CEi4OY4vjcieVaKv+gimxwoSeUwNm0K4knOYg02DadBSFjKMcp6Riqpjk0TcvJyQlFx0mqZ5yswIIlQ/YbSpYs65Kkg+moUWNTRSTS4D1N06fpyOkpKCH6S04MGZtMNA3FCSUmseI5OVkhicFOksFausa0MQeWKh9Zo7aQbvebiVkK5wJclVHnVyxiUNKYRqZKdimMPCOTWrk04jLxil+DKtHj9QbPpqeKdgiMHUL2e8ZPmPf4O4+/88qLr7z53olzI3c9fNfIyMjw8EsPDz9csmrV45PnTRhvSOP9QOmWEbkNScbQs0ZNuuIy5J1OHTRdmqqamgszn6pCnjo26BkqJHZ4ExpSXqjSoIIf4Z2AyiLZx8Yma81sGCYHb75nlNY5v4Q27pnwqeL+HZ8bqoxEVUTVTzqsFRYe7527vRCoEs/vROUnVnK98GoyptBKZNiJ+A6nO9q9avENI+eGhn48WhA7cKCt7UDB4bYFC4q3FW1csGDBxuLijcWxhqbPer8c2llS8uaqB+ZNkMdLcpbs8Xh0zZQvS01L9emSAZ4+n8OhklTVMHyppMtI+EmcTnD0ysRRMYAXbImhsHQh3GyYv5LNeSYvGdthWLp1/BAThp3EyKDYX9r+teT2n5xCtJHjFTYllgCJLRMJN0mTOr5eIU3sicthZGpWGF5l6XBGepo2/dHp101+9A/CVtMWi3V2xmLb2rY1t5XW1Gz7cuPMBTOJaG1Bw5aC2rZ9v7c3VZU1ffvJse8+eOOdeVnof0qoN69MS3WoZOKhMGTp0wiqT0Qn0RRBFqrgU4GVDIO0qpPjxCnFBpqYeRPxiGMFU8WRqSbZ2QdrOTnljCgxY7eMnUeCZ8rF16TE806xrC7ish+6hHmRy6evTCkLBCoyRNx0muj09KLA7HfXnsi9WRsTuXCmo6Z2269nDv8a66ytbW6ubessbW5uO19MAiWNxsq2FLXVdndX1TbX0Cht7qwcHHoqb4zhklPBE7aNRB7yNMP4DBUzVaf2sjfpPw1AzVRkZ5ys4cVOICmzdxkU45S4PpkfWzo/wbkR5vb4w6zFzvrkrJXFzq35uMOcRGub9Fph7uhsXoX60I1cMzODopCiQI1kazrZNu3xStDnpCAMsWLLO7d/YWH5tGklL75w5tDe1+8qP1a+pLG+4snhkQvnfhvq7awqOl+6cSOIQqMHamsKmgraamqaBc1igJ65sfn795edWPn42EkZsHeXQ1dh66YJYQqbd+ATNVkSOT0s3kUQqS0qQhHdUWlvGHiMo0xER7GkmILVZHnC41LFJevUmm5ZE9yU5OKRWIC4WLejAweY0GY63KawdSObzryedJvC0kUt6DNNUbhQ3L0Q6+pZUbF6x7IPhup2Lfxo15MflzeWNDaWTJs25c45S3qq2nd0NdWUlhLQ0qLB7h2/f3PX8MjIsWPHTp77+++/j/Y2VJ1+/3h7S13X9DHpJmBqiEUEVMVcmD+SLJU+COCQgepQI/ABKAV2CJZsn/5AkA24z0yx0pskw0GcIV2iKWfXJAbPORVi58Ev4PA3KiUVy7sp9EBURuCJVYg00TjPRiCKey2khLSBJx1dqK9hhlETpaBoBDn/+nbva2v2723f+vDhloVbjywpb6z4+cjq7deX5y7OfXrNupb2noG3SI3FC4qLOnpburu/yX367Zvmr12xYs2Rrct++Ly9oPKrT3fs6B7Y1PWAC9ZtOkwEeIePrB1UTY1qJKRUoCYRUkl2iowAzRF6CI6QqbB4xQmeijt+7QEDtWfj2Ows7RHIEudTEizFzgcmf5FpCpbNkm41NUW0k65IR/buJ/fpVzy6QZkK+UqwBEaYug84o0hjopiAKNQizz11aOfiJV3rj5UdXF1RXV749eolRz68Jnfa840PzlrSuq/9YH8ZvCgE2lnUuWdfy771ubPuv6b6u8aS+g+3bv2mtbtg266vjh8/vv5Izy8q9eio5gRT8MSuo+zUdQfSCbhYMm6ktGMERkVGUSVTFCKmCt0byXjE8rHXhnS0m/0lqFpjDr+M/yZCj73EZF+K6C70Ge+bZ9PNS95KJxP3AioJ09Sx00310SC+lLtkm88+VTjlkVNHX99eOP/JtRWNJYX1N129+MFpp679fl1364qFZ5prizeWllYeqN3TUNfQP2XOjddPyZ1/deOS1UcWtvb3xX756vPjnx9Ztm6z7NQxTNg5aOKmYYqh6YZTEvEoPiRDIn1KCr4f5El+wJDJB1Cw8mDhO/lDGZ5tFdOOjwt1W77PkuWSiNvVKdy65oFnwROXd8CbigtlqWHh9nhdMHWnjB9Gdi4CRJg4RmDshBVUaU0CPKk/4jd9d5y5/Zl7p0+tr26sL9++uGTxlKlX37d6d1/rmtbe2o7O2lhnWVHbnva6lh333PjgnTddl1tfv3dFyeI1r304eC53zWsrCh8KIv0Rp85BNFWctxwKSLT04dTpJsISfCYhxW44wZTcqkIZPpIo4Vq94rIu229nShx0LAHKAtMerPgxrwknZ7SLmJQYnH5ibQMrGKL7QX0PBd8QuCjqUNtMJYKhiNBlNCoOcALkQNHhoIZo+kNHlwaR5kTmLLr36ur6xSvvnzL1xo8GC/p2DeyOlRVVdsQG+xs29TQ1tc/PrS+sWDu/vL6isHDJiorq00OPIenye7JgwRAoXKZGYSiM6IQhORywf0k4T8hUAlM8cOGOHhPgxBqTSPZlJZtgcg7KfSFWGrtRa95vkzFnRPbrm5J3nMVbW1jIPidRWwmtDMQiyJPyOGjFSzB9LgxYXzRMKINgGfKFoVEQhnJgZ9luP951FXjmRx166NX8UDgaCs6+ZdZD19SVbenrKmvoLKqqqowV9RX0fbG7oXVX9anl0+avfG7lymrshY+ePI+L7DI8HhlkDMCDQGELmilCkoOaIoCMiGToIhJJYE5uRqdsXpcoOfZCohiKTAIV8ciaYNpbavw8GzxPuFtsocreAsNWvqbY/G0qmiEI8GOpfUyFJlpsildGDJDi/9GCfhRJMhKNRCLhkBmNACs8AQDAf3qoLTd28+ENKoKX7DADpkMyX5gxJ6d+d0HDntaBdZVlDU1byooKBjct3LNl093BO4OhSGDWC7fdu7yw5MTyk+f9o/1uD/nALNkp+skusnaRN6kaPluXNLDUXdI4Ed0RnAxxgIfXDdlF6lRQjZLdK0pCP2y39tXNBFcLTjZfzoIs1T8r3dqjEltCq6zkFHHJ3OVjURqJuj2b2t/UCBOhneKOz4TnDPtCkXAwCnsnoCHQ05EIZvszKMvafDTfN3uiJ0vRzAkhU8q7/djUkoNVDQObNq3rKCqra3mr99D6ZZt2b1k3OW/uAw41OnvGog3bCytOLD/1k98Pi/AAh+xCKU8UfVAnyiTqTDkcpmsMQhTxxJ2kk4ULqSqUgsYjEU1ESeo2klVfSkJnzIQLGx52ifKbUvi9TNt6Pjii04fZFuGwdg510jVF2Rn+7Ex4d1nU63GgLopEkWA0GIgEI9FAfjQaDvtUERY8now0LGbccWZRVBvtV/zG5DwJLi7/qfsfOVjV19+/qStW1tK3r+yLha3re+oGeya6lk90TAyGArfdt7Ji/+mTp8+MGe33+BXYMIxZU01IEtauOchpi5zeQffAST5UEiwNsEM0Six/EkqA1Q16mq2RwwiP/1teEzRsvsB2OpgehgBohc2kR4HnJFrFvCqdViIQj2A3SFHwPaEYVIFhVQ0HfTDzYCAYDYBpNAiRqugDS2iJ+N3oRrlvPbw0YIxF2ur56+zEMeNlR/7dt+yp7BvoX38oVtbQsKXjy4WtrQNvNa3L05555t38nNDc+6Y/0bXzk6nnj6p+N+xdpEIIRYQzJ0y9upColYT956BboI2h8yRRx17GBhOSMUR1T25AodZoppL4sTgwFTZZRszTJHL2l1bINLmk40jhtghfaku9ZFG8pyOZp3CEoXjgkOBByfUDqIqBGBQ2gTEIfQbANRQOqSasTyRMWEv33HE4Ihm4gGa648/ZL5vmOHXG3XMOxpoGAHFbWVlH5bbBnv4d7UUNe28OLvpx0d0b8mc981z1qe9OPHbq2yhOB9m7U8cZQsHpgzyF2UOjNBw4cw5skjSOWtQG/DZpVIIBYad2HrQpGnmISHzhq6XLYb/ay2r2VlnyjY04adf/DVCWa8AZrGguYYUdvXXRR6YmMnkiL6xLrIJDIFQTiWiUHwkE8vODUTyiBQlqn3n8HlpH+jEgBV/VPP/Mu3Bhqc8h5Z199KbdbQXd3e19zR2VlbHawR3t++piffvVuUPPBswX8oOznquu2FlR+HrvZsQjtO2AhbIjzYcPC6MwCk/QENjCsH8w1il1kmScQZi3AobQs2HA/OlbJlN9aIDjESNip8gUrWq1G7+1CcrVOrlVvghKkGTJJg/xRvKVyf9ukYngYIgLWXEQjsunI58Ph0PAGaANEhU8XZRqU83s9o8ODh0NBP7KC20OGhsiEyfPyMu79uk7W0o7mpoKOhZgzJzZVlQVi9XWveyY8erciTlmMHjb1GmFa08Unuz9B/Eoy6OAjkuVYO0uFUhNhCOYObyKT0Ijz6Tek1OjxRHy2nrcdUqUPeE5SJta/QoMnq98t4UeziqZlT1t57KcD3RLkORVKMaZHEmiKZTLIxphVTNN2Du68R5RFePMw/dTMBK9SCD0kUL/5etKYNosw7BXsWClVfGo1haLKMg4hxz1QKogVhherXJN54XzxItWRUssNuIRZK1I0lBQcRLFmyZ2KugAI0WEoMvQAAEHuIALMJnZvJ/nL/hJp3792/60Htuz533f5z2+b0UXp6TgHfEJn0ukgHo8IWXvyqZ35XL9C4nhypg0RWxWxXJGwatlbR91tnaYWPw0mcpqTykrq35202WZ2SVJiS9YKv70uT0O98DYyllgJ+Q8bBnoIaQjJmnAUzAV4DIiwfIBKCxeK1OzHSrJebhQtpup8HHDBUlP/bnO3gVEIdw73IGKyoeAR3wVMhUlRD3e8U8JP4OZEIai85AZIbRgBOYEdGWprun2ldwMxH74xVkUSohFiSk0dwpQBnhYGhToCSfc1r3/3T9+fVe/IStLp1VcHhtbfJ/Rsrustr61873aWpaUUT02JZSN35kWe3PK4/lXXZ6T/eh9vT2uwPRodVFMeIxKh/CshIeRx2mkIIQVSVOH91REyuW4kUeoI/U0eaZLEFdqeEwZ+Ml/TUZBAsOS8MQK6ZWLYBxi+6GbagRP1/1bAju8iHyBUEqkFN9jjAF4MneH6AGeZ50KhkIOqug/AShcPigSTN2zLgSem4BnInxpxMXyaNggAhcAVR3oeuPXX365Sp0l11+tl6XFpiQ6K5N7ykydnZ21Zbg6Olq7Ok0v1fdt9qU+qygsedBQ+lSFd6jF7/cMVb8bBWMFnko0NeMAZixCEt54sTcqEVahApBy2ruKYV6tklGAxgBZlRa6jsYfzg9Wd7EJcXQ4OiLnFKwUaAtIV+t1omEkBh6FVwmGKGHulJ8cy8Y6AdbOyidyzaBIhg5hIZn8xIABZDzEEiQTonzWhdGarGjgibyEwwqb5mdsDyXKMzNvzFTHpl0dW5RSZJnLGKg3tbWWJZhMp5hMpvrujtrPuncZHy+JUyQ+ZMguLs7w7qhsdADPlRjIJV2YWsbwroyWxyqRvNPo05B8gp9kZ5wsQk08wyACVOAj45GSjMafhCyYNcWEw6qEV1uXNYZumQmtzwnkxZ1wuAL9oHXjhTe8C2LKjXKElmhy0wVGlTCrdVZw+wrYSUBlQJMh5wyoz4isLM2FrHsCz6IUwHlhdBoirxKWhs0EEcqbhm8oz3i0JKfEctmGrUWZNz5+7wN+a7pjEK24hISEU/A0mToSal+rnkgvvquwoLi40LDVbjV4/YGdlZXfj8fRE4JhqCYp5NEIRFhxCEhwKhI7QU+8w4FqURGJxFNFlgJ+6E8avZq4cjBX5O98CfGfwsJFKA/BN0QiibizSmOxy5s/8ce1TZ9El66bu2EwV3gexJLUcpcyN/zSTlVyO1Ukl6SqWQ3JAp4I8IkI8YhNoC28Ad1W5O8LM87nnjIU/up8+sbEDYk5KRU/dY2kTu2qNpkIZUcZGdqBaFR9fUbuZN6NicVJz+U57XavCyL/08e698SGxcSAn5SZAJMKHktOKRorJ1MV2ggF4FbCI0h4QnawuawOk9F9grRkLDh7nJr1TzzWxSNRCjo82K+bJxGcFGYvPO3qf1VEIiHl17zq0YzugBOFZOrOqJPDz2Jdlq4J9Rxp0IUlSaAZCXOPhma6nD4U6WaaVAIFQUjnlYkp40X2pzYYCvMTtxaVlL9ypblvd03d7mrTKSAmu8i1plMQ49uqd2RbXrFk5hQU5FkqUs3+5oGWt1+tHpxQhIFayH801J2IP5o4zNzgDcJJTj2PDEkLKFmu04ONkWol72UqmLyMWpSdEEmQhkl8CfGeePyHchdL5JsCTOFZiZeojdCprnpQkPKfsim4vUWasEEWDu+JkaJwFSXzGZK1A1GpZscKExDMguyk/0y8EM6TlXO5jIDqfl72ec3mB0oyL9uwYcONhTd7zfn28sLCyTcTEsoglDq6a2tNWDB4R6rvwdtegWSqyLCUW0f8fv/Lb7+5rfsV1IZAvzCIIkUaMA1WluQsKctZVpbsHf87BUQ960tMj2Rq8FLJMAXDl3pM4TJVDPE8LD/6tyGaw61e9IlChhV4+z/b54WgIJ5HcxIZhaVTTz4+HONaCLXhVPIq1pJZD2ETQirQA0IEoQuxoJfgPxWAWUFWhL87e731hiljRoZlw9X5BQU52RW2h0qyMze4Xk2Q6Hlob/1brw7S8j9qmHy1cfLOzU+WGq0VdVbvlgcWBnp3b/toPgfDIjoVCp0KhB8wEzZOlmqALFS9ArGJYMpYopPJ1UpQWadUw+ApPtWyMwguQQ4Lx6Z0kUoLaw7tcByObWiLeLWjIXwx3xiBBDXXylhcDEicqoP/ZJcDgGKE8GR0Ctnpokrnhqow5kfU02jFcWVlZSVSgaZRjMK7sXSuVqsOjHg8U1PXPltsy90UuCLzKssVFRbznK3EtoBY1HFK58DA/La+LlN8fdlrU1OzvvMP+LfcueLe4qvy1zgGphyNo9Vt5hgpvhyH4MMcSZ7F0RCoetwyPskk+anUquUUnBSgvGRKFF74AxdwVSP9PQIMCVZE/m2kK5SQ/5Si64ucIukXpQ6+rbpRYeHiltHoXDY6OAyP6A7hw9EgiA4sbq2iyWvCmbVQX0utjrQ01u3SWAON1YBKCPG63yenA1Me69SzNwQMm0pyi/IN5mxbRUGOYQ8UvCnhlOEPW1o6TAmL9X21Hy0kGza/uLx3uPq16STrnKPZ6/XX9FY+0zajlSOr1MpliOggJHtIoGZcnEYTC6LK9aBnpEIWSZKqGJDClViweaKrVjI9olIOp/8UGdI67S6YGmrtfAvtuK8zZsFCgefa/+VYMhM85lfcO8A+MWI7whFruqeSmCr4TJUcUmltY6pMKpjjiYCUxrLIxWkXY2mYB+K3c2BseXrunluuedeyf1NmbGJBbMFTb+RbDAWFvw12lyUkdH706qc9B+tb69/s7KgtG203VFT5+t0/Z2y8dCDgdgQ8rkDL7u+bnpLqR2ERehmsG5IefIyOi1VoaPEQoHp8K4dtR7B9TNWkRitArpZT3p+hJDu1MqncJfwnlugIHWb2ovYRmi0dJQJUSH3uCOFHgniK4wb4FadsKJW4mwV5OzJNRCGmGgwL3M/P/hE3rsDcpZYHSAk48QCqaVm0Q4gX+ePD46MDt1yUVGEoLrk488bYy1ISN1kKDSUFJQsTn4/WntL20tjPM4Omrs8/ax2uTpjfnHuXEe3knpk9PmvLDn9Nv8Ph79n90XycHKFbSQ8ahxUtz6ICRZyHN8WHrNlB0ctkwJbAAUJaujxMzWYdoxNkvVKK74KeIpIItA5LMLFCRmmC8VvE9H/mk8IB8GSRNQ16NLDmZiM24ajlURBGCRJiHukOHpTpyNsRknALespgfQxJoCeYCXNPgV5CuGfYVcbOLA6P+Odus2c/ZcnfVJySk6LJubKkML/E8FPfxPf7liYWFxfHZ01t4yO9i1eODJddt7HhfKfL3Ds3s8Xsdrv8NTWO3qG+77uf15N2cmW0BhWROBg98MRDT9uXsY0cqUewipQpZMGFuITwrma5CTgryVgIJuK5JpkE7/5r4OZfx0bE0PYxf3NbcFVoTn6yzo0CUOxbOw/znmhYRJ11dvhZURhjofukN0J056ygkgFJUthSQw54gp6JKdCisWAREP1psG38+uudNyRlJ5UWGy7f9HhmYclDFYV5eTmzo5Og7lhT2ykdHbVjkzOBg5PzM6YlY4Z5Y2+Px5DxtNPt+LK5prfG9fbY2GPDl6nVWm2ENo6qHqSMg4hnG1WuV8CfyiLlBAwvOjlB5E8EFYiGMTjxhb0koZdC55TFCim/hw40rNs6HMLy1WuNsdLkrAQnL3bh0CPmtPtpaMMx/WUaDPvh/ABrxQjxLDJxRkQaXAKYsHb6z0wkSxHR4FHRwbbFfXf79kPKZ5QWpdz14IOF2eU3WrYWWH4ZfWJpfmZv08R4fVlr23hX/d7a90w7urraq7wV5jqfMXdiR1Wl27Ojt9dTOTTSN3hAppcjA9JLckKu0GTJ4xCUgC0iVDCqU2MCWD0ABZ6RQPc44Bv8NAw/sQUl8AzR8EKOCkBDEA7tt69pI+FFhY7HkwSWFgOSNJBMKY8tMWeGcxJZKioBPbBTBjSZHbO4JKPK5ihRtAbWjrJyCkiKDJ4RCs7tj66u5bmawJ6MUmeKrXBDYmGGJduaY7nxsg0r+xwLC0uT24ZHhztaW8cnZ/s6Tkn4fq/pg6pHGgL9DVu2TE555nZ84Gj299eMjoztHb5KqY+UA0/2NTUSLxVxCE16rTwSZTwp9MnR65TTYtRqQsyuPKOYPCKMZVAAulr/FDNh/0rRf4daFDnXleb5ELKeUQk3q1JUBD5u30KmeS7aRhjv5ikJ3DdNcQTJzHwDgQkXIJVJOTz64ghHlEyIRClQTWxwZml+XxxcuPYGY6rt0lsMf+YWbNhQkF2YWVpSYbktc8axeaF6Zr627bP6tu7Wjw7UzXfFJyTUJ/R4Fhzbz99SudloNHvdNb3+Gk9N41Df8ODP2mg9orw8UqtXoKysYDYUFydXUCoBtkhW5wFjzHF4x6VQRkLRhxFXLFW4TIcaVZg4QOVwGS9eQ1n5bwdfcAmSCuISylWk14yBZ/NJYukknvGHVBNj7IjvYXD6+MVDlVB90uSVfOWoIpcGNg8BCqPHikbsBUF/65q49gbbQ7Zny7Ovuu3GlMTMksKFknxLbsX008N7Z7q2zRyqH3yrraO1fpu9caw+fni+tuyHhi2OgeZHHFWu/nZHIOB37XAM9LZ8Od49+GisXHs14IPKjGWCqY9mpY4qF6EnTCtXKbU0dPhMqRLKF3VwAodqXgm2qlXIj4Lm+O/pe8hnAkeRYK5TUWKghoRcU0qhBwmtHgx4LDeyButK4Wch+cUvFZYFY4pUUpKwZRwhOyPIV2mKiSkS8IxNYz0EP8f93jX48y13331LuS31kpIHCwo2FZZa8nOKSgpvfsXy0eTc4Eezh8qa3iyrr+2ev9Q62/Xb7Gxrm9G83WdtN++ZHGpOfqTGv/2Dyg96eoc+3d01uHC1PiJORvwwZRcrh3dmdZ5lZApQ8hAEVRBDHTuejEi4mGjiXilj/0O36t5ENzd07kvYsnCW4oOgMhc9ZqCGJ4ycL38fGyhUPd+lE6yw0HM/WTJ2CiUME6AkQXoex9ZsBKMSR715BYt2bHLioblYg2oI+BkXG5v1W+fC5DU3mJ03J+VmbypJLMmxWEoMFqchOzMn59BkzdjMRKepsy0+4ZTW2Zv+ONg59sR0529V7cYGDNdZp2sarXtc73gq3650bd/RMjLctfiwXpJIMpbt9AoFrF2v0CP+QBtJJq5X4yuyEqoTzSQ6BEosbRjoy9Id4hFjhCgwhdj3f1WTRbQR8V78Z3i3RsSQQ9r4RfCFBo9gxAmGkyDkwU/8MVNMI6tDUZfyI1hklMbWATJQ1RDRrLQsOM/YaHYhf+0aXt530Rt5F9ksFYaS/E2X31hoyM7IN2QUPniX4Y/lnZULw0Pz9ab4hPhTFid2vWkqG+6oddX5jcnGhir3cmB73Yt9NTWBoT11jp6e3pHBwb0b4rQwcy2himOnQ6aFEtUjchNNVOzQ6OCtTC8jusAWBJZhyRGqtCyI6MTZaCI7X7f+1fjXb0IM2fcpXgWAvF0jJ9/ZNMImVs7Lc9cW+BkOJaKN4LAgRxho8BzIpBblWBgZGhwsykItBMMNEcisow+2jS0vz10yac3L3lpcdPmNxSWXlzxot0xcX5BrKa5b2dbd2dY38imn502tTYO1CfEJrRNf1zk2bzY6Ao2BL/cs7/vmy0ZX88b05pFPe0bGB7v2AyFcWth3nFwLIPVKhVSRDyOCWvpLpqVkqVYDpiqQZ2rpQRVqvKIiErOuX/zfQ/PiEt2kdeqdVr5uFFFgGPTPa76Bxh4sLEnb/ZEYQcerOPArk3G4GgPeCqmqxDot4KQCBZpKXEqwU6rUx2aBqLGanzr37pmevrs8/cqncu0PWjbllxYUFBZaCm8uL882pPvqrj/0R9fKb/WIRtjY1bGwcnC5vn7Xrt7tftebrkrMJ8/2jT3x6Qd7RvYYGwKju3aOtnVNXIacncl6RDQNHUTU66GH0CZm9T1GptTBd8LK0fiIk0cgfQf6x+nAW9g/m8dqnTDTw8NQSBTiU4x7/HM+VjCQn0qu8+9u8aqSD4oycTAn+xzYtQo4MTwUpdQBN0R3boRhCACIEillUKK8pMFvMlSDi1ZPfqYc6pqdc8zZbns2yZJRWrJy2+X5JfkFKQUWg82dZMhIrpo+ZJx4eHKwtnVbQmfn/Pbzl+baOvZNuz54+Z1do32V3/W//eXEy+M13z1RY9wcaNn15e7qwcX9sHPgCHrq9WpAyZRe8prBuIMcnT14NV0BPlRE8BspRK2ONqjX8BTQiWgjqCrQFvcCV94K9b6+w0dSSniLNJMdd2m35ukcl48K1uggOLBbVa9mwRHsRFBHfoS0g7sKlRH8AJKeY4SEFJkSqkBLg2Mvur1OW17xJkt5zoMPG/MLLbmJJWZvxqVJlmxPf/vCoU+WR2ZbazveK2utXqhqWFo51P3zN/1V/a7pwdpPv+sPDA384Nr1wRMoLLeMtHzZ8uXw4t7HFVfrafSAVK/li06nC9fKdOExMTE6FDzYEgB+WkQhcBPgwjdIhbsIxiOtNuRAhsNPsBKfCXMPPSpUDIhQKKwGIenirTgvWLJ2qUUcPDntPExinRoec0aU6jgSQKZgcYx7/FiyZSTiroTjuBj4I9lHYoiPJKzRjy8O77llavLFvNtyLsvfepXB8kp++YEHHyxJSs/LvbTc2t7fXN3ZMnvAutBW29bx3ms/X7p5f/X8Y+MfmNufdPW/HfC/U4nWZuC70a++bHb7R/s+/nhoqK+1belqZEWxsGaFVq+jycPoY9RqHdrzeMawDI+njj5VUkuAl2cSIMJjyWJka9qbaBwex0O1qIhCQryLCSXhDELGa0TI55GqyI2OxEAdnCf3rEtl5CipXkMLitYgp6NcYt65NqRO58lDkRiSpCfF58rg6B3Ga160lRu23mix2Sz5luLSmy3ZhnxvRvGlru/qtj8xHt80v/TGe6ba7rJtb7146+s3b1uq/r7G6NtobXdXNbieqGyZdtQ1D/W2uz1DLZ9/tevTZ5q6Fh8FnojuqIwg1mDpUCVBry4mSqdVqcMxRqILk+l0jExqJZWqGhxV4VKr5GrEfzGeGUTp/1QTnaKAmrgJvSqgFUbPLxjLBahBoBmSsM89uDvmNMwURyljtAg3cdx9zolWSFD0jMPZj+EufnpPyY/KQU4sjtpoft+28MADNzhTS7OvyrYYyu2FxZZ8g92ZHHh9q9VrDrS3N37dVJawbSl5v6ms3vTeM/svrUs+tFT9TP/G9sb+3J/dde0tvX7PyIsuf++Eo/+73p19E7vf3P3Y4NIGyM4IGYipRjtYS3uPwYWlisIKU6vYatICZTUMSgaoIedZUGZdRE29JHoVIad9/UfqLiZhBTMFxwV4gG/dFoTVE395KieaLHSfyIwg5lVhrH7I4eI15J4cD05bcaCSWT2+h/NEmkSaMhyxOV4UsdQ0dsvd903n3XGbpbRw60Ol2Xn5+VtXlpIqcpOvHUg3N29vHKl/KX7w57o/TLWtpvq+A6/XOX6bH36sLrcBCsnVnux62+3yTVfVWZu/dM29/EHfp5/27dyJNP4nBUwe8ShCpwRqOhk687oYVUxUFBxoOJElhGQoOAoPGqkNU9G4guavE0iEDNeF5J0iuxeOU7ThQo5yOWqtZyxoSZbSc0qlOuDJwhLUPKrIRA2en9qDMohBU8ORSvazURAN+lEpPYECAN5cmpt/HZzYN3fRLXekF16+6dGijIyH3GbD1or9Abf39Qx7cp6tqvmbodq2+LLl5QXM1yXUvzm53er449BvTduTXW5jc3Od0er3WTdXbWnYYvX1+Ht6ej7etXOkZffu7vnL5Ao1Y5JeqyU5SUyMM0bponCdxakaIoqgzkU1qtJDL/EGJCWehCMkMV83miCIK5SV+Cq4BGn/pwcnCU8JVR56KuXuFEs6JusYNtaw1a3QAFlpA2pw4heLRXssGWWoHG4BxR9N0cGmff67L7nNvlB+eeJTP2VOls/dkZu00Vrl/jL91ozyJNcHjc3fvpQQb1ruXUID/pTaprGJ/tz9tfPdt59fZ5gJ7Kz5oD21YrNzc/LGC3zWxsb+uoG3e8Ze27m7crztJ/nVcfKrFXCMagIKFElOrCi86dQxuii1SoUQD2+gZWVeTyyh8NVMnoSprhl9KDH5XPcmbgWQ/34OLaO9+IplJUhTmjv154nnnQg8o+A+ASdnVvVsc0eTgUoVh9HhPaXN0IjyQQXKvalwnhw1+Klr1u++w15xyZ+PX1b0++85uRZLcV5GhbU8LzU1NTu3HEWO2bG34uMTRhqJZ0JH0wG3Z0td2cLg5NS0cZu/e9y1xZxuNfvSvXPOa8Zd/uYvG3t379w9VDlaPX+vQsrb1YxGKuokQIlLF4NR6ChVlCpGqYtiWKJ6kuKSQgtIASgq/OK3H4LefySb63d3HLYde630QRsXUv7vRhyuY7n9QNrtfqZ04McZtGhKJdTBZYq0LMh2OYM7q/UISXiQqEo8pPYx5CmmMrMWhgMDP1//1HO/52QWbSp5/KpiiyX37vKHnBW52RleTNJ4XNvH+z6Pjze9/f0wZpfiTR/tt7rat3QuPbNgtRnbrZOzNcnXWjc7rckec411T53RPd03PeBo7An4xxCSFMBTy2CkVWOuCRMoAJR7R6Jo/mpiCqcJsydJwUvYDWNTJFJ4IT1DgAv1mQJGoaWEqxXBnUiuvTJPIojEVCgoFOuC5/EDUIglDlqrMWmNnk1aLOrF3NzOqTXGd54mSXtnAUcyeG5Yw/TGL4/NBObG7775iszEtMuvehyRKM+J/lFq8vXeUqM9qdToaW5/ouX9+PiOnsrf4hNg912TRp83+eD8/ETdFmv7+UnJLuP1fqvP6/WavVWPGJ3WF6cGRj3bXQM9s+PbDFp6UL1cq4uhxUMjwdbPIj1pTtAjkHgxOikugZ9xShnZST2v1InNQSHnzArweIVO1K0/PnndKzETdAyiKOIR5ZJET07QcmNalCycp3KhGQQVFBedBTjlytXd+nhTRfFYCeksY5SZgCitPXq2OzCwNH9/br7l3pJ7izOKDdm52XlJpXn2WY851WqeNns81pbdHAnpnJ+PxzK13VPlMWasDLY1bd/Y7valb7TWTc95reYbUr2pU54qb0Od1+Pq9VT5R0bGuvZrYxURWpgM0IQqYjSixdOB8gk8gWU4o7xUHMU/hxQVd1ChR4iGesjpAKEdJPE8bNZOZJsCyn82isXniEd0njwX6DxuhmPpU4f2mwJwxnEyGDOXMraJYexMRDF8rAru3sVFTKUU/teJ4QHf3sE/7e+u/Jmf/WjGQ8/Z87Zel1dRmm01J5W/2DOV7mne0Tv6WFl82+KMhOcprfvTKyoy3qhdaJturwv4Pei7T1Q2W43eqdsbvIEqa5WzfWAgMDs5N1WzvLepRMH4fjXKHgAUgZ1YStHoLPKUNyStTKdlENKrWW6KAKSQ9SIzEjpIZOSHOwHxxdqL2DO7Vu8QApQriKbo8h+LCVocKHn88aiG4BenpvaM1GAaSSaNraeBnhGgJHWUBCgDExfPvjiOKbwmYnJ49pZnbz74bv6Fhfc+nr012z55Q7Ehrzyp9NI659YKt9u958WFWYf/49b4+nd+GJb4WX9PeUZSxf7OycW9bqvbNeRwNffuaHSaje4Ft7fKbG32Oho9H4zsndvnCIwP/hkbGxcLnPQxXEjeASJpKVm8CvoJUV+Fz8PVNHNm8GiDqhjnQ9rEQquHNpJCp++EggrdqkmLF0OQx4gpHEYm6RRF6TR0HE4HtSQLZ2aBAhk7NSzAK2RKVBWJoYpiPoaAkpsRzOSlUZHox/ve67n7oWe7chMz773B/kp5nq3c/tCBlXS7c/bFjempdqdnaW/1rpZ3PtsWb9r548EEhPmy9663mZ1P1XZ8/1L9LOy9at9Qj8fT7BnwmlO9Zqe3wW3tmXON+AccUzWefYuDJYjv0J+o1uli1CQnsTwLN3ygvijlS6tTzUpqUaYjKsgBAd66QVmhOQ8PScIxhJz0/x9/aRqpKZ5HB49Fx4Y4TnxGcfsJtToqyVmowsWCovRDwBIy/yw6rnDp1CO8SHURnn54oHpx2m6790D+lRWltozbklbm7Q8l7X/ObrO5053m1HKv1d089/Nc74cvxZu++ob8NHUOPlDT7LtkaWnYc6ir2Vvl9PkDblC03bs51etMT031GT0DA7Muv+9230DPeNvDcVfLUbLTQqproTjB0SCkgPEsWn+4WoXIH8PEHXELWo/le1i+OuRotBDnuA7MEKl02LA8b1YB5I+izSE8aPDrc7mBC3uCcQwYRAijNxqK3FOI3VPAFQxlcgQ7RzA6gfwMV4aDmyQogI8u6uucucTwyr0P5udWoFxXXvr8ki0vz+6ZMzrtqc4XK8rt6AJj7/DYx/XxtS3fHES7o/aZ4TmreWrl4P7O/pWy3lT7RuOuIYfD5nJZHVar2Wy0mY0DNc2Oye+c6cYtY98Pr8SxwUmK6vTwoEENKgUlCU3yE/URnjLIZocSsIITfAux6dC2xzrDF74y5NTE9QdYCK6KiTDc0dSlKM/trxgDOxPn/dGcMcaKXFPDZmK0VC7mbAvlJ3AMhtLVYztQE6FeyvrlsfoDrxQXluQXvnFDbqq5otwGUHPNTqezZsbqvSKp3FxT6e9x1Cx/vC3+pXd+OAh+dr66OFXjDzS2T5VN/1k2luH1WUfcZnezp8FYN1AzgEBf43M4fD7Pvhetbtv07oltRbEK1pnk0PQxWjBRMnNQUwaOImGiJFWrUf/gDD1wZzteyYEwoY6EwYdu2wg5r0rgvlbrFJMiEgdF9VP0PdYST15Hn4hq3TnY/kpPxEE/SHg5Ry3jNProCNACA79gJbRU8GxFbt1GTUTFfjzGlH9u6npg671biy+/6qcbSu/wp5fD0Cvs9vSnvHMznmlnstnoXd7RO+RqbCqL/7Ly60PAs+3jRXdVe53H7O6cvah1sL+ux+8PGJNwfIi3zlk3NeJGya5mwOf2TU95xxouGBt9b/8GMvRqACqjaqIHhZg/jfTkimLhDhdMS8fEXerEw32qQ0seQiSt95uhWkkI+FCK4p66k4AKhwqZRCeAI5KDc3U8TpZ7s1Xh+FOVtpxy/vBiTZYiTQZ06ehh7hBTnP8mnsowoEm5lPX4RPfi3H35VyU+XpR3s91rtF1vS3cmlTvtTpvV7PSUpxvTzd7GxqGeobb4+NHKjzrjEzre+3Sxzuq2tnscwxPTnbWBfnelf5/XesDhqkrfiFMv3AE/fG573Z7KAceI0+qfbVpkJ0mPKKNHls6qHVynWkKSdo8cmYbFB/udWi1gxxmjKOYFwQh1i2KFwBka+2HeawKKsIlnaCaPRThJUhy0huiO7hH+Bg0Vk3f2XTkQzPF/GjyHuCmUmIfw8ffmQ45qZf1UXT9sLr43P3FDttl8u7E0yW63G41OsxO+dGOqOd1u3Zxh/WDHBx+/9l58ws7xvWUJ1a1dO4frfD3WuqqqpdbBQ2VvNqSmW7fY0i8wNvuMGzeazQ1TVqunZrtnoHfHgNVs3De2bzEXigkCFN05dJKkLIkUZS1UC98J5457FQS8hCWIGRwSU4WeyiAqRcJpird/G7BdH+EFliyGrMmlo6QRGyZJtHepd8SzV8Ih506gsceCn6yF4KQKTTQKC2HM26HlIahw3rtUXlKBoUyjLv65qf6x+wuLSy8v8nqd0w/YS21Osy3VidzxbhvQNDufmL3I98TbT+x8qTa+7ItJ38GEXbVlbw4e+IuvK41prIrCroiKiLsoiopVlLqAo2DcEU3cRX1qFBdwKcaNGENRnsXk1UbwBz5amZcUylSYagRasVRogWGggG0pi21HsCwDszIzkplxIa7R79xaX2nV1/a9Fv59ufeec75zzndKvRXXPXrbq7Uh35PdA4L6+9KnwYhcq9Y5uNJCtcOg5r38ktHFG6Ulk291dupPZbECuaSUFFh5RJ3MxCPtgegdN1wUHiHzgYwypZHpzcqW1ycqE7e4DCdeiUq0dIunkaNp4WPYIRo5MfHE4x/lVBJJJv/zDJjxdHBw6EwhP53KsPCgKmqgiWAU77PQ7EGcHYIoyi3Buje8e/j6+yEq8JJuQxVXVlXFAU8duwp1IJjq90w9blCZrZ5xhO/9A9IvOe3g6TvWNhz26Z66d2+t1db1rnWta5fDdE0hV2HitJyufsGLQnq9weGQAm7eaXBPHJ49pFQWw2NKLkYiiaU9sEJhmdhCPZ4ySWnIf2SlE1MHnwm8MywTtr9c5BHjTspHwP8IXsgwx7TTRQnkGGc0Gr6T64k3Iz+RO2LxJgIPePOpFyUDxJSCAoYoMq8I17HNASfteJLfSkWlJbF1+P9v0PA0vnTfh9e/AyBLanQ15TgHdSbu4YfVRo5DGxLErNQuj9Xa9e5ll9XODIi/5PTl5Pin2nIL1QHf09qf16491L3w8/iyUVu/tPPppz/BblcbjVqDU486O+9MwLls4kdWA/vzFUoFLlglAJqeCUizsuA5YYuz6Agrla1KcHR0PCVnInuUfuL5sraavBjj2GT2IxHTOFJezhxFKTp52UYmOBKgdAOggBOpd2SPstJgy0HVoc2vgNylPCSIUomaI/YTZWJQV2Kipal0ejKJlB8aa6ceKn/wvgfvvMk0BSOke738sV2cXr3rJ5PR8HQhx3EbuLVZtGWOwRxNmUXLLzmVd+c0r1Q4XTr/Zu6q3/dr3+/a8fNzbdxbba8erqgwwGfytlVotWiG3y06nSqXnjMJI4GpP5T5iuzi4mJFcgpS8cATYBLhBEqZnFHsbux3MHTn40YVy/DwYZ3WHX6xEmrx6Y0EZQY5yoyTrpHZZJrSGPHoo2nj4448isIjzNVA/A4lqrQT0Z+bkYw8HEsrIiEMThl+HHRrjs+i0ZiU/SRTlIT8ewYBeqjr3VnHh/c+eOfHN9TkPlGSy3HliMI5nZrz1tSoC0u4XBgefbPqYHvtZTkrmsEDtTkdOTnj40tqboPOXXj7TwdHS2tndl5TWihypXppWsx9a4Pa0WbRjhqEQUkI60U9oPVNz/+qUALPFGVKMXLHiJTSkekkYwQ2OQv7HT+xcYAhkKbDk7q8EIDKiytuR68vQ0ysX0rY7gy2xLmneLB5ovTAi9FLbJYE5C3YhAIKOFG7cgIlj1g+DuCRTABmE1ElI5EiVEKEqAmJpYz8le5N0zdc//Frr5WX1Tz1xB25NWUcDBJ4TIebqzKp0fPG7VEbnc0tTagDW5gGqOA/0Wu4/3XY/QOv3vFiS/jrqZVDFmMFb7Lwew60FT5dUTEqIYUsCZJF4p2SXu8TJ/wLP2Yr0L1YBExxYYniCL0oK/2ii7Iy6UWL8Xzs+CSCGVRdCrV4sMEAceYoQVBRBjue0MNNzsInbHy8osSnfNGMDkbXoYkLOlJQOUs9/uTMFPSbZ6NBM4UpnCWjw4MZJOTmacwGtW4ju5SSTHTJa5UHqwc/fOqBD2reuKum5M077qjiqig08gpGY+EThVq1ujAX5lrQeJoua9k0u0rFdbhyzqz9ua3+qkH1G0L3R08f2t8mmr9bfezVPfX1V15ZqvcBS63aIvC8YXEVJD1EBmb9h7L/hpO59QASXihbouQjIUTC/qetjnRHJt2SU5i2SGzmnT7xe1zOCicYffaRC75jq+jwiqtkYEN4cQObjPelaIlD5RLcpaTTM9JTyQElxi6FZOOwWpMYwXQKtGsAJ+nOIMAHzqQh8N5I48jiS59/+MCbZS988ERNSUnZ4y+Wl+kciNcFAxgmo6MwF/685PJUbhk+6Fvd312bQ4jeffAQL5VKa/UVP++27Ozeq+mZUy2uGOpzP7mmNLyqcgmjo7y0JFgESdSL6rbBGf9+JZF2RVieCth4akyg0BO+O9J0CIyAJ3lMQJdZJOTn0pPwFwZIgkp/oj2PBVR+yzFRbHUiQItyxxEc6U7zg6kl7ghWu3TSaQg3Lzz7ePjpWShMAlYkw6WAA4qONBa8J50O6UQ2QItERIiAygCcGX/ONM7e+n3ZCzfseVx3103LizfdVP4c92aNg5e8vNNhciAIkkyLvN4z3mW2j48csIs+dHNB8mKZh5LlTd9z2tkpW7Bj32Tdt/Zmm+XLtz65ktOKokWtxeb3WowOn0sQOYdvIjB1S1G+skipUGYrKZQHjYT9jgTH6QQimPk0+PDANTMdRB2oTyqxxd6XA0b2SBRCT3Sb1qt2ywn3CJzALYaOl9s5I4uUKsFoPtGp1HOE4xOogY9FMRD5Qnk4PaHlS457UtLpWdjvyDSQeD50E4hsJj7vnm2Nh8t/f+jFm7xrhmXdcw/rXuBe0dUU6kwmeI8Go54XnMuGXUu86t1Zu7Rp3myza0T3YPtKsyhYtGF1mzDd3tUz2f1N5465kBjc6+Kue2uwTa0RLKOjoxaLELbMrrpATjn905XPK3GCAs+8lGKFEp4oWA9yRBEuZVHNDa3Mk7HlqZQETii+UNXNegXzRAJZNvlxv+Wxx7HpuGPoG+7R8q/oEmbRESHKHNBzLjnnchoWDkQpH5wBFCFPlcc6sjOomSIVVB3WJk4EYI5M3PnY7kQ3Z+ft3Da/9NxHr3x827OH1Dur3ix8RecwvKLTcQYEOgBUwnYVjBUOo9S9S3L+YpEEPc/zqkXRzlss9do2PnTA32L+dmrANjk3sHdv+Luvr/3atTYqIi7i9dKoXhh0610ze7glv3/L24rsooJ8YKpAoAS/PkIxw3cCTZdFhGImVYYgmKPiGypjzMSuX6duHneKxlFK/9ZlLFshgCW3H8nBJ3vKTXhoZ6bUO+Q+aTQUyuvOPvb4JDifKAKjho0LwINelMqE/mD6qU2BiHrU1TN9KQV8qcWR2aUaXdl7H3xwV80HZa+YjEuC0ysZJKexrULgjQaT0dV/2M3zws+z/IFfBAsvoT4xoDFrRi1t2rawuM+8e37g21ZrcG5f8NvQqFZb+tZHKxa7TeDDgxPeML8asLsqV5ck/2z3b9AmIJc+G+uzOFmRjvWZiRXKPskoZmCvJGCInrk0cphwHZtQBrYe1H/36OXlGePFx1c1xM3bjk6Bh9jnqWy+NeadRNLFmak3U0c2U0QoAKOcRK2bOAiQYABhQlKGSSSFhIYjuFSL1bseKdl5z7MflN1w65tPlbxgchh5Xu2QxJWPDAaBd6hNpvDCYxbBOb+w+/Av9rDdZSws3T0KTwgbGpLJZuvMpHmgactI5bZgMNTWxrfV14clHuUkyOB5sZ4D9onqrmV+q7/yN2zz/KL8bLihzCglY2FSkQPhhmgTigJYlpQxJg0MbCEUhWJ9yigwI41PgkFKOErxWD/hSLZLMiEfLW2gmde08SMT31m4yQbsYaQRdPyOp/GCyBQzIRmIrEAWCKUiMEEINc9gwr9g5WmgRjbSIdkX3Ly8cfbxqhuqqkpeWTLlYqEaDEbJYDQKeq8EEXSDGt8NokNyuhamJtqnzBqNWajgfMAzbAnzfGjf3EjPzFh/69g3s7a5uZA99N2oth4HJ6+360N6iZf2Dk4ExhZc3q2B6l/zcHzmKbMLlNl5BazV43yclVmgOeFqEnasBIy2Otmik4E0oGYoJKp6yXgmMveRnwkanxEkZc+eLUv6Fk0cRRDFhj+Kxg1jFtkp1PROvCarDskgTDNwUbI4CR0KODyZlh3jPVPAM6Oro8C3Y/fjHHfXho9fcDgGTUs1JiPvNhhFgkISsFINIj9qhDspLszb5ldsNhgkaXRQzxssQJQf6GkObG+u2zxU981cz/a5gR5NKPSdxO8N85Jq2C6GBkXRbp1ROfWCf7rv1+yiZ5SEqUJBjxNS2OmZDvOTjcAdpSP4gS/IG2F1Ak449/DnY+VU6CEfl4l6devCenncc7TVNXpFaXv6ByqWQIXgAposWcxGuaMx7jzERxDiI4mYzOTUC6jD/OYTTkaTK/T/SFQTZCPCd5h3ohZZ3xFteIVvx+zj5bqqDR+/qav6aM+GqoDA6wWvUy/5JEGQYHsEg2TgJXE5sNKzsrD9m+0euyRNuy18eHR5tyoYXPBs397wVW8rRiV0frNXM/DtQCik0mjc1omtNqtLtIfsKr/LaHBt82N9KvML4DLl5SuLC+DTU8EI6GVcFM3T8kzGVyqmxQNOKOBOOz92mBn7Fjcc978O1cSxkliIMXySzMuj2V0+RLE82QBtqlZkaooU/qQmp8FhYkL6THwzDem3VOLrmAwvE+pLhtxahvKCPMVi+6raVF5TVXKH7ok7HFwVeE+n3i0ILp8gCoi9kSRyGQW7b77zYM/KSM/kZK812LzJGhJtAw2N5oHhji1fdW78qqezc0f1RlvIHhwYCIoem18VUlmtIX1IA2xtVtHpnglU/qooKlLm5SvyFPDqySqhRRaYgWBGJXiE76QvQBZbHuAyrz52aul/iK8kDoWXf8ve1roy+bj9f3TkH+wkxXY/DgPtjwP9eRrwhFIkEh5IwaFqCe9UlCuieimN6Xpi5tvxuJJIEOHmvBRoJhdcfaBu0Gh4qGrDDz/ccf2V+9e0OpNF0FssFlHUu5pFtygOwrhLvgNmW/f28RHP0OTQ8Fx/zthQ5UxQY523TXW01LV2bezr6Pi0dqzXbG21DgwM2zRYpEGVxq5B66HGNWEW+MGZbY2/KYqURWDt8EIv3gnZ2RGvPoUQRV0IqkSTASS7sFLxwZb/u54hLqSUPaXEcbnyR9ZPjBVFXqf2ELFAeLETlL6zHc/iTTa/EeFPFqr+SP6ABhKcTLXxKF4EWQfKDmqLCJNwvALsbEWBouDqvLzlmUEvUUXff1ZVdeVP3TtLEdcIYR67XnK7NRq7XQNkVxf2mefmbbW7be1fDWE6B0ahjB8Iafybqtub6hrqclq6qxs2Pdnd17Slo2HzxoZJ64A4MKzSeFSefjzMWOC+HXXdHyixPpVYngVAtBgLlOqUkPKkJz5wR/GTIUlHKJXbYMvLxjsRrfgueNzlh3zUAkLZ9rBP5PvRMSwTgI3wS3gToij+PI1kj6lXJhkEXSqq5m5OzcA5ClNPC/TCJNQCI/3JKmlJ85DEgpRX3PjIyISw6jXlPoUBMk8V7nqkUI0z02Jf3CmFB10qXC6NqN8f2Ddn3TZ5d0DV21851NtTB2WwZo25p71uY23Txk83bazDpq9t7+zsq26va6zr96g01q0zQZtnstXmsSL65Ke3bttym5LMUIGCHPoCNEIyE0/6AsXJeNDihEtKRj2TSGU6SUFByUre/zWDRxZRlKmlWNMTlU9kcbtMIcsW6mg4noQt5oiTVDIeR0fKFTFAE2JgmUnUYUaSEjSBEBwnm8RM9BKEKM+lDnMS3IWrRBK1insO7tYve9U1179520sVa15fhQWW3WLZtcqDx7CrVNN2t3vXbPVWz3D/5hy/rbdvvLe/rvLTzoNjmyd3tDQ1fLG5r7qzpam19Yvqr3q3N/b3tA4N9Xj2mc1BjaZ/smdyyKOaXjUu9o20b7mX3E9l/tUKLFHAqVBQPp5BiQ/EgMmugwuHicIHByjz7GNqt2UyLiEBF0sk0xWLdmy/AV5yijMq+0cLk/2dNRZHJmgToDQx95SkU5AbpokubJoopoVDpp/0rBAZXXwxok0ER0gaIXRXXF1QcGP+zS/40bLp5lFpyMGL50xa3q0XeLiWenezWzWhmhBd1pXAmR2e1tbqnOHJocaOuk+72jcNmycnt85P7mgcrx3/oqnj7oZP8f+G3q96Nvd1Dnls5h5V0Na8udcz2BzU+N1hb/vWyoaXi3DBIgFPYFmgAKigQWHm0a2JXY5yMaZ3A8cJ9H06sDwZUX2sJfpPKYG4GZv0WHfEMuQSE8YAM4pqNLN5FKw7bD4V1DIRq7PPAmV3YhbYTZijm0momAR+4SRhoBSiTXTJog6MpMDyMm7MA56K96YbG5b1XijxG1CtrTZqfQG3wEsw7rwoqJrNGrfKPF/ZMj5Wvan7yW1N7XWYHNfVUDk22d9aWbl5rOHunJYtXU/ejfEo1Y11nZ3tG/s6e76ZC243Bz1j/c3mkFXVPARPdnis+vdb8p95Rkl45mO/K4qZJhOgxB2oEp+UTmYJS5RC0DS4n0Q7xQkvyR92JaIaRVNmT6K+pswmseOTWaEolsdExdUIUaZrQwLUlEBClp3SrjQhAz4TVIpZ8yvSw5jPxXTTTwQrT+sTQkHUV/wXX9ce2lYZxX1U67N0Op2P+bZ0SiOh/0RFCMhospLcpn8ohgoBRaMUhjLNwLTXQFFIVGiSkRSWaxOyjGKaZHmMtmmaV0Oadm0X6mKHc+tW59Tp0Pp+If7Od4037WV+Xe9t3D/z1+/7zjm/c87v9CxVLy0MnTm0//PXj+4/+sulqZOXzO6RpBssiDOXDgTMSyuxxWOukM8yPtY3HU+4RiHGEs96fRaoXky7kKQDWY/EZzzusPp8095sYm5iYq2yVqkUYoimorHFknD44EYm4voJ4qwqEMq9u9s6H2/bubOXJG9QWQ9iETsS36xT9k4KkChwhx1oxUuy0RJysvo6WbPsppJkSV1NwhUo1oVXWBTaBEwpNmLPq9ppejZjQxD/ADJYHKb6h9QHaQqQEHUrnXjIGuP2ZDW0ELICpKpOTa46dH5+/ujBF9/9cODQL+cOD3wJg540R9PJtJDm+eWiubKcgdE+Zod2nc3qp2QHDYuMd49O++3d3aSuivpam82a9fusWZc1bAnCB0AgFcjzM4Hi6nH+8MBqJuP/gTMp6Pbcpeogi3Qd/KXretvgK6EWnCAl/wgvJDYJSDJKVDUmtcJeZuieXLBWYqKkY8/MukR9SJ0I/27QZkIXmIJLhtdELhPVf94DScU7toFSpp7sG7E1SeAPW5Sma4IpoeHAVJKMEgfSucDtqezsuLQCbg21m+fnD4y4R8596R74Mpk+4eRP8Ol8MRadKOYTQcs0OrRZ3ogQhIjdeDY1Op5K2bsJTCyU4Lhc8azFYXFMey2wSJ8AyuSMGUtYcbu/CZdrqT85KLeqTEATEgaI39tgFNEbQ9Yd+W0wnjex/iM0fUE3RFwPif0dEkCypJws4JS8gK3FXpspepFYpo0JNOmD6H4SmwwX9GqoVN5z860kSIvMBlSd777hPhx3JqQJAgQWCf0yBGgrhrIzCYGnlABUg/aDl6u5qZHqF9WVMxtVgU/Pn3HCrON7pshXhGhxOV8r42j7CTYASo3vUFC22zGYE23wt4toUj7J5o+7vAmPx+oLFSJzdH/OnAokeQRI37o/W8+UXK8q1CaVUkUBErxQrF6otFBBE/ORqPmYBDBa8XUNvul1PVqRG+bFyTtlJHDlu7VR2gL7ToqJJOMuPutEHaw6DjwEfmHiaeAR7Puj6DdEgxwp2QBJrBsoLYz39RAnxeBQ/BU5UYjeaRiKRqGEUNCeqrCQvrQxcubdZ6aqRSE5lZxJ8ukknwesq0unIL4SjPfZbAw42qGQn38EcqpjJPLd/R+euEVH416rxeUHnIUC+KZVPhmt5L9wziQHZh3ree9vajWk6jt3d+L32AGP/iYQdmBtWN8cmSFsUvhJdHFS41crUnXU8kH+/FY+WZL03uwXXYZIlnRZ6pn2pnrcyUq8sUEBaDMwZQl5Yj/ZRJkmeKCgmNgkru1UFkYFlDDtUOknwZDtEAyDF0oqAhBkwNWpUCg5yFE/kT6d25ianX9t//4LG6VFMy/wSQqLFoFp6nQgsz5qXY/buuuwMcVf2+iYbRSbk532OtA2W3w8Ds++AD90cn0tMMMHKjMBM1JzlzyWfOp7I6fcrVKSUYJtJ3+esoYkbQexAQrXkUFGFI91LcCE04Qvit8bnaPLMEuyIbF4y7PEUg6JIcc64Zknz1aTmFdqJnNUHxd3M8aQ3w0TD0hbbrwGbe/UB4tMEVRfURLyQCtmMN6J1BIQfRw61CqlwsBp9JqNk7NVt3v+4FHwx2lhNi3w/Iw5gK9k2lzjT9aOp0pZoPgfnrf33T7WB0wh9U1w1hcAtdvj075CYW5ucqJwik+e+HhpEWI3aedJV2bV/6OS06iVKpWqs3M3vM+bkDgmzRCcduxQ0J/MV4JpfxARHjYmIduKLKyIxlYZwAZ7I08kSW6TtCStEIYjQ7WubAH8cG02A0+K5ik6ampikvPoN9y+7YG7t2HoTgsy8VTQjbQHok0qmEd13Y07wC6RrA30aHF3cpxWp+D0n2/kZhfcBw7MD02l3U4ihKLCUnQlin1ateXDEWHUlmo81ySg3M2WuD2lvxgDoNZwYTI0uVYMLZtrqcXi6jezzrTDUwwfG1ZznAl4KkQG9CYwTCRDT44nLFLrgzDySHKyFHwr3nDoYflbrpPpqG5JIjXKsNCrzibVvfhGMkQS8oUxr6eSGLZUFALzzsg6do1ie7azArs7UH8M20MDq3FXwlmiVncSyr9xG/IcLdityByR6qdShaFwRsyWeeeLpanc/IcoKB5Jg09CPGTO5/LHl6J8OloOReaKnmMw6XXkSH6+DmYjnAxoe9ZjTUzS/oxlAoFMNRAQoPA/O+4puy5yuD+R7YALSuHRzl7gSeO6kB7GCz5nL843he3UJI9LlMpuqIpA8ubxJVNYkqCV/5fGiPI/NAEauyXZtUk/XAkDTzTyFe1ipEkmHldne3szPPrb2Jz3HQ9c+zCsPA1moj5DFHaDrkMmmTXCEuGErDwm7OE+02i0arUxt5SeGjp44JkLSFJAHRF3p2AWPFXydZaLpVI57BvtBl4SnrSAsAzPPnvcm0gEE5OTYOsRv5sz5pj5sLtq91hcP2pxGvo1HPBUwafH9oT6LwVITKyWSmxRGAYGBIBS5SI+svu1s+7Pb+aN5FIXW0LSTbWzxCTV2c5/nSOx5lM8+KJ5B5jw5rEAaDOsfHMTMaDg7OAx3YyKbtYSAzgRxG9H4wGbLoQFJvl+cj1VGo1KbdDrOU51tFq9lLtw9uDsh0MLuO+S5nQyIASFfDQaRXgU9ISDrvHubglPNiFBXBLKzCIhcrIGQ8cc8bXC3EQgUAk40+7DC2WvZXq6R62F+BCnxB5F/E6lIkgXAk3cnjDthB34D+YpURFjK4MZ6uPKTfSH+EEeGm3hkTcn5KSxEvgW8WWeZx1jHHU8CEqAChwBND6R0m87i+DZpHcWJwFSuPJMTrWF8fJUyISEJ42XUOg1GnKw1Rruz5VS9cz82bMXPjw6FeWdMEROni8uF/NBIRYVMsdCZU/KNiYBKi1pg4p42uMuX2huzmMrTGaCa8sVPu3mk7nVcCnxl15tMmkVJpVJ3al6ajexdr2UwoJdJ+RYvR2idoRFwBOFdojiERPjH7pF10bmZco1RSQ9Wmnsa13nU/oBmIk5YsbTNZG/BAFVPOEoMVTRdth+F7V0gRKBgj8JxJCfRK3ZN9BGfYgSca34R6ItqQ2ufD9MEaftMugU+nMrC8gSzw8cePv5IWeanx1K5sxRoZjPg0MKFkORcF/Kbq+beDmadTxpyJTHag3OOVKRtbVEJhODbZ8dWVqvRRx/mrRqrVGh2q3kKIhnpYt0e17DKDvsUvoRKWMSvqHovWUXXGSaHSZ1C25xkS7DzEtPvBqVwBoTxewzhe4Uq5N9YrsTf8RLAC/6QEkkFCmzibA7xAYuKkymoeCsowt9+9egO5Z0Px+Hr6TndDqt0WDUK1+prVzYP3T2wNHnXn3GnXaecDp5nPVYMV/OLwfLwXzwmMNrw4W55cRLcOJH3Kpjoy6vxWqxhuYikxORucLyavEzc9Qp1FasF9X9CqNWazJC617FCkTaACTRS/QEqLBFWCivvb6VblC0+2BvMjxpyVVtGsCSd3XJnSWJX677TACT2XX2bhLtPdYtQLMZtgnPW5thkGhgMabnYyYsFtozIWNDm5SNZsQmvQ7BO7i6ThWN6tEbDEYsnX7fF6sgRIbOnn36sf3plYtJp9nMo2A+lq/ly2EQc2VfOIuBHRJ+st0pDpyJp7Ipn9ULj2lyYq4QqBSFeNG8UKuVvT8qtKZ+o1rBKbE/wdjtRAnoLqKTUaok2vcHWVFy60N4ghmhS/4phUY/2CM1CMvgZE/JuMvlAMUf6o+G9GZdzJJVLV0JU4+QiW5Q5nzCBWiGUUKXMe3PbTjzKFPCBGhYeCoFYfwSqQCi+BedNPCWEGsqsGCKdIZBXZeB0//sWJw/O3/h4Lvnznw6NZA2C0WESeFiMVwORiLBRCLhTdnHuiUECUwZnn2jcfuY15V1ORLw5+cqHwdiQgmegsXjuWhUq9UKvdakBD+v6sQGRYiEdAc8eVQu4qCTn0RREa7Pa+HSt92E1AGn0ewdHGZwiCBdViRINn77XzAlaBvD9ibCkZ13MQcnitrAqNONykL4JsKZTeFDkTLVMe2gEdAPkIGHwBqTLYUIOatSRL0IRIw7FR16DUQtOJ12H2c0aHrOn6zOHwSkJy8eOrcBf14QAotwljLlcjkYtk57/H6/xCVJMDbuTjAlfamsf3TaMe3LlAuTlUDMW4wuRvPflVI/ctp+zmjUK/pVCiWxyTt7kcdG9oiGotzLCDvMp8C1+dB9sFBgRfEr5zhDz97htwCATGNNfvixZMGobBgPHpImg9gsQ4wSY+VZbQhLGIs61BR+ogkeSQ/WJ7cDEyJpmjJZIoTscOjJzKPIu6XtcaKXlEqV3qDt0Rl6uoa7jIPci1CTX5gyL+Wc81+d53Hao4FYNADiM5MPO6xhL4jjUcJzC5DiH1p9Y3bsz2mXf9xlydqCE6dOIVm8FKvWhNWw53etUdfP9auNWoVKpUbJTSeiTQrggRzkQMHZ3cc4EJQskZLdLprSooDsxvDe4SNbZuljyafFyaGtIyud8jqsYpaz7oZiHxKSdLxh2wEjufnw7YEshUxs4Pttt6IxFquVHXFxli0sE72Q12TTihVKuEsGrqtHTypgXT1dXZ+dXiqvL80ixTE7gtszmozyZlQk5IXwcj4UWrem4uMgQICdfH+KePaBFbUVQr6UJdWXLUyuneJ5FIocLwmLi94/OHU/3HlOqdYowDCB/sQWhX0HnqQNRKV0iOLvY/XJEGK6H5Zdo9QPDg6/deSlOp7yqsT/XVtaZPCQ9iswa7RYRM3TJqXcO6tUFj3QZqplAqKYzIVRZ2Dttm+7FgIyO0gshInetGKiFBtMgv8dBfeU0mTABWrcu1c7aOgyvG9ZLa2un/5uI8fzQDIWrZqRrYjFYkKF6HmPy5X12yQCpBFWGpHSB4c/ZfFGIr7I3PEQMsprX0dPfMkvZlY9tcW/tEajFgeeM5qMyo4OpXJXm5JRoGgmh6tEEoG0rr+TpNcgJkFuPGcYHn5r+MiRd+q3pnyEnGwG1yasG/PCW9o36yiT99nOjj22JjlLeMIMEaNM9ytuUmqSI4NEJonGIyDDgdWCeJ6UFeHNo/hbcf9OxO4mg0ED+w7pbt0+nQGInveezi/VMm7nUG7q0IDbbEa+ohjDCsQmJkPhce+0fVQCtBFVcp3G7Y9kg3OuSGQiETn1tddbqZhnPuWjtfxStPbVEQ0G0Rk5rZpDAIEAnoIjIkRYTAQyGTk5Zo+AJ/Tqd6KySonDjrP+1kvv7AEUlxUMkk/iEh+bm40YUyddnBJfxxTsmrBoxBL5nwQoNW9ejSch2o6kBy5QEc7tIOR3UL8H3aREhZBaJRWFwhOhcNOo7zJ0De7reXLf4JM9up43SrWaQxBy7vTAxqHD6WqaL1by+Qq2qjlQCpWDPq/fb+uTvM7NRAg0FxPZSMLiK0wmPv46/2v+5IlYMpoRikWP42+tFrNU9nUZAagaOSRVByClAAlCBwQpk7kClojaKeeFTCHuTkMP9ud7L+15QepHkDanvAG2sQ0ez8aJmpKfyoDEe6t4MimjE8lMQRGLOVlCSVSgv7X5UTih24gVQf0Xmo7gKeG8t4Ccp7RHC1XRQiFdAU+Zw/7Udem6ep58FtIMuo9KtbCwWlpYSf8ydHg2mjMHhHImUxQqUejPLuNmHJ8GoI8wL5S+JXoExsgx5gqGJyIgPyOFmZnSRIz/diawXIFIbfw3o26fUafrR/iOEJ5DhGTq6KD6JVKiR56Yqr6oFJnljK6j3zbg3Etn/b09e9784D+htfpLLjbfAKmsD1viTqURM+IkGcBI6BL9SXESdMH+IezMYxotgzDufcT7QA3euioa2yBqaxuUNHJUKRSlVkFJzXIFqhisNgZcRI2xC12j/AFGJYANUSldBFeLUtYukoLSrQYEVFyJRKNovM94/+b9QGrR+LX7tV+zm2yezLwz88zFXl2OTL4LPc9vpDl5UbqoKkEptduCDSLJIfYI44R3f4iMAqNmMBO32lVUZLPZ7XZzvt1mySfu/Nw7H52dfGPnkw++vBeFb+mrnY8kJrp3xVve3TuZ6B9pax7r6MEL/eclRNPumd7mp0ZDgadC81O7Vubi73f92PICB8VEaGd05nnm2NtstjJ3jhgl0ffM3FyakK6TeJNcu3QlSA/Xsdr412zxjLc1YIraq7feXXj9OmCpVQ3J8piM8QHJ/KgoeNLqfe1RdD2pskFEkbs6QPHlOUsVoSyxPPfz0y844XgizpN5bzkOlSeSh2A+VxbuEitpE/yNBeBZlF+EYefl9KDvthLbto8m4337Pn713gdbulF3rPxb8YmJxbb+5ol39k0mglQm9Xs7e27GzKtMh8bcQdR3RqbGdjWHpihcisSWw0tzfXPkm6ND08H5Ce8vgGl9zm3FWXJDgBpdlIhIuQ0viY4kXYyB1wYBCtONaS8oebShqZ2C/sYdO3zJAvnPiOg/ZrAll9Ztnvi57oVyaYBK9k0AxPiAKE9ouXAifF7ATcZWnn9K+imHi8afyvQ/CYxo5FJDQ9Ribf7LhB7FxdvKyjg/nU6zGZfJk2+1lT29r3si/mrty++9svdVckjUMrTM1U73db8a7+vzxoIjzWNtJNyGB5lel3T1dPYHwnsC/V3z2/csLITnRxLLc4+N1+5cHJruDHj/cKsR4WLf3YBa7HIZszJycZcu1iwSXJ2UKclwaiJ5itMzC5DOpqb2ux665tbCxhpf6jQruW+O2VO3nyUfmxtf/wZz/bZunbA+4IeCU6kInpIvVmz9BcB5KA4otB2jrU498WSRThlJKzNBoezUGr5sEQEhQ2zbTGa72ew05OebbR5i+ep3ZgH0tcde3/v4Y+SPdpLyqB1aDk9P9NH9PjLW3Obt6urqGR7uGSS/KRu2eUPJt3VEIT1r+wLQyMuJ+OpSy/gLc0PzbYmnnvrZY4Uh4E+O25hjNBIfAShth0dCKHP2kNqUYFP5ntgmWXaTXdSAst9VvXXr3TuubBzwp6Ta/2Mi2DpyScRz8rBPDeuUvQiaviuuTtLuqDpoHiByyg1sySEho5yfMtKfxuwjTqWijm2m4n6i+OQ9ENCzhE0uwMrmlBXn52OLTCaTmbfZYyuzFtz01eziLEm0d9585e2dc+NzLd07hyKx0aXxudqI9ySKbqRgoQ1I8e4Ht7c+eyGLezr6Ax2je955fy4MLb+wEIOPHp/uW40HFxP9nzZhgXJKANRW5spBOiFBcTyhQ8ASPKGSqbCRoB1TRAmlTldcsO3RR59vb38IZS8s9Pn8qeM+N6P7r6W1SSmQZIumVF51F26kOoEOBRfvPR0NV0VMgiSX5DiZI7KFMcpHySbok4+QChuOUNSdshCoZBnbLyugC3Q6G9yn2cRYSkOd0+L0OJ2eElvZbbQFdr/6avDzx16b7Bsn5dsy1xffOU0Bd/NEIBKJeNsCwanA7uaOjk52JfSwZNs7MjIa7IiFI+FwOLq0sDw6B/h9fYvT84noz78XG3VlxVh3q1VndVulPiQXvu4sVcggw0SOlS5jIk0KP4X2Yhs9ftKj7Q+1o+yFvjsHRD6TsPrXoWCpwX0yH/LPfdtakm5jC4q8VcqYc1MTSkBUjpJWBXrC8QedIJ2HqHs6taBbTjwcp57Od9Z+o/RE8RSAUxmSbczG/pSwE86CfCoRdVos+aLxRbe/FV/8FD5kb+3kZEvtXB9k/RctfYn5kcGx/v5wZJoEXajtqebmp0bgOWe82KGuEQj56GQgvLqwinxOz7WM01Tz1lBs8bPfXUXuYqvOVvYc8mklVUXqyAWgGRlIpqzZxhBJvp3s8LFHZlx8FkaypAHprK6+++5bOTsdpaX+FF89FdHNi2AFtORyG4x8Mnun8Z7Qn2pEEJdYck7K/ZQ5X6Ps+E2JKNpO3eLxwCkCmqY8+WNlMMMxUKD0zcgc76vEwXMVKNfTbLdbLGaLxeI054tRKslpuH7+/XgkGo9370NQ47XjwCMtBd0TswvTy0vhL0dHp0Kh/v6ODoAN8LUtODq6vBAORGjwAM6lhdUXxiFPE4nI59uMOpcVx8EqByiMnRFrpDNSZaPMu6LlpdlIWLqDz8Ejzc7EtDc9//Td7Vur724s9DkqSx2lCoOU/eOpoKZ+l3+wabCv2tWDGALjGpKaKcLsqCMUJQdFwRQc+Ybu09Z10fHHH5V+Cr1IkPQyi54x3pLbxBoJtXwMaJ6VSTRXYCsQT/5ps6nOwKhPswHNt+fbymw62w/xycT8ysrEztqdu1YmF1vGx1vmVpfmVt9+d/axlmh4z08hb2hPfzPlyLs7pn76iQ4azk3o+FBwKhQOxOfGSUD1xSKxz4qMmVYwLCmyeYg2c6xul046jKHrpD0ug6Zy2RUni/eZtaSSWoRF2KLqrdcgnQM1lQM3lpZWbl7dIc+pyp/MKifbdSWbyZQdKILqemAv6KLkovC8ucSypxNvKoFVUxax8PTGpjP8gj0zBO8s4Tv8WD6EByXavErYZKx7kW2b3d6AXBoqDJbyPJMoPhJrKyuxVe+Nx/Hk3xrqfr8jGoxMwxO9MD7+7ttLE+PfBH8KTU2Fmkf6W9sCU4Ho0urq6vgymr4cDsc6A4Gp3X0f7xwfGoI1/c0ll7EYasnlckMnWykHO49uWCJNrPt1SttpzFaDP48EzkwJ2u1NTXe13333lTvuwLT7/Y4a3wYPslkMN33yLXnNXGpjzJrPqQ1bEtuupT2UgVfcp3ifqLuWiecR9/OCtHTSHqekUUy/5eyTzz77cLLvmHm8UArqpWEz8wpdAQJqLSnJt5jqzHrEkyHJlnq7DZNkdpfYig17599fjM52d1OU0P1mbE+wr3YVK/PuUmJ0AdVejgRjMe+ehenw6vh499K+hYWh1USIKtrQwnRitTuKYZqY/MNqdOl4FRmhkkETZ4niEF2uLjfr4uvOysBTYoSiOjuBU/pNsqAQCTOfv6u6euuthZWVfkfpnY47HTcqRP5dGDfX3iQ9JBMia9quPSmJ3KgEBTo12kaZImDmUjQowRJ0CK/0dBm9lobGc4KmHSETVNUEK9xPch3si9NlFxvZYOYssTNQsQ5jVK6vq3OaxMjnexDQYmvDh5OLkeg7e8cmoonuoXAkFIssLCxPxIbfXF1aSETC/aGngsurc3PvfvLF8EhoNDYVDZNvnwqB9tB8pD8aff87ow4N15GDw5835iChuUYtr5mVcYU0HmkdRgfDgkg9HVX+V0F4CgXSvvXWWwtrBgYcjjv9N/orfajsphWlm5eUp7Qg8JGcAtEYEqXmiorTsm/aKSr13aAncimJYiAFXI1fBlGc0fQTOEKPkySncpnS1Fbo4ximqMavZEPekEoodnucyKMddS+3mMv1FSaLSCdnQJkHO1/imw1OTnR3T650TXQngnTBNHd45/fNJ2qnpwPNdB1MUyO/tDy9FA2MekdjVCSPhrmWl5cjbZGV2Ee/o+g6tw5QCdpzdC5Mey6m/TyUPSNLzJGEmQgoM9MPl1E2wJldYC959Ommh6qrGxt9d/gdN97pqKnx+2uUUKXu49nAc8MjSt2grwaiJw2k1T5Bc73yZn9YOsVy8v34tUGAJDaFZOIElfIwpFPceTym9DRcUCL4k2U7Cq1xJ597xNGnUleXlXVeQSb6XpbPWeWxmwwGQ56+rlz/jDnfYzHlOzlCPc4GW1HRLV9MsjBiKA6o0dgKxBIbuVpbd/XO9A6ueHsGxyKTK14Kv0I/TQXGdk/BKo1ORbBMkdBk5P0/jKi3241JRzqLCY2A1SXqTiEYM+wyKPLOUEUMOJ/oOzRIViba3oC2X7O1sbGx0H+nQ103VjoqNb3knrJRO/m+kcLUTFCyI7+xtVDA45e1Pg7+moioMkPyjNGX8EicT1BV8PJx0AkHnXARhYvpx5H1YKAIrB0e08mn0hRHtvNoNm2yw7SYcLPAbs83c3KWG+oM5XpnHSeomHmE1urBjfLY7HfsXZxfjMaj0dBi6/ZnWwktWwcHbx7saR3bM7yd6+abe8jTTeE2TXGNjo40h0MJb3Dxt9+zwK+YdKbbKikjV7HOSnEInYZou4y7YCy1JDaRS6h5mWlDMpMEjHieyo/34yjdfmOpo/LOyso77ljbSJRyQqbMr0zR9wOSR1ZqqILhmjlaHwkKYDxo9l1AFMnkE9eT5xMEceU/XXAROQ+Gs5yOvp9y1JYt5IwZxEIAz0hF2WGqu6qoOLOsYBuUfD4omhm5VGHR11WYCeTNHqcZNtTphMKz5TTcONvZHAwG+6f6Z1rf3HWzIufolhns3LV9sC0W7L352u0dVIDtCYy0BXChIuGYd+zzX41unc4lvIfLWmQl3HRzo0+bRHEWXYZMY5ECWimWlZmAh0j9kqTfFEV3lwqLBvDjkU2/f6Cy5vqav6mLDcu9wdP9RzdCMtja2ZlUbqOdoprQXyAfa/lhZHLN/RR+XgkqNgljdLzKelC5mH66pI3TGHGBUaLUhtLFrGxKwYhCiso89m1OOwJqsOSZysv15goLcZLH7DEjqOYG9H6bW9f09e7OrrbOwGgEEm7vmxBLcgErDQnze4Khyeh2Qs4uUA8Eg+FEaPen3z1XTFVNQS6Ruk7idZ1N0Uo5nKG4nlL0SRsCDJ3KdJx5CP0b4AmhIHHRo08/dM3WHTuudxQOOHz+0lKHz+EfKBQU1pBIxinVyqdW06fOsVoj5zE1626T4KiaivGZJNyURIfEnGi7EtEDcaKoEQFRtYZTssZHwYKeTebjOPE/6eWEXUIXM4t0BXAhZQ0e1r6a9HlXX32J3mAoNxMoOU0VEn1CMT9HVomYtP2jzuBYK0WyY0NxeM3ZfV8NqhQx2j4cGhpfqn11sLW1IxILBr3B/s+/c1s9ZR5oTpQ8B2OENXJhkPBAL3blgidYyjirDGnfkl7Cc0kT4xJnqcOz4fmHqrc23u0f8Pl8lTc6/D6///rGwgGVKF+7BK1N8xWT03HJc5c2OP31BRNKzRWcau/JBfwk9l5gBUhYEK2DE7FUD8gnsnooMRJonnAc46mZp5wGx3Rs2paTT6T+mzZu9j5z9AtjW+bGRaow5Vn0+qvz8vTPVDDL01xnQErrzFx2j+c5mxDNdf5ZKM/Wnmhz87752ErH2FddY21tMz3ewZtnl9qiYe/NPdtXvL27933QVFZl8xAKAWJBrisnF//dqrsYXynHyPfcXBejLihIZmglVcjMC5ARa7BK4ElhTUPT0w+1P0SQ2QiOpX5fKe7SHQ7fwIAyIyhpck/R5jxcymrydTD/0SgDhMl5OmXbxYsXQQVWQVGIJiJN1ZKgSFBOUNlgjM6n0cvJEXr22VtQeoySpI9Ou+IqiZIzJfVenG8xO3E98y657Opn9Ppyg7nOZJBgvspcbjBxiArTzP4JuNF7Pnizo6d3++DY2K5PZ7yBSHPrytjNgx1dM8ODXcOxYWrq3v/gO4/VY6q3PAdrBXOM927MyXIZ6T9A13ORUvo2uQAT1vg6pgaQNWIEIBl44sxiypQebXq6naB9YAA4UXbfnf5KtP76wkKNUlvHJaU2MXXrTAojmjyW9u/eLT7UI4KoVYeoJDEvxFGR86p6nicgFddJmuFlNvXpW2ReA4imcTtc8h1sTcBtztIhDgTVtked5vKKyyouu/wSfbnFYKiot3CKGp6pqzPUofJ2Q5XFXFVlMdWb77cYbvj2ozcHe3s7Orf3dlD1Ndw/81XrLsx+58zunn3f7zBUOT02T5WlinMX4hiTjjjSrnmxTgVGRvritNmfCGdGhoxaYQi5qPthGVnZmRyeilXagXqj7Q5CI+6lNb4aX2MhOogQraO5eWSVujY3G2pqnrxIe60aeX1ygHLl1Z0iBmV9FKA88zM4ir+k7pj39OOk6Ob0tNPB8+y0k48jVCLDKRugUffsq4qKXCXYG8JNU17eJZfecln5ZZfrLQb9M89U6A31z9TVP4CsovUWJNRT9XC94RHTI/z+4rdff7qro7cXInmmc3jG27W789Ovvx94qf6BBx653wxDZXZWCS2nc0EaZ0LLkSkiXeRCNGUww3nMCToHhVfTQc6QOBMjn5EFaS8JjrseggXZUdgohgiFd/gLa3wDvuux71o2d2MJ5//tMNbQBE7tiQ8t4EwqpxVMNS2XqroLeJJaG4ER+FTwzsVX3lKmzLBF1bot1fTcTuGFw0Rn3GFZZ2VdR5hcQGGdZDabLOVXsy/qsvsu1+fpH7jaYrnaBDdi4KozmEHbUlVfZam3PGIwPZJ3/xNP3P/II4/s+Pb7r3/7+vvvv//hW8efDzzyBNcDD9z/gEFfz+FpBc4iCHllfFBw8kTwSZkXG3lh2q8DTyFCZA3SuX8Rdi69bZRRGAaJSykEREMFVA1QhEtkyXamHk+IXRdf6jSRcyM3N5E7tVsTNS4B3DaSZ1FFY1EciWSSTRfjBQs2kb1i5f+R38Af4TnfF2PjcJnYYztGRXl1znfOec9N7426in28swgjLxQd0rmyv4JgbgEmwVE+n0LfNZnWR/P8dY5I/kvPBw9QXY8ssOqdZrrvHVz5hZJJaM9Lyl0SR1Q3x4KwbC3/Mnj9isrBy8habiNvqbE3qPvNGxOTS6H5r+88fb44dQ93Ppo1DcNA5Q22n7Cgp5gLh0G0gZVH3a10pRJvbHqNRuvEdVu+x9M59U9bftfpdlstv+Vz42uAj880ZwiK5hc4NqWjkLF/NyQiwvXkEzk4MUXfEGe+IwNX3qLDkLnuVyGgpo+nnj17sbubF0oJ1xNjlN/vIJq1TmK/9Ao+ocDZs9b9PMYwnoNpJO59PAdoJh1oASJ6rePPszJlTX2i4vhLnDCaweOuJBSNR0Jx6UcIkeDpieUlHycLykkUY97RsMXpdDF8L7xtRLJ7MdMobsayWetkMxvejLEOFkSteLVpVUCVM6Badd1oy4+1uv7pT6c/Ok731PFPQdTs8qsG38cblWYTAmQmRHY9RDU300noJGaizcQ4oOLKQyMTa0o9spr7Sbr9KtuZboWWiDNxlUqzBEadgx1iTCGX9g+3Olv5Ur4k+t536M9vLj3PkAxYLh2l6jOzj6xKcuhDUpl1QMXSY4cEV2GVlbmXT3wGUOXRj4pPT2aOG+Qd9v1t0nEUhlAHNnEHz3J9Gq5us7iXxR5FYrw0sidZK2wVLSB1G0imVU3HLY+3jU3LzWWNlutnu/7Rj93uqe8fHZ0ion7XR0C9KvLZrFADIoznDBUgt6WHOCAnJt3uN76RuIj6hc9g6QROmftJ/wGthpMTlILAeWLad7fyeUw7Eor7uU8mbjnV6XRKNc42KTDqbywftuvDPv5APYiiRPrzBEBVP3jfn34BeIqt09Xz6Pwl5FOlPAFWnHxo+tGgzKsd4+CErB8Zw9CTfaeiFi4Z55lFCGzT/T0NeMWcaaLu9bqRs06KOQDN5jI5N7cZthquW6l6aeBseOGWG3ZbXsv3u6enR11w5HHky0fwbFUrlUZF8Hw8gemZnPxqPHAxELiF/34jALDiK1Hvyep3aCVCdkavUP/Fl3hu01KUSJ1Sbasmur6zsg+e+3jy+Eqk3/OH/L0S+GmCfbj080yl/9nscx/KfICbRlKeiJ9u8HpVSm34PygTJG6Tkl1kWDxQvmM3itD0Y2yHHguOjJCMJ/EhMwZknjKVtPMLSxvkNtYtzPmmsVffOzFjxagRy0XQelQf9c95Oauac6tWGl1uVD2v6jY8r+W2us7R6WlXQXqkhNPx3RbfVStPN5pSk4hi0wzz1YVx4vTAZ+Mytwrh5Ek7MaySGgEkQ70/+Yzj9QbiuXj82+8vMO0dskUrQoUgpNCeP+x38rXD2q46P1FIjed/2fXhzR36GtD/XuT6mtx7031BVRd7YujFaQJaDD0f1ZxAwRzv/vr168E3g8FRuDvgREwvX/uCZhkAvUkfQmhpZuP5cfzeenrzXp0Bv5m9YixqRvdi0VjRsPf8aDG853KAApPlVt2mF56yfNdzcy0u/6h7CqL61sUe8U2u0ahWZh7PtYkxsUIElOzikuUnIKoGqI5fkHJk0HwH+ZTSRMasBS5OQCGLtv/yAtN+uI+TVFg5KBRWcUEhRZDOWj6VSIEnlwgo1z+T8kOq33MF9G1wFpN26HvqriDV1ghh1LlOqbEBYfHQ9EwRiePReS5O0C+ucI3AhOLQSwB/dQJAKV6iwnuaCGg7a+IqnRh2JGNEYwavZi7qZzJ+zmjkNl3Res+qWI2q5YJtA+i6thh2jFGXG6Ye8+7ylAO0Cesxg2ITo0s6OMDpqLoK5SOj6miPIc6U+i/EU6Yy0A8zqQ7PFy/Q9sMOeg6nhMp3Hu4fbG3VlpdT+cNaSfRdXEMF6BDtyWPoQB08QQfMj0Z2EEqkkX8SOCGNueuKbz4JmoCqmDupudPl9MjnKLTIKBtSeMqWaOYAkt5UTShLlClS+Uld3b1cNBepR9bKZt0QPa9HyqZhmrYBtuEcIupaAIW6i8PearitbAvD3v0R4eTi6FQS2nJdr9Juth8/vj1zf5K1S6qxiCcojpN5+4pJSyCMkH7OjkNJw32K08QoJuCcJmzn8JzN11ZA8+Xqilij/Q4WqUOsVOvkD0vK/1QVmYLoUKXi39s5Bg/PM/TR2jP6c6DMptdmKG/hjDWHrFrkAFPkFaOuDL+WVSw+RkohibJzXZYtku/hnzB3aSK0EFpaWno6tf4b/Ecuulc06hEjsmbUTTtsRkwwrZtuzAyfxDBBXIAZxiZZVbFHpuMAJA9ReAcJRTZ5uBik9uP7k0yrAjqUmsBHIEXFr4rTiTMvCXdZ3iG7emjE/oaWCEprCDOpVCpt5fdJDRdW0HVCTXxPfKVa6nCrlEjkRT5V7nGolnOoAHSYeeLqv+MaSBsDmEYULokXnCbkUKAkhhd2SepANRvC96LvahF8UFhQ8FSoSmUDIwaoXrtJ9uj5HCTHtBV/RrJjM2JHDCNadswMKr/nm1G7jPKb/NbPWVj5bddrWnHPO7E8JNHxHbHszhG26FQeKLzA2YaKl8ASP0ksOP6lat6Ah+cVAQXVi+ItyaxkyCUG/tKh9/z59vZvL14c5jvLDwF09WDn4KDWWT3YyiOZyVpq97D2IPHKGZwqWfGvVYrD2XfeDIKv5bOfLdZGSdsc/jN0XzUc6YhIPHk5TQVSpe9U1FJjN4aACpxjEHfMWaOwgc64iVDgzp07pN3Sc+HtE4i6vUjdyGTsPcMxo5GMnYmYdrkcrbeyPtIZ9jyXaBLprHiuhyjya3HjlTny5cJCiQg3AfRr0JS2DfJCTEFmD5xIKG0bwoBg26lX+hiokV3eMs6AIgCy7b+THSZq5/BcwbzDyROyd9D1/HKKoHM5mdL+vJ49NSig/5brHK5T5Dq/YUIJpZh1+dF2XhWKYMxBVJEgauQFv7wEvyTDqfFAg8Hg2GgQfcehZ0nPu2wcuBUIyR+yQUyybsXvnTD20zHqv2ZYGOfY0Zidseu2vcYBapsuyhye8oiUvIoESOEqpgfZFIPkaIMkoup6VQ9vvt2+DaOEOb86fkG4Dpk7SRehjPtD05FOoOUDWQ4Z9HcrMIlpJz2MdO6SMOLYLHQKqwXsewffM9/B+9zKL9cSqWRe2XcRJR4Kr6Es+9/f97zU4d44XvSJqh0mHjijvPJOLNMl1cyBMKqaRRLFEstjka6TgocPFQZUJqiPfBnELo199MVHEHZvv/46pZ+TIcJNmPf0ei6di26WjXomEomU1zKmWY7EMkjozxnHNuxYK+yKTaqKdccJFdV3fRtzxOU42CJxnsAcW1WttoncCdQxPfQIM1tWzUT+GP3GHmGB5PY5aFLcLVMssPyqavYYDvnB7vLWwerqinKWDkhwdMRTOkwlaom8PGaRGo1nTzqH3KYhbqkPbb+dq88hD+4q5o1CFNOjtV/Ppj3zkFT1J4/rou3MCsO6j+B/kukMEiVBitAgd/Ed1bIbWphbT0sZQ840OC7XTJbyxDJouWnYmbKZsW3Dt81G2BLZqwiYFTHzFkbedW3fB0psUddH0xW51Gy227QO3ycWEtzwiISKo06B6d1gB2UMohfU+BoZtUTLAeK5MHf8Pd1FUMhbP+zvFHZeou+rOwerwFnLl5Zry4lkPjGbSKVeEbMrP/05Iuc3SQ38qvfzl1gikn2SToD864O+q39ZR0WybEKf1vihYtVHxUmSR1C6OJHSICOZRhmjfk2G+L9788YCqRrqMRen1p9tWpu5mBE1jKNMxllzMna0bkeQTDuT6daBLbxZtaBCm24jXhGlB0+ulgmYKoJ3fVcsO4dntSkEPFwSVbIilywLkY0cXLJsCyHF/RTh/BzHk1MAzjM0J9n2Yzz52dQPTwByh+oaFB536SDfSS3XkvkfUqVUajaVlL7UPhWsERtObQ5vJ+/T87oDob/4uX/7q8FLTVqjTxsfXhslMfSkOKlkUNVg0hSr+g6vyJPjk24P/M93hU4OTVBPPU8fwuJ2fDucMwG0vFYvl2X9aMQpG3b05zrI+vWsGcZFwpVvEpl7FWQ0zb1ZwZj7LW2HnBYWCgnGFhFpkmAXX4n1r2xwV9tK6W1l+ZacmzLFV5a+qqPgwjufBW7dmlM18i92k0RAB4XVlwBaKCguRNQd3nP5u1TqUTL1aFb+QjG3/aBnuGGGqz+efnj6Vb9rc2BRQm+3br/YoWeZMOpXxJ+/Tq0NIwGVngcvXWYa/fujYyOXwTM4FqSW/gNZ/S/tEzSjTCxtHE+vb1v33GisjLtpiL47HKN2zI7atmlE9uBIcuG4V50i4ZFuVuLxSvNpvFrhrBQGFK6u1RXm0xOGGXWnsej+bUgk6RYWydRbIGU9HABe/EYWciirT5U82fbADRLW2KJfVIbjyepKAUBX8T2fwMoDZym/m0zm7yZKW6lkoqT8pTNV5S5w/g+ZzO3vZGgPUgDrh/D9cUyKntcMCU9MOrOsuISlu36ZIBO5vDw2NjrCzhnIJTVXmYowqV+CXpq/E5qfp3R+Ci65aBZNjtBMppwxnfJauWxGjYhpmjj0ru95ZOWwNBUyHkST/BCkY534CixBVSIjAG5W2xsEmre1K88mDr0t5oM32O8s++FeF0vEU0pnIemYnRrgzFk8ntoWOJcfPikUXhZ2dh6SNaJAUTilUp64/e7dZP7R3eQj8PxS+Ut9duN83/tgZrNf5D0gk1oq9b3XgajZJQ2x6L7YeR3B0wX7pdgezJCOi2CU+JEHb6+A5zU1eImxIbdvLoSomv0eb357O7y5ZxiYoTUbRG0Hx5Pjsxu1jZzvuulKIz1VIfDZaMolUFbkuHRd4eh90XZXPoM19Yio+/jkOKDJDD+ayNitSU4AUD8Vo44jCq0E1uLfUwKwFI8f/wKrdJhKkd7AGhXQeZwm8ZX24UFS+UTi7uzs7KMHd2c1n9yzyBqqf6mjH+6KPxPogRUoaHhv+Z6WVzlItNYr6hhA1XRvMUTU1fGCy3kJB/4aueKRMSqYyMpxo/6TZYYU15G/XViglDa9mD6xitmMITszI+j6mg2kEXstYtfLZtaOedbTdKW9CJDN9tN2szqDmGLtKyi4IkE8nkCLyDbbTeBU0vnehU8vsAmOHghZ8U4DKXBKqCSCSVkitgqC5GZofhpXiUq6B8tbK092dgovdwqrCOcq/PwWrDIkCLx8KZFKJB/c3QVPRUxqOep7m8P6PjyxcqBEp4emeq/fimb35gLKpQkXjDrZOTJw2CFElGsM/GTNzGVxkUhxjoi0fnEN95Na78BXtxZCC4LnHOJ5ryjBJpqOg2T/nEHdHcewnYhj+Fk3vD4F+TlXnYHnmKu055qgid4joii88jwx757XcKtV7JHKrsvSrddx12UhMXvCeCGFKYvSKV1AOPE7VYtRAPF8vn3827fPHhymqFjY2Vkp8CPmXWg6pPOwVNpNPppFRJPJ0qzilwRNrfLndp6cd5wGCZKeMv9tnoB+6ht3LaJcRO6KWyIaAlBKvAmKhEQeJWM0MoZ4fjj2EQovaXhaud5g7ufErfmN5xtLzzFHualYOBNBNtlaWo44sEzdqGMSHkXqJ0Xyv9QmUM7Q3Hg80xaLA7D4RZDLpOZAVHylhgeYDbcy075/m9xwgMiIkxOxlM3ZsmiegY6YeTXL9xOmrciafzk8lxaFBpmluOah+Jx/FF4+KaxwftaI3onca8naMiI6m6iVSlBPZ9ORBujhHprnZfT8Qn3eDfbB64aZwbEs+iTRlkhPusCwQ31IF4JUKXJmcvuTr7OJaayMwrA/Aa0yI2iNHdERrCgxM621LQxc78yFAtKUHy0DFjNgaaGW8Tp1WknAZDQaxUkM6saNCxfGSHBlYqJuXLgwcWs0JurCpTGZuHXr83635XrL6NfSlhn8mXfO+c7fe85R6agHnacrQbiGQ7dzf6rTcIBkCLm6kRF5SxfiF0h8LI4rKnpvfG1nfBz/MxOn2PnuxbGRxtDUwfryC2+/8MLB9PIBrwcAS2Hjda5Npel4w5v3rk/Jp0JKNWuYNflA2R/1thMzxoLZIN44ukGYniZLR3NmqVj7rLzwAVk61H1hYSFHbHQ1T0SUz20Vi3a2uLFXtG3w5KCL3glyl/y3YND0r7CzfVaDMG0CqtNcytWKvyCK4CQpDMKmY9RBD47N/VgjqXuEc/f9Ss7f06sNfGcM1WYW5icEJegLGUrvq7urcfz5NVx6rJKyTMnk5OS7m2PY9QYcpmmxZWRwXjgA0TcQURU+TI1DzudFqprKLOHLExydIHTvRNkjrP0zsD4QBk+1Dz/Ib51U3v70sKh0JifvOLkXPyAwEpxovHIhFOH2tnKOY2f3ssVSNVtUPkTDupockev2wh4dzCDAW9dtcO+mD6yqR/pqWSZ9UJz5sIYyPIqjKbmE3x1hgLKGgEZCET7yhT3qZoY6O6Ye86LNK/NTI5vPrkBZ2k5JONd2U0SbizsK3FPEmpPp/YujIw26kZ6CXww9DurMCwxUOTgnO88NKn3HzoMnibqL+J5YI7W34rKzzgI4WRQSYRWDfNDoCTRd41E7CTfPnDpFi9H85ghNB1u5Wv2ZDxYWXsS8L2Dc65+dz13NU/jIZfdyW5eLJdsuVSE5gKdH0AwofNuF2SazgQK8Twnx9kD7Q/2bY769aTeCFzzReQJMM+NCMonlCZONpzFOgEK0UfWIGSK9eJ9nbzt7BuoSTZtDo5wLk0nw5MYcl3x+tIpdSqTTlOXIgqyPIp9PEZZDNwROXhFQqbxs/EVl7A2aqDvmnbwn8qmEMcCx8K+fORs6WpkfksqbdZDIKWmmsxQ05zc1eMHK19S+wUE2xe9G2zFHe47M+2Wbs2fZPKTvwsBQ34/OZjhq7Vt4/htYPwnqAdnynTzR5K/sRhU8JKCgyQMfSWwQQATUcK8uT9ifCEn/3dBq+4netXCG9NKchjIsUYkby0wOJVJr49LzHV52UXluz3icLMgY9K6l+Zm3n5qBcYjwwZ1BRKfPHbyAgF6UjCpN8iEKv85ngiOuTxHizRLyMGAin2hGWFESB6eeRxh1V3GV2xs4kb7aeWwR6k50RB5koYwzT9nochH5vEy7YbWazdpZ7LvHwD5M0AegO8r39idXH67l8tN2ft0dgeRTYBy9SnJ3MrMOBx6fXZwvCu3hHp2HesEzfCwaAdKIlkyB5+2Yd6pHczOz3J5TQzF43oupcXJL6VWZJMloPLGaSMaoFG/C/NZ4CtMNLFOj3Pu5CTGNp9fX1xFMzBKukm5PWDZy5geV3dR6Z12eKAaghoXmA0im1poo1Ow79dzsVGOUClypZBXrdfz4BfB8ERGlBneVBHIxj7e0ZWVLOEp2sVqqOp49Uj2CQkXbKDtf+32hPEq4acKt479L4APt2/po6CEmZMd377lL82wYAwhZPtKNtyJJ7acLni0TEXnzatbHlz69PHNlaYVOLph1SWIiwNx9E+8TZOPxRCy+CWNxFOGcmn5u+tQ5w9kEUSRwQl0v8p3k4wOp4BSgB4rdn6bHgNaCMBspuT4ho0XCxr73RMNg2WlaNLtIHjAG6JXNl98pFax8GclEPM/XeV+oI5+18md5FYmtva2t7EYVQEt2wRE/U7UHz6gEFZ3TXvIIluBb3xwG8J7xaTEa/OnU3kczwkpFIsP0xMsEwd7eHjNo/p5eIcpAZVXjGP1J4zsFb7g2s4Qn2JuVydhqfBcXaTFOtJl4aXFnMb2WGHp2aIzbtdGYn1EbFjgOgBUFNak8xG3Q5BKVmpvUKO8HMliiJeGygyDzm41fIQGVlY+aJe9K4jG9f3ga33PzY5rbHbdcV2Bk7FF9gc5sxPMytuiyjS2i4XDLtrBGmPkb1JrOOVLsuF45viWfPn9JH7gpD3PKfhK/xQTVO8fkCDRD9V5OJCRpBNKIVsT10HIUVlscAEdQP9Dt0BwrplgNP/bU7MjSCGTEofhkhgIHPhKh5i5Zz0RyJ5ncN9zkxtSlZbVhEfNwqEY+DZ56TEwjj7j2wPmGpFPRJneB1F1w6vJE2yP39/Mis4SacOSFgmffxPDslIHzLdut5UxF80XCImw7WZEahLp8xSpeLlpZ2y6UaE2oVi27gLFo/aGF0HX7Edp7YpuwNT+3jp8DbUHoR5vG8Iu2ZOIiTJFy8NoeB7WGlvf+MOshQ7L3EW306GR+HZ3asOtErZumuglV8bUUxWFuzjcBdSe+u5jYjWcmR/fHaIWHBT/zJCw55PIkUQ91C2CdgArPhBovXgJMwYm646AixhLPBzxbJHWXnyZA8ZhwSMMncJuI3M/OzU9tCs+qVTlflmRy1GlUW1iolWsujCXc+UKxWLVKRZtsiL1VsHV/CoTAnIUgOfk/plgezqbl2XKR/NAIG8fxYlH5Y/ywqbg/TNAup90Q6aK9QrHn9nDULIoDWpn6frIhVN8f19y6pzCwo5c2G7HJ/QvxxdQO6Y/k7vjqWvq9NeghMbiJo6MHB1Pnpk89NzBBGe0BzeLl0DhkDNP0wbmDA2AkzSxzhLqTmtcKcgYoSd35G+QpL433cMsmnYiy4PfUMD15UBPfylYtp4Yp+gDTXi8v1OuVuo7r7DlY9+xewS5kraoQxSaBZ3MSt6AwStym5QEbrxc/Zdc+iSm4+d1Ht6X09BuRBiEBgrt097HOHsSzl7F1FDO7oz0hxDUCyuxIuZ36Lc2Gw0/Nnb409f7Y6CubIyvQ6qgVLS6+hxkaT6Z2x9Pp1P6F9aWRS8uNmXPTs6dgIai6qwSRAVTdbUgoEej6NHAaCeU2PccVS40IaxTtRzuEp+CUgGIOsUkIaYi/zgH68oh04YNsVBwXZ+mDsjxP8p/12jOVSr1CvFSsZquXt7J21bayxO62bUk+PZ6RzvW3a7fPq/Y3IQWE+dB3krTqk979VkR9oPXgzlvuOsb8abjyNGx2koen0B7mq/O4vPswSBt7dMdtj/cNKju/PHulAUl2f38yHU+OJzDqqTXiIxQ/nl6J0X1A49HsuVnm9ingkRaDFKQO8BzEaVI3EckRsNTXAYOVnpPcKrvZiS0ybGjSrhwFuybPJBE9+eB9A0/MUSKGTFe1824dV/5FXZ8AWj5fo8/dKhBnWsVitgqaG9Uiqc+SVdX9iXH36+eB5brXH76mH9Obv7GjXUoFo79XzoAtgyezJy4yRjwCRf6ecJQ/kjYg3B66gwpHN/qG6ske9St6vw/b/sSTNMKSDqGRI5mMvUacubO9uJoaj49vU+5IxCh7Ti1NP/kY/ibMLhHdtajALBWHLccVquBTgRKWHdde2RJcgImup4knw1Fk06CpHAwnjP8Lnmoi7u4aoAr3FHSlz0vQPmqouSkZAecCOk+xvZYrFPOWU0XHs0V7zyZfoqctxsshBzbI7fZPWwdyC7zAlpm2qXU8PTBVTWklXEgp08zBRBvY3aEoeXH2HvWGmKLafZuGAYZge2Py+5lnxSyWx9Wa8sLM1KWR+bGV0aVnxy+s7S6SCBlfTK0tAmoqnaabizEiy3PsnYF2JJuNKRFbk8y6+NrPTSj61Lgv+aEyTHyvPQf48lyf2CIBaiA9tEncqmyC6ztz+jR57EtwZ7cct7aAo7TwwYuqdpTpe72ad3Ou61gW4pm1qXU4yn06Nh88fQ9GR0LvP3mgwV5D36L7pU3dx3oN/JbEUw3w5JKJKVFt3E8wvMMsKOYRUtYswr2KB9Xdy1Yu2k7p4oWWcWV5dGRoZZvrchs+CGXNNTmg2/FkZuTZN+iMwweAYszCEqZNYUwAhHwGVV+osfLqJxBPIk+OCLQThqJIL/sD4BnRuV/evFg+glNohhnAMPjgqVOnNTMRc1TElZfv+cEzZWgMsOUhg7hYd/Lxxb29UgnDTjiKqltOQfwlHX8JaRug7Qs423H1Pfvg0YXJS6tkbPYl8MRhkkFSFwe3ZEi7ejroPIge7yQvgXwiLQyyg50MN4QVZ2aIP+3YS5P0IZD7REAXE0luzsXdRIpQc5TTmKZD4+zA45rpcwKfksRGFEDJXmKQ1E1kciNYeYRTuSdSIVSDqW/wkwiluTz5MkUX1J1LJyp+Iu2cOJ+ERiWnWKsrqcT9yaOez1+lLTvvQPjEi7dtp4TOY9vdgsxR1VBdwdOHs23OzdE3790POFtkWr0cEhr8+qZUvmnixa1D4ZkHhiHXwPne4xoVQj0M95McE5U4HjhQd3Sd5QaDOo//OTUyNbQZiyW21xaVnV9dA9L4ajKeHFpaGhl7anqOKv2ZrrODgyQto8ABKOCJFyrq4cTgqQnZJJKiaD1T1GS2tP6tU75SE00efJLGg6j+6a7BM8OoxvzHr7yT3XJyXJq48XWSIGUi93LFQTorDmwlCIrWFupuVS1L3rz8eXxCf01Rm3xef2aDH9YHx9b5Pr2n6xy9IZbNeRhiIjPxk17NXvoKcZU0xobL/7hU/qGQdA840Xiuzz5usMehLl2Za4yNrcQSyUkYS6s7OzvbqLvINqk0vdvwtmfmnmQjbp/o2dEwR3cgEkr9RytIn1aChBzz8jRpe25TQveB206i7Uof6+LkyRGoSKtx6WE1sN0X9uzo5ucvl7JWrlIvKxGiOBNfnu9qzlULLp1TKBSyaLpjV61CtVBwbde2sO9e74DOdRJL7dsNA5+CKi4ZbDIVm59bfq2fEiUZos4Y5lNi0Andj9/2QLdZLRMN0blJ6ETKDv/zONkl0nXQhuaw7/MrS7H09ngyvpgcTy8mM2sqyaVj6WdjjfkpxvdpRNJ9XWzFE55gIgmNin4kPLHyyogaIVXmE9zZj60flDny8HzUc5jwP6NGPm+l72maxOfHqLtdrtcxRsgmhr1crxBlkqhzJKAFG2e+4BTtolutFlwXfOEvaQBNUN+PpJWPtio0AypOi8XQlgEVovo9D2LvqH4Ez0a9mgqOCJfZynPHcbZb3Y7q96hmzCGyv52tR2arjKgZM1N4S7FEJpmMJ7aT7+3E4+IoxuMZmmRg1c9MTzymdGbXAx1R5YgAxXg98pm04LFLncIAamyRut6U+Qx71g8tP5RPvgFR/Rui5EJgI8OheOWtkp27ig+PLXrxPMJZq9fyYJm3c07eLmDblaOzbMfNF9B2TsErkuMZtu8sv37AeWRvZNsWTn9/R2CUrXeRGr4NPAZdoVhTbX9lWl037wwHeqgHM6vASVYKMu3AfWp9V/2oMbUS20+tpbfj6bWdZApOCMT511LpyVHmLJL2PI18DlBCg4eklN8xaW0IjaebQJO9YC4ofzdsJgQMQpGXsyTnU3bdPwI2womGOwmfGDmKM/8yNTYHPAk0kVJOeaGWryGixWKhAJ4yRXyoOvresUpWPo+qN4fCBxjfbbr9v/MV+egDqKD9ZiOM/w45W/7nIzzuhF4TJgwKRTFF/dEQM5PxUbDvEpj+hyJ3893xWxk7z5oJkWmXNhtLsVgsuQ2O8VWIIfCXkjvjsUyGtu2lmeXTz50ZkIAqIFcobgDl4IVC2QbQpzH0NLo+B6yDauQg0lTFXeL56DEPSxRekqoKAfr+4G2DZ0/PX2E+CJm6msf2vEpUVK9VarWahbtkSb9LBY5jyzAVdCSejsmHyJNp5dn9aJITDDfbFyH5nFG/9B5oTJBMGjg9Sw+b4eG7HrmX4mYIpQyRqIuGOgGUAB48uTkj92Ph0XfyyVSLGWVlyh2XmGpBY0cag5TcITbaXUtwfyZjyf0h2tjnWEPYNdDHRuYedBU8kTqZpJOkMbHkXQjo0ziiyo8o2Yxjyo+SmFNiqR8sW9enDHy//re6NV/JDP9i5uxWuVKBOluun6dlCxkFUzxPVyVN2wZMEs0OSLpO3rXA1Spr6ra6V33+p49bkKncPoPNp4j47cY+41tK3ioktWa1oO6U46Aio9XyTdB4jFFnt1n8TO1d9U1m3IR6tVemTzvO5qi/z6+Mjq0MvZZKrq1ya3J9xsfTUG4Smdj+EtVfmrKGuwRSN1ei8JTahu/mEjWBEhw6tJ7UE02ug9gnvKU7TjyAMYIuxQ8/CpZQzQ99UH4DSljfmdnZedMaU8zlaXh9poaqk6LDGrnYI+cyGSe3arlV1xUtxHXwl/DmK7blgqfPT26nferbo76nL8Qt8xNINUkovZxI0zZ5vyZ3nv8YCTsNYwDNYz2E6uDYH+3tRkRlXiNK5BGIkk2+te/xARrSZqEzULAdy7yWScd3dlIofGINxacUF4Oi2JhfXuZapPh7kpoJaB7zYAFZAXpCRh5zDqI0ECOexrgrcu/RzWDAv0sCysPEnNL37g7mbDw5Cylk4623LJfMfG3hfKUMouVypZx35Lgjn9VCBcIncAIsv5IvY5PyFVv5Oh6H1Z5Anf1IQ0KQVCuB9C9Rn04rHH2yIu/qOuJapVUTQgPxEXGlxJMUHYrXec/tmHqCpTCAyqFHZFk7MPj447Nzp5evQAYbofiOisNmoDsGYwRJGThpPaBSh5M+jOjdd7IDDabhWz2gHLmhiuRFRqJtUGaIOYnq3fIyyUrOyRypC6Jlj5BX/p8YNE3laOrKpUsfk4Kz8kSbFIpr2KFyxc3na4hkkXhdcsm1WXWtmuPyq1ZeAX2+1hzr0zxHCQzt9EXhF+SNBMaA6stflSA7x+fmQxV4+g/ohovcH8bZlBhJkKC6dGKNQtolB9RgC92G+tEA3E8zvn9FPdnJDJDi0MfH4YGRXErHMvtLjQZcEObMUUvn33DsMN4BK+Oaa7ubqIcEo/wQqKrNoDsqw8XhJzlQ/JoOqEKkcEcHzbGPwe9mBkPVKdQcmfV8OYdw1txajaynaxcJ3C/bRdtySoV8AYml8uFQAAFTcbSkir6+HnHar9M245NFg4NAjV/q4+kvRGpu66K1w9BtsDtaHYcpxxz1coOq8tEjwuIxFTg7yCafuQ/zPg15fozJavsXkpl0ksYOqGCppPSeyvv+/tszM9SNMDZdUD1C3McA1LoK+z3GXHe3VB7B1PNppLXDK7o3E5+PmmOu0WbE2f0g7Ub0EcNQzBb3nHyNW5Prk3uUoL1SdvdcN1/kssTEF92C5ZkiGwuFmLq6P3V7BjJ2/0FQbmfU+/ViX0APXXjThHBo4VtNH+o0NEt6Imp7FaAwE8NRDbWRt8QRDkqJ0lVxlmnk7MCEbDPGjLUMUNIKlyY7vyZnPhGDLg/Vdln7hQfFlUPmPISEqOqVQhSdhy7XxRM8EU+6DORWST4N9i04jYjya+HODoJ36fsrNBvRr1Er1xdq0vdKvVbI10q0FAJdIW9lcUBt8ETrXacAqAW34ip+17mlRVfU4+hWKV/Vj7AcAlvjON5b65MyJIpoW0hrrP+dREgiITNZDThVhZdhigCo7JGx+eoCYLaEJllNTZHkHEtnYonVFAQGVJ2JF6k40RLhO+7SC9gjVPnBDkEkjIBGEipASbUbLpISIE9T5IAwT2ZU2i75FPgt+QTN5l9DSDsZnpw1mc+s63Jpnn+RkB23qVbOI4CIIRJp5x2skAuSVtWpOq68eW5RfHsz6sNIk7S1LSkfvEfb+Uv+FeqLqu8sNc0TX61pBUqA4k2g8PBm8ZjwWESyDvM0ZBvdAYacHELfIYfgCDHac4ZJn0wATKfjiR3D8E6spdPpNfR/rDE2pWGycJHgHpo70UPIyw4bAVX9QwUlVmcSamp5u65P5UJ0exocD/vydO+SnOlTpYNU3ecbSB7VDVzO8/hKXJ2AWaig6rWqk5Mf72LnIdpwb3LK5ETB1RCL/BDRL7j9B556CTif5vgDaw8d+nub3lIrfG+O/JXH9AhdRia3o1Zi+fXdIa+QI1dHmw7vOH5mUGO92dIz31gagbOwP7mYhspAep48KLm6dHw/PTQm8YQ8M0ElTjiZFEcr68aJ8kDnJaDKL2Pbee8wdWL91wBT4sxDT8/9DHd0QTtlmxGDkT/O5vPncZHKTr2msD2Xz1tbRdIgRSuXRzALqrdbtqwQT8upVCriz8sINy86f0QYjyO2yM/f+3UOP8vUlrL3PnhboAHVCzr5TgQ7XNCe+2HRhrhBwwyc5/oknGfk710CmPm0WitDimeYHXfTMyQ5maAKhim43qk48SauaCq9v0IpbmmZYXNmbh8tbV7v6skT6tDgBcqM1D2KwoMk0xfMVFS4HwgoCi9vngOSnDvBU/IZDnHLksa+Yuap2bWaqhvk5yrCs2ghoHicSstZ1Tx4ugXw5Et+PSLq5mWPvCHnPP06pe+0B7NzQen0CfO+sPp7jA+v0UBGTxE8Cu8REyP6Q+GERim787GpcdwBt95628CZM+D55DLNcY2RyZWVVXplMOvpTDyexnViWlBmBKK81uoo3aFo0oTpTFMys3qHB+EonSRWx2ESzrj0Qlw7XSW1qhMbe+QfvutnETGDGJ5UcGRYSzV0vZyv6fJEJLE6GKAiXqciTBx5bHwOUItgimMFqNWs7LtOO7vuaLn4aJbZF00fOD84UlmKh+d4Cl8NCdOWXdMQJ9fI3JrhSKizX0fGnV8Ny53vIzpiUyPppamlK0sjOKBIKKF7Mok9kl9P9SgjYkiDlPvp4ceGNfAYY8/uovWDBr0d8GlJd4qDzDA/HZpcNY3uQRxST9+l8S1A7/L0neRSqKPvPqKylvdZB0/lQHA9qbej7xb6nnUwP1ghxUlFcqA5hNVBWDFHWcNn8NK+AsfPdfpfbQAHid++QfIZYD7LRk/iIt4Or9Cb5M9roreO/OdjpCAwuGh8c+ZamNlRMK3ZsSsyGIwsqsWx2IUEBWP584kkjHku0Nj+xXXme2s52TDjekm/N5b2aX3PXLy4z2i7fUJ75nhCBMEGaSqVVkLxkS3j4ixEwk175B+sZD/ju/uUc730OXhuFfIunieKXOHy5BV9l2hWFRtB9twoOnC89+wtvFTX4WHRgKRtT5xmRj0ofwEx1Ie22F0fzNMPMQ/BbffvTahkOmHJMcnCy/r0KD+H+mFBTHLpmOx7uPsELb4DTOfRAn1mpC8xLzmVTHJz0s0V3yHYZKjVEOHm0noDRGm+Xp7mmh19I004+t5LL6keAuD7F5ew/QPy4yGJIZzcoQ/izz9o9F3/eRl3vu58WHCCLr4v7XCQos0s32rWkTGquLmrlXLR5nJEGi2uT2gLqDodRzZdxVuIsakjufzO7u6uSIQtSfI9UH1dn77U3naop5+B99cleJeI/1fVXLRPDQnSDRwRLIGqmWTnQJMj8jzLoVH/KHt25c4/QT55jslLjJ5mMlgKRJPoeTqOt4R0pjP4pQz3bhwso+Xz6yPx/Z03P/3011+v/fHHtV+/e+m9L+PJ/fUGOi9EqcxpNio8Bm5QBUj9ikjlLj0MoFJ3ARohmuobfoJhzVijl7k+uTYxSa6uT9uk4OXASz5pJC5J3wuCsoBv77pETLsb1Q3jz+sPfZgpOiKc7UnktnFsLRQDjTJ+8tOfqa4Ja2ZxB/ZdDZukmKBaexUfU4yTeJJo62Y9BmsDlZ2nG4AbNMb9GSf7qcy8ZjEQeCp6pw1hapQrlAtzLJNOvfTpNdC8du2PX3755adr175787300PoMFFtqSJBCGWJh+tlPYPgNsY7olqhI5+E7ZeCN93nyDBs8zNKYUtauOKBZyWO6nQpvrp0zlMSqRWdMteqUaIizslnHFOOqbjW7vbG96M2aa3k4R3EUXtdjLbZNsfUV3ARFOFI89WiOD/KOBoDS0SUKvTeXUmEmHrxJjshCieSGkHSy6I7x2nNaT076c2hscx8+WBIwEVDklKajof3RFWjezKNdakBZHHpJsvnHL6/++OOP3/yic+3XT97cyYyyR4KSpki2XeJ4Y5HwoERl8KqbiOjDqDsvuj7DJ44PUKSeGcW824xcyOMvWTWLlKdN6jhv4yLZonZj6Ku8UZvHoluIJ6H8RnX7tY2NbeEpf8k/wbC9TT6Pkm2DbTKB2jGYBo3/zc3pQPz/PyqVPwaU2CPl1TkIp+Ju3Hm2yzw+8NgTuJ/Em5Q3VyafxaLHU4l0LJnOXCCI13DFi2PrzBCaYkdKUsL5xxfP//73319//fdXv//2J6D+9OuvL72XXF+GQKvckmffu/BMgdPwFAUnUHqIovLc3B0nu4bJLWlQSPayU3RhzebKuVrOose9gnlyoSXC+oQZgjhWUfQSRblsCVCpJJW2SxuLNyj9aQzGkQbjtm98SNsnV+v4rQkt496Opv4TYoRRhb/r/jsBk/BIo0JAkLQn8bu0nTeK8dyf92mtIeE7nAXNR08nodWlUHVdnckYl+izYxczyOUbxE7p+CefXPvm+691vmWZO5j+9fvPf37zy6dv/pAePTjHGMUJ8ReJkNB56Heew/SogRM0m/ZIuc8ORoVoFPrLpWpBpEQxvxwHLh33I0e8Gppf3WJVpsnNOk6J1k1wRf9ZZvryy68pflei4tAat5cz2zoM9Rps3A4yw/whwIYr0ubTEy9ph5TQxGXCrSdHJwamqMm6QUH3n8bOPDjuMYzjxgziLuO+rxrj2LVykESQrA0rIVRbrF1NVresI93UJnJMLLUZZK2NIHHEtbmoq+IWrcEEY8aM0jEaf5hhRscM/vWvz/f92bz9/RLqzV6pmjbfPs/7XN/neZbR23HwcYasyPKxxrvaawC0ujJQgVD6gkAZjJZjkaaDwemNpOxqyI0inTtfRzS/YDfPOC+ffPEJiP75/W87fp9r6FnbTM6ZMIqLtAyNh0mLB6ryKg69kDxUmBLAH0gCWj33eEuoezhMRvNB/KQmyutcnpAWZdpb+dVahZxQaetuTdQmsPVxUiLheKJ7YCCTSf8zWs41Lsjttltz722Qt8fG/Aa9hf53i+cChYkEEw2xStJjlQBQkeZhp6mgZLx5TfOnvHnQSReeT/6T+7ORtRLVwXKuzm5UXlMZqGxWBmNBrtBgPTNAKysb5rb/9P5fLCh9ZWZsbGxmZvw1MAXQPwF0/qaNNc0Xc8BTCXp59Byl5538EpA6L8eTWzqIVB1zluB417VeryCyCfBWiAmCFWqq5bVO/TG8dHbUqiUOqxTWSSQS6UQuDZ748yXvU2fpZeXeuMm765CH65S6v61DWjJI7DyCRH/m6dxe6kNCQsXylqIruaTvyC6RTj4O93PNZVfd0NgMnJGIP2CGKipvp3m0hJxVwEn0Se3DX9j55fuffDE+2Dc6whntG5yZBNL3jYTunKuchl/fePvtpElxRc2iceVKTfxeUnjwRFHoav4n2GQxRx3G/Q5yRuQ+asOKLuVu8sw35Wsx76LZKEVfl0gITKQzkx5Ih6L/8MGsSnpX9fwHcbEk0Lv2x1qJtJwwHv981PA6zZw/XhGS2guPUtDJUXHTMAYVbh6HeT9J+Tr2k2NuqqtCsajYIX559UE8UMxScBrtr2L6WnlP2+Mvvv8JC6BHZkdGR3lwugZnxscdld+R9ZMnXUtjPHcoYbxGXPxjAMHTUfd9eD0ez6IM0v5lkOaTrM6F9ZGnuIGGa94KaRAKG/ibKzDwuEv5sL6gKobj8XgiDqDR7nQ0nclIPh33e1Efgteyu0c3eLfFuquctqtTCDvumLN7V4ByXR0ug6TyhtigTp+2csDi2S0z4nkR7jyzLnAx6xmTzO0ZwBbhgNIkg8L7gwBKw7GKyIW3/mJB3Ozs6GhfF199o30gykZDqfyPW3b8XgjStv1I8w0Xm+U7jFAj6XSIk7GTJ3+6JNRcn/hpcPqurqY9O4ErxEWJquMa1XYyjBJrXsdbXWuHaN4dTflOgEQ82YA0kElkBrpDIcZjG323/KUl6x1WZr2yWxJLO9HKUsDd6aYS+1twol6QvukwPkZI4oOqGK7aD/qOnOKAYo8uAlAzGuyulhj5ZBJ04In76SPB5CN4r8Kh51atoNw58eUn46OzgDk4ONjVZQAVomPjaPyfCOhcT/C+p+h8R0BxmtB2UqUqZZr7c+H2xCiSmteEZHJ1iVvjXJ5Yo9ZWJFS2idzxrSsoFrfWkkPOd3aw0DCOy5/oTMQHEul0OhKNppkQ5VP9XeERx+1sWuQ8Zso1ocG+88m6sY5gAqs7oAdORZzHcH8yFsjE8E5OXkIKnmqEP5XiUdny5SwqZy0R/hLVuCRz+/0BhFFpEFx5gs0qwiNsVAPZ+obH3h/vGxGcY2ODXbz1GUxHWLQpjd+yY2fBH8Q5WPvyGrMmG4cJ+YS5IDqDMUinSz5peoTHcCFTFIvk6uDH0nGQb0LDwyIlhkVXuINfzOeZmxpvIgvC9Un4Hk4MpIVnCB+ZUyX51JFKWkB3QcnlwXsZd3p1dc44PR083CMCF0AW55sRa2oxNqXd0ygikVBXPtlUeTEK+2vOO+Uj6DYMU6V4VE0zLJ58AF33c3MKzWngrApSNmZ60KrXv+ga6QNH4anXQckpt2nfJAIqjZ/gju2prFzdfD6kW63ZUiZff5ipHCm64AOeWtkFF56n3FI6d32YrDHERBq1WhmWTDch4RAjPjcRDHXeSkOxWor51etR91x3IpoOJSORaDIUQT7N6oJ/QHCzFD1uk3fSqr1svRlQ52Hd+lJ4pIfio9NVsDkGB9TYdJGWEFYD574iK2q7JfboInTvBti09bhEFcp8RtD3IMe3UbfnRho66ZspZD8dG+lCODm86MwYQR0ZGfwCPPHrd7Y1FEg5Va19+WwtHS8zDqhhK6qyebrgZMjb3nsfwLoQeZ/RdAJ3kyQdTmZrON8p09PKMvJWxJSZvohtR0e4E5zzA/HcgJS9GIokOTgiyKedt/Zv/dpeC+QdBMz/aN1/i6217Nb/1P2iLaYa/YeFPeZkAcpT8bS8Jyo4ZbA/SSiLXMcSGKSTJXxRhFNg+qPB8ulK3CZElNFWzxVWrXry875RhBIMeQIlTw4SOts37uC5vdBQSNHfvbHm4msMHQwDj3yKV8eRumvG8LIDWGRGAobCO+FOE4SQ/IpLWuOXXNIpWcwTrSOY4VrNs8HJ70Rq6dNORBPpXChdZKldNFYdrQ6Zeb9ujog3gvcA65lD75ZXp6tLnxa1cQp1zUzGo4d0I4XX9CrIcOTNdH2aZgTDEDvuoDMuuIhqsUaDNbbE2BhXHsG0lxtvicRmLFiFOw+tNrWqkCo8NjlrhFJP5xhA5Y2O/eXgOZXaVuihkhe8aw3UMTReeMJ2UtaQu5wEE8HmCfJ5yS0lGRZC8gPfc11tJ5a8szZBCxwtxMI0zypYmmSQVsw7pmigOBDB60wmIwhnsd4XcepHiI5+bO9khqWn+Xs2zThgASMvuw5WtEJqc/f6c7SkXPHRgaCpjR14SVJ1pFQPug+hB0PR0DAWqsUtqxFPXzloKveJ31mO34k58vkgNgBnKvUhK551SnBKUHlIQLscPHfMTxQK77a921a+9nK6j9B3Bq/wRzv+0qHGW6KxDAqaaGDpHPki6Egd61c0sSKfSBJB7ES9adcCy84wU+bDmmLFf8EWDURzkUgs1pv0MfRA9t3ZoOPVdmvWvcfW4V01OgFqJ7W4Sx+Wh6fJtNgjBELRJtP7TVjEJz1Ji6gr6eiDy5BP0sn4gs0qvsd8McodFdHKoMIjtJ3aEfLa1gaahee+mh2cnHxlcnJGeEowOdglbPxIn8GT3F1qW6qwrVDwP/3AGuFJSQ4nDZfXaDuQoiCHlS2/iOCoX7OBsOWoO7kl9ByzA4LhsBClSRPljwMtHjxo5pDPzeneWCzJJktWCGHgTT1OFV13C5LX4bSf3Su73KkS582dVXL1dJuNXJq3pi2RDEuWVOIxganeyDLtC+Flr4PZY8rgeVahIJ81VYKzIkiCiQoSHhMSOs0Qb38DeGazqdTXszOvjY+D56D8JJ0uHnzj4Pnjdzt27EwV+Ept8wc33r7czBIATuKHUjqZnQxwUjCBxronbr1kPalP3PlwXROGCJuez3eSPmIQQwKHcyDe2omus/U9l04Xo73JGm7PasxmFUtuVH+3fDBhsQjCpfaYuwzQon1dNmRamKBu7hMlmEx8hMdEgt7UOEnZmV1nMrfovgAtO5itm8TvV0H/ZCcs7JBgRUbOZ6CSC9TnQ0AJ3NuyhVQ29Vz24xGtIp8c6xsZ3rp1eHh4dkTHCTtNhASe/NaJKXS+5+m1t4t1g8dUKhhze+5z5smwUhQbXWl2x9TVkpdX+U3LSqXzyCRySaKTNwQTuYzH07k0aG7uRdW5kVCiWDX6HnP43sajX3Ia4KL92x4v1OJoM3h72gnggtB6T3yREWF+P/J5lEqcUDa180h5EQJQZ/MRm1CwtMKT6ib+POufgyaTHMAc+QTrNHj29KDtE9nnCs/d0sde95mu2a1vvuAcFpwNzwrV2cGSfGb1eyewSZVrLz+DstwylTiP4roxCq/giJLAReLZFIs5AnZoDErWrWjKs5SjFueoI6GGbEJLTgIkE7lienOut7eYRD4j2lANqOxSj5r8/EIvmwHGa4c4Lmqoi6to+7jNq93Ga9vj7OFqgW2jSaoYJLwVI5+6N88ETnwWMiJsljkaf179fvQaNjcDqC8SJCpSvwyARkjX4coH6eLk9syixveMvTY+1jf89gvPP3SvOQ899PwLbxpRHfvrrz9/A8/fEc+57Fyh0PPUbWvPPxuPXhkmRZxKiOB8nsxC2pMIb/vZAhkPr1v3oDqNVjTxha9JBgT3iOsSwQRQrs3e9GZEs8jp7e3VpEKfJsLFqlmAs4cDp8v/tKFPCR4rtHu66HacBQR5Wvi8fn0pnczOOJ6YAA3v115tMbyl8Ir9iDZxA/cmOS/xBFBW2zfGkrEYskkK2RfAthPDExrBBM2uygLoqlT20fHJwdHhN0HzpRvNeemlex96/lXJadcXfyGfytNnp+bm5rK4TcGNKLwKcsgn96dTPzpw2b6QvJWqa6cjLr8CpvyDNzM9cV0dU+pw3hnpa9Q9kcsVN0syNxd7i8XkUFKnXUu/e2OwLlgOiv9paBxOTtm9gWfBxlsJdBt/68Bb3rK8Lheu3M62PwnDhznScHQ5KpJQ5ZgMnGyWUWmOLpr99joJDtH5cj8va2xvrq6PVUYylTCUp8sJw0nW1Uz722jZTukUCk9+3jUy/MLz977kwFnClG9fen547Asp/Pzc1Pzc/Hx2YuJd/8aL1VtztCqcKsI7sfshZdDATCqEzVuwlVZybl7/8CYElJ2FGgAm464bc2BzGixRdIFJKZt4A3o/mk6OIcK6QC40Lc6z+XkvqWHxsYwwD9a2ldPadgNv6agVT/l5qTv5JZ6iLAlTlcKZg4G7xGyGgw44CflksTb0upoW7Ls/VO5IKLGRvE8NurkpVchOpJDR+8dHtr75qqTTniuuOOKsI/S48fnRz3/6jiLy/PzU/BQq759++Xwxx1SDN4wGyeghJ+69H3Ro5jFg3cNNKzcww+baazfRIrNyExWkTfmmprDycsgmag6c1dVFZnD0DvXG5MnH2AgY48sXrQgYfbdh4VKTWLzwehu+dmF3e1vjHQtvRV6JF+k7C+MOF4iEeTLrCjX1wLzT1bnffvtB0rjwnDUQX6o5kViQVUekltgvI+eTinGPXPkG0ATUr2dmt6LtyOMCmGftco64d+TzF7dsh+XwOyXktp7pBy5eXgaecE7B0xSKj4KSsvw8rs+hIa3nWEf/FoNseGWIolaZKdmZRzypt+V6NxdZo9/OxPF2UE0WqzFEDC2q8pEMybB6EZkRng5xyyOc1r90DxSxZBH3uO/FfDrnt9iV05pww6HHmJ/iUMV7Jq+MrHKbovCngSf+0n6y7heqW6YdwxlTLIRFKmeJDMcH15shDei7oqOJX1+bmbW3pyTziAUonXPFvV1fwnDYuf33ubltlRvvun25GmIxSA79U7SlvchoXXlZO7SlputpfGUyEHOS1aINm3bFpnV5ptDiKGGDNve297a3MzJC+9Krq6rrh2rqoacm+TsSsrFyAPuuLUWuJcb/uVTKWikHYneQZA2/5NLFDHWGMCmfLBOgNmN+nGMcW3SMgj9Z+9OwR5LP5eeu0eJQRi+xoBhEKyroSCBEClLSpHhUcVMBOLOFidTcN2OTXcNv/6PvgCk09XT03Tk3jn65Zcv27cjntp7Kl3HpydA79Xe0nVbS/fcngriqvx/nk7mUjF4BT7q4tAHhWm3NXo81wrgX01jzIeHZT+toO3NJtfKXJcrIJ4AGAt1R/CUDp+Ijuyuft6Wl0gaX7hH/9lgHymq8NXKgqQ2mBEfsk1HOzlkSKW3nI1lQBHRvrX0nu3Qu9n11S+NqwmKKbpFAAMlUum6al5sqEM/sO6ns3GOvjI3PjGx94aGXQNOBkxeernPjz99j5ndOzWUbgrddjMOEfEo8laDnkjmojD8SlmKvdhIzJEQDkhlTR+MRg+ZZyLNpBcmkgYEc3tFQTUs7FIsajcjm31rqUy1LhC2KhtKOfXfCd46wWKKTwyLq+by4L8lmmZY8YoCSD+HSUtcPAaekBEBBE/cTPPdV/8/BqsbR4Xv51Y2wwSJVqHvIOJ+qFHN99ijv6ZijiT9GX8FfGhaeBk2eevWehz7/ETx/n0ttm355DRESAop1B07WhaDutOecq7J7rrOD5i0NmAdTTasD2CdoONzUAZ7cnkO9Q+394NnYfheBW319S3UVMqqN3wGWfoeoIRg8vSUkbz556aZtN5Z8WLDu1kTZVx0T3EpAyYkjoSCpliMVkqTzxEoMt6KZS/4no9aYpXo1HokvxlrYaMAfgvWpMrEfH7StgKOUBdB3fhiZHJ+cGX3hXsEp2Vz6XDH803dbuEALPYx0uJwcqLwlI6DqH2N4K+LJJkiGTtP+KtG8k/MEek9j8Z10HnW2xuO53ObeoRbUHI7a1S2rG1sUs6t8QGo+5PdHOYG0GbOvs6CYtsK+VBne2h+Pnfc2fXBs4tOW5ckmY99V4UQ0lAxx0MQaASfpJfYWI50a10OqnPtzSJsRQJB/ewwS5sgvwk2gYVVDlhAye//Po5OT4Pmq4HSh6dX4me+/2z6fYrrDbc3nI59m3oVU/ii685iNyWXdX6T/ddN6BJOh6KxBAE9en7hz5cp1D3fEyXXixre3rG5pgVNV384yf+2fl6PkZ0N1d6iCAlI6bQbNldiFi+hgLulc3D7nojLvKo6uTpqSxConQvXoH0KtykVMnscmOI0dptR5GsE71WJYL1eRDWlu6V89VI81QttDmtDgfEG3aWMYNXimJj7oAs/xmdmHrLL/y3nzPfKgU6m24Nq71iyHwbSv4k2pCGXNA5itoVQI0xjWPczEaaB84olfeF7Hk3HJ0JgINzHvQ0Pt7QzHhIOuOnYNbmdlgJvTnwkFIhWhUEUkYOL30hwVG/d4mTbuFfCWFqan6xsHRhd12Yoo9HlcJkJ4s+6IkBMoZZBwngw9GOu+78H7H8wsQNaUX0Z2gosqloxA9o74dHHyEJnB5+9poA5XmJiY+LCvDzwHt+5OPBHQ8R8xSIUecsqXLj9FhAantxtSXZliTcQzl6hlugUz0TloPIAiqb+svI55YCSP4zhLvdXIJ6vjja6zIT2JKaoM+TK4H9FIIM0rSNpih4PFYl5N6d0zzd+aIFsKtZ1Iu/bHloy9Wa2NhCKfyjGJGXyMMe5OszTmnctMHVUXUtwkGdnY3s6a3RDFbZ/iIzBVkMRew4bCKuxRFjxHSSX3Pb878eQG7fp+y86dxEdrb1+Dv7SMCqdze564P814514Na2mgM0z3q9F3iShGycjoww8+uK4jPpAeKPa2tNPw3A8HCPFMQjmXx0noVt4dqAiEAtEQeKpddaF45nLgXSOTvW3w3lKSvT/tmtOSZNoKqMrvHLO/Aw8U/xM4ZYuk8CIzLFu2//6MEqDacZVJ15FsqOe2r+D6BEutghasaoUnQHpu4u4PKHZMzsy+BJ67O8Pfb/l9Pksd/gbmAS4zDQm6bOSfET5c2d5fjN96h7qzdXs+IYU3Eor2byBTHwfQzUPJ1f1A2YjeyJ2vD+HCVUYj2jUQCGQyoahP9ohja+SufSfeT15OmEXetWuSp2XvuJehiRBGAC8Lr9VmZwrVAyWfPGGDHnbIsoMPOq4MbojIIVCJajBHsWgmRDNXhc9PPhlAgZNVKDhME+A5Nkol7s0bdyeenLd/gr6Y6qncePmlNMnJHCGecpYoE59zWUv/0EA4fP3NSKdmU2r8n8HzWraW0rhJVj5HQqmxpf/q5uoWGNMIJ9WiiD+DZHZHusEyGmL9Ivk62wRsGwe91KXSm2dgoHsWvWpzlvrtypAavx7xNARQLlAARUaFJ2iyKRJAyd1hjo5Vx8oBCjeZtSb3s1d/60yFtIp8ne5PH7U50UKUIp66fxKi0uCruxdPDNKXsBfntgVvu/3s5XLnDxNjit4ZWu0vokw8lMvVtTK7CpHccC26DqIA+ssvuKFUkjrC6RyRezVeJ/PwfdU1Pm517CQ6jmiCaER/zVDI4LkrVdMNpAtEb7OsbX/3OEt7umm29jjrN6XwWrCrqBOfSVHn8cKUduPT9oYechBsBkpjGg5WM6QETihKLY4Ek+iJQaoeMvBtGKTsOxPPfNs1NtOHdd/92fr9ju0Qlu+rueHsMtPhoQkCDFsi1IRUp0IHtES0HYHUQjMUnql1v2ig4nrM0UCCaLOmhoDtLsJ20KwKhCrFn+rmOgdRzFHA5wsZfV+o8S4qFFmcLHz65LFMFm/LZXZtTyh9KzqDZoTxOP1wbDw8O1J3gCl3nmyd/CWqcTQX6/aE2UrsjrfMX7tSbBv5Sjj1zGDiJ8Gnz9499TUVzf+H5yz2CIe+fPrlS6/ZSwVO8ISjyORWEslX09ERhi+/kmMMvLk/n/gFTAk38+E6yNyR3hacTh5kP6Z9+G/RmwjcmFLoY3FYKIOAcoGa+qa171aTvfRPT+Tp3ZZgUyT2rfTRWiORpcyaSATUACmnyZgjXjDvrH0nO8+koIvOoRmhpYVRqlAvohH4SwEooBDrABV/HtZnADoDLuijr03OjNz7f/Ds+lEB5zb/bZeff8opGnQnbT+BpakUOq5WJjkMg/Y6HSC9WTcomMr7JDyqC4dz6SKVzKEqDFFM3C9cpEzAB6s/QOAeaAgE0iFORGIpRG28aYNIL3xemN2t3dYwudPNNrNszLsCJIaA8gBO0W6oGxPN44WKm3ws5l2jgpScv6oRaYghoD4UHmkIgKR4n+VBFecYqAp3KfXOs+OTo/9HPq8Y/237/NTEtuDq288+QIQbwiM2ytD/ylXdT3t2HEbyhjtXbhCcgIrKO1M/N9By2IE5SseKNJDhdSZ9nIA52gRYkdFWO3omfBls0h4ld95UjJeco+gdtOrl29n/q7Sfz8Udt5URs3YXOhgBEnASYmLdTSs/IgpfROk6JisefBLm/bwr6d0kUh5ikVQoncEn8aPlQjKIeecdjV9FzJmdePSj12ZwP3d7HnofNv3UxLvBuy4/m5Yutcqj7XtT+jv33P52Mpsk5teTVdrA2FQGrBnxBFHs/M1NEMFwl6LVjMMlKvIZ5ixI0nDSnQk0AGcmEk1HRFg0/Qi2GGd114qbBcb+stvV14tr38wC5O7cM+bdITQgoUAKnCSZcJuUaXK4tNrccRzu52WK3q/G/axOptOYUJIhFarHKXgX4ZsPPWxHIWc3NfXZJ69tvWL3gG796TfyIdl371t7MekQxvibkS0M1oB3xjrIdLqjjvatOwGUbN16YJVPzx16Lfcnja7Aibon6X2kXoC2R0PQK/zdskUV2rsIxOlo0hctOZ4LKm/ttRVJT3TkDpm8Q6w5li5io1M7Y3EfoYnLBK0WZdeycuEpQAk3WdQjbjLVTazR0FAsmiZtk0EaMEVoO7YdU+CvZFgtva9M+U7NzX/3yeC/XqA3Lqj7Kz9u2TlHfumRtWsYN8B0N+f6XE6oyWDf4mbGnTMKTHBq4jQazw1qMF35ILxEOMg5yuw1ZD3rAyF/eTTEFOdMW4ZLNJTJRH1KfupeEh/MOQvq7mp5tQi7b1T7Wy2e9n3XIrOLcqN0CHDS4KEUKLRarXtX8lMkZUYrHmmydSfhfko++6khUlLMyM3rZqAqtgjRhBsiQCE0tBUa7p6bm/r9xfEXwHN33jzqTgfifbfdgHxqKZcaEOjk1mRKFnCl8+vo36LWwZhPhoZoOjr6jniy570pn6BUHEoOqXXHFwv6QiCKp9TW3VAR7Y4SaWYC0XQuFgoVQ4rfrTXyGnSLJMeK6xJ+qvsesKGSl++9p7x56fuBx2CMwPR0mSRTNz4NPI89SC1VF6GDl1HOEZklKgnlAjVNxQy2CflAVIkRJlOrfkTZcucbYzfuBtB7X/txy/a51E2VGynHkZ5XOgSiPuJ57mXNtL8O5LTpWcljdRRft8EkQWWPtDhqUz7emSiGKGvSrAex2yxeyzB3mEX0FfhJSnxGgsloEp1foHFYCXXZddc878X9265bwD3x35ubdqZgsFlb5Q4OSGLjVUhSLz8LoSEzMPhz/+PO0+IjefPtKHwRMCnLVHRza/q5RA3bxufHY2J1Txs55SwCuuPLWW7Q/85+ou2pVA8seqpxdMADJ1VNzbJhWAh4xlsvWX/tzdfdzEMjlzY4i82INx9kjWGYcDOdTlYRW8Cjo47VQCd+QzfD3zIBgiJ5yCwMNDr/NzwZmqua1KW3AAAAAElFTkSuQmCC\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Illuminance\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:lightbulb-on\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "brightness", + "luminance", + "luminosity", + "light", + "light level", + "light intensity", + "lux", + "candela", + "foot-candle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/illuminance_progress_bar.json b/application/src/main/data/json/system/widget_types/illuminance_progress_bar.json new file mode 100644 index 0000000000..6d7de6d4f0 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/illuminance_progress_bar.json @@ -0,0 +1,37 @@ +{ + "fqn": "illuminance_progress_bar", + "name": "Illuminance progress bar", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAn1BMVEXg4ODf39/g4OAAAADg4ODf39/////YGDjg4OAhISH29vbj4+NYWFiQkJDx8fE9PT2srKzIyMjdNVF0dHS6urriUmkvLy/64ubv7+/wqLT1xc2enp798fOGhobsjJyXl5faJkX309qpqanpfY+CgoLnboLumqjfQ13aJkTV1dVKSkrCwsLzt8Hc3Nx+fn6xsbHx2d3wqbTkYHZmZmbgRF3eeSPoAAAABnRSTlPvIL8Ar7DvmsykAAAEHklEQVR42uzYXWsaQRTGcW3Ckxxm58zIbKchUySltlRv8/2/W8/oipYQt5ALn8j5w67zohc/dBfZxd3yy+Lxs3e/vFssF0/49Kkx7hU30JP9rHATLW4F8ugQshzClkPYcghbDmHLIWw5hC2HsOUQthzC1gXImBVBBmCQgPcqDRxdgAyymoVsZQeOPgjBiuXx3v9CxmiYVdz2YyybgP0JGjMQm8aSYYVWYke3pu2woq2/zwqxtIBT14IUmyBKtiPVKrX0UzjsS01JZAOMte5ERiDVVEUKEJLYVutbYsMzCQHkVW2WFBtpE8SWQ63ATgJWfSX1rSpA2Q+qajJE7thjBJAI5Ol0hABIAktDkDRNOkuMh1x0lAKgJpzHCwmDWBOkf6xv74tyCKeYIUlWinOIHu/OTV5zD6euBhmBzQykG/QcglTV/EPYXzlQnHUlSJOUY52BVIl59w+kyS5HSX1ScksEFzuKSJ37RrZJpCTREwSGl8FetYgNrnD7fZsGzBf03SW1wdv8369D2HMIWw5hyyFsOYQth7DlELYcwpZD2HIIWw5hyyFsOYQth7DlELYcwpZD2HIIWw5hawby4/n5G459txlYuwj5/fJgfZ0o6z57WYOzS5D1w6E/e8mvafYTlF2A/GW/DlYchKEoDO/OARddXAhBCUEpSVy4mJa+/7NNHK0MjEQKQ3tL89PS2lW+xkS1JDu4M2kAtOTNwgqp8/QqQCYyAHA9RwBX0gLotE5JAdKSLXKGBNAzYa6nQGMFyP3PFwrgSIO5REJjBQiE4wR40gP2DjErJA5QVQlihex7MmAHIi1UVYI4z7nx8u6QGxnaKGTch0wDMGjxlHctD8AJR7cLifl30bIZFyCGtMhFsv27a2UIzNmLg47KELfOTASWy+L8mTaI7fVc5guQK5dhBnICwnLUkWGDQEYtE3KwRmS51xL3I5ABnZB2g3gxWpZICYLAtQm5M5c87hDLi1VzbpUgaIU5s47VC8k+YoNIANSs9sMnxMH9PrLQ2qc8s79PFaKtCtFWhWirQrRVIdqqEG19AMQaPr301Rz1OCTxBZnmsIchfElN7lR6PQ4xfEEpj/W/Z8QmPj2zrJHT/N7/0nzwrvVmVYi2KuS7vbtncRiGATDstHcvxSUH4TwIihBkCybJ//9390HmQtrBaqoXvOvBg0Z5KyDeugOpvRUal+sIiJnJNtB+iKhgM03LuiowVgZj1iyL7IdMNyjNv0QUUCEv1BXGYT9kGN1Avv9fKQHxAlGBhfogRJbMbaJpG8RmJmM20Ot+CEV7o20b5Kr2J1j7vvAAhJzxkmwDvfdmf60C4q2AeCsg3gqItwLirYB4KyDeCoi3AuKtgHjrQJDEIbqk0wFOT8PXZ+qSl+s0TyS/jHP3cXn10qk7/wALYyfOKq+pZAAAAABJRU5ErkJggg==", + "description": "Displays illuminance reading as a horizontal progress bar. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'illuminance', label: 'Illuminance', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Illuminance\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":1,\"to\":5,\"color\":\"#FFA600\"},{\"from\":5,\"to\":20,\"color\":\"#F36900\"},{\"from\":20,\"to\":50,\"color\":\"#F04022\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":100,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":1,\"to\":5,\"color\":\"#FFA600\"},{\"from\":5,\"to\":20,\"color\":\"#F36900\"},{\"from\":20,\"to\":50,\"color\":\"#F04022\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}]},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Illuminance\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"lx\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:lightbulb-on\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "progress", + "weather", + "environment", + "brightness", + "luminance", + "luminosity", + "light", + "light level", + "light intensity", + "lux", + "candela", + "foot-candle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/illuminance_progress_bar_with_background.json b/application/src/main/data/json/system/widget_types/illuminance_progress_bar_with_background.json new file mode 100644 index 0000000000..80005ba78e --- /dev/null +++ b/application/src/main/data/json/system/widget_types/illuminance_progress_bar_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "illuminance_progress_bar_with_background", + "name": "Illuminance progress bar with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays illuminance reading as a horizontal progress bar with background. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'illuminance', label: 'Illuminance', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Illuminance\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":1,\"to\":5,\"color\":\"#F89E0D\"},{\"from\":5,\"to\":20,\"color\":\"#F77410\"},{\"from\":20,\"to\":50,\"color\":\"#F04022\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":100,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":1,\"to\":5,\"color\":\"#F89E0D\"},{\"from\":5,\"to\":20,\"color\":\"#F77410\"},{\"from\":20,\"to\":50,\"color\":\"#F04022\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}]},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Illuminance\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"lx\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:lightbulb-on\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "progress", + "weather", + "environment", + "brightness", + "luminance", + "luminosity", + "light", + "light level", + "light intensity", + "lux", + "candela", + "foot-candle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/image_map.json b/application/src/main/data/json/system/widget_types/image_map.json new file mode 100644 index 0000000000..2e10796583 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/image_map.json @@ -0,0 +1,35 @@ +{ + "fqn": "maps_v2.image_map", + "name": "Image Map", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the indoor or relative location of the entities on the image map. Useful to display floor maps, smart parking, etc. Entity coordinates are expected to be in the range from 0 to 1. Highly customizable via custom markers, marker tooltips, and widget actions. ", + "descriptor": { + "type": "latest", + "sizeX": 8.5, + "sizeY": 6.5, + "resources": [], + "templateHtml": "", + "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('image-map', false, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-map-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 0.2;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || 0.3;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.9430343126300238,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.1784452363910778,\"funcBody\":\"return \\\"colorpin\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]},{\"type\":\"function\",\"name\":\"Second point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 0.6;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || 0.7;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.773875863339494,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.405822538899673,\"funcBody\":\"return \\\"thermometer\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"image-map\",\"mapImageUrl\":\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMTEzNC41IiBoZWlnaHQ9Ijc2Mi43OCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI3LjA3MSAtMzA3LjkpIj4KICA8ZyBmaWxsPSJub25lIj4KICAgPHBhdGggZD0ibTkwNi4wMyA3MDYuMTMgMy40MjkyIDE3Ljc5Nm0tODgwLjg5IDQxLjEyMWMxNTAuNDQgNi44MzM0IDE0Ni4zOS0yNi4zMzQgMTY2LjQzLTI5LjMyIDM2LjE0NC01LjM4NDggMTE0LjI5LTYuNTI1NCAxNDguMzMtOC42MjM1IDQzLjM3OC0yLjY3MzggMTQxLjc2LTExLjIzMSAxODguODYtMTkuODM0IDM5LjgxMS03LjI3MjggMjIxLjM3LTAuODYyMzUgMzE5LjA3LTAuODYyMzUgNzAuODI3IDAgMTQ2LjkyLTEuNzI0NyAyMTguMTgtMS43MjQ3LTMxLjYyIDAgMTE3Ljg2LTIuNTg3MSA4Ni4yMzYtMi41ODcxbS0yNS4wOTEtNjguMTI2Yy01Mi44IDM0Ljc4NS02NS44OTUgNTEuNzQ5LTk1LjYzOSA4MS40OTMtMjQuOTMxIDI0LjkzMS0xNDAuNC0xOS4xMzktMTc4Ljk0IDM2LjY1LTEyLjI4MSAxNy43NzctNDcuMDAzIDQ2LjU0Ny02NS4xMDggNTkuMDcxLTIwLjEwNSAxMy45MDgtNTYuMDM3IDQ0Ljk1Ny02Ny43NjkgNzMuMDc4LTQuODAxNSAxMS41MDktMTMuMzggMzUuOTkzLTIzLjQ0OSA0Ni4wNjItMTAuNDk3IDEwLjQ5Ny0zOC4zNzcgNi4zODU3LTQ0LjAyMyAxNy42NDgtMTkuMDA1IDM3LjkwOC0yNS40NjUgMTAwLjkyLTY3LjYxOCAxMDIuMDVtMTkuMjgyLTYyNC4wMWMzNC42NTktMS44NzM4IDg0LjAyNyA3LjM5MTMgMTA5LjktNC4yODU0IDEzLjI4Mi01Ljk5NDEgNDEuNDA3LTIuNDYxNCA2Ni44MjktMi4zMjA1IDM1LjMyMiAwLjE5NTc4IDY0LjM4MiAwLjYzNDc3IDEwMS45MiA1LjAyMzIgMjUuMDMgMi45MjY1IDQ0LjY2MyAzNC4yODcgNTguNTI3IDUwLjY0NCAxNy4wOTkgMjAuMTczIDYyLjc2NC0xLjcxNDcgNjYuMzA2IDMyLjEzNCA1LjEwMjcgNDguNzY2LTYuMzI4NCA3OC42MzcgNi4xNDExIDk3LjM0MiAxOS45NjkgMjkuOTU0IDUwLjQ4NiAxNy44NTYgNDQuNjE5IDgzLjk3MW0tNDcyLjQ1LTM3OC43OWM0LjY0MzUgMjMuNzI5IDE1LjA2OSA3Mi43NzYgMTkuMDYxIDEzMC42NCAwLjg3MjA2IDEyLjY0IDUuNDQ3MiAyNC45OTMgNC4yMjIzIDQ1LjI3OC0yLjUxNzIgNDEuNjg4LTE1LjcxNyA0My42NzctMTUuMDkxIDYwLjM2NSAxLjQzMiAzOC4xODIgMzAuNjE0IDkzLjgzNyAzMC42MTQgMTM5LjcgMCAyNC4xODEtMi42Njk2IDExNS4zOSA3LjMzIDEzNS4zOSAwLjE1OTExIDAuMzE4MjEgMTAuMDY1IDM1Ljg4MyAxMC43NzkgNDkuMTU0IDAuOTQzNzggMTcuNTI1LTI0LjQ3OCAzOS40Ny0yOC4wMjcgNDYuNTY3LTUuNDc3NyAxMC45NTUtMzYuOTczIDEwLjg4Mi00MC4xIDI0LjE0Ni0zLjg2ODggMTYuNDE1LTMuODY2MyA0My43OTcgNC4wNDY1IDU5LjQ0MW05Ny4zMzctNjkxLjAxYy01LjAxMzMgMzUuNTE2LTQzLjY1OSAxMS4zMTctNTguNTM5IDIzLjc4MS0yMS4zMyAxNy44NjktNjIuNSAzMS40MzItNzAuMTI0IDM1LjM2Ny0zNS4wODggMTguMTA4LTExMC40Ny0xNS4xNDItMTI1LjYxIDQuMjY4NC0xNS45NTEgMjAuNDQ3LTAuMDczNSA2MS40NjYtOS4xNDY3IDg0LjE0OS02LjAzNTcgMTUuMDg5LTE4Ljg3NyAyMy4wMTctMjcuNDQgMzIuOTI4LTE5Ljc0OCAyMi44NTYtNjkuOTc0IDY5LjgyNC04NC43NTkgMTAwLTcuNDk3NCAxNS4zMDQtMy4yODQzIDQ0LjQyLTMuNDcwNSA2My4zNDMtMC4xMjc5MyAxMi45OTQtMC44MTAxNSAyMy4xMDQgMi40MDM0IDI4LjI3NiA0Ljk2MTYgNy45ODU4IDIzLjcyIDI4LjExMiAyNC4yMzkgNTAuNjExIDAuMjk0MTEgMTIuNzcxIDAuMDEzMyA3OC41OTEgMy4wNDg5IDg3LjY1NSAyLjMxMjYgNi45MDU1IDQuMjIgMjYuNTY1IDEwLjIxNCAzNi41ODcgMTEuMzU0IDE4Ljk4NCA0LjM4NzQgNDAuMTU3IDI3Ljg5NyA1My41MDggMTkuMDUgMTAuODE5IDQ2Ljg3OCAxMi4yMTkgODEuOTI2IDE0LjQ2MSAzMy43MDMgMi4xNTU5IDYxLjUxMi0xLjQzMDQgNzYuOTIxIDYuMTQxMSAxMS41ODUgNS42OTI3IDguNTgxNSAxNy45MzMgMTQuMjk1IDI5LjM2MSA1LjY0MDQgMTEuMjgxIDMxLjUwMyAxMS4xNTYgNDEuODA0IDQzLjQ1NSA3LjYwNTkgMjMuODQ3IDMuMDg1OSA0NC4xNTcgNi43MDc2IDY1Ljg4NyIgc3Ryb2tlPSIjMzY0ZTU5IiBzdHJva2Utd2lkdGg9IjMiLz4KICAgPHBhdGggZD0ibTQzLjI3OCA1MTcuOTVzMjMwLjg1LTMuNjM4IDI1MC4wMS0zLjY1ODdjNy40ODIyLThlLTMgOC42MTk1IDUuMTUxOSAxNC4wMjEgMTEuNDU5IDI0LjU5NiAyOC43MTkgOTMuOTEgMTEyLjk0IDkzLjkxIDExMi45NCIgc3Ryb2tlPSIjMzM2IiBzdHJva2Utd2lkdGg9IjFweCIvPgogICA8cGF0aCBkPSJtMzUuOTYxIDU3Ny43czE2NS41Mi0xLjY4NDUgMjQ4Ljc4LTEuNjg0NWM0Ljk0NzUgMCA3LjcyOTktMi44ODMzIDEwLjUzOC01LjcyOTggOS42NjExLTkuNzk0MiAyNS42MzItMjguNTkgMjUuNjMyLTI4LjU5IiBzdHJva2U9IiMzMzYiIHN0cm9rZS13aWR0aD0iMXB4Ii8+CiAgPC9nPgogIDxwYXRoIGQ9Im0zOC40IDY0MS43MyAzOTMuMzEtNC4yNjg0IiBjb2xvcj0iIzAwMDAwMCIgZmlsbD0iIzMzNiIgc3Ryb2tlPSIjMzM2IiBzdHJva2Utd2lkdGg9IjFweCIvPgogIDxwYXRoIGQ9Im0zOS4wMDkgNzA0LjU0IDQ4NC4xNi02LjcwNzYiIGNvbG9yPSIjMDAwMDAwIiBmaWxsPSIjMzM2IiBzdHJva2U9IiMzMzYiIHN0cm9rZS13aWR0aD0iMXB4Ii8+CiAgPGcgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzM2Ij4KICAgPGcgc3Ryb2tlLXdpZHRoPSIxcHgiPgogICAgPHBhdGggZD0ibTMwMy45NiA2ODIuNTkgMTQ2LjggMS44MjkzYzEwLjUzNCAwLjEzMTI3IDE0LjM0NC0yLjYzNzQgMjUuNDg3LTYuMzcyOCAxMC40MTItMy40OTAzIDMxLjQyNC0yLjY5OSA0MS4zODUtMi43NzM4bDQwNS41Ni0zLjA0ODkiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtNDI2LjIyIDMxNC44OWMyLjA2NzUgOS4wNTI3IDEuODQxOCA1MS43MjggNi41MDc5IDc0LjgzNSAxLjY3NDggOC4yOTM0IDguNjc1MSAxNC4wNjYgMTAuMDU1IDE0Ljg1OSA0LjkwMTUgMi44MTQ2IDEwLjgxNSA4LjE0OTggMTMuMDQ2IDE2LjA4OCA2Ljc1NzggMjQuMDQ2IDAuODc5NzIgNjguNDUyIDAuODc5NzIgMTEwLjY5IDAgNi4wOTc4IDEuNjYwMSAzMC4xNDctMi4xNTU5IDMzLjk2My0yLjU0MDggMi41NDA4LTAuMjgxNjMgMTIuOTkxLTMuNDM2OCAxNi4xNDRsLTkuODQ5NCA5Ljg0MzFjLTEwLjM2NyAxMC4zNi0xMS41OSA2LjUyNjEtMTcuNzM4IDE4LjgyMy0zLjU2NzcgNy4xMzU0IDUuNDAyNCAyMC42NzIgNy4zNTQzIDI0LjU3NiAxLjkzMjEgMy44NjQzLTEuODQyMiA0Ljc3NzctMS43OTI0IDcuNDQ2MyAwLjI1Mjg2IDEzLjU0NSAyLjI5NzUgMzczLjkzIDIuMjk3NSAzNzMuOTMiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtMzY1LjI0IDUxOS43OCA0LjExNiA1MDIuMTUiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtMTE2LjUzIDUwNC4xOSAzLjg4MDYgMzEwLjk2IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTMxNy42OCA1NzYuNDkgMTMwLjE5IDEuNTI0NGM0LjUxMDggMy4yNDE3IDIwLjM0NSA3Ljk2ODUgMjcuNzQ1IDQuMjY4NCAzLjE1NTUtMS41Nzc3IDkuNDE5LTUuMzg4MiAxNC4wMjUtMy45NjM2IDQuMjY3IDEuMzE5OCA2LjAxNjkgMy4xMTYzIDEwLjM2NiAzLjA0ODkgMTAuMzA0LTAuMTU5NzUgMjAuMjEyIDAuMzg3NDEgMzAuNDg5IDAuMzA0ODkgMTc3Ljg5LTEuNDI4MyAzNTYuNTktMi4xMzI1IDUzNC43Ny0zLjA0ODkiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtNDc1LjMxIDU4Mi44OWMtMy40NDQyIDExLjM1MS0yLjEwMzQgMTIuNDM0IDMuNjU4NiAyMS4wMzcgMy43OTQ0IDUuNjY1NiA1MC44NjMgMTMuMDM4IDQxLjQ2NSAyNy4xMzUtMTAuNTM3IDE1LjgwNS0yMi44OTctNS40Nzc3LTMzLjg0My0xLjgyOTMtNS40NTI0IDEuODE3NC03LjM0OSA1LjQ1NjMtMy42NTg3IDkuMTQ2NiAyLjgwNjggMi44MDY4IDQuMDQ4IDEuODA0IDYuNTIwMyA1LjEwMDQiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtNDMyLjAxIDYzNi44NWM4LjMxOSAxMy4xMSAxOC44NDYgMTQuNjM1IDM1LjY3MiAxNC42MzUgMi45Mzg2IDAgNy44Ny0wLjkzMzcxIDEwLjY3MSAwIDExLjM1OSAzLjc4NjQgMjcuMTk0IDEwLjI3NiAzNi4yMDIgMjEuMTI5IDguMjggOS45NzY2IDEwLjI1MyAyMy44ODMgNy43MDIgMzcuMTA0LTYuMTY5OSAzMS45OC0xNi43MTQgNTYuOTg5LTE5LjA0NCA4Ni41NjktMS4zNDggMTcuMTE5IDQuNTA5NiAyMi41MzUgMTEuMDcxIDMzLjkyOSAxMC42NyAxOC41MjcgOC43MjQ1IDE0LjIgOC41NzE0IDM0LjI4Ni0wLjEzOTYzIDE4LjMxOSAwIDYwLjI2NCAwIDgwLjcxNCIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im01MjguNTEgNjU4Ljk2Yy0xMC42ODEgMC45MDQ1NC03LjEwOC01LjYwMjYtMTAuODI0LTguMDc5Ni00Ljc4NDUtMy4xODk3LTEyLjIyNy0xLjI1MS0xNi43NjktNS43OTI5LTAuNjY2MTItMC42NjYxMi04LjgwOTctNC4xMDg4LTEwLjE3NC0yLjc0NC04LjM2NDYgOC4zNjQ2LTMuMDQ4OSAyMC41NTItMy4wNDg5IDMzLjUzOGwzLjAyMiAzMzkuNyIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im01MTcuOTkgNjUxLjAzYy0wLjIyMTcxLTIuNzAxOCAxLjkwMzUtNS41NjIxIDMuMzUzOC03LjAxMjQgMS43OTk0LTEuNzk5NCA2LjkyMjkgMS4wMDQyIDguODQxOC0wLjkxNDY2IDAuMjg3NjUtMC4yODc2NiAwLjg0MzI5LTExLjE2NCAwLjIyODY2LTEzLjU2OC0yLjA2NDgtOC4wNzQyLTIuMDU4LTI4LjY1Ny0yLjA1OC0zOC43MjF2LTczLjE3MyIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im01MjguNjYgNjc1LjQyLTAuNDU3MzMtMzEuNTU2IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTc2Ni4zMiA1NzkuNjQgMC40MzExOCAxMy43OThjMy4xMzY0IDQuNjY5MiAzLjAxODIgOS42MDA3IDMuMDE4MiAxNi4zODV2MTU3LjM4IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTExMjIuOSA3NjUuOTFjLTIwMi4zMSA0LjY5MDUtNDAzLjc0LTEuMTEzOC02MDUuOTUgMy4zNTM5LTEwLjg2NCAwLjI0MDAyLTMuMzYxNS04LjU4NjMtMjguNTM3LTguNTg2MyIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im04NjAuMDEgNzM3LjA3cy05Ny40NDggMC44NTgwNi0xNDcuNTcgMC44NTgwNmMtNS4yNjg2IDAtNC41MTU1LTguMzI5OS03LjMwMDktOC4zMjk5LTMuOTc0NCAwLTguNjI5MiAwLjAyMDEtMTAuNTA5IDAuMDM1OS0yLjMzNDggMC4wMTk3LTEuODEwOSA4LjM2Ni00LjE0NTggOC4zNjY5LTQ2LjE2OSAwLjAxODgtMTY3LjQxLTEuMzA4LTE3NS4wNS0xLjMwOC00LjQyOTYgMC04LjU3NjMtNi40Mzk3LTEzLjEzMi02LjQzOTdoLTE0LjM5NSIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im02NzUuMDEgODMxLjE3LTAuNjA5NzgtNTIxLjc3IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTc5OS40IDMxMy4wNiAxLjIxOTYgNDk1Ljg3IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTczNi41OSAzMTIuNDUtMS4yMTk2IDcxNi40OSIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im01MzAuMDMgNjQzLjQ2IDM5Mi4zNy0zLjAxODIiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtODU5LjQ1IDMxNC45IDEuMjkzNSA1MDcuOTgiIGNvbG9yPSIjMDAwMDAwIi8+CiAgIDwvZz4KICAgPHBhdGggZD0ibTkyMS41NCAzMTAuNTkgMS43MjQ3IDUzMS43NSIgY29sb3I9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMXB4Ii8+CiAgIDxnIHN0cm9rZS13aWR0aD0iMXB4Ij4KICAgIDxwYXRoIGQ9Im03MzYuMjkgNDUzLjMxIDE4NS42OC0wLjMwNDg5IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTEwNjAuOCA1MTQuOTdzLTM2My4yOC01LjYyNjItNTQ0LjY1IDIuNTIxOGMtNC4xNzc4IDAuMTg3NjktMTIuNSAxLjA2NzEtMTIuNSAxLjA2NzEtMS41NzEgMC4xMzQxLTIuMDAwOS0yLjMyNS0yLjU5MTYtMy41MDYyLTAuMDk2Ny0wLjE5MzQzLTcuMDYwOC0xLjkzMzQtNy42MjIyLTEuMzcyLTIuODkzMSAyLjg5MzEtNy42MzE3IDQuMjQ4Ny0xMi4xOTYgNC4xMTZsLTExMi4wNS0zLjI1NzgiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtMzk5LjgyIDQ3OS42MSAxMS42NDIgNS42MDUzYzIuOTg0MSAxLjQzNjggNi41Mjg4LTAuNDc3MTIgOS45MTcxLTAuNDMxMThsMTI3LjIgMS43MjQ3IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTUxOS4yNSA1MTcuMTItMC40MzExOS0yMDguNjkiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtNDMyLjkzIDM4OS43MWMxMS4wNDUgMCAzNS41MzMgMC42MTkyNyA0Mi41OC0xLjAwNCA4LjQwNTItMS45MzYyIDcuMDY2LTYuOTUzOCAxNC4xOTctNi45NTM4IDcuODA5NSAwIDYuNTQyOSA4LjA2MjQgMjAuMTQyIDguMDYyNCAxMy45OTEgMCA0NC45NzcgMC4zNzg4NiA2My45NCAwLjM3ODg2IDEyLjA4NCAwIDgyLjAwMyAwLjMwNDg5IDkzLjYwMSAwLjMwNDg5IDguNzYwNSAwIDEzLjE2LTIuMjg4MyAyMS4zNDItNy4wMTI0IDcuMTk1Mi00LjE1NDEgMi4wNTQ2LTkuNDkxNCAyMC40MjgtOC44NDE4IDIzLjE0NSAwLjgxODMzIDEyLjY0MyAxNC4wMjUgMzIuMzE4IDE0LjAyNWgxNTAuOTJjMTQuMzMyIDAtNC4xMTkxLTEzLjExIDI5LjI2OS0xMy40MTUiIGNvbG9yPSIjMDAwMDAwIi8+CiAgIDwvZz4KICA8L2c+CiAgPGcgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9IlZlcmRhbmEiIGxldHRlci1zcGFjaW5nPSIwcHgiIHdvcmQtc3BhY2luZz0iMHB4Ij4KICAgPHRleHQgeD0iNTg4LjY3OTU3IiB5PSI3MzUuODA0NjMiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjU4OC42Nzk1NyIgeT0iNzM1LjgwNDYzIiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+TGluY29sbjwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSI2ODYuMzk4NSIgeT0iNzY1LjYyODQyIiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI2ODYuMzk4NSIgeT0iNzY1LjYyODQyIiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+SGFycnk8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI3MDkuODcxODMiIHk9Ii04MDIuMzc3MzgiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjcwOS44NzE4MyIgeT0iLTgwMi4zNzczOCIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPldvb2RsYXduPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDkwKSIgeD0iNTYyLjExOTI2IiB5PSItNzcxLjk2ODE0IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI1NjIuMTE5MjYiIHk9Ii03NzEuOTY4MTQiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5FZGdlbW9vcjwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9IjU5OC4zMDQ4NyIgeT0iLTczOC4zNjY0NiIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNTk4LjMwNDg3IiB5PSItNzM4LjM2NjQ2IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+T2xpdmVyPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDkwKSIgeD0iNTkyLjEyMjg2IiB5PSItNjc3LjIwMzk4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI1OTIuMTIyODYiIHk9Ii02NzcuMjAzOTgiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5IaWxsc2lkZTwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9IjU5Ny4zMjcwOSIgeT0iLTg2Mi42MTQwNyIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNTk3LjMyNzA5IiB5PSItODYyLjYxNDA3IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+Um9jazwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9IjU4Ny4zNzAxOCIgeT0iLTkyNi4xMzY2IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI1ODcuMzcwMTgiIHk9Ii05MjYuMTM2NiIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPldlYmI8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iODcxLjE2MTAxIiB5PSI2MzcuNTc1MiIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iODcxLjE2MTAxIiB5PSI2MzcuNTc1MiIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPkNlbnRyYWw8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iODczLjgzMjI4IiB5PSI1NzcuMDMyNDciIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9Ijg3My44MzIyOCIgeT0iNTc3LjAzMjQ3IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+MTN0aDwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSI4NzUuOTY2NDkiIHk9IjUxMC4yNjE4MSIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iODc1Ljk2NjQ5IiB5PSI1MTAuMjYxODEiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij4yMXN0PC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHg9Ijg4MS4zMTY1OSIgeT0iNDUwLjE5ODc2IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI4ODEuMzE2NTkiIHk9IjQ1MC4xOTg3NiIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPjI5dGg8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iNjE1Ljc5MjQ4IiB5PSIzODcuNzQ3MTYiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjYxNS43OTI0OCIgeT0iMzg3Ljc0NzE2IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+Mzd0aDwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSI0ODQuNjkwMzciIHk9IjQ4MS42NTI4NiIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNDg0LjY5MDM3IiB5PSI0ODEuNjUyODYiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij4yNXRoPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHg9IjU2My4wNDY3NSIgeT0iNTEzLjM2MTMzIiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI1NjMuMDQ2NzUiIHk9IjUxMy4zNjEzMyIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPjIxc3Q8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iNTY1Ljk3MTUiIHk9IjU3Ny44OTQ4NCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNTY1Ljk3MTUiIHk9IjU3Ny44OTQ4NCIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPjEzdGg8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI0MzMuNTgwNzUiIHk9Ii00NjAuNzMzMTIiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjQzMy41ODA3NSIgeT0iLTQ2MC43MzMxMiIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPkFtaWRvbjwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9IjQwNS41MzA5OCIgeT0iLTUyMy41NDAxNiIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNDA1LjUzMDk4IiB5PSItNTIzLjU0MDE2IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+QXJrYW5zYXM8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI3NDUuNDg0NjIiIHk9Ii0zNzIuNTg1OTQiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9Ijc0NS40ODQ2MiIgeT0iLTM3Mi41ODU5NCIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPldlc3Q8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI1OTYuNzI4MzMiIHk9Ii01MzEuMjU5MjgiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjU5Ni43MjgzMyIgeT0iLTUzMS4yNTkyOCIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPldhY288L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI1OTUuNDM0ODEiIHk9Ii0xMjIuNTAyOTUiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjU5NS40MzQ4MSIgeT0iLTEyMi41MDI5NSIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPk1hemllPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDQ1KSIgeD0iNjk1Ljc3Mjk1IiB5PSIxNjIuMDY4NzciIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjY5NS43NzI5NSIgeT0iMTYyLjA2ODc3IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+Wm9vPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHg9IjI0MC41ODk5NyIgeT0iNTc0LjQ0NTQzIiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSIyNDAuNTg5OTciIHk9IjU3NC40NDU0MyIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPjEzdGg8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iMjA2LjAzMTc1IiB5PSI1MTEuNjM2NjMiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjIwNi4wMzE3NSIgeT0iNTExLjYzNjYzIiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+MjFzdDwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9IjYyMC40NDMxMiIgeT0iLTUwNi42ODIxOSIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNjIwLjQ0MzEyIiB5PSItNTA2LjY4MjE5IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+TmltczwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSIzNzAuMjE2ODYiIHk9IjY5OC44NDAwOSIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iMzcwLjIxNjg2IiB5PSI2OTguODQwMDkiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5NYXBsZTwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSIzODQuMDg0MiIgeT0iNjgwLjg1MTM4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSIzODQuMDg0MiIgeT0iNjgwLjg1MTM4IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+RG91Z2xhczwvdHNwYW4+PC90ZXh0PgogIDwvZz4KICA8cGF0aCBkPSJtMzY3LjkxIDEwMTBoMjYzLjAyIiBjb2xvcj0iIzAwMDAwMCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzM2IiBzdHJva2Utd2lkdGg9IjFweCIvPgogIDxnIGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJWZXJkYW5hIiBsZXR0ZXItc3BhY2luZz0iMHB4IiB3b3JkLXNwYWNpbmc9IjBweCI+CiAgIDx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDkwKSIgeD0iNzM2LjI2NzQ2IiB5PSItNDMzLjEzNzc2IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI3MzYuMjY3NDYiIHk9Ii00MzMuMTM3NzYiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5NZXJpZGlhbjwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSI1NzIuODMyMTUiIHk9IjY0MC4yMDUyNiIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNTcyLjgzMjE1IiB5PSI2NDAuMjA1MjYiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5DZW50cmFsPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHg9IjU3NS4wODk2NiIgeT0iNjcwLjkwMzUiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjU3NS4wODk2NiIgeT0iNjcwLjkwMzUiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5Eb3VnbGFzPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHg9IjQ5OS40ODk2MiIgeT0iMTAwOC42MDY5IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI0OTkuNDg5NjIiIHk9IjEwMDguNjA2OSIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPjQ3dGg8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iMjE2LjY0NTQzIiB5PSI3MjUuOTgyOTciIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjIxNi42NDU0MyIgeT0iNzI1Ljk4Mjk3IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+S2VsbG9nZzwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9Ijc3NC44NzU2MSIgeT0iLTUwOC4xODk3MyIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNzc0Ljg3NTYxIiB5PSItNTA4LjE4OTczIiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+TWNDbGVhbjwvdHNwYW4+PC90ZXh0PgogIDwvZz4KICA8cGF0aCB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDI4Ny4zNikiIGQ9Im0zNjQuMTYgNjU4LjQzIDI5OS41MS0xLjAxMDJjNi40OTg3LTAuMDIxOSA2Ljk3NzIgOS4yNTQxIDE2LjU5NiA5LjM5MjUgMTIuMDU0IDAuMTczMzkgMjkuMTExLTAuNTM1NzIgNTQuMTE0LTAuMzAxMSIgY29sb3I9IiMwMDAwMDAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzMzNiIgc3Ryb2tlLXdpZHRoPSIxcHgiLz4KICA8dGV4dCB4PSIzNzMuOTkzMDQiIHk9Ijk0NC4zNTc1NCIgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9IlZlcmRhbmEiIGxldHRlci1zcGFjaW5nPSIwcHgiIHdvcmQtc3BhY2luZz0iMHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSIzNzMuOTkzMDQiIHk9Ijk0NC4zNTc1NCIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPk1hY0FydGh1cjwvdHNwYW4+PC90ZXh0PgogIDx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDkwKSIgeD0iNzgwLjg0NjA3IiB5PSItNDkwLjI0NTk3IiBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0iVmVyZGFuYSIgbGV0dGVyLXNwYWNpbmc9IjBweCIgd29yZC1zcGFjaW5nPSIwcHgiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9Ijc4MC44NDYwNyIgeT0iLTQ5MC4yNDU5NyIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPlNlbmVjYTwvdHNwYW4+PC90ZXh0PgogIDxwYXRoIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgMjg3LjM2KSIgZD0ibTM2Ny43IDUzNy4yMSAxNDEuMjgtMS4wMTAyYzYuNDktMC4wNDY0IDEyLjc4MSA3LjIzNTQgMTkuMTkzIDcuMzIzNiA1NS45MjQgMC43Njg5IDE1OC42OS0wLjE3MzMzIDIzNi41MS0xLjAxMDIgNy44Mzk2LTAuMDg0MyAyMi42MzEtMTkuODU0IDMwLjMwNS0yMC40NTYgMjIuMjY2LTEuMzUxOCA0NS4xNzktMC41MDUwNyA2Ny42OC0wLjUwNTA3IDE2LjE0Ny0wLjYzMjQxIDMuNjEwMiAyMC43MDggMjYuNzY5IDIwLjcwOGwyNDMuNDUtMS4wMTAyIiBjb2xvcj0iIzAwMDAwMCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzM2IiBzdHJva2Utd2lkdGg9IjFweCIvPgogIDx0ZXh0IHg9IjY4NS4yMDgxMyIgeT0iODI3LjUzMDgyIiBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0iVmVyZGFuYSIgbGV0dGVyLXNwYWNpbmc9IjBweCIgd29yZC1zcGFjaW5nPSIwcHgiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjY4NS4yMDgxMyIgeT0iODI3LjUzMDgyIiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+UGF3bmVlPC90c3Bhbj48L3RleHQ+CiAgPHBhdGggdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAyODcuMzYpIiBkPSJtNTU0LjI5IDcyMS40My00LjI4NTctMTc4LjIxLTIuODU3MS00NDAuNzEtMC4zNTcxNC03OS4yODYiIGNvbG9yPSIjMDAwMDAwIiBmaWxsPSJub25lIiBzdHJva2U9IiMzMzYiIHN0cm9rZS13aWR0aD0iMXB4Ii8+CiAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI1MjkuNjI1MzEiIHk9Ii01NTAuODQ3NzgiIGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJWZXJkYW5hIiBsZXR0ZXItc3BhY2luZz0iMHB4IiB3b3JkLXNwYWNpbmc9IjBweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNTI5LjYyNTMxIiB5PSItNTUwLjg0Nzc4IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+QnJvYWR3YXk8L3RzcGFuPjwvdGV4dD4KIDwvZz4KPC9zdmc+Cg==\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}

X Pos: ${xPos:2}
Y Pos: ${yPos:2}
Temperature: ${temperature} °C
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#fe7569\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'thermometer') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAB/CAYAAAD4mHJdAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAACWAAAAlgB7MGOJQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAwgSURBVGiB7Zt5cBT3lce/v18fc89oRoPEIRBCHIUxp2ywCAgIxLExvoidZIFNxXE2VXHirIO3aqtSseM43qpNeZfYKecox3bhpJykYgdjDkU2mBAB5vCamMNYAgQyURBCoxnNPd39O/aP7hGSEUR24L/uqqf+zfR77/Pe69/Rv6kWwcgPLRIJfZUAa7xez2xd90QBwDSNZKlkHJHAK+l09mUA7BP4vPpRUVExMVoRef+L998njxx9X57vPi/PnTsnO850yPaT7XLXrrflqjtWymhF+HA0Gp0wEp/kHymEQqG4ptJDGzf+um5RUxMSiV7Z3Lyt88L5nozgHJWj4pGmpqZav99PWve04onHHuswmViQzWb7ruZX+Udgv8/z3A+f/NGye1evxssvb+wo5PMfTZs6bfqcuXNHL7hlweh58+ZVAOTUpk2b0p9dvjyqqmrs/b8ejpUMc+unzjgUCsXjsYruE+2n1JY/NedM0zCi0VjA7/d7/f4AAgE//H4/vF4fOjvP9h5695C/oaEhcN/q1SyTzVdnMpnklXzTq4EplUsXfmaRCgC7du3cOn78+KfGj59Add3z1Md1vV7vqPa2D1sA4MYbZ6qUiqVX9X21i4TQcfX19QCA6urquN/vn0kAPRQKpYbTnzRpUhgAampqAEFrPjVYSql7fD4AgK5r2tV0AcDj8WkAoOk6JJGeTw2+nocLdsEu2AW7YBfsgl2wC3bBLtgFu2AX7IJdsAt2wS7YBbtgF+yCXbALdsEu2AW7YBfsgl2wC76mh/ppjIQgXVloPxVSBRV0rBe455P6+kTKBYF3tonxY/IWarry7DvI298Tgp0PR9RzACaN1NeIS100+EdvKXW3cMZvF8wCK10Sq2it2NAzakmukP/wmoP/KuId3BRUMg5uCfCSNVSKVn1rNto7Un8jLrUVqJ4Fi2eEQiEYBzOsy3SYL37TNQdzi8Q5FxkqJIQBsNLlYMGF/zqAJWBxSEogDAY+DJibYqTuRg4WFgO3OKhCYTExbKk5G/mbkSPP2DQhLA5IO/NhSz1MMP882BDgnAFQwdiVSs2vPVhYDIJLUMkBgw1favM6lJoZDDAYhKbAYsOX+rqAhcXAuQSIAKzhSy2vS8YmB7NYH4WCfM7kw5VaWtdpOO3bfWZJZVXgPxMX898bVsm6RhkTIseX29yyIErm/J5z5vwr6pvmsLYjBgeDwSpVJS/OmT1n1de+9qANZgLc4q9Dyj2qQhUhSSUAUCL7GBcchCymTEYBYNWqVXj30MGHT586PZEJ+WAul7ts8bjspd9QKDRNU2nz4z94YtI3H3oI+XwB//3j/9m77eRUUJ9/0eh4APGoDz6vCi4ksgUTmYyBC4k8RLGwtzF+EGu+tHqRqqrYtm0rXnzhhQ7G5cpsNnvyiuBIJFKnqvSd55772eilS5fhwIH9ye+/dPaEf1T9otW3T8GtiyYgGNBBymYEgLSbvakidu8/h01vnkYhcab1gcVs5tx5c6PHjh7DU0/9qFsINPb3939UZg28X11dXR0Qwtr9g8efqGtc+Bn89re/O7FhR9BXNaFm+n98uxHTZ1SDKQqKAihweZlITUVtXQwNs8fg+Bmzdk+bnmPdf/7bwsbGeO2ECaED+9/5XCxWuTGbzVpDwJpGNtx+28o77rr7bmzZsu3k7z+cMlHzeiPrvnoTwtVhFAVQHAZY4HBEoiAAeDXUjI/gyJGeQEd6TFj2tHYuXNgYy2azVe0fngiWDLNloHNFo4FZkXDsoTVr1+KD4x8U/3Ci1qP5PV7N74FeFUbClKDEriy57A5JANL5a68hnqoINL8OAPqbXbNp7clTxTVr1/oOHjr0MFXxq2Qy9wEFACnoY//6la9QAHj+9Q/eUL2RWkVXoWgqkhZBypRImkDKBFIWkLIk+h1JWdL+zrmeNCWSDFB0DYquQvWG637TcnozAKxbt45yTr8PAGowGBwVDAbvmT9/Pvbu3dddijV9WdUUUE0BUQm6kwaCYe+ljK/w8ruUdsYCBLlMEUQhoJoCygWM+LIvHTx4sGfevIbqYMD3BSFkJVUUrG5oaFABoPXwhd1UVUBVahtpKtoOnEV/gSHHgBwDso5c6XO6yNF24CNQTbV9qBRUUenuwz1/BoCZM2dplOJeSggWL1myFEII9IeXziIKBVUUW1QKo2Ci41Anei9kkWcY6Ex5R8qfc0wi0ZPF6QNnYeQNB2j7IQpFOtg0WwiBxoWNIBKLVQI6Z8rUqTh69FiWaFNmEIWgLFShoM5TZbIzgVxvFp6ID5rfA6JQgBAIxsGLJkrpAsycAcH4gN1gX0QPTW9vP5Grr58cJJTOpbqmjgWAnp6ei4QSEEJAKAGh1BbHCS2DLAFmMAgmICwObjDnyYMMAtJL9oN89vRc7KWUQtOUsSqhSggA8sWivSEh9qBxTiCEAGRwQARUVaB67Hf5pZAQlA0Ayrq2LTCogVyhlLURNEw55yYABP2+4ED3vHSClBKQ9jiFdHqvEBCMQzAOKYSt6/RqSGnbDPJRbgT93hAAcM4NyhjrBYDKylhswEEZJgYJFxDchnGTwSqasIomuMnsIDiH5GKIzUAQTsCVlZUxB9xLIUVbKpVEff3kiLTMfimEA7HP5bZgHMJ07mnJAiuaYEXT3jcZDMLkTgBD7exgBKRp9NfVTQwnk0kIKduoJGRH8/ZmhMNh4skc3DnEkDlAi4GbtjDDguVAmZM1M6yB68JyKsCGBqD373s7GAySnTt3gBDyFhWCvPHee/8HAJhTU5g0BMg4uMXBTT4AZSUTrGjBKpiwCnablQbDbZuyfTmAuRPMegA4euQopCRbaCaTOd2XSLzX3d2Nu+64bR7PnP3LJSCDMBm4YW9FWcmyQYMytsW+Zpfdsm1MdimAdMc7K29bMedCdzeSyeS76XT6jLNI4PGf/+w5aLqOu25IjOOWKcSg0jJjcLZ2ecsZD5TdybqsOxC0ZYpbJ58frek6nn/+eVBJHgecjXkqk2nu7Ozcdfz4cdx556rJN5C3m8v3jBt2xpdnazjysawNy5lUbKkrbmtZsWL5pGNHj6Or62+7k5lMy5CFNRQKTfN6tAMvvvhSRe3EOqx/4oXXLvia7qO6CsVZrey5154KB5YpKSG5tHs+5/ZsZnEIk6Ei1fLH73373i/09fXi0fWPpgyTLchkMqeGgAEgHA5/vjJWsf2PmzYr1dXV+K8fP7vjLxduWkY8ilpetQZPg+UJxh63lzqlNDi7gTa3fuPraz6bzxXw79/5FutP51am0+kdZdaQ/2kzDKNDUci51179w8pbP3er8sAD6+pnVCWy+/fs21LAqBnlMT50qJXFLq2a2L/5gaVy7N133j69u7sb67/7iFHIFf4tlU6/Ppg1kLGU8hYAywBMeOWV33gfXb9+1Q+ffDL+4Ne/AcYY/tS8PbV5++4Dhy+MopY2ZrLiidQDgDBSp5TS+Y7psS65ZOHsW26++eYosxje2PwGNm586eKzz/x027+sXWsBOAfgbULIQQAgUspaAA8BGAfnsamrq4u0tZ0Q333kkdGmZS3f8JNnlBXLV0AOilRKCS7sWYlxjlKxgHw+j5Y3W/C/Tz/NQ6Hgjp9seKZ31py5ajwe4wAtz9zdAH5OpJTPAqgEgL5USkpu4eLFHloqFXniYh9t3bunauuWrStisSi5//4vYnHTEkyZOhWqokBICcuy0N7ehr2trXjt1VeRzqTl3ffc81bjgsZELF4pQ6EAqa4eI6UEicfj5dhTKoCikynx6Bop5C14dJ2XcjmouipvvGFGoSJaWfr738/7tmzdjl/88pfIZjKwnH2SpmkIhSMYW1ODhvmNGFcztjhudFXR69Wgck58Hg+XEorH5ylDJYA8kVKOckpdB0ADIBOJhOzv70OhUFILuTzPZLNcSE6SfSlvJp0O5A1DN0qGDxLS4/OUAh6PGQqHC5XxeJEQgkgoRH1+L/wBP6LRuIjH4+Uf8gSAUwB+MbhzzQSwCMA0p/QUQADgNJ/PJ/v7+wnnnFiWkJZhKCYzKADoqiZUXeW67iGcSxKPx2QoFAo7AybnuE8COAZgHyHkxGXjeFAQEQCzANQCqAIQBeAH4AXgcex052w45TMcyQHIAOgBcBbAUUJI5uOM/wcaHmf3g9UM7QAAAABJRU5ErkJggg==\",\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false},\"title\":\"Image Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" + }, + "externalId": null, + "tags": [ + "building", + "interior", + "venue", + "inside", + "room", + "office", + "manufacturing", + "floor", + "plant", + "storage", + "warehouse", + "depot" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/individual_allergy_index__iai__card.json b/application/src/main/data/json/system/widget_types/individual_allergy_index__iai__card.json new file mode 100644 index 0000000000..6433cc8f6c --- /dev/null +++ b/application/src/main/data/json/system/widget_types/individual_allergy_index__iai__card.json @@ -0,0 +1,24 @@ +{ + "fqn": "individual_allergy_index_card", + "name": "Individual allergy index (IAI) card", + "deprecated": false, + "image": "tb-image:SUFJLXZhbHVlLWNhcmQuc3Zn:SUFJLXZhbHVlLWNhcmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMTIiIHJ4PSIzLjUiLz48cGF0aCBmaWxsPSIjODBDNDJDIiBkPSJNNDIuODk4IDM4LjMxNGEzLjI3IDMuMjcgMCAwIDAtMy4yNy0zLjI3Yy0uNjkzIDAtMS4zMzQuMjEtMS44My41NzZ2LS4yNDhhMy4yNyAzLjI3IDAgMSAwLTYuNTQgMHYuMjQ4YTMuMDgyIDMuMDgyIDAgMCAwLTEuODMxLS41NzUgMy4yNyAzLjI3IDAgMCAwLTMuMjcgMy4yN2MwIDEuMjk0Ljc3MiAyLjQxOSAxLjg3IDIuOTQyYTMuMjU4IDMuMjU4IDAgMCAwLS45MTIgNS4yNTUgMy4yNyAzLjI3IDAgMCAwIDIuMzEyLjk1N2MuNjkzIDAgMS4zMzQtLjIyMiAxLjgzMS0uNTc1di4yNDhhMy4yNyAzLjI3IDAgMCAwIDYuNTQgMHYtLjI0OGEzLjE3NSAzLjE3NSAwIDAgMCAxLjgzLjU3NSAzLjI3IDMuMjcgMCAwIDAgMy4yNy0zLjI3YzAtMS4zMDctLjc3Mi0yLjQxOS0xLjg3LTIuOTQyYTMuMjczIDMuMjczIDAgMCAwIDEuODctMi45NDNabS04LjM3IDYuMjEzYTMuMjcgMy4yNyAwIDEgMSAwLTYuNTQgMy4yNyAzLjI3IDAgMCAxIDAgNi41NFptLTEuMzA4LTE1LjA0YzAtLjcyLjU4OC0xLjMwOSAxLjMwOC0xLjMwOS43MTkgMCAxLjMwOC41ODkgMS4zMDggMS4zMDggMCAuNzItLjU4OSAxLjMwOC0xLjMwOCAxLjMwOC0uNzIgMC0xLjMwOC0uNTg5LTEuMzA4LTEuMzA4Wm0tNS4yMzIgMi42MTVjMC0uNzIuNTg5LTEuMzA4IDEuMzA4LTEuMzA4LjcyIDAgMS4zMDguNTg4IDEuMzA4IDEuMzA4IDAgLjcyLS41ODggMS4zMDgtMS4zMDggMS4zMDgtLjcyIDAtMS4zMDgtLjU4OS0xLjMwOC0xLjMwOFptLTIuNjE1LTIuNjE2Yy0uNzIgMC0xLjMwOC0uNTg4LTEuMzA4LTEuMzA4IDAtLjcyLjU4OC0xLjMwOCAxLjMwOC0xLjMwOC43MTkgMCAxLjMwNy41ODkgMS4zMDcgMS4zMDggMCAuNzItLjU4OCAxLjMwOC0xLjMwNyAxLjMwOFptMy45MjMtMy45MjRjMC0uNzE5LjU4OS0xLjMwNyAxLjMwOC0xLjMwNy43MiAwIDEuMzA4LjU4OCAxLjMwOCAxLjMwNyAwIC43Mi0uNTg5IDEuMzA4LTEuMzA4IDEuMzA4LS43MiAwLTEuMzA4LS41ODgtMS4zMDgtMS4zMDhabTcuODQ3IDBjMC0uNzE5LjU4OS0xLjMwNyAxLjMwOC0xLjMwNy43MiAwIDEuMzA4LjU4OCAxLjMwOCAxLjMwNyAwIC43Mi0uNTg4IDEuMzA4LTEuMzA4IDEuMzA4LS43MiAwLTEuMzA4LS41ODgtMS4zMDgtMS4zMDhabTcuODQ4IDIuNjE2YzAgLjcyLS41ODkgMS4zMDgtMS4zMDggMS4zMDgtLjcyIDAtMS4zMDgtLjU4OC0xLjMwOC0xLjMwOCAwLS43Mi41ODgtMS4zMDggMS4zMDgtMS4zMDguNzIgMCAxLjMwOC41ODkgMS4zMDggMS4zMDhabS01LjIzMiAyLjYxNmMuNzIgMCAxLjMwOC41ODggMS4zMDggMS4zMDggMCAuNzItLjU4OSAxLjMwOC0xLjMwOCAxLjMwOC0uNzIgMC0xLjMwOC0uNTg5LTEuMzA4LTEuMzA4IDAtLjcyLjU4OS0xLjMwOCAxLjMwOC0xLjMwOFoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii44NyIgZD0iTTU5LjE0IDI1LjU5MXY5LjI0MmgtMS41OTN2LTkuMjQyaDEuNTk0Wm01Ljk0NSAxLjIzMi0yLjc2MiA4LjAxaC0xLjY3bDMuNDgtOS4yNDJoMS4wNjZsLS4xMTQgMS4yMzJabTIuMzEgOC4wMS0yLjc2OC04LjAxLS4xMi0xLjIzMmgxLjA3M2wzLjQ5IDkuMjQyaC0xLjY3NVptLS4xMzMtMy40Mjd2MS4yNjNoLTUuMDI4di0xLjI2M2g1LjAyOFptNC43OTUtNS44MTV2OS4yNDJoLTEuNTk0di05LjI0MmgxLjU5NFoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii4zOCIgZD0iTTYxLjM3NCA1MC45MTh2LjkxNWgtMy4zNjF2LS45MTVoMy4zNlptLTMuMDQtNS43OHY2LjY5NUg1Ny4xOFY0NS4xNGgxLjE1NVptNi44MjIgNS42OTh2LTIuMzczYzAtLjE3OC0uMDMyLS4zMzEtLjA5Ni0uNDZhLjY3Ny42NzcgMCAwIDAtLjI5NC0uMjk5IDEuMDA2IDEuMDA2IDAgMCAwLS40ODgtLjEwNWMtLjE4IDAtLjMzNy4wMy0uNDY5LjA5MmEuNzU4Ljc1OCAwIDAgMC0uMzA4LjI0OC41OTkuNTk5IDAgMCAwLS4xMS4zNTRoLTEuMTA0YzAtLjE5Ni4wNDgtLjM4Ni4xNDMtLjU3LjA5NS0uMTg0LjIzMy0uMzQ4LjQxNC0uNDkyLjE4LS4xNDQuMzk3LS4yNTguNjQ4LS4zNC4yNTEtLjA4My41MzMtLjEyNS44NDYtLjEyNS4zNzQgMCAuNzA1LjA2My45OTMuMTg5LjI5MS4xMjYuNTIuMzE2LjY4NS41Ny4xNjkuMjUxLjI1My41NjcuMjUzLjk0N3YyLjIxMmMwIC4yMjcuMDE1LjQzLjA0Ni42MTEuMDM0LjE3OC4wODEuMzMzLjE0My40NjV2LjA3M2gtMS4xMzZhMS45NjUgMS45NjUgMCAwIDEtLjEyNC0uNDU1IDMuNzI5IDMuNzI5IDAgMCAxLS4wNDEtLjU0MlptLjE2MS0yLjAyOC4wMS42ODVoLS43OTZjLS4yMDUgMC0uMzg2LjAyLS41NDIuMDZhMS4xMTUgMS4xMTUgMCAwIDAtLjM5MS4xNjUuNzE4LjcxOCAwIDAgMC0uMzEzLjYyMWMwIC4xMzIuMDMuMjUzLjA5Mi4zNjMuMDYxLjEwOC4xNS4xOTIuMjY3LjI1My4xMi4wNjEuMjYzLjA5Mi40MzIuMDkyLjIyNyAwIC40MjUtLjA0Ni41OTMtLjEzOGExLjIyIDEuMjIgMCAwIDAgLjQwNS0uMzQ1Ljc1Ljc1IDAgMCAwIC4xNTYtLjM5bC4zNTkuNDkxYy0uMDM3LjEyNi0uMS4yNjEtLjE4OS40MDVhMS45NTkgMS45NTkgMCAwIDEtLjM1LjQxNCAxLjczNSAxLjczNSAwIDAgMS0xLjE5LjQ0MWMtLjMyNSAwLS42MTUtLjA2NC0uODctLjE5M2ExLjU0NiAxLjU0NiAwIDAgMS0uNTk3LS41MjkgMS4zNzEgMS4zNzEgMCAwIDEtLjIxNi0uNzU4YzAtLjI2NC4wNDktLjQ5Ny4xNDctLjdhMS4zNyAxLjM3IDAgMCAxIC40NDItLjUxNGMuMTk2LS4xMzguNDM1LS4yNDIuNzE3LS4zMTMuMjgyLS4wNzMuNjA0LS4xMS45NjUtLjExaC44N1ptNS4wOTggMS42NzhjMC0uMTEtLjAyOC0uMjEtLjA4My0uMjk5LS4wNTUtLjA5Mi0uMTYtLjE3NC0uMzE3LS4yNDhhMy4wNzggMy4wNzggMCAwIDAtLjY4LS4yMDIgNS44NjQgNS44NjQgMCAwIDEtLjcyNy0uMjA3IDIuMzA1IDIuMzA1IDAgMCAxLS41Ni0uMyAxLjI1OSAxLjI1OSAwIDAgMS0uMzY0LS40MTMgMS4xNDYgMS4xNDYgMCAwIDEtLjEyOS0uNTUyYzAtLjIwMi4wNDQtLjM5NC4xMzMtLjU3NS4wOS0uMTguMjE2LS4zNC4zODItLjQ3OC4xNjYtLjEzOC4zNjYtLjI0Ni42MDItLjMyNi4yNC0uMDguNTA2LS4xMi44LS4xMi40MTcgMCAuNzc0LjA3IDEuMDcyLjIxMi4zLjEzOC41My4zMjYuNjkuNTY1LjE1OS4yMzYuMjM5LjUwMy4yMzkuOGgtMS4xMDlhLjcwNy43MDcgMCAwIDAtLjEtLjM2Ny43MDUuNzA1IDAgMCAwLS4yOTUtLjI4IDEuMDEgMS4wMSAwIDAgMC0uNDk3LS4xMTFjLS4xOSAwLS4zNDcuMDMtLjQ3My4wOTJhLjY0OC42NDggMCAwIDAtLjI3Ni4yMy41ODcuNTg3IDAgMCAwLS4wNDEuNTM4LjUzLjUzIDAgMCAwIC4xNjUuMTc5Yy4wNzcuMDUyLjE4MS4xMDEuMzEzLjE0Ny4xMzUuMDQ2LjMwMy4wOS41MDYuMTMzLjM4LjA4LjcwNi4xODMuOTc5LjMwOC4yNzYuMTIzLjQ4Ny4yODIuNjM0LjQ3OS4xNDguMTkzLjIyMS40MzguMjIxLjczNSAwIC4yMjEtLjA0Ny40MjMtLjE0Mi42MDctLjA5Mi4xODEtLjIyNy4zMzktLjQwNS40NzRhMi4wMyAyLjAzIDAgMCAxLS42NC4zMDhjLS4yNDQuMDc0LS41Mi4xMS0uODI3LjExLS40NSAwLS44MzItLjA4LTEuMTQ1LS4yMzlhMS44MjYgMS44MjYgMCAwIDEtLjcxMi0uNjIgMS40NyAxLjQ3IDAgMCAxLS4yNC0uNzkxaDEuMDcyYS44My44MyAwIDAgMCAuMTcuNDkyYy4xMDQuMTIuMjMzLjIwNi4zODYuMjYyLjE1Ny4wNTIuMzE4LjA3OC40ODMuMDc4LjIgMCAuMzY2LS4wMjYuNTAxLS4wNzhhLjcyMS43MjEgMCAwIDAgLjMwOC0uMjIxLjUyNy41MjcgMCAwIDAgLjEwNi0uMzIyWm00LjUzMi0zLjYyOHYuODFoLTIuODA1di0uODFoMi44MDVabS0xLjk5NS0xLjIxOGgxLjEwOHY0LjgxOWMwIC4xNTMuMDIxLjI3LjA2NC4zNTQuMDQ2LjA4LjEwOS4xMzMuMTg5LjE2LjA4LjAyOC4xNzMuMDQyLjI4LjA0MmEyLjIwOSAyLjIwOSAwIDAgMCAuMzktLjA0MWwuMDA1Ljg0NmMtLjA5MS4wMjctLjE5OS4wNTItLjMyMS4wNzMtLjEyLjAyMS0uMjU4LjAzMi0uNDE0LjAzMi0uMjU1IDAtLjQ4LS4wNDQtLjY3Ni0uMTMzYS45OTUuOTk1IDAgMCAxLS40Ni0uNDQ2Yy0uMTEtLjIwNS0uMTY1LS40NzgtLjE2NS0uODE4VjQ1LjY0Wm04LjU4IDUuMDJ2LTMuODAyaDEuMTE0djQuOTc1aC0xLjA0OWwtLjA2NC0xLjE3MlptLjE1Ny0xLjAzNC4zNzMtLjAwOWMwIC4zMzQtLjAzNy42NDItLjExLjkyNGEyLjE0MyAyLjE0MyAwIDAgMS0uMzQuNzMxYy0uMTU0LjIwNi0uMzUuMzY3LS41OS40ODNhMS45OSAxLjk5IDAgMCAxLS44Ni4xN2MtLjI0MSAwLS40NjQtLjAzNS0uNjY2LS4xMDVhMS4zNjcgMS4zNjcgMCAwIDEtLjUyNC0uMzI3IDEuNDg2IDEuNDg2IDAgMCAxLS4zMzYtLjU3NSAyLjY1NSAyLjY1NSAwIDAgMS0uMTItLjg0NnYtMy4yMTRoMS4xMDl2My4yMjRjMCAuMTguMDIxLjMzMi4wNjQuNDU1YS43Ny43NyAwIDAgMCAuMTc1LjI5Yy4wNzMuMDczLjE2LjEyNS4yNTcuMTU2LjA5OS4wMy4yMDMuMDQ2LjMxMy4wNDYuMzE2IDAgLjU2NC0uMDYyLjc0NS0uMTg0LjE4NC0uMTI2LjMxNC0uMjk0LjM5LS41MDYuMDgtLjIxMi4xMi0uNDUuMTItLjcxM1ptMy40MDEtMS44MTF2NS45MzFoLTEuMTA4di02Ljg4OGgxLjAybC4wODguOTU3Wm0zLjI0MiAxLjQ4NXYuMDk2YzAgLjM2Mi0uMDQzLjY5OC0uMTI5IDEuMDA3YTIuNDYgMi40NiAwIDAgMS0uMzcyLjgwNSAxLjcxNSAxLjcxNSAwIDAgMS0uNjAzLjUyOWMtLjIzOS4xMjUtLjUxNS4xODgtLjgyNy4xODgtLjMxIDAtLjU4MS0uMDU2LS44MTQtLjE3YTEuNjY3IDEuNjY3IDAgMCAxLS41ODQtLjQ5MiAyLjY2OSAyLjY2OSAwIDAgMS0uMzg2LS43NDUgNC43OCA0Ljc4IDAgMCAxLS4yMDMtLjk0N1Y0OS4yYy4wNC0uMzY1LjEwOC0uNjk2LjIwMy0uOTkzYTIuNjkgMi42OSAwIDAgMSAuMzg2LS43NjhjLjE2LS4yMTUuMzU0LS4zOC41ODQtLjQ5Ny4yMy0uMTE2LjQ5OC0uMTc1LjgwNC0uMTc1LjMxMyAwIC41OS4wNjIuODMzLjE4NC4yNDIuMTIuNDQ2LjI5MS42MTEuNTE1LjE2Ni4yMi4yOS40ODguMzczLjguMDgyLjMxLjEyNC42NTUuMTI0IDEuMDM1Wm0tMS4xMDguMDk2VjQ5LjNjMC0uMjMtLjAyMi0uNDQzLS4wNjUtLjY0LS4wNDMtLjE5OC0uMTEtLjM3My0uMjAyLS41MjNhLjk5Ljk5IDAgMCAwLS4zNTQtLjM1Ljk2NC45NjQgMCAwIDAtLjUxLS4xMjljLS4xOTcgMC0uMzY1LjAzNC0uNTA2LjEwMmEuOTcuOTcgMCAwIDAtLjM1NC4yNyAxLjI4MiAxLjI4MiAwIDAgMC0uMjIuNDFjLS4wNTMuMTUzLS4wOS4zMi0uMTExLjUwMXYuODkyYy4wMzcuMjIuMS40MjMuMTg4LjYwNy4wOS4xODQuMjE1LjMzMS4zNzcuNDQyLjE2Ni4xMDcuMzc3LjE2LjYzNS4xNi4xOTkgMCAuMzctLjA0My41MS0uMTI4LjE0MS0uMDg2LjI1Ni0uMjA0LjM0NS0uMzU0LjA5Mi0uMTU0LjE2LS4zMy4yMDItLjUzLjA0My0uMTk4LjA2NS0uNDEuMDY1LS42MzRabTUuMjgxIDEuNDA3di02LjAzMmgxLjExM3Y3LjA2MmgtMS4wMDdsLS4xMDYtMS4wM1ptLTMuMjM3LTEuNDAydi0uMDk3YzAtLjM3Ny4wNDUtLjcyLjEzNC0xLjAzLjA4OC0uMzEyLjIxNy0uNTguMzg2LS44MDQuMTY4LS4yMjcuMzc0LS40LjYxNi0uNTIuMjQyLS4xMjIuNTE1LS4xODQuODE4LS4xODQuMyAwIC41NjQuMDU5Ljc5MS4xNzUuMjI3LjExNi40Mi4yODQuNTguNTAxYTIuNiAyLjYgMCAwIDEgLjM4MS43NzNjLjA5NS4yOTcuMTYzLjYyOC4yMDMuOTkzdi4zMDhjLS4wNC4zNTUtLjEwOC42OC0uMjAzLjk3NWEyLjYyIDIuNjIgMCAwIDEtLjM4MS43NjNjLS4xNi4yMTUtLjM1NC4zOC0uNTg0LjQ5N2ExLjcxOSAxLjcxOSAwIDAgMS0uNzk2LjE3NGMtLjMgMC0uNTcyLS4wNjMtLjgxNC0uMTg4YTEuNzk5IDEuNzk5IDAgMCAxLS42MTEtLjUyOSAyLjQ4OSAyLjQ4OSAwIDAgMS0uMzg2LS44IDMuNjM2IDMuNjM2IDAgMCAxLS4xMzQtMS4wMDdabTEuMTA4LS4wOTd2LjA5N2MwIC4yMjcuMDIuNDM4LjA2LjYzNC4wNDMuMTk3LjEwOS4zNy4xOTguNTIuMDg5LjE0Ny4yMDQuMjY0LjM0NS4zNS4xNDQuMDgyLjMxNi4xMjQuNTE1LjEyNC4yNTEgMCAuNDU4LS4wNTYuNjItLjE2Ni4xNjMtLjExLjI5LS4yNTkuMzgyLS40NDYuMDk1LS4xOS4xNi0uNDAxLjE5My0uNjM0di0uODMzYTIuMDMgMi4wMyAwIDAgMC0uMTE1LS41MDUgMS4zNSAxLjM1IDAgMCAwLS4yMjUtLjQxLjk0OC45NDggMCAwIDAtLjM1NC0uMjc1IDEuMTEgMS4xMSAwIDAgMC0uNDkyLS4xMDIuOTczLjk3MyAwIDAgMC0uNTE1LjEzIDEuMDQzIDEuMDQzIDAgMCAwLS4zNS4zNTNjLS4wODguMTUtLjE1NC4zMjUtLjE5Ny41MjQtLjA0My4yLS4wNjUuNDEzLS4wNjUuNjRabTcuNDAyIDEuNTMydi0yLjM3M2MwLS4xNzgtLjAzMy0uMzMxLS4wOTctLjQ2YS42NzcuNjc3IDAgMCAwLS4yOTQtLjI5OSAxLjAwNyAxLjAwNyAwIDAgMC0uNDg4LS4xMDVjLS4xOCAwLS4zMzcuMDMtLjQ2OS4wOTJhLjc1OS43NTkgMCAwIDAtLjMwOC4yNDguNi42IDAgMCAwLS4xMS4zNTRoLTEuMTAzYzAtLjE5Ni4wNDctLjM4Ni4xNDItLjU3LjA5NS0uMTg0LjIzMy0uMzQ4LjQxNC0uNDkyLjE4LS4xNDQuMzk3LS4yNTguNjQ4LS4zNC4yNTItLjA4My41MzQtLjEyNS44NDYtLjEyNS4zNzQgMCAuNzA1LjA2My45OTMuMTg5LjI5MS4xMjYuNTIuMzE2LjY4NS41Ny4xNjkuMjUxLjI1My41NjcuMjUzLjk0N3YyLjIxMmMwIC4yMjcuMDE2LjQzLjA0Ni42MTEuMDM0LjE3OC4wODEuMzMzLjE0My40NjV2LjA3M2gtMS4xMzZhMS45NiAxLjk2IDAgMCAxLS4xMjQtLjQ1NSAzLjcxNSAzLjcxNSAwIDAgMS0uMDQxLS41NDJabS4xNi0yLjAyOC4wMS42ODVoLS43OTZjLS4yMDUgMC0uMzg2LjAyLS41NDIuMDZhMS4xMTYgMS4xMTYgMCAwIDAtLjM5MS4xNjUuNzIuNzIgMCAwIDAtLjMxMy42MjFjMCAuMTMyLjAzLjI1My4wOTIuMzYzLjA2MS4xMDguMTUuMTkyLjI2Ny4yNTMuMTIuMDYxLjI2My4wOTIuNDMyLjA5Mi4yMjcgMCAuNDI1LS4wNDYuNTkzLS4xMzguMTcyLS4wOTUuMzA3LS4yMS40MDUtLjM0NWEuNzUuNzUgMCAwIDAgLjE1Ni0uMzlsLjM1OS40OTFjLS4wMzcuMTI2LS4xLjI2MS0uMTg5LjQwNWExLjk2MSAxLjk2MSAwIDAgMS0uMzUuNDE0IDEuNzM2IDEuNzM2IDAgMCAxLTEuMTkuNDQxYy0uMzI1IDAtLjYxNS0uMDY0LS44NjktLjE5M2ExLjU0NiAxLjU0NiAwIDAgMS0uNTk4LS41MjkgMS4zNzIgMS4zNzIgMCAwIDEtLjIxNi0uNzU4YzAtLjI2NC4wNS0uNDk3LjE0Ny0uNy4xMDEtLjIwNC4yNDktLjM3Ni40NDItLjUxNC4xOTYtLjEzOC40MzUtLjI0Mi43MTctLjMxMy4yODItLjA3My42MDQtLjExLjk2Ni0uMTFoLjg2OVptNC41ODgtMS45NXYuODFoLTIuODA1di0uODFoMi44MDVabS0xLjk5Ni0xLjIxOGgxLjEwOHY0LjgxOWMwIC4xNTMuMDIyLjI3LjA2NS4zNTQuMDQ2LjA4LjEwOS4xMzMuMTg4LjE2YS44Ni44NiAwIDAgMCAuMjgxLjA0MiAyLjIxIDIuMjEgMCAwIDAgLjM5MS0uMDQxbC4wMDQuODQ2YTIuODIgMi44MiAwIDAgMS0uMzIyLjA3MyAyLjM2MiAyLjM2MiAwIDAgMS0uNDE0LjAzMmMtLjI1NCAwLS40NzktLjA0NC0uNjc1LS4xMzNhLjk5Ljk5IDAgMCAxLS40Ni0uNDQ2Yy0uMTExLS4yMDUtLjE2Ni0uNDc4LS4xNjYtLjgxOFY0NS42NFptNS4yNCA2LjI4NWMtLjM2NyAwLS43LS4wNi0uOTk3LS4xNzlhMi4xOTMgMi4xOTMgMCAwIDEtLjc1NC0uNTEgMi4yNDkgMi4yNDkgMCAwIDEtLjQ3NC0uNzY4IDIuNjk3IDIuNjk3IDAgMCAxLS4xNjYtLjk1MnYtLjE4NGMwLS4zOS4wNTctLjc0Mi4xNzEtMS4wNTcuMTEzLS4zMTYuMjcxLS41ODYuNDczLS44MS4yMDMtLjIyNy40NDItLjQuNzE3LS41Mi4yNzYtLjExOS41NzUtLjE3OS44OTctLjE3OS4zNTYgMCAuNjY3LjA2LjkzMy4xOC4yNjcuMTIuNDg4LjI4OC42NjMuNTA1LjE3Ny4yMTUuMzA5LjQ3MS4zOTUuNzY4LjA4OS4yOTguMTMzLjYyNi4xMzMuOTg0di40NzRoLTMuODQ0di0uNzk2aDIuNzV2LS4wODdhMS41NDQgMS41NDQgMCAwIDAtLjEyLS41Ni45NS45NSAwIDAgMC0uMzI2LS40MjRjLS4xNDctLjEwNy0uMzQzLS4xNi0uNTg5LS4xNmEuOTk4Ljk5OCAwIDAgMC0uNDkxLjExOS45NzUuOTc1IDAgMCAwLS4zNTUuMzM1IDEuNzY0IDEuNzY0IDAgMCAwLS4yMi41MzQgMi45OTkgMi45OTkgMCAwIDAtLjA3NC42OTR2LjE4NGMwIC4yMTguMDI5LjQyLjA4OC42MDcuMDYxLjE4NC4xNS4zNDUuMjY2LjQ4My4xMTcuMTM4LjI1OC4yNDYuNDIzLjMyNi4xNjYuMDc3LjM1NC4xMTUuNTY2LjExNS4yNjYgMCAuNTA0LS4wNTQuNzEyLS4xNi4yMDktLjEwOC4zOS0uMjYuNTQzLS40NTZsLjU4NC41NjZhMi4yODcgMi4yODcgMCAwIDEtLjQxOC40NSAyLjEwMyAyLjEwMyAwIDAgMS0uNjMuMzQ1IDIuNTEzIDIuNTEzIDAgMCAxLS44NTYuMTMzWm04LjU1NC02LjgxdjYuNzE4aC0xLjEwOHYtNS40MDJsLTEuNjQxLjU1NnYtLjkxNWwyLjYxNi0uOTU2aC4xMzNabTUuNjIyIDUuNjg4di02LjAzMmgxLjExM3Y3LjA2MmgtMS4wMDdsLS4xMDYtMS4wM1ptLTMuMjM3LTEuNDAydi0uMDk3YzAtLjM3Ny4wNDQtLjcyLjEzMy0xLjAzYTIuNDIgMi40MiAwIDAgMSAuMzg3LS44MDRjLjE2OC0uMjI3LjM3NC0uNC42MTYtLjUyLjI0Mi0uMTIyLjUxNS0uMTg0LjgxOC0uMTg0LjMwMSAwIC41NjQuMDU5Ljc5MS4xNzUuMjI3LjExNi40Mi4yODQuNTc5LjUwMS4xNi4yMTUuMjg3LjQ3Mi4zODIuNzczLjA5NS4yOTcuMTYyLjYyOC4yMDIuOTkzdi4zMDhjLS4wNC4zNTUtLjEwNy42OC0uMjAyLjk3NWEyLjYwMyAyLjYwMyAwIDAgMS0uMzgyLjc2M2MtLjE1OS4yMTUtLjM1NC4zOC0uNTg0LjQ5N2ExLjcxNiAxLjcxNiAwIDAgMS0uNzk1LjE3NGMtLjMgMC0uNTcyLS4wNjMtLjgxNC0uMTg4YTEuNzk0IDEuNzk0IDAgMCAxLS42MTEtLjUyOSAyLjQ4NCAyLjQ4NCAwIDAgMS0uMzg3LS44IDMuNjMyIDMuNjMyIDAgMCAxLS4xMzMtMS4wMDdabTEuMTA4LS4wOTd2LjA5N2MwIC4yMjcuMDIuNDM4LjA2LjYzNC4wNDMuMTk3LjEwOS4zNy4xOTguNTIuMDg5LjE0Ny4yMDMuMjY0LjM0NC4zNS4xNDUuMDgyLjMxNi4xMjQuNTE1LjEyNC4yNTIgMCAuNDU5LS4wNTYuNjIxLS4xNjYuMTYzLS4xMS4yOS0uMjU5LjM4Mi0uNDQ2YTIuMDggMi4wOCAwIDAgMCAuMTkzLS42MzR2LS44MzNhMi4wMjUgMi4wMjUgMCAwIDAtLjExNS0uNTA1IDEuMzUgMS4zNSAwIDAgMC0uMjI1LS40MS45NDguOTQ4IDAgMCAwLS4zNTQtLjI3NSAxLjExIDEuMTEgMCAwIDAtLjQ5Mi0uMTAyLjk3MS45NzEgMCAwIDAtLjUxNS4xMyAxLjA0NiAxLjA0NiAwIDAgMC0uMzUuMzUzYy0uMDg5LjE1LS4xNTUuMzI1LS4xOTcuNTI0LS4wNDMuMi0uMDY1LjQxMy0uMDY1LjY0Wm05Ljk0MyAxLjUzMnYtMi4zNzNjMC0uMTc4LS4wMzMtLjMzMS0uMDk3LS40NmEuNjc4LjY3OCAwIDAgMC0uMjk0LS4yOTkgMS4wMSAxLjAxIDAgMCAwLS40ODgtLjEwNWMtLjE4MSAwLS4zMzcuMDMtLjQ2OS4wOTJhLjc2Ljc2IDAgMCAwLS4zMDguMjQ4LjYwMy42MDMgMCAwIDAtLjExLjM1NGgtMS4xMDRjMC0uMTk2LjA0OC0uMzg2LjE0My0uNTcuMDk1LS4xODQuMjMzLS4zNDguNDE0LS40OTJhMi4wNyAyLjA3IDAgMCAxIC42NDgtLjM0Yy4yNTEtLjA4My41MzMtLjEyNS44NDYtLjEyNS4zNzQgMCAuNzA1LjA2My45OTMuMTg5LjI5MS4xMjYuNTIuMzE2LjY4NS41Ny4xNjkuMjUxLjI1My41NjcuMjUzLjk0N3YyLjIxMmMwIC4yMjcuMDE2LjQzLjA0Ni42MTEuMDM0LjE3OC4wODEuMzMzLjE0My40NjV2LjA3M2gtMS4xMzZhMS45ODYgMS45ODYgMCAwIDEtLjEyNC0uNDU1IDMuNjU5IDMuNjU5IDAgMCAxLS4wNDEtLjU0MlptLjE2LTIuMDI4LjAxLjY4NWgtLjc5NmMtLjIwNSAwLS4zODYuMDItLjU0Mi4wNmExLjExIDEuMTEgMCAwIDAtLjM5MS4xNjUuNzIyLjcyMiAwIDAgMC0uMjM1LjI2Ny43ODIuNzgyIDAgMCAwLS4wNzguMzU0YzAgLjEzMi4wMzEuMjUzLjA5Mi4zNjNhLjY0LjY0IDAgMCAwIC4yNjcuMjUzLjkzNC45MzQgMCAwIDAgLjQzMi4wOTIgMS4yMTcgMS4yMTcgMCAwIDAgLjk5OC0uNDgzLjc1Ljc1IDAgMCAwIC4xNTYtLjM5bC4zNTkuNDkxYy0uMDM3LjEyNi0uMS4yNjEtLjE4OS40MDVhMS45NjIgMS45NjIgMCAwIDEtLjM0OS40MTQgMS43MyAxLjczIDAgMCAxLTEuMTkxLjQ0MWMtLjMyNSAwLS42MTUtLjA2NC0uODY5LS4xOTNhMS41NDIgMS41NDIgMCAwIDEtLjU5OC0uNTI5IDEuMzcyIDEuMzcyIDAgMCAxLS4yMTYtLjc1OGMwLS4yNjQuMDQ5LS40OTcuMTQ3LS43LjEwMS0uMjA0LjI0OC0uMzc2LjQ0Mi0uNTE0LjE5Ni0uMTM4LjQzNS0uMjQyLjcxNy0uMzEzLjI4Mi0uMDczLjYwNC0uMTEuOTY1LS4xMWguODY5Wm01LjQ5NC0xLjk1aDEuMDA2djQuODM3YzAgLjQ0OC0uMDk1LjgyOC0uMjg1IDEuMTQtLjE5LjMxMy0uNDU1LjU1LS43OTUuNzEzYTIuNzcxIDIuNzcxIDAgMCAxLTIuNDg4LS4xMDEgMS42OCAxLjY4IDAgMCAxLS41MzgtLjQ3M2wuNTItLjY1M2MuMTc4LjIxMS4zNzQuMzY2LjU4OS40NjQuMjE0LjA5OC40MzkuMTQ3LjY3NS4xNDcuMjU1IDAgLjQ3MS0uMDQ3LjY0OS0uMTQyYS45NjEuOTYxIDAgMCAwIC40MTgtLjQxYy4wOTgtLjE4LjE0Ny0uNDAxLjE0Ny0uNjYydi0zLjczM2wuMTAyLTEuMTI3Wm0tMy4zOCAyLjU0M3YtLjA5N2MwLS4zNzcuMDQ2LS43Mi4xMzgtMS4wM2EyLjQ1IDIuNDUgMCAwIDEgLjM5NS0uODA0Yy4xNzItLjIyNy4zOC0uNC42MjYtLjUyLjI0NS0uMTIyLjUyMi0uMTg0LjgzMi0uMTg0LjMyMiAwIC41OTYuMDU5LjgyMy4xNzUuMjMuMTE2LjQyMS4yODQuNTc1LjUwMS4xNTMuMjE1LjI3My40NzIuMzU4Ljc3My4wODkuMjk3LjE1NS42MjguMTk4Ljk5M3YuMzA4Yy0uMDQuMzU1LS4xMDcuNjgtLjIwMi45NzVhMi42OTcgMi42OTcgMCAwIDEtLjM3Ny43NjNjLS4xNTcuMjE1LS4zNS4zOC0uNTguNDk3YTEuNzM3IDEuNzM3IDAgMCAxLS44MDQuMTc0Yy0uMzA0IDAtLjU3OC0uMDYzLS44MjMtLjE4OGExLjg4IDEuODggMCAwIDEtLjYyNi0uNTI5IDIuNTE3IDIuNTE3IDAgMCAxLS4zOTUtLjggMy41MiAzLjUyIDAgMCAxLS4xMzgtMS4wMDdabTEuMTA4LS4wOTd2LjA5N2MwIC4yMjcuMDIyLjQzOC4wNjQuNjM0LjA0Ni4xOTcuMTE1LjM3LjIwNy41Mi4wOTUuMTQ3LjIxNS4yNjQuMzU5LjM1LjE0Ny4wODIuMzIuMTI0LjUyLjEyNC4yNiAwIC40NzMtLjA1Ni42MzktLjE2Ni4xNjgtLjExLjI5Ny0uMjU5LjM4Ni0uNDQ2LjA5Mi0uMTkuMTU2LS40MDEuMTkzLS42MzR2LS44MzNhMi4wMjUgMi4wMjUgMCAwIDAtLjExNS0uNTA1IDEuMzUgMS4zNSAwIDAgMC0uMjI1LS40MS45NDIuOTQyIDAgMCAwLS4zNTktLjI3NSAxLjE5MyAxLjE5MyAwIDAgMC0uNTEtLjEwMmMtLjE5OSAwLS4zNzMuMDQzLS41Mi4xM2ExLjA1NSAxLjA1NSAwIDAgMC0uMzYzLjM1MyAxLjc1IDEuNzUgMCAwIDAtLjIwNy41MjRjLS4wNDYuMi0uMDY5LjQxMy0uMDY5LjY0Wm00LjQxMy4wOTd2LS4xMDZjMC0uMzU4LjA1Mi0uNjkxLjE1Ni0uOTk4LjEwNC0uMzEuMjU0LS41NzcuNDUxLS44MDRhMi4wMyAyLjAzIDAgMCAxIC43MjYtLjUzNGMuMjg4LS4xMjguNjEzLS4xOTMuOTc1LS4xOTMuMzY1IDAgLjY5LjA2NS45NzUuMTk0LjI4OC4xMjUuNTMxLjMwMy43MzEuNTMzLjE5OS4yMjcuMzUxLjQ5NS40NTUuODA0LjEwNC4zMDcuMTU2LjY0LjE1Ni45OTh2LjEwNmMwIC4zNTktLjA1Mi42OTEtLjE1Ni45OThhMi4zODcgMi4zODcgMCAwIDEtLjQ1NS44MDQgMi4xMiAyLjEyIDAgMCAxLS43MjcuNTM0IDIuMzgxIDIuMzgxIDAgMCAxLS45Ny4xODhjLS4zNjUgMC0uNjkxLS4wNjMtLjk3OS0uMTg4YTIuMTEgMi4xMSAwIDAgMS0uNzI3LS41MzQgMi4zODcgMi4zODcgMCAwIDEtLjQ1NS0uODA0IDMuMDg2IDMuMDg2IDAgMCAxLS4xNTYtLjk5OFptMS4xMDgtLjEwNnYuMTA2YzAgLjIyNC4wMjMuNDM1LjA2OS42MzQuMDQ2LjIuMTE4LjM3NS4yMTYuNTI1LjA5OC4xNS4yMjQuMjY4LjM3Ny4zNTQuMTUzLjA4NS4zMzYuMTI4LjU0Ny4xMjguMjA1IDAgLjM4My0uMDQzLjUzMy0uMTI4LjE1NC0uMDg2LjI3OS0uMjA0LjM3Ny0uMzU0LjA5OC0uMTUuMTcxLS4zMjUuMjE3LS41MjUuMDQ5LS4xOTkuMDczLS40MS4wNzMtLjYzNHYtLjEwNmMwLS4yMi0uMDI0LS40MjktLjA3My0uNjI1LS4wNDYtLjItLjEyLS4zNzYtLjIyMS0uNTI5YTEuMDU3IDEuMDU3IDAgMCAwLS45MTUtLjQ5MmMtLjIwOSAwLS4zODkuMDQ1LS41NDMuMTMzLS4xNS4wODYtLjI3NC4yMDYtLjM3Mi4zNi0uMDk4LjE1Mi0uMTcuMzI5LS4yMTYuNTI4YTIuNzMzIDIuNzMzIDAgMCAwLS4wNjkuNjI1WiIvPjxwYXRoIGZpbGw9IiM4MEM0MkMiIGQ9Im05NC40NCA5OC42NDQtNC44NzYtMS4xOTQgMi4wMDYtMTguNTg1aDE5LjkzMnY1LjE4SDk2LjY0OGwtMS4wMTUgOS4wNjRjLjU3NS0uMzM5IDEuMzctLjY2OSAyLjM4Ni0uOTkgMS4wMTYtLjM0IDIuMTc2LS41MDggMy40NzktLjUwOCAxLjc0MyAwIDMuMzAxLjI4NyA0LjY3Mi44NjNhOS4zMDcgOS4zMDcgMCAwIDEgMy41MjkgMi40NjNjLjk2NSAxLjA2NiAxLjcwMSAyLjM3IDIuMjA5IDMuOTEuNTA4IDEuNTIzLjc2MiAzLjI0Mi43NjIgNS4xNTQgMCAxLjcxLS4yNTQgMy4zMS0uNzYyIDQuNzk5YTExLjE4NyAxMS4xODcgMCAwIDEtMi4yMzQgMy45MzZjLS45OTkgMS4xMzQtMi4yNiAyLjAyMi0zLjc4MyAyLjY2Ni0xLjUwNy42MjYtMy4yOTMuOTM5LTUuMzU4LjkzOS0xLjU0IDAtMy4wMjEtLjIyLTQuNDQzLS42NmExMi4xMTMgMTIuMTEzIDAgMCAxLTMuNzgzLTIuMDA2IDEwLjIxNyAxMC4yMTcgMCAwIDEtMi42OTItMy4zMDFjLS42NzctMS4zMi0xLjA2Ni0yLjgzNS0xLjE2OC00LjU0NWg1Ljk5MmMuMTUzIDEuMjAyLjQ4MyAyLjIyNi45OSAzLjA3M2E1LjI0NCA1LjI0NCAwIDAgMCAyLjA4MyAxLjkwNGMuODYzLjQ0IDEuODYyLjY2IDIuOTk2LjY2IDEuMDMyIDAgMS45MjEtLjE3OCAyLjY2Ni0uNTMzYTUuMDM5IDUuMDM5IDAgMCAwIDEuODUzLTEuNTc0Yy41MDgtLjY5NC44OC0xLjUwNyAxLjExNy0yLjQzOC4yNTQtLjkzMS4zODEtMS45NjMuMzgxLTMuMDk3IDAtMS4wODQtLjE0NC0yLjA3NC0uNDMxLTIuOTcxYTYuNDY3IDYuNDY3IDAgMCAwLTEuMjQ0LTIuMzM2IDUuMzMxIDUuMzMxIDAgMCAwLTIuMDU3LTEuNTIzYy0uODMtLjM3My0xLjc4Ni0uNTYtMi44Ny0uNTYtMS40NTUgMC0yLjU3Mi4yMTMtMy4zNS42MzZhMTAuNTEgMTAuNTEgMCAwIDAtMi4xMzQgMS41NzRaIi8+PC9zdmc+", + "description": "Indicates the concentration of airborne allergens, including pollen and mold spores, which can trigger allergic reactions in sensitive individuals.", + "descriptor": { + "type": "latest", + "sizeX": 3.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'IAI_level', label: 'IAI', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"IAI\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 12) {\\n\\tvalue = 12;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:flower-pollen\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#3FA71A\"},{\"from\":2,\"to\":6,\"color\":\"#80C32C\"},{\"from\":6,\"to\":9,\"color\":\"#F36900\"},{\"from\":9,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#3FA71A\"},{\"from\":2,\"to\":6,\"color\":\"#80C32C\"},{\"from\":6,\"to\":9,\"color\":\"#F36900\"},{\"from\":9,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Individual allergy index card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/individual_allergy_index__iai__card_with_background.json b/application/src/main/data/json/system/widget_types/individual_allergy_index__iai__card_with_background.json new file mode 100644 index 0000000000..6b858f5f1f --- /dev/null +++ b/application/src/main/data/json/system/widget_types/individual_allergy_index__iai__card_with_background.json @@ -0,0 +1,24 @@ +{ + "fqn": "individual_allergy_index_card_with_background", + "name": "Individual allergy index (IAI) card with background", + "deprecated": false, + "image": "tb-image:SUFJLXZhbHVlLWNhcmQtd2l0aC1iYWNrZ3JvdW5kLnN2Zw==:SUFJLXZhbHVlLWNhcmQtd2l0aC1iYWNrZ3JvdW5kLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuNzYiIHJ4PSI0Ii8+PC9nPjxwYXRoIGZpbGw9IiM3Q0MzMjIiIGQ9Ik0zNSAzMC45MzhhMy4xMjUgMy4xMjUgMCAwIDAtMy4xMjUtMy4xMjVjLS42NjMgMC0xLjI3NS4yLTEuNzUuNTV2LS4yMzhhMy4xMjUgMy4xMjUgMCAxIDAtNi4yNSAwdi4yMzhhMi45NDYgMi45NDYgMCAwIDAtMS43NS0uNTVBMy4xMjUgMy4xMjUgMCAwIDAgMTkgMzAuOTM4YzAgMS4yMzcuNzM4IDIuMzEyIDEuNzg4IDIuODEyYTMuMTE0IDMuMTE0IDAgMCAwLS44NzMgNS4wMjJjLjU4Ni41ODYgMS4zODEuOTE2IDIuMjEuOTE2LjY2MyAwIDEuMjc1LS4yMTMgMS43NS0uNTV2LjIzN2EzLjEyNSAzLjEyNSAwIDEgMCA2LjI1IDB2LS4yMzdjLjQ3NS4zMzcgMS4wODcuNTUgMS43NS41NUEzLjEyNSAzLjEyNSAwIDAgMCAzNSAzNi41NjJjMC0xLjI1LS43MzctMi4zMTItMS43ODgtMi44MTJBMy4xMjggMy4xMjggMCAwIDAgMzUgMzAuOTM3Wm0tOCA1LjkzN2EzLjEyNSAzLjEyNSAwIDEgMSAwLTYuMjUgMy4xMjUgMy4xMjUgMCAwIDEgMCA2LjI1Wk0yNS43NSAyMi41YzAtLjY4OC41NjMtMS4yNSAxLjI1LTEuMjUuNjg4IDAgMS4yNS41NjMgMS4yNSAxLjI1IDAgLjY4OC0uNTYzIDEuMjUtMS4yNSAxLjI1LS42ODggMC0xLjI1LS41NjMtMS4yNS0xLjI1Wm0tNSAyLjVjMC0uNjg4LjU2My0xLjI1IDEuMjUtMS4yNS42ODggMCAxLjI1LjU2MyAxLjI1IDEuMjUgMCAuNjg4LS41NjMgMS4yNS0xLjI1IDEuMjUtLjY4OCAwLTEuMjUtLjU2My0xLjI1LTEuMjVabS0yLjUtMi41Yy0uNjg4IDAtMS4yNS0uNTYzLTEuMjUtMS4yNSAwLS42ODguNTYzLTEuMjUgMS4yNS0xLjI1LjY4OCAwIDEuMjUuNTYzIDEuMjUgMS4yNSAwIC42ODgtLjU2MyAxLjI1LTEuMjUgMS4yNVpNMjIgMTguNzVjMC0uNjg4LjU2My0xLjI1IDEuMjUtMS4yNS42ODggMCAxLjI1LjU2MyAxLjI1IDEuMjUgMCAuNjg4LS41NjMgMS4yNS0xLjI1IDEuMjUtLjY4OCAwLTEuMjUtLjU2My0xLjI1LTEuMjVabTcuNSAwYzAtLjY4OC41NjMtMS4yNSAxLjI1LTEuMjUuNjg4IDAgMS4yNS41NjMgMS4yNSAxLjI1IDAgLjY4OC0uNTYzIDEuMjUtMS4yNSAxLjI1LS42ODggMC0xLjI1LS41NjMtMS4yNS0xLjI1Wm03LjUgMi41YzAgLjY4OC0uNTYzIDEuMjUtMS4yNSAxLjI1LS42ODggMC0xLjI1LS41NjMtMS4yNS0xLjI1IDAtLjY4OC41NjMtMS4yNSAxLjI1LTEuMjUuNjg4IDAgMS4yNS41NjMgMS4yNSAxLjI1Wm0tNSAyLjVjLjY4OCAwIDEuMjUuNTYzIDEuMjUgMS4yNSAwIC42ODgtLjU2MyAxLjI1LTEuMjUgMS4yNS0uNjg4IDAtMS4yNS0uNTYzLTEuMjUtMS4yNSAwLS42ODguNTYzLTEuMjUgMS4yNS0xLjI1WiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjg3IiBkPSJNNTAuODQ0IDE4LjU0N1YyOC41aC0xLjcxNnYtOS45NTNoMS43MTZabTYuMzgyIDEuMzI2TDU0LjI1MiAyOC41aC0xLjc5OGwzLjc0Ni05Ljk1M2gxLjE0OWwtLjEyMyAxLjMyNlptMi40ODggOC42MjctMi45OC04LjYyNy0uMTMtMS4zMjZoMS4xNTVsMy43NiA5Ljk1M2gtMS44MDVabS0uMTQ0LTMuNjkxdjEuMzZoLTUuNDE0di0xLjM2aDUuNDE0Wm01LjE0NS02LjI2MlYyOC41aC0xLjcxNnYtOS45NTNoMS43MTZaIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuMzgiIGQ9Ik01Mi44NzQgNDEuNTg1di45MTVoLTMuMzYxdi0uOTE1aDMuMzZabS0zLjA0LTUuNzhWNDIuNUg0OC42OHYtNi42OTVoMS4xNTVabTYuODIyIDUuNjk3VjM5LjEzYzAtLjE3OC0uMDMyLS4zMzEtLjA5Ni0uNDZhLjY3Ni42NzYgMCAwIDAtLjI5NC0uMjk5IDEuMDA2IDEuMDA2IDAgMCAwLS40ODgtLjEwNmMtLjE4IDAtLjMzNy4wMy0uNDY5LjA5MmEuNzU4Ljc1OCAwIDAgMC0uMzA4LjI0OC41OTkuNTk5IDAgMCAwLS4xMS4zNTVoLTEuMTA0YzAtLjE5Ny4wNDgtLjM4Ny4xNDMtLjU3LjA5NS0uMTg0LjIzMy0uMzQ5LjQxNC0uNDkzLjE4LS4xNDQuMzk3LS4yNTcuNjQ4LS4zNC4yNTEtLjA4My41MzMtLjEyNC44NDYtLjEyNC4zNzQgMCAuNzA1LjA2My45OTMuMTg4LjI5MS4xMjYuNTIuMzE2LjY4NS41Ny4xNjkuMjUyLjI1My41NjguMjUzLjk0OHYyLjIxMWMwIC4yMjcuMDE2LjQzMS4wNDYuNjEyLjAzNC4xNzguMDgxLjMzMy4xNDMuNDY0di4wNzRoLTEuMTM2YTEuOTY1IDEuOTY1IDAgMCAxLS4xMjQtLjQ1NSAzLjczIDMuNzMgMCAwIDEtLjA0MS0uNTQzWm0uMTYxLTIuMDI4LjAxLjY4NmgtLjc5NmMtLjIwNSAwLS4zODYuMDItLjU0Mi4wNmExLjExNSAxLjExNSAwIDAgMC0uMzkxLjE2NS43MTguNzE4IDAgMCAwLS4zMTMuNjJjMCAuMTMzLjAzLjI1My4wOTIuMzY0LjA2MS4xMDcuMTUuMTkxLjI2Ny4yNTMuMTIuMDYxLjI2My4wOTIuNDMyLjA5Mi4yMjcgMCAuNDI1LS4wNDYuNTkzLS4xMzhhMS4yMiAxLjIyIDAgMCAwIC40MDUtLjM0NS43NS43NSAwIDAgMCAuMTU2LS4zOWwuMzU5LjQ5MWMtLjAzNy4xMjYtLjEuMjYtLjE4OS40MDVhMS45NjEgMS45NjEgMCAwIDEtLjM1LjQxNCAxLjczNSAxLjczNSAwIDAgMS0xLjE5LjQ0MWMtLjMyNSAwLS42MTUtLjA2NC0uODctLjE5M2ExLjU0NCAxLjU0NCAwIDAgMS0uNTk3LS41MjkgMS4zNzEgMS4zNzEgMCAwIDEtLjIxNi0uNzU5YzAtLjI2My4wNDktLjQ5Ni4xNDctLjY5OGExLjM3IDEuMzcgMCAwIDEgLjQ0Mi0uNTE1Yy4xOTYtLjEzOC40MzUtLjI0My43MTctLjMxMy4yODItLjA3NC42MDQtLjExLjk2NS0uMTFoLjg3Wm01LjA5OCAxLjY3OWMwLS4xMS0uMDI4LS4yMS0uMDgzLS4zLS4wNTUtLjA5MS0uMTYtLjE3NC0uMzE3LS4yNDdhMy4wNzggMy4wNzggMCAwIDAtLjY4LS4yMDMgNS44NjQgNS44NjQgMCAwIDEtLjcyNy0uMjA3IDIuMzA1IDIuMzA1IDAgMCAxLS41Ni0uMjk4IDEuMjU3IDEuMjU3IDAgMCAxLS4zNjQtLjQxNCAxLjE0NiAxLjE0NiAwIDAgMS0uMTI5LS41NTJjMC0uMjAyLjA0NC0uMzk0LjEzMy0uNTc1LjA5LS4xOC4yMTYtLjM0LjM4Mi0uNDc4LjE2Ni0uMTM4LjM2Ni0uMjQ3LjYwMi0uMzI2LjI0LS4wOC41MDYtLjEyLjgtLjEyLjQxNyAwIC43NzQuMDcgMS4wNzIuMjEyLjMuMTM3LjUzLjMyNi42OS41NjUuMTU5LjIzNi4yMzkuNTAzLjIzOS44aC0xLjEwOWEuNzA3LjcwNyAwIDAgMC0uMS0uMzY4LjcwNS43MDUgMCAwIDAtLjI5NS0uMjggMS4wMDggMS4wMDggMCAwIDAtLjQ5Ny0uMTFjLS4xOSAwLS4zNDguMDMtLjQ3My4wOTFhLjY0OC42NDggMCAwIDAtLjI3Ni4yMy41ODcuNTg3IDAgMCAwLS4wNDEuNTM4LjUzLjUzIDAgMCAwIC4xNjUuMThjLjA3Ny4wNTIuMTgxLjEuMzEzLjE0Ny4xMzUuMDQ2LjMwMy4wOS41MDYuMTMzLjM4LjA4LjcwNi4xODMuOTc5LjMwOC4yNzYuMTIzLjQ4Ny4yODIuNjM0LjQ3OC4xNDguMTk0LjIyMS40MzkuMjIxLjczNiAwIC4yMi0uMDQ3LjQyMy0uMTQyLjYwNy0uMDkyLjE4LS4yMjcuMzM5LS40MDUuNDczYTIuMDMgMi4wMyAwIDAgMS0uNjQuMzA5Yy0uMjQ0LjA3My0uNTIuMTEtLjgyNy4xMS0uNDUgMC0uODMyLS4wOC0xLjE0NS0uMjRhMS44MjYgMS44MjYgMCAwIDEtLjcxMi0uNjIgMS40NyAxLjQ3IDAgMCAxLS4yNC0uNzloMS4wNzJhLjgzLjgzIDAgMCAwIC4xNy40OTFjLjEwNC4xMi4yMzMuMjA3LjM4Ni4yNjIuMTU3LjA1My4zMTcuMDc4LjQ4My4wNzguMiAwIC4zNjYtLjAyNi41MDEtLjA3OGEuNzIxLjcyMSAwIDAgMCAuMzA4LS4yMi41MjcuNTI3IDAgMCAwIC4xMDYtLjMyMlptNC41MzItMy42Mjh2LjgxaC0yLjgwNXYtLjgxaDIuODA1Wm0tMS45OTUtMS4yMThoMS4xMDh2NC44MThjMCAuMTU0LjAyMS4yNzIuMDY0LjM1NC4wNDYuMDguMTA5LjEzNC4xODkuMTYxLjA4LjAyOC4xNzMuMDQyLjI4LjA0MmEyLjIwOSAyLjIwOSAwIDAgMCAuMzktLjA0MWwuMDA1Ljg0NWMtLjA5MS4wMjgtLjE5OS4wNTItLjMyMS4wNzQtLjEyLjAyMS0uMjU4LjAzMi0uNDE0LjAzMi0uMjU1IDAtLjQ4LS4wNDQtLjY3Ni0uMTMzYS45OTUuOTk1IDAgMCAxLS40Ni0uNDQ2Yy0uMTEtLjIwNi0uMTY1LS40NzktLjE2NS0uODE5di00Ljg4OFptOC41OCA1LjAydi0zLjgwMmgxLjExNFY0Mi41aC0xLjA0OWwtLjA2NC0xLjE3MlptLjE1Ny0xLjAzNC4zNzMtLjAxYzAgLjMzNS0uMDM3LjY0My0uMTEuOTI1YTIuMTQyIDIuMTQyIDAgMCAxLS4zNC43MzEgMS41OSAxLjU5IDAgMCAxLS41OS40ODMgMS45OSAxLjk5IDAgMCAxLS44Ni4xN2MtLjI0MSAwLS40NjQtLjAzNS0uNjY2LS4xMDZhMS4zNjYgMS4zNjYgMCAwIDEtLjUyNC0uMzI2IDEuNDg1IDEuNDg1IDAgMCAxLS4zMzYtLjU3NSAyLjY1NSAyLjY1NSAwIDAgMS0uMTItLjg0NnYtMy4yMTRoMS4xMDl2My4yMjNjMCAuMTgxLjAyMS4zMzMuMDY0LjQ1NWEuNzcuNzcgMCAwIDAgLjE3NS4yOWMuMDczLjA3NC4xNi4xMjYuMjU3LjE1Ni4wOTkuMDMxLjIwMy4wNDYuMzEzLjA0Ni4zMTYgMCAuNTY0LS4wNjEuNzQ1LS4xODQuMTg0LS4xMjUuMzE0LS4yOTQuMzktLjUwNS4wOC0uMjEyLjEyLS40NS4xMi0uNzEzWm0zLjQwMS0xLjgxMnY1LjkzMmgtMS4xMDh2LTYuODg4aDEuMDJsLjA4OC45NTZabTMuMjQyIDEuNDg2di4wOTZjMCAuMzYyLS4wNDMuNjk3LS4xMjkgMS4wMDdhMi40NiAyLjQ2IDAgMCAxLS4zNzIuODA1IDEuNzE0IDEuNzE0IDAgMCAxLS42MDMuNTI4Yy0uMjM5LjEyNi0uNTE1LjE4OS0uODI3LjE4OS0uMzEgMC0uNTgxLS4wNTctLjgxNC0uMTdhMS42NjcgMS42NjcgMCAwIDEtLjU4NC0uNDkyIDIuNjcgMi42NyAwIDAgMS0uMzg2LS43NDUgNC43ODIgNC43ODIgMCAwIDEtLjIwMy0uOTQ3di0uMzczYy4wNC0uMzY0LjEwOC0uNjk2LjIwMy0uOTkzYTIuNjkgMi42OSAwIDAgMSAuMzg2LS43NjhjLjE2LS4yMTQuMzU0LS4zOC41ODQtLjQ5NmExLjc2IDEuNzYgMCAwIDEgLjgwNC0uMTc1Yy4zMTMgMCAuNTkuMDYxLjgzMy4xODQuMjQyLjEyLjQ0Ni4yOTEuNjExLjUxNS4xNjYuMjIuMjkuNDg3LjM3My44LjA4Mi4zMS4xMjQuNjU0LjEyNCAxLjAzNVptLTEuMTA4LjA5NnYtLjA5NmMwLS4yMy0uMDIyLS40NDMtLjA2NS0uNjQtLjA0My0uMTk5LS4xMS0uMzc0LS4yMDItLjUyNGEuOTkuOTkgMCAwIDAtLjM1NC0uMzUuOTYzLjk2MyAwIDAgMC0uNTEtLjEyOGMtLjE5NyAwLS4zNjUuMDM0LS41MDYuMTAxYS45Ny45NyAwIDAgMC0uMzU0LjI3MSAxLjI4MiAxLjI4MiAwIDAgMC0uMjIuNDFjLS4wNTMuMTUzLS4wOS4zMi0uMTExLjV2Ljg5M2MuMDM3LjIyLjEuNDIzLjE4OC42MDcuMDkuMTg0LjIxNS4zMy4zNzcuNDQxLjE2Ni4xMDguMzc3LjE2MS42MzUuMTYxLjE5OSAwIC4zNy0uMDQzLjUxLS4xMjkuMTQxLS4wODUuMjU2LS4yMDMuMzQ1LS4zNTQuMDkyLS4xNTMuMTYtLjMzLjIwMi0uNTI4LjA0My0uMi4wNjUtLjQxMS4wNjUtLjYzNVptNS4yODEgMS40MDd2LTYuMDMyaDEuMTEzVjQyLjVoLTEuMDA3bC0uMTA2LTEuMDNabS0zLjIzNy0xLjQwMnYtLjA5N2MwLS4zNzcuMDQ1LS43Mi4xMzQtMS4wMy4wODgtLjMxMy4yMTctLjU4LjM4Ni0uODA1LjE2OC0uMjI2LjM3NC0uNC42MTYtLjUyLjI0Mi0uMTIyLjUxNS0uMTgzLjgxOC0uMTgzLjMgMCAuNTY0LjA1OC43OTEuMTc1LjIyNy4xMTYuNDIuMjgzLjU4LjVhMi42IDIuNiAwIDAgMSAuMzgxLjc3M2MuMDk1LjI5OC4xNjMuNjI5LjIwMy45OTR2LjMwOGMtLjA0LjM1NS0uMTA4LjY4LS4yMDMuOTc0YTIuNjIgMi42MiAwIDAgMS0uMzgxLjc2NGMtLjE2LjIxNC0uMzU0LjM4LS41ODQuNDk2YTEuNzE5IDEuNzE5IDAgMCAxLS43OTYuMTc1Yy0uMyAwLS41NzItLjA2My0uODE0LS4xODlhMS43OTggMS43OTggMCAwIDEtLjYxMS0uNTI4IDIuNDkgMi40OSAwIDAgMS0uMzg2LS44IDMuNjM2IDMuNjM2IDAgMCAxLS4xMzQtMS4wMDdabTEuMTA4LS4wOTd2LjA5N2MwIC4yMjcuMDIuNDM4LjA2LjYzNC4wNDMuMTk2LjEwOS4zNy4xOTguNTIuMDg5LjE0Ny4yMDQuMjYzLjM0NS4zNS4xNDQuMDgyLjMxNi4xMjMuNTE1LjEyMy4yNTEgMCAuNDU4LS4wNTUuNjItLjE2NS4xNjMtLjExLjI5LS4yNi4zODItLjQ0Ni4wOTUtLjE5LjE2LS40MDIuMTkzLS42MzV2LS44MzJhMi4wMyAyLjAzIDAgMCAwLS4xMTUtLjUwNiAxLjM1IDEuMzUgMCAwIDAtLjIyNS0uNDA5Ljk0OC45NDggMCAwIDAtLjM1NC0uMjc2IDEuMTEgMS4xMSAwIDAgMC0uNDkyLS4xMDEuOTcyLjk3MiAwIDAgMC0uNTE1LjEyOSAxLjA0MyAxLjA0MyAwIDAgMC0uMzUuMzU0Yy0uMDg4LjE1LS4xNTQuMzI1LS4xOTcuNTI0LS4wNDMuMi0uMDY1LjQxMi0uMDY1LjY0Wm03LjQwMiAxLjUzMVYzOS4xM2MwLS4xNzgtLjAzMy0uMzMxLS4wOTctLjQ2YS42NzYuNjc2IDAgMCAwLS4yOTQtLjI5OSAxLjAwNyAxLjAwNyAwIDAgMC0uNDg4LS4xMDZjLS4xOCAwLS4zMzcuMDMtLjQ2OS4wOTJhLjc1OS43NTkgMCAwIDAtLjMwOC4yNDguNi42IDAgMCAwLS4xMS4zNTVoLTEuMTAzYzAtLjE5Ny4wNDctLjM4Ny4xNDItLjU3LjA5NS0uMTg0LjIzMy0uMzQ5LjQxNC0uNDkzLjE4LS4xNDQuMzk3LS4yNTcuNjQ4LS4zNC4yNTItLjA4My41MzQtLjEyNC44NDYtLjEyNC4zNzQgMCAuNzA1LjA2My45OTMuMTg4LjI5MS4xMjYuNTIuMzE2LjY4NS41Ny4xNjkuMjUyLjI1My41NjguMjUzLjk0OHYyLjIxMWMwIC4yMjcuMDE2LjQzMS4wNDYuNjEyLjAzNC4xNzguMDgxLjMzMy4xNDMuNDY0di4wNzRoLTEuMTM2YTEuOTYgMS45NiAwIDAgMS0uMTI0LS40NTUgMy43MTYgMy43MTYgMCAwIDEtLjA0MS0uNTQzWm0uMTYtMi4wMjguMDEuNjg2aC0uNzk2Yy0uMjA1IDAtLjM4Ni4wMi0uNTQyLjA2YTEuMTE2IDEuMTE2IDAgMCAwLS4zOTEuMTY1LjcyLjcyIDAgMCAwLS4zMTMuNjJjMCAuMTMzLjAzLjI1My4wOTIuMzY0LjA2MS4xMDcuMTUuMTkxLjI2Ny4yNTMuMTIuMDYxLjI2My4wOTIuNDMyLjA5Mi4yMjcgMCAuNDI1LS4wNDYuNTkzLS4xMzguMTcyLS4wOTUuMzA3LS4yMS40MDUtLjM0NWEuNzUxLjc1MSAwIDAgMCAuMTU2LS4zOWwuMzU5LjQ5MWMtLjAzNy4xMjYtLjEuMjYtLjE4OS40MDVhMS45NjQgMS45NjQgMCAwIDEtLjM1LjQxNCAxLjczNiAxLjczNiAwIDAgMS0xLjE5LjQ0MWMtLjMyNSAwLS42MTUtLjA2NC0uODY5LS4xOTNhMS41NDUgMS41NDUgMCAwIDEtLjU5OC0uNTI5IDEuMzcyIDEuMzcyIDAgMCAxLS4yMTYtLjc1OWMwLS4yNjMuMDUtLjQ5Ni4xNDctLjY5OC4xMDEtLjIwNi4yNDktLjM3Ny40NDItLjUxNS4xOTYtLjEzOC40MzUtLjI0My43MTctLjMxMy4yODItLjA3NC42MDQtLjExLjk2Ni0uMTFoLjg2OVptNC41ODgtMS45NDl2LjgxSDkxLjIydi0uODFoMi44MDVabS0xLjk5Ni0xLjIxOGgxLjEwOHY0LjgxOGMwIC4xNTQuMDIyLjI3Mi4wNjUuMzU0LjA0Ni4wOC4xMDkuMTM0LjE4OC4xNjEuMDguMDI4LjE3My4wNDIuMjguMDQyYTIuMjA5IDIuMjA5IDAgMCAwIC4zOS0uMDQxbC4wMDYuODQ1Yy0uMDkyLjAyOC0uMi4wNTItLjMyMi4wNzQtLjEyLjAyMS0uMjU3LjAzMi0uNDEzLjAzMi0uMjU1IDAtLjQ4LS4wNDQtLjY3Ni0uMTMzYS45OTQuOTk0IDAgMCAxLS40Ni0uNDQ2Yy0uMTEtLjIwNi0uMTY2LS40NzktLjE2Ni0uODE5di00Ljg4OFptNS4yNCA2LjI4NWMtLjM2NyAwLS43LS4wNi0uOTk3LS4xOGEyLjIwMiAyLjIwMiAwIDAgMS0uNzU0LS41MSAyLjI2MyAyLjI2MyAwIDAgMS0uNDc0LS43NjggMi42OSAyLjY5IDAgMCAxLS4xNjYtLjk1MXYtLjE4NGMwLS4zOS4wNTctLjc0Mi4xNy0xLjA1OGEyLjQgMi40IDAgMCAxIC40NzQtLjgxYy4yMDMtLjIyNi40NDItLjQuNzE4LS41MTkuMjc1LS4xMi41NzQtLjE3OS44OTYtLjE3OS4zNTYgMCAuNjY3LjA2LjkzMy4xOC4yNjcuMTE5LjQ4OC4yODguNjYzLjUwNS4xNzcuMjE1LjMxLjQ3LjM5NS43NjguMDg5LjI5Ny4xMzMuNjI1LjEzMy45ODR2LjQ3NGgtMy44NDR2LS43OTZoMi43NXYtLjA4N2ExLjU1NCAxLjU1NCAwIDAgMC0uMTItLjU2MS45NTMuOTUzIDAgMCAwLS4zMjYtLjQyM2MtLjE0Ny0uMTA3LS4zNDMtLjE2MS0uNTg5LS4xNjFhMSAxIDAgMCAwLS40OTIuMTIuOTczLjk3MyAwIDAgMC0uMzUzLjMzNSAxLjc2NyAxLjc2NyAwIDAgMC0uMjIxLjUzM2MtLjA1LjIwNi0uMDc0LjQzNy0uMDc0LjY5NXYuMTg0YzAgLjIxNy4wMy40Mi4wODguNjA3LjA2LjE4NC4xNS4zNDQuMjY2LjQ4Mi4xMTcuMTM4LjI1OC4yNDcuNDIzLjMyNy4xNjYuMDc2LjM1NC4xMTUuNTY2LjExNS4yNjcgMCAuNTA0LS4wNTQuNzEzLS4xNjEuMjA4LS4xMDcuMzg5LS4yNi41NDItLjQ1NWwuNTg0LjU2NWEyLjI4IDIuMjggMCAwIDEtLjQxOC40NSAyLjA5NSAyLjA5NSAwIDAgMS0uNjMuMzQ2Yy0uMjQ2LjA4OC0uNTMuMTMzLS44NTYuMTMzWm04LjU1NC02LjgxVjQyLjVoLTEuMTA4di01LjQwM2wtMS42NDEuNTU3di0uOTE1bDIuNjE2LS45NTdoLjEzM1ptNS42MjIgNS42ODh2LTYuMDMyaDEuMTEzVjQyLjVoLTEuMDA3bC0uMTA2LTEuMDNabS0zLjIzNy0xLjQwMnYtLjA5N2MwLS4zNzcuMDQ0LS43Mi4xMzMtMS4wM2EyLjQyIDIuNDIgMCAwIDEgLjM4Ny0uODA1Yy4xNjgtLjIyNi4zNzQtLjQuNjE2LS41Mi4yNDItLjEyMi41MTUtLjE4My44MTgtLjE4My4zMDEgMCAuNTY0LjA1OC43OTEuMTc1LjIyNy4xMTYuNDIuMjgzLjU3OS41LjE2LjIxNi4yODcuNDczLjM4Mi43NzMuMDk1LjI5OC4xNjIuNjI5LjIwMi45OTR2LjMwOGMtLjA0LjM1NS0uMTA3LjY4LS4yMDIuOTc0YTIuNjAzIDIuNjAzIDAgMCAxLS4zODIuNzY0Yy0uMTU5LjIxNC0uMzU0LjM4LS41ODQuNDk2YTEuNzE2IDEuNzE2IDAgMCAxLS43OTUuMTc1Yy0uMyAwLS41NzItLjA2My0uODE0LS4xODlhMS43OTIgMS43OTIgMCAwIDEtLjYxMS0uNTI4IDIuNDg1IDIuNDg1IDAgMCAxLS4zODctLjggMy42MzIgMy42MzIgMCAwIDEtLjEzMy0xLjAwN1ptMS4xMDgtLjA5N3YuMDk3YzAgLjIyNy4wMi40MzguMDYuNjM0LjA0My4xOTYuMTA5LjM3LjE5OC41Mi4wODkuMTQ3LjIwMy4yNjMuMzQ0LjM1LjE0NS4wODIuMzE2LjEyMy41MTUuMTIzLjI1MiAwIC40NTktLjA1NS42MjEtLjE2NS4xNjMtLjExLjI5LS4yNi4zODItLjQ0NmEyLjA4IDIuMDggMCAwIDAgLjE5My0uNjM1di0uODMyYTIuMDI1IDIuMDI1IDAgMCAwLS4xMTUtLjUwNiAxLjM0OSAxLjM0OSAwIDAgMC0uMjI1LS40MDkuOTQ4Ljk0OCAwIDAgMC0uMzU0LS4yNzYgMS4xMSAxLjExIDAgMCAwLS40OTItLjEwMS45Ny45NyAwIDAgMC0uNTE1LjEyOSAxLjA0NiAxLjA0NiAwIDAgMC0uMzUuMzU0Yy0uMDg5LjE1LS4xNTUuMzI1LS4xOTcuNTI0LS4wNDMuMi0uMDY1LjQxMi0uMDY1LjY0Wm05Ljk0MyAxLjUzMVYzOS4xM2MwLS4xNzgtLjAzMy0uMzMxLS4wOTctLjQ2YS42NzguNjc4IDAgMCAwLS4yOTQtLjI5OSAxLjAxIDEuMDEgMCAwIDAtLjQ4OC0uMTA2Yy0uMTgxIDAtLjMzNy4wMy0uNDY5LjA5MmEuNzYuNzYgMCAwIDAtLjMwOC4yNDguNjAzLjYwMyAwIDAgMC0uMTEuMzU1aC0xLjEwNGMwLS4xOTcuMDQ4LS4zODcuMTQzLS41Ny4wOTUtLjE4NC4yMzMtLjM0OS40MTQtLjQ5M2EyLjA3IDIuMDcgMCAwIDEgLjY0OC0uMzRjLjI1MS0uMDgzLjUzMy0uMTI0Ljg0Ni0uMTI0LjM3NCAwIC43MDUuMDYzLjk5My4xODguMjkxLjEyNi41Mi4zMTYuNjg1LjU3LjE2OS4yNTIuMjUzLjU2OC4yNTMuOTQ4djIuMjExYzAgLjIyNy4wMTYuNDMxLjA0Ni42MTIuMDM0LjE3OC4wODEuMzMzLjE0My40NjR2LjA3NGgtMS4xMzZhMS45ODYgMS45ODYgMCAwIDEtLjEyNC0uNDU1IDMuNjU5IDMuNjU5IDAgMCAxLS4wNDEtLjU0M1ptLjE2LTIuMDI4LjAxLjY4NmgtLjc5NmMtLjIwNSAwLS4zODYuMDItLjU0Mi4wNmExLjExIDEuMTEgMCAwIDAtLjM5MS4xNjUuNzIyLjcyMiAwIDAgMC0uMjM1LjI2Ny43ODIuNzgyIDAgMCAwLS4wNzguMzU0YzAgLjEzMi4wMzEuMjUyLjA5Mi4zNjNhLjY0LjY0IDAgMCAwIC4yNjcuMjUzLjkzNC45MzQgMCAwIDAgLjQzMi4wOTIgMS4yMTcgMS4yMTcgMCAwIDAgLjk5OC0uNDgzLjc1Ljc1IDAgMCAwIC4xNTYtLjM5bC4zNTkuNDkxYy0uMDM3LjEyNi0uMS4yNi0uMTg5LjQwNWExLjk2NCAxLjk2NCAwIDAgMS0uMzQ5LjQxNCAxLjczIDEuNzMgMCAwIDEtMS4xOTEuNDQxYy0uMzI1IDAtLjYxNS0uMDY0LS44NjktLjE5M2ExLjU0MSAxLjU0MSAwIDAgMS0uNTk4LS41MjkgMS4zNzIgMS4zNzIgMCAwIDEtLjIxNi0uNzU5YzAtLjI2My4wNDktLjQ5Ni4xNDctLjY5OC4xMDEtLjIwNi4yNDgtLjM3Ny40NDItLjUxNS4xOTYtLjEzOC40MzUtLjI0My43MTctLjMxMy4yODItLjA3NC42MDQtLjExLjk2NS0uMTFoLjg2OVptNS40OTQtMS45NDloMS4wMDZ2NC44MzdjMCAuNDQ4LS4wOTUuODI4LS4yODUgMS4xNC0uMTkuMzEzLS40NTUuNTUtLjc5NS43MTMtLjM0LjE2Ni0uNzM0LjI0OC0xLjE4Mi4yNDhhMi43NyAyLjc3IDAgMCAxLTEuMzA2LS4zNSAxLjY4IDEuNjggMCAwIDEtLjUzOC0uNDczbC41Mi0uNjUzYy4xNzguMjEyLjM3NC4zNjcuNTg5LjQ2NS4yMTQuMDk4LjQzOS4xNDcuNjc1LjE0Ny4yNTUgMCAuNDcxLS4wNDguNjQ5LS4xNDNhLjk2MS45NjEgMCAwIDAgLjQxOC0uNDA5Yy4wOTgtLjE4LjE0Ny0uNDAxLjE0Ny0uNjYydi0zLjczM2wuMTAyLTEuMTI3Wm0tMy4zOCAyLjU0M3YtLjA5N2MwLS4zNzcuMDQ2LS43Mi4xMzgtMS4wM2EyLjQ1IDIuNDUgMCAwIDEgLjM5NS0uODA1Yy4xNzItLjIyNi4zOC0uNC42MjYtLjUyLjI0NS0uMTIyLjUyMi0uMTgzLjgzMi0uMTgzLjMyMiAwIC41OTYuMDU4LjgyMy4xNzUuMjMuMTE2LjQyMS4yODMuNTc1LjUuMTUzLjIxNi4yNzMuNDczLjM1OC43NzMuMDg5LjI5OC4xNTUuNjI5LjE5OC45OTR2LjMwOGMtLjA0LjM1NS0uMTA3LjY4LS4yMDIuOTc0YTIuNjk3IDIuNjk3IDAgMCAxLS4zNzcuNzY0Yy0uMTU3LjIxNC0uMzUuMzgtLjU4LjQ5NmExLjczNyAxLjczNyAwIDAgMS0uODA0LjE3NWMtLjMwNCAwLS41NzgtLjA2My0uODIzLS4xODlhMS44NzkgMS44NzkgMCAwIDEtLjYyNi0uNTI4IDIuNTE4IDIuNTE4IDAgMCAxLS4zOTUtLjggMy41MiAzLjUyIDAgMCAxLS4xMzgtMS4wMDdabTEuMTA4LS4wOTd2LjA5N2MwIC4yMjcuMDIyLjQzOC4wNjQuNjM0LjA0Ni4xOTYuMTE1LjM3LjIwNy41Mi4wOTUuMTQ3LjIxNS4yNjMuMzU5LjM1LjE0Ny4wODIuMzIuMTIzLjUyLjEyMy4yNiAwIC40NzMtLjA1NS42MzktLjE2NS4xNjgtLjExLjI5Ny0uMjYuMzg2LS40NDYuMDkyLS4xOS4xNTYtLjQwMi4xOTMtLjYzNXYtLjgzMmEyLjAyNSAyLjAyNSAwIDAgMC0uMTE1LS41MDYgMS4zNDkgMS4zNDkgMCAwIDAtLjIyNS0uNDA5Ljk0Mi45NDIgMCAwIDAtLjM1OS0uMjc2IDEuMTkzIDEuMTkzIDAgMCAwLS41MS0uMTAxYy0uMTk5IDAtLjM3My4wNDMtLjUyLjEyOWExLjA1NSAxLjA1NSAwIDAgMC0uMzYzLjM1NCAxLjc1IDEuNzUgMCAwIDAtLjIwNy41MjRjLS4wNDYuMi0uMDY5LjQxMi0uMDY5LjY0Wm00LjQxMy4wOTd2LS4xMDZjMC0uMzU5LjA1Mi0uNjkxLjE1Ni0uOTk4LjEwNC0uMzEuMjU0LS41NzguNDUxLS44MDRhMi4wMyAyLjAzIDAgMCAxIC43MjYtLjUzNGMuMjg4LS4xMjkuNjEzLS4xOTMuOTc1LS4xOTMuMzY1IDAgLjY5LjA2NC45NzUuMTkzLjI4OC4xMjYuNTMxLjMwNC43MzEuNTM0LjE5OS4yMjYuMzUxLjQ5NS40NTUuODA0LjEwNC4zMDcuMTU2LjY0LjE1Ni45OTh2LjEwNmMwIC4zNTgtLjA1Mi42OS0uMTU2Ljk5N2EyLjM4NyAyLjM4NyAwIDAgMS0uNDU1LjgwNWMtLjIuMjI3LS40NDIuNDA1LS43MjcuNTMzYTIuMzggMi4zOCAwIDAgMS0uOTcuMTg5Yy0uMzY1IDAtLjY5MS0uMDYzLS45NzktLjE4OWEyLjExIDIuMTEgMCAwIDEtLjcyNy0uNTMzIDIuMzg3IDIuMzg3IDAgMCAxLS40NTUtLjgwNSAzLjA4NiAzLjA4NiAwIDAgMS0uMTU2LS45OTdabTEuMTA4LS4xMDZ2LjEwNmMwIC4yMjMuMDIzLjQzNS4wNjkuNjM0LjA0Ni4yLjExOC4zNzQuMjE2LjUyNC4wOTguMTUuMjI0LjI2OS4zNzcuMzU0LjE1My4wODYuMzM2LjEzLjU0Ny4xMy4yMDUgMCAuMzgzLS4wNDQuNTMzLS4xMy4xNTQtLjA4NS4yNzktLjIwMy4zNzctLjM1NC4wOTgtLjE1LjE3MS0uMzI1LjIxNy0uNTI0LjA0OS0uMi4wNzMtLjQxLjA3My0uNjM0di0uMTA2YzAtLjIyLS4wMjQtLjQzLS4wNzMtLjYyNWExLjYxIDEuNjEgMCAwIDAtLjIyMS0uNTMgMS4wNTcgMS4wNTcgMCAwIDAtLjkxNS0uNDkxYy0uMjA5IDAtLjM4OS4wNDQtLjU0My4xMzMtLjE1LjA4Ni0uMjc0LjIwNi0uMzcyLjM1OWExLjY3IDEuNjcgMCAwIDAtLjIxNi41MjkgMi43MzMgMi43MzMgMCAwIDAtLjA2OS42MjVaIi8+PHBhdGggZmlsbD0iIzdDQzMyMiIgZD0ibTk1LjI5NSA5OS40NTUtNC4xMjUtMS4wMSAxLjY5Ny0xNS43MjZoMTYuODY1djQuMzgzSDk3LjE2NGwtLjg2IDcuNjdjLjQ4OC0uMjg3IDEuMTYtLjU2NiAyLjAyLS44MzguODYtLjI4NyAxLjg0MS0uNDMgMi45NDQtLjQzIDEuNDc1IDAgMi43OTMuMjQzIDMuOTUzLjczYTcuODczIDcuODczIDAgMCAxIDIuOTg2IDIuMDg0Yy44MTYuOTAzIDEuNDM5IDIuMDA2IDEuODY5IDMuMzA5LjQzIDEuMjg5LjY0NSAyLjc0My42NDUgNC4zNjEgMCAxLjQ0Ny0uMjE1IDIuOC0uNjQ1IDQuMDYxYTkuNDU4IDkuNDU4IDAgMCAxLTEuODkgMy4zM2MtLjg0Ni45Ni0xLjkxMyAxLjcxMS0zLjIwMiAyLjI1Ni0xLjI3NC41My0yLjc4NS43OTUtNC41MzMuNzk1LTEuMzAzIDAtMi41NTYtLjE4Ny0zLjc2LS41NTlhMTAuMjQ4IDEwLjI0OCAwIDAgMS0zLjItMS42OTcgOC42NSA4LjY1IDAgMCAxLTIuMjc4LTIuNzkzYy0uNTczLTEuMTE3LS45MDItMi4zOTktLjk4OC0zLjg0Nmg1LjA3Yy4xMjkgMS4wMTcuNDA4IDEuODg0LjgzOCAyLjZhNC40MyA0LjQzIDAgMCAwIDEuNzYxIDEuNjExYy43MzEuMzcyIDEuNTc2LjU1OSAyLjUzNi41NTkuODczIDAgMS42MjUtLjE1MSAyLjI1Ni0uNDUxYTQuMjc2IDQuMjc2IDAgMCAwIDEuNTY4LTEuMzMzYy40My0uNTg3Ljc0NS0xLjI3NC45NDUtMi4wNjIuMjE1LS43ODguMzIyLTEuNjYxLjMyMi0yLjYyMSAwLS45MTctLjEyMS0xLjc1NS0uMzY1LTIuNTE0YTUuNDY4IDUuNDY4IDAgMCAwLTEuMDUyLTEuOTc2IDQuNTEyIDQuNTEyIDAgMCAwLTEuNzQxLTEuMjljLS43MDItLjMxNS0xLjUxMS0uNDcyLTIuNDI3LS40NzItMS4yMzIgMC0yLjE3OC4xNzktMi44MzYuNTM3YTguODkzIDguODkzIDAgMCAwLTEuODA1IDEuMzMyWiIvPjxkZWZzPjxwYXR0ZXJuIGlkPSJhIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBwYXR0ZXJuQ29udGVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCI+PHVzZSB4bGluazpocmVmPSIjYyIgdHJhbnNmb3JtPSJtYXRyaXgoLjAwNTAzIDAgMCAuMDA2MjkgMCAwKSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMTk3LjQxNyIgaGVpZ2h0PSIxNTcuNDE3IiB4PSIxLjI5MiIgeT0iMS4yOTIiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjIuMzU0Ii8+PGZlQ29tcG9zaXRlIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzExMDBfNTQ1Ii8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMTAwXzU0NSIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFJQkFRRUJBUUlCQVFFQ0FnSUNBZ1FEQWdJQ0FnVUVCQU1FQmdVR0JnWUZCZ1lHQndrSUJnY0pCd1lHQ0FzSUNRb0tDZ29LQmdnTERBc0tEQWtLQ2dyLzJ3QkRBUUlDQWdJQ0FnVURBd1VLQndZSENnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ3Ivd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhRQUFBZ01CQVFFQkFRQUFBQUFBQUFBQUJBWURCUWNDQVFnQUNmL0VBQnNCQUFNQkFRRUJBUUFBQUFBQUFBQUFBQUVDQXdBRUJRY0cvOW9BREFNQkFBSVFBeEFBQUFENG4vUGVQQTI0TzViUmJlN1RiRUtzNjRsRktWU1VIZTNPTVJNRE1PMkVkZzNJVHRDVDFzY284ekR2cjdpakMralljNXVpc3E0a0taTUZLaGFLY2lteVgwYm5HRm1IcGo2R2xwUmZvNGRHRko2WXpEREZ4SExWNS9QeTJoWWZnSmhpQUNsUXhFTm1EVUZqSlRVSDdHQW1KdGQxRGIzRTNvT0s4NysxMUNYRFp4ODFVYVFVZHk4bms2eWp2b21NMnhLVE1SUzBCMDhhb3ZrU3gyNk9xczlqYmFaNmlGOXphZjZUZk5ubkd5b3VkY3pCb1ZOYkxwdDVSbkw4OXh5NVE2WVNwbHdLbXBjOGFxMmVCNkJnWlcvcFh1cHFpN3Q2VE92b21ydHRkOUtpNnVTT2RjeDVCUXkyWWMxQnk4RHRiL2orYjNhdnJncm5uQXllSlhHNWI3S3lWUXR5NGQydGVweEhmVXZYZTU2MnRxNmdLdU5MVitDQkZNNjUwb3A1aDZObnNzai9BREtjYjZ2bzFmZlJQaTBFZ05vUm8zUkowNnphZHJ6ZEJhdXQyVHByb0hWWjN0VlgwMHZTYUhjN05hQ3F5WlVEeVVNbVV3bGlQeU5CcUN2czhOQVZWYmdpRE5kMTJtZDZXdmMrMCt4VzJyU0lzNzA2ZEsxMStrb20xNERSRlV0cFg4N24wVUtrNlBMa1FuOGQvSGVRV2hFcWU2STY5YzNIcEZmcVdIUVd2dExuNkZOQTc3YXpib3RSVzFuWmR5MTFJM0paek5ZRnlEWG5WNXV0VjUzeDJKenFtbi9OMzVEd0ROb0tFMm8xTDBPZlNQVmNtMUdicWUvNldacTJlSzlEeTFnaXFiTEwwemFYbmNQamczS1BuTk9abkZxTzBTc1RDeXdWL25MOGw0WUgzam0xb21uZWxMU3ZXZHk3bk5velRXbHE5dFNicElMS2VuWEZYNlBibjF1VE54emp1V3RLMUR6S1N4TjVzZWY4eHJLUy9tVjhxNWUyQmRBN2RDYS83STBEMFhlK21pMEIxbWZxVzJ1blpuS3pURlJwcHBJZGVmUHpLTzVUR1p6UFF6V1dOV1F6TnFlaXpsVFdsOFMvTitheGNGUHRGN1YxejFpOTlyUDlyZmd3UXoyZWxRUUtXam4vQURCaXV0d2J0NTYrdW5rSVZpTlJDbk40bzk2elpDOE14RXFpVmZuWDhWeFh0MG5zMjIra3V3K2hSb2Exd1g2MjlKVmtVVkhzTStXODhuYm9LeVUyYmVvejlFV1JIVWFUUVpLamlhM2xXNnpSbGVkTFlwK1k4OXo2MHV1bmJyM25YK2pvczUxcjFCTFpwcnFkU3NUWGxYV29PMDlLVUxUdUpkamF6aHRPdXdWRUt3ME1QUmJOMkZGc2owMW53dVZ3NjU2NTNwdFBUWFRhV3FJTlN5RHpXalBYVTRFWU1RQlVyQ3NtTXBDM0w2ZkwwNk9zVldHWHlNK2VPYWhYSnFxdFo0cW1vUEdqcFhmSFl1eE5HczJrMWRFZ25VenVoN0svWUJwcWNrQ3kxY2FKMEtzL1FzYjVwNC9WbFpsV0xDMG5sbFlXSzFpN09YRXp6cVNsTDhsZEg3WmZRdmZGK3BTMURKMFpNVDRDV3Fablc3V2pSMDZTS2lyQ1hlbjZHUjZMWEYyMVBKMytZMXJ6UXhnNlJ5M3Q4d1dzVmhXVy9QanFIYW4wQjNMb2xUbmZMbFdKVnVkN3BrOEthdjBiWDYyeEhrenBSMXpFc3U4anNyWVd5VGc3UWc2NFNzQ2E1ZmhTVzVyT29zZWlLUHh3MGZ0bnAxeElyVTY1WGhxK2J6cTF0dHR2V3RGRjFoR2I2dTV2YWpqWVFQMUd5YkpsdEdWak5WYWNaQlJTcXRDcWFQOEEvOFFBSlJBQUFnSUNBZ01CQUFNQkFRRUFBQUFBQXdRQ0JRQUJCaElSRXhRaEJ4VWlKQllqLzlvQUNBRUJBQUVGQWR4emNjNjV1T2RNMFB6a1JaQUdSQnZJTFpCWElLZm55NThtYlV5U3ViVjNrd2VNbUxKRHpwa0lmc1llTTNrczNEZWJoblRPbWFGa0E0TUdRQmtGOEd0ZzFNaXJyV3ZtMTQrYkpLNUpiRmFXUjQydFVST2MxOTd5U3U4a3R2V0RYL2ZYa28vazQ1NnM5T2VueG1nNUVPUkJrQTRNR0RCZ2dZRlhQbC96cGY4QUpoMXJlbHRrbHVrbjYrSzBrakI1aHc3MzFLVlJvc1M4Zm5DVGxYdUdEUi95ZGJwcVEvMlFNMERQbXdpL2pJaXlJc0dIQmh5QWNHREJMNzF0Tlh0bTYveGtLLzhBSDBKQzNTSWJJVlNuK21ISHc2VnR5MUdtVm1lTnRJWERWSktJYml0NjVHa2tHdmFXL3dBL052MmJXMzVnRFBSaEFaNnZHNGp3WXNnUEFod0svYlVWcFF5ckhvbWxVSXloT3E2Q0tnTm9WUnhuN2tKS1JWcnFPbkpOd0F4Zk0zeGtMb3JTbjlVVytPN2FiNVdwL1hxdEo3M0dGRVl1ZjFQcm5BR2JEK0VIa2g1Q09RamtkZU1EckVsdHkySktmVlJMMXlTN2ExTWZtS3RQT2JQSGF6NUMzTktYUmY2cU5lQmwrWUMxK3BTRGFWUW1kRW9DS3o1SHgrZGdjUEF2TWljWUFGRzU0enRVVUIvaEkvazRaT0dRSGtCYjhSakxLOEhZcU5acnFsVGU4ZXVQRUlLcXJpd0xPazlrYXpqMmpxVW5IWisxZW5CUExWYmNaRG9ac25Da1JNUUJBUG45UXNjTGZFVjU3WjQ4TVE3TkdTMGIyUHUxcldHeVdkUE93SjlzaFhkbzdyNTZsVm82MFJOUGN3VXBYNnR5cUNuUEk4WkhxVVVHTjc0dlVtOWtrUmpDV2NnWkJTRGU0VXdZSnQxL3QwOXZhZUpPejljN0JmZU5xZTdkaFQrNkYxeENQYlVzTEx6bTk0Q1BlZFhYKzJDMUYyZ3hVRUdjTlRPT1ZpMHh5V1Y5dVZxWHVVcUsvd0Q0OVVnWTZCSlpFYi9KeGFrV3hsaUE1eEFsT0pnMk5YQmlOalVqR3hZa21ESlc1UU5WZHBBc1pzUkpsdU9CczluNUluNTIvVTkvN290eDFCWHJ1RVVJRm1sWEQ4THFLaXhWSlV1cVd1MFBVT29Jc3UrWVBXWkR5a0lwRFVvakJucTc2UHUzOEFhcWVaZjNLSXVidE1zUXRBRUFYMU9NakNkT1pXdkFtekVuR1J2ejI1cVc5elJnU1JLU00vRlFJeHliUmxIRmU0NUNFVFkxQnpES25FVFVHbWR3MjliRDN1dnJubUdVT0xRRUhsZGlzR0p1WCt3Q3pMY2JDdDNJd0VraDhoYkkyU01xdy95YkZaUTZRMEtXN1VBWEl5bHZlUWhnUmQ5MHdCNjF4YXJDek5kUDVYaHF4T0Y0RTRGVTJYZTZMN3luZ1FLaWRweU9HcEc1Q0h1bnpJSWlwY3BOYkJzUFlNODBGcTl4Vk9HM0FiN1pYRlROUGtWYk9DMUlzYVl2akNDRnhiK2NGeXFZR0Z4K3pJaS8rZGFEWkRxMS93RHljRlhLTlZjTXpsVVNuODI2T1pEcTFBU3lSUFYwd2J1OHNXMmJUY1NhTFp6NzE3VEFybWd0RkpIYkFURjZiYnFxdEl6TWp3Z2dCSld5RmxEelNWUmo1S1pxdHRyRHhsdGExUk5xckIzdXBIcWUyRnVrS05iY1dLYXZqTlBqbFlJUWxGaHdsRWdmWWhZaFFmdTY4bEpNOGlQU2lhUUpPSmphcjdWU0tqd3FTVHJsZldFZzVUSSt4YXNJQ3NtOXlpdGlxMjhFMlhCaDU3QUtyMlBNdDFyL0FDYmtMa0JvV29yWmk1ZFNnT3ROMEp2d2NWT2x2MjBTR3BDcmc2QW9DTUk2a3hEUlRwYWR4SnFldzJOSENEWlI2SnM4NW9udGtJMmhlUGdrampOellKTWc1REl1N1NQU3V0NXloVUJZN0g1UlpSZzFjV0t5RmRkWHlaYkdpSHRsVjhYL0FKcVg4cUUrZS9ybWZPSm5uTGZHOVI4VU1vYTJHV3RMdFdlaGFKWkQzbkR5eWRLOVA0WnN0QzNvZXZvbTNVL1hLdC9qNXlCYmNpeXBlV0syWnlWVzF3ek82TTFFK1haYU9ndHRFTmNrOS9KZjVCc3U4NHBlOTlSYlNpdk5MMVpxNC9rQmoyMnRRejEzVUdIUGRMcndLbmIzc3pObDhxYkhKQkdKWEx1dEhvVG9jVXI1M2tMa3gzSEJ4RmFXUTIxZVR2NkkzL0pyYXBXSHBWOXBOZUZpdlZmY29DdnZONnJHclRRd2hsRkN4S2ZyYVhQdHNiR3ZDdlBYT2J3VkZ4NjRJU2VYRTlXcUNwWlIxeGNwQ2xxOVNrdngxUTBKM1RVdGpVNnhLTGw2dE1OL2xHN2hhaDVNNnMxV1hpbTVXVXB2Z3N4eFdCQVZZMVBheDFUcEM4amx1TzhYQ0VPMmJLZjJ0TVIzRnA2TzV0dTYwZHUva0tQTmVTYnNwUmhvdjhmMUVwYXdPNHkxdzJBeFpSbmhISVhNUWk1QmRtSVZxL2tHWkRsZE9qOTBnZ3VyaFhPSVFuWk1jNFRsVjFsZHk1ZEkwcTFRK2FGTlhFTE1JNXhzVldCM2l2d2dQY0VFWnprR2h6YzVMcVdtTG1POTZ0Q1RhdWRHS1Y5dUNNS2hoVjJhZ3BhMVF6OU1VK1F4Q00vSUR6eDUvYmczOXNvNHhaT0EwanlRdW9WYkJIeVZmSXRjRFVsekcvZEZhY3JyYkp2aDkwZXR3cjNtWjN6ajNLK04yWTVFZGlESlpTbmFiSnZUSk5qMDQxTEZKUzZEMkV6ZDg5V2xjQTJrakgvL3hBQThFQUFCQXdJREJRUUlCQVVGQVFBQUFBQUJBQUlERVNFRUVqRVRJakpCVVFVUVlYRVVJQ05DZ1pHaHNWSml3ZkF6UTNMaDhSVWxNRlBSZ3YvYUFBZ0JBUUFHUHdIMWRQVTA3dE8vVHUwOVRUL2pwVHVzdEZvdE82bmRZS3pGdk5zVnAveWFLdzc5RmNMUmFkMlZyZFZ3ZlJYYndtaWUrT1BlWnZOc3J0ODBkeXk0VndvMlZGWWV2cDNVcDNhTFJjS3EwTE01bkNzeFp1alVwK0ZvTjhWVG8zc3NXNktTSXhISVhtaFdZc1Rzb3Q1TGJ2SEpHeU5sU2kwN3RPK3ZmUlhIeFI2aFVlRTU3QWlLWHFnN0RVenNLMkFieXVqMmlXY1IzZkpYQXpVVzJETGhFWkxKc1RSdTEza01Ld2FxdEZ0R3hXUE5GcnU2cVBxMjd0NXRVNXpLZzlGbDFzaXc4d21sd3NkUEZOWlRqUWJHMm9lZ01sd3dCQ01pN3lnUjAwUkxXWFRubUw0MFhCdzIwVko3dFRvSW90NFhiWmJjMHJuOVhUdkZRZzZud1dkZ3FlaU1zUjVhVVJxeXZSWmpGUXByWTJWYzByUE1QbXFTdHJUU3FjNU94TThkaG9oSktOZVhSVldWN0FpZGxUb1VXT2JkWjlseXVxRTI2ZXBSVm9xVVdVTlZIQ2l5QUIxT1lWSFJrc1hwd2JUTlFGcWRqb0dpMXgwWHRHK0pXZkpaSEtLRkhtTkZYVmJmWlZ5V2FPdmlzenpTamExVllXNm1pckpvaFJ3cXRwR3o1ckpsMVIzYkErcm91QlV5NnJNMWhXWnpMb1AyWnFFR3hzdm1UWTN0MTFSYzVvb3QyaXlrODZKcmVLdWhYcFR4YjdyWnpHemhjSjBRZlFVVzhNeUxReWxPcWJHUjhrYTNJVmVxbzByWDFCVldWWDgwQlJiOGYwWHNnZ0ExRjZKYWFVUWZXbVd6Z1B1aTk3alFpLzZKMDNhSnBEV3pUelh0VGFtNUdEYWlrZmhaYXV5YnplWVQzNGMrMGhyWTh3bnRsczVraGFXbmt2UzhRMFUwdjFSa2phMXBXMHIvQUZMYzFJMDZMTTYzbjZndG9nelZiTEttT3BxaXdsYXNjUE5EUEdhZFFzNTBwWkZydEJxaTJoQS9LaTJsUTY3RFQ5L0pPeE9OZUFOY3BSUHBHVmpMRU5Yb2NFVkdIK2I3eTlLdzA5WFpSV3BzOGRDbmRxOWpzMmVLamlKbndydWRUU3YwVTJOZ2oyVDlKV25rZkZIQ3VpY0NHK3paVFR4VHBKSFZrSjYxeXJhU25pS004cG8xVmhJSjZlb0hQQzIyVFRSTmV6M2RVVFR4QzNRZkZVRDNCTWkycGUwblEzUkQzRHFpQkJWR1FzaUJCMDFVYkl3NFo2Q29GbXVSZ2ZqRG1KcGR1blR6VXBmRG5acFBDL2wvWmJIWVNnSGVaSXgxcitCUmxaaUhNSlpTOGFpdzBZbzg3NGxoOTBja1k4SzZOOHpUN1dXT2xENGxmNnQyZkZtY1A0MG9DSWRFNjVXMnhrZ2JsRzZ5cXpRdXNQZ0ZRejV2QnQrNG1taURVNXpOV2haNUdvK2FCYUJieFRaM05xMVo4NEhVQlppNFY2bENiQ1NPREdHN0FiVVIyanMxUlZnekl4Ung1ZkFLR1pyc3NoTVpFZko0b015ZkZUWnVFN1RrTHFBVnJ3bjlDdlRtNytUcHJYbCs5RUh4VUxuYjJVamgvd0E5RVdpQWFVSUxkT3FmRkhoOTkvOEFFZEZ4TzhCMEMzTU9jT3pOV2puai9KWG9QYWVYRU5sM1MxN2FLUi9aRTdSTEhKUjBiYldSa3hHSWtvUCtxTVVUdjkyYzV4NFc1RVozWWw1Yi9UcXJvbGl6MVdSNTRyTEtORnVCQmtobzNRbWlFR0lscUJKa2UwdDFDOUl3SU1rYm5VRHVuZ3FWM2hxT3EyTW5ENHBzMGRuZ21ua2hpbmJyWDZlSlVmYU9FdStPSndvRHl5RUQ2cjJremNveExHTkFyWGRCcWc5cjZSTTk1OWQrcUUrT3hJazNpUlNNTnI4a0pEZ0FCT1NPVnFJdHdyOTA4cUg5Q25oa2tRdnZaYkorT3lrdTBieXFkRkJCaG5NWTJZZTBvTGtuUkhGNERFRmxkQ09hcDJ4MlMwa2Z6b1JsS2JCZ2UwQTNubGNRRFJWQlZrd0VJV1ZCMFZUMFJFZ3Exd29vNTJHcjh0R3lEMytueFUySHhjVzBidEcxYWViU0UvRVlDVE15dGFjMitDYzJkcEJVZUdrSHM5bUc1dVhVcG41SlJVK0N4R05GUTk1T0hpRmZqWDUyUjdOWmN4d2d6elA0V2RkT3FqMlRnR05PV3d1YkM2aGxicTExWkZGSm10SGlTRDhRbzNzZHpGVk5FOXJYMzBjRkZCZGhkeUhnUDdnS2JhdzVtNUd0YVFMZzYxK1NraXh6QVlJMkF4dnJySWRQTlJGemcrcER3YzFLai9DZmhjTDdrY1dRam1Nb1FlRDVvWlRiUk5kS0xpeUFDK3l5dFBtcW1UeVVtQUxnNk41emYwK0tjOThSMnNOTm8wZSszcWppb3NTWXlOYzhkajArQ2F5VFp1RG5mMU4rQjFDbWZFMDBlYVpUdkFwak1WaVlvbzMzRDN2L0FBM29wZXpNRUh6djlITFdTUWpkTkwwQi9YeFdGN2FubTJNTGg3U0FIUndzZm9vOEd3RytRMUk1Qm92OGsxN24xdWMzeGNWam9CcTJmTXdlUVFqZWJaZ0FwSVJjQ2xQblJNaHdwcVhUYk9JZFJ3ajY1ai84cDB6bVZ3bzRQRy83Q2o3SW5PN0kvYVp2d3RCMVVuWWQzc2plQzV2Tm5sLzRvZTFtU2s3YUhLL3dMSEZ0UGtHcmZLYVc2ODFtVFEwb1Y1TnZST2lrbHQrTUQ3clpPaTR1RndObkoySXhFd0w2TDA1a2xIRUZ1dXJVMjdTMytISWZ5blEvQS9kUGVIN01RTmNYRzlxZnNJMHg4ejhuRlEwelBPZ3I1WFhvK0htejFPL252bnJiNUtQSHdUUDlFZklDQUhYaFBUeVdLN01iSU5kdEVCeXJvNExEdng3blo0NEhBU1YxSXpEN1BDampxRG15Q256SlFuanFXeXpIYWcrN29nWWp1T2xCWjVMRll5dTlXalB0K3FZWXBjb2lEbXRmK0hrWGZBZlZ5SFovOE9GdEJXbDIrWDVsc1kyTWJKaUp0eDR2czJONFFGaWUzbXV5UE9GWk1YZmhjYU5wWHJVRXFRWWR0VERpUzVuaUgzK3RlNXFvd0xheU4wMFZIU1phYUl5azM1dkdxSXFQdlg0SW5EWW9OQTBhNDIvc3ZRWnc2TjVPNVgzazdDNGwyYmFOT1VBNmRRZnVwZHBkczJ6WTV1bWhPYjdCYlhCUDlsR2Q4L25LL3dCendqaHZWQmlONmU2ZkpQeHNHSVppc0hMSnZsdnVlZlJSWW5CUDlwQmRyZW9PcmYzNHFQMmVhS1ZoTEIwUE1JSENFNVFLN01qUk5iRytnWnVtdWg2cHdqTjI3elIwOEVaQTZ6cnJKbXlRdHVUK0lyWTRUY3FLTkk0cjJVV0ZnWnV0RGpFLzhBcnk2bTJwWG9lM0JmTGpTWXI4Vk5mTlpacEN3aG1TVStBNFhmY2ZKYm9XYVJDeXlFaHBwcXF4dkJ5NjFLeTBMYTZGRXVuZHUzTlZuejZYOFZzMjRBbGhPL283TisvQlJNd21ka2ozQXRpbDBxb1g0VHRLTnUzYTUyM2FhaHRUZjQvK0wwSjNhT0RrZ0p5N0U1aFh6SjFLWjJqZ0tGalJUSjA4RVo4SVRHYThoZnlQVlZlTmk3bUdjSitISlJWbWIxdDFycXBjUmg1bU9PZWd5T3FXckk3Nm92cXNyTER6UmNaTDlVK1pzckhiT0l1YlYxTDhrR1BjYU5Zd1pRS0RSWUxBR3VXTENWa0EvRTV4TmZzakt4N1E0ZThPZnc1SUZDZytLeVV1clBLRXdHOE5hcHNyTVMvSktMYjNDUXFPZUhFdW9TUXFIQ3htdm1uNU1QQzFvZGNsbGI5RnRJbU5kakFSa09TMGJqb21kcTRUSHVJWWNwYkp6NW80VHRmc1p1MS83SXpRL01MWXc0cDh1RmYvQUM1YmxxMmtaMU9sTlVYTWNNdkxkVE40OFA2cHdtM21IaXNuRnJ6VG9zelZkSEtiSDZKN1hIV2dvcVNNSjN1cW1kdlZxSXhia0xKc2pMbWxlSHF2LzhRQUpCQUJBQU1BQWdJREFBTUJBUUVBQUFBQUFRQVJJVEZCVVdGeGdaRVFvY0hSc2VILzJnQUlBUUVBQVQ4UU9tS1JEY1haWm02cmZ3NFlSMTVIWElkSndvWmN1U3E2aFhrbXlWdE9kamxvVG0xQ3NNVzVZaS9rTmRJc3hKWHlSYlNwYnFEZXJsK0RpVTNCVmZQK01idGtSWGxNR0tpd3NkMWtCTG1NWlhqNXFlTy93S2xraXdjaGkvZ1pjeDZoL2syK3Y4UmF1ZndVMkVZWGpVeGJjdjFEQWgyeFlDTCtPRTVZOUNKbWxiMlRxVVBicXk3YkhSZ2Jjb3BocUVKZGRFVFlPdzVSQW54QldkZFRSaUFwR2ZUNG1DSUROWXNCMVZMSTFneEdiQjVxRm4wa0g1UllKeXhuVURkTUJkWWdPT1pkd29LSUM1a1dKZVNzN0NJVXRDaW9ZOERVdkNGeHVVelBYb25La1pGYWhOOW9KSUZyQTM4VEpzZ01PZ0pMZHo2aStqWHVNN2R1TEdjYkhpT0RNbnhDb0RPZVE1SVlPRGpHVVlGOHkxa1JVSFJHeDI1Y2p1and6aDJHMXZiNjlRYnR2UytwWU9Vb2FnZHFucmx4UkRmTXgwbFMrbWsxeEtSZzNiMTZsbHd3MHdITk1zaTE0alVVdkozRE40clJrSjBGdW1rRHdwcnh6S3dwcWlINDJyeno1aWFYY1ZFT3RXQTQ3aUZVQm5VQ0wyVXI4ZU5SNlc4SHRCNEpneGR0RUtoWXVrZ0xRQ0VEcHVEWmhDQWhVc3ZzQ3hpTmdxZ1haVS90UnZPRG1DQUJkRTFrcUZoMTdsNXhwYTNBNmxZQWhwa3R5bU1lc3pOSXZnUW9JU3IxMnZTRzhIWTFPd1YvaEVtaTNqdzZpbGFzZzBxcEJvRmRnUXBPc283blBnS0w5aXF6Z25TRWxlZUdVcHBsOWpNUjFoMzZIeEZQQ1lBdHJPU1c2QnpsSllNWFJPb1g0WWVOOXlyUXpCcGpNc1ZzU0kyam9xR0FKamxUWGpnQ1dyVnVrMkxXTFBaZFJhWTArSWN3NW9KczhNQWNEbHNMRmcxN1k0K0ZhT29iVW9MVmwrZlVrRUpyOVFwVmdjeFJkZHNBV3dodFNuQ2kxaEI2eXppSFNJb2VUSTFqM0JMakFjVHFHWENsbVdkZzRxVldob3FOYXZQUnd4d3REWkdTNUFiOXErSURNQlg2THlVUmVwWGR5NWlGL1NNc0JaRkZHeTZPOHdlVkFvWmNWSVlxRlA3RUNoMmkvRVMxWlFFWU1nMENONitzbE45d0RDMWFCV2tkSG43a1daWkwvZE50a0ZFQUNHN2diTE9XeGJLY1BoTzEvTHB1bCt4S0FVNWFIZ2pWR0VmdVh5ek50NitUbXFqVnVWSDBMbHpQZGcyb1EyLzFIOFF2MDlCQU5sRFlQTUp4cFlZUFdRTkRnNTl4RDBtbnFwOHdJV0dGVjFDQ1Q0UVdLVzl3NEhRcGZjVkY5NVlmcW85R09ZVzREVVBZOWlmZDFKVDNpcjVmTlIvR3FIMjVMNUh1QjhjRnhEdjlSYWU2bjAvNUJKQUxPYUxyR2NJNWJ0SDIrR2ZMTUlHRHppaTl4MWFRYkR5NHg4U2dIWjlMeVBMNGhYQkMxUXYrdzdDbEpDNTJhWHovQU1sUXdvM1JWOHl6OTVHcTJYSFNWRWJFM2Ewb0JMTlVJcEtrWUlpeGRSNGZGTHlHc2dXRkF1QUxvWTRwZzM2dExUWHpDYUVDYmg4WEVUQXA3MHJQRDVnL1NXMWY0Qys0K1VWcG80VVg4UFRWeDFqQkZRdEtNckxMNUdHSm9GZ1hRVUZIejFGR1dyVEt5NWlGYjhzN1hJWVRvL3lYMU9UUVR1dkh6eXgvRlp3VmR5QTNCbjRWMis1YmJIYzRQbmVZNk9rYjRuUExMbFBlSHpGT2I1d3ltTWNNb0ZOVG1OWU1uZmZ3UXVWNlRMWDNEU002V2ZrRlh4WE9ZY0laVWFob3RGWjJXaTR1cjd4dU1EOGdMdVhZVEJLS0tuS3JwNzRpL0p2MkJXY1h6eGU0NmxxYkVWNUJ6UW5KcWdWcjNxTit3Nk8yakJQQW43NkNiYjhHV0ZqZHRHL1M5OXhXajlBbCszc2xhbE5iaVB5Mi9jYlNEakhJcWVEKzFHdk5OL3RRRUt5WXYvV0hOelJRUG8zQTRqRXhqMHhGbHZZYUJZbW9QL3N3eVZrRzQrYm40aG1EMXFCYlZzVm1aMndxTVdzUytTS1BKWDJSSGVkeGtOa1U5ZEdLb0xCNlpMQTFGSzNnUSsrNHNndWVNajFVSlZnUDByclBqam00S1Nld1VJV2dORnI2d0k5YUNWTVhsblBscUMvQzFxczI2MjcwWXJibGVYZThKKzZtKzI1YTdIMmpFZnVnUlFsRFRldk02emhOcldmWUxzdDNNS1NQaHp6Nll6dC9TRVMxdXN2MGwrNWJ0d1FVdE9jZjJBMXdRdVoxZFBNWHlCdUdMdzZqZ2FhN0VkTmtXS3hwM0NLMjY2UzllS1o5VkFQRlFjc0gySS8yUzZmUHkzbDU5UUtyN0RUNjdKejlJNWgxTDlMUGNtSjRUbGZLVk12YUQwU1QxSnJpNWVXSzNkS0RBNktPWXBPTWNWZlpWcFJ2bkRKWjJ4Yzl0dEF2ZFpCdmRXSHdmOGdjUWZOUE1zNG1iWGZrcC91Q3NvVVN3c1ZSNnVTcEpoMENHbjQxSHVDYTFpcVgwRy82aEtBeEJJOUx6eXgrSXBZVTJMT0JJYjY2aEcrNndyMG1XRkZtTHdGdVJRcjF3akhnWW9iY0VTeDZnSWhDalY0VGdxZ3QycUh5MzlxSldnTGlicVE3SDRqQnFCQ20yM3AvQmozS1pjRXI0MHlPSEtyQ09DSGFEbFlndWNRQ25naW9uYUNlbk5ud2hEbGV6c091dUhPQUY4bXo0WUN4S0UzUlY5VVN3TG4zb2l2eTRqbDlnNW5CYUljNmdINnpKcDZWb05uMzlXQVZnVWpVdExQYTFUd011RXJaY3N0bmx6MmtRRmdnemE1ZWFvZkpJckxvUmhXYjdueXlrdDB5WU0yeDVqV1JKNDJNZFRYUUxZcElUVGRMOU92bUR4QXJ2QjhqRUt6ek9keVBRMHFKR3NySG1CNGsyZEY5L2wvSlJRdmRMQnI1MXhEQllqbmExOUJhcm81WmxxY0tRQW9iVnNXUjR4S0wyMER2Ui9lVGJpNDVZQVg1WGhEdFkxK2Y5cnRxOWwzNmhCNU04UDhBOWt2ako1SEJUNm81bGt4aXZzeDFMbjdBSWYyOFB0OXQrWjhyVXoyQ3dHUktFUEs0RG9WYW5LQVdpQW5rWDRqVEM0dDI3bmI5VjZJTThZcG9pLzB0OVNrcEVZYkNyZ20xblVUc0V3bVoycmdwQjZKZlhFcWhDN1JaZXVueVI3NFU0L1o1WHpudU52Vm1JNkV4SDFOWWVZV0VkNU5BZUdOWEFtWVVrK2F2dUt0N0JlYkxIeFJhL256Y0ZEeStWUjVGWDRZM200M1Z4UWVSNzVoWG9qMnJTWGtiVVBMVGFzczBiM3lJSHJScng4VGVYL1VxNFAyRlJZQmNyZjZaVTNYWHp1MzROWktVZ255YWhjMnRBMjEwK09uNWVZQUd5RGN1VDFycS93QnhoMWNDNzZpSGtBdFJ1UzVXYkZ3bXd2a3RoNXY3TUFvQnlJdnV4WDVnQ1NNVzFUSVlha2ZqbkwzTjN4akZQaUxFQzVZZ1AzakVxa0s3NGhRQ3JwVi9HcTMrNmpGOHRLK3daWDRmbVdCYm8wTGxMQi9TTmFwVXJ5eTNBaHE1YUFBdW9hUjd2NHlFbVVCcGR2eW5qeWNSZ3FYUWd1aURoK2VZbkQxR3R2Ti9xeGlVaHQrYmgrMzhzc05oRGNGbkhETEVodVBKSUxOMWlNb1VIcHlsVnJ5YksxalIxbEFDYzJqS28rTmtTemZuWHVGT0tWWitoQWFna3AxckxYanlYZEpURlRBUHRkc1hXd0hNTjV3V01OYzQxYzgzRWlEZ0N0SFp3NUxuMWN2RGppb3FzbWxzNFBpNGpFNHY5ckFEMi9rQlpsTTlhd3RPYjRJUm1Bb2lib25IRnVWREYyQ1psTEUwL1dZT1QvQXJ5ZW8xVkNoU1N4ZFAxb3p2S0NBTmZrTUhBVjZ1MUt1YU5NTmwwNUhZQThvWDdPWWZWckxLVWFrOHYrUTFwTGkzaHVWd1lNUG9ZeDFoTExrSHdmVThxLzN0VWgzNG4vL0VBQ1FSQUFJQ0FnSUNBd0VCQVFFQUFBQUFBQUFCQWhFREVoQWhCREVUSWtGUklHR0IvOW9BQ0FFQ0FRRS9BSXZoUC9ObXh1aHpOelppbUtkaW1LUW1KaVphNHNqd2hQbTJXYkRtV2JVT1p1S1lwaWtSbVJZblJGaVluZks0VExMTml6WWNqWWNoeUhJc3NUSXNqR3pCaWMreWVCcU5vaEZpaTBKRFl2UWxmQ2tLWW5aWlpZNUljaHN2aWhRTkdqRGp0bUx4N1I0MktwdEVjQ2FINDJrMlBCOVI0bW1hT3JFaElTdEd3cGtaQ2xTTFpZMlh3MHlFYlJESDBMRVJ3N0l3ZUlwUjZJNGRZVWVOaDd0a1kydXg0VkllSWVHMlpjV3NhUjhSSENmSFJac1JrUlkyWHdoSnNVYlJqeFZaaWdLQmp3ZG5pNHRTV0VqQlJSNkY2SEcwUEVUd3VUSStML1JZRlQ2SllTeUxJa1h3K0lxeUdOZWpGNDdrakg0dExveFlLWXZISVlFbzBZNHBNVHJoSzJMaXhVeWtVWkkydUlFU0pIMEpYWnFRZ1ljVm93UmxqbDJZb1I5aXdwT3hSL29sUlloc1FtM3pCY3BJcGtlbVJSRXhRdEVjRFBnZGtQSE1XTnhNY0RHbHIxeFNHNkxRdWRoY1Evd29rVUpFRjJZSXBtS0NJNGt5R0pDeHhSR0tvalNMUWlTWWlDZjd6MDdvVXZyWWlQb1RFNzRTRkZpUmppN1BIaVlvVzZGRHFrUVRRclpGUGxTR3JSR3RoOUh5SXY4QVVmdG9xNGlkQzljUklvakVVQlFNY0dlTGhUN0lZOUdKZEVrS3lGaUxvMlZrWnA5RDkySXBJU3ArejI2RTFRMTFhRmY1enVrdXlNQlJSQ0Z1akZpNlBGaFNFaUs2TmJaR0tIU1FwdTZKTTJNY251T25aaWJSVm9TSDdHNUppbXFwbUZ5Y2JaMmRmMG00L2pNY0V6NGpEaTdNT0cxUkNDU0VXSjB4cG85alExOVJxbVJoYnRDaGZzUzFaQ1hYWkRxYnRqZjhHS2tMSm82UThqcTBQSW11MFV2d3h4STRrMFlzUmp4ME5VdWE2c2krcUhEOVhEVmREaTVNd3hTdGluVG95dC9oQjlEOWlkTWNxYkUwaVRpWS9Uc3FtWm9WS2tZNGtJc3h4cEVGVEdpUWlGOHJzMHNrb1kyazM3Rzlla1NVbTFNbWxxUmxVUzNURmsrNkZLNXNiZmRIN2I5SDVRNlV0VHlPcEtTTWFveHBNZ2lKNkc0dDB6U1YwT0xpckl6Yk5oTnBzak4vMHlaZHVpTGFsMmErNGl2UkptelNQbWFYUTUxa3RFWjFiRlBxeU0zTjErQytrTC9vMDBuTC9oT3B3YVg0UlJnUkVVYVE1VWZVV1NNRVRudWlEYWRNalJLRHBqYmd1aGF5OW8rSnRXUnYvd0JQWFJOSytpVGV4Vng2NGMzVkl3c2xOdWtMdng2djlNYklvd1ExUkN1SnV4eWFKV3lMa0xKSkwwUXFYWnRETGl1TEZVZXI2SGpwWEVUY1MwMXhQVjNxTkVSR3BoVmRrcHMzcEtKaWFsMlFpWTFTSXVoeVpKMnJKcHhWanZzVEl4YllwTEJCaWs5YlE1OTBZc212WDRTYVJ1U2xTczNyMk5jUlJTUFNJTDdEVWRtUnFDUC94QUFuRVFBQ0FnSUNBUVFDQWdNQUFBQUFBQUFBQVFJUkF4SVFJVEVUSUNKQkJIRXlZU016VWYvYUFBZ0JBd0VCUHdEMzdHek5pemRDbWJDYVpaYk5sd21Yd256dFNObVd6Wkd4YkxSWnNqWTJMWW1XSmlrL3M5UWpKTkNkQ2tKb3ZoTy9aYUxOaXpiL0FJV1h4WlpZdU5oZVdRZE1jNjhDeUtpTTFRbVJZaE8rTmtXaXkxUTVNdCt6d2hPMExzaTdReEM1VjJSTnJrUlltS1M0c1Q0dm14T2p5UGhLMExpdWloR2hWY1JYWWplaFNMNVQ5bEYwajlIYlFvaVhRa1JpeDQyT0ZDb2owT0lvTW90OEpqNFhLNFpib1VSSVNGRjJRZ3lPT3V4UUhFMHRpalNFa3hRSlFORU5kRmNvWmRDa2l5eElSRkppeGZZb014UVpxcUtvOG1sS3h4SC9BRVJkRW1obzBIRHNTZGxjUHlOaUVtVUpDVmtJV2lFZWhSRWh6Sk1pTHdPQ2FIRlhSWFJOOWtHV2hpWEw0aVJYQ1JHSkNJbFEyS1ZrbzBWWGtVcU5rUW5jUlNibTBMd05iU0ZHdXhqOENWbEgxeCt5S0VJaVFWTWloanFpQ2RucDFDMlRkQ21YOWtQNG1OWE4zd3F0a2VKY3NaR0lrSk91SW1OTnNWSmRqbWtiaXlKR0xKdkdqSXFiUnJVaEVlb2tmdXVJb1Nvc1h5WjQ0WWhJU0lScENnS0taamNZazIzMmg5OGRuNDhxZEdXTnEwYWlUS1NpS0xSQjB6SjZhZnhHeVQ4bUtIVmt1aGNKRUlXUmdhS3lMcDlqV2o2NGpPdWh4VWtWMnpXMTBRZFNNZnlpU2lrU2t2VFhRcW9kSGF0bmxEc3g0M2RqZEtoK09WRk1oR2tMaHhzcDAweWtob2k2ZEZXaFdpNUg0MHJSbFhYUlAvV2lDc21pZmdpblFvTnM2aW1SZHF4b2FSUkJJanhaaitYSDZFVVljTTVwdjZSVVY0Skg0M1RvMXREamNXakRDNHRrb1dUWFpHRGw0SXdVVTJOdXJNS3ROTWtob29pdU5oZG1HVVlLbVZHVHMxa2wwZG16SVpaUUdsVzBmQlZvZ25HUkJKbzlKdDBZOFZRWThaNld6WXNYcHhzbjhwVWhWNEkzR1hmRTBhc2hGVU9CbzBoV2kySnVLNkk1QkRnbWFha0o2Uy9vVUUxY1JZMFl2QmppcUZGS3hZaU9GUk05VVB5eXY4aEpjTkNWUGl5UHlOVU9JOFpDRGJveTQ1NFpVeU1pS1VqMGtZb3VERWt5RUpRZlpCM3dtaVU2UmtkaXhpZ2licGV4SW9pUlJxZkVpblpjMCsyUlVaSzJpT01oQVdNZ2tWOFNEbzJZNXNjbXlLVEdOMGZ5NlAvWiIgaWQ9ImMiIHdpZHRoPSIxOTkiIGhlaWdodD0iMTU5Ii8+PC9kZWZzPjwvc3ZnPg==", + "description": "Indicates the concentration of airborne allergens, including pollen and mold spores, which can trigger allergic reactions in sensitive individuals.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'IAI_level', label: 'IAI', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"IAI\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 12) {\\n\\tvalue = 12;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:flower-pollen\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#3B911C\"},{\"from\":2,\"to\":6,\"color\":\"#7CC322\"},{\"from\":6,\"to\":9,\"color\":\"#F77410\"},{\"from\":9,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#3B911C\"},{\"from\":2,\"to\":6,\"color\":\"#7CC322\"},{\"from\":6,\"to\":9,\"color\":\"#F77410\"},{\"from\":9,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:SUFJLXZhbHVlLWNhcmQtYmFja2dyb3VuZC5wbmc=:SUFJLXZhbHVlLWNhcmQtYmFja2dyb3VuZC5wbmc=;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"#FFFFFFB8\",\"blur\":3}},\"autoScale\":true},\"title\":\"Individual allergy index card with background\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/individual_allergy_index__iai__chart_card.json b/application/src/main/data/json/system/widget_types/individual_allergy_index__iai__chart_card.json new file mode 100644 index 0000000000..bb6bd8757b --- /dev/null +++ b/application/src/main/data/json/system/widget_types/individual_allergy_index__iai__chart_card.json @@ -0,0 +1,34 @@ +{ + "fqn": "individual_allergy_index_iai_chart_card", + "name": "Individual allergy index (IAI) chart card", + "deprecated": false, + "image": "tb-image:SUFJLXZhbHVlLWNoYXJ0LWNhcmQuc3Zn:SUFJLXZhbHVlLWNoYXJ0LWNhcmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSIxOTkiIGhlaWdodD0iMTU5IiByeD0iMy41IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC4xMiIvPgo8cGF0aCBkPSJNMjQuMjY2NyAyMC41QzI0LjI2NjcgMjAuMDU4IDI0LjA5MTIgMTkuNjM0MSAyMy43Nzg2IDE5LjMyMTVDMjMuNDY2IDE5LjAwODkgMjMuMDQyMSAxOC44MzMzIDIyLjYwMDEgMTguODMzM0MyMi4yNDY3IDE4LjgzMzMgMjEuOTIwMSAxOC45NCAyMS42NjY3IDE5LjEyNjdWMTlDMjEuNjY2NyAxOC41NTggMjEuNDkxMiAxOC4xMzQxIDIxLjE3ODYgMTcuODIxNUMyMC44NjYgMTcuNTA4OSAyMC40NDIxIDE3LjMzMzMgMjAuMDAwMSAxNy4zMzMzQzE5LjU1ODEgMTcuMzMzMyAxOS4xMzQxIDE3LjUwODkgMTguODIxNiAxNy44MjE1QzE4LjUwOSAxOC4xMzQxIDE4LjMzMzQgMTguNTU4IDE4LjMzMzQgMTlWMTkuMTI2N0MxOC4wODAxIDE4Ljk0IDE3Ljc1MzQgMTguODMzMyAxNy40MDAxIDE4LjgzMzNDMTYuOTU4MSAxOC44MzMzIDE2LjUzNDEgMTkuMDA4OSAxNi4yMjE2IDE5LjMyMTVDMTUuOTA5IDE5LjYzNDEgMTUuNzMzNCAyMC4wNTggMTUuNzMzNCAyMC41QzE1LjczMzQgMjEuMTYgMTYuMTI2NyAyMS43MzMzIDE2LjY4NjcgMjJDMTYuMTI2NyAyMi4yNjY3IDE1LjczMzQgMjIuODMzMyAxNS43MzM0IDIzLjVDMTUuNzMzNCAyMy45NDIgMTUuOTA5IDI0LjM2NiAxNi4yMjE2IDI0LjY3ODVDMTYuNTM0MSAyNC45OTExIDE2Ljk1ODEgMjUuMTY2NyAxNy40MDAxIDI1LjE2NjdDMTcuNzUzNCAyNS4xNjY3IDE4LjA4MDEgMjUuMDUzMyAxOC4zMzM0IDI0Ljg3MzNWMjVDMTguMzMzNCAyNS40NDIgMTguNTA5IDI1Ljg2NiAxOC44MjE2IDI2LjE3ODVDMTkuMTM0MSAyNi40OTExIDE5LjU1ODEgMjYuNjY2NyAyMC4wMDAxIDI2LjY2NjdDMjAuNDQyMSAyNi42NjY3IDIwLjg2NiAyNi40OTExIDIxLjE3ODYgMjYuMTc4NUMyMS40OTEyIDI1Ljg2NiAyMS42NjY3IDI1LjQ0MiAyMS42NjY3IDI1VjI0Ljg3MzNDMjEuOTIwMSAyNS4wNTMzIDIyLjI0NjcgMjUuMTY2NyAyMi42MDAxIDI1LjE2NjdDMjMuMDQyMSAyNS4xNjY3IDIzLjQ2NiAyNC45OTExIDIzLjc3ODYgMjQuNjc4NUMyNC4wOTEyIDI0LjM2NiAyNC4yNjY3IDIzLjk0MiAyNC4yNjY3IDIzLjVDMjQuMjY2NyAyMi44MzMzIDIzLjg3MzQgMjIuMjY2NyAyMy4zMTM0IDIyQzIzLjg3MzQgMjEuNzMzMyAyNC4yNjY3IDIxLjE2IDI0LjI2NjcgMjAuNVpNMjAuMDAwMSAyMy42NjY3QzE5LjU1ODEgMjMuNjY2NyAxOS4xMzQxIDIzLjQ5MTEgMTguODIxNiAyMy4xNzg1QzE4LjUwOSAyMi44NjYgMTguMzMzNCAyMi40NDIgMTguMzMzNCAyMkMxOC4zMzM0IDIxLjU1OCAxOC41MDkgMjEuMTM0MSAxOC44MjE2IDIwLjgyMTVDMTkuMTM0MSAyMC41MDg5IDE5LjU1ODEgMjAuMzMzMyAyMC4wMDAxIDIwLjMzMzNDMjAuNDQyMSAyMC4zMzMzIDIwLjg2NiAyMC41MDg5IDIxLjE3ODYgMjAuODIxNUMyMS40OTEyIDIxLjEzNDEgMjEuNjY2NyAyMS41NTggMjEuNjY2NyAyMkMyMS42NjY3IDIyLjQ0MiAyMS40OTEyIDIyLjg2NiAyMS4xNzg2IDIzLjE3ODVDMjAuODY2IDIzLjQ5MTEgMjAuNDQyMSAyMy42NjY3IDIwLjAwMDEgMjMuNjY2N1pNMTkuMzMzNCAxNkMxOS4zMzM0IDE1LjYzMzMgMTkuNjMzNCAxNS4zMzMzIDIwLjAwMDEgMTUuMzMzM0MyMC4zNjY3IDE1LjMzMzMgMjAuNjY2NyAxNS42MzMzIDIwLjY2NjcgMTZDMjAuNjY2NyAxNi4zNjY3IDIwLjM2NjcgMTYuNjY2NyAyMC4wMDAxIDE2LjY2NjdDMTkuNjMzNCAxNi42NjY3IDE5LjMzMzQgMTYuMzY2NyAxOS4zMzM0IDE2Wk0xNi42NjY3IDE3LjMzMzNDMTYuNjY2NyAxNi45NjY3IDE2Ljk2NjcgMTYuNjY2NyAxNy4zMzM0IDE2LjY2NjdDMTcuNzAwMSAxNi42NjY3IDE4LjAwMDEgMTYuOTY2NyAxOC4wMDAxIDE3LjMzMzNDMTguMDAwMSAxNy43IDE3LjcwMDEgMTggMTcuMzMzNCAxOEMxNi45NjY3IDE4IDE2LjY2NjcgMTcuNyAxNi42NjY3IDE3LjMzMzNaTTE1LjMzMzQgMTZDMTQuOTY2NyAxNiAxNC42NjY3IDE1LjcgMTQuNjY2NyAxNS4zMzMzQzE0LjY2NjcgMTQuOTY2NyAxNC45NjY3IDE0LjY2NjcgMTUuMzMzNCAxNC42NjY3QzE1LjcwMDEgMTQuNjY2NyAxNi4wMDAxIDE0Ljk2NjcgMTYuMDAwMSAxNS4zMzMzQzE2LjAwMDEgMTUuNyAxNS43MDAxIDE2IDE1LjMzMzQgMTZaTTE3LjMzMzQgMTRDMTcuMzMzNCAxMy42MzMzIDE3LjYzMzQgMTMuMzMzMyAxOC4wMDAxIDEzLjMzMzNDMTguMzY2NyAxMy4zMzMzIDE4LjY2NjcgMTMuNjMzMyAxOC42NjY3IDE0QzE4LjY2NjcgMTQuMzY2NyAxOC4zNjY3IDE0LjY2NjcgMTguMDAwMSAxNC42NjY3QzE3LjYzMzQgMTQuNjY2NyAxNy4zMzM0IDE0LjM2NjcgMTcuMzMzNCAxNFpNMjEuMzMzNCAxNEMyMS4zMzM0IDEzLjYzMzMgMjEuNjMzNCAxMy4zMzMzIDIyLjAwMDEgMTMuMzMzM0MyMi4zNjY3IDEzLjMzMzMgMjIuNjY2NyAxMy42MzMzIDIyLjY2NjcgMTRDMjIuNjY2NyAxNC4zNjY3IDIyLjM2NjcgMTQuNjY2NyAyMi4wMDAxIDE0LjY2NjdDMjEuNjMzNCAxNC42NjY3IDIxLjMzMzQgMTQuMzY2NyAyMS4zMzM0IDE0Wk0yNS4zMzM0IDE1LjMzMzNDMjUuMzMzNCAxNS43IDI1LjAzMzQgMTYgMjQuNjY2NyAxNkMyNC4zMDAxIDE2IDI0LjAwMDEgMTUuNyAyNC4wMDAxIDE1LjMzMzNDMjQuMDAwMSAxNC45NjY3IDI0LjMwMDEgMTQuNjY2NyAyNC42NjY3IDE0LjY2NjdDMjUuMDMzNCAxNC42NjY3IDI1LjMzMzQgMTQuOTY2NyAyNS4zMzM0IDE1LjMzMzNaTTIyLjY2NjcgMTYuNjY2N0MyMy4wMzM0IDE2LjY2NjcgMjMuMzMzNCAxNi45NjY3IDIzLjMzMzQgMTcuMzMzM0MyMy4zMzM0IDE3LjcgMjMuMDMzNCAxOCAyMi42NjY3IDE4QzIyLjMwMDEgMTggMjIuMDAwMSAxNy43IDIyLjAwMDEgMTcuMzMzM0MyMi4wMDAxIDE2Ljk2NjcgMjIuMzAwMSAxNi42NjY3IDIyLjY2NjcgMTYuNjY2N1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTM0Ljg0MzggMTUuMDQ2OVYyNUgzMy4xMjc5VjE1LjA0NjlIMzQuODQzOFpNNDEuMjI1NiAxNi4zNzNMMzguMjUyIDI1SDM2LjQ1NDFMNDAuMjAwMiAxNS4wNDY5SDQxLjM0ODZMNDEuMjI1NiAxNi4zNzNaTTQzLjcxMzkgMjVMNDAuNzMzNCAxNi4zNzNMNDAuNjAzNSAxNS4wNDY5SDQxLjc1ODhMNDUuNTE4NiAyNUg0My43MTM5Wk00My41NzAzIDIxLjMwODZWMjIuNjY4OUgzOC4xNTYyVjIxLjMwODZINDMuNTcwM1pNNDguNzE0OCAxNS4wNDY5VjI1SDQ2Ljk5OVYxNS4wNDY5SDQ4LjcxNDhaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik05Ni4zODA5IDcyLjA5OTZMOTMuMDA1OSA3MS4yNzM0TDk0LjM5NDUgNTguNDA2MkgxMDguMTkzVjYxLjk5MjJIOTcuOTEwMkw5Ny4yMDcgNjguMjY3NkM5Ny42MDU1IDY4LjAzMzIgOTguMTU2MiA2Ny44MDQ3IDk4Ljg1OTQgNjcuNTgyQzk5LjU2MjUgNjcuMzQ3NyAxMDAuMzY1IDY3LjIzMDUgMTAxLjI2OCA2Ny4yMzA1QzEwMi40NzUgNjcuMjMwNSAxMDMuNTUzIDY3LjQyOTcgMTA0LjUwMiA2Ny44MjgxQzEwNS40NjMgNjguMjE0OCAxMDYuMjc3IDY4Ljc4MzIgMTA2Ljk0NSA2OS41MzMyQzEwNy42MTMgNzAuMjcxNSAxMDguMTIzIDcxLjE3MzggMTA4LjQ3NSA3Mi4yNDAyQzEwOC44MjYgNzMuMjk0OSAxMDkuMDAyIDc0LjQ4NDQgMTA5LjAwMiA3NS44MDg2QzEwOS4wMDIgNzYuOTkyMiAxMDguODI2IDc4LjA5OTYgMTA4LjQ3NSA3OS4xMzA5QzEwOC4xMzUgODAuMTYyMSAxMDcuNjE5IDgxLjA3MDMgMTA2LjkyOCA4MS44NTU1QzEwNi4yMzYgODIuNjQwNiAxMDUuMzYzIDgzLjI1NTkgMTA0LjMwOSA4My43MDEyQzEwMy4yNjYgODQuMTM0OCAxMDIuMDI5IDg0LjM1MTYgMTAwLjYgODQuMzUxNkM5OS41MzMyIDg0LjM1MTYgOTguNTA3OCA4NC4xOTkyIDk3LjUyMzQgODMuODk0NUM5Ni41NTA4IDgzLjU3ODEgOTUuNjc3NyA4My4xMTUyIDk0LjkwNDMgODIuNTA1OUM5NC4xMzA5IDgxLjg4NDggOTMuNTA5OCA4MS4xMjMgOTMuMDQxIDgwLjIyMDdDOTIuNTcyMyA3OS4zMDY2IDkyLjMwMjcgNzguMjU3OCA5Mi4yMzI0IDc3LjA3NDJIOTYuMzgwOUM5Ni40ODYzIDc3LjkwNjIgOTYuNzE0OCA3OC42MTUyIDk3LjA2NjQgNzkuMjAxMkM5Ny40Mjk3IDc5Ljc3NTQgOTcuOTEwMiA4MC4yMTQ4IDk4LjUwNzggODAuNTE5NUM5OS4xMDU1IDgwLjgyNDIgOTkuNzk2OSA4MC45NzY2IDEwMC41ODIgODAuOTc2NkMxMDEuMjk3IDgwLjk3NjYgMTAxLjkxMiA4MC44NTM1IDEwMi40MjggODAuNjA3NEMxMDIuOTQzIDgwLjM0OTYgMTAzLjM3MSA3OS45ODYzIDEwMy43MTEgNzkuNTE3NkMxMDQuMDYyIDc5LjAzNzEgMTA0LjMyIDc4LjQ3NDYgMTA0LjQ4NCA3Ny44MzAxQzEwNC42NiA3Ny4xODU1IDEwNC43NDggNzYuNDcwNyAxMDQuNzQ4IDc1LjY4NTVDMTA0Ljc0OCA3NC45MzU1IDEwNC42NDggNzQuMjUgMTA0LjQ0OSA3My42Mjg5QzEwNC4yNjIgNzMuMDA3OCAxMDMuOTc1IDcyLjQ2ODggMTAzLjU4OCA3Mi4wMTE3QzEwMy4yMTMgNzEuNTU0NyAxMDIuNzM4IDcxLjIwMzEgMTAyLjE2NCA3MC45NTdDMTAxLjU5IDcwLjY5OTIgMTAwLjkyOCA3MC41NzAzIDEwMC4xNzggNzAuNTcwM0M5OS4xNjk5IDcwLjU3MDMgOTguMzk2NSA3MC43MTY4IDk3Ljg1NzQgNzEuMDA5OEM5Ny4zMzAxIDcxLjMwMjcgOTYuODM3OSA3MS42NjYgOTYuMzgwOSA3Mi4wOTk2WiIgZmlsbD0iIzdDQzMyMiIvPgo8cGF0aCBkPSJNMTQ3LjQ0NCA3MS43Nzc4TDE1NC41NTUgNzEuNzc3OEwxNTQuNTU1IDgwLjY2NjdMMTUxLjAwNCA4My4zMzMzTDE0Ny40NDQgODAuNjY2N0wxNDcuNDQ0IDcxLjc3NzhaIiBmaWxsPSJ1cmwoI3BhaW50MF9saW5lYXJfMTEyOF8zNDkzKSIgZmlsbC1vcGFjaXR5PSIwLjMiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNTAuNjc0IDgzLjg5NkMxNTAuODU0IDg0LjA3NjIgMTUxLjE0NiA4NC4wNzYyIDE1MS4zMjYgODMuODk2TDE1NC41NTUgODAuNjY2N0wxNTMuOTAzIDgwLjAxNDJMMTUyLjQ4NCA4MS40MzMxQzE1Mi4xMDcgODEuODEwNiAxNTEuNDYxIDgxLjU0MzIgMTUxLjQ2MSA4MS4wMDkzTDE1MS40NjEgNzcuMTExMUwxNTAuNTM5IDc3LjExMTFMMTUwLjUzOSA4MS4wMDkzQzE1MC41MzkgODEuNTQzMiAxNDkuODkzIDgxLjgxMDYgMTQ5LjUxNiA4MS40MzMxTDE0OC4wOTcgODAuMDE0MkwxNDcuNDQ0IDgwLjY2NjdMMTUwLjY3NCA4My44OTZaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0xNjIuNTY4IDc5Ljc5M1Y4MC45MThIMTU5LjQ2OVY3OS43OTNIMTYyLjU2OFpNMTY3LjgyOCA3NS40Mzk1Vjg0SDE2Ni40MTZWNzcuMTE1MkwxNjQuMzI0IDc3LjgyNDJWNzYuNjU4MkwxNjcuNjU4IDc1LjQzOTVIMTY3LjgyOFpNMTc1LjMwOSA3NS40MTZIMTc1LjQ4NFY3Ni41NzAzSDE3NS4zODVDMTc0Ljg4MSA3Ni41NzAzIDE3NC40NDUgNzYuNjQ4NCAxNzQuMDc4IDc2LjgwNDdDMTczLjcxNSA3Ni45NjA5IDE3My40MTYgNzcuMTc1OCAxNzMuMTgyIDc3LjQ0OTJDMTcyLjk0NyA3Ny43MjI3IDE3Mi43NzEgNzguMDQzIDE3Mi42NTQgNzguNDEwMkMxNzIuNTQxIDc4Ljc3MzQgMTcyLjQ4NCA3OS4xNjAyIDE3Mi40ODQgNzkuNTcwM1Y4MC45MTIxQzE3Mi40ODQgODEuMjUyIDE3Mi41MjEgODEuNTUyNyAxNzIuNTk2IDgxLjgxNDVDMTcyLjY3IDgyLjA3MjMgMTcyLjc3MyA4Mi4yODkxIDE3Mi45MDYgODIuNDY0OEMxNzMuMDQzIDgyLjYzNjcgMTczLjE5OSA4Mi43Njc2IDE3My4zNzUgODIuODU3NEMxNzMuNTUxIDgyLjk0NzMgMTczLjc0IDgyLjk5MjIgMTczLjk0MyA4Mi45OTIyQzE3NC4xNTQgODIuOTkyMiAxNzQuMzQ2IDgyLjk0OTIgMTc0LjUxOCA4Mi44NjMzQzE3NC42ODkgODIuNzczNCAxNzQuODM2IDgyLjY1MDQgMTc0Ljk1NyA4Mi40OTQxQzE3NS4wNzggODIuMzM3OSAxNzUuMTcgODIuMTUyMyAxNzUuMjMyIDgxLjkzNzVDMTc1LjI5NSA4MS43MjI3IDE3NS4zMjYgODEuNDg4MyAxNzUuMzI2IDgxLjIzNDRDMTc1LjMyNiA4MC45OTIyIDE3NS4yOTUgODAuNzY1NiAxNzUuMjMyIDgwLjU1NDdDMTc1LjE3NCA4MC4zMzk4IDE3NS4wODYgODAuMTUyMyAxNzQuOTY5IDc5Ljk5MjJDMTc0Ljg1MiA3OS44MjgxIDE3NC43MDUgNzkuNzAxMiAxNzQuNTI5IDc5LjYxMTNDMTc0LjM1NyA3OS41MTc2IDE3NC4xNTggNzkuNDcwNyAxNzMuOTMyIDc5LjQ3MDdDMTczLjY1IDc5LjQ3MDcgMTczLjM5NSA3OS41MzcxIDE3My4xNjQgNzkuNjY5OUMxNzIuOTM4IDc5LjgwMjcgMTcyLjc1NCA3OS45NzY2IDE3Mi42MTMgODAuMTkxNEMxNzIuNDc3IDgwLjQwMjMgMTcyLjQwMiA4MC42MjcgMTcyLjM5MSA4MC44NjUyTDE3MS44NTIgODAuNjg5NUMxNzEuODgzIDgwLjMyNjIgMTcxLjk2MyA4MCAxNzIuMDkyIDc5LjcxMDlDMTcyLjIyNSA3OS40MjE5IDE3Mi4zOTggNzkuMTc1OCAxNzIuNjEzIDc4Ljk3MjdDMTcyLjgyOCA3OC43Njk1IDE3My4wNzYgNzguNjE1MiAxNzMuMzU3IDc4LjUwOThDMTczLjY0MyA3OC40MDA0IDE3My45NTUgNzguMzQ1NyAxNzQuMjk1IDc4LjM0NTdDMTc0LjcwOSA3OC4zNDU3IDE3NS4wNjggNzguNDIzOCAxNzUuMzczIDc4LjU4MDFDMTc1LjY3OCA3OC43MzYzIDE3NS45MyA3OC45NDkyIDE3Ni4xMjkgNzkuMjE4OEMxNzYuMzMyIDc5LjQ4NDQgMTc2LjQ4MiA3OS43ODkxIDE3Ni41OCA4MC4xMzI4QzE3Ni42ODIgODAuNDcyNyAxNzYuNzMyIDgwLjgyODEgMTc2LjczMiA4MS4xOTkyQzE3Ni43MzIgODEuNjA5NCAxNzYuNjcgODEuOTkyMiAxNzYuNTQ1IDgyLjM0NzdDMTc2LjQyIDgyLjY5OTIgMTc2LjIzNiA4My4wMDc4IDE3NS45OTQgODMuMjczNEMxNzUuNzU2IDgzLjUzOTEgMTc1LjQ2NSA4My43NDYxIDE3NS4xMjEgODMuODk0NUMxNzQuNzgxIDg0LjA0MyAxNzQuMzk1IDg0LjExNzIgMTczLjk2MSA4NC4xMTcyQzE3My41MDQgODQuMTE3MiAxNzMuMDk2IDg0LjAyOTMgMTcyLjczNiA4My44NTM1QzE3Mi4zODEgODMuNjc3NyAxNzIuMDc4IDgzLjQzNTUgMTcxLjgyOCA4My4xMjdDMTcxLjU4MiA4Mi44MTg0IDE3MS4zOTUgODIuNDYyOSAxNzEuMjY2IDgyLjA2MDVDMTcxLjEzNyA4MS42NTgyIDE3MS4wNzIgODEuMjMyNCAxNzEuMDcyIDgwLjc4MzJWODAuMTk3M0MxNzEuMDcyIDc5LjU0ODggMTcxLjE1NCA3OC45Mzc1IDE3MS4zMTggNzguMzYzM0MxNzEuNDgyIDc3Ljc4NTIgMTcxLjczNCA3Ny4yNzU0IDE3Mi4wNzQgNzYuODM0QzE3Mi40MTggNzYuMzkyNiAxNzIuODU3IDc2LjA0NjkgMTczLjM5MyA3NS43OTY5QzE3My45MjggNzUuNTQzIDE3NC41NjYgNzUuNDE2IDE3NS4zMDkgNzUuNDE2WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTc4LjU4NiA3Ny41NjA1Vjc3LjEwOTRDMTc4LjU4NiA3Ni43ODUyIDE3OC42NTYgNzYuNDkwMiAxNzguNzk3IDc2LjIyNDZDMTc4LjkzOCA3NS45NTkgMTc5LjE0MyA3NS43NDYxIDE3OS40MTIgNzUuNTg1OUMxNzkuNjgyIDc1LjQyNTggMTgwLjAwNiA3NS4zNDU3IDE4MC4zODUgNzUuMzQ1N0MxODAuNzc1IDc1LjM0NTcgMTgxLjEwNCA3NS40MjU4IDE4MS4zNjkgNzUuNTg1OUMxODEuNjM5IDc1Ljc0NjEgMTgxLjg0NCA3NS45NTkgMTgxLjk4NCA3Ni4yMjQ2QzE4Mi4xMjUgNzYuNDkwMiAxODIuMTk1IDc2Ljc4NTIgMTgyLjE5NSA3Ny4xMDk0Vjc3LjU2MDVDMTgyLjE5NSA3Ny44NzcgMTgyLjEyNSA3OC4xNjggMTgxLjk4NCA3OC40MzM2QzE4MS44NDggNzguNjk5MiAxODEuNjQ1IDc4LjkxMjEgMTgxLjM3NSA3OS4wNzIzQzE4MS4xMDkgNzkuMjMyNCAxODAuNzgzIDc5LjMxMjUgMTgwLjM5NiA3OS4zMTI1QzE4MC4wMTQgNzkuMzEyNSAxNzkuNjg2IDc5LjIzMjQgMTc5LjQxMiA3OS4wNzIzQzE3OS4xNDMgNzguOTEyMSAxNzguOTM4IDc4LjY5OTIgMTc4Ljc5NyA3OC40MzM2QzE3OC42NTYgNzguMTY4IDE3OC41ODYgNzcuODc3IDE3OC41ODYgNzcuNTYwNVpNMTc5LjU2NCA3Ny4xMDk0Vjc3LjU2MDVDMTc5LjU2NCA3Ny43MTY4IDE3OS41OTQgNzcuODY1MiAxNzkuNjUyIDc4LjAwNTlDMTc5LjcxNSA3OC4xNDY1IDE3OS44MDkgNzguMjU5OCAxNzkuOTM0IDc4LjM0NTdDMTgwLjA1OSA3OC40MzE2IDE4MC4yMTMgNzguNDc0NiAxODAuMzk2IDc4LjQ3NDZDMTgwLjU4NCA3OC40NzQ2IDE4MC43MzYgNzguNDMxNiAxODAuODU0IDc4LjM0NTdDMTgwLjk3NSA3OC4yNTk4IDE4MS4wNjQgNzguMTQ2NSAxODEuMTIzIDc4LjAwNTlDMTgxLjE4MiA3Ny44NjUyIDE4MS4yMTEgNzcuNzE2OCAxODEuMjExIDc3LjU2MDVWNzcuMTA5NEMxODEuMjExIDc2Ljk0OTIgMTgxLjE4IDc2Ljc5ODggMTgxLjExNyA3Ni42NTgyQzE4MS4wNTkgNzYuNTEzNyAxODAuOTY5IDc2LjM5ODQgMTgwLjg0OCA3Ni4zMTI1QzE4MC43MjcgNzYuMjI2NiAxODAuNTcyIDc2LjE4MzYgMTgwLjM4NSA3Ni4xODM2QzE4MC4yMDUgNzYuMTgzNiAxODAuMDUzIDc2LjIyNjYgMTc5LjkyOCA3Ni4zMTI1QzE3OS44MDcgNzYuMzk4NCAxNzkuNzE1IDc2LjUxMzcgMTc5LjY1MiA3Ni42NTgyQzE3OS41OTQgNzYuNzk4OCAxNzkuNTY0IDc2Ljk0OTIgMTc5LjU2NCA3Ny4xMDk0Wk0xODIuNjk5IDgyLjM2NTJWODEuOTA4MkMxODIuNjk5IDgxLjU4NzkgMTgyLjc3IDgxLjI5NDkgMTgyLjkxIDgxLjAyOTNDMTgzLjA1NSA4MC43NjM3IDE4My4yNjIgODAuNTUwOCAxODMuNTMxIDgwLjM5MDZDMTgzLjgwMSA4MC4yMzA1IDE4NC4xMjUgODAuMTUwNCAxODQuNTA0IDgwLjE1MDRDMTg0Ljg5NSA4MC4xNTA0IDE4NS4yMjMgODAuMjMwNSAxODUuNDg4IDgwLjM5MDZDMTg1Ljc1OCA4MC41NTA4IDE4NS45NjEgODAuNzYzNyAxODYuMDk4IDgxLjAyOTNDMTg2LjIzOCA4MS4yOTQ5IDE4Ni4zMDkgODEuNTg3OSAxODYuMzA5IDgxLjkwODJWODIuMzY1MkMxODYuMzA5IDgyLjY4NTUgMTg2LjIzOCA4Mi45Nzg1IDE4Ni4wOTggODMuMjQ0MUMxODUuOTYxIDgzLjUwOTggMTg1Ljc2IDgzLjcyMjcgMTg1LjQ5NCA4My44ODI4QzE4NS4yMjkgODQuMDQzIDE4NC45MDQgODQuMTIzIDE4NC41MjEgODQuMTIzQzE4NC4xMzUgODQuMTIzIDE4My44MDUgODQuMDQzIDE4My41MzEgODMuODgyOEMxODMuMjYyIDgzLjcyMjcgMTgzLjA1NSA4My41MDk4IDE4Mi45MSA4My4yNDQxQzE4Mi43NyA4Mi45Nzg1IDE4Mi42OTkgODIuNjg1NSAxODIuNjk5IDgyLjM2NTJaTTE4My42ODQgODEuOTA4MlY4Mi4zNjUyQzE4My42ODQgODIuNTIxNSAxODMuNzE1IDgyLjY2OTkgMTgzLjc3NyA4Mi44MTA1QzE4My44NDQgODIuOTUxMiAxODMuOTM5IDgzLjA2NjQgMTg0LjA2NCA4My4xNTYyQzE4NC4xODkgODMuMjQyMiAxODQuMzQgODMuMjg1MiAxODQuNTE2IDgzLjI4NTJDMTg0LjcxNSA4My4yODUyIDE4NC44NzUgODMuMjQyMiAxODQuOTk2IDgzLjE1NjJDMTg1LjExNyA4My4wNjY0IDE4NS4yMDMgODIuOTUzMSAxODUuMjU0IDgyLjgxNjRDMTg1LjMwOSA4Mi42NzU4IDE4NS4zMzYgODIuNTI1NCAxODUuMzM2IDgyLjM2NTJWODEuOTA4MkMxODUuMzM2IDgxLjc0OCAxODUuMzA1IDgxLjU5NzcgMTg1LjI0MiA4MS40NTdDMTg1LjE4NCA4MS4zMTY0IDE4NS4wOTIgODEuMjAzMSAxODQuOTY3IDgxLjExNzJDMTg0Ljg0NiA4MS4wMzEyIDE4NC42OTEgODAuOTg4MyAxODQuNTA0IDgwLjk4ODNDMTg0LjMyIDgwLjk4ODMgMTg0LjE2OCA4MS4wMzEyIDE4NC4wNDcgODEuMTE3MkMxODMuOTI2IDgxLjIwMzEgMTgzLjgzNCA4MS4zMTY0IDE4My43NzEgODEuNDU3QzE4My43MTMgODEuNTk3NyAxODMuNjg0IDgxLjc0OCAxODMuNjg0IDgxLjkwODJaTTE4NC44NTUgNzYuNjg3NUwxODAuNjg5IDgzLjM1NTVMMTc5Ljk2OSA4Mi45Mzk1TDE4NC4xMzUgNzYuMjcxNUwxODQuODU1IDc2LjY4NzVaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8xMTI4XzM0OTMpIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xODguMDA0IDEyNC45OTlMMTIgMTI1LjAwMUwxMiAxMjQuMDAxTDE4OC4wMDQgMTIzLjk5OUwxODguMDA0IDEyNC45OTlaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjEyIi8+CjxwYXRoIGQ9Ik0xMiAxMzMuNzc1QzEzLjAxNjYgMTMzLjc3NSAxOC41MDk4IDEzMy43NzUgMjEuMjYzMiAxMzMuNzc1TDM0LjY0MzMgMTQzTDQ0LjkzNTcgMTEyLjI1TDY2LjU0OTcgMTM0LjhMOTAuMjIyMiAxMTIuMjVMOTcuNDI2OSAxMTMuMjc1TDExMS44MzYgMTQzSDEyNi4yNDZMMTM4LjU5NiAxMTYuMzVIMTQ3Ljg2TDE1Ny4xMjMgMTAyTDE2OS40NzQgMTA2LjFMMTg4IDExNi4zNSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuODciIHN0cm9rZS13aWR0aD0iMS41Ii8+CjwvZz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl8xMTI4XzM0OTMiIHgxPSIxNTAuNjgzIiB5MT0iODQuMDI5OCIgeDI9IjE1MC42ODMiIHkyPSI3MS43Nzc4IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6I2ZmZjtzdG9wLW9wYWNpdHk6MSIgLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdHlsZT0ic3RvcC1jb2xvcjojZmZmO3N0b3Atb3BhY2l0eTowIiAvPgo8L2xpbmVhckdyYWRpZW50Pgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzExMjhfMzQ5MyI+CjxyZWN0IHdpZHRoPSIxNzYiIGhlaWdodD0iNjAiIGZpbGw9IndoaXRlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMiA5MCkiLz4KPC9jbGlwUGF0aD4KPC9kZWZzPgo8L3N2Zz4K", + "description": "Indicates the concentration of airborne allergens, including pollen and mold spores, which can trigger allergic reactions in sensitive individuals by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'IAI_level', label: 'IAI', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: null, decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'IAI_level', null, 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Individual Allergy Index\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 12) {\\n\\tvalue = 12;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#3FA71A\"},{\"from\":2,\"to\":6,\"color\":\"#80C32C\"},{\"from\":6,\"to\":9,\"color\":\"#F36900\"},{\"from\":9,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 12 - 6;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 12) {\\n\\tvalue = 12;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 8 - 4;\\nif (value < -15) {\\n\\tvalue = -15;\\n} else if (value > 15) {\\n\\tvalue = 15;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"IAI\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:flower-pollen\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "tags": [ + "weather", + "environment", + "air", + "aqi", + "pollution", + "emission", + "smog" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/individual_allergy_index__iai__chart_card_with_background.json b/application/src/main/data/json/system/widget_types/individual_allergy_index__iai__chart_card_with_background.json new file mode 100644 index 0000000000..6bfec87d67 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/individual_allergy_index__iai__chart_card_with_background.json @@ -0,0 +1,34 @@ +{ + "fqn": "individual_allergy_index_iai_chart_card_with_background", + "name": "Individual allergy index (IAI) chart card with background", + "deprecated": false, + "image": "tb-image:SUFJLXZhbHVlLWNoYXJ0LWNhcmQtd2l0aC1iYWNrZ3JvdW5kLnN2Zw==:SUFJLXZhbHVlLWNoYXJ0LWNhcmQtd2l0aC1iYWNrZ3JvdW5kLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiIHRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDYgMTU0KSIvPjwvZz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNMjQuMyAyMC41YTEuNyAxLjcgMCAwIDAtMS43LTEuN2MtLjQgMC0uNy4xLTEgLjNWMTlhMS43IDEuNyAwIDEgMC0zLjMgMHYuMWExLjYgMS42IDAgMCAwLS45LS4zIDEuNyAxLjcgMCAwIDAtMS43IDEuN2MwIC43LjQgMS4yIDEgMS41YTEuNyAxLjcgMCAwIDAtLjUgMi43IDEuNyAxLjcgMCAwIDAgMi4xLjJ2LjFhMS43IDEuNyAwIDEgMCAzLjQgMHYtLjFjLjIuMi41LjMuOS4zYTEuNyAxLjcgMCAwIDAgMS43LTEuNyAxLjcgMS43IDAgMCAwLTEtMS41Yy42LS4zIDEtLjggMS0xLjVaTTIwIDIzLjdhMS43IDEuNyAwIDEgMSAwLTMuNCAxLjcgMS43IDAgMCAxIDAgMy40Wm0tLjctNy43YzAtLjQuMy0uNy43LS43LjQgMCAuNy4zLjcuNyAwIC40LS4zLjctLjcuN2EuNy43IDAgMCAxLS43LS43Wm0tMi42IDEuM2MwLS4zLjMtLjYuNi0uNi40IDAgLjcuMy43LjYgMCAuNC0uMy43LS43LjdhLjcuNyAwIDAgMS0uNi0uN1pNMTUuMyAxNmEuNy43IDAgMCAxLS42LS43YzAtLjMuMy0uNi42LS42LjQgMCAuNy4zLjcuNiAwIC40LS4zLjctLjcuN1ptMi0yYzAtLjQuMy0uNy43LS43LjQgMCAuNy4zLjcuNyAwIC40LS4zLjctLjcuN2EuNy43IDAgMCAxLS43LS43Wm00IDBjMC0uNC4zLS43LjctLjcuNCAwIC43LjMuNy43IDAgLjQtLjMuNy0uNy43YS43LjcgMCAwIDEtLjctLjdabTQgMS4zYzAgLjQtLjMuNy0uNi43YS43LjcgMCAwIDEtLjctLjdjMC0uMy4zLS42LjctLjYuMyAwIC42LjMuNi42Wm0tMi42IDEuNGMuMyAwIC42LjMuNi42IDAgLjQtLjMuNy0uNi43YS43LjcgMCAwIDEtLjctLjdjMC0uMy4zLS42LjctLjZaTTM0LjggMTV2MTBoLTEuN1YxNWgxLjdabTYuNCAxLjQtMyA4LjZoLTEuN2wzLjctMTBoMS4xdjEuNFptMi41IDguNi0zLTguNi0uMS0xLjRoMS4ybDMuNyAxMGgtMS44Wm0tLjEtMy43djEuNGgtNS40di0xLjRoNS40Wm01LjEtNi4zdjEwSDQ3VjE1aDEuN1oiLz48cGF0aCBmaWxsPSIjN0NDMzIyIiBkPSJtOTYuNCA3Mi4xLTMuNC0uOCAxLjQtMTIuOWgxMy44VjYySDk3LjlsLS43IDYuM2E4LjMgOC4zIDAgMCAxIDQtMWMxLjMgMCAyLjQuMSAzLjMuNWE2LjQgNi40IDAgMCAxIDIuNCAxLjdjLjcuOCAxLjIgMS43IDEuNiAyLjdhMTEuMyAxMS4zIDAgMCAxIDAgNyA3LjcgNy43IDAgMCAxLTEuNiAyLjdjLS43LjctMS41IDEuNC0yLjYgMS44YTEwLjMgMTAuMyAwIDAgMS02LjguMiA4LjQgOC40IDAgMCAxLTIuNi0xLjQgNyA3IDAgMCAxLTEuOS0yLjNjLS40LS45LS43LTItLjgtMy4xaDQuMmMwIC44LjMgMS41LjcgMi4xYTMuNiAzLjYgMCAwIDAgMS40IDEuM2MuNi4zIDEuMy41IDIgLjUuOCAwIDEuNC0uMSAyLS40YTMuNSAzLjUgMCAwIDAgMS4yLTEgNC45IDQuOSAwIDAgMCAuOC0xLjggOC4xIDguMSAwIDAgMCAwLTQuMiA0LjUgNC41IDAgMCAwLTEtMS42IDMuNyAzLjcgMCAwIDAtMS4zLTFjLS42LS4zLTEuMy0uNC0yLS40YTUgNSAwIDAgMC0yLjMuNCA3LjMgNy4zIDAgMCAwLTEuNSAxLjFaIi8+PHBhdGggZmlsbD0idXJsKCNjKSIgZmlsbC1vcGFjaXR5PSIuMyIgZD0iTTE0Ny40IDcxLjhoNy4ydjguOWwtMy42IDIuNi0zLjYtMi42di05WiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTE1MC43IDgzLjljLjIuMi40LjIuNiAwbDMuMy0zLjItLjctLjctMS40IDEuNGEuNi42IDAgMCAxLTEtLjR2LTMuOWgtMXY0YS42LjYgMCAwIDEtMSAuM2wtMS40LTEuNC0uNy43IDMuMyAzLjJaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNMTYyLjYgNzkuOHYxLjFoLTMuMXYtMS4xaDNabTUuMi00LjRWODRoLTEuNHYtNi45bC0yIC43di0xLjFsMy4zLTEuM2guMVptNy41IDBoLjJ2MS4yaC0uMWMtLjUgMC0xIDAtMS4zLjJhMi40IDIuNCAwIDAgMC0xIC42IDIuNyAyLjcgMCAwIDAtLjQgMWMtLjIuNC0uMi44LS4yIDEuMnYxLjNsLjEgMSAuMy42LjUuNGgxLjFsLjUtLjRjLjItLjIuMi0uMy4yLS42YTIuNSAyLjUgMCAwIDAgLjEtLjd2LS42YTEuNyAxLjcgMCAwIDAtLjMtLjYgMS4yIDEuMiAwIDAgMC0uNS0uNCAxLjIgMS4yIDAgMCAwLS42LS4xIDEuNSAxLjUgMCAwIDAtLjcuMmMtLjMuMS0uNC4zLS42LjVsLS4yLjctLjUtLjJjMC0uNCAwLS43LjItMWwuNS0uNy44LS41LjktLjJjLjQgMCAuOC4xIDEgLjMuNC4xLjYuNC44LjZsLjUgMWEzLjcgMy43IDAgMCAxIDAgMi4xbC0uNiAxYTIuNSAyLjUgMCAwIDEtLjkuNiAyLjkgMi45IDAgMCAxLTEuMS4yYy0uNSAwLS45IDAtMS4zLS4yYTIuNyAyLjcgMCAwIDEtLjktLjggMy4zIDMuMyAwIDAgMS0uNS0xIDQuMiA0LjIgMCAwIDEtLjItMS4zdi0uNmMwLS43IDAtMS4zLjItMS44YTQuNSA0LjUgMCAwIDEgLjgtMS42IDMuNSAzLjUgMCAwIDEgMS4zLTFjLjUtLjMgMS4yLS40IDItLjRabTMuMyAyLjJWNzdjMC0uMyAwLS42LjItLjkuMS0uMi4zLS41LjYtLjYuMy0uMi42LS4zIDEtLjNzLjcuMSAxIC4zbC42LjYuMiAxdi40YzAgLjMgMCAuNi0uMi44YTEuNiAxLjYgMCAwIDEtLjYuN2wtMSAuMmEyIDIgMCAwIDEtMS0uMiAxLjYgMS42IDAgMCAxLS42LS43IDEuOCAxLjggMCAwIDEtLjItLjhabTEtLjV2LjlsLjMuM2EuOC44IDAgMCAwIC41LjJjLjIgMCAuMyAwIC41LS4ybC4yLS4zLjEtLjR2LS45YS44LjggMCAwIDAtLjQtLjQuOC44IDAgMCAwLS40LS4xLjguOCAwIDAgMC0uNS4xLjguOCAwIDAgMC0uMi40bC0uMS40Wm0zLjEgNS4zdi0uNWMwLS4zIDAtLjYuMi0uOWExLjcgMS43IDAgMCAxIC42LS42Yy4zLS4yLjYtLjIgMS0uMnMuNyAwIDEgLjJsLjYuNi4yIDF2LjRjMCAuMyAwIC42LS4yLjhhMS42IDEuNiAwIDAgMS0uNi43bC0xIC4yYTIgMiAwIDAgMS0xLS4yIDEuNyAxLjcgMCAwIDEtLjYtLjcgMS45IDEuOSAwIDAgMS0uMi0uOFptMS0uNXYuOWwuNC40YS44LjggMCAwIDAgLjQgMGguNWEuNy43IDAgMCAwIC4zLS40di0xLjNhLjguOCAwIDAgMC0uMy0uNC44LjggMCAwIDAtLjUtLjEuOC44IDAgMCAwLS41LjEuOC44IDAgMCAwLS4yLjRsLS4xLjRabTEuMi01LjItNC4yIDYuNy0uNy0uNSA0LjEtNi42LjguNFoiLz48ZyBjbGlwLXBhdGg9InVybCgjZCkiPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjEiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTE4OCAxMjVIMTJ2LTFoMTc2djFaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuOSIgc3Ryb2tlLXdpZHRoPSIxLjUiIGQ9Ik0xMiAxMzMuOGg5LjNsMTMuMyA5LjJMNDUgMTEyLjJsMjEuNiAyMi42IDIzLjctMjIuNiA3LjIgMSAxNC40IDI5LjhoMTQuNGwxMi40LTI2LjZoOS4zTDE1NyAxMDJsMTIuNCA0LjEgMTguNSAxMC4zIi8+PC9nPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iYyIgeDE9IjE1MC43IiB4Mj0iMTUwLjciIHkxPSI4NCIgeTI9IjcxLjgiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmY7c3RvcC1vcGFjaXR5OjEiLz48c3RvcCBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmY7c3RvcC1vcGFjaXR5OjAiLz48L2xpbmVhckdyYWRpZW50PjxjbGlwUGF0aCBpZD0iZCI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTEyIDkwaDE3NnY2MEgxMnoiLz48L2NsaXBQYXRoPjxwYXR0ZXJuIGlkPSJhIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBwYXR0ZXJuQ29udGVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCI+PHVzZSB4bGluazpocmVmPSIjZSIgdHJhbnNmb3JtPSJzY2FsZSguMDA1MDMgLjAwNjI5KSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMTk1LjIiIGhlaWdodD0iMTU1LjIiIHg9IjIuNCIgeT0iMi40IiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIxLjgiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTEwMF8zNTc3Ii8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMTAwXzM1NzciIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48aW1hZ2UgeGxpbms6aHJlZj0iZGF0YTppbWFnZS9qcGVnO2Jhc2U2NCwvOWovNEFBUVNrWkpSZ0FCQVFBQUFRQUJBQUQvMndCREFBSUJBUUVCQVFJQkFRRUNBZ0lDQWdRREFnSUNBZ1VFQkFNRUJnVUdCZ1lGQmdZR0J3a0lCZ2NKQndZR0NBc0lDUW9LQ2dvS0JnZ0xEQXNLREFrS0Nnci8yd0JEQVFJQ0FnSUNBZ1VEQXdVS0J3WUhDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dyL3dnQVJDQUNmQU1jREFSRUFBaEVCQXhFQi84UUFIUUFBQWdNQkFRRUJBUUFBQUFBQUFBQUFCQVlEQlFjQ0FRZ0FDZi9FQUJzQkFBTUJBUUVCQVFBQUFBQUFBQUFBQUFFQ0F3QUVCUWNHLzlvQURBTUJBQUlRQXhBQUFBRDRuL1BlUEEyNE81YlJiZTdUYkVLczY0bEZLVlNVSGUzT01STURNTzJFZGczSVR0Q1Qxc2NvOHpEdnI3aWpDK2pZYzV1aXNxNGtLWk1GS2hhS2NpbXlYMGJuR0ZtSHBqNkdscFJmbzRkR0ZKNll6RERGeEhMVjUvUHkyaFlmZ0poaUFDbFF4RU5tRFVGakpUVUg3R0FtSnRkMURiM0Uzb09LODcrMTFDWERaeDgxVWFRVWR5OG5rNnlqdm9tTTJ4S1RNUlMwQjA4YW92a1N4MjZPcXM5amJhWjZpRjl6YWY2VGZObm5HeW91ZGN6Qm9WTmJMcHQ1Um5MODl4eTVRNllTcGx3S21wYzhhcTJlQjZCZ1pXL3BYdXBxaTd0NlRPdm9tcnR0ZDlLaTZ1U09kY3g1QlF5MlljMUJ5OER0Yi9qK2IzYXZyZ3JubkF5ZUpYRzViN0t5VlF0eTRkMnRlcHhIZlV2WGU1NjJ0cTZnS3VOTFYrQ0JGTTY1MG9wNWg2Tm5zc2ovQURLY2I2dm8xZmZSUGkwRWdOb1JvM1JKMDZ6YWRyemRCYXV0MlRwcm9IVlozdFZYMDB2U2FIYzdOYUNxeVpVRHlVTW1Vd2xpUHlOQnFDdnM4TkFWVmJnaUROZDEybWQ2V3ZjKzAreFcyclNJczcwNmRLMTEra29tMTREUkZVdHBYODduMFVLazZQTGtRbjhkL0hlUVdoRXFlNkk2OWMzSHBGZnFXSFFXdnRMbjZGTkE3N2F6Ym90UlcxblpkeTExSTNKWnpOWUZ5RFhuVjV1dFY1M3gySnpxbW4vTjM1RHdETm9LRTJvMUwwT2ZTUFZjbTFHYnFlLzZXWnEyZUs5RHkxZ2lxYkxMMHphWG5jUGpnM0tQbk5PWm5GcU8wU3NUQ3l3Vi9uTDhsNFlIM2ptMW9tbmVsTFN2V2R5N25Ob3pUV2xxOXRTYnBJTEtlblhGWDZQYm4xdVROeHpqdVd0SzFEektTeE41c2VmOHhyS1MvbVY4cTVlMkJkQTdkQ2EvN0kwRDBYZSttaTBCMW1mcVcydW5abkt6VEZScHBwSWRlZlB6S081VEdaelBReldXTldRek5xZWl6bFRXbDhTL04rYXhjRlB0RjdWMXoxaTk5clA5cmZnd1F6MmVsUVFLV2puL0FEQml1dHdidDU2K3Vua0lWaU5SQ25ONG85NnpaQzhNeEVxaVZmblg4VnhYdDBuczIyK2t1dytoUm9hMXdYNjI5SlZrVVZIc00rVzg4bmJvS3lVMmJlb3o5RVdSSFVhVFFaS2ppYTNsVzZ6UmxlZExZcCtZODl6NjB1dW5icjNuWCtqb3M1MXIxQkxacHJxZFNzVFhsWFdvTzA5S1VMVHVKZGphemh0T3V3VkVLdzBNUFJiTjJGRnNqMDFud3VWdzY1NjUzcHRQVFhUYVdxSU5TeUR6V2pQWFU0RVlNUUJVckNzbU1wQzNMNmZMMDZPc1ZXR1h5TStlT2FoWEpxcXRaNHFtb1BHanBYZkhZdXhOR3MyazFkRWduVXp1aDdLL1lCcHFja0N5MWNhSjBLcy9Rc2I1cDQvVmxabFdMQzBubGxZV0sxaTdPWEV6enFTbEw4bGRIN1pmUXZmRitwUzFESjBaTVQ0Q1dxWm5XN1dqUjA2U0tpckNYZW42R1I2TFhGMjFQSjMrWTFyelF4ZzZSeTN0OHdXc1ZoV1cvUGpxSGFuMEIzTG9sVG5mTGxXSlZ1ZDdwazhLYXYwYlg2MnhIa3pwUjF6RXN1OGpzcllXeVRnN1FnNjRTc0NhNWZoU1c1ck9vc2VpS1B4dzBmdG5wMXhJclU2NVhocStienExdHR0dld0RkYxaEdiNnU1dmFqallRUDFHeWJKbHRHVmpOVmFjWkJSU3F0Q3FhUDhBLzhRQUpSQUFBZ0lDQWdNQkFBTUJBUUVBQUFBQUF3UUNCUUFCQmhJUkV4UWhCeFVpSkJZai85b0FDQUVCQUFFRkFkeHpjYzY1dU9kTTBQemtSWkFHUkJ2SUxaQlhJS2ZueTU4bWJVeVN1YlYza3dlTW1MSkR6cGtJZnNZZU0za3MzRGViaG5UT21hRmtBNE1HUUJrRjhHdGcxTWlycld2bTE0K2JKSzVKYkZhV1I0MnRVUk9jMTk3eVN1OGt0dldEWC9mWGtvL2s0NTZzOU9lbnhtZzVFT1JCa0E0TUdEQmdnWUZYUGwvenBmOEFKaDFyZWx0a2x1a242K0swa2pCNWh3NzMxS1ZSb3NTOGZuQ1RsWHVHRFIveWRicHFRLzJRTTBEUG13aS9qSWl5SXNHSEJoeUFjR0RCTDcxdE5YdG02L3hrSy84QUgwSkMzU0liSVZTbittSEh3NlZ0eTFHbVZtZU50SVhEVkpLSWJpdDY1R2trR3ZhVy93QS9OdjJiVzM1Z0RQUmhBWjZ2RzRqd1lzZ1BBaHdLL2JVVnBReXJIb21sVUl5aE9xNkNLZ05vVlJ4bjdrSktSVnJxT25KTndBeGZNM3hrTG9yU245VVcrTzdhYjVXcC9YcXRKNzNHRkVZdWYxUHJuQUdiRCtFSGtoNUNPUWprZGVNRHJFbHR5MkpLZlZSTDF5UzdhMU1mbUt0UE9iUEhhejVDM05LWFJmNnFOZUJsK1lDMStwU0RhVlFtZEVvQ0t6NUh4K2RnY1BBdk1pY1lBRkc1NHp0VVVCL2hJL2s0Wk9HUUhrQmI4UmpMSzhIWXFOWnJxbFRlOGV1UEVJS3FyaXdMT2s5a2F6ajJqcVVuSForMWVuQlBMVmJjWkRvWnNuQ2tSTVFCQVBuOVFzY0xmRVY1N1o0OE1RN05HUzBiMlB1MXJXR3lXZFBPd0o5c2hYZG83cjU2bFZvNjBSTlBjd1VwWDZ0eXFDblBJOFpIcVVVR043NHZVbTlra1JqQ1djZ1pCU0RlNFV3WUp0MS90MDl2YWVKT3o5YzdCZmVOcWU3ZGhUKzZGMXhDUGJVc0xMem05NENQZWRYWCsyQzFGMmd4VUVHY05UT09WaTB4eVdWOXVWcVh1VXFLL3dENDlVZ1k2QkpaRWIvSnhha1d4bGlBNXhBbE9KZzJOWEJpTmpVakd4WWttREpXNVFOVmRwQXNac1JKbHVPQnM5bjVJbjUyL1U5LzdvdHgxQlhydUVVSUZtbFhEOExxS2l4VkpVdXFXdTBQVU9vSXN1K1lQV1pEeWtJcERVb2pCbnE3NlB1MzhBYXFlWmYzS0l1YnRNc1F0QUVBWDFPTWpDZE9aV3ZBbXpFbkdSdnoyNXFXOXpSZ1NSS1NNL0ZRSXh5YlJsSEZlNDVDRVRZMUJ6REtuRVRVR21kdzI5YkQzdXZybm1HVU9MUUVIbGRpc0dKdVgrd0N6TGNiQ3QzSXdFa2g4aGJJMlNNcXcveWJGWlE2UTBLVzdVQVhJeWx2ZVFoZ1JkOTB3QjYxeGFyQ3pOZFA1WGhxeE9GNEU0RlUyWGU2TDd5bmdRS2lkcHlPR3BHNUNIdW56SUlpcGNwTmJCc1BZTTgwRnE5eFZPRzNBYjdaWEZUTlBrVmJPQzFJc2FZdmpDQ0Z4YitjRnlxWUdGeCt6SWkvK2RhRFpEcTEvd0R5Y0ZYS05WY016bFVTbjgyNk9aRHExQVN5UlBWMHdidThzVzJiVGNTYUxaejcxN1RBcm1ndEZKSGJBVEY2YmJxcXRJek1qd2dnQkpXeUZsRHpTVlJqNUtacXR0ckR4bHRhMVJOcXJCM3VwSHFlMkZ1a0tOYmNXS2F2ak5QamxZSVFsRmh3bEVnZlloWWhRZnU2OGxKTThpUFNpYVFKT0pqYXI3VlNLandxU1RybGZXRWc1VEkreGFzSUNzbTl5aXRpcTI4RTJYQmg1N0FLcjJQTXQxci9BQ2JrTGtCb1dvclppNWRTZ090TjBKdndjVk9sdjIwU0dwQ3JnNkFvQ01JNmt4RFJUcGFkeEpxZXcyTkhDRFpSNkpzODVvbnRrSTJoZVBna2pqTnpZSk1nNURJdTdTUFN1dDV5aFVCWTdINVJaUmcxY1dLeUZkZFh5WmJHaUh0bFY4WC9BSnFYOHFFK2Uvcm1mT0pubkxmRzlSOFVNb2EyR1d0THRXZWhhSlpEM25EeXlkSzlQNFpzdEMzb2V2b20zVS9YS3QvajV5QmJjaXlwZVdLMlp5Vlcxd3pPNk0xRStYWmFPZ3R0RU5jazkvSmY1QnN1ODRwZTk5UmJTaXZOTDFacTQva0JqMjJ0UXoxM1VHSFBkTHJ3S25iM3N6Tmw4cWJISkJHSlhMdXRIb1RvY1VyNTNrTGt4M0hCeEZhV1EyMWVUdjZJMy9KcmFwV0hwVjlwTmVGaXZWZmNvQ3Z2TjZyR3JUUXdobEZDeEtmcmFYUHRzYkd2Q3ZQWE9id1ZGeDY0SVNlWEU5V3FDcFpSMXhjcENscTlTa3Z4MVEwSjNUVXRqVTZ4S0xsNnRNTi9sRzdoYWg1TTZzMVdYaW01V1VwdmdzeHhXQkFWWTFQYXgxVHBDOGpsdU84WENFTzJiS2YydE1SM0ZwNk81dHU2MGR1L2tLUE5lU2JzcFJob3Y4ZjFFcGF3TzR5MXcyQXhaUm5oSElYTVFpNUJkbUlWcS9rR1pEbGRPajkwZ2d1cmhYT0lRblpNYzRUbFYxbGR5NWRJMHExUSthRk5YRUxNSTV4c1ZXQjNpdndnUGNFRVp6a0doemM1THFXbUxtTzk2dENUYXVkR0tWOXVDTUtoaFYyYWdwYTFRejlNVStReENNL0lEeng1L2JnMzlzbzR4Wk9BMGp5UXVvVmJCSHlWZkl0Y0RVbHpHL2RGYWNycmJKdmg5MGV0d3IzbVozemozSytOMlk1RWRpREpaU25hYkp2VEpOajA0MUxGSlM2RDJFemQ4OVdsY0Eya2pILy94QUE4RUFBQkF3SURCUVFJQkFVRkFRQUFBQUFCQUFJREVTRUVFakVUSWpKQlVRVVFZWEVVSUNOQ2daR2hzVkppd2ZBelEzTGg4UlVsTUZQUmd2L2FBQWdCQVFBR1B3SDFkUFUwN3RPL1R1MDlUVC9qcFR1c3RGb3RPNm5kWUt6RnZOc1ZwL3lhS3c3OUZjTFJhZDJWcmRWd2ZSWGJ3bWllK09QZVp2TnNydDgwZHl5NFZ3bzJWRllldnAzVXAzYUxSY0txMExNNW5Dc3hadWpVcCtGb044VlRvM3NzVzZLU0l4SElYbWhXWXNUc290NUxidkhKR3lObFNpMDd0Tyt2ZlJYSHhSNmhVZUU1N0FpS1hxZzdEVXpzSzJBYnl1ajJpV2NSM2ZKWEF6VVcyRExoRVpMSnNUUnUxM2tNS3dhcXRGdEd4V1BORnJ1NnFQcTI3dDV0VTV6S2c5Rmwxc2l3OHdtbHdzZFBGTlpUalFiRzJvZWdNbHd3QkNNaTd5Z1IwMFJMV1hUbm1MNDBYQncyMFZKN3RUb0lvdDRYYlpiYzBybjlYVHZGUWc2bndXZGdxZWlNc1I1YVVScXl2UlpqRlFwclkyVmMwclBNUG1xU3RyVFNxYzVPeE04ZGhvaEpLTmVYUlZXVjdBaWRsVG9VV09iZFo5bHl1cUUyNmVwUlZvcVVXVU5WSENpeUFCMU9ZVkhSa3NYcHdiVE5RRnFkam9HaTF4MFh0RytKV2ZKWkhLS0ZIbU5GWFZiZlpWeVdhT3Zpc3p6U2phMVZZVzZtaXJKb2hSd3F0cEd6NXJKbDFSM2JBK3JvdUJVeTZyTTFoV1p6TG9QMlpxRUd4c3ZtVFkzdDExUmM1b290Mml5azg2SnJlS3VoWHBUeGI3clp6R3poY0owUWZRVVc4TXlMUXlsT3FiR1I4a2EzSVZlcW8wclgxQlZXVlg4MEJSYjhmMFhzZ2dBMUY2SmFhVVFmV21XemdQdWk5N2pRaS82SjAzYUpwRFd6VHpYdFRhbTVHRGFpa2ZoWmF1eWJ6ZVlUMzRjKzBoclk4d250bHM1a2hhV25rdlM4UTBVMHYxUmtqYTFwVzByL0FGTGMxSTA2TE02M242Z3RvZ3pWYkxLbU9wcWl3bGFzY1BORFBHYWRRczUwcFpGcnRCcWkyaEEvS2kybFE2N0RUOS9KT3hPTmVBTmNwUlBwR1ZqTEVOWG9jRVZHSCtiN3k5S3cwOVhaUldwczhkQ25kcTlqczJlS2ppSm53cnVkVFN2MFUyTmdqMlQ5Sldua2ZGSEN1aWNDRyt6WlRUeFRwSkhWa0o2MXlyYVNuaUtNOHBvMVZoSUo2ZW9IUEMyMlRUUk5lejNkVVRUeEMzUWZGVUQzQk1pMnBlMG5RM1JEM0RxaUJCVkdRc2lCQjAxVWJJdzRaNkNvRm11UmdmakRtSnBkdW5UelVwZkRuWnBQQy9sL1piSFlTZ0hlWkl4MXIrQlJsWmlITUpaUzhhaXcwWW84NzRsaDkwY2tZOEs2Tjh6VDdXV09sRDRsZjZ0MmZGbWNQNDBvQ0lkRTY1VzJ4a2dibEc2eXF6UXVzUGdGUXo1dkJ0KzRtbWlEVTV6TldoWjVHbythQmFCYnhUWjNOcTFaODRIVUJaaTRWNmxDYkNTT0RHRzdBYlVSMmpzMVJWZ3pJeFJ4NWZBS0dacnNzaE1aRWZKNG9NeWZGVFp1RTdUa0xxQVZyd245Q3ZUbTcrVHByWGwrOUVIeFVMbmIyVWpoL3dBOUVXaUFhVUlMZE9xZkZIaDk5LzhBRWRGeE84QjBDM01PY096TldqbmovSlhvUGFlWEVObDNTMTdhS1IvWkU3UkxISlIwYmJXUmt4R0lrb1ArcU1VVHY5MmM1eDRXNUVaM1lsNWIvVHFyb2xpejFXUjU0ckxLTkZ1QkJraG8zUW1pRUdJbHFCSmtlMHQxQzlJd0lNa2JuVUR1bmdxVjNocU9xMk1uRDRwczBkbmdtbmtoaW5iclg2ZUpVZmFPRXUrT0p3b0R5eUVENnIya3pjb3hMR05BclhkQnFnOXI2Uk05NTlkK3FFK094SWszaVJTTU5yOGtKRGdBQk9TT1ZxSXR3cjkwOHFIOUNuaGtrUXZ2WmJKK095a3UwYnlxZEZCQmhuTVkyWWUwb0xrblJIRjRERUZsZENPYXAyeDJTMGtmem9SbEtiQmdlMEEzbmxjUURSVkJWa3dFSVdWQjBWVDBSRWdxMXdvbzUyR3I4dEd5RDMrbnhVMkh4Y1cwYnRHMWFlYlNFL0VZQ1RNeXRhYzIrQ2MyZHBCVWVHa0hzOW1HNXVYVXBuNUpSVStDeEdORlE5NU9IaUZmalg1MlI3TlpjeHdnenpQNFdkZE9xajJUZ0dOT1d3dWJDNmhsYnExMVpGRkptdEhpU0Q4UW8zc2R6RlZORTlyWDMwY0ZGQmRoZHlIZ1A3Z0tiYXc1bTVHdGFRTGc2MStTa2l4ekFZSTJBeHZycklkUE5SRnpnK3BEd2MxS2ovQ2ZoY0w3a2NXUWptTW9RZUQ1b1pUYlJOZEtMaXlBQyt5eXRQbXFtVHlVbUFMZzZONXpmMCtLYzk4UjJzTk5vMGUrM3FqaW9zU1l5TmM4ZGowK0NheVRadURuZjFOK0IxQ21mRTAwZWFaVHZBcGpNVmlZb28zM0Qzdi9BQTNvcGV6TUVIenY5SExXU1FqZE5MMEIvWHhXRjdhbm0yTUxoN1NBSFJ3c2ZvbzhHd0crUTFJNUJvdjhrMTduMXVjM3hjVmpvQnEyZk13ZVFRamViWmdBcElSY0NsUG5STWh3cHFYVGJPSWRSd2o2NWovOHAwem1Wd280UEcvN0NqN0luTzdJL2Fadnd0QjFVbllkM3NqZUM1dk5ubC80b2UxbVNrN2FISy93TEhGdFBrR3JmS2FXNjgxbVRRMG9WNU52Uk9pa2x0K01EN3JaT2k0dUZ3Tm5KMkl4RXdMNkwwNWtsSEVGdXVyVTI3UzMrSElmeW5RL0EvZFBlSDdNUU5jWEc5cWZzSTB4OHo4bkZRMHpQT2dyNVhYbytIbXoxTy9udm5yYjVLUEh3VFA5RWZJQ0FIWGhQVHlXSzdNYklOZHRFQnlybzRMRHZ4N25aNDRIQVNWMUl6RDdQQ2pqcURteUNuekpRbmpxV3l6SGFnKzdvZ1lqdU9sQlo1TEZZeXU5V2pQdCtxWVlwY29pRG10ZitIa1hmQWZWeUhaLzhPRnRCV2wyK1g1bHNZMk1iSmlKdHg0dnMyTjRRRmllM211eVBPRlpNWGZoY2FOcFhyVUVxUVlkdFREaVM1bmlIMyt0ZTVxb3dMYXlOMDBWSFNaYWFJeWszNXZHcUlxUHZYNEluRFlvTkEwYTQyL3N2UVp3Nk41TzVYM2s3QzRsMmJhTk9VQTZkUWZ1cGRwZHMyelk1dW1oT2I3QmJYQlA5bEdkOC9uSy93Qnp3amh2VkJpTjZlNmZKUHhzR0laaXNITEp2bHZ1ZWZSUlluQlA5cEJkcmVvT3JmMzRxUDJlYUtWaExCMFBNSUhDRTVRSzdNalJOYkcrZ1p1bXVoNnB3ak4yN3pSMDhFWkE2enJySm15UXR1VCtJclk0VGNxS05JNHIyVVdGZ1p1dERqRS84QXJ5Nm0ycFhvZTNCZkxqU1lyOFZOZk5aWnBDd2htU1UrQTRYZmNmSmJvV2FSQ3l5RWhwcHFxeHZCeTYxS3kwTGE2RkV1bmR1M05Wbno2WDhWczI0QWxoTy9vN04rL0JSTXdtZGtqM0F0aWwwcW9YNFR0S051M2E1MjNhYWh0VGY0LytMMEozYU9Ea2dKeTdFNWhYekoxS1oyamdLRmpSVEowOEVaOElUR2E4aGZ5UFZWZU5pN21HY0orSEpSVm1iMXQxcnFwY1JoNW1PT2VneU9xV3JJNzZvdnFzckxEelJjWkw5VStac3JIYk9JdWJWMUw4a0dQY2FOWXdaUUtEUllMQUd1V0xDVmtBL0U1eE5mc2pLeDdRNGU4T2Z3NUlGQ2crS3lVdXJQS0V3RzhOYXBzck1TL0pLTGIzQ1FxT2VIRXVvU1FxSEN4bXZtbjVNUEMxb2RjbGxiOUZ0SW1OZGpBUmtPUzBiam9tZHE0VEh1SVljcGJKejVvNFR0ZnNadTEvN0l6US9NTFl3NHA4dUZmL0FDNWJscTJrWjFPbE5VWE1jTXZMZFRONDhQNnB3bTNtSGlzbkZyelRvc3pWZEhLYkg2SjdYSFdnb3FTTUozdXFtZHZWcUl4YmtMSnNqTG1sZUhxdi84UUFKQkFCQUFNQUFnSURBQU1CQVFFQUFBQUFBUUFSSVRGQlVXRnhnWkVRb2NIUnNlSC8yZ0FJQVFFQUFUOFFPbUtSRGNYWlptNnJmdzRZUjE1SFhJZEp3b1pjdVNxNmhYa215VnRPZGpsb1RtMUNzTVc1WWkva05kSXN4Slh5UmJTcGJxRGVybCtEaVUzQlZmUCtNYnRrUlhsTUdLaXdzZDFrQkxtTVpYajVxZU8vd0tsa2l3Y2hpL2daY3g2aC9rMit2OFJhdWZ3VTJFWVhqVXhiY3YxREFoMnhZQ0wrT0U1WTlDSm1sYjJUcVVQYnF5N2JIUmdiY29waHFFSmRkRVRZT3c1UkFueEJXZGRUUmlBcEdmVDRtQ0lETllzQjFWTEkxZ3hHYkI1cUZuMGtINVJZSnl4blVEZE1CZFlnT09aZHdvS0lDNWtXSmVTczdDSVV0Q2lvWThEVXZDRnh1VXpQWG9uS2taRmFoTjlvSklGckEzOFRKc2dNT2dKTGR6Nmkralh1TTdkdUxHY2JIaU9ETW54Q29ET2VRNUlZT0RqR1VZRjh5MWtSVUhSR3gyNWNqdWp3emgyRzF2YjY5UWJ0dlMrcFlPVW9hZ2RxbnJseFJEZk14MGxTK21rMXhLUmczYjE2bGx3dzB3SE5Nc2kxNGpVVXZKM0RONHJSa0owRnVta0R3cHJ4ekt3cHFpSDQycnp6NWlhWGNWRU90V0E0N2lGVUJuVUNMMlVyOGVOUjZXOEh0QjRKZ3hkdEVLaFl1a2dMUUNFRHB1RFpoQ0FoVXN2c0N4aU5ncWdYWlUvdFJ2T0RtQ0FCZEUxa3FGaDE3bDV4cGEzQTZsWUFocGt0eW1NZXN6Tkl2Z1FvSVNyMTJ2U0c4SFkxT3dWL2hFbWkzanc2aWxhc2cwcXBCb0ZkZ1FwT3NvN25QZ0tMOWlxemduU0VsZWVHVXBwbDlqTVIxaDM2SHhGUENZQXRyT1NXNkJ6bEpZTVhST29YNFllTjl5clF6QnBqTXNWc1NJMmpvcUdBSmpsVFhqZ0NXclZ1azJMV0xQWmRSYVkwK0ljdzVvSnM4TUFjRGxzTEZnMTdZNCtGYU9vYlVvTFZsK2ZVa0VKcjlRcFZnY3hSZGRzQVd3aHRTbkNpMWhCNnl6aUhTSW9lVEkxajNCTGpBY1RxR1hDbG1XZGc0cVZXaG9xTmF2UFJ3eHd0RFpHUzVBYjlxK0lETUJYNkx5VVJlcFhkeTVpRi9TTXNCWkZGR3k2Tzh3ZVZBb1pjVklZcUZQN0VDaDJpL0VTMVpRRVlNZzBDTjYrc2xOOXdEQzFhQldrZEhuN2tXWlpML2ROdGtGRUFDRzdnYkxPV3hiS2NQaE8xL0xwdWwreEtBVTVhSGdqVkdFZnVYeXpOdDYrVG1xalZ1VkgwTGx6UGRnMm9RMi8xSDhRdjA5QkFObERZUE1KeHBZWVBXUU5EZzU5eEQwbW5xcDh3SVdHRlYxQ0NUNFFXS1c5dzRIUXBmY1ZGOTVZZnFvOUdPWVc0RFVQWTlpZmQxSlQzaXI1Zk5SL0dxSDI1TDVIdUI4Y0Z4RHY5UmFlNm4wLzVCSkFMT2FMckdjSTVidEgyK0dmTE1JR0R6aWk5eDFhUWJEeTR4OFNnSFo5THlQTDRoWEJDMVF2K3c3Q2xKQzUyYVh6L0FNbFF3bzNSVjh5ejk1R3EyWEhTVkViRTNhMG9CTE5VSXBLa1lJaXhkUjRmRkx5R3NnV0ZBdUFMb1k0cGczNnRMVFh6Q2FFQ2JoOFhFVEFwNzByUEQ1Zy9TVzFmNEMrNCtVVnBvNFVYOFBUVngxakJGUXRLTXJMTDVHR0pvRmdYUVVGSHoxRkdXclRLeTVpRmI4czdYSVlUby95WDFPVFFUdXZIenl4L0Zad1ZkeUEzQm40VjIrNWJiSGM0UG5lWTZPa2I0blBMTGxQZUh6Rk9iNXd5bU1jTW9GTlRtTllNbmZmd1F1VjZUTFgzRFNNNldma0ZYeFhPWWNJWlVhaG90RloyV2k0dXI3eHVNRDhnTHVYWVRCS0tLbktycDc0aS9KdjJCV2NYenhlNDZscWJFVjVCelFuSnFnVnIzcU4rdzZPMmpCUEFuNzZDYmI4R1dGamR0Ry9TOTl4V2o5QWwrM3NsYWxOYmlQeTIvY2JTRGpISXFlRCsxR3ZOTi90UUVLeVl2L1dITnpSUVBvM0E0akV4ajB4Rmx2WWFCWW1vUC9zd3lWa0c0K2JuNGhtRDFxQmJWc1ZtWjJ3cU1Xc1MrU0tQSlgyUkhlZHhrTmtVOWRHS29MQjZaTEExRkszZ1ErKzRzZ3VlTWoxVUpWZ1AwcnJQamptNEtTZXdVSVdnTkZyNndJOWFDVk1YbG5QbHFDL0MxcXMyNjI3MFlyYmxlWGU4Sis2bSsyNWE3SDJqRWZ1Z1JRbERUZXZNNnpoTnJXZllMc3QzTUtTUGh6ejZZenQvU0VTMXVzdjBsKzVidHdRVXRPY2YyQTF3UXVaMWRQTVh5QnVHTHc2amdhYTdFZE5rV0t4cDNDSzI2NlM5ZUtaOVZBUEZRY3NIMkkvMlM2ZlB5M2w1OVFLcjdEVDY3Sno5STVoMUw5TFBjbUo0VGxmS1ZNdmFEMFNUMUpyaTVlV0szZEtEQTZLT1lwT01jVmZaVnBSdm5ESloyeGM5dHRBdmRaQnZkV0h3ZjhnY1FmTlBNczRtYlhma3AvdUNzb1VTd3NWUjZ1U3BKaDBDR240MUh1Q2ExaXFYMEcvNmhLQXhCSTlMenl4K0lwWVUyTE9CSWI2NmhHKzZ3cjBtV0ZGbUx3RnVSUXIxd2pIZ1lvYmNFU3g2Z0loQ2pWNFRncWd0MnFIeTM5cUpXZ0xpYnFRN0g0akJxQkNtMjNwL0JqM0taY0VyNDB5T0hLckNPQ0hhRGxZZ3VjUUNuZ2lvbmFDZW5ObndoRGxlenNPdXVIT0FGOG16NFlDeEtFM1JWOVVTd0xuM29pdnk0amw5ZzVuQmFJYzZnSDZ6SnA2Vm9ObjM5V0FWZ1VqVXRMUGExVHdNdUVyWmNzdG5sejJrUUZnZ3phNWVhb2ZKSXJMb1JoV2I3bnl5a3QweVlNMng1aldSSjQyTWRUWFFMWXBJVFRkTDlPdm1EeEFydkI4akVLenpPZHlQUTBxSkdzckhtQjRrMmRGOS9sL0pSUXZkTEJyNTF4REJZam5hMTlCYXJvNVpscWNLUUFvYlZzV1I0eEtMMjBEdlIvZVRiaTQ1WUFYNVhoRHRZMStmOXJ0cTlsMzZoQjVNOFA4QTlrdmpKNUhCVDZvNWxreGl2c3gxTG43QUlmMjhQdDl0K1o4clV6MkN3R1JLRVBLNERvVmFuS0FXaUFua1g0alRDNHQyN25iOVY2SU04WXBvaS8wdDlTa3BFWWJDcmdtMW5VVHNFd21aMnJncEI2SmZYRXFoQzdSWmV1bnlSNzRVNC9aNVh6bnVOdlZtSTZFeEgxTlllWVdFZDVOQWVHTlhBbVlVaythdnVLdDdCZWJMSHhSYS9uemNGRHkrVlI1Rlg0WTNtNDNWeFFlUjc1aFhvajJyU1hrYlVQTFRhc3MwYjN5SUhyUnJ4OFRlWC9VcTRQMkZSWUJjcmY2WlUzWFh6dTM0TlpLVWdueWFoYzJ0QTIxMCtPbjVlWUFHeURjdVQxcnEvd0J4aDFjQzc2aUhrQXRSdVM1V2JGd213dmt0aDV2N01Bb0J5SXZ1eFg1Z0NTTVcxVElZYWtmam5MM04zeGpGUGlMRUM1WWdQM2pFcWtLNzRoUUNycFYvR3EzKzZqRjh0Syt3Wlg0Zm1XQmJvMExsTEIvU05hcFVyeXkzQWhxNWFBQXVvYVI3djR5RW1VQnBkdnluanljUmdxWFFndWlEaCtlWW5EMUd0dk4vcXhpVWh0K2JoKzM4c3NOaERjRm5IRExFaHVQSklMTjFpTW9VSHB5bFZyeWJLMWpSMWxBQ2MyaktvK05rU3pmblh1Rk9LVloraEFhZ2twMXJMWGp5WGRKVEZUQVB0ZHNYV3dITU41d1dNTmM0MWM4M0VpRGdDdEhadzVMbjFjdkRqaW9xc21sczRQaTRqRTR2OXJBRDIva0JabE05YXd0T2I0SVJtQW9pYm9uSEZ1VkRGMkNabExFMC9XWU9UL0FyeWVvMVZDaFNTeGRQMW96dktDQU5ma01IQVY2dTFLdWFOTU5sMDVIWUE4b1g3T1lmVnJMS1Vhazh2K1ExcExpM2h1VndZTVBvWXgxaExMa0h3ZlU4cS8zdFVoMzRuLy9FQUNRUkFBSUNBZ0lDQXdFQkFRRUFBQUFBQUFBQkFoRURFaEFoQkRFVElrRlJJR0dCLzlvQUNBRUNBUUUvQUl2aFAvTm14dWh6TnpaaW1LZGltS1FtSmlaYTRzandoUG0yV2JEbVdiVU9adUtZcGlrUm1SWW5SRmlZbmZLNFRMTE5pelljalljaHlISXNzVElzakd6QmljK3llQnFOb2hGaWkwSkRZdlFsZkNrS1luWlpaWTVJY2hzdmloUU5HakRqdG1MeDdSNDJLcHRFY0NhSDQyazJQQjlSNG1tYU9yRWhJU3RHd3BrWkNsU0xaWTJYdzB5RWJSREgwTEVSdzdJd2VJcFI2STRkWVVlTmg3dGtZMnV4NFZJZUllRzJaY1dzYVI4UkhDZkhSWnNSa1JZMlh3aEpzVWJSanhWWmlnS0Jqd2RuaTR0U1dFakJSUjZGNkhHMFBFVHd1VEkrTC9SWUZUNkpZU3lMSWtYdytJcXlHTmVqRjQ3a2pINHRMb3hZS1l2SElZRW8wWTRwTVRyaEsyTGl4VXlrVVpJMnVJRVNKSDBKWFpxUWdZY1Zvd1JsamwyWW9SOWl3cE94Ui9vbFJZaHNRbTN6QmNwSXBrZW1SUkV4UXRFY0RQZ2RrUEhNV054TWNER2xyMXhTRzZMUXVkaGNRL3dva1VKRUYyWUlwbUtDSTRreUdKQ3h4UkdLb2pTTFFpU1lpQ2Y3ejA3b1V2cllpUG9URTc0U0ZGaVJqaTdQSGlZb1c2RkRxa1FUUXJaRlBsU0dyUkd0aDlIeUl2OEFVZnRvcTRpZEM5Y1JJb2pFVUJRTWNHZUxoVDdJWTlHSmRFa0t5RmlMbzJWa1pwOUQ5MklwSVNwK3oyNkUxUTExYUZmNXp1a3V5TUJSUkNGdWpGaTZQRmhTRWlLNk5iWkdLSFNRcHU2Sk0yTWNudU9uWmliUlZvU0g3RzVKaW1xcG1GeWNiWjJkZjBtNC9qTWNFejRqRGk3TU9HMVJDQ1NFV0oweHBvOWpRMTlScW1SaGJ0Q2hmc1MxWkNYWFpEcWJ0amY4R0trTEpvNlE4anEwUEltdTBVdnd4eEk0azBZc1JqeDBOVXVhNnNpK3FIRDlYRFZkRGk1TXd4U3RpblRveXQvaEI5RDlpZE1jcWJFMGlUaVkvVHNxbVpvVktrWTRrSXN4eHBFRlRHaVFpRjhyczBza29ZMmszN0c5ZWtTVW0xTW1scVJsVVMzVEZrKzZGSzVzYmZkSDdiOUg1UTZVdFR5T3BLU01hb3hwTWdpSjZHNHQwelNWME9MaXJJemJOaE5wc2pOLzB5WmR1aUxhbDJhKzRpdlJKbXpTUG1hWFE1MWt0RVoxYkZQcXlNM04xK0Mra0wvbzAwbkwvaE9wd2FYNFJSZ1JFVWFRNVVmVVdTTUVUbnVpRGFkTWpSS0RwamJndWhheTlvK0p0V1J2L3dCUFhSTksraVRleFZ4NjRjM1ZJd3NsTnVrTHZ4NnY5TWJJb3dRMVJDdUp1eHlhSld5TGtMSkpMMFFxWFp0RExpdUxGVWVyNkhqcFhFVGNTMDF4UFYzcU5FUkdwaFZka3BzM3BLSmlhbDJRaVkxU0l1aHlaSjJySnB4Vmp2c1RJeGJZcExCQmlrOWJRNTkwWXNtdlg0U2FSdVNsU3MzcjJOY1JSU1BTSUw3RFVkbVJxQ1AveEFBbkVRQUNBZ0lDQVFRQ0FnTUFBQUFBQUFBQUFRSVJBeElRSVRFVElDSkJCSEV5WVNNelVmL2FBQWdCQXdFQlB3RDM3R3pOaXpkQ21iQ2FaWmJObHdtWHduenRTTm1XelpHeGJMUlpzalkyTFltV0ppay9zOVFqSk5DZENrSm92aE8vWmFMTml6Yi9BSVdYeFpaWXVOaGVXUWRNYzY4Q3lLaU0xUW1SWWhPK05rV2l5MVE1TXQrendoTzBMc2k3UXhDNVYyUk5ya1JZbUtTNHNUNHZteE9qeVBoSzBMaXVpaEdoVmNSWFlqZWhTTDVUOWxGMGo5SGJRb2lYUWtSaXg0Mk9GQ29qME9Jb01vdDhKajRYSzRaYm9VUklTRkYyUWd5T091eFFIRTB0aWpTRWt4UUpRTkVOZEZjb1pkQ2tpeXhJUkZKaXhmWW9NeFFacXFLbzhtbEt4eEgvQUVSZEVtaG8wSERzU2RsY1B5TmlFbVVKQ1ZrSVdpRWVoUkVoekpNaUx3T0NhSEZYUlhSTjlrR1doaVhMNGlSWENSR0pDSWxRMktWa28wVlhrVXFOa1FuY1JTYm0wTHdOYlNGR3V4ajhDVmxIMXgreUtFSWlRVk1paGpxaUNkbnAxQzJUZENtWDlrUDRtTlhOM3dxdGtlSmNzWkdJa0pPdUltTk5zVkpkam1rYml5SkdMSnZHaklxYlJyVWhFZW9rZnV1SW9Tb3NYeVo0NFloSVNJUnBDZ0tLWmpjWWsyMzJoOThkbjQ4cWRHV05xMGFpVEtTaUtMUkIweko2YWZ4R3lUOG1LSFZrdWhjSkVJV1JnYUt5THA5aldqNjRqT3VoeFVrVjJ6VzEwUWRTTWZ5aVNpa1NrdlRYUXFvZEhhdG5sRHN4NDNkamRLaCtPVkZNaEdrTGh4c3AwMHlraG9pNmRGV2hXaTVINDByUmxYWFJQL1dpQ3NtaWZnaW5Rb05zNmltUmRxeG9hUlJCSWp4WmorWEg2RVVZY001cHY2UlVWNEpINDNUbzF0RGpjV2pEQzR0a29XVFhaR0RsNEl3VVUyTnVyTUt0Tk1raG9vaXVOaGRtR1VZS21WR1RzMWtsMGRteklaWlFHbFcwZkJWb2duR1JCSm85SnQwWThWUVk4WjZXellzWHB4c244cFVoVjRJM0dYZkUwYXNoRlVPQm8waFdpMkp1SzZJNUJEZ21hYWtKNlMvb1VFMWNSWTBZdkJqaXFGRkt4WWlPRlJNOVVQeXl2OGhKY05DVlBpeVB5TlVPSThaQ0Rib3k0NTRaVXlNaUtVajBrWW91REVreUVKUWZaQjN3bWlVNlJrZGl4aWdpYnBleElvaVJScWZFaW5aYzArMlJVWksyaU9NaEFXTWdrVjhTRG8yWTVzY215S1RHTjBmeTZQL1oiIGlkPSJlIiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIvPjwvZGVmcz48L3N2Zz4=", + "description": "Indicates the concentration of airborne allergens, including pollen and mold spores, which can trigger allergic reactions in sensitive individuals by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'IAI_level', label: 'IAI', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: null, decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'IAI_level', null, 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Individual Allergy Index\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 12) {\\n\\tvalue = 12;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#3B911C\"},{\"from\":2,\"to\":6,\"color\":\"#7CC322\"},{\"from\":6,\"to\":9,\"color\":\"#F77410\"},{\"from\":9,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 12 - 6;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 12) {\\n\\tvalue = 12;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 8 - 4;\\nif (value < -15) {\\n\\tvalue = -15;\\n} else if (value > 15) {\\n\\tvalue = 15;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:SUFJLXZhbHVlLWFuZC1jaGFydC1jYXJkLWJhY2tncm91bmQucG5n:SUFJLXZhbHVlLWFuZC1jaGFydC1jYXJkLWJhY2tncm91bmQucG5n;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"IAI\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:flower-pollen\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "tags": [ + "weather", + "environment", + "air", + "aqi", + "pollution", + "emission", + "smog" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_co2_card.json b/application/src/main/data/json/system/widget_types/indoor_co2_card.json new file mode 100644 index 0000000000..76f8ec0a4c --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_co2_card.json @@ -0,0 +1,40 @@ +{ + "fqn": "indoor_co2_card", + "name": "Indoor CO2 card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest indoor CO2 level telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'co2', label: 'CO2 level', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"CO2 level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"co2\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3FA71A\"},{\"from\":600,\"to\":800,\"color\":\"#F36900\"},{\"from\":800,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3FA71A\"},{\"from\":600,\"to\":800,\"color\":\"#F36900\"},{\"from\":800,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"CO2 card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"ppm\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "co2", + "indoor", + "carbon dioxide", + "carbon emission", + "carbon footprint", + "carbon output", + "carbon pollution", + "carbon capture", + "carbon offset", + "carbon reduction", + "carbon neutral", + "climate gas", + "fossil fuel emission", + "carbon cycle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_co2_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_co2_card_with_background.json new file mode 100644 index 0000000000..27f8a62997 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_co2_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "indoor_co2_card_with_background", + "name": "Indoor CO2 card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest indoor CO2 level telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'co2', label: 'CO2 level', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"CO2 level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"co2\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3B911C\"},{\"from\":600,\"to\":800,\"color\":\"#F77410\"},{\"from\":800,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3B911C\"},{\"from\":600,\"to\":800,\"color\":\"#F77410\"},{\"from\":800,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"CO2 card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"ppm\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "co2", + "indoor", + "carbon dioxide", + "carbon emission", + "carbon footprint", + "carbon output", + "carbon pollution", + "carbon capture", + "carbon offset", + "carbon reduction", + "carbon neutral", + "climate gas", + "fossil fuel emission", + "carbon cycle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_co2_chart_card.json b/application/src/main/data/json/system/widget_types/indoor_co2_chart_card.json new file mode 100644 index 0000000000..ff165296cb --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_co2_chart_card.json @@ -0,0 +1,43 @@ +{ + "fqn": "indoor_co2_chart_card", + "name": "Indoor CO2 chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a indoor CO2 level data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'co2', label: 'CO2 level', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'ppm', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'co2', 'ppm', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"CO2 level\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"ppm\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3FA71A\"},{\"from\":600,\"to\":800,\"color\":\"#F36900\"},{\"from\":800,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"ppm\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"ppm\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"CO2 level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"co2\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "environment", + "co2", + "indoor", + "carbon dioxide", + "carbon emission", + "carbon footprint", + "carbon output", + "carbon pollution", + "carbon capture", + "carbon offset", + "carbon reduction", + "carbon neutral", + "climate gas", + "fossil fuel emission", + "carbon cycle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_co2_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_co2_chart_card_with_background.json new file mode 100644 index 0000000000..9e9b0391cd --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_co2_chart_card_with_background.json @@ -0,0 +1,43 @@ +{ + "fqn": "indoor_co2_chart_card_with_background", + "name": "Indoor CO2 chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a indoor CO2 level data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'co2', label: 'CO2 level', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'ppm', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'co2', 'ppm', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"CO2 level\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"ppm\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3B911C\"},{\"from\":600,\"to\":800,\"color\":\"#F77410\"},{\"from\":800,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"ppm\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"ppm\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"CO2 level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"co2\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "environment", + "co2", + "indoor", + "carbon dioxide", + "carbon emission", + "carbon footprint", + "carbon output", + "carbon pollution", + "carbon capture", + "carbon offset", + "carbon reduction", + "carbon neutral", + "climate gas", + "fossil fuel emission", + "carbon cycle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_horizontal_co2_card.json b/application/src/main/data/json/system/widget_types/indoor_horizontal_co2_card.json new file mode 100644 index 0000000000..97592bbbbb --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_horizontal_co2_card.json @@ -0,0 +1,40 @@ +{ + "fqn": "indoor_horizontal_co2_card", + "name": "Indoor horizontal CO2 card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAmVBMVEUAAADf39/f39/g4ODg4ODf39/////k5OQ/pxrg4OC33qnn9ONXsjbz8/OHyHBvvVPP6cbz+vHHx8fOzs6f04zV1dX5+flYWFhLrCjb29u2trbCwsK8vLzn5+fIyMiQkJCTzn5Xsjd0dHTD47jb79Sr2Jutra17wmHn9OKenp6CgoJmZmY5OTnD5Lhjt0Xt7e1KSkqTzn9juEU1uySMAAAABnRSTlMAIEDfv1C6kOEmAAADkElEQVR42u3aa2/aMBiGYdZ2b0wSx3HOISGcz7Rl+/8/bq+dCE3rhAobG6Dn+mCMjSbfDRlCbQ8AAAAAAAAAAAAAAAAAAAAAAAAAAADgX/py92zG07Nz7/rPX7nDyejuSeep9yLpAWQvvT49hD5CbgxCbg1Cbg1Cbg1Cbs3nQjajlCgaLc1Qm+cp/STd0v/3qZDtNjpEy120W0a7dC+JphFJSQ6RYx6jnC41cz0e566leRouktfrhRwkpU4eUbTjY7chdZ5vtoNo6hx2m8tDXCFcfgiExU2JYBN9rZA9sTylNCcajciE5KPNIZpuB9Ptcn9xyNpvQ5RYtFckFJNwvhLja4XkEY2i7YaWU9NhQ3ajKKJ9TlN+vDhkLHwb8iaoFZinWhRkaU/r11fPzki7ITGvm+mQN06FhC6/TPOP5+eXpXmeS2efH5xon+e1CUnz3YZGU97ilAtD1uKbsiF8ck1ki8xpfeGR4YpVIfgamcCV4Jln9gIzm/s8Lk6FiDdV0KxQfkI/k8fhSB4fL+VPPNVdAj7wymsTzBh2ISKZBaLQxEPivgll9iauyxXfzMbJEI88chUlCV1bwhE2ZC2KYMwn/BiyIjIv4ZAF9xZi3u4tTBIVwvtciBIioOtZm3/dhpBe22OGH0ICeyON23uHVjzaPdeG+KdD1PeC3EJNEtLeQtH1+CJx+e0SvB5v9OS3IQmPbUhwTsjM3uw8ejT21YKuRovOhELXsz958yaY2feMPobY9S7E7J4O+T0VuOGJ+1m+/7og6Ryh8SbGc1qIVXcdEvMRMuc2y+3WZ/Zq0VoI76KQsVJjsgZtjKT3kqwso6wfm4U+ZUQ0XA4pK8t2gxc+ndT+r+WLIFHm+Lrgmc9rXUi3ziF2PaCLQoxjSJrGZdPEPKuoKqs6G1RxmjZx/R7XGVE8lHVdkoztRtzU54TQWgkhlDmW+XwoFmTY8/LT754JGfNM6b8SMpBNPIzbkGFa8pzXhmVcNTakX5Yc0tTDRnJuJeks2tPU8o4zc4/w0+4T38w+ODdkGZdVPEibSporEtdlNZCDJua1dz54TCYkM1dkWC/NRlpV9OdsiGFDrvLFqhye2Eybksf7CMmyU5uS/pL58avJLJnjq+69QcitQcitQciteaCQlwf4NTuRfOk9OQ/wi/bMeer1vj73nXv3/PRIf1QDAAAAAAAAAAAAAAAAAAAAAAAAAAAA8I/8AKu9AITe3vWrAAAAAElFTkSuQmCC", + "description": "Displays the latest indoor CO2 level telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'co2', label: 'CO2 level', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"CO2 level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"co2\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3FA71A\"},{\"from\":600,\"to\":800,\"color\":\"#F36900\"},{\"from\":800,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3FA71A\"},{\"from\":600,\"to\":800,\"color\":\"#F36900\"},{\"from\":800,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal CO2 card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"ppm\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "co2", + "indoor", + "carbon dioxide", + "carbon emission", + "carbon footprint", + "carbon output", + "carbon pollution", + "carbon capture", + "carbon offset", + "carbon reduction", + "carbon neutral", + "climate gas", + "fossil fuel emission", + "carbon cycle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_horizontal_co2_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_horizontal_co2_card_with_background.json new file mode 100644 index 0000000000..d98464e003 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_horizontal_co2_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "indoor_horizontal_co2_card_with_background", + "name": "Indoor horizontal CO2 card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest indoor CO2 level telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'co2', label: 'CO2 level', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"CO2 level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"co2\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3B911C\"},{\"from\":600,\"to\":800,\"color\":\"#F77410\"},{\"from\":800,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3B911C\"},{\"from\":600,\"to\":800,\"color\":\"#F77410\"},{\"from\":800,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal CO2 card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"ppm\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "co2", + "indoor", + "carbon dioxide", + "carbon emission", + "carbon footprint", + "carbon output", + "carbon pollution", + "carbon capture", + "carbon offset", + "carbon reduction", + "carbon neutral", + "climate gas", + "fossil fuel emission", + "carbon cycle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_horizontal_humidity_card.json b/application/src/main/data/json/system/widget_types/indoor_horizontal_humidity_card.json new file mode 100644 index 0000000000..93882a86bf --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_horizontal_humidity_card.json @@ -0,0 +1,38 @@ +{ + "fqn": "indoor_horizontal_humidity_card", + "name": "Indoor horizontal humidity card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAmVBMVEUAAADf39/f39/g4ODg4ODf39/////k5OQ/pxrg4ODz8/NXsja33qnOzs7IyMisrKyf04zz+vHn9ONLrCi2traHyHD5+fnb29vCwsKQkJDn9OJvvVPb79S8vLw9PT3n5+eTzn7V1dXP6cZYWFhjt0V0dHTD5LhmZmb19fXt7e2r2Jur2JqCgoJKSkp7wmGenp4vLy8hISH9/f1hfASuAAAABnRSTlMAIEDfv1C6kOEmAAADLUlEQVR42u3aa3OaQBiGYdukD6+7nJGDKApKtDGapv3/P64cYtFOqvmgDXGea0YHd794uy6DIwMiIiIiIiIiIiIiIiIiIiIiIiIiIiKi/+nLp9dkfL0zPrvh3beqw/iOT++78XVwr3ADvt8PhrgFv4Y3EgKG9A1D+oYhfcOQvrnxkKfSwwHHQJHhwBaA7dgO+nOd9maIK6J9dJY2nAelYMBonuEAhjMubHunDKgr1qxMH8DUbHhoeBvXPxPSdRyXLH/Y2wf7AUtjtp2t10vMsF3Pxs54PBu9qK2DazFFTAChNHzUUq1Lmb4nxJVG6XUhD+P1a0i1EPhZhbzAqUKMJcY/ZgauJNVtyEQ23Yq4gQcdVnOLMyFTeZUffbWOQ9RsH2LP1riWXHQTUgo6rq7KQuQaZ0K07C2O98hu/dKtyHi9q0LULsPSwZWkkk+aEAngYe9J/FTnqfhnQkyRcBNI8CyicUy98UIZO4Ur0dpvQjwJApFnHw2vrDewDnEmRIsAYTBFWS/JeT9GuBK3imhCUgnCXHef63SKJ+2fDGl3iLsqp3CbXfKBUgnRhsBLq0cgi24uMKeu658KWUglaDskxAfS4ppmKeEKrVBc7IVhGkxKmZ4ISV87njfBx66IJ680FqYPIO9CTPHzAND5iRAEIiu4AoT1QUvhDeoRx5TCJS1qpeTVhpDnZoUW+0btImxOwqdCXBGd1/vkYHeN2hiFxxiNKEI0tOqBISIAc2eOKI7biQi4WFJ71tISupPu7aw08BT41UY5FeJp2TMPQorCim3bqo4SJHGSRaPEKgrbyh6tLAKsucqyGMpqJiw7u2QI0omITHwc8CbViHcqBOm+ZIPDEGukbGtutSHzIq6Oq7F5bCV2EzKM4yrEHs1tVeUmChfl+R7+4vvnrrU8sxQJwin+cKw4sUaFnah6RawsTkZqZFvV2GM2ty3UIVG9IvPMqSeKJMEFXP+HVTzHvxV2jPfoQ0gUnZpUeI9ehPQRQ/qGIX3DkL65pZBfuAXDwf0N/M0OqPvbuWFg8O1uaHx2d19v6aYaIiIiIiIiIiIiIiIiIiIiIiIiIiIiov/kN5uj+TgHcSVHAAAAAElFTkSuQmCC", + "description": "Displays the latest indoor humidity telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'humidity', label: 'Humidity', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:water-percent\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":30,\"color\":\"#FFA600\"},{\"from\":30,\"to\":60,\"color\":\"#3FA71A\"},{\"from\":60,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":30,\"color\":\"#FFA600\"},{\"from\":30,\"to\":60,\"color\":\"#3FA71A\"},{\"from\":60,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal humidity card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "humidity", + "indoor", + "moisture", + "dampness", + "wetness", + "humidness", + "moistness", + "dew", + "water vapor", + "condensation", + "dew point", + "steaminess" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_horizontal_humidity_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_horizontal_humidity_card_with_background.json new file mode 100644 index 0000000000..91a1348b5c --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_horizontal_humidity_card_with_background.json @@ -0,0 +1,38 @@ +{ + "fqn": "indoor_horizontal_humidity_card_with_background", + "name": "Indoor horizontal humidity card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest indoor humidity telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'humidity', label: 'Humidity', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:water-percent\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":30,\"color\":\"#F89E0D\"},{\"from\":30,\"to\":60,\"color\":\"#3B911C\"},{\"from\":60,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":30,\"color\":\"#F89E0D\"},{\"from\":30,\"to\":60,\"color\":\"#3B911C\"},{\"from\":60,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal humidity card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "humidity", + "indoor", + "moisture", + "dampness", + "wetness", + "humidness", + "moistness", + "dew", + "water vapor", + "condensation", + "dew point", + "steaminess" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_horizontal_illuminance_card.json b/application/src/main/data/json/system/widget_types/indoor_horizontal_illuminance_card.json new file mode 100644 index 0000000000..011d5ccd53 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_horizontal_illuminance_card.json @@ -0,0 +1,37 @@ +{ + "fqn": "indoor_horizontal_illuminance_card", + "name": "Indoor horizontal illuminance card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAmVBMVEUAAADg4ODf39/g4ODg4OD////g4ODzaQD7x6D2jkD82r/x8fH+7N+srKz+9u/Ozs6QkJD0fCDIyMg9PT35tIDz8/P5+fnb29u3t7d0dHTj4+PCwsL3oWD0chC8vLz4q3Dn5+f0eyD9489mZmbV1dX9/f31hTD70K/U1NRYWFj3l1D6vZCdnZ1LS0s8PDwvLy8hISH6vpCCgoLwemShAAAABXRSTlMA7yC/r1EOHTEAAANOSURBVHja7dqLbqJAGIZhtf38OSMIogIi6mrVdk/3f3E7DN1l27V1jcag+R4bMgxJM28GqYntEBEREREREREREREREREREREREREREdHJeg/d/q177KmOrv0FN+6L3e11Hm3cAfuh07/5/ah86Xb6uAt9hrQMQ9qGIW3DkLY5HFIEuDWHQzLB38ZT1w1dNOwB2uZ9iJd56mhkUPYL1CbhYOAObBtTTPURrh7AttWPOsW0PrmMjeGo48LQquVYP6INXlli/leIIWsPtUjMv0PUC0N8m3xdLpf1IHzeLl33ZTnEz+V2Oth+DXEJhoih7wrNqRaiBN5pIYgieNFunS2wML1/Q75j4E6HqF4TdzpYTtwJhvYQq+mL+3OCC3CCOsSUfb0jlpTWYifFiSH6N1UiKJ+HbGfPTchwHE5xAYUEOmQtqGXVqSd5E+I5XrVO50hIIDXrXcjqZfntbcjzdqtDMFi+TJ+HSxfnc6QwdYjk8ABdZOlVOX9CLMk9GFJ6n4YYUjuwhfaHE3Z9uICgdMzXLchFdo5O0CFWc2sVEqHatw9DfpgFCvkNlmniyiIxoEMcybOilOBgiJfnkWT4OCRSIVkTsrl6iCMZ6hB4TrVgsQ6FYF8/z064ta4skMgw1pJtUMskOhji5LLGkRCvlJqBq/PkVQDLcFC/GUypsnLxmhAVmIt19PFbvn/8xjggtt9PxDibVVlLscBedq/7EEkBLKREE+JIaUjgHQnBRpQdGuM6JoadQBuNMOr71UQfIwBzd45RktQX1MR5SfVTq5QsMqvle7kaBWquCanOdhIdC1mIkr0NWa38JAx9NUqRJulsNE791Sr0Z7Y/GwH+PJ7NEsS+vuCHs7ND4JgiYupPXaVIvkcTEkmp3yaLIyHYGIbhvA15ehrHoT/365D5U6LGam6e+GmoQ/pJokLC2TyMVW4a43z6D7jm6NHpH+Mt0yzwF9dPUn+8CtO42hF/lqTjeBz6as5WC/dRhYyqHZnP3OrCKk1xDcdD1kGBTyRzfOwpTHB9OuRko9FnF2P8j1aEtBFD2oYhbcOQtrmjkO59fBna7zzcx9fTj/fzDwOd3mP/1nUfeh0iIiIiIiIiIiIiIiIiIiIiIiIiIiIiOtUvJ7/Ykm8IwzsAAAAASUVORK5CYII=", + "description": "Displays the latest indoor illuminance telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'illuminance', label: 'Illuminance', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Illuminance\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 400 - 200;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:lightbulb-on\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":100,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":100,\"to\":300,\"color\":\"#FFA600\"},{\"from\":300,\"to\":500,\"color\":\"#F36900\"},{\"from\":500,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":100,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":100,\"to\":300,\"color\":\"#FFA600\"},{\"from\":300,\"to\":500,\"color\":\"#F36900\"},{\"from\":500,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Indoor horizontal illuminance card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"lx\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "illuminance", + "indoor", + "brightness", + "luminance", + "luminosity", + "light", + "light level", + "light intensity", + "lux", + "candela", + "foot-candle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_horizontal_illuminance_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_horizontal_illuminance_card_with_background.json new file mode 100644 index 0000000000..ce17d157c8 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_horizontal_illuminance_card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "indoor_horizontal_illuminance_card_with_background", + "name": "Indoor horizontal illuminance card with background", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC91BMVEUAAADR0dTPz8/67+uzt7zS09b37+ezs7v67+vS0tbPz9K0uLv27ui2ub/Pz9f+//7+/vz++vjw8PD//Pn08fD3dBDz8PD+/Pv//frv8fL28vH18vL18/T/+/j8/fn8+ffu7/H8+PX39fT69/b39PP08/Tz8vP9+fXIyMj8/Pft7vD8/f7x8vTx8fL29vf59PL89/T38/H9+PP69vT59fS9vL/w7u62ur78/fv79fLz7+3v7e36+fn7+Pf28e6/ub35+Pf8+/rU1Nby7+/++/bLwL/W1di7u74XFxnKwsXCu77Y19nGvb7+8+i6vcG5ur65tbrNxMe8t7vh3dz19fXNztLLysvPzMr+9u7t6ejS0tXJyc3Xzszs7e346+HUzMu0s7f49vf+9Oz57+vX1dTKycjNw8LDvr/y9PXk4d/j393o3trc19X09ffq5uTs4Nv8+/X68u/07Ono5OLT0dHMyci+vsD4hSzj2tjd2djRzsz8+/T99+zv6+r77eLm4uHNzM25u7+2tbnd293Ky8/Gx8v37unCxMizt72wtbz9+vHy6efc19ra1dg9Ojn++PH26eDQ0dPGxci/wsb//vzr6Obn5OXf29rIzdTHyczh3+Hg2NXY09H617zl4OK8wMQeHh/v5+X05972k0j57eba2dvN1NvGwMG6trWJiYnW1tmko6P4xZ78+fns4+S8urrywJv5lUn3fB39+vTw49xycG/77+bn4t/55dbX0NHFy9HUyMfIxcTBvLuppaT2pGXk6OrU2+Hs49/2rHNISUr++ffg3OD97tvc1c/6toJjYmL54s782b/quJJXU1Lx3c7o1sqvrazzs4KCfXz2girm29Pg0MS2tLK0r66ynJT3jDorLS7b4unw5+D86Nnz49m/x87JvbfCsqmQi4psa22Jh4b3nFZBQ0U5OTvWxb7z07vaw7L3zK2al5b3vI/wnmCHbmBhW1kvJiPx2ceMd2u3pZudhXryj0NQOjDoybKmnpzvrHnGrZrE1aY2AAAAD3RSTlMA7yDv768gIL+/v7+wryBdcwczAAARbElEQVR42uzBgQAAAACAoP2pF6kCAAAAAAAAAJg9OlZpGIjjOK6iOIi+zXHjQSC55gy5JGMotIPk3iB7sxiht2Ry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", + "description": "Displays the latest indoor illuminance telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'illuminance', label: 'Illuminance', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Illuminance\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 400 - 200;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:lightbulb-on\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":100,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":100,\"to\":300,\"color\":\"#F89E0D\"},{\"from\":300,\"to\":500,\"color\":\"#F77410\"},{\"from\":500,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":100,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":100,\"to\":300,\"color\":\"#F89E0D\"},{\"from\":300,\"to\":500,\"color\":\"#F77410\"},{\"from\":500,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Indoor horizontal illuminance card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"lx\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "illuminance", + "indoor", + "brightness", + "luminance", + "luminosity", + "light", + "light level", + "light intensity", + "lux", + "candela", + "foot-candle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_horizontal_pm10_card.json b/application/src/main/data/json/system/widget_types/indoor_horizontal_pm10_card.json new file mode 100644 index 0000000000..2bdde8beb5 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_horizontal_pm10_card.json @@ -0,0 +1,37 @@ +{ + "fqn": "indoor_horizontal_pm10_card", + "name": "Indoor horizontal PM10 card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAn1BMVEUAAADf39/f39/g4ODg4ODf39/////k5OT/pgDg4OD/9N//6b//3p//sSD/vED/04D/+u//x1/Ozs7IyMjz8/P5+fm2trbCwsJYWFja2trV1dW8vLz/zW/n5+etra3/2JCQkJD/tzD/qxD/46//78//wk87Ozv19fX/+e/t7e16enpmZmZKSkrx8fGenp7b29v/tjD9/f3Pz8+dnZ3/2Y/zJp/4AAAABnRSTlMAIEDfv1C6kOEmAAADdElEQVR42u3aCVPaQBjGcVvtw8tmNyEHVxIIp/W21/f/bN3dRGkR044MHaTPb0bmdcOM/F1iZOCMiIiIiIiIiIiIiIiIiIiIiIiIiIiI6F/68O75jI/nnfeue/7JdnQ+49373Pl4dpHhBHy+OOviFHztnkgIGHJsGHJsGHJsGHJs/ouQnkJtOpt1MC2A/AFIc7fQwZFpCenNTQ/e6CG9zEeXwM0dbkYzFLf5Hfay0l4PnhtaqZd36Omlag/ZdEiEJiTF7WB0m3ZczHSG2QPu9tuSSLwATuWGVrHBlqUYs1i9CGnvwOhuNMJodjOdNiFTjPYLWUukLQVLmT+GLOIXIRGURH8KqUq96fA74m9uL+/rkO8PuLzHPkJReBLL4jlEKX9u9mAFS4V60hK4Rb8CpQM4ZWhUa0ig642PsB3y/QZ1yP3d7Q32YgQ91LRE5ilEifHPu9iOcxGJJfT7Z1cDmbuVyD02f+5WsQQtIUEoErmSL2hzj/3IYi4SKv/EMtgVEstcV8aHKIldiJTL0kZUSyMRqhKQ+PWQShxfEuBwrkTWkfG/2VhWu0KUiM1c+hAtKxviRyPafb+2X2Yhy9dDAnku0Tig3grAXCqsJMauEP+4m9vwy2Z099RuVJXutf35DZ9LFA4tktI2RFovpAy2QvQmRIneEeK1nSPyVHJIK72qQ5Q0wq2QpTxvQyTqDSGIpBZgI8MO2fX2Qoa/1DyUueirwDFSrZoQ8adNjKuFqOZ+JsRbQlCKY4CNQR2T4XoMr99Hv5u4hS76ACbTCfrjcX2g35S3uDKyjsLmh/x2joiJSrEhKN20kNAW6DeFNHuit0LyPBmnaWKnIYbjYdEfDJM8T5Pi+lvRB5JJVhRjZN/8gSQt0E6tRSRUO0IWsghdCEo7u+tILL03hkDFImo7JBlkaTJJ6pBJ/mhnuzYZJ8PUh3THYxuSDiZpZnOHGdr5K/ULSsxm3U5a1jDxHv/9qgC/miaPw2SQp8PM7UhSjIeDbJAmdu26mKQJXEjf7cikmLoD+XCIN/AhaFRrhcCIPugLq/EEr8vTR1j7hvwQJ8ZBQ/r9toPZHlf8qNpERXEZ8KXu+8WQY8OQY8OQY2NDvuIUdM8uTuBtdiC7OJ0PDJx9Ou923rvzj6f0oRoiIiIiIiIiIiIiIiIiIiIiIiIiIiKif+Qnt477Z3V+66gAAAAASUVORK5CYII=", + "description": "Displays the latest indoor fine and coarse particulate matter (PM10) telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pm10', label: 'PM10', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM10\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:broom\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#80C32C\"},{\"from\":50,\"to\":150,\"color\":\"#FFA600\"},{\"from\":150,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#80C32C\"},{\"from\":50,\"to\":150,\"color\":\"#FFA600\"},{\"from\":150,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal temperature card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "pm10", + "coarse particulates", + "coarse particles", + "particulate matter 10", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_horizontal_pm10_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_horizontal_pm10_card_with_background.json new file mode 100644 index 0000000000..f5c4edad54 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_horizontal_pm10_card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "indoor_horizontal_pm10_card_with_background", + "name": "Indoor horizontal PM10 card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest indoor fine and coarse particulate matter (PM10) telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pm10', label: 'PM10', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM10\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:broom\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#7CC322\"},{\"from\":50,\"to\":150,\"color\":\"#F89E0D\"},{\"from\":150,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#7CC322\"},{\"from\":50,\"to\":150,\"color\":\"#F89E0D\"},{\"from\":150,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAaAAAABcCAMAAAAfz5G9AAADAFBMVEXS0tHMzM3V1dXQ0NDJycgpKSnExMXh4eHKysrOztDOzs3My8vHx8fS09MuLi7e3t7Z2djDw8Lb29u/vr69vb3X19eZmZm0tLTBwcHW1dSMjIyFhYWSkpKioqK4uLh7fXy2trbU1NK6uroyMjKxsbKXlpefn55/gYCsrK2Ih4ePjo+VlJWpqqqKiYqkpaVvbm5WVlbZ19ZycnKcm5x3d3evr66np6Y2NjZbWlpTUlKDgoPHx8qhoZ9kZWXk5ORoaGidnJhERERJSEh/fn9ra2t5enmamZSxsLB0dXS9vsJjYFldX17Q0M2srbKPkJK9wsjIxsVzcWpNTE1CQEHAwcW5ub+Gh4x7enOcnp2kpauflZE4OTiLjJHHxMOimpZLRz54d3BdWlCsrKpOUVB9fHaDgnyMfnebnaRfYmGhoahjYWNgXFWckY2YjYmppaJraWKTkYxoY1tYVU2Cg4l2dG07PTyno59zc3uCc2k8OjrAxMuwsLeRkpdVUUieoKS3vcPCx85pZl+5v8anqKiQjomNjZhtb3iZm5+Wl5yYlpFCPjSnqLB/gIV+fntGQji1tbyIeXCSlJ5wb2eVlI+NjYY+OjA8My6in5qHhYB7fYM+Pz6NkZ2nn5uKiYMwLTeQg3qwtLmAh4Oxtr2vp6V4eH+YmKOFipOShoCnrLN8bWM0Mj+AhI/DwL5vbGR2aFw3MCyGjIlkZW4+P0u0ucCHjZmdpa2ViYRqa3NPTUWrqqWPlZJ4d4VTTUSGhJJWXVkzLCo4OESzrKqaoKnGy9NRU1uKkY1qcW5kZV7X2NpcX2lFSFG7ws1WWGNwYVRLTVg+R0K+uLi4srF9fYuqsLiTlqMyNy6DfneTmZZXQjWiqrBgTT+MgX5jaXZqW05mVEaBdnBuaV1/eXxmYlOTnKa7u7dET0SNm4J/kHRwd4GGkX0/RVns6+t0f4y1vbz5+PjO0dlHUGNdaoBMNi1nc4lVYHV+lWJLVWyWo4xpgFGLn2+drJWvwYybrn15hma6yptWcUaVZoxXAABtEklEQVR42lyae1TadRjGu2mXY5TpwQUiJiBX4ReCXBoRF6fCUQO8hoWIJkKNUGnhZg7NbZGzZpOmy7XcXKtlNG3lynSVZdkulW1dt5nd753+6HZO5/R8sfs7+okbOsfn97zv8z7fztn12GOPHZk4smvXxNzxhYkjE59+eiL0qd2jMRiqq7cGLJb2+oHqlzP29+4cG9v34dBoacW9+9qGCp/sO9BVPrrz49dfWny9o7vkxX21lfsP7Z6c5FpPqhx2v9rv7uycjg1GQyEOjUY72d8/2N/qd1uCy8vLwaWl4BKeLC0tB8eXbLbqYKDTP9Xp0nxktGvtToeBFo7NOoopjpNG44Q4HBVNZTQ6k1ROTng4rLIbjJRd5VAl0WhJqJWrKqQKhSju5dd+/fUltml/xNZY3R4IBq1Wydfffffd1w+9f/ULd7v647TBZVvR9PtPHHv00bNnz+7IFPPp0tWsglyemE6vEXAFAomyQSDgZudk4VdOVlZWRnb6qlWpX1+YnZUhKMili8vyhEwhU9vXVtXXV7VtrLl5rLml5Z7SeysrSyqSk1OuuSYF12RyTa5Ix/dIK8niSiQSbsb67OxsrrKh4XDu3bdt39F0qn1g6XQw4h8YkOl8arV6xqp/5eAz6/buXbfpmdeOHp07fnxChX9ZogigXY8deWwXLgsTC0cmTnx64gSl0VjVmsbq6vbGRtu4rellZWlvb3NL7zMHRu+pqNzX1jZU1wZAkx8f+v6l0ddf7+4+dKB58r39XV1d630cp8Fj1enVGo0vbDQa7JwkIHK5BqOuzlZ/JLi07AajIOgQRjZ8uuS2TLkt/k67LxyzJxns4bgjHnPINIQBzUlLoiU5PR4HAcUxcGg6tUGlUalW6CRxODTyKhUnicbxCLMvueLCC9pdrRFbU2P7eCCo9gkSgJ5+/+qHXK5YLOnk8tID7jNPPHHHHffdfLNWK2TSCwokEmlufk2uNDtDWSTgKpUCbkZ2RhYAZWdncLlZaWmpX5/HzZCwaug8EZPPZ+bl5QnbqqoIoLGxO1tqa1tKKytKSlZVJKdcdNE1oJTyJ6AsbnZ6DldSWyspFZRm52QrJQXSww/ctX3HzfedffSJJ9yn3a1+v9WnxpvtmXlFfnDTur2bDgLOwvHjCwtHVvDg33YObdcu8qDtojlDhoUTJwggjVGj8803ba0esCwvu8ebns1Q9j4FQJsOjO4vr9xXV1e1ofepru7Rjxcv+/51Aui9nS/uPnRosntyvTlsoHR6WTEhPGtQezROmjOJ1uqKx1ydnf7I8nJnf79ruhNoxoGp2hZxL/v9rtbxgMU95Yp7OGorXgnpTVnw9qs4HIJB5XCEQIJjN3ucaghIYwwlJQEOAYSPIYeDhk/w9962Nu2TSLTVMl59qrG+PWL1KhOAHnn/6kdcrulY0uy4+4Njp5949L4PPnjkLhFDVEaX4G0rkK5ezerJyRAolVyusigjI6Mog9zx+FiUkZWz6sKsBklufr6YIeTztUymlilsq6tqK4OAmsGntnR9SUVJ+qqKiuRr/qwUyKiicn1p6b3r77nnnpbaUogsK0MilRYUrNnxwamzdzx67NixYLCz1RLx+SiKIgoyH1w3NLTu4GtHjx8ngCbIPx0FQEkrrHCfnzjhsaO5eT49YTeovZS6ur56fOrdY25LY823gt59Yy3bvnhqcrG8ZN+Gug2bx8a6uxc/Xrzw1ddRXYeeOrR7/3uj3V3rZU4HpdfrZUa1XhN2aDSaUHiY5vS7ZmPTrZ3u4LJ7OhqPxaLT7lbS60YaLa3uVtxJgWBkvLUzpuHI1IODrdHBfotlOImDouHLOU4IHjQ0bF/Y4DQ67JSB9LUwUQ9+cIPdoIJKHTp1xDYyMh3tDAYGqhvr68f1aulfLe7paQJosLrziTPHnjj7yC233nqLWCymry4QCJQC8tY1ZEE9GdwMJbhwi7jZhI5SADllpV0m6FktFovyGEx2IdvEZ7PR3/KqNoAP2tv69fdWrkoGneTkay6ChK7BgzBKywKg9ffUloIRnqzPKGXlsnKv295I5HPsNKp1atoS0OvVaqvX5/V6D+7tg4J8R1cGzRGVirMC5m9ANJX9Uw8elMdjtxs0GjsBNDD97hPLlq01u/fve2pb89i+nV2L5V1Pbduw5c6Wlq6uxY9HL7zsJSKhQ0+9N7l7cbSrqxSAPGadzqqTfaQzGu1qyuBwDLvcrvjgdGsrAIFQLBYfHIzFXO4l247GQKfLbbFEAmhLkc64Q8U2xPojUy7/KUuYAywqlcHIoTmdCRYqjxE/d5hj9KgIIAdpdCATsjtIixuWyQORCBUdnIrYBmw2mS3go1grCjpz+dMucmf010+/f+bRO26+9aYb1lzHo+ezeiRF3AyuRFogaUCHAxtyJWwgIiXISQRF3JzLlD25ZQwmn6lNADKZ+tDi6rYQ/ZQCTwnooIDm3HMJoASm5PR7711fCvWsxweCqLRWzMu/LrOp/Q7C5wzK3Tplichkep3Xp9PrdXp2H3tvYbHZq7aHdu1SQUE0FFHQX+X81AP1zHhnZjQeu9Fg0LRXN9qmOm9utDG3le4/0Ltt59vbPu4aLZ98aqx5c0tL7WQXWtyrCUDlh3Yulk+OdnV3tfjCdrXcp9bTmVqZ0U7pvRqHw9Pp6h8cdLXCJCyBUH806gKgWP/08vhAe3DaHRkYjwRtO4KdgyqVSY2B5HLb6v1JTo0DWKAgYKKBABlJeOAGMpDJhMmDS0gFCYVp6IYOtVmvozwO1bBVZpbp9XK92roC6Ob3L39o2u2fjkWDrnefOHvfIzfc/uDqtSypVFqkJFAIIEmRMiPR2AgxpSADnkHZ0FMgESizLpOw6GV5TD4bxWcyGEJmW1Xdhi3EH4BPZUXFqlUJ/aD+JIQmV1J57+7Se2rvIYBqMahatjzJgAy32p44dubM+6jT6OyRgEImM5tl8mK5TM7XmoQMZrFZp3bQaH/i+afFoTiYPh6PV++1zlBAZDBE6rc2WabPfiQ/sHP3ewe2bdv29scvdk92jz7V3Lz5ngSgp/Z//yoB9Pqht0c7ukYny8vrZpyUnG1WK/L4bLnDjvmnsRsot8vVH+10W+ARIkv+6GC0H4DiQNQ67R+wWAJNTZbx8caAO6pKikQsls5Bf31wanZYZ4SwYebIz0t4JD6G7VaPM4Spk4Tfw5gadiYZhvGR5gAX3H9GTojy6XBbmvVeX+61BNAd719+B/Q7PTjod525476Hbnhwz9qChgY0N7QzUFH29BQUNAgIn+wiaKZI0IBfPQ1SFksqEWRdwaLzAAj6URQyy3i8sirw2bz5TtLgiIFLXlWRAgf3D6BriEkAITQ5vAJ9rqV5bKx3SMvIzNzRfuz0++++++6P7572BzpttkK5vBjUSfPUwiEK+cUyvc+I1k6o/KvF0XBREYNAWXXeGasVziIBaMd4p6X4mQM7R/cf2LZ587adO7u6uhf3NY9tqa29ZxI2+9BL37+0kSjo7ckOKKijfJ83ZNUy+XJ2sbaQbVRbKQPlNVJ4z6OY3cGABW+/f7oTnm4wPhuLx4m36wwGx0f8tkh7+3LMkdQZaWoc73cHpwdn+3UUOtywg4P25SB4yAOfGB1JHvtfgJJCBo7RSBQV8snQMNAhKNLVrWp0DivvwgSgY1ccc1nI3xqYPn3fzbc+uOeFPQIu+hjcGmSTw22AU+hRcsEHXksJPywh5q5nNQsmXJJ9CV2cmUcAKRQmBo/OyxSVbblTiv5G+AAQ1ENGD+Fz8bm4EgERRJX33rsbI+pedLoxAqhQK8q8bcejx1YATbttflu9SVFYyAQ4Yj1EeWyFHC1PZ8fa8A+glWe4UTVEQRRpcV6vVU3ZHf7x+q2BVov2w+ffHl08UEbnvbPhQNdk+f5927Zs2FC3hbi4Q6+/9NJLG8vL32seLema7N5Yvk0fsvKfxI3A4CuYGp9cTxn1dso/PxXvdwcGgsF597S/vmnc73bNusKz8UGXc9DV6Yo0Bqoj1Y2BfqMz0Hh47S2nl96fjs2GZXl6h91BGpiGGgYMInt0MAjG41j5oYnwHcMaO1SmMuiLdWrKKzfLzD6rz2tFeYsvTwB69+rT0wAUi7b7l25++qY9L7wAe5ZN2KTDUGcV9RCXUAQxZWVxG5QN8HUwXCxULkvak5EtEueZhNrCYoVJmEnPp/Py76yVCAR46xN8StDdwIWMoIsvPheMMIqIlIiIEi/BEKpt3tY7JGfnZfLW7Di75CY9zuW3WQaamCYTPw+AGHmMMhH6mx4Nz6wJOTl/8aGds/JUZbTLT3jAZ2YG/1m9VspunK9vrHe7LPKjb308udg2Uu22zHgnRzv2f9P32mtzx58pQbM71P366xsBqLYWVrO7fGP5ZgLoZQZTJisUKgzFbBllp+x6i2WK5rK0twcjlnm3ZWSkcdw23zreCTNNltFotHNqyjI1sLU+YgjZTh0+/ODNd5xejsYdTIFw2BEODxsNelmYk1hbk+BBDJwku4pohlzQ48JGhxNPQla5zqrWyXw+sxm2yEc2QN/FBNCxH688Nh3pdMX7zwbHP7jh9hfefPNNbDngkZ6Wk56WVQQb1yPgEkBYJ/EZ0EilufTVufRcFosrhbVW8E1sBVubl5m/One1VFCK+pMPVtOUFfsGPucnHoQUCAER4dcFH1c7trNub2GemEe/7v6m9iW3+/QZf6R+oH5EKNQyRSJRGUOUmU/PZBYqoCi5evhvAUFBWIGwpNp96r6FueNzM9YZPFaa3MCOrYH+2LRx4cChrt0jX7ox6Ye/Hd24/8AXjzlpHGtFyejOF7vAZ2NH+Xu113RAQR0dW3QnrOxnn2Wa1WaT3i7P0yUq4J5Kilrq6yPBeYs7cP/26kBgYLza1urqPzk4SDt5koNWx9FYrfqYKjBy+PCeWx66wwZA4iIt2U/tCrVMAdHTKB/Gp5GCdbAT100LQT/hMAeMQrQk/CYmj9Xnw4YsIwbBBwmpk7/++rsLTwMQ/EF/zHU2+OgjN+15880sFPpZTtqqtFWrcrhKCfFrBFAGbIMEfFazWDW8Gro4P7dGQufLTbBvWr5WyBDXAJ2Au57LRXtL8En4ahACn/MvXanzzz+f6OiaazpKSiCj9bW1zTv3Ve1l19F5Yl4mX2FDUw/OR+qrt47kiYTQT1lmmZguZeXyGEwkFQq14z+AElHC3MGjbQtzc0fnIB64BJ3O522db2wccPcPOhcOLHbtbvwc1Rn95NuO9/Zt2kU7SZspqRh96u3JjaTFVdS2VFSUoMV1bPOemJG//OzDhXKFSW+WseX6Yr3MOz/fP9tvGRgPjA9YWtvXbG9st9U3NjUNoNMNnjwJRLEoJ8nhsOvjMcuOtYdvf/CGW+oHZx1MnoyDhMfK91GasCo8rNOFVUl2q93JMSZc3TCuIQ6eGAzE3lF6nVqjtmoonxzKVUNOlCb7smu/vuLMu1eeibX290dbHz396CMPvPDGG598kkMqfVXqqlWr0rPh20gh5MlQQkGs3NyaXFaNmC4WYeTkChVmNuy1ULjCp6AHdgLmImGwK1IS04fIB4DOufSccwihcyCjiwmgii5i5lqa337+rb2b1lWVMVCmwuIBW317YL66aftdInFmJp2HouNWqKFn5kFSMs0wh/N/QBNzc0ME0HGvDoCsr8h0OsqydUe1bck9c/yL/RUZv//+62+Hf17+pGTji3WbnACkKUne/c3O0XIIqKLinpbSe8vLOzZu3PbagndTW10fu6qPKZPP2eVmuZytC8z3wyNgF223zfvr12xvahq57bbb1qypd7v9QBSfjUbRZWkqTzjeP377nj0P3n77fT/Go3KTPuQYHlZDzoZhpyNkMEA4RquBw3GgvyXRnFiHhsmzUCjMgYvT6zRwCBpKX1xo1qh1lIbSiFddeeXVZ3686n2SDfZbgmfO3koApaHS09PTUgkgPIG7JoRIYCZo6Mml5+P9yi8ry8xjwBEUy2RstpaJEuUXEK1x8TpiEMAnYRAwc0hXI4DOv5gQSnQ6tDj0uN0rA+itL555Zl1VXZWWz2cXy18xm+Xt7TtG1qzBQCNNk4VWShfTkVZoTYU+O8lNSNFQBNCRIxOoTQtzR48uvPYK6XCv6Lx6ytI00ljdWF3/Slvztw2///Lbo5/9/gsAvffkppNoi96Sit3fHFgEIEzDe1rWVyaDz8be4wsHNw3hbunbW4ZgSUXJCovlOot/Kuz2B9oD4xa3v/q6u5qatq9efR0r47oRJHP+ztZwf3Rlz4nHOwNr4bH2vPDQu/E4pdA5NPD7Bsqsh682GPCDqzRyYxINZMLob/BxBg6ehUOGEM2AmeMxakhMJVOYNR61xmjU865T3nj9mR/Peb9xqrX/R8vp9z+45cEX3khPezw1Ne0vQKTLZQAQWYqUSEwBiI5bml7GyMxj5jHyCmUKk4mJHI6flw9zIIDByOCuz0knASkxCPgF/VwCPgC00ucSg4jYBPCBfnqfTwDq6xsqhFUvlhe/8oqisen+7avXSvMTRgSuUZoJB8/gswkgQ3glY0SpAIjwWZh4THfCM+Px6PfqKB1cnN6s1jNNilOnGquLH37y5YcHnnjCNd0f/aQiWfKkXJW068hRpNkHDox2d2AElbzXjEAKzzr2TSw8s25d36ahKi3DR/mMWMKsiObMnTHkbQNYctzUR7kwlQxpkeRwWrZyJFJdHYgg7YnGZ+Ozs7Mxd/2etS+gbjgdH9Qo5IZhI2V06DA3E4CGEZWbDSqVExOIjB2kCMaQCn/iHIw7/VaNGl3NQ3llCq/dgKQ2rBHR8wse6B+83nXK5XJFkSl9cNODb77x+OM33ngjwCQnE0JpCJ4zSJcToJRKiZSHqcDLh7MSavlMkZipwIJKYmwmQywlgIiZyEpDPEq2U+IHcAWYleYGRCAFPsQlAFBty9g2AFq3Ti6HAYCRLsa3kxdjKaXzWAI4RlYBcgyJUioWCvO0hfjDmeFQKOR0IhyGJwoBEIl+AMgLQBSlW6ezms1WDVI8Hb+4+FR1deNW/sP8rfXtywPznRPp16RIngwkJe167LXu8tFv3hrt2vhSeXn5oY/3AxWqbmHutU1VWza1beGb5LL5AF8m88rkhWZXf6c/aAnabEG1icUrK+NJMhoaUtOL7t5610j7/NS8zd8fn0VFp5dvvemF2998c+2jsZhPpFUb0GsNlJXyODlYhZCAqOxYjhwrSxAujmGVKoTTB6xUbreHgvnExSejHMNGozEc0+fl9vCmY3dHg9MA5I8EHiGAwCf1xtRUCAB8UtOAJwGIhDtYUAvE4kweQywSkvAAmykD6tHmCRnod/QCJfESxGGkA++KtSYdjgACIswgAgjPQQgSItsqJDTW+/xQXyH4KLDoFBeSRALSpOdKuNBsj6RHAPn25DNI2qcoZM+cIICGnSSkH3ac8xgAHXls4rEjRz+lZkDooA4mSEfuRT1bXl9dffbUVny/hxtPndo+0jSUXZHaMxKI0Y5MfFOeANS98aWOjvJDL+4e7cYQKu+dAKCyLW1tVev2yhXtAblMofbJTFYXgkEAam+P+LSsfOxRkixlxkWPN+Tef93da+5y27bOu2Kz8Vi839Vqu3nPDXteeGBpNq7+SFhIqY1WrwbJKwdpO4IfGmy1gTImViAVOh3pAg6j6mR0qtWtpoj7VHu9OhlCdCK3sMb3kVhknO2M2hAk9fsDA08/uOeNtASg669PSb4gBZQACKYbiLhkRYXJxlpCFzJE4FNoEoKViMEEo7xMulSAsyJyFIFrGs4XCBkAIpzIE9BJFGiBD4ZTRSUAkSRhbGdVG74ZmxAqRGpAaAvpBVwYyWw0V5LNClhiUSaDX2ji6zw4UFA5nSHcYmo1UdBjALTryAIAWWfsrx3UyeQyr5pSy0xsCKe6sfHhhwFo5K7tI4y6zc/W3NU00J9EO7ITgA4MEUDlHR3vvbg4Wo49qGPnxNy6vrotm6vahvaa5OPzepnWp1eI9EhuxwfGLZFIxMxkbSmr2yDJLii66HFpzf3333+4IDKy3cKZnY1jcZ1eWqq//ZG1t9/tj0eNahngGDBcKBkFnz0cRkCKeYOYg4P4dKUgJAQ/J7HvTlFqvDKRI5g1To7BOOyI2SEnMzKl2I76qWhr0DJ+69oXEoBuvP6CFLLAJKcQQCQfJQ8u4jdMa0aeKFMk1JpIdCDmieDmEPWIMlcrszCvyOxCJQSUMG8Xpfzp44CGVOJ6EbzDNSWVZFVN2IQqLdskJC2TbcJAE6GJ5uUKgDo9m5xpYGEuKhAzyvDXCJkyhKGeYZXTqMadpseB3cIEZhCcHFqc16o5KIfFKDb71NZirbDJtmSr31qzma9o3HHXXdsZZb3sJ3EgGFCrJ7aVd1S2rSvt2rgRY2j/i4tdHWQcPXV8blNV3dhYXdXQXrnZqymUsYtNWnGx3dgKDxcEHxkA7X3tmfysnqKL0gTSTDq9oUdRQ7dipjiTYm53cGnHnvv2PHh/MB4zeq3DCMMNFEIWL8w1EgMV1p6wwTH8Dx88wYnTydmTg9FWCkUQ+XDUEfZojI64Rq2xG2djsdn7r7O0Imqy3br2zTdIi7seBUQXpCanrMqGdgRIeAAIKyuLDvPGF4vYfLaimM/g8fAQw2Ll5hYUZZFYFMMrlUyvRMIDqeAMFUWcAiloiRTmEjpcdmUltxSANm+owhAj0LVa8GFk8uhicUFRDsYfzp1y4CNzAEhIIgWh1kxRXp0dkxU3nNV+zmNHcD60cBxn3iEPUgTqIKYZEju91avgC7W24ICtsUfCKKyubzzFEJdtePnlplNNp7aa2Ah3uIXy0i5iDToWX9w/WU5c3IGF4weHqrY8v6F57yY2Q21CCghdC2UeD05pGm22QMBqkuYffGyBkd3DPTeZy3q2Jr+AxWat1qHhOjgnLe7l4I49tzz44N2W2VkSxOFU1mPU56F9U5AEB9ZaNQzPpkpo5y9KISeyvXh/K+BYfdC+zkypOHZ72BGG2OyaeGw2uqbouvaB9kD9rQ8SQNDPBRdcDwmR9xmAiooACB1Okkv8Lt5LpNYmPhTEFyEo5WXCAUuxymbnrErG14AProgQ/jpiQKVAQwTPn5zwuykpFZXZJMqWSFq2bKjL0zIZQjDHlGFmAnc+S6mEHYRwuTnpOTkZsHHCPJFYpGWbrT65nELKiCOFYdo5WIG+wiEeaXMLnhP2mddkcoXCrNd7dWwTs8k2PlDP7lmUPrvV4hqkXjl6gP7sdtSOkZfrntqyhS2U7O84t6PimtIX1492w3G//s3C0U1tVb2923o3rRsSFzKGeFVDOplWprai/59qtwXmKba0xrdroi5dwL0oWSClS3HYXNXTY4ZtdkVdFoutfsfhtWvWrnHHozQaAQKr5lX4ZGarURWCygwh+AAnFiDg+aucHNrJWVrUqLFrKEQgGsqqcTrBZphmRxu3k3C4ODfjOhFD8VEm683sN1KvT7ngoosuSLngAvI+p2Vg9JAkQSBZzaPX1OA21pL7XYgmxxRjIcqnw2mRgzx4t1QiHpAlX5lKEBEZ4vO/BxK5EmqJuJQruHNzS23L5g0bNgjJ/CnGTkUiuXyksAJkShJJQ4+0ICM9PQveJBeuXshAJot8u5ii0QwOLHqcc458dfT4D8eP4/9KOAJAHuucT1Ys18PI6eFdtg6Mj299smdRyeIFBmdVKtV+6Tt3jWy/7bb7Rc/uqxIJt+f1VsLuX3PPnbsnuxeRJDx35OgzQ3W9vb2b+qr66kTsdXV7h8xyJt/rtdjaTwUtNpuOmct7bddEVWWP4KKUItazUkF6dlXLnWpEAZH5+XaL7a4H1t7+wOE17tk4TbVyJoITOo/PJ1MPG8MqlZFjDKtpSWH1ypFwIpnHfBocjEehNrvGbofB0anDTo3HoErCeYfViDFl1Atva+BKTSZxwZtFWY8no7+RuiiZAMougnuDhStg5dPpufQyYqjE2nwkZPDCLBaPly8oKoJ1w6hC9oCvuICwSU1NTwMkFC4A9KeEVoJTvClpaVncnpY7cbS5uayqrkzIVpgx3c0IKUV0aQ/yCG5BDUsAESmz09KULBw/sUR8Nk4e+FoGU085EDqS/g0F/XT0h7m5XbvQ5zwnjPY5nR76QWoiy8vTFgcGsAbdWbooybTPxpJUu75Nl951221nt99Ff3bDlkzhSBkMmTRrlaDn3squUZiEfY65deuG2t56/uDR59rKpLBze/v6+rCrynGg0AjLYZPlFaDFHW2ulEhSUrPfKWupE9w7dk/z3K7QyU6/Zb4zGtx++PYHbl/TrjGQjDoxZXAGp4N9dqjwFNtqGKs21JWYQB68JOHlotGow2n0UpCQBylCiGM3qoZpRqjJDtDDOgVbpBRgKxK8+Wb6jSnXJ6PBodCroCByBISIAOc+qHx6UQEP8RhPCltFr5FKkcgJYLTAB+aa8El0RsIHn8GnIy9KBetzUSCUsG9kBUpPr8wQtGxuvnMzTFFdHWgz2dCPFmadhc0HilXiDConJTmHCIg4OQlPaGLmmUwinkjE1qs1BvAJ/0HF2QA1XYdxHBUXAlIoDW8vjEPGxsY2bhs4JosGIzcIcCAMYsZ4OxAyBGYcyou8BCYvnYQdWHhAxnEYJ6AEgRXxIodx9ELEcXFXZqVZXnqZ2Xvf5z/r6qdByvjf/H/+z/N7nu/z/YEIun3v9tLsFNS4pQ8+0KePYIfSQQzWidAJqKGZGXsOdPfG8fTvonQbqN4XIm23nTpRfnnVauW9dIjHsQp5GDJKq+Ju3UqY2S3S6842dD5v/Xh9say5Ii0FBiCMt7hR+cZ5e1tOjt1iNAQGCIcHZhuT4+JYO/zYx/PSTHEn06oWpg6vwEQxP/7ypUyb/VOUiopgPQ29HeMqCo3TkUnohGiEmlQOOvgCPmLuQH+3ch6AIsmSRTtrrAYVHPShDI9IDQZcqLgTFSJOgL8/X46N5MPt+/fu34/8RgvpaQe6GqS3ujpSW9gQebz3hKdi6wlVsQNwK0NT2XJqffx8UXQhZrCABXUcSi+vHY6qDgEFPA9ynDuJPAC03eTnn3Y8Pj7eKktJSZFKuPAzYFeLD4BexAw3aFro54erbfdmlPSQcCQ5LiYQyLAydWxwcCRULAC6e7fh9sKVKxghREaG6UfCUKcmhumiZfEyWY4ZOUld1d29xkui7vRmwj7v1EP2l0+tvOMTVyWs7+JRgXOo/qUXDk2siiHtitI1zWfPdk5OLi2sD3eWWm9MNqSUWuObCwwx84Nmy2CbQSeTs3VnAEgsZu3urmqsWjO9+MmBvLGR9JWS+ZoaWBQqv/wixmIelGG8ozlc7ph2Y14FtQ2iAUYO1OFsZpbjU3r56Z3nT4xnZJSMt45nwIRSeWl8vDKzpjCj1aMypjBfczgxLFYdKA31w4NKCjbS1P79jhTnAkIQfXyovkaBTTsOmxMaxA9XqRA7zFQOnZG/l8lk8kIxjAyHtQPLyw9cvLYD1L+A3FzciBKQ085GEp9PCF+CO47KTSTicLQkSHCk/CCEize1Ur408MCGtt23Ds8NfD9sHtRuiVZ7NeoqF4A0YYexB43cvTM63Xdz5Mriwmxi0uGkkWxUqhBYDByhlGevnG/LL8vr7U0DoJ1nztxMSOg+zil4d/Nmvbupu0pkq+expampL3R0tAf2DAkm9vH0GmS1seGGhYX1hYWzpQCEBNyYojYOFtltHTZY9Tgh/OGBYf/kiiqWKQ7mhv4EU9XrJ8emPFbembfYMkuKir+w11jsNl68SKSEkgMyDnEXmw2V1Dt36gmQY9GX0Lpu9gAdsIGhC3gKi0swnKssrKxshbgzn58Uhl5ZJuX7+maFEB8Waz8LEYQ76ebmiCHfPcQnVYrdG1MZTrwwnK3Kgu5WlxbnHycO8k5GwiIzIyKGYobCxweoGUB+WCju0BExfFzc6cq4JhB4BwUQIMySUHBBmEDbq4L0FhSCyIH+508NECptTHYBHnEUKhdnqQCIdqpoBbR5SIxOI+fO3bkLO9bs8NnZyESP8oEkCJRJYfkKKZvDNdeYbRGdeb0XRuMPb04/c+Y2hqZVHA1u1ciR6ltB3DZeSLI/H4kbHqYh0ZD1rCE7dmx4eHihYWFyvaFvfX3y4xsVKfHHmyMwoJm322x2uzJQfnx4aqmvuiltn6nx+Oj0Wu2Fi59cHJ5a+WC+raUFoztMS2qKcmxIsRx1hDoiP/v0zvT0zeVJDq8lPsBhSsQYaDQJ0geXo09tPQUkcAhBIUcQwaVyqjUzA2FlUOBpi+Lg4czyD/H2w01FicBiMSkOgGh5eoeEyuFfhPQPQNB2pHw+ditxHCyH3r5xYgKUDA7wiqIHQlKjm+9LfyRcoIQwcHFzAyDEkTst6pbQAKPs4ABPNPSBQGQvqDtImmJvij2WF7gAOwWajxeosjA5DKE3wmarDVfVRmVs0i6UcU4D565NX2xAoT1clpSkOZ00UI4efBesBLJUIQdTWdvYjWvdvXnWwxRB92qP1X7SPLV588CV3QmmtBQFL4i122sPWyKh3niuuUyRlLS4CNqL6wvrk5OTZYgg7ELWMl1EgdKca7GZDQaJfHXhzNJJU1MTK/nA2vJy/4Xqc9f6hkdWshFjOZXjhUU1MSWVthp1IE8mCORLZYbs7EjIhhDeCBEmRHpICg5C2I9oUpevP9U6DltK5TgipritLYYcDSXFEBcqK+fVCk2EgCOEPiDHHdmObcRzPwsLhFzckOKQ4/yogkNykyLFsZk6QcVH45MW5+2Pp9w/ztcRQb7MEw8+JPfg1vr6gRa+QHzcQXuDG34jghwaH4VGaDgCIkqNzjIa279EGCDPqhPjXfgBLXEhnRaB6Lmd5Ax/MZ+tUqn4QqmarJ8Fpx3zoHsD3/fWfr80srS4OLALhd1A0s5IpH5NRDSHI7MU2XLUpdO91aPxeuxBZxow1Z4uHTiDmu8IMlOzMbX7CBSnA/FiVlBnSoU1JSpyYLFheAmQFhYa1offnpxcv3Gjr/QsLBwKc4vZblEoU/c0LpxZfD05rmnf7uS0/v7p2t5z0xcJ0LxNFlhUU4ipa0lmzbxaW5ECQCp2dKzAAGk6nYyK2HEgLfzDh1QEPWTu7F3naWxeCftoSSZa65riwnEEU0lMyby5pcuiMMrQabJ5ob6IAD9SOUkpfcAHOQmNUBASnEoFNGAjFYanquTQrXErX0R1ECf2ZwQ4X7L9OIQ43FWQgfCDhfihqpsAgZCLo0NC/OD1YrkKhkcuOhujUUkPiUoO4Mz3PpCMgBmEPb28IDT5hwAQLeHnihgDM7ZDCDkN3Pv+5MUbi7dvolPVY8Q/cHhXoiJMnx2s5PG6bEU5Lcae6QvVoxOJO1EkfA/ZYLRvagA+7lrMUUsbjvdj2pAcxJZj+Gtqyqsw6pOWlmZnh4cn19dvNDQM3ygDqNKzVF+qLbk2OO2UPDkn9sxw04tNaabk7qPdy8sJy9NrF8cIkCVQW9RWghxXbC4pitZWcCPyBVqpIJ/LVcAcm7iLiECFo/UAULoHOp5dkHoySkoyxi+BTyYJfoOFrRmF9raajMoYW1euwQjJM4Cv8sZ9odTDouW8gRZTJ0BKACA+ggexg6ccn0L9Q1BYUYsPDlRh+SV7UQAhLTGjWELjSR+cnR59dKOTuzvAPKgRAAh8aAgYUsdXCclwL8KKigrEUxJet8cb3S5DiAlFMq04FB8xrHjygACpJCCwpBCGTxRG9Dg6jdz+8ceL63fv3b45MPV1+unNA+UeYeb58XJ9PlfSntOW295TOnqhun/1Aw+PqTMXIYxWWcFn6so+046glMkDR2eO1ZrC5+QXaLTa288dPxWmwIJRf7iseay5tKxsrAymP6PdnmPpQI6zW9r5vIWBsuTktLR9ycvLy9393b2fdK8tTK1kG5SCOUNBURE5HAvnDeohiUCnjBjPqDTn2KMk4dKCRPLTYSyx858EVw53I5QeEhjC8rNjHSpC0fygvbB1PMZmVGgMSrUIdhw8vHxI0XjYUbPhiUdGcqOOn555APLxl5ONEV1qOHYJ5CU+BkPIRLiNXiZ3kw9j3klG6DCIwAy5ianodrhsfOSRjY86ObsAD/jgAwHagcyFO14nR0xiJ0UZx5Wg3IYhNygOBRxG5SjCAZgyJWDjAcCl38Ic7LVnnz548GBxYUYkySZoIjz0KLMB6M7d2zdvXlm8nD2fPoCTHsgPkafDBIGBEK9yZUOjvdXLAARzycUjCQnTfchwA0tHElhi62SjePfMkX1Zq2vVADRz5ILgcHkhzBAi7MnNnc1l1macqojCeQC73Zabk5uTk9vR8XiodXGgrDsZEWTq7r2wDPyv9I8uDHhkK9Q9XI2mqA2DV/NgkVo0NKRVS7iFp06BmFYiVEmUMTGooE+c38wsKPLoXSMV6XqYeiJ1BpSl2WhnY+FIscWMlxTlKJUaIwhBOuFRh+rvtx0R9A8gF1eydhAiZxRUQaFY5KFHjyLmAxB0A6ZG82Jt3UF3lIV7iufcF3kOmQmxQ3WXm6vTxo0btzpRv8sU2YSH+KDFYeIHjgOJLBC6EeTRRqwDYgpFAkSEQJlJkWCOww/PPf3ca8+++sybb35WeAntzi54z6HT5zvdRAR9zwBaKB2KVWsWwg5DFzbP68PKAmUWsyVXZh3trSVA2INOQhidtq6seKzcBKom62Se97GZ96vjGvvBB/6rbkHSeJHFEo2i6Xjz2c6ylJThsU4tFnzeuS05ltyO3JbwEGH+mbFuE4wmyUdf7F4ere7t619bH5laFATO9RQo7ebiwhKzvU0mTeVp1Rxu5q50e70lisc/IL1aY67UaPRM7CB4PHZqdsYqgqM8kjzKEw0RpMNFQgnGgRlbTUmhuaNFoMRIh8tVR/GEwro9vqhkPSkBsf4HyMXdEzsAhIQ66D1IdvIgKoQBE/eOBNFH9nl57du3G4CIkDcBIjmbVJ4NTsCzcaOTJ6oEsEEUOdPf0/yCtnyaW3A4zH+oDw7I09LEEEbppAoW+VXoMjTu8MW04a1Xn3rq6aefeuaZN68WXgoLLifbEgb8Aqd7dx18RkYWrNZ3FMqUlChDTY3ZkBhcZpVwc7s4KX04V9Lf+AEVCZgxvD89Bz4rSxjTpVk/VomPzLyf0H9t+dgM+a+6ReOZbRiSq7myiuazzWMNKWMNzRggGlFhY7ZkAyDL40GcYI+xo/vS8kymJhQJoxeWL66trU+dmRXIUuMNhpq2GkimdjMvlS/VKkSi+fJ0c30Ot1QoPiArslUW6fSoDNIj9dSm6pMOa3ZFREaeTgw2oLGLDSZANnNb23xJjKW9RaDmRgkwOxBhqMEP8cWzzXJm5B3SSl23uLoyuzp2DHBBBAWJs2gwtCdITCe3SGijTPiYC4s5WcJCuUaAfJi+lBZrCwXQo49sBBZ3qt6ccTFczc8b8jgc98J4jkzGoQoXW2AdHoA4pMd9DiWcyDCXQc71RaW4J+u915576pmnnn71qc8UxYoC2DU9YKOJ0Drdu0OA7t28ObJe2jlsiE6JXx1q72hRxGJzl3XkSngA1Fs7etwBCGPTa32od1dGjhypreprnuMfYwD1zswcgUWuWwDfe24XFwPd49aUlLHhsmGFWingKmtsNosdp+laOjrCg2SaMw0+7gQoL6+//5Pl/mvd3TduXhnmSifmFLoYVGE1RUV2djh/tcvccail0kPD46ZUHGhqEhq0mmgF1W36RADyOHE+13KitfVdsqjGZFaWxIbpwyoL7fa2opjKmJx2LkfAC5RQPyeE1a3OGzeGUZ4J0IOmBSUCC0nGxz+UHy6HI5vGm3579gACANHE1W3Td6SoQv50xxyIcpcPNrIHKtEmhA/lOLyCKQ7cmYuhqsDsPBTORylPghEPTwqNHGZHqgYhSbDAhNAwr6dP2AJ993h/9dWHH8HOBEvTwaufSa4WaBJhyEy8GuV0G4B+pBrh5nrZwphC3Zky1DN0SCKpsD5fIZS0vyS8+MooymxHBH1Sm1B77eLAZgBCHs3rufzHz7sZQBd++IFMwEhxhZb6x6U8DreCAI01ECCtRDmP85Q59jYYHHLDxfHBHg3Ju/PyTMlN/QDU37/Wvbw+NbIg4PQA0HxbUQ1+5QRgdHaopf2JQ/O79Eqt0Hqy6WijQhIrMiZtLteHZSdBMy2sPDF+/uUTGMghKduLCzWJBMhsw1Qjs6ilXcsVkJFAKNOys+AIoXbdnQprZwTQg6bF+V+pJyuLGQkhzflgH3dECQu1s9N3xBUKtSN5AdAOd4eGt8GV6DyKFOeGDMeU1yzyCjGAUBWiWFdB24MYlhpAZkdvpEdYtR7MkggOyQ74Fj/HoPYNEHrtPQD67KBUi4kCqfJaJUXQjz/euXd75OZww9LiwnBnJxKSaIhT8fzbVp6so77i4sk1RFDelZ0eZwamE2qrrz2PMnvXiOlIQtX1+z/96kmApqsBCDkOKW6wg81HNw4HfXNpQ4NWbYyK5giNyHu5Lfa2ltwcWz2bG1xe5rc7rcpkwrQx7traWj8AjcyOdcbHT+g0hUVFNlR7uTAkrR7qeunxl2pa58PrKp5HBFUYAwxcQXZ6sEapy45M1wyeL8nILDKfaC2pjGlrK8EZClTbmYP2wZjiGApkDoeNsjmAw1XRwUYvL09PZ9TBTPPjaFroPhMgJCVylfr77wnCNuTvC0BMMkSG2/rdbsY6yuhrJMt4sVxwEWQ1ty3EZ9sjj26EwOOQ4JjuBjYDMZkf6WQlHKoQD5A+QxgnKi6LSzkTIwLktoGq/P3MBP0Nr+0ffvTRe++9+ky97KDsanQUhnIKNbfA6S7wABBF0LnFpcXZhcvKy0VKQfPHb5+zCl+qH0o59woATQMQvFYAVHttDoAGRky1tVXXf/r9jb27a01r0wkAhInqsiDb3q4CIDmkweYUtTJeFC2D20KktplxoBJSTotFFMUVRXT6surSEIMH4uJeme5HrT0MQGet8XM6Xdt8jdmMlwvloan17QGp9Znn57N8KyZPHj1aIZBHx3PysyMiAqN0weXBlZm2jMGcrhh7e3tXjj0zA+aD8coS1OgQFMy5HVoJJ5y0L7QWWf5QnhEQjvMHbg5ZhgA5VBkvguLjjyIaYwDqdWj7xhcZQIy3dzd2dBJA8SVcBN/JAifsP9seRp1NV3Ij1A7pxw+NLxIc+KQSG5h2kDsdRlTKlkw1gTdBgJjSfC9w7fV8440PidDBN+ufOSjTajky7VUcozWgSKAcxwDqhDsbE9HL+fOX1Z3nEEHsl9pFIgC6gD1IT43qtYT3a9d6EEpoVGvfl3/z55+eT7qbPPtHHYBm1gTZChkE+wAhFxYibnRUvEggZPMkXIESMdSCArwF8yjymAmbGqtM++KCYFueXu7u7Z68Mlt2Nn5VpNPBOIAYsndI+XXh7V2Pv9CeuBIT6ltRdjIurqLseJlQFXY6LMyojUWCg2p3abBwMGaw61CuufhS6/mXW09kAA4QoUE9JJFK5QF8cOaFQsX2RECQs4Ny24MFQI4c58fo/ah2/fd4k6Hekd820JBn02POZAph7TDh7lP34r7BycnVjeBtQfw8tG3bNvyR/oytBRobhRD1veHhcihx8joI1YwCAT6O0gA0mdGEG+VZWm5792KehAkIEXrtqYMH6+slXHiyVOQLVhAgJDnUcSOLDRjZwVNwWWcwCob6+lLiD3XkdvU0o0ionlYRoKlrte9X/3rdA/83Uv1+9cRPv/3255MuJs+4NQA6hkKuvzNbIUFUU4HJFQUKRPEiNY9NBozoNrhPLMhzUVAN4TnCUCKlKk4e1HTylVFEUHfZ1KyyGaFWoKgxW9rs9kGLlC8Pn+thP3EobFe+3K9q8uSLTSfLKoYrVJr0yMgCQ+LpSzhbVIkheXHmpWJSd1pffvlUK/HJrBksLgIgWDPqcE4BTlHYM5Cz6I5scnJlbsq/gFwcgCitobSmkRwNSLFAx9UVgLY5OzOAaHcBIHcXVxhIiTIF0MNYBIgJRtJR8TJIPOipqFYP4ociZfow1MimBeqo2x1ccGnHJwo/Gn4AEAi99vTTQpyfDZTyQ58NCBIHcJ1u372DdReAlhaWZsPCMFHVRVyOHsJB5rn6b69/OwRAF0x5Kv35zQNTn9TWVsu/XYHU8gGOdPf8QoCcbzGAZhJQxS13hkWzSdUKUMkCZbx4NkcrkvCkUpEy2mgUcHNsuS2YyHPgjhjq6WwutfbMwU5RFZd29OjYwKwS/T5HYayB5mCzmXMD5Kmpc0MTE0PBHmEq/6q3D7x49PWzFcPH83RJ+kRNfnYSlFBsOji2l0mJrTjmxIlLGeMlxTXF6GsLCwxRMh7NDOShEFC8ac8nscyVTJ8EiFkujpKB9n4SYMgih+hxpiIA6jTgbHJFJe1MiwAxDRASnBMMpMTukUe2PfTQQw9v3UALsYG+iaps8neLxVlIbfyA0Czqd8EcRlSwocXaTS/fRP4sFPqbXBmBlSLoDSJEtudnX1XRIg+4WOJ08873P96hOhuAFmeDNbM4e5Ifoe5JsQ61f/PtH9/O9cwBUBVb/7LHwMDJ6upbqT0rWF93116Y+4YAud9KWHYAmplZ7tRoU0Mp9fKlnFQca5byMKQMD+cao+HY40JM0ApkbIlSxwmfwEncHoFSWdZ8vLE0TTw2FWbAMHhVaTDbbPgJDZYOuXxidahndbUneFewUFx17kBy3OtlfWONebGHE+GT1ySOFxa2to6jJM/MAJiamIyMSvxGgqNj4wVqmi3DSx1EEpf3diYiNuB+0711dcQO6gXHvUfdRZEDVEDgUFFRh2/CAoxH8Dq8iNHPKMFtATMncpBu2Ljt4Ycee2gbiGM54wUoE4EiRIxqkI6Ih/BRH/huJ2GW5eh6yUnHYmw/W12dHGuL482476cIojrhIzD6yAffG4JiIyCQAH3/448MoMXsr8PCrlzBKTudsadvSHD9m59//vbbnom1W6ZGKQwbA2deAaBrQ8Dz11/etdU///LL738+eaw2oX/t2A/HEo4A0GSwgC0PQAQhr8nDA6QqKXrEoDpONExMpBhqtQIePzDYwxiyOjfBnlCrBXC6QHB4XpcerKh/aWJCF9ZiM9s+tdhyVQETq6XNjUH8d1aCpeIDH7/eHZd349zZqqrFkeAIpQJe3hidPjtbV6BLTIK2qysI1mRCwqYdqLjIoJZI6YhCFky1viFZfkzLSaZccn/SPaEcRowc0owf9n68woVFHQoCC8EFQFsJ0MbHaApHpReFgssG3NOtG7fiNzy+2x567LGHHnZyAKK5A/kTsI/hqCsz1BZnBWX5UGYDYEfvQyfwcNWtALSJrkQ2e1e8D/e9WPsBCOu99z709PSB5AQXGEeEPgh8mE51afaDw4mJU1fAR6fsGeqxXf/2m19//vXn1KDuW6vCyNbyKQbQ9MSX93/56f4tRND1+7/89iTOqK6NHvthw76EYzOjDcGB4XzcGR5G63Drw8yKioC/J4CLFSiiwxdqVR0mfkZfcSp/+5653B7oDJ3GhrMR6dkRuV2CIYMGO9WnaJly2ezV1ZSURvEO0eFglU+Q9fWjRw9833ejqmphBONERTEOt0bgx2XA4wJAGp0SR7tRXNuKimoGB81qrQo/AyGESjI6i4ANCJkEj+sW8k4zEcQA+ic4oM9RTkPgIHLAg8mCmxgn/MbvNjHbBdPRum950Jo++PDIww89vNHxZU+ST0m8w4E9bzosDp8WdVZeTDfKCBjIm/QGQGWrw4EKRgQIDwPo7HVGCNH60JfOxzAuPQknwun2+p07ELPv3b69GPvBByVhI7P5OpxhF/X0WK5f/+UbHAoKD+r2SmWPt5YfHngFJ4evpf7++2+/3L/1/oUh6oNcZwjQkR+2JBCgdQ0HJRxbxRGIoqLjYUHu6OJqVVkY9MNOxPxSSOUSAPILCRWzWHUvTPTMsdnGnp7odJzwslyOVhhsEFUtbUUd6O/YVuvxJtPxd95h+wVVVDGA1hvzhkeCMXyIwU6jg4eUTH4wvxnURoXRXGPJNVvsdrslisPHYJm2Flq+niwXV1e3rcgnm4DHUWHTcgic5NEBQcfehC/jwxYKHrx205aN31FKRNAhibHcED3/WVuRALGnuTGCEUUQI2OHyEMQO1lBuMU0/HF3dvSl++EjQtrcSguACDveDSVcN9cNzkAESHC8wjb+hg8skhCdwuHyKQCg9fU76/cw9V6I/frrzLBIDf71l9U9Q0hx1xFBP38T3o8iIaDy1Knync/37uu9Nnf//i+//4wqbvX6/fs/bwGgfgCaIWfpWsMsl34ohySFmxKolAm0zdFcGUpGSTibkyNqiVa2tChlAdpsD4W3tzzAxGqqqxo6kHz0ePyQOv1U5qDFYNQp7S2YGw3Ot8CdFBBvbRR7NzYsVMTBvxT3YtO5i+uvVzVcwTtUFJZkFsJ7pNCpFbGJ2bGwGEcYMdHoaunqyu0IlAhD8Y+E+uzHjAaQvrAdb0UFhzvCtKhYDkCU7ugOomZjxDmgQDJ0ovwDPAQIxRwlR1cXFirs/xPCZoSXU2flTIC8mHo9BG63ULSn8IHQgJzFmLQIEzIcZTWEHoKIvpHxaREoVxdXNyyE0V4k2r3bsYUhQbJ5HLXub7LOBSjyOQ7geTvGa7A3alO43dP7VKJs09JDoc6jumK9TmcVEkvLbTa620dXXfaKLutMTpvI49Im18ojYnAYifMIY9AVd8MMN5z35/v773aD79U+urtq/p/9Pn7f10btmJ11z7t/nP159jXG8L/9lv7fPHt/TmLi1AKAUKC51E+2llh1z99//4aj/X3lfd6FDzf8vSu5uWXPzrm9u6qWMH4yONry6loBNOp+zeYts9n7QxaLzW4rzO9322zLq8ymrNVFa0jzcBbKjKlK3/jMbV3GHEvFPaThBj6hruppev2OD59/Y9OmVTduWoUHqrn93ho9TaeWSk/yyR77sK+02hco7Ryc8Iec1cNvvWkvTn/jgltuyRuT03baDZeenZuZX/hUccz4tkSpvLC8KlbCJ9UXxTUS+3UElj+K6EmiOK2SKnykKsSJkct4MICWgEi9sgWDvMwhdOL3SxBRJ3zFEcKN9AGi+OCJNNzYPxWmn0qidZkxa+npVLYlZlAJV+wpcCREkCkiIcw3DwPih4rm4vNQpYMZujjygCtJDUp9NdmUpI8riJqd3z2/2y09OG+99222TOKvyOtvtxiSWgUQLmhqXVtfs7Xs+Q0fPni0r6K8b+fCt9/+/e3Orc17fpmbW0hdIkHC5MirK1uUBr3ZW2bpDdlDE5W2dkvc8pC7t7d/p45UR88WrdpQnFCVd/8z91977bn2ildZ9xOcbOlEP4qvOOS8N8ggXHvLNVSOsHHXRDNWW1bZVN3nGXvT72hz+SnCBqsHqquH6W/JTLvlgrQCt72Y4SOGZViksrzjkpqpKsJyXSInKILrpZxJZApVvDMk1FwVGoTVx8nAR9MfsEgaTdMgRMxZJAeKSVSA+D/iLS6SITqEv1oEFLUEPAhagsIiJMTj6dOSErnUb9U3VnGaShhp3xpI+wFpfMT3oUcXX3nwkQeTM0fIRtBJmVkXtXt+YPfueff8/I6XH7zuuudffP2G3Nc5RCYZzA1CaGpqypncWeGbefdlRqeaKlb2zS38+uvf325r5tGuPxecACLMbrkHQJTD31xBvaJ/OhSattnaK+NipkPTvfaYBJ05q2tzB9Zny+bMlKpbWJa08YEH3RWvNn8w6h9tLm1zetKpvpFLrZHOHnRt8zWXxCWgC5VeALmH/Y7qIIAGgyCqHSZrVxhXwOKXYbuMraddSFQXk5KzqibaGF2VvNpgoLOWfRQnSOU5Uj6VoEnMG6qgQgStK04BUpsNNLPGPyICBgAJNnExfO3Y7xUVvnjKUci/VEjOQ2gjgCkdiDU9Fdch+SLiOOlDJSHOD9JcnhoEXwSEicOioq+ahRNAPKOfn4AunHBl05wx46viqOBAcGBoiOv45lvnZr/4RvEjTH9xbemjWr+wsDA3HvQRKpYEXLKp7MOde5pLF+b+2Pfrr7/tWbtnbm7fgvNV4mvOQaJB1IrevM5OX9yEbXp6wkbPb9MA37vXll+WlKpLqCmM2dy9JS7auGnDBooEV9ibX/3gk8HgJy211d6m19+7tGBVzpqaW27fjBPpWLPq9vT0dBoZDMmdnrFhr6PWYi2tcLjagm3W4R32SntKQXFdnrs/LpOhzoIL09IZwl7TXZW8UxebbDLSDkr8hM/GumFcxL4JHzl0KA0iMYNg7bl0gkwLqLhsXPworv6x5G+OVYwwSIfhM5ScpJ4rPvsB4d+lSIuislxO2knwQyfjQZjEo+kHdkq0grjwCSsnhPBs8qEVPfhlEDiqlhMVC57J8rLWNR1RVp+fFS+h6f5hxm9vuJ3obaospTKliVJtd/fC1E7/eOqZ35zqnLpbHNQve5oH5/bu3Uf0tmflHoaK/1SA8EFoEFuvZnassFFncNH7Ox3q7W1vcvknBgaaGIcyZiRS+E1Yn5igrwIQM/FnuZuXDI4OkqeYrPV6i9/LTotL2VyzCUCbUaGajrjiMdYeZCZ4LGPDTY7kSuvJFQ7fYMBhnR2etrnb3W4ZhmWVChOfBZmZOSmJ601L1+0k+ZWaJSEUdYHFdiggRNSDT64JgFQNAdURQKJcohnoh2gPgOBzrKIQEXkUeaoAKYDo2AFShDiZ+O381agNHaJcXAke4+kOOQ406qrLIQzR+HDDJ2gBpLI9WrgCQqXbtOVztv3mzO2Nja0N11wTVV3LliRf77Stjsbf7E1rah6ZGreUNXGZY6LXd3fjZn7b/s0J5l379iK/7Wn+ZI77v//+ra9569Sfv+6yakECa3pQoObgj681OT0eVtNMTIfae3tdQX/Q5/MwvZwgC9cSuxpaaURNExN3/1ljzUtGPvggOLm1dNBqLXwvO315WeLmVVJ1XYOZiyZfV0DAzwRybkFlbW2/9+RTSwkWOmtnh4dC7lDvmD1kb4+Ro1VaHjM30Yndpvh1nmXxyVlSNqB3ABcg5xqMmPId6ogqFo6ASTV24CG0l3WYHJdNKB0bBhRWE3XLXQTKvx6jQgfIK54rSmMDnR/rVp8vlu4EnuOQBNDBKuUHHwGEliol5FN+F3Ukk9w4inwEgqnTmurozH+fEYKGLZfcHlXaFnBZfUMWG9NszMHdsql4+bYUPd1C+favEhIbFvbu/aXIdPrcvr/++uv33wXQrn2///HrrtUcVMfxQZ5XZdHI6IhsSQDQdyu8yWyBDPhs0/b+MbfLP+MP+Jyeyri6uBj6jwBUlVpVsIHlZBtPG6tYsvWDvuDoSF+p09n/8tlx0RmG6JpLasCzqiZHb8jwxqWjHnnsrnL3to95l8WX+kur+9pmh23TIZdv2h7qr4yWzQ+ZBYwXJurXx57gcPaVLFu9FCE1TdZGdXKIfVPegysg06NKgw4lwjtOACn14cATAXQiugMg+ABC3ShU2p2GTdMu+dcSE4ZrqZJpkJQRQ3iLExBkjYSN8nDACAPStJknB3JPFKip0UXCSoINOe0SDlJeveq5z778MmqwLTDkmhnwtefeRRr/bAAVMgLmTbL0s/UtvbBj1Z8LW7Y1jZN2A1EE0N5dS/esHQku/LnLqXYpTTZzi4kLvnuDsXRZaiDgQ4MANAEgnytgqLRfl8mBKKZhfWvGuthb7n8AQHeNVfzQsrUvODnS5/B4+p/NzU+q0iV2pGPeyMXl0JLgjI5jVUF+Wm7BGGKzJHmGaj0Oh314Ymja5aMpsrcyRQjFxSXE0EbUlXxCX+qp55xA5YDzD9dfACCiHEiUCqC1XLaqY54goTURtdKsMEM8EHj4I4AWcSAaIR5EKAFIrCV8tGIdrbuIJNwgFskiiYGDTXjEmB8gFo57HquniKJDrCKiAAGHGCH+/cbLn3sOQlGTozPTA1aL11Zwx4PZZ6fFdTzySFyM3uAta3fXDXOg+al3eUdcv6177g/kNxpBF/b9sffXX1fvuWfEz1HJsxIZnWx+dW1LCz7ox4LY00udMmEHIXtIAAUHXBZb/zDdLVzF9esTqrI23Y+J23CZu+KHe0bYmDVS6gjMtL/r7vd6MgwxHfSVUB6P0RtjnfqYJOrecXnpxOp2pJf51+p1IbvLNYEMhXrLEhnBSMyPiyYxaihaXdJXfcKhFSeLqByyWBZJkB6lAZLrqfhI1KUSn5ARQCpvACDNwoEnQmI/II3OIqwzTjxJYmytlVR0SF1ZAEnUJok8SVFEhoZUChD2EVmMtOVnK0CI3AsgtbaB6u4rlL6fuxlAo6MzQ0M+ZnjqmKHOLu6oeYROpUSdjoHXOneiKzjhSoxOmJ4Yn9v76969c8QMANq3d1fq1rUj/sKn1kzVOkpKJgEEoZVv+99Nc7K+bmJaLp/dHXL5iUBcjC/l5NMhxsrB1u58fWrxxg0bN9x/2VjngSv7ANTcNmidqdzhbvc4Y6sSCjevuZ0Wx5gkZ6rTwGGVAdmxXJuTST3ZWuzur2TAH+IsPaTp25MhExs5aRQqjGbz6SWdySXnVGhD2HJ4Bw4IxHohPAp75YMRld0+SF7DuOuLJOGiwgTFR5k47QGyH5AEd4rOGYrPKVEXHSGRl8jBF0tMjy/C26vgjE9tootfQYtA/i1azIg6CRu8ZDinLUXbb+LPX72dDrnHPvtszaqnokYnfUPzQ72VcdcxhJidmd/RURhHjcCQGP3VsN3S5J+wiDcIVP2y78+9u+a2DTQtzO2b27drG91Slo5Nj9i8jvLyag6qS9YSZ0/uGPOkBlyh0IQokH3CH0SF/ANlZTEx+owkTvhFZn1Gajp4GLdy9x3VsnXr5Gizw2GdGXht2FLrSa1ano4HWrOF4dDaaqeOdM+60jKiuHiPxeutpNWO1bjusXYetFdWQtQYyyhPSlpONK0HAqi0/ICSCuDgBoQAdJAwIKUoakKEiyrluEjowP1FQFIB9CKgf4sGTAGCD5BOOgU+yq9r1borUUlRWhU0yhXHr8kDIfVfPvJE6KifLoCUG4q0R7CVhvjgqptZD/wlPYJRfv/X/t1Dvb0FsvohOzc9pyMzR/qF2EvSnpAorSMpCVsY8dF3L/zy228NZUnr5xZ+mVvYZrF6l2+jbc64tKSk2tkpG2FoyXqtILW02hWaxksw3DA0AaCg39UUzUwufZZcxFg6kIqf37jxhavPG3P8sLK5WUxcX3VgYMdYk9Nr1BWmdbBqKSU/B0C1hjKdMxVAY4HOWhcnVtZBey3t5EeLMwuZ54h2eg0ZVRn6HI5BiejQsvJmR/k5rEhW872SnhY+ketDkMAzzu9aS4LGR2N01Clye4pqcguT+A+fRQGQ4oP+HKT4IOgP9TZ+qDQyqKZvUZuIRf0/IETzP5F/dKQ6CqmuITII2+sbL795/WfXXJJ27wVnr4gaCH799cBArztX5gwZYC8sTsvEpCcyE8Zm6RSXz5C4ZUtMpWuirKyo65fuop6rirp6zEWmR2JiarYUrddnUNb11HaWr2xuEUDp5y8lLMRJhNzzGLoBYgSf12vB2xszmKBuTM0wruu4jX7rF84d6/thbXPLzOgI6zisQ7Mhn9ejiy4uIM7eEp2To8fENVk8TmeyZWw2UFLqaisvdTYll1Y35a3ILUhj400Mk4X0PcfkF+em5bME2xh/TvlI+aElqjkeEyZ8xKrtByRGTlwD6NSpRPgoK3fUSScdeJIKqoEBjmPCiLQbJZS2FSBNf046KSpi4LBM2kiyyieoTiAhJ0q0SAjt/BciFSHwVQ0yhNT/pqBLQ3e9ifDtsc8y0wryzj7vssuiftq9e2j3UPs8gN67LpetnwyBdycm7Yx5pGtcb9B7M3RdXa2thqYJl2ucEiutjw/19DQ2mopXNdRce82aBmnLa/LUtlWMjLSsnNxhP3NZLftwA65pNy6ITcEzPq/VSwdhq7GKQl5jltEkGgSg88Y6f7inpWVSAPUpQC6PLoHhsuWsUcMkOmutXtrNnad77W5real/sLyz2utYGu/MrCOZnVZMB3h/L2s+c6gxpWWmJKFBh5dXHEqPLsv5BZA20hsBpIIuRM6CYfN2yil8qnuhA4iwowGGwqHBiqhP+GsKT9jAHQkbEQBJkU8BksIFcJQX0uJGZWUXCWkRnBg40W31C6qhInE/8DGaey6//ObPrv8qvYChN6qkUd9JLYghnguP/vC9bHaPFMTFJEyNF+2c6p4aH8cXtRa1dhUVZWQ0Bbw6NOjmm656qL4+K6txzZZvtm+6vZs54q6ylByLx2OdbHO4dqQ518lCaSEUmp/w+QK+gNdjSMxM6WJLni6jp5Emvtufv5pZxUsVoGYANXcOWod22InHy+LS09KWs9JEAXJ6miBU6gyhQWR5SipKnY5TT04tpp5KEqimozDfztwnDQ5MD6QQ9S1bcmjF2gPKS1grgeCB0Bduwqk0qY/KxVDHRnXJBBAJNvgciN0CgVIbSMhDTXgGoEVC8AEPWR/4oEDie6SMc9zFxNNa0loAHb6/J0RhgpEiJFG23Crh9aLYaHVC2acBnuTU1vXdq7685pZb61iRwqqUB5gPkm1kr+WlZ7N/OpusYyaTrmWGpPHxqalxxFCkZ+ucjqmMWqOpsbEI92XeXk+U0fPQ+680PGc2F5l7zA3ri7qu2dTR1ZB5aYGzuskVmLROBvzT8xOumRm/dSZgtcTFRBv0reYGU6zZFHuJDPyueGtsK0Wk5snBlSsrqmund7hdAVe7224vzqepJCVF52S1Ozvuvcsmd89PlpDlae501DocJyf3DxcyEZWWNxaXX7xcrWTVE2Wz0IDVJWgQndS8fYKcEHH85IhVFKBKMLLFTSnQEklTq/wngjqI/gAIAopDhMcimYhKKUDg0RRIFXDQISFEL/7hYUCECcqraKKF0HyASKiIqNAF6ybeEM3hjwRv8avrixrY7ljHyjtWeDEaoN4egMXmLPm6bEVu9iFXsFsJ2w4gcmdl49u2bZsa36lj8tmEJ45Nlfbm5Nges7mxvqe+/qoejB0fPVf1NPYUXdX40E3XP/3Fp58/X9fknZjwzUxOBoLz8xyDEBI/tAilGLq6GhJidWZjfd79Gzece927sx/88OrbIwBq6WyrDb3pHggMMEzkZjQiBUIZ1RASG4d/2j1Z3umvZjNRdV9fxem9Y+xdo8gw1p4Pd2JMQk5qR2eeeeoBh2Le1BJ4TA6AuIjSx6MCbVm2x7mdy0ogjHmBDwIirjeARHMiEtYWPv4jWqpHxQewQY4//sknCem5Y6xSyx5wEsLcqUEhbkATUSU0WUTQqNhSLKSEBtrSOuZP6nu6Ur7Kyz5P+kIfXhxSi5KF1Cz9XXHdIXdk5+Zi2+OwF9H05EcrQrw8ddLDGiu7iskSF5mL6k09jdtbzT09D1111UNm/JH4JAYnnrv+8XdeWJHZTs8jUKz+6aDLJ3ysAV+TxZZA/aLmksz8ItP2pzawCfW972b7NECszeprC705HbAO2N12N3uKU/gFjMm1Ho8X77bMOx2aLO9zVXdWdLbRn1lqH5a1XvYxd29CTpmB144xKdqYtW7p0uPP0d59RIuLjpTqqfLQ6hQPIFhxVbRuA5BpgKCDSGCA0EQVxqIAIeqxllwAj+ZF0J7D6TJE4uO/EZGGuSfVTAm0OH/BRomKFrWQEVFqI4CUiDorQGoZBpm87SZz1+b0C3mfA/bmaXAEUhTLB1AltqiwUzKP8Cg9brlGKHpq2/LljyRgQthvx2wrgIBk7mmtN5nq6+uB8tBVl1/e0CqI6h+F0M2P3fflBSvqxvoBNONjrhKnMoNMWl2+SltCkrnhmgtuSDDXb5d60Ll3PDG79UBmi74eXLkWQEOz074ZF4dRdww/n1KQ6XRUiD0m1lLfdMha4pjhbRAA1FyyzD6cwLIHTkQ2fYyO1sTkWINeGgTjw+GbLEFUjNTLFEfMjWjQEsyMGoLjHgMXzk7DRwH6l/YgggsJfxE6YUASflFao1VXAXo/XgAd/43kS49Tc8GiQvxREuYDHQRFRgQTIvkLcT4AEmEr2faeou5VdecxQoi8JHT4BNCDd911h+xcefAs1hrTIpNeCKEE9tltk4WDj4BKRAbUjUZ5/w5jT2OWCSVio059Y0N3a0MDl9z00EM3Mbt3/Z033nZ2nbt9wO93uYbsAT8uyO+3BiYsvF9IYlFDTWZKhjlr9VMbN959Yfazs1slivv6k5a1Wx1tA2PTZB1s7b39Kbh8uh1jaQuuxcRZ+zy2Xmu5I1DdV1FBbbfCYXer90+wWZoM9N4lrzszS5e0+sylbCvQep8BFLYqFynrxlPuMG/MbGnOWyxcJCvNuVPxAQY4JGZTZLhZFACJaHzkGkt8QJMUiJT6CKAnSWnTUwogzqqY10iVQYk6iqJBi4D4kDSpKkyBhyE7NvVk6NlEdhlw9ouYuLvuuusyWaTNrsm8uhtyCzgECiLJPYuN05fJDoQyPUpk0CPdrWazscgEoJ6iIm4ae8x4JBPadNPNj3305a23cZTqHxoYoM4wHfCT6gkGfWRO2xMTqTV06zOorZ5/yQu33fhG3WvuzgOJ4kZFg7a2DQ0PTfqDQ/32/gQUKEefFHs6gKxej7PCY2uvLXf42ypKKgY7S0pO7nfrOScRPnhkcIBp7Cyd7vQzmfM8VK2YInzTXrZadKtO6zxachSAeERoh6mLZA3AIx9AiaCI6M4iJHoTlUfaD0iro14MoOOfFD7vgwnNkdmUU6XHHhunlEYZt4goPhFEiNTfVXlBKhMMrGQZkxIyZQoXQOyeFEGPlIl78FxZfHXaFZfmUX5Rb8VRDKEc/C+xHIB04oRwQzqyaSlsX9y2uaPLDB1ja2t9lqkRTWLVtbkVFXrssdtvu/H2gjRb5ZDkelx+VzBIxdblsrU36Q2tXcR8XWZT/arbkBWvkepZ24wPKj+n01E99Oa0ldIuUVyM7Ac1JBmlbOHkLXo6vRZvaTkHVcY3BpmvOaHdnpEUk+KtTXWmsmE0dh1v72NkNoptEAfgg1AhEvzKjoFBi2vxQdKLptXmuEBgiwBCe5BFLBE4NCTuf6y+Hgakcg74INUkRYAQ/83xaJAECRcff/L78cdfTMSsom7hI5hgI5wIViKAFtVI5XekZVU2psYaUr4quG3jA9Dh3ZWulTd42PDAaVccEnXeZQCCEosN0xi2Si8AEDNxeAIIEWejQQZjlUmiBEaSdLqp5UndHZsbGtav7+oyp5pwSI3E3Oai1ssvv+mx67+8+8ZVaQW9tpCkSydQnSBF72Bgor1MbzK3mhpNZqKMhqeuvfWN3Lr+ziUrS8qrS0vWVjjaJt4cqg0O+Ui2sZCDtu5EXRtrcmqd1cs6nbWl8eWdVvE/gxXNJSWWdhKviXin2th18bwBxrLTMzKyzqc39HAWUXLkw8Zh3VWaJ5zfV1HcgQKIryhuyP68DhIxaIt8pGU08oi7CB+iPmXhSO2AiPj6m+OPexIi4nWePOHJ4y4+8iKMH5kflWTgRsHRyg1aK2lECTkeyeyJKnwwlpSaEf1VWt0LFGIoxVx77W28u8PHFDUFEDsi8EP/1HWuP22WUQBHURPvorPMUmAqpbSlaLjUiZ1UgQ4vQ+eFm1pnhU5cy4pgRYfotLpVp2MrSlUI6nSV6NBEbcR4iSZOvxgv0U4lCHj5ZIyJf4G/8zzva8XpoXQto7C9v57znOec85xz02n3EkeQhgstsg611uNLOcVX4GLYpPkGbnZVqNRHZfyRuiPp6YmJWCKdQCeSgXh8Ph6IJSb6crnc0qGfNg8Pr7iGOojFPbfv/edQoVcP0qO0nUUMB27AF4jRXJOWZs0rHesKLyES/vgNN+zd2/3qo6/2P3dw39AzQ06HjfSOVQFiz1t9XmnVWeedULYLDSLUxjan4yUbXTtwwGmFV1RVU1NMm6oQ1r8IMKoPvHrPGk4TMAxNOpZVWdMxAP2tO5qPiQgoIgad1YBQIHaoCpCEd6gFRXXIUF/LV5TqsO0iwoCPTwxVvkNnHAwPATHrHZWPoF0EaoW5sHaXLEBPP/Xe4E8/zs0u/iAa9ORjz957WsGzAMKRY6gILYdki06pGaXuAKJo16M6e+Nzt7sxctVcDVL+nEuaSKQSien0RDqdno4FkijFdGduuHdpafA25mp1NrPiEMymIOG5gwfJB9EvDdL+wKQ37gsk0hOJiUTMY9tdVbz3+b1jXPhNOzbte/TVxx/f2j3a0UFWQn6jpXrTpopqTniVlZadtLZo3dZXZA26kVa6Hc9YqFntYIKPF9etRmqYfVXrqLA6uRBAqvGhCoDppVhdDBVC5j1rshI8+bAOAgNNxLxbxUcgAkjxKQSAKtIRRkV04MZbAJAKaN9y3ElX8w3yHezBrgXYtYQEtRh5ITOQUCjZOTnPyqlXyqs89S3rr2diz61Xbl7+cVGmozDC5s/XZaP67GUAOp9u+5dfd9UFrVi4w4cPt9VTheEkDslNuXA2t5X5DwycKJWuuHWMNfAHADQtrenTKFMs0Te8ef/27Ydo4rJ8aEsu43a9RLrukfff0oA6hjqsAPIznyCQonnZxMLS4J1ycoi4ubW6jOV16839Ox7fV81pFDeBAd4WdVV0Mnp77OGKMu9ZZ9PuaGzTAw/hZp+ABu2R7k1bu8d2cXiB5sEyha5Bzl6Tf9OAEGXkdTjUAHQqX+PygIabEvisdtlMIOYzLSYfNrQAUnxgI2sQxk1qdbk3K1Vx6DVDABEGkrQGNJGTjdyU6SNghMnB0mCE7kmlbidDy67/8PVb77ryfjQHPHoKlPLirmMemgC6CQ1qa64/jNRy4qDVQf85WYk4hsem1Yar0CWAyHB+Qmi/i9b+dFT2BdRaFEhOUDS67a737uLAzm2Dw7lMzLOywjL0/luvshbdTCDhZpfd58dJ6OKOLW6cs8z19fS5dFmt1IJ2lbYPcUiou390V7ubERQYV1LnFTu+/7h703nes848GQ3a9NBDbzIRE0AdXq/Nu3HN2G6LD+2BDwcJxH065lRURwMqXAVIlbyTG9NtDXTQOu8aKAJH8+Hrhig+GtC5SoFEWRAAwUBIXSscEBTnan6vyhIhUMLSol1SeYzIv0sDEpMIILw3BoDaPeXERS96/VbG9wzOzX3OkC7U57vvnlYatJ7uDxL3uZsjA/Vo0GER6jXZjDgapdNZuZ774SZq7ZZ6aUu7y93FcFjUZmISToEA4VRZkuSc/fD223bOLo3PRBdWVoaojDuIj/D1qzd3ON0AqvQHADTpi3PY/EB8IPRRMJKJhJdytGtOuFbaxyq6u3f176ZehVCTrEEVO97eWlN1lnfNmScVlb28475r3tx7BYDadzfg9NT0k4C109ug7uGNFWXS1wBAOEaI4qO9Wu4hZMTiAIQYaey8ZyA2bBUiZBUgzUcDkhAcgDBtWrBgf9cgEJkDkBg5ZdR4pC2qUe0NH938nE/sG1UM9PMJ1fgsTsrLLzuNTgL7l3/6XGbcYeL+RF5XgBjiAiAar11M8RKd6zUg/Djd4xlIDhVNgA/Spe+99FPz+3wShiMJl2TRT0iv2VQiEOhLL8yEZ6JZZ+PQnqGhgwcfARBxBLvHRcwhcARAxB0OoEKM8TkgkglO57YNr6y0dxdXMBd2d6VdOgu467qrH+/+eWt39drQWadfXbSmf++D17yJAp1ww+jYeefVlI52j9pZEjkNWk1FmhRhn3IS4TbZlZqAzFkxhmdglPQKHi44fDSFo4yaCcjEZySHAISFA5ByDhDdHh0+ELoWQuI3KLWRb8LGqUTGqdQ7qhorpToEsPWhE1WZJ+4BDYJszU1XXXfvkxQ+798/PCJTVn/44cfvkKefvkk0aD2TEyFFIKG+nLVHAWInxBokjABEqalTupmIO+eGFANV6J9P5oAoHGXx8yEvepTI9faBKUnzxYXMTDianehrRIfeP/jIQTg9U26xWhN42EdYiBhvwiky2T4NTAUHpnriNBGKpPucdTXdVdVkVaFjoSVlP3Py3v6mu2Jt6LVLzylas+nGBx/au1cAdYxBo6EbdZPhFKUyD2OdnGPTgLhxpxVIiOhtqpllRohbG86ByUSbMePJb3k8fNFUIAVIudisLRg1xUeI6KJDpVaCR/y3qwsLUaRC8CC6VFHpDSL1PTqtK4fL17A97aKTedudF19+2bbOzv3LhzbPIYvfaXnq3mcBJFMaLqez0vUCqK2tvk34UPGMCuHAqSanDvGqiMixENkkuk23LuI+7Gni8AnNM9cl1OBL9OGakdqbmA7PhJfC4enexlZ0qONgB6bunlqLzZZI+pFkwAchBCyCaKpnikdT/lhjZaVt1LfriMVlQawOj31sbPeu6oa1oV8uPYdzHVfc99Cbz19BSvuZrZRslpU+3G2r8YoOMeCZTRCAxMQBSBMyK5skEGckNPlEpGRH+KBBCorJgicmn7xjpwEJHlEglebWgWzNByTcVNEJXrXse/h7QIj2mIDOlaJurcTiu0llnjoNQWSIAeG0rLigZf3F79x/KLdldmRpSY9ZZQ1Cnn7q+uM/LPjgjguvA9D511/c1OpoxYdraqEyjoEnDjaqMmygcUNbSXu7286NfpGBeFIC2n7vAHOJfJVE5OIhCZwmcblxFYAzPj4+w8f4QvPKUMdLjj0veJxMLbLwMlROAnq8iJdMHQBTMN4gijRPoDDVl0gESgKVSXwOv38CDzHN0mfxbjyrYd3pAKpYe8KaFx+45r4TbrB2UXVQbPOyM6M81ksPQ6p8pZT3GDw1sz3/qVpYfrBswufszz5TFbzoj1KgPIJVhBAea0KGh0eaWwQ+ZIFERwRQEQ6CyimgMDq4oBIZkNFSqAjxGpVxkveKyukqV+FkwfPLL19tHPBHppc2j4TDI7PMf59lhjUP5hiDi5f9HhZOBEAf0AL87uuZMFLvoLsmGoQLx8ggq4O0pYTk2hrZsLa2ozuVvsoA4VI/sTguj482GpPkhwZ8/mDQz+yNWCqWzcxkMiAKj+cWaGHXIbaRw3XlF7hisWR8HroBtquYt3mlQfNk/hoaQvOh0EBvLpbs640FUilQomhoYzQhSumPTRYVUUux5oSzrnjzgd+vKHvBwqGSCoysrc4bqvZ5G6giVSd0ocJNIAEFKohONYip++yzszWgv923VXRMjzvPB0D680yFJ5+lI/+JY60A8VQURrnWCOuOTqFzpwGZAiog8a/RHhzxHSKtGPp4MLIQDo+PcMHGR7QACEJYONGhP/8suJ3Bj5JevXB9C2qDaWs7XNtKnsHqqWUIQm2jk6QLh+YucElaTOZ5SROa0mrVAY8cHkoBIH8yuTAzk4kQMA1ns/yq8HgYP44ljMEpjZBqtqWS/rgPnfOnpglADHCUGZmfR33kI+Tr4+QDQb4ACAHk8/P9PVGxmalIJBkLVFpK1625ce8Na08os5YSAqOX4a46Oq1UcxAIH44liDTDsQKoUFfymDF9yagC6DNEeXB8HgUICAY1/cwUoYMYfCSGYNZYAQgHTny2QoOHyYSnqwGpF7PHVRWsegd0LcddX4PPAPnOHi7Z0sgSlEA0O4IK4WYLIj1u9c8CZp6IlbuddDfzocinOlrRIKS5tokG5xeUMy6Lbs6cmnbDCP9NNIchsD49alQuMb5CMpULhzPRKISy2TAaFF5YWGld4QfhptMM0uOKJSfRHACld165szORlM5bWDd5PblE6sRz6S2xvlQghdeeBBAz7npSMVm2+D+kAnbpWzW6i4hOsbOujDOCodLdW8ds9uqN9KukDltOsp2oz3Vww6opwcARwebp2f8P6HRTgVZJPut9hrJwABI+4EF0HQLaow2Z1hplAgGEtgBTA9KE4CNlWuYWlR8hRXQAigeDEQp4lkbQIHXRRgCEkWMZUpAkolBwN4cer7qdUOn6OwDSUu+g9LeVuxfw3cqbrrqqpbm+2WNvb8ZD4APLVim5O19ll+oTpHQCSaYS0Ww2GumJzoRFZQVQb+dKa8kLzXQlbmyXeQ/J+QMDfl+y99B2pt0eml3KLS3ExK8IMC3In+jcMjEdmFDqE0hWElnlL5CeCH3u+EjW1eFBuqT64InStWtDpZWjo/2jcoSVUznSgYqStBNlt/PHqVoMQNqhOjYPCD5HAzJtWv5erVQmHkRpkMlHIRIFMjBwJ1S0AIiH/zJxANJ8dCkQmdevxMBNcb1mxmdnAYSwfqNECI7CnFg6gVRw2rfP3k6fNXpJynjbDza0YZWYuEUyiIWniSS4kwo0i60R80YMWTWOrKn2krrrQiqFD5cxxeXvgVAPCqus6Tgmrre3eYUf1gxhZ+c01x3vbSCeemILk5627RzkPbK4fZC2wsMJH5M77zrUFwskBBA/UZijSAEWN4UnEgwm7cRsXZQZuZts68oqqjftYnb1vv5qCrGkTQiZ4+MK0SBTgINcXSjrggZ05md/8/lvQPLYfAIgRNyDPB8FyPCnJYSTB5RHoQBpWKI/ecHCKT5i4AivflmmGpTCR2EBDms3720uHQo0J4wWf/hxcXERQKd9++v6C4mYXkg2iDnRh1tfQHAS8L1kpny9tbWk0WJ3dlmsDPki99+F5ngJ+VDnhvAH1zKGKgAIDaKlI79RMertZPHxSMRoOrG5b9I3id8WGkilpwk4oD7Lg8tzO6+k9dj+tD+1//7H7t+ZY6OLiYO48BGBDYhEWIjkTTA5GUwOpmvIFVWNvfzIvpf7CRBxlIEDOXJGHgO2qm5Twpays8eJ04DgpBVIEGk8ecmvSIg446b6mIVw8uN0GoE/+cm6tg6BhHwbN77RUBjDvPGhwWk/QukPgKRtkngIADLYcKff28DRSvTDj58T9in49Yu7f2Vw97PX0XMAH3tDbfkbCOUiJfV0Pye+3VjbWM6gaZ+N8sByajTa3dYSNKdUhEwe5aKk4SAUjYhEAYTOyq1zy0ozEz9Y0aZj0wm/v4foQWg+GfOlcltyrFQL2czwIbowDvfmppfvv+uuQznJ0Qb8mE3WIPEWksAJKkIR0KdS3GejPZ3TD4e6u/sZvyw95HbQpPVGOUtHExADj4FItAc++AoGIG3otP7kt0B5PqYYBXCiQH8rBqIAnaT4KI9N09FAhKMBkww6gABlflW/3khUoD+aD3u/IJdL3s7AmVGg8q4caqQBLRZceDtCBePl6+/cIKFsTBx4PgFQbTkbV+ap1HOVrfZSdy1ZVrfT0exxNhODtvsInZJ5IPDDhy0WoyYB9YlGs+Gl8dkRfHqOa+ccUO7ty02nUYzgVPAAGjSB56LqtfgjOwNP/mXZ2bmlzPhMzySqU0lwAkiokkRhRYNEL2Ek+AEUyaVl9mh39dZ9+9CfvVesRaRo3Qi1AcjcvHMhxeQUngggBAU6A0Ca0N9pBY1mNSBdI38Gl9vko0ycoQKKeoGCYwrfzE1rnC7dpp4rD0iZvEJ5tdl2DAUKygEgCMFHAQrzIKzwjCtEeApzi4sFeNnEs2kCun4DgGoP15dj3sikHvGwLZLyBA9Zh1YH529aa1td1tryWnulx9Ncb62rqbNQInzkCJQANJEg580tEw6zHcaMjmymk3nTBRe09DJ5OB3AKZsKfjQ/mUhPMgmaAuIg0pPN8Cge5HlkJpyNxCWV7qM+eMAbIGyEEilA6KVg4jPKMpdOVtYxmISKBVonqD55a/VRBhXKMa0cfAQQRYnnFBhuNnzOBJCZoAPQaiEuZ/IxT5mYfJC8jcO4rVpgQKDpGHgQ2ZtS0JUHpJw4AAkf8LAAcRW4XlnYZLLKuhluAqIAsQopDbrjDnaq1+EiyHlQojz42VKwSMTFgbZIczeG2ThaZbBoebPb0VLSVtdV4iQtbfNWWtgstb7wrs1lt3ECNSmA+H0zI2I7Z0eGhzfv3Mb07i0TfVtyqWwPKNBrZbXwsHkD8Sg7M3WAmq0DXx2Izo4Ln6RE9+bjofkAfl+MTFRS/z/QTlxEJBjpCwwMMJqEbpr9O86TNscAUqdJ8hXquHPqehSyc+SZAoTglK0GlA+TchNg+djbvwAVGBpkRgsMPvryHw2IwN1RgHi1Lgai8eVAEAcuG2ENQlh9uEOHFBtNSAPCXyi4YwPO24XiZLeQZMB/Q6zvEg1zwaXERlckKTR12LByrkZPbb3LQZ2gxe1ztdfUUeN+x2GHlW9rbuaU9UImmhWNHV9krzU33ru8bRsNSGlgnetMs+9BT4iOsjCKLzGJCwOiaGRqYGogzoLZM57pGYglAxRFztP3osGfSMUS0tM4Cp0sP5qXZdhkBXsmJjd+VVy1iQF+j1cYXfrlSAF8lJiAJG7GRZGIMqoleBDtYgPIBJKXPCCjyCpvnRAVzuGG/uT56OvPhxKTkLrDyq3io6NBeAh0tEJ/qJBHd8TgCCHYKEBhtRUSSPDBxgFoFg1iYBWESHYzowP7hnwigBiy53Da7QwwJsXAJBK32+KirZjHWupy+zxW8jt2a6NrzzNDe+pdHl5bv+J0kqqb4Zd8LjKytHzbT1cyLma7nNyeFEsVFD5cYnRC3kGytBArneLL3MnXMhGCCArQxjibK/BYPQlOmoUXMhkYLVD2wNmXeENZWRUtV/iUMRccioaPMnAKEDezwBArByF4KT9bdOcfLgKSR2M+5VO72VxiUz1kARLQKlgq+vMvz8B4TMhBxPDl1V3eR0dUQLUIPixAXA4AiWmDiwpfZgw6I0rAg5swByQ0aMOGO+9gaiqpBnoO0CXhXeQIC3+7i55EdkoSLKCx0TyXCTQlVo/dTqCZaX2MomhvvLl9aM9LTktJS9OdF17ctHLl4OzCzPgIuoksD972Ez3Ktw3SY7lvkuRcj8CAC4KVk4UfKFkY9ShefHEhigaFQhIhn8eXs3SxO41EUSABtJCThEZAyoiK1zHjsoL9j/RPNjohibdmdKPIr0KIAgQfReafYdKjSkO0sFPlxgU21EfuNCDECOaswmMAkpMq3GvhMXd5QLoWSDJ9a+BDjGdKLz9qG4QYGkSsJw8IEUD0v2zZcOedTURGy3Hg4HNEBO/Z7m4niUq6u47eeL6HvTYLZ3x33TxK/np06OatlH127GmnyqNjt9XBSOim6y5urd3++chMeAT/EPz7B29b/pSJSzsHr+xMx3jLgEMAZWTRV5QAZLKaUtQyWSKqIaRhY4gQUqlMXZKiyKhILFZZtREpLuavG4qLG6iMphb6Ft1e1DynrSHBx3wouAAEGfIIev2Rmwnm6BydAqQ3P3kBDHKUZ236CMIHUWy4GY/zu1wj1QcgMXDiwmazGflQXPjUdxqO4WjLn3NzfwH0jTZQPlR8ZQAAAABJRU5ErkJggg==\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal temperature card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "pm10", + "coarse particulates", + "coarse particles", + "particulate matter 10", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_horizontal_pm2_5_card.json b/application/src/main/data/json/system/widget_types/indoor_horizontal_pm2_5_card.json new file mode 100644 index 0000000000..96f201d693 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_horizontal_pm2_5_card.json @@ -0,0 +1,38 @@ +{ + "fqn": "indoor_horizontal_pm2_5_card", + "name": "Indoor horizontal PM2.5 card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAq1BMVEUAAADf39/f39/g4ODg4ODf39/////k5OSAwyzg4ODf8Mrv9+Wg0mHQ6bCQy0b3+/Lz8/POzs6/4ZXIyMiv2Xv5+fm7u7vCwsK2trZYWFja2trH5aPA4Zbn5+eYzlPY7L2tra2w2Xvj4+PV1dWQykbn9Ni43Yg9PT2QkJBLS0uIxzq33Yh0dHTt7e2enp5mZmaIxznU1NTb29uCgoKn1m6o1m6dnZ2o1m0vLy9qOap8AAAABnRSTlMAIEDfv1C6kOEmAAADhElEQVR42u3ai27aMBiG4W7tvv6NsZOQEyTjPKAUeti60/1f2WzDykbbVAMxAfoeqZVxIjWvHEMRnBERERERERERERERERERERERERERERH9T++Ons94f3557IbnH2zHZYajF12+P7uIcAKyi7MrnIQrhhwYhhwahhwahhwahhyaupBQYal/fT1EPwdGdxhe95+mDklNSNg1Ibz2XTIdtX8CixlmyeLOTeVJhB0EVVMrrGgdopZ6fkKoe6o+ZN0hH7EKSXCft++TYXuKIRZuSaaPfeygKZYZw6tEAtQaGGzoiTEP442Q+g4XMmu30b5e9PtTIJnByof3ObamxQTjphg4yrwZcpM+C2lCSfOtkGqi1x1+Rfyv+2k0xWjmbqnoDtd32FpDeu7yRMEayM1TiFJ+b4awgp6CG7nuwE36GSgdwJk0jKoNCbRfeNexEfJ9gSmms/ZjvsDj/WyIrfW08iGhv8qP5neIkk/+vkvtsCsiA2nA+maAQLpu5qO7Nr93q1SCmpCgYU92JV3Ui7Ar7f+GMgYvhQykqyvjQ5SkLkQmvbmNqHrGXmE1ByR9PaQSx5cE2C9lZOxvrPFLIUpEAT0fot2JgR8a0e7xV/tjHqT3ekggTyUaexUaf/OOJcVLIf66V78b3fXQnandUFU6rHv6bTyVKOzP+knRSFPrG5kEGyF6HaJEvxDi1e0R+V2yXwOZLK98pbER0pPVMriHaosQNGUpeGtjR7ebE/+w/yfyFU4YOEaqMVZdoYtMEd6IWl2yaWCbEMzFMcBaaxkT4baEl2XIrmI3cYUMQKffQVaWywOZO7NeJfIjtTSsv/aImOZE3L6Zu9GDNGyB3ipktSZ6I2Q0isskie2oQFEWedYq4tEoifPbOM+AuBPleYko9gfiJEetVJbS5yE38tDw83ORB/c6MpBwyxCoVERthHyOW1ESd+JlSOfzFzu2c50yLhIfclWWNiRpdZLI5hYRtuD2iH9Z9+zIPdGadIf/flWAP/XjL0XcGiVF5FYkzsuiFbWS2M7d5p0khgvJ3Ip08r47MCoKbBuCleqbQmBE7/WNVdnB6z4nX+DsGiLOAHsNybK6gxG2FjardVQznQd8q3u8GHJoGHJoGHJoTijk4gQ+ZgeiC/uFgRP4oD2zXxg4+3A+vDx25+9P6Us1RERERERERERERERERERERERERERERP/JL+y8/ulGq8beAAAAAElFTkSuQmCC", + "description": "Displays the latest indoor fine particulate matter (PM2.5) telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pm2.5', label: 'PM2.5', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM2.5\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 150) {\\n\\tvalue = 150;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:broom\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":35,\"color\":\"#80C32C\"},{\"from\":35,\"to\":75,\"color\":\"#FFA600\"},{\"from\":75,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":35,\"color\":\"#80C32C\"},{\"from\":35,\"to\":75,\"color\":\"#FFA600\"},{\"from\":75,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal temperature card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "pm2.5", + "particulate", + "matter", + "air", + "fine particulates", + "fine particles", + "particulate matter 2.5", + "airborne particles", + "microscopic particles", + "respirable particles", + "aerosol particles", + "dust particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_horizontal_pm2_5_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_horizontal_pm2_5_card_with_background.json new file mode 100644 index 0000000000..a22d207dd2 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_horizontal_pm2_5_card_with_background.json @@ -0,0 +1,38 @@ +{ + "fqn": "indoor_horizontal_pm2_5_card_with_background", + "name": "Indoor horizontal PM2.5 card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest indoor fine particulate matter (PM2.5) telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pm2.5', label: 'PM2.5', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM2.5\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 150) {\\n\\tvalue = 150;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:broom\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":35,\"color\":\"#7CC322\"},{\"from\":35,\"to\":75,\"color\":\"#F89E0D\"},{\"from\":75,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":35,\"color\":\"#7CC322\"},{\"from\":35,\"to\":75,\"color\":\"#F89E0D\"},{\"from\":75,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Indoor horizontal PM2.5 card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "pm2.5", + "particulate", + "matter", + "air", + "fine particulates", + "fine particles", + "particulate matter 2.5", + "airborne particles", + "microscopic particles", + "respirable particles", + "aerosol particles", + "dust particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_horizontal_temperature_card.json b/application/src/main/data/json/system/widget_types/indoor_horizontal_temperature_card.json new file mode 100644 index 0000000000..887b697dc6 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_horizontal_temperature_card.json @@ -0,0 +1,28 @@ +{ + "fqn": "indoor_horizontal_temperature_card", + "name": "Indoor horizontal temperature card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAt1BMVEUAAADf39/u7u7t7e3r6+vl5eXf39/x8fHw8PDr6+vf39/////z8/MjTMc+Ys7j4+ORpePHx8fk6fjPz8/x8fF1j9zI0vGQkJD5+fnV1dXCwsK2trZYWFisvOo9PT2tra3n5+ft7e28vLzy9Pvb29swV8tmZmZaeNXIyMiCgoIvLy/x9PvW3fWesedog9lMbtI8PDxaedWenp50dHRKSkqDm+DU1NQxV8q6x+6qqqohISHd3d2dnZ2F97sdAAAAC3RSTlMAIN/fz6AQ7++/MOBJYlEAAANbSURBVHja7dppc5pQFMZxbdP1cLisIiCKiLjvS5O23/9z9V6pI60NSdWmxnl+GQNceHH+IyGTiRUAAAAAAAAAAAAAAAAAAAAAAAAAAACAl/S++sq9ryifP2mvnXNXrVSqH+kGfKxW3tFNeFe5o5vwtvKGbgJCrg1Crg1Crg1Crs1th4x1djv034xJsmp7+0k60X3n5959FI2p6JEQXZ+yQQcNqUUlHJ8uZWx0dZJc3qvRjqUz67uSSZcl3SKpNMRiw+j26EC0GsIhX5AQpvBNObfcJ8ck8n3HMQU9rIUQZJIvv8vzp5ss1IgkRe5Oj9minUi3rO6EiDbMvc1U5y/WM0JGeo8KzAZRY7A2vfmguWoszbU3N/35oEHNzG9+n5uDuem16Ks8NAerbEknkxUzGXLgsks5Y0a02BDVmI38DVo8I2Tc/T1EDL1s4HnUpJYnD73lKvOGTlOeGzRbciEPETT0VhmdzKiRUQyx9m+ICphO1cH05/kJf+k8HTI5DmkK4RxCVsvBUgh55Ayd7JcQdXud4dcQlw9z3Pem6sely0Y+pWE8I4SOby1vnZn7kGG2FmKdrVTIPBu2nHnLbGZDFfK92VheLMTSOaJfjfhoqSREX3CPfucUupzCgpO/nMN1FwuJ8v0i6y9CxizN6HHCozKXC9E5OidkxsqEznR+SKR+cZx+a0WsFJ8JbfqjtkYF9bp8xSRdLERnl450eUPK2HU75SFTzt3TXphPLcfcbuW06ku2te1YbupJPr7/oC6waXdKa9fzzLNCIvXsPeLyl8O2NMTlXK0YEtthPbRFv08UaIH24Pe/9YPYF0E71ELbJrWstdSOLRIt6afbflI/M2TBLh2rMW/UpsvuEyETznWKId/sUEv7cZrmISEldpzG9jahQM7v70LatqZCtmGaxmlCdnxWiJq49ucLuLvQmXXriZDRjBWDDiFBP01aWhj4cUCUBi3tIbDjIIkTO6RQS2yxC6mHfRki1+pJkPpBSOeFuI89OSOdZYsxotIQVeJ2eRZRmfIh62lf0L80qln0GxVygjaV0tr0sm79L8TXCCHXBiHXBiHXBiHX5u52/j1d/UA34IP66MOdo712nz7f0odqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF7ID2D6pgpcp3y+AAAAAElFTkSuQmCC", + "description": "Displays the latest indoor temperature telemetry in a scalable horizontal layout.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 15) {\\n\\tvalue = 15;\\n} else if (value > 30) {\\n\\tvalue = 30;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"thermostat\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":18,\"color\":\"#234CC7\"},{\"from\":18,\"to\":24,\"color\":\"#3FA71A\"},{\"from\":24,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":18,\"color\":\"#234CC7\"},{\"from\":18,\"to\":24,\"color\":\"#3FA71A\"},{\"from\":24,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal temperature card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "temperature", + "environment", + "indoor" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_horizontal_temperature_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_horizontal_temperature_card_with_background.json new file mode 100644 index 0000000000..5295d4bad9 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_horizontal_temperature_card_with_background.json @@ -0,0 +1,28 @@ +{ + "fqn": "indoor_horizontal_temperature_card_with_background", + "name": "Indoor horizontal temperature card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest indoor temperature telemetry in a scalable horizontal layout with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 1, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n horizontal: true,\n previewWidth: '420px',\n previewHeight: '90px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 15) {\\n\\tvalue = 15;\\n} else if (value > 30) {\\n\\tvalue = 30;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"horizontal\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"thermostat\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":18,\"color\":\"#224AC2\"},{\"from\":18,\"to\":24,\"color\":\"#3B911C\"},{\"from\":24,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":18,\"color\":\"#224AC2\"},{\"from\":18,\"to\":24,\"color\":\"#3B911C\"},{\"from\":24,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAaAAAABcCAMAAAAfz5G9AAADAFBMVEXR098MERAKDg3O0d0PExPT1uAZHBgHCwrMz9scHhrRxMHSxsXZ0dMXGRYUFRPFydURFxfIzNgREhDVzMx5X07UycjMvLXYz8/Qwb2ZjobNv7m/t7O5r6qejnzJubGKeWu8s6+gkIBzXEzCuriKeWe0q6USGxumnJaomYqNfW2vp6KQgHOXhHGchWmHdmUXHh3EwcSroZqsnI9lUUN7Y1Khlo6DbVxtV0jBr6OWh3atkHOlloYZKS/BvcDHxMispJ6lk4KTgm2binjMyc6kmZK4wdQ6NzBcTEDLwcHIxs2VioOiiWynjXC+w9G/rJ6Cc2hsXVNuYlrT0tmekokaIiOkj3qKfHSNdGRnWU+Rg3Y2MSmYin3RztOWdl8vKyNFQDkiIBqxoZRodoXGs6YjKCfX2eKFeG9TRzwoLSwtMjHFtq6Shn2Dc2AzNjWZgmV/ZVbLy9Oypp1PTEkhJCHGvbvCsqmbe2O7uLuIcGARHSLX1dyonpm3pZcoJR54cGp0aWI8PTp8bWBbUUbb1daUfWmSclq1nYq+p5RQQDS0rKmLdlxjVUqcpK1eboGSe2GOblbKt6x/a1i6qZydk45KR0Ghi3bNxce5oo9mc3xXZ3QhMDjCq5h3aFpzY1RARUaRmqRLXWgxOj2yvM9sfJGKgXychXJRUlIlOUSsk3taVlJZRzhebnfNrIyqtsywtcLGpIavmIS3s7a1mXyHj5mMiYhQYnG9xtl9dnG/v8qhl5XdwKp2hpzBnntsbG22u8fSuKSCkKdEOCyGmLTLsJxDVmRZWl0qQU93j7KCfHqRd2htZWFkXlhDT1U1SFU2QEV3lsC2k3SHaFJ9f4U6T2CxrrHYt5ZrjLcKITJTcJKhgGeZpbqjsMWpp6qLoMChnaCUkpJgY2YNP1p+hZB0d3zj4+emrbeUqcd1V0NCaI8ZMh1nTDkQMEdnhKoqVXo7X4AfSGeaWzaohG7lybZaeaGvhmSGYkh6SC5RXjG3eUlmdEmpbUg0TDB5jWxRZkx6fFKUoHS3aVdWAABVF0lEQVR42lyaC1STZRjHGS10pzYW0YWEcgIDwRxQg7g5AppBGiBLgemAWjCiJYwSBcdleYEgicBLCiMMMI0ghRINKDI6FsQBwlOYAWlE2OVoB0EUsP/zfWNi/+/9LhvHs+37ff/ned7n1YIjzND2GI3lkYNXdx/a3birdtkGnzjdnomORRnNdZ1tIzn9l4b7hw9u++bvquJfP1j8ZbuFkWPJiMOxseEI+JaWNgrjxUud1e9/zQ1YxD1yUCjkckySKFJ0XfmxbrJsz+xsF5dQaXS0nV14eM369UkR+fn5uRVRUVEnT55Nyo0xGJIz4uXaFK3CkqPWxWssuRG1e/eu8fNrbW1tbKw/d+7c6dP//ms7eeXGjast711sqqqqKr5Doxd/25eY6OUqleIjXAqd3WRe/iIHpcgl8OWXs9aFhKxa5eERGBiY6aEVDqzamLUxLS0trLCAzxdw+QIcBVYCgYBvZcU7mhkWlraqU9FAam9pCUtL27iOlMVoY3rQC3v3vlBbuxdHXOG4QA4OIgcoKNDC4q7F993XrlWrNWq1Sq3JwC2xjFixYseOb7/d8e2GbzdsoJ20lZU3tJn05GbbJ7FBy2yXQRZWHKFCa6wz6nK3fnHmzKHYis1bd9Tu3fFtaYXOKNc1n23rrIvP06Vs++Hv81UsIIklq0WWIGSToLfk8NUSIZf7/Q/igEWcgAACBIYYAKQpL8pNknl6AlB26KmysmjwSWUAReRXMIByTkbFxOAqJj4uL0+r0cpTdALF0fKM4Fri43eisbGVAJ0mlUxOXhO1P1MHPE3zhHA2AboffFz9o6OVSk/w8XQNwt2KdnEEn40hYas8QAcK1HIHWtLWpYVB+8YEfKEAdNiBncdLkA+HhLSMKNrNgECIQcQCSgSgF1hAgGMGhJMDw0eEg8gDfACoIUWtBx8aAjyuhh0ABEQkM547+RAhlk8ldgbQSoMlV6KpNtbldn3y0kvPe7knxcRERuXm5h7VpYrWf6fDPUvRqDWKf/74+SIAfd4yyrVctMjEiAMXJdhwOBl5XC7n4KcBDCCxEHxYKTI0ebqYCDfwcXFxORUqBSA7M6BcFlBOTjxXnlwUlR+xPjy8TOrimZ2z6GhuV3Dtc2vWrLna2miyEDG6OjkZfviZZ7oZQICCgZ09X2IA+ftH2wGQTObpxQDy93TMYgCZ+BzP1HIvtKdlpVmvtU5bV2LFF1jxBBAXNiJZJSToy4czRyQABJkBkRYACq41kVnoIId5iQKJDwNITYj0erUEt0oPPgvgQCwc4CE+ZkLEaBnxoc3ipafqbDhWmry6tvRHHj2qjlNlqLXVGk11Xb/HOvpltIV4ZHb+9EfDe79+sPbLtW0cco/ZQwgONpYCrZorDDhyhABtE3M5jH0EGXFyuUoV12GQkX9cQonPPKD1BKiiooIcdDKnKyc1VOYGo4FOtpubLD43x30FHARAjQDU62cC9NpkSQMAMQ4CIYAhPuy5/7cHACgaBkpV4rNCEeEASOr5On7FxhAYCHSOHx8gQGFpWRs3bsSN36ACGx7fhqI1lwIdCMFEen2cfnhodIGF7gS0N6h2RbDJPHQgNi8wW3AwWSjIY/FdJkAZagyIB0CW3hTfIIpxBMfJzGehg1ix/gEgkb+/jsOx0o5cWOr4SBluX3PNyebMjVkQfaeXSXff19L90x+jPb9+OvrV4TzLO2QjsOLb2PAASLFNwVlkqRAjwpHFOGLxtnFGeTLyTygBoggHQGwOiohwT5Wm2y9xdCx0g2QyAomH380tpygqtrYWeNYMEqAtvSZA3oKRnw8fPlzH8Kli2UB0rmr6bR8MhLzj66v0DxKJMJCCpJ7Pgg9FOJbPwPFq7lBY2jomB4Udj+dy4R2OJVfAtWGjHDDxeTwrfUICT3f2wgJA0G1AwUq7cAciE+Tq6hoUhM8yRThIJIr2WAwHWVuPajLMoqiy+o7otmHrzK0FBio1u4foVM7HOAs+n74PL0OtkefpunJq7FzcCgNDQsjVQLSYfgd+TztiSEvgKALKqJawLIhyNnxVAl+QIQ5QcMXwVIAWEY7+ToC6jpYDUDUFOOA5ZYpwqUq7aGmi/VKQef311x0x7FlAniSZm7NzUkzu8hWPXyUBUO+WVobPuX9fG7t4/uPDDZ1NEACR5ildbMrycAUYB2Wsr4ODUukgwh5dGILfQYDIQMBTWDig5QylMYBCkIWauUijAhtL0LFh8hCO2AHJCmbSf9fCAlq80EEgFBR84J9//ukHoPSsVUudPRNRnkB4PoKC/GHiFtjH2vqzeUB67HRXVGx9QGIdNDNjso/TchiIFQCBEIkMZGuRIREyuYRLNUwGIpI8Prm5fbS4qT2wJWzdy79hg0YvFld53Hv/Q8ebqvJAYRHJBEgQNznm0+GD8o0rxpsHD1KAo18sGR+/qTOM4+gZSkJ95Zq4ffu7b7zyhv1SkIEIjyNkz/LBRoDcnJMM+U47Hh+8enXQr7HxxMREPeOg+n+v2V4833C46qOmposo5KhCIBXT1aX+C47wj0jpG+sbHOwbayd1kZZ5ZoKPKcLBQIWkPG4V3kxLs7bGkxdoQ1xo45vYABgOuMI7VglDAER13B2A4Jva93799df3HAhQ2lJ753SzPGVh961tsSY8n33W0EN4eAwiK4r6W/9XHczMzHh75yfFOi1MQRALCHjgII6VKaFwSAwqrmKbUFi9EWx+Iz44vFzcVFw18BAAVVV1chg6NJgQp0Lpu8VnbEx9hGsphIN6qqure3pG2nKSkiJereia1d0sDw1NTE9/9/lXHn3F3qQlJjwm2ZtDHOMgt4iYyg179my5Oojg1tg72AtATKV9zdBf3HD40oEmEgsHg1FbW7+zf5ADApy7r1KplA64ZUulskCEAtZA4GNfSKEUgNYthoNIYWlqAY/SDo/48IkQNM8I6IyZLavCQtLuqOI+JAcRoPC9BChkHtC7DCDnNKoPCNDatcViLp8Q5enyjnYKQchgds9bFNxKp6en52aaX50rhYlKb1cI83xIFnSfTYDMQmAWatMo/xAjHLOKcU8uwEEDVVUXQYH9N/N8rtyYGCy48prWUt2zyHJbD/Hpbjt5tqYZKjuVne781BtL7ZeyMgNypG1eqIrJQSwfAMopity6adOmq1tKTtS39m5qZAC1nj5zWdd/vqH9vf6m24SGTw0NEaCRuq5ULy+HYF9IKXIudJaFlkndGEAhrIEGaHIkk+Vxhjemka3S7ksrbspTdehpMoSN6GCYRdbKaPMIBCIK+GZAQR/W+q786O+//wl/gQXk7LzAQvZh83gASMjhStSanHsyzz67L49iHIsHfOa8kXympqamp2cNczenSikHmS2EMGeCBECWVqZgdRsPARJoQ1AhmHT33e0NL7dkvhny7IX+/mIdpSASzoKOSQJ07UbHxJVri3oI0Pc9Pd93j9TYRUdLs9NZHPN0liyZB0SJ57aWOMsgdrYkIwflFsVs3blz0+BgSSs5qL4RgFrPnW5M7rw42tDe3d058tGBS8A0LOqduH51/xuFQ8UjhvLopfYiAIr1VUoL3WSh+AJujvvmAcFAMogANSGnpEHWDQNhWr1Kz6YeEIFuI0LM5woFGcOB5jqBAeRsl+uvSj566cUDLzZj1sMAWhji7NMACHxIVWKhpu34s/v2ZcqeDWkDIJuthGeD07fe03NTU6UMn9nSqfGbuJrb0zdvIUpD84QsBHq602Y2zJFUHUbfiGKcSUQqrQWl5zCXHMQASiggPpMFk9f1k9ev2xzoWWSTAu+kRrvgZtOtLrQ3iQXkeBuQGc4SDFTYRAeDdVDUagDq2zk4uBOAJrDDP43nTvcWRFWNNhzu4XKEJIV67Mb16zd27X/wHsequqi8GgAiPgCEgh25OhSAQiBMSQOX2iXFymTpnp7lnGZrpvIJawhb3MazSoB/uABjzj9mPlZCzCE0xzPbQQj5mADRnKrNQy1//+sD7x/oH8p2KcwKcVxizyLypLF0rfVaBs8zzzxTJezfty/QMXDVvsB9+y5wtXxLnw0bgqIMZ+03TE8hN8/OgstK7+lx8fjUk09675mfpzKI5ieqntLUiNwuQ3xcgsCGhYOdQhy+0d0vr20nQFm3KVncZ92eYsngQReh4Njly5PHOuI6jun1l0uSTtWkRvtLo6WhLtnZlPHTTYCWMoCAwgxoyUI+cJCnmZAMXGUVq2P6+vpKNg1uwjx1AoXcufpe5CC/y7ZJIKShB4lcfhnmvXEDlZ6yRp1Xrq2xd6YQF+yrtMOESwkHyehzQ0KG1lesj4kMRa4gQJYXrVva29tHq87/3JTAT1DBQCYRJbMEqOOQn/g5mTDtcUxuCRARat5olDd99fmB97sbstZBYatg0CVLnbPRyCqTDrQQmmcOk5okdZ2dIyOX+puHhy4MDV1ok8RHZw7LMj1WVcwRofGZqemV3qWVM+OqJ996zXtBnW2eqtpaIIC6obCVebq4SFPX50QZkokVl5sSkpX18+8fP/P7V+cX//abRUtxAyq7/o/e6+w2xmMql0DCDA9RToUuQESsHatoSCoNDXVxwd1OJwsVznuIeODsbAZEdGiQg1hA9I/gIc/I5GV9fU/27dy0pbXVr7cR7plAkdDbKzf2D7zylMO33pjybR27QroBRn1FOXw0PGqc3Rx8AUgZm1pTkxrrr1TiWZHZD3xniKmMCXfGB9OTrrOsPthdp+vs6f76e6MqjvCwNRtRuSPC8a0IEM9r//btXonOyJvP7tsIQC1VdaoXz3/15fvvtSy2IGovZ2XdbXEXntzRYhSUDSDz8c/ni883NDSh68WKLoQ48vkKzSWPVasqpsa8y+GhqVulpT6zt6bmbs3MmdLPwioOw6LQGWKKKKaOYuvdbLAaGhr+zqjZplAIxWIxF3NtSwRRsxDk+KrymFwTmfBw7GZGIOTC3GyQJx4Q+YcAOR/NxltmQKyWprMJCMIJ148l25bY2vaVlAy2wkCtrfVkoPrB3kTnN1555aUzhw41TiC23WAITWwqicipkZfLk+3cPB18g1f4otLOyY94zEHp7u7rEFuni4+LC3ekIORs/5RXug7+M4kn16sSBKYpkM1C/7A1XQbPCu3T1bsO1eMjDzXu373bNQuq0+u7n/n5869fxHyKtZWFxbo06/ZiVP9NTV9+Wfzz+SrMA4pHm7hGtSJDopAoOvMudlZrjHUoGjKMmZn2jy3rSp6ZHZfc7Ji7eXPm1szU1FypucI2hziShX2hcyEhukN4LXMJpWmlUum+Mj/qaDJqHRYLIPHikqNyY+3wV9pZQKyU4UoWkclD6YBhsgxZhaJ1ebkMH7fE5B+TwASDBURy79jMAOq7agIERPV+g1f3H3rqpZfOnAAdEgG61rh38OmkJDufb1cWhDuHBvu6L3/ssaSkqJjI/Kef9g1+OlkeEa4VCE4tef7d5103KIO9PU8adejSM2HnaBzPinUQEg6o8OfxJAAQKYFH/R/3Tz7Zv3/3oUO79+9yuBcW6tcnVN/12dsHL4bRDPbNN7PWWWBGv2pgOGekTqfr7v4Ipe7FA5eqRpskaolEIlZwhcae0bYejbGJw1GrFQOZwzFT48LxmynjVjdnbyIP3Zyee+t2k4e2BYDsMT0AIlZuIEU7sjYAMfedcYYIcjeAkDwpVimyM8tMJ9U07iSEhxZiDcQwcQagowQI12YtXQo6EJIPtVVBakVHX9+YbV/fNb/Wid7WXgLU2Fqyc8tVP7/eXr/r8+5Bb7u0yPB4bbD7CpXKEFPmFu27fPnK/PyKSoPBELnyVaeiOJWGh8ldkWi59zIfd6/t7k7p1MHIDPRYdaH5bIYeFmHwMHCsEOnIRGxjm0tJKIOPdrd+x7n9u0j7D63Yh/o8+4lk4+FffgjobwlbvK73+nUwQ3e8ZaDs7Hd15XnaPONHly4d6BxpKu4nQEKhBCWXZqSaTihvxMJLzSmScbFYqBuXzMzcHIeFZqbnKhfSYbXMDAguIkjYGFTYsYITKrVLhZR2In/0m7xcRcrgSkvLrnClUiTyj4ZuOyd1gdCqJEaUh0JhIZYQVQjkFwBKji/vAiB6bcIDubkV4ohEyxTZ6dmvFgDQGAD1tm5CrxRVAviMXSvpK9mJ6RHgTJJOnDvho8rQ73l15Se1cfF58WWyUCm+dERFZBGyTmU8VxWv4ltxbFS6BEFkvuv27a5ey528tqenywoDT7UN3F9OIGAhJv/QQC9bQMBwxSdGSEE4Cflb8Xhs9vtw64kTWy9gwSJ1dbLxr3++OXKgfW3auhvXb1BLDICOuzTnHNXlQca6ujpd3aWqA0IFOBAgo2ZoJCVFmDIihqW0mm5j+Qj9aXxqfJYjHh9HhFs2N1PKpiAitGAexNbDkNlD2MlAUhDwBxu2ySQSUf6VW3YpHdCHxPskfzSfmPaKUpmaGktKxcCrcCYRYeWAIWRPgEgEyCCPj89xptdmPKzBIFxCzi5ehtUl1y6PXevr6+3d2doLQPW9g5cvj42NIe4BG9EZe2LrmdOnv4iXd1RGFe3yi5OXfxcK10vt1lcSn8jVfFR62HnyZB6c/9272zGNzTcYHPCNn/ePSnznQReeKk5PPPg8yj4cwsJLICh0hYFdwJQLvA0TJaqJiWMlvZuj2vqTfI4l133/55GDX4+2W9ddvxJFBko7/311j1Fn7KmGtFotKF2q+oijVUjEYglNC6o1ih6FUJ0iVssFnLbZcQ1nfFwotJLM3oSZxmenfSrnbj1tykFmPlQk4KEPx/2UsnWXDFMFhBsvr1ASdTjRQRORHJS+7u75i6JExEfkT3K9LX+GFTVzlazgsHlAbI2NQYDSY+RyeXwNvcB7rAqZLg+8Awfbw80yF70PAF22vVbSOvEaglw9MtHly5efGGN1eXKypBELrNDpM4aCZZExZ+pVvIzvymjJNjUi0hBZGRlHUwYrbkJcvDyBJgUZ273STzUvfXfXC6uj8mNWnHnpnQdzVHEFKr0AGHlWhAJK4KuwtNvRERcX19HRURBfkJycvNpQVBRZMug9OOjn14g4t/35L/DRD48cPFhd1W4tv3Gla926xdafB2wz68cfAwIOHjnS/eKnAdWUg9T4LopqraYuRWzslGi6+OIRWh4SjkuEYgS4lJ7Z2Yy4qdnpylu33jIhWgBofVJOLpZk1q+vSVWKwCkx0TMdicDExo654XTfg5F/H/MVRCC+sXIlQmZIXowYVCws5pzIlE4MoXlAuQQovoyQmeXmCTGr4tkypprUj5XYgse1na2b/HonkIImjh27bNbYli9O39ahosjIM/VxeXV1oeT62PxIQ7Ict51L826+SsWPj0dHSv7g69nBe2t3Ne7e9cmhM2e++OKddwriOkDGhrOIw9ZwXCrmVp4+d1v1C3WItHs3c4Rqqg9Wd1cF1l6/cvSuxT9//eePKV1duTldOmOKJiDgR1bfvI31fziI6myNmhKS1qjQ6LhctUQsseQIOSkzszPN41XNzW0FPrNzt97aw3JhEM1PVJOw9FyRH+GulCamOyPEoBwlPFJa+ZpXMAYA+TotV/kqAciMiD3dIS+CxR5ZcIksJZZQenqOXB6Hjnk2HDQve+aJgLJxpItQPapsctCW1pJeAGpsfVKAOfExkk/fIO4vdIaEyqp+x6Ezja2VeqFNdChWUx/Lr0zmERsaHIGqoGi1wVAUGRWx4UOAYfUF9KCXPkFFq6kcy0XsOgNVB/zVJ060tp6A/Pw+9PNb47dmofAK7bc1GGs+zEEwK157fNP1K3lrP/jmzz8Phu93cnps+atY5jqZ0nT+gw/efvufT7cpkIHECrILVQs0YREqUoTiOgWtPoyLubNtbRfaRi70D9sum5p+cvNmhg/LBoMB5Ovg75X+1Bt0uxBd3EAH1qH6GvI1KRjCccVKpzgHBhDtZgW5YhCuhZjuFPsa5vTydC9aHS9XyePdGDZskyEdK6kMGgRVklR/rWQMgGwHezcRoNbBjoLNy45FPrZ8s+1OWoTYMgjh7O2TbKgs3YWl5E9eUOnsokXK5fmVcpvbfSurSizgV8TboJHcCC7zoqlUjN7KKoHHg4V4CTZ8Ltsd5R977bnHH3/8NWwQLnDGEcJbdMb/Kdizp9TJdX13T091f3Pz2JXJeOSjb94bKdd1dnWdPXn2bFmOof7Mg882fPDntgAxlyumIoHDZes5hVoszEAXQC4RcDLKBeKPZm+2DUv6Z2KftK2cXtDHZnZi5GNhytSYZDNLzqGAw+R8X+pqLRAQrYhwkqM/yNAR0SaitcQgM6h5Y5FCvWi7TSoR8ko0vQhycF+ufBfVg0k0RwUhiOUjjQMgKgoGJ3ZOUBY61lHwxLL8muay2L5Ng6i1od7Gvas74pCNy8vLjhramodC3gwVBT/tFMkjMNTh0NJZt6x07wsdqBL0K3ccYox3mvTwU5FUDtAiA0eQAEqIQgTIxsfJC4szb3m/9X85ma+8bQui0s/WGY3VJ+2zr6ET+eOPB5+IR/Gm7WzLaTs7ckQzXOi46sIPP/64LUAjFEvEGgKEXARA5CedQqLWc8ZpVslV1CnGUSZMl1JQAyCzgeY1hjKb7YAQHLNz/qPj3KOiqqIwPsBEYBEDJQEi6ggYyksHZ5ACRZJJScQXIiJYEgwFCpJQKSjPEAoiIXsAKYHaFBhEyVSjC0tFJUNlhQiCVJqByrLHarXqj759zr2XmR4f5557Z7AW8ptvn33O2UfkZFxzqBNI+YTl+0VMRRbHtpPNtUiQQImbCJ0ZINJcfgnbJztfZ8qk7+zEXBCaR2Nf6W8XftQToOzi7C3I44Lv3g3uaSvBVB4J3TtfvvMlZQgfwlwbKysfUmm0KduuXRya9eiMpqIz0TT42O5Oj4izJkCJKeUFidZIxKrXT1uxuOSjJmXdynxofT72J0EEBQnWEBZKAAgBT7NsikcVbc9IPCrEJikQgNpR7dTdNdMt+7fRxL7Hkuzwf8OqUFJqRLru5GB8fGHAfoxBh7pZiEuieIvwJkfWneSV9X23f+q13XEPoPjGQu51wP/36j//ClSx/NrUQHCPBk0GNqiAkeBAxANo5qz3ngMRIh9ogY+Pd61fxIwFSBlmECT0ogRI7OtR4mQyMnE8vGN40GUCECF6PSooyG170HZSUNA7NEZ8+BT9KPrffhst1f/446/Zxb/2HMEIjYmQoA9JxwnQZxjP6TtMPf1bshetbSqqWGeN/ABVTnwDxdIOu/kJVLmS8sLaRS8jypF/nKAI+Ica5qjYsENVgiMbgzSzpjMoFRA6aAsu8QEd+jL9Ls9h47VrxsEQt0X96ri+vvqzBYmJESi4sbNIyhs27AnpzTk0Nnbple+wIQQ0bDEON0S3NV6p+z8+kGpsfG64dbBxZYKFwvqntEdmhv3+0NatF4S9BoiHN5IMH3RWq8TZ0LBDxgEf7zkEKGxOGLowQhTmXZcfNxU7/ovBRMKzGK/4a24jchN7MkNkbiPyCwTvBLkFEZ9nIXcmJ3f3Z5/dHrWvZG3Ruhcu9Ae3gQiTgIe9bLt7t+fw4cNUNfcOfuPECrSOr60pqijTJMilvWHyUopKGVgVWKsuuRtYt+vorphlq6NKnJxee2oT7LKJpqiWm3Kx6AsRITvVDs8yUJgYdKSRCBcTEI3mec7dHhUV5f6Ga9C+YO1jY4fq67dtS9ydkDhjWsE5Y8wcFHz2jX1wvbfdXy6sZLIdEuRzGIgOglDSyaPDQ3sMq5Ntq89FzJs7v/YPkMFqKV0TgIBIJVuNkVUQj2uY7kDeJN6DTxhd3h516ojFtOMPLOhwUYfXIiXzeCcFOzDijSQCeor4REUFbXcDHwYIiGY6uTqhuTI5O7u6OrkH7cQyy0eHD/e0wUScVNvG4guq0s5SrfLM2pIjR3qyKysxMvWX1fgWqZWqCA6I+KCRkwBLbhvnE1YUnahXarRx2f0vv7xvE7mHUgTk4I6WxJNmq7kqt0wAIhqSIk1eMXJlqhWeC502b97set8k96iXHzGe7D7/9mOkt8/lJWOr8tHVhu6+j3sHh3XgwawDD3kRIAsUPXl1f/XdgSRjYzspL3lZ3uOzlKotYKGBlCQO6EdVaVxCrgyeEcX4SHSw6OgtKMw7jAOqisCOP4iAiiT8h7gYLUHiCAVG9GWS4IlOmkeIGCEYiCQSciK5CoAmQfdBk6H7Jjk7bX995z6wagu+W1xcXHkhUKPRApNeq1SuK0GJVnBT0boq/KWoClEuF/Js1OzlptSdKapqUlY1ndk6qlSva3vn5dde22ltRzt1doygI1FkZQhx2u2ZiHDMQKJ/+DORElCVjeZ7ejq7v/aa8+T7XHHDj+8O47stNCw7+n71btR5pu9++/wgtn8sdV46BdWhsVFIscZfnqXAjKi+++IB3dFGttK5F6Waq8uVgaoJjar0CdjMQf0OZssyngGgF+BIeLw9pk2b5uHt4eEBPNQ8/BDiFngTIWo+uKjH4CTl4gSJc2J8TLwkpQ4CJB7kuIcg0UXQ806Sg4CI8BChB5keJj04mWBt3rl6zoqY5NrYlNLS0hRlTduRyBceUim39GTHJdjaARKEkd/6oUDNKOymfzL4wgXNQ1tVmhI2DJWAzyb8KZ7wUWdBC6ad5VOeqtgSGTkx8zFR5JORkC8clDx9j7OTu9OkBx90dnLigZmLfvYnnnji+Zl7BmcPpcstkGMDEOGxtKCETq5TZJ28Jj/f/WZqUlc7Cmmh+Y+77NoVHRtbXk50ylUalZ52ufFxsURhtYyCGkNDl7dkHj8PPw9B3lx4UtYCEBuQQOXf4qAkSMxE7OKQ0DgkMdDxYQge4sOQiIh7yJkkWQgSCUH3Mz18P54KC38oLLzPPShzKpZIfZHqtbX1YMG7v7hKg51HlBvkdmo1SGErg4M1lYEaZc1nTMePl27KBRCswBEax906C0uaCZXWTXm0DFOhSMAQ5St16GkIUqu8MxduxyfLGR8eisOuwCQIvsePO5NWr3rr5br0NZQgQASKlKVYczA1HYROJuka4Z9Ze/c+PgvV1itRbZ2Xl4866F0ro7VxyPAo8sL/AAQ2HA4usEEDC+Ch3m+CEV4p1RE+FO0YDIEJXuCNMOqYJCstZhWxyPbM8nCOSPIQBTnIxEJoQpQDH4HQZHNCIMMA8UacHiRShdiSf9g1aGfJR2trfDduzO4HLuR3bb5qjVavVbdtrdqHfIJEhL5MwSQVgOiQhp1tbpaFI2UJ2tjXpwpjEGCYKZK1LdkVZYGjtTMWFPEStzNnfGoWPTLvqdexlQg6hOfBN3J69wzmBARc7G3tbX3l+4P16angkqRT2Cr8s7CMYDzorzjfXb9md7uLy1wKcSs0sE4BVjy0Wqz8UW08lqsKYguqExM7ZUJUE83DsaBR50E9RTnqCFBVpw+NSBxFGISOS3wFQnyE4i7ihNCzeMfdxBGRiYQYJ45DUpjjeYKzGOQIERoRAgoQYV8EiV+CCrmvyFYk18wNZZjWPtnTQ6cjIKIjAPr5s+PvnElE+k1l2KUUVOKQaIOWqmBGWBll0+ZigHh8g4MCR0tX59ESal1dlfqFdazUGlVuJL85KPnKARdsa4cOdbRAvYODKNnvbW3NmT7FYDTK/Y3VJw9Y+B/oTlUcdHHZQQ4yPNeYzOemlLkh1DFUKaWxaWl+MnARNY0EKJLSqONuohsARdAfJxKMkLmIDgGayBjWmsyT0HFxF4GQFOJI5uMQtxAHBBEgEwvhkqLchIAFjOgtKQ5OmqeuLCaRn/qxBFFy5EvSy/sO+6g1NcePtLX1F29VqlI643JhHwDS1E2tqIjEaBMs6UmhO9zWRok9hiKlOjn5IE2isB+OqqBcVo2rxRZHXW1+2gvrHjfsmBkSsDQ8FIVDGSgg6kBhXUBoABQSGj4sVxiN6Tosb2Pd1P+si2Gvy/w9P0AxWpKKVK5R1inrxA07EQ7wILaZ4REJceFJqe5kPCeQFJnxQWPi45BQ8C9CmmCF0cjUQuYechctBEnDEGSeKpBEQIUmfCAx7rG33KKRvCJvDbxQCUwARVdxpXorPrBq32BfZIIqrBglWDvaWdJ4pCldELX5+D6ajZah8hOqq6sLfOFMkc+jWHRf7Yc9tRWzqxuntL8POiaylR6yEiIKDAAUHo7DIWJtezy0iq6Q3RaKrnQvnRf2vg+k+6ceNWC3kwDl5FGOreEuUnFULGmQ0ZgDeaB5TCMssBW/0JFEPLiUaYlhDCexoMtcPhIfEsIaVf+vRVjj/hGmteQhKcYJuTZkksvBQGhmhMwzBVEiD3Px9+IR5UDIcystoCjZLIPPMCo3ilqnjI5OjLO1uFdhrbB7wMIR+YImobNTFZewO3bXjEbaKXNxacRQzurIdrDCcZwLi9nWtTDvIFZ2OBlbuAidQIl8VWCY3to6PHj9WAYDxPGsil8aHz4zDymdzssrSWcXJ8e5K1363OmZi9yeeSZnqECbAix6fYoebBgoLhmngwsEgIcrn1Qrip7q6qKjo7XlcdGxTOVoTAWStCkTqq5Gq04kRXDFMUVwcHCQiYUEE0mEeL5qmsvdN0kahUQTcUzMLf+GVMh70sOPVhZXVlZeELSVVlJKE+Iwyy1XKhNz5axSSWFHZ1Ackdaq8pV67OJtO7fek9f3s2JYT76SjPVcTLcXzViQv2LD+6JzcEF4QIPoDFiswa13dur3RiOVply8eLGrqyvPePZaV/vwwiFaNk2/9v33XraJCp3O3//9xpjnVgW0Lswph38EqSBYSEvAtDIhXYN5CE8s2/uln5qVANMC+Zo1SZBOl5pORdf1ULeJvtq//60P3nzzzQ8++OCtTz7Z/9VXly5d+np8/BR2SMZOYT3q0HksGp5/Gwu+9S+iJH8DsxAhokxOBBQEQJBZoiB5CDLz0D/ybclG6AolAxVKhNwrMABBQrcRXYqctvNIwlFNOVUe0Ktc7YKpaWkU2tRlHkVMZzyKwuge5usbtgX3Lb5NFar1U86BBZDY2XJxUHiPCEUbWlsH6w3D7V1djVwxR/N27WpsN7wxXK/LMg4NtnakY47slZUasRJn2VzW7wgJYAbCh1yv12q5gdCDFgDxbI0BUm97cWR8fPz20+0fnz5x5cqJq1dampsbjnU0Dwy0HKOHjgauZrxzQ1DDkk+X4J0bN66eOH3621snAk7cvNkbevP2nfjrIyPvZuwfOxXe0td3MeetvhftNiD8Id6xPEGKcVGwkIjoWRNAAqInnJ+QCBEkhkdK6KjjX6YPEAIctclNG6FsSdhL0gt4LNEIEGao2Lmjdzq1mU+pabHUdCVB7IRcrqamYvQ5ABLj2yYRERr4CIBm57VTWf/gnj3Y0qdTFTuGhmYPBSw9NkS+MqbSNFpxoHX2DrcnpsxZFhIaUF6ugZTUVEwMll4lE9lA6KtfHPn2m9u3u3+8dPXElI6BK9envNLQENCxvHkgxL6hoSPEHkWtaOABQFeukgAI74DYjatXT5++devd+Cs377Rm3Bz/ZKkIKOQYAGW81edlSxk48SFCJnMhEuNjTojwiOOQlMwJiCQbUS8FOfaITgx0iHFriwmQhKi/nwDxFQQOyToXi+cJClr06Sx4HYCEPNtkBgQ1+YbR9LWmJjKyYnTDTApxjIokhouFuLpZOa1DZ7sagQjCLj4gkdpzrtdn2drSCjf7CYyDOUvxIZq+fkdoaAgNP1o9mJTqpUU5CA4CF4LD77tfHL/17cj4qW2nTp8IcWhubgm9smQJADU0B4BNR6j9ci4QEdWwxN7efjk7cMFqkpevsl9yLMOqY/nyVRk4qGuD717fj/35T97uU2SBj2mM46k2hTnRQZAEiCQhAh+zKavoIybwMXMT7kyUKKwtJkKmNlLRBtoEoNwEfc8XwZ3ErLQ8cx7wQEXi9AdkpE5YWShTeXvmZVkzSWh4lsABzZ3Z2p43OGjAqXY6lcuEZZ3hjoNySfgXDGx19cbwl+6ftXpu6NIQDDoaJNhMsA+EYIdwJ+P5GnrWRbw4Nj4ycvvO5V9uIcYNNA+0tCxpbsGJ5+aB6y3LM+Jt4uPpeKyNDLKSWVmxm3THA102VvQtocNpzYyWVw6NXdr/NQEiBxEfshDx4RYSFrYnTOTOZbIqxwciyJSQcCNxRJIm5kY1xZzQRJDTCA7ii90Wubm2gU9eqOyU4xBp+YbFAMQEPhwITYvQQShTOIxHjEHTVwIQ94xJhONZHAC1Dg3SmYpwOoAd/sYbOGsz3W3hrJCO3uH2s9vSk+wU/klIEJLW6FKvveTmMn8hJko0MU0phcAFrCTJ0syU0DdyaXzkzquXGaEbKAIfWN5wDNZA5+Aw0LBKZoWGXzr7/cvugdCRcIqIetzR8IyOvwVmNrKvT3196evHFFl8kQGAIAIk8ZGmQyS+5OPE13z+00MSIwmRuaS0DoAqBQtNAEKMYWhwB6aEXOvOOEcLKh/R1C4IqyAWkTy2/aNkhM1TYSFVzNwd55LE9FqKcWKIq318qBcnYMPBh4SUn39WVt0PXgEhkHFNan09Fr1Pfrc01GX+6oUU4oiLKKJVSjVf+hQAUhMZ4bap79Sp8ZHxO3cuXwagqzcGBhqWLP+UxTAHG5tfrsbLHADIwd7Gxh6XDHgkLFJPdxK/46VMZn+or/vgY30KHa2BT4UoxomJNokDgmAjKcZJiDggNG4hMVkQIJm7iXXxE4geBiDBQgxRPwckbelZOmLBxzYCS8d4J3qDZ9GWSAaDERIdxPlQY3bSdMblvTbvrE4wDqMjhjio1qXdMDTYmxMQTnB4vsJENcKELKf+fUPAUn5S24DdBjgopEBbis0FwoI2wQoOUgtwyijMWfeN4PTl+Oef37wMPohxDWyIcXBAw3Xjl1s3HYBG5kB0bLh1xJ7wSA8CIs7HqvlQ3yznAwCEaSwsBEASIYZHyhO4iTZLMU7gI2XbaCIfyUVmkyPJUmyySoCaKis5IiHVFgFJp3KxK4bJRVZihKNlufFoEXzDqcBFuEREDBMeqVcmrqw+O3zQkGWaxJEEQO15tUy78pOf24tT81NC3sC6gigU2wMUP1T5TM6yuXMJUADSAxMH6UtJlMcBkFpNhNBB8r7PGy4j91p++XLGKiuIQ8AvmcWqhqs3blB84w2ysZLxYCcpXsbCX4uNg5WMXGfDRqVDffMnnecOEoOcOSBCZLYqx/lAZtMhaRzikuiYS0IETa5hfExTOY0cEgwkt3TEvqUdBu15M9ZHVKXqKigNkBJryUciK2ajh7TbVqxsXOeXy/Bgb43ugo8IUHJqAdZSY6OjUXC4YoWf37RkqHGZYcfsPa1YpYsXz5biaNzexz0Xzp9NIU4rMKHgRqyQKlAnq1JDVZibpaFPsxh7b+D2yMh1q8uXwQSDjAxikYweYRyENyvW2X9qQ3EPcMJhLXwDPgOuVa4yIFll1UHMABEgAShjbOyrp/v6FKm0u8csZJommFtoswDoWbMYJyYKgMTFARGj/6Ek5N+Tm8BHWIgTHeQoJzLEBw8P4ITgA3jGeQgPzblzFZEYa9Ag3LhpcBPjXBt0uFLvGJu4ePPhOCJDTSTELGSbX25XXhAdS6ssWqocpn9aB6SSY2LWx5AaUT9GITBkacaevbM9M9kYhBQBWFj+hoscRKjgoCqOh+vvxs49qsqsDOPnHAhh6ugRZypG1mhesHDiYN66gFQaEuUIckRkDMiIi0oZMDlkyCVzvKQzJDKCqJAXRJuaUscxG1NCdORik7Mm0mxSVzOsFF3F6v9+77u/7zsHu6ye891ALeU3z7vfvfe791fnufew++HtYQlxkeNJ1/QyXq0ixhgv+QGPfBtGr2nw4xeQyeK4rocSv8xvgYz+VvlzDCv0/OEP4WUAgpDGOHvE9GWrDeIIAfQNIaSjcjohZrdDIoePzQg6jp34BLGt+siqVR98+kUEHVFoFmdyBAAVby2QopHchvlfPJ6X+i3YOAIUH2WjX0KH0q9Xd80euzT+pR/8Yz5YbNkDpiQJ63aMzZm/H8nwGINiHKoMPKUTc0WM85n57rMplLyRxYmD1DucIlCJuLmAo3zmNMzLKU4dc3v4+sOHDw4NDg6OG4czlpyXKx0fyRNe5yrNkYpEYYlew9aPF/NAjHvY+CWAwjUsRucuIPktAEI4qBZAVowzI3KjE22LENJUe3SUc/jY8+COi4JXbk1MmMnDQj7iow8+/RaArBjHYYc4+ABIHJRT7JGn4v2bf/V4Xuqur372d6DgRPaduQbK5n95DEAU72351uyCjEwmQVMLIoI5toEklSjP74jOmNLQ2tqAWve37p/SKrAyMwEGIoCZSbmrNTXZs2SUQQFJXIOHBUb6q9pr3epSPM+31udXR3vQbfRweGhocIQfOJ8l45HhwV0PYISJuOmD6f+Yq35XHol9kuHxm6WRGnf79s699+4BSLIE5ORxEBJAHHaMU0JWqj06yDmIjGD0iJoE00IYAYoDC01Y1WQchIccC+1wSkrkIaL+opgpev/Tnz8yLa846CAT6rhwUuPFA0/HXn311WOfnN34RmrQOSrbQDgoISd6ToOst6d9Rw1TpjH8rIJUK6doR0V+Rn1NrADKiidJ+MSKeXOmSIA7jom012rkWtewg2VOsrhRN9iBDw66Mzh4KExo2D93l7RI418ZF4mAoPAUDt9XLJCQZ9nIztzs5MIlgL58++GGDzy8XZWPgbAQfKwY5yTajoPAI4ReWvPSUy8FLWSnCRwqZ2SOm0pufK/pg01PNDXBjg+AEA4SPEj5cBhAeiBPRN5Fj3bt6WMy0RAEhH0ocVBt2bfvmWf2Ed9e4m/10gt73r9yPl9wOOPYegsCcjfMW6HS/UQWt2ZTVMDcRdaijUup9tCRtfoxEa07ihiomx6vScK8ecw6bX76iOiTKxoeB5A2RWzoBxqFI4x8D957b2h4569+NHguDBoil62wm+99Se5kAi6AAE8CnrRTrm9iFqd/yqlZhRliEL+Nfx1ALgXElBGELAtJI7TFWOgFGiGbEAeA1qx5SaKc4nEsFMqHqwOJjwFk6Ql50tmJtC8Q4pSP46HZwsVHHS6MfOEVV4sBhPghsH+aFJ7AZbReeIElJ8LpGRl4f2H5lN5TqcrFELIFYgsQDhIloOfn1Ylv8A95QszSpdOYdxNltCbsT5dl7wZQK00NYv8RJtFBdWSPstrskoogW+GeqqH3rg0NH3oKQJHj18tP38mhscktoIiWOd/U/BvJ1emYWuIZqZ3OPxweHBx+WNUo1SU6KW5byAYkEj5WK4ScEbnRndXQGBd6M/YJFVYi2qVtdtog6OhlPlgo9ExKqvK54wK9ORfdgQAVhvxHmtp7+OnfoV9+9pc0OwzsrDjCDTJbXn5ZEgQVVa+P51Y0eh0uoXwMIM+cb+uWcAnWboqtCEJy1iVMMmIWoXUugAhxAoiaUkPI1go5V0xztZu9kXB8qbvM/4dhttkaGjx0aPACgUlbf4VjzvciDRKinabgJv0O9lWVjjIyDRTf1NN1fpitbQwg+Ig008ZC/NPR97//AiHkGUOIEGdiXGiIczLt4KCc3kLgyPUxitKa5ORAC5sWpm3WNgjByEBaTCUUk3QtHUkBT1xSycWLblkpX+If40ltLD8iy5Rt5Xg3fV5SBJUOxaHln/3K47nhjc6MHYeDCI2NSMiI/nbDimkYaMW0aUAC0H4KTIyP1q3DQdOUz7TWGZU0QsZB+4WQIlJMQkrvLg8pDNNyvvaqvv629qQhhuGGBlEXgBi7jnQSaODcPP+TV8xmq0sAo3gMG+4WJO58bFch9d7PHgyLqsq/gGbSDqmFDCAImRinfIzIEkAUJAQg5Fgo2GF1LrD5YNpjfJqED1fFwwkgbYIcQhrifDiooz8pyR2XVH5xq0dLs4n1xY0le7TXY7TDG547ja9FYDIiSfjK+ztqeuHxnz1kAQIOjJA4aZ2Zn5aqkqUJ1hx2wqS6WZWxsbRB0xMTN+QXkxjshxGaFwLKlZQUqKoqPdDXdrq5ubm95YHQGRkcGbkRFlk5Di2RvqhQ4kNi/SV6p5J3f2AJaYJI+zzjuNN3lYQPjkiSBo2O/Dq6OTR08Nzwg6p6HOQU/nwevbply9dtQvtC82zwrKE9VkCj8zgnkwvVY+qdx+Ri+QdGqsp1tLT8298SPooIB/m8NEL9PextxxorBn0iqmU/sjFjijdt+iQsRFTw7I8aG7GfR6svBCGhxPUr77dWnnGgWFmco6iEDA+Anp+Gg/BQglXSMclc+dKuAUlYujFtMmvmBNAvCoyqdTp+NrXAwoqo5+po7+8+zbx18wnU0qF8um7dv38jbFma9nfsSxgYwlyvyFcACvuS6DU50E/05Atb+qU+/ES+f3Pk0MJxQ0MKyFTVkSYshw+AXjaAXgDQKEKEuCAfjXHGQ6pH8OAfpeNIAS3g0zS1M5f6t+rqPLWRAcSmLLRC7R09HW5/kp81kFF/O56bVxDt2fqx7zDJYDlof1S4d4eO8oRIf2nz/IzvbALFo5N2BfpVgQCaZ5XaYCS5AUQPCCHbQtmzKidS7aCAqgtGKUpZ5W897mprwzvQaW4GU1X/jZs3Ru6f/GvX/ZthkQsFhpnWUQ+NOx/mek3TNwBB5jVH4hw6Spp6u+SMFJnRHrHSKyOX1782MlK1Q6u8lQ+AlgPoVWmDoIP22RZSA2GhRwihEAcFGfHFKDqJ9h08AKpXPtUVU2iKvmY5SJeCJCXhIH9LIePIeRf/+f7xiwXRx9etZJ5OoXxrflR41A5qTB2ZlZFy2358x6KrTicoyAdAcjyfiYOIb/IBjrY5VlEbT7aIdCsF0HQDiCHbvGokLnIYIVezSso9AOQ/ffNmF7PW529c/klY5HqJYmEqTZnB4orkEUDjwjTEMY4jIIAmN06Xnbtpw2UeSP9+xtwF/7tVOUQ4E+PUQQpICb0cAghZDkIKyOjRRM7JFfjSJlSpp8G0AActWCCAcuFTPedFbYxsB/kL29v6A25WVAUCFYGord/94T/f37p1SsMu9N1du+bn+XyFGSu2yzIuTi5Gm2UJV8PFjLmdZZZK2Zg0SIiPASSx7U3BE8JES9yUldHajUFAWveUhxSTktLDJXBOH21DPPgO3aDwg3KRG7eWuNRBZqxAATFE6nJpwANQ5BLoyIdcwBJkjPiWcpNfCeOJJOHy5WuX74/4MjZLqSMGQnuWaxukgMRAZHGPNELIDCUYDzlj2hyPAHLwVHIAhxNCEuPSJloOysVAIEKaxcUFCvtOd3e446oCLUlVSf6xx8nevro1I0MgPL8DPP6S/ZSPOIsheWIfS/M4Py/zcCllTrVs+3P00l/QpUtHyw+w8ERHt9dlehpwkFU9TYgDiXzAY0ufs9MFUGzKom0AqnWK0wwnm5WrTQWmZuqm3OcuXLjQdevChRs3xrkiX5cc2wCyCLnClpg2CFTIGuS2AbkUkn4vNItDP7t27fI7gw89GZQvaYg7AqA9AghCTpAbbSAIkSUEk4TRs982Ii4OH/AgRaSEVBNjO8uLikrzMr/+9R8/83lFVM9qqkBZYdvp7n7aILpDXMLHzv/5z3+4OGPH9nU5+RU+b0Sgop4yUtTaMEerPYOFf/nVUZ2NJaWlZRA60Achdn80ulvujS45WpfpqaMNUgFJeMgJIr07XloZM2vqxG3TZ80QQPlCSCGByeGUm2faIOioPBe6uvAP583xYewZiSBijZCSklmFCMQ3B4BBwal4nO8pPSfPPni95yEd1TGZFJZ9QgF9BgctX34MCyEc5FgIPMZBoxohpKPaqlBATnirdJQsjCrhkyafibHFk5/4cG1h/IQJH/rQpzXK1WOg0vbC083d130CKI7T7xm7/ZcXi/n5FHjZoKUiyuetrsAPEbqRDyUe4bKhwhifB0V7/CVSJ2g25WHLvjfuCqE7nLw2wf/7pZnuOulwrktgkRV8RPZyxdBgF2MAbZylgPJZKG5DslnxcVlw9urp7rrx55uIapFxr42TQWxrBNREOQY+TbsEIFcQzqMKsuHQYYeTLWyQcu+eOxMHWR6SGEenwjjohe9rG6QGcvCMinHwgZA9IKeA9HTim7PHs00p2XJQ/aKPPJFTmpv2BIQmfFxCXL0vruJAaSn/XXYHAkktNEK+cDQWKD6v3882yn76SXTfo8cge95I+GiVI6WcJahMIxx03ti7F0B3Tt5Bvji3Alo3TYBMMuXTgIKPSDFptOPInhuzUTZom8XeTgt+0Yjya4GERgESLnuRpnHuruy5339FFeZiW1wrQdAPipR966QSYUnkaCKENzWNIweRGZS78eAPf3g4dN/duhmBR8YExUIyNhzSCIFICSkjxaOE0OjxHifR5h7EI3TMRoHQ4eCSlpw4OXvChMN5uZ1NT1Cq8KEjEuJme9mSpbBs5+rVJ1oCSYFAlcfDJlRer0mbA1Gy8M7rcfaehp2m5UpL+RSWmBboAHywD86Bzd0DZe2XStpL2/9Sd9W9bpLg4JgEEdbG6Vfqo6BWLsJBAEoBUOKGo+UioUTJdIiVXMIGOJJknz4d13Uu/tPQoRTBRYgLybG1GQIQJyReDwtygMuo4KauMb+2zB7Fu3Dv9iDNkLtus0oB7VFAW4KAwLMvJMs2hGwHIdYXWxFOFZoePAkc6KgUUSWQAJQMoNan51EUfmoCBlofq+Nys/0lA+2lpSd27mxOCpTEEZbCvRF+0+uEBHJ7w4O7t+usBEu19SsfeOADHss/mIdN1lH7GBQeQfFJXU30UgDJYnkhslI2aROt5BazVnYE00v6ovRZUxnNTol/m5GES0TLTnYxU1DGTvgJQKQGkKG+mkjXFui69SMqReDzCoC+HBmmGm8m3qQNWkZOBhE6RKFOAZBiCnXRsg8IJqGjgIbfeefaZQ+A3iTEBQEd056q4YOFiHE2IQ74hBrImXSw+RipfYTOZI7JXC1G0BFEsY20tampsRho2cLndWR7caD9AIXmfSdOdLPwzQcCm4w1iScGgo8RgIzET4V9hfChTL12QPhofgAeedlH4RjUvveSz2MBEkSTxD1sz6aL6GemB7eo5GnRDAAxkkChAoBIBFFnZ+clMFleErm6u9tOE+NW70VtLZcvUAwnAhCrj6wpukgOyOCe9XyPBOE8NtKPI2fklCM0w0O0YOfuDQ0aQCbGHUHKx4Q4ACEiHEOliscgsrtBTld19HCcGoiv0iqhAxtHwmjD1GQgcYnN19cdPEFB/bKpH//+CgjVl9YeKG1vLzwwwJaiceIfv0/ZOHyiNMAhqJATKB2PHJmNSSUtLfxhVhGIfxw+fW6l2XfyZMBTdzV8aQJkkBhpZsbamCxZKrwWLGzIJZqLAJSigGIFEBtjIIEEonJFpEZy4Z3VSAH1tXQB6KYBBIwlOhBn4pup4Vm2TIPeeZsANPRxFBz7ovcwAXSIseyhIQXkzErtCUkSTD/VzuJEW156JIlDNiDlY1/gAyB7S3uq1NG2WN0EWrUt9eKOrblXpX5kFeen8FB92cAAP+NwAeBn6WOc4gkCYiAVQEIIMKOV3y7jD7LKo8/k1/C5cBDduauE2u7ciQOQGwcZQBD6ROumpUVFZ6SsJ2XqRqqBBQ6aYQGa/vYGAN29y3oV1dWrvb1YCUb1UHI5dNBAC1k25fAkcTddribSbKQ9H25SA+JaRbBD520EysSxTNA0nI7oqp64PfTw9kjPmLo3/5ODjIUEz2+dFOErBg9HKJ9giHMg0f7AJ3abKp4X4cjOhCnCaKIaaVv9N3762dx3J1Ak+NyEHyxLoxGaXXoABw1IM4NFZBdR2JiDk0uE834XA0iShKoqMj7Ugjog1M/KmxOrDxn/dB08uFeZNp/c6wFQNA6SPXQ0Y0sAz1I5aYtiDsfMhZDg4a8Zk0KaTYTbwMbbsozoLjpzd9MmIEEJdXbWu1bf5WMhGug4d5Jx0vtSsxjmSjPjpHaliDroNTPFeh4eDqKgcWzZ5rHmKQD0cHhkaKjHs+7NXQCCj50j2A4aPRSH3godSeAYncUFEX30ycqn2OuUQCGbf2+cyz9ft+2eJUsKhND0bc8cy8/7xcIvr097btWHlv1gszhIAcXpND92gY/KjXSqiFyAU2OcQoqDjs0G9fT09/dfp/uogC5cED798EF7T3aP8dRlunk10koQTZrGZ2ndOp1OzdZXJ8XIptV8IISDpB8UC6AFyaz0Mmu9zvDCK8Q70BQSDhKZNqi9o+uv50dGtHCekR4cZOCodNjtS1qnQIgLknAMpHqUVJhWch8aHh66PzLoSWCzNSvP3vMfQpzYyGqAcj9mAH2YwxJ4HAc5GcJHn9Sd+c123zN2xyCqmbJ2n52Bi7a9zZz/2Tpejsd7gFadTZaXZHzsrRd3tLOVWOkAjYmPyTCvydv4oLFjWcjlHSPUpCMUUSE5gqTWikfgQAd1g4c0/dA5hl6IOgcvHGrXoLj6ZNkYcRCARGZDg3Xz6qRSpIhNLYvWAkhDnDg9JqUSQJM34KANp949pTp8xYbEASMcJB81ELMNXeduMF+HhxjMZkKVj6ZwqjAOnuUKoP9XCuj07Qe3h++f8yRMk6GukCROh7PRvIZpX3z15UmZNZOe/dzXnwFQY9EjWTZyQlyQEAbCP0qH3dhlf3XdGxpMcwl0KdhqVmZ+de5VXpDx9p9fZ7Vo2gobUHthqV8NBB0nf0Mer9n4KI4JvLxytZA7TvjYdFhD2H36BHjOoQtioAsXVhMwFdBqtwMIDzFkjYhzCVSYyp67RTExuw0g+CyKiQfQtslTkwlx76pOWZTAJJyQ626IgwI9g4M7HwyJh26aGe9xDhqpJ+UQK/1vQJgmVJqmX7937/btBwDapQ4iyD3ioClUgh2nfDw1tTa/Mad15sdaiz5Nkh0Mb9Bx+AQFIJrZ76ToO0B3p/NCAHsHfH4OZ8/OYOHvDF6jV1b7xsKFp17/8qr1C998SwEN0BEqDLg1wAUrfMyqhyhaJRWA8k1D5DZ8wAOcbqLbztWHDql9EHzUQCj8Th+USBJsQEQ4RDXpzJlrY9DaLIePhrhKLD55IoMeyX9GZ99996xQ4gASApQ6CDzqoKrrg5QsPhgaEkCRTXaAs0EBiBoFPkwMmc7raCjcHv1CTwDdZne7B4c86wwgi08IoM/PNkuc0dbifNRY3lmTnfXjNRSWihwuowCxH9aH17DjKRs1ngUPBdC6NNuEud3UbW5kL9RctjaqiGA7CTHQs48LoAESbADJLlh2AxTNqZLX2fFyN44qt6e6FkCIINfSYfDQLZHwBh/wGD7oXIu2Qe69HS2eYIizx3VmZkMr3aziMm2kCXHp8fif1ySwzXrynxBLJpXTWcUkWrvW5SRxDCjEPRh5Z3BoSGLcK1hmA2REpkBRQ50CU0B27u3Sp9Bk3PoGp0r+5ABlpcPXdoav2759RZCQAYRogY7oqmb7MrueSlhDae3ZlInqnKee+fSaDzMKpwVwFh8M9Bx8WMqWlS4LdTdR/5ytVbVZMYS63Xhobl5JWW2h/+i7GGhVypTH//nWi4t9he04yMcojo8WKCgdEWVxr79KFeepyMcQKnZdFkL9Bg8GEv/YItTRmsnvTGppt0JctgJC4FmJxEBZa8nhbAdhekLcVHlpjwwbvq3609sKSjUDmDHpLoluNiDfveHL10buv9N1a+Q8P+JEXfA4Tj9SPgKgk4JLauctRQqLSAUkF5V5sB2kaNv/UPWH4cs7w+dtp4SCPM4AYno/OBa3Irj4gvW0LPDP0VX+5Y1QyuQtntPXkL59eM1zbNG45snHGPlkhzK+wRv+5u5Ga7OL5MWsZLPYSEKdWkn+iXmltSVlB+LexkCVjxtAntKBgRKfpM9xAArGN8zDaHR4nMARQr68ThtQS0dLoKoDPoJH/TNKh0w/VWxkA8omxDHQwyF4lE/W2qy5WU6IcwAl0wYl6jtef6FSUiglhV6Dy/EP8vSwVJsAd+vWIICWNeEWS3DCPhLikJnGRuaqiELk8ApmCR20QUP3m73ztlPqJV3Vp/egkJEEMrh57DXBYcvajgGBSTn1Xi3aveYx/MNOYGueo1f63EepXpy6bSP/nR0+s+lM0dKaTUsl+ykqspa/E1UARDrQXuo/iYGe/DTVd0+/uLg6vzTOpwlCqIEkxdZuK+HNKJwkQQiNcbuTelBLUsf1Ns3eABR0EHx2jnEUBKSaBByzF782QfBxkjgApU2lL4CB9EXWRpBSVgZTisumg9rGtL03OPSAtXX3HwAosokhHmQwAYjDyrc15Q4SsU8l5QywOoqMfEjt/NC1014qYiEks3ZYKBSQIRRUxvwc+Zhi8xyuYOrsLG/MyZoo+0nZO/yxp/OaSoblKifPSJeXH9WwmRdF6XWAKjqTrTlDekl7CatB/UfXL/z0y0+yzO3rb83OT/UHwjXAFVZIOY9u6Ia40wPCPRYib3VvhQLipbDwuc462/bfNx+0MgQIKSMx0GlPCKDWTABNWlqku4hlwwc8AMrKwtJOEyRKB9BEA0j1OkciqJAhhZtcFp7mtu6+vvAuWrxbl2+x48ESUoFEAtvrnIiFkOqgn2is40lwjEYU+h0QjUq0h+89HHnn2umIBuaNDaGnHUIAUgsZQlMsZUAHLHzA40hYEe92T37ssSfsZPvJpyYSxtOaPvjYk7GzYrLrWHYgazxwU5EgWlvaHtDdOBbGbv4+C7omfO6t+cWpFYEx0tmhBKda5uW8loOIcAG33wEUtXVumT1i2tFzXYYtrw+Un6J/qoQMI+Gzui0pSMhdByDo0BwWZS/lasIbsnK43YbPRhxkAMGHJccLvrzAEk8KKRFILsHD3C91cW0DcV2XL1zuuszmB12AOJ9sihQtTJHiGzEQhwlxXBw45svQCfJQQpcZy752rT9qzpwGKWidxpC2GexZjoUsB7GN/AqzY6cc85WHOVGmXrhy6e3N7GW3nPQZ8ROfehJSOIoF+R9kTI4b+zGmxWZtymytqanDRiAqa6ewl80O/7ziWRaFLsxi48X8sTrP4/NFy06lMgmkL/TWRd8VY+KQQRSxNcsCFO5JkgxOxv3b2miEHESHDnGs3nmaFdIy9KB7ywsgASPZAW/zoTmUAwOtNRkCEj4hgFh1x6JjgYQgpQ/GR67mNt3Ppa+DSuXALSMsFPYzYRF0iFm1ZdZsIWf9ia3xHEbj9GOPQCgxVh/TuN3qidoPIUw0bdqbTKsqIbGQ8AEQetO8V0IAKRzbPWDhcHT1as2Zqx6Pt7S282r6xtjKDzJW7WwDM0E2R5guFtokgK7UEt/8MGqcmbh+1Zef5Z0q873KR1Jsj67j0nd4M+PAPocA8tl8ABRTagzE7+uAkNQHaJag7VBQEKKOmAhZJbPnGuLAI2lC0Updq4WF2JKJJHvGoo0zzu6eGwJosjhoVTJUIKSYeFJAaiMX1ukeaGEbHf5WSZdXX8BAUthjZchIIHBB44Ag2UIYSYNY6zyn3jmMGFFZ4sRF8Oi4EMa7P/ge6o7CG0JoHaUuMiukQY4YpwbSMbgj+iI32aOG/ADB6BE+V5H89E/xozPNQ0RFaWPvRlwk66khpVrL7zGAytRAfn/qRP7tK2fz3qhiD3LrLKkjeaW3bjUugJDfhLhn7RDnUUB4qNnwAZAi4qKAmq/3dLToeF1SwHYQbVB2ESlcukFEO8QwKSaaNZ1R3XiGdh1Aq87+Sd9WKZHOUCLIgQhOrv6WJCvi+jztXQeJbl3Xrikgex5VHaQuMRc1yRK5KjrkRDQdQUU8cDdfh1HgePnatVuEuAKamP2UySghikfUQjhIAe37xku/3bflu197ET5Wtq2IRBYf2v/MGi76wz8V4TG7l3Pkpk9Q9xg83/vBhxYCqE4BHS5UQAF/Cf/2ymc/wVZxufw5yRC87hBpK0Qsy8+vgA+pnAJaWRvwWI1QQIZ4pMhT+NiCjXx27qQN7+/pQFCyAWkjBKTsuQxCoZUAmmHHODpCKTHxiZUCaEHyleSFIvjIgaQVUhe5qqqEepx2s/peYd09YjpIqFgrVM1Fi63whVkQyYEAxYl4UFkveNXNexBWRO/cH7kmeqenQjZto8i/YR2E3oSQFMdZgPbM2b5v35zZP/3tZ+fogMLi+sU7EIhGO6hGxXDvlU2wCXfjjYqaD9vG+RAf8CQuOJwpIY58+0xAAaHEL0/QLYOfmnV4U2d+CekbVBxA0dE+rwDKnZJTAh+equKqF689mlRlz3x39PdDyAEkN33QZyn9vN7f0/MIoEmfINTVn8E/dJrZtmdjitMKoZjppNkyFLfp7WSwGPcIIO52uuDqgY4k+3JcuoFMSbUJUlJjzd3kcbKB0s0bRl2qy/9N76D797lcc6SA6iEkCzHUQ5MgZAHad+zF92fv2f5m3nL2e91MRRgrqfJlP4d6ENmQFI8GOHXQmfROLRbwl+6eoNHNRrQwOVkAaYhjwBE++hos35/SyNBBJAMRjBHFzjhc01lbKAmc+Ec2FxNA4fU5+RQgRMuSnILiZ3tZAWG1QlUAUg/ByBFsVBBiJzYDqLUXQEUKiG5ztvN8OH3jZJMmjO6oLjj17obkBbDhDMpC5BLrwEb1l4O8/lNr4AGje1px8rWKB7laDrsprT4/dEfwQFwGh0bkiV/SK2Tu24Cq+YHTuwFQA3MkCdNmPr3H8Nn17SPf3v7V948X83qSn86pTq32yjsXSpiqzi+uL68XTDm96iLwXN0kDoLQu41CKD9WNkVwNvP4EKkPgLLVQVcEEOaJ88HoKm/P+ih5tg4VpclExZrnnps+K0b8lEsJnAJye+vzS9nfmncLRrEl+tre2vIymQAXtybRDEmS4HCxzaRfqoUUUI0AAg1M6A9x8siecUVr5W1/WfAJdlQTxUFvv/GLDUoI6RYysEKSbOMg8Djt4GoinA0ERnCCCE//XTYscMFrtIDDGaKOimK1BB6StWbUjh3Zc+xV+Px2z4rf7np//vzju37z25/++tupJYUVFRE+5IVSqQxwl9dTTpHTCyK1kAF0hjf1NPZ++AlsYUIclFYlT1VARRkQsgA1VoT7I/z+xi8+O5U8O7lJAEGIqT4GjniLqL6vLT6mVAFVlBUWFiUsrt+fkZCX+pnsGqLtjuLU2tSLqbm1vKKOye47v7fzuNW2tB06HQTkrjOuAQ5qratprQMTmUJ2unaFGGhHOt1AX7v3TxsAFOoecZMmcjhoTIjuhDpGXPQKN6P/SOm1UFm43hNJhTcTdAy8PkDD1weHHj683lPlzd+qhPAQq5nmTfvCcvUP+fWxH5K4Pb911095pe284tzSUvbzLCysCPj1jQeBipIypQSjXmEEICF0JWXy9A8aPiIALUhG0vG7kpEhMQ5A/pJ69n9HtVmV8ElcYJYbp+GhyomMRSA6uhRJN/okztMpqigoiIgozKsoKM6pyS07eoZx8hr9vyTAojPZvW0n7FSbExlAbQqopSUzRwEZByGzpWjmJvkG5SNOR+hsTHzyxNhtV04xqMMOJJrCIdtAiaMBRVcfjzoJn1BzdN3AHzQ7Pzp4UIc07shfaKcuVaGMjqST7S8RkVfUgpKC6uk+cbL50urmltXdLW1tHd0/q/KN9V7cWmw8BKJ5n1i+BTqIwHZs9opvzd465e8///lnV8zOLys1KrExxVHySb1grmDqZCKYUCeEDscDBzxqIXpCiRTz6NgwgCwLbfKX12oWF6hNEz5NEzTG4SET5ayXjMdvTAGQVrb5K2gEZXlBRFRqfV5teW1eqtZA1VKpKG95PNDXeYmGSMvZDSWxkpPHtZTUAMiEOOUjwz4JXNVTdIgw0KKzM/AQgKiLi08n46YugT1ikHGQIYQSFRBdvty/fe2P+70t3f0d/LT7OlpONCfdHk7qu9OxaW5/xzsdSentSYM9Sf9doFE63PQql46BgZbSDnmkf9efVFIVTqGzrCArXqyM3jy2xcaDNm9nd/HF+sLx+YvzyxRRGR+DiaDnl+QXKxD0DCYYiYcmP6aM0CodaFQHNRXl9Cqgw28UHgAwfzhQwj+6ieZKCRlRUzdxzVO8wXrjxpTpB8Il3FOzWBEVXT1lXQ5r51KLC8YW5OXXRumYaUCnG+irBt4gmaMxAhOEhBF8TIRTB2mIE7+og4oQs6oz03UGBAcByHKQVvXMYNR6uhISJIYMJ0ciog0quPjPP24/LrWuVX+9nsSQQnNH0s9+lPRwOOnOjwbunrzeM9LRcaKj44H+rAMoyEU2m22RQ36aFCujA6LyA+VGRxGtBxeqiDpzq8eOZc0LkFjlN+XYC8Bx9Ns5uQV5i9nMoTif7K0xP9VafaOoyhw7BQJgwk4VhaVl+eWdV8/sPtxZN1n5LJyqfBAOyu51AEWVHoUuZBnxEq+ByGHEnFJl5eR4mXk96gs3DnJXeD1eVjD4/N7c4kK/z1tA4WOUVMd16Izd6dOB3kvNRnjohIY7w8cAqul010AIWQaClZmmWiujpWdFVoibGBu/O4vHWfFvQ0i9n+goWT6uv7343YbcaM0Rov2FtvTHwU+/va97oK/vAAeqrWWqk9phLqLy2ktCgFMOQdBpidq73ktc5JF7Lw8qAkd0FIQK8i5uZW/oOduXvxxE9GqB9+Ljb7Fp8kUIyRQDU3a1YApVqUjt5I/THKKw9uiVTdsmKB/FY/iQZvfmmBD3RkTunzcUaiO0oZJhOwMImaaIIbxtKbPi4w+XMBIngNwRUYURnjHeCv73eWl5WVUViCKqU1Mrwt1J/WZArqXsLzsxD2QABCHuQQMlBTI7PXV1RQaQHMpnpsz0rmTQVBER43Soh5qRM1kyeyceApEIMkFtcO2otiuQo/NRbeq/qdZWPgd04GMAlVQYlwTdYuiIjiJTZizHJQN1znfnsLUUDsq7uHhOw+LFQGrY/OoLvLcMQCuqc3MffwsHXbwIItMHglOxRv+y2jJztI/iFMBIf0nUblDiL+BDBme1QUWdnb0K6GrFldePBjBQII79Q78z9YnKCeogZYTgkxJ/tjzCawB53FEVhV6ucRFeBeSviqNtivbytyvwt9MVglC7v32gb/UddEhGEfbuPHFakmw1EGElo96T2QqaInMoIAZ8JMQpH2KchrhFAmhGjRZisVMZHnpEv0D/Apmk/T3+b1zeAAAAAElFTkSuQmCC\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Horizontal temperature card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "temperature", + "environment", + "indoor" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_humidity_card.json b/application/src/main/data/json/system/widget_types/indoor_humidity_card.json new file mode 100644 index 0000000000..ebf051823d --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_humidity_card.json @@ -0,0 +1,38 @@ +{ + "fqn": "indoor_humidity_card", + "name": "Indoor humidity card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest indoor humidity telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'humidity', label: 'Humidity', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:water-percent\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":30,\"color\":\"#FFA600\"},{\"from\":30,\"to\":60,\"color\":\"#3FA71A\"},{\"from\":60,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":30,\"color\":\"#FFA600\"},{\"from\":30,\"to\":60,\"color\":\"#3FA71A\"},{\"from\":60,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Humidity card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "humidity", + "indoor", + "moisture", + "dampness", + "wetness", + "humidness", + "moistness", + "dew", + "water vapor", + "condensation", + "dew point", + "steaminess" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_humidity_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_humidity_card_with_background.json new file mode 100644 index 0000000000..3a7487225c --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_humidity_card_with_background.json @@ -0,0 +1,38 @@ +{ + "fqn": "indoor_humidity_card_with_background", + "name": "Indoor humidity card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest indoor humidity telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'humidity', label: 'Humidity', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:water-percent\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":30,\"color\":\"#F89E0D\"},{\"from\":30,\"to\":60,\"color\":\"#3B911C\"},{\"from\":60,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":30,\"color\":\"#F89E0D\"},{\"from\":30,\"to\":60,\"color\":\"#3B911C\"},{\"from\":60,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMwAAADMCAMAAAAI/LzAAAADAFBMVEWuc1Dx8fPn6evt7vDk5uj5+fqxdlLX2dz19falbk3b3eDg4eTq7O7JzM/U19rZ296ncE+rc1Kgakri4+b+/v7e3+KztbiibEypclDS1NjP0NLOwbOrcE6XZEadaEjLz9OusbXLvK3P0ta0eFSudlTIycy0uLuqrbCaZ0ibZEXNzdDIuaqka0qRX0G4vMCUYUN2YlaeoKPDxsq5urvFyc2OXD5VPTTV1dbDsqN6aV6+wsa8vb9+dnLT0tO7v8OQYkfGxsiQkZOFdm7BxMjCwsWgaEimqa1rYl3Nv7CSlZdIPzfRw7aipamDcmpzXVCDgYDGtaaXmZt6ZlmDg4VeQjm/wMKpqKl9fX+AcGRwZmN1amV9bGFpXldZUEZiRj6JXEGzsrOdnJ3Nw7qMioqKf3ro5+WHbmBKQjuvrq6Ma1vh392NcmRjWlJSS0V6bWZrWk9ORTyNZ1KUaE52dXZ9c22DeXSjo6RCOS/Hwb6anaA9NC1+cGmFaVq+q5uobkyYlpZ2bmuFVz3l4uCLjJB0ZlxfVUw3LyqKhoV5eXuKe3VFPDSGh4rBr5+pk4ihmZNxWkxmVUtORkGSh39hUUZtTkbOzMp0cnGveVqfc1qEfXnCvLltampONy+VjIiNg37Qu6xoZmbKxcKQjo5/Z1qln5qAVDrZ2NdoSkKako+FdWeSb1ufblPe29l+eniMem94X0+MdmpVRz5wYlmqpKGcak6wfl7Px8FbS0CfeWJxX1RnRDC7pZbu7Oi7t7WQgXaugWZ+Y1JhXlyigGqwq6imel2qdVa8squFZFCtmY6XhHfJvLTTz8xtV0qYioG8q6NkYmGYdWCYcFcpIx/NycZWUE2hk4uzlIJJMimohXG0iW3Xy8Otopi2mYmgjoTVxrtubm9dWlhTOS0yKiWeiX1wSjTDtrCzjni0mpTd1tK7n4+VfG708u9zUUZZVlOAW0epjH2yqKC9n5xfPy55Tzbc0cvHppHPtqXIrKvCn4fWwLLKsJ3fvrC+lXrhx70dGBXs39UvqV17AABbK0lEQVR42mSaa0wjZRSGSztmzAwz82PSJh1/FJsMJGNCsCVWaGsqbURRFkVbobSGuigVy8VYLoqGRqkIKnIRE1BQkCAaQIuJuEGISqIgrrcVNeLdeNfoDzVqovE9MyBe3nWxQHf3e/qec75zTjH98vvdt355z2c7O5/tv9jz9IR/we9fWFpque2226655pqWpaWl26559kAWS16e5dlrrnn29CPl5TPmvNMtDLfAmfFdC+dXPB4Py+afdtrpeafTBwvDWxmx2CkzltPxKetgeVEpLvem1+Lx8fGR9hMlJbW1ZWVltbUNdaSG/v6S2rKGx57c3PmMhKPtfPPNN08++VhDyYn29vZjJWUNdY9B9NQT7dl4R9XQUGusrO7JFwyY7Xs++OCe/c97Tk34Pf6FBbBcQyy3LX311RIeEM41Bo7+KO/0PAgfdBjGkmfJ5yXA4OQ86w+EAgHBfNppp1ksBozZbOEKXRK+Dxqnq9ShCkGbkGjtIK0RzwkcvxYEDWVl/TgxAHG2nc9wKNIHBPQNYGoPv3cAU0YwrR1D3d0dqdXaa7/RYda//HJ7e/vWz2dP7QJFZ9HVsgAYWHQksODDtzgibCKnAEOHzZc5wACP4TyB5Fajl4MrDIvDwzXGYuHPCzqsOgwrOFyF5wULgu5Uc3PVUFVVRzw+0g4Y0NT2r+K02fEsvlD22OZnYNkm3XOPTlMHGuKGM4YxgBmJ56qGbu6uiqfaj91i+uWXc+++FVpfv/vlpyd2PcRiwDBkzFf4BBbpIiyzGd8hjnzITHHHyIw5P1+2cgwZZeY5dWt+cV7DuTmV8CxmPj8vXxGcomxBXJoV1l5UGLQV2Oo7Opqbm0HTmjVg+vFCj2Tja7lcfLy9pG5l5xAGNDBn5bG6shKIYCBykaIsNzTUPdSMPzLyhOmXh3659NJzobvPfflpGIMsOWQhY4gMarmtxcgfM5E9awEIY2aYfGAxeADJVjOOiqPLE1vzJ4cDsMQfzgdMvtmMbOLtStAhW8wWi8hyrvME9yWJWCsCjRQf0cOMUMaB0lFV1ZwbbzesAck+YBBqeqCV1ZZQOB6wrMbax8dzVXh+B5QbNz0EmrN+IZ11xqld2AKRA5T9CDn9U+IglxZaAAkwkMoyzwADMETFM/kMnw8W0DGB5FOLjVJeniyEKE0svIUs4yWnyAginuzkrJJDTZwfb21tHYXi2fbV/rJ+hNjIeA4oQ903dw/lsqsNKzuf7RHNIQxZQ6mF3NJZ6I/EYuOtudHWXHNV91CH6aEjXfzeT0stvA4DFgOhhQQQEiIQHtHXlqxW3ioTDOKNnDEj2hBTZgZZE9rYSIb8Vgvr9eqRh28igVAkzKLq4hizzEqKQ+vzxXWa1ng2BRZ6ndvHc81I5pvvuOOOm5vjZM1JlACQgMWAIQawoABUkzngaWurTMXjcRQBvAQHMPDnod8+/AkZwoNFBgyi7FCA+emnn77y+3c9Swt+uLfgWWixWs3AsCCMqADgl24MqjQ7pfoFTVVlpTEpIeWJQncI5nFOh5yfx0iiPRhJ6TDxeCrWj+NRmc2uNRPJp2+++ekdQ7mR1boV0Ozt7QGFCpoBQ9m/srk5OLhSUdHbVjE2k65PZXPd0M03HzkDGBgDBANG5luslC8Gy3u7YJiY3fXvnpogIo/UwlssOowFqZMPMIBQubaGvAElEHYnVc0WCeKqoWeAAPFoRjzaRafLzEh213IcIltiq2V6cepvx+t786dAge7o7kCcVWzugINExXnlSQOmGozQ2NjmYMXY8HS6MtvR/Q+YCw2Y974yDg8WgrHyCCXK+692n356dnaXXUDlnjjlX0JlgDNUo/INZ6iw8XiME3OhxsakO/JUZLEv6C4olPUryWyV4QvSX+YUobivyOEoP56NZ0EDGGKpwIu+mmqtuplQ/vzzT4KJxxqqN3FsQiGWTTzHgDH8gk6OnRyebkshxogFMP8yhuqWkf64tlt4ZIbB0tMzOzvrt3ILnt2v0BFcQ1WasRiXDVyBZGs+XTfWQGPjRnJreH64M2Pz2mwCnoHQs3Jms8zj6WZ7edFdly8vZ2IpgwVB1vDYysrKYw2r2dzQHYTyN0x/9eDm2EmSznIA0waYveH5fdL28PDmaryq+78wvyH9D2HIGJxckuj4frBM9cz2zAb8vHxbC67Mb4nmGqQ1aAiI7OFlC8obq4U23Bsb88N9TavR47ZQuJBuTFjDMVSg6XmilqxPpSorDRhKfsqBlbr+WLyj+1MiOYJBQI0ZNASDUARNA/Jkb3//x1dfhF79cX8sRkFGAsyRMe/RBYlfPEMskgdNmrTgn5g41TP1+dRsTyjgt5qfBQqk0+CQhnC/WGAPilRADXjd3o3hvpnKykzlpFcoUDhkFLocxGLeaXSnlrJiIlZZWY8iZDgDGKRy2Woq3tz96ZuHMEMd8VR/b4VhzRhYAAOhiFUPfkAoL0Ofv/ji/tj/nbnw4g9hjHE/tjDXMFbJr6max+PXThHLxsZUQFU9PLGcfjpgcAm1MBRBhlCqLLxfDYcENRQObMzPZbL10Wx8LmgrEq3UvDGAofrMyOeVygGwRAHTemBNWVsb7r9sa/PQHQYNqhnBrLZVDDbNwJuxMYKpQGqtrFSsbO7tv/r5GVdDL3/+6v7JaAoNjYFjOmSBMXobdhuQYMyCAhi7X9EmZnumXnxxYyOsKgu3EQsEGNQASUYBo3YLQrRJWigU0BTNr7i7ZlKx6POtufsLbHNBlpepcBvOyGax1GxPGzC5UarNqVhsdTVGOTRapRdm6AAm2lvR1LQ5Bm1ublY89tjKDukkwbz8NLG8+OP2yV5UM9AcwYCFLhnkthmvOS/LVhamKKKiaRRlG4DxhhTkyz9hFlieaAgFMksBOKP5JU5zR2oqH8h839xReXZy4Gw7x1vpdtVheFS0fHbSgEFrhiYE7mRTUDbeisYTF82nEGDWRmLRtorOwaZNXSs6zAc//IB+7UeieZlY9ofH0sg20BCOCX3MQ2d8eLGe/tfkW/IBcxuD641l7SJgQAMYkjuwRDCEAv+sVhn/oTkDCdFYGAXZEtIkhtNCjecvL9/4fVVzNlF0ZU1Q4q0SVTI9Z6iZE306TCuOPoS2yvAHegvdDXj0DgAw2Vg/rOnsHIRWwFJdXYHW8+33319HW/zqi5+/CJb9vc3efopQncZ097mXwhiCwQtvweyFewaVh+ckjpM0QdPjbGpqo7HR6wEM+XIN8oqXGRIdzqL3lxxS3x1SZIYTNW/ksuNXAqZ1oKCm5gLWyrAsrKHRDLnF8Gqmsr4eMJSzaMOG0Cm2pipjsec//v4tNDjAIZjcONKpLV1dUdFZAVH/8tjmB9tfr6/fevfd6+uvvvoqWIZPDrb1x2Ayum/AoPU/6+KLz7j4PbpAwCLfJpthkJm3cpyoBgITgOnpAUxyY7flGjiD9L8N/RlgcL0QizGiOUNwJuxhUDuUYlvk+J1vDTV3ZLqWly9TOUZ0WqlaEIzZXuquNGBgAQLqDiIa/biyPhPFF4EziqYGE0pubbwdNL29cKS6FzW5oe7JzQ++vPX33y+99FJMLD/+uD8/vDdW0aannwFzz/atl56JAkBRBlENyNebFCvHKhgakTM9ATXkTkY2JpaoAugwngWOhysHwhk5NRz2er0spZtQUHT28Ttbqzo6YnP3R88vkGTRIZnz6ZrBtWp3RFKozZWpHPUulB/gaX2+PuPz+TL1VAhyVe+8886DHbm1EdCgbes1On7AfHbP+u+UF7/AGroy9042pQnm0Bm9NFz9HrUyJLoyjQlSJpYwbo6eHlUDjc1twOgps+T3HGU/2hpGIRQ3wXB2V6TrpotuHEV2Z++vrJ+7TJPsTk5Gf0ATNOs478psiqrXaDdIINAMvQWW5cnldKa+MgaaBx9858FzEGg6jT69oEMuq1v5bPvWXx76DW3x75iOt/f2Phjr7I32w+SDnBme33r1RdBgDDuAyQcMTfUevNg6TGBCw/0RUv2oABClTAvHGoFDoSPLZjbkDntRzHBmq7O88JKirvubKbVRqAYua3QqrJU3U3ONaabooo+pfmVx4RsoUPPHmc6mgYEan06TGs+dQ6JAO2G01LQdqG2AM7c+9Bv00C8ItO29D05uGlFmVLNmU9/8/P6PBzQtNKwgFQwYJYTzBQQ1IPj9oNEUj2REIoY2iTsMMqS+wrKhwpAasIuSJJtlMeTyegtubO3I5VCnsgORLlWwEgylImO3XVSJeYwKcTdy5g4jyl67cWDg+PGBSYNmZHx8jQRnDBjCwVhW980PX1/6G64R0Jy7/iUazcHqNrpuKcZurhoHTN8w0bz3E2iWdneXZDNo8M9yQrk3HFY1TfGLfg0oIitZeXiHkWZX8/OU/hAjqm63LRngnE6JFUXezFgVQRS8NSkMxaPx0fhcwWWqAyyG4QxbWtCX6mjFSImiRaI6/NbDy8en5+YGmjqJBtNw+wiAjEUUwaAto23GI0/uvA9rPvwDNGfdvX4PjOmNxtAHkS9Da+2mZDIxTDRXg+YrmlasKLg04nNCyBsKCoriYT0KWFjOauUklkW7pgbCKou+kg7IuwobI10RQQvBxLBgTJIyr85lYpjL4UxfYcIFRkKhWVRyltrmRrHJAMshzOj3D0zOLS4ujjVNgga9DXSinXQCzQ5YSHWY/6+FNb8/9Btgfnvo3PV7xgYr0KBmR/UGoGPkhAkz7jxoXn35aYKZPeWXcBkithlJC5YHVcXvEYlFlDge1VpUIDXk9Qp2fEHGE63FyWSyq8vtEgRXcVGxHYyc1WzRIslEqgNKnV3cGOQYsBi1QhKdgq1+CFf9kTGtz2dqFqHpmcnOTl9vG6Z7GqMhPKC90pMVFY+RNbXXv/D2z7/89hucIZidFQyoyBhiqVprP2F6cWqqcespOIOkgTOwRpJ4BmI1VzCgeSCw2FlWgjMSPdYwR4ZLHXaEnYSjhWzJZCTSGLAL5xXhfyz8Y81mzRtKZKmgpS67IKjSIGfAWDhWtDsSo0PU5h7AVL1FMH19gGnq7OzENNx2kCZQA43JEKaZ2pKSR7754f3fUdCoBOgw/RiDqqiVwD6n3bSV3AhPfY7GbRcp4wEN8twqy4zMaqpL9aNxVlTEmCiCBgchBI+A0iYIDofT6Tiv2O12F9qSjSEh6E52NbqcdjyVk1yRs5f1vVjrjX0qXZkEQ22PxEoia6sEzCHLzc1vvfZvGAgXJW5KEs2hpCcJppYCDTRUnQmmAY1ZjkpZM/ZmIya4sjExMYGVGbYvflz3sAYDs4yFsOBCHVMUQfCLQLKLhljkjQcLC1cw6HIVFxQhYZKN7nDYbUMh8Cp2xS5JmlKaWM52kEbf+uiiYhEjDcHQ7SpaRVY9XkXGGCxDo4BpWlw8hEnrLKRqEj0kmIq6shPH7i1BoIEG+n39B32mw34WNB1rKICmrf35rVMLX+3uYsD3+CdCPQEPZ6VNkugXXJpHoaIMJJQzxQlbIAS9IAQCIW9xsddtKzo7kYgQSzJpc3tVyYrsUlQhdFllLpejpjjX+sAFHCqFAcNwkhWx2fXWf2Amp5EzgCFjEGZEg4fgAYwhVABsCe99jtLmd+DoML2rdMeimun/mAkdzo9Tu9gjTWA3C2fCBzAc61Fdil8QVAHmBFS0nE4RMQIYOxGGwlAoGLZ1XdZXhCd4u7qSG94Ay6PiacGAeyCb0zW+Fq9JYAYgGOQ/MornFUdRdAgwBkvV6Fux6PLA9PTYTNNgRUW6t62sjGgM9ZI58KoOu9kTqNevPPf6u+8TDYXZZjV1mXRndnSgXTXpd2bPLnRqAkQBwFhl6jJZf6lKlyUwkCMh4DgpbVCeJdQBFV/QFKdT8xbBGZdHSyYSoAl5eJ6XtPB5kfvXcmsQ3K/s60PhIxgLYHhOZpzBsweQMyTcD82jb6XqfTNQ02AnsVDr0tDWa6iNRKUALO0IpUfvveWbL379+QCmIrpKy4QcejN036bF4fkfX/28Z/YUEkevVAFRZjCBSKwS1JwEg9aZTAhoGLMIU69qGjUEEifZg7ZIpCgSiXQl+hJb7gBKs9mquLxzqUOW8aZEghMZwFjM6PhktAL2wssSdM2RMNGgIahEOzM5OakHGVhKUJIN9RtahWgVTVY/cf27FGh3GzCVMRrrch3AaTZN75E1U0SDKwQdpSrRbAYYNagQTMAIqJBqx2uKgRq7QcD4qebhZJKAhVJjURcEh7yqaMUsJGqFNfE1XSNr7Ym+CCfQolY202Yaw6biPrurCmqm3820oY3VpzshX0U1YOABlv2HDBipY+2kEfiSQ8c2QvWZBjTqANoMmjgo1+KmveF9wGxMTfWcUvx0IWoyYBBK/nCpE8nhCp/ndnsRZorE0w4K2xugih6QkXgxEPa6cWtGnnrqqS2v6mHxbcWbqIcppKvWOrsG3JxghjFcPg0+VosZVbyreXS0Y5Sq3WgrYFKVbWkffEHCt8GYY8eOnSDpEKmRbDaLvwos55zz4IPnrB17bOdtTGjr2ydPDvoOelM04idGCIZ2UJjyezQFMAEWDYlV4lj0Jn5yCmdttKHh9FAXTzsmWaauRmZwGaFTYBV0MbbGZCNkA3RIVcOR4/XjqPuk8ZG+hE9lHdhEmyXAoDmzmB1CcIAgDMXjBBNNgwYZA2Nqj917772E8Qr9BRC5nIPAgkEnN9LwzQfbt7766v7e3gzFZhpjHYVlyrQNY7ZISbeq4fAKj0hAzfGHQwryH+nixZWI6X6hxRhFaQvNsZyMTJfR9eAiVSingKE/FbkzkE7R+Sg8UtlMV42PFVnsCngdRnZi2BQKKltb3zKUhWh9BmsQZVTLSgDzCoEQyd8cIMGYA50z3r+ys72uw4w1Qejn0qh7dSnTPmB+/PGp+fmnkmEBzQqMoWNKalj1K4CBMwURgvFg4DHr0wwnLaDtRCHgGZlHOWBp76Eiudx4Z2YmHa3MEou+6YvFphOZ47KdJxjOTM4IPOM873JQfP9x6kAxgGNATvvgDMEA5b777nv00UevAgk4IOIw1P3g2mr15h7iaX8YNxPUhMIxuLlTHQMMND+8OLfY5UYVxs5OBozVGUJjolD644hFjd7wBN4gpMnamAAmMBGIrJXikbeydrtdUV1eW6RvuhMdPFavxFJPyiSOZyKMXUbKyFYGGylGcLCum54HyvPABW2MfuPZUViTpmIGGKDoLFdcYYAMHbHc8U53LlW9+QEsIJjp6YGZpqbJppmTYxX9pu1haHFspmYg4UZiUCuMwmxXg5rdruHKxMCMZHC70SD4F9C+Tei7GlyPqoZ7x25nUZ41pE1hsqtPH66iVF+I5YFMJhOdTCxPBhg7Q3M4z9BUWlpoS9S/9dbHz8M5iD4CSoeBdJhjBAMWwAAFAsohDdSc7R08OTwPC3QauDPZhLVnZ5sJ7wwsTo9N+nw1gNFEDrZwoqPUJdipbdEc6PepkbTNokP4amnJM7W1T3pqC9muamGXoCH/CyJdfXMzvjTtJNLpenqlsQa8Hxo4f7nPyjrRNZtlGTBW3nvT5ZWIsecB8cAD0Sh46DdYdJjqIxgyhmAOBR7Dmgfj9RUz03swARHVt7gIc7DEbfJlTNPY5IIlcz9gVEWkG9HhKi9Hi+/UqCIgadyFyUgjfkZgd6Fl4dSPH+BHDHZ2xsam+7qKCiNFjQWRRN80PMnURzO+Th/xVFbCGLDg8cDAciMj2MkYDs6YWcl75fNI/hRYMgeKkgADFIhgDBbDGdAcCTBUz1JRnWZeV4L8gR/pNhPqwdiMLxMFTKHqwMiF9UlhYTk6MowAGrImWI5kiCTdaKeXliZevOebx697/Npr6+ra0jVzyb65OTgS7dfDHizQINKmnoIMLICZHBB4lwQYXpTMeQzrCNYjpVCLwZ65P5MGDD60Qb3/gIH+C/PJJ3/jdIzHiGZ4Hncb3W8JsqfJFzU1YdHelHnggfsHEraAoIiio9xmKwyWClSgqAVAnDWiXdno2V1YAMu7L7x+3S23PFdSgk5jJpKOAoRuNGQ8wUw2TQ52YpVFL7vPt7zsq5lMiHaBpwWJHZljFV2h+6k+PE8wCMs0CURtvVTMCAYTpeEMaI7iTKchHsOabGVbE8HQlZKMPAV/hqeb0ia0RBQiDywfTzSGkCl2FcYUB10qMDTkjaIvAGmU9PtPEQvBPPfEsZLBxelI00h7CjdBfE2vxVEf1txoFn290WgGJ13unFyerEmKit2M1RrvcMgWkS0vn0vpOQVcwJAOkIw+WYf5DjSHMNAByjlEAxzs1OKpaCdgQNIIJbfgD2hMKGzLaAgyNXOXFXnLVaeTAuu88iAyW7HT8KKGUc5wbU4osy/e88ILLzzz+i3PEczM/LxvZIRaJqCgedH7K7J5cJCuC+pOqHMcsGl2nt7SwNJGNjvE0mAXSOALGfO39DqmsxgwoDlKGoIByiEM3Zu58XrAPLWFNx3prTrgbIHGVFMzOYmMvXFg7vxL3EGBpl6767zicpWGfOzOnUE3yTs7QSwE8zgZc6ykv6x9fOSVdvTlQIE746l6vDlEfTxoqtMHML6+whC25hKD5aCDkVX85bZ0DNWLYHSaTr1qAKb6EKbEgDmiAQVgoMO8QYNW6RsjmA33lH5VoCH7cd6URiT46jM151900SXuUgdgWHt5YWFYsBsrDM1b6A4FAqdmez7/8l3dmMdfegIwtcfGwQKYE7AGGh9vJ5jNmc1NvKeNWAMNlO4KF0p5jGBFS+rM5zUR67jFSp3lKMzAYsCQjJwBDGTAkMBCMmBgzVrMN63DTGGtTzQbn3/+qimaXq6pWa45v2/ueMIWEhSnyLHBwmKXHe0KJKqFtpCmYH3++frbBPP64ze89AQawYYTr7zyCpw5BmsOYFYJZnOT3k/dpIapAkonQ25rnniek5ElJ7bmLO8QImmCaaNaTBDGR0h3BtbUGh3AEc0RDEKOeFACDBhimZ0FDulzU9o3OTB3fO6i4zU15xd5XUgUTlLKg06Oh6ySErJ5sW+aeLrnxfX3QfPMGzfoMMfqagED3Xvi2CsGTXs/wezQzyHgPdWmwepemndtaohhQgVBeuPGYhVF2W53z/QbF8uh9InyCKbBcOY/3vztEHlzAIPRZZYEd0D1sildQ1ve88+vWb7y9oi3VCgvF5zOUgfHMJi9rKIQphkFMC9feuu5l76tszwHmCeq69APQuACDe2F8WMjeK8bP/K2twecMaxO63qr65J2jREjXcWS2SrmYZfBiKzW119GozCAykgN9Bn2YwbLk9XXliBloKMG7d/6B8wUfDkFGNLVPSZfzcBfdNx/TNRlHMDxC2k0CPgDuY3rDy+2u9tgu3mcG8ohuwt2SqHnQXdUVuO8RpgmFXaLRTuWtdMRaJQ3lenwcIUslGIZrjQNR2ECuqSZZbIxsijLtn7/2Hp/nu99Pfv1kcjZr3v1eZ7n+zyf5/PV5wtXgyEzAUpHNmvZkmwmbBqc/EJqZyZKSx8eWXd78el1b58VDIvZ4dpDx+9Aw4/Vhw9vZ9qAodcCjLTvaZp7pPuoK780zx0NNZgW5BXcxA47i7PQKKU9DsfXA5BgVikMqeljnCVDz82/MaxmMso++giM4uxfZ9i8eXcsFqNoLWuzzeGocUTMFYV5rKVYcgulbk55+YeL3BQWP3Ph3LknBHOisXHi+EHOg9sJloMzUuO+n7uUoWSPGJyJ4/fIZ9zG8u7c2x90c1pewILCITbfuExZFi8+SqgeTcXBoWGWrf7m1Zdf/oY17X80YN54cu8BMPvBiAbLkSOG9vvKPWFfOBxORB2hLQm/I1QdKcuV22GFKSw0mQTzw8WLv371ya5d5wbA9J1onJ8e4kCIBgwaTpQ8CWndOTTb2YnlA3IzfrC2tq+v8ViGqWFvt8OVdmvBrWxiOddluZYRtVBWS+gcwTAy+Vq2mPOM0iQHGprUOqA4bx1NYvbrmeGb4d77dnugYIn46wfD0VZ/q7kwbwEYUpOZQzHJRMsJ3Waf//jJqV1nBwbef7+tsa2tcagRDA7mDJjmZixH31gFBssVSQ2YPsEccrubuvb1N+QtoLbDJSEb86ravtraxSqxBM8s0YDBsulpfvCkYf5j0XMjmr/Hg0c3KwwGlRgwDLP7mDRh1mWPw+abYoNmNhuX5KbfCoZZw7mr0ES9mS66i799cgrMWR6ZfY2i6as9LMH8kUeoKqMso4oK5goY5s3swb6+vjYGZMOBA/3bgqU0a4LJMeVnVfWBWS0YbQFZjYVO4FWUzFWAITVQNI7CpDwkCUzz091JjFi0MGzevdsX80cTnqA5frLFb81ekl2QKRjVd5UhG4Llr2Q98uWaP948dWqEzLTxCbE0rq0VChOI7wwSqQut2ieYK4RgxjeIZcOGDTSIHOjuqlogRTMeXPl5ATAkhpO+0oARjQwzJPfxtfjo3zFosFz/gefB5k3dXdcEoz9nBCNb291RR3W0wTboCxkX5VLoz9Xar7hxzuFmz7Q8P2t58Rdvapj1AjlINK49cfiEYMgU9w9gNu3rvo6hU3p8Q1ufWMjj0MREf1Pagrx0MlOVme68jiGSmhSGoPf05X9rtLRo8Wrzsoe6pN6XtBCCYVcxyHwJOV0RrzWb1SYnJzMNiZyn+Dmn4ldyl88pyynm//pGdV3CBRADDQwz6J6DQ6s20S75UP/e8fnrmZmdH2psw6Jpjk8M52mYQGb6sb4+8rkdjJ6aZGae1jFvNO/QMdqkQUMIRXEefPXosm3TwynMMTD7Wc3i5WBCNouJRuqcLACl+TLMqD7mM/8pledYxrCgEUwjbe084A8NyYVJLSOONLFvp9bNwr93fPy7D658D0baXYcaG5OYRhaMrtJbqRvmF1RkpB9ra+tjpqlhRiQxy8BAkUO0YFgCUhhdo4tefRkMK8AcBGWRAbfTwCFqMBYK1bgKMmSfDKa0NFPuU5VlEaWAbOfw7MgphTl1bmADfXjTdBOo7j2WAhly0tK2+cl9/f3Ts991XvleYWbJzAYdg6ZvIiDN6fkFgYz0sT5SIxg9M9rarGOIN5qbwbyMBAsYIqWRxJxZvPaOCcbZMdnJKMz+nUUGLDwuPTZLRmaOqh5lmCjTlsrVZEF2dmXlorJI//ENA7tOnRLM2YH5CXX27mJHyc0cEig87zhjdu+d7uzEIhhmDKUCweipaTs0ylrP4TmQk0dmdAyhMPow0ywr3rh/xw4wEjomydAsL28H0z194NrYHBQsxwLunSZD+W5f2BNOmLNzMwuI/KwibvQtpWw8FpXZ7WarPeorX1vbhkYSc25gHAs1qwPdotEsy9bSFgame5r58j0hicEyhELDrJfUdFEA5Sq6MidvD8uhwgBJZUZhlIWCCL0AIFIWiRsTA4aWwK5PRyksq3iKXjfD7lg4EfZFjZaMjEWLFmVn5NBoQRtDaVGZ1VbjcJj98Z4VR4+eePzcrl27sAzMavXc4QnOLWjEsuwg50upw30glJ9/FgwWhUkNsxMTy6VDI9dSkOfu0zODRH/Q6BgoWDbueOnBB3FokcLwTWbMmdWCITXD1+CMjY6RGYYZefHEfCFzFXWHErs1222tcsuVjLGmtbqjrro1PjPDCxAn3n/8nBazWPjnh/ce72YvqVn27U2EucfvEsukwszOH9IwaBhsCnM8wJMLTGGeScsMGhloN2AoHmDpXbFxI3X/t9CkLDpHw0izw9N3HL8kdxjDlJdHkxiPx1MXCwUrspdYvaEI7TxQnEargx1bS9zTcZWS8I7mw+8//vgTYjk7+6nqWx3t6u6m0su58ODx/q4Q12aRA8OTxM+C6RwfGuJ+GAQBqI3oG2qg34T2AlNeKb/WtxiMFlg0zNMaZsXJq8+fl5c14ODBktJo6QHDFp3U7O2nEkgMN7jIgSHsSXjq2F822I1+n686yF1AlZHrFk8iXB+Pe7ZSSN34/I7VJxRm18jI7KfMORodh6cvTfd3b3uS8tVwkPtmbjXG5pBomENDRBKzXijvs4YH06hrSgd9xgYwyc2ZSow+Z2SU9a6YmjlPE93r9N5ogpRGn/87zoBZS2rQ9E9PA2o6xuwwJBghCX/UH/RGtvb2xBwuU3ZDayISSmBs2Vp9+VFK3M8/v53UnBsZGXnvQue1OXXslv7ILiqJ/aHhJkrRtAEek75JLCwAs0gIbfZDkVjf1pUlbUMFRVm5QymMJOaohtGnzNRV+gFpIUBztzL8E/MgVSFpul0lzbUTe4muYAM3YoZEOFwdckSjEUd1z9V7Y35XQYU/7MES9SfqO6p7HkVz/gGurAdGLly48PEHn46dfkq2dqfnlsJhovA/BQqxZ89pCTAfzB/ULWBwPN62fv36tukM6aTPKcrPPASmb7FooGABs4xhw5whM1Mnz0O5UzBo5LP/c85o7U5cglDaoszMrUb/gVF6EQzhukSoJuiPOvyJnpmeWLTCZGYXHY6F/RxvPP44FylgXmruG3rvO64SJyfn1h1h032Eo+eaSZLDcAVCuDSMGmVDgtE1DDO+wExkg8nKNWXmH5cVoRZN6jxTC+ZpwUyRmIcJDcO80eNVLS2UG9RJkG7U8m0UHdUFTTgUZHwYqGNEgt6Qo6YmOrgiHou6sr0+T2JwMOYP1rS2ttbTBcoI3sj7H+y6JpfOYeE1BzTrblt5Oxr24YST7Dy15/TcnAyzz2Z5/CQtBBAtugMLbkrLkwrNhKwASqMnRs/MfSTm6qPqjvA88YC6VtuxkXGVbNpS5VBClel3S/CM2x2LRoINVkPcF414bcEmbzDqo0YbDWTX1IV98bjPbzPbvF5Py+Wp84/SdbCKZk9pGSTYDal+75V3FV/jcnesQQVbPSiC+XicssQNmVmf1Iw708CwE8+f1jBoDsv0Tw2z9vYe0RAzKk4mPzpxP6FDKGVTlwfzJKHqplFH0Ow09MifbUajN+jxtYNxLfHW+VgK6j02K1Hj6YjPnL+64r5tqtGbtKBQcdvplbetXDo5OTZ6bVhuEUfHWJcVpnNewxxUloH1ehw/lg4mK7cgcxhMGxoUWFKYzdyDXL58ube3t4foJe4lWK9VJB1864EySPWSqKOCwbSPmKnDGO6NeUIRKnW2Go8vPpgQjG9rfKo3Xldjp+Jstzk6ps7PrGh/iG3d3FNYGEtr5m6/7ba76JVcefuaMe5JKcWw4LOV0TDfHVrF+fmQnhtxPC7f5ikHpucxzjLHFEZpNAvjTDAU21tatrbEL/O9fmuL+nk7V1KACIXqQdJSXxfe4qn2+/2tjhqvjTCz4y/IMPSEoyGHs8hpiyTqBGMvsflaLp882dviN3KxsaTC5um5erK9fB8b7j2Uc04XL5W+biS//46GacP2eOiee57snr7C4z+F0TVgCA1DoZYCVkHGMQ3DONODFYBSE0NmsJ7YWl9XV9fRUddRV18Ph3wRLfwFgL4t1a0Or81stNrtVv4oK1lCcLOUlWcojzocjoDJ2SCY8nDUWeb1tfTOoOnwVkh7n9F/+WRPiy9MyU2qhmt4q1Pa134n4Kyc7Jxf//7hM9vX0g2uNJNXZsGI5SCWFGZgYN5ZQGbS0wtynmok+jTN0aQGDBZfPQggW7Zs8Xj4JhyStLXe17GFAFLD5reismSJNLxlS3BykfZRqmOGWMTriFSZXMZI2AfGb3R56+K9M1MzV+N+s71ySYm5Oi4WVnKZ4ljeBpO0/Pm5wnz7zatnljGpwJAYwdxB6CvAwIb1zBv+mN9jEkxaQY6bX9YxejwHhlmAZQtfnmoVHjjglI5x5fCa7ZQo6dor4OwlkQOEAj/d/DSBGyjHRCKVRQFjKOZrb+fGyWjriPee7J15dKre7zXa7d4tW7f6tkRDEdbgubFJGtdpKQKiMH+efqZzfsOJb17d8ZyMM/aZk4K5QzSrqBMIBksyMy6LDLNbMwoUpg3LjQPtnjsorcinRqBmBAFHJcjD9FCSSigUKaQJIRV5eVoLmKGBnNRUZVc18GzpiYdDZqM5fLn3ZM/Ui1d768P+mhq/VKL9Dun3O6YsOgYLmMlOqjBnHnxpxeZ9Ms6IDwSj4mAjGgWBcnbgbMAtLw/fmlvoJmd/mzRHwWwqBxODAYBvfp5ycAj5OfPcWFFJTgr0TKSnqUhXP7lVLDcbqtzOYDBgqWoKl7f0kJkmDXO599HzMyxpCU94i9/Ryqa4Bg2rr2jAIJG4ePqadEo2v/7WxvZtE51JDCuCVFnvUdtMFQozsKeKk3P6gkyTe36AMg8YzcEXw2yTNJ13CKOaEIzDAYhgyWKiML4KmRtaJvj8NwYWwZiyAw1NLkuVLVYeJzMMM2P48tTJnt6r56+ypDGGPRFb0OFgIx0cHWWPz0vdP/6mWz6fuzY9vu2ONx547Ko8iRSmUzDay25tYOBgAXN2YE8gjcwsyFeYtiRG7WfggFGJ8ScDQzIkK/YKXr2ng4J+HUIkUkBKhVhuNuQUup1NVndVsK5FMA6j1RjuYc70zvAUFkwiZDPaIo4QnOFrYNC88NNPYH7Asm7u2qXufbvbN754/l4mjYyzK599N48GyloKHlBSmLk98ho0b6eBYbvGKMOiQsPs49RLRgixoNCCZdhelrSk44ChS/QQChj6Sl3BpkAgyFGMHUDEbgczRZycmSEzdWE/D1cwRGRYaXhDnaX5z4sU0488dexa/759sd0956/SmXipUzAfg+EowxFUap/XEzMycnZyTx6YW/NyFEbtm/UgOwrjAaNCt3gZYilLnliuCwgU2neFWV4KJugKOHzl8Xh9IlhhNybYT4A5OTXVM9jhCQXNDZKZkKahOY1zCy8PgfkQzGi/XIjUT83cu/khNWm+4n5NCjNiEU0yLSPElT2ZgknPsIxrmdEx25uvY1i1mCpMFiwKg4XnCvOF7hFl0RlJhPalYYpMFjuZcUV9Le0tdVFboMLoibMvEkzv5fqw7NxsQSxg6IhQr0jKxhkLPar7Aw0HEtFQKMGLMpu3aZjPLqAhM6y7OgaLxMd7MtKypELjHpcpc0LLTPP21arrf9OTTJnq1EwBAsVm0+a+JIZ5n7Sg0B16LATjdjNnjJXWRB07hTCFjSqrfzB+GQ2J4Vf8EdVNKphQRFIzRrmNjm5aul9ZXrTT4mpi/beFOuL3Pq1jvpDOh8Y+hVEWTXNq5MKe5Wl8oPRMy2xqlCEh0NzHAsD5XQ9Jim5hkNGHTGKYL1g0jA5JhcHp4phYaQnWxVoGORUYSyorasI++hxk6zpIdSCCRUsMz5rRMeqg+u+BkJlBI3pVQ9Bm9rZ6drdv2jYL5scff/niEy5yNwimNoXZRWZ2je1MSweTb7nEKGOQHeWuWg8yA6ZVDDUqNEpykKkZk8KkAKm8gLE1WF3uopKoL8aMcdgqLJZKc8gTHuwh4oMxpoxNy0yEsxw1NSrVYGhxlIs13hSwOPlPRkLVsfKnBfOjwnwChg+rMEpzTjCnTl3ZmZ6mLuX2z3UyyrTpomMkMx7ZQso5SrfQx1NRVpItidFHmT7I/g0ycEILFJlcscG6Fl/U5lxSVJQdsDn84cEWWqwYZUF6TYIRkTidgarAHqm2C0bagF8hMxaX0Uxm/Indm8F8JZg/BHOWZqHaE8wZNGCoIVITveBeIOOcF+nSvxxXB7NmPTYe3ST/uRoyYbca8UBRW2NtkJEYGl51zL8dKjWGkMMWKDTZBgfryn0OK31MtP44zd5QIsbJJxYNNhiJBquVQ76EUx9mWflqlBW56a2zGr2t0di24//AHF584oRUMx7XMRdOyzudxIJHLh55+4a87ODHG4JptZGIsjLlIbBUJLdj/zvKUPBFgPE7glVFJZGWwbr6cLCEFi2TidYfc00oylWnWHibTFqXwWhVCxlmpfwmKMrCW+YNTZxTbQ466Cb+hll/YjGXUZRCFWYX8RU3iyJ59ku5vC6mMrMdjPTJKgwXkp4acwV7+yUlZRUVeFIWEpPC/Ncgw8MwYy4ELM4oFYyYp6GoIIO3FsDYZNL7Q3zMgIUOuko0khW+UWeiXZPm01dUXgJGlu0am5fFYbjrko55910wctsJRjRPCObdW25J532jiyo+v614XqqzzVAkKFS0l/uqvXYZUoWLlMduT1oKZZ/My8cKoz8i/4NjoBppt9s8g74YZxnazWjMLCMxkQiLI++QVJoksslNIEBmKBvy0gAYQmForHP4Q0GGRINxuLPzs2Rm3h05B4ZIpgbMqZ9uXr7uNMftz8Vy18o1b6vbGd2yg967umpbBVM9I0fjVJaV3YDJAqPvKP8nDDYGktmR8NVR+TO7eL20MmBldSKkY8uErtBUSOM5jICTgq7bsrOIN23EIhjaBElijZVOYNcomK+uY9p0DBqp7L77zk0fnS4uXonm88/vum3lmqUHz+iW53fsADMYazVzeUdfmKZZQnrKlCaJSa1m/4MxOu3WoD8RY7MfrbEZXXar2cYrMdFoqKmE13+IHP6AY7GACfCys8KUgpGGdGnNNtvMvNHkvgHz7qmRAVpsUqkB89vCvCPFxcW3o+Fd6ttWFi99O2l5idqYwoQd1iUFGWBUbuCo7OiLGRowaJRF3wCkNmdgrHRh10TDdWA81dQCIxxe/NX8PGKkq3kREA6nuTk0bINxCsa0XFkYZTsRumnOpgvSlMFtocL88ssnYHZpGILUCObC17eUrtMwBAVECiPjLymLwjwPJuGt4MiSKanJKUSjpg4YPTVgkqmBoSA3BhinxeLy+hNgZPmijE4ktiTY+DtLKqssJIe5R88Lc6lKMmMpwvIKIRh3FVER4HUhU0ZRAwuAhnlXx2zXMQMjfy5M33kay+0r0YBZWbzmhReOahg0tNO27662Mf/52OpNPca3aCorycx1zPXFWbfw6mdqEw0mu9Ls8LD1rfbIo6V+sN7HBGKRdFa4XAHpK5fzdmaBzHa5BDFh0TFV8uQJFOVkV2YXuEcvXfoYjIyyN7mXpi0NjDbOBs5+v/DmjP23kxkdI6l55u2XJB4QDJnxtdrsJdKNnC8a4ajcqMxkCEalRtcQCKQvhkhTTIOxJLvEGoyGOxLV1WEoLS3UCumllxORy+6i7zSHoypN46xpvEXDzaGsy6+h4SkTcB0zOksKTAFWDueB2e/+hqEtTcO0rT/78S0Lb1q+TmFEsxKLYF7oVBhCMHU8MisZZ/TtybzBI5wlWNRDk19Gc+OBhpYY1Ran1zUMNlcVy1fIwxkvmvDRrjGIxZeIyJ6NN0lKLBbRkBjCQggmN/M1OKUmaXlucDH+aOl0N10aF8wvYN7UMas1zIbvbll4c/pOpoyGQYKFSfPMUtHomI5WLzVWhUEj77bxygTJAcN6nYtGOHgAEVpHXD5sFeANjiark7demPPVfjBEPY8ctpdcrFWWYCmx8K/P4R0H4ahXHF/JZRJl8qpWttvZ4HRDolpo7Br/F0ZicW1b46WvF5KY/acFAwOLwqxZ+swzazrl9zQTzf06Rnv/KytfeVRyCFKj15cQEfwdCsLfIG1kixbx2QweDl9NNvbFfn/IIxjqPYmQ19bkKsHB059WwFzaG3kdRV7UlAypSSTvPrIBCFj4OwhrqPv47L8wFJD61jZ+esvChbdkfsQoIxREx1AdLV56PxTB9HT4axhm1FmlhisBSJKjFrZCjaOEeiaAyFuWHMlKSir5buCcyJ7Y+1dfZwFXZxmF8c9E1DFAd0Gl1MlACUFmoAMngqLonNiF7ezAmsGcM2Z3YmBgt1iYs2b3bDcDW9HfNv3Z+n/O+V4+rnW4XBgX9Pvf55zznje/rdTLn3Iyi5zRhTKGeShUGbEMkpACOEqCf9CE8kaMG5cxivfGYdgimMWegS1Lpux+WwJjSzn2psbXoOt6ly69NE628pEI8w8Y0Wx2m8HM6D6ttaW0lkbTBmAAAmmJkArclnFbwfBwFkDYUEUScosIlYk1mqFhYIykRqVMqbza2hrOJZOR75WcWUU7Qspkofs47bHBfH9j1khSWUPLbsedsN6zn5xhMJ896iuG6OMzT3HDHmJZaI3Xd/0XGEnDeSSz2Nb1xofd7SrNtOfQIwJDIMTBlzy3+ehycdycNtnGMO2sZnaoBJsUjVdjX7PVpEl0H9g8cjWTmW2ptddu2LIW/6JM0l5mTo6hEbMd2KNGXncdPi0YVc3j2HG2fmlLe8+Jd3FUJMq8/+WbwNzPuBIDgNsjyyJiWWDkBfc6TCCRy22GNGhz+mbLPXfKjid+2DNBpVmGViGZeYzja+5YXq/BIBvSg+5JCVFidng0hdJF004p4CZBw1Rtakt+mcYKGFyU/9goLl1YGZY18WmFn8Fgtama9pf33/+B9Rzm/Tc/e/t+YPbFNrl7oaXFwrCfYJ6DIoFxGuFAM3uzQz98eevWMfV2coilXeeBBl/zZhRZardcWw0GIOpFSZJJk6DAVE4Cw7z54aS01cTI0OVWqfXJwLwFai+JdAUj7qvkAZZ21/ItRjZbmehr2rlmwsvnbLSjYKgADMZZ7jhzn6VlHHHI1vwAAwcGicMYzZrQvPv0rZ2tJSwORxhYsGE0UgcYVcFVqWACEcrdh1+7i0S44epoypSJuwAALKT0kCex80ybgNmmAYs4FiSzmw/DQWxy/CfGyWcUzVSaqV0umXHORnvteNcQzHcGc8esNQBBlsWWHLXG8wgDDDk5BoqVSWg25BSvS9tKapcZhTBuOghOME6j8nDluvW35D0Xh0gCyrXM4NtWuoiMvIu2LrK1zGhV969AD9K2zEJixjoRHuwa01mmRE0GXiwfY/Jgl0s22NFh/AzPN78D5quvZo1bJLAsvsayFwzB8HCaII1o+Nxsec7xmf/6CJ22EVikjKc1ObcWv9Gk8ZYHlie0bQYUWNimxTa6SKteJmkfBjT09NmQOW6ZNbSncQQ5UiwOYysD7TAsxjEomuVjdEknTblkux32P2ivc068a71zfXn2d9999/gZawQUDp3j7LoLXgdmOWj+zuI0gtlsFXA4lISzBRMWCYPhEgYTvGw4CzBCOVerASIaGXb60IhYR58dflQOdnzBMgRIcDD7HyxpUMDYuRoXrEzh+cQNu7P3jwUV5+x3wqEOA80ZzwtFvXIdF5w17nm8DBgWDsjS3CydZjnh7GMwSTrDlKAzlqG/+1/COMxhET+lgVxJLqNNvUrHGTrPZAUmPCWM3hspjzAGs6BgLFzWf/juG9jOuJ3DbLfOubMc5kgafAwUm/VfI4FRsRxoDMa1gSfgbCic48WSoMBiXkayaVgt6AILi94EE5yMDWIRO0rNtygbKQg4+4N2hXc+ywPGInC4MMBkrAEruqz2BNu8dFbU/sDscMA6l8+aBcqnZGOXxbaYZxjL68CIBktoEm2CPHzvOAs7ChwyD39PZf9kcZhzDcaWvbB1FjWoD2BBGHqSPhXib5CxxN8YzLLa0b0ah//upr13M95gdcgOLJ69YdYzd+/jKMCIhdZhnEVMgEmnSXCgCTyMEsye/YWJotp+KJXRxqTrok2NsISQEczaNKWMzwKB8ziLYsKcTBcfT1GJSqd6mZsty370LXe+GxjmQbb9kCPJ3tjAjvS4yuMeFGfBx1yYNGWSNJDQDDO8jWZnH6KeFkC2uFg8k8EiENclXRjBrK9QWReKYLC4l7lbSQ8zZAeKM/1GUsUss/Lak+6+dsrVJ9/KNPsbLNrZdp1LLr97STgSXdgWAUpwMocJ0vyPNkrUq6wCDhBm2pLIIQvDWCRLGosrcxgwjCJZqIxUjxKBeDf5l7xMF5+wLBxgqJs5bWqSjs3YTTBaxXXcYSevbyRJ7JMogiwB5T9psMAi83bnllEjrdB3FoJfLAJBFaGksxjMujoxcpw6C4Q8i+dhAcZqV/zLQcTCBL7YOCqIPkDGug7D0seXv4VlxoVT9hjOIm/EbZ+/II1lY2fBkhItoXGUYMTO7Pn0M7zPQk1GvYUsTpKgDAWMTkaJ1pUhDJPSFC78NTCEjM+4xaLEMEJbEF6wszihiXHZVhalvqz1VMetpmgJ8ULmI43R7oOSrkta1CTypMMcywfqqBXdZ49R2pFcq/oElti5DMRUCeHCjmrOrEAZzv1DCrRQ+UX9KGEEY4nZYIZ7GSmGDJO1ZWpSW83E8dPaO1/+cMYnvyBLAmNZYpyzpKMkbsbjv2hgwYRD5Mw//qFRK21ZtZo8DJbAAYiLIhSBgHLooREsDIX5ShqMWh8Yb2Ucx/XR90rQAsYytkypq9067bTp3bN++XWxBEUs0kXHiQ5lMWDSaNzSaBIY8RgQNBtyJOYeD62bCjUlJFOcQyQuisz3rZIA1FKq2+UDH4QEXS4b2Q08srik8cqPpdZNjOKyjHt8+5Tff5i9dDoLTeWy6MJmsJDF/kYTs/xTnEQaN68/v3joAnW9QNkFFANhpDKE/SUyB1I2M2VGYeZoFLnjGFwyGq8yQxmAGQxmMC342Wq///BFGguxz94IZNFWMGdJUJIc4JaG4zCOExu9UNqce9/dZxXNSuwCyxRorhYNPCdjV1wxAQMHuzVimpKDDGy0mnSmqMkaRz4QjWozAYkFmLgfQNQQM00pg1n3z9/EEszuacD6WPlYQHkuQUkTJ5hQEnHSadzVlvt+1x9eYr5IBoxotBpbPE40QSYYDYazrxQWXE3z2uwzYwwDpZYwNqs21ZMJpq4mW3pSzDrWrLv0r7+k60JbyTJshT4wwjEUnkyVhEWPNHnSugRG86DT0Og8+OBnb79kMPTxx1+NQSFLaIC59dZIZSYwsBD2o/AgG1am94/beZdZAIDJvGCiwCgGZkzLSksv/cWuf2NhFbY7WQh+PpnESEMRSELz/4ED1JdfPnrIwY9/X7MLPfzx4wEBABtiaTdlWDEYTSpJrQ0MjZNXlwvrnTXHoyLQOJyphddhS4lFI41ZdVXArCuE65IGBhYlMpJySGQyQDYOxrcC+4e3hS5OeoMjkAe/fPPNzzIPOWTmwW9/PxmUaVw+CwMZ25edrCcD0SB5Z8Q4LDA+ymYLB1CGAYBizTExxKQ5HsRCNJ2X40MLjDLWE/9NMULC4g0/wiQwhpCO899ZGpj0NKCN7o9mVxY2NjYOHvz4exORpb1d606nsy4VA0OKYKBcOCeqOZx5hBEM8WFGw1SMZng02pZFJEmTkcvoW2IKNhunLq5izM0p/D4zhrIABVlSxGBi2ZWPdJM2CVPsbv/0NNwLkpsLKwurx0LTMXjT42fSrrVPYAmtjEWowQwHmqh1YltV0/oMkjHoZ/MgI7OKmzTRW980AnXg4wd1dSvQBadD7ZZVlxqTWiAksAUl1hLK3LAs44Xyu6DMvnc2JLPTSRJ9/Pln4wHmyuEZGpL3GUvsq64ur66srG6sbOxAm5vevtZX0zoLMO5sEstootPGt1U1aL69gZldretgrA0namEFN3PPI+o0GNpUX2tDosVyRTuXYsyYJYN3cWhpxspbbvm65qDpPIhF0+NShplYdPkvS3c1ZzEODYpelF1eXj12bGV1ITSFlY3QHDz4yttTgDGfgoQltVppixE3Jk/UeVobTUaqtLSkpLTUFkPVNVWVMG9eUlUHRhOjgQ2lDbVNBlsnIBBTLeOGAoVDt+lxX8X+IBZoczAsR7+L5d3Zbv9FoscQyjCMdx59Kq+oqByrrhZNYWFlJUiFjR0d3wy+8ubVcjOpAolW1WLTMCeKetoPb9EySF9GxGpb1kaUtLHes6Wqtq6JtR+spmMJLh0jFn/AA82I0rb6oVaSXLzwsldx3D6LUR/88v033/ztt99++ukHOx3ySplPlMVty5Bd6UJgv/yiCerfoLjnopyuiubm5vLK6vLyoiJoKqUNMHABM/jNzPufOW2CRb4vdG7VgmGeHag9mtHORjkI+bkmmSdr/U0b5XArMPWYptLZ4mCwwqmtq01NnrzYkJNRpzG3CcxLD35OFn377Ucf5aqwe+55BzM0wbnxLU9mv2HvyJjOWbGruULWVVHRnFuRBwYwQzRjRSNH6/3m4MfPtIgXi654sswuXOpEPdNa2y2WeHlC5/TdJrKkEJZpE8cojJoaUiWsx0uxzonFzW1jEK5qTGv7UkkruTBHU6zLrq2X2Gr6Pl7yjmDuecps9OjRi45edEXZPW4rui0aFUTRojzrC/9cMaoob4YFAykzs1k05Qp+ozFpzNG+efXty93JpAssAQYWlOlkT0n3nO45F05v33pOz5wJ+iXpVJPSWo+mUiqwyS0lLRP55dbJ0JTUnNa5cwgYu30WASMvO50G7rfPoBHMRWbAxJaTkzM6Gr0oF+4EEZdtPAX8y/himgJo+FIUpCFsEEZQ1X2NHb0Dg69+N4HwDzQyeZmn7GjCtOkvy3rmXMhy5jnTbU8BzBKmvq6+RDtPxxBEWsKu0Gqd3t2+yLCePmvh14WF2VZFzGeO8tRTPCTNU0by1OgccPJzchaVKsiFRWCgin21Hw/Ths/m8nIe7mjQ8FRdBs28gZmvfsJbD46HjfuXcrMazfb27hkzZrCJSM8z5nRamkCCUiK/tq4BGGKFhQLj0YXSclpnTzdOJlMlprODYSH6HyT2pYuHzFM8ZMCgDyTQOE5ikogvCQ0wgPAhU0ILNMAor/X1FfYOnH/wK6ewFFY80z07t8coc7oj+r2gfIvB0zOnUzStWnhfT5quJZNpnWQLWsLSNvE0dtZMCroo+KkpPWCcRSiyiwTkMMIRT34+OCaHzCPHAkfmNEW5huM0zQ5jNDw6KpGmr3Fe/+DBX237ck/PnDkXIo8qGrEYTE804cIAA0u3fqN9Wk1JKU7GipimKtqelMKfec+2mvFbd384o9MzmeJlqcVZP3vkp+iCi7kuYuFJPKPdz5K4waKcAp4cxnH4nk+nyUUbo8kkC8Q0hcCIpmNsZWNZ2SEdvaxe+Yqo4FK9DBCKCyNlWMIMjnThNwBu104TcrLWFzaosEmRA2raWLepTUI9Kw/52MIUL+hiLLEubLMnKfOBcfUYEGbCQRhTJwLGTRiGBg1oeBo8zV1mRhPys302YjQ3M286VDBzgDELLD3RadO1iNnWlwODXbjb4akqQRgN6jSkiBuTZQYsU4ZY6GDDgjCui4cLLG4IkxMsHxeLIgI/KpAyHvoOBIlTFUQikzaZzc0kNdGEsPEcUMmTCs6BwZn79vRIGrQxc5QeYAge4RhJDzZ9Mk5WJRZItIBNMDWtp134ssS7cAmx2L2AFs9YeeXX2Umb7mMBJRgk2bBIEfMpLrMgLXIcKaYpaIYGdZzGHS2RRs8dHf2EzSZI0404UGA9vtx/RjTNKzfgZAgzvqTKWSg7MVhYTLfbdHZtfPhy94WHB2E4+1d3QLn0U9YmJTAe+zzA8Qc0OfnZ8ORHBoM6amHEw9UPo4mMpqsZEmgKRFORTqNHR0fHvP5vZu69LZ6EKwkDEEgUKdF4S9Leu8E6dxtTRewjS22dUOop1CaP3/rCHmSBZfrCScCM06ln2rj1JRGTtJZCSUzaSBxwiBtwqAm47AJDStMGSn0BBBNN4mjAhKZz6tSp8+b2D8685ltvTUTh+YsRb8FMcBqMVFZT2uAseBgoZLLWaRPYhOYsh4cxGI3zGYsayy/f/y3xMqVkIFBlxYv4XBEcclh+V3Y2Wsi8bSFlden7AGNJWt7mMEbD15hGMNVG0zF1an//wODBJ76R2P6xuTJOQ3Jon1iiXKy9BJCUUpjVtO7W3tmNjuxDnb71UqGF0S0ClMnWlJf95CwBBYMgbmN0pUR+Tk52ATg4lwhGU85QgnUVDJMG45+SBhIkscom0Hh6dphepPlm5mPnMMUVjO/SYax+G384LaTt6BQIpfJkeqo4GSw0qO1tXl4SMByBroChF2IsXsYElBgIvzKLWxTkCPJIm4q8vIqKgiRHuzZ8NpeDQzVTUABMbnMiTZwAenv7+3G0HaEJFmAo0QLLhPZpE2kabR9OjapRlaOMIHRStNGgdm49beFYGFYBjbvgXjLZZmLxtMxSJghuvvnmbEwJzKLEjCtWKhZOIg7hnft3GmIJacqLjKYicmmcBhKs2qQZmDt3YPD2NBg+pIwKztBHoMDk+unt8EMZhQ+Ze04PiYMStLXEWYIwsKx5rDKZUMTiMH035+VlZrt5a5ljpaVoiB4i37UhcYHD5Q/BRGnS4GjAJGET0yDNvH7lADbac6w4k8N8dXUirzo1EBVvWx0vPEsHuJ1YlLBBYYfjEs6ypG6B9DxNjHTRWhmxXHwxHE/d3Nd3yCFlZWVFfXkQgYQ6ArKw9wuOaSRDLjTNBQHGEfnswsdAMEdLaEJ6RhpgkOaFvWDhCAQeEMki76RhbC0VirFsLRRp5QMfUE1rrWlJLeJdGI6gRxicDBZy8tts/bnYWaRMH1YmntxcHnmZOBztvyJCviYMazTtGzwtt6ILRmCwmEY9NSsxm5MckIRNh/kZOeAxMBI7h49oaMOty8LDlLKeNBZeU78sK87KWmqGk8GCLN+BAgv25FMhmeVn5+WCUlRWWF4GjjUycWnsoYO5DgR4hdPEOIIhPRdBQHe5QjTp0oSgQZrzNoqNAzdkG0UEequZwh3jSaathZYHMNv2ScqmXHZhbrlOTramWEDhzJO48g8NJjVmNk6Wl1ukUhGcrmzrIP8LTaaaFL4PMDFjUTN/OnZeJT5JrabamYdJE/sZNN+cBcepesjEEzH+zSUHm2jXHh8ZUEJ2xvgupY33Ky0Sd8j2uUXCrKkdmTrz9O13kkoGA4UaMzszU4pkok+zcKjInAfzNB33ZTJzRSPQoIxebcbKpw70lhd0ZVJ4QlLtdUAsTf/cj1a/fib30MAE40SRbSKCSMMvEoOL56FBNJU1JZjaUMrohiUsZBbaYx/dLpT68vMzvuPwFtKy18uBRq6Wky8W0VDI5DUXcb3gqI0Jec0NIMu/okl7octhBsZ28TriQVI4FDUUAfjZR1sMnmcIQRiU0SBfFUTatoaVmNnlN6mawcACpb5qMRuNeUj32X59V1ieYTHWZzSXYnGYuPSXIhg4PHLyAatQFs5UGwONB0hCo64YlU0CA5mkKRo7MDC1nIxGpWZjaYWVCQzSfGR+lrCgzAitRBWRdnkRGZiu3nsy1AH6lwkzAhaEeeh4hIHlwWdAeTOBEYkngOzMvDwcy93Mc3N+Vx5XR/mCNiF0Aoy0EY0gExesIGlXzxvo7VBGyyObFFZ61FACjKXYBAZpDhpKAK6ML09nTBZ3s6JM47CgNDXhZcA4DeItIRiEAeb1557+/vMz3nxTLOkoWL5YyMnA8OHtpq4w0zrE4gEGE0dCg6MlMcMIR0FFbkX51HkD88Zm8pdYebXyW4doKh1m7urf3JdII2WAgaW4romyEvcCRCTWYYbOzdVaABa7k6YL8z4WF/5eyrghhliMRs6WZzjmTsRyMxcdaFwGHzyDJrMg+FmQpoJ0NjAwrzy7CzBGawrNz/AyS2f9c7dY/foXTiUFDPGcGmlbh3RJ2TiMzQbAIU1KZPzQk0ADKLAgzPx7n3v6QdX93lcOpYwXZeRjYIr6gKEEEBZfuDbHwWO6EhouPNAkYRM5jZUH1aSAgViaXGDKqsdOVTYjnVnQzB08dbgdAQx9MFjGkMUUMsLgIQr7kaVoYMYhzNcmDCycCQCMWn9YvJZxs9afxhLjaxEs8DSDE3qZXHSAgQZA70Lrzc+MoqCMpOmiYJY0U4sknJpOYCBR1CANMKSA1Y86wj7comLCX2nLd3inEEhmqVo/Eo2ESe0RCyMn0+ETQyyPPwmMc0CCadKOtq2xGqCiZliIZRC4VO/IhPYTEx4mJmjQxoDNVOsUVU+dN29qdUWXpLEWGGkCTD8w1+/EfURPwoZgLEJQpk3tvFuL2pxkszStTmoxgznehFGP7H0f8n/cdDGYskJAMAbsNXSHVZeVIw+tJg2NxBlt1+o0jhPDQJOZG9NEwfhJObFeWZ0LjGgcRiEztgM/++itj3Y6XyhOAla0Ql19lQwaLtyVYTRGZjDuZ6VbEjIuDMcbMS/0pbG8bbI4jMplPgqDMcJlg/eF8KBNbvCvUMwIBgulTUF2RV5mjqWzHIcpkGsxYF5Ukekw5IBCGpkOmYLmrbfmfgNEYlHWiAYFCcnXG8tSFwZpDIWfWS2zbJzK5u/6KYPkx7qTqTCDxmGClxEyMhSqlEFTbt6mLoFaE1cj8bQYBsWstQk5ASaiBi0EQ0gprQQ/CzAEjd+A+lQ+sSirzmZj6fM3QERDLx435WS9IlsCFrzsi3d3fW55ujGBBRgswBhPn/IZYU8aAEgsWFFzXl4zZn0xG58ZToMZTRc0QMRwBfmIwbyGYCzf8z3vUEelB43BDKilSVJzMZuIOcRIc+VNzFlqGraWGJLRcmonnqxuIcEgzL1sT1hT0S8WEyZRxi3XLA+jKCNoQFHceF6zxCZx0kqBkAQycUV8LI6i/GyKUPPQTDlarnTi7bEezdQYpv96emhmXs5w/cVKaZpWZjKZh+9chQQ2seiVxczLbkGY5TY71oWhYH4y8TLLynxYC+ONJh/4BkZSLYNDdCrRYDAa8ThNkIaLtoBRssihXpaa/FEuOK6SnLfRYQbmXvYWMOo7O81BG9HOaCcZ+8ZWyNBG9Sy2/BQHGKcRy7LuZfPv3XXjRBhXxlESYfKMRWW/mdxLV4E8eoErU1sPAeZzaXI7p+lCAhuVVquZA5r+1hT13pFgoJkqGk9ncwfuvDOw0DljzfDIDC6X/RFapMUqhmJIbFATo9yUNQGj8OfUHAnzJSwuDKqYMAlOngG4nwlGFXQFDIXgNJtSuV2igEDmNO5oEoOMJm3kbXRXM3kf+CP+xv6bKAxMR4CZazCOghEzrC9BC2yklvhw3LzWZ4RahkzmNg4Wwp95/eWVyoIujnLxq6AEHNXKxsOna8OwE8Un7yo+ZtrkZedEo1e0hGVYCY2K1HxobO4jP7dI7JqBIuJUOnszBolg4gxwpzAwK2dY3mNb1lcYuRRfcbF6SJSc3Vq8r7mGYP6cP/vdjVdRKvvOox8Yx3HL6ysjl4ECjzPxiXF59NYsetwJK/K9utFIh/cxLZfxa9l5SMNwLjT59OokDf1u6w45DNmRdtNgBmhogPGyzO6bGHEku+7EyCqSJUYVUzunStSPZvgsnMxlPelbVJZ9wcqLDSUMLA7z6nCYbIoA8zJDyZcgTpNtNKpB5TReSueYp2maw7kiTRXkSBrYsS7+CUlermjM0fTXhbLKWBmCBpgtvPWXfRwhimiyMlCmoRQSQHyEBvPVKYzOAEP4v5sIA0oiTDAPGSwe08wXFiYux1GOjmlGY6JxbUbzclf+6NHZTpNfwLuAmJKGzMF3zWpnJI2KAFh6e2MYw3CLtAMSV8tYfOHF60pxr8mQaLgsGDyMNR0PzC0SZrPTv3QnY18ZHEgTgoUnC5lAkx+bRFKfZrTjNLs4yGU4ntIkEsO5EMrRsiHLzyRIlMYAyoW/qLxCNIKhCBCNBc1bA1uIwu21SGsAyWJZ3OK7XpOXrQwJ2vVjejI/q3kILyNiCP8gjMOAI4hDKMwOoYTBzSyHgSQOLthMQFy7YNTFkTYVYSTankWj30IaRY3FGB5GfFlibM7LzsXZKCoMptKVUS/grf4BBzFxBKOjQ0YuvMQKVWPafKjZh5qCqQOg8JeXkZeBgSUIwxHOcGCNMqpKeNzLZDb/jzkXXwJNdpDGXoaG70xEKHhVyuBZ6t0xGAKWpMnNK/s3mFA0EzeR3+93VIZubwaMOPzsGqwBs/GABsG8K2E8/OVkr+BioPQlLDGNtZeSAnMWuY1CSDhG45E1RBNmP80deT0brjxiRTD2SezkAkN1RLUHjLvZgMFQZcZ15lER+54VNqTkKnXJ/NAqVWQqbYqDLUYum/3uchzi4cKA4oYuAYbyDxh1ahiTFQ9X7xfrHI6X7TSmjWMkyDmiycZTffgQBLVXZGhVD3zmlpWBopjplQHzUf/AXmbW0ETxwRsjbKLMScJ2ddttj5HvgCFkll/ldCoZol8w4AjlYGfxgqmD7qU8IagjmmT63C6et9/Kal0nXiUHi/w3Ym3y9bJ+iZzsCnp6xs9oaKzbJ5YA06/JDE1qUAVo3EzmQ4F2YNUyAmHnr5ZexrvXM0jM79L6b3asCTOkjKEkMB0Gox6ZiaPgGYoaDyIPbQxYKeDSoFyA4R1AE0lCJWZfmWHTcq3cIhw47iR1QGJBA8zZO+64/477YwBFflCNRpp0jhiqCEX3nvKzbBw162uFzMbyMmOJYYTiLIc0wiJlrOAQjg1n6M3nagMLZo7mVbVo/WVedZcEBv/juuVWdB4EgxUVlgFWVmbCpMPsdMKOmLEwpVHfYN3MUqw+Pq1mFAvkQWG9rA0NYrWLPfTnbDUywDzuMBxapkOY4ElCBhYLGQExAGA0mX65TpPjn9CYH6KOacNPYXEYTGVLkTUqJg0i6b9aVMbzP2Du2s9ArNQ8NaqiErOOPysWOUTQXcxRaqkIfIagarGvv9j4XbzswTe/AwWWNBRNl8XZrEwwMklk4ljScopg0qaZV+Rt/qoUM2iLeBSAQ1ePdtKR94j/Ks+8RzbY3Ou5efUTN2AvMqbwJ5uVtMh07GZTsVgwRb5VnFoD0N5OEbD2Yks+NFte9qWlsvudBWsEpy/EDCMYonEYcwrPa9khpwnHa7ZMm1bTdcIqRULQ6BVgpKuuPtdhyqvRxN4fW+gIjCmz03n7nXiOMGQ0NFGNn1g5Rk6WlSGOkaCQqhtSba1hBcCWX7Nx+t3NaGTEAgwsbiFqzM1QxsxZeGeJHaPJSVzNptGyjYZcq1gXp36ISTT9kGuuLoKmukyFHDWmj/MkMPMEc96O++2340ZAmKkCsDkzrWKutTTmJzzAwurMiad1Ms+s4zR31qjZgvPXlDCwcHhZQBFNn7NgcjTpEUw0RZYGXJsA4yU0QaUuMRj+U7HQWsqt8CUbGC3P8ylFuZfFIiwGM2/eCxyJBMwRcV0miyjEmLFsSdUTMCMNRWc+jcDJWiayfMsX3D3x9SJfL8BtSl/6TsIQMQGGRyKMsTCaqVymD1k17oHjIM5TRhNqaGBUD5s0IBiNv6pIKmMMhogvl59ZtiiTNMNgel/YDxRupH3fUUAEi1QXj+dcIUWMn+UCi8NMZnLWl0JPemiRr7mJeVbtE2e8DQrnL6e5GRDeypj5yKx7WuxqwoHGrtY9SgoA02xpDYzQWVBlgGbMxBTSuJDDBINVShrLkrAMHrgfJBvAct7ck157TbI4DJPk7dwWTOG/jDeRxTGMjWt6Z3PSnw8tNoqb0LPW/PBZXxH9QRhHMZheSwCNDM7yhPHOYjxJG2gUODEM5o6GqTZ2DL0S1zEEDatly6ApU5ZAwOqxeJo1M4NnQQLKBsCc3T+XiDGzQpNFzVtPEwx7GuRfVpUZjKpllZxaQv/n/D8XympoWKa4qmbilHPvCMI08sG1G1AQJvAkoaN6QGkrjUZtJ7pxnXERbYW0nitQRkHjf0/EQQeMxuIbZ97uoviNAq5n9NxWAGDKz4KZzgpTwRRbaWMVJonZx2bqYESuP//8/ed9GjjybETV4RxKeesme4qnMUGxyizBkTYBB3G8WCMLhODA1HSSrkw1Y8F4Mhj5mbIznma1AHS0LoO3P7ADFm5bfeBAPzDfysJ6s26U0YmPtSKptYKG0hJpnEsZDjv+z9k/Hvvp2jYJNWaXqzkxdNsTNj8mRH86TKGMxft8cq34TJ/VVQoNhU1CA4y1rAKVf8WGYpIGr+IrNBIQSR5wEt0iQDAPfCOWuff1YN09vmg24rRcLZdtqMPCAXwKHUConUHRMTPA/H7lse9/9/1qnBd6OPsCTuZwWt6cuzafKX3chMLVVUoZw+Fh4jAwaDQOYYFhRIp0RtNEI1GsINB4P78tPxMM/x3dJnLVu8x0D/HtxHIXKAMGc7YtOGWRqYCAmT5tMiEjFjyLLSWwYFmqBqSK1Ta3OMzjX83aeUzNFA7c44iqdcSj+xvqpLw4MWPW1BiNa2Qwuj69/RJGNP6tb5QJNAbDIrRM5i9JG7wp4rDbU56A6c6ooGAn3E4l4yxzt20PC87BiV6es3WrvMw6AdoMZJKgSQAhXXNYY4DZd99nb7iBQw18y+o6x3Vvu8GMGYTkgZsKifjB4sosZrJ48Up4WGPi0sDiOxjoIYuFtl4ToofQfj0GBhycLe73dd2ET8GcsPljvTSZ/bAoZG5lgZmh9EiZHluUXY8w9Zonh6pYMMbirY4GOav+nH/l6cDoOO+Zx9x4Cjs8g+nG85JIxnnYdtSkkGKhfFjAU5IQ3NEU7BiImluzpGWru8gqe9uJ4iix1gHbQ8EdBnTvZFiAWuuxxwhOYEAxZT5yUV72ZadRT+dpeBkwtdr247Wz9TTRhDyAFY/g9iBfzP/5F8Hsjc3Ejtnm6HXcOHfvMO359ntHWf7HvTmBHS7IMARz6/PBwmye3NRTle2tG1TJndba7oBTzjz30KPNwDBzlu2POWawdx55xmHcy+4UyIekMuueRcplKfQYGgOQp4lGKNrjCAx12lXzf5Yyr+huMnaBg4OPPbY9980+NCBBBNJxx3mIbgAZBpbZ5rJrzI4J97A02x7jJl8WDOZP3IrtpRcdAbvtlPUwjn8nz1SOBaN3CAYWYOCAgi4A02dHRReexu4yLrlOe5ho8q1+toTGpBMd6bpiYKrGpP78+ZcvDWams0CDzZx5zfbcCjwQ6S7avqEdMOzo43DzdEuumjuhnHKbrpUbJOuaD9PVcxOmWZfO4rbcGD/gTilq0Rp9wXkvML2CGQgw9GOsavZSM5o+vqaEHIYrwaKdJcoGVgRoOQ1gwNQ2lLTEMHIIg0nMiLYBSUyC2X24nQKaLgwTI5QQ60fcrfqDZ2a9eC7GrRO1t9/tijOfnnXFKdDduCcchzRSpskEAwUwEgYUwfSDMLw24+4fpbDw7pe02V4segO4FiWAWBAGU20zf7Zg8DLdg14EztM7qNlftOcn+M42Hq+XpCPpHAKz8IMrdBOsFz947+n3PnhRNusDXjM1gHxv18+/2vemg9UO20pmN83LCgZLYFYXSbA//ojaCf86jwu2BW7d3tpGo0M1gC4liiUNREm2q353N0OagxNpgBmMx7EFRfGko6Z1UptuQcel2RU6CwiBkK/gvPjMS8+8+Kzsg08e4cYIhIZOEH/ynYsL4+5kEEXvV0cCMxBgLhNEYHnttb8AuZL1U3UpUqQAAAAASUVORK5CYII=\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Indoor humidity card with background\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "humidity", + "indoor", + "moisture", + "dampness", + "wetness", + "humidness", + "moistness", + "dew", + "water vapor", + "condensation", + "dew point", + "steaminess" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_humidity_chart_card.json b/application/src/main/data/json/system/widget_types/indoor_humidity_chart_card.json new file mode 100644 index 0000000000..c9fc6a75d8 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_humidity_chart_card.json @@ -0,0 +1,41 @@ +{ + "fqn": "indoor_humidity_chart_card", + "name": "Indoor humidity chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a indoor humidity data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'humidity', label: 'Humidity', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: '%', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'humidity', '%', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":30,\"color\":\"#FFA600\"},{\"from\":30,\"to\":60,\"color\":\"#3FA71A\"},{\"from\":60,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Humidity\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "environment", + "humidity", + "indoor", + "moisture", + "dampness", + "wetness", + "humidness", + "moistness", + "dew", + "water vapor", + "condensation", + "dew point", + "steaminess" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_humidity_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_humidity_chart_card_with_background.json new file mode 100644 index 0000000000..96518e9bc9 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_humidity_chart_card_with_background.json @@ -0,0 +1,41 @@ +{ + "fqn": "indoor_humidity_chart_card_with_background", + "name": "Indoor humidity chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a indoor humidity data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'humidity', label: 'Humidity', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: '%', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'humidity', '%', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":30,\"color\":\"#F89E0D\"},{\"from\":30,\"to\":60,\"color\":\"#3B911C\"},{\"from\":60,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAU8AAAELCAMAAABu2vS6AAADAFBMVEWuc1CxdlLs7fDR09ba29/j5Of4+Prd3uGrc1HU1tnp6uz9/f3f4eTw8fOmb0/m6OrW2NylbEvN0NT09fbKzdCpb06faEiia0zJuqvMva60eFTPwbKscU/Fx8uiakm9v8OcZkfDsqPOztDBxcnHys3Htqeud1bKy83i4uO5vcGaY0TY2NrBwcTR0dLRw7aOXD+0tLbFxceoq61GPTZ7ent7ZVe6uryRX0G2ur5zXlKanJ+wtLihpKhKQTrArp5WPTSxsbFNRT1+fn+ioqNuWEt0YVe9vr/MwLR3dndzXE2+wsbNw7qzt7u3t7mDc2qIWT5fQzt/cGetra6tsbWnp6ifnp+VYUKNi4p/dnJ3YVOTlZh6Z12FhIWPkZSHh4mwfFyqrrJQSEKkqKyCeXWFdm6LalrQy8idoKRbTEKWmZ15a2N+bWN+c2xqXVdROjGVY0diRj45MSyZmJiMjpC9qZrLxsNnVElvZ2THwr+Kb2KBgYObaUyGbF6DaFiJf3mqdVaJio2Qbl2VlJRANzCRkI/V1NWadV5rTURgUEbJyMqFgX+CfXpnWE+Ng35fVE10Zl1wT0dUTEdjYV+VjIdtYV5NNi1za2ltW1CelZFtbG1oZ2eecVh6dHGvgWWPc2WsqafPx8BmSUKZkY2MhoWIenXFvrt/al2idlt8YE5bQDe2mYmppKGSgHleXFtycXKpe16WcFeCVTuJdGiTh4B0aGSheWJpZGGmn5tYUEygmpd5b2p2cG7AuriLeG2dbVG7oJDIurNiWVVWRT2EcGajf2ing26yh23e3dxZVlTVxrtiQTCEZFDWy8JIMSmzqaKib1HX0MyVZ01uSTa7n5mRem21m5WPalOyrauNX0aumJDb1tO9raWto5u9trOzjXczKyXDtK2ljIK3lICrinetlIumnJSej4imkYuvkoG5sazNtKF5TzhVOSrGqZWbiIHy7eqehXrVv7Dr5eHDoo11U0W/o6Hhv7LDnIGZfnLGqai8kXPLr6/eysPk2tW5gmDet6DEByRJAACaxUlEQVR42mTZXUxbZRgHcNJzanOas7SpvdCLpvG05pS0NS1Iw+aYNJUaJyVS24CSGCsEKx9Gq+vU6GKQKR1YUiGACREF9EKnF2oc4IwCmmliliXOGCeL39OZ+J2oNyb+/+97AI0PjHVrKduv/+d93vO2bu7brdMXv/jinetvfPbDM+f+/vs3VAifp06FQnaX02az7WHZnHY9HDXjkUg6Eq9UKvEHnn+gYhqmWYlH0ulIJRo+FXL+LMr5W0jUqWg0rDttey5lXSK+7rG57E5nSAn6E2l/IuFPN8c1R8hld6hKMBJLpXKpfNfThcaWZLJQqK2/sb4+/WapNBJUVI+iqLpud6JsNqfL5bQrgeZr9s8XqtX19fWJiWq1lmy89/N/1733tqw8NTPf3Ts+fuzYeG/32szrh6enh9er1eLGxt2irr12Y6NYHJuYGG6bnBwYHBzs7MSXgZ6ensnJnsHNs798+c1HBy77Vdbll//ww4H773/740+/++7ChZ9eW+4bWhuY5BOuJFtaWhobH0M11pVWT5w+ffqzi1+8Q9C/UafgCQ2A7nra4OnQvPGIP+2HJxUjcdMwvLiFMo2j8HQ5nT/bfnbS81TolCMUCu96svA0dui53BVhmUgH4j5NddhdLl1VfJFIdn/25obRpwvgpOdKFVgzI/n72j2KV1N3PfHFZdcONtz1UDVZw4OgWa0VWh6TnhITnI8l33/jzd4jR55cWloC6PzazMzhN6aHJybGihCFJTSFZ3litg2AA4PUJChFe17YfAueHx+48tdfP/jgA4JeduUVAIUnOY8v93UOvC48C8lGcDaSc4OeJ7a2IIqIfvDhOXgCEiD8ze78t6carYATYfSZpikQo1EDNysV3IIn+VHMtizQgZNFTT6H7tZU3eFNZzIR+FUUBZwOu5OvVbA+ELg9Vh8bmakJToCiqm+M5G7d7/EF3Q7HjqeNnm7f7XddvZ4sEBSRqxUaH7v3XopKTtxurK0fnj+2hOoCaG/3/NrazOHpYYJSFCU56bm3DYFkQIXpCwM9k009L7z61y9ffnr/D5fDE2WBwvMbcL52vK+jU8azmISmjOdGsW6ktLq6ip7/DKBXoeMhSotz586FHSEnCiQ2lDMUNhBPv/D0miYQFU0xAOo1ooqmwROgLpcdmuFz546Gw2HVvudSYJJUrhhIIQzJGVc8QW9YVVWHqiLXLt1t+NNKwB9I3DVfLYh4whMZPbzaum+f92BA1ZF3l9AUoHY12H7HreNVgK6MrYwVC0l4AnKX8zG2ezfCOTU11YWA/hu0XAYpS3JKzwFwsgja09PU1LP51l9ffvP2gR8Q0D+siF55xf0f0ZPx7EA8h4erKwU0u+RsGSvXjazmV+ewiG7JgBKUnGfOnDnHDqYny+kKaWYAmtLTKxGVKG/gFvTY4Cx881EE1zCimoPNKQUYcLcnkEDFWmPxsEPzKKrqdmua8ERATZ+mBc1IfvwNgKLgCdWZfHv+1oDPh24PIe18ba1uUXzNuVJ3jY8r1sgpPeUXehbY7ktdU1Oj8HzySK8FikzNTkhSVrmMeM62wZOcHZ2bEAUoPJteePDsL9+/+CMDuptQBvTCT8ePvIV2B+dEtbaTzsbidbN1+YWF/NzcqgS98UMBCs7+/jNHwyJwNEG5dC1IT6ybQakZ1jSyCU3VwTrlYLSPRjmmsK563TqfYPsVUT2B+lhrdiGXyyjob59XUTxe09B0OwZMSFUUhFXzppZm1lcAyuDVCivzqebUyDUej6rb7XLtsSH0YjX3BJobRmb4yCQ7ju0uKPk741mD5/jS6CJqdOrJI8sAHeIIAejw8CyKqJCdJWdbDzw3NztEbW4OIqBtTez479/+kQFlXUXQK+gp4knPdcxBmU52e3l2b11rrDW3WgLoCQl6BqDgPHSoP4qGt9u5KLLsuuYNRDiNfKbhEYhsdwM3MCpYaN0w62iUQyqdjpsa7uAzCFCXO5Bobc1ihOdjQUw2xNwXwICPWgF1gwxNkO3q7Z6pQkksotWrU4nb81nNrWLtgaWYamKf4NAUj685O7Le2CL7zXLkL1ZjcuXlw28e61qcW1wcWYTnkV6Adg+tvf76ZNtkG2vvLIucjOcLjOerHa++2vdqxyYC2gbPh8/+xYBaoGLIX3FAeL7V1zEIz+pKLbnNmSxO7N1bl0lkWl86gSWUa+gXNz6743nIOKpiBnDKID/sSAMzJCLnkOREPD2KW1XR6cITf4O7yBnB9I4buM+BOyjqRLv7W7PZ7AJAs+l43Oer+AKR2O3NPgSUT4+B47I5texqPjWyVpMzPlk91lBf33A7xpbLxnjSk1/oCdBgfcM4Mfk/IqWlyW5vLLz/FMfRHGpxcQoNLz2HBpBQWVJ1mJpyvHdA8y3Wq5uDyOctTS8889fXDOjl1oznCnr/299cOL78Fjmn0e2iNeRsH5tAPs8nTp5vXXjpxLffYsZjz0ROejKgoo11aEhPry9y8GDAhw5XNBQ4ZTzxGHI6wuh/FjzTiUza5/VoxJakdl1JkDOLhGYzERN7oHggk21oTRiquBs/yrnHqWVi/vrcsZWkrGpXNuNvvllx6EwnPSWozenQ3AD11ufXhaYVz51qRLtjHB3rmpsrzY0Iz2UroANWYaO5I8tuh2dfn+BE+NDw8Gx6+OwvX7/4O0B3N6EH3v7mS3h2DiCeE2MFEU9yJotlRL3u4nmMmNgnC9gziX6HJ6cRGx6bSnDobHqMbeYzEEA8o9uanOwagkqz/3oingk/PAHvBikL3xHAKILoQm6hNW0qWjCSyOZyWcx6ld+tulWnzaXU+33+/d21Fga0Jbl+bH+uPZBWdLvtUukpQffQE6CK95rx7X6TkNZwb0xi+aTn4ioDKj1ZQ5g3mOQDYuxAtUdsNqG7y8mAdg6i4WVA2fEW6A/Y00tPTiN4Fgvb06hlAxsFeH5y8fzJSPrk+YunL2LAP9sPTk5oo7/Sb0YJCk8WBoY36MOkNRRqYrKbpuB0Q4ygDjXMvwehYcb9CbhgG+DxWFn24Du5CmTERGpN+xQD3b6QSsHTwyUYOqrd7vBm2puzXW8Uqi2s5MqbI6UsniPktMFwx3MPPPFj3Xjmg6WV3YDKiErPFeG5tDi3+i/PZcykTlEdLAt1kJqDHfTse2v5LAO6KTxvaXpYdrwFCs6dfPbAU7Q7SsSTG69y3SenP7l4MoLLx5Mnn3/+2f4z5JSelYoRDYchhU0QPMOKYQYDgSDiSU5uPKNamIayq1XeZlwJa1TiAWz7g0GQsrwH22OxTCwjqrU1k44E44E0RmHuk0Qc9xPd7QBQ8ObUvvvmq0nEU4C+fPXoHV7Fq8Jy19NGT/l4tze3JvNJzB3Ox/BSrNOT/S4WUHguU9TyBJ74lLgWMDRRy/zSITz33tL04Nm/vv4KHU9RctLzl+PH6dlGTzneG1vETrY8Bs+XXvrkZLwfhRbn6hkOYwPZf+iBB+IVQ2PyOJRCehiRhCfanVOH8YQshxKB3SiN5eY+HhXGg1E+q/yxbLYhh8pmW2OZhN8fSQOWac1EsG/COsyIGh7NGys9Or+eFEXP2suv3Lbf79dc3HbuetqQTw0DUFM9zV21/62f8CxIz6nFOYrueHZve/ZZxZTKD/5JYiKh6PemvbM3APThs++KJZT1K379uO3ZBs9iQXoynqhiEZ5bJ7YuPm8chSEK206QCk+AbnsiomJ1DAbipiIHEeOpUNPwsMdZFCVsmPlVvGbQFw9E/Kw04MROKZ/KZWMJbBPSMaQUm/u034SL4kEfuL1BryfYXpp/qpjcrhq2TE/N5+sVjHfL8xLhaUc4HTp+mBLIT9NTcO62OzxfhqfcL8l8krOvj5yDwnO5b3lZiMJ0CKwWsIwnB9Le2euuu+6WRx48+973AKWozOf3lucwPAUoaqPI2tigJy7gz/eDEVNIgKL+56nDU4End+HIJsuIQpNXSh6WpJQ5haWJkxLBWV9fn6hPZACaS+0r7cvnwIlDlUwrggrOdBxbdQdy5nB7zKBpBHABX8V1dVJ8bKxwDzoz2ux2Onc9uXzq2PxzhikeT67X8uTFpkxnY6PM5/z40iJLrp/gw36J42hAgkLUyqiluRtarp97r8PFkwX6p5VQ5pOeyx3wnIWnAMVmaQOYWKXqTm9tnfh26+Ihg9skiBKUnpU4PcMqOR3wVMPc72H5lJwVxBPZxAqJKY/iIooPUYAHs8n9JejqUWm/P5HJpkqlUj4bEYd++NMCAspdqoon52wxgnFfMJJdmsa/TBaaqFYsDPeO3O7AQR9Atz1t3F6JXYHHq2RHC9JTau56Ts/g+mhqcQoFTswjSA1xAIlpPkTCXsuvb0hElFGVCwA39LMT5TEB+sy7XEJlQg/8yHl0/HjfAPM5VqxJT7k+NbbUMZ5s+EPWxhMVhac8kTM04UlSemLDhCb3WGdK4OQxU9CEnXd3YxQOs989BvodnGjtQMCHiiRa9+dLpX0NZjgK61gqD1AspREzjMWEUfP48NBEanydntK0OFbcKM50jaYcaHDb9v4TniGHDk+U1+dpuHNYem5rwrOFnsOH3+x9cmlqCfWkjOcQ4glN7I+QUAJa6RwS1WkVyNHtuHwSl/hPXNf08OPvfb3T8dueg+LqHRdIouPlXIKn4Nz65Pzz/WdYBDWYPHpGeMUYQq+jAKV42e4c8/Q0IcbHIFQgVcjpYElULQrPeITcckEwI7GGFPKZ82vYjKZz+dXVPNbSTLpyVBdXV+5oAFPq5pE3q8AUpOCEZ3V832hKdeoOJz2t6a7r9MS2GB2zL/XmYyypCU/GMyk8Z7rHYYliPNHbNBPbeIjKw6QhfsqygjsgCpyMJ/r5ufINO0uoXD8/pedy5+Q0PQkKUZT0/JZX7jiiP/nAoX6W5Wlang6OdjYXPc140KMQKghPlI/5g5nP3L60JP0pleHRkF4fD/UUsUXVPPEEVtBUDoaRSKYhlV8trb6UxXivKI6Q3N8GcEfq2DQcrSJn8fBofjSn6qoOT8HphKd4Bey4TvX42/ftX4LhbvG/Rc/q+jQCeoSWbHa2u8XZ1gZQ4glECsp6fXKneHFfLvNlvYmgVseT8/5tTxwmr68TtAjQnXwuzhEUB/Qnn3/gkCQ12MoERS/q/K+Ch55od1OcCWFyVzBt4iQnKFKLa317COXgERNjKhKK2S+PnrA8mulMa0MDL+BZ9CydWIih3xU1JI+UcT4SQzyF503F4j3FMjzHevP5rpjDrcBTXrk7xfpp13UnroI9/jvu2r8qD8dJKQuchZoI6HwvkykOl9jt9AMWCqIsMk7+y/FfZyVl/niC3oOOtwIqPS9sew4DVCZ0g6L0vLA4R8/TF88/L0Flu4tmjhuMDjm5p9SCBwNeRcwabCyJiXiykFpNNB9F4UlQBjTKaMpTUbSzEcHJS4yXR4gpPtHw8MQ84gkoPHW3YsRbR6eBCUuKCs/h0VSpK60rXodo9z0unZ4uOz3xXV5/dl8uz2xYJdNJz2p1ePrwWnevVYITnsynBJ38d+GtJatmWWx2cl7L95jKex95Brv6t3//8cCBA+K8Dp44qOLhNEExlFDiSKxu6sK250kk9IFDFTMqPA16RoUS9krwDGN983mEpwlO8daH8MRBJ44urcP5kHUQqsJTCcuLf+ty1QRoAqYNwEy9xDMtelaMMF4LeoY1rz93bALxJCg/xsrFYmcqNdpl6p6gDs5LLrWBET2PaOo6j5V9/vbcHXmuX2CUZXHCkwEd6u7ulgchfR2SswfpQ7Xt1nAbFAXlhKgyS7wpci3ftdsolm95mEfLb98PTxzQ8/2OI0NrfIePEWXL12pyfaq7AM8t4QlQ9jyWPIJydlfCIRpZ8fRGeClDalzHg1PsxuMovrlpHeuFWMwotwMMLUtI65jffAkS2NiDM/cSRKWnRlC7jh9wMDuyNsZ/F4uq5WL5yVx+aVTTvd6Q8HSpug1ND0rhqQcOHmxvL8GT6RBfdt59WoHn9OtrAIVot9i3S09yysKtWVmWJAfMP2ybXUybdRTGiaU2NF1oSG/fNGmbMAJLCmrD/IhoNzIaMFIhFJ3G2LoYLEbRjWlc42rnRkuHChQumpGtw8wMjRFkg85EpsuWGImJH2TKoovOSbKQxSsvfZ5zXtpVffox3NSRX59z/ud/zuGYitU5eE5OAuixqfuktaxAwXNzcw7/13weSBfNqGdjevR41c2bN5QngIpFRxDvmj8R7lY7ecqcx2K0e6Xm5B+F3G4W6biHy3DzMuwpg1xrCSh4avIlTwKFQZvhahgUGXQmvIZG00wb86f0UHFgYX6Ryo/qPZiVirwNDPUk712yOXx16DDdffe27RhK6Wyvulp4en3ezj5FqFKYwEmeg2PL8XgkRp4a78qzY+cUHypxJDxZJim6aOL8BkBfOLCb02M16Oo6Bpx/ZKPiepmaSqpgCtl5vOrGTeAkTxGQHqxXnkazYYE99TTC+eJpajc8klmJs72htcsPHG4c7js4O9K5SIVBcQCTr8nThmuTCyVUADm0za/qgsORhd1e1KddPUOFnOI0Yw6D3QOJYLBQDFttPotVwt3mdGxDRc92vxN/oc3wuXwtxUfIsELngZM8JwbiABoBUvzKgBecuzo6dk3xwZmHSEmWQApMBDtwgudLF48Nvvj537d//+G1137+eXX9BnjOJ04nEhkgpUchqbV2g6dpT2iNutY+4vKw3WEYLtqTPCHa0z1C0C6WSbBnK44WNopG6o8oT6wzVPBsBE827nUKZZV+noete5Vb3tnBY6ckeH+yUMR9GBgQOVup7NhYsidVzAdqnS6nfdvdCHdbdYmnxY4uEw7H1reuKMzzZR2nlOcyI561UkyOd+UJvYtXJUspe5UkWFKgCU2C59RADAa9+p3ynJ4GTxHHpsVlBAHvBt3kqfak0GmamVlDRSg86z2NDmAAEIr2bPbgNCrxxA28hTybXeB5eXvZoIqTPEETIlA88JkwqnGUlYV7J9LpuaFUXzq6PMFFg+OCUwWgmd5gejjdXOusc9gZ51bwZBVKni4kUBvuGA9+TJhkeIUQleUKNQieYwPLatDYHeEOmoR5FDBPHjv5X1sSJfWN6O3Jh14YHXvs87nN339Y3eI5noXSKG4zCPpI5DT15dhoFXGuK0xelc6idy6LHy6PpZr2JA9YFPZsQoMO9tRK393Q2daiPLkbAqB/ASgMqjh56WQvheK0g0UC1YjylcIFyxhh7LfN9Cb70pHlBVw2YBIhAQoTuRx3YyZSwWRx+JSltqbGKhjJ0y5Ffa3T57RjZup74K33z4PmlStXVu7UKB7CUw2q90mT565338XzXQA9evLkyZdffrnCl2SpNJXnKzTo8Ynu2NzmTZIiTzYACwI0ncmwT52Ym5ufeww8103htF2axpwzGA4080a+g8eJ4CQhpyfkNjzaRlaeAX8YPHnCo42vQO12+pMw0VGx6FSeRxl4MmmYIzuKd4Jm+X8El05lo6DJNQPTWKA5DE3g9/JLvYVcscdRW+M0eTpsrJfIs9qoc1gtLvf9w1eIU/xIyRcq5QmD0p8VPKmjpEmejz/+oclSJChVJk/O2gYHyPOGABWeBYpIEfOZRHaeKyMDg1VX19dhUfSYltAlTKVSQz0IeLTbLI1ayxMn14vcKJZqCMInEUsW4bAfDTfy/AkCUOgyxsWeffukA8W5HdskNlal5EqeApSnnTvASfWp8UQUCX2Mdw3SJM5F0MzlQHS40Dt0bzHjr7VanLXguc2Oz1d51tbaDMPidIWeffTKeeL8iP/h4iI+BJ62eIdKPE2VeR4lTqVJnB+WEuY9k28LTb7KeumRF1bAc/4PoDL9ibZVYR5AyTOTAM/xTfZIkD9VID5fSKcLyaDfbaD3rrU4YZKCyxvCJUh5susuocr2UCDQxH4UgTKJXmbjFJpth5rR1IOnAdTmBFe5dhImr168fMKbhWzGPCCH4aw7cFLDxWRvXz6farY76qprucagPNn/tNY6jAa3t/W5Zz56T3C+//7i4jC1CJCLeJjiwSsw0ZLT27vyPEpV4KQx7xFz6mNLvyCDPnTx+ELs9DxrdQg8gUtUYAYVe47Dn/EB8Lw5rX/MP8ikU+DZDHvSVUKTEFyG18c5HFkwGQBIQ5cf4yC8tWOwjL7UERk8Hdk/e23DVJu/nUt4mHLiGMI7kOI4Yo9ZLkm906lCIqpFMR8wqOIkzWK+WMzlosne9J7M2Wq704JZMobEcjzaZZ3B6nA/8VzL/elFJE8EO3AO36kxPJE3JsbA88W46LE7/FmJ88OKtEmgfN0R8N8wgcZPzxHojXOIZc74oHGGPRwKnlAiGl+ump5GMXWKOLHSl9+T6RN/SuMTOJWmz8QJLNJ9c8nCAmjCo23gCe0Qno37ZjdW8flR59bPsl7nupOn3s0tHQ8G+FIW9ASXkimsvOWL+XyENXExH+f5g7wHlxFnMQ+gxUwymdnTF6i1uuocnBfzmqU8rWjqdz345N49Kwh22FrNmcNjWF40PN/HyLPb5NkNgypP4sSrzFP9WValQ3nCnx+EQbO8nVNL0DQFcpJFATRLnrhvjo/Pj+OVgFOK+Qz9yU2Eaj1AlKbXS38x79VzXskjifdNsAmHA2z0cZAn9ry2fuP69du3b29u4lOaRouzBVmhqZ3DItSafg6RwLJPYNKGEVTECnVY852JU5ROFiLRXpfdhqOHM3fgVJ52B7qtgdTexZX3GO2KM/cvLVAM92V1p06HTZ5lf1akT4n4skx/Sgk6OrEcF6BsyFFLFCObR5PgjJDnOL6cxyvKEMs/mgo+4fYJT6qxzvCi2AyBMDMfkid4Iv8pTz/ujOGuWXalvpWx/b7Z1Zu3//5eNTeH3mNfaqg32NaGjlJLC2J8iCjT+Htz4qQiWxV4EWqOJzqjVnGCcHRvKh3J+G12S6iuljsh2BqzmTxRjYX25gbf02v6+4qzCOWQOiR/DORMmqy2KZm3mzzVnuRJoGWeqpfwqCAqAY+7Kxw6T6CoK8/iqVCnsW4GZRPcPakiWLgV9sS3Ijzb3CPMeQSKDUN3a2trg9fHMEe8GgCLCyd4AijsiZu4XzrRRxoZ7Qd/vrr5/YnDhw+fOPH6659+ie8/Hs32JXvahpLJoSHEOFDmFiYoFkXFfFR2DDjJzbNZw99Ue7KFgW2ETCTltlpd3hq78HSgWuB5VOvAxrI3Mzgod6FS0hUtl5TDEzhZf5JmN6dsXJyrzJ9i0Eqe/KqEdIvnCybQcYQdWG4JSIeSkkuzUZTRVXJvysKexFnc88bT4GlwVxaVI3C2dnZ2trq9PgJly8nH9MltxKZQg3Y2UNMf3McStHHHwY2r12///c7h559//s1Dhz6QPfSFeKYQDPdlMih8tWxnE4e1NowInmBJoPhemPKUZ77MMxr0YBXZVy3rNtiZtDjBE2W9xajzpo+vmFch8hSceT4gehIghWY370fmIqLM2ZRnyZ+V9RLNaWryVrmqn0STaRTdqlhiXnnOmOph41FcWkD+XKjioIozgbzwvHdvMvhEe7MOhGrwcwOdDzzQ2YolMPwO6xyjvk6rcQMnPMK9Z6YnzBOe68g74M7fLn311TuHDz9/6NCh+/r7+3ePDeAGnZ0JAhZ7hSB5TESgE/AnSHIYQZ5x+XcUp9kETmcze8OW7XW+Ots2kycSEXjaMBxxeZPsk0lRUDrERPF8XKU2JU5O3WLCk3P1DtOfFTzLDjVx3sKz5NBJMejEQjwxPw6exLkGsU0Gj1LTjPhl8MRlKZ1AvPEjfvSZJC9IBrhxjO5toD0bwNPg8ewzMD9iFcqWnbu1DelTeI7Ankf2wZ3ACZ4nwBM40V98KpaNxrNnw4hM2rK8Yw2HSvOcPCHlObAA5ZaLihOdBng62el01nlqrCjhydOCzxltEHzWIW+oZ2JlcEWvp+SpOCNUnA/BqipN1WVTtl/uRxU8JeAVKJ+kyacS1YB/WCM+mlWepNlGra3NzEjgT59C0qySchShWBxeXMztSfcle8OBJi8SpgXVkdfdgOzpDtGgvHXzd52y5j0irQyR/yB47hi5IO68JDzfPHTmDJZ/dr6YxV+9Fk5PSQtOcKoIlM1z8BSDJghUU14RSEhTmGaCrfj4LPQkedrQ8qux32VFeseu/RORFa0JttInYULmL0IU7uyOxyIlnvDnff3wZ+l+VOJpAi3Hu0JVh6pBEfHK8+xZ0tzgStY1vxCdWSXQ8USVjP+AM4fLGsK9L3l/j78h1GSAHMp4dDqpkBeTYohrC7omiHk6q88wPqBAs2eHZ7/iFJ6wJ4Kd+mD+3MzaUoR71ew0KlDtwNOguWX2ZeZQCJyGIhqgEu0Zk2i6J+DBIjhmHbIjbfLc7qqr9oXQR0bSEA1v5Yl/C97UxQXdWviSi8f9jHf1p2bQioBX4R/wIE69zuMhBp1YjpAn7bmx4b92IRC40OUHUQhAb0zPV2ULbJHkaU+s8/a9mhp6MBxwc9AOyZnuFaA8o7jOyWMKk3huzGpL+AJGFsC5evW6uPOrz8iT9tyJZapYYTyx+wDELXUBqmIK5TAiEiPP7NxpKhanAEWtiYCP9LU0+Op4BNkdtSZPdJUchgsJ1NsZHBagLNxzcLbwjPIV5ZM4VUoTe9zk+W9/Uv8Byi9vPUyD3iJRBXoRXZEF4XkOPIlTdU2vhGu82Fcl0ul0Kdz3vvrqq0igXW6DkY3QhheNEBZmcMLrjbFat4Z8cj0KcAEJI7x9+2fNYFeeh74+09/f0dFx325MX7VpOzW1c3CqxFMNCp6PxYhTDCoLB0BAKuQpy3C9uGC5qrFq43DKUjhykBOjDsNncxpGQ1vU7EURpx48lVKcpj9LPOlPBSoq8SRGlX5Jg0JqUM2gowuRxPgp8FScs5AAVaQAWoWfxyNR8ixm+lJPp4aCzwaUHptDjSyZWlExYa9OcYInlhN0eNSEHaf90OzGOtxp2vOTH7/++kxHx86Ojqd2c4itPHdCMGglz4Fu8CyJFgVDUJEanzyDXQG/YcPmkrPOBp443z02tJLrffipMF+o9dQCeOop9j88Y2bXM15eTVSeu8o4yx2mCqCMd7w0oZYMSp75hMb7hk7boFkAVQFoFcIkIwbNAWdy6MkkDyTylOjG4RoSngZ34YUm2031brqzeYTav/8gLu1iz0uCEzzPgCeEb/2AaNeBXQQKgwrQi1s8X6Q/VfOb80I0arKIyc2px+32G45t26weo2Y7eGKfjjw99Y7tuAi7g1E0PKAB4VkGSpB42+oib9kzRoOWeZaJ6hVJiQpT/dUESnGONPkweMazWzzhTtBsOtgEoKIvrv26WoU7UUR+YDSaBs5k6q2+oR5/uxd1u2ZLi9GAisnrkguo7skj3Btkk8vDVidw/vraD3/+dukSeRJniecHT3HlD0+8YfYlQMv+HCz7kzghEE0QKEeSVPwfvs49ps0yCuPEQggLBoL8oTFkGdQAEQ3UsrBCTEO5zxK5NBVWEJG5TDQxxVsJEGzqDZxTMBMQpwLiBTYXUJEpykTNjGbRjLno1DAVvCQuGG/zEvV5zvnaz4r69EKdc47fnvOe93LOuwknphHWuHPPic3YbI1FKQBCI/7cOFKNwzQqrXbweQOoMdXcqJoIT4Y9Az5/W2Q7OSrHK1E1Kb/iqUA1KSnPvRw/55DeYU/ivMVwKJ7gu7gCfx4GT9/4eLM3FAx2NPt8Ic6Y0lj6TobJqRg/q4hTt9h1N9SdixYN1uGR5yunjj/7FYC+EcZp8CzBgV8JpEhNg9Ke4Mm9XsOfZ6ZBcw0vBSqL7Tbsr02408rKUpPOSUopyM6xxLFLyZIFngngGYuNGbf/BmwgUboYoviphlKWrJ4lSfRsaEaa3CE8FagSNSf1KiEZlZ+MdSj9qTy/NnBCqJWPaDHmMKYnfePekNfrDQa9vs7iTq/DlovxErkdixF2UlflWC1SZ4dXltgzxy2TeKyJrsPguQKezz77lUb7BwbPEgBdqtlRWaKCQSsBtEQNSqD3mTxBE5uGpwFUg75NKrLxmthqyclJ2AR71rZkW7M2Cc/z4lCVHJsUj/jJcfXs3Ys6rsOClE/pLsALAkgVeFJS3kmDNkTOO1QRoEpUQ/0f8f+vPEkT+YMOpW7ffjt4Ih+hAxcKBUPNfXZ7cTMCHodFKTwxtmhrBtv9oCwI9kxPQ/sG6o5wTvTedY9vf3Jly80333z5zV8pToMngDbgN9+wjTAjPCsFqMmzpnoB+1Cyu8eu8jWNefBUteUmZ+bFbopLd9ajbSYWC6TkhDjwjEUvCLeY0p2D4Plf0p4Yw59EiXatGRg0vxL78ybQ6H16FXmaHzfwxPD5yit0Zx6JasyT5/YnYw4T57iP0e4P+ewV5Glz51itVdlM6+jPQMlqslYnESkcgr5Y+BeVc8Lz8e2LF23Zcn754s2fC04z3htmZvCbLy2pvPXWylvFsNuAWUJe5/Ns2leep09/CQGotEJPVNNeoLr0WCwKwTbF57j82EZMwAxUecaDJz6ijnQejW1UpE4OezCs79SOLRF54kGegEqDbiNLvCJYI1meRDcqvAzFJuheg2fYnkjHatDbqce3x4yCpw8zpnGMnuTZ7/P6a7Mzqgrq6lpaCrEVYk3LTJDOc+GZiD08dHlgfY9oN3iWn3/R+Yvbt5/6/NAhI9wBtKGyIX8BfTpLDQ8/DHv+UEmJQelQFoGQ5/U16KYAT+L8Qh0qveXthoYujrdgqHT7gx7b1vRNrGIAz9h49HNmgWds3mNoyFKY0dpBhYlWS7BLt+uMEfARnGamNx26QZKSwJP5KIqnEkVWorbfQp6cgEI+ifdirjg9zoKClqKi1taiVtsV2VVp+I6SDJ446sSU2orZ/iXEeed7770LnheWX7C4uHLijQjP/PyG/O78yWk0Nlc3INJBkThNgwInS35g0LYFDJ+nhSeAClF0m3IbaHJysn3amsU5Ru0YZx1pcZjKZyaCZyLazOjP+NTadnYKGjRL/yaDqPIk0CVId0QY8ArUUHTIb1Q4zZNn254x8DSGT2QPijiZnMiTK+b+ceya+8Bz3Aesfhd6LB27Pa4il8PTWphtTUhMkmIvLQXFlBqTU5Pnu+xkvuCiy59981CY5zvd+U9PBgILZ1YXliaBUFTCN6xCBajyxHcNnstizw8hAqVDwXOH8OxacieiMCKzfnB8zOUsQ0FIXGYseGbx9JkNM5bc9klWuxswt0H8ws+4EECEfx/e/CRN5aknSCbPqJBXpiZMA2l4vjTG6afBU4GqQelX8Nz3/OF+r98Lg1JwqcflcoQ6Gnd7djl2Nzps2VjvASeBKk9LClf2wvM96l0gvejbz9586tBdyvNp8uxGM/7Q6tJkg9L8ATihp7c1MOKB0+CJueHyNHh+AZxgqkBnF/A9508S6BgC3pI3NbhnEKN6LLaYUhLAE9WNXKYlJiZvBU9sXCtN1cNKlUgVqPKsMcQl0rYIzx/MxKQRL0CjaeoLMnnSn2wZ5pM8VeSJU5bDfUG/19esOP0Olyfo83U0+R3A2eRpyUhA82QUT4q9huT5EYn+/t23n7313FN33cWERHtSBDq9tEN4qtSf6JLi9siN1DaD55fkSaIECoMqz3wxaKzl3rLHxvbsGauvs7BMMf3cJPCMZ29OYlZsWTuos/mfNBEKDz+MF2cS6L0MW1R4SqoXwc74QzX9GRXyz1DRPMNSnjeQJ6uPic/ESZrb8SkGy9/RPThxhMaJ01Pk6bCPFveGyHO3w7YVU+cIT5a9JhNoSqb68yPqvbcF5994dnWBZ1cA+T2/QWyJh/KU7xmbTVpZXbqDPagmz7BBF7ryqe7Jrq7pe9Pz3I/ND/bMupxp+I1kpRg8ExJMnvkN/IWhEop5j0BJlMOGyRMPyjiSoz2jiErEm0Q36qZbS8M8dTZPmUDpT2wlcPj0hoiUu5+2Rvtt++zNXpAtKrIVWJMTlSczEotrDZ6WO6GPRG9/8/Nbzxk8D+18p/ud7kAg0NXFN9isAThV5NqAlASLItSFZ/vCAnkKzhOKkwZdDkwC52Q3f5HVnJyTOKOZnRhyupMQ8AnngCemwSkW4bkEF+cLT6UZIdoA4Rcx/Sm5iW/SDEN+G3g+8b88n3kGPA/3DM7PnWS3wUaeeMXsO3ytZPegH0CZ22u9+3CxgS/kasFZR3aaRXkqUN5LIQbFYpRApRf53fN//vVR8iROOeoABxgUkm+V7jSEbxHfJ7Ov1v/vqMaMCrfIkOeJEwD6RYRnN+0ZgGbmTs7hxHuop8fmTGQJbZLyTE3HrJg8iV5xmjwVaGmD8lwSnlw5cSGlPNWgP0ACVYGCpwlUnhQ/yBtaZ0rbJwaHongqTZ1/Yj7P1TsEZxIoqsFsxQNHXrSjrCGD7e7SuSE4lSgDPnw9w50sWProvQu+//WPq4EzzDOirknhKSQxiYLUM0gfEAy6t2bC4EmaJ+BQnTOtC0+OGAGcO3NChaqMtYl5mwW/CfzxxnOZlmlNxv7I1iVMzMSfwLnBoBGebeQZEToSMIhrwOMh7yZPcy2vFEX8Ijyvn5gdmjtm8OSTMFX8EDPKnXDWLXkcQVTX2crqKwZerOhwXQGU2HFgswCiLCzy1Ib3O3GbWhYuW3qvHDj/ybOLQsgvtYMoOEaAMoondb74EA4/2/bMTps84U8BevrsckAH4P08x6dHZ1HaMjtWn4NIQfk4eMbGZmYkxCXF5ga6wRPkaE8FKl/Jc5sGPONdecKe2Nq7/vkde2lQI+DFoZDyVKBhg+qbwoQ9DZ5j5Kl7nyrZsFOmMbIbPoo6G88u7Nb5a8vq+ytQ1FCYYWE5MSQ82SJNJXH8BE6sknBdC76t98pfgzuvfk54Kk4Z9bhSpjiCEiYlNLvwgzXsRAPO6j2DyvML8FSgxHnm7PJMN8QRGEDxRzO5NI0sNTjlTALPrKRE/I+TrWUW8HQG9Gi6Eoo2aKXiZEYy4h3Czh6QPg+eEYMqUQjrJfKMADWgmrrypifu23Z92+zYqvD8+3YdJYTBc5S61utxObCqq8tx+psc9YVVKcmc4WEyfXGW8MQLb7Qnt+kZ9BezSWHx+KfACZ7P0Z44ed/Z1RW+JIZagkEh4dndDZr4sWrhCZw4OpidXl5epz9Fak9U7OzviuBkausG0NnpwSkPhvKkxKQsdiRnZlsQ7yeVZxioKfA0/UnVEKcKPHk8qDzNIVQTkglUFM2zhEfga6tTJyPbdcAZzXN032GeDHodbP8t2pq5ucVWV1CVmhyP37RUbZMnDQqe3DCjZaFk3nWR+crxTx8VnE+F/YlZ0gJrl6aXCXRBgT6dD5g0Ln+IV+0Q5w0Y2ccGyVP8edzw52nhCUyMdhNoYGF5Ym4+eRN6ZHTaZs1OQeQ/1iU8dToWjRM8DX8KznaTJwdvnA4SKEiaONWfinQDTOK8mzO86bX5j8WgitO0J0fUGBZQqT+LPA5XbpolNbsA5V8WpKEs8LQkaCuM2jNJspGMn8no57yXOJ8jTwEq/gzMAOasbA3DomC6RIMqTZqzDUOZgROVK2Nr62fPCs/jx9WfsCd5cr5FnMoT4n89NJ+OK5rY3pBFnulxccoTf2ANJs4fzHgn0Eh+B06j5g4bURGetCifJs9og+pHvDB63v0AefYgwStPxSk8w92FMfukapDjp8uDJXJKsoXbnhbYM453XEjZt1yZJl2TXB0xS9GfqTlff/z5G0BJoJD4EzYCzTVqlhfFKNBJ0FScXEbLhi9r/4aEp+JUnsApPBHw5EkFDKCYLkzPl8XBn1mbkoRnKnx6rNvkGT1hMnjuyDfGz3a1J4/vMFlTniAUkVEQCqJGkZhijHykPUu2le6oQYJfNQxKGQectCfaqWOMIsxxPzY/6t1W7CnKFT8GT46SkHS/wRlcvGN7KTUhGaVNuVOr6y/c9RRIPvoogYpB9wPnGpIxC8hnl6EFbIlAoEmcS1ygsML1qoke2nNobR04vzghPDXc15VngMsr5Rl26OTM2mOJmzCGs5sJ+Sg1kTwp8twY8NH5qB2JCDgpKa1/GDyhME2zQAwiQzyidBPggyd2GKfXyFNTUvi8mHTZFxNDnJwuIRk5ajenXIwkRPE2ObhRLwfSDmAEewK7XdKqrJaUqlrPPMa+Ow49RaDACZGn4FxVrU0vY3SUIZQwmfBBc5L7u6iZ2jOLIr8v4ccvFafyVJwGz/0mTyU6Pc9qmyzhmbk5E0OS8tQEL8FupvcN8f68iRM8YVAQJNBINT3i3RxAiZRPk+cDmIXtaEffzKAalED/xjPvXvIkTrYhOEIhjzMPWZ0hDaDgiRs/2YnE5gTJAbhQAAWgGdnZadbNLf7Q+MRM1847DolD1Z+I97OI9SEWe0NDMChH0eolEye2JGvaWLzAjojBNW57Cs4fhaeOnmGeUUCp7oWxtCROQDnPSBWeX3drQtL1e/QKScI9wrNd3WnyZDPXE4ZDDZwGT75H6xn8IOxZAp5L0udhAIVIk594vVL6JZjPE6cXe/NeZCPUtoAnxfxuycvjvZNWNnvQnFZWMxXUXbE52xby9V9VM5nfvRM8AZQ4H8Fu3f51wYl2pmNTH6/OTkNMS/h+tDgDq5Wath5IS/gBU2lCACo8z4Z5GgYNmAbNnxxzx7H3nQNoSo4VPD/BwCw8Facp+FOBRvN80Qh34XkPearIk1KUG4BKcgfOUvKUPo85bSGm1J45ylNxhnAUF3K5M1J4oCkpB1fwXXwv24dR82lh+3sKqxfr6upaWwsLbB121Ivuxfr4nQ8OHXrkKQg4wfOO9bUvIx2Mc4M9bBvr6WEtQRhne9sEL/OQotMzNOdxxak8idPkaY6g4k/knTP1iex9F54Z5Pmk8NQFvE7n9Z32NPdDWCmtPFmmRZx67a8gNGkqT9I0QUaiPcyTJQNnABTf4klpIeYbeWq89wtNNFD2herdGek46kLGYc5Jjr0ku4D97WjMgmUzq7ILW1qLXLt2F9W1NlUMj6Dve9s2rHwIlJLd+UNnjPbvx46dVJ7sG0NhEgyiOKvBETW8fJ9dwy6I4Pzll19+BM4PsdQkzDBPSHHuBFBgezp/ev5i5EXw5P20VjTc5nUpT2MArdw4nac/ZfhUf0LkeeQI6/1AzuCJd5OmfInCSZ63siQD8VVtGJSeOXmSTPH2SVmYp288BDX392EDHJd1YR5ktaIGrOoSSwa60+FOUd4VLTYsoJo6ensdrY0VAwcOHDmCmyEann4HO/KHTK1/qOY8iXKzqTHyZOvIhLTyGzgH2RDhxfsYV0XAqTzVnkpTeRpAA5SBbWYoT9tukSutaeCZGTB4RsV7pelP7s/TnkxH5EkNHIFQKo2DQQCFgFPdKVKqUS59RnnCnwx4Neic2UjM4/gIT9oTw6HPUe/cakUVLWACXoHVWuYuy8MVNVb2a7rRmxHs8BXb7T7H7uID0n2KbWEYVIG+eujQ+8B5F3jKrw+dnBraIzwH9/BEnRu/SxOzg2P+IDdeUHE+//FxcedGnvuXZwAUIlB5yv7fO/ldQ1u52yU8M/NwaHzJQpc5gKpDFac5fG7kyQsVIJaqKFBTak0Tp8nzbvLkCSJqBpCRxph1iXQK4nmSGDQzpg8nRuDZ148FvM2dhulQdqFtl8NTh3YhVnzhMs8CNMLVOlg9Ulxx8KBvd8cwen7gT5QcVxo83wdQ4rzrTfA8JYMKDDo/uwf80MgoPHHWzlp+D28KcqHjfW7q2I+nCJM4yfPEF6eXF/BQf6pB1aJkphpz4t4L5ZkKnkmWZWIP84zEu/LkoVw0T7UnaaIxBDghgIvCaUrR4oWwj+JJg6LNA0TJVLuy3WGexBns6MMZkgOtHRkZ2TZHE4vCnG6UKLFv1W2rRwNRMNTR3Gk/ODzsa/QNPCg8UbNd0gCeBCo0obNfCM5PWGR68uOxWfJcm90DntULUok655JLmFh7j17yU0RJqT/XF7C8F3/qOpMvytxUnZ2KBU+u1JJT8pLBc1pm+uAJoCZNY/pZavhTp59muL8EoXXpmtepKwEUSDfiFJT6ZvDkvf/XE2iPfGNDFKHiQJ6dgLBfzDiKwJCOuEHv99joRH+zvaLY68DFffeiOrkst97hsdmw2YyfBJ4He4PFRx988OiBA+B568PgSaBMSXfd9cILL6x/iHtZuZh9hVe0zmHZKb3iSOf4DUifSW0tG2lZLI6/5mLlN+gXPMET9jw7Q554zCDeVXQfce58Zye/LM8lgCcMijqVvOSspIRZ5SntD7So8twY7ybPgQNHhSZv+CFPxjyQ6uPfgCpPuX5WDcoUP8vp8xiYkur81Emnm0DzYjDzlLOjIIY1RxHaC1zNFSMjo+ibcaJDO7PMWeRHD7cr6A3BoIj3iqamiqPX0KCMGgQ8Ix6T0BcgVNSeXj22svikiEA/1tZl/s/HgJMdnVLXnI3mOhT4Li4uEih5HifPBfIEUcz+A2GBImjyAXXPzKWgn4s8keBRGJB8BqMCRmYGvDllUpxmfleet0V4gqZekkagpLVRJlGT5328p551UdUTE+yBGwxryPNYrQAlTy8TfNCBBaffVV9U1Fjx4oGBfT6U1WZYLBl1rkav1+Hy86cgH8G5jU0HheeRo+RZUgmH7rwLKAHzzc+/+gK3rmNZS7Ec8hi6YwmUYlma3vmJoRmCiRcp5Snhzg0+3mYa5ilBD5ACFM+unV3zOUns1+bi12pJTIodpD8jR3IQYVbqEbzhTwToDSbPgYGjgMk765UnBWrRHNWo4Wwky6N75JQbF9Vz+6FtokdmfIbG5sUpuFwmxhsaR+kSixhgUBeuPO0YHjh65GVfyFGbZknJdjXRusSJ92BvsR3pCDyvefDoUUQNr3OsfHrnHS+88SZgfv4V5pO0p55SodxscWXLqZtBlEBZxc8/Q9IUcfVLnATKcH+W4U5/zgjPmfBmHe2p7mTb3ZAbF90Iz+R0S3xS7NpkhKcCBU5KikSUZ0216U/a86Uwzcv+RtR0osqkCZx4sSKQPNHxxwZb8ARQIuV0GofBSAkYQjfHeL2+cfas4oYZ8PQ4HM0GTz94WuuCqK9l46UXDvY4mpo7e3eHDiJeYFCOQveAJ+9sFprPnvgY9+mslBvnfo/fsp2lYlvkAhNp0sN9DhR4ZuMdOBWo8mS4LwlKkyeBaqgrTXBdy40Xf2YhIaXExsWvkqcCVaJ8UmbNTXu16U+Jdl7KTZoiwHwGDxCFNMJNnEQpDuVfQ0OebKgHT+znEKgIH8bhR434GK/P1xwETls94x01DM0Vtx0ZeBlVdjbrJWk2b2ezP4jFUzPqcLA2aupt2t1UwQH9KB4PPnjP/Q+XTuLK5nVE+glepoM711FmJnU9j0Pby/FXXpw6hpAfWqU94UoSlaA37Sk8n0W4LwnN/XjjZohKvalCop91JmMrVg6uLeCZOBfhqRY1VGrihD81vwtO8tRQl7+pQlO8XvGGzqioqahB+B6RXrsjPNFfS54UcZJnCDNAuRk6xtdX7PUDZ2098jtvlgsVv4xOJOT7WmtKVZGvuMMf6rsWs/5GzhkdKGvyF48cOCDhDpfed+Pe9pnls6dP464nwFwBTmBUgwrP8gsvOP+UpCWOnu5P3CKApTsFp/Lk5JPbUCrQ1O3kOwI7o9Q97UzHxWubWPqTwIqGY//K06ixM3nqbXO3wZ70p97Idk3k8ivWo1LYcrr/1vvwgGRxr5drYSlIlCLUfyhO7ZTUO5h8jHAbDOqOsdv7vP4i8uSPAFmw72XWh3QEbdb0KpevE2sn+8uYQAXxr21Fu1xFnt6DI/xtcc5x/42l12O5sA6cx+hNpHblqdV79OeFF15wEfM8slHuJ58gq+dCAKo4OWFSnl+dxsbejIb7/hk89ytQ8adp0OXcvE3kyUpUHh58MtkVMHlCYaCmQYWnttyPMN4BFAxVcAU4alctqAk482ZA9aSy1Ean2/Yd5olwf8+1IsV5LRB61KDY/+wLiT9t+GfyDPlGb9vX7+2AP1M3uzBg9haPDqMTscnVUljYYrO1tDZ2VhwcxrDOXn7Ys3oCBTMfCs4nt1OP81hlu6p8sRxAtyDNY8MQcn7tFOW62WCygsQe4bm+IP5cMAwaCPOM1oK7LI48WYrKje5XAsITQCkFildpQ3S8s5cRR2UECqLUAT64DYGnIjOll+SYKCnANFrzyRMQTV3FchAwdOYWxNw2ynRUBJ5Ody54ekK+CrYioUo5MzXb1dvZ2WkffnG4otO7iwU4hYWFdUVIS3ZkLfA8wpZw7A5wlYlQhzcJUaz3ZHl5OXAuLl544YXnn8Kinjix8UThPkD8w8rKL5TBE9mdPJHdlej+f+c5s9WdeA4uYgoHfN6CbI2aPAUoFOVPNm/z8oZhaGRkYGBgRAS6yPh4CS4R0e2NUIWicWp3qak+iNllt8tWh9rjjJgXR5m6Xbg5dmtZro08x696+WWs6v3wZ3ZRr724uGJkYOSgvdmBYmWqoM61O9hsHz6AaAHPG2BPHKgQp2YgMFSVQxdcgIBHxJ+iOBvltVlDfs/UY8eOYfFOmsLz+BfCs3pBBJr/4PlBmGegzBl/DvxJoMnY9b53uitAg0YBxSMS7/lGvI8CxkHRMEAOD8snQ+EbivQWl4eU7G3KESTxSRtuKyrsdiJke1Fzby+6M0NNVOMunLFn81YQ5enfVVTPa40R9H5s3SlPW1rmZvC02w++OACDFgdbr2CjcUZVdl2rq7H54ABS/ACufGaHPYJ5RUZOceQKdD54guR30KX4a5hWVsCTEye5/oVLz4+xV0egglN4LrCjRXlCkfQerUAOErzyzIoFz/QzYZ5aGJYv5jRwij+vJ0/2y1VUYHnHNxHQ2I0f4RdepyD34yjMfXIUSs4AyRdSSLGvuRkMya+xEbWcu1gTbxMVonVDmtmVJ7PT1sw85entH1V/plmFZwXGyoGR4YrmXXXZvFjAmoH9vF0h+4sYgwaev+Hantk18MQOtUR7OWhu2XLRReeffwFgUj/hJUCPffjlGenT5GHHGqqRAdTkuX62bUlwRjuUJSem7gDP2oRN4ElhAI2/ZA08NxgU+UgFe6o/OeiBn10o4iviDkNZMYWP+EGMBiqw03cRP6DppbO5o6kxjLClhXmkpRCNgyoEunHlCsZPrH8cnl21OZek8YIV5dlPnhlpm1t77ZJ7Drw4Mly821ZQxXsFrBmb8XfhFY+A5224oQY3lKxOscD0FgydMOeWyy8H0PMvuEBg8kGgiyunMERi32hmiS0szGHYThaD6nRpfbmtOoonpDxJVGHiphf485JN5yrPeCQky2qEpwnUgMkK5b17ybNNRj2wFIj6xYeZt8jn6yRQDAjCcBRmJUL9mUDv620CSYBsqbvCRFhQIG3suLMCYvuwXNQXc1s/Fj5YZjrTLskjz6AX8a48czIKhOfIARgRRXehokI2xhNodoujcwSU942iuU7qJXi6z/k5m7sAFDzDNKHvvrtwccvNQLZ/Z/c7DVi/7K1mUfIJGpQCzy+VJ4BGR3zgDkp58kPgdmdmkvJMSkSAWebIM0CeOyTehaYp9Wcbcfb1w5M+0MMXjH54Inp7O/Cp2UCnYwBZIrL5M5rxtcmxqxVTG5LEdqZEqDVNJV2ZVIJcF4/Cj5iXxx2YxDs8uZngmesEz3GDp21zVUG9F+EOngOw6HBnIwyaQYOmbW7ZDZ4DwxU4fsJgKPVREGP9ZvLERe2K80/ixF+3+N3iFoT0/p3o5EZlYElpzcQZRDxHUMGJ9B72J54zJk6d0YMjHqLA7bn3hnnG8faNqS5M/RVoviiKJuf0NQbOvr8YO++YOOgojpM6moKmpvJHjSGkgDkvHua4VHIeF4OguMAocKEiJTgRxz/gbNQowYE44ogTR6wr7qh1ktQ9ojEajcaBsW5N1MZqrNvP973f3Y/zXO8GipbCh+/7vd/7jfegaYJEm5DUSDjIYDgB1OEh8329xFyxBpOLj+YVuFGigYSfGTuWbqEvhNOkonvVDPN08p7OntrmllbxXAvPK48QzyRuvZolT4TIosyLl917VmGkG4Xy60nSnPWaa6+5d+Wa4YFJv980N+e+Tkta6yOAMDGAGtFdSzzBCU+OUZrDFzfj3gk8ESgPeMocp+sTnM7z1p7UVs6zeiklMc95lYmq83ScQZ+7W4LEy+I7PC/AFFAwvSum8MJAKo0GzxdMaFrMMWFqFuR9nLyRDgCD2T+KJTCNJo3DqgYKmb6MeDY0t1BipX/KeK6CZ3+2tTsTeD788IuXvnjtmtWFXiIZpWW685NrCJHgJJj5fRwWkD98D5rghOcfqLOE89dfA883zxZPlrpVTTMK9IcSTwEN8vS9OMZPgLqrl3huE3hW65j023/hiSahGRV6ifQpnMAaMheHnmkTGyNOG1KTo1g7Sws9Gi+zGiONpcOTeYeC8HCYXMf0bhhVuUxbWy88W2sb6uiiU2DtY+WVBKmpXH93d7p30Hlee+2Lhz162b1rVufyGUYS4vvEGkZ1Rmqy/gJFreYxx+lA//hDJHmZiedPhJzNNxx1x/fnsVV4AtPWB50nBk9txXGlutg9h9MkDhSaEafxnGvtgqc3stRR/i/F8x/9nRc8V9kpA+KPaIqdMysZRIPnB5bAlDIt4NgYGZq6iR2vchPNanAaz0yasn7k7j0J2kGy2cEN41nnCej0SOB5zTX3HIZA7+04azDXP9I3kh8/S77DLSWCGZkrqTkbRu8B840T1ztPJ+nyRKBPvmc8T9ttv/NYsrnwlMejw2OmT07muD2jEzpBnw4yvJ/LC56Li/pUh8p5q+4WUySWkSXQSJT55xG3IE/hhBfkMOeZywWe0QymaKJMsQSm+7VohnL64cGTl1m10aSge1VPfUqNSvpb6xJNNMibmpkt8uzva+vuG4cnKa940vGKNGkNRAu5MYbwKf52x6kuZuSP78ETdZ4YeUaiu/7yHiF8MxcNdj/5IBXeufKI4PAGVNN57Ro5TrewWczYGZACjhc7s9vREEU8Fy3m8uM8n5M+Y8qpq12RqPR5BFVndIc6eDnvjg4bDUz93dxcU0uynViCBpNfR3rh4YafGE7nmaprSaYz8GwUz/6pI2b3nT2FpF51mMSz495rHyaQ33vZpQj0Hqb1HcxspyYY0MdGCznh7Mz3I1DxBKjb11/D00kGeaJPeN795gO37U3XFng+tAqeQaAsJjtP3DzcsXzGd4stuFt4d3lSi4ib50sWiSei4C5+Yt4w24ayD58shvyF5/HwtAwRjlGNRs/MqBpco9kHTcKu8h3XpSzQrMYW9GTWUxbUCU+Kne6PQG/saWkSz4EjqAIKz2H8nWW1tvEOsl3xJCBpE06JJ54+NDyhRal8oTBqArVqbu8FoG987QIFJg890Sc8lR0defAlF7LtTStxH0DfKfHcaECdJnabA9XyZ5wssSF99qu3zp2zqKTP2sQ8NAEOz7jAVLTIk4wbb3eeHoR4M4Cj0fKBJuJUEAqzyuDiRZhufkGDV8TpPJvputVFbWmVEKhPdw6shOeV8Fyd6xXPMXiav1/rPFkZ2bdD8zf028fEoLPgCgUoCxxFhQaeRtLlecCH773P7W1O3hx7xflP7+k8v4Gnn1V0ng7UccIToEfBUxZo6vnOqfPNOqQMT6oyNSTmnKfkWblG7zxX3aKaCcPCGT07yDOPjeppOKHZLZr4uuP0YBOsCK/SnKZ40iS7iRGUqn51xPfCWg7OmT4nC6w2l3heS1DyMtp4vCa8utWdbhvJwxNDorZgJNtDQd5mTJHnKzfd+uGrn3KugeqDs6c8RZPxFwlIYQCVBZ6sIwec8JQFmA4UnPD8/KT5hurqncWTbh7OU4vO2D/o81gqpDhPw4kFnErHo5VcfX8CUTlOKLotICiG8Q2czpO+TybQds5+1adzQ/BkDWCl6nzTXic9ttJ4PswgaouwcnhwTiBPktkR9THCrAHPq5gTJd9U+88STtadbp1/+xHxpLjbLA3eL33qCnji8CV9fmI8BdRxYkWBOk54mm08aT4ReNJGbovI8y849/MPOmK4auYilUgRz5wbIrXhsgKnTziborMHnBVyLDNwRp5cbq1P33gjZb3r28dV11ilrYYn+9mHzLblWEyGp/YFsafwd8uJVo91Mg9NtztPBwpR57kCs/z9JcF87ddXXrnpVPG8WTzXXjBzJQ0NXqRS4d04fDg6b/o0oKfpTt2hRZ7gtNw94FzHY/NJ89NLuaenM/1q6jX3ZuQZcerm9lWRp5Wcsaml26i9OU+vRIFlbMXIa0naHZYSznKQ/0izRv6OwycIRP30majvnTSelKXmAFMrVWzach3wBChG1nkPnj/bwYk8ZqEMr+le54kZT/Y0RFSrn8tZR94Vntbijw2QIk9+rCE2UB9Fn6vOvNt5YndSvmmzAWVizhaFHlwvBigonaZwYueee+p8aisO0Pux7yWJW9+swOmbxlcFopcct8rCu8sTinpznM6SN9kI6ZAHdskTnNZa3NUZSW4bITpGpxmAEo84jUwg6u+kKXtXhnN24skendL3ZDKdW0NlOwycBpWigR3y9tF+laok8w84DagdiISnL83v+tJLr4SWicbzLpUWH+Cw4pp9H37qKSZMt0Se6+/8mOpiRZ4YOAEKUYBGnAb0w7lbt1nIc7NHdwfK0rwqNEidxvME4zljPIUzBnPnCU3DaeuZDJ6erMfBs4gzcowseeMRSPqjpkq9ZOrqpc/6ltbOgSF4Ik8VCeui5HRbYYhor31WmYrmU3OVmbw27NWRmHjk5jwx43kAxqAJTufJfmdX+sbH1M4iVxgfWPMEOza+UcKanXDCU0DXyeHFU+vqxjNGInA6zzfm5hZXL3Wei3eZPrXIM8Z2EaVQAw8hveRY44lTWUCPZijdMjZ2Fr2d/NLmnY4z+nng5y8e0UpQq6apLs3A2Z9Jp1p6csPiOasiN+i1kS7DnA67JgIFp7Q7NS6e/RmXZw6YfHgkCNQ2ko6WgVK9kAHKrQa+lnb4mauSq7JC8HDkafr84GMvJ+g8CS3iGeUZ9Xn7J3NzW3LeBp6MotS72iieIRo5zDI7IfBEBWG+6W+Y4zR5jsBT3m5ZUZBnHDz/ospKc3FKny11tDdJKr63tLRPDg91yNs5vZTnLGiqvjs/0aEAZQd/eNCAsUM8WRX0ro/QzAWBPgJPb5R4EihD7QYB9R5pTMo6OzNcaOgdnRze98pTjOcG4/n1+k13fvCB1xPE4Y0mD3gSijxtvx+cAvrsutufm5vbZakm9Go/sSU8CVlFeUZt7vVXnsRQc3E9ZAvliTodZ5AnPD22x8Ez4tSrAqfR1PhJoyw1F6ZcYlOqn6os8JxRDaZ+9JpKtWbGh1ixBihEsVOeUMErDubkDWenYAZDnqGMI/XowUlBejfqjGzRbJMy8tK+tjbaxE5cMEtZHfH8xHhu2nTnRx88L6Dr4CmaMvm7xAnNKFDsw/lmEhbx5Pp4bcsG8Yw4QRib7DnP4yl4KJ6OMF+OM4MFnMHbF86V3N2js0eSlUTN33vUJCbL0cVEoitH+aUODjiQbHL2uxHh1rcVJoaGOJdsQN3dh33zXjwjTBz5xrdZtZvzLp6hJZJssVrJQXQ6lebIBGtTnMRfPTRDOcQjjef6N76G54/wBCgChWcwROf6xNCnnzG9b93tb8w3WAJoPBPwPMp5Os5A05rDOc/HZ02fg7m80zMnX4BzBJw+84zytGhULs+/l2V0ddcnS0MArW8hj0+v7hDPfTV6Mn3SPkkq2687HWv2JWeSEaqYLRlPjZ4C+hg0galeSCkK1mLi+a7R9Oay8FSHaVYD7RjKaI6Dj7TkCvpc/zUmngC1atY6DR/0afmRaMJT5gPoF3OJrarBiXFje5e599/ZvJBnoAlOzHger/xoYnBUfq0ycgFpkabU6d4e5Sme5e4OzX82UAZ94oOcpQfnspbMEIFo5omVBPf+9qxK+CeI/PmcLnbg8ldq+48Gy7i7zj4pvFv67gd3oKkuFFZGw3guBqduVhtO9MnRcf7MSEZt4AdXr6UXi82X7izy/LnEU5El6hOeb519P5ecpNDbpc9nv5zeWUtkPpxtC9idV5xRCu6Ok5p58JQV9Ym7Kz3XEReYOlJYBprlOIu7a//As8ZfgWOZSKsK4EgnacezrL6wcvYC8vcZClaSfVpf90Rjsh23Hp9Yw36qjN+0Unc1zmUzFOM8PDBDr9iuUKbAeQJUOCnLZiGpSa0i+VEOL/AbGphZZfGoyHPTRz//LJ7M11Vt3QXqPN96i1uiAmr38HD6616dXhrm0jq5uP2ind99bYPj9Gm844w89V2zwGP7F1k2emGKSh0mOEWziDN4e5x8gjPSjEQrvL6oz/GCfDtBxa/uyX1nLxiCJ1tKrY0Q1t1svLSvlwjCfUQ/r6PBQCtPBlRcoQlOLNVVxOnjp4BajQdwWq94mgN0Y22Ho9CptUccGXmiz8jzbPGEqNV0sKM2rk94BqAHbGt5CTS1aVP97ms33bTHfvC0xMhwwlMsI8/hs3QmppstXyxpRA3pQpxFeUaecS0uEq2ppOooffxkTghPKgg09Q7MkktyfpEWMyl1RLHGeY1d2XSfgE4NsAFDnOSiQtooRoOn3uDZCs8YjxZr8DSc1g6ZWYS+9e6RvGrpUCW9Qp/Xl3hidwSg4HxrgT6fXb6IlSWLuvpRq1+gxCPHfNjVB2foU2o0eUV/H54Yy2sPoxGjR6+OI9jZNnP2oE7H6fKsGD4rEP7D/NN5JpY1pPqHZ4k2MzNDLIU0aRSx4gF1qa5sH3m62u1MDAyYOjGVqKcJkkDi7dAMQG34nBZPgGIBZ8KbQxLeUUYvPHPjN19OUYwNXxR5/ljOE5xhBH1LJpou0I+/23bRzjsDVPpcSvmnV1hueWmf5W+GggLi6UWbuaAZ9an18T4/PcDxARHFhDREIlen4zR5lvOUOssdvNyCv0ufOXjSQaouPTo0i79T2oqTIdZYQpv2tfSRycrjWUEA6aSPnQSf6QZj3RVAoj39Azjl7vAMQBfi1OpJvler0JZRPXb1ZxvEM/g7OMt5Wpkh8TSBBp7Xf7v1Ii8NI5bAlL3EOf0NezlPmZ0vdprwPMV4rua4ZVLJDwbRupJKs9HZo7cXeWJRn/8rvvOjwZO15L6xoX3JzYf4TXIP3tr1NNBShlGPETRk6rBHnVYqvWFZ87JEAGoq9XFU6pxWD3km9BCNzm4NDfkCnZmRTF5bJDQO+tx4birneX+JJ0UvuYsjnkGf3LT/qqZm+6UvqC7uK25CqvqjO4klL2iiTQfqPB8ynoP5kez+ttLBKKYO9ka0PikLsSjKU+5ezrNy4lnm+3H8JNHJtCfru7ozk0MrZ9gawqPhKXdfZr11ufRuc3cMdaU197cNfmsFX2dt4GnWBU9wpnS9G54N4imgJZ7IE55InEyBlJNIRmvYTz//O550/2CZ6A4BlUKDwwd5fleztIHREjsgEFVBVw6Yrti4VzA4CqZers8n4HnWIJVhfU9Im/ZRpDDForuX5Fnm7+WrchVw9Qr6zGSYS2bZg+/kjjFhkNl6vqcxtI3i4rb66kJytGBC7mFeZL3kDChdjhlDbaaEwxdxJhIU+g883y3XZ6eWHalHkGxFzT1vv/9J5GnDZ9Anrg5Od3hMPD2+37epZustT9rnyeU7hPVATOVxxXMnp+naDAK92PT5BPHorDFOrxIVwAVQEXWROlIsCDTyrNRnJctyqiE/6ksTd/vyuYnhC8glp5heprMMqGizpVHqlJtiGmeTqTrmUW6UqQ2dDsFoPFMpUyc4xROHf1dTT3AyfgqoBtBRfivdfJUU/bXTrxpPgBKP4AnQ+6TPl0VzvxLREJDM3X+r2XaXm/ZZju2wz64Ale26KwfKKTG+8WTjWSR6MQ3dI8+JXCbbWGuJ5JYQFU8HClEhNYFKoebvztOBRn3+hwk1PNt6stmetkxhUL0RhuxwbS8TtVQjoktm20Y0nTdjVtoih8G2KDEVdS1R1Qd9Os6G1+AphYapPOt1FpH6lAJkemjUXctXnzeeCDTqE560//BaoQa0qM/XTZ8f1dRUNx/wpHjaOXKQ7gpO47nHTk4ytCCHppDK30nqhghHSXiK1ZZlQF2iAWiFQB3ov6Sb9vmF+x3pnmSSiNOfm5xYC88J7h5qZ91Ey89v6eGkjArwKR9hxJPKdjIjai2iS/4OTuT5mnCeg7uH1ToMtWctHWmto4AGDank758Yz00VPAVUOA3oUUGg636r2Xqbo+EZgTrOHbg1tseKDWgzAr3YTTyZBg52ttHTJfKUJETUNWpAY3pUyROrwMjDQUf5wrMtSTTJ4u4cpB1i49LQ5UZBShQiEBcM59SUVvAaIcNE33hux+ioKmHWcrupMeoTmljAiTlOnnUgTLf1oHxaRC0h8XqbAP+J/B37EZy/w/P26946yvXpQF2gLxvPdZtqahbtctI+jhOebs4TgS4/pEQTnOdLn+L5EPocHjs829jkPANQ8XSiAag7fIxI0eEDtzKYPMutyJOUIZVEn+LJ8UPYYYME4rHx8UGMf8eUtLfWcTI5xW9ZPKl4w18roopb0d+JRaIpnuAMs0+vOwTPnmxKBUmW0QcoRX2xDWU8r1eVW++nAlAeu5eAKsDf/ty3yLPhpif/iece6yNPrKhPuqYyfGq2hL9Le/quDWgcRRvjAFrKj2JEcqIOUu9Os9LEM825EFaRMoXJiaDP1QNMmiamBmRnDbhNIM92VkQZVBX7xXOxG7fUNK0iIInnNDyN5i4YQEuzeZnmVo18x/CkeE4KeTpP4fwrT6+uHnmyYLfuV+S5ZKG783Sc8BTQFZvPX8jTieLvKy9g+MzqOot8OQD1KOASBSgGz3DQszzCxxQ+aDPudFbsyle1NSZQXXZESx5rjKeEKrtAp591zg+8A+MsrbfWiwhAFZXcJ6w3PPNU4jUzKfSZcJ7AFNDAk+AuU/N9Zgd0mW1YsqS2623kKZ6bnCc4cXfxLPm7A7UBFJxvkM5t1cBs6W/1aQLd6RACEQ83YAaeQ4N5naxu8u/bgUaiAagS+GVRn0Gg5dvFAWfguKjc9Kmq9pQtc7ZRNWRA+vQmSJw1n1lp1jHToahfyPQybexKJluTeGzYnxbOxUpLPfVMecmcZlfnC7x8/JxuKSb4jcvo0d1S30ir/Zaeu5RufrGQp6oGX+ft0twCTtZAz1338fakRotdnoGnE+Vj4InB0nFyxTXqc3gsnwk8hcqBiihWBIpJn6HnIDzjlCkQjVZEWR1sKebsq8jdEwmdpO3UhInsaHKQQiHQZNuI/uUQ1Rqdpe3ZVnBmtZafCJ7j/cz5jljcxFqcp3AqdcecZ8rzUco7MfQmutROLZFq//SzjeJ5JziVHl0f9Gk8HahPQsnh4fnxtzVFd/8w8hRON+N54or1TtN62HDVNfA8KwfPpDu8gHpMcqIxWyrN6KWVCBRzogFpECYQUbB3MQofqOxZRTfYJhWxGenPTQ2QHq2eHJyEZ8fKVeI5C84OtjtVfkALMV3e6xCgYc9qsa1CMWuiAa+KNUWeWzpQeMrde9LqW16Lt6d6WhkxJM9vNlbok/juPCNOFpnA+dx34Ny6uvkkeLoZxRJN8Vyxx4l7LH/HcVqXT0ZQ5zkBz7YST0i5RJ2pvF5AMXA6TwENLu9IQ0XZqEvrZoItLrcqmsUpXdSZby3ITcDT61bpfh40dS8CeXJ6QddGUuS6qUb+5mkHqvYobokmS40CT/7LC5iANntypBN8LbWMC9ke8n/qvDzyzUbnuR6c0mf09yBPvzm4m+p8b/6tBtt2m2YbPrl9B08nGnjySQTKVZ0dAOo4z6fawaNFnqPGs8l5OoeI1NNPzC9x8JnAU0QjUp4iWYRZUnm0XZZUUSNIbkwySLGAqSmOQo8PTsjfQ1NRxaNJzUZZCgGlx0GcY7qBIRRvZ7ZU2+BRkolmg03mxRMDqXja3jvybE1ZHtWaZMRuRJ53b17Ak5YAUZ+hHZUZ8izi3L4ad4fnh1GfEafxNKAnXmg8KVf16MW4/AkPP9Exwd6R89Q3DSQjGpEKaMliY0wn71AdbHBw+9MxpEVrriIZam9Lp1nrtY2yQcquaHNDF7xNnOrcVRDO9p6Ux0C8XYNNrfHcAj9J2AKJfTnjuYVwbuPDvjk8PNWts2m6LkW2Tzyra73x6gcreIIz+DueXqqHejrlSXB2vH3RLgmP7shTFnG6PgPQHXe6CqAYQBfyDBMm4QRoMEdqEo1AF7o8Tycb/0TQZYhlLGpGS1QxUc8Di80gliVzY9AkIVornmG7SDcT8Pb2LOOjjdlaxrOPfGucF8T5UajfdXKg8CT0Y7a36QGJuWkTY5Tru7En8xi1QcXzTo9HH+HvXiY89Jt0berQzTMPfPOt4dx2mwYfPUM84ukweQSe8vgTV+y4fG9FIxRKBZ6Lnwo8+3z8lDxdZ84UYAGoLAJ1omFyZfz8UzGMNQMCaaESmS9jqP4SO3LUu8D62YeQkSrBE3V6pdVcXrjTyZRohkbmfKz17oegFdBaH3foi9RQ2osLe+/iqZkpUStRx1/Kbdr+8QepBbzReBKQ1iNPtQART3CScAact1Hb65lHtjec2y9dcjTydJpx/HSuJZ4OdMe7XZ4YPK/hlo/83RNO8TQLTiyk5UTd12CKFfkZwZJJlrXTyvmUqRBV3PhYZQtKIgrSsJ/O1H4IoObtuifXa9tv9ebr5GzimfA5PeEItPA0hdrITrYpDUATCzwtrV/iv4eWxmRf59SZ3NSk6kjguSnwtHDEYU4/PIM4qSF4y63AFE9anSFP06f7e2ApsrLIc48VO7z0zoXCeYx4XiaeOfEM7q6DJcFMqgCNo2GECtVoMdt3azKWAGQqGKxHb/DU+odJVLsa0MzTUIr+Z7K12mxv70nyP5L8AA6M4sKg4Yd86DuRgCb69LwCoHRCU0Fw44nDK8BjDSqxjpipRtA7NrDq2GMPfHNjked62y1m8uklwv30jBfmvuXynQPObbdqnnaeQaDm6G6BZwS60w4vLT/wmGMoumI8VzpPW1+SOj1GVwekTjTO8CXNGO+L5hSb3LUxy54No/qMc/WtLd3H8QLxBKh83g50mnXi8WoiuXZA7fX/JOxcYOsewzDedKVOq9pQl2WpS0u67VBagrhlXRMEI0q3pZlrhUwmTbBgEkRsySGITTAhk5UQcwt2KeKSjK3RuMSldYm4E4mIuUYUz/M+7///nu/04DnntDutzfbr837vd3+5W2EOhpn0J7D5j1F3gPNW5XbAzccVLdiqVLQmP3gyRxEnrhbFVFb/5SuexxUdT4vnl8bzUax2qCTAKpwwYjkEXkOFm0tfwJphFu2dyx8wnq684XScMigHSQa0a+yDy8hzyyur7z0ZPHtOO+VAtlAalFMxtLGoj2bSMw3w4ekSSgtvcyVZCuU8qD9TTw1xyqHISvwKngR6kxeT6+HtfdiDB5464mT/P7UrzHpwJFlCeF9vCYglACSM7pGPKC7bYB/0AixTzb/qldvuvv30d38XTwD97VHsXvIKNdibaGcHQfP0zbWNgbP94uVYNwJPQ+qRLpYC6jxNKAg6a2zsMfqTPM8dxHwIp5PrWUaevfKQIfVmNIDKoJq+l7ox7ewkZUvSdJZoKl01NCeA6v4giFB9uR3lZtg7VVOLPw8ZDJOf9v9EB8IHuV6AgtJ7dD6FEzEPf3IrGMQYau/AvPXg/LW33XHH3fDnY+JJoJ+D51bbHA+c10Cs7Xjryozmzk3N9Ye0o9QneBKoIbXMLnH0rgaUyoCeNDb26bIt23EG7QmuFltNax2rpvAxiAZQikB9fQkM98tkMElSKAXTUW7okwZqgJNAeWUQ9324fX2Ro28dr1Duxg/GfkL0px/45vDAwhpcs46ET5DU0Z6zTbsq3q1FwDTTnP6+G+ev3d7bewfqWLIsLHlSn3/7yRdb4U7Iinawzse8PXJzNjXUF1B52m4nIUzPQeorhT0JNBx6EBw6Pv7GVdtf2fTEkkHwRLjDnsQpoIG0zKFCKoMyxrGijAX6I0NAGZhwKaxYDkAgNnp58EQaJ3jnDqBI9T0YI5bYe+9ug93bNA2SjRh4RA2NeQw0DCTe4xvATeTEyT4pe7rYDobEfsRVr/QeTp5Pgyfn6whUPDcKJ/Xu05ubG3OcOF1YhD2Xg6duIHo5gMqeQTRxKIGOb75q9doV5xx3FlY3W9B6amYjkKYOdaJmUG0k0aYrdHBkS8EMmhs2EObo6Kgw3kieFNhhHyglouw7gTA2MkJqRrqzW0cgL2fMo7bRibNNAETJdxIqVhBnJ9S2oB84N92Gyw17UXfxdFbVdp6sdg6eG4UTxw5H6xsDJ6raFA+hPc2fJ70Miv6EKv2ZOZSf5NA/xzcfMV88WQpPhzWCqI61kqeAet9J/sT8OXhirOwKZ4omcG7eDJi4onY9ddOanCfgYYLSeEIM+XXziJPWEs82wwnzUTHj16BehyhzqsBYSh7tXGHqPhEr/EObtvCuyN4tl5Eny+w6zy/A09z5LrS+bucynDy0tdJwyp+0Z/Q9Ix2FPwFUSAG0a2wcZ6AmluIqGatBpJPVOU0BlT8DKNc/5U/t1DCFMSfwWAdtAE3hJM01dg98jWji+sp5mD5Hi5sDnefbbNnyASfKTjA/EqdQOs5azbwQqMry4iJJW6qrC5ycEVnQP3DT4jtvu6AX925eQJ4POs8vP3j0S/J8deOqdzeC5seHlNEkTrqz3e0JvexA3ZxhT75kUHlUp/QE9K2r55xCntnB/8SezjMCje2n82S4O0vRlDNFEzjlTcJ85hkeTwVPbNhmcl/A+XV0Ujnzqw2JhhN9TfC0shPCSYwu8cxrnRInS04VpTrhpAznmiGci7Nbiy+4DTxZJgk44U/w/Iw8N279/fePrf8emaiWJZcQ7eJJnDCoeVTZiCgDqeQWtZB3okDa7ulIOKMBFc5Kntoew3BfkJhzQjjDnE6TMFlHo8b3v/dbItdGD0p76tHN4U9KGam9wIQjns14UD7PKp584zy15JG5sxuHuS4fGXpuk/O8AzzPvE48WdMQPD/DecOtFTSZ2BnstKdlIwClyNOBpvEumnnQAyeBWtCP4yxU0dMReYpmak8HynzEISDtic2aCwKneCY4Rx2nLqolT550ObuHwV2COEEpnKyXAprFAheEMVvcOQMRHPYUUOEkT6jWeeK3YJ0DAyWPdTSdG+4ZeXDZXRlPnC5OeSLR4zzs1yvLI53mbEbJNeFU8ymeROkKooGTGCk3KC1Kogz7+rJ0lOd2t2f4sw4hQZ7KRok7J+TP1J7rGeviOVRjWwf7Odtb6kTqsYiHsFhZgj1ZdA/bvlptgQOZPXgqHemvJ554Q55oNYWTGxn8INeG9SMPLn7u2TsvC57LcO0deAKnVeVCU1pKaTrOQspTkkGr4QyoKdBZSPVE2mz2DJpeRzgHqnB3e3YAZ3SRKKSiCcMpnl8p3MVT8Y6k3wOcaD1LrSRAoBBmfYUTQDnRh56nUrum/p2n1v2Ak1JDmvvTcXZzH8j6kesWL3vu2U3k2Xt8L3g+S56q+i6eLzQFTZmzAcN/4kTrSZ6K9wh4Ea0a8VBKVMv1Y0T6Ya3zlGSNiHZIPLnppTLaJwJnYk/xVLz3s16BJx8eG7ANc1AbN9YUNVRAlzzB6TzxWYETPFlAVudj8qYTOO9Zc+sQeTLeLwDPLa/gzNp1a4ynasK+0Rw02XASJ2f7DOdb4hlAgyk4VeEpqKlH2SPQDsdt/FtX0ozuklpPJCPuLkzcOQGgFTxHnWe0n4fpOmNlH8Z2yaajSo4TcAAUL+J0mmZDzdOkOJvkz4KN2UkzwzlyHXledWfGk2cAb815fvNGkoaEkz0wP2pzcfAk0a4I+jAos3wS73qGRHSWId0mnmWzS3jZpyzaMfPdYZtay3FSwkkRp8I95blAO98V3wTaWWorCWeROJniQda6SoIJOcCUp/uTMRrdzokNo+KJa3Pkz17wfPa5xbjmkvfbfPXlV+1oOKsHO3DKnuO5P+VQ4XSkDhT+TJEKaIhx77twVfQHclvKmtH3tFwUOBN3JtnIeUa8HymJZ9GWgDg/jBnLoklzncU44CTpF8IpoHxnPLMpT1uHm1i3eXQ9eaIOLfM762agO38peLJAyzcf3OMNZwS74/RmmDjdnlIXDAqLzlLIJ0POVAQYMMOkTPZjHyZjzJwocCra2zAwCpyGsr/CnVCK03ieKCEdAaHSua1RluHkrzUwkiVF89941gdP9js3GM+bh3gL0ert4Altxy3WQ/TnPd+M7tpYDWcz3Vkwd6r1hBTtFu9daSMaIR8D+STJp0QV9dsasjmlsqXLaDzBUwObLBHxta6i8Uyyu/zJiT3xbIU/AZAEhVNvnCdxiqcIBk7KcCY8kUe4xdN43oT1Ul6T5Txx+RKOqI6sh47EpNz/BLtwkqeIyp6SNogkHk369dWQ5h3St2x9iypfiZc7Na4hT43Yk1CHUpzl/jyYCn+CIcSmM3jivXiKmuETzzz6gydRwOIYWfGaF/DsQw5cNAKe8icq5WzBZVY4V7tmzYDyUOCMzF4Mc4Y7zZ9dolk1yUeal5KO0wl8JF38bcjmIdB0nNaRx2HTfAKeMEXTms7KxlM4HwPOjCdeHA1lA2/+tIRWPIuW3Q1i8ExXDNysiKH6InHi2pzSiYfNwwwhCs/BoMhHm1hTAdry+F33gedN3TunNB2nJXYtkzhN8XSgjPZZeAhougYv5TST+RE+Ne9kwri+a+ytOraY4IgnxX68cJ6YudNbzcSceU8+4QmBpy0xXY2pEHXf63xfJ35w5MmbFZyn2xMKkjFcU1fU2njMpWMJHgU70XxiHpU7xlGOUvcGq8bD6udRqeHshsYKEWfedLIvHDiDJjULItKZmUO1ahxKO6IZUT0gxT16+F3jHzLkY+8iVsI16ZmtaIQ136A3BTOCPYl28BwCTxG1cBfO2dnKObM9eK4sFJOriIJlvtdMPPGGpcTBooCV5PbuI8UTBl20JHhu377p+aFbTnRzpu7kH8CKv9bjilhPzBkC0VD1rj0fQTSkyTwMmd7CSli2TqxRZjdnLcmTOMt9GThFMzWnFWm9CDwJVJMfsqdWznOent6js5QD1Xvi1LwN3+AnARzw864trTyyKX9aacS1iHfSxK2X9y2qb6zqzmbhDJoVOLu6zJ2iyZdnpIqVDz6rhTxBOtCYb97GvzJnQBjrDHbOASvWDWcly68yc0IJzKGhi8Tzao6HlI10KZauVShypGnpnZDF0xU41Y2TPdl8oqVosTUmFMDm/evkOYgt+SuMJ/X46vv6Pa2HotupXVCdCU7BlDmFU2E/E68kx1dJ9MAWNF1XGtjcojsaduKudW5GarOOUuBUjIuklHjzViinuXjxsmXkSXl2Jzmb39RerhnefOaDo4YqPGlP50kg6MXZOmehdCJ59tn1FlZrEvEObXrmp23BMcXZjMQe5lyuTpLLaMqeLrOnK5BWDXoKCMUyBPtaop/aUdtQh339CFRNAAfNSpaehqCA6TQvQtEV58m5OfFUQ0mfAaj5E2P3mJcH01Q+0SSe2mkKIewLbTzwznPJx4DnDag1SX8+9+jfv/5QNdbVi2fjS3MKZwozklF8mMlH8MQraEaid4U9+YAsLc1kX3Rqxw+1BeQhX48UTtEMlGKZjIhAEgJMmpM8Fe08lQGetKd4EigNas2neApopUtjoslzky98zm6p4Dn/idWPX4d7gP9snK6yMSbMmbkTcpqaBXGFPcmTQR80q08xxxKIaAZSj3kR3aO25B14LQUTZ+ZJkhTKFCZxGks8xTOfTbL0TnAkpCa0YOldPO3r00xKgFlnHkgg8iTUuk5smj+/58LjcD+Q+XPtETN//fv7HY6wyaVgd3dqsEuYyytwqvU0ivbR3Cme0YJmW8QiK02PeMEkTQifSNSA/gmLNq08zDYc2UpwnzI5FCg9oUf/SO40ASl4Yg0u41k0cCTnIQ+eBGpJShLPMGnYEzzDoFZQvHW/o489i7t0kd/pz3VTv/52QpODnI5zdn19QZN8wCmeQZODTOAUxXCokDrPQJrkpCTmc6BkqReBqnP/4dSOpm3ztCMOMAeS5LMIz0WLRkziiectFIAGVoyPPN7JM9uDL570y+7oSzpmk3hS5X15kwe8ljrBk/0lXg903DFLB8+54Yri1N+/fUpjpvJgB8+6ouGkOyHRFNCI9Dze5VX6UxLSoBpKDCqccqcMqqAnUMb81A/j63wn0oB43qh9Cnws4mPkGTyAlFCxoxNIRVVkwRMyntZOmgBUcburRuIFchZRb1w91+fLHiHiFM+WzoOP5qU8qOEHnkubgbOLMFMpF2n2eIbFeslwXh3dJDz9GLFIEqOgBkty5TpyyFDaK0JeSokKqBrRsYce+HDqu7NJ03FyE02EOy0ql0okalDxcKCcryPPVmFDTs87R/wnWofejWtpmy2rY3V4ScfUfhscjg8tBzLezyfPYwYvaZr6+4993Jwp0KyjBJzkWVpOhTfxIE8zZxeeWcQr+kExcM4MpNXH804zgHrY5337gz5+eeq3A3ouGTABp3Yl0aEQSBJoinTJzS4jKp4yaKGOOCFPSuRZzKv7CKjzjFXOijbVeQJ8faENdzs4z0uadv77j3d2rkLTcNbit6CtZtNZMprodS43nHqYYEwCJEj+OpWIRtAHUXo0kryC3nmGsp7TAe999ed3rx0EoBJx6rj6+jzgiVUS1SVOUw7l/DwMytW3Yp0mlYq2xxzi1JsOwvgpKOOp/hAhqpvqeT8nDJ4Qorfj6NOIEzwvqW3c46c/Zle1pybo6uuLOifPYEcn/urwp813MtzxokUTlpHj3alpduIQVESlIGoKniTKb7zzzm+vbX3toHV94nmMcMqc0zWyaOTckSVQbtAjarh6RIO2gqfmlIp+BkI9JgD1+0WNqDbwO09D52uDRlSu5pQ3NtZ3wJ/AiashOZP0/U/TUjuewgmeLVy4Yi5iJgLRcKc6nmo/ZVA8qjk0V2rR6NtHI+oOVbgH1JdeuvL7h99/f9XWj/r6SFM4RdMBUnGS/VzidJrzb74FpwprbKMSeNKfLUWbtfLxkLZrc1+Uas1RfhaKQDMrRleK4ne1URHdJfIEUFsL3vFela6ScKphaXec2SgzevIQedKa+iS41W06MxnQp6ufnG1Ku03BEzC58/z6FydRzv7VK8/uc5qoVeLkzmWFGtYFgHCdKD+5O6221y1W1a+GR4sA9EDwLLb46luLAYUwt8F/p581ggh0tjGUFaefycs2Tc9QOgLPnRpNxZWNlTg5yuQOHeJkuMewKGbjRVOvWXya3J5VuCryp2el6mPPHChgfvv553/t/fa1b+OA+KovPsKtr4OgiSJThLkkRKp4SKSpGoleOJH3qcKh4BkX11jEiyfikJvyD2zXvUXanKyTt0pYRA/hQ2yvoFp2b5ujWyENp7zYWK3jaX+KJkFAM6aUpC48RJMKmOQqoNWJlkk4A2gMlShZEzt6P3nt/l32njs5/PYk7oNY9cUvfUsHb2QdHcL0ypIr8FwBN7pUcw4ylqrsWXM2y+6CZ+uMUIHgZiveubLf0WanuNSMEpjj42I1gOorktyJr7eecjR59tTFIL08H8UkiHByNl6KpTeClD+FMX/GW7Wm/0k0ot5oVlqUNLmf9/7z9px76PDbwwT6yCPvb3z1nQEctL5COMksJIiOUt8wmMC5FvvBUMDvSG6v8WMDdhSTJzjs/CUOuXRYuZVWa0cLBsvP6Fq2sl4WDyTkR6BbqBkHzjGchWROLgbuFuzZyrBwiqYknGPmz6rKm1J/VChCvtp4PudpgY69+/c/vPfchXOHjwLQycnhSTgUQN/DPA55EieRSeSZEqWcKPy59JiB4/px6wxkB93wBNWC+w7h3nbKKXZRMwQTW9jnVwmAMb7CIzNkTNbCie90WOvZluDkfs7mJlAVTcfZglmCsOc4cY49MAakY2A2FhatJktNeuqDYOpDOLRKIyprss7Fk9fvttfchUcdetTw8FFnDA9PQnDoUxu3vrf0nBtUSVK8pHBoKOId++eXDoAnLg2y85niasfgwIbEeGQEsmKooAfrQfZNFKpoO1D3hOpiOEk8TyHOOY3l2qNpJzNwfXODbXKs1Yyn7TcTz3HiHAdMIIVg0PBnVZiUp/3AqTQ/LS1FK0qaFuYw5pv7L1y4cC55EujkoeL5+hmvr9r46seD8KeinbByf5pW5DKgJ4tozRUoG9CDq8LtkkFIFxF1qj4iT7B2IE9T+G/UjKr/ZKjREOzuRx3xG0yGE7aeA5zna1kj9sLPpqtn4MaLkvpkviMRYlmKcQAlz3Ez6Kl44Nn1n4rGtBrTSqBlLHEi7/4nr997r7mHQgR6FASgh04K6FOPvL5q6xebzwHPxJ5G02v1Ku3nRJXfWbns/KN5asnuFpYT1WCiK097oluOXo9dloukBcIq+0fSXn7X7jLxk+PEypOap6GrlKwI23IbXN9ewo6UkupRrDQZTgU7BKCZPf8P5o9jswKrcCYKmilKpPLzrt9tX7hyIXwJYy4cRuNJngh58Dx0cvLtR34G0G8vv8LjPfcm6BnKGyQwDaTsfx4x/4al55+GXIzSVBDOLolsfjUuLmO7EGUaeni9aysx0r92pdYcljz3y9+sLAtEpPTwfmf1XFhIcHKnApe9/mHsvEJjq6IwPPYoGo06CWocY2+IWEAdo6K+xIIRK4QkFmzzIIpiwcKNgg2frGBE8c2CLQ+WBzt2wRJUbIgN7KCgWB7E/1v/2tnnWNA/M3Mmk5srfvdfe61dzt6xrFwHzjzxzCecEX8Ee6Ki2BlVLHmFJjjdgv6bM1/58Ygf820SbaX7komaJMOVbwwP90YnJibEU9odoBHxtuhbSknTevz0kzpK71zpdITCm3ImLH1cr55mWo3a0TnkA520KTdpZ2bG13RBsJLviPY9z9TJF2wQA2Lw0S5wI4kI8wZ5Q2fvqCech+9/6uS2bZxKRuuvudaarKcXTE7r/FAn8X3vs3nRK1X/5U7QJdGSiVqhjhIkWSc8yW6YO6waGhlVJkcAhec+BWjylERTPAVUTej59ici0AvMOcln9SbVLFQ7N9422z81xoAmfQ6Dj2QQ3iO9tbgOiRvod/uTx+4LYQW53CiIOFo+1tWbOtNaYN2N2RT8yFPP3D9BlsZT3fT119lwrW1kTeH85BNwcrCMTKPdLj7TFiLa9+J7+HJW2mY/bvF/RUtZzSh9Izd6z3AJjtpKY4eh7ohMOdrriaLogXN0FKIF6DlyqnieAFIifumnn5TkP/9o/hLXmTYnMJPlACVSFEQ7DyzODibPhNignEHJEZ8+Clk6ds/J/oz+jsEkG3DE/baiyW7j2rt9X+FW2qF59a7jPn778P0nBxpTQvUWYd0QwvTljntcLKDPiKdOifSdhi9Id0gn3fGH9OLvv/8CXNOFb1vb66tIx9QdqMf3CILoF/S79OIfJ61atWpovDvSHUG9Xm90tDfSA2ICbTn0BIDCFYdOB9DXHn7kq8dISI1zt2EJTHANVpgS/BRXnZdvmx/0Z+Y5i1YC64AyVgA57kLzaeCUBmwYAc2t5M7o+px66rH7qjHwYS0+j2lngl4bWkzOnL9dk6aTkUaRtBkTPDk6mkPjvlA3j1vjtNGvtrs46aFVoR1OOumkP0K/I/gGYT+rTC5lgivaXFqt25uY6PVG9ICpkY6MjAVGeNaQR3Jm8DwHhzriyUn3Xalwb+M0TaSLgVZ/Tp0/mFkIN0vK9mI9q29nz/dJvljXPI+EJIlqK2JdoLXHnhqDPn+f4PuwRf2RI0/uL8weV2GKJtU708FraOvVC8D59evC6RuLOQcl9gK8A6DW0NCqHXbYYbUdVgMLAlFbIDc3/bmgt/lqq3XGx8c7q63mbwA6usvE6IgYBlBFvATWADqaOUkhbofyBp7n8IJBv71GOemFd+ZpPAFaeJpmHirrA7qNk/Zz7/Nnzj9kpVjlSG8t5dDJ7jpvbaawl50pUkPyKKX6JDlfe8mLN+d/z5CxBFQNwKmDSw6ZXf1vyzo3ZHMhzU0LJ7mItpOEK5yIcGfDoB1O2qEtqAqUZE4hPhG58U5Hl65Idbv6VgIo7/hzPAV0F8W4gXbDoLwdg2I1KECnAYpBnZUmAPrt8jX3COj7FOuUSgKaIQxQ5HifKzjJ75fMzV3y4G0WJ7rPsjJmUads6t8C8j7FnxvyvbG46qTA2e9PctB2nz6EXD3wEd8qBuYOmZrarY2T3hCVvDaFcaX0CQfJJlCImqfVJDq08mZoXF9DqyFAgRiCqIvsTDinR5GBmicahijve/SKSEg14qfDoIie0qWXnrO7DLqsJvTqz2+y2WTQBApRxBvTBCZpqzO1MDc/tRh6VBhFVAsLdcDu1CG4O6RfYj8T8tBWKji3OpJJYI4OOzZyfxxBjz8nGYyf0YFzt+3ZwulRTrr7sifJSDy33lpZRDyNM81JkEcDypuUQt9Xqauvzg5iVYiiYBhEOwk03v8DUD2GxRO0CnlSEjwrUDvUbenEU5e+Nz397XIA/ep6jXQQvW5Dfeo5XkNUStSeUfM/2Nl7YUFHbPp8Tc6BlUsXH/CxpfyyJFZ9toMyzu223Xl/ZSlwnry/3EiyMk6qrb68vrh4ydp/X+m1jsrS6yLanxBPcNZwj63rJFACFFWkCdOXrp7iBlNL7ytEfIlD7dYEKmgCClGTzGQvjlGDZl1fmtDkec6lAvpeAH334Ue+vG1RYJJpKUKRrhTy0YF/kCjvHLIwO7VonMjvOFA7/jXEfmGur6KUalRF57biqU265c9JmstjT+1HezDgj8icg/M53vy2df7qTg4i3Hgb4aT21HGnvynWBTPMCU8JfwLxJANtMe0oQSGAKvDHh4JbQ2FWQUygjvkCdFxlEhTNNF8pRhXxAJXME6BcuIrnU0+99Z54Lt+j+Y/7hcVIHwQpVrMSJtaE52LnkvlDbrvxARh6cSZvZM/b/Ht0VAdCpb0qqeG3UkF/uJK7DKrP1J2KUh+c0RUY8E/zwGIr2u1OzQZvqtnL6Bh96MoTnMkTmtF8qlKSKs6qVYZpyWadJkzAmWIC1XvyFTRdNlF3jjQFUKFcAVoivVxOMFAZ9NVrNKH0+a0PVKCQiX481vwbz0umFN+JsyCFp7P9vLbaUSV0JCNQ7oJuxYFhcRCTiqb+TOA807EunPrdvTdp44x7EFV47njGBZHaOf4Znh9/ljQTp2I+81GUTKsI8aqCk2untpbws4wxw98CaK1DHeZkeURZCktL/rRDxXV6OlLTxKUies7S8qvLP7337tU/3CgoBWgSRbSbFSc8FaGVJ8uxwZnO1tH5KjxPdm+SnWnp0HO4lUV2h+ahk8pPxDp/0+I2LZyMIatSYlbvuoup4+EZODOxI4H8Q9vPAzMNaqSrOmbZYqpLbS6dnFIGGZR14Zkfd0dxKCrpqFqUh/udAMWn02QmRTxSxH8bw/VP2qCAqaOg7olWnMnzxqY//XsPSlRPC3N5TNjh5smokoCGNO4Uh9LTQerPze+tdkM4z6/JCJoxqrQRi2QvACeFkjvtFacyUXQ1C85GTrJF20RTpUJKnNWjeNafJ1EhHx6FJ48EavUwKCmpDRSejDRJ57y3tMT48ru/3goXZyUDrQKn7bnYmYWnQLjhJNjBiSjqOcVOwx7GSfdcVySoHO2hIhSasukCf03UBTs13OnFCtyvoWSUoyCtVARMEvtDhHvRCs6hqJxKnK8QFUdeMGnpDTUtCsrk7F4Ab7oABWZP127NSiLZzEk1xTP/IYnldPB8+NdMSQWokbZxiic3Ciw2E7z/FRA8Z1QVEeyBkQ5nyANMVKHVnI+63T2mhZMZDbYIVKwrF6nsrO5McwITJ2JLsdycal4kq+r7cQONXtAQr12IBrqqDPMKlEsChWcBml360YmakgyU1ARP0IKTodBl7dp+52nFoAkUk+oVZbAHz1lVTrynBkWLKH6mGvT84/cEJ0eXM8qcPSSFPc2oeknozDQnwX7b3hu07n/RlhXra4swuVP9IpXx/xTsCFdC0i/RdBrlsF67lJ0S8W0JKNJntmhlWkjWHr2Axq+MrBgUoElU3xHvzklSUMy4V5J/L/TTNa/+pCb07UWV58WgVWlN69GOFg7nZ+4hJU3znB2cSuOJyqh9jBo7LTH4pDyktE7L6Sprq1YdzyrZda7ajVWIxvlzxSlFJmJcCZrIQKNQwrNhzeHhgpMMVIlmJ1PX8mH1aEUbQEOUofAEaHT4IeqclAaVMsXrClTI4s/lVwF6xWnwBKizixpEHoZZ7AhP5/tKWRdram4SnNCEJzgF09MgMfIZoU5apyoA5/zaBed6a7uOf2nXbXZULlKp9Ew7F9VO+0OmCcqWoArOYQrOIeQOZu1Wmii+bRMtOPFm+alzUgWKQ3WhozQxCs/agsKxmBSDfvvqqxHxFxloqqKScUt72VmYp4QyUZgW0VDMHn/svsbJI2kaZ3TiyUOXYM6Cc2rHZrT79ix2ZZE96RaBsx3uAK1Kpm2gqDvcHU6gKC3a1cdd+U/GrbVTG6gqUP24EzmrM4JBxdBNqA3aLQZFNcXDM1vRaY2EvhoGffcodchDMNSr4pnv7EzyuXiev5ATzLnCwVR5TM32T466E+kKzTJHt7OSuxpOhvaUh1xkKX3NpD3rPQa+/0XRrtz+24EeUmr7E7WR8iTyNwdo5CNxQ+IpcKjGPCjHTfTvQNPJ9JSIeIBSNNWqyRFP0dR2KAIoOJeml5Zt0Ctuzi55W6U2Qh3NMGefCRlp1p4zex6ZOO1NcMbEJnN0px4/INRvW8ymE5x779ruta+300a6QZnWMxrPD+GJPas/6RPxQOR2PfXYPN6KJ1KpZIPqYXyWG8WIeIPNhqBhT0u8M+N3MSYi4g00DVoET7pHFt+I59K0Aj6AXiZiz1oBUZdnXbU/bt3Q4VYWgJqn1zUZ5/mTzLcR6T6fEm1pmsfGLMg897hGsDMupV/pr910J4X8ulp2zJC8cXqRJdNviXOlfm8HuWHC00g7hLyAhk2JeStgGSdsMl15PL+FNIA6x2dZH/0l2TrpGiZhX+Y+JHjiTwwKz5/uee30s8WMTiSKa2JcUWdmDp51BaMlnCf2T5Y96QzRdMqcWBOamqIjqy8Ip7sCBSf2rOPxqjxjR8DI7Z+YZ20+S+up5J5jnjyriHg7VUwhOo5HEdhKWkJJU8oe6N9yEhYtEU8LClIVnT3K19pNMk+A0j1KqQHFoEvLAOWom8KtImQHD14LT1ZAtFYxet5j4bzj9md9A1PBGe7MazK4lDiZFlls4DzkeOxpnCQj7qiVPcGpZMQSLGbCfyk4oQnO5gBdkiwKIifpsgNEMKmFJemip+sKFRFNg1athLw+B+iopj8kLCmg2ZOvES+DlnGRzEuOeAL+XAx64SkSCH2tgmjyBKeBgtJ9/fkrzzx5f+Axbxmlp2D6fo1Twcm/guypcHcqYjbgutp6gtP7f4LTA/IO918aOCWPIreBhllXbErgwyWQ2owtk+JQIRlzF/KfeQZ7V/UTmocDKcMg+odxAeWBkRrx01kuFYN+a4MK6OXaPkr0ePqSWvEnRkucUq7G07l8g0mNyNEBYqATedQzupcDNbqyZ0yMePhkakoTops0W08NKoFToyDiWXGSjLKSl8zy7tpfT6qU8iYqBmWWk1GjaC/To81GVGXk2NhYxHyLJz9cyUmI6Q+ARpvZG+90C1CnIwxaUKZLMei3MihAZdBrwXgwD/M00gh6XuE5mzwtT5LM9BlwZ7zjUMaKjxNHFjp4flS/o1+yPT0axWzTdqaZg3QsxqVnFB0j8VxJRunOCvON0HBoqKHGDGe1Ws5hlqgvRAEK0p4capKZkYo5DbQTBp0QUBdJIwLaQ3aogTrgG13PtkFPASZPxDfFocS8eGK1XFJScc4NjgffQGKeWQi9rCRevcZ0itaTaDfOucEmrTFP4YzG8wJHu9djGWea8+5Vd+sBTgPlC1WyDvuOiGZdWYjS93SlhPhQXKCE2TrNkqkF1N3OMKj5jdaIb+SkZi+ppPg0qFaCXwtGI+WRXIvgCZ9AWpZBaH5tYCPOMf4+R0mFvOwRmIxRMSxldyp7zQx2THcyOcwtMCT3SEaefis4PUYHTvTGKvF8KvTGmLEmVap3pMFjXus8MSaTikVXiFJUmii5p4jfcuv5F4Paj+R4pyQAm2cLKM9pDKqJD0kG3a8wXGFZgeLPWCPumt40Yw3EeYOyegz/tpY/Jk53YDHnJfPMb27SmGvfUOuO4ckZERUn5iw4H9FRMrc/JZyieWnV2NgbY9BsITXP0qHk0gaaDh22QyksS1aq/PWCikFR0KMIDZx1sF5Pj4sUngANnvcS8ZfD7wA//bBVrQ7IvLYhNG+cyLEd/fvoNEFUwKP0z/ETwSTUmS32SnnjlD19EgqtJzjLMMjnL2rl1/PyJjRvlyffkD3F8y0peE4IqTTMo9WcioWRJiS3om5Di7pj8MShZo+MvwlUI6FeC+ZFNyND42U6SYQRPDMZ6VIN+qqAyqD3XHgAGPXCpeHRwjNXi1hM1ptmMeescXoSiohv3O6gD2hrmY5bL2hmtPvkDuHEntu7m6mVSiyl07b9hinhTwOFp4iec+nEhJCOIrBWolSglVIiCqA15jsx0jHmkK9NLvLPJfeSiHiwAbTbWanqbVBUi1CPiWTAf4dB7zFHeJprU525EtWWUk6BOZPOrWkK2l6Pm60pZh4I58knbyWa5R6O9TixrhHtMcNhnM8/D83UGxRKQVRMS9iLaLxKYjoipDkU4jk21CYKUpnVTSRARRSgkjNSBYqXoxsf9gSbpJxknjZomycGrTwF9GjVTGcd8HcBGIkn9EAqBUZUeWJQwLV+5rXOnnpnreL+x65VUntG+0uB8+LA+bRPlIAmZ0rcLqLkIoblJQ8dx6ty0ZjiXQ//7+p/e0xIEdjqkJyBEvQFaNIeH8adrZA3z1YLSvc9F9eJV6/241kzood/UIFOE/DB8yAMetgBOpNA2muvvfz0t+ZZFoQaIUpofi2QqZvEzkUUylJKVWpMJ6/d4Bk4d9UdBqqVcKdxvgBOlDMclonmheHjMWSgEFX8DgfRpIZq1xygVukqka4RSakNNImWofoAKmxLSydg0BA5CtOmQZENClDz/I6a6WCD9AtXHsnTiJJiQArCkl71hQA5KVHiMweH+IBFIuqOqpu/RukZacqIUl57+etet4s1JH+gJtu/Aufz4Hz++TuKzDJVh+NTIhpIiXrhtUG7DaJ6ZlpCBtUC6j+cOCtQ3rpYjf+AEveSI941k3m6G588MSgRb573qht/YcHYkHjy6GilPIhysW3RwH7EkHoJcpL68nQ6Q3xrmDtrqnP15rgS9vwAezJKJ5xfRKyjF15ExgnQNk96nKo+x1pMAZpRX0K7EMWhqDoUuas0ikPrwjFXVHpaMqiBniDbfUvE5+LQkqUKT6f4LJngedBBpPhKsQUWntEzh6mUzjNEJI5hS6ETO+AxRILiA4bypK12Pnzt1qD8Sy99IJ53Kdo1w5E49QVNlDTbg/KJE5W+J0h7Y6MSYT8ipOOVqASr5BOP0kraoXQ907bVoDXFC3jyXF7apdsJh0I5OvAB1AHvGpRB0Ejw3x100Ln3vPZcm2eyjHiP6aFYkSSo0mRIlrQJw4damAg75t9jkAmW/kifSTvvWnHKnjpmF5xHVJxqPD//vLnApo1UMHPW3TAt5ow0iDwC0YkkWlfOIpvPPOn+JC9C3ou8K/pmjpcYCNVX8pRBY4yqB1CRZO1yuwUtsx4AvbcEvAgaZKEJz20ZI/YaGlOFIyhtQj1KVMfMO7cihMqNX+jIDbh1WDhdLOHON88QThbSBU7BRP8e7YwnYUg01iDKmKeIxv87IQzRDO5mD9T+rCJ4QcokSJWBu7XFi3oGz2+ndxmxq8O1YVAX9eY5zZMGFIMKqDJSYkyIvponk+kgRTJfIMWrxwYzv+bcu9EfySdBM9wJUWX3tZG3l81oN873v/oUmnQ1E6eBVpQoGs4QqccaMVB9dUGKS/GovgMJbqxEmyxBDdBwaCZ+xMeVZ9ez8bvsDs+l3Z2SotnFoTXFW5qDZ0hkmYR0y3dqQZ8rzkyclScTloGUgxGDlgwpawZGsCVM0ZSI9wZOYKr5/LOvcweRqwzD8GjWRCVqNuvsUdR1xQ14D8RiBfFWCV5YUbBWsbAyClpZiATdxsZiKxG08tIYC9sookiCQV0veClsLIQIAdlSwff53u//v3PM4juzszOzSTRP3u/yX84/t4KzN0tf/iOejnZwBk3zbDhr1d0wjXOwMnlOh/BXgxSiAC2L9pnRKc4EOo14wBoob9KDMqm0/pF4ntlY98QIoiYlUDkTYdDegYrnPQS8jakbjxOeEI2HuFizxXOYz+UmaaY9UXIGKOn3MvFE2PMvwr1F+8+//yFBs5KnN4X0phOa8DROAWvzSzx4ezdfdO72j4AiA61W1Lg8nE+g8WvNs+Ldq8vn2aAZ8OqZHl+kxDeg4PRa56481dP3KpQqniUvBRsoWxR9uRE0I9R14abYkWdHQCU18xb2/Es8lT1V23/9FZzQLHeaZsgrGrEMJ16Jk9kQDFo8ZVDt/bLCpBpORlnK3TfjWSS3o32GOSN+6k/kbLEY6Aj4M9LGaizqeTIUg3piGaDeENp4qh5RkajwbXTULMp38/T11+KJAW1PX2mEM7lLhHawLoMilbK1zvMs6fOfZs8ffv4A1cbuLxInW+pQN+elEe7ApNfsFWnRbTw8EwZ/5wHfuRmFp9X851aUuo+fATpxKPYN6BgU3bQug0o0oRh0oDVg2CmZJ0AbzwC6+f6fOzub1XuKZXWhs/rIaGhCLLoh9tRdfy0ySnwIzZTrlHXnvsJJuEfrqeSpXd2f9Dl5NDZn6VLMmToB02bR9gwjtrFN86g7px7y5dHAiScNFKLAS5yWs4L7fkacAXTjprnkxnXZ135EwAN0atDtU1undnaO3b6rZjizBXt4kz6JLbTwJD+i1k6l+BUIqLotAVPV6Gza063nd999B86c98Scn4MzPkX7ZS11eM3I1ky9tKypOp6Y5krc5DAHsX3mRKq3W2vfF4sMzEQrhWLQqvHdoPoWuFXMu0Gzxi9GBvUmcPNc7zzdgaoi7dw+BXpMN77NdAiD3Umox9gnGvhohu5IwRKKpUYUoNfandjT2dO1XSp7EurimescLwtmyXmTh9VQTn7SzsRkJnxhgpLJfIhaYyfWWlFLs/MEmpfDUX5GvT+au2eCHAaF5+HlvCKxrceTQDFoD3iBdwe69ezmzmfHdtWs2VNmZAeih0Rc0CFBi7AOmEfGMtA7w6CvBk7sCc9fvvmK1rN45nq77KkbOLGmZ5NP1LTnqmAe7WJOeQin6htIs5NMMTqUqsanXOVzd1j+MuK3pkugyVf2TDEegic9kxaT5i5ifZetNzD1gD8THShAn9t57tjzx3SX+C75ccanyiJay8eYPgIoPB8CZiATTIEeCZw93u+8YGTPv/4Je/6qzTWjFU2ucP+c5Gmcb8ITkl3fBsV1y0Tx6QpAUWww7FQSVY48xw5FOSdqtPNhKKD+oS3aIn4Zg1LgP9p4HIO21TmHytHH180Tg1bHtLWlgH9+qmN8wVN1yOfa4M+nGLdjUFcd3cBZ4/rkaaBZlJZEE5xnhfNL2ZNoh6f8KZqJ8y3saZy51lEoUz+J6E/r69q/CtK41AqSIdiObFZA/RYsEyiw2g8g5gZLL6vA88PiuawSL5ziiUHxJyLiz6lII56bOwA8V+aZJVwTTW0+CZICFtXnSJ8kkcwzgcLzlr2dJ9lTA03x9AEouPMP49RkHaua5hlhzsUoWjQyz5KRhkkzmxbRtGjZEG6daAGtfl/Uw6DzMU8rgZIrReqjw4dZnFP3ILkz9W4mB3w+dJ7Pbu88O7Un98YTmMLJJTDhRfM0TLlTJK1uUSdQz9i92rIn0f7PL2/fz3YQb68xTngmTmCmP72meVRMzbEknIcPmyhFtiENLJUyDU42M6IiCqn/pNAhgeLYVLrUk8hAE87VleBJCzoM8MSglUChHjxPb51Wx/SIAEKx3Ux05hOrWncuh2I+ZkMoQ+hI4CygHSfhrjR7AxNLaU/1Sg8K56eHbM90Z1/pACcCp5Yzbc7iWUA3AKrrV0SUwmCe0tDXkYKcI5s3yqJ+H3kDwzwsR8QjaDvWeWLay8wohT0JdfDrTmL1mLMBPewpEXie3tp6f2dT/EqUJbDOOPBPmVP43CHFjjrK0R1qn8Kefaq5YDp1esi5BE/s6WIEzitiA4NPs7A7TRM1nl0AzZVihbiCnMKEQ5nIBagdSt8Ue76iwU+gEIVNGTTftnj7wgSaFcqZs4lDb0yOM28EEg0DX7xLwHeeADVP2fNPDZF275cWDq6pj5c8w0lVihd6SXdPSrUayr7Ica1H9ntHPOmV4AnOtkYMTmh+/OHHlCPXo9wVUqL9RJrmjLKKuFbNQF3s4wbXxTl+rJgPnLsCRRTtS91cOc49K2q2LvGSRuxhzxjQ8mauCFLhc0luwnN7J8aXJRp68dTBp2IInweEkykms9LJKmE/oJJK8WSQxsZxyJ3brFv3Bk54upX/9NMrDnnJHZ6BE5rgbDxFUzy5cUdD0zJ6KVtRAp6Ib80Td1sVt6VF7VBCfsqzrMtW8TQoytzqzh+eJGncaJ5tswgGXc2A94rc4eSpBHqPeH7mITuPJeVPeVIAfRQgm7rj9DR46ZvwesApwRlH+ry1NujfZ55LZ23P++HZ7PlH4DRPAwVntvMTd3agMVzKxdyjuolmi/hB0t8TuQUqi8ITFdB0qAFmxLeXaWjzlBu96KevflWnV63Mk1nQo5E+WwLFn1vbO++w5YY720Qsxu+yJM5kAC9+B4VIYsrpckHL7hTEHTMsD1r68cXeY3NJhPvbnBWPPV9InBXt3KGpe/Es4cqvl08MXY5CcpgVFMvCzIhkFm2ARjGfZWcKlLyb8m+AJyeKZLnjP8AaVfRL5in5GkTMibzrRjxV4DfFsdTn665ZU4UXOdWla8TzYHDco1Po9FSDeybvzbRodp4ap2oiBHsGTvPs9gSn/Wl3RrAjDzMp8VbFek2HtNerKXFtRFd4OgA0HdqJZpm3bbP8m5sd6pk/vTef6agcypGUPPkXHFySGAPEjhxCRAZVw7ExTqDkT/F8UvrMm8H0TU6V1H/CkkP81lj0EMI8HdVHfUNXRI2zeCZOAXX3mdWII4/vP5QDTbvTPFHWIt3R2J8nhq+LKDwhqkenS0N11K8Eb96B6DlJ1K+nO/CaQWkNLoweyviTZ66jQDS+Bgw6kASiwMMzZ0VGPE9rln578xmAQlR3yTzzcNkDkIOnDkP18cn6xkGeayzYYc8C2oiSEva4GsHzl6/ehueVfbMn7mw4y5wnuEmuRYGVrAlRpVGmQUwzlDmz7b6B6GDMPHPj1A4McVIsg1ZfP4tVUpGyQe1n3qb0dJ5Ozqyh4tkoU4iquDHiKaDmuQ3PUcDrrngXPk6Z1uPlEegCKXMu6GRveB5sMK8N9RRqydE3N57Yk/Ojr8Se8ARn9Z5OnuDsGm+ilYYSFi3xN25EAWrEPMumMt3I82nEN6BagnLAa/tj8cz20/lDClPqOzz5L0SSIeB1rDIyTxckzTHB8z6587PCCU+hBOAB4YwDvH36+b4LZE/F/ppZUuDdKmWNR1j2xhsm9jx5smXPD3xuIvHeO/nc9jlC2u0Zu0KywuulcJZJo90Zml8AmhEPUAGqkXnQtWXtWnjqy+hk0Pj1LPDxLt8WPdUaXS9APayFJ0DpLczzIxm0eP5pnu+QOZHLvOs7h/MvKLAJX+nAfp0qv093ypF7qexB6T8RXFP6wWVlTw7plT2va9nTNGOP4puvvdllnuMpuwGcVrOmH2rJWEiHLLgeKjm1DrHjAaAO8HIocqs5Z40Tg8YfjT+VOHEu5+SwczF4YnfzjJCv+h47b+BJ+znmeWp7E5JG2XpQjY/28WE55M8oRHopmDrmGJ5rPnyFTWCIAZJH7l1Hzi4FT+zJB9WL58SeueUTnNgTGaclnqsTnqZYYrMizJwAl6edE09IiX3yPfeF+7UJB08iXjwBmoX9Qt6+EMg26CoyxxV4Ri7FmxxLzxCp+bNG8Ke2twHZ1MebC3GWfHxUt3gegOeCC7sWj73Q4cu4UA3jkRAvmaeG7px87HA3zipGibPzfLPxHBzuQ20AKy32J3np+5y9od7KFF5ahE84dMwzMyM4JfOEnA1KifeVi7w5N08bdBnnk1Ykuz9313qWHuHPVpBOw7M4Is/XwVFEFeJUJj3X04tilJQXa8Z0XW4KRd4QmmSPiKbDXThlzysO5Sn5yROcxLsFUeK8/JndkpJnSfimdhUyxNt98BSd94CiKPlK5FnjWevD8HRZ53dHic/LlXkf0xPw+Ye6JIX9wZo7wo864OEpOeAFFJ7GCMjUbzPaI7kSHaBvWlD025kNJhci1bHLeRhzoj1S9pROKntiTzZ3lzvBaZZV4BOn/WmYfgxsju+SeeI+2TX/zmKDh8E6l/t0zZeAEsezCdBEdzWm5PeabV7/PY/eITEClJklMinKlkI8CXjz7AnU/ny2OD7/m+7SjM85IW3uF0e58yJ90smedp6vjVmHr/oahJDAwvXRapYksmfxNM7MnsgF3jLOjPfxpXGmGR4tEZ3ImA00K1cm2AKqMl/7aEEXTtTPie4W8OB0KxY8oQo/jWJVoQwUvgyQzBPhTwc8/oQnEMGYKp7+QA59fMyC0ucD8UFQohk762HJRTIlUW2X1tyriWR42p5XMfE5tic0C+ek/Yy4z+YTngnUydLyG9BwlYcaRGuIaJ6LrvI+frUG8yhiO/459FMbVEDzX4a+SKKgZ2VfplELoHqjjqivBNp4biXPruIZH33ujz/Zs2+/zlRY88KcrAnNuDimgdTNQPEnPB8+i8qek3DHnNK7o26pOvn8RribJVQNNEwKIlcN1DzIe2HRVjjgadokUZ88UPPLAhq/oRl00SXJG/agmzzz0c087b0zKA098S6g5yZQNaD37MLzgCSkyqD6CL39l51/QXwuoc3pS19FTyJvOoH6UGafyvziknn+fTLtCU7xNE7zLJowjKUOFPMhPIqnWU4C3kQtCjEM2st5A0pVEpfWUwVP3Ffr7fHaJR6Dmidv4U/6A5StJ74nJbsyQTMrUvCURjy34Hnq/3gu7L/owH6dub/nmhs1Jx/uBCBp0uyUTK26uuPIrb35hGfZk2hv7mz2PHE8Bu2wtI4ie5TILZE/jbOQJhO/wJUDDAbc6dN8xzyJfl+DgEE70DTovMW7kiRAo+P00SKRQ1YAmvUIczaeY3/+Cc/t31yLuE14KtzFU7VdJ3VeruO7saccGC4UPtA9YR1Bfa34oZuXHO4nI9zB2XmC0zLQ4yeOj1CWVg10RDQ9OZFjP+VaksaqHuDlDHhwwjYSaCvx4WA39a1/sMVZz4Tnog26oj+Z6xEpeplAWX7ZSJxTntMVY/dL1gJTIIJ64PIHInuOrtcSutyOnJ+XwgvG8dKX5vn3V1/9Tbi7l097Npa6H5cU7COaP6WSaJVrx7mZdr3st/4LdGWgr+87G2XADpR0KoPi125QCZ6ZQWMiWdxclaCo5xg2Szwrxq5H5E9gpsxzC57nLHA+PyPW4RkwYzHpIXY1ENUyZR4ektNKngfVCxSvblhyM++hpop759nceVxAxVP3b3/s9iycKDYvrbZ5kWIJn7InUEZubXPO4LGcQmfdopLqu1+4opGWzXMeVs1pVJGFKkDpPvP0GxJoLhnbnxOeknjWUpx4Rnc/U7sEUWFlyo61+FjSVIgbZp4AWFPyKT+7YCmmlm4je8ZUSF1aGDBxqFgiYRRRx7xhbjSggumRuYmaaavnL3OfIqVoZ31mgnkyjIInbaiecSv+CRSeuLldLsbKics+5iTIh2yZoApOH2Q54nmm82zLRl3wDOXqhc9XIlECE1/6w6MSOtj3dOn9i5ci3G8Le8LT9qT1DJzimDx/FMyuoIkg2gVQKZBmuTZKi/fasYAC1hxqnmLkMJ5VDk2Tc7NB5+YJPKp78gSlpEcfK0LY8z5sfaZdzNCfw1MN6H1NtR5nkkKJrgmeefWWYRplJgQJpAX1EngS7sqehw7d/V3Zs/PkC1vCc0qTFL8xAbrsytSIsiHc6bOIgtSAwmKJBzGYjMJuoJYJl0E9429LR0RT1rE6RHPQvjJkhY9w98Fru/J87smmznUWtgzlrkWqDikTmCyFGGXKQMugwlk83Xw2e8LzlePWjz/annw5fbo57jhJ+rlL0YvGBjoRWUB3A55FsS650LhpQiOciAQB0ZyjMk/gAdQ8MWiuqK4kaVaPiucZ09Sdfh6ez7xhjajOcvunyws4qdxJE2d2lvH5monzYNPe4HnyZA/36j1R4jTFSbQbZ9lzHTmViigyUUxa9uTOBR9g04sJUPu0AZ3ZyS0DFE8wQtQ8pWg5c5uNWySs2U/EgGj500S37c/3OHStM33DPNdcs9vlRd79kd6EJiStRpMV5XT02l7Ku7rPGmumPe1PC3PizLKnNPUnROVQJ1GpE41PoJi400RRLoIq0SVSRvI57hz1qxy6HnZ2ZxVhTlrJK8JpmQCKMCxA+QmiWQqDFk/8uW1/fs9JgGZa/iTEfQEcNFkl4kM6wNm8Ccr0JtaUof2xUmjvJX+5m1fzCU9f6F48M9bhWekzDZr+BKH2gaEYjPC6iGK3yKJlUGgGql7il1tG5GEeQGfwNHde8xLckTZ9mEYEfudpoCuaPjZj8+T/B5jru/nz9adhiQwTnHf9C9LvvbC58TByAAAAAElFTkSuQmCC\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Humidity\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "environment", + "humidity", + "indoor", + "moisture", + "dampness", + "wetness", + "humidness", + "moistness", + "dew", + "water vapor", + "condensation", + "dew point", + "steaminess" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_humidity_progress_bar.json b/application/src/main/data/json/system/widget_types/indoor_humidity_progress_bar.json new file mode 100644 index 0000000000..0d45b09387 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_humidity_progress_bar.json @@ -0,0 +1,39 @@ +{ + "fqn": "indoor_humidity_progress_bar", + "name": "Indoor humidity progress bar", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAn1BMVEXg4ODf39/g4OAAAADg4ODf39/////g4OA/pxr29vYhISHx8fHn9OPHx8fj4+OQkJB0dHQ9PT2srKy6urpYWFhXsjfv7+9XsjaHyHC33qmGhoaBgYFvvVKenp6Xl5cvLy/z+vHP6caf04xit0TV1dWpqanc7tbLy8vD5LhmZmZLrCiTzn7CwsJ7w2E8PDzc3Nzm5ubU1NSxsbGr2ZtKSkqpy9RjAAAABnRSTlPvIL8Ar7DvmsykAAAD+0lEQVR42uzPwREAQAQDwDiGh7/+S702wmQ7WLg91HZhDsPkeg1DdB4wD5Un4EqkFCGjCBtF2CjCRhE2irBRhI0in9061nEQBqIo2r3iWTNjGzrLckPjhir//22bwK5YFBJTAZFykWyQqyOm8NV6A+miW7btzDDnKVAvOKE2JFBxr6fiRY50CyQzAZ0p9nUlCIYBCwTi5pddnQMRG4DBBGoiKQ2YFsAyAE0pesp0bjf6OFgHIJtDo+MhxghEGoSBhayPRQAGQAsfj0znoTBUxwIoRzQ6DlL9vbCGjIo0L36G9DToOEN+R6ufvhIaHQcJj7iGeEBYAWU/Q1gA+BUk06Yf2ei00VogfoHc9yeIY3C84X1XgziGJwh6ZkY0OgOSmYG8CUGgA+oa0rE0J+sUiDDkHLYhxhCNC6R2gCvs0egUCIwsdRuCRI7pDzKME8EfOlkLpJ1T9+YM/1I3QRR7u/DtV4UVrT4B0rMoWn0CRKJDs0+A7OgL+UJ+2J9jFABhIACC3XHFtaKCRfD/j5RArK/dwM4PZhdGaIzQGKExQmOExgiNERojNEZojNAYoTFCY4TGCI0RGiM0RmiM0PSRqopp1DKCqI3cmUdMby5PEHWROv/ItXfkY78OehwEgTAM3yaf2bkaIeEgkBCD8dT//98WW2jXw1Ib23Sa8urN0xNhUIUCMYj63EQSuwOZgJghXuir2AcZoIYMiVhIcHWIQ88FAswkdWTdgzCgKUMY8BGAYrokjFSFDFBUIBa5/iJR3gQSVA3iAL5C2MSZKfg8fq0huxDL2f8VCAOOCqQ0RmBcIYqCISVnFFcgCyKnEoR5c7CEFdRP86wViel/yIQ/bdabpRS7ExsmMe2GzErR2pIhKaWtE7O2anskVZbWDTAAY37uuddek4wypNJt/PaWWAGqPGDtaJKyTXZDyG3PETo50vN6y2g/hPQAICqma+OwDFL2e4bsK9gw0iY5315f9M/+ITWItBpEWg0irQaRVoNIq0Gk9QUQNjiUD90rehzicTDTvaSHIThcl/pJ91OvxyEGB/NnRvf2N8IehzKhSz0d88VT68NqEGk1yG97d6ziMAwDYNhp7/5ibsiBQYNCtYUshjR5/4e7a2nXQrpYTfWDd3140ChvBcRbTyC1t0Ljch0BMTO5D7QdIirYTNOyrgqMlcmYNcsg2yHLGUrzLxEFVMgDdYVx2g6ZRjeQ39srJSBeICowUF+EyJA5L7TsAbGZxZgN9LIdQtHeaNoDclG7Cta+L7wAIWe8JPeBPnuzv1cB8VZAvBUQbwXEWwHxVkC8FRBvBcRbAfFWQLy1I0hiF53SYQenp+HnO3VpB8en5Z9x7L5O7146dMc/B6Inq4uieH4AAAAASUVORK5CYII=", + "description": "Displays indoor humidity reading as a horizontal progress bar. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'humidity', label: 'humidity', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":30,\"color\":\"#FFA600\"},{\"from\":30,\"to\":60,\"color\":\"#3FA71A\"},{\"from\":60,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":100,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":30,\"color\":\"#FFA600\"},{\"from\":30,\"to\":60,\"color\":\"#3FA71A\"},{\"from\":60,\"to\":null,\"color\":\"#D81838\"}]},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Humidity\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "progress", + "environment", + "humidity", + "indoor", + "moisture", + "dampness", + "wetness", + "humidness", + "moistness", + "dew", + "water vapor", + "condensation", + "dew point", + "steaminess" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_humidity_progress_bar_with_background.json b/application/src/main/data/json/system/widget_types/indoor_humidity_progress_bar_with_background.json new file mode 100644 index 0000000000..3fe3f303b5 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_humidity_progress_bar_with_background.json @@ -0,0 +1,39 @@ +{ + "fqn": "indoor_humidity_progress_bar_with_background", + "name": "Indoor humidity progress bar with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays indoor humidity reading as a horizontal progress bar with background. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'humidity', label: 'humidity', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":30,\"color\":\"#F89E0D\"},{\"from\":30,\"to\":60,\"color\":\"#3B911C\"},{\"from\":60,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":100,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":30,\"color\":\"#F89E0D\"},{\"from\":30,\"to\":60,\"color\":\"#3B911C\"},{\"from\":60,\"to\":null,\"color\":\"#DE2343\"}]},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Humidity\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "progress", + "environment", + "humidity", + "indoor", + "moisture", + "dampness", + "wetness", + "humidness", + "moistness", + "dew", + "water vapor", + "condensation", + "dew point", + "steaminess" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_illuminance_card.json b/application/src/main/data/json/system/widget_types/indoor_illuminance_card.json new file mode 100644 index 0000000000..40109e0624 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_illuminance_card.json @@ -0,0 +1,37 @@ +{ + "fqn": "indoor_illuminance_card", + "name": "Indoor illuminance card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest indoor illuminance telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'illuminance', label: 'Illuminance', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Illuminance\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 400 - 200;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\\n\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:lightbulb-on\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":100,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":100,\"to\":300,\"color\":\"#FFA600\"},{\"from\":300,\"to\":500,\"color\":\"#F36900\"},{\"from\":500,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":100,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":100,\"to\":300,\"color\":\"#FFA600\"},{\"from\":300,\"to\":500,\"color\":\"#F36900\"},{\"from\":500,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Illuminance card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"lx\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "illuminance", + "indoor", + "brightness", + "luminance", + "luminosity", + "light", + "light level", + "light intensity", + "lux", + "candela", + "foot-candle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_illuminance_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_illuminance_card_with_background.json new file mode 100644 index 0000000000..a726b28cc3 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_illuminance_card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "indoor_illuminance_card_with_background", + "name": "Indoor illuminance card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest indoor illuminance telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'illuminance', label: 'Illuminance', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Illuminance\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 400 - 200;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\\n\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:lightbulb-on\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":100,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":100,\"to\":300,\"color\":\"#F89E0D\"},{\"from\":300,\"to\":500,\"color\":\"#F77410\"},{\"from\":500,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":100,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":100,\"to\":300,\"color\":\"#F89E0D\"},{\"from\":300,\"to\":500,\"color\":\"#F77410\"},{\"from\":500,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Illuminance card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"lx\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "illuminance", + "indoor", + "brightness", + "luminance", + "luminosity", + "light", + "light level", + "light intensity", + "lux", + "candela", + "foot-candle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_illuminance_chart_card.json b/application/src/main/data/json/system/widget_types/indoor_illuminance_chart_card.json new file mode 100644 index 0000000000..8ab8c3ab32 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_illuminance_chart_card.json @@ -0,0 +1,40 @@ +{ + "fqn": "indoor_illuminance_chart_card", + "name": "Indoor illuminance chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a indoor illuminance data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'illuminance', label: 'Illuminance', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'lx', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'illuminance', 'lx', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Illuminance\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 400 - 200;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"lx\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":100,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":100,\"to\":300,\"color\":\"#FFA600\"},{\"from\":300,\"to\":500,\"color\":\"#F36900\"},{\"from\":500,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 400 - 200;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"lx\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 12 - 6;\\nif (value < -20) {\\n\\tvalue = -20;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"lx\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Illuminance\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:lightbulb-on\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "environment", + "illuminance", + "indoor", + "brightness", + "luminance", + "luminosity", + "light", + "light level", + "light intensity", + "lux", + "candela", + "foot-candle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_illuminance_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_illuminance_chart_card_with_background.json new file mode 100644 index 0000000000..661bece439 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_illuminance_chart_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "indoor_illuminance_chart_card_with_background", + "name": "Indoor illuminance chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a indoor illuminance data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'illuminance', label: 'Illuminance', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'lx', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'illuminance', 'lx', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Illuminance\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 400 - 200;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"lx\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":100,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":100,\"to\":300,\"color\":\"#F89E0D\"},{\"from\":300,\"to\":500,\"color\":\"#F77410\"},{\"from\":500,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 400 - 200;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"lx\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 12 - 6;\\nif (value < -20) {\\n\\tvalue = -20;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"lx\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Illuminance\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:lightbulb-on\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "environment", + "illuminance", + "indoor", + "brightness", + "luminance", + "luminosity", + "light", + "light level", + "light intensity", + "lux", + "candela", + "foot-candle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_illuminance_progress_bar.json b/application/src/main/data/json/system/widget_types/indoor_illuminance_progress_bar.json new file mode 100644 index 0000000000..68f9c14642 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_illuminance_progress_bar.json @@ -0,0 +1,38 @@ +{ + "fqn": "indoor_illuminance_progress_bar", + "name": "Indoor illuminance progress bar", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAqFBMVEXg4ODf39/g4ODg4OAAAAD////g4ODzaQD19fUhISHk5OT+7N/0fCBYWFirq6uQkJA9PT3x8fHv7++6urp0dHQvLy/Hx8eGhob82r/+9u+Xl5f7x6Cenp75tICBgYH3oWD0chD2jj/6xqD39/f948/Ly8vV1dX6vZCOjo74qm/70LBKSkr1hC/CwsL3oV/c3NyxsbF1dXX3l0/2j0CgoKD149ZmZmb94s+rdVHIAAAABXRSTlPvIL+vAC9A4IoAAARhSURBVHja7NjdauMwEIZhZ9uvYeRY0tSSSUGbpcuSE5/3/i9tR45NvJTWCz3INMwLFvpJDh4Sm5DmcfejefruPewem13zjG8fC+OBcQc9y9cKd1FzL5AngyjLINoyiLYMoi2DaMsg2jKItgyirU8gvWc46oCOHD4qFejoE0hHwybkTCN09EUIBi1/XfwvpA+CGcK5Xn06OUwDOHggFA7JQ3IlhYouhctlh0t9neRCKg7XbgVJskAgL1fMmXKqg7ucU46R6AT0OY9EPRBzzEQJcJHkqNQjkulKogDyxrKKjBOVGSLbLmdgJIeh7sR6lAlI0yQzR0H4il1SAAmAn4cFAiASJHaO4ryoLBIefOKeEoAcsUoxxHUkLRB5Wz2eCnQJ1zRDIg2MNYSXp3OhN1/DtZtBeuC0AakGXkMQM4u/c9OdA8aqG0EKRR/yBiRT8OM/kEKjDxTrIvkSFdzsSER56xM5R6IUia8QCJ46B3Aimdzg8fs+dtjO8YdbLJP32a9fg2jPINoyiLYMoi2DaMsg2jKItgyiLYNoyyDaMoi2DKItg2jLINoyiLYMoi2DaMsg2jKItgyiLYNoaxvSti1qL+3cy7x/PP6BnjYhv/f7A2o/93OvE+O1Tg9HaGkL0h4WyK81RLYvqZFsQf6yXwerEcJQFIZXhyPc+wCGIomOIIrMQil9/zerziSDLWVEiyTD+C9d+WFyL14ZICNddau7P24NGlKRSCuQjnT+ZXuWCBkvuKbzSVYgSqse4jgglJOFdzZIo+eQgioBQrYII6shPzAlpEUaPYUIWcFDhOwdSSsz8BekiT+In0KUFgGS06eygPhhrB+I1zqkIOUBkdG1grony1eDCFkgQELGkeYvSFcDdYUIrUIGOplSqggeWbJeQgYPaZyBtojQGqTjoh/nLX9MrS/SegjGtlCDDUWCtNZibpghOdmGPRIg4iItx/U7MhWOlgdMKWlg3GKzewjUGURoBRJajl/NIZa0d0EhpiEVAVLo2GJDsSAoFnsERnnLSYAIK9l0tqJBUClJZwVzcuVUWSNA1AIRb/sSsl6d1wYhk+cG6fRG/+wv0glJrROSWicktU5Iap2Q1DohqfUGECm5t/4zO6ztkJ77K7PDumyG8D9lx7UZMnJ/fXZQe76I9Nxbedwdubzx1HqxTkhqnZDv9u5YxUEgCMCwJvmDWAg3WyxMYSPKNqIB3//ZzgSEqw72rtiJmR+22G4+WNhyrOUQa/0CmZMGijXMHSCqKsco+2Vr8iESBV0oVBPXCHQzo7LEQXpBJ9Y1HzJtEALFkghEoemZV+hGHiApHzJ2BiCP1wnhCZEIEh1SrONp0TO/IPTQpnyI9A3bRJkOiC5MyqIQ2+dl7PIhhJiUQh2QNuouYE0pQKua5A8QhoHyyc9R5LN/9vfKIdZyiLUcYi2HWMsh1nKItRxiLYdYyyHWcoi1TgSpOEX36nKC1dPwda3qysoGp38kO+NWX+/vXnWpb99gbrYRFl0QWwAAAABJRU5ErkJggg==", + "description": "Displays indoor illuminance reading as a horizontal progress bar. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'illuminance', label: 'Illuminance', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Illuminance\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 400 - 200;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\\n\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":100,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":100,\"to\":300,\"color\":\"#FFA600\"},{\"from\":300,\"to\":500,\"color\":\"#F36900\"},{\"from\":500,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":1000,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":100,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":100,\"to\":300,\"color\":\"#FFA600\"},{\"from\":300,\"to\":500,\"color\":\"#F36900\"},{\"from\":500,\"to\":null,\"color\":\"#D81838\"}]},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Illuminance\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"lx\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:lightbulb-on\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "progress", + "environment", + "illuminance", + "indoor", + "brightness", + "luminance", + "luminosity", + "light", + "light level", + "light intensity", + "lux", + "candela", + "foot-candle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_illuminance_progress_bar_with_background.json b/application/src/main/data/json/system/widget_types/indoor_illuminance_progress_bar_with_background.json new file mode 100644 index 0000000000..1e60697ce3 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_illuminance_progress_bar_with_background.json @@ -0,0 +1,38 @@ +{ + "fqn": "indoor_illuminance_progress_bar_with_background", + "name": "Indoor illuminance progress bar with background", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC/VBMVEW+trYAAAC3ur3/59f859bEwcXDq6D55tjGwMXDqp////7//vz//fr//Pj++/r8+/rJysz/+vb++vj8+ff3dBD8+vjJycn69/b++fT18vHz8fH39fT79vP8+PX28/Lw8PDn5OL99/EdIyv8+PP59vXy8O/08e/49PEVFhj59fIgIB/y7+y6ur69vcD6+fjm4uDv7u3+7t7Mysm4trrKy821tLj89Ou8vL/879+4ubz79vD59O724tDYz8fu6ub28u8bISnf29rw7OoZGRvt6+rBu77Pzs7NzMv+9u3KwMDy7en47N7T0NDh3dvW09HGxMgfJS3Qzcrp5eTi4N7+9er969zk4d7g3d3MwsHr6Obc2Nf28e339/j17ur24czf2tbc19TZ1dT67uD75tL27OHKv728uLuysbb86tf86NXKwsX36dq/v8PSzs3Gvr/76trb2trf3+G+ubzV1NX549Di3Njs7e768+jZ1NLt0r/Tz8rNxMa6trr67uPDvb/r5eHPw8H88Ob97dr54Mnc0cf+8+bm4Nzj39z46Nbq5+RWVVXCwsX48evmwqn+8eHY19jd1c75+fnk4eDp4t7T0tPZ083l3tnLxsnW0czFvLzi1czOys3rz7rRs6Tv6eL66uDw4NU8Ozr+8uj05Nrov6D37+fQ0NLMr6CNionTycju1MFxb28wLSz25dP0177g2NDe08nmwaTGrKBJT1bz6+X2597PxsftzLNQR0P848zs5NzHwsTpyLH4hi7x59zk3NSdl5Tp4drYzMrlxK7rx6qVk5P0fiXq39Ts29Pn1tCnpaMaHSLw5NdFS0/iu5323MXAubzIsaWBa1/ez8vMtKaUjo37yKHy07jBsqr3pGaUhYB7dnXn3ND5tH6zoJiFgX71k0n92r+inJuBfHjUuamIhocyO0TRwre7tbI1MzLcvqutqql1YFZHPTnR09jr3M1fUUuXdmNiYGGupqWWlpkoJCLBmXwpKi3Pqo7l08aoh3b7wJPctJJcanj80bAiphZ2AAAACnRSTlMgALcgv7+/sK+vucF9fAAALvJJREFUeNqU1T9oE2EYx/EoCI5OQh20CK3SXDAYJWTSNklLpBBpwFwUB4ecEOpRU+iScNyqDoVQKMGpi1jqJgQh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", + "description": "Displays indoor illuminance reading as a horizontal progress bar with background. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'illuminance', label: 'Illuminance', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Illuminance\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 400 - 200;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\\n\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":100,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":100,\"to\":300,\"color\":\"#F89E0D\"},{\"from\":300,\"to\":500,\"color\":\"#F77410\"},{\"from\":500,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":1000,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":100,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":100,\"to\":300,\"color\":\"#F89E0D\"},{\"from\":300,\"to\":500,\"color\":\"#F77410\"},{\"from\":500,\"to\":null,\"color\":\"#DE2343\"}]},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Illuminance\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"lx\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:lightbulb-on\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "progress", + "environment", + "illuminance", + "indoor", + "brightness", + "luminance", + "luminosity", + "light", + "light level", + "light intensity", + "lux", + "candela", + "foot-candle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_pm10_card.json b/application/src/main/data/json/system/widget_types/indoor_pm10_card.json new file mode 100644 index 0000000000..3ecdad80bc --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_pm10_card.json @@ -0,0 +1,37 @@ +{ + "fqn": "indoor_pm10_card", + "name": "Indoor PM10 card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest indoor fine and coarse particulate matter (PM10) telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pm10', label: 'PM10', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM10\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:broom\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#80C32C\"},{\"from\":50,\"to\":150,\"color\":\"#FFA600\"},{\"from\":150,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":32,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#80C32C\"},{\"from\":50,\"to\":150,\"color\":\"#FFA600\"},{\"from\":150,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Indoor PM10 card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "pm10", + "coarse particulates", + "coarse particles", + "particulate matter 10", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_pm10_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_pm10_card_with_background.json new file mode 100644 index 0000000000..a4a98bdbde --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_pm10_card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "indoor_pm10_card_with_background", + "name": "Indoor PM10 card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest indoor fine and coarse particulate matter (PM10) telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pm10', label: 'PM10', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM10\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:broom\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#7CC322\"},{\"from\":50,\"to\":150,\"color\":\"#F89E0D\"},{\"from\":150,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":32,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#7CC322\"},{\"from\":50,\"to\":150,\"color\":\"#F89E0D\"},{\"from\":150,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Indoor PM10 card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "pm10", + "coarse particulates", + "coarse particles", + "particulate matter 10", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_pm10_chart_card.json b/application/src/main/data/json/system/widget_types/indoor_pm10_chart_card.json new file mode 100644 index 0000000000..c8a8e9f109 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_pm10_chart_card.json @@ -0,0 +1,40 @@ +{ + "fqn": "indoor_pm10_chart_card", + "name": "Indoor PM10 chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a indoor fine and coarse particulate matter (PM10) data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pm10', label: 'PM10', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'µg/m³', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'pm10', 'µg/m³', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM10\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#80C32C\"},{\"from\":50,\"to\":150,\"color\":\"#FFA600\"},{\"from\":150,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nif (value < -10) {\\n\\tvalue = -10;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"PM10\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:broom\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "pm10", + "coarse particulates", + "coarse particles", + "particulate matter 10", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_pm10_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_pm10_chart_card_with_background.json new file mode 100644 index 0000000000..fa74d5bb1f --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_pm10_chart_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "indoor_pm10_chart_card_with_background", + "name": "Indoor PM10 chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a indoor fine and coarse particulate matter (PM10) data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pm10', label: 'PM10', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'µg/m³', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'pm10', 'µg/m³', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM10\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#7CC322\"},{\"from\":50,\"to\":150,\"color\":\"#F89E0D\"},{\"from\":150,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nif (value < -10) {\\n\\tvalue = -10;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"PM10\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:broom\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "pm10", + "coarse particulates", + "coarse particles", + "particulate matter 10", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_pm2_5_card.json b/application/src/main/data/json/system/widget_types/indoor_pm2_5_card.json new file mode 100644 index 0000000000..946639a5c4 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_pm2_5_card.json @@ -0,0 +1,38 @@ +{ + "fqn": "indoor_pm2_5_card", + "name": "Indoor PM2.5 card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest indoor fine particulate matter (PM2.5) telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pm2.5', label: 'PM2.5', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM2.5\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 150) {\\n\\tvalue = 150;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:broom\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":35,\"color\":\"#80C32C\"},{\"from\":35,\"to\":75,\"color\":\"#FFA600\"},{\"from\":75,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":32,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":35,\"color\":\"#80C32C\"},{\"from\":35,\"to\":75,\"color\":\"#FFA600\"},{\"from\":75,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Indoor PM2.5 card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "pm2.5", + "particulate", + "matter", + "air", + "fine particulates", + "fine particles", + "particulate matter 2.5", + "airborne particles", + "microscopic particles", + "respirable particles", + "aerosol particles", + "dust particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_pm2_5_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_pm2_5_card_with_background.json new file mode 100644 index 0000000000..5cc3267d22 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_pm2_5_card_with_background.json @@ -0,0 +1,38 @@ +{ + "fqn": "indoor_pm2_5_card_with_background", + "name": "Indoor PM2.5 card with background", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC91BMVEX5+fn39/cAAADi4uL6+vrn5+ff39/n5+f7+/v4+Pjo6Oji4uLb29vi4uL8/Pz7+vr49/j39vb6+fnT0tJ8wyLS0dLV1NTW1dXX1tb+/v718/Tk4+PR0NHh4ODT09PY19fj4uPb2trz8/Py8fHa2djc29zn5ubx8PDd29vZ2Njp6Ojo5+fr6+v19PXk5OTt7Ozv7u7e3d3w7+/q6urh4eHf39/t7e3U09Tf3d7m5eTq6enPz8/l5eXn5ufz8fLa2tri4eLd3d3c2dnX1db5+Pj29fXc2tre3NvQz9DOzs7q6ejZ19js6unh397f3t/g3t3k4uLMzMzt6+vj4eDOzMza19fZ2dnKysrp5+jn5eXe3t7Z1tZXU1SUyVDJx8euq6xcWFnX19eHxTijn6CIxzm0sbLm5OS7ubp1cXJybm+gzGdMSEnSzs7HxcbAvr+yr7CIhYaSyE7m4+NPTU7Mycq6t7i3s7Swra7v7u3v7e2PjI1qZWZlYmJhXV4cHByUkZGKh4hTT1DV0dLGw8RjX2BZVVbDwcKsqaqoqKiCxS3Dv8Crp6izzZGu0IBvbGzQ0NDIz7u9u7u4tra1tLSAfH1tampeW1vJxcXQ18OcmJmFgYJ9envj4N/X09OppaagnZ6Wk5Tg3d3Tz8/QzM2npKWYl5eZlJWDfn94c3SOyEWempt8eHkfHx/GwsK/u7umoqN6d3eqzXyoy3pGQ0TT2sadnJySj5CdymU3Nja/vL1rZ2hnZGWKyDusqqq9zqajoaKPiorN1cHJ0b2vr66cm5uMioux1oF5dXbLx8e/uLWqoqCfl5WizmlCP0A/OzzDvbvB0qm4sK6Ig4PF1K+5zpyZkY+SjY2524zm79nU2M7W5cGyq6mjy3CWy1IqKiq61JeYyVro7OG8tbOuzYao1G+a0Fbe5tHb48/P08nQ4rjA3JylnZv0+O7k6N7X3srM5avG3qez0ouLhoanz3Sf0GCMyj3c7cfJ1bd+eXns9eDa3tS/1KK60J3x3LhVAAAADnRSTlPvIADvryAg77+/v7+wr3LpCOwAADTTSURBVHjapJQxa9tQEMedkqFd6nsa1U0PrND0UeGhg4cMJsRQbBqUCmyytxQ8CTKYbl67lYIXuWAZmlKwP0NGSxqEAsqizas3Txl7d3oJxMSL/bd1eu8JvbvfuztVXhy8rBhVo1o1AACtQRfQRIAgAShbStMylRCmJU16YBgClCWddu+oP5tn81l4NjhRQC/jVvynsd5OC0hocc3gi60W7KXDgxeVgwqgY45AoAUgDBIQh7IsjF9aKNPEme1/EEq9p0em3egFzf6iiJMkTvv+wAKODHTwaMuJXoUHob8Nwd5CjEPtkQxaMAiEeBjEkgQhMRt0s0z7vNt1nZqS0q53j/qLeZRlWb5eReNO90QASSkG4eMBeEABFBkmfArxhEPAbnpVYa/61LRBSyUlCIQ5bMvEBGDw0nbf+b1gUGu43VorTNMfcZwV63UxavkIgjKEY+mTKTMr9Ka6rDZBYENiR5oKgYDBBoBsKaGEUKaQKCwqWdaXsk9s2xm0j1wv8INJc5oufyerOMlvijBwB1IpwpeKClVQcrleaQacFEOLT24TYt+0VLikuJboLgB0f2gQanNlStNEHMd1HOm2uxPf649bw2m0LFZ/4mS9WmVD3yFiRIEyUCF0RwPNGEnnA8d7tcZ2EIPyAPQT/AMuKw1iSfpWUSJsJ5ic19z2eacTjKPlaLmM4/wmSZLr/O5fOnERlrqJSehUmecxdAJ52uBbOITYFYQ7nH0TRilkUgL7u16XNqZEuh9rvUbNu7wcT7zZ1POmWZEVUXzz99v9XXJ//StPJw1krju9xsASglMKZa9zXREIWRbA8xBiT5CNjzkyWJQVpejTZHOTHDd6k1EYjqIoSuNimeI9y/M4v72+/36bY1KixbgZoDotb3hhKz4Q0E3OGDRGpu31JHYF4/ohkLKKBcWOnl6j0CuLD5RzZNcH3eZZ67LfD8NZGIafp+Hi69X8ap7Ofs5nY2/4Kbh44/u92lv3+ItrApzCqYGbbRdujxcZGvOQxTNa3FjWFz/jl3j6yMsgIKpQFu3zHo3/lJfPjpswEIfzBM3SU1jkQ/6IpcuBJZWqJIccSGIkFgsEioTVOw/AKXmXPGx/nrhxsdtKGSewa1gvH9/MhBxfTq/LC7KrH4IawUSVR3y+mvNctEHYFWVyvSZJk1zXy8XJezkiLAzf979g0AuhfqatOUEPPedM67dvX50RAyNAITA61QzNARDvtHj7UXYSGG3bgiONeLZaZQ+QBhhlWTaXt/0rSF7GPmgxgjCL07T9z8ykO01vOjg6rnsTNgChX5BgOM3xoYXs1xAS1C27CSE2ueJAEIiMi1LFrtj9QWIZsVe2774/VqLNqf3okLvM9FFFE++rroWp+UuHA0LioWZMiKrSHIgsr1ggww4IZVF0XfG9+SASG8Q3SzvJQsMyoiFs9r+wTY0RaoXqo8Q1QgXvKY5r2W1rpijSPOd8nmUEElW3ug9BUrx3cRjG77u7k/8Z8S0jlhBDga0zXCOKQr1gxAOKkuIa0fWxX6NA+poRRaR0ZAhwzKMNcqvfyhAhJfaxIlmcHjUyRbjl7Ns313VHOzdcI6r50nuiyoP0mFs4rvPlOkF9tKIiCu0i4xwwUS5YXQfB0EvZb4dgQME06/1JC/ndP50rsUvZGqMpe7hGPIrJ45l9aq1P9QGOj6aQ0IHK4ErE+fx5RlIhONrWRjCGTlYHA7XmQHblBd+9jtT3TRCMuUoLwA4994QRAkEAxDZCz5Ko8zsHOhW5AMXP2exzxSOVZUpJWlVCwYCixaaPCQSpZce/jfjPKHGN6Jg8nt6MEcKgxELfTRRHGhGHojgcDrNvWZRuFEjGozxPN5W4sVapCWRR4jEYZae/6eoYZ5hvCXlCiWPEgEAH9Ohi10HXcE+sX6yYzYsScRzG/wVnk9VGJHTEHbNpZqcQ6+DBUktXFG3BsCQolNoKaTooLVNR9AZ1CIpoL106RHMIqsWoiGjr1J46BEFCh+pU0H/Q8/06w7SSpLHPvo0s7M7H5/d8X6YzFw8MOEKR+YJ/l296etpfSOeSyVk5PUgL2QIUsJRPISOa096ndkMeDy5B4hoz7MjGiSwZ7QgnXWBHhjkEboQ7qiXEgxISqTOIb1cBjszmsiCLzYMFKHzA2BCaU6iVsAQWWFyUoYyMrE0TO0IjO3cVBnE4gAGOMDWQQBD5wL3i+2s/SHw+fyGEjMgE5q/XEX14QiDlZm1ORUtMhUUxk8Efz2ToIsMkLOAMIYx83yfOiMAaArE5kJCd0UCR6xPCfezI6/omPzISoZd7Ll07eXqXv77v0l5Ur9KGhi8QzRdbVUlKQeFoBCcvbDN53DV3KCOj3/iJHREUbo2KCzLIh5iS9C5GrHJRDoVkRPqY13swgrJVmE/LAFk0oYWT/mVzFQYV75tGs3bLsqxzM5AkHbWgZ+wORwZ/k74NOzI6CpNmBBhCOLCNHXEPFvkh6R11Lr+fQWDBe6/3yB0uXuTPLECunbzSWwCI+RIhWTWN6FOrpS1Z2zVN0/UDF3EMyzMcGD5b7nS/cbx+MakjAFFmROynLgdNWODoqhUCKclUmo6B4wm1QUgegMT8/htmfdlcOJrNtUwDII8qiZbVSqhqt/O4rWsdTZd4iLQdcTIyXgefMCP2or4GhAvWjEYcNRQtahjEcScHAUWW8RMg6CuL/cJZc6l/PPfcOGPwRH+rR5uJ2u33rX67wyDMARAn7GMYwr8dxxFFcUEEES8UHC2nEdogHRVLRjxQTaLwModsgzCJvGguLKyaS5Gz5s3+4WTvvmFg1KotW5fztJmoD9uNttXSMQzbhjDJn0PkPx2ZLCOD5z4UeXA4IEKGQODIj89XvQ9+3WEOABAG7CEUgCwttV9ms/fMC0eNV+a0YWDUWrHu5qG5SqKjdbpWG02FQWyttWQdHFkDIuJDEZQpl8PDINqXb3P5d17oKnHw7Wd5pwrhOrto4jW8uWeeeGMaz4OGESyftO5Gawxy4sZ2rUMgKQ77QE7ah1j+mouJHVEUifqIqHuIghMyAPny8eqLb7X9RMJ+kBFUtGKFCC0kAJF50LpvNoo98w1Aio3+45WVlUv55bOVYP9i66HlIxCQZJwWP0Syro5ooqIoqPZTriHoyZ++eqEX35vl63TxgM5UKEQYm+pMEjraI5Bk6VWvEXy7Giw+P4RL0nL+NqpX47zVu6fr1OfDIkmhZk8g4zFM7ghlJIxPjwNCHITBTvysFpnkAx4A0RyPqcSPhYSEmiwDBGMWqVSaxZxSLTf31zajeKmJbqeDfoLemHLEMxhIRtz4fzuiMIjI5VeSFBuEE5J5wRhMUiwRyTGZ01EgkHosNj8PmFDWnuJJyVlc0eC4Ixqvcd1iFqCQdI2gUmFF2A2QsfVvR1B+6RMgEoPA+SnbkCkCQTIckiclIjnIILxX4WyxaGvnCsD9EYI9QSyL2N0ZhVkAo6F+JdRKRU1oVIzHTslYnV0UHRCBQRQbZMqJiPcPkmTyOhwJRQASAwiNvBge/QVyheMCGhKBYIfHCg+UzYQCGACooMCDr53btiR0Jpn05kevYXAEAglAWOByQciSr3+S5HLXP6TTsQJvuq7YGQyRMdBwYErFIG+9jBJFViDmAUV+M2VH1VMK7XDrJDjigoj4oeBoeWwOgLiW2LWXk16gG+ddF18kB2gTn7KsTEsJLb1V+8FKlBVnIH70hQdfIMngP60bCB0sCCBgEKSwNCN4bA4GEcPv1pDwnsvCjugDBC1YDgkscfZERmFTwBJITzeb9NirBl9AEo/H89sSM2E+XFvXCQQMTvkV2RR2xAHB7PubdnP5iasMwzh/ARVHhkmPM8Pcz5n7nZkBpA4tIFgoBVrGJp3ECkRDAQkUZUNDkzIBE1PGhHhB7lXTiG4waZSgARLjRt241o3u/Atc+Lzv9x0OuBPHBzJzDiXp98vz3r5vDr99dorkTbdbegEOM0jwtvMIt2QHOEiPuBYTi5VMIU0XFVKh1YQAy5Eng7m6ZcuVywDZqxSIJIEjTrBA0hHeadMp6e/fGCBvaIKDACC6MM8ukyWmh8AwPzC7OyZM7bHYwEAmn7/aqvbisKu9f7nYd81kuna1vK90Drt7QDLYX1prRGwlfAIkkcCLD7shXPrOF1pi5g1VheIRF25crhphCHPQsVzuj9dOQL7jmOJ4gh+4wBVA6izTX619ZW6c2567uT9RQhkeKsXKCz3H20ePre/ubO8U+w4+3pl2rx31Z/aKR6ZOlLLI/vNXnimWywxSHinVVvcejBxo1YflUu+5QZAfcMQfDYUYRJbeK+CIp7Gl+lOPrl+FGwxCGBIE1tzuv318d3ln6sHN/IQKAWRoqee4seerqe0ynFB7ux7tKNOH3kfFzMMilbD0fm1N8qC6hkBqwr7aUnUpXF3WXinjRzXnAcGXAHGmXSF6KuAKF15QOF8Ax/VcYLIPnZD1k1g/cPjNwpkyW0aQFddnju/mR47mGh0TbQzSPrQUu2+KrT1cG+sp7/ftFYsAmSkc3hkff+THWBwtmp5Jli6IHCmNjZWq93wAqR2rrj7w/XuQEExACAHkoivuoo9u407GAIWP97iDhWsfZVTaiTz3KgJKOkFvOsj4zZuN609Nxw9Gxt13xvMTrZiOl/fUUkm932uaNc0sZRb2n5+LPQTI0vDYF9Y2y6A/EYkf2i5fPPBxjiQOquFI+W7NoRY+8CVK5+oj6BwhAnH6or40RruoizFAkb4exBYXB/A9OIOzf/fOr39JEGDABCbBxc7s7OzI8vr7R2MvYXZvdG/POfIO887cQbFt4mBuua11b+6LQ+v4TnEvY9orK+WdNbO3Mxh1jbQ8e+FmqcyrvlsaOaxOHB6WtOrew1L4fCAsJLsvFw1GffAFQcUb9UQOGFnmyHdZOKrqhMysOgFis5lMNtzYhTB76cpPNOYhtEg6rE9Sg2QNe0U/5BnCWMwriUTplfOWX+ZgR3zOXBrTXDxy/QVEVTQdJIwCPmcjDs3eKi3ggkWrx9pxxyBQg1nkjGwpLE07MgsgnHeJrnIWxNi6s3zlcu35RxQngTgB4nI1R6PpaDAx6PIhrnBwEigMK33WDOIKXZDLrcWOtZqJwwCRJGZDBCxxNCE3SDKpq9bkNSUMZQMAwdQo9okV0bMXWQBBZ0+7XNF0LuD1xflELuBlDky7mt1u4f4HEF56wwkIc3BwQfJCFgRikdLcbUySrFeykDeQQ444UWWIpJIgUBV8QfsL5iYn4/Fo+vrLfsmBsGIfZFLAGp2D5xRevKm21sSqxQW7IxqNjgJLPPgAwppUwgUaHwGSjrtoe0V5UmEQl4vaX6LQ55WGEEeqvU0DBozA6im26oiDQBrEoMUUUnTBJA0NIsQgJukSIClrfTjrbQ40Y/oFSRQoCK8KgjglCDpJLlgo+H3RtyKJwSxxILC6OERgBJYnM0KA4A4ctc9LnQbBrzILg1COcJLAkOGm5uZmeAKSxHV9o1iRodEAiSLH0/70YF8hTiXLGwZHD0cWpS1ASEA4LSz9+Y7GxsaOjg4CYRLKEuFZA4MQRovDQxwwBBDZ7DDeyZQIEoUsqawjEVc6ksMjisPeeDQS9HuV5NXuTEyC4EVUXRNEBCyyo5ElQCABgm8GIY6WFofD4XmR/GCOpmxYUegSJG/5KgbCEMKRdKeykbm10TcYfTvCDzikwNGGkkXh0UX1CuFlMgQ3YIcE4dBi0RvjMoebKDzEcUkJN3kRV+Coh5Rs4H8CSRcmN+ZXR1dvTaYjKFnhemt3TPU43ECgjMWrAKk1KtQJB6eI+CGLOKQfhKG+mEklFWBga+htAkgyeUkZDuQQWhcrFlpOHaQwkN+cn98a3WoPJjoDWQUcwKCY4lCX44hY6llHOoQfMrQAxRn/T46wF8XKH2gmkEuULwEMjgC5XGlH2m73/zi6sru7a/YHMGElUzGPwEAB0guvTYIYJEhzxBWD6BgCy4b2iTRnkFi3tV5pavb7CcQbVmAJg6RxlEIgFyraR9Sp3e9Xfhy/M7Q5TOaDQwMFMDhMSLL1nSV5F1+Cw0DDveGIBIEhDOLlXOccofOHEE+OFQ2tjVtb8/d+/uHxwvfJpNVKflho6SwbSYfQgwf3uIbOGoJQM0BQeHWQrAisJlCAhECCAJGTYyUd2RjN93/8+JdvH0/jYYZYTHVodTYjs7nW6jJI+NqwiCNL5L5IdgYRvRCtAyWLoioJEqSMH/MWQC5XGmRgcXN8bvaTJxP3PJDDccNuNvIBbzKxaeWcFPrCT1C5P+ogRmxJS3hcDCNkIZCEs80ChOZGqDKhxdPv6u3FO3PrH374ZMYNUZ4bIKLzvSRAOmSd0kuU3iDxIkgEJ1nCsSVJLqF5JK1oTdRTgEWO+GhsrJwj4MBx0Pzu0L31+x+8/npJwz6CpqsGWhdzkB8AAQkZIpzR08Vko8nFaJKQAdIFEIgrMCkGrz0xYYksvwCBKlF+Q/RdU7U1MrT4xbf3v5xYAAR3DSNFhCPCEOMKAo+JSpoBoqeN6IgaLIGELaqKiG0RICBp4vpbIRKA8BYRZ78rIw+ml9bf+/TJgmYRXVyCGMOhXL2cSiB2xiZAuCUaIz2ByJFRY91AvN7gORgJc5LuPpklFQktPseuejo6ML32/sT9r4coroxxRFRejn3OYr6QOgtiyACxE4NdeMxDW4snY4UdClTJ4GIQcfY7NTm5OP7x0tLOtEOS2MTG7wSEDcGFLjFl0YobDBBjcmQSuUVkj3mod8SsSlaMwGG9cFWglzwbgojjmaoN1b+6sjiyMvPQ0+LG/yxiyyR0EllGCsjEFrHHIKc7vE4iUThW6U7zdNdneXZUlCbEVhp7Kyb5ryBMwX+t0NoayW/emv+8+DTmYU/0faxetLhmsZDgdeQYls5BpNcrA4Skk9h1EtzY3WpK8dKoklXCDCKes/vPbfFCzQlIojD4Uau1Z/P7xYwqSJAlciYBx0sQLBEgDXXIWnudzQgl/iVjd2WQiIxnEkABxKrQ9NiM2JIgL1SC5ILEAEg64r06r5intkzWGHlyBoQ5YIk0pE5zqKqDW7+UyBm94Z8m0UEgmSLeAIGIsoV9OywBCcKrEiDIkWj9YP7W6sK9xf6kNcUkdZzDMrKYRPZ0W52mZjKqG5bAAz3/RTFmEib7Z3CJY6FYqr4JGyw4gt0ubdvJEqD8p0Q58whHsMnbtpqfnvl2TKFRwoPQMfqc9EROtmaLW+1OveiwAMQQlTUjSWQdMDOIEG9PXkzx/Ihsx2sCjvAjEU6wiEH43CAhHWQgF85PrWw/frLsxXBnjbVpGFJOnZbIraAAuYFTEbXFbsaPRS2TOcL1zACRSWLRMRw8CV/CxBiA6BwFp1s+FwQYmHL+TRZAQviGqgY6vZtTT2eOf+lKIILrU6pGFZjPqKg3MgLEmz+LhiXBM7OJ+MQ/6AXNJIsxMxkVWCMMzFyxblgCECR8LoHIAoiQCyT/tjWefoLOFXGhJ7ouVm22q7eHdvuXthLBRK5ZsapugOiipRktAiBYFrKIkkRySBRjFICMpkjn2EQRy2RStF/3ChB2RAokssmf9zTeydZWDQysjBT7R/v7E+lgIiBAWPLUgcOEuz26CM9Qbs3SQE4Z+c03ZCJ+TZJQ1RI7RRUU3amUFSAcWyAJBtMgkSx41IZJoHM4IjDivvjfnJzrb5NlGMb7H3jIqyHKslbX2a11W+2q3dKl60jmB+OiyeYH50DHPFIhxE0T1AjTaDaHDgVByUCcMQwzD0gcwpxAovIBCZFFNjICBjxAUDGgxsMHr+t67td3YwaMd2hXunZ7fu99fO7n7kKPzu/qnje8anAwUUWQdLTMURRbllf3HbtfrM/tmSDcskwPVLbJVV3vQOylajWCgVKN3Ui2Rr1s6gQoEFPJ/9tnqfolRhVCR2jLox+1Nne9e++jmwBSF05Hil11RGHjV2rQ0rlWEdreK9hTQRzIP6UzinyAMFqBA+2s+rCrfCuy2YqabLwcJJVGAjfhPgskF0Fxn0acKZfyUOcGKjf0ench3LDw1c3rb69MldfUJwGCiBOAyMO5kecCrUOkvVdQlcwEkfAoSyDYI1aTQRA47OGDbKmREEUgLp1cUCmkIMp5IJwE0+UILXz6tnjrgtV33/VsCo3fomSOINabcyAl0XwsYiqBmMsYB0UcgBSJ7X+pETo6FFIfZs+3Jmv9X2sA+ySyLagEOplOMguIDLNBLzMO+kjjvMbeNx9c9MQq5JH6dIwgWK6OOQwE6RyeM63/a56gexMu3UgIaw1gNeO1wVXfVB1glr91ADESqgQgLFaEIplNYs/rNu1JgFggDz3cunfxwmWPf756VRi9jkw0VwbPdtc0AGnKR4pdvu8f7xvr94Oyq0i+7+/vp6Io1KZSkFopiFhpOblA1N9SicKk+MO33xqJ4hZJTCmS0xO/np4NYjT+UwQhh0AaBp5+7qnlXz316bPpdCaDYosg5tnaTCCdZ9JM+Fhi35AbFgJK0PAdwTN7biKFRDzkcAop8lUS13ZER4nMJSe80YQjuYFOYiSXGsmhHvzQb37nmp1+Zojze4HAqFxeDRW6ntu1bNfyjbteUpsADexiWBFtXh7P0hXrcY3UrZ4v48p+Ahn3AhCJcVjIokgp2ZosMLI664FhjXonEgmhiEQoJBEKhsV++Qa304F3zJbZIF++tvOBLZ+/XKIeTo7lLyyLYRf/LXGHzCxM5vRRGX1jU7haPd/f5Bck7QIBhwCEYhw80QUJblJL2Dm6/CN1xvNOphIUR8L6UShylW9+OXHJJT953oRzlguAVBHkWprW/Y8t27zj7rs2r1lQrJKijJeeabC4BJoAQS5HVXGjgiWP0JfHsPI+P/lBSwSBfxPEOARiOZ0Wm+Ej8YTvK6VCas95o6laiPN5LoYsZmHUikB+J8gFFAKN3OD7yIa3Vj+w48kVz9z9vOVBHSkwe9BRi9JAAUgUijqgKw+Sm0C01QqxfigJz/dboRgohPiosQiBt0/+dnASNVeGlSOrlO88b18cInf54fDJr5UYJw7/eo1tUk6fONHjHQKDIC4EgowokNfeW7PzyUWPrFi0gdZtRg59RJJq3yCQ5aPOc/pgUVdKsPQpO24A0xhB7HCaFPjuQwSJclw4n89HI7vbNIa3Dja5DSQoU0553mF4/mfg+e6IBznxddVPvCKjh6AbFF8THlzkJ/PtC4CAwsLv5tU7XlmxffsX+9/U2Y4/N4NuQZgpLAydiAOxjGuVtNO0xLEVuumfATJHINyECIR98d02iscv29Q3vfqIN8SDH7ziCOgoRw55Tg65bfDEBJ74FWu9qEZkXChRtix/5pX92/fv34isobWaYVVja8qjuHReHEqIbvRkXGGKHGBov8JAiKF4JRC2S98HCMMFAZYe36qosC7J4utHzzvK4xiAeD1nT3026vHBylMrR/Hlh8TXZ85MINl73qn/AAIISuid4ce+XLNo+xeLFlIjDoR9qEw9au46gGTg6OLw0/f3cnp5yDEgOBB+R8MoxQ6kBCgEQcFJ39rKvDNCkCg952eqBqfgBFmHFv13VMvhRCJ1El/P1KZ6vNHDE7C7k5dfFEQlI/J7aN7a+xcuv+uDRXct9Oex1L6JZqqxDSpF0ZIHB8pEde00bzICNewRRx+I5O8C8f28jSAUgiB28PVqZ4xx2Wpsd3htTFoyPp1gI9GeQwwrj4/CtuJxM7OlrCQvDFIFhht5h/ORzudfvfuRFSs2BLETQScKX0f6QhXp2hHgsOEGxtuD/P/c/navvX/uXILsAeFMEKiCIMioWOQUFSiQ3WV33FHyGyyLOUqomik4Aq8vL0fqB8hnpaV1J88NjZ47db2SCuQiIFRJaH5n8/2fPPHkmicWGIU/RNIEr0T1FWPNonUEHMddh2UEPo/ULhBTCMfLp4MsBYiFBgMphuCNk+pyE4QVcjVBOK1SQxCNRJWnXFsVLMj2kn8FIQcl1AuSR9fvfGD9ApTtxKB1c4uabyrS4SgMiyDGcVwGr6QOwxoagzDhj48d9DPhPyDFArlnjtInA4lAmKfavZE5EoUDlpYEqUFBWdEBEHxFRQMS9lWt3WM4/woiktDid5pb9u5a88JTS1xP0NUp/mYbhiUOCjnGyWHbKZhMIFqa3i+QHKfWDEQaoRwgCJ4/AHBnxlsBohKgDSBhCkFs04L2V5VqF52BKOVrGzkbhJVnaG3zcHPLq++98PrLthBwGEkUzWBxMCwL5GCPcRBk6QyQh4opPKqaATLH+Yg8iCDr8I0Rr93iioFEowRReUkQN7SC5hGma1WlqwVm+8jzQMBAEGikcOviztbnl+14+97i6SDMA8wBcnQ2SIizh4GXViaWsT4nx+k1fQdKKNRnG1d3B0I2QVBFTiE3KFMKJJcr6/FGgOWDyAIIkkwixwBkZThcX69tpW2JK7VUICG3QCezQKp4F2q5bWBg3uLn1u9c4EAYHskQJYcKSIjb+5FjyM5ybYiRJzl7aDF3aNghYlfXOwCF8OVLkSSVR+B+k0PKI5Gj+LY7ABIIflNOkQ7vzd9JEGoGJESBVlhZUhI4VMHox+WzTYtSGRq47eqBlqff2bj5Y1gGOVgiQnAfoT6CsTnUigBZajKufg+D026tj4kumc/H8jGC9Dz0/kiPRxDIMU5zTz2kWgSfWz7mdZTQ5SGKWpzhURKltxAEBbNI6q2DBFF9mfKnhgO5HgxQGW6hwU23xTYNzl/y6ZJitxlKUjIZVfA2Cmj7d2+69JXlHG1kHdfHrYdV7ASR9AhEpUBQayW3ed5Z2mwQtRDgCaJ2HkFQ8quHhNobunFtpApsyupSN+oPY8wEqUzI+kKdrd2NLYPzN7zexU12nj9DbcFqTcyS5J+auH2adPwZS2qvkUxu6+jo2Obegrc2ZQAyNASM98fb249dKZKpdpZSBwnSdBR30ZwDOY5XRBhQRtrbp2QObR0dP+eT1tXjdeEj7cpQa8Zx9ijjCkDAQJBEZaird7gVf/3y3Y1d1oWCPvGeCk6guSrLhF4CJKUaEvPUHNpXrKnmY+ZPkAAEV3dyN91AokngyXWRKEHS6TZvKBnLlfnHpMj0ZaqsUeApEudjEKfgTB6P9ND9jmx5pZu9C0ASBEngFtqypLfQMrzq3g0vgQNNKHXQNEzJ8iRnxuyXYS6sQRl4IZRtfSqne6SxMK5ikxKizqYlIP+LWTbKz/YPoeJdeTATpartwiDTaKYKINyMwc3oodCKWiFEgzgNhUtT53nJVYSQhD5csrawabiw997FSLAambbWTWlFdZpXbiZGjnmSvGqGoFMVCKjqwZGxPGKiUqXtt0hk8qyHEstZTNTKHldOqyAVSMQaIBBmMZwzl7in8rLj6opafNhlBgjDGWFSobeGe99oLQzsXdypwT1QsGMOwbyprpyzKb/lllfrUBgpSG25JA4BSBggGYEwpfoy2UHHxx0UIhNJJ22Wylr6rsHnT0dF/ffmVLABSXqRWq6OV84EuSZRm8AqEpWp0MbC3paWgcHC8NxqXWYTVvAqGG0m3nFEkwbLYyf5mWhqwcMj9DA5kgSJKg9JchF+mkZy9g92hrBVYwc2GKDCaIWOkeaoPtL6MXKMO0oJ84xUlY811ZcmrrtqJggVksJKQr0tNzfEGru7589FzLa5vTp6CDgs/LqOLzhiGZvtq2XrViE8ASFKnAfP8HVEsaMrV/4Zi5rISPLbju7bt+9nYYRZUZfoGJKDR3bYqokQy0wEMLFKgwZGSVYThH8dNTAt9cbwL9TSkMk0DjR2d15ZYfbCAUTGLNiVqUOz/eBQDwQMLnIzqVYaSi3eQ4U0MSvGlBnFIrH+FjCYrQmig2FiNENIYvNRJAlihLqDroaNNM4AAcrl5uzwEXAApJC5OZy5sqWzu8F1+xM4SiQH/EPqMIXwILQJm8ZyqoJSJYFSyAL4bEWYCiGDcipdNGYclguAQalO50tgWrAogtx+++3NJLHjFxsxcEgQ6kK2JZD6ukomEoqZlkqXBDN796bWTLqhdXDtHBq+AwFHOkZ9iIE39oew+WU9Ghz/iYQofE9WISsZiEBwZxyudoJAcTyEoW2BRCDNMK4XbYzQ0g+zlXxd1QPUQo701YxayO0QP/zKVZkQ59/a2JpLNszrukXHlHi2Nk6FqJntRBGLQxil4rDWnkgEIstyCsn4GLIuCtTTJBBysAFs2mannEoRiY7yNetiHNAM9nfKPknWSgpb6XBd4oZruCsJQCqhEJg5SpRCoZAsKsrc0plUIxbrSsVr8Lss1BPD2irhGnLY+fg0EimEc6RMIoEYj+8fKheUQuF+eYZDkgAEJHITjU5Z+xg8AgFAkiARNvaZD6s05zUdJFVuzt7d3ZuMNLW2NJc6y7ePLMDVCQLRSTOHSSqwZQOH/lpTQAIO6JAD41ZqOWkSCh9YgUGFZMGhrGkbTyOBbQlEoQsVDf7ZxzaUfgmCkggGkeLEsFlWoBHog+G3MK+74Y2GltZCHKti244qyYYR6+UjFnjZQM2CHXtPNc5FIg5TiEBEkjaxh8TwQSCIWjbzonkqgYjDzahK+/6eWWnd1eEx49Cx6XQQ/H46KUBK0igANnU3t5Y60+cn+HSYaCA8JnHNobgMCxwQ6cRXiBsYVwFpCEWzxE4VrK48D0QeEox2ulaOtptQSBONlNex1nGYQgKQhHP2v7k6w92mgSAI+xWKBEJFkdwWjuJQJ7SXkkZ31EECISHxC/ojD8AT8P5iZnZsJ101AkSp/GV2b/f29sJhWPwof7f/rh/waAriNSVBeRKHutCD84iaRPRJhkhCEQtS+E632STPGSiIQNrAIIeasAJxfIDDIORQHlRkUFksewjQ1a04TkFUM3IJbt49Pfx+N/Tf/yy/jCSQ5GHYUhFl28frbX5TGCDisCIEkSBKhgBxTR8Y+cj0h8jpLBiRMLknFMdLZXdinE5MwKeUVcFhPTFSr0B/DnK3AgateX2xPORD3n7dAeTKILuUCUKOn4/XmwGJ0ByTIDMIPIuKyLfC4rFJJoSBzYQcrofhNmJIj2muGBhTGyA6Ut6QGGPwHQe3t0wyZXaWe3fNZp/ysB/6T3eXIrEiWQfuLEzIUXeMD3McgdyPIODgO26OKSBmA8tiwctCxlATdh5kkU0YOnyMf6XLAfSHuFOKFiq+jkBQ9eGrA0hbatm3Q9l8HEOYIL2GayJ/5H6pkTdxHAsCEOZPgMizRkXy4G22WRQcCo9jDp9A2qWcrlTJa6QezuzpWiagpOtM+ui0kwbqi3MUh/gCS1P7VDaHTT58PNcDKtg1XPOalcIjhhHBoYk3cRhEPijPcqybRBxKGbBeNObhquujFg8MOzTCp4jh4KAccKbx/gzbp155bfP/B3AZGyI4V5MvSlkcEJGrG5FEiLRb7H08r5t2zoOBcY4XeIODsa4s8mYKiqOJjcKKxMIMuk0nPV7JrwwyXZU75hjYZoTTCKUUXlamIGfmmO2qdrVWbIg+NgjL3Pc5be5v9An2c4jwR/PgqhMHIWQWzoLMi5ZJ1LogRq1qr/vvDDIKYkV0QXYGQdEuEoKggmUQc3O0lCCcHLciRyCQo8q7mloO71Jtn/brGWSpEOFbhLnjegeOmKMMOw4RcMz3p2TyqwIMzpQuoUpvsbhkselHEvuWOGgxzelQZw+kLTGuQhT8BhECz4KR4kQRCNIhRLpdU1NqLzb7pzYU+YIr+nep/YUQ4bjuNj90c0/8hhzO6wJh0T+BvJk4tPGX6VZSWA6SR5JQlNMhKQV7oJBkiL4vDTicd/b5wnPXSggQSrdqSoUew+Jzub25kSLr1a5kFCioFB8hCG97+CwfFoLEOr1ehyAAcSDgFRzJHAaxsc4HCfuldi852Dg37PrUNxtK1V50Rbtfy7OekViRriKYUGsVuPOQhn2+/0ZJAALP4rZKQygQhIUiOHBA8e1GrAYJz9IO/wH+Y1NvM1kRQQLwqM5SLeuIhzG303wNzRt3Hpch3Kde/Dpm02SOEtNc1r5WKAKQWvtN3rSprPmhTl+u5FnYjQAE48rYAqwkiPQgqiLE6VCCEMQLbS+MUpIlISXdznz9XDJOt5ldpPi6rEk4uJ19qgA59E7qdoVZwqTI8CG2tbumT08vyya15f0ZnvMSgnT2LIDIsyQIQaDICcfqFCQwkMaSFVHDC0lNGG7hqbLEMMVbh4rLRpNQEy766jsxl4yzzp64OWUhSNH8LdZfZPb+UIbUD+uzMygCEMzM7hGS8FVsphjqFkQh4jomBEFWp/cApIwUNoOo3fW353K8rCT+Sw/TNIUzvLspmvc2iSTRyNpIAp9wjMK/g2cEaWu01bom5/6wGdrDcEuQq/f3TOvXAMHbsh3KTp41R7pBJkGkiPzJsZFqUBhEeoGjKvSFTFm0Ej+BRLtEm0gQ8Yx3p5Mi5wpJbHGFx6616qsEEUh+/MzazIoQZPGJh4jYTbGRP3nWuPSK4znIgzhMYOssiTmonSoXtvLgXp5wYV9I5hl7LF1O8UgnfSWJt0G2c5NEZi8XBStO7ZoBrdGLoZQMRRQjTIdYJbW79No7c5wkwwlEz8fHVVh0Yf6VnkcOf5+YSYKYR04J54L9oI1XS8cM74bgauUNBIsk+vkxSDqUHUBqk0qpLWrG/B6fSMUP1uKuiicsi5bFMyPEjiWOU5AI9iABR4iwo5nGDfta+V1RRoYkiBMVLL6/GWZFDLKXJPxoBZPY+DwjyG3qKmdWa2pS6grK37y5lyJXyIcJyuNHqFh0Lpw5vBMZQfikJKGBA/56JzOLQVx2eSX2QvwrluGpXAmOaBIEyC/Fu7JJDPfCAHQkyUpLJF9NqhU14382zh7HbSCGwr6Cq6QRImGhMWAEg8TSpjBorFzkClu42/tfIh/JpxkICNvF2vP5kcMf0b7WWSA3injeiE/C5XC/EkcDkSANBJIwOKI+CmtDeoGUljU1btw8zU8fsbCTWR6Wfms5CL6VQ3Pt0ms14JIkeWttkHhLBUgtjAeu9ryla3kV74HqcijO3RQgqrI6CL4lCwwVFYniIOLE+ZxjjDJGUyHvs6J5/9ZXuClTFOvESIKQ4fOFZZnndxBbqRrLw8qJi8EoNet2kSKesJe4KpocPdL3CBGIJMm4vmtOfyTxKIelV8jqtLbWaGlsrQ2Lvb8KRaJTVOyFq8IUwzWXJECG12MBowBiBaCx2o3M7o1VHFLpVBgCUQ65HUDuO8QiDA3oAyQ1wRIkMASiwmvyZQ8scPSAT7HO1BgQJLGCZdqlQvDH7RT1mUpu81AWKxWQrdpWkXD44wWIHIeY0q0rS44uCO7aDuscDSJMIA0TkOhLhKGOURUko+pcPAgU1b/9GShtWtx2jp6DiPxeaYb7Zfo0w3GrndaN15zn9+cCh4O4vXWvwuGwJohA8rQiUWjomYkYmyTKisbpO4cEAQSSsFDmuzbGJikCCEeHQiPy6IIfS298fw2V+pd8bt7q2nOaP7/uav04rO5c/dzp357UEyRPK5QF619rwRwkMTCllFTErY0pNKsDRJYkOWaUaxEknBySvQMgSTIDvgOiG/hidVjraChihenZa5q/qj5zYWTdDrcX70eQJGnepQ13p1ARFnwCwXo1rwtLHA4CCbaTgNJAOHU8k1gTRKvU5OkEye3znzY+X8NqBRCjsH7N2/DjzY8KhuRwQ47zIdQ7CCgi0aZ+cIRgjaODRBYRh8bdMZzmuJ3EgyV3vvTLBM25+B+B/B68RbpIkfPGwGd+DhVFEH2Ew4aPgcNq5iOOs9uxwxWIDBKsc/D3/4PwLk0Ryng4EERPtbokkGAC6Y+7AKljrIQLJIOdo13XR6nvRHmtJ6v/Sra+F6ShKBxFBf14irVBm7QHo1EEgTJoxVw91IiCQWwgYz+Q4YZNBW2Cc6g5NRImlhoaaJGpKKUQBBpBEOFTPfTQ39NZnSFMhe1+5/u+cw+7d4mbCejlY1Eqeu3qjRuXAHC4UBqmG3J+NTy/BU0edDLEhdjFS1fgsnC5tyBW6C2B6od34yB6eKX4wsVL4Thjty/fefoUQ+7fOffo3b17T89fflwUlEePlEbLVhrZht1o7HoQ6Rc5NWcYuZeG88JQ1RLEV+Pr5Fm73f6qdlTV+F6Cf0sltWQ46Wy61cq+SGdbrUcMf/567ME1GGCcFDniIheN3eRuXoSZPRmahEiyUYKLkSSNcLEol4wSBBeGiOkkFeEZKYIytiAVcYwR+Od28VVDwu1GT1EE+/krQeARDH0kdBn83LtX6Y/OpO2oMNDZbLNRc89Lo9n71GT1pvb5fTlffVP7thjn++vXh8AfWNbesiwz+OKG4Qdes+lpmu/VNX9f8dygovnDpufvD2ZtXlusf3+ubmYTNb170jivU7wYJbkkRok3xZsxkBZYJJ5I6jgZJ0C8LBUnwgBHxv+VRozC8XAvIIJ08YikCJjE3EMgv1Ixa9sMzyt2Q5G6NCNJisJjtGKnc4ZaeuI4TqczS6UM1XgymozajvG+/OZN9fObP+NxvpzfmuvXy8BcLoPlcjDYD6yBu//iVvaaPNQ0d5hp+lp40qx7niz7zcpgMZqWrWXtzXzVmaiblCFIEs1yUXjKT1AXQVRHroFkQXeJKBYnAML/zVqgNVgISHIcQKGRrsR0IwhDR1CMbwkojnSZrtIqvipKOC/ZkiQARLT7SJAYXMdsJfvEefJ818sZm1RqlVI7m/ZoNDJyznw0G5X/fKuWa+WaaZlb87AENObrrWVCDAaB+0X74jWBE1fLaLKswRnAKtR9U9PMxVytjvPj8vx9CmLyUoKgyWQ8eS0Bo7117Uj4wP8i9EIwv8ficY4DhEQCSgUXhSPJkSSKoAJPo3oEo6HRVhicEYRutyUVi48YhBEkOABPBCN1TKcpFi++evXBlhq7F05nCncstTuj1JtcabVKTSbT/GJc/TMvL6zAsoLD6/5ibK4P5hL4cOHtj4rWHGr1uuZVCrIGUApyRs58Knj7TMYP8qnVdJ5f91//Ls9TL1qQQJC7SAkkFILETTD7Lcg/rNT8AxbjiBjgiSUBCBk6hCVZUWRhgFS4ngxcQPa7koQoLVx4hOA6HQGiAIrA6FB5dBpFIsVXYByp8SSntjeT2Sy3cUalUbuUmrfV1Ko6Hv+oVWtrE5AE23Ft0V/34cugMvS9oTysyHKzPpSb+0LTqwCGT5/+fZq/fmX8JRikNgYFBq9r81Iv3cryqE7pWSxkIkYcOXkT+qyLgAMqFzQroDnggyMSiTgXj/4DQrKkLooYzaMUJQiQd0FABQblcRQVEVyKsBhDUygAjrKijjO2DcAaz52SkzO+bmZGx5k5pY3a6bQ3qelmDnz86JtmYL3uj2tglf7v/vZgfQFTe7I2BBY0T25qYAtXq//6+TNTyGQ+yZ8AiDWwDkur4rtL8Nhopu4chcGSnMT9M0P0yKlE4hIguRTugAtlFpoE3B8HgyQBAsfpOhAoYigMV8cknGJRGtMjYVcUQdEuzXdFlhJJDCPZKIthVARcIxQ/ZHe77IteW81l047xpAV1NJdajaahvD//WGy3g8Ein68Bom2/39+ae83VhsOmXK9nPL9QqGsBSKv+69ever1eycDPclA7WMFgqPkVE1gdTdtPnoOmodreBB8QxJkjx4/BU8ZYImyGgBGIJAnCSrJkEowUZTnIMiaCahAMEzBEABAYoke6oSFaqE6DuFjo1GicjoqkCCRFeInhd62GYveyds95YdsfjZb0Ip3LdVIriM3q27j/2tybtXx/8dpcr7eL/sG0XNfzhnXgAqovOKPp+sDEr0+f6oWCK8vul4pVXhzcYUGreMG32pu26uxsltUxjrsJtSpx7MSRo8fPAh3gFuAEbMP9oyNGcPGQGlYEq4g6RbM6UNLFcEGHOiyBLSISz/RAcBgmRgEqKgIfJItSut5VBOF5Q1Ek+3kWJkDJbmR7jV66lO6VoByPptXyeLE0t7+rn/P534v1YQtwrCBwZd8PSfGHMmgLrJL5CcL6CVBkiLpc6S8rnpxpek1rtJrCfGlLGCtG9OS1GHHs9ImjfwGN3z6sVKrzPwAAAABJRU5ErkJggg==", + "description": "Displays the latest indoor fine particulate matter (PM2.5) telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pm2.5', label: 'PM2.5', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM2.5\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 150) {\\n\\tvalue = 150;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:broom\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":35,\"color\":\"#7CC322\"},{\"from\":35,\"to\":75,\"color\":\"#F89E0D\"},{\"from\":75,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":32,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":35,\"color\":\"#7CC322\"},{\"from\":35,\"to\":75,\"color\":\"#F89E0D\"},{\"from\":75,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Indoor PM2.5 card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "pm2.5", + "particulate", + "matter", + "air", + "fine particulates", + "fine particles", + "particulate matter 2.5", + "airborne particles", + "microscopic particles", + "respirable particles", + "aerosol particles", + "dust particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_pm2_5_chart_card.json b/application/src/main/data/json/system/widget_types/indoor_pm2_5_chart_card.json new file mode 100644 index 0000000000..18ee71d5bd --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_pm2_5_chart_card.json @@ -0,0 +1,41 @@ +{ + "fqn": "indoor_pm2_5_chart_card", + "name": "Indoor PM2.5 chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a indoor fine particulate matter (PM2.5) data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pm2.5', label: 'PM2.5', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'µg/m³', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'pm2.5', 'µg/m³', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM2.5\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 150) {\\n\\tvalue = 150;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":35,\"color\":\"#80C32C\"},{\"from\":35,\"to\":75,\"color\":\"#FFA600\"},{\"from\":75,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 150) {\\n\\tvalue = 150;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nif (value < -10) {\\n\\tvalue = -10;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"PM2.5\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:broom\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "environment", + "pm2.5", + "particulate", + "matter", + "air", + "fine particulates", + "fine particles", + "particulate matter 2.5", + "airborne particles", + "microscopic particles", + "respirable particles", + "aerosol particles", + "dust particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_pm2_5_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_pm2_5_chart_card_with_background.json new file mode 100644 index 0000000000..7694a42074 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_pm2_5_chart_card_with_background.json @@ -0,0 +1,41 @@ +{ + "fqn": "indoor_pm2_5_chart_card_with_background", + "name": "Indoor PM2.5 chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a indoor fine particulate matter (PM2.5) data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pm2.5', label: 'PM2.5', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'µg/m³', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'pm2.5', 'µg/m³', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM2.5\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 150) {\\n\\tvalue = 150;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":35,\"color\":\"#7CC322\"},{\"from\":35,\"to\":75,\"color\":\"#F89E0D\"},{\"from\":75,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 150) {\\n\\tvalue = 150;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nif (value < -10) {\\n\\tvalue = -10;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAU8AAAELCAMAAABu2vS6AAACN1BMVEX5+fn29vb09PTy8vL7+/vw8PDv7u5MSElKRkdgXF3o5OVIREVCPj9GQkNVUVJZVVZTT1BAPD1QTE1eWVpbV1hXU1SJhYY9OTqMiIk6NjeRjY40MDGGgoNmYmNkYGE4NDViXl9pZWZtaGlEQEFzb28/Ozxvamt3cnNxbG2Oi4t9eXl1cHE2MjPo5uZOSkt5dXano6R7d3d/e3y6treEgICsqKmTj5BFQUJSTk88ODlQS0u3s7SBfH3AvL20sLGWkpPu7e0vKyybl5jr6ekxLS6xra6Xk5SZlZbt7OydmZpoZGXEwMHCvr/Kxsefm5zHw8TSzs+hnZ7OysvQzM0sKCnW0tLY1daloaFrZ2jm4uPIxMWppabk4OGuqqvl5OXMyMm9ubqCfn/e29vi39/o6Ojg3t6BeXfi4uJwaGbr6+vc19htZmSEfHqQiIbr5+jW09Tt6uva1tdiWliVjYtaUlBmXl2clJJ2bmxrZGJeVlV+dnWjn6B4cW9yaml0bGuspKJ8dXN6cnBkXFrU0NG/urrd3d0pJSbg4OCpoZ/g3N3d2dlpYWBgWFevp6VcVFKZkpCNhoSLhIKknZugmZeIgH6Gfnyim5m6s7EnIyTKxMKxqqjEvbumn52Si4nb2tqXj41UTEuel5W1rqzAubcjHyBXUE7w7O3TzczIwcCzrKqKgoDV0M+8tbPZ1dNPSEZNRkRSSkjNx8W+t7W4srBKQ0G3sK5oYF/Z2NjPycfGv71WTk1HPz0fGxxe0jcdAADDaElEQVR42tSZQXLVMAyGJZlk4btwim66ZtcZVrBg2LzJObhBL9BNhwWsOB6yrFhyhF/y+vroVE4cRXYC/vgjuQU+jA3iYBz3LqgPe08BIACIG14IamX8HdrZpZs7HvfeYZ76Rwee0rdm7u3bP+3QHEBp4vVLp1yOcdtCA8RAY98aTwgPrrF3qcxqAdjQ/Dj8m2fuG3WoI9I4aiG26wU3VFccQm7BwqMjeZpK85ZY9oc28TKIeInkIp1emruxKPEcEIbEYTnherupzlGOmD97fZKhzQ5MGQX1dyScpQ30ToFgk2PLIoEnvlCrtyFq2dJrc6BPbo6lcjFHwz5oFB3ujDbNgzZN+1xNQIhS8SvUlWf7Ft9cnwgYA2N9ahfRqunSs4fYo1K3cQe+2hPSBy2bgxUneH0qy9IJ6nEbVeKozzZz3/CAPtX38gSDmb0cB2C3+uQW9GkTKFs0ZlM2+x6UoKvziuhta72hsrugT/TTVxA5ZEE0KRpXw2v51HDZjBxVe65cQaJuxGTE/r46bco15HqI+1NRD6dSgFK381Y/Ii4e8jgiCTB9dhL3FIPUfTMX5rRhDaZPbtdq7CXz0NUc9I7HGfVJCiUQwz7oKQR9Gl0PSYGKRXFilg0q8mtompLfQkWI+wI9bvGhnorCG5rnGPQJCsQD086CHqxNAPKDsa4pTZ1Tzq4hx5goEuE8M0+2BjN85Tt4LocZ32rq8/q0i0VC76Y7mmYxorFY5k27LUrgCxab6NNEKi5gESZOROQ+ll6MuvL/qk/DelyfvkTlqE9l4aqLiVUHPKwGWadn1XyNuCremnzOSIlo8jwprNv6wxbZiA3ZG8GoT998IOrTnhmoMYd7059/ZIUOXQliA4NnmUBDQMQ0pzmlKSFbXTHNVOlZ/ryUJx7eQ16sTzyrT2la30f6RPC68gzbbGr3ulWYYFWwTtUmo2tybDynSXgqUky6b79+z4naeX1GizlxrM/N/VifeahPyBbmHjxRIP4bWlCAUc7zN6x5sfItU+QlgD5pIRKlxDy/30+JUO7tpzXtg1kqPbiHPL5Lek19+uzYGkhnuS8XF2H1QAqzAuIoOyQvTvNKrAHgp1oEEatfYD4VW5anxMGUGk+x6vJ5RUnfxWnYxvqES/UZ86d6GbDfyjNPqLeKW0wXTgRIbgeHnacoPc/5flmY5+m0TBR4NpHK8bIffvBcGI/qEy/Pn+T1qSkTUdakSVK8Gsh1geUiYNgKHjYiIydowzqEKDE+SvfL6W65Oy3L54dlkkdVvyMEyvJ6olGir61PxmenIOQwQdsYERKiT28K2/OcDvNMEzH+5e7jic9l+frwVJ6FszzReO7BOUrzhvq0Gq51W5LjylNCtjDUdZtLlOZJ9JkIqxEbdmzQihB/5w+ffz4/Pv94/P3468+n559fTvWfA10lsv8QueWv2m+TP9eSBLL9qS4o1sKzE4ryVCuu8eRTkKq7+U0McXj6S5y57SgNhHHctrYXvEvfgYQbLjQaDSZNGk1a3SCLDYhWJYu46kVbrbgUIaUYpEQKy2IExBSfzm+mY1tsKh7jtxxmZim0P/7znWC5SiUn6gtve/Hq2H3ubG2vsBAqCGi6oNJbmZFMkoL6P/okTOngjIlUEaioQ0neImQaIMPlDZ7RAU+G8MTYbudYBoZhso4OgSCUzVdyGaEwL/hu3+yqmtac+Ut7IU5QPyRViZFe9+fiP0+U+jfxHe7h2ZLmBUiFsAQDkSb8YLClkabwmA3iUYwnd1PIZ/Gr2DxCjo/AblMQdWM+95burD8YqeposHLshZ6rcGyCQyrP/Srbj/If6jMy0rtDIBFRmoYRJhtphmx3vHkZGoMEI4RpApXlROk6j2e8TJPwDxNBvGUs5p7nLbebzabZ7pQ7d9rr5bwqChxLNnwo6PM/wzP9mtPXqIjaP4jvmR0LE3oMFQZ4CbxB7DxI3sMinthYRAutoDGLVxSB54VMJZ+tKKQW4lhF0Uuev3RWvfXMnZ05M63zxhq4jmfIusyxUeGJWiXB4Hy87fRzCeXv6RN98N/RZ7JjDDc60Cf+HY7E+0g4OzwxKzTAbpRlCc98TlEkXVAgZ0c82QmYKJYK0+123W9qbW02Xm60TqcPzwuZl3TEkw55MhzhSaSaQiXRLduLMU2fYP9An5hdWO7BM0ZMXCjSYLDViTfERgNYjjhONksUCkv5LJdTeLnKT3RJyCC/KUpGwRtPlz1TQ3Fo5rZOj+vdpuNfk3lFybMka8DyzF0nPaekOmj676VJyQkW6i/qc1fS37XlaVw9kgyatDRIcCJ+Ej8CyIgnSxIlNs/CgOMQzyzHQRF065owMcA7KhlRFI1S4ep46p8FPPv9WvnoiaU5flXmc5U8R2SC5XnzOml9JGskmklpEe2hl9LMTPyP+hN9gkUdDyzERA2O83pEnUiDQgUj3tscPEBsx3CJbNGIZVGoYjiOm1R4kc8ZJV3XZVkyJGnubXum2VdbNdW03jbXltpqlgxdgXQJH0UH70LFITJMepfp/F/WJ7E/9J/RH5xggmf4AxNSKEVFPBmEE/FESoU1MAIS5MkGuaYgC7xRWiyqkmwsFgt76vT6ptkaql1zdGI1Lcsy5yXgyQXOmMYW4wmW4Pkb9VKyl5muT7KU1Cfc9utzt2eMGCarIQAapFIkkQdDIkT2LRFlEUpkHJ/h8e6FujIjCLKky4bhbJdzY+7b06bZ28yaWkt13U3tcfkEtv1m68kZBfndMOFKz+l3JnFW+1j+sT6xJ/jp+h3sHH6K/FWMJ3kCqogfIhjwBAaEJybJcehRkGUxA80OXKbL8i0ZeI5Xq6k/HY/HWtPUurDZVWvQrx2f1NTWaLC2DUHh2IBnIHiKSvRD/tz265P6M32SEyWuk3SayDLFUIRnlBWSPnrgLdnAqHM0Mi5bQZaFfS6VrlV1jFSRdUkWIAwZZ+7AXJtav68NRm/fHDes7pOONiy3BgdPjsvdM09W8hyFpYm9CEkkwi+WRv2s39vxEay9+qTAfqjPvfGdyI7knXj0fZmOhuGA8MSBPYs8HuALNno+x/Owu3lFEKrgJguFQklaFMZb34OZ5617m01vZjqO23zbefLgcePBneFseHRQrtfvtFxbEoU8h0XOVSZ5SZzkK0SjqfqM+vdJqDGB7dUn2M4h6foMB9ERMZXGjbQ58TD2v1SeFLrySh7z5DiaRjMAKYqyKNwCjIuC5/vzhb9s9h37igcV0bo3XvXc2RJSpZODB48f33ukdnvDRvHw+EAdjC/DoXmOYSAoVaAp+qwKeSsb8kwQIYb72nt+Rt+vT4qKA/4pfUbRMsSecKBwS98yVNArJjzhFhRBcIM7svwtBWKPMe9tfd/fOv3ZZrPu9UYtEOXZqrdBNDftO69PVLVz8vrk9O2w+LB8UK613bFlQcPO97x5tWQYklGSpJIhGFeu+FIGnErkcFI3Ohklwe/VZ8QlXZ/Rt4Enu68KBwl9As0kTyrOk/mOJyna8Z2hKxI/AZ6eO14ul47TRLWP6w7f9tbr3tlm5m6csWuddt7Uagfl168hCjWK9fqTWnNst9u2bS9BwwswSKhKxrVqRoe1khCUCOBdfpEnFY12GSbYBsvp+iQIE2rcGST1SX5Bim3sZHzlOAZdXJAa4l/O4YEG0BwYw1X4THWxQpm6CSy347WmtlqODdmmO3jzuDVqHL2uN+6/LDY+frn79P7B2wfFh0ftrtWqWdrSn/tIoV5vZcMBtqFXry1Q3c/QQSM1aF/FfBCZhfab/jOKEmn63D2OcNmvTxKHyNnuPEU82bDUhBm6TMQTcQVjJygMVZ+vzD7wdDdnG80atkZjxxmfmdZpfTgsHt2pP3z5qvj43qu791+U39afQHxvj4ZvR5q9BJ5L3/M3vakN8pakarWKeAZ7YDLhmATPdJLJC6XSERFfggbpgosHf3QPAabqMxlAyZSmCVvUMCbqpGnME0boKZvDMUm5peu3ZBscp2tqY6cLkbvThfq82Wyqd9Tum/LDB62jLx8+HdWLh08Pj06GquW6a6118kYdbO25v51O7a05c5aFxULSJUiyFIhLOQjzuYrCI56JEBkZTYEefl2fVJznLoQkjYhnTHep+iRvnujgUDR5H5QYogHhCVNAC2ts9nY2m+FzGR06HfPpSuuqo9FsYw2PjustyDZPX985PTi9Uy4fNt48uP/y1cPDw8OHjeM7qtWeQdXZbFt9d2nbDkQtx5n1ZyvbW0BgwjwVCPK8AP1nacLCp4dpMZ089zjPvfokAorzjAJP1AtN1yfYPn0SeiFaJhiGH0gjCwYsg64JGZPPgjR5/lpJlPXFfOs4a7XTKL+2ulbtcfHwTc2cfXx//8XDw+Kb0+Lhi8cHxw++fHzx8Oj4Sa2taTPTcm20z+f+2oR57+xspg76lwqeBxE+owiiouhSqXrt8hW+ErWawfFgtui7DV1V/Br36DOpxmiSOif4d1t5qf6TWNzfBmGVLFCR0UF855iAJwSiCbCUSiiFh1juDt4eHDU6J6ed+vEDBLb48PPL959h5fPdd4/uHzbuvX/64sFxHQL8qNk0+7PpvFC4Nr8w7mkjs79pvh71z55Pba+giwKviPJ8Dq71xpUcpLikY49TX/Ij4E8Ul/GkPVR4aPG8OnopgUBmO0GaHB3XZDIhoOJOIub3iQyoOFIaj5mI51deruw3lTKORo3GVB9cHozxwQfjmzz5YNRomhCTJmq8uQ01KFFT7Dh2hmpnBoaZDAMz+kDZt4GwyRZkX0oBC/3nPB/glIp1108vs1x6C4ffen7n48cfT44dJwNbbzCoFzoFfzxDc5ockFlZo4xKnNLCwlRXM0kuNh023XRM0OfA00AFWgrOoqn3z4+OTr+5zLeDlaB/FmQ9hQ4myJet0IXj7NuLEIrZduPDw69hnYRmwoE4CAr/VRD/M8TH3XbE9LftfLQTOm8T1L14mtEBF3eDyfbFnRrWtE9yWEdPcgUrsUISt1qbtuj8qNezHTsXxVLKwycyFEXJFGtkRCaT6LK0NBV0TR6PXh5JgtAUInOZjnjpJN8JllnV05kU2+02an3WH+WTTCZ1VSwUMJEP2bEchw8/XDSOLOu0uIo4oAl+RDg1NaO3y7S/bd/G2koo2+a5QxrvAruNp2lX97BNpmtvdwHbpn8/no9/+fVjm0Xw3Ns7GgDPC0e7WMp6eFfXWOPZFcOZZBJ4jgROCYynr00lYdkUJDfFxNxMl6/7EwGG9yMRLdqddJLyR11G2EhdLaLRYrt1umd37K3xPP969UrWJcU+0ZCRacrm9ex0HlsSivvw3Bja3Uboz+N5t0DYZfXv0wuZqpmN+++v3W0jWiB6BeRgvG3H4PxicH3Y6vX4ctdg+XQyI7MuTQ8kaEZhhjEvlaC98HeJjix1tzssKgHK8EcTLj7Jlj1+fzZb4lmPv+ZJGiqViBb7uX5uNlkMHBaLgyxUZCQJws9XDLbdihP7Z5ti/45XP/Hozshyxyg3FzvImX69hdd9y/ydu/ZpPux2DRty1zTXJ9Z4mqZptZ7hDQNPm23QymNquXC5Ehk2UWZZZBxljlqT1uhqxKvI3rAujWJMRNDdEbdPVWW2VOL5OJ/wZEupShxRopbyVDKG3C1HJ/V6bhadTR6crP5xor5bMaImW/3tqqh4jNzBw/a83tRDbKeeW1x2ctHOMqG9LZnurF/fM5+K469i7K5qc2WEePwFT+LzmzHcig0BnBcPQyCPH162v78KZis8ZcQTFD1XRZ+qiCrXFDVK4eaMVHWH59xY0HWYZ1WYc5KPcvGlYDSK7MMnNE1zlRAokgE2nU35CyRR1bLBBQi/b88ce1ZzDECa3sdv12oksJkW7Iodbh37t1DZdnjzqXed3kRm17TxsG3EJp47NKeJ8gpPUz5Dbt+OiHCyIuIte47Q4QM0NA+vF41ZsAZfp4yAz6vTNEMriuKO0T4frc+9sSqnh8O6ex72IjEtdY4LdOMeQFdKpUqeikxl4sES7LPb5T3o6XkefX26dLX47pwU9d9++zV5GYg3a1rrCUK5boA1p007Skec/4EhmmsXL/Pi93/SRN08uScj3t4yh41rCmQN70qacHDgcIagkYPgsHVdBBsXV+mM7OakSMTtJkUmDFXnYpI3PBfGMYnxUQotarXUaCxx4QzCZtyTirsSiWDJSNRK0Vk/6q/FWb5UNkRVRlxFhf/hodMOV7BfPEF82/oVMVQI7vfXeJLPdC3h2fB6Zk96N4/8bhw0UwmWmWTuxdL8uz+F5zbhsD7ZLGD5i33iYaN7t9tBwfdaxf5sNqmnKyzLurmA5g27vbSqwj59XrcekaqCzqheWKcYcMloNSn2ZshxnCpX4q5yPBnQFFfCJ9f89X6jEI2XqYyrm/HRqiiXS/V+79xuwa/71rIa1T1xRvDcKCOB6BrPtZneetVm7cIIQ/jDZeJ5n32an5H5gMMOsHcLOGKUv8Jz062TV7SGE8t5Ah1sq1XM+YOFUlKe06IUE9FlMj4lIPtUjYlwuhQbVyO0DHhpjYqzzFxn6JsRbHjOdBOZbpkCP0/JbrXmz00anSjvQulqdH0M/pFEsN8+Pf7sSwLkj/ilQO7HTZe7Xr/guTrs2IRpe7ew3OK5ZSj347m77iZ905DJ7V08zbW5fceAVyQkgZNEsP19iBTO9pzOw97xAHhCxJnKZiShyenTkaC6Aj4kn7niY7xeLjIcjgVJkmCvmWiWTxpSczpmvHMjno5znFs0aJKjxKy/k65ks7yLhGBZFANU0hPtty8cjs/wezciKRyxoDp5HOFmH4/4YA8OMHRBGDKt8V773DHWf3XtRIEtNgbXuyMj5IBf8Dw4eByiDwyC0WWfEjwL6XSpG2si6Tx7U1V5mYaj6yJMj1nhWRWEGMGzU/K4kkJ1Oqa94STKI8CsdmkO4VbN+gseVzpddiUzrBwQRZSvtehkceFw2rfw3IykD56Agxzs4xp4YqIKjuvxFcW9TTP/bjoh7+hePHaS0f1WbZ5vkf1k3dcEb/3wo+saiaD5GOkxrT8ehx70oOOsp+JxPkmHuTB685uboa7owvSm2URjKVSr1Zub6XQqeYWl12ekc/6USEvCtBpjFDkQkYSwlgFRjxI/AKsOAMm5TxZ9PJJV4apx3XM6PrPAHr8GuwTwiGccHJDE/tiP6Dmf2LdYn9jH5qWvPiO6UawtFn919ueN0MTwr65dJzCJkl9XVWbHvHmlv3jb/grPM+DZInPLTiqddWVURg8zMWE8HkZoaTmaNpdjgWsOlwTh0TTGLas+JZmd+bOKL9acViVGEX0xiWMUthvQdDfN8pQoy7I2V8FLVTxRcPyNdn5w6viMSMqI1gRrH9ACSHSb2GZjxZ+vnzj48VvrZxZAbCbMR26rkt+B8x4D/fPrd6PsbkO/jeeajSeBnwQuEkifsBLy57h12WgUo6l+Q0PZvoqWCj0eL4ejplAdT4Xw8E1ufjOVmsPpTbWaTFRSnWBaVoTmqKnTSmw5jmko+tUKL7iNcjrBIhXJc5ViWU+2MfP4c+D5F6cXkEBZ0DJYz748s0KOa7GcQTJhsRMGHy8GoBLh08GXJMOvZJMEJ8TTO293N17+axF01/dNPM1IYOJp/u5NPt1HbY1GDwYKPI9DDbIKpWhBE5kwx6H3SbBc2KcLy+pwCCMcvaoDz9j4jRvgmUmiVue7FCUJoxgK++p4LPhUomvwVSWNTciqqMiaT6GSGI8UJ/4O8OxcDUJoahGqQS3bLdaDH61nDhz3HRfn5+Dw96zgTCxPkLUuRA8Q23FmjmJ/xxr/EZz3fxhb+WdnkmLeXg3Z4enf2olZAluCZ2hQqBcbk0IwlQ34CBEnhb2pChpJMQzrnCIPTW/08M1UePIFpKkla5R5XvYZmUhsJDG6NBwNl14aeFaHoyVqLMbNoOwMyHK55sm2F/0+8Cz5becQWgBOx9neid2K5GN1OgHoxakN6+jYfnEM1T3q+3UZBUbh2/0nzCj6FwD6S08Gnr9rqXfro52fw4F0eFYSsZwhJ96QlbBKg17uql/31yqpEqsOx+NmkzJclVo6K7ub1en0Ziw1qxHu1Wlk+OKbb1aXRobPaKqPpqvLZbOpI9jGlsNmxOtzczGdMpJ0zCd6vYyqJSud2aTRXkBi36n3BhfHIcJVh0IhAGm5CB22wGNd5rGd4dp2jqGVfQ9paUOUIApcWKwoBu6Uz3/e6O61OzOR/L5Zrn39Fs+dpL6Np8WCWHX6ndNxhPdw5jjptRoT8BZ8IhU0xOloPK4meKM8K/gpd7M5vXm2impeirww1YcvvvzyW0ujy8te2qdzo2FsPHWLaOKFN6dVNxDWma6rQjdFBX0qo2TKk+Jsck34qly/Z8NekOtLYGg7hSnuDXqYg7Yh02u1MBUdYLhkx5aHH1fN54HzGDbrsKDGf/TPoLkTY+8JAmsq+B48d4v7zenu7zA3GBFfX/d3+ySWIYJhUDa4zC8ahTTazITLpb346vSFl2mxHgVPpIWlmI97cww8h6PRdDoa3rwgxGQDo7qwHokg39/coJ1XvdzNCFWArnM0mnVFkCEcSRgYhhb6vesihkhQkYUGl+1i8fveYEXdnYO3HrQm+W96PaijWofnDiJ4Pj8mg+XHbQ/f+eTIiYQF7yfv6p+vv5u/zGaJHMzTDf+50mDvH6w96gmrBSU2GeUer/GsoUHsJssB4PnmS15fvw6KQwlLkhhe4TmaArzp+OZVQQqwta4aRvU+Ht0QPEWR0afAM8JxETpd8mtAvFwud9mkq5AbkD0LvVMbrDO/QNLrDex2hJhzWw+a3H7+aDD4/NLWOzy3IxqcHh1bCZ6HH07eP3d8e7aF578JqgmnidQ9y9w9aJ6ZVSdJ6mbHvNFqWK29h8XLUAsiJRCYNdZVSXa7yrNvvvTSawpV4dEyanBhTzIiSU2EStp9gyoUCZ1iM2rEjQOI+punp+PRCEBXmzEpmYzojJFOlVwJYuz8LN/qHcPuLmyt8/PLdj3XWGCqDMrahlFo+/tKup/v2b75/PNe78E3IVit8/QERf2ZJXT+wOZ0fHVApOhbeWDFkf6N9feLKxNPrF8ozlVwxgUpRDbsMdoTotbY2wM7d2m7bhcXoINLlWwpabDi6OblF19QZJ5M4wKy6uMzOkcQXTLu0bgZ4xgaI2Rl7mVIvTR9+oWb6Wg0GsdisQgnK+6wj8X82IWVLMdn7etWKIQdCnDuw+t2p0/mdXms697hw2K/kp4tekdHhy34u+3UYXeehICn1Q7XH5wjFO2v8TRbSlI6/8mKybz314qrXU9AZ7a54fzKcrA6gZsDzk0Zsu739p3O1YYXOPpi0Z/lcewv0CMZmugLS9MqEjTvkn3hZJoKZCgmrKuyAO4Y3bsXZVKpZlCaBn4OpekLL7384nTIzTUftA6joaahKaIyfLzgr6VTpDHCeO8IGT131cpHJ5fIP/1UulI/bECQi2dMBrDe0OF1/sGp03Fy4bTDPn/4xm5BTPj61hpxspugd+/8J0W+CfCX1o0QY011YhEo9/d/RHN08BUZ5rQOscG62ChAzkEk8bbrfDmpqG4OgdA9zxhUQJHBwrGU6GMCRhhUPRhRaOtcWQ8rK3RY0aaj6ujm1TeHVTedECVGrY71MGKpSHnSBT/vAdU0KaIWOiLynfZlMXgFyR2R4qZyh4vvOwXI7xuYzjudCKLnwPPiwm45OPiahPWvPgPVTGxgN8IBqntPtte/hqcp87wtxVbtG9hwEjbhUU8cfPYZ8MwvrvrY8VLylIL1WSN/fllMezTV7faGwzEhEECYpEF84kRT2YSoBgKy4tNkPp6uQG3DNZXAzSgmNKevVgVGSWtVRkNv2mQiY28gG+wEk3wa06NcsW375ughzHTRT3fg7I3Gdbvw/WE+Xyeq0u+vQ0DP6bA4CZ4nZ1a0Rdj5gF7JnCDcX3+Sv/6v1y29tcV/YK0HNdhCCPrB+uVXD46cID2vr/PQxtYSlXi0cJWbwEc9GBNBiihQBpgmVmb06ljUwGMS0RKkIDJ6H55wUJh36qIWiY2XwhD9E+fW3ONIGK3SuCkIAVen7vHwZZcntdpRk2sgbhYxTKrBIouL1mUul5vVg35E1En+/Jgoy/dOTp0oha1Q4mEvBDaSfIY4T5ZpZ+tUYD6Qt/d/rF2Vkokn/B3b18/2rNbPjt8/dJ5ckAqwOKlXWJfLH119IYDHE2Y0RqgOK2k/ZB4iM5/e+JSMwWa6zFyTu2xAX5Y9CdYtsiK0YLQbobY6RFkv6cJQkOZuVKRVyajlci7eU85UiIUWaiXwn40c5ku4WSvmwb3Mov5gNnjR60za34QQytd4fgvXOcPDtycXJ3ZY5wZPUq1gAcjth0f+Rzw3H+GG6iL5HNmdHFbzDUKEOIjya2A7vF7kSpUEz0PQlYLw0GA1VX/m1emIoRNlT1LRMWQPUAlWS/LJZED06hIkSwqoDzAeCh/3hiU3OD1vuDq8aYZjTUKWLDkmWSvzhWAlo8SxtbMQLLsSFU/XUBXZBXyhXr6cTCaLdhbWWpjkByHbg4c2kIZ2x7cYDIIzWe0sIWsPQoe77R05udfNcf+PIwCetXP9p/BcXa0eN1w4+bxxSqaZeK32FZ7Xl+1cquLiK8Azm4inCaMeee5FzIXCmUQtqenNsFcGnkqSd7lkZQ4RQ5j20T7CJWnxGgR3YV9kSdPj4atNRlq++mq1CgIPP+maRXlDjBcInq5E18WzMq0GXNmSn+DZnzTyl9laNoWMPwg9OPz8AYQje8Bz/8D6Jcntlm/XelGTDdlOPbsN4+5f3b92nvGH3ecWoFjmJmyycNhzgtsBi3wSOu/10Anm+/UChMe1UhoqBO+8ip7nhaGAapNh2LgvJgiQeAYC3ghDs+A7UbQzmB03pbmXDsgGC0mT7EoauKOrjFdRdGR9OVmmApzbF8i4+EQ5V/BEG1nYKJtMecpp/7uvD65nV/3+BB9h0FO/vEC3dOHAFAsvC9MXyxle3enR6YX1x8fOMCFZIbqu9+43p+1vlPgHgfWe4b7p7xtvX4tAVqQ4pjR7TsKH7zmOzyF2hQS2PbmaFeCXeHPlLoPEPgbp1qxWw25OTHoFQSBcpuJGW6T5MPrAhEhERSVxc68ookOlYyqVTHq9bhoVVCBAq5SRcJHQwIhUtxwv8/VoJTjLYkNDspytxUuFL94ftHJXs06HqHBTV6v8fuwkeFqtcHgLkYb/cHp8gle5R/A02fpbc9pMRMyFG5vD+tQE+XfN78/BvlF+mYzVLz0mmW5YrfAnu3PPAir32Hb4EFQZuB9I4vq568tiv1AxVJ+icl4xho5HqL465kAnV2mVFcPCeNmUYpDU+UC6SxG31IxFIhGfGvDGkLw8GlVxQSyacZX5uL9fVmDMLJssp4O1mj+bqKSj9dxVKZVO+a8ap8e2Vhtqe1RTi2Lu/cOjU2wJJXiC9LISDQnOTk7sdnzg6JCQRe+p3gl0t2iaV3fiAjn8Dm/3p/BcAWh+SgTKjWyB4Gm1kFxqsSN6np6fk5lRu73A27pCTMvV4xlCakLS7Y5UxxHhhrh9JJLpJhRCdMA60aP7KFkmzxA4t85hNK9CGEZRcxXNvqxU4rJR8U88XW9YVL1aN5gLliouyuWJQmbn90OIU8hdt75r5b/PReHujUnnncNvji6cF+A+wJGQcgnpyPLlZ5/ZIWduOVb7atceZpZ/u+sOsrdobdvn32P1TTzJSGuzk2PzXUpPkDE3TMARgnlanGQ5sDfzEni2i423321d1mc5T0ZU516VDtOqsAxzYOfGSy+TrvGaNKeTrjGIZglSG6wAE5kzbh1pXpMZLjxfuhF8k4lgyitiX1cwHtbDetVLFS7rfhZEfrzQmPQ7nULJ469//wHwzOcXxauryZV/cmj75hizefIRnwBM677FQTx/7xjbbZxnxDpXMoffwZPc3A2ud1P93y9XgaEprdq4+npn5j46D1iBA85OmE8yazi5aBQhz575U1nsfOuUMqIocnM0jDS0NrKscFDP0j4oQAOajxEV4a0Xnn5DV8u8JkIlFna73UBWC8gUZnFyrVZzGQbbTXhSn3pcKsb14EeocimrBShX2g8wrzqltGfWvsqBW1ojWiz2F58/+O709PQI7DEpQ8+PP9sDnJjbnXz0sLd3QMTLq3XHGjdr994Gtu20ZN7ejqu/j++2XQNPaDtNMFeHzU5XzLt/RE2H+HRm2SN4njkcs/7lh+9Ha5VKMNooeAJeUYF6gYn4AqKaTJTpkRT2YepLywky85See+m1lwUmHdREWHAYPX5AAy2SLLPka5dKQRflEz21dDpb7ipqLMYJY532eEQtA3k9GqN+p+bxTFp1DD6Oe5dYIOdhhIcPzs9tDz75/OGJ3Xl8aHPakd+feOLLk08Oj76Ec5nGcQ+eZg7Cf7dQmHCatb9544+7AbPUvFsvrSklrM3X05xZcCQKa6B5Ehpc96BOJJOIdh0btdAVlniVEzVIZlRXHDuJPGWNDutg58ZLKcJJws2zLz7dXFZjERiq5HUjG8ks7R5KxO0VABrHxLObYcFH8eiEMvR8qYPDL0NnG/Bgt0ivlaolK9nid/m8beDoFbF9JDhpFK/PH7SOBu+/M3v/AViT1vfF65YtdGyB8NxhP9tH3byeyd3qgX8bWBMD8zm3trb1k3dLyfvtc4dfWuOJx00yInhagCexUuB5chGyXbZA5X4Ivg7+Ho32rwrAM6IAT12spFO5SY0XacbNeZnpENym0Hz1xZdewCEm0b6q4NVjksRmQMsLUH/7FNZIAE/sNkY/wJYJ+al6q24lzJXj8bLmmfRzp4OsJ8NnP7A9vF7hOStkU/0J6JAe8Pzg+/oHwLN32Cii3D8PkY335JtZsfDNBCZE9+NpQmX2o1t4rq+3QP8jPH+Vj34xUoAJNcAvew1Q1n1NFC5n2Js5eLBodBaHxxdQI/eD0Y4r6all492AipRSg+CIDfAVIyDQRliQIFMEz0H0N02Bg3JhyPjC3FDQY03dzWoRr4ins5lEnHdlWIPivEZANcpRv0yB62ewv9Ng5QwCaA5as5K/sHhYShWL7Qb4kDyKi6v+O/2cbQDG6er7hz0wJ9EyX2nb8vkLJzpQq+UA1rmqmL6E7+OwDeK23W0b6J1stB0rzeS1FWP/IIxC7PnYNp6PmxZK1BfAE7Niy/kAtHy/0LadOG3fPVzUZ7NKGQL4sqH4jK4/ZSQTlJZwZWTJq3HYdACaQ8K0YzqNScCzWlWVOTcEtM1IhNI41KMU/jfiLoMCleyGDlc0XLWKEgCLQkNAigjAEpEtqZZKEC6mUv3+FfilWR7BBtLG3FXP9vCjTh+eXojmChXsZH64aCPGOu2fgQTdtEZff41++bcWwfDO9eaeeWI+4c49cr5rq3fj7No+IaA0hWebRQh5jBHs6OGJqdoeFL/HtP3Q/u0B6tEL0J7+ILI0FfAxinbVycZByyEfUe5YdTSFNJnRhy+/elMF7YEwGnFnEpwE4WIMEzpZY1iPGmMNlEkU7UU3pWoJQ2YNiPK8rObTqC40TSoA5VnWVfbwyWwhmw1mXaV+Ltf4ntDY/VkduxQfTorI+Nl0v55NleoIt++9/eH1gx9OsM97y+DuwdP0afOCLPNkO2eZ+N3ft/4KYdinOWE1fynwJKzNRrF+HLpcYPOAzXKAEh945oupGmwrINNzn68WT2YoGdoOVJ0MZIgaIiLXxGhz2dR14AmmU4bGQQjP3dWxokJEF0ZEjXCgnuaMzuk0yCiZEppusmgxk9EC8HcX4mgykUh2a8FgMJsK1r+fTBqNq1yjPyNU08M8NCSTSdQ/K2AXWQnx3N9ZfIjS1PGZKfTH+tP2iWWemH+/Oe4kcXOZcXaXn9/Cew3oE8iXdtJ0QkG7Z3+Q/7jw/U9fw/u/dlwc5RtxnqVUhUJl7sUJGnbKMArBRAByby1gQI0ogY9DKI3EgCdDT6fPxlQf9m+qkHxixdBzvvDKk7qPCVc5F6to2nAUfv6pKrZ6GRmwpjDMcjSaMUD4x1PRaK1UXKCJgITiClOkYjuXy7dzHX+hk8NMqcSzlVoqlfI32tcPH4ZOVtpfVKE7OJrgbdmUebFtaTg1Uf3dIv83w+mtVtksFNZqIPg7UVwSF3eeDz75/DunBfyD9Xhw3bgqZTHCCCiqGp5jhRkVTEea12iuOo3ouMtAT0tKeAgWwihEh6MbSSXSUIjoOSEWA9xPPv3UWxLn9c4ZlsyYhaXEwT59WjzOdsGFIOHFRYqP1zwVsMouT7DeKGIyUO+QthObkaEQDxbqBeyxT/PIYzBnT6nezs2+C62+cIjkge2aejevb7s5+XNbDGzOd1sq09q20DTvmBDfxfORDZ4//kzbtXilVcfxOp4GTuRNDPGVyh7N0ZY25bY2nVa2We1Rs9Vqzc0bE1gNuHAhLl7YWjaQhwKWk3gYtZGB88Ep/Of6/MbMNqLsnPp5dgTleubH7/0+Pt/P98uN6wO7Lw5dRwIKOEF93h2fuLQb+sAbd3++D1+KQaEY7fW5wbsR6ohwcWhqMC7dYCEdIFZKE2bUaC0BV8Ierwe9oaJBw5o4PzryQVdxva8AsgSvdPq8QByJVRAqcR+dmM1bsLkhEktaWA+kDmELz6OMSk0tozrKYrfYbCYzv5zdwCaNBxsguaYwu4TlLpBD2KbuzUyfu0BmFsgU8jagT8C6faduPa4Y43bitAXSNoGyHZCeRG/bfzyJ59YrttDGZPvANZIbn/98aIgsoQNhi0Lpm0m4gKvv/7q0vJbNLq9NZywpB8eGGB8WWHCuUCRuzBm0g4yRBpBgQJzOYLqbYj20FU0iYG4vECEjTHhTvb6uU2uKIE/qFXiQZkslAAuBHR3LW7CCIMD5kO6neJ/bMmuDddosGP2YBumKUmoWt0YCQ4rwmPNLswnMNKdStmSUEDNmWyaT7bkw+fVQpeLc0fmTfe7k/DW/UgtPfJC/5PUR4ElC++VbN4bQ7dj9iP8+NE4q5m9+fri0jORldX4qkXQkONbFuGNhGr4ReJbbhgdZzkyD2WThA0LUYLHFQ0OkuIKGsn2FokQFgqdmc1Nt0BRDBM+27uECQhU09phEMGOGNpqnkaPGgSckyxGMMSDBtNDm6CIkjEh6CZ4ZCPlmUFJU8AyHU1BSgObiI+HMwvFbFyau7RjPLXL0KYOsfbbtdKszVYXnttMkaRKpkS59c2acVG4jZ1CDTFy8MXlh4hLKzvFb58/98vNDIs9MYAphI0HHYwzmieAYV5BmRs1QcHvZTXXZhTvXapdTwk6tti3oZPyE/0xvGvrWg6FyMOChXbjj7SoJY9J0r1OA3gh36/RB8WiJ5qOI7RaE9qlpo5GjYZ2Li7BPGyiYaNxDo0dviabCNvD4kEfevz/31smwLZNYnffQcykL77Q53nnl9vuQLW6N7u7Y6mrzUbVBfrb6CfCsPHucxhM8d39287Pnfhy4fh1Uzji6snfR6ka5NHn+u3d//Q6ZaDY755iFa/PwPEghfyBYyGFiBjSIk2cVm7qQ1Wtkg3aKkqmHtW0hE2qlkN++smkoFuz2lSDGERg/KUjlQVamK5aNcJ9sAP4BSEag/Ia4MYkHCYfTiVIKbhEukk7CKuM0On3hRBJ7seAVLOEEljeNjZ1NoGc3v4CxRcCOEbyPssvnL1bcZ2Xl+P9yagO9vU1+60Fllhcb6Q6duQC3CVYMfhMLe8/c38iia5vA3GXKhjEuH7KhYhH3u9Fu8jF+exB1ZVG9uenlwM257ALhD98OatvWyZhHIUR0Icp6SG2DENa6PWBG1zVUENJmv5lDDlVi7OxchM8nTQGTxxGGodI05meisEcSyrFXzJZyeuNm3gJrXZyl4QoyUzO//vTa7Z6e1WUocjIpOo8Cdv7ed0sz9y5cr8C4Yzxr3+nbFlvxhVVXPW3SZAflU1DDTisr/m5dwLb4SbhN0lS4dP63JSgYYDGQb0XQbY97eRrjWUYzjyhtYpGiW3ObbRDNh8Av5VZUkh++1WpEdr9IvZLGYMKmTioWiXQhJ4uBY6pfVaaCTDrtKsW9Vnu/RKJ6AXJHC25ZvwlyCA+M000DTnJQH1nQovY4wZ7CYpOZMNqlMczUEtXP6munV5eRNjksSZSeYzP3spmem6Tq/BOej1H4D2wVP+IfPO0zW64DD/44pP8Ovg6c7Tg2zExc2kUKI6JzXZtG6m6xxeKLZKDNMR3Jcx7YkQdj7YzfnysatMjdN6HlXtlsb/nhTrdIUHLq2qTiIO53nVCgNqjtSERzUqlYYsU9XihY2ZjTSjUK5KoPDuNnRjBFZyW0H3gqzofBWAeWDzmwHxh/Rt7DmjyefXuOHt0TCLB0mJCwqEBPv/1pz/IaiVKOqZnU0Z7DkeN3d8F9/dk+nsiZ/tF0n/2H53973ePN50+MRwJMMHVY4EkklhMTE1fR9sKaPhQgU4vwbVAq+KYS+WiEMO9QevGYwxJLQ1ajH93NMiaQOlfsaaRI3dpuEcX67GUrInwZ/rPYt16kVPZQTlpIB535GAYVTZyZCwpk7S1E7uSjQS4VykwJWmY0PvOzs4kIjTZ0byQas8EH8MdSeXckiRSAN5lTs8jol5Z6jn88M3X//traMkpPRzI5NutYO39ooO7fOM//0s8Cz8fDz4/f/+UPPIfQnkEH6drQ1QkMWlz95dx3Z27BDMKWPKaqpxKghKMxUwBpEgeX2ChCWEf9aXSRWrKYQ+M9pDN0i+QmPsAEgrJirlODJmghXWhMh8RSIWWlM+EUY0XVagwKdR2euBupAsosz2YxyGAGxIvxOSyzBeXU2rXPjIQ95QjnD0zbEPLjcUsM2ebUA4jHbve8goT0159GsYd9GsS0JZOZXXv762v/Mhb9V3BW4vuuQ2C3KpGI+M5K9x2Dmt9cGoEc8PonpBn7/sPby9C4bWBnoo0u+S1xhiml+0Qig1rdqVe0qtuKdo9E1ycsFoPS7j7QyOm0vVwIMWR0LhAA69wv9DOIU0a3FaZLZLRgTGNo6JlC632ddqvPi9d4aa9xhcmEQa14PTHLjA2aPSMXycd56PSgH+193WHOR3k+nzdjxQPaBBvZhez89OzcHPRU0P+8Ofru7dX5h+NY67B9aiSj+OK/t866neI5gFdu4Yl/4OehWxq/OkIOVs9evfnT6VVs7Hu02dPm9gdjNHxcUafTdUOc3KBSaLSFnFMi62soFq0qzTqhldNWF9ZYlUr2HBJ2v1UqALZskKP9wDPAcmTMMBr1OFmMIwnLGPfGq7xuI5srJbC2wYieSGTKwvE0543kUSHw6JN2mT8O0wTPWD6PUR1seFhYIMsIFjOZxemFOeTzH14evbd0/urIE3jiowY0O0HpcS6704uAJ9J4ouPfiuw4qIaujt8YQmAn45PYH/DNudvTG9AQIV9y2HhTLm3keCN8pbBBoUwLRDLZe3cwgn2qo1WhlIBdYoykX8Q7QSLnkOpD+F0UFqgS6woGGGRHFNTeQey0Qw3Fo6o0d3WkVxiT2Y06NeQ6COuL8fCmPJ1y5vws2qFciOXDs3H6xfgJszGOHD8ezdiQrprpiG0Gpe/GfNbmWJizvPnWq5+Onn53EoqrnVpcjUBVFcXqalxTG8+t9zYhvnMI60Egs5lAQLp4CDqhu++fXkssrBE8kZjwnMuOtnvAbnfJ9R+ccokFDX3aYbBz/lalWCJBPYQcX+VyuZ0uF/L2lQLrX29spOxWBdXvkqgkEswrlSVG4GkyeSEU83BGu52BvB5xzZ4zOeNxbGyjaScf48CRut20B2tykg6wrMfejNBH30wl3bGEDTmp2ZxMTaOv/GB+DVRUuMmRWev58O3Ld78gUuUqOGobZ13VN7fNets+nwS9qvyq28YTlExlKJp0j0aGdn0BBoTIrMYnb505fxejVKeXpjNRx+z9h9jzYYuZQcGvpEP2NOtrrw8nQX8opAJhWk7p9Z1qlZL3ovMrEGLsyAkZWDC0qWMkcAxWZ4PovUGNrFHfTxXa1HI27mVXUDGhtGKdJlRIaHTmzcXNsgtOE8YXhbNlTnEesCIxAO+B+zTy7xxeGP1wMcz6pmy8GeX+7FxybmHs7KerqSh77KTD8WB5EiUdxroet5HI71ltTASLf/SdddUXPfX4L636GVy3vYWHrDzBZDk4z4s4EGZAQYDG5tQ0GJ0sXCfEhOS4+UJBUFgJtiqV+zyeUpHqVyhEnQ36/gaZRMnAQO0o3jsD7ahGQ7liUSk19FFWVmRAPa+WCeWnWLGkBVmBEWlVGWGd432wR3B8vDNdKAeBZwSJPTYT7O+FUMyI2IdclSOYNkcshz8+scf74mLYkhiL5FFHWUA+ZzcyjqaTXx6eyvYQOEeI2PIJSnkr3Nd2ntVOsrZNV57UsE+C5mNRCA6wvIH8CTkSZP9vv7uUBY5Lt5NJxwwEBegqLNL5FKmPhI26zgIlFYtVQafqjki+t0vbLRNIBH0SRaHsJ9FHO0hJ64PB3IpYLBVoCoBN092Go2mkjsW69r18sMPrdIFKAVY+LLxDmxixPgD/YOUhuUuWCNNy7B0zZ3dZjikYtsRY/TS/nzNGjjZf4eOg7m5/mOzlYbMxmgYd+9Gnx195dXWtZ3Li0AjuMbKs5V/H5rr/IBfF/f5ojcTIdThOgukAVDZkC//nd8dBci6CIiPrfRJTELU/gMI7D/bWbLNxzLpQoNerDS3t1iLqSpVSvQkJHVnx6So3UqoGnXZYIxJbWa6rUSYRNyLDpzpFQFOjETUq65X1e1tdEom4v1Am5BKL7N0NxS2PuQVQ+izr9TD+DkZff5DjsDO0qz1gIltyoCOh85EDzb2P2vfZ+WTkRNOVl/ZfacaSsdQ7Z8/2jF74/utP8MsMIJ/HnsttjQbO38USfKWGT6hxaidjyOfR0xjZNXStsuaEFEa7xycu3jxzc/LBjIMcoonBPNwGFFqgGs2oLW2OmK8gwBl8bz+bE/1wZ1BIbRbJmGvO6G1XdXdKRd3a4cFuyu99uVmnVSgRgYR9DQ1tbQaNRi1rbNBLAy5KIJTLKbsTa6+QkIJF8brjKITYVqVE4g/Ut+97QaSXBDgOfdAWFiw/HXMk+agDa91wsG59ZiF54PDZAy+1XtmTtOSjB06c7Dk9tOuTaxVxdd3uL6rwrGGqtaLSDvCsftkzZBsP8ETfbdej/wnUIJ/3fDr6Gyb9wNbaoO2meVt4NgESN+Mwx0CD9Iu8Pky7S7QanYCSUfsVMp0s2J6miiGWgd7GJR9uqBfrNW13dEcOGq8cUOY8nELVrx5u0wxr1aJGAUWVGQYzXwLR3i7su4At2pn2XL9c7NpPh5vE8s7OxviiiTnSIaMw080GQO77lS828+4w+dMebUrZ0Pk08yfHmvYcOXZgT/O+g7w7mvnw+DujlzEoP4LkmagZkLJU21oF2Lpasab6U22Lxqm2YeAJP/PctZGBCp5wnVik9n5Pz+17o6d/WwD1CKLM7UtiCxrsNBymIe4wFjRG5xzwHESUlmoaulob+kT+Uq5cDCElB57pYWG7Sq7W3DEc6TK9/Dzj8nFKsV7XrTFou1FB6fVlO1p4plJ/3979TKmDA54KhUCgl4s79jVFxHq1rO8Y8Dz4gUhuZcH92YOsQnyk+aCHjHsk3jyRSsVhoO43gGfXkaY9zS9CD5kPX371y9FzEIYOEDwPEd9VHXSAAD6eArQ6OaqOSH/1lUoOVYUnYBxAfUTOrgHIliZuXsauKkQfKC6zU3OYCQJfbnE45sLgwyBjMFJqrZe3BiXC4Ta1UKgTdnTslYpMHOKHpL8D85usQtbf2t8gFGo1ekWHUl5imZBO06bcLxELBJRU0mhQtLjNtl7PC+0NDUzL3g9OKVSdOq2hn9JLVYr6D5TvGfRWWLpAKFTur1cxzIqLVhiULfUKsaJrz9jxV482F9PG1Oorh5si3L6jR1oPzPW+fLD39Z7Ry+cwf0iQxLt/4lS14ismhc/VeO4kgyLfqr6i2j6fQ70JwyR4Xh8Z+Ormw4cY2yJnGbuiIbMOp/DhmHOkojGf70VTQaTmPNagtKHboG5sFPaDWZMIQLEzQalQcYo1KVQ6gVKA0n64TaA8pRQzkM/rZDJJq0Qi7++Xijs1KoXX03sQtVRfo0LRDpwEIuApVkmUcJ0tkmFdwVXyK+QUpehQ4PKQ36cQSdolKpB6vSfPjh3oxbqM5185PGaLuM1NR1qfP9F75Ujz4eOjb+N+x7vcDCAY7KoA+nTRiM9VeFZ87J8Aqqu6l6vqgoqlV6Nc4eef+52v6/BtnI7CgAS28YhnbMcZTpwdk9UkTtIll7Sl6b7m2tBy3JUl9rFX2a3KXlcBZRzzEBQhkJgC8cfxmQJuCuV3Vc6R25Pu6/P7vve99/vl2qv+JCIv8eB8jvdmv/kMMxff73yAzVsXnkHr6/uDi2/AYJSb6WwpQjcDjz1GPU6zlpDS9XT8gVeefCrxONS7YnV6PU5mUjpMklRvt2eQzYAZ+eITkfCklCWZwI4nVTvF8JxEGylWEIokMmqswzBGysxWk33WCvCCRX/xuIRlUpQiRuO3Pfnw4zzPk6xFB+KVbGKkKstyPz+9dfHihT3wUSav5YLB4Nrc3OydL6GkuxEGDnqK3qi176sfZaUBOP0EgIt/YP83ngPIDf5CjvvzXsv6cCQZYwwfncdE9/cYFfj8A4y4vPv+T+96e7e+fhPFi0h2u33q22LbfPixiBTgnJZtP5l9+DEY9a++/sRTdMt13aYiMKygO2TMIElS5DnlsbsI0ioqhEUSlKS0vR+KEXykHWPbqkqzqm53ejRgLUp9iWNpKYBS/3Eg3+SepyiAGk13OZEstmmWJgKSmcxkCma0m7wtNJW7ODU1oeVD46FcLdhYGx6dxRGaqzj75pfz19yHGvpIyB3DE+svlP/G0r/v3z7OPofx7S9f2B/z57G8gy3gzV177f3bs1+hifnTZzhR/l00FEbnnnkR3RkcCfCKI5iUHPjiO0emstmnE6/QMaMDazeRfvvBB15/Nq4bPbdTbKYs3rFJxRK6FEiGpJ94+8cfaVEhnJgtyTwt6LbrsmKaU4uKqluMauzu7rYRfS2aowWWjFK/faGYXVNiv334eamboChKphGaJE23mzJPFN5OxOOJwrM4XCiX0xrlmfGNyTcu3v3Wwlp9dBHr7Ombrl5ZR73secoDUXQUXf8Z9y+8lxOk0lEk/W/3I3ZQL3n3/9xP7H2+8PmlH97Fk/4rDj7BRo6fDvYuFrJ33+2N0z2HMKNbLPf8YxwnSbBBUjHbNvuVaq2UTSdKCRbvjWKTYUghxnjG3V1vFyKBdkBuWRwHwIhitJvmoEBd17IIwRZY1VOjumEYNAGyEjnSYmklIFGmpGQT7HePc8Vk0JSJphRQQFY8JycSiZG3sn1KotJ5LbS8tbE1Mzk3NnZh75G9yQ1UTKurSyvzS3jxxsB9jvcRHXxS/Sg7Hr0DmtVPrP91+994eveQbTw8MQCy8v77737+1dmlne8xv3rwzNePcs1sAQei3X13kWX0XV156jae4D8p0mrM1nVRCgcbSSpTKqUJFrFXbFqWpcZaKTThbr9wT4ZqOpahQn+SbEDuJqoSkivwFBDaMUw1GDFGUGFJIyw5jiNYlha5aCSitDMF9ruHimxlIhpIkfGIyeH38jqVyY8Ec9k+Kqt4PteoTw5Njg/dNDr2zIsv1s8988bn8zffcP+Xv66fwukX3sTA4W6kYwoHf07Ac4BqBgjHh9MP5pPqzb9Kd6B5xx233HA1jM/59dn1U/Pr2AT3DKZaL9796NPdzN4b94xUApzbu2QUszX89ziOtN3d/X2QOE8p6LXHBNJRi1xNU4q6mkKC7L768BNPvxKPRyKk1ZIrvNrpAFGyA1kgcESM5dgUI7huT+DFIklaDtnG8y5ypgyfirYCVLYvEy3eVDiBpgrBOCXJiNCHny5kRvKyWQqVJ8cK6akyNiaMDm1sXXzjtYPP6vWDsdGdnTN7i0sv34QtczjxcnBr3PGguxJrMCUeD9RjWgvLTw0n43mFh+ct1x7i+dKps7PzK2fXX/MOM7+AA/+ee7Jb2Hvk4tsVWXHdfYNI1+56RZKBZ29/d9e1WYJrEzasI8DGm8GcIgKwdiCekM0nb/853u9GiqzBF9p6bxcJkO3YumDxRIyWyUM8U8CTIAUG9hQr8IopkyQM1aaS7Su0Tii8yBJUKZcAnpwCPJOFakXqbk6dGR5Kx3Mz6+uLc+fWFrQGPrJio7xWHvpmeO3j0RXvCLGPbgWeg3B60XUkg3pwDuDpc/thKPsyyUdz8Bb+Oq4//8L7FmzjgZH88v3zi+dml5a2MeLy8bOZ7Nv3PPdkJT/y9cW3n5DgYwqCapa6kWWtTaudXYtm6EARAPV2myKputmpQhYXjgBhUyWb8W6XCoht0fr990v7u73e77//3rFiLsNgbImWCMa7cG38myzNkjyJ9xIUUopUFEWP0QwsANXRUyDzqmbylBlPlPKb1XzULFSX60M7o+O5fG1qfHx4djwXbByU35kazzUm1y6+dff7n5fLY+fm77/3r7MXB20PX9hfeRRHfx1NtX+TzoAGGNRXg3geHkmMdQ3wvPea6+9cPbs+ur60csr7xKdSJnHb148+7eEJvUQFdNVhiW6hG5kJtukWeASSiOIEr98hikWhE9nsRkjaYhglmik5BFQPiIhsspcuXdrfB54YcRCMnsDoUFOUSIsiMqitpxiWRRB6Pr8s8UWdVppF5FQmFTMYwqHBYoWK3KSiVKSSr1YqkUhhZHltY/jcxPhmcCoUnDw3HmpMl995ZGJmfHpt742Lb772w4vln86d/eiWK4+rcHz5F8BzcP0TwD5qx+PXlwQnxKenfL0Pb77jmluuug5O3SrGvk4tnV8fffHCx7l80rzrqUi0Wt1889OPHzTlni1KhXQ/Eh2viaD6Ns9H4oVIW4j1eoQYIHU8tSScJdz5LRpBOcroOvhcKXZ2gSh0UYdO2b1LqtBqtZpxSclXaNqJuS0HPMTVMnGFACdZyKgpgyjGGKmYQlSaXfATckA0avEjpWQymU2WNrdy+fLYZL0xPlM1pzYmZobrWjC8Mby2sBnCjOg7ey8+8+7c6Zdu8YLlxEHDAaD+CcJ/ZBB+wg/dwfV//ud1sOlQu+OQEMx6YmjppvtXVk5vLy3ufPjOG6FaRopGstlMKT+C7Zam7BoqLZvRJ+7OFwKETosSnMw41WaFFDKBo9oM2RZpoQcbCZFm2GrMsG0WkrPX6+3uMyxiUdc7PdvuoLxMBNqoOJ0/yylCVAImhYglof1dlbRSNK0zSpMImNFoNF4BNdFgQFnbLFVHcrmgNh7Mz5TX1urlqVo67I2MBCv52xoTMxPB3PQeGp8YdZi/6cbr/i7jTy7CB5cv/f9qlpy8TujQXebZSojOe+/FfqNrcc7mDS+tLN15fmXu+wt7bzS0iixRlSrKzFKmWqNko9MzmN+kjz+NmpQYo3/r9pscFYCT2eQ73nJEjgsQ+z3DjvGKe7j4wP7+vmu7PdkUWj37Tz2/67a4LAgejCRYSBx8ICIJBGooiFfL0BHlDqOiKg1QUaTgfq6aKMLF46Pj2ogWqtcnxoOb+cbE9MLccCOYL2SDwWgkkS3kqw2UStrU2traD9/PrVx932H7G+vo5oN/689BiHw8Twb0P93Rv/AEC2G37ks4XG/7/Mu3vnDrS9sr8/OLn+Gp0ULTWqa6Gf4a9UgFwi+TJCyXzCS6X9+T6HoCvClRDoGakCuyjp3i1V2hLUsKuR8jSZooqjG314PAdICnkVJVoi2Suuoxmko6uiOyguUQTZlrC7DxojJDcOyfdjOKpHaTZTnPIIlna5vxbHgkj6KTM/taUAsGp6Y0LRcKhRo7s6Nn8plsMhkMZhPddAmK9MzYmfLC9AHGb3bO3grX7u+j1f3i6PJjIB3lpONMM/iNV54cnH5WAJ6A8+ZbV8+fXpqdx1FSODxuZW7ugxcxvxbWlqeDKJO1SDyZD4bGayNS01AalcTIJsSMovBNRVEdioqAk9WY1WUvqW2K4sh9IQAaop2U29u1VT0GLrIth7UsmWMEkI8Dv1n3TBOQeiBBNZmYoZuyRXC4MOwWSYi8QpMmFZCldCkUyhZGauFqnpL62VowFK5pWi0cmlpo5JZWR+vpRDwLPEvJaLoyvjC8sz43XG6M7bz77rn1m278E08ktKOCEZn0BJQGrdB/Z88r/0oIJ9WjeAGe12E+HmNf57dXV1fPb59fXVqaGx364bOD9/c+nchlMvnQzIUP0xElPTGNsCBYQ+DjmXS6SRqqGC9lMl2JbFK03ukwNOXs91zDgAhi4F0CuRRqcyOGJSgEU+RbMdftxATD5QLQR01JcFmaYVga1bnrEgTLtES5Tep6io8EeLoYzjRFMYyJ2VwtHoFWCki1cA5EPj09g7S5sDGe21oOh6uZdC0YriXjME+1mfGGtnVqffbU9g8/rOAzf7wWkr8d/Wh84uV4fPoI+nAi/Z4YoMf53bsAnndgjA7HL5xf8vBcOo1jLXZ2PDwP9j69MJUpVUMTH36Y7jbjjUYjXGMtu0VHM+kE4fT0Yj9frSZMtig7ugE8JavXQ74Ew7DIqAhEQTV6HRt4phQiRYoxA/I/Zhm7XMBhSY4Seg7LMA5Neng6jpOKiRIPzwm/MZlHpVXhmqJW31rWarBh0lkqGg4Cz6npiYWxMxPLG9OhmYkanM9CMBSsFRLNZjOMR2lk5uxZPGbDw9v3X4+W3F+4DAxvYvnE4+M5AOvfpsbA7WMP/BF3xZeql91wK2qJU/Mr29sr62e/XDk1+8E33/+688P79Q8//OaH+tZUI9RopKOg8kSGa/bclNqSsqUKKaBQohKNaYkH5bMOI8RaNNNyyJSKTEfFTSqly027A1rXVbfn6c/djrfIqH5JF1CAi2SsZ+siD/+IRDX/+yWELgu9VCy2eUWWMoVmIG0q03OhcNxE/aRtaeFCBVlzs7pWnx0dri+XNzY2EKghrTw5HgqHw/Fiv6Bp3c368Nz5m9bX78Txv38dVY0JGN8L8k+K/QfqgQg8kl//k41OaCZhHeKJTfkrS/Ozp3Cq0ezi/Pz6zvefYRvaZ/Xy3t7YZHkhpAVHql2KFaPpQpNwOzrkZLTfb7NuKhDPahoaRQ4vigShq7BDGDBKhOJlKsBZltLGsJiB1mfMcLF6iFTbpuWYhye0u9UC98BYUjwf1djvYdTBsjBigm4zZUaTiaYSl5raRi2fiILJgjNovFWBZ3ikfGZ0aLK+Vq+fOVPfWFtemByemQ4Gw0kpWdGCBW3s3OLS6cX11ZdvuOYO/0OrjwbliULdSw1H0+sJeA6UCMeAv2x1aW7x7Ojw/MpXc+/+gHFEnNrhHY+wPKNpW8tTOWQmpsW1Vb6STDMt3e65tgDQ4PpStZGRRNyhOSqZBcdasDJjwM8gCBFOkyqZhGMQaCRVNovwQdspG+IS+ZIxgBxPQPWjy0QYMSsFzKFHGZ6wbBdd0Y4YyWcKUgD2XFHgAW0hTirRWk4LVsOIRi1YnwSaG6jfzw2VJ1F6nF0cOwMrVMNXMDhdXz91enZ0cX3p9P23gpAOg+5wC4u3EfGoOhqIRD+CTzRHBnOlbwkM4Pny6vzi6Ozi+vwcHvK1g2Ekzr0L6COAUTUwaYbidVtI2WQiHmFUR/BqalZsEgQp5dMRXkQRxMH7gWsB3R6zPS+TRm4kUgRMI9jIIkGZ8EcYR0i1dCrAWoztQkvRgB49ENrWgadtuNBVDsSn9xuDRkinswHoB5kTFTmRTvRN0wyOh2vV8MTE9PTUVnnyzPLG5NjwJNbw0NDY2MKEpkF4hjdB/NNrY6Nzs8ND2/e/cD0EE9Ygx/jr+Fsf42PLj0P/7UkS9rJbX15ZHJ4/vT47+v1nG+Mbw5MHjzzyzqcQRek02CgTL6qA0HUF5DlGhYA09mNONK4KpJyR2qDsToqkwPYdmmxbugF+R6BKFU7QdbKNblFRtJhdPOoti+70knHPIzVkibZ0laEJmtVboCG9tbvvuvCdipzacmg875GEwqFJJKEdkClF+qWEmZsZyefDC1vlcnliYuzMDKrNyXJ5aGxrZnpmAXGpIXSrlRFwfXW8Pre+sbH90bW33IJC+nDP+V+NzmP6aODi8OVk6T7w7rhU8vF86abV0yun7x99F93hC2+s1ddAQVq40jeTBawExbd390EmsNBB2tGAZwHrMpUS6GjIZKEvYxZBFa2Oa9GKgqYbdGUM9TtUp80rlFIsFm0DzzZMDwHWXApGs8Cg7OEQlUieUFVCC1HN4G9Ld3WhYxQJxD76dRQSZ78fQX0E9RuRS7VSZToE3BbW1jaGxpYXFsobU1NbWzO1eDo7jbgFcSbj1ZFEJjwxdur0uXMrd6LgA56H6+ge14Eow9VRlP1L/x2+BojcV0f/XsATldFNL788dHDwGir25fLCxFSoVulTcgHeQzoSCAQuwWYDXQhOk89GVRVFOSfD+IgvR4VLv4OG2hQhIIDpPxg7k+001isKp1lJVaWqoAoK0fetANP3CBQUBAEJ9V0ky2psL69oEEdjD604y3kCrwwy8iTjDDLxw+X71RlL6epeY4HQvfau859mn30OztCR3WHybeqhYT6ft5wRO9QmjHF74CeOiTA/EIoGwxWLaOGhuykV9Ljoxw0cBoRHmHmlTwOjJTXdTVmJThrpXCIVo1yYEuhjuUS5vlEs09asE48+rDQ2DiktRyu5gmHbICyNl30xJZt1xhY2uh8vjzd3r8UHJnChsP71j3g+Je3nssyHL+eecz0417k28kNm/xzP67Otq4+7/ZPFRY5NY1xMxfCH9li6Fshms+mU09Zx7O0N976E4zTRKbHDRkTSBzBpLrcgNL9Sq/PdL/6wGk9KLTyAP46whktTbbhVEzzdAzdW14ZR/jo0zaQN31jodLBDnTfmw4NP7aTmxqnoBj1QO/WRoxXXM6mI0263ctnFJQ6+EkmUDycLgSUOer9X3a53T0oxYtXEV4tYp90P48pSLZtujJGFnZ5fvby6+t0LzPMP7GQDzB/HWp7JEp4jOAcsWsN72u/fNpGe4Xl5Pds52J2dfoDwCtQWl9PRKYosVypbCyxVKsWMgihxMMwPP3nj7rbNKOhhW1SN7w3NppT/9FXgiX/dg4oLwy+prWR7z6+7AckLeefoSHFzOGDw0OQYa5RG4Ml/hfEtw4CWV/Xh0J+HcaKs91JbqYbUUSy5wze1eHQasQyXnYoi4FSgmVLFjfFC4OKwX6+fdzdOq3XwdMYmvkDQqvdLk0qNnIk0/6TU712/ZIfYK877H168ecDzWX/th1j+HE/+uXsOniKPfZYC3L2L5z/i+X63t7bTPWl40P5lE1nCeiwaUlxFTzG3uFAuJhSXJCP84iq4vQ6HQ6UFJwv7NAFh78u3Tx2TolLTBjDFn9yyUxn4m+6waiSKFJBhkitg9JOq67od0s0UNafhLOaiimG31Djug1PvUOOfBggbvKRNtDXEhdjGGbQ6diWRLkNu5bJL02igkikjEKldjMcrpX7pg6cWyoyydJgWxltrk3pVJEqn/XPKzf3Pl5tbv3v38sUbth798o4yp+P4OKn6gwN8fmx/REng+9gena9F71Kxp3iSf876VMMj31IZNqmYC4UiTltiKZFZquQSGcXm6BB9xNX2qjI0csEmsndVC3s5wN/2HNqe3+9m0INqva0bxjCvub1NaRrjJzTCetI9GLahkpB9oqzlydDhigYV8ktD19ttb1ySyaz2Bu243OaNRqvQQcfI2y3crxWJpYAT55mNZRYuUsWL8bZnkfBT6m+PL8pWMJDhMAUa61386vFur3ra5a+zu/WXq90DWJ5XLGG8XWM6h+fPnuD579mmx/r9Ec/vtdG8fXI9wXN/9/j8tLQyWmlkKxfZclCRWoq9APMYcvEQcxqWJavJtpAWYz8tme4G9aDlkskaRdVjqCJ9zOsa/U4/QQfKTcQsXTXs0MBSxz/8uqdrpKAE4kyqQ80pppZgk3S3JBO2krLptRmce9Vy75Ed0IWmLx92i4yeUsyycgmuHCSTbyGVE/Gofrg9KnU9lWikKbs6ujOUSTdI9D39dRLS860ZQiY+7Ic1oQcvX4kdUgSkO8rubisD8ecBz3u4bmF5BGkuiD9864frv/tPaLpel5R4hGilkk6nI1ZIwU1GUk7yv5Bi2fCfJmsUcJOwGBLoDRHVSAVDJQ369GXoNVS+5U7CSLi/7HG5WmZT9g7yYZtykaHpBuZDrQmNlMplYopEItXUTZJPh47Vu5NJvaB5TZNoFCbmh7nIR3XJ27ZZqaBylIhEyunYUTQaLWYXKpFUeVIaLTcai4uLlXIsqEKmaIadNlLDs5Qj5J9QnFT7a7OD69dv+XyPV29+j8QNA73F5aHx8TSi3LqDH1zof8fzx3fy8AOeO9XuarW3PSK3q8QyCcWezWBVsazNoShRp+ygOidm5Ak6DhsCJP3Tt2+S4dY6ujcc/votnLSpcEi6+mE5JX/5+m1vLzeNS642Ed81HddSsp/kAOkXnjdXzkQdtDLcqhrXCxJI6Fyq1NK8e1w0QDFSAhmXw+luS5anOE2Uo0dL5WkI0hoaNO2aFjfqgSXYj3Qwk0tFJRWeWi40JuPlhYQzlx0d/m739OSQT5u5+sv1/tVb4Pw9CltSIq7HzQm3Pc85N3pvpU/x/O5In4zFzr3xOZ67Z+fVfm/zryVueSB9gXAAqtxsu4KtQjZhuTqSWuhgnfC8WsEhd1xSeO9rO8n0gabFm5x46hrB1sVTUwdpFNQxFKZJZMGnSmnyzKHUIRzFgS00LWeglegyaflw0hGxc6csG4YqyUkcxNdhUmsmk5B8Ju0To6V2pgqOxamkExyTVOIolsgEo0fZQCwaS+WmViyjWE2VHy90yku5YiWlHNGp4+D7GvU+WxiZ9OBTz2k3ir8vIwKE+f+wnIlXH/D83ll/UobOFwDi+o/5/M7sptpdP/7H3+FuFiqe5UzI5qDEke0FabFitFqqJnVIDv1hnVrHHbc7NDQNfjIoN/yGf0hKOSAPSqI8TEr2PMQcveAwDrDdHnhJIJMDZygfNk1NMiwnBmZ02sOkm7tAbeB0uaaKYONtMtHtW16WOs140xxqpFKKggNmpEky7BmhZUhXpoCZiUaOcEROmoC2aQrPEu8Y3BZBjnLvplHkeNHi0sL2CTngFVv/XzE7c3uWGRH433h+lyn+v3jy+BTP483z3gwC7HTcqJS5zRiFywUbBLEOZw5mHW+eItNdEGHYHXdF0i7Yzj0ATpLgeJvtr3v4Ai3ecnCAB1/39sJ5QjiIIwBNanH/ntscimpJp1WXzXCDZL0Zx3pJBciMDMp7Kk6vqTjJyEzmRAxoEL6MxgDNcgQTdpyMze5M5Y6CHQcR0rIKBafFDzoj0xB/Pvu0tlCrNXx0lCg+XFKIxtJ2qbfWWz/e5zM8GZW7u+aNcA6VZ+ed3+bgfD7p8ZSn/8X8T4Ln5ib5J3nGyWS5kk0X09EIR8wGdUbApdNDayxPlPCbrYJM+NBskayTNqQfiEjY8YXmlz1YJa1pGNSJ4DlA+zEcDHGb1Khtc7jHGILwi7LNMNIZzjGOU+DpNR120C3A4kOLuhWnQ0Jhxv/apdi4dUepCLlF5ygLMeKkkRRNTSOyDN/kshU6dnur5aBTHeR2W0cXdOlWlj2NyVJasaRgw+ebbMPlnfd2375ieuZ7SHlMGbme4ck/8yn6PPPxwxe84zt6PDzF83wNTzO7KU22Vzy+URq9qm8hfeQysc9pxNGiQhnuCVkHcEjyIC+5jhS0nRGnZH4aDFWpaYoOmqHqpAO6X1xuN4mo6Hx8+7bnbws888R/OZh2SKJ1p3YMNT7EpZIsaHHLIDUSiJsY+zCpR6NUoULIlEgHoymlkx3bSN8WMnbQVoWKxDC9TicSUwGsohQcVigVDG2XRp7gYrd+spS2xzwV9AKNMVxz7+Pf/kRGj5h+PuV8XPDxeIxxrfOWx5fPt4rNATsf2cX143lfn11+fnvWq9P4Wh5ParkiJHc25dLC7qQTB2cDTyzNq/NFq5X36kZUsXTNaZdMOEtZMpNhEiCp2bTQK4OmSJ1MWGfMG0XI0OvHuLHXtqpECxKWb7DbIm56oZsANGkaEp42yaAXfAht47jTIuKTi5nivActR3HRbrNH0DMQtDgfasEWd1sWwm9M1EHZakWOECyf1CeNog9Ffa0cyTVqiXRxtEEqurb/z6vfvXrxBht9dHePYf4+tPMLefZ/wPMptk/7eDw8RfQnWwesHP94fFOldw1Fe+oLVC48ReTEjpZWiOFNjSGpYQchYRRtkp5XI9MjSZUkiJEmj8QpL6JFSDhDNsnE/Via7BbOQhKJPK/kBd+Uh0iWCpxVmu56291ydMxhW/P7sc5ks2WXOkdTlKKmWwQzTeL/Lsu6yuEIwvwLfj5Ig852G50QirgMO/FIa6udYhE17+Fpr7d6Wl/xNMihi556gwKV19bO9vmoj8u3L169+o3AEyhB79d8/bg09dYD/NhWukP7RzznyeMn8ejO9fLkEc+rv/DBXFdbx2vnVca4qqtjelsLORIWzMERi0ZcBlxI3m7ILrRdnDljqlAvyQVknkCjw8+b5I0qUliYI1Ui83c3NZSyGk6S89525/Mkl15Z583ktcCkIlYibWBmoS1KKRH6VWFwkrDsfFuMKKk4bnRlRkSBCqEfZw8iHjPsNqgSXetAP+F9SGJlK1Tz+Er12ZlogUzQ1y02JvWNxvKoTjtkfbb1cX/rI5/rgYU+1I3wy3Ny48fY8oDn87KSxyczCE8L1B/sk/7m9dvfvP7L1s5s82Ztt9fFjXoCGcmK6KZkgxqx0+/44o/Y3LIBXrJLSblkLkcILr1tml6zA5lMxSTa7KphSbJwjVIcEUge8wNZcd7hl1RETmZcZEASyBiscqB9xxXnv+pooo/HXSTxGFqcMEg5Baj5VsqmFhyK1bKCkia1bqWhWpJvyabakqD3ky3P8kapfnDdX8VCCUqL9OjqdXHO6IcgE3y/tsnxE0ceIB+Qeqwt56C5x3M+rj8f4Hpk+f9T45P+5vX1wdvfvuRkIBDZnZ33lxfGy5z3UEwlNwqUwRNWLVzQ26YWRqopF4xmW9Mc6IlU/6ewt6mBqipFQnkuHGC7jcU6M0iLw8DZikriVYDUC2TvcRnhN6fYJeuQU2E/BglMLjt3SKi9CfW3UjETH+oNg5ZsK8iOlqNFBqeEUimM0q5IoK01uSskAyEr6vvQ7a+ezc7Pe73exJfLLW7c9OsfNlb75+fnx+/RtR1Dlx9ssTbuN99d5wPPMVclPc055zXiz8P8Y1h/juf79wdvfy3w5AMlto6rfV9gMvZA3KTkpj24WAHPWyepoY8haUxiOEnSc4PTLg/hklUUDrrDNY2KUDQIY42Qe6Gs4vCGCfz2dIHX43HUCg6XzabDFovdS2SwFOl5f9ud1BwuxYI2sez41iTFPQlXEjz9fpr6OsG+1ZI6DCIHU8U0x9zJwW9qxPcCAznRVCjjK910+5vHPTHbMfHFYoHJWrc+Ga92e73zsyuB59UV3uyKj3kg7Ag0BZ73SdQzPB+f/48p4zs8n/TzuMDzN2/4QIlf8Xmhr19z6re2tn31D56lI8vQSBDTlHgRMnXIC78QenA+vQNxkV1CIfMScNFNtymRvJiWEZkmwbc49i3mEphbUxJch1MJ2R2xrKsVl0IF7FUTUzJ5fpjiUnhLw+b24nEh9QBQbcKQUJfjIdy2I5dkiledqeXtSs7mFLQpfxo7kR2RuaUgVB75tkvkm91+/XBjErjIVBqjldHotL+2ubneow1/jepl53gfaoTxBOL7bevjP1vjEwCffv/xy7m8f/4b6D8hsdHXXe8jYrrc2n9/Mq4fLl6kLAcB2Ygl0kcRWHaBJ8PWnPlb7mJAnc4FntDDIjWllhR4+gWepmrPTiYbtaWOKgpysn5CitOIlW0dTVZk3iMkdtwHivgkeIqSwR3GMjnnaoc8U+Ap41lUNKIIdWiEOsiCtospm0XZ7jIKHcsC0JaD55WN5YXJ4WSjK7QXk8lCLZZGwTYa1Vd7a8fr52vH76+v3+/uzPavKT0BlCmrOTzvaSce57LLeZt87k7ny86Hl+af/gQt7R9f/O7l/u6Ll++3Zju7x5v1D76FpZCt1WqbjiizMJx1KCERITrok2Vz8CXsFXDCJvnzolXnV6loQBOOmcL9k9/Iljavuv1GRUve6pOdkRRtUokGO0C6BV+HR0URAg+d1KiqNKR3Yp6LtLPV0sl8aRwPB5BaOq+QNUiJ8kUuAzFDO18WU4sCT1p9DH/FRMeztL5GECptj5ZzkCJLADpG5HS6eba1u7u++aurzZ33B9cvX/1RyFtZNfBdoCSYe9AVoYqvBPH0CNqjAvS/qBPn/eqjfSL//MML9HVbLy9nm2uzs/Xeh5VArRZFUIBmY1qLOonApqprTZJOpmNkNU+T+AvHnV/+4TeWCfg51hoUR1KnOSTwLG+sv+/drAR0E0lNOGwwC0u1qplkm16BJxkoeHIHaB/jDlWKTiKU6tK9soSErx2XHKx3aGOYkiRq2li6IuYcDMNlIDmFD+FfhYRXmmaQhWwcVqulk9M6nHiKCLUU8IxW0NevVuGVz9Y3f3cNsPvvP79+AT3CZPpcUoQbBTRgfbDU5zb5H5Sz8y/Pn3j0tL9/9+rNW4LR1Q5/ht7maWkxEC360sFg0Eu5gqbVdHckHJ5bQ6mgNTuROGwHUA4+eZv04TAy6m9sDRrOFPa35zcyi92d66vVkaNJdx45vZsfhJKjYvo2MMNEGgbhKfX5Bmdel6xQOC8zo6i0v0DLO754yYSomCRXR1ViNKFMZzSHIw+FFINxbpfUCTKakApFXPYEGlqa75PJaKPaRT3fUluhi0p5tMEepv7KSfWGU/95H13W2c7Wx79QCL79PcAB4XcD44kA9mlYeq67f7Zs9Uc8f3Fn3ug/X7168fovB+8vt1b7fSFYyxZTxQUhW7VRkjQlhyx3JPg0HTLNaN3Sv4C2R+xpm+CJld2WQKjlHREHPVC/26pso3lZr24YEsNyfnwqdQ8HX7jeMDaMm5S1MNGLnrwf65REUtQhqeVnTeYRiPusYtVlSCSqVEgYR6iYyIQiEadDqOwNWzHhDKYiiuEqVmhyY4zbyw0Cff2QifFguoJMtH++c/OhdNIDySu6t+uznf33BPrPL2GXn/NHD2XSD/TT92r932H4n0a5ie8MIby+fL//9qB33h0vLGUhbou1BilcYippfnfc5iIL1xG76vGgUhBTQro+IA6JycFbA6VP8ZW2sVcJOFXCdDPiITzU62sntlaHJUEEKhR2QhkiYjsOQ5WOFJIeeKv8p6EuO9qDaApaSRZjDPE7lThpU1M1KM+DLoOcX6kkwE+xCriOqN3pqUCDKJbDVlliQmFCj7ixEFzeXK0b8jR6VGks1Ord98SBlfWdnV0mjs/O4UOv+QT+S7ryYIZR3uE3H7h59Y5/euiBPv76TxDe2Ss/NfepND+5fPubF6/eXm7t7G7WSxuepUx0qeapJVZga07GUSf2qYSE3FjQnbLT3ik4ICiEoiEsa+29T0O/qcXbYEC+qTmkjo3JVTlSXpycrjYCrgL8cN5PZjUQ1iiU3Co1elxFNhtH/+kXGaibmjRd1MiKiD6WE7bKTa+T2yLbLaa77BbxULaUiMsFoIbM3bWljlA4IXZQAkuJWDCRXqpNVhZLHImG7yLQGE1O6iuT3dnaTZdG0tXZ/u/219bWNze3djZ3IZlB9JHW+E6NzMtpHkk5kHr4aL//sgyHd/GOBzz5SPoX795e7p7Neocbo4XKUYhplEBso3dSOj/JhWQHk5jU0mZeeDTmfB2oO4/IY4btjka/mFQfm4Jw4xeVUYu/d0uzBQNkgLWiXULOncfZDsQV9rYMu0xPXmtKqjdeyH/C55o8d2vZMtV8wSmbwZDfbWjDJHG9SQcepZkT8Rh7L+1OQ0hvDd1hFBz4T/B0OSOBpaMQjdhEeXt7Uj/fnJ0eehobG9v9amO0s7Oz2RPV0Wz/VwfH65RPu7PeGdPxr14AwAN0t58my7Nnc27/N57iXY/OFTyvL//w4tXf9ncI7P0S3uikD7NdL4/qi576RjpqUAeKI+6GKSIuU84wEMT6FBVCmNqQf72YWiHsT8K/4e4UBQ8WckjkONNMJUgvnTE3gSjvDTuN265z3tvpyG1YlKZOPkC6hAswk9B2BgoH4WypW73ulis0PXKhGNPjxDmdNhb/fXFFIlaEeY5KLVtOpzMhV6ZoKZMVdODVHm6murPWpfq86dZXuru98z7KVsZ/NtfW15AQAur+55cPtPF8yAGX749PtGH3ZOmTpHM+Wj18dKmwz7+QR1xvYZ7M6dIr6K7PZrv92riEkHqlGCO+681bPN0mZ9d9i6ddFOACT1EfIUUs2OiJACt40oicAqNuRlPBo6UpS0CEzhY8RUFkGQgXhfSh0JHaBDLK9DYsldwxKeRJD1pOrc07vPgWxDoGw0cGClwZboqGc6ejSs5IEE46yJYG8ETKksslprQ1LWVjMmJIrtft9nsfjynd19a6p+C5fr7a3zx7/3FXQLm2zjdmfMYH3fg7SAUUoPUotHvwqPPs0ZNZ7/lmyXe3KR7u8dzav7z808ezav98fX+9dHP2+dXlNbe4NFntBxjvOyoHpKbRoWWkdvTmII+HtFukM8kWORT8EkpOWOBYRybQe+GKBXVRiVkqiwHTCQhhWSU90IlbdJq8zG/JLZsr2SZ2y+bwkzsplDpx+5RpbyllyRLiRrgOHRbP6XRzUWsGaWvGgnRDOCgoQxH+ZUieUNwWQbOSLV8seDzFLKxSlWyT/H1tNpv1ztc3KZ2Pe7OdzXUUI+vrpyfAfbpd7/f7M5YNwN8xEvj7B6QEo/yQkc4Xljw8muX3Q36/IXnOwHnljvYHz4/vL/92sHPcpUD7uLa6fvb5dyju1lBb9cEzl5kWl4yC3cWIukRUwkMWoIP0OP13Gfo3TsOWIOMkYhHo8ybyWfYBeHLOQtxlT7EO1EaIcqiawFMQniYUR4s133DFKl1R06QTR9NjaqLPDdplNHhtAhPPmnYXbXjwVDn0jNCHXCTyuO5INBgSI5xH9DTKuVwZhdCCZ3EpcHp6ikGsnZ2tYYnMwPdEZJ9Rnmyu96u8sHpaqlf7pVWy1J2PV2+RirF5l9T+GZ7zseq7DHyeCH2Y8/5u4N+3OQr/eQ39sn5zWt/ZYaj47OxP715dbq71yIdXAxWEBKliDKlyIjU1hADJoUnMtGjI5cBR1xwt1UjnYOckiUmOvaFbT7qNo+4iK5XQlRAwpnT3KO1JPPPtOH4DXlRsWQM0TThFHmnWO6Pkt3oLmkCC00uSiXWEI/XDNKmsZUCJFyofhYppy0mRUV5KJPCaaU85J4CdppntmmyvrvY/+E5WGQWoEo/O16rVHkP8+2fHx8e38pfqOflLrytYqP392dbr139+Q2ZPYfRgcA/n/e56TiDPf5L89/befEJwH4+OZ1tnxME13ObmbGeH8/76b2cz7nL9JFBhVsMQy+HtSmQaS0WnWKOD0lkXPk1HEt8yBOcchPF1wNPl6SjRZVbGsHy0LhUkhkwpKvZbPL2QdjYb+hAxdCSAJBSZtxUoWwhk1M+3dwBgk0Ov2USI41AxeE2zW9Bc2RqF5FEoeJSiWxSg2EhfoA5C01SOZtCw0fiajBcvNk5KpdVDRpSwxfPe+gyz4Kp2oZbXjs/wAt3ebA3D3XkPl/YCIePjQoxH67uHCKDvrjmB/A/J6vybH/imOzy71dladfbx4HpzrbqOgb59cflPCK5e9XA7kA3Z9fweSwNQb0UDo3TCawoOMkLTghBB5mjYON6KFULujjQrCcVBXWMg1XChr4mmljzj1ZuITWSgn5IQwUdBNq9q7aFfxkUK/pNkn+anjENmDYuTJFPgnE+qbcr3lPXFT7GJiFyZNDxKy1I4KGkS+7FQ13h8uM3iYoC7GUvxJJAt5pY3GO5chA/x+aq9m/XjG7xlt0srhGrp42yzim5jV4x+7F+DJ3Jb5OD3+y7miObbZ2/e3GM150ofZ78fGf05qdNP7+pN8Dw8OT+/2Tn4uNWrQiFUq5ySrbNNmkkbK7Wy4pKTRAyRHE4XVpaXibnBxFEIvhPEkpzaJllNEL0oZA/tSv+nr3kvZzKihKKhSDGdqI1WVqaRZHs4+JREFK/c7bPSZVL0ZBNZ821iOiwo6G5lGsARog0zSWJZC3y+xPp62BTafsFU2iUpoUxqmkK6GGAotubbYJwjMFqm35FNLV3UKpkEeC7hU8U7Fle7673uaYlzDnHPX2Z98/h4NtuBwqvyuPVSXEiXwWi+qXlPMz06yPvHxwL90ZgfGnH3v81lXj+ZHHarvd3rrd3z7ocTbiiRkXoXYCfjiyJ4mrR2UWlK04XJ9jZT6rEsJI6dM24kiRxJWpDQ5M4C/Tj2BX36FnYnYk6kHYloNFuMLTE/fRSETGEWgXIHX2pQpBfoGNNsCg++ICeBhXZEWkLJYzNCqWiECt6EIy0IeYNqaOG9gaYbSsqQI8FMimBPUEcPdtE4ZCp2YWW8fNhfymQrS8hD0ddlc9FYIhWteQjq5/2T7T6MCKnnDSFpdsayq2OgPdtZ2+UjSVgQ/YAnvx7xnN/QwtKfe1SfuNN7fv7f4jkq7e7uXnEASqXt5crS0vJkl/Guw1NGKI5EV1bi5EKdd4LBqW+8XCOVVgzRGnfnGUQybKHMoiBLoY2TgmKH+CD97zg7ghkuEEcY/WSYG7mXBJoQHMJBOgyYDZhOMTHLUAeMiIsZ+oKNal2WlWBWgRBwucIme7EEaS9KXMaQLKtczGUylVqlUgt4uBZG49HKh5Gnll30McC9PM6mfYuRhC/jCKaYn/lQPVnp3tysX73f6q2eY6Tdzcuz6tkuXnXn5e+gKN+8efcw0c1vc7XPD6JZEeWf8CcsARMwiuvX3JM5USPnfXTI7pU/cbtKHyaLTMh4llGIrW6crG5vBMl39A7sO5IDCTqCBrdHCaJYVmhgQHG4VQbUc8uNhSM7pJrAM6mhaIojDGPvIsupaMyFlIJ0hycO8k5soxVacEOtji76Hu2hl+jkwt9KFLIhXVemlQgb7Wwur+lq+klukToC9VHCbi+mwTN7i+eiUPuPxr7lbfC8ENPwy6NxJbHcCKUbCUcoxmD3ZLW00j+v3lzv7/RWu8DZXRN4blF6nr3kev3ixbsXbIMnb2KF4PzM7JyM+3E4eS7jBE8BJndB4PnbeTyxz+2T9V1Yl5dv+yeBomLr5C6Y12YxB7O6CudTUdCnM6QRJgLH0LIvXJSLaZbLSWjwTFUJlWuH9fNGMZnHf7oZ+hAdeXQceWRgItuU8bAGkrkB+VLL5sBUb+M75DK0CG7Uz0VEorvB1iGEITIJBDJvOHqNPEJMJdMY4HvO0NQlbmMKBYMYMx55CEyBQKa4OD5cYZCcuTkP4xOHH8qVQDmTLtNE8mU9Kyf1w9JNlfhegi7bOcOPkbcw7sXA6sHb1++Y+Prz9VtUjUAyt3RgTrX8o7EKCB/O+61Vc5EKfN/dSv+9tDr7yGeRX171S7Uc3cdclqpsVUy7L4BmhOjiMuLkMCTbwWk0VU6T9CGGE2pavYDKtbxS6o0qqqkihvdTLtG5BIHwAOLoTnXPiCsOkfklh9HiuNPq1OKySrpEl16In+gcUfx3mGHUbQqKPrFyyURJ5kL0kKRAMHgJPAmCEXaZZMvgORoxWLpUWcSFstZ7suJZoL+xDMylD4wcVtLZiq+BLqOxLfDs9sHz5HC1v7uDJ+31+qgzd8/O/vb29Z9fYKKXf3737h3QPLjNeTzn2LpHDPntHluuBzwf7gWm2+1d/uX688uts9XDbLBpU1K50uoxsoZRpUz+HOOMQa4xzMYyNDQ2asuFxTotMHDD+UiqS1laOT70caxVLwkQ/DwdOZ1ttGSWJvxSgZUMXOGBSbZOHmvDYnXMFgOUHUnchCWROrk1xQ77n1qQqeKZUHAjIW1JYuxTz2SSCHAiolPAHsAy/CzRu07KVF89PKyu0vk7LNVq422ieW9WPxwxQwewOINJ6RTO8ZAJxfopadPp6sFZdx0l4dZHatL91+/eXV+xSeEVmB78E5ZZtD5AB7AeG+wANd/a5LUHCAXa3/H8+T3NJOIR+rqrA3Qp5JujVNCKohfyjbv0DsaBmkV9k0JGgNJ9mEcSawqTczjF8JDfj8WqGJsjdNjf6ZfKOYfU9g+wNi/vkYV+Po9YVA1LdrVpmijA9IIsFiQ6OO9NqiTYEWQhGtSdTiI7PcIYdc2KaqLzJ6OoB0Q5rkErBYNxbhraKZZfpGjMidPO6aEk6taZNBZD8PWVlQ0mOxmRHbGQZcG3Uqov+whVhyyzBc3Vbhe9SPXmbP20T97Chrbd3YPPf7s6OCBqCEAv/8RuHxHr77B6sNDnY4jAzXWP6m/Ez9xdj8kA9gnlujU7pm009ijTRHYpDTnPMHSJeW3LwmMFp2KISxSMaL0ot2VSclmm2xEmgWSuYEq3a+18cYGZbv9tR16VObl+NMyabtMGknK7S02HKuFBttG9EDPGQvwQb8kySZFJnPc1sF6Vsj4M9YQsWacUxWvwm+S01E5I3GZFSeTK2aynsdxgBeFGXbBzyJT64lo93IDB3p7UauC5crp+vL0yXtke+7prdXJQ8qWTw81ZD3B51lu/umJNyj49upevP7+kR/fu3Zs3ICpwYdWPOLyA+mTiA/Pl4WGSSeCJlvxu2EEsRr+3WvwnrdT9GQN8Y+adF0hCFhdTxeXRaIEkz2VNgxHLLqsYoverP0lUodvusuggC1YtDyTsP904Pdvc9NRQwjfjNJfYllZ2Jv0Dbxt36f1Cv0QP528nYjXsDZ+AEFcsBSN/N8BTZ7y244wqgN+R4a28SGJdIUezCXEStEOeRKdygaw3XQ5FYD+yS4Qi2q8VUQSJaePtk/45uSUz8N3zMc1OatHlUpcZ7tJJfbXfYICSWaBDFE3Hm5MTGPvjs17v6i+fX764YsH+lfiY1s9/+d0r0Uf+NZ+iIvLPe8hIpN48JJr3xPI9ZOI39vWzRPyXjxb6uGXtJ0yV7CNbmlDCkXQsepbHsRyjnJV0DTxREQr9EhHB/dVPlGZpRdKmBONQmEkv8MZV1fKVZsfHngunHXRsVlOTrIVIG/eadNkc4a9iVkaIv7kTIj0acsGagmcraoHnv9g6u+W01SsMZ6bTARWQZIQwJkJFioyESBDCgAkmpiUFAnawcRubxnUcknoyyUmaYx8m3Z3sK+hRj3oPvb0+S85OdzvV7GT7L4m9+L718673XauZg1qrLsz+7r7ZJ7Ha2a4oGW2t5x7sN9WaQm/FXfPXKms/tG340+GmN8Ocw2hobYazZet2hrzwiKm0kgaNrHK5syHVnxx8ed3l7D6xGswQWuEA9p6cHJdbZydfzk6x5xvGg/54/vWGY3rz9sP5q1+JSomFjkl9JE/iFp894kzKm9/s+cuf2/M987/4M/9tT7703qmQveeQpKGg95bT3qZdYCpsY6G4tSFlhwdXS8rD+xQ44J8ot7aqPuzFAelMlpO2P2jUXx9MqPccODAZER6AVDJJJZMxBsgRf/dYM6hL4ePR4N2iP4w85KHEmmxG56/Q0d6BftAo0gk6ms5THfq1/LZpctU1giC06D55G6Lthh+jMC2WqtVN7Hhtq7gZtovLw5cT6Qs/Ob6se04UMyC0t+qSPI9brw/4uSg897qXXPOj49bhCXyN04PDo6Oj31PCf/p4hlCdtaK/gnHE0KRHjzDQnYd8RnxCCUvMx3rv5WM/Z9Uxnw6bvnok87s51mJOPpI88GmBkruzujx8JyI6pKh0PMFzkPkMfSPfZI4qIxSkMwnTmMHRPniS1OLcToSFjkWXeRh7bgHXwAgQhJ59Uye8FPI/0Pbc1Yz9faakOh4zWoQv8tu/P3ysD4A9B6qQZvZz8BMNRTO55gUTWlghbLh5GnNCUzKb1FkDGkfkn34UhVaRysiubkKngT2H7Wg4n5xcXRyQrO+tigFf02ZCA/ZczrEnYwdmB6+PnkxwotTzV8iCCEUHh8fHT55//PHr+Ytr7AkT7hUPFBkMlDwY686e8ltiz/8VcSf+89UrIGmBUe/syZP4z89nHK8poCxqFKtb4hWGsL7wq2YN4GFU2mw2jp3JQkx6MMgz3YICx1SiUFjDhfTWfi6fymrDOFo7fgCmp0CEGQz6KIdpxJOtQ/xMy3yA/bzBcQYnFl7zrvDKOLOMwNoH/HQROMCEh0xjIG+seVX0MqpZhVbT16Tj0a9WvABAHtp0yapDpZHaqF3s1XGWgV/szefzbrdcoiIqWgjOb/lxuOuTBGG+mFNofqExdgAwcny4vHoCLkL2KQNOn5/iJn78IHNXiUsf3vyNxqdUkI9unn3DOjDQd9HWTyHnuwSZePQbmYf+PrFnMnyaLxT90d7hpczbAScKlxaKQ+xpO+bAi9qbIsVdWLEzD+ic03Vrbssh1exw2GgEFbWJpchqtJjOBlAGyb8i2hBUMEDyYk96dilZ09Hc1/kMvJ1smphDKUA+T6KJdXGSQWBmOIhNESjRG/IXj6lxzQKnXddk8IVZsDmxa4ckvU6yREZUZq5ep1Snao9i0Lleb8XEi6g4rvP2sove8EpGtpAn7bVWT444jRPOJ3T6g/nVa2h3DN89ugaZ/PyZmS0feG5u/nTz4es7BkyKR/zzO+I1pvmZPXm+2/O7QZmmwTz0n+zJ1X+fxCM6gZe9Thy4pJXVNkLqCvbU4XV16szqrTHWRlFEG0gfjbxSTpvi1ntt39YydsMo0NbUFzZZgG5C4LBtzi1W5eKLPXfUoMIfZkC3aju1XPZObmnQHdLRFebprkWdizifowpAyp3CmzZVK1byfE0ORR1usxY5dtoYt2lrNsLllJEBHXCuq2XALKbZFOjAcdb03zcbX7f2lqNuQlMUQH6ywrAY9fr09PmTKzjMpfLv90YtsqWjI6C7MyZ3fXwBgvfx6++vzz88+uPXP8LrePUKufz3JP0X3433E/0WC/Opbx+7MyPTH+7MyR+Uj93bYzRUaRMHNRRAhgfB2xY6NudrWPJrphIEQTWBNNgDpaoAwoVCLQLNQZtoOIGhQZF1RE/tqVoycK5q8xhqGo9LQqnbJocYgRvmLqThaAvrgRoo4SdTRiIuCkySKGjcyAyIT2hwNfpxOcaL5QZOVYOmpJiVntXAe8b4nlDQzVJr5rruhlIpcA2ww5rrMmw5BBFdXhyKxeSErrpfDhnewRYK7jkzr8bzL1e9W4AmkQnQs5P2Dj0ROagvXnx+cX7z6uYrbpTIhC8U1th3DDR56/uJTXzn3RuSY5Fp3dmVyJTYk9k7U4s4XlsoTZOjBWeAB4MOmWKW0Ss4NNBiMlA5f6UO0zBi8tTbi1bPq9UKCLujYcnCcZka4RkD6cL27KdpacLDzWuZ7VSzHUZGVridRv4PPyAGYXEPoCrC2HSa+v0hkLKp7eQcRcNjVH+NWJEmPUzyfFQBvjYMu1YisYRWZa83/Ga4jWUZ1VnA0cVXpzLKQvDtqtcJkR1ddPeOX3JGWsvLve6yXL+cnOy16lGL0rO7SqqACVUSQ5dP6IAQrI4h4WHYk1NmZJyzZ/SPN6+A7d8/+8V/nGVyUL+70p8qTt7Bnu9h6fIIQiUHlfs+7dGICWpOpVDAayFFoSiCVvNgu6+7FVOD+C1MLTgFA1rBw6FVLK/ozkAOHDGqy7HXXnE6uZ1ZG9xFEz2C0LkRZmaI5Jq+C6UTl6mpOFrA0Iqegq+YMnNbcBrMqAAoIuk9EDzc2a20+FZD9JymIg8SHRO2klGw1+3GwvNLJdodwboWVKuODVhiw53w14y6ctZKlSvSwNOQAYwmr58fP1mNR+OLcbk3umgh7OqhmqfqZOpd62oym3YPnr++ukIV8uSY2//iwzUV6MczPMWbD68Zuc4pJR99z3QxMZCEe07e96Lz2/m8y1G/r+VJvor3n95jqAXfpUNHlgdtKRwsLm6iSbcFhbBdum/CWU/pCDCHCFKu6HRftRgtI92bRlxfPekuO20NduEDsWdfRQaboTds6JyxDE6TojIHTrSu5VN0gFUlK4PnjWI1RY0FXEcOxcj6bdlT8dhQUB9UpaO6Jqj1sSX2dGLXcNxeLwgStpJBoiuK2YpS7TRyGle/shjCbcA11Lyw3t2j6TaflkfzEYAtF+myO2r3xkT4yXLFlDaYeIfP9xhu+/zs6Ah7frrhhH7++FIK0RdXx29ldR6tpafvniaGTC6zFPX/354/gSKJPdnMdY8smfnSUD4oebYwW6GKGfM7YBtIsvJ9zhsj6OCDUZqnm/EGttXhE06nPKWNragFJ2b0EeM5VVpCRPAsZSZnFMPqTXqdBZPcUkofHcHy9gNmfzNYcRsaI0EvLdATZziVKWi5tKY3TeQ2JE7GGpc9qGoP9ErFUVW+O9d33SgMfduujFbjHsCdCS66/SByeak1pWMxvEXrB1HDxzPM0c6MRq2lyKXrNDtnnfL8olUud+dWmVZyazmajrvgy5dErO4Bo9GOj5kaejnrlU9OuPgff2RwPdko1/7OUGJWHt79T5uD52dA0zer89tf7nl+6DNfUzOFiZTdfawqySzf30F+E2icMZB39qTOTjdDsefkNWXeDBJr3UKsoNoNEkKyqgJYOiSkXZD3vkrBA3l0h2k1kPFA38WeaQygA3JC0MUYeY3znMWe6cSeGbyCcOmgJuuwdujos8HDBHE1TcfBnoELGtJgzk15tZqh3NYEad1FOpfjUmw6ZBhqKog9n9x01GphvdacRkwJNfztdFMfL1fF4u20U14dHN/OSbguCUsXF2SKkEc4oyekqKNS8fj4y9H1KczGt4weefYze3L6+C2JSP9lyuStBBMRe3Ja398rlXgdK4SCXQhyQgbOFhS/mgUrQpHdh4NF/U76TfWHh5Upu7MV2OztsjdGLzkMsScTvhVg8Yh2z2NydZrsuim7dtC15UWqUBBIc0A/CXUb53V/J5c2+bhKC19aSFI0wTNhuYIChCqTcRBxpBQe8lHXI3h7Q1cLIkPCv6mvPcvyUMPnAREeO8ZOs6rj2WXFQrrGMIfihhp/QxMhbNMDXS69APzO8tZOHEab8vxyWo6jTbHXPd47WI7njMGjsvokI/wmDMTskrSKX+hes1UFwOknnPMb1olBOYTEp5/bUy45xn8mmROWvTdGHlH2F0wj30XZtg0cpFSDKlMYIGwiD2BGrESj3Yy5CBynDqzIaMO915ctXvLuVbFTKBCUFwvf95z0FvTiHZmdLIWN7Noh3FPe2GgN4Ir1B6TpionIHUAT7js1FIy6lFx5eLRk85r5ABFuLkVLoKAuHGQHtIh9hx1B/kJbBwoGJSN1sWd10VBSYKrbWv7+Az2NjpT8o2lGFutSQh/LRTQWKe5HS2QmRbLltaHF4TqcjtEkFovkJ3PiEdBoN7Hn9fO9g6slBdfq4lDA/Kvuy5enn5g98h0++h7U5SBySMVzfjf1s/fkBHcGff8MPsPR66vysMbNIcdmSlVzUQGlk1oRPYJZ6Kd0LunDtBMxn1KCO/nbyUvqj9vR5HhadivASBXqF/Dkh7LOgGi9zVnGBTLhQtH6zVrDzBNr8mqS1TSTjZFan8CU3frdDsNGeKDPqXbdhcaQauJklGBR9X18UHXdCDnoilAmKiLroF4KgpIFIqoLmx/m3g/CNk9G6qhuaeq5gRf5+K9iyRrG7Q2Y8hK8Ho3KbhyqwWoEfHbB5C40C7Ml2dId5YCjCggY1kf1EeBJd9K9PGaG049vH/352zgXzAbH6WdHkkCe7FEkD5B3bt4Bhcobf/4z8wQ468teO5LgC4BJOSMJ0wMi7zbFNx2fPDne40GNG02qSRMBvPH5ATrkoITU03PVftrkLFKk7/z2cRasE66cIEc6luknqmRNh2vT1HP7Taog2LNbHEvURrtwSqmk9ndxJvnKOrRZACQiEtRblWocUXAWlLi0gO3ZcG15yGzJhg2nspup6HKuuQ4/yEgc4ZVtmzDuYIVX3EZYW8dhHGK0cm8+ddyAF70fhuq6boGoADSPy3RDKPLxn9xvsv+RFLBTBgC8Pry9vLpdXk2Ozhjs944ZYzx3MwkSs/LrWzn09JvrxG0+QzbyDMNTzz+99wJaHwARAHufH8akIyalsyoHjW9ZRtAMcE5beqNT8mqR1QVlgAL0uhstmhUw0jhAsyDSJGiJjE/aIqFE08H4WZlVRb7kWRwrFXqOzE+W8ZbUR9vbA2biyJAlBmTpORrwKuTGtTGQNoepZwrYM4z7KgyH4bxmi4yUoQG4gQVlkkpEf5it6OJ34eyBq0L03YeFq/kVxfOo+QPESjjduFgfFRmpTD5rB4tKOzTtuOGSAYwOJrcciz0m214BnJBMXzLLqTNf3VolctSLLrnrcu/5nz58fvO3p7RAXz1NDqbkRj+lnO8p28XCksYnRzP53zOqpXsfPsKbmlzMxwIIZ8CGhPClpiQcS1lEjq3T8NiGZ+BU7XZRdLs0XeejcN3UIH5HrnQlMgVqUm4fF08kmbTeqfXJKAe8Mml2G6XA7hHOwlna2dXcrR18H+9gTxCSLbYqyBhl/PA6QMXZx5FqhZprSnXhdao0WUlHbSUODUMIO8D4v97VkZwgpXcVmXuZzB3d0u1GDChj0FIMcLZCJYnWUAjyqrH2IQcygiSSdk55dnk7XbZWS6sIvWluTZfdy3GvWIYOU6xfUAggB+tYyy6cx3/RCCG7f4oNBUn67kxxllLGi5Ex7Dd46akc0vfMp2Vo8uR2tVIKBB7ElALzckZsGKx6UrOoeMjHOc3gRx6WrnDghECGl7oitrD9dWYfzWC1kkcZSKUoR1rPbO8DnJDO9hVENg+cKrJ3bicPKZkdQbaRd5Aii55LuvbNvMBIbtxRtKT/rmqLhURz1Q2lArYdQITORlSGqvSckdwyu4hSIXZlyUWOdvTvtvJK3FkvbANxt4y35dVxwSRIhk3FjVMQq30fup8fUJMuKZ5m0/aQozH166PLSzzAaOa7pTq4adkajeP2dAXI/693NJTBSIg0jx5BcLyzZuI2v4X5Z5g5yeWl/yEB6d7vz15eM/et2xJ/RWXjuKY03MiqFVUjsy9IkWTqRuAQsmvx+HZy2bIgsfaGVTXvh6hWMlgmDeCeBvMk88EO/ZwJrMTfB51W+sN5m+i0lWVc/x/QHHFIgUKEjrNDAOyLawA9Hgi5qdnsK3hJb73o9Rr8ziWHxlBIMvyqktl/nNYoonaSP7VuaHk87y4KJkOjH6VW3cDU4kq24CHrUoUqZDocVPm5jHwaEbOy6YDpyzIAMtTLy+Wyy207vLxk0u3VHvrP2XzFLCrUlsVGBN3k8OzNu/N/gTlxNjFpcj7vws5TbPfNtMkvMSfWlNt/7/oajIUQf8m/XCVJcWp4MMHlFJv+uyKIhKgl7cbapuvgkXZe3LZBmGYWaxGiNkBdimNJscJ+Lgy7nyM29XMq8cmQz6Wl65sGvkjvij0JxzSP+HKYTH1kxaS4Ta6AolAfaSBLTZEcYE+nxzTVGgZlExhoQmFBmJcpwSmDop92PVhBEGooTCGPbeWqCoQg2Hmuasb2rhk0s2xQ1xlvYLuSFZgswdBNGHrszqCHsymWb29XLSrOSxrzUB26/Ic9L1aJPVf1UmLP8eH1h5uP59JQlkAv+2LI11nznNgTko440p/bk3ex59EJiNXLk72D2AftjHA5honbZIRNpQ2CQwteQ90egGtWaQox186qs80FveQ4cpTA4+Sp0JOyTF6gyM9JVk1ggU3D9u20fCItIL2bAkQSYeJvs+TzydgWhvwvwP138hn+lLtG30HFie8mJzAxwMKhY1RbV2WBUpMDZld8j39faRR0lYe5WMOySvaBkwZb4YXnXgM+M6aD+SPbpGR4YVxCBkgC5wFqup/1A7ItZxGGtosjLZJtIgY5HIMGecMSApHbFvAJNBM2/bU74wucAbyHFx++vvrbP27e3dz87R2UUUB57Cl0ccyZ3P/v6Pydde8dAwaCrxx/YUycS/89SIo9WAVBZLUdSmeEk0Kq5HzSb4O6FnWAz2gy9DqN9RqMR4MKTy8Y3BLQnSEhCFYHlD26Kn0OBAUkXo+13P275+G+mk1U81DD8xr40lYhnwNQMvazfIBKQBdYqSD2c5waYCyFsMkHcAbOoq8Tc0D1BLfWUt6wYFLjy44vlQgK2FCF40ThhZGBIhisMcArV2z8J7h1e3cHT2IvFCMIOCPSzpvNLyZH3dEmjkP6KHVr1aqXl0LXK5YgQBH8KZZOXn788PbNpzcMRX777hXnFHuSLHH/fzqe/I9fwsZP3rwHWf/o7PzN2fPShlRxWI68BcsEc80+xIHiQhbLhDH9mhio2TDSOhPPvLDElegwwbwtymkCRBZhQqNG6SMDVmCJDXalyqIvN4D9gZPkwendx3vCL9n+3e4uh/UBzVAIOhmawgoxrQk4gnQOMKqpF2yDvUeBC4+8uigYAhsykF0VLnTNd2zBlsNqRgEsGTYGGbyIrmYTDYMYf9GpYM9krGDa4IBEOkuU03btITOLDPoqaUO4fTbJfp3O3WRJwV+Hn1ePK7PLYolElKmdnYVrjaiYer2rw99//nT+4vwTNf3NjVgUczJjMLnd3/KnBAi9M6uczycyWYfdHaMSKGhxPPQxHGerCasqsh1vCEPAGtZcNBYcPDNwqQHhA3dgtXoVlUEzHENRXBFKMcl2M89GuXwinOGeDkCcHhIwMPVDlJrc+GyTeJ5FfZjVc7si5uLu2m4+Wa9Nv4PqlkBIM86PGp5LFYyq2HW4u3oeBAAX6VWUGs05FDngCcTKRGaSy3AV0nnXN4jtkZ1n2qj0qmhIcVnSasUgH2YjE+2otSLwrqzvrPc2nflyWWw0ODJLzqY/vyyDSrFQif4+LCkG2oNDTaTHjPYGOP/66OzFG1iOZFB/unlHe5kzit/8qSv6zZ4g1Cds7/j6p9U0arjllkWRYRtSJtlOBbYnf299EyXgBNC9xqmpwREOY8itxuAuR5VccweLSJKpVCHV9CFs4k8HyoCsCH682ijs3/+hUN2hIoWfnMvJxBu0yMIkpzhd1DSZAuZwM1CK9UkkFm6xBCzLq8huC5JKkTekM9hzgSx2QZRSdMaJQyHNyAAibGWqAADtomJAEa3qGWQ88CoI6Tmq0ibf+P1//nqLwdBemx8OWQO9xnnL9y/YoGMQ/wIY9iuLFStzeucjJkfPeri4tjUHLt37cvQSpiNQ6V5r7+gzfXvadx8+fvpw81Y2cP35jpz3zZrgIddnn05puZx/Xc060WJYDn25aHqWZu4AXIPZkNRtllsh9+HasTcH/nyN+sOpQgvNoj9W8/syAVgXx5Xd2SeFVVXwT3JZVnTcF8krpWKKN3ah3gEzcz3NrEwnyAIjI1Yk2hQEyrblcUjIKTOFJ08IXIuqhOw8UPoyjtCkiqdydFxV45XK0tZXtJTO0cV1GnyvgV8oSAYKCOM1TJlEZig76Pe29plCmjIDhxQiZvmP55i2xwUIL+b1CLjXi8Zs/GovUdFSvY/iUm885frB0wPgn92ScAuQf/oyYcP/48MpYx/Ysffy+b/+cfOIPJ6+6H+ee2ef357Sov74ZjWz4koMWs+pYGchIEMuTeC0q+1NGaoAR5auuM1PKtQRMN4CSBGLDR9q+Qcy2Q8jJrxN+kXSWStk9mm6PZSDmHUdJYfekECP/hX4gk9Kq3QrsWdOhDImEVxUDJUAKETXsCdP0HAMB2PS669yX+A3FLj9enPt9tU7XCVD91TFfLgMhT/lBpqGVyqQIGw6hg5tsupQOcnoUpYuGd4CQm9YWi/8StNA/BO1W9jTpiaJYZFtYuxJVX84iurT8YxDVAJJIfSODydXYPcn59cMoXz95NM5p/WaX0+OkcJJeBJiw3/s+eL87fnJKUwe9B09wMYoxn9qOLem0OYzaoXWEhPUJfmoUBVvYlrhhsTbFE5wVygKupJH5kKwMIQegxckFewTqRnNlOUTNDaTPWbQn2hsQP0kVMn+XIQeqpkcZoOYTTT0QowJ4F8BXCtXuBm+iyOlhdGoqjJkjdctqBhN9GDMrN7fkQGP+MKF51RM0yjkFb75zoY2NuOua4bi2w8QgCPUZ4MgDgEYplLBUbgeJKIaQFVmi0SFCdf8KMB3s5EVTVek+qXNcAhCSuZf7kTEprbXgfsOp/nk6zUZf6/3+svxFzCU1RhQeu8DqDPPz0rRex8/vf10TV//R2aNzxruwgNxTOyZ14kWNDgDvBVyN8Z7roOgsYkcR+xZ0FIy5m9X3L6dRxaHyKBA+s5JU9Oqkc/oKtdd2J0auRDhdhduN+gqkUM8M0NbUY5It4gSkrlUCmuOGsOAdMYs4C7jui2MJcTYNI6ihqLCNGO0Zapm87pyAfqktvch1xO61rThNOypkwwHlrUogPyvA9X07AyQVpbAly6kBYPiZQRLrWHPODAoIHYa7Q2yEIoVUie6kshtoEwURXsTAkYX6xuQ1DCutcctSsInR2+etyjzrQmaRdR40J+LFwef6DQ9A6bDnjjRxJ43b2W6zdmL82PRF/uVgrgvgzHHfdJlriElm5J0flP7DSI+g84BK7yFwu6nnR8QtT/ODipaf2vb1AVIpltECY4igQBSNSlnUhTnu4+F7YlOhPiDEyTRSab562YHUoqr6HbAitLA9jau4qyTsK7wwLdRXJW6i1fXBtKkLZgiO1Kp6jWVaM50sl3Djv2GkHyqFRrZpdGwzSguhfo9k93vLzQqr9RACiW0D56Xo/6Mi6Xa2sFNsAE0j5sh4KrQpj1OkWH1XCnup8Vli4y0E7ZplwRBxQPgnzMe4eWXq3poF4ZDau3ZnJSnQV/vE+XTs/cyi+hboXTv/B9//OO7T1x4dCSHS6uBwYyFId84tuGKuMzXFW9fMZtuEPq4VjJClK7kz1l0V1tgpgUT9hccsQFHh61FWn5/h6YQH8bmMpwNGISkCSm8bDoxbEqfvKwsVgtRRIBTNMd3HN9H+1YDScKetcAh8LgLLKNxHAn+pm3QNUnoJSlpI+uEGDY27OqGHziGCcTJ4XSLPeh3UYIsaTKZReun+OrAAafCno1mwSNxJ+A6gPs7aRxHwyUWpvkrF/BgFqGlVNsoR8BDkDCWRUEC/Ygwj/iGJTZMHJyVg4qw92EjSr54tXd9fgP2+R6sROD5u3h0isDh5sevb68hpe7N635N1ZwqeS87xyLSogAtYdtX1u01eVvcsAEn+GEJrACe/V/fp6iEjEgEwOuaMGf2gaOohnKMw2IaA0PAiUKyuoubzvBTQyQFUc1JJO2qgrfUCHgNJK5hOORuMZ6Ssj22IsIQgREyA18DckXTGSqkTK5DW2bizAnaydaPZmATzqy61Yl9DhLGiKoumbPnpnR8vIlzLteLniYDWrXGHKI4e31cld6Ysd7KXVg6MRPc3FwA53s1DnN17W8sOiLU9GhI4k2nDAiNiIjDWh+1JlZUHtErtzDM8hZ95scPj/7yDJAJe0rphD1JPW/eff3x7VvK+KODZTn2FC4Q4cJx2yEP3sugUbv2PX7GTdt3uVsAohBkOGNb8O0Ak+lnFhYLxJlC85JmKGhPKo2RwZ7Qu4rihf0nArxRb1lwdYlaQFbVoJYUll7UaHQ4DOxm4DjEbljqtGmoRaJ718hIpXHHb/uPt5Jx6yrdNypb2q4p6l/hToU0i4AeRObVjqjgAJ491QB84AcBtt+4BHbXTSmdOsJZqNRpRHlw9XOWB/5F4owgioZTo6abQeCSmwKfiel7m5DvCtqJF7ZZk14UeyLN7d72xMuWe7I9gJmtAo0SlB69EvCJeQJfb95Snf6JBXIwfUbtaG1TiQxSUCk3IQGuvV6E/sIBnBRaTSmsVhyqHlZKEM25gXLHZV2RzQQfaCHUm7vEMcmgNEEkshQvmN9UNNWtVRy/Ux6v6hbaVge8PQzJVtYBYS5K1pVZGxzX0B32UMJwXFnXg3CZpJ1YmAJA/t3jQj/DFAcgaqGGk3fJvF/XrgYNXveYl79e4k5V0N74keLgEkVCBi5a0SXV2hoshswWKoapbWbda4tcjmwBST1mTxkQnv1AM6yQRK3hU38iuBtb7RL5o8jtHdf1N6PLQ8t3POns4wM21uWSdtrxc4j0ctUfvXsq9mQ3w+nZR8QO6JifQKCql1hQ2AefL/U6xD06xFJvroOwt5pNl9PQA4xQ9QLZHVtM8n2KSl0EV2aNlF/27GR3hJvEbdYIZyrUBi/CrgY8GXpk4CjyzQyhfEDniqxOFPlRzMOmPZqPvfpwyJXtCKZmcfIqDvhSXzZVqWsvn2GPp0yFEN5TpkChFNTS/TWJVZLzux7nc9RrRMOI9+qlgt2okaiQ21aBCEDJtE5MmVmsR7VUlvu+vZU39STVDUyTPDsie6cO9WLPERx/XPataetqM4xrJNa1SAh+s+5B2SpUgSzH2Lsjrvb28OgluptHgobgQYlHJ89RZ3/+8OLzNbAya69G5cDhnppKb9wpXSwhBXJwOm5jOO7O50wUgT7EDWPmfw7asq5Sg+j0j1SVQk4b6MkkSi7jLl4ukxI6lNEpEqGQNcUwykf07EvDzRApN8EDyQM8GZ62CLDGy4tZb7Mp4+tpnhctgzhIwoiaDGiU+fN5psHIQCc441x+BW6d72X6aHzWgcuDEJ5QMWrE+IqYBL1gA/eJA2Ucj4ECUdfGHZLVoYVrhZwBw98swCS3CV5SzimN4nSuKMyQwZ6xD1mvthkdfmm3YweXFrTxxQH2HBVNgwx/xTmzeDoXKOtfsCZEmvXAIthzgrYROjl6bYR5qHk6Uqub7H7dgMtRN/TGvR4uvT5tTZA+T0qdRrsT+XyrVZwb5CycFVMlQWwNV6EVTjEt8ykHWSpQoGJwqqBhu2FbXo4xEqFL7BnHXsycNDn8FGWkH2jxEntetMZSOAfruDPtkVpCLIMOAVJiINrJpRUNQpNGXsYlVkmS2iHNEW+jaCF7XHB7CypHtkp6DLcc1SuAVGQo2SyvJr0C4TsLFBaEjCbf/cOu/uvfPaANyDfv1ljZbUVFkm8TWjTllVNrt7k2kX9xOLmMqqDk0tBTalZ51Pb7amC1JgelTZ8A4HZ6re7Jc7BRovx7qeHvXXbh6v/+48cXEGsPIUh0KIIhHLmgVsIAtoS2Wi4z1ohpEkfHB3WLsiIkFhJHlAJ4Mv+vadTgsi5TTz+Ai5jFl+qZ1N3UkbzOpoiAl3I1mY8vL69aqxKeMWITNoydco+1lMTNWR3aMa6tdTHueR6C69Ca9qp4xoKeL+BF0HeIs1bovpBZ5PMJvcQ0w5AuSbCxTaGCNsKNQF9li2ktvdteqVIVUKCwz9JkQo5GnmCaNFncqGpkdn+bVXfQgasUDZxPyqKi2BNckNRXoyQMY/ywH4y7h11PIVZI7WXXEIj7Aby29mqyVx7CEFKcIdNHT57/9YbxGSDKPPfq08kegoc3v6LxccJwiPoQPVxa90vj+awctWFT3LY4n8wLYwDXwQTgoDwdhpyimkKIVojUGlNXJAZTKeURbeWTPJnz0OCL2OvXZmT0HJ361Wy6YqyP5HXF0fR2Pl21ZlNLAmmvvJTPXEzGIhZMMp8REIxbk7QNxF43HF/RSCQEdKEAYJA/kY69clU8gKlVnN4I/JA18Im3bwzrIBlF+Eya3czZfCkQATkcgGnQ4GIFzu92SEvv/0DQVRXXZ1Ira92qUd0PFrVp0XOpoSouJKTGOmrPyjrF3r5RKRalrGLEIx0ZtzPvjur7abjRnd58xXygm3eUSKhCsGd5Ntlj++abP0KCvmZsOh5CbepGu3zRuh13iOjsD8WHLvcOJ2hJJxfj4uwW2cedszeYqK9J8TmQzXoZgjHFoHh5ybCHQzSrNbvd6c3gtK1arCREfzWe0uqCOLSarVpMFi6zrH3Favz5rDcXSlZRhiqJeMPi1UB/n8dbF0DUUI7LK0X641NMuSBFjdC1txjszxkrlkiYNiJHgxYSbUgVRnU4lzpjooA7VZLivspxMlBMROyevC+Tye//IEAA8+vrMw9aSdQhU7QXVugCWEf0emJIFGFnWk+IQXaNIXRWm9dmkG5WSEfHxY5uEgLr49Xtp0//+ivkHJpHPPdGc+i6Z+CfT44+vnhx9Ho5KoDT1opjKPzIIMmQ9p7cLqctcHy0zlfQpljlZMVkpQV5kilzVZJq1AdZUDVCh8arHmGR3ohleexw3MyR9k8TLEz+zi5TIK6uJq35LT3w3gy28CEIznSERy2xBRYphwfyaRU7m6G/VjSPthyiRzPPXCEN67WL7HsPCxVvtPIqQtPjk7BBaCNEZKDsIwAZ4l+eLhRm5clQvKCAkG+fJJMWQGaw6bHzUvYNsCYwR1GsmpsysdufT3uhT6d/wTtxBwvYAFUbGBG7j4FSXNzzcjzyqxybtNceFZmkRF7VLq/QNn3+/OIfjzAn3pN+8XQFuHd9+unH67OPnxn/1L3wPTrhyPFxxr1ysY4k6uJ2fvnlNccYDV+rvLrE6ddMdFawELKcmoFk2Oz02WqStQ80CLBAznGM3+UiRhAybycczhXWhCPc6kqPFpIQsl9UV6tDsWbrAonjiEyiVw88wnPgdCw/7sTIGRoAlTQ3BaivOZgxikiUXKMSFHthg/6GIqumyLl8H44yx7vDEZ0xZcDM7289FpYuedZ92SvA28zkqbo5UY/LTjbOqwYcrVSqVW1d7AwbghE6FNl+vKiYBaqaRrvYTvbkOX57iMePF4PEnj18Vsd1F36Jaung9PTHm8SeSf993Dp7+eXl72Hkn5+fIs45mJQt5n9Ol5whHCdHu9ultyoE1LNTCdDEkWJRNkPJmPR/93QmTWn1WRjPDmwUCJNILghXZEy8gIADICUdoRlk0DA0NAp2U1a5oVyzRJpKPoErVvkObPLh+neu3X3rfRM1Ex7+/zM853nO8e4eyQBpeujUlfuyeePMaaeSkh1FpaJSHhVFmt5J8PRFA5TLNMcMRaM9eoNZF52FyNMzo2a1WEa+yqifbCSMSCfknabVoItqjG9OSZJwUHqT7XvD8i9f6ohzinQFZBUgBBJNTOPyBSNR3v+iEq9mPOk9Rrno+38ZwP4F2r6sZjPuAhj+kC1iyM9NkbgKK4BBcV4fO5h5yQEc6cBCG58LAtzvJNDH4rIz8MIbx+95mvmQDXtyLGUcBDCcyrtw1bpvMz7j//bsjBlj1X359avXRX53R+O4HA3HKGNg7o+KRVY43NCiriQK40VDEGrUILFIiMr7RGb0nehresx+A3UL6J6Q6ARDiRAd46WSpJTFxIhhpx3OI1uSmUkEX2icWcg4XlrfiU6fC9PH3tWqHoymKc7nlIUsuDEVLWYIEhje0hfUpJWtgqqrNTs+UQ4q28trgQD/VAwXQYWEi/F4FiWYM4lRCEv9/jegPLD252tYU9+Pzt4PLbuY8vfvfRYU8OIHkakqnSeTNMf8IUZmqQOfw2RhNI8NoQPzIqzOmo9kzfjVZoklI6lywHDInnuV7jNqwmR475IMuZR5vJNwRCySzuen/phuEw1Rkd+1X+6YFERYx56VQv1mVK7wXeYmo34xURiVCnWO1rivgODCGZEhX0z+sNLWcDDgSyYsg9fRcQhAj4mRo0JX7bBeODNmUVUTylljiHC2kJu8ZUacS7GnfDBK4EYzmU6zAqiRTmoR2n1uTfWhOoDKEfGDDgbA2Sikgv6atK3xcKSSXkr9cpF0h02S0xCw1JTSKstqrqrwk0cEsn1GO1v9J0ie9WGEBuPvA/vuOyOHfx8d/mavvykQcYXODEAR0qeyh/IDfAqsEjN54JnEPsOR4ZJGlaN28sVkC8cDyewO64d2TYNgykWNk9cgd1p2PJ3bu5//wJ6CL2HPu/vNz+enzeZp9rM3p/RkVyjzI2B6ynoUpLJl4hHnszkiecQyWUjevAQSECeZkgHUh7/10np2hGF3GQgYCIV5NCXKCcwQubFcYoGqAidZaNB3Yabc3RVZaLGDsIWrnigtRjiUBegD5SgDYVwpKp0kYbYmGs2gAHcq6MGURMbhJ7ek58nXaLwqUb0pmMeXBSl7ygjfm4qyaFYp0fMOG/6HbbSHuya/4bc8PxAEsN+foeTvDhs70s+M4XxR40QwTrRGlQUnB8gMXBK80k7Bx0yjC5NBDZwgVdHt6Q2lahZ2NlwGArV0MZ1SVbYwO9Qs8ef1J5wcBHI05ul3PM2eV7o9X5hqdY89hXnuioo9629XpehpgW8Xqcmo33eTHmoOaBdm3Z6MXYJPd0RKYTTRYTxiJIjKPaSCdHkgA7BylCA+GvUTEnRuxJ5I/Ya3nHFdClTnixndnh2EzVHy0ijT1PJegXjoXYMZKHH8MLM+B0xBjwx2xJ6Co+goStqdBAnSZDG8BoW74qFgxJ5V7hRrkGy0BK7f2QR4ZjeJOVlp/fULHRoO6L/t1t0LmtiRpCdO/1OEPGCksuIfMzpqO5wTaJQnCCKPTGrNsMfEV8O5BsSm1XZMgNqhgTePbNgrXTa72LP+uoKSI9aET/vr12YDn+TpCeDu1xyMqUDUzZImTepCjoTKOCnoaVMRTLro2jk/kD0RsC9EqcG6IvAJxld41TNZ93Zuhb3uqlYofm5yQ9L/EcVXS2ZTEM7IcusS4cmXSDybLHKdwGXFf1bJBIDFOGsuymWvA1iCgE0hGJbJQEhdayFutQp+CPcYvDngJIWgaAenSRMxofQXY1OPmwyLdNbFSaPBKbIy6zEtf0T7f35f/2ArIx8dmn784T6B+gmP+sJ5CR0QsM6bjkUsEJ4d9niNCGBCqc8gCkvg6NDiFMomOYCSl+k6QdvZsbHoigg6JuxOi2+Q7dy3Z2tIeCLlxJ6cz6fN+nk9YxBoj859q9FpFqPUNKcFBt3gRBPjsQLJH2xRC0/DZuMBABL7d2kO20BvZPw2IKfTgXbDDLMlGfMU+x2WiXMe3ziVzDlk2H4ORTVIzLAh/HSGIlFYjnKFOjnYFf6Tld4AISTk0Ww+FhD/yAnVxJ4xmQWGjUOU06qa1IhHIRXkGjBOCYbcURBPKlV0nFOlGKWo9WQTxSRh20FIYbYe8CFjNw44ltfX/6Y5w34r1PeH9AsuoJAciGgUoBsqZorSwSpr0v0CENKZMRzsWWpxM8IJLG4y+gMeNzErksQLGIruNBIjHxJpA/YsJ7rdtZAaOZ1IQD8N7/45+ykzc3q9l3ar++vutl8VMnnlKjfvMnntiqR+4GVMvhbnW0V0LVM8oXAR32U4kgB3rCxkgyT6VpCZWEpMiRnHGZnGN2kMKWaZGkci34BZTm70htzqw9BsMWEmZ3+xWFSyIseQSTUx0km844DAo3IqwzKBNiC5kOTPQD8shQ0MgAzco3S42qQa9rBmgrMZpXZw0+jlTlXpbitTlKJ7jq94zR1EpsQjNq1gUGK+3H3yT4YUHdPAlTU4ewbegkBcSrBdUr8zO6a7sAVTbjTUNuFcWQORRDOsOuKKFVaCp9zxKKqP8w1HRW2On55gjEDAY/D88wN48gy+02y93ry8dMFA62j1gPjdxVEGJW79jSlbFWKr9CAIdmx3YUiazEqxGWm6Ua2DU0BjwMiCLsB0UhIjYrlMkuFuv3EmoevKQUTGX2+JUnWCXd8muVN+5sviY5vAX/Q73PRlmNZOK4d6IAL4jC2DjKAdBPwYMETZzrhKDcAT2gzgpCtdLuvzaSvINNP4T0VUnmzYLuHlk0HmP5ps8CMPaVHL4HsZi63bk4D0ZU9GDjIyy/CV9PSLZccVFhKxjX7Kpc9ppMXl/2wnyYzTYBU1pCMQapZiGnHSylLbGPSRaHxAy/SSbnAi19s+w2nU57pgT4bcvzClYLb++fTS6w5BRKq4dgKKyIzA8DKcH0XmxSVDdovwW6gsjMIuYP0wvHjmpshiJ5Zs2Kg7qXfdfYSoiGoaLYQg9cfbW2L6sPCG74DJN2y9YUimxuI6OaCn+E+hsTaz2JNWWBokVI27Y6Fwkky9FhPDulwhggUiYpS2jKYV72n2qogNouQawYiMVcSeMRlsQ/WuAIRVPP1MyG+QQff07NjSv3/NXvUvX36/X79jT9YEfvabKDj39zTHD5l1rwZL+XOmDxqgrUDItdkRiB6p405RCwVV0KVL7guj+xSOlBX3VtNwMckgil0zbv2m0F5iz+f1w7f1+uHhE+OEGcnS225WTwSmFjUMwGQUHQTxOUfRPmb/Mmq4iievAdlcAj6ZpNvAvtMLsmWsSb15coKNz1UgeFYL90dXY8of+v51eJA6M2UIctXAhJPCIzEKkgV8U+zZAmI5TfAAjGhCHghHuN2haYzAlAzL+cR/pPNCo4KQEqFkpjeUzNMGQf6OwIiUU7BjimzCpafcjAIZkMyXlWIlHlJlZQAjhVns4DPI6uof79fH138OP//7/c/17jGrCBj1fijrHb4YrGFWfSIOkuQJ1Z7JGA4ad3g9nrLRxDdsGSjR3BVHKp80U7JoWjyZUqlSoVR6xq27Dfd9vdp8g2qP/6w//vOfP1/nve2KpGnVIt0uy+y3NGl8DntSFo3HnQ50iHSS9sU5rWEmKWFHu+3ixGLaO0HBwIM9rRwVV7GIope08ob73hq25WEO/JDBm3Uqr9O3u/rpmPEpBHqZyXlz05ggVyVfKoVlzXtEpd+tppJhhdaiPoGFZD0FlBvA/9MTi8MPSZEDcCo1HdhXZH4EAj+pV5hITm2bLY2K02iWzp4P8aSURfu2oPmAkp1Z+Uff/1zsXf/7zzsMnO/05L5+f0e2RurEW3Z+ZnZIp+qzzN2LhU+cpMGVxMVntPyOULTSOHUT/lJmMGiK0mCY4ERV7S1NHts/Z9/+PttsGOnCg14Gp/n6c73+xz9ms02XyynjmyuVSmPYuu3dvY37JVA1ivBKMYwHlb1kZ9jzDGInryMCWmukOUL79xK00wMDcIyup1WYyPnrtZmD0NJ9KN3oGxwyaslTTM3E0QIVfI4oRXB3cdAI5OlpOESuDC0Z9mU5C+Msrugr6MOKRokuyTt5Clu6NdKtaLRYxnl6couoFi83yewyqAtLLNp/67uYWOitpPE/ol84sGmWQ7CbExYLsf6fIdhMy7UlL/4tdx3nJaRqAFwLvEHZnQW513gkIxRMXiVbIdH10x0AmFOYq8+C9APDeUq65nGq3bInfUromWHPp3l7DdtW8qUuQ6h7xCOGkjxtgJgeqbJz4LuUh48v9wWKzrIwIugkJfkLachAtTTqEzCwZ8jhxJ4WP91cW5iKmhITO4kCXbfn8m4p9mwNYVDdXd3oc64nOez51i4wMOUUGUSlmE1TAUzTQDZQ+wai1VYpd+FS+cSeJJxhV1KNawIgwVgmVGnSNQSUQ5EZPe1Hk0EkLzeTcZVbVMJ7d9xa0O+tuqSD7WA9oyFu2TeQPbG/+51tTcfHoCMnYVYGc+VJRGWrmvWSsj2CAIKrZvEbzuwO0msv/Q2vN+R17iASR1npg3t6dnFiBTXxQY0olwnchReaw6u//H3VW/7Xnu028zGXq+129jQnY3p8fbq/bzOjBQe4bLcfuafVctSlAXorqVQ+LPU63KRY0HzCG0sYOqGZ46BDZ/cRbCkzEwnWYTL9gLGwsrqtuxzyscza7nW7jcIbQZ17z1forlCEsoc0BSA/VUBsXFx0WMMKqBk9iLScyJhgxPyz0mAZDNC7DJyhMD51mgTQxl8r7qYbzgFpkzvRlCF2o2UOVVyMbgUNL8WHOQ2/vzMPy7gLXMfCkAMi0x9qeOpPDir7Fw/QTYsyx3ZOWRSOIMyVYMsSFqNlQFsfUodKpyzvkmnSACU0QwJT/A3j2ktNvOHt/dPz3f1y+4AMRK+QxJ7t2/nTfM4aQEanQ/2+FwsXOGBLBikzy73IwSb4KlPquzg+1MSIpYHXbGAO1cUhER+aOtgycBDsIZmHcIM9bwlCdP6WjD2hjSL2vNdrhUkDVyCrCrCq7Hhslknjpym4gW6K3BTfAZhICmIDHTEZGeIitaSZI+QRwlHYHVOT+ZQgdThRleDvFl4MsJISTVSJhdVFYaTwOkBDERHnB174Y0w0c/KqWT5wCKz0A1v+eMee37+aTChNZP+FwwEz38rvqnllzA7SFLwDOEhcVW3nPpHl0o2ll+11OJGRemMa7/eUNtBQZghvV/Me9aXEItEmfroXvffLr/b9C09ved97QdOwvG3PT6/mDDHj23alAcHo38e4aUEHUnVAWsKQEZ4qFMR9I6gnjaR4UqTnIG8EmkbjrkuahFEb9693dw2mlQOE4KcZgsA8+Mdb8k+p6IHqqpxAkVFJIZRWsEUkHC0303EAOI14JGOnKUagO/Dv+yOg6Nlsmhlh8DWDoHfZaLrIIIBOx+VJlOFAlEvN7DRIn0KMLR4iuWPZs9HI9geQmnz589309ZpVTdfk9PtnTjMJvqxb9tsBipwi2LfBPfNqZr4lSIVo1ExMjWHKgT+maLGSsnNyFMnTWY6DcdMmyvVeZtvefM7RfKBCel7r9pxLvfn0tO39BAEVWPm1DVOkdd8rvPW6pOKFSdkDXAGG6UmJ3hQ+EfFIeJwy4PdA7okUiCr1TZGW0A23WNZkLJfL4SOd/Xavu0RyO3xs6T4A5i6ntzHJ6ZjTAqgAKgaq1VIqT39jikXjSXxjLCg3CozOXVHIj9RU1MUpDsaAVD0kmTGOqDtJXxKfm0VHlKX/uihykN1gfjC78uKLYZj5RCjnFdrNDlzzi+9/flxcEOqZNn79FTHkJaXJIfJPYs+JjsfjSIGT41Ycmh/QHhR0lz2XcK5ogOJFHHY0i7LfH7i+ssjcPP1cb3qsnOYhA5Ubj157tXper1Yr4tHP5f0tYoYeirlW4753O2SzFR/X+6RLgrz13cogwOOkqwEzVuRZu0eUZjW++WSMmIXebCw9JiojGLq94SPHfcn8Rq5+i4058nDe9Yk9N54SCGiVNAK9ZJGJ8qnUFFYD7JBkKprWpDvDWKVk2FXMx/Cn02xaXAEIUt+NESHATCtKKptN8S4CtihTD0m9osWVdIg0VVWY/0sP1mf1BaxOJYxvZwIsO6r+vF9/hppGLn9gktmiEHOOfxxSzcsMGC/SBWbHkIudo/nBiLaDE7M0hQmSHrqw6aATtWoAiCKyE053qAH/8fy86T1zzUXx9VeJRvQ36XWu13NWpf/l7/98ve0+zZev97lJC81co7184XR1pcauLEjSs57ggFvBODWhT+4aULIeIfj0cU7y6CWqI0FMG1ILoYTqvZJ2tufz+WrGu0K6dNeqc1AbRHho6Kcd+kgyVRh0DYt6osEwjCwheNA2CvO2Yd68kgJgAoaRAUVM15PHg1wIM8bCeR1WmuaFgcgbUkRsTbGfwvjMbIlrlVLIx8ukle2E42cy7+6zHejrH1A72TiAaFynmB0fGSClw1FHw3fisJ7Lgnka8Ef7QoeK+Rn8aqedRP7mpvMKLUBV/fYQxD/qlhwyDma0rZ+lcn9mgij4PHjIv7Anny1Zi0AJ+uu2N+u2f7WvTlu3hKS7NiVo76UAPl/uM2wLNotKNYQeHnse7NrEnlwInwtcKMUJyVCxN8gxMWiDRaJ1qDyv8/nsn0LgR3RCPivbNOqURfVJgrM8/rAncBakqaCQljhX6CqCQJxpVzqNPYGrPEnCFYaMSjcY6RpSQFhFeVia1IB8AGRQbWJPlyb7UTjMopcoVWqA7Cah8e0za5j5MixlOPyjA8vfDWeMjpFOB0p9wyX9y312+pss2N/AOGPIwQeKwm3wGnePEZTVPpilU7p1A5jw/CO4odIbKXVvs37gYH7YE67iX//18O3TZrNdfevd8WvP7OnavJI4zXNyQDul8ekTBuVhLRNAaCKloRQGOua5RGEdtqDm89PIyRNloxAmQUIE0eRGU1U26kR5eCc4bEqGR4FFhgLbATmd5kSOSjAqZjlyIl8kyCiUOOWqPuLPxXGkEncRxAU4SOMJkqm8MIZQDUjJnk/B2CqWAKMisMqCwSYelNaYFhb+346WVXdSpLO8L0eHA/UAGRneEjT535zP/aPv3+l77aLyJnE2XprtX97Z/gfwbBCB0KU97pfgGoTmrYTMJ8e7DliVorH0ICJzAdPKCm/SvHGuO6f+ma3lfIrEEzGi3i/+sOf9Fns+iD1fdXtyYROwSl5e6cljzzeG5o77UBnpx+j2NCA4jTuN0humlI4SrMYZbIQ9c2JPcKRGi/qd+85DzXkr9mzl5JHR+nqRmeiXP+xJmziL/aIIz1ntHVf4mBeOPbnNEtg5LUQgdxQHysC1IPbk83y2lE+qEaqqeLBS9UQlBJHFuJJ2sSf9f3h2lDM1disxuWfP+GHPf1No0lKzMcRAXzFvPHce/AAlZWswnnIAFhL0EZo4i/mqK+iwwirx8QlvuCecTLkId1TUrESHVMNKzxmXG0MC1En+qevfie+r9b+ecQTEpNns6dfT6p+9t1wCqK1V57+34V0XhQhzNRZVJR3xCq0GqQeO0+xzmO32uCZYBEvbmDKQu5GaX0SPQ9zl3Xz+upm1SdHqrUcO57DV0hFRNkA0EpkJuEimieuE41qEG1Ytw5SCbAaCRIXp4iZjNJd4Vvc0muu4U3TXKe0lrYLmSUaMBwjTbPJ5Ch7ozUja3JUgUF584NCKAzRMKhJPJmiTSYIPW4MpankKdxrxTMs9A8hFDIYABaD5BKo1SdMuwuWIarUT0JzE+7grU/VMU1oSKC8gUppiMOaJRaYuvF/HM8q1Hp+I7Vx3ER8/zHo9Ar3ouz4BLK1Bm57FnmtAO+z5SpucNgUHDCPcPsrJKuQWpWJ6GvIaeUTML5mFg90dHE5KafIY7JTLsLmNXU4EM12jJ8nXPWM2G9jztoWVCVQCiug5wI2QbxCg6OtgcH9FJQpOGA/q2Z1cLOwpx4IeMovsuM4KLEeJWaT9tMKFmoM0IK1Gx9F8DPSU9yWCjgERXCTpdegca3bOM/rmDLQWSJPxQte/r0ngyZjgiEFkkN2UzCw2im6KVqYNagPdTGAQCzrqpJsCQWIgadIAexY98RhXwBXVF/uOWRtDjrnCnjyknxTqTzPRdX5arz/s+bBa4UEldfrHqzQn7rAngBsrmXJXFEkZjzgyNNtWp+FMwiav2OHQuJB0JjujcpHTfNW/epPpeq32ExLSLTH9nr10yy7TYh7JP0UEVce9YlMOap+R5wkafTEhHkFkkINaLgYjwB6iAHBxkalKcKyEqSqhp1gqR+JpbBnF1FgYdgi8PFdSEZ/mDmqoMEJwZZw+zX/ptAxqn2UYF5KSz4cIlRg1YrUeH7KR7YItjUQjGUpwhIKHFTd7l3YHQdVPLbl7BpNNZavO3knaDe7flFEYaftAa2aiMmu0uCiVowzUYl3r/XLGbf+GGgF9LAbtMnxAn2/zzEMuugYREXvOyEc3wJWiWXwrEOZvCcdgG2O8XJJ4BA2IrBcdrF1EXfCKKU5AI/qdeoN9TQ3uNfa8B1jqzsFAqFxv572hACI6sCyw5+QNFwuEz8uqlrIe4ZCTDdGiFl8ZRpNDLJH4qpflhHQSIszpUogICJs5ytgZH0GdWRaM9yOXov8eLXmtA6FROowyeQQhoxEa2PE+2ZFs77ZYzi6uaR9D/QXrYMYOj+xXBQEBmhcWlMPMUAqvLjoKDDRXsyOdV4YJQmquMrTBP9A8TV4zWFZONiMjN/4Y3SDp/Orp6e8MtaI+ovAUJ7DaPGBYDirC+dl2eStIMC2JOu2fcYa08qbMIQDONSKtZuwpk/lFIBvLV6ojGKQo8cjXBfUYtpb3rcfl3S2gCmP22HXLYuTbAppSKqbbhuy5b1xhT5IBmsYesRYP1kzx5KfwuqZYmDwoyWZSReMTciiuABqLaIqlB/T6YrGslCulJl8FpplmOaYwUlwluykciwTg2Ft2dll4QxuG2a0XZjaA8th3IIqgNhVZr1lWNMhArAM2/jv0PqIPTZ3TAhEF+lMZEXco3wQYz4GB9fNRKctcl46Iu5oY8RVQkOWwPvunGJP1uphtvX3AltLh/ETaiUU5mDzS5lxtSAN67d5mCOZLgTgRWB3XWI2mB14/Ks7PZr8+FcVmqYFHLODqysnMYb1HPegU6vcS0CUe9T6eW74GN0QIppMc930ke5YI75AYpEGJb4x6qAl4cClubEso1ae3e/ghi3OuNkF3+FBfy8OfkVPN+yD5KPBpOjZNkRumsgGHOnAKZd+rHh+Q2O0yp4Ru2x4LqmScxo4VYj+u30jYNvKTrwYb3wxmEglyzj109XTZps6UXgjpCux9cqpHvj4UON8gnmf5LK+/gR/rbZFtPWDR9eobJiX2cLEfyJf4RLeneE7uPEAzJn3tbmatBrga/zMdqEWXs+meQt5lOcKxpSZydNRmqub2kHPyu4gzd/c9XC5R7LRAKJKCCHxlSx+lNx9iT4YSScpEDiA7IDihY+xZ+rCnks9mMZBce0Xh8sbgJGG4IoARBCpWfDG435WSekjPJRg67dEZRJJscV6p62NA5UkPw6x8MFCt6A33D8DBdg+OUIZD9GfuCMREr+XoTI7lGfNiDMCOovo6An8LaLxXFMsLJFsuXfzWlGwOez6yhgMnRBd67FIGoTiVhSeHPYm2GO9Bt+dqQ1n0gPU22/Uz9qTzseKE6vYVF7qW2D9vb57xnEMdayMRhTpTjNHDCUJUYsad0FFo7snkCyhIY4pIbnb3bggK16gTwOsMJJkzO3u+XXH7WxLUKTNBnW5P9dHG/cZkBOFOhnFy5oCLcPIcDfL5dJ60PIkHpdkmSEezSrQpjkfwbRcLYPkiJT/9LNJ/nSeOK6viMZJh2Jr0J5nAHuHN5oPDQ9mwziBDqxXfSRT/cWY6lg0k1HVMkyCWO+3nZnKjINU5FnOnxfNMFTjEJdZ/JIROmFnU8fiIYqo4PE1DL1OiRTVv51q/ZpSZnD2OIP+Lv4TQgB1nEt9nYs8Hsedaf2aYk+i/lhwcezIHnw4SOVcMiXMQOg+9LBDl8yMb9oySPvRvdHvOCT18UG9hO8k3AadfxZ6PNInl4Vfq2FNQZLGnmHOBs8eeuEgY4eUpmodKmr0HMhpRkSSKE9ysMo5CVCHV0oL/PWXsORqVsaeI08tQdCu8HRpjYYiP57JOwee0Ug8fHJAf7Z/JhnU67GLP3bPPonYWex7vMW7I4bfCRwSmprZVEBrzN4LFLJpANInRIsELzXQKj8N6phMtMR4cZg+OmvqtfXfaevmwJ0fxf/bkc/lE4rs85EvYVM+X1pDDlvOX1Wy5fJ0D3NExl2/HEwMQHwQHoPGMYYALAlM6qVSYTtaQZ9klg+8+vfYk21xSsIOjQobqbmmYkpO2bppXyMWH0n+/yvUT9dNOh7M3Ski+zunEaYoHi+JEJTQJ0s+wiUSJxmw/US0RhoQs3k9k+hyfRccjV72ClIETm2KinQ/8yBwvk8Y7LWRIxBkUAAaGwpxA+jj+Sr8IXgNoEYOM0KEx/InTHEz6d8ihrXwz52a98+e1fzalXVkZc6G4uO19DiohOUMP+gpKHGlatupKn06AM7eUmVREs/YGuwlaxzymbw8EJt2eEoU+7LmWJPTnE+cT2JmB4tiTWAO0xMDAII0dv12KXh4rvGCN4fnA8VRRJEldsOr5BjwKw3axp45LY09k92zBlFS/ACLSgFoHZbzPdiBpEotl3JgQmwrKTrWJf6QRQKDHvGVY96Wq0OuLRAgenX5P160q1qekVpRsFk2d6kW3CIzmUbGnmSY66j0rUg5aF1YYgAeQGiQJ3YeMSEw/RgeKBu0SEU8SnYAVFr2sEEfmAfRs1KYiduX405KCF0jgZFcZy5UKiwUkK5gT7swNsGYPEIlos5qvKINwpMxk+xuHlJ8lvgu9jjoUr7rdYFjJl4bL7esrVnkUElcDplw5H4FyKcSFYyPdAVTqWpS9DgwoFNSfXKg+XPZWm5fXVxBUDIpguTenQJLEiWP5Rg7QgicCG6Q/anZwTmTKRG5CdDovoUh4XUWxZwUiEl/J405Hi9KCNDWxgGqdoH05gnXLMQVWSovNyakUoanlNZplfmQIA9lawe0hs6wFoFmQG7GoHvTY5EVQvOuKmI5g1B+y2ZoZLjbzOMMpBjn2Qp6V3+q0Ytgkt4H3iHMKoMiNGN2QLgopoaTT0vnVZk/6Q6sVwMSW09dbPssjOJNcc+yJfSWb57PVVtC7zZYR+Pfbebd1272Hrik+r19Jq+iNLw2QBGwwoZl0mi9J2gONrkW3SJ/nPt/Ol70tz7wNigzOrwN1Euylfm+BgHI+eZl06MfNfqZarJZINZWP8xmtgI6gLdcHnmY5goRcNB+w7jFjpTgaS7HSob+OD2MWpWhWxTmQz6f4zDsAwRswLg4lzAnZ5bkVYBG5LJv/IDJdBvYuvh7Hamy0lDye5sIZZHW3+5jYj/wcjMfmEAwfJXocd8x7la72xzyyZhpAAntyTRC8wHNLjOkBb9fbOQF9I2X69lkP9ZhR59t84+Hj1Uai1RZzYnHSmls84WnjpV2/a8NOGPcZr2YOx5xWBvs5fYzpoIBgaYssBh42AKMKBWmW9NqPJEibzZwu1faVcvOR08nZna/m93xAss/OAqElZxKeJvZsNimEUFlTHtFQYxJfCntmMbDUyxVqIE5fow6jscRuYunsJ8qEoXRKZ3d7ZIscOAmclyQdMir4gV8mGtWgGLIH3UzI/Hq8Gw5z/50qpeVFZOfo4JoJbRR4NiNmHQRoxpmEUGDYlQnEajwQQAYUnyaBsXUpSu50fMNLpqRjar2nSD13wyfz7nL1ML/n4M2woRxEEiYsuHrmkn/CncojuZL4A37h5ZU28d2SYnvYfgQPOQUzqrpDlku2ktnAtZHDhHx4z4VQtustvUwnwrc5n0twP46n3ITX3qM+sghzCj4vMFND6EvcnypOkJSyTIoJyBLlflEdx3RPKhef+AQAJ5OFS/wgmSDBvtopVbkNC2Bl0U2SUiH+i3nc0vQHt4CUp7niIRk94HXKYitrTDXus08MwiH7PSkxkeFjPmZkGoXBxs3/fH7CvB0Z/bpjpzaxo0GmEhjERaALyF2tNKX3TfgkC8SKuaqUn3xW787ncmgeMCaA8uwjlZd+HNDdR71JxOewcnaXYs+N7PO/a9/Kmgvxn0I2LLlqToOfqR8CetuDYbEn+Cjb2OSis+KuwAGcb/5nz95GtycF5iP2BE2WjRmFiW7PzKiCGGmx6BejHgAGgel0QEORYOSWh0So4hFTNuHlEvKa/EKFN0GEIRWhMXPj2cuD1DPLn4sV0SXXvAhataDzXOzJCEKrwx3ZkyV2RuY6CiBmg1JL+53AZARxwssyqH3vO/YE1UHfYQ/JWgjjiX8ALqglFx3cOOGyw43P1QuS7p0udKUPBwh79vCcxPdn4vxqQ7zh4mNAhNtiT92ifIaF8a9c+w1/gLqGjIfgLPacZGIR2cTpt51AVKyFo1nWIsjWPloaGArQmS7+fCM1gkS9HhFpuQHaJ4UVHID4PuQnUnlwgP4V+g6ypapoHaCBcNhob5aEAi1xQMGQILbsfyMIUSCBgKJryOQSfIWIz4klhkHRob2yyLOvCzJmOdvvgNiF0GGFBoC/XsRK5zh5DKSPLLrAnAZjYGBD3XG9v/v9mgfSkvRnj6mYRCpJl1bWpcMH83pitbjGvN4y2dkYgTAp2k39FBfK+rkJaEavB6FbSkmgeWkTf9TrgifLTEvuu/5IrP+vPWdiz+120+2+Yk+SIcqbUVLm6gAPAnzaiOxZpmxTyYo9MShJEj5yvtlgT6kYthLhNySj2BMo9I78dEjzXQihBdlpL/ZcVHU9LNFHUYgxJVofEuvFntSY1WZJck9anzTcCeqjK+xJLALjLVZcwMxKlFAFwyLvqSyK2UWHVV0RfzKNcAbQZkd2CFqcqN9MZ0SjfZnitufznWBPJhJjT9obX4+PxJ4sW2Q0OCONMT4COuOlz6XBrhkleO9G2FMKT1ZW0ZWlLZt5Y617d04Z/cQl36zoZ8ihFJvqswIB7siXeAStI5/6CFKcUCHbrShx7oF+J5Mx45rVHaMR1616yfCSHukwam7yCKZm3V5NAKPkHZDefo8/iFXB64CSBaDDnjL+ibpVMi9GvqBqGI1Qv4pSgQNYpB5BngTU9JHWc/YwmShkRc6Nnq+zyGWwO5AI0Esrw9fYiQIBFL8wypQrMoDKzQafRBEhOZzxIr17iHkRtNdWlq6y5vLCgEz54DtYyOG7cMDe36+Z3UwNegEX7FJfVCvjiXfidlr1cBcYURRifCQsQZhYbxMZCtZuTIZDQS/19fG9OTd+/TBpbOU2UqrL1cac+iP2fJhtth/21J//2hOVFz1j2c40jiqDHfpwgK0OoLo4uEWS1niGorxxjz2HeMo5gH+PHzdy6/GgnFfp4+MuWkQsbAreXBdPrDfieL0iPCrriAdUCXYtiDhO6p5iNioXHX2XLGErkcaPExzVRQJvNppkAJZdIsnOR7n2OAx0J1k3NArwL7C9NJ6YHlMa5iuyZyt6MxzlJbDYrmxXFLbnNfZkhLU8+xf6auB95kmYTJASHWYcqehWfDWcTpUb0QHxZpNKY1l/g7H6BvmIbwbcjJba+qHemmMmyde51FtKy7X0N/8D39DS9X6+Bg0AAAAASUVORK5CYII=\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"PM2.5\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:broom\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "environment", + "pm2.5", + "particulate", + "matter", + "air", + "fine particulates", + "fine particles", + "particulate matter 2.5", + "airborne particles", + "microscopic particles", + "respirable particles", + "aerosol particles", + "dust particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_simple_co2_chart_card.json b/application/src/main/data/json/system/widget_types/indoor_simple_co2_chart_card.json new file mode 100644 index 0000000000..84bd56528c --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_simple_co2_chart_card.json @@ -0,0 +1,43 @@ +{ + "fqn": "indoor_simple_co2_chart_card", + "name": "Indoor simple CO2 chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAkFBMVEUAAAAAAAD19fUAAAD9/f36+vpdXV3k5OTy8vLu7u7U1NSKiori4uL///8/pxohISFXsjbj4+OHyHDx8fF0dHQ8PDzHx8fP6cZYWFjn9ONvvVMvLy+f04yenp6CgoLz+vGsrKyQkJDV1dXb79RjuEW33qlmZmZLrChKSkq6urrn9OKTzn6rq6vD5Lir2Jp7wmGyHTrBAAAADXRSTlMAH8cQ8eMsj7mrczuPx27hgQAABNFJREFUeNrsz8kRgCAQADDk8qT/dh078Ak7SQdJAAAAAAAAv5VteeVr9KeOxdXzLmlrI4DW0zVC2FMeIRxRIllkMiKzedmto9WGYRgKw09wEEKyERgbxxfGgb7/881eOja2QQuDoYJ/aA+BXOQjJGRDvLUh3toQb22ItzbEW89DspZzbb1WDi2MlSl+KRLjH3seYqkoZaAk7WtDOEIQ4NUglRg4EiKdc0kiNTSKwAWRnBnxBFoFYj69QFTr+++zkgBwvC9V8Me1LggHs8SVBN0mt4fuBGJ8co+KzzR8XToAcFCsTKFhTpFUQTeh21Q0H5DBcUHYQsPqG0QoAzKGYGWKse6IwfQkiTRsUPUBKZpF9SZZI65yEuA0yTQ3UlwkxmpBgsUYG2LSjkZ5HogPyD22KriSVKQFm9tbCwEoKTJ/vLWOxJIbJKXb/FMW9QWRWhn3zkBkDPC1TCsF3iGiRIOXDkALRMXJw/5rLNeK4GfCP8/8e/sT5YXbEG9tiLc2xFsb4q0N8daGeGtD/iE5erDCeCLXkJxoqBEdeJxnSCXjOdwp42GOIRwGrnoSPMox5I09s1tSFAbC6BN05wcyARQQAXFFnfd/u22TRmfXZYMFVXLhuUkzmYvvkE475ezEflCa0FwrFjkYGEgSCLFiERHDQC4UBFiviPsyk4nCvbU2EVUcDjtXxRt4kBgIsDKR/WZzNP71mwM8yPl4xlmZiDEKoBBVpGKOPrW33iwSVUaYWD0m7hmIQhA5wCu99V4RZURSxcKov+J+5XkEzNTeel2kTGEpfP+oTcI3RJxHj86wrdrtnpRCIqdGaiCsdPiHq0REvZDLJnZLJSLvZaL/jAGz26uvRBBmB8w0EY2YuezoyXzdkYosYT7+H8SEEoVfYhhHHQWxyZU6G8HXappIiyyyxSx1UC3xu4RTh1tYgJxPAgpR0Nvm7hllf++qShiqJoqUEhsvovEbmAtiTUuPcrgvJZTWlkOdWgs30ntd1na0D5MjMIUgjHplSuRTRXrU2otkj/e/xY4Pq+RnuUVEeXW+Wt/q2h1cn7n60tGiy5ErUjyS/eK3PZGomCpyQpmySEMJs6xNXVjJB5OyCGJzi2xpj+uOtiQtGdWSFqq3o1dkLmERiS2wSIcOmd5F7A+RK0l3mDmR2m31ANL9do+uKRts4AkeVgHmi2xdNG6trq9ripz9U4SXctjLaCER7X/N8jE+wVckwGyREyVlkYGewz6L8AygPf7RNJFIFBBgvgh1Q9u2GcrWAsNhn0U48bDXBkUCf3UsKXLCOxocnOp5arHI6yey38QQYL5IqR0SpW7h6lO3FNYl5mg/RfQfd6QJiLBH6GNjvgjDd4Tn0ImsyLDDb1drGES27tlPrYvz7SeInAMei4twWtl0PtTV1W648hY9I48GxIZqmYZFzuIYQYDlRaCVt4z2UWfOg09EInYXn7fnvZBIJZKFPVgkSF2nP+qSK74jbs/nLW0KYSoRQ5DlRMKwiMOLTKKa9QGyIpE8hxmsSGQ+y4ucrIWB2tbwPlb2vdZH5COyQj4ia+MjsjY+ImvjI/K7nTuoARgEAiB4hQTaEvzbxQJPuMw4WAN7GiGnKfHOFEr0mcIfLccE6Yk6vvu3VL3VPKMwAAAAAACAPQv8h/dr54xixAAAAABJRU5ErkJggg==", + "description": "Displays historical indoor CO2 level values as a simplified chart. Optionally may display the corresponding latest indoor CO2 level value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'co2', label: 'CO2 level', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'co2', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"CO2 level\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3FA71A\"},{\"from\":600,\"to\":800,\"color\":\"#F36900\"},{\"from\":800,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"CO2 level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"co2\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"ppm\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "co2", + "indoor", + "carbon dioxide", + "carbon emission", + "carbon footprint", + "carbon output", + "carbon pollution", + "carbon capture", + "carbon offset", + "carbon reduction", + "carbon neutral", + "climate gas", + "fossil fuel emission", + "carbon cycle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_simple_co2_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_simple_co2_chart_card_with_background.json new file mode 100644 index 0000000000..1e39f4f5e4 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_simple_co2_chart_card_with_background.json @@ -0,0 +1,43 @@ +{ + "fqn": "indoor_simple_co2_chart_card_with_background", + "name": "Indoor simple CO2 chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical indoor CO2 level values as a simplified chart with background. Optionally may display the corresponding latest indoor CO2 level value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'co2', label: 'CO2 level', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'co2', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"CO2 level\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3B911C\"},{\"from\":600,\"to\":800,\"color\":\"#F77410\"},{\"from\":800,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQoAAACACAMAAADESdfxAAADAFBMVEXl4+fg3+Pm5Ojj4ebi4OXa2N3f3uLe3OHc2t/X1Nnd2+DFw8bY1tvPzdLa19vV09jFwsPU0tfT0NTJxsnRz9TT0dbCvr/OzNDMys7EwMG8t7fDwcO7tbTHxci/urrBvb24s68YGA0VFQsTEwoQEAm+ubchIxHKyMu6tbEcHQ8fHxEaHA24sa0kJBQXFgu2r6smJxTMycwaGA29t7TQy84fIQ4NDQgpKhS4s7MkJhC/u7wYGgy1racsLRfMx8rJw8UdHw0vMRkfGw7AvLs7PSAoJRCwp5+spJwjIg46NRkuKRKqoZgzNBuYc1I1MBc4NyExLxQlHxKyqqKnnpSkgWNISyo1ORwwLBwrKBg7OxlGRyUrLhKyq6VOTy2hfF26s681KyiglomTb04/QiOSVi2OaEeCXj59Vzm2sLFBQyc/Qhk0NRRVTkIJCQWdlpOeeFhjQylcPiVEQx9DPhx2UjZOTiGJgnSRa0qyrKuOh3p1TjDEu7rCuLWmn56lmo+IZEQsIxSim5hTUjWdYDSYkI6YdldjWlVZWjhVOCFNMx6oj3l6dGNbUUxnZDKpiXBdWEg5MS1URSqSiomckoO/iWJHQTduSi+WjYBwa1tgYDxfWi+Zk4iIf318c25rajpJQyxpSCsuKSc0MSKvqamDeGaITSg7LxqNhYOjhGp1bGeZel5NPyA9OSpVTydISBtBNxiYjXmrh2mSc1dORECtpqa1pJaDe25qZFM3OxSzrq+0k3qBeHaKfWmwn46xjHBkXUpjVj+IYD1VVS9FPStCOyO5sKipo6OqloNqYFzJtqzEp5Wtc0hORjZcTjRXWSSRg3CRYT5COTTXz8+1m4azeU+Jak9ubElmZkZ6W0BrTzWbgWWDclx6blx3bVKnaTtfYilhUSgoJCI4JhW9mYF9fEl8ZEl2dD57RSS/q5/DoYpwZmPJsKJwX0hwWT52ZjhFLBqTkV9CMh+whF7NvLWPiVMiHhzNwsCIf1bSxsSdim58eFmHVTS+k3WjgVKWcUJrOR4InRDfAABJaElEQVR42oybCVjTfx3HWey37Td2sLExQAR3MNjGxhiMOWDKcIDAALkROQQCpBBBSdRC0AAv8sBELQlCE+RvpWiaR96alWZWXn/tzqeyrMfu43mq9/c3Tq/69GcczsXntffn/H718voIzIv897p5ebO4lEAsjV2w8FP/+uinP/sttrfXhL35ZM9reMzba+6528tfHDoVRLPnj8SfWhfA5bOOxXf5SaQ9fQPRXBaLxYFxOVw+n8/j86jvn+dyPD/kcsnPKEokEMulAX7+YZFpG/MjN2q1WqvbbDMkJFmX6XQJPft0SfjJ+sDAwHNnHTa3Qa/Vrl20NsFkMsCsy5TpZqfWvq8pP2SHq/RaofPzjZo4jSb5eY5DqzerTCbVoLuh1GW2OY83FDYMHjebc15mnaxg/MJ/HiLvRvHpaRTTNN4KkJji8KHlvRf2BYrZ/k23bq+QsrisnuBNfvTcFxe6hIzXDAsYH0Z9/xccDgs/xo8YOjxKRIvlsoC5oUFRmVX5kYesdrvdYTcnAIUtSZewb1Gvzm42Rwb6+wfGOO02eKg/vk9nSFCrlQkJ1iRltlKbpFFVHchZ/8i1pXxZ2ilNXJzmzh6HWas3mlQm3UsXUBhtOQ05pe6GHKPR3Jx18uTst/htKGT/pyom0RAYkvau5ctP3QeKgPkX2ufSXB6/t6ndR7quZ+SCkDVhUyx43/8FF2Q8UsG3FAUUEgZFcGJmVWbkgNWutZmNxgSDVWtPSko6Hhg0bHacDfUPDfUPPGvXLluiM+gNCQkmtcpqNtt1BqveYNXE3U8tSd5VtKVwMOIYUCjvHbUatUkGlUr16u7m8vIGs6Mox1Hqtpm1Ru3zrKxVHlW83S2v11Th/R4Us3XB6erbvXz57sM0SyTu6QrncfiS+32Hg4Qjt0dGpOxJFJMsgGI6QDwoBLREKiQo1mVWpc27jQBx4BceBhK71pB0PNS/vaQxxt8/FCxulVj1S806qxKWoNPCkpJ0uiSr1uY+26i3OMtr1sbsR4D8bixXr9XpTWq16uVg+ZYthWaHs9hW6oKkgKKgouJ/oOBBFW9F8T9gbOoiKJpkHLbiVJcfh8cK2H0orE/WdKjxkJTPmWZBYEyi4EyjEAloOUHhH5yYX5VWeSDJbrfhF95vh5l1SXdCQxftmgcO+BwSummfcXGpwahSKnVWrRbEyKPWvqymJvuAKnlxzYGY/ZcPaIZ31icDkV6t0ZS8zCks31xoczhKzK4ipxHP3pNV8d4AISgmVLGBQeH1ThSvv8imkPvLly+/LeR69fWcDuDzWNHLb104FnD4i7sHBFLBFArYBIrJgAEJBoUYKJA2gwiKyBEkSaAYjthlt9nMSYbeRYsWhQBD6KKotQd29FWWrX30Zd2wMWH1mjVrSnNsdjNhYVu2zN5oVBnN+448iyzbd7etrlMHA4oP9uQ0FG7ZbHPYjMYlpU4HWOSMryp4zQev13IFTzSJ4sef/a33TBbvN//TI0DRK+NzTvX5ilkcduzy2wMvggJu924SB9A8LnsiWTAsgIIIhcEymSqmUWxMjdyEt9usM54NSXMW1dQYEhqBgihi0edLEAyG4vbKyu2fSytOz0gy2+y29KVLP+6AfEjZ0ektyRlnKyPLttfV1xXrdQaDRqN+vMfZUL7lmhNZVGcrz3GYzWbXeEEB3t93p03OFIp/AcWMtDmT3VuN3e7Xu3z5BYXMZ3eTv4jtzZYu/+KpQ+0LRhrD6LmCcAWfM+E84/9UgBCJMKliAsWckMTMjYmVm5K0iAt9fljI+huF7gSgCFkESztgRB7Ua82XKysTTzxKT1FrVM7hHHim1S75uNNmhdmuZ+iPR0Ylfu2bHUPJBoM+Qa3pHN/jHCzcXF5cXGLUm0sbciCLG1kVBcSv9+WKaRR//C2b5f3/aQJ2Ln5g+fJj4SMvett9ON7ebGo3yNBhCw5zeXPp+eG8GRECVfxmNgrRBIpwn0WRaWlREVHFZrvdUBwTExbWo7UbEo6FhSyK//w+I6qg0Zl9pSr/XtXNe3u/usRi7bzcM1BitiFjGJFh7VatNQm6+HxiWd7Xxn7eadIZlWrN3VVPcpA3y905zhKtrQithdt45WRBxXsCBCg4PBE9heIbcinFmkDxP2mwDm9CgAT3LO8JkbK8vNl85I4BVuwmBZfjIz0dzWNPkyAoOJ6u4g0U/sF5ZfOCIw5A7YbrBEXMMbPBmBYWFtKn1xvNtozikgMRwRGVNy/ebT6avXpx41pbXJxqfwNiA5XEige7TaU8Xpb3jQ/Htm1L1mtVmqPjWY8HSzcf3JyTAxZmZ01DYY35+aUCpM1363wGiu999Mef3a6gWeyJIPGG4ZE8aRZHb/ItIxshyun9xt27D4XI8Vw2qxciYYW+COBywv03hXPZM4oIL3YaBUNiEkW0n++ieZUxYUHrSpASGoNjwsCi/c56fI4Y1muNNij8eu+ueydOnHh6Zc/d7JSvbgxLt2k0OzK7GoetWmOxq8HmrlGpTI0bRwtat/UPFevVnfW13eM3CjdvKXeBxXWzvaGmwV1c3V3BoJipeK93oECu+DZJ82zGL/LA5lA88Jh+OgOHUQwbj963ehrv716+r28hl5Bj9exefpsT1OtPsYS3mhSsmc0F/+9AAZusH4wq0Fcoov18AuODQhaFhFxAQ423PywmJiY4IgY2oEeOMBbvKlEZH92svLk3NTXz4ZLrf/vE2rNfN2msaUC2rmzjw7zInDt5jSalctdoVuvPtza35maMd2dldTgJihxXzubxo3ZXjsPxpC1rVdZ7Yp9UkFkoOCxAIMbhUTxaIKK4U3mUzaUpDpflUQyUg1e89aLx1L7l9w8LvAkK3j7ECyuoN1DECegKkrOhr+me8+/IFW9BERAQPtdnjn8oWqmQFwkJA/MAgaAIjonJNOq1ucb183pV+lJ18uUTiRFf+0bi7++Vfg4R8TF1QvEt6Gf9s8jgiMuNkWWNStXw9lVDQFH3oLuiu7u6DVlzc8OwM+dl7Qd2l017tzqrIgsVZBKF5+E9KKAKGNEDk/6BA98wf58jUEhpLlB4s7y5wMX0HV37Tp061dslxHdAIULaPMVaccGHYi/o8+OxYSyPQRXTKCYaLAaFTIEI8fPxnRMY6B8admg3QREMErCYHgwdPetjGpXK7CRL9p7k2zvK19zJ/0zRZ2xG3ceLDNbLIcE7LpLnrr8elZjaaDI97fhD57YNj6ur22o7qkcfFBYWoojmPN6w1ZxjLqnOWrWqYBXx/Z3V9HUUIECM8QK+eDMCgHEFFJdPcXhcFlsAEB6ttPeMdIVJOROyYfXuW75bFDXiR7HbDwu5rEkUMC7/r3jpmQ2WiKZpCZIFWIRDGL6+gYGLgoKj5s0LhkXMA4vh4k0xO67c0emM2Updeq7GaHOu1u7bm776mtHuKLQue7Tx8ufDgGJeXnFaYmLeM8uruhaLurOlrmN8vPbq1dq7Ra4SvX5b3YYWo9uZ3ZaVVVEBFK+5/2YxnUZBHOZz4IQ3yRnwGA4RPxk6fC+M4mimWORP8TKnj52eHyBgTczu3gO7l39xRVDfXIodFkuzZqLgEBTcSRTUNArIIgAw/Ob6+MwJDYmZFwlZEPfmRVSWfD7k2Zo1ep0uW59gztap3CX2Ur12fc3qL5jNNrdrWdG1TKSLiIh5N68fTyxLTT37u/GWXJNKtedBVndtW21d6zKn0dA5VH/1idHp2lOBrgJp83UG0z+Ap5yZKH7DJZmC/NJwFrmT45E+CRlvFgCBCJ+NL7kc5jXW9dxaoaBYk3M7mc+6bmFQZ8+Xk6TDkODxmAj567en0yblCRBEiFwmFCqYIIEw/OM9KIKD50VGVkasjQnbvCYdojAqDdk5xcU5du1qvX7wR6XlhQ50j0XLNocgxYJa5K7FVYOdJTl3njZve2Wx5D6uzqruHutuu2HW5bY0t9bVHXW4Xq5aVbHq5Kr39xVTKK4CxQ/4PJIPWBDCRHrksLw45HvyP/BgsxkX8Q1JP7egCiEPKGB48vzdu5dfaDq0QMBGdzJpPD4TIX9iVMGZig+QICikHhbhHhTB86IikTEJCsIk/8Yas05rVist2Qfyr2OCX4qa0lBaetDsQN9V45wHVQQDxefv7Dh74MCuwaOdr7a9yk3eNjaWldVWW/0k9yhI1D9pfWW73gbLqp5a3UwDeCuKfxEUXHzPx4enoLIo5m3lAQ60gS6dRA2LFBHkUmAJHmg/J0NPNqEw7r77y3f3jCykWXgSxfGkHIryoPitJ1dM1g8GhZhBIWRQzPX1D4mIigILvNVQR2RZjz4n26DP1quUSSXDZWcxj65J0lvNhaXXXA6H01nkvBzG5IrKyNSbUeiwto+2/Pzo0ZLcXETI2Nh4XV39UDNQ1Pe/OlrXgVyRhVxBOMwC8W4UWL4J54hJ+YPTcJYnkPPFYh7SA4GA4CCFwdNdwKIHos7JeQgb8j3K0aHd95ffb/KTsPCqPHyQKKFkAoLiWx4U/NkoECFgMamKsHmJeWWJkRERlRBF2m5V8hWdRbdUlZDgMOuPXwaKUqveai0tLf8cUDRs/ngN0mYMnj0PA0he6vbaVfU/bx56ldt5d2x8vKOu48GD+g1DQ81XN2ytr62uXgUSBMW70yZ7Vov1A/JeSuABh8MnhZND0RIWJRIj3sk7DVmIuJQ3QsUz18h7+87J+eyP4FUAicdZcP/+8lNNfnI2m2IhHoguYumA8BkooApqGgUCREoChMmbgaHBUXlpaanroIjIqKh9SqXRoEvQrVFadMVmoxo9uLnUrNdb3YXXvuDKKSpyFbnWEllAF1GJZUCxqiJraMOGuiedH2TVVo8/aG1tbd66bVvLhvqhutrqtupu5M13jxNer6M4z+MwcmaJZEIxRb6iaUiEx8MDG7rgoBZwCRYYQoY+1HVOgiaMTSoNCRtfNFx9vlLmWZ4Xmi8NjEbdoH79LY8qJlHQjComUZAKgvhITM3Mz8yLAonEAaUyNxkkjBkWnSW7xKiyOxw2lwNTSVFh+bVCBIhNu2xHGJM4PSg+7Kit/cO2XHVc8p7qtrYOTOx1/S3NLVub6/vrq6trK9BWdE+1mm8i8Xqt8d5O4bclIwMt5ou4kAb2OsxEyWexUWYJCcZJEiXwXDFw+pyY8kba8OKgDMN1ye37F+KlyJUEKYHnHx0UwKd4b0chIXlzRnyUpeVvzE9NhCY+36nM7dQgPsz40KWXmNUOl6vE5tZb7QfLr137gtuNJZ32LEiQehOFAEn75odtHT9XYa2n6hzv7h4ff9BRP7R167aWodahDrAgPVa1Z909s5Z6vW0G+TdBQbPQVjAtBBwHCkogpChIhfyMgx6ExYYzaEHRVSBM6GMj5wJ4YAK/8RQelye+0NvuJ2GLRORvo4ZKFoT4iVg8EffXf+LO7CpE9FSATKEIiszL3Fi1MS0RJHKVyZ0WlcFiKVEmGMxusz6uYUuR2bgGRwBnDz46+IWaIqdDm1B05sCB9REkntBYfOObH9YnazQak8rSuefx2IcdD6o7hlpaWpqbm/vr2mpRQFaRCvKuzRzq41Ta/PePf/zZnTRXRJN3lEvh7ecCAUckpmnUQ0BAq4msStE8Gl97k94rum9+kIJP+i9Sgvkiis871JvvIyF8PaHGocKFPB6S79f+BBATKF4vpmQO8aBI21hVlZmY+Hmj0tJZolEmKJNLDDqDw2FUx9UUFtnQWFitDRf/ueXgo0Is9FXLihwNDTsqJ1Bs3/4YqzyVWqkEwD11Hd1jD1qbn/zu+eMnT+rqamtroYv3LB6AYrLx/iRRxQ9EfIGIxlvK/MI0xRfirYU7AoQK6iMtgCdciuRDNmm8pCOHz0VTJDqI4xTCiuo6Nj8a0sLTODAMMfAbLOTfAApP1nwNBUiQVLEQARJZBhR78xMzS0wIjyMa7Lf1xTq9wWHTx6mLGoocRhdQWLc83FL+sKGhwZXg3JJTWJNzNjEyESg2/uN3R9UaGFAY1eqSxx2tT1q2vnz+pL+5ecNQ3dW2VRXd755BpisIUDBpU8QjTRbpEwUShZQnliE1QOdCPt8bgcGlRFL4JcITmBmef6ELKLy9+UxLTVKK6PRAaDTNRlDx8DchAuI3kdIP/jRDFMgVr6PwDfTkiqr9G8t6TAiP4f2aBBQRI5aV7hKgsBW57XoXNv5JzvLCUmyzywGj8MulN3JyBvcnZq5/duXJ0+tqDWBYlMkpGg1YPBh/sq0F4dGM0b3uanV1QcV7ZnQmbVJiDwpsvEdjERgsAUTApxVyOSVSwH8ehyOVcFk8AZ9CECCLkHYTIxne4ttNoX58L28eHz06POTw6MPH5ioEbL4YCoJ+AIESAItQ8oO/ekQxUxWSaRQLgYJUkPyqE1VpPzNZcnN3XFBZlMoSM0FhPj5s0i7DmmspdvuGBCyCyw9iBn/UkJ5+Y/DLq3NcPzqyP/Pe8/FikIgDCqUW50OWVx2t/S0tW7c2929oab2KAEFf8UaymKmKWSgu0RSXEQWMTwoAxRLIBVykCo5CLqLBAw0D+kdkT/Is7otDK3yAgsUBIDHNR9pp6goMELFFtEAgEfB4ACviM9L4wd89ong9QDxZE/Hh678oOCo1c+P+qrUIj9zjkbdNFh2DQmcz65WXybmHcalVp8MPHa7yawcPYmFnd6ffuHFjcHBH3tpinCDqDKSCqHUmXRyI6PaMP2/e9oetQ/X9SJxZdcic79vZzgwQHBTulFDwEQbnoGuyx+EgdXDZAp6Y9pNTJAcIcCaIxpwpqadvzY8mKHiolwFiEYR/a8BXJmJLFUKcc6BNhVaQauE5UBB7OwoypPv7LwoiyWLvxtsmXW7x/ogepAoVIaG3mUvO5O2zm23oJnRQyb71robCR48ebSl0a/Q5gzduXL+bdtmCM7EEnVJNDpFTjEqN2mwpft669Q9/QHQ8bu1v7SBDyOzx4/0oxCIelzSaHCgePOAhwl7AZXPECprG6gZ/LBHwmaacRFdf+3wZ5jW5IlwulApIWmjvDZKJWGKFSCSMXxEgIMlXhP8H+gfff3uuEGKR5edDUgV2esFRKKeZPZbG/YM3I3YpTSa1Hls+M7Z6NyvPAoRjWZHeqk8qTjtTU3jw0aODhYMOlb6saseOvWn7jxen6PX6hASVRq21mkuMOkdy7uMHzX/4w7aWxx21SBZZHbVA8YYW3oWCCRBkThBA/4PqL+Gz0V5hDKPC5ZIAvkhOcUCEjxxJ5g7OsXZfGQezyDohace4Ioq1onedEFGFOhROCXh8D0uaEvzg+3z+TBQ0s6+QTzRYEEUoUMQEI3PmHdsXFbFxXsQLJZF8QoLBqDUPR8474y5yuJ1FenKiHnyi/NqWhw+3uNxuvZ7sbVJT0zLzj6SkL0nR2pOXWJMWZ5SYO3Nzh/pb/rB1a0tLW0Vdfx1mU6bFIh9w/HUQBAV/GgUqCIYJJjxIWqDwborEcrEE/sglfrRQTotoSoxoQD4hYwh3YOCWECiQVWAksvj+PSsCeCwJEoscL0Dc55GXoV5XhWcam+g1fTwowoKCgoIjI5uiIsgaqwu9kpGowmi07aqsvLmlpgZjhxVTmU3TefDho4f/fFT+OZdD2YhCmpaWlrm2UWVJ/2Cp89WabFhJsdmc+7ijrmXbtq3b6ttqhzbU1ZIAISjeOp56zULx0c+OSjgiFhk9yQOzxWLzBXAGkUIpJMga3miv0XAxmz6g6DocT7PwGehIkoEGgkYWKiiEk1AiQmRAYPCeeH6eoOCjhMycTOVTKMiaFygmDRvL4Ij7luJ9B/Q6oDieGXmzaQuWlUVuHJ+btXGazf/858O1Pypd/QWXUbkrDyjyG/UJSpU648s/ffL7r2ZnpJQU682WPfX9W/8AFM0brta1bEA5JSReH86nWAAFNY1ip5RWEE89AxeB4YXhg4dWk/RXcozbIgrBIZ2jULBRT9mHmhaIuV54IlDQ0rnhc+cebloo5HGjw6OF2JfzSCkl9VQgBoopWcxGwex5A4kowoIJBmYJQVA03RjclXoc1yqMZ27eTC22NRS6iop0Oq1dHRenP/jw4JFHdue1azla3f7UzPV39MnJFp0ppbNl6IlJlZKOomNXWl52tG7bhiJSXzu0taX2atsUirfOILNQVAQGzRdQ+CXJXAqfSVvJ5YtFpHGkKYmUK5Kgq4RHQqmYiwR7qF0sRAVBRAklEAOmMN8L8QEUf51EIQYJWihApkBlpaXnv8+fRMGb2udJPChIUxEKTWC4CiLKYFBURua/ysDJmAEXKvITE8/EKd0YzF0GQ5J1JUqlc0vpkS0J5pqHW3KMBzbusiTnWpINKpOlc2hD89E1a9LdRlWK0lRcj9EU2WLD45cP+vuv1nq960QYTGao4uqXPvrZ84eDYoECMSNAQyWmJUKOSBEgi0dF5CD7wTEBh/ChyFc+IsGFPv8AoCCZRegnlCr8ApsOxSv4VMicAJqWIhegSxNjihFLL9GTKHgeFFNr3vAJFIQBI4qgGEgieMf1o0ezsy1KQ4JBu/Zm3m6NyuzIaahJ0CUlAEWcweX+0UNVgqv84ZaGBqNeqUxO1iu1Sz7Y2jK0rTMlfY3TrLPrdMVjDx5jIHvy+HHHqrYNG+q9pmwmCM+wOjNAgOIS8vzEWR4NGkBB8yQrVkj5AowVQiqIK6TYeAaF06G50fHhit6+ECHrIyy5UADd0AKZiF7w4nA0P7ovUKZAVlT4BdAUaMjl0vOiyQCZ2m16dlhMV+GPUhoUHARhTKgiOPVHR47+fFuGSoVLR1rDvv1q9f1dOPusMeisqjiY0eW6VqhW57i+8GhzkcliMhWfuGLSL05++WTbts7k9O+WFumNOv1wdW3/1q0bHj9urf9eW1t/v4fCFAtCYIoGAmQaxZf++CAmQChBLMMxZufvOeYT8GlaIYifrwiUCNF6kk6cO2eOj5+v/6lNc2i+F0uBBMnh01xxtHjBi+BYfuiheSg2lNw3VIZPFC2XyIBiVoDQkwvvAM8AsigeKILCQkIAA0uIHR9k3sPKNkNJLqElGTQq/YG8467NDTW4hMXMXDlbSmsMarWxtPRRYZFJ2bu26gAabnXJlz+R/GrNat3HS53kwtad8azx51vrWrHhvLpq1Yb+t6qCfMxWRe2XvvTDl00rbq3wk8+RK0hfTZKAiAUNcEnZkMafVggEWN2RkwFOX++pw5T8Rd85io9mUy6Uy3k8KSUWLDwWrKBi58fTYhIRCp9wqUxOS8SyS6LpA6EZqkCq8KAIjUfSBAmGRcSO7A8uPn12xmJJUaoS0ENq4jrvnVWbDxa6DQlWNRm53JvLS3FFTVXoQpE9sCM/f2+JUoUjxTVrLMVrPqdf/DGH2aG3PB3tftCKXNGPUtpWu6HC660s3kBx9T9f+uHzsnhfPwXNkcuZBQ6bKZyYxskDJWvy46K0ABAgcWOjo2nJwCGCgi8Sx8oUtJRGlVlwO0pBUX5CroRCXhXMXaAIkNK0LPakCCRgb0Hhw6CAKoJjPCjWD341I+N47tHBYVVyMg7Jk4Ai7livWl1Y7kxSGTRosS36ms1bjCqVOqemwbW5KjMz/7JOpdG4Vy9Nx2b4Mw6tzYarGYYHBd3VdU+2bW0e6r+a1dZ26S0ovGaiEE2jOBmL9plMnmIMEfCfKxCDCTGM5FyxgovhlJyGECBQh6hnYD7F9WL7hKw7LUTXQQnpBbeDsKsJV9CIA4lYRCsUcxRiWrrwJAKEM4WCmkYRDhSetIkCGgYQMVE3vrrUokq2lOQmW0w474IBxc/MeNPdy4qs4IB4MbnLN+dYVGob2q7G/Mz8KiOepK0pXZNhMS39TLrN6NTrbNqLo6MFHaij2/rrIYvqLK+3sHhjX+H7yTqguCRC1iRKoCRBChHFRkFFPw2veGQiwbpLNpcPrTCbG4FEJOi6sELAJ4fsEuZgUDxHNr/nsILiyWQKpBy5jEYqjQ4QhkeHFPAmD49no2AaLAZFTGUlQgQo9qenL4lTY4+lUluUFr0JnZMa5VOlwnimG1SpVclqfLgLy8tzlWrjEXfRHXTdZzCeq12FN9YkWzTpXy7E/V+nwXHjQ5wO1aLPQl1prb+aNRUgs9PnxAPZYokZFPVbP/rDgu/z8LYBByWb7x8QQATBFQiQ3KRY6mHqkkeHYDbDKRGJGJTL28fOURykXlRXHtnyBvi1377lQ+owyqT/wjlCmTQ2Xhbtfyu/AigIi7egINs8oAiujKwkrUXMmfT0xXGQQq7SpLRYUCNNaqhCzaDobcSjnuA4Wlp+kKSHql3u42mZG8+ClrmwPCcduTNl9eecTkeRLuH6ztEPx9o6NjS3DDXX19fXtnm9yWJKF0AxGSCt//nod8YX0mIkObyZivBbTT4Ulv4KodDndPAcH6lAMMc/et26QISMhM8lyhFxRy6sE3C8eMDAlwSQE4IFx26PzMHfp8MVvmfaFyqkknPx8wMvbEqEKogxsphCIfWogokQqCIyEi0Wqkd6sS5OpTQZLfA62WQ1wGE1IwrlqbxGTOl6fK/uXF14MEenUq3NbHAjQBpBq6G8NDvFAqUsxZmRw6lTHR/dvr0gq7aOsKjrQO58DYVnOHsNBVTR+quPfufxAjn8kPigp5C+GJgvpjhUvNSnfaTnxWl/odw/aP6mvtPRQEHBIALxi5FzYo43c/Yz/1A4jfHr8EBXLOU/VxYaQgdGozsTrgja1L7p1sKTTIBMoZiZNskhIWQRFBFZGYwAgSqM5jjEhs6EdtFsMVl1SqWJQbEvM7HRqDXqVWDRWVO6paFEacLWxp1fVXUAO5vyctdqHVRhLbrmtJmdZlXuhwWjBdW1V/v7yTHyhqGO2ShmDyPTAcKgOClFjLNkCiylOGLZumgf8dyRCxdONZ66fzhULhYIBD6NfQF8gUwu4CG7UrjRfFiBCZ4UG1GIj0Ih4sY29UnRiZIBHa+BL8RYzsxduODSRIBMnx9PBMgkCv+Q4MhK5lZa2fGSG3ErLWrEg7J4vTlBCbMMq0yqkvy8xMsljhISIHF/ONqwuRBHRM+epZ0dsKgb1XGG0kKXA9RUducnnGaCwjj24ejOser61v6h1ua2jo5xr9dtBonXUFySYt5gMQeFZPzgk/MPHl8av8D/dPumEGFg9KFTPStOnxMGyBQymdxv7sC50+HMkzGPKKTRITKOKH6TnObwPbtusJAIEQMBvpkFSECzOgsBGdI9qvCsNkPDIogoECF30o/cj1OqsbkxXS9bbwYEU8J6lSphfV5q1Hq320lUEZf784bCchSKtc/uHCnWYKm50lhTU6RVmZRqm6PUiau+WvXx0Z0FBd0d9WixWus7agveg2L6oNBvAgXNAwkkRXCAi6iaZH+BmR1RQOErtJoS34Ur2rtGBuYvPLeu63bgOT8uYGHuEij8/Pr8+dzQw7HR8JRLC6VSERMOwnCfwKgC7hvX0sgMMokC2QI1hLRYaMAHN9/8/F8QElDDrtS0vcdNJkPOFaNKf7w4ed+zBpeboFip+XlNYaHTuGvv/rXHVSaTRR1XVJrjNKmUOrXTudqBm6t69cuxgg8LKtrqhupQSztau0e93jDCYEoV0yi+9J2TYrJ5EIMCCwEAEljwMleU8RMCxmN4f3lY39LR527f8g/nsyWCuU2KOSFNI/MDxPS5WzgtgUm7Dvv7Sih4jvlOEnhp4uI/UIDFVN4k6wqggCzmoIgsgoWEBd+5HFV2Rq9C+dDvT8vMzFy7KyXFYsHAlduZvaTQg0ITZ9nsctmMuw4c+4smDqaJs5W6jWpSZnBR06xNsumKxz8cG8XxYN3Vq/VX2zpq61Z5vWlvoCDFFKo4T/MhdgH5jUlXBQMENJseKsy3ZCD3FXLJoEoLe0d8F/JYQb4CPxlfHIuRPDa2L1hMiflC2cLeU33RNI9HyzCDKP46hYKRxaxqChSegjpxzz9y9dmovB1k/2C5k5qWn5+fmdaYQU44BoctysVFRQ6DGhXFlHy5qCjJXbrEaXOqyKy6UufWKoFCo1YuqzHq9em5d3/3vLpgFc7FOjqu1mbhNL3W6+32Bgqo4rwU8Y19HR8AmCMwzxIHoeJNPhMm0AebOQ1gwR+cgwSQjTCyC+kqZByMrXxA4kpk2JDH0hRZ3ACG4K87OQzF6YZTQJIFkzfBAihIQYVhsRe0+XIiUOhTkov3QxP5+RsPJKstGDxMuy4nKxe7bQa1EiTu/s3ldFy8uObjzppSzcq4lSuVZmAgPQiOmrHw+UpGyteX7nmAviIrq/Zqd3Vt1ji2WO+EMeMulgcFjbebpuCqJ0vARZwSMx8MCPyQySOM6+BxeyRW4fERKWHuQqlYHi6C9zQHKUIgRmQIyKiPevH3S1wWY1OyYJY3pJpOssBRITHEyaPNialrFw9WnTlRtTE/c/+p4WEN8iL5uH5lsdLhMKCCFOe2HPly9sWqu9nlW0pdrriVsATyLKAw2ZxKg/0rS/RLv144iJNSXFOr7sb1I1wqeKcxKGapAhlPTJOWgTWpCg4wEBBsAoQJF8Z3Bgn/WBctZJMTIYQVh0DkiVgCHjkkRKvOJV9QAgkKJ/X3829DIWFQgIUnXeDCIpFG/Po9eXmZl9dWnbi3fu/GzN2aZLzpGjVhoSxJydU59KqStWfUnc+P3rh8L3nNFw5eK6pxrCQGFCp0nQlOo0prX/3xxdbVH0/v7i7AhRvc78W/oqt4L4rptIlc8cPzSHmognANJcMje48WvL3gPTEggTssirjP4vUM0DQGOqJ7cBBxEF1cEQ5OSFblU4wyFsainsr/en7qlvOsZMEcpE/owpcYWMRHbs4vQy+9997Fiyc27ldrLGqgYFiocRyam6xHRrj+Qe6Pvnkv9eWXly1xlde4axhZxCkNBk0cOYG32O0fS7cZ1ixbMj62CrewqisIEaTNd9qsGQSN9y9E2L/AF47nMBiyYKIBLnoBC5c9kT8VXBI7iKXeTRS5qsYEiOcqO59JDyRI+HPFGMb85s+JDvWbW3meM4vFdAnxoPDECDHfOaFB+ZlAUbX/4sWn96ou48jPBC/VDAqLMhco4jSGFFPG08R/7Nd/7Lt2Q1F5Q42SQTFsUyJEICKVzv6V1W5ziSPjg7GT3d1ZBaPdWWPVbf83CgSIRE6Tt4zjMZbHcJOZj5gBEniNP8YjONHeEqpnRAixABahxaQMgZhH+m+aDiDbTYFQtsAnqKm358gUiqkLWRP95iSL8EkWc/xD5uOUDAfJF58+PbK3GGGhW+lhARSwEo3GYLNYMn5+5c9L4kyr0WubcbGdoBgeLtHhadCQKcm8erWrKGVxuuPVGIYydFo7V9W+q5jORIEA+RTJFePBcorUAArdBBfBzyc7BrgtELJYcxYGykjA8wkSLiQjUAh6N0m50ImnxkIRkAUF+SM4sNmFmwsPf3JFSNemvguZUwEyEwU9dXEzOnqqkEyi2Lj/4rMfPX1mgv86In/NPrWa3MPBpwSbBedFg1/9hDU9I+O7Tr0e9/UIrl1GdFqky1CbrNZPFNa43Okul/n5ydHugoqxSxUFY+9VBXNZcarbfLAwQBLgTw5/xGTsRgZg7powFzAFNFELKS3AQPIkiyc9tYlmunRgAyOwoLlAyezDRYgSHADIxKiS7aergGKmKniT9wqk0gldwAgLwPCPJ0fqWNEBxe+KjTgZN8bBVp5d34heA9ttg9WqBAqb/SsGS/InPneN/HvBBtfKlX/Zex/PY1BYrHagcHR+UORy/q6goODk6KWCip2jXu+TBXsaBVQxKhZIxHOkAlo2R4heev6hrvamrpD4QCkOtpjLm1j/4hPwMLmk73awhIJrPAYFh3jII40o2ndKhGzBk0iY1BiuSPwFa6YsZjRZcg8LGFjAECEEBRqKvfee/ujuYrNak7v+FNyzrE3NW5uTnI1TVIcSO72UZbaPGZSWa7/8jNto1NoL41Y2VpV4UGiGb1jthaWl2owPljiddwvGCrZ/eLJiFe54v88YFLIABEjrl7C6iZVKeCTeoXMuKqMwMPDwiva+C8eaRgZe3H7R8+LCoQvtQSF+vitCYoOiNzUeC+FjqUMxMQM8ZG8BV5nTdxwb8OEu5AEWATdnoeBPJYupu82AMZE9feYsYlCkVQHFHb3RqB5OTRvYfXx9ampe4pmUJUqlNRmNt8VZ5FiaYrKUf2ELZKG3OrI7T6zVeEiYjuRol5WW1iTk/hx3oI8WEEFsH60Y3fk+Et4TKJA2+1FMu2NpmRjvFTNkI/vBR/jGFAseLzrwVtO69k0jXe1dt0/tO3Uo/PDArXhvSgZ/ueRAmcFBviASoiQiavIyBWYQqOJtyQIsiCzQXYAGWAAG9nv4Z+qn09LyNq5/+uxKrjFDb96blom7rampZYl5xYgPrQoZ1IT7ih9PMSlrPnfwmk2r0yW5Lj69kqECio//5HNHiu0ud2mp7uhRrdZ4ZbTi5GjBzoqCnSf/Nwqmgmz4z0d/OBYrkclEEoGYJu0SFM8lHDz3V1FMSXgwDyxB1yZv0YVDXSuw6pFLeEJMYOSEAMg4eDpQoBUlkxwlYM6AZJk/eA3FzNNC6ALGsACKuUCBu3qpiZknjjz9XYnNlmt6lgoQeXllZVGRg7jWqiJbLaXN7bYtUSltnzv4uRta3EhxV208Yjn61Qz7T37yk09YnS5HTs3iV6+s2uy2se7RnaOrKs7/LxT4bT2q2IC02b1Ahl29VAQUYmBAKfG01QACGHCWMS6PZEoWQMlpvsInQC6Qi1BBmcvQPJJFmGsHJMh4TKOBY+Rv/YZpUafGEI8saNhEtphIGIwsgKIstSwqFT3W5fSMFJu6sSwVl79hUU1ag0GnUuI+iX6Z22n7OFYZ5V/43Jdx+cBi25u/VqP5+Hd/8pnv/uQLZqNZ63AVP//AZm1pbUM5rUDu3Ln9v5SdDVCTdRzH62S4sT172Ps7w4w2BEFgHDAIyBpsU9eE0giQs7qi4VFBMyixLMuLGhxRmWD0IllGvFRCWGKUGmWKd9mbGsEVEpYVZ16mddX3/zx73EYo1y8YBlfH/7Pv7+3///0fL4kC6yIoZBo4CDb0tDrsT1PQhIDUzyKcgxNdQAdk3g4FBjPhCygAwyPLQrrQbMw2yTHoSvwD2kDkJESwVqbeZFasVnxyABwYC7qVHgicAAFT+VEotdElBTXxJYU9tYOuNJsz9uUlDAdcC6hNiFuAPQmMZzYWQRUuCGTFl1s+XIhhtaWrC1vnF422jY6eOfMLtjgzF+478fqhNCc2/pfd8OijDa81NDTMogoubBIUbxllahpvEeiImO5RJIbc4esSiVyYRNG0VKXA74/lzkWuhAfBCai6KKuQEsEzeIgoWCQ8hADBH8U8WizCP5QcKFjj9iyYWMGhAAs5xwIoNLgCUBIfnViwevMLLwCFrbGE4QCrzYyLTbgCJJIPfnjripscyCSL2jbdeE1s5rzrC/PzGyfahn3jv3Q0daNdv6r92C3dzqZff737vmWrGhpWNdTvuXRdMdcfNtfd/wrqiiiTSk5LaZrmoa/EeohLiDBQoJaodDV16ZE1Rr3WqrVqyrXpSk2OOgK41NqtdTuVcgFhwwMGsKAFfDERBIZ2QBOboiEoWFWEooBx9QVSSJS5JtGMif681qbm62022xpwIFYWXZkcuwCawBML1mwpve5qRzKa1BtLb7wmEyiW5OU1nzkzMXGmpbvd556/4JvVza+73F/ci2PCex+FJIZeu2EWFDxRAMXd22QKWkrxwAKtOls5QebkC420qJDQaplRn75LmV63YeeOt99+rPKpJ3e8+vGGnUpCjoQIMWoGcQQbCCAJtVQsSEqXikJRBMImN/POGMmpkIVBh3IzWqvFIHvfw3m1Lpvtuej4MlwbgsUvXHjTfHJSFNf3JVAsWoSDgavQjsXFzWtEPOmZmjo70ebtbpvocteiYD1lcx4/dvtRjJksQ6R4rf6yObOHTRmD4vPHN8oRKyQkOSJIEM8nX4k2wvBBanAF8itNh6NWIIfCWIUQChEqBHAQMpUilmPjRorOXCDCBJdEQBnkarVRRfcGowgPqMIvCxhI+EtwWSRuTqVYrdoY/ZMHMfB9/b4CTLRHl5nJtcvKJbWQBG6UFQyWrlhkTyNPe9l081I8n+CJ1YV5Z8+Oj49PdPomzgz3H87L73G5mo6de/3Zx3999v4HcQ4AFHMugYILmwyKnxNNVjUuQBAUZLeecRDStDN9R5g4LGIO4gNCKNG4OJw4hEo2l+08mMIiHB4jVfNwkkxmWFUCGkfHIuz4Th4InwUFYcHKgqQQvZUMZ0U9uTIrOj5vdTTMDBLMaFL8muS4BQt2lB2qwvx7Mdry1E23rkiNO7j58O72r9rGJ8bP+s5MTY12DefnnTrUvnpo6NzrR79+8fG7l91V3zB7MmXDJlA0pZuENEbeaRoeD+OTDoRMMsNZMEIhJJk0nF020Q2+i7YEuYP5PkIos0RKoECRJshWWuBQYrHFlC5XTEIVnLElVoiDMDA4WZgMyhitUoOtPevGBWv1ekQNGEhoMaQEZej7lq/cao4+iGS6MC0TYzibv/IserkQkTVr02jbxISvbWpqatjr3TR4aKRnydCxW1bdcMvX3z1+xwcPvnlJVZBYIcEDXlgHOZpOCU0CCdl35EMGGPYGCfwBHBA3VGIA4EEU5JIMZnwFAIG9O4qM5JDsSUeIRHyqUmLMVmG3BofkOrlUathZp5UKJn+aFiuC7seABbEACpnGGqXUacje3o73YxA0CA5oAjLBJ/69ssZYVnbQsRTVwwKM4bQWnP9qPcm2tUVe38Swh0XR5e4Ya2k5v6T+0bdQUzyIC0P3rkKsuHRdIbkQKwaNcgOFAMZHMmXmY8j0Pg84aGYrnKmqsb1LEDCHzCoIQy6Qwy3CpaThUFmyjRuikuS4K6AW62SGGO1OpdBk3JYEFNOqzQAKhR+FlMunqCyMGqCARW3Us+OcZGKtZG1WlBWTrlajUWk2P/Pd1VfBFtjTliDTMnXHO7GequGiVOIgXd4K5/mmsV+8tfnr19evevTcLbffc/efQHHJZBoUNvc+FalC5QD/Zhtt7ONANGi8MamMXEJRKLkoHqovCCZsLkVuO0AIWrFKKLqcVqj4Sdlawc7HHrOocH6Gdp+ilCYVLZfTmt4LKAInZNytQgkDIggFcohSo8M9dTKBwo5zkhPE3Xud7X3XGsFBqYnUpxx8cSw19yrMnl0PCGCBz8aE2Fhb7hWjZ6bOeDwVFVt63A7vh3jAwVD90FD97Q8+eD9QwEUu6SAciqM1QoyTyRU0AgicBH0WboTwkBlQHpjEOxFNpZQ8QiTE/5EJlmIRtv9qKvnhWjmNWTW+CEfnCpVMoTAkfaQx0YAslWH6W77rPyh401Aw1UWgytJpIokxAQMsYDFrXQ89ZLs+BlLBz4yt7U1jXzSS0cXYxkTOnsAZsy35yqLxqfGuqiJP6f5DdpsDx4u9Q0cG6uEmywiKy2ZDoUkhdcWpV1+1CCkhlSQlMFAjYTefFx4mRZmFhVtpQ7iAV7P1yffCrJfhTzw5NKXCqlB6mpQRl89VyOpUfIk8R06rNI8ZpNnZ1FykZhpN+kAABVgED3oHoZD6mzIT6dR1LApGF9oYkNi+fbszY2UKtCIUmntGbr7OfSoLR4SL7LXxTAWWGJ9Y8MbyhxyL7VDFuLfL27kperfT5XqnBCyILva8++6lY0WEXxUpRBUnlBt2KTU4GcZZuQhvskDB4xnkIhn6LzFkQqkUBhp9GrQQjpWQwoNH+k5KhElwa11Nzo6XdoRHCWgETYUMxQZlUktoqUzNoyc/CUkgDAmuM2WCBUmpFypOoPBvfzOxE7ahEbeKbQ6XXQ9JGDQ9PWdHrlt6MCVxTayrFhw4Kzz1uDPZOzE+7vN0+XxrzYe7O6pHMA4/2dBwpP7IwEAAxawOcsJqEO6q1FJyegPFg/bFkDFfjLGTjWE43CG7+QoUTWF64zYsJwJbEka+QoKYQunUIimVlFSe/qNYLhWYpJRaRdWVK3buTJeoKBMyyCcBEqEo4CGsgUUoCk4W1wJF3ft9Gze09j23pSAyUmhYcrZ+6OyK4hWJevOG/GjWWBZlhX0PDwPFcFHX8HCB/ni3a2FpHmJq70BeXm9vL1Y8qyrgID8ARb2UMmBgQEGpdaSngCgEgMGjtB+RDQyRKVJklWExNK6P0XyLRRxBGcQmmVikQAuCKCmL2rZRg4ElnmWnvlwWGRX/EWWQYDKzXDIQpApu2oRzkACKQLAwCNlgoeNYxNQ8v3ab1agcKIs07D93YuDwSJW7eDPyLFN9mRkW5MVs3k1qC09Rv8ebaD5kW5R6dSv5GeJqb0HW/0AxJKXe01IoEyV8AWk+NSqcBglU6U+9vQ3yQfXE59PYjeCTIyCKRoYVZQvEQoVIokasxO0RmRipVZ+DaWZ6V/mr0pptFquBJ1enC3sDquCOhDgUfl0Ex00ZM7XHyQIscL6+ZvFDe1cjYjaXDQx9U2a6Nn/QW/VcFFN/MihgLJUtw6OjvoqK/v6vzM3b7bm59kozKUzKytDHcKueTmEO+WTaMQooyl4EiuPPv/pkjUpASiw59mrT3wsPQ0mF7c5KWYQEPTiNdiuCpjQYuxIZeJhclqgsKrEoUmYViekkOWXQSK0fPWUq11ueSqfFOdssOVnrJid7j3z/5m9BDhKqCs5F/NsWbNgECYYFSahMFol+ebmjo6l+YP25u48Ork+J39C6ttr7oR6jv3oCg8VASMR7vcOjw8XFFf3NJU3bU+fF7U1E+kkhptfPkkH4pNoEin9IBtmhVf8opCQCOX5FKV+tAwp+BGZaMequ42PnQsbjSXjajVbSfEolStRiknKRpFymo3gKaZIlCW+sRqrduuabIaz/hmWnT5/e8/2Bn377w8IPoAjU3aGq8HchrH+ABYOCZWGMil+5cPvx/UrN/mO3H1Oa21sOXd1UXd21ngzpxJujW5+vJCxg+sQu77DPU1ycVho/sj0tNfPlQjINCiMvzBOuZgYRtIsFFFBFpWhu+laadFXCDXXll8/B5pQCyU8i46OgosVWaxif7OmokGtVeNKNiBepUMtxlyop+/d163qGJg+cxA7inr++P3ny5IHJT4DAIse4CdkDnkkVYAGbjgIOAhRgQIThR2Gsud7Vfq1S2ZtXULYkXra2v7Mo4+ri4o7dee17V768NyEhoTGRRaGHg/j6M4oz+nZ3d2dkvF8CEsTIFEtUEIo50x2FfZwglQQUP9x226cHt4r51EcRIhP11I7HdmwNw0ZeUhIZrMrRKDXl5ZdbssnsnpQvlYgVqpxsZcr+rN76+vp39+x5EwBOHjjw0ye//fY7AGBgk8z6kYMhPgExMwpOFyFRE9vefhQ6FBd4ZQrw9g7HM4Mj9SmaoZyczTkxtxb7KnANO/XU3R3bbfYEjPwu3uHv2QZ8o6MV9owbDz/ksL3QatbGMBiMjF1cFRwKqIKgeOTzyXB4xi6FjodgasJxCCrFXXUqNF741cmehFj72e8P7M/rnRw40nDDsrtu2PPXX99/f/InAPjd8hlqJIVEAFZiACAbOSg7/E+xCCM2sypgoSg4ByEUdJyHrB9zXLG8Y3BddM+kMGf95vSC6/rbbi5dcdVel7vj5+7l86944bk3NuvNTIA83zZanNGYf3zwiVYzajNGEUa2YA9RRSgU7rlYfhSfTkoktMgq2VYOL6BJ8EQmVZd/lv3Auv0DQ0N49988vWrVaeIAB5j1/0EAYOSVUOIWj9XDWAasMRw4EjOhICxCEgiLgqk4dUxf1jrmnr9838j5sY7WyKyVzpueXNvvGS/98rrciu7CmMGme0bym48XlmQBRWLjG+7+tOfi9ev3I1z6QQADMQ2WDyMcOCDBKBALMSuVHVn2ClDsRqVogAA+0zxQko+a5Mi7DQ14dtBp5t3H+icvLF/if/exfv/isfSLWJjfQruxUBShPQgmOVkTClkUWZ3vzE/uaDk65n5HsybBiU3wzv7htirMYVW3x7Q+sbe7aeXy2sM9ifq8wZdz8aDjjJtrm8u0UYwFSAAFY2z+5F45keAAlJUFHlP87edHUKzXN7z55mnY039h/Vg9++5bpOTtx/pDlx+y2lDjvjcnwCEEBS8QK9CoMygCgydQBUuCoACLxE7HlQt9vpYx577otCtsbvv8pf2eDyd83hUju1fvs+GG6aJ5i5vz9Pq8iowMPJzVXXHzl835rCY4FBo/CtiMGYSRBQ8n2payd1c9/ReJ/Jz2LRQF78f6GQCs+v3LJyu7sNo5+JjDvEw37ichoQIWEja5jgwk/BmEHbUIRmHc3bkb1zzGOptSXUsy4hbjEaQJXZ6KlvHDnm96uru3d1e7bVckOJueSMy32YqXFq2oKvJ6O7tPXAiYAAGL5FCARag8YEDBnFogLEgpS3YSif1yLB+VJKd/FkDwux+0VOaFZUrMH5YCP+ZeQWI6CrAIRgHVTUOhAwl8YVQx0hL9jG2s091hd0RfnZtb7bTPr/J19bf4qkpfsNmc1dU4Pk1Y3PTdofX7HNWdLZ2dnb7xibFfYlgSSsaAlvs18REgcsFZyKQAYLB6DY3+4RdBECSrILYcaM7zQtQBCxwIcXEzOIOwovCjICmEM7Y9zW+uPJhR5c7ttj8c+f5VGW7XQwvivjzv6fdii8bp7HZ4qp2Y6O2yLbanZqRlOIt8Z8+dqT+3PwACqmBQzGQcCu6XZMI+ALAamBYEZvaw0EKF+zL9ayiLoPsxoXUFKwouhSBwhqBQGh+z2514AJCtL7Iyrdhe7XIuSN2ypb/fW+S2OR0VRV225Li4Lmdm7t47lyJUuCs6x3sfAIesGPy3hMR0FNOoBP+qF4v8nOqDjL2XObMFZ+s53AeHItRBgmPFtPP0IBQ6trCwPo+5gdhUlys6MvLWFcW4d25LiCv1VFR5bE6nw+Xx2mLj7A5n8sp4c1ntqcNZiQVDvcZr1/V0bdEChZ8EUEz33RA/YS0Q2QKhf2YhcDczZzcW9IwoSAoJSaZcM8ahkHGi0MFI6R2/EoeBi12bsKBSb1Vnh8ORlhCXaiu6OcHu2r7J07UYJA67c1fmpackbj516JvmI3sGenunvF2FrCYu4iAB0U8LgMQ4VBcBEfrsf/IxCwp8ciimJ1MWBItCzqlCFaoKpsgCCvPy5IR5dtdeY2TOFp/P11bksOdmOhfbq92ZjkMlHk9crr1rxGHP7DP3XVfcicP02/8+kqJ84HznEqAIqGI6iQsv/nDHufrMCGb+myECWGYRyKVQEBh0oNjkZMFOLnKmY1hY1yxPnmdzuduFOT3jZ9smOovsmfOQPNyu3H152pbiTHu/9/w+e+47WzweT8vUmL322LHeod6sE0P7jUq/KHL+Le08Y10KwzhuxRdCgxB0GaUcWiNVbYm6XD2t4CDGUUKJ1YpQaWPETqwSK1avTYiYiXXFXiExQgSJENwgPlAjREgqnve8fddxav61xyUk+vN/xvv06bmV8Foy9QNPgqIgiR9fDIQdYCjKySBhEMCsgID0LRbtNUGwioSrKRUOEf/snS6IBWWao0bHFS+2vtpa6pObtI1lBk179sjq+FJaqqqpIwu8sg8mFYdevCoteXx8wFlbl0d98h/PXW1IXFGNBb++J6D/6t+rSjEhSrqiItDkTEE/YMkPLEiHRUhgFMCCg4ES58oOTT19E+UDTT0ARHaFHJAkaeVGOIo/uzZgfySVOJxtk4n65FIl/uBLWC7JfXWefd2w4ZE+Hxfmz8KbCTyKqkyCL/6Wgx5KwTSiOB58581QiK5gqzcYBbYFFYoQ64R1LllRyzfUMD1YkRycGgMkPL5m/XLP7m+/OP7Zimerezeak5HlaGLT+9URj3JnyeoBy87Osr79eGPhl94w+WAoqhbVf3iiEiGBn7pjL/OEbs+E3c9DjwKPLHCEWGpwFdXmt6Y7RFS1vMxk+ZaCgZ0q+TwerxTuPXDA17Gjz6TXrj3TcWQmGE2M+JKbFilZPXfp/jdHLs49u3rux++ftrqFAPlnFEUSBLmKf1iPQuxaWKoA8aexOj+hQCDgQl1hs87o3zccnjbdZJrd3RePlgeDPo+vb1mjRmevnu048OumlbFezpOyktr6av80Wc7mDu/fvzV359nBw/n896dPOBRcXJCCSfU7EsY0GAoeiNCWi/WD7ucZn0H0KEA1CAoTQjG7f6RUzdYHu8zfdGlDUpGARObkha9X7fcc5i6bmrZO20Nwb+sX8C1ESvtm9x8+3PfQs3xudXh/fkV8mJ2ioC+f9RB/7ori1kAPwwwh1Gf+jE5QiBGCiinLFSRZMFfgd5IXeUuUBw3R79vauBWf1Kqd7GqaOH8ku2KD2RyTmwXKQuHFl1Jw1x9Z3RqOJORDqfz3w/sjK3KH1Pk0V1QmB2kNCv7q/3NFkYSJB0VCW8+yJk6b9JhOUODzGGahtZt8gOCG0z+5f0XFbhMiZHPEpkUkTwv4QJm8LZFQVna2t1nQtPnOW4lkVgmXlih9D8qSJ3joUD6/v6/yIBtX01BOAYVO2BlM/1ZLcSNRVSShnw0xVxiiABZ8hGAUrJpaRBQ268CpH+ZoKPyNQl9v9YzAp2VK5KiUkBbA7D8TaB4Zpyrw8blI35fJREBqEkzFv+cPKeEYoMhgVxjq31EwGR9U6QVLnGEBCP6ugtgVLEKKo0AwdlecrF9AAbe76NUfPk4mRz0BJTAJUJQFO+25+DYs95sXUcPhSNOg1PXBoVz+oK+ffWt5OMNcIczcfp4//E0ZYV+LUSFCEU56nCeggECuwKKugCLCTEHbTQt/PkUoYgPro1/VRSjMHc+jGxgkogHJJ20EFMPbNi+ZOVNxHVirTFOmeQJS8Oa3eO57SvbEtiapK4yFywk3eykuHYWidZRR5aKQtJogEh7MFqy14Hqs9loFKaBojMd6+FTVGBFCKMzm0B74cFRJwhfwdJAy6CboB5o1q3h2OLLhsTpMjUitpODJdDybSyV9i7eqybRJj0JwiDDIEF7GH8D4kwO6gAL2uFimICRqsd6bDxBiC+oKHCKYhKVuXbSc5hwCKJSEzyW7AAWwmAQ3IEzHTmwcVj4o2crVOhhMzyi/fdDnjSsr4mNiWoAYDqTxoJahoC43FkVAz6d/CoLNybQIYamCRQirIcwVGAJDAb4AFUjURQHSpj9seCeiLRCKjYACFjpdTcvs5i6JaDIeaBIMekNry1PPfJIcvR1XQwUUlYkTDOPkD3MG54Y/ByGOeAkJyoKdQxgK8WjKUAALkxYeIBQg9qGw9694JZcnENyouUJytR7gNG/wetUWriZScKUjlkhekj1SUI0nhxMUID40KhdDgS6/GVf8VnQ0VujqCAl2FkMyRsEqCMPAWOBEgVGALTa6ml6G+tEhACjaAIttrVwr7U7neV/C28QlBbuGrGWJeFb2yEFf+TAnkGAoWFwQIBQFi5N/kjjrFbpahoJlTSNb4HJaj6JgEAxQtPQ7AMXa/iWrYW2gFbzsfVBB3EOCQ2YDisxEb1AKtBo62dFjd+J2rtQrezzqYhtBQYVQ4IABGZ3T/heFbnLI4oPe36WQNsXWAtcQxAKjsBi5QiukmjCKeR2u+IIBqWmg1QlA0WvoENiycHZ22je2aCXtLGvk6HGh/FBObuEDFGUNQRQFk4ii6n+jEDtuKuI93hQEBYAw7rIICtEVGgd4WLApWtocwGJGxbyT4IqAS2oxHwLEPWm+3d4ZvZe+e8i2zK5GsBI+LB5/KreQgi0e2uqbiCsMxUJE1L9yYH02XFkc8u+zsLQJOFiAsKl3EVdQFBYgASisgCL9riQ9xhdwuWDJCOWKLnaMorPT7TQ3AhQdk6p6O+qBpd9MfYyi+FvcxiS4ue8fQiCRQc7ktMlkdRRA8CSYKejUQtjGshA7wA/CgqQK6Do0FLsr+s9IK54mAekErOchYRQIg7ZQ0GOBGk8p6Lv3ldVHqaJGJZojeFVnFZbOMf7ZGQCC8qjGGUKY2dBSCsKeEFmgAKEodJ7AJGjWrFsf3R/ocf8PsUa7TyySDrgRBSd6mjUhElYQQrEpKnuGuDUU3QBFMen7zr9jwQbm5CchQ/yKBJGwkUW2sRq0bIkbbiFVwIOwAFcACcebpdvPov6ibHhnsAM84YJcgS2hoegZT13Y2LX0PJDQXFH9FyyISCNACiDpM37DguKgEhta45aCmkJY32RpEyKALx20ljIU2gcCLlRMOQ4oOiMQQAH9ROLDCiz81h4r1WR2cv0ZJ7s0/isUhIGoPyyj9O/xfRwT13LrXSFUU7GCsHyJxSIEUDisHd++Gz/ADOrMiQ8Pv3+GenCOdohr2BiRQLmiOAoqfjdEhEIjobj09ZPVULGlYLWD0qgJoijYhoWFtVbwxKcx3GLhMwicx0ZOWTJ+JAdCs0YhaWog/DbHyQdOIEGWmRCK6vAQ/o8MbUG+ov/DBIVxI8rFD5uYEhmjwCTgyVzBoyCfsGSuAATaoMKPhA+mqLewoBarV8XSt0ACJ4kJcGG5woFQ2Pw2WARGJOABIDQUaGECXfQlpNB5ktknw2IYLrhKMADsuEJ6CUpBV6xA4viKZ0EPp/gtMg4F+KE+QAAHaLLaYIcTHUnh5U53T3BuqJh6X0sUE9q0gQeSvUDCQbbRbGjbBu1/NsYo8PIMv0UnYqnGi7yoYhVFDBWMiHITcoTeFCDuHEZZ1GSzXraCZMEJQrtjL3qxE+y7Jm/ed3053Cxi3d27q1bdvbvu6Oql7y6c3jv5mNs94ZS7oAlgC8wCXIGEhhw4yxijqF4cRWXmDL1oO0khMAasmzDggEEgiflSjBAIEB4FhAaA6Dxh+vDhw4+FNi+6C9+fftUqeKLrjh2jVo1asn3m0h07dqxat3zLmtB093QQmAXD0OoHI2GiKECGuYKxEFW8qmACIgoxRTCxNhOk7zKxdMf0OoQEeAKBMLuHh+C+u/M3w/fP3aHX83frL15+runz8x3rrm8ODccwUPKEcPJjU+DSw1Bg8WnCCIUBE10tYYttIgkmHQkko4lNYQcOvsAoaoOwKYgn/FbzhOm9BsK3nFp7YtVzeOXPCwSo3i2ZOuozpx1Ht8zvAizAGRoLq5+gqEH0A39FUWXG43fAAAAAAElFTkSuQmCC\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"CO2 level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"co2\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"ppm\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "co2", + "indoor", + "carbon dioxide", + "carbon emission", + "carbon footprint", + "carbon output", + "carbon pollution", + "carbon capture", + "carbon offset", + "carbon reduction", + "carbon neutral", + "climate gas", + "fossil fuel emission", + "carbon cycle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_simple_humidity_chart_card.json b/application/src/main/data/json/system/widget_types/indoor_simple_humidity_chart_card.json new file mode 100644 index 0000000000..2d532855af --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_simple_humidity_chart_card.json @@ -0,0 +1,41 @@ +{ + "fqn": "indoor_simple_humidity_chart_card", + "name": "Indoor simple humidity chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAflBMVEUAAADg4ODf39/g4ODg4OD////g4OAhISE/pxrj4+PHx8fx8fE8PDyQkJCsrKwvLy+HyHBYWFhXsjbn9ON0dHSrq6uenp633qnV1dWCgoJmZma6urpKSkrz+vHn9OJ7wmFLrChvvVOf04yf043b79TP6caTzn5juEXD5Lir2JpwKyMUAAAABXRSTlMA7yC/r1EOHTEAAAR2SURBVHja7M9JAcAgDAAwytEP/v0OGdAlDtIAAAAAAAB+p4/I181+HrHX83b0Ngs8zmS0XCVElUiKXEbkNh/79bZiKQxEYfhqsajKiYCGmOz3f87ZOgxK202Ghg6Rzk/Eou4+yIVOyGhNyGhNyGhNyGj9AsjDflO+hMjL4ZK6fecEt6QcZKWYktG5NkQseZU4AlAqbuUi/yBRkSJ61oa8+C59AvEC4837eC/GV0C8wXtYKcaLluD3xf406gPZeKR3CD2EKrTR2ldkhFIQQwwUoXeW0TDBU9CoC0RoUwiF8QI5OiEJlRlb2CHKinpA4CwQ435adYFsXBBzJvMJUdV0gfhdAMf95QnoCUlBuKJRH0ghbc6BrLer9TnEXCEmFBo06gNZGHJeKqk3SFiQPkIMHdwJQWRBsy4Qpa1hy7S4QRIZP0Lg9qX8XRLQTjerDUHh0Yp7Rr5YnlMN6FMbgo2krfhWm93QrBcEEME3c87g/5tfvxMydhMyWhMyWhMyWhMyWhPyIxnvFpfy0yGyBNKStP7RkBTCpgaQ1XIxz4U4Rjnnl3kqRLngbOXyVMgfdu1uR1UYCsPw0Ze1SgulLVB1i4CoM3P/N7gdQRj/cWe2wYTnxMhR3yxao6iUPJ1P8J4hEaX4SSrzniFK4VRK0TuGdAPpGSXfIUT+iQJ5ZyBAQOH4Q4Shb4lAI6MUF4yWowkRWZKEAc7NtA5nIkhIzdqOJXBtJCMJkQsirYiUOMvT7RWhaJFFmSEjcUWixf8McdscgwilQwnIVOkZflJdmQwVEZkUVwltfiNkyxxjb82NGN9WFTN/YQilZ12RQC+kFD0pJG7JKDkJE/JfQmy79t1JSMy7reUc2LrhJ6rQc3QCmmOokFTQ9kZGE5FKgmdD1se1Fxy7b3mT9wF49oC1uG+p0Jn3IxFKSQz2R9FyHkVZokkfXhWp6KmQFduyCal4jU4XsmaHu2aUoiP0UqKxIIFnpIb21Dw4vlf7N2J4iGVvm5CSPXpV6bDjzxXHuC85WXDaHl2BoQxD3NkbwYIoEQND1vyBNoQ5Lmz1lePAs/1gi8o63CUoubhHFsmSdPo7XyWXw0JWJbs2ZMUNeywpijr37B9u9RlOiNQoZUKJXyHlsJCCaxxDClu7Tc28/tFpYzyglniBRyGeLZqQTsUlOjG7TVEVDjcMea77ipDVPsF7X3LlN30bu/6jsl5xWdjS3Tt7H3hFiOfOGrnLz0OsxY432PION4Tj+AvHJj4o2cafiNk2dxP3J5pDxcDtIzikOV6pD7nU7ZHtYZtvynbV7fpjzrG5MZHA0Kies7chiJlLy2zdMWTnAM/FZ8Eel+ScdIbHXh+C2jJz5XCiZuYal0JNocRL9SEPOZfjXH7lGgJFRuBpY/s1XhhSAQYacUimdYjBRhyySCSGG3HIU6aQKeRdTCFjM4WMzRQyNlPI2Ewhf9u5jwIAQBgIgqHlg3+/GEAAZcbBCbg9jSGnySjjCRntsi/r3uzvRJCi9rxdaTUAAAAAAAB+swByyaNbfaMtTAAAAABJRU5ErkJggg==", + "description": "Displays historical indoor humidity values as a simplified chart. Optionally may display the corresponding latest indoor humidity value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'humidity', label: 'Humidity', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'humidity', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":30,\"color\":\"#FFA600\"},{\"from\":30,\"to\":60,\"color\":\"#3FA71A\"},{\"from\":60,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Humidity\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"%\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "humidity", + "indoor", + "moisture", + "dampness", + "wetness", + "humidness", + "moistness", + "dew", + "water vapor", + "condensation", + "dew point", + "steaminess" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_simple_humidity_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_simple_humidity_chart_card_with_background.json new file mode 100644 index 0000000000..eb988a6ac2 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_simple_humidity_chart_card_with_background.json @@ -0,0 +1,41 @@ +{ + "fqn": "indoor_simple_humidity_chart_card_with_background", + "name": "Indoor simple humidity chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical indoor humidity values as a simplified chart with background. Optionally may display the corresponding latest indoor humidity value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'humidity', label: 'Humidity', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'humidity', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":30,\"color\":\"#F89E0D\"},{\"from\":30,\"to\":60,\"color\":\"#3B911C\"},{\"from\":60,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Humidity\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"%\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "humidity", + "indoor", + "moisture", + "dampness", + "wetness", + "humidness", + "moistness", + "dew", + "water vapor", + "condensation", + "dew point", + "steaminess" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_simple_illuminance_chart_card.json b/application/src/main/data/json/system/widget_types/indoor_simple_illuminance_chart_card.json new file mode 100644 index 0000000000..186dc1f2b0 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_simple_illuminance_chart_card.json @@ -0,0 +1,40 @@ +{ + "fqn": "indoor_simple_illuminance_chart_card", + "name": "Indoor simple illuminance chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAApVBMVEUAAADg4ODf39/g4ODg4OD////g4ODzaQAhISHj4+NYWFg8PDx0dHSsrKzHx8eQkJDx8fH5tID+7N/0fCD3oWAvLy/+9u+6urr7x5+CgoJKSkrw8PDV1dX0chBlZWX2jkD2j0AWFhb3l0/82r+enp6dnZ3948/0cxChoaH6vZAICAj4q3AlJSX3oV9DQ0Pf39/Q0ND4qm/1hTD7z6+goKCSkpJCQkJ25ZykAAAABXRSTlMA7yC/r1EOHTEAAASuSURBVHja7M85AcAgEAAwjm9mx7/PVgYciYMUAAAAAACA59QW83a9/o/Y43oraulrJLBbmSOFyBKZIocROc3Hbt2stg4DYRhefWFGo7+NMLKMwZCCMSHt4pxz/5d2lLgUipUsHbnVi2E0ePVgG9wgtdUgtdUgtdUgtfULILHDkSpDEoDrn3WuDQ4gA6WxbTzj9ZUh17jevBh8ZugJpIZnV4acXIqX65withBrgc7my5oB1nDehnzdNuQ9o9iw6TzgjWHc1iJ0FwhwmYimvyhAtMJtqNALqV6Ch7h1s3BBi4IlUUEBEhSd0VEv4rFpH8g8UW5KTyB5BA2m7g4Z4ekMZ9ER20x6IxhiaIFoeDLYtANkXjq9Qj7G5RkkC5jMHaIBchi0yAoxhCjIeeqVIodNO0DSe3J3yJLS+xbSP4aI9vYbxLOnaK1lFNrj1UqX7Jhi6RuJgVnKEJ8t7guShx8VVM/evQyC00TTiBKEhUJfhmQFKbKfkNsmDBYi9ToIwrI4lGOPR3kubFxk7AU5zTOO1EPIxz+FI/UL/n4PVoPUVoPUVoPUVoPUVoPUVpUQZv8DIFYFIhLNx4Z4RSEa8zYSuSNDWMj59TSSOzBE0/B1jtQdFvKfHbPrVRQGwvDV5G2hfLVSkYga/MrJybnY3f//3xapdOthGzHmEEh4EuOMV/M4dKYasZOTBXk6V5EgJIeYnWYqwln0zWumIoJxemxJNBsRvo5TO3pzQY8EwUxE4oA1dLsvYdve4Y+nJ7KN4/6QCs9xLFjIzYMk6DthMDGRNMkZY7ngj2e7TO9bUESngJWc+qrrHxLRl6O2yYdSAz1KFkTx2nz3ll2XcZE3lklKfcqcvytyBFbUkMFgkmsT1ZIMGTB0HEXm8cpLskTuTOLcd2cJ+Zsi6l77ZydiYnVoXvo1kdhem9Zs7ZQ48PYltm1XT0HI8mDDXxTJuibUWMkbmkgCGdEFyF4TEbkNy8CzNLxwwZr6GwlWimQTmCE3XERCVUbkgMyeCUC3n+wdkVaRdPvmIRQ2PLPECYbBo10QiDM3SRIalYEiCoUyIhUKulPjt6m/ckQOqHQbSvKwdreBaMtIN6yk1/Gp+EWypuS7CLCq1f7DaUUBaKcjFTKSwC/ysQvJIWEsDxnbpERvqJTDROQV8i4iYVDaFZGdiGmQ/ILS/qugeMyjjTin9BZpOkykRkadSK0yecmAzCeiFa5AQT4itqUfxS9SQJERsRxQ+UToaAacB89lfAwR2SgURVFhX1z+uUE6Io/jtwKKgT8yLGOIFLBkpKW2Iiuo3tQyb6g0efCs5jFELquWCmr1pyv+C7B7pH7YI+3EqvDpOZVilIb494g9I0ezza+3RAOf7SeFK7KHukVH+g/b0rv3xhUxvbgqQMm2chOTI1K0Xgp76pOwMCYPo4tQpgDspY2rlXZEpEJtGvfRb8eTtTeKiIuUuhc/JR3Wjsn/Gx+HT9oxE5Hn7ZiJyO5pO2Yi8oxFZBGZC4vI1FhEpsYiMjUWkamxiPxt584JAIZhAAbazjN3L3+epZE6dwxEQKcRcpoduVrYMRqsg9Z6ZlQ2mAe9WRE199/lqAAAAAAAALjNB+/brAXegFpbAAAAAElFTkSuQmCC", + "description": "Displays historical indoor illuminance values as a simplified chart. Optionally may display the corresponding latest indoor illuminance value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'illuminance', label: 'Illuminance', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'illuminance', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Illuminance\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 400 - 200;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 400 - 200;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":100,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":100,\"to\":300,\"color\":\"#FFA600\"},{\"from\":300,\"to\":500,\"color\":\"#F36900\"},{\"from\":500,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Illuminance\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:lightbulb-on\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"lx\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "illuminance", + "indoor", + "brightness", + "luminance", + "luminosity", + "light", + "light level", + "light intensity", + "lux", + "candela", + "foot-candle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_simple_illuminance_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_simple_illuminance_chart_card_with_background.json new file mode 100644 index 0000000000..b698154dd7 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_simple_illuminance_chart_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "indoor_simple_illuminance_chart_card_with_background", + "name": "Indoor simple illuminance chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical indoor illuminance values as a simplified chart with background. Optionally may display the corresponding latest indoor illuminance value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'illuminance', label: 'Illuminance', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'illuminance', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Illuminance\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 400 - 200;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 400 - 200;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":100,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":100,\"to\":300,\"color\":\"#F89E0D\"},{\"from\":300,\"to\":500,\"color\":\"#F77410\"},{\"from\":500,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Illuminance\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:lightbulb-on\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"lx\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "illuminance", + "indoor", + "brightness", + "luminance", + "luminosity", + "light", + "light level", + "light intensity", + "lux", + "candela", + "foot-candle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_simple_pm10_chart_card.json b/application/src/main/data/json/system/widget_types/indoor_simple_pm10_chart_card.json new file mode 100644 index 0000000000..c5848dfabe --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_simple_pm10_chart_card.json @@ -0,0 +1,40 @@ +{ + "fqn": "indoor_simple_pm10_chart_card", + "name": "Indoor simple PM10 chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAilBMVEUAAADf39/g4ODf39/f39/g4OD////k5OT/pgDg4OAhISH/9N9YWFj/sSD/6b//3Z+QkJD/vEB0dHT/0oDy8vL/qxD/+u8vLy//x2A9PT3Hx8fV1dWrq6s8PDyCgoL/2JCsrKyenp7/zXD/78+6urqdnZ1mZmb/wlBKSkr/tzD/x1//+e//46/29vaFHg9VAAAABnRSTlMAIL9AEN/GQiaNAAAEpUlEQVR42uzPwQ2AIBAAsBMQc3/WYP/1TJzAJ5B2gwYAAAAAAMB/7dpc+xq15LO5vGvElbNvb2SNMvoBZonsR0iRxYisRuRlvw5WJISBKIruXsGjFqEoCNEoCirz/184tt0wNrSzTZzJhUC2B0Koqq0Gqa0Gqa0Gqa0Gqa1/AskZrxYzG3tgsh57Zg6gmwF4UBMU71dIjj1eBYawcYFRHwJSEOJxt2HaUvkt8xLifnYgEIAqLEUHLD4gnSrgzBB2KNo1xGWlHo4zZFOYxhmII+UJeyI4oWDXkF6ZJFBxhoRgnGEaFNPQ3QOykofkDaJqGTB1dmrTPSAeD8n6BsGRKdQoZ8gC30/BriFYuZf8M2Sh4gcC1T6w/P/7GQLjnpxpCUfjBv/KWJIAsA2ADIwZxbuAIJCGO3UFgeTyz6XNWneuQWqrQWqrQWqrQWqrQWqrQWqrdoj7X4B4iGQa5e4QSdzWdWOSe0MkDYegH5LfGvLNntk2JwoDAfg+LXsJCQRM8A2taItv///33SYxp3biFTrDFWd8ZkrCJu3sI7vGqRuWgGPONk8tsk/DLE2fWSS5PoecTZ5Y5I0t6Bpq6/lEJotL/h/1NThLn05kaU8Op1IVADe19WQiC7bfLesqsbnPAW5qa/Qik7d8k0AgrScAST37SFkBN8zSsYssKkbk4EnY0g3Ffm9DkdrqKZIJTwMebqdf0woTiUqFWnGIklT1nPIOlVOwBKLM2epbIiV6puARdvolHA/RqG6OqCDKymeeVl7L11N8Y243rCpWFUl3kRaVtPDg1UlkjVkkerJ/BcU/T+93NvdDAo9NqnRPFnlRs7yziMLy7jUV+oGI2crrjcCLOJdbILbSeJVS4Tre6Sz3Yz0jhV1VwGPmRfqRT1wHsTTpKHLA9f2dCCIZIhDlZdCIgkLORrr6QaS0KcozTUPjvJQQJcTYhffYRc1mFdsn0In3arbrJkLZNKrNQsUIiIvQVSuBF5GjqywKuZigJY0UMtzY18LE6+X6vVJRLKErSb3vJHJCz8G4dyzkcRGOqIwV8CLCJUsKPtbSBk1XiQcpNT5qke/STeSAzVke0XXKka5xEZrbXpBeRPot7pfCUmurrUFEvX7QIoOKEMa3vLYpCfNApKT1IGKFz3cinIapaxt+PpsHp8N8aJGQNOcCG2lLo5X8s8gUxVXkRDdRkUC8Rf6bCP0Eys8ize0TyXDaW2SWDiwyRXHJ1pyVQ6NWWZCL9ohC2VckZ8uBRc5ozxEuQhr3PdIAGGFFTtptaK0IJ/eeIvn3W73HyY72KSCPifgldCcMCjeXFC97iASPwUVORySEhJiIQNRtaBWaN1bkgLyPSPAYvtmNlByucG7+itCSMZyHT1XmjLjlqO52unU/xCnIY3iROEHkRm8NRIsCttkWuhI8RiRyQFGWyhZTT1bkMSYRLtB//OgtsoGfFuFZBjfI9TozMAZ+/xrVv4NeIi+R0fESGRsvkbHxEhkbL5Gx8aedO7gBEIaBIGhsEcl/2qD/9mgBiQdJNNPBFXBryGwMmU1HbpAOGuPKqP7/1vnZ3UdEnetnqbJ2CoUBAAAAAAC88gDG/flsHc1jFAAAAABJRU5ErkJggg==", + "description": "Displays historical indoor fine and coarse particulate matter (PM10) values as a simplified chart. Optionally may display the corresponding latest indoor PM10 value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pm10', label: 'PM10', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'pm10', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM10\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#80C32C\"},{\"from\":50,\"to\":150,\"color\":\"#FFA600\"},{\"from\":150,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"PM10\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:broom\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"µg/m³\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "pm10", + "coarse particulates", + "coarse particles", + "particulate matter 10", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_simple_pm10_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_simple_pm10_chart_card_with_background.json new file mode 100644 index 0000000000..e51c84156e --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_simple_pm10_chart_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "indoor_simple_pm10_chart_card_with_background", + "name": "Indoor simple PM10 chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical indoor fine and coarse particulate matter (PM10) values as a simplified chart with background. Optionally may display the corresponding latest indoor PM10 value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pm10', label: 'PM10', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'pm10', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM10\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 200) {\\n\\tvalue = 200;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#7CC322\"},{\"from\":50,\"to\":150,\"color\":\"#F89E0D\"},{\"from\":150,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"PM10\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:broom\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"µg/m³\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "pm10", + "coarse particulates", + "coarse particles", + "particulate matter 10", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_simple_pm2_5_chart_card.json b/application/src/main/data/json/system/widget_types/indoor_simple_pm2_5_chart_card.json new file mode 100644 index 0000000000..97312fcefa --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_simple_pm2_5_chart_card.json @@ -0,0 +1,41 @@ +{ + "fqn": "indoor_simple_pm2_5_chart_card", + "name": "Indoor simple PM2.5 chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAflBMVEUAAADf39/g4ODf39/f39/g4OD////k5OSAwyzg4OAhISHv9+WQy0Z0dHSQkJA8PDzP6a/f8MpYWFjy8vLA4ZWv2Xug0mD3+/OIxzkvLy+enp6srKzHx8fV1dWCgoJKSkrn9NjH5aO6urq43Yio1m5mZmaYzlPX7L25ubmQyka17GyFAAAABnRSTlMAIL9AEN/GQiaNAAAEqElEQVR42uzPuQ2AMBAAsMtD0NUp2H9TxAaUSWRv4AAAAAAAAPivl831+LSa9+byahEln7G9mS3qHAeYNXIcIUUWI7IakZfduletGIbBMLx9gg9kgTTZzg8EAr3/K2ycdjgH2iFLcFq/YDAIDc+m3hqQ3hqQ3hqQ3hqQ3vonkFrx3aaqywxUnXGkGpDFVXCW2hTXuhNS84zvElPauUHpACZSYGtdc6CVvV9IxKsDiQDcoZYD0EzZOKO9Fidc7SZISKE3xxtkd6jnAuSFgqPKwJFQNQUudQtkdpokOl4hKSkL1JOj2nRCxBJa87qU7LjWHZBCnpI3iLtWQD04HT9Kc+jrTuBSd0Ain5LyBsGZOlwpDRLriq/mDag9QlDYJPEz5IOOBlnzNk2CMmFiETdc6h4IlEfySjOcLTtirfgwEWsVWAKS0QUXuwWCRCqe1G8QSBU8qX9yaz2oAemtAemtAemtAemtAemtAemt3iERfwESKZO2yNMhYtxL2WnybIjYl2A2i0dDPtk3tx1HYRiAvhkrDpdQIKH0AtNO2f3/H1yPgcxFYaesxA6Veh5Ca6LKR7FDWql7FYFwUPuHFjnnMJLnjywSva9DonYPLNKoI4CvrccT2R3H/F9T8KT5w4k0tVKpqNQxML62HkzkqM6/mrSO3nI/AONra/MiuybZRzCRpzt5gLzmKgYI1NZyEbLwPzjWikn8TtXIJT6fJRSorWUitnCIuiRvpfUFvueqQ/amQlcRBInq9MB5T5UTqwiCHNTpX0RIswar3Ka8+H0J30J4C0bdpcQKgpyGzPN6XJAYZjjJqkWnWtVxdLdIic4AFIgdCAXeJdJhFlpdQwD4+69P7706+EPJrEmdn9kiiVOV3CvisPQXhlC7GRHbGvBoJBDItMC0xo6FWmEX7vQxp52cD1/qGOY5xPlrspMOUnl0l4gtSzMkNjTGDTs9iWSI4xoBI63EIZlupH4QOW2OUsb3hg+gSusCQrxMe+wxVWmtzhHcxb5OXxZsv4SYDRWjISQio6s0jiKlTOeQxDTfcsghSxbghjZcL77rmzhuFpzwzwtEStRj51NIREwry/NGES3JssIQuwKQ49HgzRiHMy2y+nd2ybUffAoIicjrVkpKRMwwBXm6v3XFCuCCiK6baZH1RTrOaMhc27AIDw68CAv3n0RIYhWP1Pd25ulwWF2kHz2IO94YwzVi6KtIib/fRSxqCIp4gi2ytkg77bcZeoqvIpePK5JhuVgkzdcWIT1l0FeCQ1dlowgFe6RCs1QkUc3KIqSlMTyfe+QCYDUij07yvL6JEGpYKJKoBFYWuSE6LbQBEeQF4nHYELS8NhwvlomIx9oiGidMQESz5tW3CroLT2N3WiQiHquLkMf6iPUi1hhriaZTle0RW8Lq00y57y8BYvZYXSSMFwEPdSA9oqHNWliAeGxI5Ia6KCoppmWc2GNLIqTRHz+Wiezhp0Uoy+ADpuuybfxD9sd/RXmKPEU2zlNkazxFtsZTZGs8RbbGn3bumAYAEAaAYKBhqAL8OwUJjKW5c/AGXkg1QqrJPsudyAYlO8fdUq3/t1QzOo3CAAAAAAAAnhxh9Pec6lVI3gAAAABJRU5ErkJggg==", + "description": "Displays historical indoor fine particulate matter (PM2.5) values as a simplified chart. Optionally may display the corresponding latest indoor PM2.5 value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pm2.5', label: 'PM2.5', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'pm2.5', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM2.5\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 150) {\\n\\tvalue = 150;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 150) {\\n\\tvalue = 150;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":35,\"color\":\"#80C32C\"},{\"from\":35,\"to\":75,\"color\":\"#FFA600\"},{\"from\":75,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"PM2.5\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:broom\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"µg/m³\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "pm2.5", + "particulate", + "matter", + "air", + "fine particulates", + "fine particles", + "particulate matter 2.5", + "airborne particles", + "microscopic particles", + "respirable particles", + "aerosol particles", + "dust particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_simple_pm2_5_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_simple_pm2_5_chart_card_with_background.json new file mode 100644 index 0000000000..5be8e6c4aa --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_simple_pm2_5_chart_card_with_background.json @@ -0,0 +1,41 @@ +{ + "fqn": "indoor_simple_pm2_5_chart_card_with_background", + "name": "Indoor simple PM2.5 chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical indoor fine particulate matter (PM2.5) values as a simplified chart with background. Optionally may display the corresponding latest indoor PM2.5 value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pm2.5', label: 'PM2.5', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'pm2.5', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM2.5\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 150) {\\n\\tvalue = 150;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 150) {\\n\\tvalue = 150;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":35,\"color\":\"#7CC322\"},{\"from\":35,\"to\":75,\"color\":\"#F89E0D\"},{\"from\":75,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"PM2.5\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:broom\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"µg/m³\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "pm2.5", + "particulate", + "matter", + "air", + "fine particulates", + "fine particles", + "particulate matter 2.5", + "airborne particles", + "microscopic particles", + "respirable particles", + "aerosol particles", + "dust particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_simple_temperature_chart_card.json b/application/src/main/data/json/system/widget_types/indoor_simple_temperature_chart_card.json new file mode 100644 index 0000000000..890db4f9db --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_simple_temperature_chart_card.json @@ -0,0 +1,31 @@ +{ + "fqn": "indoor_simple_temperature_chart_card", + "name": "Indoor simple temperature chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAh1BMVEUAAADg4ODf39/g4ODg4OD////g4OAjTMchISHHx8fj4+Px8fGQkJA9PT10dHSsrKxYWFgvLy8+Ys7I0vE8PDyRpePk6fjV1dVaedW6urqsvOqenp51j9yCgoLy9PtKSkqrq6tmZmafsec/Ys4wV8tLS0uDmt9ohNhMbtLW3vQxV8rW3fWDm+D/9RHFAAAABXRSTlMA7yC/r1EOHTEAAASnSURBVHja7M9JAcAgDAAwytEP/v0OGdAlDtIAAAAAAAB+p4/I181+HrHX83b0Ngs8zmS0XCVElUiKXEbkNh979rIiNwxEYXh14FSVrmAwRhi3F/P+rxi5e5huj8cQCHHUQf9GRWn1LQwGdUhrdUhrdUhrdUhrdUhrdUhrnUJ0jninTiEpDC8Sc/cmNNspxN8W9wIJYWEIN/x24hVXdg6ZksNrjndRnBQQlah1BEy2o1YXgI4isFhHK5zMBFCByTgBEgVnXQ/RvAzeQF+PJTFB6BMDsObEgpC5qk8rZVy4jJGA84jMC1xOw4xn10HC8gMkJSA70GHihEIIRzjaRIXzCINidMBHgFDxBVEob/fVs4sglslwhHifUg5ghFAQN4hCKS6n5ImQAJTV528QQJjSBwW7roAE1uQIWUVEv0HGCqHU7pBIsfQDJIqI4dlFEG6FA2T2irKHRITBhAVS7hCXVX34vB9tfUAszzCHXZdAMmvzAWIrOewhnrlst8wPiA7MS4INjLaNDwimgfwXH3shmRXHbL8Uqtpjb/hM7et4Lvdz7SIIbmFWnLSHoIk2yJ+krpFXif//7/fd6pDW6pDW6pDW6pDW6pDW6pC/lpXV+xTk3SG/2LG35VRhKADDV2uyEkhIEMJBoNbiqXu///PtoBjKFqhDvJAZvpk62OFi/SWhylYhF0KaV7boEIGSQmMX4W7BIT760GIfmCw2JDEdlhfx14RsYgDQcSeHm/yU5tCq9qc0PeUaXkJG8APFrXtIFX+RsKkhnRSuYmL8hat9Ta7C/WsuyK7fpTy3kGqfEqMJuXy1zMQFNDQJK30ml3tTfb6e/IoSEfUHp+i7hZjJ2pBOQYiGxqaZOSc5mNcmoDLhJui7ghmokNI+dmIPc0vlOYakcd4P0XZlXcipydoAQEhIbNNjmMZo8vAriUpKbOdnPGJg2TSXkI02P/2QPSG5zQy/yLndPxquTPYZpvgKEXn/IZjH1baZVuAhY2yrlC21SkzcNvtDSNi9q4q6Pla3uBRudFEUMM47oAxoJpCz3pQU7v/8DDkwsxdx9tKQ3F6Q/rY5wlNEeztK1AGs4Me6oUEwPHCifpYw55DQLiJremOM3H0yDOxQPIInJBEKCgYtOSJykbmEmDcFzA8R3ciSe3BzUAyewYRC5Fyhkn7gf0bIA292SEHIZX4IQwF39H5c4g6e5GWlEOV9/J1ELti8ED18TyqeDAmQgeUjD2iylejDXFQglrNCCrvVe47dgqu01jCGy94YH2gcMnDAxHZOiK7tYU9uTunO/oYR2f+LiNGMgas5Ibn9y/dVNSEbe81OMOIzAnfuIfbeO7y2Qt1uIpJPfMdw5R5iL8igyiSGcZ4fa0JSGMZ45IGDF4akdgE90CFppRUMolwxcOUeYg/G6Di8ZuxhkFdilMCEd3qupTd/qvGPvL4Hw94vZBTlKBmMWkoIkxhRmLCQkK1SPkxaSMin8GDaQkJ+s4asIUuxhrybNeTdrCHvZg15N2vIv3bu3AiAEAaCoHhOzuWfL0HgCOjOYP2tqcaQajLad4WMUeTcvuef90SQos88XRs9AAAAAAAAXrMA+iCoTuC9Gd4AAAAASUVORK5CYII=", + "description": "Displays historical indoor temperature values as a simplified chart. Optionally may display the corresponding latest indoor temperature value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'temperature', label: 'Temperature', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'temperature', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 15) {\\n\\tvalue = 15;\\n} else if (value > 30) {\\n\\tvalue = 30;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 15) {\\n\\tvalue = 15;\\n} else if (value > 30) {\\n\\tvalue = 30;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":18,\"color\":\"#234CC7\"},{\"from\":18,\"to\":24,\"color\":\"#3FA71A\"},{\"from\":24,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Temperature\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"thermostat\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"°C\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "temperature", + "environment", + "indoor" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_simple_temperature_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_simple_temperature_chart_card_with_background.json new file mode 100644 index 0000000000..b9be76ffda --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_simple_temperature_chart_card_with_background.json @@ -0,0 +1,31 @@ +{ + "fqn": "indoor_simple_temperature_chart_card_with_background", + "name": "Indoor simple temperature chart card with background", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC/VBMVEUAAACho6VydHppd4G3ra+TmJyLh4NiXVe4r6+co634+fvu7u/w7u/2+Pr29vjs7O3w8PHu7e3z9Pb08fHs6uvy8vPJycgjTMfn5eXs6ej19fbq5uP08/Po5ubx7uzn4dva2Nfo5OHm4t7i4N7p6erKy8rq6uzm4+Hj4eDj4+Pg3dvn5OPl5OT7+/zt6+rh3tzk39re29ofHx7T09Ls7e/l5efq5+bk4uDx8vXs6OXk4Nzq6Ojc2tnT1NTy8PHf3t7Y1tXn6Org4ODNzczS0dHW1NLn5+ji3dnp4tzz8O7v6+jb3Nzg29fa2dru7/PV1dbS1N/v7OqzrrTb2dSwrLHPzs3R0M4QFRTX19jHvb/d2tbq5N61sbbg4ePU1uDMzc6YlJUaHBzV0c7Q0NF4X0/JwMJQTkysqbBKSkgMEA/Y1tK4tLjh4+bGyNK8t7uwrbSLemzQ0t7k5um3tr6srbTOz8++ur4IDAu6ucOxsLirp6zIzNhjUEPd3uCBfn7GxcWOi423ra3e3+Tt5+LLwsXCvcGPf3HNysrSxcPM0NzDur28srCgm5xDREI3NzfCw82joKVTU1Q+PjzQwr+Rj5F8YlN1XEzEubqxqqqEcmBXVFDs5d/SzMfMu7IwMzQqLS3DwsTGsKFxbGvMz9a0s7ympqavqKRjYmNaSTyipa6glY2YmJ2GhIWCblpzcHC4vcm+vsi4oY+KiImag3Crs7/Pv7ior7vJtqq/tbWpkH2Id2bn6/C2rKZ4dHRdWlc3W8qkq7WSiIN6eHnZ2uCYnqbAqpmaj4iThHdrWEonJyeainuKf3iTe2bS1dtsYlqmmZCon5pRRToZJyyyu8OwpZ6wmIRkc31VZG5saGY2Mituh9FifMt8g45ca3V9Z1Y8ODGFlbCIkJyPcFk1R1UuKyOGmtdvd4JJW2agrdlra21Ucs5KasqCd294bWWptM/c1c1LU1svPENZW18bM0O1vtySobRqia9TcJUnTG6To9I/YIJtgZYMOFRhep9Zdcp8wtADAAAACnRSTlMAICC3v7+/sK+ve0cJVQAAJQFJREFUeNrs08GK00Acx/EKCnrxEPAie/YJFLz6AouEeCgEKcJ2CrEdtCE0EWoqLUKtNpWFlh4UarQMKbUsUoQlpdRePQmSg5e9CHsTn8Df/Kcp4is432m6u4dN8sl/UtDpdDqdTqfT6XQ6nU6n0+l0Op1Op/vvunApUR0PpuFxGJ6Es9kJPrLwuFQKkwCxgFmcm7b5skJr1yRvPp+nabM5w3/gVNtqv99fLD5+bDQadXnU4xiHzIzOt1FgWQxnZPhh5ZlY+DBcrBRsoiiwTYQr2rbtoJpTU3WLwyLqFrvD4Qt7OgtLo8sX4NiqjuneIcGt9EYt7+io+Px5++joaS+ii4Hhm3bsVHaRpfIvBCcAIxJVSOCARFWHBRJYfPEziizOOEO485yR/8IYIAwQBgAxHHx2BK9b61JgFOEBJDsBpDW6ULgiVMn7F63PxXb7wb12x3NMOjPDWf1SxCnbt/FUnXK5XCnvNbj/yXwyySQl3ShIFAm3ShIES+6BBZ6YAQIB45yeDieLif6aiAWIBQEWzYMgnjIMpQGPGQFSPwfkQ6t1sQDDWqzXgnEx/d1Tm4gxl7kuw3MLABE+hW0hbwSQQRkRprxJ07nEZBgIILMTjDMK3PidlCDCEGiRzwYQ4Zrc5dBAgSBSuwgHTSQxm1FgOjU4ZBJBjv0wwFB9jneQUWG9T/z+tRWC9q6r8mkiQawUCJDBeKwcZQlpNlMMI8smgGzUK9J7z+PGu3fVJ1WqD5AiNeqflsu18H9Egps+dxm3OFKzkA4bEtpbid8MArNGCCwwPA97CoKOmga2PL6xPjtzXHLUarUKy/VStoe4bu7g3MXV/ITFkhFLBoJjLCPMABAosml2DsjOYdUW1UePvn59dPf+/S/Us2cPH9+58fjxKTrs/xCCwcGVgtsSghQEEmtEELuG1J7yvA4FBwJD1m7DMnTmNJHRqCAV8kktCcJ2Cl8FSJy4MOCjHIvxeCUdA5KM0zRbrVb56x8meEECp1P9cvW67MA4MIzrxjXDMPAHvg8PTw+/nTGBJ+QriI1FAJKo6QQ9pxkw26MI0vHIgbCpYIACBzTDehqGaiLff55BQpbpr+3a/csRS0m8det7BVqhMVXGmmSrystXb9+8evX65evXScBcjqddr966eU1ajIPrBmEOsAzj9in6eCYEB8N1pUNlqtfacUDBRHq1WWA5Xl5HBQHV3geIt8m31h/G7PenrSqMA3hfczvaQmnKKIjCCoXS2Us7XKB0LazRISBzI6irthInyWAqyqIkUCCSRamwEpKRqCuKlbgRf8xFZ/wRN+cSNOgiM/HFfOEbExJNjH+B3+c5956VC1O/Y9A147af+zzn3nNO37v2+yXECIGCQxC+FUiIlCAEOT4wT455SC5svj9+4RlqQ/zHuaFvAEA5XOBwRXbfF2AI+pVzWEDknQKBBL2Vfn716cMnhEFnLC1hSBCDvqTk7DRDIEFr3QKDKS+Exqbig8GticSDkUikOsKJeePtR0/vcTgcKQTfTp9OpZamTyF4paX2JYdjTzgeD8YibncARdlVUNCQl4JdhYVFgWAsaEj57XgpHmfY6yyPVXPsW+Oz+3z4jthLm954ZGmcIDRGLn1y6xN2bIwNDg7Hqt0WS0DGwgkoimKlKJbqck+0rqWysvIApZIfHGxsp4S6D/R1doSjHmez26ZYS9BQheQwSIpKrDisMTY9FrsWPKWYEav4Zm3dKb5H5sYXtNa6dOkWD/WvlwcHgwRxM0RKyEUOCakChAKHAdKtQWJuCyAuQHZtg2DoKxKyPXYZ4BRI/iOVc6s0EZmZmTFtbNwSjfVNkCARQCz/WpGqsAaplBCSNApIR9gTZAhJCoVEMkRBFBzSLWPfHikBhXNnkM+xmlhIT07OTJo+2Li1sXFp49KlqWBQgxjCELIoNm6tcIuQ7ARBSeLeWLUlgJKUoCQkKcAXB47dorPc1TLbBaWlUgIKYWyMwU+zjM1so9iH6N41iTCEMzXMEPSW2wgRDIQgtWE/QYySRkj6RG8FI2jPLkgYoqewoJAdCjsi1c3/GiGB5V9jsc9hepe4idFu+vCD3z7gDAKiSzhSxA5ECQDiJUinrmghSEiHsAQQlMQiS0ICVpCDCgJHJEYpv0P2SYgx+QYeT56nMWH9cnx1ddU0N/fb3NzQ0PJyMMYlgUREWmzsQFARd4wgsiQt6DKuiEg3KB1jcS+XROniC5cMHFSQroC7mgxOjleLMz9EEa2Fd34nioUl4dwKbmsriGlo6BoYofVvhiPDQQQSioaxu+0WdmiD3d7srA3X+VtQE+GgkhyUkhCGSTiO3hIlcXFJdm2BWC2AlEsDUu+tR/BNj7N8XykctoASQBQKHukCRQ8eBzy5X34QMS0vX1tefmV9fX04EguyRFD0sqAgIJRoJbFXOz1VVBKEHHklaecRTxCvDmGHFimxEQQSnbE9ThSEXzY/XV36FcwqYlaQwXf/+FNEQh6XEHbIawpuS+h2l6sEwXWLBglDZGiQhBjS39/eeIAguBvBwRA52NlCEAy0ZpQE4WIYKFyQ5lKLQmPsvrzw65focWk/hyf/0ALIe1joYnU1Vs0SyaBQgc2taBERSOyAVNVJCR5QRQ6yoxeQvk7cESN8/dUvvyLy8ov+JAni1FPvvJ2ycjSWz2bGpWJrmKM9xnxUPBU59r0W0/pyaHmd1gx43zFEVyykxUSBWn23CL+LZm8TQfyCsRXS2x6q7Ih6y7kgeCf5DnFDpJJgkOKytI+y7XrFafb1mFtpfBmyWyTv34Xuk99fgeLKlSumuXNz2ce/GYvH3e5qUQy+3a7Ozq4SRMEh8avHchx0eLMTEEgkhEY79RZ31sHKcK0XHc6NVWScNOJmshtVhQQUDkDGNJf6fBVWcZ3g4CBylO3Sovfq56euINfPnz9vmssO/fQ4Zr1Bvk8JBpJZzWQsFioIn9iVzIA6kMm4qLdqqsKQUHRIN0OQgwdawh4nQzI5MdcyQu5jiEiplubSvMBh47PHb1dGAxieCkwT5ObNL26alv8K/YW1aHAQEBELJbOayNhsPNLFqVlSl9Dh1tSJNm9tqr19MeU/sdjr96d6T7xLFXHkjjQ2OqZPj4Y9bSfabUq3Oh7b3eEusEUbGqINIaVBSaUkBBJDSu0+ETAIUuySDhl2GCFLV65fv37ziy++MK3/2vfrN5jBBzFFBIIZEGRWVzJ8rePOkpDN7Gx2uvb47Oza2srs2mxvXXL2XHLeEVpYO5e82phLriWrxrMZNaHMqtnxTjVXkFKjUXVeTUXn57MDll0MseVDIKBv9JMesaPCXOLCixre9p0hcADyzvrzY3FMfAOKPtkNYJaWWdncAZJTu+2bx5uOv1bXu7biX1RP1iXfbxlNLoTOXQ3lkqdyyYv+6C+55qtZxapeLJKQCUX5aqAhmr2A2S9XhGPDF+Kz+bbGhs5CGxj7SIPISWghQ1ITiUTiixnMfnmXA4u6YYUgcq6bUdXXtkPez25uvpZtOn68rm523J9SF+uSiZbKc1dzyYGVTHIhl1wKR6e//HtWJYhLQlINDSr28rLjAqIw486pYAgqYnRsHzUBhwaZMY2NYYTEB4d1SIAhZ1QkoZgxRLZAZs+k02lA9gJSdxtyOIml88L0KCCO7JcjV9UAICVbICvY+1sqlJAdBT07Q+RVS5aEg2d2BYYIMikgU2NTg0GGBMCQBeGSbIUU5lSHpXe6BpC9GkR9v2UpecGxthlypPtGk6nwKfVYbFO1KNmLJTF1ouCCBvlqwNaQc+sQ4VD+f0XYsb1IEnJ5xjQ1RRBsoCldkBghsrUKAMFQHVdn1dV8SHL+3NpANwb72trAE4CMhWazs6+pURzhatH76vyABonOZ+fnxxhCJwwKSr4GqyX5qNUw2EVBDMOGnuoaupJIp2cuEwSh1Xqky6qI0OQwQZC0xQKJBtkVdgPicpz0l9cfcFTtTT1ZV4e/amL0RF83XX4PY+a7FB6Le45NV/dHA9ZTfUW7l/obokAoDUiqHwcQFQFEkcEbFwwO6lFBkCIDhCMhDfqTXVSR9OTly5cBQQiCCaZwUJQzmcyC3c5TDYLw7xHEarPvq2+qrdq7t46jYoz0dUMiluxjgHhjtBzBrd2wjYJDaBXJh5jzF+QV5godwpctCdEoxooAcjGRBuTsZVNclCQCiJUZcg2FqVcEEj45fKxCDeKsaaqq0iAri1sgWOnK5QjVMt8hIYhkGGIjzP+FIIBwa509a4qTBBetiHRQ4GjGZhk2RGxWgogjMcTXDMjtkrR0SoiQABK7DWGJfGUjRJTDSJEQAhhbywiZSGuQwTiPkUFAzAgQvPDgbQac3Zhb4UGCiIoovubyPIhfQoQEa3beaJQQcujzPTlGdMedIiEyO4x2ekpUBI6zJqwNl5G5ZbtZQFoZYm/2RsPhcNRbjZJAwuEFnr25nAYJJAYIW8Tug4CUaOsqnoDzASTkPxzFvJYxMnaGpNNnGIKNdmxOn/30zVIzB3uTBHFjA6ujszPsKXcrOKpIkQsQH0GadoDoi/bbkCKGUCERWATEKiD0Wv8fQgojBIP9PrTWGUBefdX022+3Xjh16q1vv329ogd3VpZYzTZ7eW0YGwwdYW8M492FNaKLlrutZoZQbxkhtNTtx8Yp1roMMfNcHO0BRwnOjkuDlHTJpbeMuVX8kRCwDRBi7ACZE5BHALl27cLJk2+99dbrPT2YsTGFhoiz1o+1Hy34IrSABq8VrwiHfTuEHGCMIO2hvg5AaGlpFm+eHVYrtT1DUBLdwYfkUHHo3/kV4XdeJACcHW6I980tEASfANEYSRw7Achjh3hV08N7lBgiBAl1U8tjL1fbtKTJaqkBUikgcDyB9If6UBHatLBV8JlFABHjVwyzEg0iow9ORFAewC8KdhEkAGhfO0CeR0XevfwqQZaHFg4fI0jp/v200LRbaCsy5q3y0zYP7bh5hiPVvHBA4NhX7pS3RLlCbEc9ntizp20k1DfmiWFDzNfTU1HxQLHLhQFSjK4BRO8tWRI24LERQmxIxD6DNsrzxzpfmPmprncmbuRGzz6C1jqSSywcOQLI/WCUaZt8uBl6oi0HGqnp+zpxt8YWDZAcgqAkEkKOxnYqSBsg/aFOKiHYhw4d6iEJgiGCbkGfcUkIokfuuRGkuLi4tfUBcwUCibblA4qwiMh5sKB1vbhwJrf4KrXWxvpzE6OLgNy1r0zbVcK6vTroCXd2tx89OtJPEtxQeF8TTkRCtIKQYwSN1dbWdrS9u4M7y1dKOQRJsatYh2i9BUeANz3ddrkrSxc5pLjVc3L17auJgxUPgEYBCBTe/zGY6GAEGV18FR/JmTYeX7+Ye/ejjz66F46amnp85hXBZnkwHu4M9be1cddjDhWORqtqm2rqQdEgVQKCKwIcPD7g6A9VhtFZtK+wHyEJn2gpERBC0BTIGXTSXrPYV6QaFCeyqjqLv5mDtgpRLSvvqwFJKr6MA6TTul5M5I4cfuSplwFZ2rh45sb5H39+EO+uqanW48WxsVUX7+gLjaBZ9kDCc48Of7iqtqYeVZEQP0EwkvqPEgOO3sYDLVEvIBhJZWV3l5XtP4SBgoj7kzb0rTbakA96PdEolRpTM0wy+V06M2rmZNWh0t40VgvaXgQoCNYYvHilwyD63mdgKbF45AR9OGp6aX194saPn33088NQIB4P7ZoODwLSjtHr2IOa8Oyjr7LTH4ZkO6R9BIg95KDtoFonj6P6uyllpRgoFTQTRFr5dLqsPCEN4hOjDiSMaRDvTBIE2xbmih4Us+zoV2p6P4Wvo1DwRxG0J0yHkok4EkeOnXz59OnTposPrU+c/+znj7+rrKJE45AMDw8PTnV09z+Bz29RkpH+fty3Q6EDlf69TTVODbJXg6CzjqJy7Hi4pa62Xmu+mnvuqakpw/Yn7rP44vUSj3xAUA+aOPTh/EASjGmQnJrAHffQ/jLk0deyj9JPoqAR0evxaNTj3Yc7RF7qJ5999vvvr5+/ceMfTu4/Juo6juP4/V9btYqDkcLdZXecLuBL3cVJC4ELK8iIAAdp3oWsbtTUlm4JWFkSS0lJXRRhP1Y4i3635q9YuhKsVJxNg4WuZBL2O/rdWs/3+3P3FQhb64UZYyd3j+/78+v7+Rw4Nqx/6b3tb27/pD3sZ20lxa6XLGgsNRBKgoSwj4jEE2SjNlERHHEIEUd1sZvXbjqRL+QL8uDEM3NhuW9Wh+zH62CSwxEqw7tZCdBH2jZcHIe4amo6rKanKGmqfAPps41SPk/QlRoYl5I33/xI87mj9d1tFOT1T7o7srOVsgAJjr10EXUAMdEecA0lmQihj1RW2A6emdFPHJTX56N+gfEbiHRZbVmmD95ecW8RNSlgopKSrLceHgepqdtSXiOS3JQUBtFG6idNIuR12fF6v62t3Vw7NNTd/bmjs+25g9u3v769ISk721QEyYIHG6VljYdI47k9HC9JfGq3R61KEo5VF6fVrF5NA3d5pbsVFPgYOpAEaOqzkZSZAzzT1YHk0LeQyDwljevGDW2yBpgOxOVC8oDVFIfMVve8HGkS/lDIbRIi0U1ANvf29jY4Ip117VKRoQ4KIg6CQ8csHEAMgxRSEoVwxUWid4kikYUvjpJQzVOrSRwCg9kHiEZ77bnm3hMITWt+ToUsBXLoJgUimWetv5hV2fTLgWjqtrgMhHu8AmmINO7qkuIsEWSZRIeGhMIfRxU7HV+//v729ugNODIyMuqpB5Dnv/mLgrw2eCYDt92rkN96egaWMlQLZeXA6Z6e04Pf5OfzFFlp6lBIhoZ7TJ1EUwWSAoQVCesfG7K26Yfd63NY0InkS2v+RMij1lrtI1REINS9iOaLRFIsKenYtGlTLdm82bGvylp34OC2r59M4rowqBMoYwM9TieQV51n0sP6g6Y17OzpcfbslaIEf3PGM5CXV1KcNteGGAcjuU47yYw+clzAOGocBpK0IRLp5M9y7Se3tX55/hmIV4qxri5VuwhdSisChOtVUmwQko4hldC8HB8+WdX5xgMfNDVVMrjpedWyvcMoiEBOJ+J0Dt6eMy/d73H2PBgYcI7QvmYOC29g8LRI/MVZkyBaD0nqTEbTtWvvy2GexnEpDoGUtkVeWZ60/I2+zrs56Fq1bksRfUhueALJssTgH220HpVKXiQQ2jpNCwglMQxiKqJxRKqqIhHr07Z3K/SAXK7WgDIU0lxYUUFHvvfeQafzGyaSvGtanAOB3DHnMM1+JY8Zlp4/AnwsKy2t5ikkNkQkclCIuHy3JenKwcG5GwGyJ9I0I4m80MnO8R2Rvpe48WE9rNs0QJIDudlb6nJZjsch9EQgtC1NNSKpyJBKqMidK1bcqVlVIOfj5NIB5+jw87xI5ktG1Wg0yhEOrWleeh5T+96enoUpw84RBtRRCsFahAtIaUZDaVTEQLxe09e9swgvqrnVarq14pndra053AZwfnhl8qwFq/q6ZHxflZR067G2zs49z7A/IPeVF6QEZs2S0Y6BbqP1MgW5bA5rcUYtJFxLBHaA4DCQKhwJyDQDGdvLdAikQhlhCZ3hNxgeZhE+RYVjIQ9ZmJICJVlqUyoQJE9RkPgsEtR4vcesB/QSV7S23jBbj6ZZZ3VF7q4QyCooSSyb59/gm3aR3rlNm5UBRAbslN3Wy7Nnz6F+jUCWJ7G6yC8XSrmmupqKJCDH1n3aV6UQ36xpckfEEh6/QBglorFYfjrhpft1zch1Ghk9PfwgzzLmdJ7m7lgkM0dGRhSiqXGHmGQyQxI4vuVWkxHNvNW6X6ZTXmjB+sieosIZ6iB6I3PNrIAc+8xOQFhDz95ttb5UemX9glV72iKRSN+GjUhIjA+RdGwyJWH43bXrxSOHtn349YHKDN5pxXrvWjlJVwiVyEfNimqERkQ1dMFhAkTaUwLiYnoCgkQcbnucR+LxHLPYKyYUaV2dl7Bjdk1rJ3cJpmkRM0f5ZgaoQiBZILwWgUx7qdWyOCCLRO748v6XuzojnRvzY7HY1TGCpAMGH0gEYpKnsxeF5yCd980AoT3mZfv9vAR6QwuNIxAQgLYmCj+MLgHxunnpKqmpmZuWlobBRACtddy56CGwf5HVJBKfpymysUhalmE0FwokL5OFWm6AghAgBNOCW5p4t8nLM7ibJvdDeSGHhG1IvCIP7JI88eKL2XpXpasPWXsAQcFlpH20MPZ6GQ5lLDSvnEluEiSLACFxR5qZtjL9T1iL5EaSOZPRoss6drM3/5Y6a0+YlmUYMjTqFOEPuZg4ZnploNCLCqOgkbBQ1t0NtmmEcsctSCiKQFSymQnx0KFDR6iHgXglQV16AKEt6LrGO8DY63IZCMGRzMpDIUgmQYSBhRiJvytSmZ4fC8tVZK3U1GqRvjfScyoLjSM+pMRYc4bcLld8fSP31tIE5aallAWjOIhY7u9sqywyJTETokI+/OqrD9/74OChIyVxRtCTgKDgCy4XBenBIRCV5DLg0l2ADOKgJIFUu2kBMdFPFNL6LivkaCVBkpcZfqGp6YUwOxsVzaYeFSh4Ubr4cCNxu30y5OmyE4SEoRfHDGIoqyq5Ktq0PsJhIDuW7Nt2RHp8iTroqJ5QJgHiBiFhwB0BYiQkQPmZeEfwXX+9QHJTx8bGGLUUYhTmM3SZT0QWlYeLzEpfm4+bJ8nMy493EXGIQpYeXAu3xEN8aK7BYUNwSAoFQkGuZiACYhgC4Rxx164jBw9+UI5CEiIMnUAwaJi3l7qMJAFx0WNu4iH1bPoA0XnEPXduzYS4dfjq6stLD+MQiC6R5cuZeeFKuog2rCiNCkWxOCQ8f9zhzwZhJFoRHg4Eye3iyEcPxDgYtY4cOXTwg/3795e7bYdEKmIcMva6CBI7Wh3GsuGyMqHwkNOuGrNCSU1dLWEYllbiTmrtyk4PR5HQoW2Iv9qGqE4U6lAICQqE2x3OKYiUBIgk7pjH9JYnECPp7XUc3CaKffv2xQxgPERjCmIyESJta6yMtPCQEQHkjg8k/kmTleTPy6dtyfQqN5FZAikeDynXRqWxIVRE79tYThCRSGenHEDEMT9dhvPqjlojaeh3qIKsSUB08B8HGWPeo9MbieEA0iFMVpeDw8ODTgpCA7t+fFKg8Ki11m6vxy83xIQuXWJDGH21qxcpJG0ixEN038zc7alEBmAJmwc4YPj9JR0N5NOTfDiMgkQz4wGCxIYMcLX1E29CY/eXBcNODdSFZWT6mdDkCGulddmprmCmbnbLwCSvWSExfZP91JAsHW5wKIT+kqBcLW8LKyqiIDLH8V07eDdjvyx7qxxrVLFkyRIgdoCMjo6GgkG3u3R0dJD/2RGQkchG4shoD+1rdGR6HHFdIooJr7P2eKgN2/fSUkpK1CGDWUl1LMomErEhOtgphEFbWxV2rjsQJnqh0OfB5BAgAJFIRXob+p9s6HWsWaKOHTs4NzcLIv2bBKeKLxiCBUUg0pjK6utBjFdcKJFPFm3ZcouC5CgiWaYajcwuJeVhIM1M6uMg7rnEzcOKzUqA0KWz9c5VJ0dTFhvCxeloYN+hd3NvbS0QgqO90s9ukEfjI4k1OIkLfMRjgkQhKUDsFmUUdvLZ/sxn25f9UrWYWZOoIxatBNIsEO046kBRIxB/tf0e3HA+Ep++MVgsUGhgAsHBy2neCgMMo5YoYPQ2JN0gu0GyOKCUZNbEZBC+k4frwsVAYpeETAGhHIvOqMBcnpLqcqfpqoWGFcYxo7mZQSsqEFk649DQ8NLNMFdRyQwuu7AcaxChIOGgbx5ngj4urcdUpJcG5kAhkKo/H2vk9CBxn83278RcKUEjbfUGf6aHOcZAxtVknCO5y9pQeokdoUwvy02tQWI7YNC0gMjaRCFqSQsVZ5ebTT9SIXuR7CZMm02giGS+nh1Lk/FUYjBxgJCPwz+3NOq7neLvMr1sYvRr7EtcWV/QyL58pl2SiRBb8oq1SAXjLNeVXZ/qmiuQ6nLp6TAoiZntsxSiiUN0P5kgCZfStpIDQGSFr52+lIIEZW+tY6th0NnbNUOHf71L3yOkN7vjfoaEjfB4zpN3by4ze7B+lsU2ZCpJevokBxC2EGuAmILwMnXBUaGTpO0gQEoMJH7HJXtQGWyNEymJbuIzALCpIRCiI5ejWyEHDn/R8iCOnZcl3up0o+R8/YsPDZv7c5bVFwgkc0qIkdg5OyQqc4gNoWWpQ9dqlEQG5xyoBlJBL/F7zMEMEB29CljlG0j/1q39Itkah/T++MddtKudV1xxYyJXTc7FnJCfN4f9bdNJgKy2JVNA6BhngdCyEkv4eF+nIIYhEoHkC8RYaVvp2dqSZkoymB4ZkGYmE2+wGQgK4ujWNPz4x8plFCQuiCv+8Y7uG6+Ys0wg2QI5S9tCYlNM1AFE+kiWVGQyhIIoxETm/fywDSmU4yOe0JeYyYg5pQFSubWBiiQgQ921x3/8dSUO6mEIkxg25pydy+onQyaXRE4NJ2PIuWWMvxREKmI3rUkQti4UYlfEbEvE0qv9OkmHQNiQaUA6tBr9/QIZajgeWbHizz9tyFkdSG68jLOsRm1a7rNCqMnkcFybgJQopNDuIxOaloH4WYnpI4h2o/TqbH9xJhaToA2JbhraTA+hfTmsFZrff25ZthOICs4mAbJTOokNWT0VhEwFmQ7E/Q8IPUAhKklAdNSaYUN4iLnzoiyq0eNlA+lu3/GRnFq1OyK6drR+/2WpFsS8/n+DUBHWADbE7iT/CqEg7LK73DodGogZfieuGW0IXWQKiARJSLfYpI/4HulmsNI42MM+/P2p73/55ScbYiRkSgidpHEyBMm/Ny2FBFwhCmJDiD38xiWJUUshSXbTsiEaJCGvOcn0HdraWzvULXF8/8Wpo599BuTnZRMrMoXFDFuU5P9A2Jz2MtdNgDTbEAmS/wbxxCHBpOMWOXnyeP9Wx9Gjnx09darzpxN/vLVyjvQSyRX63xShIHv3flwYjsa+Led7J57c3K2jIinq0nAufXnZ5Rp+YiQ5WNn80EMd5BF+ecA999x6Mz8t/fhivhL99pHF/GWno0N+jpnflED4xRWLF/OQCYnF5LbKs/b4yZMn+yyN4yiMo0e/OHHixE/f8PsNlmpWtpiPpS0L7dzEbwy4qWXpYydO/NK2Zs2b+/bv3/7JJ7z14/3333+WvEOelrz99tuPk0fieXwxb90xP02/+ADP+7nEqjr83amqw4cPV3Vv7930UXtbVZ82EE17d//XWzrX3fEDP5S3oY79vAP97TvIkkT4bM2Lj+469PDu48c/JSclf7Nzv69JxHEcwI8F9Zf0D+x50IN6LkFIj8oiS+JqPumgwWJgP252s67AFXEnSGfZDwmhNCwyymWGu5ncIXhtaihBE53ozBr0/n6uHTT6E+7tfb/bPRl77fP96ob7fjiTrSyrBccwFeR5fhYRRSB41urAg+EEEI8n1ZmMpc+QpD9lswRBdkMcRwiMaHSBtWcIha7kc6U3909+AsSndDo+QHzT6dW1mo+91WT/cH1e9lDURenWZqJdr9c3A34GuUuOqzuWvBauarF38hpSIw1nWYYByJ0z3W4KhqBIJYmjHkEobMeOCI5galAZr37GU16aKuI4SOJA/jKu34aDZWEBDQKu5I/Jc0cP5QBRFdMCw4sAgY8Ce8MJd2yeLqqK7N/83gbE7KmAzOCPWFuCsKlAkDm0Z5BlaBDOsEwLS2t4pNvl4zwMoIhifNbDE8KRQEGQL79HIwZxKrKrIIA4ktu3wcggtuTYj481/eihdLNZlBXLUitMAQQChgMhx9lb39v1RkMRAkVv+eFVlmWsZrrwSGrz1aUYjk+xFhqSBAzX6aAkRmv9BYPERawrlAWQIE8FIQAGXQQZ/w9CBSEKY9Dash2ZMCUTRVuT6Nlva6GT17LNpiIr/V7FF/CB4GWDMYQAIgj3KqrSl26ZzGF6hWLdW/aTYwb/c1LAuTBcSUC0+didRGITfRtYY5NVzmi3CfKz243EaYswCMLjG6dQSShBT1B8NBlP/leR52Ds2iNYWJkwTmhX9ao2n0HKn/0nD187mG4uSgoWEwwYTEIQJsGNWlH7/V7HqjfW3zd8gloPMMgy9jiOTZaSN5LJFUQLL52LPX73mjWXSaAHhZ/bY0OGo9F6Kg6EDeFnPRFCOBi6AeTteOufPUIQJPth92ZHRaJhrarpSURfwhmP3NPE/tDpAytNWVKmAwIARMGEtcVg2P+DwaDfwmtbG5BhRxD6hlD2zwDy6u7djY1SHmWBI7kUzp2/fPPJk7m5d8/QYqa8j5tiEKs13N4a7UB4kSWCAlCCNNEFyHA83n4ICBwEyaafZ5dXPrz8VPi71XcgmI7BoesFOil444GmbdSeRM5EThFEnYYAF0KzUAOlUoECMY32rwZeEXqA9AIMcvXVDCgbpRJBdEDmz1++xJpPwYLGP1McN7XHMMzW1mRrOxXnI2DMQoGtEsG+txH2J5gwxPeDwYQg6SwtrTR+i5bl1XvlH1VIHAdBojldX8mXGKSAouhSbTF25vjC16YkLxbtSrDQng8wiI+q0TGNX+TAXhckVSAIPWUtF9gXsisSvnj5ws0bjx+fOHEnv3eKc+PGjRs3bty4cePmT3twIAAAAAAgyN96kCsAAAC4CZDk+1s0ZJXxAAAAAElFTkSuQmCC", + "description": "Displays historical indoor temperature values as a simplified chart with background. Optionally may display the corresponding latest indoor temperature value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'temperature', label: 'Temperature', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'temperature', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 15) {\\n\\tvalue = 15;\\n} else if (value > 30) {\\n\\tvalue = 30;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 15) {\\n\\tvalue = 15;\\n} else if (value > 30) {\\n\\tvalue = 30;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":18,\"color\":\"#224AC2\"},{\"from\":18,\"to\":24,\"color\":\"#3B911C\"},{\"from\":24,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Temperature\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"thermostat\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"°C\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "temperature", + "environment", + "indoor" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_temperature_card.json b/application/src/main/data/json/system/widget_types/indoor_temperature_card.json new file mode 100644 index 0000000000..0d51910d42 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_temperature_card.json @@ -0,0 +1,28 @@ +{ + "fqn": "indoor_temperature_card", + "name": "Indoor temperature card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest indoor temperature telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 15) {\\n\\tvalue = 15;\\n} else if (value > 30) {\\n\\tvalue = 30;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"thermostat\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":18,\"color\":\"#234CC7\"},{\"from\":18,\"to\":24,\"color\":\"#3FA71A\"},{\"from\":24,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":18,\"color\":\"#234CC7\"},{\"from\":18,\"to\":24,\"color\":\"#3FA71A\"},{\"from\":24,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Temperature card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "temperature", + "environment", + "indoor" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_temperature_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_temperature_card_with_background.json new file mode 100644 index 0000000000..5096ab3f6d --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_temperature_card_with_background.json @@ -0,0 +1,28 @@ +{ + "fqn": "indoor_temperature_card_with_background", + "name": "Indoor temperature card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest indoor temperature telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 15) {\\n\\tvalue = 15;\\n} else if (value > 30) {\\n\\tvalue = 30;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"thermostat\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":18,\"color\":\"#224AC2\"},{\"from\":18,\"to\":24,\"color\":\"#3B911C\"},{\"from\":24,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":18,\"color\":\"#224AC2\"},{\"from\":18,\"to\":24,\"color\":\"#3B911C\"},{\"from\":24,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Temperature card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "temperature", + "environment", + "indoor" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_temperature_chart_card.json b/application/src/main/data/json/system/widget_types/indoor_temperature_chart_card.json new file mode 100644 index 0000000000..adeafa4762 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_temperature_chart_card.json @@ -0,0 +1,31 @@ +{ + "fqn": "indoor_temperature_chart_card", + "name": "Indoor temperature chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a indoor temperature data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'temperature', label: 'Temperature', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: '°C', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'temperature', '°C', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 15) {\\n\\tvalue = 15;\\n} else if (value > 30) {\\n\\tvalue = 30;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"°C\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":18,\"color\":\"#234CC7\"},{\"from\":18,\"to\":24,\"color\":\"#3FA71A\"},{\"from\":24,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 15) {\\n\\tvalue = 15;\\n} else if (value > 30) {\\n\\tvalue = 30;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"°C\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"°C\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Temperature\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"device_thermostat\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "temperature", + "environment", + "indoor" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_temperature_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/indoor_temperature_chart_card_with_background.json new file mode 100644 index 0000000000..133099b900 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_temperature_chart_card_with_background.json @@ -0,0 +1,31 @@ +{ + "fqn": "indoor_temperature_chart_card_with_background", + "name": "Indoor temperature chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a indoor temperature data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'temperature', label: 'Temperature', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: '°C', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'temperature', '°C', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 15) {\\n\\tvalue = 15;\\n} else if (value > 30) {\\n\\tvalue = 30;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"°C\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":18,\"color\":\"#224AC2\"},{\"from\":18,\"to\":24,\"color\":\"#3B911C\"},{\"from\":24,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 15) {\\n\\tvalue = 15;\\n} else if (value > 30) {\\n\\tvalue = 30;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"°C\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"°C\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Temperature\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"device_thermostat\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "temperature", + "environment", + "indoor" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_temperature_gauge.json b/application/src/main/data/json/system/widget_types/indoor_temperature_gauge.json new file mode 100644 index 0000000000..2bbaffada5 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_temperature_gauge.json @@ -0,0 +1,29 @@ +{ + "fqn": "indoor_temperature_gauge", + "name": "Indoor temperature gauge", + "deprecated": false, + "image": "tb-image:aW5kb29yX3RlbXBlcmF0dXJlX2dhdWdlLnN2Zw==:aW5kb29yX3RlbXBlcmF0dXJlX2dhdWdlLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxnIGZpbHRlcj0idXJsKCNhKSI+PHJlY3Qgd2lkdGg9IjE4OCIgaGVpZ2h0PSIxNDgiIHg9IjYiIHk9IjYiIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iLjgiIHJ4PSI0Ii8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik0yMS4xIDE1djEwaC0xLjdWMTVoMS43Wm0zLjEgMHYxLjRoLTcuOVYxNWg4Wm00IDEwLjFjLS41IDAtMSAwLTEuNC0uMmEzLjMgMy4zIDAgMCAxLTEuOS0yIDQgNCAwIDAgMS0uMi0xLjN2LS4zYzAtLjYgMC0xLjEuMi0xLjZzLjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEuMS0uOGwxLjMtLjJjLjYgMCAxIDAgMS40LjJsMSAuOGMuMy4zLjUuNy42IDEuMWwuMiAxLjV2LjdoLTUuN3YtMS4yaDR2LS4xbC0uMS0uOS0uNS0uNi0uOS0uMmMtLjIgMC0uNSAwLS43LjItLjIgMC0uNC4yLS41LjVsLS40Ljd2Mi4zbC41LjcuNi41LjguMWEyLjMgMi4zIDAgMCAwIDEuOS0uOWwuOS45LS43LjZjLS4yLjItLjUuNC0uOS41LS40LjItLjguMi0xLjMuMlptNi4yLTZWMjVoLTEuN3YtNy40aDEuNnYxLjVabS0uMyAyaC0uNmwuMi0xLjUuNi0xLjFjLjItLjMuNi0uNiAxLS44YTMgMyAwIDAgMSAxLjItLjJsMSAuMS42LjVjLjMuMi40LjUuNS44bC4yIDEuMlYyNWgtMS42di00LjhsLS4yLS44YTEgMSAwIDAgMC0uNS0uNWwtLjctLjFjLS4zIDAtLjUgMC0uOC4yLS4yIDAtLjQuMy0uNS41bC0uMy43di44Wm00LjYtLjUtLjguMmMwLS41IDAtMSAuMi0xLjMuMS0uNC4zLS44LjYtMWEyLjYgMi42IDAgMCAxIDIuMS0xbDEgLjEuOC41Yy4yLjIuNC41LjUuOWwuMSAxLjJWMjVoLTEuNnYtNC44YzAtLjQgMC0uNy0uMi0uOWEuOS45IDAgMCAwLS40LS40bC0uNy0uMS0uNy4xLS41LjQtLjMuNi0uMS43Wm04LjEtMS42djguOGgtMS42VjE3LjZoMS41bC4xIDEuNFptNC44IDIuMnYuMmwtLjEgMS41LS42IDEuMmMtLjIuMy0uNS42LS45LjhsLTEuMi4yYy0uNSAwLS45IDAtMS4yLS4yLS40LS4yLS43LS40LS45LS43YTQgNCAwIDAgMS0uNi0xLjJsLS4zLTEuNHYtLjVjMC0uNi4yLTEgLjMtMS41bC42LTEuMWEyLjUgMi41IDAgMCAxIDItMWMuNSAwIDEgMCAxLjMuMmwxIC44LjUgMS4yLjEgMS41Wm0tMS42LjJ2LS4ybC0uMS0xLS4zLS43YTEuNSAxLjUgMCAwIDAtMS4zLS43bC0uNy4xLS42LjRhMiAyIDAgMCAwLS4zLjdsLS4yLjdWMjJsLjMgMSAuNi42IDEgLjJjLjIgMCAuNSAwIC43LS4yLjIgMCAuNC0uMy41LS41bC4zLS44LjEtMVptNi42IDMuN2MtLjYgMC0xIDAtMS41LS4yYTMuMyAzLjMgMCAwIDEtMS44LTIgNCA0IDAgMCAxLS4zLTEuM3YtLjNjMC0uNiAwLTEuMS4zLTEuNi4xLS41LjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEtLjhsMS40LS4yYy41IDAgMSAwIDEuMy4yYTMuMSAzLjEgMCAwIDEgMS42IDEuOWMuMi41LjIgMSAuMiAxLjV2LjdoLTUuN3YtMS4yaDR2LTFsLS42LS42LS44LS4yYy0uMyAwLS42IDAtLjguMi0uMiAwLS40LjItLjUuNS0uMS4yLS4zLjQtLjMuN2wtLjEgMXYxLjNsLjUuNy42LjUuOS4xYTIuMyAyLjMgMCAwIDAgMS44LS45bC45LjktLjYuNi0xIC41Yy0uMy4yLS44LjItMS4yLjJabTYuMS02VjI1aC0xLjZ2LTcuNGgxLjVsLjEgMS40Wm0yLjMtMS41VjE5YTMuMiAzLjIgMCAwIDAtMS41IDAgMS40IDEuNCAwIDAgMC0uOCAxbC0uMi42aC0uM2wuMS0xLjJjMC0uNC4yLS44LjQtMSAuMi0uNC40LS42LjctLjhhMS45IDEuOSAwIDAgMSAxLjMtLjJoLjNabTUuMiA2VjIwYzAtLjMgMC0uNS0uMi0uN2ExIDEgMCAwIDAtLjQtLjRsLS43LS4yLS43LjEtLjUuNC0uMS41aC0xLjdjMC0uMyAwLS41LjItLjhsLjYtLjdhMyAzIDAgMCAxIDEtLjUgNCA0IDAgMCAxIDEuMy0uMmMuNSAwIDEgMCAxLjQuMi41LjIuOC41IDEgLjkuMy40LjQuOC40IDEuNHYzLjNsLjEuOS4yLjd2LjFoLTEuN2EzIDMgMCAwIDEtLjItLjd2LS44Wm0uMi0zLjF2MWgtMS4ybC0uOC4xYy0uMiAwLS40LjEtLjUuM2ExIDEgMCAwIDAtLjUuOWwuMS41LjQuNC43LjFhMS44IDEuOCAwIDAgMCAxLjUtLjdsLjItLjYuNS44LS4zLjZhMyAzIDAgMCAxLS41LjYgMi42IDIuNiAwIDAgMS0xLjguNmMtLjQgMC0uOSAwLTEuMi0uMy0uNC0uMS0uNy0uNC0xLS43YTIuNCAyLjQgMCAwIDEtLjEtMi4ybC43LS44IDEtLjQgMS41LS4yaDEuM1ptNi44LTIuOXYxLjJINzN2LTEuMmg0LjJabS0zLTEuOEg3NnY3LjdsLjQuMmguNGEyLjUgMi41IDAgMCAwIC41IDBWMjVhNC4yIDQuMiAwIDAgMS0xIC4xYy0uNCAwLS44IDAtMS0uMi0uMy0uMS0uNi0uMy0uNy0uNi0uMi0uMy0uMy0uNy0uMy0xLjJ2LTcuM1ptOSA3LjV2LTUuN2gxLjZWMjVoLTEuNWwtLjEtMS43Wm0uMi0xLjZoLjZsLS4yIDEuNC0uNSAxYy0uMi40LS41LjYtLjkuOGEzIDMgMCAwIDEtMS4zLjIgMyAzIDAgMCAxLTEtLjEgMiAyIDAgMCAxLS43LS41Yy0uMy0uMi0uNC0uNS0uNS0uOWE0IDQgMCAwIDEtLjItMS4ydi00LjhoMS42djQuOGwuMS43LjMuNC40LjIuNC4xYy41IDAgLjkgMCAxLjEtLjMuMy0uMi41LS40LjYtLjdsLjItMVptNS0yLjd2NmgtMS42di03LjRoMS41bC4xIDEuNFptMi4zLTEuNFYxOWEzLjIgMy4yIDAgMCAwLTEuNSAwIDEuNCAxLjQgMCAwIDAtLjggMWwtLjIuNkg4OGwuMS0xLjJjLjEtLjQuMi0uOC40LTEgLjItLjQuNC0uNi43LS44YTEuOSAxLjkgMCAwIDEgMS4zLS4yaC4zWk05NSAyNWMtLjYgMC0xIDAtMS41LS4yYTMuMyAzLjMgMCAwIDEtMS44LTIgNCA0IDAgMCAxLS4zLTEuM3YtLjNjMC0uNi4xLTEuMS4zLTEuNi4xLS41LjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEtLjhsMS40LS4yYy41IDAgMSAwIDEuNC4ybDEgLjguNSAxLjFjLjIuNS4yIDEgLjIgMS41di43aC01Ljd2LTEuMmg0LjF2LS4xYzAtLjMgMC0uNi0uMi0uOSAwLS4yLS4yLS40LS40LS42bC0xLS4yYy0uMiAwLS40IDAtLjcuMi0uMiAwLS4zLjItLjUuNWwtLjMuNy0uMSAxdi40bC4xLjkuNC43LjYuNS45LjFBMi4zIDIuMyAwIDAgMCA5NyAyM2wxIC45LS43LjYtMSAuNWMtLjMuMi0uNy4yLTEuMi4yWiIvPjxnIGNsaXAtcGF0aD0idXJsKCNiKSI+PHJlY3Qgd2lkdGg9IjI2LjIiIGhlaWdodD0iMTIuNyIgeD0iODYuOSIgeT0iMTMwIiBmaWxsPSIjRjNGM0YzIiByeD0iMy4yIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTk3LjYgMTM2djEuMmMwIC41IDAgMS0uMiAxLjMgMCAuNC0uMi43LS40IDFsLS43LjUtMSAuMWgtLjdhMS44IDEuOCAwIDAgMS0xLTFsLS40LS44VjEzNmwuMS0xLjQuNC0uOS43LS41IDEtLjJjLjIgMCAuNSAwIC43LjJhMS44IDEuOCAwIDAgMSAxIC44bC40Ljh2MS4yWm0tMS4xIDEuM1YxMzUuMmwtLjItLjVhMSAxIDAgMCAwLS4yLS40bC0uNC0uMmExIDEgMCAwIDAtLjMgMCAxIDEgMCAwIDAtLjUgMGMtLjEuMS0uMy4yLS4zLjRsLS4zLjVWMTM4LjFjMCAuMiAwIC4zLjIuNSAwIC4xIDAgLjMuMi40bC4zLjJoLjhsLjQtLjQuMi0uNnYtLjlabTUuNC00djYuN2gtMS4ydi01LjRsLTEuNi41di0xbDIuNi0uOWguMlptNyAwdi42bC0yLjcgNi4xSDEwNWwyLjctNS45aC0zLjV2LS45aDQuN1oiLz48L2c+PHBhdGggZmlsbD0iIzIzNENDNyIgZD0iTTE0MyAxMzEuNGE1NS42IDU1LjYgMCAxIDAtODYuNy4ybDcuNC02YTQ2LjEgNDYuMSAwIDEgMSA3Mi0uMWw3LjQgNloiLz48cGF0aCBmaWxsPSIjM0ZBNzFBIiBkPSJNMTQzLjIgMTMxLjJhNTUuNiA1NS42IDAgMCAwLTU2LTg4LjhsMi4xIDkuM2E0Ni4xIDQ2LjEgMCAwIDEgNDYuNSA3My42bDcuNCA2WiIvPjxwYXRoIGZpbGw9IiNEODE4MzgiIGQ9Ik0xNDMuMSAxMzEuNGE1NS42IDU1LjYgMCAwIDAtMTkuNS04NWwtNCA4LjZhNDYuMiA0Ni4yIDAgMCAxIDE2IDcwLjRsNy41IDZaIi8+PHBhdGggZmlsbD0iIzYzNjM2MyIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJtMTM1LjYgMTI1LjYuNC0uNCAzLjcgMi45IDMuNyAyLjktLjQuNS03LjQtNS45Wk0xNTQuOSAxMDMuM2wtNC42LS42LTQuOC0uNXYtLjdsNC44LjUgNC43LjUtLjEuOFpNMTUwIDczbC0uNC0uOC00LjIgMi00LjIgMi4yLjIuNiA0LjMtMiA0LjMtMlpNMTI5LjcgNDkuOGwtMi42IDQtMi42IDQtLjUtLjQgMi41LTQgMi41LTQgLjcuNFpNMTAwLjEgNDUuOFY0MWE1Ni43IDU2LjcgMCAwIDAtLjcgMHY5LjZhNTAuNyA1MC43IDAgMCAxIC43IDB2LTQuOFpNNzMgNTMuNGwtMi41LTRjLS4zIDAtLjUuMi0uNy40bDIuNiA0IDIuNSA0IC42LS40LTIuNS00Wk01NCA3NC4zbC00LjItMi4xLS40LjcgNC4zIDIgNC40IDIgLjItLjUtNC4yLTIuMVpNNDkuMSAxMDJsLTQuNy41di44bDQuOC0uNiA0LjgtLjUtLjEtLjctNC44LjVaTTU5LjcgMTI4LjFsLTMuNyAzIC41LjYgMy43LTMgMy43LTMtLjQtLjUtMy44IDIuOVoiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjxwYXRoIGZpbGw9IiM2MzYzNjMiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTE1NSAxMDNhNTUuNiA1NS42IDAgMCAxLTEyIDI4LjVsLS42LS40YTU0LjggNTQuOCAwIDEgMC04NS40IDBsLS41LjVhNTUuNiA1NS42IDAgMSAxIDk4LjQtMjguNloiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Im01Ni41IDEwMS40LS43LS4yLjMtMi44aDN2LjhINTdsLS4yIDEuNGExLjggMS44IDAgMCAxIDEtLjNjLjIgMCAuNCAwIC43LjIuMiAwIC4zLjIuNS4zbC4zLjZhMi41IDIuNSAwIDAgMSAwIDEuNmwtLjMuNi0uNi40LS44LjFoLS43bC0uNi0uNGExLjYgMS42IDAgMCAxLS42LTEuMmgxbC4xLjUuMy4zaC45bC4zLS4yLjEtLjRhMS44IDEuOCAwIDAgMCAwLTEgMSAxIDAgMCAwLS4yLS4zYzAtLjEtLjEtLjItLjMtLjJhMSAxIDAgMCAwLS40LS4xaC0uNWwtLjQuM1pNNjggMTIyLjJ2MWwtLjEgMS0uNC44LS42LjVhMi4yIDIuMiAwIDAgMS0xLjQgMCAxLjUgMS41IDAgMCAxLS45LS43bC0uMi0uNy0uMS0xdi0xbC4xLTEgLjQtLjguNi0uNC43LS4yLjcuMWExLjUgMS41IDAgMCAxIC45LjhsLjIuNy4xLjlabS0xIDFWMTIxbC0uMy0uMy0uMi0uMmgtLjdsLS4zLjMtLjIuNXYzbC4zLjMuMi4yaC43bC4zLS4zLjItLjV2LS43Wk02Mi4xIDc2djUuOGgtMXYtNC42bC0xLjMuNXYtLjhsMi4yLS44aC4xWm01LjcgMi41di45bC0uMSAxLjEtLjQuOC0uNi40YTIuMiAyLjIgMCAwIDEtMS40IDAgMS41IDEuNSAwIDAgMS0xLS43bC0uMi0uN3YtMi45bC41LS44Yy4xLS4yLjMtLjQuNi0uNGwuNy0uMi42LjFhMS41IDEuNSAwIDAgMSAxIC43bC4yLjd2MVptLTEgMVY3Ny43bC0uMS0uNC0uMi0uMy0uMy0uMmExIDEgMCAwIDAtLjMgMGgtLjRsLS4zLjMtLjEuNS0uMS43VjgwLjFsLjIuNS4xLjMuMy4yaC43bC4zLS4zLjItLjV2LS44Wk03Ni40IDU4LjZ2NS43aC0xdi00LjZsLTEuMy41di0uOGwyLjItLjhoLjFabTMgMy0uOC0uMS4zLTIuOUg4MnYuOGgtMi4zbC0uMSAxLjRhMS44IDEuOCAwIDAgMSAuOS0uMmwuNy4xLjUuNC40LjZhMi41IDIuNSAwIDAgMSAwIDEuNWMtLjEuMy0uMi41LS40LjYtLjEuMi0uMy4zLS42LjRsLS44LjItLjctLjEtLjYtLjNhMS42IDEuNiAwIDAgMS0uNS0xLjJoLjlsLjEuNC4zLjMuNS4xaC40bC4zLS4zLjItLjRhMS44IDEuOCAwIDAgMCAwLS45IDEgMSAwIDAgMC0uMi0uNGwtLjMtLjJhMSAxIDAgMCAwLS41IDBoLS41bC0uMy4zWk0xMjkuMyAxMjMuNXYuN2gtNGwtLjEtLjUgMi40LTMuOWguOGwtLjggMS40LTEuNCAyLjNoMy4xWm0tLjctMy43djUuN2gtMXYtNS43aDFabTUgMi40djJsLS40LjgtLjYuNWEyLjIgMi4yIDAgMCAxLTEuNCAwIDEuNSAxLjUgMCAwIDEtMS0uN2wtLjItLjd2LTNsLjQtLjguNi0uNC44LS4yLjYuMWExLjUgMS41IDAgMCAxIDEgLjhsLjIuN3YuOVptLS45IDF2LTEuN2wtLjEtLjUtLjItLjMtLjMtLjJoLS43bC0uMy4zLS4yLjV2Mi42bC4xLjQuMi4zLjMuMmguN2wuMy0uMy4yLS41di0uN1pNMTM2LjggMTAwLjhoLjZsLjYtLjEuMy0uM3YtMWwtLjMtLjNhMSAxIDAgMCAwLS41IDAgMSAxIDAgMCAwLS40IDAgLjguOCAwIDAgMC0uNS43aC0xYzAtLjMuMi0uNS4zLS44bC43LS41YTIgMiAwIDAgMSAuOS0uMmMuNCAwIC43IDAgMSAuMi4yIDAgLjQuMy42LjUuMi4yLjIuNS4yLjl2LjVsLS40LjQtLjYuNC0uNy4xaC0uOHYtLjVabTAgLjd2LS41aC44bC44LjEuNi4zLjMuNS4xLjYtLjEuNy0uNC41LS42LjNhMi41IDIuNSAwIDAgMS0xLjUgMCAyIDIgMCAwIDEtLjYtLjNsLS40LS41LS4yLS43aDF2LjVjLjIgMCAuMy4yLjQuM2gxbC40LS4zdi0xLjFsLS40LS4zaC0xLjJabTQuNSAwLS44LS4zLjMtMi44aDMuMXYuOGgtMi4zbC0uMSAxLjRhMS44IDEuOCAwIDAgMSAuOS0uM2MuMiAwIC41IDAgLjcuMi4yIDAgLjQuMi41LjNsLjQuNmEyLjUgMi41IDAgMCAxIDAgMS42bC0uNC42LS42LjQtLjguMWgtLjdsLS42LS40LS40LS41LS4xLS43aC45bC4xLjUuMy4zaC45bC4zLS4yLjItLjRhMS44IDEuOCAwIDAgMCAwLTEgMSAxIDAgMCAwLS4yLS4zbC0uMy0uMmExIDEgMCAwIDAtLjUtLjFoLS41bC0uMy4zWk0xMzIuOSA3OC41aDEuMWwuMy0uNC4xLS41di0uNGMtLjItLjEtLjItLjMtLjQtLjNhMSAxIDAgMCAwLS41LS4xIDEgMSAwIDAgMC0uNCAwIC44LjggMCAwIDAtLjUuOGgtLjljMC0uMyAwLS42LjItLjhsLjctLjZhMiAyIDAgMCAxIDEtLjJjLjMgMCAuNiAwIC45LjIuMy4xLjUuMy42LjYuMi4yLjMuNS4zLjggMCAuMiAwIC40LS4yLjUgMCAuMi0uMS40LS4zLjVsLS41LjMtLjguMWgtLjd2LS41Wm0wIC44di0uNmguN2MuMyAwIC42IDAgLjguMi4zIDAgLjUuMS42LjMuMi4xLjMuMy4zLjRsLjEuNi0uMS43LS40LjUtLjYuNGEyLjUgMi41IDAgMCAxLTEuNSAwIDIgMiAwIDAgMS0uNi0uM2wtLjQtLjUtLjItLjdoMWwuMS40LjMuMy41LjFoLjZjMC0uMi4yLS4zLjMtLjR2LTFjLS4xLS4yLS4yLS4zLS40LS4zbC0uNi0uMWgtLjVabTcuMS0uOHYuOWwtLjEgMS4xLS40LjhjLS4xLjItLjMuMy0uNi40YTIuMiAyLjIgMCAwIDEtMS40IDAgMS41IDEuNSAwIDAgMS0uOS0uN2wtLjItLjctLjEtLjl2LTFsLjEtMSAuNC0uOGMuMi0uMi40LS40LjYtLjRsLjgtLjIuNi4xYTEuNSAxLjUgMCAwIDEgLjkuN2wuMi43LjEgMVptLTEgMVY3Ny4zbC0uMy0uMy0uMi0uMmExIDEgMCAwIDAtLjMgMGgtLjRsLS4zLjMtLjIuNXYzbC4zLjMuMi4yaC44bC4yLS4zLjItLjV2LS44Wk0xMTguOSA2My42di43SDExNXYtLjZsMS44LTIgLjUtLjcuMy0uNGExLjEgMS4xIDAgMCAwIDAtLjlsLS40LS4zYS45LjkgMCAwIDAtLjQtLjEgMSAxIDAgMCAwLS42LjFsLS4zLjR2LjZoLTFjMC0uNCAwLS43LjItMSAuMS0uMi40LS41LjctLjZhMiAyIDAgMCAxIDEtLjNsMSAuMi42LjYuMi44LS4xLjUtLjMuNi0uMy41LS41LjUtMS4yIDEuNGgyLjZabTEuNy0yLS43LS4xLjMtMi45aDN2LjhIMTIxbC0uMiAxLjRhMS44IDEuOCAwIDAgMSAuOS0uMmwuNy4xLjYuNC4zLjZhMi41IDIuNSAwIDAgMSAwIDEuNWMwIC4zLS4yLjUtLjMuNmwtLjYuNC0uOS4yLS42LS4xYy0uMyAwLS40LS4yLS42LS4zYTEuNiAxLjYgMCAwIDEtLjYtMS4yaDF2LjRjLjEuMi4zLjMuNC4zbC40LjFoLjVsLjItLjMuMi0uNGExLjggMS44IDAgMCAwIDAtLjkgMSAxIDAgMCAwLS4yLS40bC0uMy0uMmExIDEgMCAwIDAtLjQgMGgtLjZsLS4zLjNaTTk5LjQgNTh2LjdoLTMuOHYtLjZsMS45LTIgLjQtLjYuMy0uNWExLjEgMS4xIDAgMCAwIDAtLjhsLS4zLS40YS45LjkgMCAwIDAtLjUgMCAxIDEgMCAwIDAtLjUgMGwtLjQuNHYuNmgtMWMwLS4zIDAtLjYuMi0xbC43LS42YTIgMiAwIDAgMSAxLS4yYy40IDAgLjcgMCAxIC4ybC42LjUuMi44di42bC0uMy41LS40LjUtLjUuNS0xLjIgMS40aDIuNlptNC41LTIuNnYxYzAgLjQgMCAuOC0uMiAxIDAgLjQtLjIuNy0uNC45bC0uNS40YTIuMiAyLjIgMCAwIDEtMS40IDAgMS41IDEuNSAwIDAgMS0xLS43bC0uMi0uN3YtM2wuNC0uOC42LS40LjgtLjFoLjZhMS41IDEuNSAwIDAgMSAxIC44bC4yLjd2MVptLTEgMVY1NC44bC0uMS0uNS0uMi0uMy0uMy0uMWExIDEgMCAwIDAtLjMgMGgtLjRsLS4zLjMtLjIuNVY1N2wuMS40YzAgLjIuMS4zLjIuM2wuMy4yaC43bC4zLS4zLjItLjV2LS43Wk05NS40IDExNGwuMS0uNmExLjIgMS4yIDAgMCAxIDEtLjZjLjMgMCAuNSAwIC42LjIuMiAwIC40LjIuNS40bC4xLjYtLjEuNi0uNS40LS41LjJhMS4yIDEuMiAwIDAgMS0xLjItMS4yWm0uNiAwIC4xLjQuNS4yLjQtLjIuMS0uNC0uMS0uNGEuNS41IDAgMCAwLS40LS4yYy0uMiAwLS4zIDAtLjUuMmwtLjEuNFptNyAzLjNoMS4xYzAgLjQtLjEuOC0uMyAxLjEtLjIuNC0uNS42LS45LjgtLjQuMi0uOC4zLTEuMy4zLS40IDAtLjggMC0xLjEtLjJsLS44LS42Yy0uMy0uMy0uNC0uNi0uNi0xbC0uMS0xLjN2LS41bC4xLTEuM2MuMi0uNC4zLS43LjYtMWwuOC0uNiAxLjEtLjJjLjUgMCAxIC4xIDEuMy4zLjQuMi43LjQuOS44LjIuMy4zLjcuMyAxLjFoLTFsLS4zLS43YTEgMSAwIDAgMC0uNC0uNGwtLjgtLjItLjcuMS0uNC41YTIgMiAwIDAgMC0uMy42bC0uMSAxdjEuNWwuMy42LjUuNC43LjIuNy0uMS41LS41LjItLjdaIi8+PHBhdGggZmlsbD0iI0U2NTEwMCIgZD0iTTEwMy4xIDk4LjggODQgNTAuOGwxMy41IDQ5LjUgNC44IDEwLjdjMCAuMi4yLjMuNC4ybDIuNS0uOGMuMiAwIC4zLS4yLjMtLjRMMTAzIDk4LjhaIi8+PGcgZmlsdGVyPSJ1cmwoI2MpIj48Y2lyY2xlIGN4PSI5OS42IiBjeT0iOTguMiIgcj0iNC44IiBmaWxsPSIjZmZmIi8+PC9nPjxkZWZzPjxmaWx0ZXIgaWQ9ImEiIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiB4PSIwIiB5PSIwIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIzIi8+PGZlQ29tcG9zaXRlIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzQ2NTBfOTkyMSIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfNDY1MF85OTIxIiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PGZpbHRlciBpZD0iYyIgd2lkdGg9IjM4LjIiIGhlaWdodD0iMzguMiIgeD0iODAuNiIgeT0iNzkuMiIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlQ29sb3JNYXRyaXggaW49IlNvdXJjZUFscGhhIiByZXN1bHQ9ImhhcmRBbHBoYSIgdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAxMjcgMCIvPjxmZU1vcnBob2xvZ3kgaW49IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iZGlsYXRlIiByYWRpdXM9IjEuNiIgcmVzdWx0PSJlZmZlY3QxX2Ryb3BTaGFkb3dfNDY1MF85OTIxIi8+PGZlT2Zmc2V0Lz48ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSI2LjQiLz48ZmVDb21wb3NpdGUgaW4yPSJoYXJkQWxwaGEiIG9wZXJhdG9yPSJvdXQiLz48ZmVDb2xvck1hdHJpeCB2YWx1ZXM9IjAgMCAwIDAgMSAwIDAgMCAwIDAuMjQxNjY3IDAgMCAwIDAgMC4yNDE2NjcgMCAwIDAgMC4xNSAwIi8+PGZlQmxlbmQgaW4yPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHJlc3VsdD0iZWZmZWN0MV9kcm9wU2hhZG93XzQ2NTBfOTkyMSIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfZHJvcFNoYWRvd180NjUwXzk5MjEiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48Y2xpcFBhdGggaWQ9ImIiPjxyZWN0IHdpZHRoPSIyNi4yIiBoZWlnaHQ9IjEyLjciIHg9Ijg2LjkiIHk9IjEzMCIgZmlsbD0iI2ZmZiIgcng9IjMuMiIvPjwvY2xpcFBhdGg+PC9kZWZzPjwvc3ZnPg==", + "description": "Indoor temperatue gauge.", + "descriptor": { + "type": "latest", + "sizeX": 6, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries' }];\n }\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-analogue-radial-gauge-widget-settings", + "dataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-radial-gauge-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 15) {\\n\\tvalue = 15;\\n} else if (value > 30) {\\n\\tvalue = 30;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"startAngle\":45,\"ticksAngle\":270,\"needleCircleSize\":8,\"defaultColor\":\"#e65100\",\"minValue\":0,\"maxValue\":40,\"majorTicksCount\":9,\"colorMajorTicks\":\"#444\",\"minorTicks\":9,\"colorMinorTicks\":\"#666\",\"numbersFont\":{\"family\":\"Roboto\",\"size\":18,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"numbersColor\":\"#616161\",\"showUnitTitle\":false,\"unitTitle\":\"\",\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#888\"},\"titleColor\":\"#888\",\"unitsFont\":{\"size\":26,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"color\":\"#616161\"},\"unitsColor\":\"#616161\",\"valueBox\":true,\"valueInt\":3,\"valueFont\":{\"size\":27,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"color\":\"rgba(0, 0, 0, 0.54)\",\"shadowColor\":\"#FFFFFF01\"},\"valueColor\":\"rgba(0, 0, 0, 0.54)\",\"valueColorShadow\":\"#FFFFFF01\",\"colorValueBoxRect\":\"#88888800\",\"colorValueBoxRectEnd\":\"#66666600\",\"colorValueBoxBackground\":\"rgba(243, 243, 243, 0.54)\",\"colorValueBoxShadow\":\"rgba(0, 0, 0, 0)\",\"showBorder\":false,\"colorPlate\":\"#FFFFFF\",\"colorNeedle\":null,\"colorNeedleEnd\":null,\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"colorNeedleShadowDown\":\"rgba(188,143,143,0.45)\",\"highlightsWidth\":15,\"highlights\":[{\"from\":null,\"to\":18,\"color\":\"#224AC2\"},{\"from\":18,\"to\":24,\"color\":\"#3B911C\"},{\"from\":24,\"to\":40,\"color\":\"#DE2343\"}],\"animation\":true,\"animationDuration\":500,\"animationRule\":\"cycle\"},\"title\":\"Temperature\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"decimals\":0,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\",\"units\":\"°C\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":null,\"lineHeight\":\"24px\"},\"showTitleIcon\":true,\"titleTooltip\":\"\",\"titleIcon\":\"device_thermostat\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"actions\":{},\"margin\":\"0px\",\"borderRadius\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "temperature", + "environment", + "indoor" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_temperature_progress_bar.json b/application/src/main/data/json/system/widget_types/indoor_temperature_progress_bar.json new file mode 100644 index 0000000000..0adf101ae9 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_temperature_progress_bar.json @@ -0,0 +1,29 @@ +{ + "fqn": "indoor_temperature_progress_bar", + "name": "Indoor temperature progress bar", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAqFBMVEXg4ODf39/g4OAAAADg4ODf39/////g4OD19fUjTMchISHj4+OsrKw9PT10dHSQkJDHx8fj6Pjx8fEvLy9YWFienp7v7++BgYG6urry9Ps/Ys51j9w+Ys6GhobV1dWXl5esvOpaedU8PDxLbdEwV8upqanI0vG5ubn39/fLy8vk6fiRpeODm+BKSkrj6fja3/Fng9jU1NTDw8O6x+6fsOdXdtKrq6tmZmbNeyDTAAAABnRSTlPvIL8Ar7DvmsykAAAEFElEQVR42uzPwREAQAQDwDiGv4f+S702wmQ7WLg91HZhDsPkeg1DdB4wD5Un4EqkFCGjCBtF2CjCRhE2irBRhI0in/1y13EchqFod4tLvSBZUCoVbly5nfn/P1tSNoJdON5iCjuDmQOEERmG4EmAxH43zkWS2xF8B85FAnccvoiLGadcJ5JFEr2IfHmbiYLLMJFThH5zqqEJgFglBgdpGsYHbpmRWrPDEiVWy0IV5OjZHlbTSuz2qG2blWBcLyKenqwAS9FDsBCBib6QE4DAUhjGs+aRZaUX8aRvWkvQkkO0Icgri81SrhcJjJBSMsgEIatlKmLlVUuOnxmfeghcU8+cM9pmKvhbZF76NmvWWbhBhEVDY8dIPUfY93RseujAg3EsbWRZGI4idX/jSIHLRYQb7pVIYtBkEHaRx0rlKDK25+AukdkZci5SnZJs6dGekE5FyjbrDhEUiiV4JRL4gTBW7PvSDzb8I7IA7SkyWQsyBleLVPrqJn8U8S6yCHop0QX2TSRpd/UM5hAeqKPrKbKMWeWebwRxJstyFAlaTgC6vV6xiSBq0jhZWWMO5ByeIviw3qt+fo+IvPzjzoKNLFnjIZE80v/Puv3qd+I7XU7+igA94Y34ATdW34xfkT/sz6EKwDAQwFB3cHKyTA3Gqf3/941SUdfaUPJEfGgcoXGExhEaR2gcoXGExhEaR2gcoXGExhEaR2gcoXGExhEaR2gcoXGEZj/SRq8piDYjrZ6M7s6pAmg5Ut+becDIz34drEYIQ1EY3h08S5dpskhIkEHFEWbm/V+tkJFSLUy5ljDXmm+j25/rDSYxW39aiQ4avQohw/BB4jtPemj0KmRogW1IVDqQ35Z9G9KRA1QShsxki6flaQxUkIX0pMXTGJ0B+kA6FTOShdivgZiA0QPOGtyShvUXhfRkwCIOroV3eTgB7ycKseQNi8l3wDzn1yveTxLSOzqsBAstJCGe9P8i5MdAYHOI0fA/LAjxpMXaPRkA9mDL7sgWa8aNBt3Bjt88kK0pMqU7FMghe6lZkHPd2Q+ihmhTQ7SpIdrUEG1qiDY1RJsThPSBe8VrU4w8JHK/R1PMRRzCv2jKEYcE7hebYuQTmSL3epTbkcuJT62DqSHa1JDP9u4lhUEgCMLwmEcpGCIELN00vRrQpd7/cBGyD0xW5aR/mAN8NEwvW62AqPUFsg8+Q6XdAbo7yyE0wlXGRTMg71i9HLKNwKwykrwaYEQ3lUPWrANZMw144ninhtBZByQPeZweMAJTOYRTh3GDQgvZfr6ezcshmG1wqEQDenPrf4BgWSAW/3uzn6uAqBUQtQKiVkDUCohaAVErIGoFRK2AqBUQtSqCJFRRmy4VnJ4GXvfUpAqOT/NgXJtbe/bSpbm+AQFDJgMgD9a7AAAAAElFTkSuQmCC", + "description": "Displays indoor temperature reading as a horizontal progress bar. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 15) {\\n\\tvalue = 15;\\n} else if (value > 30) {\\n\\tvalue = 30;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":18,\"color\":\"#234CC7\"},{\"from\":18,\"to\":24,\"color\":\"#3FA71A\"},{\"from\":24,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":40,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":18,\"color\":\"#234CC7\"},{\"from\":18,\"to\":24,\"color\":\"#3FA71A\"},{\"from\":24,\"to\":null,\"color\":\"#D81838\"}]},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Temperature\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"device_thermostat\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "progress", + "environment", + "indoor", + "temperature" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_temperature_progress_bar_with_background.json b/application/src/main/data/json/system/widget_types/indoor_temperature_progress_bar_with_background.json new file mode 100644 index 0000000000..2fc35d9e0b --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_temperature_progress_bar_with_background.json @@ -0,0 +1,29 @@ +{ + "fqn": "indoor_temperature_progress_bar_with_background", + "name": "Indoor temperature progress bar with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays indoor temperature reading as a horizontal progress bar with background. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 15) {\\n\\tvalue = 15;\\n} else if (value > 30) {\\n\\tvalue = 30;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":18,\"color\":\"#224AC2\"},{\"from\":18,\"to\":24,\"color\":\"#3B911C\"},{\"from\":24,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":40,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":18,\"color\":\"#224AC2\"},{\"from\":18,\"to\":24,\"color\":\"#3B911C\"},{\"from\":24,\"to\":null,\"color\":\"#DE2343\"}]},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Temperature\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"device_thermostat\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "progress", + "environment", + "indoor", + "temperature" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_temperature_range_chart.json b/application/src/main/data/json/system/widget_types/indoor_temperature_range_chart.json new file mode 100644 index 0000000000..1083322228 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_temperature_range_chart.json @@ -0,0 +1,31 @@ +{ + "fqn": "indoor_temperature_range_chart", + "name": "Indoor temperature range chart", + "deprecated": false, + "image": "tb-image:aW5kb29yX3RlbXBlcmF0dXJlX3JhbmdlX2NoYXJ0LnN2Zw==:aW5kb29yX3RlbXBlcmF0dXJlX3JhbmdlX2NoYXJ0LnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxnIGNsaXAtcGF0aD0idXJsKCNhKSI+PHBhdGggZmlsbD0idXJsKCNiKSIgZD0ibTY0LjkgNTAuNC0yMyA1Mi43LTcuOSAxNi4xdjE4aDE1NHYtMjMuN2wtLjQtMjQuNy0xMSAzNy41LTYuMy0yMy40LTIuOCA2LjktMTEuOCAyNS44Yy03LjgtMjgtMTQuNC0zOC42LTIzLTY2LjJsLTguNyA0MS4zaC0xMS40bC0yLjEtNjctNC41LTI2LjRMODUgODNsLTUtMkw2NSA1MC4zWiIvPjxwYXRoIHN0cm9rZT0idXJsKCNjKSIgc3Ryb2tlLXdpZHRoPSIuOSIgZD0ibTM0IDExOS4zIDgtMTYuMiAyMy01Mi42TDgwLjMgODFsNSAyIDIxLjEtNjUuOCA0LjggMzMuMiAxLjkgNjAuMmgxMmw3LjUtNDIuMSAyMy44IDY2LjcgMTEuOS0yNiAyLjctNi45IDYuNCAyNEwxODggODguOSIvPjwvZz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii41IiBkPSJNMTYuMyAxN3YuN2gtNHYtLjRsMi41LTRoLjVsLS42IDEuMkwxMyAxN2gzLjNabS0uOC0zLjdWMTloLS43di01LjdoLjdabTUuMiAyLjR2LjljMCAuNCAwIC44LS4yIDEuMSAwIC40LS4yLjYtLjMuOC0uMi4yLS40LjQtLjYuNGEyIDIgMCAwIDEtLjcuMmwtLjYtLjFjLS4yIDAtLjQtLjEtLjUtLjMtLjIgMC0uMy0uMi0uNC0uNGwtLjMtLjh2LTNsLjUtLjdjLjEtLjIuMy0uNC41LS40bC44LS4yLjYuMWExLjQgMS40IDAgMCAxIC44LjdsLjMuN3YxWm0tLjggMXYtMS44bC0uMS0uNWExIDEgMCAwIDAtLjMtLjNjMC0uMS0uMS0uMi0uMy0uMmExIDEgMCAwIDAtLjMgMCAxIDEgMCAwIDAtLjUgMGwtLjMuMy0uMi42LS4xLjh2MS44bC4yLjUuMi4zLjMuMmguOGMuMiAwIC4zLS4yLjQtLjNsLjItLjZ2LS44Wm0xLjgtMi41YzAtLjEgMC0uMy4yLS41bC4zLS4zLjUtLjJjLjIgMCAuNCAwIC41LjJhMSAxIDAgMCAxIDAgMS43IDEgMSAwIDAgMS0uNS4yIDEgMSAwIDAgMS0uNS0uMiAxIDEgMCAwIDEtLjMtLjMgMSAxIDAgMCAxLS4yLS42Wm0uNSAwYzAgLjIgMCAuMy4yLjRsLjMuMi40LS4yLjEtLjQtLjEtLjMtLjQtLjJjLS4xIDAtLjIgMC0uMy4ybC0uMi4zWm02LjIgM2guOGMwIC40LS4yLjctLjMgMS0uMi4yLS40LjUtLjcuNi0uMy4yLS43LjMtMS4yLjMtLjMgMC0uNiAwLS45LS4yYTIgMiAwIDAgMS0uNy0uNWwtLjQtLjktLjItMXYtLjZjMC0uNCAwLS44LjItMS4xIDAtLjMuMi0uNi40LS44LjItLjMuNS0uNS44LS42bC45LS4yYy40IDAgLjguMSAxIC4zLjQuMS42LjQuOC42bC4zIDFoLS44YTIgMiAwIDAgMC0uMi0uNiAxIDEgMCAwIDAtLjQtLjVsLS43LS4xLS43LjFjLS4yIDAtLjMuMi0uNC40YTIgMiAwIDAgMC0uMy42bC0uMS45djEuM2wuMy43LjUuNC42LjJjLjQgMCAuNiAwIC44LS4yLjIgMCAuMy0uMi40LS40bC4yLS43Wk0xMy41IDMxLjdoLjVsLjctLjEuMy0uNC4yLS41LS4xLS41LS40LS40LS41LS4xLS42LjFhLjkuOSAwIDAgMC0uNS45aC0uN2MwLS4zIDAtLjYuMi0uOGwuNy0uNmEyIDIgMCAwIDEgLjktLjJjLjMgMCAuNiAwIC45LjJsLjYuNS4yIDEtLjEuNC0uMy40LS42LjQtLjguMWgtLjZ2LS40Wm0wIC42di0uNWguNmMuNCAwIC43IDAgMSAuMi4yIDAgLjQuMi41LjNsLjMuNVYzNGwtLjQuNS0uNi4zLS43LjJjLS4zIDAtLjUgMC0uNy0uMmwtLjYtLjNhMS40IDEuNCAwIDAgMS0uNS0xLjFoLjdsLjEuNWMuMS4xLjIuMy40LjNsLjYuMmMuMiAwIC40IDAgLjUtLjIuMiAwIC4zLS4xLjQtLjNsLjItLjYtLjItLjZhLjkuOSAwIDAgMC0uNC0uM2wtLjctLjFoLS41Wm00LjUtLjEtLjYtLjIuMy0yLjhoM3YuN2gtMi40bC0uMSAxLjUuMy0uMkgxOS44bC41LjVjLjIuMS4zLjMuNC42bC4xLjgtLjEuNy0uMy42LS42LjRhMi4yIDIuMiAwIDAgMS0xLjQgMGwtLjYtLjItLjQtLjVhMiAyIDAgMCAxLS4yLS43aC43bC4yLjVjMCAuMi4yLjMuNC40aDFsLjMtLjMuMi0uNGExLjggMS44IDAgMCAwIDAtMWwtLjItLjRhMSAxIDAgMCAwLS40LS4zbC0uNS0uMS0uNS4xLS40LjNabTMuNy0yYzAtLjMgMC0uNC4yLS42bC4zLS40aDFhMSAxIDAgMCAxIDAgMS44IDEgMSAwIDAgMS0uNS4xIDEgMSAwIDAgMS0uNS0uMSAxIDEgMCAwIDEtLjMtLjQgMSAxIDAgMCAxLS4yLS41Wm0uNSAwczAgLjIuMi4zbC4zLjEuNC0uMS4xLS40LS4xLS4zLS40LS4yYy0uMSAwLS4yIDAtLjMuMmwtLjIuM1ptNi4yIDIuOWguOGwtLjMgMS0uNy42Yy0uMy4yLS43LjMtMS4yLjNsLS45LS4yYTIgMiAwIDAgMS0uNy0uNmwtLjQtLjgtLjItMXYtLjZjMC0uNCAwLS44LjItMS4xIDAtLjQuMi0uNi40LS45bC44LS41LjktLjJjLjQgMCAuOCAwIDEgLjMuNC4xLjYuMy44LjYuMS4zLjMuNi4zIDFoLS44YTIgMiAwIDAgMC0uMi0uNyAxIDEgMCAwIDAtLjQtLjRsLS43LS4yYy0uMyAwLS41IDAtLjcuMi0uMiAwLS4zLjItLjQuNGEyIDIgMCAwIDAtLjMuNmwtLjEuOHYxLjRsLjMuNi41LjUuNi4xLjgtLjEuNC0uNS4yLS42Wk0xMy41IDQ3LjZoLjVjLjMgMCAuNSAwIC43LS4ybC4zLS4zLjItLjUtLjEtLjYtLjQtLjMtLjUtLjEtLjYuMWEuOS45IDAgMCAwLS41LjloLS43YzAtLjMgMC0uNi4yLS44LjItLjMuNC0uNS43LS42YTIgMiAwIDAgMSAuOS0uMmMuMyAwIC42IDAgLjkuMi4yIDAgLjQuMy42LjVsLjIuOS0uMS41LS4zLjQtLjYuNGgtMS40di0uM1ptMCAuNXYtLjRoLjZsMSAuMS41LjQuMy41djEuMmwtLjQuNS0uNi4zLS43LjFoLS43bC0uNi0uNGExLjQgMS40IDAgMCAxLS41LTEuMmguN2wuMS42LjQuMy42LjFoLjVsLjQtLjQuMi0uNi0uMi0uNmEuOS45IDAgMCAwLS40LS4zTDE0IDQ4aC0uNVptNy4yLS42di44YzAgLjUgMCAuOS0uMiAxLjIgMCAuMy0uMi42LS4zLjhsLS42LjRhMiAyIDAgMCAxLS43LjFoLS42bC0uNS0uMy0uNC0uNS0uMy0uN3YtM2wuNS0uOC41LS40LjgtLjFoLjZhMS40IDEuNCAwIDAgMSAuOC44bC4zLjd2MVptLS44IDFWNDYuNmwtLjEtLjVhMSAxIDAgMCAwLS4zLS40bC0uMy0uMmExIDEgMCAwIDAtLjMgMCAxIDEgMCAwIDAtLjUgMGwtLjMuNC0uMi41LS4xLjh2MS44bC4yLjUuMi40LjMuMmguOGwuNC0uNC4yLS41di0uOVptMS44LTIuNWMwLS4yIDAtLjQuMi0uNSAwLS4yLjItLjMuMy0uNGwuNS0uMS41LjFhMSAxIDAgMCAxIDAgMS44IDEgMSAwIDAgMS0uNS4xIDEgMSAwIDAgMS0uNS0uMSAxIDEgMCAwIDEtLjMtLjQgMSAxIDAgMCAxLS4yLS41Wm0uNSAwYzAgLjEgMCAuMy4yLjRsLjMuMS40LS4xLjEtLjQtLjEtLjQtLjQtLjEtLjMuMS0uMi40Wm02LjIgM2guOGwtLjMgMS0uNy42LTEuMi4yYy0uMyAwLS42IDAtLjktLjJhMiAyIDAgMCAxLS43LS41bC0uNC0uOC0uMi0xLjF2LS42YzAtLjQgMC0uNy4yLTEgMC0uNC4yLS43LjQtLjlsLjgtLjVjLjItLjIuNS0uMi45LS4ycy44IDAgMSAuMmMuNC4yLjYuNC44LjcuMS4zLjMuNi4zIDFoLS44YTIgMiAwIDAgMC0uMi0uNyAxIDEgMCAwIDAtLjQtLjRsLS43LS4yLS43LjEtLjQuNWEyIDIgMCAwIDAtLjMuNmwtLjEuOFY0OWwuMy42LjUuNS42LjEuOC0uMS40LS41LjItLjdaTTE2LjIgNjZ2LjZoLTMuN3YtLjVsMS44LTIgLjYtLjcuMi0uNS4xLS40LS4xLS41YzAtLjItLjItLjMtLjMtLjRhMSAxIDAgMCAwLS42LS4xbC0uNi4xYTEgMSAwIDAgMC0uNC40bC0uMS42aC0uN2MwLS4zIDAtLjYuMi0uOWwuNi0uNmEyIDIgMCAwIDEgMS0uMmMuNCAwIC43IDAgMSAuMi4yIDAgLjQuMy41LjUuMi4yLjIuNS4yLjh2LjVsLS4zLjVhNy44IDcuOCAwIDAgMS0uOCAxTDEzLjQgNjZoMi45Wm0xLjgtMi0uNi0uMi4zLTIuOWgzdi43aC0yLjRsLS4xIDEuNi4zLS4ySDE5LjhsLjUuNC40LjYuMS44LS4xLjgtLjMuNi0uNi40YTIuMiAyLjIgMCAwIDEtMS40IDBjLS4zIDAtLjQtLjItLjYtLjNsLS40LS41YTIgMiAwIDAgMS0uMi0uN2guN2wuMi42LjQuMy41LjFoLjVsLjMtLjMuMi0uNGExLjggMS44IDAgMCAwIDAtMWwtLjItLjVhMSAxIDAgMCAwLS40LS4ybC0uNS0uMWgtLjVsLS40LjNabTMuNy0yLjFjMC0uMiAwLS40LjItLjUgMC0uMi4yLS4zLjMtLjRsLjUtLjEuNS4xYTEgMSAwIDAgMSAwIDEuNyAxIDEgMCAwIDEtLjUuMiAxIDEgMCAwIDEtLjUtLjIgMSAxIDAgMCAxLS4zLS4zIDEgMSAwIDAgMS0uMi0uNVptLjUgMGMwIC4xIDAgLjIuMi4zbC4zLjJjLjIgMCAuMyAwIC40LS4ybC4xLS4zLS4xLS40LS40LS4xLS4zLjEtLjIuNFptNi4yIDNoLjhsLS4zLjktLjcuNy0xLjIuMmMtLjMgMC0uNiAwLS45LS4yYTIgMiAwIDAgMS0uNy0uNWwtLjQtLjgtLjItMS4xdi0uNmMwLS40IDAtLjguMi0xIDAtLjQuMi0uNy40LTFsLjgtLjUuOS0uMWMuNCAwIC44IDAgMSAuMi40LjIuNi40LjguNy4xLjMuMy42LjMgMWgtLjhhMiAyIDAgMCAwLS4yLS43IDEgMSAwIDAgMC0uNC0uNWwtLjctLjEtLjcuMS0uNC40YTIgMiAwIDAgMC0uMy43bC0uMS44djEuNGwuMy42LjUuNC42LjJjLjQgMCAuNiAwIC44LS4yLjIgMCAuMy0uMi40LS40bC4yLS43Wk0xNi4yIDgydi41aC0zLjdWODJsMS44LTIgLjYtLjcuMi0uNS4xLS41LS4xLS41LS4zLS4zYTEgMSAwIDAgMC0uNi0uMmMtLjIgMC0uNCAwLS42LjJhMSAxIDAgMCAwLS40LjRsLS4xLjZoLS43YzAtLjMgMC0uNi4yLS45LjEtLjMuMy0uNS42LS42YTIgMiAwIDAgMSAxLS4zbDEgLjJjLjIuMi40LjMuNS42LjIuMi4yLjQuMi44di41bC0uMy41YTcuOCA3LjggMCAwIDEtLjggMWwtMS41IDEuNmgyLjlabTQuNS0yLjh2LjljMCAuNCAwIC44LS4yIDEuMiAwIC4zLS4yLjUtLjMuNy0uMi4yLS40LjQtLjYuNGEyIDIgMCAwIDEtLjcuMmwtLjYtLjFjLS4yIDAtLjQtLjEtLjUtLjMtLjIgMC0uMy0uMi0uNC0uNEwxNyA4MXYtM2wuNS0uN2MuMS0uMi4zLS40LjUtLjRsLjgtLjIuNi4xYTEuNCAxLjQgMCAwIDEgLjguN2wuMy43djFabS0uOCAxdi0xLjhsLS4xLS41YTEgMSAwIDAgMC0uMy0uM2MwLS4xLS4xLS4yLS4zLS4yYTEgMSAwIDAgMC0uMyAwIDEgMSAwIDAgMC0uNSAwbC0uMy4zLS4yLjYtLjEuOHYxLjhsLjIuNS4yLjMuMy4yaC44Yy4yIDAgLjMtLjIuNC0uM2wuMi0uNnYtLjhabTEuOC0yLjVjMC0uMSAwLS4zLjItLjVsLjMtLjMuNS0uMmMuMiAwIC40IDAgLjUuMmExIDEgMCAwIDEgMCAxLjcgMSAxIDAgMCAxLS41LjIgMSAxIDAgMCAxLS41LS4yIDEgMSAwIDAgMS0uMy0uMyAxIDEgMCAwIDEtLjItLjZabS41IDBjMCAuMiAwIC4zLjIuNGwuMy4yLjQtLjIuMS0uNC0uMS0uMy0uNC0uMmMtLjEgMC0uMiAwLS4zLjJsLS4yLjNabTYuMiAzaC44YzAgLjQtLjIuNy0uMyAxLS4yLjItLjQuNS0uNy42LS4zLjItLjcuMy0xLjIuMy0uMyAwLS42IDAtLjktLjJhMiAyIDAgMCAxLS43LS41TDI1IDgxbC0uMi0xdi0uNmMwLS40IDAtLjguMi0xLjEgMC0uMy4yLS42LjQtLjguMi0uMy41LS41LjgtLjZsLjktLjJjLjQgMCAuOC4xIDEgLjMuNC4xLjYuNC44LjZsLjMgMWgtLjhhMiAyIDAgMCAwLS4yLS42IDEgMSAwIDAgMC0uNC0uNWwtLjctLjEtLjcuMWMtLjIgMC0uMy4yLS40LjRhMiAyIDAgMCAwLS4zLjZsLS4xLjl2MS4zbC4zLjcuNS40LjYuMmMuNCAwIC42IDAgLjgtLjIuMiAwIC4zLS4yLjQtLjRsLjItLjdaTTE0LjggOTIuN3Y1LjdoLS43di00LjhsLTEuNC41di0uN2wyLS43aC4xWm0zLjIgMy0uNi0uMi4zLTIuOGgzdi43aC0yLjRsLS4xIDEuNS4zLS4ySDE5LjhsLjUuNWMuMi4xLjMuMy40LjZsLjEuOC0uMS43LS4zLjYtLjYuNGEyLjIgMi4yIDAgMCAxLTEuNCAwbC0uNi0uMi0uNC0uNWEyIDIgMCAwIDEtLjItLjdoLjdsLjIuNWMwIC4yLjIuMy40LjNsLjUuMi41LS4xLjMtLjMuMi0uNGExLjggMS44IDAgMCAwIDAtMWwtLjItLjRhMSAxIDAgMCAwLS40LS4zbC0uNS0uMS0uNS4xLS40LjNabTMuNy0yYzAtLjMgMC0uNC4yLS42bC4zLS40aDFhMSAxIDAgMCAxIDAgMS44IDEgMSAwIDAgMS0uNS4xIDEgMSAwIDAgMS0uNS0uMSAxIDEgMCAwIDEtLjMtLjQgMSAxIDAgMCAxLS4yLS41Wm0uNSAwczAgLjIuMi4zbC4zLjEuNC0uMS4xLS40LS4xLS4zLS40LS4yYy0uMSAwLS4yIDAtLjMuMmwtLjIuM1ptNi4yIDIuOWguOGwtLjMgMS0uNy42Yy0uMy4yLS43LjMtMS4yLjNsLS45LS4yYTIgMiAwIDAgMS0uNy0uNkwyNSA5N2wtLjItMXYtLjdjMC0uMyAwLS43LjItMSAwLS40LjItLjYuNC0uOWwuOC0uNS45LS4yYy40IDAgLjggMCAxIC4zLjQuMS42LjMuOC42LjEuMy4zLjYuMyAxaC0uOGEyIDIgMCAwIDAtLjItLjcgMSAxIDAgMCAwLS40LS40bC0uNy0uMmMtLjMgMC0uNSAwLS43LjItLjIgMC0uMy4yLS40LjRhMiAyIDAgMCAwLS4zLjZsLS4xLjh2MS40bC4zLjYuNS41LjYuMS44LS4xLjQtLjUuMi0uNlpNMTQuOCAxMDguNXY1LjhoLS43di00LjlsLTEuNC42di0uN2wyLS44aC4xWm01LjkgMi41di44YzAgLjUgMCAuOS0uMiAxLjIgMCAuMy0uMi42LS4zLjhsLS42LjRhMiAyIDAgMCAxLS43LjFoLS42bC0uNS0uMy0uNC0uNS0uMy0uN3YtM2wuNS0uOC41LS40LjgtLjFoLjZhMS40IDEuNCAwIDAgMSAuOC44bC4zLjd2MVptLS44IDFWMTEwLjFsLS4xLS41YTEgMSAwIDAgMC0uMy0uNGwtLjMtLjJhMSAxIDAgMCAwLS4zIDAgMSAxIDAgMCAwLS41IDBsLS4zLjQtLjIuNS0uMS44djEuOGwuMi41LjIuNC4zLjJoLjhsLjQtLjQuMi0uNXYtLjlabTEuOC0yLjVjMC0uMiAwLS40LjItLjUgMC0uMi4yLS4zLjMtLjRsLjUtLjEuNS4xYTEgMSAwIDAgMSAwIDEuOCAxIDEgMCAwIDEtLjUuMSAxIDEgMCAwIDEtLjUtLjEgMSAxIDAgMCAxLS4zLS40IDEgMSAwIDAgMS0uMi0uNVptLjUgMGMwIC4xIDAgLjMuMi40bC4zLjEuNC0uMS4xLS40YS41LjUgMCAwIDAtLjUtLjVsLS4zLjEtLjIuNFptNi4yIDNoLjhsLS4zIDEtLjcuNi0xLjIuMmMtLjMgMC0uNiAwLS45LS4yYTIgMiAwIDAgMS0uNy0uNWwtLjQtLjgtLjItMS4xdi0uNmMwLS40IDAtLjcuMi0xIDAtLjQuMi0uNy40LS45bC44LS41Yy4yLS4yLjUtLjIuOS0uMnMuOCAwIDEgLjJjLjQuMi42LjQuOC43LjEuMy4zLjYuMyAxaC0uOGEyIDIgMCAwIDAtLjItLjcgMSAxIDAgMCAwLS40LS40bC0uNy0uMi0uNy4xLS40LjVhMiAyIDAgMCAwLS4zLjZsLS4xLjh2MS40bC4zLjYuNS41LjYuMS44LS4xLjQtLjUuMi0uN1pNMTguNCAxMjcuNGwtLjYtLjEuMy0yLjloM3YuN2gtMi40bC0uMiAxLjYuNC0uMkgyMC4ybC41LjQuNC42djEuNmwtLjQuNi0uNS40YTIgMiAwIDAgMS0uOC4xaC0uN2wtLjUtLjQtLjQtLjVhMiAyIDAgMCAxLS4yLS43aC43YzAgLjIgMCAuNC4yLjZsLjMuMy42LjFoLjRsLjQtLjMuMi0uNGExLjggMS44IDAgMCAwIDAtMWwtLjItLjVhMSAxIDAgMCAwLS40LS4ybC0uNS0uMWgtLjVsLS40LjNabTMuNy0yYzAtLjIgMC0uNC4yLS41IDAtLjIuMi0uMy4zLS40bC41LS4xLjUuMWExIDEgMCAwIDEgMCAxLjcgMSAxIDAgMCAxLS41LjIgMSAxIDAgMCAxLS41LS4yIDEgMSAwIDAgMS0uMy0uMyAxIDEgMCAwIDEtLjItLjVabS41IDBjMCAuMSAwIC4yLjIuM2wuMy4yYy4yIDAgLjMgMCAuNC0uMmwuMS0uM2EuNS41IDAgMCAwLS41LS41bC0uMy4xLS4yLjRabTYuMiAzaC44bC0uMy45LS43LjctMS4yLjJjLS4zIDAtLjYgMC0uOS0uMmEyIDIgMCAwIDEtLjctLjVsLS40LS44Yy0uMi0uNC0uMi0uNy0uMi0xLjF2LS42YzAtLjQgMC0uOC4yLTEgMC0uNC4yLS43LjQtMWwuNy0uNSAxLS4xYy40IDAgLjggMCAxIC4yLjQuMi42LjQuOC43bC4zIDFoLS44YTIgMiAwIDAgMC0uMi0uNyAxIDEgMCAwIDAtLjQtLjVsLS43LS4xLS43LjEtLjUuNGEyIDIgMCAwIDAtLjMuN3YyLjJsLjMuNi41LjQuNi4yYy4zIDAgLjYgMCAuOC0uMi4yIDAgLjMtLjIuNC0uNGwuMi0uN1pNMjguNiAxNDJ2LjhsLS4xIDFjMCAuNC0uMi42LS4zLjhsLS41LjRIMjdhMiAyIDAgMCAxLS41IDBsLS41LS4yLS4zLS41LS4yLS42LS4xLS45di0uN2wuMS0xYzAtLjQuMi0uNi4zLS44LjItLjEuMy0uMy41LS4zbC43LS4xaC41YTEuMiAxLjIgMCAwIDEgLjguN2wuMi42LjEuOVptLS42IDF2LTEuNmwtLjItLjUtLjItLjMtLjMtLjJhMSAxIDAgMCAwLS4zIDAgMSAxIDAgMCAwLS40IDBsLS4zLjMtLjIuNXYyLjNsLjEuNS4yLjMuMy4yaC43bC4zLS4zLjItLjV2LS44Wk0zOC44IDE0MS4zdjUuN2gtLjd2LTQuOGwtMS41LjV2LS42bDIuMS0uOGguMVptNC41IDB2NS43aC0uN3YtNC44bC0xLjUuNXYtLjZsMi4xLS44aC4xWm04LjcgMHY1LjdoLS44bC0yLjgtNC40djQuNGgtLjh2LTUuN2guOGwyLjggNC40di00LjRoLjhabTEgMy42YzAtLjQgMC0uNi4yLS45IDAtLjMuMi0uNS4zLS43bC42LS40Yy4zLS4yLjUtLjIuOC0uMi4zIDAgLjYgMCAuOC4yLjMgMCAuNS4yLjYuNGwuNC43LjIuOHYuMWMwIC4zIDAgLjYtLjIuOWwtLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM3YtLjlabS43IDAgLjEuNi4yLjUuNC40aDEuMWwuMy0uNC4zLS41di0xLjJjMC0uMi0uMi0uNC0uMy0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLS41LjFsLS40LjQtLjIuNXYuNVptNS4zIDEuNCAxLjItMy41aC43bC0xLjUgNC4yaC0uNWwuMS0uN1ptLTEtMy41IDEuMiAzLjYuMS42aC0uNWwtMS41LTQuMmguN1pNNzAgMTQxLjN2NS43aC0uN3YtNC44bC0xLjQuNXYtLjZsMi0uOGguMlptNiA1LjF2LjZoLTMuOHYtLjVsMS45LTIgLjUtLjcuMy0uNXYtMWwtLjQtLjNhMSAxIDAgMCAwLS42LS4yYy0uMiAwLS40IDAtLjYuMmExIDEgMCAwIDAtLjQuNGwtLjEuNmgtLjdjMC0uMyAwLS42LjItLjkuMS0uMy4zLS41LjYtLjZhMiAyIDAgMCAxIDEtLjNsMSAuMmMuMi4yLjQuMy41LjYuMi4yLjMuNC4zLjhsLS4xLjUtLjMuNWE3LjggNy44IDAgMCAxLS44IDFsLTEuNCAxLjZoMi44Wm03LjItNXY1LjZoLS43bC0yLjktNC40djQuNEg3OXYtNS43aC43bDIuOSA0LjR2LTQuNGguN1ptMSAzLjUuMi0uOWMwLS4zLjItLjUuNC0uN2wuNi0uNGMuMi0uMi41LS4yLjgtLjIuMyAwIC42IDAgLjguMi4yIDAgLjQuMi42LjRsLjQuNy4xLjh2LjFsLS4xLjktLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM2wtLjEtLjlabS44IDB2LjZsLjMuNWMwIC4yLjIuMy40LjRoMWwuNC0uNC4yLS41di0xLjJsLS4yLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41LS4xLjVabTUuMiAxLjQgMS4yLTMuNWguN2wtMS41IDQuMmgtLjVsLjEtLjdabS0xLTMuNSAxLjMgMy42di42aC0uNGwtMS42LTQuMmguOFpNMTAxLjMgMTQxLjN2NS43aC0uN3YtNC44bC0xLjUuNXYtLjZsMi4xLS44aC4xWm0zLjIgMi41aC41bC42LS4xLjQtLjQuMS0uNXYtLjVsLS40LS40aC0xLjFhLjkuOSAwIDAgMC0uNS45aC0uN2MwLS4zIDAtLjYuMi0uOGwuNi0uNmEyIDIgMCAwIDEgMS0uMmwuOC4yYy4zLjEuNS4zLjYuNi4yLjIuMy41LjMuOGwtLjEuNS0uNC41LS41LjMtLjguMWgtLjZ2LS40Wm0wIC42di0uNGgxLjZsLjUuNC4zLjV2MS4ybC0uNS41LS41LjRoLTEuNWwtLjUtLjNhMS40IDEuNCAwIDAgMS0uNi0xLjJoLjh2LjVsLjUuNGgxLjFsLjQtLjQuMS0uNi0uMS0uNmEuOS45IDAgMCAwLS41LS4zbC0uNi0uMWgtLjVabTEwLTN2NS42aC0uOGwtMi44LTQuNHY0LjRoLS44di01LjdoLjhsMi44IDQuNHYtNC40aC44Wm0xIDMuNWMwLS40IDAtLjYuMi0uOSAwLS4zLjItLjUuMy0uN2wuNi0uNGMuMy0uMi41LS4yLjgtLjIuMyAwIC42IDAgLjguMi4zIDAgLjUuMi42LjRsLjQuNy4yLjh2LjFjMCAuMyAwIC42LS4yLjlsLS40LjdhMS44IDEuOCAwIDAgMS0xLjQuNiAxLjggMS44IDAgMCAxLTEuOC0xLjN2LS45Wm0uNyAwIC4xLjYuMi41LjQuNGgxLjFsLjMtLjQuMy0uNXYtMS4yYzAtLjItLjItLjQtLjMtLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0uNS4xbC0uNC40LS4yLjV2LjVabTUuMyAxLjQgMS4yLTMuNWguN2wtMS41IDQuMmgtLjVsLjEtLjdabS0xLTMuNSAxLjIgMy42LjEuNmgtLjVsLTEuNS00LjJoLjdaTTEzMi42IDE0MS4zdjUuN2gtLjh2LTQuOGwtMS40LjV2LS42bDItLjhoLjJabTYgMy44di42aC00LjJ2LS40bDIuNi00aC42bC0uNyAxLjItMS43IDIuNmgzLjNabS0uOS0zLjh2NS43aC0uN3YtNS43aC43Wm04IDB2NS43aC0uN2wtMi45LTQuNHY0LjRoLS43di01LjdoLjdsMi45IDQuNHYtNC40aC43Wm0xIDMuNi4yLS45YzAtLjMuMi0uNS40LS43bC42LS40Yy4yLS4yLjUtLjIuOC0uMi4zIDAgLjYgMCAuOC4yLjIgMCAuNC4yLjYuNGwuNC43LjEuOHYuMWwtLjEuOS0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zbC0uMS0uOVptLjggMHYuNmwuMy41YzAgLjIuMi4zLjQuNGgxbC40LS40LjItLjV2LTEuMmwtLjItLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0uNS4xbC0uNC40LS4yLjUtLjEuNVptNS4yIDEuNCAxLjItMy41aC43bC0xLjUgNC4yaC0uNWwuMS0uN1ptLTEtMy41IDEuMyAzLjZ2LjZoLS40bC0xLjYtNC4yaC44Wk0xNjMuOCAxNDEuM3Y1LjdoLS43di00LjhsLTEuNS41di0uNmwyLjEtLjhoLjFabTMgMy0uNS0uMi4zLTIuOGgyLjl2LjdoLTIuM2wtLjIgMS41LjQtLjEuNS0uMS44LjEuNS40LjMuNmEyLjUgMi41IDAgMCAxIDAgMS41YzAgLjMtLjIuNS0uMy42LS4xLjItLjMuMy0uNi40YTIgMiAwIDAgMS0uOC4ybC0uNi0uMS0uNi0uMy0uMy0uNWEyIDIgMCAwIDEtLjItLjdoLjdsLjEuNS40LjRoMWwuMy0uMy4yLS40YTEuOCAxLjggMCAwIDAgMC0xYzAtLjIgMC0uMy0uMi0uNGExIDEgMCAwIDAtLjMtLjNoLTEuMWwtLjMuM1ptMTAuMi0zdjUuN2gtLjhsLTIuOC00LjR2NC40aC0uOHYtNS43aC44bDIuOCA0LjR2LTQuNGguOFptMSAzLjZjMC0uNCAwLS42LjItLjkgMC0uMy4yLS41LjMtLjdsLjYtLjRjLjMtLjIuNS0uMi44LS4yLjMgMCAuNiAwIC44LjIuMyAwIC41LjIuNi40bC40LjcuMi44di4xYzAgLjMgMCAuNi0uMi45bC0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zdi0uOVptLjcgMCAuMS42LjIuNS40LjRoMS4xbC4zLS40LjMtLjV2LTEuMmMwLS4yLS4yLS40LS4zLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41di41Wm01LjMgMS40IDEuMi0zLjVoLjdsLTEuNSA0LjJoLS41bC4xLS43Wm0tMS0zLjUgMS4yIDMuNi4xLjZoLS41bC0xLjUtNC4yaC43WiIvPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii41IiBzdHJva2Utd2lkdGg9Ii40IiBkPSJNMzQgMTZoMTU0TTM0IDMzLjNoMTU0TTM0IDUwLjZoMTU0TTM0IDY3LjloMTU0TTM0IDg1LjFoMTU0TTM0IDEwMi40aDE1NE0zNCAxMTkuN2gxNTRNMzQgMTM3aDE1NCIvPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjEwNy4zIiB4Mj0iMTA3LjMiIHkxPSIxNTEuMSIgeTI9Ii0xNC4yIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIuNCIgc3RvcC1jb2xvcj0iIzQ3NjRCQiIvPjxzdG9wIG9mZnNldD0iLjQiIHN0b3AtY29sb3I9IiM1M0JEMkQiLz48c3RvcCBvZmZzZXQ9Ii41IiBzdG9wLWNvbG9yPSIjNTNCRDJEIi8+PHN0b3Agb2Zmc2V0PSIuNSIgc3RvcC1jb2xvcj0iI0QzM0U1NyIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJjIiB4MT0iMTA2IiB4Mj0iMTA2LjIiIHkxPSIxMzYuOCIgeTI9Ii0xNC4yIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIuNCIgc3RvcC1jb2xvcj0iIzIyNEFDMiIvPjxzdG9wIG9mZnNldD0iLjQiIHN0b3AtY29sb3I9IiMzRkE3MUEiLz48c3RvcCBvZmZzZXQ9Ii41IiBzdG9wLWNvbG9yPSIjM0ZBNzFBIi8+PHN0b3Agb2Zmc2V0PSIuNSIgc3RvcC1jb2xvcj0iI0Q4MTgzOCIvPjwvbGluZWFyR3JhZGllbnQ+PGNsaXBQYXRoIGlkPSJhIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMzQgOGgxNTR2MTM3SDM0eiIvPjwvY2xpcFBhdGg+PC9kZWZzPjwvc3ZnPg==", + "description": "Displays changes to indoor temperature data over time visualized with color ranges.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.rangeChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.rangeChartWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: false,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries' }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-range-chart-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-range-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 15) {\\n\\tvalue = 15;\\n} else if (value > 30) {\\n\\tvalue = 30;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":2592000000,\"interval\":86400000,\"fixedTimewindow\":{\"startTimeMs\":1700577227898,\"endTimeMs\":1700663627898},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"dataZoom\":true,\"rangeColors\":[{\"from\":null,\"to\":18,\"color\":\"#234CC7\"},{\"from\":18,\"to\":24,\"color\":\"#3FA71A\"},{\"from\":24,\"to\":null,\"color\":\"#D81838\"}],\"outOfRangeColor\":\"#ccc\",\"fillArea\":true,\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm\",\"lastUpdateAgo\":false,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Temperature\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"°C\",\"decimals\":0,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "temperature", + "environment", + "indoor" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/indoor_temperature_range_chart_with_background.json b/application/src/main/data/json/system/widget_types/indoor_temperature_range_chart_with_background.json new file mode 100644 index 0000000000..51e4953a71 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/indoor_temperature_range_chart_with_background.json @@ -0,0 +1,31 @@ +{ + "fqn": "indoor_temperature_range_chart_with_background", + "name": "Indoor temperature range chart with background", + "deprecated": false, + "image": "tb-image:aW5kb29yX3RlbXBlcmF0dXJlX3JhbmdlX2NoYXJ0X3dpdGhfYmFja2dyb3VuZC5zdmc=:aW5kb29yX3RlbXBlcmF0dXJlX3JhbmdlX2NoYXJ0X3dpdGhfYmFja2dyb3VuZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0idXJsKCNhKSIgZD0iTTAgMGgyMDB2MTYwSDB6Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48L2c+PGcgY2xpcC1wYXRoPSJ1cmwoI2MpIj48cGF0aCBmaWxsPSJ1cmwoI2QpIiBkPSJtNjQuOSA1MC40LTIzIDUyLjctNy45IDE2LjF2MThoMTU0di0yMy43bC0uNC0yNC43LTExIDM3LjUtNi4zLTIzLjQtMi44IDYuOS0xMS44IDI1LjhjLTcuOC0yOC0xNC40LTM4LjYtMjMtNjYuMmwtOC43IDQxLjNoLTExLjRsLTIuMS02Ny00LjUtMjYuNEw4NSA4M2wtNS0yTDY1IDUwLjNaIi8+PHBhdGggc3Ryb2tlPSJ1cmwoI2UpIiBzdHJva2Utd2lkdGg9Ii45IiBkPSJtMzQgMTE5LjMgOC0xNi4yIDIzLTUyLjZMODAuMyA4MWw1IDIgMjEuMS02NS44IDQuOCAzMy4yIDEuOSA2MC4yaDEybDcuNS00Mi4xIDIzLjggNjYuNyAxMS45LTI2IDIuNy02LjkgNi40IDI0TDE4OCA4OC45Ii8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Ik0xNi4zIDE3di43aC00di0uNGwyLjUtNGguNWwtLjYgMS4yTDEzIDE3aDMuM1ptLS44LTMuN1YxOWgtLjd2LTUuN2guN1ptNS4yIDIuNHYuOWMwIC40IDAgLjgtLjIgMS4xIDAgLjQtLjIuNi0uMy44LS4yLjItLjQuNC0uNi40YTIgMiAwIDAgMS0uNy4ybC0uNi0uMWMtLjIgMC0uNC0uMS0uNS0uMy0uMiAwLS4zLS4yLS40LS40bC0uMy0uOHYtM2wuNS0uN2MuMS0uMi4zLS40LjUtLjRsLjgtLjIuNi4xYTEuNCAxLjQgMCAwIDEgLjguN2wuMy43djFabS0uOCAxdi0xLjhsLS4xLS41YTEgMSAwIDAgMC0uMy0uM2MwLS4xLS4xLS4yLS4zLS4yYTEgMSAwIDAgMC0uMyAwIDEgMSAwIDAgMC0uNSAwbC0uMy4zLS4yLjYtLjEuOHYxLjhsLjIuNS4yLjMuMy4yaC44Yy4yIDAgLjMtLjIuNC0uM2wuMi0uNnYtLjhabTEuOC0yLjVjMC0uMSAwLS4zLjItLjVsLjMtLjMuNS0uMmMuMiAwIC40IDAgLjUuMmExIDEgMCAwIDEgMCAxLjcgMSAxIDAgMCAxLS41LjIgMSAxIDAgMCAxLS41LS4yIDEgMSAwIDAgMS0uMy0uMyAxIDEgMCAwIDEtLjItLjZabS41IDBjMCAuMiAwIC4zLjIuNGwuMy4yLjQtLjIuMS0uNC0uMS0uMy0uNC0uMmMtLjEgMC0uMiAwLS4zLjJsLS4yLjNabTYuMiAzaC44YzAgLjQtLjIuNy0uMyAxLS4yLjItLjQuNS0uNy42LS4zLjItLjcuMy0xLjIuMy0uMyAwLS42IDAtLjktLjJhMiAyIDAgMCAxLS43LS41bC0uNC0uOS0uMi0xdi0uNmMwLS40IDAtLjguMi0xLjEgMC0uMy4yLS42LjQtLjguMi0uMy41LS41LjgtLjZsLjktLjJjLjQgMCAuOC4xIDEgLjMuNC4xLjYuNC44LjZsLjMgMWgtLjhhMiAyIDAgMCAwLS4yLS42IDEgMSAwIDAgMC0uNC0uNWwtLjctLjEtLjcuMWMtLjIgMC0uMy4yLS40LjRhMiAyIDAgMCAwLS4zLjZsLS4xLjl2MS4zbC4zLjcuNS40LjYuMmMuNCAwIC42IDAgLjgtLjIuMiAwIC4zLS4yLjQtLjRsLjItLjdaTTEzLjUgMzEuN2guNWwuNy0uMS4zLS40LjItLjUtLjEtLjUtLjQtLjQtLjUtLjEtLjYuMWEuOS45IDAgMCAwLS41LjloLS43YzAtLjMgMC0uNi4yLS44bC43LS42YTIgMiAwIDAgMSAuOS0uMmMuMyAwIC42IDAgLjkuMmwuNi41LjIgMS0uMS40LS4zLjQtLjYuNC0uOC4xaC0uNnYtLjRabTAgLjZ2LS41aC42Yy40IDAgLjcgMCAxIC4yLjIgMCAuNC4yLjUuM2wuMy41VjM0bC0uNC41LS42LjMtLjcuMmMtLjMgMC0uNSAwLS43LS4ybC0uNi0uM2ExLjQgMS40IDAgMCAxLS41LTEuMWguN2wuMS41Yy4xLjEuMi4zLjQuM2wuNi4yYy4yIDAgLjQgMCAuNS0uMi4yIDAgLjMtLjEuNC0uM2wuMi0uNi0uMi0uNmEuOS45IDAgMCAwLS40LS4zbC0uNy0uMWgtLjVabTQuNS0uMS0uNi0uMi4zLTIuOGgzdi43aC0yLjRsLS4xIDEuNS4zLS4ySDE5LjhsLjUuNWMuMi4xLjMuMy40LjZsLjEuOC0uMS43LS4zLjYtLjYuNGEyLjIgMi4yIDAgMCAxLTEuNCAwbC0uNi0uMi0uNC0uNWEyIDIgMCAwIDEtLjItLjdoLjdsLjIuNWMwIC4yLjIuMy40LjRoMWwuMy0uMy4yLS40YTEuOCAxLjggMCAwIDAgMC0xbC0uMi0uNGExIDEgMCAwIDAtLjQtLjNsLS41LS4xLS41LjEtLjQuM1ptMy43LTJjMC0uMyAwLS40LjItLjZsLjMtLjRoMWExIDEgMCAwIDEgMCAxLjggMSAxIDAgMCAxLS41LjEgMSAxIDAgMCAxLS41LS4xIDEgMSAwIDAgMS0uMy0uNCAxIDEgMCAwIDEtLjItLjVabS41IDBzMCAuMi4yLjNsLjMuMS40LS4xLjEtLjQtLjEtLjMtLjQtLjJjLS4xIDAtLjIgMC0uMy4ybC0uMi4zWm02LjIgMi45aC44bC0uMyAxLS43LjZjLS4zLjItLjcuMy0xLjIuM2wtLjktLjJhMiAyIDAgMCAxLS43LS42bC0uNC0uOC0uMi0xdi0uNmMwLS40IDAtLjguMi0xLjEgMC0uNC4yLS42LjQtLjlsLjgtLjUuOS0uMmMuNCAwIC44IDAgMSAuMy40LjEuNi4zLjguNi4xLjMuMy42LjMgMWgtLjhhMiAyIDAgMCAwLS4yLS43IDEgMSAwIDAgMC0uNC0uNGwtLjctLjJjLS4zIDAtLjUgMC0uNy4yLS4yIDAtLjMuMi0uNC40YTIgMiAwIDAgMC0uMy42bC0uMS44djEuNGwuMy42LjUuNS42LjEuOC0uMS40LS41LjItLjZaTTEzLjUgNDcuNmguNWMuMyAwIC41IDAgLjctLjJsLjMtLjMuMi0uNS0uMS0uNi0uNC0uMy0uNS0uMS0uNi4xYS45LjkgMCAwIDAtLjUuOWgtLjdjMC0uMyAwLS42LjItLjguMi0uMy40LS41LjctLjZhMiAyIDAgMCAxIC45LS4yYy4zIDAgLjYgMCAuOS4yLjIgMCAuNC4zLjYuNWwuMi45LS4xLjUtLjMuNC0uNi40aC0xLjR2LS4zWm0wIC41di0uNGguNmwxIC4xLjUuNC4zLjV2MS4ybC0uNC41LS42LjMtLjcuMWgtLjdsLS42LS40YTEuNCAxLjQgMCAwIDEtLjUtMS4yaC43bC4xLjYuNC4zLjYuMWguNWwuNC0uNC4yLS42LS4yLS42YS45LjkgMCAwIDAtLjQtLjNMMTQgNDhoLS41Wm03LjItLjZ2LjhjMCAuNSAwIC45LS4yIDEuMiAwIC4zLS4yLjYtLjMuOGwtLjYuNGEyIDIgMCAwIDEtLjcuMWgtLjZsLS41LS4zLS40LS41LS4zLS43di0zbC41LS44LjUtLjQuOC0uMWguNmExLjQgMS40IDAgMCAxIC44LjhsLjMuN3YxWm0tLjggMVY0Ni42bC0uMS0uNWExIDEgMCAwIDAtLjMtLjRsLS4zLS4yYTEgMSAwIDAgMC0uMyAwIDEgMSAwIDAgMC0uNSAwbC0uMy40LS4yLjUtLjEuOHYxLjhsLjIuNS4yLjQuMy4yaC44bC40LS40LjItLjV2LS45Wm0xLjgtMi41YzAtLjIgMC0uNC4yLS41IDAtLjIuMi0uMy4zLS40bC41LS4xLjUuMWExIDEgMCAwIDEgMCAxLjggMSAxIDAgMCAxLS41LjEgMSAxIDAgMCAxLS41LS4xIDEgMSAwIDAgMS0uMy0uNCAxIDEgMCAwIDEtLjItLjVabS41IDBjMCAuMSAwIC4zLjIuNGwuMy4xLjQtLjEuMS0uNC0uMS0uNC0uNC0uMS0uMy4xLS4yLjRabTYuMiAzaC44bC0uMyAxLS43LjYtMS4yLjJjLS4zIDAtLjYgMC0uOS0uMmEyIDIgMCAwIDEtLjctLjVsLS40LS44LS4yLTEuMXYtLjZjMC0uNCAwLS43LjItMSAwLS40LjItLjcuNC0uOWwuOC0uNWMuMi0uMi41LS4yLjktLjJzLjggMCAxIC4yYy40LjIuNi40LjguNy4xLjMuMy42LjMgMWgtLjhhMiAyIDAgMCAwLS4yLS43IDEgMSAwIDAgMC0uNC0uNGwtLjctLjItLjcuMS0uNC41YTIgMiAwIDAgMC0uMy42bC0uMS44VjQ5bC4zLjYuNS41LjYuMS44LS4xLjQtLjUuMi0uN1pNMTYuMiA2NnYuNmgtMy43di0uNWwxLjgtMiAuNi0uNy4yLS41LjEtLjQtLjEtLjVjMC0uMi0uMi0uMy0uMy0uNGExIDEgMCAwIDAtLjYtLjFsLS42LjFhMSAxIDAgMCAwLS40LjRsLS4xLjZoLS43YzAtLjMgMC0uNi4yLS45bC42LS42YTIgMiAwIDAgMSAxLS4yYy40IDAgLjcgMCAxIC4yLjIgMCAuNC4zLjUuNS4yLjIuMi41LjIuOHYuNWwtLjMuNWE3LjggNy44IDAgMCAxLS44IDFMMTMuNCA2NmgyLjlabTEuOC0yLS42LS4yLjMtMi45aDN2LjdoLTIuNGwtLjEgMS42LjMtLjJIMTkuOGwuNS40LjQuNi4xLjgtLjEuOC0uMy42LS42LjRhMi4yIDIuMiAwIDAgMS0xLjQgMGMtLjMgMC0uNC0uMi0uNi0uM2wtLjQtLjVhMiAyIDAgMCAxLS4yLS43aC43bC4yLjYuNC4zLjUuMWguNWwuMy0uMy4yLS40YTEuOCAxLjggMCAwIDAgMC0xbC0uMi0uNWExIDEgMCAwIDAtLjQtLjJsLS41LS4xaC0uNWwtLjQuM1ptMy43LTIuMWMwLS4yIDAtLjQuMi0uNSAwLS4yLjItLjMuMy0uNGwuNS0uMS41LjFhMSAxIDAgMCAxIDAgMS43IDEgMSAwIDAgMS0uNS4yIDEgMSAwIDAgMS0uNS0uMiAxIDEgMCAwIDEtLjMtLjMgMSAxIDAgMCAxLS4yLS41Wm0uNSAwYzAgLjEgMCAuMi4yLjNsLjMuMmMuMiAwIC4zIDAgLjQtLjJsLjEtLjMtLjEtLjQtLjQtLjEtLjMuMS0uMi40Wm02LjIgM2guOGwtLjMuOS0uNy43LTEuMi4yYy0uMyAwLS42IDAtLjktLjJhMiAyIDAgMCAxLS43LS41bC0uNC0uOC0uMi0xLjF2LS42YzAtLjQgMC0uOC4yLTEgMC0uNC4yLS43LjQtMWwuOC0uNS45LS4xYy40IDAgLjggMCAxIC4yLjQuMi42LjQuOC43LjEuMy4zLjYuMyAxaC0uOGEyIDIgMCAwIDAtLjItLjcgMSAxIDAgMCAwLS40LS41bC0uNy0uMS0uNy4xLS40LjRhMiAyIDAgMCAwLS4zLjdsLS4xLjh2MS40bC4zLjYuNS40LjYuMmMuNCAwIC42IDAgLjgtLjIuMiAwIC4zLS4yLjQtLjRsLjItLjdaTTE2LjIgODJ2LjVoLTMuN1Y4MmwxLjgtMiAuNi0uNy4yLS41LjEtLjUtLjEtLjUtLjMtLjNhMSAxIDAgMCAwLS42LS4yYy0uMiAwLS40IDAtLjYuMmExIDEgMCAwIDAtLjQuNGwtLjEuNmgtLjdjMC0uMyAwLS42LjItLjkuMS0uMy4zLS41LjYtLjZhMiAyIDAgMCAxIDEtLjNsMSAuMmMuMi4yLjQuMy41LjYuMi4yLjIuNC4yLjh2LjVsLS4zLjVhNy44IDcuOCAwIDAgMS0uOCAxbC0xLjUgMS42aDIuOVptNC41LTIuOHYuOWMwIC40IDAgLjgtLjIgMS4yIDAgLjMtLjIuNS0uMy43LS4yLjItLjQuNC0uNi40YTIgMiAwIDAgMS0uNy4ybC0uNi0uMWMtLjIgMC0uNC0uMS0uNS0uMy0uMiAwLS4zLS4yLS40LS40TDE3IDgxdi0zbC41LS43Yy4xLS4yLjMtLjQuNS0uNGwuOC0uMi42LjFhMS40IDEuNCAwIDAgMSAuOC43bC4zLjd2MVptLS44IDF2LTEuOGwtLjEtLjVhMSAxIDAgMCAwLS4zLS4zYzAtLjEtLjEtLjItLjMtLjJhMSAxIDAgMCAwLS4zIDAgMSAxIDAgMCAwLS41IDBsLS4zLjMtLjIuNi0uMS44djEuOGwuMi41LjIuMy4zLjJoLjhjLjIgMCAuMy0uMi40LS4zbC4yLS42di0uOFptMS44LTIuNWMwLS4xIDAtLjMuMi0uNWwuMy0uMy41LS4yYy4yIDAgLjQgMCAuNS4yYTEgMSAwIDAgMSAwIDEuNyAxIDEgMCAwIDEtLjUuMiAxIDEgMCAwIDEtLjUtLjIgMSAxIDAgMCAxLS4zLS4zIDEgMSAwIDAgMS0uMi0uNlptLjUgMGMwIC4yIDAgLjMuMi40bC4zLjIuNC0uMi4xLS40LS4xLS4zLS40LS4yYy0uMSAwLS4yIDAtLjMuMmwtLjIuM1ptNi4yIDNoLjhjMCAuNC0uMi43LS4zIDEtLjIuMi0uNC41LS43LjYtLjMuMi0uNy4zLTEuMi4zLS4zIDAtLjYgMC0uOS0uMmEyIDIgMCAwIDEtLjctLjVMMjUgODFsLS4yLTF2LS42YzAtLjQgMC0uOC4yLTEuMSAwLS4zLjItLjYuNC0uOC4yLS4zLjUtLjUuOC0uNmwuOS0uMmMuNCAwIC44LjEgMSAuMy40LjEuNi40LjguNmwuMyAxaC0uOGEyIDIgMCAwIDAtLjItLjYgMSAxIDAgMCAwLS40LS41bC0uNy0uMS0uNy4xYy0uMiAwLS4zLjItLjQuNGEyIDIgMCAwIDAtLjMuNmwtLjEuOXYxLjNsLjMuNy41LjQuNi4yYy40IDAgLjYgMCAuOC0uMi4yIDAgLjMtLjIuNC0uNGwuMi0uN1pNMTQuOCA5Mi43djUuN2gtLjd2LTQuOGwtMS40LjV2LS43bDItLjdoLjFabTMuMiAzLS42LS4yLjMtMi44aDN2LjdoLTIuNGwtLjEgMS41LjMtLjJIMTkuOGwuNS41Yy4yLjEuMy4zLjQuNmwuMS44LS4xLjctLjMuNi0uNi40YTIuMiAyLjIgMCAwIDEtMS40IDBsLS42LS4yLS40LS41YTIgMiAwIDAgMS0uMi0uN2guN2wuMi41YzAgLjIuMi4zLjQuM2wuNS4yLjUtLjEuMy0uMy4yLS40YTEuOCAxLjggMCAwIDAgMC0xbC0uMi0uNGExIDEgMCAwIDAtLjQtLjNsLS41LS4xLS41LjEtLjQuM1ptMy43LTJjMC0uMyAwLS40LjItLjZsLjMtLjRoMWExIDEgMCAwIDEgMCAxLjggMSAxIDAgMCAxLS41LjEgMSAxIDAgMCAxLS41LS4xIDEgMSAwIDAgMS0uMy0uNCAxIDEgMCAwIDEtLjItLjVabS41IDBzMCAuMi4yLjNsLjMuMS40LS4xLjEtLjQtLjEtLjMtLjQtLjJjLS4xIDAtLjIgMC0uMy4ybC0uMi4zWm02LjIgMi45aC44bC0uMyAxLS43LjZjLS4zLjItLjcuMy0xLjIuM2wtLjktLjJhMiAyIDAgMCAxLS43LS42TDI1IDk3bC0uMi0xdi0uN2MwLS4zIDAtLjcuMi0xIDAtLjQuMi0uNi40LS45bC44LS41LjktLjJjLjQgMCAuOCAwIDEgLjMuNC4xLjYuMy44LjYuMS4zLjMuNi4zIDFoLS44YTIgMiAwIDAgMC0uMi0uNyAxIDEgMCAwIDAtLjQtLjRsLS43LS4yYy0uMyAwLS41IDAtLjcuMi0uMiAwLS4zLjItLjQuNGEyIDIgMCAwIDAtLjMuNmwtLjEuOHYxLjRsLjMuNi41LjUuNi4xLjgtLjEuNC0uNS4yLS42Wk0xNC44IDEwOC41djUuOGgtLjd2LTQuOWwtMS40LjZ2LS43bDItLjhoLjFabTUuOSAyLjV2LjhjMCAuNSAwIC45LS4yIDEuMiAwIC4zLS4yLjYtLjMuOGwtLjYuNGEyIDIgMCAwIDEtLjcuMWgtLjZsLS41LS4zLS40LS41LS4zLS43di0zbC41LS44LjUtLjQuOC0uMWguNmExLjQgMS40IDAgMCAxIC44LjhsLjMuN3YxWm0tLjggMVYxMTAuMWwtLjEtLjVhMSAxIDAgMCAwLS4zLS40bC0uMy0uMmExIDEgMCAwIDAtLjMgMCAxIDEgMCAwIDAtLjUgMGwtLjMuNC0uMi41LS4xLjh2MS44bC4yLjUuMi40LjMuMmguOGwuNC0uNC4yLS41di0uOVptMS44LTIuNWMwLS4yIDAtLjQuMi0uNSAwLS4yLjItLjMuMy0uNGwuNS0uMS41LjFhMSAxIDAgMCAxIDAgMS44IDEgMSAwIDAgMS0uNS4xIDEgMSAwIDAgMS0uNS0uMSAxIDEgMCAwIDEtLjMtLjQgMSAxIDAgMCAxLS4yLS41Wm0uNSAwYzAgLjEgMCAuMy4yLjRsLjMuMS40LS4xLjEtLjRhLjUuNSAwIDAgMC0uNS0uNWwtLjMuMS0uMi40Wm02LjIgM2guOGwtLjMgMS0uNy42LTEuMi4yYy0uMyAwLS42IDAtLjktLjJhMiAyIDAgMCAxLS43LS41bC0uNC0uOC0uMi0xLjF2LS42YzAtLjQgMC0uNy4yLTEgMC0uNC4yLS43LjQtLjlsLjgtLjVjLjItLjIuNS0uMi45LS4ycy44IDAgMSAuMmMuNC4yLjYuNC44LjcuMS4zLjMuNi4zIDFoLS44YTIgMiAwIDAgMC0uMi0uNyAxIDEgMCAwIDAtLjQtLjRsLS43LS4yLS43LjEtLjQuNWEyIDIgMCAwIDAtLjMuNmwtLjEuOHYxLjRsLjMuNi41LjUuNi4xLjgtLjEuNC0uNS4yLS43Wk0xOC40IDEyNy40bC0uNi0uMS4zLTIuOWgzdi43aC0yLjRsLS4yIDEuNi40LS4ySDIwLjJsLjUuNC40LjZ2MS42bC0uNC42LS41LjRhMiAyIDAgMCAxLS44LjFoLS43bC0uNS0uNC0uNC0uNWEyIDIgMCAwIDEtLjItLjdoLjdjMCAuMiAwIC40LjIuNmwuMy4zLjYuMWguNGwuNC0uMy4yLS40YTEuOCAxLjggMCAwIDAgMC0xbC0uMi0uNWExIDEgMCAwIDAtLjQtLjJsLS41LS4xaC0uNWwtLjQuM1ptMy43LTJjMC0uMiAwLS40LjItLjUgMC0uMi4yLS4zLjMtLjRsLjUtLjEuNS4xYTEgMSAwIDAgMSAwIDEuNyAxIDEgMCAwIDEtLjUuMiAxIDEgMCAwIDEtLjUtLjIgMSAxIDAgMCAxLS4zLS4zIDEgMSAwIDAgMS0uMi0uNVptLjUgMGMwIC4xIDAgLjIuMi4zbC4zLjJjLjIgMCAuMyAwIC40LS4ybC4xLS4zYS41LjUgMCAwIDAtLjUtLjVsLS4zLjEtLjIuNFptNi4yIDNoLjhsLS4zLjktLjcuNy0xLjIuMmMtLjMgMC0uNiAwLS45LS4yYTIgMiAwIDAgMS0uNy0uNWwtLjQtLjhjLS4yLS40LS4yLS43LS4yLTEuMXYtLjZjMC0uNCAwLS44LjItMSAwLS40LjItLjcuNC0xbC43LS41IDEtLjFjLjQgMCAuOCAwIDEgLjIuNC4yLjYuNC44LjdsLjMgMWgtLjhhMiAyIDAgMCAwLS4yLS43IDEgMSAwIDAgMC0uNC0uNWwtLjctLjEtLjcuMS0uNS40YTIgMiAwIDAgMC0uMy43djIuMmwuMy42LjUuNC42LjJjLjMgMCAuNiAwIC44LS4yLjIgMCAuMy0uMi40LS40bC4yLS43Wk0yOC42IDE0MnYuOGwtLjEgMWMwIC40LS4yLjYtLjMuOGwtLjUuNEgyN2EyIDIgMCAwIDEtLjUgMGwtLjUtLjItLjMtLjUtLjItLjYtLjEtLjl2LS43bC4xLTFjMC0uNC4yLS42LjMtLjguMi0uMS4zLS4zLjUtLjNsLjctLjFoLjVhMS4yIDEuMiAwIDAgMSAuOC43bC4yLjYuMS45Wm0tLjYgMXYtMS42bC0uMi0uNS0uMi0uMy0uMy0uMmExIDEgMCAwIDAtLjMgMCAxIDEgMCAwIDAtLjQgMGwtLjMuMy0uMi41djIuM2wuMS41LjIuMy4zLjJoLjdsLjMtLjMuMi0uNXYtLjhaTTM4LjggMTQxLjN2NS43aC0uN3YtNC44bC0xLjUuNXYtLjZsMi4xLS44aC4xWm00LjUgMHY1LjdoLS43di00LjhsLTEuNS41di0uNmwyLjEtLjhoLjFabTguNyAwdjUuN2gtLjhsLTIuOC00LjR2NC40aC0uOHYtNS43aC44bDIuOCA0LjR2LTQuNGguOFptMSAzLjZjMC0uNCAwLS42LjItLjkgMC0uMy4yLS41LjMtLjdsLjYtLjRjLjMtLjIuNS0uMi44LS4yLjMgMCAuNiAwIC44LjIuMyAwIC41LjIuNi40bC40LjcuMi44di4xYzAgLjMgMCAuNi0uMi45bC0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zdi0uOVptLjcgMCAuMS42LjIuNS40LjRoMS4xbC4zLS40LjMtLjV2LTEuMmMwLS4yLS4yLS40LS4zLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41di41Wm01LjMgMS40IDEuMi0zLjVoLjdsLTEuNSA0LjJoLS41bC4xLS43Wm0tMS0zLjUgMS4yIDMuNi4xLjZoLS41bC0xLjUtNC4yaC43Wk03MCAxNDEuM3Y1LjdoLS43di00LjhsLTEuNC41di0uNmwyLS44aC4yWm02IDUuMXYuNmgtMy44di0uNWwxLjktMiAuNS0uNy4zLS41di0xbC0uNC0uM2ExIDEgMCAwIDAtLjYtLjJjLS4yIDAtLjQgMC0uNi4yYTEgMSAwIDAgMC0uNC40bC0uMS42aC0uN2MwLS4zIDAtLjYuMi0uOS4xLS4zLjMtLjUuNi0uNmEyIDIgMCAwIDEgMS0uM2wxIC4yYy4yLjIuNC4zLjUuNi4yLjIuMy40LjMuOGwtLjEuNS0uMy41YTcuOCA3LjggMCAwIDEtLjggMWwtMS40IDEuNmgyLjhabTcuMi01djUuNmgtLjdsLTIuOS00LjR2NC40SDc5di01LjdoLjdsMi45IDQuNHYtNC40aC43Wm0xIDMuNS4yLS45YzAtLjMuMi0uNS40LS43bC42LS40Yy4yLS4yLjUtLjIuOC0uMi4zIDAgLjYgMCAuOC4yLjIgMCAuNC4yLjYuNGwuNC43LjEuOHYuMWwtLjEuOS0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zbC0uMS0uOVptLjggMHYuNmwuMy41YzAgLjIuMi4zLjQuNGgxbC40LS40LjItLjV2LTEuMmwtLjItLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0uNS4xbC0uNC40LS4yLjUtLjEuNVptNS4yIDEuNCAxLjItMy41aC43bC0xLjUgNC4yaC0uNWwuMS0uN1ptLTEtMy41IDEuMyAzLjZ2LjZoLS40bC0xLjYtNC4yaC44Wk0xMDEuMyAxNDEuM3Y1LjdoLS43di00LjhsLTEuNS41di0uNmwyLjEtLjhoLjFabTMuMiAyLjVoLjVsLjYtLjEuNC0uNC4xLS41di0uNWwtLjQtLjRoLTEuMWEuOS45IDAgMCAwLS41LjloLS43YzAtLjMgMC0uNi4yLS44bC42LS42YTIgMiAwIDAgMSAxLS4ybC44LjJjLjMuMS41LjMuNi42LjIuMi4zLjUuMy44bC0uMS41LS40LjUtLjUuMy0uOC4xaC0uNnYtLjRabTAgLjZ2LS40aDEuNmwuNS40LjMuNXYxLjJsLS41LjUtLjUuNGgtMS41bC0uNS0uM2ExLjQgMS40IDAgMCAxLS42LTEuMmguOHYuNWwuNS40aDEuMWwuNC0uNC4xLS42LS4xLS42YS45LjkgMCAwIDAtLjUtLjNsLS42LS4xaC0uNVptMTAtM3Y1LjZoLS44bC0yLjgtNC40djQuNGgtLjh2LTUuN2guOGwyLjggNC40di00LjRoLjhabTEgMy41YzAtLjQgMC0uNi4yLS45IDAtLjMuMi0uNS4zLS43bC42LS40Yy4zLS4yLjUtLjIuOC0uMi4zIDAgLjYgMCAuOC4yLjMgMCAuNS4yLjYuNGwuNC43LjIuOHYuMWMwIC4zIDAgLjYtLjIuOWwtLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM3YtLjlabS43IDAgLjEuNi4yLjUuNC40aDEuMWwuMy0uNC4zLS41di0xLjJjMC0uMi0uMi0uNC0uMy0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLS41LjFsLS40LjQtLjIuNXYuNVptNS4zIDEuNCAxLjItMy41aC43bC0xLjUgNC4yaC0uNWwuMS0uN1ptLTEtMy41IDEuMiAzLjYuMS42aC0uNWwtMS41LTQuMmguN1pNMTMyLjYgMTQxLjN2NS43aC0uOHYtNC44bC0xLjQuNXYtLjZsMi0uOGguMlptNiAzLjh2LjZoLTQuMnYtLjRsMi42LTRoLjZsLS43IDEuMi0xLjcgMi42aDMuM1ptLS45LTMuOHY1LjdoLS43di01LjdoLjdabTggMHY1LjdoLS43bC0yLjktNC40djQuNGgtLjd2LTUuN2guN2wyLjkgNC40di00LjRoLjdabTEgMy42LjItLjljMC0uMy4yLS41LjQtLjdsLjYtLjRjLjItLjIuNS0uMi44LS4yLjMgMCAuNiAwIC44LjIuMiAwIC40LjIuNi40bC40LjcuMS44di4xbC0uMS45LS40LjdhMS44IDEuOCAwIDAgMS0xLjQuNiAxLjggMS44IDAgMCAxLTEuOC0xLjNsLS4xLS45Wm0uOCAwdi42bC4zLjVjMCAuMi4yLjMuNC40aDFsLjQtLjQuMi0uNXYtMS4ybC0uMi0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLS41LjFsLS40LjQtLjIuNS0uMS41Wm01LjIgMS40IDEuMi0zLjVoLjdsLTEuNSA0LjJoLS41bC4xLS43Wm0tMS0zLjUgMS4zIDMuNnYuNmgtLjRsLTEuNi00LjJoLjhaTTE2My44IDE0MS4zdjUuN2gtLjd2LTQuOGwtMS41LjV2LS42bDIuMS0uOGguMVptMyAzLS41LS4yLjMtMi44aDIuOXYuN2gtMi4zbC0uMiAxLjUuNC0uMS41LS4xLjguMS41LjQuMy42YTIuNSAyLjUgMCAwIDEgMCAxLjVjMCAuMy0uMi41LS4zLjYtLjEuMi0uMy4zLS42LjRhMiAyIDAgMCAxLS44LjJsLS42LS4xLS42LS4zLS4zLS41YTIgMiAwIDAgMS0uMi0uN2guN2wuMS41LjQuNGgxbC4zLS4zLjItLjRhMS44IDEuOCAwIDAgMCAwLTFjMC0uMiAwLS4zLS4yLS40YTEgMSAwIDAgMC0uMy0uM2gtMS4xbC0uMy4zWm0xMC4yLTN2NS43aC0uOGwtMi44LTQuNHY0LjRoLS44di01LjdoLjhsMi44IDQuNHYtNC40aC44Wm0xIDMuNmMwLS40IDAtLjYuMi0uOSAwLS4zLjItLjUuMy0uN2wuNi0uNGMuMy0uMi41LS4yLjgtLjIuMyAwIC42IDAgLjguMi4zIDAgLjUuMi42LjRsLjQuNy4yLjh2LjFjMCAuMyAwIC42LS4yLjlsLS40LjdhMS44IDEuOCAwIDAgMS0xLjQuNiAxLjggMS44IDAgMCAxLTEuOC0xLjN2LS45Wm0uNyAwIC4xLjYuMi41LjQuNGgxLjFsLjMtLjQuMy0uNXYtMS4yYzAtLjItLjItLjQtLjMtLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0uNS4xbC0uNC40LS4yLjV2LjVabTUuMyAxLjQgMS4yLTMuNWguN2wtMS41IDQuMmgtLjVsLjEtLjdabS0xLTMuNSAxLjIgMy42LjEuNmgtLjVsLTEuNS00LjJoLjdaIi8+PHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjUiIHN0cm9rZS13aWR0aD0iLjQiIGQ9Ik0zNCAxNmgxNTRNMzQgMzMuM2gxNTRNMzQgNTAuNmgxNTRNMzQgNjcuOWgxNTRNMzQgODUuMWgxNTRNMzQgMTAyLjRoMTU0TTM0IDExOS43aDE1NE0zNCAxMzdoMTU0Ii8+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJkIiB4MT0iMTA3LjMiIHgyPSIxMDcuMyIgeTE9IjE1MS4xIiB5Mj0iLTE0LjIiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9Ii40IiBzdG9wLWNvbG9yPSIjNDc2NEJCIi8+PHN0b3Agb2Zmc2V0PSIuNCIgc3RvcC1jb2xvcj0iIzUzQkQyRCIvPjxzdG9wIG9mZnNldD0iLjUiIHN0b3AtY29sb3I9IiM1M0JEMkQiLz48c3RvcCBvZmZzZXQ9Ii41IiBzdG9wLWNvbG9yPSIjRDMzRTU3Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImUiIHgxPSIxMDYiIHgyPSIxMDYuMiIgeTE9IjEzNi44IiB5Mj0iLTE0LjIiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9Ii40IiBzdG9wLWNvbG9yPSIjMjI0QUMyIi8+PHN0b3Agb2Zmc2V0PSIuNCIgc3RvcC1jb2xvcj0iIzNGQTcxQSIvPjxzdG9wIG9mZnNldD0iLjUiIHN0b3AtY29sb3I9IiMzRkE3MUEiLz48c3RvcCBvZmZzZXQ9Ii41IiBzdG9wLWNvbG9yPSIjRDgxODM4Ii8+PC9saW5lYXJHcmFkaWVudD48Y2xpcFBhdGggaWQ9ImMiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0zNCA4aDE1NHYxMzdIMzR6Ii8+PC9jbGlwUGF0aD48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2YiIHRyYW5zZm9ybT0ibWF0cml4KC4wMDUwMzE0NSAwIDAgLjAwNjI4OTMxIDAgMCkiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjE5MiIgaGVpZ2h0PSIxNTIiIHg9IjQiIHk9IjQiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjEiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfNDY1MF85MTk0Ii8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl80NjUwXzkxOTQiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48aW1hZ2UgeGxpbms6aHJlZj0iZGF0YTppbWFnZS9qcGVnO2Jhc2U2NCwvOWovNEFBUVNrWkpSZ0FCQVFBQUFRQUJBQUQvMndCREFBSUJBUUVCQVFJQkFRRUNBZ0lDQWdRREFnSUNBZ1VFQkFNRUJnVUdCZ1lGQmdZR0J3a0lCZ2NKQndZR0NBc0lDUW9LQ2dvS0JnZ0xEQXNLREFrS0Nnci8yd0JEQVFJQ0FnSUNBZ1VEQXdVS0J3WUhDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dyL3dnQVJDQUNmQU1jREFSRUFBaEVCQXhFQi84UUFIZ0FBQUFjQkFRRUJBQUFBQUFBQUFBQUFBd1FGQmdjSUNRSUJBQXIveEFBYkFRQUNBd0VCQVFBQUFBQUFBQUFBQUFBQkFnQURCQVVHQi8vYUFBd0RBUUFDRUFNUUFBQUFoTHp2cEhCSGNFSzRZdnlLenhUSVBFbkJEQkJtUkdLWjU2T1huNTBlWWxzdjBJa0J1S0pCNUQ5Q0JJdVJRUVNabXV2bS9ZTFVDbzZ1UXM1WVZXQStZYkVNTkRMUWFReEZCSzBXdTUrWTNUNURaWmZwREVVZVRrd3JHOGtPUUVBMzBtMnZtL1hyY0t0QW9OSENDZUlPRUdTQmlCbWc1ZzhVeElMQlV1N0ZrbDArS3czcjRoOWg3S3FESlp6UlJGdEYwYlZ2ekp1RDVmMnF2SWZhQ1FPQU1aWUd5REpBN0Fjb1BJT1lPd0ZJTVJZRHN6NDE5UGlSdmJSNklPVk5FVHhkVXowc2poQ2dCOTNmTGUxU1dyaW5SalVBMW5NdlJIS21HZ3pLT1ZNdURCVWFEc3dTVHRsSFpZMWVuR0xvOFdBYjgzY1ZSaW1JUUNRUVM1bXIzQjdzcDFhSmtwMDl4elMzRG1DRWZRZU1QWFRvZzBWVURCSU8yVVJnTVkzR3F4OTM4ZW4rekNaVW5JdnBnZ1lPRDlBM2x2YmxuVXlRTEliQkVoTUNEd2p3ZXVoeHg0Vk53ZU5QV1Q2QVFnd3lsMlhMTFh5ODc5dk81azhrbXl4SVpxc3RUbDMyQXAxV0xvMHpUVHBVNFJwUEFlbFBZS0ZaV21LQW1XVXkvd0F3NUk5SU1GQkdnNUh6Sm5uZmh5cjI4dmtLb1JuNjZSZ2xuSWdvTTQ1ZE53Y2ZSc0xuMXRzRnp3cm9MbEllYk10Mkk1YkNZWmZDQ2p6NHI4VkdaUldYb2luYlo4WTcrVWdzcTBaYlNuVkZEMXh4RWxITHJRWHFqUzJrZzZxcGxvYyt5MlZPeXlsZXBjVXF4aG1LUUpDc1IrNkVIYUVHckl2V2lSV3lSVjlhY2pqak1zUDA1bmVwU0IyQ01aWFh0a3JvS0syODY5M3g3Q0pzTlBVMWRXU0t3dG1hZGxoemFqSTZYVzZHcFNVa0Y2TWhnbVdHanVXeDdKWndnVm96QTZmSVpheDRyYWRoV21qOUp6Q3dxNFMwNnpRL0xhck85SFBNRmtTa1l0WFpDOUx1MUxUb0o2Um9ySkFzQnN4WnF1VTFacElyRWlPUHYrZGkrbTZhRTFESllZSVhGT08vSDFINFk2c3BuR3dhY2I2N1lJSFpVU1Z0VkZ0TG1LTHgwczZCYTFja3F3SDFjOGptVWRzeFlmVVRSMCtIRWlPQXVnNVhkUHJWbjNyeUg0dlpLSTBHWFpXTmRSc1IxTTl6YWhKaU0zN2E2RHBxYW1QV0RUY01qdnlWMkY3bkI0akp5T3AwV01GaFNIa2RUV2JzY2VHTEVSeHBOSmRZUVVuYk0rUW5DOUN5YTdLOWRYa243anFOcXkzTG9reTVpeGVua3B2VHZPNXRQRmR2aU83YTBzajNQT202cmk4WlJ6dkRleW1yZkI5Qkttam1Nem1kMlIrL3c4YThHblVib1laWHk2NmU4M2ZCRlY5YysxeDRFZkxzUGU5dmVUMDMzcXh2UGZocjFYb2M5TnFJSDVTOVN6eVdQUmVXVTZkWHRkaHVsNHV1U0NlVDNLbmNwMlhmSlc5WDVIRjdrZEoyYmNrb2NUdTZsMGRDdFZWbE5OM0pzUjU3dlh3N1dlSmprc3ZWSkMxWjQ5NmVERFRLK2ttdW5TZHRGUDhBd3ZwMm5ndWs3M1BtTSthMnVqeHUrMnJFWUZieEowZUJaMjhkTktCOC9WTjFPcWQ3OE14RytTTHE0bXBlSlBDZTBUdlFjempWa2Iybm1oYnNjOExiaVJ1NXlmaTA3SFY5RWJvY21WNnQxYSs1d3MrdVpwbUx6enlseXVzUXM5RFkvWHhXSFpVSWp2T0FacHhiV3ZWM3pQTFhWYldCVmU1ckN5WHFnMi9GVkhIdG1yRjBNZC9TK1IwNzhyNk9kOFd1TmV4eTRBN09PeTNTeVZnYk95NmE3UDhBbmZTd0hWcFFPbndkTGZwbmg3b2NidnFpdWVySUFHTmZNc3VkNDcyTW9YSzc5dERtMTBLbHRQbGlWUjgvMnN2QlRVanYrYTFxOFI3S1c4ZWlzL29PSmx2NlB6bWlHRHRXa2w3ZHZ5ZlpkdGNlYnUvL3hBQTZFQUFBQmdFREFnTUdCQVFGQlFBQUFBQUFBUUlEQkFVR0J4RVNFeFFWSVNJUUZpUXhNa0VqTkZGaEpUTkNWUmNnUTFTQk5rVlNVM0gvMmdBSUFRRUFBUXdDYkRYbUdnMXNaN0VZUWdJUUVvQ1Vna2draEpDeHFZbHRINkVwSXpuVHBUcXpYc1NKRWlQSWhQcWh6R1RRc3lNZ1d3TC9BT0F2WnczSUpjMlFlNFljNHE0L1lLMkwrb2gxVUY5dzJ5RzJSQ2pyYjVrdDNtR1cvd0FkUVEyRUlDVWdrZ2lCRUNJRUg0ekV4ZzQwcG9sb3ozVGhxU3oxRCtWbFdUYWVVY0t3WjRtWmNmWVJqa1g2anFGOWdvOTkvSU1yNmJoTE5CS0RxWG1qNHZia2YvQTh3MnlHMlF3Mzh3eW40aFlTUVNDOGdRSUVRSUVRSWh4U3BKb1drakxOdE80VmxEVjBtVFUxZTBFN0hwUGJTeTVOcTIrM3MvNEhuK2dqTUU4OGx0MVNrcFhvNW5yZENySXZjNmYyMDJBVW1sWmx0ZWF1bWtncmlqK2dJSUlTR1MrWVpMNGgwSklKQkF2WVFJRUM5aVQyR1hZTkN2WWpuUmpKVU1teEtYamtvK2U2bzVFd1JiOGgxV2ZzMllKMzlHeUNIbkVxSmFWYkhQMVQxRm4xeDFzbk1aeTJjV25vUkpPQStqbTI0bEJMVWxDdHllMzViQkRmM0VtOW9xNHZqYlpoQWUxTm9ZeHVFMHcrK01WemZ4K3pjWjhNNlNTQkF0d1FJRnNDTWh1T1EzQkJLaGttTFFjaWpMSlRhT3JtT0RUc1prcldocGZROHVYa0VtRW1DTU5LNEs4ZzY4bDExU3VKN3Y4QTFDSnBEcXBrSmRXZTFLSkVEUUNIRlBlK3l5Q3lJbW0rbGRhWDQ3azJjcCtqd3lNNmg3SHNlN05aRnNDOW01amtDVU4zT1piZklqUG1SQXQvOGlmSVhGSkJ2WTNSbEkyVm4ybUUvSG4xeTY5amRvakNUQkdOMDdiQmlOSVErbFNtRERpU1daN0IrMm56RDNsU1Z1R2wxWi9jRVpnMUs4dGkzOWhFWUpQN2drQWtKQ2VtWjdFb2dTQ0liRjFTSGw3Q1AyRUNFbUxHbnNIRmx0Y2thbGFTdlExcnQ2UkhJalNwQm1oU2RqTGY3QXpQOVEzbDltOVFxeDFpdnJHbTBHdFIra3ZPdjFleW1KNlpmUWxGVjYyVXpucHRhcDlnNnZQc090ZGlpM3pKS2JORHFDY2FVU2trUkQ1RGtPWTVmc09yVnczMVNWU0dXMXU1VlFzckpLclZveEJ5cW5zN0pFS0UrcGF1Q2h3TUtJeVQ2ZGdaa1I3Ymp5QkRZR2hMaURhY1FTazZsNlJvbXBYZDQralpiMGFSR2VWSGtOR2hhWXkxbjVHUVRDOC9WSWJTTG1zb0tobERsWGtxSnpuRkNqM1M2UTZhdnRzWVphVzg0bHBCZWRSVjVOQWM1UUxSVWM2QzExU2IyTG1xY254TFVCMVBsaXFHUTlZNU9SN1RjbHBZWWpVTnZQVHplMURqR2xHQjB6aDd6c3RtUEJqQThIYjh6UllQQmpHc05aL2xZMXpDS1NtUTZsK0ZTUlk2MHhGSC9XQ2dsLzVtRlFVRW43aHlNMlJwOUk2YVMrU1I5OXVJSXR5SEVFV3cxRTBuZzVVd3FiVXBKbVhhVmxoVHpGd0xKbFRicE9PSitsd3hGUXhLZDZWalpHeTNKMFB6Q0x1Y2RMVHdlMHl5K083d2sxdlRKMnB1cXAvZGNWMUp0Nm81MUZiN1puSVZNcGtaMWxNLzg5azlrb1BTMXlWR3ArUzRzYmlKT2xRbk9wRGx1TktvYzYxSlEwVHNTWXFXMVVhazVMdnd0c1ZhTW8yWlZ6alpMS3RtbXBHWExWK1h4ZXhXRVgrUnZmbGNIbER2ODhjK2pGWTdZY1BVTjVPM1NyV0F3em1xWnpiayt4Z0xaVWNycWtrazdwNkx4bjlSZ2x0b1QrSklJZ2R2VXRmenJTTWtPNWhpRWY4QW5aUlhrRjZtNmVNL1ZtRUVhZ1grajJad2pUS3lWcEVwWkpTc3lTdmNpaXlUK1Vad0paU3N1YlhtT0Mwa2V5VE1McjRjZ3RwTUZsUXNzTndlU1I5L1V4Q0ZwcFpwSzZuZHliR2pIWjZZNmFNcFVjUFVlQ2t5cllpUGxEYklVc1J0VThrR3luYXROMnNRcHFDbHRzcERXUVRDL0J5eXhaQllSbkZvOTBvK2R5VEM5TmRTMXp1aStUNmswV0s2cTFQRk1iTUhpU2lUcU9UUEZkdFdwTlQyYXlkeVBOR3lDcXEvYy9NYWdTdzVqcHEvTlpwT1dITVV4bFJmRTNENnhhNmE0L09sRzk0azhSUXRKY1JOd20rOWZjVkgwWHhaUHpnbW9SOUhzWlI4cTRneHBSanFQb3JHQ0ZucHpTUXE5K1lpdmo3MDJMUUpFWHE5TWlFZlRHamhNcVJBYmxORzFwdmNPemUwZnlXNGRZVHBUWE8vbW01ancvd2d4a2tuenBXREVEUzdFMEkzS2dpa0N3SEhtWTYxTjAwY2pWRlBiNUNoaXE4U0x5Qk1FUml1akVvOWpHTVZmR2YxT1BwZGdwMytnSEJUL3dDc0toSTIrZ1YxTlh1dVBya3dVTE1xV3EvdGNjSnE2OUorbXRqRU8wWVNYcGp0RUp4T0U4eWxCN0VoZ3piSWpNd2lOK3dSSDIrd1F5TWdhL2drc1VINUhZUlVQYk9OeUQ1bWJTVHpyNlFobjlnYlBwRUpqOEg1QmJId3pua0Z4ZHZzS2x0VGM0bHQrUnpyZTJhbDhFU3RpeHEvblJJYmttVTJtU2RCWTl1VFMwdzJsQ3JhaVREZmJmZ01rb3F5dC8yTFFlcks0akxhQXlEWkpFNlNTUzJDVURnUU5zdGhLWTVQTWhESHBMeUNHUDJCTS9zQ2FQOEFRWFRIS29sRnNLTkI5bjVDb2NyN0tiSlpybE5QaktvVTFuTW81eFdXbWpRcTkvM0RRL2puOVVoc1YyT1NINjF1VjR1YVE1amtnbVQvQUkyb3hJaDhkL0lWekcwd1dzY3ZFQ0UyZmJTSlpZN1dzTmtpc3I3Tk05aVFxMGNiUmpGWk5hNzZQSXZwRHEvQ256LzcxS0V1a2ttZi9VYzFJc0lsa2M5L3Q3TjVJT0ZlNytWMCtPMHZ2NzArT3p2Zjd6SUdPVWN1eXNrUko5cko0KzQzMjk1N0VPWU54UWF2SDdVdzNoUktKeFM3cTRCWUl3dmIrTzJ4RElzTGJySWNsMXE4bnVpTFZYbkRhTE9rOGErcmhRWFZMaXh5U2VVTmw3M3NHRXRGdURiSVZCYjB6S0FwTzZlSXNvZkEva0liZTBzVzZET3dOUktGUEI3MmtuV1grclR2R2IwWm8xREVYKzRjc25ETUVMRjBpZkl0ejNTa2xUcEE2QmZvTzJJSEhJWTZrbTdobFFJemNWMVdpM0MzTGMyejRJUnlocXREZVYzZkFpVTNKLzFYMG1XWEdydFpoYitWSXg4TWZrUEdiZGgxVWFSUDVxdXNvT3Y2VWlhN0g2ajl2WFNJNW9ZWS9FelMyZG85VlllVkx1M0l6ZUk2eDQ3ZTBiYjhDSy90NzkxaWxxNVEzaU80aitnd2crRWtUMWZGcTIyTTdQSnJlcmplRHczamFiMDhUYlRNY2haSlp1cE1Zdms5TEJkbE55bjFKR1Z5YzBSSWpsaWJUSFRMdlpDR25YMkM2cFVsdW13Y1FkYThQZDZ3SkpxV1NDRDBVMkdDa2V0UkhGYzQ3bTBZcHliYXRXaWNXa2pmYzZjVlByNGhjR3g5WEsyOG9iQk12cjYwNDNRMnFDU3lReWF6UExqTHRabXhDbVo0eFQ4aGorYm5sckRpN0ZIYlNtTGJKTWlmVEZYVEs3U2d2STdkKzVWT1ZQU2paaGZWbVE1Sy9NcnF4dG1MVzNjckhxbExOWGMrclR6UDdzN2dvbHZaT1BzV2lpTkJoemwzSm1sQm1NcHhQTExSeE0yTFR5ZUdBNlRVMCtFeE55NG5uWDhoc3JpcWxvcHNmWWpxanNsbVRha3lQQzYxWXNMbDk2M2FYSVVhQmlObXJhYkpNeFl4cS9LYzFVelpvNmlNNnlDOWc1ak5oSVgyeU1MeTdDN1B3bTVxdXppVDIxOTlFVTJ0WXlMeXZaQWtmRnRkS1VYTkxkbk5nNEpjMlVlV3BMK0NhMDUrcklDcnJYSmxQSi94THkxSG4zeUZDdzFMeVNXd3RsNVRLZzFxRmtNVlBCQnQ3STBUeDUxWnVVbVoyTFo0UXhkNmR4NUxDOGdjdUJsR2JYanU2VVloQWVPczBPa05TdTZtV1RIRi9TK25tUElrVDJsbXVsd2JESXpKUml4OWtYOW5WNDdXRlp5MkVtMjFLWVhHUy9GV2cwV1NpUkFrT3FlU01ObGt0Zyt1ZmxZNUZFeUYyU21vbnlPdXA3S2FhS2JWaWpyS21aVEdwNmFGYjJqNlhEcU5iMlppNDFKVTFhK2lsN0k0bDY1YzFiRWZiT01pZHluSnBWMiswMmhjSEM4cXNveUo4R2hrUE1hVTVmbEZkUUpxTW5xSkJPWmkreEt2NDlpKzA2bUpmNVBpa0t2N2htVzRwNjMxVGlXbUp6cWZ3VitNYmVTVTBtRTBVVEhJTUdWSXo2UExkZ3RWekRxWFl6cno3QnJlYmVRdFltWkMvd0JKVGtpWHhRaTdqeXZVeEk1aVJKZFd0bmcwNlBFeUx5UG1EdGtzSHMrYWtqM3NwbWkvRXRHaUVISmFteGVPSkdsdHVyZ0xiNGNDUVJKT0ZYeTJqWmZpcE5NTEQ4YWpvVWhpdjJTbkZhc20rRWI4RUwwNFlkZjdsZG1oYTgwMEs5OEZ0dnJ5YzJscTBqZXd1V2lGYVc3cjZMKzhwR2NVbVZscmxMNVAyTUNORE5hRzd1UEpQQSsxVmpWZEJVMHJxTlY3YTE4VFNoQXQ3dWx4SGxaWFZOM2pWaGwyazl4UGRzWk5IYlJuSXRaakdaWWkwaXF2bm1Zeitnc2xubXVObTBGMFk1MkJUb3RQZlk3MUU1UldGank0N1VkVGZSa2VMUHVuMGJObHB1eTFDcXJWdGRMWE1QT3IwbnFOb3NoeDhuRUtYRzRHbExTMW9WMGkzM1Uwa3gwU1VlNm1XekJSV09XeHhHUlNzTGk1dllHZkVpclhkeURDZ3lzTkxCT0RyalV0N2xLaGJEVi8xMVVKSkN0VjJNWDRoYmlZK0Z0TXpNWml1dGI5T1BFUzhqcUlkVHRxcDRkV3hVb3NrRyt5WmxzTk1MNjRoWStpSkFXd1NFWnJZT0lWRW4xVlc4bWxnWXVXUU1USStuMEppUnFOWHZ6SG9CdFBsdjRCYWw5Qm9VUC94QUJJRUFBQkFnTUVCQWdMQkFnSEFRQUFBQUFCQUFJREVSSUVFeUV4SWtGUllSQVVJREp4a2JIUkJTTlNZbU55YzRHaHdjSXdNMEt5RlNSRFJGTjBzK0UwVkdTU29xVFM4UC9hQUFnQkFRQU5Qd0w3UDhEeG0xZnM0NDVzWGNkNmFaT2E3N0E4cThNcDZoc1ZJK2YyTHN3VVB1YlNCaXpjN2N0UjFFYlJ5eHFLcEI5eDVOU3BiOC9zam1EcldjbTgrQ2RyZHlkOTFHR1R2L3RuSk90b21oRHFyZU5LWGxVWnlVQmtuZXBQNU8rRGh5YWxvOW4yanZ2SUczZTNZVVhhRVVqTHpYYkN0d1hUd0F6RWxMU1plNWpwelVjU0REck1wUzk0dzZaSUdRSjRmSnZKbnFDcjBERFpJT0h2VG5BTk41TWpEN1Z6WkdvWVJOeDcwRHBnNXd1bmR2NU04RTRqcjFxUzIycUxRMzRyVzJFYngzd1c1b2h0VEh6dk9NdWNYZFBMMXFYS2xKc1NXWGVOeXpNTnVPRzF1MGJ0WEp6eHd3OTZDODk1UERQSDdDazhzL0JFekxXakIzYzd0UXpCNGJzbUpIYjRPYmZQNllobVo5U0d4ZWtoeVBXRnJkQ0llUGtqK0NNYUQ4VlBuTk14eW5adWRGQzEwbWNrNWhsNHNnY08va3VFaTF3elF4ZXp2NzB6bkIycGJ5dldUeHAzTUNJeTcvM2dUVzhMY1U0eUUxcXVZNStTMlA4QUI3bkhyRWw1VWJSL01RdlB0c0h2Sy8wcjNQOEF5dFhtV2MvVTViM3NiOGl2VFd0eDdKSm5OZkNCbjhUeUt1VU5ReWNtR1JCWFNwYytndjhBZ0ZKZnhIVGtQZ21Pd2VHR1MyUW9ER2RqVi9PdjcxUDlvWjhHMkZFTGV4VGwrc3NhNzQ1cnk3UGF3UGdWclkyQk9TT1hpcEwwc1NYeVhwTFYvZEhYZXorYUR2R01ndHg3RVJpK2VTOVZiM0JET3EwdFg4MjN2WG14SnBvOFZhSVVCNTY5RkE0SGF2Wmxia05RWG53Z3ZXcFc2M01RR0RIeDJPbnV3WHFxazRVcHhuaEJiOHd2UnhCTHNWSlBqWWtYNlNnLy9FY2JKSFRpWm9mZ2pVeEcvd0RNb1p2Rm1iUDh5bkkzVm1hUG9YbVBsOUlXdjliaUQ2MTU4ZWYxcHpoZ3grQUNmb3lKL3N0N2x2ZXVncUhBZWZ1Wi9oTzFaU0RFUmc0V3R3a3JtcHMvQ0JhN1BaUExldlRlRVhmKzE2U1BOVkhKcysxQmhsS0VObkJTZUc3ZHlMOTJMcDdWN0lMMkFYc205eU1UVUphbktYSTRzLzhBS1ZOTWlTcURKTDlGYlBTOE5SVjI3czRLU3BlU0VZb2EyOWRLblFjN1YwS0xGWkRwZnFxaDF6K1NnUmFEU01EZ0Q4MTZpOW1GZnZ5OWJrWHYwdVV1UnhkLzVTcHBycW42WkVzU3paNVRIZFMvUkxzZWRQeG9Yc1Y3SlBFNmJnWUlzZCs3dDJjRkpWSHpWKzF6b2tUeXFUOE1WZXdvZkZXdGFXMThXNTg4NWhOdFFuR2tBWGFEVjBoZXVGZXU3ZUhwVG5jNXJzUm9sYkttOXlBeUVRZHlZY0JmalMrQ0xRZEswN1Rsa20yWjBteFk4eGpndHprL25IYmlUMnVQV3YwVTcrcU9IKzZ1WDhGSlRZUTdTckpDZkVoUDFnaFg4QjMvQUZWeHh2OEFUYndCWHp1M2tWL1NWa09sVTY5cXE4WExaSlV0eThxYTR0a3FsU0NITmcwWWU4bFJTWVVOOGRudnBuMHBrSVBjSlU2bEZqUTcxaGpWVXdhUk9RMklXc3dxWHlCdzBrV0ZveTRLU3JvYVB2S2pCM0dhYzM0NWRDTUprVitHTkxZVG1aZFN0RWRyb1p1L01hM1YwSXpOcGZGbFZ1QUJLdTIzcld1d0R0YWl4WEZycWRIUFdkU0dxdFl6QWd1bVBkS2EzaFY2M2VhNUdPTWFrVTUyVStiZ2hDWktmazFZRmNYQ21ySENsYTRkRk5OTzJldWFzeHZHUjdLMHZxT1JxQUdDZXlHTFJFanRmREx4UzV6cFQxQ1NycHM0Ymc5ekFkRXVPdDBrOTc0bDVBSkJodU1PUXpHZlFvbjRveDVwbnQ0SkhtdG1vVUl6ZUlabExPYWVLbnd6R0lvbmtFeXlEakYvcUR0QnVXOUExYUZzaUR0YW1hUWgxenV5NFRLcmJqMXFENElZNWdKTWdiNTg4RlpMYTVzS3lRajRzQUhERDhVOTZ2eXkxMllOYXgwUHhUNTlMU1pKN2MxZVpyRFJkdXlVQ0JFZkFpbkdod1pobXJUQ04wWHdHQ1R4MERZcGE0UVVSc25HaGVxTzViUVo5eWpGbHdMZEVjQkFBbk9XSnptcmE1a0NJOGVFNG1qUENjcU1CMElPbTJBMWpuQWJ0VTB3L3U3R3d3ZmNzT2UzRTlLRVZyWWhBNW9PRTA1azJ2aDVFSzRmTXp5d1VNaVpQbXFMZHNnWE5tRDhHRTQ0N1pvMlY3MlJMdHVHR1JERGdVK3lzSmZFZHpqSlI3UmRtMXZaa1R1NmRxZFlMbU54OXNTVWc2dXNGb2tvenNib2JwRHNVVG1SSWJKZ3F5a01ndmlzKzhoNnVyTHFVU0xBMmFFY3hKSDNVb09kQ2cyWU1xZStqQ2NnSmdiMUVzTVNHNHVoa0NkT0dNdGFzZG9aRWdSNEQ0bGI4OHk1MlNpTkhHWVlOV2xQRU4zSTZWM2FCS2tidHZBMFRjU1pBTHpRU214bWsrSmYzTDJUdTVIeW9idTViNTl5RGFuTWxxbkxYdndYa2dKd2tRbnpxYldTRFBwVXNDeUczRDRJbVpjL3dkRHFQdmJKUXAwMDJmQlhvanc2WUUyT2tjK2xXMkE2Rlp6SGdVTWFaYmRhYi9sbXVJNnlBaFpaNHNJbk54TTU1YTF0ZkVFa1lrSnJHUTNEQjJrUWZnb3pwdk5uamlYVW5SSWdzN1BDRm5tMFJjaThodk9JR1V5bXNMbXM0dkZxTWxiSUxidUliTjR3ZWNKWWlSMSs5UklNNGJJY3lXUzFPbnJtdFU3UE05cXRFb1ZSQWFCTXFyRU5qenFtNStmVW5PekRwOXEzaGJ3cC93QUlLNmZRR3VKdzR6RTZ2ZHk3dC9hdU11L0tvMGU3dFlobkYwTVNtcUtZRlF4dXh6Zmd2VlVlQ2ZGc3dJaWptdUhSUGdaVStVV0FINHVjZHZRbkNsekgyY2lvZTVQZm9XbXkybDdCQ09vMHpRZ3hKMTYvR0ZibnIvL0VBQ1lRQVFBQ0FnRUNCZ01CQVFBQUFBQUFBQUVBRVNFeFFWRmhjWUdSb2JId0VNSFI0ZkgvMmdBSUFRRUFBVDhoWFVsWUFUbEFiSmQrWEg4blRHOVZNN2QxUDVNZGV4TVo2UHc4Sldxa0dTSzlwMVJqTHdKbEFPa2JRYjRnTzRRcld5SkZ0OVNObmZ3bEhFNkpDa3JJejRIa2cvVDNIYS9KdCtWR0NmNzZBQjBZanVmc3RNS3VIWG5xamtpcjRZTUtIUE5PejZUUFlYdUFTbVRyWVV1Z1JlQ3g5UHc3U2NsVHRRT1BLYVhnSTZjTVA0UitBZmd1aVZ6MGV3ZElxaXJQbkE1N0pYWFpyNC8wOVlwYVhuS0lRRGgrNDAzUjVTcmxpZUhmd0x0SDBxbWN1aEh4cHphZlNlYThyQmdUTDQwc1BwKzc4QW5nZ2grTlB3dENXTnpuZ2VnZldtYVdrVUsvb2RaYWJZQTczalNIb3Z2R3BsSmlpTmpGUnhHSXRoRUZ2V0dBU1lvVkx0dWZPOFJFU29GS2NYM2xONjBFeFlZNng5Wm5JZTRNZWNEMUFHYnJXYjRqNW5VWXg3eDJta0RCRm8rOGZWK0tFMUJRZldLVWN3NXJndGZYcUl1MVZIZjduUkxtNDNpWmZ5aU8yT1E0U0IwRW5OOXp6Zm44WUNwOUxlU0NDdXNYOWpNYlp3dzliWStPd2pCVW9PT1lFVUpaeCtDM1dlWkJjZkZyV1hwUEU4cEZ5bVlYMWhtV1c0Z0FMcGxEd25xUXdYSmZnLzZ6YUxOVHF3YWhZMXZQbkNyUzdWTGY1UzJybFFPNnVNMnpuNUJnbjZSL053UExFeXFqck0vaEt3UExCYm0rYnhpQVNtNzdKTWVJTlN5YzdsT1kra0tsNnhkUjRab0dzanlQOHZGR0pKb0ZJOUpaR2pBSk96dGZJQjdqMFR1YnIyNlNycSsvMGI4U24vaGp0dFB0SjA2eDFJRkFUekoxaENNWWRNR0ZxcUJoSGlIeXpOcUhzN21wZDFwZE1kdmd3NjBEMng4dTNvZy9rVWgwc3IxL0hNYkFnSFJKU1lYZ2ZrZW81akpzbzFNVXpBNUVQZUxwM0xuOFFWNjZuZHEraXpuQTRRcWJyZVp5eEtQaldzcjU1anUrR0V4ZmZLSHJwMEU4dWdrejd2QUU5K1h1dm9mclY3eStSZC92d0RIbmFYMGdaaDdwUWo5dXphYlM1c3ZTUkcvUWl6Szg1WlgyR2NZOUlHdk9ydXNUTVZEc2xqRUtqZGgxMmY4QVBTTENtRzVya21tUGhCTkNUZW84WGtsSHFvMTc0WE15UysxODFTZmFadVJJN25PcFJCamEvVW1EVVR6VkZlR0VXRDZLTjlYY3gxUjRTcTFjdzkwNFFFaHhzdFh1blRLdjNIK1k3b0YrNFEweWs3NlpQbU9MMFg4YUptZHNwL2lQbVBER2ozUng3Y1ZXblVKRElVYk9sY3c3UzhPRjhqMG56QUhLdExSMTNQa2YvaUxuTU9GK0JNc1ZpOTlLWkVLY3lhYVhoak5NOFA0VFBRVmkyOHkrVmJVMnlzaGRpdjFMT0o1VDgySyt2WmE5eWNlVHJqSmtkejRHTVdaVjZOUzZ1eGZwZVJTbzBiSFlmMEFqZkJUV3V1TWt4UTB2UTlJSHNHRHFKMEwwcFlLT21BTHlzU2hMTkdueHRMNU4yL1dscVdkRzU1VUlvTFhkbVhuT3ArNk5CNGJyUjZ4Sm82RVBqc25zSFI4RVF2TDRaUGlld2oraVVkOGVvVWN1WjBIWGdFZUpRV2wxd3lveGJUYmxPUnJ5NXhNZDJEOXFLTjliNXkrOGZQYU5QRDEyaTJOMmFtM1Q4QlVmMWtvRUprTXJUcXBiUUhoNlJqSDJUcXVlN2djMUY0c0RyTDd5SHduRVowa3c0WlE4THVEdEtLaHlMaHdFS0RVeFFiaUdpSy80Sm0zYzU4b2RXOVlXTWUrQjNHWmlDZll6RCtneWxDNHdKdGJYYUhXNWhYVVlaQ3JZZ3dPNk5USjR2WjFhL3FCNWdqWmJ1WURuMlVTTTl6S3FTMWpCbE05d2xnSmFPMkdDaGdsVWlyWHhqK2t5QWw5Y1I0V0JXenNEMHJjWnVmZjQ2WGFmdjhZT3RPK1A5emJNZ1BhM201aDFZUFE4Y3ZpRE96L2pLdlgwb0p5MkpiV05hcHZXNWFZT0lxalgyQnBxSFdsMWYyeUJYYWZWL3dBUVU1aVkvd0FaU2FaNlRPV2RlY0svZm44aGJYdm44blU5Si9JNjVaQWppNHhPaDRDVnhzNTJmYWNBRkFXZUNRaDRYekI0a2NkV05XaXJPY1FycjNYcTVvc2ZyZmQrNWg2K25ORWFHT3BBdzJxejRwZXZOSmpuVVJvRDdPcFE0SnF1OGFBRE52ZXk4UzNEVmJwdkYybHlUb3RYTkVRNG4wSHpnclU5bjJnRFJORHFVN3JxYktOSlVHc3JDdmRDQmJVL1U4NWRITFU1Ylh3cVlpZXU1aE4vMkNVYzJQVGErTEd0ekVUMzZ1TUhDNlBTVm9lQVBvM1ZWNXl3dUdvVEFienpkbFd5cUkzZUV1WGlRYVVERFF0Z0tCQ3l2L0ZndWFLMDdqcmhrT1R2eFRwRlcrb21KYUZXa3BVTkxLd0hnMnZLYmIyVjQwc1U1dGd2dElSMEF6TVRzZmNGbkMxNW9MNWk5NzdTc3F4RWF1L1o0VEhTVnlFdTNiUVE5S1Z1ZzBuRnRYNjFPSXJBVzdJb0NxN3FjTnNvMk52K291WmlyVTlLdUlCK3BLdzRzb1ZpODFKYXgyaDdGcFRWUnNYQTdxTXlNcUxXTTZzQ2p0Q1h4M0s1bHVCbnNFcmFHS1ZKYUt3YmJIZUxZQUM3ZjFQaVp5OTR3WnhXemc5SWJEVkhaWEp2WGFWMVFpQmhnNmI4V1h6bEpRdmlPdEQwbUYzTU5jM041dzNHd1FIbk9MK0pkN2tMdVdFT0pha0VzcWJLMmNRZnJUSE9uU0F2SzVsQWJuOEZIRGpIa2JsKzFWSXBUMXhBWkNwaDVDSUU5T0RabGZvUVl6R05Mb3FNamVaejlHM3Z4WGRvdGJhaFk4SGxESFdMUnIwbHhpM01scDM2SHVCUjIvb2N6ZXhLMUQ4SkZ3d1dkbzdaZzUvWW9tRXJpbC82Tm1mSWk1UkQ3T1pHeFdMekNLVm1WUG0xQ3BQckxSaEVsVGVNRm5MbVU4Z2E4RkxmS1ZBcXpLbGdxcExlcG1Hc1VVV2QwaFlhZXZFOFNoQW1OOXVPMElqNnhxb3lBMExRUTNmRUFINkdaVEthaDdJb3dVRGRuUTd5enRqakdvODlTWkh1MUt0bTk4enNqUE9odEdXODNrc2NseE55VzdpYnU1MFN3WWwzN2FMN29xVVlEU3A0VmVUMDFPWSt2cWhCbkNxNjh6U3VnWHJycDJPZHhFRWt3QVIxWXhVTHo4U1MyRGpiVVhEVXByd3pHV3F3RFdNNzRTMXEvWm53WmxPU093dHREVkh4Q1N2UENpZ0hsRnZjcFZZbE9vVFJsamtqRjR0WjFYM3czTGZGaTYzZE4xanBmNDFxK0wybGI3cDAwQzlVNUphdzgvZWxtL002anZoS0hNeW4yakhPSStIRFBFcEhPbS9LVkkza1kzcHZMbURtNW00ZE9FS1BGb2loSzBNbUJiNG5HNGNTVnJBNStZZGIwczlBMXNSVGhnNkd4dGNOQVZLak9aVzN1d3ZMTldIekpYdlFWbWk4dCswd2pBNUEybFpRQmZuekJ3OGpNVU9FRlZVR3hFOWFKVHdiRzFKWE1sU3VEWFRwaUhUUnFFczVIYnRoenRZMWNiRVNvb1NtWXNyWDFDSjNSQWgwV2tmb0VQRzgrVUpPbTBJMXEyNmNMN3k3NGRMdm11azY0a1NoSEpSbnZxTlFXc1NUZVIxbjJhNmkyQWlaY0kycW1GWGRVUk5FZW1tc3dHR3lXdEtYWkhSMzZiSi8vOFFBSlJBQkFBSUNBZ01BQWdJREFRQUFBQUFBQVFBUklURkJVV0Z4Z1pHeEVLSEIwZUh3LzlvQUNBRUJBQUUvRUVXbFlOUTM1SncxVkw4cUE4Z04wMU5WQ3FKV2V6TU1oSTdHYnNZSVVDUGp0WVJmSnlkckRCN2E3STFuUkhPK1Z4aEYvQXZENUhZbGlhalFXK0RHaFdQaW9wQktDTUtNNFVFd29sMmp4NXFWOWpVbkN2cmRQOEo5bTl3NExyaXNEQnpnNU9DRGRoNEZxMDdNcVhNR05hbDA0ZElvUVFjNGdKZTQ0aGEvaUdhVllHODY3ZkNiRGhNa3FrbERGVmFNM1BQMHB3NUNob3lTSEwveU9HbVgweVBCdU5BY3dSUUI3amVWUENjcFdHcEQ0TVV1N1pVMGxOWk4zN2cvMW82dzQyeFF4eTMrYnpMVjFrY0NOQVhqTGQ3ZjZDRllQK3dsQTBtRmhoaDNXWlpnWTRSODFudUJWL3dFNHRJb2JRNFI2bXlMeGFUbVo3bmVOMlpIcmlUVmQ0NmVUSjVNd01JcDJoV2pHZ2g0M3FQTkNaWWdVNUdxNTIwZllTNmQ5UHdhWkZ1OVM1cW5iRUdEYVFIK2xMNjlUZ1JKZ093cW5mOEFxSHNDUEszVUVwUEI4SVZab1Q4bjhGRFlRdGZ4NmxoRlRualVZSWJ1eGU0RFpVS1IvaWxHc1ZvWnZnVTdLd3dLWHlZSzRLdzY2Mk9oa0R1bi92VVFjTFdyOUxsUjZPby9zelFCQ2V3SmtSQkhzamZ2S1M5UGpUQ0xzWUdyc2RzaTRCaDRpMFFkMWNvaHdpck9HeU1TMkNkWlpla2tiNEVGZGhycjFaWjhnUE5Rc1R0bGVpcmFoYmR2cUUyYWlQU0dsUk0rRzNLZjh4aHA5UE80UENJQ094YVNOTkdMYkxidGNYdW83cXVudm9RMnYrelRnN2ZGaE54N2ZObDFHdE9NSWtwTGk2YVVidmpOWmdCQXRkK3krUnIvQUF5U3JXeCsyWisyckFQL0FJUkc5d0Q2L1ZSZjJGT2YwYjd3WWJCSGpJVTlKd0NNMHpnamdWWVEvZ0ZLZ3NKakF2M1pqQnZGWnZEZHdJRnA0MDBnUDVZQXczM0NHZVV2alN6SUVIT3VSY2JkaCtUSFZNb2c4K0MyMngzK1NJS1ZIczB5dk9uRXpZeGQyank2eXl3dlBJM0VaVEN4Vm0reVlhVEFjZ3Q4NkdQV25QN2FUTUFIVWJSS3NOeEVsbHJJM1l4bE1ZZ0swZFM0ck04eGZ1eVpLa0xrQnNCY0F6ZHJ4NFkwUG5aNG1ZSzR3YmdSSUIwblphTVh0MU03a1FXdVB2RjJRMHE0OXIyTWY4T29iajErRTBvY2lPRVlyaklnVlZJY2RUN25OVXpscHd3M2JoNHlLS1ZwNytyS21VTzNIdnk5cUJWb0FsUHdTTzYyVitxaUg0c2NJc1lQaEVZZ3U2RXdBbEJXazUvTXkxdDFFNTNaS0pudUp0VFRpa20rVVpzRHJDNFhCQ0FrTXdqVFNjY0VWYVVpRldrUzdJVVhOdkdaUXJUbndVamVMS2d5RERxRlJRWXRybzEvQ2paREJidE5qaWdwR3M5RnpJNy9BR2g4aDdIaExFYkkwQnZUK1JsRkFVVUpqSGJRejZobmhGZDJsOTZvWlVES1ovUDJTdnNBc0dHeSt1NFpjNktsVUJYQmxpRmZRTjRqbkVKV3EyUmVxdm8zRVEzbnMrY01QY3ZmM1g0bmdJNzJNZmx4UVFCVEhPK1A5VFlsSy9ud1NqcEQvcUpFU2JwQmkwRFZFY2lUOXBReXpNUllySzVnOFJ1cGN5eWZWQlhlQTh0bE45K0lsWFZmM1V3WmhyMVNkTVY5ODNrclF3WmQ2cm1qc0xxU2hvc2FzeDdOSVE3OHVFSGNJb2ZvRm81TE5QaUdCZHdhMDhNVVNwNUM0ZjN0bGVLVUlCeWJTV1pWVEhKOGhXdHdJRHhCdTNNb3ZmdGhraFFNdmxSSUszMmdCQ1ZYVUtIMkV3TGdyRm9BOGxlSVVuajV0L0djeks1WGhlOWhLaThxcS9IQ2w2UUYycnQzUVN0Umt0SmhSYW9LeFA4QTd4SDFpNVFsa202VTB4YmhCVkZwWXR1eU5yV2krNHhkeUpyWXE4cG5WSXdZVXRacVh6N1g5K2pKWHRiL0FOOGMxSk52RzJrY2VobUVnQjFZRFV5ZVJaQ0oyck9pV0dZTzRaRmc2RG1Pc3BFazkzYTRybnpheCtpVG5BNjZmQXRFOHB2OVhvS1ZBU3ZXQTlYNUpkQnNUcDFVWmowWHRwaVRXeUIzc1J5Y2NWOWhXbHVkcXgydmRWSG9wdnlzV251QUN3Nk0vYzBIcGxFNHpjOTRMNFJwUVZCQzBtYnZ6RCtrYWgrUzRYUXZYd2lYNDVCZDluZjZoV29kVmJyV2FjYTVrM3lyZXI2VE5KV2lIZjZSajlzclpQOEFJemIzS1ZmYWlXK3VhRW9xMXBrUUVNOENOVU9WOHh1TWowQ0ZWeWIyU3FTUjVSSlRWQ2JyUXovNjEzUkhwMGo5ZElWeHN6MWdjM3V5b2t2UEdIQWk2S3RNcVFHcHkwUVNaVnZNSG1jOXhyNFZ6cFFpYUQ3cUlLSjFVZ2V5U3ZPclV3RHdCcFFjUi94Z3orMFdDdExpcHpCS09sL2NGeEVOemhNV0xFUVVBcXFjZFREb3d0Z25YN2pvdkVQc0VGaEpEaWpFelg3SkNXTHZNTE9SbXBVY3NxK0NZazJNeDJveS93RGVNTTJLZHFmbEkzTkZHMVhJdGdqTWpWdW82TFpxRUVTMUV6Y3FXd1JMQVdzVGcyd2svd0Qrd0pHcGdHZW1NT1gydit5WHhBak0rZlVHOWdnQVZnQm9xR3I0bDVIY2N3dW9BaGFmS1JVdU05WWkwcjZsTU1Ud3pHbFd0b3RVQ3VZQnJJMHQxaEFCbFYrWGcralc3cEZRTWtLNkF5N0twR281T3BtcEpBd25NUFRGMDZXQWhaaVZEWVlIaGwrcnFFeTZGTHJabURXWC9Oak9SRVQ3MmplblhaTmlpWDBndmUrNTBhS1FxekVhZ3d0NnlXYW9nMHJnaGZzZlVwcXFRSFp4SitaSlgyRXJzcGJ5eFlDZUltdjZXWWUxVXpvYUQxMHV4blNKcUpBYzFWdlZRMldjanFMUVZOV2lBd2FGY1BjUjFxNmpGYW9ZQ3MwQWFIWjBHQ3M2WURjb1cvQVg5Z1pXQnB5bFR0bnRYaWhkSXA4MFk1Z1FaWndMbEJ5eFlCc2xhQTA4UnF1aDN1QitpV3RncXpnaC90eFFxR3p6L3BsY1c1dHlpc3BHbkpxc3l6UkdabkM3OXVZTDJ6YXVwdXRLZ1l1SEc2WlNBTGdvZ2dOeVhMbDFGOWw5L3dBUFRXQTJYb2lVSU5OWUNheTFVUVJDRElvMEdXRjVpdU1nRFBnYzVTaEhFRElsTEcxQTh3OXJvdEdIZUpmYzFCY1VkWDQzRHVGRE5WaUwxZ0R6aFZyWVduMUt1ekx1VUFKTjBkb2tpb3dnTjVXRUJrazIzVXZzTWtCUkNYOHIvTGRlUGt5UFMwbGhvd1g1azNwb0NMbVZkMEhWN2dWL0tFSkZUTENqb3Q1WnRES09od1VyUWFTMko3WHF4TXlUcTJVTjNGMUJiWXphcUJRWk1UVFVCY3hFcG9iTnZ0bGV4ZHBwRkMwcWlpM2dsL09SaGZnUzZ6VjIxUm1CMFFIVkFYWXRyQXZBM3JLNFQzWmlVNlZMNU5CUnR2VWNta3dxckc3amFsN3ZGeW1VTzJBT2t5QTRja3BBbUYySlc2TlhvM0F4MW1VdFJNR1FiOFI0cFVRUUpFb28zZkhwQkF0dFZrWjBSYk1rQWtjQllqOXJvTmh0WkxOVERieGpXbHFRSVhpZ1FncnVBNm14bGFoaFJoWEFTdDFMNU1EaWJSQnZzOHNTbmRUcktENk9idU1LRDRNNmdzWUlIeG9sQUdkdy93RGEwVXZxeWlsbk1MRjV3OFpPSWRnTjJveEs4NHdGQUxLMUVpZFpyTDRZS2xUQmcwaTBsSlcwbko0SnN1YVZIWVFoVWFFRlh2TFVRelZsYlp6QkNGSWxFZjFoS2RvVzFHVmlha2dQSUZUN0VtdTdVQzFDdFNHQ0tDN3dhbkZnR3NJTW9UM0I3WHFWb2lMc2paOW1IV21tem00YURFSkxyQ3h0REJ0Y3V5ZkpnazZkNGJuN0t5YmRWMmF0Ulh1YzVzbGNDY3NGcmxuY0J0YVNxY1E1TExVY2Q1OENWbHNBVWVhekQ2MWVxUVNVZTkwc3BaWnBveU5tQVpjeFVydFpXbG9wdExqZFNvVnh3bEV6Y2ZZcHhvdnB0U0o5TnRUUm04QVFWNklJb2kvSEpHM0ZnclRjdTA1NEpxSVhnKzB4V29Fa09WTkxiU2hhdEtZWjlxK2dzQ3dxckFyRElzODA3MGVHVmNJbGVjbzNtMHEyWmRxWU5sUUVOS2RLUVJndE5IWitvNDFXeVFYMFBVNVprRExET1FSKy9CWEl4Z2d3cExKT2JwUVVBRlFzSks1QWVQc3JsaGlkUEJDaXZHZ0NCU0VYbHlCVFlhTUI4cUhBTFlKYU9pb292WHFQdG1kTkZkQUFadGhSQ1VIbDl0Q1hXa3dzcldwdUdHT0FIVit5VVlBMGw0QWJyREJVTHJFcHZuUzU2RHBpUDBnT2dhN0QzVG1SSmpxY0EzUUtBOEVjYjgxYkFWRGthanYwS0VGRlEwbkVkckFyVXU2Q0ZXYVJnWFFoZ0R5UUJTalNEVXJ2VlIxT0VWeFRNR05TKzROaW5pQ0VhMzlvRGh3MHRnc0N5endsVkRXUXFxOVhiTlJSNHFtSXRBTXEzaVlHMjBVWHNUbjFGbndKbmQyaUFFcUJzdDU4ZEcvSnBoTTBlc0Zjdmh3NjJhY0lTREUxRjloZ0xWQnlrbjYyaXpTWXNHendPK0lXeWVGcEM1MllLYi9OYmhrU0RiMEVTenQ4bFV1aWxNUVRTbnFBWTV2ZDFUaGdZNExMSllvckJXck1WRmV2Q0RPcVlDY3d4RzdGR243RTI2VHd4UGdlZ0dvTUdncVNnRU5XN3VnTlhqeWpDNHNDMHhEVnJENUdLK2NmM3p6K2U4R0JpQ0RCMUhDRUR5Slg3TjhnY2lGQ0FwWmpVMUt3TEZRRWNqdW9UOXFVajBNQ3ozTEEzZ0FLdXNwcUFsSVBBZFVOYTRVSjJPc2xBUkFvaVJjSTc3OUVWdmFSU1p2UjNYUUlIY0pleUtINnAvL0VBRGdSQUFFREFnTUZCZ1FGQXdVQUFBQUFBQUVBQWdNRUVSSWhNUVVRRTBGUkZDSmhjWUd4RlNPUm9TQXlRc0hSQmxMd0pET1M0ZkgvMmdBSUFRSUJBVDhBRDdxNnZ1c21qSlpBYmd1VzRLQ2N4NUhSTmNIaTQzakw4R1EvQU41QTVmZ0J5UVF5SDRBVkRLWWpsb21QYThYRzRmaUl0dnZaQTlOd3lidXVodUNCSi9ERklXSEpSdkVndUVNdDRUY3loQzRqeDZjMGN4dUc0WkZYVjhncm9QdHFnUWdjazNkbXJvTzNzZTVodUNvWlJKNW9rclBjQWpQS1JhNmpPZGp2elRZcFg2QkNrbFBPeW1nNFRiM3VocWdyRUlXc2dodkEzRGN4eEJ1RkJNSk1qcnVHYUc0YnhPYlhqaU51dHJmYzJScVpqcTlvOWNYMmFEN295ZzZ1Y2ZJQnYzTnlzYm5aYUR4TjBONFFJV01XeVdKWXU3dUNHNElGUXo0c242b0RMZUZ6M2NDNXU0Mzg4MElnRmdBUVlzR3F3R3lEU3NKUWJrZ0VBRU1odU55RUZZcG9RQ0FVTTlzbjZLM1Rkb3VPNXNlRUFkTk0wTTA2amlPaU5FNGFGR25sYmZKWUFnMVlWaHlWa0dydklNUWJrZ0ZaV0ZrRUFFQlpXUWIxVU1wak5qbUZrVzNDRzZ6R0MrSy9vaHVKd2k2ZlUwLzZrK2VrNUQ3cmp4M3NFSmIzQUJLTDVlVFY4OGprZ3llMnFFRWhHWktqZ2UwYW9SUHN1RVVJa0lnQWhIWkJpRFZnV0JOeHN6SDBUWEJ3dUZ5UUFka1NzUjZKcGJleE5rY0RoWWxkaXBqeVRhU21ibzFOaGlBMEgwUUN3dEl6VG0wd05paTZuSDZseDRCelhhWVZ4MmtaTks0a3AwWWZvZ2FrajhoVWJLbHg3d3NFSVgzUWdLRUhVb1JCY01MQTNxc0lCdU5WblpBR3lFU0VSWEJQUmRtYW0wek9aWEJZQnFGaWNWbUFqR3lUTndVZExCemFtMGNBMEFSZ1l4cE5naFVQR2pTalZUV3lZdTAxSDlvWEhxdkJjV3E2ckhVSDlTWk9Rd0E1a0lUdTBzRmpsNkl5U3JITDFXT1V1c1NVVElNcm9VODQxY2k0NkJZcEQvaFh6RHlRWTlZSDJPYXpzaG9nQUZHVkZaemMxd2dqQ0VZZ2pUdE55dXp4OUZ3SStpNE1mUkNKZ2NoR3djbGd5UkFzckJBZDlPYUxxd1RnTVN0dk9UU2c3SkYrU2pQZHVqSUlvaSsxOHdtRHZGdWxrOXp4YXhOa1pKT3FobGZZNWxQM2xOUGUzRkhjMzg2ZHFqSVFOVnhEYkxOY1YxdEVaSG5rbzN1Y00xbVFVU2llNm9iNFUrb1pER1E3VXBqd2FodzhFNTREUmtGeFcvMmhRdkRyMmFwWHlOSkFSbG1YR25SbHFGREpLNlpyVHpQOHJzOGZVL1ZOcG9uRUF2OEF1cGFPTnVFQit2am91QUFEM3VaSDBHcUZPM3NSbEg1Z25TU0JFa2hSaTdDaUVVM0lLL3lpblpJRzZvWW12YVNlU25wMlR0SWNMMnpUV3RFeDhsTU1MUlpGVVREZ0tsSGVLTEZnUllNMUcyMHpSNC9zVVFHdHd1VEdVSVBlSlZSMk1OSEN1ZXFCZ3QzUWIzUDB0a21BZkN6MXVVWXdWV1ZKaHBuT1pyeXlVRzBhNlNFMnRlL1JSdkx3bzVLa1ZqNDVHV0EwUFgxUkdDbU0zb2pWeDRjTmxKb29zM0tnSTRiMUppR2g1S04zK203US9RQS9hNFVXMUtXZGdBTmlwWFBFWGNiaVBRbXdWTkxHeU96ellwNzJFa2c3c054Y0l0dHFtbHZHQXZvVkVHdmU0bmsxTW5wY3ZsS29sNGpXNFk4TnZ2bW5jY3RMbkFXdWZyYk5NdjhBRFBVcXkycEpVdkRSRU83NGMvTlVVZnk4MmtPeTZxaWdoN0p4Y1BldVJuMEF2cDVxYVZ6MzR2WlF2SFo4UFVuMnlWVEZIK2NaSDNUeW9UMzFUeU1ZMGdsYlgyelBHOTBVSnR5dXFiYTFWdGFCc0VETE5EYlBQSWNoL0k1M1Vld254dXVIL1pDUjBGUTlnY2JYUFB6S29aZUtIRXFOM0lKenNjaGZ6L2hSVFJ5a0EyQ2xpRG1scDBLMmkwOXVrSTZxT0Z6WnhLSE9CODFzV1NRMGtoTGprZXZncTJwcURUNG11SUxmYjFRMmpXRExIOWdodFd2RWVER0xlUVh4Q3MvdUgwVGFvSG9vdG9SeEN6aGNlQnQreFROcndPT1lPVnpxcDYwVFJGakNXMzU4MVJUdm9tRmpYbHd5dGl6dC93Q296bVYySnlzeXlCQWJrdEdFcXZlVEpseld6TnB6N0cyZStOa1dKemlEclpVdTI5clMxSEZtTFdzMHdEUDFKS2V4NzZva0M1UHJtbVBtZ2hjMXdBSXpWSHRLQmhJZWMvOEFPaUwrSTR1QXlPaWZYVWNUeXlSNEJISXFiYnNVTmd5emg0RTNDMlpQUVR6U2hqck9rWlkrdW9WUEZTMWxLNmR0dzFqaXk1dG5oT0cvclpVeldHa0xJbVhBR3VYN0owTWNqSE5QTlZPekNIa3h1SUFIM1JqbmduTFpUa2RQSCtGaUNFa2p6WnZOR2tyeGNHSjNxQ21VbGJmL0FHM2ZRcnNHMGpwQzQraFRObjdUY0NSQzc2RkRaKzFXYXd2SG9VK0tvZ0h6R2tYNnFPUTZKclkzWDhmRmZDcUNVZ2x1WTBzU3ZoRUJ0MzNaZFRkRFpUQUxza2NQSW4ydXFhbXFxWnppSEIxK3Q3cXZxNm1HU3ptZ0VqVy9KVXJwSHlnc2JlMWliS0tSNy8wRWVkdlpiVFpKTFZFdHQ5UUVLYWUrWkgvSUxaTEptQjJFaStYUC9vcUhqUXhjSnJSaEJKSHFibm9xS1o3R3VGN1h5UFd5Y1czeU4xTXdGbHhrVlUwa2JYNHlkUjdKcmFacmN3U2ZOYkwyWFUvRUlpNGdERVBkYldtREhsZ3NUY2drak80RGVhWWRTUmNEekhzZzk0R1Rpc1RzTnNSUk9WeVN2Nm9nZkJEQ1Nmelgrd0NCelVieW8zSmprQ2htdHUvblo1SDNXd0I4OS9rUGRUUW1TYlROb3VQTmJVTDJWcEI2QllINThsc1pqMjR3MGkrWDdvZUNuckphYVFodk5RMTc5YkJSYlFEemh3MktyRGhhMi9qN3JpTlgvOFFBT3hFQUFRUUJBZ01GQmdRRUJRVUFBQUFBQVFBQ0F4RUVFaUVGTVVFUUUxRnhrUlFpTW1HaHNTQ0J3ZkVHTTBMUkZTTWs0ZkJEVW1LQzB2L2FBQWdCQXdFQlB3QnJGU0g0TnZ3dVkxNFdSaTN2MSs2YzBoRHNwSHNCcnNJN2E3V2dpNzdLN0FEK0hrbkFPRkZaR01IajlVNWoyT284MEYwWFJVcVE3R2tIdEh6N0IxUS9CU1BiUzJWQ2xQak5lMVN4T2hkdWgyQXF4dVVUVFVabjN5MjhlaVlWWlY3Sm9DMG9CVnVVQXFDSTdLVkFkdExwMlN3TWthVk5DNklvRGRBWDJIa21SdHRiZ2JLeW1oQ2duendSL0U0QkhpV09Mb0VyRnp4UEtXNmVhRzQ3S1cvTHNwVlg0TnE3SklXeUFxZkhNUnNja093anNLQTJUT1JYc3IzR3BwUjVYWjlCYWp3TWV0bXVkK1FhUFUvMlRjV3RneG84eVhmYWdtWXpHTzFiWDhoU0hhUWl0eHNxN1FyN1hNRWdVK0tZN0xSdDJWMi8wb0VnSnM0YUthSzhrWlNlYTFsYWlyVmhXRllWN29vL2dQWllWMGlMV1RpMWJtSUd0aWhWb1ZTN2xoZlp0YkFLUGlFNEc5Rk00bXcvRUtUTXVCL0ozNklPQlFkUVdwQTlVQ1ZhOXkwWll4MVRabU9kUVJWL2hCVmhaT0tIZ3Vielh3dW84MXFDMUszUE5WMlhRUUdyWlJZMlNMMGJLS1BpSTVtL01JTXlLM0FSYThEM25nSnJHdTV2dnkvZENLSHJmcW1zZ0hUNy93Q3lCakhRZWlKanZrdGJWcld0YXlyVnExcVdwVFFzbDM2cHpDdzBRcXREWTdCSEhlakZJQnlRRDJiMGhuNWdGQnlkbVpidWJpakxLN21WWlFlNXZJcUtmTkl0dTRVYzJXNGJzK3FBbEk1RmQzTjRMdTNqbWZxZzMveUhxdmNyZDRSa1kzK3ExM3pWMzRwR2NMdmd1L0hpdS9iNHA4a1QvaVBaWVhlQmQ2RjNnUmUxRXNvb2hwNm9BS01XNVJ5U1FpbW1rL0p5ZWp5alBsdlB4aytxWWNsN3FKS2JpU1YvTVF4alc3ME1lTWorWWhqdy93RGNVSU1ieEtFT01CeVQ4UmpwRFIyUDBYc1VmVWxERGg4VDZwdUpqLzhBQ2hpWTljbEpqd05pTGdBbVJ3a1hROUVKY2VqUVBxbzRuSG1oQkhXNVBxaGp4RDkwSTRhM0M3dUFmMG9LTDQrd2dLdExyV3RCNVRYa0tLVWdFSVN1Nkx2WG9UUDhVWlgyS0tNamoxUUtCTklHMUthaEtoZGJFSGJGUXZUQ1NFRUVBakZTaWpwNm0xaDlXb1dsOGdaZk5PUHVncWdDVlNrRkJSY2sxQVdxUmFxUVF1a0FwdjVSVVd6RTJNRW1reUtuK0NiRzd4VFkvbW4wd2tCR1V0Y05sVnFOdnYyc3BsU0tIRG55cEIzZklkVDVGT2lMWWc0bnFnTnp1cVVtd1VNYkhOc2xOZ1oxS2JERlhOQ0NKWkRHUnd1Y042Qzl0bThCNkwyMllBa05VZWRNUVNXOHZxaGxTRTh1Z1AxWHRNcnNzUkhrVUltMXNnQUNhWC9WL0w5VXhOS2tvdVZYSUFtSm9vN3JQY1JJSzhGaHp5d1JsOFpvclVUR0FUMVhKeDdNZzdxQSs0RTF5YTVBbFR1LzA3L0w5VTIzTzFOUk9XYjBnS0wycmZYWHlYK2NPWjZEMXRDeHhMOGdtTzJYREkyNVU0YS9jSHpDbDRiZ1J2M3NiZUtjd1JFcklpeEk4YUthT1QzajhRNmo1Z0tNdHlNanUybERoc3VyVmFqVGhRV2NQODBlU0VoMGFGb2NNZ1FqNGlSOVZQd3JNamVYY3g1ckZaalBsckllV3Q4UUxLeUk3a09qY0tJNldnRkNRQk5rQVRIdFBJcWNIdUhlWDZwNWUxb0E2dVJ4OGpjZDZvbzlPclUrNytpYjNWZ05QUWVsN0lWN2YrUVRhcGNFYmlSbHhsMmNPaFdYUEs2WFo5dDM4UHFzekttL3hGc0FjUTEyKzMvdC9aUnNhQnBUd1dUNmdUL3dyQXpYaC9keUd3ZkhvZzJpbm4zRm14eVNTQWdkRndqZzBEb215eml6enJvcGVIWVhDeEpsenlXL1ZiQjFKNStuUS9KTy9pUnoyRU9pSHIvc21zRXNMWGFSZEJaVVpqY0U1b0w3S091Tm5kOVB2YUVMby9lYmFZNTdTSE5ORllSUHNyUEpTQmptbHBBSzRwR3dUdG9kUDFYQzRZSDVYZHlBRU8vTGNlU1BDOElmMEQxS0hDY0xYckRkL01yMktBV0srcTlsQTVXdlkzdnVuYi9QOTA3aDByUVRZM3BZdUlJSmhJOW9mWFEzUjgxbnh4Y1JmM2o0d3c5ZE8xL3QwVHNESDA2U3dIejNLTDNDeVZhYUJxV0tOTWRlQ3pPRXdjWXlXbDB1bG9IaGF5K0I4RXhzTXd3dGMrVGM2enR5NmFVeDdHWW9OMGgzYzBvTGJJT3l5T0c1QnAwWTZkVnBMUGRQTWMwekR5NUdhbU1KQldId2wwN1QzeExDUEd0MXhlSExoRU9vV3hqd1I4aUxvbFR5ejRjNGhkUmM0QjFiMk5RMWZZaFR5T2RNVElhMytmNnFQSmRFOFBhTndWbmNZeVo0aXc3RHJYS3VlM1cxd0xOa3lRL1hlNTJCM3I4MFdJc1kxcEx0Z0V6S3dpMjJ5TlA1aE95Y1F0cldQVmUyNEE1eU45UWpuOFBic1pXK3EveExoWkcwemZVS0tmSG5zUnVCcndSakNxa00vTFlDQWRqOGszaWs3YjkwZWxJOFJlNGt2amFmTWIrcXlYd1pJQWMwdHJ3cjlWdzdBeDVJeVE4a0EzVmZ1c3RrTElYTmUrcjJGbFN4eHgzVWdkNVg5MXc5MGNlT05WK2hLTTBIZ2ZRcmlNbUxZTWpTUjVkVk5rNFdSS1pIbDFrQWVnb2VQUlpFTU03V2xnc0EyUFB4SVhzY2diWjJVREdsK2x3c0ZZQkVNWlpFS0FUMzVoY2FjQVBKY1Q0bEI3QktHZ24zVDlpdUF3YThiV2JBcXdMMm9seWRIc0FEUldocE5rQmQyem5RUWpaNEJmd3hLSko1MmdmRC93RFRrQUNFUW5NNnB6VVdvaGNFQkFmK1g2citJOXNkbm1mc29KZ3lHN09seG8vTWMxdzRSdnhBUnl2WkhSMFhGVEdXaldOdC93QjFzc0hHRTBGbnAvY3FYR0ZhU1NuOE9hMzNsaHMxRjlkSyt5N3QvaXYvMlE9PSIgaWQ9ImYiIHdpZHRoPSIxOTkiIGhlaWdodD0iMTU5Ii8+PC9kZWZzPjwvc3ZnPg==", + "description": "Displays changes to indoor temperature data over time visualized with color ranges and background.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.rangeChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.rangeChartWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: false,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries' }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-range-chart-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-range-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 15) {\\n\\tvalue = 15;\\n} else if (value > 30) {\\n\\tvalue = 30;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":2592000000,\"interval\":86400000,\"fixedTimewindow\":{\"startTimeMs\":1700577227898,\"endTimeMs\":1700663627898},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"dataZoom\":true,\"rangeColors\":[{\"from\":null,\"to\":18,\"color\":\"#234CC7\"},{\"from\":18,\"to\":24,\"color\":\"#3FA71A\"},{\"from\":24,\"to\":null,\"color\":\"#D81838\"}],\"outOfRangeColor\":\"#ccc\",\"fillArea\":true,\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm\",\"lastUpdateAgo\":false,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:aW5kb29yX3RlbXBlcmF0dXJlX2NhcmRfd2l0aF9iYWNrZ3JvdW5kX3N5c3RlbV93aWRnZXRfYmFja2dyb3VuZC5wbmc=:IkluZG9vciB0ZW1wZXJhdHVyZSBjYXJkIHdpdGggYmFja2dyb3VuZCIgc3lzdGVtIHdpZGdldCBiYWNrZ3JvdW5k;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMwAAADMCAMAAAAI/LzAAAADAFBMVEUJDAsHCQgLDw4NERAPExKunJOvnZURFROwn5esm5KolIqnkYSlkoqnlY2ql4719PakkYermJCejIOYhn20p6WyrbKtqrCUg3qPfnWlj4GKeGyvoZupk4aMe3Gij4SrloutmY8QFhagj4jx8fMZHBibiYG/ur98a2G3q6hvYlispquhjICyoZm1sLYWGBV4aF6yqq19bmWHdWvt7vKvrrbS1d+CcGaysru5sreyqKm7tryzpaCzo5w7PTq/tbQTGRnJwMK2tsB1ZlvO0NtwZV8XHh7P0t6wpaOsoZ/JzdmDcmm3tLu9sbCSgXeTf2/m5+zFu728r6thV06ciHzDvcK6usM0NjTAt7qfin64rq/r6+7Mw8dqX1e+vsaXi4WXkpWIfHiPe23TycmDd3E+QUDSxcN0bWojKCccIiPJx8zZ0dJdU0sgIBvj5OnY4Ot0amSnm5iglJGLgn/f4ebCx9TOzdOHhYmpo6ZDSU1SSD46OTQnJiDV1NnBws2Pi4wvMS8vLCVmW1RLQjjEwceYg3fWzc2ckY2WjYx0Y1ZXVVORh4NFRUHR2uikjXiVgHSqn5p8cm1taWhqYl9jXl3c3ePIytTQwr5MSkfDubhRT03NvrdeWVdtXlOFcF6dmJtXT0ach3fLurKlpa2kl5WjnaC9p5Z+e3vGtax6YE/m6/G5wdOGd2QmLS+4qKGioKafjnxJUFfArqGNdWWXmqJlUUNbTEA7Myp6fobe5u/U1+HZ2N2njXAsNTljZWi6oIx0c3R0XUyWhXOjh2yymIWag2hWWl9rbXHHsqREPDJ9ZFWkmZCskX1ydn2SfGNoWU1dbn6Ba1iFjJWPk5puVkaqs7+aoKpmdYKmrbePclqyusicfGQVJy+Wdl6vkXLH0+O/zeEsPUefp7NCVWQ1R1LYvKXIrZtYZnJcRTdNX2rCoH+0l3pqe42FlKqFZ1GcrMV3k7p2hJTMqoqyxd1YdpyLoL2puNAXMkXk1crcyr1xiakiTW6JVDft4tlCZIdZaECsbkaxrrIPAABUk0lEQVR42pyYTWskZRSFy6pKKt+kk0V6GrtjDT2EEEnoFnpjQkPoFgzERQeGLBoxKKQh4iC4CIpoJIuIEkQFF0Z0r8KAoBsHV4orQcGFCAr6B/wJnnPu7bdS4vept2o60cnUk3PPfW9VtL6+frVu2pW2dNnaWltrba111lqdFlZnb2+vjdWu12q1OlWtV6n9/f2Vlf3196mTneQ+VxTFOO77c0WQXWN++ifpp8VxElP4mPCCP6IoTfFv4PD/mERDg7ngeWEwW0QRzhpAhAIR5UmoVq+BpC4SnGBZWV6+AxTgvJaHf1//8p/fGjlc/wokIoGzYBEBPxpMCT4ZSyLOaAiasTEXtKTMIpSOsQCmLglFMPsQYZb3wAK907xOk8T6WJYT/CsSUUT8kDgOlKQJmWBEQpPMt0j/l5PIFy8x0VBbxuK+gKZWhzH1YAwOstCbW/Wz909OYE8/3GPZm7L+DYiqBwQCJ0uCkxSAiYCBKz5RAqGuHGWXkQlxCbbg7HRa8mWvRmeUFpcbI2turQxhDniOo3+stH9nSZomIki7o1EepfosnBQ/FqbjZ3tojAXfHV4ZC47gitIvHAPptCWyVIVjviD5PJaXYQxobvVEc/JSAIj+D43uixyA0dk/Hh33oyQji2jwMYq81Mwa7wAR4n8VyuzigiiqMNLAGeal09lr19q1WttBginjGiMJ1q3WyeBkMBjcHsch+s804gdDliUpDlzTdPMYojUZjCDROO9YVmfWy+QMOtkuWSz+oqEzgAGFws+0qClXCxiBSDSGB/TE2Qm8GbwSBxjR/IfaytKMLClWlvIDPo0+un08Yu4JIYEBFFykEYw541JkhOIwkGCUfDlTC0VWmEJbVGTQ0tISggMN3snu+4/esLREkJFBLmQkIU4OZ/rdCCmBFPcksa0Gi6HBVTzjBoAKK8W/pcSQpk0JxeS2OI1YzJgl0iA4rLT3N5HLf5kbT3sWJAh9DSaqP9pBaiT/mal2HMBkdMY6NticBcs3zJaxdNa0w3SsxkRSqz/h2UfuHSUYs0SWmzhagwGi8/7TIQRM6d82YHqQ8aQ2N/XJYOQT1O/G3cxBdPOZw0RZyq89SdHV1RVA4EyxxbRwqMbkjIoMIk9IC5ajHACEIgtooCdBAx0HmuTPvXFQQZCCSxdI9SUSJbvbj9I8EolFHQyKDJscdlTv4ZrN5MvFlpwpphjIfWn/YePXCCMYx1k2HsfZP4MzJ4NPo7/ePeWIUhIsyXOSbHZzfhRPbPfOVDTTiJUl8cbRJu7jZONYUpqgNV9d0BrPC5Zc4aldnzQqMYgcWGLhqT4GBRSzZm65xxZ98nXyF954ZRiLLrIGKDiVlVh3XowKSZ9lBTqSUGBIYq4s4zdTwaAB8AAJD8RF2TdXjIYwYU4WisGQwtMvGQrW3OLczTsDNbVuVJoFAghRQs8ykKDUh0bk+Rp5M8WODzlLmnHLUYsmjH6YnOHIfKFmpk6GgcxLrK2d3+JSnl9I4zoQidOARdbMze0NqJPmHyttjBJzUxRLqY3FlGwpSHT/ed+irxmH7TpPoizhZIPQCCZVA2BmiILMhA3G50uxeImpxqqe/eBLucaWjGQRZ/VMOM2SN4JRpjWqgMWVmB9c1zuwk2lAS1ly/HsaDLIuwb3O5JZg4AtknWyXLKgzb2NUyIumZLDsKy6kUQ9b9j5266YCs4hFoPn5laG8ebrwJokEI1mVE0MtyxBUX0KmCB2FqsxZsr6zJvJT+Vc/S1VkgFGREaYY/dXKhDLuyBZ+HCVfvCNbWnhIizxIg+CcnZwNXvZfs9F43cdigTnisAqSI0Ix+8TmkaE1CR9rQIFpzVogskJi9nEapk3zCnkhTHgiAw2fkW26dFc8+1jmShhgcDaWbjbII83RGGphfr41QK2d3Q409msPm5y+llumIiRCjClhCjtP1daAARZNDOjtwkSIdNU+o0Lbss5sLJCMaRuLaLwpS54VmeJpaTSEQhL6ggM0Tyo4zzuDEhEGNkfkEtIYUymJ7BLKTxUZs1ppih/qIODUxVuzpv8LgzEWiiO/F1nhjKellHpRqLwEggO6SRxonxvOlW84kXj+5HHTCY1EDIUMRgOBZoAsz6Q85QVkkm1a1gAurMqwwnwJFO9kZLkkiwLjJIJxEcSBQAGBZG6G1lQqt+jNmabo6K9maCdxjjJLKDS6oBZgPVwXtXcYVNo0lRipZeMld345E8ZkSiQ4UV42WJZzD8mWGdJQC5XKQuXmEDDDk83IaaCCQRxFNZWVQEoPCFRd9jqAHFKeJTxoirAA8w0mTdsxvZW1Qlse7zBYasrekCULirfjBj1pLC56XngoNMSpzO+SZvC0ORIXcSlISClZnfk1PIeJBhGRqTFpQm5gmPLvzohFoQmRIY2xiIR7pU/JUrCFJGJpKC1eYd7JgANvSFN5AzDDob0aKMXGGWiZyz3BEpA2Q7MmSfmBijmIikhQGosyXgADCUaxGY9lvsuE4TI8HhPEZb40qEWTp96qbIGrMl2Znq5Uz86A8/zfPK0J53pcgkWcDcL7MxNYTDlG02aWCUWNHihmDXC8yhiatsZlj8yKdBBGSmeRiAIY2KHLGKWC04wBzfTjQ9J8HZVp/vhSsMBRWojCK+pLk3f4G+hrm7lg4BEDEwY/kEB6LxNeYqIBkKa1K5bQxQ4OGH1HafhMKRIeJAla4AkW0UxMT871WGrv2A2pZZkdhS3lOgOOkFKlJo5KMGxhuYTAlHYssWjMFEvYMWvQrr1NRvIF4yi3YAdgILQwoAhGuTcFJrDIF9JU3gAMm5o6GG+3ZI2BBBi3Rf5wSCZM8Rdi7jloyaVOIu2KxWzRmHl11REKBsy16n59P2QfZbYUot8wzYnFNF9yhiDzLDLFZmJy8okhkjNo6jfo+0eAKYwpaCjRoNBgTQETHfuzXjAkiFkhiLG812pdvdwZj/2AYUuWL2GuFIlgCIKejGsZZoGqYCkxYpmemppaGcKcsxeseWlSvnY7XmwlbyB95EAJhdvfSaLy7yA8M9gGY2lRI2s9v2dtDDA2wYRHffdF5SUWpcVRZmYUFVjC4IsEVxUZaCYmp6aW1odq0bp7D20xaYZSi10yxT20ZLhysZU5AkwIi80xW2Hnb2nXP18BiD1QuitLiAppxGEsM4szgAiuzDvLwrQEpqmJqan5OwwO5s7IcnP91112xuXuREQpKYCUFJktHayOP1yumTHIC2E8LwA5BIrHZabhLDwNBgLNHF1RWPinXBGKvJmcmm7RnKd0byyeuHx7UgFChWjxGhSXUUJPBAmkfuxdbE0o9f2V+p5QrCkHFHFIcAUQczPGAg51MDAsVLhbSmaMeKZQaZNPDofrw/f1CKxnyXCfodJiHtL1XqVYBX2U25d+Ca6KxSuMLII5ql9C9Q5YhHIIMfwssY1FwpDHSowYmMNAArGyqHkxyRTX5DStefDB6hA4725G2m9CssvelPscKvKPPny+oy+dpJAVmTaXPWvJnaOj6tGRYNwWwTSIwkUYRd8ER3Bi4q/MKyy+7ZsphTNkoTcPsg30hk3PTSgY72hBpVZl18KG/pd5VJLztkJagHJUx2qjyi7lTFssh4TZOBSLtBhYSLGKLoZFEWXeu5h41MwINAEWCjTb872z9fU3j23D0Shc3KqYShQxlwiuUz61UwaxdziAoSvF64uj6h5cOafqe15jUqOxYSRGM6OgWPAXsMjCgyRujWCIM4ERYMyCSptsDXvrvdslb+JyciSNPQWoD92jFBNO/55bE1oFH2gionyox30KFXbZrhrMQa0GVwhDYzY2NlBpRiIakvCBEjRyBkQS0s+84JQxcoWtzKwhzPaDT+JZvbdjk01i+37ZEIcoF13SHY2O+6NREt3unz4lFAfJJDgTYIgCmGr70p1p1+DM4WGIDOQkc7SEDcCNYSObwVmx8XLa7CGI6ku+GIvTrPSg18LzWek9jWFdH0RNn3/+0fHxqNsdJfHotP9WN/YX1vYIYA0AFfZhzUgegy+AYZldni+ft6toyQfmi6zRVul9zHwxngVYs7qwgAV5YHgABqdcwckSs0Lb3t5+vDfs9d5JtWkkYtGyNmwKXP6w09/p53n/xx+7qKi7o9PTOBEHBXfU0tuUwdSOHqtWH7usuTHLnSrKDCyggRgZZwETfVH+yYJTM7J6Mp3hCSIZA7ktEyLhAZxn76z3emeb1qEjc2H8h6CCQjJGcf+Xb7//7de82//o1e69fpbSGETIYaLICowgR489dol1WTsHDIw56KyIQwtSB7AyY+69CfCjhPBTKi8eD1j8HQYcWNQkaFBo25NvvNm7M2zKArdCGH98jo4EAhvy0ZfPPQQ981u/37370ekpSOwhAX9sRhRRPC6EuQQMUQ7ODw729g8gB2poj5kFCmRXlRl9YWZWzRcvs/FINsk6gyXT4uEJ0Rq1gTd7veHTpSfn4lWtQehlpuWi2z1+5OfX3379p59/6OfZ6K3+F329yVCRwTU+XItEKNSlwQAFal16K4NmaY0kY3hgITAqsdVVpyHLA3BHvgBkwjncmoKHwXn4id6bvfWXxjCFCGEkWZqned7s95vdZrZ577sfHn3xk3ujPMuzu7fv3k0yonIlNoKTQ9kXCuNCmOWDD+AMkBiaDS+yjcUbfN6/3yd/oMwbENoyYLw1P0BVHiCK0q/sA8GBxEJrSPN4D0KLhpxC7/4tC6DY7Dafbja7eaZv5H20si5sygE5ujf6DKlRJ0vHQ964wgLL+RHOD+TMuW8xNwCDc/bGxuzs4ixOSK54YOxZhgKGnRMTD6jKJlhlWLJE7phDgtn++GZvHS0aNGNDvNfytUuzubkJCsJBCSG6OOwbSfbU6d0vk/CyyWHEwuNSIswHYMHqwBiZossNHNAs1KAvoJFWV2e8wuxhmb54Z5Y1YxFGKLo8SD28/fD0nfV1tmii4IZB4Ri45/IGT6dIk0axfOp+NbqXp4QhqrkrFp7OgjKDL4e0Zu3wYMOL7MaNG6SZpe6HNfcLZjXAYMskkcYy4HBgBpSlZkLPmRMPqsx08UqDM9vbU3vwZpDdF3WTDDfbhBnXHju1MQrHKipPu1nsNDunLx3TGsKUnKHGMEe/03FuMXFVYRSmDHdHAohShQ6DHSJUxKLRSBBJDMjE4A0TbA1RtCoTIQEaIwIPMI1WC9HWmrRqVWha1KgYNRYfvMZ6wVarxpgYo29eEhN9hYc+uNb6fzYM0XX22eeAY3I+1v/v65nCFWrucgaZJB4QEcmIEGFVRDGcc4vRBPDQfEaDAKQNgNTTINgKmDi4MMZU5xEGAk8fWoF0Y5Y3tN6kmeSLR58UqQGPuRip+bomlU0U0hgMehg4E1DQx9wMDpQdOy5h8rsxLMBAmF0Ia3AUVVXhBAmKxDgDhYoGy24KWMwSeNPAdJE80kQzXd9bn05s8umn95tOYzjGwg0MBhaAlUc9b8kxJlmAIY0EFmjunrm5uR0cYl6/GmOqJKAUFckasLgtKhyfGU5hcRzmFJYW55bGc+M0BpZ4C7DWRJOrwcZpC9fuRd6k+0WBJ8clc+gsFtH4q0KEQYDVfB6xNzbp6XpnnsLhMHBmh2AuNxJKIDgBgaKLgERDc9QuV8MUClmDEqcrPEOY6cIbDzTvPVsWTg1hvqYOx62BAgwnPaIhCiVrCPNGB5Y3AY6Gw2GUMobyFFJ/DjBkgQDDzl8sijJAkIc4VZBCTY0AaaIeaPFiyxeCCEdxpspOyA2CNx5qnOL09ii+lP/iCdZ4zxMhiE4AAgDDToDzBUc2Z+6MZz9dgW6/mUEGBsEYSBANcVXBGGF4pSYgHi8mCVDijDCqQNdC4oivgFwKNUgwaKIXWnbhLd5xhZdeushYEiCOpA7SrNGCc6p1UxY4OFIQPHxxW+bmrK9MP8pW7NFLuhfHmubIYvnvOoeHhOYZB4EcJg4UHIUMMQCBgJWxMHcMTgMCyGEKzJqFhd1Y6ZixvX7AkCozbWSPOkjc0Bo4E2nLykpkbxIMU02pDxSEGFlgytgVF9XPLk5MDA4uzc5agEmeNGSRbBRQRRDBgAbF06W4NNdwrMR16zC4dxgBCQeJg32P+tkIN/pYrV96gsJmn8QBP4bR2TlIlfxNglGYeYwpxABz8yUDkxOrWpodAAplGEgbwiDAJHlSJRYeEpG0dqZhTSExbIYW5jbetAV3wtimZaQ+3csZji9gCMdphGOuUDCDv6zZZOJ/UGAKRTCP3vjCGAyhpqZQDU4sDXTTFw8zzxSIMKzEwlqCMXJGwQYgWoSaKFKwRo01QZQ4WrG5toGhVtieZodjEsrGpHEgvDGTrffmJOZMRGm2WTT3XH9iaco5pEWcE0vdA6F/kTPk0CGeVRo2zoEIKwFsCdRIw6NCdTalShp0PXKIbbPccSna1EYXNIGm32c3azRhTOMZg5jK4R5UxGD0cpRmdE9taereAw5JGIuLS0t7oKXFse7uc2AKwitkvUCqhVJNV8SjYDMgHz6roj2Eoilq4VCIBbH2qYGaNkqh1pfutRmOh5kHWtgetAKvGjGqcZh+MBrMOMKJGA5CjLETP3zy6t8//fTON2fOWAcjFPnhQaaGmSjW2YiHk+kgmENFo4g2TW5EYhwsod8hFJwynIWWhWnuEwSaYE7YsvEKA5vwpZZUWz5Cjh8MhsiPsXd/+OSrX37+6/eV5bNbt25duYcc7otYPF0EY1RFvJb4OHpVFbXVGkVHy+Im88VbbF/jhGyAo6LZZ0vLwl6sRc9kzqQ1Gwhb6oo1DmfAImX3qzXDEUD+PPHuD6/+9POv4Lhz65Vbt55d/m3+pGULDzmi4vLkiTkkeNY5s2UzI44wWnjKVXuN3OGEWubQEFXmz7o2bWG4Pd0+Gwne+NWbtNCsad/fYT7o4GIAiBVZY+D4+5df538LHL9/9NHJk9V63AutEkUg8kMkmH2iy5EzUU2nwbWjqITelMTYUPsEtBBjTzVqmhmAgeKAwKeglZWVoql8oPcg5gRrsRZoQpzx2QNNVk0HncrKyWKC/P3L+x/9tnz2ziu3XukcUNVJPDTlbTJPL/oPqr3w8dmLokDEutQG1GVFaNesfQYBK2+iUSldgghDc0jT0odNHF+2yfQmbEWz5KzCpGhYDX40Dhhy59kVcJwUhwkckFAMx7nMlfAhl7hj9Agc8AUo8eqy0qI4okwttDI/jGpIom2OsCrggwElzl319b1TDkOFDidDHGO2pSZ7UvqpMTsLflx5CzjmwXH6dJWiiM2XaW3s7/IRgI4gvydgCf3AEbOhTS1OsyXujbNnik5nYbW6AlXZ0FBZCXNGhtqHxn3wjCqDxnc5UU3O9GPdKVFTw5+xC7C88ttHoDh94YUoVICwCaZnPwCBoWoDgFds1zjs9HFALC5VrG43qU3OwzatEAgRVEAuo+EUB9YocYba2+/zic1Gd0SCCq9up9764603MJMmcU3WX6cpDvY1DPPdC9xCMXPFKQRSosv6XJKqUYpsGkAWuANDUGptDYASA2qGl2c/8FAg/DbMpW2+hljra2+fzc9YPqdJjmRNduq9h2547Z+3sJW2KcJ52uknn3xSczFw+FqsmGKri+UgoTkACwzrkkckPKp5xkXDCCuK5zG+Ls6NayhG8Ub5giunmwZBNlbOUskLrKlE4kxrocMJ/OJUrsTn337+gTUDKf5+DtMYCkNmsEiCEQtcwsIyWGIxAmXKOVxxMVCqiwsVWGXWpzQwM/TkNrshBwsdkgSjBq3SxEirG6nvTQ9muTx3QhpJhhh+4FYM5Fux2lXmjj9IYoB5ASvmIchiJUoO3GkcAy/iDsLTlMs+MR7PqxYLwswgBKKqMJ63tmcjqJA2ZAk43n/21k+FMAuOrN1a7IXf33QTUCoqiANr9BYWIg4wxIndHoPIEouxD/G44oSMYcVafgQcJQpVbM9fi1MI+lG95dq007JGMDorFWcBBzTl6D/rx/m4jkA5QahaFw0Gy6J6ZynjHfJLGWqeMLW2IevSKvk5Nk12TyTxsA1YAyrKlWptEuM1gCCfqTHGvCUoXBtrCqRALKRpKe+rbx/3OLPT5fagGpwwX/BFLpIYEF8qgTN6dcnSvwRbMmCJikTL5HzHN6zKEmaVqai6KoZ2pCxabGtMRXnKlvNoB20wD0gjDlscVItQsDZAQwGDiTeAQf+p91NMYdVWXrk3EymDQW+j1/xuCu/66dWlGEuM2lES1YYMUOQLxfdjuIsRL+Y2plAUbMMVTTduiZbnKW+qc7X0dx49sF1aM0Yw4GIhhvpLBRiKU3iQ4aaFNCPt9c/lW3OcuYPrF5xSKpU1BAyj8TCTMxBxSpxE+0u+5w9fsJxUzWkY7ktBA1uK45hcVlyxZTPmL4ApzS3xMMtlunuc6ca9KSCcWACCk7WCbP+aM+5NXeVQ75G2DRQbNqjkDJY2G3tmmyzKxOHfUNJLZdGSMmCgYFMJB/xAIYT2/AHFrRnNMeOoMOC/OK/YBi65JRZtGM4YBkJLUqi5U0RhCZnC67U4A4+q8vKWA3iFUM5Y5TS6GJ9gYJ7WCycPD1XIFb25KBp94aKMO8oqUb59yUL5fJ97/+SJFsMfnaU60M3EMOpHuiBnNP9HIYJc8n7m3GJxREWiJw8MkGzRAW/Ky3f3pvvFwdMpxGEv3ffga6n3dbQxm3x3avLwwQpH4aHvXJSViMQodBfYikxRFJ5i5ORSZ2EZLAHOTaUeW4Xe4WtGqVyfvlgjy2iDAArMBbVhSZEICWqpBE3ddJpvea+bDmiv3NWR+vzDt/oJozkpT05I+2d6N8sWSDTkCAIPDyHpdBSqRPEncdWsibEVrb2+wpb/LZysZyQDSi4qmBJt8BgLocVMERaVbJEz5XUjvb2t3kVGaiIGlIPNqY5Uf/9br1310I+PZG8Sh1XA1p5UaqZ3FzxhnEXL+MhGYddi/UCVFLHy3U2QRMOrQMVlmy/qvrx+YGxsbM/sZkzt87QIixNXiiyVhQXCixIz9JQhuuSN8j/Z2dlSDp5ke3pGS33I8xp8qTaFF2mgVH+qA+8FYKnWJnIGE8SYGxxPAyiKIjNcDhWkLzJAwNL3ss7bcuMlvd2zY9Ks1Zvpw7UkYFWAwgMqzstjKkU5GmDgrbOHboBD6oRoTl1d+YFuvBcNkk3ckMEGp3ZywxZuTZacIZIVVmGKmug5cuCuqIVbZqSxrClWe9MVTdenB044wyw1IM2ODVQEO4RBjlwlT2keW7jSi3FqmcZJRFGOk7VYcMVZDpzd3d1t2WiBs2qwzJwtCLx6koI1dMfbOtSyKDTkgtHy1OThB2qjGxPH6th5W664XEYYxQmH6O7m11i++u5F6oWbGpgjLBIHLra7qb4GrRmjTBQuTxKcUqdXonk8PdAf8S0okzfHtIYw2e5HuPiHdWnkQsjkc3AIBKcMoqxiV1N9emCMy4XUiRMnzAlAPPHEVy++89mnpz/6/ShW3s6eXT7KEArb/7h1NSCRNPo6huc2CcDPTkcJRJ36zc7kwXSPjWKcJsg8UVJlDuMCMmGIA6CZByoqDrT3goGLns5BK95998wLnzzxHRn+mv/yeTAQYmXl6JdvzyuqNJaUNUwcLldILQv8Y5fvF4RQIKs7M2Gam+1SB3UOHeGWlD9yprD9ns/aOcMHwooBviCVwJLn4viSFNygQPHqdy/+Ah++PLosH5ZXnn8FK1VgcDd8FrxAgDrpNlcdooYaZmpDopABYKFYN5O3uQ40PPjxnTv7ultlwjoGU08HkqmDr9mE3waYxkEs247LBpNABME1XKy1nWQsOcLb84VxTVwoOSEX3AdRbFD5bYLZX07RHbGQhhVlDDCkmfe82Q6Yrml8H49LtpkwkbY3vvcxTqQjkUi1rcuqwbH1DBAhfsCC+k+fnpx/BcG0fP+hV95uqMKM+lKOSNXGlUKGYnMrkOCv/l8kXbAGRc6YOq1M8z7Z2WwwNIXOCKVze/N2aOfO4TRG0YqcoOy2Gs8bhpmQctoSOf6JwVUOMWBP4LOT818eWlleeWX+nEdffXdpcXG2Xf/iDOcM3nifKj01XDo8PLx/v4a+1iqRBIcIuli59CvwDOMDIc89uJIIKaK4AGEVRJiu5oOziUAihQmPdzakkrJr2hobJ/bs0brtkx/NvwKEo1827Hjxhz8Xp7DTmeC/QaAt7P6Zg30XQ3tHRkbOPXXqGDlQoGSystNYQCMXujJ0HcHkz/46ip/Tp5NkQkWm/xFwdh4YmFqX+Jm3+YPevYQRwcQVeUcPPf/KQvTmJ1YZEo0kiPBE4ZXL1an7DvaN7AXOsRHAECdpsrhnzuKxk4bAMxx1XV3AGbZoawaDOJAwyWHWUnMzismdgTWguWb3vsk1UzZwDWZbv+k03L/WJtQUTjBEIt6JUvYDLqy5nZ3T0XNk18jI3mPHjpEmSZxOxQtRhNEMCleX18PH6pLDI4/3DZPBlEyCQ5VrlFRA2Lad+eI6HzSXXfP4kZ5AEIJKX+CIJNRNhuHm+pcL8zsS/anBhAj0pp4rokM+UW09+w7cRRqh4CGYuQgIkOjZ77jjumev69rZmRzZ+/juXQceaE9324inTxQS4tNCFXdJ4wADSCgnOf98VCjXjD4znr3Rmp7+BLI/QEgOKh5W+OtjfJofacvh97/WiyCS3gVD1/r0bj2GtT9E2dmcHMbj9+06MIQlPQwTKNZp3QEmCS9p6n4zA//vcaY7Nbpt23aWNZagyy7bfvA5b58D074jR5557nBrz2CjHt6/fsNbqSbH/pkEjLFx09HWUYMPrIfB4UQddknd98DeY3un+fhNGDHgwVGkNHWm1/6lm8svwcjtnZu33Libf/7ju/re5MMioGDFSyCARrfdve2CC1AuOH+jthPn/I+9bzeB7KXHp19+4OA+MB05PNPz4Xuf9yT4CjjzRJ+kORnyrHHZC4eJxGD/VOt46549h2f5J5fOdJ9J04AzZ9J6+qarX3/nnrkL47nop44+//z9KyuP3Xv/oeu2yYTjx2kBAaCXWEmCOd9oUAlh9PjdDz/88hcffr7PnOHpWZIcHW3etq35+OMPgwlEz+w73Do5mJ/Z6IU7x9NFoheRGf79g/DX57AZAE989dV3r998e3XD20efP3To0L3LDz742GOHbn22C9G3/biS+oL1z40yegH00gWgItYofvMmnv3jj59+Zt9zj7TiqwAeGMoAFQH5L/5l6/xeG6miOF78hYLig+0oqMiuWjU4GHFgYjRtog2rzkKrNCl1baeaGNcfpTas01BsogYa0jyYZCxtCGRlHAJCO3QJGJCGPCWK9CWhJeybiy+CL/4Jfs+500QXPzP3xujavZ97ztx7pjvNbgAP4SWEk2mSEq4mIc5F90hmBJ7FCdP6XLjOJTNGT8nz/veP3fP8jcMbN554/vn7Li5fukxX0MSiLHt8wO/zA032YY6BNMTjppajp+vtrlmtpOIZ/FHSHWGeOqTJf8C/cVfW6ek7p486R6lUqgMZAdlQKsqyzzfh6Ea9VAUVhOnqU8ImjCXvI/z/F8KfhbEeZDKZaDRayVfimXce+eHVG7/cuIG6/v71x4vPvXP91694awLT0+FAy5BUWdVC/km/f3Jykm1IRTg4NQOjb3UqcXyM2QF9aFFG1CG8dI6FedUk8IKb+mg8Hj/OHB/HUyBOzSxU8lWzvJaAjJuj54sFZGQ/I9npZNusVjFLmCQsAXgcH5/OFYgKIJOJ4mLDuRZ77fo3P4nR40lerBPT4IKAXsPRjmGFQiGNUGVLN04w/FQAc49PWnmKpmZzs7BX+RSPNOC57aibSXdh5o4/OgBRDFsQj+/laeyJYnFlJQliuXxsJbaS3EmOeSUsHxOSZ3SFAR/QcMJoUvM4iHur08mnUoGDq+HNTYigoYNNvgoZdCZRKWQOwvTsMU4AecBvwxfAwRGN/+CAvhWRiW4GMPj81+/hxxtwCPDk6cxr+LPZvXgqCocMolCoVPKm2SolivUVDHhneXkZHb/gBMnkTmWF/9POGCeul9cLL3UybIQO5tCvDVGtRvqEnKKbIx1EpsqYQqcFOkcURiEjHqzGd4Pi0WggvlepVE2kA5hh8EIfEYWVjj6/561HHn764m933/foow+slUyjXMRsg5UdjJNYzmH4OXEw28zFZT0PlXUUH5ABMgJDDGVIhFUUyUGyh1RVDalIk4jagAcTZSfIUFyYcovogpNu1+jEw1CZnjZLM2sCHj/OZ14kHn/koYsXP7/3nvHD8cPDy28forR+5b7f1h96aIcMirmqCMROEga5bR75OsFF0xsoyt2S/PCwlk8mt19BtTqmKPKEJFsKLlCg6A2VEhstxKh8kkZIoGA72RwSZQ82gUqpRCbtE9C3zhq94+Pf4ymkEX6Wvfj4Q9vbPzz6yrMY9bP3jqNiRoX828VllNyUMCDHY87lOHmSMd2kqwDvk7n17XW37ONCiSTAIt9SgGftfNJYZxmPoyiKKmmYe0n9f9jBOuv3mieURAFmk6WiebPMJmXEZeTSbPYawAH29rfjU/v7c1NT49+isMbU5vQSppzSBPOto0AV5MAORaW+EosVi7GZRGIN59pM3QZv2FhxYUI13ziVr4uiDKfKm2S2L6EUHDsxGpYkWarioorOavSNE4y9c3R0TFsVcRd104VAIC5siCo04AKTcqlUSrTBSb3ZbPZlhZZdjMCx/0XNrtX0WgtD3qlTNq3UMfAEoPQDGD+64jmxGNappG3z3rEh7tPo1pldphhUrxt5w9CpEuRy7OpB/KjTbWK1pM3n+CoKMMClyoU7rt5x4aOD4xT44+joz243EI//V0bALqxCLkaDVmDlfDdEY4RcDTFMMEWIYOwxQFfI9ueP4p4ZE4+7/6nxy1NzwWBwatELEYZvmgHiARPU4Qv7bLNRNdK1cZLBIhnYK1Q277qAQ/wARPggkDqi/eio0+l2qeGybtfrRjqn6+kCyUBHYLIHmQgZqBjEWSRCywYFWlLQ0PGKybtkN/n5pTdQLL/svTR+eX9hbi54JRhcuLy46H2ZIgDsnNOu1YTEcBNkFSyzU5d97AKZfWSwkHl5ETI0ScVEyUQ8PkqlWjRsXo8wdiMNDNvIwQGnXqvVbFsvAPi44TGFxsgFIuler+dklyLso6kMUheRUnBOSFZXr9miqkSD3RAv9ySj2yTjZTAD6Cbc/W/K51vgqMxhFvbhMrVhpiHjhcwb316C08sGLttOvJ5kAYyeQX6n07qh27AADv3mduGcAAWoPDQBJJNMp/v9Xs/a2lpiHUIdoWHx9HRrNYfyTxrhEW8wbrZxdKdt2x63fqPuXAYexJxgn3Q2WgbLeMf66b5Lr5UxYCHIcVSIulET15+4sdgo7BX29tgmDsqwoBNNuBhpvd/o9wdbbEM6I1TafDVZajca1rkECUAB0KjFG9i4MrDhJvvYxL/gd11gI4CNBRmdZRoumM1m23AF8IpYYPGQsfrZmEX3d1M0zVMQNkKnUE4wIjB1zk29hi+nXCMb6DAjHaSdIp00HEsoCAdScOG3EpWLbSSCD+HAiUYmKLjZgzowzyqIjWeYZmeEdXbWSCNFF33z4pfOXwlmUXDiyzqehgULRgMTe6BCHRu5KnzBsEwaMo5jhXZ3WWcoNJTCztxsOKxy+22XC2WaLtUdR6KAwAPNT8BF2LBKMDiHhk4qUWTwbY6xWy6Wphp7NUvyWG4qKxLjSA2Ldx7MKcqCiFJB5ccyTLHediPTBiSj67YlKZEvdq9dg44QIqNRlNSm4/ASxyojDREDCg3unCfqGxsTfq54NZ9f43sHtAURkOD8PHvMUZNaaZbxjg0GtwYD6m5ZzeOGRRsoq5wvqY7lKCoT0lDUROR8xWUPJ2TqawgMRWaYZRhp6OPXv3hzF9HZvfaxEOL+JhPpOdAlWEEhF6EhA8jApqbUNzyyn/CRCY4F6MBhHjYwEbiRYZlFyIDTAfsYkBFBGQE1yNB1S2EBah6MhIorMYi0WlSlt1ptfFV7Q/ItbX23ujq7Ojs7++5LAE9IP+jyJJ7O3e1jzmR3uiDAUHzcf+Dvach13PyTBQICJtkEImjoyeIKGqOwjBeR6TVunQ5OT8mHZAaKQFVFUyzJUjQEhYggV7Rq3oXzbSZRruwVDjKopVHmtCiykkeOZL97FyMfGvDB50s//vjjNUoAhZkgGZwC5BTesU5NTkqSPCnwkwkTBOhIBbg6chu5zZtmr/fXqWDQO24MBorQUQU0e5YqTADlfhW4NvBZS5gkQyqoc1r9btOAvKpGkFi7r39HBv8BMi9dayj4NaIMlIf46AAiOLZmSBOacLlNBQrChoTondZO52ocmdPT3kjmjDSojbAUSY2E+IBKFjIEu1Bb48ggMORSaDWjr73TVOuTn/QiW1/sqj9TWP5HRpIgDBN20TRouDe3bEQ2tn8kA42sa5NlDZx0ADaKIDIoZ1jm74HgFsmo4sCGQFcJeqxqGsUEJ8lkl8yvTVMIEYliGX9PQ4ZcsAGVmoEXX2uGjMmfm0u7s6tnX4ocG/k8+SNkds8UiXNYEy5+Dokfpx8dicmLG/40Npd5l2yWosIC3Nz+Q27Bpbquc202jIyQcYMSIjQ6NFyooSUmApXs1rxZhgzzKcvMXL9eOZfpGoEXX2hG0v6fm1u7q6uN11lm1PE18yTLaLAhFRETkuGDcGVkeRISZJINZrNXSOY2PiCrD6+QjO3FbcHYzZu9mzdPT3EO+rhm1JGM8FHUcxly+fjjbBmY8OHYVIvFNXzzOxOFC/ZSyKyxzJd/b705CxkW4A4M04xSl25rFcgALL4Q0EiGfGC3uOiZTMsaZATz2ZGA+/LBB8OWpcigABAyLmfHZ7fJgJEMwpLdIpkZofP11/ApxtaqkMH1DxlE5qBQaEZO/F/2tl6fXXU4MtzcHpERaYZdi/CJzs+h4Ve4sMw/bJ17TFtlGMYbnRqjRo1u4i1eajpF0eGxolUYpAh0qB8ohXBYR7POueLO4XJ6WtQWRIINeIyMnUampipDTBdGmCiltQ4TA6gjJkMXo4ltvM54i2li/MN/fN7vnNZLfHrZGVLH7zzv937v956vxREtc7R0dBCFv8MPEYB/t6mREdxHiOSR6uqaZcxuDSi+LBHPhsec1D4FDMLMXPIbxYcDaAYGPfuhvbUQcIyx4/N1ozFOUYbGEZzJAKZm/Q41GQmE1ZMxRdf1dDptt4ft4bBT9VO9FnGjElyA4p3/L76ADvmi0ThX8YsDYwPUHiB5Z0jj47RKnQmhi0YlyF5LILAQiKmqM5zWnbMLp+amTk39LUz/uBvHISzoQ3NTIVriHgANcCCfr3znQ48NGlE2upp8tLISMFjYRb5aXV+PxGI08UPIIFRnYhKGNpLJ9WVo1Vg80FM3KiKubjxohXXY24fmEiHwAp4OTEW5jKIeHYOZAcAYssRip2JOp9NuT9mP7FtPUjeCK2hqIF66GTggAReB4WRQ2wg46D4e9HnLdw491IqSEyyHKvF+/UFkswN7VzwVnfFkQJF0xZSkKHrK6XIFAjW0TAoOBOkM/49KvPhURe+V96YDPDHh4ebywNQIgkMNd+EWDqsxPB35PReop94ByaKqGxU0tUcCgc9nTZ/j/9B0fRcTRYkxlCcC0xKMW8tpCIfDDD3Bg2x0cBPtzFmtWb575CRPzS5z/ONOMsdMBG2AeByNGt6Soadd0PX/0q6jR62JJS4ZWqJHVpPzOSbhrOAn0UWWEkRxKSc7t6Gm4rKEnRE3sUAVsxRIpFDIvFKCg21hJgiotHBqBcWeYDM+4JA3E0Tj9Y7v3Mlh0Lzejq7fB5+sumfaR4JugnEWMP6uzQTATIfQO8K9Bzp8GHeA4QmHF1/c89HFF+Nzks6/kWBwIxp64JbLyvk8k/RcwG6VsyLTrFaR5eW6u3zkMHy2xFbD76bTOkVDODNVuLJASwwsu3kbIYxikdHpkERFY8zrK9CQOd6S8ZsAw6PshU2LT+0//eEW90BVV9JN84xKGEWUIsycufxv+PtaJhp8XLSDgPYR9DQbMCSZbjKH+T3PmJw7ApicKMocRqtzeCkrwFzL8KxzXxgoEsGYxd8WKv0oSVIt207O4L/ie8SUJkneAg3Pat6+cTS6qYEPmBvwVrzT3QQzEvSofNIsxpdxIODHSwTmtoRKcaZwFRm3eqh4qby4i+be+49dmJBNW4QECX9lsiwyUVKYlekmTE6us/VFx6L07i6LOh9eSZMvkqTv4UVMBUh4nxnVBWayMjgDGHgnJVIyY319BDNebtB4S3YAyYDB1YbFxfd+6SRnDBiXiYJHgUWWAVPKTxddA4f5JICRbizo0nuqjtH4IpiEwM8GPyMChAMuUbEKopyT7S19GHm0n8gSXk+v6zpQGMEYl1BAZK4haEaGMyJYAJyog4FjffAGMKABjvcBwAy1UmZGp+3dQ4ufvBz0EEzEqSgbMZOjwCTwcInMYYnJq32a7DFDQuAqiu+qmWy2CrBEBrxYdBcwVpEOSUzC37XcUqplDH3eGyFLGHNaAeabGjfRmCgEY8MhhRkjZxjBSIAp0tR2ey8zYPq5Vg5MfLwn6UmWqsGIS08DplDPFGE0eQlhVgoYQ+CBTKKq4s6adsBwZwAvWjkHSETKqKIADEkTRR3ZLJuX7cNjvZ29N2K3h0UnKXxZqPeb0z5ICnIYzoCVvimL74sWaQim5AFfbe1Lrc/yS1qHRpc7X2pd9ySn/MmIKx0+GfvP8Je4MwE4gxUxuc4bLgZOFT3QrDSBJpsFa4JHZUJL4VWapuWyGhIAzqmmWeU8E7MgzP6uZYfH0H6/Ea8pwICFKXCGhGLG5uYLCVsLDArTPKPrKV1irgRjz+wCDQ2bbnhTW3IZwWT659GkbiWYA08AZroDMCrKmX+PGStLkDUmDOGYHRdHG3X3wNJWRQIPnKEpKQF2QYObYMEko/2alwhGvlDOC1aNYPKay0MwzWiKFZ0hmHkUHSSQYLq1eUADawAjSEyk78kmEuwZ0PCMVg51c5iDrfOA2ZPpPzjTOVO5HhkIdaxHVL8/GDMYijSMnDFhbBwHHP9VEUag0W+YIhOQ4YxE8XahmMCgQRLI5gyYeDteZlFoNBRg4AwXSGhV6WlpIRjKhogvKMuYhHkb1oAGLFvLS8Z8NGZWCGb20IFg/OTseiQZb1mPOP1qMGBQFOcYRSMRTKMJY2trw52Q6MAQtcW5M+BApCUYpWZBxE0Q8Vz0mWA0pGbPQG+8p71qEjAkqQhjWgMWQiEYm0phlhJ1jL46DB/M1kag7SBn+vrGyZkVw5kDA/GFQYKxAWZ4OBjgZT8ozFlGNmAWsDaj/AIW/MH/NGGK1rQ3NSfou5HSrAL9X7gEQiqkM5GyGZypcw+g6rq/jcPoJo0ofV1jdurgCnDmllc3GzAJlpJ0mOMCV8/h3ugusmbHjq3l3d4+GjODgEEGmAdM8NBMJLkBZ1SCwT8PEDwb51KicJFNZwpqwY2rqYVayByGEoCI8YVhocmC1ZRA1jBEvGKVdGQzzDMYM3Y3WgDTVeigEwwpncbBKTXsdOt2u91PQyZ466brnnU4hlXKZuQLUwIgBkxvdKyEYMoJZnxi4qVWDpMZBcx6K5wZsAHG1hUMCCLBEBGHMYpGwNxRZiuqzSQhAQY04GlrekNcAjlmkiyFGVtK0PDJ5hDsLKuhnGEiJQANtZl7LA6Ytsk2CxXW/pgfK2IsatK6qqNQT6keaJk+qeoF2zA5I6FCVZjigjvTPT2AuQxxVoCpHdozD+3JjO59d3D/fZWfJYPu5YhTpTEDwRgorQNKkc15xgwz0xnTIa4mCBHT1vGGKCDMhCVk5Gwuh3DK5nNIAAiiRNZ+oZYTBJnDIMyi8elQ2yRgsFJBk6v59e+gpnPvPeso9ymMILng1v1nbsq4/arEEnpKFFNMTlmtUgNgntl1WYkPvzls621eLAFuGsrMz2O7Qmb0BXzY2pP7R5PBmtVIzOZOmjBWIZ1cXY4rqLbIGReHMeQAA4HQjQvtVu7ObsBgyrQmNLAABs/5LIdhVhF1AOPZTMhm4cwAh2lqsvyGKzMO29raGsE8fPz9C2BN2D/sn9r84J+nzzijEi0rl6LwKJRkBX6HGhBou8au9t2+487yrQSDLe1gwZW00RsAc8NTlZ3BRsC0dHUGjKJKEJMTtz93vFSQNEq0rgV0mbgvRU/8nKMFPb7CRaSOKjGBMBOW4AeDKdwcOasxSTSnLoFhzGg8AeBSDgWoBau16bKyY4BZ+27y5uNX3aOGYYsDjbU3/zy9/8yJnr6DXwUCIdRxfAtB1WR9w/OwZuyyq/GLA+BM396hm5Caachk5vH5L7Dm2YWpyEbA3/RINELFFB6J5G1bv/xi0kzNgKkwNwQhiP2N6WFF8RswEJ4IpnpSoJlyKZHLLwmQVSCJSAAiT2U4VvA1grkjOh0yYDwVU/Etm49RlK01n3fNtfXY8WXzuz1zU5ec/vP0dZnkfdt/+PXTZdpXgM0FeG4AzGEkZ8BgovH14aNidlbyMTPbTxs2b7317SNHjgTqznv//eNXXX+0akTCTxBWO6onu1DiLcGa2JbGueCGGk5L1FZn+vCIzgDj2FZd7B834W5NyGBZyuWZlQQeA4aJsmLVMeWlrKJIFUBFNBTaRlc3LI0x/0bpZtgCmEsff3rs+fa26Y0Kj+fUVOiZt244A1u5t8/+/OsK1oNYFZJoq+5HGDRXIwF0l/u8e4fQA1hZWYE1e/Ztol+99iaHufx90nlX3auKphge3Ckr5QP+BQbpNWWeWFhtOfkZctBuv9mwrG6iSRMw2bxkvgYSafJmWgplDGOaYCUYZ0Un1sOEb6mxqxuh0C3HX3/92NobtUMTvm13zQzE3BXfn1zYx3f7Yl/WVz/3wxd6J9P1vb31DRg0lAGuwK+pu23c2z009Njso61UaWbwils3LUYJxnnViedOnMDnCtwyImLS9YdRReieNCMcMGAln0Y8Q/4WR9ldW0KhcNqQ/tqL6WNXXXPeOEJa3b1bjfxUgXJLkTVSPqcojKE2y+ZFURExz2A904hrfpsBU21pjHg6p6cvwluTj669ONQ9Fl1OHvSqFZ+9O5+5jvbgAea633765tONPmx/wU758+vrn+cZoMQHmK3j3lo4s/2G7eds3/co7UY9c7/v888B4zpx4rlX8WEDN5/d9ce3oq1zerijqiFeJUJYtq5kMpnKTKb/zqcXF7+B5ucHPU67QfPGa+mzniNdfuL4j1++884r5Rdd8LyjzumSqd5kzKjNMPolCrOsnHJ0btvsoKsCf7V1LlBNlmEcX7BCu53SSsC08CzENLycNE3HDNgYpiRCGEecEuiGrKMYGBvg5khEOYSXSEhIUiEOrEAu8yQXuQtT7oLIpAYqIILSATmBR+3/fN8m0em/feN2RH573vf93vd5n+d5OeeWL9+mfwlJ1osu/+x56vjOc/vz286E3r//8F7oVYprpMPfenuMRhlK8W6a/9178xiYGILBAADL0AFgdqZAPaFcrukLIZgo98pKWAZ/0fMH/77tuSZGnLTRMyHB6TOsjKDwazc+wabnKQq3q4HbLjGx5qNE3LLJPPZvEkwa4VR2AaYoJ8dr5srfRaIMRFfU7Pf8xSmJwiC+PBi2KxKT+a8v2h8Sf7PECd/ilHz+0f7XKiqKUsmtduJYRsYH1/MfXLv/x9dvlWhRoRMRhnbCRqM2lwIBuer5G95Dn0lgYZbOJxjXTQ0EQyzypSmdf/3lcOPG9e15GyuvsFp08OL5NdOn652cHACD9TeaV2T4mTPXuz88xhQxdbx6HR7Omuuh5IyMBMz7kYApiieBBTDQx78vF2ntIGE83qNK0y+Px7sF7jxGqdkc/okQ/ZXK0tL6pgP88JJj2IPKv/B4ePDa12FUrxLJC4hCaTTIeiztMCGQNyyb5SY2w6DLOCP6SKM5LQAJEiKW+Rfd/ev27R9uvNmqKytE7o0CgcHb37/46zS9Xu/ku1qv9wUL9ZTAM3h5X+7I5EjIP8X0o6YN7i5yrEKR0+InZWNDMJthGT5yGQVcP/+8FfPrFlNp9tTUpqbUpjoK0iWlcrT84/p4m9LKzAIrDu/SoQzAFE8MN3Z/ncuemOSokgpvGQT9YEKLUx2fQ4YJMVmGgUEGnVqlRUTRybT4AYL5ey1uUZkZFBKFQUGr3bLmeX2Mfr1DwmszfIkFDmsG5idHFsYxDjAl+Z98HpUY5gK7/QvmCsEsBIz9chcRX6pWyyx9vkNVaX9/1hqEU1eXWZZZVlbW2sARaMM3Ib+ntC5Wc9Oi5tKxjG2XtI/vjba1WbJx8xa8lgs9BksjWAAjDV7nBicXC7NivrPz0aOuwbVKrVTAl1qsKOoauEs0tx897bzQkt/Gqvn7aa/rZ4Q4rH517gyni5FBtOkS+FuYS9hWNUionfGOBQVdgmN1K2AiCeYFU5/JYV6ghfYid3e+VKqWWtpNYQFNHWhYHI7CShV34u30hYtTFju7Hgu4dJjvUzzSONpdwuSdWTo6cqXcYiPHIGBKCEnjFtA90wSD8Mrdx5XBC1x3w9OtCk4rKuoywfhnNee3tCG8AFfz18+/8tp7evG0F3PmJl0MC4qGByswPygxMayPOXEJ1pHz9wPmo8+DEgGzPNIMw/LkkAjGJwKys9hkZkllWBgY4ICGMzPOSpWRFANHfMa5aOZtSWwf7xnu9jGFEgNGIDRwhNTHBVJphM9sTDRDJAzMOusjIeI9CUy4lZvfuvKiioG7XQSj675xIb85Hyzgyb/46sszZiIZf2GOfiMamT18i4Etidjg2A4US9xnIZ5clbyP2lkkhAHA5ltJTrzNqygKk0N9ZoY9XO4qghFY6IiF+ns2oRALoTCW4TG1kaXKZL8zLVuZDhjZcv1gUJvUFF8f0aeJwDkcWjvcD6X80wZDwln9K0iUW7TaE8shzKIciCVB/Mbu+VUVFXfvVhBMZtuNmubm/LZ8NLXufR+/ONdrg9dMfczZoEgyzAdLVgW20HAcZGU+qZWZOci3RQWFMe/n8zY2GRP98YvCPFcj93zadN8PorB74CNHSo/cwspkluyM1KXZ2QRDNNTQKCDeVKrWSvAW+Ta+T7yQv2ttSzh+AkU8ejr0SCnlGoy3chGLCH/9ak9PJybSEyAQYMhX7Obmujiv8+ZfsAwU3HajpLm5pSUf4RpnSo4+71VePkMsDkkIohv+yiVuYn6zC2g+VKl5IGFrLgNM9WE02hn1mRzbnRPtOdPCHH5dDZzZ0dgJycqS2fEz9n0jtSgAClT25MnuJybTmJqZjyObtUS1XM9EXsQtDW6li2H5LZaUUKN+BPU9ytXGHcuQvEDBrxDWtJRGxqA4Jb3hNpuBORDbcbPzbtddsAwc6L4ulBYXn3z6tMvz3OETnjnl384T62PEa4PCvFfG6NfMDtwXFhS69bo8gigcAWPJGOnwJ4gegG22bNP19l7d4xC23uHXNYCJCgVLVvFPmFNvOSat9bcN8HqiA0uTThXb8a8+4zA7QYKQkVPJCGU/EzQ6OvjhR235LYiTjaCWHKHTBDsvnUnpfnqE4iLelmCSkjzdAAMUPN8AC9GkIGWQgbl7u+LEN6tjncsKHj16enMoWZX8hle516ztM2fMm7Zq/4HFErzdcYEYt/vj5GwjAwpahiNuoZJf13hu3HEw8re4wcH+RGbrMOrDtpZrN250d99AoLqWIhCFcZe8JWVPpE9iG7KfVDWRWXARDNYp5mQ1txBJwG5XykpAHjAqgh5YOmuexKx58/T66bNpTbMeCKuD3RwAQloNGJLzEIrXDdDYPFCUfie96+nTp48eVVQtW9xgPTvNw9Y2xdnfo+G0j4/TCzNemBlwCpONmgg5HfWJB/ojmHhyR10lZrylv0a+pRse7E10AZYo6ieEibZj5/C6FhkG2hK4tIXSt47zVT7SJ3FHnzQRSiYrwJCY9GGxXnJk5jwUlPCytdrtxRyTOYNCptlyIRLQUCKyp5MTzKE57mByPhLeKkzYUoZahzqL7t69OwComzfRPIe6miLq05z5xxLmzVrcENygU9KgFTzrhXc81sx2OCdWy3lkF5iEDAThte5uXsUVr0/Fhb0TPVujn56PTPzk0M6TWMQmV+fLACOt5gv4Ah5yBYTCuEBZ4JPY7H+NZohQj9Fjqwd7PNMx6UB6/4ycmR6a3ciKpTOlINQEoqInNGOePhtR7ojDBYxyJ0jg33WgxD8kyyUUDhUMVVTgRpPZhzTV1r7OOxjWnJ3981RHp4dYNzTUolNGKHmcQIfpc77LTrO5PE2thlUc0cZ4KSkNrBY4d3UODFS+9nbpr+HFbUeennEfNsi0xQaDQWvsLQaNJVqaSnvSLtDKjicN54fHPYmtaiIUujjLVthK9LOxU4P7+gwJW37qW//g8rlUp9Bc/edVCGD6V6cj8R08nqsVKl0AYOB6x4SAEWAyiwBzs7avr6DsZumdO+l/VVZWlBbpDr/83orTPEp+iTjtp9TY4Ed34HRYHMFztEAbwwygfoDUVWRSPCm3+MzCRxd6xk6sW7p46c6a4fYeI1cg4KLEBOJ8BSr8q9yakuaMMjQ0AiFxKKIeffjleRKqc1ROSiv335RDRZogFJuiklMgYk9jM9deenXGBus1lIGNqbB+uh5qzSwoq+js7GrNzJu3sPPKwEBpfJkCOU87l6z/wl+DXqEWWNbGxlpx0yovIzH9nZlgseIyvcbKtjy1o6Oj3qS8jrymwpDcwEN5j4oNcXxsLuRWJ63tMWiRWIRcPmSW+8hU6GhS/kk+WYVYWJjVs1/QvzYDBqFjMz3oAZzsYK+cSdnYEM/ct6kSC1vP01RdFalY5z3Pe5JrZ/YLqUN9rRU0MuNJunObXnHjKbqSVq/gwgqWljzN6dMped+hIM35y3N5PJrIOGIYk1vnEMlNE0wVrga3jIzNi24aiuN0EWpl9dmVzWPtJZSJwxUACc6ggG174+TKmOzMoaEhxjRo2py5BILyRhs8PHAkGFXVRsnz1MLycq+ctJw0CB9IAJprLmpO9kExeSoZ7fkL9PPPyO6Dfv7ll8uX0zEclWImMIS3q66zoqKytKK+rpZNJLCwkKt12dkNZbHx6bZgYc735lqpZ5XXV01RfYrDz5d3/HjnSE3JicOH/Q64uhZf2KYV0ppKQDTVuSeRHBGwV5LZSiprLSzsq9VxNswqB4k1QGxZ+XsARuGBxoYnXkgoS8ea6CW0PFRkRCdCbSyiOU80Tr8QDBL+duxgA1uQs/jjz+envaCfdXR3dlVTU1VFYS12ormOBXV1rUUdrU2lt2/bynlU2dPREjASj6qKIlIFhJeq+liHH38+iF8C4ZcnObmdOxfIFQDGyg6dhq8KDz8sORSjP4Jjp6EGjYUF3htOuQcJlc5tsxmBxh9N1gMCBS72M5hoSqOD0+Bd2IYaGtkmiWzzw4+TNLsmdfEgfvBLOhaHnZ2daEMDb9/++++/b99WUHdR62Ae3YwOskwFLrzi0dlR8B4yUlHS3+a1dxf96rB+re8H3vln8i8gza46ma/lnxKGq44fCqFRsqAAmZQNCo0iOJgzB/KHQbKfKS87GzAwkAmELhJozEIZN5jn+UWLAAOnDomFIZodkzhTdfCX1LqhjorOCqxsr6TlpZYWNZUp1DxLy9ryyi4YBDBgupJeWVFlO2cnD0J3UdYqFMENezdHB9HUNJqim4IQdiQT+MgyYg59/dVe0LS2AgjlIzi2RLIse6qaTk/5MhXT7RXZJLiXl9Ip4jk5sA1Mkw4aDAFm2wAGYmGAMxUIES9JTUOZrbhhD6FD1XXUadTsCRk6hUYdLAnRJ4ScFa/G6Lhn+py9fjySHBcNFMG+P7XkI0gV+bO/rdyGPJJwuVTrE/Jy5AeuBawAVMDJts0GSqrWMGbMze3pKemt6e2tqUE8UHt7eyN0r3l4+F5j4+P2icaJnh5jv+GqocQg9PLKmfsiAwMatqURDNEQjBlnKgpanM3NTla1NCAw6yUUUCmcm9qUWo8WMDkMxB4hCnOypOMm3/2IKTsLt/D2IyEflJSc2yfgancfOjvP25UlAQrBsDKMPM7tH426f+bG1qxPQrO2ZkEP70MPQ7MePrw/Oho2/tFgW3fzJ/fOGC6MydK8Fs4l06STaTA4E4yZhpraFOOwF+FcfB4TnU4C0nCZcgKOtTdruRaFr3bUT3KUo8VV1UlMhmFx/JwoeSfkLNVOeL+a50jHNZ/47eIqcdAC1MUwi5PKynBr7PHI+OjD61k3oKwoFxHitkTLod9xTUrkstXYMybF6IaBmkzD4lyetA1kts2kduFJer4DNASjQAl5FF7nyVs1covYcur8GNBM6iqqqotRmdsZyW/Jx0HkvIXXNilo5baMY1KVTFptP88tyK+wT1FYwAAVPoPpmSCY+w+zQhMT3bNG77snuruD5ndWYDJ/5j62z6D9FjQ2L2EIAEs6axuwmGmoqU1GHxIKiWDe7egACsFYWRKMHAsNuUWARwVZgx2XWZ7CYP0puamZkWUQ6HNWDE/fl5HkitoVtD86LOxigh4wij4IOLjTmGCcDfcnmsevkTFIoqyH7iJ3LLvdYSB6Lg9zcXFPFNE3RGP7Ddpy0CzEnsG0dOj8FNOYaabgkOCB3PUOYEBjsowlV62zsrJtCF5c8S8BZ6CrTOEcHyFnRDAp/EIqARULd9l2SUiIeMlK76Aw+z0JbxAMMHBBnCZSrGJsYmJk5Jp7YpQ7mSQx1J35GIrL3cUFVyKQQEZfjY0ZVJggpOXYvA3TmGGYbgOZYQhnqojn7Q7QEI6CPZ1crbbkLo6vje0yC94qKL206fR2vZolYWDUsaQUVpQ1jPzas5ixBy3oA00BjEMwdQUaNYo0AKbx8Q360yGReyJeqZkxInOJRMvJaPTVWNuYT3n5t145MA1goCkwoIH+C7OFxXnpGQx7cJQ6gmulOa1segYzgDLeA+nn01MvRfDMzQxTuFhdah75lWJxbvEy6xU4z3STukFMMN+xnrMyZgCAXxWy7Hkw4TAS5Z4IAHTyh6H0d7vQk6I3WRELvps4MTjhg9so9Zp3ppeaRwCGBiysacw3HOa5Y8ezWGSbDqKBNFZWQhlXcEqNnQNOnC2zAuiqGhiAo3iAfmf9STVxsCMzYNSp8Mhkp85HQ5u/br4ttGK+r6/YLdq2DpD4IQ44yeMIUDNHGljc8/hx44h7WKg7IYjgP4iKChUB4F/dH9//nWAeTwDGA6YBDP7vSdNMtQxEIBDFHptwFppgOjV2XKlRKFNGYDOLE+dcRCqTl9NQ1nUF6jjN503SMJbJBkwqnKixi1dkE80yWhhG+xU0xDZBZDdOsTG8v7c38PHIyOCIaAvSkZhe7x7q7hKaNak/SaGhf+DKGvnkMd/Dgw7esXkbMP/XaQAzKaD8YE4SOYpYwTqi0eBEPKMM5aHUOrnF0hwaw5p4Crb/k1JnneT9605DMCQg0JPRHCeH2W7RfDmEMl/w0hdyeqH+/uLBwfFr4yL0f2pbLlup10RRpzG3NZiH6TtgHL8/rqT5Go0A8D+QwDIJAz2zDPQDSDaak0R2o4JDKxmnFruLRqFQxVcPYZmd11nVoeAp6s2qqm+1DVRBUhrR0HecWRjmSEazNjjBMt4n8RshOYnTT+oFzOjgfRG6DKvlNJS5g0JkammTt8/QB+OPlfUEk2ZDm7BTLTO1oZFZiAUwrGI0B9Rz343vgGVkxn6tzCA0yCuqCjLLNDyepg6efLr3kdSnqDhDdcnVC5dqqnnS+QRjm51HB5mZcSROvm6rvJOR+67SqXRqCJZphIyDbZi5uLMjMTHgY6iZAhiTl+iTsbFbtfUeNI2Ov0Iw0OQIAJYpMDAMs60PeQPmSFJA8pe7NnZ0aIQ+txqNMuSA20Wo5UIhFjfsCW108gK24GRaFR8sNVcRLX5VVhygAwa6CgyDxTCm+Tj0N4Zgok/EUS6vUqnTKXUcmk329t8adHj4MEuUSL0fNOjmouWhLAB4pujG2Mh4rT9MkwYYU5+58y+WKZb5AVYh/1rSZqLxXntknS7gy4OzAYP86fZcKdb0PGyI4ihDRw5EO6h08JcMqeH8uFzYpbm5miOQChYoba03fOdvTdV+8WINd4UkxsnBzTf6GJubGBeI2nhoZo3guTU4ysCEwiJ4kn4HzDOJzK8i0bWJkXFNPUwzCTN1aCYYEguTRP4oCr+kCKyNp9Q6t12R0zo7FPxibbhUaGElsDSdvMaWM2GOBDwJlsBwsOxrbrmK478uaBmYOdYb1sEdiQJXcLCi6pDTG6tWRmdQ8t4pAlIoOO2MAINmJsK0DM9QF6apmWEwQps/w8Ole2JiYirMHbBApQQzZRKAHpO0nhyM8EaJV4Fnc0Cl4mDY2l2/diiwpcYUXAUKl1zNYMAsGt8Qoolpi+Gn72+80DyY3yx9zrJZ5qe03bABK/t1ywIC9h7ChAbF1GAZ34+j92VkXLq08xhCq04Fc66208rF2D0+Mo7JZSgE85B10MwgDGJTbCNy78aMlIEpj2dgCOd5CiJY/IxlEuZHNAW4/2PwfoYkIJMqJPhw1Fb7sI1DpwOLi6X407nsScVUsdSCI8e+KLn7jNhM7+ltvzc8ODjYyHlOeIHjR5bBoIzQo3V7rVmYEMDYR2/bvw88TEy/K2CgxrFG3Gjuk1nYyRgzAKD/mFFAQU/qUYNjY40KxjIsTCWm31iAL5jjusbc/wFDyxq8Jr0hRmwKkkcgrEbEp9SBb70fFOYBGL5WYCEABWsYGMTOQoURQFZsBElPf+O94fFB6BaOaQ9/joWBUEZEMjMmRE+W2ezr5B1NSRKUV04pH2wz6x0bGYFlXMIYGBgmi9obDQTYF50UtTOXZsPYiCJvEibdn5ZHmIOjrKZfwBE3VJXCA5YBzI+eCfqYvQEISgEP4ST7qKo/CgvJHNKoAmUcHI4JFMBYgAU0gOHYGbS3evp7TSzjw0YLbLty/ZTLrOFBQq+ZNWPezBjQEIwTYBDhT2nqQAo5xGEWx/3GByMjD+6LwhiWREwAgAILEQ4uPCCiwZcthokRRXZefZUZJj2voBUL1wbSARQsC3YN2LPjB3KjYRxz2LMXQcN4UF0JScxJH600au2iNeUaH6EF6imihVH3pxKyQvgoqOKdEa0MTYyFGR7WPkdK4S9jjvmH/x7efApIAgyKjFA62lY2/PrNJe9zCKX/VuPgg9HRa6H2tMrMyqIbJgQAFoPlYRudaHRifESBqV09axoaAPJayyDGgZVyIMUvBU677RJfpx9+ANLaHWvXv3EWMbYUNHQkWSsUbrM5eDCtIAKTQpoV0zaAhR3BCOVLqViXsaenp5pghofHh4fbYRkIlrG1tsagjL0VCVj0cG7HYHvIOwiKjqYr2t6eAxKjQdA+Cpgs5LwyQv+PCgXRf4UuJRJ1j442KjJRnhVDgAnmchr8ozBNCp5YbOBB5XIOy1Unj+09u55ZYkYelIBmbzLK957eGOY/pEFVRQEFGmB+z+U4chmY3YCxkKHH4EYOHEa9Y0KBwUDNDJaZRYaZmUCBYm5nAbPZm6FhiCAOtgtwSO29+/BZUI9H46KGhotg8IlZhOhyg3wbDx40aphis/4eaTaAuVN5+Qq8lOg4mwgGJAzL7u18cr0q3RiWyIN7XGGaOMCEB22cmxYsRfCXBZc9T4qj1pF/P2KpEJbhMl2mHSKe9t6eW8Yxg6ty2RzIOuCQZA9uNGQacchGp81B24LCTDQwDzb4yY753Whek6LMTcJ7MKlxXGY91hQ00ArWvzy+srQyr6k++HYpJkdKhJcEb8JBZQgMoNCADCmXd3QH62aK9j7rujtgeyBgpL/8uAtT0CTxkZ2H+SpHxAJwHINldnAIzJEJYBoDaBpZ0UALmjHDcYLBdHkdup5kD/V/ppk57f84GhxmIFNVue6HWf+vh9B9cEEAGh8fId3SFW5qiHWen51WWXrnSnxrQcVfRUqmPLdM7RN4koBcXVFUZu/OQ18yLF/a20eHLAAMX4lqYV9sDNvyJU3aPGnnSnyCy+FJdTpFbe1Shd8mjSIuOTw3Aw2NTAM1Uj84rgLMsnXrUJ9q6V7yAhDM5vXnzq30BorZNEQS0adrHoavr/le+3DL8L2rje39EPph7i2SESoeM4wZDD25uUa6Bwh4Ok0h3Avlpdg1un0lta+sqL6QCp6p6NB7mRC1bOKSEe4VcG4H4y/bYk8wB1yPbg/0QedYErYFfg4M3Rud1ietF19CYAscsbURco2PTqVSq1Hwpv9wL9RobmdaP58VAQjXXYcj8+fMsjbDOOjdlnyM7s8KfUaHvQBLTq6h2FhcYtRW9+b2h4f3aFVsXTk+iUoAUXpsMukwowgqGawoSyu9fBtqUrdmooUpavnmKm5AQrVutTL56NG1u3Z5IwzbO/osYPYqD2Mv8hxYKP0V1klaJd4upTqePC7W/BohyinzLIvxfvkABdaBwFJs8FMBBiSYLWNAi2FhnNwk+oRVK+0pj5sRR01zInVtrZJkLvfFVxIDP1AVCPGfSekDBdY2pHR2lPXV9pX1FZb5o75uYcRQpqa2VlEYHIwM9DiUpuMrTVQntuw6SDU88B/GBB87mnF4m1Yg3Cn23cjSbP7mp5OOXFgGW7VYUiplMgu40qTG3B5hePXw8IV76DIEI9ukng8UmGXOBokZJmQ9PtOLl3zA5JN9ikwpDjM30mnwx+ggLNvUGh3PKoLHuE9pyVo40NnkaDrziTnKv/CuSUVNrWjohQXq2qEygqE6yFTq8AACHZNpXn563+cf+67y3cwExx86fWxnxrmT1QJZhv2b3lS3ccsS8Z7jKPIPGLRNHk+qVcnI9e+jVuVyVMepFhfqx9RUhwNGrliwdN1Sa2x9H8HkP4a1zF58Jn6LKSDAVLn6B3YK5DYAPZjNAAAAAElFTkSuQmCC\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Temperature\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"°C\",\"decimals\":0,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "temperature", + "environment", + "indoor" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/knob_control.json b/application/src/main/data/json/system/widget_types/knob_control.json new file mode 100644 index 0000000000..22554dd13b --- /dev/null +++ b/application/src/main/data/json/system/widget_types/knob_control.json @@ -0,0 +1,26 @@ +{ + "fqn": "control_widgets.knob_control", + "name": "Knob Control", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Sends the command to the device each time the user changes the value. Uses 'setValue' and 'getValue' RPC calls by default. The name of the RPC calls is configurable in advanced settings.", + "descriptor": { + "type": "rpc", + "sizeX": 5, + "sizeY": 4.5, + "resources": [], + "templateHtml": "", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-knob-control-widget-settings", + "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"maxValue\":100,\"initialValue\":50,\"minValue\":0,\"title\":\"Knob control\",\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\"},\"title\":\"Knob Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}" + }, + "externalId": null, + "tags": [ + "dial", + "regulator", + "handle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/label_widget.json b/application/src/main/data/json/system/widget_types/label_widget.json new file mode 100644 index 0000000000..1c7c84109a --- /dev/null +++ b/application/src/main/data/json/system/widget_types/label_widget.json @@ -0,0 +1,27 @@ +{ + "fqn": "cards.label_widget", + "name": "Label widget", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays attributes or the latest telemetry values as an overlay on the configured image. The position of the values is configurable using appearance settings.", + "descriptor": { + "type": "latest", + "sizeX": 4.5, + "sizeY": 5, + "resources": [], + "templateHtml": "", + "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", + "controllerScript": "self.onInit = function() {\n self.ctx.varsRegex = /\\$\\{([^\\}]*)\\}/g;\n \n var imageUrl = self.ctx.settings.backgroundImageUrl ? self.ctx.settings.backgroundImageUrl :\n 'data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg==';\n\n function processLabelPattern(pattern, data) {\n var match = self.ctx.varsRegex.exec(pattern);\n var replaceInfo = {};\n replaceInfo.variables = [];\n while (match !== null) {\n var variableInfo = {};\n variableInfo.dataKeyIndex = -1;\n var variable = match[0];\n var label = match[1];\n var valDec = 2;\n var splitVals = label.split(':');\n if (splitVals.length > 1) {\n label = splitVals[0];\n valDec = parseFloat(splitVals[1]);\n }\n variableInfo.variable = variable;\n variableInfo.valDec = valDec;\n \n if (label.startsWith('#')) {\n var keyIndexStr = label.substring(1);\n var n = Math.floor(Number(keyIndexStr));\n if (String(n) === keyIndexStr && n >= 0) {\n variableInfo.dataKeyIndex = n;\n }\n }\n if (variableInfo.dataKeyIndex === -1) {\n for (var i = 0; i < data.length; i++) {\n var datasourceData = data[i];\n var dataKey = datasourceData.dataKey;\n if (dataKey.label === label) {\n variableInfo.dataKeyIndex = i;\n break;\n }\n }\n }\n replaceInfo.variables.push(variableInfo);\n match = self.ctx.varsRegex.exec(pattern);\n }\n return replaceInfo;\n }\n\n var configuredLabels = self.ctx.settings.labels;\n if (!configuredLabels) {\n configuredLabels = [];\n }\n \n self.ctx.labels = [];\n\n for (var l = 0; l < configuredLabels.length; l++) {\n var labelConfig = configuredLabels[l];\n var localConfig = {};\n localConfig.font = {};\n \n localConfig.pattern = labelConfig.pattern ? labelConfig.pattern : '${#0}';\n localConfig.x = labelConfig.x ? labelConfig.x : 0;\n localConfig.y = labelConfig.y ? labelConfig.y : 0;\n localConfig.backgroundColor = labelConfig.backgroundColor ? labelConfig.backgroundColor : 'rgba(0,0,0,0)';\n \n var settingsFont = labelConfig.font;\n if (!settingsFont) {\n settingsFont = {};\n }\n \n localConfig.font.family = settingsFont.family || 'Roboto';\n localConfig.font.size = settingsFont.size ? settingsFont.size : 6;\n localConfig.font.style = settingsFont.style ? settingsFont.style : 'normal';\n localConfig.font.weight = settingsFont.weight ? settingsFont.weight : '500';\n localConfig.font.color = settingsFont.color ? settingsFont.color : '#fff';\n \n localConfig.replaceInfo = processLabelPattern(localConfig.pattern, self.ctx.data);\n \n var label = {};\n var labelElement = $('
');\n labelElement.css('position', 'absolute');\n labelElement.css('display', 'none');\n labelElement.css('top', '0');\n labelElement.css('left', '0');\n labelElement.css('backgroundColor', localConfig.backgroundColor);\n labelElement.css('color', localConfig.font.color);\n labelElement.css('fontFamily', localConfig.font.family);\n labelElement.css('fontStyle', localConfig.font.style);\n labelElement.css('fontWeight', localConfig.font.weight);\n \n labelElement.html(localConfig.pattern);\n self.ctx.$container.append(labelElement);\n label.element = labelElement;\n label.config = localConfig;\n label.htmlSet = false;\n label.visible = false;\n self.ctx.labels.push(label);\n }\n \n self.ctx.imagePipe.transform(imageUrl, {asString: true, ignoreLoadingImage: true}).subscribe(\n function (transformedUrl) {\n self.ctx.$container.css('background', 'url(\"'+transformedUrl+'\") no-repeat');\n self.ctx.$container.css('backgroundSize', 'contain');\n self.ctx.$container.css('backgroundPosition', '50% 50%');\n var bgImg = $('');\n bgImg.hide();\n bgImg.bind('load', function()\n {\n self.ctx.bImageHeight = $(this).height();\n self.ctx.bImageWidth = $(this).width();\n self.onResize();\n });\n self.ctx.$container.append(bgImg);\n bgImg.attr('src', transformedUrl);\n }\n );\n\n self.onDataUpdated();\n}\n\nself.onDataUpdated = function() {\n updateLabels();\n}\n\nself.onResize = function() {\n if (self.ctx.bImageHeight && self.ctx.bImageWidth) {\n var backgroundRect = {};\n var imageRatio = self.ctx.bImageWidth / self.ctx.bImageHeight;\n var componentRatio = self.ctx.width / self.ctx.height;\n if (componentRatio >= imageRatio) {\n backgroundRect.top = 0;\n backgroundRect.bottom = 1.0;\n backgroundRect.xRatio = imageRatio / componentRatio;\n backgroundRect.yRatio = 1;\n var offset = (1 - backgroundRect.xRatio) / 2;\n backgroundRect.left = offset;\n backgroundRect.right = 1 - offset;\n } else {\n backgroundRect.left = 0;\n backgroundRect.right = 1.0;\n backgroundRect.xRatio = 1;\n backgroundRect.yRatio = componentRatio / imageRatio;\n var offset = (1 - backgroundRect.yRatio) / 2;\n backgroundRect.top = offset;\n backgroundRect.bottom = 1 - offset;\n }\n for (var l = 0; l < self.ctx.labels.length; l++) {\n var label = self.ctx.labels[l];\n var labelLeft = backgroundRect.left*100 + (label.config.x*backgroundRect.xRatio);\n var labelTop = backgroundRect.top*100 + (label.config.y*backgroundRect.yRatio);\n var fontSize = self.ctx.height * backgroundRect.yRatio * label.config.font.size / 100;\n label.element.css('top', labelTop + '%');\n label.element.css('left', labelLeft + '%');\n label.element.css('fontSize', fontSize + 'px');\n if (!label.visible) {\n label.element.css('display', 'block');\n label.visible = true;\n }\n }\n } \n}\n\n\nfunction isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n}\n\nfunction padValue(val, dec, int) {\n var i = 0;\n var s, strVal, n;\n\n val = parseFloat(val);\n n = (val < 0);\n val = Math.abs(val);\n\n if (dec > 0) {\n strVal = val.toFixed(dec).toString().split('.');\n s = int - strVal[0].length;\n\n for (; i < s; ++i) {\n strVal[0] = '0' + strVal[0];\n }\n\n strVal = (n ? '-' : '') + strVal[0] + '.' + strVal[1];\n }\n\n else {\n strVal = Math.round(val).toString();\n s = int - strVal.length;\n\n for (; i < s; ++i) {\n strVal = '0' + strVal;\n }\n\n strVal = (n ? '-' : '') + strVal;\n }\n\n return strVal;\n}\n\nfunction updateLabels() {\n for (var l = 0; l < self.ctx.labels.length; l++) {\n var label = self.ctx.labels[l];\n var text = label.config.pattern;\n var replaceInfo = label.config.replaceInfo;\n var updated = false;\n for (var v = 0; v < replaceInfo.variables.length; v++) {\n var variableInfo = replaceInfo.variables[v];\n var txtVal = '';\n if (variableInfo.dataKeyIndex > -1) {\n var varData = self.ctx.data[variableInfo.dataKeyIndex].data;\n if (varData.length > 0) {\n var val = varData[varData.length-1][1];\n if (isNumber(val)) {\n txtVal = padValue(val, variableInfo.valDec, 0);\n updated = true;\n } else {\n txtVal = val;\n updated = true;\n }\n }\n }\n text = text.split(variableInfo.variable).join(txtVal);\n }\n if (updated || !label.htmlSet) {\n label.element.html(text);\n if (!label.htmlSet) {\n label.htmlSet = true;\n }\n }\n }\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n singleEntity: true\n };\n};\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-label-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"var\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"backgroundImageUrl\":\"data:image/svg+xml;base64,PHN2ZyBpZD0ic3ZnMiIgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTAwIiB3aWR0aD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDEwMCAxMDAiPgogPGcgaWQ9ImxheWVyMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtOTUyLjM2KSI+CiAgPHJlY3QgaWQ9InJlY3Q0Njg0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBoZWlnaHQ9Ijk5LjAxIiB3aWR0aD0iOTkuMDEiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiB5PSI5NTIuODYiIHg9Ii40OTUwNSIgc3Ryb2tlLXdpZHRoPSIuOTkwMTAiIGZpbGw9IiNlZWUiLz4KICA8dGV4dCBpZD0idGV4dDQ2ODYiIHN0eWxlPSJ3b3JkLXNwYWNpbmc6MHB4O2xldHRlci1zcGFjaW5nOjBweDt0ZXh0LWFuY2hvcjptaWRkbGU7dGV4dC1hbGlnbjpjZW50ZXIiIGZvbnQtd2VpZ2h0PSJib2xkIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmb250LXNpemU9IjEwcHgiIGxpbmUtaGVpZ2h0PSIxMjUlIiB5PSI5NzAuNzI4MDkiIHg9IjQ5LjM5NjQ3NyIgZm9udC1mYW1pbHk9IlJvYm90byIgZmlsbD0iIzY2NjY2NiI+PHRzcGFuIGlkPSJ0c3BhbjQ2OTAiIHg9IjUwLjY0NjQ3NyIgeT0iOTcwLjcyODA5Ij5JbWFnZSBiYWNrZ3JvdW5kIDwvdHNwYW4+PHRzcGFuIGlkPSJ0c3BhbjQ2OTIiIHg9IjQ5LjM5NjQ3NyIgeT0iOTgzLjIyODA5Ij5pcyBub3QgY29uZmlndXJlZDwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IGlkPSJyZWN0NDY5NCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgaGVpZ2h0PSIxOS4zNiIgd2lkdGg9IjY5LjM2IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgeT0iOTkyLjY4IiB4PSIxNS4zMiIgc3Ryb2tlLXdpZHRoPSIuNjM5ODYiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+Cg==\",\"labels\":[{\"pattern\":\"Value: ${#0:2} units.\",\"x\":20,\"y\":47,\"font\":{\"color\":\"#515151\",\"family\":\"Roboto\",\"size\":6,\"style\":\"normal\",\"weight\":\"500\"}}]},\"title\":\"Label widget\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" + }, + "externalId": null, + "tags": [ + "tag", + "sticker", + "marker", + "badge" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/lcd_bar_gauge.json b/application/src/main/data/json/system/widget_types/lcd_bar_gauge.json new file mode 100644 index 0000000000..b2c8caa4c3 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/lcd_bar_gauge.json @@ -0,0 +1,21 @@ +{ + "fqn": "digital_gauges.lcd_bar_gauge", + "name": "LCD bar gauge", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Preconfigured gauge to display any value reading as a bar. Allows to configure value range, gradient colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 2, + "sizeY": 3.5, + "resources": [], + "templateHtml": "", + "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-digital-gauge-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#babab2\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\"linear\",\"refreshAnimationTime\":700,\"startAnimationType\":\"linear\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"400\",\"size\":16},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":1.5,\"decimals\":0,\"showUnitTitle\":true,\"defaultColor\":\"#444444\",\"gaugeType\":\"verticalBar\",\"units\":\"%\"},\"title\":\"LCD bar gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/lcd_gauge.json b/application/src/main/data/json/system/widget_types/lcd_gauge.json new file mode 100644 index 0000000000..bfe7a64a5f --- /dev/null +++ b/application/src/main/data/json/system/widget_types/lcd_gauge.json @@ -0,0 +1,21 @@ +{ + "fqn": "digital_gauges.lcd_gauge", + "name": "LCD gauge", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Preconfigured gauge to display any value reading as an arc. Allows to configure value range, gradient colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 3, + "resources": [], + "templateHtml": "", + "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-digital-gauge-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 180) {\\n\\tvalue = 180;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#babab2\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":180,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\"linear\",\"refreshAnimationTime\":700,\"startAnimationType\":\"linear\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":1.5,\"decimals\":0,\"unitTitle\":\"MPH\",\"showUnitTitle\":true,\"defaultColor\":\"#444444\",\"gaugeType\":\"arc\"},\"title\":\"LCD gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/leaf_wetness_card.json b/application/src/main/data/json/system/widget_types/leaf_wetness_card.json new file mode 100644 index 0000000000..8d860b4f1b --- /dev/null +++ b/application/src/main/data/json/system/widget_types/leaf_wetness_card.json @@ -0,0 +1,32 @@ +{ + "fqn": "leaf_wetness_card", + "name": "Leaf wetness card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAn1BMVEXg4ODf39/g4OAAAADg4ODf39/////h4eFLcN2rq6vo7fthguEhISE9PT3z8/PCwsK7u7vS2/ft7e3n5+e7yfLx8fGenp709v2lt+7IyMjV1dX5+fnOzs62traCgoJWed+wsLAvLy9KSkp0dHRYWFjH0vWwwPCOpep4lOVti+OQkJDd5PlmZmaDnOfa2tqkpKSOpuqar+yaruxtiuPb29sx7JjMAAAABnRSTlPvIL8Ar7DvmsykAAAF9UlEQVR42uzPQQ0AMAgDwDII/Enwb3U2Cuk5OLg91HZhDkPnegNDTB7QD5Un4EqkFCGjCBtF2CjCRhE2irBRhI0in716W3EbBgIwfDcjOTNFbiOn0oWKDtsmigLd93+5SqZNnNbBWxYvXtgfhAXCwR+R0Nb6gGytV0D07JuqZHhJG4JEnvs9KnJWLREWWh1y6Pqn/X7/1PfnwyJEEsJcSDO+t4T0zztx6zQHyRwyAKb2MEypfbFKGtwAmGydckQbKEidZGAHoAsHCW0h2fY6R702pD+Ku3YzEOeVJAmBTSaDmcY9hGQ0MUiqM2dC0oocIgXlyEJhldu6s8WDJGO8WxdyuIi/+jYD8QUxFEC01XPdWjwo9hhTdSIOpLGu/R5IGZFdhaBGGEhqjatCup34pxkItRIMPqQJxJXo0hAGSNTCCcRQK4IKxArAMaV1ISfxIoh3UNOUAScQ6dnIQBaih9YE0h41bTQ60taC5bIm5KtYgnhZA+elKoP20bgJRBOPAwxFk0MFOPsHk1ipoAxljKSzN5bjipBOLEEct+oz+ISggs9hAMUIY8XV0T5Q1mUD1ZuRFYwjes56XFBtwa+6tX7eHfIrZLM9hExO+o+deL+Qw+3u6C+3OWy2R5D+epl3k1N/hM22BDkd9uLWBTbbI8hZjO26u2vxO2y2hTPSwbOYdIb/69NneNiXt4HAcfwH7q+TE0z7xY4ZrcYVAmH47l9mF0Y7LqZHz1CvWkqJQrvv/2xVs5scNrlZyAle+CHD4Kj4ISKogi3xgnesBh1xGwEr3dF/kUi7Gqe75+SMLeRa5Baji3CeWxZb4/jS20REEBdbu4VRCaY4IIrzaIi0wDG2uIsITn3j2xtywnuRlSxFSTaJ8Us/g9a8TQmHYskgWQou0YGpjnmdR5p8z2u1rVEyk1LYReRb3XgNb/w4fiAi7IoLSRyzx5sIUEQDFhMdr9S6Fss1Xuc5Qh8ehVdFEaScLaTEPURw/gV833j8wUcnktbicFG6FyGnuaaRFtNFTDJmvc3L+iLCtarwDM3N0vMeIp3No37vAcrMkQ5cXAiwK/sIIFiYKlK1eFlqKgWBup6C+SYihfsdcYSQkC6RskucCbuJ/L4dx98n3KNEdLgk1QOrKsMQAE6kC7yhcE0NmQRJIRpSQUMdsFIfHa/VRBlrre8ncjw//zudnn8+4SE8HiGLkBvzp1HxCKI2zC/TKTIqU2Q0pshoTJHRmCKjMUVGY4qMxhQZjSkyGlNkNKbIaEyR0ZgiozFFRmOKjMZnihz/s28+LQ7CQBS/lZA5BQLmllM0IgVtv/9329nJDtvYHde9vQXfoVWTQn/MnzdS22t4Wx+3bVz0DBdkcL2m3fLmRKtv57ggyzFI1eskoQIGCYcgka/UEiZ+S5qJoCDRsehbj/3vw88bi0nyTUSUMEESc2RzlRejxk1CMTI1Jsikzw9ZD62G1wNPTIYJUj+r4DTIykUEWiNcBe5x1JuL9rZFCopQiz1LQ7JE7CBfpeGYi08javt1rCj+PhitYORaZ9xJzgjVR8QP05Yda477tJEYNGUvheJRQXo/pGL9iyMvkmcJ1tmL61XeHbMS1TQ0C8EdUVKLxDTdSY6yOVKJheCCBAbI4007k5k8aiGwIKyFg6Ak9riiFuJDWEBBdjN7+GlFLSTIForgIFIwo7VE8tqUsEGK+R3FQsR0cmsLBRokGCBqIbXViZ+dq9AgVkTUQnh11H0DFkh0rNjXiGUh/VDvsUAkmR5d11oMC8EGGbLMgy+ZRQcWwssFFKRlE3mJDklmWRbCYuoEWiM6p8/rOjsjIMOkV6tETz4zw3UtLyRN1o1s9C8VdV8zv0Y4EHEF1XyY+fDOXu7ZsXKNv299OtazwM5aPgR/sqh4J/z0e06w9yN/0AVygfwXXSBoukDQdIF8tHcHVADCMBBDrxtQBfNvFRtZX76DKAiNITSG0BhCYwiNITSG0BhCYwiNITSG0BhCYwiNITSG0BhCYwiNITSdfCN01ohl83lTGVByUtn19O2yav/eUkL1IMIcZgAAAABJRU5ErkJggg==", + "description": "Displays the latest leaf wetness telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'leaf', label: 'Leaf wetness', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Leaf wetness\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:leaf\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#4B70DD\"},{\"from\":10,\"to\":50,\"color\":\"#FFA600\"},{\"from\":50,\"to\":90,\"color\":\"#F36900\"},{\"from\":90,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#4B70DD\"},{\"from\":10,\"to\":50,\"color\":\"#FFA600\"},{\"from\":50,\"to\":90,\"color\":\"#F36900\"},{\"from\":90,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Humidity card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "dew", + "leaf moisture", + "foliage dampness", + "leaf humidity", + "foliar moisture" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/leaf_wetness_card_with_background.json b/application/src/main/data/json/system/widget_types/leaf_wetness_card_with_background.json new file mode 100644 index 0000000000..a063829404 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/leaf_wetness_card_with_background.json @@ -0,0 +1,32 @@ +{ + "fqn": "leaf_wetness_card_with_background", + "name": "Leaf wetness card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest leaf wetness telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'leaf', label: 'Leaf wetness', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Leaf wetness\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:leaf\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#4369DD\"},{\"from\":10,\"to\":50,\"color\":\"#F89E0D\"},{\"from\":50,\"to\":90,\"color\":\"#F77410\"},{\"from\":90,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#4369DD\"},{\"from\":10,\"to\":50,\"color\":\"#F89E0D\"},{\"from\":50,\"to\":90,\"color\":\"#F77410\"},{\"from\":90,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Humidity card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "dew", + "leaf moisture", + "foliage dampness", + "leaf humidity", + "foliar moisture" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/leaf_wetness_chart_card.json b/application/src/main/data/json/system/widget_types/leaf_wetness_chart_card.json new file mode 100644 index 0000000000..c11a75bda7 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/leaf_wetness_chart_card.json @@ -0,0 +1,35 @@ +{ + "fqn": "leaf_wetness_chart_card", + "name": "Leaf wetness chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a leaf wetness data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'leaf', label: 'Leaf wetness', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: '%', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'leaf', '%', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Leaf wetness\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#4B70DD\"},{\"from\":10,\"to\":50,\"color\":\"#FFA600\"},{\"from\":50,\"to\":90,\"color\":\"#F36900\"},{\"from\":90,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Leaf wetness\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:leaf\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "dew", + "leaf moisture", + "foliage dampness", + "leaf humidity", + "foliar moisture" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/leaf_wetness_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/leaf_wetness_chart_card_with_background.json new file mode 100644 index 0000000000..99159a03cf --- /dev/null +++ b/application/src/main/data/json/system/widget_types/leaf_wetness_chart_card_with_background.json @@ -0,0 +1,36 @@ +{ + "fqn": "leaf_wetness_chart_card_with_background", + "name": "Leaf wetness chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a leaf wetness data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'leaf', label: 'Leaf wetness', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: '%', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'leaf', '%', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Leaf wetness\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#4369DD\"},{\"from\":10,\"to\":50,\"color\":\"#F89E0D\"},{\"from\":50,\"to\":90,\"color\":\"#F77410\"},{\"from\":90,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Leaf wetness\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:leaf\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "leaf", + "dew", + "leaf moisture", + "foliage dampness", + "leaf humidity", + "foliar moisture" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/leaf_wetness_progress_bar.json b/application/src/main/data/json/system/widget_types/leaf_wetness_progress_bar.json new file mode 100644 index 0000000000..2f8460c7cc --- /dev/null +++ b/application/src/main/data/json/system/widget_types/leaf_wetness_progress_bar.json @@ -0,0 +1,33 @@ +{ + "fqn": "leaf_wetness_progress_bar", + "name": "Leaf wetness progress bar", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAn1BMVEXg4ODf39/g4ODg4OAAAAD////19fXg4OAhISFLcN3j4+M9PT2srKxYWFh0dHTx8fGQkJDo7fsvLy/Hx8e6urqCgoKenp7v7++7yfKGhob09v2Opup4k+WXl5fS2/fV1dX39/dhguG5ubmpqanH0vVsiuPLy8tmZmZKSkre5PewwPClt+6aruzCwsJWed/c3Nx+fn6Dnejm5ubU1NSxsbH6cLmjAAAABXRSTlPvIL+vAC9A4IoAAAQySURBVHja7M/BEQBABAPAOIynp/47vTbCZDtYhD3Udm4Bw+R6DYN3HjAPlSfgSqQUIaMIG0XYKMJGETaKsFGEjSKf3TJYdRyGoehKcLFk2cixt4Fskv//wpHShkzn8TpvFQKvFxor1ln4oNb0bvmI3C2/XiQlulfeiCxSvtXIQKNb5Y0II33XUqzyZiSj0n9zD5GMmd6EQT/J9SKpquz10rsQlcrogyJjxOvsnerA3J1z2lAbSS1DpQS1aJ9ibV2HY1RGbFwvMhkY7C8bmJEpMcD5OZpCE/RRCOBkmaM9fIcNyDtkrkbeNy/dmrEx9HKRwi4x0I8TN38cjgMLKYwo/A6OGBRMpWQOihdl8yI+DStNwWTMV4vs5yfjx63bIX+JzOjEG1pDjfPt3CESjLr2FoUgZiQl7u0FOVFJ5WqRikeIhgF4ESHLDc36QDu4LyKGPZVmBrJ4qzulia4WGVglQoI1UX0VUVMm3VYLYQ1s+ioikXn/tTNGDFZW8OUTacj+LHGqxR+vIgJTJ2w9uX9FMtq+X1R8cWioQx3tOpFePYkyVKp1Gtik4lWkwO2KxebBRVHbKdLAQxQzsUnr0JhsmxjpOpE9jZICyOn53UbfRU6qPK+gkxNATxFaGLDh/Rx9x6sBJnSJyGtKKs/1p1ysZ1I6+8fGmc/f+I/IH/bnkApgGAigGBoYrIH5lzlUejjt+3EQWRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RTRFNEU0RzRhZ2+ObIuvdDphMke+WyM9+HaxaCEJRGB6tYA2diW7kCDpII+j9X+5ysrhwB96a7ciPHqAf3aWJrHbnoF4vpJAznqIXspB4jF7IxhXOPGBb/RciXIVktGhUJ/VCIg8FXz6L4pHphfiYg5sruToAQRA2GIFOLaTLtyUJHrPAJ+h0ISSRHoCLaVmsh1IXQkwLganFiNp/fCckeT8fJ5UFjbehKt1bnRBL1mNGLHYmm2izhUadELeSxblCRoNdNLYi6RyTS6ffhF2psMv30ah/sRKSlACcXNyiznnvhgAuhD/vrfXs9Y47+5OMEG1GiDYjRJsRos0I0WaEaPOCECNs8jwpcj8k8ySTIp/bIfw1aXI7RHjKkyL3V8RkNqJpRj4v/mo9zAjRZoT8tHfHqg0DMQCGz0l+Z2s9HBEaBAaDt8Mm9vs/W2uo14KznOLoh9v1cYNGeSsg3voHUjrLVK4vAyBmJvtAhyGigk1UrdVVgaEwGpP2MstxyPKAXP1LZIOo0M6UFYbxOGQc3EC+2F7OAfECUYGZ8iJE5pbHQs12iE0sxmSgz+MQsnZG1XbIU20TrF2XeQFC3+Ml+Rvoszf7exUQbwXEWwHxVkC8FRBvBcRbAfFWQLwVEG8FxFsngiRO0T1dTnB6Gr6vqUknOD4tv4xbc72/e+nS3H4AH4urRIckSugAAAAASUVORK5CYII=", + "description": "Displays leaf wetness reading as a horizontal progress bar. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'leaf', label: 'Leaf wetness', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#4B70DD\"},{\"from\":10,\"to\":50,\"color\":\"#FFA600\"},{\"from\":50,\"to\":90,\"color\":\"#F36900\"},{\"from\":90,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":100,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#4B70DD\"},{\"from\":10,\"to\":50,\"color\":\"#FFA600\"},{\"from\":50,\"to\":90,\"color\":\"#F36900\"},{\"from\":90,\"to\":null,\"color\":\"#D81838\"}]},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Leaf wetness\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:leaf\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "progress", + "weather", + "environment", + "dew", + "leaf moisture", + "foliage dampness", + "leaf humidity", + "foliar moisture" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/leaf_wetness_progress_bar_with_background.json b/application/src/main/data/json/system/widget_types/leaf_wetness_progress_bar_with_background.json new file mode 100644 index 0000000000..b341d8163e --- /dev/null +++ b/application/src/main/data/json/system/widget_types/leaf_wetness_progress_bar_with_background.json @@ -0,0 +1,33 @@ +{ + "fqn": "leaf_wetness_progress_bar_with_background", + "name": "Leaf wetness progress bar with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays leaf wetness reading as a horizontal progress bar with background. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'leaf', label: 'Leaf wetness', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#4369DD\"},{\"from\":10,\"to\":50,\"color\":\"#F89E0D\"},{\"from\":50,\"to\":90,\"color\":\"#F77410\"},{\"from\":90,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":100,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#4369DD\"},{\"from\":10,\"to\":50,\"color\":\"#F89E0D\"},{\"from\":50,\"to\":90,\"color\":\"#F77410\"},{\"from\":90,\"to\":null,\"color\":\"#DE2343\"}]},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Leaf wetness\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:leaf\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "progress", + "weather", + "environment", + "dew", + "leaf moisture", + "foliage dampness", + "leaf humidity", + "foliar moisture" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/led_indicator.json b/application/src/main/data/json/system/widget_types/led_indicator.json new file mode 100644 index 0000000000..3a8ceef5b4 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/led_indicator.json @@ -0,0 +1,21 @@ +{ + "fqn": "control_widgets.led_indicator", + "name": "Led indicator", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Visualize the state of the device. Fetches the value from the device from the attribute or using RPC.", + "descriptor": { + "type": "rpc", + "sizeX": 2.5, + "sizeY": 2.5, + "resources": [], + "templateHtml": "", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-led-indicator-widget-settings", + "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":true,\"title\":\"Led indicator\",\"ledColor\":\"#4caf50\",\"valueAttribute\":\"value\",\"retrieveValueMethod\":\"attribute\",\"parseValueFunction\":\"return data ? true : false;\",\"performCheckStatus\":true,\"checkStatusMethod\":\"checkStatus\"},\"title\":\"Led indicator\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/markdown_html_card.json b/application/src/main/data/json/system/widget_types/markdown_html_card.json new file mode 100644 index 0000000000..b7c6aa972e --- /dev/null +++ b/application/src/main/data/json/system/widget_types/markdown_html_card.json @@ -0,0 +1,25 @@ +{ + "fqn": "cards.markdown_card", + "name": "Markdown/HTML Card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Renders markdown/HTML using the configurable pattern or function with applied attributes or time-series values.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n", + "templateCss": "#container tb-markdown-widget {\n height: 100%;\n display: block;\n}\n\n#container tb-markdown-widget .tb-markdown-view {\n height: 100%;\n overflow: auto;\n}\n", + "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.markdownWidget.onDataUpdated();\n}\n\nself.actionSources = function() {\n return {\n 'elementClick': {\n name: 'widget-action.element-click',\n multiple: true\n }\n };\n}\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n datasourcesOptional: true,\n hasDataPageLink: true\n };\n}\n\nself.onDestroy = function() {\n}\n\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-markdown-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"markdownTextPattern\":\"### Markdown/HTML card\\n - **Current entity**: ${entityName}.\\n - **Current value**: ${Random}.\",\"markdownTextFunction\":\"return '# Some title\\\\n - Entity name: ' + data[0]['entityName'];\",\"useMarkdownTextFunction\":false},\"title\":\"Markdown/HTML Card\",\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}" + }, + "externalId": null, + "tags": [ + "web", + "markup" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/markers_placement___google_maps.json b/application/src/main/data/json/system/widget_types/markers_placement___google_maps.json new file mode 100644 index 0000000000..569b2677e0 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/markers_placement___google_maps.json @@ -0,0 +1,29 @@ +{ + "fqn": "input_widgets.markers_placement_google_maps", + "name": "Markers Placement - Google Maps", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Allows configuring the location of the selected entities on Google Maps. By default, store the location using 'latitude' and 'longitude' server-side attributes.", + "descriptor": { + "type": "latest", + "sizeX": 8.5, + "sizeY": 6, + "resources": [], + "templateHtml": "", + "templateCss": ".error {\n color: red;\n}\n.tb-labels {\n color: #222;\n font: 12px/1.5 \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n text-align: center;\n width: 200px;\n white-space: nowrap;\n}", + "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('google-map', false, self.ctx, null, true);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-map-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]},{\"type\":\"function\",\"name\":\"Second point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"fitMapBounds\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}

Delete\",\"markerImageSize\":34,\"gmDefaultMapType\":\"roadmap\",\"gmApiKey\":\"AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q\",\"useColorFunction\":false,\"markerImages\":[],\"useMarkerImageFunction\":false,\"colorFunction\":\"\\n\",\"color\":\"#fe7569\",\"showTooltip\":true,\"autocloseTooltip\":true,\"defaultCenterPosition\":\"0,0\",\"showTooltipAction\":\"click\",\"polygonKeyName\":\"coordinates\",\"polygonOpacity\":0.5,\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"zoomOnClick\":true,\"defaultZoomLevel\":5,\"provider\":\"google-map\",\"showCoverageOnHover\":true,\"animate\":true,\"maxClusterRadius\":80,\"removeOutsideVisibleBounds\":true,\"mapProvider\":\"HERE.normalDay\",\"draggableMarker\":true,\"editablePolygon\":true,\"mapPageSize\":16384,\"showPolygon\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${coordinates|ts:7}

Delete\",\"showPolygonTooltip\":false},\"title\":\"Markers Placement - Google Maps\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"tooltipAction\":[{\"name\":\"delete\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id;\\n });\\n\\nwidgetContext.map.setMarkerLocation(entityDatasource[0], null, null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"8d3c0156-0a14-7a6f-0ddd-0ec16b9ffc91\"},{\"name\":\"delete_polygon\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id\\n });\\n\\nwidgetContext.map.savePolygonLocation(entityDatasource[0], null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"46bf69cd-8906-234c-a879-e2e4c92f5b67\"}]},\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"displayTimewindow\":true}" + }, + "externalId": null, + "tags": [ + "mapping", + "gps", + "navigation", + "geolocation", + "satellite", + "directions" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/markers_placement___image_map.json b/application/src/main/data/json/system/widget_types/markers_placement___image_map.json new file mode 100644 index 0000000000..35f7d0dce3 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/markers_placement___image_map.json @@ -0,0 +1,35 @@ +{ + "fqn": "input_widgets.markers_placement_image_map", + "name": "Markers Placement - Image Map", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC/VBMVEX9/f3////7+/zyyUz5+fr6+vswVoD39/m0tMb39/fMzNn29vfz8/Ty8va+vs7AwND09PbGxtTPz9rx8fS4uMr19ffDw9KxscShobjIyNbBwdHNzdqyssW6usvv8PPs7PCoqL2zs8bLy9jt7fLExNOqqr68vM2srMGjo7rHx9W2tsjS093Ly9etrcHOztq4uLjy8vPu7u7p6e69vb4YGBjd3eXPz9zAwMDv7/Gvr8PR0dyursLm6Ong4Ojq6u+goKAAAADn5+2zs7PY2NjMzdLr7Oy7u8zY2t61tcfExMS2tsG6urtcbnp3h4/c3N21tbWnp6cvMC8RERGmprwzMzLm5uzq6urU1N/Iycns7u/k5OXb2+OjtMavr69EWWXX1+HLy8vj5Orc3+LT09OfoLebm517i5JwgYlRZXDi4uTOztjBwcLi4unV1dXOzs7Hx8ewsLGsra2EkppneYHm5ubg4+bKytbO0tWNjY11hY1zg4t4eHjAplnbulTa2trQ0NCkpbtmZ2fx8fGyssGqqqukpKRtfoYgICAcHBxmd4JabXdubm9FW2aYmJhkdX9ycnNqamrpwlBnX0lERUXl5erj5udqe4TjvlAmJiUFBQXe3t7U2dvW1tvT1NiTlJU8Ul7uxk1MTE3n6uvDw9HExMs4ODjg4OC+xsq/v8mOm6KJiYpgcntaWlokIyHZ2eLV1uDHy9Cut72cnbSUla+QkJB9fX5WaXQ4T1uhq7KYpauUoKeFhYZPY25JXWpWVlY8PT0vLSmys8Oosrecpq6RkawLCwvT2+TDycy8vMqxsb6Kl56BgoJgYWK1nlaAdVN5b09HQzm6v8O4uMO1vcGiprerl1ufr8Oxur+0tL2OobmYmbJbeZpYd5mAj5U4XYVNYmxSUlKIfFTYt1HL1d+rusy6wsZKa5BEZoxsd4rau1tgWUZXUkOoqLOGhqR+fpyQkpNkcIOKflqwvs7CwsW0vMF+l7ChoauDeFSHelOfjFLKr1zDqVrIrVnFqlmWgkfCQ83xAAAiiUlEQVR42qSbeVgUZRzHf+/bTHsf7C4Su7DcrLC5CyyX4IJA4BLJJQqBJURWKkKaoWVJ+lCZZlpZWWpmaplZppVZdtl93/d999RTPU/11PP0T0/vzOwyx84B8VF03nlnh/m+n/m9u8wMgM5ZbgZtHBWgDGUDIT4TqHDVMVDBm56m1jsNFEBw5wUkB9L6Q4KodFJWcRCLyramo2+rfT8SRKU3a5r8axnOWwnjwa9qxEqDgEovKLP96E5QoTy9CpQhQeRgo9w9Hh8IOv0qnbTVBAKmp3H7hMG7YAUTUTh27x49E9TGnARR7k2rVDCCYLwkB0AZ2moBAUlFwIGbYNAcrL1wFHgeOwNUIEGKQBESRNnI+IRAXmb8utiSNEjKXGAxLVls7l03OhoajBoimI+mU/wL5YLUgBKUae50fvAoAXa7HWTAEE9CnnQjvy3MGzGDgMgAsIT2n9rXqn95We8SGgiUs2jg/u270ocx0BgI9qpwT2epx2M0ejyOUn9nZ6e/NHBFMsFDcDgcpVOyY6S69BmE51KUgPScysqMbLfV2pBqdTC7cpCdBCruH3DG8mBdoiUrOwHEZObk5OhjW1izLHZDdHvKeyTvVB6rnhBJtU5Pv4LhiV3vpafnTDthbYhEIvnu7ITsbDdDXqbD4SEYn8tjkpzqCThKSTKjbYrnUDjLrrOkeS1ZYWtKldlbVcSQVT6G004Au4UbLkNa+FDAw1JaWuqxuvLzU902feWcU1iOfJJrzc3NzU5IZkdOn0lhAJwPWwMJw47UI9xGc3JSrPkpkedyAoFoDEfnggULSrZWWWisMxgMOu+u82galMEwncLKG5hSLDA+aB0GCVhncabNzZ9C66rCnQFGV+eAlwKWcK4rLzk7uccLlKUkKepQl58FDHJHbNj1olp5EnwqdUuCOBX7RC2O2KKwGl3GWMFRoo140vSxgsswgxKHz6dA8H2lIC4IAnmQKcUL40CYR5ISXA62FeuKH1FhEDsocOd1j8JkjCSSIJM00lBKmnwGdSOJoMAz98bef5WMYFUj9hTnZI3klmoZyeKD9IECn987Q8MI9mE1I5FJ14jNoWGEBImJ0O8AjEEG+xPfg4YR2ofljSBEeb06Gk+6RgJaRvggkZ3LVq8OQTwf7toOSN0IpWAE//HDyYQfv8NockbyjeM3kr9zR3//DojnqiewlhFK1gj69ouTo3zxLZqwEWHI5ExtI3yQroXBbojDvutZ0DKi89EyHd+dLOA7NBkjFZm8cm0jqx5eaJCZs87HoGGEC4KkPtgAn86f/xm7oOJE24g/E2saMQGHaydFlUE8F30OmkZMPjpuPebOq/kIfcCdXXgSRvyZtIYRryBI6yaZWnd+eQ8A0jCSGG+EnFh8EJY/0GSM0BpG+CCpO08rqIM4Lv7ymKYREoSK6/hBGuQHNBkjeNxGUmeNbDJDHNu/PKxtxO6jpEYorj7mv4DQCx/Pn/8paVKTMQKaRnTA0bBjWXAFxHHOl3drG7HEG/GyNggk01xA80nTC//fSEDrfYQPkjtrZ3AE4qj/cqW2EUucEWEQXTSIcxJGOrWN8EEag0sgDvroY9pGnPFG6PhTC0/CSImWEedYEOuslmAQ4pm9XNMICaKTGkE/Sov9x8nMWnO1jPBB8maFBssgnuXLtY14SRBpx3xpkPmTmLWSLdpG+CCzhpohnleWU5rv7N54I5AlDZL1/z/9etPx+I0k7AgNGSCe7y/Yrmkky6eLX/+Z+CPKZxITEzHSkwPaRqhYkFkrW7shHuqRck0jWTJG0Asni3hB0N/dWLZiIkYSDgHSMGIZC5I9q6l1CGQ4Y7amkTSfIW49q4TnM0Hx4pYl/UEgjNdICmga4YPs2fFnaxPIsPyolhESxBRvBA0Ig/QJXolrl9YNTmDWqimFCRhx79zR2gIyHHvcoG3EFG8E0F98jr+Q8BhaWjFMwIjfoW3EPBbEltd0ZQii1LWuaY4VzIuP36llpErOCADN/4RIiV4aMtdNYNaCQGAiRmy2+tYRiFKwZCg4tImbjS9+fKeWkSJfokyNCOr9BfE4PnRgVT2MBxoYHIHx1AgNLNhmq65dB1HsQ0NDvaZ+rueCxzSMsEGkIObrYy7Hx+KLlPNqDxiAI92XpLcypKbmZ9jcUWy5ue6AZziZXNBP7ixKdIiMwNwKx1YsHjhxkNitCXNjc7BxsPcu4GTRn5xRBepGanxenRwU5q6iAAfNcfGs06ghbhHiwRQNzpKK5AQmxbDH48k9RR8AnrDVTa7XZ/hNMAaVaEmb6wKaonQWi9NmM2GQwZxwZMMxkIeiKJPdbveGfQEPy2Kj0bjYw1PTxwQ5VJMnYvOWjbfbWIBB6XyJxizKeFCfmhlmL7abwrnuNDZuOI+5z9BgdaVMn5bO8OC0KL6UOYQcn29ahityIilpGntr5NwjR448uOE6fZR8l8uVkZHvSnW7G1wM+dzq52zu7Ow9bhsLe9uklA0SsLhPPtldkyyM4crb+/DC2mgQceEKfY+R6Sb/VLl8PnJMvge9II8ryUrZvVlFNVVGY1EaufNDUQLL5OxwOp0XX3cGWasGTupLpGiCyCltsqT10R9/TL1kwnQM5iNE/Y6zaoFF0QgfqMRt4uxbKMpizBT0iOhMCmMWg9GIQYHD10i/pXTuT5oBitALIhTwcEFagEXbyMAhEJDp4Wuerf+xrbcm2WNBehQvQ894YrvkO0lHM2mr8mXqEk9EeHw4obm+etOFWNUIwitWcC/uFGXLdIhSo2XzIIolA3MYisqxYM9iLjhPzQgiQbpBAQQ1npeAhw1SP3IWpWqk/+pXV6/vRtCTAiIyK8T51y6NHa0pH3Po7Mf7EURpkUzSyzeYuTWKRrqhsBDklVg8NlHyzOb67tqgeo3cug6h7vVQUQKIHXkCIGbZzjVia0mQWLcfY2YJdFUoCiCYicVOntmwXcPIxcHqKxWMUGIjEHYRI3ep1gieSZHlftq5aHfxagxo8Kni4+S/prarDwAq3xzc1nYbyXSw+MBu1khj9ebiteUAhaNfraJp56o6NBrc3L4Wz9s3c98i0cE+sOFdDSOLZjTXAcjWCPbo7cIenLNoZzWoGkGr9o0Aquqzry9Ho6tRd5sJVR9HjbtpWH0czZvZi+qKEdpXBvPa2SC1bWbYtButKw8WDhX32zf2olVtBti8DNHMgAgx3PuYuhESpL+/QN4IePQVIGTH3r26GapGAK1Z+/xxE1y4CsBwKzoeQgCD6CmikZoJ89pIayYu3DZWI7ULmTX0yCaM4OBqYIKQV/Q+zJxakiI5NlvTyKIZ5fJGIBDRt7Z288c6b//o7WXqRhCiCg48hQ60X03Ar7Gy0fNMobbXz+tggzQfFAVBxeXBA3UINr2K2CAAvavZIGLqd5m1jMxYZJA3QoJYzQW9MAb9zkcHajHfXlEoNkKitlANiTDTNHolYjgwhAAwemoFkATmaJD6DokRQ9lrNIJQkziIdHAv4R48UzbSdPuqkIIRh/6lwhVdgmP95XTcAhynVq1oaj1LasTQvgaROoDC9QZEB1HzjYC61qKmLYDOeg01c6cWbFuDDFdzQXYDCj0FpvbboLmAAmEQAwIxj802qBvZdPyhWnkjJIj+ytZB4Pl74e0h4EjY/1GdaWSF9H1k0Y3rt91Yj9DIffu21SFUu77tSQrBpqu3kUGf0aHzYTKv1m/bdvBJLsjBG9vatpoKlm7rONgIFj4IerV4jaRIDl+6ElRrJNQUohWMePSndXfX850kyMJYjegW79+/v78fS4qlhG2QL8T8JV+xFtfoA2Tx25k+NFYj4cIQ2Q0Cai5iAECyPz6ddumHqkaae0MjoGBk8YmHXw0VAE9leXkhxNjj2N/fRUvmklyL3J5EDFi5J7ZiNeIqbNwUxBhT4g+N0sE1X/KoqpFZ59yGlR7BXBwpf/klQS9MM4GAJB19Zdc80a5JEL6ldHmVrqnIMA5QAKi5n7QjhYWFddEgoi0lHJ6taIQN0rRwESjgObFKvxgE+HQgJAAXLhth9ykywrekRvhW2Gbbyp1EkF9I0DYCZz5yEyjNWogEqX6oUUmJUX+/fljYlyMOcsidBSAxotMyEutdtPeBWTpggzSGyjSNkObsw2pGmhsHy+aBPIv1+hPZwCMNQldUSN/Z/Q4NI4gD+vYSVgIwNdJV10+C0BpG4NFLTABKRkiQsrK7FI3o9XuEfekUiLm/RLJre8SrZoQHzKHTdizyAICtsPAuTKC1jLx7/gNqNTIYUjJCggyfGAABcyjpub5AumujDYOSEYSgbGNH8a3FHZvLAOHC7u6KPoDcQjsmaBtZef4zakZuKxsBeSPYqF+4sFHYR4JISZbsuiZ1rtQIryPY9vQbl11+x+WXvfF0WxChgu5DDsYIxtpGSNtwyT0qRs45pw6DPNgYMdevAwFXUNKBgB5PIgiPG0r98kYQwlvu++ekGP/etxGjAj8x4vfh8RmBd89TMZKo/CwgMSLpnErHp02ekijaNZ4eBtkawbvfuuUknlve2o0RU2I4ZywIreiDXVWwy6BsJJFvSKFP1YvnHGEQ/pGvwLC4FLJz5IwgtOWtO04ScsdbGxG7A2EQnqFB/mqfYV25HQizn1E2YlE2QmeKjQCeiiEOvEDvt5gF9MyhZIyg4H2sD6GT+5qYJIYcGrOQIPz4LDXQocbBrrNGD6wpq66u7hoAwlXXASgZcSoboYx6LDJCC4Lk2gju7CkNEb0+KYfFN72S/X9qdrwRBG3vnyTl/Tamzy4KEqNlpL92SVdXfdmSurpQdfVZJWYA04dHyxU/a3kVjQCVqRc/EUNNhTH895cMFGVZKBIPAySayRKDs8RNogDEGSl7Onb4l192eWzx6SWk15JDyQQJ1ra0NPV22ZeUrVlTNzq6tyRM4wH/0ZUgAXPQSUUUTWOQAVOmYb3dZLZYdFQUEwmiTQQgQWoEoY1vcMd+/c/tHe03XM813tiMEKTNMZlMTm85ufbbV5TltdSEe3rCJTL09FQcvcdBCAQccZxijeKeMmVKcgK53eEiZHCk/JouIue3c1mu8PlA+VdGrECSSN9HUMdl3LH//KQZmZ+8gWtc1oHS0p97cHoSYbrv3E8yKisrUzJYUhsarNl79rC/lGBlaUhNTd2wISGPvbA+XNrpZ4kFmUO2sE0h9zMc/gXhtEQdi8nMkpbc0FfDUJTFEZ4D4yAVCJl+CkRGirnz6Zt2M4llbo+2ihFUuSzAYXPToMGxaw2U/HX5fC8FStiHG8SdVXyxqxhxAcOhSCDs5DdGt94Rc8D6eZ2bgW9FKC2iiwWxaQY58xEM8mRYQJHEZCaI8B7wVHpcNcJRkuFL8odrighbAbXfwlU6MYJQQXvMDwJnfmynbu0gcNXdIAtOUQliGG4Q7hmRINQ4jKQAj3dBso3Bzjkg3EBqpOCraI283oHAkgoTCHLvzSALnWIGRXTDDVj85AoJok2S+HCwLlGHEdrydXTWukE4a21B2OuaUJCzFY5VLQiV3IBFRrxTdeMwUimXFl24T+Z9pONCRM5fPoj2OD3wCDXxXz6iJUbYINr4ZIOY2n86ScpP7SZkjVgnEmTlI2eCHHa1IPjUBsnzdlNN4zCSI5cWobVvxgV5cwuCInf2RIIUXPcuyGFJsasEyWwAkRHL1ETQJl12I7S0+HpJjuvblyIASj+RIHD+pSCHVy0IZEqM2EkQbSNz7CCr5ODvkiC/H0RMkAyIMkUUpHDZUgNwrMOC+fcoDTKUqwYxSowkTrWDNlP/6+1MYNoq4wD+f5/fSynvtRTacnS0pUCBlXXlctwg1xgwBnOCMIIIsqEwHYeIMJ06BZ3iPDZQN506jznn7Tym85xGnRrjFY2J8UhMjHeiicYj8fteW97Be20li7+ZdXTljd/79Xu9/q0GUDXZXnCL/F58QQ3dKE5QFUFNpobh1hoWHllTWTmy/ZymwqswEKaO7QAVnKFF3gQZXJYhgiKJZgD1VXKzTOTmKxhBpFhVJLd73YXdZ7fkn3d54mOVhKtaqnkEoPvmWBmoUJFsBW1WZshfPtdFskaQhggwdQV/Sjz+LqgTNoq9qiKwrmF4a0NDftMPiYmVrVcNVxeek8MjxPcd61MXMej0HGYFkFoRVgSRIljnZ9H1GPYiEGAT00AjSeXLd4kPdNdXMiwQDKUouNhtEhGkpwBANnkPbNSZFTkUp7Oi69znOKtVR0GBIQ+DwWFoS05K9XPRRakBolf6Kcku7Y+XYXvNEgQE8k0tLS2+EsKv1dUXjDQV5pNsGiK07e73xEPvbgBXVEJx8ZU/rnG51pBN2p/8LFHJ5QmliX42ZGa6Lyen/Tubk4sFXFFRdnuMjY7oWDyeJ233xMffYxNIXxFXsnJZqojNGy+nP8/MU6z+Dju611VX19QMPUbeJkxEpkdahhFwiU5QAfdYesgr8JcEPC4puJ8xWIptaRgtkO6pckpIM8gx6Sjjzc1lHMagxFPF6RXzV4i1mp29PdHExGjFC7AARQkoSPB6fBnuGN5amL/i/eHqy4ZbCpsADIkViyScFm9Cai8PTMvDN/lvQm5tZQCluR5BEro8OghLZ3NzJywGRTnVXqWl6B4hInJzh1chQsHYanLkrVjRZfI/gjYktiGQYo52Z8b5z2OYG74XRL4nRyxy2baLkIQ8IsKE+QUcFVGeTUXSpOdSxL/LVoqYM0UROvwVYzQaM+wWS0bsCj+D9LeF6+NygXh3+hZxNJW9j94L/uMMjgQhuJAE3oMhPOpFWCKiUYSBtlQjC1IMKaIIougcbac+Ghe0GCwhDK4ojk+nP79AbFyX7B84ldlb8O4p7xacRzwoPUiCiYiET/LscTMsvhiOcmgWAfMikQ0yERF6LAzicDuAgjDGCBREISa/4OOCBuoh0CXdSiRFmPo72nNzeYYHOUkOlSI5fiU2NRVkmLIkIlqY3RWgSXIaw1SeXskwyA8bhUSwhw1fhLlm9w3btrUyN2wECSgfm+VFKBdeF/hTqrMmV34XBYsi7YhStDf3P4lUAENYCIK9nLQIC+EgIrWIEQH/b0VXMwhkg1T0hAnihKc6GBDRZ3ELIq3XX9XaMFTZ5PMNnYekGNxpIUScCIQDSQDOi6VrBIUNQkV2ADl56kLmyMh9LUzh+oIjcNn62+7jAQA/VH3rffSu6Karz8gnRd5mcre13HrG/czu0x/eDyK6LMOCyKZNm6Z9viIi4itEUkzuCqxXHw7GODmPN3B4QUNnLpXuAg+KpMi9wh7/ci9z4925ULcewbZppv0aDAT9aRfAebfpmSNPAXqxkFn3InPhaZeRqT6G3VYIEnCWY0FkZNMmn++ty90tPl+TGUuWvcG9jMwQp0YHWbmKULKMDGfHLr/nMwvFbrQZPa5SypOKImGCUBE6eKQXRM5hoO7WHQwgctVCBkGEIa0K4TY90KEqKvISnX/Bhqc6QAKbVbQgUtnamj/0Q4LIGk9MhicqOTn5NXrrkmGMSSpZFYSqCWyIKa8w07s4AUzFssWOIDzX1AJDEEToA88v148AEWFNQZGrpukJ4NNEEZ1CBEmKIA30jgQrZkEd3WrOLyCKYCTigRDwlAMH0DW1fQfIyd3bmSsucHSyiNFvex7ar2Hr+8o6TVTkwXxhSCq3gIpsfAlMaJEIZI3rdDi0CNIX60EL1m5SinCI4pjr65sb3zk2NjYzMDDQvHnz5tGBMcpayrkS+II3RsnJ+EsfMnePHH18/90cu/8hff3r+MCucwf6Tqst2vj6OnTj23runbfh/d3MY+s7p/jTdtRf8TN78MSh544/d+jE51O7zj13fvS4206fudcWwSFEwM7LRfiE+rm590d3rQ2wc7NI8+TUwAD9r3nz6NFv+urv2NfX13dQX/DGs49P9OG7a5h3OsrK8A0Ft94394b+pYJ9hwYOzZ123frTpztfzXnnmoIb+DdEkd+aXn9n31kqvABIk2IONMmQipjHJyafIezcPDXVfHC8/jHvuB7CwTAcyG8xQDjhILhGhC9BvB3RA2NNRwC4syxI/Wd9c3NzffWEECLJ+hAiZXSRdPYd3dw8eu0zZ51751nPje8zAQWxZq85grc6NjeDCmwGDoiowMbEgIy0LF5c7JpEqYtwneMTR+d3zk8O3PnMnWubp04c7MQ4wQULIIPXoHn4FYWap9Q+RQh7qAg6woDycO0XES8euYjy4RFLDI43j+30M3X0jfFOHMTTAyImKhK+yIDae3uwRydez0QEFSqCxO1EXmQHUNic8aOfz+ya/3z+2LGnZw4dOv7qb29MWPbpsARdchuIWCMqMjmjVkRHREQ3RRFERGRjTM4sQzgRrnPf/OHD8zObJ8kx6PDMzPzkoYN1GAJwxj1YSlqCA0Q4Lx9Bkcl5tSI6O2YE1IogRREikqMqYqobn3h1lOz90cm1FKIx+nhZpx6UYOOpWEp5gk4pEr7ImH/vyosgYETEb9MoUiERIQexvqPNA/NTn4+tvfbaa9dOnZicnDzxyXjfxBNtIEP2wYxnYimrouA/r5GpMRpYPu6iGKBiFEVAUUQmQn74w2MDM80zk4c+HDdjyX51mRQ/vggyRmMptmyQENFRi4hwiiIqA1TyIqAskpdlFovwCFTRuTjxn1AqGeOxBC4BZCIRFsmRrxHlANXHdIBKWYSVFRFFtAergHV1aRaBGCPPO3vLtxgwpU0uYvWaFhXheHEaZuN5QBgYK5IXUR+gwiArwsqKVIkiyuyiE3INAmgVsXkyLJbVdou9CxOyXSCBNRERJdu3gx42PvDAdEO+b2ioaR0V6ZStEY0BKhSqSFUkRcBVq12kZCVQuLbl9PBlPDX4zDkiYJ6IKDdWU6Mb0XNXXnn79u3b84eGeCpyAEC8HREGqG5SG6BSFpH8LG2RFIGoWuAuVPekIgEQxmlP9uYRKhxpAo6chLY0s0GngwWwqebelMYyw5VXXvnKp62FhQ11Vt3A2ByIaA9QAQ8L2GIMVilbspxcjp8QRTy1rddfn6uWhIqILfHq10oVMztZ/YkbKJkJLstqS1SCVyAl5UoJa7+1ryYzQOlJSbHxhB7JANXX734tGaDqiqUIFyouzpDzZFQQjSIUS62pZVqvLkpFgo0N8vukCFuzEIQF7dxVj3RWrjc7aTkdBkpjrnsvOEB129W3fScOUOGe1Nj0+FjyuslyYyl98cGWFHdmV3lveVdcTJaZM4QpwoC9dkd+oUm9yKqFIgxwMV0gg1XO7KhyaNcciEgGqG7eXce07xYHqCTfn54OLEbSZxqx5hoRWV178bZtPnXRVZIiVTEG+VaoiPiVxlgf+9GuThARB6gufT2XYZjc1wNfFchEYgG0nmkEpFXEWHvvVS04fJFH02EJRXTuXXogaA1Qrb8kMEAVSkSfxS6IVA+XaRSJriVA+CIXlcv3ARGRnKGVhEvZKd82GaBSKXLL6VKReIWITvIkdnd3jUaRZbW1j02A+nUvTizCJig/NQ5lQfgiVvcAaK2RdukaiVxknXoRIlK5bZNGkTixSFWxch+gSIqYvdeCDMkAFTlqBQeo/riRAW0RvEEUqey+X7NIzgVHeOm5akXgzGRYSpGKzGORDVCFEmFlIhdqrpH9R/bvUAsiE7FlA7OEInvcsyiyASpYAClFUIooMtxdp16EiAwfaQGtIhihwOweLKlIT8osBzK0BqgiFKnuLtI+ap0/UqdRxEVGdLOrMFf1yEoIVUQrCMRumO0MO0B1Cxmg4ttDiLili92kUSS1tmlkpEOjSPwq8knv2Sk9PT1di4pABEVMxZfPzoUdoPr9IQZamrRFIFMqwmoUSardf33TRo0i92BEccRs6BE0ZLrh1giAIcP70WxfJANUddUtEpF0xErBKVhHYKnIxUdAo8gHb731Vh5o3LLfo0MC5ZevAQUsSkGgCWsin9oeU+qlInor5///CRjSnFW95VhlgArf3pgkUpoZLccTOI2LkwnIGVz+wFux6g8RiYgeCThdUcVJQchgjIXyY2ZmZnGyx2gJ4hIgszMZnuTihGQCFfnIbl9ti7cZ7QGyyQDVJRKPS8gAVZdEIzrVmxC9mGVxFPrjaRSJS/r0+k9ZjccjQZHBqC1grXqEviKXvjy6Z4+BxfjU/i06DKEx2DbMlhq3sP5KmLNSYNEAFbCyuLGxVhQAlDDaRZKmL34rR+PxiI1DAsYeCMBCkN7+CsVuUVn4+JVZT1XIAarfdwsXl4twWk/MhCyC0Afqa0QUsecBr1fsl6p+p+I4pjaQMTFrVPlIsI3X/BXw+IcMUMn+PiCiPHSELUJEHnx7D6gXyY4PNPbmXXzD84pN5PWnhS8CZS/QBajcvmyAimFkf7v0IovODEJEAkVK6ciBYqvOfnMERRyztsVFAKQDVHCyikgslUVsJkTZEgtoZP958k0U9RvCFmEAz74ifimeGxig+ooOUMHJK8JoFWlsfLOyBqHolRXT+/evk2/V3G+KoAia/QgtLsKVBwao7idBTtYauUizCBVpvHArQivKK7o3bVJswtTPRVAEZkdVdi63GgIDVAyctCIrQhb5YONehFxVFZdt3KTYqr4fR1AEvpgBlSJ2Q2CACuCkrZEVIYt8+gBCKKGTP+fnVsUmcD8bSZFr16qsEc5uBoYCKkXQEosMahcpIVet24lIcQ6/lcfnyLcK/RBJkZldKkX0dgMA9QCVImiJRQa1i8Q1vj/cTovw/NbtOwoVm0iESIqcmFcpoiMilP+ryLLGny9uQoi18PzZHR0XKIpcLpyGK3JwcnERIsLTM09ukTjRUmGKljXWnteAUNUgz7e3t+9QbMIdvgjd+cf+pyJrMjSLsMvIGsEIDa4gRYYbQFGkOKIi6CzVIoaTXqSkRLMIRBORWsQuJyJ7fWcrN+GKqAjMIlApYtIuwi6tSMkq7TUSR0QGSZFonu/wbVUWsURUBA7nqBUxaRdhl1Zk1Urto1ZPY+NIGVkjpMiQL1+5CWNkRQ5/snjnYrv1pBeJW6ZdpK3x3lweIWsGz1dW+pCiSEZkRa7duagIFTnpRVZGaxdhGyfacxHSWTr56Yt9SLGJ1ZEVmT/rPxbBmkWgK0SRZdHaRWC4u7uaPtI9kwV2qWtk4k7HoiTYzmkWoSJW9SKP/xKiSHSIIlDd5DufiPRmFO1/Plep6glfhP5x3531KkW4kEWsakXg2V9GtYsQEQSgVeQDPRJILlLp7IqsCHv4hEoR/X8uwj737atsyCKsVhFRxBmlMlSQEFkR+GT+wOIiOhCJqIhj8y+PgRTlzSgV0S7C6QVMbj3AUm7ZKXNjBxXbD4ps96k9baVTKQJl3zYLu6Ntzx6nQJpZwKQTQFQkj34+AhllMBisGAOYHA6Tib4Tq60n5gljEHc5KKN6tYvIjUePLy6CAYCdrpm+qruD3B1taB3aWnh2RyWCoIiCx886joGyIVGDDSmBsQK7H6PNJrxX0EOx9Pst4uNtTzyx/FEsvzJkovCPRwQO7lL6soIItPoumG4tJDQUFeY3bG3yGUQR6eb40W8fAD9FTodZgVUnkJ6OQB2E89wQ5JHk9PQ4nWyHZ7KRrREou5MHCaJIrh46Wi7raBi637y3Yejsphozj1WKTKzdOQdhkYvIV0laCizQm94T60rvyjNhwRGzqJS3mvxwOBTsgReeXTQtyWGdzizgdFj9OznN6ayqatuypzc2dkubk+BwOMxWq/X4C5O88rlf5a+AiOwMySXNbhBZY0bWdIuLUFxKyfzJFRUZa16YTfHjLSWQb7O8VupNWMBis1g85F2wXrefYreFYIyJMdLTqBdG8YLGv9vZeTxFaf+AAAAAAElFTkSuQmCC", + "description": "Allows configuring the location of the selected entities on the Image map. By default, store the location using 'xPos' and 'yPos' server-side attributes with values of 0.0 to 1.0.", + "descriptor": { + "type": "latest", + "sizeX": 8.5, + "sizeY": 6.5, + "resources": [], + "templateHtml": "", + "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('image-map', false, self.ctx, null, true);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-map-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 0.2;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || 0.3;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]},{\"type\":\"function\",\"name\":\"Second point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"xPos\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 0.6;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"yPos\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || 0.7;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showLabel\":true,\"label\":\"${entityName}\",\"tooltipPattern\":\"${entityName}

X Pos: ${xPos:2}
Y Pos: ${yPos:2}

Delete\",\"markerImageSize\":34,\"useColorFunction\":false,\"markerImages\":[],\"useMarkerImageFunction\":false,\"color\":\"#fe7569\",\"mapImageUrl\":\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMTEzNC41IiBoZWlnaHQ9Ijc2Mi43OCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI3LjA3MSAtMzA3LjkpIj4KICA8ZyBmaWxsPSJub25lIj4KICAgPHBhdGggZD0ibTkwNi4wMyA3MDYuMTMgMy40MjkyIDE3Ljc5Nm0tODgwLjg5IDQxLjEyMWMxNTAuNDQgNi44MzM0IDE0Ni4zOS0yNi4zMzQgMTY2LjQzLTI5LjMyIDM2LjE0NC01LjM4NDggMTE0LjI5LTYuNTI1NCAxNDguMzMtOC42MjM1IDQzLjM3OC0yLjY3MzggMTQxLjc2LTExLjIzMSAxODguODYtMTkuODM0IDM5LjgxMS03LjI3MjggMjIxLjM3LTAuODYyMzUgMzE5LjA3LTAuODYyMzUgNzAuODI3IDAgMTQ2LjkyLTEuNzI0NyAyMTguMTgtMS43MjQ3LTMxLjYyIDAgMTE3Ljg2LTIuNTg3MSA4Ni4yMzYtMi41ODcxbS0yNS4wOTEtNjguMTI2Yy01Mi44IDM0Ljc4NS02NS44OTUgNTEuNzQ5LTk1LjYzOSA4MS40OTMtMjQuOTMxIDI0LjkzMS0xNDAuNC0xOS4xMzktMTc4Ljk0IDM2LjY1LTEyLjI4MSAxNy43NzctNDcuMDAzIDQ2LjU0Ny02NS4xMDggNTkuMDcxLTIwLjEwNSAxMy45MDgtNTYuMDM3IDQ0Ljk1Ny02Ny43NjkgNzMuMDc4LTQuODAxNSAxMS41MDktMTMuMzggMzUuOTkzLTIzLjQ0OSA0Ni4wNjItMTAuNDk3IDEwLjQ5Ny0zOC4zNzcgNi4zODU3LTQ0LjAyMyAxNy42NDgtMTkuMDA1IDM3LjkwOC0yNS40NjUgMTAwLjkyLTY3LjYxOCAxMDIuMDVtMTkuMjgyLTYyNC4wMWMzNC42NTktMS44NzM4IDg0LjAyNyA3LjM5MTMgMTA5LjktNC4yODU0IDEzLjI4Mi01Ljk5NDEgNDEuNDA3LTIuNDYxNCA2Ni44MjktMi4zMjA1IDM1LjMyMiAwLjE5NTc4IDY0LjM4MiAwLjYzNDc3IDEwMS45MiA1LjAyMzIgMjUuMDMgMi45MjY1IDQ0LjY2MyAzNC4yODcgNTguNTI3IDUwLjY0NCAxNy4wOTkgMjAuMTczIDYyLjc2NC0xLjcxNDcgNjYuMzA2IDMyLjEzNCA1LjEwMjcgNDguNzY2LTYuMzI4NCA3OC42MzcgNi4xNDExIDk3LjM0MiAxOS45NjkgMjkuOTU0IDUwLjQ4NiAxNy44NTYgNDQuNjE5IDgzLjk3MW0tNDcyLjQ1LTM3OC43OWM0LjY0MzUgMjMuNzI5IDE1LjA2OSA3Mi43NzYgMTkuMDYxIDEzMC42NCAwLjg3MjA2IDEyLjY0IDUuNDQ3MiAyNC45OTMgNC4yMjIzIDQ1LjI3OC0yLjUxNzIgNDEuNjg4LTE1LjcxNyA0My42NzctMTUuMDkxIDYwLjM2NSAxLjQzMiAzOC4xODIgMzAuNjE0IDkzLjgzNyAzMC42MTQgMTM5LjcgMCAyNC4xODEtMi42Njk2IDExNS4zOSA3LjMzIDEzNS4zOSAwLjE1OTExIDAuMzE4MjEgMTAuMDY1IDM1Ljg4MyAxMC43NzkgNDkuMTU0IDAuOTQzNzggMTcuNTI1LTI0LjQ3OCAzOS40Ny0yOC4wMjcgNDYuNTY3LTUuNDc3NyAxMC45NTUtMzYuOTczIDEwLjg4Mi00MC4xIDI0LjE0Ni0zLjg2ODggMTYuNDE1LTMuODY2MyA0My43OTcgNC4wNDY1IDU5LjQ0MW05Ny4zMzctNjkxLjAxYy01LjAxMzMgMzUuNTE2LTQzLjY1OSAxMS4zMTctNTguNTM5IDIzLjc4MS0yMS4zMyAxNy44NjktNjIuNSAzMS40MzItNzAuMTI0IDM1LjM2Ny0zNS4wODggMTguMTA4LTExMC40Ny0xNS4xNDItMTI1LjYxIDQuMjY4NC0xNS45NTEgMjAuNDQ3LTAuMDczNSA2MS40NjYtOS4xNDY3IDg0LjE0OS02LjAzNTcgMTUuMDg5LTE4Ljg3NyAyMy4wMTctMjcuNDQgMzIuOTI4LTE5Ljc0OCAyMi44NTYtNjkuOTc0IDY5LjgyNC04NC43NTkgMTAwLTcuNDk3NCAxNS4zMDQtMy4yODQzIDQ0LjQyLTMuNDcwNSA2My4zNDMtMC4xMjc5MyAxMi45OTQtMC44MTAxNSAyMy4xMDQgMi40MDM0IDI4LjI3NiA0Ljk2MTYgNy45ODU4IDIzLjcyIDI4LjExMiAyNC4yMzkgNTAuNjExIDAuMjk0MTEgMTIuNzcxIDAuMDEzMyA3OC41OTEgMy4wNDg5IDg3LjY1NSAyLjMxMjYgNi45MDU1IDQuMjIgMjYuNTY1IDEwLjIxNCAzNi41ODcgMTEuMzU0IDE4Ljk4NCA0LjM4NzQgNDAuMTU3IDI3Ljg5NyA1My41MDggMTkuMDUgMTAuODE5IDQ2Ljg3OCAxMi4yMTkgODEuOTI2IDE0LjQ2MSAzMy43MDMgMi4xNTU5IDYxLjUxMi0xLjQzMDQgNzYuOTIxIDYuMTQxMSAxMS41ODUgNS42OTI3IDguNTgxNSAxNy45MzMgMTQuMjk1IDI5LjM2MSA1LjY0MDQgMTEuMjgxIDMxLjUwMyAxMS4xNTYgNDEuODA0IDQzLjQ1NSA3LjYwNTkgMjMuODQ3IDMuMDg1OSA0NC4xNTcgNi43MDc2IDY1Ljg4NyIgc3Ryb2tlPSIjMzY0ZTU5IiBzdHJva2Utd2lkdGg9IjMiLz4KICAgPHBhdGggZD0ibTQzLjI3OCA1MTcuOTVzMjMwLjg1LTMuNjM4IDI1MC4wMS0zLjY1ODdjNy40ODIyLThlLTMgOC42MTk1IDUuMTUxOSAxNC4wMjEgMTEuNDU5IDI0LjU5NiAyOC43MTkgOTMuOTEgMTEyLjk0IDkzLjkxIDExMi45NCIgc3Ryb2tlPSIjMzM2IiBzdHJva2Utd2lkdGg9IjFweCIvPgogICA8cGF0aCBkPSJtMzUuOTYxIDU3Ny43czE2NS41Mi0xLjY4NDUgMjQ4Ljc4LTEuNjg0NWM0Ljk0NzUgMCA3LjcyOTktMi44ODMzIDEwLjUzOC01LjcyOTggOS42NjExLTkuNzk0MiAyNS42MzItMjguNTkgMjUuNjMyLTI4LjU5IiBzdHJva2U9IiMzMzYiIHN0cm9rZS13aWR0aD0iMXB4Ii8+CiAgPC9nPgogIDxnIHN0cm9rZT0iIzMzNiIgc3Ryb2tlLXdpZHRoPSIxcHgiPgogICA8cGF0aCBkPSJtMzguNCA2NDEuNzMgMzkzLjMxLTQuMjY4NCIgY29sb3I9IiMwMDAwMDAiIGZpbGw9IiMzMzYiLz4KICAgPHBhdGggZD0ibTM5LjAwOSA3MDQuNTQgNDg0LjE2LTYuNzA3NiIgY29sb3I9IiMwMDAwMDAiIGZpbGw9IiMzMzYiLz4KICAgPGcgZmlsbD0ibm9uZSI+CiAgICA8cGF0aCBkPSJtMzAzLjk2IDY4Mi41OSAxNDYuOCAxLjgyOTNjMTAuNTM0IDAuMTMxMjcgMTQuMzQ0LTIuNjM3NCAyNS40ODctNi4zNzI4IDEwLjQxMi0zLjQ5MDMgMzEuNDI0LTIuNjk5IDQxLjM4NS0yLjc3MzhsNDA1LjU2LTMuMDQ4OSIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im00MjYuMjIgMzE0Ljg5YzIuMDY3NSA5LjA1MjcgMS44NDE4IDUxLjcyOCA2LjUwNzkgNzQuODM1IDEuNjc0OCA4LjI5MzQgOC42NzUxIDE0LjA2NiAxMC4wNTUgMTQuODU5IDQuOTAxNSAyLjgxNDYgMTAuODE1IDguMTQ5OCAxMy4wNDYgMTYuMDg4IDYuNzU3OCAyNC4wNDYgMC44Nzk3MiA2OC40NTIgMC44Nzk3MiAxMTAuNjkgMCA2LjA5NzggMS42NjAxIDMwLjE0Ny0yLjE1NTkgMzMuOTYzLTIuNTQwOCAyLjU0MDgtMC4yODE2MyAxMi45OTEtMy40MzY4IDE2LjE0NGwtOS44NDk0IDkuODQzMWMtMTAuMzY3IDEwLjM2LTExLjU5IDYuNTI2MS0xNy43MzggMTguODIzLTMuNTY3NyA3LjEzNTQgNS40MDI0IDIwLjY3MiA3LjM1NDMgMjQuNTc2IDEuOTMyMSAzLjg2NDMtMS44NDIyIDQuNzc3Ny0xLjc5MjQgNy40NDYzIDAuMjUyODYgMTMuNTQ1IDIuMjk3NSAzNzMuOTMgMi4yOTc1IDM3My45MyIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im0zNjUuMjQgNTE5Ljc4IDQuMTE2IDUwMi4xNSIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im0xMTYuNTMgNTA0LjE5IDMuODgwNiAzMTAuOTYiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtMzE3LjY4IDU3Ni40OSAxMzAuMTkgMS41MjQ0YzQuNTEwOCAzLjI0MTcgMjAuMzQ1IDcuOTY4NSAyNy43NDUgNC4yNjg0IDMuMTU1NS0xLjU3NzcgOS40MTktNS4zODgyIDE0LjAyNS0zLjk2MzYgNC4yNjcgMS4zMTk4IDYuMDE2OSAzLjExNjMgMTAuMzY2IDMuMDQ4OSAxMC4zMDQtMC4xNTk3NSAyMC4yMTIgMC4zODc0MSAzMC40ODkgMC4zMDQ4OSAxNzcuODktMS40MjgzIDM1Ni41OS0yLjEzMjUgNTM0Ljc3LTMuMDQ4OSIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im00NzUuMzEgNTgyLjg5Yy0zLjQ0NDIgMTEuMzUxLTIuMTAzNCAxMi40MzQgMy42NTg2IDIxLjAzNyAzLjc5NDQgNS42NjU2IDUwLjg2MyAxMy4wMzggNDEuNDY1IDI3LjEzNS0xMC41MzcgMTUuODA1LTIyLjg5Ny01LjQ3NzctMzMuODQzLTEuODI5My01LjQ1MjQgMS44MTc0LTcuMzQ5IDUuNDU2My0zLjY1ODcgOS4xNDY2IDIuODA2OCAyLjgwNjggNC4wNDggMS44MDQgNi41MjAzIDUuMTAwNCIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im00MzIuMDEgNjM2Ljg1YzguMzE5IDEzLjExIDE4Ljg0NiAxNC42MzUgMzUuNjcyIDE0LjYzNSAyLjkzODYgMCA3Ljg3LTAuOTMzNzEgMTAuNjcxIDAgMTEuMzU5IDMuNzg2NCAyNy4xOTQgMTAuMjc2IDM2LjIwMiAyMS4xMjkgOC4yOCA5Ljk3NjYgMTAuMjUzIDIzLjg4MyA3LjcwMiAzNy4xMDQtNi4xNjk5IDMxLjk4LTE2LjcxNCA1Ni45ODktMTkuMDQ0IDg2LjU2OS0xLjM0OCAxNy4xMTkgNC41MDk2IDIyLjUzNSAxMS4wNzEgMzMuOTI5IDEwLjY3IDE4LjUyNyA4LjcyNDUgMTQuMiA4LjU3MTQgMzQuMjg2LTAuMTM5NjMgMTguMzE5IDAgNjAuMjY0IDAgODAuNzE0IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTUyOC41MSA2NTguOTZjLTEwLjY4MSAwLjkwNDU0LTcuMTA4LTUuNjAyNi0xMC44MjQtOC4wNzk2LTQuNzg0NS0zLjE4OTctMTIuMjI3LTEuMjUxLTE2Ljc2OS01Ljc5MjktMC42NjYxMi0wLjY2NjEyLTguODA5Ny00LjEwODgtMTAuMTc0LTIuNzQ0LTguMzY0NiA4LjM2NDYtMy4wNDg5IDIwLjU1Mi0zLjA0ODkgMzMuNTM4bDMuMDIyIDMzOS43IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTUxNy45OSA2NTEuMDNjLTAuMjIxNzEtMi43MDE4IDEuOTAzNS01LjU2MjEgMy4zNTM4LTcuMDEyNCAxLjc5OTQtMS43OTk0IDYuOTIyOSAxLjAwNDIgOC44NDE4LTAuOTE0NjYgMC4yODc2NS0wLjI4NzY2IDAuODQzMjktMTEuMTY0IDAuMjI4NjYtMTMuNTY4LTIuMDY0OC04LjA3NDItMi4wNTgtMjguNjU3LTIuMDU4LTM4LjcyMXYtNzMuMTczIiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTUyOC42NiA2NzUuNDItMC40NTczMy0zMS41NTYiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtNzY2LjMyIDU3OS42NCAwLjQzMTE4IDEzLjc5OGMzLjEzNjQgNC42NjkyIDMuMDE4MiA5LjYwMDcgMy4wMTgyIDE2LjM4NXYxNTcuMzgiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtMTEyMi45IDc2NS45MWMtMjAyLjMxIDQuNjkwNS00MDMuNzQtMS4xMTM4LTYwNS45NSAzLjM1MzktMTAuODY0IDAuMjQwMDItMy4zNjE1LTguNTg2My0yOC41MzctOC41ODYzIiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTg2MC4wMSA3MzcuMDdzLTk3LjQ0OCAwLjg1ODA2LTE0Ny41NyAwLjg1ODA2Yy01LjI2ODYgMC00LjUxNTUtOC4zMjk5LTcuMzAwOS04LjMyOTktMy45NzQ0IDAtOC42MjkyIDAuMDIwMS0xMC41MDkgMC4wMzU5LTIuMzM0OCAwLjAxOTctMS44MTA5IDguMzY2LTQuMTQ1OCA4LjM2NjktNDYuMTY5IDAuMDE4OC0xNjcuNDEtMS4zMDgtMTc1LjA1LTEuMzA4LTQuNDI5NiAwLTguNTc2My02LjQzOTctMTMuMTMyLTYuNDM5N2gtMTQuMzk1IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTY3NS4wMSA4MzEuMTctMC42MDk3OC01MjEuNzciIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtNzk5LjQgMzEzLjA2IDEuMjE5NiA0OTUuODciIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtNzM2LjU5IDMxMi40NS0xLjIxOTYgNzE2LjQ5IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTUzMC4wMyA2NDMuNDYgMzkyLjM3LTMuMDE4MiIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im04NTkuNDUgMzE0LjkgMS4yOTM1IDUwNy45OCIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im05MjEuNTQgMzEwLjU5IDEuNzI0NyA1MzEuNzUiIGNvbG9yPSIjMDAwMDAwIi8+CiAgICA8cGF0aCBkPSJtNzM2LjI5IDQ1My4zMSAxODUuNjgtMC4zMDQ4OSIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im0xMDYwLjggNTE0Ljk3cy0zNjMuMjgtNS42MjYyLTU0NC42NSAyLjUyMThjLTQuMTc3OCAwLjE4NzY5LTEyLjUgMS4wNjcxLTEyLjUgMS4wNjcxLTEuNTcxIDAuMTM0MS0yLjAwMDktMi4zMjUtMi41OTE2LTMuNTA2Mi0wLjA5NjctMC4xOTM0My03LjA2MDgtMS45MzM0LTcuNjIyMi0xLjM3Mi0yLjg5MzEgMi44OTMxLTcuNjMxNyA0LjI0ODctMTIuMTk2IDQuMTE2bC0xMTIuMDUtMy4yNTc4IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTM5OS44MiA0NzkuNjEgMTEuNjQyIDUuNjA1M2MyLjk4NDEgMS40MzY4IDYuNTI4OC0wLjQ3NzEyIDkuOTE3MS0wLjQzMTE4bDEyNy4yIDEuNzI0NyIgY29sb3I9IiMwMDAwMDAiLz4KICAgIDxwYXRoIGQ9Im01MTkuMjUgNTE3LjEyLTAuNDMxMTktMjA4LjY5IiBjb2xvcj0iIzAwMDAwMCIvPgogICAgPHBhdGggZD0ibTQzMi45MyAzODkuNzFjMTEuMDQ1IDAgMzUuNTMzIDAuNjE5MjcgNDIuNTgtMS4wMDQgOC40MDUyLTEuOTM2MiA3LjA2Ni02Ljk1MzggMTQuMTk3LTYuOTUzOCA3LjgwOTUgMCA2LjU0MjkgOC4wNjI0IDIwLjE0MiA4LjA2MjQgMTMuOTkxIDAgNDQuOTc3IDAuMzc4ODYgNjMuOTQgMC4zNzg4NiAxMi4wODQgMCA4Mi4wMDMgMC4zMDQ4OSA5My42MDEgMC4zMDQ4OSA4Ljc2MDUgMCAxMy4xNi0yLjI4ODMgMjEuMzQyLTcuMDEyNCA3LjE5NTItNC4xNTQxIDIuMDU0Ni05LjQ5MTQgMjAuNDI4LTguODQxOCAyMy4xNDUgMC44MTgzMyAxMi42NDMgMTQuMDI1IDMyLjMxOCAxNC4wMjVoMTUwLjkyYzE0LjMzMiAwLTQuMTE5MS0xMy4xMSAyOS4yNjktMTMuNDE1IiBjb2xvcj0iIzAwMDAwMCIvPgogICA8L2c+CiAgPC9nPgogIDxnIGZvbnQtZmFtaWx5PSJWZXJkYW5hIiBsZXR0ZXItc3BhY2luZz0iMHB4IiB3b3JkLXNwYWNpbmc9IjBweCI+CiAgIDx0ZXh0IHg9IjU4OC42Nzk1NyIgeT0iNzM1LjgwNDYzIiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI1ODguNjc5NTciIHk9IjczNS44MDQ2MyIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPkxpbmNvbG48L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iNjg2LjM5ODUiIHk9Ijc2NS42Mjg0MiIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNjg2LjM5ODUiIHk9Ijc2NS42Mjg0MiIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPkhhcnJ5PC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDkwKSIgeD0iNzA5Ljg3MTgzIiB5PSItODAyLjM3NzM4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI3MDkuODcxODMiIHk9Ii04MDIuMzc3MzgiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5Xb29kbGF3bjwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9IjU2Mi4xMTkyNiIgeT0iLTc3MS45NjgxNCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNTYyLjExOTI2IiB5PSItNzcxLjk2ODE0IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+RWRnZW1vb3I8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI1OTguMzA0ODciIHk9Ii03MzguMzY2NDYiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjU5OC4zMDQ4NyIgeT0iLTczOC4zNjY0NiIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPk9saXZlcjwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9IjU5Mi4xMjI4NiIgeT0iLTY3Ny4yMDM5OCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNTkyLjEyMjg2IiB5PSItNjc3LjIwMzk4IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+SGlsbHNpZGU8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI1OTcuMzI3MDkiIHk9Ii04NjIuNjE0MDciIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjU5Ny4zMjcwOSIgeT0iLTg2Mi42MTQwNyIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPlJvY2s8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI1ODcuMzcwMTgiIHk9Ii05MjYuMTM2NiIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNTg3LjM3MDE4IiB5PSItOTI2LjEzNjYiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5XZWJiPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHg9Ijg3MS4xNjEwMSIgeT0iNjM3LjU3NTIiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9Ijg3MS4xNjEwMSIgeT0iNjM3LjU3NTIiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5DZW50cmFsPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHg9Ijg3My44MzIyOCIgeT0iNTc3LjAzMjQ3IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI4NzMuODMyMjgiIHk9IjU3Ny4wMzI0NyIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPjEzdGg8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iODc1Ljk2NjQ5IiB5PSI1MTAuMjYxODEiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9Ijg3NS45NjY0OSIgeT0iNTEwLjI2MTgxIiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+MjFzdDwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSI4ODEuMzE2NTkiIHk9IjQ1MC4xOTg3NiIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iODgxLjMxNjU5IiB5PSI0NTAuMTk4NzYiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij4yOXRoPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHg9IjYxNS43OTI0OCIgeT0iMzg3Ljc0NzE2IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI2MTUuNzkyNDgiIHk9IjM4Ny43NDcxNiIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPjM3dGg8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iNDg0LjY5MDM3IiB5PSI0ODEuNjUyODYiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjQ4NC42OTAzNyIgeT0iNDgxLjY1Mjg2IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+MjV0aDwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSI1NjMuMDQ2NzUiIHk9IjUxMy4zNjEzMyIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNTYzLjA0Njc1IiB5PSI1MTMuMzYxMzMiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij4yMXN0PC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHg9IjU2NS45NzE1IiB5PSI1NzcuODk0ODQiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjU2NS45NzE1IiB5PSI1NzcuODk0ODQiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij4xM3RoPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDkwKSIgeD0iNDMzLjU4MDc1IiB5PSItNDYwLjczMzEyIiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI0MzMuNTgwNzUiIHk9Ii00NjAuNzMzMTIiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5BbWlkb248L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI0MDUuNTMwOTgiIHk9Ii01MjMuNTQwMTYiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjQwNS41MzA5OCIgeT0iLTUyMy41NDAxNiIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPkFya2Fuc2FzPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDkwKSIgeD0iNzQ1LjQ4NDYyIiB5PSItMzcyLjU4NTk0IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI3NDUuNDg0NjIiIHk9Ii0zNzIuNTg1OTQiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5XZXN0PC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDkwKSIgeD0iNTk2LjcyODMzIiB5PSItNTMxLjI1OTI4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI1OTYuNzI4MzMiIHk9Ii01MzEuMjU5MjgiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5XYWNvPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDkwKSIgeD0iNTk1LjQzNDgxIiB5PSItMTIyLjUwMjk1IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI1OTUuNDM0ODEiIHk9Ii0xMjIuNTAyOTUiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5NYXppZTwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg0NSkiIHg9IjY5NS43NzI5NSIgeT0iMTYyLjA2ODc3IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI2OTUuNzcyOTUiIHk9IjE2Mi4wNjg3NyIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPlpvbzwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSIyNDAuNTg5OTciIHk9IjU3NC40NDU0MyIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iMjQwLjU4OTk3IiB5PSI1NzQuNDQ1NDMiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij4xM3RoPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHg9IjIwNi4wMzE3NSIgeT0iNTExLjYzNjYzIiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSIyMDYuMDMxNzUiIHk9IjUxMS42MzY2MyIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPjIxc3Q8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI2MjAuNDQzMTIiIHk9Ii01MDYuNjgyMTkiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjYyMC40NDMxMiIgeT0iLTUwNi42ODIxOSIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPk5pbXM8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iMzcwLjIxNjg2IiB5PSI2OTguODQwMDkiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjM3MC4yMTY4NiIgeT0iNjk4Ljg0MDA5IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+TWFwbGU8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iMzg0LjA4NDIiIHk9IjY4MC44NTEzOCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iMzg0LjA4NDIiIHk9IjY4MC44NTEzOCIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPkRvdWdsYXM8L3RzcGFuPjwvdGV4dD4KICA8L2c+CiAgPHBhdGggZD0ibTM2Ny45MSAxMDEwaDI2My4wMiIgY29sb3I9IiMwMDAwMDAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzMzNiIgc3Ryb2tlLXdpZHRoPSIxcHgiLz4KICA8ZyBmb250LWZhbWlseT0iVmVyZGFuYSIgbGV0dGVyLXNwYWNpbmc9IjBweCIgd29yZC1zcGFjaW5nPSIwcHgiPgogICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9IjczNi4yNjc0NiIgeT0iLTQzMy4xMzc3NiIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNzM2LjI2NzQ2IiB5PSItNDMzLjEzNzc2IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+TWVyaWRpYW48L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgeD0iNTcyLjgzMjE1IiB5PSI2NDAuMjA1MjYiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjU3Mi44MzIxNSIgeT0iNjQwLjIwNTI2IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+Q2VudHJhbDwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSI1NzUuMDg5NjYiIHk9IjY3MC45MDM1IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI1NzUuMDg5NjYiIHk9IjY3MC45MDM1IiBmb250LXNpemU9IjkuNjU4NHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MS4yNSI+RG91Z2xhczwvdHNwYW4+PC90ZXh0PgogICA8dGV4dCB4PSI0OTkuNDg5NjIiIHk9IjEwMDguNjA2OSIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iNDk5LjQ4OTYyIiB5PSIxMDA4LjYwNjkiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij40N3RoPC90c3Bhbj48L3RleHQ+CiAgIDx0ZXh0IHg9IjIxNi42NDU0MyIgeT0iNzI1Ljk4Mjk3IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSIyMTYuNjQ1NDMiIHk9IjcyNS45ODI5NyIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPktlbGxvZ2c8L3RzcGFuPjwvdGV4dD4KICAgPHRleHQgdHJhbnNmb3JtPSJyb3RhdGUoOTApIiB4PSI3NzQuODc1NjEiIHk9Ii01MDguMTg5NzMiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9Ijc3NC44NzU2MSIgeT0iLTUwOC4xODk3MyIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPk1jQ2xlYW48L3RzcGFuPjwvdGV4dD4KICA8L2c+CiAgPHBhdGggdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAyODcuMzYpIiBkPSJtMzY0LjE2IDY1OC40MyAyOTkuNTEtMS4wMTAyYzYuNDk4Ny0wLjAyMTkgNi45NzcyIDkuMjU0MSAxNi41OTYgOS4zOTI1IDEyLjA1NCAwLjE3MzM5IDI5LjExMS0wLjUzNTcyIDU0LjExNC0wLjMwMTEiIGNvbG9yPSIjMDAwMDAwIiBmaWxsPSJub25lIiBzdHJva2U9IiMzMzYiIHN0cm9rZS13aWR0aD0iMXB4Ii8+CiAgPHRleHQgeD0iMzczLjk5MzA0IiB5PSI5NDQuMzU3NTQiIGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJWZXJkYW5hIiBsZXR0ZXItc3BhY2luZz0iMHB4IiB3b3JkLXNwYWNpbmc9IjBweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjAlIiB4bWw6c3BhY2U9InByZXNlcnZlIj48dHNwYW4geD0iMzczLjk5MzA0IiB5PSI5NDQuMzU3NTQiIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5NYWNBcnRodXI8L3RzcGFuPjwvdGV4dD4KICA8dGV4dCB0cmFuc2Zvcm09InJvdGF0ZSg5MCkiIHg9Ijc4MC44NDYwNyIgeT0iLTQ5MC4yNDU5NyIgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9IlZlcmRhbmEiIGxldHRlci1zcGFjaW5nPSIwcHgiIHdvcmQtc3BhY2luZz0iMHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI3ODAuODQ2MDciIHk9Ii00OTAuMjQ1OTciIGZvbnQtc2l6ZT0iOS42NTg0cHgiIHN0eWxlPSJsaW5lLWhlaWdodDoxLjI1Ij5TZW5lY2E8L3RzcGFuPjwvdGV4dD4KICA8cGF0aCB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDI4Ny4zNikiIGQ9Im0zNjcuNyA1MzcuMjEgMTQxLjI4LTEuMDEwMmM2LjQ5LTAuMDQ2NCAxMi43ODEgNy4yMzU0IDE5LjE5MyA3LjMyMzYgNTUuOTI0IDAuNzY4OSAxNTguNjktMC4xNzMzMyAyMzYuNTEtMS4wMTAyIDcuODM5Ni0wLjA4NDMgMjIuNjMxLTE5Ljg1NCAzMC4zMDUtMjAuNDU2IDIyLjI2Ni0xLjM1MTggNDUuMTc5LTAuNTA1MDcgNjcuNjgtMC41MDUwNyAxNi4xNDctMC42MzI0MSAzLjYxMDIgMjAuNzA4IDI2Ljc2OSAyMC43MDhsMjQzLjQ1LTEuMDEwMiIgY29sb3I9IiMwMDAwMDAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzMzNiIgc3Ryb2tlLXdpZHRoPSIxcHgiLz4KICA8dGV4dCB4PSI2ODUuMjA4MTMiIHk9IjgyNy41MzA4MiIgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9IlZlcmRhbmEiIGxldHRlci1zcGFjaW5nPSIwcHgiIHdvcmQtc3BhY2luZz0iMHB4IiBzdHlsZT0ibGluZS1oZWlnaHQ6MCUiIHhtbDpzcGFjZT0icHJlc2VydmUiPjx0c3BhbiB4PSI2ODUuMjA4MTMiIHk9IjgyNy41MzA4MiIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPlBhd25lZTwvdHNwYW4+PC90ZXh0PgogIDxwYXRoIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgMjg3LjM2KSIgZD0ibTU1NC4yOSA3MjEuNDMtNC4yODU3LTE3OC4yMS0yLjg1NzEtNDQwLjcxLTAuMzU3MTQtNzkuMjg2IiBjb2xvcj0iIzAwMDAwMCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMzM2IiBzdHJva2Utd2lkdGg9IjFweCIvPgogIDx0ZXh0IHRyYW5zZm9ybT0icm90YXRlKDkwKSIgeD0iNTI5LjYyNTMxIiB5PSItNTUwLjg0Nzc4IiBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0iVmVyZGFuYSIgbGV0dGVyLXNwYWNpbmc9IjBweCIgd29yZC1zcGFjaW5nPSIwcHgiIHN0eWxlPSJsaW5lLWhlaWdodDowJSIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHRzcGFuIHg9IjUyOS42MjUzMSIgeT0iLTU1MC44NDc3OCIgZm9udC1zaXplPSI5LjY1ODRweCIgc3R5bGU9ImxpbmUtaGVpZ2h0OjEuMjUiPkJyb2Fkd2F5PC90c3Bhbj48L3RleHQ+CiA8L2c+Cjwvc3ZnPgo=\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"showTooltip\":true,\"autocloseTooltip\":true,\"showTooltipAction\":\"click\",\"defaultCenterPosition\":\"0,0\",\"provider\":\"image-map\",\"fitMapBounds\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"polygonKeyName\":\"coordinates\",\"polygonOpacity\":0.5,\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"mapProvider\":\"HERE.normalDay\",\"draggableMarker\":true,\"editablePolygon\":true,\"mapPageSize\":16384,\"showPolygon\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${coordinates|ts:7}

Delete\"},\"title\":\"Markers Placement - Image Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"tooltipAction\":[{\"name\":\"delete\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id;\\n });\\n\\nwidgetContext.map.setMarkerLocation(entityDatasource[0], null, null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"c39f512a-21c6-6b06-3aa1-715262c6553d\"},{\"name\":\"delete_polygon\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id\\n });\\n\\nwidgetContext.map.savePolygonLocation(entityDatasource[0], null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"94bf5ffd-b526-c6c3-ae3b-ab42191217d9\"}]},\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"displayTimewindow\":true}" + }, + "externalId": null, + "tags": [ + "building", + "interior", + "venue", + "inside", + "room", + "office", + "manufacturing", + "floor", + "plant", + "storage", + "warehouse", + "depot" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/markers_placement___openstreetmap.json b/application/src/main/data/json/system/widget_types/markers_placement___openstreetmap.json new file mode 100644 index 0000000000..93693b18e0 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/markers_placement___openstreetmap.json @@ -0,0 +1,29 @@ +{ + "fqn": "input_widgets.markers_placement_openstreetmap", + "name": "Markers Placement - OpenStreetMap", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Allows configuring the location of the selected entities on OpenStreetMap. By default, store the location using 'latitude' and 'longitude' server-side attributes.", + "descriptor": { + "type": "latest", + "sizeX": 8.5, + "sizeY": 6.5, + "resources": [], + "templateHtml": "", + "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('openstreet-map', false, self.ctx, null, true);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-map-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]},{\"type\":\"function\",\"name\":\"Second point\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#607d8b\",\"settings\":{},\"_hash\":0.7867521952070078,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.7040053227577256,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"fitMapBounds\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}

Delete\",\"markerImageSize\":34,\"useColorFunction\":false,\"markerImages\":[],\"useMarkerImageFunction\":false,\"color\":\"#fe7569\",\"mapProvider\":\"OpenStreetMap.Mapnik\",\"showTooltip\":true,\"autocloseTooltip\":true,\"defaultCenterPosition\":\"0,0\",\"customProviderTileUrl\":\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\"showTooltipAction\":\"click\",\"polygonKeyName\":\"coordinates\",\"polygonOpacity\":0.5,\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"zoomOnClick\":true,\"showCoverageOnHover\":true,\"animate\":true,\"maxClusterRadius\":80,\"removeOutsideVisibleBounds\":true,\"defaultZoomLevel\":5,\"provider\":\"openstreet-map\",\"draggableMarker\":true,\"editablePolygon\":true,\"mapPageSize\":16384,\"showPolygon\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${coordinates|ts:7}

Delete\"},\"title\":\"Markers Placement - OpenStreetMap\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"tooltipAction\":[{\"name\":\"delete\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id;\\n });\\n\\nwidgetContext.map.setMarkerLocation(entityDatasource[0], null, null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"54c293c4-9ca6-e34f-dc6a-0271944c1c66\"},{\"name\":\"delete_polygon\",\"icon\":\"more_horiz\",\"type\":\"custom\",\"customFunction\":\"var entityDatasource = widgetContext.map.map.datasources.filter(\\n function(entity) {\\n return entity.entityId === entityId.id\\n });\\n\\nwidgetContext.map.savePolygonLocation(entityDatasource[0], null).subscribe(() => widgetContext.updateAliases());\",\"id\":\"6beb7bed-dfd8-388d-b60c-82988ab52f06\"}]},\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"displayTimewindow\":true}" + }, + "externalId": null, + "tags": [ + "mapping", + "gps", + "navigation", + "geolocation", + "satellite", + "directions" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/mini_gauge.json b/application/src/main/data/json/system/widget_types/mini_gauge.json new file mode 100644 index 0000000000..2789102c6c --- /dev/null +++ b/application/src/main/data/json/system/widget_types/mini_gauge.json @@ -0,0 +1,21 @@ +{ + "fqn": "digital_gauges.mini_gauge_justgage", + "name": "Mini gauge", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Preconfigured gauge to display any value reading as a circle. Allows to configure value range, gradient colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 2, + "sizeY": 2, + "resources": [], + "templateHtml": "", + "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-digital-gauge-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#7cb342\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":0,\"decimals\":0,\"roundedLineCap\":true,\"gaugeType\":\"donut\"},\"title\":\"Mini gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/navigation_card.json b/application/src/main/data/json/system/widget_types/navigation_card.json new file mode 100644 index 0000000000..ab3811c47c --- /dev/null +++ b/application/src/main/data/json/system/widget_types/navigation_card.json @@ -0,0 +1,21 @@ +{ + "fqn": "navigation_widgets.navigation_card", + "name": "Navigation card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the link to the platform page as a button. The link is configured as the relative path in the appearance settings of the widget.", + "descriptor": { + "type": "static", + "sizeX": 2.5, + "sizeY": 2, + "resources": [], + "templateHtml": "", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n\n}\n\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-navigation-card-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(255,255,255,0)\",\"color\":\"rgba(255,255,255,0.87)\",\"padding\":\"8px\",\"settings\":{\"name\":\"{i18n:device.devices}\",\"icon\":\"devices_other\",\"path\":\"/devices\"},\"title\":\"Navigation card\",\"dropShadow\":false,\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/navigation_cards.json b/application/src/main/data/json/system/widget_types/navigation_cards.json new file mode 100644 index 0000000000..52be3671b8 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/navigation_cards.json @@ -0,0 +1,21 @@ +{ + "fqn": "navigation_widgets.navigation_cards", + "name": "Navigation cards", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays multiple links to the platform page as separate buttons. The link is configured as the relative path in the appearance settings of the widget.", + "descriptor": { + "type": "static", + "sizeX": 7, + "sizeY": 6, + "resources": [], + "templateHtml": "", + "templateCss": "/*#widget-container {\n overflow-y: auto;\n box-sizing: content-box !important;\n cursor: auto;\n}*/\n\n#widget-container #container {\n overflow-y: auto;\n box-sizing: content-box;\n cursor: auto;\n}", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.navigationCardsWidget.resize();\n}\n\nself.onResize = function() {\n self.ctx.$scope.navigationCardsWidget.resize();\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-navigation-cards-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(255,255,255,0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"filterType\":\"all\"},\"title\":\"Navigation cards\",\"dropShadow\":false,\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/neon_gauge.json b/application/src/main/data/json/system/widget_types/neon_gauge.json new file mode 100644 index 0000000000..4f595392b6 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/neon_gauge.json @@ -0,0 +1,21 @@ +{ + "fqn": "digital_gauges.neon_gauge_justgage", + "name": "Neon gauge", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Preconfigured gauge to display any value reading as an arc. Allows to configure value range, gradient colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 3, + "resources": [], + "templateHtml": "", + "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-digital-gauge-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":70,\"dashThickness\":1,\"gaugeType\":\"arc\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Neon gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/nitrogen_dioxide__no2__card.json b/application/src/main/data/json/system/widget_types/nitrogen_dioxide__no2__card.json new file mode 100644 index 0000000000..40c5ea4b4d --- /dev/null +++ b/application/src/main/data/json/system/widget_types/nitrogen_dioxide__no2__card.json @@ -0,0 +1,37 @@ +{ + "fqn": "nitrogen_dioxide_no2_card", + "name": "Nitrogen dioxide (NO2) card", + "deprecated": false, + "image": "tb-image:Tk8yLXZhbHVlLWNhcmQuc3Zn:Tk8yLXZhbHVlLWNhcmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMTIiIHJ4PSIzLjUiLz48cGF0aCBmaWxsPSIjM0ZBNzFBIiBkPSJNMzQuNTI4IDI0LjI1NWMtNy4yMiAwLTEzLjA3OSA1Ljg1OS0xMy4wNzkgMTMuMDc4IDAgNy4yMiA1Ljg2IDEzLjA3OSAxMy4wNzkgMTMuMDc5IDcuMjIgMCAxMy4wNzgtNS44NiAxMy4wNzgtMTMuMDc5IDAtNy4yMi01Ljg1OS0xMy4wNzgtMTMuMDc4LTEzLjA3OFpNMjQuMDY1IDM3LjMzM2MwLS43OTcuMTA0LTEuNTgyLjI3NC0yLjMyOGw2LjI1MiA2LjI1MnYxLjMwOGEyLjYyMyAyLjYyMyAwIDAgMCAyLjYxNiAyLjYxNnYyLjUyNGMtNS4xNC0uNjU0LTkuMTQyLTUuMDQ5LTkuMTQyLTEwLjM3MlptMTguMTY2IDcuMDYzYy0uMzQtMS4wNi0xLjMwOC0xLjgzMS0yLjQ4NS0xLjgzMWgtMS4zMDhWMzguNjRjMC0uNzItLjU4OC0xLjMwOC0xLjMwOC0xLjMwOGgtNy44NDd2LTIuNjE1SDMxLjljLjcyIDAgMS4zMDgtLjU4OSAxLjMwOC0xLjMwOHYtMi42MTZoMi42MTVhMi42MjMgMi42MjMgMCAwIDAgMi42MTYtMi42MTZ2LS41MzZjMy44MzIgMS41NDMgNi41NTMgNS4zMSA2LjU1MyA5LjY5MSAwIDIuNzItMS4wNiA1LjIwNi0yLjc2IDcuMDYzWiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjg3IiBkPSJNNjQuNzcxIDI1LjU5MXY5LjI0MmgtMS41OTNsLTQuMTQ1LTYuNjJ2Ni42Mkg1Ny40NHYtOS4yNDJoMS41OTNsNC4xNTcgNi42MzN2LTYuNjMzaDEuNTgxWm0zLjYzMyAyLjM3NHY2Ljg2OGgtMS41MzZ2LTYuODY4aDEuNTM2Wm0tMS42MzctMS44MDJjMC0uMjMzLjA3Ni0uNDI2LjIyOC0uNTc4LjE1Ny0uMTU3LjM3My0uMjM1LjY0OC0uMjM1LjI3IDAgLjQ4NC4wNzguNjQuMjM1YS43NzIuNzcyIDAgMCAxIC4yMzYuNTc4Ljc2NC43NjQgMCAwIDEtLjIzNS41N2MtLjE1Ny4xNTMtLjM3LjIzLS42NDEuMjMtLjI3NSAwLS40OTEtLjA3Ny0uNjQ4LS4yM2EuNzc1Ljc3NSAwIDAgMS0uMjI4LS41N1ptNi43MTIgMS44MDJ2MS4xMTdoLTMuODczdi0xLjExN2gzLjg3M1ptLTIuNzU1LTEuNjgyaDEuNTN2Ni42NTJjMCAuMjEyLjAyOS4zNzUuMDg4LjQ5LjA2NC4xMS4xNS4xODMuMjYuMjIxLjExLjAzOC4yNC4wNTcuMzg4LjA1N2EzLjAwMyAzLjAwMyAwIDAgMCAuNTQtLjA1N2wuMDA2IDEuMTY4YTMuODkgMy44OSAwIDAgMS0uNDQ1LjEwMmMtLjE2NS4wMy0uMzU1LjA0NC0uNTcxLjA0NC0uMzUxIDAtLjY2Mi0uMDYxLS45MzMtLjE4NGExLjM3MyAxLjM3MyAwIDAgMS0uNjM1LS42MTZjLS4xNTItLjI4My0uMjI4LS42Ni0uMjI4LTEuMTN2LTYuNzQ3Wm01LjcyMSAyLjk5djUuNTZoLTEuNTN2LTYuODY4aDEuNDZsLjA3IDEuMzA4Wm0yLjEwMS0xLjM1Mi0uMDEyIDEuNDIyYTMuOTYyIDMuOTYyIDAgMCAwLS42MjItLjA1IDEuOTMgMS45MyAwIDAgMC0uNjkyLjExMyAxLjMwNSAxLjMwNSAwIDAgMC0uNTAyLjMxOGMtLjEzLjEzNS0uMjMzLjMtLjMwNC40OTVhMi4yMTQgMi4yMTQgMCAwIDAtLjEyNy42NTNsLS4zNS4wMjZjMC0uNDMyLjA0My0uODMyLjEyNy0xLjIuMDg1LS4zNjguMjEyLS42OTIuMzgxLS45NzEuMTc0LS4yOC4zOS0uNDk3LjY0OC0uNjU0LjI2Mi0uMTU3LjU2NS0uMjM1LjkwNy0uMjM1LjA5NCAwIC4xOTMuMDA5LjI5OS4wMjYuMTEuMDE3LjE5Mi4wMzYuMjQ3LjA1N1ptLjc1MiAzLjU1NXYtLjE0NmMwLS40OTYuMDcyLS45NTUuMjE2LTEuMzc4LjE0NC0uNDI3LjM1LS43OTguNjIyLTEuMTEuMjc1LS4zMTguNjEtLjU2NCAxLjAwMy0uNzM3YTMuMjYzIDMuMjYzIDAgMCAxIDEuMzQ1LS4yNjdjLjUwNCAwIC45NTMuMDkgMS4zNDYuMjY3LjM5OC4xNzMuNzM0LjQxOSAxLjAxLjczNi4yNzQuMzEzLjQ4NC42ODQuNjI4IDEuMTExLjE0NC40MjMuMjE2Ljg4Mi4yMTYgMS4zNzd2LjE0NmMwIC40OTYtLjA3Mi45NTUtLjIxNiAxLjM3OGEzLjI5NiAzLjI5NiAwIDAgMS0uNjI5IDEuMTFjLS4yNzUuMzE0LS42MDkuNTYtMS4wMDMuNzM3LS4zOTMuMTc0LS44NC4yNi0xLjMzOS4yNmEzLjM1IDMuMzUgMCAwIDEtMS4zNTItLjI2IDIuOTEzIDIuOTEzIDAgMCAxLTEuMDAzLS43MzYgMy4yOTQgMy4yOTQgMCAwIDEtLjYyOC0xLjExMSA0LjI1MyA0LjI1MyAwIDAgMS0uMjE2LTEuMzc4Wm0xLjUzLS4xNDZ2LjE0NmMwIC4zMDguMDMxLjYuMDk1Ljg3NS4wNjMuMjc2LjE2My41MTcuMjk4LjcyNC4xMzYuMjA3LjMxLjM3LjUyLjQ4OS4yMTIuMTE4LjQ2NC4xNzguNzU2LjE3OC4yODQgMCAuNTI5LS4wNi43MzYtLjE3OC4yMTItLjExOS4zODUtLjI4Mi41Mi0uNDg5LjEzNi0uMjA3LjIzNi0uNDQ5LjMtLjcyNC4wNjctLjI3NS4xLS41NjcuMS0uODc1di0uMTQ2YzAtLjMwNS0uMDMzLS41OTMtLjEtLjg2NGEyLjIxNyAyLjIxNyAwIDAgMC0uMzA1LS43MyAxLjQ1OCAxLjQ1OCAwIDAgMC0uNTItLjQ5NSAxLjQzMyAxLjQzMyAwIDAgMC0uNzQ0LS4xODRjLS4yODcgMC0uNTM3LjA2MS0uNzQ5LjE4NGExLjQ3MyAxLjQ3MyAwIDAgMC0uNTE0LjQ5NSAyLjMwMSAyLjMwMSAwIDAgMC0uMjk4LjczYy0uMDY0LjI3MS0uMDk1LjU1OS0uMDk1Ljg2M1ptMTAuNzg3LTMuMzY1aDEuMzl2Ni42NzhjMCAuNjE4LS4xMzEgMS4xNDItLjM5NCAxLjU3NC0uMjYyLjQzMi0uNjI4Ljc2LTEuMDk4Ljk4NC0uNDcuMjI5LTEuMDEzLjM0My0xLjYzMS4zNDMtLjI2MyAwLS41NTUtLjAzOC0uODc2LS4xMTRhMy41OSAzLjU5IDAgMCAxLS45MjctLjM2OSAyLjI5OCAyLjI5OCAwIDAgMS0uNzQzLS42NTNsLjcxOC0uOTAyYy4yNDUuMjkyLjUxNi41MDYuODEyLjY0MS4yOTYuMTM2LjYwOC4yMDMuOTMzLjIwMy4zNTIgMCAuNjUtLjA2NS44OTUtLjE5NmExLjMzIDEuMzMgMCAwIDAgLjU3OC0uNTY1Yy4xMzUtLjI1LjIwMy0uNTU1LjIwMy0uOTE0VjI5LjUybC4xNC0xLjU1NVptLTQuNjY2IDMuNTF2LS4xMzNjMC0uNTIuMDY0LS45OTQuMTktMS40MjIuMTI4LS40MzEuMzEtLjgwMi41NDctMS4xMS4yMzYtLjMxNC41MjQtLjU1My44NjMtLjcxOC4zMzgtLjE3LjcyMS0uMjU0IDEuMTQ5LS4yNTQuNDQ0IDAgLjgyMy4wOCAxLjEzNi4yNDEuMzE3LjE2MS41ODIuMzkyLjc5My42OTIuMjEyLjI5Ny4zNzcuNjUyLjQ5NiAxLjA2Ny4xMjIuNDEuMjEzLjg2Ny4yNzIgMS4zN3YuNDI2Yy0uMDU0LjQ5MS0uMTQ4Ljk0LS4yNzkgMS4zNDZhMy43MTUgMy43MTUgMCAwIDEtLjUyIDEuMDU0IDIuMjIxIDIuMjIxIDAgMCAxLS44LjY4NWMtLjMxMy4xNi0uNjg0LjI0MS0xLjExLjI0MS0uNDIgMC0uNzk5LS4wODYtMS4xMzctLjI2YTIuNTg4IDIuNTg4IDAgMCAxLS44NjQtLjczIDMuNDYgMy40NiAwIDAgMS0uNTQ1LTEuMTA0IDQuODY1IDQuODY1IDAgMCAxLS4xOS0xLjM5Wm0xLjUzLS4xMzN2LjEzNGMwIC4zMTMuMDMuNjA1LjA4OS44NzUuMDYzLjI3MS4xNTkuNTEuMjg1LjcxOC4xMzIuMjAzLjI5Ny4zNjQuNDk2LjQ4Mi4yMDMuMTE0LjQ0Mi4xNzEuNzE3LjE3MS4zNiAwIC42NTQtLjA3Ni44ODItLjIyOC4yMzMtLjE1Mi40MS0uMzU4LjUzMy0uNjE2LjEyNy0uMjYyLjIxNi0uNTU0LjI2Ny0uODc2di0xLjE0OWEyLjggMi44IDAgMCAwLS4xNTktLjY5OCAxLjg2OCAxLjg2OCAwIDAgMC0uMzEtLjU2NSAxLjI5OCAxLjI5OCAwIDAgMC0uNDk2LS4zOCAxLjY0OCAxLjY0OCAwIDAgMC0uNzA0LS4xNGMtLjI3NSAwLS41MTQuMDU5LS43MTguMTc3LS4yMDMuMTE5LS4zNy4yODItLjUwMS40OWEyLjQxNyAyLjQxNyAwIDAgMC0uMjg2LjcyM2MtLjA2My4yNzUtLjA5NS41NjktLjA5NS44ODJabTkuMzkgMy42MThjLS41MDcgMC0uOTY2LS4wODItMS4zNzctLjI0N2EzLjA0MSAzLjA0MSAwIDAgMS0xLjA0LS43MDUgMy4xMjIgMy4xMjIgMCAwIDEtLjY1NS0xLjA2IDMuNzEgMy43MSAwIDAgMS0uMjI4LTEuMzE0di0uMjU0YzAtLjUzNy4wNzgtMS4wMjQuMjM1LTEuNDYuMTU2LS40MzYuMzc0LS44MDguNjUzLTEuMTE3LjI4LS4zMTMuNjEtLjU1Mi45OS0uNzE3YTMuMDggMy4wOCAwIDAgMSAxLjIzOS0uMjQ4Yy40OSAwIC45Mi4wODMgMS4yODguMjQ4LjM2OC4xNjUuNjczLjM5OC45MTQuNjk4LjI0Ni4yOTYuNDI4LjY1LjU0NiAxLjA2LjEyMy40MS4xODQuODYzLjE4NCAxLjM1OHYuNjU0aC01LjMwNnYtMS4wOThoMy43OTV2LS4xMmEyLjE0OCAyLjE0OCAwIDAgMC0uMTY1LS43NzUgMS4zMTYgMS4zMTYgMCAwIDAtLjQ1LS41ODRjLS4yMDMtLjE0OC0uNDc0LS4yMjItLjgxMy0uMjIyLS4yNTQgMC0uNDguMDU1LS42NzkuMTY1LS4xOTUuMTA2LS4zNTguMjYtLjQ4OS40NjNhMi40NCAyLjQ0IDAgMCAwLS4zMDQuNzM3IDQuMTM4IDQuMTM4IDAgMCAwLS4xMDIuOTU4di4yNTRjMCAuMy4wNC41OC4xMi44MzguMDg1LjI1NC4yMDguNDc2LjM2OS42NjYuMTYuMTkxLjM1NS4zNDEuNTg0LjQ1MS4yMjguMTA2LjQ4OS4xNTkuNzguMTU5LjM2OSAwIC42OTctLjA3NC45ODQtLjIyMi4yODgtLjE0OC41MzgtLjM1OC43NS0uNjI5bC44MDYuNzgxYTMuMTcgMy4xNyAwIDAgMS0uNTc4LjYyMiAyLjg5MyAyLjg5MyAwIDAgMS0uODcuNDc2IDMuNDU3IDMuNDU3IDAgMCAxLTEuMTguMTg0Wm01LjcxLTUuNTI5djUuNDAyaC0xLjUzdi02Ljg2OGgxLjQ0MWwuMDg5IDEuNDY2Wm0tLjI3MyAxLjcxNC0uNDk1LS4wMDZjLjAwNC0uNDg3LjA3MS0uOTMzLjIwMy0xLjM0LjEzNS0uNDA2LjMyMS0uNzU1LjU1OC0xLjA0Ny4yNDEtLjI5Mi41MjktLjUxNi44NjQtLjY3MmEyLjU0IDIuNTQgMCAwIDEgMS4xMTctLjI0MmMuMzMgMCAuNjI4LjA0Ny44OTUuMTQuMjcxLjA4OS41MDEuMjM1LjY5Mi40MzguMTk0LjIwMy4zNDIuNDY4LjQ0NC43OTMuMTAyLjMyMi4xNTIuNzE4LjE1MiAxLjE4N3Y0LjQzN0gxMDYuMlYzMC4zOWMwLS4zMy0uMDQ4LS41OS0uMTQ2LS43OGEuODE1LjgxNSAwIDAgMC0uNDEyLS40MTMgMS41NDggMS41NDggMCAwIDAtLjY2Ny0uMTI3Yy0uMjYyIDAtLjQ5Ny4wNTUtLjcwNC4xNjUtLjIwOC4xMS0uMzgzLjI2LS41MjcuNDUtLjE0LjE5LS4yNDguNDEtLjMyNC42Ni0uMDc2LjI1LS4xMTQuNTE3LS4xMTQuOFptMTMuOTE5IDIuMjY2di04LjMyOGgxLjUzNnY5Ljc1aC0xLjM5bC0uMTQ2LTEuNDIyWm0tNC40NjktMS45MzZ2LS4xMzNjMC0uNTIuMDYxLS45OTQuMTg0LTEuNDIyLjEyMy0uNDMxLjMtLjgwMi41MzMtMS4xMWEyLjM0IDIuMzQgMCAwIDEgLjg1MS0uNzE4Yy4zMzQtLjE3LjcxMS0uMjU0IDEuMTMtLjI1NC40MTQgMCAuNzc4LjA4IDEuMDkxLjI0MS4zMTQuMTYxLjU4LjM5Mi44LjY5Mi4yMi4yOTcuMzk2LjY1Mi41MjcgMS4wNjcuMTMxLjQxLjIyNC44NjcuMjc5IDEuMzd2LjQyNmMtLjA1NS40OTEtLjE0OC45NC0uMjc5IDEuMzQ2YTMuNjEzIDMuNjEzIDAgMCAxLS41MjcgMS4wNTQgMi4yOCAyLjI4IDAgMCAxLS44MDYuNjg1Yy0uMzEzLjE2LS42NzkuMjQxLTEuMDk4LjI0MS0uNDE1IDAtLjc4OS0uMDg2LTEuMTI0LS4yNmEyLjQ4NyAyLjQ4NyAwIDAgMS0uODQ0LS43MyAzLjQzOCAzLjQzOCAwIDAgMS0uNTMzLTEuMTA0IDUuMDEgNS4wMSAwIDAgMS0uMTg0LTEuMzlabTEuNTMtLjEzM3YuMTM0YzAgLjMxMy4wMjcuNjA1LjA4Mi44NzUuMDU5LjI3MS4xNS41MS4yNzMuNzE4LjEyMy4yMDMuMjgyLjM2NC40NzYuNDgyLjE5OS4xMTQuNDM2LjE3MS43MTEuMTcxLjM0NyAwIC42MzMtLjA3Ni44NTctLjIyOC4yMjQtLjE1Mi40LS4zNTguNTI3LS42MTZhMi44OCAyLjg4IDAgMCAwIC4yNjctLjg3NnYtMS4xNDlhMi44NSAyLjg1IDAgMCAwLS4xNTktLjY5OCAxLjg2OCAxLjg2OCAwIDAgMC0uMzExLS41NjUgMS4zMDYgMS4zMDYgMCAwIDAtLjQ4OS0uMzggMS41MyAxLjUzIDAgMCAwLS42NzktLjE0Yy0uMjc5IDAtLjUxNi4wNTktLjcxMS4xNzdhMS40NCAxLjQ0IDAgMCAwLS40ODMuNDkgMi40MTkgMi40MTkgMCAwIDAtLjI3Mi43MjNjLS4wNi4yNzUtLjA4OS41NjktLjA4OS44ODJabTcuOTYyLTMuMzc3djYuODY4aC0xLjUzNnYtNi44NjhoMS41MzZabS0xLjYzOC0xLjgwMmMwLS4yMzMuMDc3LS40MjYuMjI5LS41NzguMTU2LS4xNTcuMzcyLS4yMzUuNjQ3LS4yMzUuMjcxIDAgLjQ4NS4wNzguNjQxLjIzNWEuNzcuNzcgMCAwIDEgLjIzNS41NzguNzYzLjc2MyAwIDAgMS0uMjM1LjU3Yy0uMTU2LjE1My0uMzcuMjMtLjY0MS4yMy0uMjc1IDAtLjQ5MS0uMDc3LS42NDctLjIzYS43NzcuNzc3IDAgMCAxLS4yMjktLjU3Wm0zLjI3MiA1LjMxM3YtLjE0NmMwLS40OTYuMDcyLS45NTUuMjE2LTEuMzc4LjE0NC0uNDI3LjM1MS0uNzk4LjYyMi0xLjExYTIuODAzIDIuODAzIDAgMCAxIDEuMDAzLS43MzcgMy4yNTkgMy4yNTkgMCAwIDEgMS4zNDUtLjI2N2MuNTA0IDAgLjk1My4wOSAxLjM0Ni4yNjdhMi43OSAyLjc5IDAgMCAxIDEuMDA5LjczNmMuMjc1LjMxMy40ODUuNjg0LjYyOSAxLjExMS4xNDQuNDIzLjIxNi44ODIuMjE2IDEuMzc3di4xNDZjMCAuNDk2LS4wNzIuOTU1LS4yMTYgMS4zNzhhMy4yOTkgMy4yOTkgMCAwIDEtLjYyOSAxLjExIDIuOTA4IDIuOTA4IDAgMCAxLTEuMDAzLjczN2MtLjM5My4xNzQtLjg0LjI2LTEuMzM5LjI2YTMuMzUgMy4zNSAwIDAgMS0xLjM1Mi0uMjYgMi45MDggMi45MDggMCAwIDEtMS4wMDMtLjczNiAzLjI5NyAzLjI5NyAwIDAgMS0uNjI4LTEuMTExIDQuMjUgNC4yNSAwIDAgMS0uMjE2LTEuMzc4Wm0xLjUzLS4xNDZ2LjE0NmMwIC4zMDguMDMxLjYuMDk1Ljg3NS4wNjMuMjc2LjE2My41MTcuMjk4LjcyNC4xMzYuMjA3LjMwOS4zNy41MjEuNDg5LjIxMS4xMTguNDYzLjE3OC43NTUuMTc4LjI4NCAwIC41MjktLjA2LjczNi0uMTc4LjIxMi0uMTE5LjM4NS0uMjgyLjUyMS0uNDg5LjEzNS0uMjA3LjIzNS0uNDQ5LjI5OC0uNzI0LjA2OC0uMjc1LjEwMi0uNTY3LjEwMi0uODc1di0uMTQ2YzAtLjMwNS0uMDM0LS41OTMtLjEwMi0uODY0YTIuMjIgMi4yMiAwIDAgMC0uMzA0LS43MyAxLjQ2IDEuNDYgMCAwIDAtLjUyMS0uNDk1IDEuNDMyIDEuNDMyIDAgMCAwLS43NDMtLjE4NCAxLjQ3IDEuNDcgMCAwIDAtLjc0OS4xODQgMS40ODEgMS40ODEgMCAwIDAtLjUxNC40OTUgMi4zIDIuMyAwIDAgMC0uMjk4LjczYy0uMDY0LjI3MS0uMDk1LjU1OS0uMDk1Ljg2M1ptNy40MTYtMy4zNjUgMS4zMDggMi4yNzkgMS4zMzMtMi4yNzloMS42ODJsLTIuMDgyIDMuMzcgMi4xNjQgMy40OThoLTEuNjgybC0xLjM5Ni0yLjM3NC0xLjM5NyAyLjM3NGgtMS42ODhsMi4xNTgtMy40OTctMi4wNzYtMy4zN2gxLjY3NlptNy4yNTggMHY2Ljg2OGgtMS41MzZ2LTYuODY4aDEuNTM2Wm0tMS42MzgtMS44MDJhLjc4Ljc4IDAgMCAxIC4yMjktLjU3OGMuMTU2LS4xNTcuMzcyLS4yMzUuNjQ3LS4yMzUuMjcxIDAgLjQ4NS4wNzguNjQxLjIzNWEuNzcuNzcgMCAwIDEgLjIzNS41NzguNzYzLjc2MyAwIDAgMS0uMjM1LjU3Yy0uMTU2LjE1My0uMzcuMjMtLjY0MS4yMy0uMjc1IDAtLjQ5MS0uMDc3LS42NDctLjIzYS43NzMuNzczIDAgMCAxLS4yMjktLjU3Wm03Ljc1MyA3LjI0OHYtOC4zMjhoMS41MzZ2OS43NWgtMS4zOWwtLjE0Ni0xLjQyMlptLTQuNDY5LTEuOTM2di0uMTMzYzAtLjUyLjA2Mi0uOTk0LjE4NS0xLjQyMi4xMjItLjQzMS4zLS44MDIuNTMzLTEuMTEuMjMyLS4zMTQuNTE2LS41NTMuODUtLjcxOC4zMzUtLjE3LjcxMS0uMjU0IDEuMTMtLjI1NC40MTUgMCAuNzc5LjA4IDEuMDkyLjI0MS4zMTMuMTYxLjU4LjM5Mi44LjY5Mi4yMi4yOTcuMzk1LjY1Mi41MjcgMS4wNjcuMTMxLjQxLjIyNC44NjcuMjc5IDEuMzd2LjQyNmMtLjA1NS40OTEtLjE0OC45NC0uMjc5IDEuMzQ2YTMuNjM3IDMuNjM3IDAgMCAxLS41MjcgMS4wNTQgMi4yOCAyLjI4IDAgMCAxLS44MDYuNjg1Yy0uMzE0LjE2LS42OC4yNDEtMS4wOTkuMjQxLS40MTQgMC0uNzg5LS4wODYtMS4xMjMtLjI2YTIuNDc4IDIuNDc4IDAgMCAxLS44NDQtLjczIDMuNDE3IDMuNDE3IDAgMCAxLS41MzMtMS4xMDQgNS4wMDkgNS4wMDkgMCAwIDEtLjE4NS0xLjM5Wm0xLjUzLS4xMzN2LjEzNGMwIC4zMTMuMDI4LjYwNS4wODMuODc1LjA1OS4yNzEuMTUuNTEuMjczLjcxOC4xMjIuMjAzLjI4MS4zNjQuNDc2LjQ4Mi4xOTkuMTE0LjQzNi4xNzEuNzExLjE3MS4zNDcgMCAuNjMyLS4wNzYuODU3LS4yMjguMjI0LS4xNTIuNC0uMzU4LjUyNi0uNjE2LjEzMi0uMjYyLjIyMS0uNTU0LjI2Ny0uODc2di0xLjE0OWEyLjc2MSAyLjc2MSAwIDAgMC0uMTU5LS42OTggMS44NDYgMS44NDYgMCAwIDAtLjMxMS0uNTY1IDEuMzEgMS4zMSAwIDAgMC0uNDg4LS4zOCAxLjUzNSAxLjUzNSAwIDAgMC0uNjgtLjE0Yy0uMjc5IDAtLjUxNi4wNTktLjcxMS4xNzdhMS40NDcgMS40NDcgMCAwIDAtLjQ4Mi40OSAyLjM5MyAyLjM5MyAwIDAgMC0uMjczLjcyM2MtLjA1OS4yNzUtLjA4OS41NjktLjA4OS44ODJabTkuMzUzIDMuNjE4Yy0uNTA4IDAtLjk2Ny0uMDgyLTEuMzc4LS4yNDdhMy4wNDcgMy4wNDcgMCAwIDEtMS4wNDEtLjcwNSAzLjE0MyAzLjE0MyAwIDAgMS0uNjU0LTEuMDYgMy43MiAzLjcyIDAgMCAxLS4yMjgtMS4zMTR2LS4yNTRjMC0uNTM3LjA3OC0xLjAyNC4yMzUtMS40Ni4xNTYtLjQzNi4zNzQtLjgwOC42NTQtMS4xMTcuMjc5LS4zMTMuNjA5LS41NTIuOTktLjcxN2EzLjA4IDMuMDggMCAwIDEgMS4yMzgtLjI0OGMuNDkxIDAgLjkyLjA4MyAxLjI4OC4yNDguMzY4LjE2NS42NzMuMzk4LjkxNC42OTguMjQ2LjI5Ni40MjguNjUuNTQ2IDEuMDYuMTIzLjQxLjE4NC44NjMuMTg0IDEuMzU4di42NTRoLTUuMzA2di0xLjA5OGgzLjc5NnYtLjEyYTIuMTcxIDIuMTcxIDAgMCAwLS4xNjUtLjc3NSAxLjMyMiAxLjMyMiAwIDAgMC0uNDUxLS41ODRjLS4yMDMtLjE0OC0uNDc0LS4yMjItLjgxMy0uMjIyLS4yNTQgMC0uNDguMDU1LS42NzkuMTY1LS4xOTUuMTA2LS4zNTcuMjYtLjQ4OS40NjNhMi40NDUgMi40NDUgMCAwIDAtLjMwNC43MzcgNC4xMiA0LjEyIDAgMCAwLS4xMDIuOTU4di4yNTRjMCAuMy4wNC41OC4xMjEuODM4LjA4NC4yNTQuMjA3LjQ3Ni4zNjguNjY2LjE2MS4xOTEuMzU1LjM0MS41ODQuNDUxLjIyOC4xMDYuNDg5LjE1OS43ODEuMTU5LjM2OCAwIC42OTYtLjA3NC45ODQtLjIyMi4yODctLjE0OC41MzctLjM1OC43NDktLjYyOWwuODA2Ljc4MWEzLjE0MiAzLjE0MiAwIDAgMS0uNTc4LjYyMiAyLjg5MyAyLjg5MyAwIDAgMS0uODcuNDc2IDMuNDUzIDMuNDUzIDAgMCAxLTEuMTguMTg0WiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjM4IiBkPSJNNjEuMzc0IDUwLjkxOHYuOTE1aC0zLjM2MXYtLjkxNWgzLjM2Wm0tMy4wNC01Ljc4djYuNjk1SDU3LjE4VjQ1LjE0aDEuMTU1Wm02LjgyMiA1LjY5OHYtMi4zNzNjMC0uMTc4LS4wMzItLjMzMS0uMDk2LS40NmEuNjc2LjY3NiAwIDAgMC0uMjk0LS4yOTkgMS4wMDYgMS4wMDYgMCAwIDAtLjQ4OC0uMTA1Yy0uMTggMC0uMzM3LjAzLS40NjkuMDkyYS43NTguNzU4IDAgMCAwLS4zMDguMjQ4LjU5OS41OTkgMCAwIDAtLjExLjM1NGgtMS4xMDRjMC0uMTk2LjA0OC0uMzg2LjE0My0uNTcuMDk1LS4xODQuMjMzLS4zNDguNDE0LS40OTIuMTgtLjE0NC4zOTctLjI1OC42NDgtLjM0LjI1MS0uMDgzLjUzMy0uMTI1Ljg0Ni0uMTI1LjM3NCAwIC43MDUuMDYzLjk5My4xODkuMjkxLjEyNi41Mi4zMTYuNjg1LjU3LjE2OS4yNTEuMjUzLjU2Ny4yNTMuOTQ3djIuMjEyYzAgLjIyNy4wMTUuNDMuMDQ2LjYxMS4wMzQuMTc4LjA4MS4zMzMuMTQzLjQ2NXYuMDczaC0xLjEzNmExLjk2NyAxLjk2NyAwIDAgMS0uMTI0LS40NTUgMy43MyAzLjczIDAgMCAxLS4wNDEtLjU0MlptLjE2MS0yLjAyOC4wMS42ODVoLS43OTZjLS4yMDUgMC0uMzg2LjAyLS41NDIuMDZhMS4xMTMgMS4xMTMgMCAwIDAtLjM5MS4xNjUuNzE4LjcxOCAwIDAgMC0uMzEzLjYyMWMwIC4xMzIuMDMuMjUzLjA5Mi4zNjMuMDYxLjEwOC4xNS4xOTIuMjY3LjI1My4xMi4wNjIuMjYzLjA5Mi40MzIuMDkyLjIyNyAwIC40MjUtLjA0Ni41OTMtLjEzOGExLjIyIDEuMjIgMCAwIDAgLjQwNS0uMzQ1Ljc1Ljc1IDAgMCAwIC4xNTYtLjM5bC4zNTkuNDkyYy0uMDM3LjEyNS0uMS4yNi0uMTg5LjQwNGExLjk1OSAxLjk1OSAwIDAgMS0uMzUuNDE0IDEuNzM1IDEuNzM1IDAgMCAxLTEuMTkuNDQxYy0uMzI1IDAtLjYxNS0uMDY0LS44Ny0uMTkzYTEuNTQ0IDEuNTQ0IDAgMCAxLS41OTctLjUyOSAxLjM3MSAxLjM3MSAwIDAgMS0uMjE2LS43NThjMC0uMjY0LjA0OS0uNDk3LjE0Ny0uN2ExLjM3IDEuMzcgMCAwIDEgLjQ0Mi0uNTE0Yy4xOTYtLjEzOC40MzUtLjI0Mi43MTctLjMxMy4yODItLjA3My42MDQtLjExLjk2NS0uMTFoLjg3Wm01LjA5OCAxLjY3OGMwLS4xMS0uMDI4LS4yMS0uMDgzLS4yOTktLjA1NS0uMDkyLS4xNi0uMTc0LS4zMTctLjI0OGEzLjA3MyAzLjA3MyAwIDAgMC0uNjgtLjIwMiA1Ljg2NCA1Ljg2NCAwIDAgMS0uNzI3LS4yMDcgMi4zMDUgMi4zMDUgMCAwIDEtLjU2LS4zIDEuMjU4IDEuMjU4IDAgMCAxLS4zNjQtLjQxMyAxLjE0NiAxLjE0NiAwIDAgMS0uMTI5LS41NTJjMC0uMjAyLjA0NC0uMzk0LjEzMy0uNTc0LjA5LS4xODEuMjE2LS4zNC4zODItLjQ3OWExLjg2IDEuODYgMCAwIDEgLjYwMi0uMzI2Yy4yNC0uMDguNTA2LS4xMi44LS4xMi40MTcgMCAuNzc0LjA3IDEuMDcyLjIxMi4zLjEzOC41My4zMjYuNjkuNTY2LjE1OS4yMzYuMjM5LjUwMi4yMzkuOGgtMS4xMDlhLjcwNy43MDcgMCAwIDAtLjEtLjM2OC43MDUuNzA1IDAgMCAwLS4yOTUtLjI4IDEuMDA4IDEuMDA4IDAgMCAwLS40OTctLjExMWMtLjE5IDAtLjM0Ny4wMy0uNDczLjA5MmEuNjQ4LjY0OCAwIDAgMC0uMjc2LjIzLjU4Ni41ODYgMCAwIDAtLjA0MS41MzguNTMxLjUzMSAwIDAgMCAuMTY1LjE3OWMuMDc3LjA1Mi4xODEuMTAxLjMxMy4xNDcuMTM1LjA0Ni4zMDMuMDkuNTA2LjEzMy4zOC4wOC43MDYuMTgzLjk3OS4zMDguMjc2LjEyMy40ODcuMjgyLjYzNC40NzkuMTQ4LjE5My4yMjEuNDM4LjIyMS43MzUgMCAuMjIxLS4wNDcuNDIzLS4xNDIuNjA3LS4wOTIuMTgxLS4yMjcuMzM5LS40MDUuNDc0LS4xNzguMTMyLS4zOS4yMzQtLjY0LjMwOGEyLjg4IDIuODggMCAwIDEtLjgyNy4xMWMtLjQ1IDAtLjgzMi0uMDgtMS4xNDUtLjIzOWExLjgyNSAxLjgyNSAwIDAgMS0uNzEyLS42MiAxLjQ3IDEuNDcgMCAwIDEtLjI0LS43OTFoMS4wNzJhLjgzLjgzIDAgMCAwIC4xNy40OTJjLjEwNC4xMi4yMzMuMjA3LjM4Ni4yNjIuMTU3LjA1Mi4zMTguMDc4LjQ4My4wNzguMiAwIC4zNjYtLjAyNi41MDEtLjA3OGEuNzIuNzIgMCAwIDAgLjMwOC0uMjIxLjUyNi41MjYgMCAwIDAgLjEwNi0uMzIyWm00LjUzMi0zLjYyOHYuODFoLTIuODA1di0uODFoMi44MDVabS0xLjk5NS0xLjIxOGgxLjEwOHY0LjgxOWMwIC4xNTMuMDIxLjI3LjA2NC4zNTQuMDQ2LjA4LjEwOS4xMzMuMTg5LjE2YS44Ni44NiAwIDAgMCAuMjguMDQyIDIuMjY3IDIuMjY3IDAgMCAwIC4zOS0uMDQxbC4wMDUuODQ2Yy0uMDkxLjAyNy0uMTk5LjA1Mi0uMzIxLjA3M2EyLjM2IDIuMzYgMCAwIDEtLjQxNC4wMzJjLS4yNTUgMC0uNDgtLjA0NC0uNjc2LS4xMzNhLjk5NS45OTUgMCAwIDEtLjQ2LS40NDZjLS4xMS0uMjA1LS4xNjUtLjQ3OC0uMTY1LS44MThWNDUuNjRabTguNTggNS4wMnYtMy44MDJoMS4xMTR2NC45NzVoLTEuMDQ5bC0uMDY0LTEuMTcyWm0uMTU3LTEuMDM0LjM3My0uMDA5YzAgLjMzNC0uMDM3LjY0Mi0uMTEuOTI0YTIuMTQyIDIuMTQyIDAgMCAxLS4zNC43MzFjLS4xNTQuMjA2LS4zNS4zNjctLjU5LjQ4My0uMjM5LjExNC0uNTI1LjE3LS44Ni4xNy0uMjQxIDAtLjQ2NC0uMDM1LS42NjYtLjEwNWExLjM2NiAxLjM2NiAwIDAgMS0uNTI0LS4zMjcgMS40ODUgMS40ODUgMCAwIDEtLjMzNi0uNTc1IDIuNjU3IDIuNjU3IDAgMCAxLS4xMi0uODQ2di0zLjIxNGgxLjEwOXYzLjIyNGMwIC4xOC4wMjEuMzMyLjA2NC40NTVhLjc3Ljc3IDAgMCAwIC4xNzUuMjkuNjIuNjIgMCAwIDAgLjI1Ny4xNTZjLjA5OS4wMy4yMDMuMDQ2LjMxMy4wNDYuMzE2IDAgLjU2NC0uMDYyLjc0NS0uMTg0LjE4NC0uMTI2LjMxNC0uMjk0LjM5LS41MDYuMDgtLjIxMi4xMi0uNDQ5LjEyLS43MTNabTMuNDAxLTEuODExdjUuOTMxaC0xLjEwOHYtNi44ODhoMS4wMmwuMDg4Ljk1N1ptMy4yNDIgMS40ODV2LjA5NmMwIC4zNjItLjA0My42OTgtLjEyOSAxLjAwN2EyLjQ2IDIuNDYgMCAwIDEtLjM3Mi44MDUgMS43MTQgMS43MTQgMCAwIDEtLjYwMy41MjljLS4yMzkuMTI1LS41MTUuMTg4LS44MjcuMTg4LS4zMSAwLS41ODEtLjA1Ni0uODE0LS4xN2ExLjY2OCAxLjY2OCAwIDAgMS0uNTg0LS40OTIgMi42NyAyLjY3IDAgMCAxLS4zODYtLjc0NSA0Ljc4MiA0Ljc4MiAwIDAgMS0uMjAzLS45NDdWNDkuMmMuMDQtLjM2NS4xMDgtLjY5Ni4yMDMtLjk5My4wOTgtLjI5OC4yMjctLjU1NC4zODYtLjc2OC4xNi0uMjE1LjM1NC0uMzguNTg0LS40OTcuMjMtLjExNi40OTgtLjE3NS44MDQtLjE3NS4zMTMgMCAuNTkuMDYyLjgzMy4xODQuMjQyLjEyLjQ0Ni4yOTEuNjExLjUxNS4xNjYuMjIxLjI5LjQ4OC4zNzMuOC4wODIuMzEuMTI0LjY1NS4xMjQgMS4wMzVabS0xLjEwOC4wOTZWNDkuM2MwLS4yMy0uMDIyLS40NDMtLjA2NS0uNjQtLjA0My0uMTk4LS4xMS0uMzczLS4yMDItLjUyM2EuOTkuOTkgMCAwIDAtLjM1NC0uMzUuOTYzLjk2MyAwIDAgMC0uNTEtLjEyOWMtLjE5NyAwLS4zNjUuMDM0LS41MDYuMTAyYS45NjkuOTY5IDAgMCAwLS4zNTQuMjcgMS4yODEgMS4yODEgMCAwIDAtLjIyLjQxYy0uMDUzLjE1My0uMDkuMzItLjExMS41MDF2Ljg5MmMuMDM3LjIyMS4xLjQyMy4xODguNjA3LjA5LjE4NC4yMTUuMzMxLjM3Ny40NDIuMTY2LjEwNy4zNzcuMTYuNjM1LjE2LjE5OSAwIC4zNy0uMDQyLjUxLS4xMjguMTQxLS4wODYuMjU2LS4yMDQuMzQ1LS4zNTQuMDkyLS4xNTQuMTYtLjMzLjIwMi0uNTI5LjA0My0uMi4wNjUtLjQxLjA2NS0uNjM1Wm01LjI4MSAxLjQwN3YtNi4wMzJoMS4xMTN2Ny4wNjJoLTEuMDA3bC0uMTA2LTEuMDNabS0zLjIzNy0xLjQwMnYtLjA5N2MwLS4zNzcuMDQ1LS43Mi4xMzQtMS4wMy4wODgtLjMxMi4yMTctLjU4LjM4Ni0uODA0LjE2OC0uMjI3LjM3NC0uNC42MTYtLjUyLjI0Mi0uMTIyLjUxNS0uMTg0LjgxOC0uMTg0LjMgMCAuNTY0LjA1OS43OTEuMTc1LjIyNy4xMTcuNDIuMjg0LjU4LjUwMWEyLjYgMi42IDAgMCAxIC4zODEuNzczYy4wOTUuMjk3LjE2My42MjguMjAzLjk5M3YuMzA4Yy0uMDQuMzU2LS4xMDguNjgtLjIwMy45NzVhMi42MiAyLjYyIDAgMCAxLS4zODEuNzYzIDEuNjUgMS42NSAwIDAgMS0uNTg0LjQ5NyAxLjcxOSAxLjcxOSAwIDAgMS0uNzk2LjE3NGMtLjMgMC0uNTcyLS4wNjMtLjgxNC0uMTg4YTEuNzk4IDEuNzk4IDAgMCAxLS42MTEtLjUyOSAyLjQ5IDIuNDkgMCAwIDEtLjM4Ni0uOCAzLjYzNiAzLjYzNiAwIDAgMS0uMTM0LTEuMDA3Wm0xLjEwOC0uMDk3di4wOTdjMCAuMjI3LjAyLjQzOC4wNi42MzUuMDQzLjE5Ni4xMDkuMzY5LjE5OC41Mi4wODkuMTQ2LjIwNC4yNjMuMzQ1LjM0OS4xNDQuMDgyLjMxNi4xMjQuNTE1LjEyNC4yNTEgMCAuNDU4LS4wNTUuNjItLjE2Ni4xNjMtLjExLjI5LS4yNTkuMzgyLS40NDYuMDk1LS4xOS4xNi0uNDAxLjE5My0uNjM0di0uODMzYTIuMDMgMi4wMyAwIDAgMC0uMTE1LS41MDUgMS4zNSAxLjM1IDAgMCAwLS4yMjUtLjQxLjk0OC45NDggMCAwIDAtLjM1NC0uMjc1IDEuMTA5IDEuMTA5IDAgMCAwLS40OTItLjEwMi45NzIuOTcyIDAgMCAwLS41MTUuMTMgMS4wNDMgMS4wNDMgMCAwIDAtLjM1LjM1M2MtLjA4OC4xNS0uMTU0LjMyNS0uMTk3LjUyNC0uMDQzLjItLjA2NS40MTMtLjA2NS42NFptNy40MDIgMS41MzJ2LTIuMzczYzAtLjE3OC0uMDMzLS4zMzEtLjA5Ny0uNDZhLjY3Ni42NzYgMCAwIDAtLjI5NC0uMjk5IDEuMDA3IDEuMDA3IDAgMCAwLS40ODgtLjEwNWMtLjE4IDAtLjMzNy4wMy0uNDY5LjA5MmEuNzU5Ljc1OSAwIDAgMC0uMzA4LjI0OC42LjYgMCAwIDAtLjExLjM1NGgtMS4xMDNjMC0uMTk2LjA0Ny0uMzg2LjE0Mi0uNTcuMDk1LS4xODQuMjMzLS4zNDguNDE0LS40OTIuMTgtLjE0NC4zOTctLjI1OC42NDgtLjM0YTIuNyAyLjcgMCAwIDEgLjg0Ni0uMTI1Yy4zNzQgMCAuNzA1LjA2My45OTMuMTg5LjI5MS4xMjYuNTIuMzE2LjY4NS41Ny4xNjkuMjUxLjI1My41NjcuMjUzLjk0N3YyLjIxMmMwIC4yMjcuMDE2LjQzLjA0Ni42MTEuMDM0LjE3OC4wODEuMzMzLjE0My40NjV2LjA3M2gtMS4xMzZhMS45NjIgMS45NjIgMCAwIDEtLjEyNC0uNDU1IDMuNzE3IDMuNzE3IDAgMCAxLS4wNDEtLjU0MlptLjE2LTIuMDI4LjAxLjY4NWgtLjc5NmMtLjIwNSAwLS4zODYuMDItLjU0Mi4wNmExLjExNCAxLjExNCAwIDAgMC0uMzkxLjE2NS43Mi43MiAwIDAgMC0uMzEzLjYyMWMwIC4xMzIuMDMuMjUzLjA5Mi4zNjMuMDYxLjEwOC4xNS4xOTIuMjY3LjI1My4xMi4wNjIuMjYzLjA5Mi40MzIuMDkyLjIyNyAwIC40MjUtLjA0Ni41OTMtLjEzOC4xNzItLjA5NS4zMDctLjIxLjQwNS0uMzQ1YS43NS43NSAwIDAgMCAuMTU2LS4zOWwuMzU5LjQ5MmMtLjAzNy4xMjUtLjEuMjYtLjE4OS40MDRhMS45NiAxLjk2IDAgMCAxLS4zNS40MTQgMS43MzYgMS43MzYgMCAwIDEtMS4xOS40NDFjLS4zMjUgMC0uNjE1LS4wNjQtLjg2OS0uMTkzYTEuNTQ1IDEuNTQ1IDAgMCAxLS41OTgtLjUyOSAxLjM3MiAxLjM3MiAwIDAgMS0uMjE2LS43NThjMC0uMjY0LjA1LS40OTcuMTQ3LS43LjEwMS0uMjA0LjI0OS0uMzc2LjQ0Mi0uNTE0LjE5Ni0uMTM4LjQzNS0uMjQyLjcxNy0uMzEzLjI4Mi0uMDczLjYwNC0uMTEuOTY2LS4xMWguODY5Wm00LjU4OC0xLjk1di44MWgtMi44MDV2LS44MWgyLjgwNVptLTEuOTk2LTEuMjE4aDEuMTA4djQuODE5YzAgLjE1My4wMjIuMjcuMDY1LjM1NC4wNDYuMDguMTA5LjEzMy4xODguMTYuMDguMDI4LjE3My4wNDIuMjgxLjA0MmEyLjI2OCAyLjI2OCAwIDAgMCAuMzkxLS4wNDFsLjAwNC44NDZhMi44IDIuOCAwIDAgMS0uMzIyLjA3MyAyLjM1MiAyLjM1MiAwIDAgMS0uNDE0LjAzMmMtLjI1NCAwLS40NzktLjA0NC0uNjc1LS4xMzNhLjk5Ljk5IDAgMCAxLS40Ni0uNDQ2Yy0uMTExLS4yMDUtLjE2Ni0uNDc4LS4xNjYtLjgxOFY0NS42NFptNS4yNCA2LjI4NWMtLjM2NyAwLS43LS4wNi0uOTk3LS4xNzlhMi4xOSAyLjE5IDAgMCAxLS43NTQtLjUxIDIuMjQ4IDIuMjQ4IDAgMCAxLS40NzQtLjc2OCAyLjY5NyAyLjY5NyAwIDAgMS0uMTY2LS45NTJ2LS4xODRjMC0uMzkuMDU3LS43NDIuMTcxLTEuMDU3LjExMy0uMzE2LjI3MS0uNTg2LjQ3My0uODEuMjAzLS4yMjcuNDQyLS40LjcxNy0uNTIuMjc2LS4xMTkuNTc1LS4xNzkuODk3LS4xNzkuMzU2IDAgLjY2Ny4wNi45MzMuMTguMjY3LjEyLjQ4OC4yODguNjYzLjUwNi4xNzcuMjE0LjMwOS40Ny4zOTUuNzY3LjA4OS4yOTguMTMzLjYyNi4xMzMuOTg0di40NzRoLTMuODQ0di0uNzk1aDIuNzV2LS4wODhhMS41NDQgMS41NDQgMCAwIDAtLjEyLS41Ni45NS45NSAwIDAgMC0uMzI2LS40MjRjLS4xNDctLjEwNy0uMzQzLS4xNi0uNTg5LS4xNmEuOTk4Ljk5OCAwIDAgMC0uNDkxLjExOS45NzUuOTc1IDAgMCAwLS4zNTUuMzM1IDEuNzY0IDEuNzY0IDAgMCAwLS4yMi41MzQgMi45OTkgMi45OTkgMCAwIDAtLjA3NC42OTR2LjE4NGMwIC4yMTguMDI5LjQyLjA4OC42MDcuMDYxLjE4NC4xNS4zNDUuMjY2LjQ4My4xMTcuMTM4LjI1OC4yNDYuNDIzLjMyNi4xNjYuMDc3LjM1NC4xMTUuNTY2LjExNS4yNjYgMCAuNTA0LS4wNTMuNzEyLS4xNi4yMDktLjEwOC4zOS0uMjYuNTQzLS40NTZsLjU4NC41NjZhMi4yODEgMi4yODEgMCAwIDEtLjQxOC40NSAyLjEwNCAyLjEwNCAwIDAgMS0uNjMuMzQ1IDIuNTEzIDIuNTEzIDAgMCAxLS44NTYuMTMzWm04LjU1NC02LjgxdjYuNzE4aC0xLjEwOHYtNS40MDJsLTEuNjQxLjU1NnYtLjkxNWwyLjYxNi0uOTU2aC4xMzNabTUuNjIyIDUuNjg4di02LjAzMmgxLjExM3Y3LjA2MmgtMS4wMDdsLS4xMDYtMS4wM1ptLTMuMjM3LTEuNDAydi0uMDk3YzAtLjM3Ny4wNDQtLjcyLjEzMy0xLjAzYTIuNDIgMi40MiAwIDAgMSAuMzg3LS44MDRjLjE2OC0uMjI3LjM3NC0uNC42MTYtLjUyLjI0Mi0uMTIyLjUxNS0uMTg0LjgxOC0uMTg0LjMwMSAwIC41NjQuMDU5Ljc5MS4xNzUuMjI3LjExNy40Mi4yODQuNTc5LjUwMS4xNi4yMTUuMjg3LjQ3Mi4zODIuNzczLjA5NS4yOTcuMTYyLjYyOC4yMDIuOTkzdi4zMDhjLS4wNC4zNTYtLjEwNy42OC0uMjAyLjk3NWEyLjYwMyAyLjYwMyAwIDAgMS0uMzgyLjc2M2MtLjE1OS4yMTUtLjM1NC4zOC0uNTg0LjQ5N2ExLjcxNiAxLjcxNiAwIDAgMS0uNzk1LjE3NGMtLjMgMC0uNTcyLS4wNjMtLjgxNC0uMTg4YTEuNzkzIDEuNzkzIDAgMCAxLS42MTEtLjUyOSAyLjQ4NCAyLjQ4NCAwIDAgMS0uMzg3LS44IDMuNjMzIDMuNjMzIDAgMCAxLS4xMzMtMS4wMDdabTEuMTA4LS4wOTd2LjA5N2MwIC4yMjcuMDIuNDM4LjA2LjYzNS4wNDMuMTk2LjEwOS4zNjkuMTk4LjUyLjA4OS4xNDYuMjAzLjI2My4zNDQuMzQ5LjE0NS4wODIuMzE2LjEyNC41MTUuMTI0LjI1MiAwIC40NTktLjA1NS42MjEtLjE2Ni4xNjMtLjExLjI5LS4yNTkuMzgyLS40NDYuMDk1LS4xOS4xNTktLjQwMS4xOTMtLjYzNHYtLjgzM2EyLjAyNSAyLjAyNSAwIDAgMC0uMTE1LS41MDUgMS4zNSAxLjM1IDAgMCAwLS4yMjUtLjQxLjk0OC45NDggMCAwIDAtLjM1NC0uMjc1IDEuMTEgMS4xMSAwIDAgMC0uNDkyLS4xMDIuOTcuOTcgMCAwIDAtLjUxNS4xMyAxLjA0NiAxLjA0NiAwIDAgMC0uMzUuMzUzYy0uMDg5LjE1LS4xNTUuMzI1LS4xOTcuNTI0LS4wNDMuMi0uMDY1LjQxMy0uMDY1LjY0Wm05Ljk0MyAxLjUzMnYtMi4zNzNjMC0uMTc4LS4wMzMtLjMzMS0uMDk3LS40NmEuNjc4LjY3OCAwIDAgMC0uMjk0LS4yOTkgMS4wMSAxLjAxIDAgMCAwLS40ODgtLjEwNWMtLjE4MSAwLS4zMzcuMDMtLjQ2OS4wOTJhLjc2Ljc2IDAgMCAwLS4zMDguMjQ4LjYwMy42MDMgMCAwIDAtLjExLjM1NGgtMS4xMDRjMC0uMTk2LjA0OC0uMzg2LjE0My0uNTcuMDk1LS4xODQuMjMzLS4zNDguNDE0LS40OTJhMi4wNyAyLjA3IDAgMCAxIC42NDgtLjM0Yy4yNTEtLjA4My41MzMtLjEyNS44NDYtLjEyNS4zNzQgMCAuNzA1LjA2My45OTMuMTg5LjI5MS4xMjYuNTIuMzE2LjY4NS41Ny4xNjkuMjUxLjI1My41NjcuMjUzLjk0N3YyLjIxMmMwIC4yMjcuMDE2LjQzLjA0Ni42MTEuMDM0LjE3OC4wODEuMzMzLjE0My40NjV2LjA3M2gtMS4xMzZhMS45ODcgMS45ODcgMCAwIDEtLjEyNC0uNDU1IDMuNjYgMy42NiAwIDAgMS0uMDQxLS41NDJabS4xNi0yLjAyOC4wMS42ODVoLS43OTZjLS4yMDUgMC0uMzg2LjAyLS41NDIuMDZhMS4xMDggMS4xMDggMCAwIDAtLjM5MS4xNjUuNzIyLjcyMiAwIDAgMC0uMjM1LjI2Ny43ODIuNzgyIDAgMCAwLS4wNzguMzU0Ljc0Ljc0IDAgMCAwIC4wOTIuMzYzLjY0LjY0IDAgMCAwIC4yNjcuMjUzLjkzNC45MzQgMCAwIDAgLjQzMi4wOTIgMS4yMTggMS4yMTggMCAwIDAgLjk5OC0uNDgzLjc1Ljc1IDAgMCAwIC4xNTYtLjM5bC4zNTkuNDkyYy0uMDM3LjEyNS0uMS4yNi0uMTg5LjQwNGExLjk2MSAxLjk2MSAwIDAgMS0uMzQ5LjQxNCAxLjczIDEuNzMgMCAwIDEtMS4xOTEuNDQxYy0uMzI1IDAtLjYxNS0uMDY0LS44NjktLjE5M2ExLjU0MSAxLjU0MSAwIDAgMS0uNTk4LS41MjkgMS4zNzIgMS4zNzIgMCAwIDEtLjIxNi0uNzU4YzAtLjI2NC4wNDktLjQ5Ny4xNDctLjcuMTAxLS4yMDQuMjQ4LS4zNzYuNDQyLS41MTQuMTk2LS4xMzguNDM1LS4yNDIuNzE3LS4zMTMuMjgyLS4wNzMuNjA0LS4xMS45NjUtLjExaC44NjlabTUuNDk0LTEuOTVoMS4wMDZ2NC44MzdjMCAuNDQ4LS4wOTUuODI4LS4yODUgMS4xNC0uMTkuMzEzLS40NTUuNTUxLS43OTUuNzEzYTIuNzcxIDIuNzcxIDAgMCAxLTIuNDg4LS4xMDEgMS42OCAxLjY4IDAgMCAxLS41MzgtLjQ3M2wuNTItLjY1M2MuMTc4LjIxMS4zNzQuMzY2LjU4OS40NjQuMjE0LjA5OC40MzkuMTQ3LjY3NS4xNDcuMjU1IDAgLjQ3MS0uMDQ3LjY0OS0uMTQyYS45NjEuOTYxIDAgMCAwIC40MTgtLjQxYy4wOTgtLjE4LjE0Ny0uNDAxLjE0Ny0uNjYydi0zLjczM2wuMTAyLTEuMTI3Wm0tMy4zOCAyLjU0M3YtLjA5N2MwLS4zNzcuMDQ2LS43Mi4xMzgtMS4wM2EyLjQ1IDIuNDUgMCAwIDEgLjM5NS0uODA0Yy4xNzItLjIyNy4zOC0uNC42MjYtLjUyLjI0NS0uMTIyLjUyMi0uMTg0LjgzMi0uMTg0LjMyMiAwIC41OTYuMDU5LjgyMy4xNzUuMjMuMTE3LjQyMS4yODQuNTc1LjUwMS4xNTMuMjE1LjI3My40NzIuMzU4Ljc3My4wODkuMjk3LjE1NS42MjguMTk4Ljk5M3YuMzA4Yy0uMDQuMzU2LS4xMDcuNjgtLjIwMi45NzVhMi42OTcgMi42OTcgMCAwIDEtLjM3Ny43NjNjLS4xNTcuMjE1LS4zNS4zOC0uNTguNDk3YTEuNzM3IDEuNzM3IDAgMCAxLS44MDQuMTc0Yy0uMzA0IDAtLjU3OC0uMDYzLS44MjMtLjE4OGExLjg4IDEuODggMCAwIDEtLjYyNi0uNTI5IDIuNTE4IDIuNTE4IDAgMCAxLS4zOTUtLjggMy41MjIgMy41MjIgMCAwIDEtLjEzOC0xLjAwN1ptMS4xMDgtLjA5N3YuMDk3YzAgLjIyNy4wMjIuNDM4LjA2NC42MzUuMDQ2LjE5Ni4xMTUuMzY5LjIwNy41Mi4wOTUuMTQ2LjIxNS4yNjMuMzU5LjM0OS4xNDcuMDgyLjMyLjEyNC41Mi4xMjQuMjYgMCAuNDczLS4wNTUuNjM5LS4xNjYuMTY4LS4xMS4yOTctLjI1OS4zODYtLjQ0Ni4wOTItLjE5LjE1Ni0uNDAxLjE5My0uNjM0di0uODMzYTIuMDI1IDIuMDI1IDAgMCAwLS4xMTUtLjUwNSAxLjM1IDEuMzUgMCAwIDAtLjIyNS0uNDEuOTQyLjk0MiAwIDAgMC0uMzU5LS4yNzUgMS4xOTEgMS4xOTEgMCAwIDAtLjUxLS4xMDJjLS4xOTkgMC0uMzczLjA0My0uNTIuMTNhMS4wNTUgMS4wNTUgMCAwIDAtLjM2My4zNTMgMS43NSAxLjc1IDAgMCAwLS4yMDcuNTI0Yy0uMDQ2LjItLjA2OS40MTMtLjA2OS42NFptNC40MTMuMDk3di0uMTA2YzAtLjM1OC4wNTItLjY5LjE1Ni0uOTk4LjEwNC0uMzEuMjU0LS41NzcuNDUxLS44MDRhMi4wMyAyLjAzIDAgMCAxIC43MjYtLjUzNGMuMjg4LS4xMjguNjEzLS4xOTMuOTc1LS4xOTMuMzY1IDAgLjY5LjA2NS45NzUuMTk0LjI4OC4xMjUuNTMxLjMwMy43MzEuNTMzLjE5OS4yMjcuMzUxLjQ5NS40NTUuODA0LjEwNC4zMDcuMTU2LjY0LjE1Ni45OTh2LjEwNmMwIC4zNTktLjA1Mi42OTEtLjE1Ni45OThhMi4zODggMi4zODggMCAwIDEtLjQ1NS44MDRjLS4yLjIyNy0uNDQyLjQwNS0uNzI3LjUzNGEyLjM4IDIuMzggMCAwIDEtLjk3LjE4OGMtLjM2NSAwLS42OTEtLjA2My0uOTc5LS4xODhhMi4xMSAyLjExIDAgMCAxLS43MjctLjUzNCAyLjM4OCAyLjM4OCAwIDAgMS0uNDU1LS44MDQgMy4wODcgMy4wODcgMCAwIDEtLjE1Ni0uOTk4Wm0xLjEwOC0uMTA2di4xMDZjMCAuMjI0LjAyMy40MzUuMDY5LjYzNS4wNDYuMTk5LjExOC4zNzMuMjE2LjUyNC4wOTguMTUuMjI0LjI2OC4zNzcuMzU0LjE1My4wODYuMzM2LjEyOC41NDcuMTI4LjIwNSAwIC4zODMtLjA0Mi41MzMtLjEyOC4xNTQtLjA4Ni4yNzktLjIwNC4zNzctLjM1NC4wOTgtLjE1LjE3MS0uMzI1LjIxNy0uNTI0LjA0OS0uMi4wNzMtLjQxMS4wNzMtLjYzNXYtLjEwNmMwLS4yMi0uMDI0LS40MjktLjA3My0uNjI1YTEuNjEgMS42MSAwIDAgMC0uMjIxLS41MjkgMS4wNTYgMS4wNTYgMCAwIDAtLjkxNS0uNDkyYy0uMjA5IDAtLjM4OS4wNDUtLjU0My4xMzQtLjE1LjA4NS0uMjc0LjIwNS0uMzcyLjM1OGExLjY3IDEuNjcgMCAwIDAtLjIxNi41MjkgMi43MzIgMi43MzIgMCAwIDAtLjA2OS42MjVaIi8+PHBhdGggZmlsbD0iIzNGQTcxQSIgZD0iTTUyLjIzIDg3Ljg1OXYxOS45NzRoLTMuMjk0VjkxLjc2OWwtNC44ODEgMS42NTR2LTIuNzJsNy43NzktMi44NDRoLjM5NlptMTguMTMgOC4zNTN2My4yNTRjMCAxLjU1OS0uMTU2IDIuODktLjQ2NiAzLjk5Mi0uMyAxLjA5NC0uNzM4IDEuOTgzLTEuMzEyIDIuNjY2YTUuMTQ4IDUuMTQ4IDAgMCAxLTIuMDY0IDEuNTA0Yy0uNzkzLjMxOS0xLjY4Mi40NzktMi42NjYuNDc5LS43ODQgMC0xLjUxMy0uMS0yLjE4OC0uMzAxYTUuMzQgNS4zNCAwIDAgMS0xLjgwNS0uOTQzYy0uNTM4LS40MjktLjk5OC0uOTgtMS4zOC0xLjY1NS0uMzc0LS42ODMtLjY2Ni0xLjQ5OS0uODc2LTIuNDQ3LS4yLS45NDgtLjMtMi4wNDYtLjMtMy4yOTV2LTMuMjU0YzAtMS41NjcuMTU1LTIuODg5LjQ2NS0zLjk2NS4zMS0xLjA4NC43NTItMS45NjQgMS4zMjYtMi42MzhhNS4wNTEgNS4wNTEgMCAwIDEgMi4wNS0xLjQ5Yy44MDMtLjMxIDEuNjk2LS40NjUgMi42OC0uNDY1Ljc5MyAwIDEuNTIyLjEgMi4xODguM2E1LjEyIDUuMTIgMCAwIDEgMS44MDQuOTE3Yy41MzguNDE5Ljk5NC45NjYgMS4zNjcgMS42NC4zODMuNjY2LjY3NSAxLjQ3Mi44NzUgMi40Mi4yMDEuOTM5LjMwMSAyLjAzMy4zMDEgMy4yODFabS0zLjI5NiAzLjcxOXYtNC4yMWMwLS43OTQtLjA0NS0xLjQ5MS0uMTM2LTIuMDkzLS4wOTEtLjYxLS4yMjgtMS4xMjUtLjQxLTEuNTQ1LS4xNzQtLjQyOC0uMzkyLS43NzQtLjY1Ny0xLjAzOWEyLjMxMiAyLjMxMiAwIDAgMC0uOTAyLS41ODggMy4xODcgMy4xODcgMCAwIDAtMS4xMzUtLjE5MWMtLjUxIDAtLjk2Ni4xLTEuMzY3LjMtLjQwMS4xOTItLjczOC41MDItMS4wMTIuOTMtLjI3My40MjktLjQ4My45OTQtLjYyOSAxLjY5Ni0uMTM2LjY5Mi0uMjA1IDEuNTM2LS4yMDUgMi41M3Y0LjIxYzAgLjgwMi4wNDYgMS41MDguMTM3IDIuMTE5LjA5MS42MTEuMjI4IDEuMTM1LjQxIDEuNTcyLjE4Mi40MjkuNDAxLjc4NC42NTYgMS4wNjcuMjY1LjI3My41NjYuNDc0LjkwMy42MDFhMy4yNSAzLjI1IDAgMCAwIDEuMTM1LjE5MmMuNTE5IDAgLjk4LS4xIDEuMzgtLjMwMS40MDEtLjIuNzM5LS41MiAxLjAxMi0uOTU3LjI3My0uNDQ3LjQ3OS0xLjAyNS42MTUtMS43MzYuMTM3LS43MTEuMjA1LTEuNTYzLjIwNS0yLjU1N1ptMjMuMTYtNi44OWgzLjMxdjE0Ljc5MmgtMy4wNWwtLjI2LTMuNDE4VjkzLjA0Wm0uNDc5IDcuOTE1IDEuMjk5LS4wMjdjMCAxLjAzOS0uMSAxLjk5Ni0uMyAyLjg3MS0uMTkyLjg3NS0uNDkzIDEuNjM2LS45MDMgMi4yODNhNC4zMDUgNC4zMDUgMCAwIDEtMS41MzIgMS41MDRjLS42Mi4zNTYtMS4zNTMuNTMzLTIuMi41MzMtLjY3NSAwLTEuMjgxLS4wOTUtMS44MTktLjI4N2EzLjYwNyAzLjYwNyAwIDAgMS0xLjM5NS0uOTI5Yy0uMzkxLS40Mi0uNzE1LS45NjYtLjk3LTEuNjQxbC0uNDI0LTMuNzMyaDEuODczYzAgLjgyLjA2OCAxLjQ5LjIwNSAyLjAwOS4xMzcuNTIuMzI4LjkyMS41NzQgMS4yMDQuMjQ2LjI3My41MzMuNDY0Ljg2Mi41NzQuMzM3LjEwOS43MDEuMTY0IDEuMDkzLjE2NC43MiAwIDEuMzEzLS4xMSAxLjc3OC0uMzI4YTIuNjg2IDIuNjg2IDAgMCAwIDEuMTA3LS45NDRjLjI3NC0uNDEuNDY1LS44ODguNTc0LTEuNDM1YTguNzI4IDguNzI4IDAgMCAwIC4xNzgtMS44MTlabS05LjY1Mi03LjkxNmgzLjI4MXYyMC40ODFoLTMuMjgxdi0yMC40OFptMjUuODEyIDBoMi45OTR2MTQuMzgzYzAgMS4zMzEtLjI4MiAyLjQ2MS0uODQ3IDMuMzkxYTUuNDQ2IDUuNDQ2IDAgMCAxLTIuMzY2IDIuMTE5Yy0xLjAxMS40OTItMi4xODIuNzM4LTMuNTEzLjczOGE4LjI2MyA4LjI2MyAwIDAgMS0xLjg4Ny0uMjQ2IDcuNzA4IDcuNzA4IDAgMCAxLTEuOTk2LS43OTMgNC45NDUgNC45NDUgMCAwIDEtMS42LTEuNDA4bDEuNTQ1LTEuOTQxYy41MjkuNjI4IDEuMTEyIDEuMDg5IDEuNzUgMS4zOGE0Ljc3NSA0Ljc3NSAwIDAgMCAyLjAxLjQzOGMuNzU3IDAgMS4zOTktLjE0MSAxLjkyOC0uNDI0YTIuODYgMi44NiAwIDAgMCAxLjI0NC0xLjIxN2MuMjkyLS41MzcuNDM3LTEuMTk0LjQzNy0xLjk2OFY5Ni4zOWwuMzAxLTMuMzVabS0xMC4wNDkgNy41NjF2LS4yODdjMC0xLjEyMS4xMzctMi4xNDIuNDEtMy4wNjMuMjc0LS45My42NjYtMS43MjcgMS4xNzYtMi4zOTJhNS4xMjggNS4xMjggMCAwIDEgMS44Ni0xLjU0NWMuNzI5LS4zNjUgMS41NTQtLjU0NyAyLjQ3NC0uNTQ3Ljk1NyAwIDEuNzczLjE3MyAyLjQ0OC41MmE0LjYzIDQuNjMgMCAwIDEgMS43MDkgMS40OWMuNDU1LjYzOC44MTEgMS40MDMgMS4wNjYgMi4yOTcuMjY0Ljg4NC40NiAxLjg2OC41ODggMi45NTN2LjkxNmExNC43OTcgMTQuNzk3IDAgMCAxLS42MDIgMi44OThjLS4yODIuODc1LS42NTYgMS42MzItMS4xMjEgMi4yN2E0Ljc4NCA0Ljc4NCAwIDAgMS0xLjcyMiAxLjQ3NmMtLjY3NS4zNDctMS40NzIuNTItMi4zOTMuNTItLjkwMiAwLTEuNzE4LS4xODctMi40NDctLjU2MWE1LjU2NCA1LjU2NCAwIDAgMS0xLjg2LTEuNTcyYy0uNTEtLjY3NC0uOTAyLTEuNDY3LTEuMTc1LTIuMzc5LS4yNzQtLjkyLS40MS0xLjkxOS0uNDEtMi45OTRabTMuMjk1LS4yODd2LjI4N2MwIC42NzQuMDY0IDEuMzAzLjE5MiAxLjg4Ny4xMzYuNTgzLjM0MiAxLjA5OC42MTUgMS41NDUuMjgyLjQzNy42MzguNzgzIDEuMDY2IDEuMDM5LjQzOC4yNDYuOTUzLjM2OSAxLjU0NS4zNjkuNzc1IDAgMS40MDgtLjE2NCAxLjkwMS0uNDkyYTMuMTkzIDMuMTkzIDAgMCAwIDEuMTQ4LTEuMzI3IDYuNjggNi42OCAwIDAgMCAuNTc0LTEuODg2di0yLjQ3NWE2LjAxOCA2LjAxOCAwIDAgMC0uMzQxLTEuNTA0IDQuMDU3IDQuMDU3IDAgMCAwLS42Ny0xLjIxNyAyLjc5NyAyLjc5NyAwIDAgMC0xLjA2Ny0uODJjLS40MjgtLjItLjkzNC0uMy0xLjUxNy0uMy0uNTkzIDAtMS4xMDguMTI3LTEuNTQ1LjM4MmEzLjEyOCAzLjEyOCAwIDAgMC0xLjA4IDEuMDUzIDUuMTk4IDUuMTk4IDAgMCAwLS42MTYgMS41NTggOC40NzggOC40NzggMCAwIDAtLjIwNSAxLjkwMVptMjEuODIxLTEyLjM4Ny03Ljc1MiAyMS42MTVoLTIuNTg0bDcuNzY1LTIxLjYxNWgyLjU3MVptNS41NzggOC4xMjF2MTEuNzg1aC0zLjI5NVY5My4wNGgzLjEwM2wuMTkyIDMuMDA4Wm0tLjUzMyAzLjg0Mi0xLjEyMi0uMDE0YzAtMS4wMi4xMjgtMS45NjQuMzgzLTIuODMuMjU1LS44NjYuNjI5LTEuNjE4IDEuMTIxLTIuMjU2YTQuOTM1IDQuOTM1IDAgMCAxIDEuODMyLTEuNDljLjczOS0uMzU1IDEuNTkxLS41MzMgMi41NTctLjUzMy42NzQgMCAxLjI5LjEgMS44NDYuM2EzLjY2NiAzLjY2NiAwIDAgMSAxLjQ2My45MTdjLjQxOS40MTkuNzM4Ljk1Ny45NTcgMS42MTMuMjI4LjY1Ni4zNDEgMS40NS4zNDEgMi4zNzl2OS44NTdoLTMuMjk0di05LjU3YzAtLjcyLS4xMS0xLjI4NS0uMzI5LTEuNjk1YTEuODE3IDEuODE3IDAgMCAwLS45MTYtLjg3NWMtLjM5Mi0uMTgzLS44NjEtLjI3NC0xLjQwOC0uMjc0LS42MiAwLTEuMTQ4LjExOS0xLjU4Ni4zNTYtLjQyOC4yMzctLjc3OS41Ni0xLjA1Mi45Ny0uMjc0LjQxLS40NzQuODg1LS42MDIgMS40MjJhNy40MjMgNy40MjMgMCAwIDAtLjE5MSAxLjcyM1ptOS4xNzMtLjg3NS0xLjU0NS4zNDJjMC0uODkzLjEyMy0xLjczNi4zNy0yLjUzYTYuNDQ0IDYuNDQ0IDAgMCAxIDEuMTA3LTIuMTA1IDUuMTk1IDUuMTk1IDAgMCAxIDEuODE4LTEuNDM1Yy43Mi0uMzQ3IDEuNTQ1LS41MiAyLjQ3NS0uNTIuNzU2IDAgMS40MzEuMTA1IDIuMDIzLjMxNC42MDIuMiAxLjExMi41MiAxLjUzMi45NTcuNDE5LjQzOC43MzggMS4wMDguOTU3IDEuNzEuMjE4LjY5Mi4zMjggMS41My4zMjggMi41MTV2OS41N2gtMy4zMDl2LTkuNTg0YzAtLjc0Ny0uMTA5LTEuMzI2LS4zMjgtMS43MzYtLjIxLS40MS0uNTEtLjY5My0uOTAyLS44NDgtLjM5Mi0uMTY0LS44NjItLjI0Ni0xLjQwOC0uMjQ2LS41MTEgMC0uOTYyLjA5Ni0xLjM1NC4yODdhMi42OSAyLjY5IDAgMCAwLS45NzEuNzggMy40NjMgMy40NjMgMCAwIDAtLjYwMSAxLjEzNSA0Ljg1MiA0Ljg1MiAwIDAgMC0uMTkyIDEuMzk0Wm0xNC40NTItNi42NThoMS4xODljLjM5MiAwIC43MTUtLjA1NS45NzEtLjE2NC4yNTUtLjExOS40NDYtLjI3OC41NzQtLjQ3OS4xMzctLjIuMjA1LS40MzcuMjA1LS43MSAwLS4zMjktLjEyOC0uNjE2LS4zODMtLjg2Mi0uMjQ2LS4yNTUtLjY2MS0uMzgzLTEuMjQ0LS4zODMtLjQ0NyAwLS44MjUuMDk2LTEuMTM1LjI4Ny0uMzAxLjE4My0uNDUxLjQ0Mi0uNDUxLjc4aC0yLjQ4OGMwLS42NDguMTgyLTEuMTk5LjU0Ny0xLjY1NS4zNjQtLjQ1Ni44NTItLjgwMiAxLjQ2My0xLjAzOWE1LjQ2OCA1LjQ2OCAwIDAgMSAxLjk5Ni0uMzU1Yy44MjkgMCAxLjU1OC4xMTggMi4xODcuMzU1LjYyOS4yMjggMS4xMTcuNTcgMS40NjMgMS4wMjYuMzU2LjQ1NS41MzMgMS4wMi41MzMgMS42OTUgMCAuNTItLjE2NC45OC0uNDkyIDEuMzgtLjMxOS40MDItLjc2Ni43MTYtMS4zNC45NDQtLjU2NS4yMTktMS4yMy4zMjgtMS45OTYuMzI4SDE1MC42di0xLjE0OFptMCAxLjcwOXYtMS4xMjFoMS41OTljLjg0OCAwIDEuNTcyLjEgMi4xNzQuMy42MDIuMTkyIDEuMDU3LjQ4OCAxLjM2Ny44OS4zMTkuNC40NzkuOTIuNDc5IDEuNTU4IDAgLjY3NC0uMTkyIDEuMjQ4LS41NzUgMS43MjItLjM4Mi40NzQtLjkwNi44MzQtMS41NzIgMS4wOC0uNjU2LjI0Ny0xLjM5NC4zNy0yLjIxNS4zNy0uNjgzIDAtMS4zNDktLjExLTEuOTk2LS4zMjhhMy43MTMgMy43MTMgMCAwIDEtMS41ODYtMS4wNjdjLS40MS0uNDgzLS42MTUtMS4xMTYtLjYxNS0xLjloMi41MDJjMCAuMzU1LjE2NC42Ni40OTIuOTE2LjMyOC4yNTUuNzcuMzgyIDEuMzI2LjM4Mi41OTMgMCAxLjAzLS4xMjcgMS4zMTMtLjM4Mi4yOTItLjI2NS40MzctLjU5My40MzctLjk4NSAwLS4zNDYtLjA4Mi0uNjI0LS4yNDYtLjgzNGExLjI3NCAxLjI3NCAwIDAgMC0uNjctLjQ1IDMuMTUgMy4xNSAwIDAgMC0xLjAyNS0uMTUxSDE1MC42WiIvPjwvc3ZnPg==", + "description": "A gas produced from vehicle emissions and the burning of fossil fuels.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'nitrogen', label: 'Nitrogen dioxide', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Nitrogen dioxide\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"public\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#3FA71A\"},{\"from\":40,\"to\":90,\"color\":\"#80C32C\"},{\"from\":90,\"to\":120,\"color\":\"#FFA600\"},{\"from\":120,\"to\":230,\"color\":\"#F36900\"},{\"from\":230,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":28,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#3FA71A\"},{\"from\":40,\"to\":90,\"color\":\"#80C32C\"},{\"from\":90,\"to\":120,\"color\":\"#FFA600\"},{\"from\":120,\"to\":230,\"color\":\"#F36900\"},{\"from\":230,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Nitrogen dioxide (NO2) card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "enviroment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "nitrogen", + "nitrogen dioxide" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/nitrogen_dioxide__no2__card_with_background.json b/application/src/main/data/json/system/widget_types/nitrogen_dioxide__no2__card_with_background.json new file mode 100644 index 0000000000..bc41253f09 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/nitrogen_dioxide__no2__card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "nitrogen_dioxide_no2_card_with_background", + "name": "Nitrogen dioxide (NO2) card with background", + "deprecated": false, + "image": "tb-image:Tk8yLXZhbHVlLWNhcmQtd2l0aC1iYWNrZ3JvdW5kLnN2Zw==:Tk8yLXZhbHVlLWNhcmQtd2l0aC1iYWNrZ3JvdW5kLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuNzYiIHJ4PSI0Ii8+PC9nPjxwYXRoIGZpbGw9IiMzQjkxMUMiIGQ9Ik0yNyAxNy41Yy02LjkgMC0xMi41IDUuNi0xMi41IDEyLjVTMjAuMSA0Mi41IDI3IDQyLjUgMzkuNSAzNi45IDM5LjUgMzAgMzMuOSAxNy41IDI3IDE3LjVaTTE3IDMwYzAtLjc2Mi4xLTEuNTEyLjI2Mi0yLjIyNWw1Ljk3NiA1Ljk3NVYzNWMwIDEuMzc1IDEuMTI1IDIuNSAyLjUgMi41djIuNDEzQzIwLjgyNSAzOS4yODggMTcgMzUuMDg3IDE3IDMwWm0xNy4zNjIgNi43NWMtLjMyNS0xLjAxMy0xLjI1LTEuNzUtMi4zNzUtMS43NWgtMS4yNXYtMy43NWMwLS42ODgtLjU2Mi0xLjI1LTEuMjUtMS4yNWgtNy41di0yLjVoMi41Yy42ODggMCAxLjI1LS41NjMgMS4yNS0xLjI1di0yLjVoMi41YzEuMzc2IDAgMi41LTEuMTI1IDIuNS0yLjV2LS41MTJDMzQuNCAyMi4yMTIgMzcgMjUuODEzIDM3IDMwYzAgMi42LTEuMDEzIDQuOTc1LTIuNjM4IDYuNzVaIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuODciIGQ9Ik01Ni45MDcgMTguNTQ3VjI4LjVoLTEuNzE2bC00LjQ2NC03LjEzdjcuMTNoLTEuNzE1di05Ljk1M2gxLjcxNWw0LjQ3OCA3LjE0M3YtNy4xNDNoMS43MDJabTMuODk0IDIuNTU2VjI4LjVoLTEuNjU0di03LjM5Nkg2MC44Wm0tMS43NjQtMS45NGMwLS4yNTIuMDgyLS40NTkuMjQ2LS42MjMuMTY5LS4xNjkuNDAxLS4yNTMuNjk3LS4yNTMuMjkyIDAgLjUyMi4wODQuNjkuMjUzYS44MzEuODMxIDAgMCAxIC4yNTQuNjIyLjgyMy44MjMgMCAwIDEtLjI1My42MTVjLS4xNjkuMTY0LS4zOTkuMjQ2LS42OS4yNDYtLjI5NyAwLS41My0uMDgyLS42OTgtLjI0NmEuODM0LjgzNCAwIDAgMS0uMjQ2LS42MTVabTcuMjEgMS45NHYxLjIwNGgtNC4xN3YtMS4yMDNoNC4xN1ptLTIuOTY4LTEuODExaDEuNjQ4djcuMTY0YzAgLjIyOC4wMzIuNDAzLjA5NS41MjZhLjUxMS41MTEgMCAwIDAgLjI4LjI0Yy4xMi4wNC4yNTguMDYxLjQxOC4wNjEuMTE0IDAgLjIyMy0uMDA3LjMyOC0uMDJhMy4yOSAzLjI5IDAgMCAwIC4yNTMtLjA0MWwuMDA3IDEuMjU4YTQuMjA1IDQuMjA1IDAgMCAxLS40NzkuMTA5IDMuNTE0IDMuNTE0IDAgMCAxLS42MTUuMDQ4Yy0uMzc4IDAtLjcxMy0uMDY2LTEuMDA1LS4xOThhMS40OCAxLjQ4IDAgMCAxLS42ODQtLjY2NGMtLjE2NC0uMzA1LS4yNDYtLjcxLS4yNDYtMS4yMTZ2LTcuMjY3Wm02LjE0MyAzLjIyVjI4LjVoLTEuNjQ4di03LjM5NmgxLjU3M2wuMDc1IDEuNDA4Wm0yLjI2My0xLjQ1Ni0uMDE0IDEuNTNhNC4yOTMgNC4yOTMgMCAwIDAtLjY3LS4wNTVjLS4yODMuMDAxLS41My4wNDItLjc0NS4xMjRhMS40MDQgMS40MDQgMCAwIDAtLjU0LjM0MmMtLjE0MS4xNDYtLjI1LjMyNC0uMzI4LjUzMy0uMDc4LjIxLS4xMjMuNDQ1LS4xMzcuNzA0bC0uMzc2LjAyOGMwLS40NjUuMDQ2LS44OTYuMTM3LTEuMjkyYTMuNTMgMy41MyAwIDAgMSAuNDEtMS4wNDZjLjE4Ny0uMzAxLjQyLS41MzYuNjk3LS43MDQuMjgzLS4xNjkuNjA5LS4yNTMuOTc4LS4yNTMuMSAwIC4yMDcuMDA5LjMyMS4wMjcuMTE4LjAxOC4yMDcuMDM5LjI2Ny4wNjJabS43OSAzLjgyOHYtLjE1N2MwLS41MzQuMDc3LTEuMDI4LjIzMi0xLjQ4NC4xNTUtLjQ2LjM3OC0uODU5LjY3LTEuMTk2YTMuMDI0IDMuMDI0IDAgMCAxIDEuMDgtLjc5MyAzLjUxNiAzLjUxNiAwIDAgMSAxLjQ1LS4yODdjLjU0MiAwIDEuMDI1LjA5NiAxLjQ0OS4yODcuNDI4LjE4Ny43OS40NTEgMS4wODYuNzkzLjI5Ny4zMzcuNTIyLjczNi42NzcgMS4xOTYuMTU1LjQ1Ni4yMzMuOTUuMjMzIDEuNDg0di4xNTdjMCAuNTMzLS4wNzggMS4wMjctLjIzMyAxLjQ4M2EzLjU1IDMuNTUgMCAwIDEtLjY3NyAxLjE5NyAzLjEzNiAzLjEzNiAwIDAgMS0xLjA4Ljc5MiAzLjU0IDMuNTQgMCAwIDEtMS40NDIuMjggMy42MDcgMy42MDcgMCAwIDEtMS40NTYtLjI4IDMuMTM1IDMuMTM1IDAgMCAxLTEuMDgtLjc5MyAzLjU1MSAzLjU1MSAwIDAgMS0uNjc3LTEuMTk2IDQuNTgyIDQuNTgyIDAgMCAxLS4yMzItMS40ODNabTEuNjQ3LS4xNTd2LjE1N2MwIC4zMzIuMDM0LjY0Ny4xMDMuOTQzYTIuNCAyLjQgMCAwIDAgLjMyLjc4Yy4xNDcuMjIzLjMzNC4zOTguNTYxLjUyNi4yMjguMTI3LjUuMTkxLjgxNC4xOTEuMzA1IDAgLjU3LS4wNjQuNzkzLS4xOTEuMjI4LS4xMjguNDE1LS4zMDMuNTYtLjUyNy4xNDYtLjIyMy4yNTMtLjQ4My4zMjItLjc3OS4wNzMtLjI5Ni4xMS0uNjEuMTEtLjk0M3YtLjE1N2MwLS4zMjktLjAzNy0uNjM5LS4xMS0uOTNhMi4zODcgMi4zODcgMCAwIDAtLjMyOC0uNzg2IDEuNTcgMS41NyAwIDAgMC0uNTYtLjUzNCAxLjU0NCAxLjU0NCAwIDAgMC0uOC0uMTk4Yy0uMzEgMC0uNTguMDY2LS44MDcuMTk4YTEuNTg3IDEuNTg3IDAgMCAwLS41NTQuNTM0Yy0uMTQ2LjIyOC0uMjUzLjQ5LS4zMjEuNzg2YTQuMDYyIDQuMDYyIDAgMCAwLS4xMDMuOTNabTExLjU5OC0zLjYyM2gxLjQ5N3Y3LjE5YzAgLjY2Ni0uMTQxIDEuMjMxLS40MjQgMS42OTZhMi43MjQgMi43MjQgMCAwIDEtMS4xODMgMS4wNmMtLjUwNS4yNDYtMS4wOTEuMzY5LTEuNzU3LjM2OS0uMjgyIDAtLjU5Ni0uMDQxLS45NDMtLjEyM2EzLjg2NSAzLjg2NSAwIDAgMS0uOTk4LS4zOTcgMi40NzQgMi40NzQgMCAwIDEtLjgtLjcwNGwuNzczLS45N2MuMjY0LjMxNC41NTYuNTQ0Ljg3NS42OS4zMTkuMTQ2LjY1NC4yMTkgMS4wMDUuMjE5LjM3OCAwIC43LS4wNy45NjMtLjIxMi4yNy0uMTM3LjQ3Ny0uMzQuNjIzLS42MDguMTQ1LS4yNy4yMTgtLjU5OC4yMTgtLjk4NXYtNS41NWwuMTUtMS42NzVabS01LjAyNSAzLjc4di0uMTQ0YzAtLjU2LjA2OS0xLjA3LjIwNS0xLjUzMS4xMzctLjQ2NS4zMzMtLjg2NC41ODgtMS4xOTYuMjU1LS4zMzguNTY1LS41OTUuOTMtLjc3M2EyLjcyOSAyLjcyOSAwIDAgMSAxLjIzNy0uMjczYy40NzkgMCAuODg3LjA4NiAxLjIyNC4yNi4zNDIuMTczLjYyNi40MjEuODU0Ljc0NS4yMjguMzE5LjQwNi43MDEuNTM0IDEuMTQ4LjEzMi40NDIuMjMuOTM0LjI5NCAxLjQ3N3YuNDU4YTcuNCA3LjQgMCAwIDEtLjMwMSAxLjQ0OSA0IDQgMCAwIDEtLjU2IDEuMTM1Yy0uMjMzLjMxOS0uNTIuNTY1LS44NjIuNzM4LS4zMzcuMTczLS43MzYuMjYtMS4xOTYuMjYtLjQ1MiAwLS44Ni0uMDk0LTEuMjI0LS4yOGEyLjc4MiAyLjc4MiAwIDAgMS0uOTMtLjc4NyAzLjcyMyAzLjcyMyAwIDAgMS0uNTg4LTEuMTkgNS4yMzggNS4yMzggMCAwIDEtLjIwNS0xLjQ5NlptMS42NDgtLjE0NHYuMTQ0YzAgLjMzNy4wMzIuNjUxLjA5Ni45NDMuMDY4LjI5Mi4xNy41NS4zMDcuNzczLjE0MS4yMTguMzIuMzkxLjUzMy41Mi4yMTkuMTIyLjQ3Ny4xODQuNzczLjE4NC4zODcgMCAuNzA0LS4wODIuOTUtLjI0Ni4yNS0uMTY0LjQ0Mi0uMzg2LjU3NC0uNjYzLjEzNy0uMjgzLjIzMy0uNTk4LjI4Ny0uOTQ0di0xLjIzN2EzLjAyMyAzLjAyMyAwIDAgMC0uMTctLjc1MiAyLjAxMSAyLjAxMSAwIDAgMC0uMzM2LS42MDggMS4zOTkgMS4zOTkgMCAwIDAtLjUzMy0uNDFjLS4yMTQtLjEtLjQ2Ny0uMTUxLS43NTktLjE1MS0uMjk2IDAtLjU1My4wNjQtLjc3Mi4xOTFhMS41NjkgMS41NjkgMCAwIDAtLjU0LjUyN2MtLjEzNy4yMjMtLjI0LjQ4My0uMzA4Ljc3OWE0LjIyMiA0LjIyMiAwIDAgMC0uMTAyLjk1Wm0xMC4wOTMgMy44OTdhMy45NDQgMy45NDQgMCAwIDEtMS40ODMtLjI2NyAzLjI3NSAzLjI3NSAwIDAgMS0xLjEyMS0uNzU5IDMuMzY0IDMuMzY0IDAgMCAxLS43MDQtMS4xNDEgMy45OTkgMy45OTkgMCAwIDEtLjI0Ni0xLjQxNXYtLjI3NGMwLS41NzguMDg0LTEuMTAzLjI1My0xLjU3Mi4xNjgtLjQ3LjQwMy0uODcuNzA0LTEuMjAzLjMtLjMzNy42NTYtLjU5NSAxLjA2Ni0uNzczLjQxLS4xNzcuODU1LS4yNjYgMS4zMzMtLjI2Ni41MjkgMCAuOTkyLjA4OSAxLjM4OC4yNjYuMzk2LjE3OC43MjUuNDI5Ljk4NC43NTIuMjY1LjMyLjQ2LjcuNTg4IDEuMTQyLjEzMi40NDIuMTk5LjkzLjE5OSAxLjQ2M3YuNzA0aC01LjcxNVYyNC4xMWg0LjA4N3YtLjEzYTIuMzEyIDIuMzEyIDAgMCAwLS4xNzctLjgzNCAxLjQxNyAxLjQxNyAwIDAgMC0uNDg2LS42MjhjLS4yMTgtLjE2LS41MS0uMjQtLjg3NS0uMjQtLjI3MyAwLS41MTcuMDYtLjczMS4xNzgtLjIxLjExNC0uMzg1LjI4LS41MjYuNWEyLjYzIDIuNjMgMCAwIDAtLjMyOS43OTJjLS4wNzIuMzA1LS4xMDkuNjUtLjEwOSAxLjAzMnYuMjc0YzAgLjMyMy4wNDMuNjI0LjEzLjkwMi4wOTEuMjczLjIyMy41MTMuMzk3LjcxOC4xNzMuMjA1LjM4Mi4zNjcuNjI4LjQ4NS4yNDYuMTE0LjUyNy4xNzEuODQxLjE3MS4zOTcgMCAuNzUtLjA4IDEuMDYtLjI0LjMxLS4xNTkuNTc5LS4zODQuODA2LS42NzZsLjg2OS44NGMtLjE2LjIzMy0uMzY3LjQ1Ny0uNjIzLjY3LS4yNTUuMjEtLjU2Ny4zODEtLjkzNi41MTRhMy43MjcgMy43MjcgMCAwIDEtMS4yNzIuMTk4Wm02LjEzLTUuOTU0VjI4LjVoLTEuNjQ4di03LjM5NmgxLjU1MmwuMDk1IDEuNTc5Wm0tLjI5NCAxLjg0NS0uNTM0LS4wMDZhNC44MTIgNC44MTIgMCAwIDEgLjIxOS0xLjQ0M2MuMTQ2LS40MzcuMzQ2LS44MTMuNjAyLTEuMTI4LjI2LS4zMTQuNTctLjU1Ni45My0uNzI0LjM2LS4xNzQuNzYtLjI2IDEuMjAyLS4yNi4zNTYgMCAuNjc3LjA1Ljk2NC4xNS4yOTIuMDk2LjU0LjI1My43NDUuNDcyLjIxLjIxOS4zNy41MDMuNDc5Ljg1NC4xMDkuMzQ3LjE2NC43NzMuMTY0IDEuMjc5VjI4LjVoLTEuNjU0di00Ljc4NWMwLS4zNTYtLjA1My0uNjM2LS4xNTgtLjg0MWEuODc4Ljg3OCAwIDAgMC0uNDQ0LS40NDRjLS4xOTEtLjA5MS0uNDMxLS4xMzctLjcxOC0uMTM3LS4yODIgMC0uNTM1LjA2LS43NTguMTc4LS4yMjQuMTE4LS40MTMuMjgtLjU2OC40ODUtLjE1LjIwNS0uMjY2LjQ0Mi0uMzQ5LjcxMWEyLjkzOCAyLjkzOCAwIDAgMC0uMTIzLjg2MVptMTQuOTUxIDIuNDRWMThoMS42NTR2MTAuNWgtMS40OTdsLS4xNTctMS41MzFabS00LjgxMy0yLjA4NHYtLjE0NGMwLS41Ni4wNjYtMS4wNy4xOTgtMS41MzEuMTMzLS40NjUuMzI0LS44NjQuNTc1LTEuMTk2LjI1LS4zMzguNTU2LS41OTUuOTE2LS43NzMuMzYtLjE4Mi43NjUtLjI3MyAxLjIxNi0uMjczLjQ0NyAwIC44MzkuMDg2IDEuMTc2LjI2LjMzNy4xNzMuNjI1LjQyMS44NjIuNzQ1LjIzNy4zMTkuNDI2LjcwMS41NjcgMS4xNDguMTQxLjQ0Mi4yNDEuOTM0LjMwMSAxLjQ3N3YuNDU4YTcuNDczIDcuNDczIDAgMCAxLS4zMDEgMS40NDljLS4xNDEuNDM3LS4zMy44MTYtLjU2NyAxLjEzNWEyLjQ1MyAyLjQ1MyAwIDAgMS0uODY5LjczOGMtLjMzNy4xNzMtLjczMS4yNi0xLjE4Mi4yNi0uNDQ3IDAtLjg1LS4wOTQtMS4yMS0uMjhhMi42NjIgMi42NjIgMCAwIDEtLjkwOS0uNzg3IDMuNzEyIDMuNzEyIDAgMCAxLS41NzUtMS4xOSA1LjQxMyA1LjQxMyAwIDAgMS0uMTk4LTEuNDk2Wm0xLjY0OC0uMTQ0di4xNDRjMCAuMzM3LjAyOS42NTEuMDg4Ljk0My4wNjQuMjkyLjE2Mi41NS4yOTQuNzczLjEzMy4yMTguMzAzLjM5MS41MTMuNTIuMjE0LjEyMi40Ny4xODQuNzY2LjE4NC4zNzMgMCAuNjgxLS4wODIuOTIzLS4yNDYuMjQxLS4xNjQuNDMtLjM4Ni41NjctLjY2My4xNDEtLjI4My4yMzctLjU5OC4yODctLjk0NHYtMS4yMzdhMi45ODggMi45ODggMCAwIDAtLjE3MS0uNzUyIDEuOTk2IDEuOTk2IDAgMCAwLS4zMzUtLjYwOCAxLjQwNSAxLjQwNSAwIDAgMC0uNTI2LS40MSAxLjY1IDEuNjUgMCAwIDAtLjczMi0uMTUxYy0uMyAwLS41NTYuMDY0LS43NjUuMTkxLS4yMS4xMjgtLjM4My4zMDMtLjUyLjUyN2EyLjYgMi42IDAgMCAwLS4yOTQuNzc5IDQuNTYzIDQuNTYzIDAgMCAwLS4wOTUuOTVabTguNTU1LTMuNjM2VjI4LjVoLTEuNjU0di03LjM5NmgxLjY1NFptLTEuNzYzLTEuOTQyYzAtLjI1LjA4Mi0uNDU4LjI0Ni0uNjIyLjE2OC0uMTY5LjQwMS0uMjUzLjY5Ny0uMjUzLjI5MiAwIC41MjIuMDg0LjY5LjI1M2EuODMuODMgMCAwIDEgLjI1My42MjIuODIyLjgyMiAwIDAgMS0uMjUzLjYxNWMtLjE2OC4xNjQtLjM5OC4yNDYtLjY5LjI0Ni0uMjk2IDAtLjUyOS0uMDgyLS42OTctLjI0NmEuODM1LjgzNSAwIDAgMS0uMjQ2LS42MTVabTMuNTA0IDUuNzIydi0uMTU3YzAtLjUzNC4wNzctMS4wMjguMjMyLTEuNDg0LjE1NS0uNDYuMzc4LS44NTkuNjctMS4xOTZhMy4wMiAzLjAyIDAgMCAxIDEuMDgtLjc5MyAzLjUxMyAzLjUxMyAwIDAgMSAxLjQ0OS0uMjg3Yy41NDIgMCAxLjAyNi4wOTYgMS40NDkuMjg3LjQyOS4xODcuNzkxLjQ1MSAxLjA4Ny43OTMuMjk3LjMzNy41MjIuNzM2LjY3NyAxLjE5Ni4xNTUuNDU2LjIzMi45NS4yMzIgMS40ODR2LjE1N2MwIC41MzMtLjA3NyAxLjAyNy0uMjMyIDEuNDgzLS4xNTUuNDU2LS4zOC44NTUtLjY3NyAxLjE5N2EzLjEzMiAzLjEzMiAwIDAgMS0xLjA4Ljc5MmMtLjQyNC4xODctLjkwNC4yOC0xLjQ0Mi4yOGEzLjYwNyAzLjYwNyAwIDAgMS0xLjQ1Ni0uMjggMy4xMzIgMy4xMzIgMCAwIDEtMS4wOC0uNzkzIDMuNTM4IDMuNTM4IDAgMCAxLS42NzctMS4xOTYgNC41NzkgNC41NzkgMCAwIDEtLjIzMi0xLjQ4M1ptMS42NDctLjE1N3YuMTU3YzAgLjMzMi4wMzQuNjQ3LjEwMy45NDMuMDY4LjI5Ni4xNzUuNTU2LjMyMS43OC4xNDYuMjIzLjMzMi4zOTguNTYuNTI2LjIyOC4xMjcuNDk5LjE5MS44MTQuMTkxLjMwNSAwIC41NjktLjA2NC43OTMtLjE5MS4yMjgtLjEyOC40MTUtLjMwMy41Ni0uNTI3LjE0Ni0uMjIzLjI1My0uNDgzLjMyMi0uNzc5YTMuOTMgMy45MyAwIDAgMCAuMTA5LS45NDN2LS4xNTdjMC0uMzI5LS4wMzYtLjYzOS0uMTA5LS45M2EyLjQxIDIuNDEgMCAwIDAtLjMyOC0uNzg2IDEuNTcgMS41NyAwIDAgMC0uNTYxLS41MzQgMS41NDMgMS41NDMgMCAwIDAtLjgtLjE5OGMtLjMxIDAtLjU3OS4wNjYtLjgwNi4xOThhMS41ODQgMS41ODQgMCAwIDAtLjU1NC41MzRjLS4xNDYuMjI4LS4yNTMuNDktLjMyMS43ODZhNC4wMjggNC4wMjggMCAwIDAtLjEwMy45M1ptNy45NjgtMy42MjMgMS40MDggMi40NTQgMS40MzUtMi40NTRoMS44MTJsLTIuMjQyIDMuNjMgMi4zMzEgMy43NjZoLTEuODEybC0xLjUwNC0yLjU1Ny0xLjUwMyAyLjU1N2gtMS44MTlsMi4zMjQtMy43NjctMi4yMzUtMy42M2gxLjgwNVptNy43OTcgMFYyOC41aC0xLjY1NXYtNy4zOTZoMS42NTVabS0xLjc2NC0xLjk0MmMwLS4yNS4wODItLjQ1OC4yNDYtLjYyMi4xNjktLjE2OS40MDEtLjI1My42OTctLjI1My4yOTIgMCAuNTIyLjA4NC42OTEuMjUzYS44MzQuODM0IDAgMCAxIC4yNTMuNjIyLjgyNS44MjUgMCAwIDEtLjI1My42MTVjLS4xNjkuMTY0LS4zOTkuMjQ2LS42OTEuMjQ2LS4yOTYgMC0uNTI4LS4wODItLjY5Ny0uMjQ2YS44MzUuODM1IDAgMCAxLS4yNDYtLjYxNVptOC4zMyA3LjgwN1YxOGgxLjY1NHYxMC41aC0xLjQ5N2wtLjE1Ny0xLjUzMVptLTQuODEyLTIuMDg1di0uMTQ0YzAtLjU2LjA2Ni0xLjA3LjE5OC0xLjUzMS4xMzItLjQ2NS4zMjMtLjg2NC41NzQtMS4xOTYuMjUxLS4zMzguNTU2LS41OTUuOTE2LS43NzMuMzYtLjE4Mi43NjYtLjI3MyAxLjIxNy0uMjczLjQ0NiAwIC44MzguMDg2IDEuMTc2LjI2LjMzNy4xNzMuNjI0LjQyMS44NjEuNzQ1LjIzNy4zMTkuNDI2LjcwMS41NjcgMS4xNDguMTQyLjQ0Mi4yNDIuOTM0LjMwMSAxLjQ3N3YuNDU4YTcuMzI3IDcuMzI3IDAgMCAxLS4zMDEgMS40NDljLS4xNDEuNDM3LS4zMy44MTYtLjU2NyAxLjEzNWEyLjQ1IDIuNDUgMCAwIDEtLjg2OC43MzhjLS4zMzcuMTczLS43MzIuMjYtMS4xODMuMjYtLjQ0NiAwLS44NS0uMDk0LTEuMjEtLjI4YTIuNjggMi42OCAwIDAgMS0uOTA5LS43ODcgMy42OSAzLjY5IDAgMCAxLS41NzQtMS4xOSA1LjQxMyA1LjQxMyAwIDAgMS0uMTk4LTEuNDk2Wm0xLjY0Ny0uMTQ0di4xNDRjMCAuMzM3LjAzLjY1MS4wODkuOTQzLjA2NC4yOTIuMTYyLjU1LjI5NC43NzMuMTMyLjIxOC4zMDMuMzkxLjUxMi41Mi4yMTUuMTIyLjQ3LjE4NC43NjYuMTg0LjM3NCAwIC42ODEtLjA4Mi45MjMtLjI0NmExLjY3IDEuNjcgMCAwIDAgLjU2Ny0uNjYzYy4xNDItLjI4My4yMzctLjU5OC4yODctLjk0NHYtMS4yMzdhMy4wMzEgMy4wMzEgMCAwIDAtLjE3LS43NTIgMi4wMTggMi4wMTggMCAwIDAtLjMzNS0uNjA4IDEuNDE2IDEuNDE2IDAgMCAwLS41MjctLjQxYy0uMjA1LS4xLS40NDktLjE1MS0uNzMxLS4xNTEtLjMwMSAwLS41NTYuMDY0LS43NjYuMTkxYTEuNTUgMS41NSAwIDAgMC0uNTE5LjUyNyAyLjU0OCAyLjU0OCAwIDAgMC0uMjk0Ljc3OSA0LjQ5NSA0LjQ5NSAwIDAgMC0uMDk2Ljk1Wm0xMC4wNTMgMy44OTdhMy45NDYgMy45NDYgMCAwIDEtMS40ODQtLjI2NyAzLjI4IDMuMjggMCAwIDEtMS4xMjEtLjc1OSAzLjM2NyAzLjM2NyAwIDAgMS0uNzA0LTEuMTQxIDMuOTk5IDMuOTk5IDAgMCAxLS4yNDYtMS40MTV2LS4yNzRjMC0uNTc4LjA4NC0xLjEwMy4yNTMtMS41NzIuMTY5LS40Ny40MDMtLjg3LjcwNC0xLjIwM2EzLjAyNiAzLjAyNiAwIDAgMSAxLjA2Ni0uNzczIDMuMzIxIDMuMzIxIDAgMCAxIDEuMzMzLS4yNjZjLjUyOSAwIC45OTIuMDg5IDEuMzg4LjI2Ni4zOTcuMTc4LjcyNS40MjkuOTg1Ljc1Mi4yNjQuMzIuNDYuNy41ODcgMS4xNDIuMTMzLjQ0Mi4xOTkuOTMuMTk5IDEuNDYzdi43MDRoLTUuNzE1VjI0LjExaDQuMDg4di0uMTNhMi4zMTggMi4zMTggMCAwIDAtLjE3OC0uODM0IDEuNDIzIDEuNDIzIDAgMCAwLS40ODUtLjYyOGMtLjIxOS0uMTYtLjUxMS0uMjQtLjg3NS0uMjQtLjI3NCAwLS41MTguMDYtLjczMi4xNzhhMS40NSAxLjQ1IDAgMCAwLS41MjYuNSAyLjYyMSAyLjYyMSAwIDAgMC0uMzI4Ljc5MiA0LjQ1IDQuNDUgMCAwIDAtLjExIDEuMDMydi4yNzRjMCAuMzIzLjA0NC42MjQuMTMuOTAyLjA5MS4yNzMuMjI0LjUxMy4zOTcuNzE4LjE3My4yMDUuMzgzLjM2Ny42MjkuNDg1LjI0Ni4xMTQuNTI2LjE3MS44NC4xNzEuMzk3IDAgLjc1LS4wOCAxLjA2LS4yNC4zMS0uMTU5LjU3OS0uMzg0LjgwNy0uNjc2bC44NjguODRjLS4xNi4yMzMtLjM2Ny40NTctLjYyMi42Ny0uMjU1LjIxLS41NjguMzgxLS45MzcuNTE0YTMuNzIyIDMuNzIyIDAgMCAxLTEuMjcxLjE5OFoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii4zOCIgZD0iTTUyLjg3NCA0MS41ODV2LjkxNWgtMy4zNjF2LS45MTVoMy4zNlptLTMuMDQtNS43OFY0Mi41SDQ4LjY4di02LjY5NWgxLjE1NVptNi44MjIgNS42OTdWMzkuMTNjMC0uMTc4LS4wMzItLjMzMS0uMDk2LS40NmEuNjc2LjY3NiAwIDAgMC0uMjk0LS4yOTkgMS4wMDYgMS4wMDYgMCAwIDAtLjQ4OC0uMTA2Yy0uMTggMC0uMzM3LjAzLS40NjkuMDkyYS43NTguNzU4IDAgMCAwLS4zMDguMjQ4LjU5OS41OTkgMCAwIDAtLjExLjM1NWgtMS4xMDRjMC0uMTk3LjA0OC0uMzg3LjE0My0uNTcuMDk1LS4xODQuMjMzLS4zNDkuNDE0LS40OTMuMTgtLjE0NC4zOTctLjI1Ny42NDgtLjM0LjI1MS0uMDgzLjUzMy0uMTI0Ljg0Ni0uMTI0LjM3NCAwIC43MDUuMDYzLjk5My4xODguMjkxLjEyNi41Mi4zMTYuNjg1LjU3LjE2OS4yNTIuMjUzLjU2OC4yNTMuOTQ4djIuMjExYzAgLjIyNy4wMTYuNDMxLjA0Ni42MTIuMDM0LjE3OC4wODEuMzMzLjE0My40NjR2LjA3NGgtMS4xMzZhMS45NjUgMS45NjUgMCAwIDEtLjEyNC0uNDU1IDMuNzMgMy43MyAwIDAgMS0uMDQxLS41NDNabS4xNjEtMi4wMjguMDEuNjg2aC0uNzk2Yy0uMjA1IDAtLjM4Ni4wMi0uNTQyLjA2YTEuMTE1IDEuMTE1IDAgMCAwLS4zOTEuMTY1LjcxOC43MTggMCAwIDAtLjMxMy42MmMwIC4xMzMuMDMuMjUzLjA5Mi4zNjQuMDYxLjEwNy4xNS4xOTEuMjY3LjI1My4xMi4wNjEuMjYzLjA5Mi40MzIuMDkyLjIyNyAwIC40MjUtLjA0Ni41OTMtLjEzOGExLjIyIDEuMjIgMCAwIDAgLjQwNS0uMzQ1Ljc1Ljc1IDAgMCAwIC4xNTYtLjM5bC4zNTkuNDkxYy0uMDM3LjEyNi0uMS4yNi0uMTg5LjQwNWExLjk2MSAxLjk2MSAwIDAgMS0uMzUuNDE0IDEuNzM1IDEuNzM1IDAgMCAxLTEuMTkuNDQxYy0uMzI1IDAtLjYxNS0uMDY0LS44Ny0uMTkzYTEuNTQ0IDEuNTQ0IDAgMCAxLS41OTctLjUyOSAxLjM3MSAxLjM3MSAwIDAgMS0uMjE2LS43NTljMC0uMjYzLjA0OS0uNDk2LjE0Ny0uNjk4YTEuMzcgMS4zNyAwIDAgMSAuNDQyLS41MTVjLjE5Ni0uMTM4LjQzNS0uMjQzLjcxNy0uMzEzLjI4Mi0uMDc0LjYwNC0uMTEuOTY1LS4xMWguODdabTUuMDk4IDEuNjc5YzAtLjExLS4wMjgtLjIxLS4wODMtLjMtLjA1NS0uMDkxLS4xNi0uMTc0LS4zMTctLjI0N2EzLjA3OCAzLjA3OCAwIDAgMC0uNjgtLjIwMyA1Ljg2NCA1Ljg2NCAwIDAgMS0uNzI3LS4yMDcgMi4zMDUgMi4zMDUgMCAwIDEtLjU2LS4yOTggMS4yNTcgMS4yNTcgMCAwIDEtLjM2NC0uNDE0IDEuMTQ2IDEuMTQ2IDAgMCAxLS4xMjktLjU1MmMwLS4yMDIuMDQ0LS4zOTQuMTMzLS41NzUuMDktLjE4LjIxNi0uMzQuMzgyLS40NzguMTY2LS4xMzguMzY2LS4yNDcuNjAyLS4zMjYuMjQtLjA4LjUwNi0uMTIuOC0uMTIuNDE3IDAgLjc3NC4wNyAxLjA3Mi4yMTIuMy4xMzcuNTMuMzI2LjY5LjU2NS4xNTkuMjM2LjIzOS41MDMuMjM5LjhoLTEuMTA5YS43MDcuNzA3IDAgMCAwLS4xLS4zNjguNzA1LjcwNSAwIDAgMC0uMjk1LS4yOCAxLjAwOCAxLjAwOCAwIDAgMC0uNDk3LS4xMWMtLjE5IDAtLjM0OC4wMy0uNDczLjA5MWEuNjQ4LjY0OCAwIDAgMC0uMjc2LjIzLjU4Ny41ODcgMCAwIDAtLjA0MS41MzguNTMuNTMgMCAwIDAgLjE2NS4xOGMuMDc3LjA1Mi4xODEuMS4zMTMuMTQ3LjEzNS4wNDYuMzAzLjA5LjUwNi4xMzMuMzguMDguNzA2LjE4My45NzkuMzA4LjI3Ni4xMjMuNDg3LjI4Mi42MzQuNDc4LjE0OC4xOTQuMjIxLjQzOS4yMjEuNzM2IDAgLjIyLS4wNDcuNDIzLS4xNDIuNjA3LS4wOTIuMTgtLjIyNy4zMzktLjQwNS40NzNhMi4wMyAyLjAzIDAgMCAxLS42NC4zMDljLS4yNDQuMDczLS41Mi4xMS0uODI3LjExLS40NSAwLS44MzItLjA4LTEuMTQ1LS4yNGExLjgyNiAxLjgyNiAwIDAgMS0uNzEyLS42MiAxLjQ3IDEuNDcgMCAwIDEtLjI0LS43OWgxLjA3MmEuODMuODMgMCAwIDAgLjE3LjQ5MWMuMTA0LjEyLjIzMy4yMDcuMzg2LjI2Mi4xNTcuMDUzLjMxNy4wNzguNDgzLjA3OC4yIDAgLjM2Ni0uMDI2LjUwMS0uMDc4YS43MjEuNzIxIDAgMCAwIC4zMDgtLjIyLjUyNy41MjcgMCAwIDAgLjEwNi0uMzIyWm00LjUzMi0zLjYyOHYuODFoLTIuODA1di0uODFoMi44MDVabS0xLjk5NS0xLjIxOGgxLjEwOHY0LjgxOGMwIC4xNTQuMDIxLjI3Mi4wNjQuMzU0LjA0Ni4wOC4xMDkuMTM0LjE4OS4xNjEuMDguMDI4LjE3My4wNDIuMjguMDQyYTIuMjA5IDIuMjA5IDAgMCAwIC4zOS0uMDQxbC4wMDUuODQ1Yy0uMDkxLjAyOC0uMTk5LjA1Mi0uMzIxLjA3NC0uMTIuMDIxLS4yNTguMDMyLS40MTQuMDMyLS4yNTUgMC0uNDgtLjA0NC0uNjc2LS4xMzNhLjk5NS45OTUgMCAwIDEtLjQ2LS40NDZjLS4xMS0uMjA2LS4xNjUtLjQ3OS0uMTY1LS44MTl2LTQuODg4Wm04LjU4IDUuMDJ2LTMuODAyaDEuMTE0VjQyLjVoLTEuMDQ5bC0uMDY0LTEuMTcyWm0uMTU3LTEuMDM0LjM3My0uMDFjMCAuMzM1LS4wMzcuNjQzLS4xMS45MjVhMi4xNDIgMi4xNDIgMCAwIDEtLjM0LjczMSAxLjU5IDEuNTkgMCAwIDEtLjU5LjQ4MyAxLjk5IDEuOTkgMCAwIDEtLjg2LjE3Yy0uMjQxIDAtLjQ2NC0uMDM1LS42NjYtLjEwNmExLjM2NiAxLjM2NiAwIDAgMS0uNTI0LS4zMjYgMS40ODUgMS40ODUgMCAwIDEtLjMzNi0uNTc1IDIuNjU1IDIuNjU1IDAgMCAxLS4xMi0uODQ2di0zLjIxNGgxLjEwOXYzLjIyM2MwIC4xODEuMDIxLjMzMy4wNjQuNDU1YS43Ny43NyAwIDAgMCAuMTc1LjI5Yy4wNzMuMDc0LjE2LjEyNi4yNTcuMTU2LjA5OS4wMzEuMjAzLjA0Ni4zMTMuMDQ2LjMxNiAwIC41NjQtLjA2MS43NDUtLjE4NC4xODQtLjEyNS4zMTQtLjI5NC4zOS0uNTA1LjA4LS4yMTIuMTItLjQ1LjEyLS43MTNabTMuNDAxLTEuODEydjUuOTMyaC0xLjEwOHYtNi44ODhoMS4wMmwuMDg4Ljk1NlptMy4yNDIgMS40ODZ2LjA5NmMwIC4zNjItLjA0My42OTctLjEyOSAxLjAwN2EyLjQ2IDIuNDYgMCAwIDEtLjM3Mi44MDUgMS43MTQgMS43MTQgMCAwIDEtLjYwMy41MjhjLS4yMzkuMTI2LS41MTUuMTg5LS44MjcuMTg5LS4zMSAwLS41ODEtLjA1Ny0uODE0LS4xN2ExLjY2NyAxLjY2NyAwIDAgMS0uNTg0LS40OTIgMi42NyAyLjY3IDAgMCAxLS4zODYtLjc0NSA0Ljc4MiA0Ljc4MiAwIDAgMS0uMjAzLS45NDd2LS4zNzNjLjA0LS4zNjQuMTA4LS42OTYuMjAzLS45OTNhMi42OSAyLjY5IDAgMCAxIC4zODYtLjc2OGMuMTYtLjIxNC4zNTQtLjM4LjU4NC0uNDk2YTEuNzYgMS43NiAwIDAgMSAuODA0LS4xNzVjLjMxMyAwIC41OS4wNjEuODMzLjE4NC4yNDIuMTIuNDQ2LjI5MS42MTEuNTE1LjE2Ni4yMi4yOS40ODcuMzczLjguMDgyLjMxLjEyNC42NTQuMTI0IDEuMDM1Wm0tMS4xMDguMDk2di0uMDk2YzAtLjIzLS4wMjItLjQ0My0uMDY1LS42NC0uMDQzLS4xOTktLjExLS4zNzQtLjIwMi0uNTI0YS45OS45OSAwIDAgMC0uMzU0LS4zNS45NjMuOTYzIDAgMCAwLS41MS0uMTI4Yy0uMTk3IDAtLjM2NS4wMzQtLjUwNi4xMDFhLjk3Ljk3IDAgMCAwLS4zNTQuMjcxIDEuMjgyIDEuMjgyIDAgMCAwLS4yMi40MWMtLjA1My4xNTMtLjA5LjMyLS4xMTEuNXYuODkzYy4wMzcuMjIuMS40MjMuMTg4LjYwNy4wOS4xODQuMjE1LjMzLjM3Ny40NDEuMTY2LjEwOC4zNzcuMTYxLjYzNS4xNjEuMTk5IDAgLjM3LS4wNDMuNTEtLjEyOS4xNDEtLjA4NS4yNTYtLjIwMy4zNDUtLjM1NC4wOTItLjE1My4xNi0uMzMuMjAyLS41MjguMDQzLS4yLjA2NS0uNDExLjA2NS0uNjM1Wm01LjI4MSAxLjQwN3YtNi4wMzJoMS4xMTNWNDIuNWgtMS4wMDdsLS4xMDYtMS4wM1ptLTMuMjM3LTEuNDAydi0uMDk3YzAtLjM3Ny4wNDUtLjcyLjEzNC0xLjAzLjA4OC0uMzEzLjIxNy0uNTguMzg2LS44MDUuMTY4LS4yMjYuMzc0LS40LjYxNi0uNTIuMjQyLS4xMjIuNTE1LS4xODMuODE4LS4xODMuMyAwIC41NjQuMDU4Ljc5MS4xNzUuMjI3LjExNi40Mi4yODMuNTguNWEyLjYgMi42IDAgMCAxIC4zODEuNzczYy4wOTUuMjk4LjE2My42MjkuMjAzLjk5NHYuMzA4Yy0uMDQuMzU1LS4xMDguNjgtLjIwMy45NzRhMi42MiAyLjYyIDAgMCAxLS4zODEuNzY0Yy0uMTYuMjE0LS4zNTQuMzgtLjU4NC40OTZhMS43MTkgMS43MTkgMCAwIDEtLjc5Ni4xNzVjLS4zIDAtLjU3Mi0uMDYzLS44MTQtLjE4OWExLjc5OCAxLjc5OCAwIDAgMS0uNjExLS41MjggMi40OSAyLjQ5IDAgMCAxLS4zODYtLjggMy42MzYgMy42MzYgMCAwIDEtLjEzNC0xLjAwN1ptMS4xMDgtLjA5N3YuMDk3YzAgLjIyNy4wMi40MzguMDYuNjM0LjA0My4xOTYuMTA5LjM3LjE5OC41Mi4wODkuMTQ3LjIwNC4yNjMuMzQ1LjM1LjE0NC4wODIuMzE2LjEyMy41MTUuMTIzLjI1MSAwIC40NTgtLjA1NS42Mi0uMTY1LjE2My0uMTEuMjktLjI2LjM4Mi0uNDQ2LjA5NS0uMTkuMTYtLjQwMi4xOTMtLjYzNXYtLjgzMmEyLjAzIDIuMDMgMCAwIDAtLjExNS0uNTA2IDEuMzUgMS4zNSAwIDAgMC0uMjI1LS40MDkuOTQ4Ljk0OCAwIDAgMC0uMzU0LS4yNzYgMS4xMSAxLjExIDAgMCAwLS40OTItLjEwMS45NzIuOTcyIDAgMCAwLS41MTUuMTI5IDEuMDQzIDEuMDQzIDAgMCAwLS4zNS4zNTRjLS4wODguMTUtLjE1NC4zMjUtLjE5Ny41MjQtLjA0My4yLS4wNjUuNDEyLS4wNjUuNjRabTcuNDAyIDEuNTMxVjM5LjEzYzAtLjE3OC0uMDMzLS4zMzEtLjA5Ny0uNDZhLjY3Ni42NzYgMCAwIDAtLjI5NC0uMjk5IDEuMDA3IDEuMDA3IDAgMCAwLS40ODgtLjEwNmMtLjE4IDAtLjMzNy4wMy0uNDY5LjA5MmEuNzU5Ljc1OSAwIDAgMC0uMzA4LjI0OC42LjYgMCAwIDAtLjExLjM1NWgtMS4xMDNjMC0uMTk3LjA0Ny0uMzg3LjE0Mi0uNTcuMDk1LS4xODQuMjMzLS4zNDkuNDE0LS40OTMuMTgtLjE0NC4zOTctLjI1Ny42NDgtLjM0LjI1Mi0uMDgzLjUzNC0uMTI0Ljg0Ni0uMTI0LjM3NCAwIC43MDUuMDYzLjk5My4xODguMjkxLjEyNi41Mi4zMTYuNjg1LjU3LjE2OS4yNTIuMjUzLjU2OC4yNTMuOTQ4djIuMjExYzAgLjIyNy4wMTYuNDMxLjA0Ni42MTIuMDM0LjE3OC4wODEuMzMzLjE0My40NjR2LjA3NGgtMS4xMzZhMS45NiAxLjk2IDAgMCAxLS4xMjQtLjQ1NSAzLjcxNiAzLjcxNiAwIDAgMS0uMDQxLS41NDNabS4xNi0yLjAyOC4wMS42ODZoLS43OTZjLS4yMDUgMC0uMzg2LjAyLS41NDIuMDZhMS4xMTYgMS4xMTYgMCAwIDAtLjM5MS4xNjUuNzIuNzIgMCAwIDAtLjMxMy42MmMwIC4xMzMuMDMuMjUzLjA5Mi4zNjQuMDYxLjEwNy4xNS4xOTEuMjY3LjI1My4xMi4wNjEuMjYzLjA5Mi40MzIuMDkyLjIyNyAwIC40MjUtLjA0Ni41OTMtLjEzOC4xNzItLjA5NS4zMDctLjIxLjQwNS0uMzQ1YS43NTEuNzUxIDAgMCAwIC4xNTYtLjM5bC4zNTkuNDkxYy0uMDM3LjEyNi0uMS4yNi0uMTg5LjQwNWExLjk2NCAxLjk2NCAwIDAgMS0uMzUuNDE0IDEuNzM2IDEuNzM2IDAgMCAxLTEuMTkuNDQxYy0uMzI1IDAtLjYxNS0uMDY0LS44NjktLjE5M2ExLjU0NSAxLjU0NSAwIDAgMS0uNTk4LS41MjkgMS4zNzIgMS4zNzIgMCAwIDEtLjIxNi0uNzU5YzAtLjI2My4wNS0uNDk2LjE0Ny0uNjk4LjEwMS0uMjA2LjI0OS0uMzc3LjQ0Mi0uNTE1LjE5Ni0uMTM4LjQzNS0uMjQzLjcxNy0uMzEzLjI4Mi0uMDc0LjYwNC0uMTEuOTY2LS4xMWguODY5Wm00LjU4OC0xLjk0OXYuODFIOTEuMjJ2LS44MWgyLjgwNVptLTEuOTk2LTEuMjE4aDEuMTA4djQuODE4YzAgLjE1NC4wMjIuMjcyLjA2NS4zNTQuMDQ2LjA4LjEwOS4xMzQuMTg4LjE2MS4wOC4wMjguMTczLjA0Mi4yOC4wNDJhMi4yMDkgMi4yMDkgMCAwIDAgLjM5LS4wNDFsLjAwNi44NDVjLS4wOTIuMDI4LS4yLjA1Mi0uMzIyLjA3NC0uMTIuMDIxLS4yNTcuMDMyLS40MTMuMDMyLS4yNTUgMC0uNDgtLjA0NC0uNjc2LS4xMzNhLjk5NC45OTQgMCAwIDEtLjQ2LS40NDZjLS4xMS0uMjA2LS4xNjYtLjQ3OS0uMTY2LS44MTl2LTQuODg4Wm01LjI0IDYuMjg1Yy0uMzY3IDAtLjctLjA2LS45OTctLjE4YTIuMjAyIDIuMjAyIDAgMCAxLS43NTQtLjUxIDIuMjYzIDIuMjYzIDAgMCAxLS40NzQtLjc2OCAyLjY5IDIuNjkgMCAwIDEtLjE2Ni0uOTUxdi0uMTg0YzAtLjM5LjA1Ny0uNzQyLjE3LTEuMDU4YTIuNCAyLjQgMCAwIDEgLjQ3NC0uODFjLjIwMy0uMjI2LjQ0Mi0uNC43MTgtLjUxOS4yNzUtLjEyLjU3NC0uMTc5Ljg5Ni0uMTc5LjM1NiAwIC42NjcuMDYuOTMzLjE4LjI2Ny4xMTkuNDg4LjI4OC42NjMuNTA1LjE3Ny4yMTUuMzEuNDcuMzk1Ljc2OC4wODkuMjk3LjEzMy42MjUuMTMzLjk4NHYuNDc0aC0zLjg0NHYtLjc5NmgyLjc1di0uMDg3YTEuNTU0IDEuNTU0IDAgMCAwLS4xMi0uNTYxLjk1My45NTMgMCAwIDAtLjMyNi0uNDIzYy0uMTQ3LS4xMDctLjM0My0uMTYxLS41ODktLjE2MWExIDEgMCAwIDAtLjQ5Mi4xMi45NzMuOTczIDAgMCAwLS4zNTMuMzM1IDEuNzY3IDEuNzY3IDAgMCAwLS4yMjEuNTMzYy0uMDUuMjA2LS4wNzQuNDM3LS4wNzQuNjk1di4xODRjMCAuMjE3LjAzLjQyLjA4OC42MDcuMDYuMTg0LjE1LjM0NC4yNjYuNDgyLjExNy4xMzguMjU4LjI0Ny40MjMuMzI3LjE2Ni4wNzYuMzU0LjExNS41NjYuMTE1LjI2NyAwIC41MDQtLjA1NC43MTMtLjE2MS4yMDgtLjEwNy4zODktLjI2LjU0Mi0uNDU1bC41ODQuNTY1YTIuMjggMi4yOCAwIDAgMS0uNDE4LjQ1IDIuMDk1IDIuMDk1IDAgMCAxLS42My4zNDZjLS4yNDYuMDg4LS41My4xMzMtLjg1Ni4xMzNabTguNTU0LTYuODFWNDIuNWgtMS4xMDh2LTUuNDAzbC0xLjY0MS41NTd2LS45MTVsMi42MTYtLjk1N2guMTMzWm01LjYyMiA1LjY4OHYtNi4wMzJoMS4xMTNWNDIuNWgtMS4wMDdsLS4xMDYtMS4wM1ptLTMuMjM3LTEuNDAydi0uMDk3YzAtLjM3Ny4wNDQtLjcyLjEzMy0xLjAzYTIuNDIgMi40MiAwIDAgMSAuMzg3LS44MDVjLjE2OC0uMjI2LjM3NC0uNC42MTYtLjUyLjI0Mi0uMTIyLjUxNS0uMTgzLjgxOC0uMTgzLjMwMSAwIC41NjQuMDU4Ljc5MS4xNzUuMjI3LjExNi40Mi4yODMuNTc5LjUuMTYuMjE2LjI4Ny40NzMuMzgyLjc3My4wOTUuMjk4LjE2Mi42MjkuMjAyLjk5NHYuMzA4Yy0uMDQuMzU1LS4xMDcuNjgtLjIwMi45NzRhMi42MDMgMi42MDMgMCAwIDEtLjM4Mi43NjRjLS4xNTkuMjE0LS4zNTQuMzgtLjU4NC40OTZhMS43MTYgMS43MTYgMCAwIDEtLjc5NS4xNzVjLS4zIDAtLjU3Mi0uMDYzLS44MTQtLjE4OWExLjc5MiAxLjc5MiAwIDAgMS0uNjExLS41MjggMi40ODUgMi40ODUgMCAwIDEtLjM4Ny0uOCAzLjYzMiAzLjYzMiAwIDAgMS0uMTMzLTEuMDA3Wm0xLjEwOC0uMDk3di4wOTdjMCAuMjI3LjAyLjQzOC4wNi42MzQuMDQzLjE5Ni4xMDkuMzcuMTk4LjUyLjA4OS4xNDcuMjAzLjI2My4zNDQuMzUuMTQ1LjA4Mi4zMTYuMTIzLjUxNS4xMjMuMjUyIDAgLjQ1OS0uMDU1LjYyMS0uMTY1LjE2My0uMTEuMjktLjI2LjM4Mi0uNDQ2YTIuMDggMi4wOCAwIDAgMCAuMTkzLS42MzV2LS44MzJhMi4wMjUgMi4wMjUgMCAwIDAtLjExNS0uNTA2IDEuMzQ5IDEuMzQ5IDAgMCAwLS4yMjUtLjQwOS45NDguOTQ4IDAgMCAwLS4zNTQtLjI3NiAxLjExIDEuMTEgMCAwIDAtLjQ5Mi0uMTAxLjk3Ljk3IDAgMCAwLS41MTUuMTI5IDEuMDQ2IDEuMDQ2IDAgMCAwLS4zNS4zNTRjLS4wODkuMTUtLjE1NS4zMjUtLjE5Ny41MjQtLjA0My4yLS4wNjUuNDEyLS4wNjUuNjRabTkuOTQzIDEuNTMxVjM5LjEzYzAtLjE3OC0uMDMzLS4zMzEtLjA5Ny0uNDZhLjY3OC42NzggMCAwIDAtLjI5NC0uMjk5IDEuMDEgMS4wMSAwIDAgMC0uNDg4LS4xMDZjLS4xODEgMC0uMzM3LjAzLS40NjkuMDkyYS43Ni43NiAwIDAgMC0uMzA4LjI0OC42MDMuNjAzIDAgMCAwLS4xMS4zNTVoLTEuMTA0YzAtLjE5Ny4wNDgtLjM4Ny4xNDMtLjU3LjA5NS0uMTg0LjIzMy0uMzQ5LjQxNC0uNDkzYTIuMDcgMi4wNyAwIDAgMSAuNjQ4LS4zNGMuMjUxLS4wODMuNTMzLS4xMjQuODQ2LS4xMjQuMzc0IDAgLjcwNS4wNjMuOTkzLjE4OC4yOTEuMTI2LjUyLjMxNi42ODUuNTcuMTY5LjI1Mi4yNTMuNTY4LjI1My45NDh2Mi4yMTFjMCAuMjI3LjAxNi40MzEuMDQ2LjYxMi4wMzQuMTc4LjA4MS4zMzMuMTQzLjQ2NHYuMDc0aC0xLjEzNmExLjk4NiAxLjk4NiAwIDAgMS0uMTI0LS40NTUgMy42NTkgMy42NTkgMCAwIDEtLjA0MS0uNTQzWm0uMTYtMi4wMjguMDEuNjg2aC0uNzk2Yy0uMjA1IDAtLjM4Ni4wMi0uNTQyLjA2YTEuMTEgMS4xMSAwIDAgMC0uMzkxLjE2NS43MjIuNzIyIDAgMCAwLS4yMzUuMjY3Ljc4Mi43ODIgMCAwIDAtLjA3OC4zNTRjMCAuMTMyLjAzMS4yNTIuMDkyLjM2M2EuNjQuNjQgMCAwIDAgLjI2Ny4yNTMuOTM0LjkzNCAwIDAgMCAuNDMyLjA5MiAxLjIxNyAxLjIxNyAwIDAgMCAuOTk4LS40ODMuNzUuNzUgMCAwIDAgLjE1Ni0uMzlsLjM1OS40OTFjLS4wMzcuMTI2LS4xLjI2LS4xODkuNDA1YTEuOTY0IDEuOTY0IDAgMCAxLS4zNDkuNDE0IDEuNzMgMS43MyAwIDAgMS0xLjE5MS40NDFjLS4zMjUgMC0uNjE1LS4wNjQtLjg2OS0uMTkzYTEuNTQxIDEuNTQxIDAgMCAxLS41OTgtLjUyOSAxLjM3MiAxLjM3MiAwIDAgMS0uMjE2LS43NTljMC0uMjYzLjA0OS0uNDk2LjE0Ny0uNjk4LjEwMS0uMjA2LjI0OC0uMzc3LjQ0Mi0uNTE1LjE5Ni0uMTM4LjQzNS0uMjQzLjcxNy0uMzEzLjI4Mi0uMDc0LjYwNC0uMTEuOTY1LS4xMWguODY5Wm01LjQ5NC0xLjk0OWgxLjAwNnY0LjgzN2MwIC40NDgtLjA5NS44MjgtLjI4NSAxLjE0LS4xOS4zMTMtLjQ1NS41NS0uNzk1LjcxMy0uMzQuMTY2LS43MzQuMjQ4LTEuMTgyLjI0OGEyLjc3IDIuNzcgMCAwIDEtMS4zMDYtLjM1IDEuNjggMS42OCAwIDAgMS0uNTM4LS40NzNsLjUyLS42NTNjLjE3OC4yMTIuMzc0LjM2Ny41ODkuNDY1LjIxNC4wOTguNDM5LjE0Ny42NzUuMTQ3LjI1NSAwIC40NzEtLjA0OC42NDktLjE0M2EuOTYxLjk2MSAwIDAgMCAuNDE4LS40MDljLjA5OC0uMTguMTQ3LS40MDEuMTQ3LS42NjJ2LTMuNzMzbC4xMDItMS4xMjdabS0zLjM4IDIuNTQzdi0uMDk3YzAtLjM3Ny4wNDYtLjcyLjEzOC0xLjAzYTIuNDUgMi40NSAwIDAgMSAuMzk1LS44MDVjLjE3Mi0uMjI2LjM4LS40LjYyNi0uNTIuMjQ1LS4xMjIuNTIyLS4xODMuODMyLS4xODMuMzIyIDAgLjU5Ni4wNTguODIzLjE3NS4yMy4xMTYuNDIxLjI4My41NzUuNS4xNTMuMjE2LjI3My40NzMuMzU4Ljc3My4wODkuMjk4LjE1NS42MjkuMTk4Ljk5NHYuMzA4Yy0uMDQuMzU1LS4xMDcuNjgtLjIwMi45NzRhMi42OTcgMi42OTcgMCAwIDEtLjM3Ny43NjRjLS4xNTcuMjE0LS4zNS4zOC0uNTguNDk2YTEuNzM3IDEuNzM3IDAgMCAxLS44MDQuMTc1Yy0uMzA0IDAtLjU3OC0uMDYzLS44MjMtLjE4OWExLjg3OSAxLjg3OSAwIDAgMS0uNjI2LS41MjggMi41MTggMi41MTggMCAwIDEtLjM5NS0uOCAzLjUyIDMuNTIgMCAwIDEtLjEzOC0xLjAwN1ptMS4xMDgtLjA5N3YuMDk3YzAgLjIyNy4wMjIuNDM4LjA2NC42MzQuMDQ2LjE5Ni4xMTUuMzcuMjA3LjUyLjA5NS4xNDcuMjE1LjI2My4zNTkuMzUuMTQ3LjA4Mi4zMi4xMjMuNTIuMTIzLjI2IDAgLjQ3My0uMDU1LjYzOS0uMTY1LjE2OC0uMTEuMjk3LS4yNi4zODYtLjQ0Ni4wOTItLjE5LjE1Ni0uNDAyLjE5My0uNjM1di0uODMyYTIuMDI1IDIuMDI1IDAgMCAwLS4xMTUtLjUwNiAxLjM0OSAxLjM0OSAwIDAgMC0uMjI1LS40MDkuOTQyLjk0MiAwIDAgMC0uMzU5LS4yNzYgMS4xOTMgMS4xOTMgMCAwIDAtLjUxLS4xMDFjLS4xOTkgMC0uMzczLjA0My0uNTIuMTI5YTEuMDU1IDEuMDU1IDAgMCAwLS4zNjMuMzU0IDEuNzUgMS43NSAwIDAgMC0uMjA3LjUyNGMtLjA0Ni4yLS4wNjkuNDEyLS4wNjkuNjRabTQuNDEzLjA5N3YtLjEwNmMwLS4zNTkuMDUyLS42OTEuMTU2LS45OTguMTA0LS4zMS4yNTQtLjU3OC40NTEtLjgwNGEyLjAzIDIuMDMgMCAwIDEgLjcyNi0uNTM0Yy4yODgtLjEyOS42MTMtLjE5My45NzUtLjE5My4zNjUgMCAuNjkuMDY0Ljk3NS4xOTMuMjg4LjEyNi41MzEuMzA0LjczMS41MzQuMTk5LjIyNi4zNTEuNDk1LjQ1NS44MDQuMTA0LjMwNy4xNTYuNjQuMTU2Ljk5OHYuMTA2YzAgLjM1OC0uMDUyLjY5LS4xNTYuOTk3YTIuMzg3IDIuMzg3IDAgMCAxLS40NTUuODA1Yy0uMi4yMjctLjQ0Mi40MDUtLjcyNy41MzNhMi4zOCAyLjM4IDAgMCAxLS45Ny4xODljLS4zNjUgMC0uNjkxLS4wNjMtLjk3OS0uMTg5YTIuMTEgMi4xMSAwIDAgMS0uNzI3LS41MzMgMi4zODcgMi4zODcgMCAwIDEtLjQ1NS0uODA1IDMuMDg2IDMuMDg2IDAgMCAxLS4xNTYtLjk5N1ptMS4xMDgtLjEwNnYuMTA2YzAgLjIyMy4wMjMuNDM1LjA2OS42MzQuMDQ2LjIuMTE4LjM3NC4yMTYuNTI0LjA5OC4xNS4yMjQuMjY5LjM3Ny4zNTQuMTUzLjA4Ni4zMzYuMTMuNTQ3LjEzLjIwNSAwIC4zODMtLjA0NC41MzMtLjEzLjE1NC0uMDg1LjI3OS0uMjAzLjM3Ny0uMzU0LjA5OC0uMTUuMTcxLS4zMjUuMjE3LS41MjQuMDQ5LS4yLjA3My0uNDEuMDczLS42MzR2LS4xMDZjMC0uMjItLjAyNC0uNDMtLjA3My0uNjI1YTEuNjEgMS42MSAwIDAgMC0uMjIxLS41MyAxLjA1NyAxLjA1NyAwIDAgMC0uOTE1LS40OTFjLS4yMDkgMC0uMzg5LjA0NC0uNTQzLjEzMy0uMTUuMDg2LS4yNzQuMjA2LS4zNzIuMzU5YTEuNjcgMS42NyAwIDAgMC0uMjE2LjUyOSAyLjczMyAyLjczMyAwIDAgMC0uMDY5LjYyNVoiLz48cGF0aCBmaWxsPSIjM0I5MTFDIiBkPSJNNTIuMjMgODkuMDI1VjEwOWgtMy4yOTRWOTIuOTM2bC00Ljg4MSAxLjY1NHYtMi43Mmw3Ljc3OS0yLjg0NWguMzk2Wm0xOC4xMyA4LjM1NHYzLjI1NGMwIDEuNTU4LS4xNTYgMi44ODktLjQ2NiAzLjk5Mi0uMyAxLjA5NC0uNzM4IDEuOTgyLTEuMzEyIDIuNjY2YTUuMTQ3IDUuMTQ3IDAgMCAxLTIuMDY0IDEuNTA0Yy0uNzkzLjMxOS0xLjY4Mi40NzgtMi42NjYuNDc4LS43ODQgMC0xLjUxMy0uMS0yLjE4OC0uM2E1LjM0MiA1LjM0MiAwIDAgMS0xLjgwNS0uOTQ0Yy0uNTM3LS40MjgtLjk5OC0uOTgtMS4zOC0xLjY1NC0uMzc0LS42ODQtLjY2Ni0xLjQ5OS0uODc2LTIuNDQ3LS4yLS45NDgtLjMtMi4wNDYtLjMtMy4yOTV2LTMuMjU0YzAtMS41NjguMTU1LTIuODkuNDY1LTMuOTY1LjMxLTEuMDg1Ljc1Mi0xLjk2NCAxLjMyNi0yLjYzOWE1LjA1MSA1LjA1MSAwIDAgMSAyLjA1LTEuNDljLjgwMy0uMzEgMS42OTYtLjQ2NSAyLjY4LS40NjUuNzkzIDAgMS41MjIuMSAyLjE4OC4zMDFhNS4xMiA1LjEyIDAgMCAxIDEuODA0LjkxNmMuNTM4LjQyLjk5NC45NjYgMS4zNjggMS42NC4zODIuNjY2LjY3NCAxLjQ3My44NzUgMi40Mi4yLjk0LjMgMi4wMzMuMyAzLjI4MlptLTMuMjk2IDMuNzE5di00LjIxMWMwLS43OTMtLjA0NS0xLjQ5LS4xMzYtMi4wOTItLjA5MS0uNjEtLjIyOC0xLjEyNi0uNDEtMS41NDUtLjE3NC0uNDI4LS4zOTItLjc3NS0uNjU3LTEuMDRhMi4zMSAyLjMxIDAgMCAwLS45MDItLjU4NyAzLjE4NyAzLjE4NyAwIDAgMC0xLjEzNS0uMTkxYy0uNTEgMC0uOTY2LjEtMS4zNjcuMy0uNDAxLjE5Mi0uNzM4LjUwMi0xLjAxMi45My0uMjczLjQyOC0uNDgzLjk5NC0uNjI5IDEuNjk1LS4xMzYuNjkzLS4yMDUgMS41MzYtLjIwNSAyLjUzdjQuMjExYzAgLjgwMi4wNDYgMS41MDguMTM3IDIuMTE5LjA5MS42MS4yMjggMS4xMzUuNDEgMS41NzIuMTgyLjQyOC40MDEuNzg0LjY1NyAxLjA2Ni4yNjQuMjc0LjU2NS40NzQuOTAyLjYwMmEzLjI1IDMuMjUgMCAwIDAgMS4xMzUuMTkxYy41MiAwIC45OC0uMSAxLjM4LS4zLjQwMS0uMjAxLjczOS0uNTIgMS4wMTItLjk1Ny4yNzQtLjQ0Ny40NzktMS4wMjYuNjE1LTEuNzM3LjEzNy0uNzExLjIwNi0xLjU2My4yMDYtMi41NTZabTIzLjE2LTYuODkxaDMuMzFWMTA5aC0zLjA1bC0uMjYtMy40MThWOTQuMjA3Wm0uNDggNy45MTYgMS4yOTgtLjAyN2MwIDEuMDM5LS4xIDEuOTk2LS4zIDIuODcxLS4xOTIuODc1LS40OTMgMS42MzYtLjkwMyAyLjI4M2E0LjMyIDQuMzIgMCAwIDEtMS41MzEgMS41MDRjLS42Mi4zNTUtMS4zNTQuNTMzLTIuMjAyLjUzMy0uNjc0IDAtMS4yOC0uMDk2LTEuODE4LS4yODdhMy42MDkgMy42MDkgMCAwIDEtMS4zOTUtLjkzYy0uMzkxLS40MTktLjcxNS0uOTY2LS45Ny0xLjY0bC0uNDI0LTMuNzMzaDEuODczYzAgLjgyMS4wNjggMS40OTEuMjA1IDIuMDEuMTM3LjUyLjMyOC45MjEuNTc0IDEuMjAzLjI0Ni4yNzQuNTMzLjQ2NS44NjIuNTc0LjMzNy4xMS43MDEuMTY0IDEuMDkzLjE2NC43MiAwIDEuMzEzLS4xMDkgMS43NzgtLjMyOGEyLjY4NCAyLjY4NCAwIDAgMCAxLjEwNy0uOTQzYy4yNzQtLjQxLjQ2NS0uODg5LjU3NC0xLjQzNmE4LjcyNSA4LjcyNSAwIDAgMCAuMTc4LTEuODE4Wm0tOS42NTMtNy45MTZoMy4yODF2MjAuNDgxaC0zLjI4MVY5NC4yMDdabTI1LjgxMiAwaDIuOTk0djE0LjM4M2MwIDEuMzMxLS4yODIgMi40NjEtLjg0NyAzLjM5YTUuNDUgNS40NSAwIDAgMS0yLjM2NSAyLjEyYy0xLjAxMi40OTItMi4xODMuNzM4LTMuNTE0LjczOGE4LjI2MyA4LjI2MyAwIDAgMS0xLjg4Ny0uMjQ2IDcuNzYgNy43NiAwIDAgMS0xLjk5Ni0uNzkzIDQuOTU5IDQuOTU5IDAgMCAxLTEuNi0xLjQwOGwxLjU0NS0xLjk0MmMuNTI5LjYyOSAxLjExMiAxLjA4OSAxLjc1IDEuMzgxYTQuNzc1IDQuNzc1IDAgMCAwIDIuMDEuNDM4Yy43NTcgMCAxLjM5OS0uMTQyIDEuOTI4LS40MjRhMi44NjYgMi44NjYgMCAwIDAgMS4yNDQtMS4yMTdjLjI5Mi0uNTM4LjQzNy0xLjE5NC40MzctMS45NjlWOTcuNTU3bC4zMDEtMy4zNVptLTEwLjA0OSA3LjU2MXYtLjI4OGMwLTEuMTIxLjEzNy0yLjE0Mi40MS0zLjA2Mi4yNzQtLjkzLjY2Ni0xLjcyNyAxLjE3Ni0yLjM5M2E1LjEyNyA1LjEyNyAwIDAgMSAxLjg2LTEuNTQ0Yy43MjktLjM2NSAxLjU1NC0uNTQ3IDIuNDc0LS41NDcuOTU3IDAgMS43NzMuMTczIDIuNDQ4LjUyYTQuNjMgNC42MyAwIDAgMSAxLjcwOSAxLjQ5Yy40NTUuNjM3LjgxMSAxLjQwMyAxLjA2NiAyLjI5Ni4yNjQuODg0LjQ2IDEuODY5LjU4OCAyLjk1M3YuOTE2YTE0Ljc5MiAxNC43OTIgMCAwIDEtLjYwMiAyLjg5OWMtLjI4Mi44NzUtLjY1NiAxLjYzMS0xLjEyMSAyLjI2OWE0Ljc4NyA0Ljc4NyAwIDAgMS0xLjcyMiAxLjQ3N2MtLjY3NS4zNDYtMS40NzIuNTE5LTIuMzkzLjUxOS0uOTAyIDAtMS43MTgtLjE4Ni0yLjQ0Ny0uNTZhNS41NzYgNS41NzYgMCAwIDEtMS44Ni0xLjU3MmMtLjUxLS42NzUtLjkwMi0xLjQ2OC0xLjE3NS0yLjM3OWExMC40OCAxMC40OCAwIDAgMS0uNDEtMi45OTRabTMuMjk1LS4yODh2LjI4OGMwIC42NzQuMDY0IDEuMzAzLjE5MiAxLjg4Ni4xMzcuNTg0LjM0MiAxLjA5OS42MTUgMS41NDUuMjgzLjQzOC42MzguNzg0IDEuMDY2IDEuMDM5LjQzOC4yNDYuOTUzLjM2OSAxLjU0NS4zNjkuNzc1IDAgMS40MDktLjE2NCAxLjkwMS0uNDkyLjUwMS0uMzI4Ljg4NC0uNzcgMS4xNDgtMS4zMjZhNi42NzggNi42NzggMCAwIDAgLjU3NC0xLjg4N3YtMi40NzRhNi4wMiA2LjAyIDAgMCAwLS4zNDEtMS41MDQgNC4wMzUgNC4wMzUgMCAwIDAtLjY3LTEuMjE3IDIuNzk4IDIuNzk4IDAgMCAwLTEuMDY3LS44MmMtLjQyOC0uMi0uOTM0LS4zMDEtMS41MTctLjMwMS0uNTkzIDAtMS4xMDguMTI3LTEuNTQ1LjM4M2EzLjEyOCAzLjEyOCAwIDAgMC0xLjA4IDEuMDUzIDUuMTk4IDUuMTk4IDAgMCAwLS42MTYgMS41NTggOC40OCA4LjQ4IDAgMCAwLS4yMDUgMS45Wm0yMS44MjEtMTIuMzg2LTcuNzUyIDIxLjYxNWgtMi41ODRsNy43NjUtMjEuNjE1aDIuNTcxWm01LjU3OCA4LjEyVjEwOWgtMy4yOTVWOTQuMjA3aDMuMTAzbC4xOTIgMy4wMDhabS0uNTMzIDMuODQzLTEuMTIxLS4wMTRjMC0xLjAyMS4xMjctMS45NjQuMzgyLTIuODMuMjU2LS44NjYuNjI5LTEuNjE4IDEuMTIxLTIuMjU2YTQuOTM2IDQuOTM2IDAgMCAxIDEuODMyLTEuNDljLjczOS0uMzU2IDEuNTkxLS41MzMgMi41NTctLjUzMy42NzUgMCAxLjI5LjEgMS44NDYuM2EzLjY1OCAzLjY1OCAwIDAgMSAxLjQ2My45MTZjLjQxOS40Mi43MzguOTU3Ljk1NyAxLjYxNC4yMjguNjU2LjM0MiAxLjQ0OS4zNDIgMi4zNzlWMTA5aC0zLjI5NXYtOS41N2MwLS43Mi0uMTEtMS4yODYtLjMyOS0xLjY5NmExLjgxNiAxLjgxNiAwIDAgMC0uOTE2LS44NzVjLS4zOTEtLjE4Mi0uODYxLS4yNzMtMS40MDgtLjI3My0uNjIgMC0xLjE0OC4xMTgtMS41ODYuMzU1LS40MjguMjM3LS43NzkuNTYxLTEuMDUyLjk3MS0uMjc0LjQxLS40NzQuODg0LS42MDIgMS40MjJhNy40MjUgNy40MjUgMCAwIDAtLjE5MSAxLjcyM1ptOS4xNzMtLjg3NS0xLjU0NC4zNDFjMC0uODkzLjEyMy0xLjczNi4zNjktMi41MjlhNi40NDMgNi40NDMgMCAwIDEgMS4xMDctMi4xMDUgNS4xOTQgNS4xOTQgMCAwIDEgMS44MTgtMS40MzZjLjcyLS4zNDYgMS41NDUtLjUyIDIuNDc1LS41Mi43NTcgMCAxLjQzMS4xMDUgMi4wMjMuMzE1LjYwMi4yIDEuMTEyLjUyIDEuNTMyLjk1Ny40MTkuNDM4LjczOCAxLjAwNy45NTcgMS43MS4yMTkuNjkyLjMyOCAxLjUzLjMyOCAyLjUxNVYxMDloLTMuMzA5di05LjU4NGMwLS43NDctLjEwOS0xLjMyNi0uMzI4LTEuNzM2LS4yMDktLjQxLS41MS0uNjkzLS45MDItLjg0OC0uMzkyLS4xNjQtLjg2Mi0uMjQ2LTEuNDA4LS4yNDYtLjUxMSAwLS45NjIuMDk2LTEuMzU0LjI4N2EyLjY5IDIuNjkgMCAwIDAtLjk3MS43OCAzLjQ4MyAzLjQ4MyAwIDAgMC0uNjAxIDEuMTM0IDQuODUyIDQuODUyIDAgMCAwLS4xOTIgMS4zOTVabTE0LjQ1Mi02LjY1OWgxLjE4OWMuMzkyIDAgLjcxNi0uMDU0Ljk3MS0uMTY0LjI1NS0uMTE4LjQ0Ni0uMjc4LjU3NC0uNDc4LjEzNy0uMi4yMDUtLjQzOC4yMDUtLjcxMSAwLS4zMjgtLjEyOC0uNjE1LS4zODMtLjg2MS0uMjQ2LS4yNTYtLjY2MS0uMzgzLTEuMjQ0LS4zODMtLjQ0NyAwLS44MjUuMDk2LTEuMTM1LjI4Ny0uMy4xODItLjQ1MS40NDItLjQ1MS43OGgtMi40ODhjMC0uNjQ4LjE4Mi0xLjIuNTQ3LTEuNjU1LjM2NC0uNDU2Ljg1Mi0uODAyIDEuNDYzLTEuMDRhNS40NjggNS40NjggMCAwIDEgMS45OTYtLjM1NWMuODI5IDAgMS41NTguMTE5IDIuMTg3LjM1Ni42MjkuMjI4IDEuMTE3LjU3IDEuNDYzIDEuMDI1LjM1Ni40NTYuNTMzIDEuMDIxLjUzMyAxLjY5NSAwIC41Mi0uMTY0Ljk4LS40OTIgMS4zODEtLjMxOS40MDEtLjc2NS43MTYtMS4zNC45NDQtLjU2NS4yMTktMS4yMy4zMjgtMS45OTYuMzI4SDE1MC42di0xLjE0OVptMCAxLjcxVjk0LjExaDEuNTk5Yy44NDggMCAxLjU3Mi4xIDIuMTc0LjMwMS42MDIuMTkxIDEuMDU3LjQ4OCAxLjM2Ny44ODkuMzE5LjQuNDc5LjkyLjQ3OSAxLjU1OCAwIC42NzUtLjE5MiAxLjI1LS41NzQgMS43MjMtLjM4My40NzQtLjkwNy44MzQtMS41NzMgMS4wOC0uNjU2LjI0Ni0xLjM5NC4zNjktMi4yMTUuMzY5LS42ODMgMC0xLjM0OS0uMTEtMS45OTYtLjMyOGEzLjcxMiAzLjcxMiAwIDAgMS0xLjU4Ni0xLjA2NmMtLjQxLS40ODMtLjYxNS0xLjExNy0uNjE1LTEuOWgyLjUwMmMwIC4zNTUuMTY0LjY2LjQ5Mi45MTUuMzI4LjI1Ni43Ny4zODMgMS4zMjYuMzgzLjU5MyAwIDEuMDMtLjEyNyAxLjMxMy0uMzgzLjI5Mi0uMjY0LjQzNy0uNTkyLjQzNy0uOTg0IDAtLjM0Ni0uMDgyLS42MjQtLjI0Ni0uODM0YTEuMjc1IDEuMjc1IDAgMCAwLS42Ny0uNDUxYy0uMjkxLS4xLS42MzMtLjE1LTEuMDI1LS4xNUgxNTAuNloiLz48ZGVmcz48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2MiIHRyYW5zZm9ybT0ibWF0cml4KC4wMDUwMyAwIDAgLjAwNjI5IDAgMCkiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjE5Ny40MTciIGhlaWdodD0iMTU3LjQxNyIgeD0iMS4yOTIiIHk9IjEuMjkyIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIyLjM1NCIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMTA3XzM4MjQiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzExMDdfMzgyNCIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFJQkFRRUJBUUlCQVFFQ0FnSUNBZ1FEQWdJQ0FnVUVCQU1FQmdVR0JnWUZCZ1lHQndrSUJnY0pCd1lHQ0FzSUNRb0tDZ29LQmdnTERBc0tEQWtLQ2dyLzJ3QkRBUUlDQWdJQ0FnVURBd1VLQndZSENnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ3Ivd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhRQUFBZ0lEQVFFQkFBQUFBQUFBQUFBQUJBVURCZ0lIQ0FFSkFQL0VBQm9CQUFJREFRRUFBQUFBQUFBQUFBQUFBQUVDQUFNRUJRYi8yZ0FNQXdFQUFoQURFQUFBQVBwVjNPUkVZcFlLU3VaazRKc0pRalpHWlEvcEpTU1FHRmNrSi9HQU9vcnJBeTRGV0tPenJjcUVvTWNrMWhZdjRBT0N1dUZWZzlrYXFXNkZtR1pRNFNTU0dMSjFraG42QmJhQm5rWldTSzJyZGd0ak5aTUI2RHEyeEM0MkFBRVN2dkFwR1JoeWxnSjdKSkpQR01CbE1HS3JiUXNzcURaSnBIOWQ5am9leVUyTjBjaEd4WWM4NnNtSWh3TWhBd0k3UUlEMkdPQ0tBOGt1U2N6RjRNNnJuUUY1TEJuRmRLOWlxc3RORmxocXNQcmFlUHlWMU9VZUdqQlByZk1RSjBrRElyNjBjUXBYWkFtMFBPcHowQ0t3Qk1yQkRrQVVETVpZbGV4VTIybWwyQ3ZKQm92Um0vQ2VrQ09vVHFKWlhXYkVSQVRxK0JrUkJLczBwdFptWTJBZGxCYUhWeGhDUVpZRk54ejZMRFU1QmhpclRrYVVnU0FTQ0ZnTTRwMm1tcVcwM0hIc1FXNTlTN00wVUxkTExJbGg0T0ppd1NXNUNsamVTd285a1NPVVp1bGhhbjNOb2tBOUl3Qlhzc0JGTzFVZWlPc21uUjNSNTJuOW1RRmJTQTFrZ3M2MkhWMllxWW1YMkZrNHNFUjRyTVZMSlhZckxkenRtVWJPVE9ESXowRlF5Rm1LNnJlUSsveE5iTUl3cFN1NnRGbnJleDFXNG93c09aUnFHZFdLUXI0Z0ZSWHdmWm5NMmVtWlNTQ0ZpZUNSbVJHS0FlWmV4eXRQdWdqUmdDOXZSaWpuQmNJY0ZacFRZL1N3Mkg4eTRGREdqcEczWHpkNExoUmJWR1VLRGVLZWRxZEhSRitYaXZQdnZIWjROTVVWeWpiV1ZmZGV6bmt4azdLR3lOMHRFV1B5enFwamtzSk1Dc3JPRTZCNWZTOGFBT2tSUWdNSEY1T3g5SHRLL0Z4Rmk2ZTArOTUxTmJVbjUvUTA2VzNmMGVmYXFXY0FneUYxVzZyMTVzM2x0b1ovVGRrVnhKaEkyTG12ZlZYUkVlUlNDOElQS3VQZDI5Yms0T3hkSGIzb1BOK3lVN0owZFpnZEU3TUNld1BWWjZqSFZXVSs2bGE2dXEyc1ZWOE1BejF3RVlSMmRkaldwcnhUZEd3VG1jeTR0L1psK1BnSEwwTkpkUGp5bWR6NDl1c2I2ZDViTU1zaTlnMlFzYTNXTUE3S3lRME1uNFQ4WklDWlJmOEFKVG43Nkd0bjBIMVl1c0w2aHFuMEhoNlhYMW1maUhOdCtXZXJEY0Z0KzAxWitLMXlLdEdUNzkyVmNFdy9MdXE3NnphYy9VRitiNUw0OTl5ZXZ0UHRjVFVlWFZ3MXkrcjFuc3hkeWNycS9GRFBvb1ZsWkMyYlZ1cTFiVGJkaUVLVzdBS3dOWFhnZTd0bUg1dFpOc2RsZmJuby9OcHNXL2g3bDlUcW5zOFZodjUzT2ZsdldXVjYzZnF2SkRjL2VaNXYxRHJyY2o2VStUOURybGJOYm8vaFd5S0dSYW1JdzFpVmdsKzQwcjJ1WHBIUm5xT1BlUTArdW5VNC9DOWR2RjJmVnQvVGl5dXkxRGtlZ25zcVpkZmdXTEIxTFZ5ZXdoMjRlaDZwcUIxdU5SVkUzVlcxb3lXaUZHVTZaejI2eHJmU0hUelhXcTJvdzgxNk0rNjB0Z0kwRFluWVZhNzN3WGN2N0s2bGRLTFpqc09QcFd1blJaYlU1MW1MOEN6RFZRcWVCWHl1TFNuc2x4cmZ3eWFHMjU3bkYxZWFyamZWc2hXVEtMWGkwYVgzMFJRREVXSzFRSzJXR1Y5by93RE1kYkdDSWwxVkpGa1prWkdjRUpKYUdHeVJoZnpHWU5sSU82cG1hbTJwc2FtWXNUNjN3ZFp5S1plb1ZzLy94QUFpRUFBQ0FRUUNBd0VCQVFBQUFBQUFBQUFCQWdNQUJBVVNCaEVRRXhVVUlCYi8yZ0FJQVFFQUFRSURycGw4RHlQUFFBOGFsZXZBb2VCWGRkRU1uZ1VGVmZJODdGOXV4UW9BTHIxc0RSb3g2Q2cyMi9ZUGZleGN5YmgxS0ZmUFcrNGIrT3krNGZmMmV3dnQ0Rkt5c2pnOTlxeG9QN0RKNzFsZVgyQ1FNSDIyTGRoOWhYYWxhWHgzNjlQVVl0UFhLbTFGZllKRmZ2dXRnd2NNakt5dnNHMTBLNjlhemoxcERkVVdFaXlCOWkxQWhsZEhWbGJiYnowVkNUb2xxWTcyVXRzSFNSVy9oYVVxMjRrMzcvbGdLYXJvRTBvV2tZR3V4UzEyRzNEcXdydnZieWZGNnJDb3lLRmIremRTcDc3RkZvMkZOVzRrOW51d0dUOW1Edm1MSmR3WVdKVlpTbW9UZEhyb0xMVVpWeld1aGpNUERvL3o4WlM5aVFYME9KU0pkRmowa2pOb0lrclVLNklxdHQzc0RJZUlUQThUa3VHU0xMeDRHQklaRmlIcUFkdFZRTDA0U2hNSlVkQVZscmlLQk9IQ1RsY2ZMc3JiNEFnUEhHZ0lacmRVOWRkOXRReGwvZldkNXhYSVNTTy9EbkY1eFc0bXU3TnVSRzh2b3NsRU9mS0g0Y1RrWDVOaitTZlF6bVUrM3h1K2c1YzhrRTdTdGVsOGZTQ0VSUnpES2NsdVpMZDRjOXlsUldPdi9yZHdGYng1bGhhVUxQSWFJOWppcEthNWp1cGNiR1dXT0U1emxGNzNiU0duTVVra3NMVmNPYy9QbmpmZmJ1cjJYa2x6bEllUks4ZHhZM3N1U3VzTWIyd3RjaEIzeGw4aG5ja1JqNzdINHlYOSt5NW42MzJIdjVNdittZTRDd1hWdlA4QXRXOGx5dVR5K0l5LytyYlAyK2V1c3ZhWldYTC9BSC90L3Jubk5uK0w1bnh4aVRpZmpqRC9BQnhoemgvaS9GK09NTjhhYkVXZU1HTitkSGl6YUJmekMzLy94QUJFRUFBQkF3SURCQVVJQndNTkFBQUFBQUFCQUFJREJCRVNJVEVGRXlKQkVCUXlVWkVnSTBKVFlYR0JvVEF6UUZKaWNwSVZRK0VXSlRRMVJFVlFZNE9Ub3JIUi85b0FDQUVCQUFNL0EvdGgxL3hlMzBCNkQ1WnVyS3lzc3VnYy9zWTZCNUJZYnEvUVcvWXowazVJSmdDQUYvczNGYXk1OUFKTFFkUHBiZlFCQWxBSzRXQ1F0OGkzMlV1a2M0aFpyQ0xrb09GMm0vdTZiZlFXNkwrUlpXRjF0Q3MzM1htT0ZpTUdKZ0hSWFRiUXFXVFYrOWEwY0l2cG1pNitKT0IwV09qZUNIYWVnTTBHVVdqdTJlMkxIb3NuNnAvY2lCbUZieXdyb2RBS0FhYytTZ2V5b01lNjdiZnFua3B0MXZkcTFtTWsySE9MRHpWcWgyRlh5S3RSU0REZkxUSGgrYWNLRVhCN1IxZGZvQ1paTWFlRlkySEMzTlNBWTNJdEtzcjlGMitTVVVkMjc4cUVrYzlwbk80MjlxSEJ5NkE3YUZZY0VneUhiZmZtaTZURWk3TXFNN09rRXJtQVpaeWRuNG9HZ0dEQll1UFkwV0UyVDR6bXNXcXZvcmNOa01kaUZjM3NnVmtzMGVnZEYwd2hXUjNUcmZkT2lJaW40YmVjSDlvM25MdlJUVFcxdURkOHV4N3lxaGp5T29OMTliL0JWRGpZN1BIKzZueDdOZlB2WE5OaDJJOFZ2aHpRL1pyTHZ2bWN6SGgrU0JOMXZNZ2hiaVFITkQwazU3c1hKRG1nckt5QlRRTG9xdGVURzZza0l1Y3NTclMwanIwOW1uaHRLY2xXVlcvRlRVdWVHT2FHNHZjc0kxVEJTdk1yMmp6WjdTaTZyVVBZWTdid1p4dzRCb3FYbE96OVN3elZrc3NqaU1zNUdCcWl4bDFqYS9jbzN1YTRCUDhBMlBKdUMrK1gxWnNkVnRHQnpXTnJaaHczc1hyYXBlR2phYytmK1lWSEUwVzF0cWgxS0thTjFqdnM3SDJLVDFwL1VtczJSamxmY3VsT3BWTmZEanpXMlJNOW9xOUhIOTJGdGVhc1pGTFVqQ1hjWG13b2o2YWRGczB1cFo4TDhRemF0cW4rOEp2MXFhcmZMMTJzZTRBQzJJcllkUUMyQ3RBZmJoRHhhNkV0Vy8zbFlNV2ZOTzNwenk1cW9pTUliVXZZMFd2WXFSMkxIZlAwa0k4VzhxSGNiU0FBZlluT0pOdWZjbW1tbVk5NXU1bGdQaWk2TENRNFo4bDFleEY4enpXejYvWi9WR1F2Y1MwWHZrRkhMSUxOd2hyTFdRYkxjSFJiYnJYNEdUdGIzY0NyMjBVUFdxckdIUDBJME5sbmE0VWxKVDRNV1dlU2RuWjNKY1dMdlJhZDR6VUoxdUtYd1JtRzd4M1F0b2pUTXVDTTFEZ3lnNWVxQ2daQTh1YitHMkFDeXBHdDA4UTFRYnZlaG5EM2dOVWNadC8xWkZ4dUdrK0NMM1dpWVQvcU5USW5ZSGl4dDZ3Sm9iWnpzL1pML0JiTnFZWDdQbnhDUTNJS3FXUU5xV2plUjNHSjhUc1FIdjdrYjhZR25lbnZkWnJSOEZLWFhNYW91cFJUQnJMbVBDVDl3cDlTNkZqem9DVnhkcFhoc0p1ZmN1NlplMUVFRU9WTUg0aTdFbXk2UmtMOEtNZFB2ZDNlenVhbHFLUjgwUmRacEY3dTcwMlVEaWZlNHVvdXJ0cTMwOTJrODFSVy9vNmlwTUJrZ0xjWXUyeWFBVFoyV3FwS1dYREpCWjVGMXM5ODRiYnRHMkloYklxR1M5WHFZWHZpanh5VzludFZMVXhzbWlrRjN4bksrWXkwVlRSK2RwNW5NSTdsc21yZC9PVWZWMzhwNmNhL21hcWdOZE5ETHZvZldVdVp0N1JxbXNHQ2xqYXdYOUlhcmFsUkdhanJMZDJPNEtSa1crbW5Mclpab0xDMlhpQTAxWmRPaDgxSnN0cm9jclROWllxaHI2dDlUSE51ZzUyVGNIQ1BpRlVENnB6SC9sZi9BT3JhMnoyNzZvbzNzamNiTmRiVk8zVnllYWlNbTdhYmxNbWlkQ1c1RlZUSTkxdzJQS3lsZGR6ekdNdE1DbGtoM2IzZ2QzQ3BjVjQ1eDMvVjJWYk1idnFTUjNZVUhnaDcrMTJqWmRaY1pIeU9KUEx1VnVhTkxpM2J5TVRiSDNJVTh1OWprY0xhRlNPbmRQSk1idjF0a29YUlBFekw0cllTcWVDUVRiTTNyRFlmaHorQ1p0RnJKSFJYZUcrY2U3dlZQc3lHUnNsTHZITzUzeVVXSCtxSUwrMExaaGVYL3dBbmFmMlpxZ2J4blp6R3RPcllsVTFUTjBYV1ozWVUrblpoYkd3MnRrNXQ3cVorR01jREdtOWhycWpqa2twNGNKazczSEpWTGhobmhpbEg0bUxaZ3pPeWpjajBaeUFGVDc4dm8zU1JzT2pUNktwais2K2FwajZEdkZRdU9IajhVQm5ZK0tEZS93QVV6bmZ4VWV0MytLai9BQitJVFQ2VHZrbStrNTU4RXpsajhRdnZTUDhBa28vV1BUUm85L3lUUHZ2K1NpdmJHOURkbmR5T3Z5QkNta09LWVdiYmtWRG9NWGlvTkx2djcxQ3pNbC95VkV3WGM5d0YrNWJONU8vNEtuRW1KazUrSVRlVlQ4bC8vOFFBSmhBQUF3QUNBZ0lDQWdJREFRQUFBQUFBQUFFUklURkJVUkJoY1lHUm9iSFJJTUhoOFAvYUFBZ0JBUUFCUHpKSWZpNmNFNE1KaThaZ2d2SVJEODIwSW9ZUXdoUDhWQ1piS2tPVEJSTVlvb1B6TDkvQmNEbi9BSUJvSmcyaDA4R2FvcTdGZzFDTUM4UmVOejVUMExQeUxvVUl2R0dRdWZCYU9qcFdoTnQvNEpyek1JNk14Vkx3Y1pIeWFLTDBRam1OQ1VQVklyVUdSc1NGbmxsQnA3UEZlQjh4RUVXb3NoQjZqdUZFSVBDSlkrZzUwWG5BdXhtdzVCSzZSdVJUejRhY25xTkRneDVMK0JsSGVjek1IaEEzZ2FQRm9PSzBaSW1JRlI4MEplRE5zNDZJWUpSc2F2Q2tpVks4aVFnZ2lVYVR3NVJXSFk3UnhsS1M4aFB3VkU3NG9tTjVQMkh2RlF2OGpMMFF4R1lrelBYUGdwVFBrRlFiOEdHRmg0SDcrTEg1RVVoVVVveUcxbUVVcnVodUE0eEpKYmJJd3Ayd2xSaWV3WlZQd3FIeE9sTTlzaGhkRW9ZcWxHU01yWWRDWnhvdXFTMEYzQ20vc3p3V0hGdmYwamxHejRndnp1dE51RGtLN2lYUkZoQ3l3d0pqZ2ZnV2dzYjBKMHNuTVl4amtzRWtJMkdhNXp4clkvWkR2Y25XdmQ1T1lhQVJQYy9rVDFyS2ltdDJuMlB3SlNyOTc3R25jSjVzelBncTQrcXZSS0ppOUJtMVlPQUhwYUp2SWthR1Y0QWd6MjR2QTRYSzBFZkR3Ym5hK2EvQTd6dGlpY0pMWFZZeXVHWU1kbVo1dmx4UmRCemgwMExXaEpnZDE5aEp0SGhqSGI1Q0ZnY01Jb2hKanpGTDVYaGZaT1JleG5RbGlVZlpsZXh6L3NFb3hzc3NGdlk4ZjhFVUx5M0pCbEEvM2lNZ0VGODlGalJta0R1bUNpcURRRGdZV0ppM1FzTmtGbWxlU2U4anhMZm9qV2dSU0RSVnFzRFNVK2lnYVkwM3A3UmJIbk5mVlk0dHRjU1Y5bXlOdm96MTEwZDVSb2Y2WDlhMHhJTGVSMVpmOUN1eW9TMlRSVzJ4Wit4MmhWWW5yNTZHNjZlNllTTVpWOUVhbWlYeWo1NVRMVVM1U3YyUHhvbXpNb3NvbUdFYnppQlV5ZTMyU0JTc1MrenBnUXlLV1NaYjhTclY3R3JVcUpzdW1UdWpwbXByMmU5ZjdrZlJOcnBYL28yT0IzQWJYYnRETHg5Y2F5T21jV2Z0aUptc21pbDQvc05wcVk3b00rNEVabXA0RG03N1BFR2xQdHFVVGd4UjFwWDJFaG9QSXZvMklDeW94RzFwODBnNi93QXpOVnoraGxGT2QrMElMdzFJVWNwa1pWbk5lT2pZdE5mOEI2MXhHaDg1MFdpNlE5aVYyMUc4Y1pRcWovMGtEN0RIeFJzVnBYUTBRek5laTU2SUpQSHIxVEVSUXRMdkJLcGFEWE9LSzQ2RlRiYTU3YTBKV1Z3N0dsRTB4b1plNW1uK0IxbEZ1S1UwNG0reDRYbS9rVE95c1g4amw3R2RxWDJjZEt2dlovSm5wMm50VC9vbTB6QzhKOTlNWDkzN1Yxc1NsbGVHNXdKM0RWZ1o4c1l1T1BlbW5ETEw1ZTF5R1EwcGZiNzlHYlRab0tXOVA0Z2h4VzIrZ3FIK2hoWUdTdTI3YmZKSWx6cS9vVjJGb2FjZUJlNkE4QnRpdHVWbWlKV1NxK0Qra2RWSHhOTi9VTXF4TmpDT0NqdkhEdEhZbDZOdC9aS0xZbS9Ub3BXT0NWWGNqNlVHM244QzZ0S3RFaDlIeEtnaStQd1BmL3hibU1BOGtwR0VLZTVOZFBRdmFqVDIyRVZyVEZLdnlWYzBlSExqMGhpTjRPajB1ZjhBM0p0TXczSSt3d1dVYlRiL0FBL2s0Z3RmTjZhUGJPOGkvQXJkZXpmd2l5eDliOVRrZTBhNGhVcy82RjVJYXRNNCt6VDJlUzJsSjlEalgxaldma2ViOTRiK1ZrVitnRGRIeGtxR21mZmpVOGlLVXY4QXoyWmlMSVhMakU2VFM1VWQvcmlSVk4rRkdyL1FET25xRmZXRk0vYVlEdGJxR2swZ21ZL2dFMWpPQ0RzWXp2NEg5bGs1d2pML0FDWDhmc3FzYVZQbkluY2xweGd5RkxwaHA5T0oya04wOHR4aC9nUTdlYnQrai8vRUFDWVFBUUFDQWdJQkF3UURBUUFBQUFBQUFBRUFFU0V4UVZGaEVIR0JrYUd4d2RIaDhQSC8yZ0FJQVFFQUFUOGhqY1JNcDZnV3dvNmhzaDFRcng2QndHcGMxVUNzRTVMbCs1VHYwMWN4cW5oSE1lbUhmb3ZNRUJZSnFab2NKT2N6bXhmYjE0TUlZemNNSVhjcjNCNGhpeDQ0Q0tFS2tLU2hTTDJuRGdMRmJiRTJqQ1VkeENtWUo1SmJ6NklFZk9WZHp6d081ZGNvMlJubGdSR2FqVUtKU29CbHlwdUx1NG1VSUNSdHphSVhLRlhjOGtxNW1NYmN4M2VhMXhNdkV4SVpqRGJISVhhV1ZtWGN3dlF6Q21kZHVxbU8rMDJub0lhSkNKdkwxVVlVT0d6anNlb0M4emlXR1lnTHVNREJ6aVVMdUh5bU5tTHROZ3dwelBMQ0czdGVnSytHSWxvWndKVWx4ekxKVTE3UE1wRU0xWURsaE8yS09VUkhNY0psM1JIY2xXTGlJdVZDRE1BSmRiOUFjS2hYcVowQmFtUmtobkVRMVl4a3lKYzF6RFRQU1hHbVVjK2s0NUc1dUlvZWpBM0tlZlFjSW5lbDZvTm00ZDBJcEsraExlSlpOUWpMZFJiQmM4d0NOTmMvTXBzdVg0aUxjYnVLTXhZWXpkejZER1o1WUZZZzNESGFnTXNsa3YybGppb0VHUzJ2SHBSU3ZGTU1KZTdlNW1iWWVrTFJjUjhWOHg4NFBmb1ZTSkF1b2J1STJ1THpNV2xnY001a0s3dWUrVXUzMFdZeXJpQUFia2xnQ2NneUtnbmhmb1Q3VE5QRXZxNmd4Rm5Qb3hPUm5abmFnYXBBWGNDS05ReHNTWlFaand4WUhZVkdFMkxIYnhPTHB0WEt1QlJuS04yWE12UHQyeXdZb3dNUjJtVVJaZTh4ZU5oYVdydG10YW5obkV3MHJCWlJUWW9WVzhTNkN5Ym5aRTVRdnNsaDV5NHpiaFJ6WDZZWXBIZVJTeER1aFJoNFhVWTJxK2gzeDd6RUhBcE44LzhBRkNsc1hLUWRRV1QraCs1VGlqUkRVT0lRb0VObkhIN1l4YVZXb01xZGZjcjhHcExiNmc0eFdIcVNvdG1NTUdYN1lYaFUzU09veitmWnNQbzRPSnBFVkc3bzIxYmNjKzAzbUFGRnVPenpHeGN2THl0L3R1dW9zMW5aeUZ0c0NwRWZKSm5YajhXUU9yRUpjT1lQekZacnErMlUrYzVoRlFyQVJldzZoZk41bE5Pb1gxWXlyaUprSTBuVjZNc2QzQnlwQW1GOFdDMXRQRFBDclA4QWxQaUc2N0l2c0M1eGZnZnpOb3ZIQ2FpcXFRVThtWlVZM04xTC9OeGI4dE1jL2hHNk00R3NoTHVFUlZlSmhnZ202bUlINlU4c3J0VmpEYlZLK3NrN0ROc01HREV0NUxQbVZmS1d3REtRRm1JZ2lvNjVOQStKeWxJY1Zya0MvbUhubExLU2xFR2RCM0YxS0p6WHR3MythTUNrVzVmMHVjYkxqZzlteTk4UVoxeXNFbFNLU2t6QzhuR2tNbG1zUVNXOTJnckVNOEF3NUd1NFZHaE10S3kzTWwxUDJPbnlRNzZGL1BLaE1pdFJTTDlvdWVMVjJVWUFaMUF4dERKdm5OWW10c0hKZjZsb24zbjdDWnZxbjhzdHA5STcwemovQUdnUlc0Tjg5VFZyMzVnVm9ITmZlVXRsVkxXTWE1bE9acmNWa2d6TXYwWmN0bXJsdGVscXF1UHdFd0cxbXpmcU15OHhIeFRmNDRxRUN3b2JYUGRUUEtJNWRIdE9nYk5nMkc5azNlTHUzWmYzaFdja1gzREpadWdLYzFRemdhdFNiSDJxT3NvT3NEZ2NFZWVZQzI2Mzh3em1xMis3Ris3TDZMakFZYUViK3NXUHF2YWZjVW03MHlwZGZFVmpHU2ZTOEhqVU8ydVNwdldjVmtLRnNqRDQ1bDl3Q2RCNzFBS24zYWZQeERZNVpWNzhFOS85VTNsYjRHSytpQ0dOclN1dXo2L3hFYXIxMjcyckRYUDZpNDF4TWhWMEdCTWxmV1ZuQjBJZDRmWkVyK0M3QzhuWFV0K0tBZlhqY1JFTUNITDRsa0xDSVo0WmhySWxwY1AwbEt6eWl6WGNJVnk3cnhNTHFXc0lacVpCcHdGQkw3UnkxNXpFQmJpMnhXeTNkSDFjKzNjM0NWanRCNFloeUF0bkhtcGVVc3BqWk5McU1QZXhaclRlVWJzRGExRHRTOUkzZitGS1g4SWZlUjdBRE5Db2JZdkN2eUl0bFN4dnRTNC8xelRBbFFMM09EbjVtdkFsR2J3VkZZS1hVdm4vQU44T1E1STJFbk4xS1g0WVgxM1V6UWpIbkwrY0ROY2lDcmZRYlo3cGh3TTB6a0g3TVNwNExjMDZBTm4zZ1Jra1J0ZU1mTTVMYWxyYlJ1VVBrcUQxTHY4QURzcGhqclhNNjRlMUJqWVAzZ3BuYkhzQmZld01wTEg5ekYwdEowMUdNcmJXYU5TNVNsNFFjZXNQRnYxRUt0cVp1bzlyYm5pOE40YXdWcmZVdmtTaHJpcFd2NlNzTGxLTHhZUnZIeG1GVWpOdGFZSEJoam1wWUZ6Z0Q4ZGI1KzB4THdjRktCNkR4S29HUmY0a2JtV0dHQVRJOGRSY1BLclRQU3ErSXE1UkRvY0tvdXRHb2tlYkREV1crTjF5bUhweXFpN2MxeHdONlJONm9OVld5M0tOdnlZT090UWwzdVhVdHdJZG5oMUFDRHhwbmxvSDdTeUJUWGVEZTY1Y3h6b1hQQ21mY0h4Y2ZVc0ZWdG54eWh1bWJ1SDJFckViUGhXdWlyM0Ria1FGbEJpbmZSOTBiZU1Md083NUhpeVhGdFdXRytzbXJxMkRpcmlsQWlrYXJIOTVoSFI1VTN6Ykg2VU1pV3lwYjlSSmJITEkvRTR2MFVIdHkxOHN5ZDg5b0lvZlZFWGpaRE0xdVFDS2hDMnppOTRrTEpiL0FPb1U5RG5aK3NTcVBMUXlFMFgyQk9pNHVPdFRTWDFjUlVKbTJUN1M4LzNGWWxsUk8xRVlhanovQUJndUJBdEc3Mjk4VHdYMFQ0NlRMTnZkV0gvL3hBQWpFQUVCQVFFQkFRQUNBd0VBQXdFQUFBQUJFUUFoTVVGUllSQ0JrWEdoc2ZEUi85b0FDQUVCQUFFL0VCbXJtaCtETlhRZkc2dE9HUVlGQngzOTBrT3Q0eTUzM2VqelExbS94Q1phS3J2UFlPdnVTOHBmY2dHbmN6dTg3Vlc1eGZtS1FaUmVZWFJjenc4dEk2TURET1R6ZGJmdmxnRG5qT0FXYjZHbDB5dFl1TDVxRmNiTTd5LzJhQ0hQNEs2b0hmbVJqZ2JrZjQ1Vysvd3JHb013VGpnb2E5TlB6V0lVNlBYY0hPclM1OHlqRDV5dU1vN2xySTZ2WnZVditYS0RXZndoTlJLL2hwY2puOEV4QmxtNnNVMWM5TzU2RDdOMk0rL1Q4enpJTDQ1d0ZUNXFHSS9lNFVwNXZOd256RXQwajd1NloxSEc2MFNHc2orMTFCSGVoT09UQXhNeFZwWTJKenV3Z3QzVDRlTDVPNUowWHhpYWFIM0JMYmNxV2dYdTZCL3R3ZVQ0WElvaU9yeW53ZFdJejNPUFU5TU5qbmRqZEtPNHREbld3TTI0U0QxNWp4bU4rc3dpbm12Qnlmbk1IcGpQZlc0UTFYelB2Tjh6QjMvNnpnQ0Z6MnExWHIrT3NQMVAxbi80cTUvY0o4YVVKZTRBL3dETEtYWnlQWFhqODZpR3ZzZnQxVGd6anZHVlJ4cnEvY01xL3dCWU9MK004Z1dZVlJ3dFIvV0Z5TVl4WXEwUjVmdmNlMXJRS1YrNEIvQk14U0ZSWlhPaXdQRExLdzhjcWJ0NEVPRk1jYjlkeWFaS3gzTmY4T1hrZWtCUHc2aWJEdng5TU9lQWorOEk1L1Z3RzVOeUpQREtSanRJd01KblVzdWVIV1ZxWFUwWTlvclR4N3N4b2VjVnExeUs1c0cwMUpobVVIK0dwdjhBRDlGOE5CN2kxd0xRelFjRU45TEg1NWhPRG9QTUxsVzlFRENySFN2K05WTG4wekl5Wm1VNFNQelZ5bEo3ODNPWHVkL0c5bHhHUGN4S1hBbVlRb0RXWEw0dWpoWC9BSmpTd2ZXeitMSU5uZVMySFdaVlJaNmlhUlpncVM2dEZ6Vk5oNGZYdVNrZDRLSG5ZNXYrQTBaam9ZZnFyd01PVWxDMUhwVW1lWDZ5Q3BuQ1R6SUc2MkJOVmRlc0ZmRnhuL1kxWE92TWh1TDJHQ2cvc3djSy9kWG1lTEZPZVpGdmJ2ZC9xT2VRMEJhd1UvdEprejh6STFnenhXcGlsWS85NWpNaUlxVFVnNE13KzFWZnRjU01IekdoblJiZWZpcjRPaHlGdDlpS1ZBaHgrOTFENDVuVmkrNUhwQWEwUUIxekNNTzZRWHAzQWdlOCtTVTZpbzVZWFpnY3FJWDlaK3JpZVlpaGNLUGp2QzM5WUx5QnFyajgrWndxSlI3QXYvdDU3bGtvZC9UUEgvZzV3bm1QeDkxUENSaG9IUTVnajlNTkRBSDVPWUlJc1Z5MmxtUis2bis1cUo4UkZ3U2JlSG1iUytCeVo1aENlcnBXMVZ3RS9nb1hKNExaMkRnQ25iYi9BQ3pLVlEwVTkwc0RlTndBaUQ5TVpMUTE1M1NjM0ZPSEt6QzVrcDg5dUlQZCtPT1RURjlXNURTaXJRUGM5UDhBNWVhekJWWnN3SC9tdW5tY0pkRVlkdU9DaUQ4OHhoVDZmRUh6TjdKa0xTOE1CaEJRWXVMRVJFQXcxcXVJZTU3dXhUaUpSUUFPZUdGRS9Pb2QwWlNYZy9oMXVlZkpacnFMZklES2FTQ1oxTHBqNWJGdjEyRE1PSDJjR0dXdlRYTUR0TGtoQVBWWHJ1bENlczN5N2xEa0I2YUcxY2tmbnR4c05tUEJJSGs3Y3A0dkJqb24zVUNrNlVLZm0yMmtPRC9lTGY4QU45MDZkVTIvbXMyMDFMVVg0eHQwUjl4L2VsK1l5WkFlSXFlZVhMNTV4VnQ0NFAzOTNzbGRIZSs2ZVFjM25zQmovY1kxRE9oRWhISCt2N3lBUEJUL0FJdmZtN1lNSCtxWUlNUHlhajRHL3RqTEVYMzdnQU1YN3J3Q2czRzVoSDM2a3p1ekpYRndBSEg4R0pxK2VDUTVJQW1KZGVBaTNYbXNiVGxQU3hhaDNxZkRDQnhKTmF3S2ZRS2xEWFRXTkUvSVYxbmlwbVpYcXN4ajJPWHdyeDhBV0FCbkhVSG9GVUh5R1VzNVB0Q1UrT0w4MWs3eWNmc3I3TUs4OTZDNndlci9BSGZJVzgvQXpWbEUvQUVSZHE5YzZ2c3hvNGpCNDcyS3ZQQXhFS3ArRUkxRHBocHBhQjBYc3c0cE9jRlcvaUdKK0RpK1NFS2w2WVlhREtPbjJianNjVm9xN3hNZFQxZW1XZ3JBRDhjSFpqeHY1b3RlSEFlNWxvU1lIdEQ2NmY4QWM4MEFzNEhYMy91UGpDbTlkRlJ3Q0M5bWJZdzJwRUZJVmZUOVpsbjRuTmRLcldwNXZ4VXdwdTE3VW9nS1BIWDVucVB5eThEOXlPSlQxVGdncE9ORXBscXhHQUdBdGNiNWlYTkFIbFFHSlV1WStEQ2hhMDI4UWptT1VBc0w3KzlHVVV1RkE5L1ZEU0hGelRZblJNcng0emt1SGUzTDN1VkloUHVmTXNEYTBGbkFIUEIxcjZIQU44NkgzOFpUcSttNnV2ZnZYR0xIRCtCVmR5dXk3MTc0bjQrNVoxbFVmaCtMck10MytCYTExT292Vi9JSHhYN3JSMk10dTV3YlB4WVlyTlc2MStNc2dGalJUMUJ3cEFwRWIwOWNjK1BPekRPVTFLQXFDMnNRRXJ6UWgxeTVYbEQ0MWZuelc1UVZUKzRwS2Z3S1BYT1p2NkpVMFE4bjR5ZjVnc2E2QUVVS3NDdkNjNm8wQnE0SnlVbGlINmUwckpodUxCaUFwMmgvWlh6UjE0VDRqK1R2eHkrejJNaXhBc1JlejVxb29BclduVng1NjZPRWUxRDlVcDlZL2pFK1Zjb3M2cUg4WGpyOVk1WWdwNkU1ek1LcGIyMXdyZWs4ZW1nS0FqajljQnJ4T2dZQyszeitndUdRSVgwci9tQjBRL0MzaEkrZU5IU3lxU3JzNGc3K1FuekkxMEdPZUJEL0FOMkNSTjJTYWdjRS9aSi9lbXZtWFBQVGp3L3ZUeEhrZzBvVkcvT3pFczV4a3hSK1ZvUDFlNUc4YUZtaEY2SHZBdjZ4WWRrZWFSenlUeGhZbVNRRWxEL1Y1ay9abHlRRENpcUEzbnJrNkduUjBMVW5TMkZja1ZzbTdaV0lNTWl1dzhNN0Z4NXYwYWxDUVFjUTZKM25MNGtrMWdFTDNodm50TVJBRDRIVG8vUnduR1pSeldFb0ZDaTRWMFpFNWhkREhVVzJZYUlqbnNsNEhzbnBETVVOTFZYeThtL0VMaGpWbENvdFNEWG4xM0JtdnF3TW9YSVFvZUI1UzlPM0NhQ0sxR0FyRjMzV1hvWEdRQlVmeTByOE1MMlg0ZlJRSDRoK3NNZjhjNm9lU0xWVFhiNFpFYUMvdGNkVHRiQ2VvTWpOQ0ZMdXA1RS9EZ2Y3YUZsSDRQMWZVNllaTWkxZFpGbDZTUUlENHNncnprQ21nZy9oWG8ydVBFY1AzUk5QMGxYRWRKN3BXQUdvVzV3a2RFNXV1bWQyTmtJR0RrQStySmRSaGc1ZHZ3Z0F2MzF0Slo2eXVRSVV6QzFPS2txRXZ1VzBOTE9LV2FFRXluQk5ZMUJ3SnJvQm5YR1E4THNBSkVRZlErbnBBSFhqeUtZdCtoWjNXLzhBNjlEbXBDSHJQa3lselc0bERha0FTVm5tTCt0Szc1aS9aYSs2K2V4RWlyK0h5QTVGTllpbWVLdlNMajVkNWR6QkVDU3Ixei9aZGJQbnFLTjhnd0hmTzRieCtFSHZEdzRBY2c1bnhjdUNEOGNGVEo1Z3VvcE5QUnpmNmx6YzdJVHI4VEVrKy9reTdMalZSTlNHR0FhbUFFZ1QyVjliZ3ZWNkpXY2g1TW5zVEpLcTEvb2QzZmo1YjN2MDR3dDBZSG94L2I4NnUxQ0YwdGJsUUcyOGVzYy83amZFb3Y4QXlDNTJQVmxLSDNpNW42OTY3OGZzelhCWkxuMzM4ODdnczVMeVFWOFc0bngwamNDeS9QSERsQkdKaDk1NkhOTDZuZ0JSSWZUaXgxeUNCLzBPbkhINFI2RGxNY2dZeWtnZUhSOXlSdE8vUDdGU1hkdUlFZjAwNTFkU3lkdk44K1crWmMvREVGWGxrTnpYK25MQ0ZhOHBqS09pY1BZRVQ5aHBVZ2dpcEp4bmdjMy94QUF4RVFBQ0FnRUNCQVFGQXdRREFBQUFBQUFBQVFJUkF4SWhCQkF4UVFVVFVhRVVGU0p4Z1NBa3NXR1I4UEVqTXNILzJnQUlBUUlCQVQ4QUxRdVN0OURwejZjN0hMbGJRcE5FWmwySm0xQ3J2emEvUjBGTHNSWkpuUXY5Rk9pdWFUSXdkQ2pzSmJiakw1WCtxT3hxTDVWeVFuWE9xSXBOMExHdXdvbEZJb2t1dy9RcGlZcGxNVVc5Ni9TdHlyRkdiRXBjdElvV0xFUnhwZFJSWFlUU0xOaHgySEVjUnhOS29VU3JSRzVIbHhaNUpIRHU3RmdYcVN3dnNSeDBpbUtESGpWV1RSQmlFbnljVFN1VVUyVWZjWld3bFI1amFGTm9XV1FzeTdpbWpYUzJJUFVoS2k2SXZVVU5XalE0c1d3bTZMUjE1VUpEN2pnMjdOSlJwTExkOGswSVVwVVlhVUxIYko1SGJPSGJhYU1kdGtsUWttVXhPaTl1V3ppYUVhUFFyc09JNG1rMGlkOGsrVVowaldZVzlOanpxbVg5UmdnOUlocDBRU2JHclE4YU9uSk9pMlJZMHVWRGlVLzBxdVVXNDl5bXlDcWJaaGQ0MDBiRC93Q3JNU3RqUzZJcWlTZGxDTnFJMmh2WVRLWlRyazB4UU5ETkxLZk5XUWpUWmhjWXdvMmZRZTBYWmlLWWszZGlnNjNORzFvOHV4d3BpWlpGV1cwS3pwMUhaQmFtYUVLTVRSRTh0SEQvQVBKZDlqeTRuRFM4N0k0c1NpaWxaeHNxNGFYMi9KNGMxNUZtKzRuMTNMc1Zqa3E2aVdwa3NXMW1sVUl2c1FUWTBocXhiR3Rtc1UyaDVHY1BrbEZTSG5ad21TVVp5YWYrV1lKM2pUWTJqajVmdEp0SGhUdmh2eXlWYVJOa3BUYkUzVk1oS1VKN3ZZamxnNU5EbkhUUTNhRkl0RVdySlNRNDJ1U1ZpaVZRNm80VmZUSy9VYU9GVDF5SVJVWTBYVE9QZGNKTm5oc3YydjVacXNnNE5XTlV4eVNWc2R5V3BNaEc0N0d5UktUWFF2Y3RVUmFKSTBubEljSEViYVpHV29mUTRSclJMN2p5UlJ3MDByUGkwbFZDNHBQc2NaeEtudzBvbzhPVCtHL0xOMXNSYWdYZlEwdGpnK3hISkNLb3YwTld3b0tUc2VQME5GTFl4cWJsUXVOajFNMmZLNHUyTEpQUWttWVc1dzNJeHNtMTJPR1VuRmppMGNKRnlUTk45eTZNOFdzTGJSd3M1UXdVblhYb0xKT3VyR3BUUTlVWVV5MnpER2M0dG9od1dSano1WXVyTUU1NWNxaTMxWjhNb3M0dkRMSGljMGVia1hjNEtXV1dydVBoTXNkMmpLcWd6VDlLS2E3am0xRkxsS1VGRnN1UHFLVU5EM0Y1YmRGSzlrY1ZoZndqZDlhTU54eGltMFFqaFM2SEhaTWFnbEgxTFBEOGtWdy81UE8yc3laTG0zL1U0VElvOFRCdjFIbDNQRU1uN1YvZEhtTThIeWFwVFgyLzlKVG0zVGZ1eFFiMnYrUkxmL1pGYTNwWFg4a2Qvd0RHYWRqNlUvOEFaQ0dwWDIreHBTZlQyTVhEemNsbGcwaFJqcTNNMlZ5NE5SUkJVdXBTMDlUREpWZW80cWFsVVVoTFl3WkZpZzB4OFQ5RElwc2hGcWVyMEZ4TXVoeDNFTndTTGI3bkE1SlF5dEo5VWZDWVl1NlJIaFlyc2p5WVcwa2tRNGZUMklZY2MzYVErSHh6ZlJFY0dLZllmRHdVSDlLTkdKYlNnNkpMUTNHSzJzVGkrb29ySmFqNzlDQ2xGZFA3aXh1dnFkbFJ1a2lLS1psaXBkVDRKU2xldHArbG1IRzhTcnFKcGw5amlmcWtRd2FsdUxGb2VwR3BQdUtrcVJ0Yk5tdHhKSlVrSjZWc2hPbDBQTkhjM3VpM0tOTkVYU3BJZVZ4bjlLTVR6SzA2L2toUEpHVFJyVXRtdmMweHJvLzdzbmtsbzByYit0a09KeXJMb2t0dlVqa2NYZEU1U21SZTltUjZuMG91SmJhNjBSbkpScGtmRU0zcVB4SE1sZG56ZklsYm9YanFhMi9nZmpha2lQanZWQzhldDFTOXo1OVhXdmNYajBVcm9YanNiMlNYOXlQajhIMXIzUG4yUHNsN256MnQwdjVQbnNaSzZYdWZQb3AxUzl4ZVBxdTN1THhuSE4xSmJma3plSjRNQ3FMdC9abnplUzYxN256YWY5Q1hqTDZLdmNoNGxtbTZTdG54ZkdlbnVSNDdNNDFLSStPY1Z2RS8vOFFBTEJFQUFnSUJBZ1VFQWdJQ0F3QUFBQUFBQUFFQ0VRTUVJUkFTTVVGUkJSTVVnU0F5Y2FFaXNUQmhrZi9hQUFnQkF3RUJQd0JGSWEyNGRCYnJpaWhKTGd1TkRqWW9EalhDTnNpSzF3U0ZFcC9nbFY4RXJFcS80RzZMTjJtVVFXd2tVVXlpeUwvQkpsQ3RGc3Rsc1Q0Mk5qbFhSamJJeWFPeEhvUkkxWERsUWh0RmJjS3JqWlp6TDhOaHk4RjJWc0w5UktUM1lwOWhTZEVHbWl4UGhHVHRvNlBZV1JpeUljOWg1a1F5SnJjbGtkRVg1T1lqSjNUNExaY0xRdHJMTEV5eE5KQ3FTSThMUXNjVWNpUGJRNGVDbWhRR25BdXhSZEQyT1pFSitSU1RMYlExWXJYQmI4S3NqMDNJdExZUnpDWlJTS1JSUnlxeWR1VEZGRVlKUk15R29wRWVoY2toT3hOOGJwbk15TTBLWW51Y3pzc3REb29vcmdrWkYva1J3bkx0Wm5lN29USWNFNllzakZiL0FBcFVSMll0aVBNeEZzc3NUTFJzSkRTYjRUL1NpY1dwc29WcGtoQ01iN0NkOE5uWnNsUkdLVHM2amt4UGw2TTdYK2EyNE4zRXlXNXQ4RW01YkUwMDZhSEJyb1cwam1vV1FUWkdhbDFObHUrRGJYQ1hTaUs1b1VJYnFMWXNqbzVqbllzaG5TeFZYYzkxbXN3eHdZWXlpbXI4L3dBSGQ4UFQ0M3JJWFhYdjArejFOVnFOcTZMcDlrZDBQWkZTYTNJcFhRa1VpTXBKVVJsUmNmSkdtN1RNaENWYm9XNVcxSEloWW1QRW1oWVY1TlpqY25HL0hjOWcxdUdMeFJXMy90OWpOQlF5TklSNmFxMWtOLzZzOVdWNnY2WGFpS0tUUkdNYXBsSk1sRlNYVGNsaWtsWWxTb1hVVWVWYkhMSzl4cDB5RWIzb2hMZmN0Y0ZLVlVSZG91WGcxbFhHdkhtem1hUnJweGVLTy84QVZkaWJiazJSalo2ZEYvTWh5MzlkVDFXRGVyYTM2THFLRkQySTB4UXZvUjIyYUpPNWJsRVZIdUxvTFowUzNzaFludnNLVDdrV21KUnJqcjc1NC94NG9XT1ROZENUaXRuOS93QUMwcjYyZkZsNU5CZ2NkVEZ1djllZTU2azRyVk5iZEYwTFhrcStoRlZZblJHYXFtZTA1dXhSRkZITW94b1U0MEtVTEp6Z3QwZkVrakRoeHgzU0ZERzVPMFp1VEhPa2h6VldRdG11ZFRqZmp6WkdTYU5hNHdVVkZMNkUwa0tGeHMwazR2VUxkZmU1cTRRbm5iMmZUc2UzQy8xUW5DRDJSQlJjMjBLSm1sR010eDZ6RlZKa2NXS1VlaG1oREZpbE91aUZxbEpNMGVTR1RKeXZjOXJGNFJxbGpnbDJJNmpGTG96SFFuY21KK1VLRnR1aE4xUWxKc1Y3bFNjbHNOWlZHeUhOVzdOSG5jZGJVZXFzeTNQSmNuNFBiUTVaWExxYUNFM050dnNjcDZoaWt0UnMrdzhhNkdIRlVGWnE4TjZXZGVCUWpBOU14dDZwZnd5T0pIckdQa2hCci9zeDQ0MXNoeVNRN1NzYzVSUSthdHhYRzl5cHBkVG5tazFZcHpKYXFDZzhVMDJlNDNIWm1IQzFybkpuY1YzdWpOQndiVFJvb1VwU3ZocThmdVNUVEZwcm1pMGliVW9ORDA4TGJlNTZmaGlzamZMVklpajFIRW5wMUpLNlpIVXphcEQxRDhzamxrbGJib2VmbTdzZWFjZTVIVVNqZE5udnpXN1o3OHJ2bUk1c3R1cEVaKzR1YVQzWkxHK3c1enhieTMvMmU1Q2QyNlNIbmluY0lwZjJ4WmNyamNtbjlDeUo3Y09kMkxVdUVmMFRSNzZsMVI3c096S3UrVjdHaVhMalk5VEM2VEpaRmxnNFNGeitEbXlOMnhTblJlVHlQM0hiRXBiMk8ySkVKT0JGYjJodVNiZGl5WEIyVHlRbnZXNUp4bEhaQzUxK3JGa3p4Ny8wUWMrZTN2OEFRNjVMSjc3RU5ocDA5aUVHaFBJbFZpZTI2UDRGaXhNV0RHOWhhYUwyUGpVaGFaRDA2ZmMrTENtOXhhT0hheGFYSGRIeElueElTOGkwS3ZkaTBVUExJNkNDN2kwTUdmQmhmVVdpaEJXbVEwc3A5ZWg4T0F0REErRkd1cDhMRjNiUGc0dTBtUFJLOW1mQlhrLy8yUT09IiBpZD0iYyIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiLz48L2RlZnM+PC9zdmc+", + "description": "A gas produced from vehicle emissions and the burning of fossil fuels.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'nitrogen', label: 'Nitrogen dioxide', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Nitrogen dioxide\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"public\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#3B911C\"},{\"from\":40,\"to\":90,\"color\":\"#7CC322\"},{\"from\":90,\"to\":120,\"color\":\"#F89E0D\"},{\"from\":120,\"to\":230,\"color\":\"#F77410\"},{\"from\":230,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":28,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#3B911C\"},{\"from\":40,\"to\":90,\"color\":\"#7CC322\"},{\"from\":90,\"to\":120,\"color\":\"#F89E0D\"},{\"from\":120,\"to\":230,\"color\":\"#F77410\"},{\"from\":230,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:Tk8yLXZhbHVlLWNhcmQtYmFja2dyb3VuZC5wbmc=:Tk8yLXZhbHVlLWNhcmQtYmFja2dyb3VuZC5wbmc=;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Nitrogen dioxide (NO2) card with background\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "nitrogen", + "nitrogen dioxide" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/nitrogen_dioxide__no2__chart_card.json b/application/src/main/data/json/system/widget_types/nitrogen_dioxide__no2__chart_card.json new file mode 100644 index 0000000000..859adbc1ce --- /dev/null +++ b/application/src/main/data/json/system/widget_types/nitrogen_dioxide__no2__chart_card.json @@ -0,0 +1,39 @@ +{ + "fqn": "nitrogen_dioxide_no2_chart_card", + "name": "Nitrogen dioxide (NO2) chart card", + "deprecated": false, + "image": "tb-image:Tk8yLXZhbHVlLWNoYXJ0LWNhcmQuc3Zn:Tk8yLXZhbHVlLWNoYXJ0LWNhcmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSIxOTkiIGhlaWdodD0iMTU5IiByeD0iMy41IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC4xMiIvPgo8cGF0aCBkPSJNMTkuOTk5OSAxMy4zMzMzQzE2LjMxOTkgMTMuMzMzMyAxMy4zMzMzIDE2LjMyIDEzLjMzMzMgMjBDMTMuMzMzMyAyMy42OCAxNi4zMTk5IDI2LjY2NjYgMTkuOTk5OSAyNi42NjY2QzIzLjY3OTkgMjYuNjY2NiAyNi42NjY2IDIzLjY4IDI2LjY2NjYgMjBDMjYuNjY2NiAxNi4zMiAyMy42Nzk5IDEzLjMzMzMgMTkuOTk5OSAxMy4zMzMzWk0xNC42NjY2IDIwQzE0LjY2NjYgMTkuNTkzMyAxNC43MTk5IDE5LjE5MzMgMTQuODA2NiAxOC44MTMzTDE3Ljk5MzMgMjJWMjIuNjY2NkMxNy45OTMzIDIzLjQgMTguNTkzMyAyNCAxOS4zMjY2IDI0VjI1LjI4NjZDMTYuNzA2NiAyNC45NTMzIDE0LjY2NjYgMjIuNzEzMyAxNC42NjY2IDIwWk0yMy45MjY2IDIzLjZDMjMuNzUzMyAyMy4wNiAyMy4yNTk5IDIyLjY2NjYgMjIuNjU5OSAyMi42NjY2SDIxLjk5MzNWMjAuNjY2NkMyMS45OTMzIDIwLjMgMjEuNjkzMyAyMCAyMS4zMjY2IDIwSDE3LjMyNjZWMTguNjY2NkgxOC42NTk5QzE5LjAyNjYgMTguNjY2NiAxOS4zMjY2IDE4LjM2NjYgMTkuMzI2NiAxOFYxNi42NjY2SDIwLjY1OTlDMjEuMzkzMyAxNi42NjY2IDIxLjk5MzMgMTYuMDY2NiAyMS45OTMzIDE1LjMzMzNWMTUuMDZDMjMuOTQ2NiAxNS44NDY2IDI1LjMzMzMgMTcuNzY2NiAyNS4zMzMzIDIwQzI1LjMzMzMgMjEuMzg2NiAyNC43OTMzIDIyLjY1MzMgMjMuOTI2NiAyMy42WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNNDAuOTA3MiAxNS4wNDY5VjI1SDM5LjE5MTRMMzQuNzI3NSAxNy44NzAxVjI1SDMzLjAxMTdWMTUuMDQ2OUgzNC43Mjc1TDM5LjIwNTEgMjIuMTkwNFYxNS4wNDY5SDQwLjkwNzJaTTQ0LjgwMDggMTcuNjAzNVYyNUg0My4xNDY1VjE3LjYwMzVINDQuODAwOFpNNDMuMDM3MSAxNS42NjIxQzQzLjAzNzEgMTUuNDExNSA0My4xMTkxIDE1LjIwNDEgNDMuMjgzMiAxNS4wNEM0My40NTE4IDE0Ljg3MTQgNDMuNjg0MiAxNC43ODcxIDQzLjk4MDUgMTQuNzg3MUM0NC4yNzIxIDE0Ljc4NzEgNDQuNTAyMyAxNC44NzE0IDQ0LjY3MDkgMTUuMDRDNDQuODM5NSAxNS4yMDQxIDQ0LjkyMzggMTUuNDExNSA0NC45MjM4IDE1LjY2MjFDNDQuOTIzOCAxNS45MDgyIDQ0LjgzOTUgMTYuMTEzMyA0NC42NzA5IDE2LjI3NzNDNDQuNTAyMyAxNi40NDE0IDQ0LjI3MjEgMTYuNTIzNCA0My45ODA1IDE2LjUyMzRDNDMuNjg0MiAxNi41MjM0IDQzLjQ1MTggMTYuNDQxNCA0My4yODMyIDE2LjI3NzNDNDMuMTE5MSAxNi4xMTMzIDQzLjAzNzEgMTUuOTA4MiA0My4wMzcxIDE1LjY2MjFaTTUwLjI0NjEgMTcuNjAzNVYxOC44MDY2SDQ2LjA3NjJWMTcuNjAzNUg1MC4yNDYxWk00Ny4yNzkzIDE1Ljc5Mkg0OC45MjY4VjIyLjk1NjFDNDguOTI2OCAyMy4xODM5IDQ4Ljk1ODcgMjMuMzU5NCA0OS4wMjI1IDIzLjQ4MjRDNDkuMDkwOCAyMy42MDA5IDQ5LjE4NDIgMjMuNjgwNyA0OS4zMDI3IDIzLjcyMTdDNDkuNDIxMiAyMy43NjI3IDQ5LjU2MDIgMjMuNzgzMiA0OS43MTk3IDIzLjc4MzJDNDkuODMzNyAyMy43ODMyIDQ5Ljk0MyAyMy43NzY0IDUwLjA0NzkgMjMuNzYyN0M1MC4xNTI3IDIzLjc0OSA1MC4yMzcgMjMuNzM1NCA1MC4zMDA4IDIzLjcyMTdMNTAuMzA3NiAyNC45Nzk1QzUwLjE3MDkgMjUuMDIwNSA1MC4wMTE0IDI1LjA1NyA0OS44MjkxIDI1LjA4ODlDNDkuNjUxNCAyNS4xMjA4IDQ5LjQ0NjMgMjUuMTM2NyA0OS4yMTM5IDI1LjEzNjdDNDguODM1NiAyNS4xMzY3IDQ4LjUwMDcgMjUuMDcwNiA0OC4yMDkgMjQuOTM4NUM0Ny45MTczIDI0LjgwMTggNDcuNjg5NSAyNC41ODA3IDQ3LjUyNTQgMjQuMjc1NEM0Ny4zNjEzIDIzLjk3MDEgNDcuMjc5MyAyMy41NjQ1IDQ3LjI3OTMgMjMuMDU4NlYxNS43OTJaTTUzLjQyMTkgMTkuMDExN1YyNUg1MS43NzQ0VjE3LjYwMzVINTMuMzQ2N0w1My40MjE5IDE5LjAxMTdaTTU1LjY4NDYgMTcuNTU1N0w1NS42NzA5IDE5LjA4NjlDNTUuNTcwNiAxOS4wNjg3IDU1LjQ2MTMgMTkuMDU1IDU1LjM0MjggMTkuMDQ1OUM1NS4yMjg4IDE5LjAzNjggNTUuMTE0OSAxOS4wMzIyIDU1LjAwMSAxOS4wMzIyQzU0LjcxODQgMTkuMDMyMiA1NC40NzAxIDE5LjA3MzIgNTQuMjU1OSAxOS4xNTUzQzU0LjA0MTcgMTkuMjMyNyA1My44NjE3IDE5LjM0NjcgNTMuNzE1OCAxOS40OTcxQzUzLjU3NDUgMTkuNjQyOSA1My40NjUyIDE5LjgyMDYgNTMuMzg3NyAyMC4wMzAzQzUzLjMxMDIgMjAuMjM5OSA1My4yNjQ2IDIwLjQ3NDYgNTMuMjUxIDIwLjczNDRMNTIuODc1IDIwLjc2MTdDNTIuODc1IDIwLjI5NjkgNTIuOTIwNiAxOS44NjYyIDUzLjAxMTcgMTkuNDY5N0M1My4xMDI5IDE5LjA3MzIgNTMuMjM5NiAxOC43MjQ2IDUzLjQyMTkgMTguNDIzOEM1My42MDg3IDE4LjEyMyA1My44NDExIDE3Ljg4ODMgNTQuMTE5MSAxNy43MTk3QzU0LjQwMTcgMTcuNTUxMSA1NC43Mjc1IDE3LjQ2NjggNTUuMDk2NyAxNy40NjY4QzU1LjE5NjkgMTcuNDY2OCA1NS4zMDQgMTcuNDc1OSA1NS40MTggMTcuNDk0MUM1NS41MzY1IDE3LjUxMjQgNTUuNjI1MyAxNy41MzI5IDU1LjY4NDYgMTcuNTU1N1pNNTYuNDc0NiAyMS4zODM4VjIxLjIyNjZDNTYuNDc0NiAyMC42OTM0IDU2LjU1MjEgMjAuMTk4OSA1Ni43MDcgMTkuNzQzMkM1Ni44NjIgMTkuMjgyOSA1Ny4wODUzIDE4Ljg4NDEgNTcuMzc3IDE4LjU0NjlDNTcuNjczMiAxOC4yMDUxIDU4LjAzMzIgMTcuOTQwOCA1OC40NTcgMTcuNzUzOUM1OC44ODU0IDE3LjU2MjUgNTkuMzY4NSAxNy40NjY4IDU5LjkwNjIgMTcuNDY2OEM2MC40NDg2IDE3LjQ2NjggNjAuOTMxNiAxNy41NjI1IDYxLjM1NTUgMTcuNzUzOUM2MS43ODM5IDE3Ljk0MDggNjIuMTQ2MiAxOC4yMDUxIDYyLjQ0MjQgMTguNTQ2OUM2Mi43Mzg2IDE4Ljg4NDEgNjIuOTY0MiAxOS4yODI5IDYzLjExOTEgMTkuNzQzMkM2My4yNzQxIDIwLjE5ODkgNjMuMzUxNiAyMC42OTM0IDYzLjM1MTYgMjEuMjI2NlYyMS4zODM4QzYzLjM1MTYgMjEuOTE3IDYzLjI3NDEgMjIuNDExNSA2My4xMTkxIDIyLjg2NzJDNjIuOTY0MiAyMy4zMjI5IDYyLjczODYgMjMuNzIxNyA2Mi40NDI0IDI0LjA2MzVDNjIuMTQ2MiAyNC40MDA3IDYxLjc4NjEgMjQuNjY1IDYxLjM2MjMgMjQuODU2NEM2MC45Mzg1IDI1LjA0MzMgNjAuNDU3NyAyNS4xMzY3IDU5LjkxOTkgMjUuMTM2N0M1OS4zNzc2IDI1LjEzNjcgNTguODkyMyAyNS4wNDMzIDU4LjQ2MzkgMjQuODU2NEM1OC4wNCAyNC42NjUgNTcuNjggMjQuNDAwNyA1Ny4zODM4IDI0LjA2MzVDNTcuMDg3NiAyMy43MjE3IDU2Ljg2MiAyMy4zMjI5IDU2LjcwNyAyMi44NjcyQzU2LjU1MjEgMjIuNDExNSA1Ni40NzQ2IDIxLjkxNyA1Ni40NzQ2IDIxLjM4MzhaTTU4LjEyMjEgMjEuMjI2NlYyMS4zODM4QzU4LjEyMjEgMjEuNzE2NSA1OC4xNTYyIDIyLjAzMDkgNTguMjI0NiAyMi4zMjcxQzU4LjI5MyAyMi42MjM0IDU4LjQwMDEgMjIuODgzMSA1OC41NDU5IDIzLjEwNjRDNTguNjkxNyAyMy4zMjk4IDU4Ljg3ODYgMjMuNTA1MiA1OS4xMDY0IDIzLjYzMjhDNTkuMzM0MyAyMy43NjA0IDU5LjYwNTUgMjMuODI0MiA1OS45MTk5IDIzLjgyNDJDNjAuMjI1MyAyMy44MjQyIDYwLjQ4OTYgMjMuNzYwNCA2MC43MTI5IDIzLjYzMjhDNjAuOTQwOCAyMy41MDUyIDYxLjEyNzYgMjMuMzI5OCA2MS4yNzM0IDIzLjEwNjRDNjEuNDE5MyAyMi44ODMxIDYxLjUyNjQgMjIuNjIzNCA2MS41OTQ3IDIyLjMyNzFDNjEuNjY3NiAyMi4wMzA5IDYxLjcwNDEgMjEuNzE2NSA2MS43MDQxIDIxLjM4MzhWMjEuMjI2NkM2MS43MDQxIDIwLjg5ODQgNjEuNjY3NiAyMC41ODg1IDYxLjU5NDcgMjAuMjk2OUM2MS41MjY0IDIwLjAwMDcgNjEuNDE3IDE5LjczODYgNjEuMjY2NiAxOS41MTA3QzYxLjEyMDggMTkuMjgyOSA2MC45MzM5IDE5LjEwNTEgNjAuNzA2MSAxOC45Nzc1QzYwLjQ4MjcgMTguODQ1NCA2MC4yMTYxIDE4Ljc3OTMgNTkuOTA2MiAxOC43NzkzQzU5LjU5NjQgMTguNzc5MyA1OS4zMjc1IDE4Ljg0NTQgNTkuMDk5NiAxOC45Nzc1QzU4Ljg3NjMgMTkuMTA1MSA1OC42OTE3IDE5LjI4MjkgNTguNTQ1OSAxOS41MTA3QzU4LjQwMDEgMTkuNzM4NiA1OC4yOTMgMjAuMDAwNyA1OC4yMjQ2IDIwLjI5NjlDNTguMTU2MiAyMC41ODg1IDU4LjEyMjEgMjAuODk4NCA1OC4xMjIxIDIxLjIyNjZaTTY5LjcxOTcgMTcuNjAzNUg3MS4yMTY4VjI0Ljc5NDlDNzEuMjE2OCAyNS40NjAzIDcxLjA3NTUgMjYuMDI1NCA3MC43OTMgMjYuNDkwMkM3MC41MTA0IDI2Ljk1NTEgNzAuMTE2MiAyNy4zMDgzIDY5LjYxMDQgMjcuNTQ5OEM2OS4xMDQ1IDI3Ljc5NTkgNjguNTE4OSAyNy45MTg5IDY3Ljg1MzUgMjcuOTE4OUM2Ny41NzEgMjcuOTE4OSA2Ny4yNTY1IDI3Ljg3NzkgNjYuOTEwMiAyNy43OTU5QzY2LjU2ODQgMjcuNzEzOSA2Ni4yMzU3IDI3LjU4MTcgNjUuOTEyMSAyNy4zOTk0QzY1LjU5MzEgMjcuMjIxNyA2NS4zMjY1IDI2Ljk4NyA2NS4xMTIzIDI2LjY5NTNMNjUuODg0OCAyNS43MjQ2QzY2LjE0OTEgMjYuMDM5MSA2Ni40NDA4IDI2LjI2OTIgNjYuNzU5OCAyNi40MTVDNjcuMDc4OCAyNi41NjA5IDY3LjQxMzcgMjYuNjMzOCA2Ny43NjQ2IDI2LjYzMzhDNjguMTQyOSAyNi42MzM4IDY4LjQ2NDIgMjYuNTYzMiA2OC43Mjg1IDI2LjQyMTlDNjguOTk3NCAyNi4yODUyIDY5LjIwNDggMjYuMDgyNCA2OS4zNTA2IDI1LjgxMzVDNjkuNDk2NCAyNS41NDQ2IDY5LjU2OTMgMjUuMjE2NSA2OS41NjkzIDI0LjgyOTFWMTkuMjc4M0w2OS43MTk3IDE3LjYwMzVaTTY0LjY5NTMgMjEuMzgzOFYyMS4yNDAyQzY0LjY5NTMgMjAuNjc5NyA2NC43NjM3IDIwLjE2OTMgNjQuOTAwNCAxOS43MDlDNjUuMDM3MSAxOS4yNDQxIDY1LjIzMzEgMTguODQ1NCA2NS40ODgzIDE4LjUxMjdDNjUuNzQzNSAxOC4xNzU1IDY2LjA1MzQgMTcuOTE4IDY2LjQxOCAxNy43NDAyQzY2Ljc4MjYgMTcuNTU3OSA2Ny4xOTUgMTcuNDY2OCA2Ny42NTUzIDE3LjQ2NjhDNjguMTMzOCAxNy40NjY4IDY4LjU0MTcgMTcuNTUzNCA2OC44Nzg5IDE3LjcyNjZDNjkuMjIwNyAxNy44OTk3IDY5LjUwNTUgMTguMTQ4MSA2OS43MzM0IDE4LjQ3MTdDNjkuOTYxMyAxOC43OTA3IDcwLjEzOSAxOS4xNzM1IDcwLjI2NjYgMTkuNjIwMUM3MC4zOTg4IDIwLjA2MjIgNzAuNDk2NyAyMC41NTQ0IDcwLjU2MDUgMjEuMDk2N1YyMS41NTQ3QzcwLjUwMTMgMjIuMDgzMyA3MC40MDEgMjIuNTY2NCA3MC4yNTk4IDIzLjAwMzlDNzAuMTE4NSAyMy40NDE0IDY5LjkzMTYgMjMuODE5NyA2OS42OTkyIDI0LjEzODdDNjkuNDY2OCAyNC40NTc3IDY5LjE3OTcgMjQuNzAzOCA2OC44Mzc5IDI0Ljg3N0M2OC41MDA3IDI1LjA1MDEgNjguMTAxOSAyNS4xMzY3IDY3LjY0MTYgMjUuMTM2N0M2Ny4xOTA0IDI1LjEzNjcgNjYuNzgyNiAyNS4wNDMzIDY2LjQxOCAyNC44NTY0QzY2LjA1NzkgMjQuNjY5NiA2NS43NDggMjQuNDA3NiA2NS40ODgzIDI0LjA3MDNDNjUuMjMzMSAyMy43MzMxIDY1LjAzNzEgMjMuMzM2NiA2NC45MDA0IDIyLjg4MDlDNjQuNzYzNyAyMi40MjA2IDY0LjY5NTMgMjEuOTIxNSA2NC42OTUzIDIxLjM4MzhaTTY2LjM0MjggMjEuMjQwMlYyMS4zODM4QzY2LjM0MjggMjEuNzIxIDY2LjM3NDcgMjIuMDM1NSA2Ni40Mzg1IDIyLjMyNzFDNjYuNTA2OCAyMi42MTg4IDY2LjYwOTQgMjIuODc2MyA2Ni43NDYxIDIzLjA5OTZDNjYuODg3NCAyMy4zMTg0IDY3LjA2NTEgMjMuNDkxNSA2Ny4yNzkzIDIzLjYxOTFDNjcuNDk4IDIzLjc0MjIgNjcuNzU1NSAyMy44MDM3IDY4LjA1MTggMjMuODAzN0M2OC40MzkxIDIzLjgwMzcgNjguNzU1OSAyMy43MjE3IDY5LjAwMiAyMy41NTc2QzY5LjI1MjYgMjMuMzkzNiA2OS40NDQgMjMuMTcyNSA2OS41NzYyIDIyLjg5NDVDNjkuNzEyOSAyMi42MTIgNjkuODA4NiAyMi4yOTc1IDY5Ljg2MzMgMjEuOTUxMlYyMC43MTM5QzY5LjgzNTkgMjAuNDQ1IDY5Ljc3OSAyMC4xOTQzIDY5LjY5MjQgMTkuOTYxOUM2OS42MTA0IDE5LjcyOTUgNjkuNDk4NyAxOS41MjY3IDY5LjM1NzQgMTkuMzUzNUM2OS4yMTYxIDE5LjE3NTggNjkuMDM4NCAxOS4wMzkxIDY4LjgyNDIgMTguOTQzNEM2OC42MSAxOC44NDMxIDY4LjM1NzEgMTguNzkzIDY4LjA2NTQgMTguNzkzQzY3Ljc2OTIgMTguNzkzIDY3LjUxMTcgMTguODU2OCA2Ny4yOTMgMTguOTg0NEM2Ny4wNzQyIDE5LjExMiA2Ni44OTQyIDE5LjI4NzQgNjYuNzUyOSAxOS41MTA3QzY2LjYxNjIgMTkuNzM0IDY2LjUxMzcgMTkuOTkzOCA2Ni40NDUzIDIwLjI5QzY2LjM3NyAyMC41ODYzIDY2LjM0MjggMjAuOTAzIDY2LjM0MjggMjEuMjQwMlpNNzYuNDM2NSAyNS4xMzY3Qzc1Ljg4OTYgMjUuMTM2NyA3NS4zOTUyIDI1LjA0NzkgNzQuOTUzMSAyNC44NzAxQzc0LjUxNTYgMjQuNjg3OCA3NC4xNDE5IDI0LjQzNDkgNzMuODMyIDI0LjExMTNDNzMuNTI2NyAyMy43ODc4IDczLjI5MiAyMy40MDcyIDczLjEyNzkgMjIuOTY5N0M3Mi45NjM5IDIyLjUzMjIgNzIuODgxOCAyMi4wNjA1IDcyLjg4MTggMjEuNTU0N1YyMS4yODEyQzcyLjg4MTggMjAuNzAyNSA3Mi45NjYxIDIwLjE3ODQgNzMuMTM0OCAxOS43MDlDNzMuMzAzNCAxOS4yMzk2IDczLjUzODEgMTguODM4NSA3My44Mzg5IDE4LjUwNTlDNzQuMTM5NiAxOC4xNjg2IDc0LjQ5NTEgMTcuOTExMSA3NC45MDUzIDE3LjczMzRDNzUuMzE1NCAxNy41NTU3IDc1Ljc1OTggMTcuNDY2OCA3Ni4yMzgzIDE3LjQ2NjhDNzYuNzY2OSAxNy40NjY4IDc3LjIyOTUgMTcuNTU1NyA3Ny42MjYgMTcuNzMzNEM3OC4wMjI1IDE3LjkxMTEgNzguMzUwNiAxOC4xNjE4IDc4LjYxMDQgMTguNDg1NEM3OC44NzQ3IDE4LjgwNDQgNzkuMDcwNiAxOS4xODQ5IDc5LjE5ODIgMTkuNjI3Qzc5LjMzMDQgMjAuMDY5IDc5LjM5NjUgMjAuNTU2NiA3OS4zOTY1IDIxLjA4OThWMjEuNzkzOUg3My42ODE2VjIwLjYxMTNINzcuNzY5NVYyMC40ODE0Qzc3Ljc2MDQgMjAuMTg1MiA3Ny43MDEyIDE5LjkwNzIgNzcuNTkxOCAxOS42NDc1Qzc3LjQ4NyAxOS4zODc3IDc3LjMyNTIgMTkuMTc4MSA3Ny4xMDY0IDE5LjAxODZDNzYuODg3NyAxOC44NTkgNzYuNTk2IDE4Ljc3OTMgNzYuMjMxNCAxOC43NzkzQzc1Ljk1OCAxOC43NzkzIDc1LjcxNDIgMTguODM4NSA3NS41IDE4Ljk1N0M3NS4yOTA0IDE5LjA3MSA3NS4xMTQ5IDE5LjIzNzMgNzQuOTczNiAxOS40NTYxQzc0LjgzMjQgMTkuNjc0OCA3NC43MjMgMTkuOTM5MSA3NC42NDU1IDIwLjI0OUM3NC41NzI2IDIwLjU1NDQgNzQuNTM2MSAyMC44OTg0IDc0LjUzNjEgMjEuMjgxMlYyMS41NTQ3Qzc0LjUzNjEgMjEuODc4MyA3NC41Nzk0IDIyLjE3OSA3NC42NjYgMjIuNDU3Qzc0Ljc1NzIgMjIuNzMwNSA3NC44ODkzIDIyLjk2OTcgNzUuMDYyNSAyMy4xNzQ4Qzc1LjIzNTcgMjMuMzc5OSA3NS40NDUzIDIzLjU0MTcgNzUuNjkxNCAyMy42NjAyQzc1LjkzNzUgMjMuNzc0MSA3Ni4yMTc4IDIzLjgzMTEgNzYuNTMyMiAyMy44MzExQzc2LjkyODcgMjMuODMxMSA3Ny4yODE5IDIzLjc1MTMgNzcuNTkxOCAyMy41OTE4Qzc3LjkwMTcgMjMuNDMyMyA3OC4xNzA2IDIzLjIwNjcgNzguMzk4NCAyMi45MTVMNzkuMjY2NiAyMy43NTU5Qzc5LjEwNzEgMjMuOTg4MyA3OC44OTk3IDI0LjIxMTYgNzguNjQ0NSAyNC40MjU4Qzc4LjM4OTMgMjQuNjM1NCA3OC4wNzcxIDI0LjgwNjMgNzcuNzA4IDI0LjkzODVDNzcuMzQzNCAyNS4wNzA2IDc2LjkxOTYgMjUuMTM2NyA3Ni40MzY1IDI1LjEzNjdaTTgyLjU2NTQgMTkuMTgyNlYyNUg4MC45MThWMTcuNjAzNUg4Mi40Njk3TDgyLjU2NTQgMTkuMTgyNlpNODIuMjcxNSAyMS4wMjgzTDgxLjczODMgMjEuMDIxNUM4MS43NDI4IDIwLjQ5NzQgODEuODE1OCAyMC4wMTY2IDgxLjk1NyAxOS41NzkxQzgyLjEwMjkgMTkuMTQxNiA4Mi4zMDM0IDE4Ljc2NTYgODIuNTU4NiAxOC40NTEyQzgyLjgxODQgMTguMTM2NyA4My4xMjgzIDE3Ljg5NTIgODMuNDg4MyAxNy43MjY2QzgzLjg0ODMgMTcuNTUzNCA4NC4yNDkzIDE3LjQ2NjggODQuNjkxNCAxNy40NjY4Qzg1LjA0NjkgMTcuNDY2OCA4NS4zNjgyIDE3LjUxNjkgODUuNjU1MyAxNy42MTcyQzg1Ljk0NjkgMTcuNzEyOSA4Ni4xOTUzIDE3Ljg3MDEgODYuNDAwNCAxOC4wODg5Qzg2LjYxIDE4LjMwNzYgODYuNzY5NSAxOC41OTI0IDg2Ljg3ODkgMTguOTQzNEM4Ni45ODgzIDE5LjI4OTcgODcuMDQzIDE5LjcxNTggODcuMDQzIDIwLjIyMTdWMjVIODUuMzg4N1YyMC4yMTQ4Qzg1LjM4ODcgMTkuODU5NCA4NS4zMzYzIDE5LjU3OTEgODUuMjMxNCAxOS4zNzRDODUuMTMxMiAxOS4xNjQ0IDg0Ljk4MzEgMTkuMDE2MyA4NC43ODcxIDE4LjkyOTdDODQuNTk1NyAxOC44Mzg1IDg0LjM1NjQgMTguNzkzIDg0LjA2OTMgMTguNzkzQzgzLjc4NjggMTguNzkzIDgzLjUzMzkgMTguODUyMiA4My4zMTA1IDE4Ljk3MDdDODMuMDg3MiAxOS4wODkyIDgyLjg5ODEgMTkuMjUxIDgyLjc0MzIgMTkuNDU2MUM4Mi41OTI4IDE5LjY2MTEgODIuNDc2NiAxOS44OTgxIDgyLjM5NDUgMjAuMTY3QzgyLjMxMjUgMjAuNDM1OSA4Mi4yNzE1IDIwLjcyMyA4Mi4yNzE1IDIxLjAyODNaTTk3LjIyMjcgMjMuNDY4OFYxNC41SDk4Ljg3N1YyNUg5Ny4zNzk5TDk3LjIyMjcgMjMuNDY4OFpNOTIuNDEwMiAyMS4zODM4VjIxLjI0MDJDOTIuNDEwMiAyMC42Nzk3IDkyLjQ3NjIgMjAuMTY5MyA5Mi42MDg0IDE5LjcwOUM5Mi43NDA2IDE5LjI0NDEgOTIuOTMyIDE4Ljg0NTQgOTMuMTgyNiAxOC41MTI3QzkzLjQzMzMgMTguMTc1NSA5My43Mzg2IDE3LjkxOCA5NC4wOTg2IDE3Ljc0MDJDOTQuNDU4NyAxNy41NTc5IDk0Ljg2NDMgMTcuNDY2OCA5NS4zMTU0IDE3LjQ2NjhDOTUuNzYyIDE3LjQ2NjggOTYuMTU0IDE3LjU1MzQgOTYuNDkxMiAxNy43MjY2Qzk2LjgyODUgMTcuODk5NyA5Ny4xMTU2IDE4LjE0ODEgOTcuMzUyNSAxOC40NzE3Qzk3LjU4OTUgMTguNzkwNyA5Ny43Nzg2IDE5LjE3MzUgOTcuOTE5OSAxOS42MjAxQzk4LjA2MTIgMjAuMDYyMiA5OC4xNjE1IDIwLjU1NDQgOTguMjIwNyAyMS4wOTY3VjIxLjU1NDdDOTguMTYxNSAyMi4wODMzIDk4LjA2MTIgMjIuNTY2NCA5Ny45MTk5IDIzLjAwMzlDOTcuNzc4NiAyMy40NDE0IDk3LjU4OTUgMjMuODE5NyA5Ny4zNTI1IDI0LjEzODdDOTcuMTE1NiAyNC40NTc3IDk2LjgyNjIgMjQuNzAzOCA5Ni40ODQ0IDI0Ljg3N0M5Ni4xNDcxIDI1LjA1MDEgOTUuNzUyOSAyNS4xMzY3IDk1LjMwMTggMjUuMTM2N0M5NC44NTUxIDI1LjEzNjcgOTQuNDUxOCAyNS4wNDMzIDk0LjA5MTggMjQuODU2NEM5My43MzYzIDI0LjY2OTYgOTMuNDMzMyAyNC40MDc2IDkzLjE4MjYgMjQuMDcwM0M5Mi45MzIgMjMuNzMzMSA5Mi43NDA2IDIzLjMzNjYgOTIuNjA4NCAyMi44ODA5QzkyLjQ3NjIgMjIuNDIwNiA5Mi40MTAyIDIxLjkyMTUgOTIuNDEwMiAyMS4zODM4Wk05NC4wNTc2IDIxLjI0MDJWMjEuMzgzOEM5NC4wNTc2IDIxLjcyMSA5NC4wODcyIDIyLjAzNTUgOTQuMTQ2NSAyMi4zMjcxQzk0LjIxMDMgMjIuNjE4OCA5NC4zMDgzIDIyLjg3NjMgOTQuNDQwNCAyMy4wOTk2Qzk0LjU3MjYgMjMuMzE4NCA5NC43NDM1IDIzLjQ5MTUgOTQuOTUzMSAyMy42MTkxQzk1LjE2NzMgMjMuNzQyMiA5NS40MjI1IDIzLjgwMzcgOTUuNzE4OCAyMy44MDM3Qzk2LjA5MjQgMjMuODAzNyA5Ni40MDAxIDIzLjcyMTcgOTYuNjQxNiAyMy41NTc2Qzk2Ljg4MzEgMjMuMzkzNiA5Ny4wNzIzIDIzLjE3MjUgOTcuMjA5IDIyLjg5NDVDOTcuMzUwMyAyMi42MTIgOTcuNDQ2IDIyLjI5NzUgOTcuNDk2MSAyMS45NTEyVjIwLjcxMzlDOTcuNDY4OCAyMC40NDUgOTcuNDExOCAyMC4xOTQzIDk3LjMyNTIgMTkuOTYxOUM5Ny4yNDMyIDE5LjcyOTUgOTcuMTMxNSAxOS41MjY3IDk2Ljk5MDIgMTkuMzUzNUM5Ni44NDkgMTkuMTc1OCA5Ni42NzM1IDE5LjAzOTEgOTYuNDYzOSAxOC45NDM0Qzk2LjI1ODggMTguODQzMSA5Ni4wMTUgMTguNzkzIDk1LjczMjQgMTguNzkzQzk1LjQzMTYgMTguNzkzIDk1LjE3NjQgMTguODU2OCA5NC45NjY4IDE4Ljk4NDRDOTQuNzU3MiAxOS4xMTIgOTQuNTg0IDE5LjI4NzQgOTQuNDQ3MyAxOS41MTA3Qzk0LjMxNTEgMTkuNzM0IDk0LjIxNzEgMTkuOTkzOCA5NC4xNTMzIDIwLjI5Qzk0LjA4OTUgMjAuNTg2MyA5NC4wNTc2IDIwLjkwMyA5NC4wNTc2IDIxLjI0MDJaTTEwMi42MTMgMTcuNjAzNVYyNUgxMDAuOTU5VjE3LjYwMzVIMTAyLjYxM1pNMTAwLjg1IDE1LjY2MjFDMTAwLjg1IDE1LjQxMTUgMTAwLjkzMiAxNS4yMDQxIDEwMS4wOTYgMTUuMDRDMTAxLjI2NCAxNC44NzE0IDEwMS40OTcgMTQuNzg3MSAxMDEuNzkzIDE0Ljc4NzFDMTAyLjA4NSAxNC43ODcxIDEwMi4zMTUgMTQuODcxNCAxMDIuNDgzIDE1LjA0QzEwMi42NTIgMTUuMjA0MSAxMDIuNzM2IDE1LjQxMTUgMTAyLjczNiAxNS42NjIxQzEwMi43MzYgMTUuOTA4MiAxMDIuNjUyIDE2LjExMzMgMTAyLjQ4MyAxNi4yNzczQzEwMi4zMTUgMTYuNDQxNCAxMDIuMDg1IDE2LjUyMzQgMTAxLjc5MyAxNi41MjM0QzEwMS40OTcgMTYuNTIzNCAxMDEuMjY0IDE2LjQ0MTQgMTAxLjA5NiAxNi4yNzczQzEwMC45MzIgMTYuMTEzMyAxMDAuODUgMTUuOTA4MiAxMDAuODUgMTUuNjYyMVpNMTA0LjM1NCAyMS4zODM4VjIxLjIyNjZDMTA0LjM1NCAyMC42OTM0IDEwNC40MzEgMjAuMTk4OSAxMDQuNTg2IDE5Ljc0MzJDMTA0Ljc0MSAxOS4yODI5IDEwNC45NjQgMTguODg0MSAxMDUuMjU2IDE4LjU0NjlDMTA1LjU1MiAxOC4yMDUxIDEwNS45MTIgMTcuOTQwOCAxMDYuMzM2IDE3Ljc1MzlDMTA2Ljc2NCAxNy41NjI1IDEwNy4yNDcgMTcuNDY2OCAxMDcuNzg1IDE3LjQ2NjhDMTA4LjMyNyAxNy40NjY4IDEwOC44MTEgMTcuNTYyNSAxMDkuMjM0IDE3Ljc1MzlDMTA5LjY2MyAxNy45NDA4IDExMC4wMjUgMTguMjA1MSAxMTAuMzIxIDE4LjU0NjlDMTEwLjYxOCAxOC44ODQxIDExMC44NDMgMTkuMjgyOSAxMTAuOTk4IDE5Ljc0MzJDMTExLjE1MyAyMC4xOTg5IDExMS4yMyAyMC42OTM0IDExMS4yMyAyMS4yMjY2VjIxLjM4MzhDMTExLjIzIDIxLjkxNyAxMTEuMTUzIDIyLjQxMTUgMTEwLjk5OCAyMi44NjcyQzExMC44NDMgMjMuMzIyOSAxMTAuNjE4IDIzLjcyMTcgMTEwLjMyMSAyNC4wNjM1QzExMC4wMjUgMjQuNDAwNyAxMDkuNjY1IDI0LjY2NSAxMDkuMjQxIDI0Ljg1NjRDMTA4LjgxNyAyNS4wNDMzIDEwOC4zMzcgMjUuMTM2NyAxMDcuNzk5IDI1LjEzNjdDMTA3LjI1NyAyNS4xMzY3IDEwNi43NzEgMjUuMDQzMyAxMDYuMzQzIDI0Ljg1NjRDMTA1LjkxOSAyNC42NjUgMTA1LjU1OSAyNC40MDA3IDEwNS4yNjMgMjQuMDYzNUMxMDQuOTY2IDIzLjcyMTcgMTA0Ljc0MSAyMy4zMjI5IDEwNC41ODYgMjIuODY3MkMxMDQuNDMxIDIyLjQxMTUgMTA0LjM1NCAyMS45MTcgMTA0LjM1NCAyMS4zODM4Wk0xMDYuMDAxIDIxLjIyNjZWMjEuMzgzOEMxMDYuMDAxIDIxLjcxNjUgMTA2LjAzNSAyMi4wMzA5IDEwNi4xMDQgMjIuMzI3MUMxMDYuMTcyIDIyLjYyMzQgMTA2LjI3OSAyMi44ODMxIDEwNi40MjUgMjMuMTA2NEMxMDYuNTcxIDIzLjMyOTggMTA2Ljc1NyAyMy41MDUyIDEwNi45ODUgMjMuNjMyOEMxMDcuMjEzIDIzLjc2MDQgMTA3LjQ4NCAyMy44MjQyIDEwNy43OTkgMjMuODI0MkMxMDguMTA0IDIzLjgyNDIgMTA4LjM2OCAyMy43NjA0IDEwOC41OTIgMjMuNjMyOEMxMDguODIgMjMuNTA1MiAxMDkuMDA3IDIzLjMyOTggMTA5LjE1MiAyMy4xMDY0QzEwOS4yOTggMjIuODgzMSAxMDkuNDA1IDIyLjYyMzQgMTA5LjQ3NCAyMi4zMjcxQzEwOS41NDcgMjIuMDMwOSAxMDkuNTgzIDIxLjcxNjUgMTA5LjU4MyAyMS4zODM4VjIxLjIyNjZDMTA5LjU4MyAyMC44OTg0IDEwOS41NDcgMjAuNTg4NSAxMDkuNDc0IDIwLjI5NjlDMTA5LjQwNSAyMC4wMDA3IDEwOS4yOTYgMTkuNzM4NiAxMDkuMTQ2IDE5LjUxMDdDMTA5IDE5LjI4MjkgMTA4LjgxMyAxOS4xMDUxIDEwOC41ODUgMTguOTc3NUMxMDguMzYyIDE4Ljg0NTQgMTA4LjA5NSAxOC43NzkzIDEwNy43ODUgMTguNzc5M0MxMDcuNDc1IDE4Ljc3OTMgMTA3LjIwNiAxOC44NDU0IDEwNi45NzkgMTguOTc3NUMxMDYuNzU1IDE5LjEwNTEgMTA2LjU3MSAxOS4yODI5IDEwNi40MjUgMTkuNTEwN0MxMDYuMjc5IDE5LjczODYgMTA2LjE3MiAyMC4wMDA3IDEwNi4xMDQgMjAuMjk2OUMxMDYuMDM1IDIwLjU4ODUgMTA2LjAwMSAyMC44OTg0IDEwNi4wMDEgMjEuMjI2NlpNMTEzLjk2OSAxNy42MDM1TDExNS4zNzcgMjAuMDU3NkwxMTYuODEyIDE3LjYwMzVIMTE4LjYyNEwxMTYuMzgyIDIxLjIzMzRMMTE4LjcxMyAyNUgxMTYuOTAxTDExNS4zOTcgMjIuNDQzNEwxMTMuODk0IDI1SDExMi4wNzVMMTE0LjM5OSAyMS4yMzM0TDExMi4xNjQgMTcuNjAzNUgxMTMuOTY5Wk0xMjEuNzY2IDE3LjYwMzVWMjVIMTIwLjExMVYxNy42MDM1SDEyMS43NjZaTTEyMC4wMDIgMTUuNjYyMUMxMjAuMDAyIDE1LjQxMTUgMTIwLjA4NCAxNS4yMDQxIDEyMC4yNDggMTUuMDRDMTIwLjQxNyAxNC44NzE0IDEyMC42NDkgMTQuNzg3MSAxMjAuOTQ1IDE0Ljc4NzFDMTIxLjIzNyAxNC43ODcxIDEyMS40NjcgMTQuODcxNCAxMjEuNjM2IDE1LjA0QzEyMS44MDQgMTUuMjA0MSAxMjEuODg5IDE1LjQxMTUgMTIxLjg4OSAxNS42NjIxQzEyMS44ODkgMTUuOTA4MiAxMjEuODA0IDE2LjExMzMgMTIxLjYzNiAxNi4yNzczQzEyMS40NjcgMTYuNDQxNCAxMjEuMjM3IDE2LjUyMzQgMTIwLjk0NSAxNi41MjM0QzEyMC42NDkgMTYuNTIzNCAxMjAuNDE3IDE2LjQ0MTQgMTIwLjI0OCAxNi4yNzczQzEyMC4wODQgMTYuMTEzMyAxMjAuMDAyIDE1LjkwODIgMTIwLjAwMiAxNS42NjIxWk0xMjguMzMyIDIzLjQ2ODhWMTQuNUgxMjkuOTg2VjI1SDEyOC40ODlMMTI4LjMzMiAyMy40Njg4Wk0xMjMuNTIgMjEuMzgzOFYyMS4yNDAyQzEyMy41MiAyMC42Nzk3IDEyMy41ODYgMjAuMTY5MyAxMjMuNzE4IDE5LjcwOUMxMjMuODUgMTkuMjQ0MSAxMjQuMDQxIDE4Ljg0NTQgMTI0LjI5MiAxOC41MTI3QzEyNC41NDMgMTguMTc1NSAxMjQuODQ4IDE3LjkxOCAxMjUuMjA4IDE3Ljc0MDJDMTI1LjU2OCAxNy41NTc5IDEyNS45NzQgMTcuNDY2OCAxMjYuNDI1IDE3LjQ2NjhDMTI2Ljg3MSAxNy40NjY4IDEyNy4yNjMgMTcuNTUzNCAxMjcuNjAxIDE3LjcyNjZDMTI3LjkzOCAxNy44OTk3IDEyOC4yMjUgMTguMTQ4MSAxMjguNDYyIDE4LjQ3MTdDMTI4LjY5OSAxOC43OTA3IDEyOC44ODggMTkuMTczNSAxMjkuMDI5IDE5LjYyMDFDMTI5LjE3MSAyMC4wNjIyIDEyOS4yNzEgMjAuNTU0NCAxMjkuMzMgMjEuMDk2N1YyMS41NTQ3QzEyOS4yNzEgMjIuMDgzMyAxMjkuMTcxIDIyLjU2NjQgMTI5LjAyOSAyMy4wMDM5QzEyOC44ODggMjMuNDQxNCAxMjguNjk5IDIzLjgxOTcgMTI4LjQ2MiAyNC4xMzg3QzEyOC4yMjUgMjQuNDU3NyAxMjcuOTM2IDI0LjcwMzggMTI3LjU5NCAyNC44NzdDMTI3LjI1NyAyNS4wNTAxIDEyNi44NjIgMjUuMTM2NyAxMjYuNDExIDI1LjEzNjdDMTI1Ljk2NSAyNS4xMzY3IDEyNS41NjEgMjUuMDQzMyAxMjUuMjAxIDI0Ljg1NjRDMTI0Ljg0NiAyNC42Njk2IDEyNC41NDMgMjQuNDA3NiAxMjQuMjkyIDI0LjA3MDNDMTI0LjA0MSAyMy43MzMxIDEyMy44NSAyMy4zMzY2IDEyMy43MTggMjIuODgwOUMxMjMuNTg2IDIyLjQyMDYgMTIzLjUyIDIxLjkyMTUgMTIzLjUyIDIxLjM4MzhaTTEyNS4xNjcgMjEuMjQwMlYyMS4zODM4QzEyNS4xNjcgMjEuNzIxIDEyNS4xOTcgMjIuMDM1NSAxMjUuMjU2IDIyLjMyNzFDMTI1LjMyIDIyLjYxODggMTI1LjQxOCAyMi44NzYzIDEyNS41NSAyMy4wOTk2QzEyNS42ODIgMjMuMzE4NCAxMjUuODUzIDIzLjQ5MTUgMTI2LjA2MiAyMy42MTkxQzEyNi4yNzcgMjMuNzQyMiAxMjYuNTMyIDIzLjgwMzcgMTI2LjgyOCAyMy44MDM3QzEyNy4yMDIgMjMuODAzNyAxMjcuNTA5IDIzLjcyMTcgMTI3Ljc1MSAyMy41NTc2QzEyNy45OTMgMjMuMzkzNiAxMjguMTgyIDIzLjE3MjUgMTI4LjMxOCAyMi44OTQ1QzEyOC40NiAyMi42MTIgMTI4LjU1NSAyMi4yOTc1IDEyOC42MDUgMjEuOTUxMlYyMC43MTM5QzEyOC41NzggMjAuNDQ1IDEyOC41MjEgMjAuMTk0MyAxMjguNDM1IDE5Ljk2MTlDMTI4LjM1MyAxOS43Mjk1IDEyOC4yNDEgMTkuNTI2NyAxMjguMSAxOS4zNTM1QzEyNy45NTggMTkuMTc1OCAxMjcuNzgzIDE5LjAzOTEgMTI3LjU3MyAxOC45NDM0QzEyNy4zNjggMTguODQzMSAxMjcuMTI0IDE4Ljc5MyAxMjYuODQyIDE4Ljc5M0MxMjYuNTQxIDE4Ljc5MyAxMjYuMjg2IDE4Ljg1NjggMTI2LjA3NiAxOC45ODQ0QzEyNS44NjcgMTkuMTEyIDEyNS42OTMgMTkuMjg3NCAxMjUuNTU3IDE5LjUxMDdDMTI1LjQyNCAxOS43MzQgMTI1LjMyNiAxOS45OTM4IDEyNS4yNjMgMjAuMjlDMTI1LjE5OSAyMC41ODYzIDEyNS4xNjcgMjAuOTAzIDEyNS4xNjcgMjEuMjQwMlpNMTM1LjIyIDI1LjEzNjdDMTM0LjY3MyAyNS4xMzY3IDEzNC4xNzggMjUuMDQ3OSAxMzMuNzM2IDI0Ljg3MDFDMTMzLjI5OSAyNC42ODc4IDEzMi45MjUgMjQuNDM0OSAxMzIuNjE1IDI0LjExMTNDMTMyLjMxIDIzLjc4NzggMTMyLjA3NSAyMy40MDcyIDEzMS45MTEgMjIuOTY5N0MxMzEuNzQ3IDIyLjUzMjIgMTMxLjY2NSAyMi4wNjA1IDEzMS42NjUgMjEuNTU0N1YyMS4yODEyQzEzMS42NjUgMjAuNzAyNSAxMzEuNzQ5IDIwLjE3ODQgMTMxLjkxOCAxOS43MDlDMTMyLjA4NyAxOS4yMzk2IDEzMi4zMjEgMTguODM4NSAxMzIuNjIyIDE4LjUwNTlDMTMyLjkyMyAxOC4xNjg2IDEzMy4yNzggMTcuOTExMSAxMzMuNjg4IDE3LjczMzRDMTM0LjA5OSAxNy41NTU3IDEzNC41NDMgMTcuNDY2OCAxMzUuMDIxIDE3LjQ2NjhDMTM1LjU1IDE3LjQ2NjggMTM2LjAxMyAxNy41NTU3IDEzNi40MDkgMTcuNzMzNEMxMzYuODA2IDE3LjkxMTEgMTM3LjEzNCAxOC4xNjE4IDEzNy4zOTQgMTguNDg1NEMxMzcuNjU4IDE4LjgwNDQgMTM3Ljg1NCAxOS4xODQ5IDEzNy45ODEgMTkuNjI3QzEzOC4xMTQgMjAuMDY5IDEzOC4xOCAyMC41NTY2IDEzOC4xOCAyMS4wODk4VjIxLjc5MzlIMTMyLjQ2NVYyMC42MTEzSDEzNi41NTNWMjAuNDgxNEMxMzYuNTQ0IDIwLjE4NTIgMTM2LjQ4NCAxOS45MDcyIDEzNi4zNzUgMTkuNjQ3NUMxMzYuMjcgMTkuMzg3NyAxMzYuMTA4IDE5LjE3ODEgMTM1Ljg5IDE5LjAxODZDMTM1LjY3MSAxOC44NTkgMTM1LjM3OSAxOC43NzkzIDEzNS4wMTUgMTguNzc5M0MxMzQuNzQxIDE4Ljc3OTMgMTM0LjQ5NyAxOC44Mzg1IDEzNC4yODMgMTguOTU3QzEzNC4wNzQgMTkuMDcxIDEzMy44OTggMTkuMjM3MyAxMzMuNzU3IDE5LjQ1NjFDMTMzLjYxNiAxOS42NzQ4IDEzMy41MDYgMTkuOTM5MSAxMzMuNDI5IDIwLjI0OUMxMzMuMzU2IDIwLjU1NDQgMTMzLjMxOSAyMC44OTg0IDEzMy4zMTkgMjEuMjgxMlYyMS41NTQ3QzEzMy4zMTkgMjEuODc4MyAxMzMuMzYzIDIyLjE3OSAxMzMuNDQ5IDIyLjQ1N0MxMzMuNTQgMjIuNzMwNSAxMzMuNjczIDIyLjk2OTcgMTMzLjg0NiAyMy4xNzQ4QzEzNC4wMTkgMjMuMzc5OSAxMzQuMjI5IDIzLjU0MTcgMTM0LjQ3NSAyMy42NjAyQzEzNC43MjEgMjMuNzc0MSAxMzUuMDAxIDIzLjgzMTEgMTM1LjMxNSAyMy44MzExQzEzNS43MTIgMjMuODMxMSAxMzYuMDY1IDIzLjc1MTMgMTM2LjM3NSAyMy41OTE4QzEzNi42ODUgMjMuNDMyMyAxMzYuOTU0IDIzLjIwNjcgMTM3LjE4MiAyMi45MTVMMTM4LjA1IDIzLjc1NTlDMTM3Ljg5IDIzLjk4ODMgMTM3LjY4MyAyNC4yMTE2IDEzNy40MjggMjQuNDI1OEMxMzcuMTczIDI0LjYzNTQgMTM2Ljg2IDI0LjgwNjMgMTM2LjQ5MSAyNC45Mzg1QzEzNi4xMjcgMjUuMDcwNiAxMzUuNzAzIDI1LjEzNjcgMTM1LjIyIDI1LjEzNjdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik03MS4yMTY4IDU4LjMxODRWODRINjYuOTgwNVY2My4zNDU3TDYwLjcwNTEgNjUuNDcyN1Y2MS45NzQ2TDcwLjcwNyA1OC4zMTg0SDcxLjIxNjhaTTk0LjYxNTQgNjkuMDU4NlY3My4yNDIyQzk0LjYxNTQgNzUuMjQ2MSA5NC40MTYyIDc2Ljk1NyA5NC4wMTc3IDc4LjM3NUM5My42MzEgNzkuNzgxMiA5My4wNjg1IDgwLjkyMzggOTIuMzMwMiA4MS44MDI3QzkxLjU5MiA4Mi42ODE2IDkwLjcwNzIgODMuMzI2MiA4OS42NzU5IDgzLjczNjNDODguNjU2NCA4NC4xNDY1IDg3LjUxMzggODQuMzUxNiA4Ni4yNDgyIDg0LjM1MTZDODUuMjQwNCA4NC4zNTE2IDg0LjMwMjkgODQuMjIyNyA4My40MzU3IDgzLjk2NDhDODIuNTgwMiA4My43MDcgODEuODA2OCA4My4zMDI3IDgxLjExNTQgODIuNzUyQzgwLjQyNCA4Mi4yMDEyIDc5LjgzMjIgODEuNDkyMiA3OS4zNCA4MC42MjVDNzguODU5NSA3OS43NDYxIDc4LjQ4NDUgNzguNjk3MyA3OC4yMTUgNzcuNDc4NUM3Ny45NTcyIDc2LjI1OTggNzcuODI4MyA3NC44NDc3IDc3LjgyODMgNzMuMjQyMlY2OS4wNTg2Qzc3LjgyODMgNjcuMDQzIDc4LjAyNzUgNjUuMzQzOCA3OC40MjU5IDYzLjk2MDlDNzguODI0NCA2Mi41NjY0IDc5LjM5MjcgNjEuNDM1NSA4MC4xMzEgNjAuNTY4NEM4MC44NjkzIDU5LjY4OTUgODEuNzQ4MiA1OS4wNTA4IDgyLjc2NzcgNTguNjUyM0M4My43OTkgNTguMjUzOSA4NC45NDc0IDU4LjA1NDcgODYuMjEzIDU4LjA1NDdDODcuMjMyNiA1OC4wNTQ3IDg4LjE3MDEgNTguMTgzNiA4OS4wMjU1IDU4LjQ0MTRDODkuODkyNyA1OC42ODc1IDkwLjY2NjIgNTkuMDgwMSA5MS4zNDU5IDU5LjYxOTFDOTIuMDM3MyA2MC4xNTgyIDkyLjYyMzIgNjAuODYxMyA5My4xMDM3IDYxLjcyODVDOTMuNTk1OSA2Mi41ODQgOTMuOTcwOSA2My42MjExIDk0LjIyODcgNjQuODM5OEM5NC40ODY1IDY2LjA0NjkgOTQuNjE1NCA2Ny40NTMxIDk0LjYxNTQgNjkuMDU4NlpNOTAuMzc5MSA3My44Mzk4VjY4LjQyNThDOTAuMzc5MSA2Ny40MDYyIDkwLjMyMDUgNjYuNTA5OCA5MC4yMDMzIDY1LjczNjNDOTAuMDg2MSA2NC45NTEyIDg5LjkxMDMgNjQuMjg5MSA4OS42NzU5IDYzLjc1Qzg5LjQ1MzMgNjMuMTk5MiA4OS4xNzIgNjIuNzUzOSA4OC44MzIyIDYyLjQxNDFDODguNDkyMyA2Mi4wNjI1IDg4LjEwNTYgNjEuODEwNSA4Ny42NzIgNjEuNjU4MkM4Ny4yMzg0IDYxLjQ5NDEgODYuNzUyMSA2MS40MTIxIDg2LjIxMyA2MS40MTIxQzg1LjU1NjggNjEuNDEyMSA4NC45NzA5IDYxLjU0MSA4NC40NTUyIDYxLjc5ODhDODMuOTM5NiA2Mi4wNDQ5IDgzLjUwNiA2Mi40NDM0IDgzLjE1NDUgNjIuOTk0MUM4Mi44MDI5IDYzLjU0NDkgODIuNTMzNCA2NC4yNzE1IDgyLjM0NTkgNjUuMTczOEM4Mi4xNzAxIDY2LjA2NDUgODIuMDgyMiA2Ny4xNDg0IDgyLjA4MjIgNjguNDI1OFY3My44Mzk4QzgyLjA4MjIgNzQuODcxMSA4Mi4xNDA4IDc1Ljc3OTMgODIuMjU4IDc2LjU2NDVDODIuMzc1MiA3Ny4zNDk2IDgyLjU1MDkgNzguMDIzNCA4Mi43ODUzIDc4LjU4NTlDODMuMDE5NyA3OS4xMzY3IDgzLjMwMDkgNzkuNTkzOCA4My42MjkxIDc5Ljk1N0M4My45Njg5IDgwLjMwODYgODQuMzU1NiA4MC41NjY0IDg0Ljc4OTIgODAuNzMwNUM4NS4yMzQ1IDgwLjg5NDUgODUuNzIwOSA4MC45NzY2IDg2LjI0ODIgODAuOTc2NkM4Ni45MTYyIDgwLjk3NjYgODcuNTA4IDgwLjg0NzcgODguMDIzNiA4MC41ODk4Qzg4LjUzOTIgODAuMzMyIDg4Ljk3MjggNzkuOTIxOSA4OS4zMjQ0IDc5LjM1OTRDODkuNjc1OSA3OC43ODUyIDg5LjkzOTYgNzguMDQxIDkwLjExNTQgNzcuMTI3QzkwLjI5MTIgNzYuMjEyOSA5MC4zNzkxIDc1LjExNzIgOTAuMzc5MSA3My44Mzk4WiIgZmlsbD0iIzNGQTcxQSIvPgo8cGF0aCBkPSJNMTA0LjYxMiA3Ni42MDM1SDEwNS44NzdWODRIMTA0LjczNUwxMDQuNjEyIDgyLjI5MVY3Ni42MDM1Wk0xMDQuODM4IDgwLjU2MTVMMTA1LjMxIDgwLjU0NzlDMTA1LjMxIDgxLjA2NzQgMTA1LjI2MiA4MS41NDU5IDEwNS4xNjYgODEuOTgzNEMxMDUuMDc1IDgyLjQyMDkgMTA0LjkzMSA4Mi44MDE0IDEwNC43MzUgODMuMTI1QzEwNC41MzkgODMuNDQ4NiAxMDQuMjg5IDgzLjY5OTIgMTAzLjk4MyA4My44NzdDMTAzLjY3OCA4NC4wNTQ3IDEwMy4zMTEgODQuMTQzNiAxMDIuODgzIDg0LjE0MzZDMTAyLjUzNiA4NC4xNDM2IDEwMi4yMjcgODQuMDk4IDEwMS45NTMgODQuMDA2OEMxMDEuNjg0IDgzLjkyMDIgMTAxLjQ1MiA4My43ODEyIDEwMS4yNTYgODMuNTg5OEMxMDEuMDY0IDgzLjM5MzkgMTAwLjkwNSA4My4xNDEgMTAwLjc3NyA4Mi44MzExTDEwMC41MjQgODAuODU1NUgxMDEuMzE3QzEwMS4zMTcgODEuMzI0OSAxMDEuMzU2IDgxLjcwNzcgMTAxLjQzNCA4Mi4wMDM5QzEwMS41MTEgODIuMzAwMSAxMDEuNjE4IDgyLjUyOCAxMDEuNzU1IDgyLjY4NzVDMTAxLjg5NiA4Mi44NDcgMTAyLjA1OCA4Mi45NTY0IDEwMi4yNCA4My4wMTU2QzEwMi40MjMgODMuMDc0OSAxMDIuNjE4IDgzLjEwNDUgMTAyLjgyOCA4My4xMDQ1QzEwMy4yMzQgODMuMTA0NSAxMDMuNTY2IDgzLjA0MyAxMDMuODI2IDgyLjkxOTlDMTA0LjA5IDgyLjc5MjMgMTA0LjI5NiA4Mi42MTQ2IDEwNC40NDEgODIuMzg2N0MxMDQuNTg3IDgyLjE1ODkgMTA0LjY5IDgxLjg5IDEwNC43NDkgODEuNTgwMUMxMDQuODA4IDgxLjI3MDIgMTA0LjgzOCA4MC45MzA3IDEwNC44MzggODAuNTYxNVpNMTAwLjA2IDc2LjYwMzVIMTAxLjMxN1Y4Ni44NDM4SDEwMC4wNlY3Ni42MDM1Wk0xMTIuODk5IDc2LjYwMzVIMTE0LjA0OFY4My44NDI4QzExNC4wNDggODQuNDk0NSAxMTMuOTE1IDg1LjA1MDUgMTEzLjY1MSA4NS41MTA3QzExMy4zODcgODUuOTcxIDExMy4wMTggODYuMzE5NyAxMTIuNTQ0IDg2LjU1NjZDMTEyLjA3NCA4Ni43OTgyIDExMS41MzIgODYuOTE4OSAxMTAuOTE3IDg2LjkxODlDMTEwLjY2MiA4Ni45MTg5IDExMC4zNjEgODYuODc3OSAxMTAuMDE0IDg2Ljc5NTlDMTA5LjY3MyA4Ni43MTg0IDEwOS4zMzUgODYuNTg0IDEwOS4wMDMgODYuMzkyNkMxMDguNjc1IDg2LjIwNTcgMTA4LjM5OSA4NS45NTI4IDEwOC4xNzYgODUuNjMzOEwxMDguODM5IDg0Ljg4MThDMTA5LjE0OSA4NS4yNTU1IDEwOS40NzIgODUuNTE1MyAxMDkuODA5IDg1LjY2MTFDMTEwLjE1MSA4NS44MDcgMTEwLjQ4OCA4NS44Nzk5IDExMC44MjEgODUuODc5OUMxMTEuMjIyIDg1Ljg3OTkgMTExLjU2OCA4NS44MDQ3IDExMS44NiA4NS42NTQzQzExMi4xNTIgODUuNTAzOSAxMTIuMzc3IDg1LjI4MDYgMTEyLjUzNyA4NC45ODQ0QzExMi43MDEgODQuNjkyNyAxMTIuNzgzIDg0LjMzMjcgMTEyLjc4MyA4My45MDQzVjc4LjIzMDVMMTEyLjg5OSA3Ni42MDM1Wk0xMDcuODA2IDgwLjM4MzhWODAuMjQwMkMxMDcuODA2IDc5LjY3NTEgMTA3Ljg3MyA3OS4xNjI0IDEwOC4wMDUgNzguNzAyMUMxMDguMTQxIDc4LjIzNzMgMTA4LjMzNSA3Ny44Mzg1IDEwOC41ODYgNzcuNTA1OUMxMDguODQxIDc3LjE3MzIgMTA5LjE0OSA3Ni45MTggMTA5LjUwOSA3Ni43NDAyQzEwOS44NjkgNzYuNTU3OSAxMTAuMjc0IDc2LjQ2NjggMTEwLjcyNSA3Ni40NjY4QzExMS4xOSA3Ni40NjY4IDExMS41OTYgNzYuNTQ4OCAxMTEuOTQyIDc2LjcxMjlDMTEyLjI5MyA3Ni44NzI0IDExMi41ODkgNzcuMTA3MSAxMTIuODMxIDc3LjQxN0MxMTMuMDc3IDc3LjcyMjMgMTEzLjI3MSA3OC4wOTE1IDExMy40MTIgNzguNTI0NEMxMTMuNTUzIDc4Ljk1NzQgMTEzLjY1MSA3OS40NDczIDExMy43MDYgNzkuOTk0MVY4MC42MjNDMTEzLjY1NiA4MS4xNjU0IDExMy41NTggODEuNjUzIDExMy40MTIgODIuMDg1OUMxMTMuMjcxIDgyLjUxODkgMTEzLjA3NyA4Mi44ODggMTEyLjgzMSA4My4xOTM0QzExMi41ODkgODMuNDk4NyAxMTIuMjkzIDgzLjczMzQgMTExLjk0MiA4My44OTc1QzExMS41OTEgODQuMDU3IDExMS4xODEgODQuMTM2NyAxMTAuNzEyIDg0LjEzNjdDMTEwLjI3IDg0LjEzNjcgMTA5Ljg2OSA4NC4wNDMzIDEwOS41MDkgODMuODU2NEMxMDkuMTUzIDgzLjY2OTYgMTA4Ljg0OCA4My40MDc2IDEwOC41OTMgODMuMDcwM0MxMDguMzM3IDgyLjczMzEgMTA4LjE0MSA4Mi4zMzY2IDEwOC4wMDUgODEuODgwOUMxMDcuODczIDgxLjQyMDYgMTA3LjgwNiA4MC45MjE1IDEwNy44MDYgODAuMzgzOFpNMTA5LjA3MSA4MC4yNDAyVjgwLjM4MzhDMTA5LjA3MSA4MC43NTI5IDEwOS4xMDggODEuMDk5MyAxMDkuMTggODEuNDIyOUMxMDkuMjU4IDgxLjc0NjQgMTA5LjM3NCA4Mi4wMzEyIDEwOS41MjkgODIuMjc3M0MxMDkuNjg5IDgyLjUyMzQgMTA5Ljg5MSA4Mi43MTcxIDExMC4xMzcgODIuODU4NEMxMTAuMzg0IDgyLjk5NTEgMTEwLjY3OCA4My4wNjM1IDExMS4wMTkgODMuMDYzNUMxMTEuNDM5IDgzLjA2MzUgMTExLjc4NSA4Mi45NzQ2IDExMi4wNTggODIuNzk2OUMxMTIuMzMyIDgyLjYxOTEgMTEyLjU0OCA4Mi4zODQ0IDExMi43MDggODIuMDkyOEMxMTIuODcyIDgxLjgwMTEgMTEyLjk5OSA4MS40ODQ0IDExMy4wOTEgODEuMTQyNlY3OS40OTUxQzExMy4wNCA3OS4yNDQ1IDExMi45NjMgNzkuMDAyOSAxMTIuODU4IDc4Ljc3MDVDMTEyLjc1OCA3OC41MzM1IDExMi42MjYgNzguMzIzOSAxMTIuNDYyIDc4LjE0MTZDMTEyLjMwMiA3Ny45NTQ4IDExMi4xMDQgNzcuODA2NiAxMTEuODY3IDc3LjY5NzNDMTExLjYzIDc3LjU4NzkgMTExLjM1MiA3Ny41MzMyIDExMS4wMzMgNzcuNTMzMkMxMTAuNjg3IDc3LjUzMzIgMTEwLjM4OCA3Ny42MDYxIDExMC4xMzcgNzcuNzUyQzEwOS44OTEgNzcuODkzMiAxMDkuNjg5IDc4LjA4OTIgMTA5LjUyOSA3OC4zMzk4QzEwOS4zNzQgNzguNTg1OSAxMDkuMjU4IDc4Ljg3MyAxMDkuMTggNzkuMjAxMkMxMDkuMTA4IDc5LjUyNDcgMTA5LjA3MSA3OS44NzExIDEwOS4wNzEgODAuMjQwMlpNMTIwLjU3MSA3NC4wNDY5TDExNi40MjEgODQuODU0NUgxMTUuMzM1TDExOS40OTEgNzQuMDQ2OUgxMjAuNTcxWk0xMjMuNDEgNzguMDczMlY4NEgxMjIuMTM4Vjc2LjYwMzVIMTIzLjM0MUwxMjMuNDEgNzguMDczMlpNMTIzLjE1IDgwLjAyMTVMMTIyLjU2MiA4MC4wMDFDMTIyLjU2NiA3OS40OTUxIDEyMi42MzMgNzkuMDI4IDEyMi43NiA3OC41OTk2QzEyMi44ODggNzguMTY2NyAxMjMuMDc3IDc3Ljc5MDcgMTIzLjMyOCA3Ny40NzE3QzEyMy41NzggNzcuMTUyNyAxMjMuODkgNzYuOTA2NiAxMjQuMjY0IDc2LjczMzRDMTI0LjYzOCA3Ni41NTU3IDEyNS4wNzEgNzYuNDY2OCAxMjUuNTYzIDc2LjQ2NjhDMTI1LjkwOSA3Ni40NjY4IDEyNi4yMjggNzYuNTE2OSAxMjYuNTIgNzYuNjE3MkMxMjYuODEyIDc2LjcxMjkgMTI3LjA2NSA3Ni44NjU2IDEyNy4yNzkgNzcuMDc1MkMxMjcuNDkzIDc3LjI4NDggMTI3LjY1OSA3Ny41NTM3IDEyNy43NzggNzcuODgxOEMxMjcuODk2IDc4LjIxIDEyNy45NTUgNzguNjA2NCAxMjcuOTU1IDc5LjA3MTNWODRIMTI2LjY5MVY3OS4xMzI4QzEyNi42OTEgNzguNzQ1NCAxMjYuNjI1IDc4LjQzNTUgMTI2LjQ5MyA3OC4yMDMxQzEyNi4zNjUgNzcuOTcwNyAxMjYuMTgzIDc3LjgwMjEgMTI1Ljk0NiA3Ny42OTczQzEyNS43MDkgNzcuNTg3OSAxMjUuNDMxIDc3LjUzMzIgMTI1LjExMiA3Ny41MzMyQzEyNC43MzggNzcuNTMzMiAxMjQuNDI2IDc3LjU5OTMgMTI0LjE3NSA3Ny43MzE0QzEyMy45MjUgNzcuODYzNiAxMjMuNzI0IDc4LjA0NTkgMTIzLjU3NCA3OC4yNzgzQzEyMy40MjMgNzguNTEwNyAxMjMuMzE0IDc4Ljc3NzMgMTIzLjI0NiA3OS4wNzgxQzEyMy4xODIgNzkuMzc0MyAxMjMuMTUgNzkuNjg4OCAxMjMuMTUgODAuMDIxNVpNMTI3Ljk0MiA3OS4zMjQyTDEyNy4wOTQgNzkuNTg0QzEyNy4wOTkgNzkuMTc4NCAxMjcuMTY1IDc4Ljc4ODcgMTI3LjI5MiA3OC40MTVDMTI3LjQyNSA3OC4wNDEzIDEyNy42MTQgNzcuNzA4NyAxMjcuODYgNzcuNDE3QzEyOC4xMSA3Ny4xMjUzIDEyOC40MTggNzYuODk1MiAxMjguNzgzIDc2LjcyNjZDMTI5LjE0NyA3Ni41NTM0IDEyOS41NjQgNzYuNDY2OCAxMzAuMDM0IDc2LjQ2NjhDMTMwLjQzIDc2LjQ2NjggMTMwLjc4MSA3Ni41MTkyIDEzMS4wODYgNzYuNjI0QzEzMS4zOTYgNzYuNzI4OCAxMzEuNjU2IDc2Ljg5MDYgMTMxLjg2NiA3Ny4xMDk0QzEzMi4wOCA3Ny4zMjM2IDEzMi4yNDIgNzcuNTk5MyAxMzIuMzUxIDc3LjkzNjVDMTMyLjQ2IDc4LjI3MzggMTMyLjUxNSA3OC42NzQ4IDEzMi41MTUgNzkuMTM5NlY4NEgxMzEuMjQ0Vjc5LjEyNkMxMzEuMjQ0IDc4LjcxMTMgMTMxLjE3NyA3OC4zOSAxMzEuMDQ1IDc4LjE2MjFDMTMwLjkxOCA3Ny45Mjk3IDEzMC43MzUgNzcuNzY3OSAxMzAuNDk4IDc3LjY3NjhDMTMwLjI2NiA3Ny41ODExIDEyOS45ODggNzcuNTMzMiAxMjkuNjY0IDc3LjUzMzJDMTI5LjM4NiA3Ny41MzMyIDEyOS4xNCA3Ny41ODExIDEyOC45MjYgNzcuNjc2OEMxMjguNzEyIDc3Ljc3MjUgMTI4LjUzMiA3Ny45MDQ2IDEyOC4zODYgNzguMDczMkMxMjguMjQgNzguMjM3MyAxMjguMTI5IDc4LjQyNjQgMTI4LjA1MSA3OC42NDA2QzEyNy45NzggNzguODU0OCAxMjcuOTQyIDc5LjA4MjcgMTI3Ljk0MiA3OS4zMjQyWk0xMzUuNDg0IDc2LjMwMjdIMTM2LjA1OEMxMzYuMjgxIDc2LjMwMjcgMTM2LjQ2NiA3Ni4yNzA4IDEzNi42MTIgNzYuMjA3QzEzNi43NTcgNzYuMTQzMiAxMzYuODY3IDc2LjA1NjYgMTM2Ljk0IDc1Ljk0NzNDMTM3LjAxNyA3NS44MzMzIDEzNy4wNTYgNzUuNzAzNSAxMzcuMDU2IDc1LjU1NzZDMTM3LjA1NiA3NS4zNTcxIDEzNi45ODMgNzUuMTg4NSAxMzYuODM3IDc1LjA1MThDMTM2LjY5MSA3NC45MTUgMTM2LjQ2MSA3NC44NDY3IDEzNi4xNDcgNzQuODQ2N0MxMzUuODg3IDc0Ljg0NjcgMTM1LjY3IDc0LjkwMzYgMTM1LjQ5NyA3NS4wMTc2QzEzNS4zMjkgNzUuMTMxNSAxMzUuMjQ0IDc1LjI4NjUgMTM1LjI0NCA3NS40ODI0SDEzNC4xNzFDMTM0LjE3MSA3NS4xNzI1IDEzNC4yNTggNzQuOTA1OSAxMzQuNDMxIDc0LjY4MjZDMTM0LjYwNCA3NC40NTQ4IDEzNC44MzkgNzQuMjc5MyAxMzUuMTM1IDc0LjE1NjJDMTM1LjQzMSA3NC4wMzMyIDEzNS43NjIgNzMuOTcxNyAxMzYuMTI2IDczLjk3MTdDMTM2LjUyMyA3My45NzE3IDEzNi44NzEgNzQuMDMwOSAxMzcuMTcyIDc0LjE0OTRDMTM3LjQ3MyA3NC4yNjMzIDEzNy43MDggNzQuNDM0MiAxMzcuODc2IDc0LjY2MjFDMTM4LjA0NSA3NC44OSAxMzguMTI5IDc1LjE3MDIgMTM4LjEyOSA3NS41MDI5QzEzOC4xMjkgNzUuNzQ0NSAxMzguMDU0IDc1Ljk2NzggMTM3LjkwNCA3Ni4xNzI5QzEzNy43NTggNzYuMzczNCAxMzcuNTQzIDc2LjUzNTIgMTM3LjI2MSA3Ni42NTgyQzEzNi45ODMgNzYuNzc2NyAxMzYuNjQzIDc2LjgzNTkgMTM2LjI0MiA3Ni44MzU5SDEzNS40ODRWNzYuMzAyN1pNMTM1LjQ4NCA3Ny4wODg5Vjc2LjU1NTdIMTM2LjI0MkMxMzYuNjk4IDc2LjU1NTcgMTM3LjA3MiA3Ni42MTA0IDEzNy4zNjMgNzYuNzE5N0MxMzcuNjU1IDc2LjgyOTEgMTM3Ljg3MiA3Ni45ODYzIDEzOC4wMTMgNzcuMTkxNEMxMzguMTU0IDc3LjM5NjUgMTM4LjIyNSA3Ny42NDQ5IDEzOC4yMjUgNzcuOTM2NUMxMzguMjI1IDc4LjI2OTIgMTM4LjEzNCA3OC41NTQgMTM3Ljk1MSA3OC43OTFDMTM3Ljc2OSA3OS4wMjggMTM3LjUxOCA3OS4yMDggMTM3LjE5OSA3OS4zMzExQzEzNi44ODUgNzkuNDU0MSAxMzYuNTI5IDc5LjUxNTYgMTM2LjEzMyA3OS41MTU2QzEzNS43OTEgNzkuNTE1NiAxMzUuNDYzIDc5LjQ2MDkgMTM1LjE0OSA3OS4zNTE2QzEzNC44MzkgNzkuMjM3NiAxMzQuNTg2IDc5LjA2MjIgMTM0LjM5IDc4LjgyNTJDMTM0LjE5NCA3OC41ODgyIDEzNC4wOTYgNzguMjg1MiAxMzQuMDk2IDc3LjkxNkgxMzUuMTY5QzEzNS4xNjkgNzguMTIxMSAxMzUuMjYgNzguMjk0MyAxMzUuNDQzIDc4LjQzNTVDMTM1LjYyNSA3OC41NzIzIDEzNS44NjYgNzguNjQwNiAxMzYuMTY3IDc4LjY0MDZDMTM2LjQ4NiA3OC42NDA2IDEzNi43MjggNzguNTcgMTM2Ljg5MiA3OC40Mjg3QzEzNy4wNiA3OC4yODc0IDEzNy4xNDUgNzguMTA1MSAxMzcuMTQ1IDc3Ljg4MThDMTM3LjE0NSA3Ny42OTA0IDEzNy4wOTkgNzcuNTM3OCAxMzcuMDA4IDc3LjQyMzhDMTM2LjkyMSA3Ny4zMDUzIDEzNi43OTYgNzcuMjIxIDEzNi42MzIgNzcuMTcwOUMxMzYuNDczIDc3LjExNjIgMTM2LjI4MSA3Ny4wODg5IDEzNi4wNTggNzcuMDg4OUgxMzUuNDg0WiIgZmlsbD0iIzNGQTcxQSIvPgo8cGF0aCBkPSJNMTQ3LjQ0NCA3MS43Nzc4TDE1NC41NTUgNzEuNzc3OEwxNTQuNTU1IDgwLjY2NjdMMTUxLjAwNCA4My4zMzMzTDE0Ny40NDQgODAuNjY2N0wxNDcuNDQ0IDcxLjc3NzhaIiBmaWxsPSJ1cmwoI3BhaW50MF9saW5lYXJfMTEzMF8yNjczKSIgZmlsbC1vcGFjaXR5PSIwLjMiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNTAuNjc0IDgzLjg5NkMxNTAuODU0IDg0LjA3NjEgMTUxLjE0NiA4NC4wNzYxIDE1MS4zMjYgODMuODk2TDE1NC41NTUgODAuNjY2NkwxNTMuOTAzIDgwLjAxNDJMMTUyLjQ4NCA4MS40MzNDMTUyLjEwNyA4MS44MTA1IDE1MS40NjEgODEuNTQzMiAxNTEuNDYxIDgxLjAwOTNMMTUxLjQ2MSA3Ny4xMTExTDE1MC41MzkgNzcuMTExMUwxNTAuNTM5IDgxLjAwOTNDMTUwLjUzOSA4MS41NDMyIDE0OS44OTMgODEuODEwNSAxNDkuNTE2IDgxLjQzM0wxNDguMDk3IDgwLjAxNDJMMTQ3LjQ0NCA4MC42NjY2TDE1MC42NzQgODMuODk2WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTYyLjU2OCA3OS43OTNWODAuOTE4SDE1OS40NjlWNzkuNzkzSDE2Mi41NjhaTTE2Ny44MjggNzUuNDM5NVY4NEgxNjYuNDE2Vjc3LjExNTJMMTY0LjMyNCA3Ny44MjQyVjc2LjY1ODJMMTY3LjY1OCA3NS40Mzk1SDE2Ny44MjhaTTE3NS4zMDkgNzUuNDE2SDE3NS40ODRWNzYuNTcwM0gxNzUuMzg1QzE3NC44ODEgNzYuNTcwMyAxNzQuNDQ1IDc2LjY0ODQgMTc0LjA3OCA3Ni44MDQ3QzE3My43MTUgNzYuOTYwOSAxNzMuNDE2IDc3LjE3NTggMTczLjE4MiA3Ny40NDkyQzE3Mi45NDcgNzcuNzIyNyAxNzIuNzcxIDc4LjA0MyAxNzIuNjU0IDc4LjQxMDJDMTcyLjU0MSA3OC43NzM0IDE3Mi40ODQgNzkuMTYwMiAxNzIuNDg0IDc5LjU3MDNWODAuOTEyMUMxNzIuNDg0IDgxLjI1MiAxNzIuNTIxIDgxLjU1MjcgMTcyLjU5NiA4MS44MTQ1QzE3Mi42NyA4Mi4wNzIzIDE3Mi43NzMgODIuMjg5MSAxNzIuOTA2IDgyLjQ2NDhDMTczLjA0MyA4Mi42MzY3IDE3My4xOTkgODIuNzY3NiAxNzMuMzc1IDgyLjg1NzRDMTczLjU1MSA4Mi45NDczIDE3My43NCA4Mi45OTIyIDE3My45NDMgODIuOTkyMkMxNzQuMTU0IDgyLjk5MjIgMTc0LjM0NiA4Mi45NDkyIDE3NC41MTggODIuODYzM0MxNzQuNjg5IDgyLjc3MzQgMTc0LjgzNiA4Mi42NTA0IDE3NC45NTcgODIuNDk0MUMxNzUuMDc4IDgyLjMzNzkgMTc1LjE3IDgyLjE1MjMgMTc1LjIzMiA4MS45Mzc1QzE3NS4yOTUgODEuNzIyNyAxNzUuMzI2IDgxLjQ4ODMgMTc1LjMyNiA4MS4yMzQ0QzE3NS4zMjYgODAuOTkyMiAxNzUuMjk1IDgwLjc2NTYgMTc1LjIzMiA4MC41NTQ3QzE3NS4xNzQgODAuMzM5OCAxNzUuMDg2IDgwLjE1MjMgMTc0Ljk2OSA3OS45OTIyQzE3NC44NTIgNzkuODI4MSAxNzQuNzA1IDc5LjcwMTIgMTc0LjUyOSA3OS42MTEzQzE3NC4zNTcgNzkuNTE3NiAxNzQuMTU4IDc5LjQ3MDcgMTczLjkzMiA3OS40NzA3QzE3My42NSA3OS40NzA3IDE3My4zOTUgNzkuNTM3MSAxNzMuMTY0IDc5LjY2OTlDMTcyLjkzOCA3OS44MDI3IDE3Mi43NTQgNzkuOTc2NiAxNzIuNjEzIDgwLjE5MTRDMTcyLjQ3NyA4MC40MDIzIDE3Mi40MDIgODAuNjI3IDE3Mi4zOTEgODAuODY1MkwxNzEuODUyIDgwLjY4OTVDMTcxLjg4MyA4MC4zMjYyIDE3MS45NjMgODAgMTcyLjA5MiA3OS43MTA5QzE3Mi4yMjUgNzkuNDIxOSAxNzIuMzk4IDc5LjE3NTggMTcyLjYxMyA3OC45NzI3QzE3Mi44MjggNzguNzY5NSAxNzMuMDc2IDc4LjYxNTIgMTczLjM1NyA3OC41MDk4QzE3My42NDMgNzguNDAwNCAxNzMuOTU1IDc4LjM0NTcgMTc0LjI5NSA3OC4zNDU3QzE3NC43MDkgNzguMzQ1NyAxNzUuMDY4IDc4LjQyMzggMTc1LjM3MyA3OC41ODAxQzE3NS42NzggNzguNzM2MyAxNzUuOTMgNzguOTQ5MiAxNzYuMTI5IDc5LjIxODhDMTc2LjMzMiA3OS40ODQ0IDE3Ni40ODIgNzkuNzg5MSAxNzYuNTggODAuMTMyOEMxNzYuNjgyIDgwLjQ3MjcgMTc2LjczMiA4MC44MjgxIDE3Ni43MzIgODEuMTk5MkMxNzYuNzMyIDgxLjYwOTQgMTc2LjY3IDgxLjk5MjIgMTc2LjU0NSA4Mi4zNDc3QzE3Ni40MiA4Mi42OTkyIDE3Ni4yMzYgODMuMDA3OCAxNzUuOTk0IDgzLjI3MzRDMTc1Ljc1NiA4My41MzkxIDE3NS40NjUgODMuNzQ2MSAxNzUuMTIxIDgzLjg5NDVDMTc0Ljc4MSA4NC4wNDMgMTc0LjM5NSA4NC4xMTcyIDE3My45NjEgODQuMTE3MkMxNzMuNTA0IDg0LjExNzIgMTczLjA5NiA4NC4wMjkzIDE3Mi43MzYgODMuODUzNUMxNzIuMzgxIDgzLjY3NzcgMTcyLjA3OCA4My40MzU1IDE3MS44MjggODMuMTI3QzE3MS41ODIgODIuODE4NCAxNzEuMzk1IDgyLjQ2MjkgMTcxLjI2NiA4Mi4wNjA1QzE3MS4xMzcgODEuNjU4MiAxNzEuMDcyIDgxLjIzMjQgMTcxLjA3MiA4MC43ODMyVjgwLjE5NzNDMTcxLjA3MiA3OS41NDg4IDE3MS4xNTQgNzguOTM3NSAxNzEuMzE4IDc4LjM2MzNDMTcxLjQ4MiA3Ny43ODUyIDE3MS43MzQgNzcuMjc1NCAxNzIuMDc0IDc2LjgzNEMxNzIuNDE4IDc2LjM5MjYgMTcyLjg1NyA3Ni4wNDY5IDE3My4zOTMgNzUuNzk2OUMxNzMuOTI4IDc1LjU0MyAxNzQuNTY2IDc1LjQxNiAxNzUuMzA5IDc1LjQxNloiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTE3OC41ODYgNzcuNTYwNVY3Ny4xMDk0QzE3OC41ODYgNzYuNzg1MiAxNzguNjU2IDc2LjQ5MDIgMTc4Ljc5NyA3Ni4yMjQ2QzE3OC45MzggNzUuOTU5IDE3OS4xNDMgNzUuNzQ2MSAxNzkuNDEyIDc1LjU4NTlDMTc5LjY4MiA3NS40MjU4IDE4MC4wMDYgNzUuMzQ1NyAxODAuMzg1IDc1LjM0NTdDMTgwLjc3NSA3NS4zNDU3IDE4MS4xMDQgNzUuNDI1OCAxODEuMzY5IDc1LjU4NTlDMTgxLjYzOSA3NS43NDYxIDE4MS44NDQgNzUuOTU5IDE4MS45ODQgNzYuMjI0NkMxODIuMTI1IDc2LjQ5MDIgMTgyLjE5NSA3Ni43ODUyIDE4Mi4xOTUgNzcuMTA5NFY3Ny41NjA1QzE4Mi4xOTUgNzcuODc3IDE4Mi4xMjUgNzguMTY4IDE4MS45ODQgNzguNDMzNkMxODEuODQ4IDc4LjY5OTIgMTgxLjY0NSA3OC45MTIxIDE4MS4zNzUgNzkuMDcyM0MxODEuMTA5IDc5LjIzMjQgMTgwLjc4MyA3OS4zMTI1IDE4MC4zOTYgNzkuMzEyNUMxODAuMDE0IDc5LjMxMjUgMTc5LjY4NiA3OS4yMzI0IDE3OS40MTIgNzkuMDcyM0MxNzkuMTQzIDc4LjkxMjEgMTc4LjkzOCA3OC42OTkyIDE3OC43OTcgNzguNDMzNkMxNzguNjU2IDc4LjE2OCAxNzguNTg2IDc3Ljg3NyAxNzguNTg2IDc3LjU2MDVaTTE3OS41NjQgNzcuMTA5NFY3Ny41NjA1QzE3OS41NjQgNzcuNzE2OCAxNzkuNTk0IDc3Ljg2NTIgMTc5LjY1MiA3OC4wMDU5QzE3OS43MTUgNzguMTQ2NSAxNzkuODA5IDc4LjI1OTggMTc5LjkzNCA3OC4zNDU3QzE4MC4wNTkgNzguNDMxNiAxODAuMjEzIDc4LjQ3NDYgMTgwLjM5NiA3OC40NzQ2QzE4MC41ODQgNzguNDc0NiAxODAuNzM2IDc4LjQzMTYgMTgwLjg1NCA3OC4zNDU3QzE4MC45NzUgNzguMjU5OCAxODEuMDY0IDc4LjE0NjUgMTgxLjEyMyA3OC4wMDU5QzE4MS4xODIgNzcuODY1MiAxODEuMjExIDc3LjcxNjggMTgxLjIxMSA3Ny41NjA1Vjc3LjEwOTRDMTgxLjIxMSA3Ni45NDkyIDE4MS4xOCA3Ni43OTg4IDE4MS4xMTcgNzYuNjU4MkMxODEuMDU5IDc2LjUxMzcgMTgwLjk2OSA3Ni4zOTg0IDE4MC44NDggNzYuMzEyNUMxODAuNzI3IDc2LjIyNjYgMTgwLjU3MiA3Ni4xODM2IDE4MC4zODUgNzYuMTgzNkMxODAuMjA1IDc2LjE4MzYgMTgwLjA1MyA3Ni4yMjY2IDE3OS45MjggNzYuMzEyNUMxNzkuODA3IDc2LjM5ODQgMTc5LjcxNSA3Ni41MTM3IDE3OS42NTIgNzYuNjU4MkMxNzkuNTk0IDc2Ljc5ODggMTc5LjU2NCA3Ni45NDkyIDE3OS41NjQgNzcuMTA5NFpNMTgyLjY5OSA4Mi4zNjUyVjgxLjkwODJDMTgyLjY5OSA4MS41ODc5IDE4Mi43NyA4MS4yOTQ5IDE4Mi45MSA4MS4wMjkzQzE4My4wNTUgODAuNzYzNyAxODMuMjYyIDgwLjU1MDggMTgzLjUzMSA4MC4zOTA2QzE4My44MDEgODAuMjMwNSAxODQuMTI1IDgwLjE1MDQgMTg0LjUwNCA4MC4xNTA0QzE4NC44OTUgODAuMTUwNCAxODUuMjIzIDgwLjIzMDUgMTg1LjQ4OCA4MC4zOTA2QzE4NS43NTggODAuNTUwOCAxODUuOTYxIDgwLjc2MzcgMTg2LjA5OCA4MS4wMjkzQzE4Ni4yMzggODEuMjk0OSAxODYuMzA5IDgxLjU4NzkgMTg2LjMwOSA4MS45MDgyVjgyLjM2NTJDMTg2LjMwOSA4Mi42ODU1IDE4Ni4yMzggODIuOTc4NSAxODYuMDk4IDgzLjI0NDFDMTg1Ljk2MSA4My41MDk4IDE4NS43NiA4My43MjI3IDE4NS40OTQgODMuODgyOEMxODUuMjI5IDg0LjA0MyAxODQuOTA0IDg0LjEyMyAxODQuNTIxIDg0LjEyM0MxODQuMTM1IDg0LjEyMyAxODMuODA1IDg0LjA0MyAxODMuNTMxIDgzLjg4MjhDMTgzLjI2MiA4My43MjI3IDE4My4wNTUgODMuNTA5OCAxODIuOTEgODMuMjQ0MUMxODIuNzcgODIuOTc4NSAxODIuNjk5IDgyLjY4NTUgMTgyLjY5OSA4Mi4zNjUyWk0xODMuNjg0IDgxLjkwODJWODIuMzY1MkMxODMuNjg0IDgyLjUyMTUgMTgzLjcxNSA4Mi42Njk5IDE4My43NzcgODIuODEwNUMxODMuODQ0IDgyLjk1MTIgMTgzLjkzOSA4My4wNjY0IDE4NC4wNjQgODMuMTU2MkMxODQuMTg5IDgzLjI0MjIgMTg0LjM0IDgzLjI4NTIgMTg0LjUxNiA4My4yODUyQzE4NC43MTUgODMuMjg1MiAxODQuODc1IDgzLjI0MjIgMTg0Ljk5NiA4My4xNTYyQzE4NS4xMTcgODMuMDY2NCAxODUuMjAzIDgyLjk1MzEgMTg1LjI1NCA4Mi44MTY0QzE4NS4zMDkgODIuNjc1OCAxODUuMzM2IDgyLjUyNTQgMTg1LjMzNiA4Mi4zNjUyVjgxLjkwODJDMTg1LjMzNiA4MS43NDggMTg1LjMwNSA4MS41OTc3IDE4NS4yNDIgODEuNDU3QzE4NS4xODQgODEuMzE2NCAxODUuMDkyIDgxLjIwMzEgMTg0Ljk2NyA4MS4xMTcyQzE4NC44NDYgODEuMDMxMiAxODQuNjkxIDgwLjk4ODMgMTg0LjUwNCA4MC45ODgzQzE4NC4zMiA4MC45ODgzIDE4NC4xNjggODEuMDMxMiAxODQuMDQ3IDgxLjExNzJDMTgzLjkyNiA4MS4yMDMxIDE4My44MzQgODEuMzE2NCAxODMuNzcxIDgxLjQ1N0MxODMuNzEzIDgxLjU5NzcgMTgzLjY4NCA4MS43NDggMTgzLjY4NCA4MS45MDgyWk0xODQuODU1IDc2LjY4NzVMMTgwLjY4OSA4My4zNTU1TDE3OS45NjkgODIuOTM5NUwxODQuMTM1IDc2LjI3MTVMMTg0Ljg1NSA3Ni42ODc1WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDBfMTEzMF8yNjczKSI+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTg4LjAwNCAxMjQuOTk5TDEyIDEyNS4wMDFMMTIgMTI0LjAwMUwxODguMDA0IDEyMy45OTlMMTg4LjAwNCAxMjQuOTk5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC4xMiIvPgo8cGF0aCBkPSJNMTIgMTMzLjc3NUMxMy4wMTY2IDEzMy43NzUgMTguNTA5OCAxMzMuNzc1IDIxLjI2MzIgMTMzLjc3NUwzNC42NDMzIDE0M0w0NC45MzU3IDExMi4yNUw2Ni41NDk3IDEzNC44TDkwLjIyMjIgMTEyLjI1TDk3LjQyNjkgMTEzLjI3NUwxMTEuODM2IDE0M0gxMjYuMjQ2TDEzOC41OTYgMTE2LjM1SDE0Ny44NkwxNTcuMTIzIDEwMkwxNjkuNDc0IDEwNi4xTDE4OCAxMTYuMzUiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjg3IiBzdHJva2Utd2lkdGg9IjEuNSIvPgo8L2c+CjxkZWZzPgo8bGluZWFyR3JhZGllbnQgaWQ9InBhaW50MF9saW5lYXJfMTEzMF8yNjczIiB4MT0iMTUwLjY4MyIgeTE9Ijg0LjAyOTgiIHgyPSIxNTAuNjgzIiB5Mj0iNzEuNzc3OCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmY7c3RvcC1vcGFjaXR5OjEiIC8+CjxzdG9wIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6I2ZmZjtzdG9wLW9wYWNpdHk6MCIgLz4KPC9saW5lYXJHcmFkaWVudD4KPGNsaXBQYXRoIGlkPSJjbGlwMF8xMTMwXzI2NzMiPgo8cmVjdCB3aWR0aD0iMTc2IiBoZWlnaHQ9IjYwIiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTIgOTApIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==", + "description": "A gas produced from vehicle emissions and the burning of fossil fuels. Results displayed by combining the latest and aggregated values and optional simplified chart. ", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'nitrogen', label: 'Nitrogen dioxide', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'µg/m³', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'nitrogen', 'µg/m³', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Nitrogen dioxide\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#3FA71A\"},{\"from\":40,\"to\":90,\"color\":\"#80C32C\"},{\"from\":90,\"to\":120,\"color\":\"#FFA600\"},{\"from\":120,\"to\":230,\"color\":\"#F36900\"},{\"from\":230,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nif (value < -10) {\\n\\tvalue = -10;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Nitrogen dioxide\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"public\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "nitrogen", + "nitrogen dioxide" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/nitrogen_dioxide__no2__chart_card_with_background.json b/application/src/main/data/json/system/widget_types/nitrogen_dioxide__no2__chart_card_with_background.json new file mode 100644 index 0000000000..5753c4ad53 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/nitrogen_dioxide__no2__chart_card_with_background.json @@ -0,0 +1,39 @@ +{ + "fqn": "nitrogen_dioxide_no2_chart_card_with_background", + "name": "Nitrogen dioxide (NO2) chart card with background", + "deprecated": false, + "image": "tb-image:Tk8yLXZhbHVlLWNoYXJ0LWNhcmQtd2l0aC1iYWNrZ3JvdW5kLnN2Zw==:Tk8yLXZhbHVlLWNoYXJ0LWNhcmQtd2l0aC1iYWNrZ3JvdW5kLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiIHRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDYgMTU0KSIvPjwvZz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNMjAgMTMuM2E2LjcgNi43IDAgMCAwLTYuNyA2LjcgNi43IDYuNyAwIDAgMCA2LjcgNi43IDYuNyA2LjcgMCAwIDAgNi43LTYuNyA2LjcgNi43IDAgMCAwLTYuNy02LjdaTTE0LjcgMjBsLjEtMS4yTDE4IDIydi43YzAgLjcuNiAxLjMgMS4zIDEuM3YxLjNhNS4zIDUuMyAwIDAgMS00LjYtNS4zWm05LjIgMy42YTEuMyAxLjMgMCAwIDAtMS4yLTFIMjJ2LTJjMC0uMy0uMy0uNi0uNy0uNmgtNHYtMS4zaDEuNGMuMyAwIC42LS4zLjYtLjd2LTEuM2gxLjRjLjcgMCAxLjMtLjYgMS4zLTEuNHYtLjJhNS4zIDUuMyAwIDAgMSAzLjMgNC45YzAgMS40LS41IDIuNy0xLjQgMy42Wk00MSAxNXYxMGgtMS43bC00LjUtNy4xVjI1SDMzVjE1aDEuN2w0LjUgNy4yVjE1SDQxWm0zLjkgMi42VjI1aC0xLjd2LTcuNGgxLjdabS0xLjgtMiAuMy0uNi43LS4yYy4zIDAgLjUgMCAuNy4yYS44LjggMCAwIDEgLjIuNy44LjggMCAwIDEtLjIuNiAxIDEgMCAwIDEtLjcuMiAxIDEgMCAwIDEtLjctLjIuOC44IDAgMCAxLS4zLS42Wm03LjIgMnYxLjJoLTQuMXYtMS4yaDQuMVptLTMtMS44SDQ5VjIzbC4xLjVhLjUuNSAwIDAgMCAuMy4yaC43YTMuMyAzLjMgMCAwIDAgLjMgMFYyNWE0LjIgNC4yIDAgMCAxLS41IDAgMy41IDMuNSAwIDAgMS0uNi4xYy0uNCAwLS43IDAtMS0uMmExLjUgMS41IDAgMCAxLS43LS42bC0uMi0xLjJ2LTcuM1ptNi4yIDMuMnY2aC0xLjZ2LTcuNGgxLjVsLjEgMS40Wm0yLjMtMS40VjE5YTQuMyA0LjMgMCAwIDAtLjcgMGgtLjdhMS40IDEuNCAwIDAgMC0uNi40bC0uMy41LS4xLjdoLS40bC4xLTEuMmEzLjUgMy41IDAgMCAxIC40LTFjLjItLjQuNC0uNi43LS44YTEuOSAxLjkgMCAwIDEgMS4zLS4yaC4zWm0uOCAzLjh2LS4yYzAtLjUgMC0xIC4yLTEuNWwuNy0xLjJhMyAzIDAgMCAxIDEtLjcgMy41IDMuNSAwIDAgMSAxLjUtLjNjLjUgMCAxIDAgMS41LjMuNC4xLjcuNCAxIC43LjMuNC42LjguNyAxLjJsLjMgMS41di4ybC0uMyAxLjVhMy41IDMuNSAwIDAgMS0uNyAxLjIgMy4xIDMuMSAwIDAgMS0xIC44IDMuNSAzLjUgMCAwIDEtMS41LjIgMy42IDMuNiAwIDAgMS0xLjQtLjIgMy4xIDMuMSAwIDAgMS0xLjEtLjggMy42IDMuNiAwIDAgMS0uNy0xLjIgNC42IDQuNiAwIDAgMS0uMi0xLjVabTEuNi0uMnYuMmwuMSAxYTIuNCAyLjQgMCAwIDAgLjMuN2wuNi41Yy4yLjIuNS4yLjguMi4zIDAgLjYgMCAuOC0uMi4yIDAgLjQtLjMuNi0uNWwuMy0uOC4xLTEtLjEtMWEyLjQgMi40IDAgMCAwLS4zLS44IDEuNiAxLjYgMCAwIDAtLjYtLjUgMS41IDEuNSAwIDAgMC0uOC0uMmMtLjMgMC0uNiAwLS44LjJhMS42IDEuNiAwIDAgMC0uNi41bC0uMy44YTQgNCAwIDAgMCAwIDFabTExLjYtMy42aDEuNXY3LjJjMCAuNy0uMSAxLjItLjQgMS43YTIuNyAyLjcgMCAwIDEtMS4yIDEgNCA0IDAgMCAxLTIuNy4zIDMuOSAzLjkgMCAwIDEtMS0uNCAyLjUgMi41IDAgMCAxLS44LS43bC44LTFjLjIuMy41LjYuOS43YTIuNCAyLjQgMCAwIDAgMiAwbC42LS42LjItMXYtNS41bC4xLTEuN1ptLTUgMy44di0uMmMwLS41IDAtMSAuMi0xLjUuMS0uNS4zLS45LjYtMS4yLjItLjMuNi0uNiAxLS44YTIuNyAyLjcgMCAwIDEgMS4yLS4yYy40IDAgLjggMCAxLjIuMi4zLjIuNi40LjguOC4zLjMuNC43LjYgMS4xbC4zIDEuNXYuNWE3LjQgNy40IDAgMCAxLS4zIDEuNCA0IDQgMCAwIDEtLjYgMS4xIDIuNCAyLjQgMCAwIDEtMiAxYy0uNSAwLTEgMC0xLjMtLjJhMi44IDIuOCAwIDAgMS0xLS44IDMuNyAzLjcgMCAwIDEtLjUtMS4yIDUuMiA1LjIgMCAwIDEtLjItMS41Wm0xLjYtLjJ2LjJsLjEgMSAuMy43LjYuNS44LjJjLjMgMCAuNyAwIC45LS4ybC42LS43LjMtMXYtMS4yYTMgMyAwIDAgMC0uMi0uNyAyIDIgMCAwIDAtLjMtLjYgMS40IDEuNCAwIDAgMC0uNi0uNWwtLjctLjFjLS4zIDAtLjYgMC0uOC4yYTEuNiAxLjYgMCAwIDAtLjUuNWwtLjQuOGE0LjIgNC4yIDAgMCAwIDAgMVptMTAuMSA0YTQgNCAwIDAgMS0xLjQtLjMgMy4zIDMuMyAwIDAgMS0xLjItLjggMy40IDMuNCAwIDAgMS0uNy0xLjEgNCA0IDAgMCAxLS4yLTEuNHYtLjNjMC0uNiAwLTEuMS4yLTEuNnMuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS4xLS44bDEuMy0uMmMuNiAwIDEgMCAxLjQuMmEzLjEgMy4xIDAgMCAxIDEuNiAxLjlsLjIgMS41di43aC01Ljd2LTEuMmg0di0uMWEyLjMgMi4zIDAgMCAwLS4xLS45IDEuNCAxLjQgMCAwIDAtLjUtLjZsLS45LS4yYy0uMiAwLS41IDAtLjcuMi0uMiAwLS40LjItLjUuNWEyLjYgMi42IDAgMCAwLS40Ljd2Mi4zbC41LjcuNi41LjguMWEyLjMgMi4zIDAgMCAwIDEuOS0uOWwuOS45LS43LjZjLS4yLjItLjUuNC0uOS41YTMuNyAzLjcgMCAwIDEtMS4zLjJabTYuMi02VjI1aC0xLjd2LTcuNGgxLjZ2MS42Wm0tLjMgMS44aC0uNmE0LjggNC44IDAgMCAxIC4zLTEuNGMuMS0uNS4zLS44LjYtMS4xYTIuNyAyLjcgMCAwIDEgMi0xbDEgLjEuOC41LjUuOC4xIDEuM1YyNWgtMS42di00LjhjMC0uMyAwLS42LS4yLS44YS45LjkgMCAwIDAtLjQtLjVsLS43LS4xYTEuNiAxLjYgMCAwIDAtMS40LjdsLS4zLjdhMyAzIDAgMCAwLS4xLjhabTE1IDIuNXYtOWgxLjZWMjVoLTEuNWwtLjItMS41Wm0tNC45LTIuMXYtLjJjMC0uNSAwLTEgLjItMS41LjEtLjUuMy0uOS42LTEuMi4yLS4zLjUtLjYuOS0uOGwxLjItLjJjLjUgMCAuOSAwIDEuMi4ybC45LjguNSAxLjEuMyAxLjV2LjVBNy40IDcuNCAwIDAgMSA5OCAyM2wtLjUgMS4xYTIuNSAyLjUgMCAwIDEtMSAuOGMtLjMuMi0uNi4yLTEuMS4yLS40IDAtLjggMC0xLjItLjJhMi43IDIuNyAwIDAgMS0xLS44IDMuNyAzLjcgMCAwIDEtLjUtMS4yIDUuNCA1LjQgMCAwIDEtLjItMS41Wm0xLjctLjJ2MS4ybC4zLjdjLjIuMi4zLjQuNi41bC43LjJjLjQgMCAuNyAwIDEtLjJhMS43IDEuNyAwIDAgMCAuNS0uNyAzIDMgMCAwIDAgLjMtMXYtMS4yYTMgMyAwIDAgMC0uMi0uNyAyIDIgMCAwIDAtLjMtLjYgMS40IDEuNCAwIDAgMC0uNS0uNSAxLjYgMS42IDAgMCAwLS44LS4xYy0uMyAwLS41IDAtLjcuMmwtLjYuNWEyLjYgMi42IDAgMCAwLS4yLjggNC41IDQuNSAwIDAgMC0uMSAxWm04LjUtMy42VjI1SDEwMXYtNy40aDEuNlptLTEuNy0yYzAtLjIgMC0uNC4yLS42bC43LS4yYy4zIDAgLjUgMCAuNy4yYS44LjggMCAwIDEgLjIuNy44LjggMCAwIDEtLjIuNiAxIDEgMCAwIDEtLjcuMiAxIDEgMCAwIDEtLjctLjIuOC44IDAgMCAxLS4zLS42Wm0zLjUgNS44di0uMmMwLS41IDAtMSAuMi0xLjUuMS0uNC40LS44LjctMS4yYTMgMyAwIDAgMSAxLS43IDMuNSAzLjUgMCAwIDEgMS41LS4zYy41IDAgMSAwIDEuNC4zLjUuMS44LjQgMS4xLjdsLjcgMS4yYy4yLjUuMiAxIC4yIDEuNXYuMmMwIC41IDAgMS0uMiAxLjVsLS43IDEuMmEzLjEgMy4xIDAgMCAxLTEgLjhsLTEuNS4yYTMuNiAzLjYgMCAwIDEtMS41LS4yIDMuMSAzLjEgMCAwIDEtMS0uOCAzLjUgMy41IDAgMCAxLS43LTEuMiA0LjYgNC42IDAgMCAxLS4yLTEuNVptMS42LS4ydi4ybC4xIDEgLjMuNy42LjVjLjIuMi41LjIuOC4yLjMgMCAuNiAwIC44LS4yLjIgMCAuNC0uMy42LS41bC4zLS44YTQgNCAwIDAgMCAwLTF2LTFhMi40IDIuNCAwIDAgMC0uNC0uOCAxLjYgMS42IDAgMCAwLS41LS41IDEuNSAxLjUgMCAwIDAtLjgtLjJjLS4zIDAtLjYgMC0uOC4yYTEuNiAxLjYgMCAwIDAtLjYuNWwtLjMuOGE0IDQgMCAwIDAtLjEgMVptOC0zLjYgMS40IDIuNSAxLjQtMi41aDEuOGwtMi4yIDMuNiAyLjMgMy44SDExN2wtMS41LTIuNi0xLjUgMi42SDExMmwyLjMtMy44LTIuMi0zLjZoMS44Wm03LjggMFYyNUgxMjB2LTcuNGgxLjdabS0xLjgtMmMwLS4yIDAtLjQuMi0uNmwuNy0uMmMuMyAwIC42IDAgLjcuMmEuOC44IDAgMCAxIC4zLjcuOC44IDAgMCAxLS4zLjYgMSAxIDAgMCAxLS43LjIgMSAxIDAgMCAxLS43LS4yLjguOCAwIDAgMS0uMi0uNlptOC4zIDcuOXYtOWgxLjdWMjVoLTEuNWwtLjItMS41Wm0tNC44LTIuMXYtLjJjMC0uNSAwLTEgLjItMS41cy4zLS45LjYtMS4yYTIuNSAyLjUgMCAwIDEgMi4xLTFjLjUgMCAuOSAwIDEuMi4ybC45LjguNSAxLjEuMyAxLjV2LjVhNy4zIDcuMyAwIDAgMS0uMyAxLjRsLS41IDEuMWEyLjUgMi41IDAgMCAxLTEgLjhjLS4yLjItLjYuMi0xIC4yLS41IDAtMSAwLTEuMy0uMmEyLjcgMi43IDAgMCAxLTEtLjggMy43IDMuNyAwIDAgMS0uNS0xLjIgNS40IDUuNCAwIDAgMS0uMi0xLjVabTEuNy0uMnYxLjJsLjQuN2MwIC4yLjMuNC41LjVsLjcuMmMuNCAwIC43IDAgMS0uMmExLjcgMS43IDAgMCAwIC41LS43Yy4yLS4zLjMtLjYuMy0xdi0xLjJhMyAzIDAgMCAwLS4yLS43IDIgMiAwIDAgMC0uMy0uNiAxLjQgMS40IDAgMCAwLS41LS41bC0uOC0uMWMtLjMgMC0uNSAwLS43LjJhMS42IDEuNiAwIDAgMC0uNS41IDIuNSAyLjUgMCAwIDAtLjMuOCA0LjUgNC41IDAgMCAwLS4xIDFabTEwIDRhNCA0IDAgMCAxLTEuNS0uMyAzLjMgMy4zIDAgMCAxLTEtLjggMy40IDMuNCAwIDAgMS0uOC0xLjEgNCA0IDAgMCAxLS4yLTEuNHYtLjNjMC0uNiAwLTEuMS4yLTEuNnMuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS0uOCAzLjMgMy4zIDAgMCAxIDEuNC0uMmMuNSAwIDEgMCAxLjQuMmwxIC44Yy4zLjMuNS43LjYgMS4xbC4yIDEuNXYuN2gtNS43di0xLjJoNHYtLjFhMi4zIDIuMyAwIDAgMC0uMS0uOSAxLjQgMS40IDAgMCAwLS41LS42bC0uOS0uMmMtLjMgMC0uNSAwLS43LjJhMS40IDEuNCAwIDAgMC0uNS41IDIuNiAyLjYgMCAwIDAtLjQuNyA0LjUgNC41IDAgMCAwIDAgMXYxLjNsLjQuNy43LjUuOC4xYTIuMyAyLjMgMCAwIDAgMS45LS45bC44LjktLjYuNmMtLjIuMi0uNS40LTEgLjVhMy43IDMuNyAwIDAgMS0xLjIuMloiLz48cGF0aCBmaWxsPSIjM0I5MTFDIiBkPSJNNzEuMiA1OC4zVjg0SDY3VjYzLjNsLTYuMyAyLjJWNjJsMTAtMy43aC41Wm0yMy40IDEwLjh2NC4xYzAgMi0uMiAzLjgtLjYgNS4yYTkgOSAwIDAgMS0xLjcgMy40IDYuNiA2LjYgMCAwIDEtMi42IDJjLTEgLjMtMi4yLjYtMy41LjZhOS44IDkuOCAwIDAgMS0yLjgtLjQgNi45IDYuOSAwIDAgMS0yLjMtMS4yIDcuMiA3LjIgMCAwIDEtMS44LTIuMmMtLjQtLjktLjgtMS45LTEtMy4xLS4zLTEuMi0uNS0yLjctLjUtNC4zdi00LjFjMC0yIC4yLTMuOC42LTUuMWE5IDkgMCAwIDEgMS43LTMuNGMuOC0xIDEuNi0xLjUgMi43LTIgMS0uMyAyLjEtLjUgMy40LS41IDEgMCAyIDAgMi44LjNhNi42IDYuNiAwIDAgMSA0LjEgMy4zYy41LjkuOSAyIDEuMSAzLjEuMyAxLjIuNCAyLjcuNCA0LjNabS00LjIgNC43di01LjRjMC0xIDAtMS45LS4yLTIuN2E4IDggMCAwIDAtLjUtMiA0IDQgMCAwIDAtLjktMS4zIDMgMyAwIDAgMC0xLjEtLjcgNC4xIDQuMSAwIDAgMC0xLjUtLjNjLS42IDAtMS4yLjEtMS43LjQtLjYuMi0xIC42LTEuMyAxLjItLjQuNS0uNyAxLjMtLjkgMi4ybC0uMiAzLjJ2NS40YzAgMSAwIDIgLjIgMi44IDAgLjguMyAxLjQuNSAybC44IDEuNGMuNC4zLjguNiAxLjIuN2E0IDQgMCAwIDAgMy4yLS4xYy41LS4zIDEtLjcgMS4zLTEuMi40LS42LjYtMS40LjgtMi4zLjItLjkuMy0yIC4zLTMuM1ptMTQuMiAyLjhoMS4zVjg0aC0xLjJ2LTcuNFptLjIgNGguNWE2LjcgNi43IDAgMCAxLS4xIDEuNCAzLjYgMy42IDAgMCAxLS41IDEuMSAyLjEgMi4xIDAgMCAxLS43LjhjLS4zLjItLjcuMi0xLjEuMmEzIDMgMCAwIDEtMSAwIDEuOCAxLjggMCAwIDEtLjYtLjUgMi40IDIuNCAwIDAgMS0uNS0uOGwtLjMtMmguOGwuMSAxLjIuNC43LjQuMy42LjFjLjQgMCAuOCAwIDEtLjIuMy0uMS41LS4zLjYtLjUuMi0uMi4zLS41LjMtLjhsLjEtMVptLTQuNy00aDEuMnYxMC4yaC0xLjJWNzYuNlptMTIuOCAwaDEuMXY3LjJjMCAuNyAwIDEuMi0uMyAxLjdzLS43LjgtMS4yIDFhMy41IDMuNSAwIDAgMS0yLjUuMyAzLjYgMy42IDAgMCAxLTEtLjQgMi41IDIuNSAwIDAgMS0uOC0uOGwuNi0uN2MuMy40LjcuNiAxIC44bDEgLjIgMS0uMmMuNC0uMi42LS40LjctLjcuMi0uMy4zLS43LjMtMS4xdi01LjdsLjEtMS42Wm0tNSAzLjh2LS4ybC4xLTEuNWMuMS0uNS4zLS45LjYtMS4yYTIuNiAyLjYgMCAwIDEgMi4xLTFjLjUgMCAuOSAwIDEuMi4ybDEgLjcuNSAxLjEuMyAxLjV2LjZhNi40IDYuNCAwIDAgMS0uMyAxLjUgMy40IDMuNCAwIDAgMS0uNiAxYy0uMi40LS41LjYtLjkuOGEzIDMgMCAwIDEtMi40IDAgMi43IDIuNyAwIDAgMS0xLS44IDMuNyAzLjcgMCAwIDEtLjUtMS4yIDUuNCA1LjQgMCAwIDEtLjItMS41Wm0xLjItLjJ2MS4ybC40LjkuNi42IDEgLjJjLjMgMCAuNy0uMSAxLS4zbC42LS43LjQtMXYtMS42YTMuNCAzLjQgMCAwIDAtLjItLjcgMi4yIDIuMiAwIDAgMC0uNC0uNyAxLjcgMS43IDAgMCAwLS42LS40IDIgMiAwIDAgMC0uOS0uMmwtLjkuM2ExLjcgMS43IDAgMCAwLS42LjUgMi44IDIuOCAwIDAgMC0uMy45bC0uMSAxWm0xMS41LTYuMkwxMTYuNCA4NWgtMWw0LTEwLjloMS4yWm0yLjggNHY2aC0xLjN2LTcuNGgxLjJsLjEgMS41Wm0tLjMgMmgtLjVjMC0uNSAwLTEgLjItMS40IDAtLjQuMy0uOC41LTEuMWwxLS44YTMgMyAwIDAgMSAxLjMtLjJsMSAuMWEyIDIgMCAwIDEgMS4yIDEuM2wuMiAxLjJWODRoLTEuM3YtNC45YzAtLjQgMC0uNy0uMi0uOWExLjEgMS4xIDAgMCAwLS42LS41IDIgMiAwIDAgMC0uOC0uMiAyIDIgMCAwIDAtMSAuMmMtLjIuMi0uNC4zLS41LjZsLS40LjhhNC41IDQuNSAwIDAgMCAwIDFabTQuOC0uNy0uOC4zYzAtLjQgMC0uOC4yLTEuMi4xLS40LjMtLjcuNi0xIC4yLS4zLjUtLjUuOS0uN2wxLjItLjIgMSAuMWEyIDIgMCAwIDEgMS40IDEuM2wuMSAxLjJWODRoLTEuM3YtNC45YzAtLjQgMC0uNy0uMi0xYTEgMSAwIDAgMC0uNS0uNCAyLjIgMi4yIDAgMCAwLS44LS4yYy0uMyAwLS42IDAtLjguMmExLjUgMS41IDAgMCAwLS41LjQgMS43IDEuNyAwIDAgMC0uMy41IDIuMSAyLjEgMCAwIDAtLjIuN1ptNy42LTNoMS4xYS43LjcgMCAwIDAgLjUtLjdjMC0uMi0uMS0uNC0uMy0uNS0uMS0uMi0uMy0uMy0uNy0uM2wtLjYuMmEuNS41IDAgMCAwLS4zLjVoLTFjMC0uMyAwLS42LjItLjhsLjctLjUgMS0uMiAxIC4xYy40LjIuNi4zLjguNmwuMi44YzAgLjIgMCAuNS0uMi43bC0uNi41LTEgLjFoLS44di0uNVptMCAuOHYtLjVoLjdsMS4yLjFjLjMuMS41LjMuNi41LjIuMi4yLjQuMi43IDAgLjQgMCAuNy0uMi45YTEuNyAxLjcgMCAwIDEtLjguNSAzIDMgMCAwIDEtMiAwIDEuNyAxLjcgMCAwIDEtLjgtLjVjLS4yLS4yLS4zLS41LS4zLS45aDFjMCAuMi4yLjQuMy41LjIuMi41LjIuOC4yLjMgMCAuNSAwIC43LS4yYS43LjcgMCAwIDAgLjItLjUuNy43IDAgMCAwIDAtLjUuNy43IDAgMCAwLS41LS4yIDEuOCAxLjggMCAwIDAtLjUtLjFoLS42WiIvPjxwYXRoIGZpbGw9InVybCgjYykiIGZpbGwtb3BhY2l0eT0iLjMiIGQ9Ik0xNDcuNCA3MS44aDcuMnY4LjlsLTMuNiAyLjYtMy42LTIuNnYtOVoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNTAuNyA4My45Yy4yLjIuNC4yLjYgMGwzLjMtMy4yLS43LS43LTEuNCAxLjRhLjYuNiAwIDAgMS0xLS40di0zLjloLTF2NGEuNi42IDAgMCAxLTEgLjNsLTEuNC0xLjQtLjcuNyAzLjMgMy4yWiIgY2xpcC1ydWxlPSJldmVub2RkIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZD0iTTE2Mi42IDc5Ljh2MS4xaC0zLjF2LTEuMWgzWm01LjItNC40Vjg0aC0xLjR2LTYuOWwtMiAuN3YtMS4xbDMuMy0xLjNoLjFabTcuNSAwaC4ydjEuMmgtLjFjLS41IDAtMSAwLTEuMy4yYTIuNCAyLjQgMCAwIDAtMSAuNiAyLjcgMi43IDAgMCAwLS40IDFjLS4yLjQtLjIuOC0uMiAxLjJ2MS4zbC4xIDEgLjMuNi41LjRoMS4xbC41LS40Yy4yLS4yLjItLjMuMi0uNmEyLjUgMi41IDAgMCAwIC4xLS43di0uNmExLjcgMS43IDAgMCAwLS4zLS42IDEuMiAxLjIgMCAwIDAtLjUtLjQgMS4yIDEuMiAwIDAgMC0uNi0uMSAxLjUgMS41IDAgMCAwLS43LjJjLS4zLjEtLjQuMy0uNi41bC0uMi43LS41LS4yYzAtLjQgMC0uNy4yLTFsLjUtLjcuOC0uNS45LS4yYy40IDAgLjguMSAxIC4zLjQuMS42LjQuOC42bC41IDFhMy43IDMuNyAwIDAgMSAwIDIuMWwtLjYgMWEyLjUgMi41IDAgMCAxLS45LjYgMi45IDIuOSAwIDAgMS0xLjEuMmMtLjUgMC0uOSAwLTEuMy0uMmEyLjcgMi43IDAgMCAxLS45LS44IDMuMyAzLjMgMCAwIDEtLjUtMSA0LjIgNC4yIDAgMCAxLS4yLTEuM3YtLjZjMC0uNyAwLTEuMy4yLTEuOGE0LjUgNC41IDAgMCAxIC44LTEuNiAzLjUgMy41IDAgMCAxIDEuMy0xYy41LS4zIDEuMi0uNCAyLS40Wm0zLjMgMi4yVjc3YzAtLjMgMC0uNi4yLS45LjEtLjIuMy0uNS42LS42LjMtLjIuNi0uMyAxLS4zcy43LjEgMSAuM2wuNi42LjIgMXYuNGMwIC4zIDAgLjYtLjIuOGExLjYgMS42IDAgMCAxLS42LjdsLTEgLjJhMiAyIDAgMCAxLTEtLjIgMS42IDEuNiAwIDAgMS0uNi0uNyAxLjggMS44IDAgMCAxLS4yLS44Wm0xLS41di45bC4zLjNhLjguOCAwIDAgMCAuNS4yYy4yIDAgLjMgMCAuNS0uMmwuMi0uMy4xLS40di0uOWEuOC44IDAgMCAwLS40LS40LjguOCAwIDAgMC0uNC0uMS44LjggMCAwIDAtLjUuMS44LjggMCAwIDAtLjIuNGwtLjEuNFptMy4xIDUuM3YtLjVjMC0uMyAwLS42LjItLjlhMS43IDEuNyAwIDAgMSAuNi0uNmMuMy0uMi42LS4yIDEtLjJzLjcgMCAxIC4ybC42LjYuMiAxdi40YzAgLjMgMCAuNi0uMi44YTEuNiAxLjYgMCAwIDEtLjYuN2wtMSAuMmEyIDIgMCAwIDEtMS0uMiAxLjcgMS43IDAgMCAxLS42LS43IDEuOSAxLjkgMCAwIDEtLjItLjhabTEtLjV2LjlsLjQuNGEuOC44IDAgMCAwIC40IDBoLjVhLjcuNyAwIDAgMCAuMy0uNHYtMS4zYS44LjggMCAwIDAtLjMtLjQuOC44IDAgMCAwLS41LS4xLjguOCAwIDAgMC0uNS4xLjguOCAwIDAgMC0uMi40bC0uMS40Wm0xLjItNS4yLTQuMiA2LjctLjctLjUgNC4xLTYuNi44LjRaIi8+PGcgY2xpcC1wYXRoPSJ1cmwoI2QpIj48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii4xIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xODggMTI1SDEydi0xaDE3NnYxWiIgY2xpcC1ydWxlPSJldmVub2RkIi8+PHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjkiIHN0cm9rZS13aWR0aD0iMS41IiBkPSJNMTIgMTMzLjhoOS4zbDEzLjMgOS4yTDQ1IDExMi4ybDIxLjYgMjIuNiAyMy43LTIyLjYgNy4yIDEgMTQuNCAyOS44aDE0LjRsMTIuNC0yNi42aDkuM0wxNTcgMTAybDEyLjQgNC4xIDE4LjUgMTAuMyIvPjwvZz48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImMiIHgxPSIxNTAuNyIgeDI9IjE1MC43IiB5MT0iODQiIHkyPSI3MS44IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojZmZmO3N0b3Atb3BhY2l0eToxIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdHlsZT0ic3RvcC1jb2xvcjojZmZmO3N0b3Atb3BhY2l0eTowIi8+PC9saW5lYXJHcmFkaWVudD48Y2xpcFBhdGggaWQ9ImQiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0xMiA5MGgxNzZ2NjBIMTJ6Ii8+PC9jbGlwUGF0aD48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2UiIHRyYW5zZm9ybT0ic2NhbGUoLjAwNTAzIC4wMDYyOSkiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjE5NS4yIiBoZWlnaHQ9IjE1NS4yIiB4PSIyLjQiIHk9IjIuNCIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMS44Ii8+PGZlQ29tcG9zaXRlIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzExMDdfMjYwNCIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTEwN18yNjA0IiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PGltYWdlIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3QkRBQUlCQVFFQkFRSUJBUUVDQWdJQ0FnUURBZ0lDQWdVRUJBTUVCZ1VHQmdZRkJnWUdCd2tJQmdjSkJ3WUdDQXNJQ1FvS0Nnb0tCZ2dMREFzS0RBa0tDZ3IvMndCREFRSUNBZ0lDQWdVREF3VUtCd1lIQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnci93Z0FSQ0FDZkFNY0RBUkVBQWhFQkF4RUIvOFFBSFFBQUFnSURBUUVCQUFBQUFBQUFBQUFBQkFVREJnSUhDQUVKQVAvRUFCb0JBQUlEQVFFQUFBQUFBQUFBQUFBQUFBRUNBQU1FQlFiLzJnQU1Bd0VBQWhBREVBQUFBUHBWM09SRVlwWUtTdVprNEpzSlFqWkdaUS9wSlNTUUdGY2tKL0dBT29yckF5NEZXS096cmNxRW9NY2sxaFl2NEFPQ3V1RlZnOWthcVc2Rm1HWlE0U1NTR0xKMWtobjZCYmFCbmtaV1NLMnJkZ3RqTlpNQjZEcTJ4QzQyQUFFU3Z2QXBHUmh5bGdKN0pKSlBHTUJsTUdLcmJRc3NxRFpKcEg5ZDlqb2V5VTJOMGNoR3hZYzg2c21JaHdNaEF3STdRSUQyR09DS0E4a3VTY3pGNE02cm5RRjVMQm5GZEs5aXFzdE5GbGhxc1ByYWVQeVYxT1VlR2pCUHJmTVFKMGtESXI2MGNRcFhaQW0wUE9wejBDS3dCTXJCRGtBVURNWllsZXhVMjJtbDJDdkpCb3ZSbS9DZWtDT29UcUpaWFdiRVJBVHErQmtSQktzMHB0Wm1ZMkFkbEJhSFZ4aENRWllGTnh6NkxEVTVCaGlyVGthVWdTQVNDRmdNNHAybW1xVzAzSEhzUVc1OVM3TTBVTGRMTElsaDRPSml3U1c1Q2xqZVN3bzlrU09VWnVsaGFuM05va0E5SXdCWHNzQkZPMVVlaU9zbW5SM1I1Mm45bVFGYlNBMWtnczYySFYyWXFZbVgyRms0c0VSNHJNVkxKWFlyTGR6dG1VYk9UT0RJejBGUXlGbUs2cmVRKy94TmJNSXdwU3U2dEZucmV4MVc0b3dzT1pScUdkV0tRcjRnRlJYd2Zabk0yZW1aU1NDRmllQ1JtUkdLQWVaZXh5dFB1Z2pSZ0M5dlJpam5CY0ljRlpwVFkvU3cySDh5NEZER2pwRzNYemQ0TGhSYlZHVUtEZUtlZHFkSFJGK1hpdlB2dkhaNE5NVVZ5amJXVmZkZXpua3hrN0tHeU4wdEVXUHl6cXBqa3NKTUNzck9FNkI1ZlM4YUFPa1JRZ01IRjVPeDlIdEsvRnhGaTZlMCs5NTFOYlVuNS9RMDZXM2YwZWZhcVdjQWd5RjFXNnIxNXMzbHRvWi9UZGtWeEpoSTJMbXZmVlhSRWVSU0M4SVBLdVBkMjliazRPeGRIYjNvUE4reVU3SjBkWmdkRTdNQ2V3UFZaNmpIVldVKzZsYTZ1cTJzVlY4TUF6MXdFWVIyZGRqV3ByeFRkR3dUbWN5NHQvWmwrUGdITDBOSmRQanltZHo0OXVzYjZkNWJNTXNpOWcyUXNhM1dNQTdLeVEwTW40VDhaSUNaUmY4QUpUbjc2R3RuMEgxWXVzTDZocW4wSGg2WFgxbWZpSE50K1dlckRjRnQrMDFaK0sxeUt0R1Q3OTJWY0V3L0x1cTc2emFjL1VGK2I1TDQ5OXlldnRQdGNUVWVYVncxeStyMW5zeGR5Y3JxL0ZEUG9vVmxaQzJiVnVxMWJUYmRpRUtXN0FLd05YWGdlN3RtSDV0Wk5zZGxmYm5vL05wc1cvaDdsOVRxbnM4Vmh2NTNPZmx2V1dWNjNmcXZKRGMvZVo1djFEcnJjajZVK1Q5RHJsYk5iby9oV3lLR1JhbUl3MWlWZ2wrNDByMnVYcEhSbnFPUGVRMCt1blU0L0M5ZHZGMmZWdC9UaXl1eTFEa2VnbnNxWmRmZ1dMQjFMVnlld2gyNGVoNnBxQjF1TlJWRTNWVzFveVdpRkdVNlp6MjZ4cmZTSFR6WFdxMm93ODE2TSs2MHRnSTBEWW5ZVmE3M3dYY3Y3SzZsZEtMWmpzT1BwV3VuUlpiVTUxbUw4Q3pEVlFxZUJYeXVMU25zbHhyZnd5YUcyNTduRjFlYXJqZlZzaFdUS0xYaTBhWDMwUlFERVdLMVFLMldHVjlvL3dETWRiR0NJbDFWSkZrWmtaR2NFSkphR0d5UmhmekdZTmxJTzZwbWFtMnBzYW1Zc1Q2M3dkWnlLWmVvVnMvL3hBQWlFQUFDQVFRQ0F3RUJBUUFBQUFBQUFBQUJBZ01BQkFVU0JoRVFFeFVVSUJiLzJnQUlBUUVBQVFJRHJwbDhEeVBQUUE4YWxldkFvZUJYZGRFTW5nVUZWZkk4N0Y5dXhRb0FMcjFzRFJveDZDZzIyL1lQZmV4Y3liaDFLRmZQVys0YitPeSs0ZmYyZXd2dDRGS3lzamc5OXF4b1A3REo3MWxlWDJDUU1IMjJMZGg5aFhhbGFYeDM2OVBVWXRQWEttMUZmWUpGZnZ1dGd3Y01qS3l2c0cxMEs2OWF6ajFwRGRVV0VpeUI5aTFBaGxkSFZsYmJiejBWQ1RvbHFZNzJVdHNIU1JXL2hhVXEyNGszNy9sZ0thcm9FMG9Xa1lHdXhTMTJHM0Rxd3J2dmJ5ZkY2ckNveUtGYit6ZFNwNzdGRm8yRk5XNGs5bnV3R1Q5bUR2bUxKZHdZV0pWWlNtb1RkSHJvTExVWlZ6V3Voak1QRG8vejhaUzlpUVgwT0pTSmRGajBrak5vSWtyVUs2SXF0dDNzREllSVRBOFRrdUdTTEx4NEdCSVpGaUhxQWR0VlFMMDRTaE1KVWRBVmxyaUtCT0hDVGxjZkxzcmI0QWdQSEdnSVpyZFU5ZGQ5dFF4bC9mV2Q1eFhJU1NPL0RuRjV4VzRtdTdOdVJHOHZvc2xFT2ZLSDRjVGtYNU5qK1NmUXptVSszeHUrZzVjOGtFN1N0ZWw4ZlNDRVJSekRLY2x1WkxkNGM5eWxSV092L3Jkd0ZieDVsaGFVTFBJYUk5amlwS2E1anVwY2JHV1dPRTV6bEY3M2JTR25NVWtrc0xWY09jL1BuamZmYnVyMlhrbHpsSWVSSzhkeFkzc3VTdXNNYjJ3dGNoQjN4bDhobmNrUmo3N0g0eVg5K3k1bjYzMkh2NU12K21lNEN3WFZ2UDhBdFc4bHl1VHkrSXkvK3JiUDIrZXVzdmFaV1hML0FIL3Qvcm5uTm4rTDVueHhpVGlmampEL0FCeGh6aC9pL0YrT01OOGFiRVdlTUdOK2RIaXphQmZ6QzMvL3hBQkVFQUFCQXdJREJBVUlCd01OQUFBQUFBQUJBQUlEQkJFU0lURUZFeUpCRUJReVVaRWdJMEpUWVhHQm9UQXpRRkppY3BJVlErRVdKVFExUkVWUVk0T1RvckhSLzlvQUNBRUJBQU0vQS90aDEveGUzMEI2RDVadXJLeXNzdWdjL3NZNkI1QllicS9RVy9ZejBrNUlKZ0NBRi9zM0ZheTU5QUpMUWRQcGJmUUJBbEFLNFdDUXQ4aTMyVXVrYzRoWnJDTGtvT0YybS91NmJmUVc2TCtSWldGMXRDczMzWG1PRmlNR0pnSFJYVGJRcVdUVis5YTBjSXZwbWk2K0pPQjBXT2plQ0hhZWdNMEdVV2p1MmUyTEhvc242cC9jaUJtRmJ5d3JvZEFLQWFjK1NnZXlvTWU2N2JmcW5rcHQxdmRxMW1NazJIT0xEelZxaDJGWHlLdFJTRERmTFRIaCthY0tFWEI3UjFkZm9DWlpNYWVGWTJIQzNOU0FZM0l0S3NyOUYyK1NVVWQyNzhxRWtjOXBuTzQyOXFIQnk2QTdhRlljRWd5SGJmZm1pNlRFaTdNcU03T2tFcm1BWlp5ZG40b0dnR0RCWXVQWTBXRTJUNHptc1dxdm9yY05rTWRpRmMzc2dWa3MwZWdkRjB3aFdSM1RyZmRPaUlpbjRiZWNIOW8zbkx2UlRUVzF1RGQ4dXg3eXFoanlPb04xOWIvQlZEalk3UEgrNm54N05mUHZYTk5oMkk4VnZoelEvWnJMdnZtY3pIaCtTQk4xdk1naGJpUUhORDBrNTdzWEpEbWdyS3lCVFFMb3F0ZVRHNnNrSXVjc1NyUzBqcjA5bW5odEtjbFdWVy9GVFV1ZUdPYUc0dmNzSTFUQlN2TXIyanpaN1NpNnJVUFlZN2J3Wnh3NEJvcVhsT3o5U3d6Vmtzc2ppTXM1R0JxaXhsMWphL2NvM3VhNEJQOEEyUEp1QysrWDFac2RWdEdCeldOclpodzNzWHJhcGVHamFjK2YrWVZIRTBXMXRxaDFLS2FOMWp2czdIMktUMXAvVW1zMlJqbGZjdWxPcFZOZkRqelcyUk05b3E5SEg5MkZ0ZWFzWkZMVWpDWGNYbXdvajZhZEZzMHVwWjhMOFF6YXRxbis4SnYxcWFyZkwxMnNlNEFDMklyWWRRQzJDdEFmYmhEeGE2RXRXLzNsWU1XZk5PM3B6eTVxb2lNSWJVdlkwV3ZZcVIyTEhmUDBrSThXOHFIY2JTQUFmWW5PSk51ZmNtbW1tWTk1dTVsZ1BpaTZMQ1E0WjhsMWV4Rjh6eld6Ni9aL1ZHUXZjUzBYdmtGSExJTE53aHJMV1FiTGNIUmJiclg0R1R0YjNjQ3IyMFVQV3FyR0hQMEkwTmxuYTRVbEpUNE1XV2VTZG5aM0pjV0x2UmFkNHpVSjF1S1h3Um1HN3gzUXRvalRNdUNNMURneWc1ZXFDZ1pBOHViK0cyQUN5cEd0MDhRMVFidmVobkQzZ05VY1p0LzFaRnh1R2srQ0wzV2lZVC9xTlRJbllIaXh0NndKb2JaenMvWkwvQmJOcVlYN1BueENRM0lLcVdRTnFXamVSM0dKOFRzUUh2N2tiOFlHbmVudmRaclI4RktYWE1hb3VwUlRCckxtUENUOXdwOVM2Rmp6b0NWeGRwWGhzSnVmY3U2WmUxRUVFT1ZNSDRpN0VteTZSa0w4S01kUHZkM2V6dWFscUtSODBSZFpwRjd1NzAyVURpZmU0dW91cnRxMzA5Mms4MVJXL282aXBNQmtnTGNZdTJ5YUFUWjJXcXBLV1hESkJaNUYxczk4NGJidEcySWhiSXFHUzlYcVlYdmlqeHlXOW50VkxVeHNtaWtGM3huSytZeTBWVFIrZHA1bk1JN2xzbXJkL09VZlYzOHA2Y2EvbWFxZ05kTkRMdm9mV1V1WnQ3UnFtc0dDbGphd1g5SWFyYWxSR2FqckxkMk80S1JrVyttbkxyWlpvTEMyWGlBMDFaZE9oODFKc3Ryb2NyVE5aWXFocjZ0OVRITnVnNTJUY0hDUGlGVUQ2cHpIL2xmL0FPcmEyejI3Nm9vM3NqY2JOZGJWTzNWeWVhaU1tN2FibE1taWRDVzVGVlRJOTF3MlBLeWxkZHp6R010TUNsa2gzYjNnZDNDcGNWNDV4My9WMlZiTWJ2cVNSM1lVSGdoNysxMmpaZFpjWkh5T0pQTHVWdWFOTGkzYnlNVGJIM0lVOHU5amtjTGFGU09uZFBKTWJ2MXRrb1hSUEV6TDRyWVNxZUNRVGJNM3JEWWZoeitDWnRGckpIUlhlRytjZTd2VlBzeUdSc2xMdkhPNTN5VVdIK3FJTCswTFpoZVgvd0FuYWYyWnFnYnhuWnpHdE9yWWxVMVROMFhXWjNZVStuWmhiR3cydGs1dDdxWitHTWNER205aHJxampra3A0Y0prNzNISlZMaGhuaGlsSDRtTFpnek95amNqMFp5QUZUNzh2bzNTUnNPalQ2S3BqKzYrYXBqNkR2RlF1T0hqOFVCblkrS0RlL3dBVXpuZnhVZXQzK0tqL0FCK0lUVDZUdmttK2s1NThFemxqOFF2dlNQOEFrby9XUFRSbzkveVRQdnYrU2l2Ykc5RGRuZHlPdnlCQ21rT0tZV2Jia1ZEb01YaW9OTHZ2NzFDek1sL3lWRXdYYzl3Ris1Yk41Ty80S25FbUprNStJVGVWVDhsLy84UUFKaEFBQXdBQ0FnSUNBZ0lEQVFBQUFBQUFBQUVSSVRGQlVSQmhjWUdSb2JIUklNSGg4UC9hQUFnQkFRQUJQekpJZmk2Y0U0TUppOFpnZ3ZJUkQ4MjBJb1lRd2hQOFZDWmJLa09UQlJNWW9vUHpMOS9CY0RuL0FJQm9KZzJoMDhHYW9xN0ZnMUNNQzhSZU56NVQwTFB5TG9VSXZHR1F1ZkJhT2pwV2hOdC80SnJ6TUk2TXhWTHdjWkh5YUtMMFFqbU5DVVBWSXJVR1JzU0ZubGxCcDdQRmVCOHhFRVdvc2hCNmp1RkVJUENKWStnNTBYbkF1eG13NUJLNlJ1UlR6NGFjbnFORGd4NUwrQmxIZWN6TUhoQTNnYVBGb09LMFpJbUlGUjgwSmVETnM0NklZSlJzYXZDa2lWSzhpUWdnaVVhVHc1UldIWTdSeGxLUzhoUHdWRTc0b21ONVAySHZGUXY4akwwUXhHWWt6UFhQZ3BUUGtGUWI4R0dGaDRINytMSDVFVWhVVW95RzFtRVVydWh1QTR4SkpiYkl3cDJ3bFJpZXdaVlB3cUh4T2xNOXNoaGRFb1lxbEdTTXJZZENaeG91cVMwRjNDbS9zendXSEZ2ZjBqbEd6NGd2enV0TnVEa0s3aVhSRmhDeXd3SmpnZmdXZ3NiMEowc25NWXhqa3NFa0kyR2E1enhyWS9aRHZjbld2ZDVPWWFBUlBjL2tUMXJLaW10Mm4yUHdKU3I5NzdHbmNKNXN6UGdxNCtxdlJLSmk5Qm0xWU9BSHBhSnZJa2FHVjRBZ3oyNHZBNFhLMEVmRHdibmErYS9BN3p0aWljSkxYVll5dUdZTWRtWjV2bHhSZEJ6aDAwTFdoSmdkMTloSnRIaGpIYjVDRmdjTUlvaEpqekZMNVhoZlpPUmV4blFsaVVmWmxleHovc0VveHNzc0Z2WThmOEVVTHkzSkJsQS8zaU1nRUY4OUZqUm1rRHVtQ2lxRFFEZ1lXSmkzUXNOa0ZtbGVTZThqeExmb2pXZ1JTRFJWcXNEU1UraWdhWTAzcDdSYkhuTmZWWTR0dGNTVjlteU52b3oxMTBkNVJvZjZYOWEweElMZVIxWmY5Q3V5b1MyVFJXMnhaK3gyaFZZbnI1Nkc2NmU2WVNNWlY5RWFtaVh5ajU1VExVUzVTdjJQeG9tek1vc29tR0ViemlCVXllMzJTQlNzUyt6cGdReUtXU1piOFNyVjdHclVxSnN1bVR1anBtcHIyZTlmN2tmUk5ycFgvbzJPQjNBYlhidERMeDljYXlPbWNXZnRpSm1zbWlsNC9zTnBxWTdvTSs0RVptcDREbTc3UEVHbFB0cVVUZ3hSMXBYMkVob1BJdm8ySUN5b3hHMXA4MGc2L3dBek5WeitobEZPZCswSUx3MUlVY3BrWlZuTmVPall0TmY4QjYxeEdoODUwV2k2UTlpVjIxRzhjWlFxai8wa0Q3REh4UnNWcFhRMFF6TmVpNTZJSlBIcjFURVJRdEx2QktwYURYT0tLNDZGVGJhNTdhMEpXVnc3R2xFMHhvWmU1bW4rQjFsRnVLVTA0bSt4NFhtL2tUT3lzWDhqbDdHZHFYMmNkS3Z2Wi9KbnAybnRUL29tMHpDOEo5OU1YOTM3VjFzU2xsZUc1d0ozRFZnWjhzWXVPUGVtbkRMTDVlMXlHUTBwZmI3OUdiVFpvS1c5UDRnaHhXMitncUgraGhZR1N1MjdiZkpJbHpxL29WMkZvYWNlQmU2QThCdGl0dVZtaUpXU3ErRCtrZFZIeE5OL1VNcXhOakNPQ2p2SER0SFlsNk50L1pLTFltL1RvcFdPQ1ZYY2o2VUczbjhDNnRLdEVoOUh4S2dpK1B3UGYveGJtTUE4a3BHRUtlNU5kUFF2YWpUMjJFVnJURkt2eVZjMGVITGowaGlONE9qMHVmOEEzSnRNdzNJK3d3V1ViVGIvQUEvazRndGZONmFQYk84aS9BcmRlemZ3aXl4OWI5VGtlMGE0aFVzLzZGNUlhdE00K3pUMmVTMmxKOURqWDFqV2ZrZWI5NGIrVmtWK2dEZEh4a3FHbWZmalU4aUtVdjhBejJaaUxJWExqRTZUUzVVZC9yaVJWTitGR3IvUURPbnFGZldGTS9hWUR0YnFHazBnbVkvZ0Uxak9DRHNZenY0SDlsazV3akwvQUNYOGZzcXNhVlBuSW5jbHB4Z3lGTHBocDlPSjJrTjA4dHhoL2dRN2VidCtqLy9FQUNZUUFRQUNBZ0lCQXdRREFRQUFBQUFBQUFFQUVTRXhRVkZoRUhHQmthR3h3ZEhoOFBILzJnQUlBUUVBQVQ4aGpjUk1wNmdXd282aHNoMVFyeDZCd0dwYzFVQ3NFNUxsKzVUdjAxY3hxbmhITWVtSGZvdk1FQllKcVpvY0pPY3pteGZiMTRNSVl6Y01JWGNyM0I0aGl4NDRDS0VLa0tTaFNMMm5EZ0xGYmJFMmpDVWR4Q21ZSjVKYno2SUVmT1Zkenp3TzVkY28yUm5sZ1JHYWpVS0pTb0JseXB1THU0bVVJQ1J0emFJWEtGWGM4a3E1bU1iY3gzZWExeE12RXhJWmpEYkhJWGFXVm1YY3d2UXpDbWRkdXFtTyswMm5vSWFKQ0p2TDFVWVVPR3pqc2VvQzh6aVdHWWdMdU1EQnppVUx1SHltTm1MdE5nd3B6UExDRzN0ZWdLK0dJbG9ad0pVbHh6TEpVMTdQTXBFTTFZRGxoTzJLT1VSSE1jSmwzUkhjbFdMaUl1VkNETUFKZGI5QWNLaFhxWjBCYW1Sa2huRVExWXhreUpjMXpEVFBTWEdtVWMrazQ1RzV1SW9lakEzS2VmUWNJbmVsNm9ObTRkMElwSytoTGVKWk5RakxkUmJCYzh3Q05OYy9NcHN1WDRpTGNidUtNeFlZemR6NkRHWjVZRllnM0RIYWdNc2xrdjJsamlvRUdTMnZIcFJTdkZNTUplN2U1bWJZZWtMUmNSOFY4eDg0UGZvVlNKQXVvYnVJMnVMek1XbGdjTTVrSzd1ZStVdTMwV1l5cmlBQWJrbGdDY2d5S2duaGZvVDdUTlBFdnE2Z3hGblBveE9SblpuYWdhcEFYY0NLTlF4c1NaUVpqd3hZSFlWR0UyTEhieE9McHRYS3VCUm5LTjJYTXZQdDJ5d1lvd01SMm1VUlplOHhlTmhhV3J0bXRhbmhuRXcwckJaUlRZb1ZXOFM2Q3liblpFNVF2c2xoNXk0emJoUnpYNllZcEhlUlN4RHVoUmg0WFVZMnEraDN4N3pFSEFwTjgvOEFGQ2xzWEtRZFFXVCtoKzVUaWpSRFVPSVFvRU5uSEg3WXhhVldvTXFkZmNyOEdwTGI2ZzR4V0hxU290bU1NR1g3WVhoVTNTT296K2Zac1BvNE9KcEVWRzdvMjFiY2MrMDNtQUZGdU96ekd4Y3ZMeXQvdHV1b3Mxblp5RnRzQ3BFZkpKblhqOFdRT3JFSmNPWVB6RlpycSsyVStjNWhGUXJBUmV3NmhmTjVsTk9vWDFZeXJpSmtJMG5WNk1zZDNCeXBBbUY4V0MxdFBEUENyUDhBbFBpRzY3SXZzQzV4Zmdmek5vdkhDYWlxcVFVOG1aVVkzTjFML054Yjh0TWMvaEc2TTRHc2hMdUVSVmVKaGdnbTZtSUg2VThzcnRWakRiVksrc2s3RE5zTUdERXQ1TFBtVmZLV3dES1FGbUlnaW82NU5BK0p5bEljVnJrQy9tSG5sTEtTbEVHZEIzRjFLSnpYdHczK2FNQ2tXNWYwdWNiTGpnOW15OThRWjF5c0VsU0tTa3pDOG5Ha01sbXNRU1c5MmdyRU04QXc1R3U0VkdoTXRLeTNNbDFQMk9ueVE3NkYvUEtoTWl0UlNMOW91ZUxWMlVZQVoxQXh0REp2bk5ZbXRzSEpmNmxvbjNuN0NadnFuOHN0cDlJNzB6ai9BR2dSVzROODlUVnIzNWdWb0hOZmVVdGxWTFdNYTVsT1pyY1ZrZ3pNdjBaY3Rtcmx0ZWxxcXVQd0V3RzFtemZxTXk4eEh4VGY0NHFFQ3dvYlhQZFRQS0k1ZEh0T2diTmcyRzlrM2VMdTNaZjNoV2NrWDNESlp1Z0tjMVF6Z2F0U2JIMnFPc29Pc0RnY0VlZVlDMjYzOHd6bXEyKzdGKzdMNkxqQVlhRWIrc1dQcXZhZmNVbTcweXBkZkVWakdTZlM4SGpVTzJ1U3B2V2NWa0tGc2pENDVsOXdDZEI3MUFLbjNhZlB4RFk1WlY3OEU5LzlVM2xiNEdLK2lDR05yU3V1ejYveEVhcjEyNzJyRFhQNmk0MXhNaFYwR0JNbGZXVm5CMElkNGZaRXIrQzdDOG5YVXQrS0FmWGpjUkVNQ0hMNGxrTENJWjRaaHJJbHBjUDBsS3p5aXpYY0lWeTdyeE1McVdzSVpxWkJwd0ZCTDdSeTE1ekVCYmkyeFd5M2RIMWMrM2MzQ1ZqdEI0WWh5QXRuSG1wZVVzcGpaTkxxTVBleFpyVGVVYnNEYTFEdFM5STNmK0ZLWDhJZmVSN0FETkNvYll2Q3Z5SXRsU3h2dFM0LzF6VEFsUUwzT0RuNW12QWxHYndWRllLWFV2bi9BTjhPUTVJMkVuTjFLWDRZWDEzVXpRakhuTCtjRE5jaUNyZlFiWjdwaHdNMHprSDdNU3A0TGMwNkFObjNnUmtrUnRlTWZNNUxhbHJiUnVVUGtxRDFMdjhBRHNwaGpyWE02NGUxQmpZUDNncG5iSHNCZmV3TXBMSDl6RjB0SjAxR01yYldhTlM1U2w0UWNlc1BGdjFFS3RxWnVvOXJibmk4TjRhd1ZyZlV2a1NocmlwV3Y2U3NMbEtMeFlSdkh4bUZVak50YVlIQmhqbXBZRnpnRDhkYjUrMHhMd2NGS0I2RHhLb0dSZjRrYm1XR0dBVEk4ZFJjUEtyVFBTcStJcTVSRG9jS291dEdva2ViRERXVytOMXltSHB5cWk3YzF4d042Uk42b05WV3kzS052eVlPT3RRbDN1WFV0d0lkbmgxQUNEeHBubG9IN1N5QlRYZURlNjVjeHpvWFBDbWZjSHhjZlVzRlZ0bnh5aHVtYnVIMkVyRWJQaFd1aXIzRGJrUUZsQmluZlI5MGJlTUx3Tzc1SGl5WEZ0V1dHK3NtcnEyRGlyaWxBaWthckg5NWhIUjVVM3piSDZVTWlXeXBiOVJKYkhMSS9FNHYwVUh0eTE4c3lkODlvSW9mVkVYalpETTF1UUNLaEMyemk5NGtMSmIvQU9vVTlEblorc1NxUExReUUwWDJCT2k0dU90VFNYMWNSVUptMlQ3UzgvM0ZZbGxSTzFFWWFqei9BQmd1QkF0RzcyOThUd1gwVDQ2VExOdmRXSC8veEFBakVBRUJBUUVCQVFBQ0F3RUFBd0VBQUFBQkVRQWhNVUZSWVJDQmtYR2hzZkRSLzlvQUNBRUJBQUUvRUJtcm1oK0ROWFFmRzZ0T0dRWUZCeDM5MGtPdDR5NTMzZWp6UTFtL3hDWmFLcnZQWU92dVM4cGZjZ0duY3p1ODdWVzV4Zm1LUVpSZVlYUmN6dzh0STZNRERPVHpkYmZ2bGdEbmpPQVdiNkdsMHl0WXVMNXFGY2JNN3kvMmFDSFA0SzZvSGZtUmpnYmtmNDVXKy93ckdvTXdUamdvYTlOUHpXSVU2UFhjSE9yUzU4eWpENXl1TW83bHJJNnZadlV2K1hLRFdmd2hOUksvaHBjam44RXhCbG02c1UxYzlPNTZEN04yTSsvVDh6eklMNDV3RlQ1cUdJL2U0VXA1dk53bnpFdDBqN3U2WjFIRzYwU0dzaisxMUJIZWhPT1RBeE14VnBZMkp6dXdndDNUNGVMNU81SjBYeGlhYUgzQkxiY3FXZ1h1NkIvdHdlVDRYSW9pT3J5bndkV0l6M09QVTlNTmpuZGpkS080dERuV3dNMjRTRDE1anhtTitzd2lubXZCeWZuTUhwalBmVzRRMVh6UHZOOHpCMy82emdDRnoycTFYcitPc1AxUDFuLzRxNS9jSjhhVUplNEEvd0RMS1haeVBYWGo4NmlHdnNmdDFUZ3pqdkdWUnhycS9jTXEvd0JZT0wrTThnV1lWUnd0Ui9XRnlNWXhZcTBSNWZ2Y2UxclFLVis0Qi9CTXhTRlJaWE9pd1BETEt3OGNxYnQ0RU9GTWNiOWR5YVpLeDNOZjhPWGtla0JQdzZpYkR2eDlNT2VBais4STUvVndHNU55SlBES1JqdEl3TUpuVXN1ZUhXVnFYVTBZOW9yVHg3c3hvZWNWcTF5SzVzRzAxSmhtVUgrR3B2OEFEOUY4TkI3aTF3TFF6UWNFTjlMSDU1aE9Eb1BNTGxXOUVEQ3JIU3YrTlZMbjB6SXlabVU0U1B6VnlsSjc4M09YdWQvRzlseEdQY3hLWEFtWVFvRFdYTDR1amhYL0FKalN3Zld6K0xJTm5lUzJIV1pWUlo2aWFSWmdxUzZ0RnpWTmg0Zlh1U2tkNEtIblk1ditBMFpqb1lmcXJ3TU9VbEMxSHBVbWVYNnlDcG5DVHpJRzYyQk5WZGVzRmZGeG4vWTFYT3ZNaHVMMkdDZy9zd2NLL2RYbWVMRk9lWkZ2YnZkL3FPZVEwQmF3VS90Smt6OHpJMWd6eFdwaWxZLzk1ak1pSXFUVWc0TXcrMVZmdGNTTUh6R2huUmJlZmlyNE9oeUZ0OWlLVkFoeCs5MUQ0NW5WaSs1SHBBYTBRQjF6Q01PNlFYcDNBZ2U4K1NVNmlvNVlYWmdjcUlYOVorcmllWWloY0tQanZDMzlZTHlCcXJqOCtad3FKUjdBdi90NTdsa29kL1RQSC9nNXdubVB4OTFQQ1Job0hRNWdqOU1OREFINU9ZSUlzVnkybG1SKzZuKzVxSjhSRndTYmVIbWJTK0J5WjVoQ2VycFcxVndFL2dvWEo0TFoyRGdDbmJiL0FDektWUTBVOTBzRGVOd0FpRDlNWkxRMTUzU2MzRk9IS3pDNWtwODl1SVBkK09PVFRGOVc1RFNpclFQYzlQOEE1ZWF6QlZac3dIL211bm1jSmRFWWR1T0NpRDg4eGhUNmZFSHpON0prTFM4TUJoQlFZdUxFUkVBdzFxdUllNTd1eFRpSlJRQU9lR0ZFL09vZDBaU1hnL2gxdWVmSlpycUxmSURLYVNDWjFMcGo1YkZ2MTJETU9IMmNHR1d2VFhNRHRMa2hBUFZYcnVsQ2VzM3k3bERrQjZhRzFja2ZudHhzTm1QQklIazdjcDR2QmpvbjNVQ2s2VUtmbTIya09EL2VMZjhBTjkwNmRVMi9tczIwMUxVWDR4dDBSOXgvZWwrWXlaQWVJcWVlWEw1NXhWdDQ0UDM5M3NsZEhlKzZlUWMzbnNCai9jWTFET2hFaEhIK3Y3eUFQQlQvQUl2Zm03WU1IK3FZSU1QeWFqNEcvdGpMRVgzN2dBTVg3cndDZzNHNWhIMzZrenV6SlhGd0FISDhHSnErZUNRNUlBbUpkZUFpM1htc2JUbFBTeGFoM3FmRENCeEpOYXdLZlFLbERYVFdORS9JVjFuaXBtWlhxc3hqMk9Yd3J4OEFXQUJuSFVIb0ZVSHlHVXM1UHRDVStPTDgxazd5Y2ZzcjdNSzg5NkM2d2VyL0FIZklXOC9BelZsRS9BRVJkcTljNnZzeG80akI0NzJLdlBBeEVLcCtFSTFEcGhwcGFCMFhzdzRwT2NGVy9pR0orRGkrU0VLbDZZWWFES09uMmJqc2NWb3E3eE1kVDFlbVdnckFEOGNIWmp4djVvdGVIQWU1bG9TWUh0RDY2ZjhBYzgwQXM0SFgzL3VQakNtOWRGUndDQzltYll3MnBFRklWZlQ5WmxuNG5OZEtyV3A1dnhVd3B1MTdVb2dLUEhYNW5xUHl5OEQ5eU9KVDFUZ2dwT05FcGxxeEdBR0F0Y2I1aVhOQUhsUUdKVXVZK0RDaGEwMjhRam1PVUFzTDcrOUdVVXVGQTkvVkRTSEZ6VFluUk1yeDR6a3VIZTNMM3VWSWhQdWZNc0RhMEZuQUhQQjFyNkhBTjg2SDM4WlRxK202dXZmdlhHTEhEK0JWZHl1eTcxNzRuNCs1WjFsVWZoK0xyTXQzK0JhMTFPb3ZWL0lIeFg3clIyTXR1NXdiUHhZWXJOVzYxK01zZ0ZqUlQxQndwQXBFYjA5Y2MrUE96RE9VMUtBcUMyc1FFcnpRaDF5NVhsRDQxZm56VzVRVlQrNHBLZndLUFhPWnY2SlUwUThuNHlmNWdzYTZBRVVLc0N2Q2M2bzBCcTRKeVVsaUg2ZTBySmh1TEJpQXAyaC9aWHpSMTRUNGorVHZ4eSt6Mk1peEFzUmV6NXFvb0FyV25WeDU2Nk9FZTFEOVVwOVkvakUrVmNvczZxSDhYanI5WTVZZ3A2RTV6TUtwYjIxd3JlazhlbWdLQWpqOWNCcnhPZ1lDKzN6K2d1R1FJWDByL21CMFEvQzNoSStlTkhTeXFTcnM0ZzcrUW56STEwR09lQkQvQU4yQ1JOMlNhZ2NFL1pKL2Vtdm1YUFBUancvdlR4SGtnMG9WRy9PekVzNXhreFIrVm9QMWU1RzhhRm1oRjZIdkF2NnhZZGtlYVJ6eVR4aFltU1FFbEQvVjVrL1pseVFEQ2lxQTNucms2R25SMExVblMyRmNrVnNtN1pXSU1NaXV3OE03Rng1djBhbENRUWNRNkozbkw0a2sxZ0VMM2h2bnRNUkFENEhUby9Sd25HWlJ6V0VvRkNpNFYwWkU1aGRESFVXMllhSWpuc2w0SHNucERNVU5MVlh5OG0vRUxoalZsQ290U0RYbjEzQm12cXdNb1hJUW9lQjVTOU8zQ2FDSzFHQXJGMzNXWG9YR1FCVWZ5MHI4TUwyWDRmUlFINGgrc01mOGM2b2VTTFZUWGI0WkVhQy90Y2RUdGJDZW9Nak5DRkx1cDVFL0RnZjdhRmxINFAxZlU2WVpNaTFkWkZsNlNRSUQ0c2dyemtDbWdnL2hYbzJ1UEVjUDNSTlAwbFhFZEo3cFdBR29XNXdrZEU1dXVtZDJOa0lHRGtBK3JKZFJoZzVkdndnQXYzMXRKWjZ5dVFJVXpDMU9La3FFdnVXME5MT0tXYUVFeW5CTlkxQndKcm9CblhHUThMc0FKRVFmUStucEFIWGp5S1l0K2haM1cvOEE2OURtcENIclBreWx6VzRsRGFrQVNWbm1MK3RLNzVpL1phKzYrZXhFaXIrSHlBNUZOWWltZUt2U0xqNWQ1ZHpCRUNTcjF6L1pkYlBucUtOOGd3SGZPNGJ4K0VIdkR3NEFjZzVueGN1Q0Q4Y0ZUSjVndW9wTlBSemY2bHpjN0lUcjhURWsrL2t5N0xqVlJOU0dHQWFtQUVnVDJWOWJndlY2SldjaDVNbnNUSktxMS9vZDNmajViM3YwNHd0MFlIb3gvYjg2dTFDRjB0YmxRRzI4ZXNjLzdqZkVvdjhBeUM1MlBWbEtIM2k1bjY5Njc4ZnN6WEJaTG4zMzg4N2dzNUx5UVY4VzRueDBqY0N5L1BIRGxCR0poOTU2SE5MNm5nQlJJZlRpeDF5Q0IvME9uSEg0UjZEbE1jZ1l5a2dlSFI5eVJ0Ty9QN0ZTWGR1SUVmMDA1MWRTeWR2TjgrVytaYy9ERUZYbGtOelgrbkxDRmE4cGpLT2ljUFlFVDlocFVnZ2lwSnhuZ2MzL3hBQXhFUUFDQWdFQ0JBUUZBd1FEQUFBQUFBQUFBUUlSQXhJaEJCQXhRUVVUVWFFVUZTSnhnU0Frc1dHUjhQRWpNc0gvMmdBSUFRSUJBVDhBTFF1U3Q5RHB6NmM3SExsYlFwTkVabDJKbTFDcnZ6YS9SMEZMc1JaSm5RdjlGT2l1YVRJd2RDanNKYmJqTDVYK3FPeHFMNVZ5UW5YT3FJcE4wTEd1d29sRklva3V3L1FwaVlwbE1VVzk2L1N0eXJGR2JFcGN0SW9XTEVSeHBkUlJYWVRTTE5oeDJIRWNSeE5Lb1VTclJHNUhseFo1SkhEdTdGZ1hxU3d2c1J4MGltS0RIalZXVFJCaUVueWNUU3VVVTJVZmNaV3dsUjVqYUZOb1dXUXN5N2ltalhTMklQVWhLaTZJdlVVTldqUTRzV3dtNkxSMTVVSkQ3amcyN05KUnBMTGQ4azBJVXBVWWFVTEhiSjVIYk9IYmFhTWR0a2xRa21VeE9pOXVXemlhRWFQUXJzT0k0bWswaWQ4aytVWjBqV1lXOU5qenFtWDlSZ2c5SWhwMFFTYkdyUThhT25KT2kyUlkwdVZEaVUvMHF1VVc0OXlteUNxYlpoZDQwMGJEL3dDck1TdGpTNklxaVNkbENOcUkyaHZZVEtaVHJrMHhRTkROTEtmTldRalRaaGNZd28yZlFlMFhaaUtZazNkaWc2M05HMW84dXh3cGlaWkZXVzBLenAxSFpCYW1hRUtNVFJFOHRIRC9BUEpkOWp5NG5EUzg3STRzU2lpbFp4c3E0YVgyL0o0YzE1Rm0rNG4xM0xzVmprcTZpV3Brc1cxbWxVSXZzUVRZMGhxeGJHdG1zVTJoNUdjUGtsRlNIblp3bVNVWnlhZitXWUozalRZMmpqNWZ0SnRIaFR2aHZ5eVZhUk5rcFRiRTNWTWhLVUo3dllqbGc1TkRuSFRRM2FGSXRFV3JKU1E0MnVTVmlpVlE2bzRWZlRLL1VhT0ZUMXlJUlVZMFhUT1BkY0pObmhzdjJ2NVpxc2c0TldOVXh5U1ZzZHlXcE1oRzQ3R3lSS1RYUXZjdFVSYUpJMG5sSWNIRWJhWkdXb2ZRNFJyUkw3anlSUncwMHJQaTBsVkM0cFBzY1p4S253MG9vOE9UK0cvTE4xc1JhZ1hmUTB0amcreEhKQ0tvdjBOV3dvS1RzZVAwTkZMWXhxYmxRdU5qMU0yZks0dTJMSlBRa21ZVzV3M0l4c20xMk9HVW5GamkwY0pGeVROTjl5Nk04V3NMYlJ3czVRd1VuWFhvTEpPdXJHcFRROVVZVXkyekRHYzR0b2h3V1JqejVZdXJNRTU1Y3FpMzFaOE1vczR2RExIaWMwZWJrWGM0S1dXV3J1UGhNc2QyaktxZ3pUOUtLYTdqbTFGTGxLVUZGc3VQcUtVTkQzRjViZEZLOWtjVmhmd2pkOWFNTnh4aW0wUWpoUzZISFpNYWdsSDFMUEQ4a1Z3LzVQTzJzeVpMbTMvVTRUSW84VEJ2MUhsM1BFTW43Vi9kSG1NOEh5YXBUWDIvOUpUbTNUZnV4UWIyditSTGYvWkZhM3BYWDhrZC93REdhZGo2VS84QVpDR3BYMit4cFNmVDJNWER6Y2xsZzBoUmpxM00yVnk0TlJSQlV1cFMwOVRESlZlbzRxYWxVVWhMWXdaRmlnMHg4VDlESXBzaEZxZXIwRnhNdWh4M0VOd1NMYjduQTVKUXl0SjlVZkNZWXU2UkhoWXJzanlZVzBra1E0ZlQySVljYzNhUStIeHpmUkVjR0tmWWZEd1VIOUtOR0piU2c2SkxRM0dLMnNUaStvb3JKYWo3OUNDbEZkUDdpeHV2cWRsUnVraUtLWmxpcGRUNEpTbGV0cCtsbUhHOFNycUpwbDlqaWZxa1F3YWx1TEZvZXBHcFB1S2txUnRiTm10eEpKVWtKNlZzaE9sMFBOSGMzdWkzS05ORVhTcEllVnhuOUtNVHpLMDYva2hQSkdUUnJVdG12YzB4cm8vN3Nua2xvMHJiK3RrT0p5ckxva3R2VWprY1hkRTVTbVJlOW1SNm4wb3VKYmE2MFJuSlJwa2ZFTTNxUHhITWxkbnpmSWxib1hqcWEyL2dmamFraVBqdlZDOGV0MVM5ejU5WFd2Y1hqMFVyb1hqc2IyU1g5eVBqOEgxcjNQbjJQc2w3bnoydDB2NVBuc1pLNlh1ZlBvcDFTOXhlUHF1M3VMeG5ITjFKYmZremVKNE1DcUx0L1puemVTNjE3bnphZjlDWGpMNkt2Y2g0bG1tNlN0bnhmR2VudVI0N000MUtJK09jVnZFLy84UUFMQkVBQWdJQkFnVUVBZ0lDQXdBQUFBQUFBQUVDRVFNRUlSQVNNVUZSQlJNVWdTQXljYUVpc1RCaGtmL2FBQWdCQXdFQlB3QkZJYTI0ZEJicmlpaEpMZ3VORGpZb0RqWENOc2lLMXdTRkVwL2dsVjhFckVxLzRHNkxOMm1VUVd3a1VVeWl5TC9CSmxDdEZzdGxzVDQyTmpsWFJqYkl5YU94SG9SSTFYRGxRaHRGYmNLcmpaWnpMOE5oeThGMlZzTDlSS1QzWXA5aFNkRUdtaXhQaEdUdG82UFlXUml5SWM5aDVrUXlKcmNsa2RFWDVPWWpKM1Q0TFpjTFF0ckxMRXl4TkpDcVNJOExRc2NVY2lQYlE0ZUNtaFFHbkF1eFJkRDJPWkVKK1JTVExiUTFZclhCYjhLc2owM0l0TFlSekNaUlNLUlJSeXF5ZHVURkZFWUpSTXlHb3BFZWhja2hPeE44YnBuTXlNMEtZbnVjenNzdERvb29yZ2taRi9rUnduTHRabmU3b1RJY0U2WXNqRmIvQUFwVVIyWXRpUE14RnNzc1RMUnNKRFNiNFQvU2ljV3Bzb1Zwa2hDTWI3Q2Q4Tm5ac2xSR0tUczZqa3hQbDZNN1grYTI0TjNFeVc1dDhFbTViRTAwNmFIQnJvVzBqbW9XUVRaR2FsMU5sdStEYlhDWFNpSzVvVUlicUxZc2pvNWpuWXNoblN4VlhjOTFtc3d4d1lZeWltcjgvd0FIZDhQVDQzcklYWFh2MCt6MU5WcU5xNkxwOWtkMFBaRlNhM0lwWFFrVWlNcEpVUmxSY2ZKR203VE1oQ1Zib1c1VzFISWhZbVBFbWhZVjVOWmpjbkcvSGM5ZzF1R0x4UlczL3Q5ak5CUXlOSVI2YXExa04vNnM5V1Y2djZYYWlLS1RSR01hcGxKTWxGU1hUY2xpa2xZbFNvWFVVZVZiSExLOXhwMHlFYjNvaExmY3RjRktWVVJkb3VYZzFsWEd2SG16bWFScnB4ZUtPLzhBVmRpYmJrMlJqWjZkRi9NaHkzOWRUMVdEZXJhMzZMcUtGRDJJMHhRdm9SMjJhSk81YmxFVkh1TG9MWjBTM3NoWW52c0tUN2tXbUpScmpyNzU0L3g0b1dPVE5kQ1RpdG45L3dBQzByNjJmRmw1TkJnY2RURnV2OWVlNTZrNHJWTmJkRjBMWGtxK2hGVlluUkdhcW1lMDV1eFJGRkhNb3hvVTQwS1VMSnpndDBmRWtqRGh4eDNTRkRHNU8wWnVUSE9raHpWV1F0bXVkVGpmanpaR1NhTmE0d1VWRkw2RTBrS0Z4czBrNHZVTGRmZTVxNFFubmIyZlRzZTNDLzFRbkNEMlJCUmMyMEtKbWxHTXR4NnpGVkprY1dLVWVobWhERmlsT3VpRnFsSk0wZVNHVEp5dmM5ckY0UnFsamdsMkk2akZMb3pIUW5jbUorVUtGdHVoTjFRbEpzVjdsU2Nsc05aVkd5SE5XN05IbmNkYlVlcXN5M1BKY240UGJRNVpYTHFhQ0UzTnR2c2NwNmhpa3RScyt3OGE2R0hGVUZacThONldkZUJRakE5TXh0NnBmd3lPSkhyR1BraEJyL3N4NDQxc2h5U1E3U3NjNVJRK2F0eFhHOXlwcGRUbm1rMVlwekphcUNnOFUwMmU0M0habUhDMXJuSm5jVjN1ak5Cd2JUUm9vVXBTdmhxOGZ1U1RURnBybWkwaWJVb05EMDhMYmU1NmZoaXNqZkxWSWlqMUhFbnAxSks2WkhVemFwRDFEOHNqbGtsYmJvZWZtN3NlYWNlNUhVU2pkTm52elc3Wjc4cnZtSTVzdHVwRVorNHVhVDNaTEcrdzV6eGJ5My8yZTVDZDI2U0huaW5jSXBmMnhaY3JqY21uOUN5SjdjT2QyTFV1RWYwVFI3NmwxUjdzT3pLdStWN0dpWExqWTlUQzZUSlpGbGc0U0Z6K0RteU4yeFNuUmVUeVAzSGJFcGIyTzJKRUpPQkZiMmh1U2JkaXlYQjJUeVFudlc1SnhsSFpDNTErckZreng3LzBRYytlM3Y4QVE2NUxKNzdFTmhwMDlpRUdoUElsVmllMjZQNEZpeE1XREc5aGFhTDJQalVoYVpEMDZmYytMQ205eGFPSGF4YVhIZEh4SW54SVM4aTBLdmRpMFVQTEk2Q0M3aTBNR2ZCaGZVV2loQldtUTBzcDllaDhPQXREQStGR3VwOExGM2JQZzR1MG1QUks5bWZCWGsvLzJRPT0iIGlkPSJlIiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIvPjwvZGVmcz48L3N2Zz4=", + "description": "A gas produced from vehicle emissions and the burning of fossil fuels. Results displayed by combining the latest and aggregated values and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'nitrogen', label: 'Nitrogen dioxide', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'µg/m³', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'nitrogen', 'µg/m³', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Nitrogen dioxide\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#3B911C\"},{\"from\":40,\"to\":90,\"color\":\"#7CC322\"},{\"from\":90,\"to\":120,\"color\":\"#F89E0D\"},{\"from\":120,\"to\":230,\"color\":\"#F77410\"},{\"from\":230,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nif (value < -10) {\\n\\tvalue = -10;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:Tk8yLXZhbHVlLWFuZC1jaGFydC1jYXJkLWJhY2tncm91bmQucG5n:Tk8yLXZhbHVlLWFuZC1jaGFydC1jYXJkLWJhY2tncm91bmQucG5n;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Nitrogen dioxide\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"public\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "nitrogen", + "nitrogen dioxide" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/noise_level_card.json b/application/src/main/data/json/system/widget_types/noise_level_card.json new file mode 100644 index 0000000000..d91fcf6632 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/noise_level_card.json @@ -0,0 +1,35 @@ +{ + "fqn": "noise_level_card", + "name": "Noise level card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest noise level telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'noise', label: 'Noise level ', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Noise level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value;\\nif (!prevValue) {\\n value = Math.random() * 120;\\n} else {\\n value = prevValue + Math.random() * 40 - 20;\\n}\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"bar_chart\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":50,\"color\":\"#80C32C\"},{\"from\":50,\"to\":70,\"color\":\"#FFA600\"},{\"from\":70,\"to\":90,\"color\":\"#F36900\"},{\"from\":90,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":50,\"color\":\"#80C32C\"},{\"from\":50,\"to\":70,\"color\":\"#FFA600\"},{\"from\":70,\"to\":90,\"color\":\"#F36900\"},{\"from\":90,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Noise level card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"dB\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "sound level", + "acoustic level", + "decibel level", + "volume", + "loudness", + "ambient noise", + "sound intensity", + "acoustic intensity" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/noise_level_card_with_background.json b/application/src/main/data/json/system/widget_types/noise_level_card_with_background.json new file mode 100644 index 0000000000..3dee42dc6a --- /dev/null +++ b/application/src/main/data/json/system/widget_types/noise_level_card_with_background.json @@ -0,0 +1,35 @@ +{ + "fqn": "noise_level_card_with_background", + "name": "Noise level card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest noise level telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'noise', label: 'Noise level ', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Noise level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value;\\nif (!prevValue) {\\n value = Math.random() * 120;\\n} else {\\n value = prevValue + Math.random() * 40 - 20;\\n}\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"bar_chart\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":50,\"color\":\"#7CC322\"},{\"from\":50,\"to\":70,\"color\":\"#F89E0D\"},{\"from\":70,\"to\":90,\"color\":\"#F77410\"},{\"from\":90,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":50,\"color\":\"#7CC322\"},{\"from\":50,\"to\":70,\"color\":\"#F89E0D\"},{\"from\":70,\"to\":90,\"color\":\"#F77410\"},{\"from\":90,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Noise level card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"dB\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "sound level", + "acoustic level", + "decibel level", + "volume", + "loudness", + "ambient noise", + "sound intensity", + "acoustic intensity" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/noise_level_chart_card.json b/application/src/main/data/json/system/widget_types/noise_level_chart_card.json new file mode 100644 index 0000000000..0a91cd3f9f --- /dev/null +++ b/application/src/main/data/json/system/widget_types/noise_level_chart_card.json @@ -0,0 +1,38 @@ +{ + "fqn": "noise_level_chart_card", + "name": "Noise level chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a noise level data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'noise', label: 'Noise level', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'dB', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'noise', 'dB', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Noise level\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value;\\nif (!prevValue) {\\n value = Math.random() * 120;\\n} else {\\n value = prevValue + Math.random() * 40 - 20;\\n}\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"dB\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":50,\"color\":\"#80C32C\"},{\"from\":50,\"to\":70,\"color\":\"#FFA600\"},{\"from\":70,\"to\":90,\"color\":\"#F36900\"},{\"from\":90,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value;\\nif (!prevValue) {\\n value = Math.random() * 120;\\n} else {\\n value = prevValue + Math.random() * 40 - 20;\\n}\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"dB\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < -20) {\\n\\tvalue = -20;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"dB\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Noise level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"bar_chart\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "sound level", + "acoustic level", + "decibel level", + "volume", + "loudness", + "ambient noise", + "sound intensity", + "acoustic intensity" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/noise_level_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/noise_level_chart_card_with_background.json new file mode 100644 index 0000000000..c573028288 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/noise_level_chart_card_with_background.json @@ -0,0 +1,38 @@ +{ + "fqn": "noise_level_chart_card_with_background", + "name": "Noise level chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a noise level data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'noise', label: 'Noise level', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'dB', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'noise', 'dB', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Noise level\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value;\\nif (!prevValue) {\\n value = Math.random() * 120;\\n} else {\\n value = prevValue + Math.random() * 40 - 20;\\n}\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"dB\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":50,\"color\":\"#7CC322\"},{\"from\":50,\"to\":70,\"color\":\"#F89E0D\"},{\"from\":70,\"to\":90,\"color\":\"#F77410\"},{\"from\":90,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value;\\nif (!prevValue) {\\n value = Math.random() * 120;\\n} else {\\n value = prevValue + Math.random() * 40 - 20;\\n}\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"dB\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < -20) {\\n\\tvalue = -20;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"dB\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Noise level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"bar_chart\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "sound level", + "acoustic level", + "decibel level", + "volume", + "loudness", + "ambient noise", + "sound intensity", + "acoustic intensity" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/openstreet_map.json b/application/src/main/data/json/system/widget_types/openstreet_map.json new file mode 100644 index 0000000000..88fbe1e485 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/openstreet_map.json @@ -0,0 +1,29 @@ +{ + "fqn": "maps_v2.openstreetmap", + "name": "OpenStreet Map", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the location of the entities on OpenStreetMap. Allows to choose among existing tile providers or configure own server. Some providers require additional licenses. Highly customizable via custom markers, marker tooltips, and widget actions. ", + "descriptor": { + "type": "latest", + "sizeX": 8.5, + "sizeY": 6, + "resources": [], + "templateHtml": "", + "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('openstreet-map', false, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-map-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.05427416942713381,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.680594833308841,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.9430343126300238,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.1784452363910778,\"funcBody\":\"return \\\"colorpin\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]},{\"type\":\"function\",\"name\":\"Second point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.05012157428742059,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.6742359401617628,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.773875863339494,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.405822538899673,\"funcBody\":\"return \\\"thermometer\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"openstreet-map\",\"mapProvider\":\"OpenStreetMap.Mapnik\",\"useCustomProvider\":false,\"customProviderTileUrl\":\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#fe7569\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'thermometer') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"useClusterMarkers\":false,\"zoomOnClick\":true,\"maxClusterRadius\":80,\"animate\":true,\"spiderfyOnMaxZoom\":false,\"showCoverageOnHover\":true,\"chunkedLoading\":false,\"removeOutsideVisibleBounds\":true,\"useIconCreateFunction\":false},\"title\":\"OpenStreet Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" + }, + "externalId": null, + "tags": [ + "mapping", + "gps", + "navigation", + "geolocation", + "satellite", + "directions" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/ozone__o3__card.json b/application/src/main/data/json/system/widget_types/ozone__o3__card.json new file mode 100644 index 0000000000..d494ccc16e --- /dev/null +++ b/application/src/main/data/json/system/widget_types/ozone__o3__card.json @@ -0,0 +1,37 @@ +{ + "fqn": "ozone_o3_card", + "name": "Ozone (O3) card", + "deprecated": false, + "image": "tb-image:b3pvbmUtdmFsdWUtY2FyZC5zdmc=:b3pvbmUtdmFsdWUtY2FyZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMTIiIHJ4PSIzLjUiLz48cGF0aCBmaWxsPSIjRkZBNjAwIiBkPSJNMzQuNTI4IDI0LjI1NWMtNy4yMiAwLTEzLjA3OSA1Ljg1OS0xMy4wNzkgMTMuMDc4IDAgNy4yMiA1Ljg2IDEzLjA3OSAxMy4wNzkgMTMuMDc5IDcuMjIgMCAxMy4wNzgtNS44NiAxMy4wNzgtMTMuMDc5IDAtNy4yMi01Ljg1OS0xMy4wNzgtMTMuMDc4LTEzLjA3OFpNMjQuMDY1IDM3LjMzM2MwLS43OTcuMTA0LTEuNTgyLjI3NC0yLjMyOGw2LjI1MiA2LjI1MnYxLjMwOGEyLjYyMyAyLjYyMyAwIDAgMCAyLjYxNiAyLjYxNnYyLjUyNGMtNS4xNC0uNjU0LTkuMTQyLTUuMDQ5LTkuMTQyLTEwLjM3MlptMTguMTY2IDcuMDYzYy0uMzQtMS4wNi0xLjMwOC0xLjgzMS0yLjQ4NS0xLjgzMWgtMS4zMDhWMzguNjRjMC0uNzItLjU4OC0xLjMwOC0xLjMwOC0xLjMwOGgtNy44NDd2LTIuNjE1SDMxLjljLjcyIDAgMS4zMDgtLjU4OSAxLjMwOC0xLjMwOHYtMi42MTZoMi42MTVhMi42MjMgMi42MjMgMCAwIDAgMi42MTYtMi42MTZ2LS41MzZjMy44MzIgMS41NDMgNi41NTMgNS4zMSA2LjU1MyA5LjY5MSAwIDIuNzItMS4wNiA1LjIwNi0yLjc2IDcuMDYzWiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjg3IiBkPSJNNjQuODEgMjkuOTU4di41MDhjMCAuNjk4LS4wOTIgMS4zMjUtLjI3NCAxLjg3OWE0LjA1NSA0LjA1NSAwIDAgMS0uNzggMS40MTZjLS4zMzUuMzg5LS43MzcuNjg3LTEuMjA3Ljg5NS0uNDcuMjAzLS45OS4zMDQtMS41NjEuMzA0YTMuODc4IDMuODc4IDAgMCAxLTEuNTU1LS4zMDQgMy40OTQgMy40OTQgMCAwIDEtMS4yMTMtLjg5NSA0LjE1NyA0LjE1NyAwIDAgMS0uOC0xLjQxNmMtLjE4Ni0uNTU0LS4yNzktMS4xOC0uMjc5LTEuODc5di0uNTA4YzAtLjY5OC4wOTMtMS4zMjIuMjgtMS44NzJhNC4xMzUgNC4xMzUgMCAwIDEgLjc4Ny0xLjQxNiAzLjM5IDMuMzkgMCAwIDEgMS4yMTItLjg5NSAzLjgxIDMuODEgMCAwIDEgMS41NTUtLjMxYy41NzEgMCAxLjA5Mi4xMDMgMS41NjIuMzEuNDcuMjAzLjg3My41MDIgMS4yMTIuODk1LjMzOS4zOS41OTkuODYxLjc4IDEuNDE2LjE4Ny41NS4yOCAxLjE3NC4yOCAxLjg3MlptLTEuNTk0LjUwOHYtLjUyYzAtLjUxNy0uMDUxLS45NzItLjE1My0xLjM2NWEyLjgwNCAyLjgwNCAwIDAgMC0uNDM4LS45OTcgMS44MzEgMS44MzEgMCAwIDAtLjcwNC0uNjFjLS4yOC0uMTM5LS41OTUtLjIwOS0uOTQ2LS4yMDlzLS42NjQuMDctLjk0LjIxYy0uMjc0LjEzNS0uNTEuMzM4LS43MDQuNjEtLjE5LjI2Ni0uMzM2LjU5OC0uNDM4Ljk5NmE1LjQ4NiA1LjQ4NiAwIDAgMC0uMTUyIDEuMzY1di41MmMwIC41MTYuMDUuOTczLjE1MiAxLjM3MXMuMjUuNzM0LjQ0NCAxLjAxYy4yLjI3LjQzNi40NzYuNzExLjYxNWEyLjEgMi4xIDAgMCAwIC45NC4yMDNjLjM1NSAwIC42Ny0uMDY3Ljk0Ni0uMjAzLjI3NS0uMTQuNTA3LS4zNDUuNjk4LS42MTYuMTktLjI3NS4zMzQtLjYxMS40MzEtMS4wMDlhNS41NyA1LjU3IDAgMCAwIC4xNTMtMS4zN1ptOC41OTcgMy4xNDl2MS4yMThoLTUuMTE2di0xLjIxOGg1LjExNlptLS4xNC00LjY5OC00LjQ1NiA1LjkxNmgtLjk5di0uOTg0bDQuNDMtNS44ODRoMS4wMTZ2Ljk1MlptLS41OS0uOTUydjEuMjE5aC00Ljc4di0xLjIxOWg0Ljc4Wm0xLjgxMSAzLjUxdi0uMTQ2YzAtLjQ5NS4wNzItLjk1NC4yMTYtMS4zNzcuMTQ0LS40MjcuMzUyLS43OTguNjIyLTEuMTEuMjc1LS4zMTguNjEtLjU2NCAxLjAwMy0uNzM3YTMuMjY1IDMuMjY1IDAgMCAxIDEuMzQ2LS4yNjdjLjUwNCAwIC45NTIuMDkgMS4zNDYuMjY3LjM5Ny4xNzMuNzM0LjQxOSAxLjAwOS43MzYuMjc1LjMxMy40ODUuNjg0LjYyOCAxLjExMS4xNDQuNDIzLjIxNi44ODIuMjE2IDEuMzc3di4xNDZjMCAuNDk1LS4wNzIuOTU1LS4yMTYgMS4zNzhhMy4yOTYgMy4yOTYgMCAwIDEtLjYyOCAxLjExIDIuOTEgMi45MSAwIDAgMS0xLjAwMy43MzdjLS4zOTMuMTczLS44NC4yNi0xLjM0LjI2YTMuMzUgMy4zNSAwIDAgMS0xLjM1MS0uMjYgMi45MSAyLjkxIDAgMCAxLTEuMDAzLS43MzYgMy4yOTQgMy4yOTQgMCAwIDEtLjYyOS0xLjExMSA0LjI1MiA0LjI1MiAwIDAgMS0uMjE2LTEuMzc4Wm0xLjUzLS4xNDZ2LjE0NmMwIC4zMS4wMzIuNjAxLjA5NS44NzYuMDY0LjI3NS4xNjMuNTE3LjI5OS43MjQuMTM1LjIwNy4zMDkuMzcuNTIuNDg5LjIxMi4xMTguNDY0LjE3OC43NTYuMTc4LjI4MyAwIC41MjktLjA2LjczNi0uMTc4LjIxMi0uMTE5LjM4NS0uMjgyLjUyLS40ODkuMTM2LS4yMDcuMjM1LS40NDkuMjk5LS43MjQuMDY4LS4yNzUuMTAyLS41NjcuMTAyLS44NzZ2LS4xNDZhMy41NSAzLjU1IDAgMCAwLS4xMDItLjg2MyAyLjIxOCAyLjIxOCAwIDAgMC0uMzA1LS43MyAxLjQ1NyAxLjQ1NyAwIDAgMC0uNTItLjQ5NSAxLjQzMiAxLjQzMiAwIDAgMC0uNzQzLS4xODRjLS4yODggMC0uNTM3LjA2MS0uNzQ5LjE4NGExLjQ3NSAxLjQ3NSAwIDAgMC0uNTE0LjQ5NSAyLjMwNSAyLjMwNSAwIDAgMC0uMjk5LjczIDMuNzggMy43OCAwIDAgMC0uMDk1Ljg2M1ptNy45MDYtMS44OTh2NS40MDJIODAuOHYtNi44NjhoMS40NGwuMDkgMS40NjZabS0uMjczIDEuNzE0LS40OTYtLjAwNmMuMDA1LS40ODcuMDcyLS45MzMuMjA0LTEuMzQuMTM1LS40MDYuMzIxLS43NTUuNTU4LTEuMDQ3LjI0Mi0uMjkyLjUzLS41MTYuODY0LS42NzMuMzM0LS4xNi43MDYtLjI0IDEuMTE3LS4yNC4zMyAwIC42MjguMDQ2Ljg5NS4xMzkuMjcuMDg5LjUwMS4yMzUuNjkyLjQzOC4xOTQuMjAzLjM0Mi40NjcuNDQ0Ljc5My4xMDIuMzIyLjE1Mi43MTguMTUyIDEuMTg3djQuNDM3aC0xLjUzNlYzMC4zOWMwLS4zMy0uMDQ4LS41OS0uMTQ2LS43OGEuODE3LjgxNyAwIDAgMC0uNDEyLS40MTMgMS41NDggMS41NDggMCAwIDAtLjY2Ny0uMTI3IDEuNDggMS40OCAwIDAgMC0uNzA0LjE2NWMtLjIwOC4xMS0uMzg0LjI2LS41MjcuNDUtLjE0LjE5LS4yNDguNDEtLjMyNC42Ni0uMDc2LjI1LS4xMTQuNTE3LS4xMTQuOFptOS4yNyAzLjgxNWMtLjUwOCAwLS45NjctLjA4Mi0xLjM3OC0uMjQ3YTMuMDQyIDMuMDQyIDAgMCAxLTEuMDQtLjcwNSAzLjEyMyAzLjEyMyAwIDAgMS0uNjU1LTEuMDYgMy43MTQgMy43MTQgMCAwIDEtLjIyOC0xLjMxNHYtLjI1NGMwLS41MzcuMDc4LTEuMDI0LjIzNS0xLjQ2LjE1Ni0uNDM2LjM3NC0uODA4LjY1NC0xLjExNy4yNzktLjMxMy42MDktLjU1Mi45OS0uNzE3LjM4LS4xNjUuNzkzLS4yNDggMS4yMzgtLjI0OC40OSAwIC45Mi4wODMgMS4yODguMjQ4LjM2OC4xNjUuNjczLjM5OC45MTQuNjk4LjI0Ni4yOTYuNDI4LjY1LjU0NiAxLjA2LjEyMy40MS4xODQuODYzLjE4NCAxLjM1OHYuNjU0SDg4Ljc3di0xLjA5OGgzLjc5NXYtLjEyYTIuMTQ4IDIuMTQ4IDAgMCAwLS4xNjUtLjc3NSAxLjMxNSAxLjMxNSAwIDAgMC0uNDUtLjU4NGMtLjIwMy0uMTQ4LS40NzQtLjIyMi0uODEzLS4yMjItLjI1NCAwLS40OC4wNTUtLjY3OS4xNjUtLjE5NS4xMDYtLjM1OC4yNi0uNDg5LjQ2M2EyLjQzOCAyLjQzOCAwIDAgMC0uMzA0LjczNyA0LjEzOCA0LjEzOCAwIDAgMC0uMTAyLjk1OHYuMjU0YzAgLjMuMDQuNTguMTIuODM4LjA4NS4yNTQuMjA4LjQ3Ni4zNjkuNjY2LjE2LjE5LjM1NS4zNDEuNTg0LjQ1MS4yMjguMTA2LjQ4OS4xNTkuNzguMTU5LjM2OSAwIC42OTctLjA3NC45ODQtLjIyMi4yODgtLjE0OC41MzgtLjM1OC43NS0uNjI5bC44MDYuNzgxYy0uMTQ4LjIxNi0uMzQxLjQyMy0uNTc4LjYyMmEyLjg5MSAyLjg5MSAwIDAgMS0uODcuNDc2IDMuNDYgMy40NiAwIDAgMS0xLjE4LjE4NFoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii4zOCIgZD0iTTYxLjM3NCA1MC45MTh2LjkxNWgtMy4zNjF2LS45MTVoMy4zNlptLTMuMDQtNS43OHY2LjY5NUg1Ny4xOFY0NS4xNGgxLjE1NVptNi44MjIgNS42OTh2LTIuMzczYzAtLjE3OC0uMDMyLS4zMzEtLjA5Ni0uNDZhLjY3Ny42NzcgMCAwIDAtLjI5NC0uMjk5IDEuMDA2IDEuMDA2IDAgMCAwLS40ODgtLjEwNWMtLjE4IDAtLjMzNy4wMy0uNDY5LjA5MmEuNzU4Ljc1OCAwIDAgMC0uMzA4LjI0OC41OTkuNTk5IDAgMCAwLS4xMS4zNTRoLTEuMTA0YzAtLjE5Ni4wNDgtLjM4Ni4xNDMtLjU3LjA5NS0uMTg0LjIzMy0uMzQ4LjQxNC0uNDkyLjE4LS4xNDQuMzk3LS4yNTguNjQ4LS4zNC4yNTEtLjA4My41MzMtLjEyNS44NDYtLjEyNS4zNzQgMCAuNzA1LjA2My45OTMuMTg5LjI5MS4xMjYuNTIuMzE2LjY4NS41Ny4xNjkuMjUxLjI1My41NjcuMjUzLjk0N3YyLjIxMmMwIC4yMjcuMDE1LjQzLjA0Ni42MTEuMDM0LjE3OC4wODEuMzMzLjE0My40NjV2LjA3M2gtMS4xMzZhMS45NjUgMS45NjUgMCAwIDEtLjEyNC0uNDU1IDMuNzI5IDMuNzI5IDAgMCAxLS4wNDEtLjU0MlptLjE2MS0yLjAyOC4wMS42ODVoLS43OTZjLS4yMDUgMC0uMzg2LjAyLS41NDIuMDZhMS4xMTUgMS4xMTUgMCAwIDAtLjM5MS4xNjUuNzE4LjcxOCAwIDAgMC0uMzEzLjYyMWMwIC4xMzIuMDMuMjUzLjA5Mi4zNjMuMDYxLjEwOC4xNS4xOTIuMjY3LjI1My4xMi4wNjEuMjYzLjA5Mi40MzIuMDkyLjIyNyAwIC40MjUtLjA0Ni41OTMtLjEzOGExLjIyIDEuMjIgMCAwIDAgLjQwNS0uMzQ1Ljc1Ljc1IDAgMCAwIC4xNTYtLjM5bC4zNTkuNDkxYy0uMDM3LjEyNi0uMS4yNjEtLjE4OS40MDVhMS45NTkgMS45NTkgMCAwIDEtLjM1LjQxNCAxLjczNSAxLjczNSAwIDAgMS0xLjE5LjQ0MWMtLjMyNSAwLS42MTUtLjA2NC0uODctLjE5M2ExLjU0NiAxLjU0NiAwIDAgMS0uNTk3LS41MjkgMS4zNzEgMS4zNzEgMCAwIDEtLjIxNi0uNzU4YzAtLjI2NC4wNDktLjQ5Ny4xNDctLjdhMS4zNyAxLjM3IDAgMCAxIC40NDItLjUxNGMuMTk2LS4xMzguNDM1LS4yNDIuNzE3LS4zMTMuMjgyLS4wNzMuNjA0LS4xMS45NjUtLjExaC44N1ptNS4wOTggMS42NzhjMC0uMTEtLjAyOC0uMjEtLjA4My0uMjk5LS4wNTUtLjA5Mi0uMTYtLjE3NC0uMzE3LS4yNDhhMy4wNzggMy4wNzggMCAwIDAtLjY4LS4yMDIgNS44NjQgNS44NjQgMCAwIDEtLjcyNy0uMjA3IDIuMzA1IDIuMzA1IDAgMCAxLS41Ni0uMyAxLjI1OSAxLjI1OSAwIDAgMS0uMzY0LS40MTMgMS4xNDYgMS4xNDYgMCAwIDEtLjEyOS0uNTUyYzAtLjIwMi4wNDQtLjM5NC4xMzMtLjU3NS4wOS0uMTguMjE2LS4zNC4zODItLjQ3OC4xNjYtLjEzOC4zNjYtLjI0Ni42MDItLjMyNi4yNC0uMDguNTA2LS4xMi44LS4xMi40MTcgMCAuNzc0LjA3IDEuMDcyLjIxMi4zLjEzOC41My4zMjYuNjkuNTY1LjE1OS4yMzYuMjM5LjUwMy4yMzkuOGgtMS4xMDlhLjcwNy43MDcgMCAwIDAtLjEtLjM2Ny43MDUuNzA1IDAgMCAwLS4yOTUtLjI4IDEuMDEgMS4wMSAwIDAgMC0uNDk3LS4xMTFjLS4xOSAwLS4zNDcuMDMtLjQ3My4wOTJhLjY0OC42NDggMCAwIDAtLjI3Ni4yMy41ODcuNTg3IDAgMCAwLS4wNDEuNTM4LjUzLjUzIDAgMCAwIC4xNjUuMTc5Yy4wNzcuMDUyLjE4MS4xMDEuMzEzLjE0Ny4xMzUuMDQ2LjMwMy4wOS41MDYuMTMzLjM4LjA4LjcwNi4xODMuOTc5LjMwOC4yNzYuMTIzLjQ4Ny4yODIuNjM0LjQ3OS4xNDguMTkzLjIyMS40MzguMjIxLjczNSAwIC4yMjEtLjA0Ny40MjMtLjE0Mi42MDctLjA5Mi4xODEtLjIyNy4zMzktLjQwNS40NzRhMi4wMyAyLjAzIDAgMCAxLS42NC4zMDhjLS4yNDQuMDc0LS41Mi4xMS0uODI3LjExLS40NSAwLS44MzItLjA4LTEuMTQ1LS4yMzlhMS44MjYgMS44MjYgMCAwIDEtLjcxMi0uNjIgMS40NyAxLjQ3IDAgMCAxLS4yNC0uNzkxaDEuMDcyYS44My44MyAwIDAgMCAuMTcuNDkyYy4xMDQuMTIuMjMzLjIwNi4zODYuMjYyLjE1Ny4wNTIuMzE4LjA3OC40ODMuMDc4LjIgMCAuMzY2LS4wMjYuNTAxLS4wNzhhLjcyMS43MjEgMCAwIDAgLjMwOC0uMjIxLjUyNy41MjcgMCAwIDAgLjEwNi0uMzIyWm00LjUzMi0zLjYyOHYuODFoLTIuODA1di0uODFoMi44MDVabS0xLjk5NS0xLjIxOGgxLjEwOHY0LjgxOWMwIC4xNTMuMDIxLjI3LjA2NC4zNTQuMDQ2LjA4LjEwOS4xMzMuMTg5LjE2LjA4LjAyOC4xNzMuMDQyLjI4LjA0MmEyLjIwOSAyLjIwOSAwIDAgMCAuMzktLjA0MWwuMDA1Ljg0NmMtLjA5MS4wMjctLjE5OS4wNTItLjMyMS4wNzMtLjEyLjAyMS0uMjU4LjAzMi0uNDE0LjAzMi0uMjU1IDAtLjQ4LS4wNDQtLjY3Ni0uMTMzYS45OTUuOTk1IDAgMCAxLS40Ni0uNDQ2Yy0uMTEtLjIwNS0uMTY1LS40NzgtLjE2NS0uODE4VjQ1LjY0Wm04LjU4IDUuMDJ2LTMuODAyaDEuMTE0djQuOTc1aC0xLjA0OWwtLjA2NC0xLjE3MlptLjE1Ny0xLjAzNC4zNzMtLjAwOWMwIC4zMzQtLjAzNy42NDItLjExLjkyNGEyLjE0MyAyLjE0MyAwIDAgMS0uMzQuNzMxYy0uMTU0LjIwNi0uMzUuMzY3LS41OS40ODNhMS45OSAxLjk5IDAgMCAxLS44Ni4xN2MtLjI0MSAwLS40NjQtLjAzNS0uNjY2LS4xMDVhMS4zNjcgMS4zNjcgMCAwIDEtLjUyNC0uMzI3IDEuNDg2IDEuNDg2IDAgMCAxLS4zMzYtLjU3NSAyLjY1NSAyLjY1NSAwIDAgMS0uMTItLjg0NnYtMy4yMTRoMS4xMDl2My4yMjRjMCAuMTguMDIxLjMzMi4wNjQuNDU1YS43Ny43NyAwIDAgMCAuMTc1LjI5Yy4wNzMuMDczLjE2LjEyNS4yNTcuMTU2LjA5OS4wMy4yMDMuMDQ2LjMxMy4wNDYuMzE2IDAgLjU2NC0uMDYyLjc0NS0uMTg0LjE4NC0uMTI2LjMxNC0uMjk0LjM5LS41MDYuMDgtLjIxMi4xMi0uNDUuMTItLjcxM1ptMy40MDEtMS44MTF2NS45MzFoLTEuMTA4di02Ljg4OGgxLjAybC4wODguOTU3Wm0zLjI0MiAxLjQ4NXYuMDk2YzAgLjM2Mi0uMDQzLjY5OC0uMTI5IDEuMDA3YTIuNDYgMi40NiAwIDAgMS0uMzcyLjgwNSAxLjcxNSAxLjcxNSAwIDAgMS0uNjAzLjUyOWMtLjIzOS4xMjUtLjUxNS4xODgtLjgyNy4xODgtLjMxIDAtLjU4MS0uMDU2LS44MTQtLjE3YTEuNjY3IDEuNjY3IDAgMCAxLS41ODQtLjQ5MiAyLjY2OSAyLjY2OSAwIDAgMS0uMzg2LS43NDUgNC43OCA0Ljc4IDAgMCAxLS4yMDMtLjk0N1Y0OS4yYy4wNC0uMzY1LjEwOC0uNjk2LjIwMy0uOTkzYTIuNjkgMi42OSAwIDAgMSAuMzg2LS43NjhjLjE2LS4yMTUuMzU0LS4zOC41ODQtLjQ5Ny4yMy0uMTE2LjQ5OC0uMTc1LjgwNC0uMTc1LjMxMyAwIC41OS4wNjIuODMzLjE4NC4yNDIuMTIuNDQ2LjI5MS42MTEuNTE1LjE2Ni4yMi4yOS40ODguMzczLjguMDgyLjMxLjEyNC42NTUuMTI0IDEuMDM1Wm0tMS4xMDguMDk2VjQ5LjNjMC0uMjMtLjAyMi0uNDQzLS4wNjUtLjY0LS4wNDMtLjE5OC0uMTEtLjM3My0uMjAyLS41MjNhLjk5Ljk5IDAgMCAwLS4zNTQtLjM1Ljk2NC45NjQgMCAwIDAtLjUxLS4xMjljLS4xOTcgMC0uMzY1LjAzNC0uNTA2LjEwMmEuOTcuOTcgMCAwIDAtLjM1NC4yNyAxLjI4MiAxLjI4MiAwIDAgMC0uMjIuNDFjLS4wNTMuMTUzLS4wOS4zMi0uMTExLjUwMXYuODkyYy4wMzcuMjIuMS40MjMuMTg4LjYwNy4wOS4xODQuMjE1LjMzMS4zNzcuNDQyLjE2Ni4xMDcuMzc3LjE2LjYzNS4xNi4xOTkgMCAuMzctLjA0My41MS0uMTI4LjE0MS0uMDg2LjI1Ni0uMjA0LjM0NS0uMzU0LjA5Mi0uMTU0LjE2LS4zMy4yMDItLjUzLjA0My0uMTk4LjA2NS0uNDEuMDY1LS42MzRabTUuMjgxIDEuNDA3di02LjAzMmgxLjExM3Y3LjA2MmgtMS4wMDdsLS4xMDYtMS4wM1ptLTMuMjM3LTEuNDAydi0uMDk3YzAtLjM3Ny4wNDUtLjcyLjEzNC0xLjAzLjA4OC0uMzEyLjIxNy0uNTguMzg2LS44MDQuMTY4LS4yMjcuMzc0LS40LjYxNi0uNTIuMjQyLS4xMjIuNTE1LS4xODQuODE4LS4xODQuMyAwIC41NjQuMDU5Ljc5MS4xNzUuMjI3LjExNi40Mi4yODQuNTguNTAxYTIuNiAyLjYgMCAwIDEgLjM4MS43NzNjLjA5NS4yOTcuMTYzLjYyOC4yMDMuOTkzdi4zMDhjLS4wNC4zNTUtLjEwOC42OC0uMjAzLjk3NWEyLjYyIDIuNjIgMCAwIDEtLjM4MS43NjNjLS4xNi4yMTUtLjM1NC4zOC0uNTg0LjQ5N2ExLjcxOSAxLjcxOSAwIDAgMS0uNzk2LjE3NGMtLjMgMC0uNTcyLS4wNjMtLjgxNC0uMTg4YTEuNzk5IDEuNzk5IDAgMCAxLS42MTEtLjUyOSAyLjQ4OSAyLjQ4OSAwIDAgMS0uMzg2LS44IDMuNjM2IDMuNjM2IDAgMCAxLS4xMzQtMS4wMDdabTEuMTA4LS4wOTd2LjA5N2MwIC4yMjcuMDIuNDM4LjA2LjYzNC4wNDMuMTk3LjEwOS4zNy4xOTguNTIuMDg5LjE0Ny4yMDQuMjY0LjM0NS4zNS4xNDQuMDgyLjMxNi4xMjQuNTE1LjEyNC4yNTEgMCAuNDU4LS4wNTYuNjItLjE2Ni4xNjMtLjExLjI5LS4yNTkuMzgyLS40NDYuMDk1LS4xOS4xNi0uNDAxLjE5My0uNjM0di0uODMzYTIuMDMgMi4wMyAwIDAgMC0uMTE1LS41MDUgMS4zNSAxLjM1IDAgMCAwLS4yMjUtLjQxLjk0OC45NDggMCAwIDAtLjM1NC0uMjc1IDEuMTEgMS4xMSAwIDAgMC0uNDkyLS4xMDIuOTczLjk3MyAwIDAgMC0uNTE1LjEzIDEuMDQzIDEuMDQzIDAgMCAwLS4zNS4zNTNjLS4wODguMTUtLjE1NC4zMjUtLjE5Ny41MjQtLjA0My4yLS4wNjUuNDEzLS4wNjUuNjRabTcuNDAyIDEuNTMydi0yLjM3M2MwLS4xNzgtLjAzMy0uMzMxLS4wOTctLjQ2YS42NzcuNjc3IDAgMCAwLS4yOTQtLjI5OSAxLjAwNyAxLjAwNyAwIDAgMC0uNDg4LS4xMDVjLS4xOCAwLS4zMzcuMDMtLjQ2OS4wOTJhLjc1OS43NTkgMCAwIDAtLjMwOC4yNDguNi42IDAgMCAwLS4xMS4zNTRoLTEuMTAzYzAtLjE5Ni4wNDctLjM4Ni4xNDItLjU3LjA5NS0uMTg0LjIzMy0uMzQ4LjQxNC0uNDkyLjE4LS4xNDQuMzk3LS4yNTguNjQ4LS4zNC4yNTItLjA4My41MzQtLjEyNS44NDYtLjEyNS4zNzQgMCAuNzA1LjA2My45OTMuMTg5LjI5MS4xMjYuNTIuMzE2LjY4NS41Ny4xNjkuMjUxLjI1My41NjcuMjUzLjk0N3YyLjIxMmMwIC4yMjcuMDE2LjQzLjA0Ni42MTEuMDM0LjE3OC4wODEuMzMzLjE0My40NjV2LjA3M2gtMS4xMzZhMS45NiAxLjk2IDAgMCAxLS4xMjQtLjQ1NSAzLjcxNSAzLjcxNSAwIDAgMS0uMDQxLS41NDJabS4xNi0yLjAyOC4wMS42ODVoLS43OTZjLS4yMDUgMC0uMzg2LjAyLS41NDIuMDZhMS4xMTYgMS4xMTYgMCAwIDAtLjM5MS4xNjUuNzIuNzIgMCAwIDAtLjMxMy42MjFjMCAuMTMyLjAzLjI1My4wOTIuMzYzLjA2MS4xMDguMTUuMTkyLjI2Ny4yNTMuMTIuMDYxLjI2My4wOTIuNDMyLjA5Mi4yMjcgMCAuNDI1LS4wNDYuNTkzLS4xMzguMTcyLS4wOTUuMzA3LS4yMS40MDUtLjM0NWEuNzUuNzUgMCAwIDAgLjE1Ni0uMzlsLjM1OS40OTFjLS4wMzcuMTI2LS4xLjI2MS0uMTg5LjQwNWExLjk2MSAxLjk2MSAwIDAgMS0uMzUuNDE0IDEuNzM2IDEuNzM2IDAgMCAxLTEuMTkuNDQxYy0uMzI1IDAtLjYxNS0uMDY0LS44NjktLjE5M2ExLjU0NiAxLjU0NiAwIDAgMS0uNTk4LS41MjkgMS4zNzIgMS4zNzIgMCAwIDEtLjIxNi0uNzU4YzAtLjI2NC4wNS0uNDk3LjE0Ny0uNy4xMDEtLjIwNC4yNDktLjM3Ni40NDItLjUxNC4xOTYtLjEzOC40MzUtLjI0Mi43MTctLjMxMy4yODItLjA3My42MDQtLjExLjk2Ni0uMTFoLjg2OVptNC41ODgtMS45NXYuODFoLTIuODA1di0uODFoMi44MDVabS0xLjk5Ni0xLjIxOGgxLjEwOHY0LjgxOWMwIC4xNTMuMDIyLjI3LjA2NS4zNTQuMDQ2LjA4LjEwOS4xMzMuMTg4LjE2YS44Ni44NiAwIDAgMCAuMjgxLjA0MiAyLjIxIDIuMjEgMCAwIDAgLjM5MS0uMDQxbC4wMDQuODQ2YTIuODIgMi44MiAwIDAgMS0uMzIyLjA3MyAyLjM2MiAyLjM2MiAwIDAgMS0uNDE0LjAzMmMtLjI1NCAwLS40NzktLjA0NC0uNjc1LS4xMzNhLjk5Ljk5IDAgMCAxLS40Ni0uNDQ2Yy0uMTExLS4yMDUtLjE2Ni0uNDc4LS4xNjYtLjgxOFY0NS42NFptNS4yNCA2LjI4NWMtLjM2NyAwLS43LS4wNi0uOTk3LS4xNzlhMi4xOTMgMi4xOTMgMCAwIDEtLjc1NC0uNTEgMi4yNDkgMi4yNDkgMCAwIDEtLjQ3NC0uNzY4IDIuNjk3IDIuNjk3IDAgMCAxLS4xNjYtLjk1MnYtLjE4NGMwLS4zOS4wNTctLjc0Mi4xNzEtMS4wNTcuMTEzLS4zMTYuMjcxLS41ODYuNDczLS44MS4yMDMtLjIyNy40NDItLjQuNzE3LS41Mi4yNzYtLjExOS41NzUtLjE3OS44OTctLjE3OS4zNTYgMCAuNjY3LjA2LjkzMy4xOC4yNjcuMTIuNDg4LjI4OC42NjMuNTA1LjE3Ny4yMTUuMzA5LjQ3MS4zOTUuNzY4LjA4OS4yOTguMTMzLjYyNi4xMzMuOTg0di40NzRoLTMuODQ0di0uNzk2aDIuNzV2LS4wODdhMS41NDQgMS41NDQgMCAwIDAtLjEyLS41Ni45NS45NSAwIDAgMC0uMzI2LS40MjRjLS4xNDctLjEwNy0uMzQzLS4xNi0uNTg5LS4xNmEuOTk4Ljk5OCAwIDAgMC0uNDkxLjExOS45NzUuOTc1IDAgMCAwLS4zNTUuMzM1IDEuNzY0IDEuNzY0IDAgMCAwLS4yMi41MzQgMi45OTkgMi45OTkgMCAwIDAtLjA3NC42OTR2LjE4NGMwIC4yMTguMDI5LjQyLjA4OC42MDcuMDYxLjE4NC4xNS4zNDUuMjY2LjQ4My4xMTcuMTM4LjI1OC4yNDYuNDIzLjMyNi4xNjYuMDc3LjM1NC4xMTUuNTY2LjExNS4yNjYgMCAuNTA0LS4wNTQuNzEyLS4xNi4yMDktLjEwOC4zOS0uMjYuNTQzLS40NTZsLjU4NC41NjZhMi4yODcgMi4yODcgMCAwIDEtLjQxOC40NSAyLjEwMyAyLjEwMyAwIDAgMS0uNjMuMzQ1IDIuNTEzIDIuNTEzIDAgMCAxLS44NTYuMTMzWm04LjU1NC02LjgxdjYuNzE4aC0xLjEwOHYtNS40MDJsLTEuNjQxLjU1NnYtLjkxNWwyLjYxNi0uOTU2aC4xMzNabTUuNjIyIDUuNjg4di02LjAzMmgxLjExM3Y3LjA2MmgtMS4wMDdsLS4xMDYtMS4wM1ptLTMuMjM3LTEuNDAydi0uMDk3YzAtLjM3Ny4wNDQtLjcyLjEzMy0xLjAzYTIuNDIgMi40MiAwIDAgMSAuMzg3LS44MDRjLjE2OC0uMjI3LjM3NC0uNC42MTYtLjUyLjI0Mi0uMTIyLjUxNS0uMTg0LjgxOC0uMTg0LjMwMSAwIC41NjQuMDU5Ljc5MS4xNzUuMjI3LjExNi40Mi4yODQuNTc5LjUwMS4xNi4yMTUuMjg3LjQ3Mi4zODIuNzczLjA5NS4yOTcuMTYyLjYyOC4yMDIuOTkzdi4zMDhjLS4wNC4zNTUtLjEwNy42OC0uMjAyLjk3NWEyLjYwMyAyLjYwMyAwIDAgMS0uMzgyLjc2M2MtLjE1OS4yMTUtLjM1NC4zOC0uNTg0LjQ5N2ExLjcxNiAxLjcxNiAwIDAgMS0uNzk1LjE3NGMtLjMgMC0uNTcyLS4wNjMtLjgxNC0uMTg4YTEuNzk0IDEuNzk0IDAgMCAxLS42MTEtLjUyOSAyLjQ4NCAyLjQ4NCAwIDAgMS0uMzg3LS44IDMuNjMyIDMuNjMyIDAgMCAxLS4xMzMtMS4wMDdabTEuMTA4LS4wOTd2LjA5N2MwIC4yMjcuMDIuNDM4LjA2LjYzNC4wNDMuMTk3LjEwOS4zNy4xOTguNTIuMDg5LjE0Ny4yMDMuMjY0LjM0NC4zNS4xNDUuMDgyLjMxNi4xMjQuNTE1LjEyNC4yNTIgMCAuNDU5LS4wNTYuNjIxLS4xNjYuMTYzLS4xMS4yOS0uMjU5LjM4Mi0uNDQ2YTIuMDggMi4wOCAwIDAgMCAuMTkzLS42MzR2LS44MzNhMi4wMjUgMi4wMjUgMCAwIDAtLjExNS0uNTA1IDEuMzUgMS4zNSAwIDAgMC0uMjI1LS40MS45NDguOTQ4IDAgMCAwLS4zNTQtLjI3NSAxLjExIDEuMTEgMCAwIDAtLjQ5Mi0uMTAyLjk3MS45NzEgMCAwIDAtLjUxNS4xMyAxLjA0NiAxLjA0NiAwIDAgMC0uMzUuMzUzYy0uMDg5LjE1LS4xNTUuMzI1LS4xOTcuNTI0LS4wNDMuMi0uMDY1LjQxMy0uMDY1LjY0Wm05Ljk0MyAxLjUzMnYtMi4zNzNjMC0uMTc4LS4wMzMtLjMzMS0uMDk3LS40NmEuNjc4LjY3OCAwIDAgMC0uMjk0LS4yOTkgMS4wMSAxLjAxIDAgMCAwLS40ODgtLjEwNWMtLjE4MSAwLS4zMzcuMDMtLjQ2OS4wOTJhLjc2Ljc2IDAgMCAwLS4zMDguMjQ4LjYwMy42MDMgMCAwIDAtLjExLjM1NGgtMS4xMDRjMC0uMTk2LjA0OC0uMzg2LjE0My0uNTcuMDk1LS4xODQuMjMzLS4zNDguNDE0LS40OTJhMi4wNyAyLjA3IDAgMCAxIC42NDgtLjM0Yy4yNTEtLjA4My41MzMtLjEyNS44NDYtLjEyNS4zNzQgMCAuNzA1LjA2My45OTMuMTg5LjI5MS4xMjYuNTIuMzE2LjY4NS41Ny4xNjkuMjUxLjI1My41NjcuMjUzLjk0N3YyLjIxMmMwIC4yMjcuMDE2LjQzLjA0Ni42MTEuMDM0LjE3OC4wODEuMzMzLjE0My40NjV2LjA3M2gtMS4xMzZhMS45ODYgMS45ODYgMCAwIDEtLjEyNC0uNDU1IDMuNjU5IDMuNjU5IDAgMCAxLS4wNDEtLjU0MlptLjE2LTIuMDI4LjAxLjY4NWgtLjc5NmMtLjIwNSAwLS4zODYuMDItLjU0Mi4wNmExLjExIDEuMTEgMCAwIDAtLjM5MS4xNjUuNzIyLjcyMiAwIDAgMC0uMjM1LjI2Ny43ODIuNzgyIDAgMCAwLS4wNzguMzU0YzAgLjEzMi4wMzEuMjUzLjA5Mi4zNjNhLjY0LjY0IDAgMCAwIC4yNjcuMjUzLjkzNC45MzQgMCAwIDAgLjQzMi4wOTIgMS4yMTcgMS4yMTcgMCAwIDAgLjk5OC0uNDgzLjc1Ljc1IDAgMCAwIC4xNTYtLjM5bC4zNTkuNDkxYy0uMDM3LjEyNi0uMS4yNjEtLjE4OS40MDVhMS45NjIgMS45NjIgMCAwIDEtLjM0OS40MTQgMS43MyAxLjczIDAgMCAxLTEuMTkxLjQ0MWMtLjMyNSAwLS42MTUtLjA2NC0uODY5LS4xOTNhMS41NDIgMS41NDIgMCAwIDEtLjU5OC0uNTI5IDEuMzcyIDEuMzcyIDAgMCAxLS4yMTYtLjc1OGMwLS4yNjQuMDQ5LS40OTcuMTQ3LS43LjEwMS0uMjA0LjI0OC0uMzc2LjQ0Mi0uNTE0LjE5Ni0uMTM4LjQzNS0uMjQyLjcxNy0uMzEzLjI4Mi0uMDczLjYwNC0uMTEuOTY1LS4xMWguODY5Wm01LjQ5NC0xLjk1aDEuMDA2djQuODM3YzAgLjQ0OC0uMDk1LjgyOC0uMjg1IDEuMTQtLjE5LjMxMy0uNDU1LjU1LS43OTUuNzEzYTIuNzcxIDIuNzcxIDAgMCAxLTIuNDg4LS4xMDEgMS42OCAxLjY4IDAgMCAxLS41MzgtLjQ3M2wuNTItLjY1M2MuMTc4LjIxMS4zNzQuMzY2LjU4OS40NjQuMjE0LjA5OC40MzkuMTQ3LjY3NS4xNDcuMjU1IDAgLjQ3MS0uMDQ3LjY0OS0uMTQyYS45NjEuOTYxIDAgMCAwIC40MTgtLjQxYy4wOTgtLjE4LjE0Ny0uNDAxLjE0Ny0uNjYydi0zLjczM2wuMTAyLTEuMTI3Wm0tMy4zOCAyLjU0M3YtLjA5N2MwLS4zNzcuMDQ2LS43Mi4xMzgtMS4wM2EyLjQ1IDIuNDUgMCAwIDEgLjM5NS0uODA0Yy4xNzItLjIyNy4zOC0uNC42MjYtLjUyLjI0NS0uMTIyLjUyMi0uMTg0LjgzMi0uMTg0LjMyMiAwIC41OTYuMDU5LjgyMy4xNzUuMjMuMTE2LjQyMS4yODQuNTc1LjUwMS4xNTMuMjE1LjI3My40NzIuMzU4Ljc3My4wODkuMjk3LjE1NS42MjguMTk4Ljk5M3YuMzA4Yy0uMDQuMzU1LS4xMDcuNjgtLjIwMi45NzVhMi42OTcgMi42OTcgMCAwIDEtLjM3Ny43NjNjLS4xNTcuMjE1LS4zNS4zOC0uNTguNDk3YTEuNzM3IDEuNzM3IDAgMCAxLS44MDQuMTc0Yy0uMzA0IDAtLjU3OC0uMDYzLS44MjMtLjE4OGExLjg4IDEuODggMCAwIDEtLjYyNi0uNTI5IDIuNTE3IDIuNTE3IDAgMCAxLS4zOTUtLjggMy41MiAzLjUyIDAgMCAxLS4xMzgtMS4wMDdabTEuMTA4LS4wOTd2LjA5N2MwIC4yMjcuMDIyLjQzOC4wNjQuNjM0LjA0Ni4xOTcuMTE1LjM3LjIwNy41Mi4wOTUuMTQ3LjIxNS4yNjQuMzU5LjM1LjE0Ny4wODIuMzIuMTI0LjUyLjEyNC4yNiAwIC40NzMtLjA1Ni42MzktLjE2Ni4xNjgtLjExLjI5Ny0uMjU5LjM4Ni0uNDQ2LjA5Mi0uMTkuMTU2LS40MDEuMTkzLS42MzR2LS44MzNhMi4wMjUgMi4wMjUgMCAwIDAtLjExNS0uNTA1IDEuMzUgMS4zNSAwIDAgMC0uMjI1LS40MS45NDIuOTQyIDAgMCAwLS4zNTktLjI3NSAxLjE5MyAxLjE5MyAwIDAgMC0uNTEtLjEwMmMtLjE5OSAwLS4zNzMuMDQzLS41Mi4xM2ExLjA1NSAxLjA1NSAwIDAgMC0uMzYzLjM1MyAxLjc1IDEuNzUgMCAwIDAtLjIwNy41MjRjLS4wNDYuMi0uMDY5LjQxMy0uMDY5LjY0Wm00LjQxMy4wOTd2LS4xMDZjMC0uMzU4LjA1Mi0uNjkxLjE1Ni0uOTk4LjEwNC0uMzEuMjU0LS41NzcuNDUxLS44MDRhMi4wMyAyLjAzIDAgMCAxIC43MjYtLjUzNGMuMjg4LS4xMjguNjEzLS4xOTMuOTc1LS4xOTMuMzY1IDAgLjY5LjA2NS45NzUuMTk0LjI4OC4xMjUuNTMxLjMwMy43MzEuNTMzLjE5OS4yMjcuMzUxLjQ5NS40NTUuODA0LjEwNC4zMDcuMTU2LjY0LjE1Ni45OTh2LjEwNmMwIC4zNTktLjA1Mi42OTEtLjE1Ni45OThhMi4zODcgMi4zODcgMCAwIDEtLjQ1NS44MDQgMi4xMiAyLjEyIDAgMCAxLS43MjcuNTM0IDIuMzgxIDIuMzgxIDAgMCAxLS45Ny4xODhjLS4zNjUgMC0uNjkxLS4wNjMtLjk3OS0uMTg4YTIuMTEgMi4xMSAwIDAgMS0uNzI3LS41MzQgMi4zODcgMi4zODcgMCAwIDEtLjQ1NS0uODA0IDMuMDg2IDMuMDg2IDAgMCAxLS4xNTYtLjk5OFptMS4xMDgtLjEwNnYuMTA2YzAgLjIyNC4wMjMuNDM1LjA2OS42MzQuMDQ2LjIuMTE4LjM3NS4yMTYuNTI1LjA5OC4xNS4yMjQuMjY4LjM3Ny4zNTQuMTUzLjA4NS4zMzYuMTI4LjU0Ny4xMjguMjA1IDAgLjM4My0uMDQzLjUzMy0uMTI4LjE1NC0uMDg2LjI3OS0uMjA0LjM3Ny0uMzU0LjA5OC0uMTUuMTcxLS4zMjUuMjE3LS41MjUuMDQ5LS4xOTkuMDczLS40MS4wNzMtLjYzNHYtLjEwNmMwLS4yMi0uMDI0LS40MjktLjA3My0uNjI1LS4wNDYtLjItLjEyLS4zNzYtLjIyMS0uNTI5YTEuMDU3IDEuMDU3IDAgMCAwLS45MTUtLjQ5MmMtLjIwOSAwLS4zODkuMDQ1LS41NDMuMTMzLS4xNS4wODYtLjI3NC4yMDYtLjM3Mi4zNi0uMDk4LjE1Mi0uMTcuMzI5LS4yMTYuNTI4YTIuNzMzIDIuNzMzIDAgMCAwLS4wNjkuNjI1WiIvPjxwYXRoIGZpbGw9IiNGRkE2MDAiIGQ9Ik00NS43OSA4Ny44NTl2MTkuOTc0aC0zLjI5NFY5MS43NjlsLTQuODggMS42NTR2LTIuNzJsNy43NzktMi44NDRoLjM5NlptMTIuODggMHYxOS45NzRoLTMuMjk1VjkxLjc2OWwtNC44OCAxLjY1NHYtMi43Mmw3Ljc3OC0yLjg0NGguMzk3Wm0xOC4xMjkgOC4zNTN2My4yNTRjMCAxLjU1OS0uMTU1IDIuODg5LS40NjUgMy45OTItLjMgMS4wOTQtLjczOCAxLjk4My0xLjMxMyAyLjY2NmE1LjE0NyA1LjE0NyAwIDAgMS0yLjA2NCAxLjUwNGMtLjc5My4zMTktMS42ODIuNDc5LTIuNjY2LjQ3OWE3LjY2MyA3LjY2MyAwIDAgMS0yLjE4OC0uMzAxIDUuMzU4IDUuMzU4IDAgMCAxLTEuODA0LS45NDNjLS41MzgtLjQyOS0uOTk4LS45OC0xLjM4MS0xLjY1NS0uMzc0LS42ODMtLjY2Ni0xLjQ5OS0uODc1LTIuNDQ3LS4yLS45NDgtLjMtMi4wNDYtLjMtMy4yOTV2LTMuMjU0YzAtMS41NjguMTU0LTIuODkuNDY0LTMuOTY1LjMxLTEuMDg0Ljc1Mi0xLjk2NCAxLjMyNi0yLjYzOGE1LjA1MSA1LjA1MSAwIDAgMSAyLjA1LTEuNDljLjgwMy0uMzEgMS42OTYtLjQ2NSAyLjY4LS40NjUuNzk0IDAgMS41MjMuMSAyLjE4OC4zYTUuMTIzIDUuMTIzIDAgMCAxIDEuODA1LjkxNmMuNTM3LjQyLjk5My45NjcgMS4zNjcgMS42NDEuMzgzLjY2NS42NzQgMS40NzIuODc1IDIuNDIuMi45MzkuMyAyLjAzMi4zIDMuMjgxWm0tMy4yOTUgMy43MTlWOTUuNzJjMC0uNzkzLS4wNDYtMS40OS0uMTM3LTIuMDkyLS4wOTEtLjYxLS4yMjgtMS4xMjUtLjQxLTEuNTQ1LS4xNzMtLjQyOC0uMzkyLS43NzQtLjY1Ni0xLjAzOWEyLjMwOSAyLjMwOSAwIDAgMC0uOTAzLS41ODggMy4xODcgMy4xODcgMCAwIDAtMS4xMzQtLjE5MWMtLjUxIDAtLjk2Ny4xLTEuMzY4LjMtLjQuMTkyLS43MzguNTAyLTEuMDExLjkzLS4yNzQuNDI5LS40ODMuOTk0LS42MyAxLjY5Ni0uMTM2LjY5Mi0uMjA0IDEuNTM1LS4yMDQgMi41Mjl2NC4yMTFjMCAuODAyLjA0NSAxLjUwOC4xMzYgMi4xMTkuMDkyLjYxMS4yMjggMS4xMzUuNDEgMS41NzIuMTgzLjQyOS40MDIuNzg0LjY1NyAxLjA2Ny4yNjQuMjczLjU2NS40NzQuOTAyLjYwMWEzLjI1IDMuMjUgMCAwIDAgMS4xMzUuMTkyYy41MiAwIC45OC0uMTAxIDEuMzgtLjMwMS40MDItLjIwMS43NC0uNTIgMS4wMTItLjk1Ny4yNzQtLjQ0Ny40NzktMS4wMjUuNjE2LTEuNzM2LjEzNi0uNzExLjIwNS0xLjU2NC4yMDUtMi41NTdabTIzLjE2LTYuODloMy4zMDl2MTQuNzkyaC0zLjA1bC0uMjU5LTMuNDE4VjkzLjA0Wm0uNDc4IDcuOTE1IDEuMy0uMDI3YzAgMS4wMzktLjEgMS45OTYtLjMwMSAyLjg3MS0uMTkyLjg3NS0uNDkzIDEuNjM2LS45MDMgMi4yODNhNC4zMTMgNC4zMTMgMCAwIDEtMS41MzEgMS41MDRjLS42Mi4zNTYtMS4zNTQuNTMzLTIuMjAxLjUzMy0uNjc1IDAtMS4yOC0uMDk1LTEuODE5LS4yODdhMy42MDcgMy42MDcgMCAwIDEtMS4zOTQtLjkyOWMtLjM5Mi0uNDItLjcxNi0uOTY3LS45Ny0xLjY0MWwtLjQyNS0zLjczMmgxLjg3M2MwIC44Mi4wNjkgMS40OS4yMDYgMi4wMDkuMTM2LjUyLjMyOC45MjEuNTc0IDEuMjAzLjI0Ni4yNzQuNTMzLjQ2NS44NjEuNTc1LjMzNy4xMDkuNzAyLjE2NCAxLjA5NC4xNjQuNzIgMCAxLjMxMi0uMTEgMS43NzctLjMyOGEyLjY4NSAyLjY4NSAwIDAgMCAxLjEwOC0uOTQ0Yy4yNzMtLjQxLjQ2NC0uODg4LjU3NC0xLjQzNWE4LjcyOCA4LjcyOCAwIDAgMCAuMTc3LTEuODE5Wk04Ny40OSA5My4wNGgzLjI4MXYyMC40ODFoLTMuMjh2LTIwLjQ4Wm0yNS44MTMgMGgyLjk5NHYxNC4zODNjMCAxLjMzMS0uMjgzIDIuNDYxLS44NDggMy4zOTFhNS40NSA1LjQ1IDAgMCAxLTIuMzY1IDIuMTE5Yy0xLjAxMi40OTItMi4xODMuNzM4LTMuNTE0LjczOGE4LjI0OCA4LjI0OCAwIDAgMS0xLjg4Ni0uMjQ2IDcuNzI3IDcuNzI3IDAgMCAxLTEuOTk3LS43OTMgNC45NCA0Ljk0IDAgMCAxLTEuNTk5LTEuNDA4bDEuNTQ1LTEuOTQxYy41MjguNjI4IDEuMTEyIDEuMDg5IDEuNzUgMS4zOGE0Ljc3NSA0Ljc3NSAwIDAgMCAyLjAxLjQzOGMuNzU2IDAgMS4zOTktLjE0MSAxLjkyNy0uNDI0YTIuODU1IDIuODU1IDAgMCAwIDEuMjQ0LTEuMjE3Yy4yOTItLjUzNy40MzgtMS4xOTQuNDM4LTEuOTY4Vjk2LjM5bC4zMDEtMy4zNVptLTEwLjA0OSA3LjU2MXYtLjI4N2MwLTEuMTIxLjEzNy0yLjE0Mi40MS0zLjA2My4yNzMtLjkzLjY2NS0xLjcyNyAxLjE3Ni0yLjM5MmE1LjEyNiA1LjEyNiAwIDAgMSAxLjg1OS0xLjU0NWMuNzI5LS4zNjUgMS41NTQtLjU0NyAyLjQ3NS0uNTQ3Ljk1NyAwIDEuNzczLjE3MyAyLjQ0Ny41MmE0LjYyMiA0LjYyMiAwIDAgMSAxLjcwOSAxLjQ5Yy40NTYuNjM4LjgxMSAxLjQwMyAxLjA2NiAyLjI5Ni4yNjUuODg1LjQ2MSAxLjg2OS41ODggMi45NTR2LjkxNmExNC43OTggMTQuNzk4IDAgMCAxLS42MDEgMi44OThjLS4yODMuODc1LS42NTcgMS42MzItMS4xMjEgMi4yN2E0Ljc3OSA0Ljc3OSAwIDAgMS0xLjcyMyAxLjQ3NmMtLjY3NC4zNDctMS40NzIuNTItMi4zOTMuNTItLjkwMiAwLTEuNzE4LS4xODctMi40NDctLjU2MWE1LjU2IDUuNTYgMCAwIDEtMS44NTktMS41NzJjLS41MTEtLjY3NS0uOTAzLTEuNDY4LTEuMTc2LTIuMzc5YTEwLjQ5NCAxMC40OTQgMCAwIDEtLjQxLTIuOTk0Wm0zLjI5NS0uMjg3di4yODdjMCAuNjc0LjA2NCAxLjMwMy4xOTEgMS44ODcuMTM3LjU4My4zNDIgMS4wOTguNjE1IDEuNTQ1LjI4My40MzcuNjM4Ljc4MyAxLjA2NyAxLjAzOS40MzcuMjQ2Ljk1Mi4zNjkgMS41NDUuMzY5Ljc3NCAwIDEuNDA4LS4xNjQgMS45LS40OTJhMy4yMDIgMy4yMDIgMCAwIDAgMS4xNDktMS4zMjcgNi43NDYgNi43NDYgMCAwIDAgLjU3NC0xLjg4NnYtMi40NzVhNi4wNjUgNi4wNjUgMCAwIDAtLjM0Mi0xLjUwNCA0LjAxNCA0LjAxNCAwIDAgMC0uNjctMS4yMTcgMi44MDIgMi44MDIgMCAwIDAtMS4wNjYtLjgyYy0uNDI5LS4yLS45MzUtLjMtMS41MTgtLjMtLjU5MiAwLTEuMTA3LjEyNy0xLjU0NS4zODJhMy4xNDYgMy4xNDYgMCAwIDAtMS4wOCAxLjA1MyA1LjIyMyA1LjIyMyAwIDAgMC0uNjE1IDEuNTU4IDguNDIgOC40MiAwIDAgMC0uMjA1IDEuOTAxWm0yMS44Mi0xMi4zODctNy43NTIgMjEuNjE1aC0yLjU4NGw3Ljc2Ni0yMS42MTVoMi41N1ptNS41NzggOC4xMjF2MTEuNzg1aC0zLjI5NVY5My4wNGgzLjEwNGwuMTkxIDMuMDA4Wm0tLjUzMyAzLjg0Mi0xLjEyMS0uMDE0YzAtMS4wMi4xMjctMS45NjQuMzgzLTIuODMuMjU1LS44NjYuNjI5LTEuNjE4IDEuMTIxLTIuMjU2YTQuOTI4IDQuOTI4IDAgMCAxIDEuODMyLTEuNDljLjczOC0uMzU1IDEuNTktLjUzMyAyLjU1Ni0uNTMzLjY3NSAwIDEuMjkuMSAxLjg0Ni4zYTMuNjU4IDMuNjU4IDAgMCAxIDEuNDYzLjkxN2MuNDE5LjQxOS43MzguOTU3Ljk1NyAxLjYxMy4yMjguNjU2LjM0MiAxLjQ1LjM0MiAyLjM3OXY5Ljg1N2gtMy4yOTV2LTkuNTdjMC0uNzItLjEwOS0xLjI4NS0uMzI4LTEuNjk1YTEuODIgMS44MiAwIDAgMC0uOTE2LS44NzVjLS4zOTItLjE4My0uODYxLS4yNzQtMS40MDgtLjI3NC0uNjIgMC0xLjE0OS4xMTktMS41ODYuMzU2LS40MjkuMjM3LS43OC41Ni0xLjA1My45Ny0uMjczLjQxLS40NzQuODg0LS42MDIgMS40MjJhNy40NzkgNy40NzkgMCAwIDAtLjE5MSAxLjcyM1ptOS4xNzQtLjg3NS0xLjU0NS4zNDJjMC0uODk0LjEyMy0xLjczNy4zNjktMi41M2E2LjQ0MyA2LjQ0MyAwIDAgMSAxLjEwNy0yLjEwNSA1LjIwNyA1LjIwNyAwIDAgMSAxLjgxOS0xLjQzNmMuNzItLjM0NiAxLjU0NS0uNTIgMi40NzQtLjUyLjc1NyAwIDEuNDMxLjEwNiAyLjAyNC4zMTUuNjAxLjIgMS4xMTIuNTIgMS41MzEuOTU3LjQxOS40MzguNzM4IDEuMDA4Ljk1NyAxLjcxLjIxOS42OTIuMzI4IDEuNTMuMzI4IDIuNTE1djkuNTdoLTMuMzA4di05LjU4NGMwLS43NDctLjExLTEuMzI2LS4zMjgtMS43MzYtLjIxLS40MS0uNTExLS42OTMtLjkwMy0uODQ4LS4zOTItLjE2NC0uODYxLS4yNDYtMS40MDgtLjI0Ni0uNTEgMC0uOTYyLjA5Ni0xLjM1NC4yODdhMi42ODcgMi42ODcgMCAwIDAtLjk3Ljc4IDMuNDY1IDMuNDY1IDAgMCAwLS42MDIgMS4xMzQgNC44ODggNC44ODggMCAwIDAtLjE5MSAxLjM5NVptMTQuNDUxLTYuNjU4aDEuMTg5Yy4zOTIgMCAuNzE2LS4wNTUuOTcxLS4xNjQuMjU1LS4xMTkuNDQ3LS4yNzguNTc0LS40NzkuMTM3LS4yLjIwNS0uNDM3LjIwNS0uNzFhMS4xNiAxLjE2IDAgMCAwLS4zODItLjg2MmMtLjI0Ni0uMjU1LS42NjEtLjM4My0xLjI0NS0uMzgzLS40NDYgMC0uODI0LjA5Ni0xLjEzNC4yODctLjMwMS4xODItLjQ1MS40NDItLjQ1MS43OGgtMi40ODljMC0uNjQ4LjE4My0xLjIuNTQ3LTEuNjU1LjM2NS0uNDU2Ljg1Mi0uODAyIDEuNDYzLTEuMDM5YTUuNDc0IDUuNDc0IDAgMCAxIDEuOTk2LS4zNTVjLjgzIDAgMS41NTkuMTE4IDIuMTg4LjM1NS42MjkuMjI4IDEuMTE2LjU3IDEuNDYzIDEuMDI1LjM1NS40NTYuNTMzIDEuMDIxLjUzMyAxLjY5NiAwIC41Mi0uMTY0Ljk4LS40OTIgMS4zOC0uMzE5LjQwMi0uNzY2LjcxNi0xLjM0Ljk0NC0uNTY1LjIxOS0xLjIzMS4zMjgtMS45OTYuMzI4aC0xLjZ2LTEuMTQ4Wm0wIDEuNzA5di0xLjEyMWgxLjZjLjg0NyAwIDEuNTcyLjEgMi4xNzMuMy42MDIuMTkyIDEuMDU4LjQ4OCAxLjM2OC44OS4zMTkuNC40NzguOTIuNDc4IDEuNTU4IDAgLjY3NC0uMTkxIDEuMjQ4LS41NzQgMS43MjItLjM4My40NzQtLjkwNy44MzQtMS41NzIgMS4wOC0uNjU3LjI0Ni0xLjM5NS4zNy0yLjIxNS4zNy0uNjg0IDAtMS4zNDktLjExLTEuOTk2LS4zMjlhMy43MDYgMy43MDYgMCAwIDEtMS41ODYtMS4wNjZjLS40MS0uNDgzLS42MTUtMS4xMTctLjYxNS0xLjloMi41MDFjMCAuMzU1LjE2NS42Ni40OTMuOTE2LjMyOC4yNTUuNzcuMzgyIDEuMzI2LjM4Mi41OTIgMCAxLjAzLS4xMjcgMS4zMTItLjM4Mi4yOTItLjI2NS40MzgtLjU5My40MzgtLjk4NSAwLS4zNDYtLjA4Mi0uNjI0LS4yNDYtLjgzNGExLjI4IDEuMjggMCAwIDAtLjY3LS40NWMtLjI5Mi0uMTAxLS42MzQtLjE1MS0xLjAyNi0uMTUxaC0xLjE4OVoiLz48L3N2Zz4=", + "description": "A beneficial layer in the upper atmosphere, but harmful when present near ground level. Results mainly from vehicle exhaust and industrial emissions.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'ozone', label: 'Ozone', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Ozone\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"public\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#3FA71A\"},{\"from\":50,\"to\":100,\"color\":\"#80C32C\"},{\"from\":100,\"to\":130,\"color\":\"#FFA600\"},{\"from\":130,\"to\":240,\"color\":\"#F36900\"},{\"from\":240,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":28,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#3FA71A\"},{\"from\":50,\"to\":100,\"color\":\"#80C32C\"},{\"from\":100,\"to\":130,\"color\":\"#FFA600\"},{\"from\":130,\"to\":240,\"color\":\"#F36900\"},{\"from\":240,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Ozone \",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "enviroment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "O3", + "ozone" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/ozone__o3__card_with_background.json b/application/src/main/data/json/system/widget_types/ozone__o3__card_with_background.json new file mode 100644 index 0000000000..90531b66f7 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/ozone__o3__card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "ozone_o3_card_with_background", + "name": "Ozone (O3) card with background", + "deprecated": false, + "image": "tb-image:b3pvbmUtdmFsdWUtY2FyZC13aXRoLWJhY2tncm91bmQuc3Zn:b3pvbmUtdmFsdWUtY2FyZC13aXRoLWJhY2tncm91bmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuNzYiIHJ4PSI0Ii8+PC9nPjxwYXRoIGZpbGw9IiNGODlFMEQiIGQ9Ik0yNyAxNy41Yy02LjkgMC0xMi41IDUuNi0xMi41IDEyLjVTMjAuMSA0Mi41IDI3IDQyLjUgMzkuNSAzNi45IDM5LjUgMzAgMzMuOSAxNy41IDI3IDE3LjVaTTE3IDMwYzAtLjc2Mi4xLTEuNTEyLjI2Mi0yLjIyNWw1Ljk3NiA1Ljk3NVYzNWMwIDEuMzc1IDEuMTI1IDIuNSAyLjUgMi41djIuNDEzQzIwLjgyNSAzOS4yODggMTcgMzUuMDg3IDE3IDMwWm0xNy4zNjIgNi43NWMtLjMyNS0xLjAxMy0xLjI1LTEuNzUtMi4zNzUtMS43NWgtMS4yNXYtMy43NWMwLS42ODgtLjU2Mi0xLjI1LTEuMjUtMS4yNWgtNy41di0yLjVoMi41Yy42ODggMCAxLjI1LS41NjMgMS4yNS0xLjI1di0yLjVoMi41YzEuMzc2IDAgMi41LTEuMTI1IDIuNS0yLjV2LS41MTJDMzQuNCAyMi4yMTIgMzcgMjUuODEzIDM3IDMwYzAgMi42LTEuMDEzIDQuOTc1LTIuNjM4IDYuNzVaIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuODciIGQ9Ik01Ni45NDggMjMuMjV2LjU0N2MwIC43NTItLjA5OCAxLjQyNi0uMjk0IDIuMDIzYTQuMzY2IDQuMzY2IDAgMCAxLS44NCAxLjUyNWMtLjM2LjQxOS0uNzk0Ljc0LTEuMy45NjRhNC4xOTQgNC4xOTQgMCAwIDEtMS42ODEuMzI4Yy0uNjEgMC0xLjE2OS0uMTEtMS42NzUtLjMyOGEzLjc2MiAzLjc2MiAwIDAgMS0xLjMwNi0uOTY0IDQuNDc3IDQuNDc3IDAgMCAxLS44Ni0xLjUyNWMtLjIwMS0uNTk3LS4zMDItMS4yNzEtLjMwMi0yLjAyM3YtLjU0N2MwLS43NTIuMS0xLjQyNC4zMDEtMi4wMTcuMi0uNTk3LjQ4My0xLjEwNS44NDgtMS41MjQuMzY5LS40MjQuODA0LS43NDUgMS4zMDYtLjk2NGE0LjEwMiA0LjEwMiAwIDAgMSAxLjY3NC0uMzM1IDQuMTIgNC4xMiAwIDAgMSAxLjY4Mi4zMzVjLjUwNi4yMTkuOTQxLjU0IDEuMzA2Ljk2NC4zNjQuNDIuNjQ1LjkyNy44NCAxLjUyNC4yMDEuNTkzLjMwMSAxLjI2NS4zMDEgMi4wMTdabS0xLjcxNi41NDd2LS41NmMwLS41NTctLjA1NC0xLjA0Ny0uMTY0LTEuNDctLjEwNC0uNDI5LS4yNjItLjc4Ny0uNDcxLTEuMDc0YTEuOTcyIDEuOTcyIDAgMCAwLS43Ni0uNjU2Yy0uMy0uMTUtLjY0LS4yMjYtMS4wMTgtLjIyNnMtLjcxNS4wNzYtMS4wMTEuMjI2YTIuMDQgMi4wNCAwIDAgMC0uNzYuNjU2IDMuMTkgMy4xOSAwIDAgMC0uNDcgMS4wNzRjLS4xMS40MjMtLjE2NS45MTMtLjE2NSAxLjQ3di41NmMwIC41NTYuMDU1IDEuMDQ4LjE2NCAxLjQ3Ni4xMS40MjkuMjY5Ljc5MS40NzkgMS4wODcuMjE0LjI5Mi40Ny41MTMuNzY1LjY2M2EyLjI2IDIuMjYgMCAwIDAgMS4wMTIuMjJjLjM4MyAwIC43MjItLjA3NCAxLjAxOS0uMjIuMjk2LS4xNS41NDYtLjM3MS43NTItLjY2My4yMDUtLjI5Ni4zNi0uNjU4LjQ2NC0xLjA4Ny4xMS0uNDI4LjE2NC0uOTIuMTY0LTEuNDc2Wm05LjI0IDMuMzlWMjguNWgtNS41MXYtMS4zMTNoNS41MVptLS4xNS01LjA1OC00LjggNi4zNzFoLTEuMDY2di0xLjA2bDQuNzcxLTYuMzM3aDEuMDk0djEuMDI2Wm0tLjYzNy0xLjAyNnYxLjMxM2gtNS4xNDd2LTEuMzEzaDUuMTQ4Wm0xLjkzMiAzLjc4di0uMTU2YzAtLjUzNC4wNzgtMS4wMjguMjMzLTEuNDg0LjE1NS0uNDYuMzc4LS44NTkuNjctMS4xOTZhMy4wMjUgMy4wMjUgMCAwIDEgMS4wOC0uNzkzIDMuNTE2IDMuNTE2IDAgMCAxIDEuNDQ5LS4yODdjLjU0MiAwIDEuMDI1LjA5NiAxLjQ0OS4yODcuNDI4LjE4Ny43OS40NTEgMS4wODcuNzkzLjI5Ni4zMzcuNTIyLjczNi42NzcgMS4xOTYuMTU1LjQ1Ni4yMzIuOTUuMjMyIDEuNDg0di4xNTdjMCAuNTMzLS4wNzcgMS4wMjctLjIzMiAxLjQ4M2EzLjU1IDMuNTUgMCAwIDEtLjY3NyAxLjE5NyAzLjEzNyAzLjEzNyAwIDAgMS0xLjA4Ljc5MiAzLjU0IDMuNTQgMCAwIDEtMS40NDIuMjggMy42MDcgMy42MDcgMCAwIDEtMS40NTctLjI4IDMuMTM2IDMuMTM2IDAgMCAxLTEuMDgtLjc5MyAzLjU1IDMuNTUgMCAwIDEtLjY3Ni0xLjE5NiA0LjU4MiA0LjU4MiAwIDAgMS0uMjMzLTEuNDgzWm0xLjY0OC0uMTU2di4xNTdjMCAuMzMyLjAzNC42NDcuMTAyLjk0My4wNjkuMjk2LjE3Ni41NTYuMzIyLjc4LjE0NS4yMjMuMzMyLjM5OC41Ni41MjYuMjI4LjEyNy40OTkuMTkxLjgxNC4xOTEuMzA1IDAgLjU3LS4wNjQuNzkzLS4xOTEuMjI3LS4xMjguNDE0LS4zMDMuNTYtLjUyN2EyLjQgMi40IDAgMCAwIC4zMjEtLjc3OWMuMDczLS4yOTYuMTEtLjYxLjExLS45NDN2LS4xNTdjMC0uMzI5LS4wMzctLjYzOS0uMTEtLjkzYTIuMzkgMi4zOSAwIDAgMC0uMzI4LS43ODYgMS41NyAxLjU3IDAgMCAwLS41Ni0uNTM0IDEuNTQzIDEuNTQzIDAgMCAwLS44LS4xOThjLS4zMSAwLS41NzkuMDY2LS44MDcuMTk4YTEuNTg3IDEuNTg3IDAgMCAwLS41NTMuNTM0Yy0uMTQ2LjIyOC0uMjUzLjQ5LS4zMjIuNzg2YTQuMDYyIDQuMDYyIDAgMCAwLS4xMDIuOTNabTguNDk0LTIuMDQ0VjI4LjVINzQuMTF2LTcuMzk2aDEuNTUybC4wOTYgMS41NzlabS0uMjk0IDEuODQ1LS41MzMtLjAwNmE0LjgxMiA0LjgxMiAwIDAgMSAuMjE4LTEuNDQzIDMuNTUgMy41NSAwIDAgMSAuNjAyLTEuMTI4Yy4yNi0uMzE0LjU3LS41NTYuOTMtLjcyNGEyLjc0IDIuNzQgMCAwIDEgMS4yMDMtLjI2Yy4zNTUgMCAuNjc2LjA1Ljk2NC4xNS4yOTEuMDk2LjU0LjI1My43NDUuNDcyLjIxLjIxOS4zNjkuNTAzLjQ3OC44NTQuMTEuMzQ3LjE2NC43NzMuMTY0IDEuMjc5VjI4LjVoLTEuNjU0di00Ljc4NWMwLS4zNTYtLjA1Mi0uNjM2LS4xNTctLjg0MWEuODguODggMCAwIDAtLjQ0NC0uNDQ0Yy0uMTkyLS4wOTEtLjQzMS0uMTM3LS43MTgtLjEzNy0uMjgzIDAtLjUzNi4wNi0uNzYuMTc4LS4yMjIuMTE4LS40MTIuMjgtLjU2Ny40ODUtLjE1LjIwNS0uMjY2LjQ0Mi0uMzQ4LjcxMWEyLjkzOCAyLjkzOCAwIDAgMC0uMTIzLjg2MVptOS45NjQgNC4xMDlhMy45NDQgMy45NDQgMCAwIDEtMS40ODQtLjI2NyAzLjI3NSAzLjI3NSAwIDAgMS0xLjEyLS43NTkgMy4zNjQgMy4zNjQgMCAwIDEtLjcwNS0xLjE0MSAzLjk5OSAzLjk5OSAwIDAgMS0uMjQ2LTEuNDE1di0uMjc0YzAtLjU3OC4wODQtMS4xMDMuMjUzLTEuNTcyLjE2OS0uNDcuNDAzLS44Ny43MDQtMS4yMDMuMy0uMzM3LjY1Ni0uNTk1IDEuMDY2LS43NzMuNDEtLjE3Ny44NTUtLjI2NiAxLjMzNC0uMjY2LjUyOCAwIC45OS4wODkgMS4zODcuMjY2LjM5Ny4xNzguNzI1LjQyOS45ODUuNzUyLjI2NC4zMi40Ni43LjU4NyAxLjE0Mi4xMzMuNDQyLjE5OS45My4xOTkgMS40NjN2LjcwNGgtNS43MTVWMjQuMTFoNC4wODh2LS4xM2EyLjMxMiAyLjMxMiAwIDAgMC0uMTc4LS44MzQgMS40MTcgMS40MTcgMCAwIDAtLjQ4NS0uNjI4Yy0uMjItLjE2LS41MS0uMjQtLjg3NS0uMjQtLjI3NCAwLS41MTguMDYtLjczMi4xNzgtLjIxLjExNC0uMzg1LjI4LS41MjYuNS0uMTQxLjIxOC0uMjUuNDgyLS4zMjguNzkyLS4wNzMuMzA1LS4xMS42NS0uMTEgMS4wMzJ2LjI3NGMwIC4zMjMuMDQ0LjYyNC4xMy45MDIuMDkxLjI3My4yMjQuNTEzLjM5Ny43MTguMTczLjIwNS4zODIuMzY3LjYyOS40ODUuMjQ2LjExNC41MjYuMTcxLjg0LjE3MS4zOTcgMCAuNzUtLjA4IDEuMDYtLjI0LjMxLS4xNTkuNTc5LS4zODQuODA3LS42NzZsLjg2OC44NGMtLjE2LjIzMy0uMzY3LjQ1Ny0uNjIyLjY3LS4yNTUuMjEtLjU2OC4zODEtLjkzNy41MTRhMy43MjcgMy43MjcgMCAwIDEtMS4yNzEuMTk4WiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjM4IiBkPSJNNTIuODc0IDQxLjU4NXYuOTE1aC0zLjM2MXYtLjkxNWgzLjM2Wm0tMy4wNC01Ljc4VjQyLjVINDguNjh2LTYuNjk1aDEuMTU1Wm02LjgyMiA1LjY5N1YzOS4xM2MwLS4xNzgtLjAzMi0uMzMxLS4wOTYtLjQ2YS42NzYuNjc2IDAgMCAwLS4yOTQtLjI5OSAxLjAwNiAxLjAwNiAwIDAgMC0uNDg4LS4xMDZjLS4xOCAwLS4zMzcuMDMtLjQ2OS4wOTJhLjc1OC43NTggMCAwIDAtLjMwOC4yNDguNTk5LjU5OSAwIDAgMC0uMTEuMzU1aC0xLjEwNGMwLS4xOTcuMDQ4LS4zODcuMTQzLS41Ny4wOTUtLjE4NC4yMzMtLjM0OS40MTQtLjQ5My4xOC0uMTQ0LjM5Ny0uMjU3LjY0OC0uMzQuMjUxLS4wODMuNTMzLS4xMjQuODQ2LS4xMjQuMzc0IDAgLjcwNS4wNjMuOTkzLjE4OC4yOTEuMTI2LjUyLjMxNi42ODUuNTcuMTY5LjI1Mi4yNTMuNTY4LjI1My45NDh2Mi4yMTFjMCAuMjI3LjAxNi40MzEuMDQ2LjYxMi4wMzQuMTc4LjA4MS4zMzMuMTQzLjQ2NHYuMDc0aC0xLjEzNmExLjk2NSAxLjk2NSAwIDAgMS0uMTI0LS40NTUgMy43MyAzLjczIDAgMCAxLS4wNDEtLjU0M1ptLjE2MS0yLjAyOC4wMS42ODZoLS43OTZjLS4yMDUgMC0uMzg2LjAyLS41NDIuMDZhMS4xMTUgMS4xMTUgMCAwIDAtLjM5MS4xNjUuNzE4LjcxOCAwIDAgMC0uMzEzLjYyYzAgLjEzMy4wMy4yNTMuMDkyLjM2NC4wNjEuMTA3LjE1LjE5MS4yNjcuMjUzLjEyLjA2MS4yNjMuMDkyLjQzMi4wOTIuMjI3IDAgLjQyNS0uMDQ2LjU5My0uMTM4YTEuMjIgMS4yMiAwIDAgMCAuNDA1LS4zNDUuNzUuNzUgMCAwIDAgLjE1Ni0uMzlsLjM1OS40OTFjLS4wMzcuMTI2LS4xLjI2LS4xODkuNDA1YTEuOTYxIDEuOTYxIDAgMCAxLS4zNS40MTQgMS43MzUgMS43MzUgMCAwIDEtMS4xOS40NDFjLS4zMjUgMC0uNjE1LS4wNjQtLjg3LS4xOTNhMS41NDQgMS41NDQgMCAwIDEtLjU5Ny0uNTI5IDEuMzcxIDEuMzcxIDAgMCAxLS4yMTYtLjc1OWMwLS4yNjMuMDQ5LS40OTYuMTQ3LS42OThhMS4zNyAxLjM3IDAgMCAxIC40NDItLjUxNWMuMTk2LS4xMzguNDM1LS4yNDMuNzE3LS4zMTMuMjgyLS4wNzQuNjA0LS4xMS45NjUtLjExaC44N1ptNS4wOTggMS42NzljMC0uMTEtLjAyOC0uMjEtLjA4My0uMy0uMDU1LS4wOTEtLjE2LS4xNzQtLjMxNy0uMjQ3YTMuMDc4IDMuMDc4IDAgMCAwLS42OC0uMjAzIDUuODY0IDUuODY0IDAgMCAxLS43MjctLjIwNyAyLjMwNSAyLjMwNSAwIDAgMS0uNTYtLjI5OCAxLjI1NyAxLjI1NyAwIDAgMS0uMzY0LS40MTQgMS4xNDYgMS4xNDYgMCAwIDEtLjEyOS0uNTUyYzAtLjIwMi4wNDQtLjM5NC4xMzMtLjU3NS4wOS0uMTguMjE2LS4zNC4zODItLjQ3OC4xNjYtLjEzOC4zNjYtLjI0Ny42MDItLjMyNi4yNC0uMDguNTA2LS4xMi44LS4xMi40MTcgMCAuNzc0LjA3IDEuMDcyLjIxMi4zLjEzNy41My4zMjYuNjkuNTY1LjE1OS4yMzYuMjM5LjUwMy4yMzkuOGgtMS4xMDlhLjcwNy43MDcgMCAwIDAtLjEtLjM2OC43MDUuNzA1IDAgMCAwLS4yOTUtLjI4IDEuMDA4IDEuMDA4IDAgMCAwLS40OTctLjExYy0uMTkgMC0uMzQ4LjAzLS40NzMuMDkxYS42NDguNjQ4IDAgMCAwLS4yNzYuMjMuNTg3LjU4NyAwIDAgMC0uMDQxLjUzOC41My41MyAwIDAgMCAuMTY1LjE4Yy4wNzcuMDUyLjE4MS4xLjMxMy4xNDcuMTM1LjA0Ni4zMDMuMDkuNTA2LjEzMy4zOC4wOC43MDYuMTgzLjk3OS4zMDguMjc2LjEyMy40ODcuMjgyLjYzNC40NzguMTQ4LjE5NC4yMjEuNDM5LjIyMS43MzYgMCAuMjItLjA0Ny40MjMtLjE0Mi42MDctLjA5Mi4xOC0uMjI3LjMzOS0uNDA1LjQ3M2EyLjAzIDIuMDMgMCAwIDEtLjY0LjMwOWMtLjI0NC4wNzMtLjUyLjExLS44MjcuMTEtLjQ1IDAtLjgzMi0uMDgtMS4xNDUtLjI0YTEuODI2IDEuODI2IDAgMCAxLS43MTItLjYyIDEuNDcgMS40NyAwIDAgMS0uMjQtLjc5aDEuMDcyYS44My44MyAwIDAgMCAuMTcuNDkxYy4xMDQuMTIuMjMzLjIwNy4zODYuMjYyLjE1Ny4wNTMuMzE3LjA3OC40ODMuMDc4LjIgMCAuMzY2LS4wMjYuNTAxLS4wNzhhLjcyMS43MjEgMCAwIDAgLjMwOC0uMjIuNTI3LjUyNyAwIDAgMCAuMTA2LS4zMjJabTQuNTMyLTMuNjI4di44MWgtMi44MDV2LS44MWgyLjgwNVptLTEuOTk1LTEuMjE4aDEuMTA4djQuODE4YzAgLjE1NC4wMjEuMjcyLjA2NC4zNTQuMDQ2LjA4LjEwOS4xMzQuMTg5LjE2MS4wOC4wMjguMTczLjA0Mi4yOC4wNDJhMi4yMDkgMi4yMDkgMCAwIDAgLjM5LS4wNDFsLjAwNS44NDVjLS4wOTEuMDI4LS4xOTkuMDUyLS4zMjEuMDc0LS4xMi4wMjEtLjI1OC4wMzItLjQxNC4wMzItLjI1NSAwLS40OC0uMDQ0LS42NzYtLjEzM2EuOTk1Ljk5NSAwIDAgMS0uNDYtLjQ0NmMtLjExLS4yMDYtLjE2NS0uNDc5LS4xNjUtLjgxOXYtNC44ODhabTguNTggNS4wMnYtMy44MDJoMS4xMTRWNDIuNWgtMS4wNDlsLS4wNjQtMS4xNzJabS4xNTctMS4wMzQuMzczLS4wMWMwIC4zMzUtLjAzNy42NDMtLjExLjkyNWEyLjE0MiAyLjE0MiAwIDAgMS0uMzQuNzMxIDEuNTkgMS41OSAwIDAgMS0uNTkuNDgzIDEuOTkgMS45OSAwIDAgMS0uODYuMTdjLS4yNDEgMC0uNDY0LS4wMzUtLjY2Ni0uMTA2YTEuMzY2IDEuMzY2IDAgMCAxLS41MjQtLjMyNiAxLjQ4NSAxLjQ4NSAwIDAgMS0uMzM2LS41NzUgMi42NTUgMi42NTUgMCAwIDEtLjEyLS44NDZ2LTMuMjE0aDEuMTA5djMuMjIzYzAgLjE4MS4wMjEuMzMzLjA2NC40NTVhLjc3Ljc3IDAgMCAwIC4xNzUuMjljLjA3My4wNzQuMTYuMTI2LjI1Ny4xNTYuMDk5LjAzMS4yMDMuMDQ2LjMxMy4wNDYuMzE2IDAgLjU2NC0uMDYxLjc0NS0uMTg0LjE4NC0uMTI1LjMxNC0uMjk0LjM5LS41MDUuMDgtLjIxMi4xMi0uNDUuMTItLjcxM1ptMy40MDEtMS44MTJ2NS45MzJoLTEuMTA4di02Ljg4OGgxLjAybC4wODguOTU2Wm0zLjI0MiAxLjQ4NnYuMDk2YzAgLjM2Mi0uMDQzLjY5Ny0uMTI5IDEuMDA3YTIuNDYgMi40NiAwIDAgMS0uMzcyLjgwNSAxLjcxNCAxLjcxNCAwIDAgMS0uNjAzLjUyOGMtLjIzOS4xMjYtLjUxNS4xODktLjgyNy4xODktLjMxIDAtLjU4MS0uMDU3LS44MTQtLjE3YTEuNjY3IDEuNjY3IDAgMCAxLS41ODQtLjQ5MiAyLjY3IDIuNjcgMCAwIDEtLjM4Ni0uNzQ1IDQuNzgyIDQuNzgyIDAgMCAxLS4yMDMtLjk0N3YtLjM3M2MuMDQtLjM2NC4xMDgtLjY5Ni4yMDMtLjk5M2EyLjY5IDIuNjkgMCAwIDEgLjM4Ni0uNzY4Yy4xNi0uMjE0LjM1NC0uMzguNTg0LS40OTZhMS43NiAxLjc2IDAgMCAxIC44MDQtLjE3NWMuMzEzIDAgLjU5LjA2MS44MzMuMTg0LjI0Mi4xMi40NDYuMjkxLjYxMS41MTUuMTY2LjIyLjI5LjQ4Ny4zNzMuOC4wODIuMzEuMTI0LjY1NC4xMjQgMS4wMzVabS0xLjEwOC4wOTZ2LS4wOTZjMC0uMjMtLjAyMi0uNDQzLS4wNjUtLjY0LS4wNDMtLjE5OS0uMTEtLjM3NC0uMjAyLS41MjRhLjk5Ljk5IDAgMCAwLS4zNTQtLjM1Ljk2My45NjMgMCAwIDAtLjUxLS4xMjhjLS4xOTcgMC0uMzY1LjAzNC0uNTA2LjEwMWEuOTcuOTcgMCAwIDAtLjM1NC4yNzEgMS4yODIgMS4yODIgMCAwIDAtLjIyLjQxYy0uMDUzLjE1My0uMDkuMzItLjExMS41di44OTNjLjAzNy4yMi4xLjQyMy4xODguNjA3LjA5LjE4NC4yMTUuMzMuMzc3LjQ0MS4xNjYuMTA4LjM3Ny4xNjEuNjM1LjE2MS4xOTkgMCAuMzctLjA0My41MS0uMTI5LjE0MS0uMDg1LjI1Ni0uMjAzLjM0NS0uMzU0LjA5Mi0uMTUzLjE2LS4zMy4yMDItLjUyOC4wNDMtLjIuMDY1LS40MTEuMDY1LS42MzVabTUuMjgxIDEuNDA3di02LjAzMmgxLjExM1Y0Mi41aC0xLjAwN2wtLjEwNi0xLjAzWm0tMy4yMzctMS40MDJ2LS4wOTdjMC0uMzc3LjA0NS0uNzIuMTM0LTEuMDMuMDg4LS4zMTMuMjE3LS41OC4zODYtLjgwNS4xNjgtLjIyNi4zNzQtLjQuNjE2LS41Mi4yNDItLjEyMi41MTUtLjE4My44MTgtLjE4My4zIDAgLjU2NC4wNTguNzkxLjE3NS4yMjcuMTE2LjQyLjI4My41OC41YTIuNiAyLjYgMCAwIDEgLjM4MS43NzNjLjA5NS4yOTguMTYzLjYyOS4yMDMuOTk0di4zMDhjLS4wNC4zNTUtLjEwOC42OC0uMjAzLjk3NGEyLjYyIDIuNjIgMCAwIDEtLjM4MS43NjRjLS4xNi4yMTQtLjM1NC4zOC0uNTg0LjQ5NmExLjcxOSAxLjcxOSAwIDAgMS0uNzk2LjE3NWMtLjMgMC0uNTcyLS4wNjMtLjgxNC0uMTg5YTEuNzk4IDEuNzk4IDAgMCAxLS42MTEtLjUyOCAyLjQ5IDIuNDkgMCAwIDEtLjM4Ni0uOCAzLjYzNiAzLjYzNiAwIDAgMS0uMTM0LTEuMDA3Wm0xLjEwOC0uMDk3di4wOTdjMCAuMjI3LjAyLjQzOC4wNi42MzQuMDQzLjE5Ni4xMDkuMzcuMTk4LjUyLjA4OS4xNDcuMjA0LjI2My4zNDUuMzUuMTQ0LjA4Mi4zMTYuMTIzLjUxNS4xMjMuMjUxIDAgLjQ1OC0uMDU1LjYyLS4xNjUuMTYzLS4xMS4yOS0uMjYuMzgyLS40NDYuMDk1LS4xOS4xNi0uNDAyLjE5My0uNjM1di0uODMyYTIuMDMgMi4wMyAwIDAgMC0uMTE1LS41MDYgMS4zNSAxLjM1IDAgMCAwLS4yMjUtLjQwOS45NDguOTQ4IDAgMCAwLS4zNTQtLjI3NiAxLjExIDEuMTEgMCAwIDAtLjQ5Mi0uMTAxLjk3Mi45NzIgMCAwIDAtLjUxNS4xMjkgMS4wNDMgMS4wNDMgMCAwIDAtLjM1LjM1NGMtLjA4OC4xNS0uMTU0LjMyNS0uMTk3LjUyNC0uMDQzLjItLjA2NS40MTItLjA2NS42NFptNy40MDIgMS41MzFWMzkuMTNjMC0uMTc4LS4wMzMtLjMzMS0uMDk3LS40NmEuNjc2LjY3NiAwIDAgMC0uMjk0LS4yOTkgMS4wMDcgMS4wMDcgMCAwIDAtLjQ4OC0uMTA2Yy0uMTggMC0uMzM3LjAzLS40NjkuMDkyYS43NTkuNzU5IDAgMCAwLS4zMDguMjQ4LjYuNiAwIDAgMC0uMTEuMzU1aC0xLjEwM2MwLS4xOTcuMDQ3LS4zODcuMTQyLS41Ny4wOTUtLjE4NC4yMzMtLjM0OS40MTQtLjQ5My4xOC0uMTQ0LjM5Ny0uMjU3LjY0OC0uMzQuMjUyLS4wODMuNTM0LS4xMjQuODQ2LS4xMjQuMzc0IDAgLjcwNS4wNjMuOTkzLjE4OC4yOTEuMTI2LjUyLjMxNi42ODUuNTcuMTY5LjI1Mi4yNTMuNTY4LjI1My45NDh2Mi4yMTFjMCAuMjI3LjAxNi40MzEuMDQ2LjYxMi4wMzQuMTc4LjA4MS4zMzMuMTQzLjQ2NHYuMDc0aC0xLjEzNmExLjk2IDEuOTYgMCAwIDEtLjEyNC0uNDU1IDMuNzE2IDMuNzE2IDAgMCAxLS4wNDEtLjU0M1ptLjE2LTIuMDI4LjAxLjY4NmgtLjc5NmMtLjIwNSAwLS4zODYuMDItLjU0Mi4wNmExLjExNiAxLjExNiAwIDAgMC0uMzkxLjE2NS43Mi43MiAwIDAgMC0uMzEzLjYyYzAgLjEzMy4wMy4yNTMuMDkyLjM2NC4wNjEuMTA3LjE1LjE5MS4yNjcuMjUzLjEyLjA2MS4yNjMuMDkyLjQzMi4wOTIuMjI3IDAgLjQyNS0uMDQ2LjU5My0uMTM4LjE3Mi0uMDk1LjMwNy0uMjEuNDA1LS4zNDVhLjc1MS43NTEgMCAwIDAgLjE1Ni0uMzlsLjM1OS40OTFjLS4wMzcuMTI2LS4xLjI2LS4xODkuNDA1YTEuOTY0IDEuOTY0IDAgMCAxLS4zNS40MTQgMS43MzYgMS43MzYgMCAwIDEtMS4xOS40NDFjLS4zMjUgMC0uNjE1LS4wNjQtLjg2OS0uMTkzYTEuNTQ1IDEuNTQ1IDAgMCAxLS41OTgtLjUyOSAxLjM3MiAxLjM3MiAwIDAgMS0uMjE2LS43NTljMC0uMjYzLjA1LS40OTYuMTQ3LS42OTguMTAxLS4yMDYuMjQ5LS4zNzcuNDQyLS41MTUuMTk2LS4xMzguNDM1LS4yNDMuNzE3LS4zMTMuMjgyLS4wNzQuNjA0LS4xMS45NjYtLjExaC44NjlabTQuNTg4LTEuOTQ5di44MUg5MS4yMnYtLjgxaDIuODA1Wm0tMS45OTYtMS4yMThoMS4xMDh2NC44MThjMCAuMTU0LjAyMi4yNzIuMDY1LjM1NC4wNDYuMDguMTA5LjEzNC4xODguMTYxLjA4LjAyOC4xNzMuMDQyLjI4LjA0MmEyLjIwOSAyLjIwOSAwIDAgMCAuMzktLjA0MWwuMDA2Ljg0NWMtLjA5Mi4wMjgtLjIuMDUyLS4zMjIuMDc0LS4xMi4wMjEtLjI1Ny4wMzItLjQxMy4wMzItLjI1NSAwLS40OC0uMDQ0LS42NzYtLjEzM2EuOTk0Ljk5NCAwIDAgMS0uNDYtLjQ0NmMtLjExLS4yMDYtLjE2Ni0uNDc5LS4xNjYtLjgxOXYtNC44ODhabTUuMjQgNi4yODVjLS4zNjcgMC0uNy0uMDYtLjk5Ny0uMThhMi4yMDIgMi4yMDIgMCAwIDEtLjc1NC0uNTEgMi4yNjMgMi4yNjMgMCAwIDEtLjQ3NC0uNzY4IDIuNjkgMi42OSAwIDAgMS0uMTY2LS45NTF2LS4xODRjMC0uMzkuMDU3LS43NDIuMTctMS4wNThhMi40IDIuNCAwIDAgMSAuNDc0LS44MWMuMjAzLS4yMjYuNDQyLS40LjcxOC0uNTE5LjI3NS0uMTIuNTc0LS4xNzkuODk2LS4xNzkuMzU2IDAgLjY2Ny4wNi45MzMuMTguMjY3LjExOS40ODguMjg4LjY2My41MDUuMTc3LjIxNS4zMS40Ny4zOTUuNzY4LjA4OS4yOTcuMTMzLjYyNS4xMzMuOTg0di40NzRoLTMuODQ0di0uNzk2aDIuNzV2LS4wODdhMS41NTQgMS41NTQgMCAwIDAtLjEyLS41NjEuOTUzLjk1MyAwIDAgMC0uMzI2LS40MjNjLS4xNDctLjEwNy0uMzQzLS4xNjEtLjU4OS0uMTYxYTEgMSAwIDAgMC0uNDkyLjEyLjk3My45NzMgMCAwIDAtLjM1My4zMzUgMS43NjcgMS43NjcgMCAwIDAtLjIyMS41MzNjLS4wNS4yMDYtLjA3NC40MzctLjA3NC42OTV2LjE4NGMwIC4yMTcuMDMuNDIuMDg4LjYwNy4wNi4xODQuMTUuMzQ0LjI2Ni40ODIuMTE3LjEzOC4yNTguMjQ3LjQyMy4zMjcuMTY2LjA3Ni4zNTQuMTE1LjU2Ni4xMTUuMjY3IDAgLjUwNC0uMDU0LjcxMy0uMTYxLjIwOC0uMTA3LjM4OS0uMjYuNTQyLS40NTVsLjU4NC41NjVhMi4yOCAyLjI4IDAgMCAxLS40MTguNDUgMi4wOTUgMi4wOTUgMCAwIDEtLjYzLjM0NmMtLjI0Ni4wODgtLjUzLjEzMy0uODU2LjEzM1ptOC41NTQtNi44MVY0Mi41aC0xLjEwOHYtNS40MDNsLTEuNjQxLjU1N3YtLjkxNWwyLjYxNi0uOTU3aC4xMzNabTUuNjIyIDUuNjg4di02LjAzMmgxLjExM1Y0Mi41aC0xLjAwN2wtLjEwNi0xLjAzWm0tMy4yMzctMS40MDJ2LS4wOTdjMC0uMzc3LjA0NC0uNzIuMTMzLTEuMDNhMi40MiAyLjQyIDAgMCAxIC4zODctLjgwNWMuMTY4LS4yMjYuMzc0LS40LjYxNi0uNTIuMjQyLS4xMjIuNTE1LS4xODMuODE4LS4xODMuMzAxIDAgLjU2NC4wNTguNzkxLjE3NS4yMjcuMTE2LjQyLjI4My41NzkuNS4xNi4yMTYuMjg3LjQ3My4zODIuNzczLjA5NS4yOTguMTYyLjYyOS4yMDIuOTk0di4zMDhjLS4wNC4zNTUtLjEwNy42OC0uMjAyLjk3NGEyLjYwMyAyLjYwMyAwIDAgMS0uMzgyLjc2NGMtLjE1OS4yMTQtLjM1NC4zOC0uNTg0LjQ5NmExLjcxNiAxLjcxNiAwIDAgMS0uNzk1LjE3NWMtLjMgMC0uNTcyLS4wNjMtLjgxNC0uMTg5YTEuNzkyIDEuNzkyIDAgMCAxLS42MTEtLjUyOCAyLjQ4NSAyLjQ4NSAwIDAgMS0uMzg3LS44IDMuNjMyIDMuNjMyIDAgMCAxLS4xMzMtMS4wMDdabTEuMTA4LS4wOTd2LjA5N2MwIC4yMjcuMDIuNDM4LjA2LjYzNC4wNDMuMTk2LjEwOS4zNy4xOTguNTIuMDg5LjE0Ny4yMDMuMjYzLjM0NC4zNS4xNDUuMDgyLjMxNi4xMjMuNTE1LjEyMy4yNTIgMCAuNDU5LS4wNTUuNjIxLS4xNjUuMTYzLS4xMS4yOS0uMjYuMzgyLS40NDZhMi4wOCAyLjA4IDAgMCAwIC4xOTMtLjYzNXYtLjgzMmEyLjAyNSAyLjAyNSAwIDAgMC0uMTE1LS41MDYgMS4zNDkgMS4zNDkgMCAwIDAtLjIyNS0uNDA5Ljk0OC45NDggMCAwIDAtLjM1NC0uMjc2IDEuMTEgMS4xMSAwIDAgMC0uNDkyLS4xMDEuOTcuOTcgMCAwIDAtLjUxNS4xMjkgMS4wNDYgMS4wNDYgMCAwIDAtLjM1LjM1NGMtLjA4OS4xNS0uMTU1LjMyNS0uMTk3LjUyNC0uMDQzLjItLjA2NS40MTItLjA2NS42NFptOS45NDMgMS41MzFWMzkuMTNjMC0uMTc4LS4wMzMtLjMzMS0uMDk3LS40NmEuNjc4LjY3OCAwIDAgMC0uMjk0LS4yOTkgMS4wMSAxLjAxIDAgMCAwLS40ODgtLjEwNmMtLjE4MSAwLS4zMzcuMDMtLjQ2OS4wOTJhLjc2Ljc2IDAgMCAwLS4zMDguMjQ4LjYwMy42MDMgMCAwIDAtLjExLjM1NWgtMS4xMDRjMC0uMTk3LjA0OC0uMzg3LjE0My0uNTcuMDk1LS4xODQuMjMzLS4zNDkuNDE0LS40OTNhMi4wNyAyLjA3IDAgMCAxIC42NDgtLjM0Yy4yNTEtLjA4My41MzMtLjEyNC44NDYtLjEyNC4zNzQgMCAuNzA1LjA2My45OTMuMTg4LjI5MS4xMjYuNTIuMzE2LjY4NS41Ny4xNjkuMjUyLjI1My41NjguMjUzLjk0OHYyLjIxMWMwIC4yMjcuMDE2LjQzMS4wNDYuNjEyLjAzNC4xNzguMDgxLjMzMy4xNDMuNDY0di4wNzRoLTEuMTM2YTEuOTg2IDEuOTg2IDAgMCAxLS4xMjQtLjQ1NSAzLjY1OSAzLjY1OSAwIDAgMS0uMDQxLS41NDNabS4xNi0yLjAyOC4wMS42ODZoLS43OTZjLS4yMDUgMC0uMzg2LjAyLS41NDIuMDZhMS4xMSAxLjExIDAgMCAwLS4zOTEuMTY1LjcyMi43MjIgMCAwIDAtLjIzNS4yNjcuNzgyLjc4MiAwIDAgMC0uMDc4LjM1NGMwIC4xMzIuMDMxLjI1Mi4wOTIuMzYzYS42NC42NCAwIDAgMCAuMjY3LjI1My45MzQuOTM0IDAgMCAwIC40MzIuMDkyIDEuMjE3IDEuMjE3IDAgMCAwIC45OTgtLjQ4My43NS43NSAwIDAgMCAuMTU2LS4zOWwuMzU5LjQ5MWMtLjAzNy4xMjYtLjEuMjYtLjE4OS40MDVhMS45NjQgMS45NjQgMCAwIDEtLjM0OS40MTQgMS43MyAxLjczIDAgMCAxLTEuMTkxLjQ0MWMtLjMyNSAwLS42MTUtLjA2NC0uODY5LS4xOTNhMS41NDEgMS41NDEgMCAwIDEtLjU5OC0uNTI5IDEuMzcyIDEuMzcyIDAgMCAxLS4yMTYtLjc1OWMwLS4yNjMuMDQ5LS40OTYuMTQ3LS42OTguMTAxLS4yMDYuMjQ4LS4zNzcuNDQyLS41MTUuMTk2LS4xMzguNDM1LS4yNDMuNzE3LS4zMTMuMjgyLS4wNzQuNjA0LS4xMS45NjUtLjExaC44NjlabTUuNDk0LTEuOTQ5aDEuMDA2djQuODM3YzAgLjQ0OC0uMDk1LjgyOC0uMjg1IDEuMTQtLjE5LjMxMy0uNDU1LjU1LS43OTUuNzEzLS4zNC4xNjYtLjczNC4yNDgtMS4xODIuMjQ4YTIuNzcgMi43NyAwIDAgMS0xLjMwNi0uMzUgMS42OCAxLjY4IDAgMCAxLS41MzgtLjQ3M2wuNTItLjY1M2MuMTc4LjIxMi4zNzQuMzY3LjU4OS40NjUuMjE0LjA5OC40MzkuMTQ3LjY3NS4xNDcuMjU1IDAgLjQ3MS0uMDQ4LjY0OS0uMTQzYS45NjEuOTYxIDAgMCAwIC40MTgtLjQwOWMuMDk4LS4xOC4xNDctLjQwMS4xNDctLjY2MnYtMy43MzNsLjEwMi0xLjEyN1ptLTMuMzggMi41NDN2LS4wOTdjMC0uMzc3LjA0Ni0uNzIuMTM4LTEuMDNhMi40NSAyLjQ1IDAgMCAxIC4zOTUtLjgwNWMuMTcyLS4yMjYuMzgtLjQuNjI2LS41Mi4yNDUtLjEyMi41MjItLjE4My44MzItLjE4My4zMjIgMCAuNTk2LjA1OC44MjMuMTc1LjIzLjExNi40MjEuMjgzLjU3NS41LjE1My4yMTYuMjczLjQ3My4zNTguNzczLjA4OS4yOTguMTU1LjYyOS4xOTguOTk0di4zMDhjLS4wNC4zNTUtLjEwNy42OC0uMjAyLjk3NGEyLjY5NyAyLjY5NyAwIDAgMS0uMzc3Ljc2NGMtLjE1Ny4yMTQtLjM1LjM4LS41OC40OTZhMS43MzcgMS43MzcgMCAwIDEtLjgwNC4xNzVjLS4zMDQgMC0uNTc4LS4wNjMtLjgyMy0uMTg5YTEuODc5IDEuODc5IDAgMCAxLS42MjYtLjUyOCAyLjUxOCAyLjUxOCAwIDAgMS0uMzk1LS44IDMuNTIgMy41MiAwIDAgMS0uMTM4LTEuMDA3Wm0xLjEwOC0uMDk3di4wOTdjMCAuMjI3LjAyMi40MzguMDY0LjYzNC4wNDYuMTk2LjExNS4zNy4yMDcuNTIuMDk1LjE0Ny4yMTUuMjYzLjM1OS4zNS4xNDcuMDgyLjMyLjEyMy41Mi4xMjMuMjYgMCAuNDczLS4wNTUuNjM5LS4xNjUuMTY4LS4xMS4yOTctLjI2LjM4Ni0uNDQ2LjA5Mi0uMTkuMTU2LS40MDIuMTkzLS42MzV2LS44MzJhMi4wMjUgMi4wMjUgMCAwIDAtLjExNS0uNTA2IDEuMzQ5IDEuMzQ5IDAgMCAwLS4yMjUtLjQwOS45NDIuOTQyIDAgMCAwLS4zNTktLjI3NiAxLjE5MyAxLjE5MyAwIDAgMC0uNTEtLjEwMWMtLjE5OSAwLS4zNzMuMDQzLS41Mi4xMjlhMS4wNTUgMS4wNTUgMCAwIDAtLjM2My4zNTQgMS43NSAxLjc1IDAgMCAwLS4yMDcuNTI0Yy0uMDQ2LjItLjA2OS40MTItLjA2OS42NFptNC40MTMuMDk3di0uMTA2YzAtLjM1OS4wNTItLjY5MS4xNTYtLjk5OC4xMDQtLjMxLjI1NC0uNTc4LjQ1MS0uODA0YTIuMDMgMi4wMyAwIDAgMSAuNzI2LS41MzRjLjI4OC0uMTI5LjYxMy0uMTkzLjk3NS0uMTkzLjM2NSAwIC42OS4wNjQuOTc1LjE5My4yODguMTI2LjUzMS4zMDQuNzMxLjUzNC4xOTkuMjI2LjM1MS40OTUuNDU1LjgwNC4xMDQuMzA3LjE1Ni42NC4xNTYuOTk4di4xMDZjMCAuMzU4LS4wNTIuNjktLjE1Ni45OTdhMi4zODcgMi4zODcgMCAwIDEtLjQ1NS44MDVjLS4yLjIyNy0uNDQyLjQwNS0uNzI3LjUzM2EyLjM4IDIuMzggMCAwIDEtLjk3LjE4OWMtLjM2NSAwLS42OTEtLjA2My0uOTc5LS4xODlhMi4xMSAyLjExIDAgMCAxLS43MjctLjUzMyAyLjM4NyAyLjM4NyAwIDAgMS0uNDU1LS44MDUgMy4wODYgMy4wODYgMCAwIDEtLjE1Ni0uOTk3Wm0xLjEwOC0uMTA2di4xMDZjMCAuMjIzLjAyMy40MzUuMDY5LjYzNC4wNDYuMi4xMTguMzc0LjIxNi41MjQuMDk4LjE1LjIyNC4yNjkuMzc3LjM1NC4xNTMuMDg2LjMzNi4xMy41NDcuMTMuMjA1IDAgLjM4My0uMDQ0LjUzMy0uMTMuMTU0LS4wODUuMjc5LS4yMDMuMzc3LS4zNTQuMDk4LS4xNS4xNzEtLjMyNS4yMTctLjUyNC4wNDktLjIuMDczLS40MS4wNzMtLjYzNHYtLjEwNmMwLS4yMi0uMDI0LS40My0uMDczLS42MjVhMS42MSAxLjYxIDAgMCAwLS4yMjEtLjUzIDEuMDU3IDEuMDU3IDAgMCAwLS45MTUtLjQ5MWMtLjIwOSAwLS4zODkuMDQ0LS41NDMuMTMzLS4xNS4wODYtLjI3NC4yMDYtLjM3Mi4zNTlhMS42NyAxLjY3IDAgMCAwLS4yMTYuNTI5IDIuNzMzIDIuNzMzIDAgMCAwLS4wNjkuNjI1WiIvPjxwYXRoIGZpbGw9IiNGODlFMEQiIGQ9Ik00NS43OTEgODkuMDI1VjEwOWgtMy4yOTVWOTIuOTM2bC00Ljg4IDEuNjU0di0yLjcybDcuNzc5LTIuODQ1aC4zOTZabTEyLjg3OSAwVjEwOWgtMy4yOTVWOTIuOTM2bC00Ljg4IDEuNjU0di0yLjcybDcuNzc4LTIuODQ1aC4zOTdabTE4LjEyOSA4LjM1NHYzLjI1NGMwIDEuNTU4LS4xNTUgMi44ODktLjQ2NSAzLjk5Mi0uMyAxLjA5NC0uNzM4IDEuOTgyLTEuMzEzIDIuNjY2YTUuMTQ3IDUuMTQ3IDAgMCAxLTIuMDY0IDEuNTA0Yy0uNzkzLjMxOS0xLjY4Mi40NzgtMi42NjYuNDc4LS43ODQgMC0xLjUxMy0uMS0yLjE4OC0uM2E1LjM0NCA1LjM0NCAwIDAgMS0xLjgwNC0uOTQ0Yy0uNTM4LS40MjgtLjk5OC0uOTgtMS4zODEtMS42NTQtLjM3NC0uNjg0LS42NjUtMS40OTktLjg3NS0yLjQ0Ny0uMi0uOTQ4LS4zLTIuMDQ2LS4zLTMuMjk1di0zLjI1NGMwLTEuNTY4LjE1NC0yLjg5LjQ2NC0zLjk2NS4zMS0xLjA4NS43NTItMS45NjQgMS4zMjYtMi42MzlhNS4wNTEgNS4wNTEgMCAwIDEgMi4wNTEtMS40OWMuODAyLS4zMSAxLjY5NS0uNDY1IDIuNjgtLjQ2NS43OTMgMCAxLjUyMi4xIDIuMTg3LjMwMWE1LjEyMiA1LjEyMiAwIDAgMSAxLjgwNS45MTZjLjUzOC40Mi45OTMuOTY2IDEuMzY3IDEuNjQuMzgzLjY2Ni42NzQgMS40NzMuODc1IDIuNDIuMi45NC4zIDIuMDMzLjMgMy4yODJabS0zLjI5NSAzLjcxOXYtNC4yMTFjMC0uNzkzLS4wNDYtMS40OS0uMTM3LTIuMDkyLS4wOTEtLjYxLS4yMjgtMS4xMjYtLjQxLTEuNTQ1LS4xNzMtLjQyOC0uMzkyLS43NzUtLjY1Ni0xLjA0YTIuMzEgMi4zMSAwIDAgMC0uOTAzLS41ODcgMy4xODYgMy4xODYgMCAwIDAtMS4xMzQtLjE5MWMtLjUxIDAtLjk2Ni4xLTEuMzY3LjMtLjQwMi4xOTItLjczOS41MDItMS4wMTIuOTMtLjI3NC40MjgtLjQ4My45OTQtLjYzIDEuNjk1LS4xMzYuNjkzLS4yMDQgMS41MzYtLjIwNCAyLjUzdjQuMjExYzAgLjgwMi4wNDUgMS41MDguMTM2IDIuMTE5LjA5Mi42MS4yMjggMS4xMzUuNDEgMS41NzIuMTgzLjQyOC40MDIuNzg0LjY1NyAxLjA2Ni4yNjQuMjc0LjU2NS40NzQuOTAyLjYwMmEzLjI1IDMuMjUgMCAwIDAgMS4xMzUuMTkxYy41MiAwIC45OC0uMSAxLjM4LS4zLjQwMi0uMjAxLjc0LS41MiAxLjAxMy0uOTU3LjI3My0uNDQ3LjQ3OC0xLjAyNi42MTUtMS43MzcuMTM2LS43MTEuMjA1LTEuNTYzLjIwNS0yLjU1NlptMjMuMTYtNi44OTFoMy4zMDlWMTA5aC0zLjA1bC0uMjU5LTMuNDE4Vjk0LjIwN1ptLjQ3OSA3LjkxNiAxLjI5OC0uMDI3YzAgMS4wMzktLjEgMS45OTYtLjMgMi44NzEtLjE5Mi44NzUtLjQ5MyAxLjYzNi0uOTAzIDIuMjgzYTQuMzIyIDQuMzIyIDAgMCAxLTEuNTMxIDEuNTA0Yy0uNjIuMzU1LTEuMzUzLjUzMy0yLjIwMS41MzMtLjY3NSAwLTEuMjgtLjA5Ni0xLjgxOS0uMjg3YTMuNjA4IDMuNjA4IDAgMCAxLTEuMzk0LS45M2MtLjM5Mi0uNDE5LS43MTYtLjk2Ni0uOTctMS42NGwtLjQyNS0zLjczM2gxLjg3NGMwIC44MjEuMDY4IDEuNDkxLjIwNSAyLjAxLjEzNi41Mi4zMjguOTIxLjU3NCAxLjIwMy4yNDYuMjc0LjUzMy40NjUuODYxLjU3NC4zMzcuMTEuNzAyLjE2NCAxLjA5NC4xNjQuNzIgMCAxLjMxMi0uMTA5IDEuNzc3LS4zMjhhMi42ODMgMi42ODMgMCAwIDAgMS4xMDgtLjk0M2MuMjczLS40MS40NjUtLjg4OS41NzQtMS40MzZhOC43MjQgOC43MjQgMCAwIDAgLjE3OC0xLjgxOFptLTkuNjUzLTcuOTE2aDMuMjgydjIwLjQ4MUg4Ny40OVY5NC4yMDdabTI1LjgxMyAwaDIuOTk0djE0LjM4M2MwIDEuMzMxLS4yODMgMi40NjEtLjg0OCAzLjM5YTUuNDQ1IDUuNDQ1IDAgMCAxLTIuMzY1IDIuMTJjLTEuMDEyLjQ5Mi0yLjE4My43MzgtMy41MTQuNzM4YTguMjQ4IDguMjQ4IDAgMCAxLTEuODg2LS4yNDYgNy43NDIgNy43NDIgMCAwIDEtMS45OTYtLjc5MyA0Ljk0NSA0Ljk0NSAwIDAgMS0xLjYtMS40MDhsMS41NDUtMS45NDJjLjUyOC42MjkgMS4xMTIgMS4wODkgMS43NSAxLjM4MWE0Ljc3NSA0Ljc3NSAwIDAgMCAyLjAxLjQzOGMuNzU2IDAgMS4zOTktLjE0MiAxLjkyNy0uNDI0YTIuODYgMi44NiAwIDAgMCAxLjI0NC0xLjIxN2MuMjkyLS41MzguNDM4LTEuMTk0LjQzOC0xLjk2OVY5Ny41NTdsLjMwMS0zLjM1Wm0tMTAuMDQ5IDcuNTYxdi0uMjg4YzAtMS4xMjEuMTM3LTIuMTQyLjQxLTMuMDYyLjI3NC0uOTMuNjY1LTEuNzI3IDEuMTc2LTIuMzkzYTUuMTI1IDUuMTI1IDAgMCAxIDEuODU5LTEuNTQ0Yy43MjktLjM2NSAxLjU1NC0uNTQ3IDIuNDc1LS41NDcuOTU3IDAgMS43NzMuMTczIDIuNDQ3LjUyYTQuNjIyIDQuNjIyIDAgMCAxIDEuNzA5IDEuNDljLjQ1Ni42MzcuODExIDEuNDAzIDEuMDY2IDIuMjk2LjI2NS44ODQuNDYxIDEuODY5LjU4OCAyLjk1M3YuOTE2YTE0Ljc5MyAxNC43OTMgMCAwIDEtLjYwMSAyLjg5OWMtLjI4My44NzUtLjY1NiAxLjYzMS0xLjEyMSAyLjI2OWE0Ljc4MSA0Ljc4MSAwIDAgMS0xLjcyMyAxLjQ3N2MtLjY3NC4zNDYtMS40NzIuNTE5LTIuMzkzLjUxOS0uOTAyIDAtMS43MTgtLjE4Ni0yLjQ0Ny0uNTZhNS41NzIgNS41NzIgMCAwIDEtMS44NTktMS41NzJjLS41MTEtLjY3NS0uOTAyLTEuNDY4LTEuMTc2LTIuMzc5YTEwLjQ5NCAxMC40OTQgMCAwIDEtLjQxLTIuOTk0Wm0zLjI5NS0uMjg4di4yODhjMCAuNjc0LjA2NCAxLjMwMy4xOTEgMS44ODYuMTM3LjU4NC4zNDIgMS4wOTkuNjE1IDEuNTQ1LjI4My40MzguNjM4Ljc4NCAxLjA2NyAxLjAzOS40MzcuMjQ2Ljk1Mi4zNjkgMS41NDUuMzY5Ljc3NSAwIDEuNDA4LS4xNjQgMS45LS40OTIuNTAxLS4zMjguODg0LS43NyAxLjE0OS0xLjMyNmE2Ljc0MyA2Ljc0MyAwIDAgMCAuNTc0LTEuODg3di0yLjQ3NGE2LjA2NCA2LjA2NCAwIDAgMC0uMzQyLTEuNTA0IDQuMDE0IDQuMDE0IDAgMCAwLS42Ny0xLjIxNyAyLjc5NSAyLjc5NSAwIDAgMC0xLjA2Ni0uODJjLS40MjktLjItLjkzNS0uMzAxLTEuNTE4LS4zMDEtLjU5MiAwLTEuMTA3LjEyNy0xLjU0NS4zODNhMy4xNDYgMy4xNDYgMCAwIDAtMS4wOCAxLjA1MyA1LjIyMyA1LjIyMyAwIDAgMC0uNjE1IDEuNTU4IDguNDIxIDguNDIxIDAgMCAwLS4yMDUgMS45Wm0yMS44Mi0xMi4zODYtNy43NTIgMjEuNjE1aC0yLjU4NGw3Ljc2Ni0yMS42MTVoMi41N1ptNS41NzggOC4xMlYxMDloLTMuMjk1Vjk0LjIwN2gzLjEwNGwuMTkxIDMuMDA4Wm0tLjUzMyAzLjg0My0xLjEyMS0uMDE0YzAtMS4wMjEuMTI4LTEuOTY0LjM4My0yLjgzLjI1NS0uODY2LjYyOS0xLjYxOCAxLjEyMS0yLjI1NmE0LjkyOCA0LjkyOCAwIDAgMSAxLjgzMi0xLjQ5Yy43MzgtLjM1NiAxLjU5LS41MzMgMi41NTctLjUzMy42NzQgMCAxLjI4OS4xIDEuODQ1LjNhMy42NTggMy42NTggMCAwIDEgMS40NjMuOTE2Yy40MTkuNDIuNzM4Ljk1Ny45NTcgMS42MTQuMjI4LjY1Ni4zNDIgMS40NDkuMzQyIDIuMzc5VjEwOWgtMy4yOTV2LTkuNTdjMC0uNzItLjEwOS0xLjI4Ni0uMzI4LTEuNjk2YTEuODIgMS44MiAwIDAgMC0uOTE2LS44NzVjLS4zOTItLjE4Mi0uODYxLS4yNzMtMS40MDgtLjI3My0uNjIgMC0xLjE0OS4xMTgtMS41ODYuMzU1LS40MjkuMjM3LS43OC41NjEtMS4wNTMuOTcxLS4yNzMuNDEtLjQ3NC44ODQtLjYwMiAxLjQyMmE3LjQ4IDcuNDggMCAwIDAtLjE5MSAxLjcyM1ptOS4xNzQtLjg3NS0xLjU0NS4zNDFjMC0uODkzLjEyMy0xLjczNi4zNjktMi41MjlhNi40MjYgNi40MjYgMCAwIDEgMS4xMDgtMi4xMDUgNS4xOTQgNS4xOTQgMCAwIDEgMS44MTgtMS40MzZjLjcyLS4zNDYgMS41NDUtLjUyIDIuNDc0LS41Mi43NTcgMCAxLjQzMS4xMDUgMi4wMjQuMzE1LjYwMi4yIDEuMTEyLjUyIDEuNTMxLjk1Ny40MTkuNDM4LjczOCAxLjAwNy45NTcgMS43MS4yMTkuNjkyLjMyOCAxLjUzLjMyOCAyLjUxNVYxMDloLTMuMzA4di05LjU4NGMwLS43NDctLjExLTEuMzI2LS4zMjgtMS43MzYtLjIxLS40MS0uNTExLS42OTMtLjkwMy0uODQ4LS4zOTItLjE2NC0uODYxLS4yNDYtMS40MDgtLjI0Ni0uNTEgMC0uOTYyLjA5Ni0xLjM1My4yODdhMi42ODMgMi42ODMgMCAwIDAtLjk3MS43OCAzLjQ4NSAzLjQ4NSAwIDAgMC0uNjAyIDEuMTM0IDQuODg4IDQuODg4IDAgMCAwLS4xOTEgMS4zOTVabTE0LjQ1MS02LjY1OWgxLjE5Yy4zOTEgMCAuNzE1LS4wNTQuOTctLjE2NC4yNTUtLjExOC40NDctLjI3OC41NzQtLjQ3OC4xMzctLjIuMjA2LS40MzguMjA2LS43MTEgMC0uMzI4LS4xMjgtLjYxNS0uMzgzLS44NjEtLjI0Ni0uMjU2LS42NjEtLjM4My0xLjI0NC0uMzgzLS40NDcgMC0uODI1LjA5Ni0xLjEzNS4yODctLjMwMS4xODItLjQ1MS40NDItLjQ1MS43OGgtMi40ODljMC0uNjQ4LjE4My0xLjIuNTQ3LTEuNjU1LjM2NS0uNDU2Ljg1Mi0uODAyIDEuNDYzLTEuMDRhNS40NzIgNS40NzIgMCAwIDEgMS45OTYtLjM1NWMuODMgMCAxLjU1OS4xMTkgMi4xODguMzU2LjYyOS4yMjggMS4xMTYuNTcgMS40NjMgMS4wMjUuMzU1LjQ1Ni41MzMgMS4wMjEuNTMzIDEuNjk1IDAgLjUyLS4xNjQuOTgtLjQ5MiAxLjM4MS0uMzE5LjQwMS0uNzY2LjcxNi0xLjM0Ljk0NC0uNTY1LjIxOS0xLjIzMS4zMjgtMS45OTYuMzI4aC0xLjZ2LTEuMTQ5Wm0wIDEuNzFWOTQuMTFoMS42Yy44NDcgMCAxLjU3Mi4xIDIuMTczLjMwMS42MDIuMTkxIDEuMDU4LjQ4OCAxLjM2OC44ODkuMzE5LjQuNDc4LjkyLjQ3OCAxLjU1OCAwIC42NzUtLjE5MSAxLjI1LS41NzQgMS43MjMtLjM4My40NzQtLjkwNy44MzQtMS41NzIgMS4wOC0uNjU3LjI0Ni0xLjM5NS4zNjktMi4yMTUuMzY5LS42ODQgMC0xLjM0OS0uMTEtMS45OTYtLjMyOGEzLjcwNiAzLjcwNiAwIDAgMS0xLjU4Ni0xLjA2NmMtLjQxLS40ODMtLjYxNS0xLjExNy0uNjE1LTEuOWgyLjUwMmMwIC4zNTUuMTY0LjY2LjQ5Mi45MTUuMzI4LjI1Ni43Ny4zODMgMS4zMjYuMzgzLjU5MiAwIDEuMDMtLjEyNyAxLjMxMi0uMzgzLjI5Mi0uMjY0LjQzOC0uNTkyLjQzOC0uOTg0IDAtLjM0Ni0uMDgyLS42MjQtLjI0Ni0uODM0YTEuMjc1IDEuMjc1IDAgMCAwLS42Ny0uNDUxYy0uMjkyLS4xLS42MzQtLjE1LTEuMDI1LS4xNWgtMS4xOVoiLz48ZGVmcz48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2MiIHRyYW5zZm9ybT0ibWF0cml4KC4wMDUwMyAwIDAgLjAwNjI5IDAgMCkiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjE5Ny40MTciIGhlaWdodD0iMTU3LjQxNyIgeD0iMS4yOTIiIHk9IjEuMjkyIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIyLjM1NCIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMTAzXzM0ODMiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzExMDNfMzQ4MyIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFJQkFRRUJBUUlCQVFFQ0FnSUNBZ1FEQWdJQ0FnVUVCQU1FQmdVR0JnWUZCZ1lHQndrSUJnY0pCd1lHQ0FzSUNRb0tDZ29LQmdnTERBc0tEQWtLQ2dyLzJ3QkRBUUlDQWdJQ0FnVURBd1VLQndZSENnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ3Ivd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhnQUFBUVFEQVFFQkFBQUFBQUFBQUFBQUF3SUVCUVlCQndnQUNRci94QUFjQVFBREFRRUJBUUVCQUFBQUFBQUFBQUFCQWdNRUFBVUdCd2ovMmdBTUF3RUFBaEFERUFBQUFQcVY5TDgwWmVPaE9wY0JqcnhsTGhYTXBPcGNxVEs1VUxnTTVESFJqS3gxWXlzVldjS1NxeFF4QVM4ZVpmYjhzeWs4Mk9DNFZqSXhsTGhXTXBjSVRLeDBkeE56b1hJSmxKVkp1SlF4MTQ0SlR4dTVaNHhITWZyWnpveFpzZENaV09oUE5qcXhGYzgyT2hPanVFSjFMZ0Z3cE1wT0FYZ1FBaEN5Q0JWa0tjYzIrMXhsWXMyTk56STVwdVpHTk56emM4MmNSWndyT0pzZE9Lb1dxNVhsSXVSeXdwQ3BPVXhWWEFsRTU0K3lZc25OR2hVY3FVTkNocDBNbEhFWGN4bzRTaFpoU0twZVVnVWlxbUZCVmpsU1JYREtnaUtRcVFyb245UXlsUnl6b1NkU1RvV1ZqUW9WS25qVThxR1NpNVVWTUxpcXdxMEdaaks5NVFxUVV2WVVLbUZoU2N1Z1A2SC9BQzV1NEVCS1pkVHlHaGxiT3gxWmlCanJSekdqM0hzZDU5VVhxeCtuUXlQR2JNS1NsbjhuMDIxWUxLb0lUTjNtYlMyMFp4c05jZmFmTVNlUFpJNU5IaXE3d2lQVThtQzlieWE3NmZtaGRaWEhzczNqK3hUUEk5YldHUysvSFMrNnNiQ3M2bjZYbXVWcEVYbkgxbEZhWXNxU0tyekdUWlBlZnZxNjJjWjlNeGowdmM5V2dmQzg3ZWREMjR1WmFRNVkwdzU3Vmw4WHZOWTB0Mk9SMlM4cm9VcWU3UFFmYzhPSzM0ZlBONHBuOG1wMTVucTFYRnIybjVmcnQrNHFneW1BcExXMnJMb3JaaTBEY1VtTm9GQzRkWHhENWEyU0ZycjVubzdnOFAyK2g4MWVrNzQzSHFlWFRmZDhXb2VuNTNKWWZrbnkvUzFaajJ1dWY3ZUIwZ1lJalQxZktVbW1mVSszTHoxdWpUSjlZS3pLeG15cytxdGtxM3hiTjkvTWZUYjg4RDErY051YnAzMGZIcTIzei9uSFpkVGwyUGNwdU96ZmRXVkU4QUVOaUtxeTZyZUduZFVkUGFPcWxVQ1p5YnBhbm5NTDA1bnA2RjVyenZUNkYrYzkvYm5uYXVTTitQUnZvZWZYdG1iVFJuQVB6WmlTcWZlQ2J1MForalBKc2dOVHFKcjlHb0FycmE4b2paa2pMNVkrc2tWamgweEtqTExxay9POVBlZmllcnRQSm81ODFZK05OL25NTkVhOXFuQkpaRGQrZ2ZIcDhyTG1YRTZPdTVnODZ0UVV4dTErNHBscHcrbk0xMDRvUnAxeCtqR1RNTkRmRHZsL1AzYkd4WGFsZE02c25OL1RnU1UyVnJxUDZHL0gzblZpS3hCMlJ6YWlSRlpRbWlWUnRPaDFTbVVteGVjMXl5SE1pVmF2SzBQbnVmTHBaUk1PVnFGRTFDWWFuck9BZmhPUDBCK2Y2WmxLa09EeW1Ed2RJOHFtV0Uwd3B1dlBBV1NPSE5rSzBNb0drWlBtVjYvQ3REbTlDRDExYVJYQ3JzS1cwNkpvei9hUExyTWpZVXQrQVNHeko2aXZtbk1EcE1yRjZZd041TVNvMU9KMHdyWm5SMHJTY25ZSWRmcFNnaW1zZXBTbkppdjBMTW5reVlGS2xuM05tUnF5b29wV1Y4VWt5SHRKNVpJOGlQN2hjWWhHMUxtclRKUFNwblFFZTFmSnFjajBPWlJ4L1FwNzB6VGM2ZzZzdnVEd2FrTlNvbTV5NitNNkxTT3FRbERrZFl6YlFlVzNPVUhGTnZEc04za2J4R1VmREJZUDZHUG9VTk1rVGxna0hLSEpLa0hVNHB5cEh1QTVMUXRtZWUxWm9QRHB5R2hzMVJJeFpWOGhHeTRYc2NjbmtrS1Uvd0QveEFBakVBQUJCUUFCQkFNQkFRQUFBQUFBQUFBQ0FRTUVCUVlBQndnVEZCRVNGUllKLzlvQUNBRUJBQUVDQVJRVUFRRVJFUlFFRkFRRUhnb0hBNEtDZ0lJaUlpS0R3VkhnOEJSVVVCQkVVQkI0Q2dvOEhnOERnOFRnOEhpY0ZFUVVIbjJGUklGRWhVQ0V4TkRGUk1YQk1TQlI0S2lhT0k2anlQSStMNHZDUW1KaWFHRGlHSmlRa0tpb0tMaVBKSVNUN0hzZXlNbEpLU1VmUjhYRU1URjFIaGVCNFh3ZkIwWDBrKzE3WHRKSlNTa3IyaGxwTDlwSlF5eGxnNmpvdUlhT2c2TG91QTZqd3lQT2p5UEk4TDNtODNtUjd6K3dra1pTU2lkL2JLOGF0VXMvMm50VkgxeGFkblFKb1AxaXZrMTM5Uzd0STIzYjBSM1kycTNmOVNHdWUwUzZWbldOWHd5RWlGV3ZWa3BsMThMV0xOYTBGeDNQc2QvV0o2NC9yekx6Mm5OTTcxRmQ2cm4xRi91bTl6RjI4YldJREp4NUsyQzZkTjQzMVMyUGREcysvVFg5MlV1elpWdGlqdGNoM2FZYXdmNmYyR0RtWTM4QWFsbWloVUUyOVk3aGZsZUJJOXV3UzRoNnV2dU14TXh4NWxySUJrNDlEQm82M0ZVbVV6WFhyTWEyZG5KbUNzOHJ0TzVmV2RkUEtCcUNLUXZsTG5XZHRvYlhRdkJvV1o0VmtHUUVSNkEyblMvWTJlUHZxUHB2M05kVHU3dnFqMXRhYzhyVDZTaEpXVmFObXpxN2l0dnF5NGdGQUtCRW1RNUxQRzN2RFZQWXZYeklYVkhvKzFzYjNwN1BxWkpKSURnSTJUUWd3L1YyTkxaWnV5eDlqaEZ4NytSV2w4Q0tmRnM2N1I1clZ0YXpxZDA5a0xLMWs4akJIZnovQUVoakJ3VGtqTWcyTlZZeDViYXE0M0twcE5KNmFRL3lJTm5DMU5wYTZrcm1xS1I4SXJjbEpDU0VMd093SkVOOW14cWJIUHlxWnFzalZMZGE5bFpXUldzR05KeXB3TGlpdk1yWlpsWVM4RWtMNTh3ekdyRnVjY1dabkp0RzVUdVpYK2NDdVlndFFuTTVNNmMybUZtUnBGZTlYenFDeHlNM0hnUGpWb3VLMnZCZGFteDdKSHBWSzVVbHo0OVA4OUl6UmdVeW1zK25FL0MzRktkWi9QQndGVGhNa3dyUk5JZ3F5K3pLQjFZajFBdFA2dG5QdE80SSs3dlM5OE9wNzVYdTZhODZ5U3JWWm9tTGdPSTU5aUVoSnZ4dHBOc2JUcmhQNzI3Zi9RdXcvd0JDOWwzVnkzL3R4T0R4QlFmaEVSUklYQmRGL3dBM2tEbXA2aWFudnQ2cWRiSytRL1R0VXo5R29mUkIrdjBWUHFwaTU1UC94QUJJRUFBQkF3SUNCZ2NEQ0FjRkNRQUFBQUFCQUFJREJCRUZFZ1lUSVRGUllRY1FJakpCY1lFVVlxRVZJek5DVW5LQ2tTQWtOWkt4d2RFV0pUUkRjekJGVTRPaXNzTGg4UC9hQUFnQkFRQURQd0VjRnlSL1RIVndRLzJGMEVBaDFEcUpUdUNQRmMxejZnRUVPb0lMZ2lqeVJSdHZSdDFiRUVFRUZicThicm1nZ3VINkFRUTVxNkhGRGltOFVPSzVybXVhSEZBcng2dWZWejZod1hORGloeFFQaWh4WEE5UUNISDlMM2w3eUhVT0t2MURqMWpxSGgxYzFaRHE1b2NWd1BVQWh2UTRybWh4WE5EaXVhNW9mYVE0cjNselhOSDdTUEZlOGlOemtmRXEvd0JaYzF6WE5jRVZ6WE5jVnY2dWE1b3JHNmZ0UFlIajNXS3J2WndEZk9JcXNjNnduamFQZEcxVEZuWm5iZjN3cG14M2t5K2JSY0xFN0V3VTdKZnVrWC9JckdJVGI1THQ5L1lzUWNmbnFWak9aY3E1NXREVjB6ZnczV0tYczZycG5lb1dJM3RsWis3ZitDeFo3UDFka2JqdzNmeFdsa2NoL3VpVzNQSlpZNHc1WmNKbkhOa04xaXVyMndWQTUremk2N0JpcWFpcGJ4MkFMTklJMllrY28zTnlIK3FyNUdacWVWai9BTDJ4YVZTdXRBSUFQdXJTVmd6VlZWQ1ArV1FzY20rZ3hLRDhIL3RxcnFML0FCVWxTNDhmWmJoR2R1eVNScDUweEN4VS93Q0duYy83cldqK0lXa2NZL1pOYS9uSHFqL0JUTzJGeWRMM3orYXA2aHZiaFlWaGp6dGh5OHdpOXRxU3Z5K2Ewa1o5RlhaMXBTQVErRXU4cGxpbE9mbmFGOS9OVE83L0FHZlJVb09aOVlCK0JZZlF4NjJweGRrVFFOcnBTR2o0cm9XMFdMb01XNlFxV1dSbStLamlkVU8vNkxqNHJvbmZpb29YNk40MktRbXpxNHdSN09lVFBtWFJMMGxWRDZQUS9US0NTcGJ0OWxxSTN3eUg3b2VCbTlMcWdoZDg1WFEramxvN093dHJLcUlqL3dDNHJSTms1ZFMxemp5WkhzV0NVemYyaWZMVmdyRGFjL3E4VDVEeGRzV0pib05WRVBKWXhJZjJqYjdvc3NhZi92aVg5NVkxZjlxU0g4U3hFN0phbDVUbjk1NzB6Z3NtNXl0NDJVa1F2dkNwWWpaN2kxWVpTN1pLN1o1TFFwclM2czBrcElyYjljL0wvRmRER2h0SVpKY1liaWM5dXpTNFl6T1Q1dU5tajgxbzc4bUZ1aEhSTm1ySGY1dU1UTjFUUFNQYTQrb1hTN3BhejJhamx3N0JtSGY4a1VPUjUvRy9NZnlzc1R4T1owK0s0aFBVeU9kZDdwNTNQSlBIYW5XeWdLZDNkYXNXd0N0anhHZ2ZxNW9YaDBiL0FMSjRoWTY4TnB0T05FNmJFbTdBYWlrZnFKZlB4RGorUzZPZWsybU5Ub3Jpa3JwQUx2b3FyNXVWbnA0am1GMmNzYnNvODFXNVMyR1Q0TEhJbmtPZTBEelZYSGZQS1Q2S3J2WnBWYzdmSzBlcWFIRFhWRitRQzBOMGFiYkc5STZPbHNMMnFhbHJEYnlLNkZZNm4yWCsyTktUOXZVeVpQenlvRkZwMkl0M3JadlVjNE54dFR3MTJXUXFXUnJ0WTY0NXFHY2ttSkczemQ3SnpIV2N0WWJzY3FpRTltTzZuQitjakk1cUo1QWZLUHhCVUU1RG56UnM5VmkrQjZuRXNNcVhqVjdZNTRuYnZVTFNQQTVCRHBoVEd2cGZyVFJNQW1aL0ovOEFGYUthYzRmOG82T1lzeWRnK2thTmo0andjMDdXb1ZkOHRSOEZXa2t4UzV2VllsaGRQSlcxVFkyUXhOelNUU1NCcldEaVNkeTBVMGFNbEpvN0FjV25iL214T3l3QS9lM3U5RjBsYVlUMnFOSVpLU0hlMmx3NG1KbzlRY3g5U3BwbnVra2NYT0oydmNibE9IZWVuQmNVQ0UrUGFFQmU1c3NvT2Zjb25zZGF4Q3l5bDlOTVdIeldKVXIvQUo0WENvc1RoK2NZTCtTZEF6WDB6cjhFN2RVTVZMVWJ0NkxQb21xZUoxODdsUFF6ZXdWa1lkQS93Y3NLMGdwVFdZU1ExOXU2RnBIb2ppbnlyZ0dJMUdIMWpOMDlNOHR1T0I0amtWb3ZqZEo4bDZlU3R3bkZvMjJPejVtcTV4OEQ3aDlGVGFJaDFOb2hvaFBYU2VGVGlEOVRGKzZMdVB3WFNoMHJWQmRwZmpyelRNTjQ4UHBScTZlUDhBM25tNjVSY3h6WGI3YkxxenU2T0NiWWc4VXkxazEyOVg4RThiazV3c1dJeU1PVUtyaXpEYVZVRzdtQjNsWlZaQmErbk8vZXFuY1lGTEVjekdGdHVDcTRleTVwS2ptRnBHMktERDJTYks3TE9VTDczS2hvcGM5eVBKTWpBQkxyZmFDd0xTcWlMWmd4OXh2RzlUVTBVczlFelhROEFOclZxRy9JdW0xUHJxY055TXJXc0plMzc0K3Q1NzFSVHhIRmRHTVFqcUlUdEdyZmV5ckthVHR4MkkzaE96QTVNcEE3WE5PSUlPM21ua1hCc3BicCs1WnQ2ajRLQ1ZwczFNc1ErUE1GUVMzT3AycWdrQkdvVkkxNWMwSHlWTGF6ZC9OTWpPWmpGcWZwSXJlU0VQTUlmVmZaZUxWUFR2c3B0YmYyaDdEN3BWYXlNUE5SK052ODFEV3hhcXQyRS9XOEZoZU9Oa3FzTGEyS29jTDJIZGVzYjBNeEo3SU5aQThIdHQ4SGYxVHNRZmFyZ1kxM2k5bjlFT3pJOWwydjdyZ2dMNWR4Nm5oU2NGSTN3UmFnZzlNa3ZkcWVBU3dKekwzWW04RTlwdGE2RXV6WW55ZHFNZkJWOGU2RStheEpuWjFaVTc5a3NDbmE4bUNJK3F4UERIQmhwWDI1S2txSWNranNwS3E0SUhQYS9XeEg0TEQ4Y2lkSFdzMitEcmJRbllkS2NycnRCN3lJTnc2NDRMV0F1alA0Vlp4emJFVDFCUk8zaFJPM0ZTTzdyMVVzdmRtemtnUVE0SnNvUFpXL3NLYU8vWlQzbXhDbFlOb3VvbmJIeHFscVczajJIa3FxRWx6STh3VXNMaURFNEtKNHlUd3FHY0dXbG15bmtxNmp2Ry91Zld0NHFuckduVmd0ZnhDeEZqWFhpekRqWlZrTHM4Y0J0eUNxSVNidElLLzRtM3pSNmluTlJhdWFqZjNsUlZJMmdKcnhlRnlxSWI1NGJoUXkzenNVYjc2c0txaEhaMnFWaHRKRW1XN2lnM09qQ3d5cTcwUXVvSmdYVXJzcFdPVUYzd1FFZ2VMVmlVUkxLbUJ3UE5xcXp0Ykdxd3RPZW11bzM5cDlPUXFkOXl5UGFvNy9BRVNIVTRkV3pZbk5SQjJGU004VmZZU29KMjJjcVdjRnpOaFU5UDNSZEZuZVlvMzcycU4yN3FjdzlseWV6WVV4NDJyRDY1bHBxWmp2UllmTmQ5TGVNOGxpVkptMWVXUWZGVkVUclRVOGpPZVZWRFhuT0x0NGxSMUhmcHg2ZFFzbXBybGwzTGlod1Z1cHpQRmMwSGphcWVmdk1DamZ0aU5sVVE4MU1ObVZZUmd6RExqT0swdEkwQzVOVlV0ajJmaUs2RXNHemUwZEptR3ZMZnEwcjNURStXUUc2NkM0M2xuOXBLdzIrdTNDWmJMbzZ3dUlqUmZBOFR4V2Erek93VTBmNXV1Zmd1bFBGQnFkRjhCd3pDVy9iY0RVdlA3MW0vQmRQbFhzbTA2eS93Q25SUU4vOFYwcGFTc2EzR3RQTVFrRERkcllwZFgvQU5nQ3hDdW5kVTF1TDFVcjNkOTB0UTRrL21WVlh2N2ZOYi9WUDlldm11YUJUZHR1cmduaGNWaG1EMHByc1d4R0NraGJzTTFUTUdOdjVsZEVPQWxqTVM2Uk1NYVhzek4xVStzMmZndlpkQ09GMVBzK3V4V2NYK2tob1A1RjExb1BUR1JtQWFBWXJWMitqa3FhbU9FTzlCbUswOG5CR0VkSGVDMC9PZW9sbS9tMWROK21iblJWV21qNkNFbTRwOEhqRk9OMXU4TzBmVXFTdHFYVkZiTEpPOXpydWtua0wzRStaVmhzM2VhQTNBZnZKMXV5emN2RnpFN2FqOW15Y1BCTnYzN0szaittSExRclFxbjlwMG54cjJacExnUDFhUjk4dS91dEs2TjhNZkpUNkk2TllqaTcyTzdFejdVOFR4YmYycnUzN082dWtIcGl4UmxacEpYc1pUVTBqblVPSDBveXhRWCtMbmU4VlZUU2hzc1dlNTJrV0I4MWhZcEh5elJCcmcyK1lYdUR6OENuUHZxS3FNK1lJL2tzVHBRV3lSRDBlRUdPN2JsMnljNDVKbmpkUjhGN3h2NXB0OXNqdnpRRzVNRGJCbytLN1ZnZk5OdG5LLy9FQUNVUUFRQUNBUVFDQWdJREFRQUFBQUFBQUFFQUVTRXhRVkZoY1lHUm9SQ3h3ZEhoOFAvYUFBZ0JBUUFCUHhEWFE2TlpSTzVNYVJ5YTB2eGY0YnJSR1VJR3RRTGRDQ201Q1dyOFRNVlhMTGxVK1lUaUNNNC9DV0s3Z0JtUm1veU9HZFUwZ2pMc0JORTZJMjhjVWRsU3BWeWcvVi9nMXFkQkxyb2dIRVZYUVJNd3dxdE9adW9LaGFoQ2NNS3FacUlXM0JTcmhOSUVhcWZnSHl5cmQvQTR3eHJjQk1QNW8vRE02bFFlYzNwdkkwaEJqL2FJUVlrOUNhOUpEcE5oRVd6RE1YTnVOUnFCcDlJUk5oQjVobWtoeU9CYjVrS0ttYUZHYkpjd2VnaUc5eElRS0NMZzVzUURTTjlEWTR5U1ZvWmdabExJRy80YzZDWEFxd0xEMXBEZ21yT3FRT2tERjNqWmpmWmxpU1FNYlk0aTNFRHBCWGJPK1FUVzBDbG5VdU9KcVVwTTBwRGNKbXJpVGJpR3pWcUx1SVpBb3hOU1VISmZ4WHJNb1dQTUpqa2pYWXBlTG1yRTJoRzFrSkEzWlJWSzlLd3Z0emcvdUNBczBXM3RNUVVMNXFmWVI0MXRtL1ZMaThiR1hzR1dEYnVFWDdaUytqZFFQelczOXRKZFhQU2dZUzBObC91UytlTFMzeEdidnVQclRjVzhhQ2ZtT2QxRDk2LzFISGRvRi93Vk5nV1ViK3hLSjEyemZaL01jbm5OL3dDWlU4R3RIUFlSUkZnNklYNFNJdzRXL1hDWmhvMS9aREZhSzJ1WlphSG0rbGZ4THBjMFNiNFhLc0wwVEpwY3FWOFIyeEJneWZ1WHFaTkxRL1VOR25kOU1mTlE2dHFqS0U1alorNERWOWYxWTh0R0ZSemJJZkRtbUR4WkJlNHE4Sm1yMXBFR3FJcmE4b3J2cFJiTWJrWnlWTTUvU0RqQTNWUjd3UkZRQWFXZkl4ZHhOUWZVWGI3ZWlzZG8xeWNKbFE4dis0TUNQdWJQM2VXRGVPMkNGVGU5UzFaUFVyVmFrUXJGR2lvNEpaR0JxWFlUYXJ0ZndtY2JnUDhBMkJVOVJUNUdhZzVQbmdSOEIwSWVTdyswZHdtVmhMdVlyU2pZV2lOVy93QXlnNEQzWnRpR1I3SlVkYXFaZVZzbXlRUFYyTlhPYjVsSlVoSHNObFlTODJoYTF1cGdkTWNDaWJudGNSSkMrNXRhZFpVeEVyc2QrTExqa1pJM3d2cWpxMkppTk55RFpSZHFrV29YRVhEbXlUME0rYnY1eVN5V05WUkV0WnJKVXNnNTJXSnFlMHdocjBNY0NEVWpmQW5EakMxaGpzYWNaQ21VUE9IWUhzSFRYYU9LR256OFk4bE95d3ZDdmFDVGNCVWFub1NYS3VBN1dEMzlDak9nc284YTdoMjkzeUZ2YVVqZEh4bE03cm1DS2gzQUd2aVg0MXhpd0pURUpoQzhZYndIU0RKRXNpdURDVUhQNmtYVjk2VVl3Q2R0MzlRclZQR2tEc0ViTzltR1VOWWFXb3dPNUN4dW5tNHJEWGRCOVJVVHFkYnJ3WWVzMkRBWUJaYXZNWGR2azVKQnNrUk1mSHlsbThPMWZhRVR1dXlaZTh4eERPOEVxRWdxbVVsekdCaTFzYVFPaE14SUQzeHl1UHVXWUpCNmxCOTdJdHEyY0plc29iSkRDZFlwRXd3YjFCSzBOVTFoZ1JzYWlZV0FhUFVEblBMY2lhQUlNS2V5TVhLTHlqTUVXVnhJM2xJYkRsYkFadEJwOHJtTXR3WlByZnBpVVRBUEV1SlZDMnljMUNGQnBDVVFkd0ZxOE1NMmdZdUtXeXlwQVlmMEpsdERva2M0aGs1ZVJLb2tEbVdWdU0zMm9iQUpCdVBDWUVGNjdIRVpaNjR2ZUFLQ1RORXVVMWczL0NGeUFVYTFMSkVHSCt0bU9SRmxucGswRVJOT3BUUEt2NGdhdXh5TlNPVWFEVXJzMk5vWDBCbFhVbWVrNjl2NEE3S0lCTG5CNGp3MHVvNjdReGUwTkxxV1Z2QmxpczhZU3dWOEFneFdPV3pVM25yRWxxVkVsTGdpM0NjVW5rZ3hrZ09yM09KWXVJMC9jbDAyQ1hHZFlMS2w4eVpJRGhVNitJd0xyZXlIeEkvQ1RUVmdHNjRBNTViTDl6Qzl6cGpJQlBFV3FVN0k0aGE4UjdVVFJDV0VmQWpINk1lbjJDUGhMaUtST3p6R0l2TlJjU1o0ZkVxVUtBb0U3VXg5VllBZ2cxRGhZU2FuQmtTVW43cVBWSFQ4TjI2U29peVl4Y3NJVzVNcWtSbW0xaW10d01ldGNoS2NQc2xvVThSUk1maVBuL0UxU0dIeWpEMm11b0pQWU5wcVZaYmYxQUtoeXNSRks5a0dxLzNFVmJldFkzbGJCdXhyQ096WEZSOXdLWWdLRUFMQ1VyTXVydEhsb3gzTGh4bUpEY0NFYmlYelY2bWVOanFDbXVJbkVZQXR1b25tMC9US3JYaHNqSzhjbU9HZTJiZmlBSUJQRUtXUTdpU3QxNm82L2NwVXpxTjRNTlRuUkdYQ0pFMUkxMkhxREt3cVNFTE53eE5XR3BWaDZJOVJCZ1BLQ0NYTHQ2NWo2Y2hzRytDR0FzK0lGRjhwVHhyVlY5WEQzR2xZZHJLYlVYaG16bXk4bUM2bE9iSk91ZGxIMlM4czRvaTRWelBkeEpMMm11ZGRTVmhUazMrMkJpNkxCSVBlRU5heFJBMTBsOXNHUm1iY0orSUVkWFJhRnVhTldWMFoyeGlMU3hzb2FXSXpyb3RiNXBVOVRSYnZzdWFJanQzYkd2VWFHR1E4eURhWFVYa3N0SU1xcXIzS2lackZZVkxLbm1qUzdndCt3NFpnM0RzaGhaT0t6SDBBRHZhZy9Nc3ROR1ZsNmUwZkNWMFdJQ3h6ZUxXcmlXUkZoZkZ5OVVDRXBpcEJTR2tEZXBSdFN2YkMwY3NFTXAvcHBxK2tBcXZGRUNsbUp0R09peDdUUTFObXdYNGJQTU5Ed1dHQytVcEE2c1VZMytZNkdIaUYya2JpOWI4UURyTzMzTURTQSthekN5aFVNTjRPdWxxai9NdEZRRXJWOXlza0dpZjZtWG5ob3M3NWlBd2ZNLy9FQUNJUkFBSUNBUVFEQVFFQkFBQUFBQUFBQUFFQ0F4TUFCQVlSRWdVUUZCVVdJUC9hQUFnQkFnRUJBZ0FTMjNYWFczVzIzWFcyV1dXWFgzMzNXV2QrOWx0dGx0dHQvd0JIMFhuVVgzWFhYMzMyM0NRU2lXMjB6R2EzdlpiY1pqTmJkZGRkZGNKN3JoT0o3N3hQejJzTXZjeUdXd3lHVzR6bWEyNjIyNis0VGllOFRDY2c0VGhCR2NrbjBTZlJ3bm5rdFpaWmNKUk9ZK2hqcXJhTXdtTXgxR0dzeFZHTXcxVjlESFZWVUl6RU5GK092aUc4UWZERHdVVzFuMmt1MTVOdUhiZjRBMjMvQUNZMnZGdG1YYkRiYlRiQTI3L05uYXAyZEZ0YitYZlJ2Q1hHdGkxOE91MDhxS2ROTnBadkh6YUw0VWdIall2RkRSSjRsTnZMdGhmQmZqdDRsL0Z5ZVBNakI0akI4bnlwQkhBTVdmT3RiSW1yRzRsM0pCdURUK1ZFM0phUjVHSjljQlZWVmlRd3RvL2pHaStNNlo0Wmtsa0FRNmJVd2VVaDhsTjVLVFZZZjhEQUZNQkJFb3hCeVNjbVh5T203UXpST2txdTJGd1FNNXdFRkdSMWxSamlQeWNPTms4V28wdlhUVEt5Smd3SDBTWE1pekNYdXM4V3BYVXJLa3F5aG1ENU9rc1ZjTG82NmdIcURjWmJDd1lNSDRWRVdOZ0FSTGE3U0dSREZHaStrS3lqQzNQUElaWkVkTVVCZ1JMZDNMR1Y1R05va0Vna0JRcm5QWU1ENkdCNDlTa29IUGJudVdPSEMzcnRZanJLU1R5Q0NHVWdjS1k1aFBoR0U4NGNLbk9RUWV4QjljREJnSUlPREFjRGQreFp0UjlSMVoxbjFmVFlHNDRJNDR3WU1CR0RBclMvVWRTMDV5cXFxc2poVDZJL3h3TTVPcE9vMDJwZlQvTTJsd2tudjM3ZHdBcUwvOFFBTlJFQUFnRUNCQU1HQXdZSEFBQUFBQUFBQUFFQ0VTRURCQkF4QlNCaEVqSkJVWUdSSWpCeEV6T0NvYkhSQmlOQ1EzS1M4UC9hQUFnQkFnRURQd0JDMVk5T3VpMFpJNm5VaWlJdEdTSHR5TGxSQkNFV0dQejA2aVF1WmpvUFdnL0FZeW5qcGI1blU2blU2ajh5dWk1TGI2TXNXMWZucTBkZFh5STY2V0hxeGpvUFJqMHBxOUdoakpESFFhSDVEMWZrSmpHVzJIcFRSakdTSG94a3hra000ZGliVzlUS1UzYjlVWk5LdEczMU11MjI0dW5Rd0hLMWZjeUQ3OG1qaHM3L0FHcmYwTWpGZkRKc3lNSTFsR2I5YUhEZXpYc1NSdzZqZC9lbjZuREl0OXR5WC9kRGdNNGZlWC9GVTRUdkhFOTIwY0xVdDEvczZHVGhMdFljSXYzZjYxTW0wNVN3bFgwL1k0WldrNHRmUS9oNksrUHRlNXdGdW1IaE4rdFRna08vZ3YxZjdNNGJqcDlpRVV2OHFNeVVKVjdOZnhuQ0ZIK1pocjFiZjZNNEUvR0MrdmJJb1VkaWNIWm1QRmJrSXY0NFZPRlM3MkhRNFUrN2IwTXROZkN5RkxFNUpxaEs3YUlwMlJMZEdJck9OVEdtclFabll1c0lzNGoyS1NpbDlXWnFlOGZ6SnpYeHVoa2wzazM2bVNpdTRqS1IvdHIyTWxUdUwyTXF0b293dHhOQ2tJcVRkMFl5OERNTGFMTTMxWDFabW9QN3htYWpiN1JrcGJzcWlUSDRtYXkvM2MvZTVtNFZVL3lKSjN1VjNSZzRxSVNFSkRhZEJMdk5HWGwvWHlMUkVHaFMzUW5zUjh5TFc1UW9Tb3lVRTZSRTVOQ3I1RTRrOExaRFNvMFFrdDZHRkMwYnN4OFhkMFhUbnNTcU5ibHJNYXMwUmtVclFZcUN1S2duOFNKd2RIcEhkRUdoTFNxSXRNcXE4eWlKYkVYR2paRitJbGRNZXpFeGtxREZKTkZDV0d4TjBrUTdWeFNWUmxHV3N4YUlmZ01lazByTW1tVEpsYmpHSm9RaE5PeFJ1eFIxUTFhUktONHNyM2tSbHNVVDBRaFU1UE1UMGNTbTRub2x1UWFJZVl2TVVpanNJVktQUnhaUzBoUG5pK1N3NG9US29hcUpEbDRsRTZEVytpWXE2V2RTaXQ4aG9rdHlFdkVhUlRWamU1WWxvcWFVMGFUTFg1bUlXalJPSlVURU1mSWhwYStSSkxrYUh6MjBwb2hKWE1HRzhrWmQrSmdwV1loM3VZc2liSk5WK1l6RHc5MlFIVFl4NTdPaE43czY2UjhXTHpJZVpId1luVkk2ZkpvcW1HaWZlcVlrNVVrcW1WZUUzU2pKcGZESmZtWmlGYW9veDFKRW1MeUpMd0pNYlphcEpXUC94QUFpRVFBQ0FnRUZBUUVCQVFFQUFBQUFBQUFCQXdJVEFBUUZCaEVTRUJVZ0ZCYi8yZ0FJQVFNQkFRSUFDd3FvS3FwQ1FvTENxZ29LQ2FxUW1rSnFFS3hBUThCUVVFaFFUUlFFVUJJVUVoTk5OSVZWVUYxK1BQZ1JDUWdKQ2dxa0lDQWdJb0NLQWdKcHBxcnI4R0hueDVDd2tKQ1FrSkNBZ0lDUWdKcHFycnJxcUtxcWFxNjRyQ29yRUlyaXFLZ3FLUW1LYUthYWFhcXFhYWFLS0tCZ0lNU0RFQVJqRVJBQVhWWFZWVlZWVlRUVFJRTmVONk85UTNtTytIa00rWHE1aWVXTDVRdms0NUtlVURsLy9WTjVLbmtnNUZQa3Y3NDVBT1NqbFUrUkRrQzlWQ1lXZEZQYjM3ZTVNc2k5T3FSdUtkV05YTnY2TE4wT3QvVU8reTVDZDQvVmh1U3Rlcld4WERJTUR4cWY5RW5UWVJKQXlKN2d5ZWpQSFR4MXV3YWpiQ25JQldLRVYvTys1R2VTbXQwWEI1MUVYaGtHSmtoYzRTelVLZG9XYU5PM3Ewb3lQOEUrams0eGlWOUdSaURIRlMwR29HT1U1TTlPRlF3UTZ3ZnhLSmdZQlpoNEsvSXlPUU9sY2pVZHZWSUdXRFBHQ1BueUllUEVZQmNrVDB4Z1ZsWFVDckVUV3dUWkNjU3FPUmtZQllXRmlQUkFqaFlaTWljNnJDNEJXTGtHU25QQmdIV0NBSFhrUnJLejhJeXVnS2lJcmd1SUt5b29waU1BRVFPdXZwakpKaDBBSWlJaEdNWkNRSXlKODFTUkxUREJuWFhub2o0Y2tzckdBaVlJd0RBWXpCR2RkRDRNQi9qcnljNnp3SUNJeUlBRVFvS0NoRG9ZQ1BnUGY4RWxrVDVoRUxHQmxsZ21ERTRNSDBZUG95enZ3MU1YQm9hSXhpSWlBaDE1N0JKLy84UUFOUkVBQWdFQ0JBTUdCQU1KQUFBQUFBQUFBQUVDRVNFREJCQXhCU0JCRWlKUllZR1JNREp4Z2tLeDhRWVRVcEtob3RIaDhQL2FBQWdCQXdFRFB3QmoxZEJqS0RLaTA4aCtBeDZvUzBYSXhqR09uSWhMVitBOUZRUWhDRnl2blhVVk5IcDVEOE5HTVl4bE9aYzFPZDZQbHNNWjVjaTBYSXRIMEdNOGp5MVl4OHFFSVJCQ0VSWXRGVFJqV3FGb2hDRUxUaVdIdmYwTTV0UkwwWm5aVzdTUzhrWmxSdEplcU15b3V0UFk0aXJ4Z3BVLzd4UnhLRnYzU1gxL1U0akxlRVVjVG5hTTRMMHFjVzI3Y1g2STRxck9uc2NXY2U3MmEvUS9hQ09JMzJMZmJRNHh0TEQvQUtIRnV6dC9halBUaTQ0azVMMlg1VU0vWHN4eFhUMS95Y1NTN3NrL3FjZms2UVVWNkhHbGVlSWw2SEY1L0xpcjIvMFovQ1hmazM5cG1aUnBXbjJuRXBTN2s2K2lYNW80dlM4WlArVWs5eXRtWWNsZEdETG9TYXBDZERpa2E5bkVxY1VpcVNkVE14K1pFcTNJUlpIWk5rNUxka2RtUnBWTXdZTzgwWldhcEtTTWlwVmpKdjBNckJXYjlpTVBrVmZxWng3Tkl6ci9BQk16Yi9HL2N6ZjhiOXpNdmVUTWJhNHlpR2hpTVBxekFhZmVSbHZxWmFWZTRqS3QxN0M5aEw1YkNWUlhFdGpLWmo1NFg4ckdWbGVLcVlSRlJzekV3V3ljQmphRW5jazEzRXpOUlZlenl1aEltbU5Sdm82RFFoVXJVUmh6ZDJNc0loTWd4UjJNV1Y1VVNNREMyVldlaFY4akVLOUMxVHlFeVVTcXZwSklsdXp2YkQ3TkdKM0U3a2syWWgyS3VtblpxU3FQbHFUWXlneHZSclJDanNPSlZDYUxOaktNVjZGVXhkQ2cyTXErU0hVZzBRb0pIUXU2Nk5GdEhGMUxXWW1ya1duUXJabG4yV1N1UHFOb2VqNVdrTVJadUpJWktXeGlKa3lTUk9LMkUwTkZhNlZSUXRibFdrbHBYUmlsWFNndW9uZGFLZ21WSFVtaVNXbS93RXhkQ1VkK1h5R2hvV3F1VkZRVEwyMXNMblRHaGpvSjZKcldtdzB4VXRwVFNueEU5V1UzWko3SWt1aFB3TVVsNGlXN0lKQ1Z2aHBLNWgrSTViSW5RbStwaHJlcklMWkZlZ3ZBbFRZZmdUOENaSmZDaGNrOWtLVm1LRWFwbU5DZEsxUkhacG1ITFpsbXhVM0VpS09xWW10eUtMZnJvdHovMlE9PSIgaWQ9ImMiIHdpZHRoPSIxOTkiIGhlaWdodD0iMTU5Ii8+PC9kZWZzPjwvc3ZnPg==", + "description": "A beneficial layer in the upper atmosphere, but harmful when present near ground level. Results mainly from vehicle exhaust and industrial emissions.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'ozone', label: 'Ozone', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Ozone\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"public\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#3B911C\"},{\"from\":50,\"to\":100,\"color\":\"#7CC322\"},{\"from\":100,\"to\":130,\"color\":\"#F89E0D\"},{\"from\":130,\"to\":240,\"color\":\"#F77410\"},{\"from\":240,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":28,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#3B911C\"},{\"from\":50,\"to\":100,\"color\":\"#7CC322\"},{\"from\":100,\"to\":130,\"color\":\"#F89E0D\"},{\"from\":130,\"to\":240,\"color\":\"#F77410\"},{\"from\":240,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:b3pvbmUtdmFsdWUtY2FyZC1iYWNrZ3JvdW5kLnBuZw==:b3pvbmUtdmFsdWUtY2FyZC1iYWNrZ3JvdW5kLnBuZw==;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Ozone\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "O3", + "ozone" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/ozone__o3__chart_card.json b/application/src/main/data/json/system/widget_types/ozone__o3__chart_card.json new file mode 100644 index 0000000000..9e67c7e523 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/ozone__o3__chart_card.json @@ -0,0 +1,39 @@ +{ + "fqn": "ozone_o3_chart_card", + "name": "Ozone (O3) chart card", + "deprecated": false, + "image": "tb-image:b3pvbmUtdmFsdWUtY2hhcnQtY2FyZC5zdmc=:b3pvbmUtdmFsdWUtY2hhcnQtY2FyZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSIxOTkiIGhlaWdodD0iMTU5IiByeD0iMy41IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC4xMiIvPgo8cGF0aCBkPSJNMTkuOTk5OSAxMy4zMzMzQzE2LjMxOTkgMTMuMzMzMyAxMy4zMzMzIDE2LjMyIDEzLjMzMzMgMjBDMTMuMzMzMyAyMy42OCAxNi4zMTk5IDI2LjY2NjcgMTkuOTk5OSAyNi42NjY3QzIzLjY3OTkgMjYuNjY2NyAyNi42NjY2IDIzLjY4IDI2LjY2NjYgMjBDMjYuNjY2NiAxNi4zMiAyMy42Nzk5IDEzLjMzMzMgMTkuOTk5OSAxMy4zMzMzWk0xNC42NjY2IDIwQzE0LjY2NjYgMTkuNTkzMyAxNC43MTk5IDE5LjE5MzMgMTQuODA2NiAxOC44MTMzTDE3Ljk5MzMgMjJWMjIuNjY2N0MxNy45OTMzIDIzLjQgMTguNTkzMyAyNCAxOS4zMjY2IDI0VjI1LjI4NjdDMTYuNzA2NiAyNC45NTMzIDE0LjY2NjYgMjIuNzEzMyAxNC42NjY2IDIwWk0yMy45MjY2IDIzLjZDMjMuNzUzMyAyMy4wNiAyMy4yNTk5IDIyLjY2NjcgMjIuNjU5OSAyMi42NjY3SDIxLjk5MzNWMjAuNjY2N0MyMS45OTMzIDIwLjMgMjEuNjkzMyAyMCAyMS4zMjY2IDIwSDE3LjMyNjZWMTguNjY2N0gxOC42NTk5QzE5LjAyNjYgMTguNjY2NyAxOS4zMjY2IDE4LjM2NjcgMTkuMzI2NiAxOFYxNi42NjY3SDIwLjY1OTlDMjEuMzkzMyAxNi42NjY3IDIxLjk5MzMgMTYuMDY2NyAyMS45OTMzIDE1LjMzMzNWMTUuMDZDMjMuOTQ2NiAxNS44NDY3IDI1LjMzMzMgMTcuNzY2NyAyNS4zMzMzIDIwQzI1LjMzMzMgMjEuMzg2NyAyNC43OTMzIDIyLjY1MzMgMjMuOTI2NiAyMy42WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNNDAuOTQ4MiAxOS43NVYyMC4yOTY5QzQwLjk0ODIgMjEuMDQ4OCA0MC44NTAzIDIxLjcyMzMgNDAuNjU0MyAyMi4zMjAzQzQwLjQ1ODMgMjIuOTE3MyA0MC4xNzgxIDIzLjQyNTUgMzkuODEzNSAyMy44NDQ3QzM5LjQ1MzUgMjQuMjY0IDM5LjAyMDUgMjQuNTg1MyAzOC41MTQ2IDI0LjgwODZDMzguMDA4OCAyNS4wMjczIDM3LjQ0ODIgMjUuMTM2NyAzNi44MzMgMjUuMTM2N0MzNi4yMjIzIDI1LjEzNjcgMzUuNjY0MSAyNS4wMjczIDM1LjE1ODIgMjQuODA4NkMzNC42NTY5IDI0LjU4NTMgMzQuMjIxNyAyNC4yNjQgMzMuODUyNSAyMy44NDQ3QzMzLjQ4MzQgMjMuNDI1NSAzMy4xOTYzIDIyLjkxNzMgMzIuOTkxMiAyMi4zMjAzQzMyLjc5MDcgMjEuNzIzMyAzMi42OTA0IDIxLjA0ODggMzIuNjkwNCAyMC4yOTY5VjE5Ljc1QzMyLjY5MDQgMTguOTk4IDMyLjc5MDcgMTguMzI1OCAzMi45OTEyIDE3LjczMzRDMzMuMTkxNyAxNy4xMzY0IDMzLjQ3NDMgMTYuNjI4MyAzMy44Mzg5IDE2LjIwOUMzNC4yMDggMTUuNzg1MiAzNC42NDMyIDE1LjQ2MzkgMzUuMTQ0NSAxNS4yNDUxQzM1LjY1MDQgMTUuMDIxOCAzNi4yMDg3IDE0LjkxMDIgMzYuODE5MyAxNC45MTAyQzM3LjQzNDYgMTQuOTEwMiAzNy45OTUxIDE1LjAyMTggMzguNTAxIDE1LjI0NTFDMzkuMDA2OCAxNS40NjM5IDM5LjQ0MjEgMTUuNzg1MiAzOS44MDY2IDE2LjIwOUM0MC4xNzEyIDE2LjYyODMgNDAuNDUxNSAxNy4xMzY0IDQwLjY0NzUgMTcuNzMzNEM0MC44NDggMTguMzI1OCA0MC45NDgyIDE4Ljk5OCA0MC45NDgyIDE5Ljc1Wk0zOS4yMzI0IDIwLjI5NjlWMTkuNzM2M0MzOS4yMzI0IDE5LjE4MDMgMzkuMTc3NyAxOC42OTA0IDM5LjA2ODQgMTguMjY2NkMzOC45NjM1IDE3LjgzODIgMzguODA2MyAxNy40ODA1IDM4LjU5NjcgMTcuMTkzNEMzOC4zOTE2IDE2LjkwMTcgMzguMTM4NyAxNi42ODI5IDM3LjgzNzkgMTYuNTM3MUMzNy41MzcxIDE2LjM4NjcgMzcuMTk3NiAxNi4zMTE1IDM2LjgxOTMgMTYuMzExNUMzNi40NDExIDE2LjMxMTUgMzYuMTAzOCAxNi4zODY3IDM1LjgwNzYgMTYuNTM3MUMzNS41MTE0IDE2LjY4MjkgMzUuMjU4NSAxNi45MDE3IDM1LjA0ODggMTcuMTkzNEMzNC44NDM4IDE3LjQ4MDUgMzQuNjg2NSAxNy44MzgyIDM0LjU3NzEgMTguMjY2NkMzNC40Njc4IDE4LjY5MDQgMzQuNDEzMSAxOS4xODAzIDM0LjQxMzEgMTkuNzM2M1YyMC4yOTY5QzM0LjQxMzEgMjAuODUyOSAzNC40Njc4IDIxLjM0NTEgMzQuNTc3MSAyMS43NzM0QzM0LjY4NjUgMjIuMjAxOCAzNC44NDYgMjIuNTY0MSAzNS4wNTU3IDIyLjg2MDRDMzUuMjY5OSAyMy4xNTIgMzUuNTI1MSAyMy4zNzMgMzUuODIxMyAyMy41MjM0QzM2LjExNzUgMjMuNjY5MyAzNi40NTQ4IDIzLjc0MjIgMzYuODMzIDIzLjc0MjJDMzcuMjE1OCAyMy43NDIyIDM3LjU1NTMgMjMuNjY5MyAzNy44NTE2IDIzLjUyMzRDMzguMTQ3OCAyMy4zNzMgMzguMzk4NCAyMy4xNTIgMzguNjAzNSAyMi44NjA0QzM4LjgwODYgMjIuNTY0MSAzOC45NjM1IDIyLjIwMTggMzkuMDY4NCAyMS43NzM0QzM5LjE3NzcgMjEuMzQ1MSAzOS4yMzI0IDIwLjg1MjkgMzkuMjMyNCAyMC4yOTY5Wk00OC40NzE3IDIzLjY4NzVWMjVINDIuOTYxOVYyMy42ODc1SDQ4LjQ3MTdaTTQ4LjMyMTMgMTguNjI4OUw0My41MjI1IDI1SDQyLjQ1NjFWMjMuOTQwNEw0Ny4yMjc1IDE3LjYwMzVINDguMzIxM1YxOC42Mjg5Wk00Ny42ODU1IDE3LjYwMzVWMTguOTE2SDQyLjUzODFWMTcuNjAzNUg0Ny42ODU1Wk00OS42MTcyIDIxLjM4MzhWMjEuMjI2NkM0OS42MTcyIDIwLjY5MzQgNDkuNjk0NyAyMC4xOTg5IDQ5Ljg0OTYgMTkuNzQzMkM1MC4wMDQ2IDE5LjI4MjkgNTAuMjI3OSAxOC44ODQxIDUwLjUxOTUgMTguNTQ2OUM1MC44MTU4IDE4LjIwNTEgNTEuMTc1OCAxNy45NDA4IDUxLjU5OTYgMTcuNzUzOUM1Mi4wMjggMTcuNTYyNSA1Mi41MTExIDE3LjQ2NjggNTMuMDQ4OCAxNy40NjY4QzUzLjU5MTEgMTcuNDY2OCA1NC4wNzQyIDE3LjU2MjUgNTQuNDk4IDE3Ljc1MzlDNTQuOTI2NCAxNy45NDA4IDU1LjI4ODcgMTguMjA1MSA1NS41ODUgMTguNTQ2OUM1NS44ODEyIDE4Ljg4NDEgNTYuMTA2OCAxOS4yODI5IDU2LjI2MTcgMTkuNzQzMkM1Ni40MTY3IDIwLjE5ODkgNTYuNDk0MSAyMC42OTM0IDU2LjQ5NDEgMjEuMjI2NlYyMS4zODM4QzU2LjQ5NDEgMjEuOTE3IDU2LjQxNjcgMjIuNDExNSA1Ni4yNjE3IDIyLjg2NzJDNTYuMTA2OCAyMy4zMjI5IDU1Ljg4MTIgMjMuNzIxNyA1NS41ODUgMjQuMDYzNUM1NS4yODg3IDI0LjQwMDcgNTQuOTI4NyAyNC42NjUgNTQuNTA0OSAyNC44NTY0QzU0LjA4MTEgMjUuMDQzMyA1My42MDAzIDI1LjEzNjcgNTMuMDYyNSAyNS4xMzY3QzUyLjUyMDIgMjUuMTM2NyA1Mi4wMzQ4IDI1LjA0MzMgNTEuNjA2NCAyNC44NTY0QzUxLjE4MjYgMjQuNjY1IDUwLjgyMjYgMjQuNDAwNyA1MC41MjY0IDI0LjA2MzVDNTAuMjMwMSAyMy43MjE3IDUwLjAwNDYgMjMuMzIyOSA0OS44NDk2IDIyLjg2NzJDNDkuNjk0NyAyMi40MTE1IDQ5LjYxNzIgMjEuOTE3IDQ5LjYxNzIgMjEuMzgzOFpNNTEuMjY0NiAyMS4yMjY2VjIxLjM4MzhDNTEuMjY0NiAyMS43MTY1IDUxLjI5ODggMjIuMDMwOSA1MS4zNjcyIDIyLjMyNzFDNTEuNDM1NSAyMi42MjM0IDUxLjU0MjYgMjIuODgzMSA1MS42ODg1IDIzLjEwNjRDNTEuODM0MyAyMy4zMjk4IDUyLjAyMTIgMjMuNTA1MiA1Mi4yNDkgMjMuNjMyOEM1Mi40NzY5IDIzLjc2MDQgNTIuNzQ4IDIzLjgyNDIgNTMuMDYyNSAyMy44MjQyQzUzLjM2NzggMjMuODI0MiA1My42MzIyIDIzLjc2MDQgNTMuODU1NSAyMy42MzI4QzU0LjA4MzMgMjMuNTA1MiA1NC4yNzAyIDIzLjMyOTggNTQuNDE2IDIzLjEwNjRDNTQuNTYxOCAyMi44ODMxIDU0LjY2ODkgMjIuNjIzNCA1NC43MzczIDIyLjMyNzFDNTQuODEwMiAyMi4wMzA5IDU0Ljg0NjcgMjEuNzE2NSA1NC44NDY3IDIxLjM4MzhWMjEuMjI2NkM1NC44NDY3IDIwLjg5ODQgNTQuODEwMiAyMC41ODg1IDU0LjczNzMgMjAuMjk2OUM1NC42Njg5IDIwLjAwMDcgNTQuNTU5NiAxOS43Mzg2IDU0LjQwOTIgMTkuNTEwN0M1NC4yNjMzIDE5LjI4MjkgNTQuMDc2NSAxOS4xMDUxIDUzLjg0ODYgMTguOTc3NUM1My42MjUzIDE4Ljg0NTQgNTMuMzU4NyAxOC43NzkzIDUzLjA0ODggMTguNzc5M0M1Mi43Mzg5IDE4Ljc3OTMgNTIuNDcwMSAxOC44NDU0IDUyLjI0MjIgMTguOTc3NUM1Mi4wMTg5IDE5LjEwNTEgNTEuODM0MyAxOS4yODI5IDUxLjY4ODUgMTkuNTEwN0M1MS41NDI2IDE5LjczODYgNTEuNDM1NSAyMC4wMDA3IDUxLjM2NzIgMjAuMjk2OUM1MS4yOTg4IDIwLjU4ODUgNTEuMjY0NiAyMC44OTg0IDUxLjI2NDYgMjEuMjI2NlpNNTkuNzU4OCAxOS4xODI2VjI1SDU4LjExMTNWMTcuNjAzNUg1OS42NjMxTDU5Ljc1ODggMTkuMTgyNlpNNTkuNDY0OCAyMS4wMjgzTDU4LjkzMTYgMjEuMDIxNUM1OC45MzYyIDIwLjQ5NzQgNTkuMDA5MSAyMC4wMTY2IDU5LjE1MDQgMTkuNTc5MUM1OS4yOTYyIDE5LjE0MTYgNTkuNDk2NyAxOC43NjU2IDU5Ljc1MiAxOC40NTEyQzYwLjAxMTcgMTguMTM2NyA2MC4zMjE2IDE3Ljg5NTIgNjAuNjgxNiAxNy43MjY2QzYxLjA0MTcgMTcuNTUzNCA2MS40NDI3IDE3LjQ2NjggNjEuODg0OCAxNy40NjY4QzYyLjI0MDIgMTcuNDY2OCA2Mi41NjE1IDE3LjUxNjkgNjIuODQ4NiAxNy42MTcyQzYzLjE0MDMgMTcuNzEyOSA2My4zODg3IDE3Ljg3MDEgNjMuNTkzOCAxOC4wODg5QzYzLjgwMzQgMTguMzA3NiA2My45NjI5IDE4LjU5MjQgNjQuMDcyMyAxOC45NDM0QzY0LjE4MTYgMTkuMjg5NyA2NC4yMzYzIDE5LjcxNTggNjQuMjM2MyAyMC4yMjE3VjI1SDYyLjU4MlYyMC4yMTQ4QzYyLjU4MiAxOS44NTk0IDYyLjUyOTYgMTkuNTc5MSA2Mi40MjQ4IDE5LjM3NEM2Mi4zMjQ1IDE5LjE2NDQgNjIuMTc2NCAxOS4wMTYzIDYxLjk4MDUgMTguOTI5N0M2MS43ODkxIDE4LjgzODUgNjEuNTQ5OCAxOC43OTMgNjEuMjYyNyAxOC43OTNDNjAuOTgwMSAxOC43OTMgNjAuNzI3MiAxOC44NTIyIDYwLjUwMzkgMTguOTcwN0M2MC4yODA2IDE5LjA4OTIgNjAuMDkxNSAxOS4yNTEgNTkuOTM2NSAxOS40NTYxQzU5Ljc4NjEgMTkuNjYxMSA1OS42Njk5IDE5Ljg5ODEgNTkuNTg3OSAyMC4xNjdDNTkuNTA1OSAyMC40MzU5IDU5LjQ2NDggMjAuNzIzIDU5LjQ2NDggMjEuMDI4M1pNNjkuNDI4NyAyNS4xMzY3QzY4Ljg4MTggMjUuMTM2NyA2OC4zODc0IDI1LjA0NzkgNjcuOTQ1MyAyNC44NzAxQzY3LjUwNzggMjQuNjg3OCA2Ny4xMzQxIDI0LjQzNDkgNjYuODI0MiAyNC4xMTEzQzY2LjUxODkgMjMuNzg3OCA2Ni4yODQyIDIzLjQwNzIgNjYuMTIwMSAyMi45Njk3QzY1Ljk1NjEgMjIuNTMyMiA2NS44NzQgMjIuMDYwNSA2NS44NzQgMjEuNTU0N1YyMS4yODEyQzY1Ljg3NCAyMC43MDI1IDY1Ljk1ODMgMjAuMTc4NCA2Ni4xMjcgMTkuNzA5QzY2LjI5NTYgMTkuMjM5NiA2Ni41MzAzIDE4LjgzODUgNjYuODMxMSAxOC41MDU5QzY3LjEzMTggMTguMTY4NiA2Ny40ODczIDE3LjkxMTEgNjcuODk3NSAxNy43MzM0QzY4LjMwNzYgMTcuNTU1NyA2OC43NTIgMTcuNDY2OCA2OS4yMzA1IDE3LjQ2NjhDNjkuNzU5MSAxNy40NjY4IDcwLjIyMTcgMTcuNTU1NyA3MC42MTgyIDE3LjczMzRDNzEuMDE0NiAxNy45MTExIDcxLjM0MjggMTguMTYxOCA3MS42MDI1IDE4LjQ4NTRDNzEuODY2OSAxOC44MDQ0IDcyLjA2MjggMTkuMTg0OSA3Mi4xOTA0IDE5LjYyN0M3Mi4zMjI2IDIwLjA2OSA3Mi4zODg3IDIwLjU1NjYgNzIuMzg4NyAyMS4wODk4VjIxLjc5MzlINjYuNjczOFYyMC42MTEzSDcwLjc2MTdWMjAuNDgxNEM3MC43NTI2IDIwLjE4NTIgNzAuNjkzNCAxOS45MDcyIDcwLjU4NCAxOS42NDc1QzcwLjQ3OTIgMTkuMzg3NyA3MC4zMTc0IDE5LjE3ODEgNzAuMDk4NiAxOS4wMTg2QzY5Ljg3OTkgMTguODU5IDY5LjU4ODIgMTguNzc5MyA2OS4yMjM2IDE4Ljc3OTNDNjguOTUwMiAxOC43NzkzIDY4LjcwNjQgMTguODM4NSA2OC40OTIyIDE4Ljk1N0M2OC4yODI2IDE5LjA3MSA2OC4xMDcxIDE5LjIzNzMgNjcuOTY1OCAxOS40NTYxQzY3LjgyNDUgMTkuNjc0OCA2Ny43MTUyIDE5LjkzOTEgNjcuNjM3NyAyMC4yNDlDNjcuNTY0OCAyMC41NTQ0IDY3LjUyODMgMjAuODk4NCA2Ny41MjgzIDIxLjI4MTJWMjEuNTU0N0M2Ny41MjgzIDIxLjg3ODMgNjcuNTcxNiAyMi4xNzkgNjcuNjU4MiAyMi40NTdDNjcuNzQ5MyAyMi43MzA1IDY3Ljg4MTUgMjIuOTY5NyA2OC4wNTQ3IDIzLjE3NDhDNjguMjI3OSAyMy4zNzk5IDY4LjQzNzUgMjMuNTQxNyA2OC42ODM2IDIzLjY2MDJDNjguOTI5NyAyMy43NzQxIDY5LjIxIDIzLjgzMTEgNjkuNTI0NCAyMy44MzExQzY5LjkyMDkgMjMuODMxMSA3MC4yNzQxIDIzLjc1MTMgNzAuNTg0IDIzLjU5MThDNzAuODkzOSAyMy40MzIzIDcxLjE2MjggMjMuMjA2NyA3MS4zOTA2IDIyLjkxNUw3Mi4yNTg4IDIzLjc1NTlDNzIuMDk5MyAyMy45ODgzIDcxLjg5MTkgMjQuMjExNiA3MS42MzY3IDI0LjQyNThDNzEuMzgxNSAyNC42MzU0IDcxLjA2OTMgMjQuODA2MyA3MC43MDAyIDI0LjkzODVDNzAuMzM1NiAyNS4wNzA2IDY5LjkxMTggMjUuMTM2NyA2OS40Mjg3IDI1LjEzNjdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik01NC4yMTY4IDU4LjMxODRWODRINDkuOTgwNVY2My4zNDU3TDQzLjcwNTEgNjUuNDcyN1Y2MS45NzQ2TDUzLjcwNyA1OC4zMTg0SDU0LjIxNjhaTTcwLjg2NTQgNTguMzE4NFY4NEg2Ni42MjkxVjYzLjM0NTdMNjAuMzUzNyA2NS40NzI3VjYxLjk3NDZMNzAuMzU1NiA1OC4zMTg0SDcwLjg2NTRaTTk0LjI2NCA2OS4wNTg2VjczLjI0MjJDOTQuMjY0IDc1LjI0NjEgOTQuMDY0OCA3Ni45NTcgOTMuNjY2MyA3OC4zNzVDOTMuMjc5NiA3OS43ODEyIDkyLjcxNzEgODAuOTIzOCA5MS45Nzg4IDgxLjgwMjdDOTEuMjQwNSA4Mi42ODE2IDkwLjM1NTggODMuMzI2MiA4OS4zMjQ1IDgzLjczNjNDODguMzA1IDg0LjE0NjUgODcuMTYyNCA4NC4zNTE2IDg1Ljg5NjggODQuMzUxNkM4NC44ODkgODQuMzUxNiA4My45NTE1IDg0LjIyMjcgODMuMDg0MyA4My45NjQ4QzgyLjIyODggODMuNzA3IDgxLjQ1NTQgODMuMzAyNyA4MC43NjQgODIuNzUyQzgwLjA3MjYgODIuMjAxMiA3OS40ODA4IDgxLjQ5MjIgNzguOTg4NiA4MC42MjVDNzguNTA4MSA3OS43NDYxIDc4LjEzMzEgNzguNjk3MyA3Ny44NjM2IDc3LjQ3ODVDNzcuNjA1OCA3Ni4yNTk4IDc3LjQ3NjkgNzQuODQ3NyA3Ny40NzY5IDczLjI0MjJWNjkuMDU4NkM3Ny40NzY5IDY3LjA0MyA3Ny42NzYxIDY1LjM0MzggNzguMDc0NSA2My45NjA5Qzc4LjQ3MyA2Mi41NjY0IDc5LjA0MTMgNjEuNDM1NSA3OS43Nzk2IDYwLjU2ODRDODAuNTE3OSA1OS42ODk1IDgxLjM5NjggNTkuMDUwOCA4Mi40MTYzIDU4LjY1MjNDODMuNDQ3NiA1OC4yNTM5IDg0LjU5NiA1OC4wNTQ3IDg1Ljg2MTYgNTguMDU0N0M4Ni44ODEyIDU4LjA1NDcgODcuODE4NyA1OC4xODM2IDg4LjY3NDEgNTguNDQxNEM4OS41NDEzIDU4LjY4NzUgOTAuMzE0OCA1OS4wODAxIDkwLjk5NDUgNTkuNjE5MUM5MS42ODU5IDYwLjE1ODIgOTIuMjcxOCA2MC44NjEzIDkyLjc1MjMgNjEuNzI4NUM5My4yNDQ1IDYyLjU4NCA5My42MTk1IDYzLjYyMTEgOTMuODc3MyA2NC44Mzk4Qzk0LjEzNTEgNjYuMDQ2OSA5NC4yNjQgNjcuNDUzMSA5NC4yNjQgNjkuMDU4NlpNOTAuMDI3NyA3My44Mzk4VjY4LjQyNThDOTAuMDI3NyA2Ny40MDYyIDg5Ljk2OTEgNjYuNTA5OCA4OS44NTE5IDY1LjczNjNDODkuNzM0NyA2NC45NTEyIDg5LjU1ODkgNjQuMjg5MSA4OS4zMjQ1IDYzLjc1Qzg5LjEwMTkgNjMuMTk5MiA4OC44MjA2IDYyLjc1MzkgODguNDgwOCA2Mi40MTQxQzg4LjE0MDkgNjIuMDYyNSA4Ny43NTQyIDYxLjgxMDUgODcuMzIwNiA2MS42NTgyQzg2Ljg4NyA2MS40OTQxIDg2LjQwMDcgNjEuNDEyMSA4NS44NjE2IDYxLjQxMjFDODUuMjA1NCA2MS40MTIxIDg0LjYxOTUgNjEuNTQxIDg0LjEwMzggNjEuNzk4OEM4My41ODgyIDYyLjA0NDkgODMuMTU0NiA2Mi40NDM0IDgyLjgwMyA2Mi45OTQxQzgyLjQ1MTUgNjMuNTQ0OSA4Mi4xODIgNjQuMjcxNSA4MS45OTQ1IDY1LjE3MzhDODEuODE4NyA2Ni4wNjQ1IDgxLjczMDggNjcuMTQ4NCA4MS43MzA4IDY4LjQyNThWNzMuODM5OEM4MS43MzA4IDc0Ljg3MTEgODEuNzg5NCA3NS43NzkzIDgxLjkwNjYgNzYuNTY0NUM4Mi4wMjM4IDc3LjM0OTYgODIuMTk5NSA3OC4wMjM0IDgyLjQzMzkgNzguNTg1OUM4Mi42NjgzIDc5LjEzNjcgODIuOTQ5NSA3OS41OTM4IDgzLjI3NzcgNzkuOTU3QzgzLjYxNzUgODAuMzA4NiA4NC4wMDQyIDgwLjU2NjQgODQuNDM3OCA4MC43MzA1Qzg0Ljg4MzEgODAuODk0NSA4NS4zNjk1IDgwLjk3NjYgODUuODk2OCA4MC45NzY2Qzg2LjU2NDggODAuOTc2NiA4Ny4xNTY2IDgwLjg0NzcgODcuNjcyMiA4MC41ODk4Qzg4LjE4NzggODAuMzMyIDg4LjYyMTQgNzkuOTIxOSA4OC45NzMgNzkuMzU5NEM4OS4zMjQ1IDc4Ljc4NTIgODkuNTg4MiA3OC4wNDEgODkuNzY0IDc3LjEyN0M4OS45Mzk4IDc2LjIxMjkgOTAuMDI3NyA3NS4xMTcyIDkwLjAyNzcgNzMuODM5OFoiIGZpbGw9IiNGRkE2MDAiLz4KPHBhdGggZD0iTTEwNC42MTIgNzYuNjAzNUgxMDUuODc3Vjg0SDEwNC43MzVMMTA0LjYxMiA4Mi4yOTFWNzYuNjAzNVpNMTA0LjgzOCA4MC41NjE1TDEwNS4zMSA4MC41NDc5QzEwNS4zMSA4MS4wNjc0IDEwNS4yNjIgODEuNTQ1OSAxMDUuMTY2IDgxLjk4MzRDMTA1LjA3NSA4Mi40MjA5IDEwNC45MzEgODIuODAxNCAxMDQuNzM1IDgzLjEyNUMxMDQuNTM5IDgzLjQ0ODYgMTA0LjI4OSA4My42OTkyIDEwMy45ODMgODMuODc3QzEwMy42NzggODQuMDU0NyAxMDMuMzExIDg0LjE0MzYgMTAyLjg4MyA4NC4xNDM2QzEwMi41MzYgODQuMTQzNiAxMDIuMjI3IDg0LjA5OCAxMDEuOTUzIDg0LjAwNjhDMTAxLjY4NCA4My45MjAyIDEwMS40NTIgODMuNzgxMiAxMDEuMjU2IDgzLjU4OThDMTAxLjA2NCA4My4zOTM5IDEwMC45MDUgODMuMTQxIDEwMC43NzcgODIuODMxMUwxMDAuNTI0IDgwLjg1NTVIMTAxLjMxN0MxMDEuMzE3IDgxLjMyNDkgMTAxLjM1NiA4MS43MDc3IDEwMS40MzQgODIuMDAzOUMxMDEuNTExIDgyLjMwMDEgMTAxLjYxOCA4Mi41MjggMTAxLjc1NSA4Mi42ODc1QzEwMS44OTYgODIuODQ3IDEwMi4wNTggODIuOTU2NCAxMDIuMjQgODMuMDE1NkMxMDIuNDIzIDgzLjA3NDkgMTAyLjYxOCA4My4xMDQ1IDEwMi44MjggODMuMTA0NUMxMDMuMjM0IDgzLjEwNDUgMTAzLjU2NiA4My4wNDMgMTAzLjgyNiA4Mi45MTk5QzEwNC4wOSA4Mi43OTIzIDEwNC4yOTYgODIuNjE0NiAxMDQuNDQxIDgyLjM4NjdDMTA0LjU4NyA4Mi4xNTg5IDEwNC42OSA4MS44OSAxMDQuNzQ5IDgxLjU4MDFDMTA0LjgwOCA4MS4yNzAyIDEwNC44MzggODAuOTMwNyAxMDQuODM4IDgwLjU2MTVaTTEwMC4wNiA3Ni42MDM1SDEwMS4zMTdWODYuODQzOEgxMDAuMDZWNzYuNjAzNVpNMTEyLjg5OSA3Ni42MDM1SDExNC4wNDhWODMuODQyOEMxMTQuMDQ4IDg0LjQ5NDUgMTEzLjkxNSA4NS4wNTA1IDExMy42NTEgODUuNTEwN0MxMTMuMzg3IDg1Ljk3MSAxMTMuMDE4IDg2LjMxOTcgMTEyLjU0NCA4Ni41NTY2QzExMi4wNzQgODYuNzk4MiAxMTEuNTMyIDg2LjkxODkgMTEwLjkxNyA4Ni45MTg5QzExMC42NjIgODYuOTE4OSAxMTAuMzYxIDg2Ljg3NzkgMTEwLjAxNCA4Ni43OTU5QzEwOS42NzMgODYuNzE4NCAxMDkuMzM1IDg2LjU4NCAxMDkuMDAzIDg2LjM5MjZDMTA4LjY3NSA4Ni4yMDU3IDEwOC4zOTkgODUuOTUyOCAxMDguMTc2IDg1LjYzMzhMMTA4LjgzOSA4NC44ODE4QzEwOS4xNDkgODUuMjU1NSAxMDkuNDcyIDg1LjUxNTMgMTA5LjgwOSA4NS42NjExQzExMC4xNTEgODUuODA3IDExMC40ODggODUuODc5OSAxMTAuODIxIDg1Ljg3OTlDMTExLjIyMiA4NS44Nzk5IDExMS41NjggODUuODA0NyAxMTEuODYgODUuNjU0M0MxMTIuMTUyIDg1LjUwMzkgMTEyLjM3NyA4NS4yODA2IDExMi41MzcgODQuOTg0NEMxMTIuNzAxIDg0LjY5MjcgMTEyLjc4MyA4NC4zMzI3IDExMi43ODMgODMuOTA0M1Y3OC4yMzA1TDExMi44OTkgNzYuNjAzNVpNMTA3LjgwNiA4MC4zODM4VjgwLjI0MDJDMTA3LjgwNiA3OS42NzUxIDEwNy44NzMgNzkuMTYyNCAxMDguMDA1IDc4LjcwMjFDMTA4LjE0MSA3OC4yMzczIDEwOC4zMzUgNzcuODM4NSAxMDguNTg2IDc3LjUwNTlDMTA4Ljg0MSA3Ny4xNzMyIDEwOS4xNDkgNzYuOTE4IDEwOS41MDkgNzYuNzQwMkMxMDkuODY5IDc2LjU1NzkgMTEwLjI3NCA3Ni40NjY4IDExMC43MjUgNzYuNDY2OEMxMTEuMTkgNzYuNDY2OCAxMTEuNTk2IDc2LjU0ODggMTExLjk0MiA3Ni43MTI5QzExMi4yOTMgNzYuODcyNCAxMTIuNTg5IDc3LjEwNzEgMTEyLjgzMSA3Ny40MTdDMTEzLjA3NyA3Ny43MjIzIDExMy4yNzEgNzguMDkxNSAxMTMuNDEyIDc4LjUyNDRDMTEzLjU1MyA3OC45NTc0IDExMy42NTEgNzkuNDQ3MyAxMTMuNzA2IDc5Ljk5NDFWODAuNjIzQzExMy42NTYgODEuMTY1NCAxMTMuNTU4IDgxLjY1MyAxMTMuNDEyIDgyLjA4NTlDMTEzLjI3MSA4Mi41MTg5IDExMy4wNzcgODIuODg4IDExMi44MzEgODMuMTkzNEMxMTIuNTg5IDgzLjQ5ODcgMTEyLjI5MyA4My43MzM0IDExMS45NDIgODMuODk3NUMxMTEuNTkxIDg0LjA1NyAxMTEuMTgxIDg0LjEzNjcgMTEwLjcxMiA4NC4xMzY3QzExMC4yNyA4NC4xMzY3IDEwOS44NjkgODQuMDQzMyAxMDkuNTA5IDgzLjg1NjRDMTA5LjE1MyA4My42Njk2IDEwOC44NDggODMuNDA3NiAxMDguNTkzIDgzLjA3MDNDMTA4LjMzNyA4Mi43MzMxIDEwOC4xNDEgODIuMzM2NiAxMDguMDA1IDgxLjg4MDlDMTA3Ljg3MyA4MS40MjA2IDEwNy44MDYgODAuOTIxNSAxMDcuODA2IDgwLjM4MzhaTTEwOS4wNzEgODAuMjQwMlY4MC4zODM4QzEwOS4wNzEgODAuNzUyOSAxMDkuMTA4IDgxLjA5OTMgMTA5LjE4IDgxLjQyMjlDMTA5LjI1OCA4MS43NDY0IDEwOS4zNzQgODIuMDMxMiAxMDkuNTI5IDgyLjI3NzNDMTA5LjY4OSA4Mi41MjM0IDEwOS44OTEgODIuNzE3MSAxMTAuMTM3IDgyLjg1ODRDMTEwLjM4NCA4Mi45OTUxIDExMC42NzggODMuMDYzNSAxMTEuMDE5IDgzLjA2MzVDMTExLjQzOSA4My4wNjM1IDExMS43ODUgODIuOTc0NiAxMTIuMDU4IDgyLjc5NjlDMTEyLjMzMiA4Mi42MTkxIDExMi41NDggODIuMzg0NCAxMTIuNzA4IDgyLjA5MjhDMTEyLjg3MiA4MS44MDExIDExMi45OTkgODEuNDg0NCAxMTMuMDkxIDgxLjE0MjZWNzkuNDk1MUMxMTMuMDQgNzkuMjQ0NSAxMTIuOTYzIDc5LjAwMjkgMTEyLjg1OCA3OC43NzA1QzExMi43NTggNzguNTMzNSAxMTIuNjI2IDc4LjMyMzkgMTEyLjQ2MiA3OC4xNDE2QzExMi4zMDIgNzcuOTU0OCAxMTIuMTA0IDc3LjgwNjYgMTExLjg2NyA3Ny42OTczQzExMS42MyA3Ny41ODc5IDExMS4zNTIgNzcuNTMzMiAxMTEuMDMzIDc3LjUzMzJDMTEwLjY4NyA3Ny41MzMyIDExMC4zODggNzcuNjA2MSAxMTAuMTM3IDc3Ljc1MkMxMDkuODkxIDc3Ljg5MzIgMTA5LjY4OSA3OC4wODkyIDEwOS41MjkgNzguMzM5OEMxMDkuMzc0IDc4LjU4NTkgMTA5LjI1OCA3OC44NzMgMTA5LjE4IDc5LjIwMTJDMTA5LjEwOCA3OS41MjQ3IDEwOS4wNzEgNzkuODcxMSAxMDkuMDcxIDgwLjI0MDJaTTEyMC41NzEgNzQuMDQ2OUwxMTYuNDIxIDg0Ljg1NDVIMTE1LjMzNUwxMTkuNDkxIDc0LjA0NjlIMTIwLjU3MVpNMTIzLjQxIDc4LjA3MzJWODRIMTIyLjEzOFY3Ni42MDM1SDEyMy4zNDFMMTIzLjQxIDc4LjA3MzJaTTEyMy4xNSA4MC4wMjE1TDEyMi41NjIgODAuMDAxQzEyMi41NjYgNzkuNDk1MSAxMjIuNjMzIDc5LjAyOCAxMjIuNzYgNzguNTk5NkMxMjIuODg4IDc4LjE2NjcgMTIzLjA3NyA3Ny43OTA3IDEyMy4zMjggNzcuNDcxN0MxMjMuNTc4IDc3LjE1MjcgMTIzLjg5IDc2LjkwNjYgMTI0LjI2NCA3Ni43MzM0QzEyNC42MzggNzYuNTU1NyAxMjUuMDcxIDc2LjQ2NjggMTI1LjU2MyA3Ni40NjY4QzEyNS45MDkgNzYuNDY2OCAxMjYuMjI4IDc2LjUxNjkgMTI2LjUyIDc2LjYxNzJDMTI2LjgxMiA3Ni43MTI5IDEyNy4wNjUgNzYuODY1NiAxMjcuMjc5IDc3LjA3NTJDMTI3LjQ5MyA3Ny4yODQ4IDEyNy42NTkgNzcuNTUzNyAxMjcuNzc4IDc3Ljg4MThDMTI3Ljg5NiA3OC4yMSAxMjcuOTU1IDc4LjYwNjQgMTI3Ljk1NSA3OS4wNzEzVjg0SDEyNi42OTFWNzkuMTMyOEMxMjYuNjkxIDc4Ljc0NTQgMTI2LjYyNSA3OC40MzU1IDEyNi40OTMgNzguMjAzMUMxMjYuMzY1IDc3Ljk3MDcgMTI2LjE4MyA3Ny44MDIxIDEyNS45NDYgNzcuNjk3M0MxMjUuNzA5IDc3LjU4NzkgMTI1LjQzMSA3Ny41MzMyIDEyNS4xMTIgNzcuNTMzMkMxMjQuNzM4IDc3LjUzMzIgMTI0LjQyNiA3Ny41OTkzIDEyNC4xNzUgNzcuNzMxNEMxMjMuOTI1IDc3Ljg2MzYgMTIzLjcyNCA3OC4wNDU5IDEyMy41NzQgNzguMjc4M0MxMjMuNDIzIDc4LjUxMDcgMTIzLjMxNCA3OC43NzczIDEyMy4yNDYgNzkuMDc4MUMxMjMuMTgyIDc5LjM3NDMgMTIzLjE1IDc5LjY4ODggMTIzLjE1IDgwLjAyMTVaTTEyNy45NDIgNzkuMzI0MkwxMjcuMDk0IDc5LjU4NEMxMjcuMDk5IDc5LjE3ODQgMTI3LjE2NSA3OC43ODg3IDEyNy4yOTIgNzguNDE1QzEyNy40MjUgNzguMDQxMyAxMjcuNjE0IDc3LjcwODcgMTI3Ljg2IDc3LjQxN0MxMjguMTEgNzcuMTI1MyAxMjguNDE4IDc2Ljg5NTIgMTI4Ljc4MyA3Ni43MjY2QzEyOS4xNDcgNzYuNTUzNCAxMjkuNTY0IDc2LjQ2NjggMTMwLjAzNCA3Ni40NjY4QzEzMC40MyA3Ni40NjY4IDEzMC43ODEgNzYuNTE5MiAxMzEuMDg2IDc2LjYyNEMxMzEuMzk2IDc2LjcyODggMTMxLjY1NiA3Ni44OTA2IDEzMS44NjYgNzcuMTA5NEMxMzIuMDggNzcuMzIzNiAxMzIuMjQyIDc3LjU5OTMgMTMyLjM1MSA3Ny45MzY1QzEzMi40NiA3OC4yNzM4IDEzMi41MTUgNzguNjc0OCAxMzIuNTE1IDc5LjEzOTZWODRIMTMxLjI0NFY3OS4xMjZDMTMxLjI0NCA3OC43MTEzIDEzMS4xNzcgNzguMzkgMTMxLjA0NSA3OC4xNjIxQzEzMC45MTggNzcuOTI5NyAxMzAuNzM1IDc3Ljc2NzkgMTMwLjQ5OCA3Ny42NzY4QzEzMC4yNjYgNzcuNTgxMSAxMjkuOTg4IDc3LjUzMzIgMTI5LjY2NCA3Ny41MzMyQzEyOS4zODYgNzcuNTMzMiAxMjkuMTQgNzcuNTgxMSAxMjguOTI2IDc3LjY3NjhDMTI4LjcxMiA3Ny43NzI1IDEyOC41MzIgNzcuOTA0NiAxMjguMzg2IDc4LjA3MzJDMTI4LjI0IDc4LjIzNzMgMTI4LjEyOSA3OC40MjY0IDEyOC4wNTEgNzguNjQwNkMxMjcuOTc4IDc4Ljg1NDggMTI3Ljk0MiA3OS4wODI3IDEyNy45NDIgNzkuMzI0MlpNMTM1LjQ4NCA3Ni4zMDI3SDEzNi4wNThDMTM2LjI4MSA3Ni4zMDI3IDEzNi40NjYgNzYuMjcwOCAxMzYuNjEyIDc2LjIwN0MxMzYuNzU3IDc2LjE0MzIgMTM2Ljg2NyA3Ni4wNTY2IDEzNi45NCA3NS45NDczQzEzNy4wMTcgNzUuODMzMyAxMzcuMDU2IDc1LjcwMzUgMTM3LjA1NiA3NS41NTc2QzEzNy4wNTYgNzUuMzU3MSAxMzYuOTgzIDc1LjE4ODUgMTM2LjgzNyA3NS4wNTE4QzEzNi42OTEgNzQuOTE1IDEzNi40NjEgNzQuODQ2NyAxMzYuMTQ3IDc0Ljg0NjdDMTM1Ljg4NyA3NC44NDY3IDEzNS42NyA3NC45MDM2IDEzNS40OTcgNzUuMDE3NkMxMzUuMzI5IDc1LjEzMTUgMTM1LjI0NCA3NS4yODY1IDEzNS4yNDQgNzUuNDgyNEgxMzQuMTcxQzEzNC4xNzEgNzUuMTcyNSAxMzQuMjU4IDc0LjkwNTkgMTM0LjQzMSA3NC42ODI2QzEzNC42MDQgNzQuNDU0OCAxMzQuODM5IDc0LjI3OTMgMTM1LjEzNSA3NC4xNTYyQzEzNS40MzEgNzQuMDMzMiAxMzUuNzYyIDczLjk3MTcgMTM2LjEyNiA3My45NzE3QzEzNi41MjMgNzMuOTcxNyAxMzYuODcxIDc0LjAzMDkgMTM3LjE3MiA3NC4xNDk0QzEzNy40NzMgNzQuMjYzMyAxMzcuNzA4IDc0LjQzNDIgMTM3Ljg3NiA3NC42NjIxQzEzOC4wNDUgNzQuODkgMTM4LjEyOSA3NS4xNzAyIDEzOC4xMjkgNzUuNTAyOUMxMzguMTI5IDc1Ljc0NDUgMTM4LjA1NCA3NS45Njc4IDEzNy45MDQgNzYuMTcyOUMxMzcuNzU4IDc2LjM3MzQgMTM3LjU0MyA3Ni41MzUyIDEzNy4yNjEgNzYuNjU4MkMxMzYuOTgzIDc2Ljc3NjcgMTM2LjY0MyA3Ni44MzU5IDEzNi4yNDIgNzYuODM1OUgxMzUuNDg0Vjc2LjMwMjdaTTEzNS40ODQgNzcuMDg4OVY3Ni41NTU3SDEzNi4yNDJDMTM2LjY5OCA3Ni41NTU3IDEzNy4wNzIgNzYuNjEwNCAxMzcuMzYzIDc2LjcxOTdDMTM3LjY1NSA3Ni44MjkxIDEzNy44NzIgNzYuOTg2MyAxMzguMDEzIDc3LjE5MTRDMTM4LjE1NCA3Ny4zOTY1IDEzOC4yMjUgNzcuNjQ0OSAxMzguMjI1IDc3LjkzNjVDMTM4LjIyNSA3OC4yNjkyIDEzOC4xMzQgNzguNTU0IDEzNy45NTEgNzguNzkxQzEzNy43NjkgNzkuMDI4IDEzNy41MTggNzkuMjA4IDEzNy4xOTkgNzkuMzMxMUMxMzYuODg1IDc5LjQ1NDEgMTM2LjUyOSA3OS41MTU2IDEzNi4xMzMgNzkuNTE1NkMxMzUuNzkxIDc5LjUxNTYgMTM1LjQ2MyA3OS40NjA5IDEzNS4xNDkgNzkuMzUxNkMxMzQuODM5IDc5LjIzNzYgMTM0LjU4NiA3OS4wNjIyIDEzNC4zOSA3OC44MjUyQzEzNC4xOTQgNzguNTg4MiAxMzQuMDk2IDc4LjI4NTIgMTM0LjA5NiA3Ny45MTZIMTM1LjE2OUMxMzUuMTY5IDc4LjEyMTEgMTM1LjI2IDc4LjI5NDMgMTM1LjQ0MyA3OC40MzU1QzEzNS42MjUgNzguNTcyMyAxMzUuODY2IDc4LjY0MDYgMTM2LjE2NyA3OC42NDA2QzEzNi40ODYgNzguNjQwNiAxMzYuNzI4IDc4LjU3IDEzNi44OTIgNzguNDI4N0MxMzcuMDYgNzguMjg3NCAxMzcuMTQ1IDc4LjEwNTEgMTM3LjE0NSA3Ny44ODE4QzEzNy4xNDUgNzcuNjkwNCAxMzcuMDk5IDc3LjUzNzggMTM3LjAwOCA3Ny40MjM4QzEzNi45MjEgNzcuMzA1MyAxMzYuNzk2IDc3LjIyMSAxMzYuNjMyIDc3LjE3MDlDMTM2LjQ3MyA3Ny4xMTYyIDEzNi4yODEgNzcuMDg4OSAxMzYuMDU4IDc3LjA4ODlIMTM1LjQ4NFoiIGZpbGw9IiNGRkE2MDAiLz4KPHBhdGggZD0iTTE0Ny40NDQgNzEuNzc3OEwxNTQuNTU1IDcxLjc3NzhMMTU0LjU1NSA4MC42NjY3TDE1MS4wMDQgODMuMzMzM0wxNDcuNDQ0IDgwLjY2NjdMMTQ3LjQ0NCA3MS43Nzc4WiIgZmlsbD0idXJsKCNwYWludDBfbGluZWFyXzExMjlfMjk5MSkiIGZpbGwtb3BhY2l0eT0iMC4zIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTUwLjY3NCA4My44OTZDMTUwLjg1NCA4NC4wNzYyIDE1MS4xNDYgODQuMDc2MiAxNTEuMzI2IDgzLjg5NkwxNTQuNTU1IDgwLjY2NjdMMTUzLjkwMyA4MC4wMTQyTDE1Mi40ODQgODEuNDMzMUMxNTIuMTA3IDgxLjgxMDYgMTUxLjQ2MSA4MS41NDMyIDE1MS40NjEgODEuMDA5M0wxNTEuNDYxIDc3LjExMTFMMTUwLjUzOSA3Ny4xMTExTDE1MC41MzkgODEuMDA5M0MxNTAuNTM5IDgxLjU0MzIgMTQ5Ljg5MyA4MS44MTA2IDE0OS41MTYgODEuNDMzMUwxNDguMDk3IDgwLjAxNDJMMTQ3LjQ0NCA4MC42NjY3TDE1MC42NzQgODMuODk2WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMTYyLjU2OCA3OS43OTNWODAuOTE4SDE1OS40NjlWNzkuNzkzSDE2Mi41NjhaTTE2Ny44MjggNzUuNDM5NVY4NEgxNjYuNDE2Vjc3LjExNTJMMTY0LjMyNCA3Ny44MjQyVjc2LjY1ODJMMTY3LjY1OCA3NS40Mzk1SDE2Ny44MjhaTTE3NS4zMDkgNzUuNDE2SDE3NS40ODRWNzYuNTcwM0gxNzUuMzg1QzE3NC44ODEgNzYuNTcwMyAxNzQuNDQ1IDc2LjY0ODQgMTc0LjA3OCA3Ni44MDQ3QzE3My43MTUgNzYuOTYwOSAxNzMuNDE2IDc3LjE3NTggMTczLjE4MiA3Ny40NDkyQzE3Mi45NDcgNzcuNzIyNyAxNzIuNzcxIDc4LjA0MyAxNzIuNjU0IDc4LjQxMDJDMTcyLjU0MSA3OC43NzM0IDE3Mi40ODQgNzkuMTYwMiAxNzIuNDg0IDc5LjU3MDNWODAuOTEyMUMxNzIuNDg0IDgxLjI1MiAxNzIuNTIxIDgxLjU1MjcgMTcyLjU5NiA4MS44MTQ1QzE3Mi42NyA4Mi4wNzIzIDE3Mi43NzMgODIuMjg5MSAxNzIuOTA2IDgyLjQ2NDhDMTczLjA0MyA4Mi42MzY3IDE3My4xOTkgODIuNzY3NiAxNzMuMzc1IDgyLjg1NzRDMTczLjU1MSA4Mi45NDczIDE3My43NCA4Mi45OTIyIDE3My45NDMgODIuOTkyMkMxNzQuMTU0IDgyLjk5MjIgMTc0LjM0NiA4Mi45NDkyIDE3NC41MTggODIuODYzM0MxNzQuNjg5IDgyLjc3MzQgMTc0LjgzNiA4Mi42NTA0IDE3NC45NTcgODIuNDk0MUMxNzUuMDc4IDgyLjMzNzkgMTc1LjE3IDgyLjE1MjMgMTc1LjIzMiA4MS45Mzc1QzE3NS4yOTUgODEuNzIyNyAxNzUuMzI2IDgxLjQ4ODMgMTc1LjMyNiA4MS4yMzQ0QzE3NS4zMjYgODAuOTkyMiAxNzUuMjk1IDgwLjc2NTYgMTc1LjIzMiA4MC41NTQ3QzE3NS4xNzQgODAuMzM5OCAxNzUuMDg2IDgwLjE1MjMgMTc0Ljk2OSA3OS45OTIyQzE3NC44NTIgNzkuODI4MSAxNzQuNzA1IDc5LjcwMTIgMTc0LjUyOSA3OS42MTEzQzE3NC4zNTcgNzkuNTE3NiAxNzQuMTU4IDc5LjQ3MDcgMTczLjkzMiA3OS40NzA3QzE3My42NSA3OS40NzA3IDE3My4zOTUgNzkuNTM3MSAxNzMuMTY0IDc5LjY2OTlDMTcyLjkzOCA3OS44MDI3IDE3Mi43NTQgNzkuOTc2NiAxNzIuNjEzIDgwLjE5MTRDMTcyLjQ3NyA4MC40MDIzIDE3Mi40MDIgODAuNjI3IDE3Mi4zOTEgODAuODY1MkwxNzEuODUyIDgwLjY4OTVDMTcxLjg4MyA4MC4zMjYyIDE3MS45NjMgODAgMTcyLjA5MiA3OS43MTA5QzE3Mi4yMjUgNzkuNDIxOSAxNzIuMzk4IDc5LjE3NTggMTcyLjYxMyA3OC45NzI3QzE3Mi44MjggNzguNzY5NSAxNzMuMDc2IDc4LjYxNTIgMTczLjM1NyA3OC41MDk4QzE3My42NDMgNzguNDAwNCAxNzMuOTU1IDc4LjM0NTcgMTc0LjI5NSA3OC4zNDU3QzE3NC43MDkgNzguMzQ1NyAxNzUuMDY4IDc4LjQyMzggMTc1LjM3MyA3OC41ODAxQzE3NS42NzggNzguNzM2MyAxNzUuOTMgNzguOTQ5MiAxNzYuMTI5IDc5LjIxODhDMTc2LjMzMiA3OS40ODQ0IDE3Ni40ODIgNzkuNzg5MSAxNzYuNTggODAuMTMyOEMxNzYuNjgyIDgwLjQ3MjcgMTc2LjczMiA4MC44MjgxIDE3Ni43MzIgODEuMTk5MkMxNzYuNzMyIDgxLjYwOTQgMTc2LjY3IDgxLjk5MjIgMTc2LjU0NSA4Mi4zNDc3QzE3Ni40MiA4Mi42OTkyIDE3Ni4yMzYgODMuMDA3OCAxNzUuOTk0IDgzLjI3MzRDMTc1Ljc1NiA4My41MzkxIDE3NS40NjUgODMuNzQ2MSAxNzUuMTIxIDgzLjg5NDVDMTc0Ljc4MSA4NC4wNDMgMTc0LjM5NSA4NC4xMTcyIDE3My45NjEgODQuMTE3MkMxNzMuNTA0IDg0LjExNzIgMTczLjA5NiA4NC4wMjkzIDE3Mi43MzYgODMuODUzNUMxNzIuMzgxIDgzLjY3NzcgMTcyLjA3OCA4My40MzU1IDE3MS44MjggODMuMTI3QzE3MS41ODIgODIuODE4NCAxNzEuMzk1IDgyLjQ2MjkgMTcxLjI2NiA4Mi4wNjA1QzE3MS4xMzcgODEuNjU4MiAxNzEuMDcyIDgxLjIzMjQgMTcxLjA3MiA4MC43ODMyVjgwLjE5NzNDMTcxLjA3MiA3OS41NDg4IDE3MS4xNTQgNzguOTM3NSAxNzEuMzE4IDc4LjM2MzNDMTcxLjQ4MiA3Ny43ODUyIDE3MS43MzQgNzcuMjc1NCAxNzIuMDc0IDc2LjgzNEMxNzIuNDE4IDc2LjM5MjYgMTcyLjg1NyA3Ni4wNDY5IDE3My4zOTMgNzUuNzk2OUMxNzMuOTI4IDc1LjU0MyAxNzQuNTY2IDc1LjQxNiAxNzUuMzA5IDc1LjQxNloiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTE3OC41ODYgNzcuNTYwNVY3Ny4xMDk0QzE3OC41ODYgNzYuNzg1MiAxNzguNjU2IDc2LjQ5MDIgMTc4Ljc5NyA3Ni4yMjQ2QzE3OC45MzggNzUuOTU5IDE3OS4xNDMgNzUuNzQ2MSAxNzkuNDEyIDc1LjU4NTlDMTc5LjY4MiA3NS40MjU4IDE4MC4wMDYgNzUuMzQ1NyAxODAuMzg1IDc1LjM0NTdDMTgwLjc3NSA3NS4zNDU3IDE4MS4xMDQgNzUuNDI1OCAxODEuMzY5IDc1LjU4NTlDMTgxLjYzOSA3NS43NDYxIDE4MS44NDQgNzUuOTU5IDE4MS45ODQgNzYuMjI0NkMxODIuMTI1IDc2LjQ5MDIgMTgyLjE5NSA3Ni43ODUyIDE4Mi4xOTUgNzcuMTA5NFY3Ny41NjA1QzE4Mi4xOTUgNzcuODc3IDE4Mi4xMjUgNzguMTY4IDE4MS45ODQgNzguNDMzNkMxODEuODQ4IDc4LjY5OTIgMTgxLjY0NSA3OC45MTIxIDE4MS4zNzUgNzkuMDcyM0MxODEuMTA5IDc5LjIzMjQgMTgwLjc4MyA3OS4zMTI1IDE4MC4zOTYgNzkuMzEyNUMxODAuMDE0IDc5LjMxMjUgMTc5LjY4NiA3OS4yMzI0IDE3OS40MTIgNzkuMDcyM0MxNzkuMTQzIDc4LjkxMjEgMTc4LjkzOCA3OC42OTkyIDE3OC43OTcgNzguNDMzNkMxNzguNjU2IDc4LjE2OCAxNzguNTg2IDc3Ljg3NyAxNzguNTg2IDc3LjU2MDVaTTE3OS41NjQgNzcuMTA5NFY3Ny41NjA1QzE3OS41NjQgNzcuNzE2OCAxNzkuNTk0IDc3Ljg2NTIgMTc5LjY1MiA3OC4wMDU5QzE3OS43MTUgNzguMTQ2NSAxNzkuODA5IDc4LjI1OTggMTc5LjkzNCA3OC4zNDU3QzE4MC4wNTkgNzguNDMxNiAxODAuMjEzIDc4LjQ3NDYgMTgwLjM5NiA3OC40NzQ2QzE4MC41ODQgNzguNDc0NiAxODAuNzM2IDc4LjQzMTYgMTgwLjg1NCA3OC4zNDU3QzE4MC45NzUgNzguMjU5OCAxODEuMDY0IDc4LjE0NjUgMTgxLjEyMyA3OC4wMDU5QzE4MS4xODIgNzcuODY1MiAxODEuMjExIDc3LjcxNjggMTgxLjIxMSA3Ny41NjA1Vjc3LjEwOTRDMTgxLjIxMSA3Ni45NDkyIDE4MS4xOCA3Ni43OTg4IDE4MS4xMTcgNzYuNjU4MkMxODEuMDU5IDc2LjUxMzcgMTgwLjk2OSA3Ni4zOTg0IDE4MC44NDggNzYuMzEyNUMxODAuNzI3IDc2LjIyNjYgMTgwLjU3MiA3Ni4xODM2IDE4MC4zODUgNzYuMTgzNkMxODAuMjA1IDc2LjE4MzYgMTgwLjA1MyA3Ni4yMjY2IDE3OS45MjggNzYuMzEyNUMxNzkuODA3IDc2LjM5ODQgMTc5LjcxNSA3Ni41MTM3IDE3OS42NTIgNzYuNjU4MkMxNzkuNTk0IDc2Ljc5ODggMTc5LjU2NCA3Ni45NDkyIDE3OS41NjQgNzcuMTA5NFpNMTgyLjY5OSA4Mi4zNjUyVjgxLjkwODJDMTgyLjY5OSA4MS41ODc5IDE4Mi43NyA4MS4yOTQ5IDE4Mi45MSA4MS4wMjkzQzE4My4wNTUgODAuNzYzNyAxODMuMjYyIDgwLjU1MDggMTgzLjUzMSA4MC4zOTA2QzE4My44MDEgODAuMjMwNSAxODQuMTI1IDgwLjE1MDQgMTg0LjUwNCA4MC4xNTA0QzE4NC44OTUgODAuMTUwNCAxODUuMjIzIDgwLjIzMDUgMTg1LjQ4OCA4MC4zOTA2QzE4NS43NTggODAuNTUwOCAxODUuOTYxIDgwLjc2MzcgMTg2LjA5OCA4MS4wMjkzQzE4Ni4yMzggODEuMjk0OSAxODYuMzA5IDgxLjU4NzkgMTg2LjMwOSA4MS45MDgyVjgyLjM2NTJDMTg2LjMwOSA4Mi42ODU1IDE4Ni4yMzggODIuOTc4NSAxODYuMDk4IDgzLjI0NDFDMTg1Ljk2MSA4My41MDk4IDE4NS43NiA4My43MjI3IDE4NS40OTQgODMuODgyOEMxODUuMjI5IDg0LjA0MyAxODQuOTA0IDg0LjEyMyAxODQuNTIxIDg0LjEyM0MxODQuMTM1IDg0LjEyMyAxODMuODA1IDg0LjA0MyAxODMuNTMxIDgzLjg4MjhDMTgzLjI2MiA4My43MjI3IDE4My4wNTUgODMuNTA5OCAxODIuOTEgODMuMjQ0MUMxODIuNzcgODIuOTc4NSAxODIuNjk5IDgyLjY4NTUgMTgyLjY5OSA4Mi4zNjUyWk0xODMuNjg0IDgxLjkwODJWODIuMzY1MkMxODMuNjg0IDgyLjUyMTUgMTgzLjcxNSA4Mi42Njk5IDE4My43NzcgODIuODEwNUMxODMuODQ0IDgyLjk1MTIgMTgzLjkzOSA4My4wNjY0IDE4NC4wNjQgODMuMTU2MkMxODQuMTg5IDgzLjI0MjIgMTg0LjM0IDgzLjI4NTIgMTg0LjUxNiA4My4yODUyQzE4NC43MTUgODMuMjg1MiAxODQuODc1IDgzLjI0MjIgMTg0Ljk5NiA4My4xNTYyQzE4NS4xMTcgODMuMDY2NCAxODUuMjAzIDgyLjk1MzEgMTg1LjI1NCA4Mi44MTY0QzE4NS4zMDkgODIuNjc1OCAxODUuMzM2IDgyLjUyNTQgMTg1LjMzNiA4Mi4zNjUyVjgxLjkwODJDMTg1LjMzNiA4MS43NDggMTg1LjMwNSA4MS41OTc3IDE4NS4yNDIgODEuNDU3QzE4NS4xODQgODEuMzE2NCAxODUuMDkyIDgxLjIwMzEgMTg0Ljk2NyA4MS4xMTcyQzE4NC44NDYgODEuMDMxMiAxODQuNjkxIDgwLjk4ODMgMTg0LjUwNCA4MC45ODgzQzE4NC4zMiA4MC45ODgzIDE4NC4xNjggODEuMDMxMiAxODQuMDQ3IDgxLjExNzJDMTgzLjkyNiA4MS4yMDMxIDE4My44MzQgODEuMzE2NCAxODMuNzcxIDgxLjQ1N0MxODMuNzEzIDgxLjU5NzcgMTgzLjY4NCA4MS43NDggMTgzLjY4NCA4MS45MDgyWk0xODQuODU1IDc2LjY4NzVMMTgwLjY4OSA4My4zNTU1TDE3OS45NjkgODIuOTM5NUwxODQuMTM1IDc2LjI3MTVMMTg0Ljg1NSA3Ni42ODc1WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDBfMTEyOV8yOTkxKSI+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTg4LjAwNCAxMjQuOTk5TDEyIDEyNS4wMDFMMTIgMTI0LjAwMUwxODguMDA0IDEyMy45OTlMMTg4LjAwNCAxMjQuOTk5WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC4xMiIvPgo8cGF0aCBkPSJNMTIgMTMzLjc3NUMxMy4wMTY2IDEzMy43NzUgMTguNTA5OCAxMzMuNzc1IDIxLjI2MzIgMTMzLjc3NUwzNC42NDMzIDE0M0w0NC45MzU3IDExMi4yNUw2Ni41NDk3IDEzNC44TDkwLjIyMjIgMTEyLjI1TDk3LjQyNjkgMTEzLjI3NUwxMTEuODM2IDE0M0gxMjYuMjQ2TDEzOC41OTYgMTE2LjM1SDE0Ny44NkwxNTcuMTIzIDEwMkwxNjkuNDc0IDEwNi4xTDE4OCAxMTYuMzUiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjg3IiBzdHJva2Utd2lkdGg9IjEuNSIvPgo8L2c+CjxkZWZzPgo8bGluZWFyR3JhZGllbnQgaWQ9InBhaW50MF9saW5lYXJfMTEyOV8yOTkxIiB4MT0iMTUwLjY4MyIgeTE9Ijg0LjAyOTgiIHgyPSIxNTAuNjgzIiB5Mj0iNzEuNzc3OCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmY7c3RvcC1vcGFjaXR5OjEiIC8+CjxzdG9wIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6I2ZmZjtzdG9wLW9wYWNpdHk6MCIgLz4KPC9saW5lYXJHcmFkaWVudD4KPGNsaXBQYXRoIGlkPSJjbGlwMF8xMTI5XzI5OTEiPgo8cmVjdCB3aWR0aD0iMTc2IiBoZWlnaHQ9IjYwIiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTIgOTApIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==", + "description": "A beneficial layer in the upper atmosphere, but harmful when present near ground level. Results displays mainly from vehicle exhaust and industrial emissions by combining the latest and aggregated values and optional simplified chart. ", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'ozone', label: 'Ozone', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'µg/m³', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'ozone', 'µg/m³', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Ozone\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#3FA71A\"},{\"from\":50,\"to\":100,\"color\":\"#80C32C\"},{\"from\":100,\"to\":130,\"color\":\"#FFA600\"},{\"from\":130,\"to\":240,\"color\":\"#F36900\"},{\"from\":240,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nif (value < -10) {\\n\\tvalue = -10;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Ozone\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"public\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "O3", + "ozone" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/ozone__o3__chart_card_with_background.json b/application/src/main/data/json/system/widget_types/ozone__o3__chart_card_with_background.json new file mode 100644 index 0000000000..5d7e7c1acb --- /dev/null +++ b/application/src/main/data/json/system/widget_types/ozone__o3__chart_card_with_background.json @@ -0,0 +1,39 @@ +{ + "fqn": "ozone_o3_chart_card_with_background", + "name": "Ozone (O3) chart card with background", + "deprecated": false, + "image": "tb-image:b3pvbmUtdmFsdWUtY2hhcnQtY2FyZC13aXRoLWJhY2tncm91bmQuc3Zn:b3pvbmUtdmFsdWUtY2hhcnQtY2FyZC13aXRoLWJhY2tncm91bmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiIHRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDYgMTU0KSIvPjwvZz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNMjAgMTMuM2E2LjcgNi43IDAgMCAwLTYuNyA2LjcgNi43IDYuNyAwIDAgMCA2LjcgNi43IDYuNyA2LjcgMCAwIDAgNi43LTYuNyA2LjcgNi43IDAgMCAwLTYuNy02LjdaTTE0LjcgMjBsLjEtMS4yTDE4IDIydi43YzAgLjcuNiAxLjMgMS4zIDEuM3YxLjNhNS4zIDUuMyAwIDAgMS00LjYtNS4zWm05LjIgMy42YTEuMyAxLjMgMCAwIDAtMS4yLTFIMjJ2LTJjMC0uMy0uMy0uNi0uNy0uNmgtNHYtMS4zaDEuNGMuMyAwIC42LS4zLjYtLjd2LTEuM2gxLjRjLjcgMCAxLjMtLjYgMS4zLTEuNHYtLjJhNS4zIDUuMyAwIDAgMSAzLjMgNC45YzAgMS40LS41IDIuNy0xLjQgMy42Wm0xNy0zLjl2LjZjMCAuNyAwIDEuNC0uMiAyYTQuNCA0LjQgMCAwIDEtLjkgMS41Yy0uMy41LS44LjgtMS4zIDFhNC4yIDQuMiAwIDAgMS0xLjcuM2MtLjYgMC0xLjEgMC0xLjYtLjNhMy44IDMuOCAwIDAgMS0xLjMtMSA0LjUgNC41IDAgMCAxLTEtMS41bC0uMi0ydi0uNmMwLS43IDAtMS40LjMtMiAuMi0uNi41LTEgLjgtMS41bDEuMy0xYTQuMSA0LjEgMCAwIDEgMS43LS4zIDQuMSA0LjEgMCAwIDEgMS43LjNjLjUuMyAxIC42IDEuMyAxIC40LjQuNyAxIC44IDEuNS4yLjYuMyAxLjMuMyAyWm0tMS43LjZ2LS42bC0uMS0xLjRhMyAzIDAgMCAwLS41LTEuMSAyIDIgMCAwIDAtLjgtLjdsLTEtLjJjLS40IDAtLjcgMC0xIC4yYTIgMiAwIDAgMC0uOC43IDMuMiAzLjIgMCAwIDAtLjQgMWwtLjIgMS41di42YzAgLjYgMCAxIC4yIDEuNSAwIC40LjIuOC41IDEgLjIuNC40LjYuNy43YTIuMyAyLjMgMCAwIDAgMSAuMmMuNCAwIC44IDAgMS0uMi4zLS4xLjYtLjMuOC0uNmwuNS0xLjEuMS0xLjVabTkuMyAzLjRWMjVINDN2LTEuM2g1LjVabS0uMi01TDQzLjUgMjVoLTF2LTFsNC43LTYuNGgxLjF2MVptLS42LTEuMXYxLjNoLTUuMnYtMS4zaDUuMlptMiAzLjh2LS4yYzAtLjUgMC0xIC4yLTEuNWwuNi0xLjJhMyAzIDAgMCAxIDEuMS0uNyAzLjUgMy41IDAgMCAxIDEuNC0uM2MuNiAwIDEgMCAxLjUuMy40LjEuOC40IDEgLjcuNC40LjYuOC44IDEuMmwuMiAxLjV2LjJjMCAuNSAwIDEtLjIgMS41YTMuNSAzLjUgMCAwIDEtLjcgMS4yIDMuMSAzLjEgMCAwIDEtMS4xLjggMy41IDMuNSAwIDAgMS0xLjQuMiAzLjYgMy42IDAgMCAxLTEuNS0uMiAzLjEgMy4xIDAgMCAxLTEtLjggMy41IDMuNSAwIDAgMS0uNy0xLjIgNC42IDQuNiAwIDAgMS0uMy0xLjVabTEuNi0uMnYxLjJsLjQuNy41LjVjLjMuMi41LjIuOS4yLjMgMCAuNSAwIC44LS4yLjIgMCAuNC0uMy41LS41YTIuNCAyLjQgMCAwIDAgLjMtLjhsLjEtMXYtMWEyLjQgMi40IDAgMCAwLS40LS44IDEuNiAxLjYgMCAwIDAtLjYtLjUgMS41IDEuNSAwIDAgMC0uOC0uMmMtLjMgMC0uNSAwLS44LjJhMS42IDEuNiAwIDAgMC0uNS41Yy0uMi4yLS4zLjUtLjMuOGE0IDQgMCAwIDAtLjEgMVptOC41LTJWMjVINTh2LTcuNGgxLjZ2MS42Wm0tLjMgMS44aC0uNmE0LjggNC44IDAgMCAxIC4zLTEuNCAzLjUgMy41IDAgMCAxIC42LTEuMWMuMi0uNC41LS42LjktLjhhMi43IDIuNyAwIDAgMSAxLjItLjJsMSAuMS43LjUuNS44LjEgMS4zVjI1aC0xLjZ2LTQuOGMwLS4zIDAtLjYtLjItLjhhLjkuOSAwIDAgMC0uNC0uNWwtLjctLjFhMS42IDEuNiAwIDAgMC0xLjQuN2wtLjMuN2EzIDMgMCAwIDAtLjEuOFptMTAgNC4xYTQgNCAwIDAgMS0xLjYtLjIgMy4zIDMuMyAwIDAgMS0xLS44QTMuNCAzLjQgMCAwIDEgNjYgMjNhNCA0IDAgMCAxLS4yLTEuNHYtLjNjMC0uNiAwLTEuMS4yLTEuNnMuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS4xLS44bDEuMy0uMmMuNiAwIDEgMCAxLjQuMmwxIC44Yy4zLjMuNS43LjYgMS4xbC4yIDEuNXYuN2gtNS43di0xLjJoNHYtLjFhMi4zIDIuMyAwIDAgMC0uMS0uOSAxLjQgMS40IDAgMCAwLS41LS42bC0uOS0uMmMtLjIgMC0uNSAwLS43LjItLjIgMC0uNC4yLS41LjVsLS40Ljd2Mi4zbC41LjcuNi41LjguMWEyLjMgMi4zIDAgMCAwIDEuOS0uOWwuOS45LS43LjZjLS4yLjItLjUuNC0uOS41YTMuNyAzLjcgMCAwIDEtMS4zLjJaIi8+PHBhdGggZmlsbD0iI0Y4OUUwRCIgZD0iTTU0LjIgNTguM1Y4NEg1MFY2My4zbC02LjMgMi4yVjYybDEwLTMuN2guNVptMTYuNyAwVjg0aC00LjNWNjMuM2wtNi4yIDIuMlY2MmwxMC0zLjdoLjVabTIzLjQgMTAuOHY0LjFjMCAyLS4yIDMuOC0uNiA1LjJhOSA5IDAgMCAxLTEuNyAzLjQgNi42IDYuNiAwIDAgMS0yLjcgMmMtMSAuMy0yLjEuNi0zLjQuNmE5LjggOS44IDAgMCAxLTIuOC0uNCA2LjkgNi45IDAgMCAxLTIuMy0xLjIgNy4yIDcuMiAwIDAgMS0xLjgtMi4yYy0uNS0uOS0uOS0xLjktMS4xLTMuMS0uMy0xLjItLjQtMi43LS40LTQuM3YtNC4xYzAtMiAuMi0zLjguNi01LjFhOSA5IDAgMCAxIDEuNy0zLjRjLjctMSAxLjYtMS41IDIuNi0yIDEtLjMgMi4yLS41IDMuNS0uNSAxIDAgMiAwIDIuOC4zYTYuNiA2LjYgMCAwIDEgNCAzLjNjLjUuOSAxIDIgMS4yIDMuMS4yIDEuMi40IDIuNy40IDQuM1pNOTAgNzMuOHYtNS40bC0uMS0yLjdhOCA4IDAgMCAwLS42LTIgNCA0IDAgMCAwLS44LTEuMyAzIDMgMCAwIDAtMS4yLS43IDQuMSA0LjEgMCAwIDAtMS40LS4zYy0uNyAwLTEuMy4xLTEuOC40LS41LjItMSAuNi0xLjMgMS4yLS4zLjUtLjYgMS4zLS44IDIuMi0uMi45LS4zIDItLjMgMy4ydjUuNGMwIDEgMCAyIC4yIDIuOGwuNSAyYy4zLjUuNSAxIC45IDEuNC4zLjMuNy42IDEuMS43bDEuNS4zYTQgNCAwIDAgMCAxLjgtLjRjLjUtLjMgMS0uNyAxLjMtMS4yLjMtLjYuNi0xLjQuOC0yLjNsLjItMy4zWm0xNC42IDIuOGgxLjNWODRoLTEuMnYtNy40Wm0uMiA0aC41YTYuNyA2LjcgMCAwIDEtLjEgMS40IDMuNiAzLjYgMCAwIDEtLjUgMS4xIDIuMSAyLjEgMCAwIDEtLjcuOGMtLjMuMi0uNy4yLTEuMS4yYTMgMyAwIDAgMS0xIDAgMS44IDEuOCAwIDAgMS0uNi0uNSAyLjQgMi40IDAgMCAxLS41LS44bC0uMy0yaC44bC4xIDEuMi40LjcuNC4zLjYuMWMuNCAwIC44IDAgMS0uMi4zLS4xLjUtLjMuNi0uNS4yLS4yLjMtLjUuMy0uOGwuMS0xWm0tNC43LTRoMS4ydjEwLjJoLTEuMlY3Ni42Wm0xMi44IDBoMS4xdjcuMmMwIC43IDAgMS4yLS4zIDEuN3MtLjcuOC0xLjIgMWEzLjUgMy41IDAgMCAxLTIuNS4zIDMuNiAzLjYgMCAwIDEtMS0uNCAyLjUgMi41IDAgMCAxLS44LS44bC42LS43Yy4zLjQuNy42IDEgLjhsMSAuMiAxLS4yYy40LS4yLjYtLjQuNy0uNy4yLS4zLjMtLjcuMy0xLjF2LTUuN2wuMS0xLjZabS01IDMuOHYtLjJsLjEtMS41Yy4xLS41LjMtLjkuNi0xLjJhMi42IDIuNiAwIDAgMSAyLjEtMWMuNSAwIC45IDAgMS4yLjJsMSAuNy41IDEuMS4zIDEuNXYuNmE2LjQgNi40IDAgMCAxLS4zIDEuNSAzLjQgMy40IDAgMCAxLS42IDFjLS4yLjQtLjUuNi0uOS44YTMgMyAwIDAgMS0yLjQgMCAyLjcgMi43IDAgMCAxLTEtLjggMy43IDMuNyAwIDAgMS0uNS0xLjIgNS40IDUuNCAwIDAgMS0uMi0xLjVabTEuMi0uMnYxLjJsLjQuOS42LjYgMSAuMmMuMyAwIC43LS4xIDEtLjNsLjYtLjcuNC0xdi0xLjZhMy40IDMuNCAwIDAgMC0uMi0uNyAyLjIgMi4yIDAgMCAwLS40LS43IDEuNyAxLjcgMCAwIDAtLjYtLjQgMiAyIDAgMCAwLS45LS4ybC0uOS4zYTEuNyAxLjcgMCAwIDAtLjYuNSAyLjggMi44IDAgMCAwLS4zLjlsLS4xIDFabTExLjUtNi4yTDExNi40IDg1aC0xbDQtMTAuOWgxLjJabTIuOCA0djZoLTEuM3YtNy40aDEuMmwuMSAxLjVabS0uMyAyaC0uNWMwLS41IDAtMSAuMi0xLjQgMC0uNC4zLS44LjUtMS4xbDEtLjhhMyAzIDAgMCAxIDEuMy0uMmwxIC4xYTIgMiAwIDAgMSAxLjIgMS4zbC4yIDEuMlY4NGgtMS4zdi00LjljMC0uNCAwLS43LS4yLS45YTEuMSAxLjEgMCAwIDAtLjYtLjUgMiAyIDAgMCAwLS44LS4yIDIgMiAwIDAgMC0xIC4yYy0uMi4yLS40LjMtLjUuNmwtLjQuOGE0LjUgNC41IDAgMCAwIDAgMVptNC44LS43LS44LjNjMC0uNCAwLS44LjItMS4yLjEtLjQuMy0uNy42LTEgLjItLjMuNS0uNS45LS43bDEuMi0uMiAxIC4xYTIgMiAwIDAgMSAxLjQgMS4zbC4xIDEuMlY4NGgtMS4zdi00LjljMC0uNCAwLS43LS4yLTFhMSAxIDAgMCAwLS41LS40IDIuMiAyLjIgMCAwIDAtLjgtLjJjLS4zIDAtLjYgMC0uOC4yYTEuNSAxLjUgMCAwIDAtLjUuNCAxLjcgMS43IDAgMCAwLS4zLjUgMi4xIDIuMSAwIDAgMC0uMi43Wm03LjYtM2gxLjFhLjcuNyAwIDAgMCAuNS0uN2MwLS4yLS4xLS40LS4zLS41LS4xLS4yLS4zLS4zLS43LS4zbC0uNi4yYS41LjUgMCAwIDAtLjMuNWgtMWMwLS4zIDAtLjYuMi0uOGwuNy0uNSAxLS4yIDEgLjFjLjQuMi42LjMuOC42bC4yLjhjMCAuMiAwIC41LS4yLjdsLS42LjUtMSAuMWgtLjh2LS41Wm0wIC44di0uNWguN2wxLjIuMWMuMy4xLjUuMy42LjUuMi4yLjIuNC4yLjcgMCAuNCAwIC43LS4yLjlhMS43IDEuNyAwIDAgMS0uOC41IDMgMyAwIDAgMS0yIDAgMS43IDEuNyAwIDAgMS0uOC0uNWMtLjItLjItLjMtLjUtLjMtLjloMWMwIC4yLjIuNC4zLjUuMi4yLjUuMi44LjIuMyAwIC41IDAgLjctLjJhLjcuNyAwIDAgMCAuMi0uNS43LjcgMCAwIDAgMC0uNS43LjcgMCAwIDAtLjUtLjIgMS44IDEuOCAwIDAgMC0uNS0uMWgtLjZaIi8+PHBhdGggZmlsbD0idXJsKCNjKSIgZmlsbC1vcGFjaXR5PSIuMyIgZD0iTTE0Ny40IDcxLjhoNy4ydjguOWwtMy42IDIuNi0zLjYtMi42di05WiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTE1MC43IDgzLjljLjIuMi40LjIuNiAwbDMuMy0zLjItLjctLjctMS40IDEuNGEuNi42IDAgMCAxLTEtLjR2LTMuOWgtMXY0YS42LjYgMCAwIDEtMSAuM2wtMS40LTEuNC0uNy43IDMuMyAzLjJaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNMTYyLjYgNzkuOHYxLjFoLTMuMXYtMS4xaDNabTUuMi00LjRWODRoLTEuNHYtNi45bC0yIC43di0xLjFsMy4zLTEuM2guMVptNy41IDBoLjJ2MS4yaC0uMWMtLjUgMC0xIDAtMS4zLjJhMi40IDIuNCAwIDAgMC0xIC42IDIuNyAyLjcgMCAwIDAtLjQgMWMtLjIuNC0uMi44LS4yIDEuMnYxLjNsLjEgMSAuMy42LjUuNGgxLjFsLjUtLjRjLjItLjIuMi0uMy4yLS42YTIuNSAyLjUgMCAwIDAgLjEtLjd2LS42YTEuNyAxLjcgMCAwIDAtLjMtLjYgMS4yIDEuMiAwIDAgMC0uNS0uNCAxLjIgMS4yIDAgMCAwLS42LS4xIDEuNSAxLjUgMCAwIDAtLjcuMmMtLjMuMS0uNC4zLS42LjVsLS4yLjctLjUtLjJjMC0uNCAwLS43LjItMWwuNS0uNy44LS41LjktLjJjLjQgMCAuOC4xIDEgLjMuNC4xLjYuNC44LjZsLjUgMWEzLjcgMy43IDAgMCAxIDAgMi4xbC0uNiAxYTIuNSAyLjUgMCAwIDEtLjkuNiAyLjkgMi45IDAgMCAxLTEuMS4yYy0uNSAwLS45IDAtMS4zLS4yYTIuNyAyLjcgMCAwIDEtLjktLjggMy4zIDMuMyAwIDAgMS0uNS0xIDQuMiA0LjIgMCAwIDEtLjItMS4zdi0uNmMwLS43IDAtMS4zLjItMS44YTQuNSA0LjUgMCAwIDEgLjgtMS42IDMuNSAzLjUgMCAwIDEgMS4zLTFjLjUtLjMgMS4yLS40IDItLjRabTMuMyAyLjJWNzdjMC0uMyAwLS42LjItLjkuMS0uMi4zLS41LjYtLjYuMy0uMi42LS4zIDEtLjNzLjcuMSAxIC4zbC42LjYuMiAxdi40YzAgLjMgMCAuNi0uMi44YTEuNiAxLjYgMCAwIDEtLjYuN2wtMSAuMmEyIDIgMCAwIDEtMS0uMiAxLjYgMS42IDAgMCAxLS42LS43IDEuOCAxLjggMCAwIDEtLjItLjhabTEtLjV2LjlsLjMuM2EuOC44IDAgMCAwIC41LjJjLjIgMCAuMyAwIC41LS4ybC4yLS4zLjEtLjR2LS45YS44LjggMCAwIDAtLjQtLjQuOC44IDAgMCAwLS40LS4xLjguOCAwIDAgMC0uNS4xLjguOCAwIDAgMC0uMi40bC0uMS40Wm0zLjEgNS4zdi0uNWMwLS4zIDAtLjYuMi0uOWExLjcgMS43IDAgMCAxIC42LS42Yy4zLS4yLjYtLjIgMS0uMnMuNyAwIDEgLjJsLjYuNi4yIDF2LjRjMCAuMyAwIC42LS4yLjhhMS42IDEuNiAwIDAgMS0uNi43bC0xIC4yYTIgMiAwIDAgMS0xLS4yIDEuNyAxLjcgMCAwIDEtLjYtLjcgMS45IDEuOSAwIDAgMS0uMi0uOFptMS0uNXYuOWwuNC40YS44LjggMCAwIDAgLjQgMGguNWEuNy43IDAgMCAwIC4zLS40di0xLjNhLjguOCAwIDAgMC0uMy0uNC44LjggMCAwIDAtLjUtLjEuOC44IDAgMCAwLS41LjEuOC44IDAgMCAwLS4yLjRsLS4xLjRabTEuMi01LjItNC4yIDYuNy0uNy0uNSA0LjEtNi42LjguNFoiLz48ZyBjbGlwLXBhdGg9InVybCgjZCkiPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjEiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTE4OCAxMjVIMTJ2LTFoMTc2djFaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuOSIgc3Ryb2tlLXdpZHRoPSIxLjUiIGQ9Ik0xMiAxMzMuOGg5LjNsMTMuMyA5LjJMNDUgMTEyLjJsMjEuNiAyMi42IDIzLjctMjIuNiA3LjIgMSAxNC40IDI5LjhoMTQuNGwxMi40LTI2LjZoOS4zTDE1NyAxMDJsMTIuNCA0LjEgMTguNSAxMC4zIi8+PC9nPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iYyIgeDE9IjE1MC43IiB4Mj0iMTUwLjciIHkxPSI4NCIgeTI9IjcxLjgiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmY7c3RvcC1vcGFjaXR5OjEiLz48c3RvcCBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmY7c3RvcC1vcGFjaXR5OjAiLz48L2xpbmVhckdyYWRpZW50PjxjbGlwUGF0aCBpZD0iZCI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTEyIDkwaDE3NnY2MEgxMnoiLz48L2NsaXBQYXRoPjxwYXR0ZXJuIGlkPSJhIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBwYXR0ZXJuQ29udGVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCI+PHVzZSB4bGluazpocmVmPSIjZSIgdHJhbnNmb3JtPSJzY2FsZSguMDA1MDMgLjAwNjI5KSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMTk1LjIiIGhlaWdodD0iMTU1LjIiIHg9IjIuNCIgeT0iMi40IiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIxLjgiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTEwM18zNTQ2Ii8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMTAzXzM1NDYiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48aW1hZ2UgeGxpbms6aHJlZj0iZGF0YTppbWFnZS9qcGVnO2Jhc2U2NCwvOWovNEFBUVNrWkpSZ0FCQVFBQUFRQUJBQUQvMndCREFBSUJBUUVCQVFJQkFRRUNBZ0lDQWdRREFnSUNBZ1VFQkFNRUJnVUdCZ1lGQmdZR0J3a0lCZ2NKQndZR0NBc0lDUW9LQ2dvS0JnZ0xEQXNLREFrS0Nnci8yd0JEQVFJQ0FnSUNBZ1VEQXdVS0J3WUhDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dyL3dnQVJDQUNmQU1jREFSRUFBaEVCQXhFQi84UUFIZ0FBQVFRREFRRUJBQUFBQUFBQUFBQUFBd0lFQlFZQkJ3Z0FDUXIveEFBY0FRQURBUUVCQVFFQkFBQUFBQUFBQUFBQkFnTUVBQVVHQndqLzJnQU1Bd0VBQWhBREVBQUFBUHFWOUw4MFplT2hPcGNCanJ4bExoWE1wT3BjcVRLNVVMZ001REhSakt4MVl5c1ZXY0tTcXhReEFTOGVaZmI4c3lrODJPQzRWakl4bExoV01wY0lUS3gwZHhOem9YSUpsSlZKdUpReDE0NEpUeHU1WjR4SE1mclp6b3hac2RDWldPaFBOanF4RmM4Mk9oT2p1RUoxTGdGd3BNcE9BWGdRQWhDeUNCVmtLY2MyKzF4bFlzMk5Oekk1cHVaR05OenpjODJjUlp3ck9Kc2RPS29XcTVYbEl1Unl3cENwT1V4VlhBbEU1NCt5WXNuTkdoVWNxVU5DaHAwTWxIRVhjeG80U2haaFNLcGVVZ1VpcW1GQlZqbFNSWERLZ2lLUXFRcm9uOVF5bFJ5em9TZFNUb1dWalFvVktualU4cUdTaTVVVk1MaXF3cTBHWmpLOTVRcVFVdllVS21GaFNjdWdQNkgvQUM1dTRFQktaZFR5R2hsYk94MVppQmpyUnpHajNIc2Q1OVVYcXgrblF5UEdiTUtTbG44bjAyMVlMS29JVE4zbWJTMjBaeHNOY2ZhZk1TZVBaSTVOSGlxN3dpUFU4bUM5YnlhNzZmbWhkWlhIc3Mzait4VFBJOWJXR1MrL0hTKzZzYkNzNm42WG11VnBFWG5IMWxGYVlzcVNLcnpHVFpQZWZ2cTYyY1o5TXhqMHZjOVdnZkM4N2VkRDI0dVphUTVZMHc1N1ZsOFh2TlkwdDJPUjJTOHJvVXFlN1BRZmM4T0szNGZQTjRwbjhtcDE1bnExWEZyMm41ZnJ0KzRxZ3ltQXBMVzJyTG9yWmkwRGNVbU5vRkM0ZFh4RDVhMlNGcnI1bm83ZzhQMitoODFlazc0M0hxZVhUZmQ4V29lbjUzSllma255L1MxWmoydXVmN2VCMGdZSWpUMWZLVW1tZlUrM0x6MXVqVEo5WUt6S3hteXMrcXRrcTN4Yk45L01mVGI4OEQxK2NOdWJwMzBmSHEyM3ovbkhaZFRsMlBjcHVPemZkV1ZFOEFFTmlLcXk2cmVHbmRVZFBhT3FsVUNaeWJwYW5uTUwwNW5wNkY1cnp2VDZGK2M5L2JubmF1U04rUFJ2b2VmWHRtYlRSbkFQelppU3FmZUNidTBaK2pQSnNnTlRxSnI5R29BcnJhOG9qWmtqTDVZK3NrVmpoMHhLakxMcWsvTzlQZWZpZXJ0UEpvNTgxWStOTi9uTU5FYTlxbkJKWkRkK2dmSHA4ckxtWEU2T3U1Zzg2dFFVeHUxKzRwbHB3K25NMTA0b1JwMXgrakdUTU5EZkR2bC9QM2JHeFhhbGRNNnNuTi9UZ1NVMlZycVA2Ry9IM25WaUt4QjJSemFpUkZaUW1pVlJ0T2gxU21VbXhlYzF5eUhNaVZhdkswUG51ZkxwWlJNT1ZxRkUxQ1lhbnJPQWZoT1AwQitmNlpsS2tPRHltRHdkSThxbVdFMHdwdXZQQVdTT0hOa0swTW9Ha1pQbVY2L0N0RG05Q0QxMWFSWENyc0tXMDZKb3ovYVBMck1qWVV0K0FTR3pKNml2bW5NRHBNckY2WXdONU1TbzFPSjB3clpuUjByU2NuWUlkZnBTZ2ltc2VwU25KaXYwTE1ua3lZRktsbjNObVJxeW9vcFdWOFVreUh0SjVaSThpUDdoY1loRzFMbXJUSlBTcG5RRWUxZkpxY2owT1pSeC9RcDcwelRjNmc2c3Z1RHdha05Tb201eTYrTTZMU09xUWxEa2RZemJRZVczT1VIRk52RHNOM2tieEdVZkRCWVA2R1BvVU5Na1RsZ2tIS0hKS2tIVTRweXBIdUE1TFF0bWVlMVpvUERweUdoczFSSXhaVjhoR3k0WHNjY25ra0tVL3dEL3hBQWpFQUFCQlFBQkJBTUJBUUFBQUFBQUFBQUNBUU1FQlFZQUJ3Z1RGQkVTRlJZSi85b0FDQUVCQUFFQ0FSUVVBUUVSRVJRRUZBUUVIZ29IQTRLQ2dJSWlJaUtEd1ZIZzhCUlVVQkJFVUJCNENnbzhIZzhEZzhUZzhIaWNGRVFVSG4yRlJJRkVoVUNFeE5ERlJNWEJNU0JSNEtpYU9JNmp5UEkrTDR2Q1FtSmlhR0RpR0ppUWtLaW9LTGlQSklTVDdIc2V5TWxKS1NVZlI4WEVNVEYxSGhlQjRYd2ZCMFgwaysxN1h0SkpTU2tyMmhscEw5cEpReXhsZzZqb3VJYU9nNkxvdUE2and5UE9qeVBJOEwzbTgzbVI3eit3a2taU1NpZC9iSzhhdFVzLzJudFZIMXhhZG5RSm9QMWl2azEzOVM3dEkyM2IwUjNZMnEzZjlTR3VlMFM2Vm5XTlh3eUVpRld2VmtwbDE4TFdMTmEwRngzUHNkL1dKNjQvcnpMejJuTk03MUZkNnJuMUYvdW05ekYyOGJXSURKeDVLMkM2ZE40MzFTMlBkRHMrL1RYOTJVdXpaVnRpanRjaDNhWWF3ZjZmMkdEbVkzOEFhbG1paFVFMjlZN2hmbGVCSTl1d1M0aDZ1dnVNeE14eDVscklCazQ5REJvNjNGVW1VelhYck1hMmRuSm1DczhydE81ZldkZFBLQnFDS1F2bExuV2R0b2JYUXZCb1daNFZrR1FFUjZBMm5TL1kyZVB2cVBwdjNOZFR1N3ZxajF0YWM4clQ2U2hKV1ZhTm16cTdpdHZxeTRnRkFLQkVtUTVMUEczdkRWUFl2WHpJWFZIbysxc2IzcDdQcVpKSklEZ0kyVFFndy9WMk5MWlp1eXg5amhGeDcrUldsOENLZkZzNjdSNXJWdGF6cWQwOWtMSzFrOGpCSGZ6L0FFaGpCd1Rrak1nMk5WWXg1YmFxNDNLcHBOSjZhUS95SU5uQzFOcGE2a3JtcUtSOElyY2xKQ1NFTHdPd0pFTjlteHFiSFB5cVpxc2pWTGRhOWxaV1JXc0dOSnlwd0xpaXZNclpabFlTOEVrTDU4d3pHckZ1Y2NXWm5KdEc1VHVaWCtjQ3VZZ3RRbk01TTZjMm1GbVJwRmU5WHpxQ3h5TTNIZ1BqVm91SzJ2QmRhbXg3SkhwVks1VWx6NDlQODlJelJnVXltcytuRS9DM0ZLZFovUEJ3RlRoTWt3clJOSWdxeSt6S0IxWWoxQXRQNnRuUHRPNEkrN3ZTOThPcDc1WHU2YTg2eVNyVlpvbUxnT0k1OWlFaEp2eHRwTnNiVHJoUDcyN2YvUXV3L3dCQzlsM1Z5My90eE9EeEJRZmhFUlJJWEJkRi93QTNrRG1wNmlhbnZ0NnFkYksrUS9UdFV6OUdvZlJCK3YwVlBxcGk1NVAveEFCSUVBQUJBd0lDQmdjRENBY0ZDUUFBQUFBQkFBSURCQkVGRWdZVElURlJZUWNRSWpKQmNZRVVZcUVWSXpOQ1VuS0NrU0FrTlpLeHdkRVdKVFJEY3pCRlU0T2lzc0xoOFAvYUFBZ0JBUUFEUHdFY0Z5Ui9USFZ3US8yRjBFQWgxRHFKVHVDUEZjMXo2Z0VFT29JTGdpanlSUnR2UnQxYkVFRUVGYnE4YnJtZ2d1SDZBUVE1cTZIRkRpbThVT0s1cm11YUhGQXJ4NnVmVno2aHdYTkRpaHhRUGloeFhBOVFDSEg5TDNsN3lIVU9LdjFEajFqcUhoMWMxWkRxNW9jVndQVUFodlE0cm1oeFhORGl1YTVvZmFRNHIzbHpYTkg3U1BGZThpTnprZkVxL3dCWmMxelhOY0VWelhOY1Z2NnVhNW9yRzZmdFBZSGozV0tydlp3RGZPSXFzYzZ3bmphUGRHMVRGblpuYmYzd3BteDNreStiUmNMRTdFd1U3SmZ1a1gvSXJHSVRiNUx0OS9Zc1FjZm5xVmpPWmNxNTV0RFYwemZ3M1dLWHM2cnBuZW9XSTN0bForN2YrQ3haN1AxZGtianczZnhXbGtjaC91aVczUEpaWTR3NVpjSm5ITmtOMWl1cjJ3VkE1K3ppNjdCaXFhaXBieDJBTE5JSTJZa2NvM055SCtxcjVHWnFlVmovQUwyeGFWU3V0QUlBUHVyU1ZnelZWVkNQK1dRc2NtK2d4S0Q4SC90cXJxTC9BQlVsUzQ4ZlpiaEdkdXlTUnA1MHhDeFUvd0NHbmMvN3JXaitJV2tjWS9aTmEvbkhxai9CVE8yRnlkTDN6K2FwNmh2YmhZVmhqenRoeTh3aTl0cVN2eSthMGtaOUZYWjFwU0FRK0V1OHBsaWxPZm5hRjkvTlRPNy9BR2ZSVW9PWjlZQitCWWZReDYycHhka1RRTnJwU0dqNHJvVzBXTG9NVzZRcVdXUm0rS2ppZFVPLzZMajRyb25maW9vWDZONDJLUW16cTR3UjdPZVRQbVhSTDBsVkQ2UFEvVEtDU3BidDlscUkzd3lIN29lQm05THFnaGQ4NVhRK2psbzdPd3RyS3FJai93QzRyUk5rNWRTMXpqeVpIc1dDVXpmMmlmTFZnckRhYy9xOFQ1RHhkc1dKYm9OVkVQSll4SWYyamI3b3NzYWYvdmlYOTVZMWY5cVNIOFN4RTdKYWw1VG45NTcwemdzbTV5dDQyVWtRdnZDcFlqWjdpMVlaUzdaSzdaNUxRcHJTNnMwa3BJcmI5Yy9ML0ZkREdodElaSmNZYmljOXV6UzRZek9UNXVObWo4MW83OG1GdWhIUk5tckhmNXVNVE4xVFBTUGE0K29YUzdwYXoyYWpsdzdCbUhmOGtVT1I1L0cvTWZ5c3NUeE9aMCtLNGhQVXlPZGQ3cDUzUEpQSGFuV3lnS2QzZGFzV3dDdGp4R2dmcTVvWGgwYi9BTEo0aFk2OE5wdE9ORTZiRW03QWFpa2ZxSmZQeERqK1M2T2VrMm1OVG9yaWtycEFMdm9xcjV1Vm5wNGptRjJjc2JzbzgxVzVTMkdUNExISW5rT2UwRHpWWEhmUEtUNktydlpwVmM3ZkswZXFhSERYVkYrUUMwTjBhYmJHOUk2T2xzTDJxYWxyRGJ5SzZGWTZuMlgrMk5LVDl2VXlaUHp5b0ZGcDJJdDNyWnZVYzROeHRUdzEyV1FxV1JydFk2NDVxR2NrbUpHM3pkN0p6SFdjdFlic2NxaUU5bU82bkIrY2pJNXFKNUFmS1B4QlVFNURuelJzOVZpK0I2bkVzTXFYalY3WTU0bmJ2VUxTUEE1QkRwaFRHdnBmclRSTUFtWi9KLzhBRmFLYWM0ZjhvNk9Zc3lkZytrYU5qNGp3YzA3V29WZDh0UjhGV2treFM1dlZZbGhkUEpXMVRZMlF4TnpTVFNTQnJXRGlTZHkwVTBhTWxKbzdBY1duYi9teE95d0EvZTN1OUYwbGFZVDJxTklaS1NIZTJsdzRtSm85UWN4OVNwcG51a2tjWE9KMnZjYmxPSGVlbkJjVUNFK1BhRUJlNXNzb09mY29uc2RheEN5eWw5Tk1XSHpXSlVyL0FKNFhDb3NUaCtjWUwrU2RBelgwenI4RTdkVU1WTFVidDZMUG9tcWVKMTg3bFBRemV3VmtZZEEvd2NzSzBncFRXWVNRMTl1NkZwSG9qaW55cmdHSTFHSDFqTjA5TTh0dU9CNGprVm92amRKOGw2ZVN0d25GbzIyT3o1bXE1eDhEN2g5RlRhSWgxTm9ob2hQWFNlRlRpRDlURis2THVQd1hTaDByVkJkcGZqcnpUTU40OFBwUnE2ZVA4QTNubTY1UmN4elhiN2JMcXp1Nk9DYllnOFV5MWsxMjlYOEU4Yms1d3NXSXlNT1VLcml6RGFWVUc3bUIzbFpWWkJhK25PL2VxbmNZRkxFY3pHRnR1Q3E0ZXk1cEtqbUZwRzJLREQyU2JLN0xPVUw3M0tob3BjOXlQSk1qQUJMcmZhQ3dMU3FpTFpneDl4dkc5VFUwVXM5RXpYUThBTnJWcUcvSXVtMVBycWNOeU1yV3NKZTM3NCt0NTcxUlR4SEZkR01RanFJVHRHcmZleXJLYVR0eDJJM2hPekE1TXBBN1hOT0lJTzNtbmtYQnNwYnArNVp0Nmo0S0NWcHMxTXNRK1BNRlFTM09wMnFna0JHb1ZJMTVjMEh5VkxhemQvTk1qT1pqRnFmcElyZVNFUE1JZlZmWmVMVlBUdnNwdGJmMmg3RDdwVmF5TVBOUitOdjgxRFd4YXF0MkUvVzhGaGVPTmtxc0xhMktvY0wySGRlc2IwTXhKN0lOWkE4SHR0OEhmMVRzUWZhcmdZMTNpOW45RU96STlsMnY3cmdnTDVkeDZuaFNjRkkzd1JhZ2c5TWt2ZHFlQVN3SnpMM1ltOEU5cHRhNkV1ellueWRxTWZCVjhlNkUrYXhKbloxWlU3OWtzQ25hOG1DSStxeFBESEJocFgyNUtrcUlja2pzcEtxNElIUGEvV3hINExEOGNpZEhXczIrRHJiUW5ZZEtjcnJ0Qjd5SU53NjQ0TFdBdWpQNFZaeHpiRVQxQlJPM2hSTzNGU083cjFVc3ZkbXprZ1FRNEpzb1BaVy9zS2FPL1pUM214Q2xZTm91b25iSHhxbHFXM2oySGtxcUVsekk4d1VzTGlERTRLSjR5VHdxR2NHV2xteW5rcTZqdkcvdWZXdDRxbnJHblZndGZ4Q3hGalhYaXpEalpWa0xzOGNCdHlDcUlTYnRJSy80bTN6UjZpbk5SYXVhamYzbFJWSTJnSnJ4ZUZ5cUliNTRiaFF5M3pzVWI3NnNLcWhIWjJxVmh0SkVtVzdpZzNPakN3eXE3MFF1b0pnWFVyc3BXT1VGM3dRRWdlTFZpVVJMS21Cd1BOcXF6dGJHcXd0T2VtdW8zOXA5T1FxZDl5eVBhbzcvQUVTSFU0ZFd6WW5OUkIyRlNNOFZmWVNvSjIyY3FXY0Z6TmhVOVAzUmRGbmVZbzM3MnFOMjdxY3c5bHllellVeDQyckQ2NWxwcVpqdlJZZk5kOUxlTThsaVZKbTFlV1FmRlZFVHJUVThqT2VWVkRYbk9MdDRsUjFIZnB4NmRRc21wcmxsM0xpaHdWdXB6UEZjMEhqYXFlZnZNQ2pmdGlObFVRODFNTm1WWVJnekRMak9LMHRJMEM1TlZVdGoyZmlLNkVzR3plMGRKbUd2TGZxMHIzVEUrV1FHNjZDNDNsbjlwS3cyK3UzQ1piTG82d3VJalJmQThUeFdhK3pPd1UwZjV1dWZndWxQRkJxZEY4Qnd6Q1cvYmNEVXZQNzFtL0JkUGxYc20wNnkvd0NuUlFOLzhWMHBhU3NhM0d0UE1Ra0REZHJZcGRYL0FOZ0N4Q3VuZFUxdUwxVXIzZDkwdFE0ay9tVlZYdjdmTmIvVlA5ZXZtdWFCVGR0dXJnbmhjVmhtRDBwcnNXeEdDa2hic00xVE1HTnY1bGRFT0Fsak1TNlJNTWFYc3pOMVUrczJmZ3ZaZENPRjFQcyt1eFdjWCtraG9QNUYxMW9QVEdSbUFhQVlyVjIramtxYW1PRU85Qm1LMDhuQkdFZEhlQzAvT2VvbG0vbTFkTittYm5SVldtajZDRW00cDhIakZPTjF1OE8wZlVxU3RxWFZGYkxKTzl6cnVrbmtMM0UrWlZoczNlYUEzQWZ2SjF1eXpjdkZ6RTdhajlteWNQQk52MzdLM2orbUhMUXJRcW45cDBueHIyWnBMZ1AxYVI5OHUvdXRLNk44TWZKVDZJNk5Zamk3Mk83RXo3VThUeGJmMnJ1MzdPNnVrSHBpeFJsWnBKWHNaVFUwam5VT0gwb3l4UVgrTG5lOFZWVFNoc3NXZTUya1dCODFoWXBIeXpSQnJnMitZWHVEejhDblB2cUtxTStZSS9rc1RwUVd5UkQwZUVHTzdibDJ5YzQ1Sm5qZFI4Rjd4djVwdDlzanZ6UUc1TURiQm8rSzdWZ2ZOTnRuSy8vRUFDVVFBUUFDQVFRQ0FnSURBUUFBQUFBQUFBRUFFU0V4UVZGaGNZR1JvUkN4d2RIaDhQL2FBQWdCQVFBQlB4RFhRNk5aUk81TWFSeWEwdnhmNGJyUkdVSUd0UUxkQ0NtNUNXcjhUTVZYTExsVStZVGlDTTQvQ1dLN2dCbVJtb3lPR2RVMGdqTHNCTkU2STI4Y1VkbFNwVnlnL1YvZzFxZEJMcm9nSEVWWFFSTXd3cXRPWnVvS2hhaENjTUtxWnFJVzNCU3JoTklFYXFmZ0h5eXJkL0E0d3hyY0JNUDVvL0RNNmxRZWMzcHZJMGhCai9hSVFZazlDYTlKRHBOaEVXekRNWE51TlJxQnA5SVJOaEI1aG1raHlPQmI1a0tLbWFGR2JKY3dlZ2lHOXhJUUtDTGc1c1FEU045RFk0eVNWb1pnWmxMSUcvNGM2Q1hBcXdMRDFwRGdtck9xUU9rREYzalpqZlpsaVNRTWJZNGkzRURwQlhiTytRVFcwQ2xuVXVPSnFVcE0wcERjSm1yaVRiaUd6VnFMdUlaQW94TlNVSEpmeFhyTW9XUE1KamtqWFlwZUxtckUyaEcxa0pBM1pSVks5S3d2dHpnL3VDQXMwVzN0TVFVTDVxZllSNDF0bS9WTGk4YkdYc0dXRGJ1RVg3WlMramRRUHpXMzl0SmRYUFNnWVMwTmwvdVMrZUxTM3hHYnZ1UHJUY1c4YUNmbU9kMUQ5Ni8xSEhkb0Yvd1ZOZ1dVYit4S0oxMnpmWi9NY25uTi93Q1pVOEd0SFBZUlJGZzZJWDRTSXc0Vy9YQ1pobzEvWkRGYUsydVpaYUhtK2xmeExwYzBTYjRYS3NMMFRKcGNxVjhSMnhCZ3lmdVhxWk5MUS9VTkduZDlNZk5RNnRxaktFNWpaKzREVjlmMVk4dEdGUnpiSWZEbW1EeFpCZTRxOEptcjFwRUdxSXJhOG9ydnBSYk1ia1p5Vk01L1NEakEzVlI3d1JGUUFhV2ZJeGR4TlFmVVhiN2Vpc2RvMXljSmxROHYrNE1DUHViUDNlV0RlTzJDRlRlOVMxWlBVclZha1FyRkdpbzRKWkdCcVhZVGFydGZ3bWNiZ1A4QTJCVTlSVDVHYWc1UG5nUjhCMEllU3crMGR3bVZoTHVZclNqWVdpTlcvd0F5ZzREM1p0aUdSN0pVZGFxWmVWc215UVBWMk5YT2I1bEpVaEhzTmxZUzgyaGExdXBnZE1jQ2libnRjUkpDKzV0YWRaVXhFcnNkK0xMamtaSTN3dnFqcTJKaU5OeURaUmRxa1dvWEVYRG15VDBNK2J2NXlTeVdOVlJFdFpySlVzZzUyV0pxZTB3aHIwTWNDRFVqZkFuRGpDMWhqc2FjWkNtVVBPSFlIc0hUWGFPS0duejhZOGxPeXd2Q3ZhQ1RjQlVhbm9TWEt1QTdXRDM5Q2pPZ3NvOGE3aDI5M3lGdmFVamRIeGxNN3JtQ0toM0FHdmlYNDF4aXdKVEVKaEM4WWJ3SFNESkVzaXVEQ1VIUDZrWFY5NlVZd0NkdDM5UXJWUEdrRHNFYk85bUdVTllhV293TzVDeHVubTRyRFhkQjlSVVRxZGJyd1llczJEQVlCWmF2TVhkdms1SkJza1JNZkh5bG04TzFmYUVUdXV5WmU4eHhETzhFcUVncW1VbHpHQmkxc2FRT2hNeElEM3h5dVB1V1lKQjZsQjk3SXRxMmNKZXNvYkpEQ2RZcEV3d2IxQkswTlUxaGdSc2FpWVdBYVBVRG5QTGNpYUFJTUtleU1YS0x5ak1FV1Z4STNsSWJEbGJBWnRCcDhybU10d1pQcmZwaVVUQVBFdUpWQzJ5YzFDRkJwQ1VRZHdGcThNTTJnWXVLV3l5cEFZZjBKbHREb2tjNGhrNWVSS29rRG1XVnVNMzJvYkFKQnVQQ1lFRjY3SEVaWjY0dmVBS0NUTkV1VTFnMy9DRnlBVWExTEpFR0grdG1PUkZsbnBrMEVSTk9wVFBLdjRnYXV4eU5TT1VhRFVyczJOb1gwQmxYVW1lazY5djRBN0tJQkxuQjRqdzB1bzY3UXhlME5McVdWdkJsaXM4WVN3VjhBZ3hXT1d6VTNuckVscVZFbExnaTNDY1Vua2d4a2dPcjNPSll1STAvY2wwMkNYR2RZTEtsOHlaSURoVTYrSXdMcmV5SHhJL0NUVFZnRzY0QTU1Ykw5ekM5enBqSUJQRVdxVTdJNGhhOFI3VVRSQ1dFZkFqSDZNZW4yQ1BoTGlLUk96ekdJdk5SY1NaNGZFcVVLQW9FN1V4OVZZQWdnMURoWVNhbkJrU1VuN3FQVkhUOE4yNlNvaXlZeGNzSVc1TXFrUm1tMWltdHdNZXRjaEtjUHNsb1U4UlJNZmlQbi9FMVNHSHlqRDJtdW9KUFlOcHFWWmJmMUFLaHlzUkZLOWtHcS8zRVZiZXRZM2xiQnV4ckNPelhGUjl3S1lnS0VBTENVck11cnRIbG94M0xoeG1KRGNDRWJpWHpWNm1lTmpxQ211SW5FWUF0dW9ubTAvVEtyWGhzaks4Y21PR2UyYmZpQUlCUEVLV1E3aVN0MTZvNi9jcFV6cU40TU5UblJHWENKRTFJMTJIcURLd3FTRUxOd3hOV0dwVmg2STlSQmdQS0NDWEx0NjVqNmNoc0crQ0dBcytJRkY4cFR4clZWOVhEM0dsWWRyS2JVWGhtem15OG1DNmxPYkpPdWRsSDJTOHM0b2k0VnpQZHhKTDJtdWRkU1ZoVGszKzJCaTZMQklQZUVOYXhSQTEwbDlzR1JtYmNKK0lFZFhSYUZ1YU5XVjBaMnhpTFN4c29hV0l6cm90YjVwVTlUUmJ2c3VhSWp0M2JHdlVhR0dROHlEYVhVWGtzdElNcXFyM0tpWnJGWVZMS25talM3Z3QrdzRaZzNEc2hoWk9LekgwQUR2YWcvTXN0TkdWbDZlMGZDVjBXSUN4emVMV3JpV1JGaGZGeTlVQ0VwaXBCU0drRGVwUnRTdmJDMGNzRU1wL3BwcStrQXF2RkVDbG1KdEdPaXg3VFExTm13WDRiUE1ORHdXR0MrVXBBNnNVWTMrWTZHSGlGMmtiaTliOFFEck8zM01EU0ErYXpDeWhVTU40T3VscWovTXRGUUVyVjl5c2tHaWY2bVhuaG9zNzVpQXdmTS8vRUFDSVJBQUlDQVFRREFRRUJBQUFBQUFBQUFBRUNBeE1BQkFZUkVnVVFGQlVXSVAvYUFBZ0JBZ0VCQWdBUzIzWFhXM1cyM1hXMldXV1hYMzMzV1dkKzlsdHRsdHR0L3dCSDBYblVYM1hYWDMzMjNDUVNpVzIwekdhM3ZaYmNaak5iZGRkZGRjSjdyaE9KNzd4UHoyc012Y3lHV3d5R1c0em1hMjYyMjYrNFRpZThUQ2NnNFRoQkdja24wU2ZSd25ua3RaWlpjSlJPWStoanFyYU13bU14MUdHc3hWR013MVY5REhWVlVJekVORitPdmlHOFFmRER3VVcxbjJrdTE1TnVIYmY0QTIzL0FDWTJ2RnRtWGJEYmJUYkEyNy9ObmFwMmRGdGIrWGZSdkNYR3RpMThPdTA4cUtkTk5wWnZIemFMNFVnSGpZdkZEUko0bE52THRoZkJmanQ0bC9GeWVQTWpCNGpCOG55cEJIQU1XZk90Ykltckc0bDNKQnVEVCtWRTNKYVI1R0o5Y0JWVlZpUXd0by9qR2krTTZaNFprbGtBUTZiVXdlVWg4bE41S1RWWWY4REFGTUJCRW94QnlTY21YeU9tN1F6Uk9rcXUyRndRTTV3RUZHUjFsUmppUHljT05rOFdvMHZYVFRLeUpnd0gwU1hNaXpDWHVzOFdwWFVyS2txeWhtRDVPa3NWY0xvNjZnSHFEY1piQ3dZTUg0VkVXTmdBUkxhN1NHUkRGR2kra0t5akMzUFBJWlpFZE1VQmdSTGQzTEdWNUdOb2tFZ2tCUXJuUFlNRDZHQjQ5U2tvSFBibnVXT0hDM3J0WWpyS1NUeUNDR1VnY0tZNWhQaEdFODRjS25PUVFleEI5Y0RCZ0lJT0RBY0RkK3hadFI5UjFaMW4xZlRZRzQ0STQ0d1lNQkdEQXJTL1VkUzA1eXFxcXNqaFQ2SS94d001T3BPbzAycGZUL00ybHdrbnYzN2R3QXFMLzhRQU5SRUFBZ0VDQkFNR0F3WUhBQUFBQUFBQUFBRUNFU0VEQkJBeEJTQmhFakpCVVlHUklqQnhFek9Db2JIUkJpTkNRM0tTOFAvYUFBZ0JBZ0VEUHdCQzFZOU91aTBaSTZuVWlpSXRHU0h0eUxsUkJDRVdHUHowNmlRdVpqb1BXZy9BWXluanBiNW5VNm5VNmo4eXVpNUxiNk1zVzFmbnEwZGRYeUk2NldIcXhqb1BSajBwcTlHaGpKREhRYUg1RDFma0pqR1cySHBUUmpHU0hveGt4a2tNNGRpYlc5VEtVM2I5VVpOS3RHMzFNdTIyNHVuUXdISzFmY3lENzhtamhzNy9BR3JmME1qRmZESnN5TUkxbEdiOWFIRGV6WHNTUnc2amQvZW42bkRJdDl0eVgvZERnTTRmZVgvRlU0VHZIRTkyMGNMVXQxL3M2R1RoTHRZY0l2M2Y2MU1tMDVTd2xYMC9ZNFpXazR0ZlEvaDZLK1B0ZTV3RnVtSGhOK3RUZ2tPL2d2MWY3TTRianA5aUVVdjhxTXlVSlY3TmZ4bkNGSCtaaHIxYmY2TTRFL0dDK3ZiSW9VZGljSFptUEZia0l2NDRWT0ZTNzJIUTRVKzdiME10TmZDeUZMRTVKcWhLN2FJcDJSTGRHSXJPTlRHbXJRWm5ZdXNJczRqMktTaWw5V1pxZThmekp6WHh1aGtsM2szNm1TaXU0aktSL3RyMk1sVHVMMk1xdG9vd3R4TkNrSXFUZDBZeThETUxhTE0zMVgxWm1vUDd4bWFqYjdSa3Bic3FpVEg0bWF5LzNjL2U1bTRWVS95SkozdVYzUmc0cUlTRUpEYWRCTHZOR1hsL1h5TFJFR2hTM1Fuc1I4eUxXNVFvU295VUU2UkU1TkNyNUU0azhMWkRTbzBRa3Q2R0ZDMGJzeDhYZDBYVG5zU3FOYmxyTWFzMFJrVXJRWXFDdUtnbjhTSndkSHBIZEVHaExTcUl0TXFxOHlpSmJFWEdqWkYrSWxkTWV6RXhrcURGSk5GQ1dHeE4wa1E3VnhTVlJsR1dzeGFJZmdNZWswck1tbVRKbGJqR0pvUWhOT3hSdXhSMVExYVJLTjRzcjNrUmxzVVQwUWhVNVBNVDBjU200bm9sdVFhSWVZdk1VaWpzSVZLUFJ4WlMwaFBuaStTdzRvVEtvYXFKRGw0bEU2RFcraVlxNldkU2l0OGhva3R5RXZFYVJUVmplNVlsb3FhVTBhVExYNW1JV2pST0pVVEVNZklocGErUkpMa2FIejIwcG9oSlhNR0c4a1pkK0pncFdZaDN1WXNpYkpOVitZekR3OTJRSFRZeDU3T2hON3M2NlI4V0x6SWVaSHdZblZJNmZKb3FtR2lmZXFZazVVa3FtVmVFM1NqSnBmREpmbVppRmFvb3gxSkVtTHlKTHdKTWJaYXBKV1AveEFBaUVRQUNBZ0VGQVFFQkFRRUFBQUFBQUFBQkF3SVRBQVFGQmhFU0VCVWdGQmIvMmdBSUFRTUJBUUlBQ3dxb0txcENRb0xDcWdvS0NhcVFta0pxRUt4QVE4QlFVRWhRVFJRRVVCSVVFaE5OTklWVlVGMStQUGdSQ1FnSkNncWtJQ0FnSW9DS0FnSnBwcXJyOEdIbng1Q3drSkNRa0pDQWdJQ1FnSnBxcnJycXFLcXFhcTY0ckNvckVJcmlxS2dxS1FtS2FLYWFhYXFxYWFhS0tLQmdJTVNERUFSakVSQUFYVlhWVlZWVlZUVFRSUU5lTjZPOVEzbU8rSGtNK1hxNWllV0w1UXZrNDVLZVVEbC8vVk41S25rZzVGUGt2NzQ1QU9TamxVK1JEa0M5VkNZV2RGUGIzN2U1TXNpOU9xUnVLZFdOWE52NkxOME90L1VPK3k1Q2Q0L1ZodVN0ZXJXeFhESU1EeHFmOUVuVFlSSkF5SjdneWVqUEhUeDF1d2FqYkNuSUJXS0VWL08rNUdlU210MFhCNTFFWGhrR0praGM0U3pVS2RvV2FOTzNxMG95UDhFK2prNHhpVjlHUmlESEZTMEdvR09VNU05T0ZRd1E2d2Z4S0pnWUJaaDRLL0l5T1FPbGNqVWR2VklHV0RQR0NQbnlJZVBFWUJja1QweGdWbFhVQ3JFVFd3VFpDY1NxT1JrWUJZV0ZpUFJBamhZWk1pYzZyQzRCV0xrR1NuUEJnSFdDQUhYa1JyS3o4SXl1Z0tpSXJndUlLeW9vcGlNQUVRT3V2cGpKSmgwQUlpSWhHTVpDUUl5SjgxU1JMVERCblhYbm9qNGNrc3JHQWlZSXdEQVl6QkdkZEQ0TUIvanJ5YzZ6d0lDSXlJQUVRb0tDaERvWUNQZ1BmOEVsa1Q1aEVMR0JsbGdtREU0TUgwWVBveXp2dzFNWEJvYUl4aUlpQWgxNTdCSi8vOFFBTlJFQUFnRUNCQU1HQkFNSkFBQUFBQUFBQUFFQ0VTRURCQkF4QlNCQkVpSlJZWUdSTURKeGdrS3g4UVlUVXBLaG90SGg4UC9hQUFnQkF3RURQd0JqMWRCaktES2kwOGgrQXg2b1MwWEl4akdPbkloTFYrQTlGUVFoQ0Z5dm5YVVZOSHA1RDhOR01ZeGxPWmMxT2Q2UGxzTVo1Y2kwWEl0SDBHTThqeTFZeDhxRUlSQkNFUll0RlRSaldxRm9oQ0VMVGlXSHZmME01dFJMMFpuWlc3U1M4a1psUnRKZXFNeW91dFBZNGlyeGdwVS83eFJ4S0Z2M1NYMS9VNGpMZUVVY1RuYU00TDBxY1cyN2NYNkk0cXJPbnNjV2NlNzJhL1EvYUNPSTMyTGZiUTR4dExEL0FLSEZ1enQvYWpQVGk0NGs1TDJYNVVNL1hzeHhYVDEveWNTUzdzay9xY2ZrNlFVVjZIR2xlZUlsNkhGNS9MaXIyLzBaL0NYZmszOXBtWlJwV24ybkVwUzdrNitpWDVvNHZTOFpQK1VrOXl0bVljbGRHRExvU2FwQ2REaWthOW5FcWNVaXFTZFRNeCtaRXEzSVJaSFpOazVMZGtkbVJwVk13WU84MFpXYXBLU01pcFZqSnYwTXJCV2I5aU1Qa1ZmcVp4N05JenIvQUJNemIvRy9jemY4Yjl6TXZlVE1iYTR5aUdoaU1QcXpBYWZlUmx2cVphVmU0akt0MTdDOWhMNWJDVlJYRXRqS1pqNTRYOHJHVmxlS3FZUkZSc3pFd1d5Y0JqYUVuY2sxM0V6TlJWZXp5dWhJbW1OUnZvNkRRaFVyVVJoemQyTXNJaE1neFIyTVdWNVVTTURDMlZXZWhWOGpFSzlDMVR5RXlVU3F2cEpJbHV6dmJEN05HSjNFN2trMlloMkt1bW5acVNxUGxxVFl5Z3h2UnJSQ2pzT0pWQ2FMTmpLTVY2RlV4ZENnMk1xK1NIVWcwUW9KSFF1NjZORnRIRjFMV1ltcmtXblFyWmxuMldTdVBxTm9lajVXa01SWnVKSVpLV3hpSmt5U1JPSzJFME5GYTZWUlF0YmxXa2xwWFJpbFhTZ3VvbmRhS2dtVkhVbWlTV20vd0V4ZENVZCtYeUdob1dxdVZGUVRMMjFzTG5UR2hqb0o2SnJXbXcweFV0cFRTbnhFOVdVM1pKN0lrdWhQd01VbDRpVzdJSkNWdmhwSzVoK0k1YkluUW0rcGhyZXJJTFpGZWd2QWxUWWZnVDhDWkpmQ2hjazlrS1ZtS0VhcG1OQ2RLMVJIWnBtSExabG14VTNFaUtPcVltdHlLTGZyb3R6LzJRPT0iIGlkPSJlIiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIvPjwvZGVmcz48L3N2Zz4=", + "description": "A beneficial layer in the upper atmosphere, but harmful when present near ground level. Results displays mainly from vehicle exhaust and industrial emissions by combining the latest and aggregated values and optional simplified chart. ", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'ozone', label: 'Ozone', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'µg/m³', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'ozone', 'µg/m³', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Ozone\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#3B911C\"},{\"from\":50,\"to\":100,\"color\":\"#7CC322\"},{\"from\":100,\"to\":130,\"color\":\"#F89E0D\"},{\"from\":130,\"to\":240,\"color\":\"#F77410\"},{\"from\":240,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nif (value < -10) {\\n\\tvalue = -10;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:b3pvbmUtdmFsdWUtYW5kLWNoYXJ0LWNhcmQtYmFja2dyb3VuZC5wbmc=:b3pvbmUtdmFsdWUtYW5kLWNoYXJ0LWNhcmQtYmFja2dyb3VuZC5wbmc=;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Ozone\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"public\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "O3", + "ozone" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/persistent_rpc_table.json b/application/src/main/data/json/system/widget_types/persistent_rpc_table.json new file mode 100644 index 0000000000..2dadf40b85 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/persistent_rpc_table.json @@ -0,0 +1,34 @@ +{ + "fqn": "control_widgets.persistent_table", + "name": "Persistent RPC table", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays Persistent RPC requests based on entity alias and optional filter with the ability of pagination. It also allows to resend or delete selected RPC requests.", + "descriptor": { + "type": "rpc", + "sizeX": 7.5, + "sizeY": 4, + "resources": [], + "templateHtml": "", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-persistent-table-widget-settings", + "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"enableStickyAction\":true,\"enableFilter\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"enableStickyHeader\":true,\"displayColumns\":[\"rpcId\",\"messageType\",\"status\",\"method\",\"createdTime\",\"expirationTime\"],\"displayDetails\":true,\"defaultSortOrder\":\"-createdTime\",\"allowSendRequest\":true,\"allowDelete\":true},\"title\":\"Persistent RPC table\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px\"},\"targetDeviceAliasIds\":[]}" + }, + "externalId": null, + "tags": [ + "command", + "downlink", + "device configuration", + "device control", + "invocation", + "remote method", + "remote function", + "interface", + "subroutine call", + "inter-process communication", + "server request" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/photo_camera_input.json b/application/src/main/data/json/system/widget_types/photo_camera_input.json new file mode 100644 index 0000000000..901d992009 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/photo_camera_input.json @@ -0,0 +1,21 @@ +{ + "fqn": "input_widgets.web_camera_input", + "name": "Photo camera input", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "A simple form to take web camera images or upload photos. The taken picture is stored in a configurable format converted to Base64 data as the target entity's server-side attribute or telemetry value. ", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.photoCameraInputWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-photo-camera-input-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Photo camera input\",\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pie___chart_js.json b/application/src/main/data/json/system/widget_types/pie___chart_js.json new file mode 100644 index 0000000000..f54c4f9702 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pie___chart_js.json @@ -0,0 +1,25 @@ +{ + "fqn": "charts.pie_chart_js", + "name": "Pie - Chart.js", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest values of the attributes or time series data for multiple entities in a pie chart. Supports numeric values only.", + "descriptor": { + "type": "latest", + "sizeX": 8, + "sizeY": 5, + "resources": [ + { + "url": "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js" + } + ], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showTooltip = utils.defaultValue(settings.showTooltip, true);\n \n Chart.defaults.global.tooltips.enabled = settings.showTooltip;\n \n var pieData = {\n labels: [],\n datasets: []\n };\n\n var dataset = {\n data: [],\n backgroundColor: [],\n borderColor: [],\n borderWidth: [],\n hoverBackgroundColor: []\n }\n \n pieData.datasets.push(dataset);\n \n for (var i=0; i < self.ctx.data.length; i++) {\n var dataKey = self.ctx.data[i].dataKey;\n var units = dataKey.units && dataKey.units.length ? dataKey.units : self.ctx.units;\n units = units ? (' (' + units + ')') : '';\n pieData.labels.push(dataKey.label + units);\n dataset.data.push(0);\n var hoverBackgroundColor = tinycolor(dataKey.color).lighten(15);\n var borderColor = tinycolor(dataKey.color).darken();\n dataset.backgroundColor.push(dataKey.color);\n dataset.borderColor.push('#fff');\n dataset.borderWidth.push(5);\n dataset.hoverBackgroundColor.push(hoverBackgroundColor.toRgbString());\n }\n\n var ctx = $('#pieChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'pie',\n data: pieData,\n options: {\n responsive: false,\n maintainAspectRatio: false\n }\n }); \n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.data.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData.data.length > 0) {\n var decimals;\n if (typeof cellData.dataKey.decimals !== 'undefined' \n && cellData.dataKey.decimals !== null ) {\n decimals = cellData.dataKey.decimals; \n } else {\n decimals = self.ctx.decimals;\n }\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = self.ctx.utils.formatValue(tvPair[1], decimals);\n self.ctx.chart.data.datasets[0].data[i] = parseFloat(value);\n }\n }\n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n self.ctx.chart.resize();\n}\n\nself.onDestroy = function() {\n self.ctx.chart.destroy();\n self.ctx.chart = null;\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-chart-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Pie - Chart.js\"}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pie___flot.json b/application/src/main/data/json/system/widget_types/pie___flot.json new file mode 100644 index 0000000000..a50993639e --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pie___flot.json @@ -0,0 +1,22 @@ +{ + "fqn": "charts.pie", + "name": "Pie - Flot", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest values of the attributes or time series data for multiple entities in a pie chart. Supports numeric values only.", + "descriptor": { + "type": "latest", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.pie-label {\n font-size: 12px;\n font-family: 'Roboto';\n font-weight: bold;\n text-align: center;\n padding: 2px;\n color: white;\n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'pie'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\nself.actionSources = function() {\n return {\n 'sliceClick': {\n name: 'widget-action.pie-slice-click',\n multiple: false\n }\n };\n}\n", + "settingsSchema": "{}\n", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-flot-pie-widget-settings", + "dataKeySettingsDirective": "tb-flot-pie-key-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.6114638304362894,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.9955906536344441,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.9430835931647599,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"radius\":1,\"fontColor\":\"#545454\",\"fontSize\":10,\"decimals\":1,\"legend\":{\"show\":true,\"position\":\"nw\",\"labelBoxBorderColor\":\"#CCCCCC\",\"backgroundColor\":\"#F0F0F0\",\"backgroundOpacity\":0.85},\"innerRadius\":0,\"showLabels\":true,\"showPercentages\":true,\"stroke\":{\"width\":5},\"tilt\":1,\"animatedPie\":false},\"title\":\"Pie - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pm10_card.json b/application/src/main/data/json/system/widget_types/pm10_card.json new file mode 100644 index 0000000000..0d15c4b323 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pm10_card.json @@ -0,0 +1,37 @@ +{ + "fqn": "pm10_card", + "name": "PM10 card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest fine and coarse particulate matter (PM10) telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pm10', label: 'PM10', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM10\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"bubble_chart\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#80C32C\"},{\"from\":20,\"to\":50,\"color\":\"#FFA600\"},{\"from\":50,\"to\":150,\"color\":\"#F36900\"},{\"from\":150,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":32,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#80C32C\"},{\"from\":20,\"to\":50,\"color\":\"#FFA600\"},{\"from\":50,\"to\":150,\"color\":\"#F36900\"},{\"from\":150,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Indoor PM10 card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "pm10", + "coarse particulates", + "coarse particles", + "particulate matter 10", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pm10_card_with_background.json b/application/src/main/data/json/system/widget_types/pm10_card_with_background.json new file mode 100644 index 0000000000..8b677898c8 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pm10_card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "pm10_card_with_background", + "name": "PM10 card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest fine and coarse particulate matter (PM10) telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pm10', label: 'PM10', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM10\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"bubble_chart\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#7CC322\"},{\"from\":20,\"to\":50,\"color\":\"#F89E0D\"},{\"from\":50,\"to\":150,\"color\":\"#F77410\"},{\"from\":150,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":32,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#7CC322\"},{\"from\":20,\"to\":50,\"color\":\"#F89E0D\"},{\"from\":50,\"to\":150,\"color\":\"#F77410\"},{\"from\":150,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Indoor PM10 card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "pm10", + "coarse particulates", + "coarse particles", + "particulate matter 10", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pm10_chart_card.json b/application/src/main/data/json/system/widget_types/pm10_chart_card.json new file mode 100644 index 0000000000..6d3692826c --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pm10_chart_card.json @@ -0,0 +1,40 @@ +{ + "fqn": "pm10_chart_card", + "name": "PM10 chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a fine and coarse particulate matter (PM10) data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pm10', label: 'PM10', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'µg/m³', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'pm10', 'µg/m³', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM10\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#80C32C\"},{\"from\":20,\"to\":50,\"color\":\"#FFA600\"},{\"from\":50,\"to\":150,\"color\":\"#F36900\"},{\"from\":150,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nif (value < -10) {\\n\\tvalue = -10;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"PM10\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"bubble_chart\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "pm10", + "coarse particulates", + "coarse particles", + "particulate matter 10", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pm10_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/pm10_chart_card_with_background.json new file mode 100644 index 0000000000..54cb32eac4 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pm10_chart_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "pm10_chart_card_with_background", + "name": "PM10 chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a fine and coarse particulate matter (PM10) data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pm10', label: 'PM10', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'µg/m³', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'pm10', 'µg/m³', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM10\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#7CC322\"},{\"from\":20,\"to\":50,\"color\":\"#F89E0D\"},{\"from\":50,\"to\":150,\"color\":\"#F77410\"},{\"from\":150,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nif (value < -10) {\\n\\tvalue = -10;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"PM10\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"bubble_chart\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "pm10", + "coarse particulates", + "coarse particles", + "particulate matter 10", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pm2_5_card.json b/application/src/main/data/json/system/widget_types/pm2_5_card.json new file mode 100644 index 0000000000..0f30162aa2 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pm2_5_card.json @@ -0,0 +1,38 @@ +{ + "fqn": "pm2_5_card", + "name": "PM2.5 card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest fine particulate matter (PM2.5) telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pm2.5', label: 'PM2.5', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM2.5\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 120 - 60;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"bubble_chart\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#80C32C\"},{\"from\":10,\"to\":35,\"color\":\"#FFA600\"},{\"from\":35,\"to\":75,\"color\":\"#F36900\"},{\"from\":75,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":32,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#80C32C\"},{\"from\":10,\"to\":35,\"color\":\"#FFA600\"},{\"from\":35,\"to\":75,\"color\":\"#F36900\"},{\"from\":75,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"PM2.5 card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "pm2.5", + "particulate", + "matter", + "air", + "fine particulates", + "fine particles", + "particulate matter 2.5", + "airborne particles", + "microscopic particles", + "respirable particles", + "aerosol particles", + "dust particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pm2_5_card_with_background.json b/application/src/main/data/json/system/widget_types/pm2_5_card_with_background.json new file mode 100644 index 0000000000..f7b55c7aaa --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pm2_5_card_with_background.json @@ -0,0 +1,38 @@ +{ + "fqn": "pm2_5_card_with_background", + "name": "PM2.5 card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest fine particulate matter (PM2.5) telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pm2.5', label: 'PM2.5', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM2.5\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 120 - 60;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"bubble_chart\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#7CC322\"},{\"from\":10,\"to\":35,\"color\":\"#F89E0D\"},{\"from\":35,\"to\":75,\"color\":\"#F77410\"},{\"from\":75,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":32,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#7CC322\"},{\"from\":10,\"to\":35,\"color\":\"#F89E0D\"},{\"from\":35,\"to\":75,\"color\":\"#F77410\"},{\"from\":75,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"PM2.5 card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "pm2.5", + "particulate", + "matter", + "air", + "fine particulates", + "fine particles", + "particulate matter 2.5", + "airborne particles", + "microscopic particles", + "respirable particles", + "aerosol particles", + "dust particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pm2_5_chart_card.json b/application/src/main/data/json/system/widget_types/pm2_5_chart_card.json new file mode 100644 index 0000000000..8b549b24ed --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pm2_5_chart_card.json @@ -0,0 +1,41 @@ +{ + "fqn": "pm2_5_chart_card", + "name": "PM2.5 chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a fine particulate matter (PM2.5) data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pm2.5', label: 'PM2.5', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'µg/m³', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'pm2.5', 'µg/m³', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM2.5\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 120 - 60;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#80C32C\"},{\"from\":10,\"to\":35,\"color\":\"#FFA600\"},{\"from\":35,\"to\":75,\"color\":\"#F36900\"},{\"from\":75,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 120 - 60;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nif (value < -10) {\\n\\tvalue = -10;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"PM2.5\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"bubble_chart\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "environment", + "pm2.5", + "particulate", + "matter", + "air", + "fine particulates", + "fine particles", + "particulate matter 2.5", + "airborne particles", + "microscopic particles", + "respirable particles", + "aerosol particles", + "dust particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pm2_5_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/pm2_5_chart_card_with_background.json new file mode 100644 index 0000000000..bddaa13a6d --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pm2_5_chart_card_with_background.json @@ -0,0 +1,41 @@ +{ + "fqn": "pm2_5_chart_card_with_background", + "name": "PM2.5 chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a fine particulate matter (PM2.5) data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pm2.5', label: 'PM2.5', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'µg/m³', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'pm2.5', 'µg/m³', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM2.5\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 120 - 60;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#7CC322\"},{\"from\":10,\"to\":35,\"color\":\"#F89E0D\"},{\"from\":35,\"to\":75,\"color\":\"#F77410\"},{\"from\":75,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 120 - 60;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nif (value < -10) {\\n\\tvalue = -10;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"PM2.5\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"bubble_chart\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "environment", + "pm2.5", + "particulate", + "matter", + "air", + "fine particulates", + "fine particles", + "particulate matter 2.5", + "airborne particles", + "microscopic particles", + "respirable particles", + "aerosol particles", + "dust particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/polar_area.json b/application/src/main/data/json/system/widget_types/polar_area.json new file mode 100644 index 0000000000..4cc516321c --- /dev/null +++ b/application/src/main/data/json/system/widget_types/polar_area.json @@ -0,0 +1,25 @@ +{ + "fqn": "charts.polar_area_chart_js", + "name": "Polar Area", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest values of the attributes or time series data for multiple entities in a polar area chart. Supports numeric values only.", + "descriptor": { + "type": "latest", + "sizeX": 7, + "sizeY": 5, + "resources": [ + { + "url": "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js" + } + ], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showTooltip = utils.defaultValue(settings.showTooltip, true);\n \n Chart.defaults.global.tooltips.enabled = settings.showTooltip;\n \n var pieData = {\n labels: [],\n datasets: []\n };\n\n var dataset = {\n data: [],\n backgroundColor: [],\n borderColor: [],\n borderWidth: [],\n hoverBackgroundColor: []\n }\n\n pieData.datasets.push(dataset);\n \n for (var i = 0; i < self.ctx.data.length; i++) {\n var dataKey = self.ctx.data[i].dataKey;\n var units = dataKey.units && dataKey.units.length ? dataKey.units : self.ctx.units;\n units = units ? (' (' + units + ')') : '';\n pieData.labels.push(dataKey.label + units);\n dataset.data.push(0);\n var hoverBackgroundColor = tinycolor(dataKey.color).lighten(15);\n var borderColor = tinycolor(dataKey.color).darken();\n dataset.backgroundColor.push(dataKey.color);\n dataset.borderColor.push('#fff');\n dataset.borderWidth.push(5);\n dataset.hoverBackgroundColor.push(hoverBackgroundColor.toRgbString());\n }\n \n var floatingPoint;\n if (typeof self.ctx.decimals !== 'undefined' && self.ctx.decimals !== null) {\n floatingPoint = self.ctx.widget.config.decimals;\n } else {\n floatingPoint = 2;\n }\n\n\n var ctx = $('#pieChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'polarArea',\n data: pieData,\n options: {\n responsive: false,\n maintainAspectRatio: false,\n scale: {\n ticks: {\n callback: function(tick) {\n \treturn tick.toFixed(floatingPoint);\n }\n }\n }\n }\n });\n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.data.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData.data.length > 0) {\n var decimals;\n if (typeof cellData.dataKey.decimals !== 'undefined' \n && cellData.dataKey.decimals !== null ) {\n decimals = cellData.dataKey.decimals; \n } else {\n decimals = self.ctx.decimals;\n }\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = self.ctx.utils.formatValue(tvPair[1], decimals);\n self.ctx.chart.data.datasets[0].data[i] = parseFloat(value);\n }\n }\n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n if (self.ctx.height >= 70) {\n try {\n self.ctx.chart.resize();\n } catch (e) {}\n }\n}\n\nself.onDestroy = function() {\n self.ctx.chart.destroy();\n self.ctx.chart = null;\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-chart-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fifth\",\"color\":\"#607d8b\",\"settings\":{},\"_hash\":0.2074391823443591,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Polar Area\"}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/power_consumption_card.json b/application/src/main/data/json/system/widget_types/power_consumption_card.json new file mode 100644 index 0000000000..0e38e25a08 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/power_consumption_card.json @@ -0,0 +1,32 @@ +{ + "fqn": "power_consumption_card", + "name": "Power consumption card", + "deprecated": false, + "image": "tb-image:cG93ZXJfY29uc3VtcHRpb25fY2FyZC5zdmc=:cG93ZXJfY29uc3VtcHRpb25fY2FyZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxwYXRoIGZpbGw9IiMzRkE3MUEiIGQ9Ik0zMy4yIDUwLjVIMzJsMS4zLTloLTQuNWMtLjggMC0uNy0uNS0uNS0xIC4yLS40IDAgMCAwIDBsNy42LTEzLjNIMzdsLTEuMyA5aDQuNWMuNiAwIC43LjUuNi43di4ybC03LjcgMTMuNFoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNNjAuNiAzMy41aC0yLjN2LTEuMmgyLjNjLjQgMCAuNyAwIDEtLjIuMi0uMS40LS4zLjUtLjZsLjItLjdjMC0uMyAwLS42LS4yLS44LS4xLS4zLS4zLS41LS41LS42LS4zLS4yLS42LS4zLTEtLjNoLTEuOXY3LjdoLTEuNVYyOGgzLjRjLjcgMCAxLjMuMSAxLjcuNC41LjIuOS42IDEuMSAxIC4zLjQuNC45LjQgMS40IDAgLjYtLjEgMS4xLS40IDEuNS0uMi40LS42LjgtMSAxLS41LjItMS4xLjMtMS44LjNabTQuMyAwYzAtLjUgMC0xIC4yLTEuNC4xLS40LjMtLjguNi0xIC4zLS40LjYtLjYgMS0uOGwxLjMtLjJjLjQgMCAuOSAwIDEuMy4ybDEgLjcuNSAxLjFjLjIuNC4zLjkuMyAxLjN2LjJjMCAuNS0uMSAxLS4zIDEuMyAwIC40LS4zLjgtLjYgMWEyLjggMi44IDAgMCAxLTIuMiAxYy0uNSAwLTEgMC0xLjMtLjJsLTEtLjdjLS4zLS4zLS41LS43LS42LTFsLS4yLTEuNFptMS41IDB2MWwuMy42LjUuNS44LjJhMS40IDEuNCAwIDAgMCAxLjItLjdsLjMtLjd2LTEuOGMwLS4zLS4yLS41LS4zLS43YTEuNCAxLjQgMCAwIDAtMS4yLS42bC0uOC4xLS41LjUtLjIuNy0uMS44Wm03LjkgMS45IDEuNS01LjJoMWwtLjMgMS41LTEuNSA1LjFINzRsLjItMS40Wm0tMS01LjIgMS4zIDUuMnYxLjRoLS45TDcyIDMwLjJoMS41Wm00LjkgNS4xIDEuMi01LjFoMS40TDc5IDM2LjhoLTFsLjItMS41Wm0tMS4zLTUuMSAxLjUgNS4xLjIgMS41aC0uOGwtMS42LTUtLjItMS42aDFabTggNi44Yy0uNSAwLTEtLjEtMS4zLS4zQTMgMyAwIDAgMSA4MiAzNWMtLjItLjQtLjMtLjgtLjMtMS4zdi0uMmMwLS41LjEtMSAuMy0xLjQuMS0uNC4zLS44LjYtMSAuMy0uNC42LS42IDEtLjhhMyAzIDAgMCAxIDEuMS0uMmMuNSAwIDEgMCAxLjMuMmwuOS43LjUgMSAuMiAxLjN2LjdoLTUuMXYtMS4xSDg2di0uMUw4NiAzMmwtLjUtLjVjLS4yLS4yLS40LS4yLS44LS4ybC0uNi4xLS41LjUtLjMuN3YybC40LjZjLjEuMi4zLjMuNi40bC43LjJhMiAyIDAgMCAwIDEuNy0uOGwuOC43YTIuOCAyLjggMCAwIDEtMS40IDFjLS40LjItLjcuMy0xLjIuM1ptNS41LTUuNXY1LjNIODl2LTYuNmgxLjRsLjEgMS4zWm0yLTEuM3YxLjNhMi45IDIuOSAwIDAgMC0xLjIgMCAxLjMgMS4zIDAgMCAwLS44LjlsLS4xLjZoLS40TDkwIDMybC40LTEgLjYtLjZhMS43IDEuNyAwIDAgMSAxLjItLjJoLjJabTcuMiA1LjZjLjIgMCAuNSAwIC42LS4yLjIgMCAuNC0uMi41LS40bC4yLS42aDEuNGMwIC41LS4xLjktLjQgMS4yLS4yLjQtLjUuNi0xIC44YTMgMyAwIDAgMS0xLjMuNGMtLjUgMC0xLS4xLTEuMy0uM2wtMS0uNy0uNS0xLS4yLTEuNHYtLjJjMC0uNCAwLS45LjItMS4zLjEtLjQuMy0uNy42LTEgLjItLjMuNS0uNi45LS44YTMgMyAwIDAgMSAxLjMtLjJjLjUgMCAxIDAgMS40LjMuNC4yLjcuNSAxIC45LjIuMy4zLjguMyAxLjNIMTAxYzAtLjMgMC0uNS0uMi0uNyAwLS4yLS4yLS4zLS40LS41bC0uNy0uMS0uOC4xLS40LjUtLjIuNy0uMS44djFsLjMuNy40LjUuOC4yWm0zLjgtMi4ydi0uMmMwLS40IDAtLjkuMi0xLjMuMS0uNC4zLS44LjYtMSAuMi0uNC42LS42IDEtLjhsMS4yLS4yYy41IDAgMSAwIDEuMy4ybDEgLjdjLjMuMy41LjcuNiAxLjEuMi40LjIuOS4yIDEuM3YuMmMwIC41IDAgMS0uMiAxLjMtLjEuNC0uMy44LS42IDEtLjMuNC0uNi42LTEgLjgtLjMuMi0uOC4zLTEuMi4zLS41IDAtMS0uMS0xLjQtLjNsLTEtLjctLjUtMWMtLjItLjUtLjItMS0uMi0xLjRabTEuNC0uMnYuMmwuMS44YzAgLjMuMi41LjMuN2wuNS41LjguMmExLjQgMS40IDAgMCAwIDEuMi0uN2wuMy0uN3YtMS44bC0uMy0uN2ExLjQgMS40IDAgMCAwLTEuMy0uNmwtLjcuMS0uNS41LS4zLjd2LjhabTcuNi0xLjh2NS4ySDExMXYtNi42aDEuM2wuMSAxLjRabS0uMiAxLjdoLS41YzAtLjUgMC0xIC4yLTEuM2wuNS0xYTIuNCAyLjQgMCAwIDEgMi0xbC44LjIuNy40LjQuOC4xIDEuMXY0LjNIMTE1di00LjNjMC0uMyAwLS41LS4yLS43IDAtLjItLjItLjMtLjQtLjRsLS42LS4xYTEuNCAxLjQgMCAwIDAtMS4yLjZsLS4zLjYtLjEuOFptOS43IDEuNy0uMS0uNGExIDEgMCAwIDAtLjUtLjNsLS45LS4zLTEtLjJhMyAzIDAgMCAxLS43LS40bC0uNS0uNi0uMS0uNy4xLS44LjUtLjYuOC0uNSAxLjEtLjFjLjYgMCAxIDAgMS40LjMuNC4xLjcuNCAxIC43LjIuMy4zLjcuMyAxaC0xLjVsLS4xLS40YTEgMSAwIDAgMC0uNC0uNGwtLjctLjFoLS42bC0uNC40YS44LjggMCAwIDAgMCAuN2MwIC4xIDAgLjIuMi4zbC40LjIuNy4xIDEuMy40LjguN2MuMi4yLjMuNi4zIDFhMS43IDEuNyAwIDAgMS0uNyAxLjRsLS45LjQtMSAuMmMtLjcgMC0xLjItLjItMS42LS40bC0xLS44YTIgMiAwIDAgMS0uMy0xaDEuNWMwIC4yIDAgLjQuMi42bC41LjRIMTIxLjNsLjQtLjMuMi0uNVptNi45LjN2LTUuMWgxLjV2Ni42aC0xLjRsLS4xLTEuNVptLjItMS40aC41bC0uMSAxLjItLjUgMWMtLjIuMy0uNS41LS44LjYtLjMuMi0uNy4zLTEuMS4zLS40IDAtLjYgMC0xLS4yLS4yIDAtLjQtLjItLjYtLjRhMiAyIDAgMCAxLS41LS44bC0uMS0xLjF2LTQuM2gxLjVWMzUuMWwuMy40LjMuMmguNGMuNSAwIC44IDAgMS0uMi4zLS4yLjQtLjQuNS0uNy4yLS4yLjItLjYuMi0xWm00LjUtMi40djUuM0gxMzJ2LTYuNmgxLjR2MS4zWm0tLjMgMS44aC0uNWMwLS41IDAtMSAuMi0xLjNsLjUtMSAuOC0uNyAxLjItLjIuOC4xLjcuNC40LjcuMSAxLjF2NC40SDEzNnYtNC4zYzAtLjMgMC0uNS0uMi0uNyAwLS4yLS4yLS4zLS40LS40bC0uNi0uMS0uNy4xLS41LjVhMiAyIDAgMCAwLS4zLjZ2LjhabTQuMS0uNC0uNi4xLjEtMS4xLjUtMSAuOC0uNiAxLjEtLjIgMSAuMS42LjQuNS44LjEgMS4xdjQuM0gxNDB2LTQuM2wtLjEtLjctLjQtLjQtLjctLjEtLjYuMS0uNC4zLS4zLjZ2LjZabTcuMy0xLjR2Ny45aC0xLjV2LTkuMmgxLjRsLjEgMS4zWm00LjMgMi0uMSAxLjQtLjUgMS4xLS44LjdhMi41IDIuNSAwIDAgMS0yLjIgMGMtLjMtLjEtLjYtLjMtLjgtLjZsLS41LTFjLS4yLS40LS4yLS44LS4zLTEuM3YtLjVjMC0uNS4yLTEgLjMtMS4zbC41LTFhMi4yIDIuMiAwIDAgMSAxLjgtMWMuNSAwIC44LjIgMS4yLjNsLjguNy41IDEgLjEgMS41Wm0tMS40IDAtLjEtLjljMC0uMy0uMi0uNS0uMy0uN2ExLjMgMS4zIDAgMCAwLTEuMi0uNmwtLjYuMWMtLjIgMC0uNC4yLS41LjRsLS4zLjUtLjEuN3YxLjJjMCAuMyAwIC41LjIuOGwuNS42LjkuMmMuMiAwIC40IDAgLjYtLjJsLjUtLjUuMy0uN3YtLjhabTYtMy4zdjFoLTMuOHYtMWgzLjdabS0yLjctMS42aDEuNFYzNWwuMS41LjMuMmguNGEyLjMgMi4zIDAgMCAwIC41IDB2MS4xYTMuNyAzLjcgMCAwIDEtMSAuMmwtLjktLjJjLS4zLS4xLS41LS4zLS42LS42LS4yLS4zLS4yLS43LS4yLTEuMXYtNi41Wm01LjUgMS42djYuNkgxNTV2LTYuNmgxLjRabS0xLjUtMS43YzAtLjMgMC0uNS4yLS42LjEtLjIuMy0uMi42LS4ycy41IDAgLjYuMmMuMi4xLjMuMy4zLjYgMCAuMi0uMS40LS4zLjUtLjEuMi0uMy4yLS42LjJzLS41IDAtLjYtLjJhLjcuNyAwIDAgMS0uMi0uNVptMyA1YzAtLjUuMS0xIC4zLTEuNC4xLS40LjMtLjguNi0xIC4zLS40LjYtLjYgMS0uOGwxLjMtLjJjLjQgMCAuOSAwIDEuMy4ybDEgLjcuNSAxLjFjLjIuNC4yLjkuMiAxLjN2LjJjMCAuNSAwIDEtLjIgMS4zLS4xLjQtLjMuOC0uNiAxYTIuOCAyLjggMCAwIDEtMi4yIDFjLS41IDAtMSAwLTEuMy0uMmwtMS0uN2MtLjMtLjMtLjUtLjctLjYtMS0uMi0uNS0uMi0xLS4yLTEuNFptMS42IDB2MWwuMy42LjUuNS44LjJhMS40IDEuNCAwIDAgMCAxLjItLjdsLjMtLjd2LTEuOGMwLS4zLS4yLS41LS4zLS43YTEuNCAxLjQgMCAwIDAtMS4yLS42bC0uOC4xLS41LjUtLjMuN3YuOFptNy41LTEuOXY1LjJoLTEuNHYtNi42aDEuNHYxLjRabS0uMiAxLjdoLS41YzAtLjUgMC0xIC4yLTEuM2wuNS0xYTIuNCAyLjQgMCAwIDEgMi0xbC44LjIuNy40LjQuOC4yIDEuMXY0LjNoLTEuNXYtNC4zYzAtLjMgMC0uNS0uMi0uNyAwLS4yLS4yLS4zLS40LS40bC0uNi0uMWExLjQgMS40IDAgMCAwLTEuMi42bC0uMy42LS4xLjhaIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNCIgZD0iTTYxLjIgNTB2LjhoLTMuNFY1MGgzLjRabS0zLTUuOXY2LjdINTd2LTYuN0g1OFptNi43IDUuN1Y0N2wtLjQtLjNhMSAxIDAgMCAwLS40LS4xaC0uNWwtLjMuMy0uMS40SDYybC4xLS42LjQtLjUuNy0uMy44LS4xYy40IDAgLjcgMCAxIC4yLjMgMCAuNS4zLjcuNS4yLjMuMi42LjIgMVY1MC4zbC4yLjVoLTEuMWwtLjEtLjR2LS42Wm0uMi0ydi43aC0xLjNsLS40LjJhLjcuNyAwIDAgMC0uMy42di40bC4zLjNoLjVhMS4yIDEuMiAwIDAgMCAxLS40bC4xLS40LjQuNS0uMi40LS40LjRhMS43IDEuNyAwIDAgMS0xLjIuNGMtLjMgMC0uNiAwLS44LS4yLS4zLS4xLS41LS4zLS42LS41LS4yLS4yLS4yLS41LS4yLS44bC4xLS43LjQtLjUuOC0uM0g2NVptNSAxLjd2LS4zbC0uMy0uM2EzIDMgMCAwIDAtLjctLjJjLS4zIDAtLjUgMC0uNy0uMmwtLjYtLjNjLS4xIDAtLjMtLjItLjMtLjRsLS4yLS41YzAtLjIgMC0uNC4yLS42IDAtLjIuMi0uMy4zLS41bC43LS4zLjgtLjFjLjQgMCAuNyAwIDEgLjIuMy4xLjYuMy43LjUuMi4zLjMuNS4zLjhINzBWNDdsLS40LS4zYTEgMSAwIDAgMC0uNC0uMSAxIDEgMCAwIDAtLjUgMGwtLjMuM2EuNi42IDAgMCAwIDAgLjVsLjEuMi4zLjIuNS4xYy40IDAgLjcuMiAxIC4zbC43LjUuMi43YTEuMyAxLjMgMCAwIDEtLjYgMS4xIDIgMiAwIDAgMS0uNi4zbC0uOC4xYy0uNSAwLS45IDAtMS4yLS4ybC0uNy0uNmMtLjItLjMtLjItLjYtLjItLjhoMWwuMi41LjQuMi41LjFoLjVsLjMtLjN2LS4zWm00LjYtMy42di44SDcydi0uOGgyLjhabS0yLTEuM2gxLjF2NC45bC4xLjMuMi4yaC4zYTEuNyAxLjcgMCAwIDAgLjQgMHYuOGEyLjggMi44IDAgMCAxLS44LjFsLS42LS4xYTEgMSAwIDAgMS0uNS0uNWwtLjItLjh2LTQuOVptOC42IDVWNDZoMS4xdjVoLTF2LTEuMlptLjItMWguM3YxbC0uNC43LS42LjVhMiAyIDAgMCAxLS45LjEgMiAyIDAgMCAxLS42IDBsLS42LS40LS4zLS42LS4xLS44di0zLjJoMS4xdjMuNmwuMi4zLjMuMmguM2MuMyAwIC42IDAgLjgtLjIuMSAwIC4zLS4yLjMtLjVsLjItLjdabTMuNC0xLjh2NmgtMS4xdi03aDF2MVptMy4yIDEuNXYuMWwtLjEgMS0uNC44LS42LjVjLS4yLjItLjUuMi0uOC4ybC0uOC0uMS0uNi0uNS0uNC0uOC0uMi0xdi0uM2wuMi0xYzAtLjMuMi0uNS40LS44YTEuNiAxLjYgMCAwIDEgMS40LS42Yy4zIDAgLjYgMCAuOC4yLjIgMCAuNC4yLjYuNWwuNC44LjEgMVptLTEuMS4xdi0uN2wtLjMtLjZhMSAxIDAgMCAwLS44LS40aC0uNWExIDEgMCAwIDAtLjQuM2wtLjIuNC0uMS41djFjMCAuMiAwIC40LjIuNSAwIC4yLjIuNC4zLjVsLjcuMWguNWwuMy0uNC4yLS42LjEtLjZabTUuMyAxLjR2LTZoMS4xdjdoLTFsLS4xLTFaTTg5IDQ4LjR2LS4xYzAtLjQgMC0uNy4yLTEgMC0uMy4yLS42LjQtLjhhMS43IDEuNyAwIDAgMSAxLjQtLjdsLjguMS42LjUuNC44LjIgMXYuM2wtLjIgMS0uNC44YTEuNiAxLjYgMCAwIDEtMS40LjYgMS43IDEuNyAwIDAgMS0xLjQtLjdsLS40LS44LS4yLTFabTEuMi0uMXYuN2wuMi42LjQuMy41LjEuNi0uMS40LS41LjItLjZWNDhsLS4xLS42LS4zLS40YTEgMSAwIDAgMC0uMy0uMmwtLjUtLjFhMSAxIDAgMCAwLS41IDAgMSAxIDAgMCAwLS40LjRsLS4yLjZ2LjZabTcuNCAxLjV2LTIuM2wtLjEtLjUtLjMtLjNhMSAxIDAgMCAwLS41LS4xaC0uNWwtLjMuMy0uMS40aC0xLjFsLjEtLjYuNC0uNS43LS4zLjgtLjFjLjQgMCAuNyAwIDEgLjIuMyAwIC41LjMuNy41LjIuMy4zLjYuMyAxVjUwLjNsLjIuNWgtMS4ybC0uMS0uNHYtLjZabS4xLTJ2LjdoLTEuM2wtLjQuMmEuNy43IDAgMCAwLS4zLjZ2LjRsLjMuM2guNWExLjIgMS4yIDAgMCAwIDEtLjRsLjEtLjQuNC41LS4yLjQtLjQuNGExLjcgMS43IDAgMCAxLTEuMS40Yy0uNCAwLS43IDAtMS0uMmwtLjUtLjVjLS4yLS4yLS4yLS41LS4yLS44bC4xLS43LjUtLjUuNy0uM2gxLjhabTQuNi0ydi45aC0yLjh2LS44aDIuOFptLTItMS4yaDEuMXY1LjJsLjMuMmguM2ExLjcgMS43IDAgMCAwIC4zIDB2LjhhMi44IDIuOCAwIDAgMS0uNy4xbC0uNy0uMWExIDEgMCAwIDEtLjQtLjVsLS4yLS44di00LjlabTUuMiA2LjNjLS4zIDAtLjcgMC0xLS4yYTIuMiAyLjIgMCAwIDEtMS4yLTEuMmwtLjEtMXYtLjJsLjEtMSAuNS0uOC43LS42IDEtLjEuOC4xLjcuNi40LjcuMSAxdi41aC0zLjh2LS44aDIuN3YtLjdhMSAxIDAgMCAwLS40LS40IDEgMSAwIDAgMC0uNi0uMiAxIDEgMCAwIDAtLjguNWwtLjMuNXYxLjVsLjMuNS40LjMuNi4xYTEuNSAxLjUgMCAwIDAgMS4zLS42bC42LjYtLjUuNGEyIDIgMCAwIDEtLjYuNGwtLjkuMVptOC42LTYuOHY2LjdIMTEzdi01LjRsLTEuNi42di0xbDIuNi0uOWguMVptNS42IDUuN3YtNmgxLjF2N2gtMXYtMVptLTMuMi0xLjR2LS4xbC4xLTEgLjQtLjhjLjItLjMuNC0uNC42LS41LjMtLjIuNS0uMi44LS4ybC44LjEuNi41LjQuOC4yIDF2LjNsLS4yIDEtLjQuOGExLjYgMS42IDAgMCAxLTEuNC42IDEuNyAxLjcgMCAwIDEtMS40LS43bC0uNC0uOC0uMS0xWm0xLS4xdi4xbC4yLjZjMCAuMiAwIC40LjIuNmwuMy4zLjUuMS42LS4xLjQtLjUuMi0uNlY0OGwtLjEtLjYtLjItLjRhMSAxIDAgMCAwLS40LS4ybC0uNS0uMWExIDEgMCAwIDAtLjUgMCAxIDEgMCAwIDAtLjMuNGwtLjIuNi0uMS42Wm0xMCAxLjVWNDdsLS40LS4zYTEgMSAwIDAgMC0uNC0uMWgtLjVsLS4zLjMtLjEuNGgtMS4xbC4xLS42LjQtLjUuNy0uMy44LS4xYy40IDAgLjcgMCAxIC4yLjMgMCAuNS4zLjcuNS4yLjMuMy42LjMgMVY1MC4zbC4xLjVoLTEuMWwtLjEtLjR2LS42Wm0uMi0ydi43aC0xLjNsLS40LjJhLjcuNyAwIDAgMC0uMy42di40bC4zLjNoLjVhMS4yIDEuMiAwIDAgMCAxLS40bC4xLS40LjQuNS0uMi40LS40LjRhMS43IDEuNyAwIDAgMS0xLjIuNGMtLjMgMC0uNiAwLS44LS4yLS4zLS4xLS41LS4zLS42LS41LS4yLS4yLS4yLS41LS4yLS44bC4xLS43LjQtLjUuOC0uM2gxLjhabTUuNS0yaDF2NC45YzAgLjQtLjEuOC0uMyAxLjEtLjIuMy0uNC42LS44LjdhMi43IDIuNyAwIDAgMS0xLjguMmMtLjIgMC0uNS0uMS0uNy0uMy0uMiAwLS40LS4yLS41LS40bC41LS43LjYuNWExLjYgMS42IDAgMCAwIDEuMyAwbC40LS40LjItLjdWNDZabS0zLjQgMi42di0uMWwuMS0xIC40LS44Yy4yLS4zLjQtLjQuNy0uNS4yLS4yLjUtLjIuOC0uMmwuOC4xLjYuNS40LjguMiAxdi4zbC0uMiAxLS40LjhhMS42IDEuNiAwIDAgMS0xLjQuNiAxLjggMS44IDAgMCAxLTEuNS0uN2wtLjQtLjh2LTFabTEuMS0uMXYuN2wuMy42LjQuMy41LjEuNi0uMS40LS41LjItLjZWNDhsLS4xLS42LS4zLS40YTEgMSAwIDAgMC0uMy0uMmwtLjUtLjFhMSAxIDAgMCAwLS45LjRsLS4yLjZ2LjZabTQuNC4xdi0uMWMwLS40IDAtLjcuMi0xIDAtLjMuMi0uNi40LS44bC44LS41Yy4zLS4yLjYtLjIgMS0uMi4zIDAgLjYgMCAxIC4yLjIgMCAuNC4zLjYuNWwuNS44LjIgMXYuMWMwIC40IDAgLjctLjIgMWwtLjUuOGEyLjEgMi4xIDAgMCAxLTEuNy43Yy0uMyAwLS42IDAtMS0uMi0uMiAwLS41LS4zLS43LS41bC0uNC0uOGEzIDMgMCAwIDEtLjItMVptMS4xLS4xdi4xbC4xLjYuMi42LjQuMy42LjFhMSAxIDAgMCAwIC45LS40bC4yLS42VjQ3LjdsLS4yLS42YTEgMSAwIDAgMC0xLS41IDEgMSAwIDAgMC0uNS4yIDEgMSAwIDAgMC0uNC4zbC0uMi42di42WiIvPjxwYXRoIGZpbGw9IiMzRkE3MUEiIGQ9Ik02NC4xIDgxdjM3LjJINThWODguM2wtOSAzLjF2LTVMNjMuMyA4MWguN1ptMjYuNy0xLjh2MzloLTYuMXYtMzloNi4xWk0xMDggOTAuN2wtMTIgMTMuMi02LjUgNi44LTEuNi01LjIgNS02LjEgNy43LTguN2g3LjRabS02IDI3LjUtOS0xMyAzLjktNC4yIDEyLjEgMTcuMmgtN1ptMTguOC04LjMgNy40LTI4LjdoMy45bC0uNSA3LjQtNy44IDI5LjZoLTRsMS04LjNabS01LjUtMjguNyA2IDI4LjQuNSA4LjZoLTQuMmwtOC42LTM3aDYuM1ptMjUuMSAyOC40IDYtMjguNGg2LjNsLTguNiAzN2gtNC4zbC42LTguNlptLTYuOC0yOC40IDcuMyAyOC43IDEgOC4zaC00bC03LjctMjkuNi0uNC03LjRoMy44WiIvPjwvc3ZnPg==", + "description": "Displays the latest power consumption telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'powerConsumption', label: 'Power consumption', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Power consumption\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"square\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"bolt\",\"iconColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":5,\"color\":\"#3FA71A\"},{\"from\":5,\"to\":15,\"color\":\"#F36900\"},{\"from\":15,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"52px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":5,\"color\":\"#3FA71A\"},{\"from\":5,\"to\":15,\"color\":\"#F36900\"},{\"from\":15,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":10,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Power consumption card\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"kW\",\"decimals\":0,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "power", + "energy", + "energy usage", + "electric load", + "electricity", + "power efficiency", + "load profile" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/power_consumption_card_with_background.json b/application/src/main/data/json/system/widget_types/power_consumption_card_with_background.json new file mode 100644 index 0000000000..4c20568ce2 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/power_consumption_card_with_background.json @@ -0,0 +1,32 @@ +{ + "fqn": "power_consumption_card_with_background", + "name": "Power consumption card with background", + "deprecated": false, + "image": "tb-image:cG93ZXJfY29uc3VtcHRpb25fY2FyZF93aXRoX2JhY2tncm91bmQuc3Zn:cG93ZXJfY29uc3VtcHRpb25fY2FyZF93aXRoX2JhY2tncm91bmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48L2c+PHBhdGggZmlsbD0iIzNCOTExQyIgZD0iTTI1LjggNDEuM2gtMS4zbDEuMy04LjhoLTQuNGMtLjcgMC0uNy0uNC0uNS0uOC4yLS40IDAtLjEgMC0uMmw3LjQtMTIuOGgxLjJsLTEuMiA4LjhoNC4zYy42IDAgLjcuNC42LjZ2LjJsLTcuNCAxM1oiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNNTIuOCAyNC44aC0yLjZ2LTEuNGgyLjZjLjQgMCAuOCAwIDEtLjIuNC0uMS42LS4zLjctLjZsLjItLjljMC0uMyAwLS42LS4yLS45LS4xLS4yLS4zLS41LS42LS42YTIgMiAwIDAgMC0xLjEtLjNoLTJ2OC42SDQ5di0xMGgzLjhjLjggMCAxLjQuMiAyIC41LjUuMiAxIC42IDEuMiAxIC4zLjYuNCAxIC40IDEuNyAwIC42LS4xIDEuMi0uNCAxLjctLjMuNC0uNy44LTEuMiAxLS42LjMtMS4yLjQtMiAuNFptNC44IDBjMC0uNi4xLTEuMS4zLTEuNmwuNi0xLjJhMyAzIDAgMCAxIDEuMS0uN2MuNC0uMiAxLS4zIDEuNS0uM3MxIDAgMS40LjNjLjQuMS44LjQgMS4xLjdsLjcgMS4yLjIgMS41di4yYzAgLjUgMCAxLS4yIDEuNWwtLjcgMS4yYTMuMSAzLjEgMCAwIDEtMi41IDFjLS42IDAtMSAwLTEuNS0uMmEzLjYgMy42IDAgMCAxLTEuOC0ybC0uMi0xLjVabTEuNyAwdjFsLjQuOGMuMS4yLjMuNC42LjUuMi4yLjUuMi44LjJhMS42IDEuNiAwIDAgMCAxLjMtLjdjLjItLjIuMy0uNS4zLS44bC4yLTFjMC0uNCAwLS43LS4yLTEgMC0uMy0uMS0uNi0uMy0uOGExLjYgMS42IDAgMCAwLTEuMy0uN2MtLjMgMC0uNiAwLS44LjItLjMuMS0uNS4zLS42LjUtLjEuMi0uMy41LS4zLjhsLS4xIDFabTguOCAyIDEuOC01LjdoMWwtLjMgMS43LTEuNyA1LjdoLTFsLjItMS42Wm0tMS01LjcgMS40IDUuOHYxLjZoLTFsLTItNy40aDEuNlptNS40IDUuNyAxLjMtNS43aDEuNmwtMiA3LjRoLTFsLjEtMS43Wm0tMS40LTUuNyAxLjcgNS43LjIgMS43aC0xbC0xLjctNS43LS4zLTEuN2gxWm05IDcuNWMtLjYgMC0xLjEgMC0xLjUtLjJhMy4zIDMuMyAwIDAgMS0xLjktMiA0IDQgMCAwIDEtLjItMS4zdi0uM2MwLS42IDAtMS4xLjItMS42cy40LS45LjctMS4yYTMgMyAwIDAgMSAxLjEtLjhsMS4zLS4yYy42IDAgMSAwIDEuNC4yYTMuMSAzLjEgMCAwIDEgMS42IDEuOWwuMiAxLjV2LjdoLTUuN1YyNGg0VjI0bC0uMS0uOS0uNS0uNi0uOS0uMmMtLjIgMC0uNSAwLS43LjItLjIgMC0uNC4yLS41LjVsLS40LjdWMjZsLjUuNy42LjUuOC4xYTIuMyAyLjMgMCAwIDAgMS45LS45bC45LjktLjcuNmMtLjIuMi0uNS40LS45LjUtLjQuMi0uOC4yLTEuMy4yWm02LTZ2NS45aC0xLjZ2LTcuNGgxLjZ2MS40Wm0yLjQtMS41djEuNWEzLjIgMy4yIDAgMCAwLTEuNSAwIDEuNCAxLjQgMCAwIDAtLjggMWwtLjIuNmgtLjRjMC0uNCAwLS44LjItMS4yIDAtLjQuMi0uOC40LTEgLjItLjQuNC0uNi43LS44YTEuOSAxLjkgMCAwIDEgMS4zLS4yaC4zWm04IDYuMi43LS4xLjUtLjUuMy0uNmgxLjVjMCAuNC0uMS45LS40IDEuM2EzIDMgMCAwIDEtMSAuOWMtLjUuMi0xIC4zLTEuNi4zLS42IDAtMSAwLTEuNS0uM2EzIDMgMCAwIDEtMS0uOGMtLjMtLjMtLjUtLjctLjYtMS4xLS4yLS41LS4yLTEtLjItMS41di0uMmMwLS41IDAtMSAuMi0xLjQuMS0uNS4zLS45LjYtMS4yLjMtLjQuNi0uNiAxLS44bDEuNS0uM2MuNiAwIDEuMSAwIDEuNi4zLjQuMy44LjYgMSAxIC4zLjQuNC45LjQgMS40SDk4bC0uMi0uNy0uNS0uNWMtLjMtLjItLjUtLjItLjgtLjItLjMgMC0uNiAwLS44LjItLjMuMS0uNC4zLS42LjVsLS4yLjgtLjEuOXYxLjJsLjMuN2MuMi4yLjMuNC42LjUuMi4yLjUuMi44LjJabTQuMi0yLjR2LS4ybC4zLTEuNS42LTEuMmEzIDMgMCAwIDEgMS4xLS43Yy40LS4yIDEtLjMgMS41LS4zczEgMCAxLjQuM2MuNC4xLjguNCAxIC43LjQuNC42LjguOCAxLjJsLjIgMS41di4yYzAgLjUgMCAxLS4yIDEuNWwtLjcgMS4yYTMuMSAzLjEgMCAwIDEtMi41IDFjLS42IDAtMSAwLTEuNS0uMmEzLjYgMy42IDAgMCAxLTEuOC0ybC0uMi0xLjVabTEuNy0uMlYyNmwuNC43Yy4xLjIuMy40LjYuNS4yLjIuNC4yLjguMmExLjYgMS42IDAgMCAwIDEuMy0uN2MuMi0uMi4zLS41LjMtLjhsLjEtMXYtMWwtLjQtLjhhMS42IDEuNiAwIDAgMC0xLjMtLjdjLS40IDAtLjYgMC0uOS4ybC0uNS41Yy0uMi4yLS4zLjUtLjMuOGwtLjEgMVptOC41LTJ2NS44aC0xLjd2LTcuNGgxLjZ2MS42Wm0tLjMgMS44aC0uNmMwLS41LjEtMSAuMy0xLjQuMS0uNS4zLS44LjYtMS4xYTIuNiAyLjYgMCAwIDEgMi0xbDEgLjEuOC41LjUuOC4xIDEuM3Y0LjhoLTEuNnYtNC44YzAtLjMgMC0uNi0uMi0uOGEuOS45IDAgMCAwLS40LS41bC0uNy0uMWExLjYgMS42IDAgMCAwLTEuNC43bC0uMy43LS4xLjhabTEwLjggMi0uMS0uNGExIDEgMCAwIDAtLjUtLjRsLTEtLjMtMS0uM2MtLjQtLjEtLjctLjMtLjktLjUtLjItLjEtLjQtLjMtLjUtLjYtLjItLjItLjItLjUtLjItLjggMC0uMyAwLS42LjItLjlsLjUtLjcgMS0uNSAxLjEtLjFjLjYgMCAxLjIgMCAxLjYuMy41LjIuOC41IDEgLjguMy40LjQuOC40IDEuMmgtMS43bC0uMS0uNWExIDEgMCAwIDAtLjQtLjVsLS44LS4xLS43LjFhMSAxIDAgMCAwLS40LjQuOS45IDAgMCAwIDAgLjhsLjIuMi41LjIuNy4yIDEuNS41Yy40LjIuNy40LjkuNy4yLjMuMy43LjMgMS4xYTIgMiAwIDAgMS0uOCAxLjYgMyAzIDAgMCAxLTEgLjVsLTEuMi4xYy0uNiAwLTEuMiAwLTEuNy0uMy0uNC0uMy0uOC0uNi0xLTEtLjMtLjMtLjQtLjctLjQtMS4xaDEuNmMwIC4zLjEuNS4zLjdsLjUuNC44LjEuNy0uMWMuMiAwIC40LS4yLjUtLjNsLjEtLjVabTcuOC4zVjIxaDEuNnY3LjRoLTEuNWwtLjEtMS43Wm0uMi0xLjZoLjZjMCAuNSAwIDEtLjIgMS40bC0uNSAxYy0uMi40LS41LjYtLjkuOGEzIDMgMCAwIDEtMS4zLjIgMyAzIDAgMCAxLTEtLjEgMiAyIDAgMCAxLS43LS41Yy0uMi0uMi0uNC0uNS0uNS0uOWE0IDQgMCAwIDEtLjItMS4yVjIxaDEuNnY0LjhsLjEuNy4zLjQuNC4yLjQuMWMuNSAwIC45IDAgMS4xLS4zLjMtLjIuNS0uNC42LS43bC4yLTFabTUtMi42djUuOWgtMS42di03LjRoMS41bC4xIDEuNVptLS4zIDJoLS41YzAtLjYgMC0xIC4yLTEuNWwuNS0xLjEgMS0uOGEzIDMgMCAwIDEgMS4yLS4ybDEgLjEuNy41LjUuOC4xIDEuMnY0LjloLTEuNnYtNC44YzAtLjMgMC0uNi0uMi0uOGEuOS45IDAgMCAwLS40LS41bC0uNy0uMWMtLjMgMC0uNiAwLS44LjItLjIgMC0uNC4zLS42LjVsLS4zLjd2LjhabTQuNi0uNS0uNy4yLjEtMS4zLjYtMWEyLjYgMi42IDAgMCAxIDIuMS0xbDEgLjFjLjQuMS42LjMuOC41LjIuMi40LjUuNS45bC4yIDEuMnY0LjhoLTEuN3YtNC44YzAtLjQgMC0uNy0uMi0uOWEuOS45IDAgMCAwLS40LS40bC0uNy0uMS0uNy4xLS41LjQtLjMuNnYuN1ptOC4yLTEuNnY4LjhoLTEuN1YyMS4xaDEuNWwuMiAxLjRabTQuOCAyLjJ2LjJjMCAuNSAwIDEtLjIgMS41bC0uNiAxLjJjLS4yLjMtLjUuNi0uOS44bC0xLjIuMmMtLjQgMC0uOCAwLTEuMi0uMmwtLjktLjdhNCA0IDAgMCAxLS41LTEuMmMtLjItLjQtLjMtLjktLjMtMS40di0uNWMwLS42LjEtMSAuMy0xLjVsLjUtMS4xYTIuNSAyLjUgMCAwIDEgMi4xLTFjLjUgMCAuOSAwIDEuMi4ybDEgLjguNSAxLjIuMiAxLjVabS0xLjcuMnYtMS4ybC0uNC0uN2MwLS4yLS4zLS40LS41LS41bC0uNy0uMi0uOC4xLS41LjRjLS4yLjItLjMuNC0uMy43bC0uMi43djEuM2MwIC40LjEuNy4zIDFsLjUuNiAxIC4yYy4zIDAgLjUgMCAuNy0uMi4yIDAgLjQtLjMuNS0uNS4yLS4yLjMtLjUuMy0uOGwuMS0xWm02LjctMy44djEuMmgtNC4xdi0xLjJoNC4xWm0tMy0xLjhoMS43djcuMmwuMS41LjMuMmguNGEyLjUgMi41IDAgMCAwIC42IDB2MS4zYTQuMiA0LjIgMCAwIDEtMS4xLjFjLS40IDAtLjcgMC0xLS4yLS4zLS4xLS41LS4zLS43LS42LS4yLS4zLS4yLS43LS4yLTEuMnYtNy4zWk0xNjAgMjF2Ny40aC0xLjZ2LTcuNGgxLjZabS0xLjgtMiAuMy0uNi43LS4yYy4zIDAgLjUgMCAuNy4yLjIuMi4yLjQuMi43IDAgLjIgMCAuNC0uMi42YTEgMSAwIDAgMS0uNy4yIDEgMSAwIDAgMS0uNy0uMi44LjggMCAwIDEtLjMtLjZabTMuNiA1Ljh2LS4yYzAtLjUgMC0xIC4yLTEuNS4xLS40LjQtLjguNy0xLjJhMyAzIDAgMCAxIDEtLjdsMS41LS4zYy41IDAgMSAwIDEuNC4zLjUuMS44LjQgMS4xLjdsLjcgMS4yYy4yLjUuMiAxIC4yIDEuNXYuMmMwIC41IDAgMS0uMiAxLjVsLS43IDEuMmEzLjIgMy4yIDAgMCAxLTIuNSAxYy0uNSAwLTEgMC0xLjUtLjJsLTEtLjhjLS4zLS40LS42LS44LS43LTEuMi0uMi0uNS0uMi0xLS4yLTEuNVptMS42LS4ydi4ybC4xIDEgLjMuNy42LjVjLjIuMi41LjIuOC4yYTEuNiAxLjYgMCAwIDAgMS40LS43bC4zLS44di0yYzAtLjMtLjItLjYtLjQtLjhhMS42IDEuNiAwIDAgMC0xLjMtLjdjLS4zIDAtLjYgMC0uOC4ybC0uNi41LS4zLjgtLjEgMVptOC41LTJ2NS44aC0xLjd2LTcuNGgxLjZsLjEgMS42Wm0tLjMgMS44aC0uNWMwLS41IDAtMSAuMi0xLjQuMS0uNS4zLS44LjYtMS4xYTIuNiAyLjYgMCAwIDEgMi4xLTFsMSAuMS43LjUuNS44LjIgMS4zdjQuOGgtMS43di00LjhsLS4xLS44YS45LjkgMCAwIDAtLjUtLjVsLS43LS4xYTEuNiAxLjYgMCAwIDAtMS4zLjdsLS40LjctLjEuOFoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii40IiBkPSJNNTIuOSA0MS42di45aC0zLjR2LTFINTNabS0zLTUuOHY2LjdoLTEuMnYtNi43aDEuMVptNi44IDUuN3YtMi40bC0uMS0uNC0uMy0uM2ExIDEgMCAwIDAtLjUtLjFoLS41bC0uMy4zLS4xLjRoLTEuMWwuMS0uNi40LS41Yy4yLS4xLjQtLjMuNy0uM2wuOC0uMiAxIC4yYy4zLjEuNi4zLjcuNi4yLjIuMy42LjMgMVY0MmwuMi40di4xaC0xLjJsLS4xLS41di0uNVptLjEtMnYuN2gtMS4zbC0uNC4yYS43LjcgMCAwIDAtLjMuNnYuNGwuMy4yLjUuMWExLjIgMS4yIDAgMCAwIDEtLjVsLjEtLjQuNC41LS4yLjRjMCAuMi0uMi4zLS4zLjVhMS43IDEuNyAwIDAgMS0xLjIuNGMtLjQgMC0uNyAwLTEtLjJsLS41LS41Yy0uMi0uMy0uMi0uNS0uMi0uOGwuMS0uNy41LS41LjctLjMgMS0uMWguOFptNS4xIDEuN3YtLjNsLS40LS4zYTMgMyAwIDAgMC0uNy0uMmwtLjctLjItLjYtLjMtLjMtLjQtLjEtLjZ2LS41bC41LS41Yy4xLS4yLjMtLjMuNi0uM2wuOC0uMiAxIC4yLjcuNmMuMi4yLjMuNS4zLjhoLTEuMWwtLjEtLjQtLjMtLjJhMSAxIDAgMCAwLS41LS4xIDEgMSAwIDAgMC0uNSAwbC0uMy4zYS42LjYgMCAwIDAgMCAuNWwuMS4yLjQuMS41LjIgMSAuMy42LjUuMi43YTEuMyAxLjMgMCAwIDEtLjUgMSAyIDIgMCAwIDEtLjcuNEg2MWwtMS4yLS4xLS43LS43LS4yLS44aDFsLjIuNWMuMS4yLjMuMi40LjNoMWwuMy0uMi4xLS4zWm00LjUtMy43di44aC0yLjh2LS44aDIuOFptLTItMS4yaDEuMnY1LjJsLjIuMWguM2ExLjcgMS43IDAgMCAwIC40IDB2LjlhMi44IDIuOCAwIDAgMS0uNyAwSDY1YTEgMSAwIDAgMS0uNS0uNWwtLjEtLjh2LTQuOVptOC42IDV2LTMuOGgxLjF2NWgtMXYtMS4yWm0uMi0xaC40bC0uMSAxLS40LjZjLS4xLjItLjMuNC0uNi41YTIgMiAwIDAgMS0uOC4yIDIgMiAwIDAgMS0uNy0uMWMtLjIgMC0uNC0uMi0uNS0uM2wtLjQtLjZWMzcuNWgxdjMuN2wuMy4zLjIuMS4zLjFjLjMgMCAuNiAwIC44LS4ybC40LS41di0uN1ptMy40LTEuOHY2aC0xLjF2LTdoMXYxWm0zLjIgMS41LS4xIDFjMCAuNC0uMi42LS40LjktLjEuMi0uMy40LS42LjVsLS44LjJjLS4zIDAtLjYgMC0uOC0uMi0uMiAwLS40LS4zLS42LS41bC0uNC0uNy0uMi0xVjQwbC4yLTEgLjQtLjhhMS43IDEuNyAwIDAgMSAxLjQtLjdjLjMgMCAuNiAwIC44LjIuMy4xLjUuMy42LjVsLjQuOC4xIDFabS0xIDAtLjEtLjctLjItLjVhMSAxIDAgMCAwLTEtLjVsLS40LjFhMSAxIDAgMCAwLS40LjNsLS4yLjQtLjEuNXYuOWwuMi42YzAgLjIuMi4zLjQuNC4xLjIuMy4yLjYuMmwuNS0uMS40LS40LjItLjV2LS42Wm01LjIgMS41di02aDEuMXY3aC0xdi0xWk04MC44IDQwVjQwbC4xLTFjMC0uNC4yLS42LjQtLjlhMS43IDEuNyAwIDAgMSAxLjQtLjdjLjMgMCAuNiAwIC44LjJsLjYuNS40LjguMiAxdi4zbC0uMiAxLS40LjdhMS42IDEuNiAwIDAgMS0xLjQuNyAxLjcgMS43IDAgMCAxLTEuNC0uN2wtLjQtLjgtLjEtMVptMS0uMS4xLjcuMi41LjQuNC41LjFjLjIgMCAuNSAwIC42LS4yLjIgMCAuMy0uMi40LS40bC4yLS43di0uOGwtLjEtLjUtLjMtLjRhMSAxIDAgMCAwLS4zLS4zSDgzYTEgMSAwIDAgMC0uNSAwIDEgMSAwIDAgMC0uNC40bC0uMi41di43Wm03LjUgMS41di0yLjRsLS4xLS40YzAtLjItLjItLjMtLjMtLjNhMSAxIDAgMCAwLS41LS4xaC0uNWwtLjMuM3YuNGgtMS4yYzAtLjIgMC0uNC4yLS42IDAtLjIuMi0uNC40LS41bC42LS4zLjktLjIgMSAuMmMuMi4xLjUuMy42LjYuMi4yLjMuNi4zIDFWNDJsLjIuNHYuMWgtMS4yVjQybC0uMS0uNVptLjEtMnYuN0g4OC4xbC0uNC4yYS43LjcgMCAwIDAtLjMuNmwuMS40cy4xLjIuMy4ybC40LjFhMS4yIDEuMiAwIDAgMCAxLS41bC4yLS40LjMuNWMwIC4yIDAgLjMtLjIuNCAwIC4yLS4yLjMtLjMuNWExLjcgMS43IDAgMCAxLTEuMi40Yy0uMyAwLS42IDAtLjktLjJsLS42LS41LS4yLS44LjEtLjcuNS0uNS43LS4zIDEtLjFoLjhabTQuNi0ydi44aC0yLjh2LS44SDk0Wm0tMi0xLjJoMS4xdjQuOGwuMS40LjIuMWguM2ExLjcgMS43IDAgMCAwIC40IDB2LjlhMi44IDIuOCAwIDAgMS0uOCAwaC0uNmExIDEgMCAwIDEtLjUtLjVsLS4yLS44di00LjlabTUuMyA2LjNjLS40IDAtLjcgMC0xLS4yYTIuMiAyLjIgMCAwIDEtMS4zLTEuM2wtLjEtMVY0MGwuMS0xIC41LS45LjctLjUgMS0uMmMuMyAwIC42IDAgLjkuMmwuNi41LjQuOC4yIDF2LjRoLTMuOXYtLjhoMi44YzAtLjIgMC0uNC0uMi0uNmExIDEgMCAwIDAtLjMtLjQgMSAxIDAgMCAwLS42LS4yIDEgMSAwIDAgMC0uOC41bC0uMi41LS4xLjd2LjhsLjMuNS41LjMuNS4xYTEuNSAxLjUgMCAwIDAgMS4zLS42bC42LjYtLjQuNC0uNy40aC0uOFptOC41LTYuOHY2LjdoLTF2LTUuNGwtMS43LjZ2LTFsMi42LTFoLjFabTUuNiA1Ljd2LTZoMS4ydjdoLTFsLS4yLTFabS0zLjItMS40VjQwbC4xLTEgLjQtLjkuNi0uNS45LS4yYy4zIDAgLjUgMCAuOC4ybC41LjUuNC44LjIgMXYuM2MwIC4zIDAgLjctLjIgMSAwIC4zLS4yLjUtLjQuN2ExLjYgMS42IDAgMCAxLTEuMy43IDEuNyAxLjcgMCAwIDEtMS41LS43bC0uNC0uOHYtMVptMS4xLS4xdi43bC4zLjVjMCAuMi4yLjMuMy40bC41LjFjLjMgMCAuNSAwIC43LS4ybC4zLS40LjItLjd2LTEuM2wtLjMtLjRhMSAxIDAgMCAwLS40LS4zaC0uNWExIDEgMCAwIDAtLjUgMCAxIDEgMCAwIDAtLjMuNGwtLjIuNXYuN1ptMTAgMS41di0yLjRsLS4xLS40LS4zLS4zYTEgMSAwIDAgMC0uNS0uMWgtLjVsLS4zLjMtLjEuNGgtMS4xbC4xLS42LjQtLjVjLjItLjEuNC0uMy43LS4zbC44LS4yIDEgLjJjLjMuMS42LjMuNy42LjIuMi4zLjYuMyAxVjQybC4yLjR2LjFoLTEuMmwtLjEtLjV2LS41Wm0uMS0ydi43aC0xLjNsLS40LjJhLjcuNyAwIDAgMC0uMy42di40bC4zLjIuNS4xYTEuMiAxLjIgMCAwIDAgMS0uNWwuMS0uNC40LjUtLjIuNGMwIC4yLS4yLjMtLjMuNWExLjcgMS43IDAgMCAxLTEuMi40Yy0uNCAwLS43IDAtMS0uMmwtLjUtLjUtLjItLjguMS0uNy41LS41LjctLjMgMS0uMWguOFptNS41LTJoMXY0LjljMCAuNCAwIC44LS4zIDEuMS0uMi4zLS40LjYtLjguN2EyLjcgMi43IDAgMCAxLTEuOC4ybC0uNi0uM2MtLjMtLjEtLjQtLjMtLjYtLjVsLjUtLjYuNi41YTEuNiAxLjYgMCAwIDAgMS4zIDBsLjUtLjUuMS0uNnYtMy43bC4xLTEuMlptLTMuNCAyLjZWNDBjMC0uNCAwLS43LjItMSAwLS40LjItLjYuNC0uOS4xLS4yLjMtLjQuNi0uNWwuOC0uMmMuMyAwIC42IDAgLjguMi4zLjEuNS4zLjYuNWwuNC44LjIgMXYuM2wtLjIgMS0uNC43YTEuNiAxLjYgMCAwIDEtMS40LjcgMS44IDEuOCAwIDAgMS0xLjQtLjdsLS40LS44LS4yLTFabTEuMS0uMS4xLjcuMi41LjQuNC41LjFjLjMgMCAuNSAwIC42LS4yLjIgMCAuMy0uMi40LS40bC4yLS43di0uOGwtLjEtLjUtLjItLjRhMSAxIDAgMCAwLS40LS4zaC0uNWExIDEgMCAwIDAtLjkuNGwtLjIuNXYuN1ptNC41IDAgLjEtMSAuNS0uOC43LS42IDEtLjIgMSAuMi43LjYuNC44LjIgMWMwIC40IDAgLjgtLjIgMSAwIC40LS4yLjYtLjQuOWEyLjEgMi4xIDAgMCAxLTEuNy43Yy0uNCAwLS43IDAtMS0uMmwtLjctLjUtLjUtLjhhMyAzIDAgMCAxLS4xLTFabTEgMCAuMS43LjIuNS40LjQuNi4xYTEgMSAwIDAgMCAuOS0uNWwuMi0uNXYtMS40bC0uMi0uNWExIDEgMCAwIDAtMS0uNSAxIDEgMCAwIDAtLjUuMSAxIDEgMCAwIDAtLjQuNGwtLjIuNXYuN1oiLz48cGF0aCBmaWxsPSIjM0I5MTFDIiBkPSJNNjcuNSA4Mi42VjExNGgtNS4yVjg4LjhsLTcuNiAyLjZWODdsMTIuMi00LjVoLjZaTTk1LjEgODF2MzNoLTUuMlY4MUg5NVptMTQuNCA5LjhMOTkuNCAxMDJsLTUuNiA1LjYtMS4zLTQuMyA0LjItNS4yIDYuNi03LjNoNi4yWm0tNS4xIDIzLjItNy42LTExIDMuMy0zLjUgMTAuMyAxNC41aC02Wm0xNi45LTcgNi4yLTI0LjNoMy4zbC0uNCA2LjMtNi43IDI1aC0zLjRsMS03Wm0tNC43LTI0LjMgNSAyNCAuNSA3LjNoLTMuNmwtNy4yLTMxLjNoNS4zWm0yMS4yIDI0IDUtMjRoNS40bC03LjMgMzEuM2gtMy42bC41LTcuM1ptLTUuOC0yNCA2LjIgMjQuMy45IDdoLTMuNGwtNi42LTI1LS4zLTYuM2gzLjJaIi8+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHBhdHRlcm5Db250ZW50VW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48dXNlIHhsaW5rOmhyZWY9IiNjIiB0cmFuc2Zvcm09Im1hdHJpeCguMDA1MDMxNDUgMCAwIC4wMDYyODkzMSAwIDApIi8+PC9wYXR0ZXJuPjxmaWx0ZXIgaWQ9ImIiIHdpZHRoPSIxOTcuNCIgaGVpZ2h0PSIxNTcuNCIgeD0iMS4zIiB5PSIxLjMiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjIuNCIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xNTE2XzcwNTA1Ii8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xNTE2XzcwNTA1IiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PGltYWdlIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3QkRBQUlCQVFFQkFRSUJBUUVDQWdJQ0FnUURBZ0lDQWdVRUJBTUVCZ1VHQmdZRkJnWUdCd2tJQmdjSkJ3WUdDQXNJQ1FvS0Nnb0tCZ2dMREFzS0RBa0tDZ3IvMndCREFRSUNBZ0lDQWdVREF3VUtCd1lIQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnci93Z0FSQ0FDZkFNY0RBUkVBQWhFQkF4RUIvOFFBSFFBQUFRUURBUUVBQUFBQUFBQUFBQUFBQndNRkJnZ0JBZ1FBQ2YvRUFCc0JBQUVGQVFFQUFBQUFBQUFBQUFBQUFBSUFBUU1FQlFZSC85b0FEQU1CQUFJUUF4QUFBQUQ2TSsvY2VtUWFFS1ppaVRja2dqYXJOVDdtdFhta3IzZjZySmsxd0VpRk1tMUpreVNaQm9iYXV5VHQ1MndtOG44bDVMS2NyOGR2SkUycHg2T3lEanpTdFh2SXUxZHpaMkdqYnREdlpwajJhTXZ2UXB1eVJpbTdKbUtSeDZ1K2lmRHRsTjVQNUx5V0U1VzVEYlRKdFhIUW00ekJabFZ5aE9JS2R1QTFaT3BOWWZYcVRXOURMTGNNcnMxa2pXaE1tUTZHMkhXRTJFM2t2Si9KZVNLZkk3V0hIUWx6RzJVT1dZVlJHTEtkZ2RSR1ZibGVHUXlEQ0dSZ1k3bTcrWk5ydGZxbERSMXE2MWRZZFladkpZUzVYWnNGemZ5dXhvU1Rka1NGVlBobUZrQ0lmT1RWdzFJYkc3SWhJb0FqQ1FTek5FNzM0Wm5ZaklsMkNjV1lWU1dxVFM3Sk1PaWZVaFhTY1NFcGNudDZvZVVrdWxoMWhPRGE3ZE9QWFo3T29kSWhGM1dZcERHY1BpMFByeXdHT1hnWlI5M3RIcFo4cW1Uc2E4N0lrT1hiVkxMSW84dHM4N3NzeTg2MVRhSlYwWXd0RWZRQTI1dTEzcWNNbUlhaE1xV0JHcHhDV042aVpGK3pONkFyM1lTZFBFNHV3ckZGbVJ0MmZka1FlYTFmTXQzZnlXanJVbUJiRUxDVEhBOXVydFh5THNJWUZITFF2RzByamJlV1JKUmlNMVdOQkxWQ2xNenNkeTlPcmdtTkNEblJvcEVubnRGUkxaTnFscTZSZHdQSW1GblJCalhNRHlrMmt1Qm1vcG4yYjliT2V6UXpCK0tkMXMweUpNRlc2Y290aW5GS2Y2b1hxait6OXp2TGNTOWxMeERxbHpwY2JvVkc0MVR6Y0ZMcDRwZ0lyR210bStibVhlK2pPeG5vTVVjQ1NXeVIxbW8yeTNhZ1pIWU9TRGFpVnBRREtNVW55N0htV3dyZ2RiT3VwSUV1UWRNM2xDWHlZcEZCaEtFUnlVVXhkMzZONzNPcXVUUzQ3T3FYVUxOdUwwSU10cGd6YkU4MUFBbEdReXNOc3FTMVM1V1hVeTg2RlF5aHVjQklhdERFRDBpazZhUW9JTkhQVWZMNkMvdXh6T0RUTWx4a05Ob0pyUVg0S2xFcXVSekhLekZaY1hJVkl5blVQdEorbG1ZV1RNTGhNRDdiUUFJanRUVVVSTXp1SVA3akMzZW9kTG9MMTZQTlY0YzZlSXpkWWloZGV5ZnJjVW4wTTM1MDVWa3kyR3ZWTkRRYU9lM21kY3k3UmNna1VGaVNGRFUydG9GaWVvREpWWkdvUUFhYTk3Vi9NNDRNcTIxZHE2ZW56VUIxS3Z6ZXJ5ZlN6ZG9oN0ExZUliUU9scFZraWV4MHRtMlBUYzFReU9iNjZjbDBDOGE0UlcwY3pLY1ZBYXVnVzc5S0hSRmNjQStiRUZyNlFIVmZwWS9uR3h1VGE5bGVrNVJ5bWorWU9SYitudWhYR2xLNC93QlM1VGJWeGg3cjUzWm5iVEp1WUxrWi9aZmpPelVBdFdIZUlsM2pvVEJJUnpoRVpQYVlGODhhay8wTXUxWlJORFRXS1FZVk52NkNXOFZyWmZKQ2tmMlcxcWd0bUVGUVd4TUZhclNtS0xXVjBFbGlzZlI3dU1xditZY3hNMFUzUEVxeVlGbzAyYUFFYWV4ZGxWOHdkRzQyMVRsTW9BdWc0Qno5cjZWYitGQlFWTmMyeGRuVHFpYUFvRTA0RXJ4dytyZE4rcG14ZlJ4cks2dWZTR2VvVnRHaEZKNjhjeE5rMjlmeU5XUEt2VWV2V3laOUcvdHpGYitKN1d3L28vbis5SzJEc1RiMTV2czdDK3BlT3hERTJ4Qnp2U0V2cWVYMXk5R09SYU5lc2UxTGNuZmwvVjhud1BFZE9rNTRNYzFyeVRMdnRrbGhHUks2dUNFT0g3dVU2RUVaaEdaNjJWMFl1NHYwZk5PekJHcVdnNlpmUnJkTHdqSGk3a1JtbFp6R1QxNFdaV2hFY1pWeHR1SzZ1REVpa2ZJQTdLTzg2UkcwcG5TVjh3R3NMd09LMFVMTUlLaWhtOFV2UkZNTUxNTzZWZ1liVmFwcWVDZVhOSDAxNVIzSUVtR1NNRkNTVklKV0RnSmNzZ2YveEFBd0VBQUNBZ0lCQXdNRUFRSUdBd0FBQUFBQ0F3RUVBQVVSQmhJVEZDRWlFQ014TWhWQlVRY1dKQ1V6TkNBbVlmL2FBQWdCQVFBQkNBUGpPTTR6ak9NTCswYk80aWxVT1R1TDVDSFh0UWV3aXZOdllWR1ZISTc2WEdjWnhuR2NaeG5HY1p4bkdjWnhuR2NaeG5HY1p4bkgwZ2Y2ejFGUlhhVW1aOFI3TGFNdERXdnhZWUtyR3Y3cXJXVnhHMkhkNDdIR2NaeG5HY2ZUak9NNCt2R2NaeG5HY1p4bkdjY3puR2IvQUZTYlJMZWZ5WFo3TDhWNmwrbEpTbHpkYlRtUVJZVnR1SldrN0NoaVdMdUlQbUo0empPTTR6ak9NNHpqT000empPTTR6ak9NbiswY1p4bTBURDRTcVJMelc1YWFkYXBkUWJZckMzcjNFMDdTWXNXZ2NienZhbHNuZHFwaTdlVnNyc1BzQTRSWUpDZjY4WnhuR2NaeG5HY1p4aEVJZnQ1aG45T000K2tSL1hPTTR6WnJZZmhCVmJWQXladlY2eXlHcEZVaEhnSXpaMUtwY0dOeWphVFc5VzVkRXR1MytYbTB5NVU0QjRrdDNiZXErcmxUUEhZaVJLTzRlTTR6eXFqUEpNL3J3K2M4SFA3aWhRL2pqNmNaeHpuR2NaeG15dlZsV0YxNVM5aUxaMTQxN2xIU0ViR3lzTFdzU3ExSy9rLzFMZU1PZ3EyZGdTczByS2dHWlpTVlpzZytyNnBtc2lhMThObGR0SkMxcmhNU09RYkZWRWUrZHZINDR6MitudG50bkdUL0FHempPTTR6ak4yVmF1K3Zic0xuWXJZYzVWdGRtcVczQkQ3VVNRL3JuR1JlUlh0dFU0WkV4N2cyQ0tUSjVhR3B0SklyQnhVdDJZaHRTTmpZcE1tTm5YZDQwQjRWdUJudGsvam5OVC93a291M09NNCtrUm5IMTR6WjAxM3JsZFRQKzFZYUNkUlRZdXN1NVpUSGRYSEZUK1F3cGdZOTZhdm14aDlJVGN2THZHU0YycW4zSEpzSXNleXJOT3ZjczlqMlVIQlk4OGR2cHlHZFo2c0djcTNQcVRoUjkxS1k4bnc0enVDUHo1Vlp4bkgxNHlmYkxjeSs5NmJEQVYzV3hHdUxtaXFnZEllS3dqaEtncDdzN0JHZmRZVExmbjBFcnQvbEJ4ZjY4Wllxb2ZITFptelZzL1lDMTZvb0N5VkdxWXdKTjZiQURXZ2JTVHBsUG9sY3VrSFdVMWxlSWVZVXNmeHgvd0NNekE1MnlYdVRlQmM4ODdYZzgrNmxFTTFpckE2OHY5Tjh2a1dRTVIrQWo3MmY0ZGNlZmNEQlJJRjNqOTQ4aWdrYkhma0xFMStKbSt0LzVkckx0MVplMU5tUFY3VkZEWVZsbTFOYVZ3QXRxZHZwVjl1Y2ZYakpuMzdSZ1A2emllWXNPdlpzTEMvVnVaaXhzZHNPcmF2dWxaK1RqT01lN3dlUnNkRElTdnp2VkFSSDQ0d28rNE9URWlYY1BYM3o2ZjdvN1M4NHRuYktYRWVVTmJWczJEN0VvdjAwSzdIVytvN01PN2E5WTk0K0ZqT2ZqOCs1WkVjWi93RGNidFZSWVZVVHIwdnVMN2MyaVVVbTJoZ0hzU3NhRGFDb1RZZUFQdTE2L3NmWk0vdGJXUFkzam9nUTlMQlp4bkdNajVEamJOVkJkcnY4UkpBdWtMV0E0Q3JwYm5WQjJ2U0U2cHBkck41SHFXOUxyaGhYMHU5QlBtN29VZFdqd0RRYUxSN2t3UDhBV2NlN3hSSEhwNWI3MmR0TUJ1OWZKVmlpdFdRa04wM3NOMHNqc2FudlRVQjU3TnFMdmhXdU94VWU4YzVhL0I1MGYyd2dZajN5NXZscmJBVW5kWFhydDFkV2dyWHYySWMydHhST2xxSlZpd2FkVlJGYVU3MFpCblJxdlRkUFdZWjAwOVA4NXVFRDNMZ2VSNjN0R05aRElHczhjSTdTUjV3TE93TmhlVmR6N2tIaDdVUStNYnU2WjdDSEhyclZNckVKZHR2OVE5aGkycEtuVFpwOVBiR3p0YjZyV1JObnZ3SEhBL0t4YnFOTTE1MGt3SVRFd1ZuNDl3K09sM1M1dDRQL0FHNGJJTDhoUjc5VHVTV25PWW5icHEwRnBIYzlZT1NIaG5wbEYrNm03V25weUppNjhTYkRQekhXZ1EzVEd1dnp4N1pIOTg1K1JSbjdIT01wVXBqMzNTMHh2UERucGUrMTJOM01NSzBwT2VzR0V6blJQd3NvNHMzR1VGUyt4NXlYSmVSTzJYSFdsaHA5T3ZXaWwzdEhZYStmMUowT2o3VzV0VzBkVERhUDBiUmp1UGYxeWpWTjUxM3A1MWNkL1hIWTJpck5Kc1ZxMjE5R1ZXRkZpMUN0cGIyYXpGWlhhalZWZkljRkdlVCt3bDd6T0NmekxDS0pqTHZ6Nm1FY1crYURNMm5lbmIxM091TE1xN0x4OUxReW5za0lucUEvOXVqaFZsbHp5eGxKemg2eXNWb3FjSHA3SzVpbFFJQmtvb2EyVDdnNnJyQlgzSkVONjQ2clZPeWpkK3NicG5FWFpZZDB2WmxPNFJWSG1iR3BJcTdnMklUYk03Sk9HMFY1NnZWdnNhemQrSHlPOVRFUnppdHB1MmJFYTdBMmx0MXRsTmFiemxrZm0yUFVENm4vQUFzMmJVN2FidG9kM2NVdjFDclY4ckczcnpWdjdQdHFHb3E3UEhkOGtia3BkVEFHVUgvOW1jcVdxNDlWc1NHdU9UMXo0eFQ0aFlSbEcxSHFMSTV1eUV0NkRjdXVTV3ZieDVvYjA0WW5ySmhtaHNKeTZYd3JOWHBRVW0ybUoyanY5M1d4WnZyMmVtVkN1dHNwL2pscGZyOW8yYlUrZFZtNDdabktsM0xZN2hxeHFYYkhyRXJpMWFOeVRYRzBlNExoK1EzV3pKaDVhckduWXBBTmxCZHZZdXIyUXlTQzVkN3ZHRURmN0ZId214UDhxellocnBrYUxzbTg0VEVJcjNIY01kRDJlYll4TXZjaHl2RGtEV21qTVRRZXNkY2N0VVVCV1dLOWV6dGVFNWJjMmJpU1BXdkF0ZTVXVTJyS291U2lvc0VFb0RtUmhnZ3dRNTlUUHQ1bEV2ei9BQ0lKTG15NllJR1Y1KzFucVE5bWp5SHBoR1hUQTJEbUkyTmRuWUFEWlFDSU1hN3dqNVRXWkhvVzlyZHBYR3hNVC9JS1VtSnoxY2xlREljbXExenlkdXE2UHRZVmp4Ni94WkxZcXJJaDFiKytGc3hlM0s5WkdDTzBkRVdlRTk1WnFyQVZsdTFsSHhMZFZER0lrdHZYT3VLY2pZTGVVK0JPeU43WldwM2RTSHpzWHNlV2p3dWpZYXFDbjFzcHNjbVYvd0RrSHo0bTFtMXlocTN5WVRDMitwcmVpbXRNTkNOV2NaSVV6bnZaNHRaaVgwd2NMaXRXS2pwSXNsZFNmMEt5UnJnTWQzTi9laFpYV0R0Yk5kcEZ5TTY4eW4zL0FJczgvOFFBUVJBQUFRTURBUVFIQXdnSkJBTUFBQUFBQVFBQ0VRTVNJVEVFSWtGUkVCTWpNbUZ4Z1VKU2tRVWdNR0toc2NIUkZETkFRMU5VY3BMaEZTUmpnNkt6OFAvYUFBZ0JBUUFKUHdQNTFVQXhnU3JSMVp1NnVjandubnhWRTFLUkFMV3Q3N1J6OFU1cFo5VDltWUxoV0VFOHVLTit6aUtsRVZtNGQ0cDdyS1Bkb2U4SjE4WVZDcHZQdVpwbmltOVc3SGUvWStIUnRUMmIrUVhiaHh4Q2NLUFozTjVhNmVBVkJnWTF4a2s1T2RCK2ErVUdiUVdQZ05mVTMyRWN1WVcxc1BXTXNxTXN5emlxb08vWTlwR2hUckNEQmEvSDdIb2EyWThpdFN6TGdOTTRQa3V5M08xdEc3TW5ndGk2eHRWb2w5UGdmSmJJRGRUdFk5dGZNaklnajFXek5yQnhhR0NsM3ZYN2xTWVhYUUtOUEFwY2dmRlV0eXAzTTVsSFRYNkZ3Q2E1M2tQbjk0MWZ3S2dOdHRzalhtbTNVeTF6OEJja1RUckRMYWxNeEhpVlRyMWl4d2QyWjNqNHh6VlRhS0xXMVFLYkdUZUJQdFQ5eTJTcFdGKzVWWnI1RnMvY21pQjNpMStuZ2dQNjJsR1J6NlhqMFZGM3JoV3QrMVZYSDdFd2ZRTzdUVzArV3FmM0dObXIvaE5OMGt1amtPU3B6SW5QQmNjZ0g3K2h6bWt2Ry9UZEIwQ0ZXdkZRRzVsWFB3VkEwNWE2NXdyWk9tcXBNTm9EdjBpbG1PVWowUkZOaGZhYXJoTjBuZ1B4VlNxUHNCVk9mUEtIMGdZYmJnMXBHWG1PNnFsTnIzTXd4MmZSVTVObG5nUithTTZkSWNONmI0d0U0RWN3cVFOVDJTTlVHUG1NQ2tMa0g3TStlMEpwUVpuM2Z4V3l1Z054VlpOcGJ6STRLbzJwZjNZTWdyWDNlaHVsUS9SUnVYVkdrODBiWHZwQzBlN0NxZnVZZzkxcFAvMnE5MWFqb2JEbm52TGJLbDFIYlhOWTRIZ3FBcmZYcGFqMEtxU1JxM2lQUk1ucytmaXJhakxMU0xkNVBOUGFRN3RHeHVRYzVDcUdtNXRUVmoreitQTlpaaTB4bGFSdkRvZVBpbi9ENStHdllHbDNxaHVzb2k0alhqbnpDR09yQmU3bUU2WXhLTUhtRWJpdmg2b3R6dDd1NjZWd1ZPU05IY1I2cXIxL1o2UE9kZWFKb1RveCtMdlZVQmpReGxUV1o3WFduTnM2Si9WTXAxTE9yYzBrT0oweHdUbml4a2w3ZUdZMUhpbWgyNzNvMVZNZkQ2Qmw5MXRNTTQ2TGZEZGxreDdlVGxPSFdXTnRQTEdpeER5Q3NkUDgvd0RtczgycGx2bXBrak1IVk5CR2hCUWREOW9iVExKM2NxaEc0ZCtudkQvQ0RhalJWRU9CMFhhNzlXVnBZSStoeXdWU0hEN0xrOE5MOWttbDlxbzIwMnNFdWMzUThTQnhWWVZIQ3BsNDQ5SWtzWVhSNktaMnQ1cVZNNlovejh6MUN4WnROSjI5ajJrZlpPRlRnM0NhbFB6VDJ4VHJrNU9aaFZXc3N4dk9pZkxtdGtydlp6WUxXLzNGVmFORzlzaTVwYzdwd09uZWRXSkZOeDd1TWxiUzd2T05RQVJNbkNveGJzTWkzMlRLWnYyNnM5M21vdEphNEJ2RENxWjkwYTlBL2NuN2tON09lbm1xN1duNnhSMHNQL2tFWUJhTStpWlVkYVpjUndDcTFYQjFYTmpyZFZzbldkWHR4RFM0YUNBaVFlVHVJODB3VUp4dkhYMVJ1SFBwYkxuR0doRy82dnNqMDRyMmFlME9IOWliL3VlcUJiOWRNdVA2SDJnOTAzb0dlOTF2SkMwdW9oeEZQQTFUQVBJZEh1ZmdVYzU2SFVhdjhTYWtRaE11d0tiTmZpdC9keUxoSVRLcEIyZ0h0cWwzcDVKM29uZ05PREhtcVdhTzBPRGQzaURxbkczcjZiZzJkTjNvWTRkVnRQZVA5Qld6WTRXVjFTMm4waHlyVktZQTQwRlVwdUlidXlZS3BYbi9BSTNncGxuVTBITllDZmYxVllFa1cwOGQ0Zm1ueTExSmd6N3ZXS3BVRkZvMXVtUFRpRlVhMXo5anVkeDlvNFRHT3h3ZEMyWi9wQlZVTmUwYU8xMFF4blFLbWZOd1ZDVEVmcStDMmJINlcwNmNEUUkvQk9BYnJDT1JVYjk2cDMxWEFXQ01MWkdzZHJ2RXFwM2R0ZExDOU1rNHpvdHBwc0g5VWxWVFVybHpiYytQU2VTSEFhclphZmU5eENHR3JTQmhQcWRYSm5leTFWUitycEJ0bnRpOU5qRWVYbnlVNXBudmVTcGdnMUd0M0R6TUtqeHhCVkxkUHljQjYzTHVnbVNRaWZTUXEzeE1xaGh0ZWc3ZC9wZTFVeVR5YnZMUVJqcXp6VlcwMjhTcXpaRjRQWi9VL3dBTHEyeTVyMnpvWmFqRXV4YWZGVkhRN2wvVUIrS3JTUVJPUG5SSFhDWkhKcW92ZFQ0ZlZUUko2dDFyQnB2b3c0VWp1amlPUlZTYk1PK0MvbWFYL3NDWU96cWxtdXNJUzMvVHdiZVJuVk0vZHYxOGxzOGJtclFRcXJobUR2OEE1cXZ1OWc2NlJ3ZTZVT3NoaGNNWVRBQjFVbktxRVc3TzZJNXdnNTF6S1VBbjNxYndyTjZrTlc2NGhXM3ZHUTBLazhzY0pEK3F3ZUsyYXFXY2Q5RlV0bnBVclM3QkxpUlAySzFocGdGenpuVlZIVklyd0Y4bDFLZ0dyN3cxcXAybHQxNHBHWXh3Vm9GT2x1Tk9ZL3lxRHk5amh1a1JtVHBLbWtYTklJYmtQOGt5SE1jMGdmQlIrdnB6bnhsZnpMNCt4VUFISFlBWFA5VnhZNXArQ2RveE8wMmdvVHVqVWVKVWZxVHA1STczVXU0cHgzcUxocjRGVkpJMldnUWRjZ0g4MUZyT0pib3Y0QkdmVlV0NXJIalhtQ0ZUSmQxWXZOK3BWVjNWaWtEY2MrMlJQNEtpNGluUXllR3FkaDl0eEhDQXF6b3JWNnQxeGlZMFdvY0JqelJoOG03ZDhWdFQ3QSswNDFWWnJydjRmQVpXMFBNUkpsRWlQZWNqUGFEaWpKa20wSmd2c0RPOXc2U0p1L0ZlSDNwak1VeVJhN1JDRERqaDBJYU41cDExdEwzdVVkRk5yZFl0TXlvUTlnSjI4ZXJhQ2VRZEtjNE9xR0hHZFJPaU1EUldsektlSjhVK1hUY1dvM09jNGJ4UU9XNGxWVHVqQmlNSWlTWmN2YWNQaW41cHZ6aFZOV1krSi9OT3hlandUbmRuaFNiK1JRNzNpcXB6YzJ6MVRLYjkzV1V3TzZ3MmlWM2lNenc1ZERHdHRQQlljN0VxcXlMQmlGVUxDcXp5WXpMZFU4NE02TE81bkN0dUlSQU40VmRrNmQwcDdmREdGdGsydTl4VlliZGQzY3JhNUo5bHdXMnhBd0lLMnZOODNXbFZZbW5yQytVekoxN0ZiYzgvOWEyeHpyZUJZcWpZTTRnODFWcGYydVcyMHQweU95SytVV0gvQUtpdHFEdDZSdWxiZnh4cXRzYWZpdG9wL0FyL3hBQXBFQUVBQWdJQkF3TUVBZ01CQUFBQUFBQUJBQkVoTVVGUllYR0JrYUVRc2NId0lORXc0ZkZBLzlvQUNBRUJBQUUvTXY0Z0htWmh5ZTFHNWxVMG5sWUtXNHZxbUJzczJpSUtyalc0YStQVDBlai9BT1RBQUFnRnNUUmxuQklkcXlmaVM4OUFhb2UrbmpydXBvOE1zWkc1eGNlWlhpdXdESXdjKzNyRGRnaUtidXMrbjhnVi9BVksvd0FBRDBYM2ZUV0ljOVJGajBLeEdoWUZzR05zL1F6S2pKeEM2ZFA5RW9yNGRjdGVBODVwcVpEUTJaVk1MWmtjek1jRkxDSFQzL00zN3VJci9YK0llSCtOK1BXTUtsSDBXZzVITUFsazQxM05iS3Z1OHMxRlhVM1NYOExNbGtJMWVlQVJXZGdxdXVvblhlcm5RTG9pdTB6Z09DRGxIR215OE9tcFpxbjhzaDNKTFBHRFlkdlhFdXZDY24rQjlhQlg1aC9oc3h3V3puYmZxczNiQVZ5WnM4WWw3NkE0cGVYN3hLaXhteXJhSHg5cDRRWmVubVkrUGNPTytvbktISFFISzN3dkJkUU5mSjlSYjQvaGNxb0FRT2ROZFU2K3lZZVl1WUdDMk9ONWhndDdrTVhTUW1aTkIraHBsaXlybnBsOXB5cDRmbFAxeWx2YUEwK0prZk9yNksrakxmQnIrQU42M1JncTZGK2xwcVk5RllzcFhib21NK1ppU0hqWDlEN3plK2JVTmMvMUUyVlhLMzFmZzQralVpd0JrKzh1NStvUE5zZkVZSjhUbW1MZDFlbU9PMjZQa1hkVndnQnEyWDNQbDdScXdhVi8wSWhoWFYvbEFHZ1BCOUdXbVVkWjVUeStnWGo2enMvZ0c0dmhlTCtvK0RuS29aeTZwQnlOSTVBdiswMzhVb2JQajZRVURpWHdCbXRlZFE4NWFXeUFodnNOOTVrdWdWeWpWWFQ2VEF0eXlIUVBmVi9TWWtZV25xTnY0aXQyb09CdGFlMFNxMTF0dzVyUVFvSkxwM3R0K1pUcFBINkhFcFBvcVY5Qnhka0lLRDk2WERJL1BPemF5Ky9oaUkyMWY1eCtLN0ZoRTZrZ05qbHlTL2V5SUdydXV3MVY5TVFNOGlBZGlhWnQyNy9hdkRMb2V4WThpeVRHUHFpSVJNV3llbldYWGZodjBtV29uRTJGcndjbGFtSTRhcUVjNlo2TXpKam9HaDZkWUFOZW9kMHY3VERmMFJkQVB1aHpmb3FWOUZCYkJEMm9heWZ4N3pXTS93Q09kbjZFcUNRazB2UGUzMmlQWWVUbXBVQ0dHWWFMRzNMQld6Q2dHbTkxcWN6bFRLMXhZTWVJWkpqZ0M3aFRnRGZyS1FYV1c4TkdPQStBZXNYa3c0enZJeFhydWJEejVSU0tkbXVrelhMYTdkZzRQaUpaK2xvOE56V1h4OUtwVXFWQ013WjdKTDUxQThtUFZqZ3JOdlg1TjdJTmg1VW9DN2RZZWl4VHZmSGFWcndtbFRJOC93QlRQSmhZam1LUEpIU01jd0NJTXJSNm8zQ3NIWXhyYmFiaTlYVHIwbU42YnphWjZ3blgzTGQ0d0x0RTZ4ZXExck1yR3ZHRkdvVXpMK0RTTFpVMld5b1ROK0ZyUjRDdmVGenRUcG4zV2I4eGRaYmhxT285OTQ1bWFTS1dPV1BxVWFlRHFxMzRqcUNZdHJIQ05TK21wcFhheGtPN094bGRiTFQrZ0hlbVhVMzJ5OUQxbDV5eFZCY2o3MUtRSE9tRUtWYjdKV09uTm11OWJhckw1bDdlNmxoMTBjNytpZ3RTcGdtZ2pRV2FEYkE5cDVvNjdPY2RQZU1tV3ZBTFJkbzc4Ujg3VWRYVlU3cDc5WnF1UGdmdjFtYklqWXR3N1lpUWE2QmtzOXB5M29RR01zZm5INjBFNmpIMUtIZzA3RmhUaVZEZTJDUzR0TVRXNEgwUXBEa1F2M1pRZGpubkIxbVduVjk2ckFMa2lnV3hMSzdGYy9lQkpHSnNLbnZsU2lhT3BRMjIvU3ZiNU5YL0FLbC92WlN2Mk9ZMXFvQ3NZRVVOY0ZjVzRYcDlxZzM4YTJlcWwwMXFUVit6elh6TnpFTTFJaUtWcGEyVEE2aUFxczN4R295MzRrT3FCcGdkMThjd01IWEpVdEJta2RLT1RlbDZ1SHhPNlY3T3lBMnNxcmFtdm1VT29KYUlNM2NoVG43SEF2bWU4MkRYVWlQeXp5alh6TU1ZamJQdk9NN2hoNDd3Y0t6VlB6eXc3dEtoMHR6dnhGdkQ0bDhId29HazlkRlRaNFBnTDkzQWtWdzlxVmY1bENzb2FXY2kwL0hFYXdlVlVlVjMwd0t1c0RKOXhLSmpZLzdJNHF3MWJIWHJNS2w4ZWFwblpTNnpQU1ljZUlaWEwwTFZvSG1LTERNR0dFalhlOE9BWEs3WjZrMGxSS0FwdU5Mcml5b1A3bVhXZzNDeDFlWm5mb3U2UitzVFRmVlhwTUZZTTIzRWdNb0FMU3VCNWdzVmk2YnVWVlVuM1ZjeW5NNFcxWXYrdFFwRTZNWEgyWW44d3FWVjYxK2hLcUpDMXRkV0FXZVhYaU8rN0t1d1pVV09JQWdQVytlSVd4MWhQRWMrTXM5b2ZmMTNXcFlvYlBRKzF6dWNOTmRGUHRIaXhkc0RyenFPdU5MRDhTajI5UGNwM0FwcTA2SU52dkhxSVY5cnIzbUd0czRNSmliRkg5b2FGK3VCY2J6TWg0bFFlc1k0Z29yQUkwZkFxeEY4bnBMam81aUhvNnZlVy9NQ1dFWDNTTGVzdlhmWEg5UzRqU3grempQek5MRmV1RDhKUXNsd0RYWExoWkhZaDBqYk0rVGhPSW9zYmtHdnNHWTgydXFXeGU1OStycU1HeUZFbDF1UUh2eDlXMTNsMUhSSEdvdUtGVmxuZGNoNlFVMkJxQVpKak9yOUpabjNJdlBTNW5PSFY2eCsreDF3TjROdTg1bHBhNVZqMG1hZU1ZVUFQNWx0V2xvQTNWZGZpS3h0T05CazFjWWpnai9UdWhSS3dCeU4xVktqbFFXdGVIZDRpYkl4MTBWS1Vsc1MydnpFcDhsMzRqQ3dTMmRROFRHYzl5TXVjZlFmRTRWSUR5UjNuUnBoUzdNYkhkQytsR3JuaTQ3QzAzVVV0MVVHRGtZNlY0NER0TXRYWUVVc09rdXVsSHcxajZUWFJjdDFHSkJBQ3RsRlR4VVZOaDkxZGxwU3BTUHlOd1Q4cmhBaUxjanlmU0pxMUJiVXZSTEVTUmFVSERLcHN0YXRGdnhIUGhGQXQ0S0prK0JZN1RqR1hyRk9RTmdjWER6cllweXZPdUdXemxWM2VucGR4ZFE1eEt3OC9hVnNFbEs3aVBUaHE2YzVFc2JBakdyNkdJZGZJTGk1MzFndEVYejNMK3NpK1dFbllIcTF4TGtvRFZPclpHc3B2cnJDU3dFN0xpQWNJci82dnpLMWVGaTRJeDVRdGJYR0lOc2JkUU5uN1FLS1FrZFI3eEc4N3l3R25RUDd3VXZqRFdOR3ZpQ1YxZ3ZiRHcwQTZHc3lyREtDaTl5OFBtOENDOFpUS2lGVGQwUTFOVnJyTDJna1ZwMFl6ZVlLL1FDNmI5SWNBaXk0TTh6WWhTKy9EUHNBS3hlSVpxWUhYcUhTWEx1LzdtZlFlYk1GMGJlQm0vOEFNaFpNUmd1UVdCVFJPNVhjcmozVzF2akU2WmtUWFFRa0ZrWForSncybGRvZTBaN3RKdjhBTTA0K1dyMGxEcEpCZUdBWXd3VzdOelJWMmprbXFpdFVla0NUV3BpT21zejQ3eG11bHEyL2VVbTE2Wm1ES29FWnlKcmJjVFFEMTVWS0xYT01HUXpINlZMNFFzR3IyalVJWDdNenpucWYxanMvWThUL3hBQW9FQUVBQWdJQ0FnRUVBZ01CQVFBQUFBQUJFU0VBTVVGUllYR0JFSkdoc2NId0lPSHhNTkgvMmdBSUFRRUFBVDhoeHZqaDhNZU9MWHNPanZEQ3FQVVdvT2MyRjBYUVlCVllHSWNCbGROejNrY2xGcGQ0Wnh0bVlLQnlCdnNHOHQ5VitrNVQ5UHY5TThuazhuaDlZL1FaNnpMM2N2QjFqZE0yQlIybno0Q1pia1c0VEw0RkplVkh6YUpRcUt3UjQwbU10N0g2QVFKR21WdVBERVNocGRNUUNscXI2ZGRZL1Ruai93Q2ZZTDR2TmZrKy93RHBqamFzTGp1enlTU0pGM2t4MVFMRndMb3VDQWpIWkJqSlZKb205S0RaUWU0ZDZEMHh4eUFzbkkrK2FveUE4WUVMcmVkdTJteUpGS0hpdUpueG5SU0l1R1lSSFpuTWpOTWNldVF4SHJJWkhJNUQvQStuMGgvb0huRE1xTmZUV3ZpRVltRHlRZm5LRk90MWlOcVJvNmllY0pPbGE3QktScmhTRTd5VEZ2T3EwUytPRXlXOENYNTV5eUF6MWgyS3loSHBEd0VNTE5DV1VveHoxYVJhaGJsUVpGWVFSZ1laY1VRRlN4QVFwelhPYmZxaGFYcE5tVCtwVGY4QWdmQmtFWjY4MFBZc2ZkK29SMG94YkgrRHg5TU1wZllxR2kvSnYxbWlmc2hNSG5jWHprU0lsZ25NRTNMam9lWVlKL3c1NWxDWGFpRXNiQ3loZzB1V1k3T3lwQUsza2pEbkNBYzRHb3ZJNXBQOG0wbEhCUFpURno4R251aVJuSTVEV1VSZFJrU0d4TUV5VmhNOW1hSDZDR2s3YzkwaHorMHMvTHNCL1hySHJIei9BQUdPTHdFTC92Sis3bGlQcHRoWi93QWg3dytzdUhUU052R0NMWW5IQnpiQVdpWll0Nm5rV0ZSMHBWT1RhSTVuZ3RvTnF6VHpIN21JeUJIbU1PSG4rQStieGlVWUdLUW9JSFkwbXh5UUdhR0NaU0U2MzF4a0ZZbmZBTG0wZFRoMk13akN0RXZKRkRNR2JyeUZYQUc0bHA4SXlMcTB2eHFnTnFoNXlkdTB5L0p5UEk2aXlseGcyajVuUEQrK01Objc0ZFpldm8vYkhSZ1NoSCtGV0dUYXFVSWJuaURkNXpodm8vUC9BSjRGYzVNUnEzNUFTQmt6eDd6dHdCT3lKOXh6a3A4VEhCL2hwZm5hZmNlMlc1ek5UNU1DZThIRFM0TlBTTTlZTTdPbUJ5aTVHT1RjNUQxbVp4cDBTVlJiMkNjMVBmWk4yV2RDclBsazlJRldFWE1GaXBOWjVoY2VnMTNTYTd5Q25RdU5zMk50aXg2U1lkUDdZRGpBK1RCQ0FsZFlQYzh2ZWVtUitzUUdrSE9tdk15eVlXMU1tM1NOZlY1b0V3clhZOUpCQTVnT1FJaHhQVEZodlVaSGI4V25ON3VwYnlXd1VJTmFZVEN4UExNWUM0djRud2Z2ajR6dXcxTjI1Uy9LeTljWjNzZVBoajVES1IyVEMwTEpNSDBoSGtLclk2ZzhNbStocS9VVDEycTJ6bDN3VzdYdmxwQnhZWk81MEl1WHJoRS90WTRKZ2htM0dHd20vR1I2ZmVNbUY3T2NQb3orbU5lMS9HSDBmVDZCTTRERHQ1OGsySGhTQTlnM2hyMWVIaVlQS2xlNWVzajdvVmJxVWFKY2NFWXB4eWttVkxJa3Yyd1FmSU44ZE03TWxlRGh4MTZZYS9xY2dmOEFUSzZ5Vzk0SmU1TU52c3UvRkh3T05CbGRpR2poNGZPSERYLzJ5V2hmV1J0cXpiTzQydkkrT1lNenk3TWFVVzV5ZlFNb2FxQldrMFZNT1dWZUJzV0FGeVdLd3JNV2RWbmJoTmF2ZForSm1NMWpJNUQ2TDVNSGZCbW5adzRqWEZzcElDUEZycUluakxZbTJqcGJrK0MvYktpWTVuNVlocDVYakZNTGhrZzU4dVI0VFAyRzdjUGo1ZThqcTUvdDROeTBEcWN6RmxCK01qdzRtUGxBeG16UklDVlRzWXdDRURBMUkxck43TGZid2Z2ajFoU3dpOEZxQWk5aVh2QTdHUFpaRTJvbkZ4Qmp0azlIVEhTQnJQaWpPSWFPTTJEQWFaR1I5SCtrRXo1Nkw5UEtZTFlpTDVJT1NXeTY5YzEyZ0ZEbFNPaDFPU0xtSWdFMHBHa0pTTG1SRjdzRFBFaDFDZlJBeU9HUW1rY1BHQk05MkNpRlpDT2ZXRGN2ZmYwZktSa2RnWTVYcFBQakoyQnl2U04wWnN0Y1c3RS9CamxYUElyQUp5bDJPTmxTWVFmanFZbzZoeTNoR2hVb01FajdNaXBuRjZxRW5jQ0JvYXpTM2xxVmlZbUVjc2pOWG1mMEV1VVZHSjR3U2pBR1Q4eUVEU1NZMjRZYThXRGsrQ05EbzNqY3dVbzVhVXNkS05PVjQ5YzJSUGgxUENzNVhzTXhpcVQyZnRrVkEwekpCVFY5NUEwNXE4aHRIZjhBenhOczk1RmYxUVZ3aGZacXBpYjhvWmYwQjIxZzVjTVNndURydHNORUxjbG5FK01sSkJnMjRjNkFReVRIdkF0Y0NkUTJOMDdaeWVWa0s2TnVYdVJteUpMeWpCTFFtbjdZRzJ5Zjl1RDdCdkgwVGk3dExhcndBVmY1Y0RKd1pUWjQyWG1mb3lEbnJPditONFZkb3hlWGpjNzdpZWFHMDdTRXdtWHc2aUVyRk9ZamtoUW9URlNRa2s1eVBpRkdPVmpMSXlsSWtkTEtDN2hTQ3A3dDV5UFdqanI0NzNZWTRTVGxic3djSmpnSDVjSFVBa1BneDVLcER2d0JJQzNIZXNseDNHb3pJUzVFN2RZMlliSENJSFNFSU1OaitXdFJ3Zmw4bVNUeStjSVQ5ODF5cVg4N0NOZEdRU0luMVRwNGhuangyck9RcXJSQWZkdEZ3V1R6c3ZpTlBHRDRpSi9maWUzNG5KNUhhQ0dlSXZvb1BPVGZJSWJJcTAweldmQ2lpZEt6WDd4VWNvQTZPYmZHdkRMN1ppRVQ4aVJNa0dIQTJBTm9iWlN1OHJOWWlnMGdBdG9zTDdYSUt0OFptWW1LR2gyUW1LTTVITlh5ZU9jSmk3bGFXT0pZaGZ6NDBta2wxY1Rab0RLMTBiY2dlckpNWHFHOFljOFhJaWlybE5VWkx4akFXUmVOWEdHcUNwZXlvdzY5cXA0SDk1TnFJRkxBa3cwNHVYQ0NVdWdPc2tCNGJIY0V4Qk1ZTlJ5YmdTOVhwOFlFN2REd1RmcTNvY3JBRHNZb25Oem45TWpLcUh4ZUx5b0xLdnl6bFBUOXByZVhKOFI1VlhXS0hQTEk1ZGQ4Vyt5WnhVeGVoTWdEVlhyblduckJ1aU9qdVBtZWl6aW91WWI1Uzc0d1lHSENNbEIwVGdFTzJMSUo1cThKcHZTcGs2TlA2TWFZZ3dBUTRheWRnRm85ODQvOXlwcUU2bmdnTEtlS2dHRE1XaTJXSUlCQVFwWVlHVlpVMndrZGtzT2o4Z2pGRU1ONUp1d2xUZzJ0KzhsNUhkaFk1U0FlTWJMQWloU1Z1TUZTcEhrRk9qcS9mRFA4T1lYVXpnV24ydUFMKzNHV0JJZkdXSFRYN1lNdkk1cysrdXhVaXordVFNSWNHWUJCdG9SeVZ1S2trUlJuYjhBdno2eTY5M3dnaXJyRFRjdXNoQjZOUnNZZk9QNmY0US9PUnhlMk12Qm82WmJ6TXJFNVVlRVpFR2lMRUhId2ljM1Jyc2NLRklERkRiV2gxSjZ5OXZlSWpPNFo1Wkd4WktuWUhTZU1xY3dKeWxDTHl4N0lDRnJaWnhtbUxpWW9WZFB4a3JVUnJDUU9vSWF3NUtxdVFnTXcwczZ5dDErS2lBS1JwZ3NlQkJJME52T05ocFJ3ZUdVQkFidWVWWHJnQkJjdW1sZUZONVJqYkNTRlVWTFBSbEFsdDVBSlpUS2FXQXVhZVhHbjVJSElKUllhRnNrNVh5M0huTFZraWpNUFl5eFRHWEVpNmdVUjArVFM5NTNUMjNCR1l1bkRXTmhyRUlmVGhGcW1hb0d5dUpIN1lXWWpzblUxSnlqdThrSDV6Uis1ZmpGVEpUZTRCazErTUFSVndDNUE2bkZnQWdHQlQ0dk9TRVU1VXJ6dUl5WXJnYnR0aW1RcGdJUmt2ZjhBK1pIV2ppa0FJZlRPZlBvZ1VuNzRuV2wyRTdQZU1MRll4Q0VkUEtUODUzUDMvdkZLZGxZTUI2aUtmb1FCVjBZNDRtNkphM0gyTW1rbXpKQStLa3hZQ1lic3ZueCtzdXNta0NwNFVZRXhTZUtnY1BjOG1TdlZrd3pOZXpmR0dCWTI0UmlvcThWckJ2Z3pudXdDRmlNQ1pPLzFrcmJ5SmkrSmw3T01JRjdLSjJRSGtjNERZcFowN3dySEtHYTBZK2F3VXY4QWdSVDliQ0pTcTFlV24xaXVVYVlGRnRQS3luRzJPUUc5b0o5MlErRVN0REVZQlptMEJ6RWsxalRvSVQrVjQxaEhKSzhpTkIvT0lCODZLdGJyeVpzeUpMMWxRVzNLV3hkbTJ0dHRZdEIxaTh4OVFQanJDMVFnN0VMZkhmR011ZTJNRkYwc0NZOVpmYmRhWk9PanZGS2dGak1TdmRQNnpRVEZ5d056NVFQM3dUM1lHcFIweCs3a0pOaGpSSVI0SXdVTDc2Q0hzbkc1VU9ibzdJajFnMXd6QXMzWDN4R3lnQ3VmbmY4QXZJMGw4Ym92KzhlWnVWek9lK1Q1dzQyK2x5bzRBNUNWak1JSjl0T01TM2x1Z2lHNzNxc2pCZEFKbmJoMFg4NWU2a2dUMkNMYXdqcWJLMEx3TlBuS055NE9PVW1CV29POWF5R0N5Q21xUm92OE9FSlNFek0rZkdDbFVOamZaazFKQ0lSM09ZTW0xb290ajRFUE9YQm80MEhKcVRXc1NvUzhJTi9ZQms4U3NVZGJGSjNoTUtpNHVKNWJ5VmxnSG9yQjR3NEFpSHRtS0NjUFh5bmpGT0dJNmt0TE5oUzZ4d1podXhXUGt1Um5Yb1lISGVRUmtVMktmTGpHdS9rNnJiZVczRTdnL2JraEpwOElmNWd3b1p4RFJBbE9NamprU2Yzbk5qeDZRSTU4WkEyNENxcGl2Ykt6L0RlbjVZMzNJSUczZ3gvS1ZycGNEL016Zi8vRUFDY1FBUUVBQWdNQUFnSUNBZ01CQVFBQUFBRVJBQ0V4UVZGaGNZR1JFTEdod1NEUjRmRHgvOW9BQ0FFQkFBRS9FSlJobGZ3NjJZcFdNd0FsL3dEZGY5UG5FRWlIbXBVMVN1aHcxYm9YZkpWZG5LQnl3ay9LUFBNd1lwYXlQdjBvRGJCQUZJd3ZKakRHTG5Cd0lwamtCNnlCeEV4V0ZCcC80cXFUYmp5eFE0R0ptTkVBMWNBRmFVZVZuNGYydUFDODE0ZDR0MjBqdkpwdGdVNlJ6RlF1WU1TNS9zQmFJcWs3UjFCb0dBZU43b25hU0ZUSU1NcDNOdmtDbWlUT0NZc1V5RXgxWmpLd3haRU1xTHJHZk1reWYrRVM5R2FqaThWMk0yQk1oT1hrMEgrdjdmV1F4dTBkdXp5cVN5QzBwb00vMnMwaGFLMFNoZ0x5MUhpV0pGcWNxODVpdEFIR3JuL1BoQnNNaU9qeUpCTUJ0Q1VPSUdRZE9BTUEzQkZFZ3l2cThDSjNRMnBObEJaanpEaUJZbUo3d2t4R1gzR2ZFZnl2a1pPVDhmckZtVDR4SU9hVGRRdlh2OFA3VElKamdlY0tNbkQ3RzdCUm0xV2FEaW85cFdCTXNEVnBHcU1DUjN0RWNCQ2R5R2d3N21adHR1aW5XWndiQ1V0MzlhVU5wTFhnWkhVQTRyQzJHZ1lsajY2cm1haHZZQ0dLc2VOQ3BJU0pnc3VKNFp2UG9PSjltT1M2NnhlT3lQNFVHRjVza1k4NStVdjhWeVpnbUwvVXc0NGN2RmhYUENIUjZkRCtId1lwa1M1dWV0M0dPM0RjNkd2OGQxYWk3QlZSY3Q4Q0hxTlRMVVZyaUdCWFNBbndjVlRQclFOREVrZDIxMFJXdEtrVVEwSmhvSmNKMFR5cjVtNVNwcFVnVU5HTXpZQ3BvR2xPZHJWcDVYSTBOSzdXRUdsS3BWRkRYQTRKblFFZjBUVGtyd1pUMHVkSiszR3M4eC93RVdOaDVmN2pRNFJZL0QvOTErOEd2dkRUOEFmODVNUmQydDkyT0h3SjFqcmdjRnJMendQUDBPdm11QXVpNG1ZTXNNRXROYTQ0d0ZTQ3BzTEhPVFpBbkRBRWEwc3NSUEVUdmJSVWh6SFUzRVZ5QXBFTnVoSnlFUFRUWGx3RUlFcHFzNjdWYmdiYlBjWEszRzNGa1lRUzQ4eERvWTdKRFMzS0hIMzFWMURBTGx0OExqTGVxcmV5MlpEZGd3UTgvWHBWeE5MSjR4aHlxaDdvMUloMGdWQmMvd0QxT2EyVHpud0IrakVoUWhoNzVJVVI4UjJaUG45UEJvL1Q1WW8vU3Y4QVg4SFc0b0pmbyszQjljYUFNN1l1UDhOb0lUam9DcmFCeTZIQ1cvcGRtc2oxS01ZVW9HdldhbGpkOTZyVUZBNE90RWlrQkk3YTMrQUJoeWlqSjBoRS9aaUkzQXdmQWNJT1dwb2ZYY21IazZCWDBSSEZINGp6Y3VoQnh1RG1RSWlmYkVRN1FZWUlZUVMvWVo0cWd5SExhQmhiTVRXZ0p6WjZhSk9TZzRLNGdnajhBWlhMZWtES1A2clJhZ0xNZTVKUXhRRjU2d3RRQVl1OEwrRUo5RkR2K2poamh3RkV4dG8wSHIvMGQ1SURSckVWa1ZjTWxqbTN1WG11WVNRNzZXSHNNWTlDMTJxaWxudUtYWXhRdkF0MGphMjBSY0pndW0rV2g4M2Vlek4rK1N6eFZud21Nd0UyWUdPZWRBY3E2TVljaC84QUwwVkpjMWhnOUg0bmJ0eFVGdVBHRHFUWEFoc3BmOUtvWTQvb0l0NFQ4SXpucXpna0lCSk9UcEV3d3Fmb3V3YjA1ZVVqY0xtaE1obVlOcjRGMGN1ZWdCaVdHazE0U1hObTM4d2hSZW1HalZLNEh3SXNGamp5amVUUzdja1ZFZlRIZzJTaFUvQzVSVmgvOEpjbkdQbHNIaC9DVGxvbjhDdnVwY0lXb3ZRdjY1WERsaGk4L2VBTkFXaEc3Q1lNVkR1aERBQllvRkxVVC9jU2lxZHFVdVU0SllGdG9DZkVtdXNXVzZhdWsyVU5hNURya084NE5ESjA1N3dsazhUSWdqOGVGY01xZW9xWXVldjczbStyUVpQNTE0TWtLZXIzVFdDYzlLbWVsdjNNNGxpdWV4eE9KaVhjZXh4M3FZeTNxVTljV21vd3lWbmVuNkl2SytZRTFrWXNrZmEzVlBveWo5eGFKT3pYUUc5WUFFOGgvcmpEa0xoa0ZuMHdkWWczaHI1YzMrSHhRLzEvYjhjWS9iQWlCakZ3VG5SRFl3NVlhN2Myd3ZLa1NoZ2w1UXViWmU2THdUbEJERUJzSVkwQUl0UUJabXdGWDZQNDh4M3pPVnQrem1sOC93RDRIM2cyWTUvME54NEVJc0ZqOE1uVHArTVNHM3AxNzRZZmRmcHlVcnZnMmptWmY5WVFzNUxMZ2xHaVV6aXhMN3d2b0UzT1lkSElNNDRhVHZoR0RxcTd4T0p2WWpxRFRJeCsyVE1KQ3JxSHdCZE1mRFNheUVoNXlLZ254aEZ4T1RoaTZWTGJOZDdlTUY3dHJ4SDFpNU1zK05sY05keWdQT1pERmNibGVUa3JVVXJiSEl1bkZVL2FFRTJvVkdSdFdoOEFjUWdDVVpwY2hYSUh3NXdyQ213STJnb1dGelcrdldXb1FoeXlua0xubS9samdBTzNJa2RCR1Q1YWhBb3JrazJiRXUwVGpDNjQ1elV3NGhpajBlN3pMQ0tvb0E2UUs4L1RYTFFTYm1nNUxFbjJ5Rk96NlFBZ0NnbDAza3ZwSnZnYWdyT0lNdDJMb1hWQzc4RTJPcTRXTnhjQUhiazBCVDlmK2pQKzJjKzhaUkVFRnlxNkQ1Y2VicHVmb3lHNkw0aHlmalJCUFNUb2toQUxNbG9MUVRmVnBSVkxMYlJ0eklFeHNQT2xZd21ESWRzbTJ1c2hTUXJBR0prTklsSHRhUVdEQ3U1dDRQd3VYanZuQXRzc2ZsblY3MmlLSFNpYXhPS2puUUF6OXpLaWpvd0hjQVFLN1dHVFpQcnhMQzZaQkpxd041ZHpKWjVNOU5aVlFLWU5qc3k0RUJ2RmJobEtRTytrYWtvMG1lMFpiNEZpbFVpa0VyYWlWc1J2eEdRNUxGQ2lBekhZRmtGOVA0Y1lNUjRVUG9kWmNoS0paa3I4WlpoWVFFQkxhWDAxaXFnNmY0WUhkQnNwdmlraHJwd3Y5blZ5UGVkUUpIU3dtVjhrRlFsVlVOb1JDVWcxRU42NXFNT2hpYXh4NU5hT3pubWJPaGdnaFJ4UTBqYmcxTGpPR2pkZEpjMENpb1h6Zm5SbXI3MGtpdEI1Y0RVdjA1NjdnWXRXaHFwdkJ3MThaVzJMcm9MREU5VmNpZUJCSGtDRmhNT3VNODhBQVJWc1M0UUhXV2dvQTZ4SlBpd1d2UkNXNEpLNmFzcVhHRHMrNlNrbHVCRjJZU0ZXOFpnZEFvVEY1bVkvSFhuK2NoUGwwbTVvbDdFemFNeDRKVVlEVjIwOHMxam5wd3MybFdyZ0pnTFR5YlZvNm9DZytSZ0FjTTFlTkVRV2dCUGNXcGdLTlJFQVZOUFlnNDNYQUJEQTBBMURiWWNEOGFIZUxWdHg4R1FFQXFLOWhlOGlJQTFTMHFzYy93QStnT1JTcU9tNFQrTGQ3TlpGbTZkTk1UT01PVkswR3d3WDlwRE5SZmllWlZaamJESy9oUW1uM0dXRjdUVE0yVHJHZEtnQ3B4eWo4YXhnUjhtb01GUTBTS3JGM2d1T0tmQ3BGVFhiRlFnUk56ZFRGOEF3NENMYThZL0dEUFVxenFRQXdlbWNaMFM4bWtSRkx4Z0I0TkRPOEtFV0tRdU0yVmVFOVIyOUhpU0dDWFVkMmZRSWZveVVuaVAzd1FUZEdIcW1SZG41Y21oOFk5ZlRCRkhUZkVwWDk0M3RKQUhnQ0gyN3cyN0Z1V092N01RTUNSMFRiWlAyYnhXMmtLWmhsb2QySHQ0NjV6bnFaRERvV3hFd2xjUDBoM2RVb3JRSXpLK0tiY1dGSHJUamlacXg1c0w0a0xGME9QVTJ5TFdJQjBweGlRSEdCa05iTkZIN2NMZnFRZnUwNDBIL0FOOE9yVExJaDB4ZWd1U09iVGFQZC9HQmtSUGVQQzJQQU1YZ0JXRUJVbHRSZ0N4d05qMVpTcGVEc2hpcHVVZHZvZGozY3hXbXRUVk52bEFIYkVCZHVVRTIwRVVwcGdCUXM0VWdmRFJVWmllZG1nVXA2ZE0xQ3NYN2pOaFd0UldZais1RzM1eE1CMFMrbnZ2QzF6dTZnNk00dEFMdk1kL3ZFRUUxMU9HbU54eFZUS1lDbk92cGdFZGZ5V0lXbFJKNGxoSXZla3FaVzd1b1ZJR0RDbElOTmtDbFZzZHJGZ3hHazBlMlJGRUNoTGpvU0pUL0FIUkpDL0Z4N1M2VFhFN1VpSnVzb1BnaXBkUG9vWUVkZlpiS2FpamZTWTNua3lTaUxwUjY2d3JUcG5Xb0VhazdLeG90Nit4QVJMYWxHUzRQSEJ6RlFGb3RZbGZDVENsQTZBOG1GQkpjU3BjU013cUxpNjQ3QXFLZEtUSFNNZXA1c1lCRmJSVXc3VjhBTGdBVmh5VXh4WkJZYWFDazFoVmNzY0JhWWFPdFkzWVFLTlFwY2xmM2xPQ2M3UFpLREJrSEpMVkd0bXVEdEtLWTNtb3JLdEZSWGdtRXF1YmtBdkt0YkIxbWNldkxTTEJiRkM0OXE4dWhTVlZEN2RJbzFPSElSckVxa0ZqTGpLSnl4Z1BaU1NVb3ozcmc0cWhZQXVweURVMWpGM3U4SVdRRzhiQ1lXY3RUTkFKeWdYbmJML3duaHFvQ0NtdFpnczJZaXpJMGltOGNBVzFNbFh2ek1kUk1kSWV2dUdFSkRXQTJqVFN4M2ptMzFSQ0lpRWpIMkFieDgzV2dmUmdmV1NodEdpOTlZMjI1MDVTNWdoSFNPSGxMU1hucEpRc21Hc3RuWGVyYVhnYTEwWTh5L082YVVVZ2xjUkFwQ1ZVTXQzM3dEK1hHcEVpSmtzRjRYQzBUZE1ycnBlb01haEZqT0tVU0hVTE43d3NsaTNiZjFuUXZHT1NrSzdXRStxbk1XUVpFS1d1UWVzMlpKcU1ubHdnUlhQSk1wTzZUbW9BS2l6bzFtOFR5Z1h6Q0UxWWhsQzl3czhsV0NUeHBrTmFlUVFJMHJ4b3psbUgwL3dDZ1FJQmVLbTRZU1dBK3phOHByNG1CcE1ob0IyaTEvd0FaNS9xSmNBMmc1MGlPT1E0YUt2Q1AyeGxvUEpDYlBCQW5PSjdSSWg2MFdsSWdtTzR6RXg1a3JWS0hzeHNPTWwxQU5DMXd0OTd6V214WURyaXhJSVFhZklyNnBpdFQzOWdSSXFzR2tqY0ovZ2dKaE5DaStWeXJqQ1JZZk1IdmNOYkRXcUNKdTRESXZnNkNTb3NSc2N0ZXIxSUNJUzNOVHgrVEUwaVJTUG1RazNBMUZWMENKWmVxT1BCVmlZekpPU25JRG5sY0hoeGhBcFpPb0k2NXdzbDR3am5WMCt0SVc4Q1VJS3pKdkRENk1JWlRNU1RDYVlHZHk0N2c3ZTFSUVFGVlRpMG1Gc2dacmlON21NVXpPM3ZKVjdSUFFZODB1d2lDSGZIQzh6ZVA4aUlpT0JhR2k4NEpEWFhJYU1tdHhEdHdnL0EwMG1oRm5NTGhGV2g5WlVOc05LMHd0OGhCMVlYUmJUbDRUQ3E4V3NCc0JCYTFmY2xDL3dBQUNJdHllbHAzajZTSzhDcG5yajVqNnJOR0dONi9nTHRvUkhteDRIV0d6WjNVQjNVZmppL0Evd0FwUzBRS2VkVEhobklnalJRS0JjYmRwYlQwMVEwME1pd2xUOFlobzZOK21TSURkRWhDcWNCVXdzK2FxWW1BV0ZmZ3dTZS83bzJmNUhJcVZGSUVOTjNPeUxBeUo3RzROMkFGQkxnVGJYQzZpZVRkaFRDYjVyT0NCNGZwd3Y4QWQyQThnN2xkN3lJK3RTQVh2RWNvT3NBSTVOcnJEY2NaZVZFS3Z1RWRjcW91K2dUTGdzSmpnckNPZzRhWHB2YjVOS0RRekxURE1ZWElOeWZPeUU1SjdqNlNBaHFScndzT0J1UGdvNnhNSjNTc3hhZktEaHpjSlVqVjMyeFc1V0MrYkgvL3hBQWxFUUFDQWdJQ0FnSURBUUVCQUFBQUFBQUJBZ0FEQkJJRkVSTWhCaFFpSXpFUUZUTC8yZ0FJQVFJQkFRZ0FER2J6dWJFUldpTk12TnBvcVlOeU9OVTlpWkZ1RGJtMFl2bXljTEl4cjZONkF4RVZ6QTVnY3dPWnZBODNnZWJHYkdiR2JHYkdkbUJwdVlIZ2N4WGhmcitjbmpWWklRazF2a1p4dW1IbnJtdDRiT082dzNhcXRNMUZPdGdlQjRMSUhtNWdlYm1ibWJtYm1ibWJtYm1ibUJ4TnB1SUhnZnJ1QitwbVl0ZDdDdzJPVXZIazB4c3JFMkNuS3hjYjlkV1RYbmpvVVgzQUF5ck5wZnNRUEE4Rmdua0VEZ2p1YlRlYlRhYnphYlFQTjRIaXRONXYxTFNISFJlenU0ZDExSlZUdkVObExreTZoTDdnUzc1ZUErem9LOHJLUzY0WkZ0VmdWa3VWdjV0Tm9INmdzbmtFOGdnZWJ6eWdmM3p6YnFiUldNM204M2wxaEE3R2R6ZEZMSXFZT1Y1TVFCNm5YWG9mV1M4OWpMWnExREY4WVp6TGUxdHQ5S2p5TGNsb0Z0WXlTRzZaWEJIWTJnY0NDd1R5R0JubXhJaTlkemFiVGFiYWlCNEdtOGU2c3ZwTXJoOEt6TCt3ejIyVTRKYXZnY2pNc3IzeVRjQ09sRFN5dGJFNmppMVFJNkpaYUNvdmFnRmJGeXJia1Y2UmI3NklQVURBenNRT0JONXRDNW14Z2FibUJwdE41K3ZiczNDNEh0RkpYSER4UU92UWJvUmJKZm5KUVZEVjMxMnJzaTBwWWU0VDR0dFNodFhlb1pWbExFMnBhZEFVRm9tOFJ3QVFQSVo1SnZBODNNMk0zTURUZUViRHNNZnc5WXhKcVV0dDFCWk56QjdVN2NaU2pvN0E1ZVpXb0QxWmxOM1lWbERwN0lZUHRBalZqdERkMk9zaGJSMFlscWtldHpCWk5qTnB0TnB0QS9VRGR4SE5mdVdQK25zMUVDc0E3SHViUU9SQTVDbWNYK0RXQ0s0TXRycHVYcHRzaWhla1RrS0VBMlovS2UyZW44RE8vR1NVUDU5RnhhM1UyRTNFMm0wMm0wMmdjQ0FoZ0FYZHhYTyt3QUMvY0RlNEdIVUxmZ1p4YmRXVzlCdW9MR01DSnZ0T3dSMGIzT0ltMWE1VmdxTzZPbHY1S1BZOXFEcjZPd00zTTdNMm5ZZ2IxTzUyWVc2VUNNU2FkSXI2TDBFTEJCM3RObzFuU0djWmt2OEFhZElINm01bTgyUFV6anRWMkZidjFQbzlydE0vTFBIS0dlcTVlZ3JITEpzWVFaREhzRC9PNXRPNFc2N0UyQTlGdFBxZWd3UDhCNlhxRjFWZTV0TFBhbVlYYVpKQjdtMERUeUgrVE9wdFRHWmpqMTJkZGl5eXdvZGNSSHpzUGU3aThQRHRONHR5YTZhRy9TY2l0UDcvQUErLzZKMGY4WHFNMnJqcXdiZDJEdnZINmx2WklCM0dnbTNTR0IreEFTUjdySUdiT3hGVm4vZ2VzSDB0dFpIVFo3ajZoUmVIcUJ3MU56M2NYVTVLOFd0NW9LcmgwWk5PWGNDQmtQYnFjM0RUeGhnZW9OU2ZiQ2tIOFNCMlJCVXhYYVdrcmtJb1p5RjFnYnZGaWZyVUZpeHNUc0F2MUVjOWRIeUFlb3ZySzJsTk4xOW1pVmNWbDk2Sy9HcFRuaGJHK2xRbjZzL0R5cWEvenhLV2ZFQkwvWG9BQndDYkJaV2NUSXZwNU1zbUhmazN0cTN5UERPUHhoWUgvUDVEL3dDcDIwUGYya0piVmw3Q01SUkFOeDZxQjhqUWJyL2FjZkp0UDRyUTV5V1E0NjBmZEhsVGpxclVIanhhZnB1VkhLVjA0bkpFRC90dlN1dE9iekZtUmlzaHduZDBBT2ZqbzJPQ2VHRlE1SElwVjY3YS9raFJlVUhNOGNmeTVqQnBxNGt4cWlCQlUwRlhmOThYNW1Dc3p4OTN4aW9oWFpBSTFaQW1DeHRzNmxpZEFHWWxqMW51TS9seVc3WUR6ZHdHZDltWHJZWGxHTWowczdaSndLOFIxVEd2eHF1T2VzMzUxdVFHQzBaZVRTL21ydnpzcTdKODlsMmRuWks5T044bCttUEZxWDFqY1p5VnQzakNjZGxQYWE1WHhyMXU1Ykp4N3FRZXU3VjVBclpaeHRkeXZaSG9ZV29sZHlPS1Q1ZVBRRzhCT1N3N2FzYmNZK0NYVitrd3NwdVRkSWZZWXlqQXNzcVZwWHg3bDNVWjFUWTE0VXR4MXYxMmNKaEczQThvdzhVM1k3OUh6TFlWZW1zMlhxZ3pPTnlNUElSYksrR3pNdmlQczE4ZjhTZk93MHZOVmhwemZ0MTUzTDRuSDhzVnZOR2JiekZsVkFwM3ljU3c1MkxqVjRWcjNOYlZmekZsbFYyTUtiN1JWZGJocnlkSG41M2lNTVZXR3Y0L1Vwem4zK1VWNFM4WW9yNFI4TjdNeUpsMkg1SittdW8rZHdPTnhMV3djYnJoS3JINVBQV2ZLY2MxNTZ5bkZ4OHpqdzgrUDRXTmsvR2k5bngxS0RoM213MTBYNHBkY1RvWlNrL0lNZDZMNmlmanBzZmlNaWtmSHNqS2Jpa0M1ZVMrRm5Vc2ZrMWRmTGNzOTlTWlBJNCtWZDl2QXVSOGpHYWMxeWxWL0gzMXRXRng3ZWwvN1BGS1dwRi9KVTh4VjBGNUt0L2p6NHpjZGswSllkdVIrWFlmTDhVS01iaE9mK3FML3M0UEx0UmwvWnRmSld1ODJMaC9LcUsweHlNRDVFdUh5T1E3YzVrdmwyaDJ3TTJuajlyWncveUd2anNGNkRpOGtNS3BxeUh4eUdLMHNVczJHYnoyVnlyRHo0L05VMGgvRFQ4aDVYQnh4WGlQbTVMRXNEbFpqdDIvMnNsU1NxWDNqc3krKys0ZFE1TlZyYUtNZXBTR2dmRXhzWmxYeW90UklxMHh3R3Nwdng4d2xLZWtyWnA1UUIwTHM1QTZJRWVvTDBQS0FmVm1UYmF3TEJuNzJieWpxQ3hlK3o1bGIzQlowWjVFUHFCNmgrSVV6Ly9FQURNUkFBRURBd0lEQmdRR0F3RUFBQUFBQUFFQUFoRWhNVUVTVVFOaGNSQWdJb0d4d1RDUm9kRVRNa0JDVXZBRWN1R0MvOW9BQ0FFQ0FRay9BTzg0QXFoWlUxTXh5TytVTlhDZ0duNWdMQWtDL3FpQzNsK21IaUJGZnFmb0VaWkVnR3hpOU5pcERPSCsyMnFwZ25lQWhBSkpGdk1HcThKcGZuK2xjUkhPaDZoRUNCVGE5dVFUUkZlUjZEN3Avd0NKRmdUNGdSTVJ1SXdjRkdwRUVSQkhraUx3UnpIMzkwWUlwQi9SSHN3SSt0Q2hwa1ZUWkJBcVBzbTNvRE1HazV1bTYybUxEeFVtK0NvSm0zOFRpZVpWajgwZmduNEJzZ1MvTmFRYkNOd3JFVTZIc2tIY2JxWGFZdFBvaVd3UkVYNm4xakNCZU1FWG5tUHNzWCt4L1FtdEV6eFJFeW15NW9NRG1FQ0ozdXJkaGpvcE1FS2hnekg5cjVvVEVlSWU0UmdXbmZvRlQ0MTBSMEtCdDNBWWlaRlk2NVRwSEpZeXFqcFZTdzd4YzlQZER6Rm8zSXdxejJINGR3cnJQY3FVU0hhalVVLzRVQTREYWg4eFpHb3hZL0picTBRanBkdGd6V29SanBaV1IrSFN2ZU9UN2RnVHRRbXh2ODBDSGM3ZjlWU3ExeWpITEVxaEg5dWo4RTg1Vy9lM1BiZnNNU1FJeFVwdm1GWDRmSkZHVWU0S0d2ZHdSNmhXUUlPNFFrRXhTaHlqRkFlZFUwbUlRK0FjOTdidXRJQSs2cXF1RmdLUjF3blNBZVZ5dUlLT3BLZlBJb1FlOXVQVkdpdFB0MkhsM051MHlVMjZlU0p5cDArcVlTZXFpNW9ydGk0bkNFbjZJaWhySFE5dGUweUQyREtxWVI3QjJpcWMzNWdwNGMzVUFULzVKOW9UQ1hXbkNIaFRvRlFCV3NjN0JWSnFtaytJMkZrQ1pCQm10c25tdUM0bmt6M1hEREhFaUovTWE5MHJBS3VyejJiRDFRVFpCVklFcW9oSFNZdzZ5NDFlZ25ybFAvRWFDMDdYbWJiSmtEYlZLYkVxb0dPcTRaYmZNZzBKVXQvSzV0UUx0eW5RUzYvVUxqSFJGMm5FZ1c4MXh0ZkVrWE1tL2UyN2NMYjBXNkZ2SXEwZTYyN0N1WDBLNGdicEZqYzlJVHRaTnFFUXVDSFBNdzRtM1FMaGh0aDRSR0NQcURYZE8wdUlBbk1BVVRwZnZsUGtJMWlhbkN2dE5ma2dHWjNKQU1jb1FBMGdTZXFreVkrUzRaSTNvRTIxNDZKc1RCbXQwMnFiQmord3FtTithRVZIcXYya2p6UnBwQmlQZFlCK2dLRkNBZm1oWW9Ld0NNZ2lVSlJwcGFWdUlRalVLSncwQ1ozcElLNGdFalpjUFVBMENTWmNXbmlFRFNENzRDNHJPSERRQVhaSmRZQVZtQk1Kd2NDRzFrTkg1WjVsTjFPZHhPTFFtb2lpZTF1Z2pPSkZUSzRnY3h4TU9GaUl4eVhGSWJxT0JXQ0JLY1dNbUNTQTNKQVBPVStSdzdtUmMyQWpsVXB3aHJaNitJV1ZTWHN0MVRDWTRqaUxSVUNubEM0UU9vYVlONDlFTVA4QVJ5QXF4dm5SUkxYQTI2aFpIdVF0TS9oa3dEbXlJMURYbURTWVFvMEErcXVHRVgvaVF0eDZvQ0RNUVpvbkVBRTA1Rk9vRTNVd040V3FKMUNYU0taekpUUUJxOEJPSE5JcnNpT01XaXY3WnZBb1JKaWd0SlFFdDRqb2JKRnlJbmt0T3QzRUZCL0VleFFFQUcxQUs0Q0JjWGc2WnZGRGprRDVsTmpRUzBtOWR4aVFnUThPRlNSWFlxOFVQUFVQUUJVY3gyeEZqUTJqNUZPQWM4eVlCeUlrY3BpNmZZRVRBT2FmUkdobVBPVTk4TWJEclZJSDdRVnFKNHNRUWJHU2ZGdUZtWXJPWmhHcm1sc2Rjci9IRHlTYW5udFRDNFlkK0pBLzE1aFhJTitpM0JUR3REYTBuenVuUTV3anlDY05QTVRWT2dtSzVFV2pvcWk4a21TY20xemxVQngyT2dDTVRaUGt4QnBDQWtDQVlzakJOYWIydjBDekNtUnNtbHU4UWhVb1VRdWhBd2dzSjNZU2oyRkRzLy9FQUNrUkFBSUNBZ0lDQWdNQUFnSURBQUFBQUFFQ0FBTUVFUVVTRXlFR01SQVVJaUJCRlRJakpFTC8yZ0FJQVFNQkFRZ0FJTTZ6M1BjS3pyTWJIc3NjYXhyWEZiVmpKeDhmSnQ4VmRsRDBNVWNDZFoxTTBabzZtaitQYzBUOTZNMFpvelJtakFEK05DYUUwWnFGSmhXdFFXTVVpdkY2U3pHOEI4aTVSYThkMzhERWJTYW12d0ZuV0Q4Nm1oTkNhRSt2d0FmeG9RZ2IvR1BhNnFVQ0JUUWVpK1dtMENhcnZ1L3RxbngvcDBRc1JMTWF4ZlkxT3Y0MU9zNlFyb1RRbWhOQ2RaMU0xTkdhbW9GaFdWL3cyNEZJcE92SzFsbldQMXNIcXR1aUVBTmo1QzlGSXR4NldWUEdqcVREVzYvZldkWjFuV2RaMUU2aWRSK09qYTNPczZpYUVBKzRGOVFyQUFJbEJlaHJGcDJ3RmdLblpsbVMxQUlHTUZMR0xhMk1wcld0S2JSNk5iMWcxc2FleTdYcVFkSHJPc0srcDBNNlF6N0g1Nm1BVHJPcG1wNFhJM0V4YWkva21CV2xlVUErZFhXQ1FsZU1ReFo1U1NqRXhBcDNFZGtVaUxXTFc3RHhDcGl0cHh1MjJpb0YzT3M2bUZUT3M2d0xOQ0VldlhXZFpxYU0vclhxdng2MFJ0cjlRNzJkNmhTWStLOXlzeTJVdlMzVjJ1RlFLaXRlK2l5VytQOEFsdkFsZzNXNjdiVDZtakNOenJBazZ6cUoxRTBKb1RRbW9qRmRpSWcremNvVzV1dXAxOWJtbzJob0psNUQxQkVneHNGMkpXekd1cDl1R0t1WnRkYW5jT2RNS2hydFVLeVRHVHI5NkVWZGoxME02enJPcG5VenFaMWdYK1NZdnM2RGoreVFSdmY0NmlFVE5Pd2sxS3JyS2ovSlhIdU83SHdMM0o2VnA0eG9JK25pNmIvc0RvRUthSzUxMTlBVFFtaE5HYU0wWm93SVI3bGFqZm82TE5HVFIwUHgvcVoyd2xaZ0cvc0lzOGorUHBORUhZcFBuYnJjYUttTzBOVG9kRWpYNDBJQnFhTTBmOFNOalVvVDNBcW5jdUk3L3dBNjJaMWxkZlpoT1R3a1RDU3dkUk9zNnpxSmlLUExBQm83cnlpaDZ5cXFySUJBeU1DNmhoQmlBRFVLVUFFci9ocUxWdEN4YjFyVkxXTzhDalpqRGFnaEszYzZBRXFIc1RrbDN4b2FhbldkWUsyLzFpT3Y3QzZjcDdCcUZRUHUyMU1hNGl2T3ljZ0d0bHF0OHE2Y1VPKytvbXBvVHFUc3pRLzBpRTF0b0E3MGExNm5jVlJvenJxcmNUUWJjSUFNVDdFekFCeGM2bUNzZ0hzbFkvOEFwaVY5cmd0NWN6czNMYkdlNFFWNUwvZVgxOG5ZM1BXK1BXMDJxRFl4THY3SU9vUm9SRkxqYkZOMW1hRzlUSHBSc1YySVFrYmdIUmpPNVplcGZTS1FTSy85TldoYllTbHZzWmczeHhqTldpN1o4eEFObE1peTJqYXFMblA5OGZkU2J4clA2ZnZFem9TNU10SVIxWVpGVlZ2RmpkNjFWN000ZS95Wm8xMGdxaTF3cHBCQUNQcFBXRzBHL3FNZzdtYUV1L3JGRUNJLy9WelVBTnI2cEJYTDduakc2SEtzcko3MldHNU56QUwzWW0yR0JWWWV6NE9DbE9TckRrYVI1OXloMjhyQVo1Y1kxZGpZNzFQOGRheHNDM2pNNzBuRjVtUmZ5SUVDZ0NhRTFPbjhDZEoyMVIxbTFFQTB4Z1FFeTVPbFJFcTlOTGFrY1JVNjA2Rm0vd0JNckZSdFFWdVlxZ0RxYmY4QXhNQU1XcktPU3JuUG92dHlrc1d2R0ZTZ0ZzSEh1VHE5R0pqVTR4cFN2RXdzY2pxVFZTbXdPUVRYYURrY1lEWk9mamlzUFA4QWtGUG9MbHF4MU93YkhEQU9uYlVSMUlKbFQxRi81eW02b1MrTTlGbGhFZTVFMXZ6VmlrR01WZW5xVGxVVnN5bHN1a0tETVN4TC9hL3UwbXpwQmxMWGsrTTV0NjFsZGp4dFVHQlBqeDJZNFBKNCtYVlkxVm5ONE9QeUl4WHpmbVNZV2ErT3o1SnR4VGoyNGFadVh4aXZRT2JveCtGUy9JZmxIU25MRXhQa09aZGswclI1YjZPSFR5VThoZmZqVjlxMzVGK0p2TlBCYzF5TnRsZmw1Mjk2c05mRDhWenVWdTVOeGZ6dC9JMDFZbXZEU254bzIzVzIvd0RvQnp5WExWcG01SVBONTFlUHhQSDJUNGxsREp3WE11emJzTGxHckhQOGxmaWZKUlVueWJJdVRJeDFyNzNZK1FLcGFkWWJHZkhNK3JMcnRWZWRORlhKMFd0OGlvNDhjdTV0eDhSY3pCdlZQalZkL0Y4VlhSZit2aE54OVA2bktZNy9BSzJVcDRiaDdzZlB4N0FhMnlhLzZUZ3Vac1VYbW5qTWppQU82OFhZbnlCTWxlUnhyTFNBbkcvRE1uZ09hZS9LNXY0Nk1ud0NqSzRldkk0NDQxWW9MWUlyT1Y4VnR5WHkrMmY4ZWJNNC9GclRnc0plTXA4UTVUQlBJb3FMem54KzdrczliMXp1UHN6R3JjTWxpNmxTbXpHNkhpdURyNGd0MXllSmU0TDVjejQ1aWNqZjVMbDQvSEd0amo4WlBhTmlWTUJ0NkErcFRpMWh1cFhDdHg5Mk4rMWN5TWtzeDgzSXlGYXcxaFg5MjFmc2doRFhsVXFEYXZrY0FUcHIzRW9KcVl3SXhKMmFpWlZpcFcyeDRhejdBb1QvQUY0ZjQ2dzQyMTFFcjZqVS9YY1EwdVRzbkhjL1gvL0VBREFSQUFFREF3RUdCUU1FQXdBQUFBQUFBQUVBQWhFaE1VRlJBeEFTSUdGeElvR1JvYkV3UU1FVE10SHdjb0x4LzlvQUNBRURBUWsvQU9ZVVJtYVdwbjJSaDNzVDAwVFlJKzJ0Qlg3cGd3djNPenAvMUdTS0gzVmZ0UmYxUXlPOWpWRXBwYk9salB3VUtBeURxaDFCUWtkUHRhd2FJeEdFZkxDZHdrZWkwdmVSbUZRK3lIMlJ0K0w3NE00S2dUcjhxdERPaVBEcURieU83MCt3c3JqdjhLM3NvbmtvcWhHSndmd1VPdjhBU2o3L0FGeGNvOGhFelkwSjdZVFlLck9GUTk2WFhpR2lOZEQrRGxVSStybFdubC9wUWxwRmpWSGhQV284amNJVU9SVWV1K3JkVjczUStxYzh1bTRwdkNkUitRaUhOR2wvUzRWRlJCV1ErbHFPYlRmWlhWZXVmWCtVN3lQODJRK3Bxcm9VNURVR09jZ2pRcWtEdUZXZEU0SWt4SHJ6WTU5VWM4NW9td1NFRFVZN29laU0vVzE1RFZORWdJZjJFNWFLeG5jRHpVTzhKdExLUWlyTEJDS0Jqc20xajhvMDkxZERBbTFLSW9pWXlnQkJCcFJQQTdsT0xoQi94M0hrenlhN25WVmFvd1pFS283Smc5MDNodU5iV1RxOW9SbGFDcWZOc1JsUVJVR2hOWlRaQUZ1eFd4OFV4NGhtQ1I4TFpGcklPSTV0ZVRCV203WGxZVE9SWUp2Q0IxQlcxNFdnVkVYVHk2NXFlc3BvSUdFMkc2SnNJZWdWdHh2K054Q3p1TkVVSXFQaEZVQldxekNkWXdqUTdybWl2TWJyeVFzQkdlRzlFMC9xR0kwcmFxMlpQQ1ltVnRJSmNhQUFORGd3RXlRdGkvYWNUakliRkliZVRnbE1JSUxxUVhFZUxoNkRDTU5aczltWkF2eFNhQXJaUGR4Z2tVdkFOQkNad3ZEUkxUY0hJSzJYaUxRYVRTUWIra0puRnRBQVFBU1pwSkhTQW1RZHJZUWFBWEpuckFDYVNYT2p0UTFLQkRReDl3ZEU4RGkyYlFadk1tdVlsYllndDhVaUluQWo4WFJ5ejVDZWZDOXc3VkttSHRjT3RLcWFPejJCWEVRTm9BSEd4Q2FTM3dXQmlxbnhPSW9xamlhUVkxQlF3ZmdyRVRQbW1TU0JYcUNtU1RCdkdFNHRmeDdXQ2JIaGJ3K1dJVHk0aHN1R29NcWRpSE9pS0U5VDRnWUUxS21EczJTNFhvRE1XcWkvOU51eU5YR3ZFVG5FZ0l5NHhVM1BkT0RXc2ppQW1DZkVEZXNTUlRvbmZ1QWNMaW56QlJIQVcyQXNhMGxXSnFQOVhCRWtiUmhpb0lnaUl2S2FTR05nRWtXRHJIcW1VTGdTQ1NNR1ZXT0gyaGJKa3ZkTGJ6RXpVakpUV3hzcGtFVEFpRHc2VlVZbnlFSXh3dUR2UXJibGdBYlFHbEpPcWVXL3B1bnZpQ3B1UGxaQkhxbkYzRW16QmtLaGdaVFJBbW1ETjU3NVZLUkdJd093VnhNSXBzK2NJUUpwV1U0dzR5ZXAxVUZvRVYwdmJ1ckJHNmZ4OTVUcUJHcUtLTzg3anZLSy85az0iIGlkPSJjIiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIvPjwvZGVmcz48L3N2Zz4=", + "description": "Displays the latest power consumption telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'powerConsumption', label: 'Power consumption', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Power consumption\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"square\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"bolt\",\"iconColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":5,\"color\":\"#3B911C\"},{\"from\":5,\"to\":15,\"color\":\"#F77410\"},{\"from\":15,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"52px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":5,\"color\":\"#3B911C\"},{\"from\":5,\"to\":15,\"color\":\"#F77410\"},{\"from\":15,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":10,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:cG93ZXJfY29uc3VtcHRpb25fY2FyZF9iYWNrZ3JvdW5kLnBuZw==:cG93ZXJfY29uc3VtcHRpb25fY2FyZF9iYWNrZ3JvdW5kLnBuZw==;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPQAAADMCAMAAABz2H4MAAADAFBMVEXh5f/p6v+uwP6rvv/m6P3a3/6huP6kuv6nvf+xw/7H0/+4yP7c4f+etv+0xP7Q2f/Y3v+7yv7E0P/Azf+Zs/7N1/7T2//L1f+2xv/e4//Cz/7V3P+9y//t6v3u7f/j5//r7P/w6/vS2PvX3Pvr6Pvx7f04JzXm6P93Znju4/rJ0vtyYnTl5Pnf4fp6aX1vXW7w5/wxIS/s3/ghFSK4xfZnXG+tvfOzwfZrWGZdU2W/yvdSSVxjV2rm3fLU2fbh4PR9boNeXHfp3PhXTmGTi6LT1vDN0u7N0/Xb3fbDyu3GyeTJw9qCbn4XDRc+LDq+xunOyN2Hj7aPhZtkUV5ZVW1LRVe2we90faRtdpzj1vHV0+iMeYna2/Dg2+6AiLHRzOGmqs2eps1nbpKyqL1OTmmkrNXGz/jb1euOmsiakKdlY3/MzujHvdGGc4VpX3W+udGWoMuaptV1bYTIzvC3tMt3eZmSfpFlaoh/a3dNOkWQoNIQBgyro7iIf5RxaH6xvOizrcSOl7/p4vTZzuVHMz6juPOvtNWZla+ChqeAeZRuZXpxdJOttt+fosSQkK58f56msdxEPk+Qlbd0g7B8gqhdY4Tc0PKqt+uXncKFksB9dYo/N0XAxOBtbYlTV3W6vt7CtsqEgZxeTFjl2PiltOLAv9eamblDRmKJjKyKhqSfrN2ioLmrqsWUp9x2cY2EeIxRQE0pHCi5wOWhjZ80LT2hl6tZRk+usM1zkMPVzPS8r8J5i7q0u+CZhpijsuqhnLPt5/e2uNc7Pllym9Osmq+no8CKdH3h0viulqMyNEtpe6dicp1baZGYgYtxpeQhIDJriLpQXoSareTBwvSGm899lsl8Zm+DqOaase9nntxnlM7MyPSCotu+pbVigrIoKT6Lr+6gipNhp+vQxeleisJFUXXg1OTNsb7EuuDcy9qokJrIqrd0rvHTwdK0nrP7/PuXfX9Seq26n6V2Xme1rNTVtsOUtPWOcXF5WFlTcZ2PaWJ+SUXm6+Lt0Lqbxq5iMS+aR8o2AABszklEQVR42rSXvavTUBTAJYWXBrEgOjj4T4h6i+gSnB0KPpIpQ6gQ3yB2kEJAocPFjw5Sk1CDNCCIkUDEQUQoZMjWVqiNFTqImfpHuHlub9tjPupX9XfOvcl9vOXXc8/t7bFaTSogFKjkqS4OYGKEyefPT+7fezttukP1vTUZvp9/+fLx27ePIRU5BxlOsHHiBIyfcfo05P+CSdeAjDO677Q+WFRX0qLohhPXevq8d/ltrCjiMFx8scD4y0LccsAzIw3Ag0eJcuadxb+XhkBgIfBgidJZ74VY4dILZeJGQ/3JJfmVfM43Z+OPH615QkWE+UIiaL9VR3PUhoHO2dxfGpAgAS6/cpXWb+VUFpSZM+tkOKafl8PHj/xWpPWmU39GKQ1FEbXRfTfcnc+ojd5refTdW1piyeXLKbiLi8oGcxIO4/HcObruTSajZ+euLWFjhyLsgXIOYPxMHsjXnM8Y+0ufObPpaoi8N9Y9V+jqVtoaD5fLjx/bnQfNyEpH6eU0ntOEVkUWnGrRnEe5MiREQTzjvr90kYJ3Fkq3RxudKOlyWIn0ViB/mI6uLa2OlzpJUt2A2kWYd8E+65y3BZjxfpUHadBmo7aaETSG4OL8HQrNjzamP3dny3AcJmpIbN87v4yGlGq9VKXVMtAepdnzJ5z4NX9qfYJJc2p82lVvAVlUVtIk6ROiK/GcTOammoTe9VEaLr6Gi0XnylvfQFMsOuQuigc7gDUvq/qPxcf8NSCNoHQRlJdEystO3UZH8eI4gr42FTNeHsWz+Rfr/Zcv5stHV1MvqlSz5Nc56eI2p+X3mIPTFBudq6P7b0kfB7LeNRi7tYUKLzRsedJoTtOrM6EaToZK7Kd+06GVZF4N1Iev7x4e6vxbbRMgCVkRM7ZFb0i0xsMtE/vBpdEa2VluYVGFCYA5aMdXL6tCuFQ6r5xRanY9WqHvk1Fv8Pzm5eeaSbM319WE7N7wZccaDMj9wUqzwRPZYS3SH9rckOWe7ibuyO8OUrdieEGVjuO33dv27aYXGW5CtrXm2iJfbZzZ2FX6rHih5bNPCOyDX0ufPHnyB+2MdZm6tJBqaE1ULzZDy4qvnW/a5nwoO1/H6fROi2pR0+tUIqPFi43m3B53PARjp7SIpmU/WlCW3+h4/CJB+jiTZlw4cwGsmXoJW+cKTEjo+aFE9Om01xzFw7DbbC2nAzsiLhk/lkE4MUNa2OGQq5rnyUuLOWMEK71eYAC/tIZKA+D96fha/6fSlG56m38GSbMZSpLpj251AnWpPTjsHh0ZM4s0yFxvu9D5JDCTXGdD5rY5L3l5hx+UQg/2YVVpvsUvnIQAZQiAPXmf17ackRa5G4zpq64kwPXzSLfCwOsdDu4NWqYXNEg0u+1EhNIocI2E4o80JHe2c3HInDQkCwa+/MKbls8onYXpcuUMNfgLfIdT4UxOuhlY1Er9ZVuL5sqr6+fOXXyuuU3dHk98R5YnwNiyVDcShKIvTwh44sHG559QsKN84FQG/p+I0sd5sOTGeLjVIFaFJxTPOC5vOHVTTZeWpbdCyZCvXnx3/q0ztJpKELlWS37Bf5O3HMOIos3vcph4oD+I7+CHeufUMXL+uNr8ByQG/DVXaa5dqDLAJCMue7y2Pdof6zVj5CdjQ39hEOPO9cNLb3zfCdU7dYl8IHcUYQWZEaOrEy4NcOfVAg90RCy/rKMsgAYow0dJ5BfHznJ+YX6mBqa0DoVHbci6rIZxaoSWOTCMoCP71wfys9SPvUFLkj70Ayg1o/7BNIKmFgkrttYssbchyyr+Y2/jO4yNzDqz7jxxlV2CdIEdbS5R/mTeMNVIo/PksOunLXccmfKgb6i659iqLcdpOh20BMsUHnQJu8NI5m0i2F3DXGtvKy1w9yr33lCm/ndQsQyUPrkOACu+YqNKa5s33udmLB++HaW3a65eN466HV1NnLGimrrmTHvXUqWlSX35tgQkhiUJL5woMgwqIOtK//xkF8EYd/p+oPQpFkyVjdJKQ4AqEfI7PmiPplMnSSzHHA6MI2dOZ0NXI0HXkwfnz19zVCI12nVJSMz+h7pAbIXQViPo/1htfG5VCV7ddh9w4l4fAVaa51oaRo46zbc59HT/Q7zUXowVWx1oj1VTmjUsI4i8dmSP5NHb7o0X5pFtmA1NW6qWonoTQLUDgYPq2OKZM53f1fdBhCyJY6c4+bYudDg9jqVHXMVeNh3b1gdK/6lDPM2uN+LJHUXR2y/a094d+86hYoZhoI0j2lc0QoS6phib1pY25rjX10/s8d9y2xmQJRxjwpuARLLW31k1n9dmijCOi3qwWUjdlmqsQUTw0pu8ywzGU/AgClIW2t09LWZJypKD7CglEMGwh2ok0azJLptVGxEWUlfyglKwEAmYW2wgptSb2FPP/gMiPrOTZkzTH6J+Z/bZeaf05f3M82PmnQTh9E3Q532p4kH5Mh6SFK7ZnZ/0aOhhUnTJEK+Q0vHu+y/vo2QSQgLOrJHnQ4KXdb9cPkB/dzbXtaxmD5j/V49sxn5m5HQEluPPS5uA4H1DUQ8CIXka9qfeQyuV8mv5qd3VLEQOS2gImxYm3ePjmo+TSTymZj8A6MHpyqMD3RjEnr52y7pEzpBp/x/ZwdNAHYvBZyj1UsiLB5IgLue6gAHaapXN8vQw64bYzu+OWhIigZXFmgFJP1APd46zTcDWItjXSQl2MHS6shKZpRpL7dnVI4dfzHZ0ZdFjiIEz+GsRca8Wf+ORTQ7N0aGzh5FDcAuxp6+Vdurp0DB9QRof5rt5rbFzrGKMc7rdcOQhKQeGNva6k4tiDpFWMpnC+zZAnqKzMiJZXR48eoAo8iL2YkFnLzZite0G3QnL2tWImhj67w1QOTltFFtAt6V64NkauB9NazXLzXc/3zE0V254Xq1zedG57Ey98VR1a6Pjw7aaBFklhFALDuJ+VJL1kp73wPUcG3Nqhg2v+So8AdD/QgyZ/4F5OqbOgMlA57RU7J0Wffq6kTrIehZAl6dfFYyKZF/svrJfiFzL0hWseQGGrHdCIWVd5MHbZqulFkuqqtlqC4Yd48G37xf9lSvxYr4U5jzb/52u/94ja2trm5vxA2aNxjrwL0Y7StLJebLz0xtAm54D7C23qliqUCkd1UZdw5LaVUGwRkgASQSnULeuDz9/2PYRKeGB1U/CHUSl1H1wvGMTH8+YWae6DZ+i/wtgbjl0DDvHnonBMwmI8y8mfVrqB6omiaGsV5WMbhHPUgwNziuFqhTpw1CgCn1BuLRSytFw1ChUShY9nsG9i50djYp6EkXEB9q5s9l7NrNMPcNedDosxm3iWcK5WXhTamoZNYz5gKIzZs4NuZCBh2JLukEsWQxC+5OK4+zXOyRdUHVt6u23dWzYMTSOJMHVYFCpjj5tHHp+8ic/mVQnFzu2awI9IgFZ+kYAaHlLuxPtps7PuYu74iOAyj3NxAegdSQu1XceBf2xtN4g/bT5jpxZr9c6RBRdSyxrbldFxGD/Q40qgtOVaKTbO8fHWSep9/B0MlEbngXQQK1nC2gBGxp0NuKB/290xQ6WNWoA+jaxpViX8JyQI4vQ4lA/1dfXFcNctz+qKetitUvSSG5UxLpdkT23WwhDJ4qswGyOVFfVjGl+OHmw05RhGxsFmHScwB+EkRnUSnKEb/l0HPBZY6//KAZ/MzR3eVphzCzSmZNnWU+ltnwwWsZuFqoZRc6TNAnrVakii6Ji5qGoadpPLVXVOx1ddjWLtIidn+zu7E60fuBmDS0wzX65FxnEJQMcfw0kNsti1Fz/FB8xsyiAvhNbybCKvsZyn+GyRnuLmFFFbtt2MyNXc3WvIAaZ9XbbAmgxXbE8zcmIkkQEqyP6JhLFKESl3YtvP31YQMhWSzpK+QROLa4t44gg9qWIW7A5LjP3aR4XmJm5nhg8kkjcBS1JDJT7nhquVpQhqpOp7jftXLWulKIwgkJXPWxCxaNnuJyhRYIYIaVbsRGtakFfnmj5nVG2XXGjw5KPCYbjqmPIyaQvmz67TU9RsSHtrIFux+NidGyKl4e54we98nkZoBNricQWfxYcjeG5WRQ/k1FziU3VruS85vbad41KKTIliItc8WhzdoSVolPNkSKx4/q0qB20emgIoW11Pn/7yG8Ww8hPAqCk6WCx5VoIx7CAfI+WuVlfmmeNehiAqXpnAH1dW4nEFTpe5wtwFd6zEhe/lamVqNgFVdabe2t7hWqJWJTWqj7c5CcZYmh2aBgiQKOfT4jfDco9H5v5z4vVV80ILhjhEsZysT8465mqYVLkFPT7oJNcd6ZB7PUnDs5mwAePrazcAB0zx9wZae0uwQKouUwrqO/lxs36VmLryHMrsCyK+XGNVbrZ4T008o28gtDg5OdQbg3VvqnbgbYzeemVBiFlMwgC12sFgVm2dMszfYp9o65h87YSP7ewo7mHEZvh0MvUNLjvFkA3TX1vYyNjtKuvP/30dlGl4eCQgswLfOzugnrRnY6nY2N86RWHpy1N1Trem29CRWsWQh8pDnhYEJKR75eISdAck324tEx9Ez7VLcC93gBh/kMOvSQIbiDny7A1f8BAB61rJcvZWF3N/OR8+cnWxuvFUi6jhGZGsTevUaeJdjluIXTyS/mHS1OUztVa9sFoMsp/2nAkQQgDFQmpwSDlu7IUWgQz4ju0SJ2CBqLwqRk9OjjrQdXqnQ0OMFsRDv30TMvQmUziPmXUqrG38dST/i/ome+/f3a72IB9qxCtKTYNAwrNqVf63qV8cpIKnS5saEFnMuk4Rqg0u8NqXfDNwEohuGiILB0LUmiGEr1mptxg7oSmY9rmc/BGMS8EtI8XwoFDL2uLLsGmdCsr+Hsm1zG+W33qSSS98Nzjf76xXf24Ubdbm2tKOzFLACagFs5PsDK+7EuS5alilJ+MTCWqhaKouMNdPVeuaJhgjExTl0070NUgxEKse33OqFkTktg/Y7wDxHHjGIj5r0NDJZo9T0NLSEB2OzY00NNukGu89uze68///vvvf/z5RfWD7Wpdh/Bub3FmEGzavROcOj9BwTgg+54zmlzmRDHaD0VBkuqHx/mjdtEN5CBQA0uDJMdS4GqIU6dmAy4BprjTY2SMfMpbpgm8GP1x5wLomyXdH9zArqpbH7SfVB4HZqD+8GF99a397YrsfLwFi8Kx18Wz37D46884vS7Z04s3HzzYdyJitrpav99q9fXOxUU+W6SfK1i+pPuUUIeNkEjCXMC4IBb93Nfg4PNzmsGY+vtqLeI3kwAjOuaeXgrwOLifjhvVwg/5/N7Y3lj95oMvf2f683B74/Wvf9x6125T6Dn2Wrr3MxJ+PQFmkKRO3nvzcqqR0DfM+LKRKGbn8PNduSKmLUV0ZEEUMRFC3Qkijn0NmYr9MElL9C8xL+wArApcD3sWDGwKPL2xcSO1MvfmFSnvs3lIhL3xDxurr33yxYsx83M/fra9Wnjr6N2N7Rz9axNX3Omznw/EX0/Q1VVjKHd1LYBjqh3Qa5gQpS1PKtQ+zbYlE4uSikSBmBJW/VAPfOEOQUCf0YCOBkiKcQXwMguCv4tWdD6m0Fz0HwqdSvqLc3MLjZwK47iC+2AV0mnJiNMFJYMv8QJqtBpFKGpDvBACNTNeiJNxEkoFSRyGgfUS8+A4OplOmqY0VTulErrj2PUSqxU63oqK9UKtbB9lH6R4eVAQwRcVv5PMJJ22uur/y5xz0u7D/s73nfNNvpOSF51NJFxs3mAx3P7zgd9/B+gr7qhyuEv6zzB+LknE2LCewc+7VCquRCW0rAiVNzUP2/r8e4nj83Pg8Mri5hg/b7VFB7azjGGIGU22RK2L3Su1oxb2K3AvhHOwfo+ekNDpyOJdIHQ1QPdTwwfAFRq6I0Fj4XBLZjoq6+de/BPp90vHG1zHxPHVSs688Fh6BMMD6sSpr0YTp7+6IBUJ0BPwUsasLpWFxOgpavT69lhbBdCdzTNTvGxlLU2TEwnKgb2dH9V4K9EVwj0f4S5szE5qyL1nEXD2jcNMEEIfICe5iyLXRyM8pg0NUWdYReXZ9Oqff95xx59Xjjd8g8Rx5uNfvz33vPMuBmpw9vCpd4D5nYA5xr5g48RxKzu3WS7ly/DQnT/5ubgwqbXhAGyzJledUc0DRkNOWJPHLbtmUQmKGkW5aPedjXdg/c6P9qbhKIsFkGELXSyAPiyWjHD71GWN6ZUMi7NZkzWXHr792Or6wyVLwjFs5Pvvr4FovzSgTn29i5ijxzVY4j1oeBwrbJVlXaBovdam0Iqn1YI7tjlRplQBFrtQTajWqEY5kmHsnWhDMvI8yGbxUWL/0cP+z5HqsZ+DIQEQFrPRSnAXQccuDz9xRNCZIi6psFfX15lBZrpSMzgsmSQ+vPdn0GWXjChC6tQGiu3EwUcVgEYVGFrOZ2Wg1Wu6PgyyzKZFGSe3GjwFrhUaUkadPKHxY9szY1aiX9F5y2FsaP9RAB1iB00ghQ7noHcf/6ofGUzJFLlMBzfVUgu2LrY2xcM/Tb94xwc/I12SrsgnNkYv2N294FClgtrQhsPlbeXLFnia1mgot3n+nEwJ887WyazR9jxJ5L12wpvbLGyqmhyGNA0WIiPowzoMffAnCYCOFcLQOHYQOcbul5KXVBV+JZXrdgfDcxMtFksS9Tt/+D2A5jheFKjd01RfeSaIcTqEDrjNDO9OWYYKW5mYKTiZjOM45a2CY1G0U9Ws/BmYgYyX0DWVAtaeIk+fTT1UuEIBdL8InCa7o14ERNTICMRKhB1JKnMNCGhcMZoSU+WwpcW62mFzH1753S9/QNq+/UNH6kizC3pcmggSdwjdTsXyq1ubmYzKy7qbEWQald+qN8Eb1JK7WD6zaVPmnNGmKMHhvUR8norauI8V7XBh38cfQxMRYxIjFAXhARZ2SIAK/o5mp/Nq1c22qjim6JNNGVuqMv5UyZckl0m/9ds3H1x23jhv+ooh6jpCDhUVZWLo0PmCnRVlqKzW+ZRFI/c3WkZrZm2qsN2ECDCajuGqvOvyqhBC9w5ZaLgOMFOjF/xzBBwH6Bg7CWB0D/Eo6h4vgYGxzz5yplYvc5xwkZiVcSJn+4t2o2g6zMj37993zrH0ElP0ZV6XlcDTMXUE3RfyNL8j0alpI0WbKVBN0Dy5uf3CVB12d70pecO0ltAcwXABO2RGFqgfen50Pl4FfVEOe2Ogc5IggA0/wByAIah/gCcwhE2MPztTnqvnObwzmcnBT1vr09x6Xaq47NLq+CDTYZKcxBomSUbAMTW10e7fz0EUn3Grtm4a6uRC9WTm8/znzebW9sxyh3bzGW/Yo4Zl9XhCV619p+eh7dcoRWmQ2A6LooA79HQypEbXSJKkSRhFihijm3gaAJppNctV0zVww2zUizjbaUyx+HSVYdzcKsN08GTR9HFOCb6491PH0P3ktFs4k6+KoiuJOxsnTmiTKl8or820Sq4o0RAaqjoM4SvzZg/4sKD+uveFsD+nHX6BCEHHkCSZPCCs12GoJQLDwh78ze/xJFatG53aMzmfJaoTLF5psAQurbI+jnE+3sFZ9sjaDLXhRZWo/VKkLC/IiqXtvUdtnB7WpB2htLV2T2ulNKlZVpaXZcubt0VJOIDaDXNgnve+yCb2h/3wIZ0zEigZNDjdY4wsua+PkQOB7zs7PoYVa5JUe2a1WKyslFncqEwzODct4bjP4RiCxvvqFLC6Y+jY8wq6AtFStmRPfj779K7WTslGlled2j1n7rmVh6NSXkfVVp03BM2iho/0M0XPz87Ox6j9r1aEAwTd48ZoLBz9k4iewST4/I6MYYpYytdue/7l5bHmlN+R8coKmVsxyQ6LI2jFj763k/GDKUDPD8Qiw0ojifghEco7O+X857zhOJm5nfznzlzp2ruvLtiCJaOtnbZEi0oJskfHsOiA7XhqmEYz4S1saN0j5aNXQB+0QsY3Rwsj+vhzdsaySB5eNqm1lp99/pHlKdlQMMZ2MzmuQwIz5isQ4Xjk6G4tKoYmI2eTQ9AGzEPU6RNe84wjW5YqzfGWJm+e3CycHFsrNATa0wVLFT3VsyzXNYEVhJjRiKYS0A+rs7MLNNzDEDVgBxVDp3Fl5CyK2MEI1PoiK+X39qRW2a5Mv/LoI2Mtm8VJuWbXzYAZ41hwdk9kjD8gbHidIgn4hzR0/a43evqULoqmIpsFnna3tspU1qLt7e0pW1ZNiTepNpVKmbyjCkGmDytSNKxk1C9sLHymwxApjIK/hyaUJLSDZ4Huly8S7M6cVKk4lQrXenF5rLoy7bqd1uI0G2Z1hcN8MqYGi6Dtuz66s0geZvZ2r5/f9VJDim+Irr3JNzcXTVHf0QW+NDGz9eq0IRjDQ4I+lFIlS1Y9uvsmRSLRZRO8WW2hHU0G0gHsoXMGBwEUNUqyD/go/N6ajiQ7SmbPcVnOqbMmt/xxoUp0+JWXl594GaCRSJSz8H2KodXnXrrh8TsOeps+dZpq7wphxPv81t3XblV5t1prSoZrZs3a1S9MSC78x2WF5i2hLXiaHuDRx6EL+D0dDr0n6dQ/KIQGaoaBHgYgxIe6w0qDRUOYA3dsb66kmCYptlo5Fso99Y7Mcnz1GfB0KB9XWOyIOoUwawr+8k03fHrnOBmms6Chdr+GFU33JoLslLcaHG+yTTslKaSqV+3G2gsl2fNUecGxdM0ULEPWUzQdHya3BWNhwdKDCYid2yeADjgJxDx4Vo3EhqBhuVVV35fcjGS33EqrwKssJ4n2SqvYhQY/c1hX+6H12dZE/aJi5bar7n/qziITuj/VPg2hPT+0z/kkq4qfyUpZtnSSFOw5KqVPrRWqOmXx7aEBSk8tCIIjCamIOqUps7JKy/tAIWiiYaAudLqYHvyPQtDGzFpeYgksV15nqnUuN1XhJInnl16MoNGixoNRP7o+K73qo4e1O368/92f7kTUJPiYgujuFlu70CRJi7NGs2qYpiWXm67qOrWp7YKtigKkdks3KF2VWq0dqotFa3RWVwWLChnRBRYpgj4GwQ3M/wN6+tGb3KWcWzHLlWSxIa08UurUGyfZEabOoe8zCLGD+QzWrwDa8aEF5pevu/zNu1768s6HSf0db3T3VFByRsyRUrwFZaWc1fGFRpUkLd10J8rb95QtyzSlrGRKLvxFc0PvQlOyZZiqCTtddOYMioexpxkC0A/q7NBM6+VS45Y0wWXyOSZdGXv5kZVyeWuHHSGkChbmdUBmYX0fwq6UAmjm1VfeuPbW2vprr3/y0MfC1+9cHyCTfc5OLVgkKWcMmhwoNxRaG1AWGwOVie2xnDKkGwpMSK0zZUpSiK1L+qRiTVIWEB6hGPoW5tjgsQuPoQbag/oH6ul1w15Ppxneqbu2tDwzVVpcrnEqS8gs3s3oRZbh0KBfzCuP+gF97sbH7r52hfSXnrzxxo9W2ehbevhVBi4E7UGngFP9Ut2UTUoo86Ys1TbXXpXtUmHPha3T8AaEhkWDQzVdUBVhwVIp5F5yAFmsiwI7B2DTTBpY/71i6JbdGa8W07Ip5jjuw1efH6uNT9sjjMRVuF5iIzpgB5/ciKWPVysEgiZue+zuNzjSMmWudPPb7z5YJA8W2Qf+ouRsXpwGwjDeSy5LwSJ0RXqbXCSiICroKgVRM4xShopWQSjJ2hS/rQ6xsIrVy1htY9dQMUq3sVLX+HkISkGjUlCxJ29ePYjg3X/AdxLburLrx5PNZCi77P7mfWfmyZuwy291J0Rfma0V7JpnzOrtpk+nSvDe5bOT+0wnCOq10LOAa1GKeqV+eDm0eh0YlQXHxHA8Y4CLBUk8bP4DGoSqDZRwPXwJ5y2M2OWt6x6nrKorI93x8cjAYMLG9zAhNvKrLrJYCL0HIu37JKkbWtC7fffgi2N4VGWOtnUBHQ1BUau26ysmLNsmSq50adDY+/HZZq1YnJ3eUFeMXMnJGRVDyeUrG/TZeqkYboMKhDYpfhquQwE0lqW40H9GGsTKHBb+R82ANTXMrcdrt7SwRTwyiXjAJn+6mSRBbuRqhkJ1H1vaPAlj3gHoQCE0YBysG7l8ZvXz3VgBYPgCQW/icBeY4RDYxqUSrdKgRLMvD7Q9u9y+v8XzTaPezdWVLFWKsIrRS8UgW5kyNkRDBSZwpGTUxuLpdBRjoP4DeXwxaDqNoMU17uevWRzLOzaqGk21PJyqB2R0H+NjH4dWZpTwBFEHExrW5HaEkWaIYj9c1zF/uvHVnS8Q7kgR9FiKn79/Ta/BHScwG47eHAz2Pe3rul439huVipHdr2X9eik3UZkdoyZ/dn4qJu8UuHAK6EW1ZLxRUPMBmtsN3LwwD5ve49vYATPXs7DNSWoozNXWL9AIksClHDFfpDoW0AEUHCmm0IZyH2x8/+ILXgg9kRzOdAXuNMzp2QNVlvV1eqPWqA0KHVsrXqIGrPJ6ltha3a9AUiiCc8icVEaPKIoxYB7h/ie0FMz3g5ScJ70H5rzXlxPvbgdcOB0nyw02NvFuT2OpITUOGIRaDhDygZnRTiazXiQ6I0RZNhSbX/3pLWWKYBbQE+FfHmU8uJUnt6YL+g0eeE7W/lqdM83Bxy3zgb6iTpWs4XBbtxyPTuXoipHVB5NTJL4fgMGB2kss4v0b9OIDwDUpqFtcfdyAfbrTkh5dsXA4103XIHBNybKcSshyi/o/PTswYyRSH1b0YBIThvcIaBFyiou/WDfcO7Pp1XE1CRKRTo6kFHOnX3dnjOygYdu1xg3bqh2tDj5/HMxpdcPiTdu+1NR4Y8s23tenuuI15NOnT0MVuaRrRkBJUYFfEgOSlePz7/jR/I8uFk8grelbu9T9WmJXR31zxkoIuVZVF9CyOGQ4W9QV8ClEXQTYKQGdChhFjO4A6DDRfUQXGph+Z/OJQ8cIYYdnf0Geghdnn3QnkskAMjo7faDIwZz0567APx8xue47JfD++aZ99Pr1z3O0262AcnSqCAM6VmylYA0buCyqReDXRBe7hWl/v40lOa9JklvdsY2ERl6TeyZNjCVJrM8xxtRliPooBQJ4DSOCJ99kzmVcAS1WsoVC6uWNnz5chCryiFmpzNzsnp5aAa5dYJc/793brpZpUOg3dHvvvjlz3tdvlLk2vXef6RU8hsZaAB1JcC+hJeOetjmjzRbR0nI+yyXp3vZtXHh4yhNuw5eBdQgNQhp3VeK6CD4U0C0WEAKwb9afy/TDyU6GhuYXbNI/s+rVob5AhKdHSm6mW5mpJEdm1m8PGhyKVmWPBU2MO8+emfNmp1z4ah0wO2at7aGxxuZoMjaG+29o1fvW1zQpznScd6qqtGbPFaqlpYTFWq7lIsE6YgalVaam5fAzWe2pEGmY4/KjTCaCRhSzyL+MHzBgxtzWtc0nzp/qEwIFw4c3Zx4qIS8ShRkYCOJYtl4uUISahJj24Ojek4VtVlUrH93W95ts2ViTI/0z9Mpfjp9Ku+Y74qnQw9YBvXVNjh+5yrFGiOWqkqalR9DQWShEsOuqrljcAPpchk8KUUSHwUARNWKYIYTtB1e/n91NZmduPpyZAtYFQn4+W2sQSh2nWm2WG+19W556N258bbRp4ESVTBQeMJpj6NhS+stQYKY+fbrLCvlJWZOs3pojD3a4ErdcOR7nrfTi+xw0jMmMqwkXpgJAr89keLSfM4qGNamfYgxD3mc19fLt1Wff+Idzy6JMGM8BSApdnytz2CCm5yyu8wYffC68q5aDmuXo8H2/1/dEP7Y0c+yPzKoad689NlkI3QJo2Tt25LLaaWlOD6D7Ll7C0+EfnJxLq9NAFMezcHZZdCUEF0JnoaCjK0FRDgrONBmRqgiCCo6PLjSoYKwKvjepr7R48YLoaHRhrCQoipKNIhEv6ELET+DCvZ/BM0nbWL0+/52bTKe9pb/8z5k5mQtXAjZZQWMR+/H68wp6YTsIzKkuWdFp7LW3Ru0ij66sWvH2y+gvTLVtCN/pNNuRCponk5st1R10B2+y/oZDKpxNOotKSLqQ4s/fQKN+z0yESuFkXGZ3cgNTWz56lNC5x4kIOmDrojVvVUMDYRrxAlJByw1XbrUqMzC+x5ZQfGDzELpVzKj4maeSonds9YtNMAau8h/V6eDBUzMPr0Yvk+13Bldn1Qz+J4r+w65aVBltPq0WQv+XXEltX6gs8vsCZ/2XaitC2+mRfsGLLtigPBmK+Rb5VkBFQLHjeQ2c6vD7mNk7b5SVTMvzyuWs9gTDO85bw6QVyFDh5B4/Wbnr3k5YOKVrCI0fFHdndat4fyce3N03q7pKH7jTbVKKFeFP+i9mLhwqqaPmFBM96sBFWkLnlzNi59kQbBIqNQ80D4D7lBgJA22cvo8TWdIwolSL9g97zkG8Lb9wNvWAFgU1I5CdW/p1CzQRc6QxNKUiT65+PvnpBq7TRXSzyB/d7QtKG/NBL/47UKfuCe5wwRwnuhQw53hqhy/J1oxAkd/KbK5Ynro20VHwIzQVAgKYZHZDl9CXa+gGSG3cH2+30mYTgjjd9uqFBD/0RknQjLct+3CU0wn0+Y45URTk1z/Pdr3hJeVFg34yM4gFXTQ/9JT+TM9812Bj/XpjxsGnaZ4Qe+uczCW9ldtKMByx7aCCxrc5pILmAqkpGQnapA1mMt+Is3dSGU2oFBJ740sAIIK2KnR69nWqgyCY7MV7r5ed2u+X2DhyvtOYxLA+efJqMDxZqOGspvEAqvCu6qRaaPRfQbNJOvuM+K4xnHa1g9Tu1cKyh5cz14Fjx0E5jLk9bUcj6PG6Tn1EoHWogyayZaB7mNM9PJeieYyDRgY5ECKOwY+P+zoTTc+j9e4cDW+dOnhUmIK+8axTu9mKwv7sgYehiLuhJ24GFKnx136Etpb8k9MgGPjM9Jjo5oBnPuyBvPH4kevwc0cLiZeH8X4eKq9itg21iWwB5PtY18RrEZSpvftmT9I4TXgYgvmCtAkCkTWAFKCFRixPTqCNd1TeWv/qHsexZ3vxVVJhNYeS3uweUCAHKqaqoKXV1LxM6aRKshYv+V5/gHY4t4CP+ftCCctK/fyRHL6eSxm/cjyzLRSTd3gux8U8I1y4QpDpWU0TX0yg61s40DEhFEAgay6JkJRQDZI0sAvfFfO0Ic+ueHd4PUZ5e+8iQse13iVCsv4g7keDImxGbWSepyZE6MUL6sevoRdjY8LFNh4RPcbC2M/kgzTfmrK5dOeq1HfKVHiQDTNJRkUsCI7U2JmCjokoodOnp0/3xszmKHMphO/r2EdOTlCaSuOU9EktuzFY83Rd/mDbslcX8rr2ae6TUlwaEHpzNlZ5GNMmLTUNbaJ7QcW7ZBp6KtfL1He5hW0inWCYyx05t1i/GzE2t3HD/WqO5xl7cEeDSWiHCADhMseaprY1Eb5t105Xu3QmCmmYhaEGLqUgpILWhsr3SL2bR4j4uPk2ODbdv3R9xl1WQTdmkoBEdwhVV4NoRheY8yOzp6AXLDDMCGyOePilLM4Z5989L7LlFsuTDDDSBxGm8uu1/Wq+e+Bb7P4cx3RgJrKRvlYNzaU5X8C7rDOV+9goABcgfBSn4+Vd0vKd3MR3HRO7n29+DjZKXrq89MUet/J6qGLIr5I47rZj1Ve0MRGpua2xxxX4/FqCsrhrCYZ9DI3l5tq4c8US/qDPXSUtawZdX/zgSppxM8HnFtNJIrAnXLOiT4uV6NqFEuURzt7HRhsTBpmD4NXNvT2WpLo6C4RFMWKshuenK2gvsv3eqhObXBvtHhYqFndEDAMFMFQmdBZWZd4oxu3a6dLlXz4Q9RsjZxjTOBnG8TUpH9SLGhNN0MxNVvWibzUXRD3QVWI7Sicyx6Gxdg5Fb0LPW4TO02yQqQll3G0IaqJQxJl0sqxBPQ8XL5i5xJn7Yvxg4jdjjIl6d9EPRr9p4r8dcwME/RfKS9kafu/zvM/7vE/biQHbtesdQDvylKqGKlmAXF09sLhYBXRX3iivsq4ZVa2KyENF1fbs9n8Va/ia0F1gYeHY4PU7frEdWvVLNmy7ih9NiQvJmwS7MfkmUHzWwQdGtYD/TSk34z9RbvPNvnllYHGxo6ktJ0cIxJjeLrqploMeRXApSiPQO91Au3I5xVUQbUeXRnDbheqJz0k5FXFoxJBYl/q75BcFsblwceGrxdj4q2GT6balYVpWdKzsP7BrILQHtqDbJKG9ubRne5KHAo6l32yzp0VRm7jv82C+upqbebva5svlJKl/rQ81lw4s15C2NmesA669mKHmUUEkgth6BGKL66pR9jhST2TVohKfU9Si4F/M2o4tWargb65SXVvU//zegOY/THZOH2hzkIXdox9wyP+Q6NsKSA5yXez9A3VoKcfaLxZ8qpk6+P6GuZnttVb7FstZ+DegQQ3/7kAhxw4Z/auT/w+aFVhYmt4BLRaDxQJCP+SRTmRZSdemRY9atAypLAj8szWpJV1v7JrsqsAa7dAY0tCuOvIeIwHJgepAw9SBlgFyZOCQkwtoJbwRY9xv57FTQ89WCivG6kwuWw70F3wdKCbCznYu6jeyubXF7GJB+G9oWlFomHmXhBxXS9SPs4VZXaHjS3OiSy2tqy715En+U0Tv/YQAZ+AHO/biN18+EFUEjIRGJwGovmHGt/HsTKXd6TV/1d8cLnMDh4x2EWGlKrEsgmAf7iExCvPS1H3va0pgvmBaRtbnq9cRkNZmi8XF2Q4EDivt2k6ym5kILCsQ9MZOqTnFMnVCQ9VqnpdpZeK4sCoZtWJqYqyUeb8WaPfsB626DOzDtw998dvFiyGWdQHRfgNQd6T8IgYKGjauoYkNatfEQKcWCAiqKdnQBwJ9V98yuTmSzQqoSzx/dmxE0ldRp/U5ifc1k3xNCEjzqlHLTX3uoqntW1MOM4BFAfy7oaVCWaXTNbSULAtoJc5PwBAla2IuLkaVVEVl/w3a42yY2iVAt0tD7w698NvFC8H913WC4PwErmo2qF1LA501QRWQChiAbhcG+/pPna4WspMja+WR9x/7NSSPFiR1MtBxBS7ulyyfdk94pXdlfHruo52Wplpk8yCCcU6L2rHdXM1ZmNprcZq2AmymqJnCKM+Kpd6ima6IMlE0CdB7SwS0KEaHhgD92/fB/Zb1rATohgJVgXUOKktYnjlpQUBiEciQ7tZqM6dmN3ObhezTgdBvf/15fvTnwWqp75Zbrv5kVAh9cGvXkeUHl8fDqb0tDVQic6xC20anmmo4v5VQbqYpoqvSDE0yGVM2KxOlpYypENoqxYiRM8T9Vm6iwZmKEIWlvzl38ftjTtLb4g1Nl4Ck1uyXDZiGipxNGBu+LV53BJXFZXb7trDBN9fWCuuBkdl+18t4duaGP2q6jiXLlb4f3im++OCDy089ePu6GVb3sTTNCaKqNHzd2Rz8uqFpvXAzjYa8VmBpaSVvpSvhacsy7BOylRJXKQK6KUBsodS/WcVU4ooi29AXLl4AdEP1V7R2lacF2vZrF8JeQFCsMw/q/0C3XX0VrsEe7X765NrpQnbF7/rMfjJsNBP+PCrgnuzM2HBPz/2VNT7Er5suF7VdAPLiG6IVjsDMe0nMFQLOSC/lo3EpMTHHa3wG87aTvcipvKSbgK7HgjoM2h408UVYBMe4Yoq0co8NfeHCMec40kKIeHaIALqBLPraGv2jJweOb0GLrgMdePz52qPXPvzIw0eza4lNI/LXnze8XFg//0t4/f6eXj5XZmO6mcgXeTDvgGZo71aLUzCgqb1F62q1ShDErY0JPjO+PrUhh3lTBKINLefTuiYi/u3WzUAWFUXROJWjI3cP3f3COUDT+8ijinLD8MhDPFvJfyk5cA9re4QUENEfbVfgmqaRTZw6+urr86dmtNT5d47xmYN3H8dN2SlVwB2r47rhY9WSUHbttDNDNcwcI9Q+IpkapZYVboxfCoeV2YSpj01l4rQDzcoWp0/UmtCkSQBkRREVlqTNNEdzDvS5c63QBGeAmom/A90c8J4tf0kNDBx2oDGjodTScXX/SPHs1Eo+u5lbefhUgefv6+q66cN3UPIWSuGN0KNiCUmyWRrPW4DeJa+XwMwKtZ/o4MooxcVPJkpRQkq1+YRESoePc7QjTlNpbolXtlmaqhPTogJoNIN0ENBdgP72IqBb1fo+G18VVaA6xJBzGNBpGxoNlyTgSkhb3+BatiivjOf4WyfyxenDPUt6/ljnQBrBG5VjbezTs8FCPp+zsEgB9G4pYCbUviLxcT5XLOrzFksUuXhiHpgT02XRwVPT2KfyAoZxqwikQCza6DVatqHfBfT5X36kb94nRQI0Gp6G6tAkPpDsQdiwHSHQ4Wvzr6lt5ZnE+PJjH787fDxjFVFO1DsHUm0dV1yHx3snjfvGzgYle0GzG9pLARgDen87y+nwdA0BzAqpBUnhxERCpWNzc5XqKoFFNdCYo3ljy2IUtYXMiqIiwtUb0CLF3Tp09ze/nT//I+D2hhYUFQES1PitCW12Jj8Q60NeRUIXjbvaVt9+6sjC0KHp3pl0X1Eb7Ct2duodt9g62o0acm3qo5CMpftuS3MKcczs9m71gtvLeNFuyv0KH65ZGRNTTkmJSeVVj2ctoVLmcFhPq2UZhmZkLSZnJLDV5YxqDkbmmp0ZZGIKRQ4C+twf539s6dBmqyFAA3dnt0QPJbs4Gm6PmRX70Cu4SXXlxMQ9wz3V3mKu+2Tu9Olc58B89x2XX35Hd/fX/bf0+Q0tFY77d1kawBxnk1JAhdmxZ7BrdQWZ/1SmvdXpIMUVBGDQKIrOJnLEXNBJpUQkqyabEk2JmWgd2jEsER1kuhVaiVHkNkBfAHRLnrA7DxIFWHqXK8DSN0moFfsFu5DECqXBhxfHDy8snLnpqd4jJx964+RDz536bmD8LnxCGZ40f/jro/398Ag1PZbaDo2gzYm0F8gMaJ2dww1RW5L59w+GQ5TXmhtVrCqHF6OeYJ1IFFLaQokiJYtYpVqMYSjCh8BQd2Ugy9qG3IoEaE6uQ5/76Y/zzH6DSZRVQjnUhDgxzElvAH1oteOatgOiarAox1XzuVq660zPTS8OD+OjJB5KvD7y0FcDvXcC+pLLur8+PXna8HlcioeEmtDgIhidBI0WOfC2sSEYXknl47HlBZmiC2N6qkacN6InehNSKdwzSnnpCl8xo5rMUHQuSGgCZPg1kPnHgttjY4hwMkPuc6B/+oXZC9jeOFkmjnvL6RJO5qrf8WJ8mPxwBsVOURw8jfmqjKzWnMmtvJdMLh95/K3nnjwxW1g9M3DkDjzecekVHT6k5qafFUR0XQs0zXEIYN4dora40XKT9JRGU9zyB8jVipYyEW90l/ZGQn15okejwL80FnFzZpW4+WMYwQ5ysMKnNx57hdoBTaKAPnz7f1uaRBHobWiLH3vneMa4xdHgV8nOtAj8vs1TX4+U/aKpBXx9hfGB5MLCk8+89MaTIyNGT2eXr142RvoqhsxR7uZt0EQkInLQXaIdZohoU6MEZuXODMuKUjSDaT3qrru8NZtQI1M9JmJyRS6lPvPikFQ8xjKcEoGRKyGaiUa57ShRQFNkqevWF2xo+EtDzL9AYxJFPVbT80W9a0nf7LZvDDOWX7z7LOdp93esWmVZjGsCcbkOlHETy/LCLD4+6YRk6fpwWLRL7ltF/3bY2Q59bnfdjTnI7a3/5t7a7C+mwR7kKxHnX4ud6Ylxwny8xijhGF7DcDWxmNC0yv1BzjRN98sfjRLCEDN/NhaJmaNjmgIMIkfupexTO/oHmpk6ePABFBF+wVyxpxCoY4pL1jRVqpWKx8OZylImcJ0xdfCFCz/9Qli/VKiKAlayNOQpVzuTZ6bLk5urs5Kq3588I/t8yE+P9gFbbEcB2C5XXO+24QhHOMbtvt59vSO7gcNNkaA+Jjs+TrjQocOEUjK67GVkXvZez2hRupiI06OH42ZEIwx374bGuBlSGg1ppUqQcToyErk3gsYWNBqUzDEhLzN1+23PfvvHH/tAQ8QMmvGaJmm6XigUwktDL/bgUthtLzxw7o9fRKlcNaTqjN9nlznZjoeeQD08v2oMBrJVQzyS/NDAw/+4ORJ/Rj0V5TLAu9wQgZWJG6wMdg65F7u6zRl7L/N6xPZxe+bh5K/uYSh1RbP9Q+E5b9BkgisJ1ZueCjKIYF7us5c3Ulwkmtk4e4zB+RzWKPnsRwaNeq/iWAP61tseuLgN2rsDn8hWeGliXc8jhyzUcDm0yPMLE4IrcvCBLy78dFFD2cSqne4+intfr7z6jrtee2Ih+VW+jMvxM4sBMpXsNJ2iL3yaxSUsFERRAgc0YyN7YebrWw3tbZiackfGgAxRiEmc1yt/1cVQ1XF4NgRb12JatJgIhfT7Y7LpvvHGyGcvR/hUaioTjm6dD+eKMm5AQ/VORUNWmBDcu+u+x2BpO3rvWNRH/6brzIMap6M4Xh0YndHxGEcdj7K2pNqlhUUBlUIoK0FYQiVWqBqI2TVK7BqMsgGPhhLTkVJw2cVrV1bc4tgVLIKKHZx6tCg4630fo9ZjxgGBWcdx1L/9pq2r/uEjhE4hJZ/fe7/3e7/3OyLzblSS1MzMVG/V3JQSPxJfWZldWdphPae4zhgNab2n9a0fMwWlscWhtgexqNbYGeW0ysd944GKibFG5KVHG1tSgQoZIVypqSAb0YEc2f5CE5A5N/iyNwP550e+LrtTXt6wfALITuNNsWGAuGh2ks+6dnMsqMnugplIXKRqumSn2aKLJCulJoKiHJFQdvliBP9qGqj/FKsomhmC8Nrne34zoJ05AXFO3JS3NxIZiUxNUZFge+9EEoPB4fDSnsZm1EtrU/eeJ+xXbnt/bU1efLGp7dNBVyXW/7u6S7qvHepFP1tp6drSNRpv1gIexciulsIPGklio0IXFpuy/gvMRTlV/0fTRiV2T8gwegPZ7YTOIOxL9cRFK1NyDPW7gNB6KaLoopneCcJbE0PAwYmckhA4xivwE1o6D20m2IvMHPmP0wA9J5pZJ6CRwjOgC/KRx/FoeOaOYNCfUuSx4O7d23tr7VUpWUmljhxJrezdtXPn4lKsvrW19cejLMpgx+B+8FqtCDSLpxsxbb4hxjY+0Tg1e3vM49GKrSXWQrRTpcWGgZfW7WkzuaG9/xOYNkHJhp/r4txEkRkCCNqzjXCOHTmBXTzSQiiqUxDMF83M+f0THX6O42IpjiVsnJJO+CdSuiWvaVLE9+pxRVuA7WaLutxmr6Mn58iMAgWrUfwobPgZWk3w7MpUJDjXu71hvOpQgxrvovkCZ1fL0OKSHIvJ9ivtnZtrzktOtXbvaCtEGmELcnalCIUDgZrGq5f2DN3w4s6hFzwz3SgPOG10xzDqumcHbMIEpLxt53/+82Vgx/yE0dg6oW0cEAPaYS6iBPiCI7MaxdkIVTNPj9JK7Xic11Iyr5uLdCVtS3uDgm4BJgiLRLfZrBtkluwXDrOTLXJ3FVHb5jsza5t/EDn1G2fEwAytKMBaXBnDBNdxLMnp9fMyW+BMcMer/paC/tbO99eONVvPPqcQWbLiYjRZgJ6ho57A223d3Q/iIRG79nk8cyWnY1AHEVkj29jY1O1yVfpMBhy+/8ubexN6dVKsO6fkvP1DJE+9mZhCq9QVn56Ri2yWtKrMjMbEiXbKLxNFom4jJNFmszG9Sd0CQEN4fIRlPf8BQDY0TRvcfkc92unfMjnofHPGeKmRgTlqdmyqt+almobeBk+qxclotEoTgM6GL4B+LQv92AVnWK2YjwPbdSLv444r8kvX1GDS14s7rn/89Zs9nu0IZPZgqvyeOqSUzi5BEdWZYE75/5U95Y98NSfkhGH++Vs9Dv2aWYeT4meV+KzCWCwEl9g9qqpVtQMMr/DH1m08A2YxPhHlLEW2oiIbXLUZ0Ktpyz+KtrFxMyuY/Vc67EfXstB5IQiCnkhGazuouKJRfn/HC4naACUIqfhhjCNuQVNruuiE0jprkzcLzRpt0BO3FxAEunJIUuCi9tqq5tKWlr0Hb3rg8Rcqbnlw0AfuEtj4edaSc89BYGaSGZlhObfTac5LFs0AJ3SR83PAP66dnGgVA2ZxTpIZPhZ2W2iF5XR9alTjgtW/dDKk9ovEybh1mpaC6jqgDUmzNmCukvkPMg6zLhNuBdCt8N7ocOT9G5AR17jNeoJqaVwMt5TGxiuojkBQ4LK5JtMZsObzYMVIZmMVcv/mMRFRlimmOI2kJpKs7Iwz3nFpR9urhwcmBkYemqvdtqD4Ks8qKcG+Adi+zAWHt+NFE0pG04QYy8s0z8JD45/i4900Q9OcM72cq5N57JyqBc+Ame1NcQQd1kSOYzXeJk32xfRg+3JifuGXowvhkM7JIZs0IqytWrLQbs4GesRkeWTjo9zoRfDicllnJ5qsP9IGcL4CgdxN080YpCrF7VR7ElUBr+g+z2ot3HLeOdhf24rhLhh5wg7oP1i0wU6WBXSBqbBwCxumqYrAC9joaC4YrJ4K1pcd/cN3YiU2FrziRGw2eaHLh1UsphgjMgyjxTVFVYFNazQdng6vrMgh0VbERYvykmO32XDD6gvzurw9ZRNX4jRPQI2xFf9Y3xQ/ErRZxIHl/v6w359IHltPjqgLSUDjMlE34NOreI3D+Ib5kyzDhbz1/f0ZAzpvYVkh+JjY/ETMbWRtioIeeiIwf3VbpQtrOFwuV8kFpi1GW0osO3oALWKMxsxmM7cEGvvbdy7tfCnwQt/w9EMHZybGIv4rN/7wnXbmha7zYeDdbftvu8/Y1NyJSot+LS5jEeFC7+H2OSg/RtMyzUo8FP83d+5uOWph42hSmPMKSkpkOQIwTkqb2qcMVPfiNekVaELW4v0LSeqOgeXk+iq5tdwChVuAvQprPy682axwFn9Zf+fRP9agaQicIv7MzElowmNdADPD+8975PnAo4NPu0rOrTzxNBhq8XmXZDva6rbWnsxa6ASUksiIMO1TMY947zAeFRyomBret29qpm9u5PlDnWsiJo5iFe2eV3ft2n8XniN7OdppkYCZoZ9gFpHXfGJpr6LQqqasyLJCC5Is8Swrctkqn9UD6/hxY+NHysuFpxhcp+P2o5K4e4pRayagVZs4oMqiGKkZkJINflWNJn9ZziSj65wOL/Zv6DRjIeiQmLDbe35b+zc0R7NpfOc69sSWwohn1hsYqWvDfjsnnnyF61wAbCktdRZcJGQj2BD4S1ci1MrQzl0PDfdNtjfMHQp4JoaHh2+69oZre7c5+tdCdRjWP3zrrmexRMvYUPVMkybHVFlQeJ5HYMG2YMLZIm6aNYtuVpajPC+piizLPF7wtDa7c2j2DkBveBNmzZsq53XOZtNVPTY5oyUaBsBi1oQBlbD4PdWK2kCJBkWaW09KyaSaTK5l2XPMEmMhQxYugTp9dHNtLY1LUag6y/BulgFykRGqmEpclfs8B6cD7ZVPG2EmNr1Bi2uCptHzT+agt5xR0n2wt7d33/Do9Nz29vb27S8FPH3TM2MHHxqejjjsPZno0tDhJ2JLt+5/0IfqgQ37TLSsqP6ELKuUPxUem9o7G5tFboBzc+gAhyTCSYq0m2NoTZ6lesMxiOM1QM8J6GX4kwwJTfNRVgvPaKFaL2CUI2xaVdN+TxVBNXhFMONNnTMwyfVoUlhezkSZVTKtR7kikbTYfrH39CBdtKabiTQH58Lh1HjqOWeXZHv9LpBOex6aDnRcsb/7XCvGMUBswrkYPaVorb1seW29CQu1Do6NzYxN74vs3j05h0cwBgL+mEqnwuHJYG3ttg0h9sQTNNvsRBAKX2fdg+AkK9AtL+PWp6gw1R5BxKvJAhQulZPoB7vhfbv2Ht7LaqoSVe0G9AhFKdHlei/DkzaJ1kKRGYavpmy2mIxKblETC54qc+rQfCgHvarjBNFX8Us9FBV+Wf7FmyTXubRleZvDvgFoSaXmFQnUrBOolyN9iXCx8kL43IM199/kGR886EIeoNAEW0bDjIkkhecpNdvKjm5GD+6biIwNz4Rn+vqq25ELHZ+cCAQUekW+vUuZTIzUd64xW+DhsxmY5sYdvgOXn5yFhrcyBNrlnRytMQw4EpRfSyb9UQFmTYr80mLL7bRMMjbGDvN+/46JyEQCo0UT3qRFSGkytY9hqhO6poDRoG6oqIouXDkh5aD5dA7aYkBDcBaXM5uby8tq/1OO1o3NTUPTOrKSCHatLgis+HwEjKA/ZajjzjurJ15dabMau86eV1Bq7UYn8nzXjoMN9vqja9KuyWBDx5G9i8rYZEfH+Nz4S8pAIBCJH8b6vsNzCPh61mhUCWsTVjG5XJef9CfERGShgU0YyRNDaCecJqezIiMlVRVKp6ixsTH4dTUa4kJM2fs//tjfLkgUIwnB+XnS1u8P2fx9DF1NaVqeLu2/ZluZHXN0AQhCNGw5WbdZcvQWbaDmuiuFqL58peHINjdR2/Xm5tITzKXdaIfREBdjelXxuWedfZ7mGd45XtYSi1nRYJ2PWQWuyspByKdDtXb7Qibqe/DVeFyTlZVUPD7gmXvBMzkXCPRhJ0rfgStujlTV12d+PzB44PXXH7j88gd++OGHLyEmIOcEfUwiq3aGALSb0IvQRYAbSuuiRgE54Q36tahAAfr9nu0Kq4lRyS8lhNQy/LI/wki9KhOy5SVxyOG458d6NZ0tguNvr6f/hmZUR8MAk7YI1Q7De29GM5lfklEECs2lzcXFcM6ldSWo0WeWWGc9kRXPuFu+fclXefkDr3/yCVaNVhr7Er1a67C/lkl+WtLCiIrScjvm7e3+cOqQZ26xIvDkAwfw9Imv3r27qrc+M/vJVw/gSdgPfGUQf/v5Kw+bgJuL/BCMmfPQFouZ1AkD2tBMuU6SbtCLPBeiJUC//37r9hlVTSUALVV5UTKAlsNVCSmHBKiE56mn3towoC1GVc7ZNKJvPQ+tu6Vazx0WC6DLso7MMDKOl6KCkExKyAM2F5dAo0gLnKN45qOeQ110y+GhBwexx7urG4/KgPj219rrF9aiT9BMbHHv4RYj1Zv6cKwmcGTqUMfog/t9Lt8n7zxZGwwuqzfddP+BZ5977rnbXnn30d137xs1HWcmCAD/DW0hSXORTV/HK50j8YYhLAjKowZ0WS+jTqUi8oA/lchr2quO14MoL/6KoH3b+/XoWxIWG0fmrrdZyNX8n7jd0vOBWryXhAve2PxjrRlTQ5HKKijt4lbXJSma1DSZuboRDbLiCSqeS5duHTywf+f+QVc3CgOttdXqevCOsp7X1pIKzdfVYQEeflOyt2KyJrD0UEftyEO79ldWXn7bLQ0jwaPLu42n0F0/Orf9juqG2t3evpym3XqOGaccdFonEWlARWmSTCMsskDSBrQtaUCPDJh1LRVPehk/Sb6GmhuO9M4foqT0cfO+tKys9QuHwBnXRaHpnKTX89Cc/jf0U/aspnfAO5V0l6CpOrXgBCMOdyMijocTqhL2VM16Lm178YrKbh+2N9n14Dcl51pdvsEHdzpae17bjHJGyn5xaBc8/uX3ex69JXD/J89cuv2z515/9tnBO9urR+wLv4xdO9w3fNPdo9uRg7nlmd3vZqFJkiTKc2JAA5HjdEu5jVzXOT2vJEgWWgP0r7WUTZekSEpQpbS+LgjlGP8eqIly0VAOSvdeie7Aj7UpzqYnj2WO6el0DvqP7McBmpPuDVShNJPbeuYBvfmzFQshrMC2FhpuDEO6t7ehTzh44NZhz6Epzwu/3zpY2d1tLW3mFUGWF4+MIYHhaG3t34zqZgx2/by4d/BT3+CdFR/fHXj4yzcC33/75bfffvbR5082OBwLmdFHpqb75to7Op5845Z333v3J9PWrbpO6mD+B9piKSfJcpjyjetQc15sOWiLUA9N16YQh6WiSq8QAnRaEPwzvFrlT6cZmQRSOup9od/eudGZ4GzpY8cSQjQZjUrrIY5bPpb9LEZIea8JdNBdXUJtWdkCvDdbWOp0nof8Rx3WmfoGB9u6PwWj64E/f/i84tK+iop+r3c5KjEhzq0TRDqbs+bKstAi18V1MazMNxd3Dz7z5JOBdz97I/DhZ/BY77zyypM1Dsdrmcl3Hn7nvbsf3d7xzhtvPPzOux+ZtuokugT/EoIpL0c5lOMcWrdAynEYJyILvdxqaFrjBJlXmEjY0LSlXAhSdKLaT5EWUqbTFiZEBVrtrZ09A0xWvzjSyf4oI0U3MmvH1lc5KUFTlwbGx5YWB6oAjXZaPnx4aRFbMjW5fHus0HdddyX6vq7Lv/ry5YoK7KvYEFFEEfFTUkjSop4uR4+KQw4YSTLOCVtFL5Vnukobqyf3dQzvfwY9joO7nj3w1Wcf1yD4ztz90ddff/3yd49+eP33H9788pc/mMiryK1by7cSx6G3AhoVOY3CWEWdBq0NWjfuPWSUgL/1/V/fqk4IPMPEBUEMS+Q6fjXvlfzVIYlCHUbzQVsoT4+9daPfiEPNEFz42jWSxYBPowmQNb9AXROo0XjZD+jXNjOZqKwxS7ceGGxqO+Dz+Sq79zR96mvb07gDT9Y4dPFOrFxQmp0QwsyOCEnVMB0m1PrFPT1HM2xzc7OTb7y6qW1ocfHD9uEPJx+aDFTsvvkTRCHfvtNR5ejMTL4M6J9+evfDl994IWy009AzQeAo33oceitHkjqO8tVVQ8tQM07QtAFNLgO6p8Gf5mTez2ghSVKNxjzi1ajaVRtNoVKH4grnrXA4YN4RQOcleTHcGZqJuqa6uqZX94YnLg7cG6eOeBvsPQubaKdXZsauxYMSh6/H05Swj/mtsdsxF7rJd0Xljjs6pHFMmHG6OYzrcxxPgl3nQlK0zNHamdkMNTc3XX145113oad8//fPjHreeO76gOeW97788s3vPnq3psrRkxx95c03P/jgg5c//PzjioQBfdnWnJRnz4AWQ39Rcf6/iZNxHF9M/DP8QV2WIlthnT2H8Hg9ht5MwVQmFyuyanpeRctQnLhLW8Q+UXLo1BndKfilO9TzvBm227qLxN04g9v8cvG7WaIzLosoLkxj/NEf/DzA3eabAp+nbYDX53k/5fO0GxIygRlCgHZX4RmQQQ6ANlE8sXUp7SlgBBbFWSy5FZt3m4Vw2OOBaYUSVpWk27RS3jKBLiju/dDdcLXtluG+G264/ZnZmWKPN3c2Fx+hW9Drho9bOTt+fvb8xcmPJief+QROa8zCpO65j8aLA/qYqyiTfm6rU0NW006YUG/KtqzbudnXz8FvbT4ZOToaWvz+cy10+vTnX3zx81dfHWWdwcrMnU9vQDH23aev3T0VHh29s+sKLwC3oEWEGNLNjjb0nhwSKVPi0a0tOi+EK3o2iXQkuVHjnU9QqiB7Iog4IR4wrzWzhcVyPrGVNnD1Wjd8RjeBVq4hYW8vQH9731vvnQjUYiaq+Ljgwm8AnfeeWNHDcT0uy0mVF1Z0XU8mZ5PZWXsxZHiL/XJ3N3F3CxoWuDjQvewKru7uqKiKeDtd01dShUnvm0+Fvvzsc007evr7n0j9dcjnpDfPvHBh44OXR0ffHH12PvDPuXMEusPcoReRiEymlYJGvVrdg0YYxxh3PA3QBdM0bL4SkzHviDXcop7KxzkfIpmp2HZdlxYGWA+1FTUExk3UgYagu3d4GE5wPCOFPVQUd3dbbDlKjt5NvWc8yY/LcUPWZ8LhQNywyRE/putnF0OBIS2kQwHV3REkbhgcE3BNJHZ3pOG+6Yt3sa+efJ+deeTjj18YWHw8N9D/0K+PvH3kwMY3T3ictHz+jXM3H3ng5IsPvXTGWy6k8gDNOPYEsVIXO93uwLF21IFGJAinty6xZUZZMXQLq1h11BtuN74rnyy7TNjbilXtvFDlQmMslMYc7+9Auys9mATg8IP3wj9l8Dm2J+Eg0OnE1s7ODlaLwkGwrYIwHAjhr6eNcLgAXa/b9pwW9mohu+rez9x3/Q3HAq5gcH3z4k1Qlbwy8N0vGx8fevTQkHS8/903Iuyhu+DUz/ZtF894nNTu+AwMpJkTqUw+73RSFNXF7EEzDDQQFpnLw7shkt7vqA3tAOhll4FkbAV03hIkdx07pHA+b9E+E5gxU5GtQJ3WqDFqgh0bR5fHRgsaioJ7leG+D398a/v5KW2odPCg5SpzywSaLybhSj4SUNUBuTCVmCIJkpCVDWNxILykhcoqSMAxmAf0ga4/dvONz7egH4DhuvF16MGnrzpauC6Qj3nnU96podNfevUKXFgKO2k6wdEURcOdiERdzP+EwNwA3cFs1h37hBBsYIz0cpoNyCLGFSNeM+FAJsgZ3shUfB7kkCSlxkOJYpwKUa4f/gqOZcUOtLpw3JJwHStwhv36H3/kp3NFzTsdU5KucmJ5c2eTTy5mUQyZ2EHUjWLV7nuq8EWKeD6iRSltvgYJQwrmVcu2eEF6+PbDx3KDE8GF3Qc2Ng7c+Kx2/upbhwr+LKv45tlMcTHiZY0C3SKlO9qj7vIzDCxtiaLoZwg0aQAhQO/zvtiCLkfTUY+BHRhblm4IPGPZAZUxMlmfD2FVlUmeGGvk1CUXFR1k9VjHKabsVaXmsm2pTZUnZ1rjc5oXMb28k6PTAN1MFpMmWAW33zAGdZdCigckSR7N4LQ53Wy5rnf49aFJKNiee/T++86M0ACdvLWvty+sVRyNRKakR+SMJxIZ8nqoDJXJACi9T3vQbQE6ILefrqShCWGbn2lDw5ZCerk8lHU41LgM/uHsrB7gGUc4I/t8GJzNtNIjD05MUE7XqnNFIf0GaUB6v9AbGzRiZq95MIbHLYCek3PvyCzNpXc3N5tqUQWsDjRC8KCYJBJUn8aVtTtq5DPc8tiRAxemHjxw4MjJk7ffkm1By6rKC2GthhoLrCkX9aEels1QYxzNEdE0B9H/oJ1OgG5LVMROQJ4BG8AbAL0nEcEqBNAJ1oLRK/O8bRtsxbZgm5GRPezKCt9J0e7g+urIyOpf3AoGYD8MGSTPCyZ2BhimF6GYoir2cc0r8TCt5rj0+u5mRR6SwbVYUEhhFDOvQKtqPkQFenqW4YVL317Y2LgQmTx2eLjPD3kfDAYXIM+wf01bblbSHtUYiLNafozjYNmnDrJr0HNqyedKdJVAYkmcni61mSH0X1ZDJPhwb6llBJFLL3P5+LjIYwsLAdnibKUNHfEFIGpL/uHvP1y+1b/KOewXYyKC15DnJb/ijKNWC/GKHdG88L4EmvR0o1GsQG4UXoVRq6qWJWABg7dEFYe9Kc4358OEevjw4dt82c6Ya0JPL+9WyGhUpypmXWZFe8BmNd//oIHWxcI82kmny96z3iJWEUADJsbADBFpAHSJ3GAh0ET7LKBw6TSd4nl93FQFHeYdlbAKpjcyeiagJ9uJA0JqPeh0Dq6mc7wCkESVfknBbKCVQT8SBBsOZNCrTVc0kV4HezfuyBKTiITUROG4WBfUJsDLlYw3Ein29OtMW2Zed3RG3yAdTLeg/SseW5Rqxe2cdiKledhUaoz07JiP9S0tLQ0Ga5S3JiIx6RXLvrqFu9amW1ordQTx5ZBAl7bFUvtWapkBQ087C5L/nhXr3/q/kmpd21iw/H6jEPaUS79/st3G1qnVP6iJVZeRE1orev3+2ryFMReHBoHOyoGidgpiwZVIRAk0vkNuf30wRPGAgwF+ESFeKBen2DkXXWsKIIzrVA0xIDCii55I78p+Bvml/vi0cHbxxZdDz89qcyxNs87jIwt6RW06YZpVqyx441ic5iOSUaxbUtc2AK+Re0fTJC7BDaK6uLb2O6BCC5ZWNrZT6airLJU+nLX+/LNegYd/Y7YsGnlfxCiV1mYBG1QbWf+bmrgUZTkeWgfBS+LCgKUgLo4QEsHFWcn2LJXBA8IgdwWaJERsQ9thBkJYgQQcGIKqareCWhYRFVyOxy2eFwRJdVF0oiaT3fB/RJ3vq9JQGMfvP1MMBpMKyeGilb4pLWxlYBYWFpeZOrAyI7esQcuRgkErFMqSftgvK7WaFLdiM4OiF1K9GL24HLicgiQui14FPUdv9X2hz845ip9znuc5P5R5Mudgu3Hj9rZBGkYaPJrxjL4n5pfSPGyzJoMsW4ZDl5tv7fq2ZikxN4U0zSm2aiC4NGf0qFT82cSGhP53BzwQaG8HGW1tzxlYIG98uWppX2Lhbl2X/XIL6k37KTJwh1peLsDGO65IBlGstaG/ptnaIPdjoNZm2NRYfL6rAvTOTD5vTaGL/2IIoOV0jJjQIBFkYeIhbCtiOnx68wbA/+6jqYw1CYUwdrdlUWKwfSDUkoMa7WMYD5W3VBU2oB2OzkyaSlaGj5GohYonvVJoziRChBrDd02xCJgwvK4k9WXu2wsJ4X/QiPQG5mFtIpc0VzLhBydtIyF9j0hb3U1KMJBdMAx4L3z37t1+eHmZCufjgrKAZoGjZoZfDIOWW1NBkhr8Gr4A6NDOcBxietL82hNJDZASWZ0SQG+AAilZ5woU1Z+Aq8/UqefT8NRqLcFIx62JihBKPOJCaetRvB5gAw2/j2Jo6v2kWUwvLQkczVuDeq5OHCyahoNVNTaDxhiL7KOHP0bfky5CCNYbtOLeK+ePYoSwuSKEADzJ5DPeuk1eoQVLN9ta8Fu3hI1rOcEfFDto6gztqhwejQA6P8jZBNowUNq3+OXNG0qYMkPqV1/+fvLpzNpWaCeViXetUfPriS6pmZ9llKRQfGPMx6AgnZABmu5a87EVCbwwmBoqTYUzAE1wVnPzavOEdS3HAfcU+kW3BBNBKQhp2xKVcg5i2giU0usaib1zDshO9R71er2s6LqSi4VwxgYqF6dumg5egXaA3i5HH8Y97zMBRjLxU+2aoAsL1ZSna79CZYWLLpgif8NEkoZRO9sdAcxwAifDNiJSaQKtCG+IVDEyP1hcwyVisdBODw091E1aw2XosdhKdinySQIdg+QTEVkCPVL/QWeFokG0h6Ehpi1E7EZAVd/7Fh8EuZzS8TAMHI1Ze/YkQk35upfuC3o5G4FW9WIpGr23dc4ZO+O28qFRC5TdsYNxuxxgNBc7UL6p4pyv2JddfBW7Vb1Razzq+XkqXuB2iq6cu6ZpgmtjLeW+eoUf6N6H0RPWiMe4ijESmU4nlxkO+2VF0MB3IHiYRXBvvTybLFyEj7LbAtgw0td9nvBkYjX74P5EJsmrb4pK0TBVco0jAlsAhBEyVhTkgrZJdISfQkeIHWUPH+76Rg+CXl3naWoKDcVbpSCb1ft9dtOus1vGz49vuvK6dvH+3Bh0qAL3OTt4m5jPXTu6y308JrpSgZr9Vfeq+6od/dCLbmK1/e3CkPIUvH5Wr17Q2m270obbnimNms75/ax4b+FBHdKBSIcXO14q/mMgKveQSWQw376YpveB+TecSn6ABouM9ARi2hpCPWjW3NaLJgKTOFyZLXgI9MzjTEdgZM0hOlL3MHzXwmBuTcn4CEDr17J8lqJohva8sBzn7OWzlYCHFrVUlD0NZycfn73bldvxeQ7+U+nS7jun1o9vnSagu08dqKXGMx08MD50+sKF/Qfcyu3quZv7j/1h6epelIii+L711P8xMKBEjR/sPuzgvmwZYc26giA6aajgoIarWe0mNOXHpKYu2i5rGKKrpUYWfZhG1spW29eMpqXVSAW1RA+LCL11ZrefOtzDHfX+zj3Mufeeh59iNCooWyhqu6yYzKX8+rB+NMqsFcr5q8QiNrkBfz6aKQsiJJSovonCfaNw+X6/DwPtfxNtn+z3gxVowtBH3w7nYEX2s//wJ8y0pwp1LJjpvoBvwuXhBvbkW38WWsdGT2emlJBrq8fAOgWfWdHr8wVgfPr0bGCPNBiz+OL12eey4eJNeZBCRWLIWfV7+msd4LlL2v1iTgenZB8/vVzM2LsTS41GEt4SSdMsEZC0N9eku1Avm6RRu8meTC5FGSbLMBGCYQpKmxithHFFObyei0DqK1tjkVAEh5nWCyHDcWFnoFyubG6irRaSKQRMXB/81+87h+e4kW0HRg0G8/iOQPoxxz29IUM91T9/HuU8O7Mc4BgHlVoup8jd4YR4U0eZMKZEgfT1XfuQ+tDK64UI3HeM4zKQm6r3RhyAOMCoz4mHVCRoo1ClGHLWQlXn7Y35NoGJ65c1qydCbwDfX1pXUt2JESifAmh6zbFvH72PbpibpGSP/rIpOTpywZ6UJJOgNpZszC9LpetKLYY6SStucGWu+KVZJpJQRWI5nApacybTul5vyDkr51eANNJqtZ5kMqZDu+CC1ZPcJe1nTjDURYaJXT07Z1JLGYUYUdaBdMqzowZHq5ekAmZsFihvHIIojDJ+IA0UVqibjFoNuhvO1zar3mBIpBwBID1j0auXokn8oCE2g70mSFRLif+TvuL1gtapAhMvXHZNytLP3vx+8/0d5UyVJgrtUg9A000vvR9ePTMRpwENttg0O57/+mwGlwBruOoSEokfWcAwa4pcc9yqFvwGuypEboECaTqILfrXE76YzxfLnJ92OqenF2E7G3BliqCkDVAHt09KTLadJUkSLG+2qIO1tyMpiSpkYqT+/M/JFAJ94GnoBmSG9aykAY1GMTEnB9La6vo1Q9ShC7nTlBZVEAVQxLMHIDfVLWF7KuJLH1S5nJQWj4iBtA3CG75hyDp0qpQcE5+3BGQyXAMHhe9qFJIrTbS73fGY58e9r94xoMfH83GaLRVTqsBXnZn6EDCzPQEsy5LLdC+CaOXyStjq8gWOEzgoruIaUP1n3UFK4Y+DvMs11fza9NTV1YsXV2+fQGMRf4neQ7C6QTtsOw36sZruDXps/OrZfKlHs3J4kAmkzUroo2lwv4DMdr242yy1zQnKg4ptVRMNUajR6OLWVmuYbbCkMU/IEdHxe7liyex1HyVxDWUzuqa0U1M2mUcgfTmhgtNADMMqluMy2ZQb5q3mo9CcfWIwGAjleb70ts0DuoOXL9w6XaLmUxm/Xmu7J3Wd3hgAHYOt9yVvGdEqMCtO4MtWjCAUGC63QhVUYwwGjfO1efJ9+scPd35Ojg634Xy2ZQ+5SuM9WKsbY69tszdm4ccGXR7KTGSJH5fksKyC8N4A0tAzBt/ycAXSDp4HT4Pwv45CBNIGFpCle+2QVjuMsmyJZVUnEGTBkuoBNEdJtyoocpPyikAaEUjnig6Qqoet1rRlRY6i1myxobLjmOXKRK0THzwbtDu1Zu1ZB/Rv320dIAKJWuddrXm31jG+nW8LrgDO3cGrrVoEE9kmFYp8Pr+VXjUSq2fmFDhJksvp4CTx4/3fLw8G3Y7ux3u3eHMbVXoQnSY04AG/ed5azfGDyo5gCQ7skLDjjXS6XblM2Ab9Y8p+X9Sn4wCA39P+ix59GQyUqMEt98AxH1qRTRwIBy0f3A0cNVCPI+0HQafWHd5O6DSlOyq7Ii28YiD2BVeHbnO3hZ5t2L4ps+ibxXlBXwy+0Fv7Qe+nt932+rx/fD6y314+8dzujUejQeRk/MNoOzfpvwYvNdwbDAbwzGtAfwgV9+IH3306OIzf9KEMIE6eQzzpf9FM5ITLMcc+P0EEAQ13vP3Vt+9/vMfhgOap51Dy42+/bW2TBKC/+ORZWZaNoRhWWoffiIatXWim1HptW6oVZbl2TzyBZx/tSdZ8bhj6Iu+JU2eoOdP0ZU+sVmsqqwylE0PNFWh5Pp//MJ8zimlWifYEDV7HExI7mP8dXPOd+Qq9ukTXRyLhjV5Gk/tjH7EJ54ivv4gg3deGsLKiMx5JIqCfHY8lrdUSh0Uc2UWizdDWeCSy8DEyEu/fQBlAADrX2PkQSuK7rU6EEVMYK8bSOOEB9GaweSAqjKqSOJYJCNjmNQ1Vs0Z/uHH4zjdv6brhcE8ZtrvQp1OHql11Ykd721RH1zuP5cShLDuyNZ3Pl4v5gvX7KR+gl9NFbbtoGjPTsYfKWM2FC8oaZ+QXrlYkkSYavIwzFjv82yxz7YDyTvT2U0DrttSKUD7vJirkRdhGkeYKneme6GN5YBiSJKk8ZHo0Hzmy5MgahQThh0W7NBgNzR9GUiTYj34KHQl7MI7yntABDCiJJDMZkcVNtVPA8RXa62kfakuzpxUpLBeo+768pqXx2BhyROBowzW6L1iyvehc2VN7MZ1Olz2tNrOn1kdnnanV+7y698d8urCmEOZiumCDHpzCzM5iOusxmiqZNeX9dz958jhDdRy4aKQvVXuhpXgsiSKXJUlnpbXZmfHNA3fYuH31/ZE7m2lFNpcpNZuB2gyaDYGevruV6Y71saEYtqzrDg3o1W2yvJCXPjToaXzR3h/NZXOkyyKg90ZzoA7xHOr/G03jaE5kKW0Y8/2HPhjLhmykfAR2KuBfXmOtFbqIhwDtYhXHcZe9K8NynB/0qXnPXc5W+Mc6lnXvs+oTj0ynC9dao61FxYNRWHjWW9qdi6LCKE5Peeb1nx8NZPDwiTOdj1wNVsZMCQLG7+7WxzqzrUM5Q3DJo9FLpdv8cGxZ9sLYFzyNZvdDR6Yx1APlfbeVuz/W56tPqqvtFNBHOuBt+x90fGfnNQDPdEB7+pd7+ipkkJYCB+PVKuH+tKrSphTzUziyQiPtA6hQfcz6CD4k4Ge/0yK0sVTE0wcbrt3R4FFLKv3Fjz/+8t7YAvRslfFFQbOmvQsTuIB2p1Mr8tF7P8IeSfEr9MIsKM4+O6BTe1vP76Vy1NnPaWs+0lR7vjArDT+G7+76h1JlC7rDlmUFax+1QsHbqjtwZMNRBQ4nrg9FVydzfg+U94cSD2jddt2/0f3+gT5doR176fMC+uudiCwPtTV6crm9Rg8B7Q99BPcZpM8rsAxdlGJxHEc3UdTrbYdUGS5XfRifzuLUGclAnxoa3TjYkKSeo8tSUWjePnw44WRdC4szE2abSp9Kcu9OajjSdS0iybqRn/zy8NJLUQRVjZlahVSU0wRLV5XjbbGaw88edtUWk9JkSy3y0Tbmg0/m+SLXkhQZurQIaJf1d0157Fqu4phqptFOOK5Fo7DtdFfo5ngKaMj09P/ohb3OdLCZ7lSrsYIDL4ZMdgOioigMC6WcC52LihrrXRTKBZ9PKNb+QydDKqy2a4QxLJ3tXVwUfIpsANr/0cbJSQ8avEWnEUCHZrpskqkUG0nk66UvToZ3ns6J0DLa1slg3CoFf3m466EIjCrQsRQp1CE4f9g83mI0QP90n2SK2tKyVIyITzz0pv8yR3KipFlQJUsy+ayrCV1NHrm6K6raspJsshKgvXCw7nbvrtHTVaYt3XKq0X7AmuoLd43mPYinmSxQOSIXsW0VneyGWJZhlAhFYZnQeUJRaxBkjI41YlWYOV4Yjiigh/DfbCNMEI1srdaJlVWYixqVOd94ZnBvYVmLagjv3t4yM9lyVpYE/GjIfi0Pnj779R0dytuBpjaywk4QQSgCoWskTXKVSopj1JLHNCJKMYfDE4W6oCm2s0LfbxM+JNpieRGm4xrdP3C10q1przKtKqZaSfoV6FgaKrTd7N5t8e2hBYWvOBYsGxe/CUmSoQJRUQnes7vbTPpo/KLMrNHXb8oWhExiuUz6dGjZs1isE37qgkwWTUAToIZMByDPC9fxEUQJ0DXfU0XJWWoUdr7xinTlgioVwHYSb1w+uD+JZgTOdN2FBmgHi6aN1QhbwsKI6dJJD/FQ3mCu6qPpSiPrcJIrlGqKJmkZnIv9+WATNQxrqeTQ6P0mTxNxickw0j/o5JvOKtMrtMOkObYS9SpQ+iSa8Ueb3R0Ga6bziYQg7BuGwmHxm6iiRPKSWySLBIYgSDNJ0eGnHmdtW8P+Q1cpgm9kpTW6Frsqx3ilRoWp57xeyHQ0INm24RiAzpQAHaNZSe3UKCK0MVihrUX+5fTd47MHl5Obm914UYOWZxtft5iG/xC4lsnA+u83Sudh1E+hlyWOItk6Uncqks0VzarmmDkiw117nyOHK7QAZ4mmh6eEVj7HOqv9zl7S/fuH0NN5aezYBhlF+FwUqeezQ9ILnzjazaaATY5grlfqp6IoCkj8Ji1bMqA1Zukr4IBu4zRdLrCupBH96x3Zhj8DGicaMHrWaI7jYlWxelH2nUGm0c1GSHSgcjQKxzPZGKA5bhbr3QvjpY2fn12jD3+9+8K+988Vun+TOITIJr+OKDmaixhqpFRxbfu8UQpgKOLbvEYpH2spFcWpDKW65tRSYrFE4DQZo+r/oidtJOhFhyrPGiq8YIvF+jd/3gS7jcQ+NECR3uQzUX9EOgS0B9DJUASbvAa5Yw73ZUuqo/F+ElT1lqEdKkTZh6DNNk3V6xXGMFKADhimZjgRmuAh0wwMsn/QnU7nAj7TQqbhHBrCy+FCoRymcL6xRpOdzr2rQjiz8XA/DKcSJbsjfF9CL0PnO+lm2y8E0vlssis6p5WMUEmxgTxMhFKjEaBRPnzmxcP4R04iyzKNrUhDeJngA2SWz2QqOJ48F+2l6kfikza6i/IGx9eNPMMw+XM+Gn0A6HOjdSwZKThUCNGSaCSGJOpBot3uToQAtC3lD7OSLFXQ4M31sWPU2SpdrRKF8Bodq1cqlUixSvR/vyzEuDDM4xU6TZTLBUCTJFeFeXV1VS5vbqLIZjx5dudO+c6dAoVjDViOGl2NdXqPF6j0Rqh0+azcOq6HuN/SmQbjbJ12+0lB8j9oJLuKkqj79+2FtJMyXCnkyYVIHqO8uwTuD/EZP+6BrSSa/KuMc3l1GojCuH9NCQQ6FAlMmCxa4jIGvJOgEHAhbhSSRcGaLFoQsrAVlPhY2KDgrHRjUyyDIEVQKL6KbgzoQnHrqpJFS7u4fpP69rvJZGZ6kuY35+TM3Fy4i5Vc0mEUDTxmrbf9GxfONPVwvtTLOHpzcfTwZP/E7edP7hnB5GHP+Xr/8fub715d7JJBdzK+cmp4+rzeVIns0YDM7705ywYDzbh7dqSgZZ4ZXgfyZhnlmu6zO9BDrwPodcKiwpXMNKLxhKRprrLz3Q5+4Ms8U3MWdxS0C2gGaCvL7Dz7/PLl0TzTjpxZrOfvTw/vXes8vT/q33h1evjiy7krj8NV2Ht08tXD4aMTH749P3fl3acnB83owDMsm2rMDEKhO4LvdjSIWSPd0bFlDGyzvVree3LtpPL0ksQLrdNpdunu8PDOmb4VhFiGvJhcPDU8f7YbYYkTtgeRI5h6Y997NOfkS5znLDKMoZkbegzoNDExSXTOezMXubjH/AH+Q2DXZuayWkklIU3DaAcxFyzLfJudZW8/K+icKOhmjzdc11XQVmj5vp0X/svZNC/KI6eCxdX39/sHl88cOxbde//qxvDr/PqpJ1fGD3uPDk51J49OfHx1shddvHXnYGIcnAW0JuxBsEhIzDe7XRXEidwd0i4eL3vWbmuTUxc6gF4uaRzHht1ubw4PD9NCtwPCbO1FII1J0+/iHgy8XNE0LpBzML5fKP2yToqMEGHJhHNAF5Jz8/zlBxe9Wa7r87nve55n53kuVpymbpqmbmEbxjigaW6apmW+fdnJniG8Zy4HNNV6otVotFo5s82YYojSpJjmbpoA+kJ44jagT7+eH0T906euDb8eu/TmrucZvTlW2e355cenbw49vxUdzKOJbZgmKQZGb7UrF3D0bhOUrWor/Y7PbJ+NBzyMPDyBixq6lKY22RzuDgtm2w6fZcaXWBpdQ3AhOMEICUoE1XSnN/9S0eWmKGRRJChFsdlKKPU7n9+qpE1Iv2/a9sxlDCGbuA1Fo3jg6ZBOW5ZlWqMsi1j+djp1c8KJzvWAT6dHj07dgolqVyQSwzRrTRspP3LmQvjk8eUJoPuBOTJMvMg8uPTG6zCz17Mtf9zrnI/OPex0B4N+YExsixmmHbFwvYsdUUOTFt9uWQfIvj3QaGyYNfSKxmUpmTXYKCsprQUVBf2y4YRyLqQQgKYJCKmmhYAu+ArRqNRQ+nHIO2fPduxWg3MjsoWeu1K0fmkKaMtqLgBt1tCJ1WodnU6RyAQBd0CAK7hMCrmR6sItDILSkc4wPHnjcvvYlfPjvjkajPDKun/hrNcZtgE9Hg8nHtZm3c7Dh90gMIYM0JluAXoTh3Kz2VSBsy2rrY/pinGhaBgrmpqCLstS2DYDawIBOi2qVfqLymp3TRwKS9PDANDFJmn8KYXVyD3kMTZrcRrMudSKXHLA/mAGnrQsbV21chPSk9R0W3km8oRyQigNyd6sLo/+oSNeN9ockvYB8cdja4RffsJH/SizvWjUC+xRBOAB9pGmt0MHKxPP95ljsV6Z6GHM3VQGDhcWZ4Cma/iwgBJOFquFgk4y3/fzmoFvNw041lW8dUGbTRNVQGMKWM6TX7B/qYG/sU2PNjhR0GWRFsrT6nQ820kiC8HLbSUKjLWwXTeZ7gcj5QTUDjk6bR39T9OjR2zbPzykDw80a9y2miPDjueBQy07NXshNVk0sv1Rc9DkIiw3m21l+j4ymOxpHIQQdUqUtu15fpKktR9xnxzQnFQ0beR25meKJdnIRqEqs9rPgI5jjhoj7aYTL5aJ4v3BrIrfHs8bLZeTcMllhUitKrWwplyox7/GpgKYClXx7NUSlFIekz9R//Q0tQwhudaLdb2kTWCGS6fCOQkFO+U60qAQpEl4vIEo863KKVIugQjB1QkQWAdJIAOJ0qyGXu+rrJNlnu/uQZMfyHWLl6WCLixdjxerPfSeF6WbFQWChNksT5DPBGJnyYuNkJIn+5GFR//WUWxKqlJDC42ocfiXWEFLjBgp10plSYSQzkpwDlyC+Y8LgV0mKQjVd2XM9009TkH3S6mb+93O2YvnMTqwqSWr7UZ9VDDLA1RTJ2BC9Lm/kAFNKw57STX1t5CVTAup0o6FKQwB7zgxqTjuvUAES1TKWKYSwL+ioFULh98COgrFL4EgKP8vtI/XOqLFMbZagCaUVhWtsFM9rngt+lsWFv2W4xBd07T9aU2cjnW0hQWZVuICtaqfotDPAdU4JaVSXMZ/qEKYL2CwWARQr6fKcKHkqP2H4hhbjAo+CmGxN4BgpcqwlgMLxUHKvbFGalHyt+iRvQvTH3LRqA9KSYKd49HZtyiuEwTqDhDbqjcpElQ0oreDHrRcrgRMf846OEhOkQdqCbhdCFzxt9x6k7yq1uvtVu6/pC7+MkMjqVWooERLtev+X93qICE0sBUFqkhtQqAFY3QICF0QjrI40oD+CDoU2PeqcyuShqwxXFoqpymCovU71boVr/BMrlZryN1H2c89wXdtanHODKwwuUh+ZSqoTrQFpyUs8AF61SX/skDtZ3edMuqw/n1/P6oIelXu76gu6wZcuB8glD9VQN8BuSgNqDP0ckwAAAAASUVORK5CYII=\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Power consumption card with background\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"kW\",\"decimals\":0,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "power", + "energy", + "energy usage", + "electric load", + "electricity", + "power efficiency", + "load profile" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/power_consumption_chart_card.json b/application/src/main/data/json/system/widget_types/power_consumption_chart_card.json new file mode 100644 index 0000000000..79105f50f5 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/power_consumption_chart_card.json @@ -0,0 +1,34 @@ +{ + "fqn": "power_consumption_chart_card", + "name": "Power consumption chart card", + "deprecated": false, + "image": "tb-image:cG93ZXJfY29uc3VtcHRpb25fY2hhcnRfY2FyZC5zdmc=:cG93ZXJfY29uc3VtcHRpb25fY2hhcnRfY2FyZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxnIGZpbHRlcj0idXJsKCNhKSI+PHJlY3Qgd2lkdGg9IjE4NiIgaGVpZ2h0PSIxNDYiIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iLjgiIHJ4PSI0IiB0cmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSA3IDE1MykiLz48L2c+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZD0iTTIxLjcgMjlIMjFsLjctNC43aC0yLjRjLS40IDAtLjQtLjItLjItLjUuMS0uMiAwIDAgMCAwbDMuOS02LjloLjdsLS43IDQuN2gyLjNjLjQgMCAuNC4yLjMuM3YuMWwtNCA3Wm0xOC4xLTUuOGgtMi42VjIyaDIuNmMuNCAwIC44LS4xIDEtLjMuNC0uMS42LS4zLjctLjZsLjItLjhjMC0uNCAwLS42LS4yLTFsLS42LS42YTIgMiAwIDAgMC0xLjEtLjJoLTJWMjdIMzZWMTdoMy44Yy44IDAgMS40LjIgMiAuNS41LjMgMSAuNiAxLjIgMS4xLjMuNS40IDEgLjQgMS42YTMgMyAwIDAgMS0uNCAxLjdjLS4zLjUtLjcuOC0xLjIgMS0uNi4zLTEuMi40LTIgLjRabTQuOC4xdi0uMWMwLS42LjEtMSAuMy0xLjUuMS0uNS4zLS45LjYtMS4yYTMgMyAwIDAgMSAxLjEtLjggMy43IDMuNyAwIDAgMSAyLjkgMGMuNC4yLjguNCAxLjEuOC4zLjMuNS43LjcgMS4ybC4yIDEuNXYuMWMwIC42IDAgMS0uMiAxLjVzLS40LjktLjcgMS4yYTMuMSAzLjEgMCAwIDEtMi41IDFjLS42IDAtMSAwLTEuNS0uMmEzLjYgMy42IDAgMCAxLTEuOC0ybC0uMi0xLjVabTEuNy0uMXYxLjFsLjQuN2MuMS4zLjMuNC42LjZsLjguMmExLjYgMS42IDAgMCAwIDEuMy0uOGMuMi0uMi4zLS40LjMtLjdsLjItMXYtLjFjMC0uNCAwLS43LS4yLTFhMSAxIDAgMCAwLS4zLS43IDEuNiAxLjYgMCAwIDAtMS4zLS44Yy0uMyAwLS42IDAtLjguMi0uMy4xLS41LjMtLjYuNi0uMS4yLS4zLjQtLjMuN2wtLjEgMVptOC44IDIuMSAxLjgtNS44aDFsLS4zIDEuOC0xLjcgNS42aC0xbC4yLTEuNlptLTEtNS44IDEuNCA1LjhWMjdoLTFsLTItNy40aDEuNlptNS40IDUuOCAxLjMtNS44aDEuNmwtMiA3LjRoLTFsLjEtMS42Wm0tMS40LTUuOCAxLjcgNS43LjIgMS43aC0xbC0xLjctNS42LS4zLTEuOGgxWm05IDcuNmMtLjYgMC0xLjEtLjEtMS41LS4zYTMuMyAzLjMgMCAwIDEtMS45LTEuOSA0IDQgMCAwIDEtLjItMS40di0uM2MwLS42IDAtMSAuMi0xLjZsLjctMS4yYTMgMyAwIDAgMSAxLjEtLjcgMy40IDMuNCAwIDAgMSAyLjcgMGMuNC4yLjguNCAxIC43LjMuMy41LjcuNiAxLjJMNzAgMjN2LjdoLTUuN3YtMS4xaDR2LS4ybC0uMS0uOGExIDEgMCAwIDAtLjUtLjZjLS4yLS4yLS41LS4zLS45LS4zLS4yIDAtLjUgMC0uNy4ybC0uNS41LS40Ljh2Mi4ybC41LjdjLjEuMi4zLjQuNi41bC44LjJhMi4zIDIuMyAwIDAgMCAxLjktMWwuOS45LS43LjctLjkuNS0xLjMuMlptNi02LjF2NmgtMS42di03LjVoMS42VjIxWm0yLjQtMS41VjIxYTMuMiAzLjIgMCAwIDAtMS41LjEgMS40IDEuNCAwIDAgMC0uOC45bC0uMi43aC0uNGMwLS41IDAtLjkuMi0xLjMgMC0uNC4yLS43LjQtMSAuMi0uMy40LS42LjctLjdhMS45IDEuOSAwIDAgMSAxLjMtLjNsLjMuMVptOCA2LjNjLjMgMCAuNSAwIC43LS4ybC41LS40Yy4yLS4yLjMtLjUuMy0uN2gxLjVjMCAuNS0uMSAxLS40IDEuM2EzIDMgMCAwIDEtMSAxIDQuMSA0LjEgMCAwIDEtMy4xIDAgMyAzIDAgMCAxLTEtLjhsLS42LTEuMmMtLjItLjQtLjItMS0uMi0xLjRWMjNjMC0uNSAwLTEgLjItMS40LjEtLjUuMy0uOS42LTEuMmwxLS44IDEuNS0uM2EzIDMgMCAwIDEgMS42LjRjLjQuMi44LjUgMSAxIC4zLjQuNC44LjQgMS40SDg1bC0uMi0uNy0uNS0uNi0uOC0uMmMtLjMgMC0uNiAwLS44LjJsLS42LjYtLjIuNy0uMSAxdjEuMWwuMy43LjYuNi44LjJabTQuMi0yLjV2LS4xYzAtLjYuMS0xIC4zLTEuNS4xLS41LjMtLjkuNi0xLjJhMyAzIDAgMCAxIDEuMS0uOCAzLjcgMy43IDAgMCAxIDIuOSAwYy40LjIuOC40IDEgLjguNC4zLjYuNy44IDEuMmwuMiAxLjV2LjFjMCAuNiAwIDEtLjIgMS41cy0uNC45LS43IDEuMmEzLjEgMy4xIDAgMCAxLTIuNSAxYy0uNiAwLTEgMC0xLjUtLjJhMy41IDMuNSAwIDAgMS0xLjgtMmwtLjItMS41Wm0xLjctLjF2MS4xbC40LjdjLjEuMy4zLjQuNi42bC44LjJhMS42IDEuNiAwIDAgMCAxLjMtLjhjLjItLjIuMy0uNC4zLS43bC4xLTF2LTEuMWwtLjQtLjdhMS42IDEuNiAwIDAgMC0xLjMtLjhjLS40IDAtLjYgMC0uOS4yYTEgMSAwIDAgMC0uNS42IDEgMSAwIDAgMC0uMy43bC0uMSAxWm04LjUtMnY1LjdoLTEuN3YtNy40aDEuNnYxLjZabS0uMyAxLjhIOTdjMC0uNi4xLTEgLjMtMS41LjEtLjQuMy0uOC42LTEuMWEyLjcgMi43IDAgMCAxIDItMWMuNCAwIC44IDAgMSAuMi4zIDAgLjYuMi44LjQuMi4yLjQuNS41LjlsLjEgMS4zdjQuN2gtMS42di00LjdjMC0uNCAwLS43LS4yLS45YS45LjkgMCAwIDAtLjQtLjRsLS43LS4yYTEuNiAxLjYgMCAwIDAtMS40LjdsLS4zLjctLjEuOVptMTAuOCAyLS4xLS41YTEgMSAwIDAgMC0uNS0uNGwtMS0uM2MtLjQgMC0uNy0uMi0xLS4zbC0uOS0uNC0uNS0uNmMtLjItLjMtLjItLjYtLjItLjkgMC0uMyAwLS42LjItLjhsLjUtLjcgMS0uNSAxLjEtLjJjLjYgMCAxLjIuMSAxLjYuMy41LjIuOC41IDEgLjkuMy4zLjQuNy40IDEuMmgtMS43bC0uMS0uNmExIDEgMCAwIDAtLjQtLjRsLS44LS4yYy0uMyAwLS41IDAtLjcuMmExIDEgMCAwIDAtLjQuMy45LjkgMCAwIDAgMCAuOGMwIC4xIDAgLjIuMi4zbC41LjIuNy4yIDEuNS40Yy40LjIuNy41LjkuOC4yLjIuMy42LjMgMWEyIDIgMCAwIDEtLjggMS43IDMgMyAwIDAgMS0xIC40bC0xLjIuMmMtLjYgMC0xLjItLjEtMS43LS40LS40LS4yLS44LS41LTEtLjlhMiAyIDAgMCAxLS40LTEuMmgxLjZjMCAuMy4xLjYuMy44LjEuMS4zLjMuNS4zbC44LjJjLjMgMCAuNSAwIC43LS4yLjIgMCAuNC0uMS41LS4zbC4xLS41Wm03LjguMnYtNS43aDEuNlYyN2gtMS41bC0uMS0xLjdabS4yLTEuNWguNmMwIC40IDAgLjktLjIgMS4zbC0uNSAxLjFjLS4yLjMtLjUuNi0uOS43YTMgMyAwIDAgMS0xLjMuMyAzIDMgMCAwIDEtMS0uMiAyIDIgMCAwIDEtLjctLjVsLS41LS44YTQgNCAwIDAgMS0uMi0xLjN2LTQuOGgxLjZ2NC44bC4xLjcuMy40Yy4xLjIuMi4yLjQuM2guNGMuNSAwIC45IDAgMS4xLS4yLjMtLjIuNS0uNS42LS44bC4yLTFabTUtMi43djZoLTEuNnYtNy41aDEuNWwuMSAxLjVabS0uMyAyaC0uNWMwLS41IDAtMSAuMi0xLjVsLjUtMWMuMy0uNC42LS43IDEtLjhhMyAzIDAgMCAxIDEuMi0uM2MuNCAwIC43IDAgMSAuMmwuNy40LjUuOC4xIDEuMnY1aC0xLjZ2LTQuOGMwLS40IDAtLjctLjItLjlhLjkuOSAwIDAgMC0uNC0uNGwtLjctLjJjLS4zIDAtLjYgMC0uOC4ybC0uNi41LS4zLjd2LjlabTQuNi0uNS0uNy4yLjEtMS4zLjYtMWEyLjYgMi42IDAgMCAxIDIuMS0xYy40IDAgLjggMCAxIC4yLjQgMCAuNi4yLjguNC4yLjMuNC41LjUuOWwuMiAxLjN2NC43aC0xLjd2LTQuOGMwLS4zIDAtLjYtLjItLjhhLjkuOSAwIDAgMC0uNC0uNGwtLjctLjJjLS4zIDAtLjUgMC0uNy4yLS4yIDAtLjMuMi0uNS40bC0uMy41di43Wk0xMzQgMjF2OC44aC0xLjdWMTkuNWgxLjVsLjIgMS41Wm00LjggMi4ydi4xYzAgLjYgMCAxLS4yIDEuNS0uMS41LS4zLjktLjYgMS4yLS4yLjMtLjUuNi0uOS44LS4zLjItLjcuMy0xLjIuM2wtMS4yLS4zYy0uMy0uMS0uNi0uNC0uOS0uN2E0IDQgMCAwIDEtLjUtMS4xYy0uMi0uNC0uMy0xLS4zLTEuNFYyM2wuMy0xLjUuNS0xYTIuNSAyLjUgMCAwIDEgMi4xLTFjLjUgMCAuOSAwIDEuMi4ybDEgLjcuNSAxLjIuMiAxLjZabS0xLjcuMXYtMS4xbC0uNC0uOGMwLS4yLS4zLS40LS41LS41bC0uNy0uMmMtLjMgMC0uNiAwLS44LjItLjIgMC0uNC4yLS41LjRsLS4zLjYtLjIuN1YyNGwuMy45LjUuNmMuMy4yLjYuMyAxIC4zbC43LS4yYy4yLS4yLjQtLjMuNS0uNi4yLS4yLjMtLjQuMy0uN2wuMS0xWm02LjctMy44djEuMmgtNC4xdi0xLjJoNC4xWm0tMy0xLjhoMS43VjI1bC4xLjUuMy4zaC40YTIuNSAyLjUgMCAwIDAgLjYgMFYyN2E0LjIgNC4yIDAgMCAxLTEuMS4yYy0uNCAwLS43IDAtMS0uMmwtLjctLjdjLS4yLS4zLS4yLS43LS4yLTEuMnYtNy4zWm02LjMgMS44VjI3aC0xLjZ2LTcuNGgxLjZabS0xLjgtMS45YzAtLjIuMS0uNS4zLS42LjItLjIuNC0uMy43LS4zLjMgMCAuNS4xLjcuMy4yLjEuMi40LjIuNiAwIC4yIDAgLjUtLjIuNmExIDEgMCAwIDEtLjcuMyAxIDEgMCAwIDEtLjctLjMuOC44IDAgMCAxLS4zLS42Wm0zLjYgNS43di0uMWMwLS42IDAtMSAuMi0xLjUuMS0uNS40LS45LjctMS4yYTMgMyAwIDAgMSAxLS44bDEuNS0uM2MuNSAwIDEgLjEgMS40LjMuNS4yLjguNCAxLjEuOC4zLjMuNS43LjcgMS4yLjIuNC4yIDEgLjIgMS41di4xYzAgLjYgMCAxLS4yIDEuNXMtLjQuOS0uNyAxLjJhMy4yIDMuMiAwIDAgMS0yLjUgMWMtLjUgMC0xIDAtMS41LS4ybC0xLS44Yy0uMy0uMy0uNi0uNy0uNy0xLjItLjItLjQtLjItMS0uMi0xLjVabTEuNi0uMXYuMWwuMSAxYzAgLjMuMi41LjMuN2wuNi42LjguMmExLjYgMS42IDAgMCAwIDEuNC0uOGwuMy0uN3YtMi4xYzAtLjMtLjItLjUtLjQtLjdhMS42IDEuNiAwIDAgMC0xLjMtLjhjLS4zIDAtLjYgMC0uOC4ybC0uNi42LS4zLjctLjEgMVptOC41LTJ2NS43aC0xLjd2LTcuNGgxLjZsLjEgMS42Wm0tLjMgMS44aC0uNWMwLS42IDAtMSAuMi0xLjUuMS0uNC4zLS44LjYtMS4xYTIuNiAyLjYgMCAwIDEgMi4xLTFjLjQgMCAuNyAwIDEgLjIuMyAwIC41LjIuNy40LjIuMi40LjUuNS45bC4yIDEuM3Y0LjdoLTEuN3YtNC43bC0uMS0uOWEuOS45IDAgMCAwLS41LS40bC0uNy0uMmExLjYgMS42IDAgMCAwLTEuMy43bC0uNC43LS4xLjlaIi8+PHBhdGggZmlsbD0iIzNCOTExQyIgZD0iTTkxIDY0LjFWODdoLTMuOFY2OC42bC01LjYgMS45di0zLjFsOS0zLjNoLjRabTcuNiAxMi4zVjg3aC0xLjNWNzYuNGgxLjNabTQuNSAzLjFMOTkuOSA4MyA5OCA4NC45bC0uMi0xLjQgMS4zLTEuNSAyLjMtMi41aDEuNlptLTEuMiA3LjQtMi42LTMuNS42LTEuMSAzLjUgNC42SDEwMlptNS41LTMgMi03aDFsLS42IDIuOC0yLjEgNy4yaC0xbC43LTNabS0yLTcgMS41IDYuOS41IDMuMWgtMUwxMDQgNzdoMS40Wm03LjUgNi45IDEuNS02LjhoMS4zbC0yLjQgMTBoLTFsLjYtMy4yWm0tMi40LTYuOCAxLjkgNyAuNyAzaC0xbC0yLTcuMy0uNi0yLjdoMVoiLz48cGF0aCBmaWxsPSJ1cmwoI2IpIiBmaWxsLW9wYWNpdHk9Ii4zIiBkPSJNMTQ0LjggNzUuMmg3djlsLTMuNSAyLjYtMy41LTIuN3YtOC45WiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTE0OCA4Ny4zYy4yLjIuNS4yLjcgMGwzLjItMy4yLS43LS42LTEuNCAxLjRjLS40LjQtMSAwLTEtLjV2LTMuOGgtMXYzLjhjMCAuNi0uNi45LTEgLjVsLTEuNC0xLjQtLjYuNiAzLjIgMy4yWiIgY2xpcC1ydWxlPSJldmVub2RkIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZD0iTTE1OS45IDgxLjJ2MS4yaC0zLjF2LTEuMmgzLjFabTUuMy00LjN2OC41aC0xLjV2LTYuOGwtMiAuN1Y3OGwzLjMtMS4yaC4yWm03LjQgMGguMlY3OGMtLjYgMC0xIDAtMS40LjItLjQuMi0uNy40LS45LjdsLS41IDEtLjIgMS4xdjEuNGwuMS45LjMuNi41LjQuNi4xLjUtLjFjLjIgMCAuNC0uMi41LS40bC4zLS41YTIuNSAyLjUgMCAwIDAgMC0xLjRsLS4zLS42LS40LS4zLS42LS4yYy0uMyAwLS42IDAtLjguMmwtLjYuNS0uMi43LS41LS4yYzAtLjMgMC0uNy4yLTFsLjUtLjcuOC0uNGMuMy0uMi42LS4yIDEtLjIuMyAwIC43IDAgMSAuMmwuOC43Yy4yLjIuMy41LjQuOWEzLjcgMy43IDAgMCAxIDAgMi4ybC0uNiAxYTIgMiAwIDAgMS0uOS41IDIgMiAwIDAgMS0xLjEuM2MtLjUgMC0uOS0uMS0xLjItLjNsLTEtLjctLjUtMS4xLS4yLTEuM3YtLjZjMC0uNiAwLTEuMi4yLTEuOC4yLS42LjUtMSAuOC0xLjUuMy0uNS44LS44IDEuMy0xIC42LS4zIDEuMi0uNCAyLS40Wm0zLjkgMi4xdi0uNWMwLS4zIDAtLjYuMi0uOGwuNi0uNyAxLS4yYy40IDAgLjcgMCAxIC4yLjMuMi41LjQuNi43LjIuMi4yLjUuMi44di41YzAgLjMgMCAuNi0uMi45LS4xLjItLjMuNS0uNi42LS4zLjItLjYuMy0xIC4zYTIgMiAwIDAgMS0xLS4zbC0uNi0uNi0uMi0uOVptMS0uNXYuOWwuNC40LjQuMS41LS4xLjItLjQuMS0uNHYtLjlsLS4zLS4zYS44LjggMCAwIDAtLjUtLjJjLS4yIDAtLjMgMC0uNC4yLS4yIDAtLjMuMi0uMy4zbC0uMS40Wm0zLjEgNS4zdi0uNWMwLS4zIDAtLjYuMi0uOGwuNy0uNyAxLS4yYy4zIDAgLjYgMCAxIC4ybC41LjdjLjIuMi4yLjUuMi44di41YzAgLjMgMCAuNi0uMi45LS4xLjMtLjMuNS0uNi42LS4yLjItLjYuMy0xIC4zYTIgMiAwIDAgMS0xLS4zbC0uNi0uNi0uMi0uOVptMS0uNXYuNWwuMS41LjMuMy40LjEuNS0uMS4zLS4zdi0xLjRhLjguOCAwIDAgMC0uOC0uNWMtLjIgMC0uMyAwLS40LjJsLS4zLjN2LjRabTEuMi01LjItNC4yIDYuNy0uNy0uNCA0LjItNi43LjcuNFoiLz48ZyBzdHJva2U9IiMwMDAiIGNsaXAtcGF0aD0idXJsKCNjKSI+PHBhdGggc3Ryb2tlLW9wYWNpdHk9Ii4xIiBzdHJva2Utd2lkdGg9Ii42IiBkPSJNMTQuMyAxMjkuM2gxNzAuOCIvPjxwYXRoIHN0cm9rZS1vcGFjaXR5PSIuOSIgZD0iTTE3IDEzOC45aDlsMTIgOC40IDEwLjItMzAuNCAxOS43IDIyIDIyLjgtMjIgNi42LjdjNC40IDguMSAxMy4zIDI5LjcgMTMuOCAyOS43aDEzLjJsMTEuNC0yNi42aDkuNWw4LjQtMTQuNEwxNjUgMTEwbDE4IDEwLjYiLz48L2c+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNCIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTQuMiA5NGgtLjl2My41aDF2LTIuOGgyLjZWOTRoLTIuN1ptMCA1MnYtMi43aC0uOXYzLjdIMTd2LTFoLTIuN1pNMTg3IDk3LjZ2LTMuN2gtMy43djFoMi43djIuN2gxWm0wIDQ5LjR2LTMuN2gtMXYyLjdoLTIuN3YxaDMuNloiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjE0OCIgeDI9IjE0OCIgeTE9Ijg3LjUiIHkyPSI3NS4yIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojZmZmO3N0b3Atb3BhY2l0eToxIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdHlsZT0ic3RvcC1jb2xvcjojZmZmO3N0b3Atb3BhY2l0eTowIi8+PC9saW5lYXJHcmFkaWVudD48Y2xpcFBhdGggaWQ9ImMiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0xNC4zIDkyaDE3MXY2MGgtMTcxeiIvPjwvY2xpcFBhdGg+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjE5My4yIiBoZWlnaHQ9IjE1My4yIiB4PSIzLjQiIHk9IjMuNCIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMS44Ii8+PGZlQ29tcG9zaXRlIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzE1MTFfNDM4MzYiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzE1MTFfNDM4MzYiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48L2RlZnM+PC9zdmc+", + "description": "Displays power consumption data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'powerConsumption', label: 'Power consumption', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'kW', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'powerConsumption', 'kW', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Power consumption\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"kW\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":5,\"color\":\"#3FA71A\"},{\"from\":5,\"to\":15,\"color\":\"#F36900\"},{\"from\":15,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"kW\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"#000000DE\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"kW\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Power consumption\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"bolt\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"margin\":\"0px\"}" + }, + "tags": [ + "power", + "energy", + "energy usage", + "electric load", + "electricity", + "power efficiency", + "load profile" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/power_consumption_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/power_consumption_chart_card_with_background.json new file mode 100644 index 0000000000..296c2bec5d --- /dev/null +++ b/application/src/main/data/json/system/widget_types/power_consumption_chart_card_with_background.json @@ -0,0 +1,34 @@ +{ + "fqn": "power_consumption_chart_card_with_background", + "name": "Power consumption chart card with background", + "deprecated": false, + "image": "tb-image:cG93ZXJfY29uc3VtcHRpb25fY2hhcnRfY2FyZF93aXRoX2JhY2tncm91bmQuc3Zn:cG93ZXJfY29uc3VtcHRpb25fY2hhcnRfY2FyZF93aXRoX2JhY2tncm91bmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiIHRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDYgMTU0KSIvPjwvZz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNMTkuMyAyNmgtLjZsLjYtNC43SDE3Yy0uNCAwLS40LS4yLS4zLS40LjItLjIgMCAwIDAgMGw0LTYuOWguNmwtLjYgNC43SDIzYy4zIDAgLjQuMi4zLjN2LjFsLTQgNi45Wm0xNy41LTQuN2gtMi42di0xLjRoMi42Yy40IDAgLjggMCAxLS4yLjQtLjEuNi0uMy43LS42bC4yLS45YzAtLjMgMC0uNi0uMi0uOS0uMS0uMi0uMy0uNS0uNi0uNmEyIDIgMCAwIDAtMS4xLS4zaC0yVjI1SDMzVjE1aDMuOGMuOCAwIDEuNC4yIDIgLjUuNS4yIDEgLjYgMS4yIDEgLjMuNi40IDEgLjQgMS43IDAgLjYtLjEgMS4yLS40IDEuN2EzIDMgMCAwIDEtMS4yIDFjLS42LjMtMS4yLjQtMiAuNFptNC44IDBjMC0uNi4xLTEuMS4zLTEuNmwuNi0xLjJhMyAzIDAgMCAxIDEuMS0uN2MuNC0uMiAxLS4zIDEuNS0uM3MxIDAgMS40LjNjLjQuMS44LjQgMS4xLjdsLjcgMS4yLjIgMS41di4yYzAgLjUgMCAxLS4yIDEuNWwtLjcgMS4yYTMuMSAzLjEgMCAwIDEtMi41IDFjLS42IDAtMSAwLTEuNS0uMmEzLjYgMy42IDAgMCAxLTEuOC0ybC0uMi0xLjVabTEuNyAwdjFsLjQuOGMuMS4yLjMuNC42LjUuMi4yLjUuMi44LjJhMS42IDEuNiAwIDAgMCAxLjMtLjdjLjItLjIuMy0uNS4zLS44bC4yLTFjMC0uNCAwLS43LS4yLTEgMC0uMy0uMS0uNi0uMy0uOGExLjYgMS42IDAgMCAwLTEuMy0uN2MtLjMgMC0uNiAwLS44LjJhMSAxIDAgMCAwLS42LjVjLS4xLjItLjMuNS0uMy44bC0uMSAxWm04LjggMiAxLjgtNS43aDFsLS4zIDEuN0w1MyAyNWgtMWwuMi0xLjZabS0xLTUuNyAxLjQgNS44VjI1aC0xbC0yLTcuNGgxLjZabTUuNCA1LjcgMS4zLTUuN2gxLjZsLTIgNy40aC0xbC4xLTEuN1ptLTEuNC01LjcgMS43IDUuNy4yIDEuN2gtMWwtMS43LTUuNy0uMy0xLjdoMVptOSA3LjVjLS42IDAtMS4xIDAtMS41LS4yYTMuMyAzLjMgMCAwIDEtMS45LTIgNCA0IDAgMCAxLS4yLTEuM3YtLjNjMC0uNiAwLTEuMS4yLTEuNnMuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS4xLS44bDEuMy0uMmMuNiAwIDEgMCAxLjQuMmEzLjEgMy4xIDAgMCAxIDEuNiAxLjlsLjIgMS41di43aC01Ljd2LTEuMmg0di0uMWwtLjEtLjktLjUtLjYtLjktLjJjLS4yIDAtLjUgMC0uNy4yLS4yIDAtLjQuMi0uNS41bC0uNC43djIuM2wuNS43LjYuNS44LjFBMi4zIDIuMyAwIDAgMCA2NiAyM2wuOS45LS43LjZhMiAyIDAgMCAxLS45LjVjLS40LjItLjguMi0xLjMuMlptNi02VjI1aC0xLjZ2LTcuNGgxLjZWMTlabTIuNC0xLjVWMTlhMy4yIDMuMiAwIDAgMC0xLjUgMCAxLjQgMS40IDAgMCAwLS44IDFsLS4yLjZoLS40YzAtLjQgMC0uOC4yLTEuMiAwLS40LjItLjguNC0xIC4yLS40LjQtLjYuNy0uOGExLjkgMS45IDAgMCAxIDEuMy0uMmguM1ptOCA2LjIuNy0uMS41LS41LjMtLjZoMS41YzAgLjQtLjEuOS0uNCAxLjNhMyAzIDAgMCAxLTEgLjljLS41LjItMSAuMy0xLjYuMy0uNiAwLTEgMC0xLjUtLjNhMyAzIDAgMCAxLTEtLjhjLS4zLS4zLS41LS43LS42LTEuMS0uMi0uNS0uMi0xLS4yLTEuNXYtLjJjMC0uNSAwLTEgLjItMS40LjEtLjUuMy0uOS42LTEuMi4zLS40LjYtLjYgMS0uOGwxLjUtLjNjLjYgMCAxLjEgMCAxLjYuMy40LjMuOC42IDEgMSAuMy40LjQuOS40IDEuNEg4MmwtLjItLjctLjUtLjVjLS4zLS4yLS41LS4yLS44LS4yLS4zIDAtLjYgMC0uOC4yLS4zLjEtLjQuMy0uNi41bC0uMi44LS4xLjl2MS4ybC4zLjdjLjIuMi4zLjQuNi41LjIuMi41LjIuOC4yWm00LjItMi40di0uMmwuMy0xLjUuNi0xLjJhMyAzIDAgMCAxIDEuMS0uN2MuNC0uMiAxLS4zIDEuNS0uM3MxIDAgMS40LjNjLjQuMS44LjQgMSAuNy40LjQuNi44LjggMS4ybC4yIDEuNXYuMmMwIC41IDAgMS0uMiAxLjVsLS43IDEuMmEzLjEgMy4xIDAgMCAxLTIuNSAxYy0uNiAwLTEgMC0xLjUtLjJhMy41IDMuNSAwIDAgMS0xLjgtMmwtLjItMS41Wm0xLjctLjJ2MS4ybC40LjdjLjEuMi4zLjQuNi41LjIuMi40LjIuOC4yYTEuNiAxLjYgMCAwIDAgMS4zLS43Yy4yLS4yLjMtLjUuMy0uOGwuMS0xdi0xbC0uNC0uOGExLjYgMS42IDAgMCAwLTEuMy0uN2MtLjQgMC0uNiAwLS45LjJsLS41LjVjLS4yLjItLjMuNS0uMy44bC0uMSAxWm04LjUtMlYyNWgtMS43di03LjRoMS42djEuNlptLS4zIDEuOEg5NGMwLS41LjEtMSAuMy0xLjQuMS0uNS4zLS44LjYtMS4xYTIuNyAyLjcgMCAwIDEgMi0xbDEgLjEuOC41LjUuOC4xIDEuM1YyNWgtMS42di00LjhjMC0uMyAwLS42LS4yLS44YS45LjkgMCAwIDAtLjQtLjVsLS43LS4xYTEuNiAxLjYgMCAwIDAtMS40LjdsLS4zLjctLjEuOFptMTAuOCAyLS4xLS40YTEgMSAwIDAgMC0uNS0uNGwtMS0uMy0xLS4zYTIgMiAwIDAgMS0uOS0uNSAxIDEgMCAwIDEtLjUtLjZjLS4yLS4yLS4yLS41LS4yLS44IDAtLjMgMC0uNi4yLS45bC41LS43IDEtLjUgMS4xLS4xYy42IDAgMS4yIDAgMS42LjMuNS4yLjguNSAxIC44LjMuNC40LjguNCAxLjJoLTEuN2wtLjEtLjVhMSAxIDAgMCAwLS40LS41bC0uOC0uMS0uNy4xYTEgMSAwIDAgMC0uNC40LjkuOSAwIDAgMCAwIC44bC4yLjIuNS4yLjcuMiAxLjUuNWMuNC4yLjcuNC45LjcuMi4zLjMuNy4zIDEuMWEyIDIgMCAwIDEtLjggMS42IDMgMyAwIDAgMS0xIC41bC0xLjIuMWMtLjYgMC0xLjIgMC0xLjctLjMtLjQtLjMtLjgtLjYtMS0xLS4zLS4zLS40LS43LS40LTEuMWgxLjZjMCAuMy4xLjUuMy43bC41LjQuOC4xLjctLjFjLjIgMCAuNC0uMi41LS4zbC4xLS41Wm03LjguM3YtNS43aDEuNlYyNWgtMS41bC0uMS0xLjdabS4yLTEuNmguNmMwIC41IDAgMS0uMiAxLjRsLS41IDFjLS4yLjQtLjUuNi0uOS44YTMgMyAwIDAgMS0xLjMuMiAzIDMgMCAwIDEtMS0uMSAyIDIgMCAwIDEtLjctLjUgMiAyIDAgMCAxLS41LS45IDQgNCAwIDAgMS0uMi0xLjJ2LTQuOGgxLjZ2NC44bC4xLjcuMy40LjQuMi40LjFjLjUgMCAuOSAwIDEuMS0uMy4zLS4yLjUtLjQuNi0uN2wuMi0xWm01LTIuNlYyNWgtMS42di03LjRoMS41bC4xIDEuNVptLS4zIDJoLS41YzAtLjYgMC0xIC4yLTEuNWwuNS0xLjEgMS0uOGEzIDMgMCAwIDEgMS4yLS4ybDEgLjEuNy41LjUuOC4xIDEuMlYyNWgtMS42di00LjhjMC0uMyAwLS42LS4yLS44YS45LjkgMCAwIDAtLjQtLjVsLS43LS4xYy0uMyAwLS42IDAtLjguMi0uMiAwLS40LjMtLjYuNWwtLjMuN3YuOFptNC42LS41LS43LjIuMS0xLjMuNi0xYTIuNiAyLjYgMCAwIDEgMi4xLTFsMSAuMWMuNC4xLjYuMy44LjUuMi4yLjQuNS41LjlsLjIgMS4yVjI1aC0xLjd2LTQuOGMwLS40IDAtLjctLjItLjlhLjkuOSAwIDAgMC0uNC0uNGwtLjctLjEtLjcuMS0uNS40LS4zLjZ2LjdaTTEzMSAxOXY4LjhoLTEuN1YxNy42aDEuNWwuMiAxLjRabTQuOCAyLjJ2LjJjMCAuNSAwIDEtLjIgMS41bC0uNiAxLjJjLS4yLjMtLjUuNi0uOS44bC0xLjIuMmMtLjQgMC0uOCAwLTEuMi0uMmwtLjktLjdhNCA0IDAgMCAxLS41LTEuMmMtLjItLjQtLjMtLjktLjMtMS40di0uNWMwLS42LjEtMSAuMy0xLjVsLjUtMS4xYTIuNSAyLjUgMCAwIDEgMi4xLTFjLjUgMCAuOSAwIDEuMi4ybDEgLjguNSAxLjIuMiAxLjVabS0xLjcuMnYtMS4ybC0uNC0uN2MwLS4yLS4zLS40LS41LS41bC0uNy0uMi0uOC4xLS41LjRhMSAxIDAgMCAwLS4zLjdsLS4yLjdWMjJjMCAuNC4xLjcuMyAxbC41LjYgMSAuMmMuMyAwIC41IDAgLjctLjIuMiAwIC40LS4zLjUtLjUuMi0uMi4zLS41LjMtLjhsLjEtMVptNi43LTMuOHYxLjJoLTQuMXYtMS4yaDQuMVptLTMtMS44aDEuN1YyM2wuMS41LjMuMmguNGEyLjUgMi41IDAgMCAwIC42IDBWMjVhNC4yIDQuMiAwIDAgMS0xLjEuMWMtLjQgMC0uNyAwLTEtLjItLjMtLjEtLjUtLjMtLjctLjYtLjItLjMtLjItLjctLjItMS4ydi03LjNabTYuMyAxLjhWMjVoLTEuNnYtNy40aDEuNlptLTEuOC0yIC4zLS42LjctLjJjLjMgMCAuNSAwIC43LjIuMi4yLjIuNC4yLjcgMCAuMiAwIC40LS4yLjZhMSAxIDAgMCAxLS43LjIgMSAxIDAgMCAxLS43LS4yLjguOCAwIDAgMS0uMy0uNlptMy42IDUuOHYtLjJjMC0uNSAwLTEgLjItMS41LjEtLjQuNC0uOC43LTEuMmEzIDMgMCAwIDEgMS0uN2wxLjUtLjNjLjUgMCAxIDAgMS40LjMuNS4xLjguNCAxLjEuN2wuNyAxLjJjLjIuNS4yIDEgLjIgMS41di4yYzAgLjUgMCAxLS4yIDEuNWwtLjcgMS4yYTMuMiAzLjIgMCAwIDEtMi41IDFjLS41IDAtMSAwLTEuNS0uMmwtMS0uOGMtLjMtLjQtLjYtLjgtLjctMS4yLS4yLS41LS4yLTEtLjItMS41Wm0xLjYtLjJ2LjJsLjEgMSAuMy43LjYuNWMuMi4yLjUuMi44LjJhMS42IDEuNiAwIDAgMCAxLjQtLjdsLjMtLjh2LTJjMC0uMy0uMi0uNi0uNC0uOGExLjYgMS42IDAgMCAwLTEuMy0uN2MtLjMgMC0uNiAwLS44LjJsLS42LjUtLjMuOC0uMSAxWm04LjUtMlYyNWgtMS43di03LjRoMS42bC4xIDEuNlptLS4zIDEuOGgtLjVjMC0uNSAwLTEgLjItMS40LjEtLjUuMy0uOC42LTEuMWEyLjYgMi42IDAgMCAxIDIuMS0xbDEgLjEuNy41LjUuOC4yIDEuM1YyNWgtMS43di00LjhsLS4xLS44YS45LjkgMCAwIDAtLjUtLjVsLS43LS4xYTEuNiAxLjYgMCAwIDAtMS4zLjdsLS40LjctLjEuOFoiLz48cGF0aCBmaWxsPSIjM0I5MTFDIiBkPSJNOTMuNSA1OC4zVjg0aC00LjJWNjMuM0w4MyA2NS41VjYybDEwLTMuN2guNVptMTEuNyAxNS4yVjg0SDEwNFY3My41aDEuMlptNC42IDMuMS0zLjMgMy41LTEuOCAxLjh2LTEuM2wxLjItMS42IDIuMy0yLjRoMS42Wm0tMS4yIDcuNC0yLjYtMy41LjYtMS4yIDMuNSA0LjdoLTEuNVptNS41LTMgMi03aC45bC0uNiAyLjctMiA3LjNoLTFsLjctM1ptLTItNyAxLjUgNi45LjUgMy4xaC0xbC0yLjQtMTBoMS4zWm03LjUgNi44TDEyMSA3NGgxLjNMMTIwIDg0aC0xbC42LTMuMlpNMTE3IDc0bDIgNyAuNyAzaC0xbC0yLTcuMy0uNi0yLjdoMVoiLz48cGF0aCBmaWxsPSJ1cmwoI2MpIiBmaWxsLW9wYWNpdHk9Ii4zIiBkPSJNMTQ3LjQgNzEuOGg3LjJ2OC45bC0zLjYgMi42LTMuNi0yLjZ2LTlaIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTUwLjcgODMuOWMuMi4yLjQuMi42IDBsMy4zLTMuMi0uNy0uNy0xLjQgMS40Yy0uNC40LTEgLjEtMS0uNHYtMy45aC0xdjRjMCAuNC0uNi43LTEgLjNsLTEuNC0xLjQtLjcuNyAzLjMgMy4yWiIgY2xpcC1ydWxlPSJldmVub2RkIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZD0iTTE2Mi42IDc5Ljh2MS4xaC0zLjF2LTEuMWgzWm01LjItNC40Vjg0aC0xLjR2LTYuOWwtMiAuN3YtMS4xbDMuMy0xLjNoLjFabTcuNSAwaC4ydjEuMmgtLjFjLS41IDAtMSAwLTEuMy4ybC0xIC42LS40IDFjLS4yLjQtLjIuOC0uMiAxLjJ2MS4zbC4xIDEgLjMuNi41LjRoMS4xbC41LS40LjItLjZhMi41IDIuNSAwIDAgMC0uMi0ybC0uNS0uMy0uNi0uMWMtLjMgMC0uNSAwLS43LjItLjMuMS0uNC4zLS42LjVsLS4yLjctLjUtLjJjMC0uNCAwLS43LjItMWwuNS0uNy44LS41LjktLjJjLjQgMCAuOC4xIDEgLjMuNC4xLjYuMy44LjZsLjUgMWEzLjcgMy43IDAgMCAxIDAgMi4xbC0uNiAxLS45LjYtMS4xLjJjLS41IDAtLjkgMC0xLjMtLjJsLS45LS44LS41LTFjLS4yLS40LS4yLS45LS4yLTEuM3YtLjZjMC0uNyAwLTEuMy4yLTEuOC4yLS42LjQtMS4xLjgtMS42LjMtLjQuOC0uOCAxLjMtMWE0IDQgMCAwIDEgMi0uNFptMy4zIDIuMlY3N2MwLS4zIDAtLjYuMi0uOS4xLS4yLjMtLjUuNi0uNi4zLS4yLjYtLjMgMS0uM3MuNy4xIDEgLjNsLjYuNi4yIDF2LjRjMCAuMyAwIC42LS4yLjhsLS42LjctMSAuMmEyIDIgMCAwIDEtMS0uMmMtLjMtLjItLjUtLjQtLjYtLjdsLS4yLS44Wm0xLS41di45bC4zLjMuNS4yYy4yIDAgLjMgMCAuNS0uMmwuMi0uMy4xLS40di0uOWwtLjQtLjRhLjguOCAwIDAgMC0uNC0uMWwtLjUuMS0uMi40LS4xLjRabTMuMSA1LjN2LS41YzAtLjMgMC0uNi4yLS45bC42LS42Yy4zLS4yLjYtLjIgMS0uMnMuNyAwIDEgLjJsLjYuNi4yIDF2LjRjMCAuMyAwIC42LS4yLjgtLjEuMy0uMy41LS42LjdsLTEgLjJhMiAyIDAgMCAxLTEtLjJsLS42LS43LS4yLS44Wm0xLS41di45bC40LjRoLjlsLjMtLjR2LTEuM2EuOC44IDAgMCAwLS44LS41bC0uNS4xLS4yLjQtLjEuNFptMS4yLTUuMi00LjIgNi43LS43LS41IDQuMS02LjYuOC40WiIvPjxnIGNsaXAtcGF0aD0idXJsKCNkKSI+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuMSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTg4IDEyNUgxMnYtMWgxNzZ2MVoiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii45IiBzdHJva2Utd2lkdGg9IjEuNSIgZD0iTTEyIDEzMy44aDkuM2wxMy4zIDkuMkw0NSAxMTIuMmwyMS42IDIyLjYgMjMuNy0yMi42IDcuMiAxIDE0LjQgMjkuOGgxNC40bDEyLjQtMjYuN2g5LjNsOS0xNC4zIDEyLjQgNC4xIDE4LjUgMTAuMyIvPjwvZz48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImMiIHgxPSIxNTAuNyIgeDI9IjE1MC43IiB5MT0iODQiIHkyPSI3MS44IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojZmZmO3N0b3Atb3BhY2l0eToxIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdHlsZT0ic3RvcC1jb2xvcjojZmZmO3N0b3Atb3BhY2l0eTowIi8+PC9saW5lYXJHcmFkaWVudD48Y2xpcFBhdGggaWQ9ImQiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0xMiA5MGgxNzZ2NjBIMTJ6Ii8+PC9jbGlwUGF0aD48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2UiIHRyYW5zZm9ybT0ic2NhbGUoLjAwNTAzMTQ1IC4wMDYyODkzMSkiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjE5NS4yIiBoZWlnaHQ9IjE1NS4yIiB4PSIyLjQiIHk9IjIuNCIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMS44Ii8+PGZlQ29tcG9zaXRlIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzE1MTZfNzA4NzgiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzE1MTZfNzA4NzgiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48aW1hZ2UgeGxpbms6aHJlZj0iZGF0YTppbWFnZS9qcGVnO2Jhc2U2NCwvOWovNEFBUVNrWkpSZ0FCQVFBQUFRQUJBQUQvMndCREFBSUJBUUVCQVFJQkFRRUNBZ0lDQWdRREFnSUNBZ1VFQkFNRUJnVUdCZ1lGQmdZR0J3a0lCZ2NKQndZR0NBc0lDUW9LQ2dvS0JnZ0xEQXNLREFrS0Nnci8yd0JEQVFJQ0FnSUNBZ1VEQXdVS0J3WUhDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dyL3dnQVJDQUNmQU1jREFSRUFBaEVCQXhFQi84UUFIUUFBQVFRREFRRUFBQUFBQUFBQUFBQUFCd01GQmdnQkFnUUFDZi9FQUJzQkFBRUZBUUVBQUFBQUFBQUFBQUFBQUFJQUFRTUVCUVlILzlvQURBTUJBQUlRQXhBQUFBRDZNKy9jZW1RYUVLWmlpVGNrZ2phck5UN210WG1rcjNmNnJKazF3RWlGTW0xSmt5U1pCb2JhdXlUdDUyd204bjhsNUxLY3I4ZHZKRTJweDZPeURqelN0WHZJdTFkeloyR2pidER2WnBqMmFNdnZRcHV5UmltN0ptS1J4NnUraWZEdGxONVA1THlXRTVXNURiVEp0WEhRbTR6QlpsVnloT0lLZHVBMVpPcE5ZZlhxVFc5RExMY01yczFraldoTW1RNkcySFdFMkUza3ZKL0plU0tmSTdXSEhRbHpHMlVPV1lWUkdMS2RnZFJHVmJsZUdReURDR1JnWTdtNytaTnJ0ZnFsRFIxcTYxZFlkWVp2SllTNVhac0Z6Znl1eG9TVGRrU0ZWUGhtRmtDSWZPVFZ3MUliRzdJaElvQWpDUVN6TkU3MzRabllqSWwyQ2NXWVZTV3FUUzdKTU9pZlVoWFNjU0VwY250Nm9lVWt1bGgxaE9EYTdkT1BYWjdPb2RJaEYzV1lwREdjUGkwUHJ5d0dPWGdaUjkzdEhwWjhxbVRzYTg3SWtPWGJWTExJbzh0czg3c3N5ODYxVGFKVjBZd3RFZlFBMjV1MTNxY01tSWFoTXFXQkdweENXTjZpWkYrek42QXIzWVNkUEU0dXdyRkZtUnQyZmRrUWVhMWZNdDNmeVdqclVtQmJFTENUSEE5dXJ0WHlMc0lZRkhMUXZHMHJqYmVXUkpSaU0xV05CTFZDbE16c2R5OU9yZ21OQ0RuUm9wRW5udEZSTFpOcWxxNlJkd1BJbUZuUkJqWE1EeWsya3VCbW9wbjJiOWJPZXpRekIrS2QxczB5Sk1GVzZjb3RpbkZLZjZvWHFqK3o5enZMY1M5bEx4RHFsenBjYm9WRzQxVHpjRkxwNHBnSXJHbXRtK2JtWGUrak94bm9NVWNDU1d5UjFtbzJ5M2FnWkhZT1NEYWlWcFFES01Vbnk3SG1Xd3JnZGJPdXBJRXVRZE0zbENYeVlwRkJoS0VSeVVVeGQzNk43M09xdVRTNDdPcVhVTE51TDBJTXRwZ3piRTgxQUFsR1F5c05zcVMxUzVXWFV5ODZGUXlodWNCSWF0REVEMGlrNmFRb0lOSFBVZkw2Qy91eHpPRFRNbHhrTk5vSnJRWDRLbEVxdVJ6SEt6RlpjWElWSXluVVB0SitsbVlXVE1MaE1EN2JRQUlqdFRVVVJNenVJUDdqQzNlb2RMb0wxNlBOVjRjNmVJemRZaWhkZXlmcmNVbjBNMzUwNVZreTJHdlZORFFhT2UzbWRjeTdSY2drVUZpU0ZEVTJ0b0ZpZW9ESlZaR29RQWFhOTdWL000NE1xMjFkcTZlbnpVQjFLdnplcnlmU3pkb2g3QTFlSWJRT2xwVmtpZXgwdG0yUFRjMVF5T2I2NmNsMEM4YTRSVzBjektjVkFhdWdXNzlLSFJGY2NBK2JFRnI2UUhWZnBZL25HeHVUYTlsZWs1UnltaitZT1JiK251aFhHbEs0L3dCUzVUYlZ4aDdyNTNabmJUSnVZTGtaL1pmak96VUF0V0hlSWwzam9UQklSemhFWlBhWUY4OGFrLzBNdTFaUk5EVFdLUVlWTnY2Q1c4VnJaZkpDa2YyVzFxZ3RtRUZRV3hNRmFyU21LTFdWMEVsaXNmUjd1TXF2K1ljeE0wVTNQRXF5WUZvMDJhQUVhZXhkbFY4d2RHNDIxVGxNb0F1ZzRCejlyNlZiK0ZCUVZOYzJ4ZG5UcWlhQW9FMDRFcnh3K3JkTitwbXhmUnhySzZ1ZlNHZW9WdEdoRko2OGN4TmsyOWZ5TldQS3ZVZXZXeVo5Ry90ekZiK0o3V3cvby9uKzlLMkRzVGIxNXZzN0MrcGVPeERFMnhCenZTRXZxZVgxeTlHT1JhTmVzZTFMY25mbC9WOG53UEVkT2s1NE1jMXJ5VEx2dGtsaEdSSzZ1Q0VPSDd1VTZFRVpoR1o2MlYwWXU0djBmTk96QkdxV2c2WmZScmRMd2pIaTdrUm1sWnpHVDE0V1pXaEVjWlZ4dHVLNnVERWlrZklBN0tPODZSRzBwblNWOHdHc0x3T0swVUxNSUtpaG04VXZSRk1NTE1PNlZnWWJWYXBxZUNlWE5IMDE1UjNJRW1HU01GQ1NWSUpXRGdKY3NnZi94QUF3RUFBQ0FnSUJBd01FQVFJR0F3QUFBQUFDQXdFRUFBVVJCaElURkNFaUVDTXhNaFZCVVFjV0pDVXpOQ0FtWWYvYUFBZ0JBUUFCQ0FQak9NNHpqT01MKzBiTzRpbFVPVHVMNUNIWHRRZXdpdk52WVZHVkhJNzZYR2NaeG5HY1p4bkdjWnhuR2NaeG5HY1p4bkdjWnhuSDBnZjZ6MUZSWGFVbVo4UjdMYU10RFd2eFlZS3JHdjdxcldWeEcySGQ0N0hHY1p4bkdjZlRqT000K3ZHY1p4bkdjWnhuR2Njem5HYi9BRlNiUkxlZnlYWjdMOFY2bCtsSlNsemRiVG1RUllWdHVKV2s3Q2hpV0x1SVBtSjR6ak9NNHpqT000empPTTR6ak9NNHpqT01uKzBjWnhtMFRENFNxUkx6VzVhYWRhcGRRYllyQzNyM0UwN1NZc1dnY2J6dmFsc25kcXBpN2VWc3JzUHNBNFJZSkNmNjhaeG5HY1p4bkdjWnhoRUlmdDVobjlPTTQra1IvWE9NNHpacllmaEJWYlZBeVp2VjZ5eUdwRlVoSGdJeloxS3BjR055amFUVzlXNWRFdHUzK1htMHk1VTRCNGt0M2JlcStybFRQSFlpUktPNGVNNHp5cWpQSk0vcncrYzhIUDdpaFEvamo2Y1p4em5HY1p4bXl2VmxXRjE1UzlpTFoxNDE3bEhTRWJHeXNMV3NTcTFLL2svMUxlTU9ncTJkZ1NzMHJLZ0daWlNWWnNnK3I2cG1zaWExOE5sZHRKQzFyaE1TT1FiRlZFZStkdkg0NHoyK250bnRuR1QvQUd6ak9NNHpqTjJWYXUrdmJzTG5ZclljNVZ0ZG1xVzNCRDdVU1Evcm5HUmVSWHR0VTRaRXg3ZzJDS1RKNWFHcHRKSXJCeFV0MllodFNOallwTW1OblhkNDBCNFZ1Qm50ay9qbk5UL3drb3UzT000K2tSbkgxNHpaMDEzcmxkVFArMVlhQ2RSVFl1c3U1WlRIZFhIRlQrUXdwZ1k5NmF2bXhoOUlUY3ZMdkdTRjJxbjNISnNJc2V5ck5PdmNzOWoyVUhCWTg4ZHZweUdkWjZzR2NxM1BxVGhSOTFLWThudzR6dUNQejVWWnhuSDE0eWZiTGN5Kzk2YkRBVjNXeEd1TG1pcWdkSWVLd2poS2dwN3M3QkdmZFlUTGZuMEVydC9sQnhmNjhaWXFvZkhMWm16VnMvWUMxNm9vQ3lWR3FZd0pONmJBRFdnYlNUcGxQb2xjdWtIV1UxbGVJZVlVc2Z4eC93Q016QTUyeVh1VGVCYzg4N1hnOCs2bEVNMWlyQTY4djlOOHZrV1FNUitBajcyZjRkY2VmY0RCUklGM2o5NDhpZ2tiSGZrTEUxK0ptK3QvNWRyTHQxWmUxTm1QVjdWRkRZVmxtMU5hVndBdHFkdnBWOXVjZlhqSm4zN1JnUDZ6aWVZc092WnNMQy9WdVppeHNkc09yYXZ1bForVGpPTWU3d2VSc2RESVN2enZWQVJINDR3bys0T1RFaVhjUFgzejZmN283Uzg0dG5iS1hFZVVOYlZzMkQ3RW92MDBLN0hXK283TU83YTlZOTQrRmpPZmo4KzVaRWNaL3dEY2J0VlJZVlVUcjB2dUw3YzJpVVVtMmhnSHNTc2FEYUNvVFllQVB1MTYvc2ZaTS90YldQWTNqb2dROUxCWnhuR01qNURqYk5WQmRydjhSSkF1a0xXQTRDcnBiblZCMnZTRTZwcGRyTjVIcVc5THJoaFgwdTlCUG03b1VkV2p3RFFhTFI3a3dQOEFXY2U3eFJISHA1YjcyZHRNQnU5ZkpWaWl0V1FrTjAzc04wc2pzYW52VFVCNTdOcUx2aFd1T3hVZThjNWEvQjUwZjJ3Z1lqM3k1dmxyYkFVbmRYWHJ0MWRXZ3JYdjJJYzJ0eFJPbHFKVml3YWRWUkZhVTcwWkJuUnF2VGRQV1laMDA5UDg1dUVEM0xnZVI2M3RHTlpESUdzOGNJN1NSNXdMT3dOaGVWZHo3a0hoN1VRK01idTZaN0NISHJyVk1yRUpkdHY5UTloaTJwS25UWnA5UGJHenRiNnJXUk5udndISEEvS3hicU5NMTUwa3dJVEV3Vm40OXcrT2wzUzV0NFAvQUc0YklMOGhSNzlUdVNXbk9ZbmJwcTBGcEhjOVlPU0hobnBsRis2bTdXbnB5Smk2OFNiRFB6SFdnUTNUR3V2eng3Wkg5ODUrUlJuN0hPTXBVcGozM1MweHZQRG5wZSsxMk4zTU1LMHBPZXNHRXpuUlB3c280czNHVUZTK3g1eVhKZVJPMlhIV2xocDlPdldpbDN0SFlhK2YxSjBPajdXNXRXMGRURGFQMGJSanVQZjF5alZONTEzcDUxY2QvWEhZMmlyTkpzVnEyMTlHVldGRmkxQ3RwYjJhekZaWGFqVlZmSWNGR2VUK3dsN3pPQ2Z6TENLSmpMdno2bUVjVythRE0ybmVuYjEzT3VMTXE3THg5TFF5bnNrSW5xQS85dWpoVmxsenl4bEp6aDZ5c1ZvcWNIcDdLNWlsUUlCa29vYTJUN2c2cnJCWDNKRU42NDZyVk95amQrc2JwbkVYWllkMHZabE80UlZIbWJHcElxN2cySVRiTTdKT0cwVjU2dlZ2c2F6ZCtIeU85VEVSeml0cHUyYkVhN0EybHQxdGxOYWJ6bGtmbTJQVUQ2bi9BQXMyYlU3YWJ0b2QzY1V2MUNyVjhyRzNyelZ2N1B0cUdvcTdQSGQ4a2JrcGRUQUdVSC85bWNxV3E0OVZzU0d1T1QxejR4VDRoWVJsRzFIcUxJNXV5RXQ2RGN1dVNXdmJ4NW9iMDRZbnJKaG1oc0p5Nlh3ck5YcFFVbTJtSjJqdjkzV3hadnIyZW1WQ3V0c3AvamxwZnI5bzJiVStkVm00N1puS2wzTFk3aHF4cVhiSHJFcmkxYU55VFhHMGU0TGgrUTNXekpoNWFyR25ZcEFObEJkdll1cjJReVNDNWQ3dkdFRGY3Rkh3bXhQOHF6WWhycGthTHNtODRURUlyM0hjTWREMmViWXhNdmNoeXZEa0RXbWpNVFFlc2RjY3RVVUJXV0s5ZXp0ZUU1YmMyYmlTUFd2QXRlNVdVMnJLb3VTaW9zRUVvRG1SaGdnd1E1OVRQdDVsRXZ6L0FDSUpMbXk2WUlHVjUrMW5xUTltanlIcGhHWFRBMkRtSTJOZG5ZQURaUUNJTWE3d2o1VFdaSG9XOXJkcFhHeE1UL0lLVW1KejFjbGVESWNtcTF6eWR1cTZQdFlWang2L3haTFlxckloMWIrK0ZzeGUzSzlaR0NPMGRFV2VFOTVacXJBVmx1MWxIeExkVkRHSWt0dlhPdUtjallMZVUrQk95TjdaV3AzZFNIenNYc2VXand1allhcUNuMXNwc2NtVi93RGtIejRtMW0xeWhxM3lZVEMyK3ByZWltdE1OQ05XY1pJVXpudlo0dFppWDB3Y0xpdFdLanBJc2xkU2YwS3lScmdNZDNOL2VoWlhXRHRiTmRwRnlNNjh5bjMvQUlzOC84UUFRUkFBQVFNREFRUUhBd2dKQkFNQUFBQUFBUUFDRVFNU0lURUVJa0ZSRUJNak1tRnhnVUpTa1FVZ01HS2hzY0hSRkROQVExTlVjcExoRlNSamc2S3o4UC9hQUFnQkFRQUpQd1A1MVVBeGdTclIxWnU2dWNqd25ueFZFMUtSQUxXdDc3Uno4VTVwWjlUOW1ZTGhXRUU4dUtOK3ppS2xFVm00ZDRwN3JLUGRvZThKMThZVkNwdlB1WnBuaW05VzdIZS9ZK0hSdFQyYitRWGJoeHhDY0tQWjNONWE2ZUFWQmdZMXhrazVPZEIrYStVR2JRV1BnTmZVMzJFY3VZVzFzUFdNc3FNc3l6aXFvTy9ZOXBHaFRyQ0RCYS9IN0hvYTJZOGl0U3pMZ05NNFBrdXkzTzF0RzdNbmd0aTZ4dFZvbDlQZ2ZKYklEZFR0WTl0Zk1qSWdqMVd6TnJCeGFHQ2wzdlg3bFNZWFhRS05QQXBjZ2ZGVXR5cDNNNWxIVFg2RndDYTUza1BuOTQxZndLZ050dHNqWG1tM1V5MXo4QmNrVFRyRExhbE14SGlWVHIxaXh3ZDJaM2o0eHpWVGFLTFcxUUtiR1RlQlB0VDl5MlNwV0YrNVZacjVGcy9jbWlCM2kxK25nZ1A2MmxHUno2WGowVkYzcmhXdCsxVlhIN0V3ZlFPN1RXMCtXcWYzR05tci9oTk4wa3Vqa09TcHpJblBCY2NnSDcraHpta3ZHL1RkQjBDRld2RlFHNWxYUHdWQTA1YTY1d3JaT21xcE1Ob0R2MGlsbU9VajBSRk5oZmFhcmhOMG5nUHhWU3FQc0JWT2ZQS0gwZ1liYmcxcEdYbU82cWxOcjNNd3gyZlJVNU5sbmdSK2FNNmRJY042YjR3RTRFY3dxUU5UMlNOVUdQbU1Da0xrSDdNK2UwSnBRWm4zZnhXeXVnTnhWWk5wYnpJNEtvMnBmM1lNZ3JYM2VodWxRL1JSdVhWR2s4MGJYdnBDMGU3Q3FmdVlnOTFwUC8ycTkxYWpvYkRubnZMYktsMUhiWE5ZNEhncUFyZlhwYWowS3FTUnEzaVBSTW5zK2ZpcmFqTExTTGQ1UE5QYVE3dEd4dVFjNUNxR201dFRWait6K1BOWlppMHhsYVJ2RG9lUGluL0Q1K0d2WUdsM3FodXNvaTRqWGpuekNHT3JCZTdtRTZZeEtNSG1FYml2aDZvdHp0N3U2NlZ3Vk9TTkhjUjZxcjEvWjZQT2RlYUpvVG94K0x2VlVCalF4bFRXWjdYV25OczZKL1ZNcDFMT3JjMGtPSjB4d1RuaXhrbDdlR1kxSGltaDI3M28xVk1mRDZCbDkxdE1NNDZMZkRkbGt4N2VUbE9IV1dOdFBMR2l4RHlDc2RQOC93RG1zODJwbHZtcGtqTUhWTkJHaEJRZEQ5b2JUTEozY3FoRzRkK252RC9DRGFqUlZFT0IwWGE3OVdWcFlJK2h5d1ZTSEQ3TGs4Tkw5a21sOXFvMjAyc0V1YzNROFNCeFZZVkhDcGw0NDlJa3NZWFI2S1oydDVxVk02Wi96OHoxQ3hadE5KMjlqMmtmWk9GVGczQ2FsUHpUMnhUcms1T1poVldzc3h2T2lmTG10a3J2WnpZTFcvM0ZWYU5HOXNpNXBjN3B3T25lZFdKRk54N3VNbGJTN3ZPTlFBUk1uQ294YnNNaTMyVEtadjI2czkzbW90SmE0QnZEQ3FaOTBhOUEvY243a043T2VubXE3V242eFIwc1Ava0VZQmFNK2laVWRhWmNSd0NxMVhCMVhOanJkVnNuV2RYdHhEUzRhQ0FpUWVUdUk4MHdVSnh2SFgxUnVIUHBiTG5HR2hHLzZ2c2owNHIyYWUwT0g5aWIvdWVxQmI5ZE11UDZIMmc5MDNvR2U5MXZKQzB1b2h4RlBBMVRBUElkSHVmZ1VjNTZIVWF2OFNha1FoTXV3S2JOZml0L2R5TGhJVEtwQjJnSHRxbDNwNUozb25nTk9ESG1xV2FPME9EZDNpRHFuRzNyNmJnMmROM29ZNGRWdFBlUDlCV3pZNFdWMVMybjBoeXJWS1lBNDBGVXB1SWJ1eVlLcFhuL0FJM2dwbG5VMEhOWUNmZjFWWUVrVzA4ZDRmbW55MTFKZ3o3dldLcFVGRm8xdW1QVGlGVWExejlqdWR4OW80VEdPeHdkQzJaL3BCVlVOZTBhTzEwUXhuUUttZk53VkNURWZxK0MyYkg2VzA2Y0RRSS9CT0FickNPUlViOTZwMzFYQVdDTUxaR3NkcnZFcXAzZHRkTEM5TWs0em90cHBzSDlVbFZUVXJsemJjK1BTZVNIQWFyWmFmZTl4Q0dHclNCaFBxZFhKbmV5MVZSK3JwQnRudGk5TmpFZVhueVU1cG52ZVNwZ2cxR3QzRHpNS2p4eEJWTGRQeWNCNjNMdWdtU1FpZlNRcTN4TXFoaHRlZzdkL3BlMVV5VHlidkxRUmpxenpWVzAyOFNxelpGNFBaL1Uvd0FMcTJ5NXIyem9aYWpFdXhhZkZWSFE3bC9VQitLclNRUk9QblJIWENaSEpxb3ZkVDRmVlRSSjZ0MXJCcHZvdzRVanVqaU9SVlNiTU8rQy9tYVgvc0NZT3pxbG11c0lTMy9Ud2JlUm5WTS9kdjE4bHM4Ym1yUVFxcmhtRHY4QTVxdnU5ZzY2UndlNlVPc2hoY01ZVEFCMVVuS3FFVzdPNkk1d2c1MXpLVUFuM3Fid3JONmtOVzY0aFczdkdRMEtrOHNjSkQrcXdlSzJhcVdjZDlGVXRucFVyUzdCTGlSUDJLMWhwZ0Z6em5WVkhWSXJ3RjhsMUtnR3I3dzFxcDJsdDE0cEdZeHdWb0ZPbHVOT1kveXFEeTlqaHVrUm1UcEtta1hOSUlia1A4a3lITWMwZ2ZCUit2cHpueGxmekw0K3hVQUhIWUFYUDlWeFk1cCtDZG94TzAyZ29UdWpVZUpVZnFUcDVJNzNVdTRweDNxTGhyNEZWSkkyV2dRZGNnSDgxRnJPSmJvdjRCR2ZWVXQ1ckhqWG1DRlRKZDFZdk4rcFZWM1Zpa0RjYysyUlA0S2k0aW5ReWVHcWRoOXR4SENBcXpvclY2dDF4aVkwV29jQmp6Umg4bTdkOFZ0VDdBKzA0MVZacnJ2NGZBWlcwUE1SSmxFaVBlY2pQYURpakprbTBKZ3ZzRE85dzZTSnUvRmVIM3BqTVV5UmE3UkNERGpoMElhTjVwMTF0TDN1VWRGTnJkWXRNeW9ROWdKMjhlcmFDZVFkS2M0T3FHSEdkUk9pTURSV2x6S2VKOFUrWFRjV28zT2M0YnhRT1c0bFZUdWpCaU1JaVNaY3ZhY1BpbjVwdnpoVk5XWStKL05PeGVqd1RuZG5oU2IrUlE3M2lxcHpjMnoxVEtiOTNXVXdPNncyaVYzaU16dzVkREd0dFBCWWM3RXFxeUxCaUZVTENxenlZekxkVTg0TTZMTzVuQ3R1SVJBTjRWZGs2ZDBwN2ZER0Z0azJ1OXhWWWJkZDNjcmE1Sjlsd1cyeEF3SUsydk44M1dsVlltbnJDK1V6SjE3RmJjOC85YTJ4enJlQllxallNNGc4MVZwZjJ1VzIwdDB5T3lLK1VXSC9BS2l0cUR0NlJ1bGJmeHhxdHNhZml0b3AvQXIveEFBcEVBRUFBZ0lCQXdNRUFnTUJBQUFBQUFBQkFCRWhNVUZSWVhHQmthRVFzY0h3SU5FdzRmRkEvOW9BQ0FFQkFBRS9NdjRnSG1aaHllMUc1bFUwbmxZS1c0dnFtQnNzMmlJS3JqVzRhK1BUMGVqL0FPVEFBQWdGc1RSbG5CSWRxeWZpUzg5QWFvZStuanJ1cG84TXNaRzV4Y2VaWGl1d0RJd2MrM3JEZGdpS2J1cytuOGdWL0FWSy93QUFEMFgzZlRXSWM5UkZqMEt4R2hZRnNHTnMvUXpLakp4QzZkUDlFb3I0ZGN0ZUE4NXBxWkRRMlpWTUxaa2N6TWNGTENIVDMvTTM3dUlyL1grSWVIK04rUFdNS2xIMFdnNUhNQWxrNDEzTmJLdnU4czFGWFUzU1g4TE1sa0kxZWVBUldkZ3F1dW9uWGVyblFMb2l1MHpnT0NEbEhHbXk4T21wWnFuOHNoM0pMUEdEWWR2WEV1dkNjbitCOWFCWDVoL2hzeHdXem5iZnFzM2JBVnlaczhZbDc2QTRwZVg3eEtpeG15cmFIeDlwNFFaZW5tWStQY09PK29uS0hIUUhLM3d2QmRRTmZKOVJiNC9oY3FvQVFPZE5kVTYreVllWXVZR0MyT041aGd0N2tNWFNRbVpOQitocGxpeXJucGw5cHlwNGZsUDF5bHZhQTArSmtmT3I2SytqTGZCcitBTjYzUmdxNkYrbHBxWTlGWXNwWGJvbU0rWmlTSGpYOUQ3emUrYlVOYy8xRTJWWEszMWZnNCtqVWl3QmsrOHU1K29QTnNmRVlKOFRtbUxkMWVtT08yNlBrWGRWd2dCcTJYM1BsN1Jxd2FWLzBJaGhYVi9sQUdnUEI5R1dtVWRaNVR5K2dYajZ6cy9nRzR2aGVMK28rRG5Lb1p5NnBCeU5JNUF2KzAzOFVvYlBqNlFVRGlYd0JtdGVkUTg1YVd5QWh2c045NWt1Z1Z5alZYVDZUQXR5eUhRUGZWL1NZa1lXbnFOdjRpdDJvT0J0YWUwU3ExMXR3NXJRUW9KTHAzdHQrWlRwUEg2SEVwUG9xVjlCeGRrSUtEOTZYREkvUE96YXkrL2hpSTIxZjV4K0s3RmhFNmtnTmpseVMvZXlJR3J1dXcxVjlNUU04aUFkaWFadDI3L2F2RExvZXhZOGl5VEdQcWlJUk1XeWVuV1hYZmh2MG1Xb25FMkZyd2NsYW1JNGFxRWM2WjZNekpqb0doNmRZQU5lb2QwdjdURGYwUmRBUHVoemZvcVY5RkJiQkQyb2F5Zng3eldNL3dDT2RuNkVxQ1FrMHZQZTMyaVBZZVRtcFVDR0dZYUxHM0xCV3pDZ0dtOTFxY3psVEsxeFlNZUlaSmpnQzdoVGdEZnJLUVhXVzhOR09BK0Flc1hrdzR6dkl4WHJ1YkR6NVJTS2RtdWt6WExhN2RnNFBpSlorbG84TnpXWHg5S3BVcVZDTXdaN0pMNTFBOG1QVmpnck52WDVON0lOaDVVb0M3ZFllaXhUdmZIYVZyd21sVEk4L3dCVFBKaFlqbUtQSkhTTWN3Q0lNclI2bzNDc0hZeHJiYWJpOVhUcjBtTjZiemFaNnduWDNMZDR3THRFNnhlcTFyTXJHdkdGR29VekwrRFNMWlUyV3lvVE4rRnJSNEN2ZUZ6dFRwbjNXYjh4ZFpiaHFPbzk5NDVtYVNLV09XUHFVYWVEcXEzNGpxQ1l0ckhDTlMrbXBwWGF4a083T3hsZGJMVCtnSGVtWFUzMnk5RDFsNXl4VkJjajcxS1FIT21FS1ZiN0pXT25ObXU5YmFyTDVsN2U2bGgxMGM3K2lndFNwZ21nalFXYURiQTlwNW82N09jZFBlTW1XdkFMUmRvNzhSODdVZFhWVTdwNzlacXVQZ2Z2MW1iSWpZdHc3WWlRYTZCa3M5cHkzb1FHTXNmbkg2MEU2akgxS0hnMDdGaFRpVkRlMkNTNHRNVFc0SDBRcERrUXYzWlFkam5uQjFtV25WOTZyQUxraWdXeExLN0ZjL2VCSkdKc0tudmxTaWFPcFEyMi9TdmI1TlgvQUtsL3ZaU3YyT1kxcW9Dc1lFVU5jRmNXNFhwOXFnMzhhMmVxbDAxcVRWK3p6WHpOekVNMUlpS1ZwYTJUQTZpQXFzM3hHb3kzNGtPcUJwZ2QxOGN3TUhYSlV0Qm1rZEtPVGVsNnVIeE82VjdPeUEyc3FyYW12bVVPb0phSU0zY2hUbjdIQXZtZTgyRFhVaVB5enlqWHpNTVlqYlB2T003aGg0N3djS3pWUHp5dzd0S2gwdHp2eEZ2RDRsOEh3b0drOWRGVFo0UGdMOTNBa1Z3OXFWZjVsQ3NvYVdjaTAvSEVhd2VWVWVWMzB3S3VzREo5eEtKalkvN0k0cXcxYkhYck1LbDhlYXBuWlM2elBTWWNlSVpYTDBMVm9IbUtMRE1HR0VqWGU4T0FYSzdaNmswbFJLQXB1TkxyaXlvUDdtWFdnM0N4MWVabmZvdTZSK3NUVGZWWHBNRllNMjNFZ01vQUxTdUI1Z3NWaTZidVZWVW4zVmN5bk00VzFZdit0UXBFNk1YSDJZbjh3cVZWNjEraEtxSkMxdGRXQVdlWFhpTys3S3V3WlVXT0lBZ1BXK2VJV3gxaFBFYytNczlvZmYxM1dwWW9iUFErMXp1Y05OZEZQdEhpeGRzRHJ6cU91TkxEOFNqMjlQY3AzQXBxMDZJTnZ2SHFJVjlycjNtR3RzNE1KaWJGSDlvYUYrdUJjYnpNaDRsUWVzWTRnb3JBSTBmQXF4RjhucExqbzVpSG82dmVXL01DV0VYM1NMZXN2WGZYSDlTNGpTeCt6alB6TkxGZXVEOEpRc2x3RFhYTGhaSFloMGpiTStUaE9Jb3Nia0d2c0dZODJ1cVd4ZTU5K3JxTUd5RkVsMXVRSHZ4OVcxM2wxSFJIR291S0ZWbG5kY2g2UVUyQnFBWkpqT3I5SlpuM0l2UFM1bk9IVjZ4Kyt4MXdONE51ODVscGE1VmowbWFlTVlVQVA1bHRXbG9BM1ZkZmlLeHRPTkJrMWNZamdqL1R1aFJLd0J5TjFWS2psUVd0ZUhkNGliSXgxMFZLVWxzUzJ2ekVwOGwzNGpDd1MyZFE4VEdjOXlNdWNmUWZFNFZJRHlSM25ScGhTN01iSGRDK2xHcm5pNDdDMDNVVXQxVUdEa1k2VjQ0RHRNdFhZRVVzT2t1dWxIdzFqNlRYUmN0MUdKQkFDdGxGVHhVVk5oOTFkbHBTcFNQeU53VDhyaEFpTGNqeWZTSnExQmJVdlJMRVNSYVVIREtwc3RhdEZ2eEhQaEZBdDRLSmsrQlk3VGpHWHJGT1FOZ2NYRHpyWXB5dk91R1d6bFYzZW5wZHhkUTV4S3c4L2FWc0VsSzdpUFRocTZjNUVzYkFqR3I2R0lkZklMaTUzMWd0RVh6M0wrc2krV0VuWUhxMXhMa29EVk9yWkdzcHZyckNTd0U3TGlBY0lyLzZ2eksxZUZpNEl4NVF0YlhHSU5zYmRRTm43UUtLUWtkUjd4Rzg3eXdHblFQN3dVdmpEV05HdmlDVjFndmJEdzBBNkdzeXJES0NpOXk4UG04Q0M4WlRLaUZUZDBRMU5WcnJMMmdrVnAwWXplWUsvUUM2YjlJY0FpeTRNOHpZaFMrL0RQc0FLeGVJWnFZSFhxSFNYTHUvN21mUWViTUYwYmVCbS84QU1oWk1SZ3VRV0JUUk81WGNyajNXMXZqRTZaa1RYUVFrRmtYWitKdzJsZG9lMFo3dEp2OEFNMDQrV3IwbERwSkJlR0FZd3dXN056UlYyamttcWl0VWVrQ1RXcGlPbXN6NDd4bXVscTIvZVVtMTZabURLb0VaeUpyYmNUUUQxNVZLTFhPTUdRekg2Vkw0UXNHcjJqVUlYN016em5xZjFqcy9ZOFQveEFBb0VBRUFBZ0lDQWdFRUFnTUJBUUFBQUFBQkVTRUFNVUZSWVhHQkVKR2hzY0h3SU9IeE1OSC8yZ0FJQVFFQUFUOGh4dmpoOE1lT0xYc09qdkRDcVBVV29PYzJGMFhRWUJWWUdJY0JsZE56M2tjbEZwZDRaeHRtWUtCeUJ2c0c4dDlWK2s1VDlQdjlNOG5rOG5oOVkvUVo2ekwzY3ZCMWpkTTJCUjJuejRDWmJrVzRUTDRGSmVWSHphSlFxS3dSNDBtTXQ3SDZBUUpHbVZ1UERFU2hwZE1RQ2xxcjZkZFkvVG5qL3dDZllMNHZOZmsrL3dEcGpqYXNManV6eVNTSkYza3gxUUxGd0xvdUNBakhaQmpKVkpvbTlLRFpRZTRkNkQweHh5QXNuSSsrYW95QThZRUxyZWR1Mm15SkZLSGl1Sm54blJTSXVHWVJIWm5Nak5NY2V1UXhIcklaSEk1RC9BK24waC9vSG5ETXFOZlRXdmlFWW1EeVFmbktGT3QxaU5xUm82aWVjSk9sYTdCS1JyaFNFN3lURnZPcTBTK09FeVc4Q1g1NXl5QXoxaDJLeWhIcER3RU1MTkNXVW94ejFhUmFoYmxRWkZZUVJnWVpjVVFGU3hBUXB6WE9iZnFoYVhwTm1UK3BUZjhBZ2ZCa0VaNjgwUFlzZmQrb1Iwb3hiSCtEeDlNTXBmWXFHaS9KdjFtaWZzaE1IbmNYemtTSWxnbk1FM0xqb2VZWUovdzU1bENYYWlFc2JDeWhnMHVXWTdPeXBBSzNrakRuQ0FjNEdvdkk1cFA4bTBsSEJQWlRGejhHbnVpUm5JNURXVVJkUmtTR3hNRXlWaE05bWFINkNHazdjOTBoeiswcy9Mc0IvWHJIckh6L0FBR09Md0VML3ZKKzdsaVBwdGhaL3dBaDd3K3N1SFRTTnZHQ0xZbkhCemJBV2laWXQ2bmtXRlIwcFZPVGFJNW5ndG9OcXpUekg3bUl5QkhtTU9IbitBK2J4aVVZR0tRb0lIWTBteHlRR2FHQ1pTRTYzMXhrRlluZkFMbTBkVGgyTXdqQ3RFdkpGRE1HYnJ5RlhBRzRscDhJeUxxMHZ4cWdOcWg1eWR1MHkvSnlQSTZpeWx4ZzJqNW5QRCsrTU5uNzRkWmV2by9iSFJnU2hIK0ZXR1RhcVVJYm5pRGQ1emh2by9QL0FKNEZjNU1ScTM1QVNCa3p4N3p0d0JPeUo5eHprcDhUSEIvaHBmbmFmY2UyVzV6TlQ1TUNlOEhEUzROUFNNOVlNN09tQnlpNUdPVGM1RDFtWnhwMFNWUmIyQ2MxUGZaTjJXZENyUGxrOUlGV0VYTUZpcE5aNWhjZWcxM1NhN3lDblF1TnMyTnRpeDZTWWRQN1lEakErVEJDQWxkWVBjOHZlZW1SK3NRR2tIT212TXl5WVcxTW0zU05mVjVvRXdyWFk5SkJBNWdPUUloeFBURmh2VVpIYjhXbk43dXBieVd3VUlOYVlUQ3hQTE1ZQzR2NG53ZnZqNHp1dzFOMjVTL0t5OWNaM3NlUGhqNURLUjJUQzBMSk1IMGhIa0tyWTZnOE1tK2hxL1VUMTJxMnpsM3dXN1h2bHBCeFlaTzUwSXVYcmhFL3RZNEpnaG0zR0d3bS9HUjZmZU1tRjdPY1BveittTmUxL0dIMGZUNkJNNEREdDU4azJIaFNBOWczaHIxZUhpWVBLbGU1ZXNqN29WYnFVYUpjY0VZcHh5a21WTElrdjJ3UWZJTjhkTTdNbGVEaHgxNllhL3FjZ2Y4QVRLNnlXOTRKZTVNTnZzdS9GSHdPTkJsZGlHamg0Zk9IRFgvMnlXaGZXUnRxemJPNDJ2SStPWU16eTdNYVVXNXlmUU1vYXFCV2swVk1PV1ZlQnNXQUZ5V0t3ck1XZFZuYmhOYXZkWitKbU0xakk1RDZMNU1IZkJtblp3NGpYRnNwSUNQRnJxSW5qTFltMmpwYmsrQy9iS2lZNW41WWhwNVhqRk1MaGtnNTh1UjRUUDJHN2NQajVlOGpxNS90NE55MERxY3pGbEIrTWp3NG1QbEF4bXpSSUNWVHNZd0NFREExSTFyTjdMZmJ3ZnZqMWhTd2k4RnFBaTlpWHZBN0dQWlpFMm9uRnhCanRrOUhUSFNCclBpak9JYU9NMkRBYVpHUjlIK2tFejU2TDlQS1lMWWlMNUlPU1d5NjljMTJnRkRsU09oMU9TTG1JZ0UwcEdrSlNMbVJGN3NEUEVoMUNmUkF5T0dRbWtjUEdCTTkyQ2lGWkNPZldEY3ZmZjBmS1JrZGdZNVhwUFBqSjJCeXZTTjBac3RjVzdFL0JqbFhQSXJBSnlsMk9ObFNZUWZqcVlvNmh5M2hHaFVvTUVqN01pcG5GNnFFbmNDQm9helMzbHFWaVltRWNzak5YbWYwRXVVVkdKNHdTakFHVDh5RURTU1kyNFlhOFdEaytDTkRvM2pjd1VvNWFVc2RLTk9WNDljMlJQaDFQQ3M1WHNNeGlxVDJmdGtWQTB6SkJUVjk1QTA1cThodEhmOEF6eE5zOTVGZjFRVndoZlpxcGliOG9aZjBCMjFnNWNNU2d1RHJ0c05FTGNsbkUrTWxKQmcyNGM2QVF5VEh2QXRjQ2RRMk4wN1p5ZVZrSzZOdVh1Um15Skx5akJMUW1uN1lHMnlmOXVEN0J2SDBUaTd0TGFyd0FWZjVjREp3WlRaNDJYbWZveURuck92K040VmRveGVYamM3N2llYUcwN1NFd21YdzZpRXJGT1lqa2hRb1RGU1FrazV5UGlGR09WakxJeWxJa2RMS0M3aFNDcDd0NXlQV2pqcjQ3M1lZNFNUbGJzd2NKamdINWNIVUFrUGd4NUtwRHZ3QklDM0hlc2x4M0dveklTNUU3ZFkyWWJIQ0lIU0VJTU5qK1d0UndmbDhtU1R5K2NJVDk4MXlxWDg3Q05kR1FTSW4xVHA0aG5qeDJyT1FxclJBZmR0RndXVHpzdmlOUEdENGlKL2ZpZTM0bko1SGFDR2VJdm9vUE9UZklJYklxMDB6V2ZDaWlkS3pYN3hVY29BNk9iZkd2REw3WmlFVDhpUk1rR0hBMkFOb2JaU3U4ck5ZaWcwZ0F0b3NMN1hJS3Q4Wm1ZbUtHaDJRbUtNNUhOWHllT2NKaTdsYVdPSlloZno0MG1rbDFjVFpvREsxMGJjZ2VySk1YcUc4WWM4WElpaXJsTlVaTHhqQVdSZU5YR0dxQ3BleW93NjlxcDRIOTVOcUlGTEFrdzA0dVhDQ1V1Z09za0I0YkhjRXhCTVlOUnliZ1M5WHA4WUU3ZER3VGZxM29jckFEc1lvbk56bjlNaktxSHhlTHlvTEt2eXpsUFQ5cHJlWEo4UjVWWFdLSFBMSTVkZDhXK3laeFV4ZWhNZ0RWWHJuV25yQnVpT2p1UG1laXppb3VZYjVTNzR3WUdIQ01sQjBUZ0VPMkxJSjVxOEpwdlNwazZOUDZNYVlnd0FRNGF5ZGdGbzk4NC85eXBxRTZuZ2dMS2VLZ0dETVdpMldJSUJBUXBZWUdWWlUyd2tka3NPajhnakZFTU41SnV3bFRnMnQrOGw1SGRoWTVTQWVNYkxBaWhTVnVNRlNwSGtGT2pxL2ZEUDhPWVhVemdXbjJ1QUwrM0dXQklmR1dIVFg3WU12STVzKyt1eFVpeit1UU1JY0dZQkJ0b1J5VnVLa2tSUm5iOEF2ejZ5Njkzd2dpcnJEVGN1c2hCNk5Sc1lmT1A2ZjRRL09SeGUyTXZCbzZaYnpNckU1VWVFWkVHaUxFSEh3aWMzUnJzY0tGSURGRGJXaDFKNnk5dmVJak80WjVaR3haS25ZSFNlTXFjd0p5bENMeXg3SUNGclpaeG1tTGlZb1ZkUHhrclVSckNRT29JYXc1S3F1UWdNdzBzNnl0MStLaUFLUnBnc2VCQkkwTnZPTmhwUndlR1VCQWJ1ZVZYcmdCQmN1bWxlRk41UmpiQ1NGVVZMUFJsQWx0NUFKWlRLYVdBdWFlWEduNUlISUpSWWFGc2s1WHkzSG5MVmtpak1QWXl4VEdYRWk2Z1VSMCtUUzk1M1QyM0JHWXVuRFdOaHJFSWZUaEZxbWFvR3l1Skg3WVdZanNuVTFKeWp1OGtINXpSKzVmakZUSlRlNEJrMStNQVJWd0M1QTZuRmdBZ0dCVDR2T1NFVTVVcnp1SXlZcmdidHRpbVFwZ0lSa3ZmOEErWkhXamlrQUlmVE9mUG9nVW43NG5XbDJFN1BlTUxGWXhDRWRQS1Q4NTNQMy92RktkbFlNQjZpS2ZvUUJWMFk0NG02SmEzSDJNbWttekpBK0treFlDWWJzdm54K3N1c21rQ3A0VVlFeFNlS2djUGM4bVN2Vmt3ek5lemZHR0JZMjRSaW9xOFZyQnZnem51d0NGaU1DWk8vMWtyYnlKaStKbDdPTUlGN0tKMlFIa2M0RFlwWjA3d3JIS0dhMFkrYXdVdjhBZ1JUOWJDSlNxMWVXbjFpdVVhWUZGdFBLeW5HMk9RRzlvSjkyUStFU3RERVlCWm0wQnpFazFqVG9JVCtWNDFoSEpLOGlOQi9PSUI4Nkt0YnJ5WnN5SkwxbFFXM0tXeGRtMnR0dFl0QjFpOHg5UVBqckMxUWc3RUxmSGZHTXVlMk1GRjBzQ1k5WmZiZGFaT09qdkZLZ0ZqTVN2ZFA2elFURnl3Tno1UVAzd1QzWUdwUjB4KzdrSk5oalJJUjRJd1VMNzZDSHNuRzVVT2JvN0lqMWcxd3pBczNYM3hHeWdDdWZuZjhBdkkwbDhib3YrOGVadVZ6T2UrVDV3NDIrbHlvNEE1Q1ZqTUlKOXRPTVMzbHVnaUc3M3FzakJkQUpuYmgwWDg1ZTZrZ1QyQ0xhd2pxYkswTHdOUG5LTnk0T09VbUJXb085YXlHQ3lDbXFSb3Y4T0VKU0V6TStmR0NsVU5qZlprMUpDSVIzT1lNbTFvb3RqNEVQT1hCbzQwSEpxVFdzU29TOElOL1lCazhTc1VkYkZKM2hNS2k0dUo1YnlWbGdIb3JCNHc0QWlIdG1LQ2NQWHluakZPR0k2a3RMTmhTNnh3Wmh1eFdQa3VSblhvWUhIZVFSa1UyS2ZMakd1L2s2cmJlVzNFN2cvYmtoSnA4SWY1Z3dvWnhEUkFsT01qamtTZjNuTmp4NlFJNThaQTI0Q3FwaXZiS3ovRGVuNVkzM0lJRzNneC9LVnJwY0QvTXpmLy9FQUNjUUFRRUFBZ01BQWdJQ0FnTUJBUUFBQUFFUkFDRXhRVkZoY1lHUkVMR2h3U0RSNGZEeC85b0FDQUVCQUFFL0VKUmhsZnc2MllwV013QWwvd0RkZjlQbkVFaUhtcFUxU3VodzFib1hmSlZkbktCeXdrL0tQUE13WXBheVB2MG9EYkJBRkl3dkpqREdMbkJ3SXBqa0I2eUJ4RXhXRkJwLzRxcVRianl4UTRHSm1ORUExY0FGYVVlVm40ZjJ1QUM4MTRkNHQyMGp2SnB0Z1U2UnpGUXVZTVM1L3NCYUlxazdSMUJvR0FlTjdvbmFTRlRJTU1wM052a0NtaVRPQ1lzVXlFeDFaakt3eFpFTXFMckdmTWt5ZitFUzlHYWppOFYyTTJCTWhPWGswSCt2N2ZXUXh1MGR1enlxU3lDMHBvTS8yczBoYUswU2hnTHkxSGlXSkZxY3E4NWl0QUhHcm4vUGhCc01pT2p5SkJNQnRDVU9JR1FkT0FNQTNCRkVneXZxOENKM1EycE5sQlpqekRpQlltSjd3a3hHWDNHZkVmeXZrWk9UOGZyRm1UNHhJT2FUZFF2WHY4UDdUSUpqZ2VjS01uRDdHN0JSbTFXYURpbzlwV0JNc0RWcEdxTUNSM3RFY0JDZHlHZ3c3bVp0dHVpbldad2JDVXQzOWFVTnBMWGdaSFVBNHJDMkdnWWxqNjZybWFodllDR0tzZU5DcElTSmdzdUo0WnZQb09KOW1PUzY2eGVPeVA0VUdGNXNrWTg1K1V2OFZ5WmdtTC9VdzQ0Y3ZGaFhQQ0hSNmREK0h3WXBrUzV1ZXQzR08zRGM2R3Y4ZDFhaTdCVlJjdDhDSHFOVExVVnJpR0JYU0Fud2NWVFByUU5ERWtkMjEwUld0S2tVUTBKaG9KY0owVHlyNW01U3BwVWdVTkdNellDcG9HbE9kclZwNVhJME5LN1dFR2xLcFZGRFhBNEpuUUVmMFRUa3J3WlQwdWRKKzNHczh4L3dFV05oNWY3alE0UlkvRC85MSs4R3Z2RFQ4QWY4NU1SZDJ0OTJPSHdKMWpyZ2NGckx6d1BQME92bXVBdWk0bVlNc01FdE5hNDR3RlNDcHNMSE9UWkFuREFFYTBzc1JQRVR2YlJVaHpIVTNFVnlBcEVOdWhKeUVQVFRYbHdFSUVwcXM2N1ZiZ2JiUGNYSzNHM0ZrWVFTNDh4RG9ZN0pEUzNLSEgzMVYxREFMbHQ4TGpMZXFyZXkyWkRkZ3dROC9YcFZ4TkxKNHhoeXFoN28xSWgwZ1ZCYy93RDFPYTJUem53QitqRWhRaGg3NUlVUjhSMlpQbjlQQm8vVDVZby9TdjhBWDhIVzRvSmZvKzNCOWNhQU03WXVQOE5vSVRqb0NyYUJ5NkhDVy9wZG1zajFLTVlVb0d2V2FsamQ5NnJVRkE0T3RFaWtCSTdhMytBQmh5aWpKMGhFL1ppSTNBd2ZBY0lPV3BvZlhjbUhrNkJYMFJIRkg0anpjdWhCeHVEbVFJaWZiRVE3UVlZSVlRUy9ZWjRxZ3lITGFCaGJNVFdnSnpaNmFKT1NnNEs0Z2dqOEFaWExla0RLUDZyUmFnTE1lNUpReFFGNTZ3dFFBWXU4TCtFSjlGRHYramhqaHdGRXh0bzBIci8wZDVJRFJyRVZrVmNNbGptM3VYbXVZU1E3NldIc01ZOUMxMnFpbG51S1hZeFF2QXQwamEyMFJjSmd1bStXaDgzZWV6TisrU3p4Vm53bU13RTJZR09lZEFjcTZNWWNoLzhBTDBWSmMxaGc5SDRuYnR4VUZ1UEdEcVRYQWhzcGY5S29ZNC9vSXQ0VDhJem5xemdrSUJKT1RwRXd3cWZvdXdiMDVlVWpjTG1oTWhtWU5yNEYwY3VlZ0JpV0drMTRTWE5tMzh3aFJlbUdqVks0SHdJc0ZqanlqZVRTN2NrVkVmVEhnMlNoVS9DNVJWaC84SmNuR1Bsc0hoL0NUbG9uOEN2dXBjSVdvdlF2NjVYRGxoaTgvZUFOQVdoRzdDWU1WRHVoREFCWW9GTFVUL2NTaXFkcVV1VTRKWUZ0b0NmRW11c1dXNmF1azJVTmE1RHJrTzg0TkRKMDU3d2xrOFRJZ2o4ZUZjTXFlb3FZdWV2NzNtK3JRWlA1MTRNa0tlcjNUV0NjOUttZWx2M000bGl1ZXh4T0ppWGNleHgzcVl5M3FVOWNXbW93eVZuZW42SXZLK1lFMWtZc2tmYTNWUG95ajl4YUpPelhRRzlZQUU4aC9yakRrTGhrRm4wd2RZZzNocjVjMytIeFEvMS9iOGNZL2JBaUJqRndUblJEWXc1WWE3YzJ3dktrU2hnbDVRdWJaZTZMd1RsQkRFQnNJWTBBSXRRQlptd0ZYNlA0OHgzek9WdCt6bWw4L3dENEgzZzJZNS8wTng0RUlzRmo4TW5UcCtNU0czcDE3NFlmZGZweVVydmcyam1aZjlZUXM1TExnbEdpVXppeEw3d3ZvRTNPWWRISU00NGFUdmhHRHFxN3hPSnZZanFEVEl4KzJUTUpDcnFId0JkTWZEU2F5RWg1eUtnbnhoRnhPVGhpNlZMYk5kN2VNRjd0cnhIMWk1TXMrTmxjTmR5Z1BPWkRGY2JsZVRrclVVcmJISXVuRlUvYUVFMm9WR1J0V2g4QWNRZ0NVWnBjaFhJSHc1d3JDbXdJMmdvV0Z6Vyt2V1dvUWh5eW5rTG5tL2xqZ0FPM0lrZEJHVDVhaEFvcmtrMmJFdTBUakM2NDV6VXc0aGlqMGU3ekxDS29vQTZRSzgvVFhMUVNibWc1TEVuMnlGT3o2UUFnQ2dsMDNrdnBKdmdhZ3JPSU10MkxvWFZDNzhFMk9xNFdOeGNBSGJrMEJUOWYralArMmMrOFpSRUVGeXE2RDVjZWJwdWZveUc2TDRoeWZqUkJQU1Rva2hBTE1sb0xRVGZWcFJWTExiUnR6SUV4c1BPbFl3bURJZHNtMnVzaFNRckFHSmtOSWxIdGFRV0RDdTV0NFB3dVhqdm5BdHNzZmxuVjcyaUtIU2lheE9Lam5RQXo5ektpam93SGNBUUs3V0dUWlByeExDNlpCSnF3TjVkekpaNU05TlpWUUtZTmpzeTRFQnZGYmhsS1FPK2tha28wbWUwWmI0RmlsVWlrRXJhaVZzUnZ4R1E1TEZDaUF6SFlGa0Y5UDRjWU1SNFVQb2RaY2hLSlprcjhaWmhZUUVCTGFYMDFpcWc2ZjRZSGRCc3B2aWtocnB3djluVnlQZWRRSkhTd21WOGtGUWxWVU5vUkNVZzFFTjY1cU1PaGlheHg1TmFPem5tYk9oZ2doUnhRMGpiZzFMak9HamRkSmMwQ2lvWHpmblJtcjcwa2l0QjVjRFV2MDU2N2dZdFdocXB2QncxOFpXMkxyb0xERTlWY2llQkJIa0NGaE1PdU04OEFBUlZzUzRRSFdXZ29BNnhKUGl3V3ZSQ1c0Sks2YXNxWEdEcys2U2tsdUJGMllTRlc4WmdkQW9URjVtWS9IWG4rY2hQbDBtNW9sN0V6YU14NEpVWURWMjA4czFqbnB3czJsV3JnSmdMVHliVm82b0NnK1JnQWNNMWVORVFXZ0JQY1dwZ0tOUkVBVk5QWWc0M1hBQkRBMEExRGJZY0Q4YUhlTFZ0eDhHUUVBcUs5aGU4aUlBMVMwcXNjL3dBK2dPUlNxT200VCtMZDdOWkZtNmROTVRPTU9WSzBHd3dYOXBETlJmaWVaVlpqYkRLL2hRbW4zR1dGN1RUTTJUckdkS2dDcHh5ajhheGdSOG1vTUZRMFNLckYzZ3VPS2ZDcEZUWGJGUWdSTnpkVEY4QXc0Q0xhOFkvR0RQVXF6cVFBd2VtY1owUzhta1JGTHhnQjRORE84S0VXS1F1TTJWZUU5UjI5SGlTR0NYVWQyZlFJZm95VW5pUDN3UVRkR0hxbVJkbjVjbWg4WTlmVEJGSFRmRXBYOTQzdEpBSGdDSDI3dzI3RnVXT3Y3TVFNQ1IwVGJaUDJieFcya0taaGxvZDJIdDQ2NXpucVpERG9XeEV3bGNQMGgzZFVvclFJeksrS2JjV0ZIclRqaVpxeDVzTDRrTEYwT1BVMnlMV0lCMHB4aVFIR0JrTmJORkg3Y0xmcVFmdTA0MEgvQU44T3JUTEloMHhlZ3VTT2JUYVBkL0dCa1JQZVBDMlBBTVhnQldFQlVsdFJnQ3h3TmoxWlNwZURzaGlwdVVkdm9kajNjeFdtdFRWTnZsQUhiRUJkdVVFMjBFVXBwZ0JRczRVZ2ZEUlVaaWVkbWdVcDZkTTFDc1g3ak5oV3RSV1lqKzVHMzV4TUIwUytudnZDMXp1Nmc2TTR0QUx2TWQvdkVFRTExT0dtTnh4VlRLWUNuT3ZwZ0VkZnlXSVdsUko0bGhJdmVrcVpXN3VvVklHRENsSU5Oa0NsVnNkckZneEdrMGUyUkZFQ2hMam9TSlQvQUhSSkMvRng3UzZUWEU3VWlKdXNvUGdpcGRQb29ZRWRmWmJLYWlqZlNZM25reVNpTHBSNjZ3clRwbldvRWFrN0t4b3Q2K3hBUkxhbEdTNFBIQnpGUUZvdFlsZkNUQ2xBNkE4bUZCSmNTcGNTTXdxTGk2NDdBcUtkS1RIU01lcDVzWUJGYlJVdzdWOEFMZ0FWaHlVeHhaQllhYUNrMWhWY3NjQmFZYU90WTNZUUtOUXBjbGYzbE9DYzdQWktEQmtISkxWR3RtdUR0S0tZM21vckt0RlJYZ21FcXVia0F2S3RiQjFtY2V2TFNMQmJGQzQ5cTh1aFNWVkQ3ZElvMU9ISVJyRXFrRmpMaktKeXhnUFpTU1VvejNyZzRxaFlBdXB5RFUxakYzdThJV1FHOGJDWVdjdFROQUp5Z1huYkwvd25ocW9DQ210WmdzMllpekkwaW04Y0FXMU1sWHZ6TWRSTWRJZXZ1R0VKRFdBMmpUU3gzam0zMVJDSWlFakgyQWJ4ODNXZ2ZSZ2ZXU2h0R2k5OVkyMjUwNVM1Z2hIU09IbExTWG5wSlFzbUdzdG5YZXJhWGdhMTBZOHkvTzZhVVVnbGNSQXBDVlVNdDMzd0QrWEdwRWlKa3NGNFhDMFRkTXJycGVvTWFoRmpPS1VTSFVMTjd3c2xpM2JmMW5RdkdPU2tLN1dFK3FuTVdRWkVLV3VRZXMyWkpxTW5sd2dSWFBKTXBPNlRtb0FLaXpvMW04VHlnWHpDRTFZaGxDOXdzOGxXQ1R4cGtOYWVRUUkwcnhvemxtSDAvd0NnUUlCZUttNFlTV0EremE4cHI0bUJwTWhvQjJpMS93QVo1L3FKY0EyZzUwaU9PUTRhS3ZDUDJ4bG9QSkNiUEJBbk9KN1JJaDYwV2xJZ21PNHpFeDVrclZLSHN4c09NbDFBTkMxd3Q5N3pXbXhZRHJpeElJUWFmSXI2cGl0VDM5Z1JJcXNHa2pjSi9nZ0poTkNpK1Z5cmpDUllmTUh2Y05iRFdxQ0p1NERJdmc2Q1Nvc1JzY3RlcjFJQ0lTM05UeCtURTBpUlNQbVFrM0ExRlYwQ0paZXFPUEJWaVl6Sk9TbklEbmxjSGh4aEFwWk9vSTY1d3NsNHdqblYwK3RJVzhDVUlLekp2REQ2TUlaVE1TVENhWUdkeTQ3ZzdlMVJRUUZWVGkwbUZzZ1pyaU43bU1Vek8zdkpWN1JQUVk4MHV3aUNIZkhDOHplUDhpSWlPQmFHaTg0SkRYWElhTW10eER0d2cvQTAwbWhGbk1MaEZXaDlaVU5zTkswd3Q4aEIxWVhSYlRsNFRDcThXc0JzQkJhMWZjbEMvd0FBQ0l0eWVscDNqNlNLOENwbnJqNWo2ck5HR042L2dMdG9SSG14NEhXR3paM1VCM1VmamkvQS93QXBTMFFLZWRUSGhuSWdqUlFLQmNiZHBiVDAxUTAwTWl3bFQ4WWhvNk4rbVNJRGRFaENxY0JVd3MrYXFZbUFXRmZnd1NlLzdvMmY1SElxVkZJRU5OM095TEF5SjdHNE4yQUZCTGdUYlhDNmllVGRoVENiNXJPQ0I0ZnB3djhBZDJBOGc3bGQ3eUkrdFNBWHZFY29Pc0FJNU5yckRjY1plVkVLdnVFZGNxb3UrZ1RMZ3NKamdyQ09nNGFYcHZiNU5LRFF6TFRETVlYSU55Zk95RTVKN2o2U0FocVJyd3NPQnVQZ282eE1KM1NzeGFmS0RoemNKVWpWMzJ4VzVXQytiSC8veEFBbEVRQUNBZ0lDQWdJREFRRUJBQUFBQUFBQkFnQURCQklGRVJNaEJoUWlJekVRRlRMLzJnQUlBUUlCQVFnQURHYnp1YkVSV2lOTXZOcG9xWU55T05VOWlaRnVEYm0wWXZteWNMSXhyNk42QXhFVnpBNWdjd09adkE4M2dlYkdiR2JHYkdiR2RtQnB1WUhnY3hYaGZyK2NualZaSVFrMXZrWnh1bUhucm10NGJPTzZ3M2FxdE0xRk90Z2VCNExJSG01Z2VibWJtYm1ibWJtYm1ibWJtQnhOcHVJSGdmcnVCK3BtWXRkN0N3Mk9VdkhrMHhzckUyQ25LeGNiOWRXVFhuam9VWDNBQXlyTnBmc1FQQThGZ25rRURnanViVGViVGFiemFiUVBONEhpdE41djFMU0hIUmV6dTRkMTFKVlR2RU5sTGt5NmhMN2dTNzVlQSt6b0s4cktTNjRaRnRWZ1ZrdVZ2NXROb0g2Z3Nua0U4Z2dlYnp5Z2YzenpicWJSV00zbTgzbDFoQTdHZHpkRkxJcVlPVjVNUUI2blhYb2ZXUzg5akxacTFERjhZWnpMZTF0dDlLanlMY2xvRnRZeVNHNlpYQkhZMmdjQ0N3VHlHQm5teElpOWR6YWJUYWJhaUI0R204ZTZzdnBNcmg4S3pMK3d6MjJVNEphdmdjak1zcjN5VGNDT2xEU3l0YkU2amkxUUk2SlphQ292YWdGYkZ5cmJrVjZSYjc2SVBVREF6c1FPQk41dEM1bXhnYWJtQnB0TjUrdmJzM0M0SHRGSlhIRHhRT3ZRYm9SYkpmbkpRVkRWMzEycnNpMHBZZTRUNHR0U2h0WGVvWlZsTEUycGFkQVVGb204UndBUVBJWjVKdkE4M00yTTNNRFRlRWJEc01mdzlZeEpxVXR0MUJaTnpCN1U3Y1pTam83QTVlWldvRDFabE4zWVZsRHA3SVlQdEFqVmp0RGQyT3NoYlIwWWxxa2V0ekJaTmpOcHROcHRBL1VEZHhITmZ1V1ArbnMxRUNzQTdIdWJRT1JBNUNtY1grRFdDSzRNdHJwdVhwdHNpaGVrVGtLRUEyWi9LZTJlbjhETy9HU1VQNTlGeGEzVTJFM0UybTAybTAyZ2NDQWhnQVhkeFhPK3dBQy9jRGU0R0hVTGZnWnhiZFdXOUJ1b0xHTUNKdnRPd1IwYjNPSW0xYTVWZ3FPNk9sdjVLUFk5cURyNk93TTNNN00ybllnYjFPNTJZVzZVQ01TYWRJcjZMMEVMQkIzdE5vMW5TR2Naa3Y4QWFkSUg2bTVtODJQVXpqdFYyRmJ2MVBvOXJ0TS9MUEhLR2VxNWVnckhMSnNZUVpESHNEL081dE80VzY3RTJBOUZ0UHFlZ3dQOEI2WHFGMVZlNXRMUGFtWVhhWkpCN20wRFR5SCtUT3B0VEdaamoxMmRkaXl5d29kY1JIenNQZTdpOFBEdE40dHlhNmFHL1NjaXRQNy9BQSsvNkowZjhYcU0ycmpxd2JkMkR2dkg2bHZaSUIzR2dtM1NHQit4QVNSN3JJR2JPeEZWbi9nZXNIMHR0WkhUWjdqNmhSZUhxQncxTnozY1hVNUs4V3Q1b0tyaDBaTk9YY0NCa1BicWMzRFR4aGdlb05TZmJDa0g4U0IyUkJVeFhhV2tya0lvWnlGMWdidkZpZnJVRml4c1RzQXYxRWM5ZEh5QWVvdnJLMmxOTjE5bWlWY1ZsOTZLL0dwVG5oYkcrbFFuNnMvRHlxYS96eEtXZkVCTC9Yb0FCd0NiQlpXY1RJdnA1TXNtSGZrM3RxM3lQRE9QeGhZSC9QNUQvd0NwMjBQZjJrSmJWbDdDTVJSQU54NnFCOGpRYnIvYWNmSnRQNHJRNXlXUTQ2MGZkSGxUanFyVUhqeGFmcHVWSEtWMDRuSkVEL3R2U3V0T2J6Rm1SaXNod25kMEFPZmpvMk9DZUdGUTVISXBWNjdhL2toUmVVSE04Y2Z5NWpCcHE0a3hxaUJCVTBGWGY5OFg1bUNzeng5M3hpb2hYWkFJMVpBbUN4dHM2bGlkQUdZbGoxbnVNL2x5VzdZRHpkd0dkOW1YcllYbEdNajBzN1pKd0s4UjFUR3Z4cXVPZXMzNTF1UUdDMFplVFMvbXJ2enNxN0o4OWwyZG5aSzlPTjhsK21QRnFYMWpjWnlWdDNqQ2NkbFBhYTVYeHIxdTViSng3cVFldTdWNUFyWlp4dGR5dlpIb1lXb2xkeU9LVDVlUFFHOEJPU3c3YXNiY1krQ1hWK2t3c3B1VGRJZllZeWpBc3NxVnBYeDdsM1VaMVRZMTRVdHgxdjEyY0poRzNBOG93OFUzWTc5SHpMWVZlbXMyWHFnek9OeU1QSVJiSytHek12aVBzMThmOFNmT3cwdk5WaHB6ZnQxNTNMNG5IOHNWdk5HYmJ6RmxWQXAzeWNTdzUyTGpWNFZyM05iVmZ6RmxsVjJNS2I3UlZkYmhyeWRIbjUzaU1NVldHdjQvVXB6bjMrVVY0UzhZb3I0UjhON015SmwySDVKK211bytkd09OeExXd2NicmhLckg1UFBXZktjYzE1NnluRng4emp3OCtQNFdOay9HaTlueDFLRGgzbXcxMFg0cGRjVG9aU2svSU1kNkw2aWZqcHNmaU1pa2ZIc2pLYmlrQzVlUytGblVzZmsxZGZMY3M5OVNaUEk0K1ZkOXZBdVI4akdhYzF5bFYvSDMxdFdGeDdlbC83UEZLV3BGL0pVOHhWMEY1S3Qvano0emNkazBKWWR1UitYWWZMOFVLTWJoT2YrcUwvczRQTHRSbC9adGZKV3U4MkxoL0txSzB4eU1ENUV1SHlPUTdjNWt2bDJoMndNMm5qOXJady95R3Zqc0Y2RGk4a01LcHF5SHh5R0swc1VzMkdiejJWeXJEejQvTlUwaC9EVDhoNVhCeHhYaVBtNUxFc0RsWmp0Mi8yc2xTU3FYM2pzeSsrKzRkUTVOVnJhS01lcFNHZ2ZFeHNabFh5b3RSSXEweHdHc3B2eDh3bEtla3JacDVRQjBMczVBNklFZW9MMFBLQWZWbVRiYXdMQm43MmJ5anFDeGUrejVsYjNCWjBaNUVQcUI2aCtJVXovL0VBRE1SQUFFREF3SURCZ1FHQXdFQUFBQUFBQUVBQWhFaE1VRVNVUU5oY1JBZ0lvR3h3VENSb2RFVE1rQkNVdkFFY3VHQy85b0FDQUVDQVFrL0FPODRBcWhaVTFNeHlPK1VOWENnR241Z0xBa0MvcWlDM2wrbUhpQkZmcWZvRVpaRWdHeGk5TmlwRE9IKzIycXBnbmVBaEFKSkZ2TUdxOEpwZm4rbGNSSE9oNmhFQ0JUYTl1UVRSRmVSNkQ3cC93Q0pGZ1Q0Z1JNUnVJd2NGR3BFRVJCSGtpTHdSekgzOTBZSXBCL1JIc3dJK3RDaHBrVlRaQkFxUHNtM29ETUdrNXVtNjJtTER4VW0rQ29KbTM4VGllWlZqODBmZ240QnNnUy9OYVFiQ053ckVVNkhza0hjYnFYYVl0UG9pV3dSRVg2bjFqQ0JlTUVYbm1Qc3NYK3gvUW10RXp4UkV5bXk1b01EbUVDSjN1cmRoam9wTUVLaGd6SDlyNW9URWVJZTRSZ1duZm9GVDQxMFIwS0J0M0FZaVpGWTY1VHBISll5cWpwVlN3N3hjOVBkRHpGbzNJd3F6Mkg0ZHdyclBjcVVTSGFqVVUvNFVBNERhaDh4WkdveFkvSmJxMFFqcGR0Z3pXb1JqcFpXUitIU3ZlT1Q3ZGdUdFFteHY4MENIYzdmOVZTcTF5akhMRXFoSDl1ajhFODVXL2UzUGJmc01TUUl4VXB2bUZYNGZKRkdVZTRLR3Zkd1I2aFdRSU80UWtFeFNoeWpGQWVkVTBtSVErQWM5N2J1dElBKzZxcXVGZ0tSMXduU0FlVnl1SUtPcEtmUElvUWU5dVBWR2l0UHQySGwzTnUweVUyNmVTSnlwMCtxWVNlcWk1b3J0aTRuQ0VuNklpaHJIUTl0ZTB5RDJES3FZUjdCMmlxYzM1Z3A0YzNVQVQvNUo5b1RDWFduQ0hoVG9GUUJXc2M3QlZKcW1rK0kyRmtDWkJCbXRzbm11QzRua3ozWERESEVpSi9NYTkwckFLdXJ6MmJEMVFUWkJWSUVxb2hIU1l3Nnk0MWVnbnJsUC9FYUMwN1htYmJKa0RiVktiRXFvR09xNFpiZk1nMEpVdC9LNXRRTHR5blFTNi9VTGpIUkYybkVnVzgxeHRmRWtYTW0vZTI3Y0xiMFc2RnZJcTBlNjI3Q3VYMEs0Z2JwRmpjOUlUdFpOcUVRdUNIUE13NG0zUUxoaHRoNFJHQ1BxRFhkTzB1SUFuTUFVVHBmdmxQa0kxaWFuQ3Z0TmZrZ0daM0pBTWNvUUEwZ1NlcWt5WStTNFpJM29FMjE0NkpzVEJtdDAycWJCait3cW1OK2FFVkhxdjJranpScHBCaVBkWUIrZ0tGQ0FmbWhZb0t3Q01naVVKUnBwYVZ1SVFqVUtKdzBDWjNwSUs0Z0VqWmNQVUEwQ1NaY1duaUVEU0Q3NEM0ck9IRFFBWFpKZFlBVm1CTUp3Y0NHMWtOSDVaNWxOMU9keE9MUW1vaWllMXVnak9KRlRLNGdjeHhNT0ZpSXh5WEZJYnFPQldDQktjV01tQ1NBM0pBUE9VK1J3N21SYzJBamxVcHdoclo2K0lXVlNYc3QxVENZNGppTFJVQ25sQzRRT29hWU40OUVNUDhBUnlBcXh2blJSTFhBMjZoWkh1UXRNL2hrd0RteUkxRFhtRFNZUW8wQStxdUdFWC9pUXR4Nm9DRE1RWm9uRUFFMDVGT29FM1V3TjRXcUoxQ1hTS1p6SlRRQnE4Qk9ITklyc2lPTVdpdjdadkFvUkppZ3RKUUV0NGpvYkpGeUlua3RPdDNFRkIvRWV4UUVBRzFBSzRDQmNYZzZadkZEamtENWxOalFTMG05ZHhpUWdROE9GU1JYWXE4VVBQVVBRQlVjeDJ4RmpRMmo1Rk9BYzh5WUJ5SWtjcGk2ZllFVEFPYWZSR2htUE9VOThNYkRyVklIN1FWcUo0c1FRYkdTZkZ1Rm1Zck9aaEdybWxzZGNyL0hEeVNhbm50VEM0WWQrSkEvMTVoWElOK2kzQlRHdERhMG56dW5RNXdqeUNjTlBNVFZPZ21LNUVXam9xaThrbVNjbTF6bFVCeDJPZ0NNVFpQa3hCcENBa0NBWXNqQk5hYjJ2MEN6Q21Sc21sdThRaFVvVVF1aEF3Z3NKM1lTajJGRHMvL0VBQ2tSQUFJQ0FnSUNBZ01BQWdJREFBQUFBQUVDQUFNRUVRVVNFeUVHTVJBVUlpQkJGVElqSkVMLzJnQUlBUU1CQVFnQUlNNnozUGNLenJNYkhzc2NheHJYRmJWakp4OGZKdDhWZGxEME1VY0NkWjFNMFpvNm1qK1BjMFQ5Nk0wWm96Um1qQUQrTkNhRTBacUZKaFd0UVdNVWl2RjZTekc4QjhpNVJhOGQzOERFYlNhbXZ3Rm5XRDg2bWhOQ2FFK3Z3QWZ4b1FnYi9HUGE2cVVDQlRRZWkrV20wQ2FydnUvdHFueC9wMFFzUkxNYXhmWTFPdjQxT3M2UXJvVFFtaE5DZFoxTTFOR2Ftb0ZoV1YvdzI0RklwT3ZLMWxuV1Axc0hxdHVpRUFOajVDOUZJdHg2V1ZQR2pxVERXNi9mV2RaMW5XZFoxRTZpZFIrT2phM09zNmlhRUErNEY5UXJBQUlsQmVockZwMndGZ0tuWmxtUzFBSUdNRkxHTGEyTXByV3RLYlI2TmIxZzFzYWV5N1hxUWRIck9zSytwME02UXo3SDU2bUFUck9wbXA0WEkzRXhhaS9rbUJXbGVVQStkWFdDUWxlTVF4WjVTU2pFeEFwM0Vka1VpTFdMVzdEeENwaXRweHUyMmlvRjNPczZtRlRPczZ3TE5DRWV2WFdkWnFhTS9yWHF2eDYwUnRyOVE3MmQ2aFNZK0s5eXN5MlV2UzNWMnVGUUtpdGUraXlXK1A4QWx2QWxnM1c2N2JUNm1qQ056ckFrNnpxSjFFMEpvVFFtb2pGZGlJZyt6Y29XNXV1cDE5Ym1vMmhvSmw1RDFCRWd4c0YySld6R3VwOXVHS3VadGRhbmNPZE1LaHJ0VUt5VEdUcjk2RVZkajEwTTZ6ck9wblV6cVoxZ1grU1l2czZEait5UVJ2ZjQ2aUVUTk93azFLcnJLai9KWEh1TzdId0wzSjZWcDR4b0krbmk2Yi9zRG9FS2FLNTExOUFUUW1oTkdhTTBab3dJUjdsYWpmbzZMTkdUUjBQeC9xWjJ3bFpnRy9zSXM4aitQcE5FSFlwUG5icmNhS21PME5Ub2RFalg0MElCcWFNMGY4U05qVW9UM0FxbmN1STcvd0E2MloxbGRmWmhPVHdrVENTd2RST3M2enFKaUtQTEFCbzdyeWloNnlxcXJJQkF5TUM2aGhCaUFEVUtVQUVyL2hxTFZ0Q3hiMXJWTFdPOENqWmpEYWdoSzNjNkFFcUhzVGtsM3hvYWFuV2RZSzIvMWlPdjdDNmNwN0JxRlFQdTIxTWE0aXZPeWNnR3RscXQ4cTZjVU8rK29tcG9UcVRzelEvMGlFMXRvQTcwYTE2bmNWUm96cnFyY1RRYmNJQU1UN0V6QUJ4YzZtQ3NnSHNsWS84QXBpVjlyZ3Q1Y3pzM0xiR2U0UVY1TC9lWDE4blkzUFcrUFcwMnFEWXhMdjdJT29Sb1JGTGpiRk4xbWFHOVRIcFJzVjJJUWtiZ0hSak81WmVwZlNLUVNLLzlOV2hiWVNsdnNaZzN4eGpOV2k3Wjh4QU5sTWl5MmphcUxuUDk4ZmRTYnhyUDZmdkV6b1M1TXRJUjFZWkZWVnZGamQ2MVY3TTRlL3labzEwZ3FpMXdwcEJBQ1BwUFdHMEcvcU1nN21hRXUvckZFQ0kvL1Z6VUFOcjZwQlhMN25qRzZIS3NySjcyV0c1TnpBTDNZbTJHQlZZZXo0T0NsT1NyRGthUjU5eWgyOHJBWjVjWTFkalk3MVA4ZGF4c0Mzak03MG5GNW1SZnlJRUNnQ2FFMU9uOENkSjIxUjFtMUVBMHhnUUV5NU9sUkVxOU5MYWtjUlU2MDZGbS93Qk1yRlJ0UVZ1WXFnRHFiZjhBeE1BTVdyS09Tcm5Qb3Z0eWtzV3ZHRlNnRnNISHVUcTlHSmpVNHhwU3ZFd3NjanFUVlNtd09RVFhhRGtjWURaT2ZqaXNQUDhBa0ZQb0xscXgxT3diSERBT25iVVIxSUpsVDFGLzV5bTZvUytNOUZsaEVlNUUxdnpWaWtHTVZlbnFUbFVWc3lsc3VrS0RNU3hML2EvdTBtenBCbExYaytNNXQ2MWxkanh0VUdCUGp4Mlk0UEo0K1hWWTFWbk40T1B5SXhYemZtU1lXYStPejVKdHhUajI0YVp1WHhpdlFPYm94K0ZTL0lmbEhTbkxFeFBrT1pkazByUjViNk9IVHlVOGhmZmpWOXEzNUYrSnZOUEJjMXlOdGxmbDUyOTZzTmZEOFZ6dVZ1NU54Znp0L0kwMVltdkRTbnhvMjNXMi93RG9CenlYTFZwbTVJUE41MWVQeFBIMlQ0bGxESndYTXV6YnNMbEdySFA4bGZpZkpSVW55Ykl1VEl4MXI3M1krUUtwYWRZYkdmSE0rckxydFZlZE5GWEowV3Q4aW80OGN1NXR4OFJjekJ2VlBqVmQvRjhWWFJmK3ZoTng5UDZuS1k3L0FLMlVwNGJoN3NmUHg3QWEyeWEvNlRndVpzVVhtbmpNamlBTzY4WFlueUJNbGVSeHJMU0FuRy9ETW5nT2FlL0s1djQ2TW53Q2pLNGV2STQ0NDFZb0xZSXJPVjhWdHlYeSsyZjhlYk00L0ZyVGdzSmVNcDhRNVRCUElvcUx6bngrN2tzOWIxenVQc3pHcmNNbGk2bFNtekc2SGl1RHI0Z3QxeWVKZTRMNWN6NDVpY2pmNUxsNC9IR3RqajhaUGFOaVZNQnQ2QStwVGkxaHVwWEN0eDkyTisxY3lNa3N4ODNJeUZhdzFoWDkyMWZzZ2hEWGxVcURhdmtjQVRwcjNFb0pxWXdJeEoyYWlaVmlwVzJ4NGF6N0FvVC9BRjRmNDZ3NDIxMUVyNmpVL1hjUTB1VHNuSGMvWC8vRUFEQVJBQUVEQXdFR0JRTUVBd0FBQUFBQUFBRUFBaEVoTVVGUkF4QVNJR0Z4SW9HUm9iRXdRTUVUTXRId2NvTHgvOW9BQ0FFREFRay9BT1lVUm1hV3BuMlJoM3NUMDBUWUkrMnRCWDdwZ3d2M096cC8xR1NLSDNWZnRSZjFReU85alZFcHBiT2xqUHdVS0F5RHFoMUJRa2RQdGF3YUl4R0VmTENkd2tlaTB2ZVJtRlEreUgyUnQrTDc0TTRLZ1RyOHF0RE9pUERxRGJ5TzcwK3dzcmp2OEszc29ua29xaEdKd2Z3VU92OEFTajcvQUZ4Y284aEV6WTBKN1lUWUtyT0ZROTZYWGlHaU5kRCtEbFVJK3JsV25sL3BRbHBGalZIaFBXbzhqY0lVT1JVZXUrcmRWNzNRK3FjOHVtNHB2Q2RSK1FpSE5HbC9TNFZGUkJXUStscU9iVGZaWFZldWZYK1U3eVA4MlErcHFyb1U1RFVHT2NnalFxa0R1RldkRTRJa3hIcnpZNTlVYzg1b213U0VEVVk3b2VpTS9XMTVEVk5FZ0lmMkU1YUt4bmNEelVPOEp0TEtRaXJMQkNLQmpzbTFqOG8wOTFkREFtMUtJb2lZeWdCQkJwUlBBN2xPTGhCL3gzSGt6eWE3blZWYW93WkVLbzdKZzkwM2h1TmJXVHE5b1JsYUNxZk5zUmxRUlVHaE5aVFpBRnV4V3g4VXg0aG1DUjhMWkZySU9JNXRlVEJXbTdYbFlUT1JZSnZDQjFCVzE0V2dWRVhUeTY1cWVzcG9JR0UyRzZKc0llZ1Z0eHYrTnhDenVORVVJcVBoRlVCV3F6Q2RZd2pRN3JtaXZNYnJ5UXNCR2VHOUUwL3FHSTByYXEyWlBDWW1WdElKY2FBQU5EZ3dFeVF0aS9hY1RqSWJGSWJlVGdsTUlJTHFRWEVlTGg2RENNTlpzOW1aQXZ4U2FBclpQZHhna1V2QU5CQ1p3dkRSTFRjSElLMlhpTFFhVFNRYitrSm5GdEFBUUFTWnBKSFNBbVFkcllRYUFYSm5yQUNhU1hPanRRMUtCRFF4OXdkRThEaTJiUVp2TW11WWxiWWd0OFVpSW5BajhYUnl6NUNlZkM5dzdWS21IdGNPdEtxYU96MkJYRVFOb0FIR3hDYVMzd1dCaXFueE9Jb3FqaWFRWTFCUXdmZ3JFVFBtbVNTQlhxQ21TVEJ2R0U0dGZ4N1dDYkhoYncrV0lUeTRoc3VHb01xZGlIT2lLRTlUNGdZRTFLbURzMlM0WG9ETVdxaS85TnV5TlhHdkVUbkVnSXk0eFUzUGRPRFdzamlBbUNmRURlc1NSVG9uZnVBY0xpbnpCUkhBVzJBc2EwbFdKcVA5WEJFa2JSaGlvSWdpSXZLYVNHTmdFa1dEckhxbVVMZ1NDU01HVldPSDJoYkprdmRMYnpFelVqSlRXeHNwa0VUQWlEdzZWVVlueUVJeHd1RHZRcmJsZ0FiUUdsSk9xZVcvcHVudmlDcHVQbFpCSHFuRjNFbXpCa0toZ1pUUkFtbURONTc1VktSR0l3T3dWeE1JcHMrY0lRSnBXVTR3NHllcDFVRm9FVjB2YnVyQkc2Zng5NVRxQkdxS0tPODdqdktLLzlrPSIgaWQ9ImUiIHdpZHRoPSIxOTkiIGhlaWdodD0iMTU5Ii8+PC9kZWZzPjwvc3ZnPg==", + "description": "Displays power consumption data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'powerConsumption', label: 'Power consumption', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'kW', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'powerConsumption', 'kW', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Power consumption\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"kW\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":5,\"color\":\"#3B911C\"},{\"from\":5,\"to\":15,\"color\":\"#F77410\"},{\"from\":15,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"kW\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0,0,0,0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"kW\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:cG93ZXJfY29uc3VtcHRpb25fY2hhcnRfY2FyZF9iYWNrZ3JvdW5kLnBuZw==:cG93ZXJfY29uc3VtcHRpb25fY2hhcnRfY2FyZF9iYWNrZ3JvdW5kLnBuZw==;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":4}},\"autoScale\":true},\"title\":\"Power consumption\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"bolt\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"margin\":\"0px\"}" + }, + "tags": [ + "power", + "energy", + "energy usage", + "electric load", + "electricity", + "power efficiency", + "load profile" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/power_consumption_range_chart.json b/application/src/main/data/json/system/widget_types/power_consumption_range_chart.json new file mode 100644 index 0000000000..43478ddb8b --- /dev/null +++ b/application/src/main/data/json/system/widget_types/power_consumption_range_chart.json @@ -0,0 +1,35 @@ +{ + "fqn": "power_consumption_range_chart", + "name": "Power consumption range chart", + "deprecated": false, + "image": "tb-image:cG93ZXJfY29uc3VtcHRpb25fcmFuZ2VfY2hhcnQuc3Zn:cG93ZXJfY29uc3VtcHRpb25fcmFuZ2VfY2hhcnQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxnIGNsaXAtcGF0aD0idXJsKCNhKSI+PHBhdGggZmlsbD0idXJsKCNiKSIgZD0ibTY3LjggNDkuMy0yMi45IDU2LTcuOSAxN3YxOS4zaDE1My41di0yNS4zTDE5MCA5MGwtMTAuOSA0MC02LjMtMjUtMi44IDcuMy0xMS43IDI3LjVjLTcuOC0yOS45LTE0LjMtNDEtMjIuOS03MC40bC04LjcgNDMuOWgtMTEuM0wxMTMuMiA0MmwtNC40LTMwLTIxIDcyLTQuOS0yLjItMTUuMS0zMi41WiIvPjxwYXRoIHN0cm9rZT0idXJsKCNjKSIgc3Ryb2tlLXdpZHRoPSIuOSIgZD0ibTM3LjMgMTIyLjQgOC0xNy4yIDIyLjgtNTYgMTUuMSAzMi42IDUgMi4xTDEwOSAxMmw0LjcgMzcuMyAxLjkgNjRoMTEuOGw3LjUtNDQuNyAyMy42IDcwLjkgMTEuNy0yNy42IDIuOC03LjQgNi4zIDI1LjVMMTkwIDkwIi8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Ik0xNS4yIDE2LjR2LjZoLTMuN3YtLjVsMS44LTIgLjYtLjcuMi0uNS4xLS41LS4xLS41LS4zLS4zYTEgMSAwIDAgMC0uNi0uMmMtLjIgMC0uNCAwLS42LjJhMSAxIDAgMCAwLS40LjRsLS4xLjZoLS43YzAtLjMgMC0uNi4yLS45LjEtLjMuMy0uNS42LS42YTIgMiAwIDAgMSAxLS4zbDEgLjJjLjIuMi40LjMuNS42LjIuMi4yLjQuMi44di41bC0uMy41YTcuOCA3LjggMCAwIDEtLjggMWwtMS41IDEuNmgyLjlabTEuOC0yLjEtLjYtLjIuMy0yLjhoM3YuN2gtMi40bC0uMSAxLjUuMy0uMS42LS4xLjcuMS41LjQuNC42LjEuOC0uMS43YzAgLjMtLjIuNS0uMy42bC0uNi40YTIuMiAyLjIgMCAwIDEtMS40IDBjLS4zIDAtLjQgMC0uNi0uMmwtLjQtLjVhMiAyIDAgMCAxLS4yLS43aC43bC4yLjVjMCAuMi4yLjMuNC40aDFsLjMtLjMuMi0uNGExLjggMS44IDAgMCAwIDAtMWwtLjItLjRhMSAxIDAgMCAwLS40LS4zaC0xbC0uNC4zWm00LjUtMy4zdjZoLS43di02aC43Wm0yLjYgMS44LTEuOCAyLTEgMS0uMS0uOC43LS44IDEuMy0xLjRoLjlabS0uNyA0LjJMMjIgMTVsLjQtLjcgMiAyLjdoLS45Wm0zLjItMS43IDEtNGguNkwyOCAxMyAyNi43IDE3aC0uNWwuNC0xLjdabS0xLjItNCAuOSAzLjkuMyAxLjhIMjZsLTEuNC01LjdoLjhabTQuMyAzLjkuOS0zLjloLjdMMzAgMTdoLS42bC4zLTEuOFptLTEuNC0zLjkgMS4xIDQgLjQgMS43aC0uNUwyOCAxMi45bC0uMy0xLjZoLjVaTTE1LjIgNDIuMXYuNmgtMy43di0uNWwxLjgtMi4xLjYtLjYuMi0uNS4xLS41LS4xLS41LS4zLS4zYTEgMSAwIDAgMC0uNi0uMmMtLjIgMC0uNCAwLS42LjJhMSAxIDAgMCAwLS40LjRsLS4xLjZoLS43YzAtLjMgMC0uNi4yLS45LjEtLjMuMy0uNS42LS42YTIgMiAwIDAgMSAxLS4zYy40IDAgLjcgMCAxIC4ybC41LjVjLjIuMy4yLjUuMi44di42bC0uMy41YTcuOCA3LjggMCAwIDEtLjggMUwxMi40IDQyaDIuOVptNC41LTIuN3YuOWMwIC40IDAgLjgtLjIgMS4xIDAgLjQtLjIuNi0uMy44LS4yLjItLjQuNC0uNi40YTIgMiAwIDAgMS0uNy4ybC0uNi0uMWMtLjIgMC0uNC0uMi0uNS0uM2wtLjQtLjQtLjMtLjh2LTNsLjUtLjdjLjEtLjIuMy0uNC41LS40bC44LS4yLjYuMWExLjQgMS40IDAgMCAxIC44LjdsLjMuN3YxWm0tLjggMXYtMS44bC0uMS0uNWExIDEgMCAwIDAtLjMtLjNjMC0uMS0uMS0uMi0uMy0uMmExIDEgMCAwIDAtLjMgMCAxIDEgMCAwIDAtLjUgMGwtLjMuMy0uMi42LS4xLjhWNDFsLjIuNS4yLjMuMy4yaC44Yy4yIDAgLjMtLjIuNC0uM2wuMi0uNnYtLjhabTIuNi0zLjd2NmgtLjd2LTZoLjdabTIuNiAxLjgtMS44IDItMSAxLS4xLS44LjctLjggMS4zLTEuNGguOVptLS43IDQuMi0xLjUtMiAuNC0uNyAyIDIuN2gtLjlabTMuMi0xLjcgMS00aC42bC0uMyAxLjUtMS4yIDQuMmgtLjVsLjQtMS43Wm0tMS4yLTQgLjkgMy45LjMgMS44SDI2TDI0LjYgMzdoLjhabTQuMyAzLjkuOS0zLjloLjdMMzAgNDIuN2gtLjZsLjMtMS44Wk0yOC4zIDM3bDEuMSA0IC40IDEuN2gtLjVMMjggMzguNWwtLjMtMS41aC41Wk0xMy44IDYyLjd2NS43aC0uN3YtNC44bC0xLjQuNXYtLjdsMi0uN2guMVptMy4yIDMtLjYtLjIuMy0yLjhoM3YuN2gtMi40bC0uMSAxLjUuMy0uMmguNmwuNy4xYy4yIDAgLjQuMi41LjRsLjQuNi4xLjgtLjEuN2MwIC4zLS4yLjUtLjMuNmwtLjYuNGEyLjIgMi4yIDAgMCAxLTEuNCAwYy0uMyAwLS40IDAtLjYtLjJsLS40LS41YTIgMiAwIDAgMS0uMi0uN2guN2wuMi41YzAgLjIuMi4zLjQuNGgxbC4zLS4zLjItLjRhMS44IDEuOCAwIDAgMCAwLTFsLS4yLS40YTEgMSAwIDAgMC0uNC0uM2gtMWwtLjQuM1ptNC41LTMuM3Y2aC0uN3YtNmguN1ptMi42IDEuOC0xLjggMi0xIDEtLjEtLjguNy0uOSAxLjMtMS4zaC45Wm0tLjcgNC4yLTEuNS0yIC40LS43IDIgMi43aC0uOVptMy4yLTEuNyAxLTRoLjZsLS4zIDEuNS0xLjIgNC4yaC0uNWwuNC0xLjdabS0xLjItNCAuOSAzLjkuMyAxLjhIMjZsLTEuNC01LjdoLjhabTQuMyAzLjkuOS0zLjloLjdMMzAgNjguNGgtLjZsLjMtMS44Wm0tMS40LTMuOSAxLjEgNCAuNCAxLjdoLS41TDI4IDY0LjJsLS4zLTEuNWguNVpNMTMuOCA4OC40Vjk0aC0uN3YtNC44bC0xLjQuNVY4OWwyLS43aC4xWm01LjkgMi40di44YzAgLjUgMCAxLS4yIDEuMiAwIC4zLS4yLjYtLjMuOGwtLjYuNGEyIDIgMCAwIDEtLjcuMmwtLjYtLjFjLS4yIDAtLjQtLjItLjUtLjNsLS40LS41LS4zLS43di0zYy4yLS4zLjMtLjYuNS0uN2wuNS0uNWgxLjRhMS40IDEuNCAwIDAgMSAuOC43bC4zLjd2MVptLS44IDFWOTBsLS4xLS41YTEgMSAwIDAgMC0uMy0uM2MwLS4xLS4xLS4yLS4zLS4yYTEgMSAwIDAgMC0uMy0uMSAxIDEgMCAwIDAtLjUuMWwtLjMuMy0uMi41LS4xLjl2MS43bC4yLjUuMi40LjMuMmguOGwuNC0uNC4yLS41di0uOFptMi42LTMuN3Y2aC0uN3YtNmguN1ptMi42IDEuOC0xLjggMi0xIDEtLjEtLjguNy0uOSAxLjMtMS4zaC45Wm0tLjcgNC4yTDIyIDkybC40LS43IDIgMi43aC0uOVptMy4yLTEuNyAxLTRoLjZsLS4zIDEuNS0xLjIgNC4yaC0uNWwuNC0xLjdabS0xLjItNCAuOSAzLjkuMyAxLjhIMjZsLTEuNC01LjdoLjhabTQuMyAzLjkuOS00aC43TDMwIDk0LjJoLS42bC4zLTEuOFptLTEuNC00IDEuMSA0IC40IDEuOGgtLjVMMjggODkuOWwtLjMtMS41aC41Wk0xNy40IDExN2gtLjZsLjMtM2gzdi44aC0yLjRsLS4yIDEuNS40LS4ySDE5LjJsLjUuNWMuMi4xLjMuMy40LjZ2MS41bC0uNC42LS41LjRhMi4yIDIuMiAwIDAgMS0xLjQgMGwtLjYtLjItLjQtLjVhMiAyIDAgMCAxLS4yLS43aC43YzAgLjIgMCAuNC4yLjUgMCAuMi4yLjMuMy4zbC42LjIuNC0uMWMuMiAwIC4zLS4yLjQtLjNsLjItLjRhMS44IDEuOCAwIDAgMCAwLTFsLS4yLS40YTEgMSAwIDAgMC0uNC0uM2wtLjUtLjEtLjUuMWEyIDIgMCAwIDAtLjQuM1ptNC41LTMuMnY2aC0uN3YtNmguN1ptMi42IDEuNy0xLjkgMi0xIDF2LS43bC43LS45IDEuMy0xLjRoLjlabS0uNyA0LjMtMS41LTIgLjQtLjcgMiAyLjdoLS45Wk0yNyAxMThsMS4yLTRoLjVsLS4zIDEuNS0xLjIgNC4yaC0uNmwuNC0xLjdabS0xLjEtNCAuOSAzLjkuMiAxLjhoLS41TDI1IDExNGguOFpNMzAgMTE4bC45LTRoLjdsLTEuNCA1LjhoLS41bC4zLTEuOFptLTEuNC00IDEuMSA0IC40IDEuOGgtLjVsLTEuMi00LjItLjMtMS41aC41Wk0yMCAxNDIuMnYybC0uNC44LS42LjRhMiAyIDAgMCAxLS44LjFoLS42bC0uNS0uMy0uMy0uNS0uMy0uN3YtM2wuNC0uOC42LS40LjctLjFoLjdhMS40IDEuNCAwIDAgMSAuOC44bC4zLjd2MVptLS43IDFWMTQxLjRsLS4yLS41YTEgMSAwIDAgMC0uMi0uNGwtLjMtLjJhMSAxIDAgMCAwLS40IDAgMSAxIDAgMCAwLS40IDBsLS40LjQtLjIuNXYyLjZsLjIuNWMwIC4yIDAgLjMuMi40bC4zLjJoLjhjLjEtLjEuMy0uMi4zLS40bC4zLS41di0uOFptMi42LTMuN3Y2aC0uN3YtNmguN1ptMi42IDEuNy0xLjkgMi0xIDF2LS43bC43LS45IDEuMy0xLjRoLjlabS0uNyA0LjMtMS41LTIgLjQtLjcgMiAyLjdoLS45Wm0zLjEtMS44IDEuMi00aC41bC0uMyAxLjYtMS4yIDQuMmgtLjZsLjQtMS44Wm0tMS4xLTQgLjkgNCAuMiAxLjhoLS41bC0xLjQtNS43aC44Wm00LjMgNCAuOS00aC43bC0xLjQgNS44aC0uNWwuMy0xLjhabS0xLjQtNCAxLjEgNCAuNCAxLjhoLS41bC0xLjItNC4yLS4zLTEuNWguNVoiLz48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuNSIgc3Ryb2tlLXdpZHRoPSIuNCIgZD0iTTM2IDE0aDE1NE0zNiA0MC41aDE1NE0zNiA2N2gxNTRNMzYgOTMuNWgxNTRNMzYgMTIwaDE1NCIvPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLW9wYWNpdHk9Ii41IiBzdHJva2Utd2lkdGg9Ii4zIiBkPSJNNTEuMyAxNDN2LS44Ii8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTQxLjYgMTQzLjJ2NS43aC0uN3YtNC44bC0xLjUuNXYtLjZsMi0uOGguMlptNC42IDB2NS43aC0uOHYtNC44bC0xLjQuNXYtLjZsMi0uOGguMlptOC44IDB2NS43aC0uOGwtMi45LTQuNHY0LjRoLS43di01LjdoLjdsMyA0LjR2LTQuNGguN1ptMSAzLjYuMi0uOWMwLS4zLjItLjUuNC0uNy4xLS4yLjQtLjMuNi0uNGwuOC0uMmMuMyAwIC41IDAgLjguMi4yIDAgLjQuMi42LjRsLjQuNy4xLjh2LjFsLS4xLjktLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM2wtLjEtLjlabS44IDB2LjZsLjMuNWMwIC4yLjIuMy40LjRoMWwuNC0uNC4yLS41di0xLjJsLS4yLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41LS4xLjVabTUuMyAxLjUgMS4yLTMuNmguN2wtMS41IDQuMkg2MmwuMS0uNlptLTEtMy42IDEuMiAzLjYuMS42SDYybC0xLjUtNC4yaC43WiIvPjxnIGNsaXAtcGF0aD0idXJsKCNkKSI+PHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0ic3F1YXJlIiBzdHJva2Utb3BhY2l0eT0iLjUiIHN0cm9rZS13aWR0aD0iLjMiIGQ9Ik04Mi4zIDE0MS40di0uOCIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Ik03Mi42IDE0My42djUuN2gtLjd2LTQuOGwtMS41LjV2LS42bDItLjhoLjJabTYgNS4xdi42aC0zLjh2LS41bDEuOS0yIC41LS43LjItLjUuMS0uNHYtLjZsLS40LS4zYTEgMSAwIDAgMC0uNi0uMmMtLjIgMC0uNCAwLS42LjJhMSAxIDAgMCAwLS40LjRsLS4xLjZoLS43YzAtLjMgMC0uNi4yLS45LjEtLjMuMy0uNS42LS42YTIgMiAwIDAgMSAxLS4zbDEgLjJjLjIuMi40LjMuNS42LjIuMi4yLjUuMi44di41bC0uMy41YTcuOCA3LjggMCAwIDEtLjggMWwtMS40IDEuNmgyLjhabTcuNC01djUuNmgtLjhsLTIuOS00LjR2NC40aC0uN3YtNS43aC43bDMgNC40di00LjRoLjdabTEgMy41LjItLjljMC0uMy4yLS41LjQtLjcuMS0uMi40LS4zLjYtLjRsLjgtLjJjLjMgMCAuNSAwIC44LjIuMiAwIC40LjIuNi40bC40LjcuMS45LS4xLjktLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM2wtLjEtLjlabS44IDB2LjZsLjMuNWMwIC4yLjIuMy40LjRoMWwuNC0uNC4yLS41di0xLjJsLS4yLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtMSAuNWwtLjEuNS0uMS42Wm01LjMgMS41IDEuMi0zLjZoLjdsLTEuNSA0LjJIOTNsLjEtLjZabS0xLTMuNiAxLjIgMy42LjEuNkg5M2wtMS41LTQuMmguN1oiLz48L2c+PGcgY2xpcC1wYXRoPSJ1cmwoI2UpIj48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiIHN0cm9rZS1vcGFjaXR5PSIuNSIgc3Ryb2tlLXdpZHRoPSIuMyIgZD0iTTExMy4zIDE0MS40di0uOCIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Ik0xMDMuNiAxNDMuNnY1LjdoLS43di00LjhsLTEuNS41di0uNmwyLS44aC4yWm0zLjIgMi41aC42bC42LS4xLjQtLjR2LTFjLS4xLS4yLS4yLS4zLS40LS4zbC0uNS0uMmMtLjIgMC0uNCAwLS42LjJhLjkuOSAwIDAgMC0uNC44aC0uOGExLjUgMS41IDAgMCAxIC45LTEuMyAyIDIgMCAwIDEgLjktLjNsLjkuMi42LjYuMi45LS4xLjRjMCAuMi0uMi4zLS4zLjVsLS41LjMtLjguMWgtLjd2LS40Wm0wIC42di0uNGguN2wuOS4xYy4yIDAgLjQuMi41LjNsLjMuNXYxLjJsLS40LjYtLjYuM2gtMS40bC0uNi0uM2ExLjQgMS40IDAgMCAxLS41LTEuMmguN2wuMS41LjQuNGgxLjJsLjMtLjQuMi0uNmMwLS4yIDAtLjQtLjItLjVhLjkuOSAwIDAgMC0uNC0uNGwtLjYtLjFoLS42Wm0xMC4yLTN2NS42aC0uOGwtMi45LTQuNHY0LjRoLS43di01LjdoLjdsMyA0LjR2LTQuNGguN1ptMSAzLjUuMi0uOWMwLS4zLjItLjUuNC0uNy4xLS4yLjQtLjMuNi0uNGwuOC0uMmMuMyAwIC41IDAgLjguMi4yIDAgLjQuMi42LjRsLjQuNy4xLjktLjEuOS0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zbC0uMS0uOVptLjggMHYuNmwuMy41YzAgLjIuMi4zLjQuNGgxbC40LS40LjItLjV2LTEuMmwtLjItLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0xIC41bC0uMS41LS4xLjZabTUuMyAxLjUgMS4yLTMuNmguN2wtMS41IDQuMmgtLjVsLjEtLjZabS0xLTMuNiAxLjIgMy42LjEuNmgtLjVsLTEuNS00LjJoLjdaIi8+PC9nPjxnIGNsaXAtcGF0aD0idXJsKCNmKSI+PHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0ic3F1YXJlIiBzdHJva2Utb3BhY2l0eT0iLjUiIHN0cm9rZS13aWR0aD0iLjMiIGQ9Ik0xNDMuOCAxNDEuNHYtLjgiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii41IiBkPSJNMTM0IDE0My42djUuN2gtLjZ2LTQuOGwtMS41LjV2LS42bDItLjhoLjJabTYuMSAzLjh2LjZoLTR2LS40bDIuNS00aC42bC0uNyAxLjItMS43IDIuNmgzLjNabS0uOC0zLjh2NS43aC0uN3YtNS43aC43Wm04LjIgMHY1LjdoLS44bC0yLjktNC40djQuNGgtLjd2LTUuN2guN2wzIDQuNHYtNC40aC43Wm0xIDMuNi4yLS45YzAtLjMuMi0uNS40LS43LjEtLjIuNC0uMy42LS40bC44LS4yYy4zIDAgLjUgMCAuOC4yLjIgMCAuNC4yLjYuNGwuNC43LjEuOS0uMS45LS40LjdhMS44IDEuOCAwIDAgMS0xLjQuNiAxLjggMS44IDAgMCAxLTEuOC0xLjNsLS4xLS45Wm0uOCAwdi42bC4zLjVjMCAuMi4yLjMuNC40aDFsLjQtLjQuMi0uNXYtMS4ybC0uMi0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLTEgLjVsLS4xLjUtLjEuNlptNS4zIDEuNSAxLjItMy42aC43bC0xLjUgNC4yaC0uNWwuMS0uNlptLTEtMy42IDEuMiAzLjYuMS42aC0uNWwtMS41LTQuMmguN1oiLz48L2c+PGcgY2xpcC1wYXRoPSJ1cmwoI2cpIj48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiIHN0cm9rZS1vcGFjaXR5PSIuNSIgc3Ryb2tlLXdpZHRoPSIuMyIgZD0iTTE3NC4zIDE0MS40di0uOCIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Ik0xNjQuNiAxNDMuNnY1LjdoLS43di00LjhsLTEuNS41di0uNmwyLS44aC4yWm0zLjEgMy0uNi0uMS4zLTIuOWgzdi43SDE2OGwtLjIgMS41LjQtLjEuNi0uMS43LjEuNS40LjQuNnYxLjZjMCAuMi0uMi40LS40LjYtLjEuMS0uMy4zLS41LjNhMiAyIDAgMCAxLS44LjJsLS43LS4xYy0uMiAwLS4zLS4yLS41LS4zbC0uNC0uNWEyIDIgMCAwIDEtLjItLjdoLjdjMCAuMiAwIC40LjIuNmwuNC4zaC45Yy4yIDAgLjMtLjEuNC0uM2wuMi0uNGExLjggMS44IDAgMCAwIDAtMWwtLjItLjRhMSAxIDAgMCAwLS40LS4zaC0xbC0uNC4zWm0xMC4zLTN2NS43aC0uOGwtMi45LTQuNHY0LjRoLS43di01LjdoLjdsMyA0LjR2LTQuNGguN1ptMSAzLjYuMi0uOWMwLS4zLjItLjUuNC0uNy4xLS4yLjQtLjMuNi0uNGwuOC0uMmMuMyAwIC41IDAgLjguMi4yIDAgLjQuMi42LjRsLjQuNy4xLjktLjEuOS0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zbC0uMS0uOVptLjggMHYuNmwuMy41YzAgLjIuMi4zLjQuNGgxbC40LS40LjItLjV2LTEuMmwtLjItLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0xIC41bC0uMS41LS4xLjZabTUuMyAxLjUgMS4yLTMuNmguN2wtMS41IDQuMmgtLjVsLjEtLjZabS0xLTMuNiAxLjIgMy42LjEuNmgtLjVsLTEuNS00LjJoLjdaIi8+PC9nPjxkZWZzPjxjbGlwUGF0aCBpZD0iYSI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTM3IDVoMTUzdjEzN0gzN3oiLz48L2NsaXBQYXRoPjxjbGlwUGF0aCBpZD0iZCI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTY3IDE0MC41aDMxdjEwSDY3eiIvPjwvY2xpcFBhdGg+PGNsaXBQYXRoIGlkPSJlIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNOTggMTQwLjVoMzF2MTBIOTh6Ii8+PC9jbGlwUGF0aD48Y2xpcFBhdGggaWQ9ImYiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0xMjkgMTQwLjVoMzB2MTBoLTMweiIvPjwvY2xpcFBhdGg+PGNsaXBQYXRoIGlkPSJnIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTU5IDE0MC41aDMxdjEwaC0zMXoiLz48L2NsaXBQYXRoPjxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjExMCIgeDI9IjExMCIgeTE9IjE1Ni4zIiB5Mj0iLTE5LjUiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9Ii4yIiBzdG9wLWNvbG9yPSIjNzBDQzRDIi8+PHN0b3Agb2Zmc2V0PSIuMiIgc3RvcC1jb2xvcj0iI0ZEOEYzQyIvPjxzdG9wIG9mZnNldD0iLjUiIHN0b3AtY29sb3I9IiNGRDhGM0MiLz48c3RvcCBvZmZzZXQ9Ii41IiBzdG9wLWNvbG9yPSIjRDMzRTU3Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImMiIHgxPSIxMDguOSIgeDI9IjEwOC45IiB5MT0iMTI2LjEiIHkyPSItMTkuNSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iIzNGQTcxQSIvPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI0YzNjkwMCIvPjxzdG9wIG9mZnNldD0iLjQiIHN0b3AtY29sb3I9IiNGMzY5MDAiLz48c3RvcCBvZmZzZXQ9Ii40IiBzdG9wLWNvbG9yPSIjRDgxODM4Ii8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PC9zdmc+", + "description": "Displays changes to power consumption data over time visualized with color ranges.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.rangeChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.rangeChartWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: false,\n defaultDataKeysFunction: function() {\n return [{ name: 'powerConsumption', label: 'Power consumption', type: 'timeseries' }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-range-chart-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-range-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Power consumption\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":2592000000,\"interval\":86400000,\"fixedTimewindow\":{\"startTimeMs\":1700577227898,\"endTimeMs\":1700663627898},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"dataZoom\":true,\"rangeColors\":[{\"from\":null,\"to\":5,\"color\":\"#3FA71A\"},{\"from\":5,\"to\":15,\"color\":\"#F36900\"},{\"from\":15,\"to\":null,\"color\":\"#D81838\"}],\"outOfRangeColor\":\"#ccc\",\"fillArea\":true,\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm\",\"lastUpdateAgo\":false,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Power consumption\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"kW\",\"decimals\":0,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "power", + "energy", + "energy usage", + "electric load", + "electricity", + "power efficiency", + "load profile" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/power_consumption_range_chart_with_background.json b/application/src/main/data/json/system/widget_types/power_consumption_range_chart_with_background.json new file mode 100644 index 0000000000..50c12dc23e --- /dev/null +++ b/application/src/main/data/json/system/widget_types/power_consumption_range_chart_with_background.json @@ -0,0 +1,35 @@ +{ + "fqn": "power_consumption_range_chart_with_background", + "name": "Power consumption range chart with background", + "deprecated": false, + "image": "tb-image:cG93ZXJfY29uc3VtcHRpb25fcmFuZ2VfY2hhcnRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==:cG93ZXJfY29uc3VtcHRpb25fcmFuZ2VfY2hhcnRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48L2c+PGcgY2xpcC1wYXRoPSJ1cmwoI2MpIj48cGF0aCBmaWxsPSJ1cmwoI2QpIiBkPSJNNjggNTAuOCA0NS44IDEwMyAzOCAxMTl2MThoMTUwdi0yMy42bC0uNC0yNC41LTEwLjcgMzcuMi02LjItMjMuMi0yLjcgNi44LTExLjQgMjUuNmMtNy43LTI3LjktMTQtMzguMy0yMi40LTY1LjdsLTguNiA0MWgtMTFMMTEyLjUgNDRsLTQuMy0yOC0yMC41IDY3LjItNC44LTJMNjggNTAuN1oiLz48cGF0aCBzdHJva2U9InVybCgjZSkiIHN0cm9rZS13aWR0aD0iLjkiIGQ9Im0zOCAxMTguNiA3LjgtMTYgMjIuMy01Mkw4MyA4MWw1IDJMMTA4LjIgMTZsMy44IDI4LjRjLjggMjUuOCAxLjUgMzkuOSAyLjMgNjUuN0gxMjZsOC42LTQwLjYgMjIgNjUuNSAxNC0zMiA2IDIyLjUgMTEtMzYuNSIvPjwvZz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii41IiBkPSJNMTYuMiAxOC40di42aC0zLjd2LS41bDEuOC0yIC42LS43LjItLjUuMS0uNS0uMS0uNS0uMy0uM2ExIDEgMCAwIDAtLjYtLjJjLS4yIDAtLjQgMC0uNi4yYTEgMSAwIDAgMC0uNC40bC0uMS42aC0uN2MwLS4zIDAtLjYuMi0uOS4xLS4zLjMtLjUuNi0uNmEyIDIgMCAwIDEgMS0uM2wxIC4yYy4yLjIuNC4zLjUuNi4yLjIuMi40LjIuOHYuNWwtLjMuNWE3LjggNy44IDAgMCAxLS44IDFsLTEuNSAxLjZoMi45Wm0xLjgtMi4xLS42LS4yLjMtMi44aDN2LjdoLTIuNGwtLjEgMS41LjMtLjEuNi0uMS43LjEuNS40LjQuNi4xLjgtLjEuN2MwIC4zLS4yLjUtLjMuNmwtLjYuNGEyLjIgMi4yIDAgMCAxLTEuNCAwYy0uMyAwLS40IDAtLjYtLjJsLS40LS41YTIgMiAwIDAgMS0uMi0uN2guN2wuMi41YzAgLjIuMi4zLjQuNGgxbC4zLS4zLjItLjRhMS44IDEuOCAwIDAgMCAwLTFsLS4yLS40YTEgMSAwIDAgMC0uNC0uM2gtMWwtLjQuM1ptNC41LTMuM3Y2aC0uN3YtNmguN1ptMi42IDEuOC0xLjggMi0xIDEtLjEtLjguNy0uOCAxLjMtMS40aC45Wm0tLjcgNC4yTDIzIDE3bC40LS43IDIgMi43aC0uOVptMy4yLTEuNyAxLTRoLjZMMjkgMTUgMjcuNyAxOWgtLjVsLjQtMS43Wm0tMS4yLTQgLjkgMy45LjMgMS44SDI3bC0xLjQtNS43aC44Wm00LjMgMy45LjktMy45aC43TDMxIDE5aC0uNmwuMy0xLjhabS0xLjQtMy45IDEuMSA0IC40IDEuN2gtLjVMMjkgMTQuOWwtLjMtMS42aC41Wk0xNi4yIDQyLjV2LjZoLTMuN3YtLjVsMS44LTIuMS42LS42LjItLjUuMS0uNS0uMS0uNS0uMy0uM2ExIDEgMCAwIDAtLjYtLjJjLS4yIDAtLjQgMC0uNi4yYTEgMSAwIDAgMC0uNC40bC0uMS42aC0uN2MwLS4zIDAtLjYuMi0uOS4xLS4zLjMtLjUuNi0uNmEyIDIgMCAwIDEgMS0uM2MuNCAwIC43IDAgMSAuMmwuNS41Yy4yLjMuMi41LjIuOHYuNmwtLjMuNWE3LjggNy44IDAgMCAxLS44IDFsLTEuNSAxLjZoMi45Wm00LjUtMi43di45YzAgLjQgMCAuOC0uMiAxLjEgMCAuNC0uMi42LS4zLjgtLjIuMi0uNC40LS42LjRhMiAyIDAgMCAxLS43LjJsLS42LS4xYy0uMiAwLS40LS4yLS41LS4zbC0uNC0uNC0uMy0uOHYtM2wuNS0uN2MuMS0uMi4zLS40LjUtLjRsLjgtLjIuNi4xYTEuNCAxLjQgMCAwIDEgLjguN2wuMy43djFabS0uOCAxVjM5bC0uMS0uNWExIDEgMCAwIDAtLjMtLjNjMC0uMS0uMS0uMi0uMy0uMmExIDEgMCAwIDAtLjMgMCAxIDEgMCAwIDAtLjUgMGwtLjMuMy0uMi42LS4xLjh2MS44bC4yLjUuMi4zLjMuMmguOGMuMiAwIC4zLS4yLjQtLjNsLjItLjZ2LS44Wm0yLjYtMy43djZoLS43di02aC43Wm0yLjYgMS44LTEuOCAyLTEgMS0uMS0uOC43LS44IDEuMy0xLjRoLjlabS0uNyA0LjJMMjMgNDFsLjQtLjcgMiAyLjdoLS45Wm0zLjItMS43IDEtNGguNkwyOSAzOWwtMS4yIDQuMmgtLjVsLjQtMS43Wm0tMS4yLTQgLjkgMy45LjMgMS44SDI3bC0xLjQtNS43aC44Wm00LjMgMy45LjktMy45aC43TDMxIDQzLjFoLS42bC4zLTEuOFptLTEuNC0zLjkgMS4xIDQgLjQgMS43aC0uNUwyOSAzOC45bC0uMy0xLjVoLjVaTTE0LjggNjEuNXY1LjdoLS43di00LjhsLTEuNC41di0uN2wyLS43aC4xWm0zLjIgMy0uNi0uMi4zLTIuOGgzdi43aC0yLjRsLS4xIDEuNS4zLS4yaC42bC43LjFjLjIgMCAuNC4yLjUuNGwuNC42LjEuOC0uMS43YzAgLjMtLjIuNS0uMy42bC0uNi40YTIuMiAyLjIgMCAwIDEtMS40IDBjLS4zIDAtLjQgMC0uNi0uMmwtLjQtLjVhMiAyIDAgMCAxLS4yLS43aC43bC4yLjVjMCAuMi4yLjMuNC40aDFsLjMtLjMuMi0uNGExLjggMS44IDAgMCAwIDAtMWwtLjItLjRhMSAxIDAgMCAwLS40LS4zaC0xbC0uNC4zWm00LjUtMy4zdjZoLS43di02aC43Wm0yLjYgMS44LTEuOCAyLTEgMS0uMS0uOC43LS45IDEuMy0xLjNoLjlabS0uNyA0LjItMS41LTIgLjQtLjcgMiAyLjdoLS45Wm0zLjItMS43IDEtNGguNkwyOSA2M2wtMS4yIDQuMmgtLjVsLjQtMS43Wm0tMS4yLTQgLjkgMy45LjMgMS44SDI3bC0xLjQtNS43aC44Wm00LjMgMy45LjktMy45aC43TDMxIDY3LjJoLS42bC4zLTEuOFptLTEuNC0zLjkgMS4xIDQgLjQgMS43aC0uNUwyOSA2M2wtLjMtMS41aC41Wk0xNC44IDg1LjZ2NS43aC0uN3YtNC44bC0xLjQuNXYtLjdsMi0uN2guMVptNS45IDIuNHYuOGMwIC41IDAgMS0uMiAxLjIgMCAuMy0uMi42LS4zLjhsLS42LjRhMiAyIDAgMCAxLS43LjJsLS42LS4xYy0uMiAwLS40LS4yLS41LS4zbC0uNC0uNS0uMy0uN3YtM2MuMi0uMy4zLS42LjUtLjdsLjUtLjVoMS40YTEuNCAxLjQgMCAwIDEgLjguN2wuMy43djFabS0uOCAxdi0xLjhsLS4xLS41YTEgMSAwIDAgMC0uMy0uM2MwLS4xLS4xLS4yLS4zLS4yYTEgMSAwIDAgMC0uMy0uMSAxIDEgMCAwIDAtLjUuMWwtLjMuMy0uMi41LS4xLjl2MS43bC4yLjUuMi40LjMuMmguOGwuNC0uNC4yLS41Vjg5Wm0yLjYtMy43djZoLS43di02aC43Wm0yLjYgMS44LTEuOCAyLTEgMS0uMS0uOC43LS45IDEuMy0xLjNoLjlabS0uNyA0LjItMS41LTIgLjQtLjcgMiAyLjdoLS45Wm0zLjItMS43IDEtNGguNkwyOSA4N2wtMS4yIDQuMmgtLjVsLjQtMS43Wm0tMS4yLTQgLjkgMy45LjMgMS44SDI3bC0xLjQtNS43aC44Wm00LjMgMy45LjktNGguN0wzMSA5MS40aC0uNmwuMy0xLjhabS0xLjQtNCAxLjEgNCAuNCAxLjhoLS41TDI5IDg3bC0uMy0xLjVoLjVaTTE4LjQgMTEyLjdsLS42LS4yLjMtMi44aDN2LjdoLTIuNGwtLjIgMS41LjQtLjJIMjAuMmwuNS41Yy4yLjEuMy4zLjQuNnYxLjVsLS40LjYtLjUuNGEyLjIgMi4yIDAgMCAxLTEuNCAwbC0uNi0uMi0uNC0uNWEyIDIgMCAwIDEtLjItLjdoLjdjMCAuMiAwIC40LjIuNSAwIC4yLjIuMy4zLjNsLjYuMi40LS4xYy4yIDAgLjMtLjIuNC0uM2wuMi0uNGExLjggMS44IDAgMCAwIDAtMWwtLjItLjRhMSAxIDAgMCAwLS40LS4zbC0uNS0uMS0uNS4xYy0uMiAwLS4zLjItLjQuM1ptNC41LTMuM3Y2aC0uN3YtNmguN1ptMi42IDEuNy0xLjkgMi0xIDF2LS43bC43LS45IDEuMy0xLjRoLjlabS0uNyA0LjMtMS41LTIgLjQtLjcgMiAyLjdoLS45Wm0zLjEtMS43IDEuMi00aC41bC0uMyAxLjUtMS4yIDQuMmgtLjZsLjQtMS43Wm0tMS4xLTQgLjkgMy45LjIgMS44aC0uNWwtMS40LTUuN2guOFptNC4zIDMuOS45LTRoLjdsLTEuNCA1LjhoLS41bC4zLTEuOFptLTEuNC00IDEuMSA0IC40IDEuOGgtLjVsLTEuMi00LjItLjMtMS41aC41Wk0yMSAxMzYuMnYybC0uNC44LS42LjRhMiAyIDAgMCAxLS44LjFoLS42bC0uNS0uMy0uMy0uNS0uMy0uN3YtM2wuNC0uOC42LS40LjctLjFoLjdhMS40IDEuNCAwIDAgMSAuOC44bC4zLjd2MVptLS43IDFWMTM1LjRsLS4yLS41YTEgMSAwIDAgMC0uMi0uNGwtLjMtLjJhMSAxIDAgMCAwLS40IDAgMSAxIDAgMCAwLS40IDBsLS40LjQtLjIuNXYyLjZsLjIuNWMwIC4yIDAgLjMuMi40bC4zLjJoLjhjLjEtLjEuMy0uMi4zLS40bC4zLS41di0uOFptMi42LTMuN3Y2aC0uN3YtNmguN1ptMi42IDEuNy0xLjkgMi0xIDF2LS43bC43LS45IDEuMy0xLjRoLjlabS0uNyA0LjMtMS41LTIgLjQtLjcgMiAyLjdoLS45Wm0zLjEtMS44IDEuMi00aC41bC0uMyAxLjYtMS4yIDQuMmgtLjZsLjQtMS44Wm0tMS4xLTQgLjkgNCAuMiAxLjhoLS41bC0xLjQtNS43aC44Wm00LjMgNCAuOS00aC43bC0xLjQgNS44aC0uNWwuMy0xLjhabS0xLjQtNCAxLjEgNCAuNCAxLjhoLS41bC0xLjItNC4yLS4zLTEuNWguNVoiLz48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuNSIgc3Ryb2tlLXdpZHRoPSIuNCIgZD0iTTM3IDE2aDE1MU0zNyA0MC44aDE1MU0zNyA2NS41aDE1MU0zNyA5MC4zaDE1MU0zNyAxMTVoMTUxIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTQxLjggMTQxLjN2NS43aC0uN3YtNC44bC0xLjUuNXYtLjZsMi4xLS44aC4xWm00LjUgMHY1LjdoLS43di00LjhsLTEuNS41di0uNmwyLjEtLjhoLjFabTguNyAwdjUuN2gtLjhsLTIuOC00LjR2NC40aC0uOHYtNS43aC44bDIuOCA0LjR2LTQuNGguOFptMSAzLjZjMC0uNCAwLS42LjItLjkgMC0uMy4yLS41LjMtLjdsLjYtLjRjLjMtLjIuNS0uMi44LS4yLjMgMCAuNiAwIC44LjIuMyAwIC41LjIuNi40bC40LjcuMi44di4xYzAgLjMgMCAuNi0uMi45bC0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zdi0uOVptLjcgMCAuMS42LjIuNS40LjRoMS4xbC4zLS40LjMtLjV2LTEuMmMwLS4yLS4yLS40LS4zLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41di41Wm01LjMgMS40IDEuMi0zLjVoLjdsLTEuNSA0LjJoLS41bC4xLS43Wm0tMS0zLjUgMS4yIDMuNi4xLjZoLS41bC0xLjUtNC4yaC43Wk03MC44IDE0MS4zdjUuN2gtLjd2LTQuOGwtMS41LjV2LS42bDIuMS0uOGguMVptNS45IDUuMXYuNmgtMy44di0uNWwxLjktMiAuNS0uNy4zLS41di0xbC0uNC0uM2ExIDEgMCAwIDAtLjUtLjJjLS4zIDAtLjUgMC0uNi4yYTEgMSAwIDAgMC0uNC40bC0uMS42aC0uOGwuMi0uOWMuMi0uMy40LS41LjctLjZhMiAyIDAgMCAxIDEtLjNsMSAuMmMuMi4yLjQuMy41LjZsLjIuOHYuNWwtLjMuNWE3LjggNy44IDAgMCAxLS44IDFsLTEuNSAxLjZoMi45Wm03LjMtNXY1LjZoLS44bC0yLjgtNC40djQuNGgtLjh2LTUuN2guOGwyLjggNC40di00LjRoLjhabTEgMy41YzAtLjQgMC0uNi4yLS45IDAtLjMuMi0uNS4zLS43bC42LS40Yy4zLS4yLjUtLjIuOC0uMi4zIDAgLjYgMCAuOC4yLjMgMCAuNS4yLjYuNGwuNC43LjIuOHYuMWMwIC4zIDAgLjYtLjIuOWwtLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM3YtLjlabS43IDAgLjEuNi4yLjUuNC40aDEuMWwuMy0uNC4zLS41di0xLjJjMC0uMi0uMi0uNC0uMy0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLS41LjFsLS40LjQtLjIuNXYuNVptNS4zIDEuNCAxLjItMy41aC43bC0xLjUgNC4yaC0uNWwuMS0uN1ptLTEtMy41IDEuMiAzLjYuMS42aC0uNWwtMS41LTQuMmguN1pNOTkuOCAxNDEuM3Y1LjdoLS43di00LjhsLTEuNS41di0uNmwyLjEtLjhoLjFabTMuMiAyLjVoLjVsLjYtLjEuNC0uNC4xLS41di0uNWwtLjQtLjRIMTAzYS45LjkgMCAwIDAtLjUuOWgtLjdjMC0uMyAwLS42LjItLjhsLjYtLjZhMiAyIDAgMCAxIDEtLjJsLjguMmMuMy4xLjUuMy42LjYuMi4yLjMuNS4zLjhsLS4xLjUtLjQuNS0uNS4zLS44LjFoLS42di0uNFptMCAuNnYtLjRoMS42bC41LjQuMy41djEuMmwtLjUuNS0uNS40aC0xLjVsLS41LS4zYTEuNCAxLjQgMCAwIDEtLjYtMS4yaC44di41bC41LjRoMS4xbC40LS40LjEtLjYtLjEtLjZhLjkuOSAwIDAgMC0uNS0uM2wtLjYtLjFoLS41Wm0xMC0zdjUuNmgtLjhsLTIuOC00LjR2NC40aC0uOHYtNS43aC44bDIuOCA0LjR2LTQuNGguOFptMSAzLjVjMC0uNCAwLS42LjItLjkgMC0uMy4yLS41LjMtLjdsLjYtLjRjLjMtLjIuNS0uMi44LS4yLjMgMCAuNiAwIC44LjIuMyAwIC41LjIuNi40bC40LjcuMi44di4xYzAgLjMgMCAuNi0uMi45bC0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zdi0uOVptLjcgMCAuMS42LjIuNS40LjRoMS4xbC4zLS40LjMtLjV2LTEuMmMwLS4yLS4yLS40LS4zLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41di41Wm01LjMgMS40IDEuMi0zLjVoLjdsLTEuNSA0LjJoLS41bC4xLS43Wm0tMS0zLjUgMS4yIDMuNi4xLjZoLS41bC0xLjUtNC4yaC43Wk0xMjguOCAxNDEuM3Y1LjdoLS43di00LjhsLTEuNS41di0uNmwyLjEtLjhoLjFabTYgMy44di42aC00LjF2LS40bDIuNS00aC42bC0uNiAxLjItMS43IDIuNmgzLjNabS0uOC0zLjh2NS43aC0uN3YtNS43aC43Wm04IDB2NS43aC0uOGwtMi44LTQuNHY0LjRoLS44di01LjdoLjhsMi44IDQuNHYtNC40aC44Wm0xIDMuNmMwLS40IDAtLjYuMi0uOSAwLS4zLjItLjUuMy0uN2wuNi0uNGMuMy0uMi41LS4yLjgtLjIuMyAwIC42IDAgLjguMi4zIDAgLjUuMi42LjRsLjQuNy4yLjh2LjFjMCAuMyAwIC42LS4yLjlsLS40LjdhMS44IDEuOCAwIDAgMS0xLjQuNiAxLjggMS44IDAgMCAxLTEuOC0xLjN2LS45Wm0uNyAwIC4xLjYuMi41LjQuNGgxLjFsLjMtLjQuMy0uNXYtMS4yYzAtLjItLjItLjQtLjMtLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0uNS4xbC0uNC40LS4yLjV2LjVabTUuMyAxLjQgMS4yLTMuNWguN2wtMS41IDQuMmgtLjVsLjEtLjdabS0xLTMuNSAxLjIgMy42LjEuNmgtLjVsLTEuNS00LjJoLjdaTTE1Ny44IDE0MS4zdjUuN2gtLjd2LTQuOGwtMS41LjV2LS42bDIuMS0uOGguMVptMyAzLS41LS4yLjMtMi44aDIuOXYuN2gtMi4zbC0uMiAxLjUuNC0uMS41LS4xLjguMS41LjQuMy42YTIuNSAyLjUgMCAwIDEgMCAxLjVjMCAuMy0uMi41LS4zLjYtLjEuMi0uMy4zLS42LjRhMiAyIDAgMCAxLS44LjJsLS42LS4xLS42LS4zLS4zLS41YTIgMiAwIDAgMS0uMi0uN2guN2wuMS41LjQuNGgxbC4zLS4zLjItLjRhMS44IDEuOCAwIDAgMCAwLTFjMC0uMiAwLS4zLS4yLS40YTEgMSAwIDAgMC0uMy0uM2gtMS4xbC0uMy4zWm0xMC4yLTN2NS43aC0uOGwtMi44LTQuNHY0LjRoLS44di01LjdoLjhsMi44IDQuNHYtNC40aC44Wm0xIDMuNmMwLS40IDAtLjYuMi0uOSAwLS4zLjItLjUuMy0uN2wuNi0uNGMuMy0uMi41LS4yLjgtLjIuMyAwIC42IDAgLjguMi4zIDAgLjUuMi42LjRsLjQuNy4yLjh2LjFjMCAuMyAwIC42LS4yLjlsLS40LjdhMS44IDEuOCAwIDAgMS0xLjQuNiAxLjggMS44IDAgMCAxLTEuOC0xLjN2LS45Wm0uNyAwIC4xLjYuMi41LjQuNGgxLjFsLjMtLjQuMy0uNXYtMS4yYzAtLjItLjItLjQtLjMtLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0uNS4xbC0uNC40LS4yLjV2LjVabTUuMyAxLjQgMS4yLTMuNWguN2wtMS41IDQuMmgtLjVsLjEtLjdabS0xLTMuNSAxLjIgMy42LjEuNmgtLjVsLTEuNS00LjJoLjdaIi8+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJkIiB4MT0iMTA5LjQiIHgyPSIxMDkuNCIgeTE9IjE1MC43IiB5Mj0iLTEzLjQiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9Ii4yIiBzdG9wLWNvbG9yPSIjNzBDQzRDIi8+PHN0b3Agb2Zmc2V0PSIuMiIgc3RvcC1jb2xvcj0iI0ZEOEYzQyIvPjxzdG9wIG9mZnNldD0iLjUiIHN0b3AtY29sb3I9IiNGRDhGM0MiLz48c3RvcCBvZmZzZXQ9Ii41IiBzdG9wLWNvbG9yPSIjRDMzRTU3Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImUiIHgxPSIxMDguMSIgeDI9IjEwOC4xIiB5MT0iMTIyLjEiIHkyPSItMTMuMyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iIzNGQTcxQSIvPjxzdG9wIG9mZnNldD0iLjEiIHN0b3AtY29sb3I9IiNGMzY5MDAiLz48c3RvcCBvZmZzZXQ9Ii40IiBzdG9wLWNvbG9yPSIjRjM2OTAwIi8+PHN0b3Agb2Zmc2V0PSIuNCIgc3RvcC1jb2xvcj0iI0Q4MTgzOCIvPjwvbGluZWFyR3JhZGllbnQ+PGNsaXBQYXRoIGlkPSJjIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMzggMTZoMTUwdjEyMUgzOHoiLz48L2NsaXBQYXRoPjxwYXR0ZXJuIGlkPSJhIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBwYXR0ZXJuQ29udGVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCI+PHVzZSB4bGluazpocmVmPSIjZiIgdHJhbnNmb3JtPSJtYXRyaXgoLjAwNTAzMTQ1IDAgMCAuMDA2Mjg5MzEgMCAwKSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgeD0iMCIgeT0iMCIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMyIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMDg1XzY5Mjk0Ii8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMDg1XzY5Mjk0IiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PGltYWdlIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3QkRBQUlCQVFFQkFRSUJBUUVDQWdJQ0FnUURBZ0lDQWdVRUJBTUVCZ1VHQmdZRkJnWUdCd2tJQmdjSkJ3WUdDQXNJQ1FvS0Nnb0tCZ2dMREFzS0RBa0tDZ3IvMndCREFRSUNBZ0lDQWdVREF3VUtCd1lIQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnci93Z0FSQ0FDZkFNY0RBUkVBQWhFQkF4RUIvOFFBSFFBQUFRUURBUUVBQUFBQUFBQUFBQUFBQndNRkJnZ0JBZ1FBQ2YvRUFCc0JBQUVGQVFFQUFBQUFBQUFBQUFBQUFBSUFBUU1FQlFZSC85b0FEQU1CQUFJUUF4QUFBQUQ2TSsvY2VtUWFFS1ppaVRja2dqYXJOVDdtdFhta3IzZjZySmsxd0VpRk1tMUpreVNaQm9iYXV5VHQ1MndtOG44bDVMS2NyOGR2SkUycHg2T3lEanpTdFh2SXUxZHpaMkdqYnREdlpwajJhTXZ2UXB1eVJpbTdKbUtSeDZ1K2lmRHRsTjVQNUx5V0U1VzVEYlRKdFhIUW00ekJabFZ5aE9JS2R1QTFaT3BOWWZYcVRXOURMTGNNcnMxa2pXaE1tUTZHMkhXRTJFM2t2Si9KZVNLZkk3V0hIUWx6RzJVT1dZVlJHTEtkZ2RSR1ZibGVHUXlEQ0dSZ1k3bTcrWk5ydGZxbERSMXE2MWRZZFladkpZUzVYWnNGemZ5dXhvU1Rka1NGVlBobUZrQ0lmT1RWdzFJYkc3SWhJb0FqQ1FTek5FNzM0Wm5ZaklsMkNjV1lWU1dxVFM3Sk1PaWZVaFhTY1NFcGNudDZvZVVrdWxoMWhPRGE3ZE9QWFo3T29kSWhGM1dZcERHY1BpMFByeXdHT1hnWlI5M3RIcFo4cW1Uc2E4N0lrT1hiVkxMSW84dHM4N3NzeTg2MVRhSlYwWXd0RWZRQTI1dTEzcWNNbUlhaE1xV0JHcHhDV042aVpGK3pONkFyM1lTZFBFNHV3ckZGbVJ0MmZka1FlYTFmTXQzZnlXanJVbUJiRUxDVEhBOXVydFh5THNJWUZITFF2RzByamJlV1JKUmlNMVdOQkxWQ2xNenNkeTlPcmdtTkNEblJvcEVubnRGUkxaTnFscTZSZHdQSW1GblJCalhNRHlrMmt1Qm1vcG4yYjliT2V6UXpCK0tkMXMweUpNRlc2Y290aW5GS2Y2b1hxait6OXp2TGNTOWxMeERxbHpwY2JvVkc0MVR6Y0ZMcDRwZ0lyR210bStibVhlK2pPeG5vTVVjQ1NXeVIxbW8yeTNhZ1pIWU9TRGFpVnBRREtNVW55N0htV3dyZ2RiT3VwSUV1UWRNM2xDWHlZcEZCaEtFUnlVVXhkMzZONzNPcXVUUzQ3T3FYVUxOdUwwSU10cGd6YkU4MUFBbEdReXNOc3FTMVM1V1hVeTg2RlF5aHVjQklhdERFRDBpazZhUW9JTkhQVWZMNkMvdXh6T0RUTWx4a05Ob0pyUVg0S2xFcXVSekhLekZaY1hJVkl5blVQdEorbG1ZV1RNTGhNRDdiUUFJanRUVVVSTXp1SVA3akMzZW9kTG9MMTZQTlY0YzZlSXpkWWloZGV5ZnJjVW4wTTM1MDVWa3kyR3ZWTkRRYU9lM21kY3k3UmNna1VGaVNGRFUydG9GaWVvREpWWkdvUUFhYTk3Vi9NNDRNcTIxZHE2ZW56VUIxS3Z6ZXJ5ZlN6ZG9oN0ExZUliUU9scFZraWV4MHRtMlBUYzFReU9iNjZjbDBDOGE0UlcwY3pLY1ZBYXVnVzc5S0hSRmNjQStiRUZyNlFIVmZwWS9uR3h1VGE5bGVrNVJ5bWorWU9SYitudWhYR2xLNC93QlM1VGJWeGg3cjUzWm5iVEp1WUxrWi9aZmpPelVBdFdIZUlsM2pvVEJJUnpoRVpQYVlGODhhay8wTXUxWlJORFRXS1FZVk52NkNXOFZyWmZKQ2tmMlcxcWd0bUVGUVd4TUZhclNtS0xXVjBFbGlzZlI3dU1xditZY3hNMFUzUEVxeVlGbzAyYUFFYWV4ZGxWOHdkRzQyMVRsTW9BdWc0Qno5cjZWYitGQlFWTmMyeGRuVHFpYUFvRTA0RXJ4dytyZE4rcG14ZlJ4cks2dWZTR2VvVnRHaEZKNjhjeE5rMjlmeU5XUEt2VWV2V3laOUcvdHpGYitKN1d3L28vbis5SzJEc1RiMTV2czdDK3BlT3hERTJ4Qnp2U0V2cWVYMXk5R09SYU5lc2UxTGNuZmwvVjhud1BFZE9rNTRNYzFyeVRMdnRrbGhHUks2dUNFT0g3dVU2RUVaaEdaNjJWMFl1NHYwZk5PekJHcVdnNlpmUnJkTHdqSGk3a1JtbFp6R1QxNFdaV2hFY1pWeHR1SzZ1REVpa2ZJQTdLTzg2UkcwcG5TVjh3R3NMd09LMFVMTUlLaWhtOFV2UkZNTUxNTzZWZ1liVmFwcWVDZVhOSDAxNVIzSUVtR1NNRkNTVklKV0RnSmNzZ2YveEFBd0VBQUNBZ0lCQXdNRUFRSUdBd0FBQUFBQ0F3RUVBQVVSQmhJVEZDRWlFQ014TWhWQlVRY1dKQ1V6TkNBbVlmL2FBQWdCQVFBQkNBUGpPTTR6ak9NTCswYk80aWxVT1R1TDVDSFh0UWV3aXZOdllWR1ZISTc2WEdjWnhuR2NaeG5HY1p4bkdjWnhuR2NaeG5HY1p4bkgwZ2Y2ejFGUlhhVW1aOFI3TGFNdERXdnhZWUtyR3Y3cXJXVnhHMkhkNDdIR2NaeG5HY2ZUak9NNCt2R2NaeG5HY1p4bkdjY3puR2IvQUZTYlJMZWZ5WFo3TDhWNmwrbEpTbHpkYlRtUVJZVnR1SldrN0NoaVdMdUlQbUo0empPTTR6ak9NNHpqT000empPTTR6ak9NbiswY1p4bTBURDRTcVJMelc1YWFkYXBkUWJZckMzcjNFMDdTWXNXZ2NienZhbHNuZHFwaTdlVnNyc1BzQTRSWUpDZjY4WnhuR2NaeG5HY1p4aEVJZnQ1aG45T000K2tSL1hPTTR6WnJZZmhCVmJWQXladlY2eXlHcEZVaEhnSXpaMUtwY0dOeWphVFc5VzVkRXR1MytYbTB5NVU0QjRrdDNiZXErcmxUUEhZaVJLTzRlTTR6eXFqUEpNL3J3K2M4SFA3aWhRL2pqNmNaeHpuR2NaeG15dlZsV0YxNVM5aUxaMTQxN2xIU0ViR3lzTFdzU3ExSy9rLzFMZU1PZ3EyZGdTczByS2dHWlpTVlpzZytyNnBtc2lhMThObGR0SkMxcmhNU09RYkZWRWUrZHZINDR6MitudG50bkdUL0FHempPTTR6ak4yVmF1K3Zic0xuWXJZYzVWdGRtcVczQkQ3VVNRL3JuR1JlUlh0dFU0WkV4N2cyQ0tUSjVhR3B0SklyQnhVdDJZaHRTTmpZcE1tTm5YZDQwQjRWdUJudGsvam5OVC93a291M09NNCtrUm5IMTR6WjAxM3JsZFRQKzFZYUNkUlRZdXN1NVpUSGRYSEZUK1F3cGdZOTZhdm14aDlJVGN2THZHU0YycW4zSEpzSXNleXJOT3ZjczlqMlVIQlk4OGR2cHlHZFo2c0djcTNQcVRoUjkxS1k4bnc0enVDUHo1Vlp4bkgxNHlmYkxjeSs5NmJEQVYzV3hHdUxtaXFnZEllS3dqaEtncDdzN0JHZmRZVExmbjBFcnQvbEJ4ZjY4Wllxb2ZITFptelZzL1lDMTZvb0N5VkdxWXdKTjZiQURXZ2JTVHBsUG9sY3VrSFdVMWxlSWVZVXNmeHgvd0NNekE1MnlYdVRlQmM4ODdYZzgrNmxFTTFpckE2OHY5Tjh2a1dRTVIrQWo3MmY0ZGNlZmNEQlJJRjNqOTQ4aWdrYkhma0xFMStKbSt0LzVkckx0MVplMU5tUFY3VkZEWVZsbTFOYVZ3QXRxZHZwVjl1Y2ZYakpuMzdSZ1A2emllWXNPdlpzTEMvVnVaaXhzZHNPcmF2dWxaK1RqT01lN3dlUnNkRElTdnp2VkFSSDQ0d28rNE9URWlYY1BYM3o2ZjdvN1M4NHRuYktYRWVVTmJWczJEN0VvdjAwSzdIVytvN01PN2E5WTk0K0ZqT2ZqOCs1WkVjWi93RGNidFZSWVZVVHIwdnVMN2MyaVVVbTJoZ0hzU3NhRGFDb1RZZUFQdTE2L3NmWk0vdGJXUFkzam9nUTlMQlp4bkdNajVEamJOVkJkcnY4UkpBdWtMV0E0Q3JwYm5WQjJ2U0U2cHBkck41SHFXOUxyaGhYMHU5QlBtN29VZFdqd0RRYUxSN2t3UDhBV2NlN3hSSEhwNWI3MmR0TUJ1OWZKVmlpdFdRa04wM3NOMHNqc2FudlRVQjU3TnFMdmhXdU94VWU4YzVhL0I1MGYyd2dZajN5NXZscmJBVW5kWFhydDFkV2dyWHYySWMydHhST2xxSlZpd2FkVlJGYVU3MFpCblJxdlRkUFdZWjAwOVA4NXVFRDNMZ2VSNjN0R05aRElHczhjSTdTUjV3TE93TmhlVmR6N2tIaDdVUStNYnU2WjdDSEhyclZNckVKZHR2OVE5aGkycEtuVFpwOVBiR3p0YjZyV1JObnZ3SEhBL0t4YnFOTTE1MGt3SVRFd1ZuNDl3K09sM1M1dDRQL0FHNGJJTDhoUjc5VHVTV25PWW5icHEwRnBIYzlZT1NIaG5wbEYrNm03V25weUppNjhTYkRQekhXZ1EzVEd1dnp4N1pIOTg1K1JSbjdIT01wVXBqMzNTMHh2UERucGUrMTJOM01NSzBwT2VzR0V6blJQd3NvNHMzR1VGUyt4NXlYSmVSTzJYSFdsaHA5T3ZXaWwzdEhZYStmMUowT2o3VzV0VzBkVERhUDBiUmp1UGYxeWpWTjUxM3A1MWNkL1hIWTJpck5Kc1ZxMjE5R1ZXRkZpMUN0cGIyYXpGWlhhalZWZkljRkdlVCt3bDd6T0NmekxDS0pqTHZ6Nm1FY1crYURNMm5lbmIxM091TE1xN0x4OUxReW5za0lucUEvOXVqaFZsbHp5eGxKemg2eXNWb3FjSHA3SzVpbFFJQmtvb2EyVDdnNnJyQlgzSkVONjQ2clZPeWpkK3NicG5FWFpZZDB2WmxPNFJWSG1iR3BJcTdnMklUYk03Sk9HMFY1NnZWdnNhemQrSHlPOVRFUnppdHB1MmJFYTdBMmx0MXRsTmFiemxrZm0yUFVENm4vQUFzMmJVN2FidG9kM2NVdjFDclY4ckczcnpWdjdQdHFHb3E3UEhkOGtia3BkVEFHVUgvOW1jcVdxNDlWc1NHdU9UMXo0eFQ0aFlSbEcxSHFMSTV1eUV0NkRjdXVTV3ZieDVvYjA0WW5ySmhtaHNKeTZYd3JOWHBRVW0ybUoyanY5M1d4WnZyMmVtVkN1dHNwL2pscGZyOW8yYlUrZFZtNDdabktsM0xZN2hxeHFYYkhyRXJpMWFOeVRYRzBlNExoK1EzV3pKaDVhckduWXBBTmxCZHZZdXIyUXlTQzVkN3ZHRURmN0ZId214UDhxellocnBrYUxzbTg0VEVJcjNIY01kRDJlYll4TXZjaHl2RGtEV21qTVRRZXNkY2N0VVVCV1dLOWV6dGVFNWJjMmJpU1BXdkF0ZTVXVTJyS291U2lvc0VFb0RtUmhnZ3dRNTlUUHQ1bEV2ei9BQ0lKTG15NllJR1Y1KzFucVE5bWp5SHBoR1hUQTJEbUkyTmRuWUFEWlFDSU1hN3dqNVRXWkhvVzlyZHBYR3hNVC9JS1VtSnoxY2xlREljbXExenlkdXE2UHRZVmp4Ni94WkxZcXJJaDFiKytGc3hlM0s5WkdDTzBkRVdlRTk1WnFyQVZsdTFsSHhMZFZER0lrdHZYT3VLY2pZTGVVK0JPeU43WldwM2RTSHpzWHNlV2p3dWpZYXFDbjFzcHNjbVYvd0RrSHo0bTFtMXlocTN5WVRDMitwcmVpbXRNTkNOV2NaSVV6bnZaNHRaaVgwd2NMaXRXS2pwSXNsZFNmMEt5UnJnTWQzTi9laFpYV0R0Yk5kcEZ5TTY4eW4zL0FJczgvOFFBUVJBQUFRTURBUVFIQXdnSkJBTUFBQUFBQVFBQ0VRTVNJVEVFSWtGUkVCTWpNbUZ4Z1VKU2tRVWdNR0toc2NIUkZETkFRMU5VY3BMaEZTUmpnNkt6OFAvYUFBZ0JBUUFKUHdQNTFVQXhnU3JSMVp1NnVjandubnhWRTFLUkFMV3Q3N1J6OFU1cFo5VDltWUxoV0VFOHVLTit6aUtsRVZtNGQ0cDdyS1Bkb2U4SjE4WVZDcHZQdVpwbmltOVc3SGUvWStIUnRUMmIrUVhiaHh4Q2NLUFozTjVhNmVBVkJnWTF4a2s1T2RCK2ErVUdiUVdQZ05mVTMyRWN1WVcxc1BXTXNxTXN5emlxb08vWTlwR2hUckNEQmEvSDdIb2EyWThpdFN6TGdOTTRQa3V5M08xdEc3TW5ndGk2eHRWb2w5UGdmSmJJRGRUdFk5dGZNaklnajFXek5yQnhhR0NsM3ZYN2xTWVhYUUtOUEFwY2dmRlV0eXAzTTVsSFRYNkZ3Q2E1M2tQbjk0MWZ3S2dOdHRzalhtbTNVeTF6OEJja1RUckRMYWxNeEhpVlRyMWl4d2QyWjNqNHh6VlRhS0xXMVFLYkdUZUJQdFQ5eTJTcFdGKzVWWnI1RnMvY21pQjNpMStuZ2dQNjJsR1J6NlhqMFZGM3JoV3QrMVZYSDdFd2ZRTzdUVzArV3FmM0dObXIvaE5OMGt1amtPU3B6SW5QQmNjZ0g3K2h6bWt2Ry9UZEIwQ0ZXdkZRRzVsWFB3VkEwNWE2NXdyWk9tcXBNTm9EdjBpbG1PVWowUkZOaGZhYXJoTjBuZ1B4VlNxUHNCVk9mUEtIMGdZYmJnMXBHWG1PNnFsTnIzTXd4MmZSVTVObG5nUithTTZkSWNONmI0d0U0RWN3cVFOVDJTTlVHUG1NQ2tMa0g3TStlMEpwUVpuM2Z4V3l1Z054VlpOcGJ6STRLbzJwZjNZTWdyWDNlaHVsUS9SUnVYVkdrODBiWHZwQzBlN0NxZnVZZzkxcFAvMnE5MWFqb2JEbm52TGJLbDFIYlhOWTRIZ3FBcmZYcGFqMEtxU1JxM2lQUk1ucytmaXJhakxMU0xkNVBOUGFRN3RHeHVRYzVDcUdtNXRUVmoreitQTlpaaTB4bGFSdkRvZVBpbi9ENStHdllHbDNxaHVzb2k0alhqbnpDR09yQmU3bUU2WXhLTUhtRWJpdmg2b3R6dDd1NjZWd1ZPU05IY1I2cXIxL1o2UE9kZWFKb1RveCtMdlZVQmpReGxUV1o3WFduTnM2Si9WTXAxTE9yYzBrT0oweHdUbml4a2w3ZUdZMUhpbWgyNzNvMVZNZkQ2Qmw5MXRNTTQ2TGZEZGxreDdlVGxPSFdXTnRQTEdpeER5Q3NkUDgvd0RtczgycGx2bXBrak1IVk5CR2hCUWREOW9iVExKM2NxaEc0ZCtudkQvQ0RhalJWRU9CMFhhNzlXVnBZSStoeXdWU0hEN0xrOE5MOWttbDlxbzIwMnNFdWMzUThTQnhWWVZIQ3BsNDQ5SWtzWVhSNktaMnQ1cVZNNlovejh6MUN4WnROSjI5ajJrZlpPRlRnM0NhbFB6VDJ4VHJrNU9aaFZXc3N4dk9pZkxtdGtydlp6WUxXLzNGVmFORzlzaTVwYzdwd09uZWRXSkZOeDd1TWxiUzd2T05RQVJNbkNveGJzTWkzMlRLWnYyNnM5M21vdEphNEJ2RENxWjkwYTlBL2NuN2tON09lbm1xN1duNnhSMHNQL2tFWUJhTStpWlVkYVpjUndDcTFYQjFYTmpyZFZzbldkWHR4RFM0YUNBaVFlVHVJODB3VUp4dkhYMVJ1SFBwYkxuR0doRy82dnNqMDRyMmFlME9IOWliL3VlcUJiOWRNdVA2SDJnOTAzb0dlOTF2SkMwdW9oeEZQQTFUQVBJZEh1ZmdVYzU2SFVhdjhTYWtRaE11d0tiTmZpdC9keUxoSVRLcEIyZ0h0cWwzcDVKM29uZ05PREhtcVdhTzBPRGQzaURxbkczcjZiZzJkTjNvWTRkVnRQZVA5Qld6WTRXVjFTMm4waHlyVktZQTQwRlVwdUlidXlZS3BYbi9BSTNncGxuVTBITllDZmYxVllFa1cwOGQ0Zm1ueTExSmd6N3ZXS3BVRkZvMXVtUFRpRlVhMXo5anVkeDlvNFRHT3h3ZEMyWi9wQlZVTmUwYU8xMFF4blFLbWZOd1ZDVEVmcStDMmJINlcwNmNEUUkvQk9BYnJDT1JVYjk2cDMxWEFXQ01MWkdzZHJ2RXFwM2R0ZExDOU1rNHpvdHBwc0g5VWxWVFVybHpiYytQU2VTSEFhclphZmU5eENHR3JTQmhQcWRYSm5leTFWUitycEJ0bnRpOU5qRWVYbnlVNXBudmVTcGdnMUd0M0R6TUtqeHhCVkxkUHljQjYzTHVnbVNRaWZTUXEzeE1xaGh0ZWc3ZC9wZTFVeVR5YnZMUVJqcXp6VlcwMjhTcXpaRjRQWi9VL3dBTHEyeTVyMnpvWmFqRXV4YWZGVkhRN2wvVUIrS3JTUVJPUG5SSFhDWkhKcW92ZFQ0ZlZUUko2dDFyQnB2b3c0VWp1amlPUlZTYk1PK0MvbWFYL3NDWU96cWxtdXNJUzMvVHdiZVJuVk0vZHYxOGxzOGJtclFRcXJobUR2OEE1cXZ1OWc2NlJ3ZTZVT3NoaGNNWVRBQjFVbktxRVc3TzZJNXdnNTF6S1VBbjNxYndyTjZrTlc2NGhXM3ZHUTBLazhzY0pEK3F3ZUsyYXFXY2Q5RlV0bnBVclM3QkxpUlAySzFocGdGenpuVlZIVklyd0Y4bDFLZ0dyN3cxcXAybHQxNHBHWXh3Vm9GT2x1Tk9ZL3lxRHk5amh1a1JtVHBLbWtYTklJYmtQOGt5SE1jMGdmQlIrdnB6bnhsZnpMNCt4VUFISFlBWFA5VnhZNXArQ2RveE8wMmdvVHVqVWVKVWZxVHA1STczVXU0cHgzcUxocjRGVkpJMldnUWRjZ0g4MUZyT0pib3Y0QkdmVlV0NXJIalhtQ0ZUSmQxWXZOK3BWVjNWaWtEY2MrMlJQNEtpNGluUXllR3FkaDl0eEhDQXF6b3JWNnQxeGlZMFdvY0JqelJoOG03ZDhWdFQ3QSswNDFWWnJydjRmQVpXMFBNUkpsRWlQZWNqUGFEaWpKa20wSmd2c0RPOXc2U0p1L0ZlSDNwak1VeVJhN1JDRERqaDBJYU41cDExdEwzdVVkRk5yZFl0TXlvUTlnSjI4ZXJhQ2VRZEtjNE9xR0hHZFJPaU1EUldsektlSjhVK1hUY1dvM09jNGJ4UU9XNGxWVHVqQmlNSWlTWmN2YWNQaW41cHZ6aFZOV1krSi9OT3hlandUbmRuaFNiK1JRNzNpcXB6YzJ6MVRLYjkzV1V3TzZ3MmlWM2lNenc1ZERHdHRQQlljN0VxcXlMQmlGVUxDcXp5WXpMZFU4NE02TE81bkN0dUlSQU40VmRrNmQwcDdmREdGdGsydTl4VlliZGQzY3JhNUo5bHdXMnhBd0lLMnZOODNXbFZZbW5yQytVekoxN0ZiYzgvOWEyeHpyZUJZcWpZTTRnODFWcGYydVcyMHQweU95SytVV0gvQUtpdHFEdDZSdWxiZnh4cXRzYWZpdG9wL0FyL3hBQXBFQUVBQWdJQkF3TUVBZ01CQUFBQUFBQUJBQkVoTVVGUllYR0JrYUVRc2NId0lORXc0ZkZBLzlvQUNBRUJBQUUvTXY0Z0htWmh5ZTFHNWxVMG5sWUtXNHZxbUJzczJpSUtyalc0YStQVDBlai9BT1RBQUFnRnNUUmxuQklkcXlmaVM4OUFhb2UrbmpydXBvOE1zWkc1eGNlWlhpdXdESXdjKzNyRGRnaUtidXMrbjhnVi9BVksvd0FBRDBYM2ZUV0ljOVJGajBLeEdoWUZzR05zL1F6S2pKeEM2ZFA5RW9yNGRjdGVBODVwcVpEUTJaVk1MWmtjek1jRkxDSFQzL00zN3VJci9YK0llSCtOK1BXTUtsSDBXZzVITUFsazQxM05iS3Z1OHMxRlhVM1NYOExNbGtJMWVlQVJXZGdxdXVvblhlcm5RTG9pdTB6Z09DRGxIR215OE9tcFpxbjhzaDNKTFBHRFlkdlhFdXZDY24rQjlhQlg1aC9oc3h3V3puYmZxczNiQVZ5WnM4WWw3NkE0cGVYN3hLaXhteXJhSHg5cDRRWmVubVkrUGNPTytvbktISFFISzN3dkJkUU5mSjlSYjQvaGNxb0FRT2ROZFU2K3lZZVl1WUdDMk9ONWhndDdrTVhTUW1aTkIraHBsaXlybnBsOXB5cDRmbFAxeWx2YUEwK0prZk9yNksrakxmQnIrQU42M1JncTZGK2xwcVk5RllzcFhib21NK1ppU0hqWDlEN3plK2JVTmMvMUUyVlhLMzFmZzQralVpd0JrKzh1NStvUE5zZkVZSjhUbW1MZDFlbU9PMjZQa1hkVndnQnEyWDNQbDdScXdhVi8wSWhoWFYvbEFHZ1BCOUdXbVVkWjVUeStnWGo2enMvZ0c0dmhlTCtvK0RuS29aeTZwQnlOSTVBdiswMzhVb2JQajZRVURpWHdCbXRlZFE4NWFXeUFodnNOOTVrdWdWeWpWWFQ2VEF0eXlIUVBmVi9TWWtZV25xTnY0aXQyb09CdGFlMFNxMTF0dzVyUVFvSkxwM3R0K1pUcFBINkhFcFBvcVY5Qnhka0lLRDk2WERJL1BPemF5Ky9oaUkyMWY1eCtLN0ZoRTZrZ05qbHlTL2V5SUdydXV3MVY5TVFNOGlBZGlhWnQyNy9hdkRMb2V4WThpeVRHUHFpSVJNV3llbldYWGZodjBtV29uRTJGcndjbGFtSTRhcUVjNlo2TXpKam9HaDZkWUFOZW9kMHY3VERmMFJkQVB1aHpmb3FWOUZCYkJEMm9heWZ4N3pXTS93Q09kbjZFcUNRazB2UGUzMmlQWWVUbXBVQ0dHWWFMRzNMQld6Q2dHbTkxcWN6bFRLMXhZTWVJWkpqZ0M3aFRnRGZyS1FYV1c4TkdPQStBZXNYa3c0enZJeFhydWJEejVSU0tkbXVrelhMYTdkZzRQaUpaK2xvOE56V1h4OUtwVXFWQ013WjdKTDUxQThtUFZqZ3JOdlg1TjdJTmg1VW9DN2RZZWl4VHZmSGFWcndtbFRJOC93QlRQSmhZam1LUEpIU01jd0NJTXJSNm8zQ3NIWXhyYmFiaTlYVHIwbU42YnphWjZ3blgzTGQ0d0x0RTZ4ZXExck1yR3ZHRkdvVXpMK0RTTFpVMld5b1ROK0ZyUjRDdmVGenRUcG4zV2I4eGRaYmhxT285OTQ1bWFTS1dPV1BxVWFlRHFxMzRqcUNZdHJIQ05TK21wcFhheGtPN094bGRiTFQrZ0hlbVhVMzJ5OUQxbDV5eFZCY2o3MUtRSE9tRUtWYjdKV09uTm11OWJhckw1bDdlNmxoMTBjNytpZ3RTcGdtZ2pRV2FEYkE5cDVvNjdPY2RQZU1tV3ZBTFJkbzc4Ujg3VWRYVlU3cDc5WnF1UGdmdjFtYklqWXR3N1lpUWE2QmtzOXB5M29RR01zZm5INjBFNmpIMUtIZzA3RmhUaVZEZTJDUzR0TVRXNEgwUXBEa1F2M1pRZGpubkIxbVduVjk2ckFMa2lnV3hMSzdGYy9lQkpHSnNLbnZsU2lhT3BRMjIvU3ZiNU5YL0FLbC92WlN2Mk9ZMXFvQ3NZRVVOY0ZjVzRYcDlxZzM4YTJlcWwwMXFUVit6elh6TnpFTTFJaUtWcGEyVEE2aUFxczN4R295MzRrT3FCcGdkMThjd01IWEpVdEJta2RLT1RlbDZ1SHhPNlY3T3lBMnNxcmFtdm1VT29KYUlNM2NoVG43SEF2bWU4MkRYVWlQeXp5alh6TU1ZamJQdk9NN2hoNDd3Y0t6VlB6eXc3dEtoMHR6dnhGdkQ0bDhId29HazlkRlRaNFBnTDkzQWtWdzlxVmY1bENzb2FXY2kwL0hFYXdlVlVlVjMwd0t1c0RKOXhLSmpZLzdJNHF3MWJIWHJNS2w4ZWFwblpTNnpQU1ljZUlaWEwwTFZvSG1LTERNR0dFalhlOE9BWEs3WjZrMGxSS0FwdU5Mcml5b1A3bVhXZzNDeDFlWm5mb3U2UitzVFRmVlhwTUZZTTIzRWdNb0FMU3VCNWdzVmk2YnVWVlVuM1ZjeW5NNFcxWXYrdFFwRTZNWEgyWW44d3FWVjYxK2hLcUpDMXRkV0FXZVhYaU8rN0t1d1pVV09JQWdQVytlSVd4MWhQRWMrTXM5b2ZmMTNXcFlvYlBRKzF6dWNOTmRGUHRIaXhkc0RyenFPdU5MRDhTajI5UGNwM0FwcTA2SU52dkhxSVY5cnIzbUd0czRNSmliRkg5b2FGK3VCY2J6TWg0bFFlc1k0Z29yQUkwZkFxeEY4bnBMam81aUhvNnZlVy9NQ1dFWDNTTGVzdlhmWEg5UzRqU3grempQek5MRmV1RDhKUXNsd0RYWExoWkhZaDBqYk0rVGhPSW9zYmtHdnNHWTgydXFXeGU1OStycU1HeUZFbDF1UUh2eDlXMTNsMUhSSEdvdUtGVmxuZGNoNlFVMkJxQVpKak9yOUpabjNJdlBTNW5PSFY2eCsreDF3TjROdTg1bHBhNVZqMG1hZU1ZVUFQNWx0V2xvQTNWZGZpS3h0T05CazFjWWpnai9UdWhSS3dCeU4xVktqbFFXdGVIZDRpYkl4MTBWS1Vsc1MydnpFcDhsMzRqQ3dTMmRROFRHYzl5TXVjZlFmRTRWSUR5UjNuUnBoUzdNYkhkQytsR3JuaTQ3QzAzVVV0MVVHRGtZNlY0NER0TXRYWUVVc09rdXVsSHcxajZUWFJjdDFHSkJBQ3RsRlR4VVZOaDkxZGxwU3BTUHlOd1Q4cmhBaUxjanlmU0pxMUJiVXZSTEVTUmFVSERLcHN0YXRGdnhIUGhGQXQ0S0prK0JZN1RqR1hyRk9RTmdjWER6cllweXZPdUdXemxWM2VucGR4ZFE1eEt3OC9hVnNFbEs3aVBUaHE2YzVFc2JBakdyNkdJZGZJTGk1MzFndEVYejNMK3NpK1dFbllIcTF4TGtvRFZPclpHc3B2cnJDU3dFN0xpQWNJci82dnpLMWVGaTRJeDVRdGJYR0lOc2JkUU5uN1FLS1FrZFI3eEc4N3l3R25RUDd3VXZqRFdOR3ZpQ1YxZ3ZiRHcwQTZHc3lyREtDaTl5OFBtOENDOFpUS2lGVGQwUTFOVnJyTDJna1ZwMFl6ZVlLL1FDNmI5SWNBaXk0TTh6WWhTKy9EUHNBS3hlSVpxWUhYcUhTWEx1LzdtZlFlYk1GMGJlQm0vOEFNaFpNUmd1UVdCVFJPNVhjcmozVzF2akU2WmtUWFFRa0ZrWForSncybGRvZTBaN3RKdjhBTTA0K1dyMGxEcEpCZUdBWXd3VzdOelJWMmprbXFpdFVla0NUV3BpT21zejQ3eG11bHEyL2VVbTE2Wm1ES29FWnlKcmJjVFFEMTVWS0xYT01HUXpINlZMNFFzR3IyalVJWDdNenpucWYxanMvWThUL3hBQW9FQUVBQWdJQ0FnRUVBZ01CQVFBQUFBQUJFU0VBTVVGUllYR0JFSkdoc2NId0lPSHhNTkgvMmdBSUFRRUFBVDhoeHZqaDhNZU9MWHNPanZEQ3FQVVdvT2MyRjBYUVlCVllHSWNCbGROejNrY2xGcGQ0Wnh0bVlLQnlCdnNHOHQ5VitrNVQ5UHY5TThuazhuaDlZL1FaNnpMM2N2QjFqZE0yQlIybno0Q1pia1c0VEw0RkplVkh6YUpRcUt3UjQwbU10N0g2QVFKR21WdVBERVNocGRNUUNscXI2ZGRZL1Ruai93Q2ZZTDR2TmZrKy93RHBqamFzTGp1enlTU0pGM2t4MVFMRndMb3VDQWpIWkJqSlZKb205S0RaUWU0ZDZEMHh4eUFzbkkrK2FveUE4WUVMcmVkdTJteUpGS0hpdUpueG5SU0l1R1lSSFpuTWpOTWNldVF4SHJJWkhJNUQvQStuMGgvb0huRE1xTmZUV3ZpRVltRHlRZm5LRk90MWlOcVJvNmllY0pPbGE3QktScmhTRTd5VEZ2T3EwUytPRXlXOENYNTV5eUF6MWgyS3loSHBEd0VNTE5DV1VveHoxYVJhaGJsUVpGWVFSZ1laY1VRRlN4QVFwelhPYmZxaGFYcE5tVCtwVGY4QWdmQmtFWjY4MFBZc2ZkK29SMG94YkgrRHg5TU1wZllxR2kvSnYxbWlmc2hNSG5jWHprU0lsZ25NRTNMam9lWVlKL3c1NWxDWGFpRXNiQ3loZzB1V1k3T3lwQUsza2pEbkNBYzRHb3ZJNXBQOG0wbEhCUFpURno4R251aVJuSTVEV1VSZFJrU0d4TUV5VmhNOW1hSDZDR2s3YzkwaHorMHMvTHNCL1hySHJIei9BQUdPTHdFTC92Sis3bGlQcHRoWi93QWg3dytzdUhUU052R0NMWW5IQnpiQVdpWll0Nm5rV0ZSMHBWT1RhSTVuZ3RvTnF6VHpIN21JeUJIbU1PSG4rQStieGlVWUdLUW9JSFkwbXh5UUdhR0NaU0U2MzF4a0ZZbmZBTG0wZFRoMk13akN0RXZKRkRNR2JyeUZYQUc0bHA4SXlMcTB2eHFnTnFoNXlkdTB5L0p5UEk2aXlseGcyajVuUEQrK01Objc0ZFpldm8vYkhSZ1NoSCtGV0dUYXFVSWJuaURkNXpodm8vUC9BSjRGYzVNUnEzNUFTQmt6eDd6dHdCT3lKOXh6a3A4VEhCL2hwZm5hZmNlMlc1ek5UNU1DZThIRFM0TlBTTTlZTTdPbUJ5aTVHT1RjNUQxbVp4cDBTVlJiMkNjMVBmWk4yV2RDclBsazlJRldFWE1GaXBOWjVoY2VnMTNTYTd5Q25RdU5zMk50aXg2U1lkUDdZRGpBK1RCQ0FsZFlQYzh2ZWVtUitzUUdrSE9tdk15eVlXMU1tM1NOZlY1b0V3clhZOUpCQTVnT1FJaHhQVEZodlVaSGI4V25ON3VwYnlXd1VJTmFZVEN4UExNWUM0djRud2Z2ajR6dXcxTjI1Uy9LeTljWjNzZVBoajVES1IyVEMwTEpNSDBoSGtLclk2ZzhNbStocS9VVDEycTJ6bDN3VzdYdmxwQnhZWk81MEl1WHJoRS90WTRKZ2htM0dHd20vR1I2ZmVNbUY3T2NQb3orbU5lMS9HSDBmVDZCTTRERHQ1OGsySGhTQTlnM2hyMWVIaVlQS2xlNWVzajdvVmJxVWFKY2NFWXB4eWttVkxJa3Yyd1FmSU44ZE03TWxlRGh4MTZZYS9xY2dmOEFUSzZ5Vzk0SmU1TU52c3UvRkh3T05CbGRpR2poNGZPSERYLzJ5V2hmV1J0cXpiTzQydkkrT1lNenk3TWFVVzV5ZlFNb2FxQldrMFZNT1dWZUJzV0FGeVdLd3JNV2RWbmJoTmF2ZForSm1NMWpJNUQ2TDVNSGZCbW5adzRqWEZzcElDUEZycUluakxZbTJqcGJrK0MvYktpWTVuNVlocDVYakZNTGhrZzU4dVI0VFAyRzdjUGo1ZThqcTUvdDROeTBEcWN6RmxCK01qdzRtUGxBeG16UklDVlRzWXdDRURBMUkxck43TGZid2Z2ajFoU3dpOEZxQWk5aVh2QTdHUFpaRTJvbkZ4Qmp0azlIVEhTQnJQaWpPSWFPTTJEQWFaR1I5SCtrRXo1Nkw5UEtZTFlpTDVJT1NXeTY5YzEyZ0ZEbFNPaDFPU0xtSWdFMHBHa0pTTG1SRjdzRFBFaDFDZlJBeU9HUW1rY1BHQk05MkNpRlpDT2ZXRGN2ZmYwZktSa2RnWTVYcFBQakoyQnl2U04wWnN0Y1c3RS9CamxYUElyQUp5bDJPTmxTWVFmanFZbzZoeTNoR2hVb01FajdNaXBuRjZxRW5jQ0JvYXpTM2xxVmlZbUVjc2pOWG1mMEV1VVZHSjR3U2pBR1Q4eUVEU1NZMjRZYThXRGsrQ05EbzNqY3dVbzVhVXNkS05PVjQ5YzJSUGgxUENzNVhzTXhpcVQyZnRrVkEwekpCVFY5NUEwNXE4aHRIZjhBenhOczk1RmYxUVZ3aGZacXBpYjhvWmYwQjIxZzVjTVNndURydHNORUxjbG5FK01sSkJnMjRjNkFReVRIdkF0Y0NkUTJOMDdaeWVWa0s2TnVYdVJteUpMeWpCTFFtbjdZRzJ5Zjl1RDdCdkgwVGk3dExhcndBVmY1Y0RKd1pUWjQyWG1mb3lEbnJPditONFZkb3hlWGpjNzdpZWFHMDdTRXdtWHc2aUVyRk9ZamtoUW9URlNRa2s1eVBpRkdPVmpMSXlsSWtkTEtDN2hTQ3A3dDV5UFdqanI0NzNZWTRTVGxic3djSmpnSDVjSFVBa1BneDVLcER2d0JJQzNIZXNseDNHb3pJUzVFN2RZMlliSENJSFNFSU1OaitXdFJ3Zmw4bVNUeStjSVQ5ODF5cVg4N0NOZEdRU0luMVRwNGhuangyck9RcXJSQWZkdEZ3V1R6c3ZpTlBHRDRpSi9maWUzNG5KNUhhQ0dlSXZvb1BPVGZJSWJJcTAweldmQ2lpZEt6WDd4VWNvQTZPYmZHdkRMN1ppRVQ4aVJNa0dIQTJBTm9iWlN1OHJOWWlnMGdBdG9zTDdYSUt0OFptWW1LR2gyUW1LTTVITlh5ZU9jSmk3bGFXT0pZaGZ6NDBta2wxY1Rab0RLMTBiY2dlckpNWHFHOFljOFhJaWlybE5VWkx4akFXUmVOWEdHcUNwZXlvdzY5cXA0SDk1TnFJRkxBa3cwNHVYQ0NVdWdPc2tCNGJIY0V4Qk1ZTlJ5YmdTOVhwOFlFN2REd1RmcTNvY3JBRHNZb25Oem45TWpLcUh4ZUx5b0xLdnl6bFBUOXByZVhKOFI1VlhXS0hQTEk1ZGQ4Vyt5WnhVeGVoTWdEVlhyblduckJ1aU9qdVBtZWl6aW91WWI1Uzc0d1lHSENNbEIwVGdFTzJMSUo1cThKcHZTcGs2TlA2TWFZZ3dBUTRheWRnRm85ODQvOXlwcUU2bmdnTEtlS2dHRE1XaTJXSUlCQVFwWVlHVlpVMndrZGtzT2o4Z2pGRU1ONUp1d2xUZzJ0KzhsNUhkaFk1U0FlTWJMQWloU1Z1TUZTcEhrRk9qcS9mRFA4T1lYVXpnV24ydUFMKzNHV0JJZkdXSFRYN1lNdkk1cysrdXhVaXordVFNSWNHWUJCdG9SeVZ1S2trUlJuYjhBdno2eTY5M3dnaXJyRFRjdXNoQjZOUnNZZk9QNmY0US9PUnhlMk12Qm82WmJ6TXJFNVVlRVpFR2lMRUhId2ljM1Jyc2NLRklERkRiV2gxSjZ5OXZlSWpPNFo1Wkd4WktuWUhTZU1xY3dKeWxDTHl4N0lDRnJaWnhtbUxpWW9WZFB4a3JVUnJDUU9vSWF3NUtxdVFnTXcwczZ5dDErS2lBS1JwZ3NlQkJJME52T05ocFJ3ZUdVQkFidWVWWHJnQkJjdW1sZUZONVJqYkNTRlVWTFBSbEFsdDVBSlpUS2FXQXVhZVhHbjVJSElKUllhRnNrNVh5M0huTFZraWpNUFl5eFRHWEVpNmdVUjArVFM5NTNUMjNCR1l1bkRXTmhyRUlmVGhGcW1hb0d5dUpIN1lXWWpzblUxSnlqdThrSDV6Uis1ZmpGVEpUZTRCazErTUFSVndDNUE2bkZnQWdHQlQ0dk9TRVU1VXJ6dUl5WXJnYnR0aW1RcGdJUmt2ZjhBK1pIV2ppa0FJZlRPZlBvZ1VuNzRuV2wyRTdQZU1MRll4Q0VkUEtUODUzUDMvdkZLZGxZTUI2aUtmb1FCVjBZNDRtNkphM0gyTW1rbXpKQStLa3hZQ1lic3ZueCtzdXNta0NwNFVZRXhTZUtnY1BjOG1TdlZrd3pOZXpmR0dCWTI0UmlvcThWckJ2Z3pudXdDRmlNQ1pPLzFrcmJ5SmkrSmw3T01JRjdLSjJRSGtjNERZcFowN3dySEtHYTBZK2F3VXY4QWdSVDliQ0pTcTFlV24xaXVVYVlGRnRQS3luRzJPUUc5b0o5MlErRVN0REVZQlptMEJ6RWsxalRvSVQrVjQxaEhKSzhpTkIvT0lCODZLdGJyeVpzeUpMMWxRVzNLV3hkbTJ0dHRZdEIxaTh4OVFQanJDMVFnN0VMZkhmR011ZTJNRkYwc0NZOVpmYmRhWk9PanZGS2dGak1TdmRQNnpRVEZ5d056NVFQM3dUM1lHcFIweCs3a0pOaGpSSVI0SXdVTDc2Q0hzbkc1VU9ibzdJajFnMXd6QXMzWDN4R3lnQ3VmbmY4QXZJMGw4Ym92KzhlWnVWek9lK1Q1dzQyK2x5bzRBNUNWak1JSjl0T01TM2x1Z2lHNzNxc2pCZEFKbmJoMFg4NWU2a2dUMkNMYXdqcWJLMEx3TlBuS055NE9PVW1CV29POWF5R0N5Q21xUm92OE9FSlNFek0rZkdDbFVOamZaazFKQ0lSM09ZTW0xb290ajRFUE9YQm80MEhKcVRXc1NvUzhJTi9ZQms4U3NVZGJGSjNoTUtpNHVKNWJ5VmxnSG9yQjR3NEFpSHRtS0NjUFh5bmpGT0dJNmt0TE5oUzZ4d1podXhXUGt1Um5Yb1lISGVRUmtVMktmTGpHdS9rNnJiZVczRTdnL2JraEpwOElmNWd3b1p4RFJBbE9NamprU2Yzbk5qeDZRSTU4WkEyNENxcGl2Ykt6L0RlbjVZMzNJSUczZ3gvS1ZycGNEL016Zi8vRUFDY1FBUUVBQWdNQUFnSUNBZ01CQVFBQUFBRVJBQ0V4UVZGaGNZR1JFTEdod1NEUjRmRHgvOW9BQ0FFQkFBRS9FSlJobGZ3NjJZcFdNd0FsL3dEZGY5UG5FRWlIbXBVMVN1aHcxYm9YZkpWZG5LQnl3ay9LUFBNd1lwYXlQdjBvRGJCQUZJd3ZKakRHTG5Cd0lwamtCNnlCeEV4V0ZCcC80cXFUYmp5eFE0R0ptTkVBMWNBRmFVZVZuNGYydUFDODE0ZDR0MjBqdkpwdGdVNlJ6RlF1WU1TNS9zQmFJcWs3UjFCb0dBZU43b25hU0ZUSU1NcDNOdmtDbWlUT0NZc1V5RXgxWmpLd3haRU1xTHJHZk1reWYrRVM5R2FqaThWMk0yQk1oT1hrMEgrdjdmV1F4dTBkdXp5cVN5QzBwb00vMnMwaGFLMFNoZ0x5MUhpV0pGcWNxODVpdEFIR3JuL1BoQnNNaU9qeUpCTUJ0Q1VPSUdRZE9BTUEzQkZFZ3l2cThDSjNRMnBObEJaanpEaUJZbUo3d2t4R1gzR2ZFZnl2a1pPVDhmckZtVDR4SU9hVGRRdlh2OFA3VElKamdlY0tNbkQ3RzdCUm0xV2FEaW85cFdCTXNEVnBHcU1DUjN0RWNCQ2R5R2d3N21adHR1aW5XWndiQ1V0MzlhVU5wTFhnWkhVQTRyQzJHZ1lsajY2cm1haHZZQ0dLc2VOQ3BJU0pnc3VKNFp2UG9PSjltT1M2NnhlT3lQNFVHRjVza1k4NStVdjhWeVpnbUwvVXc0NGN2RmhYUENIUjZkRCtId1lwa1M1dWV0M0dPM0RjNkd2OGQxYWk3QlZSY3Q4Q0hxTlRMVVZyaUdCWFNBbndjVlRQclFOREVrZDIxMFJXdEtrVVEwSmhvSmNKMFR5cjVtNVNwcFVnVU5HTXpZQ3BvR2xPZHJWcDVYSTBOSzdXRUdsS3BWRkRYQTRKblFFZjBUVGtyd1pUMHVkSiszR3M4eC93RVdOaDVmN2pRNFJZL0QvOTErOEd2dkRUOEFmODVNUmQydDkyT0h3SjFqcmdjRnJMendQUDBPdm11QXVpNG1ZTXNNRXROYTQ0d0ZTQ3BzTEhPVFpBbkRBRWEwc3NSUEVUdmJSVWh6SFUzRVZ5QXBFTnVoSnlFUFRUWGx3RUlFcHFzNjdWYmdiYlBjWEszRzNGa1lRUzQ4eERvWTdKRFMzS0hIMzFWMURBTGx0OExqTGVxcmV5MlpEZGd3UTgvWHBWeE5MSjR4aHlxaDdvMUloMGdWQmMvd0QxT2EyVHpud0IrakVoUWhoNzVJVVI4UjJaUG45UEJvL1Q1WW8vU3Y4QVg4SFc0b0pmbyszQjljYUFNN1l1UDhOb0lUam9DcmFCeTZIQ1cvcGRtc2oxS01ZVW9HdldhbGpkOTZyVUZBNE90RWlrQkk3YTMrQUJoeWlqSjBoRS9aaUkzQXdmQWNJT1dwb2ZYY21IazZCWDBSSEZINGp6Y3VoQnh1RG1RSWlmYkVRN1FZWUlZUVMvWVo0cWd5SExhQmhiTVRXZ0p6WjZhSk9TZzRLNGdnajhBWlhMZWtES1A2clJhZ0xNZTVKUXhRRjU2d3RRQVl1OEwrRUo5RkR2K2poamh3RkV4dG8wSHIvMGQ1SURSckVWa1ZjTWxqbTN1WG11WVNRNzZXSHNNWTlDMTJxaWxudUtYWXhRdkF0MGphMjBSY0pndW0rV2g4M2Vlek4rK1N6eFZud21Nd0UyWUdPZWRBY3E2TVljaC84QUwwVkpjMWhnOUg0bmJ0eFVGdVBHRHFUWEFoc3BmOUtvWTQvb0l0NFQ4SXpucXpna0lCSk9UcEV3d3Fmb3V3YjA1ZVVqY0xtaE1obVlOcjRGMGN1ZWdCaVdHazE0U1hObTM4d2hSZW1HalZLNEh3SXNGamp5amVUUzdja1ZFZlRIZzJTaFUvQzVSVmgvOEpjbkdQbHNIaC9DVGxvbjhDdnVwY0lXb3ZRdjY1WERsaGk4L2VBTkFXaEc3Q1lNVkR1aERBQllvRkxVVC9jU2lxZHFVdVU0SllGdG9DZkVtdXNXVzZhdWsyVU5hNURya084NE5ESjA1N3dsazhUSWdqOGVGY01xZW9xWXVldjczbStyUVpQNTE0TWtLZXIzVFdDYzlLbWVsdjNNNGxpdWV4eE9KaVhjZXh4M3FZeTNxVTljV21vd3lWbmVuNkl2SytZRTFrWXNrZmEzVlBveWo5eGFKT3pYUUc5WUFFOGgvcmpEa0xoa0ZuMHdkWWczaHI1YzMrSHhRLzEvYjhjWS9iQWlCakZ3VG5SRFl3NVlhN2Myd3ZLa1NoZ2w1UXViWmU2THdUbEJERUJzSVkwQUl0UUJabXdGWDZQNDh4M3pPVnQrem1sOC93RDRIM2cyWTUvME54NEVJc0ZqOE1uVHArTVNHM3AxNzRZZmRmcHlVcnZnMmptWmY5WVFzNUxMZ2xHaVV6aXhMN3d2b0UzT1lkSElNNDRhVHZoR0RxcTd4T0p2WWpxRFRJeCsyVE1KQ3JxSHdCZE1mRFNheUVoNXlLZ254aEZ4T1RoaTZWTGJOZDdlTUY3dHJ4SDFpNU1zK05sY05keWdQT1pERmNibGVUa3JVVXJiSEl1bkZVL2FFRTJvVkdSdFdoOEFjUWdDVVpwY2hYSUh3NXdyQ213STJnb1dGelcrdldXb1FoeXlua0xubS9samdBTzNJa2RCR1Q1YWhBb3JrazJiRXUwVGpDNjQ1elV3NGhpajBlN3pMQ0tvb0E2UUs4L1RYTFFTYm1nNUxFbjJ5Rk96NlFBZ0NnbDAza3ZwSnZnYWdyT0lNdDJMb1hWQzc4RTJPcTRXTnhjQUhiazBCVDlmK2pQKzJjKzhaUkVFRnlxNkQ1Y2VicHVmb3lHNkw0aHlmalJCUFNUb2toQUxNbG9MUVRmVnBSVkxMYlJ0eklFeHNQT2xZd21ESWRzbTJ1c2hTUXJBR0prTklsSHRhUVdEQ3U1dDRQd3VYanZuQXRzc2ZsblY3MmlLSFNpYXhPS2puUUF6OXpLaWpvd0hjQVFLN1dHVFpQcnhMQzZaQkpxd041ZHpKWjVNOU5aVlFLWU5qc3k0RUJ2RmJobEtRTytrYWtvMG1lMFpiNEZpbFVpa0VyYWlWc1J2eEdRNUxGQ2lBekhZRmtGOVA0Y1lNUjRVUG9kWmNoS0paa3I4WlpoWVFFQkxhWDAxaXFnNmY0WUhkQnNwdmlraHJwd3Y5blZ5UGVkUUpIU3dtVjhrRlFsVlVOb1JDVWcxRU42NXFNT2hpYXh4NU5hT3pubWJPaGdnaFJ4UTBqYmcxTGpPR2pkZEpjMENpb1h6Zm5SbXI3MGtpdEI1Y0RVdjA1NjdnWXRXaHFwdkJ3MThaVzJMcm9MREU5VmNpZUJCSGtDRmhNT3VNODhBQVJWc1M0UUhXV2dvQTZ4SlBpd1d2UkNXNEpLNmFzcVhHRHMrNlNrbHVCRjJZU0ZXOFpnZEFvVEY1bVkvSFhuK2NoUGwwbTVvbDdFemFNeDRKVVlEVjIwOHMxam5wd3MybFdyZ0pnTFR5YlZvNm9DZytSZ0FjTTFlTkVRV2dCUGNXcGdLTlJFQVZOUFlnNDNYQUJEQTBBMURiWWNEOGFIZUxWdHg4R1FFQXFLOWhlOGlJQTFTMHFzYy93QStnT1JTcU9tNFQrTGQ3TlpGbTZkTk1UT01PVkswR3d3WDlwRE5SZmllWlZaamJESy9oUW1uM0dXRjdUVE0yVHJHZEtnQ3B4eWo4YXhnUjhtb01GUTBTS3JGM2d1T0tmQ3BGVFhiRlFnUk56ZFRGOEF3NENMYThZL0dEUFVxenFRQXdlbWNaMFM4bWtSRkx4Z0I0TkRPOEtFV0tRdU0yVmVFOVIyOUhpU0dDWFVkMmZRSWZveVVuaVAzd1FUZEdIcW1SZG41Y21oOFk5ZlRCRkhUZkVwWDk0M3RKQUhnQ0gyN3cyN0Z1V092N01RTUNSMFRiWlAyYnhXMmtLWmhsb2QySHQ0NjV6bnFaRERvV3hFd2xjUDBoM2RVb3JRSXpLK0tiY1dGSHJUamlacXg1c0w0a0xGME9QVTJ5TFdJQjBweGlRSEdCa05iTkZIN2NMZnFRZnUwNDBIL0FOOE9yVExJaDB4ZWd1U09iVGFQZC9HQmtSUGVQQzJQQU1YZ0JXRUJVbHRSZ0N4d05qMVpTcGVEc2hpcHVVZHZvZGozY3hXbXRUVk52bEFIYkVCZHVVRTIwRVVwcGdCUXM0VWdmRFJVWmllZG1nVXA2ZE0xQ3NYN2pOaFd0UldZais1RzM1eE1CMFMrbnZ2QzF6dTZnNk00dEFMdk1kL3ZFRUUxMU9HbU54eFZUS1lDbk92cGdFZGZ5V0lXbFJKNGxoSXZla3FaVzd1b1ZJR0RDbElOTmtDbFZzZHJGZ3hHazBlMlJGRUNoTGpvU0pUL0FIUkpDL0Z4N1M2VFhFN1VpSnVzb1BnaXBkUG9vWUVkZlpiS2FpamZTWTNua3lTaUxwUjY2d3JUcG5Xb0VhazdLeG90Nit4QVJMYWxHUzRQSEJ6RlFGb3RZbGZDVENsQTZBOG1GQkpjU3BjU013cUxpNjQ3QXFLZEtUSFNNZXA1c1lCRmJSVXc3VjhBTGdBVmh5VXh4WkJZYWFDazFoVmNzY0JhWWFPdFkzWVFLTlFwY2xmM2xPQ2M3UFpLREJrSEpMVkd0bXVEdEtLWTNtb3JLdEZSWGdtRXF1YmtBdkt0YkIxbWNldkxTTEJiRkM0OXE4dWhTVlZEN2RJbzFPSElSckVxa0ZqTGpLSnl4Z1BaU1NVb3ozcmc0cWhZQXVweURVMWpGM3U4SVdRRzhiQ1lXY3RUTkFKeWdYbmJML3duaHFvQ0NtdFpnczJZaXpJMGltOGNBVzFNbFh2ek1kUk1kSWV2dUdFSkRXQTJqVFN4M2ptMzFSQ0lpRWpIMkFieDgzV2dmUmdmV1NodEdpOTlZMjI1MDVTNWdoSFNPSGxMU1hucEpRc21Hc3RuWGVyYVhnYTEwWTh5L082YVVVZ2xjUkFwQ1ZVTXQzM3dEK1hHcEVpSmtzRjRYQzBUZE1ycnBlb01haEZqT0tVU0hVTE43d3NsaTNiZjFuUXZHT1NrSzdXRStxbk1XUVpFS1d1UWVzMlpKcU1ubHdnUlhQSk1wTzZUbW9BS2l6bzFtOFR5Z1h6Q0UxWWhsQzl3czhsV0NUeHBrTmFlUVFJMHJ4b3psbUgwL3dDZ1FJQmVLbTRZU1dBK3phOHByNG1CcE1ob0IyaTEvd0FaNS9xSmNBMmc1MGlPT1E0YUt2Q1AyeGxvUEpDYlBCQW5PSjdSSWg2MFdsSWdtTzR6RXg1a3JWS0hzeHNPTWwxQU5DMXd0OTd6V214WURyaXhJSVFhZklyNnBpdFQzOWdSSXFzR2tqY0ovZ2dKaE5DaStWeXJqQ1JZZk1IdmNOYkRXcUNKdTRESXZnNkNTb3NSc2N0ZXIxSUNJUzNOVHgrVEUwaVJTUG1RazNBMUZWMENKWmVxT1BCVmlZekpPU25JRG5sY0hoeGhBcFpPb0k2NXdzbDR3am5WMCt0SVc4Q1VJS3pKdkRENk1JWlRNU1RDYVlHZHk0N2c3ZTFSUVFGVlRpMG1Gc2dacmlON21NVXpPM3ZKVjdSUFFZODB1d2lDSGZIQzh6ZVA4aUlpT0JhR2k4NEpEWFhJYU1tdHhEdHdnL0EwMG1oRm5NTGhGV2g5WlVOc05LMHd0OGhCMVlYUmJUbDRUQ3E4V3NCc0JCYTFmY2xDL3dBQUNJdHllbHAzajZTSzhDcG5yajVqNnJOR0dONi9nTHRvUkhteDRIV0d6WjNVQjNVZmppL0Evd0FwUzBRS2VkVEhobklnalJRS0JjYmRwYlQwMVEwME1pd2xUOFlobzZOK21TSURkRWhDcWNCVXdzK2FxWW1BV0ZmZ3dTZS83bzJmNUhJcVZGSUVOTjNPeUxBeUo3RzROMkFGQkxnVGJYQzZpZVRkaFRDYjVyT0NCNGZwd3Y4QWQyQThnN2xkN3lJK3RTQVh2RWNvT3NBSTVOcnJEY2NaZVZFS3Z1RWRjcW91K2dUTGdzSmpnckNPZzRhWHB2YjVOS0RRekxURE1ZWElOeWZPeUU1SjdqNlNBaHFScndzT0J1UGdvNnhNSjNTc3hhZktEaHpjSlVqVjMyeFc1V0MrYkgvL3hBQWxFUUFDQWdJQ0FnSURBUUVCQUFBQUFBQUJBZ0FEQkJJRkVSTWhCaFFpSXpFUUZUTC8yZ0FJQVFJQkFRZ0FER2J6dWJFUldpTk12TnBvcVlOeU9OVTlpWkZ1RGJtMFl2bXljTEl4cjZONkF4RVZ6QTVnY3dPWnZBODNnZWJHYkdiR2JHYkdkbUJwdVlIZ2N4WGhmcitjbmpWWklRazF2a1p4dW1IbnJtdDRiT082dzNhcXRNMUZPdGdlQjRMSUhtNWdlYm1ibWJtYm1ibWJtYm1ibUJ4TnB1SUhnZnJ1QitwbVl0ZDdDdzJPVXZIazB4c3JFMkNuS3hjYjlkV1RYbmpvVVgzQUF5ck5wZnNRUEE4Rmdua0VEZ2p1YlRlYlRhYnphYlFQTjRIaXRONXYxTFNISFJlenU0ZDExSlZUdkVObExreTZoTDdnUzc1ZUErem9LOHJLUzY0WkZ0VmdWa3VWdjV0Tm9INmdzbmtFOGdnZWJ6eWdmM3p6YnFiUldNM204M2wxaEE3R2R6ZEZMSXFZT1Y1TVFCNm5YWG9mV1M4OWpMWnExREY4WVp6TGUxdHQ5S2p5TGNsb0Z0WXlTRzZaWEJIWTJnY0NDd1R5R0JubXhJaTlkemFiVGFiYWlCNEdtOGU2c3ZwTXJoOEt6TCt3ejIyVTRKYXZnY2pNc3IzeVRjQ09sRFN5dGJFNmppMVFJNkpaYUNvdmFnRmJGeXJia1Y2UmI3NklQVURBenNRT0JONXRDNW14Z2FibUJwdE41K3ZiczNDNEh0RkpYSER4UU92UWJvUmJKZm5KUVZEVjMxMnJzaTBwWWU0VDR0dFNodFhlb1pWbExFMnBhZEFVRm9tOFJ3QVFQSVo1SnZBODNNMk0zTURUZUViRHNNZnc5WXhKcVV0dDFCWk56QjdVN2NaU2pvN0E1ZVpXb0QxWmxOM1lWbERwN0lZUHRBalZqdERkMk9zaGJSMFlscWtldHpCWk5qTnB0TnB0QS9VRGR4SE5mdVdQK25zMUVDc0E3SHViUU9SQTVDbWNYK0RXQ0s0TXRycHVYcHRzaWhla1RrS0VBMlovS2UyZW44RE8vR1NVUDU5RnhhM1UyRTNFMm0wMm0wMmdjQ0FoZ0FYZHhYTyt3QUMvY0RlNEdIVUxmZ1p4YmRXVzlCdW9MR01DSnZ0T3dSMGIzT0ltMWE1VmdxTzZPbHY1S1BZOXFEcjZPd00zTTdNMm5ZZ2IxTzUyWVc2VUNNU2FkSXI2TDBFTEJCM3RObzFuU0djWmt2OEFhZElINm01bTgyUFV6anRWMkZidjFQbzlydE0vTFBIS0dlcTVlZ3JITEpzWVFaREhzRC9PNXRPNFc2N0UyQTlGdFBxZWd3UDhCNlhxRjFWZTV0TFBhbVlYYVpKQjdtMERUeUgrVE9wdFRHWmpqMTJkZGl5eXdvZGNSSHpzUGU3aThQRHRONHR5YTZhRy9TY2l0UDcvQUErLzZKMGY4WHFNMnJqcXdiZDJEdnZINmx2WklCM0dnbTNTR0IreEFTUjdySUdiT3hGVm4vZ2VzSDB0dFpIVFo3ajZoUmVIcUJ3MU56M2NYVTVLOFd0NW9LcmgwWk5PWGNDQmtQYnFjM0RUeGhnZW9OU2ZiQ2tIOFNCMlJCVXhYYVdrcmtJb1p5RjFnYnZGaWZyVUZpeHNUc0F2MUVjOWRIeUFlb3ZySzJsTk4xOW1pVmNWbDk2Sy9HcFRuaGJHK2xRbjZzL0R5cWEvenhLV2ZFQkwvWG9BQndDYkJaV2NUSXZwNU1zbUhmazN0cTN5UERPUHhoWUgvUDVEL3dDcDIwUGYya0piVmw3Q01SUkFOeDZxQjhqUWJyL2FjZkp0UDRyUTV5V1E0NjBmZEhsVGpxclVIanhhZnB1VkhLVjA0bkpFRC90dlN1dE9iekZtUmlzaHduZDBBT2ZqbzJPQ2VHRlE1SElwVjY3YS9raFJlVUhNOGNmeTVqQnBxNGt4cWlCQlUwRlhmOThYNW1Dc3p4OTN4aW9oWFpBSTFaQW1DeHRzNmxpZEFHWWxqMW51TS9seVc3WUR6ZHdHZDltWHJZWGxHTWowczdaSndLOFIxVEd2eHF1T2VzMzUxdVFHQzBaZVRTL21ydnpzcTdKODlsMmRuWks5T044bCttUEZxWDFqY1p5VnQzakNjZGxQYWE1WHhyMXU1Ykp4N3FRZXU3VjVBclpaeHRkeXZaSG9ZV29sZHlPS1Q1ZVBRRzhCT1N3N2FzYmNZK0NYVitrd3NwdVRkSWZZWXlqQXNzcVZwWHg3bDNVWjFUWTE0VXR4MXYxMmNKaEczQThvdzhVM1k3OUh6TFlWZW1zMlhxZ3pPTnlNUElSYksrR3pNdmlQczE4ZjhTZk93MHZOVmhwemZ0MTUzTDRuSDhzVnZOR2JiekZsVkFwM3ljU3c1MkxqVjRWcjNOYlZmekZsbFYyTUtiN1JWZGJocnlkSG41M2lNTVZXR3Y0L1Vwem4zK1VWNFM4WW9yNFI4TjdNeUpsMkg1SittdW8rZHdPTnhMV3djYnJoS3JINVBQV2ZLY2MxNTZ5bkZ4OHpqdzgrUDRXTmsvR2k5bngxS0RoM213MTBYNHBkY1RvWlNrL0lNZDZMNmlmanBzZmlNaWtmSHNqS2Jpa0M1ZVMrRm5Vc2ZrMWRmTGNzOTlTWlBJNCtWZDl2QXVSOGpHYWMxeWxWL0gzMXRXRng3ZWwvN1BGS1dwRi9KVTh4VjBGNUt0L2p6NHpjZGswSllkdVIrWFlmTDhVS01iaE9mK3FML3M0UEx0UmwvWnRmSld1ODJMaC9LcUsweHlNRDVFdUh5T1E3YzVrdmwyaDJ3TTJuajlyWncveUd2anNGNkRpOGtNS3BxeUh4eUdLMHNVczJHYnoyVnlyRHo0L05VMGgvRFQ4aDVYQnh4WGlQbTVMRXNEbFpqdDIvMnNsU1NxWDNqc3krKys0ZFE1TlZyYUtNZXBTR2dmRXhzWmxYeW90UklxMHh3R3Nwdng4d2xLZWtyWnA1UUIwTHM1QTZJRWVvTDBQS0FmVm1UYmF3TEJuNzJieWpxQ3hlK3o1bGIzQlowWjVFUHFCNmgrSVV6Ly9FQURNUkFBRURBd0lEQmdRR0F3RUFBQUFBQUFFQUFoRWhNVUVTVVFOaGNSQWdJb0d4d1RDUm9kRVRNa0JDVXZBRWN1R0MvOW9BQ0FFQ0FRay9BTzg0QXFoWlUxTXh5TytVTlhDZ0duNWdMQWtDL3FpQzNsK21IaUJGZnFmb0VaWkVnR3hpOU5pcERPSCsyMnFwZ25lQWhBSkpGdk1HcThKcGZuK2xjUkhPaDZoRUNCVGE5dVFUUkZlUjZEN3Avd0NKRmdUNGdSTVJ1SXdjRkdwRUVSQkhraUx3UnpIMzkwWUlwQi9SSHN3SSt0Q2hwa1ZUWkJBcVBzbTNvRE1HazV1bTYybUxEeFVtK0NvSm0zOFRpZVpWajgwZmduNEJzZ1MvTmFRYkNOd3JFVTZIc2tIY2JxWGFZdFBvaVd3UkVYNm4xakNCZU1FWG5tUHNzWCt4L1FtdEV6eFJFeW15NW9NRG1FQ0ozdXJkaGpvcE1FS2hnekg5cjVvVEVlSWU0UmdXbmZvRlQ0MTBSMEtCdDNBWWlaRlk2NVRwSEpZeXFqcFZTdzd4YzlQZER6Rm8zSXdxejJINGR3cnJQY3FVU0hhalVVLzRVQTREYWg4eFpHb3hZL0picTBRanBkdGd6V29SanBaV1IrSFN2ZU9UN2RnVHRRbXh2ODBDSGM3ZjlWU3ExeWpITEVxaEg5dWo4RTg1Vy9lM1BiZnNNU1FJeFVwdm1GWDRmSkZHVWU0S0d2ZHdSNmhXUUlPNFFrRXhTaHlqRkFlZFUwbUlRK0FjOTdidXRJQSs2cXF1RmdLUjF3blNBZVZ5dUlLT3BLZlBJb1FlOXVQVkdpdFB0MkhsM051MHlVMjZlU0p5cDArcVlTZXFpNW9ydGk0bkNFbjZJaWhySFE5dGUweUQyREtxWVI3QjJpcWMzNWdwNGMzVUFULzVKOW9UQ1hXbkNIaFRvRlFCV3NjN0JWSnFtaytJMkZrQ1pCQm10c25tdUM0bmt6M1hEREhFaUovTWE5MHJBS3VyejJiRDFRVFpCVklFcW9oSFNZdzZ5NDFlZ25ybFAvRWFDMDdYbWJiSmtEYlZLYkVxb0dPcTRaYmZNZzBKVXQvSzV0UUx0eW5RUzYvVUxqSFJGMm5FZ1c4MXh0ZkVrWE1tL2UyN2NMYjBXNkZ2SXEwZTYyN0N1WDBLNGdicEZqYzlJVHRaTnFFUXVDSFBNdzRtM1FMaGh0aDRSR0NQcURYZE8wdUlBbk1BVVRwZnZsUGtJMWlhbkN2dE5ma2dHWjNKQU1jb1FBMGdTZXFreVkrUzRaSTNvRTIxNDZKc1RCbXQwMnFiQmord3FtTithRVZIcXYya2p6UnBwQmlQZFlCK2dLRkNBZm1oWW9Ld0NNZ2lVSlJwcGFWdUlRalVLSncwQ1ozcElLNGdFalpjUFVBMENTWmNXbmlFRFNENzRDNHJPSERRQVhaSmRZQVZtQk1Kd2NDRzFrTkg1WjVsTjFPZHhPTFFtb2lpZTF1Z2pPSkZUSzRnY3h4TU9GaUl4eVhGSWJxT0JXQ0JLY1dNbUNTQTNKQVBPVStSdzdtUmMyQWpsVXB3aHJaNitJV1ZTWHN0MVRDWTRqaUxSVUNubEM0UU9vYVlONDlFTVA4QVJ5QXF4dm5SUkxYQTI2aFpIdVF0TS9oa3dEbXlJMURYbURTWVFvMEErcXVHRVgvaVF0eDZvQ0RNUVpvbkVBRTA1Rk9vRTNVd040V3FKMUNYU0taekpUUUJxOEJPSE5JcnNpT01XaXY3WnZBb1JKaWd0SlFFdDRqb2JKRnlJbmt0T3QzRUZCL0VleFFFQUcxQUs0Q0JjWGc2WnZGRGprRDVsTmpRUzBtOWR4aVFnUThPRlNSWFlxOFVQUFVQUUJVY3gyeEZqUTJqNUZPQWM4eVlCeUlrY3BpNmZZRVRBT2FmUkdobVBPVTk4TWJEclZJSDdRVnFKNHNRUWJHU2ZGdUZtWXJPWmhHcm1sc2Rjci9IRHlTYW5udFRDNFlkK0pBLzE1aFhJTitpM0JUR3REYTBuenVuUTV3anlDY05QTVRWT2dtSzVFV2pvcWk4a21TY20xemxVQngyT2dDTVRaUGt4QnBDQWtDQVlzakJOYWIydjBDekNtUnNtbHU4UWhVb1VRdWhBd2dzSjNZU2oyRkRzLy9FQUNrUkFBSUNBZ0lDQWdNQUFnSURBQUFBQUFFQ0FBTUVFUVVTRXlFR01SQVVJaUJCRlRJakpFTC8yZ0FJQVFNQkFRZ0FJTTZ6M1BjS3pyTWJIc3NjYXhyWEZiVmpKeDhmSnQ4VmRsRDBNVWNDZFoxTTBabzZtaitQYzBUOTZNMFpvelJtakFEK05DYUUwWnFGSmhXdFFXTVVpdkY2U3pHOEI4aTVSYThkMzhERWJTYW12d0ZuV0Q4Nm1oTkNhRSt2d0FmeG9RZ2IvR1BhNnFVQ0JUUWVpK1dtMENhcnZ1L3RxbngvcDBRc1JMTWF4ZlkxT3Y0MU9zNlFyb1RRbWhOQ2RaMU0xTkdhbW9GaFdWL3cyNEZJcE92SzFsbldQMXNIcXR1aUVBTmo1QzlGSXR4NldWUEdqcVREVzYvZldkWjFuV2RaMUU2aWRSK09qYTNPczZpYUVBKzRGOVFyQUFJbEJlaHJGcDJ3RmdLblpsbVMxQUlHTUZMR0xhMk1wcld0S2JSNk5iMWcxc2FleTdYcVFkSHJPc0srcDBNNlF6N0g1Nm1BVHJPcG1wNFhJM0V4YWkva21CV2xlVUErZFhXQ1FsZU1ReFo1U1NqRXhBcDNFZGtVaUxXTFc3RHhDcGl0cHh1MjJpb0YzT3M2bUZUT3M2d0xOQ0VldlhXZFpxYU0vclhxdng2MFJ0cjlRNzJkNmhTWStLOXlzeTJVdlMzVjJ1RlFLaXRlK2l5VytQOEFsdkFsZzNXNjdiVDZtakNOenJBazZ6cUoxRTBKb1RRbW9qRmRpSWcremNvVzV1dXAxOWJtbzJob0psNUQxQkVneHNGMkpXekd1cDl1R0t1WnRkYW5jT2RNS2hydFVLeVRHVHI5NkVWZGoxME02enJPcG5VenFaMWdYK1NZdnM2RGoreVFSdmY0NmlFVE5Pd2sxS3JyS2ovSlhIdU83SHdMM0o2VnA0eG9JK25pNmIvc0RvRUthSzUxMTlBVFFtaE5HYU0wWm93SVI3bGFqZm82TE5HVFIwUHgvcVoyd2xaZ0cvc0lzOGorUHBORUhZcFBuYnJjYUttTzBOVG9kRWpYNDBJQnFhTTBmOFNOalVvVDNBcW5jdUk3L3dBNjJaMWxkZlpoT1R3a1RDU3dkUk9zNnpxSmlLUExBQm83cnlpaDZ5cXFySUJBeU1DNmhoQmlBRFVLVUFFci9ocUxWdEN4YjFyVkxXTzhDalpqRGFnaEszYzZBRXFIc1RrbDN4b2FhbldkWUsyLzFpT3Y3QzZjcDdCcUZRUHUyMU1hNGl2T3ljZ0d0bHF0OHE2Y1VPKytvbXBvVHFUc3pRLzBpRTF0b0E3MGExNm5jVlJvenJxcmNUUWJjSUFNVDdFekFCeGM2bUNzZ0hzbFkvOEFwaVY5cmd0NWN6czNMYkdlNFFWNUwvZVgxOG5ZM1BXK1BXMDJxRFl4THY3SU9vUm9SRkxqYkZOMW1hRzlUSHBSc1YySVFrYmdIUmpPNVplcGZTS1FTSy85TldoYllTbHZzWmczeHhqTldpN1o4eEFObE1peTJqYXFMblA5OGZkU2J4clA2ZnZFem9TNU10SVIxWVpGVlZ2RmpkNjFWN000ZS95Wm8xMGdxaTF3cHBCQUNQcFBXRzBHL3FNZzdtYUV1L3JGRUNJLy9WelVBTnI2cEJYTDduakc2SEtzcko3MldHNU56QUwzWW0yR0JWWWV6NE9DbE9TckRrYVI1OXloMjhyQVo1Y1kxZGpZNzFQOGRheHNDM2pNNzBuRjVtUmZ5SUVDZ0NhRTFPbjhDZEoyMVIxbTFFQTB4Z1FFeTVPbFJFcTlOTGFrY1JVNjA2Rm0vd0JNckZSdFFWdVlxZ0RxYmY4QXhNQU1XcktPU3JuUG92dHlrc1d2R0ZTZ0ZzSEh1VHE5R0pqVTR4cFN2RXdzY2pxVFZTbXdPUVRYYURrY1lEWk9mamlzUFA4QWtGUG9MbHF4MU93YkhEQU9uYlVSMUlKbFQxRi81eW02b1MrTTlGbGhFZTVFMXZ6VmlrR01WZW5xVGxVVnN5bHN1a0tETVN4TC9hL3UwbXpwQmxMWGsrTTV0NjFsZGp4dFVHQlBqeDJZNFBKNCtYVlkxVm5ONE9QeUl4WHpmbVNZV2ErT3o1SnR4VGoyNGFadVh4aXZRT2JveCtGUy9JZmxIU25MRXhQa09aZGswclI1YjZPSFR5VThoZmZqVjlxMzVGK0p2TlBCYzF5TnRsZmw1Mjk2c05mRDhWenVWdTVOeGZ6dC9JMDFZbXZEU254bzIzVzIvd0RvQnp5WExWcG01SVBONTFlUHhQSDJUNGxsREp3WE11emJzTGxHckhQOGxmaWZKUlVueWJJdVRJeDFyNzNZK1FLcGFkWWJHZkhNK3JMcnRWZWRORlhKMFd0OGlvNDhjdTV0eDhSY3pCdlZQalZkL0Y4VlhSZit2aE54OVA2bktZNy9BSzJVcDRiaDdzZlB4N0FhMnlhLzZUZ3Vac1VYbW5qTWppQU82OFhZbnlCTWxlUnhyTFNBbkcvRE1uZ09hZS9LNXY0Nk1ud0NqSzRldkk0NDQxWW9MWUlyT1Y4VnR5WHkrMmY4ZWJNNC9GclRnc0plTXA4UTVUQlBJb3FMem54KzdrczliMXp1UHN6R3JjTWxpNmxTbXpHNkhpdURyNGd0MXllSmU0TDVjejQ1aWNqZjVMbDQvSEd0amo4WlBhTmlWTUJ0NkErcFRpMWh1cFhDdHg5Mk4rMWN5TWtzeDgzSXlGYXcxaFg5MjFmc2doRFhsVXFEYXZrY0FUcHIzRW9KcVl3SXhKMmFpWlZpcFcyeDRhejdBb1QvQUY0ZjQ2dzQyMTFFcjZqVS9YY1EwdVRzbkhjL1gvL0VBREFSQUFFREF3RUdCUU1FQXdBQUFBQUFBQUVBQWhFaE1VRlJBeEFTSUdGeElvR1JvYkV3UU1FVE10SHdjb0x4LzlvQUNBRURBUWsvQU9ZVVJtYVdwbjJSaDNzVDAwVFlJKzJ0Qlg3cGd3djNPenAvMUdTS0gzVmZ0UmYxUXlPOWpWRXBwYk9salB3VUtBeURxaDFCUWtkUHRhd2FJeEdFZkxDZHdrZWkwdmVSbUZRK3lIMlJ0K0w3NE00S2dUcjhxdERPaVBEcURieU83MCt3c3JqdjhLM3NvbmtvcWhHSndmd1VPdjhBU2o3L0FGeGNvOGhFelkwSjdZVFlLck9GUTk2WFhpR2lOZEQrRGxVSStybFdubC9wUWxwRmpWSGhQV284amNJVU9SVWV1K3JkVjczUStxYzh1bTRwdkNkUitRaUhOR2wvUzRWRlJCV1ErbHFPYlRmWlhWZXVmWCtVN3lQODJRK3Bxcm9VNURVR09jZ2pRcWtEdUZXZEU0SWt4SHJ6WTU5VWM4NW9td1NFRFVZN29laU0vVzE1RFZORWdJZjJFNWFLeG5jRHpVTzhKdExLUWlyTEJDS0Jqc20xajhvMDkxZERBbTFLSW9pWXlnQkJCcFJQQTdsT0xoQi94M0hrenlhN25WVmFvd1pFS283Smc5MDNodU5iV1RxOW9SbGFDcWZOc1JsUVJVR2hOWlRaQUZ1eFd4OFV4NGhtQ1I4TFpGcklPSTV0ZVRCV203WGxZVE9SWUp2Q0IxQlcxNFdnVkVYVHk2NXFlc3BvSUdFMkc2SnNJZWdWdHh2K054Q3p1TkVVSXFQaEZVQldxekNkWXdqUTdybWl2TWJyeVFzQkdlRzlFMC9xR0kwcmFxMlpQQ1ltVnRJSmNhQUFORGd3RXlRdGkvYWNUakliRkliZVRnbE1JSUxxUVhFZUxoNkRDTU5aczltWkF2eFNhQXJaUGR4Z2tVdkFOQkNad3ZEUkxUY0hJSzJYaUxRYVRTUWIra0puRnRBQVFBU1pwSkhTQW1RZHJZUWFBWEpuckFDYVNYT2p0UTFLQkRReDl3ZEU4RGkyYlFadk1tdVlsYllndDhVaUluQWo4WFJ5ejVDZWZDOXc3VkttSHRjT3RLcWFPejJCWEVRTm9BSEd4Q2FTM3dXQmlxbnhPSW9xamlhUVkxQlF3ZmdyRVRQbW1TU0JYcUNtU1RCdkdFNHRmeDdXQ2JIaGJ3K1dJVHk0aHN1R29NcWRpSE9pS0U5VDRnWUUxS21EczJTNFhvRE1XcWkvOU51eU5YR3ZFVG5FZ0l5NHhVM1BkT0RXc2ppQW1DZkVEZXNTUlRvbmZ1QWNMaW56QlJIQVcyQXNhMGxXSnFQOVhCRWtiUmhpb0lnaUl2S2FTR05nRWtXRHJIcW1VTGdTQ1NNR1ZXT0gyaGJKa3ZkTGJ6RXpVakpUV3hzcGtFVEFpRHc2VlVZbnlFSXh3dUR2UXJibGdBYlFHbEpPcWVXL3B1bnZpQ3B1UGxaQkhxbkYzRW16QmtLaGdaVFJBbW1ETjU3NVZLUkdJd093VnhNSXBzK2NJUUpwV1U0dzR5ZXAxVUZvRVYwdmJ1ckJHNmZ4OTVUcUJHcUtLTzg3anZLSy85az0iIGlkPSJmIiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIvPjwvZGVmcz48L3N2Zz4=", + "description": "Displays changes to power consumption data over time visualized with color ranges and background.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.rangeChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.rangeChartWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: false,\n defaultDataKeysFunction: function() {\n return [{ name: 'powerConsumption', label: 'Power consumption', type: 'timeseries' }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-range-chart-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-range-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Power consumption\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":2592000000,\"interval\":86400000,\"fixedTimewindow\":{\"startTimeMs\":1700577227898,\"endTimeMs\":1700663627898},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"dataZoom\":true,\"rangeColors\":[{\"from\":0,\"to\":5,\"color\":\"#3B911C\"},{\"from\":5,\"to\":15,\"color\":\"#F77410\"},{\"from\":15,\"to\":null,\"color\":\"#DE2343\"}],\"outOfRangeColor\":\"#ccc\",\"fillArea\":true,\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm\",\"lastUpdateAgo\":false,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:cG93ZXJfY29uc3VtcHRpb25fcmFuZ2VfY2hhcnRfYmFja2dyb3VuZF8oMSkucG5n:cG93ZXJfY29uc3VtcHRpb25fcmFuZ2VfY2hhcnRfYmFja2dyb3VuZC5wbmc=;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":4}}},\"title\":\"Power consumption\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"kW\",\"decimals\":0,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "power", + "energy", + "energy usage", + "electric load", + "electricity", + "power efficiency", + "load profile" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pressure_card.json b/application/src/main/data/json/system/widget_types/pressure_card.json new file mode 100644 index 0000000000..8f6523e435 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pressure_card.json @@ -0,0 +1,28 @@ +{ + "fqn": "pressure_card", + "name": "Pressure card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest pressure telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pressure', label: 'Pressure', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 80 - 40;\\nif (value < 980) {\\n\\tvalue = 980;\\n} else if (value > 1040) {\\n\\tvalue = 1040;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"compress\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1000,\"color\":\"#D81838\"},{\"from\":1000,\"to\":1020,\"color\":\"#80C32C\"},{\"from\":1020,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":1000,\"color\":\"#D81838\"},{\"from\":1000,\"to\":1020,\"color\":\"#80C32C\"},{\"from\":1020,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Pressure card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"hPa\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "barometry" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pressure_card_with_background.json b/application/src/main/data/json/system/widget_types/pressure_card_with_background.json new file mode 100644 index 0000000000..b25d02d356 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pressure_card_with_background.json @@ -0,0 +1,28 @@ +{ + "fqn": "pressure_card_with_background", + "name": "Pressure card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest pressure telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pressure', label: 'Pressure', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 80 - 40;\\nif (value < 980) {\\n\\tvalue = 980;\\n} else if (value > 1040) {\\n\\tvalue = 1040;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"compress\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1000,\"color\":\"#DE2343\"},{\"from\":1000,\"to\":1020,\"color\":\"#7CC322\"},{\"from\":1020,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":1000,\"color\":\"#DE2343\"},{\"from\":1000,\"to\":1020,\"color\":\"#7CC322\"},{\"from\":1020,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Pressure card with background\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"hPa\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "barometry" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pressure_chart_card.json b/application/src/main/data/json/system/widget_types/pressure_chart_card.json new file mode 100644 index 0000000000..b829652b7d --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pressure_chart_card.json @@ -0,0 +1,31 @@ +{ + "fqn": "pressure_chart_card", + "name": "Pressure chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a pressure data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pressure', label: 'Pressure', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'hPa', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'pressure', 'hPa', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 80 - 40;\\nif (value < 980) {\\n\\tvalue = 980;\\n} else if (value > 1040) {\\n\\tvalue = 1040;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"hPa\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1000,\"color\":\"#D81838\"},{\"from\":1000,\"to\":1020,\"color\":\"#80C32C\"},{\"from\":1020,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 80 - 40;\\nif (value < 980) {\\n\\tvalue = 980;\\n} else if (value > 1040) {\\n\\tvalue = 1040;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"hPa\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"hPa\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Pressure\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"compress\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "barometry" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pressure_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/pressure_chart_card_with_background.json new file mode 100644 index 0000000000..7fd0a6e738 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pressure_chart_card_with_background.json @@ -0,0 +1,31 @@ +{ + "fqn": "pressure_chart_card_with_background", + "name": "Pressure chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a pressure data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pressure', label: 'Pressure', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'hPa', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'pressure', 'hPa', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 80 - 40;\\nif (value < 980) {\\n\\tvalue = 980;\\n} else if (value > 1040) {\\n\\tvalue = 1040;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"hPa\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1000,\"color\":\"#DE2343\"},{\"from\":1000,\"to\":1020,\"color\":\"#7CC322\"},{\"from\":1020,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 80 - 40;\\nif (value < 980) {\\n\\tvalue = 980;\\n} else if (value > 1040) {\\n\\tvalue = 1040;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"hPa\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"hPa\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Pressure\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"compress\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "barometry" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pressure_progress_bar.json b/application/src/main/data/json/system/widget_types/pressure_progress_bar.json new file mode 100644 index 0000000000..4350226555 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pressure_progress_bar.json @@ -0,0 +1,29 @@ +{ + "fqn": "pressure_progress_bar", + "name": "Pressure progress bar", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays pressure reading as a horizontal progress bar. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pressure', label: 'Pressure', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 80 - 40;\\nif (value < 980) {\\n\\tvalue = 980;\\n} else if (value > 1040) {\\n\\tvalue = 1040;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1000,\"color\":\"#D81838\"},{\"from\":1000,\"to\":1020,\"color\":\"#80C32C\"},{\"from\":1020,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":870,\"tickMax\":1085,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":1000,\"color\":\"#D81838\"},{\"from\":1000,\"to\":1020,\"color\":\"#80C32C\"},{\"from\":1020,\"to\":null,\"color\":\"#D81838\"}]},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Pressure\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"hPa\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"compress\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "progress", + "weather", + "environment", + "barometry" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pressure_progress_bar_with_background.json b/application/src/main/data/json/system/widget_types/pressure_progress_bar_with_background.json new file mode 100644 index 0000000000..eb7216ffd5 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pressure_progress_bar_with_background.json @@ -0,0 +1,29 @@ +{ + "fqn": "pressure_progress_bar_with_background", + "name": "Pressure progress bar with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays pressure reading as a horizontal progress bar with background. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'pressure', label: 'Pressure', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 80 - 40;\\nif (value < 980) {\\n\\tvalue = 980;\\n} else if (value > 1040) {\\n\\tvalue = 1040;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1000,\"color\":\"#DE2343\"},{\"from\":1000,\"to\":1020,\"color\":\"#7CC322\"},{\"from\":1020,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":870,\"tickMax\":1085,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":1000,\"color\":\"#DE2343\"},{\"from\":1000,\"to\":1020,\"color\":\"#7CC322\"},{\"from\":1020,\"to\":null,\"color\":\"#DE2343\"}]},\"barBackground\":\"rgba(0, 0, 0, 0.1)\"},\"title\":\"Pressure\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"hPa\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"compress\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "progress", + "weather", + "environment", + "barometry" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/progress_bar.json b/application/src/main/data/json/system/widget_types/progress_bar.json new file mode 100644 index 0000000000..1a92b6d470 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/progress_bar.json @@ -0,0 +1,28 @@ +{ + "fqn": "progress_bar", + "name": "Progress bar", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAeFBMVEXg4ODf39/g4ODg4OAAAAD////g4OD19fUhISE/Ut3j4+OsrKzHx8eQkJB0dHQ8PDzx8fEvLy+6urrV1dWCgoJYWFienp5KSkrv7++GhoZmZmaXl5f39/epqanLy8vCwsL29vZtfORiceJ+fn7m5ubU1NTn6fve4fIbcz38AAAABXRSTlPvIL+vAC9A4IoAAASZSURBVHja7d0NT9swEIDhMji4nb/jxEk/NmCM7f//w/niQLtJyxamtdfiF6EmDkh+1Di0IojVzdWH1e25d311s7paDXD2+cy49nABDfm0gotodSmQ2woRVoVIq0KkVSHSqhBpVYi0KkRaFSKtGUijctrDv6XQwEzHgDjkXLoAiKaoAurzhxAAGGxBm2gMgE+25SHeUNE0QKbRNo/otlWQo2QNQa6xrdpD1DSqW6vyBhmtrD86RKHNGIcOyOUHZJDDEEIe1xgQdT487gGF0CNmgUXnsH+BhPzhCKDFfNh50OgQ6dgQv0HFEB2hw8QYDQo3HlqGZFf0066GDiNQFmvcMEZNEMdH2zza8WhivopwVEjX9QF7yBCVSbwFCc04YdBoy4xhkw3QYAsWjQciiNhH8OT3a8RnXo4ooRm/M3dMSN91m+QnCM+6zN9h2ShTyq4xy08X9mpcVxgswR4yfktymDsJhICbIPEA4n+GJJXTfNS48atIWQzxABKQl0YEJQDiQ/Dl1LeoDyHtONWYj41TDtDYvJMwTZA2w7ADwwNJAAQS9sqgY4NLyb1CKGCrGNeh0QY7Pq6bHvUEQaMcKh5VJkiAgAmIHfGGw7B5hQD1iMHkR4vlC5TLA+l1jeS9dlo5LdojQeYj/7rBp8xrnvz+kSOCg349WjolpES9itphBEm9BeI3OJ46omLI8qhppP2e6z28sTqvKkRaFSKtCpFWhUirQqRVIdKqEGlViLQqRFoVIq0KkVaFSKtCpFUh0qoQaVWItCpEWhUirQqR1jzER609HEbSbhT4G4g3AXOWYKrpAnYgszlIjyVXJNTl7XOEJMQ+ArXT5L3LpBQJZDYD6RGJAQED5DKoBbnN3uTfvIAAgBB7EFyBzBbQlXsTNV/EQGh/hPgWUZW7e1PglR+hJOw6PA/RfCs2Gsg5nApFYvtO1L2A8xC1n3ne6DWQRewKEfQGSM7FeB5CSrUOUY0QN13D0DPEQuzANiAlhsxHAYNnSA9ci6gZ5Jq2VRbEVCCzWcRYrl2cKRAgk6gT9NPx9xBvbTqYeofBTyz9IlTaiDm3Zp6RckoxAePIScxzGKBEPTnVK5DRDMS+vtZyLMguBbTZv1BxpAw0UpbJDMQ7LAWCXIMl52EsGVAtKCmvv2YgQNYxY0PTbs97xh9SN07KemfITFHHw4nzntA3jO/kPfsZVSHSqhBpVYi0KkRaFSKtCpHWO4B8e/y4rC/f7/5/D8shXz4u7fHuCO0WQz4u7+4YLYY8LnZ8uTtCu+VrZOm59XiMNbJ7x1etM6tCpFUh0qoQac1A7s325L+P2t1/AoBhu90O04SWQ4b1ANsTP10Pa7XOD5/u4fMWbte74WlYDnlOAM3Jn5KBIesBHp7gXmXS5+WQz5/EQOz42TQVIgWyHgDyqfVGyPD0AOkZThxD+JrznBf7NpO+LodAszZbOHEF8nW9ZYEyZvnll9vtQErDNKH3/ZP9vKoQaVWItCpEWhUirQqRVoVIq0KkVSHSqhBpVYi0LgiygovodvXhAv71NIC/Xl2txPzpxNsbMuPm6vr23Ft9uLr5AdpbpSAPQyy7AAAAAElFTkSuQmCC", + "description": "Displays any value reading as a horizontal progress bar. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'humidity', label: 'humidity', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"humidity\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":100,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Progress bar\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "progress", + "loading", + "state" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pump_vibration_card.json b/application/src/main/data/json/system/widget_types/pump_vibration_card.json new file mode 100644 index 0000000000..8ca932dffa --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pump_vibration_card.json @@ -0,0 +1,33 @@ +{ + "fqn": "pump_vibration_card", + "name": "Vibration card", + "deprecated": false, + "image": "tb-image:dmlicmF0aW9uX2NhcmQuc3Zn:dmlicmF0aW9uX2NhcmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxwYXRoIGZpbGw9IiNGRkE2MDAiIGQ9Ik00MSA0NS4zYy0xLjggMC0yLjkuNS0zLjkgMS0uOC40LTEuNS44LTIuNi44YTUgNSAwIDAgMS0yLjYtLjhjLTEtLjUtMi0xLTMuOS0xLTEuNyAwLTIuOC41LTMuOCAxLS44LjQtMS41LjgtMi42Ljh2Mi41YzEuNyAwIDIuOC0uNSAzLjgtMSAuOC0uNSAxLjUtLjggMi42LS44YTUgNSAwIDAgMSAyLjcuOGMxIC41IDIgMSAzLjggMSAxLjggMCAyLjgtLjUgMy44LTFhNSA1IDAgMCAxIDUuMyAwYzEgLjUgMiAxIDMuOCAxdi0yLjVhNSA1IDAgMCAxLTIuNi0uOGMtMS0uNS0yLjEtMS0zLjgtMVptMC01LjhjLTEuOCAwLTIuOS42LTMuOSAxLS44LjUtMS41LjgtMi42LjhhNSA1IDAgMCAxLTIuNi0uN2MtMS0uNS0yLTEtMy45LTEtMS43IDAtMi44LjUtMy44IDEtLjguNC0xLjUuNy0yLjYuN1Y0NGMxLjcgMCAyLjgtLjYgMy44LTFhNSA1IDAgMCAxIDUuMyAwYzEgLjQgMiAxIDMuOCAxIDEuOCAwIDIuOC0uNiAzLjgtMWE1IDUgMCAwIDEgNS4zIDBjMSAuNCAyIDEgMy44IDF2LTIuNmE1IDUgMCAwIDEtMi42LS43Yy0xLS41LTIuMS0xLTMuOC0xWm0zLjgtMTAuNGE3LjYgNy42IDAgMCAwLTcuNyAwYy0uOC40LTEuNS44LTIuNi44YTUgNSAwIDAgMS0yLjYtLjhjLTEtLjUtMi0xLTMuOS0xLTEuNyAwLTIuOC41LTMuOCAxLS44LjQtMS41LjgtMi42Ljh2Mi41YzEuNyAwIDIuOC0uNiAzLjgtMSAuOC0uNSAxLjUtLjggMi42LS44YTUgNSAwIDAgMSAyLjcuN2MxIC41IDIgMSAzLjggMSAxLjggMCAyLjgtLjUgMy44LTEgLjgtLjQgMS41LS43IDIuNy0uN2E1IDUgMCAwIDEgMi42LjdjMSAuNSAyIDEgMy44IDF2LTIuNWMtMS4xIDAtMS44LS4zLTIuNi0uN1pNNDEgMzMuOGMtMS44IDAtMi45LjUtMy45IDFhNSA1IDAgMCAxLTUuMiAwYy0xLS41LTItMS0zLjktMS0xLjcgMC0yLjguNS0zLjggMWE1IDUgMCAwIDEtMi42LjhWMzhjMS43IDAgMi44LS41IDMuOC0xIC44LS40IDEuNS0uOCAyLjYtLjhhNSA1IDAgMCAxIDIuNy44YzEgLjUgMiAxIDMuOCAxIDEuOCAwIDIuOC0uNSAzLjgtMSAuOC0uNCAxLjUtLjggMi43LS44YTUgNSAwIDAgMSAyLjYuOGMxIC41IDIgMSAzLjggMXYtMi41YTUgNSAwIDAgMS0yLjYtLjhjLTEtLjUtMi4xLTEtMy44LTFaIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZD0ibTYwLjIgMzUuMiAyLjQtNy4zaDEuN0w2MSAzN2gtMS4ybC4zLTEuN1ptLTIuMS03LjMgMi4zIDcuMy4zIDEuNmgtMS4xTDU2LjQgMjhINThabTguOCAyLjN2Ni42aC0xLjV2LTYuNkg2N1ptLTEuNi0xLjdjMC0uMy4xLS41LjMtLjYuMS0uMi4zLS4yLjYtLjIuMiAwIC41IDAgLjYuMi4yLjEuMi4zLjIuNiAwIC4yIDAgLjQtLjIuNS0uMS4yLS40LjItLjYuMi0uMyAwLS41IDAtLjYtLjJhLjcuNyAwIDAgMS0uMy0uNVptMy40LTFoMS41djhsLS4xIDEuM2gtMS40di05LjRabTUuOCA2LS4xIDEuNC0uNSAxLjEtLjguN2MtLjMuMi0uNy4zLTEuMS4zLS40IDAtLjgtLjEtMS4xLS4zbC0uOC0uNi0uNS0xLS4yLTEuNHYtLjRsLjItMS4zLjUtMWEyLjEgMi4xIDAgMCAxIDEuOS0xYy40IDAgLjguMiAxIC4zLjQuMi43LjQuOS43bC41IDEgLjEgMS41Wm0tMS40IDAtLjEtLjktLjItLjctLjUtLjUtLjctLjEtLjcuMWMtLjIgMC0uMy4yLS41LjRsLS4zLjUtLjEuN3YxbC4yIDFjLjIuMi4zLjQuNi41LjIuMi41LjMuOC4zLjMgMCAuNSAwIC43LS4ybC40LS41Yy4yLS4yLjItLjQuMy0uN3YtLjhabTQuNC0ydjUuM0g3NnYtNi42aDEuNHYxLjNabTItMS4zdjEuM2EyLjkgMi45IDAgMCAwLTEuMyAwIDEuMyAxLjMgMCAwIDAtLjguOXYuNkg3N0w3NyAzMmMwLS40LjItLjcuNC0xbC42LS42YTEuNyAxLjcgMCAwIDEgMS4yLS4yaC4yWm00LjYgNS4zVjMxLjdsLS41LS40LS42LS4xLS42LjFhMSAxIDAgMCAwLS41LjNsLS4xLjVoLTEuNWMwLS4yIDAtLjUuMi0uNy4xLS4zLjMtLjUuNi0uN2wuOC0uNSAxLjItLjFjLjUgMCAuOSAwIDEuMy4ycy43LjQuOS44Yy4yLjMuMy43LjMgMS4zVjM2bC4zLjZ2LjFoLTEuNWwtLjItLjZ2LS43Wm0uMy0yLjd2MWgtMWEzIDMgMCAwIDAtLjggMGwtLjUuMmExIDEgMCAwIDAtLjQuOHYuNWwuNC40aC42YTEuNiAxLjYgMCAwIDAgMS4zLS42Yy4yLS4xLjItLjMuMi0uNWwuNS43LS4yLjUtLjUuNmEyLjMgMi4zIDAgMCAxLTEuNi42Yy0uNCAwLS44LS4xLTEuMS0uM2EyIDIgMCAwIDEtLjgtLjdjLS4yLS4zLS4zLS42LS4zLTFzMC0uNy4yLTFjLjEtLjIuMy0uNS42LS42LjItLjIuNS0uNCAxLS41bDEuMi0uMWgxLjJabTYtMi42djFoLTMuN3YtMWgzLjdabS0yLjYtMS42aDEuNFYzNWwuMS41LjMuMmguM2EyLjMgMi4zIDAgMCAwIC42IDB2MS4xYTMuOCAzLjggMCAwIDEtMSAuMmwtMS0uMmMtLjItLjEtLjQtLjMtLjUtLjYtLjItLjMtLjItLjctLjItMS4xdi02LjVabTUuNSAxLjZ2Ni42aC0xLjV2LTYuNmgxLjVabS0xLjYtMS43YzAtLjMuMS0uNS4zLS42LjEtLjIuMy0uMi42LS4ycy41IDAgLjYuMmMuMi4xLjIuMy4yLjYgMCAuMiAwIC40LS4yLjUtLjEuMi0uMy4yLS42LjJzLS41IDAtLjYtLjJhLjcuNyAwIDAgMS0uMy0uNVptMy4yIDVjMC0uNSAwLTEgLjItMS40LjEtLjQuMy0uOC42LTEgLjItLjQuNi0uNiAxLS44bDEuMi0uMmMuNSAwIDEgMCAxLjMuMi40LjIuOC40IDEgLjcuMy4zLjUuNy42IDEuMS4yLjQuMi45LjIgMS4zdi4yYzAgLjUgMCAxLS4yIDEuMy0uMS40LS4zLjgtLjYgMWEyLjggMi44IDAgMCAxLTIuMiAxYy0uNSAwLTEgMC0xLjQtLjJsLTEtLjctLjUtMWMtLjItLjUtLjItMS0uMi0xLjRabTEuNCAwIC4xIDEgLjMuNi41LjUuOC4yYTEuNCAxLjQgMCAwIDAgMS4yLS43bC4zLS43di0xLjhsLS4zLS43YTEuNCAxLjQgMCAwIDAtMS4zLS42bC0uNy4xLS41LjUtLjMuN3YuOFptNy42LTEuOXY1LjJoLTEuNHYtNi42aDEuM2wuMSAxLjRabS0uMiAxLjdoLS41YzAtLjUgMC0xIC4yLTEuM2wuNS0xYTIuNCAyLjQgMCAwIDEgMi0xbC44LjIuNy40LjQuOC4xIDEuMXY0LjNoLTEuNHYtNC4zYzAtLjMgMC0uNS0uMi0uNyAwLS4yLS4yLS4zLS40LS40bC0uNi0uMWExLjQgMS40IDAgMCAwLTEuMi42bC0uMy42LS4xLjhaIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNCIgZD0iTTYxLjIgNTB2LjhoLTMuNFY1MGgzLjRabS0zLTUuOXY2LjdINTd2LTYuN0g1OFptNi43IDUuN1Y0N2wtLjQtLjNhMSAxIDAgMCAwLS40LS4xaC0uNWwtLjMuMy0uMS40SDYybC4xLS42LjQtLjUuNy0uMy44LS4xYy40IDAgLjcgMCAxIC4yLjMgMCAuNS4zLjcuNS4yLjMuMi42LjIgMVY1MC4zbC4yLjVoLTEuMWwtLjEtLjR2LS42Wm0uMi0ydi43aC0xLjNsLS40LjJhLjcuNyAwIDAgMC0uMy42di40bC4zLjNoLjVhMS4yIDEuMiAwIDAgMCAxLS40bC4xLS40LjQuNS0uMi40LS40LjRhMS43IDEuNyAwIDAgMS0xLjIuNGMtLjMgMC0uNiAwLS44LS4yLS4zLS4xLS41LS4zLS42LS41LS4yLS4yLS4yLS41LS4yLS44bC4xLS43LjQtLjUuOC0uM0g2NVptNSAxLjd2LS4zbC0uMy0uM2EzIDMgMCAwIDAtLjctLjJjLS4zIDAtLjUgMC0uNy0uMmwtLjYtLjNjLS4xIDAtLjMtLjItLjMtLjRsLS4yLS41YzAtLjIgMC0uNC4yLS42IDAtLjIuMi0uMy4zLS41bC43LS4zLjgtLjFjLjQgMCAuNyAwIDEgLjIuMy4xLjYuMy43LjUuMi4zLjMuNS4zLjhINzBWNDdsLS40LS4zYTEgMSAwIDAgMC0uNC0uMSAxIDEgMCAwIDAtLjUgMGwtLjMuM2EuNi42IDAgMCAwIDAgLjVsLjEuMi4zLjIuNS4xYy40IDAgLjcuMiAxIC4zbC43LjUuMi43YTEuMyAxLjMgMCAwIDEtLjYgMS4xIDIgMiAwIDAgMS0uNi4zbC0uOC4xYy0uNSAwLS45IDAtMS4yLS4ybC0uNy0uNmMtLjItLjMtLjItLjYtLjItLjhoMWwuMi41LjQuMi41LjFoLjVsLjMtLjN2LS4zWm00LjYtMy42di44SDcydi0uOGgyLjhabS0yLTEuM2gxLjF2NC45bC4xLjMuMi4yaC4zYTEuNyAxLjcgMCAwIDAgLjQgMHYuOGEyLjggMi44IDAgMCAxLS44LjFsLS42LS4xYTEgMSAwIDAgMS0uNS0uNWwtLjItLjh2LTQuOVptOC42IDVWNDZoMS4xdjVoLTF2LTEuMlptLjItMWguM3YxbC0uNC43LS42LjVhMiAyIDAgMCAxLS45LjEgMiAyIDAgMCAxLS42IDBsLS42LS40LS4zLS42LS4xLS44di0zLjJoMS4xdjMuNmwuMi4zLjMuMmguM2MuMyAwIC42IDAgLjgtLjIuMSAwIC4zLS4yLjMtLjVsLjItLjdabTMuNC0xLjh2NmgtMS4xdi03aDF2MVptMy4yIDEuNXYuMWwtLjEgMS0uNC44LS42LjVjLS4yLjItLjUuMi0uOC4ybC0uOC0uMS0uNi0uNS0uNC0uOC0uMi0xdi0uM2wuMi0xYzAtLjMuMi0uNS40LS44YTEuNiAxLjYgMCAwIDEgMS40LS42Yy4zIDAgLjYgMCAuOC4yLjIgMCAuNC4yLjYuNWwuNC44LjEgMVptLTEuMS4xdi0uN2wtLjMtLjZhMSAxIDAgMCAwLS44LS40aC0uNWExIDEgMCAwIDAtLjQuM2wtLjIuNC0uMS41djFjMCAuMiAwIC40LjIuNSAwIC4yLjIuNC4zLjVsLjcuMWguNWwuMy0uNC4yLS42LjEtLjZabTUuMyAxLjR2LTZoMS4xdjdoLTFsLS4xLTFaTTg5IDQ4LjR2LS4xYzAtLjQgMC0uNy4yLTEgMC0uMy4yLS42LjQtLjhhMS43IDEuNyAwIDAgMSAxLjQtLjdsLjguMS42LjUuNC44LjIgMXYuM2wtLjIgMS0uNC44YTEuNiAxLjYgMCAwIDEtMS40LjYgMS43IDEuNyAwIDAgMS0xLjQtLjdsLS40LS44LS4yLTFabTEuMi0uMXYuN2wuMi42LjQuMy41LjEuNi0uMS40LS41LjItLjZWNDhsLS4xLS42LS4zLS40YTEgMSAwIDAgMC0uMy0uMmwtLjUtLjFhMSAxIDAgMCAwLS41IDAgMSAxIDAgMCAwLS40LjRsLS4yLjZ2LjZabTcuNCAxLjV2LTIuM2wtLjEtLjUtLjMtLjNhMSAxIDAgMCAwLS41LS4xaC0uNWwtLjMuMy0uMS40aC0xLjFsLjEtLjYuNC0uNS43LS4zLjgtLjFjLjQgMCAuNyAwIDEgLjIuMyAwIC41LjMuNy41LjIuMy4zLjYuMyAxVjUwLjNsLjIuNWgtMS4ybC0uMS0uNHYtLjZabS4xLTJ2LjdoLTEuM2wtLjQuMmEuNy43IDAgMCAwLS4zLjZ2LjRsLjMuM2guNWExLjIgMS4yIDAgMCAwIDEtLjRsLjEtLjQuNC41LS4yLjQtLjQuNGExLjcgMS43IDAgMCAxLTEuMS40Yy0uNCAwLS43IDAtMS0uMmwtLjUtLjVjLS4yLS4yLS4yLS41LS4yLS44bC4xLS43LjUtLjUuNy0uM2gxLjhabTQuNi0ydi45aC0yLjh2LS44aDIuOFptLTItMS4yaDEuMXY1LjJsLjMuMmguM2ExLjcgMS43IDAgMCAwIC4zIDB2LjhhMi44IDIuOCAwIDAgMS0uNy4xbC0uNy0uMWExIDEgMCAwIDEtLjQtLjVsLS4yLS44di00LjlabTUuMiA2LjNjLS4zIDAtLjcgMC0xLS4yYTIuMiAyLjIgMCAwIDEtMS4yLTEuMmwtLjEtMXYtLjJsLjEtMSAuNS0uOC43LS42IDEtLjEuOC4xLjcuNi40LjcuMSAxdi41aC0zLjh2LS44aDIuN3YtLjdhMSAxIDAgMCAwLS40LS40IDEgMSAwIDAgMC0uNi0uMiAxIDEgMCAwIDAtLjguNWwtLjMuNXYxLjVsLjMuNS40LjMuNi4xYTEuNSAxLjUgMCAwIDAgMS4zLS42bC42LjYtLjUuNGEyIDIgMCAwIDEtLjYuNGwtLjkuMVptOC42LTYuOHY2LjdIMTEzdi01LjRsLTEuNi42di0xbDIuNi0uOWguMVptNS42IDUuN3YtNmgxLjF2N2gtMXYtMVptLTMuMi0xLjR2LS4xbC4xLTEgLjQtLjhjLjItLjMuNC0uNC42LS41LjMtLjIuNS0uMi44LS4ybC44LjEuNi41LjQuOC4yIDF2LjNsLS4yIDEtLjQuOGExLjYgMS42IDAgMCAxLTEuNC42IDEuNyAxLjcgMCAwIDEtMS40LS43bC0uNC0uOC0uMS0xWm0xLS4xdi4xbC4yLjZjMCAuMiAwIC40LjIuNmwuMy4zLjUuMS42LS4xLjQtLjUuMi0uNlY0OGwtLjEtLjYtLjItLjRhMSAxIDAgMCAwLS40LS4ybC0uNS0uMWExIDEgMCAwIDAtLjUgMCAxIDEgMCAwIDAtLjMuNGwtLjIuNi0uMS42Wm0xMCAxLjVWNDdsLS40LS4zYTEgMSAwIDAgMC0uNC0uMWgtLjVsLS4zLjMtLjEuNGgtMS4xbC4xLS42LjQtLjUuNy0uMy44LS4xYy40IDAgLjcgMCAxIC4yLjMgMCAuNS4zLjcuNS4yLjMuMy42LjMgMVY1MC4zbC4xLjVoLTEuMWwtLjEtLjR2LS42Wm0uMi0ydi43aC0xLjNsLS40LjJhLjcuNyAwIDAgMC0uMy42di40bC4zLjNoLjVhMS4yIDEuMiAwIDAgMCAxLS40bC4xLS40LjQuNS0uMi40LS40LjRhMS43IDEuNyAwIDAgMS0xLjIuNGMtLjMgMC0uNiAwLS44LS4yLS4zLS4xLS41LS4zLS42LS41LS4yLS4yLS4yLS41LS4yLS44bC4xLS43LjQtLjUuOC0uM2gxLjhabTUuNS0yaDF2NC45YzAgLjQtLjEuOC0uMyAxLjEtLjIuMy0uNC42LS44LjdhMi43IDIuNyAwIDAgMS0xLjguMmMtLjIgMC0uNS0uMS0uNy0uMy0uMiAwLS40LS4yLS41LS40bC41LS43LjYuNWExLjYgMS42IDAgMCAwIDEuMyAwbC40LS40LjItLjdWNDZabS0zLjQgMi42di0uMWwuMS0xIC40LS44Yy4yLS4zLjQtLjQuNy0uNS4yLS4yLjUtLjIuOC0uMmwuOC4xLjYuNS40LjguMiAxdi4zbC0uMiAxLS40LjhhMS42IDEuNiAwIDAgMS0xLjQuNiAxLjggMS44IDAgMCAxLTEuNS0uN2wtLjQtLjh2LTFabTEuMS0uMXYuN2wuMy42LjQuMy41LjEuNi0uMS40LS41LjItLjZWNDhsLS4xLS42LS4zLS40YTEgMSAwIDAgMC0uMy0uMmwtLjUtLjFhMSAxIDAgMCAwLS45LjRsLS4yLjZ2LjZabTQuNC4xdi0uMWMwLS40IDAtLjcuMi0xIDAtLjMuMi0uNi40LS44bC44LS41Yy4zLS4yLjYtLjIgMS0uMi4zIDAgLjYgMCAxIC4yLjIgMCAuNC4zLjYuNWwuNS44LjIgMXYuMWMwIC40IDAgLjctLjIgMWwtLjUuOGEyLjEgMi4xIDAgMCAxLTEuNy43Yy0uMyAwLS42IDAtMS0uMi0uMiAwLS41LS4zLS43LS41bC0uNC0uOGEzIDMgMCAwIDEtLjItMVptMS4xLS4xdi4xbC4xLjYuMi42LjQuMy42LjFhMSAxIDAgMCAwIC45LS40bC4yLS42VjQ3LjdsLS4yLS42YTEgMSAwIDAgMC0xLS41IDEgMSAwIDAgMC0uNS4yIDEgMSAwIDAgMC0uNC4zbC0uMi42di42WiIvPjxwYXRoIGZpbGw9IiNGRkE2MDAiIGQ9Ik01NyAxMDN2My40SDM4LjVsLS4xLTIuNSAxMS0xNy4zaDMuNGwtMy43IDYuMy02LjMgMTAuMUg1N1ptLTMuMi0xNi40djI1LjZoLTQuM1Y4Ni42aDQuM1pNNzMuNCA5N3YxNS4yaC00LjJ2LTE5aDRsLjIgMy44Wm0tLjcgNWgtMS40YzAtMS40LjItMi42LjUtMy43cy44LTIgMS40LTIuOWMuNy0uOCAxLjUtMS41IDIuNC0yIDEtLjQgMi0uNiAzLjMtLjZhNyA3IDAgMCAxIDIuNC40Yy43LjIgMS4zLjYgMS44IDEuMi42LjUgMSAxLjIgMS4zIDIgLjMuOS40IDEuOS40IDN2MTIuOGgtNC4yVjk5LjljMC0xLS4yLTEuNy0uNS0yLjItLjItLjUtLjYtMS0xLjEtMS4xLS41LS4zLTEuMS0uNC0xLjgtLjQtLjggMC0xLjUuMi0yIC41LS42LjMtMSAuNy0xLjQgMS4yLS40LjUtLjYgMS4xLS44IDEuOC0uMi43LS4zIDEuNS0uMyAyLjNabTExLjgtMS4yLTIgLjVjMC0xLjIuMi0yLjMuNS0zLjMuNC0xIC44LTIgMS41LTIuN2E2LjcgNi43IDAgMCAxIDUuNS0yLjVjMSAwIDEuOC4xIDIuNi40LjcuMyAxLjQuNyAyIDEuMi41LjYuOSAxLjMgMS4yIDIuMi4zIDEgLjQgMiAuNCAzLjN2MTIuM2gtNC4zVjk5LjhjMC0xLS4xLTEuNy0uNC0yLjItLjMtLjUtLjYtLjktMS4xLTEtLjUtLjMtMS4yLS40LTEuOS0uNGE0IDQgMCAwIDAtMS43LjRjLS41LjItLjkuNS0xLjIgMS0uNC40LS42LjktLjggMS40bC0uMyAxLjhaTTEwNSA5N3YxNS4yaC00LjN2LTE5aDRsLjMgMy44Wm0tLjcgNWgtMS40YzAtMS40LjEtMi42LjQtMy43LjQtMS4xLjktMiAxLjUtMi45LjYtLjggMS40LTEuNSAyLjMtMiAxLS40IDItLjYgMy4zLS42YTcgNyAwIDAgMSAyLjQuNGMuNy4yIDEuNC42IDEuOSAxLjIuNS41IDEgMS4yIDEuMiAyIC4zLjkuNSAxLjkuNSAzdjEyLjhIMTEyVjk5LjljMC0xLS4xLTEuNy0uNC0yLjItLjMtLjUtLjctMS0xLjItMS4xLS41LS4zLTEtLjQtMS44LS40cy0xLjUuMi0yIC41Yy0uNi4zLTEgLjctMS40IDEuMi0uMy41LS42IDEuMS0uOCAxLjhsLS4yIDIuM1ptMTEuOC0xLjItMiAuNWMwLTEuMi4yLTIuMy41LTMuM3MuOC0yIDEuNC0yLjdhNi43IDYuNyAwIDAgMSA1LjUtMi41YzEgMCAxLjkuMSAyLjYuNC44LjMgMS41LjcgMiAxLjIuNS42IDEgMS4zIDEuMiAyLjIuMyAxIC40IDIgLjQgMy4zdjEyLjNoLTQuMlY5OS44YzAtMS0uMi0xLjctLjQtMi4yLS4zLS41LS43LS45LTEuMi0xLS41LS4zLTEuMS0uNC0xLjgtLjRhNCA0IDAgMCAwLTEuNy40Yy0uNS4yLTEgLjUtMS4zIDFsLS44IDEuNC0uMiAxLjhabTI3LjItMTQuMi0xMCAyNy44SDEzMGwxMC0yNy44aDMuM1pNMTU3IDEwN2MwLS40IDAtLjgtLjMtMS4xLS4yLS40LS42LS43LTEuMi0xLS42LS4zLTEuNS0uNS0yLjYtLjdsLTIuOC0uOGMtLjgtLjQtMS41LS43LTIuMS0xLjJhNC40IDQuNCAwIDAgMS0xLjktMy43YzAtLjcuMi0xLjUuNS0yLjIuMy0uNy44LTEuMyAxLjQtMS44LjctLjUgMS40LTEgMi40LTEuMi45LS40IDEuOS0uNSAzLS41IDEuNiAwIDMgLjMgNC4xLjggMS4yLjUgMiAxLjMgMi42IDIuMi43LjkgMSAxLjkgMSAzaC00LjNjMC0uNS0uMS0xLS40LTEuNC0uMi0uNC0uNi0uOC0xLTEtLjYtLjMtMS4yLS41LTItLjUtLjcgMC0xLjMuMS0xLjguNC0uNS4yLS44LjUtMSAuOWEyLjIgMi4yIDAgMCAwLS4yIDJjLjEuMy4zLjUuNi43bDEuMi42IDIgLjVjMS40LjMgMi43LjcgMy43IDEuMWE2IDYgMCAwIDEgMi40IDEuOWMuNi43LjkgMS43LjkgMi44YTUgNSAwIDAgMS0yLjEgNC4xYy0uNy41LTEuNSAxLTIuNSAxLjItMSAuMy0yIC40LTMuMS40LTEuOCAwLTMuMi0uMy00LjQtLjlhNyA3IDAgMCAxLTIuNy0yLjRjLS42LTEtMS0yLTEtM2g0LjFjMCAuOC4zIDEuNC43IDEuOS40LjQuOS44IDEuNSAxbDEuOC4zYy44IDAgMS40LS4xIDItLjMuNC0uMi44LS41IDEuMS0uOS4zLS4zLjQtLjcuNC0xLjJaIi8+PC9zdmc+", + "description": "Displays the latest vibration telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'vibration', label: 'Vibration', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Vibration\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 3.3 - 1.7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"square\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"waves\",\"iconColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":2.8,\"color\":\"#3FA71A\"},{\"from\":2.8,\"to\":4.5,\"color\":\"#FFA600\"},{\"from\":4.5,\"to\":7.1,\"color\":\"#F36900\"},{\"from\":7.1,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":2.8,\"color\":\"#3FA71A\"},{\"from\":2.8,\"to\":4.5,\"color\":\"#FFA600\"},{\"from\":4.5,\"to\":7.1,\"color\":\"#F36900\"},{\"from\":7.1,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Vibration card\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"mm/s\",\"decimals\":1,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "vibration", + "pump vibration", + "motion", + "resonance", + "dynamic balancing", + "mechanical integrity", + "pulsation", + "frequency" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pump_vibration_card_with_background.json b/application/src/main/data/json/system/widget_types/pump_vibration_card_with_background.json new file mode 100644 index 0000000000..fe45d55d90 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pump_vibration_card_with_background.json @@ -0,0 +1,33 @@ +{ + "fqn": "pump_vibration_card_with_background", + "name": "Vibration card with background", + "deprecated": false, + "image": "tb-image:dmlicmF0aW9uX2NhcmRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==:dmlicmF0aW9uX2NhcmRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48L2c+PHBhdGggZmlsbD0iI0Y4OUUwRCIgZD0iTTMzLjMgMzYuMmMtMS43IDAtMi44LjYtMy43IDEtLjkuNC0xLjUuOC0yLjYuOHMtMS44LS4zLTIuNi0uOGMtLjktLjQtMi0xLTMuNi0xLTEuOCAwLTIuOC42LTMuNyAxLS45LjQtMS41LjgtMi42Ljh2Mi40YzEuNyAwIDIuOC0uNSAzLjctMSAuOC0uNCAxLjQtLjcgMi42LS43IDEgMCAxLjcuMyAyLjUuNyAxIC41IDIgMSAzLjcgMSAxLjcgMCAyLjgtLjUgMy43LTEgLjgtLjQgMS41LS43IDIuNi0uNyAxIDAgMS43LjMgMi41LjcgMSAuNSAyIDEgMy43IDFWMzhjLTEuMSAwLTEuOC0uMy0yLjYtLjgtLjktLjQtMi0xLTMuNi0xWm0wLTUuNWMtMS43IDAtMi44LjUtMy43IDEtLjkuNC0xLjUuNy0yLjYuN3MtMS44LS4zLTIuNi0uN2MtLjktLjUtMi0xLTMuNi0xLTEuOCAwLTIuOC41LTMuNyAxLS45LjQtMS41LjctMi42LjdWMzVjMS43IDAgMi44LS42IDMuNy0xYTQuOCA0LjggMCAwIDEgNS4xIDBjMSAuNCAyIDEgMy43IDEgMS43IDAgMi44LS42IDMuNy0xYTQuOCA0LjggMCAwIDEgNS4xIDBjMSAuNCAyIDEgMy43IDF2LTIuNWMtMS4xIDAtMS44LS4zLTIuNi0uNy0uOS0uNS0yLTEtMy42LTFabTMuNi0xMC4xYTcuNCA3LjQgMCAwIDAtNy4zIDBjLS45LjQtMS41LjctMi42LjdzLTEuOC0uMy0yLjYtLjdjLS45LS41LTItMS0zLjYtMS0xLjggMC0yLjguNS0zLjcgMS0uOS40LTEuNS43LTIuNi43djIuNGMxLjcgMCAyLjgtLjUgMy43LTEgLjgtLjQgMS40LS43IDIuNi0uNyAxIDAgMS43LjMgMi41LjcgMSAuNSAyIDEgMy43IDEgMS43IDAgMi44LS41IDMuNy0xIC44LS40IDEuNS0uNyAyLjYtLjcgMSAwIDEuNy4zIDIuNS43IDEgLjUgMiAxIDMuNyAxdi0yLjRhNSA1IDAgMCAxLTIuNi0uN1pNMzMuMyAyNWMtMS43IDAtMi44LjUtMy43IDFhNC44IDQuOCAwIDAgMS01LjIgMGMtLjktLjUtMi0xLTMuNi0xLTEuOCAwLTIuOC41LTMuNyAxLS45LjQtMS41LjgtMi42Ljh2Mi40YzEuNyAwIDIuOC0uNSAzLjctMSAuOC0uNCAxLjUtLjggMi42LS44IDEgMCAxLjcuNCAyLjUuOCAxIC41IDIgMSAzLjcgMSAxLjcgMCAyLjgtLjUgMy43LTEgLjgtLjQgMS41LS44IDIuNi0uOCAxIDAgMS43LjQgMi41LjggMSAuNSAyIDEgMy43IDF2LTIuNGMtMS4xIDAtMS44LS4zLTIuNi0uOC0uOS0uNS0yLTEtMy42LTFaIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZD0ibTUyLjQgMjYuNyAyLjYtOC4yaDJsLTMuNiAxMEg1MmwuMy0xLjhaTTUwIDE4LjVsMi42IDguMi40IDEuOGgtMS4zbC0zLjYtMTBINTBabTkuOSAyLjZ2Ny40aC0xLjd2LTcuNEg2MFptLTEuOC0yIC4zLS42LjctLjJjLjMgMCAuNSAwIC43LjJsLjIuN2MwIC4yIDAgLjQtLjIuNmExIDEgMCAwIDEtLjcuMiAxIDEgMCAwIDEtLjctLjIuOC44IDAgMCAxLS4zLS42Wk02MiAxOGgxLjZ2OWwtLjEgMS41SDYyVjE4Wm02LjQgNi43di4yYzAgLjUgMCAxLS4yIDEuNSAwIC40LS4zLjgtLjUgMS4yLS4yLjMtLjUuNi0uOS44bC0xLjIuMmMtLjUgMC0uOSAwLTEuMi0uMi0uNC0uMi0uNy0uNC0uOS0uOGwtLjYtMS4xLS4zLTEuNXYtLjRMNjMgMjNsLjYtMS4xYTIuNCAyLjQgMCAwIDEgMi0xYy41IDAgMSAwIDEuMy4ybDEgLjguNCAxLjJjLjIuNS4yIDEgLjIgMS41Wm0tMS42LjJ2LS4ybC0uMS0uOWMwLS4zLS4xLS42LS4zLS44IDAtLjItLjMtLjQtLjUtLjVsLS44LS4yLS43LjEtLjUuNWEyIDIgMCAwIDAtLjQuNmwtLjEuN3YxLjJsLjIgMSAuNi43IDEgLjJjLjMgMCAuNSAwIC43LS4ybC41LS41Yy4yLS4yLjItLjUuMy0uOHYtMVptNS0yLjR2Nkg3MHYtNy40aDEuNnYxLjRabTIuMi0xLjR2MS41YTMuMiAzLjIgMCAwIDAtMS40IDAgMS40IDEuNCAwIDAgMC0uOSAxbC0uMS42aC0uNGwuMS0xLjIuNC0xYy4yLS40LjUtLjYuNy0uOGExLjkgMS45IDAgMCAxIDEuMy0uMmguM1ptNS4yIDZ2LTMuNmwtLjEtLjdhMSAxIDAgMCAwLS41LS40bC0uNy0uMi0uNy4xLS41LjQtLjEuNWgtMS43bC4yLS44LjctLjdhMyAzIDAgMCAxIDEtLjVBNCA0IDAgMCAxIDc4IDIxYy41IDAgMSAwIDEuNS4yLjQuMi43LjUgMSAuOS4yLjQuNC44LjQgMS40djQuMmwuMi43di4xaC0xLjdhMyAzIDAgMCAxLS4xLS43bC0uMS0uOFptLjItMy4xdjFoLTEuMWwtLjguMWMtLjMgMC0uNS4xLS42LjNhMSAxIDAgMCAwLS41LjljMCAuMiAwIC40LjIuNSAwIC4yLjIuMy40LjRsLjYuMWExLjggMS44IDAgMCAwIDEuNS0uN2wuMi0uNi41LjgtLjIuNmEzIDMgMCAwIDEtLjYuNiAyLjYgMi42IDAgMCAxLTEuNy42Yy0uNSAwLTEgMC0xLjMtLjMtLjQtLjEtLjctLjQtMS0uN2EyLjQgMi40IDAgMCAxLS4xLTIuMmwuNy0uOCAxLjEtLjQgMS40LS4yaDEuM1ptNi44LTIuOXYxLjJIODJ2LTEuMmg0LjJabS0zLTEuOEg4NXY3LjJsLjEuNS4zLjJoLjRhMi41IDIuNSAwIDAgMCAuNiAwdjEuM2E0LjIgNC4yIDAgMCAxLTEuMS4xYy0uNCAwLS43IDAtMS0uMi0uMy0uMS0uNS0uMy0uNy0uNi0uMi0uMy0uMi0uNy0uMi0xLjJ2LTcuM1ptNi4zIDEuOHY3LjRoLTEuN3YtNy40aDEuN1ptLTEuOC0yIC4zLS42LjctLjJjLjMgMCAuNSAwIC43LjJsLjIuN2MwIC4yIDAgLjQtLjIuNmExIDEgMCAwIDEtLjcuMiAxIDEgMCAwIDEtLjctLjIuOC44IDAgMCAxLS4zLS42Wm0zLjUgNS44di0uMmwuMy0xLjUuNi0xLjJhMyAzIDAgMCAxIDEuMS0uN2wxLjUtLjNjLjUgMCAxIDAgMS40LjMuNS4xLjguNCAxLjEuN2wuNyAxLjIuMiAxLjV2LjJjMCAuNSAwIDEtLjIgMS41bC0uNyAxLjJhMy4xIDMuMSAwIDAgMS0yLjUgMWMtLjYgMC0xIDAtMS41LS4yYTMuNSAzLjUgMCAwIDEtMS44LTJsLS4yLTEuNVptMS43LS4yVjI2bC40LjdjLjIuMi4zLjQuNi41LjIuMi41LjIuOC4yYTEuNiAxLjYgMCAwIDAgMS4zLS43bC40LS44di0yTDk2IDIzYTEuNiAxLjYgMCAwIDAtMS4zLS43Yy0uMyAwLS42IDAtLjguMi0uMy4xLS40LjMtLjYuNWwtLjMuOC0uMSAxWm04LjUtMnY1LjhoLTEuN3YtNy40aDEuNnYxLjZabS0uMyAxLjhoLS41YzAtLjUgMC0xIC4yLTEuNC4xLS41LjMtLjguNi0xLjFhMi43IDIuNyAwIDAgMSAyLjEtMWwxIC4xLjcuNS41LjguMiAxLjN2NC44aC0xLjd2LTQuOGwtLjEtLjhhLjkuOSAwIDAgMC0uNS0uNWwtLjctLjFhMS42IDEuNiAwIDAgMC0xLjMuN2wtLjQuNy0uMS44WiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjQiIGQ9Ik01Mi45IDQxLjZ2LjloLTMuNHYtMUg1M1ptLTMtNS44djYuN2gtMS4ydi02LjdoMS4xWm02LjggNS43di0yLjRsLS4xLS40LS4zLS4zYTEgMSAwIDAgMC0uNS0uMWgtLjVsLS4zLjMtLjEuNGgtMS4xbC4xLS42LjQtLjVjLjItLjEuNC0uMy43LS4zbC44LS4yIDEgLjJjLjMuMS42LjMuNy42LjIuMi4zLjYuMyAxVjQybC4yLjR2LjFoLTEuMmwtLjEtLjV2LS41Wm0uMS0ydi43aC0xLjNsLS40LjJhLjcuNyAwIDAgMC0uMy42di40bC4zLjIuNS4xYTEuMiAxLjIgMCAwIDAgMS0uNWwuMS0uNC40LjUtLjIuNGMwIC4yLS4yLjMtLjMuNWExLjcgMS43IDAgMCAxLTEuMi40Yy0uNCAwLS43IDAtMS0uMmwtLjUtLjVjLS4yLS4zLS4yLS41LS4yLS44bC4xLS43LjUtLjUuNy0uMyAxLS4xaC44Wm01LjEgMS43di0uM2wtLjQtLjNhMyAzIDAgMCAwLS43LS4ybC0uNy0uMi0uNi0uMy0uMy0uNC0uMS0uNnYtLjVsLjUtLjVjLjEtLjIuMy0uMy42LS4zbC44LS4yIDEgLjIuNy42Yy4yLjIuMy41LjMuOGgtMS4xbC0uMS0uNC0uMy0uMmExIDEgMCAwIDAtLjUtLjEgMSAxIDAgMCAwLS41IDBsLS4zLjNhLjYuNiAwIDAgMCAwIC41bC4xLjIuNC4xLjUuMiAxIC4zLjYuNS4yLjdhMS4zIDEuMyAwIDAgMS0uNSAxIDIgMiAwIDAgMS0uNy40SDYxbC0xLjItLjEtLjctLjctLjItLjhoMWwuMi41Yy4xLjIuMy4yLjQuM2gxbC4zLS4yLjEtLjNabTQuNS0zLjd2LjhoLTIuOHYtLjhoMi44Wm0tMi0xLjJoMS4ydjUuMmwuMi4xaC4zYTEuNyAxLjcgMCAwIDAgLjQgMHYuOWEyLjggMi44IDAgMCAxLS43IDBINjVhMSAxIDAgMCAxLS41LS41bC0uMS0uOHYtNC45Wm04LjYgNXYtMy44aDEuMXY1aC0xdi0xLjJabS4yLTFoLjRsLS4xIDEtLjQuNmMtLjEuMi0uMy40LS42LjVhMiAyIDAgMCAxLS44LjIgMiAyIDAgMCAxLS43LS4xYy0uMiAwLS40LS4yLS41LS4zbC0uNC0uNlYzNy41aDF2My43bC4zLjMuMi4xLjMuMWMuMyAwIC42IDAgLjgtLjJsLjQtLjV2LS43Wm0zLjQtMS44djZoLTEuMXYtN2gxdjFabTMuMiAxLjUtLjEgMWMwIC40LS4yLjYtLjQuOS0uMS4yLS4zLjQtLjYuNWwtLjguMmMtLjMgMC0uNiAwLS44LS4yLS4yIDAtLjQtLjMtLjYtLjVsLS40LS43LS4yLTFWNDBsLjItMSAuNC0uOGExLjcgMS43IDAgMCAxIDEuNC0uN2MuMyAwIC42IDAgLjguMi4zLjEuNS4zLjYuNWwuNC44LjEgMVptLTEgMC0uMS0uNy0uMi0uNWExIDEgMCAwIDAtMS0uNWwtLjQuMWExIDEgMCAwIDAtLjQuM2wtLjIuNC0uMS41di45bC4yLjZjMCAuMi4yLjMuNC40LjEuMi4zLjIuNi4ybC41LS4xLjQtLjQuMi0uNXYtLjZabTUuMiAxLjV2LTZoMS4xdjdoLTF2LTFaTTgwLjggNDBWNDBsLjEtMWMwLS40LjItLjYuNC0uOWExLjcgMS43IDAgMCAxIDEuNC0uN2MuMyAwIC42IDAgLjguMmwuNi41LjQuOC4yIDF2LjNsLS4yIDEtLjQuN2ExLjYgMS42IDAgMCAxLTEuNC43IDEuNyAxLjcgMCAwIDEtMS40LS43bC0uNC0uOC0uMS0xWm0xLS4xLjEuNy4yLjUuNC40LjUuMWMuMiAwIC41IDAgLjYtLjIuMiAwIC4zLS4yLjQtLjRsLjItLjd2LS44bC0uMS0uNS0uMy0uNGExIDEgMCAwIDAtLjMtLjNIODNhMSAxIDAgMCAwLS41IDAgMSAxIDAgMCAwLS40LjRsLS4yLjV2LjdabTcuNSAxLjV2LTIuNGwtLjEtLjRjMC0uMi0uMi0uMy0uMy0uM2ExIDEgMCAwIDAtLjUtLjFoLS41bC0uMy4zdi40aC0xLjJjMC0uMiAwLS40LjItLjYgMC0uMi4yLS40LjQtLjVsLjYtLjMuOS0uMiAxIC4yYy4yLjEuNS4zLjYuNi4yLjIuMy42LjMgMVY0MmwuMi40di4xaC0xLjJWNDJsLS4xLS41Wm0uMS0ydi43SDg4LjFsLS40LjJhLjcuNyAwIDAgMC0uMy42bC4xLjRzLjEuMi4zLjJsLjQuMWExLjIgMS4yIDAgMCAwIDEtLjVsLjItLjQuMy41YzAgLjIgMCAuMy0uMi40IDAgLjItLjIuMy0uMy41YTEuNyAxLjcgMCAwIDEtMS4yLjRjLS4zIDAtLjYgMC0uOS0uMmwtLjYtLjUtLjItLjguMS0uNy41LS41LjctLjMgMS0uMWguOFptNC42LTJ2LjhoLTIuOHYtLjhIOTRabS0yLTEuMmgxLjF2NC44bC4xLjQuMi4xaC4zYTEuNyAxLjcgMCAwIDAgLjQgMHYuOWEyLjggMi44IDAgMCAxLS44IDBoLS42YTEgMSAwIDAgMS0uNS0uNWwtLjItLjh2LTQuOVptNS4zIDYuM2MtLjQgMC0uNyAwLTEtLjJhMi4yIDIuMiAwIDAgMS0xLjMtMS4zbC0uMS0xVjQwbC4xLTEgLjUtLjkuNy0uNSAxLS4yYy4zIDAgLjYgMCAuOS4ybC42LjUuNC44LjIgMXYuNGgtMy45di0uOGgyLjhjMC0uMiAwLS40LS4yLS42YTEgMSAwIDAgMC0uMy0uNCAxIDEgMCAwIDAtLjYtLjIgMSAxIDAgMCAwLS44LjVsLS4yLjUtLjEuN3YuOGwuMy41LjUuMy41LjFhMS41IDEuNSAwIDAgMCAxLjMtLjZsLjYuNi0uNC40LS43LjRoLS44Wm04LjUtNi44djYuN2gtMXYtNS40bC0xLjcuNnYtMWwyLjYtMWguMVptNS42IDUuN3YtNmgxLjJ2N2gtMWwtLjItMVptLTMuMi0xLjRWNDBsLjEtMSAuNC0uOS42LS41LjktLjJjLjMgMCAuNSAwIC44LjJsLjUuNS40LjguMiAxdi4zYzAgLjMgMCAuNy0uMiAxIDAgLjMtLjIuNS0uNC43YTEuNiAxLjYgMCAwIDEtMS4zLjcgMS43IDEuNyAwIDAgMS0xLjUtLjdsLS40LS44di0xWm0xLjEtLjF2LjdsLjMuNWMwIC4yLjIuMy4zLjRsLjUuMWMuMyAwIC41IDAgLjctLjJsLjMtLjQuMi0uN3YtMS4zbC0uMy0uNGExIDEgMCAwIDAtLjQtLjNoLS41YTEgMSAwIDAgMC0uNSAwIDEgMSAwIDAgMC0uMy40bC0uMi41di43Wm0xMCAxLjV2LTIuNGwtLjEtLjQtLjMtLjNhMSAxIDAgMCAwLS41LS4xaC0uNWwtLjMuMy0uMS40aC0xLjFsLjEtLjYuNC0uNWMuMi0uMS40LS4zLjctLjNsLjgtLjIgMSAuMmMuMy4xLjYuMy43LjYuMi4yLjMuNi4zIDFWNDJsLjIuNHYuMWgtMS4ybC0uMS0uNXYtLjVabS4xLTJ2LjdoLTEuM2wtLjQuMmEuNy43IDAgMCAwLS4zLjZ2LjRsLjMuMi41LjFhMS4yIDEuMiAwIDAgMCAxLS41bC4xLS40LjQuNS0uMi40YzAgLjItLjIuMy0uMy41YTEuNyAxLjcgMCAwIDEtMS4yLjRjLS40IDAtLjcgMC0xLS4ybC0uNS0uNS0uMi0uOC4xLS43LjUtLjUuNy0uMyAxLS4xaC44Wm01LjUtMmgxdjQuOWMwIC40IDAgLjgtLjMgMS4xLS4yLjMtLjQuNi0uOC43YTIuNyAyLjcgMCAwIDEtMS44LjJsLS42LS4zYy0uMy0uMS0uNC0uMy0uNi0uNWwuNS0uNi42LjVhMS42IDEuNiAwIDAgMCAxLjMgMGwuNS0uNS4xLS42di0zLjdsLjEtMS4yWm0tMy40IDIuNlY0MGMwLS40IDAtLjcuMi0xIDAtLjQuMi0uNi40LS45LjEtLjIuMy0uNC42LS41bC44LS4yYy4zIDAgLjYgMCAuOC4yLjMuMS41LjMuNi41bC40LjguMiAxdi4zbC0uMiAxLS40LjdhMS42IDEuNiAwIDAgMS0xLjQuNyAxLjggMS44IDAgMCAxLTEuNC0uN2wtLjQtLjgtLjItMVptMS4xLS4xLjEuNy4yLjUuNC40LjUuMWMuMyAwIC41IDAgLjYtLjIuMiAwIC4zLS4yLjQtLjRsLjItLjd2LS44bC0uMS0uNS0uMi0uNGExIDEgMCAwIDAtLjQtLjNoLS41YTEgMSAwIDAgMC0uOS40bC0uMi41di43Wm00LjUgMCAuMS0xIC41LS44LjctLjYgMS0uMiAxIC4yLjcuNi40LjguMiAxYzAgLjQgMCAuOC0uMiAxIDAgLjQtLjIuNi0uNC45YTIuMSAyLjEgMCAwIDEtMS43LjdjLS40IDAtLjcgMC0xLS4ybC0uNy0uNS0uNS0uOGEzIDMgMCAwIDEtLjEtMVptMSAwIC4xLjcuMi41LjQuNC42LjFhMSAxIDAgMCAwIC45LS41bC4yLS41di0xLjRsLS4yLS41YTEgMSAwIDAgMC0xLS41IDEgMSAwIDAgMC0uNS4xIDEgMSAwIDAgMC0uNC40bC0uMi41di43WiIvPjxwYXRoIGZpbGw9IiNGODlFMEQiIGQ9Ik01NyAxMDEuOXYzLjRIMzguNWwtLjEtMi42IDExLTE3LjNoMy40bC0zLjcgNi4zLTYuMyAxMC4ySDU3Wm0tMy4yLTE2LjVWMTExaC00LjNWODUuNGg0LjNabTE5LjYgMTAuNFYxMTFoLTQuMlY5Mmg0bC4yIDMuOFptLS43IDVoLTEuNGMwLTEuMy4yLTIuNi41LTMuNy4zLTEgLjgtMiAxLjQtMi45LjctLjggMS41LTEuNCAyLjQtMS45IDEtLjQgMi0uNyAzLjMtLjdhNyA3IDAgMCAxIDIuNC40IDUuMiA1LjIgMCAwIDEgMy4xIDMuM2MuMy44LjQgMS44LjQgM1YxMTFoLTQuMlY5OC43YzAtMS0uMi0xLjctLjUtMi4yLS4yLS41LS42LS45LTEuMS0xLjEtLjUtLjItMS4xLS40LTEuOC0uNC0uOCAwLTEuNS4yLTIgLjUtLjYuMy0xIC43LTEuNCAxLjItLjQuNi0uNiAxLjItLjggMS45LS4yLjctLjMgMS40LS4zIDIuMlptMTEuOC0xLjEtMiAuNGMwLTEuMS4yLTIuMi41LTMuMy40LTEgLjgtMS45IDEuNS0yLjdhNi43IDYuNyAwIDAgMSA1LjUtMi41YzEgMCAxLjguMiAyLjYuNC43LjMgMS40LjcgMiAxLjMuNS41LjkgMS4zIDEuMiAyLjIuMy45LjQgMiAuNCAzLjJWMTExaC00LjNWOTguN2MwLTEtLjEtMS43LS40LTIuMy0uMy0uNS0uNi0uOC0xLjEtMS0uNS0uMy0xLjItLjQtMS45LS40YTQgNCAwIDAgMC0xLjcuNGMtLjUuMi0uOS42LTEuMiAxLS40LjQtLjYgMS0uOCAxLjUtLjIuNS0uMyAxLjEtLjMgMS44Wm0yMC40LTMuOVYxMTFoLTQuM1Y5Mmg0bC4zIDMuOFptLS43IDVoLTEuNGMwLTEuMy4xLTIuNi40LTMuNy40LTEgLjktMiAxLjUtMi45LjYtLjggMS40LTEuNCAyLjMtMS45IDEtLjQgMi0uNyAzLjMtLjdhNyA3IDAgMCAxIDIuNC40Yy43LjMgMS40LjcgMS45IDEuMnMxIDEuMiAxLjIgMmMuMyAxIC41IDIgLjUgMy4xVjExMUgxMTJWOTguN2MwLTEtLjEtMS43LS40LTIuMi0uMy0uNS0uNy0uOS0xLjItMS4xLS41LS4yLTEtLjQtMS44LS40cy0xLjUuMi0yIC41Yy0uNi4zLTEgLjctMS40IDEuMmwtLjggMS45LS4yIDIuMlptMTEuOC0xLjEtMiAuNGMwLTEuMS4yLTIuMi41LTMuMy4zLTEgLjgtMS45IDEuNC0yLjdhNi43IDYuNyAwIDAgMSA1LjUtMi41YzEgMCAxLjkuMiAyLjYuNC44LjMgMS41LjcgMiAxLjMuNS41IDEgMS4zIDEuMiAyLjIuMy45LjQgMiAuNCAzLjJWMTExaC00LjJWOTguN2MwLTEtLjItMS43LS40LTIuMy0uMy0uNS0uNy0uOC0xLjItMS0uNS0uMy0xLjEtLjQtMS44LS40YTQgNCAwIDAgMC0xLjcuNGMtLjUuMi0xIC42LTEuMyAxbC0uOCAxLjUtLjIgMS44Wm0yNy4yLTE0LjMtMTAgMjcuOEgxMzBsMTAtMjcuOGgzLjNabTEzLjcgMjAuNGMwLS40IDAtLjgtLjMtMS0uMi0uNC0uNi0uOC0xLjItMS0uNi0uMy0xLjUtLjYtMi42LS44bC0yLjgtLjhjLS44LS4zLTEuNS0uNy0yLjEtMS4xYTQuNCA0LjQgMCAwIDEtMS45LTMuN2MwLS44LjItMS41LjUtMi4yLjMtLjcuOC0xLjMgMS40LTEuOS43LS41IDEuNC0xIDIuNC0xLjIuOS0uMyAxLjktLjUgMy0uNSAxLjYgMCAzIC4zIDQuMS44IDEuMi42IDIgMS4zIDIuNiAyLjIuNy45IDEgMiAxIDNoLTQuM2MwLS40LS4xLTEtLjQtMS4zLS4yLS41LS42LS45LTEtMS4xLS42LS4zLTEuMi0uNC0yLS40LS43IDAtMS4zIDAtMS44LjMtLjUuMi0uOC41LTEgLjlhMi4yIDIuMiAwIDAgMC0uMiAyYy4xLjMuMy41LjYuN2wxLjIuNiAyIC41YzEuNC4zIDIuNy43IDMuNyAxLjJhNiA2IDAgMCAxIDIuNCAxLjhjLjYuNy45IDEuNy45IDIuOGE1IDUgMCAwIDEtMi4xIDQuMiAxMSAxMSAwIDAgMS01LjYgMS42Yy0xLjggMC0zLjItLjQtNC40LTFhNyA3IDAgMCAxLTIuNy0yLjNjLS42LTEtMS0yLTEtM2g0LjFjMCAuNy4zIDEuNC43IDEuOC40LjUuOS44IDEuNSAxbDEuOC4zYy44IDAgMS40IDAgMi0uM2wxLjEtLjhjLjMtLjQuNC0uOC40LTEuM1oiLz48ZGVmcz48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2MiIHRyYW5zZm9ybT0ibWF0cml4KC4wMDUwMzE0NSAwIDAgLjAwNjI4OTMxIDAgMCkiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjE5Ny40IiBoZWlnaHQ9IjE1Ny40IiB4PSIxLjMiIHk9IjEuMyIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMi40Ii8+PGZlQ29tcG9zaXRlIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzE1MTZfNjk0ODAiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzE1MTZfNjk0ODAiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48aW1hZ2UgeGxpbms6aHJlZj0iZGF0YTppbWFnZS9qcGVnO2Jhc2U2NCwvOWovNEFBUVNrWkpSZ0FCQVFBQUFRQUJBQUQvMndCREFBSUNBZ0lDQVFJQ0FnSURBZ0lEQXdZRUF3TURBd2NGQlFRR0NBY0pDQWdIQ0FnSkNnMExDUW9NQ2dnSUN3OExEQTBPRGc4T0NRc1FFUkFPRVEwT0RnNy8yd0JEQVFJREF3TURBd2NFQkFjT0NRZ0pEZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc3L3dnQVJDQUNmQU1jREFSRUFBaEVCQXhFQi84UUFIZ0FBQWdJQ0F3RUJBQUFBQUFBQUFBQUFCZ2NGQ0FNRUFBRUNDUXIveEFBYUFRQURBUUVCQVFBQUFBQUFBQUFBQUFBQ0F3UUJBQVVHLzlvQURBTUJBQUlRQXhBQUFBRDd4Wm52aDc3T1ozTjd2ZTY3dVozTTduYjEzZGRuZmJ6c3g5bnJONzNzT1poN091N3ZzNTNZTXlLRk1RQ1dCUjZIck83THVoN3J1NTNkN3ZudTUzYzdzUTVuM283QXc1bWJkamhCWm84K0d3Y2ZCcGlyM3d4MkpnbHo2UXB6OTF5UFUrMzUyK2V6dmM5WnZudTg5M2ZkNnpvdkZjN3ZPOEpxakJKNEY4bno0L0VuVHJNSENMaUE5M0ErQU9ablc3b1owZUN2cTM2MzIvZmQ2N3UrNzF2YzdjR0JvaU9UZXlkc09NNGtxSUhSNTJQRlE0cGdnVEhZSUVwSTUyTFBtclpsWVMycFlrNkkwaVVWL281OUg2VEJtYUdLMEJHU0k4M0VNQktWTXFtamNCSjg5WUk4dkRpOUxCZ3dtS1NwWGFva3JMNStMaVRTZ2dPSmZNcFY5RjQxVGZ0TXBZdlo5OVcxQ0twRjFQNWNvV2t6S2NHOE5DcUI3UEhkTThXSGhXeW9uRy8xMWN1UFd4Y2NLU2ZUWDg4UmFUUVJhWkkzalpiQXY5S0FkYnFNcitsQVJ3Q29kTVZDUzVSZkFBTzFVZ0xBM1Qwemt1TFR4Y2lURjltajVHcStZVGxzMTJ2TGhLc0NZemlKbEdUVEFGekF6YVZXZGRqWlBDbGlNWFdwV0FDRnpWSnA3R0Z1TFE2T1NlYng0WkRnNWlZMnUrTUEzMkNodU9OTjRZdHVnZ2t6T0xsOGlqQVJyQTZGSTIyN3lZQTFhSS9CVU9GWG82bHpyVHRhWEFFNS91YkFvYnpucmhNMm4yd0p0cStkaStvdkRXMXYvaE9nU3hWUlEwOGVBRXhuYm90b1Q5UG9Xa2g4WFBnQ3V0aVcwQmJyQ29udm9aeXBIbWM0MmMrZ0VValVDZGFxV002eE9IU0lVWFZ2cDlSb0VWbHVHTFJKbG5nS0ZTZU03VmE1WTBXMkJoOFplaWZ0ak5KMWtnKzBvTHI4bTlkVGVKSDVwOVJmWHFmenpjVWdLbHdZOHV1MlpvdCtkdEhyUFZ6Mlp4TU1Rc2VxVWFuODBKV3BJRWFUZmRkR1h5R2tiVU9QV0dzOVpMbTlhWWk5VEtrQWp5WUxEYnJISVZhbWtxRlhJUWNkZ3RtVkVkNlFjNnNCb3ZaL2Q5Uk5GcHFtVnFrUnFZYXNrNU9Ob3ZSUDV0ZzZMT2lkcTZRYS93QkNqd1MzQmJkVDVVQ0E1endHYkRnTjBGTTVjOVJJb25jelZPeXFxVC9RWjFOaU1YbDltSGVkVVRsWlZYS1R5cXNaeWNkYThvZkVlN1RWdXRzSStsZE11cVllL1ZTbjJ2akduelVkcnhBSlBwUFA1ZFVpcCt5b3cvSzJLY240Qzl6NktNOUNOYlJJc2EwVVNQWVUyZ1ZDV0V5eTF2cy9ObFV2UFA4QUhJelBLVzJQcjlTVk45UmkyM1ZIcVVXWExWWVZyN1MraXNmaGFtNlFJaWNtWjh5Rk5QRDJ1ZGZyclEzemU4MUV5V2JYR3hrenY1clo1OXh0VDZWYi9MK1ZYK012Slo2RVYzYTVGV2ZsNkIwdXB0eWlYR2h3VzMxUTJmeFNOUWdVUkhpWlZTcDNwYTV0UWI3eUhjZktaR0xMNXRyZFd3bU9XaktZbDlzOUI0VXdSdWRyMW9BMkZxOUtLSjlkVWVNN0tQVjBzVUNJZ2lRVTVkT1dJcTl5ZWRURGFaaGo2NFUrbkhtL1FSTlpBSTdXak10NTUzN1JSQ1B2QTIzT0dUd0srS0t6ak9ITzFtTm9qY0FhWEt4blVzWi9vamlvZ05FVzVwR2pLZFVpcnJORDgxcVBWM1RjYjROZzhXdDRmTWZlcWZibnJMbUxSbEU0KzRqaThKZUNjSDJuTzl0OXR2cS9RWUxyNi9RZk1uNzdpTnRRc3FJS1RMdm0vd0JNb0RUZlZjRnBmRGZRenRNVk1yQkVscWk4d0taUmtmZHBFWXZCNDh3YkYxaFdLTUhjMmhkQXRoRTAwYThHUjV1bHpwaGpSMEpnb2MySFdpaG1aTXFobFRDQ3BUVlNDamNDc1pvRnNDZEdvNnBJS1YvL3hBQTJFQUFCQlFFQUFRUUJBZ01HQkFjQUFBQUZBUUlEQkFZSEVRQUlFaE1VRmlFVkZ6RVFJaU15UVVJSklDVXpKQ1pSVW1KeGdmL2FBQWdCQVFBQkRBQkcvd0JpSi95ZUU4ZjZKNlQ5L3dDbmwzcndyZjYrRytrL2YvM1A5ZUZSUDhxTjlJOVA2Sko1OUkxWEovMm51OUw0WXhmbStLTDBrOFArMldTWlUrYmtYNDFaWGV2aktpZnV5Q0gwNnpESDUrd2hFejErZlVYejhaTGRuMGxsemxYNmhGeVgxNUpPL3dDMEVpWjZTTTJxTDRxaklQVDNrb2s4ekZCVlZKOUZUcW92NVcwRXdlcHVoWlN2NVNib2d4cW9uci85OUlpL3Y0UlYvcy8ra2ExUEtmNnY4K2tSZjMrTGZIcFZhbitlUnp2VEVlcUw4SVBUazhJdjIyRWFqVmpYeWtWWjloVVpiK0txdjFWV1QyeGRmeWx3bWtyNDcwTC9BQ3RBTkxaOUpaTFNLclVTdFZTOWZwRDRIU0dUaktUQy9adVZoWHVaTWZqSXp3OXVzRUdlY2x5elNHMlNkTDZyUDUvNkJsY3BHbXczdHAvaXh2S255VzlyWjIrWmIrdHYrcG95a3lMOThQcXlNSE9WeTNYZ1l2VElNakZOOEhrZ3JIc0lZaUpWYXBtTDE0WC9BT0tldlA4QTZPUlBYbG4rcm5POU5UeW45MkpWOWZzbitacWVrV1gvQUdOYXhQaW5sVmxuK1hwcjIvMGhUeXJvVlJxdW5zZlcyVXNHcVBWckVkY21aY04zMFZLdEpLRU1nQmtxTElWSlQyVUxiSEJaQmpsbXRRTW1zOWYwSmFHUmNoaDdkcUMrYzJ4K1o4SmpvTGFUS25PUjE2ZFo1UVY0eThabUlBMFN1cldCV2VhUU1ZcXE1eWxTdG83UEwwL1AxWExFQXprRTB0L3FPMHNJNWxhcTZnMGxzVENQYzR6cDZ3eG42enlzaXFsblNYeTcxNkRoNmJVY21XTVgwc2QyQTBsZkZXNXlxeXM5d1JpMHg2ak1DTVpGOHE3UDJWN3BGU1JGL3BGNlIwdit5SDBrTjJUL0FET1NOcVZVWW45K2YxK1BBNytzNitwSGlxclZkUE8zMGgrcEk1WXFNU3lyOVVkbEZmZFZWUmxvWlJaL2d3Tmo5WE5ZNXIxaW8wMzJKak5pMHRSWjlRWGpDVkl2amJ1cXpKNW1Hcyt5QXB6UC9JMHhpWTVQUHJzOE1ZK3VLci9uU3lhRGFIVlZndWcycEZheVY2V3UrMXFkSko5Vi9VYzJEU3ZnSHhPMGR5N3M5QmRIdmtyVkJ1V0ZGTmxrV1BmRVIxWjdZMlMvVkdBbGNtZDVPRkNQdWJudStsclBrcmsyaHh4alN4akxVaWFqcHcyM2VpNlNTZzhzQVdFclJVK21haXo0Y1Q3SGRHSzZlaFdSZkxtK3Y0dEIrLzFNOCtyT2tpcnNWcUo4M3N1NkVtditEV2ZYaGlHVzBiNXNXRy9LU21xUXI4cjhjVExNMlZxMnZGNHNsdVlSTFFuaFJhVkw0UXZnaGNpK1VSRTBlb0FBWTFiTFhrSVdwTlJzVDA3cTRtQm9TckdJRkI1WDNUOTFiTjJYUmxEVks1R0VyUTBROE9jSUhwZmtzVjBzeUhPQWdkTkpUVXNMcGozUlNOcFgwY2dQaitCTWJmUGwxWVZ2WDlZU0pZd21CQ3JlMFpNVHp3V1NLZ0xqNXJHYXpWN1lPVVpyZEJXa1JrOSs5VUlWdjRDUWZWcDN4UVFwcHN6UGVldGpVelRRUk1GaVJWVHlJeUJ2UUtJRGREMDZ2VEw1SE5pS29ybU9oUmswcG5vTUVsaWRvZWg1ZVVMTmU4ejFIRmc0Rll5dk1RbWw2MXNTZGxZOHhrNncxbFNMc3BaUHV1MzBId1ZCczlWbmsxdUlYeWtQMC9MRXJKdElSc3RRNWo4NHI1cW95T1NjajI2ZFYrcUUxRFdaUTdRVjhJa0oxbGhxZEpQWC93QjJyV2V0bzdxTHRKeVBzdmtpdjNTbGk2dGQwTm1WdUswS2pjTmNCVmhVQWQrazJXcVUvWUhSd1dTRTF5M1FHYytsSUhvN055MExSQzFOeGZRenRFaGFtM2tKMWI4V1VyMHdRd2Z6Mm50ZFNoVys5UzhDNUhHVkxVenhBSmRpWnRjdjB2U0RMNGpCNld5S29jQXo5Rmt0QnR5V1ZoSGtzOGQrTWRtQXdiRkxaOXNWMkV5NVk4dmQycDdZY2pENUdWamV4ZFVYOC9FOE9LYjhqTmVabTlaejdFV2N6bkt2eWNZTHBKSkpIZ3FVaXlWZ0tYNVhiSjhiWHdDSzhWVkwyaElXWm5mTi93QTVMbDh1dmxFOHhLZXYycTlaenkya2FJZ2tPODN0WG5SeUc3dW5zWitpTGxSSlJmTkxId2JvcVlpRkV2RWN4bkVnM0l1ekY4YXV3UzYrMWZLRTBjMkFycjR2VVRpWTZrbDkrbkpYSG1ZWjlUZjh2aU5CaHhNbzdPNTl6S3RrdmJVYnRDWTQvWjJiTFdoVzhLMnpaTk4rcU5DNHlQcTUvc3VJUFc3ZXpRM1dmenpMYUlIcGFINVJRaFJneklxQ3EraGNvTXJNb2dLbW1IeURKUjFhMHRHcU5JQVpzdytqUkt6cTZ4VHlZMmc2Mk1pdWpTWkNOakNkVTNJZFMzWk83ZlZRYVJERmQ5UVc3c1d5NW4wNG5UczlMeFpvNDFsbVcxTzVxV0Zsa1lJbCtsWmJjZGVqRVgxMk9Gc1d2UGVzbjdCM3BtM2xxUFprK2ZNcDFqNUxxaGg3Mkh1alFnYXhXaG44NC84QU9MdXB5V2h1bzFSS3IvNWZQNktZZFRmbmJ4TklOUGNVelBuYkdOQW9rNHJtVTc1cStxTGxsU09vRW8wbUJoZDFZM1dpVlNpMlRURUs5cVpvNzhGOStONDdOaGN0TVFNWXdjTllkRU4zSjlrdDhOUlhOT29WVCt3cVF4TXFOQTZzbnJ0QnFZOGdPaW9PRVlvbHF0RDFEOG1zSW82WUczZVZkdHQ0Z3czbnFad2ZUSmtMUjZQTTVITjBpQUszMWtlS0hPbzYzbkdqRVMwVDNLdERTYituU3RLd2FrdW9GcytJTlpZR3kyZFRvMHBPbXRnYSttSGxkamdMOXQ5YWE5WXlaTFI1MGpFcnJReTdTTlFNMWR1MHo4Yk1DMndRbVRJQ3NWU3RwZEJNZUwzakV3MFY5cm9CZUZIYVUvZmtveVd4d3EwVGFZWnB6RUZtMWUxZGxCZ2FzSGxOenBpNmtweStOeXNoMHV4Q0RBbHUrTXp0MGF4dE95VUsxNDZ3YllvUGdjS21zREtZeU1PU3ZwU005RnRYSTdWRUtGeVZsNHloWmUwUFVJRXp0MG5PaEtwVzN4T2VUYURNaU52UGlYYnp0eG5HN05obCswOG5tSjN4WkhZNit6Qklsdk1EcUZIR2FUVFRaMWFobmwrMFNEanhNaFF1VGZmeWM2NEg3YURPN20rMkczeTQxemtyaFRGM0s5WE41clFpZDcwWWVZZlltNkxuOXVtcjE2SGJ5WGRkbHFZdU9zWEpVODh0a05mT0RBZFc0KzVETVJFQ0lTem02clVIS2xxc01JNTNzbGU5ck9jMWRMSUtsS20rSDZjcHErZzdLZ3l4WnRKeXZEVWJsREk1R1M0T2RCaVFiZG5mdnhTRThrSmlGQnI1V200VEtjdVhVc1Zydzhmb2FIOGVJT2dzaXhXVW1MNVZoaUxOV05ObnNqeU9IU2dQdk85U0lYd2c3S1pMTjA1cXVhQnoySjcrV3F1eTBGaTZFeXNjZVJvVng4VFhWN0VQam8xeFdQcFF1akhyNndZT3RCbXdzU1ZhYmZRdXFtbDl3VnExYWxyMmZYdU1hNk1JREd0ZTJyVUtaWmduMlRDaGtEYWpHM3NyWUZlem5RTVpXclJUY21EMlp1Q2hJbUpZaW53VTdJUCtIVHpPM1lMaWhZcm1JeTFydUJibkluZWE5S042UEVaVFZnOExOSkZsdWk0QUZuTStYcUdieE1HQ3N5MTQ4M2V2U1NhRVlKVFZTVUtRTFpIMXFFN2NPcUxIZ0d5cldmdkUyUmZhaDVQWDVmZVVKOHE4UkFhSjE4L3FhYmxMWFkwM3U5bnJzczlNL0htWCtiL1R5OGhLZlVyUW9WVjZYdXhlS29YdFJPWjFCeHUvMUlteG90SjE5NEhrK0lUcG04Rnl6R2U1UkphT1A5eEhSN1VHZ0Y4cW9WOUxGUkp6NlFoVHBCUHF0WEhSeFc3YjRjdGhJSzQrcU5lSXN5TGxEMHJyamJYcnBoRjBaSTU4SzhDZXNMWldjSU5lckkwWG1OeGtmVkdSVFdFOCs1R28rQU5SdlJ5TEY2SkxJUzlvZyt5MzRUVHZvRDlMeGQxQ05ZVnI4TEtRZ2FWNFlScHlMYjU0UFBwN1Z1bDg3bktobW51T0lDZjdrT2hZTS8xUW1iSGN5byszWEJhOE5jTTlQMUdZMjJqNDV4VTNzcnVoVFJsTUJvN2REVTUxOU1qY01RZE56blRoN2l1UnJGOVpqWWVpWldTclgwT1ZqbXZtRTZsejdxZWFQRFJGYjliQTV1N1lIWG10VjBqYTNzWGdaSGpBV1F3MlV4M05yQThQQjlWRzBoM3FaeTJGeDZTNVRtY1BPOFJ6RGxNZEEwR1cwWW9pdG4wUE9IOHpsMWc1eHc0Rk1iMCtyZ3p1QWZMaXNQcU11M25QSjhlTkdXV3pCdHpzcXcwUzVLNkxZdTZIYWZoNHJqMXU3ZFNTdG05aFZzWmc3RTJSRTlkYk1NYjBjUlVmT3FONGVYaU5jZEZFSUZYMXN6RCtkZTRpU2VieERRNmVRcmJ2Mjl6WEtrclhyelN1aDdoYzlhQ0x5K2wwWi9LdmNtbUowMDc0c2ZxQTkvSmUvRzNKREJFeXQwWXlRNWgyVUIyVVorWlBtcWpJUmZXUlpvZGMrOCt2TStjNjNmNW5mRHY0WVg1bFd3UUJPWDNjWFNsbFRSM3VNNkFBQmsydkhGWkZkRjcyL2U1MVkwR1FxTXV6R1lCZTAwWm5mOFBzdnduVnJaUFM3RVJQcGVSWC93QkhidmNubUwwWE04VjU3LzB1MC9KNThoalIyVitTd2g4Mll6QXJYV25WYU5aRXlUS0JmcXhYWTZsLzU5TDNGN1NDZjI1NUhCNXFDSWZCWk0zQlpSb3NKTzhXdlNvcnVWOWxQS0h1dFRXVTFla2F1Ym5uK1hsMnMzRnUxN05Nb1hTWlY5Y3I2YlZ2aUFsbVdmOEE4TDM2eVdUMjZpdFpRUmY0OTdVTmtNc3lsUk5WVWVOOXpIT25IK1ZXN05aZk5uaVhRbzN2dTQzVnp1cURlVm1wc3gxVXpqRDd2d0ovZUp5TitseE1tbENWMlFrOG5wZjV3ZTI5YzFlcy9oN3lFbUo2OXhjbGJ2RGxlYjVuTGJwRXY1RzZBN01QUGUybnMxREtNUGNtNmJVakFZd2lLUFZkT0tJUW1ZNkd5eDUya2Z4eEYxQ0Y0bzUwV1lhUDNWYnBRZUphck85YzlQQnRGVjYxenVOSWRyMDBHUjBtTnpudUc0OVZmTWQ0UllsT2RhNlowVzVOOTlqbkhRcHRibGR2cG9IZld6ak9ublBhZnFHbGU5eTFpOHp3bkNzZFRaNVNiTlUzNi8zcjVZUGRaODZIU29YaHZkQnJ4cXA5YkNlUEc5SDlrSVVjN3hKSG9uM0dWeUF5ZnlrK1RxV2RLenEvSS82M1BiN3JKTE5xY0hQSXZ3RFVvTmxoTDRVcjRkWDR3WU1jZTczTm5US1BobnEzQm1vNTR4SnZoYXFkeDVyZndQWXlOcUJGL0NDSEY2Vnk1aG1LdzZydnFmYVJkaGk1M2FvbEtUdUdISjhZN3RGMGpNcTFBQjRvVllUcWRmNW1qSDZIUmp3UGJlS1FiekZ6MllydktOZU43T0taa2RuTFZCZGd4SGI5eHhnaXpudlNLTmt2enZSSDc1UG50UTNpRDdXV3NESHNOM3ptY3VZQWZEV2pjQmxyUU1YU3NXcGFNZVIwb3pIYi9VNVBFcW9lRGhURlo3Tk5uWmcvYTM3Vy9CSDJ3NjJsOC9FdkFNemZIWUxVQ0w4RDY5blFlRFB0NjRub1lmM2h3QXVuQm5zUDBLTmlKZTkxbVRXcDdrcTJvR29qNm50TDZKR2Y1UExnU2t2Z3IxN0N5aFBjanJhUDFlSXRsa0RlWjJBclo1eDYxRFdveTlQTmU1K2h1Y3RVL0J4dUJxdHRDSzBySHBGWDdWeW1YU1VLSFM4L1JiK3FPSTlBbUZqYStidHpTU1Y5M25SZlRPY3pqbXhOc2xTemRKeUxyMWJVaG55Tm42SGpzLzE3akROZmlKRm9Qd25VNUgwU1BIZXJReE1UNFM4WDZ5dWNPckpMenNvTjFYSE9xeGRFeGJGdUQ1c2ZtdXVaU3AwbmtVN3F1ajVGMWZLOU9vQzhQMXNVejlWRWNwc09ONnFlMXptQ1kxbCtjZDBya3VkMmlJR3RhV0xCQ081Ym1ocXFXNG5yV0k3R1I1SGxtTTZCb1F0TFZudVA0ZHdISTdiRnJMOTcvYm5OK2wvYzF2dVprVld0RmREMEgxNUxZNk5rQmJKb01tcEh1ZmtaZnB6b0JsZ0daMGVNTUtnNnBvNXlKdFpzdnBVV014bmF4ckliSWRvd0xsZ0pkT3BpdDVBSjZXTXIrS3hmbmtCTFBTeHRqU1Z0Zm1DWDRiM09yOGFSczQ1VmthRWVESVJ1aWtIVlh3cExKTXhKVjN2TUdDQ3NoUVI1Z0ZSYkVzTmprbGxaTlZLNjhVQTY3aHJlZ0NmWEViRjNEM0grbTJYVld4TUVkcTV3RDZka290ZmpKbmlOUG1ETVhRTWZlNS9xNElxbWg1VHRyUEo5amE1cDFHazh2emsvN2ZkVmtOTi9NdmhsOUN3VE1uY2owcDc0TkNQWmsraGM0MC9TaE9wR2hkcUhqTFpyYzg1TDd0TE52SkhZY3VTTDNkZngzakVHaTZ0MU90YU5YdDl4c0R4WFZKejQ1ZjBPTzJXZ3M4cDYwQzBqNG56NWJjYy9qTWIvQURQVWNUYVI5KzNKSHMrWnp1cFdKS05yUGJSNWMzZXl1eXJNR2F3RW9qWjBtWnJWekpBZTN2RWsyK1hpTkM3c0V1Mkd3a2g5MlNwZnBPU1htOUlCZTJFbzIwOXNOQ2ZIMjg5cWlJcU9SYjlQWFllSzdXV2RrU3NmQ0xrdlhaTkxCNFlkRU9sdDBJYk1EbUpaZlJZOFhKRzZKSGpOeHp1YW5LazlOeVJMcjM3RG1teWJ0Y3d5eFRqSjNzbDJ6bTFza0VXTVhvczhUdFlqYXRCNk85RkV6cVBHYitwSlFiREUxWng1N0VHczcwZU92Zyt4MEpNeHJlWVhOcjdkamJnejZNK3Z3RnprZk0rMDVCbXRGMnZ3clRyKzc1SEE4UnBZTG13dy9PaU9BRnJIcmc1VzRnNUx5ZFBQOUl4ZDdoeEl0QnYrY2RHMHZ0cXoxTGplU3kvTXh0cW9OMStMdjRmVXRqL053KzNPOEE2US9ubSsvSnM0STh0OGJTYnNNYjlaYXBTTTRQc0ltS2FwY1Nyb0FnUXJaSHpVSnBJTGR3Nk8xTWVxVXVFUFc4dHNKZFNYZHEvcDNncDRvNE9xaUNOZGpvMWFzaURyT2dEUGN5ZHk2TDVyTFVXSzNENnUwbkM5REdVR3ZqYk5SaVpXalVtSWtWQlZaSjNRdW1obWlsakowR0lLYXNzLzNpdC9qSzd4cjBiYWphdXc1OXBlYmRVZm9BRm1hclVPUHpYUzhWRkpMV2htTmMwMzVISDNoK1R0QWlaSVZydmFqbCtpWm4rTkVyeWlySVVqcWVHZUJXbHR4YlRHNUt2YjFtZW4wWExOWEJuSHhiWUdPelkwS1RJMTNuTnB4bVMrVXJhck45Qm54QnN6MjJYS2QvQTRJb2NKVkpzSFY1Ymt0R1U1NG42cUsyU1RxSFNlWVZ0WHo4a3lPK00zK1I2aFRzOHc2d05hQTFnNlRxSHQyT3ZySkhMdCtZRWNmak9zVE0zUE1UNjVMY2hldmF2RGs0QW5ZUVV3eGMxMElRYno5VkNTVmRNR3VETXZmNXJZZ3I1NkhUZzRnWTZxVGtRZk5hR3doVk5VTGtGeWlTWStXNVZ1emlaalk2S05HVzVJckZWeVd4ZjdwYnBoN2x4V1JOZlVySlZoenNaaW9UZElIa0lqNjBVc3NaV09XTFRNQVIxSnI0bVo1TUZ1cWVSMFF4bEtWTEYrbFd3MWJtV3p0b0V5TTh0c05udEx2SUt4WmI3TTJWQ1pEU0JkNHlxWjNQNFFwMjA1bmZvdnoyZFVZV3M3RGttMXNoYTFyTW42R1J1R09oNWZuV0lnRmJ5NEdtTWJjcjAzY0FNcEx6elNaaUhPN25wOVlYMWNGa0tlc0VGOVV6R1Z1ZSs0UXYxSXYyYzZKWm9PZGF6aVc0bjFPREpObEFrQkdGOXdlUmVzMVI0SFhVK3A3M2hlaWd5SFVJbTY3S1o3TllmVzBLbXZ4VTB3ajFReTVlL25LOWN1eW1XSFhlUVVzZnQ1Mlp5MWM1K2F6Wi9ZNXUyNnhmR3drRzJOUnpQVjBuV21Xcm9VbGJHay90YzdPYU9PVDFXMnZYY2hMUDhBZUdHblIyWjJKYldISGpCQUZJQ3QzY3dXNDVZN1lCV3Z0Yk9sV0ZUajNVNVpSZWprekdkeGVXTDJJTDBOa2Z1czZOMU5BdFF6azg5aUc5WTIyOXRwaEExR3NTcFo3b1UrdHJ4R2hRZ2NESjRMdTVPL1BXemV5RTBCWUhIZ3NabllMTzdLVmRicGM0eXArc0xOekw0Q2txVUJYdVE2dDBOYVY5b01TTTNPSHdSSHFOYlBHOFhTMUdyNlp6enJjdmJobWVGUjVjSm5lODc4QnhPNVFCR3dJNnlaMW5OK3pkazdsVDA1UFlpd21mOEEvOFFBVFJBQUFnRUNBd1FIQlFRRkNRTU5BQUFBQVFJREJCRUFCUklUSVRGQkJpSXlRbEZoY1JSU1lvR1JJek55Z2djVmtxR3hFQ1JEUkZOam9zSGhJTExSRmlVd05EVkZaR1Z6ZElPVHRQL2FBQWdCQVFBTlB3RC9BS0g0Umo0emp3UVk4V2EyUE0zT1BDTk1lTHRiSHhOZkhnaTQvdW9qajQyQ2o5NXg0elZmL0FISHhGMi95R1AvQUV2K0xEQTQ5YUZmNHZqeW5pL3l2L0o1ZnkrSng1REhpY2VDNDhYT1BkVEhpZjVQY1J2OGhqbEpJdWxmcWNlNmw1Ry9jTURpWjZoSUI5Q2I0QnNJNlNPU29ZL3dHT1ZROUVLYUg5cGhiOStQL05jK0UwZy9KRVdPUDdMSWVqTXN2MGVVcU1lOUxKVDBLZlFCamptYTNwSFBLZm1JOU9QQ1NHV1kvV1NVL3dBTWVFV1YwcFA3MUp4L2RVTUMvd0FJdjlud0dQRTQ4Qi9KNFh3T1NEVWNjcEp1UDB4eEs3WFpwZ2NFcG90cElmbWNEK3Y1aWRuQ3ZtV2F5ajY0UEhMK2pGTzFVNDhqSXVtTWZ0bkhGcWpQczJZZytaamcwZ2VoYzRISDlVNVpFai8vQUdXWnZxK0Y0aXBybWY4QXdndGdjRERSYlZ2cWRYMXRnbXdXU1ZZZjhJTndmeTRKNzgyay93Q0xRYjRCc1JTUVNTMzQ3allNTVc3VXNRUmJqd3ViWVRld3FxbFFRQnhKREc1K1dCdzJjd25iNW91OGY3Zmd6WThRdGhqM2NlSVhmZzhBQVRodXpUcTJxZVR5Q0RmamljenpPRVZGUVI3eW9lcW5xY0ozWjV0cEdua0JjUnI4c1I3dG5TeGlRTDZzYkl1T1VqZmFrZm1heUQ4dDhMdlliWUJWOVdheWo5bkNidE5MRWFrQStidmFOZnBnY0tqTUp0dGI1QXBFTWNxSElvbWlpSjhQc2dvSStadmpnbFgwbXIxTXg4OW1MdWY0Y3I0S2NjcHlOS1NGZkcwdFNWK21tL01FNEFBTUUvU0NXdWtESHhpcGtBZGZJbTR3SDNWRVBSUllsUnI5b1MxY29zRGdFcTFQVFpqRUhCOEFsTEEzMUpJdC9MeVZSZkI3ekMyUGlmRnVPckhLTkd2ams3RCtSdXpESHdQcWNOM0tDSFhNUjV2YmRqaTBLemJXZHo4Ym00VDk1eFIyOXBua0pXbmh2d3ZhN1NPZVEzazR2OTVQR1ZpK1VTbTM3Ykgwd2kzam9vM1JtRnQ1TmgxSXdPWndlcUtvZ21QOHA0eWZJRWVlQ2VyUTBhbVZWUGhwQjBMK1pqNllVYmhJeTFWWjZBa2FFUDRFeEh1ZlB1azFVWXFSRDRndWQvb0xZUGFwZWpkR0tDaUErS29jQXNQT3hHSE8rbnlTa2JPTTBZOE44cjNWVDVqL0FFeGI3enBibkx6eXNCN3RIQVFCeU9rMnQ0amhnQmdzcGlqeVNpSjM4TEFPd1BQaUNPSUhIQ0FBMUZIbFJyekduZ1o1eUZBOG1PNDNzeHh1QmlvOHdMaEJ5VmtvNHlDQndHc3F3K0xBNHBHY2QyT1BlV3dlekxNbTBsK2d3ZS9NQkVvK3B4eldFbC80WTVyVHdsYi9BRFlqQTMrMFpuVmF6NjZSWWZ2d09DVXlLcWpCOTRSdmc5NzJKU2Y5MDRQY0JrVmYyVlFEQWF6TFMwNS8zbjBnZlRFaW1SSzJ1ckZsRzE4WkxnS0xnV0ZodXhHMmw1a1pwWVI2TnVRajBKR0hkUVlHdTRuUEVYMGkxZ2JXVGh6T284SWlIYkxkbTZKTW81VFNqZVYrRVdCeEVoYWZNUlRMQkZZRGdtNFhHQko5cm1lWmtTa0RuN1BURWdOK0p1cjRoc1VvNnRWbTB3ZUNrOEFxQUJGK1F3aHUxTFJTK3daWkFCM1dsQXZLM3dvQ2Z3NFJMeVpYMFpwL1oxMCtNMVFidWZyYzhyNHFIMFJUQ2o5dnpXb1B3THZJUG1UNmdjZ3dEWmgwcXIxaW9vWCtHSzJ6YTNrakQ0c0NNTlM5RGVndVNtc3FnRHdCZVFTTENENUtua1JpSUhZWnRubGZSNWM4dnVnUFVDcW5hL2t5REhORmJVY0RoSlZOWmZwZ2YwZVcwdThlckFFL3Z3ZUllUXp5ZnNwcXQ4eU1mM3poRCt3dXR2NFlBTjJra1dBZldWcm45bkFiZkhRYmFvSHlLQlI2MnVNQTNFMllKSEg4emU1d083UFh4bHIrUTFBNEIvN3B5eDZoZnFxZjU0TnRJajZQbU1OdjVGeUxZamlNVDA5ZWtFTHF1L2NMNlI4NzNHSXdDWmFMcEJUeVJTbnhJdVI4cjRpQjJWRkRtOUp0cDdBN2wzZzRvWE1GWjBZemlqaldzSlpleFQ5YjdRbTUzcndGamJCRFRSWkZXWmROYWxYM1dLMkVqZW8zbmxpZ2N4MTJXMVYwbGlrNEFWRUpSRnN4c1UzdGlhaU1pVTBVNFFpNjNXK2k3QWcyQjREMUdDb2VOcVFNaVZJNDNFbmVVL2hzZkhFZTRDdGpFa1k4ZEtzQnBQbU1OSmVwMk9pUGJlU3R2Q2dlbHppMW5xMFUxVHY1Rmcxd1BTd3hTeWlXS2xxVTJMQmh3YXpteitsOEdZU3l6bkxvYjFGandad3ZQOFY4VTZCWTZYTE9renhVd1B2Q0prSzN4ZmZGUXJ0MnY1dmNSajZ0Z2J5OWJJWjMrbTVCK3ljTHdwYVZESVBraWRYQTRWR2ExQWlRRHhJQkErcllmY2FIbzVTYXBENWErcU9IZ1d3YkgyL3Bybmh1Ky9pSUVzVDZDL2xoZTkwTTZHeFpkU0FmKzlyU290NWpGeHJwczcvU0ZVNXpVSHk5bXk5ZEFKOWNFZmZSNUxEbG9QeENXcllTRWZYQzhJSnVsZFhOSjZhS1dKaGY4d3h4VkkyekhhSW81QjUzQU9KRHM5QVhLNVdBSk4xSW5tMUVuMUdJNGRWcTNMc3VvWG1iNFpJaTZrK1ozWXVTVG1OSmt1WTBFU0M5MUZSRElKb2lSd2EzSEdVa1FaS21ZWmRsK1owZEw4ZnRVRlZITkZ3dU5hSEN5aHk3d1ZsSFMxa3FtLzJHWTAwa2lCcksxa2JIUnZMNGpERFg1bWxQbURLb0FNOGtVa1R4eXFRUUF4WU5wQU5oYzNUUzFkWFJVb3JLRis3czVVU1JYaERFaTBsaW5nT1lwRUR0UWpNb3hNWDViQWtQcVRkYlNXQjhSaE9wN1l0QzlUVHgvak5OckZ2VlZ3eHRKQlFWR2lYeU9rbFhCOGl1T0lqemFJNmYyaU1EK3NaZE9yMkhwdncrN1JWQnFhNS9FRHBiNW5EbTZzMWtiZWVPdU1yZjVnNDdJcUpRQVBsL3A5TU4yY3FvQ1hhL21GNGZ1d3c2bnRBRXRYSjZLT2VCd3pYcEhXK3hVbzgxWHRuMEFHQUx5UjlIb1Z5bWhBSHYxazE1R0Z1YTJ3aCszcU9pZVQrM3lwWWtFeTVwV25aeCtaQndEWXBtVmZWOU1jd1UrR3lqS1VpSHl1Y0FiNlZxbW15aU1BZUZKUXB0Q1BKamlicW9hU2dwcWVUU1BHV3RrTDI4R1pCZ1NBQ2tmcDA3a0hnU3lVU0lnOUJmRmtVUEpWbXJYV2V5UzlVOXJieHh1UmlCelRVMjF5ZkxxaEJwN2Q5SzY3QWdnY04zTENNaVZTMEdiUVVsbmRTNy9aU3F5ZFZBUnA2eHVSdUdLc05HanlUMDlIV0xHZzNndEdnamV6RUFNYkErZUk1UFlLYW9hbHkyR3VqWFMwczFwWUVDVkNGSTlTNnQ0TERldUFwaHFLNGRHNEtTYlE1dXdrcDFrZUtaUWlTTWtnQko0QUcyS2pNbm82YWdPWTE4MldRR1daVkY0SldCaWVOWGRpVTBqU0xrakVrY3B5bXZ6UTVZS2t3eG5RZ2lyS1lIMm9hZXJwa0FJSlFLV3hESzAwZEJTOUkzcloySmZyQ3J5K3FoK3dtWG1JZ0U1TGltdWtCelJxL0pKWkV2L1J6dUdwNVFmd2dFNDVUOUkramZ0OUEvbkhtVkF4TWZxUmpuUGxGYW5TM0k3ZkZFL3dCdkN1RXVaTTUvUmZuTFFWY0pIRnBjdG5JUHFCaUs0cU1rejJrL1UyZXhlSzJZQlhPT011VGRJMGFDRndPSmljblp1UE5XeE1MUjkvTXE5ZkJGSFdzZnlyZ2c3YlBjMmtFbGRWa2NkRitKOGxCdGcvZWROZW1GeTAzaWFhQTlaejRXM2VlRUgyblNmcFcxb3o1MDlQZTJFR3RjKzZhdWZabFgzcWVnV3dJOEM0QVBKc1FzRmo2VGZwSXpIL2svMFdwei93Q0hvSXRMMUhrTUl1K2k2SzVFM1IzSWsrQk5BRTlSK1pySEZyTkhrRklESzl2N1Nwa0pBOVMrSnB0bnRzMG5semFzcDVtc3pLUWJMSzRVRG1VVytJS2Q1RkM1UEhUeEd3dFloUU5JTjdldGhpbHBwcXUxVTdNem1PTm1BS2dXVDF3RVF2TFJGMHVXYTdFZzd6Y0tjU1BVeUxIVjVlckViU29FS2tHM1ZHbUxGcWRkRThBbGlEUExKTGEzUGRHUGtEaXE5b2xrUktjaW5KZHFXUGRiZUZzeHNQUEM1ZlV6TEZCSTVFWldnbVliTjIzMiswWTNPL2lNTzVjeVRzSlFwVTVybzFwZTBxQmtCQ1hBYXhVNGJOUk9YaDZQRTBRRU5SQTM4NGlNbldsWmhFamdIWjdFbzN2QTVYbnRYTlYwSFRDZ2hGZlJoNWpyanA2eEZhT3Naci9DVklVZ1dZdGlXZVNPT3Zpcms2UjVITllrYWFyTHBsMjFMSUFMTXFMYjNTUmhXTzN6MzlFK2Q2Tko4YWpLcGlWOVZBeEU5NTgyNkpodWovU0NsSS90NlUyamtieHVOK0tjZHQ2VTVIMHN5OCtKQ2FSVVc4VnVjVTI0NVpuUldnNlIwbzkyS3FBRzBZZTdLTitLTmcxVjBMNmJVeVUrWVVoQjdTaVVGSkZIdm9SaXQzcExNeEdYVVA0Yi9lVzhSMVQ1amdvdXdrdCtwc2pVY3lEMUNGOFQxZkFIQ3JlYXBxUHM4bXlnRG1TZHhDK2Y3SnhSdlo1cGx0bHVWdU81REVlcUNQRnJzUENPNEpvcGY1M25HYmkyVVpBM3ZTdTMyY2JEam9HcWM4MlhGTExzS25wRFZobDZQWlM0TmpUWmZBdGc0VTdyeGhRZWJOaVloSE5QUlF4d1VGK0VLQWNHUEVuZWNXNnRKU2RXL3J6dzJYeVZJcUpkL3dCbEpNMmtXNXYxU1dQbUJ5d3RORUdYUUJ4bEExajAvd0E4RG8vT3cxbmZKWjRyL1FkYjVZS3hXZUkvQklNV2xVby9pbGRKcVA4QWlHSURTTWJpNEFTZDRtK20wR0taSjFaTGRUVnNZcHgvK2R3UFBGWFE3QndodU5EaVdsWStscFlXUHJqb3RuN3pUeGJRNjJFRlF0WXFFSGtZM3JBRlBPTnhqOUdmU3IybkxwS09tQ2UzTlZTTHNKakVSYTRhbnBuUTlrcklVT09sUFEybTZlMGZSVjVUVXBGbWRaRHRLdjhBVjg4aEJkWTMyd0srNkN2Yml4bmVaelVsZm16d3RTWlZWVllPcEtETkVabmdOUXlXZU9vY0lab3pxRWpFRWlqZ2FkcTJrYzAxWkVpalUwc2NxWEV5RHRNakJ4cDNrYzhTSUhvZWwzUjJNSm1LeG5nOGtVWkt6anhhRWx2R1BHamFVM1NUbzROam05QUIvU0JWdHIwOHloQkhOQmpmSFM5SnFTWnFicERrTDhsbmRMTXdIaXd2aUVnNWJuMlhFUTlKY3JEZGhHZFB2bzI3SllIRUIwOUl1azdvZE1hYzQwNEVzUllCQnZQa04ySVRldnJaV0RQTEx6THVQdkpqNVhXUHNxQzIvRkpVYkxNTTRwSTlNMlpTQTIyRk5iaWJteGNYdGV3Sllsc1QwNGp5em85U1NNb29sYzZWa2xaT3U4N2syV3h2ZGpwc2J2akkxa1RwVm5sR2dwcWFvQ2ZlWmZSYUxCb3diaWFaYm1WN3JxMEM3cEl0TlRHbSt5cmE5YjJCWjErNlErQzhzUkFpb25NdXBwYXh4ZDNQeUZoNEREYytKR0tubzlNVkt0L1NyV1NpVkQrRzZERXVWYlpEZmpzcEVjajltK003eXVyeStFM3NBOGxPK3orcmhSODhleXBKWStNYkJpUDJkV0pNM0kybmhEV0pkVDZDV0kvTjhhM3BwdC9ZOW9XOFpKOEZxSTFHS3FFVHdSRWNLaEMwZ2ovTi9PSThWMnFHbnJIUDNNRWcwTTM1VU5QTjViQjhkTTdHTldzSWhtY0xrR0p1V21Sbm1pL0RXSTJLdU9ISU9uOUFsdzlWUU1BdE5Va2NwVlNJSmZ1ejBhZS9qbzlPL1N6b0ZtZEYvWElWdE5tdVhSKzhzc0pUTVlJL0VUZ2RzNDZmNU1sUG10REtnYW1xWTMrMGlzZUN2REwxNDI3U1hJR09pVWxxWFZMOXVzQUpNYktlSlZlNmVWeU9HSjd6Wi8wRmxPakw4MmZ2eTBnNFUxUWVPa2RSamhaR1hNY2t6UUdHUXlwdWtnbTV4ekx5azRqZHFMSVF5NVlTT2tQUmJNRUNSMXdGN3gxRVE2ckEyTnBGL3dBaUJsMVFhZnBOMEh6UWsweVNrMmQwSEZONURYWGMzTWNHeFgxSDg4cVkrckpUeGpyVFRNUjM3WDM4aVdQRkJnMHAreVEyWktKYmg1RDhjeEJBOEZQbmpKYUlISnNvZ0FFVUY1REJHNUE5NXd5SjVKSytNc0RtRXlkbVdwZFNyVEg4S2txZzdvTzdIU1NvWnhUMHFCRWhvSUR2QVVjbWF3eEFkTWxSR2RNZ1BBMjhEZ3M4MVhOSzVabWtrWGNTVDlNYVRiSFJ2cDVYNWJWK01jZGJUeFZNSHlMUVRnWUxHa3JEcTRSU3FZblB5RDMrV09pbWFxWkI0U1F5WEh5MUpnVEptV1dKNzlGVkx0VUh5VjJqUG1oeFRRbWtsbStCaUhnbTlGY1J0NkU0em1PU2hyREtiR2ttSkNsL0l4ekJYOUM1eE5VeU1qOG9LcEdCbEE4dFFTZGZGWFlZcG5hVkJhNndUb1NYalBpbDJQckhLY2RIQWhwSmF0N1MzaE95aGtrUGlOMUpNZktDWEFSOHA2Y1pCSVJITWxTeDBzOWoyTnU2SzZ2d1NxajhKUmZMSmt6TDlHdlNxRk5NamlPUm5nS0E4SllaTlkyUjhab0QzY1YxZWhNS0VtSG9sbWM1M2JMeHkycWE4bE5Kd1c3d0d6eHJmTFlnbVU1eExJRFNaelRIc1FUdHczamNzaDNNRHBheEFPS1oyRmZrVlJ1cUtHb1hlNmdIZXluaUQ0WXJab3FEcHJSUUFFM2E0cHE5UndKdmRDM0FucXQyempKSXZhWllLWWtSNXRTQmRieGZFclJqV2g0Z0R4aXNaOHVpT1paZEFYak9ZMGt2MllKRWZXRXNFckJUYmVBekx3QnhIR3VXVU5RVGZTWnBBck9ENTMxZk00TExEU2VFY2R5c0MrZ1VRSEF6Q1o2Y0h1UVVkT2tGT255RTVQcmpOY3FueldvUHZXQTAvdmNZeWo5SCtXdFRnOENaWW1uYy9ObnhGVmxrWHlJdmo5VG9ZV0hkYlozQkdJSkhpY2ZFcElQOE1kTE1nTmJrQ25ubStXRnF5blFlY3NRcW9SNXlERXlYUU5qcEJRdFNWUlBjcklsQzZ2enJvZjFMWTZOVmp3dUgzYmJMSjVyMzg5ak81UDRLandYRXRMc2FoZUlrcDMzS2Z5bHJIeUl4VnpLVEllenRDQ0k1VDhNaWpTM3hBKzlqbzFER21iYzVab0lqcGdyUU84OFJPeWw4VU4rZUpZaEJYb1Jxc0VHNmRQZk1ZMzI3OEpQdVlxWmdhOHdEYXd4NjFzSnJEN3lHU002WEhmaklQYVRDVTJ3enpJNW4ya09kMHVnRjZlUzNiY1JpNnVOOGtZUjFPdE1VY3Z0VTBjUTJtWVpUVktvTFNxRjN5T0FvMnFMOS9Hb2xTMGlFWXl1amFHa3pQWkNwcHM0cFpoY2gwNFZWTk9CZDR1OTI0eWtxNFFySEN0Uk43WFZaRnRUWk5uTzFoVlVrbkNPUnJheDltK2laYnNJMGJKODlXWnJ3cURaSVozN1RVNVBVV1k5ZUI3TEoxYjJvSjNnei9KSjZmU0twRys4T3k1eFREZVF2WWtHdERZcmFLVkpzdHA2d2dWVktkWVpvQ1R1a1Fua2VaUGljWkIwbHpGS1dPclc2VWNkVE84OENrSHVGV3JJL0pvUmo5Y0x2SEd3MEJmOEFFMkJuZE1qK1NGS2NBLzRUaXVuek9pR3NXKzAwUWtENnhTZnM0YkpKY2xyZjd1WXhDd1AvQU1sUEl1TXVvamtHYm5uYUYyRVJQNUNNWm5UamE2ZUJaZjhBVEdXdzZJQS9HV0h1a2VuQTRrcm1tai9DL1d4bE5kRlgwRTZqN3VhSnc2SDB1QmNjd1RqcGdnenpLWUY3TkcwamtWZEY2d1ZBbGp0N216UFBGZXFGWkR3aG5YN3QvSVhKVStUbnd4bE40c3pwSFhkVXBZcEpISVBCbExJMk5rWk1wbHF1MUxEdlZvWlBqUTNqY2VJdnpHS2VCelJKSWJOVlE5K0JqNzYyRmo0aFd4UlBya1pvU1JVUUM2c1hUdldGMGxqNWp6Q1lCUmhEVHpYbnllcTdhUmhoeFFrNm9aT0JVNlRpRldocEo2a2FZb1ZadXg1VXp0dkI0MDhoOXc3c3huTVdYMTB0MWJMbjFhOWhLVjNxRlk2Z1J2UW5XbDFKQnIzVnEramkzUTVxcDYrNEp1U3BBNjRDbTBuYmlON3JpRnpWVFpQUjZVblNZblZJOUtMZ0NSbUYzcGlRa3BHcU14eThZaEpCUTV2b0FnekFOMUhSaElCb0xkbVJKQll1TFNxamRmRXJHV3A2TlZBZVVXVVdkb0wza2prVWRxUGUxdUcxUUVDa2owVmVXR1A3ZWp2eDJURDd0aDJsSDNMMnNOQnVvb2lqOUZzMU5VcUROYWVZTnJBM2x3RjB4U0FzTG8ydGNaelNqTE02cU11cVVsb3ErZEdXUjJrWGdKTlVZYjF1ZVp4VUtkbXg3N0tTRWIwWUNFL254bThCa29MN3J2SGVTT3c4VEU3RDFTMk15bld1cG9oWUE1akFHMjhBODVVZVFqeDFqSFNLWDlaZEhhdzhLS3NKMXRINVhjQng1bHhqTzAyVE8zWXBhd0MwY29QdU51RjhaTlV0Qk5ET0xPakRkYnpCNGcrR0tLUU9qTDJYSE5HOFFSaXRSYVBPS2J2ME5XdkJXOG1IQThEaGw4TVZWV2Ezb3hVeWJsbzh5dHBNWlBLT3BWUkczSU9zVGNqaU1sR2ltV3pJUWJGU0R3SU53UmhVQ1ZxRVhFMFhBUytvM0szeVBqaXBtMjhjeUFuMktmZ0pCYnVrQUs0NWl4NHFNVURxYXVLTVhZRmVFMGZKdDNFRGN5bjBPSUVFMmEwZFB2WlRheTFrQTd5a0N6THpGd2JFYnBsSzFWTEpkcVY0bmE1RER2VXptNXVCZUY3a2J0UzRvV0dsWmlCUFR5a2ZjVEhnUXczTEp2U1JTTCtKb1l0aEpSMXhaVUtwY0JTZTFzMVBZY2RlQSs4bkNkeFRoc3hoRWt1VmF6ZEVsQTR4TWQ2c3AwbnR4c0d1TVZTZTBWRkhIT0pGbmo0blVlRCtVMXI4cFJmcjRRb1hxWm9tamFhVUN5N2RRYmxyYmxuUTdRRG5LdU5qYW16cDBWNnFqVk9ETXk3cGxYa3dGaU9VYmJnc1N2SDBteXFxM0JyMzJjclJFU2JOK0lMRFVyVzdRdU1ETW8xa3plbW9aYW9Gb2lUR3dXSUJsa01RdElyS0ZrdHFGaVd2bWRmUTVqUlYxWE1CQWdsUnBFRk1oQVlxQkt5Rm4zalRwN3VKOU5IV3lMdUtSSGRESWZsZUkrREpIaW10VXhTVTM5YmdKTG5UOGNiWGtVY3daVnhPcU01cFQ5cFNWU2RhT2FMZjJsTjJYMzBMSnhGeENEVTFBcHdSRFdLRC93Qm8wbmlqRVhsakhXamE1dGJVRldML0FKc3pPT3hGWW5FQUhnelczMjU4UmpLb1JDdGRBNDA1bENuQ0tid2RlQ2sraHhFMmlXSjFzeWtjY1pyVG1tcnFPWGRIVUE5a0VjbUIzcXczZzRnblpJMms3ZW0rNi9uYkhHNEZpTVV5cit2SWd0dHYzUldLUFBjSkJ5YnJjR05vK0JOamZrUVJ6Qkc0akV6Yk9TRjk1cDNQZHZ6QjRxZlVjUmlQL3FOVWVDamY5aTU5MDhpZXlkM0E3cU9jdG1OSERHV2VqWW5ydUkrL0UzZmkrWTM4YWVQMmlzeTJuSWQ2Wm0vclZML2F3UDNsL2d3eEpFUlExUmpNbE84SllncUJ4bHB5YmhvN2E0bU53QWJxMEVZcWFldHBYTW9VTHdKa1crMmg1Q1VkZE9EQWpoUnhHR2xyVmpRUG9mallOMUpJbTRtTWt4dHhVbzJLaVVQVHlVYnlQbytPblk3d3dIR0ovdEZIQXlMaVJOY2RiUnFxTkF4OTRBV3NUeDNhTDhRaHh0Z0lzem9sWXkwcmpnVHZ1amdjR0RDL0ptNERNQ1dycVFyOW5OckdseTBKQU5qM3doQkIzNkFlS1plWmRlZFRUelJSbUNTMFRJVkNobEt5eU11a2xuSFBIUTdOVGxzRmJYNVRTMDlRMUUwVjBWQlU2dElFeGt1U05UYThDRmtEVEc0a0JYdFhIRUVXSnQ4TGpGVlZFNVZtSnVYb1h2Y0VFZklrRDhhNG5qRWs5TFNtOFZZaDYycVBUelBhMGpuMWszM1hGQk9KMEtWSHM5WlJUZzJFaU9CZUtTNHRyQUtQYXpyZmNJRHFrUXA3TUpEeWtNWXY3TkpmZnFqTFFzZDRNWndxamFQVWxraXJnT0czQzhXNUNkQXdQZUI3V0hBSmtlTlFsU1BmUjE2a2dQdkE0M1dLbXpERkZGZFpHNDFzSTVONHV2anpHQ0xYMDRqSjNNdDFkU0NHUmh6VmdTQ1BBNFp3a3NETnFlaWM4STJQTlRZNlg1OERZakVpYVpvWDdNaStCL2lDTjRPS202amFXSmljY1kyUGp6QjVnM3dGMDA5V3d1b1gremwrRWNtNWNEdTRRem1XZWhwN0NhaFp0N1MwL2RLdHhhTW5RNDhEWnNiVGFtbW95UkRXU2dXTTFMSUFUQlBZZGFOaDVNcEdLdVhWUjE2VTVnYW1tN0xMSWQreW5IQmxJS1NDeEZ4WWllOHRKTEJBS2RxYWZpZkVSdWU4dTlKQmlTWVVrck5Cck1iRGVFZENTQWJieDF2TkdHR3FMK3ltSXU3a2I1SW52WXNiRUVIdGo0eGlyalpDNlVwRU5RUmJVRHBGMVpTZDl4Y2dqZHpLb0o1dllJMWtvYXBibTBoaU81dkJocFBEZ01Wa2lLY3hnb3BIcGFoWmx0c3hMR3dPemRXM01WSlFOeE5zZElUWk12aW5qbHE2TjZjdEtUSWhRRXZZT3BadVJPSVUydFBzMklNREEzS0h2QmIvQUprSjRFR3hqR3lpTlVvUlpqM1NqZVBNV1BtaDVZbFl0bzdiMHFFN3lmQWVMQWFEM2doeEdOYmxEczJkanlsWG5maGNuZU84NHhUdUJUZElhUVBzQWZlMXFOVUorV255QXhPQTBGVWdqWi9VV09pVDFRaHNYTEdDT1k2NlpqeElWK3RHZlE0MTlTT1ZnMWg0SEVSdXVvWEI4aU1JL3dEUHFJNzlpVDNrUE5ENTd4aTNhUUFqRlhDWUt1bWt1b2xRbS9IdWtFQWc4aU1QVWV6c1pXdEpETGJWb2JrYmplR0hIeXhJdHBxZVk2b3BoNE10L3dCL0VjaU1TSUdsU056TEpSSHZSeWdkWUFIZzVGaU9lSXdVaG1wSXkxUlRJU1RwQTRTUmdra0lTQ0xuU1J3TldxdThrTWpHbnpHUHRSenhzYkJUd0t1QnFVM0I1akZPb1JxNlduRzNVY0F0UW82a3FIaHFzUnlJR0tzcXNPVzNDd0ZqM0luNE9wUFpXVGVMZ1hQRXZHc1ZkU3cwNFNQYUxjRm1ZS1U5UnlOOFE5U3JvYVdtRWtMb0RkRGNBb29zZDRBeHNRMHFVbThPYlhFb3VHR2tqM3ZrY1MweVZOZkhIZXBncFZkaVZsTDZHV3p4NkRvVTN2ZkVXV1VnaXpCTXBkS0xMd1lWN09tSXBvYTViMFlpd3hVa3h5MDVxTnBGUk02MjJNSU1vQmpCUFZOdXpiRWhFOVZsODB4Q0ZPQVljL0svYUh4akZJK3pGUkhIWm9aU0NiYWhjRUhqNEh3VTRrY3gwbGRIS0duVUR5SnZjRC9Sc1RneVJ6VTBSaUk5NjZHd1BuYTErWWJFNDJjTlRJdDRuNVdaVFowOVZCSHc0bDY4bExReXBOUlZOK0JhQWd4TVB5bzJJQjl0WFpIVmV6dnArT25uSlUraXlZWVhrSW9lb3g4U3FrajZISEZCSkU2Zy9VYnNUSm9uak5VRkxyNm14QndJbWtGRFd6cFp3b3VRc3FrZy9tVmNLU3JhWlZOaURZNGttMnpVYzREUmlRQzJ0U0NHVnJicmdqR2RVNzFDWmE4b2wwUWhyTEtKQnVzeEI2aDZ3NTRwcDBrRnFuWk13RFhLNmdiaS9ERmZPOHFaWm1rYXdNR1lsaXF5UkIwUGxjSmlHdUM1blRtWDJpeUI3U2xGRmdIdGV6S1FiODhSTzU5cmdwQUpSRmMrK0xucThkMkk1eEtsVjdIS0xBQy9WWHNCMVlBZzJHSmtra2xxSzU0b0NaQ2pGQ0JGZmVXMDc5T0k1Rld0V21rVll5bkE2aURyZmQ0S0w0UXBCUjVja0N3VWo2T3FvbGN0MWh1NGJsSGhpajJjTUdTMEVHbE5rcWk4YlBZZFFIVllDNUF4RlRRK3dJa0cwcDZZTW9JRHNZMmRsREUyQUp3YUJKS0NoZ3Bnb3BvdG5vWGdqTjFpQ1F1bzZRYlh4Ly9FQUNjUkFBSUNBZ0lDQVFRREFRRUFBQUFBQUFFQ0FCRVNJUU14SWtFUUV6SkNVU0JoY1NNdy85b0FDQUVDQVFFSUFQNEEvd0FyL3dEQ3g4Yk0zOFpBZC9WVCtOZkZ5NFBuY3NTLzF1V0IyZWJpRUhOZjIvVTVKbTVsdEthWWlVa3RCQjhYTGxpRDVxV0JBU1pqKzI1T05CUHJzMzJGbmJzY1lNQzFDVTlqbFVkWnNaWjk2bmpMQW1meHY0cjVzU3hBSm9UT0VuOHUvc3h2N3MxR2hrN2RZR3Q1Y1ltWk1ETDdEQ0FrekdZd2d5d0pjeWdMR0FINDhCQlIrR1pWbVR0MEZBN3NuckF0QWlyMjNJZWxJSk84Q28zWTlDekZNVXdRQ0VDZUk3UE1pejY3bjdSOWRvQVIyUXNHQzlmVmc1VFBxRXpKak5tSWFXZ1dhVUF1d0wyY2orSTQ4ek1VOUJHYzdIR0JxQmQwRlZ1b0tHaXU1Z283cmpFK3A2R1JNR1VvKy9HREgxY3pFeUpnSkc1ZHk0Tkc1L1pzWGNGSHNEMU1oMUZLNDBLUWREdTVrM2NMQXk3bU1Mb0ptM29aSHNBQ1hDVEJqTW9vSjJTMXdVTmxUZTR1OXdiZ0VPNVdxQmZWSC9tZXJ4Z1krMUlQUUptZnBTVi9LeVBnQXl2MFpVcHEwSzZKQUEwTEpqSGRSaml0UzhVZ05MOGY0UUlTUkRaNy93QVgrbDMybG5vc3hnME5DQ0FTckV4QmxEMWpTeEJVZUlvcURiemxqOVJ1aFAxQkNObU10UWo5VmNHNHRRYjNLUHNkd2Q2UUQwZ3ZvN01JSVRiRFVyUUVVYU1ZYk1XSnBoT1VSdHFERHRBWURhQm9Pb1lRcEZrb2pRZ2lmMmY5QkFPeVN4SUNxQUlQS0JRb3Rsc2lBWDB5MEtEQ0VhRUhSaEZtTEhHSnVNUTZ4TjhjNGZKQ3M0VFlLbFAwUnNFUUFYQ2dxZ2VNa1pJR0lGZ0VVV0FwQmNZL2dvVlNBczR5b056ajhuTEZtc0FBUWp4bFRIeGxWQkNLYU1Jb3ExUEdhYWpaNHVUS09NR3pIWXVFbFRrQ1JWdzJZakJnWTVGNXdmOEFOcmc4Q1p4N0pNUmkyUmlIeExRZVBFSW15SU5FenNTb0lZRFJqYkZ5d1JIR0p1TU5XRVAxRm84TGQ4VEo0SEVnaTZKdmhhTnBjbEp5R2xETUxpb3BGUUcwMXhmWk9IN0RPTUVvUk8rSlRPTDdRWTJtaUhWUTl6cUV4aExOWEx4TU5PSnhzRk9KSVBFMWhsWGxYTlZKZGQwSEZRUDN4OGh2ak5FaXVrNUtXd2c1SFFobENxTG5HS3RaeGFZcktIcmpOV2hYeEpVaHIwUWFoM3VlcGZxWGVvSTZ3TVZNZGJGamljTU1XVUhoYUdnYmpEV1NuSGtGR3lQRmxMQXdvV05xMTBMSkhHcEFhME5obHk4bHNPc0J5MFVJZnhadUlzTEN2KytNZ21vZEVpTUpkN2dzOVZxUHgxT0p2eExjWlUyRU5pQ2hxRXR4R0VnK1MwT1JadGRGTVZPVVFCeWI4bUVGTU1TcFBHYUxBL2NxbFhFQXNRNWd3dWI4bG95aXdsNjMwYkEvWUZ3cnFOeHpqYlZOVlM2RTlRMmh1RHkyQTZoYVAwNjhvcldhQzRycUVaQzFWZ2ROVGNmUXhmWURNdjNMeUFpbXBDaG9LQjB1UTZJSkZnN0VCQ3dEVmdVSlMrcUVOaVhDRGNMcDBHNG03QVlLS05PYWhZZXRpRUhqTmpUd0ZrZ1ZIMkFqVkNtSnFLWFUzTXVKNWlmeEQ4eVhGWm4wTXdld3dxVW9BTURDWGtkYnZaVi9RQVViV3N0QWMzS2FoVlExQXErVkVqSFVMTWV2LzhRQUtSRUFBZ0VEQWdRR0F3RUFBQUFBQUFBQUFBRVFJQ0V4RVVFQ01GR1JnYUd4d2RIaFlYSHdRUC9hQUFnQkFnRUpQd0QvQUJvVXM0dWNvWXpoWm92RWZaR3A2bWcxMkh6SEhDY1hZV3Y3TEZ4UXhpRUpjeDZDSHJMTHd4Y3QwV2pGRnl4ZXR3bzRoaXFXbEZvV28vZzhpd2h3eFVPRkROUjA0akVPSFRjV2c2Rkh1ZXdxa0l5T2xsanpxZm5IdHlISHllUmN1V1JhbHhwelBzK3o3UHN1WFpkeDNpOHFPbkpZeDZycWR6dVdmV2NIWXh5dHFjTXlqUFNGYmRGK0dQNUcyRGJsN3hoemhtVmo4bWVoNC9KZ3VwL3RJNlY3UnRHWXhGMHk2OUIzMllyN3FObzZuNU9oK3VWaHptanNZUEF2RzV2Nm1IZzNOcThVWW5NWFIzSGVuUDhBWE1tVlhpckZQQ0tNVVBSbG5HYWNWWUdNWTlLTUZuRnhhcUhDaDJIeU1tQmpxczVaZENGRzlPUlFyMGYveEFBcUVRQUNBZ0lDQWdJQ0FBY0JBUUFBQUFBQkFnQURFUkloTVFRVElrRWpNZ1VRRkNBelFsRXdVdi9hQUFnQkF3RUJDQUQrOGYzZ2ljbitXUU85Z1prejV6Rmt5d2h1UmV6NWZqanVEL3h5SjhvV1FkaHMvcmw0em9uN3Q1L2lKQjV6Ti9qUGwrVlA2aTh3dmNZZHoyVUgzK0lUZW9mM0QrZU9PZDBIUU5qZGVzZHRaNVhpMERrK2ZkWVB3dmRmWi9rV2xDY2hRVUVObEgrdzhtdGYwUGszTk11ZTlWK3d0WW54K2lUOWZ6K1V4TUNFcU8vWUQxZ0h2WkZqZVI5TFlXeG0wTnNjVU5XR09iZmRXbkNleTZ6bzFOMjVhcFlMQ2VsUC9VS3hTc0ZaYnIwZ2Rtdi9BSmxSTnhEYUJNMnZBamZlc0xVS2ZsV1ZickFsMTFWVU4va1c4SUtrVExXRjdMRklRVU5ZWVBIU3NaZDcyNnIxWmp5YWluY1V4REszeEZ5ZXdxelZUMStOUDJzODd4cWhEL0U3V09LVlA4U3M1S3F5L3dDUnZTZXc5RlhLdDV0cDZYekxoUDZxMW9iTFdFSkpPRDQ5Z1dzb3RqMms0aFVCQ1dDWitUZTFqa0lsUnNiSksxaklTdXV5MW90UDBBR0RhaEVzQjFnMHI0c1RMeHFxVkg1R1BocjEvVk4xVzFqdDJQWVpxMzNoQkFWK2cySjdSQzVNRHVCbVozZ09Cd3VRMlo5NW5UNWlldHYzVkFBVmd1WFhRVnZTYTlRUjQxYTVRRWJCd0xydHRvYmxzUHl6bUJQc3RaVXZCRnJrZkVaUFlFQUFqRXdGUjN1c1VFOG5hQXFQMlFqdUp5TXhBTWNoUmpJY0Joa0ZDZmlwc0d1R0lvSzhaMFBBc2IvYXM1NVJIUDhBc0xUMVd4WHQ4a1FBSHRRTVRCamtEaUFabnpQUWJKMWo2Z2NkdEdKQjRKMUdJVzFRQ0JpRUFoeWNDTU5qeFlvWHV3a0hKSUp6UHFLQUlvQkhOWWNqS2xpUkJuc0tBT1NvaXFSTlJqTTFRamc4OFFxRnF6S0Z3Y3l6OWhLa0UvWmdKWjJaWTN6ak55c09OMW1Cc0pZcERuRjFmMkdHZjVjamtKbzNhcXdHMHhCbUlPVEU0SEZTTXg0dEpac0cxYlVwdzlvQVhsbElyckFyQjliU3hlV2xVcndIUW00ZHgrY0daMnJSb3h6VWp3OUhGd0I1Sld0Nmd6K3VsOHg2OEhqQjdJK0pCS01vT1Njc1RBb3dZb1VESkNDcGRyVUZqSVlCbmhicXZYV0FIVVIwT2lST0ZZVFRaNHE0Smxpa0tjRnZaZ3hNbXVVSUxhR1VlSy9zcktHbjVLVVpNdldVaW9xdU1QUW9RcW5vYXl2ZFF6S0lwMVhNQVdvY24vNFFWb1ZDQ2cxcXhzUGlzTGJtdHN2dEJxQ3F2SFZpa1VxSnJQWHRWTWFtVm5rU3hSWGRHWFJzRXBqS3hEcStUVTU4YS9hWElLTHZZcDczbGhhc2k1YkNqSnVHMlpoS1hEb3dXMnhIL01LejZYMWF2OExsQ24zaEdaaTdRdGlvc0FEVDRxeWtiMktKMVlSTlExZUppVmpzUzVjaUJzUEx2eVZiRElkT1NTRG1XSUY1bFlOeUZUNGxnZFQ0OWxXYXlhblZoVy9yZHhaNGRtVnNWR3IzckxiNERLdGpzWFNyeDZTZ1U3RjZnNkp3Qkt2MUV4bWdyTFFMUERyY2VLUjYwc2wzeHR6UEhzRExySCtMa1FXYW5Nc0lJbGc1aVdrRE1jK3Q4ak93NG9kVCtObDluaTJ5MnBia0YxU3Q3NjVoZklUUXJlVnpSZTZ2NDc1SEJ5QlhlMVM1V3V1N3lLU3JWcFhVbXpKa0ZsaWNrcE5WWUVqeGJGdzNqdlNUUzdWTjdHUHhhcTBxY2kxZzRGZ0ppMmJmQ0U3Y0VaQkpqalhncVNqWUxLUnpLblc1UFhaU1g4VjhGc0kzc1c1TjE5bFJ0cjhoTmJTWHJCUmxJQkdBak81WVA4dFNMQ2xDRkZJS05zSFVzdVVETFltWXczSE5ESjVBOVYxdmh0WW15SlllbThabEw2bHdhM0t5d2Y4QUN4c0c0VWx1UVYrTXNxSTRQak9EK04ycGFrNUNNSFRtdGd2eFp0L0diYXV3cGNkMFU3S1JQcm10a3FmYzBJSExSVGJZdXhWcy9GbE9rWldCM3JWd1JrTFg3UndXdFY4bDdIM1B0UXFZQTE2SEcyUnlTYTIyVURIelJkdXcxWTFqVllsTnVWOWRnVTFIaHVGekJZeXJHSE9RTW5KZ3RVSmdpcjFqYUlTelpXa1ZnbXNzbVJrQW43d1ZHVkRLM001WG1WK1VNYTNNdERVRUxvb2I0MUcxRzJWMVoxTmlNUVJ5ckNzbUtvQ2JoQ282TmRaNVVCWTdNT0R2OWhzbDhLOXRXdUExRHFNd1lBMWdENEd6TmtqVXN5bUhOY0dHNUFCSEkxUitZbE5tQVpZclZObzlUdldkbGUzd2J4bUdzNStGZC9sVVpsUmU0Nm9iUWV4WUF1c1pLMFJXbGJxcEJHUmE1MDVEWVlyWVNkUUF2YWMyeEU4bnkzeENwU3dxcjFXQjlYZW9vZFFiU2YxLy84UUFMeEVBQWdBREJBa0VBZ01CQUFBQUFBQUFBQUVSSVRFQ0VFRlJZWEdCa2FHeHdkSHdFaUF5NFFNaU1FRHhndi9hQUFnQkF3RUpQd0QraTdrSWdORnRMY2ZsWEQrVmxtNjFEZ1dvNnBuNG0rQXJObld6OG02ejNQVTl5RnZ0UG9lbmkrbzF1UmE0THQvQ3lZb0RpUGNmamxtejh1eXpQNkxEZW0wKzNjdEt6cVEzYTJsbFhNWWl5aEwzdjJLSmE5SzRsamE1c3RSODNDamN5WXZZcmw3V01jUlhUWXZTdEE0dmlTc3JkOWliMTlqZDVKQ0prdmE3MlRMQkpiajhxTGJaWm5wR01hM0R1VU5MSmlLWkVoeThvaFIxbE9BNDZoZFdKOGtOUnlxeU1OeGFMTVhwRkRVaDlYY3hpSkZvYjNGcTkzVXlHb0NndEdPMGNGcG1XbnlJcDUxTFVZYkNRbnNZOTAvb1NXdVk0aWdPSXI1bk5uVWZJWENIS1B0VUJlYUN3VkxXOFVSaUZBY1JlbGNUamY1c1YzTkRndGZZNzh6RFZmaU90OGkxMTQzeUZ0US9VdUpKRys3N3Vud1c4Y0ZvVXZzbnFJYzJuMTVJZE5CaEYzWXZyZGd1M3NqWEFYUjdqczc5NjZvbXMwVHUza2xuaUkvWjhCSngwS1dnZXdVSmMyT05PWXNHYU9wNU15WE5veFhROHdNREFrb1JsbVBiM0xUbGc2clV5MUhJM2tubWgrbDU0Tzd6UnI1RkNkcDBXWDJTV09XcFhVNjNaZFdaR0tQSUdENW1QWDdLMmZPNDZUUEhkVkh4dFlEL0FHc2I0RmNWbnBXVE1lT2g1UFNLTmwxMFB1TDFXR2VhV09IUlo2Mktpa3VCUmN5ajVlekIzVlJRMWRuZGpKNnloOGJWMjArTDRQUHVPRnRVZURXVEpXbFZZcGkwV2x5dEUwK0sraWRtcHFNWEJlYmpQZ3BJeFVUQ3l2ZGp6S284UlZjVDRzcWpaMktZTW95ZGxrN09XV3JSb0hCcWxydm81Qy9aZkpaeDZQbU9HVWFyUVZzdHJZWm1mWXdiUmxBd1VIc01mYmliU2pQOVdSUjBkMVQ1SXFpbVk2NDRQV0xaMnlZNDJlSzdjbU9Md2J4Uk51c0tSTURHaFcxUC9wVlcwcGFtbnB5Sksxd1pWZXpaZHQ3bEh3dTI5MGJ5YVpRcVNlWk5FMXhYYzNycU9GcmVZbUhGRlZORkhYUTgxMHpSV3YydXFIKzJEekhHMHVPdlNTWTVPK3FyMzczVWZBMk82bmxMcUUwVnk4eEtjaDdUYmt5eHBxM1FzcVJWRk9SdFdaNTJlbWpLN3Y4QUh3RzdOclBQWDNRcDg3dmt1S3ZweXVjbWIrOTFDaFIzT1k1NTVqWFFjSWViaE9aaDdNTWZLQzlTODJQWVdQVlo1ZGlLR2JVSW81TkRsUVpOY3JtTWNJblloSEZDbVNXUmFXQmFoN0pNMytUUW9NVzZYQnk0amRsNmkwUWFZcDVQdVdSU3lGWEFSVjRGTVNaVXM3N2xPZ2tKU0ZNLy85az0iIGlkPSJjIiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIvPjwvZGVmcz48L3N2Zz4=", + "description": "Displays the latest vibration telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'vibration', label: 'Vibration', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Vibration\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 3.3 - 1.7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"square\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"waves\",\"iconColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":2.8,\"color\":\"#3B911C\"},{\"from\":2.8,\"to\":4.5,\"color\":\"#F89E0D\"},{\"from\":4.5,\"to\":7.1,\"color\":\"#F77410\"},{\"from\":7.1,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"36px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":2.8,\"color\":\"#3B911C\"},{\"from\":2.8,\"to\":4.5,\"color\":\"#F89E0D\"},{\"from\":4.5,\"to\":7.1,\"color\":\"#F77410\"},{\"from\":7.1,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:dmlicmF0aW9uX2NhcmRfYmFja2dyb3VuZC5wbmc=:dmlicmF0aW9uX2NhcmRfYmFja2dyb3VuZC5wbmc=;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMwAAADMCAMAAAAI/LzAAAADAFBMVEX5+/r3+vj+/v71+Pb7/fvz9/Tw9vK22cLE4c/J5NPH49HA3su73Me528W93smz2L/M5tVKdVHD382s07mx1r6v1byr0bfB4MyrzbbB2srD3My818XY593H4NC92sevzbmlzrKgyq230cC42cTr9O7c6eDv8/C01L/p8+xIck+z0b2Ww6O21sGox7KYv6SizbCjw62Zxaao0bXt9fCbxKjl7uicyKqyzrx/qIqxybqtybfV5dq51sOw0rumw7C508Kv0LqkyrC/18eTwaGjx6+cv6esxLTQ5Nbh7+any7MdKiCqybVfdmbz+PXl8emnzrRrmnZtk3bL4dKfxKtdiGZ8rImSt517nYWDs5DR59i/3Mno7+rd7eKjvqwYJBrf6+Nun3qSvJ6WvKKLopKbu6WXtKFsjXWSspzi6+WPrZmfyKyCrY5xjXpriHNXbl4lOCnO4NSfwKqBo4t0kn1liG1nl3GMq5WEnYzG3c6DqY4gMSQPFxB9loV3loBxl3oqQC6nvq9Hb02WuKCKp5PV6NuWsJ5ZeWCOuJphgWpokXGfu6l+ood3m4Fyo35znH1wgnZmgW6Hq5F2p4IKEAvY6996o4R6kYFTdFuGr5J5qYVTfluFo483Vz0tOTBNeFWPvZyLtpeGt5STrJyOs5qds6V1oYF3iX2auKSInY9hfGhac2EyQTakuauRv56LsJaAnIhfjWlHYk1Ea0uKupdneW1EXUo2UTstRzLs8O1mjW9CZ0hDVUiRqJh/sIw9UUKqwLKIs5Q7XUEUHhW908Rxh3hikWyft6easKGMu5pbf2M/Y0VSbVlMY1JrfnFZhWK3zL6wxbeEloo+WERQe1hMaFI4STyPo5aAkYYGCwc0RzkxTDV8i4FNcFVSaFhmc2pIV0xWgl7D1MgmMSk8TEDK3NFrg3PT4NduenGYppzH2M1bZl69zsJOXFJgbWR2g3q3yLzO19HEzcfJ0szd499TYVefqqPh5uLT2tW+x8GxvLWtt7CmsKnZ3tq2wbmTnJaMl497i2x+AACBm0lEQVR42oSYz2sTURDHd91kDyooVaRJN1ZjdhuqCdHtGk3UtDE1LbJREqw/osQWwWAv6S1eRRAEBcVCc1IpxZO5eOmlUChI/5BQ8Cx4dt7svszLvhVH64+3b7fz2e98Z16qqMNQxBAXvD2av43WaFm8T9Pgt0ZbIxF+gcK7FIEYPkwbCZV2YEgboxi6H/gf11VkDkVliwEO/nhapbWIAAJrPFfaHJokPScM8hD8ol2UP6buB39eBG+AUEZT9NloRU7Ow9BGOFQ5WbikSmoQBV6SryIGhgQhEPi8UihDmpBiU/9bbMRBOlEilChdoDtEfMxNZBgWESdACWQAvAvvge2cQc4bY0QOeZEw6LXTW+cZYIRXHEJ4JeVhuh6D5WGEM+B+2Az5W6l0zWm3TNMwisWiEmp9Pw96+5xFyFh67ZGRZAUHD5+J6Y3WCW5g93kQNlHIEPgkwLVT6SYQIMABBoAYptmSOCSV0DNDkKAiHoVcV4F1XiyapyAvD05hWbZlWQxDliLCGXSbMaAMfpitttNMWzqyw01hjgiIQl6gEvI7APqcXEDZCkJFMWQOxLCtFARwkBgcgSCsbU4B0WIAzW1GoIrUYvuW7R9YCnDwViZVltiKyLy8rHiKqEbKthEDKQhDQGUUDAMI2sDAEQ79I+S2Fe72AAhZXXY6CUIGJnMgB4gBIb9cpEAxwBZNAIFgDFYYg1yJfvKhxqGuzVGIQwnWGwkSHAgeCHFsbzMOvBjMBgXbRg74go06QUQwBCJ8JuiXSrNuUKzW44rcySA/cUwPVRJXZIsMC0v36p+DULOybLCHLXFQl2UUKEUzvQ1acEXxW/I6xQVVBYSmAwTFziBevjaVH1+sNGaySshwCQ64kRocndochJxORhZBbOxWwYpHjig6fFhSHgZvxPhdOBXstdJOywCCWLmUzE/eXsxUKo1G40WlMrc4np9SgiGf/QgldByKXicri+MD7SH5A+5zdd/jKAdXwyMEco4OFHq6bRYHsb3pQia3Np9dgchm13KZQr4U61Zft5y0Dc+SYAJ+kLoZeYSmtmCRQyIIG4IQcFERBYHbsKxYp4JgeoA3YANiWBC27qrIpQKF0YklC7n5V5u7G6sQG/2llbVMfq9bNdtp3dWids2ol2crMyvLcjfzDwBEQlv8FQEkIAlNBwSxqbJIEM23LIwNNvRAD58D+KjFKZrlGJ1y/sb80peN1Xv3er31K6sbS/OZZKxjOik47+vpVjWeLDSyy8sr5y9ePjt35xSqQCwI4i1QyUlNAF+8aBIBJGARsbLgmm6lawsM4zeAMEEAFNd1b7/C7K03zfpeYW3Zp/i4tdV7ufmssNcxHMtlJWcCZaaRzWZnGmCWU9enLlybOH360aPRESO3ANE7dA7QwkkQUAcUW/Y6KAIgeBgxzbaDHLiMegw5HKM7nVsBDMaxvvX+/frL5zeSAzMdVRXXdoxBaTw3zyAWx2cRAigg4t16lR00FQpRooB3iI8k8Se75xE6LDLnChx+xjgMDAbS5IXF/GHbDJvJE02b9VIm+2b3wc7OvfWtp3ffr3/4dvWX0YyqqmsBxl5+MQdKAMU0UpTL5W69aDxusQcXO91yiSsgaIJfZHd/7FAL1qTRzgkZR6giMBK8g6GzzUFgM3C4Ktrcbhdjk43lL7sPHuzc6717+n69nysdtKEtuKl2cVCGOQIQk7PTSaSIdTtGy0nZKcc8qMcBKgYRj8cFy7PgXERClhc/UOJKcFhCbkgigqRqC4+NIjulgyS6b3X/dSBHtGl0p+ay+1+/fkWQd73+WqnoMG84RmJidnwRC2rqGtgCIIpmzYYOlm4Zg1gMIeCv8kRpevJ2peEn6BscuUYOz2LORALLxEfFRSTYnez0ggkgnYMD449jaQou41NwI3PI67HrL27u//BB1ldfFQYtS9XshYexacBg5gYIRlE1HduFggOMTpwzXEMIaGhvvuyuXtnwJ3rYsZnePj86kihc0GiQhCRpwpg7QEmgthRPErzfB6kZiam5m59/AAmAfOztgiA1F/R4eHpycW7uzomTFyagQ40lqp8WbBcMbbeNaiIeiyMGOwBk2PDpb7zceJ4rFHJPcmsKkowmT/8U/EBzEfcKn9PhT0YYdIlhsI9/Dne7pysCK1qqVY3NXv6+DyQPdnq91aXb8ZauurUiG34vzgDH6UdHxsYSx40FW1OYHMV6t9uNMxBUIze/stn/0H/25Mmtq7e/9T/gJK1wCuLwW0DYpyuWM7G43rpkExsbl8ncrqv+cSSKdsINURgTE+Mz3/f3GcnOl2y+09ZV24xfn5uZOXvpxLFzR4+MJRKHq59qugoCPn5dTyQSQBJDa2Ryz5Y2P2zeuPr2560n/ftXXvaff1u7UchPJ0tJRWSAoKOYQOIGf2w3tE+gc6mcBCYi1hYuIi/KA2b4S6i5f6VZx3Ec0xDwOlRkOAQSg4kiCuIVB4EDUkDACy6WYo6at3Lq0k5jzZHKvJzNI3nZUmzzdFxpLlJjNeeq1bS11madU2c7W2fthy4/9gf0+T5KD2qXzy9zezg7z4v393N7f3V2pLIzZicRiQVATC89CSC7tRKBND0zLjQ8kkqiUMgAogM9rM5oEwQTDldiAU+uNQsr5ua6WLkH6w519ruBo6uoMC83Ozs7L/fbn3/9/Hf/qdmyZ+EoOn8V3lkKcJJATQAEWglWgfElBPspmKgfNiXS6pUbJFM9imeS4WhVM+gCZQYHgYQhDnLVMMoczXA0GQWFSd2dmCrTqoQVKxUtB9PyuvqgkeaMvlgnKs5j5aUV7s+9uv/33KtX9+fmZgfUW39LCdlq623hwB+hD+IkMABz9S8hv2QP6opY5QoE1XE1TmaBQQIkk0DiqWX9ApJw1SnmWYGUHRMaHk8CDnL0sIaICYIFmUI9kJRCU0hqPXNpeYdEde7BaXfnXEVLWl5eceG7p3IvHPoy98sv38jL2/9G3YtnKo2E7XYcvn/hsxjOsfN8wZsCycdZGj8JyvidJKYEunRDk6kb5n3q54KD9R1a5SwmSSQVQIDEKibyrSDI3yAyQ71g0lOhgjfPgQGtYa5WVMzKLhbV1dW9ewji4P68tINw4uy+xtbW1sYmwlYQvOzuNIVxywhfTdDGBEsfNqkgEu4OkmCxPlnN4GmxPPFOSuiMZHGweE+BeXI2I50WFRFPQocpelgvBhAKgEBQqOGpMQqpsscjFLWkVbiHykd7hCpWSgpLVVT07uG6U+ffLcwrFnWdbe1ts9tPN74Of7b2HzkLMJscuAQ7fUs/B4qAvWWzCr+EWUC4JoGnj6sfVjNS6ZwMpVIgoReoIbG5albGpFKaHhMVHk9BJORhPnRJygYJmRQZGpMuUXottSxRi6283OJR1tN4STy5qKW4qKKuqy6NxSp8sc++utraerqx1W6vPHn4xcOfXrjwwlt4am/v80iLrRhEXBW/KODLISfIP81vQ3kShid1RyKPBpOVvMCUzA0K4pvoys008ZOIucNkEpBUgSQIJGPWa+lRFYs85VemJiXs2IjEJJlCq4JhWZjGK2AVdRpXV9saG1sb7ZVHD506ePDq1Tfev/DVm4cuvLmVBLdQt3LgogAIXoeBBTwIAAmQBH8MJSG5ykRNTOWlJnTka8DW5qvlqHIZIE1QvlNI0VYuf5iCSJAkEXFsKSjiMYvotnKLVymlhUaGhfNo8jQt3ayQJRXQa/t8Zb6mpkqj/cj5U4VXc7+9ffvb2++/f/DLw683np5Z32HabYPBW+aOaVgHiwsXNtxgnCTgKaBYnWom9cBuhsn5kjgo5ElrQr0AEj6QRO+kkDYOVzxIIpicumKji8wrFu+sNDM0Mj4+Ik6WSqdpDamR4XJhjrGs/5jPYXSM1hUWp32773Zu7u39+08dbm1qnbl5/5Plc3f8Q5Z/ygpk2bp0bvvedRsfR4jbQDDN+FmAYmKSmKYqKz84hCCukmcEkFCiNUR9FYlEiYbTRYkPpXEEN6YsQhbdZpmazDBERZJIYaGyVB7PQAulxoeaR0tdDofD5Zs+b9Zq01i5uXlpeblphyobT5c0X1o8vrT03vePlh9AAcBBUGyxYwJ9t22qEPFtcrt3BKJokhEK06SG4YrwRJDeRJNy8Dyp0hD5TkQCQQkLzcRIzMUym+WGID0mIp4CRSAiNSkqMzSMFEmzjdUMGisHfdMes0IFucTKK5SzWs70N440N59bWjq+9OjRe3+UlJQ8fkzAL6YCvbt/Db/zAAYrYsFgA51VInLnrHuqMBQ0XhFCEAonPTM2IoxEhsBIKH4SpMmk1yJJYUm8kxns2HCYAuKTSAeSomJBwwi2d6Cmz+i4Xtog4NQLFUVForQUecsRu7H760uL5xaXlj5ZXr74TdPpma/vNz94RMCzGytT/x1oZEQ2MPIcIVmCA/1bzJDh8pEogGIyqZ3W556EkpfFpIEoqAxjJLonreSNPKFAnnBQ6bLxCiQ3lFJDVBiFTIk/QCGHQfshU0I5lpppt9s1P+CRSmdtQmFRvYyn6jT6ers/PLe2du3OpUvNvfZjjc03b958sLx86eJpgn82+X8QzCRCtzxgECHTDRMSXwXEQIIsU1DlFZMazhc3OCQkWMOUp/ubfPSwWKff7CdkSG62ZNY7NZuSwIKuk4lIyCQSMEZEUqJJURLLwNige/760I2MWY+nwWYzxGnnHK7u3pHLe0+cuLe+3mY/Vtnavb5w8+adhZHHH7z6+g/vECD+ISu2BHCgVBBz9VnVm/2RzwUWLPwgXD6gaKwgS5W6ygnLbRCGImPTYpMwlCp9sA7r8VhjjILjNev1apkFEkQSCWJRSFVOcmQYJZocwZkacOcMIhKBt9yTs2KLiZW4XdBf9k7cGr870t3mODrXvzoys7BwZ727rfXTt05+evJk60eQwDs5trZOdITADtZr/nYlnuOK/UUPUDZZntMDC/rEsDWLD+M/gWilyhHKAdRH9nAJxGEyRkIJQ50RlmXJM2q6QGqAPIF/h2l5OD6eHE2OZN8YGuubRiRKS7l7pVwZJ7OVtrc57u4quz5ftjroOFN73gUk62sIxH701ZNHX2gsufngwfHt4y0hYBX2B0qFrGo/Cgxg8AjDRDA4jV6vQQGP4RsK4ucXGNgxgEJlksl7xJsoZBi7YmF8nJ20pZrC6qXsOMgOIKzSWKsQUnycoDzH3Td/fcCrtAy5x1YEcZme9kGfa+Jhe02725fTKRSeKfPdW712Yu+Io7fpxZOHzzY1LSz/+dqzz3/2iBBYVVEfRPcn/I+56E5rI57eSAW/KnyU+FuuMXSbxh8XAh7C/0gQWxlyBTuGh1AoZCeXoEsGFGiUB0Jj2FLB7KxWrZZL06HDA4E6ephrjUakEenesR53+3zNlMALTA222BhPu2twfvzWQPlQjnvUZpC424wjI5d3nbjXu9p/tGuuceb0nYWll1977e1HP/5RQvCfLADBXhviOT6iwQK7NMGNYfjWtxgXEH9babpNn4lA1Jh4YEhgKFSoz3pCkMYElYwJszAtXSJQqjqsHSpOJlbgkChEK2gDomRYPBXT10uvCARXchoaPLQoZanLZxyfH/NaVlY8AprKbSwz3jsBmnT3V851dbZ1f72+CH1m+b379y8+Lll4QPAb9uCdZmGXhwADLEgaf1ojsx4VsKd3DpOIBgu/I6jTOxPoKq1BzktKZJCg1yTrCPwqMswCYUmxmRyJICPB6uRxDDIQBaE49TqNGkSJpCltXedLS8cmpZPlDT0r7CjO2Hy/r2x+bPaGcEUiiWM3DLa5du3au3d1xNhnO+/2jdwbObf406WFhcampqZLJZ988sdxKM3+mzQrdpeLWNB5QYFoEA/AQYsMGDK3173Nn4O5VmaKVoG5XYkMKrDkc0N0ydA/sU1LJREU5+s76ApYY8IABVW4IH4VHL/wTKW5qKF0wJIh9VpGewShsZ52o8N3a0iZIWiRSOGvpYNt44Byt82VI+xxlNm7P1y7dm19pLv1bGXvesnCwtLi8vpbBJTeer99mgwsG0cssALgRiXKEnzNDAzM0gOPFfMfwfBiMDqYTDhhhCwTk8pILJDRVRJzgl6fqDDIUhOR/WIiqzWEICualg0Kedp0TfkkR+rtqbXRwhVj7Y6c+QElO90g58SGm6ddxvG9ExPjZUa3UujwrZbthVbT3WasPH9mtXu9efGnn+40N3/3+BgBiQIVFfsdh2qkihhB4MYG3oZguERzDHDunJORRad+hkWns2Q8QNnNAJfLVB0cws1ndgBKCl1lVnWIsxK09JQCQAHNmNFWXQjRSaEm0lP3qQaGLIJ0qUcozIgKFdS4Xe5Sb2ZMnCwiJjLWM+Zz7b07scvlyrEJx2AF2DV+d8Le2zlXO1pmH5lZPIdIXm88+8FMMwGJku9HeZrot/a3pwYykbDmr8niEnXblEEWUj4jhSaXpfAKEAoDUNC77sFQeCy4Pt0tFjOKAQWeUqkdTHU1MYTAj6aGyximorHyKQlbajPXa8Nje2o6+6YbOKEwNCSGx9PLc4wPd41PTBinezijLmPZ3bvX5139nTZFhcve27127dydmYv2I4f7Ry5+9OcjwiYKtvXiu9e2QZiIHEr1K6+8ogb3fceeH4Suf3ZnA8k+hAKywAELBhTMuHsqm64Q0ZMJWfvoKfsSEKgpXwPzAYHrhCW0Y4+o/EqP2VAvFKXRw+JWpjsroEuGUaOdVEo8p6HfODH+8OGtwRyptNTouDXePjSWI7QZ6qeNbd2XL6+tjXzRdKTiWPfMzH0Yn48TNroHtvXir4/fBOjQFAMgTHRzwPhFnbyTBVCr839JeAp8emTLM0z51bBV6vMBZTfyIOkKVXF1iLgj+yl4bsoH+xK+Hy64HIm8FOu+hlFbPU2hEsllYbSpwS7RKCeJQrZaoVTX9wDKrYfjxhwPe/K6wzjuKp8S2lS0OI/bZ/zwxOW13tW+zoq+1aaLMz9dW1z85rfv/iLS6oPSIMM462OgoBIo8o2ChiEgOhoICMj48shPGgbBmQwldRaoKXSTGDK/IBsdps2uLXXRdpu2vlerraO0zUor213duvKyXZ7d1X/92/Ni1nO3Ozd0vr/3+Xx/vweDXLKvVe+LkSXQXfYMlQZlC50hFICswAiYYOa6d78U7BM08D5GfmvZ3t4Gzz1Zci8clbm9xwsjfVukl9Dr73uQ2eJTPgwoDz7uVPrQpyoqp81/o10PRUNGFeC1Yy+mOO7G3KxoQ4OdWFjrXvr8uzc//zo+KyU/NhEfgMomzac05lB64qGbUADuDISC1hsDvb2jt+7M3Dn9zdCJwT7M/2rcBzAoovrMZEaZYMlkkgkTMCDha6mN0Dh0LWgS/ldMBcz7UYn+niGeQSGGLx8EdYj+n2qiQFhETAzkFcwN9z9p2kHaEIo+qlXBdNskWi2XY6mhCdqPzvoEWlLUDG9pIq5KutD89TNNTdNjjZSjTVOhZ85W5asI2NxueTz06quvPjDQnBBXTjUPDLx1ZiIUH3F5U12dx1/HIBLlA7QpAIP7doCOrgyVVXiQ+IDlpTtUAIVKqRE6WpgPQ4BkMsSMEi0TnYfQM2jPEIFmZpq2aRnLaCfILRIw7sNo0Djw+F1AiIyRBi283cL0+DlcelohcHDFPUc9JYcaSkrMSXjW5Uv1wWeef1k+W1lQdXSqb1pegc0msgrx3TeCE6+eOfNLq7y9aKy5r/PmL1M9Pa5qmcjfOnfspS/fx3wAOvuODn6xloqMQoF4wrIdqBGgXsfmCYXQAH17TsHUNzB9AXRSlQYMCXItPhOYz9eCsn3fGKCf/AvF7/fL2g6A+w6Z9j5B0orFH/Gut0ksaWVAIaCprLPyWam5rA1wwAXyrAb/80dfG2svx9b0PBG5cTkPl0TtqHhsaeK7N998MzhWS+4JxTrPhM6WW3UBDd8wuHJt7ZUPb97E6AQWiVR8BKy8vFIKL3VKgZCAZ2WZTPAwyVi01JwZ6e+H5QEUP+ioGu6eASIVJP6+pRkMFQNw7kPxGwwGi+/gvfUH7sEwGWkwQKIAbX8p5VMa06VKLlfHEC0Ee2wiBj2AIiLArrWJbLPtUr01L+/cEzZaLcmJciib4naHvm56YnpJTKYclY/EJsYayQ6Tic13+13DH0IGXV2+jgEF7bW3v7h8BPYCDhdXFFUV8PDZ9mjSaW5oaCgrgx6ayYR6BGRfewMkAoFMJhAIMnj2DQEU7H3EQWlvNbgjfp3yLk3AkelKMUz0DQhJZCnhSm9VW2glOxpG2mILymfFHMaexurQuhc4NjHfYuXzxC+/zL23DBgruz0r74hktunlx9ptVizlsWnRwmNFWFYyScQJxRx16MzEtDwW3tpWYF7LAAEcFY1V5PwcKPDOBmiL/2mwqLcrAcgeFIQEHfZfk+0bfE1VUKmWjIlEeuQTl4y2w/V4QdJSWzjrvnu3ZQqR3+adVKtXugYjIsF2m4YrssnlHdXb5icDSNFXacVLFpFBqKsTOrp7jp7jwSuHaLcT8cVi7cdvl9dSGnMLXnvRQpNiieYGeB9hKxW0WFNP+5hrx6ejRjYwAAQ5hAJA8FlJcxmaVfa413oksIK+TgdDUPYzQQHH3Te9GvJbLeJw0zraTgBq8/+2zfUsLCx4I0CocJCs1XZAubOTXg8HW/sSXo9IxuWW3tW4vB1dQZtEoVGWmmhcqjsSGWtk0VXsuhtHz1EdaBYFtoB8Wau93MivqcLnXT46Zi5xlh2EipdFKLYqLY9V1gpoJpqKY1M8uYEBIOSCfCw+KwpADiBXoHKVjNqJMCGSEI798EJIFJAIVr/BA78V7liR1qXXZbL1dZQgaoleLxJZFMhdSAKGb3J7AIlCxtVAsmyXACewu5BIDHZ1gBC5DjGpkHhjrbFESg1gBRyOKCI/L2+nCpNsLXjT0nKoLIuEw5HypN2axioWIa+QcPi117T3wysjCd4qpFSy2xqLcqJAZuNr2t2lD6sjGACSC0BK6lFYwYyVtINaBf8HiYT/HwnyCZxQbfWAvBvsG4xMhsOTqYVEMJhYWrC5DX69iMqBvQkk1uvYUAW4Ai2HaqEil8DPpnd8d31bmjRzZ9LjVxsV1dVItttOr3QlBjtcCtl6GgIukojJl/wcFc1E9y/Nuik8Hina4MxWceqsRQQnAVdYdPaL7qwycxSQAHvTXWtuaGGWRdmFwOSco95/b1u0FJMNHkFAMlIoMRuMhQMgeLwDGayloIYB0W5122ZjrcGukTmvt3WiGaS4xFLEkwkhQOAA2GhQNimZGYMVPdq/483Olk4HztFwFWq1cWdna2trNw1//qw2hnc3XJPXgTL+M721G+4a9AJSWSAgWZq16YUOdAAetfLG2+VkQjKrkFLerXLWA5MAhsupKmqpLztUnwTKEFtRfkR1CCV3CQaFFgakN3gjIQ4Yl0GSQYFuWMVFOrXYNiuXB1PeWF98YgCAxBILICnKhGjdwIGKEDDKCIAyw8kGkJyqoj1c1sKgKWGxB/ZDIBpFHpsXhEiQUjNWbXR55+Y62pRb60bjRrhj0LhV/SeXW71F20p7DQoV6tx0NgWEnFqgNksasoQCHt4OZIITeKr8AlOZueFACRyZQDksrWA7GyCiWAHMfftA9pE4cglsHo8HpHUNbD4abAs2g9c76W1tvjoRgh2jDAwoyHw+X8BnoFlfqQQIyAKQYg4GmvglBn3aRLPUCZhtukyPXOibao73dXVMho1GF5Lwuub6+5evbbX9CSEXHmwdXu4ybqW54L6OwUlYhvD5lGAaqbRWK3RkRYkUaXcBkOk4exl0CWdDEp9jP9BAzMIXVFRQ2ESU39DhaZgkEexfKIX4XEIOD5uXX0Om1nYbxHUCjkci8U6FQBiVQwkCsZezN23xGRodzWQP0EmAwMEGFwr5NbDn0i11Lxj0SwlvAn6kZ8nm8XO5u8ZUam64d7l3vDPe2toa6+o4dW3uRG9n79pAX2r3ekdf7/DQ0GLaB3XEkgq2dm2VMCFodYI6A5OmYplY9mRpsoicg8vKWNJpzyVjC+31JUQclozNMh8qMRMRFDxbiMkAASQkBARwFJCpRcVWCTdA51rEEF2wf7cAcrsFCbx+j1/C4Van6QFUF9gMIcxtIGlJDWKDNZM9MrVfYlhAu4c3xtxWPbyhLdyA8smHlSKb1xZJTbokatdkKjV5KTXSPzA+cPxE1/Und471n+5d9k5u7PiMHadWujxq35M0hsxvs6UP3ldihyWAgjqWOYrMbrdnE/Ly8/BZ9oP1drzDDhUSEVtJHEABoRCzh6Qwg6SAUlXRXcunE1ka8Tlgd8baxdaMM9Qej9+VMmogvhiMf1eI3G6DRK32qzlcKHVaDkerRctHfCGYikcgEGDY5ljg32tUDKGWw9BBLtEhIXbXq8GM4cX5kZc+++3ayqXrt48PH1u9FDZWb991xUN9Ho5PyVDY1DSNhQFPVZKwsbuOT7IjMEln2SF8HgFHBBTQbWCdrj4zxQPBQwIsWgwEF0KSV0BGQHgtJhZffLb9sljaXUuVQfqLPB5FR4daJqmGiIbqDLs3wYja2wU3CJcnttZpgYlht8AuBdFkQrdnMjmjeESYmZNoAtdxRe4lqOAei/G6US0Jq9Xhya7W3tXd68rFoVOXRvqPD21sGK9v/Lm7u74LuEZSu3c16tSWLGVhsA4o2TV6IcvutGd0HVIuD9fghEnrYD0U4ENJe9JcchBGFaQEIyYJg89FLiEXVRTziU67qrscjQTohAyNTO1yWTpcLomCqxGAd2yJ1tZlb3y5r/W8/JxbQhUIeQwHPYvuIEHhwEFFd/Dyqd11jLQePFrLobXQOBarLTgVH4z3JQa9qQ7v4MhI16mu1osXN79d6185NTd8cXPt9NDK4uripXB4dbl3dWs9vDhpXOnbWumA1VIzptRX6qNaqexkQ3YmCYBtQ/MNyAVmKABIQ7QnAR3UshaWz4nB5pOriirIdKeTRUWjZiMZ3bSOC2sPl4ypSQU3zZWJDN6+5tDwwHDn+Hjz9JJbT9WiLc8aNlRwoMVxQFVSu23nYLMyvtQ61XT+6I12KZBnQqbJodLqWRy0ciQRoakNWk21ZKR5dPPKlc3jG3+nH/nh6fdmxkE5Xh46fmGz942NnRW1sWt4eHDX57urvLtNK2VGDDCp4VhRE1soxDJYoIgAlsLcXFzUDGMwTAM4cEd+QUEeloepqmgk2BuYjsbMyCzkOfB0BqSGq9qllqWRd1KJ0HhsdLR35s54KOgV+60GKrVOy8Yx8DgSm18XCQYj8uaJ5uZEu1x+Y0x8uBbGowJyI1lLxWqxJJJDyOdBz0C9aJum4SgUaomHm0rdvnDxpZH5kWNDb7xxe/72if4TJ06/++yzz92JuSbnbo72d1TLIBw0ApFH5oPFORBJiFRpJcRMEZanIlAo+Tm5uEzlgkgrM2fzsNgcQq4Dk20uswuBbaMU5BGA0KIzNDRZtUKh0elUMjUc82r/zOjaWzMD8eDS0hKUXYmQ4WDDhRsisdb4zTszozcnms/3wN4BqDDI6UQcDsmvURYJKgxf3z473RP0SNQAoSOV8o7Ehgc6e18aXtlYvjbfPzozPrS6sgi2euraN6+fvvjuU8fnFy/88OOd5tjgQsTiMthsCiUTNTC+gMZiCGoKCgooFZUVUilFS8AWgpegeuFxoIZkkQpJJExWDZo00UoRjoWnB3Q6jkBHpzH4Iltsqnl0YPPWWzNXp+LxWLDPYOEIoKsZIon46M21W7dunXzgzOfnP+5prwJpjAchTS6iyGgi9KKQ1nEEIhFU9sFQ/OaZO2cevdoMYnFzczwWH//2RyCFvn3k0u2Xvvzj149Ov/TG0Iljx/p/e+e334Z2j82tLl/4bR7ArS7Ow95F69Wr024dCwLAaqUK6SYoL3Q2D7YFzn7xxdluaq0QKGtQ2KGbEqPJpB0DHkHEL5QKE3QPrgMXANpELD8fGh348NO3ZsahHYRCfRaPQmDxQwEYv7L27Y+v3Hrr5KtfTzRNi8l1INfztMVHzvW4x4Jj559/EbYqjlRCQJC1DkJeTjabz+Pxa9A0IdJT+QyGzGKxJcbXrlzsSk3OXdh86sJnwHu/tDj/yA/vXzm9emqxf23t9/XtjXA4vLFefWlxON63IKKXKXUcLosZpTsKSTg8ic7g6bsPHz5ceeRyt7Q4H4T2TK0rxGNy94TTqC/AJrGALK2R9kyHro4+9OHJmVEg2sf752DpRh3pG+59ZPPd995/79MPT776aLO8vU6YgyfU6MXnoEO6rRK99DLsXhRl8gUyL0eY5yjE80CAddcymKXKgE4Azclvi8X7FhPh7bm5lVRrb+fy9Q1YFVkFm3/ko+9/+PL26rWL39zun//zwce3jeHUyMjO3bvbGnhnKBXuOj7bXnLQmU0Q5kNNI7KI7JqC4sPlsPBVKeZra7DIPRgiOCgaJYHqyMILa8dmpyaunjz56szM5pXNtf6ReZHRM9LceWEThJBXPr118rtHQwk9X0jg6w0R2MqzGRS+hgZnC4MBpD8L9YPCXLzdycomaBu7Lz/2cnBaLm96HupCu4Gr29qdTDR/+Mp7r2yOjsz3Xvzok59/+uqr468PvXH8nYtP3T4Rvv3IZ8d++/L2C0Mrxp0H73v8r7+My8O2wLaQwfHYjAFYyG5BlC8Q3g3OJOo7OAJOQK0oLy8/AqqH30+ty8egcRM0eRyBXNlzIzRx5uR3X4/e+Xbzp9Mjp0SiVF8njFCb375yZROxbgiH1h+JxDoHQvJzZ9tr9QY9qEp5OTl55G6pdSERa5poevGxt9uL8035ZHKV0FSLulZxd+M/LFx5ONt3HM6eHUnlUBKJtqs6tlkiJDymqaHM0SEbmQTrxhBirphEmCspJsRV0UoI6r6vuJ9IXK0rqLYoo1Gms2rXarvV02tr97Xt93eePM8nv+/n876f932/0XfSJxocNXA0r47KVwvVfgEO56KX+xcX+/v754vYN7eTAys3oiU7G3cWqc2j169ffzE6+uh3ags3gfzrpydcXcFq8J8S9OG7X1w8SjTR/fa9n1AIBAqFgB0zxfx74nqz60IgB/abrolXVRQln66zJBfFa5bZO4lhtrauYhogg0VzGmFOTo6CE+p5wjMjIa2IPT87JJv0Li6hADCxR6CgTnVJhSK6WnZJR0bnFQzXzDhHOmGxaCQch9BGWx378uuvP74INzBAgBhPVFKTrcuVXyLCHALEhfyw3+6N1qtUtVldXS3KysseypVXT7e3K+epialZKlU5a+L5q/aA61fu/Zaq0ShKM1zcTYlnDQinj3xmr2+ie/GL9z7/+ijywLKCwwgYL0tLYF5DQIKANFNdkk/fkgeV8NUtMQlhngTzuvxCP5pffA6LxeXGZ8blOoQBtJ6bP2gZHbpIJPZxgSJM4zrzaGqpTH1JZ0vUMGbhZKWnbQK/+PkBpJnYG1UNl5UA+NlakIsagusycrMUahBGqKjop3Y1x7QsL85vbKwyVc2pE+xl6qqkdrd1R0Kl9t1+sLmpVHYXSfpad5pP/XrC9tQUJ7NelZAnosvoeRQgNXtpGxw+A/ani6ZWVkTYWQPUQTwFibQ3MuqAkHp9k/ImJwv8qym0NL9cBwKB3FkoFndx+Plp3LSY8qymgHAuS9o/MrheMd5D5ye52EOhLp0cvlqjWbq0FD8pKgnGYqBQg2+PGloC4c3Q3h5zxACE5YKPuDYUNJSUgdsWY9XB+iZnHX18oyh8oTAnH8TfUqXdiwMj8xO/PHrRp/RIVrbuS260DihXXz7+/mX79MrqrauC2EFN/vXT4X5pnCYH46PX3LIS4jVD6XIFxQeK0MIRiQRT+0iSIckeaw9Fg4wnEqEP8RUHFfib2WSLO5scCAYE69KkuvCEPAqH0xXNLQ8jh6epD1xQ8Eo0Ck6cqemJuASuWtOT3oNfWhA1RFm4a2OcnMeqx0rLgoqHe52B+BYJrDG0HhKFgmpDL2ojdbV+OmyCxMExlv5Rxj72R35ydbU9CUY/gPhrqq7EhPAIJnVkTaDcZTbP7e0pqZJXf07XNk+03nrMrFVlXX/8+Jv2LPKVTz88Yko8fdL2HFc4NAv0Zd8QQ/CbEu1MXQzBAKiydLfG6iFwEMaMkVck0B0Jx87aW1f7dmaIKaXiTG5XamZAqB9L2t0/OLg+rmGJXfQd4sJjhHOLFbNDSwt0SrYFGqlnPVYWBCxu/wIGCFbZkAy9MHZn4SY4GBgn1jXD1eKoguGOSC8tXVOsMzhzdPwlRb64PYyjyJHOLfe3NjMlXVzhHLU+tX6XSm1OZa8qRzYmpj+pV52X7J6nJt67HnBuWiKpZ2Y22d5794szdmB70idcCSinxV9K3yosqdLWhqNMTZzMOmr8gamb4QIBNIpAAGiE8cn28fWNGqtLCud2JYY5HJyt5cXBwcHlHL+4uPAEZg578eCg6Sho1T5YpJ5FlLdcJB698hXBXV8PCj8M+B4cdfxrsBkRrM06QOTKfMybB0ZbVdUMFgmFIY2OUYIKioMUSQB+I0+qh8bXk2PnH5UzH9ypDEy+eXuH2qpc23zy6Pn+7o3m1NbnN9f6qWm51yLSQAZz6tp1N+P33z7I7oAd9OJxnIFBkx+rpwcvp5VaoXE4ItQnm+HNmxRBDI7AdGFQI2efjLGo7BDfiMzETAfPTpZa2rMuEIy0Rkc0hftFUzcGBgHsLykorhgoJlTMVyj45ad+uR4aYm2op43xsjrhJvaWL2xtgXnm76hL0kMikDhdbSwgGxiofiQaB7fqqDlCMoHBTwaT7TDBZbbZ5NDE6BhOaGdYzvLI9uXtWy93b1cmX96+D9hNal+38uVq442V9j/+2t3c3GDlOpw4aTsVVn/vVKeFu/1XPyH0TEwc3TNYW+l4HTllzBCJQ6JDSr3pEJgWwsnCJ6SakW0R0hSRFe5pLubThd2DsYP91MyEMFAIuxsUki5jccxN7ckJXKlUmI6n0wtL8iywSO3IsbIGkWJJRwd4wZPeBWMWWDQWnDOElVmNq4t/FQlDPA74uQ2Dx6fF00V5deS4kE5+PCteGpO6W5TYlC8UMllZKyvsnWbWBLWt0uOyx97fb+7eeXBr5clvWdO1kvN7m9uxsetzLA7Q4MMi3LIiyE76x34k6NuDKJ69Y1L8JbyOTEQx8kIh9Z0gembWrmNl1ZYWrrm5AQTHukK+sGc9VjDPBvoYNyd6o7+iW6MRljcRTriWA5wbH+9ZEskp2U4mCGxdoYi2QJfpLC3wiscsrECzg/QoWAehXmY1xaUUuWxpwbuM4X8cDYc6k/lLMjp9S8Y5beqYHd4zWyFIbttNKBdOzAsEG337O80T/Tf3+ydWX189f6vxTVtKSuxG4qNaEPBplfzxZH+vVSkYkKr9Os31jQ0IAbZxYyWM0hB3DBJHdEpSgKSDTJ3n6oiD+IYUgyiBT9xJc6JpMIXGWh4fvLkp6WoKp8VQd7r7QSFFYQ6eYeWtbHDSAMjQ6iz0UKYWJQpFDosGTlthnbvBRRzY8MCDhZ75Vhes/1CgAP3o7z9jYwiMfesqQzjSpsauwwql52I+5mNiP+NLaUhSd/fl5JIbVAOCtZsj+42qncXXyYFt3fvUG6vza6u32K2tq4+fqKJ3/2ivv/LdFbep2sYHd2LXZ+MpvqYmRHJoZ7wUf4leEoI2xRFdKAsyPL5H6AdhdBhZExxOm5pEliYBfjk7Nz+RGNoEto6WVo1UulMU0NTUtdM/L1gf75HxS+0dTZxK+eI0VtZnaYXV1hZOaLRNpKcruFzl32FjiEVDv4YeJK5wGJKLgbMRVAumfQi4XtCZIG/XoF6s6Y8+LkT9KF6nyLszf2U58WSUPJ/PT6Wep6bWxxRd9Ui+o9ynNlPbNl/3Nf795+4NpoT67DxbEnbug188HXITypv7HoDRukWLAsFgFzJHIe3By/hRLlgEBryfJXwPhORkbAeU5yrfwvilBbUkJiIXfKorpwW8kdTUsKYEVlH/AKgkXZGhT3T0Ca/ryuEXcvLyGiiRGCTWmdFQzRfJvRkzloCB2TgamOghYYdhAFMK4kq8i3uNrAzPoOFwm7KgcB0dnn919o+Odmbe5bKe8Yq1vdZyv0ua1CFhqupqq0qlXr4lEGyvvaEyNyp3U1LWqK9UzSruxoM3d7tzMj/96FpuBDBDzI1P1UbPAczbKqk2hBKJdflqUI+81AZ4HYalIshnx3BIy+xS/oKID3Loufp1/HxuiyaHG81UhZbHbLDXBYMVGr8A4ADkJpYrQJNTABW2NsHphZRNyvM4NB5IiVhh/s3Ffv6BLhyhjbWZGS4WZ8gX5AW9VTU1p21Qh9E12aXy4qoZhkuIlxZ2OGMJj19PvttiGyIvHx8EPf+mdWd+ncpeYz/dXbt9Y75tNzC5bWR+5Y/miau3G5+9+9cTIHNMv/99fbjbZ0fsDGxzuRvdFUMyUbUXEecekq9Ox+PpFFcsDgOBYYN9KXLvEkpmaOhJnxIxiwvafaK2vCkzpnV5IFawLkwMMLa9ltgllIoKeKWFFsEYGDSkhH6JlvXLlwRHO92fLh7IJuAegpkZGJt6ICWLxJDOEmY6SNraJDNiLwkGsykrOOFPQqKJFmYIXWdv8mRQqbpvOZqQPZm5JJOu3XkzMcVitnmkpATubfZR25Lf3Kbe2H/1av/V88qfL7ftTDk0nZuOpr6eWBTyKa7AeCWah09sLIJRREsCHNGljiU8qMePDLHwbeCBS1+55BME37wErlDNZSYyc8NjijYWY2MFc+WjbmGjqdRZjahQVCh2sYcj4woV0dPXT7n6WBtqQ7WtjC7ql3lPLmzRvYuH/fUJkWiUFkiO1fww3GsDRWkbOJIQuMjiSbE3r8bQyc7Hy8Q6iJe0tESbO98d4R60Fd6jTmvd7+/KpeVIBpNTfn7a18iO3bsfOLLzx27qSqPy4e2UC5eTF4sSxJw0FndlpbFFrSjMy4i0xpqbdwn7Z/ELiiRrbaghBYzodClEFORfHWrraaJfSsmLV7O6VF1NuX6gFMDz2KkBuRGq5rmB7hxvUUPEaSTCIdxPyI1Py/ItzPZCItCAblE4YDTTvYd7qzrMCMcxoGO0oDa9xdmuB3krQ5LVWTQO2RFUqtC5xBtmhJzWw9kEhenghwaSr86FZsjjoysqlpX7G6pyqbRZMHKzbe/mjb61VxcuJCsnnqU2Xx2p3H9+9erD+xvdD/omojlkB9tHAaqiloHZ+KQ6DBFxIvMgS7sgSnJCajsl0XUgvdlkgiPSpy4vX6bglE9HOISz0ooWB9avTriFZTJTGwcrpCJvSsDRoycDIlpyND1qGoVioYdDGFaV8fIS+HRvihlAmLNARvj2PaC9A7/BaGa41JzHK+i1NOt19yLBDhkx4gp5/jbO/sbB9ocMi0OXdGTjsY3qk5G8hKHxioG2/blpjrRrIPny5c0RZTN7bVUZWHmH+v3qSt/a3pPN1nPvnlKt3LrfFhjbzc0ydbRzyGx8OlIxtFXoi0UhfDhqTTogaiH6KLQPhKxvBw3u5CgWRBxmVoC5mJYmBCvHjaypMO6N8/OC7hxa3qkfvjI+lUjtrxi6xPcLdkHAkKSq4eKCYsJ7dnbAlUIcrHwoOBz2wbcwoAii9GxmCGRGjSVaz8jZrgp9CNVRQOksNkN7mbpYw3FVvISFreKtiTnaMQueGD+rGRnZpbZzpO3ryYEpN58+uKWMvb16s7Xvyb6E+bDt+d3kkZbRe+HnpqlPH95JSV7XsDJ8HB3Dy6mbg7Pp9PwQd5RjBk3Yg09Xi52QEIR2NoUDpJby6awT5Lz8VGk/GPqq3ASWpHFwQMrijH764afXVh6A6YyndZqjYTDQ1R0dIMGrd7AUHf/ijI0lUHQjgdqE/PrLswdZXoRhh/VngMlAQcdjSSgtQwbPYZLXSzICwTldNEPuekmHJ5V0c+z86eIlPE24srnjxtk6KObCnftsyYjgZWDy09Vn7MZmduXu/QuVram//RKemVZEbXx560GsoEKYJ/aJdAnPLFqu6MErAERALcTxADUBaJZS+HjZVmpEE8GnJClao+EmTtcG5MVI+gT9OWnnfv30h2e798E39CxEABkKjiFZWh9crkIBAQ4HyrL7kVFwQPOxGAza7kNTIu4QyCVXMTLIBcPOVhgrKBQNg1sW8wJkdH8zM32inpZXsRjQD531HWqEfsNWU89QjvSVMvpcviygInY75Vbbw+bujb2Uny9sv1lpaWRXbj+536akPoL8NqXKqm8fvXbvSvPepqAinm8RaU/0zIpZ7sHTC62dkPaufmodPEQBiAAri0y0KElIk6q7VKqp3DRu4tzAHGv6l1/dfn8ENr4BqYhi/PkPBo4ukQBSACFGglIAlfQvdcibDPK3xEIRwKM69AEcYA0g4DXFDca8yaAaIDPoE7FaMCNGlK+8oMPS+awL5jCaEbqgs7Q1cD4m1IQxyewZXxy8fX6ilqU5N5AsCNxNftg8v/63R6BHcuWTfeqdys1nz148/ubxy/r9P//YfCD5/TdjO7es8w9HBmfVhXFEOMKcE69J14kXeyGR2DoRJH2oJZxMDC7Ni5YqyleYAU38NMn8opA5em8069yrjdjBZRol7gfIF9989x0wRL6FYv7H+GHvkkeuDTOGUDgQScGj9cHHADKh9k6WM/5f1TWAnQ/cJPnREntIy4ZRQi6YIVnZ/GADPYxllDYseQdpanPIBv6TufhLwsW+G4mj+fSsdY/LKa8r2ZL+wRcjN1sf7j/bXe37a3/bY7P+k6yI6Qnl67uXAwVDtQ7upqcjJu4qBeM98WJ3DM49I1raoyPzAzzTC1LEcSEGJ4lZC/REJtMhlN/F3ehvqR89NVXevrIZu5iTV3ft+3cg319xM7e/+NYZPQzq4CDVFPB4GR98AHIgKCRQZlEHRqjul1+CmXYcKD1mRwhjVUbaQAI8YwQ9jOgoKCEH9Rqivb4ioWCRxeJCHV6QrF3hSWTI3fDywpYXbOYpSnx9Rezln28r70u6B59sB955+MnexOqDuy8v/HzBY+MaMELOK5++vp/isT6UZmvh5JlRPnF7cDxdna/vhcJmcOPBFh8f7g4xRzpHRcRvyVMTuzzjCvMki4tF9e1TTWmq3RFBd3T51K/vvf3XVFphqTX2/c+JSHB/Qs95uJgxQ4LCDh8689bnJCOjg7/iAEko4tdfHjt7/NBxYmS176+MMTMsMOIOeyEO6dV4ZyTx/I2sSBexcLhZkPmCjtxb1k7Tty+TX0/forU8ZzPdKPRrs4LYlLsP2M0b6489wGh+stK3Sm17+MIj5XLywPNTXTHUDXb7s/3b/eNDXDcHVx+fkNzmDQCaCT5GUIxLeE46fkgNsSwVxy/IYyR+5uQSPmt8kNo8HdbEZe5urs9xEx59D/nmxYRUQQlxP/rxGdxBR3TMOBvpoYA2jUAbEb9wBFgJlk0o8hjx+JfHDHS/ghGNshnkEw2MKhLwfX6ygmuha4KMJyf9bUhG36JxMJti4y2dLTl+in8Cw5h0S5fRWv5uYTpQ6L+PCwY93txhM9kDf2x7rCl3n/U1UteSnzy/33d19flbbz97MdX+/Lc/P3z/uWpidrk2lwAEbLvRNLVM7umIxkCdyHwwAP5h6szjoV6/OD4MWmx3bOmXEKGRLdmFNsqkoka6yaQskWVkzU6yCyWjMOrawsgWsisGN/suyZ5933f9zlf3tzz/8OL18pr365znPOc5z+ccL3QGqgJGbKW4k0Ltaj07eoxqjO2cEq1j0+wS167SCq+kZfuFPJUSucxwaT8T102IwXvtISArkLsgc+0MdL7wc/AywovzUbQNFGjZmLklPr+xcX4OMNKSx/6UZvyD4+NzNcOXF65LX3/Fvv+ApK4aPq7K4Ue1x1nBUgcNbGOo/Uayj8aLrEGwDOFXR31nauvmcgV5uEe4q0eru2N7u2VlFT2/sbFU0DXZs3OaR01AIz1xtzvWL0aNjYlRjM8ArH7WhpmVScw4GBXtEEB1DxKx8NDJ+eQ5lhz/OETHK6zVz67cDNS0G/WxP+xgdIAyIuc/oiaGPH0AykFm9mNw2Nx9dQpps2BDZHZHL9P8fpHRlPj8+l5S5O2T/NLHuO/wswCMobzFbTkOVqY/OQ4e4Ne94jBgmIQv9zjL9JeDIrYxxDTdyEcpIRBgcK7TfcUt4XkNO8Tu4h76la7cjt2CGXLqIr2vudHy7DrZ1SUvnkdNXi0oc6WnIzanUOkO8/6zUgLGum/FXu3fx8iBwls5ZYjdijT9EevZF2akamuaaJTt5za3NL9gtmDt2WrlGHTlX8fpQKENzWPP7sD5Ap2hHPxycFDyoi5Jq+xjgqc2NljPUEL/+hdoy1Ue+b+TfaP7To6dnf9fKtKHDnLd1lay0D3FyXpImfXgQUldKe02B8PGcg9ugBHAVAW7pyf6KD7QnvrxHdLmre7k3Nb6pe4y652Crq7k4t1t128VeXODmTWd9ZTNMQLhe075E+6zfE/dN8fgkmUfJLbv0HXjSuPSC2KvjjKh3GJENKH7odZzWitTPCHQN10rym5usLmmZr4HkuZMEy+YzmE2WllpI0wnxMty9ADSUckvKSnJz38UxcDIrgJvm4KCZwSP8OrTw2sfyKs071uYvdD9LMOlwn/wLNehg5ql2vKRb05C19lluHlK6oo7xOENGw10zjF9dOCLy3K29Ur3UXycZOweSyREbFB6qHm5iy4dy1sFYcktvcOLk9++VXzamAouscwbXlno68iOfgE9Hpo3M0x2Gr4XYT2kLjIxyzxWcy49KfIMpbnPIqTQO7+7jyr+OFRvytLPcm4nsVovcYXiUmYV877p589m8+AXxko8KB5etoNILw8nLA4O+HKX7hKEsnMXNbmQ2SMnZI8cfvLksOjjz294nKHWeZ1dmvEMbPlzpQ5qoS8vcLGynth38ID0bT6HuGiHthKdxyx/aytFNyrY1sw5aagq6Bl88XSp2CRTnepGNlwryMtL47ngZ2Xb0xEEz96lnzHuWtnWC+jVnZIXV/QZnmhelFG3mafW5mBtpc4xc0jwGQS+/Aslk1TYVus5SfXls9BRrP5qurORYuBjmt7r4lln6TM1ulhjae/u+Bh2Dc9h2N+8Z+BYQdRO+1T4H9nQiH64fwHkLSBrOnID/eQ8qETOXHlgESrkoR35TubcMS42iGbct7UFQt8ADOdxCOccH6Ui2wAm0/kB2ylD1ei40GC9tUwDDQudqVpPF+TU7EzNnndxxVFyzZIbGvqJ40v9ZfXh83TKiW6k8XlhFOjIhIVpaGnuPjlz+Nlx5RiMd9v7K9eZ2WUqg6NRwfbY2I4wN1EZQ8dBO/vquZaaatOUlE8uHWmkKdklr5GoAPeHBsAiuyfyAS3WfoRJ5PqjUxKXefyhI5FbhBeRpOkz7CnTRKUSFBRGC+FVQOaYzDk2fpYDKp+1lV4gbsZxnJXtj31vPxsPRBs22j1M2C/5/FYIxjDYfT7mvWKC9mDOJ1zE5EyZ0Ui2UQsBQsD6dljDMnlMeLeYtIgWXlufWt3rC4W5KQDzT0v7teNCJnFYe8WznMzSZxVRbTn5Wm5BIp9Dm3ztnRZSnFSd7WusicTWkXV69JpbfV1AJgj3eOgYlEGtBZpgRCH8L+gCl7n/7uxV8fuIWYAB1mmG87DO8N1MUHj6UycUHh80H3HzSjIhMO+fvrzAvo/1NOuhP5gvvGxqbDOsMk20uMP+JikIo+2sN1fjK68e2oTPB4PskpNJ7XVLw939y5vorZ6ZjhX0SnfvAmqhxoi6gF7/1XyC7rTyebUJfejLBbarl5/xCJm0YfXOi7IyS6Nysp1izlx//rQ8oLB809IgKNCp8zuRYr2BpqVZT8urs4oxCdIQoqMTFvqppsZ7GBHrHgahgoT/O3+h0xdlznGLQjMDaB7PX0P/C/kqKv7UOeOeRygcs9ySKoclmQ8CzAsDw7/5WVmvgeaQ5eRzgUaMQ9VXr0gx1lIHRUwVCF+9fBRVI41jkAiwQtZqiWpdzyUWzyzQr7SE9a/rT+PynArmfWrCine2O3DtXgzXDospvW9ioLl34xkDzd39d/4FzhZtcEmMCTXi85RXQlve7of9VFeMUga+3ApKzCsFaBT9Sh48yxv5Vs8JIRpr6IGrhKc40E6JcCMjePwlrl0VvXlR/AofIvOrVONB/awE0whceeAcck9PJxI6qiU1b0gKHhR5px3SDOkMJ8er/cwHDsm9lMdjshyiEz24Wf52UGyLTtIzmnIr53sdqQEnTcTYcn9Lal7nJoFcvFLQ09XTP1vQ/62iNZHB19xXi7K5PkT4bj96TURE/pbjkrBX0q0bV18xHuI68tOjUUH5T5QPt+BtBQNsEXXHyktcz7Sp/lNZz5L+KoNwS2t2KsnHt7pJWV9/VdkMTTcqLwAyxSOiV27uzbGT4KH7KQHaLMARBTHdTxS8CwOMxlNbDxvbwtDX6prnHvFABfCMv3bgVNWbk1ygToSDhl9XyQEz4BBdHaIOEUBdB+MQYjpvZSJ/S7sS6k+uQysdJMv28HkCjjJp9quhZYayshHxDdc6t+Tja1RH2VwkE757N8tCp86DkKZVKhafIfRM8BCHurLS81Je1BmO0Id6Rfld43ry6jqOU3mt4QtmE4v6+tTW3vDMkpr0psVBL6NM80G0PszfAwmt/JWL/+hnK1HKt249lUAm2MgLNE2gJkRBJdukmqBnOuhjr6Pw4Nw5ubvHOPbzSoTiJ7KSPkC98zL7oT/Yb6s7x7VpN+qZZzBKv0x6iI3Wsd+0zFR8/Px9SCwR57pDyXVKy95G4tnuYl1ychl5vu9bRXeq7KCjr1t98eJav2dOtMHxs9IqEnrpq9Qv2AHFZ1AzOinAc/9vlHSo8dfa/F03E9GEQI3FqIANs8FyryWzhtY6akpJoldzeWe4lqmtiRBaADRav9suQPX72PhWhiqNzeuMhP/MsZmgqUT8Td7A0d1+yuhrYfCtixflnj1i3892MzJrsFAbqW7elWb6Y99HhaC2OChOGSiI7bvt0ITB4r/mpocHXVEIbEJOmpblaSOr7HSkxESen9XqsiZOb5MjyHXp9Bq+8dS8MdntsKjA0ImrZ/i5JJzTZVO+eGNslfmZmOQemwmgQn2xtflb7kp8rz0m9DXmZNfKg3zmV63z6+yM4qvL01MC6qytnMwHaUYVQXaoCCpePhgsAHItR+cgmkpbZ0cQkWoowgIYECs3KRo7ewTMGX2117t186LcDWlpxv3XFQq9QqpKT0lK/inNCuUNQ8UBuNFkNSWdYztpqDSAjYvK2+iNkf8cWOn9nUggb1GSW6LqFnAREa7Ti71hycO4lXVXXJ6dWYGBiWN4/SbNUmeIs/gJGkYuyGmVCnbaczD4CXEOluv+yihb7JfWzUwBcQsFZRRMw5gzCMpsWqr/VEfqrNnYiA+rqweWEg164UH4wErAoigPMEEPHW31gmjKg0E8/h6mv8CvKtGVSsCr8R7mp3gl+tnrPbx47hivoDTTUZXXge7VeMO3cnJMItL7D556Ll6FzYK6gO0DNuk3SQ+hGzY2LCy8Wd0wITOW6ELY6Fh2Cs8eH6v4FuG6+6uONDnUsTpLKEudQvEEmTilJi+h1jSCBO7RMRzlvH7qwWGGbfscTFszH1zS/kYBS7r5kUcWCTygmZdN9wrS89ru97QmGZUv7Bhp1denUZ1KqmXpZKc0ykGzqzg3B5rEIBNH2xCPcnQiKOH3FLGwFNGDGopzTc1BPnYBmVPwpJwgpXlO5Qa/4FExfx1800AVNMY+YpNjOsD/JtIRM/DyeVLz6zPMHx0U47xz8snjefFX3hRqgJ/hln91UEl17VNEKGuQF3sbkjsIv7Y7OlITC2hsDGDwxxy98MT5J/dQdJfZODVlRJXpC3ywWEyNwCMWTlRtXrMJ783P6kLQFrTtW26gV7OdByzji6PrTtbtvakkoxLfNcAc9PIqBxhYGgbA4q5j2kTvC6pAH6AxKIdxKbJrqkCjaOBoF6C1nfZV58VjTX5NIX5WiABJX6faBgw/fvj76AX2AxylVUqYOGj2rwzWZDv2/Gkgtug7YbYjXPFdVZDfdxccbqO4tyU8O6WLAFfm5HEwjcvQKqQ5Nat0wj+9zEt2ZOm2K39eRtOe/pOXi/uMzeWr6OZoLMZoQoIZVb/zlPfY3zd50HTon74aqpE+S3WedaTNJbNFI+u6tHBgiZ8roKUr2J5LR2iam4HF3Nbd0tR0AQ2iIz2EptrAwCDdZglMpAgwllp1i+FR8F6pqXnu3nUONrZzCvhq+7is0o9/iZ48tp/lo4MGVM5KIw2ePrjDetvQGOMdS6D0Ddfc1Nap+UIk4vp2y8JIdXmLYJpvuPn2kR4K4ddS/zDJBkVzb7C5fMqMrmChSZkB0jM2lbO8l+6CYH4Cj8XY2jxATakznnt7joceRT8Yr6Fqq7eaCjXmBWHh7fE00E+5AUv6KAP0WBWMagANLIPE+BhgsbRsppmDUTbumTFAA2tOPzHIq7kZstS09q2uHwEhTy9ya17iZWc5KpIQ6B6PjTO8rXv77Nt9B09pRwZjG0tLLSozRA6dMpSKxtYSXWdxqUoKWV4/PhGJxPX+3q6R7J4WxDRjnXVukzjy6vhQXXoBXYHNlOKgGR3NaLm8EAp0wrwqgjZC16CPQcgDg3EbRUkxXv9wTogexTDoq6Hk7LFK+p5WsgRdvBu5WuFUt5ROMMySMCJQlW2uTgdZaHpivJGbu52lpWUJw6hVuJWpnV5MfE1iom+1cA1kcc1ePlbW2aSVbD+PBCnu60f+5GLez6se8rXcO6fx5UtDgVLJA1wv8QbYtpd/WQxmSDFyliq8wHh/h3syJf5BID49lkih9G0UN3Slta53gGkIO3XhXR0VY0vDFMsJGjRPZfPcKJrOrMmE7zIdzeUDR47cELoM/T4o2eA4jBOKjePkxVdgl58mSnwJgaM7raleBbS09PMtwEJqAZjEhW0zM300rX55YmJ6ejXokZ0y3Ugws8pyyWxEKzXcyi7TKN7XNz5ePzG+2guBqcuzXs+LMnWU4uY6e40LdJrczvj0qFpviMe3LN6yMN3OeojHaH9897jpqRjjKUPxOEytS8RYhbV4UqOv3yd441zI7e2hZueO4yK+uU6Pt5NyXQjrKxXZJWZ0+iB0mFulKxg0zjh7gu74M7Y7N+4iLVhAYxvdhmKWk3lGA4YDrcFThyYzJ6dBesivZTfDw0eSSU5GnSXpcwvr6/MFtKs1NYiYMr7EKMUJYKgjqevCYfCOmmpFcuqMLykxGk338U3X8CqxqmstW8xrD49R5z7LfVmE485+sVuB7pm1X9ocBrKkdKUPnnLIcsQ23n77Ttlfk02l1DkS4/3JdWjYxehWVWN1LLF7uG9xOpek1bo1XQGmGdfyaymuIK8u47Q0GFA8AgaJc/qo0fTXFqLCNJf/YLtx98RxoDlNIxvciOK/cAnimJCBksDjpIfotalVFB1k1/MpASN7hunsXEnsbBmfEkbNl4Bizze+pHMPxnIkNW2FPrm9F4IE4o2dRk5NOzHm1eVe8dS61u6tvuxUPeMrKmcv/cnJtP/OzaTo8trYIvwAxtzw7X7O5wNBmLiXHz7Iy0sgpjFow9bivk27FhskNTqGexKHi3d2e6k9vXk78Pzk6rnTPkIiu/bN4yjuEyh6G6Ug37kC+vUYCwvRq1dfHX3y7AT0+F4+cRolFIr6cAdy4gKlK2qaFsH3UPoM0PVHQyu8Qw2n2rmlwAdvaaE2UFMW6NHrJfHxYIBO5GduJGp4Wl0PfVdedn2d1ghYEAgT1zJ9TLzKa+wAJne8NcruobiKypnjUL5hU8mwT/wR+yVuAFv44qXKob/whU6YrL9PPhB6qsnIcds5GDENbriC9DSrzSvWhVI8s9qn1TOS1zULMYBgbfTDTYvgurnrmm0uRHfi8JUg3ybhgvQQC4sjNKefgbKdAWa8XQZPO4ziBiE6Wk38yNn7b9RoaKEMA5dSuqWSgJFktxT42F3JDSAzGF9DCe907i3EMC2k5JFU6/rcgt2y4rx6JOilpMBGWnVzMg8qT3SzbqV0L+a3WzlKyYuqnGDcx3iUVz00YPxLbE40Bmte9WH/MYdoE2yc7smTlQLqIozHDKXaMHumodREDhRmfnIZ7h9f6G3o0srfKIM8wMXI3s+pv8Jle5qQqqpPZ6amZPxwil7WN9Ki9DDN8Wd/8JxggF5ViNBo1Al6ejob1SMCMrcf3IOYhS4wK6CjGbQMoO6xkBCW5K7NVVr9DaO9lQIsXSRqQ1pv9vLSFox7yqtPa0huQYLCvJGeT6JXupNWMYW8uZyXaqvOJyJySQg6Edi4XuPTv+TXYuKwpjrPOVl0G3VsMVVvZdSFjMV5Bf9KsoWA5vpt0rX3fVW0b7sLsWx67Zd1ck/99BbiaJ/Ko+ydyK79a0NEd3lh2DZK6iaD9Ga+CroWN2D2CMwMPX3t2aVXJ1AoYaGrx1XFjmjef3dEHw2TYIWWIJxXp46QfrOEAQspZb2AVn8nBRaC0tLS1ROWa11fXLy43t0xXNzfqxVGcoOYkNjsnhmf7tUZnkchzuzmW7s/5RYR4WUQhDIhr4SHHSk2NgeDyfHB++8/VdXmi5hGprLSWJ4RXgYHMEUu38hDBCeFxgHfTzhy8dhqbl9PWGtYDzgaLs8rzj0ZF9GzWVFme54efUlU6bHJBHq12uLN5z+hnw66iK/9eejQKwbUIA9N5ZU7ojL3b9pcE0amV48W0I2SAiydEBYqwtLTNb4oTKu//qvln9UF9tLqnS4e3tzuoHQPF09b51JJEN8s1yzdwTS+1PpuInm9I9vKUVxUTOzyJUHGo2zcFl9rwDRYjHd4gDan4JuBQnfYNTLq94xvnuG9YOgYh813/TYdQfHVifawIhIo/eOLk+Bo3eP94GjE1JLomFSC69avivYXh9H0NqJ8UsYT9PrNFi/9eWBaD2IZFg7BVyjVa7LqR45onpQ4e0lW30x5YhC6d+cCRtxgb3QhdqECzMo8PW3Bwm7Xf1YPNXemt7+so6VgmULp7iieBkdLtqSOLMIzla9XIqm3g+iyO9maavv4rJjYjdOCTGxHj0jo+Gjlg2mwtZnRnw+BaUzi4l6elOGzeSp+R6XUMRCTQ4yo6He1Nse31bTicB3TC+PWYaTe7k0i4mgpOnjH3oqKhTGC34vDNAw3RKBLYJBeWE3hpcQrFC3Mg3zGxMyvghJHN4n/i+PRSZkjyvegnU1e0QYtW5JqmQKxFpp1RsKSAWZrDU1bsDjeA2uPJRm8bBlgZoR7yGQypaN4WSuMSrUaia+21HNMT0/RKiPipse76+xAonJGkOEoM2Iaha81sYhpcvzsC7mY3wx8tcU0/iUjoWxgLM8rY2gch/1C+FZMILjpRbclela4DE9uz6YlJxf3jyMR7XuivY5jf8XQ4oyLn/N5NMMNMTFu9abj9JUKbx49o6ejRd8FObIc6vyJW2fuSMucPPcEhOGDigYao3TzluFunSU1nWCYkTAq0Gxuo2mF57d64FtkJc829O3BDK9tEoeAZrgfHI06Ek5dsLKLSfSKHykmQ/LbkRfuqCp6RvDZNWYWULVJ6Bil7ZnmCwmrwPKoqi0mGqP9VkJK+eFjNbF3obYQniO+wXtsZ2BboRuxYmh4bM1aq6uhY7ILjk6XL+lZoSVwds7PuEQpqNHT24gJqqgb3EMPKpRKHr17lZaejYuZCyVUqc64D7TUojZCPBOKGu8NeGiaA0xToH9qPHkEQlnY7G8Yhu2NFvgeWbNhY32T0/3D3eStpe4hkCJTypa1GsJGwgPm3C314tOrSfUUF9zYbH6anjHfGUHeq0eZGA/u57LA/941RXVRjTdZShujnbBtuheeVjYlqJ6//iYDj611+Vax7Nrv0xhn1ItzHepoWe9taNHynO1zhW2T45UVbL5Hg2uPVDp+1QYeMdQf2qAnnD9D989l1DV+aWZUgTkfo4rMBZnzyjw28oqqCUFmwp0gOUnc2UkkNSAw1NmezSUaWvTS+sosoMEKC8udmYT9TyGPFUziXFyGyB1gmhGAMao2dfepTjdC/GxoxzPbNOEKlNVfXUZMwyvjkRmOiDy9vyTXhgpKa0eHmGLwt+8nKMc/VDpyUhvkQbEV38iUily9aGzNd4KrS/fKeC+1ZXmoqzgCEoEoL7xzfB6BvDhDyA69eRl9lJNJUOZFJfqnBagnHt1A27zlRK3aMt+5fuHtuRtCPJXQdZQRVLBkGeBWsrGwnu4WnpsLOLM9GwiM2eLGr7Dfq+E/MMNLuzgczmUITDPTADCpi5B2moBqJ9sTh+uaKftq+1j+jOCdq2xgGrg+g2liEdO0pv7wP/Q2q+096LY+J5j8tH0oACXVF3E54Gj9BBzJNO6HL9HVFde90WOd3FXsskuO+FbxPao867VJL468MEOI1fa/cfWONNAoaNDbvEZGivMxiL9FNSewCR57+7cYzz0bDQ0ldWcD+kW/8MyaqfmFjZbcVKAJC+vZWIXhBPrbC1vJDQjL2FjfPzDEjbUhhIbcDaZpCA8ISHfSCUFCAMj7ceTN7na7h3umuYZ0CrE8SnK3BNNgvb9Q8/DX971sjI6BK+dr52Yvvfdqmi9fBCKOFtHvSm4JwPiRXFwjCMM7WmluyR0u40OQceZ/VQ1MCNIaIi9M4vLxz0VPn5FkFpR5875gVEHb0LCq+kQGyugmG+upv+4fvWw2EaR05YGCKrr8h71T+vz2/E5KqpYWApO8sYqGNGd1bf3X2N7K/Q/MENQbCQQcbogMuyY3PDzAasrUI+Z9eY1VHoSA3eXWAOeborBrGNiY4YVdxL+wuii2CEwTm1ubxHTMYcDdHhOdpKBX6aYXpCahfSsacTRCf0VZph/WNBfqMxXD62mpTmGUoXECvDrHBng5JEiFk4fWJ3HfvfHGx1XkBJluKjjK2uAxA41xzcooOxU2zg+3ZXj078m/V7xyK7IZXdJu5bSzvbo21/n/MJDorM5vhf1m+Q3TTR4CPyMgNOSO36aBEOAR/BBSGsQ0wyvddR63NME0f57Yx8R2cP853UKjWjAN9ku99Y9bjB+zoh/GYRqTQtynwvU0RD9XPQVHc4VtQ1gm/chxyyPAVinbTAtPaSAPjSNJ2hd7DQd/cTvPoZ1J3KfaOIXLZ0+yspzTDeGZ8sa2tf0YRekIMkp/1OW+fFzIwERR9VaSIoMbdOWtr5ptT5Wkpu3BzG4s0cPFWbhge6cnF9ZvGAjN5KGhrbUhhGaIUtzb1wCmcSvXCfEJQkwDhZbd6dYA28egcxc8fkAQXqgFJbTN/b4UgSYkvyEfz836Bo93xGDw2qZuK1qZippvtDMwtcSIb9NDuBnL9iKnYYjJrsVbUVZGfUNDu3B2EoFG2/+KO2WPpggbyHP2LSfLuUid0XJoGYtzR4XcYeG/rXvmhLCyr4m8akJokzC13cpowUx/aWH8H5iwzW1hSKfRwmaLu32w9mCQcwb8bEZ48rdphqe1IASE20/ZuduaqHqlpA0TXSibYJoETZF9+/Yf38cML+/stwt9i2qRNOB77pdQZn7DxuBCDKbR3q8zOc2ITx3aRJCjM2ISN9QQnl1k5Ak0FcXjUaYl1rihLiSk1RYqGvpzu+UPbYKngdTsJ99fXCzHntuPJkK/VRbKmY3pWKnuEwaGUR8TeSl/gLH8Ydq5qF+wupgYngY0EM+21gpQyKCdgrWV3H9gZpZ/bxrK2hbht2n6rRtGrEasfNI9gh0NNBItIUFz+TUDplHnAsXg3T9YQVXLKJfkblcEu8Y7tr4+O4PlgsOAI1wLoqOyx3vTHBXfOTjjvb+DQ83gyGGptbUpSO2MsGwUZVfSSyD3QEj7VGuq8fwtn08reWvS5XsRpk2Ar5SfSU47QLk6rvE5KoGN6ZGu7qWrDDa27+WlEkLnhElRliWL+sJm8+kjv2HGxuf1aZCnKuHtzbG+32vm96kJ5l+l/MfPcsOoVlamEx4hzu81yn1Siz2JlM38HzoJFzlZWQVPiyF9bqz+DiZRRWCaok+5+X5SzLez8D4YjLd3a/FKWZqJhkLWw7giT9dvhBlcB9U6NjsZB86F0zLycy+ZBhogA7mJ/PP7Z82zKUCTD7XMOaVS/n0S2jrKTVnPUffZmOXeKCjT0P/MfI9YRoO+xM8uHmD0RzcsAaYPYFoWVumRaZrCSzuz/+dnxYif9cv2IDAuiJ+NWEI5rcQAOvgMFIPcrMs8icmzrV8jH1yH59yDrzj3gaPx61YleoNpsF88w/ILVTh0swKDAS3Ws2OWklYtlYR39P7iAll/n0sZtTe/Pgypabik+nzVK4HMbDYiApxLR+3NfZWnUZStPiJkR3EaarePccjpJv2UvYJ6wMYk9/KNEB2NjR7iZknG6HJ7O1+AKVhad0uz3jNN8s62MApMQ7+60AUg/w0BwwgNHDUIDMSzmRFLKEHpTOghr9PNIAbr8KRstrYXvr7JBT2Cd5k44YGW6dTz4GDvHMg387utW/UE+Z/jI+0hKfAkFOd6alW/1250zMmHbUOeJPaH1X/PGyOAcxFTM7/qxdcThrrgJM0vKlQrvc9u/DV/a9Yz1huDeX/j8ylOrgu3uY8jMMeeR96jRY2GPJSXuhVpjp7IdK9ZlGUoWB0s0bK2/r1p5mXRAIOWXdzNRdYY4Oztmm4KeQZSGlhESrF1QzLJzs7DXAP5nzGKXjGp2R2frMfz/EL9j7FzsnNclmZnPgC6IAfzxhz4DLWf+pFtc8qwMeMroOFc+/vJWgYZVYXuRZ9g21D6PKep9Z+KcyuAxjPVBx/sCzS/XF1dYosCn9y+IK1u2r6ykv8F/pLes4tv2ZmlP0igbrGxSD4PvUdLK6RnLn8R+pMLzKp9EhdWhZFNE2792zS/1pcgnsHoqPmVsX8WQtOPxADK+g6SBBApZaDjJbm5u4cOOitkqCspBrn75eV3r1i32ys84GdnBxkNPyfLHyBryAqKy9lztLGOKGOmDw6NCXGYok+EiEkyMTfdFm/v/sUTKZr3feqFtoeyvoqICMKnOp/G4PfWcBDjgCbH/pLEh+uaGfYli+G14LONU0f9JZngCvCOkfH6S20hWlr9mBh58QcWSUL0ldXpU0tw6xzdsUz7bZrZzTV9MA2qYHtz9n80e9cAJNuchvSMSIFNQ3XL1AsJMRdQeP1UXF7R1y4qLz9vMztK5/Wp69LSks9UJDkO/cF08nmgI8YbA9Zwmf309eK+jw54RzhHiRUITcP7kAFT01jkRjY8012XXO/ZMeaK0EQ54iODGlxce8gVRLDNDZEP/Kzc74IHvdqLEOPYqH9gZUE5sxzi1K0ahSKmrx4fDJpImkDxKHo1jUIEWJoy0oJdAzANuwtgGtg0SztdYf/NNvt+HzaU+a29e8Bwf1qYm5NTsF7k4YcWCTDtS8MRiputJLf2r0n+cvzXJSVfHeMHkQbnR21bPXiFAGt0z+SbcnPcrgrUw3rHQjLWV0EOq9Fps7fPRzZ+R26ZName2N2H0HxHaAyggDYzAzRx2leeyUgyM2oqmC9GFUFP0MAU32cuVJIgG+tf+EGYJNdsJ86t/i5JlUb4p2LTxGqB/hKcNFppaX1IErC5t2vQZgu7wPHPReA/NGOy00NEuHDCSQMVtBhb2xdPLN49EBcQkIqxh6a/nToQrN1/xC8px3X0FD8zRLTSKnM8QvPFc3iy1UOevbRKJwSbgwDMVJCp5vZxplHgdRGuxSOtaaRlIiVsjybHsSpUqqSjYnoMaLD418/OnmI9qKKgM2hXC+p5bPyEAsqDg435QpYqHQPdmqUx381bCg9PoFYHpwa3ZfVX5zdIkJ6lIklAy9xeFqA/v5UMIP/c0Rp+03QsbpGR+yacNCQnHx+YEySv+vnWTT41UeNgoKnb+BGtA032x+ROHVE5xc90gOXUyyzjaIDBxhJn+vNCRK/rVgUXwjMNACA0Rl8x7nWesFUqpkdAmNxLxI0RgCa/yDZQ+6ZJHhw4Q8T8ojbto6IfuA5xvi6cc4IGeJDNDqJCNdlYjjk4owpoCpwc5S8+eG2hTCc8Ojg1v2QGuaYR3FFy4e4cNju+CKahE97e6EJAepAF12otqDiVdc/qT5Mp3QCTmuzkY27u+CLDJsNfQlPkCN+tkICodqfMKHzo55NycicvHJU5JX3oAOvfhg7mcUCTA80axdm2IufeZAXbe9dC+lIxCTROft5WdbBtoMJByg5wsya6zAIaLjYnM9BBIgiuNLs42DeYqvOib7lYOD9XaZQUYYHGD+Xz4A4j+5sqfShkpuspXVF/pyCARpn9nKqcX1pam6qxhAL6CNQBqD17CRr90vouYPxT2UimAs00jBlf2KTswWhZOpXEP0zIyFA973//5tkzZ64khNhH/WgO8MtKenfyFAjujr49yb7/oPRf2qEvMNCoBjqG2eH2GIFHL7Myvnp/AYA9GreoIqtsF6Ah9Du1B7hpESv6cMgdwDtYp8pfNdVlaJcMB2Zc1b/UPvDvY/+sraERBzRF/+7qSqCazK5wKJgoYZEtRLYkBAQJWyAiskkIGBAlYUeMEpBFNmXfiiI7yCJQUBbL5jCKimwVVxB1UFSKCjI9ONpix46jU2uny0zb6Wn7vR+6nN5zZs44h3P8P+57991377vfR9vZztdlzI6gz6/2fX0mJK3a/bb+aLP7vSf3fv/V999eKk5MTwcW2D//giuaynff/hkoYM9gqDmt+ubtvg8Ytro5dyG9OK8yoSzpbO8bv1gRpt497C435lTVPO2cr+2YxXjouP2OcaEBGDInc5sbgWbxzpGiT5cf59mIGsKaO8coNH86cPGzU88fDc7tImh+e/5xzvDPLx54+2skNmfGgiNnpu3SS4p+MVVyRycs3NQUdPUOQ5PbMhoXF8NoJm0spgZPXqh6TWVzfzInAIQzGSrot21/+s273397++BRtJVOnTqFRfU1+hpq+/7ww7M1O3+eKgcCza9u/vUvU1MLKNSibVh5Lr4MHC7usfagooi2dQSah8OVD8Mw/iSRzPaJffuE2hs0hB0zSbUEDerkn149nMyxV4bldY6dIanM+10XT2Y/P3P4FbnQHLj5eVV6/qmpA+9/Ct9MjD3MC5sUZy0c+d3Jq49WGqKMFWIMu3JFngqj6Q7aumaRFvpXM+6qe9Red3mxQ2OnA63R1NmJx+qvv9l5C12yUyi+olp26gdSpPn+byg3rxmBQ6EZPfzdFws3rzw/3JTd1X/uBN5vxJobS4hroq1CLzden79UHzajnOyRQJOBc7ZPxN/AGGqYSQrDQlucKJo6uXw6zy1WCd/cQEiDb4AmbXRi7gOFZuF+emL5rcO/vvkn+ObFjc663BZxZs3Eq69HH60o+9j+e9kO+pgHNW6V0uhPerV1NYW5SSoqP9qXcI4TIKqOyFBbl3E7qfLS04+3K1ERLy4ehiOwstDoVfvu4+f/Y2jVkJX2au6ff3w1Ovfq8GDaMPgkMolKpNRXyGMxosm4NCY5P9aO1DZE9QyNT4ZvS5120VA3mFXOnFsBmu6JI1O/Wzqc51Utn+nqfkRC2q73RRd//tmVF1c+3EWA3rXwIPtY3iefLf/0l3d/fODqjdK6RqXIs/Dl6MFTd3SUswIrhYIPakpDhwBa0NZ2cbSW3qQ8Ayf8uyQ7Dp5fwTWWO/Py+kE69KArf3h4OB+MKoBzEDXnzX/4+vNbX6/aLXSe8otJ3+nx42//Ovf88YVBCKgknHD0wluhUEshRtA1mGbefkBzbPvITC6Gg2ejOlzdBqJStNYjCOTGEwrmly+O/OrTpQvlXmflYXk4e0jg+uWR5Z99tnB1YRXNxK26xsv7h6eK3u4Cmkdbcppzexih10ubLj0vHVFOjntbmzppAQ2tQsUrjsHkCxsuWyCl2RnPYYOWrUBls3lhV15//wNQw3TlEywHCZyvv1dR++5vaNo+WLP+W2toDp/c9+zx6aqa9KPllZjaw2MhoXmIiMfWjmaasZBX5BYenwElQt94X7jS1VGZmsI0dJiUy5NwmenGK5PfwjcH9589Nl9edQYBGvv+yPLgZ1MlX+L0x5zWmf4koCl/XvT2pz8+UIJ35s3ycAPPutMvLw0+Kl2Rh0v8rXia6zVpputMetlwzXiDOYKVe6YXaOtiHQ/RvkqOJGj6k0FJdJAydJnyf/hOjf6Hr/srSXvz/n2qx/l5fnH2b2p2X/jhu8GqwabEo10YQQwMDPBNEUltIVnGZzI1nYBGnnm2Nlfe0Tce1SCvK5On+jK1MCTc+F80Xx4+vz9JvpJ1GkMB8M2HpYndn00dufjhANAcuVMeP3A5s3J3yZ9+S/mmuyu4gcfonS/tT79RupjbYm/NwgQcjaFQtbPnM/m8yVR3mtrmd5mBHLFf7A46fWd9ZFdyf39lfzLw5JWT/h+881e0rm7DMZ+gjw67DzQgK8uGmkXT3/86WAMZhSxQUoF5C6/NuIoAB5a+BlhOZJKoAbn5QC4ZQ4/CvF1heXCqGCOVHbkDzTpbShcxgPz+06VXw/fzgleyHk4QNAc+XJnYfepXR4o+/Bp/KLlRd7ut3e9c2sLNt3d3Ac2Wujb5kH7sSOnnw2Nb5pXjZhZ8rozmFE2PqZYhGggm7WNUVLfu8HJj+/rZh6iYFB6rp9BUJuHfq/1MuOcvm1W+f1BJwBzHG5PjgAPqtcjsxPSa85uHm9LXwKD/x+KKrRRcrh5YpJkMLlj+B+4pCZrJyYaR+fKs665iXb6gJbctC2i6R1/gifjSq1MPymsX60+/ABrclhfO7C6eO7Lr/U9/grU1Vn+vuT3OMa9q9IuLRVdxjcHGiXIQNs7nVaIAL3QypVewaAE8qcq2OMzVOPX0sYPW0S3N7dhsX3s7S5V7x47VZ+Ul91cmJFRWVuIfqtV8Cy9ZP/avgYEBTmVy1zDebKR/+1V6Ikh/8soyA7eBNocl44aEQLIBGlMEDXb+9lU04fL5zvK0HNcAXYakJdcV9f+x088nLr59uzSX1t+V8/JoFaYcgWb08J0L+Rd+emDpPdBcfRm8PbPdPtAr8vGpD0euombd2dXWIuBKwl3dTrT3SRxsK6xpHj2+MRZxvhpMhkPPrPFWuoWJeSC42ERW1vQTtcEUmgTYOdgnVOe8/3uVrZcqAWbtKc3t/fcTksvr6rODh69dCj5WX9cMpVgOSNswvC1TmEEfDzIshkDT19FrJ5fnKjsmO2YWdQ42hdXZ6WoPNeS2NW8ZGzs9OrH89ouluZMHh2tepl0o2UXQTDU9qsr/zcUDP/1A0IxdjzPvtfe2ik8cfDZB0OjUJrX0MDRSxnt3FKT2gd+EFhPbF7A5IwJkRPopQ5Lorc7WJjsC2WCWK6Dvu7yGhmC5jwn7E/cJnk/+TjOpxATY2iunnTuPnwCarMj6Y8evZTXWU1SOHCsPPkPfk6UfpM+CxskamvA4R6VcrgyP6pjpLr0F+hlfpkFPw0xbV+mNGy9Hp5Z/+QV6OvnFNc+bdh85ADRFV9JuVOWfWtp14Je/JlEszNXI3t7bjJ1Vdf7CC1Ln1cmrjmKpMySTqSHncnNbaHt2tESZ73HD6wJNPReJi2mQhbWJTyA7wJHtrmLpmntsFQ2FBaxx++GGyuOHaL/vr7y/H2Cot3Q7959ISGrOqouM/OoNnjlBsi+UELnxCQlcdBBFBAt2GG0HoHHMVCqVDZOTLWGlY7dGqyK9zFjjDTMD5aWPKDR/+seXV372bBhM/ydX0YymjVUNF9/cdfc9QXNjJNVHLGAb25zNKX4GNLhi5pQphVqagsmWJ+U6KzQj58vKcCNn+1BNJp/lIuSZgv4wpoDjFuC7I4ZW4Qol2K685OSEc9Dg8toJIwyFT66pfNtfST0BJFytEIY+UXY2rzCrLe/a7TrXQrAe+RLFBg0+0Ehb9Shqq/WraALjCJrwSeXKo9HPR08fczSW9Sln6svH7tx5tLB88eYPvx0dfHa+aW7u5JG7ZBL4xe6HnUfzX/36wIeLd3e9eJkb5eMpcdC3sj+Wc+vRHeKblXhlnx5DGK60S9ahpew1qc1tCTlULY5mMoBGHBJEp+9z38EJ4BRcU3MvJHJ8eclJlUADMG5kXXl5faW67klCwn1wyILX7T8ync2uro4WSa6p09WgCvS05ZtRBH2KjQSNIZmtJWg40w0ETbi8+86ZZ1eeB/uZi6eVeKl948yLMxPLF5d++BPQgLNp4tOlu8Q3V9OhN9HVtFT0q/cHiibCOvo8fIQuMlZoe/3OmhtjQKOD9wBCrrBvMnAbTVtk+WRxpNE9o5elpUXQBOx1hoxlTIGNt487XU3a3kKIWZOSkhJwsBNX4OO3v7FWQ7Jw4sRxCg1hQV0THS18Z9IOMOCOZlkxNDX52hjmCFpP+cbQUMOABzQeqQRNR0vt2MTEs6nniWU2vlHykbrhO6DomkDr+odXc/DNb+aW394kaHZdfT68GJxU/vjq0tsvi848HJj2lQYIHfQ8U6Yz989vIffVSMeWPhcnB4ELbb2BMKZSZ/6Yyetpb2Y0g5UilJnsUUPNH6z+GXiaLe2ddIUkN8AkxFPrCg9+37lfU6O5g+DPy46g20FxUfrFRUDAuzfmHTg0kf4HeFrxgQajkPrWugb6UMo0ZGoSNJNG4Q1KubKlIezR1Rd4fJOTxPFtl4/UD5+5+mJhYeHm3F8Pz+0+lY/HBB/+tIrmRuL14LKdR6+UvP8Z5oGVUT3GIWKuNpMvtt/mF6YDW8zzmxZq442mui7L/lCdTmewiXmvHlMLaARsBcWvbm3prnDGR8dGtbeD+7esLAGSohQayEeqqVn7cNxghPgXaLwDfTMpXds4a3PwbwpSuCwCYJVi0UJXXxukbrrwjYNkvHfTKhpl2B3Ub28uDCZvtwOa4PJHL6ApdeXmlb/9DFfWYvjm/fu7P/4x9s2dyODcCE754xdffrGEmDbQJ9ob7cRYv4HvEmdeSBUTuhPie1nracwNhty4Q/U6i7WWT6b1qJUmYAchg4Z3LIKuYb5BGtfb21sdURZ3DtKPRAPSxidmHcoBISBwhlHE4ISjHiq9cYBj5xwYKxEiK8PQMJOppQU40fRobTKtRu0bwXi1UUeLkjDqPDxz5MgvvpzajbkGsMZcfzAKLZmbIAn7+hdzePjV9HzpT28PgPGA8IAcy223S6hZWP7dr66eWWkZF0S3EskqSCLZBcA5KADUPE21pxkYqhtyIxT1Ot05Jk/a2VpamBWVyIJ+pLqZSESsA4Ponr2inuqe2Dh7P2JwDscGe0bNImMToWZeMw8b7wCgkWDiwdzZVyJwIUc/CBPBOQo4G501GZpaEABFTOMKZ6s3YWj9v2iWHucf318YPL94cA5orlwZHf3Fnw8fbvo0e/DKzd/99O5doLlT1TzTIfLD/fPUyRd35jtmhbb+oB8heUuc20w3stUbpbezaHv1DDcYOlSb1Ol0h311r12swdTEWhD7q1m7E/GOH8Ho0hSBRAKB8dBQjAFQqvwZ9B+tk/qAwJQy8NBSGrG+qL/E9lRnBKEVzwOJG3GGOuh5NTT9/fmaGhT14Coao9QOzOB3NAJN0adTy89P3Y/Pql15+fnukpKLX8I7h//2i0E8ec8fvPnF+113DyAZe4krWa9fcw7EGa48mgcni62/rRO4G3lDoZtSRxa70eXZT6so0DNUZ3J73fOA5s2b5lC+loY+VyLyVzHZZmcjvUYkmKxNxS4ikUuKr1hMyNgdfe04UlW1rT7GRLIEY40UM7KxrSdbTEgHY+33SQVr+rhMw1U6WA2LCsaab7QImp6CqI4WmLJqAnWkqYuj6f1Jkajj5B8uQgl+Co/YfriUlv8ATfi5uV/8FGhASpRYKJ8W+xZev16e9mhkUuCgr9grTuE5gCtyr8d0S0vDTBitx9KYhZUmm5ZeQn3jtcnlOG0tDYaTcMhdtQCs6aE+MXRVuiJa7EBm5WRsFjRmCF97YIYqzZ0o2RM0ISEAxDRjGLC4PBGcyN5jC4oABxaFRpdi6WXSN2oDDfmThj7QjJu2U2L5coLm7VTJzcGDyUDz/PzP0FBYRp1n8NLxo10Jlx6cOrzw6dSuA0UlZ25UNbdFcfm+qcfq9yfOTEocZLYVe21l+oYYsF4fwh7vkNNmqy09CBpWn883K50jT7bGV7MIpw9vyGePz8BAaq+9W0hMjNRKBnNiUcS/Np5E3SFEVTXEyhZTaIBDSUpi5lwfaAjZgZGzWCjicVfRGBoSfltDuro2gcM0JOtYMDsekkrQNNaexivz9+/BrJlP0Ix+erJoV8kUAkDax+O4TnxSObz7yskPRfAN0pfI9skULQgGKMvOBXf0SIaGRNH+G5kbiA6XbYigg9Yz3lthg9lQXYNZN/fGlRmva9uneQwNTSy1AIsCZe5AanVsJsfMg0XM0xMMr3xQTSPD9w5R3VPgjSlNSukH1OiYoIdvZASNcG9QCuFWJaclOCmoCK1hsdFAH3A0QEGMAdzx3pBURAF5bdjzkl0Hpt6CsSu/P7/p8ejjf148sjy3Oz376NOn9XjPnoeZRvDQFSEKjJXmlHUIbJniqIE6cxD0yTvGBbKg9YSFRNNBGFJNwz12UmoFdoj1DEFczP6RmYit0giJJyEsdRFUWNaNyDumEclSAtgsPU8cfqBijiYc6CAO36uyx9RTnyhvMOEc7BwtMCVTvhGKYkxWWXkNVj+fOm78FRTfrSZyNqKfGQs0ADPycBRolr4omTpc3N+V/nj0yj8/XKVcc/TS6y480M8fzq7JP3/lBdjvwMtgH97HZTj1Kmufni7Vmeno4TGsEfbJxuiR0sIno8I7CoxTNNeDUK33zVeuue3u1nbVYgYWDVfoEXRiZEYZ3hsL/mYZC79ZgNHCt8OAyF+NrmtlRf4fFYRBxo1kzAlokLAGSYGG6wT48A1xDUNbu9WaT/kmepUEIc7UdUCZez2nam65aNfFXyyhgdF/EO/Vpz59tjyB6mh2dvHTj111EJpN6Bo+Xvzyzh1onHVGpE4OueAaPrDt4ZaVhlknDYW/FUiz8ZdyaXYNyJOUnAKB9np1LafZwH3oFBnTQ2Lt9fhkOYgVGQMjqBFV24uQb3nCM1j2AEPBCXFWszBiwDCIip1OoaEYk0GmyrLYyAMYPeQB2Cer7OmaCmczBh+OZGKimxToTC8PNAbXXh98jF7u1D+Wpl6lfX7wM6B59fXUwujjC7ubErO+uVfmyNlh7ticdBxERrDuurhwcEPhFPD2KgznIQPcay1lCckgPy3jCS5/qGBlCPSY4PiAs/w5cdu2OpvHgjaEAThG1o74ifC+WKEoVMzSJ65ZA2PIrFBV84/GRyILoz4ZRwohF5eR3xPbYi+XAkMCMozQwTPp/mageEZABxq2qPryPcfGxkbwuFWNLpcs/eO3U69+k5+f/hj9th+wSSbmHiZGFm6PseMYG23aVlZ34pPazsXF0i3djUmxEm11Da6AZ5TBcpE58QKMKyxN+Uwaz2OTPGxmJqxN6ueggaXG7WFbW9rYSNcdCozj6pNlY4BJWXKjGpeIUnyhXUgtKmK6huv9abRWnCLYFICjQf3Xqm9AqsW2VsgAhuKkouQaESIqVKR8Y2OCx8wY9PLVqV5P6gEmsenC6MTF5WevIPH5G5DcLky8+icOnIUqqADHhUjZ3rbGxt5+zVlPMFF+B3ey7shAoQFYY1IkgoJNQ33hkHsNcA8ypmlw2RlghZvvDDNnS7RRe9AXxprSLY1Mt6qYBAhl+trgXZXSN0W1tHREYfhXLCYne7Tu6i9bV+qMbIdJUjAAIUkyxV+PJewAhjAWhYaEZ131Vd9Ea/iv22RlDkp3EhE5vnFnCxPe1QNMetPuV7ibvf1ieWFud1PN4bkru/+xVAKiqeZMNifISMz2trEJiG1L/KbzBsAgtQxmz3KZEEPt6eNkgq+rD18mtaSBJsrvTV7pFtD6Jd3rkWnBOU4SXwVdITVxVrHkgB0HLxJ0LSy8+ybDwcUqSBHLwGS6phgPGjCamrU65ScsMmSVFBgnrDPCQWygKnXAYUMyTsqTyGvM/Olm3jB8nDfbzg9SEMlv6hMTc2qqni+UFBUtf7F79ELawc+L8dLl1rOm9EI/sIlzDmX4horsq1PrBs49la9sKUXSrxSzx4VgtpFETdqeBasiPixwK00TMVnE2fmSwM25Zy8gzgE7jpX1Hn+TGLqKv5mMwNENUlGIier6+BASYieWpzbJTQCoYg9cA1yUqVNp5do6c4Ep9ughPgMOogBMg8+w0pc6FwTYrZpXJqjs65LfBQPL49GF5SO7dpWcPFiVlnf8dmVx+sOaB7ePRjaXZfr6+rj7xKemguxWHBLiO6mURwkhgOrdQ2nnCARmZj194xKezSaatzYSc1ncvZwbuLV1J5hPO2hSd0KREV01SAEdbHqFmYEBfy+EJDNSiCJ+D8nvWZB5Icc5tWsUQEV2BZEf2YjFRB03lHP4KiEuJH3CT+P4J17zZMus/GPckLI6Eq5wP6CJvP86vabqNEiCv1wuubqQ9iC9Pun20/vD6YNVn33bdbQwKeJyoWtEYIi7sbYuKEkYVmZWLkNI7cJFmfZRQzIGH2UTEA7jdTst0wZLS9cqlnOrlNyor5v79egRLWu9FJ50j6oFOPhpUMhsbfVHBm2t6eBCshWBiEfBIccO05+2ZyMTqNQpOMADTmty3hD3OJiss+U5gAme+mGYPsmEAjLom7wAhVjmuaTCyE9eZwPN6ceww1U1n+SnZ2M8vJKg2f16mHASH2tLrRZ40yuwFAhNF9tUPDQeHtbdPeIY6KCtvkHLQCzWbZWyaIuFHjw++FXFEU+bsLW2dJcb94q0mfgeFk+vAtUA631Em4ZGaQk479V34uIbebzVXze+UJ9RoUJXJycjhQcGNMQ5+ogDXNlGuhmPS4GBZ1B9srUiyjQFql9hle2EkaJb+XDXN5/U1DSlpx0tPgrN8v7+nMR66LEeBatr1TfPR1/Ogx1awGOB445QeDBx+fYRtSzqwFZSjcR6zA3r+U4pIuNo2hadWjcxS2u9oXZ1ZmUpVby57dbLY4BumcFyMGjFzD2ZHIKMkwpkG633amqTIgUiFlxjAF0L2HpnlY2ARQFai3L/QeNgG9SqJ2Pp4bhF7QmHzKrqkzvtnRuBQlVEE/Lyj1a+LgaYNEx/ob8N7sYaIDuajcBw9GPNw+sD7UM8PUZrEEU4jfre+GRIR+4KVANXRtpM43i2uobaYIjbQbuho9MZx+Fh32vJmp8Wj40Bb/C90FkuFOCRAjgxFCqU0A6Ej2AbTTXx0f9nDKY1IXEiRnY6cy2uUTFaBuIQ5xBP5Gh8M5IGkZscVFx81jl/BBYMsqJafb+yfDg77Zv7Tai7pwFN1enHt/LTiWA+BJ3qCu9h78eKNRBl9rTaEib92b7xngJdAXgUleBUDQ2xhz4E+CGV5rQzj7oh8GiewkVyxre/fCnxESJfd9cm+x4ZPgxwDKL9aRCWDWpVZ+ix4A894gPyxespM4RpqTurBWlhg68aA/ew1QNHj4DhWllbe+gRz0A4iQJzz/yJO83d6zgZ7zxxguojFKelH3+6e3BwcPcFpJqjLxNvV7qexTCRGwJepo3pJiuiNcJUBG1gxUb1ShwMtFoVpjLJkIjLdeGZclwb5PKGSSPa4J1H3Z3dnWXbhHpYa/oRyaitYa0tJhT4DQEOSU8MdIMIHP+NGtjWMKx/TdxTqBC2pviC1eiPZGYtowQO6gedyAaDsYP2mLEIp4sxpTdEhNvc3FUyTpxDfwHtrFvlXcPZiU01vz9YVVV14fTpC1UPa643ej0568cBL7qNr31snE2MEVsPPfKo6dhQs1YF6PI3MPmaTOl6A6SYkqi6yKxMqNnR7FDnhdDjYi0nwEWbuZ6pNx1/P52E6RUvI18JV5vK3m03OpNiTashWTwUHGwQoPnPjt9orabSSlSr1pbY2oYhR6cIshGeqtZssUyG+xAk3TjQSnE8Ef/k0KGd5VnD6P8exe7IRktk+LviGjxZRV/kWGN9W6GXT1yot1kB0zYANaxY9x3TrspcJInjQm6rNTniiCy3QYWtIGoGHZ4bY7XvkkE96ZYIFlfwE1/eIXBgYOuwe+OPAw7e6O6XclDYZZBv08LWwUiU7urvHXsf3tFacw5Bg0EJ+kakbJQBz3+OTiGu0RIrlRgRpdYXmhIKZgRI0yUl5+Xtz/ju43kgKS5GBxid0+FLf/wk+yhuMHVZlErXDp84Ecq8tmxRr2tj85OwTihRt/RJeE6m/pR0NbivXGY3yVcIljNXJ/pzaLYBZwNPhIE4fiZsxtFKIOMDjtN0/MfsMZyhYZmtxiLEEawdxkbI8ND/jYbAIe75TzxupaupBZG8Rp0YVZLhU0dnighwCmgZsbFDQyjxwOIokTTSK+3qf0f/fX95eT9p94CHrLLym+/vZ3WVJ0PpzRGKhcY+sbgXxl5uk1+/HvluZD4Md5FZIVdboaDu4gynoQ7lNhSagOVFyejHZJqmsWdo0o6kkRm5Uj4zsIMtkTFwyLCq4590nS7tXgwrc5d681hktTEVqpjAVSdbmwpUTpCtI1FKgwBqpZNTaANlBN1qUYCv7ykTp4jsfWOcOdURlJXBzp0rSyDtOLQYX9MOXUKnhzR6nu48jrbV0zfbk5NQOkUZKDMu3jy+rvZ6rXwAvP3uHrHhHZOzPAbW9B4EI64gqiG3wUc02RCGFsLLLrcCSxrP1syDXeZ4rzm4saWjQR5RIJM4ETjesXHbj9eULq6MXDbyD9FzIrtkozX0eCo0VjNjrCGYA3W+oy6uUPnRngr1/4AhcIiCpa0nKwBZop+5pYnbKhTURdHrIXYc9oZ2CO03QPkPmuSnJicuJ5XFQ7unvnY+B1u7bDrFlqll4KnvzTbeS0TlNigsWpnszPh4r03Rnraemcl5+wv2bvK2oYlFLD7RNLExb28LT8UVOjBELOBqaxlq6YsizM2zOjvnZzq8FUEbPbS1NU0VdLU1OEQVCQsIJoCBrR0HkrM6QrYutdRIkKMkrkjhlmhR2nsVWGZszzyR6ehFSBLW7Amm9ajOKLH9aP0kJTdnJe/rwq0ArO6gDEWszQ1UaGNN6cm4Tsgk1J2D+E7CSZCQd3aOhA+ZmWS6tlWL2Wwja/Y0bSXCN9TTmG/jGOdz72x7L8mMjaEaAZpyJpMRAMa52/XzI7kNscb+zlCyq1A4YzKV3qqLqIZCjJAwsxODimVPD083qNUKaQ6VDKBUAMOBD80wDjtA7Iudb+5v8cbNLpAir8A/RKrJUm3f0/tkzVEt+q6syKOJ6TmHhh8+rHp54w65h3WGNfQpeE6Ei9BQkwRisUV1wwj0IbCjlR0R37S120WjTZYy5B2xQhvrljv68qz4Ht6hmQV4WGUvEAwJpSYBkhRCtw7RE5HHm/3BMxCWEHpY0ome9pp+VTRDG2stRTIbFd7S0KBEKWEyqs83pNWYcIYKRSk8LupsxNgBAb4ECilUv1Pd92S/F2XwxH2QsH97bfMfHgxHIjqTF97psOsHNw8H1+aEPVyZDyN/75DMf7WSoKEtQ+qfei8MSAByBFIQPmfZ0brAIopqsDvWTSNc7G1sEdfKw9Y7xfcrExvUYHkunv4KbwFo5fhaWoiMgdI359qULeF9AiddE+s9KkSIjw5BS7LD9bg8Yew08KD1KkcxPNzXvcI7dpzY7Gx1T08sZdgu1dVQpCyLUctIIgY35JXjXQ4OmYNPv//j6/vns9MQliOHu5rz7Jy/im92rWsb6EjtEeOwQKFP5oAoLxifxK8t021mfh5IwmYGqk1M+baeTjzJpDJX6Z7qSjtzBq3B+V62i8wWcMRs9yCph6ennmfFnq0eIiEhAY020+ameLiHhKaGR40PURCNTDP2VpBMjdRotPj6UMqXzE5Phre0DLS0hPvFOBvZY91V98xWz6IdMj3dDh03V1jha1X3vLo6tHLrhiOJFROLjOy69PvN+97tJw2gQA5OVah/bOOYc8QsPi7cTL4n09SquqOBKPUMCT39PXvCsRKixEx/NX/M/kUpZ0aUEQU0d2Pa6TMEjs6MvY3ICXBs2N7udGeFu7TCfx3qSClCHhotSIv0ZSwjhZTdM9sjoWRRYdQ1BUcNdrwpwrDtahSGlhvHEhc5e5wQMPw5NqIaLbX21ELX5nc0yzwKBABlZXV1dZWXo/tbiabcCcjNxZibgw7KxwOtKw6RTffxt7TyxN7koUrWWBhhtyOAZ4CUS0PTCgpYRq0WUCR2trSNiDxWn+eVoULPMP8XySriJDZMTqwAAAAASUVORK5CYII=\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Vibration card with background\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"mm/s\",\"decimals\":1,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "vibration", + "pump vibration", + "motion", + "resonance", + "dynamic balancing", + "mechanical integrity", + "pulsation", + "frequency" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pump_vibration_chart_card.json b/application/src/main/data/json/system/widget_types/pump_vibration_chart_card.json new file mode 100644 index 0000000000..4b10ad0ee2 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pump_vibration_chart_card.json @@ -0,0 +1,35 @@ +{ + "fqn": "pump_vibration_chart_card", + "name": "Vibration chart card", + "deprecated": false, + "image": "tb-image:dmlicmF0aW9uX2NoYXJ0X2NhcmQuc3Zn:dmlicmF0aW9uX2NoYXJ0X2NhcmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxnIGZpbHRlcj0idXJsKCNhKSI+PHJlY3Qgd2lkdGg9IjE4NiIgaGVpZ2h0PSIxNDYiIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iLjgiIHJ4PSI0IiB0cmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSA3IDE1MykiLz48L2c+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZD0iTTI1LjcgMjYuM2E0IDQgMCAwIDAtMiAuNWMtLjQuMi0uOC40LTEuNC40LS42IDAtMS0uMi0xLjMtLjQtLjUtLjMtMS0uNS0yLS41cy0xLjUuMi0yIC41Yy0uNC4yLS44LjQtMS4zLjR2MS4zYTQgNCAwIDAgMCAyLS41Yy40LS4zLjctLjQgMS4zLS40LjYgMCAxIC4xIDEuNC40LjUuMiAxIC41IDIgLjUuOCAwIDEuNC0uMyAxLjktLjUuNC0uMy44LS40IDEuNC0uNC42IDAgLjkuMSAxLjMuNC41LjIgMSAuNSAyIC41di0xLjNjLS42IDAtMS0uMi0xLjQtLjRhNCA0IDAgMCAwLTItLjVabTAtM2MtMSAwLTEuNS4zLTIgLjUtLjQuMi0uOC40LTEuNC40LS42IDAtMS0uMS0xLjMtLjQtLjUtLjItMS0uNS0yLS41cy0xLjUuMy0yIC41Yy0uNC4yLS44LjQtMS4zLjR2MS4zYy45IDAgMS40LS4zIDItLjVhMi41IDIuNSAwIDAgMSAyLjcgMGMuNS4zIDEgLjUgMiAuNS44IDAgMS40LS4zIDEuOS0uNWEyLjUgMi41IDAgMCAxIDIuNyAwYy41LjMgMSAuNSAyIC41di0xLjNjLS42IDAtMS0uMS0xLjQtLjRhNCA0IDAgMCAwLTItLjVabTItNS40YTQgNCAwIDAgMC00IDBjLS40LjItLjguNC0xLjQuNC0uNiAwLTEtLjItMS4zLS40LS41LS4yLTEtLjUtMi0uNXMtMS41LjMtMiAuNWMtLjQuMi0uOC40LTEuMy40djEuM2MuOSAwIDEuNC0uMyAyLS41LjQtLjMuNy0uNCAxLjMtLjQuNiAwIDEgLjEgMS40LjQuNS4yIDEgLjUgMiAuNS44IDAgMS40LS4zIDEuOS0uNS40LS4yLjgtLjQgMS40LS40LjYgMCAuOS4xIDEuMy40LjUuMiAxIC41IDIgLjV2LTEuM2MtLjYgMC0xLS4yLTEuNC0uNFptLTIgMi40Yy0xIDAtMS41LjMtMiAuNmEyLjUgMi41IDAgMCAxLTIuNyAwYy0uNS0uMy0xLS42LTItLjZzLTEuNS4zLTIgLjZjLS40LjItLjcuNC0xLjMuNHYxLjNjLjkgMCAxLjQtLjMgMi0uNi40LS4yLjctLjQgMS4zLS40LjYgMCAxIC4yIDEuNC40LjUuMyAxIC42IDIgLjYuOCAwIDEuNC0uMyAxLjktLjYuNC0uMi44LS40IDEuNC0uNC42IDAgLjkuMiAxLjMuNC41LjMgMSAuNiAyIC42di0xLjNjLS42IDAtMS0uMi0xLjQtLjRhNCA0IDAgMCAwLTItLjZabTEzLjcgNC44TDQyIDE3aDJsLTMuNiAxMEgzOWwuMy0xLjlaTTM3IDE3bDIuNiA4LjEuNCAxLjhoLTEuM0wzNSAxN2gyWm05LjkgMi41VjI3aC0xLjd2LTcuNEg0N1ptLTEuOC0xLjljMC0uMi4xLS41LjMtLjYuMS0uMi40LS4zLjctLjMuMyAwIC41LjEuNy4zbC4yLjZjMCAuMiAwIC41LS4yLjZhMSAxIDAgMCAxLS43LjMgMSAxIDAgMCAxLS43LS4zLjguOCAwIDAgMS0uMy0uNlptMy45LTEuMmgxLjZ2OWwtLjEgMS41SDQ5VjE2LjRabTYuNCA2Ljh2LjFjMCAuNiAwIDEtLjIgMS41IDAgLjUtLjMuOS0uNSAxLjItLjIuNC0uNS42LS45LjhsLTEuMi4zYy0uNSAwLS45LS4xLTEuMi0uMy0uNC0uMi0uNy0uNC0uOS0uN2wtLjYtMS4yLS4zLTEuNFYyM2wuMy0xLjQuNi0xLjJhMi40IDIuNCAwIDAgMSAyLTFjLjUgMCAxIC4xIDEuMy4zLjQuMi43LjQgMSAuOGwuNCAxLjFjLjIuNS4yIDEgLjIgMS42Wm0tMS42LjF2LS4xbC0uMS0xYTEgMSAwIDAgMC0uMy0uN2MwLS4zLS4zLS40LS41LS42bC0uOC0uMmMtLjMgMC0uNSAwLS43LjItLjIgMC0uNC4yLS41LjRhMiAyIDAgMCAwLS40LjZsLS4xLjdWMjRsLjIgMSAuNi42Yy4yLjIuNi4zIDEgLjNsLjctLjIuNS0uNS4zLS44di0xWm01LTIuM3Y2SDU3di03LjVoMS42VjIxWm0yLjItMS41VjIxYTMuMiAzLjIgMCAwIDAtMS40LjEgMS40IDEuNCAwIDAgMC0uOS45bC0uMS43aC0uNGwuMS0xLjMuNC0xIC43LS43YTEuOSAxLjkgMCAwIDEgMS4zLS4zbC4zLjFabTUuMiA2di0zLjZsLS4xLS43YTEgMSAwIDAgMC0uNS0uNGwtLjctLjJjLS4zIDAtLjUgMC0uNy4yLS4yIDAtLjQuMi0uNS40bC0uMS41aC0xLjdsLjItLjkuNy0uN2EzIDMgMCAwIDEgMS0uNSA0IDQgMCAwIDEgMS4yLS4ybDEuNS4zIDEgLjhjLjIuNC40LjkuNCAxLjR2NC4zbC4yLjZ2LjFoLTEuN2EzIDMgMCAwIDEtLjEtLjZsLS4xLS44Wm0uMi0zdjFoLTEuOGwtLjYuM2ExIDEgMCAwIDAtLjUgMWMwIC4xIDAgLjMuMi41bC40LjMuNi4yQTEuOCAxLjggMCAwIDAgNjYgMjVsLjItLjYuNS43LS4yLjZhMyAzIDAgMCAxLS42LjYgMi42IDIuNiAwIDAgMS0xLjcuN2MtLjUgMC0xLS4xLTEuMy0uM2wtMS0uOGEyLjQgMi40IDAgMCAxLS4xLTIuMmwuNy0uNyAxLjEtLjUgMS40LS4yaDEuM1ptNi44LTN2MS4ySDY5di0xLjJoNC4yWm0tMy0xLjhINzJWMjVsLjEuNS4zLjNoLjRhMi41IDIuNSAwIDAgMCAuNiAwVjI3YTQuMiA0LjIgMCAwIDEtMS4xLjJjLS40IDAtLjcgMC0xLS4ybC0uNy0uN2MtLjItLjMtLjItLjctLjItMS4ydi03LjNabTYuMyAxLjhWMjdoLTEuN3YtNy40aDEuN1ptLTEuOC0xLjljMC0uMi4xLS41LjMtLjYuMi0uMi40LS4zLjctLjMuMyAwIC41LjEuNy4zbC4yLjZjMCAuMiAwIC41LS4yLjZhMSAxIDAgMCAxLS43LjMgMSAxIDAgMCAxLS43LS4zLjguOCAwIDAgMS0uMy0uNlptMy41IDUuN3YtLjFjMC0uNi4xLTEgLjMtMS41LjEtLjUuNC0uOS42LTEuMmEzIDMgMCAwIDEgMS4xLS44bDEuNS0uM2MuNSAwIDEgLjEgMS40LjMuNS4yLjguNCAxLjEuOC4zLjMuNS43LjcgMS4ybC4yIDEuNXYuMWMwIC42IDAgMS0uMiAxLjVzLS40LjktLjcgMS4yYTMuMSAzLjEgMCAwIDEtMi41IDFjLS42IDAtMSAwLTEuNS0uMmEzLjUgMy41IDAgMCAxLTEuOC0ybC0uMi0xLjVabTEuNy0uMXYxLjFsLjQuNy42LjYuOC4yQTEuNiAxLjYgMCAwIDAgODMgMjVsLjQtLjd2LTIuMWwtLjQtLjdhMS42IDEuNiAwIDAgMC0xLjMtLjhjLS4zIDAtLjYgMC0uOC4yLS4zLjEtLjQuMy0uNi42bC0uMy43LS4xIDFabTguNS0ydjUuN2gtMS43di03LjRoMS42djEuNlpNODggMjNoLS41YzAtLjYgMC0xIC4yLTEuNS4xLS40LjMtLjguNi0xLjFhMi43IDIuNyAwIDAgMSAyLjEtMWMuNCAwIC43IDAgMSAuMi4zIDAgLjUuMi43LjQuMi4yLjQuNS41LjlsLjIgMS4zdjQuN2gtMS43di00LjdsLS4xLS45YS45LjkgMCAwIDAtLjUtLjRsLS43LS4yYTEuNiAxLjYgMCAwIDAtMS4zLjdsLS40LjctLjEuOVoiLz48cGF0aCBmaWxsPSIjRjg5RTBEIiBkPSJNODkuNSA3OC44djNINzMuMWwtLjEtMi4yIDkuOC0xNS40aDNsLTMuMiA1LjYtNS43IDloMTIuNlptLTIuOC0xNC42djIyLjdoLTMuOFY2NC4yaDMuOFpNOTQuNSA4MXY2aC0xLjJ2LTcuNWgxLjJWODFabS0uMiAyaC0uNmMwLS42IDAtMSAuMi0xLjUuMS0uNC4zLS44LjYtMSAuMi0uNC41LS43LjktLjhhMyAzIDAgMCAxIDEuMy0uM2MuMyAwIC43IDAgMSAuMmEyIDIgMCAwIDEgMS4yIDEuMmwuMiAxLjJ2NWgtMS4zdi01YzAtLjMgMC0uNi0uMi0uOSAwLS4yLS4zLS40LS41LS41YTIgMiAwIDAgMC0uOC0uMSAyIDIgMCAwIDAtMSAuMmwtLjYuNS0uMy44LS4xIDFabTQuOC0uNy0uOS4yLjItMS4xLjYtMWMuMy0uMy42LS42IDEtLjcuMy0uMi43LS4zIDEuMi0uMy40IDAgLjcgMCAxIC4yLjMgMCAuNi4yLjguNC4yLjMuNC41LjUuOWwuMiAxLjJ2NC44aC0xLjN2LTQuOGMwLS40IDAtLjgtLjItMWExIDEgMCAwIDAtLjYtLjUgMi4yIDIuMiAwIDAgMC0xLjUgMGwtLjYuNC0uMy42LS4xLjdabTgtMS4zdjZoLTEuM3YtNy41aDEuMlY4MVptLS4zIDJoLS42YzAtLjYgMC0xIC4yLTEuNWwuNS0xYy4zLS40LjYtLjcgMS0uOGEzIDMgMCAwIDEgMS4zLS4zYy4zIDAgLjYgMCAxIC4yYTIgMiAwIDAgMSAxLjIgMS4ybC4yIDEuMnY1aC0xLjN2LTVjMC0uMyAwLS42LS4yLS45bC0uNS0uNWEyIDIgMCAwIDAtLjktLjEgMiAyIDAgMCAwLTEgLjJsLS41LjVjLS4yLjMtLjMuNS0uMy44bC0uMSAxWm00LjgtLjctLjkuMmMwLS40IDAtLjguMi0xLjEuMS0uNC4zLS44LjYtMSAuMi0uMy41LS42LjktLjdhMy4yIDMuMiAwIDAgMSAyLjMtLjFjLjMgMCAuNi4yLjguNC4yLjMuNC41LjUuOWwuMSAxLjJ2NC44SDExNXYtNC44bC0uMi0xYTEgMSAwIDAgMC0uNi0uNSAyLjIgMi4yIDAgMCAwLTEuNiAwbC0uNS40LS4zLjYtLjEuN1ptMTEtNS4zLTQuMSAxMC44aC0xbDQtMTAuOGgxLjFabTYgOC0uMi0uNWExIDEgMCAwIDAtLjUtLjUgNCA0IDAgMCAwLTEtLjNjLS41IDAtLjgtLjItMS4yLS4zbC0uOC0uNC0uNS0uNmMtLjItLjMtLjItLjUtLjItLjggMC0uMyAwLS42LjItLjkgMC0uMi4zLS40LjUtLjdsLjktLjQgMS4xLS4yYy42IDAgMS4xLjEgMS41LjNhMi4yIDIuMiAwIDAgMSAxLjMgMmgtMS4ybC0uMi0uNi0uNS0uNS0uOS0uMmEyIDIgMCAwIDAtLjguMiAxIDEgMCAwIDAtLjUuNCAxIDEgMCAwIDAgMCAuOWMwIC4xIDAgLjIuMi4zbC41LjIuOC4zYTcgNyAwIDAgMSAxLjUuNWwxIC43LjIgMWEyIDIgMCAwIDEtLjggMS42Yy0uMi4yLS41LjMtLjkuNGE0IDQgMCAwIDEtMS4yLjJjLS42IDAtMS4yLS4xLTEuNi0uNC0uNS0uMi0uOC0uNS0xLS45LS4zLS4zLS40LS43LS40LTEuMWgxLjNjMCAuMy4xLjYuMy44bC43LjRhMi42IDIuNiAwIDAgMCAxLjYgMGMuMiAwIC40LS4yLjYtLjRsLjEtLjVaIi8+PHBhdGggZmlsbD0idXJsKCNiKSIgZmlsbC1vcGFjaXR5PSIuMyIgZD0iTTE0NC44IDc1LjJoN3Y5bC0zLjUgMi42LTMuNS0yLjd2LTguOVoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNDggODcuM2MuMi4yLjUuMi43IDBsMy4yLTMuMi0uNy0uNi0xLjQgMS40Yy0uNC40LTEgMC0xLS41di0zLjhoLTF2My44YzAgLjYtLjYuOS0xIC41bC0xLjQtMS40LS42LjYgMy4yIDMuMloiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik0xNTkuOSA4MS4ydjEuMmgtMy4xdi0xLjJoMy4xWm01LjMtNC4zdjguNWgtMS41di02LjhsLTIgLjdWNzhsMy4zLTEuMmguMlptNy40IDBoLjJWNzhjLS42IDAtMSAwLTEuNC4yLS40LjItLjcuNC0uOS43bC0uNSAxLS4yIDEuMXYxLjRsLjEuOS4zLjYuNS40LjYuMS41LS4xYy4yIDAgLjQtLjIuNS0uNGwuMy0uNWEyLjUgMi41IDAgMCAwIDAtMS40bC0uMy0uNi0uNC0uMy0uNi0uMmMtLjMgMC0uNiAwLS44LjJsLS42LjUtLjIuNy0uNS0uMmMwLS4zIDAtLjcuMi0xbC41LS43LjgtLjRjLjMtLjIuNi0uMiAxLS4yLjMgMCAuNyAwIDEgLjJsLjguN2MuMi4yLjMuNS40LjlhMy43IDMuNyAwIDAgMSAwIDIuMmwtLjYgMWEyIDIgMCAwIDEtLjkuNSAyIDIgMCAwIDEtMS4xLjNjLS41IDAtLjktLjEtMS4yLS4zbC0xLS43LS41LTEuMS0uMi0xLjN2LS42YzAtLjYgMC0xLjIuMi0xLjguMi0uNi41LTEgLjgtMS41LjMtLjUuOC0uOCAxLjMtMSAuNi0uMyAxLjItLjQgMi0uNFptMy45IDIuMXYtLjVjMC0uMyAwLS42LjItLjhsLjYtLjcgMS0uMmMuNCAwIC43IDAgMSAuMi4zLjIuNS40LjYuNy4yLjIuMi41LjIuOHYuNWMwIC4zIDAgLjYtLjIuOS0uMS4yLS4zLjUtLjYuNi0uMy4yLS42LjMtMSAuM2EyIDIgMCAwIDEtMS0uM2wtLjYtLjYtLjItLjlabTEtLjV2LjlsLjQuNC40LjEuNS0uMS4yLS40LjEtLjR2LS45bC0uMy0uM2EuOC44IDAgMCAwLS41LS4yYy0uMiAwLS4zIDAtLjQuMi0uMiAwLS4zLjItLjMuM2wtLjEuNFptMy4xIDUuM3YtLjVjMC0uMyAwLS42LjItLjhsLjctLjcgMS0uMmMuMyAwIC42IDAgMSAuMmwuNS43Yy4yLjIuMi41LjIuOHYuNWMwIC4zIDAgLjYtLjIuOS0uMS4zLS4zLjUtLjYuNi0uMi4yLS42LjMtMSAuM2EyIDIgMCAwIDEtMS0uM2wtLjYtLjYtLjItLjlabTEtLjV2LjVsLjEuNS4zLjMuNC4xLjUtLjEuMy0uM3YtMS40YS44LjggMCAwIDAtLjgtLjVjLS4yIDAtLjMgMC0uNC4ybC0uMy4zdi40Wm0xLjItNS4yLTQuMiA2LjctLjctLjQgNC4yLTYuNy43LjRaIi8+PGcgc3Ryb2tlPSIjMDAwIiBjbGlwLXBhdGg9InVybCgjYykiPjxwYXRoIHN0cm9rZS1vcGFjaXR5PSIuMSIgc3Ryb2tlLXdpZHRoPSIuNiIgZD0iTTE0LjMgMTI5LjNoMTcwLjgiLz48cGF0aCBzdHJva2Utb3BhY2l0eT0iLjkiIGQ9Ik0xNyAxMzguOWg5bDEyIDguNCAxMC4yLTMwLjQgMTkuNyAyMiAyMi44LTIyIDYuNi43YzQuNCA4LjEgMTMuMyAyOS43IDEzLjggMjkuN2gxMy4ybDExLjQtMjYuNmg5LjVsOC40LTE0LjRMMTY1IDExMGwxOCAxMC42Ii8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjQiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTE0LjIgOTRoLS45djMuNWgxdi0yLjhoMi42Vjk0aC0yLjdabTAgNTJ2LTIuN2gtLjl2My43SDE3di0xaC0yLjdaTTE4NyA5Ny42di0zLjdoLTMuN3YxaDIuN3YyLjdoMVptMCA0OS40di0zLjdoLTF2Mi43aC0yLjd2MWgzLjZaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImIiIHgxPSIxNDgiIHgyPSIxNDgiIHkxPSI4Ny41IiB5Mj0iNzUuMiIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6I2ZmZjtzdG9wLW9wYWNpdHk6MSIvPjxzdG9wIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6I2ZmZjtzdG9wLW9wYWNpdHk6MCIvPjwvbGluZWFyR3JhZGllbnQ+PGNsaXBQYXRoIGlkPSJjIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTQuMyA5MmgxNzF2NjBoLTE3MXoiLz48L2NsaXBQYXRoPjxmaWx0ZXIgaWQ9ImEiIHdpZHRoPSIxOTMuMiIgaGVpZ2h0PSIxNTMuMiIgeD0iMy40IiB5PSIzLjQiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjEuOCIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xNTA5XzEyMzIxIi8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xNTA5XzEyMzIxIiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PC9kZWZzPjwvc3ZnPg==", + "description": "Displays vibration data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'vibration', label: 'Vibration', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'mm/s', decimals: 1 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'vibration', 'mm/s', 1);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Vibration\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 3.3 - 1.7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"mm/s\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2.8,\"color\":\"#3FA71A\"},{\"from\":2.8,\"to\":4.5,\"color\":\"#FFA600\"},{\"from\":4.5,\"to\":7.1,\"color\":\"#F36900\"},{\"from\":7.1,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 3.3 - 1.7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"mm/s\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"#000000DE\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"mm/s\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Vibration\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"waves\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"margin\":\"0px\"}" + }, + "tags": [ + "vibration", + "pump vibration", + "motion", + "resonance", + "dynamic balancing", + "mechanical integrity", + "pulsation", + "frequency" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pump_vibration_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/pump_vibration_chart_card_with_background.json new file mode 100644 index 0000000000..cf29dc4bf2 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pump_vibration_chart_card_with_background.json @@ -0,0 +1,35 @@ +{ + "fqn": "pump_vibration_chart_card_with_background", + "name": "Vibration chart card with background", + "deprecated": false, + "image": "tb-image:dmlicmF0aW9uX2NoYXJ0X2NhcmRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==:dmlicmF0aW9uX2NoYXJ0X2NhcmRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiIHRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDYgMTU0KSIvPjwvZz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNMjMuMyAyMy4zYTQgNCAwIDAgMC0yIC42Yy0uNC4yLS43LjQtMS4zLjQtLjYgMC0xLS4yLTEuNC0uNC0uNS0uMy0xLS42LTItLjYtLjkgMC0xLjQuMy0xLjkuNi0uNC4yLS44LjQtMS40LjR2MS4zYTQgNCAwIDAgMCAyLS42Yy40LS4yLjgtLjQgMS40LS40LjYgMCAuOS4yIDEuMy40LjUuMyAxIC42IDIgLjZzMS41LS4zIDItLjZjLjQtLjIuOC0uNCAxLjMtLjQuNiAwIDEgLjIgMS40LjQuNS4zIDEgLjYgMiAuNnYtMS4zYy0uNiAwLTEtLjItMS40LS40YTQgNCAwIDAgMC0yLS42Wm0wLTNjLS45IDAtMS40LjMtMiAuNi0uNC4yLS43LjQtMS4zLjQtLjYgMC0xLS4yLTEuNC0uNC0uNS0uMy0xLS41LTItLjUtLjkgMC0xLjQuMi0xLjkuNS0uNC4yLS44LjQtMS40LjR2MS4zYzEgMCAxLjUtLjMgMi0uNWEyLjUgMi41IDAgMCAxIDIuNyAwYy41LjIgMSAuNSAyIC41czEuNS0uMyAyLS41YTIuNSAyLjUgMCAwIDEgMi43IDBjLjUuMiAxIC41IDIgLjV2LTEuM2MtLjYgMC0xLS4yLTEuNC0uNGE0IDQgMCAwIDAtMi0uNVptMi01LjNhNCA0IDAgMCAwLTQgMGMtLjQuMi0uNy40LTEuMy40LS42IDAtMS0uMi0xLjQtLjQtLjUtLjMtMS0uNi0yLS42LS45IDAtMS40LjMtMS45LjYtLjQuMi0uOC40LTEuNC40djEuM2MxIDAgMS41LS4zIDItLjYuNC0uMi44LS40IDEuNC0uNC42IDAgLjkuMiAxLjMuNC41LjMgMSAuNiAyIC42czEuNS0uMyAyLS42Yy40LS4yLjgtLjQgMS4zLS40LjYgMCAxIC4yIDEuNC40LjUuMyAxIC42IDIgLjZ2LTEuM2MtLjYgMC0xLS4yLTEuNC0uNFptLTIgMi40Yy0uOSAwLTEuNC4zLTIgLjVhMi41IDIuNSAwIDAgMS0yLjcgMGMtLjUtLjItMS0uNS0yLS41LS45IDAtMS40LjMtMS45LjUtLjQuMy0uOC40LTEuNC40djEuM2MxIDAgMS41LS4zIDItLjUuNC0uMi44LS40IDEuNC0uNC41IDAgLjkuMiAxLjMuNC41LjIgMSAuNSAyIC41czEuNS0uMyAyLS41Yy40LS4yLjgtLjQgMS4zLS40LjYgMCAxIC4yIDEuNC40LjUuMiAxIC41IDIgLjV2LTEuM2MtLjYgMC0xLS4xLTEuNC0uNGE0IDQgMCAwIDAtMi0uNVptMTMuMSA1LjhMMzkgMTVoMmwtMy42IDEwSDM2bC4zLTEuOFpNMzQgMTVsMi42IDguMi40IDEuOGgtMS4zTDMyIDE1aDJabTkuOSAyLjZWMjVoLTEuN3YtNy40SDQ0Wm0tMS44LTIgLjMtLjYuNy0uMmMuMyAwIC41IDAgLjcuMmwuMi43YzAgLjIgMCAuNC0uMi42YTEgMSAwIDAgMS0uNy4yIDEgMSAwIDAgMS0uNy0uMi44LjggMCAwIDEtLjMtLjZabTMuOS0xLjFoMS42djlsLS4xIDEuNUg0NlYxNC41Wm02LjQgNi43di4yYzAgLjUgMCAxLS4yIDEuNSAwIC40LS4zLjgtLjUgMS4yLS4yLjMtLjUuNi0uOS44bC0xLjIuMmMtLjUgMC0uOSAwLTEuMi0uMi0uNC0uMi0uNy0uNC0uOS0uOGwtLjYtMS4xLS4zLTEuNXYtLjRsLjMtMS41LjYtMS4xYTIuNCAyLjQgMCAwIDEgMi0xYy41IDAgMSAwIDEuMy4ybDEgLjguNCAxLjJjLjIuNS4yIDEgLjIgMS41Wm0tMS42LjJ2LS4ybC0uMS0uOWMwLS4zLS4xLS42LS4zLS44IDAtLjItLjMtLjQtLjUtLjVsLS44LS4yLS43LjEtLjUuNWEyIDIgMCAwIDAtLjQuNmwtLjEuN3YxLjJsLjIgMSAuNi43IDEgLjJjLjMgMCAuNSAwIC43LS4ybC41LS41Yy4yLS4yLjItLjUuMy0uOHYtMVptNS0yLjR2Nkg1NHYtNy40aDEuNlYxOVptMi4yLTEuNFYxOWEzLjIgMy4yIDAgMCAwLTEuNCAwIDEuNCAxLjQgMCAwIDAtLjkgMWwtLjEuNmgtLjRsLjEtMS4yLjQtMWMuMi0uNC41LS42LjctLjhhMS45IDEuOSAwIDAgMSAxLjMtLjJoLjNabTUuMiA2VjIwbC0uMS0uN2ExIDEgMCAwIDAtLjUtLjRsLS43LS4yLS43LjEtLjUuNC0uMS41aC0xLjdsLjItLjguNy0uN2EzIDMgMCAwIDEgMS0uNSA0IDQgMCAwIDEgMS4yLS4yYy41IDAgMSAwIDEuNS4yLjQuMi43LjUgMSAuOS4yLjQuNC44LjQgMS40djQuMmwuMi43di4xaC0xLjdhMyAzIDAgMCAxLS4xLS43bC0uMS0uOFptLjItMy4xdjFoLTEuMWwtLjguMWMtLjMgMC0uNS4xLS42LjNhMSAxIDAgMCAwLS41LjljMCAuMiAwIC40LjIuNSAwIC4yLjIuMy40LjRsLjYuMWExLjggMS44IDAgMCAwIDEuNS0uN2wuMi0uNi41LjgtLjIuNmEzIDMgMCAwIDEtLjYuNiAyLjYgMi42IDAgMCAxLTEuNy42Yy0uNSAwLTEgMC0xLjMtLjMtLjQtLjEtLjctLjQtMS0uN2EyLjQgMi40IDAgMCAxLS4xLTIuMmwuNy0uOCAxLjEtLjQgMS40LS4yaDEuM1ptNi44LTIuOXYxLjJINjZ2LTEuMmg0LjJabS0zLTEuOEg2OVYyM2wuMS41LjMuMmguNGEyLjUgMi41IDAgMCAwIC42IDBWMjVhNC4yIDQuMiAwIDAgMS0xLjEuMWMtLjQgMC0uNyAwLTEtLjItLjMtLjEtLjUtLjMtLjctLjYtLjItLjMtLjItLjctLjItMS4ydi03LjNabTYuMyAxLjhWMjVoLTEuN3YtNy40aDEuN1ptLTEuOC0yIC4zLS42LjctLjJjLjMgMCAuNSAwIC43LjJsLjIuN2MwIC4yIDAgLjQtLjIuNmExIDEgMCAwIDEtLjcuMiAxIDEgMCAwIDEtLjctLjIuOC44IDAgMCAxLS4zLS42Wm0zLjUgNS44di0uMmwuMy0xLjUuNi0xLjJhMyAzIDAgMCAxIDEuMS0uN2wxLjUtLjNjLjUgMCAxIDAgMS40LjMuNS4xLjguNCAxLjEuN2wuNyAxLjIuMiAxLjV2LjJjMCAuNSAwIDEtLjIgMS41bC0uNyAxLjJhMy4xIDMuMSAwIDAgMS0yLjUgMWMtLjYgMC0xIDAtMS41LS4yYTMuNSAzLjUgMCAwIDEtMS44LTJsLS4yLTEuNVptMS43LS4ydjEuMmwuNC43Yy4yLjIuMy40LjYuNS4yLjIuNS4yLjguMmExLjYgMS42IDAgMCAwIDEuMy0uN2wuNC0uOHYtMmwtLjQtLjhhMS42IDEuNiAwIDAgMC0xLjMtLjdjLS4zIDAtLjYgMC0uOC4yLS4zLjEtLjQuMy0uNi41bC0uMy44LS4xIDFabTguNS0yVjI1aC0xLjd2LTcuNGgxLjZ2MS42Wk04NSAyMWgtLjVjMC0uNSAwLTEgLjItMS40LjEtLjUuMy0uOC42LTEuMWEyLjcgMi43IDAgMCAxIDIuMS0xbDEgLjEuNy41LjUuOC4yIDEuM1YyNWgtMS43di00LjhsLS4xLS44YS45LjkgMCAwIDAtLjUtLjVsLS43LS4xYTEuNiAxLjYgMCAwIDAtMS4zLjdsLS40LjctLjEuOFoiLz48cGF0aCBmaWxsPSIjRjg5RTBEIiBkPSJNOTYuNiA3NC45djMuNEg3OGwtLjEtMi42IDExLTE3LjNoMy40bC0zLjcgNi4zLTYuMyAxMC4yaDE0LjJabS0zLjItMTYuNVY4NEg4OVY1OC40aDQuM1ptOC44IDE5LjZ2NkgxMDF2LTcuNGgxLjN2MS41Wm0tLjIgMmgtLjZjMC0uNSAwLTEgLjItMS40bC41LTEuMSAxLS44YTMgMyAwIDAgMSAxLjMtLjJsMSAuMWEyIDIgMCAwIDEgMS4yIDEuM2wuMiAxLjJWODRoLTEuM3YtNC45YzAtLjQgMC0uNy0uMi0uOWwtLjUtLjVhMiAyIDAgMCAwLS45LS4yIDIgMiAwIDAgMC0xIC4yYy0uMi4yLS40LjMtLjUuNi0uMi4yLS4zLjUtLjMuOGwtLjEgMVptNC44LS43LS45LjNjMC0uNCAwLS44LjItMS4yLjEtLjQuMy0uNy42LTEgLjItLjMuNS0uNS45LS43bDEuMi0uMiAxLjEuMS44LjUuNS44LjEgMS4yVjg0aC0xLjJ2LTQuOWwtLjItMWExIDEgMCAwIDAtLjYtLjQgMi4yIDIuMiAwIDAgMC0xLjYgMGMtLjIgMC0uNC4yLS41LjQtLjEuMS0uMy4zLS4zLjVsLS4xLjdabTcuOS0xLjJWODRoLTEuM3YtNy40aDEuMmwuMSAxLjVabS0uMyAyLS41LS4xLjEtMS40LjYtMS4xIDEtLjhhMyAzIDAgMCAxIDEuMy0uMmwxIC4xQTIgMiAwIDAgMSAxMTkgNzhsLjEgMS4yVjg0SDExOHYtNC45YzAtLjQgMC0uNy0uMi0uOWExIDEgMCAwIDAtLjYtLjUgMiAyIDAgMCAwLS44LS4yIDIgMiAwIDAgMC0xIC4yYy0uMi4yLS40LjMtLjUuNmwtLjQuOHYxWm00LjgtLjgtLjguM2MwLS40IDAtLjguMi0xLjJsLjUtMSAxLS43IDEuMi0uMiAxIC4xYy40LjEuNi4zLjkuNWwuNC44Yy4yLjQuMi44LjIgMS4yVjg0aC0xLjN2LTQuOWMwLS40IDAtLjctLjItMWExIDEgMCAwIDAtLjUtLjQgMi4yIDIuMiAwIDAgMC0xLjYgMGMtLjIgMC0uNC4yLS41LjRsLS40LjV2LjdabTExLjEtNS4zLTQuMSAxMUgxMjVsNC4xLTEwLjloMS4xWm02IDgtLjItLjVhMSAxIDAgMCAwLS41LS40IDQgNCAwIDAgMC0xLS4zbC0xLjItLjMtLjgtLjVhLjkuOSAwIDAgMS0uNi0uNmwtLjItLjguMi0uOC42LS43LjktLjUgMS0uMWMuNyAwIDEuMiAwIDEuNi4zbDEgLjguMyAxLjJoLTEuM2MwLS4yIDAtLjQtLjItLjYgMC0uMi0uMi0uNC0uNS0uNWwtLjgtLjJhMiAyIDAgMCAwLS45LjJjLS4yIDAtLjMuMi0uNC40YTEgMSAwIDAgMC0uMS45bC4yLjMuNi4yLjguMmE3IDcgMCAwIDEgMS41LjVsLjkuN2EyIDIgMCAwIDEtLjUgMi42bC0xIC41YTQgNCAwIDAgMS0xIC4xYy0uNyAwLTEuMyAwLTEuNy0uMy0uNS0uMi0uOC0uNS0xLTEtLjMtLjMtLjQtLjctLjQtMWgxLjNjMCAuMyAwIC41LjMuN2wuNi41YTIuNyAyLjcgMCAwIDAgMS43IDBsLjUtLjQuMi0uNloiLz48cGF0aCBmaWxsPSJ1cmwoI2MpIiBmaWxsLW9wYWNpdHk9Ii4zIiBkPSJNMTQ3LjQgNzEuOGg3LjJ2OC45bC0zLjYgMi42LTMuNi0yLjZ2LTlaIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTUwLjcgODMuOWMuMi4yLjQuMi42IDBsMy4zLTMuMi0uNy0uNy0xLjQgMS40Yy0uNC40LTEgLjEtMS0uNHYtMy45aC0xdjRjMCAuNC0uNi43LTEgLjNsLTEuNC0xLjQtLjcuNyAzLjMgMy4yWiIgY2xpcC1ydWxlPSJldmVub2RkIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZD0iTTE2Mi42IDc5Ljh2MS4xaC0zLjF2LTEuMWgzWm01LjItNC40Vjg0aC0xLjR2LTYuOWwtMiAuN3YtMS4xbDMuMy0xLjNoLjFabTcuNSAwaC4ydjEuMmgtLjFjLS41IDAtMSAwLTEuMy4ybC0xIC42LS40IDFjLS4yLjQtLjIuOC0uMiAxLjJ2MS4zbC4xIDEgLjMuNi41LjRoMS4xbC41LS40LjItLjZhMi41IDIuNSAwIDAgMC0uMi0ybC0uNS0uMy0uNi0uMWMtLjMgMC0uNSAwLS43LjItLjMuMS0uNC4zLS42LjVsLS4yLjctLjUtLjJjMC0uNCAwLS43LjItMWwuNS0uNy44LS41LjktLjJjLjQgMCAuOC4xIDEgLjMuNC4xLjYuMy44LjZsLjUgMWEzLjcgMy43IDAgMCAxIDAgMi4xbC0uNiAxLS45LjYtMS4xLjJjLS41IDAtLjkgMC0xLjMtLjJsLS45LS44LS41LTFjLS4yLS40LS4yLS45LS4yLTEuM3YtLjZjMC0uNyAwLTEuMy4yLTEuOC4yLS42LjQtMS4xLjgtMS42LjMtLjQuOC0uOCAxLjMtMWE0IDQgMCAwIDEgMi0uNFptMy4zIDIuMlY3N2MwLS4zIDAtLjYuMi0uOS4xLS4yLjMtLjUuNi0uNi4zLS4yLjYtLjMgMS0uM3MuNy4xIDEgLjNsLjYuNi4yIDF2LjRjMCAuMyAwIC42LS4yLjhsLS42LjctMSAuMmEyIDIgMCAwIDEtMS0uMmMtLjMtLjItLjUtLjQtLjYtLjdsLS4yLS44Wm0xLS41di45bC4zLjMuNS4yYy4yIDAgLjMgMCAuNS0uMmwuMi0uMy4xLS40di0uOWwtLjQtLjRhLjguOCAwIDAgMC0uNC0uMWwtLjUuMS0uMi40LS4xLjRabTMuMSA1LjN2LS41YzAtLjMgMC0uNi4yLS45bC42LS42Yy4zLS4yLjYtLjIgMS0uMnMuNyAwIDEgLjJsLjYuNi4yIDF2LjRjMCAuMyAwIC42LS4yLjgtLjEuMy0uMy41LS42LjdsLTEgLjJhMiAyIDAgMCAxLTEtLjJsLS42LS43LS4yLS44Wm0xLS41di45bC40LjRoLjlsLjMtLjR2LTEuM2EuOC44IDAgMCAwLS44LS41bC0uNS4xLS4yLjQtLjEuNFptMS4yLTUuMi00LjIgNi43LS43LS41IDQuMS02LjYuOC40WiIvPjxnIGNsaXAtcGF0aD0idXJsKCNkKSI+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuMSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTg4IDEyNUgxMnYtMWgxNzZ2MVoiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii45IiBzdHJva2Utd2lkdGg9IjEuNSIgZD0iTTEyIDEzMy44aDkuM2wxMy4zIDkuMkw0NSAxMTIuMmwyMS42IDIyLjYgMjMuNy0yMi42IDcuMiAxIDE0LjQgMjkuOGgxNC40bDEyLjQtMjYuN2g5LjNsOS0xNC4zIDEyLjQgNC4xIDE4LjUgMTAuMyIvPjwvZz48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImMiIHgxPSIxNTAuNyIgeDI9IjE1MC43IiB5MT0iODQiIHkyPSI3MS44IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojZmZmO3N0b3Atb3BhY2l0eToxIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdHlsZT0ic3RvcC1jb2xvcjojZmZmO3N0b3Atb3BhY2l0eTowIi8+PC9saW5lYXJHcmFkaWVudD48Y2xpcFBhdGggaWQ9ImQiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0xMiA5MGgxNzZ2NjBIMTJ6Ii8+PC9jbGlwUGF0aD48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2UiIHRyYW5zZm9ybT0ic2NhbGUoLjAwNTAzMTQ1IC4wMDYyODkzMSkiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjE5NS4yIiBoZWlnaHQ9IjE1NS4yIiB4PSIyLjQiIHk9IjIuNCIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMS44Ii8+PGZlQ29tcG9zaXRlIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzE1MTZfNjk4OTQiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzE1MTZfNjk4OTQiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48aW1hZ2UgeGxpbms6aHJlZj0iZGF0YTppbWFnZS9qcGVnO2Jhc2U2NCwvOWovNEFBUVNrWkpSZ0FCQVFBQUFRQUJBQUQvMndCREFBSUNBZ0lDQVFJQ0FnSURBZ0lEQXdZRUF3TURBd2NGQlFRR0NBY0pDQWdIQ0FnSkNnMExDUW9NQ2dnSUN3OExEQTBPRGc4T0NRc1FFUkFPRVEwT0RnNy8yd0JEQVFJREF3TURBd2NFQkFjT0NRZ0pEZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc3L3dnQVJDQUNmQU1jREFSRUFBaEVCQXhFQi84UUFIZ0FBQWdJQ0F3RUJBQUFBQUFBQUFBQUFCZ2NGQ0FNRUFBRUNDUXIveEFBYUFRQURBUUVCQVFBQUFBQUFBQUFBQUFBQ0F3UUJBQVVHLzlvQURBTUJBQUlRQXhBQUFBRDd4Wm52aDc3T1ozTjd2ZTY3dVozTTduYjEzZGRuZmJ6c3g5bnJONzNzT1poN091N3ZzNTNZTXlLRk1RQ1dCUjZIck83THVoN3J1NTNkN3ZudTUzYzdzUTVuM283QXc1bWJkamhCWm84K0d3Y2ZCcGlyM3d4MkpnbHo2UXB6OTF5UFUrMzUyK2V6dmM5WnZudTg5M2ZkNnpvdkZjN3ZPOEpxakJKNEY4bno0L0VuVHJNSENMaUE5M0ErQU9ablc3b1owZUN2cTM2MzIvZmQ2N3UrNzF2YzdjR0JvaU9UZXlkc09NNGtxSUhSNTJQRlE0cGdnVEhZSUVwSTUyTFBtclpsWVMycFlrNkkwaVVWL281OUg2VEJtYUdLMEJHU0k4M0VNQktWTXFtamNCSjg5WUk4dkRpOUxCZ3dtS1NwWGFva3JMNStMaVRTZ2dPSmZNcFY5RjQxVGZ0TXBZdlo5OVcxQ0twRjFQNWNvV2t6S2NHOE5DcUI3UEhkTThXSGhXeW9uRy8xMWN1UFd4Y2NLU2ZUWDg4UmFUUVJhWkkzalpiQXY5S0FkYnFNcitsQVJ3Q29kTVZDUzVSZkFBTzFVZ0xBM1Qwemt1TFR4Y2lURjltajVHcStZVGxzMTJ2TGhLc0NZemlKbEdUVEFGekF6YVZXZGRqWlBDbGlNWFdwV0FDRnpWSnA3R0Z1TFE2T1NlYng0WkRnNWlZMnUrTUEzMkNodU9OTjRZdHVnZ2t6T0xsOGlqQVJyQTZGSTIyN3lZQTFhSS9CVU9GWG82bHpyVHRhWEFFNS91YkFvYnpucmhNMm4yd0p0cStkaStvdkRXMXYvaE9nU3hWUlEwOGVBRXhuYm90b1Q5UG9Xa2g4WFBnQ3V0aVcwQmJyQ29udm9aeXBIbWM0MmMrZ0VValVDZGFxV002eE9IU0lVWFZ2cDlSb0VWbHVHTFJKbG5nS0ZTZU03VmE1WTBXMkJoOFplaWZ0ak5KMWtnKzBvTHI4bTlkVGVKSDVwOVJmWHFmenpjVWdLbHdZOHV1MlpvdCtkdEhyUFZ6Mlp4TU1Rc2VxVWFuODBKV3BJRWFUZmRkR1h5R2tiVU9QV0dzOVpMbTlhWWk5VEtrQWp5WUxEYnJISVZhbWtxRlhJUWNkZ3RtVkVkNlFjNnNCb3ZaL2Q5Uk5GcHFtVnFrUnFZYXNrNU9Ob3ZSUDV0ZzZMT2lkcTZRYS93QkNqd1MzQmJkVDVVQ0E1endHYkRnTjBGTTVjOVJJb25jelZPeXFxVC9RWjFOaU1YbDltSGVkVVRsWlZYS1R5cXNaeWNkYThvZkVlN1RWdXRzSStsZE11cVllL1ZTbjJ2akduelVkcnhBSlBwUFA1ZFVpcCt5b3cvSzJLY240Qzl6NktNOUNOYlJJc2EwVVNQWVUyZ1ZDV0V5eTF2cy9ObFV2UFA4QUhJelBLVzJQcjlTVk45UmkyM1ZIcVVXWExWWVZyN1MraXNmaGFtNlFJaWNtWjh5Rk5QRDJ1ZGZyclEzemU4MUV5V2JYR3hrenY1clo1OXh0VDZWYi9MK1ZYK012Slo2RVYzYTVGV2ZsNkIwdXB0eWlYR2h3VzMxUTJmeFNOUWdVUkhpWlZTcDNwYTV0UWI3eUhjZktaR0xMNXRyZFd3bU9XaktZbDlzOUI0VXdSdWRyMW9BMkZxOUtLSjlkVWVNN0tQVjBzVUNJZ2lRVTVkT1dJcTl5ZWRURGFaaGo2NFUrbkhtL1FSTlpBSTdXak10NTUzN1JSQ1B2QTIzT0dUd0srS0t6ak9ITzFtTm9qY0FhWEt4blVzWi9vamlvZ05FVzVwR2pLZFVpcnJORDgxcVBWM1RjYjROZzhXdDRmTWZlcWZibnJMbUxSbEU0KzRqaThKZUNjSDJuTzl0OXR2cS9RWUxyNi9RZk1uNzdpTnRRc3FJS1RMdm0vd0JNb0RUZlZjRnBmRGZRenRNVk1yQkVscWk4d0taUmtmZHBFWXZCNDh3YkYxaFdLTUhjMmhkQXRoRTAwYThHUjV1bHpwaGpSMEpnb2MySFdpaG1aTXFobFRDQ3BUVlNDamNDc1pvRnNDZEdvNnBJS1YvL3hBQTJFQUFCQlFFQUFRUUJBZ01HQkFjQUFBQUZBUUlEQkFZSEVRQUlFaE1VRmlFVkZ6RVFJaU15UVVJSklDVXpKQ1pSVW1KeGdmL2FBQWdCQVFBQkRBQkcvd0JpSi95ZUU4ZjZKNlQ5L3dDbmwzcndyZjYrRytrL2YvM1A5ZUZSUDhxTjlJOVA2Sko1OUkxWEovMm51OUw0WXhmbStLTDBrOFArMldTWlUrYmtYNDFaWGV2aktpZnV5Q0gwNnpESDUrd2hFejErZlVYejhaTGRuMGxsemxYNmhGeVgxNUpPL3dDMEVpWjZTTTJxTDRxaklQVDNrb2s4ekZCVlZKOUZUcW92NVcwRXdlcHVoWlN2NVNib2d4cW9uci85OUlpL3Y0UlYvcy8ra2ExUEtmNnY4K2tSZjMrTGZIcFZhbitlUnp2VEVlcUw4SVBUazhJdjIyRWFqVmpYeWtWWjloVVpiK0txdjFWV1QyeGRmeWx3bWtyNDcwTC9BQ3RBTkxaOUpaTFNLclVTdFZTOWZwRDRIU0dUaktUQy9adVZoWHVaTWZqSXp3OXVzRUdlY2x5elNHMlNkTDZyUDUvNkJsY3BHbXczdHAvaXh2S255VzlyWjIrWmIrdHYrcG95a3lMOThQcXlNSE9WeTNYZ1l2VElNakZOOEhrZ3JIc0lZaUpWYXBtTDE0WC9BT0tldlA4QTZPUlBYbG4rcm5POU5UeW45MkpWOWZzbitacWVrV1gvQUdOYXhQaW5sVmxuK1hwcjIvMGhUeXJvVlJxdW5zZlcyVXNHcVBWckVkY21aY04zMFZLdEpLRU1nQmtxTElWSlQyVUxiSEJaQmpsbXRRTW1zOWYwSmFHUmNoaDdkcUMrYzJ4K1o4SmpvTGFUS25PUjE2ZFo1UVY0eThabUlBMFN1cldCV2VhUU1ZcXE1eWxTdG83UEwwL1AxWExFQXprRTB0L3FPMHNJNWxhcTZnMGxzVENQYzR6cDZ3eG42enlzaXFsblNYeTcxNkRoNmJVY21XTVgwc2QyQTBsZkZXNXlxeXM5d1JpMHg2ak1DTVpGOHE3UDJWN3BGU1JGL3BGNlIwdit5SDBrTjJUL0FET1NOcVZVWW45K2YxK1BBNytzNitwSGlxclZkUE8zMGgrcEk1WXFNU3lyOVVkbEZmZFZWUmxvWlJaL2d3Tmo5WE5ZNXIxaW8wMzJKak5pMHRSWjlRWGpDVkl2amJ1cXpKNW1Hcyt5QXB6UC9JMHhpWTVQUHJzOE1ZK3VLci9uU3lhRGFIVlZndWcycEZheVY2V3UrMXFkSko5Vi9VYzJEU3ZnSHhPMGR5N3M5QmRIdmtyVkJ1V0ZGTmxrV1BmRVIxWjdZMlMvVkdBbGNtZDVPRkNQdWJudStsclBrcmsyaHh4alN4akxVaWFqcHcyM2VpNlNTZzhzQVdFclJVK21haXo0Y1Q3SGRHSzZlaFdSZkxtK3Y0dEIrLzFNOCtyT2tpcnNWcUo4M3N1NkVtditEV2ZYaGlHVzBiNXNXRy9LU21xUXI4cjhjVExNMlZxMnZGNHNsdVlSTFFuaFJhVkw0UXZnaGNpK1VSRTBlb0FBWTFiTFhrSVdwTlJzVDA3cTRtQm9TckdJRkI1WDNUOTFiTjJYUmxEVks1R0VyUTBROE9jSUhwZmtzVjBzeUhPQWdkTkpUVXNMcGozUlNOcFgwY2dQaitCTWJmUGwxWVZ2WDlZU0pZd21CQ3JlMFpNVHp3V1NLZ0xqNXJHYXpWN1lPVVpyZEJXa1JrOSs5VUlWdjRDUWZWcDN4UVFwcHN6UGVldGpVelRRUk1GaVJWVHlJeUJ2UUtJRGREMDZ2VEw1SE5pS29ybU9oUmswcG5vTUVsaWRvZWg1ZVVMTmU4ejFIRmc0Rll5dk1RbWw2MXNTZGxZOHhrNncxbFNMc3BaUHV1MzBId1ZCczlWbmsxdUlYeWtQMC9MRXJKdElSc3RRNWo4NHI1cW95T1NjajI2ZFYrcUUxRFdaUTdRVjhJa0oxbGhxZEpQWC93QjJyV2V0bzdxTHRKeVBzdmtpdjNTbGk2dGQwTm1WdUswS2pjTmNCVmhVQWQrazJXcVUvWUhSd1dTRTF5M1FHYytsSUhvN055MExSQzFOeGZRenRFaGFtM2tKMWI4V1VyMHdRd2Z6Mm50ZFNoVys5UzhDNUhHVkxVenhBSmRpWnRjdjB2U0RMNGpCNld5S29jQXo5Rmt0QnR5V1ZoSGtzOGQrTWRtQXdiRkxaOXNWMkV5NVk4dmQycDdZY2pENUdWamV4ZFVYOC9FOE9LYjhqTmVabTlaejdFV2N6bkt2eWNZTHBKSkpIZ3FVaXlWZ0tYNVhiSjhiWHdDSzhWVkwyaElXWm5mTi93QTVMbDh1dmxFOHhLZXYycTlaenkya2FJZ2tPODN0WG5SeUc3dW5zWitpTGxSSlJmTkxId2JvcVlpRkV2RWN4bkVnM0l1ekY4YXV3UzYrMWZLRTBjMkFycjR2VVRpWTZrbDkrbkpYSG1ZWjlUZjh2aU5CaHhNbzdPNTl6S3RrdmJVYnRDWTQvWjJiTFdoVzhLMnpaTk4rcU5DNHlQcTUvc3VJUFc3ZXpRM1dmenpMYUlIcGFINVJRaFJneklxQ3EraGNvTXJNb2dLbW1IeURKUjFhMHRHcU5JQVpzdytqUkt6cTZ4VHlZMmc2Mk1pdWpTWkNOakNkVTNJZFMzWk83ZlZRYVJERmQ5UVc3c1d5NW4wNG5UczlMeFpvNDFsbVcxTzVxV0Zsa1lJbCtsWmJjZGVqRVgxMk9Gc1d2UGVzbjdCM3BtM2xxUFprK2ZNcDFqNUxxaGg3Mkh1alFnYXhXaG44NC84QU9MdXB5V2h1bzFSS3IvNWZQNktZZFRmbmJ4TklOUGNVelBuYkdOQW9rNHJtVTc1cStxTGxsU09vRW8wbUJoZDFZM1dpVlNpMlRURUs5cVpvNzhGOStONDdOaGN0TVFNWXdjTllkRU4zSjlrdDhOUlhOT29WVCt3cVF4TXFOQTZzbnJ0QnFZOGdPaW9PRVlvbHF0RDFEOG1zSW82WUczZVZkdHQ0Z3czbnFad2ZUSmtMUjZQTTVITjBpQUszMWtlS0hPbzYzbkdqRVMwVDNLdERTYituU3RLd2FrdW9GcytJTlpZR3kyZFRvMHBPbXRnYSttSGxkamdMOXQ5YWE5WXlaTFI1MGpFcnJReTdTTlFNMWR1MHo4Yk1DMndRbVRJQ3NWU3RwZEJNZUwzakV3MFY5cm9CZUZIYVUvZmtveVd4d3EwVGFZWnB6RUZtMWUxZGxCZ2FzSGxOenBpNmtweStOeXNoMHV4Q0RBbHUrTXp0MGF4dE95VUsxNDZ3YllvUGdjS21zREtZeU1PU3ZwU005RnRYSTdWRUtGeVZsNHloWmUwUFVJRXp0MG5PaEtwVzN4T2VUYURNaU52UGlYYnp0eG5HN05obCswOG5tSjN4WkhZNit6Qklsdk1EcUZIR2FUVFRaMWFobmwrMFNEanhNaFF1VGZmeWM2NEg3YURPN20rMkczeTQxemtyaFRGM0s5WE41clFpZDcwWWVZZlltNkxuOXVtcjE2SGJ5WGRkbHFZdU9zWEpVODh0a05mT0RBZFc0KzVETVJFQ0lTem02clVIS2xxc01JNTNzbGU5ck9jMWRMSUtsS20rSDZjcHErZzdLZ3l4WnRKeXZEVWJsREk1R1M0T2RCaVFiZG5mdnhTRThrSmlGQnI1V200VEtjdVhVc1Zydzhmb2FIOGVJT2dzaXhXVW1MNVZoaUxOV05ObnNqeU9IU2dQdk85U0lYd2c3S1pMTjA1cXVhQnoySjcrV3F1eTBGaTZFeXNjZVJvVng4VFhWN0VQam8xeFdQcFF1akhyNndZT3RCbXdzU1ZhYmZRdXFtbDl3VnExYWxyMmZYdU1hNk1JREd0ZTJyVUtaWmduMlRDaGtEYWpHM3NyWUZlem5RTVpXclJUY21EMlp1Q2hJbUpZaW53VTdJUCtIVHpPM1lMaWhZcm1JeTFydUJibkluZWE5S042UEVaVFZnOExOSkZsdWk0QUZuTStYcUdieE1HQ3N5MTQ4M2V2U1NhRVlKVFZTVUtRTFpIMXFFN2NPcUxIZ0d5cldmdkUyUmZhaDVQWDVmZVVKOHE4UkFhSjE4L3FhYmxMWFkwM3U5bnJzczlNL0htWCtiL1R5OGhLZlVyUW9WVjZYdXhlS29YdFJPWjFCeHUvMUlteG90SjE5NEhrK0lUcG04Rnl6R2U1UkphT1A5eEhSN1VHZ0Y4cW9WOUxGUkp6NlFoVHBCUHF0WEhSeFc3YjRjdGhJSzQrcU5lSXN5TGxEMHJyamJYcnBoRjBaSTU4SzhDZXNMWldjSU5lckkwWG1OeGtmVkdSVFdFOCs1R28rQU5SdlJ5TEY2SkxJUzlvZyt5MzRUVHZvRDlMeGQxQ05ZVnI4TEtRZ2FWNFlScHlMYjU0UFBwN1Z1bDg3bktobW51T0lDZjdrT2hZTS8xUW1iSGN5byszWEJhOE5jTTlQMUdZMjJqNDV4VTNzcnVoVFJsTUJvN2REVTUxOU1qY01RZE56blRoN2l1UnJGOVpqWWVpWldTclgwT1ZqbXZtRTZsejdxZWFQRFJGYjliQTV1N1lIWG10VjBqYTNzWGdaSGpBV1F3MlV4M05yQThQQjlWRzBoM3FaeTJGeDZTNVRtY1BPOFJ6RGxNZEEwR1cwWW9pdG4wUE9IOHpsMWc1eHc0Rk1iMCtyZ3p1QWZMaXNQcU11M25QSjhlTkdXV3pCdHpzcXcwUzVLNkxZdTZIYWZoNHJqMXU3ZFNTdG05aFZzWmc3RTJSRTlkYk1NYjBjUlVmT3FONGVYaU5jZEZFSUZYMXN6RCtkZTRpU2VieERRNmVRcmJ2Mjl6WEtrclhyelN1aDdoYzlhQ0x5K2wwWi9LdmNtbUowMDc0c2ZxQTkvSmUvRzNKREJFeXQwWXlRNWgyVUIyVVorWlBtcWpJUmZXUlpvZGMrOCt2TStjNjNmNW5mRHY0WVg1bFd3UUJPWDNjWFNsbFRSM3VNNkFBQmsydkhGWkZkRjcyL2U1MVkwR1FxTXV6R1lCZTAwWm5mOFBzdnduVnJaUFM3RVJQcGVSWC93QkhidmNubUwwWE04VjU3LzB1MC9KNThoalIyVitTd2g4Mll6QXJYV25WYU5aRXlUS0JmcXhYWTZsLzU5TDNGN1NDZjI1NUhCNXFDSWZCWk0zQlpSb3NKTzhXdlNvcnVWOWxQS0h1dFRXVTFla2F1Ym5uK1hsMnMzRnUxN05Nb1hTWlY5Y3I2YlZ2aUFsbVdmOEE4TDM2eVdUMjZpdFpRUmY0OTdVTmtNc3lsUk5WVWVOOXpIT25IK1ZXN05aZk5uaVhRbzN2dTQzVnp1cURlVm1wc3gxVXpqRDd2d0ovZUp5TitseE1tbENWMlFrOG5wZjV3ZTI5YzFlcy9oN3lFbUo2OXhjbGJ2RGxlYjVuTGJwRXY1RzZBN01QUGUybnMxREtNUGNtNmJVakFZd2lLUFZkT0tJUW1ZNkd5eDUya2Z4eEYxQ0Y0bzUwV1lhUDNWYnBRZUphck85YzlQQnRGVjYxenVOSWRyMDBHUjBtTnpudUc0OVZmTWQ0UllsT2RhNlowVzVOOTlqbkhRcHRibGR2cG9IZld6ak9ublBhZnFHbGU5eTFpOHp3bkNzZFRaNVNiTlUzNi8zcjVZUGRaODZIU29YaHZkQnJ4cXA5YkNlUEc5SDlrSVVjN3hKSG9uM0dWeUF5ZnlrK1RxV2RLenEvSS82M1BiN3JKTE5xY0hQSXZ3RFVvTmxoTDRVcjRkWDR3WU1jZTczTm5US1BobnEzQm1vNTR4SnZoYXFkeDVyZndQWXlOcUJGL0NDSEY2Vnk1aG1LdzZydnFmYVJkaGk1M2FvbEtUdUdISjhZN3RGMGpNcTFBQjRvVllUcWRmNW1qSDZIUmp3UGJlS1FiekZ6MllydktOZU43T0taa2RuTFZCZGd4SGI5eHhnaXpudlNLTmt2enZSSDc1UG50UTNpRDdXV3NESHNOM3ptY3VZQWZEV2pjQmxyUU1YU3NXcGFNZVIwb3pIYi9VNVBFcW9lRGhURlo3Tk5uWmcvYTM3Vy9CSDJ3NjJsOC9FdkFNemZIWUxVQ0w4RDY5blFlRFB0NjRub1lmM2h3QXVuQm5zUDBLTmlKZTkxbVRXcDdrcTJvR29qNm50TDZKR2Y1UExnU2t2Z3IxN0N5aFBjanJhUDFlSXRsa0RlWjJBclo1eDYxRFdveTlQTmU1K2h1Y3RVL0J4dUJxdHRDSzBySHBGWDdWeW1YU1VLSFM4L1JiK3FPSTlBbUZqYStidHpTU1Y5M25SZlRPY3pqbXhOc2xTemRKeUxyMWJVaG55Tm42SGpzLzE3akROZmlKRm9Qd25VNUgwU1BIZXJReE1UNFM4WDZ5dWNPckpMenNvTjFYSE9xeGRFeGJGdUQ1c2ZtdXVaU3AwbmtVN3F1ajVGMWZLOU9vQzhQMXNVejlWRWNwc09ONnFlMXptQ1kxbCtjZDBya3VkMmlJR3RhV0xCQ081Ym1ocXFXNG5yV0k3R1I1SGxtTTZCb1F0TFZudVA0ZHdISTdiRnJMOTcvYm5OK2wvYzF2dVprVld0RmREMEgxNUxZNk5rQmJKb01tcEh1ZmtaZnB6b0JsZ0daMGVNTUtnNnBvNXlKdFpzdnBVV014bmF4ckliSWRvd0xsZ0pkT3BpdDVBSjZXTXIrS3hmbmtCTFBTeHRqU1Z0Zm1DWDRiM09yOGFSczQ1VmthRWVESVJ1aWtIVlh3cExKTXhKVjN2TUdDQ3NoUVI1Z0ZSYkVzTmprbGxaTlZLNjhVQTY3aHJlZ0NmWEViRjNEM0grbTJYVld4TUVkcTV3RDZka290ZmpKbmlOUG1ETVhRTWZlNS9xNElxbWg1VHRyUEo5amE1cDFHazh2emsvN2ZkVmtOTi9NdmhsOUN3VE1uY2owcDc0TkNQWmsraGM0MC9TaE9wR2hkcUhqTFpyYzg1TDd0TE52SkhZY3VTTDNkZngzakVHaTZ0MU90YU5YdDl4c0R4WFZKejQ1ZjBPTzJXZ3M4cDYwQzBqNG56NWJjYy9qTWIvQURQVWNUYVI5KzNKSHMrWnp1cFdKS05yUGJSNWMzZXl1eXJNR2F3RW9qWjBtWnJWekpBZTN2RWsyK1hpTkM3c0V1Mkd3a2g5MlNwZnBPU1htOUlCZTJFbzIwOXNOQ2ZIMjg5cWlJcU9SYjlQWFllSzdXV2RrU3NmQ0xrdlhaTkxCNFlkRU9sdDBJYk1EbUpaZlJZOFhKRzZKSGpOeHp1YW5LazlOeVJMcjM3RG1teWJ0Y3d5eFRqSjNzbDJ6bTFza0VXTVhvczhUdFlqYXRCNk85RkV6cVBHYitwSlFiREUxWng1N0VHczcwZU92Zyt4MEpNeHJlWVhOcjdkamJnejZNK3Z3RnprZk0rMDVCbXRGMnZ3clRyKzc1SEE4UnBZTG13dy9PaU9BRnJIcmc1VzRnNUx5ZFBQOUl4ZDdoeEl0QnYrY2RHMHZ0cXoxTGplU3kvTXh0cW9OMStMdjRmVXRqL053KzNPOEE2US9ubSsvSnM0STh0OGJTYnNNYjlaYXBTTTRQc0ltS2FwY1Nyb0FnUXJaSHpVSnBJTGR3Nk8xTWVxVXVFUFc4dHNKZFNYZHEvcDNncDRvNE9xaUNOZGpvMWFzaURyT2dEUGN5ZHk2TDVyTFVXSzNENnUwbkM5REdVR3ZqYk5SaVpXalVtSWtWQlZaSjNRdW1obWlsakowR0lLYXNzLzNpdC9qSzd4cjBiYWphdXc1OXBlYmRVZm9BRm1hclVPUHpYUzhWRkpMV2htTmMwMzVISDNoK1R0QWlaSVZydmFqbCtpWm4rTkVyeWlySVVqcWVHZUJXbHR4YlRHNUt2YjFtZW4wWExOWEJuSHhiWUdPelkwS1RJMTNuTnB4bVMrVXJhck45Qm54QnN6MjJYS2QvQTRJb2NKVkpzSFY1Ymt0R1U1NG42cUsyU1RxSFNlWVZ0WHo4a3lPK00zK1I2aFRzOHc2d05hQTFnNlRxSHQyT3ZySkhMdCtZRWNmak9zVE0zUE1UNjVMY2hldmF2RGs0QW5ZUVV3eGMxMElRYno5VkNTVmRNR3VETXZmNXJZZ3I1NkhUZzRnWTZxVGtRZk5hR3doVk5VTGtGeWlTWStXNVZ1emlaalk2S05HVzVJckZWeVd4ZjdwYnBoN2x4V1JOZlVySlZoenNaaW9UZElIa0lqNjBVc3NaV09XTFRNQVIxSnI0bVo1TUZ1cWVSMFF4bEtWTEYrbFd3MWJtV3p0b0V5TTh0c05udEx2SUt4WmI3TTJWQ1pEU0JkNHlxWjNQNFFwMjA1bmZvdnoyZFVZV3M3RGttMXNoYTFyTW42R1J1R09oNWZuV0lnRmJ5NEdtTWJjcjAzY0FNcEx6elNaaUhPN25wOVlYMWNGa0tlc0VGOVV6R1Z1ZSs0UXYxSXYyYzZKWm9PZGF6aVc0bjFPREpObEFrQkdGOXdlUmVzMVI0SFhVK3A3M2hlaWd5SFVJbTY3S1o3TllmVzBLbXZ4VTB3ajFReTVlL25LOWN1eW1XSFhlUVVzZnQ1Mlp5MWM1K2F6Wi9ZNXUyNnhmR3drRzJOUnpQVjBuV21Xcm9VbGJHay90YzdPYU9PVDFXMnZYY2hMUDhBZUdHblIyWjJKYldISGpCQUZJQ3QzY3dXNDVZN1lCV3Z0Yk9sV0ZUajNVNVpSZWprekdkeGVXTDJJTDBOa2Z1czZOMU5BdFF6azg5aUc5WTIyOXRwaEExR3NTcFo3b1UrdHJ4R2hRZ2NESjRMdTVPL1BXemV5RTBCWUhIZ3NabllMTzdLVmRicGM0eXArc0xOekw0Q2txVUJYdVE2dDBOYVY5b01TTTNPSHdSSHFOYlBHOFhTMUdyNlp6enJjdmJobWVGUjVjSm5lODc4QnhPNVFCR3dJNnlaMW5OK3pkazdsVDA1UFlpd21mOEEvOFFBVFJBQUFnRUNBd1FIQlFRRkNRTU5BQUFBQVFJREJCRUFCUklUSVRGQkJpSXlRbEZoY1JSU1lvR1JJek55Z2djVmtxR3hFQ1JEUkZOam9zSGhJTExSRmlVd05EVkZaR1Z6ZElPVHRQL2FBQWdCQVFBTlB3RC9BS0g0Umo0emp3UVk4V2EyUE0zT1BDTk1lTHRiSHhOZkhnaTQvdW9qajQyQ2o5NXg0elZmL0FISHhGMi95R1AvQUV2K0xEQTQ5YUZmNHZqeW5pL3l2L0o1ZnkrSng1REhpY2VDNDhYT1BkVEhpZjVQY1J2OGhqbEpJdWxmcWNlNmw1Ry9jTURpWjZoSUI5Q2I0QnNJNlNPU29ZL3dHT1ZROUVLYUg5cGhiOStQL05jK0UwZy9KRVdPUDdMSWVqTXN2MGVVcU1lOUxKVDBLZlFCamptYTNwSFBLZm1JOU9QQ1NHV1kvV1NVL3dBTWVFV1YwcFA3MUp4L2RVTUMvd0FJdjlud0dQRTQ4Qi9KNFh3T1NEVWNjcEp1UDB4eEs3WFpwZ2NFcG90cElmbWNEK3Y1aWRuQ3ZtV2F5ajY0UEhMK2pGTzFVNDhqSXVtTWZ0bkhGcWpQczJZZytaamcwZ2VoYzRISDlVNVpFai8vQUdXWnZxK0Y0aXBybWY4QXdndGdjRERSYlZ2cWRYMXRnbXdXU1ZZZjhJTndmeTRKNzgyay93Q0xRYjRCc1JTUVNTMzQ3allNTVc3VXNRUmJqd3ViWVRld3FxbFFRQnhKREc1K1dCdzJjd25iNW91OGY3Zmd6WThRdGhqM2NlSVhmZzhBQVRodXpUcTJxZVR5Q0RmamljenpPRVZGUVI3eW9lcW5xY0ozWjV0cEdua0JjUnI4c1I3dG5TeGlRTDZzYkl1T1VqZmFrZm1heUQ4dDhMdlliWUJWOVdheWo5bkNidE5MRWFrQStidmFOZnBnY0tqTUp0dGI1QXBFTWNxSElvbWlpSjhQc2dvSStadmpnbFgwbXIxTXg4OW1MdWY0Y3I0S2NjcHlOS1NGZkcwdFNWK21tL01FNEFBTUUvU0NXdWtESHhpcGtBZGZJbTR3SDNWRVBSUllsUnI5b1MxY29zRGdFcTFQVFpqRUhCOEFsTEEzMUpJdC9MeVZSZkI3ekMyUGlmRnVPckhLTkd2ams3RCtSdXpESHdQcWNOM0tDSFhNUjV2YmRqaTBLemJXZHo4Ym00VDk1eFIyOXBua0pXbmh2d3ZhN1NPZVEzazR2OTVQR1ZpK1VTbTM3Ykgwd2kzam9vM1JtRnQ1TmgxSXdPWndlcUtvZ21QOHA0eWZJRWVlQ2VyUTBhbVZWUGhwQjBMK1pqNllVYmhJeTFWWjZBa2FFUDRFeEh1ZlB1azFVWXFSRDRndWQvb0xZUGFwZWpkR0tDaUErS29jQXNQT3hHSE8rbnlTa2JPTTBZOE44cjNWVDVqL0FFeGI3enBibkx6eXNCN3RIQVFCeU9rMnQ0amhnQmdzcGlqeVNpSjM4TEFPd1BQaUNPSUhIQ0FBMUZIbFJyekduZ1o1eUZBOG1PNDNzeHh1QmlvOHdMaEJ5VmtvNHlDQndHc3F3K0xBNHBHY2QyT1BlV3dlekxNbTBsK2d3ZS9NQkVvK3B4eldFbC80WTVyVHdsYi9BRFlqQTMrMFpuVmF6NjZSWWZ2d09DVXlLcWpCOTRSdmc5NzJKU2Y5MDRQY0JrVmYyVlFEQWF6TFMwNS8zbjBnZlRFaW1SSzJ1ckZsRzE4WkxnS0xnV0ZodXhHMmw1a1pwWVI2TnVRajBKR0hkUVlHdTRuUEVYMGkxZ2JXVGh6T284SWlIYkxkbTZKTW81VFNqZVYrRVdCeEVoYWZNUlRMQkZZRGdtNFhHQko5cm1lWmtTa0RuN1BURWdOK0p1cjRoc1VvNnRWbTB3ZUNrOEFxQUJGK1F3aHUxTFJTK3daWkFCM1dsQXZLM3dvQ2Z3NFJMeVpYMFpwL1oxMCtNMVFidWZyYzhyNHFIMFJUQ2o5dnpXb1B3THZJUG1UNmdjZ3dEWmgwcXIxaW9vWCtHSzJ6YTNrakQ0c0NNTlM5RGVndVNtc3FnRHdCZVFTTENENUtua1JpSUhZWnRubGZSNWM4dnVnUFVDcW5hL2t5REhORmJVY0RoSlZOWmZwZ2YwZVcwdThlckFFL3Z3ZUllUXp5ZnNwcXQ4eU1mM3poRCt3dXR2NFlBTjJra1dBZldWcm45bkFiZkhRYmFvSHlLQlI2MnVNQTNFMllKSEg4emU1d083UFh4bHIrUTFBNEIvN3B5eDZoZnFxZjU0TnRJajZQbU1OdjVGeUxZamlNVDA5ZWtFTHF1L2NMNlI4NzNHSXdDWmFMcEJUeVJTbnhJdVI4cjRpQjJWRkRtOUp0cDdBN2wzZzRvWE1GWjBZemlqaldzSlpleFQ5YjdRbTUzcndGamJCRFRSWkZXWmROYWxYM1dLMkVqZW8zbmxpZ2N4MTJXMVYwbGlrNEFWRUpSRnN4c1UzdGlhaU1pVTBVNFFpNjNXK2k3QWcyQjREMUdDb2VOcVFNaVZJNDNFbmVVL2hzZkhFZTRDdGpFa1k4ZEtzQnBQbU1OSmVwMk9pUGJlU3R2Q2dlbHppMW5xMFUxVHY1Rmcxd1BTd3hTeWlXS2xxVTJMQmh3YXpteitsOEdZU3l6bkxvYjFGandad3ZQOFY4VTZCWTZYTE9renhVd1B2Q0prSzN4ZmZGUXJ0MnY1dmNSajZ0Z2J5OWJJWjMrbTVCK3ljTHdwYVZESVBraWRYQTRWR2ExQWlRRHhJQkErcllmY2FIbzVTYXBENWErcU9IZ1d3YkgyL3Bybmh1Ky9pSUVzVDZDL2xoZTkwTTZHeFpkU0FmKzlyU290NWpGeHJwczcvU0ZVNXpVSHk5bXk5ZEFKOWNFZmZSNUxEbG9QeENXcllTRWZYQzhJSnVsZFhOSjZhS1dKaGY4d3h4VkkyekhhSW81QjUzQU9KRHM5QVhLNVdBSk4xSW5tMUVuMUdJNGRWcTNMc3VvWG1iNFpJaTZrK1ozWXVTVG1OSmt1WTBFU0M5MUZSRElKb2lSd2EzSEdVa1FaS21ZWmRsK1owZEw4ZnRVRlZITkZ3dU5hSEN5aHk3d1ZsSFMxa3FtLzJHWTAwa2lCcksxa2JIUnZMNGpERFg1bWxQbURLb0FNOGtVa1R4eXFRUUF4WU5wQU5oYzNUUzFkWFJVb3JLRis3czVVU1JYaERFaTBsaW5nT1lwRUR0UWpNb3hNWDViQWtQcVRkYlNXQjhSaE9wN1l0QzlUVHgvak5OckZ2VlZ3eHRKQlFWR2lYeU9rbFhCOGl1T0lqemFJNmYyaU1EK3NaZE9yMkhwdncrN1JWQnFhNS9FRHBiNW5EbTZzMWtiZWVPdU1yZjVnNDdJcUpRQVBsL3A5TU4yY3FvQ1hhL21GNGZ1d3c2bnRBRXRYSjZLT2VCd3pYcEhXK3hVbzgxWHRuMEFHQUx5UjlIb1Z5bWhBSHYxazE1R0Z1YTJ3aCszcU9pZVQrM3lwWWtFeTVwV25aeCtaQndEWXBtVmZWOU1jd1UrR3lqS1VpSHl1Y0FiNlZxbW15aU1BZUZKUXB0Q1BKamlicW9hU2dwcWVUU1BHV3RrTDI4R1pCZ1NBQ2tmcDA3a0hnU3lVU0lnOUJmRmtVUEpWbXJYV2V5UzlVOXJieHh1UmlCelRVMjF5ZkxxaEJwN2Q5SzY3QWdnY04zTENNaVZTMEdiUVVsbmRTNy9aU3F5ZFZBUnA2eHVSdUdLc05HanlUMDlIV0xHZzNndEdnamV6RUFNYkErZUk1UFlLYW9hbHkyR3VqWFMwczFwWUVDVkNGSTlTNnQ0TERldUFwaHFLNGRHNEtTYlE1dXdrcDFrZUtaUWlTTWtnQko0QUcyS2pNbm82YWdPWTE4MldRR1daVkY0SldCaWVOWGRpVTBqU0xrakVrY3B5bXZ6UTVZS2t3eG5RZ2lyS1lIMm9hZXJwa0FJSlFLV3hESzAwZEJTOUkzcloySmZyQ3J5K3FoK3dtWG1JZ0U1TGltdWtCelJxL0pKWkV2L1J6dUdwNVFmd2dFNDVUOUkramZ0OUEvbkhtVkF4TWZxUmpuUGxGYW5TM0k3ZkZFL3dCdkN1RXVaTTUvUmZuTFFWY0pIRnBjdG5JUHFCaUs0cU1rejJrL1UyZXhlSzJZQlhPT011VGRJMGFDRndPSmljblp1UE5XeE1MUjkvTXE5ZkJGSFdzZnlyZ2c3YlBjMmtFbGRWa2NkRitKOGxCdGcvZWROZW1GeTAzaWFhQTlaejRXM2VlRUgyblNmcFcxb3o1MDlQZTJFR3RjKzZhdWZabFgzcWVnV3dJOEM0QVBKc1FzRmo2VGZwSXpIL2svMFdwei93Q0hvSXRMMUhrTUl1K2k2SzVFM1IzSWsrQk5BRTlSK1pySEZyTkhrRklESzl2N1Nwa0pBOVMrSnB0bnRzMG5semFzcDVtc3pLUWJMSzRVRG1VVytJS2Q1RkM1UEhUeEd3dFloUU5JTjdldGhpbHBwcXUxVTdNem1PTm1BS2dXVDF3RVF2TFJGMHVXYTdFZzd6Y0tjU1BVeUxIVjVlckViU29FS2tHM1ZHbUxGcWRkRThBbGlEUExKTGEzUGRHUGtEaXE5b2xrUktjaW5KZHFXUGRiZUZzeHNQUEM1ZlV6TEZCSTVFWldnbVliTjIzMiswWTNPL2lNTzVjeVRzSlFwVTVybzFwZTBxQmtCQ1hBYXhVNGJOUk9YaDZQRTBRRU5SQTM4NGlNbldsWmhFamdIWjdFbzN2QTVYbnRYTlYwSFRDZ2hGZlJoNWpyanA2eEZhT3Naci9DVklVZ1dZdGlXZVNPT3Zpcms2UjVITllrYWFyTHBsMjFMSUFMTXFMYjNTUmhXTzN6MzlFK2Q2Tko4YWpLcGlWOVZBeEU5NTgyNkpodWovU0NsSS90NlUyamtieHVOK0tjZHQ2VTVIMHN5OCtKQ2FSVVc4VnVjVTI0NVpuUldnNlIwbzkyS3FBRzBZZTdLTitLTmcxVjBMNmJVeVUrWVVoQjdTaVVGSkZIdm9SaXQzcExNeEdYVVA0Yi9lVzhSMVQ1amdvdXdrdCtwc2pVY3lEMUNGOFQxZkFIQ3JlYXBxUHM4bXlnRG1TZHhDK2Y3SnhSdlo1cGx0bHVWdU81REVlcUNQRnJzUENPNEpvcGY1M25HYmkyVVpBM3ZTdTMyY2JEam9HcWM4MlhGTExzS25wRFZobDZQWlM0TmpUWmZBdGc0VTdyeGhRZWJOaVloSE5QUlF4d1VGK0VLQWNHUEVuZWNXNnRKU2RXL3J6dzJYeVZJcUpkL3dCbEpNMmtXNXYxU1dQbUJ5d3RORUdYUUJ4bEExajAvd0E4RG8vT3cxbmZKWjRyL1FkYjVZS3hXZUkvQklNV2xVby9pbGRKcVA4QWlHSURTTWJpNEFTZDRtK20wR0taSjFaTGRUVnNZcHgvK2R3UFBGWFE3QndodU5EaVdsWStscFlXUHJqb3RuN3pUeGJRNjJFRlF0WXFFSGtZM3JBRlBPTnhqOUdmU3IybkxwS09tQ2UzTlZTTHNKakVSYTRhbnBuUTlrcklVT09sUFEybTZlMGZSVjVUVXBGbWRaRHRLdjhBVjg4aEJkWTMyd0srNkN2Yml4bmVaelVsZm16d3RTWlZWVllPcEtETkVabmdOUXlXZU9vY0lab3pxRWpFRWlqZ2FkcTJrYzAxWkVpalUwc2NxWEV5RHRNakJ4cDNrYzhTSUhvZWwzUjJNSm1LeG5nOGtVWkt6anhhRWx2R1BHamFVM1NUbzROam05QUIvU0JWdHIwOHloQkhOQmpmSFM5SnFTWnFicERrTDhsbmRMTXdIaXd2aUVnNWJuMlhFUTlKY3JEZGhHZFB2bzI3SllIRUIwOUl1azdvZE1hYzQwNEVzUllCQnZQa04ySVRldnJaV0RQTEx6THVQdkpqNVhXUHNxQzIvRkpVYkxNTTRwSTlNMlpTQTIyRk5iaWJteGNYdGV3Sllsc1QwNGp5em85U1NNb29sYzZWa2xaT3U4N2syV3h2ZGpwc2J2akkxa1RwVm5sR2dwcWFvQ2ZlWmZSYUxCb3diaWFaYm1WN3JxMEM3cEl0TlRHbSt5cmE5YjJCWjErNlErQzhzUkFpb25NdXBwYXh4ZDNQeUZoNEREYytKR0tubzlNVkt0L1NyV1NpVkQrRzZERXVWYlpEZmpzcEVjajltK003eXVyeStFM3NBOGxPK3orcmhSODhleXBKWStNYkJpUDJkV0pNM0kybmhEV0pkVDZDV0kvTjhhM3BwdC9ZOW9XOFpKOEZxSTFHS3FFVHdSRWNLaEMwZ2ovTi9PSThWMnFHbnJIUDNNRWcwTTM1VU5QTjViQjhkTTdHTldzSWhtY0xrR0p1V21Sbm1pL0RXSTJLdU9ISU9uOUFsdzlWUU1BdE5Va2NwVlNJSmZ1ejBhZS9qbzlPL1N6b0ZtZEYvWElWdE5tdVhSKzhzc0pUTVlJL0VUZ2RzNDZmNU1sUG10REtnYW1xWTMrMGlzZUN2REwxNDI3U1hJR09pVWxxWFZMOXVzQUpNYktlSlZlNmVWeU9HSjd6Wi8wRmxPakw4MmZ2eTBnNFUxUWVPa2RSamhaR1hNY2t6UUdHUXlwdWtnbTV4ekx5azRqZHFMSVF5NVlTT2tQUmJNRUNSMXdGN3gxRVE2ckEyTnBGL3dBaUJsMVFhZnBOMEh6UWsweVNrMmQwSEZONURYWGMzTWNHeFgxSDg4cVkrckpUeGpyVFRNUjM3WDM4aVdQRkJnMHAreVEyWktKYmg1RDhjeEJBOEZQbmpKYUlISnNvZ0FFVUY1REJHNUE5NXd5SjVKSytNc0RtRXlkbVdwZFNyVEg4S2txZzdvTzdIU1NvWnhUMHFCRWhvSUR2QVVjbWF3eEFkTWxSR2RNZ1BBMjhEZ3M4MVhOSzVabWtrWGNTVDlNYVRiSFJ2cDVYNWJWK01jZGJUeFZNSHlMUVRnWUxHa3JEcTRSU3FZblB5RDMrV09pbWFxWkI0U1F5WEh5MUpnVEptV1dKNzlGVkx0VUh5VjJqUG1oeFRRbWtsbStCaUhnbTlGY1J0NkU0em1PU2hyREtiR2ttSkNsL0l4ekJYOUM1eE5VeU1qOG9LcEdCbEE4dFFTZGZGWFlZcG5hVkJhNndUb1NYalBpbDJQckhLY2RIQWhwSmF0N1MzaE95aGtrUGlOMUpNZktDWEFSOHA2Y1pCSVJITWxTeDBzOWoyTnU2SzZ2d1NxajhKUmZMSmt6TDlHdlNxRk5NamlPUm5nS0E4SllaTlkyUjhab0QzY1YxZWhNS0VtSG9sbWM1M2JMeHkycWE4bE5Kd1c3d0d6eHJmTFlnbVU1eExJRFNaelRIc1FUdHczamNzaDNNRHBheEFPS1oyRmZrVlJ1cUtHb1hlNmdIZXluaUQ0WXJab3FEcHJSUUFFM2E0cHE5UndKdmRDM0FucXQyempKSXZhWllLWWtSNXRTQmRieGZFclJqV2g0Z0R4aXNaOHVpT1paZEFYak9ZMGt2MllKRWZXRXNFckJUYmVBekx3QnhIR3VXVU5RVGZTWnBBck9ENTMxZk00TExEU2VFY2R5c0MrZ1VRSEF6Q1o2Y0h1UVVkT2tGT255RTVQcmpOY3FueldvUHZXQTAvdmNZeWo5SCtXdFRnOENaWW1uYy9ObnhGVmxrWHlJdmo5VG9ZV0hkYlozQkdJSkhpY2ZFcElQOE1kTE1nTmJrQ25ubStXRnF5blFlY3NRcW9SNXlERXlYUU5qcEJRdFNWUlBjcklsQzZ2enJvZjFMWTZOVmp3dUgzYmJMSjVyMzg5ak81UDRLandYRXRMc2FoZUlrcDMzS2Z5bHJIeUl4VnpLVEllenRDQ0k1VDhNaWpTM3hBKzlqbzFER21iYzVab0lqcGdyUU84OFJPeWw4VU4rZUpZaEJYb1Jxc0VHNmRQZk1ZMzI3OEpQdVlxWmdhOHdEYXd4NjFzSnJEN3lHU002WEhmaklQYVRDVTJ3enpJNW4ya09kMHVnRjZlUzNiY1JpNnVOOGtZUjFPdE1VY3Z0VTBjUTJtWVpUVktvTFNxRjN5T0FvMnFMOS9Hb2xTMGlFWXl1amFHa3pQWkNwcHM0cFpoY2gwNFZWTk9CZDR1OTI0eWtxNFFySEN0Uk43WFZaRnRUWk5uTzFoVlVrbkNPUnJheDltK2laYnNJMGJKODlXWnJ3cURaSVozN1RVNVBVV1k5ZUI3TEoxYjJvSjNnei9KSjZmU0twRys4T3k1eFREZVF2WWtHdERZcmFLVkpzdHA2d2dWVktkWVpvQ1R1a1Fua2VaUGljWkIwbHpGS1dPclc2VWNkVE84OENrSHVGV3JJL0pvUmo5Y0x2SEd3MEJmOEFFMkJuZE1qK1NGS2NBLzRUaXVuek9pR3NXKzAwUWtENnhTZnM0YkpKY2xyZjd1WXhDd1AvQU1sUEl1TXVvamtHYm5uYUYyRVJQNUNNWm5UamE2ZUJaZjhBVEdXdzZJQS9HV0h1a2VuQTRrcm1tai9DL1d4bE5kRlgwRTZqN3VhSnc2SDB1QmNjd1RqcGdnenpLWUY3TkcwamtWZEY2d1ZBbGp0N216UFBGZXFGWkR3aG5YN3QvSVhKVStUbnd4bE40c3pwSFhkVXBZcEpISVBCbExJMk5rWk1wbHF1MUxEdlZvWlBqUTNqY2VJdnpHS2VCelJKSWJOVlE5K0JqNzYyRmo0aFd4UlBya1pvU1JVUUM2c1hUdldGMGxqNWp6Q1lCUmhEVHpYbnllcTdhUmhoeFFrNm9aT0JVNlRpRldocEo2a2FZb1ZadXg1VXp0dkI0MDhoOXc3c3huTVdYMTB0MWJMbjFhOWhLVjNxRlk2Z1J2UW5XbDFKQnIzVnEramkzUTVxcDYrNEp1U3BBNjRDbTBuYmlON3JpRnpWVFpQUjZVblNZblZJOUtMZ0NSbUYzcGlRa3BHcU14eThZaEpCUTV2b0FnekFOMUhSaElCb0xkbVJKQll1TFNxamRmRXJHV3A2TlZBZVVXVVdkb0wza2prVWRxUGUxdUcxUUVDa2owVmVXR1A3ZWp2eDJURDd0aDJsSDNMMnNOQnVvb2lqOUZzMU5VcUROYWVZTnJBM2x3RjB4U0FzTG8ydGNaelNqTE02cU11cVVsb3ErZEdXUjJrWGdKTlVZYjF1ZVp4VUtkbXg3N0tTRWIwWUNFL254bThCa29MN3J2SGVTT3c4VEU3RDFTMk15bld1cG9oWUE1akFHMjhBODVVZVFqeDFqSFNLWDlaZEhhdzhLS3NKMXRINVhjQng1bHhqTzAyVE8zWXBhd0MwY29QdU51RjhaTlV0Qk5ET0xPakRkYnpCNGcrR0tLUU9qTDJYSE5HOFFSaXRSYVBPS2J2ME5XdkJXOG1IQThEaGw4TVZWV2Ezb3hVeWJsbzh5dHBNWlBLT3BWUkczSU9zVGNqaU1sR2ltV3pJUWJGU0R3SU53UmhVQ1ZxRVhFMFhBUytvM0szeVBqaXBtMjhjeUFuMktmZ0pCYnVrQUs0NWl4NHFNVURxYXVLTVhZRmVFMGZKdDNFRGN5bjBPSUVFMmEwZFB2WlRheTFrQTd5a0N6THpGd2JFYnBsSzFWTEpkcVY0bmE1RER2VXptNXVCZUY3a2J0UzRvV0dsWmlCUFR5a2ZjVEhnUXczTEp2U1JTTCtKb1l0aEpSMXhaVUtwY0JTZTFzMVBZY2RlQSs4bkNkeFRoc3hoRWt1VmF6ZEVsQTR4TWQ2c3AwbnR4c0d1TVZTZTBWRkhIT0pGbmo0blVlRCtVMXI4cFJmcjRRb1hxWm9tamFhVUN5N2RRYmxyYmxuUTdRRG5LdU5qYW16cDBWNnFqVk9ETXk3cGxYa3dGaU9VYmJnc1N2SDBteXFxM0JyMzJjclJFU2JOK0lMRFVyVzdRdU1ETW8xa3plbW9aYW9Gb2lUR3dXSUJsa01RdElyS0ZrdHFGaVd2bWRmUTVqUlYxWE1CQWdsUnBFRk1oQVlxQkt5Rm4zalRwN3VKOU5IV3lMdUtSSGRESWZsZUkrREpIaW10VXhTVTM5YmdKTG5UOGNiWGtVY3daVnhPcU01cFQ5cFNWU2RhT2FMZjJsTjJYMzBMSnhGeENEVTFBcHdSRFdLRC93Qm8wbmlqRVhsakhXamE1dGJVRldML0FKc3pPT3hGWW5FQUhnelczMjU4UmpLb1JDdGRBNDA1bENuQ0tid2RlQ2sraHhFMmlXSjFzeWtjY1pyVG1tcnFPWGRIVUE5a0VjbUIzcXczZzRnblpJMms3ZW0rNi9uYkhHNEZpTVV5cit2SWd0dHYzUldLUFBjSkJ5YnJjR05vK0JOamZrUVJ6Qkc0akV6Yk9TRjk1cDNQZHZ6QjRxZlVjUmlQL3FOVWVDamY5aTU5MDhpZXlkM0E3cU9jdG1OSERHV2VqWW5ydUkrL0UzZmkrWTM4YWVQMmlzeTJuSWQ2Wm0vclZML2F3UDNsL2d3eEpFUlExUmpNbE84SllncUJ4bHB5YmhvN2E0bU53QWJxMEVZcWFldHBYTW9VTHdKa1crMmg1Q1VkZE9EQWpoUnhHR2xyVmpRUG9mallOMUpJbTRtTWt4dHhVbzJLaVVQVHlVYnlQbytPblk3d3dIR0ovdEZIQXlMaVJOY2RiUnFxTkF4OTRBV3NUeDNhTDhRaHh0Z0lzem9sWXkwcmpnVHZ1amdjR0RDL0ptNERNQ1dycVFyOW5OckdseTBKQU5qM3doQkIzNkFlS1plWmRlZFRUelJSbUNTMFRJVkNobEt5eU11a2xuSFBIUTdOVGxzRmJYNVRTMDlRMUUwVjBWQlU2dElFeGt1U05UYThDRmtEVEc0a0JYdFhIRUVXSnQ4TGpGVlZFNVZtSnVYb1h2Y0VFZklrRDhhNG5qRWs5TFNtOFZZaDYycVBUelBhMGpuMWszM1hGQk9KMEtWSHM5WlJUZzJFaU9CZUtTNHRyQUtQYXpyZmNJRHFrUXA3TUpEeWtNWXY3TkpmZnFqTFFzZDRNWndxamFQVWxraXJnT0czQzhXNUNkQXdQZUI3V0hBSmtlTlFsU1BmUjE2a2dQdkE0M1dLbXpERkZGZFpHNDFzSTVONHV2anpHQ0xYMDRqSjNNdDFkU0NHUmh6VmdTQ1BBNFp3a3NETnFlaWM4STJQTlRZNlg1OERZakVpYVpvWDdNaStCL2lDTjRPS202amFXSmljY1kyUGp6QjVnM3dGMDA5V3d1b1gremwrRWNtNWNEdTRRem1XZWhwN0NhaFp0N1MwL2RLdHhhTW5RNDhEWnNiVGFtbW95UkRXU2dXTTFMSUFUQlBZZGFOaDVNcEdLdVhWUjE2VTVnYW1tN0xMSWQreW5IQmxJS1NDeEZ4WWllOHRKTEJBS2RxYWZpZkVSdWU4dTlKQmlTWVVrck5Cck1iRGVFZENTQWJieDF2TkdHR3FMK3ltSXU3a2I1SW52WXNiRUVIdGo0eGlyalpDNlVwRU5RUmJVRHBGMVpTZDl4Y2dqZHpLb0o1dllJMWtvYXBibTBoaU81dkJocFBEZ01Wa2lLY3hnb3BIcGFoWmx0c3hMR3dPemRXM01WSlFOeE5zZElUWk12aW5qbHE2TjZjdEtUSWhRRXZZT3BadVJPSVUydFBzMklNREEzS0h2QmIvQUprSjRFR3hqR3lpTlVvUlpqM1NqZVBNV1BtaDVZbFl0bzdiMHFFN3lmQWVMQWFEM2doeEdOYmxEczJkanlsWG5maGNuZU84NHhUdUJUZElhUVBzQWZlMXFOVUorV255QXhPQTBGVWdqWi9VV09pVDFRaHNYTEdDT1k2NlpqeElWK3RHZlE0MTlTT1ZnMWg0SEVSdXVvWEI4aU1JL3dEUHFJNzlpVDNrUE5ENTd4aTNhUUFqRlhDWUt1bWt1b2xRbS9IdWtFQWc4aU1QVWV6c1pXdEpETGJWb2JrYmplR0hIeXhJdHBxZVk2b3BoNE10L3dCL0VjaU1TSUdsU056TEpSSHZSeWdkWUFIZzVGaU9lSXdVaG1wSXkxUlRJU1RwQTRTUmdra0lTQ0xuU1J3TldxdThrTWpHbnpHUHRSenhzYkJUd0t1QnFVM0I1akZPb1JxNlduRzNVY0F0UW82a3FIaHFzUnlJR0tzcXNPVzNDd0ZqM0luNE9wUFpXVGVMZ1hQRXZHc1ZkU3cwNFNQYUxjRm1ZS1U5UnlOOFE5U3JvYVdtRWtMb0RkRGNBb29zZDRBeHNRMHFVbThPYlhFb3VHR2tqM3ZrY1MweVZOZkhIZXBncFZkaVZsTDZHV3p4NkRvVTN2ZkVXV1VnaXpCTXBkS0xMd1lWN09tSXBvYTViMFlpd3hVa3h5MDVxTnBGUk02MjJNSU1vQmpCUFZOdXpiRWhFOVZsODB4Q0ZPQVljL0svYUh4akZJK3pGUkhIWm9aU0NiYWhjRUhqNEh3VTRrY3gwbGRIS0duVUR5SnZjRC9Sc1RneVJ6VTBSaUk5NjZHd1BuYTErWWJFNDJjTlRJdDRuNVdaVFowOVZCSHc0bDY4bExReXBOUlZOK0JhQWd4TVB5bzJJQjl0WFpIVmV6dnArT25uSlUraXlZWVhrSW9lb3g4U3FrajZISEZCSkU2Zy9VYnNUSm9uak5VRkxyNm14QndJbWtGRFd6cFp3b3VRc3FrZy9tVmNLU3JhWlZOaURZNGttMnpVYzREUmlRQzJ0U0NHVnJicmdqR2RVNzFDWmE4b2wwUWhyTEtKQnVzeEI2aDZ3NTRwcDBrRnFuWk13RFhLNmdiaS9ERmZPOHFaWm1rYXdNR1lsaXF5UkIwUGxjSmlHdUM1blRtWDJpeUI3U2xGRmdIdGV6S1FiODhSTzU5cmdwQUpSRmMrK0xucThkMkk1eEtsVjdIS0xBQy9WWHNCMVlBZzJHSmtra2xxSzU0b0NaQ2pGQ0JGZmVXMDc5T0k1Rld0V21rVll5bkE2aURyZmQ0S0w0UXBCUjVja0N3VWo2T3FvbGN0MWh1NGJsSGhpajJjTUdTMEVHbE5rcWk4YlBZZFFIVllDNUF4RlRRK3dJa0cwcDZZTW9JRHNZMmRsREUyQUp3YUJKS0NoZ3Bnb3BvdG5vWGdqTjFpQ1F1bzZRYlh4Ly9FQUNjUkFBSUNBZ0lDQVFRREFRRUFBQUFBQUFFQ0FCRVNJUU14SWtFUUV6SkNVU0JoY1NNdy85b0FDQUVDQVFFSUFQNEEvd0FyL3dEQ3g4Yk0zOFpBZC9WVCtOZkZ5NFBuY3NTLzF1V0IyZWJpRUhOZjIvVTVKbTVsdEthWWlVa3RCQjhYTGxpRDVxV0JBU1pqKzI1T05CUHJzMzJGbmJzY1lNQzFDVTlqbFVkWnNaWjk2bmpMQW1meHY0cjVzU3hBSm9UT0VuOHUvc3h2N3MxR2hrN2RZR3Q1Y1ltWk1ETDdEQ0FrekdZd2d5d0pjeWdMR0FINDhCQlIrR1pWbVR0MEZBN3NuckF0QWlyMjNJZWxJSk84Q28zWTlDekZNVXdRQ0VDZUk3UE1pejY3bjdSOWRvQVIyUXNHQzlmVmc1VFBxRXpKak5tSWFXZ1dhVUF1d0wyY2orSTQ4ek1VOUJHYzdIR0JxQmQwRlZ1b0tHaXU1Z283cmpFK3A2R1JNR1VvKy9HREgxY3pFeUpnSkc1ZHk0Tkc1L1pzWGNGSHNEMU1oMUZLNDBLUWREdTVrM2NMQXk3bU1Mb0ptM29aSHNBQ1hDVEJqTW9vSjJTMXdVTmxUZTR1OXdiZ0VPNVdxQmZWSC9tZXJ4Z1krMUlQUUptZnBTVi9LeVBnQXl2MFpVcHEwSzZKQUEwTEpqSGRSaml0UzhVZ05MOGY0UUlTUkRaNy93QVgrbDMybG5vc3hnME5DQ0FTckV4QmxEMWpTeEJVZUlvcURiemxqOVJ1aFAxQkNObU10UWo5VmNHNHRRYjNLUHNkd2Q2UUQwZ3ZvN01JSVRiRFVyUUVVYU1ZYk1XSnBoT1VSdHFERHRBWURhQm9Pb1lRcEZrb2pRZ2lmMmY5QkFPeVN4SUNxQUlQS0JRb3Rsc2lBWDB5MEtEQ0VhRUhSaEZtTEhHSnVNUTZ4TjhjNGZKQ3M0VFlLbFAwUnNFUUFYQ2dxZ2VNa1pJR0lGZ0VVV0FwQmNZL2dvVlNBczR5b056ajhuTEZtc0FBUWp4bFRIeGxWQkNLYU1Jb3ExUEdhYWpaNHVUS09NR3pIWXVFbFRrQ1JWdzJZakJnWTVGNXdmOEFOcmc4Q1p4N0pNUmkyUmlIeExRZVBFSW15SU5FenNTb0lZRFJqYkZ5d1JIR0p1TU5XRVAxRm84TGQ4VEo0SEVnaTZKdmhhTnBjbEp5R2xETUxpb3BGUUcwMXhmWk9IN0RPTUVvUk8rSlRPTDdRWTJtaUhWUTl6cUV4aExOWEx4TU5PSnhzRk9KSVBFMWhsWGxYTlZKZGQwSEZRUDN4OGh2ak5FaXVrNUtXd2c1SFFobENxTG5HS3RaeGFZcktIcmpOV2hYeEpVaHIwUWFoM3VlcGZxWGVvSTZ3TVZNZGJGamljTU1XVUhoYUdnYmpEV1NuSGtGR3lQRmxMQXdvV05xMTBMSkhHcEFhME5obHk4bHNPc0J5MFVJZnhadUlzTEN2KytNZ21vZEVpTUpkN2dzOVZxUHgxT0p2eExjWlUyRU5pQ2hxRXR4R0VnK1MwT1JadGRGTVZPVVFCeWI4bUVGTU1TcFBHYUxBL2NxbFhFQXNRNWd3dWI4bG95aXdsNjMwYkEvWUZ3cnFOeHpqYlZOVlM2RTlRMmh1RHkyQTZoYVAwNjhvcldhQzRycUVaQzFWZ2ROVGNmUXhmWURNdjNMeUFpbXBDaG9LQjB1UTZJSkZnN0VCQ3dEVmdVSlMrcUVOaVhDRGNMcDBHNG03QVlLS05PYWhZZXRpRUhqTmpUd0ZrZ1ZIMkFqVkNtSnFLWFUzTXVKNWlmeEQ4eVhGWm4wTXdld3dxVW9BTURDWGtkYnZaVi9RQVViV3N0QWMzS2FoVlExQXErVkVqSFVMTWV2LzhRQUtSRUFBZ0VEQWdRR0F3RUFBQUFBQUFBQUFBRVFJQ0V4RVVFQ01GR1JnYUd4d2RIaFlYSHdRUC9hQUFnQkFnRUpQd0QvQUJvVXM0dWNvWXpoWm92RWZaR3A2bWcxMkh6SEhDY1hZV3Y3TEZ4UXhpRUpjeDZDSHJMTHd4Y3QwV2pGRnl4ZXR3bzRoaXFXbEZvV28vZzhpd2h3eFVPRkROUjA0akVPSFRjV2c2Rkh1ZXdxa0l5T2xsanpxZm5IdHlISHllUmN1V1JhbHhwelBzK3o3UHN1WFpkeDNpOHFPbkpZeDZycWR6dVdmV2NIWXh5dHFjTXlqUFNGYmRGK0dQNUcyRGJsN3hoemhtVmo4bWVoNC9KZ3VwL3RJNlY3UnRHWXhGMHk2OUIzMllyN3FObzZuNU9oK3VWaHptanNZUEF2RzV2Nm1IZzNOcThVWW5NWFIzSGVuUDhBWE1tVlhpckZQQ0tNVVBSbG5HYWNWWUdNWTlLTUZuRnhhcUhDaDJIeU1tQmpxczVaZENGRzlPUlFyMGYveEFBcUVRQUNBZ0lDQWdJQ0FBY0JBUUFBQUFBQkFnQURFUkloTVFRVElrRWpNZ1VRRkNBelFsRXdVdi9hQUFnQkF3RUJDQUQrOGYzZ2ljbitXUU85Z1prejV6Rmt5d2h1UmV6NWZqanVEL3h5SjhvV1FkaHMvcmw0em9uN3Q1L2lKQjV6Ti9qUGwrVlA2aTh3dmNZZHoyVUgzK0lUZW9mM0QrZU9PZDBIUU5qZGVzZHRaNVhpMERrK2ZkWVB3dmRmWi9rV2xDY2hRVUVObEgrdzhtdGYwUGszTk11ZTlWK3d0WW54K2lUOWZ6K1V4TUNFcU8vWUQxZ0h2WkZqZVI5TFlXeG0wTnNjVU5XR09iZmRXbkNleTZ6bzFOMjVhcFlMQ2VsUC9VS3hTc0ZaYnIwZ2Rtdi9BSmxSTnhEYUJNMnZBamZlc0xVS2ZsV1ZickFsMTFWVU4va1c4SUtrVExXRjdMRklRVU5ZWVBIU3NaZDcyNnIxWmp5YWluY1V4REszeEZ5ZXdxelZUMStOUDJzODd4cWhEL0U3V09LVlA4U3M1S3F5L3dDUnZTZXc5RlhLdDV0cDZYekxoUDZxMW9iTFdFSkpPRDQ5Z1dzb3RqMms0aFVCQ1dDWitUZTFqa0lsUnNiSksxaklTdXV5MW90UDBBR0RhaEVzQjFnMHI0c1RMeHFxVkg1R1BocjEvVk4xVzFqdDJQWVpxMzNoQkFWK2cySjdSQzVNRHVCbVozZ09Cd3VRMlo5NW5UNWlldHYzVkFBVmd1WFhRVnZTYTlRUjQxYTVRRWJCd0xydHRvYmxzUHl6bUJQc3RaVXZCRnJrZkVaUFlFQUFqRXdGUjN1c1VFOG5hQXFQMlFqdUp5TXhBTWNoUmpJY0Joa0ZDZmlwc0d1R0lvSzhaMFBBc2IvYXM1NVJIUDhBc0xUMVd4WHQ4a1FBSHRRTVRCamtEaUFabnpQUWJKMWo2Z2NkdEdKQjRKMUdJVzFRQ0JpRUFoeWNDTU5qeFlvWHV3a0hKSUp6UHFLQUlvQkhOWWNqS2xpUkJuc0tBT1NvaXFSTlJqTTFRamc4OFFxRnF6S0Z3Y3l6OWhLa0UvWmdKWjJaWTN6ak55c09OMW1Cc0pZcERuRjFmMkdHZjVjamtKbzNhcXdHMHhCbUlPVEU0SEZTTXg0dEpac0cxYlVwdzlvQVhsbElyckFyQjliU3hlV2xVcndIUW00ZHgrY0daMnJSb3h6VWp3OUhGd0I1Sld0Nmd6K3VsOHg2OEhqQjdJK0pCS01vT1Njc1RBb3dZb1VESkNDcGRyVUZqSVlCbmhicXZYV0FIVVIwT2lST0ZZVFRaNHE0Smxpa0tjRnZaZ3hNbXVVSUxhR1VlSy9zcktHbjVLVVpNdldVaW9xdU1QUW9RcW5vYXl2ZFF6S0lwMVhNQVdvY24vNFFWb1ZDQ2cxcXhzUGlzTGJtdHN2dEJxQ3F2SFZpa1VxSnJQWHRWTWFtVm5rU3hSWGRHWFJzRXBqS3hEcStUVTU4YS9hWElLTHZZcDczbGhhc2k1YkNqSnVHMlpoS1hEb3dXMnhIL01LejZYMWF2OExsQ24zaEdaaTdRdGlvc0FEVDRxeWtiMktKMVlSTlExZUppVmpzUzVjaUJzUEx2eVZiRElkT1NTRG1XSUY1bFlOeUZUNGxnZFQ0OWxXYXlhblZoVy9yZHhaNGRtVnNWR3IzckxiNERLdGpzWFNyeDZTZ1U3RjZnNkp3Qkt2MUV4bWdyTFFMUERyY2VLUjYwc2wzeHR6UEhzRExySCtMa1FXYW5Nc0lJbGc1aVdrRE1jK3Q4ak93NG9kVCtObDluaTJ5MnBia0YxU3Q3NjVoZklUUXJlVnpSZTZ2NDc1SEJ5QlhlMVM1V3V1N3lLU3JWcFhVbXpKa0ZsaWNrcE5WWUVqeGJGdzNqdlNUUzdWTjdHUHhhcTBxY2kxZzRGZ0ppMmJmQ0U3Y0VaQkpqalhncVNqWUxLUnpLblc1UFhaU1g4VjhGc0kzc1c1TjE5bFJ0cjhoTmJTWHJCUmxJQkdBak81WVA4dFNMQ2xDRkZJS05zSFVzdVVETFltWXczSE5ESjVBOVYxdmh0WW15SlllbThabEw2bHdhM0t5d2Y4QUN4c0c0VWx1UVYrTXNxSTRQak9EK04ycGFrNUNNSFRtdGd2eFp0L0diYXV3cGNkMFU3S1JQcm10a3FmYzBJSExSVGJZdXhWcy9GbE9rWldCM3JWd1JrTFg3UndXdFY4bDdIM1B0UXFZQTE2SEcyUnlTYTIyVURIelJkdXcxWTFqVllsTnVWOWRnVTFIaHVGekJZeXJHSE9RTW5KZ3RVSmdpcjFqYUlTelpXa1ZnbXNzbVJrQW43d1ZHVkRLM001WG1WK1VNYTNNdERVRUxvb2I0MUcxRzJWMVoxTmlNUVJ5ckNzbUtvQ2JoQ282TmRaNVVCWTdNT0R2OWhzbDhLOXRXdUExRHFNd1lBMWdENEd6TmtqVXN5bUhOY0dHNUFCSEkxUitZbE5tQVpZclZObzlUdldkbGUzd2J4bUdzNStGZC9sVVpsUmU0Nm9iUWV4WUF1c1pLMFJXbGJxcEJHUmE1MDVEWVlyWVNkUUF2YWMyeEU4bnkzeENwU3dxcjFXQjlYZW9vZFFiU2YxLy84UUFMeEVBQWdBREJBa0VBZ01CQUFBQUFBQUFBQUVSSVRFQ0VFRlJZWEdCa2FHeHdkSHdFaUF5NFFNaU1FRHhndi9hQUFnQkF3RUpQd0QraTdrSWdORnRMY2ZsWEQrVmxtNjFEZ1dvNnBuNG0rQXJObld6OG02ejNQVTl5RnZ0UG9lbmkrbzF1UmE0THQvQ3lZb0RpUGNmamxtejh1eXpQNkxEZW0wKzNjdEt6cVEzYTJsbFhNWWl5aEwzdjJLSmE5SzRsamE1c3RSODNDamN5WXZZcmw3V01jUlhUWXZTdEE0dmlTc3JkOWliMTlqZDVKQ0prdmE3MlRMQkpiajhxTGJaWm5wR01hM0R1VU5MSmlLWkVoeThvaFIxbE9BNDZoZFdKOGtOUnlxeU1OeGFMTVhwRkRVaDlYY3hpSkZvYjNGcTkzVXlHb0NndEdPMGNGcG1XbnlJcDUxTFVZYkNRbnNZOTAvb1NXdVk0aWdPSXI1bk5uVWZJWENIS1B0VUJlYUN3VkxXOFVSaUZBY1JlbGNUamY1c1YzTkRndGZZNzh6RFZmaU90OGkxMTQzeUZ0US9VdUpKRys3N3Vud1c4Y0ZvVXZzbnFJYzJuMTVJZE5CaEYzWXZyZGd1M3NqWEFYUjdqczc5NjZvbXMwVHUza2xuaUkvWjhCSngwS1dnZXdVSmMyT05PWXNHYU9wNU15WE5veFhROHdNREFrb1JsbVBiM0xUbGc2clV5MUhJM2tubWgrbDU0Tzd6UnI1RkNkcDBXWDJTV09XcFhVNjNaZFdaR0tQSUdENW1QWDdLMmZPNDZUUEhkVkh4dFlEL0FHc2I0RmNWbnBXVE1lT2g1UFNLTmwxMFB1TDFXR2VhV09IUlo2Mktpa3VCUmN5ajVlekIzVlJRMWRuZGpKNnloOGJWMjArTDRQUHVPRnRVZURXVEpXbFZZcGkwV2x5dEUwK0sraWRtcHFNWEJlYmpQZ3BJeFVUQ3l2ZGp6S284UlZjVDRzcWpaMktZTW95ZGxrN09XV3JSb0hCcWxydm81Qy9aZkpaeDZQbU9HVWFyUVZzdHJZWm1mWXdiUmxBd1VIc01mYmliU2pQOVdSUjBkMVQ1SXFpbVk2NDRQV0xaMnlZNDJlSzdjbU9Md2J4Uk51c0tSTURHaFcxUC9wVlcwcGFtbnB5Sksxd1pWZXpaZHQ3bEh3dTI5MGJ5YVpRcVNlWk5FMXhYYzNycU9GcmVZbUhGRlZORkhYUTgxMHpSV3YydXFIKzJEekhHMHVPdlNTWTVPK3FyMzczVWZBMk82bmxMcUUwVnk4eEtjaDdUYmt5eHBxM1FzcVJWRk9SdFdaNTJlbWpLN3Y4QUh3RzdOclBQWDNRcDg3dmt1S3ZweXVjbWIrOTFDaFIzT1k1NTVqWFFjSWViaE9aaDdNTWZLQzlTODJQWVdQVlo1ZGlLR2JVSW81TkRsUVpOY3JtTWNJblloSEZDbVNXUmFXQmFoN0pNMytUUW9NVzZYQnk0amRsNmkwUWFZcDVQdVdSU3lGWEFSVjRGTVNaVXM3N2xPZ2tKU0ZNLy85az0iIGlkPSJlIiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIvPjwvZGVmcz48L3N2Zz4=", + "description": "Displays vibrarion data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'vibration', label: 'Vibration', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'mm/s', decimals: 1 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'vibration', 'mm/s', 1);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Vibration\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 3.3 - 1.7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"mm/s\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2.8,\"color\":\"#3B911C\"},{\"from\":2.8,\"to\":4.5,\"color\":\"#F89E0D\"},{\"from\":4.5,\"to\":7.1,\"color\":\"#F77410\"},{\"from\":7.1,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 3.3 - 1.7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"mm/s\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0,0,0,0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"mm/s\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:dmlicmF0aW9uX2NoYXJ0X2NhcmRfYmFja2dyb3VuZC5wbmc=:dmlicmF0aW9uX2NoYXJ0X2NhcmRfYmFja2dyb3VuZC5wbmc=;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Vibration\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"waves\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"margin\":\"0px\"}" + }, + "tags": [ + "vibration", + "pump vibration", + "motion", + "resonance", + "dynamic balancing", + "mechanical integrity", + "pulsation", + "frequency" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pump_vibration_range_chart.json b/application/src/main/data/json/system/widget_types/pump_vibration_range_chart.json new file mode 100644 index 0000000000..d79cc32935 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pump_vibration_range_chart.json @@ -0,0 +1,36 @@ +{ + "fqn": "pump_vibration_range_chart", + "name": "Vibration range chart", + "deprecated": false, + "image": "tb-image:dmlicmF0aW9uX3JhbmdlX2NoYXJ0LnN2Zw==:dmlicmF0aW9uX3JhbmdlX2NoYXJ0LnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxnIGNsaXAtcGF0aD0idXJsKCNhKSI+PHBhdGggZmlsbD0idXJsKCNiKSIgZD0ibTcxLjggNDkuMy0yMS40IDU2LTcuNCAxN3YxOS4zaDE0My41di0yNS4zTDE4NiA5MGwtMTAuMiA0MC01LjktMjUtMi41IDcuMy0xMSAyNy41QzE0OSAxMTAgMTQzIDk4LjggMTM1IDY5LjRsLTguMiA0My45aC0xMC41bC0yLTcxLjMtNC4yLTI4LTE5LjUgNzAtNC43LTIuMi0xNC4xLTMyLjVaIi8+PHBhdGggc3Ryb2tlPSJ1cmwoI2MpIiBzdHJva2Utd2lkdGg9Ii45IiBkPSJtNDMuMyAxMjIuNCA3LjQtMTcuMiAyMS40LTU2IDE0LjEgMzIuNiA0LjcgMi4xIDE5LjUtNzAgNC40IDM1LjMgMS43IDY0aDExLjFsNy00NC43IDIyIDcwLjkgMTEtMjcuNiAyLjYtNy40TDE3NiAxMzBsOS44LTM5LjkiLz48L2c+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTEyLjIgMjkuNGMuNSAwIC44IDAgMS0uMmwuNy0uNS4zLS42VjI1LjhsLS4zLS42YTEgMSAwIDAgMC0uMy0uMy45LjkgMCAwIDAtLjQgMCAxIDEgMCAwIDAtLjguM2wtLjIuNGExLjkgMS45IDAgMCAwIC4yIDEuNWwuMy4zLjQuMS41LS4xYTEuMyAxLjMgMCAwIDAgLjYtLjZsLjEtLjVoLjRsLS4xLjdhMiAyIDAgMCAxLTEgMWwtLjYuMWMtLjMgMC0uNSAwLS43LS4yLS4yIDAtLjQtLjItLjUtLjRhMiAyIDAgMCAxLS4zLS42IDIuNiAyLjYgMCAwIDEgMC0xLjVsLjMtLjYuNi0uNC44LS4yLjguMmMuMi4yLjQuMy41LjZsLjQuN3YybC0uMi45LS41LjctLjcuNS0xLjIuMmgtLjF2LS42Wm00LjctMi44VjMwaC0uN3YtNC4yaC42di44Wm0tLjIgMS4xaC0uM2wuMS0uOC4zLS42LjYtLjUuNy0uMWguNmExLjEgMS4xIDAgMCAxIC43LjhWMzBoLS42di0yLjhjMC0uMiAwLS40LS4yLS41bC0uMy0uMy0uNC0uMS0uNi4xLS4zLjMtLjIuNXYuNVptMi44LS40LS41LjIuMS0uNy4zLS42LjYtLjQuNy0uMWguNmwuNC40LjMuNFYzMGgtLjZ2LTIuOGwtLjEtLjVjLS4xLS4yLS4yLS4zLS40LS4zYTEuMiAxLjIgMCAwIDAtLjkgMGwtLjMuMi0uMi4zdi40Wm00LjUtLjdWMzBoLS43di00LjJoLjd2LjhabS0uMSAxLjFoLS40bC4xLS44LjQtLjYuNS0uNS44LS4xaC41YTEuMSAxLjEgMCAwIDEgLjcuOGwuMS43VjMwSDI2di0yLjhsLS4xLS41LS4zLS4zLS41LS4xLS41LjFjLS4yIDAtLjMuMi0uNC4zbC0uMi41di41Wm0yLjctLjQtLjUuMi4xLS43LjQtLjYuNS0uNC43LS4xaC42bC41LjQuMi40LjEuN1YzMGgtLjd2LTIuOGwtLjEtLjVjMC0uMi0uMi0uMy0uMy0uM2ExLjIgMS4yIDAgMCAwLTEgMGwtLjIuMi0uMi4zdi40Wm02LjQtMy0yLjQgNi4ySDMwbDIuMy02LjJoLjdabTMuMyA0LjZ2LS4zbC0uMy0uMy0uNy0uMmE1IDUgMCAwIDEtLjYtLjEgMiAyIDAgMCAxLS41LS4zIDEgMSAwIDAgMS0uNC0uOGwuMS0uNC4zLS40LjUtLjMuNy0uMWMuMyAwIC42IDAgLjkuMi4yIDAgLjQuMy41LjVsLjIuNmgtLjdsLS4xLS4zLS4zLS4zYTEgMSAwIDAgMC0uNS0uMSAxIDEgMCAwIDAtLjUgMGwtLjMuM2EuNi42IDAgMCAwIDAgLjVsLjEuMi4zLjEuNS4yLjkuMi41LjQuMS42YTEuMSAxLjEgMCAwIDEtLjQgMWwtLjUuMmgtLjdhMiAyIDAgMCAxLTEtLjFsLS41LS41Yy0uMi0uMi0uMi0uNS0uMi0uN2guN2MwIC4yIDAgLjQuMi41bC40LjJhMS41IDEuNSAwIDAgMCAxIDBsLjItLjIuMS0uM1pNMTUuMSA1MHYuNWwtMi4zIDUuM0gxMmwyLjQtNS4xaC0zLjFWNTBoMy44Wm0xLjggMi40djMuNGgtLjd2LTQuM2guNnYuOVptLS4yIDFoLS4zbC4xLS43YzAtLjMuMi0uNS4zLS43bC42LS40LjctLjIuNi4xYTEuMSAxLjEgMCAwIDEgLjcuOHYzLjVoLS42VjUzYzAtLjMgMC0uNC0uMi0uNmwtLjMtLjNoLTFsLS4zLjQtLjIuNHYuNlptMi44LS4zLS41LjEuMS0uNmMwLS4zLjItLjQuMy0uNmwuNi0uNC43LS4yLjYuMS40LjMuMy41djMuNWgtLjZWNTNsLS4xLS42LS40LS4zYTEuMiAxLjIgMCAwIDAtLjkgMGwtLjMuMy0uMi4zdi40Wm00LjUtLjd2My40aC0uN3YtNC4zaC43di45Wm0tLjEgMWgtLjRsLjEtLjcuNC0uNy41LS40LjgtLjIuNS4xYTEuMSAxLjEgMCAwIDEgLjcuOGwuMS42djIuOUgyNlY1M2wtLjEtLjYtLjMtLjNoLTFsLS40LjQtLjIuNHYuNlptMi43LS4zLS41LjEuMS0uNi40LS42LjUtLjQuNy0uMi42LjEuNS4zLjIuNS4xLjd2Mi44aC0uN1Y1M2wtLjEtLjYtLjMtLjNhMS4yIDEuMiAwIDAgMC0xIDBsLS4yLjMtLjIuM3YuNFptNi40LTMtMi40IDYuMUgzMGwyLjMtNi4xaC43Wm0zLjMgNC41di0uM2wtLjMtLjItLjctLjJhNSA1IDAgMCAxLS42LS4yIDIgMiAwIDAgMS0uNS0uMiAxIDEgMCAwIDEtLjQtLjhsLjEtLjUuMy0uNC41LS4zaC43bC45LjEuNS41LjIuN2gtLjdsLS4xLS40LS4zLS4zYTEgMSAwIDAgMC0uNSAwIDEgMSAwIDAgMC0uNSAwbC0uMy4zYS42LjYgMCAwIDAgMCAuNWwuMS4xLjMuMi41LjFjLjQgMCAuNy4yLjkuM2wuNS40LjEuNmExLjEgMS4xIDAgMCAxLS40LjlsLS41LjItLjcuMWEyIDIgMCAwIDEtMS0uMmwtLjUtLjVjLS4yLS4yLS4yLS40LS4yLS43aC43YzAgLjIgMCAuNC4yLjVsLjQuM2ExLjUgMS41IDAgMCAwIDEgMGwuMi0uMy4xLS4zWk0xMi40IDc4LjhsLS42LS4yLjMtMi44aDN2LjdoLTIuNGwtLjIgMS41LjQtLjEuNi0uMS43LjEuNS40LjQuNnYxLjVjLS4xLjMtLjIuNS0uNC42bC0uNS40YTIuMiAyLjIgMCAwIDEtMS40IDBjLS4zIDAtLjUgMC0uNi0uMmwtLjQtLjVhMiAyIDAgMCAxLS4yLS43aC43YzAgLjIgMCAuNC4yLjUgMCAuMi4yLjMuMy40aDFjLjIgMCAuMy0uMi40LS4zbC4yLS40YTEuOCAxLjggMCAwIDAgMC0xbC0uMi0uNGExIDEgMCAwIDAtLjQtLjNoLTFjLS4yIDAtLjMuMi0uNC4zWm00LjUtLjd2My40aC0uN3YtNC4yaC42di44Wm0tLjIgMS4xaC0uM2wuMS0uOC4zLS42LjYtLjUuNy0uMWguNmExLjEgMS4xIDAgMCAxIC43Ljh2My41aC0uNnYtMi44YzAtLjIgMC0uNC0uMi0uNWwtLjMtLjMtLjQtLjEtLjYuMS0uMy4zLS4yLjV2LjVabTIuOC0uNC0uNS4yLjEtLjcuMy0uNi42LS40LjctLjFoLjZsLjQuNC4zLjR2My41aC0uNnYtMi44bC0uMS0uNWMtLjEtLjItLjItLjMtLjQtLjNhMS4yIDEuMiAwIDAgMC0uOSAwbC0uMy4yLS4yLjN2LjRabTQuNS0uN3YzLjRoLS43di00LjJoLjd2LjhabS0uMSAxLjFoLS40bC4xLS44LjQtLjYuNS0uNS44LS4xaC41YTEuMSAxLjEgMCAwIDEgLjcuOGwuMS43djIuOEgyNnYtMi44bC0uMS0uNS0uMy0uMy0uNS0uMS0uNS4xYy0uMiAwLS4zLjItLjQuM2wtLjIuNXYuNVptMi43LS40LS41LjIuMS0uNy40LS42LjUtLjQuNy0uMWguNmwuNS40LjIuNC4xLjd2Mi44aC0uN3YtMi44bC0uMS0uNWMwLS4yLS4yLS4zLS4zLS4zYTEuMiAxLjIgMCAwIDAtMSAwbC0uMi4yLS4yLjN2LjRabTYuNC0zTDMwLjYgODJIMzBsMi4zLTYuMmguN1ptMy4zIDQuNlY4MGwtLjMtLjMtLjctLjJhNSA1IDAgMCAxLS42LS4xIDIgMiAwIDAgMS0uNS0uMyAxIDEgMCAwIDEtLjQtLjhsLjEtLjQuMy0uNC41LS4zLjctLjFjLjMgMCAuNiAwIC45LjIuMiAwIC40LjMuNS41bC4yLjZoLS43bC0uMS0uMy0uMy0uM2ExIDEgMCAwIDAtLjUtLjEgMSAxIDAgMCAwLS41IDBsLS4zLjNhLjYuNiAwIDAgMCAwIC41bC4xLjIuMy4xLjUuMi45LjIuNS40LjEuNmExLjEgMS4xIDAgMCAxLS40IDFsLS41LjJoLS43YTIgMiAwIDAgMS0xLS4xbC0uNS0uNWMtLjItLjItLjItLjUtLjItLjdoLjdjMCAuMiAwIC40LjIuNWwuNC4yYTEuNSAxLjUgMCAwIDAgMSAwbC4yLS4yLjEtLjNaTTEyLjUgMTA0aDEuMmwuMy0uNC4yLS41LS4xLS42LS40LS4zLS41LS4xLS42LjFhLjkuOSAwIDAgMC0uNS45aC0uN2MwLS4zIDAtLjYuMi0uOC4yLS4zLjQtLjUuNy0uNmEyIDIgMCAwIDEgLjktLjJjLjMgMCAuNiAwIC45LjIuMiAwIC40LjMuNi41bC4yLjktLjEuNS0uMy40LS42LjRoLTEuNHYtLjNabTAgLjZ2LS40aC42bDEgLjEuNS40LjMuNXYxLjJsLS40LjUtLjYuMy0uNy4xaC0uN2wtLjYtLjRhMS40IDEuNCAwIDAgMS0uNS0xLjJoLjdsLjEuNi40LjMuNi4xaC41bC40LS40LjItLjYtLjItLjZhLjkuOSAwIDAgMC0uNC0uM2wtLjctLjJoLS41Wm00LjQtLjd2My4zaC0uN1YxMDNoLjZ2LjlabS0uMiAxaC0uM2wuMS0uN2MwLS4zLjItLjUuMy0uN2wuNi0uNC43LS4yLjYuMWExLjEgMS4xIDAgMCAxIC43Ljh2My40aC0uNnYtMi43YzAtLjMgMC0uNC0uMi0uNmwtLjMtLjNoLTFsLS4zLjQtLjIuNHYuNlptMi44LS4zLS41LjEuMS0uNmMwLS4zLjItLjQuMy0uNmwuNi0uNC43LS4yLjYuMWExLjEgMS4xIDAgMCAxIC43Ljh2My41aC0uNnYtMi44bC0uMS0uNi0uNC0uM2ExLjIgMS4yIDAgMCAwLS45IDBsLS4zLjMtLjIuM3YuNFptNC41LS43djMuM2gtLjdWMTAzaC43di45Wm0tLjEgMWgtLjRsLjEtLjcuNC0uNy41LS40LjgtLjIuNS4xYTEuMSAxLjEgMCAwIDEgLjcuOGwuMS42djIuOEgyNnYtMi43bC0uMS0uNi0uMy0uM2gtMWwtLjQuNC0uMi40di42Wm0yLjctLjMtLjUuMS4xLS42LjQtLjYuNS0uNC43LS4yLjYuMWExLjEgMS4xIDAgMCAxIC43LjhsLjEuN3YyLjhoLS43di0yLjhsLS4xLS42LS4zLS4zYTEuMiAxLjIgMCAwIDAtMSAwbC0uMi4zLS4yLjN2LjRabTYuNC0zLTIuNCA2LjFIMzBsMi4zLTYuMWguN1ptMy4zIDQuNXYtLjNsLS4zLS4yLS43LS4yYTUgNSAwIDAgMS0uNi0uMiAyIDIgMCAwIDEtLjUtLjIgMSAxIDAgMCAxLS40LS44bC4xLS41LjMtLjQuNS0uM2guN2wuOS4xLjUuNS4yLjdoLS43bC0uMS0uNC0uMy0uM2ExIDEgMCAwIDAtLjUgMCAxIDEgMCAwIDAtLjUgMGwtLjMuM2EuNi42IDAgMCAwIDAgLjVsLjEuMS4zLjIuNS4xYy40IDAgLjcuMi45LjNsLjUuNC4xLjZhMS4xIDEuMSAwIDAgMS0uNC45bC0uNS4yLS43LjFhMiAyIDAgMCAxLTEtLjJsLS41LS41Yy0uMi0uMi0uMi0uNC0uMi0uN2guN2MwIC4yIDAgLjQuMi41bC40LjNhMS41IDEuNSAwIDAgMCAxIDBsLjItLjMuMS0uM1pNMTMuOCAxMjcuM3Y1LjdoLS43di00LjhsLTEuNC41di0uNmwyLS44aC4xWm0zIDIuM3YzLjRoLS42di00LjJoLjZ2LjhabTAgMS4xaC0uNGwuMS0uOC4zLS42LjYtLjUuNy0uMWguNmExLjEgMS4xIDAgMCAxIC43Ljh2My41aC0uNnYtMi44YzAtLjIgMC0uNC0uMi0uNWwtLjMtLjNoLTFsLS4zLjMtLjIuNXYuNVptMi43LS40LS41LjIuMS0uNy4zLS42LjYtLjQuNy0uMWguNmExLjEgMS4xIDAgMCAxIC43Ljh2My41aC0uNnYtMi44bC0uMS0uNWMtLjEtLjItLjItLjMtLjQtLjNhMS4yIDEuMiAwIDAgMC0uOSAwbC0uMy4yLS4yLjN2LjRabTQuNS0uN3YzLjRoLS43di00LjJoLjd2LjhabS0uMSAxLjFoLS40bC4xLS44LjQtLjYuNS0uNS44LS4xaC41YTEuMSAxLjEgMCAwIDEgLjcuOGwuMS43djIuOEgyNnYtMi44bC0uMS0uNS0uMy0uM2gtMWMtLjIgMC0uMy4yLS40LjNsLS4yLjV2LjVabTIuNy0uNC0uNS4yLjEtLjcuNC0uNi41LS40LjctLjFoLjZhMS4xIDEuMSAwIDAgMSAuNy44bC4xLjd2Mi44aC0uN3YtMi44bC0uMS0uNWMwLS4yLS4yLS4zLS4zLS4zYTEuMiAxLjIgMCAwIDAtMSAwbC0uMi4yLS4yLjN2LjRabTYuNC0zLTIuNCA2LjJIMzBsMi4zLTYuMmguN1ptMy4zIDQuNnYtLjNsLS4zLS4zLS43LS4yYTUgNSAwIDAgMS0uNi0uMSAyIDIgMCAwIDEtLjUtLjMgMSAxIDAgMCAxLS40LS44bC4xLS40LjMtLjQuNS0uMy43LS4xYy4zIDAgLjYgMCAuOS4yLjIgMCAuNC4zLjUuNWwuMi42aC0uN2wtLjEtLjMtLjMtLjNhMSAxIDAgMCAwLS41LS4xIDEgMSAwIDAgMC0uNSAwbC0uMy4zYS42LjYgMCAwIDAgMCAuNWwuMS4yLjMuMS41LjIuOS4yLjUuNC4xLjZhMS4xIDEuMSAwIDAgMS0uNCAxbC0uNS4yaC0uN2EyIDIgMCAwIDEtMS0uMWwtLjUtLjVjLS4yLS4yLS4yLS41LS4yLS43aC43YzAgLjIgMCAuNC4yLjVsLjQuMmExLjUgMS41IDAgMCAwIDEgMGwuMi0uMi4xLS4zWiIvPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii41IiBzdHJva2Utd2lkdGg9Ii40IiBkPSJNNDIgMjZoMTQ0TTQyIDUyLjNoMTQ0TTQyIDc4LjVoMTQ0TTQyIDEwNC44aDE0NE00MiAxMzFoMTQ0Ii8+PHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0ic3F1YXJlIiBzdHJva2Utb3BhY2l0eT0iLjUiIHN0cm9rZS13aWR0aD0iLjMiIGQ9Ik01Ni4zIDE0M3YtLjgiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii41IiBkPSJNNDYuNiAxNDMuMnY1LjdoLS43di00LjhsLTEuNS41di0uNmwyLS44aC4yWm00LjYgMHY1LjdoLS44di00LjhsLTEuNC41di0uNmwyLS44aC4yWm04LjggMHY1LjdoLS44bC0yLjktNC40djQuNGgtLjd2LTUuN2guN2wzIDQuNHYtNC40aC43Wm0xIDMuNi4yLS45YzAtLjMuMi0uNS40LS43LjEtLjIuNC0uMy42LS40bC44LS4yYy4zIDAgLjUgMCAuOC4yLjIgMCAuNC4yLjYuNGwuNC43LjEuOHYuMWwtLjEuOS0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zbC0uMS0uOVptLjggMHYuNmwuMy41YzAgLjIuMi4zLjQuNGgxbC40LS40LjItLjV2LTEuMmwtLjItLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0uNS4xbC0uNC40LS4yLjUtLjEuNVptNS4zIDEuNSAxLjItMy42aC43bC0xLjUgNC4ySDY3bC4xLS42Wm0tMS0zLjYgMS4yIDMuNi4xLjZINjdsLTEuNS00LjJoLjdaIi8+PGcgY2xpcC1wYXRoPSJ1cmwoI2QpIj48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiIHN0cm9rZS1vcGFjaXR5PSIuNSIgc3Ryb2tlLXdpZHRoPSIuMyIgZD0iTTg1LjMgMTQxLjR2LS44Ii8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTc1LjYgMTQzLjZ2NS43aC0uN3YtNC44bC0xLjUuNXYtLjZsMi0uOGguMlptNiA1LjF2LjZoLTMuOHYtLjVsMS45LTIgLjUtLjcuMi0uNS4xLS40di0uNmwtLjQtLjNhMSAxIDAgMCAwLS42LS4yYy0uMiAwLS40IDAtLjYuMmExIDEgMCAwIDAtLjQuNGwtLjEuNmgtLjdjMC0uMyAwLS42LjItLjkuMS0uMy4zLS41LjYtLjZhMiAyIDAgMCAxIDEtLjNsMSAuMmMuMi4yLjQuMy41LjYuMi4yLjIuNS4yLjh2LjVsLS4zLjVhNy44IDcuOCAwIDAgMS0uOCAxbC0xLjQgMS42aDIuOFptNy40LTV2NS42aC0uOGwtMi45LTQuNHY0LjRoLS43di01LjdoLjdsMyA0LjR2LTQuNGguN1ptMSAzLjUuMi0uOWMwLS4zLjItLjUuNC0uNy4xLS4yLjQtLjMuNi0uNGwuOC0uMmMuMyAwIC41IDAgLjguMi4yIDAgLjQuMi42LjRsLjQuNy4xLjktLjEuOS0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zbC0uMS0uOVptLjggMHYuNmwuMy41YzAgLjIuMi4zLjQuNGgxbC40LS40LjItLjV2LTEuMmwtLjItLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0xIC41bC0uMS41LS4xLjZabTUuMyAxLjUgMS4yLTMuNmguN2wtMS41IDQuMkg5NmwuMS0uNlptLTEtMy42IDEuMiAzLjYuMS42SDk2bC0xLjUtNC4yaC43WiIvPjwvZz48ZyBjbGlwLXBhdGg9InVybCgjZSkiPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLW9wYWNpdHk9Ii41IiBzdHJva2Utd2lkdGg9Ii4zIiBkPSJNMTE0LjMgMTQxLjR2LS44Ii8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTEwNC42IDE0My42djUuN2gtLjd2LTQuOGwtMS41LjV2LS42bDItLjhoLjJabTMuMiAyLjVoLjZsLjYtLjEuNC0uNHYtMWMtLjEtLjItLjItLjMtLjQtLjNsLS41LS4yYy0uMiAwLS40IDAtLjYuMmEuOS45IDAgMCAwLS40LjhoLS44YTEuNSAxLjUgMCAwIDEgLjktMS4zIDIgMiAwIDAgMSAuOS0uM2wuOS4yLjYuNi4yLjktLjEuNGMwIC4yLS4yLjMtLjMuNWwtLjUuMy0uOC4xaC0uN3YtLjRabTAgLjZ2LS40aC43bC45LjFjLjIgMCAuNC4yLjUuM2wuMy41djEuMmwtLjQuNi0uNi4zaC0xLjRsLS42LS4zYTEuNCAxLjQgMCAwIDEtLjUtMS4yaC43bC4xLjUuNC40aDEuMmwuMy0uNC4yLS42YzAtLjIgMC0uNC0uMi0uNWEuOS45IDAgMCAwLS40LS40bC0uNi0uMWgtLjZabTEwLjItM3Y1LjZoLS44bC0yLjktNC40djQuNGgtLjd2LTUuN2guN2wzIDQuNHYtNC40aC43Wm0xIDMuNS4yLS45YzAtLjMuMi0uNS40LS43LjEtLjIuNC0uMy42LS40bC44LS4yYy4zIDAgLjUgMCAuOC4yLjIgMCAuNC4yLjYuNGwuNC43LjEuOS0uMS45LS40LjdhMS44IDEuOCAwIDAgMS0xLjQuNiAxLjggMS44IDAgMCAxLTEuOC0xLjNsLS4xLS45Wm0uOCAwdi42bC4zLjVjMCAuMi4yLjMuNC40aDFsLjQtLjQuMi0uNXYtMS4ybC0uMi0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLTEgLjVsLS4xLjUtLjEuNlptNS4zIDEuNSAxLjItMy42aC43bC0xLjUgNC4yaC0uNWwuMS0uNlptLTEtMy42IDEuMiAzLjYuMS42aC0uNWwtMS41LTQuMmguN1oiLz48L2c+PGcgY2xpcC1wYXRoPSJ1cmwoI2YpIj48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiIHN0cm9rZS1vcGFjaXR5PSIuNSIgc3Ryb2tlLXdpZHRoPSIuMyIgZD0iTTE0My4zIDE0MS40di0uOCIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Ik0xMzMuNiAxNDMuNnY1LjdoLS43di00LjhsLTEuNS41di0uNmwyLS44aC4yWm02IDMuOHYuNmgtNHYtLjRsMi41LTRoLjZsLS43IDEuMi0xLjcgMi42aDMuM1ptLS44LTMuOHY1LjdoLS43di01LjdoLjdabTguMiAwdjUuN2gtLjhsLTIuOS00LjR2NC40aC0uN3YtNS43aC43bDMgNC40di00LjRoLjdabTEgMy42LjItLjljMC0uMy4yLS41LjQtLjcuMS0uMi40LS4zLjYtLjRsLjgtLjJjLjMgMCAuNSAwIC44LjIuMiAwIC40LjIuNi40bC40LjcuMS45LS4xLjktLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM2wtLjEtLjlabS44IDB2LjZsLjMuNWMwIC4yLjIuMy40LjRoMWwuNC0uNC4yLS41di0xLjJsLS4yLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtMSAuNWwtLjEuNS0uMS42Wm01LjMgMS41IDEuMi0zLjZoLjdsLTEuNSA0LjJoLS41bC4xLS42Wm0tMS0zLjYgMS4yIDMuNi4xLjZoLS41bC0xLjUtNC4yaC43WiIvPjwvZz48ZyBjbGlwLXBhdGg9InVybCgjZykiPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLW9wYWNpdHk9Ii41IiBzdHJva2Utd2lkdGg9Ii4zIiBkPSJNMTcxLjggMTQxLjR2LS44Ii8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTE2MiAxNDMuNnY1LjdoLS42di00LjhsLTEuNS41di0uNmwyLS44aC4yWm0zLjIgMy0uNi0uMS4zLTIuOWgzdi43aC0yLjRsLS4yIDEuNS40LS4xLjYtLjEuNy4xLjUuNC40LjZ2MS42YzAgLjItLjIuNC0uNC42LS4xLjEtLjMuMy0uNS4zYTIgMiAwIDAgMS0uOC4ybC0uNy0uMWMtLjIgMC0uMy0uMi0uNS0uM2wtLjQtLjVhMiAyIDAgMCAxLS4yLS43aC43YzAgLjIgMCAuNC4yLjZsLjQuM2guOWMuMiAwIC4zLS4xLjQtLjNsLjItLjRhMS44IDEuOCAwIDAgMCAwLTFsLS4yLS40YTEgMSAwIDAgMC0uNC0uM2gtMWwtLjQuM1ptMTAuMy0zdjUuN2gtLjhsLTIuOS00LjR2NC40aC0uN3YtNS43aC43bDMgNC40di00LjRoLjdabTEgMy42LjItLjljMC0uMy4yLS41LjQtLjcuMS0uMi40LS4zLjYtLjRsLjgtLjJjLjMgMCAuNSAwIC44LjIuMiAwIC40LjIuNi40bC40LjcuMS45LS4xLjktLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM2wtLjEtLjlabS44IDB2LjZsLjMuNWMwIC4yLjIuMy40LjRoMWwuNC0uNC4yLS41di0xLjJsLS4yLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtMSAuNWwtLjEuNS0uMS42Wm01LjMgMS41IDEuMi0zLjZoLjdsLTEuNSA0LjJoLS41bC4xLS42Wm0tMS0zLjYgMS4yIDMuNi4xLjZoLS41bC0xLjUtNC4yaC43WiIvPjwvZz48ZGVmcz48Y2xpcFBhdGggaWQ9ImEiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik00MyA1aDE0M3YxMzdINDN6Ii8+PC9jbGlwUGF0aD48Y2xpcFBhdGggaWQ9ImQiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik03MSAxNDAuNWgyOXYxMEg3MXoiLz48L2NsaXBQYXRoPjxjbGlwUGF0aCBpZD0iZSI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTEwMCAxNDAuNWgyOXYxMGgtMjl6Ii8+PC9jbGlwUGF0aD48Y2xpcFBhdGggaWQ9ImYiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0xMjkgMTQwLjVoMjl2MTBoLTI5eiIvPjwvY2xpcFBhdGg+PGNsaXBQYXRoIGlkPSJnIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTU4IDE0MC41aDI4djEwaC0yOHoiLz48L2NsaXBQYXRoPjxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjExMS4zIiB4Mj0iMTExLjMiIHkxPSIxNTYuMyIgeTI9Ii0xOS41IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIuMyIgc3RvcC1jb2xvcj0iIzZGQ0M0QyIvPjxzdG9wIG9mZnNldD0iLjMiIHN0b3AtY29sb3I9IiNGRUJBM0QiLz48c3RvcCBvZmZzZXQ9Ii40IiBzdG9wLWNvbG9yPSIjRkVCQTNEIi8+PHN0b3Agb2Zmc2V0PSIuNCIgc3RvcC1jb2xvcj0iI0ZEOEYzQyIvPjxzdG9wIG9mZnNldD0iLjYiIHN0b3AtY29sb3I9IiNGRDhGM0MiLz48c3RvcCBvZmZzZXQ9Ii42IiBzdG9wLWNvbG9yPSIjRjQ2MDQ3Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImMiIHgxPSIxMTAuMiIgeDI9IjExMC4yIiB5MT0iMTM5IiB5Mj0iLTE5LjUiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9Ii4yIiBzdG9wLWNvbG9yPSIjM0ZBNzFBIi8+PHN0b3Agb2Zmc2V0PSIuMiIgc3RvcC1jb2xvcj0iI0ZGQTYwMCIvPjxzdG9wIG9mZnNldD0iLjQiIHN0b3AtY29sb3I9IiNGRkE2MDAiLz48c3RvcCBvZmZzZXQ9Ii40IiBzdG9wLWNvbG9yPSIjRjM2OTAwIi8+PHN0b3Agb2Zmc2V0PSIuNSIgc3RvcC1jb2xvcj0iI0YzNjkwMCIvPjxzdG9wIG9mZnNldD0iLjYiIHN0b3AtY29sb3I9IiNGMDQwMjIiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48L3N2Zz4=", + "description": "Displays changes to vibration data over time visualized with color ranges.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.rangeChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.rangeChartWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: false,\n defaultDataKeysFunction: function() {\n return [{ name: 'vibration', label: 'Vibration', type: 'timeseries' }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-range-chart-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-range-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Vibration\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 3.3 - 1.7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":2592000000,\"interval\":86400000,\"fixedTimewindow\":{\"startTimeMs\":1700577227898,\"endTimeMs\":1700663627898},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"dataZoom\":true,\"rangeColors\":[{\"from\":0,\"to\":2.8,\"color\":\"#3FA71A\"},{\"from\":2.8,\"to\":4.5,\"color\":\"#FFA600\"},{\"from\":4.5,\"to\":7.1,\"color\":\"#F36900\"},{\"from\":7.1,\"to\":null,\"color\":\"#F04022\"}],\"outOfRangeColor\":\"#ccc\",\"fillArea\":true,\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm\",\"lastUpdateAgo\":false,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Vibration\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"mm/s\",\"decimals\":1,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "vibration", + "pump vibration", + "motion", + "resonance", + "dynamic balancing", + "mechanical integrity", + "pulsation", + "frequency" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/pump_vibration_range_chart_with_background.json b/application/src/main/data/json/system/widget_types/pump_vibration_range_chart_with_background.json new file mode 100644 index 0000000000..f17af28f69 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/pump_vibration_range_chart_with_background.json @@ -0,0 +1,36 @@ +{ + "fqn": "pump_vibration_range_chart_with_background", + "name": "Vibration range chart with background", + "deprecated": false, + "image": "tb-image:dmlicmF0aW9uX3JhbmdlX2NoYXJ0X3dpdGhfYmFja2dyb3VuZC5zdmc=:dmlicmF0aW9uX3JhbmdlX2NoYXJ0X3dpdGhfYmFja2dyb3VuZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48L2c+PGcgY2xpcC1wYXRoPSJ1cmwoI2MpIj48cGF0aCBmaWxsPSJ1cmwoI2QpIiBkPSJtNzIgNDkuNC0yMS41IDUzLTcuNSAxNi40VjEzN2gxNDV2LTI0bC0uNC0yNC45LTEwLjMgMzcuOC02LTIzLjYtMi42IDctMTEgMjZDMTUwLjEgMTA3IDE0NCA5Ni40IDEzNiA2OC42bC04LjMgNDEuNUgxMTdsLTItNjcuNS00LjItMjYuNi0xOS43IDY2LjMtNC43LTJMNzIgNDkuNFoiLz48cGF0aCBzdHJva2U9InVybCgjZSkiIHN0cm9rZS13aWR0aD0iLjkiIGQ9Im00MyAxMTguOSA3LjUtMTYuMyAyMS43LTUzLjEgMTQuNCAzMC44IDQuNyAyLjFMMTExLjIgMTZsMy44IDI2LjVjLjggMjYuNCAxLjcgNDEuMyAyLjUgNjcuN2gxMC4ybDguMi00Mi41IDIxLjQgNjcuMyAxMy43LTMyLjEgNiAyMy4xIDEwLjUtMzgiLz48L2c+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTEzLjIgMTguNGMuNSAwIC44IDAgMS0uMmwuNy0uNS4zLS42VjE0LjdsLS4zLS41YTEgMSAwIDAgMC0uMy0uMy45LjkgMCAwIDAtLjQgMCAxIDEgMCAwIDAtLjguM2wtLjIuNGExLjkgMS45IDAgMCAwIC4yIDEuNWwuMy4zLjQuMS41LS4xYTEuMyAxLjMgMCAwIDAgLjYtLjZsLjEtLjVoLjRsLS4xLjdhMiAyIDAgMCAxLTEgMWwtLjYuMWMtLjMgMC0uNSAwLS43LS4yLS4yIDAtLjQtLjItLjUtLjRhMiAyIDAgMCAxLS4zLS42IDIuNiAyLjYgMCAwIDEgMC0xLjVsLjMtLjYuNi0uNC44LS4yLjguMmMuMi4yLjQuMy41LjZsLjQuN3YybC0uMi45LS41LjctLjcuNS0xLjIuMmgtLjF2LS42Wm00LjctMi44VjE5aC0uN3YtNC4yaC42di44Wm0tLjIgMS4xaC0uM2wuMS0uOC4zLS42LjYtLjUuNy0uMWguNmExLjEgMS4xIDAgMCAxIC43LjhWMTloLS42di0yLjhjMC0uMiAwLS40LS4yLS41bC0uMy0uMy0uNC0uMS0uNi4xLS4zLjMtLjIuNXYuNVptMi44LS40LS41LjIuMS0uNy4zLS42LjYtLjQuNy0uMWguNmwuNC40LjMuNFYxOWgtLjZ2LTIuOGwtLjEtLjVjLS4xLS4yLS4yLS4zLS40LS4zYTEuMiAxLjIgMCAwIDAtLjkgMGwtLjMuMi0uMi4zdi40Wm00LjUtLjdWMTloLS43di00LjJoLjd2LjhabS0uMSAxLjFoLS40bC4xLS44LjQtLjYuNS0uNS44LS4xaC41YTEuMSAxLjEgMCAwIDEgLjcuOGwuMS43VjE5SDI3di0yLjhsLS4xLS41LS4zLS4zLS41LS4xLS41LjFjLS4yIDAtLjMuMi0uNC4zbC0uMi41di41Wm0yLjctLjQtLjUuMi4xLS43LjQtLjYuNS0uNC43LS4xaC42bC41LjQuMi40LjEuN1YxOWgtLjd2LTIuOGwtLjEtLjVjMC0uMi0uMi0uMy0uMy0uM2ExLjIgMS4yIDAgMCAwLTEgMGwtLjIuMi0uMi4zdi40Wm02LjQtMy0yLjQgNi4ySDMxbDIuMy02LjJoLjdabTMuMyA0LjZ2LS4zbC0uMy0uMy0uNy0uMmE1IDUgMCAwIDEtLjYtLjEgMiAyIDAgMCAxLS41LS4zIDEgMSAwIDAgMS0uNC0uOGwuMS0uNC4zLS40LjUtLjMuNy0uMWMuMyAwIC42IDAgLjkuMi4yIDAgLjQuMy41LjVsLjIuNmgtLjdsLS4xLS4zLS4zLS4zYTEgMSAwIDAgMC0uNS0uMSAxIDEgMCAwIDAtLjUgMGwtLjMuM2EuNi42IDAgMCAwIDAgLjVsLjEuMi4zLjEuNS4yLjkuMi41LjQuMS42YTEuMSAxLjEgMCAwIDEtLjQgMWwtLjUuMmgtLjdhMiAyIDAgMCAxLTEtLjFsLS41LS41Yy0uMi0uMi0uMi0uNS0uMi0uN2guN2MwIC4yIDAgLjQuMi41bC40LjJhMS41IDEuNSAwIDAgMCAxIDBsLjItLjIuMS0uM1pNMTYuMSAzOS44di40bC0yLjMgNS4zSDEzbDIuNC01aC0zLjF2LS43aDMuOFptMS44IDIuM3YzLjRoLS43di00LjJoLjZ2LjhabS0uMiAxLjFoLS4zbC4xLS44LjMtLjYuNi0uNS43LS4xaC42YTEuMSAxLjEgMCAwIDEgLjcuOHYzLjVoLS42di0yLjhjMC0uMiAwLS40LS4yLS41bC0uMy0uMy0uNC0uMS0uNi4xLS4zLjMtLjIuNXYuNVptMi44LS40LS41LjIuMS0uNy4zLS42LjYtLjQuNy0uMWguNmwuNC40LjMuNHYzLjVoLS42di0yLjhsLS4xLS41Yy0uMS0uMi0uMi0uMy0uNC0uM2ExLjIgMS4yIDAgMCAwLS45IDBsLS4zLjItLjIuM3YuNFptNC41LS43djMuNGgtLjd2LTQuMmguN3YuOFptLS4xIDEuMWgtLjRsLjEtLjguNC0uNi41LS41LjgtLjFoLjVhMS4xIDEuMSAwIDAgMSAuNy44bC4xLjd2Mi44SDI3di0yLjhsLS4xLS41LS4zLS4zLS41LS4xLS41LjFjLS4yIDAtLjMuMi0uNC4zbC0uMi41di41Wm0yLjctLjQtLjUuMi4xLS43LjQtLjYuNS0uNC43LS4xaC42bC41LjQuMi40LjEuN3YyLjhoLS43di0yLjhsLS4xLS41YzAtLjItLjItLjMtLjMtLjNhMS4yIDEuMiAwIDAgMC0xIDBsLS4yLjItLjIuM3YuNFptNi40LTNMMzEuNiA0NkgzMWwyLjMtNi4yaC43Wm0zLjMgNC42VjQ0bC0uMy0uMy0uNy0uMmE1IDUgMCAwIDEtLjYtLjEgMiAyIDAgMCAxLS41LS4zIDEgMSAwIDAgMS0uNC0uOGwuMS0uNC4zLS40LjUtLjMuNy0uMWMuMyAwIC42IDAgLjkuMi4yIDAgLjQuMy41LjVsLjIuNmgtLjdsLS4xLS4zLS4zLS4zYTEgMSAwIDAgMC0uNS0uMSAxIDEgMCAwIDAtLjUgMGwtLjMuM2EuNi42IDAgMCAwIDAgLjVsLjEuMi4zLjEuNS4yLjkuMi41LjQuMS42YTEuMSAxLjEgMCAwIDEtLjQgMWwtLjUuMmgtLjdhMiAyIDAgMCAxLTEtLjFsLS41LS41Yy0uMi0uMi0uMi0uNS0uMi0uN2guN2MwIC4yIDAgLjQuMi41bC40LjJhMS41IDEuNSAwIDAgMCAxIDBsLjItLjIuMS0uM1pNMTMuNCA2OS4zbC0uNi0uMi4zLTIuOGgzdi43aC0yLjRsLS4yIDEuNS40LS4xLjYtLjEuNy4xLjUuNC40LjZ2MS41Yy0uMS4zLS4yLjUtLjQuNmwtLjUuNGEyLjIgMi4yIDAgMCAxLTEuNCAwYy0uMyAwLS41IDAtLjYtLjJsLS40LS41YTIgMiAwIDAgMS0uMi0uN2guN2MwIC4yIDAgLjQuMi41IDAgLjIuMi4zLjMuNGgxYy4yIDAgLjMtLjIuNC0uM2wuMi0uNGExLjggMS44IDAgMCAwIDAtMWwtLjItLjRhMSAxIDAgMCAwLS40LS4zaC0xYy0uMiAwLS4zLjItLjQuM1ptNC41LS43VjcyaC0uN3YtNC4yaC42di44Wm0tLjIgMS4xaC0uM2wuMS0uOC4zLS42LjYtLjUuNy0uMWguNmExLjEgMS4xIDAgMCAxIC43LjhWNzJoLS42di0yLjhjMC0uMiAwLS40LS4yLS41bC0uMy0uMy0uNC0uMS0uNi4xLS4zLjMtLjIuNXYuNVptMi44LS40LS41LjIuMS0uNy4zLS42LjYtLjQuNy0uMWguNmwuNC40LjMuNFY3MmgtLjZ2LTIuOGwtLjEtLjVjLS4xLS4yLS4yLS4zLS40LS4zYTEuMiAxLjIgMCAwIDAtLjkgMGwtLjMuMi0uMi4zdi40Wm00LjUtLjdWNzJoLS43di00LjJoLjd2LjhabS0uMSAxLjFoLS40bC4xLS44LjQtLjYuNS0uNS44LS4xaC41YTEuMSAxLjEgMCAwIDEgLjcuOGwuMS43VjcySDI3di0yLjhsLS4xLS41LS4zLS4zLS41LS4xLS41LjFjLS4yIDAtLjMuMi0uNC4zbC0uMi41di41Wm0yLjctLjQtLjUuMi4xLS43LjQtLjYuNS0uNC43LS4xaC42bC41LjQuMi40LjEuN1Y3MmgtLjd2LTIuOGwtLjEtLjVjMC0uMi0uMi0uMy0uMy0uM2ExLjIgMS4yIDAgMCAwLTEgMGwtLjIuMi0uMi4zdi40Wm02LjQtMy0yLjQgNi4ySDMxbDIuMy02LjJoLjdabTMuMyA0LjZ2LS4zbC0uMy0uMy0uNy0uMmE1IDUgMCAwIDEtLjYtLjEgMiAyIDAgMCAxLS41LS4zIDEgMSAwIDAgMS0uNC0uOGwuMS0uNC4zLS40LjUtLjMuNy0uMWMuMyAwIC42IDAgLjkuMi4yIDAgLjQuMy41LjVsLjIuNmgtLjdsLS4xLS4zLS4zLS4zYTEgMSAwIDAgMC0uNS0uMSAxIDEgMCAwIDAtLjUgMGwtLjMuM2EuNi42IDAgMCAwIDAgLjVsLjEuMi4zLjEuNS4yLjkuMi41LjQuMS42YTEuMSAxLjEgMCAwIDEtLjQgMWwtLjUuMmgtLjdhMiAyIDAgMCAxLTEtLjFsLS41LS41Yy0uMi0uMi0uMi0uNS0uMi0uN2guN2MwIC4yIDAgLjQuMi41bC40LjJhMS41IDEuNSAwIDAgMCAxIDBsLjItLjIuMS0uM1pNMTMuNSA5NS4zaC41bC43LS4xYy4xLS4xLjMtLjIuMy0uNGwuMi0uNS0uMS0uNS0uNC0uNGgtMS4xYS45LjkgMCAwIDAtLjUuOWgtLjdjMC0uMyAwLS42LjItLjhsLjctLjZhMiAyIDAgMCAxIC45LS4ybC45LjIuNi42LjIuOC0uMS41YzAgLjItLjIuMy0uMy41bC0uNi4zLS44LjFoLS42di0uNFptMCAuNnYtLjRoMS42bC41LjQuMy41djEuMmwtLjQuNS0uNi40aC0xLjRsLS42LS4zYTEuNCAxLjQgMCAwIDEtLjUtMS4yaC43bC4xLjUuNC40aDEuMWwuNC0uNC4yLS42LS4yLS42YS45LjkgMCAwIDAtLjQtLjNMMTQgOTZoLS41Wk0xOCA5NXYzLjRoLS43di00LjJoLjZ2LjhabS0uMiAxLjFoLS4zbC4xLS44LjMtLjYuNi0uNS43LS4xaC42YTEuMSAxLjEgMCAwIDEgLjcuOHYzLjVoLS42di0yLjhjMC0uMiAwLS40LS4yLS41bC0uMy0uMy0uNC0uMS0uNi4xLS4zLjMtLjIuNXYuNVptMi44LS40LS41LjIuMS0uNy4zLS42LjYtLjQuNy0uMWguNmwuNC40LjMuNHYzLjVoLS42di0yLjhsLS4xLS41Yy0uMS0uMi0uMi0uMy0uNC0uM2ExLjIgMS4yIDAgMCAwLS45IDBsLS4zLjItLjIuM3YuNFptNC41LS43djMuNGgtLjd2LTQuMmguN3YuOFptLS4xIDEuMWgtLjRsLjEtLjguNC0uNi41LS41LjgtLjFoLjVhMS4xIDEuMSAwIDAgMSAuNy44bC4xLjd2Mi44SDI3di0yLjhsLS4xLS41LS4zLS4zLS41LS4xLS41LjFjLS4yIDAtLjMuMi0uNC4zbC0uMi41di41Wm0yLjctLjQtLjUuMi4xLS43LjQtLjYuNS0uNC43LS4xaC42bC41LjQuMi40LjEuN3YyLjhoLS43di0yLjhsLS4xLS41YzAtLjItLjItLjMtLjMtLjNhMS4yIDEuMiAwIDAgMC0xIDBsLS4yLjItLjIuM3YuNFptNi40LTNMMzEuNiA5OUgzMWwyLjMtNi4yaC43Wm0zLjMgNC42Vjk3bC0uMy0uMy0uNy0uMmE1IDUgMCAwIDEtLjYtLjEgMiAyIDAgMCAxLS41LS4zIDEgMSAwIDAgMS0uNC0uOGwuMS0uNC4zLS40LjUtLjMuNy0uMWMuMyAwIC42IDAgLjkuMi4yIDAgLjQuMy41LjVsLjIuNmgtLjdsLS4xLS4zLS4zLS4zYTEgMSAwIDAgMC0uNS0uMSAxIDEgMCAwIDAtLjUgMGwtLjMuM2EuNi42IDAgMCAwIDAgLjVsLjEuMi4zLjEuNS4yLjkuMi41LjQuMS42YTEuMSAxLjEgMCAwIDEtLjQgMWwtLjUuMmgtLjdhMiAyIDAgMCAxLTEtLjFsLS41LS41Yy0uMi0uMi0uMi0uNS0uMi0uN2guN2MwIC4yIDAgLjQuMi41bC40LjJhMS41IDEuNSAwIDAgMCAxIDBsLjItLjIuMS0uM1pNMTQuOCAxMTkuM3Y1LjdoLS43di00LjhsLTEuNC41di0uNmwyLS44aC4xWm0zIDIuM3YzLjRoLS42di00LjJoLjZ2LjhabTAgMS4xaC0uNGwuMS0uOC4zLS42LjYtLjUuNy0uMWguNmExLjEgMS4xIDAgMCAxIC43Ljh2My41aC0uNnYtMi44YzAtLjIgMC0uNC0uMi0uNWwtLjMtLjNoLTFsLS4zLjMtLjIuNXYuNVptMi43LS40LS41LjIuMS0uNy4zLS42LjYtLjQuNy0uMWguNmExLjEgMS4xIDAgMCAxIC43Ljh2My41aC0uNnYtMi44bC0uMS0uNWMtLjEtLjItLjItLjMtLjQtLjNhMS4yIDEuMiAwIDAgMC0uOSAwbC0uMy4yLS4yLjN2LjRabTQuNS0uN3YzLjRoLS43di00LjJoLjd2LjhabS0uMSAxLjFoLS40bC4xLS44LjQtLjYuNS0uNS44LS4xaC41YTEuMSAxLjEgMCAwIDEgLjcuOGwuMS43djIuOEgyN3YtMi44bC0uMS0uNS0uMy0uM2gtMWMtLjIgMC0uMy4yLS40LjNsLS4yLjV2LjVabTIuNy0uNC0uNS4yLjEtLjcuNC0uNi41LS40LjctLjFoLjZhMS4xIDEuMSAwIDAgMSAuNy44bC4xLjd2Mi44aC0uN3YtMi44bC0uMS0uNWMwLS4yLS4yLS4zLS4zLS4zYTEuMiAxLjIgMCAwIDAtMSAwbC0uMi4yLS4yLjN2LjRabTYuNC0zLTIuNCA2LjJIMzFsMi4zLTYuMmguN1ptMy4zIDQuNnYtLjNsLS4zLS4zLS43LS4yYTUgNSAwIDAgMS0uNi0uMSAyIDIgMCAwIDEtLjUtLjMgMSAxIDAgMCAxLS40LS44bC4xLS40LjMtLjQuNS0uMy43LS4xYy4zIDAgLjYgMCAuOS4yLjIgMCAuNC4zLjUuNWwuMi42aC0uN2wtLjEtLjMtLjMtLjNhMSAxIDAgMCAwLS41LS4xIDEgMSAwIDAgMC0uNSAwbC0uMy4zYS42LjYgMCAwIDAgMCAuNWwuMS4yLjMuMS41LjIuOS4yLjUuNC4xLjZhMS4xIDEuMSAwIDAgMS0uNCAxbC0uNS4yaC0uN2EyIDIgMCAwIDEtMS0uMWwtLjUtLjVjLS4yLS4yLS4yLS41LS4yLS43aC43YzAgLjIgMCAuNC4yLjVsLjQuMmExLjUgMS41IDAgMCAwIDEgMGwuMi0uMi4xLS4zWiIvPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii41IiBzdHJva2Utd2lkdGg9Ii40IiBkPSJNNDMgMTZoMTQ1TTQzIDQyLjVoMTQ1TTQzIDY5aDE0NU00MyA5NS41aDE0NU00MyAxMjJoMTQ1Ii8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTQ3LjggMTQxLjN2NS43aC0uN3YtNC44bC0xLjUuNXYtLjZsMi4xLS44aC4xWm00LjUgMHY1LjdoLS43di00LjhsLTEuNS41di0uNmwyLjEtLjhoLjFabTguNyAwdjUuN2gtLjhsLTIuOC00LjR2NC40aC0uOHYtNS43aC44bDIuOCA0LjR2LTQuNGguOFptMSAzLjZjMC0uNCAwLS42LjItLjkgMC0uMy4yLS41LjMtLjdsLjYtLjRjLjMtLjIuNS0uMi44LS4yLjMgMCAuNiAwIC44LjIuMyAwIC41LjIuNi40bC40LjcuMi44di4xYzAgLjMgMCAuNi0uMi45bC0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zdi0uOVptLjcgMCAuMS42LjIuNS40LjRoMS4xbC4zLS40LjMtLjV2LTEuMmMwLS4yLS4yLS40LS4zLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41di41Wm01LjMgMS40IDEuMi0zLjVoLjdsLTEuNSA0LjJoLS41bC4xLS43Wm0tMS0zLjUgMS4yIDMuNi4xLjZoLS41bC0xLjUtNC4yaC43Wk03Ni44IDE0MS4zdjUuN2gtLjd2LTQuOGwtMS41LjV2LS42bDIuMS0uOGguMVptNS45IDUuMXYuNmgtMy44di0uNWwxLjktMiAuNS0uNy4zLS41di0xbC0uNC0uM2ExIDEgMCAwIDAtLjUtLjJjLS4zIDAtLjUgMC0uNi4yYTEgMSAwIDAgMC0uNC40bC0uMS42aC0uOGwuMi0uOWMuMi0uMy40LS41LjctLjZhMiAyIDAgMCAxIDEtLjNsMSAuMmMuMi4yLjQuMy41LjZsLjIuOHYuNWwtLjMuNWE3LjggNy44IDAgMCAxLS44IDFsLTEuNSAxLjZoMi45Wm03LjMtNXY1LjZoLS44bC0yLjgtNC40djQuNGgtLjh2LTUuN2guOGwyLjggNC40di00LjRoLjhabTEgMy41YzAtLjQgMC0uNi4yLS45IDAtLjMuMi0uNS4zLS43bC42LS40Yy4zLS4yLjUtLjIuOC0uMi4zIDAgLjYgMCAuOC4yLjMgMCAuNS4yLjYuNGwuNC43LjIuOHYuMWMwIC4zIDAgLjYtLjIuOWwtLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM3YtLjlabS43IDAgLjEuNi4yLjUuNC40aDEuMWwuMy0uNC4zLS41di0xLjJjMC0uMi0uMi0uNC0uMy0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLS41LjFsLS40LjQtLjIuNXYuNVptNS4zIDEuNCAxLjItMy41aC43bC0xLjUgNC4yaC0uNWwuMS0uN1ptLTEtMy41IDEuMiAzLjYuMS42aC0uNWwtMS41LTQuMmguN1pNMTA1LjggMTQxLjN2NS43aC0uN3YtNC44bC0xLjUuNXYtLjZsMi4xLS44aC4xWm0zLjIgMi41aC41bC42LS4xLjQtLjQuMS0uNXYtLjVsLS40LS40SDEwOWEuOS45IDAgMCAwLS41LjloLS43YzAtLjMgMC0uNi4yLS44bC42LS42YTIgMiAwIDAgMSAxLS4ybC44LjJjLjMuMS41LjMuNi42LjIuMi4zLjUuMy44bC0uMS41LS40LjUtLjUuMy0uOC4xaC0uNnYtLjRabTAgLjZ2LS40aDEuNmwuNS40LjMuNXYxLjJsLS41LjUtLjUuNGgtMS41bC0uNS0uM2ExLjQgMS40IDAgMCAxLS42LTEuMmguOHYuNWwuNS40aDEuMWwuNC0uNC4xLS42LS4xLS42YS45LjkgMCAwIDAtLjUtLjNsLS42LS4xaC0uNVptMTAtM3Y1LjZoLS44bC0yLjgtNC40djQuNGgtLjh2LTUuN2guOGwyLjggNC40di00LjRoLjhabTEgMy41YzAtLjQgMC0uNi4yLS45IDAtLjMuMi0uNS4zLS43bC42LS40Yy4zLS4yLjUtLjIuOC0uMi4zIDAgLjYgMCAuOC4yLjMgMCAuNS4yLjYuNGwuNC43LjIuOHYuMWMwIC4zIDAgLjYtLjIuOWwtLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM3YtLjlabS43IDAgLjEuNi4yLjUuNC40aDEuMWwuMy0uNC4zLS41di0xLjJjMC0uMi0uMi0uNC0uMy0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLS41LjFsLS40LjQtLjIuNXYuNVptNS4zIDEuNCAxLjItMy41aC43bC0xLjUgNC4yaC0uNWwuMS0uN1ptLTEtMy41IDEuMiAzLjYuMS42aC0uNWwtMS41LTQuMmguN1pNMTM0LjggMTQxLjN2NS43aC0uN3YtNC44bC0xLjUuNXYtLjZsMi4xLS44aC4xWm02IDMuOHYuNmgtNC4xdi0uNGwyLjUtNGguNmwtLjYgMS4yLTEuNyAyLjZoMy4zWm0tLjgtMy44djUuN2gtLjd2LTUuN2guN1ptOCAwdjUuN2gtLjhsLTIuOC00LjR2NC40aC0uOHYtNS43aC44bDIuOCA0LjR2LTQuNGguOFptMSAzLjZjMC0uNCAwLS42LjItLjkgMC0uMy4yLS41LjMtLjdsLjYtLjRjLjMtLjIuNS0uMi44LS4yLjMgMCAuNiAwIC44LjIuMyAwIC41LjIuNi40bC40LjcuMi44di4xYzAgLjMgMCAuNi0uMi45bC0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zdi0uOVptLjcgMCAuMS42LjIuNS40LjRoMS4xbC4zLS40LjMtLjV2LTEuMmMwLS4yLS4yLS40LS4zLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41di41Wm01LjMgMS40IDEuMi0zLjVoLjdsLTEuNSA0LjJoLS41bC4xLS43Wm0tMS0zLjUgMS4yIDMuNi4xLjZoLS41bC0xLjUtNC4yaC43Wk0xNjMuOCAxNDEuM3Y1LjdoLS43di00LjhsLTEuNS41di0uNmwyLjEtLjhoLjFabTMgMy0uNS0uMi4zLTIuOGgyLjl2LjdoLTIuM2wtLjIgMS41LjQtLjEuNS0uMS44LjEuNS40LjMuNmEyLjUgMi41IDAgMCAxIDAgMS41YzAgLjMtLjIuNS0uMy42LS4xLjItLjMuMy0uNi40YTIgMiAwIDAgMS0uOC4ybC0uNi0uMS0uNi0uMy0uMy0uNWEyIDIgMCAwIDEtLjItLjdoLjdsLjEuNS40LjRoMWwuMy0uMy4yLS40YTEuOCAxLjggMCAwIDAgMC0xYzAtLjIgMC0uMy0uMi0uNGExIDEgMCAwIDAtLjMtLjNoLTEuMWwtLjMuM1ptMTAuMi0zdjUuN2gtLjhsLTIuOC00LjR2NC40aC0uOHYtNS43aC44bDIuOCA0LjR2LTQuNGguOFptMSAzLjZjMC0uNCAwLS42LjItLjkgMC0uMy4yLS41LjMtLjdsLjYtLjRjLjMtLjIuNS0uMi44LS4yLjMgMCAuNiAwIC44LjIuMyAwIC41LjIuNi40bC40LjcuMi44di4xYzAgLjMgMCAuNi0uMi45bC0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zdi0uOVptLjcgMCAuMS42LjIuNS40LjRoMS4xbC4zLS40LjMtLjV2LTEuMmMwLS4yLS4yLS40LS4zLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41di41Wm01LjMgMS40IDEuMi0zLjVoLjdsLTEuNSA0LjJoLS41bC4xLS43Wm0tMS0zLjUgMS4yIDMuNi4xLjZoLS41bC0xLjUtNC4yaC43WiIvPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iZCIgeDE9IjExMiIgeDI9IjExMiIgeTE9IjE1MC45IiB5Mj0iLTE1LjciIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9Ii4zIiBzdG9wLWNvbG9yPSIjNkZDQzRDIi8+PHN0b3Agb2Zmc2V0PSIuMyIgc3RvcC1jb2xvcj0iI0ZFQkEzRCIvPjxzdG9wIG9mZnNldD0iLjUiIHN0b3AtY29sb3I9IiNGRUJBM0QiLz48c3RvcCBvZmZzZXQ9Ii41IiBzdG9wLWNvbG9yPSIjRkQ4RjNDIi8+PHN0b3Agb2Zmc2V0PSIuNiIgc3RvcC1jb2xvcj0iI0ZEOEYzQyIvPjxzdG9wIG9mZnNldD0iLjciIHN0b3AtY29sb3I9IiNGNDYwNDciLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iZSIgeDE9IjExMSIgeDI9IjExMSIgeTE9IjEzNC43IiB5Mj0iLTE1LjgiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9Ii4zIiBzdG9wLWNvbG9yPSIjM0ZBNzFBIi8+PHN0b3Agb2Zmc2V0PSIuMyIgc3RvcC1jb2xvcj0iI0ZGQTYwMCIvPjxzdG9wIG9mZnNldD0iLjQiIHN0b3AtY29sb3I9IiNGRkE2MDAiLz48c3RvcCBvZmZzZXQ9Ii40IiBzdG9wLWNvbG9yPSIjRjM2OTAwIi8+PHN0b3Agb2Zmc2V0PSIuNiIgc3RvcC1jb2xvcj0iI0YzNjkwMCIvPjxzdG9wIG9mZnNldD0iLjYiIHN0b3AtY29sb3I9IiNGMDQwMjIiLz48L2xpbmVhckdyYWRpZW50PjxjbGlwUGF0aCBpZD0iYyI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTQzIDEyaDE0NXYxMjVINDN6Ii8+PC9jbGlwUGF0aD48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2YiIHRyYW5zZm9ybT0ibWF0cml4KC4wMDUwMzE0NSAwIDAgLjAwNjI4OTMxIDAgMCkiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjE5MiIgaGVpZ2h0PSIxNTIiIHg9IjQiIHk9IjQiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjEiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA4NV82Mzk2NCIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA4NV82Mzk2NCIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFJQ0FnSUNBUUlDQWdJREFnSURBd1lFQXdNREF3Y0ZCUVFHQ0FjSkNBZ0hDQWdKQ2cwTENRb01DZ2dJQ3c4TERBME9EZzhPQ1FzUUVSQU9FUTBPRGc3LzJ3QkRBUUlEQXdNREF3Y0VCQWNPQ1FnSkRnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzcvd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhnQUFBZ0lDQXdFQkFBQUFBQUFBQUFBQUJnY0ZDQU1FQUFFQ0NRci94QUFhQVFBREFRRUJBUUFBQUFBQUFBQUFBQUFDQXdRQkFBVUcvOW9BREFNQkFBSVFBeEFBQUFEN3habnZoNzdPWjNON3ZlNjd1WjNNN25iMTNkZG5mYnpzeDluck43M3NPWmg3T3U3dnM1M1lNeUtGTVFDV0JSNkhyTzdMdWg3cnU1M2Q3dm51NTNjN3NRNW4zbzdBdzVtYmRqaEJabzgrR3djZkJwaXIzd3gySmdsejZRcHo5MXlQVSszNTIrZXp2Yzladm51ODkzZmQ2em92RmM3dk84SnFqQko0RjhuejQvRW5Uck1IQ0xpQTkzQStBT1puVzdvWjBlQ3ZxMzYzMi9mZDY3dSs3MXZjN2NHQm9pT1RleWRzT000a3FJSFI1MlBGUTRwZ2dUSFlJRXBJNTJMUG1yWmxZUzJwWWs2STBpVVYvbzU5SDZUQm1hR0swQkdTSTgzRU1CS1ZNcW1qY0JKODlZSTh2RGk5TEJnd21LU3BYYW9rckw1K0xpVFNnZ09KZk1wVjlGNDFUZnRNcFl2Wjk5VzFDS3BGMVA1Y29Xa3pLY0c4TkNxQjdQSGRNOFdIaFd5b25HLzExY3VQV3hjY0tTZlRYODhSYVRRUmFaSTNqWmJBdjlLQWRicU1yK2xBUndDb2RNVkNTNVJmQUFPMVVnTEEzVDB6a3VMVHhjaVRGOW1qNUdxK1lUbHMxMnZMaEtzQ1l6aUpsR1RUQUZ6QXphVldkZGpaUENsaU1YV3BXQUNGelZKcDdHRnVMUTZPU2VieDRaRGc1aVkydStNQTMyQ2h1T05ONFl0dWdna3pPTGw4aWpBUnJBNkZJMjI3eVlBMWFJL0JVT0ZYbzZsenJUdGFYQUU1L3ViQW9iem5yaE0ybjJ3SnRxK2RpK292RFcxdi9oT2dTeFZSUTA4ZUFFeG5ib3RvVDlQb1draDhYUGdDdXRpVzBCYnJDb252b1p5cEhtYzQyYytnRVVqVUNkYXFXTTZ4T0hTSVVYVnZwOVJvRVZsdUdMUkpsbmdLRlNlTTdWYTVZMFcyQmg4WmVpZnRqTkoxa2crMG9McjhtOWRUZUpINXA5UmZYcWZ6emNVZ0tsd1k4dXUyWm90K2R0SHJQVnoyWnhNTVFzZXFVYW44MEpXcElFYVRmZGRHWHlHa2JVT1BXR3M5WkxtOWFZaTlUS2tBanlZTERickhJVmFta3FGWElRY2RndG1WRWQ2UWM2c0JvdlovZDlSTkZwcW1WcWtScVlhc2s1T05vdlJQNXRnNkxPaWRxNlFhL3dCQ2p3UzNCYmRUNVVDQTV6d0diRGdOMEZNNWM5UklvbmN6Vk95cXFUL1FaMU5pTVhsOW1IZWRVVGxaVlhLVHlxc1p5Y2RhOG9mRWU3VFZ1dHNJK2xkTXVxWWUvVlNuMnZqR256VWRyeEFKUHBQUDVkVWlwK3lvdy9LMktjbjRDOXo2S005Q05iUklzYTBVU1BZVTJnVkNXRXl5MXZzL05sVXZQUDhBSEl6UEtXMlByOVNWTjlSaTIzVkhxVVdYTFZZVnI3Uytpc2ZoYW02UUlpY21aOHlGTlBEMnVkZnJyUTN6ZTgxRXlXYlhHeGt6djVyWjU5eHRUNlZiL0wrVlgrTXZKWjZFVjNhNUZXZmw2QjB1cHR5aVhHaHdXMzFRMmZ4U05RZ1VSSGlaVlNwM3BhNXRRYjd5SGNmS1pHTEw1dHJkV3dtT1dqS1lsOXM5QjRVd1J1ZHIxb0EyRnE5S0tKOWRVZU03S1BWMHNVQ0lnaVFVNWRPV0lxOXllZFREYVpoajY0VStuSG0vUVJOWkFJN1dqTXQ1NTM3UlJDUHZBMjNPR1R3SytLS3pqT0hPMW1Ob2pjQWFYS3huVXNaL29qaW9nTkVXNXBHaktkVWlyck5EODFxUFYzVGNiNE5nOFd0NGZNZmVxZmJuckxtTFJsRTQrNGppOEplQ2NIMm5POXQ5dHZxL1FZTHI2L1FmTW43N2lOdFFzcUlLVEx2bS93Qk1vRFRmVmNGcGZEZlF6dE1WTXJCRWxxaTh3S1pSa2ZkcEVZdkI0OHdiRjFoV0tNSGMyaGRBdGhFMDBhOEdSNXVsenBoalIwSmdvYzJIV2lobVpNcWhsVENDcFRWU0NqY0NzWm9Gc0NkR282cElLVi8veEFBMkVBQUJCUUVBQVFRQkFnTUdCQWNBQUFBRkFRSURCQVlIRVFBSUVoTVVGaUVWRnpFUUlpTXlRVUlKSUNVekpDWlJVbUp4Z2YvYUFBZ0JBUUFCREFCRy93QmlKL3llRThmNko2VDkvd0NubDNyd3JmNitHK2svZi8zUDllRlJQOHFOOUk5UDZKSjU5STFYSi8ybnU5TDRZeGZtK0tMMGs4UCsyV1NaVStia1g0MVpYZXZqS2lmdXlDSDA2ekRINSt3aEV6MStmVVh6OFpMZG4wbGx6bFg2aEZ5WDE1Sk8vd0MwRWlaNlNNMnFMNHFqSVBUM2tvazh6RkJWVko5RlRxb3Y1VzBFd2VwdWhaU3Y1U2JvZ3hxb25yLzk5SWkvdjRSVi9zLytrYTFQS2Y2djgra1JmMytMZkhwVmFuK2VSenZURWVxTDhJUFRrOEl2MjJFYWpWalh5a1ZaOWhVWmIrS3F2MVZXVDJ4ZGZ5bHdta3I0NzBML0FDdEFOTFo5SlpMU0tyVVN0VlM5ZnBENEhTR1RqS1RDL1p1VmhYdVpNZmpJenc5dXNFR2VjbHl6U0cyU2RMNnJQNS82QmxjcEdtdzN0cC9peHZLbnlXOXJaMitaYit0ditwb3lreUw5OFBxeU1IT1Z5M1hnWXZUSU1qRk44SGtnckhzSVlpSlZhcG1MMTRYL0FPS2V2UDhBNk9SUFhsbitybk85TlR5bjkySlY5ZnNuK1pxZWtXWC9BR05heFBpbmxWbG4rWHByMi8waFR5cm9WUnF1bnNmVzJVc0dxUFZyRWRjbVpjTjMwVkt0SktFTWdCa3FMSVZKVDJVTGJIQlpCamxtdFFNbXM5ZjBKYUdSY2hoN2RxQytjMngrWjhKam9MYVRLbk9SMTZkWjVRVjR5OFptSUEwU3VyV0JXZWFRTVlxcTV5bFN0bzdQTDAvUDFYTEVBemtFMHQvcU8wc0k1bGFxNmcwbHNUQ1BjNHpwNnd4bjZ6eXNpcWxuU1h5NzE2RGg2YlVjbVdNWDBzZDJBMGxmRlc1eXF5czl3UmkweDZqTUNNWkY4cTdQMlY3cEZTUkYvcEY2UjB2K3lIMGtOMlQvQURPU05xVlVZbjkrZjErUEE3K3M2K3BIaXFyVmRQTzMwaCtwSTVZcU1TeXI5VWRsRmZkVlZSbG9aUlovZ3dOajlYTlk1cjFpbzAzMkpqTmkwdFJaOVFYakNWSXZqYnVxeko1bUdzK3lBcHpQL0kweGlZNVBQcnM4TVkrdUtyL25TeWFEYUhWVmd1ZzJwRmF5VjZXdSsxcWRKSjlWL1VjMkRTdmdIeE8wZHk3czlCZEh2a3JWQnVXRkZObGtXUGZFUjFaN1kyUy9WR0FsY21kNU9GQ1B1Ym51K2xyUGtyazJoeHhqU3hqTFVpYWpwdzIzZWk2U1NnOHNBV0VyUlUrbWFpejRjVDdIZEdLNmVoV1JmTG0rdjR0QisvMU04K3JPa2lyc1ZxSjgzc3U2RW12K0RXZlhoaUdXMGI1c1dHL0tTbXFRcjhyOGNUTE0yVnEydkY0c2x1WVJMUW5oUmFWTDRRdmdoY2krVVJFMGVvQUFZMWJMWGtJV3BOUnNUMDdxNG1Cb1NyR0lGQjVYM1Q5MWJOMlhSbERWSzVHRXJRMFE4T2NJSHBma3NWMHN5SE9BZ2ROSlRVc0xwajNSU05wWDBjZ1BqK0JNYmZQbDFZVnZYOVlTSll3bUJDcmUwWk1UendXU0tnTGo1ckdhelY3WU9VWnJkQldrUms5KzlVSVZ2NENRZlZwM3hRUXBwc3pQZWV0alV6VFFSTUZpUlZUeUl5QnZRS0lEZEQwNnZUTDVITmlLb3JtT2hSazBwbm9NRWxpZG9laDVlVUxOZTh6MUhGZzRGWXl2TVFtbDYxc1NkbFk4eGs2dzFsU0xzcFpQdXUzMEh3VkJzOVZuazF1SVh5a1AwL0xFckp0SVJzdFE1ajg0cjVxb3lPU2NqMjZkVitxRTFEV1pRN1FWOElrSjFsaHFkSlBYL3dCMnJXZXRvN3FMdEp5UHN2a2l2M1NsaTZ0ZDBObVZ1SzBLamNOY0JWaFVBZCtrMldxVS9ZSFJ3V1NFMXkzUUdjK2xJSG83TnkwTFJDMU54ZlF6dEVoYW0za0oxYjhXVXIwd1F3ZnoybnRkU2hXKzlTOEM1SEdWTFV6eEFKZGladGN2MHZTREw0akI2V3lLb2NBejlGa3RCdHlXVmhIa3M4ZCtNZG1Bd2JGTFo5c1YyRXk1WTh2ZDJwN1ljakQ1R1ZqZXhkVVg4L0U4T0tiOGpOZVptOVp6N0VXY3puS3Z5Y1lMcEpKSkhncVVpeVZnS1g1WGJKOGJYd0NLOFZWTDJoSVdabmZOL3dBNUxsOHV2bEU4eEtldjJxOVp6eTJrYUlna084M3RYblJ5Rzd1bnNaK2lMbFJKUmZOTEh3Ym9xWWlGRXZFY3huRWczSXV6RjhhdXdTNisxZktFMGMyQXJyNHZVVGlZNmtsOStuSlhIbVlaOVRmOHZpTkJoeE1vN081OXpLdGt2YlVidENZNC9aMmJMV2hXOEsyelpOTitxTkM0eVBxNS9zdUlQVzdlelEzV2Z6ekxhSUhwYUg1UlFoUmd6SXFDcStoY29Nck1vZ0ttbUh5REpSMWEwdEdxTklBWnN3K2pSS3pxNnhUeVkyZzYyTWl1alNaQ05qQ2RVM0lkUzNaTzdmVlFhUkRGZDlRVzdzV3k1bjA0blRzOUx4Wm80MWxtVzFPNXFXRmxrWUlsK2xaYmNkZWpFWDEyT0ZzV3ZQZXNuN0IzcG0zbHFQWmsrZk1wMWo1THFoaDcySHVqUWdheFdobjg0LzhBT0x1cHlXaHVvMVJLci81ZlA2S1lkVGZuYnhOSU5QY1V6UG5iR05Bb2s0cm1VNzVxK3FMbGxTT29FbzBtQmhkMVkzV2lWU2kyVFRFSzlxWm83OEY5K040N05oY3RNUU1Zd2NOWWRFTjNKOWt0OE5SWE5Pb1ZUK3dxUXhNcU5BNnNucnRCcVk4Z09pb09FWW9scXREMUQ4bXNJbzZZRzNlVmR0dDRndzNucVp3ZlRKa0xSNlBNNUhOMGlBSzMxa2VLSE9vNjNuR2pFUzBUM0t0RFNiK25TdEt3YWt1b0ZzK0lOWllHeTJkVG8wcE9tdGdhK21IbGRqZ0w5dDlhYTlZeVpMUjUwakVyclF5N1NOUU0xZHUwejhiTUMyd1FtVElDc1ZTdHBkQk1lTDNqRXcwVjlyb0JlRkhhVS9ma295V3h3cTBUYVlacHpFRm0xZTFkbEJnYXNIbE56cGk2a3B5K055c2gwdXhDREFsdStNenQwYXh0T3lVSzE0NndiWW9QZ2NLbXNES1l5TU9TdnBTTTlGdFhJN1ZFS0Z5Vmw0eWhaZTBQVUlFenQwbk9oS3BXM3hPZVRhRE1pTnZQaVhienR4bkc3TmhsKzA4bm1KM3haSFk2K3pCSWx2TURxRkhHYVRUVFoxYWhubCswU0RqeE1oUXVUZmZ5YzY0SDdhRE83bSsyRzN5NDF6a3JoVEYzSzlYTjVyUWlkNzBZZVlmWW02TG45dW1yMTZIYnlYZGRscVl1T3NYSlU4OHRrTmZPREFkVzQrNURNUkVDSVN6bTZyVUhLbHFzTUk1M3NsZTlyT2MxZExJS2xLbStINmNwcStnN0tneXhadEp5dkRVYmxESTVHUzRPZEJpUWJkbmZ2eFNFOGtKaUZCcjVXbTRUS2N1WFVzVnJ3OGZvYUg4ZUlPZ3NpeFdVbUw1VmhpTE5XTk5uc2p5T0hTZ1B2TzlTSVh3ZzdLWkxOMDVxdWFCejJKNytXcXV5MEZpNkV5c2NlUm9WeDhUWFY3RVBqbzF4V1BwUXVqSHI2d1lPdEJtd3NTVmFiZlF1cW1sOXdWcTFhbHIyZlh1TWE2TUlER3RlMnJVS1paZ24yVENoa0Rhakczc3JZRmV6blFNWldyUlRjbUQyWnVDaEltSllpbndVN0lQK0hUek8zWUxpaFlybUl5MXJ1QmJuSW5lYTlLTjZQRVpUVmc4TE5KRmx1aTRBRm5NK1hxR2J4TUdDc3kxNDgzZXZTU2FFWUpUVlNVS1FMWkgxcUU3Y09xTEhnR3lyV2Z2RTJSZmFoNVBYNWZlVUo4cThSQWFKMTgvcWFibExYWTAzdTlucnNzOU0vSG1YK2IvVHk4aEtmVXJRb1ZWNlh1eGVLb1h0Uk9aMUJ4dS8xSW14b3RKMTk0SGsrSVRwbThGeXpHZTVSSmFPUDl4SFI3VUdnRjhxb1Y5TEZSSno2UWhUcEJQcXRYSFJ4VzdiNGN0aElLNCtxTmVJc3lMbEQwcnJqYlhycGhGMFpJNThLOENlc0xaV2NJTmVySTBYbU54a2ZWR1JUV0U4KzVHbytBTlJ2UnlMRjZKTElTOW9nK3kzNFRUdm9EOUx4ZDFDTllWcjhMS1FnYVY0WVJweUxiNTRQUHA3VnVsODduS2htbnVPSUNmN2tPaFlNLzFRbWJIY3lvKzNYQmE4TmNNOVAxR1kyMmo0NXhVM3NydWhUUmxNQm83ZERVNTE5TWpjTVFkTnpuVGg3aXVSckY5WmpZZWlaV1NyWDBPVmptdm1FNmx6N3FlYVBEUkZiOWJBNXU3WUhYbXRWMGphM3NYZ1pIakFXUXcyVXgzTnJBOFBCOVZHMGgzcVp5MkZ4NlM1VG1jUE84UnpEbE1kQTBHVzBZb2l0bjBQT0g4emwxZzV4dzRGTWIwK3JnenVBZkxpc1BxTXUzblBKOGVOR1dXekJ0enNxdzBTNUs2TFl1NkhhZmg0cmoxdTdkU1N0bTloVnNaZzdFMlJFOWRiTU1iMGNSVWZPcU40ZVhpTmNkRkVJRlgxc3pEK2RlNGlTZWJ4RFE2ZVFyYnYyOXpYS2tyWHJ6U3VoN2hjOWFDTHkrbDBaL0t2Y21tSjAwNzRzZnFBOS9KZS9HM0pEQkV5dDBZeVE1aDJVQjJVWitaUG1xaklSZldSWm9kYys4K3ZNK2M2M2Y1bmZEdjRZWDVsV3dRQk9YM2NYU2xsVFIzdU02QUFCazJ2SEZaRmRGNzIvZTUxWTBHUXFNdXpHWUJlMDBabmY4UHN2d25WclpQUzdFUlBwZVJYL3dCSGJ2Y25tTDBYTThWNTcvMHUwL0o1OGhqUjJWK1N3aDgyWXpBclhXblZhTlpFeVRLQmZxeFhZNmwvNTlMM0Y3U0NmMjU1SEI1cUNJZkJaTTNCWlJvc0pPOFd2U29ydVY5bFBLSHV0VFdVMWVrYXVibm4rWGwyczNGdTE3Tk1vWFNaVjljcjZiVnZpQWxtV2Y4QThMMzZ5V1QyNml0WlFSZjQ5N1VOa01zeWxSTlZVZU45ekhPbkgrVlc3TlpmTm5pWFFvM3Z1NDNWenVxRGVWbXBzeDFVempEN3Z3Si9lSnlOK2x4TW1sQ1YyUWs4bnBmNXdlMjljMWVzL2g3eUVtSjY5eGNsYnZEbGViNW5MYnBFdjVHNkE3TVBQZTJuczFES01QY202YlVqQVl3aUtQVmRPS0lRbVk2R3l4NTJrZnh4RjFDRjRvNTBXWWFQM1ZicFFlSmFyTzljOVBCdEZWNjF6dU5JZHIwMEdSMG1Oem51RzQ5VmZNZDRSWWxPZGE2WjBXNU45OWpuSFFwdGJsZHZwb0hmV3pqT25uUGFmcUdsZTl5MWk4enduQ3NkVFo1U2JOVTM2LzNyNVlQZFo4NkhTb1hodmRCcnhxcDliQ2VQRzlIOWtJVWM3eEpIb24zR1Z5QXlmeWsrVHFXZEt6cS9JLzYzUGI3ckpMTnFjSFBJdndEVW9ObGhMNFVyNGRYNHdZTWNlNzNOblRLUGhucTNCbW81NHhKdmhhcWR4NXJmd1BZeU5xQkYvQ0NIRjZWeTVobUt3NnJ2cWZhUmRoaTUzYW9sS1R1R0hKOFk3dEYwak1xMUFCNG9WWVRxZGY1bWpINkhSandQYmVLUWJ6RnoyWXJ2S05lTjdPS1prZG5MVkJkZ3hIYjl4eGdpem52U0tOa3Z6dlJINzVQbnRRM2lEN1dXc0RIc04zem1jdVlBZkRXamNCbHJRTVhTc1dwYU1lUjBvekhiL1U1UEVxb2VEaFRGWjdOTm5aZy9hMzdXL0JIMnc2Mmw4L0V2QU16ZkhZTFVDTDhENjluUWVEUHQ2NG5vWWYzaHdBdW5CbnNQMEtOaUplOTFtVFdwN2txMm9Hb2o2bnRMNkpHZjVQTGdTa3ZncjE3Q3loUGNqcmFQMWVJdGxrRGVaMkFyWjV4NjFEV295OVBOZTUraHVjdFUvQnh1QnF0dENLMHJIcEZYN1Z5bVhTVUtIUzgvUmIrcU9JOUFtRmphK2J0elNTVjkzblJmVE9jempteE5zbFN6ZEp5THIxYlVobnlObjZIanMvMTdqRE5maUpGb1B3blU1SDBTUEhlclF4TVQ0UzhYNnl1Y09ySkx6c29OMVhIT3F4ZEV4YkZ1RDVzZm11dVpTcDBua1U3cXVqNUYxZks5T29DOFAxc1V6OVZFY3BzT042cWUxem1DWTFsK2NkMHJrdWQyaUlHdGFXTEJDTzVibWhxcVc0bnJXSTdHUjVIbG1NNkJvUXRMVm51UDRkd0hJN2JGckw5Ny9ibk4rbC9jMXZ1WmtWV3RGZEQwSDE1TFk2TmtCYkpvTW1wSHVma1pmcHpvQmxnR1owZU1NS2c2cG81eUp0WnN2cFVXTXhuYXhySWJJZG93TGxnSmRPcGl0NUFKNldNcitLeGZua0JMUFN4dGpTVnRmbUNYNGIzT3I4YVJzNDVWa2FFZURJUnVpa0hWWHdwTEpNeEpWM3ZNR0NDc2hRUjVnRlJiRXNOamtsbFpOVks2OFVBNjdocmVnQ2ZYRWJGM0QzSCttMlhWV3hNRWRxNXdENmRrb3RmakpuaU5QbURNWFFNZmU1L3E0SXFtaDVUdHJQSjlqYTVwMUdrOHZ6ay83ZmRWa05OL012aGw5Q3dUTW5jajBwNzROQ1BaaytoYzQwL1NoT3BHaGRxSGpMWnJjODVMN3RMTnZKSFljdVNMM2RmeDNqRUdpNnQxT3RhTlh0OXhzRHhYVkp6NDVmME9PMldnczhwNjBDMGo0bno1YmNjL2pNYi9BRFBVY1RhUjkrM0pIcytaenVwV0pLTnJQYlI1YzNleXV5ck1HYXdFb2paMG1aclZ6SkFlM3ZFazIrWGlOQzdzRXUyR3draDkyU3BmcE9TWG05SUJlMkVvMjA5c05DZkgyODlxaUlxT1JiOVBYWWVLN1dXZGtTc2ZDTGt2WFpOTEI0WWRFT2x0MEliTURtSlpmUlk4WEpHNkpIak54enVhbktrOU55UkxyMzdEbW15YnRjd3l4VGpKM3NsMnptMXNrRVdNWG9zOFR0WWphdEI2TzlGRXpxUEdiK3BKUWJERTFaeDU3RUdzNzBlT3ZnK3gwSk14cmVZWE5yN2RqYmd6Nk0rdndGemtmTSswNUJtdEYydndyVHIrNzVIQThScFlMbXd3L09pT0FGckhyZzVXNGc1THlkUFA5SXhkN2h4SXRCditjZEcwdnRxejFMamVTeS9NeHRxb04xK0x2NGZVdGovTncrM084QTZRL25tKy9KczRJOHQ4YlNic01iOVphcFNNNFBzSW1LYXBjU3JvQWdRclpIelVKcElMZHc2TzFNZXFVdUVQVzh0c0pkU1hkcS9wM2dwNG80T3FpQ05kam8xYXNpRHJPZ0RQY3lkeTZMNXJMVVdLM0Q2dTBuQzlER1VHdmpiTlJpWldqVW1Ja1ZCVlpKM1F1bWhtaWxqSjBHSUthc3MvM2l0L2pLN3hyMGJhamF1dzU5cGViZFVmb0FGbWFyVU9QelhTOFZGSkxXaG1OYzAzNUhIM2grVHRBaVpJVnJ2YWpsK2labitORXJ5aXJJVWpxZUdlQldsdHhiVEc1S3ZiMW1lbjBYTE5YQm5IeGJZR096WTBLVEkxM25OcHhtUytVcmFyTjlCbnhCc3oyMlhLZC9BNElvY0pWSnNIVjVia3RHVTU0bjZxSzJTVHFIU2VZVnRYejhreU8rTTMrUjZoVHM4dzZ3TmFBMWc2VHFIdDJPdnJKSEx0K1lFY2ZqT3NUTTNQTVQ2NUxjaGV2YXZEazRBbllRVXd4YzEwSVFiejlWQ1NWZE1HdURNdmY1cllncjU2SFRnNGdZNnFUa1FmTmFHd2hWTlVMa0Z5aVNZK1c1VnV6aVpqWTZLTkdXNUlyRlZ5V3hmN3BicGg3bHhXUk5mVXJKVmh6c1ppb1RkSUhrSWo2MFVzc1pXT1dMVE1BUjFKcjRtWjVNRnVxZVIwUXhsS1ZMRitsV3cxYm1XenRvRXlNOHRzTm50THZJS3haYjdNMlZDWkRTQmQ0eXFaM1A0UXAyMDVuZm92ejJkVVlXczdEa20xc2hhMXJNbjZHUnVHT2g1Zm5XSWdGYnk0R21NYmNyMDNjQU1wTHp6U1ppSE83bnA5WVgxY0ZrS2VzRUY5VXpHVnVlKzRRdjFJdjJjNkpab09kYXppVzRuMU9ESk5sQWtCR0Y5d2VSZXMxUjRIWFUrcDczaGVpZ3lIVUltNjdLWjdOWWZXMEttdnhVMHdqMVF5NWUvbks5Y3V5bVdIWGVRVXNmdDUyWnkxYzUrYXpaL1k1dTI2eGZHd2tHMk5SelBWMG5XbVdyb1VsYkdrL3RjN09hT09UMVcydlhjaExQOEFlR0duUjJaMkpiV0hIakJBRklDdDNjd1c0NVk3WUJXdnRiT2xXRlRqM1U1WlJlamt6R2R4ZVdMMklMME5rZnVzNk4xTkF0UXprODlpRzlZMjI5dHBoQTFHc1NwWjdvVSt0cnhHaFFnY0RKNEx1NU8vUFd6ZXlFMEJZSEhnc1puWUxPN0tWZGJwYzR5cCtzTE56TDRDa3FVQlh1UTZ0ME5hVjlvTVNNM09Id1JIcU5iUEc4WFMxR3I2Wnp6cmN2YmhtZUZSNWNKbmU4NzhCeE81UUJHd0k2eVoxbk4remRrN2xUMDVQWWl3bWY4QS84UUFUUkFBQWdFQ0F3UUhCUVFGQ1FNTkFBQUFBUUlEQkJFQUJSSVRJVEZCQmlJeVFsRmhjUlJTWW9HUkl6TnlnZ2NWa3FHeEVDUkRSRk5qb3NIaElMTFJGaVV3TkRWRlpHVnpkSU9UdFAvYUFBZ0JBUUFOUHdEL0FLSDRSajR6andRWThXYTJQTTNPUENOTWVMdGJIeE5mSGdpNC91b2pqNDJDajk1eDR6VmYvQUhIeEYyL3lHUC9BRXYrTERBNDlhRmY0dmp5bmkveXYvSjVmeStKeDVESGljZUM0OFhPUGRUSGlmNVBjUnY4aGpsSkl1bGZxY2U2bDVHL2NNRGlaNmhJQjlDYjRCc0k2U09Tb1kvd0dPVlE5RUthSDlwaGI5K1AvTmMrRTBnL0pFV09QN0xJZWpNc3YwZVVxTWU5TEpUMEtmUUJqam1hM3BIUEtmbUk5T1BDU0dXWS9XU1Uvd0FNZUVXVjBwUDcxSngvZFVNQy93QUl2OW53R1BFNDhCL0o0WHdPU0RVY2NwSnVQMHh4SzdYWnBnY0Vwb3RwSWZtY0QrdjVpZG5Ddm1XYXlqNjRQSEwrakZPMVU0OGpJdW1NZnRuSEZxalBzMllnK1pqZzBnZWhjNEhIOVU1WkVqLy9BR1dadnErRjRpcHJtZjhBd2d0Z2NERFJiVnZxZFgxdGdtd1dTVllmOElOd2Z5NEo3ODJrL3dDTFFiNEJzUlNRU1MzNDdqWU1NVzdVc1FSYmp3dWJZVGV3cXFsUVFCeEpERzUrV0J3MmN3bmI1b3U4ZjdmZ3pZOFF0aGozY2VJWGZnOEFBVGh1elRxMnFlVHlDRGZqaWN6ek9FVkZRUjd5b2VxbnFjSjNaNXRwR25rQmNScjhzUjd0blN4aVFMNnNiSXVPVWpmYWtmbWF5RDh0OEx2WWJZQlY5V2F5ajluQ2J0TkxFYWtBK2J2YU5mcGdjS2pNSnR0YjVBcEVNY3FISW9taWlKOFBzZ29JK1p2amdsWDBtcjFNeDg5bUx1ZjRjcjRLY2NweU5LU0ZmRzB0U1YrbW0vTUU0QUFNRS9TQ1d1a0RIeGlwa0FkZkltNHdIM1ZFUFJSWWxScjlvUzFjb3NEZ0VxMVBUWmpFSEI4QWxMQTMxSkl0L0x5VlJmQjd6QzJQaWZGdU9ySEtOR3ZqazdEK1J1ekRId1BxY04zS0NIWE1SNXZiZGppMEt6YldkejhibTRUOTV4UjI5cG5rSlduaHZ3dmE3U09lUTNrNHY5NVBHVmkrVVNtMzdiSDB3aTNqb28zUm1GdDVOaDFJd09ad2VxS29nbVA4cDR5ZklFZWVDZXJRMGFtVlZQaHBCMEwrWmo2WVViaEl5MVZaNkFrYUVQNEV4SHVmUHVrMVVZcVJENGd1ZC9vTFlQYXBlamRHS0NpQStLb2NBc1BPeEdITytueVNrYk9NMFk4TjhyM1ZUNWovQUV4Yjd6cGJuTHp5c0I3dEhBUUJ5T2sydDRqaGdCZ3NwaWp5U2lKMzhMQU93UFBpQ09JSEhDQUExRkhsUnJ6R25nWjV5RkE4bU80M3N4eHVCaW84d0xoQnlWa280eUNCd0dzcXcrTEE0cEdjZDJPUGVXd2V6TE1tMGwrZ3dlL01CRW8rcHh6V0VsLzRZNXJUd2xiL0FEWWpBMyswWm5WYXo2NlJZZnZ3T0NVeUtxakI5NFJ2Zzk3MkpTZjkwNFBjQmtWZjJWUURBYXpMUzA1LzNuMGdmVEVpbVJLMnVyRmxHMThaTGdLTGdXRmh1eEcybDVrWnBZUjZOdVFqMEpHSGRRWUd1NG5QRVgwaTFnYldUaHpPbzhJaUhiTGRtNkpNbzVUU2plVitFV0J4RWhhZk1SVExCRllEZ200WEdCSjlybWVaa1NrRG43UFRFZ04rSnVyNGhzVW82dFZtMHdlQ2s4QXFBQkYrUXdodTFMUlMrd1paQUIzV2xBdkszd29DZnc0Ukx5WlgwWnAvWjEwK00xUWJ1ZnJjOHI0cUgwUlRDajl2eldvUHdMdklQbVQ2Z2Nnd0RaaDBxcjFpb29YK0dLMnphM2tqRDRzQ01OUzlEZWd1U21zcWdEd0JlUVNMQ0Q1S25rUmlJSFladG5sZlI1Yzh2dWdQVUNxbmEva3lESE5GYlVjRGhKVk5aZnBnZjBlVzB1OGVyQUUvdndlSWVRenlmc3BxdDh5TWYzemhEK3d1dHY0WUFOMmtrV0FmV1ZybjluQWJmSFFiYW9IeUtCUjYydU1BM0UyWUpISDh6ZTV3TzdQWHhscitRMUE0Qi83cHl4NmhmcXFmNTROdElqNlBtTU52NUZ5TFlqaU1UMDlla0VMcXUvY0w2Ujg3M0dJd0NaYUxwQlR5UlNueEl1UjhyNGlCMlZGRG05SnRwN0E3bDNnNG9YTUZaMFl6aWpqV3NKWmV4VDliN1FtNTNyd0ZqYkJEVFJaRldaZE5hbFgzV0syRWplbzNubGlnY3gxMlcxVjBsaWs0QVZFSlJGc3hzVTN0aWFpTWlVMFU0UWk2M1craTdBZzJCNEQxR0NvZU5xUU1pVkk0M0VuZVUvaHNmSEVlNEN0akVrWThkS3NCcFBtTU5KZXAyT2lQYmVTdHZDZ2VsemkxbnEwVTFUdjVGZzF3UFN3eFN5aVdLbHFVMkxCaHdhem16K2w4R1lTeXpuTG9iMUZqd1p3dlA4VjhVNkJZNlhMT2t6eFV3UHZDSmtLM3hmZkZRcnQydjV2Y1JqNnRnYnk5YklaMyttNUIreWNMd3BhVkRJUGtpZFhBNFZHYTFBaVFEeElCQStyWWZjYUhvNVNhcEQ1YStxT0hnV3diSDIvcHJuaHUrL2lJRXNUNkMvbGhlOTBNNkd4WmRTQWYrOXJTb3Q1akZ4cnBzNy9TRlU1elVIeTlteTlkQUo5Y0VmZlI1TERsb1B4Q1dyWVNFZlhDOElKdWxkWE5KNmFLV0poZjh3eHhWSTJ6SGFJbzVCNTNBT0pEczlBWEs1V0FKTjFJbm0xRW4xR0k0ZFZxM0xzdW9YbWI0WklpNmsrWjNZdVNUbU5Ka3VZMEVTQzkxRlJESUpvaVJ3YTNIR1VrUVpLbVlaZGwrWjBkTDhmdFVGVkhORnd1TmFIQ3loeTd3VmxIUzFrcW0vMkdZMDBraUJySzFrYkhSdkw0akREWDVtbFBtREtvQU04a1VrVHh5cVFRQXhZTnBBTmhjM1RTMWRYUlVvcktGKzdzNVVTUlhoREVpMGxpbmdPWXBFRHRRak1veE1YNWJBa1BxVGRiU1dCOFJoT3A3WXRDOVRUeC9qTk5yRnZWVnd4dEpCUVZHaVh5T2tsWEI4aXVPSWp6YUk2ZjJpTUQrc1pkT3IySHB2dys3UlZCcWE1L0VEcGI1bkRtNnMxa2JlZU91TXJmNWc0N0lxSlFBUGwvcDlNTjJjcW9DWGEvbUY0ZnV3dzZudEFFdFhKNktPZUJ3elhwSFcreFVvODFYdG4wQUdBTHlSOUhvVnltaEFIdjFrMTVHRnVhMndoKzNxT2llVCszeXBZa0V5NXBXblp4K1pCd0RZcG1WZlY5TWN3VStHeWpLVWlIeXVjQWI2VnFtbXlpTUFlRkpRcHRDUEpqaWJxb2FTZ3BxZVRTUEdXdGtMMjhHWkJnU0FDa2ZwMDdrSGdTeVVTSWc5QmZGa1VQSlZtclhXZXlTOVU5cmJ4eHVSaUJ6VFUyMXlmTHFoQnA3ZDlLNjdBZ2djTjNMQ01pVlMwR2JRVWxuZFM3L1pTcXlkVkFScDZ4dVJ1R0tzTkdqeVQwOUhXTEdnM2d0R2dqZXpFQU1iQStlSTVQWUthb2FseTJHdWpYUzBzMXBZRUNWQ0ZJOVM2dDRMRGV1QXBocUs0ZEc0S1NiUTV1d2twMWtlS1pRaVNNa2dCSjRBRzJLak1ubzZhZ09ZMTgyV1FHV1pWRjRKV0JpZU5YZGlVMGpTTGtqRWtjcHltdnpRNVlLa3d4blFnaXJLWUgyb2FlcnBrQUlKUUtXeERLMDBkQlM5STNyWjJKZnJDcnkrcWgrd21YbUlnRTVMaW11a0J6UnEvSkpaRXYvUnp1R3A1UWZ3Z0U0NVQ5SStqZnQ5QS9uSG1WQXhNZnFSam5QbEZhblMzSTdmRkUvd0J2Q3VFdVpNNS9SZm5MUVZjSkhGcGN0bklQcUJpSzRxTWt6MmsvVTJleGVLMllCWE9PTXVUZEkwYUNGd09KaWNuWnVQTld4TUxSOS9NcTlmQkZIV3NmeXJnZzdiUGMya0VsZFZrY2RGK0o4bEJ0Zy9lZE5lbUZ5MDNpYWFBOVp6NFczZWVFSDJuU2ZwVzFvejUwOVBlMkVHdGMrNmF1ZlpsWDNxZWdXd0k4QzRBUEpzUXNGajZUZnBJekgvay8wV3B6L3dDSG9JdEwxSGtNSXUraTZLNUUzUjNJaytCTkFFOVIrWnJIRnJOSGtGSURLOXY3U3BrSkE5UytKcHRudHMwbmx6YXNwNW1zektRYkxLNFVEbVVXK0lLZDVGQzVQSFR4R3d0WWhRTklON2V0aGlscHBxdTFVN016bU9ObUFLZ1dUMXdFUXZMUkYwdVdhN0VnN3pjS2NTUFV5TEhWNWVyRWJTb0VLa0czVkdtTEZxZGRFOEFsaURQTEpMYTNQZEdQa0RpcTlvbGtSS2NpbkpkcVdQZGJlRnN4c1BQQzVmVXpMRkJJNUVaV2dtWWJOMjMyKzBZM08vaU1PNWN5VHNKUXBVNXJvMXBlMHFCa0JDWEFheFU0Yk5ST1hoNlBFMFFFTlJBMzg0aU1uV2xaaEVqZ0haN0VvM3ZBNVhudFhOVjBIVENnaEZmUmg1anJqcDZ4RmFPc1pyL0NWSVVnV1l0aVdlU09PdmlyazZSNUhOWWthYXJMcGwyMUxJQUxNcUxiM1NSaFdPM3ozOUUrZDZOSjhhaktwaVY5VkF4RTk1ODI2Smh1ai9TQ2xJL3Q2VTJqa2J4dU4rS2NkdDZVNUgwc3k4K0pDYVJVVzhWdWNVMjQ1Wm5SV2c2UjBvOTJLcUFHMFllN0tOK0tOZzFWMEw2YlV5VStZVWhCN1NpVUZKRkh2b1JpdDNwTE14R1hVUDRiL2VXOFIxVDVqZ291d2t0K3BzalVjeUQxQ0Y4VDFmQUhDcmVhcHFQczhteWdEbVNkeEMrZjdKeFJ2WjVwbHRsdVZ1TzVERWVxQ1BGcnNQQ080Sm9wZjUzbkdiaTJVWkEzdlN1MzJjYkRqb0dxYzgyWEZMTHNLbnBEVmhsNlBaUzROalRaZkF0ZzRVN3J4aFFlYk5pWWhITlBSUXh3VUYrRUtBY0dQRW5lY1c2dEpTZFcvcnp3Mlh5VklxSmQvd0JsSk0ya1c1djFTV1BtQnl3dE5FR1hRQnhsQTFqMC93QThEby9PdzFuZkpaNHIvUWRiNVlLeFdlSS9CSU1XbFVvL2lsZEpxUDhBaUdJRFNNYmk0QVNkNG0rbTBHS1pKMVpMZFRWc1lweC8rZHdQUEZYUTdCd2h1TkRpV2xZK2xwWVdQcmpvdG43elR4YlE2MkVGUXRZcUVIa1kzckFGUE9OeGo5R2ZTcjJuTHBLT21DZTNOVlNMc0pqRVJhNGFucG5ROWtySVVPT2xQUTJtNmUwZlJWNVRVcEZtZFpEdEt2OEFWODhoQmRZMzJ3Sys2Q3ZiaXhuZVp6VWxmbXp3dFNaVlZWWU9wS0RORVpuZ05ReVdlT29jSVpvenFFakVFaWpnYWRxMmtjMDFaRWlqVTBzY3FYRXlEdE1qQnhwM2tjOFNJSG9lbDNSMk1KbUt4bmc4a1VaS3pqeGFFbHZHUEdqYVUzU1RvNE5qbTlBQi9TQlZ0cjA4eWhCSE5CamZIUzlKcVNacWJwRGtMOGxuZExNd0hpd3ZpRWc1Ym4yWEVROUpjckRkaEdkUHZvMjdKWUhFQjA5SXVrN29kTWFjNDA0RXNSWUJCdlBrTjJJVGV2clpXRFBMTHpMdVB2Smo1WFdQc3FDMi9GSlViTE1NNHBJOU0yWlNBMjJGTmJpYm14Y1h0ZXdKWWxzVDA0anl6bzlTU01vb2xjNlZrbFpPdTg3azJXeHZkanBzYnZqSTFrVHBWbmxHZ3BxYW9DZmVaZlJhTEJvd2JpYVpibVY3cnEwQzdwSXROVEdtK3lyYTliMkJaMSs2UStDOHNSQWlvbk11cHBheHhkM1B5Rmg0RERjK0pHS25vOU1WS3QvU3JXU2lWRCtHNkRFdVZiWkRmanNwRWNqOW0rTTd5dXJ5K0Uzc0E4bE8reityaFI4OGV5cEpZK01iQmlQMmRXSk0zSTJuaERXSmRUNkNXSS9OOGEzcHB0L1k5b1c4Wko4RnFJMUdLcUVUd1JFY0toQzBnai9OL09JOFYycUduckhQM01FZzBNMzVVTlBONWJCOGRNN0dOV3NJaG1jTGtHSnVXbVJubWkvRFdJMkt1T0hJT245QWx3OVZRTUF0TlVrY3BWU0lKZnV6MGFlL2pvOU8vU3pvRm1kRi9YSVZ0Tm11WFIrOHNzSlRNWUkvRVRnZHM0NmY1TWxQbXRES2dhbXFZMyswaXNlQ3ZETDE0MjdTWElHT2lVbHFYVkw5dXNBSk1iS2VKVmU2ZVZ5T0dKN3paLzBGbE9qTDgyZnZ5MGc0VTFRZU9rZFJqaFpHWE1ja3pRR0dReXB1a2dtNXh6THlrNGpkcUxJUXk1WVNPa1BSYk1FQ1Ixd0Y3eDFFUTZyQTJOcEYvd0FpQmwxUWFmcE4wSHpRazB5U2syZDBIRk41RFhYYzNNY0d4WDFIODhxWStySlR4anJUVE1SMzdYMzhpV1BGQmcwcCt5UTJaS0piaDVEOGN4QkE4RlBuakphSUhKc29nQUVVRjVEQkc1QTk1d3lKNUpLK01zRG1FeWRtV3BkU3JUSDhLa3FnN29PN0hTU29aeFQwcUJFaG9JRHZBVWNtYXd4QWRNbFJHZE1nUEEyOERnczgxWE5LNVpta2tYY1NUOU1hVGJIUnZwNVg1YlYrTWNkYlR4Vk1IeUxRVGdZTEdrckRxNFJTcVluUHlEMytXT2ltYXFaQjRTUXlYSHkxSmdUSm1XV0o3OUZWTHRVSHlWMmpQbWh4VFFta2xtK0JpSGdtOUZjUnQ2RTR6bU9TaHJES2JHa21KQ2wvSXh6Qlg5QzV4TlV5TWo4b0twR0JsQTh0UVNkZkZYWVlwbmFWQmE2d1RvU1hqUGlsMlBySEtjZEhBaHBKYXQ3UzNoT3loa2tQaU4xSk1mS0NYQVI4cDZjWkJJUkhNbFN4MHM5ajJOdTZLNnZ3U3FqOEpSZkxKa3pMOUd2U3FGTk1qaU9SbmdLQThKWVpOWTJSOFpvRDNjVjFlaE1LRW1Ib2xtYzUzYkx4eTJxYThsTkp3Vzd3R3p4cmZMWWdtVTV4TElEU1p6VEhzUVR0dzNqY3NoM01EcGF4QU9LWjJGZmtWUnVxS0dvWGU2Z0hleW5pRDRZclpvcURwclJRQUUzYTRwcTlSd0p2ZEMzQW5xdDJ6akpJdmFaWUtZa1I1dFNCZGJ4ZkVyUmpXaDRnRHhpc1o4dWlPWlpkQVhqT1kwa3YyWUpFZldFc0VyQlRiZUF6THdCeEhHdVdVTlFUZlNacEFyT0Q1MzFmTTRMTERTZUVjZHlzQytnVVFIQXpDWjZjSHVRVWRPa0ZPbnlFNVByak5jcW56V29QdldBMC92Y1l5ajlIK1d0VGc4Q1pZbW5jL05ueEZWbGtYeUl2ajlUb1lXSGRiWjNCR0lKSGljZkVwSVA4TWRMTWdOYmtDbm5tK1dGcXluUWVjc1Fxb1I1eURFeVhRTmpwQlF0U1ZSUGNySWxDNnZ6cm9mMUxZNk5Wand1SDNiYkxKNXIzODlqTzVQNEtqd1hFdExzYWhlSWtwMzNLZnlsckh5SXhWektUSWV6dENDSTVUOE1palMzeEErOWpvMURHbWJjNVpvSWpwZ3JRTzg4Uk95bDhVTitlSlloQlhvUnFzRUc2ZFBmTVkzMjc4SlB1WXFaZ2E4d0Rhd3g2MXNKckQ3eUdTTTZYSGZqSVBhVENVMnd6ekk1bjJrT2QwdWdGNmVTM2JjUmk2dU44a1lSMU90TVVjdnRVMGNRMm1ZWlRWS29MU3FGM3lPQW8ycUw5L0dvbFMwaUVZeXVqYUdrelBaQ3BwczRwWmhjaDA0VlZOT0JkNHU5MjR5a3E0UXJIQ3RSTjdYVlpGdFRaTm5PMWhWVWtuQ09ScmF4OW0raVpic0kwYko4OVdacndxRFpJWjM3VFU1UFVXWTllQjdMSjFiMm9KM2d6L0pKNmZTS3BHKzhPeTV4VERlUXZZa0d0RFlyYUtWSnN0cDZ3Z1ZWS2RZWm9DVHVrUW5rZVpQaWNaQjBsekZLV09yVzZVY2RUTzg4Q2tIdUZXckkvSm9SajljTHZIR3cwQmY4QUUyQm5kTWorU0ZLY0EvNFRpdW56T2lHc1crMDBRa0Q2eFNmczRiSkpjbHJmN3VZeEN3UC9BTWxQSXVNdW9qa0dibm5hRjJFUlA1Q01ablRqYTZlQlpmOEFUR1d3NklBL0dXSHVrZW5BNGtybW1qL0MvV3hsTmRGWDBFNmo3dWFKdzZIMHVCY2N3VGpwZ2d6ektZRjdORzBqa1ZkRjZ3VkFsanQ3bXpQUEZlcUZaRHdoblg3dC9JWEpVK1Rud3hsTjRzenBIWGRVcFlwSkhJUEJsTEkyTmtaTXBscXUxTER2Vm9aUGpRM2pjZUl2ekdLZUJ6UkpJYk5WUTkrQmo3NjJGajRoV3hSUHJrWm9TUlVRQzZzWFR2V0YwbGo1anpDWUJSaERUelhueWVxN2FSaGh4UWs2b1pPQlU2VGlGV2hwSjZrYVlvVlp1eDVVenR2QjQwOGg5dzdzeG5NV1gxMHQxYkxuMWE5aEtWM3FGWTZnUnZRbldsMUpCcjNWcStqaTNRNXFwNis0SnVTcEE2NENtMG5iaU43cmlGelZUWlBSNlVuU1luVkk5S0xnQ1JtRjNwaVFrcEdxTXh5OFloSkJRNXZvQWd6QU4xSFJoSUJvTGRtUkpCWXVMU3FqZGZFckdXcDZOVkFlVVdVV2RvTDNramtVZHFQZTF1RzFRRUNrajBWZVdHUDdlanZ4MlREN3RoMmxIM0wyc05CdW9vaWo5RnMxTlVxRE5hZVlOckEzbHdGMHhTQXNMbzJ0Y1p6U2pMTTZxTXVxVWxvcStkR1dSMmtYZ0pOVVliMXVlWnhVS2RteDc3S1NFYjBZQ0UvbnhtOEJrb0w3cnZIZVNPdzhURTdEMVMyTXluV3Vwb2hZQTVqQUcyOEE4NVVlUWp4MWpIU0tYOVpkSGF3OEtLc0oxdEg1WGNCeDVseGpPMDJUTzNZcGF3QzBjb1B1TnVGOFpOVXRCTkRPTE9qRGRiekI0ZytHS0tRT2pMMlhITkc4UVJpdFJhUE9LYnYwTld2Qlc4bUhBOERobDhNVlZXYTNveFV5YmxvOHl0cE1aUEtPcFZSRzNJT3NUY2ppTWxHaW1XeklRYkZTRHdJTndSaFVDVnFFWEUwWEFTK28zSzN5UGppcG0yOGN5QW4yS2ZnSkJidWtBSzQ1aXg0cU1VRHFhdUtNWFlGZUUwZkp0M0VEY3luME9JRUUyYTBkUHZaVGF5MWtBN3lrQ3pMekZ3YkVicGxLMVZMSmRxVjRuYTVERHZVem01dUJlRjdrYnRTNG9XR2xaaUJQVHlrZmNUSGdRdzNMSnZTUlNMK0pvWXRoSlIxeFpVS3BjQlNlMXMxUFljZGVBKzhuQ2R4VGhzeGhFa3VWYXpkRWxBNHhNZDZzcDBudHhzR3VNVlNlMFZGSEhPSkZuajRuVWVEK1UxcjhwUmZyNFFvWHFab21qYWFVQ3k3ZFFibHJibG5RN1FEbkt1TmphbXpwMFY2cWpWT0RNeTdwbFhrd0ZpT1ViYmdzU3ZIMG15cXEzQnIzMmNyUkVTYk4rSUxEVXJXN1F1TURNbzFremVtb1phb0ZvaVRHd1dJQmxrTVF0SXJLRmt0cUZpV3ZtZGZRNWpSVjFYTUJBZ2xScEVGTWhBWXFCS3lGbjNqVHA3dUo5TkhXeUx1S1JIZERJZmxlSStESkhpbXRVeFNVMzliZ0pMblQ4Y2JYa1Vjd1pWeE9xTTVwVDlwU1ZTZGFPYUxmMmxOMlgzMExKeEZ4Q0RVMUFwd1JEV0tEL3dCbzBuaWpFWGxqSFdqYTV0YlVGV0wvQUpzek9PeEZZbkVBSGd6VzMyNThSaktvUkN0ZEE0MDVsQ25DS2J3ZGVDaytoeEUyaVdKMXN5a2NjWnJUbW1ycU9YZEhVQTlrRWNtQjNxdzNnNGduWkkyazdlbSs2L25iSEc0RmlNVXlyK3ZJZ3R0djNSV0tQUGNKQnlicmNHTm8rQk5qZmtRUnpCRzRqRXpiT1NGOTVwM1BkdnpCNHFmVWNSaVAvcU5VZUNqZjlpNTkwOGlleWQzQTdxT2N0bU5IREdXZWpZbnJ1SSsvRTNmaStZMzhhZVAyaXN5Mm5JZDZabS9yVkwvYXdQM2wvZ3d4SkVSUTFSak1sTzhKWWdxQnhscHliaG83YTRtTndBYnEwRVlxYWV0cFhNb1VMd0prVysyaDVDVWRkT0RBamhSeEdHbHJWalFQb2ZqWU4xSkltNG1Na3h0eFVvMktpVVBUeVVieVBvK09uWTd3d0hHSi90RkhBeUxpUk5jZGJScXFOQXg5NEFXc1R4M2FMOFFoeHRnSXN6b2xZeTByamdUdnVqZ2NHREMvSm00RE1DV3JxUXI5bk5yR2x5MEpBTmozd2hCQjM2QWVLWmVaZGVkVFR6UlJtQ1MwVElWQ2hsS3l5TXVrbG5IUEhRN05UbHNGYlg1VFMwOVExRTBWMFZCVTZ0SUV4a3VTTlRhOENGa0RURzRrQlh0WEhFRVdKdDhMakZWVkU1Vm1KdVhvWHZjRUVmSWtEOGE0bmpFazlMU204VlloNjJxUFR6UGEwam4xazMzWEZCT0owS1ZIczlaUlRnMkVpT0JlS1M0dHJBS1BhenJmY0lEcWtRcDdNSkR5a01ZdjdOSmZmcWpMUXNkNE1ad3FqYVBVbGtpcmdPRzNDOFc1Q2RBd1BlQjdXSEFKa2VOUWxTUGZSMTZrZ1B2QTQzV0ttekRGRkZkWkc0MXNJNU40dXZqekdDTFgwNGpKM010MWRTQ0dSaHpWZ1NDUEE0Wndrc0ROcWVpYzhJMlBOVFk2WDU4RFlqRWlhWm9YN01pK0IvaUNONE9LbTZqYVdKaWNjWTJQanpCNWczd0YwMDlXd3VvWCt6bCtFY201Y0R1NFF6bVdlaHA3Q2FoWnQ3UzAvZEt0eGFNblE0OERac2JUYW1tb3lSRFdTZ1dNMUxJQVRCUFlkYU5oNU1wR0t1WFZSMTZVNWdhbW03TExJZCt5bkhCbElLU0N4RnhZaWU4dEpMQkFLZHFhZmlmRVJ1ZTh1OUpCaVNZVWtyTkJyTWJEZUVkQ1NBYmJ4MXZOR0dHcUwreW1JdTdrYjVJbnZZc2JFRUh0ajR4aXJqWkM2VXBFTlFSYlVEcEYxWlNkOXhjZ2pkektvSjV2WUkxa29hcGJtMGhpTzV2QmhwUERnTVZraUtjeGdvcEhwYWhabHRzeExHd096ZFczTVZKUU54TnNkSVRaTXZpbmpscTZONmN0S1RJaFFFdllPcFp1Uk9JVTJ0UHMySU1EQTNLSHZCYi9BSmtKNEVHeGpHeWlOVW9SWmozU2plUE1XUG1oNVlsWXRvN2IwcUU3eWZBZUxBYUQzZ2h4R05ibERzMmRqeWxYbmZoY25lTzg0eFR1QlRkSWFRUHNBZmUxcU5VSitXbnlBeE9BMEZVZ2paL1VXT2lUMVFoc1hMR0NPWTY2Wmp4SVYrdEdmUTQxOVNPVmcxaDRIRVJ1dW9YQjhpTUkvd0RQcUk3OWlUM2tQTkQ1N3hpM2FRQWpGWENZS3Vta3VvbFFtL0h1a0VBZzhpTVBVZXpzWld0SkRMYlZvYmtiamVHSEh5eEl0cHFlWTZvcGg0TXQvd0IvRWNpTVNJR2xTTnpMSlJIdlJ5Z2RZQUhnNUZpT2VJd1VobXBJeTFSVElTVHBBNFNSZ2trSVNDTG5TUndOV3F1OGtNakduekdQdFJ6eHNiQlR3S3VCcVUzQjVqRk9vUnE2V25HM1VjQXRRbzZrcUhocXNSeUlHS3Nxc09XM0N3RmozSW40T3BQWldUZUxnWFBFdkdzVmRTdzA0U1BhTGNGbVlLVTlSeU44UTlTcm9hV21Fa0xvRGREY0Fvb3NkNEF4c1EwcVVtOE9iWEVvdUdHa2ozdmtjUzB5Vk5mSEhlcGdwVmRpVmxMNkdXeng2RG9VM3ZmRVdXVWdpekJNcGRLTEx3WVY3T21JcG9hNWIwWWl3eFVreHkwNXFOcEZSTTYyMk1JTW9CakJQVk51emJFaEU5Vmw4MHhDRk9BWWMvSy9hSHhqRkkrekZSSEhab1pTQ2JhaGNFSGo0SHdVNGtjeDBsZEhLR25VRHlKdmNEL1JzVGd5UnpVMFJpSTk2Nkd3UG5hMStZYkU0MmNOVEl0NG41V1pUWjA5VkJIdzRsNjhsTFF5cE5SVk4rQmFBZ3hNUHlvMklCOXRYWkhWZXp2cCtPbm5KVStpeVlZWGtJb2VveDhTcWtqNkhIRkJKRTZnL1Vic1RKb25qTlVGTHI2bXhCd0lta0ZEV3pwWndvdVFzcWtnL21WY0tTcmFaVk5pRFk0a20yelVjNERSaVFDMnRTQ0dWcmJyZ2pHZFU3MUNaYThvbDBRaHJMS0pCdXN4QjZoNnc1NHBwMGtGcW5aTXdEWEs2Z2JpL0RGZk84cVpabWthd01HWWxpcXlSQjBQbGNKaUd1QzVuVG1YMml5QjdTbEZGZ0h0ZXpLUWI4OFJPNTlyZ3BBSlJGYysrTG5xOGQySTV4S2xWN0hLTEFDL1ZYc0IxWUFnMkdKa2trbHFLNTRvQ1pDakZDQkZmZVcwNzlPSTVGV3RXbWtWWXluQTZpRHJmZDRLTDRRcEJSNWNrQ3dVajZPcW9sY3QxaHU0YmxIaGlqMmNNR1MwRUdsTmtxaThiUFlkUUhWWUM1QXhGVFErd0lrRzBwNllNb0lEc1kyZGxERTJBSndhQkpLQ2hncGdvcG90bm9YZ2pOMWlDUXVvNlFiWHgvL0VBQ2NSQUFJQ0FnSUNBUVFEQVFFQUFBQUFBQUVDQUJFU0lRTXhJa0VRRXpKQ1VTQmhjU013LzlvQUNBRUNBUUVJQVA0QS93QXIvd0RDeDhiTTM4WkFkL1ZUK05mRnk0UG5jc1MvMXVXQjJlYmlFSE5mMi9VNUptNWx0S2FZaVVrdEJCOFhMbGlENXFXQkFTWmorMjVPTkJQcnMzMkZuYnNjWU1DMUNVOWpsVWRac1paOTZuakxBbWZ4djRyNXNTeEFKb1RPRW44dS9zeHY3czFHaGs3ZFlHdDVjWW1aTURMN0RDQWt6R1l3Z3l3SmN5Z0xHQUg0OEJCUitHWlZtVHQwRkE3c25yQXRBaXIyM0llbElKTzhDbzNZOUN6Rk1Vd1FDRUNlSTdQTWl6NjduN1I5ZG9BUjJRc0dDOWZWZzVUUHFFekpqTm1JYVdnV2FVQXV3TDJjaitJNDh6TVU5QkdjN0hHQnFCZDBGVnVvS0dpdTVnbzdyakUrcDZHUk1HVW8rL0dESDFjekV5SmdKRzVkeTRORzUvWnNYY0ZIc0QxTWgxRks0MEtRZER1NWszY0xBeTdtTUxvSm0zb1pIc0FDWENUQmpNb29KMlMxd1VObFRlNHU5d2JnRU81V3FCZlZIL21lcnhnWSsxSVBRSm1mcFNWL0t5UGdBeXYwWlVwcTBLNkpBQTBMSmpIZFJqaXRTOFVnTkw4ZjRRSVNSRFo3L3dBWCtsMzJsbm9zeGcwTkNDQVNyRXhCbEQxalN4QlVlSW9xRGJ6bGo5UnVoUDFCQ05tTXRRajlWY0c0dFFiM0tQc2R3ZDZRRDBndm83TUlJVGJEVXJRRVVhTVliTVdKcGhPVVJ0cUREdEFZRGFCb09vWVFwRmtvalFnaWYyZjlCQU95U3hJQ3FBSVBLQlFvdGxzaUFYMHkwS0RDRWFFSFJoRm1MSEdKdU1RNnhOOGM0ZkpDczRUWUtsUDBSc0VRQVhDZ3FnZU1rWklHSUZnRVVXQXBCY1kvZ29WU0FzNHlvTnpqOG5MRm1zQUFRanhsVEh4bFZCQ0thTUlvcTFQR2Fhalo0dVRLT01HekhZdUVsVGtDUlZ3MllqQmdZNUY1d2Y4QU5yZzhDWng3Sk1SaTJSaUh4TFFlUEVJbXlJTkV6c1NvSVlEUmpiRnl3UkhHSnVNTldFUDFGbzhMZDhUSjRIRWdpNkp2aGFOcGNsSnlHbERNTGlvcEZRRzAxeGZaT0g3RE9NRW9STytKVE9MN1FZMm1pSFZROXpxRXhoTE5YTHhNTk9KeHNGT0pJUEUxaGxYbFhOVkpkZDBIRlFQM3g4aHZqTkVpdWs1S1d3ZzVIUWhsQ3FMbkdLdFp4YVlyS0hyak5XaFh4SlVocjBRYWgzdWVwZnFYZW9JNndNVk1kYkZqaWNNTVdVSGhhR2diakRXU25Ia0ZHeVBGbExBd29XTnExMExKSEdwQWEwTmhseThsc09zQnkwVUlmeFp1SXNMQ3YrK01nbW9kRWlNSmQ3Z3M5VnFQeDFPSnZ4TGNaVTJFTmlDaHFFdHhHRWcrUzBPUlp0ZEZNVk9VUUJ5YjhtRUZNTVNwUEdhTEEvY3FsWEVBc1E1Z3d1Yjhsb3lpd2w2MzBiQS9ZRndycU54empiVk5WUzZFOVEyaHVEeTJBNmhhUDA2OG9yV2FDNHJxRVpDMVZnZE5UY2ZReGZZRE12M0x5QWltcENob0tCMHVRNklKRmc3RUJDd0RWZ1VKUytxRU5pWENEY0xwMEc0bTdBWUtLTk9haFlldGlFSGpOalR3RmtnVkgyQWpWQ21KcUtYVTNNdUo1aWZ4RDh5WEZabjBNd2V3d3FVb0FNRENYa2RidlpWL1FBVWJXc3RBYzNLYWhWUTFBcStWRWpIVUxNZXYvOFFBS1JFQUFnRURBZ1FHQXdFQUFBQUFBQUFBQUFFUUlDRXhFVUVDTUZHUmdhR3h3ZEhoWVhId1FQL2FBQWdCQWdFSlB3RC9BQm9VczR1Y29Zemhab3ZFZlpHcDZtZzEySHpISENjWFlXdjdMRnhReGlFSmN4NkNIckxMd3hjdDBXakZGeXhldHdvNGhpcVdsRm9Xby9nOGl3aHd4VU9GRE5SMDRqRU9IVGNXZzZGSHVld3FrSXlPbGxqenFmbkh0eUhIeWVSY3VXUmFseHB6UHMrejdQc3VYWmR4M2k4cU9uSll4NnJxZHp1V2ZXY0hZeHl0cWNNeWpQU0ZiZEYrR1A1RzJEYmw3eGh6aG1WajhtZWg0L0pndXAvdEk2VjdSdEdZeEYweTY5QjMyWXI3cU5vNm41T2grdVZoem1qc1lQQXZHNXY2bUhnM05xOFVZbk1YUjNIZW5QOEFYTW1WWGlyRlBDS01VUFJsbkdhY1ZZR01ZOUtNRm5GeGFxSENoMkh5TW1CanFzNVpkQ0ZHOU9SUXIwZi94QUFxRVFBQ0FnSUNBZ0lDQUFjQkFRQUFBQUFCQWdBREVSSWhNUVFUSWtFak1nVVFGQ0F6UWxFd1V2L2FBQWdCQXdFQkNBRCs4ZjNnaWNuK1dRTzlnWmt6NXpGa3l3aHVSZXo1ZmpqdUQveHlKOG9XUWRocy9ybDR6b243dDUvaUpCNXpOL2pQbCtWUDZpOHd2Y1lkejJVSDMrSVRlb2YzRCtlT09kMEhRTmpkZXNkdFo1WGkwRGsrZmRZUHd2ZGZaL2tXbENjaFFVRU5sSCt3OG10ZjBQazNOTXVlOVYrd3RZbngraVQ5ZnorVXhNQ0VxTy9ZRDFnSHZaRmplUjlMWVd4bTBOc2NVTldHT2JmZFduQ2V5NnpvMU4yNWFwWUxDZWxQL1VLeFNzRlpicjBnZG12L0FKbFJOeERhQk0ydkFqZmVzTFVLZmxXVmJyQWwxMVZVTi9rVzhJS2tUTFdGN0xGSVFVTllZUEhTc1pkNzI2cjFaanlhaW5jVXhESzN4Rnlld3F6VlQxK05QMnM4N3hxaEQvRTdXT0tWUDhTczVLcXkvd0NSdlNldzlGWEt0NXRwNlh6TGhQNnExb2JMV0VKSk9ENDlnV3NvdGoyazRoVUJDV0NaK1RlMWprSWxSc2JKSzFqSVN1dXkxb3RQMEFHRGFoRXNCMWcwcjRzVEx4cXFWSDVHUGhyMS9WTjFXMWp0MlBZWnEzM2hCQVYrZzJKN1JDNU1EdUJtWjNnT0J3dVEyWjk1blQ1aWV0djNWQUFWZ3VYWFFWdlNhOVFSNDFhNVFFYkJ3THJ0dG9ibHNQeXptQlBzdFpVdkJGcmtmRVpQWUVBQWpFd0ZSM3VzVUU4bmFBcVAyUWp1SnlNeEFNY2hSakljQmhrRkNmaXBzR3VHSW9LOFowUEFzYi9hczU1UkhQOEFzTFQxV3hYdDhrUUFIdFFNVEJqa0RpQVpuelBRYkoxajZnY2R0R0pCNEoxR0lXMVFDQmlFQWh5Y0NNTmp4WW9YdXdrSEpJSnpQcUtBSW9CSE5ZY2pLbGlSQm5zS0FPU29pcVJOUmpNMVFqZzg4UXFGcXpLRndjeXo5aEtrRS9aZ0paMlpZM3pqTnlzT04xbUJzSllwRG5GMWYyR0dmNWNqa0pvM2Fxd0cweEJtSU9URTRIRlNNeDR0SlpzRzFiVXB3OW9BWGxsSXJyQXJCOWJTeGVXbFVyd0hRbTRkeCtjR1oyclJveHpVanc5SEZ3QjVKV3Q2Z3ordWw4eDY4SGpCN0krSkJLTW9PU2NzVEFvd1lvVURKQ0NwZHJVRmpJWUJuaGJxdlhXQUhVUjBPaVJPRllUVFo0cTRKbGlrS2NGdlpneE1tdVVJTGFHVWVLL3NyS0duNUtVWk12V1Vpb3F1TVBRb1Fxbm9heXZkUXpLSXAxWE1BV29jbi80UVZvVkNDZzFxeHNQaXNMYm10c3Z0QnFDcXZIVmlrVXFKclBYdFZNYW1WbmtTeFJYZEdYUnNFcGpLeERxK1RVNThhL2FYSUtMdllwNzNsaGFzaTViQ2pKdUcyWmhLWERvd1cyeEgvTUt6NlgxYXY4TGxDbjNoR1ppN1F0aW9zQURUNHF5a2IyS0oxWVJOUTFlSmlWanNTNWNpQnNQTHZ5VmJESWRPU1NEbVdJRjVsWU55RlQ0bGdkVDQ5bFdheWFuVmhXL3JkeFo0ZG1Wc1ZHcjNyTGI0REt0anNYU3J4NlNnVTdGNmc2SndCS3YxRXhtZ3JMUUxQRHJjZUtSNjBzbDN4dHpQSHNETHJIK0xrUVdhbk1zSUlsZzVpV2tETWMrdDhqT3c0b2RUK05sOW5pMnkycGJrRjFTdDc2NWhmSVRRcmVWelJlNnY0NzVIQnlCWGUxUzVXdXU3eUtTclZwWFVtekprRmxpY2twTlZZRWp4YkZ3M2p2U1RTN1ZON0dQeGFxMHFjaTFnNEZnSmkyYmZDRTdjRVpCSmpqWGdxU2pZTEtSektuVzVQWFpTWDhWOEZzSTNzVzVOMTlsUnRyOGhOYlNYckJSbElCR0FqTzVZUDh0U0xDbENGRklLTnNIVXN1VURMWW1ZdzNITkRKNUE5VjF2aHRZbXlKWWVtOFpsTDZsd2EzS3l3ZjhBQ3hzRzRVbHVRVitNc3FJNFBqT0QrTjJwYWs1Q01IVG10Z3Z4WnQvR2JhdXdwY2QwVTdLUlBybXRrcWZjMElITFJUYll1eFZzL0ZsT2taV0IzclZ3UmtMWDdSd1d0VjhsN0gzUHRRcVlBMTZIRzJSeVNhMjJVREh6UmR1dzFZMWpWWWxOdVY5ZGdVMUhodUZ6Qll5ckdIT1FNbkpndFVKZ2lyMWphSVN6WldrVmdtc3NtUmtBbjd3VkdWREszTTVYbVYrVU1hM010RFVFTG9vYjQxRzFHMlYxWjFOaU1RUnlyQ3NtS29DYmhDbzZOZFo1VUJZN01PRHY5aHNsOEs5dFd1QTFEcU13WUExZ0Q0R3pOa2pVc3ltSE5jR0c1QUJISTFSK1lsTm1BWllyVk5vOVR2V2RsZTN3YnhtR3M1K0ZkL2xVWmxSZTQ2b2JRZXhZQXVzWkswUldsYnFwQkdSYTUwNURZWXJZU2RRQXZhYzJ4RThueTN4Q3BTd3FyMVdCOVhlb29kUWJTZjEvLzhRQUx4RUFBZ0FEQkFrRUFnTUJBQUFBQUFBQUFBRVJJVEVDRUVGUllYR0JrYUd4d2RId0VpQXk0UU1pTUVEeGd2L2FBQWdCQXdFSlB3RCtpN2tJZ05GdExjZmxYRCtWbG02MURnV282cG40bStBck5uV3o4bTZ6M1BVOXlGdnRQb2VuaStvMXVSYTRMdC9DeVlvRGlQY2ZqbG16OHV5elA2TERlbTArM2N0S3pxUTNhMmxsWE1ZaXloTDN2MktKYTlLNGxqYTVzdFI4M0NqY3lZdllybDdXTWNSWFRZdlN0QTR2aVNzcmQ5aWIxOWpkNUpDSmt2YTcyVExCSmJqOHFMYlpabnBHTWEzRHVVTkxKaUtaRWh5OG9oUjFsT0E0NmhkV0o4a05SeXF5TU54YUxNWHBGRFVoOVhjeGlKRm9iM0ZxOTNVeUdvQ2d0R08wY0ZwbVdueUlwNTFMVVliQ1Fuc1k5MC9vU1d1WTRpZ09JcjVuTm5VZklYQ0hLUHRVQmVhQ3dWTFc4VVJpRkFjUmVsY1RqZjVzVjNORGd0Zlk3OHpEVmZpT3Q4aTExNDN5RnRRL1V1SkpHKzc3dW53VzhjRm9VdnNucUljMm4xNUlkTkJoRjNZdnJkZ3Uzc2pYQVhSN2pzNzk2Nm9tczBUdTNrbG5pSS9aOEJKeDBLV2dld1VKYzJPTk9Zc0dhT3A1TXlYTm94WFE4d01EQWtvUmxtUGIzTFRsZzZyVXkxSEkza25taCtsNTRPN3pScjVGQ2RwMFdYMlNXT1dwWFU2M1pkV1pHS1BJR0Q1bVBYN0syZk80NlRQSGRWSHh0WUQvQUdzYjRGY1ZucFdUTWVPaDVQU0tObDEwUHVMMVdHZWFXT0hSWjYyS2lrdUJSY3lqNWV6QjNWUlExZG5kako2eWg4YlYyMCtMNFBQdU9GdFVlRFdUSldsVllwaTBXbHl0RTArSytpZG1wcU1YQmVialBncEl4VVRDeXZkanpLbzhSVmNUNHNxaloyS1lNb3lkbGs3T1dXclJvSEJxbHJ2bzVDL1pmSlp4NlBtT0dVYXJRVnN0cllabWZZd2JSbEF3VUhzTWZiaWJTalA5V1JSMGQxVDVJcWltWTY0NFBXTFoyeVk0MmVLN2NtT0x3YnhSTnVzS1JNREdoVzFQL3BWVzBwYW1ucHlKSzF3WlZlelpkdDdsSHd1MjkwYnlhWlFxU2VaTkUxeFhjM3JxT0ZyZVltSEZGVk5GSFhRODEwelJXdjJ1cUgrMkR6SEcwdU92U1NZNU8rcXIzNzNVZkEyTzZubExxRTBWeTh4S2NoN1Ria3l4cHEzUXNxUlZGT1J0V1o1MmVtaks3djhBSHdHN05yUFBYM1FwODd2a3VLdnB5dWNtYis5MUNoUjNPWTU1NWpYUWNJZWJoT1poN01NZktDOVM4MlBZV1BWWjVkaUtHYlVJbzVORGxRWk5jcm1NY0luWWhIRkNtU1dSYVdCYWg3Sk0zK1RRb01XNlhCeTRqZGw2aTBRYVlwNVB1V1JTeUZYQVJWNEZNU1pVczc3bE9na0pTRk0vLzlrPSIgaWQ9ImYiIHdpZHRoPSIxOTkiIGhlaWdodD0iMTU5Ii8+PC9kZWZzPjwvc3ZnPg==", + "description": "Displays changes to vibration data over time visualized with color ranges and background.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.rangeChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.rangeChartWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: false,\n defaultDataKeysFunction: function() {\n return [{ name: 'vibration', label: 'Vibration', type: 'timeseries' }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-range-chart-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-range-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Vibration\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 3.3 - 1.7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":2592000000,\"interval\":86400000,\"fixedTimewindow\":{\"startTimeMs\":1700577227898,\"endTimeMs\":1700663627898},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"dataZoom\":true,\"rangeColors\":[{\"from\":0,\"to\":2.8,\"color\":\"#3B911C\"},{\"from\":2.8,\"to\":4.5,\"color\":\"#F89E0D\"},{\"from\":4.5,\"to\":7.1,\"color\":\"#F77410\"},{\"from\":7.1,\"to\":null,\"color\":\"#F04022\"}],\"outOfRangeColor\":\"#ccc\",\"fillArea\":true,\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm\",\"lastUpdateAgo\":false,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:dmlicmF0aW9uX3JhbmdlX2NoYXJ0X2JhY2tncm91bmRfKDIpLnBuZw==:dmlicmF0aW9uX3JhbmdlX2NoYXJ0X2JhY2tncm91bmQucG5n;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Vibration\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"mm/s\",\"decimals\":1,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "vibration", + "pump vibration", + "motion", + "resonance", + "dynamic balancing", + "mechanical integrity", + "pulsation", + "frequency" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/qr_code.json b/application/src/main/data/json/system/widget_types/qr_code.json new file mode 100644 index 0000000000..4443ee2830 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/qr_code.json @@ -0,0 +1,21 @@ +{ + "fqn": "cards.qr_code", + "name": "QR Code", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the QR code. The code is calculated using a configured pattern or function based on the values of the selected entity attributes or the latest telemetry data.", + "descriptor": { + "type": "latest", + "sizeX": 4, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n", + "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", + "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.qrCodeWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n datasourcesOptional: true\n };\n}\n\nself.onDestroy = function() {\n}\n\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-qrcode-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7036904308224163,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"qrCodeTextPattern\":\"${entityName}\",\"useQrCodeTextFunction\":false,\"qrCodeTextFunction\":\"return data[0] ? data[0]['entityName'] : '';\"},\"title\":\"QR Code\"}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/quick_links.json b/application/src/main/data/json/system/widget_types/quick_links.json new file mode 100644 index 0000000000..cdbf44e631 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/quick_links.json @@ -0,0 +1,21 @@ +{ + "fqn": "home_page_widgets.quick_links", + "name": "Quick links", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Configure and display quick links to the platform's UI components.", + "descriptor": { + "type": "static", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "\n\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-quick-links-widget-settings", + "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"columns\":3},\"title\":\"Quick links\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/radar.json b/application/src/main/data/json/system/widget_types/radar.json new file mode 100644 index 0000000000..18bc07ac98 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/radar.json @@ -0,0 +1,25 @@ +{ + "fqn": "charts.radar_chart_js", + "name": "Radar", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest values of the attributes or time series data for multiple entities in a radar chart. Supports numeric values only.", + "descriptor": { + "type": "latest", + "sizeX": 7, + "sizeY": 5, + "resources": [ + { + "url": "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js" + } + ], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showTooltip = utils.defaultValue(settings.showTooltip, true);\n \n Chart.defaults.global.tooltips.enabled = settings.showTooltip;\n \n var barData = {\n labels: [],\n datasets: []\n };\n\n var backgroundColor = tinycolor(self.ctx.data[0].dataKey.color);\n backgroundColor.setAlpha(0.2);\n var borderColor = tinycolor(self.ctx.data[0].dataKey.color);\n borderColor.setAlpha(1);\n var dataset = {\n label: self.ctx.datasources[0].name,\n data: [],\n backgroundColor: backgroundColor.toRgbString(),\n borderColor: borderColor.toRgbString(),\n pointBackgroundColor: borderColor.toRgbString(),\n pointBorderColor: borderColor.darken().toRgbString(),\n borderWidth: 1\n }\n \n barData.datasets.push(dataset);\n \n for (var i = 0; i < self.ctx.data.length; i++) {\n var dataKey = self.ctx.data[i].dataKey;\n var units = dataKey.units && dataKey.units.length ? dataKey.units : self.ctx.units;\n units = units ? (' (' + units + ')') : '';\n barData.labels.push(dataKey.label + units);\n dataset.data.push(0);\n }\n \n var floatingPoint;\n if (typeof self.ctx.decimals !== 'undefined' && self.ctx.decimals !== null) {\n floatingPoint = self.ctx.widget.config.decimals;\n } else {\n floatingPoint = 2;\n }\n\n var ctx = $('#radarChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'radar',\n data: barData,\n options: {\n responsive: false,\n maintainAspectRatio: false,\n scale: {\n ticks: {\n callback: function(tick) {\n \treturn tick.toFixed(floatingPoint);\n }\n }\n }\n }\n });\n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.data.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData.data.length > 0) {\n var decimals;\n if (typeof cellData.dataKey.decimals !== 'undefined' \n && cellData.dataKey.decimals !== null ) {\n decimals = cellData.dataKey.decimals; \n } else {\n decimals = self.ctx.decimals;\n }\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = self.ctx.utils.formatValue(tvPair[1], decimals);\n self.ctx.chart.data.datasets[0].data[i] = parseFloat(value);\n }\n } \n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n if (self.ctx.height >= 70) {\n self.ctx.chart.resize();\n }\n}\n\nself.onDestroy = function() {\n self.ctx.chart.destroy();\n self.ctx.chart = null;\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-chart-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Radar\"}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/radial_gauge.json b/application/src/main/data/json/system/widget_types/radial_gauge.json new file mode 100644 index 0000000000..d3da6ef045 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/radial_gauge.json @@ -0,0 +1,21 @@ +{ + "fqn": "analogue_gauges.radial_gauge_canvas_gauges", + "name": "Radial gauge", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC+lBMVEX////m3d398OrwlGL1vJ33ybH75Njthk7xoXX51sTzr4n//v7mUQDx8fHn5+dsXFzt7e3+/f3h4eHj4+Pl5eX7+vr09PTp6enq6urv7+/9/Pz8+/vy8vL49/fd3d2lpaX5+fn29vbk5OT++fff39+9vb20nZCxsbGzloapoaHb29r++/ns7OyampqenZ18d3awgmq0tLT++PX+9vL97ubExMSqqqqVlZS3sa/79fL31cO6urqKiYnY19f74tT62snwuZvOgVehoaGwpqDGmoGpjHz98uzi2trNxsPy0cCZgnWphnP86+Hc1NPIyMi3traurq6uiHPokmPZglTnVQbMzMzaycDzxq6srKysnZW5oJPxrojtrIh+fX2DfHnp39rU1NT63s/a0M/TzMnBwcHIwL/Bs633xqzKs6esp6XntJiblpOek42WjovoqoefjYOXe2zHiGepemH86N324NXx3dPNvbW5rKaFgoGwj33fkGXnhVD37ejq1szpyrrFvrqPj4+umY6klo7toHXonnV0b29TTk4uKirs5N/gzcKvqqinp6fGsKX2waS0qaPWtKGyopnBppe5monAloCOgoCeiHzSlnSngGu3hWjYjWWufGBGQEDpaSTpYhnz6ePi2NLl0cbtzb34zrjfxLW1r63nvqmnn5/Qr568qZ7JqprCn4yXiYJ5amqzfF68flzCf1rIgFj38e326+Xm3NbWz8/Vxbzavq/RuazOtqnCraKgmpjXq5PQp5Djp4awlYbXooSrkoTOnYLzqYC7lH/YmHXNlHTGkXS6jHOzinOig3LwmGfvj1vtgUbrcC3x5t/v2s/Ozs75076+uba8tLLuw6ymm5SSh4HypHnemnW+jnS/h2jRjGXuiVLhhFHsdzjoXA/RwbjduKT1tpS9nozcpIWYhnuBeHOPe3GbeGVbVlbQ0ND51sPkx7fTu671uJfcrpX0tJHJo46nj4Pmo32Nf3fjnHXxn3FNSEjOysjFuLKjeWNnYWHsfUE8NzepajCWAAAUc0lEQVR42tSWW2xLcRzH/6GH/M/Rk3Pac3p6tV7mEh0JiqyChU6CxSWR1WwuQ6KWWh9clzBpvbiUbWqvmGVZ6dOIy7LNhjdEIsyTiMs8uYWIROLB75z2nNPt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", + "description": "Preconfigured gauge to display any value reading. Allows to configure value range, gradient colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 6, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries' }];\n }\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n", "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-analogue-radial-gauge-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-radial-gauge-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nif (value < -100) {\\n\\tvalue = -100;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"maxValue\":100,\"startAngle\":45,\"ticksAngle\":270,\"showBorder\":true,\"defaultColor\":\"#e65100\",\"needleCircleSize\":10,\"highlights\":[],\"showUnitTitle\":true,\"colorPlate\":\"#fff\",\"colorMajorTicks\":\"#444\",\"colorMinorTicks\":\"#666\",\"minorTicks\":10,\"valueInt\":3,\"valueDec\":0,\"highlightsWidth\":15,\"valueBox\":true,\"animation\":true,\"animationDuration\":500,\"animationRule\":\"cycle\",\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"numbersFont\":{\"family\":\"Roboto\",\"size\":18,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#888\"},\"unitsFont\":{\"family\":\"Roboto\",\"size\":22,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"size\":36,\"style\":\"normal\",\"weight\":\"normal\",\"shadowColor\":\"rgba(0, 0, 0, 0.49)\",\"color\":\"#444\"},\"minValue\":-100,\"colorNeedleShadowDown\":\"rgba(188,143,143,0.45)\",\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\"},\"title\":\"Radial gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"decimals\":0,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\"}" }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/radon_level_card.json b/application/src/main/data/json/system/widget_types/radon_level_card.json new file mode 100644 index 0000000000..acc646531b --- /dev/null +++ b/application/src/main/data/json/system/widget_types/radon_level_card.json @@ -0,0 +1,29 @@ +{ + "fqn": "radon_level_card", + "name": "Radon level card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest radon level telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'radon', label: 'Radon level', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Radon level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 75 - 37.5;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 300) {\\n\\tvalue = 300;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:radioactive\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#80C32C\"},{\"from\":100,\"to\":200,\"color\":\"#FFA600\"},{\"from\":200,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":32,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#80C32C\"},{\"from\":100,\"to\":200,\"color\":\"#FFA600\"},{\"from\":200,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Radon level card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"Bq/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "indoor", + "air", + "radon" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/radon_level_card_with_background.json b/application/src/main/data/json/system/widget_types/radon_level_card_with_background.json new file mode 100644 index 0000000000..3ef3e93f47 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/radon_level_card_with_background.json @@ -0,0 +1,29 @@ +{ + "fqn": "radon_level_card_with_background", + "name": "Radon level card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest radon level telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'radon', label: 'Radon level', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Radon level\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 75 - 37.5;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 300) {\\n\\tvalue = 300;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:radioactive\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#7CC322\"},{\"from\":100,\"to\":200,\"color\":\"#F89E0D\"},{\"from\":200,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":32,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#7CC322\"},{\"from\":100,\"to\":200,\"color\":\"#F89E0D\"},{\"from\":200,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Radon level card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"Bq/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "indoor", + "air", + "radon" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/radon_level_chart_card.json b/application/src/main/data/json/system/widget_types/radon_level_chart_card.json new file mode 100644 index 0000000000..c9c79b1408 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/radon_level_chart_card.json @@ -0,0 +1,32 @@ +{ + "fqn": "radon_level_chart_card", + "name": "Radon level chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a radon level data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'radon', label: 'Radon level', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'Bq/m³', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'radon', 'Bq/m³', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Radon level\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 75 - 37.5;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 300) {\\n\\tvalue = 300;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"Bq/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#80C32C\"},{\"from\":100,\"to\":200,\"color\":\"#FFA600\"},{\"from\":200,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 75 - 37.5;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 300) {\\n\\tvalue = 300;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"Bq/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 8 - 4;\\nif (value < -15) {\\n\\tvalue = -15;\\n} else if (value > 15) {\\n\\tvalue = 15;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"Bq/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Radon level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:radioactive\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "environment", + "indoor", + "air", + "radon" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/radon_level_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/radon_level_chart_card_with_background.json new file mode 100644 index 0000000000..9f77fe5bc2 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/radon_level_chart_card_with_background.json @@ -0,0 +1,32 @@ +{ + "fqn": "radon_level_chart_card_with_background", + "name": "Radon level chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a radon level data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'radon', label: 'Radon level', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'Bq/m³', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'radon', 'Bq/m³', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Radon level\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 75 - 37.5;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 300) {\\n\\tvalue = 300;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"Bq/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#7CC322\"},{\"from\":100,\"to\":200,\"color\":\"#F89E0D\"},{\"from\":200,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 75 - 37.5;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 300) {\\n\\tvalue = 300;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"Bq/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 8 - 4;\\nif (value < -15) {\\n\\tvalue = -15;\\n} else if (value > 15) {\\n\\tvalue = 15;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"Bq/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Radon level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:radioactive\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "environment", + "indoor", + "air", + "radon" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/rainfall_card.json b/application/src/main/data/json/system/widget_types/rainfall_card.json new file mode 100644 index 0000000000..9103680d8a --- /dev/null +++ b/application/src/main/data/json/system/widget_types/rainfall_card.json @@ -0,0 +1,35 @@ +{ + "fqn": "rainfall_card", + "name": "Rainfall card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest rainfall telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'rainfall', label: 'Rainfall', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Rainfall \",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 8) {\\n\\tvalue = 8;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:weather-pouring\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#7191EF\"},{\"from\":0,\"to\":2.5,\"color\":\"#4B70DD\"},{\"from\":2.5,\"to\":7.6,\"color\":\"#305AD7\"},{\"from\":7.6,\"to\":null,\"color\":\"#234CC7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#7191EF\"},{\"from\":0,\"to\":2.5,\"color\":\"#4B70DD\"},{\"from\":2.5,\"to\":7.6,\"color\":\"#305AD7\"},{\"from\":7.6,\"to\":null,\"color\":\"#234CC7\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Rainfall card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"mm\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "rain", + "precipitation", + "downpour", + "rain shower", + "drizzle", + "raindrop", + "cloudburst", + "rainwater" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/rainfall_card_with_background.json b/application/src/main/data/json/system/widget_types/rainfall_card_with_background.json new file mode 100644 index 0000000000..dcc19b4adf --- /dev/null +++ b/application/src/main/data/json/system/widget_types/rainfall_card_with_background.json @@ -0,0 +1,35 @@ +{ + "fqn": "rainfall_card_with_background", + "name": "Rainfall card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest rainfall telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'rainfall', label: 'Rainfall', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Rainfall \",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 8) {\\n\\tvalue = 8;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:weather-pouring\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#6083EC\"},{\"from\":0,\"to\":2.5,\"color\":\"#4369DD\"},{\"from\":2.5,\"to\":7.6,\"color\":\"#2B54CE\"},{\"from\":7.6,\"to\":null,\"color\":\"#224AC2\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#6083EC\"},{\"from\":0,\"to\":2.5,\"color\":\"#4369DD\"},{\"from\":2.5,\"to\":7.6,\"color\":\"#2B54CE\"},{\"from\":7.6,\"to\":null,\"color\":\"#224AC2\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMwAAADMCAMAAAAI/LzAAAADAFBMVEW1vr+9xsa2wMAVGhq/ycm0vb2vubnCy8u6w8O7xMSmsLC/yMertbWyvLwXHR2osrK3wcGtuLips7Ott7fEzMwTFxgQFRWira2wurq5wsKxurvBysm8xcUTGRnFzs4aIB8SFhaqtLQXHBsZHh2strUXHx+msbEPExShrKsOERIVHBykr6++x8kcIyIZISEfKSkbJCScpqaeqamzvLweJCQjLi4aIiIdJia8xsYeJyclMDCstre4wcIiKSgoMDDByssnMzOYoqKfq6uQmpqkrq4rMjIfJiUlLCwgKysiLS0cISFPYWGSnJxHWlo2R0eZpaVDVFQiKyrH0M+lr65MYGAqODgpNTU+T1BJXF1KXl5FVVUvNzcwPj4nLi6eqKhJW1tRZmZBT08+TU1dcHA2REUuOzsVGRicqKgkKipecnKXoKAzRUQlMjIgJyegqalPY2SIk5NTZ2hIVldAU1MzQECao6M3SUktPT0rNTVLWVkMDg+OmJlqfX1ZbGs6SEhNXl5GWFhEWFhBUVGptLSKlZUwQUBxg4M1PT0yQkItOTlWa2ubpaWNlpd4jItQXl47T06Ak5JUaWtVZmZSZWSXo6Nsf39NYWM+UVI8TEtYaGg6Skp0iYhoe3pVaWiHkJBmeXlidnVhdHMrOjpabm6FlpZ7j45OXFw1QkKSnp6Ejo5RY2MxPTyWn58/SUmDlJQ7RUU6QkJyhoVCS0snNjUxOjl+kZBtgYFZb3FCVlY3QD+CjIxGWlwuNTR7jIxYbW5VY2Rld3ZTYWGToJ9wgH9icXB4iYl3goJ6g4RwfHw7TE5EVllFUVI4TEszOjqUnp5+jo51hoWAi4tnfXt9hodbZmaUoqF/iYlcampzfX1ha2tOY2dJXmI/RkZ2f4BmdHSLm5tueXpdbW56hoZrdHRncXFVa29GT06ImJhkbm5BVFdLVFSEkJCOnp17iYlpd3dQZ2tZY2NIU1JedHZVXl1ug4NjeXiLmZhud3dfaGhNWlrJ0tJOV1dRWllYYWCSmpoFBwfAn555AACB/klEQVR42ixWsYs8NRROwptJstlhZ3cDu5mEwMKxLFyadZuBGwh7d+WBMHYW1wpXHL/mqisstLsrbWysFfwLbAQVFQvBRm0EFQtFtFTZQvyi5re/2ZlM8t773vd9uWUH7TGC0SG4duql93puJHdSehspsC6sPBlpjLFkDyQzJWlssjbLaGWU1kSTje/Jytxu3dJyG2NUpLyPi2U8TsZrLRE2Z0u5GxS+k/ZyrkPFZhMrq66xuwkZg8zZW2JzZw7X45jZcYrZcKv7oSLneXg/mkPLTTL1UzSq1inNAteoO3AMEwxnpibkn3PPnbGxjJGkH8s3PiMqtjYaeoXZcazTto/CYt6PKtqTjCNWnMZoUyR+gadzxcfTze1IJywXZ2yML+xDRAQMmw99DhRT1KONluvhvn8tjYdGdqngx38ZKXYZ7ZlZS1MiLq1opJCls9heSrHSyoudwpT9N6bIMWaJN1IaZkZujfbGy9AFQu0RH8lbPwLN5OopJ0FxZN1ZxHVcVT5SgflStwOQaMdTuc7366j6EGMBOpNTIfGNVV7SeJ0i8pZhuyHaISVQRB3xOO5HeVZJ/V9WE60xPi98KUsGlolbzMSnKyNjteel3p+eZ1G2UJKx3OcCzRYcTxM5lZX1DJVHHbm2IIgKXEQG426/CmTPaG+SlvZ+Y2lp7XKstRlHC0bMS9XIzcmeIkmws1UeEFMsZU3W+/EEYDPU33E+G+1mUaABnOVpqe0p2dEgCvi0ZMbSfqp17CAAvW5i0ChAmRyTtOzarA4cBdSpgHlnPEhlClrP0SibrUxy2FXKG+5XiklZBS9lN2EEwFTQcI4JU65+kWzcLaRoI0hoow0SfGx+fexjKQ+0nE42Xl0iD3Bk39uxGI1GVLheIbYd5XFp2X2CKqGUlFiPqijmEW0N0bh4LiODR2s1Tm1FBnZqKuiKSz8bZLy3mg6luTbrndX1tYHgnDJQEqqj+wXxIbjbAzQ1UwMDggqln+0OdC09vFrqKQM6hZcoeg+UpeMuxPPHFg1+Mlc89gRYBVr8da1fIcBBI7QtEzygaVgn7TkADwvqi8b6OIqLpjIqujg5p/HsNWMtZ9Na9pAct1BmUVh8gwcmW5Byzm3e7arAMSsR3sy/YlQMdOz9B29/vKsqs54dtPWKNA+ecQSTlivpz+5M6Kg4rUbDMVAUoInosKDYrmRiYclGgIn3Ik63IwaKn++svVGR8E9drBbdKiLGsZgZ0Rbn8DZ3YMFEJZnmz++ta/7iSCL6fKW8Z0slp11pHbALdBs4MIyU60akXIQkdYnEOd2mKhCXGPaTlr+vuVbcSJDh562Byyt2CDW/QADrVzZAcEorK3l0gLNAAaq0uEGiKuKyu0b7Yrx9/z+V0W4Xx93p9D6TxflSVC+tQl0sWRpdpiosD/6d337beRvh3ONl+IrGoz3NVS8WNZU+ZVDuWxRN6LuV7TvXEsMZm3NKg0dpD+dRQVhf3u8lZMLJy9+tHQez4GtuNG+rjup75x2TNHgqCjReHXwQxjsKUjI6QScuV95MYReviopQn+JQjC1S9HjaTGLUj3d30paejgUhoepyhIA0rN5uDLa9SXQHTXkRruQoWXwUDjnVMWh/+LIbbHt3g2VKyoeFNVeBMCRja/hWkIT2z07WeQkMHFyXc5d/8M6uC2SmZtcGQ2vD10xww8K6oJW08U4zXrlCqDeOm3LuiScFkAal4kEWL2UeJQCMEA/DI6qOuJ/GctU22AgU4M5s4u3VsXeRCrM3etiXtcLor27ujR4ejC8ijyT3Yy1yHdfrydddq14S0uz3EDbJtdQQtrFPUio3WiNJw72tAVAOdqSpuA6Kz7unesk8U2QYZ+0jeJtwH6BAQgoOlozhkGLrrSaLwSyfHJzsewtE8U7Grbi1cgSWIIu35fgG4yNw2WIsaq/20Z03qxM2RmTFkhe3ox6sRHys0q+sqG9hIXCFv1BzJFhpe0djsI+6ldUQPS215F6++FLck/PlKEooT3OmA2k8GgnzNFrwqnXi78UinJFHTOZJm2b+Jp8YgCcYCnDKQObOCA/FEfwYFzYkC2gZBeZ0Om9JUpx95aIthkIL4a/3Jdllyy/qpZcgZ3UrC7ubytvF4HUJAwsSqrxs8dgaZbQKBmVAovadw8bJthMydA5LHu7R4xfTQM2tAilc3r2BumrDQ+HG+DVoMJwDYrU8VoaMaYDVYcLUex0OmxdCXdClV05xXvTJkcXYsfSwRj+Sy0QJWnvrcGFidK0dzqdRmrEumlyOz8NL+bh48h6cyTj6po+vqcu4hTBlHbO0U8iqg7JT6LWXzKL/xqMgKYvLJ1pCKuQ9cs/4ZO9bTrwjs8F7zC3U9rl9fg3CIQPHMbO8gQECM4qvNDeX7i0G4jgBrbppTbUgU0klBeKJoFk1Lc/nVdxa13QlH4jJlO0bm2wj3W4l49IW09cC7+hhMuHqeo104GO8pGSiM3tp51srtkQyW4QQsF6W3k/XW4rKTFov/8MCizS6R9l4LK5Hl5WCuMkXe6H14XkRdrNgzPxmzfmL6XExCK71/qGaGsOYGnhgf8JH7l7vHhNsYxRjxMhwD3xThhyUvFnzYjhmfR/Om57ygMbb58WNRHVQFlxObTMAJTxeSCKLQf//Erz49qal4hWbyJezJcp+ZeQmhQJjMUcMA34sAAIFPI7sULiEoBDMYwrDaGhMt4TGcxIVMyZwXoXQNkrXdbsWrOrDZM/21TEYDvLrJ8WENtwI1yoAI8RCupTJz/RMI2Rab7vXUuCZ7FvLm9MDvR/cfz+JDz+6xmqrg+lSgRQNrz996QA48ktjD8ntuSUsPK9kvFZmCuAIImtJaAjBYQACNOVjAKmUD1Srd17muOsxzcNAZYovO6YhfsGebjgPgd2c6c1Za0RgbtJAaK9dQVW8MgIdCoqDSAeXlTDE5hRKPkvghksvplXWumrylOoH5Sedq9H8g3svyFIsVmKd8JV1/Epuhqq18v60nPYTTtnk/OmvOy83Wuacw/NXfXaE4MbLU0RKTy1JkE/kjZJAc3MlBe6RGtdgIJU1ytcBG2r95fNkw8W03kxVdWC6Uz1zc+bMWkN6fOuE4ooL4mUv9OCE7pXETh9lO3NN9aYDVS0pabHGounWQyMR+luaGO9lm9NKrh9vIc0++/Xj1AKBtxOR83o4X8T07gWxncxdkweRv4zX98dEcol00NR9b45XG8I9km8m5Qxd3ew6IkMECN4w1arD2UFx7dSwDU9/173mdYuSK3Zog3hZTRNjohxwzz/slJpP7ngrDtemN8NxY0gbtMV6FO2Bb7FlEpnCm2/OyjlQXCrtf79aXkmn55NNGc7fyZXthegXILVKOWNLsiLS833Od27Uypusbd+I/Zd5sZL03uTgS/i+oTupCSYRX319RJdAmkkZ6BJRgo6UrvqPOqFVj3oDZDc4JVwQQbXXXFR1xdeB1a5lbjd54y+tNWthHuWo54GZ6rvXf+ZUwhqI1m2khAXXb+qn2ypB8xp+ZkcnXzqTka1Ptzh5xfLdNV+dD5G1y3nM4+yllLhNmoacN1ul0tfPfyOYzBgaGniL5+5lApMwzMDpesVKg+a9tycowkDqWauCJSXVh0G5QByodlyHFoDYIjhnHFdQlmLtUGlWLKDq5/htKIJUQOPZH0/c8M/fmH6hjDHvXnj9mpAh0FK59kJUlEPI9vYDfvvm5Xt5sQQ9k/EtpJSrU9c01gqXh2Hx+KJiwS7emWQSaPFciSHJ/kIG0Nq4PJ2raU4vLaPS/jB4InwoQA+O2XM4xRhOmqqL7170DCpL2kH1mOJYw9Q9Sq2ZPhx0PdH6WHUTx/Y75oBq0V68dwUwHKQJAO3Z+048e/89wsmz7aCSgZ+8+GBJvL3O1h6P1Gzp5TDeWWuNlv1IQYOQS7H9KNC0ARvi4Rr8yT3VGfwk3UiWuLxIWaPsGaVh4IYyPZ5O3kxCAXPNvWkxaQ7IhkRm5rr0XTufYj9H91/98DdzdxvKIRZWnHGneKvWLWNqpd7RQS8WTIdfv3/9d1AyrFT9lVgoKufZOTOzT79dMeNq7XNmDCzTTfO9JGops+sXmZLMj8fa2twnaU8Wj7BQEinkpB8/eG9r8Yqrab8Oq55STpnjwikYbFWc9UYJItNX0dGqUEHMV3yDKVPEnlByz9P68uqmQkWpq4+/islqPm8H9jxwjMltp9o2OK6N0reHoTmD1lTD229uwr5hlz/88hXeK4MVGEZzJBTUh9QTpeb6+vFL8g/v3jXbqct5yKK5SK7LAOG9LucPfKTAQ+4bhbMvvzuhqylIUoPLf+T9jEgmrQjQUqCrx6MvRyVJ1D+fcNcH0/ZFXbwzptEoIH12AfpESuV+eO07aw668DLTfNY17Exrpd8/rJ47VdedWNeO3YXNOy++m/LLRfdeu31bsUatFe/dThBRY3jI7Lc5TzhtU6e08sOEVeSlledKXm6lG1CY516uW7P8cWEZ69751S5Wp9Ym+Evark/Z76tM50PDHAmASZSqL19SzBlgIe7BjzOzgYrlkyhtDKHdwb7nDTzQQ/hpUO82i6/3ZtKKNujrX1uuqxZyeku5m0fnAhumX7aCQXVu7YIKulnah69DpUTL0ZrFg9yzFuKiYX3DeS9dYoPW5Z0UZnI876mbqWFuU4WG994Iv2ZDT+ppe/Gcp28Jr9G5nFc5tz//w5G5g0ZQhHH8m4+dcWZnh53dFXZnMggHx3Fw29xtM7AL4R5lIKCdRRqLQIoQhFRnZ7BJtBALsUgpiaSykjSKbyx84LvwLSr4KFUsxO8cLm8y+/2/7///zWZz9i51Y0hYQmOkyexPEnd7NNuBak+//vWfhtE8Khy/4VXP3vnuqzXFF6w2qSdDKdVaKrbM5GAVs8J3w3VbhjoTKoRpEqSUUDl+12AOkhYAOLr03f1lsIS6ri2PQYOYkNMybfYlGS2JqWyjYsD09p09LYoxbRObi1+L96/W6oGMPVgf7Ds5RFXHh6j1eo/eJZPkASr9kpIw30+ipchUSSRj7ZMKOpda+mT6Fj4Y6Zvatj9U5smhvUV6dnqWE3MJBsh2XedQBzkMgwyAxaQPR6d8NLJSuhNQ3jkHKKDhXMAlokOE4JSTUtF8WIjQTwCDbh75unC63H+SVPiAcX7QExvaeq79pXa1QMgfGJvz7FLa4oHk+EbE7R9PPMfm82Fond1niLi/iPuJut3MJWGYtZMbnuz+5nZJS4up2A437YM9b3PN9NWmpUeE6p2TtYdKBurktpDs0jnmRzYM5dsn0koLHy0BXFDOvn/oTMZzr6S0t6vCWQ/9Fu3o+0tJP+e9ZZK86B4ppduBr05uh5YCTfUohTARx62+00kKjvztwgO6/KduFvTAzBJuZ+lD0IP9KOxRBJZFbOl2tFGtplNdIjkKhmTvgEPC6DmM1oS2YXRTJMPhQ//Mkn2kDc8n/3Qy0URlF8h2icggtm3ZHhtryWzrWpBpWHMxclY6NdiuCsHLg15WfvjUzGBTQM+dLcVFN7Jz4lww9HZ9+yi504OD1SgQTWLL9KBs29Ng2iSIy8Ed6dleZ0CgQlO3fXvyzlmL++7ooX8ayHXQ0yeJwD5ZAInZlwktaorW2VhrMm/QZZxxXCcpoXLwLf0Q5kydI0WZkUOcZiR3pMdWKRZ4cIIzJw0FfjIEFWiRGoUwE/M6a3CwMMCq47Bbzm7M+jOwqop8JdHN+zOmGPAgWadbampDBuZtgv8fXvrojOVXQJwTBH0/h0FiPH5w+WSyr+DsaqH0HpDdnzxOonmAkcvmLd3L7Zfru9nuaNTk5ai098gwNzT23YnKXHJpODdqoG1DWVaM0OPYCCgnbVB0JYojkDJiikMWsoMo3bzJ8mnHuYejt0cmP+UcyoNb23A52ZyD+/Oe7LdxqgnUosB2s9bgGIHzRINygMu7q50PSKAwhu7aXKNWhq23LVwuyDKLHfzo8FxstD6uyVsyS5J3fyYbyQewbaYXVu+lB0y1mU4yoGkRpukrkvMgM3Q11wSZHDk+qyWjwgfZ8TB5XUr4LJVqCjbP5sP1Vg7y8EmPooSGppiJdPP16zfih+0MoMh/t5h5+dzvC8zGTXSgrGospooxrrhUUVKSwFoLnJPvYGbY+32gho1DXFamLursrErl/Dgeqjaukv02+/7tqAlQ9CnfXyYPnbJICIPTHo1WVw9MojaOGbXhEqLcjyiZbHcMppecLT0yyd9+F1zfyHfWVDEZbFO7q6NTwbonbV131olyJLsMIHsEYfjm+CS54x8fkM/Q26YDsM1zFYJ11kp6SQ81XYyzQBcg+7o2YW2JputQRW0npQ1rTjVwm0gL+TnNqhbkwzhq6SHBxb6opW6gbbHkap2iVglRObLkIa3SiQLH1J5kGuc2nmSRhIBkkU10kFwNhYzq6DKtQ+3Z+RLQBdcNUk5PxkqEwWb96NTJ07RugN++m8uuafi7Byl0AhBS7q3orD10OJlSktSYq+PgKGEgJU+R5To3MiludJTpR+8kCmdH1MmV30rWxtndRCYhxLChahND+SIx6fKSsbu33/2IUpMX+7t/vO363mpfMh33EpKhdxE/XCBAWJyGpA1RP+ncbNwH1XDq5/VJkJSPHieDdW5+74SyXBlRcPFpX0l7dohFI79/oMJRhR2QIgBeQGM6TK+eRNrhz7OP7EUqucuZTCfEtieDY4wtsxRIj2RskX1MJ54UJgD2m5xmGPdMCzpJpttbgcttG3e3Zg9E2E7WdL/mkv0HXLJHSXmukBbh/qhoGIs6YmyUZgR/2TuLutUEMQigCAHqWA0gK0taFHL4bbBV+ubigvuC5KxAgSkk75bCWkD/vqjAFp3gvDn56M6BsGVRbormyYtycblGKMCiLKzcoAKllvcuOSwEV5m1qq8zD0GR9abpwAURJewlYfuuTODkn4Vmh5uWzvJ9Wkk2q+7maaIIx0wlxe0Tj1100EovXYiyjDIiY7vipbO7qNMXVL/DzeztbFDK8deXUkqXrwxan65TTVzjExi7NcHLUBb4LsNu4BzuKg7TruzyQ4HQlQuBxuPhp1NDe+9iM5RII1Y0aL7+4D5IDfk7n1+PJ7NbpwmrHvzAXWm1YjbQ6TDJ2sA2RlcfXyT/r0gnJtlNxd3Dbu2IiO8a8dSZJF4h6PajBWqb2ErmAR0amr185ItPpV4g7z+GNcSZrT+dSXfQ1305zhYze7UmsxnH/XDEcfDoM+fQlTk2iFcwWpx+8m3FR4WHzen13FyUaQcAeFh4T34riiDuJzIotlze7QaUVtk2BHJG0IGbJo/WamA1zYiCC1Y2JbMqFaVesB1w293SRaR0ML1bZ0PQhoFmJe2wNHL895vzJnRKB9hBWL66ejsrCYzqYDoBqyQNDLkw1lvHCxz1IIJ7HQ00w8BtvhwGAGvRYuczXnTlt2bg41lagx9x082wybtlZv7HmYKyFvfPPasacFKrgBz9+Qkq/b8blJkq651ERUWcbsM8mqQYpdeOitoV75SKSRujghxJvAuaAXuHHnyOdMWZ3mNTmsTZQ3otQ7RK39Kew+yCxTV5YdkWRKUhI5wGHAZnuTShmpXoJNnK2PffhgGHwxwRncMOoAOBdT9fXR9ledetYMr9QWNGTQXosfhs/jph8PjaH80vqLwnzwOb3vYK0DqUO8AEI52xkRCjZ97VJ4DzIGF8fhKI4WdL3ciEVMc2Bp0QflmyMOm5XuT/nGeR0bLpcxhCtj63rBcMVVdJqeT1JVIAqVXy7qDi1Vhlaz1FnzYOV6SMu85/Rtq2/QLzKdqbGYWmE96DH5fgc05CqnSygS4VYlKdnxoLHMuDM7Q2/eIHB0jTHpA6vH3yl0hCikA+m19WaHrVZCRKLaxknCqEMYOyJMvpm2uJinzoqPlBo2OJji4gMobvO5Dq2LKByfe5IoiApKUuZvKAkDKwUgcQaiPODcESKjuR4NKuQXD4yPsFp2jkgAI5IOB4M0ILIyCVX91ORdlllfGNqOfQLBsSRnbEApBn9wtrr/FbsDAIWYEZ27S+hNpjUBSh0xtVZ0O2POERnJJRshj1yVK2Fi8ltT06Js/fXeBGWpnEwGSMVqKJElkf/1el2FqOAm1mQ3BW3vTlv8YOMqChudCOgNZKN1jboEPbDELAylXWH9aAaA0At5QS5Jw7xXR/MzH9zV1fjjj41MNotjVjCnUDaWHg6w+fARy9c40AZDcnpMLIkCbsiwylKkCtP47DQEXKaOm1nEen3I9vb4Ezdn1xT+W/PfkLodWGamOtjhF1S7GQVikyErogeq2kDYQX7txN6z13Jg+qufZqeK4uvEMD9n9fr7eDKbxYAhrOaRRpZ/u8mopm7hteQdDVzDZp+uZFfr0GSMtTSD/Lb/Ou6SoPvBxf731hPABmT36KHKQ1yoxxhmAkOmeGmqvy3CkPY0s5P7pjE/+c02x+OXfLJtYnh0tmPqrug319qlyMLWMmE4Ym5Ha8uHKjX37l1H423xCVbmqpBm4MYINMHaixVJXHxtn8k28vKBNfvPF7iryxhsJPHzkXhdmgeLvYvg+b+XgDWdXUaV++fV7flUTkRQHrV16uzDuP/7jKZytei4t+1xgSUkh8sjt51QXrlAWBciNxtYDxFlmjCtKpWGbI/DPKaICoZpWMYrxiRZJGpVCxwfRVoMkwXSgG/KAXEE4eSCq52Mp1bsGt0Hgjr24/cgMaF2oRUODVF2NnL6ZHbpm/vSemHQK6mn//UQ8GFgsPH+VTni2q4hFAIWhuec9Tb2aTab/IBLxbP2/ESf3SL6YZewOjKYATpwglzxWWvRwmhVJKuJgTBCaiBtnB7YdfEkhjJOtL4hpI1wiFy6lSzqodwEcU6WA7GykIDN/+7vdYHFOLwWY1lqfKZaP7izArvTwa1duCwkPIKqyy5fYjf5dzpH264ZH7O8jcAlf1Zn2ejnpzvuDmJiu6j6+amajGE+CF6dORb7rU+CItX4fugR+a7ECvPhEcALrJASz+4oJ7QIu4qaphsBSffCMVmtB//6ZyXB0sDhTIsMssklBwKko+bqJCXjbHp0eve8WuWXpoh1Pe+6MSreXSwOHEOYV9P3rfXS1Gl4X96YX3XjOWlCiiifOjhs8+u9sQXTvEriDmZjfYN93dbKDA+8tL2m3pJ/wKRbf2vOBnHhrPr2ddt6lvTvsVNLezmTfXZ0Azzzw3WZfPxrOiFOnXn8+4mK02Yzvg5Oqzr9+xTj58rewgOsN31y+ikqz+8sUciUPTQ34inouVDJVqEP0gCSQX0bpwfPVwMvK2G7lxDYIbqsKdLs8DfvxBbk6PqsYotOn11FvS/HpEkBaqshqtc+z5dNyXJ5xgS8TNVgVNYNJUhmPaeS7S2ToX/Lmu+eJ7Pln4BRCmRNM13vdH7ywNcA4Iqz9+bTiG1x+dwt3Xr3I7N4CXBycHX/ZdQdx0+WAHp+BQQSavIfuewm7ETK4XnCSCi+yyTt9PZ4cHE+UoXGImdtqH+aQRXvFOyAwPjbA/gkrEdea8Azssicdg0XWIYG03E6NRht5ygJO6ovTPpiCavus6sg1kp2eGT043k09Xwpuj/n6UielsBE3V7FbmF+unv+ekBoG7uOxMV+7N3+nfh29x8XGgmowoqwtEHJQpTIErkiMJv9MiuVFSTnzmQIh6whswFxdfxpVUVg27QKFH6ej3mqXawoCnBbW5qL0w6ZfzcdtYS37PJHAkMVSAv6sR8vspvp5evl9zqp77bc4nwnNe9uumO6q59ySwySD/lWw8OvQHt9X7Hy/6/zi2lhAHajAcB/KzxoQ0iZB09DIQhsHxMjMIwojYmR4LPVTwVlAPxSKlikWkKsKICK0PtKKi1rdupQpeFBbE9+O0uq6u68G3qPhCEXxcxL8uSwkdKP+f7/8eSSlRABBq7gRkXSGwGVIGmkFYFF6cYQ09e9lSLCiNb7VVJ8HTASU0XhCxCgxrY8FBt+uuvZyRdnQhCgUNvv4m2puyNcXoFKgwPxPv6LBKt/fO1AatCvYCChHGXx5rQOoslEhpgZwixMh5BAF2rg01LkulUcWAJFTTUHx7ypE1opTlx2ozj01EERA128IUTMXTlJW8u1xlZP7RAvjy9y9B4IBRQbEzTUU9oQ52c4pUcojvu5C6iPAlaWfd9h5pXHs+68yJSnAiyMbGp1iwEIEKRMfMLhJAov7xX6hKwSKhUFKw6a0dFHKMuQF+Y1WqVR20QVJRiF1GuYhQQOoU79R7pmaIBpFOjUjNqeu+My9Cf3Su9NjNzgvFsGO9nGi5+OayB2cdcZIqVaX847XSQ7L54iPHehd9yne5ze26ESGAdz4oYkMrimawNzptX3TRXz8QUZKLFDdeV6uT/WcIBKQEmHO3OeezBoMAwdnnPGmjDE5XZy4Pz9wL9WefWQw0QQ9o+AYs5y16ONjdjnQpyyTxSrCiRGCgIEXRokPruor4gByjrlmgfUnXsoYBJZqqkNT98HC5UpWwUeqE+LTcO/SDoQN9BGGcFuk+0TVWx8VQlQlYBSMoy33CXOi9nZ9Otv+jazPTIdN10JruDcNUkdwGyWGPTFXMnYttwk4c2uRejIM4SdMAuxFAZwm5qN3dfBO59ro9k3hsENkkOPnzHRt0uAjWt/aG7916URAgSUM0msDM6NhTTRHiggNXtsIlSm8ROldwo8LSSlfWaWms82UI7r3ONHUhi70kfJx0WW9IvPITBtzkUJouGC8EqDCUk89ClY83g65UnorY+RRNBGmT0haehOwCyMdB0xC8TTzoUBe2qRChDs8sdmcggQwmezknSAS8buuYFkbw9n6DiZ2sBz2UKx22gLXsoUXax52ROAiweocE1/RwaMqRbLRJY8LJT2HOJ/Z/tU2FaKTgUJ+6AvtJrexwANhXUjqmPt2YdxnBN1LIIqG4UtOsDg0zDuifWTLeGRVX3qHv6gilP8YrNYFCQ1q5So1QRpzFxwctzABy3CYNBEqc2fcFFV6hhIlPvzxu7cHOL/leiuobhCG2KDhtpwrBI6xIWhkIYkaktTsQkbDlTKGU5YbnIMJGh33KhHJodXSAmRiUqytTvkOqDhI7D0tnjAfHBn+6Q7mJo5km2r7zD2PCKROh4LlKMu5CUdFbe5IJrupJ55QaagLihu3AfXR5SXGP9g04GgaUkJQE9V4VQHsvbgUjlKk2ig9xWPe74y3qGa9C3+odrynrOwzxLC3PzEm65iRwinMrsENDgB1M9lD/KBQFQBS7gggnFM7bcg4hk998DmHfGSd+oSVUhwogxWJhlTunwDupZddHxzNLem4x6UQfO1NW2+owAzXfEWjN09r4AciYfv3nX2R+QIJImJYZpnUYxCRHDUWtwfDexgqBYluBji9yKghHDmesyazenOkT6oi6KCALTO8FSUIX9F2g0iSDRn592ggVpcXJNgjLYJLRBChd/nOtpkj3WDQhwz4Fsrko7LFDB57YfZG1JQDM1SoHk2pewsjnGpK1v1Wm064hKtJRz1ljGst8EtrEg9GycJQP0qGpu3S87V5kwjAc2QGI6QLRwPsRt5nF6cERGh3Xe4TsXNv7oOEpzQVsvnaISV+wMW4/4qfoSLP2lO1oHmDmE2VgWuseHbZGUviGU/wAlGNKk2s/Tg9CWmitlGiMpkVYKCf1gQCnueoilwX/OrWIR8lVDM23ByVz5j0mJ7nwjJPa+4UHrkxpIpZz4Z0fgeaFCGGQCDVeLHRg2gEvkb4CfQ+1usEAbJHe60OpGk0VijGInXvzkoomMHo4aIs9WiFevNXaXX3lOMGZae083qgWOEIoBrGSUMFDTnepA49BXJr3zjKHhgoiqMAX5xAcOmE9Bi5LFoUpFZhQlxUXJjHelhs7VIaratBxtfXvfUlm76bww+8GygX3PImnLnSgwe/7YeJNT9YkF+GUizEB1QSyGQsrgioWgYbRhGF4Cshnn79Hob0D5wRBSHlFAsKNCCKvAvnRO620v+xEk852zLAbxFJXAZZPA5TlUImYAd0qElbS+SI/2WKuIezLoSD2r/dw0nzXQCfsYq0Ih3rHVAvAw8sKYKVO+mndKaH03hglIz4D0peytF8C/HbJp8brwXyziKLjvwA0TL897U+KqnBYccD6G9xySwTsiYuUFMFhSjPaGuU0ZMlhoByIltK2ajnUX6yTbbO3firQLHvC9gOedwj6wUUodegdISAwwc5hKhp9fsypJ7QAJorWMOaG2OQzXYuQ9IaygGEchrH8qfLKQFdbXg8WaPlx/2vjt8dDNUIyeQ9Hmvd60pM05RE+Vu/UnzZ1lpikq8JOJ3IVxPFnzBEyYHxMnaKch8t1MVn1u23qBNimkamR1rVzS6IUJR07DXa1KmIJ1Um+txVke9hmnpWj1E/+qWWZFQkBTohRggZyeIz8GJqQdh1F0ZD/9kgh0UvI8WGHo9lR50B557I7Vx4UdAAiBsB8qMCMvHr+07d6Zfn3keewb354/YevPVElRGiqs83iXV1K/Y0yspOkJnZyMlZuCKPP3iPCUDULcDup0+40FEoEwc5ooOk3ODE90ztRBFmMiFAcwo4eH0R9dTbmFnyM7bUc9e/Zvw/n9O/Lf2xx7BeHUrmf8lAl3Huih58lVFI1KJwRceoLyI0xA2vc0YlSsNwrjDOeM+m8Ando6oGUanaCehr7W5mRfGZvzRPFCDO2hHrZZX3NsbxQx07H0lTO5HMcaQbJfqUKxR0dW3fyUXSyeZfsCg9jYUIkvF0It+pGLUsI8tuO7jklpuyWSAWCfdm6DaJdOm7eyf6NV/QvcqBss4t827BMF//89q8DLUx3zI2m3mN0CX8uNwKccWqeRX+988saYqadch4G32YaFOz3TKUkn/S6w2OuN93UJHo79D7WR+S7iz/lab98o28bVSreN8V7iQx1VkWzhm21w+OiF9Io2qrfdbf+G/fYvuDGWqgyiDDyiEzQdBW4xcEB5ZU5EaeqxG3PYNcx360tV20h2rMjbsXReNwCIoG2+qEwz9/12O0HLnKQFyCoYlZyJSIRhJHj05x5OKmzAEArydS+WiYDhAmc8mwUOc1NZ8QU6yb6nY9lIjMpNYGjZyHK9ks+7milEc7uNlMpeADlrR4g7eLJxiBFHefCUCQRCDRokcII6tKQpoyhh5RZ7UekCDgGUkE1xC4UFyHV0zhGA+FCWJucnwqM2U3oRNIdCmrj7FXr0yMqXfZehxvDv/nMN1q4eWUKUBYk77r/Lf74ied+ygxnidIDlUgZx4rr/CRnNcex4r7SVWVtN0tI9u5IzxZ/aG9S0/3oIxUp5F48veEjx53xuFGFmO0rEiNHUYogOrJA55mx/T81EF1POZSrnSDEQF2ALftAGNY66IHiDDhxQojpKCEVBYqPsb/pe+u+p5Qo1jvB7wKPCaez7r91UdAZONtf6TQOS6UiXgDnh2Ngsx+Cc13E0Qbfeb02VivHD5MBD6J8zHSHsXRQffz9Fz9nA0le+GJU+eOkwt1nM/bLe8p7Bupd/8m+cZEB8dPprVsVf3TFkSscpfSjX26WQqtch2WY3ZpCg2orEHkQiBfZuR03x8di++/goz80rcQ7G1fUa9lhPBCIRihEHdeAS4Ee/NfJWTCR6GyeMhAUDCilSmC5U6bLcw6j307hm2y27gxYhq74mf1Ycov2bpj2EQLDzZad9AarF+5+6Y79uSQPv/TFNluwo7//khHMO6PIaA+T4a3v2uGd32iA8WzxNfz99mMXUKw66t56xqkECgpKFLyVWeUImKEkSL2bTimiEjZBjU7j6qI1py7J5s6AtTEq7fwtlBARitY8CMeSvmEdDDeT9RJq/a4VjsLO/3pZGZ52pm/0OUQm6dz6b7LOmE0G3jJ59G7dNaZsmrKM2CT65tutHv3266Fm2d0vffjoQ2/OyJO3ffDLtJ+tJ1PujTTcODCh4W6fn4i/c/WxkIdh8/Xe3u8XUS29d8tYTmdOmA40qm+IoirovmuMTC9anxjBlzsAFAEqbIe6uOhFuBTzCF++Vu/YSFBCYqqwtxCI465Ybz0pEqsmAHzza+J8Y2KbJoqPjnXEnYkmJz+k1vb70YxJNmCLvPE870ofTYdjrX8ZDHW+evqfq2668tFXyI93vP7Med+vplEUwWi/8liXnn7pjYbFbGmRP7kK4WD67vEqcZy/+8lfzaozUiLmWX+ZJX2VjljeUU4LK2OjQBCRatz+HTsKRwpKCwJzvkTqbDvJ1GJTnAff1IKJcH+uwctOOBAs5lEZ/XDnF7/Y1Ji8yzXzHhvpfPtNonvDeJLnHZ+YatCLkVNNvIqr4TbimiXcdO1PP3/0/vtXPfLobeTep288463b3qiHOpLG6J0g+gPcE+9xbuFncytvwkbH1e7UVqXTBBUMYGRzu6n+Hx4aV8IhFnSfhgAr+/WBMD+NtT/tCQoMYXEOUhser6US8BMTMWOC/Hurayg+Zr7POVtFyKqyEj+QvWGqo0N5wkaYS3TV/a0XJPk3095slme+ruMkllL23pBxqhn+jZLlaP5x/+dXfo5fufLJm54k17z51OS277/IE/vD3MzjbBvLZJn3bvhjZuJ+1Y2VreaKubCENxrbGLA6vFXbHkgz0np5aMSuWjplq5Xo97SCzVRX2TfJXKxBxN98a/K8weSuIkCxns+XuRKGz+ENxTr1q4u+KqRJvTiaJysrjzq1slkl8r+uvv5bYyqffgabfNibaenjKJIRdtCReoDbHrOIbSepHsTJ4uO7X3z4w4f/eu2qZ8nLLz1639NPffBxf/SFPK4+ve5iNFPtv/3ilj/4zAB0t2EK+rNYxVZVu38uTWxCfnKrDMvPLn3GIyyUyD5D0FepeWe16qfpUfll95Pq5+bVp+77ehQqnX5+qqqocLObb3kAQFHCDeS9Jf7qMYbJG92KIVWVgT4ty6Qx6fz+t1ef8K73USeLDOcDnkRaSzv+8a9cJmw4PrGMfflplvWiyE/2X7jiq6sev/KRmx7/m7z84cM33vH0Cz///EWfN34Zf/Ked04dFO2PIrPJYunqGlUfItPYEWeNKUUsEuO0BRcdtb4EnziFGgU07YPo1V/iMoSZjGseNp9daNeJMv74PwqtNaaRKgoTo1WvV6+9/XHxmUwyTibODzNjNNEmxk6baEJxNhkfi9G6Ws2k1XS7rl01bVWkNkqLILQqUmgrdS2IKCqINWgtT+OGdxF+sOUVUWENKlaNj3jsH0JaJvec873OLaaFC5qwUvu3VrsgwCDN+FJ2peOCgGx2+EQ+ZtPNnIXvi1isxCwhgWz7Lzp/lqowCKyKbAtxnCE4NIT+SWB+yYk4OUGRr3qRQ0RCzGkvr81M/NbR7XZvnq75ZeTwt8N33qnm8kzC5h7vdgz2hlpuZSrmkpqAIFi0WwQpT/JNmC44BFizwBRlajVfqQWDPpcZorthMkuq1dxUshJ7wAKmq61Q3qwq2FyAX22r6AJ7oJaaGb9zBY5ceakJU3nLpolk2hcg4PgWyjizORZhZou5LHAEsfWlhRhZlSQiy0IclOmPP3awzMhWxM8hgBrVnUGFx7M7Ec7FONcWnUv2Pt/cnQoDzLof9bTM7w5WVMJhUC8zfyXVdexDGuXMRIkITDY3GfKVDjNWmEmAD5Cmpe9LTc6EwCwKT7jYpWatSZIsTQkC3+I11VovNRHRQq5MMIlTOZElILJDkkA8lvpssDyqUq2MkRUpIqQyKkCQBFESDR7hprzkkwVAMtGxmTh8RObB4WXBHhSJQHVZ0EVRQQ6MDn6qpcQ3gHTCcYg5d1s8dXVHXzha05lebn9l6DD5zn5pKQ7DwYLPMsD5XD5dEtBAgiRMq2jaduV0D8tjxImFVRNnXLrApsyChIHVoMbgoIphkkXb1JIJRRQMUiOpzjj+fcVkSahywmyFXAPg4mULZ5Ek0QEtCuzYzGYeYiznQ66ZoE4SMlItvEogyFPJhQSuJPPUrmAH1X06cRCEUJ5jOtEJmnHhaYcLBqhzDi+vuFifv22yWPfNcn/N3Z9u/ds+NBQ9rExV319TDBkSAIrFegJifC0hcAzRoC2A4Olm+FYXBwsZOzWrTicEQ6ASBWQARMilpqYELV0wdalFalI4bW/bK0lLk2dBGQUCd49OKNkMyszDLl57aa1TsWxLUzqJ8DwstKR2egmgJqg8oSwQ43SRd83CD153irK/TyRTX8WJymuUuPac8SDhiF3A2MshhxcUHXMoZqtobRnPzt79NfVHmxcPh5KhyfGqvzBWUvahR7B5IqW8PfchsVqxlXLS/7mbNjnZNP9vn8RzJisF9bEIPNzXFLzIZTZ7IwwLriZshhuGhNPhVK7ELn8MnhMfIKrZvDpjqq2lPO9L5BFv5y6t9VOfaa3/V7g34jnfgJfwIl6wU0b2JC5YBhASQmGRRZzD4NiBoA0kZIMhzkhIgKwyQ0i380Q0FMA4RNOSIVcmM+4X6mo+b2j+IlRMHhbbqnNdh++0VRIADSzmtfKWbUmRkWC1Ap50E7JC3LZhTiEMyTaLGYopme0Kb8Hx9W0LtpkJtublvY1aTEoax++t1xKOHwhAWnfW0p92boSID39CgDlmDArIkGX16hUBWcCbIXXwBBrd45JFhUl2RCj//abIRTAFd0H+gsz1MUTg9H4mUiIITJHWRYSIDwUIK5+bqlb3xnqz7d01DZ8fO/7LT6HQ+O7uWDIZPWyBeqplljg31pbbNwwBEQyhrW+jj1gQ1gwnNkvQIyCtBRZxYeZ3yByCTGDLFQuXGljJA5VcjGA/A2Bt+Vd50/9vuf69UTcRWUWyaApYCK+DgxEAiYh0SgiuZRiCdcyWFxmkQ0USbX3eSzUmiDQPI4KzG4aCcJ/rkxuWHYgJSPtohlCk7ATBOPN735+LTg6Fip5wXU1ra0P2T3c4PTY8OA8TWhw5bBnPVSq/7w62HI7l9qtVeChvtfUxnFeNJsue0qRTC88DWUVqYfEZALcPS4rVakFxu33PgrmESHm43AO7kyHxsr51O3JSGYHvYUwRF7E4RAYl1OomUDg4K9KZDsboXPH9yyEcWUecX/AGbURkkm8FIVUmMK9praytvfztP0s+TUJOJyDGJkIjRF3OTY1nvwgXQ729x2sa77z+ruO/fPFpdn6+K7kYHkl1tGfemavs7w4PjXRNtrTMvFqVGZVAtVWvJGgEKKMgg2KBwrpENEiI2KSVCAVM+4lkmInGoCDV9vPPftgmdIxdJhlSCFX07T+dFkR4xTsNIcPPW2QLjwm0RZSR7sJ9v3MBHBTt6y5IgAzARKl3y8s5kGOW6UhR0L++pT7Thk8hPqI7lYSyDk71+ytT5YR/bCTsCXcf83TX/LC9cfOxXzIf3J9MJ4upXncoWRwaGh4ebjlc/M2zOB8trgxVykpwG1NLxIolwQn9RZLfYmE4UhAUzDFC8lICgQltMT9RSR4CyEAEF66dxkRnftGELMASjHv85iWKKGYUgEts0J2Ak8mxheUgzjNOj8SA/0j93uYF8wD3IAc2VeKIbpe9QA82s2OrkumrTLLTRwl19HEsFgjKf1wMxgPZLHPigZuvv6eu5sgav3z8RF3wg2wm40kVi6FkOvXN+6GukcXfRn5bTBY9h4ctuYTksJ+2CRysRYg6cA/iCNmiikJ0hAWmOjEjyrQqYdlQKUUMuexIXvEhDKcVMRAf4aCEepwBjnPJLg4vnWYYUgnBPV6zXwzM2oHo1OYkNjZgn43Z7FSnTO/xyhyq5gdsdln3OntkzqF7e24kBPCKfHaHX03klULBrtirE696HrjhrW9jHTUnj7/5U+PrJ8abN3vd2XQymUqn3Lbg+6m0ZzGz6G72/BYKnX1nOO+wk23CzcSp1uS0repiPkGUoBNcm8KoNKwVlER87ScXxnhjx0tFASIgG+AHeCTD2xonctMm3jcQ4XUO+lCw2DgqmBUdUUrMGGQGMvB2n06c08QrUIYQ8JvkI05b/NUdKtMCQoousi9XdQSQI0h3DNj6DPvGHjiRUa7OBNKv2MmNKw/VnDzSeHf4iy+6e5s97v70yNns36fPdn5bDC2GMiNfPnWn57f5ZzPJyaqmR7wcNYK8tKIQJzUSexYOVI3ZmNSEmCg5BTYbCyB4O69ucAqNzNoxz3HC1Kyd5yS0bRLKXuSACcRXKbzsNo7TZ0WR9TTJCjgypYZXzAvQduRdZQAvkaCYS0c7ljiK25YQ0VVVixgu5PDB0BI6U5X9ubnJqe38gFbdPzf5CfwXyJ9319Q/9NDoayfam7uz4YPvPipmwhOpKPBmJJXOZvyx75ozqb7oYnqwIjOXSE0Y1FeQrOpSUwT1CMwQZbBuoQm4NFAQwAnA6iIxGwcChRjDcPY8ryxIPPZZeCtcFWJLZGIeHImjoPfUIQZhTkxQqaRIECthUl4/WjECyAFz+OPOx2aRd7aADMLHXEu/nz03mNvWdQdCMDVRWx+fCxXT+dxMvFzazY09stbuvrum9f6G0eMnGk501G12bvR/1Z/dzLjTxUx6cbkz3ezOZNKp9OJkW0s1D1GIYAq+0+dgOMCrTCI2KCNuPbdFIN9u9fyLlcAAsMIO4HEFOPBHJIrBWWiiwBy0CT5rsdkV3QTRm5EYhe4DYoTtk35iGPtlFUphRAcVpmKEDcjOv0Ve1kkgToEm7PeDvpFQJloq2/MKEqvyzOTkfCidOhbaHE+dm59vGVr01D90qqa1seG+G27//OH2458eb/i091iqc7NYTE2EYDBpjzsD1WS7Oj+ZGM5pLkT5REHAVkC9Y4+zxfs0HPcyKxEguAq2Pge3fPTRFZgV6DiB9ulLs0oQU8wRHuiOOcGKYBARLxG3Yus7NoR3fNQu9lxYDbJzsE9pZVlJMEQ4ROW4KOtwRxNzRvj3D3oQ03r+9Uc97q53Bgcr+VlBn47MZ1999dUOd6auszfb0VX89ItP2+tfrGk9c/ejb16WPBh9rvn45qevtT85cSLT7N74ojl1YrPoTmc9qWRxu+Y0FGOokIiJ6CWg8DYHceJ8BFs1QRMAIyXCMcoKF88GKELQc2Ud9f3qwwgBD0DQrBhTRrBEVKHHsFBp68nUXxyqpXSgx8tY3si1jU2O5Xar5YRKgnaHSxJFgfgUkUrytoUih2js5qKhzIud7mJysCQ6nFPuT95/1d3e2+4+Wne089nO1z7vaG1srWm4uz7gvavh9OsTnx/79PXjxzY7ujPZunef+PY1Tybb6+7NutOTqc1o2/B+3jkdJ15JbRK1QpyjTslBJNjbSlWYFPMuA7Fjdi/lnDKJzBqM+vw8R/myLEnTG1YKYIFQJQHaCPbRvSsvmXYYDDQYMaOcGx7uTh2Gus6O5QbLZV2ErVLWXQHQfRFeDMmcuj88FM2c/mrDncy88pEi2vfap/Gz7Z3Nr797+0dH6kdfajzTUF9/V81bLz5u+/71bvfRz8Ovv/7pI/ee7q7rDn/25a2X9XaHe8OeR28Lh0Ph6Mj8q/O7uQUlssMhuFnURezdgqN5ExoxVEkw1MTlwAAEiJqZ+oYCFRhCkq4L0h87hAwESERRmMK0gx1QCbykcv4V55IXnsJ8Dm14uOvwcD4ZXQstFsfHS2yJlTQVbBitEgfwZVYr6Af39Kcy4eXnj/WGnlwv9OT9M6H2b8LuYrb5uUjf1Gs/nPyh9czJj6+refql+odOPlQ/2vpDw/Fjr3xw5/Xtdd2d6IrCe70wlnBn31p7xv3VeDTlXR7O+ctlVdbQXkHGJGFMn206iBPBONeDmjiBEkpgRGghVgBtUiL7ksOBEvqGf0lQRFE1tERC2fnyTZlw8QAJuLB/qUDxkh0tLOy3Hf62+GTHb5OLqenkzEJC29/NVXbLiZg/8IfNZedssnBgW/d0Pv9Q/9G6xQn/9M78RPZ0f/tBNtzd31D/Rm/j3U+P3vd462erNT+feulI55FTL55+442TjT3nBe5or2sOf7n9D8DRvdnt/unZ9ubeukwylE225XKD+6pzBeRRB2aoC1Ols/dHqpbyvmiXiOHwxwWEBIiHDI5eHlO0smIoKybKxMiPP4OulrWly3uYGBeRl8k6ZxdF8HKqyKWJ5J/F8WGIHJnD6NBwpdI2Em4ZbtvPa1uiPY4gRDiCejjt7ui///qbHhuabEt++9DmeK/nhYznN/fdnS803vfQSnfj52eesdc8/PgjZ06deuHx0bduuPfxzp1/Xjxa3+Bur9vsruvvXXQX3dlOd/Ygm/42WtnLjQ/uMaeF4znRCWOQ88S/EMiXVEZBbH89cDp0YuyVqolS7iwk1f2SViqVS4Zq5P863fnwYFtJ2wqsiEwMcMipcDpBhG0pik/JZ0ITE5nmbk/zYjKVjE62dD356unMSEvLvOasKEyWwSink8l0MfzhT4GltOenzv7m1HxXpjnbEk2mPQ1vLHiOHx89eebM2zVvP/DSrdfc8PipUxuRt+p/qG89Wfd8XV1dRzj8fLjZ7dmsv+tFqH98c3ysMpfbyn27zqSI396z6lQcoLkcbFfKVE/BaxSmomtI0YxSDgL3vDK+C9VUKpXtSnmutL920cXf5yYHy5pazcuaEsAo0MP8HKQEGS0NlLqG+r4cGdlze0YWQTrfbxta866EUsvzbZX9RDnvhaxZWJ13b4Qz7y3Yv/XUHe1uD2eTydDDH5yt7G5kXj964pcTx0ZPnbnrVM3bL7+9vnPd46cev+vxU62jJ9+470hd3ZdP/nhvf8OL3XWe7MRPHRA/+/8cOpcbn/t+0F9C8V/JaiHP1D80u6QIPgcakJlis624/Eppq1qpDLeNtRTb3hkcHqyM7baN7x9EdwfXC5Ntk3C43d3KXLmEkSL6AnaVYw4U2Vkq5VpSoXA2s5jxZEeK7XxxKBQOpdLF1Hjb4O7wRsmfVwNzZz3Z5x98/6H7j9QfeTh8omMz/fAtT9z5YcGYSx8bbTjZONq6fP9dEzW3P3bP8o/3vPzmmR/PPPfmu40PvNL4448v1G+8cDfcQ73uPhb9ors7605FxyYn26LD54IyCxSQ1w4gm92iJCYSatOCdiNfcsXU/VIuN34OqJWbi4K95aaGR95p210BugzvDud2d9+ZmpobbNkvK5pLp0AvXSc0HnFuV7pScPZQCIIuuHwxOplcDKV6i+lx90+T0cFKKR8ZjraH764/63+v/siRR369/+Tokfv6fx8PkPly5cu3G88ceenOe75+/K6X/qPQamPaKOM4iYmJi9FEo9MPflODmshLcls4ozlkJm2QAzOujHoOS3sfWKubtLYj6ws03HmdHD3BWkrLCscdZCCHpTtkmnPQ9gBD9eoGAxLH2widvDSSKZMMFx+f5O7D3X14/s/v///9f8/vubxhG41j+JzBhJphoqZuuruqqaqlrqXH2vlxQ21Df8/lvh6waoFwNKbI4Va1DLjRF8qeLi699P7Mq2vzR6fLWvOBdb3LXwIJvpsV2IAksuKoGZZZHuQaCEwWwSULLCswKr/DrMri6kLl8WNll47nA/3Wlrh9RQwvzncYPBHjeWPHZEQJh0G6Ac3u7HVFo0mVXVWcuZXOOn2Xv76qpqU2aLI2pdP2ifrbL16KSNR2YoK2jy4um2kHmofrHBxGzHWZSQ38vA+taW5MN9Y2u5tnDdXNl5t9vo+Nl/ujvVGnk8kkZ3daWwEYxy+cOvb5v2BHe/XRo/xzd68kHm4dXiirBNOUWDbDCpKYenm6j2VViZeoHM9nrqqBLJ8NMKKSFbP87sLXxaDhPnm6+ELbqcrE6p4zMjk31/rQ2NNi1Vd0RoyRiDNk7Hc6+9ec4cl1UV6jXJtD1uGtKjtyscYadBSkgy30HOrfWbSGaj3tJGm23R4z22u4PB1S8tQJ3EQ66PxHoy+NWb22oL8+tFyRthovV+ufOZr4vv97Yx9gGeBHryaKvsz/9NidpfyiK6Wbx48ljhUV7R7s7S59WgroixclRmDDARBFcmYJhCWLAKQZ8FgCuPAiz/CrDDW7l91dWKjMByeqp2Y2jx9fyCZvRoY6Zm6FLlenSRzV13UAFdUwZACyfdFlPJzOqTvjDR5X3f4tm25uI9fihQ+xYNBkQv325UN7t58mQbmbaZR02PN0r5x8/jEYMzkc2+fu4aijvh71HrrtduJ8bV31V3rv+XrXUDTqGY8oGfWHc0Wnrlx9+ttHwPf5tfWZ0m/BOV1r9kA82OVXRZ4VWGZcEAClCuAusEImJYqUzDMsAIxiRR7U/zrSaFgFTLeaLXv33VnEO7JwsOFMphom22+0twfpevegyeaf+Li5s39F3xMy3sh0pBpiEhXV16ZReoC7faH8R/LR424H6SANaDPeaLfTZPx6I0mm62kzmWfKv/u2JU4YbHS310bQZj2K+k2pisjSmYlvzrTUVneueHqN/4w4YwKbWzv35cy/V189Bdjo3NLZ45fazj6Q97KtP/ywlz2QWJBkDMO2O5mwFBiIgdAyGZZl9wIAqsh6bjEUpkRlIPHCGgBJlFU+UTSf//iVhb3MjYFI083ZpKtC30w6MHzbRyNNnh7P3fLOnsvRiMsVHe9rrgqiXV1oTRAMP6ZDcBvqM/j1mKObS3PckQH92Ww2O7A83xOPhpF4HHHjcQdO/0ybTWT3VHBiwj4xVDFh+HF96GNXQ28qFYr8tbw/lih69vQbpXfyi0+D/Xp+cWlldi0n39rb2WHlzKQcDgth5mqOiaSSX04yQmCKkVkmxmd4ZadVSs6KvKjcudSmZu+oYjb722+bF2ba9kSWCiQnkw3Gvh4rqSVoU5xAfNum+pqmqvMmW+eUES6Aifob9GGNPYia/WmzG7UcoSRm0XEOM4fRpo8+wmmORFD4QxAMQrjjIBjcNmdrQg20e6TL7U0TJWARrPY/Wv7vnuddhurFBlc0URZpTRS9e/b0+0X57wIXAxwJV+6qiijvqLKqKvsdYYZhwlkltjILskxSZPBOYMHEJV4QBWVWEXNZlhIBgOx801KWX1DVA15KArfBdTPa19c4SsBxLo4UaC3dNu3mABpsv1MeajiJQESP3+8n06AyMBQ14XEC7nYQCIRxaBxBBtumsDdtR8Nb9zgkTwsRNhvusOD6X2CLheMcNea4I/QADVpDLbXnK85Uuwzls2eah4AJtRLdkxYSpWeLK79+/2t1oTiRUCtFOSmJzD1ZGV37oo9hQMUkYwpFMRlJkqWwLPErFC8BgpaEZFLOZHhpZU3N8Ucv3GFBCfEgsvGGqSEgAxt69KNaLTaCF0AEDF/D3ORE2oGiU52vvWmT2vxm1OOgaxxVXTiG3LcMY7/8brlPxF+vipNx0jHcOu8bvU5AeJ7FBiGcBcGwwRHdAO61oSRq6LKnrcEWu90etFbXpho7r+E9nT+BnUAqQO20JhYO1L2sml1Sd3l5T5xtlFV5gxFyZ5coUPhh0CXkAOU0BuYlgQJjPSPEsqDPSFEFUMSKkgEOI5tMZVSRV9XAtBSIJIe8+vK6jiq0Cx906yy+rcKSD0ps3m4bzvlbcGKx7NlbqDmNcXHMMegdbrQhnDa+5bPgCPGnHsEQfN82gl8bvD84iOVpaa0PdmueesWCYQRGxLmPHLTd769K+4FGaPFbz1S31L0/0PlZKhQ19v59M0ntKPJkLgLWfHeVp7KKmnBuAhBkVgGRgDRjepkwNTB6vSMgy+FU7t64AniNyUoMJVBJSsqIuUwms8GqQIJnFYkVOjs83hoT3bVPYxxEjHIQRCBbR1AJ4ZgzIeYzlmnwI2692cHRpvq49jYX12q1J156HRrWQY74veU4MgL53tNq4ac+gGA4b7vwIqzRjv26VWIhYYx0d2+77UCmBauDab89XeH3VIxVpbwPhzw9i0P9QIiPj0c6AmvJ6Qif5VVRyrDJPqcsZSIMIwkMCCfGMMo7hZj2+WSMTa6wUVmRJBF8J8fGmZzY5xRFRVFFFTTUDcAJEhWarec+0Nr8g5AGQWBd/AukALr2DvTdB8QXkIXsMoMfZ+7S5MV/5jic4Cw4dAKHPpme1YKBYJAGBjmp2dbAJz/8zlcynHdR60MKMEuJVgfSEXmIYMNarNFLpxfRtN1Mc/6H1vSUp7yjujZk8vTcdx0ajb2Ryb9vOgMsmBVILOfOjCL3NumoMBNmAQM4w7H114einufGnRFlMhnuvbEoA2gABuBrRs6Ahs6vZJTZzKIsJ6lAQ11XHHkwY5uDlqZhi+YEphnECsfeLntwAnqqEIa+c3Pz0zigrQcW0hKHEdjy51s2+NrMMgzrcC03ohsDS0DoLhb88+Qonss7+btGo9PAUBzSFECwFtO6R8xxiEYJBCUnBvaDzf4/Jsr18+XfTDZVpDzG9RHIZuybjERCgrD4xX6UEQEcMeevi5QihAWnQE0K4d5NYCp9Fw2oTCyQCS23y0DnpO5JImg9siQKanIjKUp7v/Ebub71b/SAZTGsAB+B4IL34JPXYQ3y8PfCQug/Cq03pnEyDhOJRhMzP/nJGPzXqCT0fT9c27QJTWMyaZ23sIQNYRAcsChu5lRQinackKxjJmMIqQod7g+OmZs4CrseU5ARNsUcCC6LeAm3OCUXJJEcssW76Knv9U/SNP3QJ8/ze35Pf30X2INjysbEvIOJ7oHhhUHnIOEmHO5l3kq4F9wC8DmcvRxwAFu7f4EheJ6iOa6GxqHAD/h9fhsQOBuznLC6zIc2l/nkHdfQS8G3tvtHvh75vaWrxdQVutkz2XP77LVM+GKsT5HldqwDGCpyQFX2tXhcXgtcVvVpxI+mLjx1/6GsSuVSfG2t9N6JimJmHhXZRNzx1gTa8psTe7sTE2pelU+CHb85GffwqZ+3JSwYh6+iF7MznBujk6t3FgF6H14YTnMpq5uL9Q4lTgeSoqWY9g34UrzfT51REEIrxzsFomjj/DXkqZtPA4Yj/QM7BpvPuQz45fVu7yAKPDPoE9SMph7To23tQ8cvtb10ZbbdNH99PRp9w1ONSv5fd7se7dM0XdU1BXVMLSdV9wLIBLS99eK6qoa/XAvkA/qaehkd8md7qOmcvJff++MzlNS2Nicuf7an6rPrM2cHQ+sdKdbPMNg/bNMhaQcQM1CdS004oPm/GfrqP7bMuR0/n+I7UwRD3UiLIuunGQCAQAPK7RaMooGw2SBGwJoBnhe4lLiUZOx/L7jdfu6vu81m2RzbNvd/HFz5OIgCRPNK28rUB20tF8rrHtSqi31zpQ+qlfHHH6hZUiSlolU0Wa+ompZHvOhqOaKrGiKsoiNzi8d3A7vS5gn6IxTfy3+2N6Hu5tf21tYuX/t1/ySg6B7TO73esZiVczICT7FMJwBU8SpGQqzpmEwDcXn56IxLfZLMEPQdKxhmFhFtzkXuhtuGQg2kIaQgxX6HU2DxDQEZwDlqqRMhtomnhMXG+NzMwGDMvTzo9sbam1GjGR0xt/YHg5MrUy2J799885Lng9l9T/TKBxVdGz/aKgUqd5mJ6HJc0rSoelKSAxrCFUZ7/NSp5FH7lMZzqppFK8GUy2sBdS0wXDqa2N3d3JRlVZ28M9sVdHfERAv00YCneSxtqTc2knaLgWRFnOZxLskQFJMeSCWFUxvtX7JahIY0z0HBRvC+Mxsl+hKcUQSEDQrumgMfvwg4yPnFFAWExVU+3RHj+djYDffgmMuFstq2ubt/ZiZ4tdm0OfTyRy2eqf43Ixdmr4x2KQVULeOSooX1UESvSJFwrrxZ0Iv6sAehOZyPf/f2pJrX1JC8GdfiAfmnXADVkLz/Yy4v78rzmbTk2JBCnpb+n685AW08xCCFgVQ9ewviBoYWkiyBYcjdhBu2U8Qb7RZZGysSbOeBnWdwigKAgDaSonEAUc0IkKSoGgGINAk5gRcXfhOFRcJ2Fosd3bYOWr1jg67mmRkzihKfdI++P7RiGm+5dOHDTyBI9M3OXslGC1ldkrKaXKlI8lt9hVA4hC5kyRN/bldTp/RIAI3fFTUb13UFSXAvrgSy43FZiUf35JL6ZfHBo7mt65Pr4+VL5mE3Rwts0znyJgtJwggZmkt8i5MY1pkmIGCMvLF4w88A2CT6CGoxSQDiJgQ4RnCZRYjVPorTNI5DEsIanOXsD6NneB4IIkMQnH2BS/VanahszANjw61er6N4+nHrSHurqa2l70JL7+c5/1RbzzcfFLSCJikhRZJ65I0/1kNaJOQwlSYv59FIvktV4oHKXQyVQEDXS6opi+SYP/lVUgJiwoTMHAHLlfZNt/eHRrvMiWUnYcFXv+VxzEKRJCFQACdpaG9qFCiY4dMWREPaDcCZXUweOKAFQgIHnf8sPO9rJBtS19yQpzASo8maDMX7fnsSwAQQ051UfVOTxe/vSPliO7xr7HZvcPAO4ZhJtG7frZ6V8x9dmF555peuKc/sAB/RoyEksNBkWZdkpJyKtH9yvXxeQwA2SgoyBFWX0HVFl2VF27imBPRIUcv/Ern277WAqoZyemh6aH36raH5LtdMjAEkbT8TSCQdiqZZTLDX1+HPPk3C5DAHLCRB08CYYTlfmoEsZlnFSQYWjzppCAB26y+aMtDkgeEYq6F4xvcqKZIUZB4Wl75ib+44UjZacDuWl2Mzg8uu7Xlvu8u1vd0rwIeH2978+CXHxfdNppevJr7RCz1yOLR5vWcyrCuKXpW13Bu/9kmaqikyMjVl6xAxoyNwVdRzfpR1XYrqIU92c0+XNal8UnZsXro9NN/8frOjl3MSd6y8nWKpWgwu8oKRwgzks7WUY+tt1khCEgcA7OCLhI+kKRqS9RbwJE42kJ3w1bomJwUHaNJOgnPHNXWNSGw8zznBwQGTzLxqJLiU89iJcnRsbMzpuOsCXu/HZm+djahPtpxvGxlpOX/x5dn7clciVSWMhkOefSmrRLJSqBKWQ0h2SGEa0p+mFMK6pEgvd/RKFa26Fc71VBQpK0XL01J4f9oj3fpqcnp+pulqV7A/eHPB7SAAncEaiyJMAcyC4TjW0P3tQ9/VrYrYASUyfnhAExgh+oo3MMpnJ5+04J2Z5889ees6jUFooVJCPcnWYJA856vtpGmKTVpI4edGICz4ndxyhzPh9Q60xjpi3u13vF7L1h+mR7db3/F3NK+cP9/WNv7WxUjVE/llPJsthXo85XHJMydFJL1QqBYq6FSUaqFQqVZ6br34o1ot9GWjuZL+Syi8L4Un93Ph6aNq9/r+xVEDQY60vt9vZlBWbjqmuDuHIgl3MLwWq4erP9zzaSPe0EDyAKKbJA7PGoyYERoerSXql16ABIkQY2mAi+km6BRqaarmv/9Io88OMhS0gIzR8jpvswkEgzba6uQSy7FWV68r6B374pF7b3qbV7qf6Q2uNL8/v1L2zPVNXYhOeXo8fVlkYtkcKp+NOVNfqID0pClSVbkLKhx6Ze5PtKCg3J7LRS9Go9GN3NB6tGlgenpyaLq9LXn24/FwMOhMemMpK4DWUx4XsTRWX0sZDAas9mEkJEMDZO1OH87yfB1GPoGx9Y9iYm2jxbdK00UcscKyOINbMunjTrbm3f8eq0PWsAOMZGqHtVO8mxFoFDwdR26GsQ4ODrpcaL42ZjUn3mmdDLYGp0e3P3F6zf0z1pdXYqlL89PRnnIk+slUruckFK1kS+GqEi3oG7eiJr+tUK2G55SCFPmfIysLbaaKwsV58cULKjNPXmZMHMZcmEWYhQnMEJfJTFJbO2BS6yTWpqmkJnFpY5OqrbYYk6pYsIixWO3fglVjaUj6YKW4oLjwW8QFF6jiRpEfFLW4YV1OzdJCOm3n3HO+73znO0/OfXbmw6feeO3Mj2+89sPXm79/8mT/yptQs/d3L7q6lZt6/caZ/ER9+2dPoTQAr6u4YQJ8a9lxscyiYA/J0AJBFCQsjexShX20i21JScf4fb0DDV+PWgnWL8SkvtC/Yd5hERs0xLiR0B3TTHkgz7R2JtaLblRTvWq1/l69OZ1fzc0MLc/mZxeHi8+PNaspr1XrkIn86Nrxo+tnPnrhzGNnzny5Pvno2MLkh7/+srk+TmYr5ocfrs89uf7a3PpDz4xvPrP5bPssTETjT74+NTrw6Er/yMjw6Hujw+/mbmwNr+Z6WxP+O9tYl6I84jsNHVuKUQ7ccohYkAypjSTJMgETxA+VUdx2iLLnRokpeQmQDxLfNeLHfRQqsAd/O1ISKLHsJzQ5IcsxuZ32PKInNtKljXoJDMJTkw189ve2ikOgbWaarY0LPm5tXfOyPTbV/9SjEMnca5+OT0LpvfnBg898+DiA5qFrLznvu2eeeebXM7+u3fnYHOwTJ1/746bPHhrvf3Plvifvu3FxYWBgeHZqcHl5dfjGVns+l8rk2y5/fGtXNwhoAAWre4RQIW77syz6izdc/YulpagpIQrvB77iSlZWdahANzXxoFYLvEAhel+cqEjkNd+ynKwamCZIUa3jtU2ZUVAknc9sNDOZ3EwRBoJ6czX/6kZ/t9hsVtO93GC+1/4pk79xZeWrqbHxZ9bHJ/+Gfe43b6w/9CHk5t4PH1r5cRLWopPr65uwhDtTG3ts7cfXX50cGHny6xeGFvtnhxe2Eumh3PDGUrE506qm0hoYM5KlIpUgx/D3MKGREObdP/bdc2+jfZNHe7yDMJfMYmKy2KFMJ77voD1idgjE6iYO9D6JjSAUDkPNKWEWdIFThuxo0tZf352z7uaXuhMVslUFPssNFpuLMz8VV1/NzWSq26lqPTOfH+vll594bBdObHzy49c2NyfXIBMfT45P3jl+Zv2XXzfnXtkchy9rize33tmZW1t7aO6xoykwL3ytcv/oov3Wt7mUrPskk89VTydDXaRU3EE8DijJjEvhz09UqmD6es3Rze19yTFoivq7i8S/C1mJxTYpOLrtQmcxGYH2oUX1kee7JA4XBSxFHZhxRLF3E5DWhvft+bc6KBFr//59qVXNZ/KZrWaqnp/YBkqIefViNVbSSulefez+hZNLr9lam9y8c/aF9ZXNybnJHW1obXN8cn3yoevXHoIfzGn1G0Yfvv/1+9YG77/vnT/nRhdmX75qYGDmjr+L9bPis98cbKV3etpnDelA4r+iFF13JIIpUtO7VlauiWUXIx/9hQ1CHJV2LcCNXHPLPEe6vEyXkVVW7b8aZeDAPoIoT9pndxk+TDGERTVdiJTSsuxpzlcvy2ktkwKXIXW2WK0+Wjm5cb56tlo169HWjZUeGCXdyvzZG3OrufmRh26+ffjjytzc5JnNm7/65vr19TNzH26Or9y8cObmp5Ymr1sbGW8NPfnoG/3rQ/dfP7dw8w9PTi/P1+uwmkh8ftPn25CVXYPsEk8jPObDoXKY8CoqIwA/nDNFtg1eIvukG5K4jsgSe5dT98TvuzxP1bJlCu1T3XgYqVZfgaYwDqPDJMbHRxY6uu0Yc7IBuUl42o6XKUVjMOWk5JLhbWTyqdZ8L6P92H9jtd4EvE5E/0q3mvVz868uDLx0+/Vvvvl67r6VO9emRjfX1l4Yv30uNQI7noceeuPip174ePSp0f7vR+4beHbkzoGF0eWxxa1HB88MbvR+HpsopWI1cyn68/O8LBnhbwM9WeAwESjMYUqleNlERLSw4SC30ChYiN7/iRURxi6SkI4l7LhqnMFJnOT79EgBcSRyS4hQmOXD/O+XFZAO3VbrlqJaWotqR1Ytik3F+qn0Z2c1vZvL9x5uz7QyzZ2ldq1bqfS2K/l8xcDF0evvvH2kf+WG2bXOwe1ra3M3fvrKOysDA7Af7b8vn8uNTS8vbA2OXT/UGh1Yfm95eOi9+gw0sNFqqjq1o8iGoaQRiH588reMMOY4+pgwmKLLFu9L2I0bDYpxTLIfZ0JsgbCHpuL6VgIhhqYAH3FM72Ca6+tSND465CIhxKGCqKvaH65qeKJuRDFnagnNM2t28Nt1XcmMpRKpBozqsldKTYA2rLXldiodnJ3fSKH5JfP++fTUS89CIxx58837+kcSU3euffrY2gt3rt08et+XU63j4vLy7z9df/2516fzN7xXbQ0Whwenx/Ibzw4OVrWdqGxAf+F1JFEUTe/HPxBZXIjjA4riiO/sYRsz+ABzCMXVeIfhCwaK1yhUdrD+nUIMyJKJdyiuD2NMZ7/okvBREoWTvBEPeEoWE3LM+K5hep7XiQJXf9uQl2owpwY+TEqlhK9JQcLbkCfkiViloyBZu+mmYOHN2aGX0l5p4Pr+kfH+sY8GV+4bGLg+NwjJWRyayeSK9cGZmYX3+ltjyzOr+USs+V69uAHOXHUrlUrI0A5NXuRdnqeBjpjDEMY28G+SIgmLZhAi2S6mgmMHU5woNRQO07RFg3YJi3YZIkAOg2iqD2oMw2kQMU4okcKEY0IUD+VoYEM2NecwGsAQYfjBrmdLUtY3tJguGztLKOalZd9LpDzv+b+ipfTnzzanXxpe/vzWN4emp6cXRl969/r+hxZGXjr7zsoLs63FiWBjYr5YzC0uD9Yrf03lS6+/WW1Vmxvw6DVShiDA1MsDnjnWDvZV7lAS4QaxjIiL4DvGepymszqQHGZOklhlaR0phK4lu1iiEaE5qZAtc3t9ZZGzaDgIEqLY7G7gdihMKY4feyQuyVrbiy150X3FOlAk2zyrHDigAx3l77uOoq4S1k0/WmrXYrK3E6tDFcGcPbo88wIoletH1gZGbr7+hpXB1o0DQ35iMNWeWIr1chspL5+qljZamdLHQ9tb0WpJMpd+kiPgPWI5UMLffs6jOAtHznGYcyjMCBwJYVrJWjYVp3F2m2BBpDlKiDAu4nwd5jeEkAR8QBFGEPsIBZlrFFChI+67SESIE2hCkKRItZruBEbN7ugHWcPzVaOMAqlh+rIpmoboO5Qoa0s7WQ2sqspGbvgGTduI3b+4OjQRU4qjLy1Mr7yU/2ngg9H7rSVf3tppp5cqsTQVjdqZv9ZSg2BUzseCmPfFcS/qHXz24nPPW0GWnCCi4mTXogHNNGYYmqEIJlKWdI6QT5jT1v93kmG4uxWDoXyZM9izBwxKizDEoGOO6QP2E5IQNkUhvWwDp9M0R3hJMduNAIBy5Aa2LIV5kKs8IwWI1y0s+6Zx9PKLJ9quWTO70Uxsu52baLWGnk2LrcXW2NaVxzPTCwuwDnlsaDE3NFC74u1sJR20t9tBov3i02/1plobzVS0mjK+mN8uaRmglHO//w22SjlgyzyikweEpx2FFoSQzUBAyLWQSCAGuFVMJwU1THWff5rnoOQ4UKAHKlAYKTum2+gD2jsMhZI8TXCIit+N0B9usE14RcpGjR0DH9i+ZjvsnhU8ckR40LOhbYqAn9g4svZKO9HjLYMHRRsttqqD+Svv1YZm8outxenlJ4YHphemF1dbjxVnV7tv1drRtgaWvAd+dqOUSidMKVoCV7gXO9staQnPlyUTHVHxJIsVMV6gCQozmGOYcJLlmA5uqDQXfBZGGOR9JBAERihnCQ3RsZgzaJQNY+CDEOb6OA6C5yIRzqZD3x0BGxzuIwqLDV9CtsIjrMiOqxxEE65vl1XkyJ0Xf1YsQ47C/4dJyvOQZpAo2AV/rea+uRDyMrg8nJ9ZbcuL900XiVZcHVydz8W+75QmwJ9vL0Vh9vOj0di3Xc2PguEN1qU3T0TDly0lRDFYPCSqSOmEhWKHcMJCmOq4DsN2VQAKpjH7h8VE9oGxGDNbJgUua6kBTYXdOO0wDaGPpri7I3iP3A06IFwOURhTGN52ObqzR7Y82pB123Bc6AIBv61LOjCoISletvPH32BatxPaPe9LsVIKnN10MXe0DfwLz+hUbbD9HjWBYPLOZLYm2r1YopaQDQuMCfiD2l605C9BTNHGVFcjFeQoOmcxX5wgjouLLEdzOnV6xjTUEcsQgUblsm2Lro3xPt4+5oSOQtm4rHBMAQWIpWwuhMsCq/eda6iRu+nP/oBfBWFDx6lQmSIqnzW0hiE98kAgGoplSpSpyzpv2H7c3TEs3eEbpi4aCY+WsXm8VWpXqr3Zocr8fHOwuQqb1EdfnJ15deavB3Yz7cz8Ur69VFqqgXOa2JMbu/vKiVbyrN2aL0e94JqDCfufdHnPOujEHApCCmgEbZNHHHCzLJpYUFVLPaLOxTkuyQgHBSyi0J7rMnGFcThBEGiBwfQ+owidn7J9z//+bZfhGApR0K0KJyKNuXKc5uOBsy/HHJG1XBGGV8sBoe12CSKGxVtig+d9MZqoSe7Vf6XljQrsG3u9Zn6jms8Xm++1lqvFTGv5vdzln9fr9YlzxV5ajvOyDD7XQboWmJAdRd6W/N10KV2KGZ4jy3bDFInEWwVMU2XHVuMc3C8gIBwkJUpltDLFA7vZexy3j109DoECYiTImxtxRZ0BAcMwyO7jMLAXo2IGfwwRIZVWEYMoEXM8MiQJaM3ZE8OfZyUbSfyhyCOx4ZZF0+cVTbETbccUEax5z6aqg2AWVDPVTH6+CMZuPtd8b3CwWYcGX9PapQn7k0d83QTC3zZMAsOhCdMXLI80T3P3tIbnp8M6sZApOo74PyOz7r4IhQKNholQwFY8JpTPuifwiZilpDiTdVCEY3GEiUQY4e7I3Sy0JLYPx7EIxBAkaZdyC9jm9hBHE51kWS7iiFJWZiWeZcO87ds2CDRzJzixpAKEpgSB4wTix125U0pP7MRSeVjx5usT1Uoxld/IVOtQbc1UEQpMaZQStm+52z+JZixIsJAYNQt8aYDZ4IPnpSJRO5It6NyOzTI0FI6YBMqCB8eorFCgk0QXBHQOPoA43VMknVqEtBuhRUEoRyI02gsJHBL6wM8BjcMyGFucSui4xWLmbkgpTb4voCRvv+OQnY4EXVc0eIs3TcUE5Ch8mBXlgsSbcU3e8rRUqZHKVNIZMI23VqvNVuVsM5WpxmC+7qXbne8C3y6Yp1Z3oIH9evx9QrEU9NNJ2vZFU6Y6dtcUWZ7gHY5SCQCfwdDP4RFhgNNsJgSCnu824mF8EuFIUuT26DIpY6dGhyMR4fS1/dFPBK5m+wplzJTpIOxSB8CHnMipb//B8Ogc5h2H9iVi6yz0UPXcocWGywhYM6EoWXwoiK4ZfM/KjmdENdmOtWMSkiYqSz1lPpMa6+1M7KW0o3R+dy+W8bSDdBAokqnZCVYBaSzBuKT//DQvQh2bhn6Y3eUxTVwax3kOYqEgNUgVOCZyd0QQTl8gBBgKsM6dZkY0ChEuwglxxEAkHMQMueToCFRcnxhWKUHlT6+jDiFNmA6FQ2oS8VCmOk0FYRDlCO+KvP2zva820N5BIZ7wj1m+pmQbnlyLdo2Y05DTREtgoxftfbeR6qXTvYl0JVOpNEtRx0kAlwDQdFBIAEIQKJqiQKHZhqTznx96UqCGrS+QiEEAnKYFbhDiUAVROD31rMBFOsQV/oocRli2A5pMEFQ6ghEEyTT+iGBRAMjQAmZcB/UxDnEBLUzDYexwkoRUsN2seNiiiIX5c7QFDb4MedNDPrCNgRqobPnY5BHacWs8bxmSb0TNnSw/++OXZ2W5ovUSsEzdWppPGLEYrF6XwNOCZ0FONlS2hkCvfolV3UdH4ERImiQhvgzpR7ecohzRp8EAWHSBZSLbQSTCMgy8oZAw3DsKh7CQhcOoUZEkf447bUNCyArfHQfYcI4gxP8j2WpeHCai+MK7v9vMbZghQ4gZmDRCmlIhwUsSN3jpQS81Ny1BDHhQXHLwJBQv3v0TRBZL2Uul4EVQkIKiCEIVv1hkQVAEFXZl/UWHNt9p38vv4+V1sxeDiKjVTWQt9iW90jazLdX+A3y6YKrf53BVOUGUUOGcJgpK/UFhdsvEJ8m+v7kOeZm/f/nrt598+fd69vT86fkbz+F5HHQO+qlnnrr5bJaW4X1ymZR3uMmvF/kfX/nehRWssaxCSJ+sLjgsyEj1Hy4I0aoYokb4mOENCEaJ5PzY21Zi1YnhIE/yhEMFiUqvbyMwDq+LEccWuvTSWJkNQgil7jOlLI4iLoVvNRQT7tFpdwosZXaMjMi6cFXq+y7lm7VKFt2Qo79C77uaP4NWCx333c+z+eG5y+3Tr6xmN4AXzYhLn0rIVWF6ZF4m5aqiUC0ZQwuSp8xIAVwihYAM3pPrjiYc1/t4TJJGWotEEDNxJoMwEtTpGmfKyE3p6hR8vKDIqPFGSl0JY896GdUCNUk+HRcEOBSR24qmbxX37fr3q0gxh861boRDdfvdPHEPvyc6bXFHEOY6XW2fieIQHf3V5fOvXS2fKfNZmS+hPLecFdfbRfrb9VylvL1Dfrm9zYvBUYD2A7KH3iF4g8iMtIRkVCPH5p0xGqm43RKEMVaWY/ZSWZVowyoGEQdAhzHh99BexNEH51GqTF7TZOw8wUzKuMgLagnVSCkntGpVo4vX/zY2r70ikcxZw6nTWXnPCwjShd5HmsSxDF56SfohvcRfUBd47mC9W+Lm7o/Jzpsixy+mYXhu2OF6XDVOqazN4/hYtCo0j9MYFRCZTOD6mzg+0eHD7QSPjoGIKaB0Q7FkoyZgxETBAG+Y3siSJpoVUXMWEFpWq1GL1ohBrFRIv7TGEcmnZqM4Kugf24tQBcrR0IdOPr2Mg0LrZpbyYQGFRVutnn+7jHgX/fVkkdZbdKGrV5Ylfry/ScpZNXBVhTdLxNGv0BE5p0yaaKrALQ6VAr8eH2nI1OPkxkKhvlzBXmOsIdZ9WzTjSBl01GgIKJvcOsLOyRbAzHLdYgvGhYhaYCRMocAzs5Z9Q/2VcQ83eb81dP8J4TpozV0rWFGgiEXAnf7lBxlQ5VDrdnC/Kx+h2n387FsHvVs6F86TYhUunrlKb1Hbl1fFIaxVuVhynv3SuQjPIC3E+4tJKwxLqr7OlFfx46qcw+EQPkkbt1oJ9vEokMs4IkBM21EAsj6K91GbafU7IDvbUaLI3Nf/uYW8MEq2v8Wklf3iFuJXo++S/ck1oRBdb+n2LA4g6ZovTZMBW1LnMCDq3tsz3/g1Alql+5fvamz69qFjqtJ1n+qBknKxWC7SU5fPnpo7pt/zQe97UjS++HSIan9fGlGdKJDwTNC2jR9F1L3wUiqVzEYVY4yEBORYIxuAcBxbpCCxLCGnVsWdxVZ4H9YmwUQQyQZuJs3GSKOMEcJn8dKMKDeKVcZmLYZeL2qxcD44kxJ9NriKDI+KRbnOk3q1bua+OXzbLA61Zp/zgZStXcDHtU+TP1Jd+HwWVvNbx5k+s+JMRE6SQ/hM4LH2YtEviPD9j3cffPf5n9fgvvD3Uy5cI3JkgmnWYg4Py+APY6U7ibmUgOv8zYOL4szstEikRjLCwA87MJRbJeVn9yUuQ1MQMQUWX6q7QGJZEUZoaS0C/WMQJs4WybFJrlZOrZKV87QSi+u9FRy1B3KaUYSGG61zm+ZAGRAmQ3e85IL6d98k0bheqYpcX8SREih9o4kPT+KfUPYxree2BasQvsHEBNFAk3SE5IiEpKjqJyCAyQRIG0nRSWHZc5PTRYNewEBzUggHQ+z++UfUksmcqAQWTUYbfYys6BzFsEQRGCVOZWL7m2QNxaTFutG+qb8vlutZWNvNl7Y/HcKU08Gx1ce1DcNyPi+eOq5rWl5esjt8+OplADqvXRiyMs+wVMRvnkD94O7TOxlBvxqwABC1gz6Qk8V6JBF5E0EfmP//wgDjNjJoN3MHbg0njbb5HJAGb8MT09kqtSq8VqVtvaE+6DoK9Dfas/XNw1SvGkq4KJLcRqyk9hhZUfsiWTThXdkG1lJnda+EEA+s+6oIMpsucWGXC43am+Uz53i5rtwuDV2mze6nn1oqtt/viWho5FQ0BTLgn4HG7RcZTpxe8ZLZgljTboSFxJt/qk087ZMaXoodk5TEhQ20IedLzZmvtaio5p4liw+iuhRBEXAge5pSc6eF22T1oc7mrDSvfu/iPMsqW4igP4ZEPuSs1d5aG2TlR9JZ6709EmpPPHtbpF5zWOuT9w0VlhmtKgdk9d3ZkZp8xQDyiMEQCCKSNO4MIjVYEazyaz1KguW2rRwwE7LvIZzJnQ99IGcdi2tjzD8XxAF7nIhP7FsttAcdMzBu32+yjbHG+I3Xm+IfH1R90QRZvRG2u6V2+9PO6k1gd7q/plOja0hcB0NlNcZ5n3WcrXy9DXTQiW+faI61NnYY6hANUx46qy03rbfBBgsuCDZiIw35QUs+Jx1kHJf/cmj1rLEbUdRw+9uNOjHDvYhlBSO7GNY4IJFGo2Q6N2kWFWkGFVH/cB1Y8kcSQghezDYG1em2SooYtguYkCrmFXnBfrycjfwlyTPje84954jxLlQ0BVdK4DpSWex3PB1WuoYfHOLJYKtsugXdKcdVUQqyv3IXVozQ4u8M2mTEG3raOUm9IOMyae7yzjgnTuI4kbt2VvX00gyWcwD2LoUuqoha7kWTelUfxlOIluiyTr3n6PJMcmqm+s+xsTppv4HV2HssZEVInMF0FoMSgzG2KnEYh18UQBcyONZKGPCK2ZRuffiYnLNxHMzb7PZ57abFda1jYCzjhc1c5N9ec3px4QOvXBZDHCQ/F5TwY3H57tOntfY2SggfwzgF5O3BL0fpYkicT9CSt3BPsglYJMVwhMeIPi4Lx9Xfb04VU0YJt5QAYSdbhn2FgEGjuJdJCE8JVLsxriWXFUQra3KB86TGUnm2dI/MRr/W2Kt0VOhUrJ2xAesUzERGbEk0Xoj/bacpmcKIaY/OZTRHSOnoKZNA8av6m8tP/hTPPBJMA50sqFl1ay2NWWzIqiyjMq4Z55psTNdaUlnPclxaKxhTDtLHJfZpzbLqv1hnyRqGMTtHtAp94VY4rV5cK/7L9ssulFOcTIQ8TLEiHOaODQbB5wz9OUMvsFmh6wxjtYWrjIpDAKAsmyS22UXwAzRBRYukmf5CmeTWFRefHGl+1f0whXT+X+1hsUbWMkUbZjwKyIqVFYm491DbOUkevr+mMhliybdiKds367PIoPR5wY8/fpAybYRJyBmT8+mf0WW3Le4n43RYfthmByIl0+RB0oMGMiY+/ZPW7hxaxW4sBO4+hxs+V3DF+6kAQHiGJtZWJPZ0cLKHbinrKYCns73JTqvfnwTjB4Ok652KtxSH1E75OUmwhC0ewKtA7Um0f6/WK2nNhattOxxuYwSGg8ikAEair/c07+NMxwPiKeohFD5nAAJdA7IehM47EpfjBJgOU5ipEerKs4dg63Pa1SuaqHWmPKMxLZveziYPHy4gdcmCRoQzT0p3WJiUaP+acSaifv+LOCcibVj+bD10cCQEV0IluOd3fxIJWvn0oCTIMsASazv6easttw3MO8JMvW9VFcs9/z6XwsGQKfQS0u6dAfMmo3LpNIJDI0Ji3FLcORfAcjZCR6JKQBwXrXpZuWvkxVltZymd2cc0EtILtMFRXq/+38jlp9ezdjU7TYRxOKWv2ThyMg1JxIsCAW/NOIc4eiEw/j4J4OjHGWAiRWgP9z1v/+Ir0g2jHKUZs84oUVrmGsee6K7ahyIUFMaASatMezLQfXGXo0BWovcuSFY5x3eldcNF6GsaEHWGNiQ3ZnxJZW2c1KUzswzAeiHRtJq2ayPHbu0yaD0+r4DGF4ZEF8PsndG9cTFpTNaKb+nRqgIHviM0RBmnaNamFtArWbmXfqhqnWtmlinXioIw5M6Bw4GQS1Jj5t/fPks+GgkqUR6JjKrh7N4iK/mumZ71LMuccxdEIN4027khTYkCSyPy5545969ieRIiROF8oW30ef4QwPHPEl1n0Q0mot8fjLphC0WKdYG63ZIn8tImu0TFYVMAtDNxOIDsc24p85ctrhV/ihvUyOzwpcRWS7Grd8Y/mNQWL0+7M0UWvj50oprIWPIagRkLthazNPauZFt4G95O6nEbcn2Hp5W3Sa2Q8mSFE5OfopK4KKHL+YJa7wpiOmbQFTu63/LYdTKWLkxHipJqEHxVWl671g+2tWpVk+r0b/AEswnt2qNgi8ISSmryQxm2tqEP17aukvspMHaUXF8P9mb36z2Voy3bhx1r0w6LnqZdO0soENS2ScnDiVgcku2p5uf9hk8LLqNm9eNMTcOW79aNJdYznI1aXohRkvSLHft4vNCJTt1Z1qFtU1LejlE7mqgqCr+xPhAxnhk/27W5o5AU3fAxqK8qoHrec0pYLN5c1bWgAHz4BuLquNaReVreJgoVU/U4sWJEWVSzQ81V01a7m9hsMPtoj71Y7QBiVo1sz4/fe9bqxmP1tAiFxw5TNsx+U/jjtlKq7cbHmG6rHvjZSiopWeYLxhqLSGaxM+15nubRmjrWvDOlCFc2Nv4RlfffVEWHtLK26bErDs3SDL4H2vPbWJXLA1dEPtwivBg0j180dXlD/t3E4m+vq+Fdu7mtnvtruhquL7t914xfXvqw0eD5qbeAUB06Lsj+ZcPmJl6FTXsdmmTn2S8xM49MqCPZP/4YroJvYvS+vx297/xNCDHupx5NvRDHWYoAD/Gpt0Ngng8fhdW/TiRKzM0tGnHV/fbP9nR50+DVsnB5NQYf/AYduZx9s7neXJvvOMzDfrMMuHVT6WW48c1nX9WbU/eu/6wsN/9RaMegbQNRGIAD3rs1mzEEQuni85DzcQ8UboqlQAU36AKNvAQdFKROPU5OISItCJ8mQReZjB6qwRhCnMGGjqWg2EuhS4aWli5poVOXdmov+8HB/44b/vfdF7JdMUCwO57sox7yrIR3jlftaw9RghwOjJD+NrY1NCEO9yzRpsjump4Tzof48PKrmK8pdxjiBy4iCHOECQGHO6hDXWKbE6AYX23d2nR3cLvj2ND3Duw/aaeIV/aB9VqODaxzPOwOPOrMPg9dz2EeHyCMEOUcgTcbtA+BUqu9MfxSK44xZx4C/swBLxEM3f2cyxPmMfchfkCpCwJg5jElkylT4wBkpES0JgBs2wULmXESvBcCtknCRA2sFt0RBiGvsai/XAlRCxGyugimgZCLwLtUWSFnozYhgQhPLfALmNqyM8f8bjRcjQfYoXRGi997B30LP1e7NoOdHQzHHCCBwkuSboALzEQhlUpYOl30AB6DhO9ZasHwIlzgwDK4yPwN83kc5mku8/vFU34x/vhJnNSTONfCMXGKrFubkE5m9CZt9dq+K0QsbpWJUpP6j1jvwuI2nVvi9jY3kZSZNkYfNc3r2JRTZPyJ0bnW2Tq8P/cOKdNksZZ+cl5vMZaMChAJ0GAligWlUBTiz2UoI2DR/qy9YErJVNl7QrePw71edyP9SPS1bjbAoJU3aXb6VMXr0v33kh5Vr14cRcsgPa/KalneRBpd/Kia+k1VNqapmvjsxm+5u7IEtp7q5aaaf/gWbs58XT7R5Wb5n0IzeGkbiuO4sL9gf8DYLrvsoLtsssKgtzlKQgZp30spJA2VIREe8UnUw2JfoaP2YIjCS5fhwRWUKSKIkAc/KBvbYQm9y/IuMqwsp4LXXVa/fC+f6+f8CTz1wauy41VLTMw4ihzqdGMnQIyNXzyLYy29Qq14zFZfqrS0g8qI2ddbJlIorZhHvjZ33GosHp8tq5ePZzmZV7vZrPl+dfm8d+r4O93dvzNXyPcc1UMB+nXu9c4vNjSvI3AlYiyAjw9bSreKgX4Tg/lFtbpg6X1S+WTtsdxlnGEOLEmF0jYCFgFkUKjLUDS/3ihCufrBYEApRameUaaIDGOAVEbf36M0Ol3jVGkUXgDE0LmuN5A+WHobG3WTKQZKi3Lp3akRdX7zbY21623DNIX+9LU2h7TdM6R27K0Y+RpCTtw79uOu3/CcruPN4kzVo4h6AoQQiAX4SNEFp1jHGBcG8Az23MNwqHwYPl8vLz5ScjdJdBfvJSEPCbEk7xsrK0fI2tcXVnMZtfJkmn+B+gW3WSYhW+WpiwuJFdIkwgUJMgMggbyT0okw4O0NgzEVGzV9tPTvkLvM7LPmLr4rtNve0OC83j7Rjf0D0l/XSOXPwdyg2bE1xFJ7cPsmbqK007FZcyBUVR1TauCICsEU7GFQgGFgwPEoaSswtELX6jrJdHJ/Ho78n5+vN0ckBIuOWySZjJLEdUNukfudOaxsy3x6N5F5Vs5DfFmoSyUpbWATiSA0gOlklMM0z6WU2XQqZQTz83Efk50nD2qcmNxEger2T9Z0Bv8pOJdXJ2IojN+/wp1LcaXehQ90IW58Mc5lhHibGSuT28eiqMTcFlGwnVofcGehHAdy2wvFR0FhXAhuIhQiF0XsKAjixkRFhSy6MTIgLkQwcrJMOOfj+y3znfWrXBEk79KQuV/GnTMDEUo5zVb6C/FqsBIF0ZuV3mqw0fZr7WHU3Pn20uKDYNa+7VK0GPuhP8aAkSD0U+8ZYrI1wZKmlDVO7dVGG6sscTvd7l77MdyzBK2w3S4NMWe5ZEC4hTAycH8K3l9e6EfzQum2MUqqG6+R0MqAVdAmRKSQzh9oo4ydam3mVk2L3oXxhG4sLjcfUSq0MW9PE+gdOZi2mLaJZITVWUbT1oHDbKmiGEAdL6ziOB4+avdXveEArUa1Sj/IVpZePh/EYZtWd3bikIWo0iOspETcZ6kEwmRp7gmZoi9fCluW1pQgxC/Oy/B7WNbHnapTKPfsRMCNLkszrQIlYb+mvhTEfmOm0VDKzeaUuJoyBSAJosTvHZgVF7U6e9vqeTGbKz1rttC5S3++38HPpLbWQEbo4w8UJdxaFokyTeux5+UtIhQrBG+NFrD/sJc898N+P/Zq8cCvHep7OCCI+JTxn28wplcjFzfaD0IA6gxCoYUp35eFGOGcKSN+/SqNKIVVDivB0l+is3TMGmV//5SVinWvjB5xQ3edP3oyUOvGclNe4Qq4MO5Wqbk1EmoF5qSMmsq5bIq5M8fodTXdu6sdhl+v96EPp24QIBJzVpvUuVaCswxEP5PVftQNUy5cgcILWZbciZJOPMnHpxMSeb7vhd3cy+IKI1kYc58w/+nTM02HV0pnEE6GFevGl4SLVjmhihvr3JnZEhuSSt6apDfvKqFgrTi2vAiu7dy1ATh04NJhEKTkibJzUBaeWUypTou4DtWjyyHl7gTKESjs+uIBxbVWG6/GzOcxUHKKcIRCkFSi8l6A5ZSDZN5y1kXpKBsPPM45cY0WkofdO4l3N44bZJzkeZ4Bo8/8hGyucEZwKrFDs3vhW5qylMgySJtrJ+2UrYtbRMp7CIuR3ZxaAcLMFMWtCXszpoRoHOYoeJ1f1fv3XxbR1VLW3r3otroAioExYl5M6pSUGmq79qzAvrVTCDCi8a0UpDKd7S4WozGQ/3SG6FmSOUaqg5j3CaU+1Bb3DIeTpTfdxpMc30ouR9/Wqg7botOjC9jzcOJ5YWc8TmY5u5UkXm1jx5YDgXM+RC2nANULOkkZYyXNgX77HFipFBA0OFmBeliW2vFstWZso0FiT8pJ8/ih0L8Ileep7B1qbqCHseUTlo/waJgDaxHTX9w3brVHdRZvOxf12OYJBISzSmeQ0vCq2XqoUV2+4wMDxGg6AY7qlXAXTRFHPvW9ysdNn2XJZj/7snr5+cpq5jiSpH+w2okWkmwzy8c4aGCMs8S7leT+2nJzbXct8IJxnCZLl2pBF9GQXvQiChQxvv6vgusLjZyIwwXfffRNEKSP2nuwCgeWvhxaQ5YR0k4yt5pMun1YVJadLGsDNpkN6rWLRDbBzR+s/xayuGXZoxTaGtkYPNQkHD54sHqn5cp5x62iXk9FFxEcB0IgEPJ9v+/3fRPCTIDGiiZJFjHGQltU7me3O4zNbXqSdqpM/bb9JY0lTpGMWpd+/bXQc9Uxs2e9vbXl1Gt1dq/11ef1dq3dbleI6HDjdSDXa4LCVkfUavWGAlbOzd6sd9c7XN3stddhrcYeI3C6C8ozUG1/4IpcKDvpILC50+OlCwlCkb1uXf3rGpmzI4czdZMzBa7HWXinzyH91hff9RGVAexxIFV3Bl3FdLsSbbZyl5eOapWQZ8wl4bV1SWHodz/IWZdVbx+2uQ/G/XmBpWSn12GO7imCOF4XX5PPyNrO+jrbqrHefvfP++/cr9bGprI77n249vCjK1yn+mH7z+qhsjnPZL3/drsqffQHK2uDXmG//KsJ1arBbS9aPRU5lDOpG5gN1+XWgyhw0snory9dO1DTwennpxNnLtrDSJ7pJjBlXcfkblmkluUQmYbnXnkws1rm52WO5zDPt0xNFqSWYimovvGSyAsirx7xzaOuUm3XO+H0tvHAg74SnAhKW1HqgtI75Nrv8+3d7jrGld/71ferjz+g9u4r7Wq115H61drWh7Ecqut15b4kK/XO+ANp3BHrXI1jJWj3NvpwfNhWqmVBCbae3FzmF9QydcyJOeDuyo7A1ufraZIG9xLbdAZMJzEtnLnLBxd1mdDIdomlWyTiZUBNIMtyv7/2JJBkBDRZFAYh7/I/HdUFPuz0xsKJDFpua9Dl+5s7LCSlzuG40172/QXKDWpRq32oCPXGjqrQ861DwQ16W2+6Th2vNOaFcU8ZM+uz3FNqHdcTmS2Udl2oW4AVoIYoL9ChzHfbbXVtQ9hVXt61BorY6n/xmQgG2t4Om1YinacDE1OuiBJ7NkiiID0N7MBMgv7cdz8MbVu3CUGICYN1lRER1YEMbr7+r+/KBhJ5QFvQ5XnZUbeZHLuK4vJKrSWO6/Ue+IUfj69XpmPro/rq6rOLrimIZi4cXb/+6vm1c6LColVSFOl/DA314MfNrjkQ+M6YTVkrT4SKLElvC6JWi0UTh7HQls295VYrjmVxF1N8xEsSN+DMPUAtLY4lEV3CCGPBlGOWTDoeJOxjIg5Sx3FJgJOonsyd6bq+T4bPDKOIENZuhaqrKlIRBphS0wBUlOQBV7P6GyqaRIPW0UAUhJa488K5vltr3cBeR6gsvjw9vN258otMxUiTFMFjINVvPslEioDH55rUtCj9kWfzhiKKvNgtUbiwsecj2IJvAwQB3zBCcDSNJV8tQcTHJcD8qkmMyKYJpFSCvBZrGhU5AlRXrok3ujEV1QAvFWdGioijnwZRYgYfzkWzVCfq6PL3NkkI0QsCKhdfwcXMNGWJWAS5nDzid52lg+UFKrh1TnFWkczRazcvoS6Mp3Y/jxsNj70h1gTRVcCN7g02PC9TcyjIWoUXNWSVLAiscPvIy6exJvBeCcb451PwBhBkWOI1HxmkKPFZDn6CoNJohte8vSceL1FL5mwOSfu52KLTloU4Sqhcz7vT7IZY4QkeFn+/Z0e6HbEkINFpEszZyI7OEnJRZ37S5WiIU0KWVB3pkTxIOJ4gDm1xbXr31rG8qwxMkQoR5gccN1JFWrr+W/X6DbGXd/Ju3KWSeSQwuLE3jT1PqNVkf2cDrS0+VTappyEQZi0Nxpq3w85arjUzCGDDYNgtbPhwozEVvX5/ni3QOj+tPN0YYQQsxEuiyFoeUc2CIsNPeLOUr+yUBE9eOp4VP870KAlse8JxabB1ZW4SpJc/jWzCGNpn9tnBkhV9yxHdWCtsGuGrBxc2TQe75ikwTU7mebWiNRg/C9GuZHmemMX5NBdzbapJGuLLDz++PRWnWR5Pp3ILyIJHywbhW9CDfkPGCB55R9lAg9NVDzZLKPOHBipBuPj0UznKs1Kmk0dG2H82y/755lcQYgsxvs0mOxC1BAvJoZNneT48ARR0+3/rRLfTKIpYqulOEAR47l7i3Dt2gjRhYiX7+pd3iK3LuXM8fBoTZ3L8+6VFHOmENRZT5f8Q4FoBtVSjv5dNY0bEy2Mtg3HmASRr9Fy//Gyc56vz86/G8G2ILWAwONCCOfCNSrjwrFqyVktQ00oZG82s6VdKy7l2UbehV9nZLq1WHnxsCa/CZuaHFjKsAiyxVSNYU1F5FPqQ6CNUQjzEnkU2+/sE2aM9wuJrdCkdFbOEzCWRfuLYqW2naWQnepGmXGqo4t1btulGZHLz7sAw9UJ3bY7KhWnJJjZHKvKX+0vadBqSnPeYFLzh+yCE3P5bn8A8Lz9p7D2UTzVclCy1XwGqgVXf3zGebzy/Z1QmWehtl4A39bxwyQ9DMwzVSQjLC+UVWAx37r34cebBnIRnFO2TVba/7FELqwRhxBOCMSUZs/UIZi/cSvVZNLppF/bVTy/tpbbjTObSK1ec5HQySdKkSKIT3Q6Sy8NnQMoxekk0i8jMvvrchcgsdM4HIpCE1YULTxOkf9vPc8/zch6p8Pwa0nmAMdZxplXw5oWDZ57MyhD7TQvrFgiN+QfmN2xj7aCxFkJYXg7zrNnMKuESrsDtclYqQcsy1C8T4IdGqlsazIyVPoFEtV45vjO7rKqqBQApdDLUkYp0Uz0dkHvpjOXwd6z+9vFwOGO9Ngnm9DuTJAgmk0makH/vJcG/SXT28U8osZmH2CWbI9HFswv7eGSbDpIpr44K46AAgMh5nlVefnUKyVLjhb5OES4BQPM8LIV3Xr9jhANwfrGxg5vIgMsbfmVVLamzn5EKrH8QZC3nZZWsSFUPwrBpwX7D9ytZVjZUXLE2QO79rn9fNHy0N/r00/M6I8KA2zqxi81RQTYvD2c2QhG5eBYVySQtrv11EtjO6eQ/S6XNZUrl9NoAAAAASUVORK5CYII=\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Rainfall card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"mm\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "rain", + "precipitation", + "downpour", + "rain shower", + "drizzle", + "raindrop", + "cloudburst", + "rainwater" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/rainfall_chart_card.json b/application/src/main/data/json/system/widget_types/rainfall_chart_card.json new file mode 100644 index 0000000000..ebbc1e8cc5 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/rainfall_chart_card.json @@ -0,0 +1,38 @@ +{ + "fqn": "rainfall_chart_card", + "name": "Rainfall chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a rainfall data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'rainfall', label: 'Rainfall', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'mm', decimals: 1 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'rainfall', 'mm', 1);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Rainfall\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 8) {\\n\\tvalue = 8;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"mm\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#7191EF\"},{\"from\":0,\"to\":2.5,\"color\":\"#4B70DD\"},{\"from\":2.5,\"to\":7.6,\"color\":\"#305AD7\"},{\"from\":7.6,\"to\":null,\"color\":\"#234CC7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 8) {\\n\\tvalue = 8;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"mm\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 2 - 1;\\nif (value < -3) {\\n\\tvalue = -3;\\n} else if (value > 3) {\\n\\tvalue = 3;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"mm\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Rainfall\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:weather-pouring\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "rain", + "precipitation", + "downpour", + "rain shower", + "drizzle", + "raindrop", + "cloudburst", + "rainwater" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/rainfall_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/rainfall_chart_card_with_background.json new file mode 100644 index 0000000000..272f96da91 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/rainfall_chart_card_with_background.json @@ -0,0 +1,38 @@ +{ + "fqn": "rainfall_chart_card_with_background", + "name": "Rainfall chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a rainfall data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'rainfall', label: 'Rainfall', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'mm', decimals: 1 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'rainfall', 'mm', 1);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Rainfall\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 8) {\\n\\tvalue = 8;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"mm\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#6083EC\"},{\"from\":0,\"to\":2.5,\"color\":\"#4369DD\"},{\"from\":2.5,\"to\":7.6,\"color\":\"#2B54CE\"},{\"from\":7.6,\"to\":null,\"color\":\"#224AC2\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 8) {\\n\\tvalue = 8;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"mm\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 2 - 1;\\nif (value < -3) {\\n\\tvalue = -3;\\n} else if (value > 3) {\\n\\tvalue = 3;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"mm\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Rainfall\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:weather-pouring\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "rain", + "precipitation", + "downpour", + "rain shower", + "drizzle", + "raindrop", + "cloudburst", + "rainwater" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/range_chart.json b/application/src/main/data/json/system/widget_types/range_chart.json new file mode 100644 index 0000000000..981e3582c5 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/range_chart.json @@ -0,0 +1,31 @@ +{ + "fqn": "range_chart", + "name": "Range chart", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays changes to time-series data over time visualized with color ranges — for example, temperature or humidity readings.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.rangeChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.rangeChartWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: false,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries' }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-range-chart-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-range-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":0,\"realtime\":{\"realtimeType\":0,\"timewindowMs\":60000,\"quickInterval\":\"CURRENT_DAY\",\"interval\":1000},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"dataZoom\":true,\"rangeColors\":[{\"to\":-20,\"color\":\"#234CC7\"},{\"from\":-20,\"to\":0,\"color\":\"#305AD7\"},{\"from\":0,\"to\":10,\"color\":\"#7191EF\"},{\"from\":10,\"to\":20,\"color\":\"#FFA600\"},{\"from\":20,\"to\":30,\"color\":\"#F36900\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"color\":\"#D81838\"}],\"outOfRangeColor\":\"#ccc\",\"fillArea\":true,\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm\",\"lastUpdateAgo\":false,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Range chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"°C\",\"decimals\":0,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "range", + "color range", + "line chart" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/raspberry_pi_gpio_control.json b/application/src/main/data/json/system/widget_types/raspberry_pi_gpio_control.json new file mode 100644 index 0000000000..b2e03f19a7 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/raspberry_pi_gpio_control.json @@ -0,0 +1,35 @@ +{ + "fqn": "gpio_widgets.raspberry_pi_gpio_control", + "name": "Raspberry Pi GPIO Control", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Allows to display the state of the GPIO for the target Raspberry Pi device using the latest attribute values. You should set the label of the selected data key to the GPIO PIN (e.g., '1') and use boolean values for the widget to display the data.", + "descriptor": { + "type": "rpc", + "sizeX": 6, + "sizeY": 10.5, + "resources": [], + "templateHtml": "
\n
\n
\n
\n {{ cell.label }}\n
\n {{cell.pin}}\n \n \n \n \n {{cell.pin}}\n
\n {{ cell.label }}\n
\n
\n \n \n \n
\n
\n
\n {{rpcErrorText}}\n \n
", + "templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.gpio-panel section[fxflex] {\n min-width: 0px;\n}\n\n.switch-panel {\n margin: 0;\n height: 32px;\n width: 66px;\n min-width: 66px;\n}\n\n.switch-panel mat-slide-toggle {\n margin: 0;\n width: 36px;\n min-width: 36px;\n}\n\n.switch-panel.col-0 mat-slide-toggle {\n margin-left: 8px;\n margin-right: 4px;\n}\n\n.switch-panel.col-1 mat-slide-toggle {\n margin-left: 4px;\n margin-right: 8px;\n}\n\n.gpio-row {\n height: 32px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.switch-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.switch-panel.col-1 .pin {\n margin-right: auto;\n \n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}", + "controllerScript": "var namespace;\nvar cssParser = new cssjs();\n\nself.onInit = function() {\n var utils = self.ctx.$injector.get(self.ctx.servicesMap.get('utils'));\n namespace = 'gpio-control-' + utils.guid();\n cssParser.testMode = false;\n cssParser.cssPreviewNamespace = namespace;\n self.ctx.$container.addClass(namespace);\n self.ctx.ngZone.run(function() {\n init(); \n });\n}\n\nfunction init() {\n \n var i, gpio;\n var scope = self.ctx.$scope;\n var settings = self.ctx.settings;\n scope.gpioList = [];\n for (var g = 0; g < settings.gpioList.length; g++) {\n gpio = settings.gpioList[g];\n scope.gpioList.push(\n {\n row: gpio.row,\n col: gpio.col,\n pin: gpio.pin,\n label: gpio.label,\n enabled: false\n }\n );\n }\n\n scope.requestTimeout = settings.requestTimeout || 1000;\n\n scope.switchPanelBackgroundColor = settings.switchPanelBackgroundColor || tinycolor('green').lighten(2).toRgbString();\n\n scope.gpioStatusRequest = {\n method: \"getGpioStatus\",\n paramsBody: \"{}\"\n };\n \n if (settings.gpioStatusRequest) {\n scope.gpioStatusRequest.method = settings.gpioStatusRequest.method || scope.gpioStatusRequest.method;\n scope.gpioStatusRequest.paramsBody = settings.gpioStatusRequest.paramsBody || scope.gpioStatusRequest.paramsBody;\n }\n \n scope.gpioStatusChangeRequest = {\n method: \"setGpioStatus\",\n paramsBody: \"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"\n };\n \n if (settings.gpioStatusChangeRequest) {\n scope.gpioStatusChangeRequest.method = settings.gpioStatusChangeRequest.method || scope.gpioStatusChangeRequest.method;\n scope.gpioStatusChangeRequest.paramsBody = settings.gpioStatusChangeRequest.paramsBody || scope.gpioStatusChangeRequest.paramsBody;\n }\n \n scope.parseGpioStatusFunction = \"return body[pin] === true;\";\n \n if (settings.parseGpioStatusFunction && settings.parseGpioStatusFunction.length > 0) {\n scope.parseGpioStatusFunction = settings.parseGpioStatusFunction;\n }\n \n scope.parseGpioStatusFunction = new Function(\"body, pin\", scope.parseGpioStatusFunction);\n \n function requestGpioStatus() {\n self.ctx.controlApi.sendTwoWayCommand(scope.gpioStatusRequest.method, \n scope.gpioStatusRequest.paramsBody, \n scope.requestTimeout)\n .subscribe(\n function success(responseBody) {\n for (var g = 0; g < scope.gpioList.length; g++) {\n var gpio = scope.gpioList[g];\n var enabled = scope.parseGpioStatusFunction.apply(this, [responseBody, gpio.pin]);\n gpio.enabled = enabled; \n self.ctx.detectChanges();\n }\n }\n );\n }\n \n function changeGpioStatus(gpio) {\n var pin = gpio.pin + '';\n var enabled = !gpio.enabled;\n enabled = enabled === true ? 'true' : 'false';\n var paramsBody = scope.gpioStatusChangeRequest.paramsBody;\n var requestBody = JSON.parse(paramsBody.replace(\"\\\"{$pin}\\\"\", pin).replace(\"\\\"{$enabled}\\\"\", enabled));\n self.ctx.controlApi.sendTwoWayCommand(scope.gpioStatusChangeRequest.method, \n requestBody, scope.requestTimeout)\n .subscribe(\n function success(responseBody) {\n var enabled = scope.parseGpioStatusFunction.apply(this, [responseBody, gpio.pin]);\n gpio.enabled = enabled;\n self.ctx.detectChanges();\n }\n );\n }\n \n scope.gpioCells = {};\n var rowCount = 0;\n for (i = 0; i < scope.gpioList.length; i++) {\n gpio = scope.gpioList[i];\n scope.gpioCells[gpio.row+'_'+gpio.col] = gpio;\n rowCount = Math.max(rowCount, gpio.row+1);\n }\n \n scope.prefferedRowHeight = 32;\n scope.rows = [];\n for (i = 0; i < rowCount; i++) {\n var row = [];\n for (var c =0; c<2;c++) {\n if (scope.gpioCells[i+'_'+c]) {\n row[c] = scope.gpioCells[i+'_'+c];\n } else {\n row[c] = null;\n }\n }\n scope.rows.push(row);\n }\n\n scope.gpioClick = function($event, gpio) {\n if (scope.rpcEnabled && !scope.executingRpcRequest) {\n changeGpioStatus(gpio);\n }\n };\n \n scope.gpioToggleChange = function($event, gpio) {\n gpio.enabled = !$event.checked;\n $event.source.toggle();\n self.ctx.detectChanges();\n }\n \n if (scope.rpcEnabled) {\n requestGpioStatus(); \n }\n \n self.onResize();\n}\n\nself.onResize = function() {\n var scope = self.ctx.$scope;\n var rowCount = scope.rows.length;\n var prefferedRowHeight = (self.ctx.height - 35)/rowCount;\n prefferedRowHeight = Math.min(32, prefferedRowHeight);\n prefferedRowHeight = Math.max(12, prefferedRowHeight);\n scope.prefferedRowHeight = prefferedRowHeight;\n var ratio = prefferedRowHeight/32;\n \n var css = '.mat-slide-toggle .mat-slide-toggle-bar {\\n' +\n ' height: ' + 14*ratio+'px;\\n'+\n ' width: ' + 36*ratio+'px;\\n'+\n '}\\n';\n css += '.mat-slide-toggle .mat-slide-toggle-thumb-container {\\n' +\n ' height: ' + 20*ratio+'px;\\n'+\n ' width: ' + 20*ratio+'px;\\n'+\n '}\\n';\n css += '.mat-slide-toggle .mat-slide-toggle-thumb {\\n' +\n ' height: ' + 20*ratio+'px;\\n'+\n ' width: ' + 20*ratio+'px;\\n'+\n '}\\n';\n css += '.mat-slide-toggle .mat-slide-toggle-ripple {\\n' +\n ' height: ' + 40*ratio+'px;\\n'+\n ' width: ' + 40*ratio+'px;\\n'+\n ' top: calc(50% - '+20*ratio+'px);\\n'+\n ' left: calc(50% - '+20*ratio+'px);\\n'+\n '}\\n';\n css += '.gpio-left-label, .gpio-right-label {\\n' +\n ' font-size: ' + 16*ratio+'px;\\n'+\n '}\\n';\n var pinsFontSize = Math.max(9, 12*ratio);\n css += '.pin {\\n' +\n ' font-size: ' + pinsFontSize+'px;\\n'+\n '}\\n';\n\n cssParser.createStyleElement(namespace, css);\n \n self.ctx.detectChanges();\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-gpio-control-widget-settings", + "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"parseGpioStatusFunction\":\"return body[pin] === true;\",\"gpioStatusChangeRequest\":{\"method\":\"setGpioStatus\",\"paramsBody\":\"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"},\"requestTimeout\":500,\"switchPanelBackgroundColor\":\"#008a00\",\"gpioStatusRequest\":{\"method\":\"getGpioStatus\",\"paramsBody\":\"{}\"},\"gpioList\":[{\"pin\":7,\"label\":\"GPIO 4 (GPCLK0)\",\"row\":3,\"col\":0,\"_uniqueKey\":0},{\"pin\":11,\"label\":\"GPIO 17\",\"row\":5,\"col\":0,\"_uniqueKey\":1},{\"pin\":12,\"label\":\"GPIO 18\",\"row\":5,\"col\":1,\"_uniqueKey\":2},{\"_uniqueKey\":3,\"pin\":13,\"label\":\"GPIO 27\",\"row\":6,\"col\":0},{\"_uniqueKey\":4,\"pin\":15,\"label\":\"GPIO 22\",\"row\":7,\"col\":0},{\"_uniqueKey\":5,\"pin\":16,\"label\":\"GPIO 23\",\"row\":7,\"col\":1},{\"_uniqueKey\":6,\"pin\":18,\"label\":\"GPIO 24\",\"row\":8,\"col\":1},{\"_uniqueKey\":7,\"pin\":22,\"label\":\"GPIO 25\",\"row\":10,\"col\":1},{\"_uniqueKey\":8,\"pin\":29,\"label\":\"GPIO 5\",\"row\":14,\"col\":0},{\"_uniqueKey\":9,\"pin\":31,\"label\":\"GPIO 6\",\"row\":15,\"col\":0},{\"_uniqueKey\":10,\"pin\":32,\"label\":\"GPIO 12\",\"row\":15,\"col\":1},{\"_uniqueKey\":11,\"pin\":33,\"label\":\"GPIO 13\",\"row\":16,\"col\":0},{\"_uniqueKey\":12,\"pin\":35,\"label\":\"GPIO 19\",\"row\":17,\"col\":0},{\"_uniqueKey\":13,\"pin\":36,\"label\":\"GPIO 16\",\"row\":17,\"col\":1},{\"_uniqueKey\":14,\"pin\":37,\"label\":\"GPIO 26\",\"row\":18,\"col\":0},{\"_uniqueKey\":15,\"pin\":38,\"label\":\"GPIO 20\",\"row\":18,\"col\":1},{\"_uniqueKey\":16,\"pin\":40,\"label\":\"GPIO 21\",\"row\":19,\"col\":1}]},\"title\":\"Raspberry Pi GPIO Control\"}" + }, + "externalId": null, + "tags": [ + "pin", + "pins", + "board", + "circuit", + "digital read", + "digital write", + "analog read", + "analog write", + "microcontroller", + "i/o", + "input/output", + "hardware" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/raspberry_pi_gpio_panel.json b/application/src/main/data/json/system/widget_types/raspberry_pi_gpio_panel.json new file mode 100644 index 0000000000..1bbe2482f2 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/raspberry_pi_gpio_panel.json @@ -0,0 +1,21 @@ +{ + "fqn": "gpio_widgets.raspberry_pi_gpio_panel", + "name": "Raspberry Pi GPIO Panel", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Allows to change the state of the GPIO for Raspberry Pi devices using RPC commands. Requires handling of the RPC commands in the device firmware. Uses 'getGpioStatus' and 'setGpioStatus' RPC calls", + "descriptor": { + "type": "latest", + "sizeX": 7, + "sizeY": 10.5, + "resources": [], + "templateHtml": "
\n
\n
\n
\n {{ cell.label }}\n
\n {{cell.pin}}\n \n \n \n \n {{cell.pin}}\n
\n {{ cell.label }}\n
\n
\n \n \n \n
\n
\n
\n
", + "templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.gpio-panel section[fxflex] {\n min-width: 0px;\n}\n\n.gpio-panel tb-led-light > div {\n margin: auto;\n}\n\n.led-panel {\n margin: 0;\n width: 66px;\n min-width: 66px;\n}\n\n.led-container {\n width: 48px;\n min-width: 48px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.led-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.led-panel.col-1 .pin {\n margin-right: auto;\n \n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}", + "controllerScript": "var namespace;\nvar cssParser = new cssjs();\n\nself.onInit = function() {\n var utils = self.ctx.$injector.get(self.ctx.servicesMap.get('utils'));\n namespace = 'gpio-panel-' + utils.guid();\n cssParser.testMode = false;\n cssParser.cssPreviewNamespace = namespace;\n self.ctx.$container.addClass(namespace);\n self.ctx.ngZone.run(function() {\n init(); \n });\n}\n\nfunction init() {\n var i, gpio;\n \n var scope = self.ctx.$scope;\n var settings = self.ctx.settings;\n \n scope.gpioList = [];\n scope.gpioByPin = {};\n for (var g = 0; g < settings.gpioList.length; g++) {\n gpio = settings.gpioList[g];\n scope.gpioList.push(\n {\n row: gpio.row,\n col: gpio.col,\n pin: gpio.pin,\n label: gpio.label,\n enabled: false,\n colorOn: tinycolor(gpio.color).lighten(20).toHexString(),\n colorOff: tinycolor(gpio.color).darken().toHexString()\n }\n );\n scope.gpioByPin[gpio.pin] = scope.gpioList[scope.gpioList.length-1];\n }\n\n scope.ledPanelBackgroundColor = settings.ledPanelBackgroundColor || tinycolor('green').lighten(2).toRgbString();\n\n scope.gpioCells = {};\n var rowCount = 0;\n for (i = 0; i < scope.gpioList.length; i++) {\n gpio = scope.gpioList[i];\n scope.gpioCells[gpio.row+'_'+gpio.col] = gpio;\n rowCount = Math.max(rowCount, gpio.row+1);\n }\n \n scope.prefferedRowHeight = 32;\n scope.rows = [];\n for (i = 0; i < rowCount; i++) {\n var row = [];\n for (var c =0; c<2;c++) {\n if (scope.gpioCells[i+'_'+c]) {\n row[c] = scope.gpioCells[i+'_'+c];\n } else {\n row[c] = null;\n }\n }\n scope.rows.push(row);\n } \n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n var changed = false;\n for (var d = 0; d < self.ctx.data.length; d++) {\n var cellData = self.ctx.data[d];\n var dataKey = cellData.dataKey;\n var gpio = self.ctx.$scope.gpioByPin[dataKey.label];\n if (gpio) {\n var enabled = false;\n if (cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length - 1];\n enabled = (tvPair[1] === true || tvPair[1] === 'true');\n }\n if (gpio.enabled != enabled) {\n changed = true;\n gpio.enabled = enabled;\n }\n }\n }\n if (changed) {\n self.ctx.detectChanges();\n } \n}\n\nself.onResize = function() {\n var rowCount = self.ctx.$scope.rows.length;\n var prefferedRowHeight = (self.ctx.height - 35)/rowCount;\n prefferedRowHeight = Math.min(32, prefferedRowHeight);\n prefferedRowHeight = Math.max(12, prefferedRowHeight);\n self.ctx.$scope.prefferedRowHeight = prefferedRowHeight;\n \n var ratio = prefferedRowHeight/32;\n \n var css = '.gpio-left-label, .gpio-right-label {\\n' +\n ' font-size: ' + 16*ratio+'px;\\n'+\n '}\\n';\n var pinsFontSize = Math.max(9, 12*ratio);\n css += '.pin {\\n' +\n ' font-size: ' + pinsFontSize+'px;\\n'+\n '}\\n';\n \n cssParser.createStyleElement(namespace, css); \n \n self.ctx.detectChanges();\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-gpio-panel-widget-settings", + "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"gpioList\":[{\"pin\":1,\"label\":\"3.3V\",\"row\":0,\"col\":0,\"color\":\"#fc9700\",\"_uniqueKey\":0},{\"pin\":2,\"label\":\"5V\",\"row\":0,\"col\":1,\"color\":\"#fb0000\",\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 2 (I2C1_SDA)\",\"row\":1,\"col\":0,\"color\":\"#02fefb\",\"_uniqueKey\":2},{\"color\":\"#fb0000\",\"pin\":4,\"label\":\"5V\",\"row\":1,\"col\":1},{\"color\":\"#02fefb\",\"pin\":5,\"label\":\"GPIO 3 (I2C1_SCL)\",\"row\":2,\"col\":0},{\"color\":\"#000000\",\"pin\":6,\"label\":\"GND\",\"row\":2,\"col\":1},{\"color\":\"#00fd00\",\"pin\":7,\"label\":\"GPIO 4 (GPCLK0)\",\"row\":3,\"col\":0},{\"color\":\"#fdfb00\",\"pin\":8,\"label\":\"GPIO 14 (UART_TXD)\",\"row\":3,\"col\":1},{\"color\":\"#000000\",\"pin\":9,\"label\":\"GND\",\"row\":4,\"col\":0},{\"color\":\"#fdfb00\",\"pin\":10,\"label\":\"GPIO 15 (UART_RXD)\",\"row\":4,\"col\":1},{\"color\":\"#00fd00\",\"pin\":11,\"label\":\"GPIO 17\",\"row\":5,\"col\":0},{\"color\":\"#00fd00\",\"pin\":12,\"label\":\"GPIO 18\",\"row\":5,\"col\":1},{\"color\":\"#00fd00\",\"pin\":13,\"label\":\"GPIO 27\",\"row\":6,\"col\":0},{\"color\":\"#000000\",\"pin\":14,\"label\":\"GND\",\"row\":6,\"col\":1},{\"color\":\"#00fd00\",\"pin\":15,\"label\":\"GPIO 22\",\"row\":7,\"col\":0},{\"color\":\"#00fd00\",\"pin\":16,\"label\":\"GPIO 23\",\"row\":7,\"col\":1},{\"color\":\"#fc9700\",\"pin\":17,\"label\":\"3.3V\",\"row\":8,\"col\":0},{\"color\":\"#00fd00\",\"pin\":18,\"label\":\"GPIO 24\",\"row\":8,\"col\":1},{\"color\":\"#fd01fd\",\"pin\":19,\"label\":\"GPIO 10 (SPI_MOSI)\",\"row\":9,\"col\":0},{\"color\":\"#000000\",\"pin\":20,\"label\":\"GND\",\"row\":9,\"col\":1},{\"color\":\"#fd01fd\",\"pin\":21,\"label\":\"GPIO 9 (SPI_MISO)\",\"row\":10,\"col\":0},{\"color\":\"#00fd00\",\"pin\":22,\"label\":\"GPIO 25\",\"row\":10,\"col\":1},{\"color\":\"#fd01fd\",\"pin\":23,\"label\":\"GPIO 11 (SPI_SCLK)\",\"row\":11,\"col\":0},{\"color\":\"#fd01fd\",\"pin\":24,\"label\":\"GPIO 8 (SPI_CE0)\",\"row\":11,\"col\":1},{\"color\":\"#000000\",\"pin\":25,\"label\":\"GND\",\"row\":12,\"col\":0},{\"color\":\"#fd01fd\",\"pin\":26,\"label\":\"GPIO 7 (SPI_CE1)\",\"row\":12,\"col\":1},{\"color\":\"#ffffff\",\"pin\":27,\"label\":\"ID_SD\",\"row\":13,\"col\":0},{\"color\":\"#ffffff\",\"pin\":28,\"label\":\"ID_SC\",\"row\":13,\"col\":1},{\"color\":\"#00fd00\",\"pin\":29,\"label\":\"GPIO 5\",\"row\":14,\"col\":0},{\"color\":\"#000000\",\"pin\":30,\"label\":\"GND\",\"row\":14,\"col\":1},{\"color\":\"#00fd00\",\"pin\":31,\"label\":\"GPIO 6\",\"row\":15,\"col\":0},{\"color\":\"#00fd00\",\"pin\":32,\"label\":\"GPIO 12\",\"row\":15,\"col\":1},{\"color\":\"#00fd00\",\"pin\":33,\"label\":\"GPIO 13\",\"row\":16,\"col\":0},{\"color\":\"#000000\",\"pin\":34,\"label\":\"GND\",\"row\":16,\"col\":1},{\"color\":\"#00fd00\",\"pin\":35,\"label\":\"GPIO 19\",\"row\":17,\"col\":0},{\"color\":\"#00fd00\",\"pin\":36,\"label\":\"GPIO 16\",\"row\":17,\"col\":1},{\"color\":\"#00fd00\",\"pin\":37,\"label\":\"GPIO 26\",\"row\":18,\"col\":0},{\"color\":\"#00fd00\",\"pin\":38,\"label\":\"GPIO 20\",\"row\":18,\"col\":1},{\"color\":\"#000000\",\"pin\":39,\"label\":\"GND\",\"row\":19,\"col\":0},{\"color\":\"#00fd00\",\"pin\":40,\"label\":\"GPIO 21\",\"row\":19,\"col\":1}],\"ledPanelBackgroundColor\":\"#008a00\"},\"title\":\"Raspberry Pi GPIO Panel\",\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"7\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.22518255793320163,\"funcBody\":\"var period = time % 1500;\\nreturn period < 500;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"11\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.7008206860666621,\"funcBody\":\"var period = time % 1500;\\nreturn period >= 500 && period < 1000;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"12\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.42600325102193426,\"funcBody\":\"var period = time % 1500;\\nreturn period >= 1000;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"13\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.48362241571415243,\"funcBody\":\"var period = time % 1500;\\nreturn period < 500;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"29\",\"color\":\"#607d8b\",\"settings\":{},\"_hash\":0.7217670147518815,\"funcBody\":\"var period = time % 1500;\\nreturn period >= 500 && period < 1000;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/rectangle_tank.json b/application/src/main/data/json/system/widget_types/rectangle_tank.json new file mode 100644 index 0000000000..ad2c567168 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/rectangle_tank.json @@ -0,0 +1,36 @@ +{ + "fqn": "rectangle_tank", + "name": "Rectangle tank", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Widget indicates the level of liquid in Rectangle tank.", + "descriptor": { + "type": "latest", + "sizeX": 4, + "sizeY": 4, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.liquidLevelWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.liquidLevelWidget.update();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n}\n\nself.actionSources = function() { \n return { \n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false \n } \n };\n}", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-liquid-level-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-liquid-level-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.floor(Math.random() * 101);\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"tankSelectionType\":\"static\",\"selectedShape\":\"Rectangle\",\"shapeAttributeName\":\"tankShape\",\"tankColor\":{\"type\":\"range\",\"color\":\"#242770\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E73535DE\"},{\"from\":20,\"to\":null,\"color\":\"#242770\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E73535DE';\\n }\\n}\\nreturn '#242770';\"},\"datasourceUnits\":\"%\",\"layout\":\"percentage\",\"volumeSource\":\"static\",\"volumeConstant\":500,\"volumeAttributeName\":\"volume\",\"volumeUnits\":\"L\",\"volumeFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"volumeColor\":\"rgba(0, 0, 0, 0.18)\",\"units\":\"%\",\"widgetUnitsSource\":\"static\",\"widgetUnitsAttributeName\":\"units\",\"liquidColor\":{\"type\":\"range\",\"color\":\"#7A8BFF\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E27C7CDE\"},{\"from\":20,\"to\":null,\"color\":\"#7A8BFF\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FF0000DE\"},{\"from\":20,\"to\":null,\"color\":\"rgba(0,0,0,0.87)\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FF0000DE';\\n }\\n}\\nreturn '#000000DE';\"},\"showBackgroundOverlay\":true,\"backgroundOverlayColor\":{\"type\":\"range\",\"color\":\"#FFFFFFC2\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FFEFEFDE\"},{\"from\":20,\"to\":null,\"color\":\"#FFFFFFC2\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FFEFEFDE';\\n }\\n}\\nreturn '#FFFFFFC2';\"},\"showTooltip\":true,\"showTooltipLevel\":true,\"tooltipUnits\":\"%\",\"tooltipLevelDecimals\":0,\"tooltipLevelFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipLevelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.76)\",\"rangeList\":[],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"showTooltipDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":3,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Liquid level\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"titleFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1.5\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"showTitleIcon\":false,\"titleIcon\":\"water_drop\",\"iconColor\":\"#5469FF\",\"decimals\":0,\"enableDataExport\":false,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" + }, + "externalId": null, + "tags": [ + "reservoir", + "container", + "vessel", + "storage unit", + "cistern", + "canister", + "vat", + "basin", + "repository", + "bin", + "hopper" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/rotational_speed_card.json b/application/src/main/data/json/system/widget_types/rotational_speed_card.json new file mode 100644 index 0000000000..13fbf4e40f --- /dev/null +++ b/application/src/main/data/json/system/widget_types/rotational_speed_card.json @@ -0,0 +1,30 @@ +{ + "fqn": "rotational_speed_card", + "name": "Rotational speed card", + "deprecated": false, + "image": "tb-image:cm90YXRpb25hbF9zcGVlZF9jYXJkLnN2Zw==:cm90YXRpb25hbF9zcGVlZF9jYXJkLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxnIGZpbHRlcj0idXJsKCNhKSI+PHJlY3Qgd2lkdGg9IjE4OCIgaGVpZ2h0PSIxNDgiIHg9IjYiIHk9IjYiIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iLjgiIHJ4PSI0Ii8+PC9nPjxwYXRoIGZpbGw9IiMzMDVBRDciIGQ9Ik0yNyAyMy44Yy02LjkgMC0xMi41IDIuOC0xMi41IDYuMiAwIDIuOCAzLjcgNS4yIDguOCA2djRsNS01LTUtNXYzLjRjLTQtLjctNi4zLTIuNC02LjMtMy40IDAtMS4zIDMuOC0zLjggMTAtMy44czEwIDIuNSAxMCAzLjhjMCAxLTEuOCAyLjQtNSAzLjJ2Mi41YzQuNC0xIDcuNS0zLjEgNy41LTUuNyAwLTMuNC01LjYtNi4zLTEyLjUtNi4zWiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik00OSAxOC41aDMuNWE1IDUgMCAwIDEgMiAuNGMuNS4yIDEgLjYgMS4yIDEgLjMuNC40IDEgLjQgMS42IDAgLjUgMCAxLS4zIDEuNGwtLjcgMS0xLjIuNS0uNS4zaC0zLjJ2LTEuNGgyLjRjLjQgMCAuNyAwIDEtLjJsLjYtLjYuMi0uOWMwLS4zIDAtLjYtLjItLjlsLS42LS42LTEtLjJoLTEuOXY4LjZINDl2LTEwWm01LjcgMTBMNTIuNCAyNGgxLjhsMi4zIDQuNHYuMWgtMS44Wm0yLjktMy42di0uMmMwLS41IDAtMSAuMi0xLjVsLjctMS4yYTMgMyAwIDAgMSAxLS43TDYxIDIxYy42IDAgMSAwIDEuNS4zLjQuMS44LjQgMSAuNy40LjQuNi44LjcgMS4ybC4zIDEuNXYuMmwtLjMgMS41LS42IDEuMmEzLjEgMy4xIDAgMCAxLTIuNiAxYy0uNSAwLTEgMC0xLjQtLjJhMy41IDMuNSAwIDAgMS0xLjgtMmwtLjItMS41Wm0xLjYtLjJ2LjJjMCAuMyAwIC42LjIgMSAwIC4yLjEuNS4zLjdsLjUuNWMuMy4yLjUuMi44LjJhMS42IDEuNiAwIDAgMCAxLjQtLjdjLjEtLjIuMy0uNS4zLS44bC4xLTF2LTFsLS40LS44YTEuNiAxLjYgMCAwIDAtMS40LS43Yy0uMyAwLS41IDAtLjguMmwtLjUuNWMtLjIuMi0uMy41LS4zLjhsLS4yIDFabTEwLjMtMy42djEuMmgtNC4ydi0xLjJoNC4yWm0tMy0xLjhoMS43VjI3bC40LjJoLjRhMi41IDIuNSAwIDAgMCAuNiAwdjEuM2E0LjIgNC4yIDAgMCAxLTEuMS4xYy0uNCAwLS43IDAtMS0uMi0uMy0uMS0uNi0uMy0uNy0uNi0uMi0uMy0uMy0uNy0uMy0xLjJ2LTcuM1ptOC43IDcuN3YtMy41YzAtLjMgMC0uNS0uMi0uN2ExIDEgMCAwIDAtLjQtLjRsLS43LS4yLS43LjEtLjUuNC0uMS41aC0xLjdsLjItLjguNy0uN2EzIDMgMCAwIDEgMS0uNUE0IDQgMCAwIDEgNzQgMjFjLjUgMCAxIDAgMS41LjIuNC4yLjcuNSAxIC45LjIuNC4zLjguMyAxLjR2My4zbC4xLjkuMi43di4xaC0xLjdhMyAzIDAgMCAxLS4xLS43bC0uMS0uOFptLjItM3YxaC0xLjFsLS44LjFjLS4zIDAtLjUuMS0uNi4zYTEgMSAwIDAgMC0uNS45bC4xLjUuNC40LjcuMWExLjggMS44IDAgMCAwIDEuNS0uN2wuMi0uNi41LjgtLjIuNmEzIDMgMCAwIDEtLjYuNiAyLjYgMi42IDAgMCAxLTEuNy42Yy0uNSAwLTEgMC0xLjMtLjMtLjQtLjEtLjctLjQtMS0uN2EyLjQgMi40IDAgMCAxLS4xLTIuMmwuNy0uOCAxLjEtLjQgMS40LS4yaDEuM1ptNi44LTIuOXYxLjJINzh2LTEuMmg0LjJabS0zLTEuOEg4MVYyN2wuNC4yaC40YTIuNSAyLjUgMCAwIDAgLjYgMHYxLjNhNC4yIDQuMiAwIDAgMS0xLjEuMWMtLjQgMC0uNyAwLTEtLjItLjMtLjEtLjUtLjMtLjctLjYtLjItLjMtLjMtLjctLjMtMS4ydi03LjNabTYuMyAxLjh2Ny40aC0xLjd2LTcuNGgxLjdabS0xLjgtMiAuMy0uNi43LS4yYy4zIDAgLjUgMCAuNy4ybC4yLjdjMCAuMiAwIC40LS4yLjZhMSAxIDAgMCAxLS43LjIgMSAxIDAgMCAxLS43LS4yLjguOCAwIDAgMS0uMy0uNlptMy41IDUuOHYtLjJsLjMtMS41LjYtMS4yYTMgMyAwIDAgMSAxLjEtLjdjLjQtLjIgMS0uMyAxLjUtLjNzMSAwIDEuNC4zYy40LjEuOC40IDEuMS43bC43IDEuMi4yIDEuNXYuMmMwIC41IDAgMS0uMiAxLjVsLS43IDEuMmEzLjEgMy4xIDAgMCAxLTIuNSAxYy0uNiAwLTEgMC0xLjUtLjJhMy41IDMuNSAwIDAgMS0xLjgtMmwtLjItMS41Wm0xLjctLjJWMjZsLjQuN2MuMi4yLjMuNC42LjUuMi4yLjUuMi44LjJhMS42IDEuNiAwIDAgMCAxLjMtLjdsLjQtLjh2LTJMOTIgMjNhMS42IDEuNiAwIDAgMC0xLjMtLjdjLS4zIDAtLjYgMC0uOC4yLS4zLjEtLjQuMy0uNi41bC0uMy44LS4xIDFabTguNS0ydjUuOGgtMS43di03LjRoMS42djEuNlptLS4zIDEuOGgtLjZjMC0uNS4xLTEgLjMtMS40LjEtLjUuMy0uOC42LTEuMWEyLjcgMi43IDAgMCAxIDIuMS0xbDEgLjEuNy41LjUuOC4yIDEuM3Y0LjhoLTEuN3YtNC44YzAtLjMgMC0uNi0uMi0uOGEuOS45IDAgMCAwLS40LS41bC0uNy0uMWExLjYgMS42IDAgMCAwLTEuMy43bC0uNC43LS4xLjhaTTEwOCAyN3YtMy41YzAtLjMgMC0uNS0uMi0uN2ExIDEgMCAwIDAtLjQtLjRsLS44LS4yLS42LjEtLjUuNC0uMi41aC0xLjZjMC0uMyAwLS41LjItLjguMS0uMy4zLS41LjYtLjdhMyAzIDAgMCAxIDEtLjUgNCA0IDAgMCAxIDEuMi0uMmMuNiAwIDEgMCAxLjUuMi40LjIuOC41IDEgLjkuMy40LjQuOC40IDEuNHY0LjJsLjMuN3YuMWgtMS43YTMgMyAwIDAgMS0uMi0uN1YyN1ptLjItM3YxSDEwN2wtLjguMS0uNi4zYTEgMSAwIDAgMC0uNC45bC4xLjUuNC40LjYuMWExLjggMS44IDAgMCAwIDEuNS0uN2wuMy0uNi41LjgtLjMuNmEyLjYgMi42IDAgMCAxLTIuMyAxLjNjLS41IDAtLjktLjItMS4zLS40LS4zLS4xLS42LS40LS45LS43YTIuNCAyLjQgMCAwIDEtLjEtMi4ybC43LS44IDEtLjQgMS41LS4yaDEuM1ptNS4xLTZ2MTAuNWgtMS42VjE4aDEuNlptMTAgOC41LS4xLS40YTEgMSAwIDAgMC0uNS0uNGwtMS0uMy0xLjEtLjMtLjgtLjVjLS4zLS4xLS40LS4zLS42LS42bC0uMi0uOC4yLS45LjYtLjcuOS0uNSAxLjItLjFjLjYgMCAxLjEgMCAxLjYuMy40LjIuOC41IDEgLjguMi40LjQuOC40IDEuMmgtMS43bC0uMS0uNWExIDEgMCAwIDAtLjUtLjVsLS43LS4xLS43LjFhMSAxIDAgMCAwLS40LjQuOS45IDAgMCAwIDAgLjhsLjIuMi40LjIuOC4yIDEuNC41Yy40LjIuOC40IDEgLjcuMi4zLjMuNy4zIDEuMWEyIDIgMCAwIDEtLjggMS42IDMgMyAwIDAgMS0xIC41bC0xLjIuMWMtLjcgMC0xLjIgMC0xLjctLjNzLS44LS42LTEtMWMtLjMtLjMtLjQtLjctLjQtMS4xaDEuNmMwIC4zIDAgLjUuMi43bC42LjQuNy4xLjgtLjFjLjIgMCAuMy0uMi40LS4zbC4yLS41Wm01LTR2OC44aC0xLjdWMjEuMWgxLjVsLjEgMS40Wm00LjggMi4ydi4ybC0uMiAxLjUtLjYgMS4yYy0uMi4zLS41LjYtLjkuOGwtMS4yLjJjLS41IDAtLjkgMC0xLjItLjJsLS45LS43YTQgNCAwIDAgMS0uNi0xLjJsLS4zLTEuNHYtLjVsLjMtMS41LjYtMS4xYTIuNSAyLjUgMCAwIDEgMi0xYy41IDAgMSAwIDEuMy4ybDEgLjguNSAxLjIuMiAxLjVabS0xLjcuMnYtMS4ybC0uNC0uN2ExLjUgMS41IDAgMCAwLTEuMy0uN2wtLjcuMS0uNS40LS40LjctLjEuN3YxLjNsLjIgMSAuNi42IDEgLjJjLjIgMCAuNSAwIC43LS4yLjIgMCAuNC0uMy41LS41LjItLjIuMy0uNS4zLS44bC4xLTFabTYuNiAzLjdjLS42IDAtMSAwLTEuNS0uMmEzLjMgMy4zIDAgMCAxLTEuOC0yIDQgNCAwIDAgMS0uMy0xLjN2LS4zYzAtLjYuMS0xLjEuMy0xLjYuMS0uNS40LS45LjctMS4yYTMgMyAwIDAgMSAxLS44bDEuNC0uMmMuNSAwIDEgMCAxLjQuMmwxIC44LjUgMS4xYy4yLjUuMiAxIC4yIDEuNXYuN2gtNS43VjI0aDQuMVYyNGMwLS4zIDAtLjYtLjItLjkgMC0uMi0uMi0uNC0uNS0uNmwtLjgtLjJjLS4zIDAtLjUgMC0uOC4yLS4yIDAtLjMuMi0uNS41bC0uMy43LS4xIDF2LjRsLjEuOS40LjcuNi41LjkuMWEyLjMgMi4zIDAgMCAwIDEuOC0uOWwxIC45LS43LjYtMSAuNWMtLjMuMi0uNy4yLTEuMi4yWm03LjcgMGMtLjUgMC0xIDAtMS40LS4yYTMuMyAzLjMgMCAwIDEtMS45LTIgNCA0IDAgMCAxLS4yLTEuM3YtLjNjMC0uNiAwLTEuMS4yLTEuNnMuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS4xLS44bDEuMy0uMmMuNiAwIDEgMCAxLjQuMmwxIC44Yy4zLjMuNS43LjYgMS4xbC4yIDEuNXYuN0gxNDNWMjRoNFYyNGwtLjEtLjktLjUtLjYtLjktLjJjLS4yIDAtLjUgMC0uNy4yLS4yIDAtLjQuMi0uNS41bC0uNC43VjI2bC41LjcuNi41LjguMWEyLjMgMi4zIDAgMCAwIDEuOS0uOWwuOS45LS43LjZjLS4yLjItLjUuNC0uOS41LS40LjItLjguMi0xLjMuMlptOS0xLjZ2LTloMS43djEwLjVoLTEuNWwtLjItMS41Wm0tNC44LTIuMXYtLjJjMC0uNSAwLTEgLjItMS41cy4zLS45LjYtMS4yYTIuNSAyLjUgMCAwIDEgMi4xLTFjLjUgMCAuOSAwIDEuMi4ybC45LjguNSAxLjEuMyAxLjV2LjVjMCAuNS0uMSAxLS4zIDEuNGE0IDQgMCAwIDEtLjUgMS4xIDIuNSAyLjUgMCAwIDEtMiAxIDIuNiAyLjYgMCAwIDEtMi4yLTFsLS42LTEuMi0uMi0xLjVabTEuNy0uMlYyNmwuNC43YzAgLjIuMy40LjUuNWwuNy4yYy40IDAgLjcgMCAxLS4ybC41LS43Yy4yLS4zLjMtLjYuMy0xdi0xLjJjMC0uMyAwLS41LS4yLS43YTIgMiAwIDAgMC0uMy0uNmwtLjUtLjUtLjgtLjFjLS4zIDAtLjUgMC0uNy4ybC0uNS41Yy0uMi4yLS4zLjUtLjMuOGwtLjEgMVoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii40IiBkPSJNNTIuOSA0MS42di45aC0zLjR2LTFINTNabS0zLTUuOHY2LjdoLTEuMnYtNi43aDEuMVptNi44IDUuN3YtMi40bC0uMS0uNC0uMy0uM2ExIDEgMCAwIDAtLjUtLjFoLS41bC0uMy4zLS4xLjRoLTEuMWwuMS0uNi40LS41Yy4yLS4xLjQtLjMuNy0uM2wuOC0uMiAxIC4yYy4zLjEuNi4zLjcuNi4yLjIuMy42LjMgMVY0MmwuMi40di4xaC0xLjJsLS4xLS41di0uNVptLjEtMnYuN2gtMS4zbC0uNC4yYS43LjcgMCAwIDAtLjMuNnYuNGwuMy4yLjUuMWExLjIgMS4yIDAgMCAwIDEtLjVsLjEtLjQuNC41LS4yLjRjMCAuMi0uMi4zLS4zLjVhMS43IDEuNyAwIDAgMS0xLjIuNGMtLjQgMC0uNyAwLTEtLjJsLS41LS41Yy0uMi0uMy0uMi0uNS0uMi0uOGwuMS0uNy41LS41LjctLjMgMS0uMWguOFptNS4xIDEuN3YtLjNsLS40LS4zYTMgMyAwIDAgMC0uNy0uMmwtLjctLjItLjYtLjMtLjMtLjQtLjEtLjZ2LS41bC41LS41Yy4xLS4yLjMtLjMuNi0uM2wuOC0uMiAxIC4yLjcuNmMuMi4yLjMuNS4zLjhoLTEuMWwtLjEtLjQtLjMtLjJhMSAxIDAgMCAwLS41LS4xIDEgMSAwIDAgMC0uNSAwbC0uMy4zYS42LjYgMCAwIDAgMCAuNWwuMS4yLjQuMS41LjIgMSAuMy42LjUuMi43YTEuMyAxLjMgMCAwIDEtLjUgMSAyIDIgMCAwIDEtLjcuNEg2MWwtMS4yLS4xLS43LS43LS4yLS44aDFsLjIuNWMuMS4yLjMuMi40LjNoMWwuMy0uMi4xLS4zWm00LjUtMy43di44aC0yLjh2LS44aDIuOFptLTItMS4yaDEuMnY1LjJsLjIuMWguM2ExLjcgMS43IDAgMCAwIC40IDB2LjlhMi44IDIuOCAwIDAgMS0uNyAwSDY1YTEgMSAwIDAgMS0uNS0uNWwtLjEtLjh2LTQuOVptOC42IDV2LTMuOGgxLjF2NWgtMXYtMS4yWm0uMi0xaC40bC0uMSAxLS40LjZjLS4xLjItLjMuNC0uNi41YTIgMiAwIDAgMS0uOC4yIDIgMiAwIDAgMS0uNy0uMWMtLjIgMC0uNC0uMi0uNS0uM2wtLjQtLjZWMzcuNWgxdjMuN2wuMy4zLjIuMS4zLjFjLjMgMCAuNiAwIC44LS4ybC40LS41di0uN1ptMy40LTEuOHY2aC0xLjF2LTdoMXYxWm0zLjIgMS41LS4xIDFjMCAuNC0uMi42LS40LjktLjEuMi0uMy40LS42LjVsLS44LjJjLS4zIDAtLjYgMC0uOC0uMi0uMiAwLS40LS4zLS42LS41bC0uNC0uNy0uMi0xVjQwbC4yLTEgLjQtLjhhMS43IDEuNyAwIDAgMSAxLjQtLjdjLjMgMCAuNiAwIC44LjIuMy4xLjUuMy42LjVsLjQuOC4xIDFabS0xIDAtLjEtLjctLjItLjVhMSAxIDAgMCAwLTEtLjVsLS40LjFhMSAxIDAgMCAwLS40LjNsLS4yLjQtLjEuNXYuOWwuMi42YzAgLjIuMi4zLjQuNC4xLjIuMy4yLjYuMmwuNS0uMS40LS40LjItLjV2LS42Wm01LjIgMS41di02aDEuMXY3aC0xdi0xWk04MC44IDQwVjQwbC4xLTFjMC0uNC4yLS42LjQtLjlhMS43IDEuNyAwIDAgMSAxLjQtLjdjLjMgMCAuNiAwIC44LjJsLjYuNS40LjguMiAxdi4zbC0uMiAxLS40LjdhMS42IDEuNiAwIDAgMS0xLjQuNyAxLjcgMS43IDAgMCAxLTEuNC0uN2wtLjQtLjgtLjEtMVptMS0uMS4xLjcuMi41LjQuNC41LjFjLjIgMCAuNSAwIC42LS4yLjIgMCAuMy0uMi40LS40bC4yLS43di0uOGwtLjEtLjUtLjMtLjRhMSAxIDAgMCAwLS4zLS4zSDgzYTEgMSAwIDAgMC0uNSAwIDEgMSAwIDAgMC0uNC40bC0uMi41di43Wm03LjUgMS41di0yLjRsLS4xLS40YzAtLjItLjItLjMtLjMtLjNhMSAxIDAgMCAwLS41LS4xaC0uNWwtLjMuM3YuNGgtMS4yYzAtLjIgMC0uNC4yLS42IDAtLjIuMi0uNC40LS41bC42LS4zLjktLjIgMSAuMmMuMi4xLjUuMy42LjYuMi4yLjMuNi4zIDFWNDJsLjIuNHYuMWgtMS4yVjQybC0uMS0uNVptLjEtMnYuN0g4OC4xbC0uNC4yYS43LjcgMCAwIDAtLjMuNmwuMS40cy4xLjIuMy4ybC40LjFhMS4yIDEuMiAwIDAgMCAxLS41bC4yLS40LjMuNWMwIC4yIDAgLjMtLjIuNCAwIC4yLS4yLjMtLjMuNWExLjcgMS43IDAgMCAxLTEuMi40Yy0uMyAwLS42IDAtLjktLjJsLS42LS41LS4yLS44LjEtLjcuNS0uNS43LS4zIDEtLjFoLjhabTQuNi0ydi44aC0yLjh2LS44SDk0Wm0tMi0xLjJoMS4xdjQuOGwuMS40LjIuMWguM2ExLjcgMS43IDAgMCAwIC40IDB2LjlhMi44IDIuOCAwIDAgMS0uOCAwaC0uNmExIDEgMCAwIDEtLjUtLjVsLS4yLS44di00LjlabTUuMyA2LjNjLS40IDAtLjcgMC0xLS4yYTIuMiAyLjIgMCAwIDEtMS4zLTEuM2wtLjEtMVY0MGwuMS0xIC41LS45LjctLjUgMS0uMmMuMyAwIC42IDAgLjkuMmwuNi41LjQuOC4yIDF2LjRoLTMuOXYtLjhoMi44YzAtLjIgMC0uNC0uMi0uNmExIDEgMCAwIDAtLjMtLjQgMSAxIDAgMCAwLS42LS4yIDEgMSAwIDAgMC0uOC41bC0uMi41LS4xLjd2LjhsLjMuNS41LjMuNS4xYTEuNSAxLjUgMCAwIDAgMS4zLS42bC42LjYtLjQuNC0uNy40aC0uOFptOC41LTYuOHY2LjdoLTF2LTUuNGwtMS43LjZ2LTFsMi42LTFoLjFabTUuNiA1Ljd2LTZoMS4ydjdoLTFsLS4yLTFabS0zLjItMS40VjQwbC4xLTEgLjQtLjkuNi0uNS45LS4yYy4zIDAgLjUgMCAuOC4ybC41LjUuNC44LjIgMXYuM2MwIC4zIDAgLjctLjIgMSAwIC4zLS4yLjUtLjQuN2ExLjYgMS42IDAgMCAxLTEuMy43IDEuNyAxLjcgMCAwIDEtMS41LS43bC0uNC0uOHYtMVptMS4xLS4xdi43bC4zLjVjMCAuMi4yLjMuMy40bC41LjFjLjMgMCAuNSAwIC43LS4ybC4zLS40LjItLjd2LTEuM2wtLjMtLjRhMSAxIDAgMCAwLS40LS4zaC0uNWExIDEgMCAwIDAtLjUgMCAxIDEgMCAwIDAtLjMuNGwtLjIuNXYuN1ptMTAgMS41di0yLjRsLS4xLS40LS4zLS4zYTEgMSAwIDAgMC0uNS0uMWgtLjVsLS4zLjMtLjEuNGgtMS4xbC4xLS42LjQtLjVjLjItLjEuNC0uMy43LS4zbC44LS4yIDEgLjJjLjMuMS42LjMuNy42LjIuMi4zLjYuMyAxVjQybC4yLjR2LjFoLTEuMmwtLjEtLjV2LS41Wm0uMS0ydi43aC0xLjNsLS40LjJhLjcuNyAwIDAgMC0uMy42di40bC4zLjIuNS4xYTEuMiAxLjIgMCAwIDAgMS0uNWwuMS0uNC40LjUtLjIuNGMwIC4yLS4yLjMtLjMuNWExLjcgMS43IDAgMCAxLTEuMi40Yy0uNCAwLS43IDAtMS0uMmwtLjUtLjUtLjItLjguMS0uNy41LS41LjctLjMgMS0uMWguOFptNS41LTJoMXY0LjljMCAuNCAwIC44LS4zIDEuMS0uMi4zLS40LjYtLjguN2EyLjcgMi43IDAgMCAxLTEuOC4ybC0uNi0uM2MtLjMtLjEtLjQtLjMtLjYtLjVsLjUtLjYuNi41YTEuNiAxLjYgMCAwIDAgMS4zIDBsLjUtLjUuMS0uNnYtMy43bC4xLTEuMlptLTMuNCAyLjZWNDBjMC0uNCAwLS43LjItMSAwLS40LjItLjYuNC0uOS4xLS4yLjMtLjQuNi0uNWwuOC0uMmMuMyAwIC42IDAgLjguMi4zLjEuNS4zLjYuNWwuNC44LjIgMXYuM2wtLjIgMS0uNC43YTEuNiAxLjYgMCAwIDEtMS40LjcgMS44IDEuOCAwIDAgMS0xLjQtLjdsLS40LS44LS4yLTFabTEuMS0uMS4xLjcuMi41LjQuNC41LjFjLjMgMCAuNSAwIC42LS4yLjIgMCAuMy0uMi40LS40bC4yLS43di0uOGwtLjEtLjUtLjItLjRhMSAxIDAgMCAwLS40LS4zaC0uNWExIDEgMCAwIDAtLjkuNGwtLjIuNXYuN1ptNC41IDAgLjEtMSAuNS0uOC43LS42IDEtLjIgMSAuMi43LjYuNC44LjIgMWMwIC40IDAgLjgtLjIgMSAwIC40LS4yLjYtLjQuOWEyLjEgMi4xIDAgMCAxLTEuNy43Yy0uNCAwLS43IDAtMS0uMmwtLjctLjUtLjUtLjhhMyAzIDAgMCAxLS4xLTFabTEgMCAuMS43LjIuNS40LjQuNi4xYTEgMSAwIDAgMCAuOS0uNWwuMi0uNXYtMS40bC0uMi0uNWExIDEgMCAwIDAtMS0uNSAxIDEgMCAwIDAtLjUuMSAxIDEgMCAwIDAtLjQuNGwtLjIuNXYuN1oiLz48cGF0aCBmaWxsPSIjMzA1QUQ3IiBkPSJNMzIuOSA5Ni4zaDIuNWMxIDAgMS44LS4yIDIuNS0uNS42LS40IDEuMS0uOCAxLjQtMS40LjMtLjYuNS0xLjMuNS0yIDAtLjktLjItMS42LS41LTIuMkEzIDMgMCAwIDAgMzggODljLS41LS4zLTEuMy0uNS0yLjEtLjVzLTEuNS4yLTIgLjVhMy41IDMuNSAwIDAgMC0yIDMuMmgtNC4zYzAtMS40LjQtMi42IDEtMy43LjgtMSAxLjgtMS44IDMtMi40YTEwLjUgMTAuNSAwIDAgMSA4LjUgMGMxLjIuNCAyLjIgMS4yIDMgMi4zLjYgMSAxIDIuNCAxIDQgMCAuNy0uMiAxLjUtLjYgMi4yLS4zLjgtLjggMS41LTEuNSAyLS42LjctMS41IDEuMi0yLjUgMS42LTEgLjQtMi4xLjUtMy40LjVoLTMuMnYtMi4zWm0wIDMuM3YtMi4zSDM2YzEuNSAwIDIuNy4xIDMuOC41IDEgLjQgMiAuOCAyLjYgMS41LjcuNiAxLjEgMS4zIDEuNSAyIC4zLjguNCAxLjcuNCAyLjUgMCAxLjItLjIgMi4zLS42IDMuMi0uNCAxLTEgMS44LTEuOCAyLjRhOCA4IDAgMCAxLTIuOCAxLjUgMTEuMyAxMS4zIDAgMCAxLTYuNiAwYy0xLS4zLTEuOS0uOC0yLjctMS40YTYuNyA2LjcgMCAwIDEtMS45LTIuMiA3IDcgMCAwIDEtLjctMy4yaDQuM2MwIC44LjIgMS40LjUgMiAuNC42LjkgMSAxLjUgMS40YTUgNSAwIDAgMCAyLjMuNWMuOSAwIDEuNi0uMiAyLjMtLjUuNi0uMyAxLjEtLjggMS40LTEuNC40LS42LjYtMS40LjYtMi4zIDAtMS0uMi0xLjgtLjYtMi40LS40LS42LTEtMS0xLjctMS40LS43LS4zLTEuNS0uNC0yLjUtLjRIMzNaTTY1IDk2djQuMWMwIDItLjIgMy44LS42IDUuMmE5IDkgMCAwIDEtMS42IDMuNGMtLjguOS0xLjcgMS41LTIuNyAyYTkuOCA5LjggMCAwIDEtNi4yLjIgNi45IDYuOSAwIDAgMS00LjEtMy40Yy0uNS0uOS0uOS0xLjktMS4yLTMuMS0uMi0xLjItLjMtMi43LS4zLTQuM3YtNC4xYzAtMiAuMi0zLjguNS01LjFhOSA5IDAgMCAxIDEuOC0zLjRjLjctMSAxLjYtMS41IDIuNi0yIDEtLjMgMi4yLS41IDMuNC0uNSAxIDAgMiAwIDIuOC4zYTYuNiA2LjYgMCAwIDEgNC4xIDMuM2MuNS45LjkgMiAxLjIgMy4xLjIgMS4yLjMgMi43LjMgNC4zWm0tNC4yIDQuN3YtNS40YzAtMSAwLTEuOS0uMi0yLjdhOCA4IDAgMCAwLS41LTIgNCA0IDAgMCAwLS44LTEuMyAzIDMgMCAwIDAtMS4yLS43Yy0uNC0uMi0xLS4zLTEuNS0uMy0uNiAwLTEuMi4xLTEuNy40LS41LjItMSAuNi0xLjMgMS4yLS40LjUtLjYgMS4zLS44IDIuMi0uMi45LS4zIDItLjMgMy4ydjUuNGMwIDEgMCAyIC4yIDIuOGwuNSAyYy4yLjUuNSAxIC45IDEuNC4zLjMuNy42IDEuMS43bDEuNS4zYTQgNCAwIDAgMCAxLjctLjRjLjYtLjMgMS0uNyAxLjMtMS4yLjQtLjYuNy0xLjQuOC0yLjMuMi0uOS4zLTIgLjMtMy4zWm0yNC44LTQuN3Y0LjFjMCAyLS4yIDMuOC0uNiA1LjJhOSA5IDAgMCAxLTEuNyAzLjRjLS43LjktMS42IDEuNS0yLjcgMmE5LjggOS44IDAgMCAxLTYuMi4yIDYuOSA2LjkgMCAwIDEtNC0zLjRjLS42LS45LTEtMS45LTEuMi0zLjEtLjMtMS4yLS40LTIuNy0uNC00LjN2LTQuMWMwLTIgLjItMy44LjYtNS4xYTkgOSAwIDAgMSAxLjctMy40Yy43LTEgMS42LTEuNSAyLjYtMiAxLS4zIDIuMi0uNSAzLjUtLjUgMSAwIDIgMCAyLjguM2E2LjYgNi42IDAgMCAxIDQgMy4zYy42LjkgMSAyIDEuMiAzLjEuMyAxLjIuNCAyLjcuNCA0LjNabS00LjIgNC43di01LjRsLS4yLTIuN2E4IDggMCAwIDAtLjYtMiA0IDQgMCAwIDAtLjgtMS4zIDMgMyAwIDAgMC0xLjItLjcgMy45IDMuOSAwIDAgMC0zLjIuMWMtLjUuMi0xIC42LTEuMyAxLjItLjMuNS0uNiAxLjMtLjggMi4yLS4yLjktLjIgMi0uMiAzLjJ2NS40bC4xIDIuOGMuMS44LjMgMS40LjYgMmwuOCAxLjRjLjMuMy43LjYgMS4yLjdhNCA0IDAgMCAwIDMuMi0uMWMuNS0uMyAxLS43IDEuMy0xLjIuMy0uNi42LTEuNC44LTIuMy4yLS45LjMtMiAuMy0zLjNabTE3LjgtMTUuNGg5YzIgMCAzLjYuMyA1IC45IDEuNC42IDIuNCAxLjQgMy4yIDIuNmE3LjYgNy42IDAgMCAxIDEgNC4yYzAgMS4zLS4yIDIuNC0uNiAzLjQtLjUgMS0xLjIgMS44LTIgMi40LS45LjctMS45IDEuMi0zIDEuNmwtMS40LjZoLTguMnYtMy41aDYuMWMxIDAgMi0uMiAyLjYtLjUuNy0uNCAxLjMtMSAxLjYtMS42YTQuOSA0LjkgMCAwIDAgMC00LjVjLS4zLS43LS44LTEuMi0xLjUtMS41YTYgNiAwIDAgMC0yLjgtLjZoLTQuNlYxMTFoLTQuNFY4NS40Wm0xNC42IDI1LjYtNi0xMS41aDQuNmw2LjEgMTEuM3YuMmgtNC43Wm0xNy45LTkuNUgxMjVWOThoNi43YTYgNiAwIDAgMCAyLjgtLjZjLjctLjQgMS4yLS45IDEuNS0xLjZhNSA1IDAgMCAwIC42LTIuMmMwLS44LS4yLTEuNi0uNi0yLjMtLjMtLjctLjgtMS4zLTEuNS0xLjctLjctLjUtMS43LS43LTIuOC0uN2gtNS4zVjExMUgxMjJWODUuNGg5LjhjMiAwIDMuNi40IDUgMSAxLjQuOCAyLjUgMS43IDMuMiAzYTggOCAwIDAgMSAxIDQuMSA4IDggMCAwIDEtMSA0LjMgNyA3IDAgMCAxLTMuMiAyLjdjLTEuNC42LTMgMS01IDFabTE1LjEtMTZoNGw3LjQgMTkuNiA3LjMtMTkuN2g0bC05LjggMjUuNmgtMy4xbC05LjgtMjUuNlptLTEuOCAwaDMuOGwuNiAxN3Y4LjVoLTQuNFY4NS40Wm0yMi41IDBoMy44VjExMUgxNjd2LTguNWwuNi0xN1oiLz48ZGVmcz48ZmlsdGVyIGlkPSJhIiB3aWR0aD0iMTk3LjQiIGhlaWdodD0iMTU3LjQiIHg9IjEuMyIgeT0iMS4zIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIyLjQiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTUxOF82MzQwIi8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xNTE4XzYzNDAiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48L2RlZnM+PC9zdmc+", + "description": "Displays the latest rotational speed telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'rotationalSpeed', label: 'Rotational speed', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Rotational speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 4000 - 2000;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 4000) {\\n\\tvalue = 4000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"square\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"360\",\"iconColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":500,\"color\":\"#305AD7\"},{\"from\":500,\"to\":1500,\"color\":\"#3FA71A\"},{\"from\":1500,\"to\":3000,\"color\":\"#FFA600\"},{\"from\":3000,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":28,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":500,\"color\":\"#305AD7\"},{\"from\":500,\"to\":1500,\"color\":\"#3FA71A\"},{\"from\":1500,\"to\":3000,\"color\":\"#FFA600\"},{\"from\":3000,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Rotational speed card\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"RPM\",\"decimals\":0,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "angular speed", + "spin rate", + "revolutions", + "rotational frequency", + "spin motion" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/rotational_speed_card_with_background.json b/application/src/main/data/json/system/widget_types/rotational_speed_card_with_background.json new file mode 100644 index 0000000000..d2e23b78d2 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/rotational_speed_card_with_background.json @@ -0,0 +1,30 @@ +{ + "fqn": "rotational_speed_card_with_background", + "name": "Rotational speed card with background", + "deprecated": false, + "image": "tb-image:cm90YXRpb25hbF9zcGVlZF9jYXJkX3dpdGhfYmFja2dyb3VuZC5zdmc=:cm90YXRpb25hbF9zcGVlZF9jYXJkX3dpdGhfYmFja2dyb3VuZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48L2c+PHBhdGggZmlsbD0iIzJCNTRDRSIgZD0iTTI3IDIzLjhjLTYuOSAwLTEyLjUgMi44LTEyLjUgNi4yIDAgMi44IDMuNyA1LjIgOC44IDZ2NGw1LTUtNS01djMuNGMtNC0uNy02LjMtMi40LTYuMy0zLjQgMC0xLjMgMy44LTMuOCAxMC0zLjhzMTAgMi41IDEwIDMuOGMwIDEtMS44IDIuNC01IDMuMnYyLjVjNC40LTEgNy41LTMuMSA3LjUtNS43IDAtMy40LTUuNi02LjMtMTIuNS02LjNaIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZD0iTTQ5IDE4LjVoMy41YTUgNSAwIDAgMSAyIC40Yy41LjIgMSAuNiAxLjIgMSAuMy40LjQgMSAuNCAxLjYgMCAuNSAwIDEtLjMgMS40bC0uNyAxLTEuMi41LS41LjNoLTMuMnYtMS40aDIuNGMuNCAwIC43IDAgMS0uMmwuNi0uNi4yLS45YzAtLjMgMC0uNi0uMi0uOWwtLjYtLjYtMS0uMmgtMS45djguNkg0OXYtMTBabTUuNyAxMEw1Mi40IDI0aDEuOGwyLjMgNC40di4xaC0xLjhabTIuOS0zLjZ2LS4yYzAtLjUgMC0xIC4yLTEuNWwuNy0xLjJhMyAzIDAgMCAxIDEtLjdMNjEgMjFjLjYgMCAxIDAgMS41LjMuNC4xLjguNCAxIC43LjQuNC42LjguNyAxLjJsLjMgMS41di4ybC0uMyAxLjUtLjYgMS4yYTMuMSAzLjEgMCAwIDEtMi42IDFjLS41IDAtMSAwLTEuNC0uMmEzLjUgMy41IDAgMCAxLTEuOC0ybC0uMi0xLjVabTEuNi0uMnYuMmMwIC4zIDAgLjYuMiAxIDAgLjIuMS41LjMuN2wuNS41Yy4zLjIuNS4yLjguMmExLjYgMS42IDAgMCAwIDEuNC0uN2MuMS0uMi4zLS41LjMtLjhsLjEtMXYtMWwtLjQtLjhhMS42IDEuNiAwIDAgMC0xLjQtLjdjLS4zIDAtLjUgMC0uOC4ybC0uNS41Yy0uMi4yLS4zLjUtLjMuOGwtLjIgMVptMTAuMy0zLjZ2MS4yaC00LjJ2LTEuMmg0LjJabS0zLTEuOGgxLjdWMjdsLjQuMmguNGEyLjUgMi41IDAgMCAwIC42IDB2MS4zYTQuMiA0LjIgMCAwIDEtMS4xLjFjLS40IDAtLjcgMC0xLS4yLS4zLS4xLS42LS4zLS43LS42LS4yLS4zLS4zLS43LS4zLTEuMnYtNy4zWm04LjcgNy43di0zLjVjMC0uMyAwLS41LS4yLS43YTEgMSAwIDAgMC0uNC0uNGwtLjctLjItLjcuMS0uNS40LS4xLjVoLTEuN2wuMi0uOC43LS43YTMgMyAwIDAgMSAxLS41QTQgNCAwIDAgMSA3NCAyMWMuNSAwIDEgMCAxLjUuMi40LjIuNy41IDEgLjkuMi40LjMuOC4zIDEuNHYzLjNsLjEuOS4yLjd2LjFoLTEuN2EzIDMgMCAwIDEtLjEtLjdsLS4xLS44Wm0uMi0zdjFoLTEuMWwtLjguMWMtLjMgMC0uNS4xLS42LjNhMSAxIDAgMCAwLS41LjlsLjEuNS40LjQuNy4xYTEuOCAxLjggMCAwIDAgMS41LS43bC4yLS42LjUuOC0uMi42YTMgMyAwIDAgMS0uNi42IDIuNiAyLjYgMCAwIDEtMS43LjZjLS41IDAtMSAwLTEuMy0uMy0uNC0uMS0uNy0uNC0xLS43YTIuNCAyLjQgMCAwIDEtLjEtMi4ybC43LS44IDEuMS0uNCAxLjQtLjJoMS4zWm02LjgtMi45djEuMkg3OHYtMS4yaDQuMlptLTMtMS44SDgxVjI3bC40LjJoLjRhMi41IDIuNSAwIDAgMCAuNiAwdjEuM2E0LjIgNC4yIDAgMCAxLTEuMS4xYy0uNCAwLS43IDAtMS0uMi0uMy0uMS0uNS0uMy0uNy0uNi0uMi0uMy0uMy0uNy0uMy0xLjJ2LTcuM1ptNi4zIDEuOHY3LjRoLTEuN3YtNy40aDEuN1ptLTEuOC0yIC4zLS42LjctLjJjLjMgMCAuNSAwIC43LjJsLjIuN2MwIC4yIDAgLjQtLjIuNmExIDEgMCAwIDEtLjcuMiAxIDEgMCAwIDEtLjctLjIuOC44IDAgMCAxLS4zLS42Wm0zLjUgNS44di0uMmwuMy0xLjUuNi0xLjJhMyAzIDAgMCAxIDEuMS0uN2MuNC0uMiAxLS4zIDEuNS0uM3MxIDAgMS40LjNjLjQuMS44LjQgMS4xLjdsLjcgMS4yLjIgMS41di4yYzAgLjUgMCAxLS4yIDEuNWwtLjcgMS4yYTMuMSAzLjEgMCAwIDEtMi41IDFjLS42IDAtMSAwLTEuNS0uMmEzLjUgMy41IDAgMCAxLTEuOC0ybC0uMi0xLjVabTEuNy0uMlYyNmwuNC43Yy4yLjIuMy40LjYuNS4yLjIuNS4yLjguMmExLjYgMS42IDAgMCAwIDEuMy0uN2wuNC0uOHYtMkw5MiAyM2ExLjYgMS42IDAgMCAwLTEuMy0uN2MtLjMgMC0uNiAwLS44LjItLjMuMS0uNC4zLS42LjVsLS4zLjgtLjEgMVptOC41LTJ2NS44aC0xLjd2LTcuNGgxLjZ2MS42Wm0tLjMgMS44aC0uNmMwLS41LjEtMSAuMy0xLjQuMS0uNS4zLS44LjYtMS4xYTIuNyAyLjcgMCAwIDEgMi4xLTFsMSAuMS43LjUuNS44LjIgMS4zdjQuOGgtMS43di00LjhjMC0uMyAwLS42LS4yLS44YS45LjkgMCAwIDAtLjQtLjVsLS43LS4xYTEuNiAxLjYgMCAwIDAtMS4zLjdsLS40LjctLjEuOFpNMTA4IDI3di0zLjVjMC0uMyAwLS41LS4yLS43YTEgMSAwIDAgMC0uNC0uNGwtLjgtLjItLjYuMS0uNS40LS4yLjVoLTEuNmMwLS4zIDAtLjUuMi0uOC4xLS4zLjMtLjUuNi0uN2EzIDMgMCAwIDEgMS0uNSA0IDQgMCAwIDEgMS4yLS4yYy42IDAgMSAwIDEuNS4yLjQuMi44LjUgMSAuOS4zLjQuNC44LjQgMS40djQuMmwuMy43di4xaC0xLjdhMyAzIDAgMCAxLS4yLS43VjI3Wm0uMi0zdjFIMTA3bC0uOC4xLS42LjNhMSAxIDAgMCAwLS40LjlsLjEuNS40LjQuNi4xYTEuOCAxLjggMCAwIDAgMS41LS43bC4zLS42LjUuOC0uMy42YTIuNiAyLjYgMCAwIDEtMi4zIDEuM2MtLjUgMC0uOS0uMi0xLjMtLjQtLjMtLjEtLjYtLjQtLjktLjdhMi40IDIuNCAwIDAgMS0uMS0yLjJsLjctLjggMS0uNCAxLjUtLjJoMS4zWm01LjEtNnYxMC41aC0xLjZWMThoMS42Wm0xMCA4LjUtLjEtLjRhMSAxIDAgMCAwLS41LS40bC0xLS4zLTEuMS0uMy0uOC0uNWMtLjMtLjEtLjQtLjMtLjYtLjZsLS4yLS44LjItLjkuNi0uNy45LS41IDEuMi0uMWMuNiAwIDEuMSAwIDEuNi4zLjQuMi44LjUgMSAuOC4yLjQuNC44LjQgMS4yaC0xLjdsLS4xLS41YTEgMSAwIDAgMC0uNS0uNWwtLjctLjEtLjcuMWExIDEgMCAwIDAtLjQuNC45LjkgMCAwIDAgMCAuOGwuMi4yLjQuMi44LjIgMS40LjVjLjQuMi44LjQgMSAuNy4yLjMuMy43LjMgMS4xYTIgMiAwIDAgMS0uOCAxLjYgMyAzIDAgMCAxLTEgLjVsLTEuMi4xYy0uNyAwLTEuMiAwLTEuNy0uM3MtLjgtLjYtMS0xYy0uMy0uMy0uNC0uNy0uNC0xLjFoMS42YzAgLjMgMCAuNS4yLjdsLjYuNC43LjEuOC0uMWMuMiAwIC4zLS4yLjQtLjNsLjItLjVabTUtNHY4LjhoLTEuN1YyMS4xaDEuNWwuMSAxLjRabTQuOCAyLjJ2LjJsLS4yIDEuNS0uNiAxLjJjLS4yLjMtLjUuNi0uOS44bC0xLjIuMmMtLjUgMC0uOSAwLTEuMi0uMmwtLjktLjdhNCA0IDAgMCAxLS42LTEuMmwtLjMtMS40di0uNWwuMy0xLjUuNi0xLjFhMi41IDIuNSAwIDAgMSAyLTFjLjUgMCAxIDAgMS4zLjJsMSAuOC41IDEuMi4yIDEuNVptLTEuNy4ydi0xLjJsLS40LS43YTEuNSAxLjUgMCAwIDAtMS4zLS43bC0uNy4xLS41LjQtLjQuNy0uMS43djEuM2wuMiAxIC42LjYgMSAuMmMuMiAwIC41IDAgLjctLjIuMiAwIC40LS4zLjUtLjUuMi0uMi4zLS41LjMtLjhsLjEtMVptNi42IDMuN2MtLjYgMC0xIDAtMS41LS4yYTMuMyAzLjMgMCAwIDEtMS44LTIgNCA0IDAgMCAxLS4zLTEuM3YtLjNjMC0uNi4xLTEuMS4zLTEuNi4xLS41LjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEtLjhsMS40LS4yYy41IDAgMSAwIDEuNC4ybDEgLjguNSAxLjFjLjIuNS4yIDEgLjIgMS41di43aC01LjdWMjRoNC4xVjI0YzAtLjMgMC0uNi0uMi0uOSAwLS4yLS4yLS40LS41LS42bC0uOC0uMmMtLjMgMC0uNSAwLS44LjItLjIgMC0uMy4yLS41LjVsLS4zLjctLjEgMXYuNGwuMS45LjQuNy42LjUuOS4xYTIuMyAyLjMgMCAwIDAgMS44LS45bDEgLjktLjcuNi0xIC41Yy0uMy4yLS43LjItMS4yLjJabTcuNyAwYy0uNSAwLTEgMC0xLjQtLjJhMy4zIDMuMyAwIDAgMS0xLjktMiA0IDQgMCAwIDEtLjItMS4zdi0uM2MwLS42IDAtMS4xLjItMS42cy40LS45LjctMS4yYTMgMyAwIDAgMSAxLjEtLjhsMS4zLS4yYy42IDAgMSAwIDEuNC4ybDEgLjhjLjMuMy41LjcuNiAxLjFsLjIgMS41di43SDE0M1YyNGg0VjI0bC0uMS0uOS0uNS0uNi0uOS0uMmMtLjIgMC0uNSAwLS43LjItLjIgMC0uNC4yLS41LjVsLS40LjdWMjZsLjUuNy42LjUuOC4xYTIuMyAyLjMgMCAwIDAgMS45LS45bC45LjktLjcuNmMtLjIuMi0uNS40LS45LjUtLjQuMi0uOC4yLTEuMy4yWm05LTEuNnYtOWgxLjd2MTAuNWgtMS41bC0uMi0xLjVabS00LjgtMi4xdi0uMmMwLS41IDAtMSAuMi0xLjVzLjMtLjkuNi0xLjJhMi41IDIuNSAwIDAgMSAyLjEtMWMuNSAwIC45IDAgMS4yLjJsLjkuOC41IDEuMS4zIDEuNXYuNWMwIC41LS4xIDEtLjMgMS40YTQgNCAwIDAgMS0uNSAxLjEgMi41IDIuNSAwIDAgMS0yIDEgMi42IDIuNiAwIDAgMS0yLjItMWwtLjYtMS4yLS4yLTEuNVptMS43LS4yVjI2bC40LjdjMCAuMi4zLjQuNS41bC43LjJjLjQgMCAuNyAwIDEtLjJsLjUtLjdjLjItLjMuMy0uNi4zLTF2LTEuMmMwLS4zIDAtLjUtLjItLjdhMiAyIDAgMCAwLS4zLS42bC0uNS0uNS0uOC0uMWMtLjMgMC0uNSAwLS43LjJsLS41LjVjLS4yLjItLjMuNS0uMy44bC0uMSAxWiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjQiIGQ9Ik01Mi45IDQxLjZ2LjloLTMuNHYtMUg1M1ptLTMtNS44djYuN2gtMS4ydi02LjdoMS4xWm02LjggNS43di0yLjRsLS4xLS40LS4zLS4zYTEgMSAwIDAgMC0uNS0uMWgtLjVsLS4zLjMtLjEuNGgtMS4xbC4xLS42LjQtLjVjLjItLjEuNC0uMy43LS4zbC44LS4yIDEgLjJjLjMuMS42LjMuNy42LjIuMi4zLjYuMyAxVjQybC4yLjR2LjFoLTEuMmwtLjEtLjV2LS41Wm0uMS0ydi43aC0xLjNsLS40LjJhLjcuNyAwIDAgMC0uMy42di40bC4zLjIuNS4xYTEuMiAxLjIgMCAwIDAgMS0uNWwuMS0uNC40LjUtLjIuNGMwIC4yLS4yLjMtLjMuNWExLjcgMS43IDAgMCAxLTEuMi40Yy0uNCAwLS43IDAtMS0uMmwtLjUtLjVjLS4yLS4zLS4yLS41LS4yLS44bC4xLS43LjUtLjUuNy0uMyAxLS4xaC44Wm01LjEgMS43di0uM2wtLjQtLjNhMyAzIDAgMCAwLS43LS4ybC0uNy0uMi0uNi0uMy0uMy0uNC0uMS0uNnYtLjVsLjUtLjVjLjEtLjIuMy0uMy42LS4zbC44LS4yIDEgLjIuNy42Yy4yLjIuMy41LjMuOGgtMS4xbC0uMS0uNC0uMy0uMmExIDEgMCAwIDAtLjUtLjEgMSAxIDAgMCAwLS41IDBsLS4zLjNhLjYuNiAwIDAgMCAwIC41bC4xLjIuNC4xLjUuMiAxIC4zLjYuNS4yLjdhMS4zIDEuMyAwIDAgMS0uNSAxIDIgMiAwIDAgMS0uNy40SDYxbC0xLjItLjEtLjctLjctLjItLjhoMWwuMi41Yy4xLjIuMy4yLjQuM2gxbC4zLS4yLjEtLjNabTQuNS0zLjd2LjhoLTIuOHYtLjhoMi44Wm0tMi0xLjJoMS4ydjUuMmwuMi4xaC4zYTEuNyAxLjcgMCAwIDAgLjQgMHYuOWEyLjggMi44IDAgMCAxLS43IDBINjVhMSAxIDAgMCAxLS41LS41bC0uMS0uOHYtNC45Wm04LjYgNXYtMy44aDEuMXY1aC0xdi0xLjJabS4yLTFoLjRsLS4xIDEtLjQuNmMtLjEuMi0uMy40LS42LjVhMiAyIDAgMCAxLS44LjIgMiAyIDAgMCAxLS43LS4xYy0uMiAwLS40LS4yLS41LS4zbC0uNC0uNlYzNy41aDF2My43bC4zLjMuMi4xLjMuMWMuMyAwIC42IDAgLjgtLjJsLjQtLjV2LS43Wm0zLjQtMS44djZoLTEuMXYtN2gxdjFabTMuMiAxLjUtLjEgMWMwIC40LS4yLjYtLjQuOS0uMS4yLS4zLjQtLjYuNWwtLjguMmMtLjMgMC0uNiAwLS44LS4yLS4yIDAtLjQtLjMtLjYtLjVsLS40LS43LS4yLTFWNDBsLjItMSAuNC0uOGExLjcgMS43IDAgMCAxIDEuNC0uN2MuMyAwIC42IDAgLjguMi4zLjEuNS4zLjYuNWwuNC44LjEgMVptLTEgMC0uMS0uNy0uMi0uNWExIDEgMCAwIDAtMS0uNWwtLjQuMWExIDEgMCAwIDAtLjQuM2wtLjIuNC0uMS41di45bC4yLjZjMCAuMi4yLjMuNC40LjEuMi4zLjIuNi4ybC41LS4xLjQtLjQuMi0uNXYtLjZabTUuMiAxLjV2LTZoMS4xdjdoLTF2LTFaTTgwLjggNDBWNDBsLjEtMWMwLS40LjItLjYuNC0uOWExLjcgMS43IDAgMCAxIDEuNC0uN2MuMyAwIC42IDAgLjguMmwuNi41LjQuOC4yIDF2LjNsLS4yIDEtLjQuN2ExLjYgMS42IDAgMCAxLTEuNC43IDEuNyAxLjcgMCAwIDEtMS40LS43bC0uNC0uOC0uMS0xWm0xLS4xLjEuNy4yLjUuNC40LjUuMWMuMiAwIC41IDAgLjYtLjIuMiAwIC4zLS4yLjQtLjRsLjItLjd2LS44bC0uMS0uNS0uMy0uNGExIDEgMCAwIDAtLjMtLjNIODNhMSAxIDAgMCAwLS41IDAgMSAxIDAgMCAwLS40LjRsLS4yLjV2LjdabTcuNSAxLjV2LTIuNGwtLjEtLjRjMC0uMi0uMi0uMy0uMy0uM2ExIDEgMCAwIDAtLjUtLjFoLS41bC0uMy4zdi40aC0xLjJjMC0uMiAwLS40LjItLjYgMC0uMi4yLS40LjQtLjVsLjYtLjMuOS0uMiAxIC4yYy4yLjEuNS4zLjYuNi4yLjIuMy42LjMgMVY0MmwuMi40di4xaC0xLjJWNDJsLS4xLS41Wm0uMS0ydi43SDg4LjFsLS40LjJhLjcuNyAwIDAgMC0uMy42bC4xLjRzLjEuMi4zLjJsLjQuMWExLjIgMS4yIDAgMCAwIDEtLjVsLjItLjQuMy41YzAgLjIgMCAuMy0uMi40IDAgLjItLjIuMy0uMy41YTEuNyAxLjcgMCAwIDEtMS4yLjRjLS4zIDAtLjYgMC0uOS0uMmwtLjYtLjUtLjItLjguMS0uNy41LS41LjctLjMgMS0uMWguOFptNC42LTJ2LjhoLTIuOHYtLjhIOTRabS0yLTEuMmgxLjF2NC44bC4xLjQuMi4xaC4zYTEuNyAxLjcgMCAwIDAgLjQgMHYuOWEyLjggMi44IDAgMCAxLS44IDBoLS42YTEgMSAwIDAgMS0uNS0uNWwtLjItLjh2LTQuOVptNS4zIDYuM2MtLjQgMC0uNyAwLTEtLjJhMi4yIDIuMiAwIDAgMS0xLjMtMS4zbC0uMS0xVjQwbC4xLTEgLjUtLjkuNy0uNSAxLS4yYy4zIDAgLjYgMCAuOS4ybC42LjUuNC44LjIgMXYuNGgtMy45di0uOGgyLjhjMC0uMiAwLS40LS4yLS42YTEgMSAwIDAgMC0uMy0uNCAxIDEgMCAwIDAtLjYtLjIgMSAxIDAgMCAwLS44LjVsLS4yLjUtLjEuN3YuOGwuMy41LjUuMy41LjFhMS41IDEuNSAwIDAgMCAxLjMtLjZsLjYuNi0uNC40LS43LjRoLS44Wm04LjUtNi44djYuN2gtMXYtNS40bC0xLjcuNnYtMWwyLjYtMWguMVptNS42IDUuN3YtNmgxLjJ2N2gtMWwtLjItMVptLTMuMi0xLjRWNDBsLjEtMSAuNC0uOS42LS41LjktLjJjLjMgMCAuNSAwIC44LjJsLjUuNS40LjguMiAxdi4zYzAgLjMgMCAuNy0uMiAxIDAgLjMtLjIuNS0uNC43YTEuNiAxLjYgMCAwIDEtMS4zLjcgMS43IDEuNyAwIDAgMS0xLjUtLjdsLS40LS44di0xWm0xLjEtLjF2LjdsLjMuNWMwIC4yLjIuMy4zLjRsLjUuMWMuMyAwIC41IDAgLjctLjJsLjMtLjQuMi0uN3YtMS4zbC0uMy0uNGExIDEgMCAwIDAtLjQtLjNoLS41YTEgMSAwIDAgMC0uNSAwIDEgMSAwIDAgMC0uMy40bC0uMi41di43Wm0xMCAxLjV2LTIuNGwtLjEtLjQtLjMtLjNhMSAxIDAgMCAwLS41LS4xaC0uNWwtLjMuMy0uMS40aC0xLjFsLjEtLjYuNC0uNWMuMi0uMS40LS4zLjctLjNsLjgtLjIgMSAuMmMuMy4xLjYuMy43LjYuMi4yLjMuNi4zIDFWNDJsLjIuNHYuMWgtMS4ybC0uMS0uNXYtLjVabS4xLTJ2LjdoLTEuM2wtLjQuMmEuNy43IDAgMCAwLS4zLjZ2LjRsLjMuMi41LjFhMS4yIDEuMiAwIDAgMCAxLS41bC4xLS40LjQuNS0uMi40YzAgLjItLjIuMy0uMy41YTEuNyAxLjcgMCAwIDEtMS4yLjRjLS40IDAtLjcgMC0xLS4ybC0uNS0uNS0uMi0uOC4xLS43LjUtLjUuNy0uMyAxLS4xaC44Wm01LjUtMmgxdjQuOWMwIC40IDAgLjgtLjMgMS4xLS4yLjMtLjQuNi0uOC43YTIuNyAyLjcgMCAwIDEtMS44LjJsLS42LS4zYy0uMy0uMS0uNC0uMy0uNi0uNWwuNS0uNi42LjVhMS42IDEuNiAwIDAgMCAxLjMgMGwuNS0uNS4xLS42di0zLjdsLjEtMS4yWm0tMy40IDIuNlY0MGMwLS40IDAtLjcuMi0xIDAtLjQuMi0uNi40LS45LjEtLjIuMy0uNC42LS41bC44LS4yYy4zIDAgLjYgMCAuOC4yLjMuMS41LjMuNi41bC40LjguMiAxdi4zbC0uMiAxLS40LjdhMS42IDEuNiAwIDAgMS0xLjQuNyAxLjggMS44IDAgMCAxLTEuNC0uN2wtLjQtLjgtLjItMVptMS4xLS4xLjEuNy4yLjUuNC40LjUuMWMuMyAwIC41IDAgLjYtLjIuMiAwIC4zLS4yLjQtLjRsLjItLjd2LS44bC0uMS0uNS0uMi0uNGExIDEgMCAwIDAtLjQtLjNoLS41YTEgMSAwIDAgMC0uOS40bC0uMi41di43Wm00LjUgMCAuMS0xIC41LS44LjctLjYgMS0uMiAxIC4yLjcuNi40LjguMiAxYzAgLjQgMCAuOC0uMiAxIDAgLjQtLjIuNi0uNC45YTIuMSAyLjEgMCAwIDEtMS43LjdjLS40IDAtLjcgMC0xLS4ybC0uNy0uNS0uNS0uOGEzIDMgMCAwIDEtLjEtMVptMSAwIC4xLjcuMi41LjQuNC42LjFhMSAxIDAgMCAwIC45LS41bC4yLS41di0xLjRsLS4yLS41YTEgMSAwIDAgMC0xLS41IDEgMSAwIDAgMC0uNS4xIDEgMSAwIDAgMC0uNC40bC0uMi41di43WiIvPjxwYXRoIGZpbGw9IiMyQjU0Q0UiIGQ9Ik0zMi45IDk2LjNoMi41YzEgMCAxLjgtLjIgMi41LS41LjYtLjQgMS4xLS44IDEuNC0xLjQuMy0uNi41LTEuMy41LTIgMC0uOS0uMi0xLjYtLjUtMi4yQTMgMyAwIDAgMCAzOCA4OWMtLjUtLjMtMS4zLS41LTIuMS0uNXMtMS41LjItMiAuNWEzLjUgMy41IDAgMCAwLTIgMy4yaC00LjNjMC0xLjQuNC0yLjYgMS0zLjcuOC0xIDEuOC0xLjggMy0yLjRhMTAuNSAxMC41IDAgMCAxIDguNSAwYzEuMi40IDIuMiAxLjIgMyAyLjMuNiAxIDEgMi40IDEgNCAwIC43LS4yIDEuNS0uNiAyLjItLjMuOC0uOCAxLjUtMS41IDItLjYuNy0xLjUgMS4yLTIuNSAxLjYtMSAuNC0yLjEuNS0zLjQuNWgtMy4ydi0yLjNabTAgMy4zdi0yLjNIMzZjMS41IDAgMi43LjEgMy44LjUgMSAuNCAyIC44IDIuNiAxLjUuNy42IDEuMSAxLjMgMS41IDIgLjMuOC40IDEuNy40IDIuNSAwIDEuMi0uMiAyLjMtLjYgMy4yLS40IDEtMSAxLjgtMS44IDIuNGE4IDggMCAwIDEtMi44IDEuNSAxMS4zIDExLjMgMCAwIDEtNi42IDBjLTEtLjMtMS45LS44LTIuNy0xLjRhNi43IDYuNyAwIDAgMS0xLjktMi4yIDcgNyAwIDAgMS0uNy0zLjJoNC4zYzAgLjguMiAxLjQuNSAyIC40LjYuOSAxIDEuNSAxLjRhNSA1IDAgMCAwIDIuMy41Yy45IDAgMS42LS4yIDIuMy0uNS42LS4zIDEuMS0uOCAxLjQtMS40LjQtLjYuNi0xLjQuNi0yLjMgMC0xLS4yLTEuOC0uNi0yLjQtLjQtLjYtMS0xLTEuNy0xLjQtLjctLjMtMS41LS40LTIuNS0uNEgzM1pNNjUgOTZ2NC4xYzAgMi0uMiAzLjgtLjYgNS4yYTkgOSAwIDAgMS0xLjYgMy40Yy0uOC45LTEuNyAxLjUtMi43IDJhOS44IDkuOCAwIDAgMS02LjIuMiA2LjkgNi45IDAgMCAxLTQuMS0zLjRjLS41LS45LS45LTEuOS0xLjItMy4xLS4yLTEuMi0uMy0yLjctLjMtNC4zdi00LjFjMC0yIC4yLTMuOC41LTUuMWE5IDkgMCAwIDEgMS44LTMuNGMuNy0xIDEuNi0xLjUgMi42LTIgMS0uMyAyLjItLjUgMy40LS41IDEgMCAyIDAgMi44LjNhNi42IDYuNiAwIDAgMSA0LjEgMy4zYy41LjkuOSAyIDEuMiAzLjEuMiAxLjIuMyAyLjcuMyA0LjNabS00LjIgNC43di01LjRjMC0xIDAtMS45LS4yLTIuN2E4IDggMCAwIDAtLjUtMiA0IDQgMCAwIDAtLjgtMS4zIDMgMyAwIDAgMC0xLjItLjdjLS40LS4yLTEtLjMtMS41LS4zLS42IDAtMS4yLjEtMS43LjQtLjUuMi0xIC42LTEuMyAxLjItLjQuNS0uNiAxLjMtLjggMi4yLS4yLjktLjMgMi0uMyAzLjJ2NS40YzAgMSAwIDIgLjIgMi44bC41IDJjLjIuNS41IDEgLjkgMS40LjMuMy43LjYgMS4xLjdsMS41LjNhNCA0IDAgMCAwIDEuNy0uNGMuNi0uMyAxLS43IDEuMy0xLjIuNC0uNi43LTEuNC44LTIuMy4yLS45LjMtMiAuMy0zLjNabTI0LjgtNC43djQuMWMwIDItLjIgMy44LS42IDUuMmE5IDkgMCAwIDEtMS43IDMuNGMtLjcuOS0xLjYgMS41LTIuNyAyYTkuOCA5LjggMCAwIDEtNi4yLjIgNi45IDYuOSAwIDAgMS00LTMuNGMtLjYtLjktMS0xLjktMS4yLTMuMS0uMy0xLjItLjQtMi43LS40LTQuM3YtNC4xYzAtMiAuMi0zLjguNi01LjFhOSA5IDAgMCAxIDEuNy0zLjRjLjctMSAxLjYtMS41IDIuNi0yIDEtLjMgMi4yLS41IDMuNS0uNSAxIDAgMiAwIDIuOC4zYTYuNiA2LjYgMCAwIDEgNCAzLjNjLjYuOSAxIDIgMS4yIDMuMS4zIDEuMi40IDIuNy40IDQuM1ptLTQuMiA0Ljd2LTUuNGwtLjItMi43YTggOCAwIDAgMC0uNi0yIDQgNCAwIDAgMC0uOC0xLjMgMyAzIDAgMCAwLTEuMi0uNyAzLjkgMy45IDAgMCAwLTMuMi4xYy0uNS4yLTEgLjYtMS4zIDEuMi0uMy41LS42IDEuMy0uOCAyLjItLjIuOS0uMiAyLS4yIDMuMnY1LjRsLjEgMi44Yy4xLjguMyAxLjQuNiAybC44IDEuNGMuMy4zLjcuNiAxLjIuN2E0IDQgMCAwIDAgMy4yLS4xYy41LS4zIDEtLjcgMS4zLTEuMi4zLS42LjYtMS40LjgtMi4zLjItLjkuMy0yIC4zLTMuM1ptMTcuOC0xNS40aDljMiAwIDMuNi4zIDUgLjkgMS40LjYgMi40IDEuNCAzLjIgMi42YTcuNiA3LjYgMCAwIDEgMSA0LjJjMCAxLjMtLjIgMi40LS42IDMuNC0uNSAxLTEuMiAxLjgtMiAyLjQtLjkuNy0xLjkgMS4yLTMgMS42bC0xLjQuNmgtOC4ydi0zLjVoNi4xYzEgMCAyLS4yIDIuNi0uNS43LS40IDEuMy0xIDEuNi0xLjZhNC45IDQuOSAwIDAgMCAwLTQuNWMtLjMtLjctLjgtMS4yLTEuNS0xLjVhNiA2IDAgMCAwLTIuOC0uNmgtNC42VjExMWgtNC40Vjg1LjRabTE0LjYgMjUuNi02LTExLjVoNC42bDYuMSAxMS4zdi4yaC00LjdabTE3LjktOS41SDEyNVY5OGg2LjdhNiA2IDAgMCAwIDIuOC0uNmMuNy0uNCAxLjItLjkgMS41LTEuNmE1IDUgMCAwIDAgLjYtMi4yYzAtLjgtLjItMS42LS42LTIuMy0uMy0uNy0uOC0xLjMtMS41LTEuNy0uNy0uNS0xLjctLjctMi44LS43aC01LjNWMTExSDEyMlY4NS40aDkuOGMyIDAgMy42LjQgNSAxIDEuNC44IDIuNSAxLjcgMy4yIDNhOCA4IDAgMCAxIDEgNC4xIDggOCAwIDAgMS0xIDQuMyA3IDcgMCAwIDEtMy4yIDIuN2MtMS40LjYtMyAxLTUgMVptMTUuMS0xNmg0bDcuNCAxOS42IDcuMy0xOS43aDRsLTkuOCAyNS42aC0zLjFsLTkuOC0yNS42Wm0tMS44IDBoMy44bC42IDE3djguNWgtNC40Vjg1LjRabTIyLjUgMGgzLjhWMTExSDE2N3YtOC41bC42LTE3WiIvPjxkZWZzPjxwYXR0ZXJuIGlkPSJhIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBwYXR0ZXJuQ29udGVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCI+PHVzZSB4bGluazpocmVmPSIjYyIgdHJhbnNmb3JtPSJtYXRyaXgoLjAwNTAzMTQ1IDAgMCAuMDA2Mjg5MzEgMCAwKSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMTk3LjQiIGhlaWdodD0iMTU3LjQiIHg9IjEuMyIgeT0iMS4zIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIyLjQiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTUxNl83MTc0MyIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTUxNl83MTc0MyIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFJQkFRRUJBUUlCQVFFQ0FnSUNBZ1FEQWdJQ0FnVUVCQU1FQmdVR0JnWUZCZ1lHQndrSUJnY0pCd1lHQ0FzSUNRb0tDZ29LQmdnTERBc0tEQWtLQ2dyLzJ3QkRBUUlDQWdJQ0FnVURBd1VLQndZSENnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ3Ivd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhnQUFBZ01CQVFFQkFRRUFBQUFBQUFBQUJ3Z0ZCZ2tFQXdFQ0NnRC94QUFhQVFBREFRRUJBUUFBQUFBQUFBQUFBQUFCQWdNRUFBVUcvOW9BREFNQkFBSVFBeEFBQUFER1A1L2Q3QS80Z2pjUXZxbEtSYSs1NlF0RnFiTG9WaDF1YkN2WHdRdlJFVFVVdnpiUlBOWXZxMlBXL01tMW85NE9oZVRTWVZhTzRHeWJGOVQvQURMZXg1MzNqK3VIaFJZdTQ5TzUzUEoxc1RLaWphSXMvbnN4RTZMTGFTYVhpWVpWZDZGZFE4OWI5eEVCWEFYMGNZT3BON3Nta3hLK2JPdks4T2JRY3BuRFAxTVBvT2p0Q2VaNjJUZmFUeTlvQzRjbkYxWlVlS0ZLRXd3TzM1SUU4VkViUnpMYlJtVk83dXBQY2pWcFl3ZWxqWnpQWnVzOWJxUXk4YVdzZC9ORjYrRXBBeFpObW1UOUNwSUhDRGhReUNIeHJOa0Y1Vzd3cVhFYnQ3aTNOdEVvMXZxbWlzQWd5NVdic3k4V201dVBUT0FYcFdURzhiZ3JRemNXQWFxUVFVWkJQUnlXVE5UaUJwZGtzMHpabFpySVdOQ1BhMTVrcHZUbUJzUnFvVkdwR2UrdUN4YW9rak5WZXJTOXU1dTRXZnZQYkNyMk1GYkkxSDgzWVlZdFZXNWJxekpLTXVOVXV5bjM3bTVoV1k0OEI0WE1udjNRSjVsNDBtKzVITkVjMlBUeHQ1aDAwZmdRNnJvbmcxSWJzeitLSGhJYVhOY0gxbnF4ajBMTFZha3loV2l3M0Nvc0hhaFd2ZHhSVXJSV1lYZEdrbFU4eWZsN3N3TnVZZDBSNGM5aVZKNDg4SXFJaU9pSlFCSTRMZVpOQkRVa0JURm5zK05FUW5WQVphTVV3ZnZKcGU3TGN0S2IramZqalhpb3ZkYVd3VURSSnFzOUx5Q1dGYW84RmZxdWFldkxFM1UveWNtWnFoUjBPU1A0SGxhMlFEOXAyYlBRa28zSFZkZHZMMkdwV0p5TkNkME9lSDdLdU5VRmpLWFVkOG91VGd3bklUdXFaOGFzeVgrbG10Q0hSYnpOazhPNkF5LzJpRmJUZFBOb1BFWFhTaTV5ZWhqYmlWdE4vTzFHUkQwOFlFQk85TTZBZWgrRE5Tb2U1a3dLd3hkUXNSa3Q2dUpVOVVTRkNqbzQ3YXRZdFFDZGNiL1R4RHJSUFJiemRkQ0hMYmVRcjB5NUNHV3pXMEx4M251S3YybXJGNVBkaTBrVlhpZUJXVXR0SnF1M0wrNHVmRCtmUDJ2T25jN3ZGazBQZm5zbjJtT2Z1bUxCeHBHdW92dk9SbXducWtTZ3ZpT1M1MHIzZDlQRWRDNE1MZWc2aHVORWM5WWpoRW5ya3B5MzF3SFY1aEdzOUp2UDExTmxiYU5Nc3R1WmNxemc3TFdLemREenRaTW15RmJNemI1dEEvSTZBZnozVzBjL0dlOWVLamh3N2NLQmxsT2FPSVhUSEQwTWhEVS9LQjY4R3BtNXZLams4dkxQSFZEMzdoRGFSQ2xRdXljbXExZVBVN2g5UGZPUFlCcTNrMFZWaU1Da1NRc0ZwMXM5T3IwU3dvbGs5SFhRM3o5YWtYbE1xWjRGTE5tZW9GWXlpOGRsbVVibElNMlN4dW01UWxRb3pZQjFSbTQwWVdiaEoxKzkxeEJyWEJmYUxBTW9DdEpvcDBrcHVjVUt0VlNvTUZSMHc2M0ZHdk9BZFovTlM2SXpUWnJQQm50RU9xM1ZtcDFFTDg2YVRaZEZjUFJoQmFtZVE4R0hVTjBTWlV5WGRmQVdBbTYvdXFGNklBTFJIMURSVkVZU0ZYQ3pXNEtLdmRwcDlhWEp3OFc3a1plaVplVEpxZWpMZTJBa3dHRTRDSit6MjA1aWdqOFhBY09BRnBqelVXbEozcVJJa25MTFlpSGx1dkttdUlTcWM1SHc5KzE2U21aQ2IrNTVuTTlYd3k2R2pqVG1QQ0ZremsweE1xdUZLeW9EQUI3SVFOMXNHVnZ2Y2M0MkljSE5pVU1NMlUzUkZRdGtPM3VxTlU0bjQ3ZWJvN082S1pmeWVzazJMS1BHT0JYU1pWalJpcFZBOW9xNXJrTjlNdkp1bW9OYkVZeDVMRk9kQ3VqUHJDbEFiczF0ZVlGVmtPTk03VkpwaVRrK1ZScldVVVJjVmNjdlBncXM2amYveEFBckVBQUJCUUFCQkFJQkF3VUJBUUFBQUFBRUFRSURCUVlIQUJFU0V4UWhGU0lqSkFnUUZqRXlNeGYvMmdBSUFRRUFBUWdDam1ZMW5icjNNNjl6UDk5RDMrV0RIVkpuMjBzbHIrVzZ1THFhNk4rYVZWNTgyNWcrUXkrb3A2U0pza3RZVjZqSTM5WkFTR2FRVjNVVVRHdCt5SDF3TUxyRXpXYmtxOEpjS01IZFROL2FxYXgrb2xkNXBpemVRb0krN2JpaG85Z0lVRFB5Wnh5VGtEWnpZM0tvL2RlblRTcDl5OGVQOTFZTzNxOXVETXhXUHVnTVp0OXB0ckI0WFZwVGNvaDM0TksrMDQxdHFrTlRyeEhMMjY4bmRKNTkrNnlQNy8yYTFmTkU2eG1uaW80ZnhrdC9TVldraVJoTnRuS2RvM3Rvc3NNWUN4czA3WFc1VHY0M0kxdmNpdVpUeSt2M2Y5MDBLZVNkOHhWZktlaWRaeW5RUUpFNkpCSEtnOUJWNVdQT0dlRk5zOE5QUVcwd3RWYTNwSnJQaG1jV2QzVmNVcWJCNDMrTmxOc2o3Yk1QSFdJRE1iSVU5QUtnYSt3dk16aVVnalI3ZXZhdlN2VjMyN3YxU0NEa0h3L1ByT05NamREUVdGeHlKbXFTazEwUWRWRWZIREQ2MDRjbGpkWkdTOUZkbG5qVkx2UXBYeExESHAreDlsTE15S2xtZDJjdFNGSkZJMUV3amcxZTFpaWRrZ1RwN211K2tOalpOQ283K1M4MURyS3hKU3RkajdlcGkvSmRjTW5lWS93dXRSWFJXbENaWE8vcDB4V0dJcFN6VE9aTXJhbjU5bDlrK00rVjZQZTA3SkhqY2YycEpNTE9qT0pXRWgvSm9CdUg3QjMyWEh4UlhEdlQ1TkZ4WmxHd3Vqbnl2SitMcDZrUUkvYzNRMS9wV25WZGhLc0RrZ2ZTOG5SNEFkMWt5NC9xUHZyRnFOR0c1S3ZkYVkyc3RYVjZrMkw0MFpWb25SQWFqcWpZczA0aXNLU2FXajExUFppZG9HbE1lbm1sZ1Q5TDJMbFI4N2tkeUpSWmNHWWlJbmpNcXlhYzRXdElYUU1JOWI2TTdUNVUvd0RLNXM3a1RVd0J3V1E5ZVNKcXRTU2RmYVlsOEFLK2tEVEdHeVJ3akY2ek9qSis4TnRhQTBoc0EwRzFiNjBsQXNzRnFrbWtzbzc1OXBDc01nUVFPalBiKzFObmJ3bXdVS1pNcFh3SjJLb1cxbExheGtCMXhJMFU2SFNET2duN09UT1ZEajdLUStVbzBPMnIzeERoWUlrTW44bkJWMTc2K0NXRlNyRkZWVVNRbnltYWljazNSUWhjdzNYSE9tQXpwczdwTDdrTzhMTWxQREl0ck02WDNseUdIRXJGRW1Bck5VQkFwQlc4d01GbFFwUFZYOXhibFBkVkgxd2J6eUd4TW84elc1aXViN3M3ZlYyalNhdmoyZVJqTEVJc0FhS21zREFmajNiS011U1ozeHJTdHNoTG1VS01MREdXY0h6d2hzbE0weEhsUVptRW12WU9QQUEwUkU2V2V4cW5SM2RjRnljT05hSDJJMWh5L2JqMXNJRUVHMnN6TGlRZU90bkpkRTE4ckh1VWhybDVNSXRoelhlc24ySk9zcm1NSHRjd1BZd0tBa3JGWkdMeEZ5bGNTTUk2eCtKdTZOUG4zOS95bmFtNnV2dUlOclJETXYzK1BIMVVNMjdVOUtTc3RMbHY1d2Frcnk4cnZ4cDlQWml3enpvY0JaT3JRU25sVFUxelRqMzFobHp0emVnV2MwTXMyVDNJRlE4bUNFcmtXWWt2K05pTDM4NVN6b3lwSWtKbGRFNWFNZ2hubUhkOFdhZy96TkdCNGkyMHhEaTdUTGNjdXEzS1VVMkwxeDl1bXMvZVIzVy9uR2JlUGU2cTQ4MEdqbTdEWkRpc3VycnB4K3YvQUpWNklwNWh5NTlObUozTlplOHNhZUdLSnlEVy9walZJZDNaRlR4Q2xKdzVWejNQeTRSbjJ4K1NuL0JPR3Y0TkpYSlc2SUNjZXJDYlhURVIxRjBQSEZQYjRUVUJXci9qWHhqWGVzRUk2V1h1OXFSTmJDcU5sNGR1SjRyWi9xU2xIRnNYa1FEUkkxVVZHdGE5UDBMQXhPL3NKamF6eWJHOWlUUDhlcnd2OGZXa0dOejJKU3l1MjJVNjZQSVloc05YQlhuY3FYLzc0NDFKeUlqZkoxMG1vRWdXTzdNQXFUajVMc0duejZGc1JHYmwwYUhSeE40TXNob0xNZ1IrdTFXdE92SkJ3U2pMK09WSTdlZzBkVlBuN0t5dUJkdE11VGtJaU41SnVwNnVPc1g1VjVmR09RYXZwM3dTZnlyek1qMVlqTGF1d2wvUFIyekluSmRJTzFudkNiMjdlY0EvNk9pR0w1ZmM2ZVRQcVZqWVcvVnRMK1ROWkN5N3ZDSzJPTEdaVEZaeXR6eUk1YXoyVE03eVF4TVJPdEMxT3l0YlloUnl2ODVBNmovRXc1Uzdxek1kWUh5Rk95OXkyaXVZU3lOeGk3KzRuYnB3UnNCcjlKVEl6Vkp4NkJYUUpBemRIeDFZaWdpMS9IdDdiVURORXZITk9Sa0tpRzNHMlE5RHBnSjc3cWlOcGlhTTBTWCtJTll1K0h4dHY0ZzJNb2phVnhBTWF5NStMVTB6V2VzcVRRWjUwZmRTTFJyMmZ3OXBzNHE4ZFlvWUxGZ05MTGN3Wk9xa2hZdGpKQVV3TlU3VTF2NXAyY3l5VkU2c0MzRXZjaUZwMlg3NUpvTlRXR08vSXdPaGprN3pCQXdYU3lzcStNdVVtWmtSOVZiTW5yWlJIakJhYTdyYTJQNHNldTBrbHlZcm92S3doZ2FuWEhSSSs0cFA4WU4xb0s1S0dTcWlGZlpRUVN5OVR6T2ttVjdvM1BiSWtzZVg1SnZjMUQ2WWdPY2dpWCtwNjh1VkhreElOSHl6WldRYm1oQ1dKdHRwVW5tdW1PSW5IRFl4eVFxckd2TysvTHJQVFNQa1JlZ1hReXA2M0ZCS2oxOWxtc2tUMWF0bG5RdFp4NjJRbTZxNXFXeWxxeXE0NmRVK0tOaWNVTUxFeTd0czhUUlByNHE2SGxmQVdiUnBicUFhQVdkV1JIQVh3bVRZanhOUnVhRFExeXl3NVBmM0dWbC9qRjIxdnRKRTduMUJ0WCt1YUZxT1g3Z3IvaGxSUHNyaEswaXlsbXAvUXZTeGQwKzhtUEpCY05XRHNraGo1RmxtWHMvc3NzanBXeE15R2VCSEVXY3E0RkhzbnBYaVVWYlorTGdKWXFDUjQzY3JEM0ducTdTVHR5MVhzSE9pTGp3b2NCOTh3ZVdOc0ZyY3VoZnM3K2NSa0EwdVpOdGJQS1JUYUhrSFBQcnRDOHFDMStRN3dTV1N1bStLK1pmdnQyNjQ3cndwQ25PbE9XbnNaSS9rRHRkR1Yxc05JbWpxQWh4UXgxU0ZxTzhGUmVuTTc5WnhrbjVOdmdQV1NUUnlGTWNKMlZVWDQ2c3NZazZOTUZ0S1FKR1ZPcmZjVlJJaFZweVpTMTlWR3NWN3lQS1FiT1AwbWtzSkNZMTYwRTZrNWdZdDNGcStWK3FMbDFhQUdzbGhPL0Q3eXVXcm1yeExNV2lnRnRPVGhRQ0t4ZmxPakdpRVdYcXNOOTdwNHlIb3hIZFZwc2ZxVWJxb2ovSTJRelhGOFV1S25jYlFTWWJUMW5mcC93QW1CM3FJOTBxZjcrYUwvcDRieGZtTThza2ltUWZFbkpnOEpGYTRxTll5RWxhZEpXa0I5TnNTNjlDSU9qTkJKWTJEZW81eVRIT05tSnlES2V5YzEyeFZnNHNZRGVMcHZUcUdvdklXc3FocG9TYlF2a0MxTDdmakxEa0d6c255MTdaeTV2eDN5RnRIeHlROTNRU3JFaTluOU4rMStrRk1JalY2UU9tamNub0EzR3VxdXpCaE9ZN21adnhyV3ZteCtuZjR5eWNWZWNYdnJqK043bi8xRHd6NWFwNkFsNlJQM1BrcEt6MnM3OUNqaWwvdEZDWTZublY2azJHQnFZWTNQWVJVc2pmNFIzT2pVcU4zdmlEUzZLV3pzOC94MVRYbzBabEZMbGpiK3lodDdUVDhXSVJYb1RRdzFoMmJwWnFKU0NpcStpOFRQRkNXT1VxRXlnRVo3QnJHeGxzWnZhNWlvaTkxaVFxUmV3NFdZdEMxKzh6d3hjM2k5NFJlSE1wa0JVTzNKZk5uRW1RWjZjbmIvd0JTL0lsbWptVkgrVjMwa0t0a3pPM1BpSzlOa0dVaGtQd2lwZ0NCNUhJcllVa1h1MnRVaUtQczZ4V1dkdmowVUYyNjJsWGpLblRFVjhWWmk2ZTRiRklTN1ZXV0V0RS9IV3QrVlhQVFRDRGFTbk9DZWdONmJYRE5jU3V0dXZjOVlHUStKVDNOZDR4TkdkNEIxVDdCdnM2b09ORGJDWnJJeCtPcURNTTl1MG01YzRxeW42TXpvT2YrVE5ER29vWkUweGt5azJIc2laL3k0dnYxNzVYZFFxOXIwZW1DMWtRZzN3aUJMRnJZRWlsaUZobTdFSVBCMmkvUVdrNnAyUXlGNnI5dGlMdnJsMXRKYWFqOEcxUGdXbTZySVFUQmdRcjJTOHo2MXNuK1FXUVVQcGdLMTl3VTlYOU1Wem05M3lPZEgvNXduZVV6R0dVOXBaeHZnbkZOMFBJWjBNc2tSbWZPR21lOHlhTnczVDVudi82OGw2N2YyWkEvL2F0YkZGOXUrZjRyK2pNOGdtKzlZN0hQYkdCMGZ1QWcxVUNzUkp5ZE5USXo2TTE4N1AwMStiemRvVVEyU28yZU1zN0VwclNEK0tOTkVIM21rcm0wMUtzUkpjanZIMS8yVmV3N09veDNtUFZzZEZsVWIvSUsvUFZnVC9qMTlEcHIxanZGN1dVbGxNeU8wMkhISmRDOVZFbXJ1MHZqMUZWRUVRdWRBUU5LTS93a2IyOGs4cStWazhYcDZkWGhPVDl4YzhGTjlReVpLMEhUMnRxN1FtcG1oazZyZHEycmpDYTZQa3VTRDRpVHUzTm16NGhFVjVabFpzMXdWWEZjNkhWMDRSRi9jYjR1dEsvRmxYRjhTZkwrcHlxNzdYcVQvd0FJK3M2RklXY2owdHlaWlhzb1FLMEVlcWdTTWV0a2tiTTE2MDA3WkFmRjk0UEdCRjdRTnBuUmErSDhvQXcyMEdyL0FEQUw5MGtuc0txS1JMVmtpUmZqUzYxc1phdGlWOEt5S2ZLclY3SUtkTXgzbEd0bVJZZVZmTTE2ZjdUd0x0Qy9HSU1FWjZ1Ny93RC94QUJGRUFBQ0FRTUJCUVFHQndVR0JRVUFBQUFCQWdNQUJCRVNCUk1oTVVFaU1sRmhFQ05DY1lHUkJoUlNjcUhCMFRORFlySGhCMU56a3FMeFk3TEM0dkFnSkRTQ2xQL2FBQWdCQVFBSlB3TDBHdGcyODhvajRPVmJpL24yNmdoUnc0WUlpZGdFZVZMSHZEL2RSNlJUTGdjTk9lVkVIVytuaFE1U0tmeG9jK09Ld0trQ1JSTHFrYzlCVnp1YldKMkFNVGtieGM4MnpWb3Ivd0RGbDRML0FGcmJFVVgrSGFBL3pxYXd2NC9hamtoTUxmTVpINFZadnNyYWwzWWZWQzdSNUloMWFzSjdKSHVxMVN5c3BiMDIrejdkaVpKcGxDOFpQZCt0UnVBZTZ6TGlsWVo0OFJ6cnBibmw3NnlrMEdDbnhPUHpyNloyTmhJRUJqRjVNVjNudXI2VXdTUlhqQVBld1pLUW56cisxSVc2NXdIbWgwTG4zbHZUajQwRStIcHNsY1BMcU1oNlZjQW9yYXdZUjF4Um5rbFM2M1RRNDFIMzhCV3o3aEFuRFU4REN0bDNEOE9rRFZCZFdwWmNuVWNMS2g0VWV4MFg3WG5RcGZ3b1ZIa0E1QkhOVDRpbmpXOUVNaTdPMmc4SVlwcUdDZmZXemRwM3NWcTRSN3lZYVZNZ0hISGxVR2xvbjREN1BEbFI1UUZmOVZhaENFeklVNThEVnRQdmZZZG5yNk8zRnpmU2FJOVFQQm41YzYraWNFNmNTcmk4RWcvMUdsbzBNK0hHZ0taa2dNZ0R5TDdOYk1GM2RORXFtVXlOeVhnT0FPSzJYRkREOVRWdEFIUGllZExnVWcvK012OEF6VWZIK1ZXclN5N3NzbVRoTTlNdDBxeStyZldKV1pvVmtMaFBIajc2WG4wcGV0ZDRVTWVqaDFSdnNtclc2dUpiUUhGcmIzQVRKOGVOZlF5ODJmYUx3TXR4SnJERStkT083SVB6cVh0VFc3S09IWEZiRXRibmFNZDUyamNSQjkwbnNhUWZqU0ZiN1plWFdPRlAya1hNajNqQVllNnJ1S0MvUmNYVnE3Z0hQMmw4UldZNDJUTTBrcTQzWjhQUCt0YlgzaFU0azN5OW41aXRzd0o1SkdUVzE1bSs3R0JTM0w1UmpocDhkckJ4eXJiNkxNa0lqbGlFYkVnK0hLakk4SzJ5cmwwd2M1TklOZkQyczBrTXpYWHF0MmNuVHA0OUtqM0dEMzRJUm4vVVRXMDd4bzNQWkRNQ3BQbUF0SjdTUmorWnBldUtISHFhMlZkeWVhUmZyVjBBNkR0eHVOTExUWkhsNkFTSDVoV3dhK2ozMGxOM3BiYzlzR0pUMEoxWllpcldTV1hWbFJHbWZJMXFKNmdmeXJSQk82YUhXUWRtVWU0MWNvTGlPWnQyWnRURkc2ODJxL2lzcDU4dnZMYTNWQXpkZVhLbUlHZTFpb2pGQWdUMXl6YVV3T2VWODYyM2JBK0FtQi9sVTBzNWVWWXh1b1RqVWVReld6Skg5UTB1V2tWZXlHMEg4YXNRNDN1b3FqZDNyMXE3M2x0ZTJvZUlNdnpIenFDNFkrRWNWUXpseHhLc09WU09XN1BCSEhESXlmbFcwWEVjZzdVZTZ6cUhtYTdyM0RZd1A0UmowQm1qdC8yVVVlTmJucVJtcERjV3NZSDFtMGwvYUovRVBza2ZJMU9yUkwwKzJuKzFiUXpHamtKcTU0NlUzby90RG1oUW5qWjJxOXo3MktuSUxOMkpEN1FxYmRhbndtN1hIWjgvT3IyVmlHR0RubFYyVGh1d3VHWmkzdzYxWVc4VVRTSFVKVjljZkQ0VnNwUHJrY0JXMEFrM1VldHNEVy9qcEhHdHFwTEhiU0ZRTGRld1NEakk4YTE2VjR5Ym1ITGFCemJGV1AxMjVFWTlYYnB3Y0k1WkpTRHlJNm10bmJLbU9qdFdxeTlvZ25PTzZCNTFQTkMwYnlUM1VPalZJVzA4RkdlbEtVRnZMcjBidjEwTW5Ec2ZFY2FsbnRKSkZaTFg2eXh3V1BVL2xWeGZYdTRtMGI0UUJVWmh6NTlNMWI3bVpXMDNFRER1bnhIa2FsYU1rRGVzeStyMEQyZmZUS2RETDIvL0FLbjlLeHk4YXRvSGpEYVpwNUp0SnR4N3ZEclU0ZFo0ajJ5dkFtcnNBL1YxQndPdUt2bks0WFBhNmdjYWMvR3Z0MURZVzBiTzJpWEdYbSs5UkdTMmNxT0ZSOXc0bEFISTFHN0UrekdlTGVWYkhqc1l3Y3hmV3J4SXdud3ptdnBWYXZjb3VsUHFZbFlmRTRGWGR5MGNaeE5BWnNvMGVvamlvNFpLODYybFlXTUxMNm0zdExja29nNUZzS2VKcTZ1TGxZVHd1MVhkcWhITlNEenlEVTJMbTZuMXhobHpwaVU5Z0R3LzdxTTFwR0ovMmtjQkFaUEx5cVpaTlVlVWtUazYwMFNRU1Bxaytzdm9aL2Q0bjMxZHRORHVtZERKKzdJNDRyYlU1Rzc3TnJDNVgzc2RQUFBuVmdxeHBiaklQaUNQT2swSWdQSG5wOC9PbVhVeTluVDRqdEQ4TTF6elZ6dVpjWTR4aDFjZURCZ1JWdWx4SjdJM3FJUGdvL2x3cUZZUWg1U1NqUDRVUXo0cnBScllQMVNWbVl5U1RTNTE4ZURDcmZjeE5BWm81cHhnT281NDhhbXVMMk82c0JjSUZoMElYK3dDZXRiQ21rQ2JORWtTdGNjWHVPc2ZDckNNUjIrekV1cnFXVlcwcDR4amp4TlBGWVJTb0pJUk5wMU9wNUhIR29sWHRMeVgzMUZiSUpJeHFsVi9XT2NlMEt0bVdXVkRHdDFKSmlJL3c0OGEyTmM3MjJSWW5DWXcyQnpCNml0Zzd0WFBETDV4L0VQQTFlb3JRTndieU9mMHE0dEpwR3U4V1NQYmFzeVlQZDhENytGYlB2TjlPVzNsemNLRkhIcndKcmFObTBNYWRtVGRuZThPQkIrTlhva3p3QjA0NFVWR1R4SlBjcVVUcmdieGd1QUc1citueG9lb2w3Y1h1UG83UGpUam5RK05lTlcwczJpTTRpaDd6ZTZvcmdteXVFOVh0UnRZa2dLOU9mSG5VYjNsM0RxRnZCRUE4cUJqeXo3SXJZdGhzeUU5MDNaYVIvbHdyNlViTVBpcDJlY2ZnMWJKdDdxRmgybXM1TThQdVA4QXJVTWYxemNMQ0dtWElqVUhscDZVNEpQUFBBMUJCMlV3SjQzeVdBNFlQaGcwMDRsd0pJblFkaGNlUGhUY1lFeE5JNDBkcmo0MXRaa1BQU0ZiSXFabW1sbExSUzZTUkZwNVo2SE5iWE96cmlPZHQxZHRiNURBanU0ODZ1SEt4MmNNS0hXZURSdGtTZStvNVpaWkcxTUxlUGlmbFNZd2UyT29xOVc1aWNEVDZqTWp1VHl4NVVsMjBiTmlHMGp4NnlSaUJ4clNiUXRneXArNmI5S0lJWWRramtmUVBuNlUxQkc3REpOanliNVZLN1RCZER6RnNzdng4Zk9vdDdlUHhrbGFwS2FwYTRNUDNpY0dxOTF6N3NCUkh4M2NlZE9yenh3b1I2bVBhTWFZREg3WHhxZTVXRU9HblcxZkRTQWNRdnpxRGRpY1JyMnBzYnRtSEovQThsOEsyd1lXaXdpcHVoeFVlZERYcDVuSGVxOGdqY3g5cUNXSE84UThPSG1LVnZxY2pZV2FIRFlPY1lZRGl0V1R6Q1c0WlpwSSs5a2NNZVFxMGppdTdhUFVTNHd6cU9ZYXIvY3VrWmVONFJsazZaSG5YMWpScU9tV2Jzdjc2aVhRQWtGdmJScGxwM2QrMDV6NzZranZyUGVGWHNwSmU2d09Eb2ZvZktwWnRueWYzVjlDUVA4QU1PRmJjc21iK0NUVWZ3cTJkaDBlVk5BK1hNL2hXMFZTZVZITUYwVjFSYTA1cHc2K1ZDRTc4LzhBc1JFbU9EZjF5YXlacHljU0h3OGZqUXkxU1lyajhLWEdLRlJzSXpxdUhqUWZzRmQ5Mk5UZnhhQWZqVVc4SDJkZUsyWXl0QmJ0TklETG5zTHpQR2tSclV4eXkzR3Ixa2wwN0ZRRjQ4T1hTci9Ra2JsWFNRWjBZNWdFOFJqNDFkcTg3dG9CZHVHdlRxVlRqdTVGWEVtNXpyV09UR1kySTdTZytGWDdxQTJwSVJJZUI4Y1Y5SVpOblhGdDlqZzF5dlBQSHI0MXRGN3U0dVZJS2ppd1h4T0tqWlE2NlI0dDFPS21kL042WXF5OTByekZPV0VGbk5IYUJUd0VqdG5lTjlxa1JrK3NXMEthdXlXREQxc2hIOEpxMVNQZVNYVWVveTRDbUx1azRISjYxd3RMWndTUjdyZ1lMZ04yMWI3UzRyYWlXQ3ZkTlBHb1hNYVNId0huWDd0TlI5L2QvT3VTREM0bzF6cSt0bGY3Qm5YTkVBOU8xWFEweTdSamhiVm9FdU11dWRLdjF4NzZuaWFXSTRrRnZ4VUh3ejVVcnhsMU1aM2I2UzZuaGcxTHVoQXl2bWZneUZkU3VoL2dOU1hFbHVpQkZNc1RNR0h2STQ1NVZ0QlRhaUZwNzZjbkNOcGJUREdFSFVLY1pxekkzWXhyWWFkVldLYXZEQUgrOWJPUzN1eDdTUWdFKzhpbkpRdTd5NFBha0pRcU1ucUJ6eFM5clJIcWQrSjFxTUVyanhwRlpEd0RvZUhvdFgzV1ZkMUl4cVN0bjdpM2VYVkNvWTlsY2QzNSttNXQxeUJxMzQ3d3p5SG5YakdQeEovTDBESkxZRlptY0wyMjRoUDYxYXBabkhxb3lpNlp2dXNCenFSdEg3bDIvZHQrbGNmZnpCOEt2M1dHNmowWEljOEc4RDg2dXJLSkpWeUliWmZXdVR4Wm0rTkNKODgxbk9OWTVFRHo2L0N1Tm5zN1NpeDU0UExqaVQ0NDVWc3R0N0hKcWlsamNhcFJqOEJ5eFZqcGxZZHVLVWQ4ZENjMHQ1ZlR6c1o1NVhYRWFGbTREaFdNK0N0bWtJYUxpNFBoNk5ySkR1cmN2RzdwMkdmN0o4cTA0MmxDeXkvZTlsdmYvU2x5RVBHb0djd0pnek5IalQvRDUxem9laTBobFBQMXB4cDQ4eDUwT0JDbi9LK1ArcXV0RFByQU9GQ08xQVVtR1pKKzE4UjhPVlQ2ZnFoTEkyT0l4MStkV3JHV2UyVmlPZ0pHYXdveUcwRHpGVG5oSUR3OTlYRUc4SmJXSmYybmY5bnlxZTM1ZnM1RjdiZmNwZERiNlI1Rmw0Y1N4OGF4SGRJdVlMZ0RsN21IUDNWY29UYlNidFpDM05PbGJUdUJFcmF0eGFETzhib0Q1Vlp2SGp1bnFLazFhNEhVbHV2RCtub1pzZE5Kb3JEYjI0eHZKT0ZiYnRMbUtRa2lQZllZZVhETmJKdXNEMnJkMWxGT3FrYzF1YmN4bXJRa2VNRW9hbjAvNGlrVkI5WUdyOWxITHhhb2pHN0RHaHZaMXJqL0FKc1VtT3RjRG5nYU52YTdSUlQ2eTZtWUpKeDlublVvemNkbVY5T0FCMXhSTzZpQUNqeUhLdTg3MWI2OTJlenhvdzVHQ1UwZHBldVI1VThDYWx3cnpkN1YwMCtkYktSN21MMU9kWFBGV1FnUld5cFJhdVpPMURHR1ZPV29ZcmFadHlFL2JzTTZhbmxsSVBCdWxlRmMvUmwxVG5scWRrT2VCUTRyYjgya2V6TWQ1L090alcxK3YzTWZyWDBIbnRaRDdVSC9BR244cW4yaEd2OEF4WXQ0djYwOWxjNDZqMWJWcys2Z1JSaDN1SnRlb25tUWZJME9NbkU0NWF2YUg1L0gwSVBqV3pJSmczVjF6VmxIRmpvcTB2S3AwWW52dVl3RHp4azQ2WjUxY0dHMWpJYlZJMzh2RVZkd3ZjUU5DRUZ6RjNVRWhabng3Undmd3FhM3VJWWdOL3VvbVZpbnROai9BTTRWYzJzUFk3aGgvTVZzNVpwcEgxNzlPWmJ4OHVGU3hRUzZqbzNxNmxQWEZYVXFvcTZuWVJmbFd3cExvNTRQZXpkbi9LbjYxQkJFTVlXTzNoQ0tQbDZJMjQrRklSVmhMSVB0WTRWOUpMS3dYKzdlUWF2bFd3cDlwVEFmdFdYUW56UDZWQlo3T1E4akZCcVlmRnEyaTI4SjR6SWNQenp6cTRsdXRRMHdSa2pKWStkUDJUMSt3UnliOGpTOFJ6SGo1aXVCb1VUandvWW9JUFduVEFuSFRuQlBNK05SM080dFJxMHdFWVh6d2U4ZktwL3I5aElvYU10M0pVOHVxbitWV2s4c0REVXJDWUtxbks0em1ya1lFam9WaTRwcVU0YkE2ZkRoUkpJVW5MREFBOTNXcG1SZmFHUVZaZWF1UE9weEdqRERhc3NXcVBzb09YaFVUcnFQQTQ0R3JTU1JtNUxwcmI5bHN4ZE9kRThvM24rUVphdm8xYzdablhsUGUrcGkvd0F2RW10b3JzMjJQN2pac2U3R1B2YzZ2M21rUE4yWXVmbWFRZTl1SnJqNzZQeXB6a2NqVzdnaGdqZVdlZVNUTE81UFNrTHc0eW1qdlIrN3hIbFRhMFBLYVBsOGZDcEt4Uno1Q3J1T2E1ZVhYS1VBQkpML0FLVmZidmN2b3VWWElFMmVPQ0RWcjZ0eUdUVjAxanRmalZvazRpQVlReVBnRThSbjVWZElxaU9XTkkxSEJSSjNzZWZDcFFyYnpXQ3ZNSFJvUHpGRStWTjFwZ1loM2xYclZna2thTndnbUpDZHJoNytkYmFGaENZQ3p3N01HNUdnTnBPY2NUeDg2bWJVSkNzalB4N1hobW9COS92VWZuLzZCZ2VkR2tCODJxVGV6WE4ybVdjNFNKTVlPUENyclNzMHhqNktaR1h3KzFWcFp2OEFmUm8yL3dCUEN0bTIzLzYzUC9UVnVrZitCRnAvRTVOV2VjU0JScGNBaytYalVPaWNkMDNNdW50ZUh2cWUzV1hWeHQ5NTJzZU9lV1BqV2plU05uZE8yTXFQL00wZmhwd1BSMW84aG11OERrSHc2Z2p4cTBOdy9MMVE0Zk92b2Z2RlBUZllKK1lyWXMxaVM3c2pYVWZaMXN1bk9lNmVIaldxV05kekhFTk9wcDNQTStWS1JqdnBuTFZzV2R0UDd4VG5GTGcxeXp4bzlqMmRTMWJmRkRXMEZRbnBNdVB4cEJJbjI0bURqOEtKemJ1V2lSdVFKOHFlVGQyenlHVk5mR1hWVytrTVc4K3RaSTlhRDNmaUtQcjdWWFRlTzJkYW54b3BMYnk5cTJ1SGo3ZVB5SXE4VjVyZVRBbGtHZFFIRlNmR3JnM0c2VlFaU25GanJKYi9BRWtmS3M5TTVQUHo4dUhwOEt5QW50akhBOUs3Q2NueC9LazdYVitwOUNoZ1IybFlaQnBjUkR2d2RGSDhQaDdxMHdXcUlPV1M4cmx1cHJhRXNlWHdVMWN4VXhaanpxNFJKVVhVc1RBNWNkZU5QbU4rQTQrbVJsKzZhbDdUUityWmt6L3RRelNETkF6c085MnRDajg2LzhRQUpoQUJBQUlDQWdJQ0FnTUJBUUVBQUFBQUFRQVJJVEZCVVdGeGdhR1JzY0hSOEJEaDhmL2FBQWdCQVFBQlB5SEFORURuV3pNVEpwcXc2S3ZvaDdFSFdvcjF4TUF0cE9hWFh6RGc2dW9WbVNpQ3pqRnl5dDh6MEpYMDlqak1weEQrSlZxVlNsbThabFR3SFRVTHdQdVdRRzI3cjEyRzRPTkx3RGZhRlRVeVg5ZmNRRVBJNGdxdHRaemxuSkxsaHhuNFU3T2YwbFExSVR2S0dpYVhGT3lWaXVaVVdlRGJ5WlAxRlZZbnk3ckhLZFJpUjJxbEF1aXI0ZzhLQzF4MXlvT2wvd0RPZ0Y2UERLRFJYd1lreE01U1c3azI0Zkh4QkRlb1hodnhkZXlXdHUxTDQzQmZjYS9sYkExZ01kU29CNEZ2K0k4Q3VGcFlRNlRtYWRGOXp0NDZpQ3U4Y0VxdXRWcUN3aXpNd0pKaGdhVGg4a2YvQUZYR2tkVTNXOXpMakttNVZWWlFTUzl1dWprUGU1UjlUUkdQdXpMSUtWOVJ0WERGQm5yOHdjV1g0VFZqaG5tVzhrTC9BTVBQOFNwZFJsT2llaWFHTzJrMnJnNC80VWxZUGZlOEFYQXFVRXQwRGJ4RmJjc3lQQVkvVTgvY3JOdTg3dzBYN1RqWWtmNk5pekdWcUNJM1pGbE12ekd0Y3Z3SmIxdW1vQlVtQnhZeEJPYVAvYSsyT21WVkU1ZWRPSDh5a25nOVFZeUQ2bEJzcEhpbWNwc3JQaytadDJHc1FjclIyM1pNSjlSTHFHSEo3aEJkWG9ZN3MyNzhhWlNIbGNTalcrSUl3REdHZkk2SEQ0bnk0My9kU2xOdk5IL01vWWlyd05xT1pvOXRGVnBGZ1VyMkNZSExzaHBJTFVRQ1dVbmlDdmJPUzdKRVY4c3ZMN2dIV3QvcXdLK3N1bkFFVjVaMHRnNDM5UkhQaVlna2dmQ3c1dUY2MWZoY3hacStlQXlpdWpDcFZaMDRxRUJHMUc0d21uekZyYlpoOHBIcTRMcWh0U3pKVXNVVGFiUFNaYkpCTG13Y1YzcnpFV2R2U0kxR3UrbXc0cWZCUmdiMkZ5MnpiQ2ZPSE9uVXZPc0l2OTdDcmxqM3lvSDR0TGd6RDdOWEtDNEs4UnpvdEhMVll6cHRvSnljQ3VKVEhPTmM0Tk0yRDNEMlVlaitJLzBWZmJxODlYM05jTDdOWU82Tkh6QWJ3enFWNFFNWHlFQlpYMUNYUXd0a0RQRDlLMXgrZXBsNW9rOXJucEVCcjRVUytwaXM1UmZsK2pOd0R6S3E3VXVXMVBLdDhPVjlzcy9Bc254SFBQektIdmUyaFZ2bGIvRUNIb3VxcnhSM01lSCtNQ21sVzZ1TGIwZDExUUxyMjh3NjdNaUhFY1JBNDNBL3gwdldBRk1ZWWFkTFNDYW02R1gzM0hWWjByYmRWekZDT3UxWXM3bmFyTnhOeUJMdUd3TG5yTnpnbHNsdkFjVS9LWTZYNjQzdzlEVWJCMnFDUnpXTEdlcFdHbFN1aTUxZmhHSXc1dFF5VWVFdDNNMkZnYkxTdis4R0xkVVl2UitLYVpHMUtnY0xLN091aTkvK3pCbmF0MzBuczNUaWkvTVNNWExEb2FHTzl6QmJZOFZqeWpKSm1HdGZFQVIrRG0vWjlrcEVnbFZ6UmZPbzdBLzJPWkxlYXVhaUdBN3d0RXRkWW1nN21oRlhWdkROREtxRTZvQUREZE1zcThobmFCMU9JdEtkVTdIS3RWcStVRitUZGxkaEMvRHFEbXl6bG02LzJtWHNZK3dkVEZnT2xUUEhSelNLRjFqTkhNcjlPYjdzOEljb2pRT3lORmRxdTkrNHBUM3ZTKzBaNFFOelpQOEE5TkdBTmhqYktqZUFxYVNGT1JyRXk3bTdWOUVNZWpsdVdzTlhDZTZ0R2FpeldZQlY2U212cTc5UmxBYjJmQjhYdUp5SXpjSlFWNzBRZms1V3RzTEdkMW9Zci9XTGNBMzl3NkszUGN0UHRZTVJjQmJjclQ1cWJqdnhHVC82bkEzWUs4V2FLd3hDMC9BMnE4RmJ2cTVkbXBFNnF6bHd5NG83VHY0NUM1N2h6eWo2Y2EvODRnN3pFbGFXbkdMZmdkazQzY1hubWdBK2JZK1JJZWhvMVhvdVNWU0ZSMnB6VmNaalk3czRQSnpOMFJQR2VUNVVFWmRCK0QyT0ltR080YzNjSlltWFJPNVJrRHZ0TXBWdGc0Z09oK1pMMjUrSlhUU2M3T1dGRnc5TXo0SlhBUEY0dDFPZFIwN3ZxaWVGRHQrUU1hSnNxcjVidmhSTXF4cTJNcG5Wa1c0Vm0zZ3c2eFBUajJYZmtERWh5NkxIOGlwdkRVVjBHcWJKSWNrOFJPL3p5Sm5rTnh1ZUd0b3dYc1ZWWG01VXMrSmtxajJMNHhDd1Q4NzhsODlQZEV2Tk10YkhMUmxEYkd4YnNsZCtJUHNQZEJhV3FUc2pVZXhwUUdUeVRNM1ZNNWFWcms3NlpqclNLc2V4bDFnMFdRaXpWbU1IcjNHbEFIN2xuZG5YbHk3WFY5M0MzUlRsNnQxM3dKUTZHM2I4WG41NWxJdjBmOGlRNjF2cVhVN1g4RmtlNzdJSXhoZExXNmh3NGltb3JCMDB2NW03ZllZQjRSSmZ1SnZ2UXN0cFZQbURWNVprSWZxbW9zWnhLcnpkKzRtdWlzdnhPek1RR1hXUStRRHVWN2dSczdabmd4eERwVEYyRkdIS1hZdlUyVUtCUmEwcmJYZ2xGc0FOVTdCcHZ6TTVKSi9iR0dzaWFuVXdWRHhTYlNxbDcrRWdVZVlmcHJYMGdWbGY1RC93djlZOHdNMjQ3Z3R0NWh3OXhpdmxLbEdHL2M5UlcxN0pTK2ZaVEhSOHZjSEkxWUNZR3Qrb2N4QzZvUytZbzU1VEdSNU5UcTR0SDZYN3dtRTBEa0YzYlVjUnBEaGkvd0NjRjdXNmhzY3ZybTFJQVRrQnpVeGNyeVdOTEFoaGQzRlNublFOUVpxbUp1dEVaMlltampNTStBUzFvTFZSOW85elF1eExTWXYrbWd2OUNXN0p4dVNLd1FWVWh6ZmNCVmkrSHFyaGljeHJrdmJuY0xBdHgxSEtlc3dUVVBGL01UN2tEeHFjb0k0cnFlOTJ2NVRLaGRqb3ZReDdUS2NTdjhBd1FMUDBtMWkwVkRldHd5L3E0c3QrRzBzY3FsWUl2VGVQbEVPVDJoRVJJM1JiNXJVK3BhczBiQW01T3huSTFCVFdEQ0NaaE1EekZ1eDA1VTlUQTFERmI3VXN6Y0JwNTVhdkF3WlU1d0NlMGZ5bFNBdjgvSEpCK2pxdzdhN1ZGY3pIOW5CeDNqZzNTTkRHeWEvVDArSE1icjB5cG0xRzFic3ZoT1lWdXh5SDBEbXRpOGt2YlBxTzF2Y1FsaUZsZ3VOQ0ZoZi9BTnVFSnpkeEdnUUhMY0RUaEljWkJNKzBaYVZZS3M0VDRNWW1vN1BBVzhuSGlhRkZWclYzS1B6R0owZXp3ZG55cTU0eTFNR1JrMHU0ZFdEYnBkRHhHa0xMY3lETGVFb1BOc1dscUtJV0ZEQnlaM09TRmhvdGk1YzN6TGl2YjRFMFp3UmpsZVVsSDg0bUlkQUt6YXY0ZjNCRHRxWmZqdThYbnQyUlB1cmRzb1lYb2xYeVFRdjhnMHlxQ0hEQ2JMOHNZZ2s0a3Y3d2xuVk1wUUtod29rSm51cjVIMUNWbkdvMlhzb1ZjMUNTY0k4YTNJOG4xQUZMa1lyQjhCaHJ0eGdIKzBTZWY5bGd1ZmR4STQ0TWUwUUNyN3FJRDVzenhwdVZBNTZWejRZZWE2MitDc3kxRFFkbnhmOEFRVytQVWNtVEgwdWw5V1k5enhYNEdWNGg1NGxic2kxK1J4VXZwdmYwZjdobVFGNTVaWU5nckJRNm1SWkpxaHZuWC8yYkgrVVZ2S2g5d2lsSi9GQTM5Ui8rQnNCcUdVKzM5UmN6WXAzcFUvWkgwK01DRmNBYzdoMVNMcGJvRDZFclp6Z21tWTNXd0dtY2ZxZ1p0VlRKM1dPcGl1ZEZ0Y0RuK1dWN1R0MVhRK284cU8vSCtxVUNTbHFGOXlrU1czZTNaN0dHWFAySktjRDRTMTVKbnNEWXhONTd6cjNPR0pMMCs0djFUQjI2djRZdG10eU8rdjhBYmlQSGFsSHkzeExadVVmTVZOcjZRdmFtMDJEWlh3eXFPV1hSOGt2VmNMRDhYblltaHY4QXcwaGJ3SlMvaWk0Vml3RXZvZ2dXRnloNTQvY05OczV0UjF4a0plVGhvYWpvK2NQRVBVNGZ4TXJ5T0tabUphR1crOVRqVGluRjlFV1BFMnQ0cEgxclJHZkVjSUxTajVVUWpkMUZCb2o2b1BITUs1V0wwTkMzMjFtbk5RaXlCRlVsWVJLZTdyTW9xQlpiT0JZY3d0NXVZSHVya052c3VMdzB3YXIwV0xsQkV5VStsZnpOQ2lvSlBXM3R0bGJHL0UwTE5PZVNZUXJ3WmhHN2VYNzRKUW9kNXArMzRHTnF3dG03MnZ4SDl0NnUrZlViZ1ZWa3RLeFJ0Z3AwRmhFQU5MbkhTR2R1TjhINFBVb0phbHdmc0dXaW9qOXl4cGErQ290MUFpZVUybFY5bU9JSGtpeTc5YjFXUEU1bmprM25pTmNLTXgzaEh1UkhaU3Q1eVZ6QTBxYm0zQ0dqdXpuZ2pla05VaTFvclFqNmpwNFNZMks0OVRpdU9pbzNHTTZrSENmK0NyekdyOXNxL0JDVzNuSDRQMWhDa0lWdXZaOWxVNk5OQTZXdjdUZkFpVDgwTWtML0FLdVBxYm1YM0wxSkJsaldEa2U1aytFNEp5T2Q1TXhYRnNaRTV1VldONVplT3p3eGl6bGFZb3dLM2N1OXIyek9QTkNRclJycjNBNDQvZnA0VmtUNWdJbkR5bWxRNHNES1p3NkJRdDRYMUxQUU9zbCtpbzM0bGdPb1Y2UXZnTWt0eFdtVEJkVGdzZXZBeWdKbW5QTExmdXFNdWxvbEtHdXBWVEJZMHhmbEZaSzZsQnNndnlPZVhtTEtWRlpQTDZpRnF4ZHpEeWdCRUJhdE01WXMybkJzMGZpWDY5SGZSTnZxV2xpOTJabWpaMjlRbTVXWDh1dDlJN1ZQbjZRUDNOOW5YOXJmcVhSVDF1Y0JSK0V4UFFVblVhUDdSOG90TzFKakZxNlh4THlWcmVmeWJjSlRxZzVySnppRS9wQTNtWmNMcndGNnJYQVc1NmhWcTRHQm02dDU2aElXTzFnL0JPYUM0ZjdHWENzTkV3dGl6S3RKcmpBenEzbkE1eHhDdGF5OGU1VlROR0duaVpyT2lGTEtBb1NmaGlRcTlQNllrY2huWDRSZWZGLzJ3K28vYWtteHFXMnJqekJQdnQ1dnBjVFVYZEJuMmhyNU85dnZIeDM0aHd1cVJtclBhS3N4aXlCSVhjZXB1MkV2Y2VMNnFsY2gwRHpEVGM1ZGdzQUhLbGhIRjhyL0FNKzIvY2Rwcyt4aDJZWnh3R2hkL3AzQkN0TTVrbWJmUGN3S245TUVaaUl0MitRMy9wNnFXZ0lhS2F0VDNOaG9INXYvQUpQOWpMcWVoOU5WQUlJZDFjTGx1cndUWlh6S1FhTnk5MkRjYkpmSVRNTmNBeVBPOFBjTlZ5ZHk1eC9WZ0FUbisxcy9OSytDZi8vRUFDVVFBUUVBQXdBQ0F3QURBUUVBQXdBQUFBRVJBQ0V4UVZGaGNZR1JvZEd4d1JEaDhmL2FBQWdCQVFBQlB4QS9BRmdlTjV4MzZwZ1R5QzljVkxuM1pEcjVBY1N2TmY0Z2wyU3A1cmlsMmJxMGhHdFVId0JsTTRVemJUamU0R2tUWmtMeVBPQzhnd0ZZaC9veUZlVUxic1g1bUVETlgvV0pUZU1GREFWMDRZNTZKaDFDR25naEtNT0tvSzJ3ajcvbU5mWWZybFdVTUQ5WkZlYnJ2K0dPOGZiNU56SXpBZVdCblRTaEtTSGRrUkJHSytMNEtYai9BTGhSQ2NZOEZnbys4UkRncCtOWE9vZXlST0h0TUJ3VUxrbHlIZG9jSCtobW1LU3FSTmdwamI3UVUzdUZ0aGswWDI0UTZ1TlRnYnFZSXNQUlNvZkZ6em1FZVRHejI0YmNDWWdCRndRK1IzRHJHd0F0cU5FTkNhUXlzSGFuSTNXTFFxb2ppblU4Z1lEYWJPc2RPb1dadmRkVEJicU5MVUt4Z2lQTk1HaXFCWmtQL3dBVDFpMzNRQWFQb3hWOEtFN2laMGZJWEprZXNhNWRuNGc0elBJRFlobk5DSUFlVE5VZ1hWcnVoeFhabmhybHZBd1hTTDI3Y0ptS2pKdy83OGNUSmdHOFFjblpVWThiTytzQ1FPMUlHUUFDTzd4eG1IWDVnRm16d1hHVW9laGhLeEdrVHZJNFJNT3pwYU1MRlk2dUNBMXFoV3dQR1J4MHZlNlhUV2dBM0N5dG1rQURROUgvQUppdTFnQWcyQkNxNDE1MlgvRmpDaWpPQmRkUUMrTW92RFV6M3VGSG9RS1lsRWtJMTRqRTV2dTRDdHdrcnE3REUyaEd1R3VwOGY4QXpBK3RLblFEOS9wVE51SE0zeElLQkxHT0dhZGx5VEpqN0VJVERYMWtkeWh4MVpwMEFuRDZBYnhhZ3hTRlFBNlc2c1pSNmlhUytDRk9xQmkvK2VvdW1hcnB0ME1uM2NKSWFxNkV3UURFY1dmdXNUVzNDaDFhY2NoREh2OEFKOGNiZklqWDh1V0RwdUY4VG1JeXh3V1hicnQ3WU5QRGplNUVFODBxUzk1RHVrSUN5VWFqenJaMHh2YUFsZkl2S2t3TG82Y3RVQklSOFZnWCs1OWNlbDRTRGdzdFN2Q1EvaVpDanNnK0dRQ2tDZ0o0TDV3U252MnBxSDR1UkRRdWo4NmFmSTVRN0JBUytFZlNZZEc3a3YzRHZ6OHpUMU1UVFk0eWcxQnhVcU1xOWpFbWpRNllBb1F3aUVvblVsVUNKU2lEOFlQUytXVmsxQTl0Sk4yR0wwYTVpSjFNUWh4SmptNnZwREN3WHR0WEZ0SUlOUDhBNGVUemhlVGtyY2dubk5wWUZ3MUdzZjVqRm01dURPeURXeGxUamxzWjNMblBrTVR6Zzk0MXc3S0NRbmxha01ReVE2eDZyMHpzVXlTaVFwT0VYdDA5eG8yRWJwTW5BQVhzRnluOGp5VFBiWTgzR0ZCM1EzaUo5aWFqcTVzcDRGcWFqSnBjTVoybEpNMWZmZkU2Ty93UEtJcTdNVmc1SmJ2V0swUUVnNDROY3BVcjhpc1pucmhrQjFmVm5BT2ZiWmh1bXpUKy93RG1HWjJNRVE0YWtHRG1ndDZQZUMwUTdCdnZKYlJVNmdnMkdMenJGb3ZTZGhXOHRtRXhtT2xhNkEydlk1ZEtQdzJocDNvOTdCZ3dNYUl5bGlTRldzVndpR1VXc0ZHVXA0YUJneVk3cDRtR3puZm94NmJVcDJzTVVWU3B4aWM3dEUzY2tka3JUYkJHUnJFbE1xNGxRdzVCaWhTZ21MbjFBQUhDUlhsRzRHYUNVWEJ4UVRjUVBnaElsSmoxQldpKzB3TEVJZ0JTWjhZT0lNcVBlb0hCS1Q3NytDSzhPcWlTRTUzS2RWY0o2SU9KcHRlNDFJZGlFRk9naHM4WUJOV284Vy9zc3hHMk1XbmRyeldCNU5KTitNM0YyQWVaZzFhWTdVU1BnZlRqVURvTFUwR2d2ak5xc2xBbjR2Zy9NaTdnR01xcHk1aEEwaHZxaFRhMjF4cHV2dUhiclVTcTNobmIzOHQ2YnlCVTRybWlLemM1bmdXcVhMR3RLMVRmNG80VENLc3NoQlNHbXdYRUtRSmFOVmVVSXFZVStNMVA0YWlXRHNJemVNUEN0Y3k3WUJBcWdZTWJBcXFxV3pDU0J3ejRmRG9xaTMySHhqSDhrYVE0MVZYZFBjVTRTRTlCY2llRXBXSmlWRzZvb3dEd0Uva3hqYXRxZXJ2RHhZUmVoUVE3QWwyZGNWbHFiYit1ZENnM2FGajVNRXd2Z01XYVFMRzkydVI1VStFdjFqbVFKQ2N6dUpNU1FwQU5nSlJjUk1PblRhOG1oRFR2RzExaWlseTNqVEV2MmhyWWRRa0JmRm9HSHpXUzJRcExoUFRWQXgwMFIyVUlqSmlLenM1bXd2c3YwNGYrbXgxYzRDR3B2dkY2eGRSbVVlb0l0NEdNelJZQk5jSWhzN3Nnb0FZenBsNXN0RFF1YzNidGdnZkNhUDhBTEF2ekQ1d2l5eWRBbXdySkFyYzNFQXIwaUFCaW1TZDFLVFE1QWRNUzJKbVpRcEhmeDlEanJyaTRsMkhxMmNiTWEzSUpGUUlEWnFlTUlNR3Z2V3Z0SStNcnpjUWVISHFDNUxINXhaVnFRTUhCT0pkZkxrLzFKZUtrNzR1YTJJWFNhQnFFQ2pldU0yS0NVTEFNUGxKVFF1R0xKdkJhOFpvRnc0NGFWQXNPb294bWVXSkg1OUZNYzloTjYyU2lkTUF2bUF4dlhOc1RmaldEWEx2U3g5SlZPamJjdUVjQjVoc0cvYUppKzZNdlFhV1BDSVRnWXY4QTZsQklnUzdHUzU1Skl3NllEWTh3QXN6aE9CZ0VOeGs0UktRMDNZRkVhSlllWHR1dmdRNkNoK0ZseGZGbC9XZlVMZ3FUMXJqdE5vanF4eDZjeUJ2U1RxUE1VU1l1K0t2eUFtRVhQcnErNW5WK3ltcVlUSC9jRHpnY1pFbXhEZUVoWDZOUGx6VlhjZm4vQVBXYmpaVld2K1MvNWpCVlJ5b1lVZkdhR1FISXhqZXI1dTNzMzh5VDJFRGJ0UjZDOTZyZU52S1hZQmp1T3BMMEw4WUVIRUFCRnRpQVQvTU12dEJTRlQ0UHNmWm94eEFBNzFYVk1oWTlycmJteUhsK1FINVdqT05Reko4VUFJZkN4ZVVOb285b1lVRFlvWHdJMTZPS0pnQ3VQTDI1Kys3NU1aWUVVUU5KSUpNYUJjUDk5aThTMVNPZU53OTdYdUpNb0tHdE53a2MrRnBDc2RselV4M1ptaUFQSlFaYlZLWnJSbVlpS0p1c1BrTXluamEvSDg0bzdIQnN3V1gyelV3dkJKWEVheEprK1B2R0dQTElOdStENjRtWHR3ZTY5WnkrMWVjSHVnSHVjYXlhUmF3emRjanB5bEZJRGhDcUxtN3FJRHYrUXYxTWZpNk83R3d3OU9HclIrWkdFQWFuTW9HRHZtTVZKdmdPNHMxMG9SQ1M3V3NGaEhWcDM3bi9BREF3cUQxS0lLMmVUNHl1MlMyNGlpQlNoU29tRit1S1Izbm9iYWE4REsrTGhmbEJGT29pM1pPb2xHbmZMMWM4ek5RbHY5SU5zS0xTc01MSzNBUVhUOWhCUURqY3dHMmVraDBWaGxlN1NJUWhZc0Rzd0ptbzZjaFMrdFl2N1A3S2hEUUphWkVjcFdydkwwbkc0bFNlOGFMd0FGR0g4b09pdHdWZzZUZzRaQ2NWWmU1WVRkSVBXUWp2MjJCT0hsU1k4eWRGMDJoNXJqRTVQYUNLSDlZTWxhbFBkNW9RazZrUEdOblI5YkIyajhPSkRsSTFsWWt5M0JuaEQ2d0x4UFBhc0RQRUlUQ20rYWhwRUZSZHRyYmxNVnBtVFNSK2FDTzNIMFA1RVNoc0VMV0NaSFZ1Q3RvYW9yWndTY0UycXBhdWVaV0llUTFXYUhTaFlYWWVjV3V3Q2RkTjlaeEdSTDZibmRteCttU3FQdFNLc2tBU2NINEZualIwMmFQY2VZS3NnbzZoTjBnQjBtS0FBQytyZ215MTdNRUtaZzArTTcwYjVXSXY5SUhNNzVQTVo0OEhyQk92SWZ3QitqVEtoaEIyVmZ5NE5QQjBNN1VhSFNBL2x4V1B6WTlCeUJ4NTdERkNUUkFJR0JJNnhjWjcxZnNzN2pzRDJ1RDN1cXljd2NlbmR3d3U1dEFyTHVEWjBHVFpielFVK1VoWVU0K1pBcXAyZTRmREd3cCtpaVcyRFZwZll4YWNTeVFtbDFob2UwZGFYNHdwQktLQUVhR1ZvRG5VcUJERFV4TFNMc3dlUVN2dmJrNURiVXpaK2tjVHFpMHB2QWJFeFJscWdHOTB3MG8yNHZQT1hQWXQ4WkdCdEhxYi9hSDg1cDdYRzh2S0JHTHZaalk0ZnRQbkN3VU0vdkFkQWhHbWNkMGVDeFI4UEJGeGlOV3lhbWw2d2NhaUtweEUvd0M0bmQ0TE41T0V6OVFEYnZFRXRkVGVXSTErbVpmb2dud3A1d0NwSENJYlZVRXpGYTJQSTZzdmVLOHZrb0FIV09MM0RoOWZNVVgzckUzdURlZ0x6YmViWGs0YlhGeDBuR3JDYVJKaEQrcjFackJGOHRTQnRsbytnTmx2VXdIa1l0eUhOWWdWQW9kcGhoVWVDQ3hTQTg1ZnN5QkNEU3JvTDhCZkpqa3VCMExqeXluUmEzUnM3aHk1QlYyaXNCZEI3VTRqdXB3V1U0aXlrdzNQdWhwN1hmdEdQVHRGTCtoWUthM1dLUFRXK3JsVFNHL3Z5WU9zSHFlbGhLMDFNY1lMYXBHNHQ5TVd5ekUzdVB4cGord0JDbHNRZEtPRFIyVDJFTUZWVW9WckNJRnJDdG9Sb2dKUldCdkhKZDY2QjgxUVhIZmNEeVR3K0RUREs4eG9VRi9seEtJamFTY01FUGN4Mnd4SThyT0dSMU11REh6VlJCU1Z0c0p3b1hrWWxyYTZ6RlB5dnJseFpPbWJIc0pESVY4eHMvbGptZkRxTDVWVjQwVEVFVE1QWW4vTGhNZFc1YW4zNnlOYlVpTWJoekFPa29LdFBYckhpckRPeXRST0dHbHNUNFJRWWZwZ0ZaZ3N4OGY4c3hSYTZXWDJnL2NEKzJ1SWcvM25CaU5SM0RaV2c2eTYycGxXcXlNdDhaMXNzaitBRmZRNDBJVWs4cXlvV3p5SDJQVENMMk54L3JYOFlCbjVvL2daVFRnRGNmT0xMMlVwbFdwRVFWUE9HUXpDZFA1clVWZ3dNODJMN2lpbUdUZkhnK1dYdTNJazZNQ01ybmx0aWhPTG9neEVlTEtLMkpDZWhJVFJpQkh2VzhBSm9Jdmwxa2wva3RZOWtJRlFNNnlieHorZThpWXdNNGJkZEJWYjJpZVhJNHdrc0wvT09YV0lsVDI4d2xXalhYK0dUbDZJWWZiai9PQkVSZjRmMGZqSkJZcUp4NnN2b3pVR003NnJpNVVOMnNBZzBWTnhseHNkaWoxSFFtaVhlVjk4RGtvSTlLbjVsd085ZHRlSWhvR3pMMThTTVI5UCtab29TTFFudVpwOGsxUVBvTXBERWlFelMzMG85U2FqNXdNM0ZMN3VvRnZvRCtDcmhtWWh6V0cyenJkcUE1VW9BZVFRSnBvVlFBOEVKY3NnTDBYU3hRWFpNN25LUTBNRm5EMDRLbW96VnFORVlnVURDWmxOVk8rNFh2Z01TdzVzc3F5dXlyN2NjMVl2QUdLaElkTTVnNWdTK3ZrcitHQkNBRTYrM1g0YzI5VmhuNGdSWVcrR0FmRkpQNmVPNnVwR3IxWC9BRndLUmNTbjg2Zm1LTEQ4VFI5ZXNXWXZScGdvSHR3RFFKc1JLSmoxaWFkQUpyUkFpY0Fjbng1eHZZYTluakRuZEd4Z3lIUTg0TzNady8zRDIrSUdLQlcwTkg3aXkyQit1c0F0SmdnY1V4YUVHbHBpNkxRVUQ2SHZON2gycTZoSG91TXdmeTdtL1BCNTB3Y1U2YUVVaUxQbERqOEdGS0JGdGhES0x2TDVvQWo4Wkt3dDF6VHJlYjE0dFNSRGFBTnNIVXg2bEdiN21qdXNtaTRzYTc1eTRXR2FZbHgwUnVvdzNhVEMrQU9OMmJrWWVDOHY0NW9QelF0SDBHakVjWERIYS84QXdLUk8wMHA3RHppUThOMTBmeGl3T1llaEdpY1A3Y0lPbUhOb1JPYUFlemxEWlVMSjBJQ2hsY0ZEQkdRK1ZkbHg0YjFrdjd6OVJsMTJ0cVVIeTM1ayt4dzFhd05PbUxuZ09Iem9kVlZDQzJBOTh1S3N0M3J5Z0o4V0t0TUVUV00wT2lHeWVBMTZCNmQ0Uzd4VXRaNURLdjQ1clp6TGFXUVBMaDhyNVIwN0tRcFdFZHlpWFplOVMxZ1FjSEUxVHQ0aVltUDJ3WE9LZVNTcllGREc3dXZaMFFiTm9ZbFl4NkV6QkViK3MwdjgvRzNSNEQ0d1k2eTk5K09iamhJZXJuakFTQkdnRkF5NzZlYy83QnhmNDhEL0FDVCtzdFI2RUQ1cVAzRHNLSkk2NFVKWEVaVEJzSURvM1BnZUprR3ZqOGtYNHVBc05aYjZVNXlEV0xwS1ZRNHBjQXA3M2RmTktoSTZOL0o4ZEJ1TnE0QzVLUm85WjhRTEVtS3VTQ09uT0thVUlwY1ZxMExuSEUveCtOcUZOVlZYMThrTVY4TnVwN0I5RWI3VU9HNGNNQ3YwK0QwWTdnRW9LcHZOTWpFbWVoSW1GckhhMGxWS2s2MTViT2xteFh1SENRTVY0VEM3MW1yVUlrMTFROURlRkwvcWRzL29iOFlldVNRSmRNYXNrT053aHlJYm8yK0VSelRjMUpzbzZPRnRNL1dEek9sdTc0eHlDNzNuTU5LUUMwcnhNRzA1VlQwKzhENWdWZVp6ZXo0RnhhazdhaEp1ajlRUGVmL0VBREFSQUFJQ0FRTUNCUU1EQXdVQUFBQUFBQUVDQUJFREVpRXhCRUVRRXlKUmNUSmhvWUdSNFNPeDBSUWdRbExCLzlvQUNBRUNBUUUvQVBFWWM3SDdRNGg1ZWdSTUxJcEhhTXlvYW1FaklTSTZVc2Z2T1lMNEVUR0Y1bWdkekQ1WUhFY1k0ck1oOXhNT1FPSzdqbWJHQlJNbjFHSXVzMUd4b2k4WEZPTXFUVVYxUEN5aEtFQVdJTis4VXduMEdPdXJjUkdaT0lydGZxakcrODJFeEtPWUlZeHFYdjRLYU14WkE0M0lGeGNZVGNHWnRtTXhnbHdGZ1J4ZHhrSXMzdEZiRlhNS24zZ1V3S0VGQXdDaE16bFFRdk1IVVprQlZUUW1KMlpDVEttV3dJQlVWYmliQ2F4Q1k5d1Rmd3hPVU14WkZjVnFCUHRPcFdtdVkyS09HSGFaOG1Rbm5hWVdBYW00TXlZbVJwL3FGQ251Wmo2d3Jlc1E5YXRiQXh1c2Roc0kvVVpXTm1IcGM3Z3NGMm1OZEtFR0x2RndQbU5EdEU2QTk0L1NqRXBZVkxvVFZBWWFJaFNwWGdCT25MbnVKMVNpZ1lLcUVJd293WTF1b2JWS0c4UVdkNFVDaUxnelB3cGg2Zktnc2lwNVo3bVl1cHdhTkJQYUlFRmdqY0dFb0lNcUt1MVR6bjdSeTdyVFN1M2d4b1ZCdEMrMEpnV0FWTUMzL3dBTCtaMVdOM1VSTUt3S29IRUNnWE1weDNzWmh5MC9xTzNlWWtSTjFYbjNNWTZiSkYvSmo1V3lNYTIvbnRHQlhmZVkzbzFISURlbitKcUZSU0N2WVE1SzJNOHkxcWF1OHU1UU94bmttZ0lNSUpubGdMRFhoaEM5N014RlNLaktjZVFyRk84ODNwTVlyVmZ3STdveDlJMis4eGRNcHhNQ2FtSnJYaS9rek8zb3FnUHRDUU5vU0d4ZW1MN1FXZG9WYlRxRXhLUmRDUGpZeGNWQ09wUnZBR291VmU4T2RLcFkyUy9IQ0xUbTRNNjRqWm1icUJrSU5WVTgyS0VZYzk2aWRPSEpBM2hTNWdWZCtUT3FwSy90M2dVTnZjclJ1RE9kNEN5azFCa1FyRVdySkJpQmZhQUUzVTZuRVVHKzB1eC9zSGdnMU5VYkpTVUR6N1FJNzd5c0k3M0x4KzBYVDJNRHNxNmUwZDZuVEFsRHpPc1E2YnFJaUJMTUFYc0l5RU1BSXVQK3BSRndkT2pPVzQzUDVpNDhXSmJQNWo1TlZtWTNMblNkdjEybVlMa1RrWDcvQUdFcTVmaUlJb29SUlpMdEhZdDRpTEw4MXFVUkZDclcvd0M4YkdjcUVDdjEvdk1icXRxWjVtTld0UlBOSm5Ub1dKTlhEblhFNUFHOHo1RG1jM0UxS2Foc09EVnh5eFhjajRFNmpBWDlZK1Q5dnREOTVvUHpOTGUwQW1QR1dQRzBvNnFqbnRLSmhIZ0I0ZE0rS3ZUeng4OS94TjJIOFJ5NkN5MHpZZGU0NThFVXR2TUdJVlZSV1N5QUwrLzh6T2d4UHFBQkIvRXhuek42NG5wSnFVS29WQ2FXbytQSGtPL3VML2lIcHpVOGhoM3ZqOC80aTRNYU52dnYrNG15NHlLdmF2MGlHclBpWlFnTVVSTWo0c3Ryc1lqQjAxQWJmZU9vQnU3bVhKZGdRNnJKblQ1VitudjIvd0RZV2RCc1lRMlE3bUpneUkzMm1UQW1SWm94NE84Umt5Q2hETHNHb3QxUjhCVVkya0hIZ0FBSXpSVFVKRXVPRUluU3NOSkZYODhUT1Q1ZHo2VitaalhuZU1BR05URTRiSFJvZjNtT2daaXlBTnBsQW1aaWI0aTZsNDdTL1RNYWFHTncrQWpmVExnbGdpQUVFd3BSaTR5VEZ4N1FZeFJtSmY2aEZUcVZBeGNSdHp0QnJ4bTVlKzB3RXEyd201YXJsRmVJcGF0b3lzRFpuQ21ETlFwaFBNUTl4NFVQZVVaUnFHRHdXeDhTaEFsTE5odEE1WlowL0pNNnFqaW1KQ1FhZ3hBY21MaUFGd0FYeE1RTndnR0lMSDJqR2wzZ3lvUHRDNEc1M2hURys1WDl0b2VtVGtHb3dkT0d1RE43d1pSMzJqMGQ3aWVGa1RXWUhNQkppSlV2UUtITTg5bHNIOFFPRkJBaVpkNk1zTzEzRkFab3RocVVSbGNFaG1BK1A4bUlpcDkvbmVNdGlFSXZNT1pCeEc2a2djenpYeWJLTGk5TG1mNmpVSFM0azUzTTh0UGFQakhhSDNnSThWOEVPVmt1YXlJRURxYjVpb0c5SmhSbE84VUU3VEVvaWtvYkhNdG1iZU5rYkY2YnVaT3BKSE1EdSt5aTR2UzVtM1kxRTZUQmo1MytZS1VVQkFHYllSY0pubG9JMVRKakpOaVZMOFJMQ3JVVkMwWEd4SUpoVFE4UkE1L2I4UWRPZ0VZQUdoRUFKTnc0L1NTb2hWTE9vd2VRdkMzODd3WjFaYXFJZFk1aTR4VTBnUzVaSWpNSlphQlBlWmNJSDA5bzJNam1hSUZNQ1JtRmJ6RmtDZ3hjNkJycWFpNzNNSzJTWU9JVnZJWXBHSVdabDZobTJFQ01SWmhSUCswRmdiRzRtVU5GekhUUE4yM1lURzZ1dHpzYWpRTVJCbFljd1pWYU11b0dISGR3WUx1RERkaUlvY1daU294MHpIZ0RpeE1XQlFPWW9xQ0Q2Mm1kbFhIVVFBV3hoWXNkNGVJUlppekU5N0htQVl5L3FFeEVBSFNJK1poekF3YXg0SUpRTUNnRFY0TG9STE1aMjdiVC84UUFLQkVBQWdJQ0FnRURCQU1CQVFBQUFBQUFBQUVDRVFNaEVqRVFFeUpCQkRKUllTQkNjU09CLzlvQUNBRURBUUUvQUU2UmFPU09jRWhTZkxrT2ZOa1lPU0p4Y0VRZXlDOFdrU2xaR1g0SXFmNUk4LzhBVGlwWDhNbkRoZndqb3N4TzBOMHJJemxMNUh6Rkg5aTh2emprb3FpVVl5MHh3aWxvaG90azVQb1JFaWhlR3JST0hGNlRZNVgyWWI0a3FVSFp5aFdpRTdWRlMvQW1peS9FRmIyTEZCdTJ0a29wUzE0eCtMSmRpalFreUg4SnhVa1RnNHE2cEdCKzBrbEtMUmloQ3JKcDFhSVRVa0xFN0hndjdXTDZaL0xGZ1M3WkREQ0tJNWNhMDJOMi9IcXFDc2YxWDRJNW5OMHl0aVhpS29UVC9oa2pIOE0rbmJWcEd4Y283UnlaR20yUG9qS3g1SUx0aXlRYjBMSVN3NUxiSHk3VDdFcE1jSk4weFlrdXlLakY2RUttSlcyelZDaUpGK01qcit4Z21vdGp5UHRGeWZaYk1YT3JhSndianJzbEtUMDJSVjZJUVVGdlpGcVE0a0UydGxGU1Ryc1VHMW9VSFlrcUVxTGFkb1dSYlBVYU9Uc1hpZkpERlVvV05hUFN6U2QxUkNNbDl6SlpYelRST0tVdXpFdmRaRzNzaW5HZHZ4cENhdHBrcWZ5UW1sZER5VzlFR3BJaU5XUEd4WXB0MnlNSzh6cFM2RmlsTXg0dUNlemgraTVSSlpIL0FJS1prYlBwOTJYeFZVTGZZalRSd2xZM2ZUSFoxMllaVyt4THhyK0RkS3lNTGRseGpyNUl1YktuK1I4cTJWRnUva2pBeXRXZlROVzBTbEp1a2U3NVpHZHhiWno5bmRIcXRSb3ZKTFNJUTRrb2NWZlpoazRTK1N4SVNFUHJ3OWwxN1lrWXFJdWloK0V1Q2JiRzdka0pjSFpLRGZ1UW9UY2ZjeFkwWkpVcUZqYzQyekZEZ3REcHF5Tk9MMktrMllzcSsxaS9RcFJvMVJaT2FTRTZWa0kwdkNZaCtNa1pwN0k2SXFMdWtZc25CVXhkTWNrakpPMlZKTHN4UG5IaSt5ZFFWSmliU1pleGQyUXlUZ2lPVk94WlVQSkpvVGJuM1Evd0lzV3hGZUhGU2hzbEhpMm1SYjZNZU5KV3lOVlJsZzZzU2krMEp4eHJSTEpCeElaSEF1V1VjWlJFS05QWSs5RkZNZ3FrS3I4eFd2Q1RLSU9TTXkzMllVbk1XNWY0VGswUmJjZGs0OFphMnlRNE9yRVlrcUtpL3dEMFYyVGx5aUpVaGVJOWlWbEhUUGdVclRISkpEbXhUZGsyK0ZtRDcreUgyN0Z4a3FFblJrcHgyVXFJdTdYaUR0VVIyejBVN3BuQ1NOK0xRcXNqNGFvWmRXamxiOGVueFprMHFNQ3FaT1NYWjZqZlE1M2FMMFRyaUpqb1J4azBMOUVaeldreFpwZktzaTR5dlZIcGFIaVpEMnFtUHdqaWppaXFISk5DWExiRmppMWFPTjdZNGEwS0xqR2g2anM3VzJKeHEwckpOdGlGYjZJNHBNaGdzV09NRmJaNjJLSFNzZjFHUjlhUFVsVldZNXRPbUx3dk5iR29KaWdtaStBMjFzVFRXaVRSS1IyVWtuUkdLbVJ3b1VZd1h1WTgrS1BTc2VmTExvNzIyV2prV3lKaW5TOHJ5azNLeHo0anlLblFwY29VT2JpdER5U1l2Q2x1bVJiUCtyVHRucHUrenI0SEovd1M4Y2pIa2U3K1NNazBKNkxPVCtDRVg4RW9jaDRadEZLRUJ0OUNGcUpUbDBReEpiWnlTMGlNNWZncFBzZVBpT0d6aCtocHhZaU5OSEJIcG85TnBDZENuUjZ0SHFWVEpTY1hTRTVTamJKWlhGMFNtL1A5VEZGdGpmOEFWRVlxS0Y0ZXVpY2VPeTVLT2lWM3NoQk1VWEh3MktSZDY4VkticEVZcDM4bi85az0iIGlkPSJjIiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIvPjwvZGVmcz48L3N2Zz4=", + "description": "Displays the latest rotational speed telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'rotationalSpeed', label: 'Rotational speed', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Rotational speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 4000 - 2000;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 4000) {\\n\\tvalue = 4000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"square\",\"autoScale\":true,\"showLabel\":true,\"labelFont\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"#000000DE\",\"rangeList\":null,\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"360\",\"iconColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":500,\"color\":\"#2B54CE\"},{\"from\":500,\"to\":1500,\"color\":\"#3B911C\"},{\"from\":1500,\"to\":3000,\"color\":\"#F89E0D\"},{\"from\":3000,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":28,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"36px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":0,\"to\":500,\"color\":\"#2B54CE\"},{\"from\":500,\"to\":1500,\"color\":\"#3B911C\"},{\"from\":1500,\"to\":3000,\"color\":\"#F89E0D\"},{\"from\":3000,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:cm90YXRpb25hbF9zcGVlZF9jYXJkX2JhY2tncm91bmQucG5n:cm90YXRpb25hbF9zcGVlZF9jYXJkX2JhY2tncm91bmQucG5n;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Rotational speed card with background\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"units\":\"RPM\",\"decimals\":0,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\"}" + }, + "externalId": null, + "tags": [ + "angular speed", + "spin rate", + "revolutions", + "rotational frequency", + "spin motion" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/rotational_speed_chart_card.json b/application/src/main/data/json/system/widget_types/rotational_speed_chart_card.json new file mode 100644 index 0000000000..74e8738fbc --- /dev/null +++ b/application/src/main/data/json/system/widget_types/rotational_speed_chart_card.json @@ -0,0 +1,32 @@ +{ + "fqn": "rotational_speed_chart_card", + "name": "Rotational speed chart card", + "deprecated": false, + "image": "tb-image:cm90YXRpb25hbF9zcGVlZF9jaGFydF9jYXJkLnN2Zw==:cm90YXRpb25hbF9zcGVlZF9jaGFydF9jYXJkLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik0yMCAxNi43Yy0zLjcgMC02LjcgMS41LTYuNyAzLjMgMCAxLjUgMiAyLjggNC43IDMuMnYyLjFsMi43LTIuNkwxOCAyMHYxLjhjLTIuMS0uNC0zLjMtMS4yLTMuMy0xLjggMC0uNyAyLTIgNS4zLTJzNS4zIDEuMyA1LjMgMmMwIC41LTEgMS4zLTIuNiAxLjdWMjNjMi4zLS42IDQtMS43IDQtMy4xIDAtMS44LTMtMy4zLTYuNy0zLjNaTTMzIDE1aDMuNWE1IDUgMCAwIDEgMiAuNGMuNS4yIDEgLjYgMS4yIDEgLjMuNC40IDEgLjQgMS42IDAgLjUgMCAxLS4zIDEuNGwtLjcgMS0xLjIuNS0uNS4zaC0zLjJ2LTEuNGgyLjRjLjQgMCAuNyAwIDEtLjJsLjYtLjYuMi0uOWMwLS4zIDAtLjYtLjItLjlsLS42LS42LTEtLjJoLTEuOVYyNUgzM1YxNVptNS43IDEwLTIuMy00LjVoMS44bDIuMyA0LjR2LjFoLTEuOFptMi45LTMuNnYtLjJjMC0uNSAwLTEgLjItMS41bC43LTEuMmEzIDMgMCAwIDEgMS0uN2wxLjUtLjNjLjYgMCAxIDAgMS41LjMuNC4xLjguNCAxIC43LjQuNC42LjguNyAxLjJsLjMgMS41di4ybC0uMyAxLjUtLjYgMS4yYTMuMSAzLjEgMCAwIDEtMi42IDFjLS41IDAtMSAwLTEuNC0uMmEzLjUgMy41IDAgMCAxLTEuOC0ybC0uMi0xLjVabTEuNi0uMnYuMmMwIC4zIDAgLjYuMiAxIDAgLjIuMS41LjMuN2wuNS41Yy4zLjIuNS4yLjguMmExLjYgMS42IDAgMCAwIDEuNC0uN2MuMS0uMi4zLS41LjMtLjhsLjEtMXYtMWwtLjQtLjhhMS42IDEuNiAwIDAgMC0xLjQtLjdjLS4zIDAtLjUgMC0uOC4ybC0uNS41Yy0uMi4yLS4zLjUtLjMuOGwtLjIgMVptMTAuMy0zLjZ2MS4yaC00LjJ2LTEuMmg0LjJabS0zLTEuOGgxLjd2Ny43bC40LjJoLjRhMi41IDIuNSAwIDAgMCAuNiAwVjI1YTQuMiA0LjIgMCAwIDEtMS4xLjFjLS40IDAtLjcgMC0xLS4yLS4zLS4xLS42LS4zLS43LS42LS4yLS4zLS4zLS43LS4zLTEuMnYtNy4zWm04LjcgNy43VjIwYzAtLjMgMC0uNS0uMi0uN2ExIDEgMCAwIDAtLjQtLjRsLS43LS4yLS43LjEtLjUuNC0uMS41aC0xLjdsLjItLjguNy0uN2EzIDMgMCAwIDEgMS0uNSA0IDQgMCAwIDEgMS4yLS4yYy41IDAgMSAwIDEuNS4yLjQuMi43LjUgMSAuOS4yLjQuMy44LjMgMS40djMuM2wuMS45LjIuN3YuMWgtMS43YTMgMyAwIDAgMS0uMS0uN2wtLjEtLjhabS4yLTN2MWgtMS4xbC0uOC4xYy0uMyAwLS41LjEtLjYuM2ExIDEgMCAwIDAtLjUuOWwuMS41LjQuNC43LjFhMS44IDEuOCAwIDAgMCAxLjUtLjdsLjItLjYuNS44LS4yLjZhMyAzIDAgMCAxLS42LjYgMi42IDIuNiAwIDAgMS0xLjcuNmMtLjUgMC0xIDAtMS4zLS4zLS40LS4xLS43LS40LTEtLjdhMi40IDIuNCAwIDAgMS0uMS0yLjJsLjctLjggMS4xLS40IDEuNC0uMmgxLjNabTYuOC0yLjl2MS4ySDYydi0xLjJoNC4yWm0tMy0xLjhINjV2Ny43bC40LjJoLjRhMi41IDIuNSAwIDAgMCAuNiAwVjI1YTQuMiA0LjIgMCAwIDEtMS4xLjFjLS40IDAtLjcgMC0xLS4yLS4zLS4xLS41LS4zLS43LS42LS4yLS4zLS4zLS43LS4zLTEuMnYtNy4zWm02LjMgMS44VjI1aC0xLjd2LTcuNGgxLjdabS0xLjgtMiAuMy0uNi43LS4yYy4zIDAgLjUgMCAuNy4ybC4yLjdjMCAuMiAwIC40LS4yLjZhMSAxIDAgMCAxLS43LjIgMSAxIDAgMCAxLS43LS4yLjguOCAwIDAgMS0uMy0uNlptMy41IDUuOHYtLjJsLjMtMS41LjYtMS4yYTMgMyAwIDAgMSAxLjEtLjdjLjQtLjIgMS0uMyAxLjUtLjNzMSAwIDEuNC4zYy40LjEuOC40IDEuMS43bC43IDEuMi4yIDEuNXYuMmMwIC41IDAgMS0uMiAxLjVsLS43IDEuMmEzLjEgMy4xIDAgMCAxLTIuNSAxYy0uNiAwLTEgMC0xLjUtLjJhMy41IDMuNSAwIDAgMS0xLjgtMmwtLjItMS41Wm0xLjctLjJ2MS4ybC40LjdjLjIuMi4zLjQuNi41LjIuMi41LjIuOC4yYTEuNiAxLjYgMCAwIDAgMS4zLS43bC40LS44di0ybC0uNC0uOGExLjYgMS42IDAgMCAwLTEuMy0uN2MtLjMgMC0uNiAwLS44LjItLjMuMS0uNC4zLS42LjVsLS4zLjgtLjEgMVptOC41LTJWMjVoLTEuN3YtNy40aDEuNnYxLjZaTTgxIDIxaC0uNmMwLS41LjEtMSAuMy0xLjQuMS0uNS4zLS44LjYtMS4xYTIuNyAyLjcgMCAwIDEgMi4xLTFsMSAuMS43LjUuNS44LjIgMS4zVjI1aC0xLjd2LTQuOGMwLS4zIDAtLjYtLjItLjhhLjkuOSAwIDAgMC0uNC0uNWwtLjctLjFhMS42IDEuNiAwIDAgMC0xLjMuN2wtLjQuNy0uMS44Wm0xMSAyLjVWMjBjMC0uMyAwLS41LS4yLS43YTEgMSAwIDAgMC0uNC0uNGwtLjgtLjItLjYuMS0uNS40LS4yLjVoLTEuNmMwLS4zIDAtLjUuMi0uOC4xLS4zLjMtLjUuNi0uN2EzIDMgMCAwIDEgMS0uNSA0IDQgMCAwIDEgMS4yLS4yYy42IDAgMSAwIDEuNS4yLjQuMi44LjUgMSAuOS4zLjQuNC44LjQgMS40djQuMmwuMy43di4xaC0xLjdhMyAzIDAgMCAxLS4yLS43di0uOFptLjItM3YxSDkxbC0uOC4xLS42LjNhMSAxIDAgMCAwLS40LjlsLjEuNS40LjQuNi4xYTEuOCAxLjggMCAwIDAgMS41LS43bC4zLS42LjUuOC0uMy42YTMgMyAwIDAgMS0uNS42IDIuNiAyLjYgMCAwIDEtMS44LjZjLS41IDAtLjkgMC0xLjMtLjMtLjMtLjEtLjYtLjQtLjktLjdhMi40IDIuNCAwIDAgMS0uMS0yLjJsLjctLjggMS0uNCAxLjUtLjJoMS4zWm01LjEtNlYyNWgtMS42VjE0LjVoMS42Wm0xMCA4LjUtLjEtLjRhMSAxIDAgMCAwLS41LS40bC0xLS4zLTEuMS0uMy0uOC0uNWMtLjMtLjEtLjQtLjMtLjYtLjZsLS4yLS44LjItLjkuNi0uNy45LS41IDEuMi0uMWMuNiAwIDEuMSAwIDEuNi4zLjQuMi44LjUgMSAuOC4yLjQuNC44LjQgMS4yaC0xLjdsLS4xLS41YTEgMSAwIDAgMC0uNS0uNWwtLjctLjEtLjcuMWExIDEgMCAwIDAtLjQuNC45LjkgMCAwIDAgMCAuOGwuMi4yLjQuMi44LjIgMS40LjVjLjQuMi44LjQgMSAuNy4yLjMuMy43LjMgMS4xYTIgMiAwIDAgMS0uOCAxLjYgMyAzIDAgMCAxLTEgLjVsLTEuMi4xYy0uNyAwLTEuMiAwLTEuNy0uM3MtLjgtLjYtMS0xYy0uMy0uMy0uNC0uNy0uNC0xLjFoMS42YzAgLjMgMCAuNS4yLjdsLjYuNC43LjEuOC0uMWMuMiAwIC4zLS4yLjQtLjNsLjItLjVabTUtNHY4LjhoLTEuN1YxNy42aDEuNWwuMSAxLjRabTQuOCAyLjJ2LjJsLS4yIDEuNS0uNiAxLjJjLS4yLjMtLjUuNi0uOS44bC0xLjIuMmMtLjUgMC0uOSAwLTEuMi0uMmwtLjktLjdhNCA0IDAgMCAxLS42LTEuMmwtLjMtMS40di0uNWwuMy0xLjUuNi0xLjFhMi41IDIuNSAwIDAgMSAyLTFjLjUgMCAxIDAgMS4zLjJsMSAuOC41IDEuMi4yIDEuNVptLTEuNy4ydi0xLjJsLS40LS43YTEuNSAxLjUgMCAwIDAtMS4zLS43bC0uNy4xLS41LjQtLjQuNy0uMS43VjIybC4yIDEgLjYuNiAxIC4yYy4yIDAgLjUgMCAuNy0uMi4yIDAgLjQtLjMuNS0uNS4yLS4yLjMtLjUuMy0uOGwuMS0xWk0xMjIgMjVjLS42IDAtMSAwLTEuNS0uMmEzLjMgMy4zIDAgMCAxLTEuOC0yIDQgNCAwIDAgMS0uMy0xLjN2LS4zYzAtLjYuMS0xLjEuMy0xLjYuMS0uNS40LS45LjctMS4yYTMgMyAwIDAgMSAxLS44bDEuNC0uMmMuNSAwIDEgMCAxLjQuMmwxIC44LjUgMS4xYy4yLjUuMiAxIC4yIDEuNXYuN2gtNS43di0xLjJoNC4xdi0uMWMwLS4zIDAtLjYtLjItLjkgMC0uMi0uMi0uNC0uNS0uNmwtLjgtLjJjLS4zIDAtLjUgMC0uOC4yLS4yIDAtLjMuMi0uNS41bC0uMy43LS4xIDF2LjRsLjEuOS40LjcuNi41LjkuMWEyLjMgMi4zIDAgMCAwIDEuOC0uOWwxIC45LS43LjYtMSAuNWMtLjMuMi0uNy4yLTEuMi4yWm03LjcgMGMtLjUgMC0xIDAtMS40LS4yYTMuMyAzLjMgMCAwIDEtMS45LTIgNCA0IDAgMCAxLS4yLTEuM3YtLjNjMC0uNiAwLTEuMS4yLTEuNnMuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS4xLS44bDEuMy0uMmMuNiAwIDEgMCAxLjQuMmwxIC44Yy4zLjMuNS43LjYgMS4xbC4yIDEuNXYuN0gxMjd2LTEuMmg0di0uMWwtLjEtLjktLjUtLjYtLjktLjJjLS4yIDAtLjUgMC0uNy4yLS4yIDAtLjQuMi0uNS41bC0uNC43djIuM2wuNS43LjYuNS44LjFhMi4zIDIuMyAwIDAgMCAxLjktLjlsLjkuOS0uNy42YTIgMiAwIDAgMS0uOS41Yy0uNC4yLS44LjItMS4zLjJabTktMS42di05aDEuN1YyNWgtMS41bC0uMi0xLjVabS00LjgtMi4xdi0uMmMwLS41IDAtMSAuMi0xLjVzLjMtLjkuNi0xLjJhMi41IDIuNSAwIDAgMSAyLjEtMWMuNSAwIC45IDAgMS4yLjJsLjkuOC41IDEuMS4zIDEuNXYuNWMwIC41LS4xIDEtLjMgMS40YTQgNCAwIDAgMS0uNSAxLjEgMi41IDIuNSAwIDAgMS0yIDEgMi42IDIuNiAwIDAgMS0yLjItMWwtLjYtMS4yLS4yLTEuNVptMS43LS4ydjEuMmwuNC43YzAgLjIuMy40LjUuNWwuNy4yYy40IDAgLjcgMCAxLS4ybC41LS43Yy4yLS4zLjMtLjYuMy0xdi0xLjJjMC0uMyAwLS41LS4yLS43YTIgMiAwIDAgMC0uMy0uNmwtLjUtLjUtLjgtLjFjLS4zIDAtLjUgMC0uNy4ybC0uNS41Yy0uMi4yLS4zLjUtLjMuOGwtLjEgMVoiLz48cGF0aCBmaWxsPSIjMzA1QUQ3IiBkPSJNNTEgNjkuM2gyLjRjMSAwIDEuOC0uMiAyLjUtLjUuNi0uNCAxLjEtLjggMS40LTEuNC40LS42LjUtMS4zLjUtMiAwLS45LS4xLTEuNi0uNC0yLjJBMyAzIDAgMCAwIDU2IDYyYTQuOCA0LjggMCAwIDAtNC4yIDAgMy41IDMuNSAwIDAgMC0yIDMuMmgtNC4zYzAtMS40LjQtMi42IDEuMS0zLjcuNy0xIDEuNy0xLjggMy0yLjQgMS4yLS42IDIuNi0xIDQuMS0xIDEuNyAwIDMuMS4zIDQuMy45IDEuMy41IDIuMiAxLjMgMyAyLjQuNiAxIDEgMi40IDEgNGE1LjMgNS4zIDAgMCAxLTIuMSA0LjJjLS42LjctMS41IDEuMi0yLjQgMS42LTEgLjQtMi4yLjUtMy41LjVoLTMuMnYtMi4zWm0wIDMuM3YtMi4zaDNjMS42IDAgMi45LjEgNCAuNSAxIC40IDEuOS44IDIuNSAxLjUuNy42IDEuMiAxLjMgMS41IDJhNy41IDcuNSAwIDAgMS0uMiA1LjdjLS40IDEtMSAxLjgtMS44IDIuNGE4IDggMCAwIDEtMi44IDEuNSAxMS4zIDExLjMgMCAwIDEtNi41IDBjLTEtLjMtMi0uOC0yLjctMS40YTYuNyA2LjcgMCAwIDEtMi0yLjIgNyA3IDAgMCAxLS42LTMuMmg0LjJjMCAuOC4yIDEuNC41IDIgLjQuNi45IDEgMS41IDEuNGE1IDUgMCAwIDAgMi4zLjVjLjkgMCAxLjctLjIgMi4zLS41YTQgNCAwIDAgMCAxLjUtMS40Yy4zLS42LjUtMS40LjUtMi4zIDAtMS0uMi0xLjgtLjYtMi40LS40LS42LTEtMS0xLjYtMS40LS43LS4zLTEuNi0uNC0yLjYtLjRINTFaTTgzLjEgNjl2NC4xYzAgMi0uMiAzLjgtLjYgNS4yYTkgOSAwIDAgMS0xLjcgMy40Yy0uOC45LTEuNiAxLjUtMi43IDJhOS44IDkuOCAwIDAgMS02LjIuMiA2LjkgNi45IDAgMCAxLTQuMS0zLjRjLS41LS45LS45LTEuOS0xLjEtMy4xLS4zLTEuMi0uNC0yLjctLjQtNC4zVjY5YzAtMiAuMi0zLjguNi01LjFhOSA5IDAgMCAxIDEuNy0zLjRjLjctMSAxLjYtMS41IDIuNi0yIDEtLjMgMi4yLS41IDMuNS0uNSAxIDAgMiAwIDIuOC4zYTYuNiA2LjYgMCAwIDEgNCAzLjNjLjUuOSAxIDIgMS4yIDMuMS4yIDEuMi40IDIuNy40IDQuM1ptLTQuMyA0Ljd2LTUuNGwtLjEtMi43YTggOCAwIDAgMC0uNi0yIDQgNCAwIDAgMC0uOC0xLjMgMyAzIDAgMCAwLTEuMi0uNyAzLjkgMy45IDAgMCAwLTMuMi4xYy0uNS4yLTEgLjYtMS4zIDEuMi0uMy41LS42IDEuMy0uOCAyLjItLjIuOS0uMyAyLS4zIDMuMnY1LjRjMCAxIDAgMiAuMiAyLjhsLjUgMiAuOSAxLjRjLjMuMy43LjYgMS4xLjdsMS41LjNhNCA0IDAgMCAwIDEuOC0uNGMuNS0uMyAxLS43IDEuMy0xLjIuMy0uNi42LTEuNC44LTIuM2wuMi0zLjNabTI0LjgtNC43djQuMWMwIDItLjIgMy44LS42IDUuMmE5IDkgMCAwIDEtMS43IDMuNGMtLjcuOS0xLjYgMS41LTIuNiAyLTEgLjMtMi4yLjYtMy41LjYtMSAwLTEuOS0uMi0yLjgtLjRhNi45IDYuOSAwIDAgMS00LTMuNGMtLjUtLjktMS0xLjktMS4yLTMuMS0uMi0xLjItLjQtMi43LS40LTQuM1Y2OWMwLTIgLjItMy44LjYtNS4xYTkgOSAwIDAgMSAxLjctMy40Yy44LTEgMS42LTEuNSAyLjctMiAxLS4zIDIuMS0uNSAzLjQtLjUgMSAwIDIgMCAyLjguM2E2LjYgNi42IDAgMCAxIDQuMSAzLjNjLjUuOS45IDIgMS4xIDMuMS4zIDEuMi40IDIuNy40IDQuM1ptLTQuMiA0Ljd2LTUuNGMwLTEgMC0xLjktLjItMi43YTggOCAwIDAgMC0uNS0yIDQgNCAwIDAgMC0uOS0xLjMgMyAzIDAgMCAwLTEuMS0uN2wtMS41LS4zYy0uNiAwLTEuMi4xLTEuNy40LS42LjItMSAuNi0xLjMgMS4yLS40LjUtLjcgMS4zLS45IDIuMmwtLjIgMy4ydjUuNGMwIDEgMCAyIC4yIDIuOCAwIC43LjMgMS40LjUgMmwuOCAxLjRjLjQuMy44LjYgMS4yLjdhNCA0IDAgMCAwIDMuMi0uMWMuNS0uMyAxLS43IDEuMy0xLjIuNC0uNi42LTEuNC44LTIuMy4yLS45LjMtMiAuMy0zLjNabTkuOC4zaDMuM2MuNyAwIDEuMy4yIDEuOC40LjYuMiAxIC42IDEuMiAxIC4zLjQuNCAxIC40IDEuNiAwIC41IDAgMS0uMiAxLjNsLS44IDEtMS4zLjUtLjMuMmgtMy4xdi0xLjFoMi4zYy41IDAgLjkgMCAxLjItLjNhMS43IDEuNyAwIDAgMCAxLTEuNmwtLjMtMWMtLjItLjMtLjQtLjUtLjctLjYtLjMtLjItLjctLjMtMS4zLS4zaC0yVjg0aC0xLjJWNzRabTUuOCAxMC0yLjQtNC41aDEuM2wyLjUgNC40di4xSDExNVptNi43LTMuOUgxMTl2LTFoMi43Yy41IDAgLjktLjIgMS4yLS4zLjMtLjIuNi0uNC43LS43LjItLjMuMi0uNi4yLTEgMC0uMyAwLS43LS4yLTEtLjEtLjMtLjQtLjUtLjctLjctLjMtLjItLjctLjMtMS4yLS4zaC0yLjRWODRIMTE4Vjc0aDMuN2MuNyAwIDEuMy4yIDEuOS40YTMgMyAwIDAgMSAxLjYgMi42YzAgLjctLjIgMS4zLS41IDEuNy0uMi41LS42LjgtMS4xIDEtLjYuMy0xLjIuNC0yIC40Wm01LjctNmgxLjNsMy4zIDggMy4yLThoMS4zbC00IDkuOWgtMWwtNC0xMFptLS40IDBoMS4xbC4yIDZWODRIMTI3Vjc0Wm04LjggMGgxLjFWODRoLTEuM3YtMy45bC4yLTZaIi8+PHBhdGggZmlsbD0idXJsKCNhKSIgZmlsbC1vcGFjaXR5PSIuMyIgZD0iTTE0Ny40IDcxLjhoNy4ydjguOWwtMy42IDIuNi0zLjYtMi42di05WiIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTE1MC43IDgzLjljLjIuMi40LjIuNiAwbDMuMy0zLjItLjctLjctMS40IDEuNGMtLjQuNC0xIC4xLTEtLjR2LTMuOWgtMXY0YzAgLjQtLjYuNy0xIC4zbC0xLjQtMS40LS43LjcgMy4zIDMuMloiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik0xNjIuNiA3OS44djEuMWgtMy4xdi0xLjFoM1ptNS4yLTQuNFY4NGgtMS40di02LjlsLTIgLjd2LTEuMWwzLjMtMS4zaC4xWm03LjUgMGguMnYxLjJoLS4xYy0uNSAwLTEgMC0xLjMuMmwtMSAuNi0uNCAxYy0uMi40LS4yLjgtLjIgMS4ydjEuM2wuMSAxIC4zLjYuNS40aDEuMWwuNS0uNC4yLS42YTIuNSAyLjUgMCAwIDAtLjItMmwtLjUtLjMtLjYtLjFjLS4zIDAtLjUgMC0uNy4yLS4zLjEtLjQuMy0uNi41bC0uMi43LS41LS4yYzAtLjQgMC0uNy4yLTFsLjUtLjcuOC0uNS45LS4yYy40IDAgLjguMSAxIC4zLjQuMS42LjMuOC42bC41IDFhMy43IDMuNyAwIDAgMSAwIDIuMWwtLjYgMS0uOS42LTEuMS4yYy0uNSAwLS45IDAtMS4zLS4ybC0uOS0uOC0uNS0xYy0uMi0uNC0uMi0uOS0uMi0xLjN2LS42YzAtLjcgMC0xLjMuMi0xLjguMi0uNi40LTEuMS44LTEuNi4zLS40LjgtLjggMS4zLTFhNCA0IDAgMCAxIDItLjRabTMuMyAyLjJWNzdjMC0uMyAwLS42LjItLjkuMS0uMi4zLS41LjYtLjYuMy0uMi42LS4zIDEtLjNzLjcuMSAxIC4zbC42LjYuMiAxdi40YzAgLjMgMCAuNi0uMi44bC0uNi43LTEgLjJhMiAyIDAgMCAxLTEtLjJjLS4zLS4yLS41LS40LS42LS43bC0uMi0uOFptMS0uNXYuOWwuMy4zLjUuMmMuMiAwIC4zIDAgLjUtLjJsLjItLjMuMS0uNHYtLjlsLS40LS40YS44LjggMCAwIDAtLjQtLjFsLS41LjEtLjIuNC0uMS40Wm0zLjEgNS4zdi0uNWMwLS4zIDAtLjYuMi0uOWwuNi0uNmMuMy0uMi42LS4yIDEtLjJzLjcgMCAxIC4ybC42LjYuMiAxdi40YzAgLjMgMCAuNi0uMi44LS4xLjMtLjMuNS0uNi43bC0xIC4yYTIgMiAwIDAgMS0xLS4ybC0uNi0uNy0uMi0uOFptMS0uNXYuOWwuNC40aC45bC4zLS40di0xLjNhLjguOCAwIDAgMC0uOC0uNWwtLjUuMS0uMi40LS4xLjRabTEuMi01LjItNC4yIDYuNy0uNy0uNSA0LjEtNi42LjguNFoiLz48ZyBjbGlwLXBhdGg9InVybCgjYikiPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjEiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTE4OCAxMjVIMTJ2LTFoMTc2djFaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuOSIgc3Ryb2tlLXdpZHRoPSIxLjUiIGQ9Ik0xMiAxMzMuOGg5LjNsMTMuMyA5LjJMNDUgMTEyLjJsMjEuNiAyMi42IDIzLjctMjIuNiA3LjIgMSAxNC40IDI5LjhoMTQuNGwxMi40LTI2LjdoOS4zbDktMTQuMyAxMi40IDQuMSAxOC41IDEwLjMiLz48L2c+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJhIiB4MT0iMTUwLjciIHgyPSIxNTAuNyIgeTE9Ijg0IiB5Mj0iNzEuOCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6I2ZmZjtzdG9wLW9wYWNpdHk6MSIvPjxzdG9wIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6I2ZmZjtzdG9wLW9wYWNpdHk6MCIvPjwvbGluZWFyR3JhZGllbnQ+PGNsaXBQYXRoIGlkPSJiIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTIgOTBoMTc2djYwSDEyeiIvPjwvY2xpcFBhdGg+PC9kZWZzPjwvc3ZnPg==", + "description": "Displays rotational speed data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'rotationalSpeed', label: 'Rotational speed', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'RPM', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'rotationalSpeed', 'RPM', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Rotational speed\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 4000 - 2000;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 4000) {\\n\\tvalue = 4000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"°C\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":500,\"color\":\"#305AD7\"},{\"from\":500,\"to\":1500,\"color\":\"#3FA71A\"},{\"from\":1500,\"to\":3000,\"color\":\"#FFA600\"},{\"from\":3000,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 4000 - 2000;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 4000) {\\n\\tvalue = 4000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"RPM\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0,0,0,0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"RPM\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Rotational speed\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"360\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"margin\":\"0px\"}" + }, + "tags": [ + "angular speed", + "spin rate", + "revolutions", + "rotational frequency", + "spin motion" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/rotational_speed_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/rotational_speed_chart_card_with_background.json new file mode 100644 index 0000000000..6f005f2526 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/rotational_speed_chart_card_with_background.json @@ -0,0 +1,32 @@ +{ + "fqn": "rotational_speed_chart_card_with_background", + "name": "Rotational speed chart card with background", + "deprecated": false, + "image": "tb-image:cm90YXRpb25hbF9zcGVlZF9jaGFydF9jYXJkX3dpdGhfYmFja2dyb3VuZC5zdmc=:cm90YXRpb25hbF9zcGVlZF9jaGFydF9jYXJkX3dpdGhfYmFja2dyb3VuZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiIHRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDYgMTU0KSIvPjwvZz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNMjAgMTYuN2MtMy43IDAtNi43IDEuNS02LjcgMy4zIDAgMS41IDIgMi44IDQuNyAzLjJ2Mi4xbDIuNy0yLjZMMTggMjB2MS44Yy0yLjEtLjQtMy4zLTEuMi0zLjMtMS44IDAtLjcgMi0yIDUuMy0yczUuMyAxLjMgNS4zIDJjMCAuNS0xIDEuMy0yLjYgMS43VjIzYzIuMy0uNiA0LTEuNyA0LTMuMSAwLTEuOC0zLTMuMy02LjctMy4zWk0zMyAxNWgzLjVhNSA1IDAgMCAxIDIgLjRjLjUuMiAxIC42IDEuMiAxIC4zLjQuNCAxIC40IDEuNiAwIC41IDAgMS0uMyAxLjRsLS43IDEtMS4yLjUtLjUuM2gtMy4ydi0xLjRoMi40Yy40IDAgLjcgMCAxLS4ybC42LS42LjItLjljMC0uMyAwLS42LS4yLS45bC0uNi0uNi0xLS4yaC0xLjlWMjVIMzNWMTVabTUuNyAxMC0yLjMtNC41aDEuOGwyLjMgNC40di4xaC0xLjhabTIuOS0zLjZ2LS4yYzAtLjUgMC0xIC4yLTEuNWwuNy0xLjJhMyAzIDAgMCAxIDEtLjdsMS41LS4zYy42IDAgMSAwIDEuNS4zLjQuMS44LjQgMSAuNy40LjQuNi44LjcgMS4ybC4zIDEuNXYuMmwtLjMgMS41LS42IDEuMmEzLjEgMy4xIDAgMCAxLTIuNiAxYy0uNSAwLTEgMC0xLjQtLjJhMy41IDMuNSAwIDAgMS0xLjgtMmwtLjItMS41Wm0xLjYtLjJ2LjJjMCAuMyAwIC42LjIgMSAwIC4yLjEuNS4zLjdsLjUuNWMuMy4yLjUuMi44LjJhMS42IDEuNiAwIDAgMCAxLjQtLjdjLjEtLjIuMy0uNS4zLS44bC4xLTF2LTFsLS40LS44YTEuNiAxLjYgMCAwIDAtMS40LS43Yy0uMyAwLS41IDAtLjguMmwtLjUuNWMtLjIuMi0uMy41LS4zLjhsLS4yIDFabTEwLjMtMy42djEuMmgtNC4ydi0xLjJoNC4yWm0tMy0xLjhoMS43djcuN2wuNC4yaC40YTIuNSAyLjUgMCAwIDAgLjYgMFYyNWE0LjIgNC4yIDAgMCAxLTEuMS4xYy0uNCAwLS43IDAtMS0uMi0uMy0uMS0uNi0uMy0uNy0uNi0uMi0uMy0uMy0uNy0uMy0xLjJ2LTcuM1ptOC43IDcuN1YyMGMwLS4zIDAtLjUtLjItLjdhMSAxIDAgMCAwLS40LS40bC0uNy0uMi0uNy4xLS41LjQtLjEuNWgtMS43bC4yLS44LjctLjdhMyAzIDAgMCAxIDEtLjUgNCA0IDAgMCAxIDEuMi0uMmMuNSAwIDEgMCAxLjUuMi40LjIuNy41IDEgLjkuMi40LjMuOC4zIDEuNHYzLjNsLjEuOS4yLjd2LjFoLTEuN2EzIDMgMCAwIDEtLjEtLjdsLS4xLS44Wm0uMi0zdjFoLTEuMWwtLjguMWMtLjMgMC0uNS4xLS42LjNhMSAxIDAgMCAwLS41LjlsLjEuNS40LjQuNy4xYTEuOCAxLjggMCAwIDAgMS41LS43bC4yLS42LjUuOC0uMi42YTMgMyAwIDAgMS0uNi42IDIuNiAyLjYgMCAwIDEtMS43LjZjLS41IDAtMSAwLTEuMy0uMy0uNC0uMS0uNy0uNC0xLS43YTIuNCAyLjQgMCAwIDEtLjEtMi4ybC43LS44IDEuMS0uNCAxLjQtLjJoMS4zWm02LjgtMi45djEuMkg2MnYtMS4yaDQuMlptLTMtMS44SDY1djcuN2wuNC4yaC40YTIuNSAyLjUgMCAwIDAgLjYgMFYyNWE0LjIgNC4yIDAgMCAxLTEuMS4xYy0uNCAwLS43IDAtMS0uMi0uMy0uMS0uNS0uMy0uNy0uNi0uMi0uMy0uMy0uNy0uMy0xLjJ2LTcuM1ptNi4zIDEuOFYyNWgtMS43di03LjRoMS43Wm0tMS44LTIgLjMtLjYuNy0uMmMuMyAwIC41IDAgLjcuMmwuMi43YzAgLjIgMCAuNC0uMi42YTEgMSAwIDAgMS0uNy4yIDEgMSAwIDAgMS0uNy0uMi44LjggMCAwIDEtLjMtLjZabTMuNSA1Ljh2LS4ybC4zLTEuNS42LTEuMmEzIDMgMCAwIDEgMS4xLS43Yy40LS4yIDEtLjMgMS41LS4zczEgMCAxLjQuM2MuNC4xLjguNCAxLjEuN2wuNyAxLjIuMiAxLjV2LjJjMCAuNSAwIDEtLjIgMS41bC0uNyAxLjJhMy4xIDMuMSAwIDAgMS0yLjUgMWMtLjYgMC0xIDAtMS41LS4yYTMuNSAzLjUgMCAwIDEtMS44LTJsLS4yLTEuNVptMS43LS4ydjEuMmwuNC43Yy4yLjIuMy40LjYuNS4yLjIuNS4yLjguMmExLjYgMS42IDAgMCAwIDEuMy0uN2wuNC0uOHYtMmwtLjQtLjhhMS42IDEuNiAwIDAgMC0xLjMtLjdjLS4zIDAtLjYgMC0uOC4yLS4zLjEtLjQuMy0uNi41bC0uMy44LS4xIDFabTguNS0yVjI1aC0xLjd2LTcuNGgxLjZ2MS42Wk04MSAyMWgtLjZjMC0uNS4xLTEgLjMtMS40LjEtLjUuMy0uOC42LTEuMWEyLjcgMi43IDAgMCAxIDIuMS0xbDEgLjEuNy41LjUuOC4yIDEuM1YyNWgtMS43di00LjhjMC0uMyAwLS42LS4yLS44YS45LjkgMCAwIDAtLjQtLjVsLS43LS4xYTEuNiAxLjYgMCAwIDAtMS4zLjdsLS40LjctLjEuOFptMTEgMi41VjIwYzAtLjMgMC0uNS0uMi0uN2ExIDEgMCAwIDAtLjQtLjRsLS44LS4yLS42LjEtLjUuNC0uMi41aC0xLjZjMC0uMyAwLS41LjItLjguMS0uMy4zLS41LjYtLjdhMyAzIDAgMCAxIDEtLjUgNCA0IDAgMCAxIDEuMi0uMmMuNiAwIDEgMCAxLjUuMi40LjIuOC41IDEgLjkuMy40LjQuOC40IDEuNHY0LjJsLjMuN3YuMWgtMS43YTMgMyAwIDAgMS0uMi0uN3YtLjhabS4yLTN2MUg5MWwtLjguMS0uNi4zYTEgMSAwIDAgMC0uNC45bC4xLjUuNC40LjYuMWExLjggMS44IDAgMCAwIDEuNS0uN2wuMy0uNi41LjgtLjMuNmEzIDMgMCAwIDEtLjUuNiAyLjYgMi42IDAgMCAxLTEuOC42Yy0uNSAwLS45IDAtMS4zLS4zLS4zLS4xLS42LS40LS45LS43YTIuNCAyLjQgMCAwIDEtLjEtMi4ybC43LS44IDEtLjQgMS41LS4yaDEuM1ptNS4xLTZWMjVoLTEuNlYxNC41aDEuNlptMTAgOC41LS4xLS40YTEgMSAwIDAgMC0uNS0uNGwtMS0uMy0xLjEtLjMtLjgtLjVjLS4zLS4xLS40LS4zLS42LS42bC0uMi0uOC4yLS45LjYtLjcuOS0uNSAxLjItLjFjLjYgMCAxLjEgMCAxLjYuMy40LjIuOC41IDEgLjguMi40LjQuOC40IDEuMmgtMS43bC0uMS0uNWExIDEgMCAwIDAtLjUtLjVsLS43LS4xLS43LjFhMSAxIDAgMCAwLS40LjQuOS45IDAgMCAwIDAgLjhsLjIuMi40LjIuOC4yIDEuNC41Yy40LjIuOC40IDEgLjcuMi4zLjMuNy4zIDEuMWEyIDIgMCAwIDEtLjggMS42IDMgMyAwIDAgMS0xIC41bC0xLjIuMWMtLjcgMC0xLjIgMC0xLjctLjNzLS44LS42LTEtMWMtLjMtLjMtLjQtLjctLjQtMS4xaDEuNmMwIC4zIDAgLjUuMi43bC42LjQuNy4xLjgtLjFjLjIgMCAuMy0uMi40LS4zbC4yLS41Wm01LTR2OC44aC0xLjdWMTcuNmgxLjVsLjEgMS40Wm00LjggMi4ydi4ybC0uMiAxLjUtLjYgMS4yYy0uMi4zLS41LjYtLjkuOGwtMS4yLjJjLS41IDAtLjkgMC0xLjItLjJsLS45LS43YTQgNCAwIDAgMS0uNi0xLjJsLS4zLTEuNHYtLjVsLjMtMS41LjYtMS4xYTIuNSAyLjUgMCAwIDEgMi0xYy41IDAgMSAwIDEuMy4ybDEgLjguNSAxLjIuMiAxLjVabS0xLjcuMnYtMS4ybC0uNC0uN2ExLjUgMS41IDAgMCAwLTEuMy0uN2wtLjcuMS0uNS40LS40LjctLjEuN1YyMmwuMiAxIC42LjYgMSAuMmMuMiAwIC41IDAgLjctLjIuMiAwIC40LS4zLjUtLjUuMi0uMi4zLS41LjMtLjhsLjEtMVpNMTIyIDI1Yy0uNiAwLTEgMC0xLjUtLjJhMy4zIDMuMyAwIDAgMS0xLjgtMiA0IDQgMCAwIDEtLjMtMS4zdi0uM2MwLS42LjEtMS4xLjMtMS42LjEtLjUuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS0uOGwxLjQtLjJjLjUgMCAxIDAgMS40LjJsMSAuOC41IDEuMWMuMi41LjIgMSAuMiAxLjV2LjdoLTUuN3YtMS4yaDQuMXYtLjFjMC0uMyAwLS42LS4yLS45IDAtLjItLjItLjQtLjUtLjZsLS44LS4yYy0uMyAwLS41IDAtLjguMi0uMiAwLS4zLjItLjUuNWwtLjMuNy0uMSAxdi40bC4xLjkuNC43LjYuNS45LjFhMi4zIDIuMyAwIDAgMCAxLjgtLjlsMSAuOS0uNy42LTEgLjVjLS4zLjItLjcuMi0xLjIuMlptNy43IDBjLS41IDAtMSAwLTEuNC0uMmEzLjMgMy4zIDAgMCAxLTEuOS0yIDQgNCAwIDAgMS0uMi0xLjN2LS4zYzAtLjYgMC0xLjEuMi0xLjZzLjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEuMS0uOGwxLjMtLjJjLjYgMCAxIDAgMS40LjJsMSAuOGMuMy4zLjUuNy42IDEuMWwuMiAxLjV2LjdIMTI3di0xLjJoNHYtLjFsLS4xLS45LS41LS42LS45LS4yYy0uMiAwLS41IDAtLjcuMi0uMiAwLS40LjItLjUuNWwtLjQuN3YyLjNsLjUuNy42LjUuOC4xYTIuMyAyLjMgMCAwIDAgMS45LS45bC45LjktLjcuNmEyIDIgMCAwIDEtLjkuNWMtLjQuMi0uOC4yLTEuMy4yWm05LTEuNnYtOWgxLjdWMjVoLTEuNWwtLjItMS41Wm0tNC44LTIuMXYtLjJjMC0uNSAwLTEgLjItMS41cy4zLS45LjYtMS4yYTIuNSAyLjUgMCAwIDEgMi4xLTFjLjUgMCAuOSAwIDEuMi4ybC45LjguNSAxLjEuMyAxLjV2LjVjMCAuNS0uMSAxLS4zIDEuNGE0IDQgMCAwIDEtLjUgMS4xIDIuNSAyLjUgMCAwIDEtMiAxIDIuNiAyLjYgMCAwIDEtMi4yLTFsLS42LTEuMi0uMi0xLjVabTEuNy0uMnYxLjJsLjQuN2MwIC4yLjMuNC41LjVsLjcuMmMuNCAwIC43IDAgMS0uMmwuNS0uN2MuMi0uMy4zLS42LjMtMXYtMS4yYzAtLjMgMC0uNS0uMi0uN2EyIDIgMCAwIDAtLjMtLjZsLS41LS41LS44LS4xYy0uMyAwLS41IDAtLjcuMmwtLjUuNWMtLjIuMi0uMy41LS4zLjhsLS4xIDFaIi8+PHBhdGggZmlsbD0iIzJCNTRDRSIgZD0iTTUxIDY5LjNoMi40YzEgMCAxLjgtLjIgMi41LS41LjYtLjQgMS4xLS44IDEuNC0xLjQuNC0uNi41LTEuMy41LTIgMC0uOS0uMS0xLjYtLjQtMi4yQTMgMyAwIDAgMCA1NiA2MmE0LjggNC44IDAgMCAwLTQuMiAwIDMuNSAzLjUgMCAwIDAtMiAzLjJoLTQuM2MwLTEuNC40LTIuNiAxLjEtMy43LjctMSAxLjctMS44IDMtMi40IDEuMi0uNiAyLjYtMSA0LjEtMSAxLjcgMCAzLjEuMyA0LjMuOSAxLjMuNSAyLjIgMS4zIDMgMi40LjYgMSAxIDIuNCAxIDRhNS4zIDUuMyAwIDAgMS0yLjEgNC4yYy0uNi43LTEuNSAxLjItMi40IDEuNi0xIC40LTIuMi41LTMuNS41aC0zLjJ2LTIuM1ptMCAzLjN2LTIuM2gzYzEuNiAwIDIuOS4xIDQgLjUgMSAuNCAxLjkuOCAyLjUgMS41LjcuNiAxLjIgMS4zIDEuNSAyYTcuNSA3LjUgMCAwIDEtLjIgNS43Yy0uNCAxLTEgMS44LTEuOCAyLjRhOCA4IDAgMCAxLTIuOCAxLjUgMTEuMyAxMS4zIDAgMCAxLTYuNSAwYy0xLS4zLTItLjgtMi43LTEuNGE2LjcgNi43IDAgMCAxLTItMi4yIDcgNyAwIDAgMS0uNi0zLjJoNC4yYzAgLjguMiAxLjQuNSAyIC40LjYuOSAxIDEuNSAxLjRhNSA1IDAgMCAwIDIuMy41Yy45IDAgMS43LS4yIDIuMy0uNWE0IDQgMCAwIDAgMS41LTEuNGMuMy0uNi41LTEuNC41LTIuMyAwLTEtLjItMS44LS42LTIuNC0uNC0uNi0xLTEtMS42LTEuNC0uNy0uMy0xLjYtLjQtMi42LS40SDUxWk04My4xIDY5djQuMWMwIDItLjIgMy44LS42IDUuMmE5IDkgMCAwIDEtMS43IDMuNGMtLjguOS0xLjYgMS41LTIuNyAyYTkuOCA5LjggMCAwIDEtNi4yLjIgNi45IDYuOSAwIDAgMS00LjEtMy40Yy0uNS0uOS0uOS0xLjktMS4xLTMuMS0uMy0xLjItLjQtMi43LS40LTQuM1Y2OWMwLTIgLjItMy44LjYtNS4xYTkgOSAwIDAgMSAxLjctMy40Yy43LTEgMS42LTEuNSAyLjYtMiAxLS4zIDIuMi0uNSAzLjUtLjUgMSAwIDIgMCAyLjguM2E2LjYgNi42IDAgMCAxIDQgMy4zYy41LjkgMSAyIDEuMiAzLjEuMiAxLjIuNCAyLjcuNCA0LjNabS00LjMgNC43di01LjRsLS4xLTIuN2E4IDggMCAwIDAtLjYtMiA0IDQgMCAwIDAtLjgtMS4zIDMgMyAwIDAgMC0xLjItLjcgMy45IDMuOSAwIDAgMC0zLjIuMWMtLjUuMi0xIC42LTEuMyAxLjItLjMuNS0uNiAxLjMtLjggMi4yLS4yLjktLjMgMi0uMyAzLjJ2NS40YzAgMSAwIDIgLjIgMi44bC41IDIgLjkgMS40Yy4zLjMuNy42IDEuMS43bDEuNS4zYTQgNCAwIDAgMCAxLjgtLjRjLjUtLjMgMS0uNyAxLjMtMS4yLjMtLjYuNi0xLjQuOC0yLjNsLjItMy4zWm0yNC44LTQuN3Y0LjFjMCAyLS4yIDMuOC0uNiA1LjJhOSA5IDAgMCAxLTEuNyAzLjRjLS43LjktMS42IDEuNS0yLjYgMi0xIC4zLTIuMi42LTMuNS42LTEgMC0xLjktLjItMi44LS40YTYuOSA2LjkgMCAwIDEtNC0zLjRjLS41LS45LTEtMS45LTEuMi0zLjEtLjItMS4yLS40LTIuNy0uNC00LjNWNjljMC0yIC4yLTMuOC42LTUuMWE5IDkgMCAwIDEgMS43LTMuNGMuOC0xIDEuNi0xLjUgMi43LTIgMS0uMyAyLjEtLjUgMy40LS41IDEgMCAyIDAgMi44LjNhNi42IDYuNiAwIDAgMSA0LjEgMy4zYy41LjkuOSAyIDEuMSAzLjEuMyAxLjIuNCAyLjcuNCA0LjNabS00LjIgNC43di01LjRjMC0xIDAtMS45LS4yLTIuN2E4IDggMCAwIDAtLjUtMiA0IDQgMCAwIDAtLjktMS4zIDMgMyAwIDAgMC0xLjEtLjdsLTEuNS0uM2MtLjYgMC0xLjIuMS0xLjcuNC0uNi4yLTEgLjYtMS4zIDEuMi0uNC41LS43IDEuMy0uOSAyLjJsLS4yIDMuMnY1LjRjMCAxIDAgMiAuMiAyLjggMCAuNy4zIDEuNC41IDJsLjggMS40Yy40LjMuOC42IDEuMi43YTQgNCAwIDAgMCAzLjItLjFjLjUtLjMgMS0uNyAxLjMtMS4yLjQtLjYuNi0xLjQuOC0yLjMuMi0uOS4zLTIgLjMtMy4zWm05LjguM2gzLjNjLjcgMCAxLjMuMiAxLjguNC42LjIgMSAuNiAxLjIgMSAuMy40LjQgMSAuNCAxLjYgMCAuNSAwIDEtLjIgMS4zbC0uOCAxLTEuMy41LS4zLjJoLTMuMXYtMS4xaDIuM2MuNSAwIC45IDAgMS4yLS4zYTEuNyAxLjcgMCAwIDAgMS0xLjZsLS4zLTFjLS4yLS4zLS40LS41LS43LS42LS4zLS4yLS43LS4zLTEuMy0uM2gtMlY4NGgtMS4yVjc0Wm01LjggMTAtMi40LTQuNWgxLjNsMi41IDQuNHYuMUgxMTVabTYuNy0zLjlIMTE5di0xaDIuN2MuNSAwIC45LS4yIDEuMi0uMy4zLS4yLjYtLjQuNy0uNy4yLS4zLjItLjYuMi0xIDAtLjMgMC0uNy0uMi0xLS4xLS4zLS40LS41LS43LS43LS4zLS4yLS43LS4zLTEuMi0uM2gtMi40Vjg0SDExOFY3NGgzLjdjLjcgMCAxLjMuMiAxLjkuNGEzIDMgMCAwIDEgMS42IDIuNmMwIC43LS4yIDEuMy0uNSAxLjctLjIuNS0uNi44LTEuMSAxLS42LjMtMS4yLjQtMiAuNFptNS43LTZoMS4zbDMuMyA4IDMuMi04aDEuM2wtNCA5LjloLTFsLTQtMTBabS0uNCAwaDEuMWwuMiA2Vjg0SDEyN1Y3NFptOC44IDBoMS4xVjg0aC0xLjN2LTMuOWwuMi02WiIvPjxwYXRoIGZpbGw9InVybCgjYykiIGZpbGwtb3BhY2l0eT0iLjMiIGQ9Ik0xNDcuNCA3MS44aDcuMnY4LjlsLTMuNiAyLjYtMy42LTIuNnYtOVoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNTAuNyA4My45Yy4yLjIuNC4yLjYgMGwzLjMtMy4yLS43LS43LTEuNCAxLjRjLS40LjQtMSAuMS0xLS40di0zLjloLTF2NGMwIC40LS42LjctMSAuM2wtMS40LTEuNC0uNy43IDMuMyAzLjJaIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNMTYyLjYgNzkuOHYxLjFoLTMuMXYtMS4xaDNabTUuMi00LjRWODRoLTEuNHYtNi45bC0yIC43di0xLjFsMy4zLTEuM2guMVptNy41IDBoLjJ2MS4yaC0uMWMtLjUgMC0xIDAtMS4zLjJsLTEgLjYtLjQgMWMtLjIuNC0uMi44LS4yIDEuMnYxLjNsLjEgMSAuMy42LjUuNGgxLjFsLjUtLjQuMi0uNmEyLjUgMi41IDAgMCAwLS4yLTJsLS41LS4zLS42LS4xYy0uMyAwLS41IDAtLjcuMi0uMy4xLS40LjMtLjYuNWwtLjIuNy0uNS0uMmMwLS40IDAtLjcuMi0xbC41LS43LjgtLjUuOS0uMmMuNCAwIC44LjEgMSAuMy40LjEuNi4zLjguNmwuNSAxYTMuNyAzLjcgMCAwIDEgMCAyLjFsLS42IDEtLjkuNi0xLjEuMmMtLjUgMC0uOSAwLTEuMy0uMmwtLjktLjgtLjUtMWMtLjItLjQtLjItLjktLjItMS4zdi0uNmMwLS43IDAtMS4zLjItMS44LjItLjYuNC0xLjEuOC0xLjYuMy0uNC44LS44IDEuMy0xYTQgNCAwIDAgMSAyLS40Wm0zLjMgMi4yVjc3YzAtLjMgMC0uNi4yLS45LjEtLjIuMy0uNS42LS42LjMtLjIuNi0uMyAxLS4zcy43LjEgMSAuM2wuNi42LjIgMXYuNGMwIC4zIDAgLjYtLjIuOGwtLjYuNy0xIC4yYTIgMiAwIDAgMS0xLS4yYy0uMy0uMi0uNS0uNC0uNi0uN2wtLjItLjhabTEtLjV2LjlsLjMuMy41LjJjLjIgMCAuMyAwIC41LS4ybC4yLS4zLjEtLjR2LS45bC0uNC0uNGEuOC44IDAgMCAwLS40LS4xbC0uNS4xLS4yLjQtLjEuNFptMy4xIDUuM3YtLjVjMC0uMyAwLS42LjItLjlsLjYtLjZjLjMtLjIuNi0uMiAxLS4ycy43IDAgMSAuMmwuNi42LjIgMXYuNGMwIC4zIDAgLjYtLjIuOC0uMS4zLS4zLjUtLjYuN2wtMSAuMmEyIDIgMCAwIDEtMS0uMmwtLjYtLjctLjItLjhabTEtLjV2LjlsLjQuNGguOWwuMy0uNHYtMS4zYS44LjggMCAwIDAtLjgtLjVsLS41LjEtLjIuNC0uMS40Wm0xLjItNS4yLTQuMiA2LjctLjctLjUgNC4xLTYuNi44LjRaIi8+PGcgY2xpcC1wYXRoPSJ1cmwoI2QpIj48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii4xIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xODggMTI1SDEydi0xaDE3NnYxWiIgY2xpcC1ydWxlPSJldmVub2RkIi8+PHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjkiIHN0cm9rZS13aWR0aD0iMS41IiBkPSJNMTIgMTMzLjhoOS4zbDEzLjMgOS4yTDQ1IDExMi4ybDIxLjYgMjIuNiAyMy43LTIyLjYgNy4yIDEgMTQuNCAyOS44aDE0LjRsMTIuNC0yNi43aDkuM2w5LTE0LjMgMTIuNCA0LjEgMTguNSAxMC4zIi8+PC9nPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iYyIgeDE9IjE1MC43IiB4Mj0iMTUwLjciIHkxPSI4NCIgeTI9IjcxLjgiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmY7c3RvcC1vcGFjaXR5OjEiLz48c3RvcCBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmY7c3RvcC1vcGFjaXR5OjAiLz48L2xpbmVhckdyYWRpZW50PjxjbGlwUGF0aCBpZD0iZCI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTEyIDkwaDE3NnY2MEgxMnoiLz48L2NsaXBQYXRoPjxwYXR0ZXJuIGlkPSJhIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBwYXR0ZXJuQ29udGVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCI+PHVzZSB4bGluazpocmVmPSIjZSIgdHJhbnNmb3JtPSJzY2FsZSguMDA1MDMxNDUgLjAwNjI4OTMxKSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMTk1LjIiIGhlaWdodD0iMTU1LjIiIHg9IjIuNCIgeT0iMi40IiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIxLjgiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTUxNl83MjE1NCIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTUxNl83MjE1NCIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFJQkFRRUJBUUlCQVFFQ0FnSUNBZ1FEQWdJQ0FnVUVCQU1FQmdVR0JnWUZCZ1lHQndrSUJnY0pCd1lHQ0FzSUNRb0tDZ29LQmdnTERBc0tEQWtLQ2dyLzJ3QkRBUUlDQWdJQ0FnVURBd1VLQndZSENnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ3Ivd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhnQUFBZ01CQVFFQkFRRUFBQUFBQUFBQUJ3Z0ZCZ2tFQXdFQ0NnRC94QUFhQVFBREFRRUJBUUFBQUFBQUFBQUFBQUFCQWdNRUFBVUcvOW9BREFNQkFBSVFBeEFBQUFER1A1L2Q3QS80Z2pjUXZxbEtSYSs1NlF0RnFiTG9WaDF1YkN2WHdRdlJFVFVVdnpiUlBOWXZxMlBXL01tMW85NE9oZVRTWVZhTzRHeWJGOVQvQURMZXg1MzNqK3VIaFJZdTQ5TzUzUEoxc1RLaWphSXMvbnN4RTZMTGFTYVhpWVpWZDZGZFE4OWI5eEVCWEFYMGNZT3BON3Nta3hLK2JPdks4T2JRY3BuRFAxTVBvT2p0Q2VaNjJUZmFUeTlvQzRjbkYxWlVlS0ZLRXd3TzM1SUU4VkViUnpMYlJtVk83dXBQY2pWcFl3ZWxqWnpQWnVzOWJxUXk4YVdzZC9ORjYrRXBBeFpObW1UOUNwSUhDRGhReUNIeHJOa0Y1Vzd3cVhFYnQ3aTNOdEVvMXZxbWlzQWd5NVdic3k4V201dVBUT0FYcFdURzhiZ3JRemNXQWFxUVFVWkJQUnlXVE5UaUJwZGtzMHpabFpySVdOQ1BhMTVrcHZUbUJzUnFvVkdwR2UrdUN4YW9rak5WZXJTOXU1dTRXZnZQYkNyMk1GYkkxSDgzWVlZdFZXNWJxekpLTXVOVXV5bjM3bTVoV1k0OEI0WE1udjNRSjVsNDBtKzVITkVjMlBUeHQ1aDAwZmdRNnJvbmcxSWJzeitLSGhJYVhOY0gxbnF4ajBMTFZha3loV2l3M0Nvc0hhaFd2ZHhSVXJSV1lYZEdrbFU4eWZsN3N3TnVZZDBSNGM5aVZKNDg4SXFJaU9pSlFCSTRMZVpOQkRVa0JURm5zK05FUW5WQVphTVV3ZnZKcGU3TGN0S2IramZqalhpb3ZkYVd3VURSSnFzOUx5Q1dGYW84RmZxdWFldkxFM1UveWNtWnFoUjBPU1A0SGxhMlFEOXAyYlBRa28zSFZkZHZMMkdwV0p5TkNkME9lSDdLdU5VRmpLWFVkOG91VGd3bklUdXFaOGFzeVgrbG10Q0hSYnpOazhPNkF5LzJpRmJUZFBOb1BFWFhTaTV5ZWhqYmlWdE4vTzFHUkQwOFlFQk85TTZBZWgrRE5Tb2U1a3dLd3hkUXNSa3Q2dUpVOVVTRkNqbzQ3YXRZdFFDZGNiL1R4RHJSUFJiemRkQ0hMYmVRcjB5NUNHV3pXMEx4M251S3YybXJGNVBkaTBrVlhpZUJXVXR0SnF1M0wrNHVmRCtmUDJ2T25jN3ZGazBQZm5zbjJtT2Z1bUxCeHBHdW92dk9SbXducWtTZ3ZpT1M1MHIzZDlQRWRDNE1MZWc2aHVORWM5WWpoRW5ya3B5MzF3SFY1aEdzOUp2UDExTmxiYU5Nc3R1WmNxemc3TFdLemREenRaTW15RmJNemI1dEEvSTZBZnozVzBjL0dlOWVLamh3N2NLQmxsT2FPSVhUSEQwTWhEVS9LQjY4R3BtNXZLams4dkxQSFZEMzdoRGFSQ2xRdXljbXExZVBVN2g5UGZPUFlCcTNrMFZWaU1Da1NRc0ZwMXM5T3IwU3dvbGs5SFhRM3o5YWtYbE1xWjRGTE5tZW9GWXlpOGRsbVVibElNMlN4dW01UWxRb3pZQjFSbTQwWVdiaEoxKzkxeEJyWEJmYUxBTW9DdEpvcDBrcHVjVUt0VlNvTUZSMHc2M0ZHdk9BZFovTlM2SXpUWnJQQm50RU9xM1ZtcDFFTDg2YVRaZEZjUFJoQmFtZVE4R0hVTjBTWlV5WGRmQVdBbTYvdXFGNklBTFJIMURSVkVZU0ZYQ3pXNEtLdmRwcDlhWEp3OFc3a1plaVplVEpxZWpMZTJBa3dHRTRDSit6MjA1aWdqOFhBY09BRnBqelVXbEozcVJJa25MTFlpSGx1dkttdUlTcWM1SHc5KzE2U21aQ2IrNTVuTTlYd3k2R2pqVG1QQ0ZremsweE1xdUZLeW9EQUI3SVFOMXNHVnZ2Y2M0MkljSE5pVU1NMlUzUkZRdGtPM3VxTlU0bjQ3ZWJvN082S1pmeWVzazJMS1BHT0JYU1pWalJpcFZBOW9xNXJrTjlNdkp1bW9OYkVZeDVMRk9kQ3VqUHJDbEFiczF0ZVlGVmtPTk03VkpwaVRrK1ZScldVVVJjVmNjdlBncXM2amYveEFBckVBQUJCUUFCQkFJQkF3VUJBUUFBQUFBRUFRSURCUVlIQUJFU0V4UWhGU0lqSkFnUUZqRXlNeGYvMmdBSUFRRUFBUWdDam1ZMW5icjNNNjl6UDk5RDMrV0RIVkpuMjBzbHIrVzZ1THFhNk4rYVZWNTgyNWcrUXkrb3A2U0pza3RZVjZqSTM5WkFTR2FRVjNVVVRHdCt5SDF3TUxyRXpXYmtxOEpjS01IZFROL2FxYXgrb2xkNXBpemVRb0krN2JpaG85Z0lVRFB5Wnh5VGtEWnpZM0tvL2RlblRTcDl5OGVQOTFZTzNxOXVETXhXUHVnTVp0OXB0ckI0WFZwVGNvaDM0TksrMDQxdHFrTlRyeEhMMjY4bmRKNTkrNnlQNy8yYTFmTkU2eG1uaW80ZnhrdC9TVldraVJoTnRuS2RvM3Rvc3NNWUN4czA3WFc1VHY0M0kxdmNpdVpUeSt2M2Y5MDBLZVNkOHhWZktlaWRaeW5RUUpFNkpCSEtnOUJWNVdQT0dlRk5zOE5QUVcwd3RWYTNwSnJQaG1jV2QzVmNVcWJCNDMrTmxOc2o3Yk1QSFdJRE1iSVU5QUtnYSt3dk16aVVnalI3ZXZhdlN2VjMyN3YxU0NEa0h3L1ByT05NamREUVdGeHlKbXFTazEwUWRWRWZIREQ2MDRjbGpkWkdTOUZkbG5qVkx2UXBYeExESHAreDlsTE15S2xtZDJjdFNGSkZJMUV3amcxZTFpaWRrZ1RwN211K2tOalpOQ283K1M4MURyS3hKU3RkajdlcGkvSmRjTW5lWS93dXRSWFJXbENaWE8vcDB4V0dJcFN6VE9aTXJhbjU5bDlrK00rVjZQZTA3SkhqY2YycEpNTE9qT0pXRWgvSm9CdUg3QjMyWEh4UlhEdlQ1TkZ4WmxHd3Vqbnl2SitMcDZrUUkvYzNRMS9wV25WZGhLc0RrZ2ZTOG5SNEFkMWt5NC9xUHZyRnFOR0c1S3ZkYVkyc3RYVjZrMkw0MFpWb25SQWFqcWpZczA0aXNLU2FXajExUFppZG9HbE1lbm1sZ1Q5TDJMbFI4N2tkeUpSWmNHWWlJbmpNcXlhYzRXdElYUU1JOWI2TTdUNVUvd0RLNXM3a1RVd0J3V1E5ZVNKcXRTU2RmYVlsOEFLK2tEVEdHeVJ3akY2ek9qSis4TnRhQTBoc0EwRzFiNjBsQXNzRnFrbWtzbzc1OXBDc01nUVFPalBiKzFObmJ3bXdVS1pNcFh3SjJLb1cxbExheGtCMXhJMFU2SFNET2duN09UT1ZEajdLUStVbzBPMnIzeERoWUlrTW44bkJWMTc2K0NXRlNyRkZWVVNRbnltYWljazNSUWhjdzNYSE9tQXpwczdwTDdrTzhMTWxQREl0ck02WDNseUdIRXJGRW1Bck5VQkFwQlc4d01GbFFwUFZYOXhibFBkVkgxd2J6eUd4TW84elc1aXViN3M3ZlYyalNhdmoyZVJqTEVJc0FhS21zREFmajNiS011U1ozeHJTdHNoTG1VS01MREdXY0h6d2hzbE0weEhsUVptRW12WU9QQUEwUkU2V2V4cW5SM2RjRnljT05hSDJJMWh5L2JqMXNJRUVHMnN6TGlRZU90bkpkRTE4ckh1VWhybDVNSXRoelhlc24ySk9zcm1NSHRjd1BZd0tBa3JGWkdMeEZ5bGNTTUk2eCtKdTZOUG4zOS95bmFtNnV2dUlOclJETXYzK1BIMVVNMjdVOUtTc3RMbHY1d2Frcnk4cnZ4cDlQWml3enpvY0JaT3JRU25sVFUxelRqMzFobHp0emVnV2MwTXMyVDNJRlE4bUNFcmtXWWt2K05pTDM4NVN6b3lwSWtKbGRFNWFNZ2hubUhkOFdhZy96TkdCNGkyMHhEaTdUTGNjdXEzS1VVMkwxeDl1bXMvZVIzVy9uR2JlUGU2cTQ4MEdqbTdEWkRpc3VycnB4K3YvQUpWNklwNWh5NTlObUozTlplOHNhZUdLSnlEVy9walZJZDNaRlR4Q2xKdzVWejNQeTRSbjJ4K1NuL0JPR3Y0TkpYSlc2SUNjZXJDYlhURVIxRjBQSEZQYjRUVUJXci9qWHhqWGVzRUk2V1h1OXFSTmJDcU5sNGR1SjRyWi9xU2xIRnNYa1FEUkkxVVZHdGE5UDBMQXhPL3NKamF6eWJHOWlUUDhlcnd2OGZXa0dOejJKU3l1MjJVNjZQSVloc05YQlhuY3FYLzc0NDFKeUlqZkoxMG1vRWdXTzdNQXFUajVMc0duejZGc1JHYmwwYUhSeE40TXNob0xNZ1IrdTFXdE92SkJ3U2pMK09WSTdlZzBkVlBuN0t5dUJkdE11VGtJaU41SnVwNnVPc1g1VjVmR09RYXZwM3dTZnlyek1qMVlqTGF1d2wvUFIyekluSmRJTzFudkNiMjdlY0EvNk9pR0w1ZmM2ZVRQcVZqWVcvVnRMK1ROWkN5N3ZDSzJPTEdaVEZaeXR6eUk1YXoyVE03eVF4TVJPdEMxT3l0YlloUnl2ODVBNmovRXc1Uzdxek1kWUh5Rk95OXkyaXVZU3lOeGk3KzRuYnB3UnNCcjlKVEl6Vkp4NkJYUUpBemRIeDFZaWdpMS9IdDdiVURORXZITk9Sa0tpRzNHMlE5RHBnSjc3cWlOcGlhTTBTWCtJTll1K0h4dHY0ZzJNb2phVnhBTWF5NStMVTB6V2VzcVRRWjUwZmRTTFJyMmZ3OXBzNHE4ZFlvWUxGZ05MTGN3Wk9xa2hZdGpKQVV3TlU3VTF2NXAyY3l5VkU2c0MzRXZjaUZwMlg3NUpvTlRXR08vSXdPaGprN3pCQXdYU3lzcStNdVVtWmtSOVZiTW5yWlJIakJhYTdyYTJQNHNldTBrbHlZcm92S3doZ2FuWEhSSSs0cFA4WU4xb0s1S0dTcWlGZlpRUVN5OVR6T2ttVjdvM1BiSWtzZVg1SnZjMUQ2WWdPY2dpWCtwNjh1VkhreElOSHl6WldRYm1oQ1dKdHRwVW5tdW1PSW5IRFl4eVFxckd2TysvTHJQVFNQa1JlZ1hReXA2M0ZCS2oxOWxtc2tUMWF0bG5RdFp4NjJRbTZxNXFXeWxxeXE0NmRVK0tOaWNVTUxFeTd0czhUUlByNHE2SGxmQVdiUnBicUFhQVdkV1JIQVh3bVRZanhOUnVhRFExeXl3NVBmM0dWbC9qRjIxdnRKRTduMUJ0WCt1YUZxT1g3Z3IvaGxSUHNyaEswaXlsbXAvUXZTeGQwKzhtUEpCY05XRHNraGo1RmxtWHMvc3NzanBXeE15R2VCSEVXY3E0RkhzbnBYaVVWYlorTGdKWXFDUjQzY3JEM0ducTdTVHR5MVhzSE9pTGp3b2NCOTh3ZVdOc0ZyY3VoZnM3K2NSa0EwdVpOdGJQS1JUYUhrSFBQcnRDOHFDMStRN3dTV1N1bStLK1pmdnQyNjQ3cndwQ25PbE9XbnNaSS9rRHRkR1Yxc05JbWpxQWh4UXgxU0ZxTzhGUmVuTTc5WnhrbjVOdmdQV1NUUnlGTWNKMlZVWDQ2c3NZazZOTUZ0S1FKR1ZPcmZjVlJJaFZweVpTMTlWR3NWN3lQS1FiT1AwbWtzSkNZMTYwRTZrNWdZdDNGcStWK3FMbDFhQUdzbGhPL0Q3eXVXcm1yeExNV2lnRnRPVGhRQ0t4ZmxPakdpRVdYcXNOOTdwNHlIb3hIZFZwc2ZxVWJxb2ovSTJRelhGOFV1S25jYlFTWWJUMW5mcC93QW1CM3FJOTBxZjcrYUwvcDRieGZtTThza2ltUWZFbkpnOEpGYTRxTll5RWxhZEpXa0I5TnNTNjlDSU9qTkJKWTJEZW81eVRIT05tSnlES2V5YzEyeFZnNHNZRGVMcHZUcUdvdklXc3FocG9TYlF2a0MxTDdmakxEa0d6c255MTdaeTV2eDN5RnRIeHlROTNRU3JFaTluOU4rMStrRk1JalY2UU9tamNub0EzR3VxdXpCaE9ZN21adnhyV3ZteCtuZjR5eWNWZWNYdnJqK043bi8xRHd6NWFwNkFsNlJQM1BrcEt6MnM3OUNqaWwvdEZDWTZublY2azJHQnFZWTNQWVJVc2pmNFIzT2pVcU4zdmlEUzZLV3pzOC94MVRYbzBabEZMbGpiK3lodDdUVDhXSVJYb1RRdzFoMmJwWnFKU0NpcStpOFRQRkNXT1VxRXlnRVo3QnJHeGxzWnZhNWlvaTkxaVFxUmV3NFdZdEMxKzh6d3hjM2k5NFJlSE1wa0JVTzNKZk5uRW1RWjZjbmIvd0JTL0lsbWptVkgrVjMwa0t0a3pPM1BpSzlOa0dVaGtQd2lwZ0NCNUhJcllVa1h1MnRVaUtQczZ4V1dkdmowVUYyNjJsWGpLblRFVjhWWmk2ZTRiRklTN1ZXV0V0RS9IV3QrVlhQVFRDRGFTbk9DZWdONmJYRE5jU3V0dXZjOVlHUStKVDNOZDR4TkdkNEIxVDdCdnM2b09ORGJDWnJJeCtPcURNTTl1MG01YzRxeW42TXpvT2YrVE5ER29vWkUweGt5azJIc2laL3k0dnYxNzVYZFFxOXIwZW1DMWtRZzN3aUJMRnJZRWlsaUZobTdFSVBCMmkvUVdrNnAyUXlGNnI5dGlMdnJsMXRKYWFqOEcxUGdXbTZySVFUQmdRcjJTOHo2MXNuK1FXUVVQcGdLMTl3VTlYOU1Wem05M3lPZEgvNXduZVV6R0dVOXBaeHZnbkZOMFBJWjBNc2tSbWZPR21lOHlhTnczVDVudi82OGw2N2YyWkEvL2F0YkZGOXUrZjRyK2pNOGdtKzlZN0hQYkdCMGZ1QWcxVUNzUkp5ZE5USXo2TTE4N1AwMStiemRvVVEyU28yZU1zN0VwclNEK0tOTkVIM21rcm0wMUtzUkpjanZIMS8yVmV3N09veDNtUFZzZEZsVWIvSUsvUFZnVC9qMTlEcHIxanZGN1dVbGxNeU8wMkhISmRDOVZFbXJ1MHZqMUZWRUVRdWRBUU5LTS93a2IyOGs4cStWazhYcDZkWGhPVDl4YzhGTjlReVpLMEhUMnRxN1FtcG1oazZyZHEycmpDYTZQa3VTRDRpVHUzTm16NGhFVjVabFpzMXdWWEZjNkhWMDRSRi9jYjR1dEsvRmxYRjhTZkwrcHlxNzdYcVQvd0FJK3M2RklXY2owdHlaWlhzb1FLMEVlcWdTTWV0a2tiTTE2MDA3WkFmRjk0UEdCRjdRTnBuUmErSDhvQXcyMEdyL0FEQUw5MGtuc0txS1JMVmtpUmZqUzYxc1phdGlWOEt5S2ZLclY3SUtkTXgzbEd0bVJZZVZmTTE2ZjdUd0x0Qy9HSU1FWjZ1Ny93RC94QUJGRUFBQ0FRTUJCUVFHQndVR0JRVUFBQUFCQWdNQUJCRVNCUk1oTVVFaU1sRmhFQ05DY1lHUkJoUlNjcUhCMFRORFlySGhCMU56a3FMeFk3TEM0dkFnSkRTQ2xQL2FBQWdCQVFBSlB3TDBHdGcyODhvajRPVmJpL24yNmdoUnc0WUlpZGdFZVZMSHZEL2RSNlJUTGdjTk9lVkVIVytuaFE1U0tmeG9jK09Ld0trQ1JSTHFrYzlCVnp1YldKMkFNVGtieGM4MnpWb3Ivd0RGbDRML0FGcmJFVVgrSGFBL3pxYXd2NC9hamtoTUxmTVpINFZadnNyYWwzWWZWQzdSNUloMWFzSjdKSHVxMVN5c3BiMDIrejdkaVpKcGxDOFpQZCt0UnVBZTZ6TGlsWVo0OFJ6cnBibmw3NnlrMEdDbnhPUHpyNloyTmhJRUJqRjVNVjNudXI2VXdTUlhqQVBld1pLUW56cisxSVc2NXdIbWgwTG4zbHZUajQwRStIcHNsY1BMcU1oNlZjQW9yYXdZUjF4Um5rbFM2M1RRNDFIMzhCV3o3aEFuRFU4REN0bDNEOE9rRFZCZFdwWmNuVWNMS2g0VWV4MFg3WG5RcGZ3b1ZIa0E1QkhOVDRpbmpXOUVNaTdPMmc4SVlwcUdDZmZXemRwM3NWcTRSN3lZYVZNZ0hISGxVR2xvbjREN1BEbFI1UUZmOVZhaENFeklVNThEVnRQdmZZZG5yNk8zRnpmU2FJOVFQQm41YzYraWNFNmNTcmk4RWcvMUdsbzBNK0hHZ0taa2dNZ0R5TDdOYk1GM2RORXFtVXlOeVhnT0FPSzJYRkREOVRWdEFIUGllZExnVWcvK012OEF6VWZIK1ZXclN5N3NzbVRoTTlNdDBxeStyZldKV1pvVmtMaFBIajc2WG4wcGV0ZDRVTWVqaDFSdnNtclc2dUpiUUhGcmIzQVRKOGVOZlF5ODJmYUx3TXR4SnJERStkT083SVB6cVh0VFc3S09IWEZiRXRibmFNZDUyamNSQjkwbnNhUWZqU0ZiN1plWFdPRlAya1hNajNqQVllNnJ1S0MvUmNYVnE3Z0hQMmw4UldZNDJUTTBrcTQzWjhQUCt0YlgzaFU0azN5OW41aXRzd0o1SkdUVzE1bSs3R0JTM0w1UmpocDhkckJ4eXJiNkxNa0lqbGlFYkVnK0hLakk4SzJ5cmwwd2M1TklOZkQyczBrTXpYWHF0MmNuVHA0OUtqM0dEMzRJUm4vVVRXMDd4bzNQWkRNQ3BQbUF0SjdTUmorWnBldUtISHFhMlZkeWVhUmZyVjBBNkR0eHVOTExUWkhsNkFTSDVoV3dhK2ozMGxOM3BiYzlzR0pUMEoxWllpcldTV1hWbFJHbWZJMXFKNmdmeXJSQk82YUhXUWRtVWU0MWNvTGlPWnQyWnRURkc2ODJxL2lzcDU4dnZMYTNWQXpkZVhLbUlHZTFpb2pGQWdUMXl6YVV3T2VWODYyM2JBK0FtQi9sVTBzNWVWWXh1b1RqVWVReld6Skg5UTB1V2tWZXlHMEg4YXNRNDN1b3FqZDNyMXE3M2x0ZTJvZUlNdnpIenFDNFkrRWNWUXpseHhLc09WU09XN1BCSEhESXlmbFcwWEVjZzdVZTZ6cUhtYTdyM0RZd1A0UmowQm1qdC8yVVVlTmJucVJtcERjV3NZSDFtMGwvYUovRVBza2ZJMU9yUkwwKzJuKzFiUXpHamtKcTU0NlUzby90RG1oUW5qWjJxOXo3MktuSUxOMkpEN1FxYmRhbndtN1hIWjgvT3IyVmlHR0RubFYyVGh1d3VHWmkzdzYxWVc4VVRTSFVKVjljZkQ0VnNwUHJrY0JXMEFrM1VldHNEVy9qcEhHdHFwTEhiU0ZRTGRld1NEakk4YTE2VjR5Ym1ITGFCemJGV1AxMjVFWTlYYnB3Y0k1WkpTRHlJNm10bmJLbU9qdFdxeTlvZ25PTzZCNTFQTkMwYnlUM1VPalZJVzA4RkdlbEtVRnZMcjBidjEwTW5Ec2ZFY2FsbnRKSkZaTFg2eXh3V1BVL2xWeGZYdTRtMGI0UUJVWmh6NTlNMWI3bVpXMDNFRER1bnhIa2FsYU1rRGVzeStyMEQyZmZUS2RETDIvL0FLbjlLeHk4YXRvSGpEYVpwNUp0SnR4N3ZEclU0ZFo0ajJ5dkFtcnNBL1YxQndPdUt2bks0WFBhNmdjYWMvR3Z0MURZVzBiTzJpWEdYbSs5UkdTMmNxT0ZSOXc0bEFISTFHN0UrekdlTGVWYkhqc1l3Y3hmV3J4SXdud3ptdnBWYXZjb3VsUHFZbFlmRTRGWGR5MGNaeE5BWnNvMGVvamlvNFpLODYybFlXTUxMNm0zdExja29nNUZzS2VKcTZ1TGxZVHd1MVhkcWhITlNEenlEVTJMbTZuMXhobHpwaVU5Z0R3LzdxTTFwR0ovMmtjQkFaUEx5cVpaTlVlVWtUazYwMFNRU1Bxaytzdm9aL2Q0bjMxZHRORHVtZERKKzdJNDRyYlU1Rzc3TnJDNVgzc2RQUFBuVmdxeHBiaklQaUNQT2swSWdQSG5wOC9PbVhVeTluVDRqdEQ4TTF6elZ6dVpjWTR4aDFjZURCZ1JWdWx4SjdJM3FJUGdvL2x3cUZZUWg1U1NqUDRVUXo0cnBScllQMVNWbVl5U1RTNTE4ZURDcmZjeE5BWm81cHhnT281NDhhbXVMMk82c0JjSUZoMElYK3dDZXRiQ21rQ2JORWtTdGNjWHVPc2ZDckNNUjIrekV1cnFXVlcwcDR4amp4TlBGWVJTb0pJUk5wMU9wNUhIR29sWHRMeVgzMUZiSUpJeHFsVi9XT2NlMEt0bVdXVkRHdDFKSmlJL3c0OGEyTmM3MjJSWW5DWXcyQnpCNml0Zzd0WFBETDV4L0VQQTFlb3JRTndieU9mMHE0dEpwR3U4V1NQYmFzeVlQZDhENytGYlB2TjlPVzNsemNLRkhIcndKcmFObTBNYWRtVGRuZThPQkIrTlhva3p3QjA0NFVWR1R4SlBjcVVUcmdieGd1QUc1citueG9lb2w3Y1h1UG83UGpUam5RK05lTlcwczJpTTRpaDd6ZTZvcmdteXVFOVh0UnRZa2dLOU9mSG5VYjNsM0RxRnZCRUE4cUJqeXo3SXJZdGhzeUU5MDNaYVIvbHdyNlViTVBpcDJlY2ZnMWJKdDdxRmgybXM1TThQdVA4QXJVTWYxemNMQ0dtWElqVUhscDZVNEpQUFBBMUJCMlV3SjQzeVdBNFlQaGcwMDRsd0pJblFkaGNlUGhUY1lFeE5JNDBkcmo0MXRaa1BQU0ZiSXFabW1sbExSUzZTUkZwNVo2SE5iWE96cmlPZHQxZHRiNURBanU0ODZ1SEt4MmNNS0hXZURSdGtTZStvNVpaWkcxTUxlUGlmbFNZd2UyT29xOVc1aWNEVDZqTWp1VHl4NVVsMjBiTmlHMGp4NnlSaUJ4clNiUXRneXArNmI5S0lJWWRramtmUVBuNlUxQkc3REpOanliNVZLN1RCZER6RnNzdng4Zk9vdDdlUHhrbGFwS2FwYTRNUDNpY0dxOTF6N3NCUkh4M2NlZE9yenh3b1I2bVBhTWFZREg3WHhxZTVXRU9HblcxZkRTQWNRdnpxRGRpY1JyMnBzYnRtSEovQThsOEsyd1lXaXdpcHVoeFVlZERYcDVuSGVxOGdqY3g5cUNXSE84UThPSG1LVnZxY2pZV2FIRFlPY1lZRGl0V1R6Q1c0WlpwSSs5a2NNZVFxMGppdTdhUFVTNHd6cU9ZYXIvY3VrWmVONFJsazZaSG5YMWpScU9tV2Jzdjc2aVhRQWtGdmJScGxwM2QrMDV6NzZranZyUGVGWHNwSmU2d09Eb2ZvZktwWnRueWYzVjlDUVA4QU1PRmJjc21iK0NUVWZ3cTJkaDBlVk5BK1hNL2hXMFZTZVZITUYwVjFSYTA1cHc2K1ZDRTc4LzhBc1JFbU9EZjF5YXlacHljU0h3OGZqUXkxU1lyajhLWEdLRlJzSXpxdUhqUWZzRmQ5Mk5UZnhhQWZqVVc4SDJkZUsyWXl0QmJ0TklETG5zTHpQR2tSclV4eXkzR3Ixa2wwN0ZRRjQ4T1hTci9Ra2JsWFNRWjBZNWdFOFJqNDFkcTg3dG9CZHVHdlRxVlRqdTVGWEVtNXpyV09UR1kySTdTZytGWDdxQTJwSVJJZUI4Y1Y5SVpOblhGdDlqZzF5dlBQSHI0MXRGN3U0dVZJS2ppd1h4T0tqWlE2NlI0dDFPS21kL042WXF5OTByekZPV0VGbk5IYUJUd0VqdG5lTjlxa1JrK3NXMEthdXlXREQxc2hIOEpxMVNQZVNYVWVveTRDbUx1azRISjYxd3RMWndTUjdyZ1lMZ04yMWI3UzRyYWlXQ3ZkTlBHb1hNYVNId0huWDd0TlI5L2QvT3VTREM0bzF6cSt0bGY3Qm5YTkVBOU8xWFEweTdSamhiVm9FdU11dWRLdjF4NzZuaWFXSTRrRnZ4VUh3ejVVcnhsMU1aM2I2UzZuaGcxTHVoQXl2bWZneUZkU3VoL2dOU1hFbHVpQkZNc1RNR0h2STQ1NVZ0QlRhaUZwNzZjbkNOcGJUREdFSFVLY1pxekkzWXhyWWFkVldLYXZEQUgrOWJPUzN1eDdTUWdFKzhpbkpRdTd5NFBha0pRcU1ucUJ6eFM5clJIcWQrSjFxTUVyanhwRlpEd0RvZUhvdFgzV1ZkMUl4cVN0bjdpM2VYVkNvWTlsY2QzNSttNXQxeUJxMzQ3d3p5SG5YakdQeEovTDBESkxZRlptY0wyMjRoUDYxYXBabkhxb3lpNlp2dXNCenFSdEg3bDIvZHQrbGNmZnpCOEt2M1dHNmowWEljOEc4RDg2dXJLSkpWeUliWmZXdVR4Wm0rTkNKODgxbk9OWTVFRHo2L0N1Tm5zN1NpeDU0UExqaVQ0NDVWc3R0N0hKcWlsamNhcFJqOEJ5eFZqcGxZZHVLVWQ4ZENjMHQ1ZlR6c1o1NVhYRWFGbTREaFdNK0N0bWtJYUxpNFBoNk5ySkR1cmN2RzdwMkdmN0o4cTA0MmxDeXkvZTlsdmYvU2x5RVBHb0djd0pnek5IalQvRDUxem9laTBobFBQMXB4cDQ4eDUwT0JDbi9LK1ArcXV0RFByQU9GQ08xQVVtR1pKKzE4UjhPVlQ2ZnFoTEkyT0l4MStkV3JHV2UyVmlPZ0pHYXdveUcwRHpGVG5oSUR3OTlYRUc4SmJXSmYybmY5bnlxZTM1ZnM1RjdiZmNwZERiNlI1Rmw0Y1N4OGF4SGRJdVlMZ0RsN21IUDNWY29UYlNidFpDM05PbGJUdUJFcmF0eGFETzhib0Q1Vlp2SGp1bnFLazFhNEhVbHV2RCtub1pzZE5Kb3JEYjI0eHZKT0ZiYnRMbUtRa2lQZllZZVhETmJKdXNEMnJkMWxGT3FrYzF1YmN4bXJRa2VNRW9hbjAvNGlrVkI5WUdyOWxITHhhb2pHN0RHaHZaMXJqL0FKc1VtT3RjRG5nYU52YTdSUlQ2eTZtWUpKeDlublVvemNkbVY5T0FCMXhSTzZpQUNqeUhLdTg3MWI2OTJlenhvdzVHQ1UwZHBldVI1VThDYWx3cnpkN1YwMCtkYktSN21MMU9kWFBGV1FnUld5cFJhdVpPMURHR1ZPV29ZcmFadHlFL2JzTTZhbmxsSVBCdWxlRmMvUmwxVG5scWRrT2VCUTRyYjgya2V6TWQ1L090alcxK3YzTWZyWDBIbnRaRDdVSC9BR244cW4yaEd2OEF4WXQ0djYwOWxjNDZqMWJWcys2Z1JSaDN1SnRlb25tUWZJME9NbkU0NWF2YUg1L0gwSVBqV3pJSmczVjF6VmxIRmpvcTB2S3AwWW52dVl3RHp4azQ2WjUxY0dHMWpJYlZJMzh2RVZkd3ZjUU5DRUZ6RjNVRWhabng3Undmd3FhM3VJWWdOL3VvbVZpbnROai9BTTRWYzJzUFk3aGgvTVZzNVpwcEgxNzlPWmJ4OHVGU3hRUzZqbzNxNmxQWEZYVXFvcTZuWVJmbFd3cExvNTRQZXpkbi9LbjYxQkJFTVlXTzNoQ0tQbDZJMjQrRklSVmhMSVB0WTRWOUpMS3dYKzdlUWF2bFd3cDlwVEFmdFdYUW56UDZWQlo3T1E4akZCcVlmRnEyaTI4SjR6SWNQenp6cTRsdXRRMHdSa2pKWStkUDJUMSt3UnliOGpTOFJ6SGo1aXVCb1VUandvWW9JUFduVEFuSFRuQlBNK05SM080dFJxMHdFWVh6d2U4ZktwL3I5aElvYU10M0pVOHVxbitWV2s4c0REVXJDWUtxbks0em1ya1lFam9WaTRwcVU0YkE2ZkRoUkpJVW5MREFBOTNXcG1SZmFHUVZaZWF1UE9weEdqRERhc3NXcVBzb09YaFVUcnFQQTQ0R3JTU1JtNUxwcmI5bHN4ZE9kRThvM24rUVphdm8xYzdablhsUGUrcGkvd0F2RW10b3JzMjJQN2pac2U3R1B2YzZ2M21rUE4yWXVmbWFRZTl1SnJqNzZQeXB6a2NqVzdnaGdqZVdlZVNUTE81UFNrTHc0eW1qdlIrN3hIbFRhMFBLYVBsOGZDcEt4Uno1Q3J1T2E1ZVhYS1VBQkpML0FLVmZidmN2b3VWWElFMmVPQ0RWcjZ0eUdUVjAxanRmalZvazRpQVlReVBnRThSbjVWZElxaU9XTkkxSEJSSjNzZWZDcFFyYnpXQ3ZNSFJvUHpGRStWTjFwZ1loM2xYclZna2thTndnbUpDZHJoNytkYmFGaENZQ3p3N01HNUdnTnBPY2NUeDg2bWJVSkNzalB4N1hobW9COS92VWZuLzZCZ2VkR2tCODJxVGV6WE4ybVdjNFNKTVlPUENyclNzMHhqNktaR1h3KzFWcFp2OEFmUm8yL3dCUEN0bTIzLzYzUC9UVnVrZitCRnAvRTVOV2VjU0JScGNBaytYalVPaWNkMDNNdW50ZUh2cWUzV1hWeHQ5NTJzZU9lV1BqV2plU05uZE8yTXFQL00wZmhwd1BSMW84aG11OERrSHc2Z2p4cTBOdy9MMVE0Zk92b2Z2RlBUZllKK1lyWXMxaVM3c2pYVWZaMXN1bk9lNmVIaldxV05kekhFTk9wcDNQTStWS1JqdnBuTFZzV2R0UDd4VG5GTGcxeXp4bzlqMmRTMWJmRkRXMEZRbnBNdVB4cEJJbjI0bURqOEtKemJ1V2lSdVFKOHFlVGQyenlHVk5mR1hWVytrTVc4K3RaSTlhRDNmaUtQcjdWWFRlTzJkYW54b3BMYnk5cTJ1SGo3ZVB5SXE4VjVyZVRBbGtHZFFIRlNmR3JnM0c2VlFaU25GanJKYi9BRWtmS3M5TTVQUHo4dUhwOEt5QW50akhBOUs3Q2NueC9LazdYVitwOUNoZ1IybFlaQnBjUkR2d2RGSDhQaDdxMHdXcUlPV1M4cmx1cHJhRXNlWHdVMWN4VXhaanpxNFJKVVhVc1RBNWNkZU5QbU4rQTQrbVJsKzZhbDdUUityWmt6L3RRelNETkF6c085MnRDajg2LzhRQUpoQUJBQUlDQWdJQ0FnTUJBUUVBQUFBQUFRQVJJVEZCVVdGeGdhR1JzY0hSOEJEaDhmL2FBQWdCQVFBQlB5SEFORURuV3pNVEpwcXc2S3ZvaDdFSFdvcjF4TUF0cE9hWFh6RGc2dW9WbVNpQ3pqRnl5dDh6MEpYMDlqak1weEQrSlZxVlNsbThabFR3SFRVTHdQdVdRRzI3cjEyRzRPTkx3RGZhRlRVeVg5ZmNRRVBJNGdxdHRaemxuSkxsaHhuNFU3T2YwbFExSVR2S0dpYVhGT3lWaXVaVVdlRGJ5WlAxRlZZbnk3ckhLZFJpUjJxbEF1aXI0ZzhLQzF4MXlvT2wvd0RPZ0Y2UERLRFJYd1lreE01U1c3azI0Zkh4QkRlb1hodnhkZXlXdHUxTDQzQmZjYS9sYkExZ01kU29CNEZ2K0k4Q3VGcFlRNlRtYWRGOXp0NDZpQ3U4Y0VxdXRWcUN3aXpNd0pKaGdhVGg4a2YvQUZYR2tkVTNXOXpMakttNVZWWlFTUzl1dWprUGU1UjlUUkdQdXpMSUtWOVJ0WERGQm5yOHdjV1g0VFZqaG5tVzhrTC9BTVBQOFNwZFJsT2llaWFHTzJrMnJnNC80VWxZUGZlOEFYQXFVRXQwRGJ4RmJjc3lQQVkvVTgvY3JOdTg3dzBYN1RqWWtmNk5pekdWcUNJM1pGbE12ekd0Y3Z3SmIxdW1vQlVtQnhZeEJPYVAvYSsyT21WVkU1ZWRPSDh5a25nOVFZeUQ2bEJzcEhpbWNwc3JQaytadDJHc1FjclIyM1pNSjlSTHFHSEo3aEJkWG9ZN3MyNzhhWlNIbGNTalcrSUl3REdHZkk2SEQ0bnk0My9kU2xOdk5IL01vWWlyd05xT1pvOXRGVnBGZ1VyMkNZSExzaHBJTFVRQ1dVbmlDdmJPUzdKRVY4c3ZMN2dIV3QvcXdLK3N1bkFFVjVaMHRnNDM5UkhQaVlna2dmQ3c1dUY2MWZoY3hacStlQXlpdWpDcFZaMDRxRUJHMUc0d21uekZyYlpoOHBIcTRMcWh0U3pKVXNVVGFiUFNaYkpCTG13Y1YzcnpFV2R2U0kxR3UrbXc0cWZCUmdiMkZ5MnpiQ2ZPSE9uVXZPc0l2OTdDcmxqM3lvSDR0TGd6RDdOWEtDNEs4UnpvdEhMVll6cHRvSnljQ3VKVEhPTmM0Tk0yRDNEMlVlaitJLzBWZmJxODlYM05jTDdOWU82Tkh6QWJ3enFWNFFNWHlFQlpYMUNYUXd0a0RQRDlLMXgrZXBsNW9rOXJucEVCcjRVUytwaXM1UmZsK2pOd0R6S3E3VXVXMVBLdDhPVjlzcy9Bc254SFBQektIdmUyaFZ2bGIvRUNIb3VxcnhSM01lSCtNQ21sVzZ1TGIwZDExUUxyMjh3NjdNaUhFY1JBNDNBL3gwdldBRk1ZWWFkTFNDYW02R1gzM0hWWjByYmRWekZDT3UxWXM3bmFyTnhOeUJMdUd3TG5yTnpnbHNsdkFjVS9LWTZYNjQzdzlEVWJCMnFDUnpXTEdlcFdHbFN1aTUxZmhHSXc1dFF5VWVFdDNNMkZnYkxTdis4R0xkVVl2UitLYVpHMUtnY0xLN091aTkvK3pCbmF0MzBuczNUaWkvTVNNWExEb2FHTzl6QmJZOFZqeWpKSm1HdGZFQVIrRG0vWjlrcEVnbFZ6UmZPbzdBLzJPWkxlYXVhaUdBN3d0RXRkWW1nN21oRlhWdkROREtxRTZvQUREZE1zcThobmFCMU9JdEtkVTdIS3RWcStVRitUZGxkaEMvRHFEbXl6bG02LzJtWHNZK3dkVEZnT2xUUEhSelNLRjFqTkhNcjlPYjdzOEljb2pRT3lORmRxdTkrNHBUM3ZTKzBaNFFOelpQOEE5TkdBTmhqYktqZUFxYVNGT1JyRXk3bTdWOUVNZWpsdVdzTlhDZTZ0R2FpeldZQlY2U212cTc5UmxBYjJmQjhYdUp5SXpjSlFWNzBRZms1V3RzTEdkMW9Zci9XTGNBMzl3NkszUGN0UHRZTVJjQmJjclQ1cWJqdnhHVC82bkEzWUs4V2FLd3hDMC9BMnE4RmJ2cTVkbXBFNnF6bHd5NG83VHY0NUM1N2h6eWo2Y2EvODRnN3pFbGFXbkdMZmdkazQzY1hubWdBK2JZK1JJZWhvMVhvdVNWU0ZSMnB6VmNaalk3czRQSnpOMFJQR2VUNVVFWmRCK0QyT0ltR080YzNjSlltWFJPNVJrRHZ0TXBWdGc0Z09oK1pMMjUrSlhUU2M3T1dGRnc5TXo0SlhBUEY0dDFPZFIwN3ZxaWVGRHQrUU1hSnNxcjVidmhSTXF4cTJNcG5Wa1c0Vm0zZ3c2eFBUajJYZmtERWh5NkxIOGlwdkRVVjBHcWJKSWNrOFJPL3p5Sm5rTnh1ZUd0b3dYc1ZWWG01VXMrSmtxajJMNHhDd1Q4NzhsODlQZEV2Tk10YkhMUmxEYkd4YnNsZCtJUHNQZEJhV3FUc2pVZXhwUUdUeVRNM1ZNNWFWcms3NlpqclNLc2V4bDFnMFdRaXpWbU1IcjNHbEFIN2xuZG5YbHk3WFY5M0MzUlRsNnQxM3dKUTZHM2I4WG41NWxJdjBmOGlRNjF2cVhVN1g4RmtlNzdJSXhoZExXNmh3NGltb3JCMDB2NW03ZllZQjRSSmZ1SnZ2UXN0cFZQbURWNVprSWZxbW9zWnhLcnpkKzRtdWlzdnhPek1RR1hXUStRRHVWN2dSczdabmd4eERwVEYyRkdIS1hZdlUyVUtCUmEwcmJYZ2xGc0FOVTdCcHZ6TTVKSi9iR0dzaWFuVXdWRHhTYlNxbDcrRWdVZVlmcHJYMGdWbGY1RC93djlZOHdNMjQ3Z3R0NWh3OXhpdmxLbEdHL2M5UlcxN0pTK2ZaVEhSOHZjSEkxWUNZR3Qrb2N4QzZvUytZbzU1VEdSNU5UcTR0SDZYN3dtRTBEa0YzYlVjUnBEaGkvd0NjRjdXNmhzY3ZybTFJQVRrQnpVeGNyeVdOTEFoaGQzRlNublFOUVpxbUp1dEVaMlltampNTStBUzFvTFZSOW85elF1eExTWXYrbWd2OUNXN0p4dVNLd1FWVWh6ZmNCVmkrSHFyaGljeHJrdmJuY0xBdHgxSEtlc3dUVVBGL01UN2tEeHFjb0k0cnFlOTJ2NVRLaGRqb3ZReDdUS2NTdjhBd1FMUDBtMWkwVkRldHd5L3E0c3QrRzBzY3FsWUl2VGVQbEVPVDJoRVJJM1JiNXJVK3BhczBiQW01T3huSTFCVFdEQ0NaaE1EekZ1eDA1VTlUQTFERmI3VXN6Y0JwNTVhdkF3WlU1d0NlMGZ5bFNBdjgvSEpCK2pxdzdhN1ZGY3pIOW5CeDNqZzNTTkRHeWEvVDArSE1icjB5cG0xRzFic3ZoT1lWdXh5SDBEbXRpOGt2YlBxTzF2Y1FsaUZsZ3VOQ0ZoZi9BTnVFSnpkeEdnUUhMY0RUaEljWkJNKzBaYVZZS3M0VDRNWW1vN1BBVzhuSGlhRkZWclYzS1B6R0owZXp3ZG55cTU0eTFNR1JrMHU0ZFdEYnBkRHhHa0xMY3lETGVFb1BOc1dscUtJV0ZEQnlaM09TRmhvdGk1YzN6TGl2YjRFMFp3UmpsZVVsSDg0bUlkQUt6YXY0ZjNCRHRxWmZqdThYbnQyUlB1cmRzb1lYb2xYeVFRdjhnMHlxQ0hEQ2JMOHNZZ2s0a3Y3d2xuVk1wUUtod29rSm51cjVIMUNWbkdvMlhzb1ZjMUNTY0k4YTNJOG4xQUZMa1lyQjhCaHJ0eGdIKzBTZWY5bGd1ZmR4STQ0TWUwUUNyN3FJRDVzenhwdVZBNTZWejRZZWE2MitDc3kxRFFkbnhmOEFRVytQVWNtVEgwdWw5V1k5enhYNEdWNGg1NGxic2kxK1J4VXZwdmYwZjdobVFGNTVaWU5nckJRNm1SWkpxaHZuWC8yYkgrVVZ2S2g5d2lsSi9GQTM5Ui8rQnNCcUdVKzM5UmN6WXAzcFUvWkgwK01DRmNBYzdoMVNMcGJvRDZFclp6Z21tWTNXd0dtY2ZxZ1p0VlRKM1dPcGl1ZEZ0Y0RuK1dWN1R0MVhRK284cU8vSCtxVUNTbHFGOXlrU1czZTNaN0dHWFAySktjRDRTMTVKbnNEWXhONTd6cjNPR0pMMCs0djFUQjI2djRZdG10eU8rdjhBYmlQSGFsSHkzeExadVVmTVZOcjZRdmFtMDJEWlh3eXFPV1hSOGt2VmNMRDhYblltaHY4QXcwaGJ3SlMvaWk0Vml3RXZvZ2dXRnloNTQvY05OczV0UjF4a0plVGhvYWpvK2NQRVBVNGZ4TXJ5T0tabUphR1crOVRqVGluRjlFV1BFMnQ0cEgxclJHZkVjSUxTajVVUWpkMUZCb2o2b1BITUs1V0wwTkMzMjFtbk5RaXlCRlVsWVJLZTdyTW9xQlpiT0JZY3d0NXVZSHVya052c3VMdzB3YXIwV0xsQkV5VStsZnpOQ2lvSlBXM3R0bGJHL0UwTE5PZVNZUXJ3WmhHN2VYNzRKUW9kNXArMzRHTnF3dG03MnZ4SDl0NnUrZlViZ1ZWa3RLeFJ0Z3AwRmhFQU5MbkhTR2R1TjhINFBVb0phbHdmc0dXaW9qOXl4cGErQ290MUFpZVUybFY5bU9JSGtpeTc5YjFXUEU1bmprM25pTmNLTXgzaEh1UkhaU3Q1eVZ6QTBxYm0zQ0dqdXpuZ2pla05VaTFvclFqNmpwNFNZMks0OVRpdU9pbzNHTTZrSENmK0NyekdyOXNxL0JDVzNuSDRQMWhDa0lWdXZaOWxVNk5OQTZXdjdUZkFpVDgwTWtML0FLdVBxYm1YM0wxSkJsaldEa2U1aytFNEp5T2Q1TXhYRnNaRTV1VldONVplT3p3eGl6bGFZb3dLM2N1OXIyek9QTkNRclJycjNBNDQvZnA0VmtUNWdJbkR5bWxRNHNES1p3NkJRdDRYMUxQUU9zbCtpbzM0bGdPb1Y2UXZnTWt0eFdtVEJkVGdzZXZBeWdKbW5QTExmdXFNdWxvbEtHdXBWVEJZMHhmbEZaSzZsQnNndnlPZVhtTEtWRlpQTDZpRnF4ZHpEeWdCRUJhdE01WXMybkJzMGZpWDY5SGZSTnZxV2xpOTJabWpaMjlRbTVXWDh1dDlJN1ZQbjZRUDNOOW5YOXJmcVhSVDF1Y0JSK0V4UFFVblVhUDdSOG90TzFKakZxNlh4THlWcmVmeWJjSlRxZzVySnppRS9wQTNtWmNMcndGNnJYQVc1NmhWcTRHQm02dDU2aElXTzFnL0JPYUM0ZjdHWENzTkV3dGl6S3RKcmpBenEzbkE1eHhDdGF5OGU1VlROR0duaVpyT2lGTEtBb1NmaGlRcTlQNllrY2huWDRSZWZGLzJ3K28vYWtteHFXMnJqekJQdnQ1dnBjVFVYZEJuMmhyNU85dnZIeDM0aHd1cVJtclBhS3N4aXlCSVhjZXB1MkV2Y2VMNnFsY2gwRHpEVGM1ZGdzQUhLbGhIRjhyL0FNKzIvY2Rwcyt4aDJZWnh3R2hkL3AzQkN0TTVrbWJmUGN3S245TUVaaUl0MitRMy9wNnFXZ0lhS2F0VDNOaG9INXYvQUpQOWpMcWVoOU5WQUlJZDFjTGx1cndUWlh6S1FhTnk5MkRjYkpmSVRNTmNBeVBPOFBjTlZ5ZHk1eC9WZ0FUbisxcy9OSytDZi8vRUFDVVFBUUVBQXdBQ0F3QURBUUVBQXdBQUFBRVJBQ0V4UVZGaGNZR1JvZEd4d1JEaDhmL2FBQWdCQVFBQlB4QS9BRmdlTjV4MzZwZ1R5QzljVkxuM1pEcjVBY1N2TmY0Z2wyU3A1cmlsMmJxMGhHdFVId0JsTTRVemJUamU0R2tUWmtMeVBPQzhnd0ZZaC9veUZlVUxic1g1bUVETlgvV0pUZU1GREFWMDRZNTZKaDFDR25naEtNT0tvSzJ3ajcvbU5mWWZybFdVTUQ5WkZlYnJ2K0dPOGZiNU56SXpBZVdCblRTaEtTSGRrUkJHSytMNEtYai9BTGhSQ2NZOEZnbys4UkRncCtOWE9vZXlST0h0TUJ3VUxrbHlIZG9jSCtobW1LU3FSTmdwamI3UVUzdUZ0aGswWDI0UTZ1TlRnYnFZSXNQUlNvZkZ6em1FZVRHejI0YmNDWWdCRndRK1IzRHJHd0F0cU5FTkNhUXlzSGFuSTNXTFFxb2ppblU4Z1lEYWJPc2RPb1dadmRkVEJicU5MVUt4Z2lQTk1HaXFCWmtQL3dBVDFpMzNRQWFQb3hWOEtFN2laMGZJWEprZXNhNWRuNGc0elBJRFlobk5DSUFlVE5VZ1hWcnVoeFhabmhybHZBd1hTTDI3Y0ptS2pKdy83OGNUSmdHOFFjblpVWThiTytzQ1FPMUlHUUFDTzd4eG1IWDVnRm16d1hHVW9laGhLeEdrVHZJNFJNT3pwYU1MRlk2dUNBMXFoV3dQR1J4MHZlNlhUV2dBM0N5dG1rQURROUgvQUppdTFnQWcyQkNxNDE1MlgvRmpDaWpPQmRkUUMrTW92RFV6M3VGSG9RS1lsRWtJMTRqRTV2dTRDdHdrcnE3REUyaEd1R3VwOGY4QXpBK3RLblFEOS9wVE51SE0zeElLQkxHT0dhZGx5VEpqN0VJVERYMWtkeWh4MVpwMEFuRDZBYnhhZ3hTRlFBNlc2c1pSNmlhUytDRk9xQmkvK2VvdW1hcnB0ME1uM2NKSWFxNkV3UURFY1dmdXNUVzNDaDFhY2NoREh2OEFKOGNiZklqWDh1V0RwdUY4VG1JeXh3V1hicnQ3WU5QRGplNUVFODBxUzk1RHVrSUN5VWFqenJaMHh2YUFsZkl2S2t3TG82Y3RVQklSOFZnWCs1OWNlbDRTRGdzdFN2Q1EvaVpDanNnK0dRQ2tDZ0o0TDV3U252MnBxSDR1UkRRdWo4NmFmSTVRN0JBUytFZlNZZEc3a3YzRHZ6OHpUMU1UVFk0eWcxQnhVcU1xOWpFbWpRNllBb1F3aUVvblVsVUNKU2lEOFlQUytXVmsxQTl0Sk4yR0wwYTVpSjFNUWh4SmptNnZwREN3WHR0WEZ0SUlOUDhBNGVUemhlVGtyY2dubk5wWUZ3MUdzZjVqRm01dURPeURXeGxUamxzWjNMblBrTVR6Zzk0MXc3S0NRbmxha01ReVE2eDZyMHpzVXlTaVFwT0VYdDA5eG8yRWJwTW5BQVhzRnluOGp5VFBiWTgzR0ZCM1EzaUo5aWFqcTVzcDRGcWFqSnBjTVoybEpNMWZmZkU2Ty93UEtJcTdNVmc1SmJ2V0swUUVnNDROY3BVcjhpc1pucmhrQjFmVm5BT2ZiWmh1bXpUKy93RG1HWjJNRVE0YWtHRG1ndDZQZUMwUTdCdnZKYlJVNmdnMkdMenJGb3ZTZGhXOHRtRXhtT2xhNkEydlk1ZEtQdzJocDNvOTdCZ3dNYUl5bGlTRldzVndpR1VXc0ZHVXA0YUJneVk3cDRtR3puZm94NmJVcDJzTVVWU3B4aWM3dEUzY2tka3JUYkJHUnJFbE1xNGxRdzVCaWhTZ21MbjFBQUhDUlhsRzRHYUNVWEJ4UVRjUVBnaElsSmoxQldpKzB3TEVJZ0JTWjhZT0lNcVBlb0hCS1Q3NytDSzhPcWlTRTUzS2RWY0o2SU9KcHRlNDFJZGlFRk9naHM4WUJOV284Vy9zc3hHMk1XbmRyeldCNU5KTitNM0YyQWVaZzFhWTdVU1BnZlRqVURvTFUwR2d2ak5xc2xBbjR2Zy9NaTdnR01xcHk1aEEwaHZxaFRhMjF4cHV2dUhiclVTcTNobmIzOHQ2YnlCVTRybWlLemM1bmdXcVhMR3RLMVRmNG80VENLc3NoQlNHbXdYRUtRSmFOVmVVSXFZVStNMVA0YWlXRHNJemVNUEN0Y3k3WUJBcWdZTWJBcXFxV3pDU0J3ejRmRG9xaTMySHhqSDhrYVE0MVZYZFBjVTRTRTlCY2llRXBXSmlWRzZvb3dEd0Uva3hqYXRxZXJ2RHhZUmVoUVE3QWwyZGNWbHFiYit1ZENnM2FGajVNRXd2Z01XYVFMRzkydVI1VStFdjFqbVFKQ2N6dUpNU1FwQU5nSlJjUk1PblRhOG1oRFR2RzExaWlseTNqVEV2MmhyWWRRa0JmRm9HSHpXUzJRcExoUFRWQXgwMFIyVUlqSmlLenM1bXd2c3YwNGYrbXgxYzRDR3B2dkY2eGRSbVVlb0l0NEdNelJZQk5jSWhzN3Nnb0FZenBsNXN0RFF1YzNidGdnZkNhUDhBTEF2ekQ1d2l5eWRBbXdySkFyYzNFQXIwaUFCaW1TZDFLVFE1QWRNUzJKbVpRcEhmeDlEanJyaTRsMkhxMmNiTWEzSUpGUUlEWnFlTUlNR3Z2V3Z0SStNcnpjUWVISHFDNUxINXhaVnFRTUhCT0pkZkxrLzFKZUtrNzR1YTJJWFNhQnFFQ2pldU0yS0NVTEFNUGxKVFF1R0xKdkJhOFpvRnc0NGFWQXNPb294bWVXSkg1OUZNYzloTjYyU2lkTUF2bUF4dlhOc1RmaldEWEx2U3g5SlZPamJjdUVjQjVoc0cvYUppKzZNdlFhV1BDSVRnWXY4QTZsQklnUzdHUzU1Skl3NllEWTh3QXN6aE9CZ0VOeGs0UktRMDNZRkVhSlllWHR1dmdRNkNoK0ZseGZGbC9XZlVMZ3FUMXJqdE5vanF4eDZjeUJ2U1RxUE1VU1l1K0t2eUFtRVhQcnErNW5WK3ltcVlUSC9jRHpnY1pFbXhEZUVoWDZOUGx6VlhjZm4vQVBXYmpaVld2K1MvNWpCVlJ5b1lVZkdhR1FISXhqZXI1dTNzMzh5VDJFRGJ0UjZDOTZyZU52S1hZQmp1T3BMMEw4WUVIRUFCRnRpQVQvTU12dEJTRlQ0UHNmWm94eEFBNzFYVk1oWTlycmJteUhsK1FINVdqT05Reko4VUFJZkN4ZVVOb285b1lVRFlvWHdJMTZPS0pnQ3VQTDI1Kys3NU1aWUVVUU5KSUpNYUJjUDk5aThTMVNPZU53OTdYdUpNb0tHdE53a2MrRnBDc2RselV4M1ptaUFQSlFaYlZLWnJSbVlpS0p1c1BrTXluamEvSDg0bzdIQnN3V1gyelV3dkJKWEVheEprK1B2R0dQTElOdStENjRtWHR3ZTY5WnkrMWVjSHVnSHVjYXlhUmF3emRjanB5bEZJRGhDcUxtN3FJRHYrUXYxTWZpNk83R3d3OU9HclIrWkdFQWFuTW9HRHZtTVZKdmdPNHMxMG9SQ1M3V3NGaEhWcDM3bi9BREF3cUQxS0lLMmVUNHl1MlMyNGlpQlNoU29tRit1S1Izbm9iYWE4REsrTGhmbEJGT29pM1pPb2xHbmZMMWM4ek5RbHY5SU5zS0xTc01MSzNBUVhUOWhCUURqY3dHMmVraDBWaGxlN1NJUWhZc0Rzd0ptbzZjaFMrdFl2N1A3S2hEUUphWkVjcFdydkwwbkc0bFNlOGFMd0FGR0g4b09pdHdWZzZUZzRaQ2NWWmU1WVRkSVBXUWp2MjJCT0hsU1k4eWRGMDJoNXJqRTVQYUNLSDlZTWxhbFBkNW9RazZrUEdOblI5YkIyajhPSkRsSTFsWWt5M0JuaEQ2d0x4UFBhc0RQRUlUQ20rYWhwRUZSZHRyYmxNVnBtVFNSK2FDTzNIMFA1RVNoc0VMV0NaSFZ1Q3RvYW9yWndTY0UycXBhdWVaV0llUTFXYUhTaFlYWWVjV3V3Q2RkTjlaeEdSTDZibmRteCttU3FQdFNLc2tBU2NINEZualIwMmFQY2VZS3NnbzZoTjBnQjBtS0FBQytyZ215MTdNRUtaZzArTTcwYjVXSXY5SUhNNzVQTVo0OEhyQk92SWZ3QitqVEtoaEIyVmZ5NE5QQjBNN1VhSFNBL2x4V1B6WTlCeUJ4NTdERkNUUkFJR0JJNnhjWjcxZnNzN2pzRDJ1RDN1cXljd2NlbmR3d3U1dEFyTHVEWjBHVFpielFVK1VoWVU0K1pBcXAyZTRmREd3cCtpaVcyRFZwZll4YWNTeVFtbDFob2UwZGFYNHdwQktLQUVhR1ZvRG5VcUJERFV4TFNMc3dlUVN2dmJrNURiVXpaK2tjVHFpMHB2QWJFeFJscWdHOTB3MG8yNHZQT1hQWXQ4WkdCdEhxYi9hSDg1cDdYRzh2S0JHTHZaalk0ZnRQbkN3VU0vdkFkQWhHbWNkMGVDeFI4UEJGeGlOV3lhbWw2d2NhaUtweEUvd0M0bmQ0TE41T0V6OVFEYnZFRXRkVGVXSTErbVpmb2dud3A1d0NwSENJYlZVRXpGYTJQSTZzdmVLOHZrb0FIV09MM0RoOWZNVVgzckUzdURlZ0x6YmViWGs0YlhGeDBuR3JDYVJKaEQrcjFackJGOHRTQnRsbytnTmx2VXdIa1l0eUhOWWdWQW9kcGhoVWVDQ3hTQTg1ZnN5QkNEU3JvTDhCZkpqa3VCMExqeXluUmEzUnM3aHk1QlYyaXNCZEI3VTRqdXB3V1U0aXlrdzNQdWhwN1hmdEdQVHRGTCtoWUthM1dLUFRXK3JsVFNHL3Z5WU9zSHFlbGhLMDFNY1lMYXBHNHQ5TVd5ekUzdVB4cGord0JDbHNRZEtPRFIyVDJFTUZWVW9WckNJRnJDdG9Sb2dKUldCdkhKZDY2QjgxUVhIZmNEeVR3K0RUREs4eG9VRi9seEtJamFTY01FUGN4Mnd4SThyT0dSMU11REh6VlJCU1Z0c0p3b1hrWWxyYTZ6RlB5dnJseFpPbWJIc0pESVY4eHMvbGptZkRxTDVWVjQwVEVFVE1QWW4vTGhNZFc1YW4zNnlOYlVpTWJoekFPa29LdFBYckhpckRPeXRST0dHbHNUNFJRWWZwZ0ZaZ3N4OGY4c3hSYTZXWDJnL2NEKzJ1SWcvM25CaU5SM0RaV2c2eTYycGxXcXlNdDhaMXNzaitBRmZRNDBJVWs4cXlvV3p5SDJQVENMMk54L3JYOFlCbjVvL2daVFRnRGNmT0xMMlVwbFdwRVFWUE9HUXpDZFA1clVWZ3dNODJMN2lpbUdUZkhnK1dYdTNJazZNQ01ybmx0aWhPTG9neEVlTEtLMkpDZWhJVFJpQkh2VzhBSm9Jdmwxa2wva3RZOWtJRlFNNnlieHorZThpWXdNNGJkZEJWYjJpZVhJNHdrc0wvT09YV0lsVDI4d2xXalhYK0dUbDZJWWZiai9PQkVSZjRmMGZqSkJZcUp4NnN2b3pVR003NnJpNVVOMnNBZzBWTnhseHNkaWoxSFFtaVhlVjk4RGtvSTlLbjVsd085ZHRlSWhvR3pMMThTTVI5UCtab29TTFFudVpwOGsxUVBvTXBERWlFelMzMG85U2FqNXdNM0ZMN3VvRnZvRCtDcmhtWWh6V0cyenJkcUE1VW9BZVFRSnBvVlFBOEVKY3NnTDBYU3hRWFpNN25LUTBNRm5EMDRLbW96VnFORVlnVURDWmxOVk8rNFh2Z01TdzVzc3F5dXlyN2NjMVl2QUdLaElkTTVnNWdTK3ZrcitHQkNBRTYrM1g0YzI5VmhuNGdSWVcrR0FmRkpQNmVPNnVwR3IxWC9BRndLUmNTbjg2Zm1LTEQ4VFI5ZXNXWXZScGdvSHR3RFFKc1JLSmoxaWFkQUpyUkFpY0Fjbng1eHZZYTluakRuZEd4Z3lIUTg0TzNady8zRDIrSUdLQlcwTkg3aXkyQit1c0F0SmdnY1V4YUVHbHBpNkxRVUQ2SHZON2gycTZoSG91TXdmeTdtL1BCNTB3Y1U2YUVVaUxQbERqOEdGS0JGdGhES0x2TDVvQWo4Wkt3dDF6VHJlYjE0dFNSRGFBTnNIVXg2bEdiN21qdXNtaTRzYTc1eTRXR2FZbHgwUnVvdzNhVEMrQU9OMmJrWWVDOHY0NW9QelF0SDBHakVjWERIYS84QXdLUk8wMHA3RHppUThOMTBmeGl3T1llaEdpY1A3Y0lPbUhOb1JPYUFlemxEWlVMSjBJQ2hsY0ZEQkdRK1ZkbHg0YjFrdjd6OVJsMTJ0cVVIeTM1ayt4dzFhd05PbUxuZ09Iem9kVlZDQzJBOTh1S3N0M3J5Z0o4V0t0TUVUV00wT2lHeWVBMTZCNmQ0Uzd4VXRaNURLdjQ1clp6TGFXUVBMaDhyNVIwN0tRcFdFZHlpWFplOVMxZ1FjSEUxVHQ0aVltUDJ3WE9LZVNTcllGREc3dXZaMFFiTm9ZbFl4NkV6QkViK3MwdjgvRzNSNEQ0d1k2eTk5K09iamhJZXJuakFTQkdnRkF5NzZlYy83QnhmNDhEL0FDVCtzdFI2RUQ1cVAzRHNLSkk2NFVKWEVaVEJzSURvM1BnZUprR3ZqOGtYNHVBc05aYjZVNXlEV0xwS1ZRNHBjQXA3M2RmTktoSTZOL0o4ZEJ1TnE0QzVLUm85WjhRTEVtS3VTQ09uT0thVUlwY1ZxMExuSEUveCtOcUZOVlZYMThrTVY4TnVwN0I5RWI3VU9HNGNNQ3YwK0QwWTdnRW9LcHZOTWpFbWVoSW1GckhhMGxWS2s2MTViT2xteFh1SENRTVY0VEM3MW1yVUlrMTFROURlRkwvcWRzL29iOFlldVNRSmRNYXNrT053aHlJYm8yK0VSelRjMUpzbzZPRnRNL1dEek9sdTc0eHlDNzNuTU5LUUMwcnhNRzA1VlQwKzhENWdWZVp6ZXo0RnhhazdhaEp1ajlRUGVmL0VBREFSQUFJQ0FRTUNCUU1EQXdVQUFBQUFBQUVDQUJFREVpRXhCRUVRRXlKUmNUSmhvWUdSNFNPeDBSUWdRbExCLzlvQUNBRUNBUUUvQVBFWWM3SDdRNGg1ZWdSTUxJcEhhTXlvYW1FaklTSTZVc2Z2T1lMNEVUR0Y1bWdkekQ1WUhFY1k0ck1oOXhNT1FPSzdqbWJHQlJNbjFHSXVzMUd4b2k4WEZPTXFUVVYxUEN5aEtFQVdJTis4VXduMEdPdXJjUkdaT0lydGZxakcrODJFeEtPWUlZeHFYdjRLYU14WkE0M0lGeGNZVGNHWnRtTXhnbHdGZ1J4ZHhrSXMzdEZiRlhNS24zZ1V3S0VGQXdDaE16bFFRdk1IVVprQlZUUW1KMlpDVEttV3dJQlVWYmliQ2F4Q1k5d1Rmd3hPVU14WkZjVnFCUHRPcFdtdVkyS09HSGFaOG1Rbm5hWVdBYW00TXlZbVJwL3FGQ251Wmo2d3Jlc1E5YXRiQXh1c2Roc0kvVVpXTm1IcGM3Z3NGMm1OZEtFR0x2RndQbU5EdEU2QTk0L1NqRXBZVkxvVFZBWWFJaFNwWGdCT25MbnVKMVNpZ1lLcUVJd293WTF1b2JWS0c4UVdkNFVDaUxnelB3cGg2Zktnc2lwNVo3bVl1cHdhTkJQYUlFRmdqY0dFb0lNcUt1MVR6bjdSeTdyVFN1M2d4b1ZCdEMrMEpnV0FWTUMzL3dBTCtaMVdOM1VSTUt3S29IRUNnWE1weDNzWmh5MC9xTzNlWWtSTjFYbjNNWTZiSkYvSmo1V3lNYTIvbnRHQlhmZVkzbzFISURlbitKcUZSU0N2WVE1SzJNOHkxcWF1OHU1UU94bmttZ0lNSUpubGdMRFhoaEM5N014RlNLaktjZVFyRk84ODNwTVlyVmZ3STdveDlJMis4eGRNcHhNQ2FtSnJYaS9rek8zb3FnUHRDUU5vU0d4ZW1MN1FXZG9WYlRxRXhLUmRDUGpZeGNWQ09wUnZBR291VmU4T2RLcFkyUy9IQ0xUbTRNNjRqWm1icUJrSU5WVTgyS0VZYzk2aWRPSEpBM2hTNWdWZCtUT3FwSy90M2dVTnZjclJ1RE9kNEN5azFCa1FyRVdySkJpQmZhQUUzVTZuRVVHKzB1eC9zSGdnMU5VYkpTVUR6N1FJNzd5c0k3M0x4KzBYVDJNRHNxNmUwZDZuVEFsRHpPc1E2YnFJaUJMTUFYc0l5RU1BSXVQK3BSRndkT2pPVzQzUDVpNDhXSmJQNWo1TlZtWTNMblNkdjEybVlMa1RrWDcvQUdFcTVmaUlJb29SUlpMdEhZdDRpTEw4MXFVUkZDclcvd0M4YkdjcUVDdjEvdk1icXRxWjVtTld0UlBOSm5Ub1dKTlhEblhFNUFHOHo1RG1jM0UxS2Foc09EVnh5eFhjajRFNmpBWDlZK1Q5dnREOTVvUHpOTGUwQW1QR1dQRzBvNnFqbnRLSmhIZ0I0ZE0rS3ZUeng4OS94TjJIOFJ5NkN5MHpZZGU0NThFVXR2TUdJVlZSV1N5QUwrLzh6T2d4UHFBQkIvRXhuek42NG5wSnFVS29WQ2FXbytQSGtPL3VML2lIcHpVOGhoM3ZqOC80aTRNYU52dnYrNG15NHlLdmF2MGlHclBpWlFnTVVSTWo0c3Ryc1lqQjAxQWJmZU9vQnU3bVhKZGdRNnJKblQ1VitudjIvd0RZV2RCc1lRMlE3bUpneUkzMm1UQW1SWm94NE84Umt5Q2hETHNHb3QxUjhCVVkya0hIZ0FBSXpSVFVKRXVPRUluU3NOSkZYODhUT1Q1ZHo2VitaalhuZU1BR05URTRiSFJvZjNtT2daaXlBTnBsQW1aaWI0aTZsNDdTL1RNYWFHTncrQWpmVExnbGdpQUVFd3BSaTR5VEZ4N1FZeFJtSmY2aEZUcVZBeGNSdHp0QnJ4bTVlKzB3RXEyd201YXJsRmVJcGF0b3lzRFpuQ21ETlFwaFBNUTl4NFVQZVVaUnFHRHdXeDhTaEFsTE5odEE1WlowL0pNNnFqaW1KQ1FhZ3hBY21MaUFGd0FYeE1RTndnR0lMSDJqR2wzZ3lvUHRDNEc1M2hURys1WDl0b2VtVGtHb3dkT0d1RE43d1pSMzJqMGQ3aWVGa1RXWUhNQkppSlV2UUtITTg5bHNIOFFPRkJBaVpkNk1zTzEzRkFab3RocVVSbGNFaG1BK1A4bUlpcDkvbmVNdGlFSXZNT1pCeEc2a2djenpYeWJLTGk5TG1mNmpVSFM0azUzTTh0UGFQakhhSDNnSThWOEVPVmt1YXlJRURxYjVpb0c5SmhSbE84VUU3VEVvaWtvYkhNdG1iZU5rYkY2YnVaT3BKSE1EdSt5aTR2UzVtM1kxRTZUQmo1MytZS1VVQkFHYllSY0pubG9JMVRKakpOaVZMOFJMQ3JVVkMwWEd4SUpoVFE4UkE1L2I4UWRPZ0VZQUdoRUFKTnc0L1NTb2hWTE9vd2VRdkMzODd3WjFaYXFJZFk1aTR4VTBnUzVaSWpNSlphQlBlWmNJSDA5bzJNam1hSUZNQ1JtRmJ6RmtDZ3hjNkJycWFpNzNNSzJTWU9JVnZJWXBHSVdabDZobTJFQ01SWmhSUCswRmdiRzRtVU5GekhUUE4yM1lURzZ1dHpzYWpRTVJCbFljd1pWYU11b0dISGR3WUx1RERkaUlvY1daU294MHpIZ0RpeE1XQlFPWW9xQ0Q2Mm1kbFhIVVFBV3hoWXNkNGVJUlppekU5N0htQVl5L3FFeEVBSFNJK1poekF3YXg0SUpRTUNnRFY0TG9STE1aMjdiVC84UUFLQkVBQWdJQ0FnRURCQU1CQVFBQUFBQUFBQUVDRVFNaEVqRVFFeUpCQkRKUllTQkNjU09CLzlvQUNBRURBUUUvQUU2UmFPU09jRWhTZkxrT2ZOa1lPU0p4Y0VRZXlDOFdrU2xaR1g0SXFmNUk4LzhBVGlwWDhNbkRoZndqb3N4TzBOMHJJemxMNUh6Rkg5aTh2emprb3FpVVl5MHh3aWxvaG90azVQb1JFaWhlR3JST0hGNlRZNVgyWWI0a3FVSFp5aFdpRTdWRlMvQW1peS9FRmIyTEZCdTJ0a29wUzE0eCtMSmRpalFreUg4SnhVa1RnNHE2cEdCKzBrbEtMUmloQ3JKcDFhSVRVa0xFN0hndjdXTDZaL0xGZ1M3WkREQ0tJNWNhMDJOMi9IcXFDc2YxWDRJNW5OMHl0aVhpS29UVC9oa2pIOE0rbmJWcEd4Y283UnlaR20yUG9qS3g1SUx0aXlRYjBMSVN3NUxiSHk3VDdFcE1jSk4weFlrdXlLakY2RUttSlcyelZDaUpGK01qcit4Z21vdGp5UHRGeWZaYk1YT3JhSndianJzbEtUMDJSVjZJUVVGdlpGcVE0a0UydGxGU1Ryc1VHMW9VSFlrcUVxTGFkb1dSYlBVYU9Uc1hpZkpERlVvV05hUFN6U2QxUkNNbDl6SlpYelRST0tVdXpFdmRaRzNzaW5HZHZ4cENhdHBrcWZ5UW1sZER5VzlFR3BJaU5XUEd4WXB0MnlNSzh6cFM2RmlsTXg0dUNlemgraTVSSlpIL0FJS1prYlBwOTJYeFZVTGZZalRSd2xZM2ZUSFoxMllaVyt4THhyK0RkS3lNTGRseGpyNUl1YktuK1I4cTJWRnUva2pBeXRXZlROVzBTbEp1a2U3NVpHZHhiWno5bmRIcXRSb3ZKTFNJUTRrb2NWZlpoazRTK1N4SVNFUHJ3OWwxN1lrWXFJdWloK0V1Q2JiRzdka0pjSFpLRGZ1UW9UY2ZjeFkwWkpVcUZqYzQyekZEZ3REcHF5Tk9MMktrMllzcSsxaS9RcFJvMVJaT2FTRTZWa0kwdkNZaCtNa1pwN0k2SXFMdWtZc25CVXhkTWNrakpPMlZKTHN4UG5IaSt5ZFFWSmliU1pleGQyUXlUZ2lPVk94WlVQSkpvVGJuM1Evd0lzV3hGZUhGU2hzbEhpMm1SYjZNZU5KV3lOVlJsZzZzU2krMEp4eHJSTEpCeElaSEF1V1VjWlJFS05QWSs5RkZNZ3FrS3I4eFd2Q1RLSU9TTXkzMllVbk1XNWY0VGswUmJjZGs0OFphMnlRNE9yRVlrcUtpL3dEMFYyVGx5aUpVaGVJOWlWbEhUUGdVclRISkpEbXhUZGsyK0ZtRDcreUgyN0Z4a3FFblJrcHgyVXFJdTdYaUR0VVIyejBVN3BuQ1NOK0xRcXNqNGFvWmRXamxiOGVueFprMHFNQ3FaT1NYWjZqZlE1M2FMMFRyaUpqb1J4azBMOUVaeldreFpwZktzaTR5dlZIcGFIaVpEMnFtUHdqaWppaXFISk5DWExiRmppMWFPTjdZNGEwS0xqR2g2anM3VzJKeHEwckpOdGlGYjZJNHBNaGdzV09NRmJaNjJLSFNzZjFHUjlhUFVsVldZNXRPbUx3dk5iR29KaWdtaStBMjFzVFRXaVRSS1IyVWtuUkdLbVJ3b1VZd1h1WTgrS1BTc2VmTExvNzIyV2prV3lKaW5TOHJ5azNLeHo0anlLblFwY29VT2JpdER5U1l2Q2x1bVJiUCtyVHRucHUrenI0SEovd1M4Y2pIa2U3K1NNazBKNkxPVCtDRVg4RW9jaDRadEZLRUJ0OUNGcUpUbDBReEpiWnlTMGlNNWZncFBzZVBpT0d6aCtocHhZaU5OSEJIcG85TnBDZENuUjZ0SHFWVEpTY1hTRTVTamJKWlhGMFNtL1A5VEZGdGpmOEFWRVlxS0Y0ZXVpY2VPeTVLT2lWM3NoQk1VWEh3MktSZDY4VkticEVZcDM4bi85az0iIGlkPSJlIiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIvPjwvZGVmcz48L3N2Zz4=", + "description": "Displays rotational speed data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'rotationalSpeed', label: 'Rotational speed', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'RPM', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'rotationalSpeed', 'RPM', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Rotational speed\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 4000 - 2000;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 4000) {\\n\\tvalue = 4000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"RPM\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":500,\"color\":\"#2B54CE\"},{\"from\":500,\"to\":1500,\"color\":\"#3B911C\"},{\"from\":1500,\"to\":3000,\"color\":\"#F89E0D\"},{\"from\":3000,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 4000 - 2000;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 4000) {\\n\\tvalue = 4000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"RPM\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0,0,0,0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"RPM\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:cm90YXRpb25hbF9zcGVlZF9jaGFydF9jYXJkX2JhY2tncm91bmQucG5n:cm90YXRpb25hbF9zcGVlZF9jaGFydF9jYXJkX2JhY2tncm91bmQucG5n;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Rotational speed\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"360\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"margin\":\"0px\"}" + }, + "tags": [ + "angular speed", + "spin rate", + "revolutions", + "rotational frequency", + "spin motion" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/rotational_speed_gauge.json b/application/src/main/data/json/system/widget_types/rotational_speed_gauge.json new file mode 100644 index 0000000000..2f6c82ab65 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/rotational_speed_gauge.json @@ -0,0 +1,31 @@ +{ + "fqn": "rotational_speed_gauge", + "name": "Rotational speed gauge", + "deprecated": false, + "image": "tb-image:cm90YXRpb25hbF9zcGVlZF9nYXVnZS5zdmc=:cm90YXRpb25hbF9zcGVlZF9nYXVnZS5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48ZyBjbGlwLXBhdGg9InVybCgjYSkiPjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSIjZmZmIiByeD0iNCIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik0yOC41IDIxLjJjLTQuNCAwLTggMS44LTggNCAwIDEuOCAyLjQgMy4zIDUuNiAzLjh2Mi41bDMuMi0zLjEtMy4yLTMuMnYyLjFjLTIuNS0uNC00LTEuNS00LTIuMSAwLS45IDIuNS0yLjQgNi40LTIuNCA0IDAgNi4zIDEuNSA2LjMgMi40IDAgLjYtMS4xIDEuNS0zLjEgMnYxLjZjMi44LS42IDQuNy0yIDQuNy0zLjYgMC0yLjItMy41LTQtNy45LTRaTTQ1LjIgMjAuOGgzLjFjLjcgMCAxLjMgMCAxLjguMy40LjIuOC41IDEgLjkuMy40LjUuOS41IDEuNCAwIC41LS4xLjktLjMgMS4ybC0uNy45LTEgLjUtLjUuMmgtMi45VjI1aDIuMmMuMyAwIC42IDAgLjktLjJsLjUtLjVjLjItLjIuMi0uNS4yLS44IDAtLjMgMC0uNi0uMi0uOCAwLS4yLS4zLS40LS41LS41YTIgMiAwIDAgMC0xLS4yaC0xLjZ2Ny43aC0xLjV2LTlabTUgOC45LTItNGgxLjZsMi4xIDRoLTEuNlptMi43LTMuM3YtLjFjMC0uNSAwLTEgLjItMS4zLjEtLjQuMy0uOC42LTEuMS4yLS4zLjUtLjUgMS0uNy4zLS4yLjctLjMgMS4yLS4zczEgLjEgMS4zLjNsMSAuN2MuMy4zLjUuNy42IDFsLjIgMS40di4xYzAgLjUgMCAxLS4yIDEuNC0uMS40LS4zLjctLjYgMWEyLjggMi44IDAgMCAxLTIuMyAxYy0uNCAwLS45IDAtMS4zLS4ybC0xLS44LS41LTFjLS4yLS40LS4yLS45LS4yLTEuNFptMS40LS4xdi4xbC4xLjljMCAuMy4yLjUuMy43bC41LjUuNy4xYTEuNCAxLjQgMCAwIDAgMS4zLS42bC4yLS43LjEtLjl2LS45bC0uNC0uN0ExLjQgMS40IDAgMCAwIDU2IDI0Yy0uMiAwLS41IDAtLjcuMmwtLjUuNS0uMy43di44Wm05LjItMy4ydjFoLTMuN3YtMWgzLjdabS0yLjctMS43aDEuNXY2LjlsLjMuMi40LjFhMi4zIDIuMyAwIDAgMCAuNSAwdjFhMy44IDMuOCAwIDAgMS0xIC4yYy0uMyAwLS42IDAtLjgtLjItLjMgMC0uNS0uMy0uNi0uNi0uMi0uMi0uMy0uNi0uMy0xdi02LjZabTcuOCA3di0zLjJjMC0uMiAwLS40LS4yLS42IDAtLjItLjItLjMtLjQtLjRsLS42LS4yYy0uMiAwLS41IDAtLjYuMmExIDEgMCAwIDAtLjQuM2wtLjIuNWgtMS41bC4yLS44Yy4yLS4yLjMtLjUuNi0uNmwuOC0uNSAxLjItLjJjLjUgMCAxIC4xIDEuMy4zbDEgLjcuMiAxLjN2M2wuMS44LjIuNmgtMS41bC0uMi0uNXYtLjdabS4yLTIuN3YuOWgtMWEzIDMgMCAwIDAtLjggMGwtLjUuM2ExIDEgMCAwIDAtLjQuOHYuNWwuNC4zLjYuMWExLjYgMS42IDAgMCAwIDEuNC0uNmwuMi0uNS40LjZjMCAuMiAwIC40LS4yLjZsLS41LjVhMi4zIDIuMyAwIDAgMS0xLjYuNmMtLjQgMC0uOCAwLTEuMS0uMmEyIDIgMCAwIDEtLjgtLjhjLS4yLS4zLS4zLS42LS4zLTEgMC0uMyAwLS42LjItLjkuMS0uMy4zLS41LjYtLjcuMi0uMi42LS4zIDEtLjRsMS4yLS4xaDEuMlptNi0yLjZ2MWgtMy43di0xaDMuN1ptLTIuNi0xLjdoMS40VjI4bC4xLjQuMy4yLjQuMWEyLjMgMi4zIDAgMCAwIC41IDB2MWEzLjggMy44IDAgMCAxLTEgLjJjLS4zIDAtLjYgMC0uOS0uMi0uMyAwLS41LS4zLS42LS42LS4yLS4yLS4yLS42LS4yLTF2LTYuNlptNS41IDEuN3Y2LjZoLTEuNFYyM2gxLjRabS0xLjUtMS44YzAtLjIgMC0uNC4yLS41LjEtLjIuMy0uMy42LS4zcy41LjEuNi4zYy4yLjEuMy4zLjMuNWwtLjMuNi0uNi4yYy0uMyAwLS41IDAtLjYtLjJhLjcuNyAwIDAgMS0uMi0uNlptMyA1LjF2LS4xYzAtLjUuMS0xIC4zLTEuMy4xLS40LjMtLjguNi0xLjFhMy4yIDMuMiAwIDAgMSAyLjMtMWMuNCAwIC45LjEgMS4yLjMuNC4yLjguNCAxIC43LjMuMy41LjcuNiAxIC4yLjUuMiAxIC4yIDEuNHYuMWMwIC41IDAgMS0uMiAxLjQtLjEuNC0uMy43LS42IDFhMi44IDIuOCAwIDAgMS0yLjIgMWMtLjUgMC0xIDAtMS4zLS4ybC0xLS44Yy0uMy0uMy0uNS0uNi0uNi0xLS4yLS40LS4yLS45LS4yLTEuNFptMS42LS4xdjFsLjMuNy41LjUuOC4xYTEuNCAxLjQgMCAwIDAgMS4yLS42bC4zLS43di0xLjhsLS4zLS43YTEuNCAxLjQgMCAwIDAtMS4yLS43Yy0uMyAwLS42IDAtLjguMmwtLjUuNS0uMy43di44Wm03LjUtMS44djUuMkg4N1YyM2gxLjR2MS40Wm0tLjIgMS42aC0uNWMwLS40IDAtLjkuMi0xLjNsLjUtMWEyLjQgMi40IDAgMCAxIDItLjljLjMgMCAuNSAwIC44LjIuMyAwIC41LjIuNy40bC40LjguMiAxLjF2NC4zaC0xLjV2LTQuM2MwLS4zIDAtLjYtLjItLjhsLS40LS40aC0uNmExLjQgMS40IDAgMCAwLTEuMi41bC0uMy43LS4xLjdabTkuNyAyLjN2LTMuMmwtLjEtLjYtLjQtLjQtLjctLjJjLS4yIDAtLjQgMC0uNi4yYTEgMSAwIDAgMC0uNC4zbC0uMi41SDk0YzAtLjMgMC0uNS4yLS44IDAtLjIuMy0uNS41LS42LjMtLjIuNS0uNC45LS41bDEuMS0uMmMuNSAwIDEgLjEgMS4zLjNsMSAuN2MuMi40LjMuOC4zIDEuM1YyOWwuMi42SDk4bC0uMi0uNXYtLjdabS4yLTIuN3YuOWgtMWEzIDMgMCAwIDAtLjggMGwtLjUuM2ExIDEgMCAwIDAtLjQuOGwuMS41LjQuMy42LjFhMS42IDEuNiAwIDAgMCAxLjMtLjZsLjItLjUuNS42LS4zLjYtLjQuNWEyLjMgMi4zIDAgMCAxLTEuNi42Yy0uNSAwLS45IDAtMS4yLS4yYTIgMiAwIDAgMS0uOC0uOGMtLjItLjMtLjMtLjYtLjMtMSAwLS4zIDAtLjYuMi0uOWwuNi0uNyAxLS40IDEuMi0uMUg5OFptNC42LTUuNHY5LjRIMTAxdi05LjRoMS41Wm04LjkgNy42YzAtLjIgMC0uMy0uMi0uNGExIDEgMCAwIDAtLjQtLjNsLS45LS4zLTEtLjNhMyAzIDAgMCAxLS43LS40bC0uNS0uNS0uMi0uOC4yLS43LjUtLjcuOC0uNCAxLS4yYy42IDAgMS4xLjEgMS41LjNsMSAuOGMuMi4zLjMuNi4zIDFoLTEuNWMwLS4xIDAtLjMtLjItLjVhMSAxIDAgMCAwLS4zLS4zbC0uNy0uMi0uNi4xLS40LjRhLjguOCAwIDAgMCAwIC43bC4yLjIuNC4yLjcuMmMuNSAwIC45LjIgMS4zLjRsLjguNmMuMi4zLjMuNi4zIDFhMS43IDEuNyAwIDAgMS0uNyAxLjVsLS45LjQtMSAuMWMtLjcgMC0xLjItLjEtMS42LS4zbC0xLS44YTIgMiAwIDAgMS0uMy0xaDEuNGwuMy42LjUuMy42LjFoLjdsLjQtLjQuMi0uNFptNC40LTMuNnY4aC0xLjVWMjNoMS40djEuMlptNC4zIDJ2LjFjMCAuNSAwIDEtLjIgMS40IDAgLjQtLjMuNy0uNSAxLS4yLjQtLjUuNi0uOC44bC0xIC4yYy0uNSAwLS45IDAtMS4yLS4ybC0uNy0uN2MtLjMtLjMtLjQtLjYtLjYtMWwtLjItMS4ydi0uNWwuMi0xLjNjLjItLjQuMy0uOC42LTFhMi4yIDIuMiAwIDAgMSAxLjgtMWMuNCAwIC44LjEgMS4xLjMuMy4xLjYuNC44LjdsLjUgMSAuMiAxLjRabS0xLjUuMXYtMWwtLjMtLjZhMS4zIDEuMyAwIDAgMC0xLjItLjdjLS4zIDAtLjUgMC0uNy4yLS4yIDAtLjMuMi0uNC4zbC0uMy42LS4yLjZWMjdjMCAuMy4xLjYuMy44IDAgLjMuMy41LjUuNi4yLjIuNS4yLjguMmwuNy0uMS41LS41LjItLjcuMS0uOVptNS45IDMuNGMtLjUgMC0xIDAtMS40LS4yYTMgMyAwIDAgMS0xLjYtMS43bC0uMi0xLjN2LS4yYzAtLjYgMC0xIC4yLTEuNWwuNi0xYy4zLS4zLjYtLjYgMS0uN2EzIDMgMCAwIDEgMS4yLS4zYy41IDAgLjkuMSAxLjIuMy40LjEuNy40IDEgLjZsLjQgMWMuMi41LjIuOS4yIDEuNHYuNmgtNS4xdi0xaDMuN3YtLjJsLS4yLS43LS40LS42LS44LS4yYy0uMyAwLS41IDAtLjcuMi0uMiAwLS4zLjItLjQuNC0uMi4yLS4zLjQtLjMuN2wtLjEgMXYxbC40LjcuNi40LjcuMWEyIDIgMCAwIDAgMS43LS44bC44LjhhMi44IDIuOCAwIDAgMS0xLjQgMWwtMS4xLjJabTYuOSAwYy0uNSAwLTEgMC0xLjMtLjJhMyAzIDAgMCAxLTEuNy0xLjdsLS4yLTEuM3YtLjJjMC0uNiAwLTEgLjItMS41bC43LTFjLjItLjMuNS0uNiAxLS43YTMgMyAwIDAgMSAxLjEtLjNjLjUgMCAuOS4xIDEuMy4zbC44LjZjLjMuMy40LjcuNiAxbC4xIDEuNHYuNmgtNXYtMWgzLjZ2LS4yYzAtLjIgMC0uNS0uMi0uNyAwLS4yLS4yLS40LS40LS42bC0uOC0uMmMtLjIgMC0uNSAwLS43LjJsLS40LjRjLS4yLjItLjMuNC0uMy43bC0uMSAxdi4ybC4xLjhjMCAuMy4yLjUuNC43bC41LjQuOC4xYTIgMiAwIDAgMCAxLjYtLjhsLjguOGEyLjggMi44IDAgMCAxLTEuNCAxbC0xLjEuMlptOC0xLjV2LThoMS41djkuNGgtMS4zbC0uMi0xLjRabS00LjMtMS45YzAtLjYgMC0xIC4yLTEuNWwuNS0xYTIuMyAyLjMgMCAwIDEgMi0xYy4zIDAgLjcuMSAxIC4zLjMuMS41LjMuNy42LjMuMy40LjcuNiAxbC4yIDEuNHYuNGwtLjIgMS4zYy0uMi40LS4zLjctLjYgMWEyLjIgMi4yIDAgMCAxLTEuOCAxIDIuMyAyLjMgMCAwIDEtMS45LTFsLS41LTEuMS0uMi0xLjRabTEuNSAwdi45bC4zLjcuNS40Yy4yLjIuNC4yLjcuMi4zIDAgLjYgMCAuOC0uMmwuNS0uNi4zLS44di0xLjJsLS4yLS42LS4zLS42LS40LS4zLS43LS4yYy0uMyAwLS41IDAtLjcuMmwtLjUuNS0uMi43LS4xLjhaIi8+PGcgY2xpcC1wYXRoPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMjYiIGhlaWdodD0iMTMiIHg9Ijg3IiB5PSIxMzAiIGZpbGw9IiNGM0YzRjMiIHJ4PSIzLjEiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii41IiBkPSJNOTQuNiAxMzguNnYuOWgtNC44di0uN2wyLjgtNC41aDFsLTEgMS42LTEuNyAyLjdoMy43Wm0tLjgtNC4zdjYuN2gtMS4xdi02LjdoMVptNi4xIDIuOHYxbC0uMSAxLjQtLjUgMS0uNy40LS45LjItLjctLjFhMS44IDEuOCAwIDAgMS0xLS45bC0uNC0uOHYtMy41bC41LTEgLjctLjQgMS0uMi43LjFhMS43IDEuNyAwIDAgMSAxIC45Yy4yLjIuMi41LjMuOGwuMSAxWm0tMSAxLjJWMTM3bC0uMS0uNy0uMi0uNWExIDEgMCAwIDAtLjItLjRsLS4zLS4yYTEgMSAwIDAgMC0uNCAwIDEgMSAwIDAgMC0uNCAwbC0uNC40LS4yLjZ2M2wuMS41LjIuMy4zLjIuNC4xaC41bC4zLS40LjItLjYuMS0uOVptNi42LTEuMnYxYzAgLjYgMCAxLS4yIDEuNCAwIC40LS4yLjctLjQgMWwtLjcuNC0xIC4yLS43LS4xYTEuOCAxLjggMCAwIDEtMS0uOWMtLjItLjItLjMtLjUtLjMtLjhsLS4xLTEuMVYxMzdsLjEtMS4zLjUtMSAuNy0uNC45LS4yLjcuMWExLjcgMS43IDAgMCAxIDEgLjlsLjQuOHYxWm0tMS4xIDEuMlYxMzYuMmwtLjItLjVhMSAxIDAgMCAwLS4yLS40bC0uMy0uMmExIDEgMCAwIDAtLjQgMCAxIDEgMCAwIDAtLjUgMGwtLjMuNC0uMi42LS4xLjh2Mi4ybC4yLjUuMi4zLjMuMi40LjFoLjRsLjQtLjQuMi0uNnYtLjlabTYuNi0xLjJ2MWwtLjEgMS40LS41IDEtLjcuNC0uOS4yLS43LS4xYTEuOCAxLjggMCAwIDEtMS0uOSAzIDMgMCAwIDEtLjQtLjh2LTMuNWwuNS0xIC43LS40IDEtLjIuNy4xYTEuNyAxLjcgMCAwIDEgMSAuOWMuMi4yLjMuNS4zLjhsLjEgMVptLTEgMS4yVjEzN2wtLjEtLjctLjItLjVhMSAxIDAgMCAwLS4yLS40bC0uMy0uMmExIDEgMCAwIDAtLjQgMCAxIDEgMCAwIDAtLjQgMGwtLjQuNC0uMi42djNsLjEuNS4zLjNjMCAuMS4xLjIuMy4ybC4zLjFoLjVsLjMtLjQuMy0uNnYtLjlaIi8+PC9nPjxwYXRoIGZpbGw9IiMzMDVBRDciIGQ9Ik0xMzguOSAxMzQuOWE1NSA1NSAwIDEgMC03Ny44IDBsNi42LTYuNmE0NS42IDQ1LjYgMCAxIDEgNjQuNiAwbDYuNiA2LjZaIi8+PHBhdGggZmlsbD0iIzNGQTcxQSIgZD0iTTEzOC45IDEzNC45YTU1IDU1IDAgMSAwLTkyLTI0LjZsOS0yLjRhNDUuNiA0NS42IDAgMSAxIDc2LjQgMjAuNGw2LjYgNi42WiIvPjxwYXRoIGZpbGw9IiNGRkE2MDAiIGQ9Ik0xMzguOSAxMzQuOUE1NSA1NSAwIDAgMCA2MSA1N2w2LjYgNi42YTQ1LjYgNDUuNiAwIDAgMSA2NC42IDY0LjZsNi42IDYuNloiLz48cGF0aCBmaWxsPSIjRDgxODM4IiBkPSJNMTM4LjkgMTM0LjlhNTUgNTUgMCAwIDAgMC03Ny44bC02LjYgNi42YTQ1LjYgNDUuNiAwIDAgMSAwIDY0LjZsNi42IDYuNloiLz48cGF0aCBmaWxsPSIjNjM2MzYzIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Im0xMzguOCAxMzQtLjQuNC02LjYtNi42LjMtLjMgMy4zIDMuMyAzLjMgMy4yWm0xNC0yNC43LS4zLjgtNC40LTEuMi00LjYtMS4zLjItLjcgNC42IDEuMiA0LjQgMS4yWm0tLjMtMjguNC4yLjgtNC40IDEuMi00LjYgMS4ydi0uNGwtLjEtLjJ2LS4xTDE0OCA4Mmw0LjQtMS4yWm0tMTQuNC0yNC41LjcuNi0zLjMgMy4yLTMuNCAzLjMtLjUtLjUgMy4zLTMuMyAzLjItMy4zWm0tMjQuNy0xNC4xLjkuMkwxMTMgNDdsLTEuMyA0LjYtLjctLjIgMS4yLTQuNiAxLjItNC41Wk02NyAxMjcuNWwuMi4zLTYuNSA2LjUtLjMtLjIgMy40LTMuMyAzLjItMy4zWk01NS40IDEwN2wuMS43LTQuNCAxLjItNC41IDEuM2E0OC40IDQ4LjQgMCAwIDEtLjItLjlsNC41LTEuMiA0LjUtMS4xWm0uMS0yMy43LS4xLjctNC41LTEuMS00LjUtMS4yLjItLjkgNC41IDEuMyA0LjQgMS4yWm0xMi0yMC40LS41LjUtMy4yLTMuMi0zLjQtMy4zLjYtLjYgMy4zIDMuNCAzLjMgMy4yWm0yMC42LTExLjYtLjcuMi0xLjItNC41LTEuMy00LjVhNTguNSA1OC41IDAgMCAxIC45LS4ybDEuMiA0LjUgMS4xIDQuNVoiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjxwYXRoIGZpbGw9IiM2MzYzNjMiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTE1My40IDEwNi4yYTU1IDU1IDAgMCAxLTE1IDI4LjJsLS41LS41YTU0LjIgNTQuMiAwIDEgMC03Ni43LS4xbC0uNS41YTU1IDU1IDAgMSAxIDkyLjgtMjguMVoiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjxwYXRoIGZpbGw9IiM2MzYzNjMiIGQ9Ik01OSA4MS4zVjg3aC0xdi00LjZsLTEuMy41VjgybDIuMi0uOGguMVptNS43IDIuNHYxbC0uMSAxLS40LjhjLS4yLjItLjQuNC0uNi40YTIuMiAyLjIgMCAwIDEtMS40IDAgMS41IDEuNSAwIDAgMS0xLS42bC0uMi0uN3YtM2wuNS0uOGMuMS0uMi4zLS4zLjYtLjRsLjctLjIuNi4xYTEuNSAxLjUgMCAwIDEgMSAuOGwuMi42djFabS0xIDFWODNsLS4xLS40LS4yLS4zLS4zLS4yYTEgMSAwIDAgMC0uMyAwaC0uNGwtLjMuMy0uMS41LS4xLjdWODUuM2wuMi41LjEuMy4zLjJoLjdsLjMtLjMuMi0uNXYtLjhabTUuNS0xdjFsLS4xIDFjMCAuNC0uMi42LS40LjgtLjEuMi0uMy40LS42LjRhMi4yIDIuMiAwIDAgMS0xLjQgMCAxLjUgMS41IDAgMCAxLS45LS42bC0uMi0uNy0uMS0xdi0xbC4xLTEgLjQtLjguNi0uNC44LS4yLjYuMWExLjUgMS41IDAgMCAxIC45LjhsLjIuNi4xIDFabS0xIDFWODIuNWwtLjMtLjMtLjItLjJhMSAxIDAgMCAwLS4zIDBINjdsLS4zLjMtLjIuNXYyLjVsLjEuNS4yLjMuMi4yaC44bC4zLS4zLjEtLjV2LS44Wm01LjYtMXYxYzAgLjQgMCAuNy0uMiAxIDAgLjQtLjIuNi0uMy44LS4yLjItLjQuNC0uNi40YTIuMiAyLjIgMCAwIDEtMS40IDAgMS41IDEuNSAwIDAgMS0xLS42bC0uMi0uN3YtM2wuNS0uOC41LS40LjgtLjIuNi4xYTEuNSAxLjUgMCAwIDEgMSAuOGwuMi42djFabS0xIDFWODNsLS4xLS40LS4yLS4zLS4zLS4yYTEgMSAwIDAgMC0uMyAwaC0uNGwtLjMuMy0uMS41LS4xLjdWODUuM2wuMS41LjIuMy4zLjJoLjdsLjMtLjMuMi0uNXYtLjhaTTcxLjEgMTIzLjd2MWwtLjEgMWMwIC40LS4yLjYtLjQuOC0uMS4yLS4zLjMtLjYuNGEyLjIgMi4yIDAgMCAxLTEuNCAwIDEuNSAxLjUgMCAwIDEtLjktLjZsLS4yLS43di0zbC40LS44LjYtLjQuOC0uMi42LjFhMS41IDEuNSAwIDAgMSAuOS44bC4zLjZ2MVptLS45IDFWMTIzbC0uMi0uNC0uMS0uMy0uMy0uMmExIDEgMCAwIDAtLjMgMGgtLjRsLS4zLjMtLjIuNXYyLjVsLjEuNS4yLjMuMy4yaC43bC4zLS4zLjEtLjUuMS0uOFpNODEuMiA2MS4zdi43aC0zLjh2LS42bDEuOS0yIC41LS43LjItLjRhMS4xIDEuMSAwIDAgMCAwLS45bC0uMy0uM2EuOS45IDAgMCAwLS41LS4xIDEgMSAwIDAgMC0uNS4xYy0uMS4xLS4zLjItLjMuNGwtLjEuNWgtMWwuMi0uOWMuMi0uMi40LS41LjctLjZhMiAyIDAgMCAxIDEtLjNsMSAuMmMuMy4yLjUuMy42LjYuMi4yLjIuNS4yLjh2LjVsLS4zLjYtLjQuNS0uNS41LTEuMiAxLjRoMi42Wm00LjUtMi42djFsLS4xIDEtLjQuOGMtLjIuMi0uNC40LS42LjRhMi4yIDIuMiAwIDAgMS0xLjQgMCAxLjUgMS41IDAgMCAxLTEtLjZsLS4yLS43di0zbC41LS44Yy4xLS4yLjMtLjMuNi0uNGwuNy0uMi42LjFhMS41IDEuNSAwIDAgMSAxIC44bC4yLjZ2MVptLTEgMVY1OGwtLjEtLjQtLjItLjMtLjMtLjJhMSAxIDAgMCAwLS4zIDBoLS40bC0uMy4zLS4xLjUtLjEuN1Y2MC4zbC4yLjUuMS4zLjMuMmguN2wuMy0uMy4yLS41di0uOFptNS41LTF2MWwtLjEgMWMwIC40LS4yLjYtLjQuOC0uMS4yLS4zLjQtLjYuNGEyLjIgMi4yIDAgMCAxLTEuNCAwIDEuNSAxLjUgMCAwIDEtLjktLjZsLS4yLS43LS4xLTF2LTFsLjEtMSAuNC0uOC42LS40LjgtLjIuNi4xYTEuNSAxLjUgMCAwIDEgLjkuOGwuMi42LjEgMVptLTEgMVY1Ny41bC0uMy0uMy0uMi0uMmExIDEgMCAwIDAtLjMgMEg4OGwtLjMuMy0uMi41djIuNWwuMS41LjIuMy4yLjJoLjhsLjMtLjMuMS0uNXYtLjhabTUuNi0xdjFjMCAuNCAwIC43LS4yIDEgMCAuNC0uMi42LS4zLjgtLjIuMi0uNC40LS42LjRhMi4yIDIuMiAwIDAgMS0xLjQgMCAxLjUgMS41IDAgMCAxLTEtLjZsLS4yLS43di0zbC41LS44LjUtLjQuOC0uMi42LjFhMS41IDEuNSAwIDAgMSAxIC44bC4yLjZ2MVptLTEgMVY1OGwtLjEtLjQtLjItLjMtLjMtLjJhMSAxIDAgMCAwLS4zIDBoLS40bC0uMy4zLS4xLjUtLjEuN1Y2MC4zbC4xLjUuMi4zLjMuMmguN2wuMy0uMy4yLS41di0uOFpNMTE3LjIgMTI1di43SDExM3YtLjVsMi40LTMuOWguN2wtLjggMS40LTEuNCAyLjNoMy4yWm0tLjgtMy43djUuN2gtLjl2LTUuN2gxWm0yLjQgMy0uOC0uMS4zLTIuOWgzLjF2LjhoLTIuM2wtLjEgMS40YTEuOCAxLjggMCAwIDEgLjktLjJsLjcuMWMuMiAwIC40LjIuNS40LjIuMS4zLjQuNC42YTIuNSAyLjUgMCAwIDEgMCAxLjVsLS40LjZjLS4xLjItLjMuMy0uNi40bC0uOC4yLS43LS4xLS42LS4zLS40LS41LS4xLS43aC45bC4xLjQuMy4zLjUuMWguNGwuMy0uMy4yLS40YTEuOCAxLjggMCAwIDAgMC0xIDEgMSAwIDAgMC0uMi0uM2wtLjMtLjJhMSAxIDAgMCAwLS41IDBoLS41bC0uMy4zWm03LjItLjZ2MmwtLjUuOGMtLjEuMi0uMy4zLS42LjRhMi4yIDIuMiAwIDAgMS0xLjQgMCAxLjUgMS41IDAgMCAxLS45LS42bC0uMi0uN3YtM2wuNC0uOC42LS40LjgtLjIuNi4xYTEuNSAxLjUgMCAwIDEgMSAuOGwuMi42djFabS0uOSAxVjEyM2wtLjItLjQtLjEtLjMtLjMtLjJhMSAxIDAgMCAwLS4zIDBoLS40bC0uMy4zLS4yLjV2Mi41bC4xLjUuMi4zLjMuMmguN2wuMy0uMy4xLS41LjEtLjhabTUuNS0xdjFsLS4xIDEtLjQuOC0uNi40YTIuMiAyLjIgMCAwIDEtMS40IDAgMS41IDEuNSAwIDAgMS0xLS42YzAtLjIgMC0uNS0uMi0uN3YtM2wuNS0uOGMuMS0uMi4zLS4zLjYtLjRsLjctLjIuNi4xYTEuNSAxLjUgMCAwIDEgMSAuOGwuMi42djFabS0xIDFWMTIzbC0uMS0uNC0uMi0uMy0uMy0uMmExIDEgMCAwIDAtLjMgMGgtLjRsLS4zLjMtLjEuNXYzbC4yLjMuMy4yaC43bC4zLS4zLjItLjV2LS44Wk0xMjUuMyA4My43aDEuMmwuMy0uNHYtLjlsLS4zLS4zYTEgMSAwIDAgMC0uNS0uMSAxIDEgMCAwIDAtLjQgMCAuOC44IDAgMCAwLS41LjhoLTFjMC0uMy4yLS42LjMtLjhsLjctLjZhMiAyIDAgMCAxIC45LS4ybDEgLjIuNi42Yy4yLjIuMi41LjIuOHYuNWwtLjQuNS0uNi4zLS43LjJoLS44di0uNlptMCAuOHYtLjZoLjhjLjMgMCAuNiAwIC44LjJsLjYuMy4zLjUuMS41LS4xLjctLjQuNS0uNi40YTIuNSAyLjUgMCAwIDEtMS41IDAgMiAyIDAgMCAxLS42LS4zbC0uNC0uNS0uMi0uN2gxdi40bC40LjMuNS4xaC41bC40LS40di0xYy0uMS0uMi0uMy0uMy0uNC0uM2wtLjYtLjFoLS42Wm00LjUtLjEtLjgtLjIuMy0yLjloMy4xdi44aC0yLjNsLS4xIDEuNGExLjggMS44IDAgMCAxIC45LS4ybC43LjFjLjIgMCAuNC4yLjUuNC4yLjEuMy4zLjQuNmEyLjUgMi41IDAgMCAxIDAgMS41bC0uNC42Yy0uMS4yLS4zLjMtLjYuNGwtLjguMi0uNy0uMS0uNi0uM2ExLjYgMS42IDAgMCAxLS41LTEuMmguOWwuMS40LjMuMy41LjFoLjRsLjMtLjMuMi0uNGExLjggMS44IDAgMCAwIDAtLjkgMSAxIDAgMCAwLS4yLS40bC0uMy0uMmExIDEgMCAwIDAtLjUgMGgtLjVsLS4zLjNabTcuMi0uN3YybC0uNS44Yy0uMS4yLS4zLjQtLjYuNGEyLjIgMi4yIDAgMCAxLTEuNCAwIDEuNSAxLjUgMCAwIDEtLjktLjZsLS4yLS43di0zbC40LS44LjYtLjQuOC0uMi42LjFhMS41IDEuNSAwIDAgMSAxIC44bC4yLjZ2MVptLS45IDFWODNsLS4yLS40LS4xLS4zLS4zLS4yYTEgMSAwIDAgMC0uMyAwaC0uNGwtLjMuMy0uMi41djIuNWwuMS41LjIuMy4zLjJoLjdsLjMtLjMuMS0uNS4xLS44Wm01LjUtMXYxbC0uMSAxLS40LjhjLS4yLjItLjQuNC0uNi40YTIuMiAyLjIgMCAwIDEtMS40IDAgMS41IDEuNSAwIDAgMS0xLS42YzAtLjIgMC0uNS0uMi0uN3YtM2wuNS0uOGMuMS0uMi4zLS4zLjYtLjRsLjctLjIuNi4xYTEuNSAxLjUgMCAwIDEgMSAuOGwuMi42djFabS0xIDFWODNsLS4xLS40LS4yLS4zLS4zLS4yYTEgMSAwIDAgMC0uMyAwaC0uNGwtLjMuMy0uMS41djNsLjIuMy4zLjJoLjdsLjMtLjMuMi0uNXYtLjhaTTEwOCA2MS4zdi43aC0zLjh2LS42bDEuOS0yIC41LS43LjItLjRhMS4xIDEuMSAwIDAgMCAwLS45bC0uMy0uM2EuOS45IDAgMCAwLS41LS4xIDEgMSAwIDAgMC0uNS4xbC0uMy40LS4xLjVoLTFsLjMtLjljLjEtLjIuMy0uNS42LS42YTIgMiAwIDAgMSAxLS4zbDEgLjJjLjMuMi41LjMuNi42LjIuMi4yLjUuMi44di41bC0uMy42LS40LjUtLjQuNS0xLjMgMS40aDIuN1ptMS44LTItLjgtLjEuMy0yLjloMy4xdi44aC0yLjNsLS4xIDEuNGExLjggMS44IDAgMCAxIC45LS4ybC43LjFjLjIgMCAuNC4yLjUuNC4yLjEuMy4zLjQuNmEyLjUgMi41IDAgMCAxIDAgMS41bC0uNC42Yy0uMS4yLS4zLjMtLjYuNGwtLjguMi0uNy0uMS0uNi0uM2ExLjYgMS42IDAgMCAxLS41LTEuMmguOWwuMS40LjMuMy41LjFoLjRsLjMtLjMuMi0uNGExLjggMS44IDAgMCAwIDAtLjkgMSAxIDAgMCAwLS4yLS40bC0uMy0uMmExIDEgMCAwIDAtLjUgMGgtLjVsLS4zLjNabTcuMi0uNnYybC0uNS44Yy0uMS4yLS4zLjQtLjYuNGEyLjIgMi4yIDAgMCAxLTEuNCAwIDEuNSAxLjUgMCAwIDEtLjktLjZsLS4yLS43di0zbC40LS44LjYtLjQuOC0uMi42LjFhMS41IDEuNSAwIDAgMSAxIC44bC4yLjZ2MVptLS45IDFWNThsLS4yLS40LS4xLS4zLS4zLS4yYTEgMSAwIDAgMC0uMyAwaC0uNGwtLjMuMy0uMi41djIuNWwuMS41LjIuMy4zLjJoLjdsLjMtLjMuMS0uNS4xLS44Wm01LjUtMXYxbC0uMSAxLS40LjhjLS4yLjItLjQuNC0uNi40YTIuMiAyLjIgMCAwIDEtMS40IDAgMS41IDEuNSAwIDAgMS0xLS42YzAtLjIgMC0uNS0uMi0uN3YtM2wuNS0uOGMuMS0uMi4zLS4zLjYtLjRsLjctLjIuNi4xYTEuNSAxLjUgMCAwIDEgMSAuOGwuMi42djFabS0xIDFWNThsLS4xLS40LS4yLS4zLS4zLS4yYTEgMSAwIDAgMC0uMyAwaC0uNGwtLjMuMy0uMS41djNsLjIuMy4zLjJoLjdsLjMtLjMuMi0uNXYtLjhaIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTg4LjkgMTEyLjhoMi40Yy41IDAgLjkgMCAxLjMuMmwuOC43Yy4yLjMuMy43LjMgMS4xIDAgLjMgMCAuNi0uMi45LS4xLjMtLjMuNS0uNS42bC0uOC40LS40LjJoLTIuMXYtLjloMS42Yy4yIDAgLjUgMCAuNy0uMmwuNC0uNC4xLS41LS4xLS42LS40LS40LS43LS4ySDkwdjUuOEg4OXYtNi43Wm0zLjggNi43LTEuNi0zaDEuMmwxLjYgM2gtMS4yWm00LjktMi41aC0xLjh2LTFoMi41bC40LS41LjEtLjZ2LS42YTEgMSAwIDAgMC0uNS0uNGwtLjctLjJoLTEuNHY1LjhIOTV2LTYuN2gyLjZjLjUgMCAxIDAgMS4zLjNsLjguN2MuMi4zLjMuNy4zIDEuMSAwIC41IDAgLjgtLjMgMS4xbC0uOC44LTEuMy4yWm00LjEtNC4yaDFsMiA1LjIgMi01LjJoMWwtMi42IDYuN2gtLjhsLTIuNi02LjdabS0uNSAwaDFsLjIgNC41djIuMmgtMS4ydi02LjdabTYgMGgxdjYuN0gxMDd2LTIuMmwuMS00LjVaIi8+PHBhdGggZmlsbD0iI0U2NTEwMCIgZD0ibTk3LjggOTkuNSA1MC4zIDguOEw5OS40IDk0IDg4IDkyLjVjLS4yIDAtLjQgMC0uNS4zbC0uNiAyLjRjMCAuMiAwIC40LjMuNWwxMC42IDMuOFoiLz48ZyBmaWx0ZXI9InVybCgjYykiPjxjaXJjbGUgY3g9Ijk5LjUiIGN5PSI5Ni41IiByPSI0LjUiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxyZWN0IHdpZHRoPSIxOTkuMiIgaGVpZ2h0PSIxNTkuMiIgeD0iLjQiIHk9Ii40IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgc3Ryb2tlLXdpZHRoPSIuOCIgcng9IjMuNiIvPjxkZWZzPjxjbGlwUGF0aCBpZD0iYSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9IiNmZmYiIHJ4PSI0Ii8+PC9jbGlwUGF0aD48Y2xpcFBhdGggaWQ9ImIiPjxyZWN0IHdpZHRoPSIyNiIgaGVpZ2h0PSIxMyIgeD0iODciIHk9IjEzMCIgZmlsbD0iI2ZmZiIgcng9IjMuMSIvPjwvY2xpcFBhdGg+PGZpbHRlciBpZD0iYyIgd2lkdGg9IjM3LjMiIGhlaWdodD0iMzcuMyIgeD0iODAuOSIgeT0iNzcuOSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlQ29sb3JNYXRyaXggaW49IlNvdXJjZUFscGhhIiByZXN1bHQ9ImhhcmRBbHBoYSIgdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAxMjcgMCIvPjxmZU1vcnBob2xvZ3kgaW49IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iZGlsYXRlIiByYWRpdXM9IjEuNiIgcmVzdWx0PSJlZmZlY3QxX2Ryb3BTaGFkb3dfMTUxOF82NDA0Ii8+PGZlT2Zmc2V0Lz48ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSI2LjMiLz48ZmVDb21wb3NpdGUgaW4yPSJoYXJkQWxwaGEiIG9wZXJhdG9yPSJvdXQiLz48ZmVDb2xvck1hdHJpeCB2YWx1ZXM9IjAgMCAwIDAgMSAwIDAgMCAwIDAuMjQxNjY3IDAgMCAwIDAgMC4yNDE2NjcgMCAwIDAgMC4xNSAwIi8+PGZlQmxlbmQgaW4yPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHJlc3VsdD0iZWZmZWN0MV9kcm9wU2hhZG93XzE1MThfNjQwNCIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfZHJvcFNoYWRvd18xNTE4XzY0MDQiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48L2RlZnM+PC9zdmc+", + "description": "Rotational speed gauge.", + "descriptor": { + "type": "latest", + "sizeX": 6, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'rotationalSpeed', label: 'Rotational speed', type: 'timeseries' }];\n }\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-analogue-radial-gauge-widget-settings", + "dataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-radial-gauge-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Rotational speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 4000 - 2000;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 4000) {\\n\\tvalue = 4000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"startAngle\":45,\"ticksAngle\":270,\"needleCircleSize\":8,\"defaultColor\":\"#e65100\",\"minValue\":0,\"maxValue\":4500,\"majorTicksCount\":9,\"colorMajorTicks\":\"#444\",\"minorTicks\":8,\"colorMinorTicks\":\"#666\",\"numbersFont\":{\"family\":\"Roboto\",\"size\":18,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"numbersColor\":\"#616161\",\"showUnitTitle\":false,\"unitTitle\":\"\",\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#888\"},\"titleColor\":\"#888\",\"unitsFont\":{\"size\":26,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"color\":\"#616161\"},\"unitsColor\":\"#616161\",\"valueBox\":true,\"valueInt\":3,\"valueFont\":{\"size\":27,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"color\":\"rgba(0, 0, 0, 0.54)\",\"shadowColor\":\"#FFFFFF01\"},\"valueColor\":\"rgba(0, 0, 0, 0.54)\",\"valueColorShadow\":\"#FFFFFF01\",\"colorValueBoxRect\":\"#88888800\",\"colorValueBoxRectEnd\":\"#66666600\",\"colorValueBoxBackground\":\"rgba(243, 243, 243, 0.54)\",\"colorValueBoxShadow\":\"rgba(0, 0, 0, 0)\",\"showBorder\":false,\"colorPlate\":\"#FFFFFF\",\"colorNeedle\":null,\"colorNeedleEnd\":null,\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"colorNeedleShadowDown\":\"rgba(188,143,143,0.45)\",\"highlightsWidth\":15,\"highlights\":[{\"from\":0,\"to\":500,\"color\":\"#305AD7\"},{\"from\":500,\"to\":1500,\"color\":\"#3FA71A\"},{\"from\":1500,\"to\":3000,\"color\":\"#FFA600\"},{\"from\":3000,\"to\":4500,\"color\":\"#F04022\"}],\"animation\":true,\"animationDuration\":500,\"animationRule\":\"cycle\"},\"title\":\"Rotational speed\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"decimals\":0,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\",\"units\":\"RPM\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":null,\"lineHeight\":\"24px\"},\"showTitleIcon\":true,\"titleTooltip\":\"\",\"titleIcon\":\"360\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"actions\":{},\"margin\":\"0px\",\"borderRadius\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "angular speed", + "spin rate", + "revolutions", + "rotational frequency", + "spin motion" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/rotational_speed_progress_bar.json b/application/src/main/data/json/system/widget_types/rotational_speed_progress_bar.json new file mode 100644 index 0000000000..236533c54f --- /dev/null +++ b/application/src/main/data/json/system/widget_types/rotational_speed_progress_bar.json @@ -0,0 +1,30 @@ +{ + "fqn": "rotational_speed_progress_bar", + "name": "Rotational speed progress bar", + "deprecated": false, + "image": "tb-image:cm90YXRpb25hbF9zcGVlZF9wcm9ncmVzc19iYXIuc3Zn:cm90YXRpb25hbF9zcGVlZF9wcm9ncmVzc19iYXIuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik0xNyAxNWgzLjVhNSA1IDAgMCAxIDIgLjRjLjUuMiAxIC42IDEuMiAxIC4zLjQuNCAxIC40IDEuNiAwIC41IDAgMS0uMyAxLjRsLS43IDEtMS4yLjUtLjUuM2gtMy4ydi0xLjRoMi40Yy40IDAgLjcgMCAxLS4ybC42LS42LjItLjljMC0uMyAwLS42LS4yLS45bC0uNi0uNi0xLS4yaC0xLjlWMjVIMTdWMTVabTUuNyAxMC0yLjMtNC41aDEuOGwyLjMgNC40di4xaC0xLjhabTIuOS0zLjZ2LS4yYzAtLjUgMC0xIC4yLTEuNWwuNy0xLjJhMyAzIDAgMCAxIDEtLjdsMS41LS4zYy42IDAgMSAwIDEuNS4zLjQuMS44LjQgMSAuNy40LjQuNi44LjcgMS4ybC4zIDEuNXYuMmwtLjMgMS41LS42IDEuMmEzLjEgMy4xIDAgMCAxLTIuNiAxYy0uNSAwLTEgMC0xLjQtLjJhMy41IDMuNSAwIDAgMS0xLjgtMmwtLjItMS41Wm0xLjYtLjJ2LjJjMCAuMyAwIC42LjIgMSAwIC4yLjEuNS4zLjdsLjUuNWMuMy4yLjUuMi44LjJhMS42IDEuNiAwIDAgMCAxLjQtLjdjLjEtLjIuMy0uNS4zLS44bC4xLTF2LTFsLS40LS44YTEuNiAxLjYgMCAwIDAtMS40LS43Yy0uMyAwLS41IDAtLjguMmwtLjUuNWMtLjIuMi0uMy41LS4zLjhsLS4yIDFabTEwLjMtMy42djEuMmgtNC4ydi0xLjJoNC4yWm0tMy0xLjhoMS43djcuN2wuNC4yaC40YTIuNSAyLjUgMCAwIDAgLjYgMFYyNWE0LjIgNC4yIDAgMCAxLTEuMS4xYy0uNCAwLS43IDAtMS0uMi0uMy0uMS0uNi0uMy0uNy0uNi0uMi0uMy0uMy0uNy0uMy0xLjJ2LTcuM1ptOC43IDcuN1YyMGMwLS4zIDAtLjUtLjItLjdhMSAxIDAgMCAwLS40LS40bC0uNy0uMi0uNy4xLS41LjQtLjEuNWgtMS43bC4yLS44LjctLjdhMyAzIDAgMCAxIDEtLjUgNCA0IDAgMCAxIDEuMi0uMmMuNSAwIDEgMCAxLjUuMi40LjIuNy41IDEgLjkuMi40LjMuOC4zIDEuNHYzLjNsLjEuOS4yLjd2LjFoLTEuN2EzIDMgMCAwIDEtLjEtLjdsLS4xLS44Wm0uMi0zdjFoLTEuMWwtLjguMWMtLjMgMC0uNS4xLS42LjNhMSAxIDAgMCAwLS41LjlsLjEuNS40LjQuNy4xYTEuOCAxLjggMCAwIDAgMS41LS43bC4yLS42LjUuOC0uMi42YTMgMyAwIDAgMS0uNi42IDIuNiAyLjYgMCAwIDEtMS43LjZjLS41IDAtMSAwLTEuMy0uMy0uNC0uMS0uNy0uNC0xLS43YTIuNCAyLjQgMCAwIDEtLjEtMi4ybC43LS44IDEuMS0uNCAxLjQtLjJoMS4zWm02LjgtMi45djEuMkg0NnYtMS4yaDQuMlptLTMtMS44SDQ5djcuN2wuNC4yaC40YTIuNSAyLjUgMCAwIDAgLjYgMFYyNWE0LjIgNC4yIDAgMCAxLTEuMS4xYy0uNCAwLS43IDAtMS0uMi0uMy0uMS0uNS0uMy0uNy0uNi0uMi0uMy0uMy0uNy0uMy0xLjJ2LTcuM1ptNi4zIDEuOFYyNWgtMS43di03LjRoMS43Wm0tMS44LTIgLjMtLjYuNy0uMmMuMyAwIC41IDAgLjcuMmwuMi43YzAgLjIgMCAuNC0uMi42YTEgMSAwIDAgMS0uNy4yIDEgMSAwIDAgMS0uNy0uMi44LjggMCAwIDEtLjMtLjZabTMuNSA1Ljh2LS4ybC4zLTEuNS42LTEuMmEzIDMgMCAwIDEgMS4xLS43Yy40LS4yIDEtLjMgMS41LS4zczEgMCAxLjQuM2MuNC4xLjguNCAxLjEuN2wuNyAxLjIuMiAxLjV2LjJjMCAuNSAwIDEtLjIgMS41bC0uNyAxLjJhMy4xIDMuMSAwIDAgMS0yLjUgMWMtLjYgMC0xIDAtMS41LS4yYTMuNSAzLjUgMCAwIDEtMS44LTJsLS4yLTEuNVptMS43LS4ydjEuMmwuNC43Yy4yLjIuMy40LjYuNS4yLjIuNS4yLjguMmExLjYgMS42IDAgMCAwIDEuMy0uN2wuNC0uOHYtMmwtLjQtLjhhMS42IDEuNiAwIDAgMC0xLjMtLjdjLS4zIDAtLjYgMC0uOC4yLS4zLjEtLjQuMy0uNi41bC0uMy44LS4xIDFabTguNS0yVjI1aC0xLjd2LTcuNGgxLjZ2MS42Wk02NSAyMWgtLjZjMC0uNS4xLTEgLjMtMS40LjEtLjUuMy0uOC42LTEuMWEyLjcgMi43IDAgMCAxIDIuMS0xbDEgLjEuNy41LjUuOC4yIDEuM1YyNWgtMS43di00LjhjMC0uMyAwLS42LS4yLS44YS45LjkgMCAwIDAtLjQtLjVsLS43LS4xYTEuNiAxLjYgMCAwIDAtMS4zLjdsLS40LjctLjEuOFpNNzYgMjMuNVYyMGMwLS4zIDAtLjUtLjItLjdhMSAxIDAgMCAwLS40LS40bC0uOC0uMi0uNi4xLS41LjQtLjIuNWgtMS42YzAtLjMgMC0uNS4yLS44LjEtLjMuMy0uNS42LS43YTMgMyAwIDAgMSAxLS41IDQgNCAwIDAgMSAxLjItLjJjLjYgMCAxIDAgMS41LjIuNC4yLjguNSAxIC45LjMuNC40LjguNCAxLjR2NC4ybC4zLjd2LjFoLTEuN2EzIDMgMCAwIDEtLjItLjd2LS44Wm0uMi0zdjFINzVsLS44LjEtLjYuM2ExIDEgMCAwIDAtLjQuOWwuMS41LjQuNC42LjFhMS44IDEuOCAwIDAgMCAxLjUtLjdsLjMtLjYuNS44LS4zLjZhMyAzIDAgMCAxLS41LjYgMi42IDIuNiAwIDAgMS0xLjguNmMtLjUgMC0uOSAwLTEuMy0uMy0uMy0uMS0uNi0uNC0uOS0uN2EyLjQgMi40IDAgMCAxLS4xLTIuMmwuNy0uOCAxLS40IDEuNS0uMmgxLjNabTUuMS02VjI1aC0xLjZWMTQuNWgxLjZabTEwIDguNS0uMS0uNGExIDEgMCAwIDAtLjUtLjRsLTEtLjMtMS4xLS4zLS44LS41Yy0uMy0uMS0uNC0uMy0uNi0uNmwtLjItLjguMi0uOS42LS43LjktLjUgMS4yLS4xYy42IDAgMS4xIDAgMS42LjMuNC4yLjguNSAxIC44LjIuNC40LjguNCAxLjJoLTEuN2wtLjEtLjVhMSAxIDAgMCAwLS41LS41bC0uNy0uMS0uNy4xYTEgMSAwIDAgMC0uNC40LjkuOSAwIDAgMCAwIC44bC4yLjIuNC4yLjguMiAxLjQuNWMuNC4yLjguNCAxIC43LjIuMy4zLjcuMyAxLjFhMiAyIDAgMCAxLS44IDEuNiAzIDMgMCAwIDEtMSAuNUw5MCAyNWMtLjcgMC0xLjIgMC0xLjctLjNzLS44LS42LTEtMWMtLjMtLjMtLjQtLjctLjQtMS4xaDEuNmMwIC4zIDAgLjUuMi43bC42LjQuNy4xLjgtLjFjLjIgMCAuMy0uMi40LS4zbC4yLS41Wm01LTR2OC44aC0xLjdWMTcuNmgxLjVsLjEgMS40Wm00LjggMi4ydi4ybC0uMiAxLjUtLjYgMS4yYTIuNSAyLjUgMCAwIDEtMi4xIDFjLS41IDAtLjkgMC0xLjItLjJsLS45LS43YTQgNCAwIDAgMS0uNi0xLjJsLS4zLTEuNHYtLjVsLjMtMS41LjYtMS4xYTIuNSAyLjUgMCAwIDEgMi0xYy41IDAgMSAwIDEuMy4ybDEgLjguNSAxLjIuMiAxLjVabS0xLjcuMnYtMS4ybC0uNC0uN2ExLjUgMS41IDAgMCAwLTEuMy0uN2wtLjcuMS0uNS40YTIgMiAwIDAgMC0uNC43bC0uMS43VjIybC4yIDEgLjYuNiAxIC4yYy4yIDAgLjUgMCAuNy0uMi4yIDAgLjQtLjMuNS0uNS4yLS4yLjMtLjUuMy0uOGwuMS0xWk0xMDYgMjVjLS42IDAtMSAwLTEuNS0uMmEzLjMgMy4zIDAgMCAxLTEuOC0yIDQgNCAwIDAgMS0uMy0xLjN2LS4zYzAtLjYuMS0xLjEuMy0xLjYuMS0uNS40LS45LjctMS4yYTMgMyAwIDAgMSAxLS44bDEuNC0uMmMuNSAwIDEgMCAxLjQuMmwxIC44LjUgMS4xYy4yLjUuMiAxIC4yIDEuNXYuN2gtNS43di0xLjJoNC4xdi0uMWMwLS4zIDAtLjYtLjItLjkgMC0uMi0uMi0uNC0uNS0uNmwtLjgtLjJjLS4zIDAtLjUgMC0uOC4yLS4yIDAtLjMuMi0uNS41bC0uMy43LS4xIDF2LjRsLjEuOS40LjcuNi41LjkuMWEyLjMgMi4zIDAgMCAwIDEuOC0uOWwxIC45LS43LjYtMSAuNWMtLjMuMi0uNy4yLTEuMi4yWm03LjcgMGMtLjUgMC0xIDAtMS41LS4yYTMuMyAzLjMgMCAwIDEtMS44LTIgNCA0IDAgMCAxLS4yLTEuM3YtLjNjMC0uNiAwLTEuMS4yLTEuNnMuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS4xLS44bDEuMy0uMmMuNiAwIDEgMCAxLjQuMmwxIC44Yy4zLjMuNS43LjYgMS4xbC4yIDEuNXYuN0gxMTF2LTEuMmg0di0uMWwtLjEtLjktLjUtLjYtLjktLjJjLS4yIDAtLjUgMC0uNy4yLS4yIDAtLjQuMi0uNS41bC0uNC43djIuM2wuNS43LjYuNS44LjFhMi4zIDIuMyAwIDAgMCAxLjktLjlsLjkuOS0uNy42Yy0uMi4yLS41LjQtLjkuNS0uNC4yLS44LjItMS4zLjJabTktMS42di05aDEuN1YyNWgtMS41bC0uMi0xLjVabS00LjgtMi4xdi0uMmMwLS41IDAtMSAuMi0xLjVsLjYtMS4yYTIuNSAyLjUgMCAwIDEgMi4xLTFjLjUgMCAuOSAwIDEuMi4ybC45LjguNSAxLjEuMyAxLjV2LjVjMCAuNS0uMSAxLS4zIDEuNGE0IDQgMCAwIDEtLjUgMS4xIDIuNSAyLjUgMCAwIDEtMiAxIDIuNiAyLjYgMCAwIDEtMi4yLTFsLS42LTEuMi0uMi0xLjVabTEuNy0uMnYxLjJsLjQuN2MwIC4yLjMuNC41LjVsLjcuMmMuNCAwIC43IDAgMS0uMmwuNS0uN2MuMi0uMy4zLS42LjMtMXYtMS4yYzAtLjMgMC0uNS0uMi0uN2EyIDIgMCAwIDAtLjMtLjZsLS41LS41LS44LS4xYy0uMyAwLS41IDAtLjcuMmwtLjUuNWMtLjIuMi0uMy41LS4zLjhsLS4xIDFaIi8+PHBhdGggZmlsbD0iIzIyNEFDMiIgZD0iTTIzIDkxLjNoMi40YzEgMCAxLjgtLjIgMi41LS41LjYtLjQgMS4xLS44IDEuNC0xLjQuNC0uNi41LTEuMy41LTIgMC0uOS0uMS0xLjYtLjQtMi4yQTMgMyAwIDAgMCAyOCA4NGMtLjYtLjMtMS4zLS41LTIuMi0uNS0uOCAwLTEuNS4yLTIgLjVhMy41IDMuNSAwIDAgMC0yIDMuMmgtNC4zYzAtMS40LjQtMi42IDEuMS0zLjcuNy0xIDEuNy0xLjggMy0yLjQgMS4yLS42IDIuNi0xIDQuMS0xIDEuNyAwIDMuMS4zIDQuMy45IDEuMy41IDIuMiAxLjMgMyAyLjQuNiAxIDEgMi40IDEgNCAwIC43LS4yIDEuNS0uNSAyLjItLjQuOC0uOSAxLjUtMS42IDItLjYuNy0xLjUgMS4yLTIuNCAxLjYtMSAuNC0yLjIuNS0zLjUuNWgtMy4ydi0yLjNabTAgMy4zdi0yLjNoM2MxLjYgMCAyLjkuMSA0IC41IDEgLjQgMS45LjggMi41IDEuNS43LjYgMS4yIDEuMyAxLjUgMmE3LjUgNy41IDAgMCAxLS4yIDUuN2MtLjQgMS0xIDEuOC0xLjggMi40YTggOCAwIDAgMS0yLjggMS41IDExLjMgMTEuMyAwIDAgMS02LjUgMGMtMS0uMy0yLS44LTIuNy0xLjRhNi43IDYuNyAwIDAgMS0yLTIuMiA3IDcgMCAwIDEtLjYtMy4yaDQuMmMwIC44LjIgMS40LjUgMiAuNC42LjkgMSAxLjUgMS40YTUgNSAwIDAgMCAyLjMuNWMuOSAwIDEuNy0uMiAyLjMtLjUuNi0uMyAxLjEtLjggMS41LTEuNC4zLS42LjUtMS40LjUtMi4zIDAtMS0uMi0xLjgtLjYtMi40LS40LS42LTEtMS0xLjYtMS40LS43LS4zLTEuNi0uNC0yLjYtLjRIMjNaTTU1LjEgOTF2NC4xYzAgMi0uMiAzLjgtLjYgNS4yYTkgOSAwIDAgMS0xLjcgMy40Yy0uOC45LTEuNiAxLjUtMi43IDJhOS44IDkuOCAwIDAgMS02LjIuMiA2LjkgNi45IDAgMCAxLTQuMS0zLjRjLS41LS45LS45LTEuOS0xLjEtMy4xLS4zLTEuMi0uNC0yLjctLjQtNC4zdi00LjFjMC0yIC4yLTMuOC42LTUuMWE5IDkgMCAwIDEgMS43LTMuNGMuNy0xIDEuNi0xLjUgMi42LTIgMS0uMyAyLjItLjUgMy41LS41IDEgMCAyIDAgMi44LjNhNi42IDYuNiAwIDAgMSA0IDMuM2MuNS45IDEgMiAxLjIgMy4xLjIgMS4yLjQgMi43LjQgNC4zWm0tNC4zIDQuN3YtNS40bC0uMS0yLjdhOCA4IDAgMCAwLS42LTIgNCA0IDAgMCAwLS44LTEuMyAzIDMgMCAwIDAtMS4yLS43IDMuOSAzLjkgMCAwIDAtMy4yLjFjLS41LjItMSAuNi0xLjMgMS4yLS4zLjUtLjYgMS4zLS44IDIuMi0uMi45LS4zIDItLjMgMy4ydjUuNGMwIDEgMCAyIC4yIDIuOGwuNSAyIC45IDEuNGMuMy4zLjcuNiAxLjEuN2wxLjUuM2E0IDQgMCAwIDAgMS44LS40Yy41LS4zIDEtLjcgMS4zLTEuMi4zLS42LjYtMS40LjgtMi4zbC4yLTMuM1ptMjQuOC00Ljd2NC4xYzAgMi0uMiAzLjgtLjYgNS4yYTkgOSAwIDAgMS0xLjcgMy40Yy0uNy45LTEuNiAxLjUtMi42IDItMSAuMy0yLjIuNi0zLjUuNi0xIDAtMS45LS4yLTIuOC0uNGE2LjkgNi45IDAgMCAxLTQtMy40Yy0uNS0uOS0xLTEuOS0xLjItMy4xLS4yLTEuMi0uNC0yLjctLjQtNC4zdi00LjFjMC0yIC4yLTMuOC42LTUuMWE5IDkgMCAwIDEgMS43LTMuNGMuOC0xIDEuNi0xLjUgMi43LTIgMS0uMyAyLjEtLjUgMy40LS41IDEgMCAyIDAgMi44LjNhNi42IDYuNiAwIDAgMSA0LjEgMy4zYy41LjkuOSAyIDEuMSAzLjEuMyAxLjIuNCAyLjcuNCA0LjNabS00LjIgNC43di01LjRjMC0xIDAtMS45LS4yLTIuN2E4IDggMCAwIDAtLjUtMiA0IDQgMCAwIDAtLjktMS4zIDMgMyAwIDAgMC0xLjEtLjdsLTEuNS0uM2MtLjYgMC0xLjIuMS0xLjcuNC0uNi4yLTEgLjYtMS4zIDEuMi0uNC41LS43IDEuMy0uOSAyLjJsLS4yIDMuMnY1LjRjMCAxIDAgMiAuMiAyLjggMCAuNy4zIDEuNC41IDJsLjggMS40YTQuMiA0LjIgMCAwIDAgMi42IDEgNCA0IDAgMCAwIDEuOC0uNGMuNS0uMyAxLS43IDEuMy0xLjIuNC0uNi42LTEuNC44LTIuMy4yLS45LjMtMiAuMy0zLjNaTTgyLjcgODloNmMxLjQgMCAyLjUuMSAzLjQuNS45LjQgMS42IDEgMiAxLjguNi43LjggMS43LjggMi44IDAgLjgtLjEgMS42LS40IDIuMi0uNC43LS44IDEuMi0xLjQgMS43LS41LjQtMS4yLjctMiAxbC0uOS40aC01LjR2LTIuM2g0Yy43IDAgMS4zLS4xIDEuOC0uNC40LS4yLjgtLjYgMS0xIC4zLS41LjQtMSAuNC0xLjUgMC0uNi0uMS0xLjEtLjQtMS41LS4yLS41LS41LS44LTEtMWE0IDQgMCAwIDAtMS44LS40aC0zLjFWMTA2aC0zVjg5Wm05LjggMTctNC03LjdoM2w0LjEgNy41di4yaC0zLjFabTExLjgtNi40SDEwMHYtMi4zaDQuNGE0IDQgMCAwIDAgMi0uNGMuNC0uMi43LS42IDEtMSAuMi0uNC4zLTEgLjMtMS41IDAtLjYtLjEtMS0uMy0xLjUtLjMtLjUtLjYtLjktMS0xLjItLjYtLjMtMS4yLS40LTItLjRoLTMuNVYxMDZoLTNWODloNi41YzEuNCAwIDIuNS4yIDMuNC42YTUgNSAwIDAgMSAyLjEgMmMuNS44LjcgMS43LjcgMi44IDAgMS0uMiAyLS43IDIuOC0uNS44LTEuMiAxLjQtMi4xIDEuOGE4IDggMCAwIDEtMy40LjZabTEwLTEwLjdoMi43bDUgMTMuMiA0LjktMTMuMmgyLjZMMTIzIDEwNmgtMi4xbC02LjUtMTdabS0xLjEgMGgyLjVsLjQgMTEuNHY1LjdoLTNWODlabTE1IDBoMi41VjEwNmgtM3YtNS43bC41LTExLjRaIi8+PHJlY3Qgd2lkdGg9IjE2OCIgaGVpZ2h0PSI2IiB4PSIxNiIgeT0iMTIyIiBmaWxsPSJub25lIiByeD0iMyIvPjxyZWN0IHdpZHRoPSIxOCIgaGVpZ2h0PSI2IiBmaWxsPSIjMjM0Q0M3IiByeD0iMyIgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgMzQgMTIyKSIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Ik0yMS42IDEzOS41djEuMmwtLjIgMS42LS41IDFjLS4yLjMtLjUuNS0uOC42bC0xIC4yYTMgMyAwIDAgMS0uOC0uMSAyIDIgMCAwIDEtLjctLjRsLS41LS42LS40LTFhNyA3IDAgMCAxIDAtMS4zdi0yLjhsLjYtMWMuMi0uNC41LS41LjgtLjdsMS0uMWguOGExLjkgMS45IDAgMCAxIDEuMiAxYy4yLjMuMy43LjMgMWwuMiAxLjRabS0xIDEuM3YtMS41bC0uMS0xYTMgMyAwIDAgMC0uMi0uNmMwLS4yLS4yLS40LS4zLS41YTEgMSAwIDAgMC0uNC0uMmwtLjUtLjEtLjYuMS0uNS40LS4zLjh2My42YzAgLjIgMCAuNS4yLjYgMCAuMi4xLjQuMy41IDAgLjIuMi4yLjQuM2gxLjFsLjUtLjUuMy0uOHYtMVpNMTYwIDE0MC4zbC0uOS0uMi40LTRoNHYxaC0zLjJsLS4yIDIuMS42LS4yLjctLjFjLjQgMCAuNyAwIDEgLjJsLjcuNS41LjguMSAxLjEtLjEgMWMtLjEuNC0uMy42LS41LjlhMiAyIDAgMCAxLS43LjVsLTEuMS4yLTEtLjEtLjctLjQtLjUtLjdjLS4yLS4zLS4yLS42LS4zLTFoMWwuMi44LjUuNC44LjIuNi0uMS41LS40LjItLjVhMi41IDIuNSAwIDAgMCAwLTEuNWMwLS4yLS4xLS40LS4zLS41IDAtLjItLjMtLjMtLjQtLjRsLS43LS4xLS44LjEtLjUuNFptMTAtLjh2MS4yYzAgLjYgMCAxLjEtLjIgMS42IDAgLjQtLjMuOC0uNSAxLS4yLjMtLjQuNS0uNy42bC0xIC4yYTMgMyAwIDAgMS0uOS0uMSAyIDIgMCAwIDEtLjctLjRsLS41LS42Yy0uMS0uMy0uMy0uNi0uMy0xYTcgNyAwIDAgMS0uMS0xLjN2LTEuMmwuMS0xLjZjLjEtLjUuMy0uOC41LTEgLjItLjQuNS0uNS44LS43bDEtLjFoLjlhMiAyIDAgMCAxIDEuMiAxbC4zIDEgLjEgMS40Wm0tMSAxLjN2LTIuNWwtLjItLjYtLjMtLjVhMSAxIDAgMCAwLS41LS4ybC0uNS0uMS0uNi4xLS41LjQtLjIuOC0uMSAxLjF2Mi41bC4yLjZjMCAuMi4yLjQuMy41LjEuMi4zLjIuNC4zaDEuMmwuNC0uNWMuMi0uMi4yLS40LjMtLjhsLjEtMVptNy40LTEuM3YxLjJsLS4xIDEuNi0uNSAxYy0uMi4zLS41LjUtLjguNmwtMSAuMmEzIDMgMCAwIDEtLjktLjEgMiAyIDAgMCAxLS43LS40bC0uNS0uNi0uMy0xYTcgNyAwIDAgMS0uMS0xLjN2LTEuMmMwLS43IDAtMS4yLjItMS42IDAtLjUuMi0uOC41LTEgLjItLjQuNC0uNS43LS43bDEtLjFoLjlhMS45IDEuOSAwIDAgMSAxLjIgMWwuMyAxIC4xIDEuNFptLTEgMS4zdi0yLjVhMyAzIDAgMCAwLS4yLS42YzAtLjItLjItLjQtLjMtLjVhMSAxIDAgMCAwLS40LS4ybC0uNS0uMS0uNy4xLS40LjRjLS4yLjItLjIuNS0uMy44bC0uMSAxLjF2Mi41bC4yLjYuMy41LjQuM2gxLjJsLjUtLjUuMi0uOC4xLTFabTcuNS0xLjN2MS4yYzAgLjYgMCAxLjEtLjIgMS42bC0uNSAxYy0uMi4zLS41LjUtLjguNmwtMSAuMmEzIDMgMCAwIDEtLjgtLjEgMiAyIDAgMCAxLS43LS40bC0uNS0uNi0uNC0xYTcgNyAwIDAgMSAwLTEuM3YtMS4ybC4xLTEuNmMuMS0uNS4zLS44LjUtMSAuMi0uNC41LS41LjgtLjdsMS0uMWguOGEyIDIgMCAwIDEgMS4yIDFsLjQgMXYxLjRabS0xIDEuM3YtMi41bC0uMy0uNmMwLS4yLS4xLS40LS4zLS41YTEgMSAwIDAgMC0uNC0uMmwtLjUtLjEtLjYuMS0uNS40LS4zLjh2My42YzAgLjIgMCAuNS4yLjYgMCAuMi4yLjQuMy41IDAgLjIuMi4yLjQuM2gxLjFsLjUtLjUuMy0uOHYtMVoiLz48L3N2Zz4=", + "description": "Displays rotational speed reading as a horizontal progress bar. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'rotationalSpeed', label: 'Rotational speed', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Rotational speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 4000 - 2000;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 4000) {\\n\\tvalue = 4000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":500,\"color\":\"#305AD7\"},{\"from\":500,\"to\":1500,\"color\":\"#3FA71A\"},{\"from\":1500,\"to\":3000,\"color\":\"#FFA600\"},{\"from\":3000,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":5000,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":500,\"color\":\"#305AD7\"},{\"from\":500,\"to\":1500,\"color\":\"#3FA71A\"},{\"from\":1500,\"to\":3000,\"color\":\"#FFA600\"},{\"from\":3000,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Rotational speed\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"RPM\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "angular speed", + "spin rate", + "revolutions", + "rotational frequency", + "spin motion" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/rotational_speed_progress_bar_with_background.json b/application/src/main/data/json/system/widget_types/rotational_speed_progress_bar_with_background.json new file mode 100644 index 0000000000..bbcca9436a --- /dev/null +++ b/application/src/main/data/json/system/widget_types/rotational_speed_progress_bar_with_background.json @@ -0,0 +1,30 @@ +{ + "fqn": "rotational_speed_progress_bar_with_background", + "name": "Rotational speed progress bar with background", + "deprecated": false, + "image": "tb-image:cm90YXRpb25hbF9zcGVlZF9wcm9ncmVzc19iYXJfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==:cm90YXRpb25hbF9zcGVlZF9wcm9ncmVzc19iYXJfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48L2c+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZD0iTTE3IDE1aDMuNWE1IDUgMCAwIDEgMiAuNGMuNS4yIDEgLjYgMS4yIDEgLjMuNC40IDEgLjQgMS42IDAgLjUgMCAxLS4zIDEuNGwtLjcgMS0xLjIuNS0uNS4zaC0zLjJ2LTEuNGgyLjRjLjQgMCAuNyAwIDEtLjJsLjYtLjYuMi0uOWMwLS4zIDAtLjYtLjItLjlsLS42LS42LTEtLjJoLTEuOVYyNUgxN1YxNVptNS43IDEwLTIuMy00LjVoMS44bDIuMyA0LjR2LjFoLTEuOFptMi45LTMuNnYtLjJjMC0uNSAwLTEgLjItMS41bC43LTEuMmEzIDMgMCAwIDEgMS0uN2wxLjUtLjNjLjYgMCAxIDAgMS41LjMuNC4xLjguNCAxIC43LjQuNC42LjguNyAxLjJsLjMgMS41di4ybC0uMyAxLjUtLjYgMS4yYTMuMSAzLjEgMCAwIDEtMi42IDFjLS41IDAtMSAwLTEuNC0uMmEzLjUgMy41IDAgMCAxLTEuOC0ybC0uMi0xLjVabTEuNi0uMnYuMmMwIC4zIDAgLjYuMiAxIDAgLjIuMS41LjMuN2wuNS41Yy4zLjIuNS4yLjguMmExLjYgMS42IDAgMCAwIDEuNC0uN2MuMS0uMi4zLS41LjMtLjhsLjEtMXYtMWwtLjQtLjhhMS42IDEuNiAwIDAgMC0xLjQtLjdjLS4zIDAtLjUgMC0uOC4ybC0uNS41Yy0uMi4yLS4zLjUtLjMuOGwtLjIgMVptMTAuMy0zLjZ2MS4yaC00LjJ2LTEuMmg0LjJabS0zLTEuOGgxLjd2Ny43bC40LjJoLjRhMi41IDIuNSAwIDAgMCAuNiAwVjI1YTQuMiA0LjIgMCAwIDEtMS4xLjFjLS40IDAtLjcgMC0xLS4yLS4zLS4xLS42LS4zLS43LS42LS4yLS4zLS4zLS43LS4zLTEuMnYtNy4zWm04LjcgNy43VjIwYzAtLjMgMC0uNS0uMi0uN2ExIDEgMCAwIDAtLjQtLjRsLS43LS4yLS43LjEtLjUuNC0uMS41aC0xLjdsLjItLjguNy0uN2EzIDMgMCAwIDEgMS0uNSA0IDQgMCAwIDEgMS4yLS4yYy41IDAgMSAwIDEuNS4yLjQuMi43LjUgMSAuOS4yLjQuMy44LjMgMS40djMuM2wuMS45LjIuN3YuMWgtMS43YTMgMyAwIDAgMS0uMS0uN2wtLjEtLjhabS4yLTN2MWgtMS4xbC0uOC4xYy0uMyAwLS41LjEtLjYuM2ExIDEgMCAwIDAtLjUuOWwuMS41LjQuNC43LjFhMS44IDEuOCAwIDAgMCAxLjUtLjdsLjItLjYuNS44LS4yLjZhMyAzIDAgMCAxLS42LjYgMi42IDIuNiAwIDAgMS0xLjcuNmMtLjUgMC0xIDAtMS4zLS4zLS40LS4xLS43LS40LTEtLjdhMi40IDIuNCAwIDAgMS0uMS0yLjJsLjctLjggMS4xLS40IDEuNC0uMmgxLjNabTYuOC0yLjl2MS4ySDQ2di0xLjJoNC4yWm0tMy0xLjhINDl2Ny43bC40LjJoLjRhMi41IDIuNSAwIDAgMCAuNiAwVjI1YTQuMiA0LjIgMCAwIDEtMS4xLjFjLS40IDAtLjcgMC0xLS4yLS4zLS4xLS41LS4zLS43LS42LS4yLS4zLS4zLS43LS4zLTEuMnYtNy4zWm02LjMgMS44VjI1aC0xLjd2LTcuNGgxLjdabS0xLjgtMiAuMy0uNi43LS4yYy4zIDAgLjUgMCAuNy4ybC4yLjdjMCAuMiAwIC40LS4yLjZhMSAxIDAgMCAxLS43LjIgMSAxIDAgMCAxLS43LS4yLjguOCAwIDAgMS0uMy0uNlptMy41IDUuOHYtLjJsLjMtMS41LjYtMS4yYTMgMyAwIDAgMSAxLjEtLjdjLjQtLjIgMS0uMyAxLjUtLjNzMSAwIDEuNC4zYy40LjEuOC40IDEuMS43bC43IDEuMi4yIDEuNXYuMmMwIC41IDAgMS0uMiAxLjVsLS43IDEuMmEzLjEgMy4xIDAgMCAxLTIuNSAxYy0uNiAwLTEgMC0xLjUtLjJhMy41IDMuNSAwIDAgMS0xLjgtMmwtLjItMS41Wm0xLjctLjJ2MS4ybC40LjdjLjIuMi4zLjQuNi41LjIuMi41LjIuOC4yYTEuNiAxLjYgMCAwIDAgMS4zLS43bC40LS44di0ybC0uNC0uOGExLjYgMS42IDAgMCAwLTEuMy0uN2MtLjMgMC0uNiAwLS44LjItLjMuMS0uNC4zLS42LjVsLS4zLjgtLjEgMVptOC41LTJWMjVoLTEuN3YtNy40aDEuNnYxLjZaTTY1IDIxaC0uNmMwLS41LjEtMSAuMy0xLjQuMS0uNS4zLS44LjYtMS4xYTIuNyAyLjcgMCAwIDEgMi4xLTFsMSAuMS43LjUuNS44LjIgMS4zVjI1aC0xLjd2LTQuOGMwLS4zIDAtLjYtLjItLjhhLjkuOSAwIDAgMC0uNC0uNWwtLjctLjFhMS42IDEuNiAwIDAgMC0xLjMuN2wtLjQuNy0uMS44Wk03NiAyMy41VjIwYzAtLjMgMC0uNS0uMi0uN2ExIDEgMCAwIDAtLjQtLjRsLS44LS4yLS42LjEtLjUuNC0uMi41aC0xLjZjMC0uMyAwLS41LjItLjguMS0uMy4zLS41LjYtLjdhMyAzIDAgMCAxIDEtLjUgNCA0IDAgMCAxIDEuMi0uMmMuNiAwIDEgMCAxLjUuMi40LjIuOC41IDEgLjkuMy40LjQuOC40IDEuNHY0LjJsLjMuN3YuMWgtMS43YTMgMyAwIDAgMS0uMi0uN3YtLjhabS4yLTN2MUg3NWwtLjguMS0uNi4zYTEgMSAwIDAgMC0uNC45bC4xLjUuNC40LjYuMWExLjggMS44IDAgMCAwIDEuNS0uN2wuMy0uNi41LjgtLjMuNmEzIDMgMCAwIDEtLjUuNiAyLjYgMi42IDAgMCAxLTEuOC42Yy0uNSAwLS45IDAtMS4zLS4zLS4zLS4xLS42LS40LS45LS43YTIuNCAyLjQgMCAwIDEtLjEtMi4ybC43LS44IDEtLjQgMS41LS4yaDEuM1ptNS4xLTZWMjVoLTEuNlYxNC41aDEuNlptMTAgOC41LS4xLS40YTEgMSAwIDAgMC0uNS0uNGwtMS0uMy0xLjEtLjMtLjgtLjVjLS4zLS4xLS40LS4zLS42LS42bC0uMi0uOC4yLS45LjYtLjcuOS0uNSAxLjItLjFjLjYgMCAxLjEgMCAxLjYuMy40LjIuOC41IDEgLjguMi40LjQuOC40IDEuMmgtMS43bC0uMS0uNWExIDEgMCAwIDAtLjUtLjVsLS43LS4xLS43LjFhMSAxIDAgMCAwLS40LjQuOS45IDAgMCAwIDAgLjhsLjIuMi40LjIuOC4yIDEuNC41Yy40LjIuOC40IDEgLjcuMi4zLjMuNy4zIDEuMWEyIDIgMCAwIDEtLjggMS42IDMgMyAwIDAgMS0xIC41TDkwIDI1Yy0uNyAwLTEuMiAwLTEuNy0uM3MtLjgtLjYtMS0xYy0uMy0uMy0uNC0uNy0uNC0xLjFoMS42YzAgLjMgMCAuNS4yLjdsLjYuNC43LjEuOC0uMWMuMiAwIC4zLS4yLjQtLjNsLjItLjVabTUtNHY4LjhoLTEuN1YxNy42aDEuNWwuMSAxLjRabTQuOCAyLjJ2LjJsLS4yIDEuNS0uNiAxLjJhMi41IDIuNSAwIDAgMS0yLjEgMWMtLjUgMC0uOSAwLTEuMi0uMmwtLjktLjdhNCA0IDAgMCAxLS42LTEuMmwtLjMtMS40di0uNWwuMy0xLjUuNi0xLjFhMi41IDIuNSAwIDAgMSAyLTFjLjUgMCAxIDAgMS4zLjJsMSAuOC41IDEuMi4yIDEuNVptLTEuNy4ydi0xLjJsLS40LS43YTEuNSAxLjUgMCAwIDAtMS4zLS43bC0uNy4xLS41LjRhMiAyIDAgMCAwLS40LjdsLS4xLjdWMjJsLjIgMSAuNi42IDEgLjJjLjIgMCAuNSAwIC43LS4yLjIgMCAuNC0uMy41LS41LjItLjIuMy0uNS4zLS44bC4xLTFaTTEwNiAyNWMtLjYgMC0xIDAtMS41LS4yYTMuMyAzLjMgMCAwIDEtMS44LTIgNCA0IDAgMCAxLS4zLTEuM3YtLjNjMC0uNi4xLTEuMS4zLTEuNi4xLS41LjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEtLjhsMS40LS4yYy41IDAgMSAwIDEuNC4ybDEgLjguNSAxLjFjLjIuNS4yIDEgLjIgMS41di43aC01Ljd2LTEuMmg0LjF2LS4xYzAtLjMgMC0uNi0uMi0uOSAwLS4yLS4yLS40LS41LS42bC0uOC0uMmMtLjMgMC0uNSAwLS44LjItLjIgMC0uMy4yLS41LjVsLS4zLjctLjEgMXYuNGwuMS45LjQuNy42LjUuOS4xYTIuMyAyLjMgMCAwIDAgMS44LS45bDEgLjktLjcuNi0xIC41Yy0uMy4yLS43LjItMS4yLjJabTcuNyAwYy0uNSAwLTEgMC0xLjUtLjJhMy4zIDMuMyAwIDAgMS0xLjgtMiA0IDQgMCAwIDEtLjItMS4zdi0uM2MwLS42IDAtMS4xLjItMS42cy40LS45LjctMS4yYTMgMyAwIDAgMSAxLjEtLjhsMS4zLS4yYy42IDAgMSAwIDEuNC4ybDEgLjhjLjMuMy41LjcuNiAxLjFsLjIgMS41di43SDExMXYtMS4yaDR2LS4xbC0uMS0uOS0uNS0uNi0uOS0uMmMtLjIgMC0uNSAwLS43LjItLjIgMC0uNC4yLS41LjVsLS40Ljd2Mi4zbC41LjcuNi41LjguMWEyLjMgMi4zIDAgMCAwIDEuOS0uOWwuOS45LS43LjZjLS4yLjItLjUuNC0uOS41LS40LjItLjguMi0xLjMuMlptOS0xLjZ2LTloMS43VjI1aC0xLjVsLS4yLTEuNVptLTQuOC0yLjF2LS4yYzAtLjUgMC0xIC4yLTEuNWwuNi0xLjJhMi41IDIuNSAwIDAgMSAyLjEtMWMuNSAwIC45IDAgMS4yLjJsLjkuOC41IDEuMS4zIDEuNXYuNWMwIC41LS4xIDEtLjMgMS40YTQgNCAwIDAgMS0uNSAxLjEgMi41IDIuNSAwIDAgMS0yIDEgMi42IDIuNiAwIDAgMS0yLjItMWwtLjYtMS4yLS4yLTEuNVptMS43LS4ydjEuMmwuNC43YzAgLjIuMy40LjUuNWwuNy4yYy40IDAgLjcgMCAxLS4ybC41LS43Yy4yLS4zLjMtLjYuMy0xdi0xLjJjMC0uMyAwLS41LS4yLS43YTIgMiAwIDAgMC0uMy0uNmwtLjUtLjUtLjgtLjFjLS4zIDAtLjUgMC0uNy4ybC0uNS41Yy0uMi4yLS4zLjUtLjMuOGwtLjEgMVoiLz48cGF0aCBmaWxsPSIjMjI0QUMyIiBkPSJNMjMgOTEuM2gyLjRjMSAwIDEuOC0uMiAyLjUtLjUuNi0uNCAxLjEtLjggMS40LTEuNC40LS42LjUtMS4zLjUtMiAwLS45LS4xLTEuNi0uNC0yLjJBMyAzIDAgMCAwIDI4IDg0Yy0uNi0uMy0xLjMtLjUtMi4yLS41LS44IDAtMS41LjItMiAuNWEzLjUgMy41IDAgMCAwLTIgMy4yaC00LjNjMC0xLjQuNC0yLjYgMS4xLTMuNy43LTEgMS43LTEuOCAzLTIuNCAxLjItLjYgMi42LTEgNC4xLTEgMS43IDAgMy4xLjMgNC4zLjkgMS4zLjUgMi4yIDEuMyAzIDIuNC42IDEgMSAyLjQgMSA0IDAgLjctLjIgMS41LS41IDIuMi0uNC44LS45IDEuNS0xLjYgMi0uNi43LTEuNSAxLjItMi40IDEuNi0xIC40LTIuMi41LTMuNS41aC0zLjJ2LTIuM1ptMCAzLjN2LTIuM2gzYzEuNiAwIDIuOS4xIDQgLjUgMSAuNCAxLjkuOCAyLjUgMS41LjcuNiAxLjIgMS4zIDEuNSAyYTcuNSA3LjUgMCAwIDEtLjIgNS43Yy0uNCAxLTEgMS44LTEuOCAyLjRhOCA4IDAgMCAxLTIuOCAxLjUgMTEuMyAxMS4zIDAgMCAxLTYuNSAwYy0xLS4zLTItLjgtMi43LTEuNGE2LjcgNi43IDAgMCAxLTItMi4yIDcgNyAwIDAgMS0uNi0zLjJoNC4yYzAgLjguMiAxLjQuNSAyIC40LjYuOSAxIDEuNSAxLjRhNSA1IDAgMCAwIDIuMy41Yy45IDAgMS43LS4yIDIuMy0uNS42LS4zIDEuMS0uOCAxLjUtMS40LjMtLjYuNS0xLjQuNS0yLjMgMC0xLS4yLTEuOC0uNi0yLjQtLjQtLjYtMS0xLTEuNi0xLjQtLjctLjMtMS42LS40LTIuNi0uNEgyM1pNNTUuMSA5MXY0LjFjMCAyLS4yIDMuOC0uNiA1LjJhOSA5IDAgMCAxLTEuNyAzLjRjLS44LjktMS42IDEuNS0yLjcgMmE5LjggOS44IDAgMCAxLTYuMi4yIDYuOSA2LjkgMCAwIDEtNC4xLTMuNGMtLjUtLjktLjktMS45LTEuMS0zLjEtLjMtMS4yLS40LTIuNy0uNC00LjN2LTQuMWMwLTIgLjItMy44LjYtNS4xYTkgOSAwIDAgMSAxLjctMy40Yy43LTEgMS42LTEuNSAyLjYtMiAxLS4zIDIuMi0uNSAzLjUtLjUgMSAwIDIgMCAyLjguM2E2LjYgNi42IDAgMCAxIDQgMy4zYy41LjkgMSAyIDEuMiAzLjEuMiAxLjIuNCAyLjcuNCA0LjNabS00LjMgNC43di01LjRsLS4xLTIuN2E4IDggMCAwIDAtLjYtMiA0IDQgMCAwIDAtLjgtMS4zIDMgMyAwIDAgMC0xLjItLjcgMy45IDMuOSAwIDAgMC0zLjIuMWMtLjUuMi0xIC42LTEuMyAxLjItLjMuNS0uNiAxLjMtLjggMi4yLS4yLjktLjMgMi0uMyAzLjJ2NS40YzAgMSAwIDIgLjIgMi44bC41IDIgLjkgMS40Yy4zLjMuNy42IDEuMS43bDEuNS4zYTQgNCAwIDAgMCAxLjgtLjRjLjUtLjMgMS0uNyAxLjMtMS4yLjMtLjYuNi0xLjQuOC0yLjNsLjItMy4zWm0yNC44LTQuN3Y0LjFjMCAyLS4yIDMuOC0uNiA1LjJhOSA5IDAgMCAxLTEuNyAzLjRjLS43LjktMS42IDEuNS0yLjYgMi0xIC4zLTIuMi42LTMuNS42LTEgMC0xLjktLjItMi44LS40YTYuOSA2LjkgMCAwIDEtNC0zLjRjLS41LS45LTEtMS45LTEuMi0zLjEtLjItMS4yLS40LTIuNy0uNC00LjN2LTQuMWMwLTIgLjItMy44LjYtNS4xYTkgOSAwIDAgMSAxLjctMy40Yy44LTEgMS42LTEuNSAyLjctMiAxLS4zIDIuMS0uNSAzLjQtLjUgMSAwIDIgMCAyLjguM2E2LjYgNi42IDAgMCAxIDQuMSAzLjNjLjUuOS45IDIgMS4xIDMuMS4zIDEuMi40IDIuNy40IDQuM1ptLTQuMiA0Ljd2LTUuNGMwLTEgMC0xLjktLjItMi43YTggOCAwIDAgMC0uNS0yIDQgNCAwIDAgMC0uOS0xLjMgMyAzIDAgMCAwLTEuMS0uN2wtMS41LS4zYy0uNiAwLTEuMi4xLTEuNy40LS42LjItMSAuNi0xLjMgMS4yLS40LjUtLjcgMS4zLS45IDIuMmwtLjIgMy4ydjUuNGMwIDEgMCAyIC4yIDIuOCAwIC43LjMgMS40LjUgMmwuOCAxLjRhNC4yIDQuMiAwIDAgMCAyLjYgMSA0IDQgMCAwIDAgMS44LS40Yy41LS4zIDEtLjcgMS4zLTEuMi40LS42LjYtMS40LjgtMi4zLjItLjkuMy0yIC4zLTMuM1pNODIuNyA4OWg2YzEuNCAwIDIuNS4xIDMuNC41LjkuNCAxLjYgMSAyIDEuOC42LjcuOCAxLjcuOCAyLjggMCAuOC0uMSAxLjYtLjQgMi4yLS40LjctLjggMS4yLTEuNCAxLjctLjUuNC0xLjIuNy0yIDFsLS45LjRoLTUuNHYtMi4zaDRjLjcgMCAxLjMtLjEgMS44LS40LjQtLjIuOC0uNiAxLTEgLjMtLjUuNC0xIC40LTEuNSAwLS42LS4xLTEuMS0uNC0xLjUtLjItLjUtLjUtLjgtMS0xYTQgNCAwIDAgMC0xLjgtLjRoLTMuMVYxMDZoLTNWODlabTkuOCAxNy00LTcuN2gzbDQuMSA3LjV2LjJoLTMuMVptMTEuOC02LjRIMTAwdi0yLjNoNC40YTQgNCAwIDAgMCAyLS40Yy40LS4yLjctLjYgMS0xIC4yLS40LjMtMSAuMy0xLjUgMC0uNi0uMS0xLS4zLTEuNS0uMy0uNS0uNi0uOS0xLTEuMi0uNi0uMy0xLjItLjQtMi0uNGgtMy41VjEwNmgtM1Y4OWg2LjVjMS40IDAgMi41LjIgMy40LjZhNSA1IDAgMCAxIDIuMSAyYy41LjguNyAxLjcuNyAyLjggMCAxLS4yIDItLjcgMi44LS41LjgtMS4yIDEuNC0yLjEgMS44YTggOCAwIDAgMS0zLjQuNlptMTAtMTAuN2gyLjdsNSAxMy4yIDQuOS0xMy4yaDIuNkwxMjMgMTA2aC0yLjFsLTYuNS0xN1ptLTEuMSAwaDIuNWwuNCAxMS40djUuN2gtM1Y4OVptMTUgMGgyLjVWMTA2aC0zdi01LjdsLjUtMTEuNFoiLz48cmVjdCB3aWR0aD0iMTY4IiBoZWlnaHQ9IjYiIHg9IjE2IiB5PSIxMjIiIGZpbGw9Im5vbmUiIHJ4PSIzIi8+PHJlY3Qgd2lkdGg9IjE4IiBoZWlnaHQ9IjYiIGZpbGw9IiMyMzRDQzciIHJ4PSIzIiB0cmFuc2Zvcm09Im1hdHJpeCgtMSAwIDAgMSAzNCAxMjIpIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTIxLjYgMTM5LjV2MS4ybC0uMiAxLjYtLjUgMWMtLjIuMy0uNS41LS44LjZsLTEgLjJhMyAzIDAgMCAxLS44LS4xIDIgMiAwIDAgMS0uNy0uNGwtLjUtLjYtLjQtMWE3IDcgMCAwIDEgMC0xLjN2LTIuOGwuNi0xYy4yLS40LjUtLjUuOC0uN2wxLS4xaC44YTEuOSAxLjkgMCAwIDEgMS4yIDFjLjIuMy4zLjcuMyAxbC4yIDEuNFptLTEgMS4zdi0xLjVsLS4xLTFhMyAzIDAgMCAwLS4yLS42YzAtLjItLjItLjQtLjMtLjVhMSAxIDAgMCAwLS40LS4ybC0uNS0uMS0uNi4xLS41LjQtLjMuOHYzLjZjMCAuMiAwIC41LjIuNiAwIC4yLjEuNC4zLjUgMCAuMi4yLjIuNC4zaDEuMWwuNS0uNS4zLS44di0xWk0xNjQgMTQxLjR2LjhoLTUuN3YtLjZsMy41LTUuNGguOGwtLjkgMS41LTIuMyAzLjdoNC41Wm0tMS4yLTUuMnY3LjhoLTF2LTcuOGgxWm0zLjUgNC0uOC0uMS40LTRoNHYxaC0zLjFsLS4zIDIuMS42LS4yLjgtLjFjLjMgMCAuNiAwIDEgLjJsLjcuNS40LjguMiAxLjFjMCAuNCAwIC43LS4yIDEgMCAuNC0uMi42LS40LjlhMiAyIDAgMCAxLS44LjVsLTEgLjJhMyAzIDAgMCAxLTEtLjFsLS43LS40LS42LS43LS4yLTFoMWMwIC4zIDAgLjYuMi44bC41LjQuNy4yLjYtLjEuNS0uNC4zLS41YTIuNCAyLjQgMCAwIDAgMC0xLjVjMC0uMi0uMi0uNC0uMy0uNWwtLjUtLjQtLjctLjEtLjcuMS0uNi40Wm0xMC4xLS43djEuMmwtLjEgMS42LS41IDFjLS4yLjMtLjUuNS0uOC42bC0xIC4yYTMgMyAwIDAgMS0uOS0uMSAyIDIgMCAwIDEtLjctLjRsLS41LS42LS4zLTFhNyA3IDAgMCAxLS4xLTEuM3YtMS4yYzAtLjcgMC0xLjIuMi0xLjYgMC0uNS4yLS44LjUtMSAuMi0uNC40LS41LjctLjdsMS0uMWguOWExLjkgMS45IDAgMCAxIDEuMiAxbC4zIDEgLjEgMS40Wm0tMSAxLjN2LTIuNWEzIDMgMCAwIDAtLjItLjZjMC0uMi0uMi0uNC0uMy0uNWExIDEgMCAwIDAtLjQtLjJsLS41LS4xLS43LjEtLjQuNGMtLjIuMi0uMi41LS4zLjhsLS4xIDEuMXYyLjVsLjIuNi4zLjUuNC4zaDEuMmwuNS0uNS4yLS44LjEtMVptNy41LTEuM3YxLjJjMCAuNiAwIDEuMS0uMiAxLjZsLS41IDFjLS4yLjMtLjUuNS0uOC42bC0xIC4yYTMgMyAwIDAgMS0uOC0uMSAyIDIgMCAwIDEtLjctLjRsLS41LS42LS40LTFhNyA3IDAgMCAxIDAtMS4zdi0xLjJsLjEtMS42Yy4xLS41LjMtLjguNS0xIC4yLS40LjUtLjUuOC0uN2wxLS4xaC44YTIgMiAwIDAgMSAxLjIgMWwuNCAxdjEuNFptLTEgMS4zdi0yLjVsLS4zLS42YzAtLjItLjEtLjQtLjMtLjVhMSAxIDAgMCAwLS40LS4ybC0uNS0uMS0uNi4xLS41LjQtLjMuOHYzLjZjMCAuMiAwIC41LjIuNiAwIC4yLjIuNC4zLjUgMCAuMi4yLjIuNC4zaDEuMWwuNS0uNS4zLS44di0xWiIvPjxkZWZzPjxwYXR0ZXJuIGlkPSJhIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBwYXR0ZXJuQ29udGVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCI+PHVzZSB4bGluazpocmVmPSIjYyIgdHJhbnNmb3JtPSJtYXRyaXgoLjAwNTAzMTQ1IDAgMCAuMDA2Mjg5MzEgMCAwKSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgeD0iMCIgeT0iMCIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMyIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xNTE2XzczMDMzIi8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xNTE2XzczMDMzIiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PGltYWdlIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3QkRBQUlCQVFFQkFRSUJBUUVDQWdJQ0FnUURBZ0lDQWdVRUJBTUVCZ1VHQmdZRkJnWUdCd2tJQmdjSkJ3WUdDQXNJQ1FvS0Nnb0tCZ2dMREFzS0RBa0tDZ3IvMndCREFRSUNBZ0lDQWdVREF3VUtCd1lIQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnci93Z0FSQ0FDZkFNY0RBUkVBQWhFQkF4RUIvOFFBSGdBQUFnTUJBUUVCQVFFQUFBQUFBQUFBQndnRkJna0VBd0VDQ2dEL3hBQWFBUUFEQVFFQkFRQUFBQUFBQUFBQUFBQUJBZ01FQUFVRy85b0FEQU1CQUFJUUF4QUFBQURHUDUvZDdBLzRnamNRdnFsS1JhKzU2UXRGcWJMb1ZoMXViQ3ZYd1F2UkVUVVV2emJSUE5ZdnEyUFcvTW0xbzk0T2hlVFNZVmFPNEd5YkY5VC9BRExleDUzM2ordUhoUll1NDlPNTNQSjFzVEtpamFJcy9uc3hFNkxMYVNhWGlZWlZkNkZkUTg5Yjl4RUJYQVgwY1lPcE43c21reEsrYk92SzhPYlFjcG5EUDFNUG9PanRDZVo2MlRmYVR5OW9DNGNuRjFaVWVLRktFd3dPMzVJRThWRWJSekxiUm1WTzd1cFBjalZwWXdlbGpaelBadXM5YnFReThhV3NkL05GNitFcEF4Wk5tbVQ5Q3BJSENEaFF5Q0h4ck5rRjVXN3dxWEVidDdpM050RW8xdnFtaXNBZ3k1V2JzeThXbTV1UFRPQVhwV1RHOGJnclF6Y1dBYXFRUVVaQlBSeVdUTlRpQnBka3MwelpsWnJJV05DUGExNWtwdlRtQnNScW9WR3BHZSt1Q3hhb2tqTlZlclM5dTV1NFdmdlBiQ3IyTUZiSTFIODNZWVl0Vlc1YnF6SktNdU5VdXluMzdtNWhXWTQ4QjRYTW52M1FKNWw0MG0rNUhORWMyUFR4dDVoMDBmZ1E2cm9uZzFJYnN6K0tIaElhWE5jSDFucXhqMExMVmFreWhXaXczQ29zSGFoV3ZkeFJVclJXWVhkR2tsVTh5Zmw3c3dOdVlkMFI0YzlpVko0ODhJcUlpT2lKUUJJNExlWk5CRFVrQlRGbnMrTkVRblZBWmFNVXdmdkpwZTdMY3RLYitqZmpqWGlvdmRhV3dVRFJKcXM5THlDV0ZhbzhGZnF1YWV2TEUzVS95Y21acWhSME9TUDRIbGEyUUQ5cDJiUFFrbzNIVmRkdkwyR3BXSnlOQ2QwT2VIN0t1TlVGaktYVWQ4b3VUZ3duSVR1cVo4YXN5WCtsbXRDSFJiek5rOE82QXkvMmlGYlRkUE5vUEVYWFNpNXllaGpiaVZ0Ti9PMUdSRDA4WUVCTzlNNkFlaCtETlNvZTVrd0t3eGRRc1JrdDZ1SlU5VVNGQ2pvNDdhdFl0UUNkY2IvVHhEclJQUmJ6ZGRDSExiZVFyMHk1Q0dXelcwTHgzbnVLdjJtckY1UGRpMGtWWGllQldVdHRKcXUzTCs0dWZEK2ZQMnZPbmM3dkZrMFBmbnNuMm1PZnVtTEJ4cEd1b3Z2T1Jtd25xa1NndmlPUzUwcjNkOVBFZEM0TUxlZzZodU5FYzlZamhFbnJrcHkzMXdIVjVoR3M5SnZQMTFObGJhTk1zdHVaY3F6ZzdMV0t6ZER6dFpNbXlGYk16YjV0QS9JNkFmejNXMGMvR2U5ZUtqaHc3Y0tCbGxPYU9JWFRIRDBNaERVL0tCNjhHcG01dktqazh2TFBIVkQzN2hEYVJDbFF1eWNtcTFlUFU3aDlQZk9QWUJxM2swVlZpTUNrU1FzRnAxczlPcjBTd29sazlIWFEzejlha1hsTXFaNEZMTm1lb0ZZeWk4ZGxtVWJsSU0yU3h1bTVRbFFvellCMVJtNDBZV2JoSjErOTF4QnJYQmZhTEFNb0N0Sm9wMGtwdWNVS3RWU29NRlIwdzYzRkd2T0FkWi9OUzZJelRaclBCbnRFT3EzVm1wMUVMODZhVFpkRmNQUmhCYW1lUThHSFVOMFNaVXlYZGZBV0FtNi91cUY2SUFMUkgxRFJWRVlTRlhDelc0S0t2ZHBwOWFYSnc4VzdrWmVpWmVUSnFlakxlMkFrd0dFNENKK3oyMDVpZ2o4WEFjT0FGcGp6VVdsSjNxUklrbkxMWWlIbHV2S211SVNxYzVIdzkrMTZTbVpDYis1NW5NOVh3eTZHampUbVBDRmt6azB4TXF1Rkt5b0RBQjdJUU4xc0dWdnZjYzQySWNITmlVTU0yVTNSRlF0a08zdXFOVTRuNDdlYm83TzZLWmZ5ZXNrMkxLUEdPQlhTWlZqUmlwVkE5b3E1cmtOOU12SnVtb05iRVl4NUxGT2RDdWpQckNsQWJzMXRlWUZWa09OTTdWSnBpVGsrVlJyV1VVUmNWY2N2UGdxczZqZi94QUFyRUFBQkJRQUJCQUlCQXdVQkFRQUFBQUFFQVFJREJRWUhBQkVTRXhRaEZTSWpKQWdRRmpFeU14Zi8yZ0FJQVFFQUFRZ0NqbVkxbmJyM002OXpQOTlEMytXREhWSm4yMHNscitXNnVMcWE2TithVlY1ODI1ZytReStvcDZTSnNrdFlWNmpJMzlaQVNHYVFWM1VVVEd0K3lIMXdNTHJFeldia3E4SmNLTUhkVE4vYXFheCtvbGQ1cGl6ZVFvSSs3YmlobzlnSVVEUHlaeHlUa0RaelkzS28vZGVuVFNwOXk4ZVA5MVlPM3E5dURNeFdQdWdNWnQ5cHRyQjRYVnBUY29oMzROSyswNDF0cWtOVHJ4SEwyNjhuZEo1OSs2eVA3LzJhMWZORTZ4bW5pbzRmeGt0L1NWV2tpUmhOdG5LZG8zdG9zc01ZQ3hzMDdYVzVUdjQzSTF2Y2l1WlR5K3YzZjkwMEtlU2Q4eFZmS2VpZFp5blFRSkU2SkJIS2c5QlY1V1BPR2VGTnM4TlBRVzB3dFZhM3BKclBobWNXZDNWY1VxYkI0MytObE5zajdiTVBIV0lETWJJVTlBS2dhK3d2TXppVWdqUjdldmF2U3ZWMzI3djFTQ0RrSHcvUHJPTk1qZERRV0Z4eUptcVNrMTBRZFZFZkhERDYwNGNsamRaR1M5RmRsbmpWTHZRcFh4TERIcCt4OWxMTXlLbG1kMmN0U0ZKRkkxRXdqZzFlMWlpZGtnVHA3bXUra05qWk5DbzcrUzgxRHJLeEpTdGRqN2VwaS9KZGNNbmVZL3d1dFJYUldsQ1pYTy9wMHhXR0lwU3pUT1pNcmFuNTlsOWsrTStWNlBlMDdKSGpjZjJwSk1MT2pPSldFaC9Kb0J1SDdCMzJYSHhSWER2VDVORnhabEd3dWpueXZKK0xwNmtRSS9jM1ExL3BXblZkaEtzRGtnZlM4blI0QWQxa3k0L3FQdnJGcU5HRzVLdmRhWTJzdFhWNmsyTDQwWlZvblJBYWpxallzMDRpc0tTYVdqMTFQWmlkb0dsTWVubWxnVDlMMkxsUjg3a2R5SlJaY0dZaUluak1xeWFjNFd0SVhRTUk5YjZNN1Q1VS93REs1czdrVFV3QndXUTllU0pxdFNTZGZhWWw4QUsra0RUR0d5UndqRjZ6T2pKKzhOdGFBMGhzQTBHMWI2MGxBc3NGcWtta3NvNzU5cENzTWdRUU9qUGIrMU5uYndtd1VLWk1wWHdKMktvVzFsTGF4a0IxeEkwVTZIU0RPZ243T1RPVkRqN0tRK1VvME8ycjN4RGhZSWtNbjhuQlYxNzYrQ1dGU3JGRlZVU1FueW1haWNrM1JRaGN3M1hIT21BenBzN3BMN2tPOExNbFBESXRyTTZYM2x5R0hFckZFbUFyTlVCQXBCVzh3TUZsUXBQVlg5eGJsUGRWSDF3Ynp5R3hNbzh6VzVpdWI3czdmVjJqU2F2ajJlUmpMRUlzQWFLbXNEQWZqM2JLTXVTWjN4clN0c2hMbVVLTUxER1djSHp3aHNsTTB4SGxRWm1FbXZZT1BBQTBSRTZXZXhxblIzZGNGeWNPTmFIMkkxaHkvYmoxc0lFRUcyc3pMaVFlT3RuSmRFMThySHVVaHJsNU1JdGh6WGVzbjJKT3NybU1IdGN3UFl3S0FrckZaR0x4RnlsY1NNSTZ4K0p1Nk5QbjM5L3luYW02dXZ1SU5yUkRNdjMrUEgxVU0yN1U5S1NzdExsdjV3YWtyeThydnhwOVBaaXd6em9jQlpPclFTbmxUVTF6VGozMWhsenR6ZWdXYzBNczJUM0lGUThtQ0Vya1dZa3YrTmlMMzg1U3pveXBJa0psZEU1YU1naG5tSGQ4V2FnL3pOR0I0aTIweERpN1RMY2N1cTNLVVUyTDF4OXVtcy9lUjNXL25HYmVQZTZxNDgwR2ptN0RaRGlzdXJycHgrdi9BSlY2SXA1aHk1OU5tSjNOWmU4c2FlR0tKeURXL3BqVklkM1pGVHhDbEp3NVZ6M1B5NFJuMngrU24vQk9HdjROSlhKVzZJQ2NlckNiWFRFUjFGMFBIRlBiNFRVQldyL2pYeGpYZXNFSTZXWHU5cVJOYkNxTmw0ZHVKNHJaL3FTbEhGc1hrUURSSTFVVkd0YTlQMExBeE8vc0pqYXp5Ykc5aVRQOGVyd3Y4ZldrR056MkpTeXUyMlU2NlBJWWhzTlhCWG5jcVgvNzQ0MUp5SWpmSjEwbW9FZ1dPN01BcVRqNUxzR256NkZzUkdibDBhSFJ4TjRNc2hvTE1nUit1MVd0T3ZKQndTakwrT1ZJN2VnMGRWUG43S3l1QmR0TXVUa0lpTjVKdXA2dU9zWDVWNWZHT1FhdnAzd1NmeXJ6TWoxWWpMYXV3bC9QUjJ6SW5KZElPMW52Q2IyN2VjQS82T2lHTDVmYzZlVFBxVmpZVy9WdEwrVE5aQ3k3dkNLMk9MR1pURlp5dHp5STVhejJUTTd5UXhNUk90QzFPeXRiWWhSeXY4NUE2ai9FdzVTN3F6TWRZSHlGT3k5eTJpdVlTeU54aTcrNG5icHdSc0JyOUpUSXpWSng2QlhRSkF6ZEh4MVlpZ2kxL0h0N2JVRE5FdkhOT1JrS2lHM0cyUTlEcGdKNzdxaU5waWFNMFNYK0lOWXUrSHh0djRnMk1vamFWeEFNYXk1K0xVMHpXZXNxVFFaNTBmZFNMUnIyZnc5cHM0cThkWW9ZTEZnTkxMY3daT3FraFl0akpBVXdOVTdVMXY1cDJjeXlWRTZzQzNFdmNpRnAyWDc1Sm9OVFdHTy9Jd09oams3ekJBd1hTeXNxK011VW1aa1I5VmJNbnJaUkhqQmFhN3JhMlA0c2V1MGtseVlyb3ZLd2hnYW5YSFJJKzRwUDhZTjFvSzVLR1NxaUZmWlFRU3k5VHpPa21WN28zUGJJa3NlWDVKdmMxRDZZZ09jZ2lYK3A2OHVWSGt4SU5IeXpaV1FibWhDV0p0dHBVbm11bU9JbkhEWXh5UXFyR3ZPKy9MclBUU1BrUmVnWFF5cDYzRkJLajE5bG1za1QxYXRsblF0Wng2MlFtNnE1cVd5bHF5cTQ2ZFUrS05pY1VNTEV5N3RzOFRSUHI0cTZIbGZBV2JScGJxQWFBV2RXUkhBWHdtVFlqeE5SdWFEUTF5eXc1UGYzR1ZsL2pGMjF2dEpFN24xQnRYK3VhRnFPWDdnci9obFJQc3JoSzBpeWxtcC9RdlN4ZDArOG1QSkJjTldEc2toajVGbG1Ycy9zc3NqcFd4TXlHZUJIRVdjcTRGSHNucFhpVVZiWitMZ0pZcUNSNDNjckQzR25xN1NUdHkxWHNIT2lMandvY0I5OHdlV05zRnJjdWhmczcrY1JrQTB1Wk50YlBLUlRhSGtIUFBydEM4cUMxK1E3d1NXU3VtK0srWmZ2dDI2NDdyd3BDbk9sT1duc1pJL2tEdGRHVjFzTkltanFBaHhReDFTRnFPOEZSZW5NNzlaeGtuNU52Z1BXU1RSeUZNY0oyVlVYNDZzc1lrNk5NRnRLUUpHVk9yZmNWUkloVnB5WlMxOVZHc1Y3eVBLUWJPUDBta3NKQ1kxNjBFNms1Z1l0M0ZxK1YrcUxsMWFBR3NsaE8vRDd5dVdybXJ4TE1XaWdGdE9UaFFDS3hmbE9qR2lFV1hxc045N3A0eUhveEhkVnBzZnFVYnFvai9JMlF6WEY4VXVLbmNiUVNZYlQxbmZwL3dBbUIzcUk5MHFmNythTC9wNGJ4Zm1NOHNraW1RZkVuSmc4SkZhNHFOWXlFbGFkSldrQjlOc1M2OUNJT2pOQkpZMkRlbzV5VEhPTm1KeURLZXljMTJ4Vmc0c1lEZUxwdlRxR292SVdzcWhwb1NiUXZrQzFMN2ZqTERrR3pzbnkxN1p5NXZ4M3lGdEh4eVE5M1FTckVpOW45TisxK2tGTUlqVjZRT21qY25vQTNHdXF1ekJoT1k3bVp2eHJXdm14K25mNHl5Y1ZlY1h2cmorTjduLzFEd3o1YXA2QWw2UlAzUGtwS3oyczc5Q2ppbC90RkNZNm5uVjZrMkdCcVlZM1BZUlVzamY0UjNPalVxTjN2aURTNktXenM4L3gxVFhvMFpsRkxsamIreWh0N1RUOFdJUlhvVFF3MWgyYnBacUpTQ2lxK2k4VFBGQ1dPVXFFeWdFWjdCckd4bHNadmE1aW9pOTFpUXFSZXc0V1l0QzErOHp3eGMzaTk0UmVITXBrQlVPM0pmTm5FbVFaNmNuYi93QlMvSWxtam1WSCtWMzBrS3Rrek8zUGlLOU5rR1Voa1B3aXBnQ0I1SElyWVVrWHUydFVpS1BzNnhXV2R2ajBVRjI2MmxYaktuVEVWOFZaaTZlNGJGSVM3VldXRXRFL0hXdCtWWFBUVENEYVNuT0NlZ042YlhETmNTdXR1dmM5WUdRK0pUM05kNHhOR2Q0QjFUN0J2czZvT05EYkNackl4K09xRE1NOXUwbTVjNHF5bjZNem9PZitUTkRHb29aRTB4a3lrMkhzaVoveTR2djE3NVhkUXE5cjBlbUMxa1FnM3dpQkxGcllFaWxpRmhtN0VJUEIyaS9RV2s2cDJReUY2cjl0aUx2cmwxdEphYWo4RzFQZ1dtNnJJUVRCZ1FyMlM4ejYxc24rUVdRVVBwZ0sxOXdVOVg5TVZ6bTkzeU9kSC81d25lVXpHR1U5cFp4dmduRk4wUElaME1za1JtZk9HbWU4eWFOdzNUNW52LzY4bDY3ZjJaQS8vYXRiRkY5dStmNHIrak04Z20rOVk3SFBiR0IwZnVBZzFVQ3NSSnlkTlRJejZNMTg3UDAxK2J6ZG9VUTJTbzJlTXM3RXByU0QrS05ORUgzbWtybTAxS3NSSmNqdkgxLzJWZXc3T294M21QVnNkRmxVYi9JSy9QVmdUL2oxOURwcjFqdkY3V1VsbE15TzAySEhKZEM5VkVtcnUwdmoxRlZFRVF1ZEFRTktNL3drYjI4azhxK1ZrOFhwNmRYaE9UOXhjOEZOOVF5WkswSFQydHE3UW1wbWhrNnJkcTJyakNhNlBrdVNENGlUdTNObXo0aEVWNVpsWnMxd1ZYRmM2SFYwNFJGL2NiNHV0Sy9GbFhGOFNmTCtweXE3N1hxVC93QUkrczZGSVdjajB0eVpaWHNvUUswRWVxZ1NNZXRra2JNMTYwMDdaQWZGOTRQR0JGN1FOcG5SYStIOG9BdzIwR3IvQURBTDkwa25zS3FLUkxWa2lSZmpTNjFzWmF0aVY4S3lLZktyVjdJS2RNeDNsR3RtUlllVmZNMTZmN1R3THRDL0dJTUVaNnU3L3dEL3hBQkZFQUFDQVFNQkJRUUdCd1VHQlFVQUFBQUJBZ01BQkJFU0JSTWhNVUVpTWxGaEVDTkNjWUdSQmhSU2NxSEIwVE5EWXJIaEIxTnprcUx4WTdMQzR2QWdKRFNDbFAvYUFBZ0JBUUFKUHdMMEd0ZzI4OG9qNE9WYmkvbjI2Z2hSdzRZSWlkZ0VlVkxIdkQvZFI2UlRMZ2NOT2VWRUhXK25oUTVTS2Z4b2MrT0t3S2tDUlJMcWtjOUJWenViV0oyQU1Ua2J4YzgyelZvci93REZsNEwvQUZyYkVVWCtIYUEvenFhd3Y0L2Fqa2hNTGZNWkg0Vlp2c3JhbDNZZlZDN1I1SWgxYXNKN0pIdXExU3lzcGIwMit6N2RpWkpwbEM4WlBkK3RSdUFlNnpMaWxZWjQ4UnpycGJubDc2eWswR0NueE9QenI2WjJOaElFQmpGNU1WM251cjZVd1NSWGpBUGV3WktRbnpyKzFJVzY1d0htaDBMbjNsdlRqNDBFK0hwc2xjUExxTWg2VmNBb3Jhd1lSMXhSbmtsUzYzVFE0MUgzOEJXejdoQW5EVThEQ3RsM0Q4T2tEVkJkV3BaY25VY0xLaDRVZXgwWDdYblFwZndvVkhrQTVCSE5UNGlualc5RU1pN08yZzhJWXBxR0NmZld6ZHAzc1ZxNFI3eVlhVk1nSEhIbFVHbG9uNEQ3UERsUjVRRmY5VmFoQ0V6SVU1OERWdFB2ZllkbnI2TzNGemZTYUk5UVBCbjVjNitpY0U2Y1NyaThFZy8xR2xvME0rSEdnS1prZ01nRHlMN05iTUYzZE5FcW1VeU55WGdPQU9LMlhGREQ5VFZ0QUhQaWVkTGdVZy8rTXY4QXpVZkgrVldyU3k3c3NtVGhNOU10MHF5K3JmV0pXWm9Wa0xoUEhqNzZYbjBwZXRkNFVNZWpoMVJ2c21yVzZ1SmJRSEZyYjNBVEo4ZU5mUXk4MmZhTHdNdHhKckRFK2RPTzdJUHpxWHRUVzdLT0hYRmJFdGJuYU1kNTJqY1JCOTBuc2FRZmpTRmI3WmVYV09GUDJrWE1qM2pBWWU2cnVLQy9SY1hWcTdnSFAybDhSV1k0MlRNMGtxNDNaOFBQK3RiWDNoVTRrM3k5bjVpdHN3SjVKR1RXMTVtKzdHQlMzTDVSamhwOGRyQnh5cmI2TE1rSWpsaUViRWcrSEtqSThLMnlybDB3YzVOSU5mRDJzMGtNelhYcXQyY25UcDQ5S2ozR0QzNElSbi9VVFcwN3hvM1BaRE1DcFBtQXRKN1NSaitacGV1S0hIcWEyVmR5ZWFSZnJWMEE2RHR4dU5MTFRaSGw2QVNINWhXd2ErajMwbE4zcGJjOXNHSlQwSjFaWWlyV1NXWFZsUkdtZkkxcUo2Z2Z5clJCTzZhSFdRZG1VZTQxY29MaU9adDJadFRGRzY4MnEvaXNwNTh2dkxhM1ZBemRlWEttSUdlMWlvakZBZ1QxeXphVXdPZVY4NjIzYkErQW1CL2xVMHM1ZVZZeHVvVGpVZVF6V3pKSDlRMHVXa1ZleUcwSDhhc1E0M3VvcWpkM3IxcTczbHRlMm9lSU12ekh6cUM0WStFY1ZRemx4eEtzT1ZTT1c3UEJISERJeWZsVzBYRWNnN1VlNnpxSG1hN3IzRFl3UDRSajBCbWp0LzJVVWVOYm5xUm1wRGNXc1lIMW0wbC9hSi9FUHNrZkkxT3JSTDArMm4rMWJRekdqa0pxNTQ2VTNvL3REbWhRbmpaMnE5ejcyS25JTE4ySkQ3UXFiZGFud203WEhaOC9PcjJWaUdHRG5sVjJUaHV3dUdaaTN3NjFZVzhVVFNIVUpWOWNmRDRWc3BQcmtjQlcwQWszVWV0c0RXL2pwSEd0cXBMSGJTRlFMZGV3U0RqSThhMTZWNHlibUhMYUJ6YkZXUDEyNUVZOVhicHdjSTVaSlNEeUk2bXRuYkttT2p0V3F5OW9nbk9PNkI1MVBOQzBieVQzVU9qVklXMDhGR2VsS1VGdkxyMGJ2MTBNbkRzZkVjYWxudEpKRlpMWDZ5eHdXUFUvbFZ4Zlh1NG0wYjRRQlVaaHo1OU0xYjdtWlcwM0VERHVueEhrYWxhTWtEZXN5K3IwRDJmZlRLZERMMi8vQUtuOUt4eThhdG9IakRhWnA1SnRKdHg3dkRyVTRkWjRqMnl2QW1yc0EvVjFCd091S3ZuSzRYUGE2Z2NhYy9HdnQxRFlXMGJPMmlYR1htKzlSR1MyY3FPRlI5dzRsQUhJMUc3RSt6R2VMZVZiSGpzWXdjeGZXcnhJd253em12cFZhdmNvdWxQcVlsWWZFNEZYZHkwY1p4TkFac28wZW9qaW80Wks4NjJsWVdNTEw2bTN0TGNrb2c1RnNLZUpxNnVMbFlUd3UxWGRxaEhOU0R6eURVMkxtNm4xeGhsenBpVTlnRHcvN3FNMXBHSi8ya2NCQVpQTHlxWlpOVWVVa1RrNjAwU1FTUHFrK3N2b1ovZDRuMzFkdE5EdW1kREorN0k0NHJiVTVHNzdOckM1WDNzZFBQUG5WZ3F4cGJqSVBpQ1BPazBJZ1BIbnA4L09tWFV5OW5UNGp0RDhNMXp6Vnp1WmNZNHhoMWNlREJnUlZ1bHhKN0kzcUlQZ28vbHdxRllRaDVTU2pQNFVRejRycFJyWVAxU1ZtWXlTVFM1MThlRENyZmN4TkFabzVweGdPbzU0OGFtdUwyTzZzQmNJRmgwSVgrd0NldGJDbWtDYk5Fa1N0Y2NYdU9zZkNyQ01SMit6RXVycVdWVzBwNHhqanhOUEZZUlNvSklSTnAxT3A1SEhHb2xYdEx5WDMxRmJJSkl4cWxWL1dPY2UwS3RtV1dWREd0MUpKaUkvdzQ4YTJOYzcyMlJZbkNZdzJCekI2aXRnN3RYUERMNXgvRVBBMWVvclFOd2J5T2YwcTR0SnBHdThXU1BiYXN5WVBkOEQ3K0ZiUHZOOU9XM2x6Y0tGSEhyd0pyYU5tME1hZG1UZG5lOE9CQitOWG9rendCMDQ0VVZHVHhKUGNxVVRyZ2J4Z3VBRzVyK254b2VvbDdjWHVQbzdQalRqblErTmVOVzBzMmlNNGloN3plNm9yZ215dUU5WHRSdFlrZ0s5T2ZIblViM2wzRHFGdkJFQThxQmp5ejdJcll0aHN5RTkwM1phUi9sd3I2VWJNUGlwMmVjZmcxYkp0N3FGaDJtczVNOFB1UDhBclVNZjF6Y0xDR21YSWpVSGxwNlU0SlBQUEExQkIyVXdKNDN5V0E0WVBoZzAwNGx3SkluUWRoY2VQaFRjWUV4Tkk0MGRyajQxdFprUFBTRmJJcVptbWxsTFJTNlNSRnA1WjZITmJYT3pyaU9kdDFkdGI1REFqdTQ4NnVIS3gyY01LSFdlRFJ0a1NlK281WlpaRzFNTGVQaWZsU1l3ZTJPb3E5VzVpY0RUNmpNanVUeXg1VWwyMGJOaUcwang2eVJpQnhyU2JRdGd5cCs2YjlLSUlZZGtqa2ZRUG42VTFCRzdESk5qeWI1Vks3VEJkRHpGc3N2eDhmT290N2VQeGtsYXBLYXBhNE1QM2ljR3E5MXo3c0JSSHgzY2VkT3J6eHdvUjZtUGFNYVlESDdYeHFlNVdFT0duVzFmRFNBY1F2enFEZGljUnIycHNidG1ISi9BOGw4SzJ3WVdpd2lwdWh4VWVkRFhwNW5IZXE4Z2pjeDlxQ1dITzhROE9IbUtWdnFjallXYUhEWU9jWVlEaXRXVHpDVzRaWnBJKzlrY01lUXEwaml1N2FQVVM0d3pxT1lhci9jdWtaZU40UmxrNlpIblgxalJxT21XYnN2NzZpWFFBa0Z2YlJwbHAzZCswNXo3NmtqdnJQZUZYc3BKZTZ3T0RvZm9mS3BadG55ZjNWOUNRUDhBTU9GYmNzbWIrQ1RVZndxMmRoMGVWTkErWE0vaFcwVlNlVkhNRjBWMVJhMDVwdzYrVkNFNzgvOEFzUkVtT0RmMXlheVpweWNTSHc4ZmpReTFTWXJqOEtYR0tGUnNJenF1SGpRZnNGZDkyTlRmeGFBZmpVVzhIMmRlSzJZeXRCYnROSURMbnNMelBHa1JyVXh5eTNHcjFrbDA3RlFGNDhPWFNyL1FrYmxYU1FaMFk1Z0U4Umo0MWRxODd0b0JkdUd2VHFWVGp1NUZYRW01enJXT1RHWTJJN1NnK0ZYN3FBMnBJUkllQjhjVjlJWk5uWEZ0OWpnMXl2UFBIcjQxdEY3dTR1VklLaml3WHhPS2paUTY2UjR0MU9LbWQvTjZZcXk5MHJ6Rk9XRUZuTkhhQlR3RWp0bmVOOXFrUmsrc1cwS2F1eVdERDFzaEg4SnExU1BlU1hVZW95NENtTHVrNEhKNjF3dExad1NSN3JnWUxnTjIxYjdTNHJhaVdDdmROUEdvWE1hU0h3SG5YN3ROUjkvZC9PdVNEQzRvMXpxK3RsZjdCblhORUE5TzFYUTB5N1JqaGJWb0V1TXV1ZEt2MXg3Nm5pYVdJNGtGdnhVSHd6NVVyeGwxTVozYjZTNm5oZzFMdWhBeXZtZmd5RmRTdWgvZ05TWEVsdWlCRk1zVE1HSHZJNDU1VnRCVGFpRnA3NmNuQ05wYlRER0VIVUtjWnF6STNZeHJZYWRWV0thdkRBSCs5Yk9TM3V4N1NRZ0UrOGluSlF1N3k0UGFrSlFxTW5xQnp4UzlyUkhxZCtKMXFNRXJqeHBGWkR3RG9lSG90WDNXVmQxSXhxU3RuN2kzZVhWQ29ZOWxjZDM1K201dDF5QnEzNDd3enlIblhqR1B4Si9MMERKTFlGWm1jTDIyNGhQNjFhcFpuSHFveWk2WnZ1c0J6cVJ0SDdsMi9kdCtsY2ZmekI4S3YzV0c2ajBYSWM4RzhEODZ1cktKSlZ5SWJaZld1VHhabStOQ0o4ODFuT05ZNUVEejYvQ3VObnM3U2l4NTRQTGppVDQ0NVZzdHQ3SEpxaWxqY2FwUmo4Qnl4VmpwbFlkdUtVZDhkQ2MwdDVmVHpzWjU1WFhFYUZtNERoV00rQ3Rta0lhTGk0UGg2TnJKRHVyY3ZHN3AyR2Y3SjhxMDQybEN5eS9lOWx2Zi9TbHlFUEdvR2N3Smd6TkhqVC9ENTF6b2VpMGhsUFAxcHhwNDh4NTBPQkNuL0srUCtxdXREUHJBT0ZDTzFBVW1HWkorMThSOE9WVDZmcWhMSTJPSXgxK2RXckdXZTJWaU9nSkdhd295RzBEekZUbmhJRHc5OVhFRzhKYldKZjJuZjlueXFlMzVmczVGN2JmY3BkRGI2UjVGbDRjU3g4YXhIZEl1WUxnRGw3bUhQM1Zjb1RiU2J0WkMzTk9sYlR1QkVyYXR4YURPOGJvRDVWWnZIanVucUtrMWE0SFVsdXZEK25vWnNkTkpvckRiMjR4dkpPRmJidExtS1FraVBmWVllWEROYkp1c0QycmQxbEZPcWtjMXViY3htclFrZU1Fb2FuMC80aWtWQjlZR3I5bEhMeGFvakc3REdodloxcmovQUpzVW1PdGNEbmdhTnZhN1JSVDZ5Nm1ZSkp4OW5uVW96Y2RtVjlPQUIxeFJPNmlBQ2p5SEt1ODcxYjY5MmV6eG93NUdDVTBkcGV1UjVVOENhbHdyemQ3VjAwK2RiS1I3bUwxT2RYUEZXUWdSV3lwUmF1Wk8xREdHVk9Xb1lyYVp0eUUvYnNNNmFubGxJUEJ1bGVGYy9SbDFUbmxxZGtPZUJRNHJiODJrZXpNZDUvT3RqVzErdjNNZnJYMEhudFpEN1VIL0FHbjhxbjJoR3Y4QXhZdDR2NjA5bGM0NmoxYlZzKzZnUlJoM3VKdGVvbm1RZkkwT01uRTQ1YXZhSDUvSDBJUGpXeklKZzNWMXpWbEhGam9xMHZLcDBZbnZ1WXdEenhrNDZaNTFjR0cxakliVkkzOHZFVmR3dmNRTkNFRnpGM1VFaFpueDdSd2Z3cWEzdUlZZ04vdW9tVmludE5qL0FNNFZjMnNQWTdoaC9NVnM1WnBwSDE3OU9aYng4dUZTeFFTNmpvM3E2bFBYRlhVcW9xNm5ZUmZsV3dwTG81NFBlemRuL0tuNjFCQkVNWVdPM2hDS1BsNkkyNCtGSVJWaExJUHRZNFY5SkxLd1grN2VRYXZsV3dwOXBUQWZ0V1hRbnpQNlZCWjdPUThqRkJxWWZGcTJpMjhKNHpJY1B6enpxNGx1dFEwd1JrakpZK2RQMlQxK3dSeWI4alM4UnpIajVpdUJvVVRqd29Zb0lQV25UQW5IVG5CUE0rTlIzTzR0UnEwd0VZWHp3ZThmS3AvcjloSW9hTXQzSlU4dXFuK1ZXazhzRERVckNZS3FuSzR6bXJrWUVqb1ZpNHBxVTRiQTZmRGhSSklVbkxEQUE5M1dwbVJmYUdRVlplYXVQT3B4R2pERGFzc1dxUHNvT1hoVVRycVBBNDRHclNTUm01THByYjlsc3hkT2RFOG8zbitRWmF2bzFjN1puWGxQZStwaS93QXZFbXRvcnMyMlA3alpzZTdHUHZjNnYzbWtQTjJZdWZtYVFlOXVKcmo3NlB5cHprY2pXN2doZ2plV2VlU1RMTzVQU2tMdzR5bWp2Uis3eEhsVGEwUEthUGw4ZkNwS3hSejVDcnVPYTVlWFhLVUFCSkwvQUtWZmJ2Y3ZvdVZYSUUyZU9DRFZyNnR5R1RWMDFqdGZqVm9rNGlBWVF5UGdFOFJuNVZkSXFpT1dOSTFIQlJKM3NlZkNwUXJieldDdk1IUm9QekZFK1ZOMXBnWWgzbFhyVmdra2FOd2dtSkNkcmg3K2RiYUZoQ1lDenc3TUc1R2dOcE9jY1R4ODZtYlVKQ3NqUHg3WGhtb0I5L3ZVZm4vNkJnZWRHa0I4MnFUZXpYTjJtV2M0U0pNWU9QQ3JyU3MweGo2S1pHWHcrMVZwWnY4QWZSbzIvd0JQQ3RtMjMvNjNQL1RWdWtmK0JGcC9FNU5XZWNTQlJwY0FrK1hqVU9pY2QwM011bnRlSHZxZTNXWFZ4dDk1MnNlT2VXUGpXamVTTm5kTzJNcVAvTTBmaHB3UFIxbzhobXU4RGtIdzZnanhxME53L0wxUTRmT3ZvZnZGUFRmWUorWXJZczFpUzdzalhVZloxc3VuT2U2ZUhqV3FXTmR6SEVOT3BwM1BNK1ZLUmp2cG5MVnNXZHRQN3hUbkZMZzF5enhvOWoyZFMxYmZGRFcwRlFucE11UHhwQkluMjRtRGo4S0p6YnVXaVJ1UUo4cWVUZDJ6eUdWTmZHWFZXK2tNVzgrdFpJOWFEM2ZpS1ByN1ZYVGVPMmRhbnhvcExieTlxMnVIajdlUHlJcThWNXJlVEFsa0dkUUhGU2ZHcmczRzZWUVpTbkZqckpiL0FFa2ZLczlNNVBQejh1SHA4S3lBbnRqSEE5SzdDY254L0trN1hWK3A5Q2hnUjJsWVpCcGNSRHZ3ZEZIOFBoN3Ewd1dxSU9XUzhybHVwcmFFc2VYd1UxY3hVeFpqenE0UkpVWFVzVEE1Y2RlTlBtTitBNCttUmwrNmFsN1RSK3Jaa3ovdFF6U0ROQXpzTzkydENqODYvOFFBSmhBQkFBSUNBZ0lDQWdNQkFRRUFBQUFBQVFBUklURkJVV0Z4Z2FHUnNjSFI4QkRoOGYvYUFBZ0JBUUFCUHlIQU5FRG5Xek1USnBxdzZLdm9oN0VIV29yMXhNQXRwT2FYWHpEZzZ1b1ZtU2lDempGeXl0OHowSlgwOWpqTXB4RCtKVnFWU2xtOFpsVHdIVFVMd1B1V1FHMjdyMTJHNE9OTHdEZmFGVFV5WDlmY1FFUEk0Z3F0dFp6bG5KTGxoeG40VTdPZjBsUTFJVHZLR2lhWEZPeVZpdVpVV2VEYnlaUDFGVllueTdySEtkUmlSMnFsQXVpcjRnOEtDMXgxeW9PbC93RE9nRjZQREtEUlh3WWt4TTVTVzdrMjRmSHhCRGVvWGh2eGRleVd0dTFMNDNCZmNhL2xiQTFnTWRTb0I0RnYrSThDdUZwWVE2VG1hZEY5enQ0NmlDdThjRXF1dFZxQ3dpek13SkpoZ2FUaDhrZi9BRlhHa2RVM1c5ekxqS201VlZaUVNTOXV1amtQZTVSOVRSR1B1ekxJS1Y5UnRYREZCbnI4d2NXWDRUVmpobm1XOGtML0FNUFA4U3BkUmxPaWVpYUdPMmsycmc0LzRVbFlQZmU4QVhBcVVFdDBEYnhGYmNzeVBBWS9VOC9jck51ODd3MFg3VGpZa2Y2Tml6R1ZxQ0kzWkZsTXZ6R3RjdndKYjF1bW9CVW1CeFl4Qk9hUC9hKzJPbVZWRTVlZE9IOHlrbmc5UVl5RDZsQnNwSGltY3BzclBrK1p0MkdzUWNyUjIzWk1KOVJMcUdISjdoQmRYb1k3czI3OGFaU0hsY1NqVytJSXdER0dmSTZIRDRueTQzL2RTbE52TkgvTW9ZaXJ3TnFPWm85dEZWcEZnVXIyQ1lITHNocElMVVFDV1VuaUN2Yk9TN0pFVjhzdkw3Z0hXdC9xd0src3VuQUVWNVowdGc0MzlSSFBpWWdrZ2ZDdzV1RjYxZmhjeFpxK2VBeWl1akNwVlowNHFFQkcxRzR3bW56RnJiWmg4cEhxNExxaHRTekpVc1VUYWJQU1piSkJMbXdjVjNyekVXZHZTSTFHdSttdzRxZkJSZ2IyRnkyemJDZk9IT25Vdk9zSXY5N0NybGozeW9INHRMZ3pEN05YS0M0SzhSem90SExWWXpwdG9KeWNDdUpUSE9OYzROTTJEM0QyVWVqK0kvMFZmYnE4OVgzTmNMN05ZTzZOSHpBYnd6cVY0UU1YeUVCWlgxQ1hRd3RrRFBEOUsxeCtlcGw1b2s5cm5wRUJyNFVTK3BpczVSZmwrak53RHpLcTdVdVcxUEt0OE9WOXNzL0FzbnhIUFB6S0h2ZTJoVnZsYi9FQ0hvdXFyeFIzTWVIK01DbWxXNnVMYjBkMTFRTHIyOHc2N01pSEVjUkE0M0EveDB2V0FGTVlZYWRMU0NhbTZHWDMzSFZaMHJiZFZ6RkNPdTFZczduYXJOeE55Qkx1R3dMbnJOemdsc2x2QWNVL0tZNlg2NDN3OURVYkIycUNSeldMR2VwV0dsU3VpNTFmaEdJdzV0UXlVZUV0M00yRmdiTFN2KzhHTGRVWXZSK0thWkcxS2djTEs3T3VpOS8rekJuYXQzMG5zM1RpaS9NU01YTERvYUdPOXpCYlk4Vmp5akpKbUd0ZkVBUitEbS9aOWtwRWdsVnpSZk9vN0EvMk9aTGVhdWFpR0E3d3RFdGRZbWc3bWhGWFZ2RE5ES3FFNm9BRERkTXNxOGhuYUIxT0l0S2RVN0hLdFZxK1VGK1RkbGRoQy9EcURteXpsbTYvMm1Yc1krd2RURmdPbFRQSFJ6U0tGMWpOSE1yOU9iN3M4SWNvalFPeU5GZHF1OSs0cFQzdlMrMFo0UU56WlA4QTlOR0FOaGpiS2plQXFhU0ZPUnJFeTdtN1Y5RU1lamx1V3NOWENlNnRHYWl6V1lCVjZTbXZxNzlSbEFiMmZCOFh1SnlJemNKUVY3MFFmazVXdHNMR2Qxb1lyL1dMY0EzOXc2SzNQY3RQdFlNUmNCYmNyVDVxYmp2eEdULzZuQTNZSzhXYUt3eEMwL0EycThGYnZxNWRtcEU2cXpsd3k0bzdUdjQ1QzU3aHp5ajZjYS84NGc3ekVsYVduR0xmZ2RrNDNjWG5tZ0ErYlkrUkllaG8xWG91U1ZTRlIycHpWY1pqWTdzNFBKek4wUlBHZVQ1VUVaZEIrRDJPSW1HTzRjM2NKWW1YUk81UmtEdnRNcFZ0ZzRnT2grWkwyNStKWFRTYzdPV0ZGdzlNejRKWEFQRjR0MU9kUjA3dnFpZUZEdCtRTWFKc3FyNWJ2aFJNcXhxMk1wblZrVzRWbTNndzZ4UFRqMlhma0RFaHk2TEg4aXB2RFVWMEdxYkpJY2s4Uk8venlKbmtOeHVlR3Rvd1hzVlZYbTVVcytKa3FqMkw0eEN3VDg3OGw4OVBkRXZOTXRiSExSbERiR3hic2xkK0lQc1BkQmFXcVRzalVleHBRR1R5VE0zVk01YVZyazc2WmpyU0tzZXhsMWcwV1FpelZtTUhyM0dsQUg3bG5kblhseTdYVjkzQzNSVGw2dDEzd0pRNkczYjhYbjU1bEl2MGY4aVE2MXZxWFU3WDhGa2U3N0lJeGhkTFc2aHc0aW1vckIwMHY1bTdmWVlCNFJKZnVKdnZRc3RwVlBtRFY1WmtJZnFtb3NaeEtyemQrNG11aXN2eE96TVFHWFdRK1FEdVY3Z1JzN1puZ3h4RHBURjJGR0hLWFl2VTJVS0JSYTByYlhnbEZzQU5VN0JwdnpNNUpKL2JHR3NpYW5Vd1ZEeFNiU3FsNytFZ1VlWWZwclgwZ1ZsZjVEL3d2OVk4d00yNDdndHQ1aHc5eGl2bEtsR0cvYzlSVzE3SlMrZlpUSFI4dmNISTFZQ1lHdCtvY3hDNm9TK1lvNTVUR1I1TlRxNHRINlg3d21FMERrRjNiVWNScERoaS93Q2NGN1c2aHNjdnJtMUlBVGtCelV4Y3J5V05MQWhoZDNGU25uUU5RWnFtSnV0RVoyWW1qak1NK0FTMW9MVlI5bzl6UXV4TFNZdittZ3Y5Q1c3Snh1U0t3UVZVaHpmY0JWaStIcXJoaWN4cmt2Ym5jTEF0eDFIS2Vzd1RVUEYvTVQ3a0R4cWNvSTRycWU5MnY1VEtoZGpvdlF4N1RLY1N2OEF3UUxQMG0xaTBWRGV0d3kvcTRzdCtHMHNjcWxZSXZUZVBsRU9UMmhFUkkzUmI1clUrcGFzMGJBbTVPeG5JMUJUV0RDQ1poTUR6RnV4MDVVOVRBMURGYjdVc3pjQnA1NWF2QXdaVTV3Q2UwZnlsU0F2OC9ISkIranF3N2E3VkZjekg5bkJ4M2pnM1NOREd5YS9UMCtITWJyMHlwbTFHMWJzdmhPWVZ1eHlIMERtdGk4a3ZiUHFPMXZjUWxpRmxndU5DRmhmL0FOdUVKemR4R2dRSExjRFRoSWNaQk0rMFphVllLczRUNE1ZbW83UEFXOG5IaWFGRlZyVjNLUHpHSjBlendkbnlxNTR5MU1HUmswdTRkV0RicGREeEdrTExjeURMZUVvUE5zV2xxS0lXRkRCeVozT1NGaG90aTVjM3pMaXZiNEUwWndSamxlVWxIODRtSWRBS3phdjRmM0JEdHFaZmp1OFhudDJSUHVyZHNvWVhvbFh5UVF2OGcweXFDSERDYkw4c1lnazRrdjd3bG5WTXBRS2h3b2tKbnVyNUgxQ1ZuR28yWHNvVmMxQ1NjSThhM0k4bjFBRkxrWXJCOEJocnR4Z0grMFNlZjlsZ3VmZHhJNDRNZTBRQ3I3cUlENXN6eHB1VkE1NlZ6NFllYTYyK0NzeTFEUWRueGY4QVFXK1BVY21USDB1bDlXWTl6eFg0R1Y0aDU0bGJzaTErUnhVdnB2ZjBmN2htUUY1NVpZTmdyQlE2bVJaSnFodm5YLzJiSCtVVnZLaDl3aWxKL0ZBMzlSLytCc0JxR1UrMzlSY3pZcDNwVS9aSDArTUNGY0FjN2gxU0xwYm9ENkVyWnpnbW1ZM1d3R21jZnFnWnRWVEozV09waXVkRnRjRG4rV1Y3VHQxWFErbzhxTy9IK3FVQ1NscUY5eWtTVzNlM1o3R0dYUDJKS2NENFMxNUpuc0RZeE41N3pyM09HSkwwKzR2MVRCMjZ2NFl0bXR5Tyt2OEFiaVBIYWxIeTN4TFp1VWZNVk5yNlF2YW0wMkRaWHd5cU9XWFI4a3ZWY0xEOFhuWW1odjhBdzBoYndKUy9paTRWaXdFdm9nZ1dGeWg1NC9jTk5zNXRSMXhrSmVUaG9ham8rY1BFUFU0ZnhNcnlPS1ptSmFHVys5VGpUaW5GOUVXUEUydDRwSDFyUkdmRWNJTFNqNVVRamQxRkJvajZvUEhNSzVXTDBOQzMyMW1uTlFpeUJGVWxZUktlN3JNb3FCWmJPQlljd3Q1dVlIdXJrTnZzdUx3MHdhcjBXTGxCRXlVK2xmek5DaW9KUFczdHRsYkcvRTBMTk9lU1lRcndaaEc3ZVg3NEpRb2Q1cCszNEdOcXd0bTcydnhIOXQ2dStmVWJnVlZrdEt4UnRncDBGaEVBTkxuSFNHZHVOOEg0UFVvSmFsd2ZzR1dpb2o5eXhwYStDb3QxQWllVTJsVjltT0lIa2l5NzliMVdQRTVuamszbmlOY0tNeDNoSHVSSFpTdDV5VnpBMHFibTNDR2p1em5namVrTlVpMW9yUWo2anA0U1kySzQ5VGl1T2lvM0dNNmtIQ2YrQ3J6R3I5c3EvQkNXM25INFAxaENrSVZ1dlo5bFU2Tk5BNld2N1RmQWlUODBNa0wvQUt1UHFibVgzTDFKQmxqV0RrZTVrK0U0SnlPZDVNeFhGc1pFNXVWV041WmVPend4aXpsYVlvd0szY3U5cjJ6T1BOQ1FyUnJyM0E0NC9mcDRWa1Q1Z0luRHltbFE0c0RLWnc2QlF0NFgxTFBRT3NsK2lvMzRsZ09vVjZRdmdNa3R4V21UQmRUZ3NldkF5Z0ptblBMTGZ1cU11bG9sS0d1cFZUQlkweGZsRlpLNmxCc2d2eU9lWG1MS1ZGWlBMNmlGcXhkekR5Z0JFQmF0TTVZczJuQnMwZmlYNjlIZlJOdnFXbGk5MlptaloyOVFtNVdYOHV0OUk3VlBuNlFQM045blg5cmZxWFJUMXVjQlIrRXhQUVVuVWFQN1I4b3RPMUpqRnE2WHhMeVZyZWZ5YmNKVHFnNXJKemlFL3BBM21aY0xyd0Y2clhBVzU2aFZxNEdCbTZ0NTZoSVdPMWcvQk9hQzRmN0dYQ3NORXd0aXpLdEpyakF6cTNuQTV4eEN0YXk4ZTVWVE5HR25pWnJPaUZMS0FvU2ZoaVFxOVA2WWtjaG5YNFJlZkYvMncrby9ha214cVcycmp6QlB2dDV2cGNUVVhkQm4yaHI1Tzl2dkh4MzRod3VxUm1yUGFLc3hpeUJJWGNlcHUyRXZjZUw2cWxjaDBEekRUYzVkZ3NBSEtsaEhGOHIvQU0rMi9jZHBzK3hoMllaeHdHaGQvcDNCQ3RNNWttYmZQY3dLbjlNRVppSXQyK1EzL3A2cVdnSWFLYXRUM05ob0g1di9BSlA5akxxZWg5TlZBSUlkMWNMbHVyd1RaWHpLUWFOeTkyRGNiSmZJVE1OY0F5UE84UGNOVnlkeTV4L1ZnQVRuKzFzL05LK0NmLy9FQUNVUUFRRUFBd0FDQXdBREFRRUFBd0FBQUFFUkFDRXhRVkZoY1lHUm9kR3h3UkRoOGYvYUFBZ0JBUUFCUHhBL0FGZ2VONXgzNnBnVHlDOWNWTG4zWkRyNUFjU3ZOZjRnbDJTcDVyaWwyYnEwaEd0VUh3QmxNNFV6YlRqZTRHa1Raa0x5UE9DOGd3RlloL295RmVVTGJzWDVtRUROWC9XSlRlTUZEQVYwNFk1NkpoMUNHbmdoS01PS29LMndqNy9tTmZZZnJsV1VNRDlaRmVicnYrR084ZmI1TnpJekFlV0JuVFNoS1NIZGtSQkdLK0w0S1hqL0FMaFJDY1k4RmdvKzhSRGdwK05YT29leVJPSHRNQndVTGtseUhkb2NIK2htbUtTcVJOZ3BqYjdRVTN1RnRoazBYMjRRNnVOVGdicVlJc1BSU29mRnp6bUVlVEd6MjRiY0NZZ0JGd1ErUjNEckd3QXRxTkVOQ2FReXNIYW5JM1dMUXFvamluVThnWURhYk9zZE9vV1p2ZGRUQmJxTkxVS3hnaVBOTUdpcUJaa1Avd0FUMWkzM1FBYVBveFY4S0U3aVowZklYSmtlc2E1ZG40ZzR6UElEWWhuTkNJQWVUTlVnWFZydWh4WFpuaHJsdkF3WFNMMjdjSm1Lakp3Lzc4Y1RKZ0c4UWNuWlVZOGJPK3NDUU8xSUdRQUNPN3h4bUhYNWdGbXp3WEdVb2VoaEt4R2tUdkk0Uk1PenBhTUxGWTZ1Q0ExcWhXd1BHUngwdmU2WFRXZ0EzQ3l0bWtBRFE5SC9BSml1MWdBZzJCQ3E0MTUyWC9GakNpak9CZGRRQytNb3ZEVXozdUZIb1FLWWxFa0kxNGpFNXZ1NEN0d2tycTdERTJoR3VHdXA4ZjhBekErdEtuUUQ5L3BUTnVITTN4SUtCTEdPR2FkbHlUSmo3RUlURFgxa2R5aHgxWnAwQW5ENkFieGFneFNGUUE2VzZzWlI2aWFTK0NGT3FCaS8rZW91bWFycHQwTW4zY0pJYXE2RXdRREVjV2Z1c1RXM0NoMWFjY2hESHY4QUo4Y2JmSWpYOHVXRHB1RjhUbUl5eHdXWGJydDdZTlBEamU1RUU4MHFTOTVEdWtJQ3lVYWp6cloweHZhQWxmSXZLa3dMbzZjdFVCSVI4VmdYKzU5Y2VsNFNEZ3N0U3ZDUS9pWkNqc2crR1FDa0NnSjRMNXdTbnYycHFINHVSRFF1ajg2YWZJNVE3QkFTK0VmU1lkRzdrdjNEdno4elQxTVRUWTR5ZzFCeFVxTXE5akVtalE2WUFvUXdpRW9uVWxVQ0pTaUQ4WVBTK1dWazFBOXRKTjJHTDBhNWlKMU1RaHhKam02dnBEQ3dYdHRYRnRJSU5QOEE0ZVR6aGVUa3JjZ25uTnBZRncxR3NmNWpGbTV1RE95RFd4bFRqbHNaM0xuUGtNVHpnOTQxdzdLQ1FubGFrTVF5UTZ4NnIwenNVeVNpUXBPRVh0MDl4bzJFYnBNbkFBWHNGeW44anlUUGJZODNHRkIzUTNpSjlpYWpxNXNwNEZxYWpKcGNNWjJsSk0xZmZmRTZPL3dQS0lxN01WZzVKYnZXSzBRRWc0NE5jcFVyOGlzWm5yaGtCMWZWbkFPZmJaaHVtelQrL3dEbUdaMk1FUTRha0dEbWd0NlBlQzBRN0J2dkpiUlU2Z2cyR0x6ckZvdlNkaFc4dG1FeG1PbGE2QTJ2WTVkS1B3MmhwM285N0Jnd01hSXlsaVNGV3NWd2lHVVdzRkdVcDRhQmd5WTdwNG1Hem5mb3g2YlVwMnNNVVZTcHhpYzd0RTNja2RrclRiQkdSckVsTXE0bFF3NUJpaFNnbUxuMUFBSENSWGxHNEdhQ1VYQnhRVGNRUGdoSWxKajFCV2krMHdMRUlnQlNaOFlPSU1xUGVvSEJLVDc3K0NLOE9xaVNFNTNLZFZjSjZJT0pwdGU0MUlkaUVGT2doczhZQk5XbzhXL3NzeEcyTVduZHJ6V0I1TkpOK00zRjJBZVpnMWFZN1VTUGdmVGpVRG9MVTBHZ3ZqTnFzbEFuNHZnL01pN2dHTXFweTVoQTBodnFoVGEyMXhwdXZ1SGJyVVNxM2huYjM4dDZieUJVNHJtaUt6YzVuZ1dxWExHdEsxVGY0bzRUQ0tzc2hCU0dtd1hFS1FKYU5WZVVJcVlVK00xUDRhaVdEc0l6ZU1QQ3RjeTdZQkFxZ1lNYkFxcXFXekNTQnd6NGZEb3FpMzJIeGpIOGthUTQxVlhkUGNVNFNFOUJjaWVFcFdKaVZHNm9vd0R3RS9reGphdHFlcnZEeFlSZWhRUTdBbDJkY1ZscWJiK3VkQ2czYUZqNU1Fd3ZnTVdhUUxHOTJ1UjVVK0V2MWptUUpDY3p1Sk1TUXBBTmdKUmNSTU9uVGE4bWhEVHZHMTFpaWx5M2pURXYyaHJZZFFrQmZGb0dIeldTMlFwTGhQVFZBeDAwUjJVSWpKaUt6czVtd3ZzdjA0ZitteDFjNENHcHZ2RjZ4ZFJtVWVvSXQ0R016UllCTmNJaHM3c2dvQVl6cGw1c3REUXVjM2J0Z2dmQ2FQOEFMQXZ6RDV3aXl5ZEFtd3JKQXJjM0VBcjBpQUJpbVNkMUtUUTVBZE1TMkptWlFwSGZ4OURqcnJpNGwySHEyY2JNYTNJSkZRSURacWVNSU1HdnZXdnRJK01yemNRZUhIcUM1TEg1eFpWcVFNSEJPSmRmTGsvMUplS2s3NHVhMklYU2FCcUVDamV1TTJLQ1VMQU1QbEpUUXVHTEp2QmE4Wm9GdzQ0YVZBc09vb3htZVdKSDU5Rk1jOWhONjJTaWRNQXZtQXh2WE5zVGZqV0RYTHZTeDlKVk9qYmN1RWNCNWhzRy9hSmkrNk12UWFXUENJVGdZdjhBNmxCSWdTN0dTNTVKSXc2WURZOHdBc3poT0JnRU54azRSS1EwM1lGRWFKWWVYdHV2Z1E2Q2grRmx4ZkZsL1dmVUxncVQxcmp0Tm9qcXh4NmN5QnZTVHFQTVVTWXUrS3Z5QW1FWFBycSs1blYreW1xWVRIL2NEemdjWkVteERlRWhYNk5QbHpWWGNmbi9BUFdialpWV3YrUy81akJWUnlvWVVmR2FHUUhJeGplcjV1M3MzOHlUMkVEYnRSNkM5NnJlTnZLWFlCanVPcEwwTDhZRUhFQUJGdGlBVC9NTXZ0QlNGVDRQc2Zab3h4QUE3MVhWTWhZOXJyYm15SGwrUUg1V2pPTlF6SjhVQUlmQ3hlVU5vbzlvWVVEWW9Yd0kxNk9LSmdDdVBMMjUrKzc1TVpZRVVRTkpJSk1hQmNQOTlpOFMxU09lTnc5N1h1Sk1vS0d0TndrYytGcENzZGx6VXgzWm1pQVBKUVpiVktaclJtWWlLSnVzUGtNeW5qYS9IODRvN0hCc3dXWDJ6VXd2QkpYRWF4SmsrUHZHR1BMSU51K0Q2NG1YdHdlNjlaeSsxZWNIdWdIdWNheWFSYXd6ZGNqcHlsRklEaENxTG03cUlEditRdjFNZmk2TzdHd3c5T0dyUitaR0VBYW5Nb0dEdm1NVkp2Z080czEwb1JDUzdXc0ZoSFZwMzduL0FEQXdxRDFLSUsyZVQ0eXUyUzI0aWlCU2hTb21GK3VLUjNub2JhYThESytMaGZsQkZPb2kzWk9vbEduZkwxYzh6TlFsdjlJTnNLTFNzTUxLM0FRWFQ5aEJRRGpjd0cyZWtoMFZobGU3U0lRaFlzRHN3Sm1vNmNoUyt0WXY3UDdLaERRSmFaRWNwV3J2TDBuRzRsU2U4YUx3QUZHSDhvT2l0d1ZnNlRnNFpDY1ZaZTVZVGRJUFdRanYyMkJPSGxTWTh5ZEYwMmg1cmpFNVBhQ0tIOVlNbGFsUGQ1b1FrNmtQR05uUjliQjJqOE9KRGxJMWxZa3kzQm5oRDZ3THhQUGFzRFBFSVRDbSthaHBFRlJkdHJibE1WcG1UU1IrYUNPM0gwUDVFU2hzRUxXQ1pIVnVDdG9hb3Jad1NjRTJxcGF1ZVpXSWVRMVdhSFNoWVhZZWNXdXdDZGROOVp4R1JMNmJuZG14K21TcVB0U0tza0FTY0g0Rm5qUjAyYVBjZVlLc2dvNmhOMGdCMG1LQUFDK3JnbXkxN01FS1pnMCtNNzBiNVdJdjlJSE03NVBNWjQ4SHJCT3ZJZndCK2pUS2hoQjJWZnk0TlBCME03VWFIU0EvbHhXUHpZOUJ5Qng1N0RGQ1RSQUlHQkk2eGNaNzFmc3M3anNEMnVEM3VxeWN3Y2VuZHd3dTV0QXJMdURaMEdUWmJ6UVUrVWhZVTQrWkFxcDJlNGZER3dwK2lpVzJEVnBmWXhhY1N5UW1sMWhvZTBkYVg0d3BCS0tBRWFHVm9EblVxQkREVXhMU0xzd2VRU3Z2Yms1RGJVelora2NUcWkwcHZBYkV4UmxxZ0c5MHcwbzI0dlBPWFBZdDhaR0J0SHFiL2FIODVwN1hHOHZLQkdMdlpqWTRmdFBuQ3dVTS92QWRBaEdtY2QwZUN4UjhQQkZ4aU5XeWFtbDZ3Y2FpS3B4RS93QzRuZDRMTjVPRXo5UURidkVFdGRUZVdJMSttWmZvZ253cDV3Q3BIQ0liVlVFekZhMlBJNnN2ZUs4dmtvQUhXT0wzRGg5Zk1VWDNyRTN1RGVnTHpiZWJYazRiWEZ4MG5HckNhUkpoRCtyMVpyQkY4dFNCdGxvK2dObHZVd0hrWXR5SE5ZZ1ZBb2RwaGhVZUNDeFNBODVmc3lCQ0RTcm9MOEJmSmprdUIwTGp5eW5SYTNSczdoeTVCVjJpc0JkQjdVNGp1cHdXVTRpeWt3M1B1aHA3WGZ0R1BUdEZMK2hZS2EzV0tQVFcrcmxUU0cvdnlZT3NIcWVsaEswMU1jWUxhcEc0dDlNV3l6RTN1UHhwait3QkNsc1FkS09EUjJUMkVNRlZVb1ZyQ0lGckN0b1JvZ0pSV0J2SEpkNjZCODFRWEhmY0R5VHcrRFRESzh4b1VGL2x4S0lqYVNjTUVQY3gyd3hJOHJPR1IxTXVESHpWUkJTVnRzSndvWGtZbHJhNnpGUHl2cmx4Wk9tYkhzSkRJVjh4cy9sam1mRHFMNVZWNDBURUVUTVBZbi9MaE1kVzVhbjM2eU5iVWlNYmh6QU9rb0t0UFhySGlyRE95dFJPR0dsc1Q0UlFZZnBnRlpnc3g4ZjhzeFJhNldYMmcvY0QrMnVJZy8zbkJpTlIzRFpXZzZ5NjJwbFdxeU10OFoxc3NqK0FGZlE0MElVazhxeW9XenlIMlBUQ0wyTngvclg4WUJuNW8vZ1pUVGdEY2ZPTEwyVXBsV3BFUVZQT0dRekNkUDVyVVZnd004Mkw3aWltR1RmSGcrV1h1M0lrNk1DTXJubHRpaE9Mb2d4RWVMS0sySkNlaElUUmlCSHZXOEFKb0l2bDFrbC9rdFk5a0lGUU02eWJ4eitlOGlZd000YmRkQlZiMmllWEk0d2tzTC9PT1hXSWxUMjh3bFdqWFgrR1RsNklZZmJqL09CRVJmNGYwZmpKQllxSng2c3ZvelVHTTc2cmk1VU4yc0FnMFZOeGx4c2RpajFIUW1pWGVWOThEa29JOUtuNWx3TzlkdGVJaG9HekwxOFNNUjlQK1pvb1NMUW51WnA4azFRUG9NcERFaUV6UzMwbzlTYWo1d00zRkw3dW9Gdm9EK0NyaG1ZaHpXRzJ6cmRxQTVVb0FlUVFKcG9WUUE4RUpjc2dMMFhTeFFYWk03bktRME1GbkQwNEttb3pWcU5FWWdVRENabE5WTys0WHZnTVN3NXNzcXl1eXI3Y2MxWXZBR0toSWRNNWc1Z1MrdmtyK0dCQ0FFNiszWDRjMjlWaG40Z1JZVytHQWZGSlA2ZU82dXBHcjFYL0FGd0tSY1NuODZmbUtMRDhUUjllc1dZdlJwZ29IdHdEUUpzUktKajFpYWRBSnJSQWljQWNueDV4dllhOW5qRG5kR3hneUhRODRPM1p3LzNEMitJR0tCVzBOSDdpeTJCK3VzQXRKZ2djVXhhRUdscGk2TFFVRDZIdk43aDJxNmhIb3VNd2Z5N20vUEI1MHdjVTZhRVVpTFBsRGo4R0ZLQkZ0aERLTHZMNW9BajhaS3d0MXpUcmViMTR0U1JEYUFOc0hVeDZsR2I3bWp1c21pNHNhNzV5NFdHYVlseDBSdW93M2FUQytBT04yYmtZZUM4djQ1b1B6UXRIMEdqRWNYREhhLzhBd0tSTzAwcDdEemlROE4xMGZ4aXdPWWVoR2ljUDdjSU9tSE5vUk9hQWV6bERaVUxKMElDaGxjRkRCR1ErVmRseDRiMWt2N3o5UmwxMnRxVUh5MzVrK3h3MWF3Tk9tTG5nT0h6b2RWVkNDMkE5OHVLc3QzcnlnSjhXS3RNRVRXTTBPaUd5ZUExNkI2ZDRTN3hVdFo1REt2NDVyWnpMYVdRUExoOHI1UjA3S1FwV0VkeWlYWmU5UzFnUWNIRTFUdDRpWW1QMndYT0tlU1NyWUZERzd1dlowUWJOb1lsWXg2RXpCRWIrczB2OC9HM1I0RDR3WTZ5OTkrT2JqaEllcm5qQVNCR2dGQXk3NmVjLzdCeGY0OEQvQUNUK3N0UjZFRDVxUDNEc0tKSTY0VUpYRVpUQnNJRG8zUGdlSmtHdmo4a1g0dUFzTlpiNlU1eURXTHBLVlE0cGNBcDczZGZOS2hJNk4vSjhkQnVOcTRDNUtSbzlaOFFMRW1LdVNDT25PS2FVSXBjVnEwTG5IRS94K05xRk5WVlgxOGtNVjhOdXA3QjlFYjdVT0c0Y01DdjArRDBZN2dFb0twdk5NakVtZWhJbUZySGEwbFZLazYxNWJPbG14WHVIQ1FNVjRUQzcxbXJVSWsxMVE5RGVGTC9xZHMvb2I4WWV1U1FKZE1hc2tPTndoeUlibzIrRVJ6VGMxSnNvNk9GdE0vV0R6T2x1NzR4eUM3M25NTktRQzByeE1HMDVWVDArOEQ1Z1ZlWnplejRGeGFrN2FoSnVqOVFQZWYvRUFEQVJBQUlDQVFNQ0JRTURBd1VBQUFBQUFBRUNBQkVERWlFeEJFRVFFeUpSY1RKaG9ZR1I0U094MFJRZ1FsTEIvOW9BQ0FFQ0FRRS9BUEVZYzdIN1E0aDVlZ1JNTElwSGFNeW9hbUVqSVNJNlVzZnZPWUw0RVRHRjVtZ2R6RDVZSEVjWTRyTWg5eE1PUU9LN2ptYkdCUk1uMUdJdXMxR3hvaThYRk9NcVRVVjFQQ3loS0VBV0lOKzhVd24wR091cmNSR1pPSXJ0ZnFqRys4MkV4S09ZSVl4cVh2NEthTXhaQTQzSUZ4Y1lUY0dadG1NeGdsd0ZnUnhkeGtJczN0RmJGWE1LbjNnVXdLRUZBd0NoTXpsUVF2TUhVWmtCVlRRbUoyWkNUS21Xd0lCVVZiaWJDYXhDWTl3VGZ3eE9VTXhaRmNWcUJQdE9wV211WTJLT0dIYVo4bVFubmFZV0FhbTRNeVltUnAvcUZDbnVaajZ3cmVzUTlhdGJBeHVzZGhzSS9VWldObUhwYzdnc0YybU5kS0VHTHZGd1BtTkR0RTZBOTQvU2pFcFlWTG9UVkFZYUloU3BYZ0JPbkxudUoxU2lnWUtxRUl3b3dZMXVvYlZLRzhRV2Q0VUNpTGd6UHdwaDZmS2dzaXA1WjdtWXVwd2FOQlBhSUVGZ2pjR0VvSU1xS3UxVHpuN1J5N3JUU3UzZ3hvVkJ0QyswSmdXQVZNQzMvd0FMK1oxV04zVVJNS3dLb0hFQ2dYTXB4M3NaaHkwL3FPM2VZa1JOMVhuM01ZNmJKRi9KajVXeU1hMi9udEdCWGZlWTNvMUhJRGVuK0pxRlJTQ3ZZUTVLMk04eTFxYXU4dTVRT3hua21nSU1JSm5sZ0xEWGhoQzk3TXhGU0tqS2NlUXJGTzg4M3BNWXJWZndJN294OUkyKzh4ZE1weE1DYW1KclhpL2t6TzNvcWdQdENRTm9TR3hlbUw3UVdkb1ZiVHFFeEtSZENQall4Y1ZDT3BSdkFHb3VWZThPZEtwWTJTL0hDTFRtNE02NGpabWJxQmtJTlZVODJLRVljOTZpZE9ISkEzaFM1Z1ZkK1RPcXBLL3QzZ1VOdmNyUnVET2Q0Q3lrMUJrUXJFV3JKQmlCZmFBRTNVNm5FVUcrMHV4L3NIZ2cxTlViSlNVRHo3UUk3N3lzSTczTHgrMFhUMk1Ec3E2ZTBkNm5UQWxEek9zUTZicUlpQkxNQVhzSXlFTUFJdVArcFJGd2RPak9XNDNQNWk0OFdKYlA1ajVOVm1ZM0xuU2R2MTJtWUxrVGtYNy9BR0VxNWZpSUlvb1JSWkx0SFl0NGlMTDgxcVVSRkNyVy93QzhiR2NxRUN2MS92TWJxdHFaNW1OV3RSUE5KblRvV0pOWERuWEU1QUc4ejVEbWMzRTFLYWhzT0RWeHl4WGNqNEU2akFYOVkrVDl2dEQ5NW9Qek5MZTBBbVBHV1BHMG82cWpudEtKaEhnQjRkTStLdlR6eDg5L3hOMkg4Unk2Q3kwellkZTQ1OEVVdHZNR0lWVlJXU3lBTCsvOHpPZ3hQcUFCQi9FeG56TjY0bnBKcVVLb1ZDYVdvK1BIa08vdUwvaUhwelU4aGgzdmo4LzRpNE1hTnZ2dis0bXk0eUt2YXYwaUdyUGlaUWdNVVJNajRzdHJzWWpCMDFBYmZlT29CdTdtWEpkZ1E2ckpuVDVWK252Mi93RFlXZEJzWVEyUTdtSmd5STMybVRBbVJab3g0TzhSa3lDaERMc0dvdDFSOEJVWTJrSEhnQUFJelJUVUpFdU9FSW5Tc05KRlg4OFRPVDVkejZWK1pqWG5lTUFHTlRFNGJIUm9mM21PZ1ppeUFOcGxBbVppYjRpNmw0N1MvVE1hYUdOdytBamZUTGdsZ2lBRUV3cFJpNHlURng3UVl4Um1KZjZoRlRxVkF4Y1J0enRCcnhtNWUrMHdFcTJ3bTVhcmxGZUlwYXRveXNEWm5DbUROUXBoUE1ROXg0VVBlVVpScUdEd1d4OFNoQWxMTmh0QTVaWjAvSk02cWppbUpDUWFneEFjbUxpQUZ3QVh4TVFOd2dHSUxIMmpHbDNneW9QdEM0RzUzaFRHKzVYOXRvZW1Ua0dvd2RPR3VETjd3WlIzMmowZDdpZUZrVFdZSE1CSmlKVXZRS0hNODlsc0g4UU9GQkFpWmQ2TXNPMTNGQVpvdGhxVVJsY0VobUErUDhtSWlwOS9uZU10aUVJdk1PWkJ4RzZrZ2N6elh5YktMaTlMbWY2alVIUzRrNTNNOHRQYVBqSGFIM2dJOFY4RU9Wa3VheUlFRHFiNWlvRzlKaFJsTzhVRTdURW9pa29iSE10bWJlTmtiRjZidVpPcEpITUR1K3lpNHZTNW0zWTFFNlRCajUzK1lLVVVCQUdiWVJjSm5sb0kxVEpqSk5pVkw4UkxDclVWQzBYR3hJSmhUUThSQTUvYjhRZE9nRVlBR2hFQUpOdzQvU1NvaFZMT293ZVF2QzM4N3daMVphcUlkWTVpNHhVMGdTNVpJak1KWmFCUGVaY0lIMDlvMk1qbWFJRk1DUm1GYnpGa0NneGM2QnJxYWk3M01LMlNZT0lWdklZcEdJV1psNmhtMkVDTVJaaFJQKzBGZ2JHNG1VTkZ6SFRQTjIzWVRHNnV0enNhalFNUkJsWWN3WlZhTXVvR0hIZHdZTHVERGRpSW9jV1pTb3gwekhnRGl4TVdCUU9Zb3FDRDYybWRsWEhVUUFXeGhZc2Q0ZUlSWml6RTk3SG1BWXkvcUV4RUFIU0krWmh6QXdheDRJSlFNQ2dEVjRMb1JMTVoyN2JULzhRQUtCRUFBZ0lDQWdFREJBTUJBUUFBQUFBQUFBRUNFUU1oRWpFUUV5SkJCREpSWVNCQ2NTT0IvOW9BQ0FFREFRRS9BRTZSYU9TT2NFaFNmTGtPZk5rWU9TSnhjRVFleUM4V2tTbFpHWDRJcWY1STgvOEFUaXBYOE1uRGhmd2pvc3hPME4wckl6bEw1SHpGSDlpOHZ6amtvcWlVWXkweHdpbG9ob3RrNVBvUkVpaGVHclJPSEY2VFk1WDJZYjRrcVVIWnloV2lFN1ZGUy9BbWl5L0VGYjJMRkJ1MnRrb3BTMTR4K0xKZGlqUWt5SDhKeFVrVGc0cTZwR0IrMGtsS0xSaWhDckpwMWFJVFVrTEU3SGd2N1dMNlovTEZnUzdaRERDS0k1Y2EwMk4yL0hxcUNzZjFYNEk1bk4weXRpWGlLb1RUL2hrakg4TStuYlZwR3hjbzdSeVpHbTJQb2pLeDVJTHRpeVFiMExJU3c1TGJIeTdUN0VwTWNKTjB4WWt1eUtqRjZFS21KVzJ6VkNpSkYrTWpyK3hnbW90anlQdEZ5ZlpiTVhPcmFKd2JqcnNsS1QwMlJWNklRVUZ2WkZxUTRrRTJ0bEZTVHJzVUcxb1VIWWtxRXFMYWRvV1JiUFVhT1RzWGlmSkRGVW9XTmFQU3pTZDFSQ01sOXpKWlh6VFJPS1V1ekV2ZFpHM3NpbkdkdnhwQ2F0cGtxZnlRbWxkRHlXOUVHcElpTldQR3hZcHQyeU1LOHpwUzZGaWxNeDR1Q2V6aCtpNVJKWkgvQUlLWmtiUHA5Mlh4VlVMZllqVFJ3bFkzZlRIWjEyWVpXK3hMeHIrRGRLeU1MZGx4anI1SXViS24rUjhxMlZGdS9rakF5dFdmVE5XMFNsSnVrZTc1WkdkeGJaejluZEhxdFJvdkpMU0lRNGtvY1ZmWmhrNFMrU3hJU0VQcnc5bDE3WWtZcUl1aWgrRXVDYmJHN2RrSmNIWktEZnVRb1RjZmN4WTBaSlVxRmpjNDJ6RkRndERwcXlOT0wyS2syWXNxKzFpL1FwUm8xUlpPYVNFNlZrSTB2Q1loK01rWnA3STZJcUx1a1lzbkJVeGRNY2tqSk8yVkpMc3hQbkhpK3lkUVZKaWJTWmV4ZDJReVRnaU9WT3haVVBKSm9UYm4zUS93SXNXeEZlSEZTaHNsSGkybVJiNk1lTkpXeU5WUmxnNnNTaSswSnh4clJMSkJ4SVpIQXVXVWNaUkVLTlBZKzlGRk1ncWtLcjh4V3ZDVEtJT1NNeTMyWVVuTVc1ZjRUazBSYmNkazQ4WmEyeVE0T3JFWWtxS2kvd0QwVjJUbHlpSlVoZUk5aVZsSFRQZ1VyVEhKSkRteFRkazIrRm1ENyt5SDI3RnhrcUVuUmtweDJVcUl1N1hpRHRVUjJ6MFU3cG5DU04rTFFxc2o0YW9aZFdqbGI4ZW54WmswcU1DcVpPU1haNmpmUTUzYUwwVHJpSmpvUnhrMEw5RVp6V2t4WnBmS3NpNHl2VkhwYUhpWkQycW1Qd2ppamlpcUhKTkNYTGJGamkxYU9ON1k0YTBLTGpHaDZqczdXMkp4cTBySk50aUZiNkk0cE1oZ3NXT01GYlo2MktIU3NmMUdSOWFQVWxWV1k1dE9tTHd2TmJHb0ppZ21pK0EyMXNUVFdpVFJLUjJVa25SR0ttUndvVVl3WHVZOCtLUFNzZWZMTG83MjJXamtXeUppblM4cnlrM0t4ejRqeUtuUXBjb1VPYml0RHlTWXZDbHVtUmJQK3JUdG5wdSt6cjRISi93UzhjakhrZTcrU01rMEo2TE9UK0NFWDhFb2NoNFp0RktFQnQ5Q0ZxSlRsMFF4SmJaeVMwaU01ZmdwUHNlUGlPR3poK2hweFlpTk5IQkhwbzlOcENkQ25SNnRIcVZUSlNjWFNFNVNqYkpaWEYwU20vUDlURkZ0amY4QVZFWXFLRjRldWljZU95NUtPaVYzc2hCTVVYSHcyS1JkNjhWS2JwRVlwMzhuLzlrPSIgaWQ9ImMiIHdpZHRoPSIxOTkiIGhlaWdodD0iMTU5Ii8+PC9kZWZzPjwvc3ZnPg==", + "description": "Displays rotational speed reading as a horizontal progress bar with background. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'rotationalSpeed', label: 'Rotational speed', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Rotational speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 4000 - 2000;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 4000) {\\n\\tvalue = 4000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":500,\"color\":\"#2B54CE\"},{\"from\":500,\"to\":1500,\"color\":\"#3B911C\"},{\"from\":1500,\"to\":3000,\"color\":\"#F89E0D\"},{\"from\":3000,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":5000,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:cm90YXRpb25hbF9zcGVlZF9wcm9ncmVzc19iYXJfYmFja2dyb3VuZC5wbmc=:cm90YXRpb25hbF9zcGVlZF9wcm9ncmVzc19iYXJfYmFja2dyb3VuZC5wbmc=;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":500,\"color\":\"#2B54CE\"},{\"from\":500,\"to\":1500,\"color\":\"#3B911C\"},{\"from\":1500,\"to\":3000,\"color\":\"#F89E0D\"},{\"from\":3000,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Rotational speed\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"RPM\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "angular speed", + "spin rate", + "revolutions", + "rotational frequency", + "spin motion" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/rotational_speed_range_chart.json b/application/src/main/data/json/system/widget_types/rotational_speed_range_chart.json new file mode 100644 index 0000000000..556cd66c8b --- /dev/null +++ b/application/src/main/data/json/system/widget_types/rotational_speed_range_chart.json @@ -0,0 +1,33 @@ +{ + "fqn": "rotational_speed_range_chart", + "name": "Rotational speed range chart", + "deprecated": false, + "image": "tb-image:cm90YXRpb25hbF9zcGVlZF9yYW5nZV9jaGFydC5zdmc=:cm90YXRpb25hbF9zcGVlZF9yYW5nZV9jaGFydC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxnIGNsaXAtcGF0aD0idXJsKCNhKSI+PHBhdGggZmlsbD0idXJsKCNiKSIgZD0ibTc4LjYgNDkuMy0yMC41IDU2LTcuMSAxN3YxOS4zaDEzNy41di0yNS4zTDE4OCA5MGwtOS44IDQwLTUuNy0yNS0yLjQgNy4zLTEwLjYgMjcuNWMtNy0yOS45LTEyLjgtNDEtMjAuNC03MC40bC03LjkgNDMuOWgtMTAuMWwtMi03MS4zLTMuOS0yOC0xOC43IDcwLTQuNS0yLjItMTMuNS0zMi41WiIvPjxwYXRoIHN0cm9rZT0idXJsKCNjKSIgc3Ryb2tlLXdpZHRoPSIuOSIgZD0ibTUxLjMgMTIyLjQgNy4xLTE3LjIgMjAuNS01NiAxMy41IDMyLjYgNC41IDIuMSAxOC42LTcwIDQuMyAzNS4zIDEuNyA2NEgxMzJsNi43LTQ0LjcgMjEgNzAuOSAxMC42LTI3LjYgMi40LTcuNCA1LjcgMjUuNUwxODggOTAiLz48L2c+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTE1LjMgMTV2LjdoLTR2LS40bDIuNS00aC41bC0uNiAxLjJMMTIgMTVoMy4zWm0tLjgtMy43VjE3aC0uN3YtNS43aC43Wm0yLjUgMy0uNi0uMi4zLTIuOGgzdi43aC0yLjRsLS4xIDEuNS4zLS4xLjYtLjEuNy4xLjUuNC40LjYuMS44LS4xLjdjMCAuMy0uMi41LS4zLjZsLS42LjRhMi4yIDIuMiAwIDAgMS0xLjQgMGMtLjMgMC0uNCAwLS42LS4ybC0uNC0uNWEyIDIgMCAwIDEtLjItLjdoLjdsLjIuNWMwIC4yLjIuMy40LjRoMWwuMy0uMy4yLS40YTEuOCAxLjggMCAwIDAgMC0xbC0uMi0uNGExIDEgMCAwIDAtLjQtLjNoLTFsLS40LjNabTcuMy0uNnYuOWwtLjEgMS4yLS40LjdjLS4yLjItLjQuNC0uNi40YTIgMiAwIDAgMS0uNy4ybC0uNi0uMWMtLjIgMC0uNC0uMS0uNS0uMy0uMiAwLS4zLS4yLS40LS40bC0uMi0uOC0uMS0xdi0uOGwuMS0xLjIuNC0uN2MuMS0uMi4zLS40LjUtLjRsLjgtLjIuNi4xYTEuNCAxLjQgMCAwIDEgLjkuN2wuMi43djFabS0uNyAxdi0xLjhsLS4yLS41YTEgMSAwIDAgMC0uMi0uM2wtLjMtLjJhMSAxIDAgMCAwLS40IDAgMSAxIDAgMCAwLS41IDBsLS4zLjMtLjIuNnYzLjFsLjMuMy4zLjJoLjlsLjMtLjMuMi0uNnYtLjhabTUuMy0xdi45bC0uMSAxLjItLjQuN2MtLjEuMi0uMy40LS42LjRhMiAyIDAgMCAxLS43LjJsLS42LS4xYy0uMiAwLS40LS4xLS41LS4zbC0uNC0uNC0uMi0uOC0uMS0xdi0uOGwuMS0xLjIuNC0uN2MuMS0uMi4zLS40LjYtLjRsLjctLjIuNi4xYTEuNCAxLjQgMCAwIDEgLjkuN2wuMi43LjEgMVptLS43IDF2LTEuOGwtLjItLjVhMSAxIDAgMCAwLS4yLS4zbC0uMy0uMmExIDEgMCAwIDAtLjQgMCAxIDEgMCAwIDAtLjUgMGwtLjMuMy0uMi42djIuNmwuMS41LjIuMy4zLjJoLjlsLjMtLjMuMi0uNnYtLjhabTItMy40SDMyYy40IDAgLjggMCAxLjEuMi4zLjEuNS4zLjcuNmwuMiAxYzAgLjIgMCAuNC0uMi42IDAgLjItLjIuNC0uNC42YTIgMiAwIDAgMS0uNy4zbC0uMi4xaC0xLjh2LS42SDMyYy4zIDAgLjUgMCAuNy0uMi4yIDAgLjMtLjIuNC0uM2wuMi0uNmMwLS4yIDAtLjQtLjItLjZsLS40LS4zLS43LS4ySDMxVjE3SDMwdi01LjdabTMuMyA1LjdMMzIgMTQuNGguOGwxLjQgMi42aC0uOFptMy44LTIuMmgtMS41di0uNmgxLjVjLjMgMCAuNSAwIC43LS4yLjIgMCAuMy0uMi40LS40bC4xLS41LS4xLS42YTEgMSAwIDAgMC0uNC0uNGwtLjctLjJoLTEuNFYxN2gtLjd2LTUuN2gyYy41IDAgLjkgMCAxLjIuMi4zLjIuNS40LjYuNy4yLjIuMy41LjMuOCAwIC40LS4xLjctLjMgMWwtLjYuNi0xLjEuMlptMy4zLTMuNWguN2wxLjkgNC42IDEuOC00LjZoLjhMNDMuNSAxN2gtLjZsLTIuMy01LjdabS0uMyAwaC43VjE3aC0uN3YtNS43Wm01IDBoLjdWMTdoLS43di01LjdaTTEyLjUgNDJoLjVsLjctLjJjLjEgMCAuMy0uMi4zLS40bC4yLS41LS4xLS41Yy0uMS0uMi0uMi0uMy0uNC0uM2wtLjUtLjJjLS4yIDAtLjQgMC0uNi4yYS45LjkgMCAwIDAtLjUuOGgtLjdjMC0uMyAwLS41LjItLjhsLjctLjVhMiAyIDAgMCAxIC45LS4ybC45LjEuNi42LjIuOS0uMS40YzAgLjItLjIuMy0uMy41bC0uNi4zLS44LjJoLS42di0uNVptMCAuNXYtLjRoLjZsMSAuMWMuMiAwIC40LjIuNS4zbC4zLjV2MS4ybC0uNC42LS42LjNoLTEuNGwtLjYtLjNhMS40IDEuNCAwIDAgMS0uNS0xLjJoLjdsLjEuNS40LjQuNi4xLjUtLjFjLjIgMCAuMy0uMi40LS40bC4yLS42LS4yLS41YS45LjkgMCAwIDAtLjQtLjRoLTEuMlptNC41IDAtLjYtLjIuMy0yLjloM3YuN2gtMi40bC0uMSAxLjUuMy0uMS42LS4xLjcuMS41LjQuNC42LjEuOC0uMS44LS4zLjYtLjYuNGEyLjIgMi4yIDAgMCAxLTEuNCAwYy0uMyAwLS40LS4yLS42LS4zbC0uNC0uNWEyIDIgMCAwIDEtLjItLjdoLjdsLjIuNi40LjMuNS4xaC41bC4zLS4zLjItLjVhMS44IDEuOCAwIDAgMCAwLTFsLS4yLS40YTEgMSAwIDAgMC0uNC0uM2gtMWwtLjQuM1ptNy4zLS43di45bC0uMSAxLjItLjQuNy0uNi41YTIgMiAwIDAgMS0uNyAwaC0uNmwtLjUtLjItLjQtLjUtLjItLjctLjEtMXYtLjlsLjEtMS4yLjQtLjcuNS0uNC44LS4xaC42YTEuNCAxLjQgMCAwIDEgLjkuN2wuMi43djFabS0uNyAxVjQxbC0uMi0uNWExIDEgMCAwIDAtLjItLjNsLS4zLS4yYTEgMSAwIDAgMC0uNCAwIDEgMSAwIDAgMC0uNSAwbC0uMy4zLS4yLjZWNDRsLjMuMy4zLjIuNC4xLjUtLjEuMy0uMy4yLS42di0uOFptNS4zLTF2LjlsLS4xIDEuMi0uNC43Yy0uMS4yLS4zLjQtLjYuNWEyIDIgMCAwIDEtLjcgMGgtLjZMMjYgNDVsLS40LS41LS4yLS43LS4xLTF2LS45bC4xLTEuMi40LS43Yy4xLS4yLjMtLjMuNi0uNGwuNy0uMWguNmExLjQgMS40IDAgMCAxIC45LjdsLjIuNy4xIDFabS0uNyAxVjQxbC0uMi0uNWExIDEgMCAwIDAtLjItLjNsLS4zLS4yYTEgMSAwIDAgMC0uNCAwIDEgMSAwIDAgMC0uNSAwbC0uMy4zLS4yLjZ2Mi42bC4xLjUuMi4zLjMuMi40LjEuNS0uMS4zLS4zLjItLjZ2LS44Wm0yLTMuNEgzMmMuNCAwIC44IDAgMS4xLjJsLjcuNi4yIDFjMCAuMiAwIC40LS4yLjcgMCAuMi0uMi4zLS40LjVhMiAyIDAgMCAxLS43LjNsLS4yLjFoLTEuOHYtLjZIMzJsLjctLjEuNC0uNC4yLS42YzAtLjIgMC0uNC0uMi0uNSAwLS4yLS4yLS4zLS40LS40TDMyIDQwSDMxdjUuMUgzMHYtNS43Wm0zLjMgNS43TDMyIDQyLjVoLjhsMS40IDIuNmgtLjhabTMuOC0yLjJoLTEuNXYtLjZoMS41Yy4zIDAgLjUgMCAuNy0uMi4yIDAgLjMtLjIuNC0uNGwuMS0uNS0uMS0uNmExIDEgMCAwIDAtLjQtLjRsLS43LS4yaC0xLjR2NS4xaC0uN3YtNS43aDJjLjUgMCAuOS4xIDEuMi4zLjMuMS41LjMuNi42LjIuMi4zLjUuMy45bC0uMy45Yy0uMS4zLS4zLjUtLjYuNmwtMS4xLjJabTMuMy0zLjVoLjdsMS45IDQuNyAxLjgtNC43aC44bC0yLjMgNS43aC0uNmwtMi4zLTUuN1ptLS4zIDBoLjd2NS43aC0uN3YtNS43Wm01IDBoLjd2NS43aC0uN3YtNS43Wk0xNS4yIDcyLjZ2LjZoLTMuN3YtLjVsMS44LTIgLjYtLjcuMi0uNS4xLS40LS4xLS41YzAtLjItLjItLjMtLjMtLjRhMSAxIDAgMCAwLS42LS4xbC0uNi4xYTEgMSAwIDAgMC0uNC40bC0uMS42aC0uN2MwLS4zIDAtLjYuMi0uOWwuNi0uNmEyIDIgMCAwIDEgMS0uMmMuNCAwIC43IDAgMSAuMi4yIDAgLjQuMy41LjUuMi4yLjIuNS4yLjh2LjVsLS4zLjVhNy44IDcuOCAwIDAgMS0uOCAxbC0xLjUgMS42aDIuOVptMS44LTItLjYtLjIuMy0yLjloM3YuN2gtMi40bC0uMSAxLjYuMy0uMkgxOC44bC41LjQuNC42LjEuOC0uMS44LS4zLjYtLjYuNGEyLjIgMi4yIDAgMCAxLTEuNCAwYy0uMyAwLS40LS4xLS42LS4zbC0uNC0uNWEyIDIgMCAwIDEtLjItLjdoLjdsLjIuNi40LjMuNS4xaC41bC4zLS4zLjItLjRhMS44IDEuOCAwIDAgMCAwLTFsLS4yLS41YTEgMSAwIDAgMC0uNC0uMmwtLjUtLjFoLS41bC0uNC4zWm03LjMtLjd2LjlsLS4xIDEuMi0uNC44LS42LjRhMiAyIDAgMCAxLS43LjFoLS42bC0uNS0uMy0uNC0uNS0uMi0uNy0uMS0xdi0uOWwuMS0xLjEuNC0uOC41LS40LjgtLjFoLjZhMS40IDEuNCAwIDAgMSAuOS44bC4yLjd2MVptLS43IDF2LTEuOGwtLjItLjVhMSAxIDAgMCAwLS4yLS4zbC0uMy0uMmExIDEgMCAwIDAtLjQgMCAxIDEgMCAwIDAtLjUgMGwtLjMuNC0uMi41djMuMWMuMi4xLjIuMy4zLjRsLjMuMmguOWMwLS4yLjItLjMuMy0uNGwuMi0uNlY3MVptNS4zLTF2LjlsLS4xIDEuMi0uNC44Yy0uMS4yLS4zLjMtLjYuNGEyIDIgMCAwIDEtLjcuMWgtLjZMMjYgNzNsLS40LS41LS4yLS43LS4xLTF2LS45bC4xLTEuMS40LS44Yy4xLS4yLjMtLjMuNi0uNGwuNy0uMWguNmExLjQgMS40IDAgMCAxIC45LjhsLjIuNy4xIDFabS0uNyAxdi0xLjhsLS4yLS41YTEgMSAwIDAgMC0uMi0uM2wtLjMtLjJhMSAxIDAgMCAwLS40IDAgMSAxIDAgMCAwLS41IDBsLS4zLjQtLjIuNXYyLjZsLjEuNS4yLjQuMy4yaC45bC4zLS40LjItLjZWNzFabTItMy40SDMybDEuMS4yLjcuNi4yIDFjMCAuMiAwIC40LS4yLjcgMCAuMi0uMi40LS40LjVhMiAyIDAgMCAxLS43LjNsLS4yLjFoLTEuOHYtLjZIMzJsLjctLjEuNC0uNC4yLS41YzAtLjMgMC0uNS0uMi0uNiAwLS4yLS4yLS4zLS40LS40bC0uNy0uMUgzMXY1SDMwdi01LjdabTMuMyA1LjdMMzIgNzAuN2guOGwxLjQgMi41aC0uOFptMy44LTIuMmgtMS41di0uNmgxLjVjLjMgMCAuNSAwIC43LS4yLjIgMCAuMy0uMi40LS4zbC4xLS42LS4xLS42YTEgMSAwIDAgMC0uNC0uNGwtLjctLjFoLTEuNHY1aC0uN3YtNS43aDJjLjUgMCAuOS4xIDEuMi4zLjMuMS41LjMuNi42LjIuMi4zLjUuMy45IDAgLjMtLjEuNy0uMyAxLS4xLjItLjMuNC0uNi41bC0xLjEuMlptMy4zLTMuNWguN2wxLjkgNC43IDEuOC00LjdoLjhsLTIuMyA1LjdoLS42bC0yLjMtNS43Wm0tLjMgMGguN3Y1LjdoLS43di01LjdabTUgMGguN3Y1LjdoLS43di01LjdaTTEzLjggOTUuNnY1LjdoLS43di00LjhsLTEuNC42di0uN2wyLS44aC4xWm0zLjIgMy0uNi0uMS4zLTIuOGgzdi42aC0yLjRsLS4xIDEuNi4zLS4ySDE4LjhsLjUuNC40LjYuMS44LS4xLjgtLjMuNi0uNi40YTIuMiAyLjIgMCAwIDEtMS40IDBjLS4zIDAtLjQtLjEtLjYtLjMtLjIgMC0uMy0uMy0uNC0uNWEyIDIgMCAwIDEtLjItLjZoLjdsLjIuNS40LjMuNS4xaC41bC4zLS4zLjItLjRhMS44IDEuOCAwIDAgMCAwLTFsLS4yLS41YTEgMSAwIDAgMC0uNC0uMmwtLjUtLjFoLS41bC0uNC4zWm03LjMtLjV2LjhsLS4xIDEuMi0uNC44LS42LjRhMiAyIDAgMCAxLS43LjFoLS42bC0uNS0uMy0uNC0uNS0uMi0uNy0uMS0xdi0uOGwuMS0xLjIuNC0uOC41LS40LjgtLjFoLjZhMS40IDEuNCAwIDAgMSAuOS44bC4yLjd2MVptLS43IDFWOTcuMmwtLjItLjVhMSAxIDAgMCAwLS4yLS40bC0uMy0uMmExIDEgMCAwIDAtLjQgMCAxIDEgMCAwIDAtLjUgMGwtLjMuNC0uMi41djMuMWwuMy40LjMuMmguOWwuMy0uNC4yLS41Vjk5Wm01LjMtMXYuOGwtLjEgMS4yLS40LjhjLS4xLjItLjMuMy0uNi40YTIgMiAwIDAgMS0uNy4xaC0uNmwtLjUtLjMtLjQtLjUtLjItLjctLjEtMXYtLjhsLjEtMS4yLjQtLjhjLjEtLjIuMy0uMy42LS40bC43LS4xaC42YTEuNCAxLjQgMCAwIDEgLjkuOGwuMi43LjEgMVptLS43IDFWOTcuMmwtLjItLjVhMSAxIDAgMCAwLS4yLS40bC0uMy0uMmExIDEgMCAwIDAtLjQgMCAxIDEgMCAwIDAtLjUgMGwtLjMuNC0uMi41djIuNmwuMS41LjIuNC4zLjJoLjlsLjMtLjQuMi0uNVY5OVptMi0zLjRIMzJjLjQgMCAuOCAwIDEuMS4yLjMgMCAuNS4zLjcuNWwuMiAxYzAgLjIgMCAuNS0uMi43IDAgLjItLjIuNC0uNC41YTIgMiAwIDAgMS0uNy40aC0ydi0uNkgzMmwuNy0uMS40LS40LjItLjVjMC0uMyAwLS40LS4yLS42IDAtLjItLjItLjMtLjQtLjRsLS43LS4xSDMxdjVIMzB2LTUuNlptMy4zIDUuNkwzMiA5OC44aC44bDEuNCAyLjVoLS44Wm0zLjgtMi4yaC0xLjV2LS42aDEuNWwuNy0uMS40LS40LjEtLjYtLjEtLjVhMSAxIDAgMCAwLS40LS41bC0uNy0uMWgtMS40djVoLS43di01LjZoMmMuNSAwIC45IDAgMS4yLjIuMy4xLjUuMy42LjZsLjMuOWMwIC40LS4xLjctLjMgMS0uMS4yLS4zLjQtLjYuNS0uMy4yLS43LjItMS4xLjJabTMuMy0zLjRoLjdsMS45IDQuNiAxLjgtNC42aC44bC0yLjMgNS42aC0uNmwtMi4zLTUuNlptLS4zIDBoLjd2NS42aC0uN3YtNS42Wm01IDBoLjd2NS42aC0uN3YtNS42Wk0xOS40IDEyNi44bC0uNi0uMi4zLTIuOGgzdi42aC0yLjRsLS4yIDEuNi40LS4ySDIxLjJsLjUuNWMuMi4xLjMuMy40LjZ2MS41bC0uNC42LS41LjRhMi4yIDIuMiAwIDAgMS0xLjQgMGwtLjYtLjItLjQtLjVhMiAyIDAgMCAxLS4yLS43aC43YzAgLjIgMCAuNC4yLjUgMCAuMi4yLjMuMy4zbC42LjFoLjRsLjQtLjMuMi0uNGExLjggMS44IDAgMCAwIDAtMWwtLjItLjRhMSAxIDAgMCAwLS40LS4zbC0uNS0uMWgtLjVhMiAyIDAgMCAwLS40LjRabTcuMy0uNnYuOGMwIC41IDAgLjktLjIgMS4yIDAgLjMtLjIuNi0uMy44bC0uNi40YTIgMiAwIDAgMS0uNy4xaC0uNmMtLjIgMC0uNC0uMi0uNS0uM2wtLjQtLjUtLjMtLjd2LTNsLjUtLjguNS0uNC44LS4xaC42YTEuNCAxLjQgMCAwIDEgLjguOGwuMy43djFabS0uOCAxVjEyNS40bC0uMS0uNWExIDEgMCAwIDAtLjMtLjRsLS4zLS4yYTEgMSAwIDAgMC0uMyAwIDEgMSAwIDAgMC0uNSAwbC0uMy40LS4yLjUtLjEuOFYxMjcuOGwuMi41LjIuNC4zLjJoLjhsLjQtLjQuMi0uNXYtLjhabTUuNC0xdi44bC0uMSAxLjItLjQuOC0uNi40YTIgMiAwIDAgMS0uNy4xaC0uNmMtLjIgMC0uNC0uMi0uNS0uM2wtLjQtLjUtLjItLjctLjEtMXYtLjhsLjEtMS4yLjQtLjguNS0uNC44LS4xaC42YTEuNCAxLjQgMCAwIDEgLjkuOGwuMi43djFabS0uNyAxVjEyNS40bC0uMi0uNWExIDEgMCAwIDAtLjItLjRsLS4zLS4yYTEgMSAwIDAgMC0uNCAwIDEgMSAwIDAgMC0uNSAwbC0uMy40LS4yLjV2My4xbC4zLjQuMy4yaC45bC4zLS40LjItLjV2LS44Wm0yLjUtMS4zdjMuNmgtLjd2LTQuM2guN3YuN1ptMS4zLS43di43SDMzLjdhMSAxIDAgMCAwLS40LjNsLS4yLjN2LjRsLS4zLjF2LS43bC4zLS42LjQtLjRhMS4xIDEuMSAwIDAgMSAxIDBabTEuNi44djVoLS44di01LjhoLjd2LjhabTIuOCAxLjN2LjFsLS4xLjktLjMuNi0uNS41LS43LjFoLS43bC0uNi0uNWEyIDIgMCAwIDEtLjMtLjZsLS4yLS44di0uNGwuMi0uOS4zLS42LjUtLjQuOC0uMS43LjEuNS41LjMuNi4xIDFabS0uNy4xdi0uN2wtLjMtLjVhMSAxIDAgMCAwLS45LS40aC0uNGExIDEgMCAwIDAtLjQuM2wtLjIuNC0uMS40djFjMCAuMiAwIC40LjIuNSAwIC4yLjIuMy40LjRsLjUuMWguNmwuMy0uNC4yLS41di0uNlptMi41LTEuM3YzLjRoLS44di00LjNoLjd2LjlabS0uMiAxaC0uM3YtLjdsLjQtLjcuNi0uNC43LS4xaC41YTEuMSAxLjEgMCAwIDEgLjguOHYzLjVoLS43di0zLjNjLS4xLS4yLS4zLS4zLS40LS4zbC0uNS0uMUg0MWExIDEgMCAwIDAtLjMuNGwtLjIuNXYuNVptMi44LS4zLS41LjF2LS42bC40LS42LjUtLjQuNy0uMWguNmwuNS4zLjMuNXYzLjVINDVWMTI2bC0uNC0uM2ExLjIgMS4yIDAgMCAwLS45IDBsLS4zLjMtLjIuM3YuNFoiLz48ZyBjbGlwLXBhdGg9InVybCgjZCkiPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii40IiBzdHJva2Utd2lkdGg9Ii40IiBkPSJNNTEgMTRoMTQxLjZNNTEgNDEuOGgxNDEuNiIvPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii41IiBzdHJva2Utd2lkdGg9Ii40IiBkPSJNNTEgNjkuNWgxNDEuNk01MSA5Ny4zaDE0MS42TTUxIDEyNWgxNDEuNiIvPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLW9wYWNpdHk9Ii41IiBzdHJva2Utd2lkdGg9Ii4zIiBkPSJNNjguOCAxNDN2LS44Ii8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTU5IDE0My4ydjUuN2gtLjZ2LTQuOGwtMS41LjV2LS42bDItLjhoLjJabTQuNyAwdjUuN2gtLjh2LTQuOGwtMS40LjV2LS42bDItLjhoLjJabTguOCAwdjUuN2gtLjhsLTIuOS00LjR2NC40aC0uN3YtNS43aC43bDMgNC40di00LjRoLjdabTEgMy42LjItLjljMC0uMy4yLS41LjQtLjcuMS0uMi40LS4zLjYtLjRsLjgtLjJjLjMgMCAuNSAwIC44LjIuMiAwIC40LjIuNi40bC40LjcuMS44di4xbC0uMS45LS40LjdhMS44IDEuOCAwIDAgMS0xLjQuNiAxLjggMS44IDAgMCAxLTEuOC0xLjNsLS4xLS45Wm0uOCAwdi42bC4zLjVjMCAuMi4yLjMuNC40aDFsLjQtLjQuMi0uNXYtMS4ybC0uMi0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLS41LjFsLS40LjQtLjIuNS0uMS41Wm01LjMgMS41IDEuMi0zLjZoLjdsLTEuNSA0LjJoLS41bC4xLS42Wm0tMS0zLjYgMS4yIDMuNi4xLjZoLS41bC0xLjUtNC4yaC43WiIvPjxnIGNsaXAtcGF0aD0idXJsKCNlKSI+PHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0ic3F1YXJlIiBzdHJva2Utb3BhY2l0eT0iLjUiIHN0cm9rZS13aWR0aD0iLjMiIGQ9Ik0xMDIuOCAxNDEuNHYtLjgiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii41IiBkPSJNOTMgMTQzLjZ2NS43aC0uNnYtNC44bC0xLjUuNXYtLjZsMi0uOGguMlptNiA1LjF2LjZoLTMuN3YtLjVsMS45LTIgLjUtLjcuMi0uNS4xLS40di0uNmwtLjQtLjNhMSAxIDAgMCAwLS42LS4yYy0uMiAwLS40IDAtLjYuMmExIDEgMCAwIDAtLjQuNGwtLjEuNmgtLjdjMC0uMyAwLS42LjItLjkuMS0uMy4zLS41LjYtLjZhMiAyIDAgMCAxIDEtLjNsMSAuMmMuMi4yLjQuMy41LjYuMi4yLjIuNS4yLjh2LjVsLS4zLjVhNy44IDcuOCAwIDAgMS0uOCAxbC0xLjQgMS42SDk5Wm03LjUtNXY1LjZoLS44bC0yLjktNC40djQuNGgtLjd2LTUuN2guN2wzIDQuNHYtNC40aC43Wm0xIDMuNS4yLS45YzAtLjMuMi0uNS40LS43LjEtLjIuNC0uMy42LS40bC44LS4yYy4zIDAgLjUgMCAuOC4yLjIgMCAuNC4yLjYuNGwuNC43LjEuOS0uMS45LS40LjdhMS44IDEuOCAwIDAgMS0xLjQuNiAxLjggMS44IDAgMCAxLTEuOC0xLjNsLS4xLS45Wm0uOCAwdi42bC4zLjVjMCAuMi4yLjMuNC40aDFsLjQtLjQuMi0uNXYtMS4ybC0uMi0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLTEgLjVsLS4xLjUtLjEuNlptNS4zIDEuNSAxLjItMy42aC43bC0xLjUgNC4yaC0uNWwuMS0uNlptLTEtMy42IDEuMiAzLjYuMS42aC0uNWwtMS41LTQuMmguN1oiLz48L2c+PGcgY2xpcC1wYXRoPSJ1cmwoI2YpIj48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiIHN0cm9rZS1vcGFjaXR5PSIuNSIgc3Ryb2tlLXdpZHRoPSIuMyIgZD0iTTEzNi44IDE0MS40di0uOCIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Ik0xMjcgMTQzLjZ2NS43aC0uNnYtNC44bC0xLjUuNXYtLjZsMi0uOGguMlptMy4zIDIuNWguNmwuNi0uMS40LS40di0xYy0uMS0uMi0uMi0uMy0uNC0uM2wtLjUtLjJjLS4yIDAtLjQgMC0uNi4yYS45LjkgMCAwIDAtLjQuOGgtLjhhMS41IDEuNSAwIDAgMSAuOS0xLjMgMiAyIDAgMCAxIC45LS4zbC45LjIuNi42LjIuOS0uMS40YzAgLjItLjIuMy0uMy41bC0uNS4zLS44LjFoLS43di0uNFptMCAuNnYtLjRoLjdsLjkuMWMuMiAwIC40LjIuNS4zbC4zLjV2MS4ybC0uNC42LS42LjNoLTEuNGwtLjYtLjNhMS40IDEuNCAwIDAgMS0uNS0xLjJoLjdsLjEuNS40LjRoMS4ybC4zLS40LjItLjZjMC0uMiAwLS40LS4yLS41YS45LjkgMCAwIDAtLjQtLjRsLS42LS4xaC0uNlptMTAuMi0zdjUuNmgtLjhsLTIuOS00LjR2NC40aC0uN3YtNS43aC43bDMgNC40di00LjRoLjdabTEgMy41LjItLjljMC0uMy4yLS41LjQtLjcuMS0uMi40LS4zLjYtLjRsLjgtLjJjLjMgMCAuNSAwIC44LjIuMiAwIC40LjIuNi40bC40LjcuMS45LS4xLjktLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM2wtLjEtLjlabS44IDB2LjZsLjMuNWMwIC4yLjIuMy40LjRoMWwuNC0uNC4yLS41di0xLjJsLS4yLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtMSAuNWwtLjEuNS0uMS42Wm01LjMgMS41IDEuMi0zLjZoLjdsLTEuNSA0LjJoLS41bC4xLS42Wm0tMS0zLjYgMS4yIDMuNi4xLjZoLS41bC0xLjUtNC4yaC43WiIvPjwvZz48ZyBjbGlwLXBhdGg9InVybCgjZykiPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLW9wYWNpdHk9Ii41IiBzdHJva2Utd2lkdGg9Ii4zIiBkPSJNMTcwLjggMTQxLjR2LS44Ii8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTE2MSAxNDMuNnY1LjdoLS42di00LjhsLTEuNS41di0uNmwyLS44aC4yWm02LjEgMy44di42aC00di0uNGwyLjUtNGguNmwtLjcgMS4yLTEuNyAyLjZoMy4zWm0tLjgtMy44djUuN2gtLjd2LTUuN2guN1ptOC4yIDB2NS43aC0uOGwtMi45LTQuNHY0LjRoLS43di01LjdoLjdsMyA0LjR2LTQuNGguN1ptMSAzLjYuMi0uOWMwLS4zLjItLjUuNC0uNy4xLS4yLjQtLjMuNi0uNGwuOC0uMmMuMyAwIC41IDAgLjguMi4yIDAgLjQuMi42LjRsLjQuNy4xLjktLjEuOS0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zbC0uMS0uOVptLjggMHYuNmwuMy41YzAgLjIuMi4zLjQuNGgxbC40LS40LjItLjV2LTEuMmwtLjItLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0xIC41bC0uMS41LS4xLjZabTUuMyAxLjUgMS4yLTMuNmguN2wtMS41IDQuMmgtLjVsLjEtLjZabS0xLTMuNiAxLjIgMy42LjEuNmgtLjVsLTEuNS00LjJoLjdaIi8+PC9nPjwvZz48ZGVmcz48Y2xpcFBhdGggaWQ9ImEiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik01MSA1aDEzN3YxMzdINTF6Ii8+PC9jbGlwUGF0aD48Y2xpcFBhdGggaWQ9ImQiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik01MCAxMGgxMzh2MTQzSDUweiIvPjwvY2xpcFBhdGg+PGNsaXBQYXRoIGlkPSJlIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNODYgMTQwLjVoMzR2MTBIODZ6Ii8+PC9jbGlwUGF0aD48Y2xpcFBhdGggaWQ9ImYiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0xMjAgMTQwLjVoMzR2MTBoLTM0eiIvPjwvY2xpcFBhdGg+PGNsaXBQYXRoIGlkPSJnIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTU0IDE0MC41aDM0djEwaC0zNHoiLz48L2NsaXBQYXRoPjxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjExNi40IiB4Mj0iMTE2LjQiIHkxPSIxNTYuMyIgeTI9Ii0xOS41IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIuMiIgc3RvcC1jb2xvcj0iIzUxNkZDOSIvPjxzdG9wIG9mZnNldD0iLjIiIHN0b3AtY29sb3I9IiM2RENBNDkiLz48c3RvcCBvZmZzZXQ9Ii4zIiBzdG9wLWNvbG9yPSIjNkRDQTQ5Ii8+PHN0b3Agb2Zmc2V0PSIuMyIgc3RvcC1jb2xvcj0iI0ZFQkIzRCIvPjxzdG9wIG9mZnNldD0iLjciIHN0b3AtY29sb3I9IiNGRUJCM0QiLz48c3RvcCBvZmZzZXQ9Ii43IiBzdG9wLWNvbG9yPSIjRDMzRTU3Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImMiIHgxPSIxMTUuNSIgeDI9IjExNS40IiB5MT0iMTQxIiB5Mj0iLTE5LjUiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9Ii4xIiBzdG9wLWNvbG9yPSIjMzA1QUQ3Ii8+PHN0b3Agb2Zmc2V0PSIuMSIgc3RvcC1jb2xvcj0iIzNGQTcxQSIvPjxzdG9wIG9mZnNldD0iLjMiIHN0b3AtY29sb3I9IiMzRkE3MUEiLz48c3RvcCBvZmZzZXQ9Ii4zIiBzdG9wLWNvbG9yPSIjRkZBNjAwIi8+PHN0b3Agb2Zmc2V0PSIuNiIgc3RvcC1jb2xvcj0iI0ZGQTYwMCIvPjxzdG9wIG9mZnNldD0iLjYiIHN0b3AtY29sb3I9IiNEODE4MzgiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48L3N2Zz4=", + "description": "Displays changes to rotational speed data over time visualized with color ranges.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.rangeChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.rangeChartWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: false,\n defaultDataKeysFunction: function() {\n return [{ name: 'rotationalSpeed', label: 'Rotational speed', type: 'timeseries' }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-range-chart-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-range-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Rotational speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 4000 - 2000;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 4000) {\\n\\tvalue = 4000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":2592000000,\"interval\":86400000,\"fixedTimewindow\":{\"startTimeMs\":1700577227898,\"endTimeMs\":1700663627898},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"dataZoom\":true,\"rangeColors\":[{\"from\":0,\"to\":500,\"color\":\"#305AD7\"},{\"from\":500,\"to\":1500,\"color\":\"#3FA71A\"},{\"from\":1500,\"to\":3000,\"color\":\"#FFA600\"},{\"from\":3000,\"to\":null,\"color\":\"#F04022\"}],\"outOfRangeColor\":\"#ccc\",\"fillArea\":true,\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm\",\"lastUpdateAgo\":false,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Rotational speed\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"RPM\",\"decimals\":0,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "angular speed", + "spin rate", + "revolutions", + "rotational frequency", + "spin motion" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/rotational_speed_range_chart_with_background.json b/application/src/main/data/json/system/widget_types/rotational_speed_range_chart_with_background.json new file mode 100644 index 0000000000..5d63d44132 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/rotational_speed_range_chart_with_background.json @@ -0,0 +1,33 @@ +{ + "fqn": "rotational_speed_range_chart_with_background", + "name": "Rotational speed range chart with background", + "deprecated": false, + "image": "tb-image:cm90YXRpb25hbF9zcGVlZF9yYW5nZV9jaGFydF93aXRoX2JhY2tncm91bmQuc3Zn:cm90YXRpb25hbF9zcGVlZF9yYW5nZV9jaGFydF93aXRoX2JhY2tncm91bmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48L2c+PGcgY2xpcC1wYXRoPSJ1cmwoI2MpIj48cGF0aCBmaWxsPSJ1cmwoI2QpIiBkPSJtNzkuMyA0OS40LTIwLjMgNTMtNyAxNi40VjEzN2gxMzZ2LTI0bC0uNC0yNC45LTkuNyAzNy44LTUuNi0yMy42LTIuNCA3LTEwLjQgMjZjLTctMjguMy0xMi43LTM4LjktMjAuMy02Ni43bC03LjcgNDEuNWgtMTBsLTItNjcuNS0zLjktMjYuNi0xOC41IDY2LjMtNC40LTItMTMuNC0zMC45WiIvPjxwYXRoIHN0cm9rZT0idXJsKCNlKSIgc3Ryb2tlLXdpZHRoPSIuOSIgZD0ibTUyIDExOC45IDctMTYuMyAyMC40LTUzLjFMOTMgODAuM2w0LjQgMi4xTDExNiAxNmw0LjIgMzMuNSAxLjYgNjAuN2gxMC42bDYuNy00Mi41IDIwLjQgNjcuOCAxMi41LTMzIDYgMjMuNSA5LjUtMzcuNSIvPjwvZz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii41IiBkPSJNMTYuMyAxN3YuN2gtNHYtLjRsMi41LTRoLjVsLS42IDEuMkwxMyAxN2gzLjNabS0uOC0zLjdWMTloLS43di01LjdoLjdabTIuNSAzLS42LS4yLjMtMi44aDN2LjdoLTIuNGwtLjEgMS41LjMtLjEuNi0uMS43LjEuNS40LjQuNi4xLjgtLjEuN2MwIC4zLS4yLjUtLjMuNmwtLjYuNGEyLjIgMi4yIDAgMCAxLTEuNCAwYy0uMyAwLS40IDAtLjYtLjJsLS40LS41YTIgMiAwIDAgMS0uMi0uN2guN2wuMi41YzAgLjIuMi4zLjQuNGgxbC4zLS4zLjItLjRhMS44IDEuOCAwIDAgMCAwLTFsLS4yLS40YTEgMSAwIDAgMC0uNC0uM2gtMWwtLjQuM1ptNy4zLS42di45bC0uMSAxLjEtLjQuOGMtLjIuMi0uNC40LS42LjRhMiAyIDAgMCAxLS43LjJsLS42LS4xYy0uMiAwLS40LS4xLS41LS4zLS4yIDAtLjMtLjItLjQtLjRsLS4yLS44LS4xLTF2LS44bC4xLTEuMi40LS43Yy4xLS4yLjMtLjQuNS0uNGwuOC0uMi42LjFhMS40IDEuNCAwIDAgMSAuOS43bC4yLjd2MVptLS43IDF2LTEuOGwtLjItLjVhMSAxIDAgMCAwLS4yLS4zbC0uMy0uMmExIDEgMCAwIDAtLjQgMCAxIDEgMCAwIDAtLjUgMGwtLjMuMy0uMi42djMuMWwuMy4zLjMuMmguOWwuMy0uMy4yLS42di0uOFptNS4zLTF2LjlsLS4xIDEuMS0uNC44Yy0uMS4yLS4zLjQtLjYuNGEyIDIgMCAwIDEtLjcuMmwtLjYtLjFjLS4yIDAtLjQtLjEtLjUtLjNsLS40LS40LS4yLS44LS4xLTF2LS44bC4xLTEuMi40LS43Yy4xLS4yLjMtLjQuNi0uNGwuNy0uMi42LjFhMS40IDEuNCAwIDAgMSAuOS43bC4yLjcuMSAxWm0tLjcgMXYtMS44bC0uMi0uNWExIDEgMCAwIDAtLjItLjNsLS4zLS4yYTEgMSAwIDAgMC0uNCAwIDEgMSAwIDAgMC0uNSAwbC0uMy4zLS4yLjZ2Mi42bC4xLjUuMi4zLjMuMmguOWwuMy0uMy4yLS42di0uOFptMi0zLjRIMzNjLjQgMCAuOCAwIDEuMS4yLjMuMS41LjMuNy42bC4yIDFjMCAuMiAwIC40LS4yLjYgMCAuMi0uMi40LS40LjZhMiAyIDAgMCAxLS43LjNsLS4yLjFoLTEuOHYtLjZIMzNjLjMgMCAuNSAwIC43LS4yLjIgMCAuMy0uMi40LS4zbC4yLS42YzAtLjIgMC0uNC0uMi0uNmwtLjQtLjMtLjctLjJIMzJWMTlIMzF2LTUuN1ptMy4zIDUuN0wzMyAxNi40aC44bDEuNCAyLjZoLS44Wm0zLjgtMi4yaC0xLjV2LS42aDEuNWMuMyAwIC41IDAgLjctLjIuMiAwIC4zLS4yLjQtLjRsLjEtLjUtLjEtLjZhMSAxIDAgMCAwLS40LS40bC0uNy0uMmgtMS40VjE5aC0uN3YtNS43aDJjLjUgMCAuOSAwIDEuMi4yLjMuMi41LjQuNi43LjIuMi4zLjUuMy44IDAgLjQtLjEuNy0uMyAxbC0uNi42LTEuMS4yWm0zLjMtMy41aC43bDEuOSA0LjYgMS44LTQuNmguOEw0NC41IDE5aC0uNmwtMi4zLTUuN1ptLS4zIDBoLjdWMTloLS43di01LjdabTUgMGguN1YxOWgtLjd2LTUuN1pNMTMuNSA0NGguNWwuNy0uMmMuMSAwIC4zLS4yLjMtLjRsLjItLjUtLjEtLjVjLS4xLS4yLS4yLS4zLS40LS4zbC0uNS0uMmMtLjIgMC0uNCAwLS42LjJhLjkuOSAwIDAgMC0uNS44aC0uN2MwLS4zIDAtLjUuMi0uOGwuNy0uNWEyIDIgMCAwIDEgLjktLjJsLjkuMS42LjYuMi45LS4xLjRjMCAuMi0uMi4zLS4zLjVsLS42LjMtLjguMmgtLjZ2LS41Wm0wIC41di0uNGguNmwxIC4xYy4yIDAgLjQuMi41LjNsLjMuNXYxLjJsLS40LjYtLjYuM2gtMS40bC0uNi0uM2ExLjQgMS40IDAgMCAxLS41LTEuMmguN2wuMS41LjQuNC42LjEuNS0uMWMuMiAwIC4zLS4yLjQtLjRsLjItLjYtLjItLjVhLjkuOSAwIDAgMC0uNC0uNGgtMS4yWm00LjUgMC0uNi0uMi4zLTIuOWgzdi43aC0yLjRsLS4xIDEuNS4zLS4xLjYtLjEuNy4xLjUuNC40LjYuMS44LS4xLjgtLjMuNi0uNi40YTIuMiAyLjIgMCAwIDEtMS40IDBjLS4zIDAtLjQtLjItLjYtLjNsLS40LS41YTIgMiAwIDAgMS0uMi0uN2guN2wuMi42LjQuMy41LjFoLjVsLjMtLjMuMi0uNWExLjggMS44IDAgMCAwIDAtMWwtLjItLjRhMSAxIDAgMCAwLS40LS4zaC0xbC0uNC4zWm03LjMtLjd2LjlsLS4xIDEuMi0uNC43LS42LjVhMiAyIDAgMCAxLS43IDBoLS42bC0uNS0uMi0uNC0uNS0uMi0uNy0uMS0xdi0uOWwuMS0xLjIuNC0uNy41LS40LjgtLjFoLjZhMS40IDEuNCAwIDAgMSAuOS43bC4yLjd2MVptLS43IDFWNDNsLS4yLS41YTEgMSAwIDAgMC0uMi0uM2wtLjMtLjJhMSAxIDAgMCAwLS40IDAgMSAxIDAgMCAwLS41IDBsLS4zLjMtLjIuNlY0NmwuMy4zLjMuMi40LjEuNS0uMS4zLS4zLjItLjZ2LS44Wm01LjMtMXYuOWwtLjEgMS4yLS40LjdjLS4xLjItLjMuNC0uNi41YTIgMiAwIDAgMS0uNyAwaC0uNkwyNyA0N2wtLjQtLjUtLjItLjctLjEtMXYtLjlsLjEtMS4yLjQtLjdjLjEtLjIuMy0uMy42LS40bC43LS4xaC42YTEuNCAxLjQgMCAwIDEgLjkuN2wuMi43LjEgMVptLS43IDFWNDNsLS4yLS41YTEgMSAwIDAgMC0uMi0uM2wtLjMtLjJhMSAxIDAgMCAwLS40IDAgMSAxIDAgMCAwLS41IDBsLS4zLjMtLjIuNnYyLjZsLjEuNS4yLjMuMy4yLjQuMS41LS4xLjMtLjMuMi0uNnYtLjhabTItMy40SDMzYy40IDAgLjggMCAxLjEuMmwuNy42LjIgMWMwIC4yIDAgLjQtLjIuNyAwIC4yLS4yLjMtLjQuNWEyIDIgMCAwIDEtLjcuM2wtLjIuMWgtMS44di0uNkgzM2wuNy0uMS40LS40LjItLjZjMC0uMiAwLS40LS4yLS41IDAtLjItLjItLjMtLjQtLjRMMzMgNDJIMzJ2NS4xSDMxdi01LjdabTMuMyA1LjdMMzMgNDQuNWguOGwxLjQgMi42aC0uOFptMy44LTIuMmgtMS41di0uNmgxLjVjLjMgMCAuNSAwIC43LS4yLjIgMCAuMy0uMi40LS40bC4xLS41LS4xLS42YTEgMSAwIDAgMC0uNC0uNGwtLjctLjJoLTEuNHY1LjFoLS43di01LjdoMmMuNSAwIC45LjEgMS4yLjMuMy4xLjUuMy42LjYuMi4yLjMuNS4zLjlsLS4zLjljLS4xLjMtLjMuNS0uNi42bC0xLjEuMlptMy4zLTMuNWguN2wxLjkgNC43IDEuOC00LjdoLjhsLTIuMyA1LjdoLS42bC0yLjMtNS43Wm0tLjMgMGguN3Y1LjdoLS43di01LjdabTUgMGguN3Y1LjdoLS43di01LjdaTTE2LjIgNzQuNnYuNmgtMy43di0uNWwxLjgtMiAuNi0uNy4yLS41LjEtLjQtLjEtLjVjMC0uMi0uMi0uMy0uMy0uNGExIDEgMCAwIDAtLjYtLjFsLS42LjFhMSAxIDAgMCAwLS40LjRsLS4xLjZoLS43YzAtLjMgMC0uNi4yLS45bC42LS42YTIgMiAwIDAgMSAxLS4yYy40IDAgLjcgMCAxIC4yLjIgMCAuNC4zLjUuNS4yLjIuMi41LjIuOHYuNWwtLjMuNWE3LjggNy44IDAgMCAxLS44IDFsLTEuNSAxLjZoMi45Wm0xLjgtMi0uNi0uMi4zLTIuOWgzdi43aC0yLjRsLS4xIDEuNi4zLS4ySDE5LjhsLjUuNC40LjYuMS44LS4xLjgtLjMuNi0uNi40YTIuMiAyLjIgMCAwIDEtMS40IDBjLS4zIDAtLjQtLjEtLjYtLjNsLS40LS41YTIgMiAwIDAgMS0uMi0uN2guN2wuMi42LjQuMy41LjFoLjVsLjMtLjMuMi0uNGExLjggMS44IDAgMCAwIDAtMWwtLjItLjVhMSAxIDAgMCAwLS40LS4ybC0uNS0uMWgtLjVsLS40LjNabTcuMy0uN3YuOWwtLjEgMS4yLS40LjgtLjYuNGEyIDIgMCAwIDEtLjcuMWgtLjZsLS41LS4zLS40LS41LS4yLS43LS4xLTF2LS45bC4xLTEuMS40LS44LjUtLjQuOC0uMWguNmExLjQgMS40IDAgMCAxIC45LjhsLjIuN3YxWm0tLjcgMXYtMS44bC0uMi0uNWExIDEgMCAwIDAtLjItLjNsLS4zLS4yYTEgMSAwIDAgMC0uNCAwIDEgMSAwIDAgMC0uNSAwbC0uMy40LS4yLjV2My4xYy4yLjEuMi4zLjMuNGwuMy4yaC45YzAtLjIuMi0uMy4zLS40bC4yLS42VjczWm01LjMtMXYuOWwtLjEgMS4yLS40LjhjLS4xLjItLjMuMy0uNi40YTIgMiAwIDAgMS0uNy4xaC0uNkwyNyA3NWwtLjQtLjUtLjItLjctLjEtMXYtLjlsLjEtMS4xLjQtLjhjLjEtLjIuMy0uMy42LS40bC43LS4xaC42YTEuNCAxLjQgMCAwIDEgLjkuOGwuMi43LjEgMVptLS43IDF2LTEuOGwtLjItLjVhMSAxIDAgMCAwLS4yLS4zbC0uMy0uMmExIDEgMCAwIDAtLjQgMCAxIDEgMCAwIDAtLjUgMGwtLjMuNC0uMi41djIuNmwuMS41LjIuNC4zLjJoLjlsLjMtLjQuMi0uNlY3M1ptMi0zLjRIMzNsMS4xLjIuNy42LjIgMWMwIC4yIDAgLjQtLjIuNyAwIC4yLS4yLjQtLjQuNWEyIDIgMCAwIDEtLjcuM2wtLjIuMWgtMS44di0uNkgzM2wuNy0uMS40LS40LjItLjVjMC0uMyAwLS41LS4yLS42IDAtLjItLjItLjMtLjQtLjRsLS43LS4xSDMydjVIMzF2LTUuN1ptMy4zIDUuN0wzMyA3Mi43aC44bDEuNCAyLjVoLS44Wm0zLjgtMi4yaC0xLjV2LS42aDEuNWMuMyAwIC41IDAgLjctLjIuMiAwIC4zLS4yLjQtLjNsLjEtLjYtLjEtLjZhMSAxIDAgMCAwLS40LS40bC0uNy0uMWgtMS40djVoLS43di01LjdoMmMuNSAwIC45LjEgMS4yLjMuMy4xLjUuMy42LjYuMi4yLjMuNS4zLjkgMCAuMy0uMS43LS4zIDEtLjEuMi0uMy40LS42LjVsLTEuMS4yWm0zLjMtMy41aC43bDEuOSA0LjcgMS44LTQuN2guOGwtMi4zIDUuN2gtLjZsLTIuMy01LjdabS0uMyAwaC43djUuN2gtLjd2LTUuN1ptNSAwaC43djUuN2gtLjd2LTUuN1pNMTQuOCA5Ny42djUuN2gtLjd2LTQuOGwtMS40LjZ2LS43bDItLjhoLjFabTMuMiAzLS42LS4xLjMtMi44aDN2LjZoLTIuNGwtLjEgMS42LjMtLjJIMTkuOGwuNS40LjQuNy4xLjctLjEuOC0uMy42LS42LjRhMi4yIDIuMiAwIDAgMS0xLjQgMGMtLjMgMC0uNC0uMS0uNi0uMy0uMiAwLS4zLS4zLS40LS41YTIgMiAwIDAgMS0uMi0uNmguN2wuMi41LjQuMy41LjFoLjVsLjMtLjMuMi0uNGExLjggMS44IDAgMCAwIDAtMWwtLjItLjVhMSAxIDAgMCAwLS40LS4ybC0uNS0uMWgtLjVhMiAyIDAgMCAwLS40LjNabTcuMy0uNXYuOGwtLjEgMS4yLS40LjgtLjYuNGEyIDIgMCAwIDEtLjcuMWgtLjZsLS41LS4zLS40LS41LS4yLS43LS4xLTF2LS45bC4xLTEuMS40LS44LjUtLjQuOC0uMWguNmExLjQgMS40IDAgMCAxIC45LjhsLjIuN3YxWm0tLjcgMVY5OS4ybC0uMi0uNWExIDEgMCAwIDAtLjItLjRsLS4zLS4yYTEgMSAwIDAgMC0uNCAwIDEgMSAwIDAgMC0uNSAwbC0uMy40LS4yLjV2My4xbC4zLjQuMy4yaC45bC4zLS40LjItLjV2LS45Wm01LjMtMXYuOGwtLjEgMS4yLS40LjhjLS4xLjItLjMuMy0uNi40YTIgMiAwIDAgMS0uNy4xaC0uNmwtLjUtLjMtLjQtLjUtLjItLjctLjEtMXYtLjlsLjEtMS4xLjQtLjhjLjEtLjIuMy0uMy42LS40bC43LS4xaC42YTEuNCAxLjQgMCAwIDEgLjkuOGwuMi43LjEgMVptLS43IDFWOTkuMmwtLjItLjVhMSAxIDAgMCAwLS4yLS40bC0uMy0uMmExIDEgMCAwIDAtLjQgMCAxIDEgMCAwIDAtLjUgMGwtLjMuNC0uMi41djIuNmwuMS41LjIuNC4zLjJoLjlsLjMtLjQuMi0uNXYtLjlabTItMy40SDMzYy40IDAgLjggMCAxLjEuMi4zIDAgLjUuMy43LjVsLjIgMWMwIC4yIDAgLjUtLjIuNyAwIC4yLS4yLjQtLjQuNWEyIDIgMCAwIDEtLjcuNGgtMnYtLjZIMzNsLjctLjFhMSAxIDAgMCAwIC42LTFjMC0uMiAwLS4zLS4yLS41IDAtLjItLjItLjMtLjQtLjRsLS43LS4xSDMydjVIMzF2LTUuNlptMy4zIDUuNi0xLjQtMi41aC44bDEuNCAyLjVoLS44Wm0zLjgtMi4yaC0xLjV2LS42aDEuNWwuNy0uMS40LS40LjEtLjYtLjEtLjVhMSAxIDAgMCAwLS40LS41bC0uNy0uMWgtMS40djVoLS43di01LjZoMmMuNSAwIC45IDAgMS4yLjIuMy4xLjUuMy42LjZsLjMuOWMwIC40LS4xLjctLjMgMS0uMS4yLS4zLjQtLjYuNWwtMS4xLjJabTMuMy0zLjRoLjdsMS45IDQuNiAxLjgtNC42aC44bC0yLjMgNS42aC0uNmwtMi4zLTUuNlptLS4zIDBoLjd2NS42aC0uN3YtNS42Wm01IDBoLjd2NS42aC0uN3YtNS42Wk0yMC40IDEyOC44bC0uNi0uMi4zLTIuOGgzdi42aC0yLjRsLS4yIDEuNi40LS4ySDIyLjJsLjUuNWMuMi4xLjMuMy40LjZ2MS41bC0uNC42LS41LjRhMi4yIDIuMiAwIDAgMS0xLjQgMGwtLjYtLjItLjQtLjVhMiAyIDAgMCAxLS4yLS43aC43YzAgLjIgMCAuNC4yLjUgMCAuMi4yLjMuMy4zbC42LjFoLjRsLjQtLjMuMi0uNGExLjggMS44IDAgMCAwIDAtMWwtLjItLjRhMSAxIDAgMCAwLS40LS4zbC0uNS0uMWgtLjVsLS40LjRabTcuMy0uNnYuOGMwIC41IDAgLjktLjIgMS4yIDAgLjMtLjIuNi0uMy44bC0uNi40YTIgMiAwIDAgMS0uNy4xaC0uNmMtLjIgMC0uNC0uMi0uNS0uM2wtLjQtLjUtLjMtLjd2LTNsLjUtLjguNS0uNC44LS4xaC42YTEuNCAxLjQgMCAwIDEgLjguOGwuMy43djFabS0uOCAxVjEyNy40bC0uMS0uNWExIDEgMCAwIDAtLjMtLjRsLS4zLS4yYTEgMSAwIDAgMC0uMyAwIDEgMSAwIDAgMC0uNSAwbC0uMy40LS4yLjUtLjEuOFYxMjkuOGwuMi41LjIuNC4zLjJoLjhsLjQtLjQuMi0uNXYtLjhabTUuNC0xdi44bC0uMSAxLjItLjQuOC0uNi40YTIgMiAwIDAgMS0uNy4xaC0uNmMtLjIgMC0uNC0uMi0uNS0uM2wtLjQtLjUtLjItLjctLjEtMXYtLjhsLjEtMS4yLjQtLjguNS0uNC44LS4xaC42YTEuNCAxLjQgMCAwIDEgLjkuOGwuMi43djFabS0uNyAxVjEyNy40bC0uMi0uNWExIDEgMCAwIDAtLjItLjRsLS4zLS4yYTEgMSAwIDAgMC0uNCAwIDEgMSAwIDAgMC0uNSAwbC0uMy40LS4yLjV2My4xbC4zLjQuMy4yaC45bC4zLS40LjItLjV2LS44Wm0yLjUtMS4zdjMuNmgtLjd2LTQuM2guN3YuN1ptMS4zLS43di43SDM0LjdhMSAxIDAgMCAwLS40LjNsLS4yLjN2LjRsLS4zLjF2LS43bC4zLS42LjQtLjRhMS4xIDEuMSAwIDAgMSAxIDBabTEuNi44djVoLS44di01LjhoLjd2LjhabTIuOCAxLjN2LjFsLS4xLjktLjMuNi0uNS41LS43LjFoLS43bC0uNi0uNWEyIDIgMCAwIDEtLjMtLjZsLS4yLS44di0uNGwuMi0uOS4zLS42LjUtLjQuOC0uMS43LjEuNS41LjMuNi4xIDFabS0uNy4xdi0uN2wtLjMtLjVhMSAxIDAgMCAwLS45LS40aC0uNGExIDEgMCAwIDAtLjQuM2wtLjIuNC0uMS40djFjMCAuMiAwIC40LjIuNSAwIC4yLjIuMy40LjRsLjUuMWguNmwuMy0uNC4yLS41di0uNlptMi41LTEuM3YzLjRoLS44di00LjNoLjd2LjlabS0uMiAxaC0uM3YtLjdsLjQtLjcuNi0uNC43LS4xaC41YTEuMSAxLjEgMCAwIDEgLjguOHYzLjVoLS43di0zLjNjLS4xLS4yLS4zLS4zLS40LS4zbC0uNS0uMUg0MmExIDEgMCAwIDAtLjMuNGwtLjIuNXYuNVptMi44LS4zLS41LjF2LS42bC40LS42LjUtLjQuNy0uMWguNmwuNS4zLjMuNXYzLjVINDZWMTI4bC0uNC0uM2ExLjIgMS4yIDAgMCAwLS45IDBsLS4zLjMtLjIuM3YuNFoiLz48ZyBjbGlwLXBhdGg9InVybCgjZikiPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii41IiBzdHJva2Utd2lkdGg9Ii40IiBkPSJNNTIgMTZoMTM3TTUyIDQyLjVoMTM3TTUyIDY5aDEzN001MiA5NS41aDEzN001MiAxMjJoMTM3Ii8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTU2LjggMTQxLjN2NS43aC0uN3YtNC44bC0xLjUuNXYtLjZsMi4xLS44aC4xWm00LjUgMHY1LjdoLS43di00LjhsLTEuNS41di0uNmwyLjEtLjhoLjFabTguNyAwdjUuN2gtLjhsLTIuOC00LjR2NC40aC0uOHYtNS43aC44bDIuOCA0LjR2LTQuNGguOFptMSAzLjZjMC0uNCAwLS42LjItLjkgMC0uMy4yLS41LjMtLjdsLjYtLjRjLjMtLjIuNS0uMi44LS4yLjMgMCAuNiAwIC44LjIuMyAwIC41LjIuNi40bC40LjcuMi44di4xYzAgLjMgMCAuNi0uMi45bC0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zdi0uOVptLjcgMCAuMS42LjIuNS40LjRoMS4xbC4zLS40LjMtLjV2LTEuMmMwLS4yLS4yLS40LS4zLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41di41Wm01LjMgMS40IDEuMi0zLjVoLjdsLTEuNSA0LjJoLS41bC4xLS43Wm0tMS0zLjUgMS4yIDMuNi4xLjZoLS41bC0xLjUtNC4yaC43Wk04My44IDE0MS4zdjUuN2gtLjd2LTQuOGwtMS41LjV2LS42bDIuMS0uOGguMVptNS45IDUuMXYuNmgtMy44di0uNWwxLjktMiAuNS0uNy4zLS41di0xbC0uNC0uM2ExIDEgMCAwIDAtLjUtLjJjLS4zIDAtLjUgMC0uNi4yYTEgMSAwIDAgMC0uNC40bC0uMS42aC0uOGwuMi0uOWMuMi0uMy40LS41LjctLjZhMiAyIDAgMCAxIDEtLjNsMSAuMmMuMi4yLjQuMy41LjZsLjIuOHYuNWwtLjMuNWE3LjggNy44IDAgMCAxLS44IDFsLTEuNSAxLjZoMi45Wm03LjMtNXY1LjZoLS44bC0yLjgtNC40djQuNGgtLjh2LTUuN2guOGwyLjggNC40di00LjRoLjhabTEgMy41YzAtLjQgMC0uNi4yLS45IDAtLjMuMi0uNS4zLS43bC42LS40Yy4zLS4yLjUtLjIuOC0uMi4zIDAgLjYgMCAuOC4yLjMgMCAuNS4yLjYuNGwuNC43LjIuOHYuMWMwIC4zIDAgLjYtLjIuOWwtLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM3YtLjlabS43IDAgLjEuNi4yLjUuNC40aDEuMWwuMy0uNC4zLS41di0xLjJjMC0uMi0uMi0uNC0uMy0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLS41LjFsLS40LjQtLjIuNXYuNVptNS4zIDEuNCAxLjItMy41aC43bC0xLjUgNC4yaC0uNWwuMS0uN1ptLTEtMy41IDEuMiAzLjYuMS42aC0uNWwtMS41LTQuMmguN1pNMTEwLjggMTQxLjN2NS43aC0uN3YtNC44bC0xLjUuNXYtLjZsMi4xLS44aC4xWm0zLjIgMi41aC41bC42LS4xLjQtLjQuMS0uNXYtLjVsLS40LS40SDExNGEuOS45IDAgMCAwLS41LjloLS43YzAtLjMgMC0uNi4yLS44bC42LS42YTIgMiAwIDAgMSAxLS4ybC44LjJjLjMuMS41LjMuNi42LjIuMi4zLjUuMy44bC0uMS41LS40LjUtLjUuMy0uOC4xaC0uNnYtLjRabTAgLjZ2LS40aDEuNmwuNS40LjMuNXYxLjJsLS41LjUtLjUuNGgtMS41bC0uNS0uM2ExLjQgMS40IDAgMCAxLS42LTEuMmguOHYuNWwuNS40aDEuMWwuNC0uNC4xLS42LS4xLS42YS45LjkgMCAwIDAtLjUtLjNsLS42LS4xaC0uNVptMTAtM3Y1LjZoLS44bC0yLjgtNC40djQuNGgtLjh2LTUuN2guOGwyLjggNC40di00LjRoLjhabTEgMy41YzAtLjQgMC0uNi4yLS45IDAtLjMuMi0uNS4zLS43bC42LS40Yy4zLS4yLjUtLjIuOC0uMi4zIDAgLjYgMCAuOC4yLjMgMCAuNS4yLjYuNGwuNC43LjIuOHYuMWMwIC4zIDAgLjYtLjIuOWwtLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM3YtLjlabS43IDAgLjEuNi4yLjUuNC40aDEuMWwuMy0uNC4zLS41di0xLjJjMC0uMi0uMi0uNC0uMy0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLS41LjFsLS40LjQtLjIuNXYuNVptNS4zIDEuNCAxLjItMy41aC43bC0xLjUgNC4yaC0uNWwuMS0uN1ptLTEtMy41IDEuMiAzLjYuMS42aC0uNWwtMS41LTQuMmguN1pNMTM3LjggMTQxLjN2NS43aC0uN3YtNC44bC0xLjUuNXYtLjZsMi4xLS44aC4xWm02IDMuOHYuNmgtNC4xdi0uNGwyLjUtNGguNmwtLjYgMS4yLTEuNyAyLjZoMy4zWm0tLjgtMy44djUuN2gtLjd2LTUuN2guN1ptOCAwdjUuN2gtLjhsLTIuOC00LjR2NC40aC0uOHYtNS43aC44bDIuOCA0LjR2LTQuNGguOFptMSAzLjZjMC0uNCAwLS42LjItLjkgMC0uMy4yLS41LjMtLjdsLjYtLjRjLjMtLjIuNS0uMi44LS4yLjMgMCAuNiAwIC44LjIuMyAwIC41LjIuNi40bC40LjcuMi44di4xYzAgLjMgMCAuNi0uMi45bC0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zdi0uOVptLjcgMCAuMS42LjIuNS40LjRoMS4xbC4zLS40LjMtLjV2LTEuMmMwLS4yLS4yLS40LS4zLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41di41Wm01LjMgMS40IDEuMi0zLjVoLjdsLTEuNSA0LjJoLS41bC4xLS43Wm0tMS0zLjUgMS4yIDMuNi4xLjZoLS41bC0xLjUtNC4yaC43Wk0xNjQuOCAxNDEuM3Y1LjdoLS43di00LjhsLTEuNS41di0uNmwyLjEtLjhoLjFabTMgMy0uNS0uMi4zLTIuOGgyLjl2LjdoLTIuM2wtLjIgMS41LjQtLjEuNS0uMS44LjEuNS40LjMuNmEyLjUgMi41IDAgMCAxIDAgMS41YzAgLjMtLjIuNS0uMy42LS4xLjItLjMuMy0uNi40YTIgMiAwIDAgMS0uOC4ybC0uNi0uMS0uNi0uMy0uMy0uNWEyIDIgMCAwIDEtLjItLjdoLjdsLjEuNS40LjRoMWwuMy0uMy4yLS40YTEuOCAxLjggMCAwIDAgMC0xYzAtLjIgMC0uMy0uMi0uNGExIDEgMCAwIDAtLjMtLjNoLTEuMWwtLjMuM1ptMTAuMi0zdjUuN2gtLjhsLTIuOC00LjR2NC40aC0uOHYtNS43aC44bDIuOCA0LjR2LTQuNGguOFptMSAzLjZjMC0uNCAwLS42LjItLjkgMC0uMy4yLS41LjMtLjdsLjYtLjRjLjMtLjIuNS0uMi44LS4yLjMgMCAuNiAwIC44LjIuMyAwIC41LjIuNi40bC40LjcuMi44di4xYzAgLjMgMCAuNi0uMi45bC0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zdi0uOVptLjcgMCAuMS42LjIuNS40LjRoMS4xbC4zLS40LjMtLjV2LTEuMmMwLS4yLS4yLS40LS4zLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41di41Wm01LjMgMS40IDEuMi0zLjVoLjdsLTEuNSA0LjJoLS41bC4xLS43Wm0tMS0zLjUgMS4yIDMuNi4xLjZoLS41bC0xLjUtNC4yaC43WiIvPjwvZz48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImQiIHgxPSIxMTYuNyIgeDI9IjExNi43IiB5MT0iMTUwLjkiIHkyPSItMTUuNyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iLjIiIHN0b3AtY29sb3I9IiM1MTZGQzkiLz48c3RvcCBvZmZzZXQ9Ii4yIiBzdG9wLWNvbG9yPSIjNkRDQTQ5Ii8+PHN0b3Agb2Zmc2V0PSIuMyIgc3RvcC1jb2xvcj0iIzZEQ0E0OSIvPjxzdG9wIG9mZnNldD0iLjMiIHN0b3AtY29sb3I9IiNGRUJCM0QiLz48c3RvcCBvZmZzZXQ9Ii43IiBzdG9wLWNvbG9yPSIjRkVCQjNEIi8+PHN0b3Agb2Zmc2V0PSIuNyIgc3RvcC1jb2xvcj0iI0QzM0U1NyIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJlIiB4MT0iMTE1LjkiIHgyPSIxMTUuOCIgeTE9IjEzNi42IiB5Mj0iLTE1LjgiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9Ii4xIiBzdG9wLWNvbG9yPSIjMzA1QUQ3Ii8+PHN0b3Agb2Zmc2V0PSIuMSIgc3RvcC1jb2xvcj0iIzNGQTcxQSIvPjxzdG9wIG9mZnNldD0iLjMiIHN0b3AtY29sb3I9IiMzRkE3MUEiLz48c3RvcCBvZmZzZXQ9Ii4zIiBzdG9wLWNvbG9yPSIjRkZBNjAwIi8+PHN0b3Agb2Zmc2V0PSIuNiIgc3RvcC1jb2xvcj0iI0ZGQTYwMCIvPjxzdG9wIG9mZnNldD0iLjYiIHN0b3AtY29sb3I9IiNEODE4MzgiLz48L2xpbmVhckdyYWRpZW50PjxjbGlwUGF0aCBpZD0iYyI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTUyIDdoMTM2djEzN0g1MnoiLz48L2NsaXBQYXRoPjxjbGlwUGF0aCBpZD0iZiI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTUyIDEyaDEzNnYxMzZINTJ6Ii8+PC9jbGlwUGF0aD48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2ciIHRyYW5zZm9ybT0ibWF0cml4KC4wMDUwMzE0NSAwIDAgLjAwNjI4OTMxIDAgMCkiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIHg9IjAiIHk9IjAiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjMiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA4N183NTg2MyIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA4N183NTg2MyIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFJQkFRRUJBUUlCQVFFQ0FnSUNBZ1FEQWdJQ0FnVUVCQU1FQmdVR0JnWUZCZ1lHQndrSUJnY0pCd1lHQ0FzSUNRb0tDZ29LQmdnTERBc0tEQWtLQ2dyLzJ3QkRBUUlDQWdJQ0FnVURBd1VLQndZSENnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ3Ivd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhnQUFBZ01CQVFFQkFRRUFBQUFBQUFBQUJ3Z0ZCZ2tFQXdFQ0NnRC94QUFhQVFBREFRRUJBUUFBQUFBQUFBQUFBQUFCQWdNRUFBVUcvOW9BREFNQkFBSVFBeEFBQUFER1A1L2Q3QS80Z2pjUXZxbEtSYSs1NlF0RnFiTG9WaDF1YkN2WHdRdlJFVFVVdnpiUlBOWXZxMlBXL01tMW85NE9oZVRTWVZhTzRHeWJGOVQvQURMZXg1MzNqK3VIaFJZdTQ5TzUzUEoxc1RLaWphSXMvbnN4RTZMTGFTYVhpWVpWZDZGZFE4OWI5eEVCWEFYMGNZT3BON3Nta3hLK2JPdks4T2JRY3BuRFAxTVBvT2p0Q2VaNjJUZmFUeTlvQzRjbkYxWlVlS0ZLRXd3TzM1SUU4VkViUnpMYlJtVk83dXBQY2pWcFl3ZWxqWnpQWnVzOWJxUXk4YVdzZC9ORjYrRXBBeFpObW1UOUNwSUhDRGhReUNIeHJOa0Y1Vzd3cVhFYnQ3aTNOdEVvMXZxbWlzQWd5NVdic3k4V201dVBUT0FYcFdURzhiZ3JRemNXQWFxUVFVWkJQUnlXVE5UaUJwZGtzMHpabFpySVdOQ1BhMTVrcHZUbUJzUnFvVkdwR2UrdUN4YW9rak5WZXJTOXU1dTRXZnZQYkNyMk1GYkkxSDgzWVlZdFZXNWJxekpLTXVOVXV5bjM3bTVoV1k0OEI0WE1udjNRSjVsNDBtKzVITkVjMlBUeHQ1aDAwZmdRNnJvbmcxSWJzeitLSGhJYVhOY0gxbnF4ajBMTFZha3loV2l3M0Nvc0hhaFd2ZHhSVXJSV1lYZEdrbFU4eWZsN3N3TnVZZDBSNGM5aVZKNDg4SXFJaU9pSlFCSTRMZVpOQkRVa0JURm5zK05FUW5WQVphTVV3ZnZKcGU3TGN0S2IramZqalhpb3ZkYVd3VURSSnFzOUx5Q1dGYW84RmZxdWFldkxFM1UveWNtWnFoUjBPU1A0SGxhMlFEOXAyYlBRa28zSFZkZHZMMkdwV0p5TkNkME9lSDdLdU5VRmpLWFVkOG91VGd3bklUdXFaOGFzeVgrbG10Q0hSYnpOazhPNkF5LzJpRmJUZFBOb1BFWFhTaTV5ZWhqYmlWdE4vTzFHUkQwOFlFQk85TTZBZWgrRE5Tb2U1a3dLd3hkUXNSa3Q2dUpVOVVTRkNqbzQ3YXRZdFFDZGNiL1R4RHJSUFJiemRkQ0hMYmVRcjB5NUNHV3pXMEx4M251S3YybXJGNVBkaTBrVlhpZUJXVXR0SnF1M0wrNHVmRCtmUDJ2T25jN3ZGazBQZm5zbjJtT2Z1bUxCeHBHdW92dk9SbXducWtTZ3ZpT1M1MHIzZDlQRWRDNE1MZWc2aHVORWM5WWpoRW5ya3B5MzF3SFY1aEdzOUp2UDExTmxiYU5Nc3R1WmNxemc3TFdLemREenRaTW15RmJNemI1dEEvSTZBZnozVzBjL0dlOWVLamh3N2NLQmxsT2FPSVhUSEQwTWhEVS9LQjY4R3BtNXZLams4dkxQSFZEMzdoRGFSQ2xRdXljbXExZVBVN2g5UGZPUFlCcTNrMFZWaU1Da1NRc0ZwMXM5T3IwU3dvbGs5SFhRM3o5YWtYbE1xWjRGTE5tZW9GWXlpOGRsbVVibElNMlN4dW01UWxRb3pZQjFSbTQwWVdiaEoxKzkxeEJyWEJmYUxBTW9DdEpvcDBrcHVjVUt0VlNvTUZSMHc2M0ZHdk9BZFovTlM2SXpUWnJQQm50RU9xM1ZtcDFFTDg2YVRaZEZjUFJoQmFtZVE4R0hVTjBTWlV5WGRmQVdBbTYvdXFGNklBTFJIMURSVkVZU0ZYQ3pXNEtLdmRwcDlhWEp3OFc3a1plaVplVEpxZWpMZTJBa3dHRTRDSit6MjA1aWdqOFhBY09BRnBqelVXbEozcVJJa25MTFlpSGx1dkttdUlTcWM1SHc5KzE2U21aQ2IrNTVuTTlYd3k2R2pqVG1QQ0ZremsweE1xdUZLeW9EQUI3SVFOMXNHVnZ2Y2M0MkljSE5pVU1NMlUzUkZRdGtPM3VxTlU0bjQ3ZWJvN082S1pmeWVzazJMS1BHT0JYU1pWalJpcFZBOW9xNXJrTjlNdkp1bW9OYkVZeDVMRk9kQ3VqUHJDbEFiczF0ZVlGVmtPTk03VkpwaVRrK1ZScldVVVJjVmNjdlBncXM2amYveEFBckVBQUJCUUFCQkFJQkF3VUJBUUFBQUFBRUFRSURCUVlIQUJFU0V4UWhGU0lqSkFnUUZqRXlNeGYvMmdBSUFRRUFBUWdDam1ZMW5icjNNNjl6UDk5RDMrV0RIVkpuMjBzbHIrVzZ1THFhNk4rYVZWNTgyNWcrUXkrb3A2U0pza3RZVjZqSTM5WkFTR2FRVjNVVVRHdCt5SDF3TUxyRXpXYmtxOEpjS01IZFROL2FxYXgrb2xkNXBpemVRb0krN2JpaG85Z0lVRFB5Wnh5VGtEWnpZM0tvL2RlblRTcDl5OGVQOTFZTzNxOXVETXhXUHVnTVp0OXB0ckI0WFZwVGNvaDM0TksrMDQxdHFrTlRyeEhMMjY4bmRKNTkrNnlQNy8yYTFmTkU2eG1uaW80ZnhrdC9TVldraVJoTnRuS2RvM3Rvc3NNWUN4czA3WFc1VHY0M0kxdmNpdVpUeSt2M2Y5MDBLZVNkOHhWZktlaWRaeW5RUUpFNkpCSEtnOUJWNVdQT0dlRk5zOE5QUVcwd3RWYTNwSnJQaG1jV2QzVmNVcWJCNDMrTmxOc2o3Yk1QSFdJRE1iSVU5QUtnYSt3dk16aVVnalI3ZXZhdlN2VjMyN3YxU0NEa0h3L1ByT05NamREUVdGeHlKbXFTazEwUWRWRWZIREQ2MDRjbGpkWkdTOUZkbG5qVkx2UXBYeExESHAreDlsTE15S2xtZDJjdFNGSkZJMUV3amcxZTFpaWRrZ1RwN211K2tOalpOQ283K1M4MURyS3hKU3RkajdlcGkvSmRjTW5lWS93dXRSWFJXbENaWE8vcDB4V0dJcFN6VE9aTXJhbjU5bDlrK00rVjZQZTA3SkhqY2YycEpNTE9qT0pXRWgvSm9CdUg3QjMyWEh4UlhEdlQ1TkZ4WmxHd3Vqbnl2SitMcDZrUUkvYzNRMS9wV25WZGhLc0RrZ2ZTOG5SNEFkMWt5NC9xUHZyRnFOR0c1S3ZkYVkyc3RYVjZrMkw0MFpWb25SQWFqcWpZczA0aXNLU2FXajExUFppZG9HbE1lbm1sZ1Q5TDJMbFI4N2tkeUpSWmNHWWlJbmpNcXlhYzRXdElYUU1JOWI2TTdUNVUvd0RLNXM3a1RVd0J3V1E5ZVNKcXRTU2RmYVlsOEFLK2tEVEdHeVJ3akY2ek9qSis4TnRhQTBoc0EwRzFiNjBsQXNzRnFrbWtzbzc1OXBDc01nUVFPalBiKzFObmJ3bXdVS1pNcFh3SjJLb1cxbExheGtCMXhJMFU2SFNET2duN09UT1ZEajdLUStVbzBPMnIzeERoWUlrTW44bkJWMTc2K0NXRlNyRkZWVVNRbnltYWljazNSUWhjdzNYSE9tQXpwczdwTDdrTzhMTWxQREl0ck02WDNseUdIRXJGRW1Bck5VQkFwQlc4d01GbFFwUFZYOXhibFBkVkgxd2J6eUd4TW84elc1aXViN3M3ZlYyalNhdmoyZVJqTEVJc0FhS21zREFmajNiS011U1ozeHJTdHNoTG1VS01MREdXY0h6d2hzbE0weEhsUVptRW12WU9QQUEwUkU2V2V4cW5SM2RjRnljT05hSDJJMWh5L2JqMXNJRUVHMnN6TGlRZU90bkpkRTE4ckh1VWhybDVNSXRoelhlc24ySk9zcm1NSHRjd1BZd0tBa3JGWkdMeEZ5bGNTTUk2eCtKdTZOUG4zOS95bmFtNnV2dUlOclJETXYzK1BIMVVNMjdVOUtTc3RMbHY1d2Frcnk4cnZ4cDlQWml3enpvY0JaT3JRU25sVFUxelRqMzFobHp0emVnV2MwTXMyVDNJRlE4bUNFcmtXWWt2K05pTDM4NVN6b3lwSWtKbGRFNWFNZ2hubUhkOFdhZy96TkdCNGkyMHhEaTdUTGNjdXEzS1VVMkwxeDl1bXMvZVIzVy9uR2JlUGU2cTQ4MEdqbTdEWkRpc3VycnB4K3YvQUpWNklwNWh5NTlObUozTlplOHNhZUdLSnlEVy9walZJZDNaRlR4Q2xKdzVWejNQeTRSbjJ4K1NuL0JPR3Y0TkpYSlc2SUNjZXJDYlhURVIxRjBQSEZQYjRUVUJXci9qWHhqWGVzRUk2V1h1OXFSTmJDcU5sNGR1SjRyWi9xU2xIRnNYa1FEUkkxVVZHdGE5UDBMQXhPL3NKamF6eWJHOWlUUDhlcnd2OGZXa0dOejJKU3l1MjJVNjZQSVloc05YQlhuY3FYLzc0NDFKeUlqZkoxMG1vRWdXTzdNQXFUajVMc0duejZGc1JHYmwwYUhSeE40TXNob0xNZ1IrdTFXdE92SkJ3U2pMK09WSTdlZzBkVlBuN0t5dUJkdE11VGtJaU41SnVwNnVPc1g1VjVmR09RYXZwM3dTZnlyek1qMVlqTGF1d2wvUFIyekluSmRJTzFudkNiMjdlY0EvNk9pR0w1ZmM2ZVRQcVZqWVcvVnRMK1ROWkN5N3ZDSzJPTEdaVEZaeXR6eUk1YXoyVE03eVF4TVJPdEMxT3l0YlloUnl2ODVBNmovRXc1Uzdxek1kWUh5Rk95OXkyaXVZU3lOeGk3KzRuYnB3UnNCcjlKVEl6Vkp4NkJYUUpBemRIeDFZaWdpMS9IdDdiVURORXZITk9Sa0tpRzNHMlE5RHBnSjc3cWlOcGlhTTBTWCtJTll1K0h4dHY0ZzJNb2phVnhBTWF5NStMVTB6V2VzcVRRWjUwZmRTTFJyMmZ3OXBzNHE4ZFlvWUxGZ05MTGN3Wk9xa2hZdGpKQVV3TlU3VTF2NXAyY3l5VkU2c0MzRXZjaUZwMlg3NUpvTlRXR08vSXdPaGprN3pCQXdYU3lzcStNdVVtWmtSOVZiTW5yWlJIakJhYTdyYTJQNHNldTBrbHlZcm92S3doZ2FuWEhSSSs0cFA4WU4xb0s1S0dTcWlGZlpRUVN5OVR6T2ttVjdvM1BiSWtzZVg1SnZjMUQ2WWdPY2dpWCtwNjh1VkhreElOSHl6WldRYm1oQ1dKdHRwVW5tdW1PSW5IRFl4eVFxckd2TysvTHJQVFNQa1JlZ1hReXA2M0ZCS2oxOWxtc2tUMWF0bG5RdFp4NjJRbTZxNXFXeWxxeXE0NmRVK0tOaWNVTUxFeTd0czhUUlByNHE2SGxmQVdiUnBicUFhQVdkV1JIQVh3bVRZanhOUnVhRFExeXl3NVBmM0dWbC9qRjIxdnRKRTduMUJ0WCt1YUZxT1g3Z3IvaGxSUHNyaEswaXlsbXAvUXZTeGQwKzhtUEpCY05XRHNraGo1RmxtWHMvc3NzanBXeE15R2VCSEVXY3E0RkhzbnBYaVVWYlorTGdKWXFDUjQzY3JEM0ducTdTVHR5MVhzSE9pTGp3b2NCOTh3ZVdOc0ZyY3VoZnM3K2NSa0EwdVpOdGJQS1JUYUhrSFBQcnRDOHFDMStRN3dTV1N1bStLK1pmdnQyNjQ3cndwQ25PbE9XbnNaSS9rRHRkR1Yxc05JbWpxQWh4UXgxU0ZxTzhGUmVuTTc5WnhrbjVOdmdQV1NUUnlGTWNKMlZVWDQ2c3NZazZOTUZ0S1FKR1ZPcmZjVlJJaFZweVpTMTlWR3NWN3lQS1FiT1AwbWtzSkNZMTYwRTZrNWdZdDNGcStWK3FMbDFhQUdzbGhPL0Q3eXVXcm1yeExNV2lnRnRPVGhRQ0t4ZmxPakdpRVdYcXNOOTdwNHlIb3hIZFZwc2ZxVWJxb2ovSTJRelhGOFV1S25jYlFTWWJUMW5mcC93QW1CM3FJOTBxZjcrYUwvcDRieGZtTThza2ltUWZFbkpnOEpGYTRxTll5RWxhZEpXa0I5TnNTNjlDSU9qTkJKWTJEZW81eVRIT05tSnlES2V5YzEyeFZnNHNZRGVMcHZUcUdvdklXc3FocG9TYlF2a0MxTDdmakxEa0d6c255MTdaeTV2eDN5RnRIeHlROTNRU3JFaTluOU4rMStrRk1JalY2UU9tamNub0EzR3VxdXpCaE9ZN21adnhyV3ZteCtuZjR5eWNWZWNYdnJqK043bi8xRHd6NWFwNkFsNlJQM1BrcEt6MnM3OUNqaWwvdEZDWTZublY2azJHQnFZWTNQWVJVc2pmNFIzT2pVcU4zdmlEUzZLV3pzOC94MVRYbzBabEZMbGpiK3lodDdUVDhXSVJYb1RRdzFoMmJwWnFKU0NpcStpOFRQRkNXT1VxRXlnRVo3QnJHeGxzWnZhNWlvaTkxaVFxUmV3NFdZdEMxKzh6d3hjM2k5NFJlSE1wa0JVTzNKZk5uRW1RWjZjbmIvd0JTL0lsbWptVkgrVjMwa0t0a3pPM1BpSzlOa0dVaGtQd2lwZ0NCNUhJcllVa1h1MnRVaUtQczZ4V1dkdmowVUYyNjJsWGpLblRFVjhWWmk2ZTRiRklTN1ZXV0V0RS9IV3QrVlhQVFRDRGFTbk9DZWdONmJYRE5jU3V0dXZjOVlHUStKVDNOZDR4TkdkNEIxVDdCdnM2b09ORGJDWnJJeCtPcURNTTl1MG01YzRxeW42TXpvT2YrVE5ER29vWkUweGt5azJIc2laL3k0dnYxNzVYZFFxOXIwZW1DMWtRZzN3aUJMRnJZRWlsaUZobTdFSVBCMmkvUVdrNnAyUXlGNnI5dGlMdnJsMXRKYWFqOEcxUGdXbTZySVFUQmdRcjJTOHo2MXNuK1FXUVVQcGdLMTl3VTlYOU1Wem05M3lPZEgvNXduZVV6R0dVOXBaeHZnbkZOMFBJWjBNc2tSbWZPR21lOHlhTnczVDVudi82OGw2N2YyWkEvL2F0YkZGOXUrZjRyK2pNOGdtKzlZN0hQYkdCMGZ1QWcxVUNzUkp5ZE5USXo2TTE4N1AwMStiemRvVVEyU28yZU1zN0VwclNEK0tOTkVIM21rcm0wMUtzUkpjanZIMS8yVmV3N09veDNtUFZzZEZsVWIvSUsvUFZnVC9qMTlEcHIxanZGN1dVbGxNeU8wMkhISmRDOVZFbXJ1MHZqMUZWRUVRdWRBUU5LTS93a2IyOGs4cStWazhYcDZkWGhPVDl4YzhGTjlReVpLMEhUMnRxN1FtcG1oazZyZHEycmpDYTZQa3VTRDRpVHUzTm16NGhFVjVabFpzMXdWWEZjNkhWMDRSRi9jYjR1dEsvRmxYRjhTZkwrcHlxNzdYcVQvd0FJK3M2RklXY2owdHlaWlhzb1FLMEVlcWdTTWV0a2tiTTE2MDA3WkFmRjk0UEdCRjdRTnBuUmErSDhvQXcyMEdyL0FEQUw5MGtuc0txS1JMVmtpUmZqUzYxc1phdGlWOEt5S2ZLclY3SUtkTXgzbEd0bVJZZVZmTTE2ZjdUd0x0Qy9HSU1FWjZ1Ny93RC94QUJGRUFBQ0FRTUJCUVFHQndVR0JRVUFBQUFCQWdNQUJCRVNCUk1oTVVFaU1sRmhFQ05DY1lHUkJoUlNjcUhCMFRORFlySGhCMU56a3FMeFk3TEM0dkFnSkRTQ2xQL2FBQWdCQVFBSlB3TDBHdGcyODhvajRPVmJpL24yNmdoUnc0WUlpZGdFZVZMSHZEL2RSNlJUTGdjTk9lVkVIVytuaFE1U0tmeG9jK09Ld0trQ1JSTHFrYzlCVnp1YldKMkFNVGtieGM4MnpWb3Ivd0RGbDRML0FGcmJFVVgrSGFBL3pxYXd2NC9hamtoTUxmTVpINFZadnNyYWwzWWZWQzdSNUloMWFzSjdKSHVxMVN5c3BiMDIrejdkaVpKcGxDOFpQZCt0UnVBZTZ6TGlsWVo0OFJ6cnBibmw3NnlrMEdDbnhPUHpyNloyTmhJRUJqRjVNVjNudXI2VXdTUlhqQVBld1pLUW56cisxSVc2NXdIbWgwTG4zbHZUajQwRStIcHNsY1BMcU1oNlZjQW9yYXdZUjF4Um5rbFM2M1RRNDFIMzhCV3o3aEFuRFU4REN0bDNEOE9rRFZCZFdwWmNuVWNMS2g0VWV4MFg3WG5RcGZ3b1ZIa0E1QkhOVDRpbmpXOUVNaTdPMmc4SVlwcUdDZmZXemRwM3NWcTRSN3lZYVZNZ0hISGxVR2xvbjREN1BEbFI1UUZmOVZhaENFeklVNThEVnRQdmZZZG5yNk8zRnpmU2FJOVFQQm41YzYraWNFNmNTcmk4RWcvMUdsbzBNK0hHZ0taa2dNZ0R5TDdOYk1GM2RORXFtVXlOeVhnT0FPSzJYRkREOVRWdEFIUGllZExnVWcvK012OEF6VWZIK1ZXclN5N3NzbVRoTTlNdDBxeStyZldKV1pvVmtMaFBIajc2WG4wcGV0ZDRVTWVqaDFSdnNtclc2dUpiUUhGcmIzQVRKOGVOZlF5ODJmYUx3TXR4SnJERStkT083SVB6cVh0VFc3S09IWEZiRXRibmFNZDUyamNSQjkwbnNhUWZqU0ZiN1plWFdPRlAya1hNajNqQVllNnJ1S0MvUmNYVnE3Z0hQMmw4UldZNDJUTTBrcTQzWjhQUCt0YlgzaFU0azN5OW41aXRzd0o1SkdUVzE1bSs3R0JTM0w1UmpocDhkckJ4eXJiNkxNa0lqbGlFYkVnK0hLakk4SzJ5cmwwd2M1TklOZkQyczBrTXpYWHF0MmNuVHA0OUtqM0dEMzRJUm4vVVRXMDd4bzNQWkRNQ3BQbUF0SjdTUmorWnBldUtISHFhMlZkeWVhUmZyVjBBNkR0eHVOTExUWkhsNkFTSDVoV3dhK2ozMGxOM3BiYzlzR0pUMEoxWllpcldTV1hWbFJHbWZJMXFKNmdmeXJSQk82YUhXUWRtVWU0MWNvTGlPWnQyWnRURkc2ODJxL2lzcDU4dnZMYTNWQXpkZVhLbUlHZTFpb2pGQWdUMXl6YVV3T2VWODYyM2JBK0FtQi9sVTBzNWVWWXh1b1RqVWVReld6Skg5UTB1V2tWZXlHMEg4YXNRNDN1b3FqZDNyMXE3M2x0ZTJvZUlNdnpIenFDNFkrRWNWUXpseHhLc09WU09XN1BCSEhESXlmbFcwWEVjZzdVZTZ6cUhtYTdyM0RZd1A0UmowQm1qdC8yVVVlTmJucVJtcERjV3NZSDFtMGwvYUovRVBza2ZJMU9yUkwwKzJuKzFiUXpHamtKcTU0NlUzby90RG1oUW5qWjJxOXo3MktuSUxOMkpEN1FxYmRhbndtN1hIWjgvT3IyVmlHR0RubFYyVGh1d3VHWmkzdzYxWVc4VVRTSFVKVjljZkQ0VnNwUHJrY0JXMEFrM1VldHNEVy9qcEhHdHFwTEhiU0ZRTGRld1NEakk4YTE2VjR5Ym1ITGFCemJGV1AxMjVFWTlYYnB3Y0k1WkpTRHlJNm10bmJLbU9qdFdxeTlvZ25PTzZCNTFQTkMwYnlUM1VPalZJVzA4RkdlbEtVRnZMcjBidjEwTW5Ec2ZFY2FsbnRKSkZaTFg2eXh3V1BVL2xWeGZYdTRtMGI0UUJVWmh6NTlNMWI3bVpXMDNFRER1bnhIa2FsYU1rRGVzeStyMEQyZmZUS2RETDIvL0FLbjlLeHk4YXRvSGpEYVpwNUp0SnR4N3ZEclU0ZFo0ajJ5dkFtcnNBL1YxQndPdUt2bks0WFBhNmdjYWMvR3Z0MURZVzBiTzJpWEdYbSs5UkdTMmNxT0ZSOXc0bEFISTFHN0UrekdlTGVWYkhqc1l3Y3hmV3J4SXdud3ptdnBWYXZjb3VsUHFZbFlmRTRGWGR5MGNaeE5BWnNvMGVvamlvNFpLODYybFlXTUxMNm0zdExja29nNUZzS2VKcTZ1TGxZVHd1MVhkcWhITlNEenlEVTJMbTZuMXhobHpwaVU5Z0R3LzdxTTFwR0ovMmtjQkFaUEx5cVpaTlVlVWtUazYwMFNRU1Bxaytzdm9aL2Q0bjMxZHRORHVtZERKKzdJNDRyYlU1Rzc3TnJDNVgzc2RQUFBuVmdxeHBiaklQaUNQT2swSWdQSG5wOC9PbVhVeTluVDRqdEQ4TTF6elZ6dVpjWTR4aDFjZURCZ1JWdWx4SjdJM3FJUGdvL2x3cUZZUWg1U1NqUDRVUXo0cnBScllQMVNWbVl5U1RTNTE4ZURDcmZjeE5BWm81cHhnT281NDhhbXVMMk82c0JjSUZoMElYK3dDZXRiQ21rQ2JORWtTdGNjWHVPc2ZDckNNUjIrekV1cnFXVlcwcDR4amp4TlBGWVJTb0pJUk5wMU9wNUhIR29sWHRMeVgzMUZiSUpJeHFsVi9XT2NlMEt0bVdXVkRHdDFKSmlJL3c0OGEyTmM3MjJSWW5DWXcyQnpCNml0Zzd0WFBETDV4L0VQQTFlb3JRTndieU9mMHE0dEpwR3U4V1NQYmFzeVlQZDhENytGYlB2TjlPVzNsemNLRkhIcndKcmFObTBNYWRtVGRuZThPQkIrTlhva3p3QjA0NFVWR1R4SlBjcVVUcmdieGd1QUc1citueG9lb2w3Y1h1UG83UGpUam5RK05lTlcwczJpTTRpaDd6ZTZvcmdteXVFOVh0UnRZa2dLOU9mSG5VYjNsM0RxRnZCRUE4cUJqeXo3SXJZdGhzeUU5MDNaYVIvbHdyNlViTVBpcDJlY2ZnMWJKdDdxRmgybXM1TThQdVA4QXJVTWYxemNMQ0dtWElqVUhscDZVNEpQUFBBMUJCMlV3SjQzeVdBNFlQaGcwMDRsd0pJblFkaGNlUGhUY1lFeE5JNDBkcmo0MXRaa1BQU0ZiSXFabW1sbExSUzZTUkZwNVo2SE5iWE96cmlPZHQxZHRiNURBanU0ODZ1SEt4MmNNS0hXZURSdGtTZStvNVpaWkcxTUxlUGlmbFNZd2UyT29xOVc1aWNEVDZqTWp1VHl4NVVsMjBiTmlHMGp4NnlSaUJ4clNiUXRneXArNmI5S0lJWWRramtmUVBuNlUxQkc3REpOanliNVZLN1RCZER6RnNzdng4Zk9vdDdlUHhrbGFwS2FwYTRNUDNpY0dxOTF6N3NCUkh4M2NlZE9yenh3b1I2bVBhTWFZREg3WHhxZTVXRU9HblcxZkRTQWNRdnpxRGRpY1JyMnBzYnRtSEovQThsOEsyd1lXaXdpcHVoeFVlZERYcDVuSGVxOGdqY3g5cUNXSE84UThPSG1LVnZxY2pZV2FIRFlPY1lZRGl0V1R6Q1c0WlpwSSs5a2NNZVFxMGppdTdhUFVTNHd6cU9ZYXIvY3VrWmVONFJsazZaSG5YMWpScU9tV2Jzdjc2aVhRQWtGdmJScGxwM2QrMDV6NzZranZyUGVGWHNwSmU2d09Eb2ZvZktwWnRueWYzVjlDUVA4QU1PRmJjc21iK0NUVWZ3cTJkaDBlVk5BK1hNL2hXMFZTZVZITUYwVjFSYTA1cHc2K1ZDRTc4LzhBc1JFbU9EZjF5YXlacHljU0h3OGZqUXkxU1lyajhLWEdLRlJzSXpxdUhqUWZzRmQ5Mk5UZnhhQWZqVVc4SDJkZUsyWXl0QmJ0TklETG5zTHpQR2tSclV4eXkzR3Ixa2wwN0ZRRjQ4T1hTci9Ra2JsWFNRWjBZNWdFOFJqNDFkcTg3dG9CZHVHdlRxVlRqdTVGWEVtNXpyV09UR1kySTdTZytGWDdxQTJwSVJJZUI4Y1Y5SVpOblhGdDlqZzF5dlBQSHI0MXRGN3U0dVZJS2ppd1h4T0tqWlE2NlI0dDFPS21kL042WXF5OTByekZPV0VGbk5IYUJUd0VqdG5lTjlxa1JrK3NXMEthdXlXREQxc2hIOEpxMVNQZVNYVWVveTRDbUx1azRISjYxd3RMWndTUjdyZ1lMZ04yMWI3UzRyYWlXQ3ZkTlBHb1hNYVNId0huWDd0TlI5L2QvT3VTREM0bzF6cSt0bGY3Qm5YTkVBOU8xWFEweTdSamhiVm9FdU11dWRLdjF4NzZuaWFXSTRrRnZ4VUh3ejVVcnhsMU1aM2I2UzZuaGcxTHVoQXl2bWZneUZkU3VoL2dOU1hFbHVpQkZNc1RNR0h2STQ1NVZ0QlRhaUZwNzZjbkNOcGJUREdFSFVLY1pxekkzWXhyWWFkVldLYXZEQUgrOWJPUzN1eDdTUWdFKzhpbkpRdTd5NFBha0pRcU1ucUJ6eFM5clJIcWQrSjFxTUVyanhwRlpEd0RvZUhvdFgzV1ZkMUl4cVN0bjdpM2VYVkNvWTlsY2QzNSttNXQxeUJxMzQ3d3p5SG5YakdQeEovTDBESkxZRlptY0wyMjRoUDYxYXBabkhxb3lpNlp2dXNCenFSdEg3bDIvZHQrbGNmZnpCOEt2M1dHNmowWEljOEc4RDg2dXJLSkpWeUliWmZXdVR4Wm0rTkNKODgxbk9OWTVFRHo2L0N1Tm5zN1NpeDU0UExqaVQ0NDVWc3R0N0hKcWlsamNhcFJqOEJ5eFZqcGxZZHVLVWQ4ZENjMHQ1ZlR6c1o1NVhYRWFGbTREaFdNK0N0bWtJYUxpNFBoNk5ySkR1cmN2RzdwMkdmN0o4cTA0MmxDeXkvZTlsdmYvU2x5RVBHb0djd0pnek5IalQvRDUxem9laTBobFBQMXB4cDQ4eDUwT0JDbi9LK1ArcXV0RFByQU9GQ08xQVVtR1pKKzE4UjhPVlQ2ZnFoTEkyT0l4MStkV3JHV2UyVmlPZ0pHYXdveUcwRHpGVG5oSUR3OTlYRUc4SmJXSmYybmY5bnlxZTM1ZnM1RjdiZmNwZERiNlI1Rmw0Y1N4OGF4SGRJdVlMZ0RsN21IUDNWY29UYlNidFpDM05PbGJUdUJFcmF0eGFETzhib0Q1Vlp2SGp1bnFLazFhNEhVbHV2RCtub1pzZE5Kb3JEYjI0eHZKT0ZiYnRMbUtRa2lQZllZZVhETmJKdXNEMnJkMWxGT3FrYzF1YmN4bXJRa2VNRW9hbjAvNGlrVkI5WUdyOWxITHhhb2pHN0RHaHZaMXJqL0FKc1VtT3RjRG5nYU52YTdSUlQ2eTZtWUpKeDlublVvemNkbVY5T0FCMXhSTzZpQUNqeUhLdTg3MWI2OTJlenhvdzVHQ1UwZHBldVI1VThDYWx3cnpkN1YwMCtkYktSN21MMU9kWFBGV1FnUld5cFJhdVpPMURHR1ZPV29ZcmFadHlFL2JzTTZhbmxsSVBCdWxlRmMvUmwxVG5scWRrT2VCUTRyYjgya2V6TWQ1L090alcxK3YzTWZyWDBIbnRaRDdVSC9BR244cW4yaEd2OEF4WXQ0djYwOWxjNDZqMWJWcys2Z1JSaDN1SnRlb25tUWZJME9NbkU0NWF2YUg1L0gwSVBqV3pJSmczVjF6VmxIRmpvcTB2S3AwWW52dVl3RHp4azQ2WjUxY0dHMWpJYlZJMzh2RVZkd3ZjUU5DRUZ6RjNVRWhabng3Undmd3FhM3VJWWdOL3VvbVZpbnROai9BTTRWYzJzUFk3aGgvTVZzNVpwcEgxNzlPWmJ4OHVGU3hRUzZqbzNxNmxQWEZYVXFvcTZuWVJmbFd3cExvNTRQZXpkbi9LbjYxQkJFTVlXTzNoQ0tQbDZJMjQrRklSVmhMSVB0WTRWOUpMS3dYKzdlUWF2bFd3cDlwVEFmdFdYUW56UDZWQlo3T1E4akZCcVlmRnEyaTI4SjR6SWNQenp6cTRsdXRRMHdSa2pKWStkUDJUMSt3UnliOGpTOFJ6SGo1aXVCb1VUandvWW9JUFduVEFuSFRuQlBNK05SM080dFJxMHdFWVh6d2U4ZktwL3I5aElvYU10M0pVOHVxbitWV2s4c0REVXJDWUtxbks0em1ya1lFam9WaTRwcVU0YkE2ZkRoUkpJVW5MREFBOTNXcG1SZmFHUVZaZWF1UE9weEdqRERhc3NXcVBzb09YaFVUcnFQQTQ0R3JTU1JtNUxwcmI5bHN4ZE9kRThvM24rUVphdm8xYzdablhsUGUrcGkvd0F2RW10b3JzMjJQN2pac2U3R1B2YzZ2M21rUE4yWXVmbWFRZTl1SnJqNzZQeXB6a2NqVzdnaGdqZVdlZVNUTE81UFNrTHc0eW1qdlIrN3hIbFRhMFBLYVBsOGZDcEt4Uno1Q3J1T2E1ZVhYS1VBQkpML0FLVmZidmN2b3VWWElFMmVPQ0RWcjZ0eUdUVjAxanRmalZvazRpQVlReVBnRThSbjVWZElxaU9XTkkxSEJSSjNzZWZDcFFyYnpXQ3ZNSFJvUHpGRStWTjFwZ1loM2xYclZna2thTndnbUpDZHJoNytkYmFGaENZQ3p3N01HNUdnTnBPY2NUeDg2bWJVSkNzalB4N1hobW9COS92VWZuLzZCZ2VkR2tCODJxVGV6WE4ybVdjNFNKTVlPUENyclNzMHhqNktaR1h3KzFWcFp2OEFmUm8yL3dCUEN0bTIzLzYzUC9UVnVrZitCRnAvRTVOV2VjU0JScGNBaytYalVPaWNkMDNNdW50ZUh2cWUzV1hWeHQ5NTJzZU9lV1BqV2plU05uZE8yTXFQL00wZmhwd1BSMW84aG11OERrSHc2Z2p4cTBOdy9MMVE0Zk92b2Z2RlBUZllKK1lyWXMxaVM3c2pYVWZaMXN1bk9lNmVIaldxV05kekhFTk9wcDNQTStWS1JqdnBuTFZzV2R0UDd4VG5GTGcxeXp4bzlqMmRTMWJmRkRXMEZRbnBNdVB4cEJJbjI0bURqOEtKemJ1V2lSdVFKOHFlVGQyenlHVk5mR1hWVytrTVc4K3RaSTlhRDNmaUtQcjdWWFRlTzJkYW54b3BMYnk5cTJ1SGo3ZVB5SXE4VjVyZVRBbGtHZFFIRlNmR3JnM0c2VlFaU25GanJKYi9BRWtmS3M5TTVQUHo4dUhwOEt5QW50akhBOUs3Q2NueC9LazdYVitwOUNoZ1IybFlaQnBjUkR2d2RGSDhQaDdxMHdXcUlPV1M4cmx1cHJhRXNlWHdVMWN4VXhaanpxNFJKVVhVc1RBNWNkZU5QbU4rQTQrbVJsKzZhbDdUUityWmt6L3RRelNETkF6c085MnRDajg2LzhRQUpoQUJBQUlDQWdJQ0FnTUJBUUVBQUFBQUFRQVJJVEZCVVdGeGdhR1JzY0hSOEJEaDhmL2FBQWdCQVFBQlB5SEFORURuV3pNVEpwcXc2S3ZvaDdFSFdvcjF4TUF0cE9hWFh6RGc2dW9WbVNpQ3pqRnl5dDh6MEpYMDlqak1weEQrSlZxVlNsbThabFR3SFRVTHdQdVdRRzI3cjEyRzRPTkx3RGZhRlRVeVg5ZmNRRVBJNGdxdHRaemxuSkxsaHhuNFU3T2YwbFExSVR2S0dpYVhGT3lWaXVaVVdlRGJ5WlAxRlZZbnk3ckhLZFJpUjJxbEF1aXI0ZzhLQzF4MXlvT2wvd0RPZ0Y2UERLRFJYd1lreE01U1c3azI0Zkh4QkRlb1hodnhkZXlXdHUxTDQzQmZjYS9sYkExZ01kU29CNEZ2K0k4Q3VGcFlRNlRtYWRGOXp0NDZpQ3U4Y0VxdXRWcUN3aXpNd0pKaGdhVGg4a2YvQUZYR2tkVTNXOXpMakttNVZWWlFTUzl1dWprUGU1UjlUUkdQdXpMSUtWOVJ0WERGQm5yOHdjV1g0VFZqaG5tVzhrTC9BTVBQOFNwZFJsT2llaWFHTzJrMnJnNC80VWxZUGZlOEFYQXFVRXQwRGJ4RmJjc3lQQVkvVTgvY3JOdTg3dzBYN1RqWWtmNk5pekdWcUNJM1pGbE12ekd0Y3Z3SmIxdW1vQlVtQnhZeEJPYVAvYSsyT21WVkU1ZWRPSDh5a25nOVFZeUQ2bEJzcEhpbWNwc3JQaytadDJHc1FjclIyM1pNSjlSTHFHSEo3aEJkWG9ZN3MyNzhhWlNIbGNTalcrSUl3REdHZkk2SEQ0bnk0My9kU2xOdk5IL01vWWlyd05xT1pvOXRGVnBGZ1VyMkNZSExzaHBJTFVRQ1dVbmlDdmJPUzdKRVY4c3ZMN2dIV3QvcXdLK3N1bkFFVjVaMHRnNDM5UkhQaVlna2dmQ3c1dUY2MWZoY3hacStlQXlpdWpDcFZaMDRxRUJHMUc0d21uekZyYlpoOHBIcTRMcWh0U3pKVXNVVGFiUFNaYkpCTG13Y1YzcnpFV2R2U0kxR3UrbXc0cWZCUmdiMkZ5MnpiQ2ZPSE9uVXZPc0l2OTdDcmxqM3lvSDR0TGd6RDdOWEtDNEs4UnpvdEhMVll6cHRvSnljQ3VKVEhPTmM0Tk0yRDNEMlVlaitJLzBWZmJxODlYM05jTDdOWU82Tkh6QWJ3enFWNFFNWHlFQlpYMUNYUXd0a0RQRDlLMXgrZXBsNW9rOXJucEVCcjRVUytwaXM1UmZsK2pOd0R6S3E3VXVXMVBLdDhPVjlzcy9Bc254SFBQektIdmUyaFZ2bGIvRUNIb3VxcnhSM01lSCtNQ21sVzZ1TGIwZDExUUxyMjh3NjdNaUhFY1JBNDNBL3gwdldBRk1ZWWFkTFNDYW02R1gzM0hWWjByYmRWekZDT3UxWXM3bmFyTnhOeUJMdUd3TG5yTnpnbHNsdkFjVS9LWTZYNjQzdzlEVWJCMnFDUnpXTEdlcFdHbFN1aTUxZmhHSXc1dFF5VWVFdDNNMkZnYkxTdis4R0xkVVl2UitLYVpHMUtnY0xLN091aTkvK3pCbmF0MzBuczNUaWkvTVNNWExEb2FHTzl6QmJZOFZqeWpKSm1HdGZFQVIrRG0vWjlrcEVnbFZ6UmZPbzdBLzJPWkxlYXVhaUdBN3d0RXRkWW1nN21oRlhWdkROREtxRTZvQUREZE1zcThobmFCMU9JdEtkVTdIS3RWcStVRitUZGxkaEMvRHFEbXl6bG02LzJtWHNZK3dkVEZnT2xUUEhSelNLRjFqTkhNcjlPYjdzOEljb2pRT3lORmRxdTkrNHBUM3ZTKzBaNFFOelpQOEE5TkdBTmhqYktqZUFxYVNGT1JyRXk3bTdWOUVNZWpsdVdzTlhDZTZ0R2FpeldZQlY2U212cTc5UmxBYjJmQjhYdUp5SXpjSlFWNzBRZms1V3RzTEdkMW9Zci9XTGNBMzl3NkszUGN0UHRZTVJjQmJjclQ1cWJqdnhHVC82bkEzWUs4V2FLd3hDMC9BMnE4RmJ2cTVkbXBFNnF6bHd5NG83VHY0NUM1N2h6eWo2Y2EvODRnN3pFbGFXbkdMZmdkazQzY1hubWdBK2JZK1JJZWhvMVhvdVNWU0ZSMnB6VmNaalk3czRQSnpOMFJQR2VUNVVFWmRCK0QyT0ltR080YzNjSlltWFJPNVJrRHZ0TXBWdGc0Z09oK1pMMjUrSlhUU2M3T1dGRnc5TXo0SlhBUEY0dDFPZFIwN3ZxaWVGRHQrUU1hSnNxcjVidmhSTXF4cTJNcG5Wa1c0Vm0zZ3c2eFBUajJYZmtERWh5NkxIOGlwdkRVVjBHcWJKSWNrOFJPL3p5Sm5rTnh1ZUd0b3dYc1ZWWG01VXMrSmtxajJMNHhDd1Q4NzhsODlQZEV2Tk10YkhMUmxEYkd4YnNsZCtJUHNQZEJhV3FUc2pVZXhwUUdUeVRNM1ZNNWFWcms3NlpqclNLc2V4bDFnMFdRaXpWbU1IcjNHbEFIN2xuZG5YbHk3WFY5M0MzUlRsNnQxM3dKUTZHM2I4WG41NWxJdjBmOGlRNjF2cVhVN1g4RmtlNzdJSXhoZExXNmh3NGltb3JCMDB2NW03ZllZQjRSSmZ1SnZ2UXN0cFZQbURWNVprSWZxbW9zWnhLcnpkKzRtdWlzdnhPek1RR1hXUStRRHVWN2dSczdabmd4eERwVEYyRkdIS1hZdlUyVUtCUmEwcmJYZ2xGc0FOVTdCcHZ6TTVKSi9iR0dzaWFuVXdWRHhTYlNxbDcrRWdVZVlmcHJYMGdWbGY1RC93djlZOHdNMjQ3Z3R0NWh3OXhpdmxLbEdHL2M5UlcxN0pTK2ZaVEhSOHZjSEkxWUNZR3Qrb2N4QzZvUytZbzU1VEdSNU5UcTR0SDZYN3dtRTBEa0YzYlVjUnBEaGkvd0NjRjdXNmhzY3ZybTFJQVRrQnpVeGNyeVdOTEFoaGQzRlNublFOUVpxbUp1dEVaMlltampNTStBUzFvTFZSOW85elF1eExTWXYrbWd2OUNXN0p4dVNLd1FWVWh6ZmNCVmkrSHFyaGljeHJrdmJuY0xBdHgxSEtlc3dUVVBGL01UN2tEeHFjb0k0cnFlOTJ2NVRLaGRqb3ZReDdUS2NTdjhBd1FMUDBtMWkwVkRldHd5L3E0c3QrRzBzY3FsWUl2VGVQbEVPVDJoRVJJM1JiNXJVK3BhczBiQW01T3huSTFCVFdEQ0NaaE1EekZ1eDA1VTlUQTFERmI3VXN6Y0JwNTVhdkF3WlU1d0NlMGZ5bFNBdjgvSEpCK2pxdzdhN1ZGY3pIOW5CeDNqZzNTTkRHeWEvVDArSE1icjB5cG0xRzFic3ZoT1lWdXh5SDBEbXRpOGt2YlBxTzF2Y1FsaUZsZ3VOQ0ZoZi9BTnVFSnpkeEdnUUhMY0RUaEljWkJNKzBaYVZZS3M0VDRNWW1vN1BBVzhuSGlhRkZWclYzS1B6R0owZXp3ZG55cTU0eTFNR1JrMHU0ZFdEYnBkRHhHa0xMY3lETGVFb1BOc1dscUtJV0ZEQnlaM09TRmhvdGk1YzN6TGl2YjRFMFp3UmpsZVVsSDg0bUlkQUt6YXY0ZjNCRHRxWmZqdThYbnQyUlB1cmRzb1lYb2xYeVFRdjhnMHlxQ0hEQ2JMOHNZZ2s0a3Y3d2xuVk1wUUtod29rSm51cjVIMUNWbkdvMlhzb1ZjMUNTY0k4YTNJOG4xQUZMa1lyQjhCaHJ0eGdIKzBTZWY5bGd1ZmR4STQ0TWUwUUNyN3FJRDVzenhwdVZBNTZWejRZZWE2MitDc3kxRFFkbnhmOEFRVytQVWNtVEgwdWw5V1k5enhYNEdWNGg1NGxic2kxK1J4VXZwdmYwZjdobVFGNTVaWU5nckJRNm1SWkpxaHZuWC8yYkgrVVZ2S2g5d2lsSi9GQTM5Ui8rQnNCcUdVKzM5UmN6WXAzcFUvWkgwK01DRmNBYzdoMVNMcGJvRDZFclp6Z21tWTNXd0dtY2ZxZ1p0VlRKM1dPcGl1ZEZ0Y0RuK1dWN1R0MVhRK284cU8vSCtxVUNTbHFGOXlrU1czZTNaN0dHWFAySktjRDRTMTVKbnNEWXhONTd6cjNPR0pMMCs0djFUQjI2djRZdG10eU8rdjhBYmlQSGFsSHkzeExadVVmTVZOcjZRdmFtMDJEWlh3eXFPV1hSOGt2VmNMRDhYblltaHY4QXcwaGJ3SlMvaWk0Vml3RXZvZ2dXRnloNTQvY05OczV0UjF4a0plVGhvYWpvK2NQRVBVNGZ4TXJ5T0tabUphR1crOVRqVGluRjlFV1BFMnQ0cEgxclJHZkVjSUxTajVVUWpkMUZCb2o2b1BITUs1V0wwTkMzMjFtbk5RaXlCRlVsWVJLZTdyTW9xQlpiT0JZY3d0NXVZSHVya052c3VMdzB3YXIwV0xsQkV5VStsZnpOQ2lvSlBXM3R0bGJHL0UwTE5PZVNZUXJ3WmhHN2VYNzRKUW9kNXArMzRHTnF3dG03MnZ4SDl0NnUrZlViZ1ZWa3RLeFJ0Z3AwRmhFQU5MbkhTR2R1TjhINFBVb0phbHdmc0dXaW9qOXl4cGErQ290MUFpZVUybFY5bU9JSGtpeTc5YjFXUEU1bmprM25pTmNLTXgzaEh1UkhaU3Q1eVZ6QTBxYm0zQ0dqdXpuZ2pla05VaTFvclFqNmpwNFNZMks0OVRpdU9pbzNHTTZrSENmK0NyekdyOXNxL0JDVzNuSDRQMWhDa0lWdXZaOWxVNk5OQTZXdjdUZkFpVDgwTWtML0FLdVBxYm1YM0wxSkJsaldEa2U1aytFNEp5T2Q1TXhYRnNaRTV1VldONVplT3p3eGl6bGFZb3dLM2N1OXIyek9QTkNRclJycjNBNDQvZnA0VmtUNWdJbkR5bWxRNHNES1p3NkJRdDRYMUxQUU9zbCtpbzM0bGdPb1Y2UXZnTWt0eFdtVEJkVGdzZXZBeWdKbW5QTExmdXFNdWxvbEtHdXBWVEJZMHhmbEZaSzZsQnNndnlPZVhtTEtWRlpQTDZpRnF4ZHpEeWdCRUJhdE01WXMybkJzMGZpWDY5SGZSTnZxV2xpOTJabWpaMjlRbTVXWDh1dDlJN1ZQbjZRUDNOOW5YOXJmcVhSVDF1Y0JSK0V4UFFVblVhUDdSOG90TzFKakZxNlh4THlWcmVmeWJjSlRxZzVySnppRS9wQTNtWmNMcndGNnJYQVc1NmhWcTRHQm02dDU2aElXTzFnL0JPYUM0ZjdHWENzTkV3dGl6S3RKcmpBenEzbkE1eHhDdGF5OGU1VlROR0duaVpyT2lGTEtBb1NmaGlRcTlQNllrY2huWDRSZWZGLzJ3K28vYWtteHFXMnJqekJQdnQ1dnBjVFVYZEJuMmhyNU85dnZIeDM0aHd1cVJtclBhS3N4aXlCSVhjZXB1MkV2Y2VMNnFsY2gwRHpEVGM1ZGdzQUhLbGhIRjhyL0FNKzIvY2Rwcyt4aDJZWnh3R2hkL3AzQkN0TTVrbWJmUGN3S245TUVaaUl0MitRMy9wNnFXZ0lhS2F0VDNOaG9INXYvQUpQOWpMcWVoOU5WQUlJZDFjTGx1cndUWlh6S1FhTnk5MkRjYkpmSVRNTmNBeVBPOFBjTlZ5ZHk1eC9WZ0FUbisxcy9OSytDZi8vRUFDVVFBUUVBQXdBQ0F3QURBUUVBQXdBQUFBRVJBQ0V4UVZGaGNZR1JvZEd4d1JEaDhmL2FBQWdCQVFBQlB4QS9BRmdlTjV4MzZwZ1R5QzljVkxuM1pEcjVBY1N2TmY0Z2wyU3A1cmlsMmJxMGhHdFVId0JsTTRVemJUamU0R2tUWmtMeVBPQzhnd0ZZaC9veUZlVUxic1g1bUVETlgvV0pUZU1GREFWMDRZNTZKaDFDR25naEtNT0tvSzJ3ajcvbU5mWWZybFdVTUQ5WkZlYnJ2K0dPOGZiNU56SXpBZVdCblRTaEtTSGRrUkJHSytMNEtYai9BTGhSQ2NZOEZnbys4UkRncCtOWE9vZXlST0h0TUJ3VUxrbHlIZG9jSCtobW1LU3FSTmdwamI3UVUzdUZ0aGswWDI0UTZ1TlRnYnFZSXNQUlNvZkZ6em1FZVRHejI0YmNDWWdCRndRK1IzRHJHd0F0cU5FTkNhUXlzSGFuSTNXTFFxb2ppblU4Z1lEYWJPc2RPb1dadmRkVEJicU5MVUt4Z2lQTk1HaXFCWmtQL3dBVDFpMzNRQWFQb3hWOEtFN2laMGZJWEprZXNhNWRuNGc0elBJRFlobk5DSUFlVE5VZ1hWcnVoeFhabmhybHZBd1hTTDI3Y0ptS2pKdy83OGNUSmdHOFFjblpVWThiTytzQ1FPMUlHUUFDTzd4eG1IWDVnRm16d1hHVW9laGhLeEdrVHZJNFJNT3pwYU1MRlk2dUNBMXFoV3dQR1J4MHZlNlhUV2dBM0N5dG1rQURROUgvQUppdTFnQWcyQkNxNDE1MlgvRmpDaWpPQmRkUUMrTW92RFV6M3VGSG9RS1lsRWtJMTRqRTV2dTRDdHdrcnE3REUyaEd1R3VwOGY4QXpBK3RLblFEOS9wVE51SE0zeElLQkxHT0dhZGx5VEpqN0VJVERYMWtkeWh4MVpwMEFuRDZBYnhhZ3hTRlFBNlc2c1pSNmlhUytDRk9xQmkvK2VvdW1hcnB0ME1uM2NKSWFxNkV3UURFY1dmdXNUVzNDaDFhY2NoREh2OEFKOGNiZklqWDh1V0RwdUY4VG1JeXh3V1hicnQ3WU5QRGplNUVFODBxUzk1RHVrSUN5VWFqenJaMHh2YUFsZkl2S2t3TG82Y3RVQklSOFZnWCs1OWNlbDRTRGdzdFN2Q1EvaVpDanNnK0dRQ2tDZ0o0TDV3U252MnBxSDR1UkRRdWo4NmFmSTVRN0JBUytFZlNZZEc3a3YzRHZ6OHpUMU1UVFk0eWcxQnhVcU1xOWpFbWpRNllBb1F3aUVvblVsVUNKU2lEOFlQUytXVmsxQTl0Sk4yR0wwYTVpSjFNUWh4SmptNnZwREN3WHR0WEZ0SUlOUDhBNGVUemhlVGtyY2dubk5wWUZ3MUdzZjVqRm01dURPeURXeGxUamxzWjNMblBrTVR6Zzk0MXc3S0NRbmxha01ReVE2eDZyMHpzVXlTaVFwT0VYdDA5eG8yRWJwTW5BQVhzRnluOGp5VFBiWTgzR0ZCM1EzaUo5aWFqcTVzcDRGcWFqSnBjTVoybEpNMWZmZkU2Ty93UEtJcTdNVmc1SmJ2V0swUUVnNDROY3BVcjhpc1pucmhrQjFmVm5BT2ZiWmh1bXpUKy93RG1HWjJNRVE0YWtHRG1ndDZQZUMwUTdCdnZKYlJVNmdnMkdMenJGb3ZTZGhXOHRtRXhtT2xhNkEydlk1ZEtQdzJocDNvOTdCZ3dNYUl5bGlTRldzVndpR1VXc0ZHVXA0YUJneVk3cDRtR3puZm94NmJVcDJzTVVWU3B4aWM3dEUzY2tka3JUYkJHUnJFbE1xNGxRdzVCaWhTZ21MbjFBQUhDUlhsRzRHYUNVWEJ4UVRjUVBnaElsSmoxQldpKzB3TEVJZ0JTWjhZT0lNcVBlb0hCS1Q3NytDSzhPcWlTRTUzS2RWY0o2SU9KcHRlNDFJZGlFRk9naHM4WUJOV284Vy9zc3hHMk1XbmRyeldCNU5KTitNM0YyQWVaZzFhWTdVU1BnZlRqVURvTFUwR2d2ak5xc2xBbjR2Zy9NaTdnR01xcHk1aEEwaHZxaFRhMjF4cHV2dUhiclVTcTNobmIzOHQ2YnlCVTRybWlLemM1bmdXcVhMR3RLMVRmNG80VENLc3NoQlNHbXdYRUtRSmFOVmVVSXFZVStNMVA0YWlXRHNJemVNUEN0Y3k3WUJBcWdZTWJBcXFxV3pDU0J3ejRmRG9xaTMySHhqSDhrYVE0MVZYZFBjVTRTRTlCY2llRXBXSmlWRzZvb3dEd0Uva3hqYXRxZXJ2RHhZUmVoUVE3QWwyZGNWbHFiYit1ZENnM2FGajVNRXd2Z01XYVFMRzkydVI1VStFdjFqbVFKQ2N6dUpNU1FwQU5nSlJjUk1PblRhOG1oRFR2RzExaWlseTNqVEV2MmhyWWRRa0JmRm9HSHpXUzJRcExoUFRWQXgwMFIyVUlqSmlLenM1bXd2c3YwNGYrbXgxYzRDR3B2dkY2eGRSbVVlb0l0NEdNelJZQk5jSWhzN3Nnb0FZenBsNXN0RFF1YzNidGdnZkNhUDhBTEF2ekQ1d2l5eWRBbXdySkFyYzNFQXIwaUFCaW1TZDFLVFE1QWRNUzJKbVpRcEhmeDlEanJyaTRsMkhxMmNiTWEzSUpGUUlEWnFlTUlNR3Z2V3Z0SStNcnpjUWVISHFDNUxINXhaVnFRTUhCT0pkZkxrLzFKZUtrNzR1YTJJWFNhQnFFQ2pldU0yS0NVTEFNUGxKVFF1R0xKdkJhOFpvRnc0NGFWQXNPb294bWVXSkg1OUZNYzloTjYyU2lkTUF2bUF4dlhOc1RmaldEWEx2U3g5SlZPamJjdUVjQjVoc0cvYUppKzZNdlFhV1BDSVRnWXY4QTZsQklnUzdHUzU1Skl3NllEWTh3QXN6aE9CZ0VOeGs0UktRMDNZRkVhSlllWHR1dmdRNkNoK0ZseGZGbC9XZlVMZ3FUMXJqdE5vanF4eDZjeUJ2U1RxUE1VU1l1K0t2eUFtRVhQcnErNW5WK3ltcVlUSC9jRHpnY1pFbXhEZUVoWDZOUGx6VlhjZm4vQVBXYmpaVld2K1MvNWpCVlJ5b1lVZkdhR1FISXhqZXI1dTNzMzh5VDJFRGJ0UjZDOTZyZU52S1hZQmp1T3BMMEw4WUVIRUFCRnRpQVQvTU12dEJTRlQ0UHNmWm94eEFBNzFYVk1oWTlycmJteUhsK1FINVdqT05Reko4VUFJZkN4ZVVOb285b1lVRFlvWHdJMTZPS0pnQ3VQTDI1Kys3NU1aWUVVUU5KSUpNYUJjUDk5aThTMVNPZU53OTdYdUpNb0tHdE53a2MrRnBDc2RselV4M1ptaUFQSlFaYlZLWnJSbVlpS0p1c1BrTXluamEvSDg0bzdIQnN3V1gyelV3dkJKWEVheEprK1B2R0dQTElOdStENjRtWHR3ZTY5WnkrMWVjSHVnSHVjYXlhUmF3emRjanB5bEZJRGhDcUxtN3FJRHYrUXYxTWZpNk83R3d3OU9HclIrWkdFQWFuTW9HRHZtTVZKdmdPNHMxMG9SQ1M3V3NGaEhWcDM3bi9BREF3cUQxS0lLMmVUNHl1MlMyNGlpQlNoU29tRit1S1Izbm9iYWE4REsrTGhmbEJGT29pM1pPb2xHbmZMMWM4ek5RbHY5SU5zS0xTc01MSzNBUVhUOWhCUURqY3dHMmVraDBWaGxlN1NJUWhZc0Rzd0ptbzZjaFMrdFl2N1A3S2hEUUphWkVjcFdydkwwbkc0bFNlOGFMd0FGR0g4b09pdHdWZzZUZzRaQ2NWWmU1WVRkSVBXUWp2MjJCT0hsU1k4eWRGMDJoNXJqRTVQYUNLSDlZTWxhbFBkNW9RazZrUEdOblI5YkIyajhPSkRsSTFsWWt5M0JuaEQ2d0x4UFBhc0RQRUlUQ20rYWhwRUZSZHRyYmxNVnBtVFNSK2FDTzNIMFA1RVNoc0VMV0NaSFZ1Q3RvYW9yWndTY0UycXBhdWVaV0llUTFXYUhTaFlYWWVjV3V3Q2RkTjlaeEdSTDZibmRteCttU3FQdFNLc2tBU2NINEZualIwMmFQY2VZS3NnbzZoTjBnQjBtS0FBQytyZ215MTdNRUtaZzArTTcwYjVXSXY5SUhNNzVQTVo0OEhyQk92SWZ3QitqVEtoaEIyVmZ5NE5QQjBNN1VhSFNBL2x4V1B6WTlCeUJ4NTdERkNUUkFJR0JJNnhjWjcxZnNzN2pzRDJ1RDN1cXljd2NlbmR3d3U1dEFyTHVEWjBHVFpielFVK1VoWVU0K1pBcXAyZTRmREd3cCtpaVcyRFZwZll4YWNTeVFtbDFob2UwZGFYNHdwQktLQUVhR1ZvRG5VcUJERFV4TFNMc3dlUVN2dmJrNURiVXpaK2tjVHFpMHB2QWJFeFJscWdHOTB3MG8yNHZQT1hQWXQ4WkdCdEhxYi9hSDg1cDdYRzh2S0JHTHZaalk0ZnRQbkN3VU0vdkFkQWhHbWNkMGVDeFI4UEJGeGlOV3lhbWw2d2NhaUtweEUvd0M0bmQ0TE41T0V6OVFEYnZFRXRkVGVXSTErbVpmb2dud3A1d0NwSENJYlZVRXpGYTJQSTZzdmVLOHZrb0FIV09MM0RoOWZNVVgzckUzdURlZ0x6YmViWGs0YlhGeDBuR3JDYVJKaEQrcjFackJGOHRTQnRsbytnTmx2VXdIa1l0eUhOWWdWQW9kcGhoVWVDQ3hTQTg1ZnN5QkNEU3JvTDhCZkpqa3VCMExqeXluUmEzUnM3aHk1QlYyaXNCZEI3VTRqdXB3V1U0aXlrdzNQdWhwN1hmdEdQVHRGTCtoWUthM1dLUFRXK3JsVFNHL3Z5WU9zSHFlbGhLMDFNY1lMYXBHNHQ5TVd5ekUzdVB4cGord0JDbHNRZEtPRFIyVDJFTUZWVW9WckNJRnJDdG9Sb2dKUldCdkhKZDY2QjgxUVhIZmNEeVR3K0RUREs4eG9VRi9seEtJamFTY01FUGN4Mnd4SThyT0dSMU11REh6VlJCU1Z0c0p3b1hrWWxyYTZ6RlB5dnJseFpPbWJIc0pESVY4eHMvbGptZkRxTDVWVjQwVEVFVE1QWW4vTGhNZFc1YW4zNnlOYlVpTWJoekFPa29LdFBYckhpckRPeXRST0dHbHNUNFJRWWZwZ0ZaZ3N4OGY4c3hSYTZXWDJnL2NEKzJ1SWcvM25CaU5SM0RaV2c2eTYycGxXcXlNdDhaMXNzaitBRmZRNDBJVWs4cXlvV3p5SDJQVENMMk54L3JYOFlCbjVvL2daVFRnRGNmT0xMMlVwbFdwRVFWUE9HUXpDZFA1clVWZ3dNODJMN2lpbUdUZkhnK1dYdTNJazZNQ01ybmx0aWhPTG9neEVlTEtLMkpDZWhJVFJpQkh2VzhBSm9Jdmwxa2wva3RZOWtJRlFNNnlieHorZThpWXdNNGJkZEJWYjJpZVhJNHdrc0wvT09YV0lsVDI4d2xXalhYK0dUbDZJWWZiai9PQkVSZjRmMGZqSkJZcUp4NnN2b3pVR003NnJpNVVOMnNBZzBWTnhseHNkaWoxSFFtaVhlVjk4RGtvSTlLbjVsd085ZHRlSWhvR3pMMThTTVI5UCtab29TTFFudVpwOGsxUVBvTXBERWlFelMzMG85U2FqNXdNM0ZMN3VvRnZvRCtDcmhtWWh6V0cyenJkcUE1VW9BZVFRSnBvVlFBOEVKY3NnTDBYU3hRWFpNN25LUTBNRm5EMDRLbW96VnFORVlnVURDWmxOVk8rNFh2Z01TdzVzc3F5dXlyN2NjMVl2QUdLaElkTTVnNWdTK3ZrcitHQkNBRTYrM1g0YzI5VmhuNGdSWVcrR0FmRkpQNmVPNnVwR3IxWC9BRndLUmNTbjg2Zm1LTEQ4VFI5ZXNXWXZScGdvSHR3RFFKc1JLSmoxaWFkQUpyUkFpY0Fjbng1eHZZYTluakRuZEd4Z3lIUTg0TzNady8zRDIrSUdLQlcwTkg3aXkyQit1c0F0SmdnY1V4YUVHbHBpNkxRVUQ2SHZON2gycTZoSG91TXdmeTdtL1BCNTB3Y1U2YUVVaUxQbERqOEdGS0JGdGhES0x2TDVvQWo4Wkt3dDF6VHJlYjE0dFNSRGFBTnNIVXg2bEdiN21qdXNtaTRzYTc1eTRXR2FZbHgwUnVvdzNhVEMrQU9OMmJrWWVDOHY0NW9QelF0SDBHakVjWERIYS84QXdLUk8wMHA3RHppUThOMTBmeGl3T1llaEdpY1A3Y0lPbUhOb1JPYUFlemxEWlVMSjBJQ2hsY0ZEQkdRK1ZkbHg0YjFrdjd6OVJsMTJ0cVVIeTM1ayt4dzFhd05PbUxuZ09Iem9kVlZDQzJBOTh1S3N0M3J5Z0o4V0t0TUVUV00wT2lHeWVBMTZCNmQ0Uzd4VXRaNURLdjQ1clp6TGFXUVBMaDhyNVIwN0tRcFdFZHlpWFplOVMxZ1FjSEUxVHQ0aVltUDJ3WE9LZVNTcllGREc3dXZaMFFiTm9ZbFl4NkV6QkViK3MwdjgvRzNSNEQ0d1k2eTk5K09iamhJZXJuakFTQkdnRkF5NzZlYy83QnhmNDhEL0FDVCtzdFI2RUQ1cVAzRHNLSkk2NFVKWEVaVEJzSURvM1BnZUprR3ZqOGtYNHVBc05aYjZVNXlEV0xwS1ZRNHBjQXA3M2RmTktoSTZOL0o4ZEJ1TnE0QzVLUm85WjhRTEVtS3VTQ09uT0thVUlwY1ZxMExuSEUveCtOcUZOVlZYMThrTVY4TnVwN0I5RWI3VU9HNGNNQ3YwK0QwWTdnRW9LcHZOTWpFbWVoSW1GckhhMGxWS2s2MTViT2xteFh1SENRTVY0VEM3MW1yVUlrMTFROURlRkwvcWRzL29iOFlldVNRSmRNYXNrT053aHlJYm8yK0VSelRjMUpzbzZPRnRNL1dEek9sdTc0eHlDNzNuTU5LUUMwcnhNRzA1VlQwKzhENWdWZVp6ZXo0RnhhazdhaEp1ajlRUGVmL0VBREFSQUFJQ0FRTUNCUU1EQXdVQUFBQUFBQUVDQUJFREVpRXhCRUVRRXlKUmNUSmhvWUdSNFNPeDBSUWdRbExCLzlvQUNBRUNBUUUvQVBFWWM3SDdRNGg1ZWdSTUxJcEhhTXlvYW1FaklTSTZVc2Z2T1lMNEVUR0Y1bWdkekQ1WUhFY1k0ck1oOXhNT1FPSzdqbWJHQlJNbjFHSXVzMUd4b2k4WEZPTXFUVVYxUEN5aEtFQVdJTis4VXduMEdPdXJjUkdaT0lydGZxakcrODJFeEtPWUlZeHFYdjRLYU14WkE0M0lGeGNZVGNHWnRtTXhnbHdGZ1J4ZHhrSXMzdEZiRlhNS24zZ1V3S0VGQXdDaE16bFFRdk1IVVprQlZUUW1KMlpDVEttV3dJQlVWYmliQ2F4Q1k5d1Rmd3hPVU14WkZjVnFCUHRPcFdtdVkyS09HSGFaOG1Rbm5hWVdBYW00TXlZbVJwL3FGQ251Wmo2d3Jlc1E5YXRiQXh1c2Roc0kvVVpXTm1IcGM3Z3NGMm1OZEtFR0x2RndQbU5EdEU2QTk0L1NqRXBZVkxvVFZBWWFJaFNwWGdCT25MbnVKMVNpZ1lLcUVJd293WTF1b2JWS0c4UVdkNFVDaUxnelB3cGg2Zktnc2lwNVo3bVl1cHdhTkJQYUlFRmdqY0dFb0lNcUt1MVR6bjdSeTdyVFN1M2d4b1ZCdEMrMEpnV0FWTUMzL3dBTCtaMVdOM1VSTUt3S29IRUNnWE1weDNzWmh5MC9xTzNlWWtSTjFYbjNNWTZiSkYvSmo1V3lNYTIvbnRHQlhmZVkzbzFISURlbitKcUZSU0N2WVE1SzJNOHkxcWF1OHU1UU94bmttZ0lNSUpubGdMRFhoaEM5N014RlNLaktjZVFyRk84ODNwTVlyVmZ3STdveDlJMis4eGRNcHhNQ2FtSnJYaS9rek8zb3FnUHRDUU5vU0d4ZW1MN1FXZG9WYlRxRXhLUmRDUGpZeGNWQ09wUnZBR291VmU4T2RLcFkyUy9IQ0xUbTRNNjRqWm1icUJrSU5WVTgyS0VZYzk2aWRPSEpBM2hTNWdWZCtUT3FwSy90M2dVTnZjclJ1RE9kNEN5azFCa1FyRVdySkJpQmZhQUUzVTZuRVVHKzB1eC9zSGdnMU5VYkpTVUR6N1FJNzd5c0k3M0x4KzBYVDJNRHNxNmUwZDZuVEFsRHpPc1E2YnFJaUJMTUFYc0l5RU1BSXVQK3BSRndkT2pPVzQzUDVpNDhXSmJQNWo1TlZtWTNMblNkdjEybVlMa1RrWDcvQUdFcTVmaUlJb29SUlpMdEhZdDRpTEw4MXFVUkZDclcvd0M4YkdjcUVDdjEvdk1icXRxWjVtTld0UlBOSm5Ub1dKTlhEblhFNUFHOHo1RG1jM0UxS2Foc09EVnh5eFhjajRFNmpBWDlZK1Q5dnREOTVvUHpOTGUwQW1QR1dQRzBvNnFqbnRLSmhIZ0I0ZE0rS3ZUeng4OS94TjJIOFJ5NkN5MHpZZGU0NThFVXR2TUdJVlZSV1N5QUwrLzh6T2d4UHFBQkIvRXhuek42NG5wSnFVS29WQ2FXbytQSGtPL3VML2lIcHpVOGhoM3ZqOC80aTRNYU52dnYrNG15NHlLdmF2MGlHclBpWlFnTVVSTWo0c3Ryc1lqQjAxQWJmZU9vQnU3bVhKZGdRNnJKblQ1VitudjIvd0RZV2RCc1lRMlE3bUpneUkzMm1UQW1SWm94NE84Umt5Q2hETHNHb3QxUjhCVVkya0hIZ0FBSXpSVFVKRXVPRUluU3NOSkZYODhUT1Q1ZHo2VitaalhuZU1BR05URTRiSFJvZjNtT2daaXlBTnBsQW1aaWI0aTZsNDdTL1RNYWFHTncrQWpmVExnbGdpQUVFd3BSaTR5VEZ4N1FZeFJtSmY2aEZUcVZBeGNSdHp0QnJ4bTVlKzB3RXEyd201YXJsRmVJcGF0b3lzRFpuQ21ETlFwaFBNUTl4NFVQZVVaUnFHRHdXeDhTaEFsTE5odEE1WlowL0pNNnFqaW1KQ1FhZ3hBY21MaUFGd0FYeE1RTndnR0lMSDJqR2wzZ3lvUHRDNEc1M2hURys1WDl0b2VtVGtHb3dkT0d1RE43d1pSMzJqMGQ3aWVGa1RXWUhNQkppSlV2UUtITTg5bHNIOFFPRkJBaVpkNk1zTzEzRkFab3RocVVSbGNFaG1BK1A4bUlpcDkvbmVNdGlFSXZNT1pCeEc2a2djenpYeWJLTGk5TG1mNmpVSFM0azUzTTh0UGFQakhhSDNnSThWOEVPVmt1YXlJRURxYjVpb0c5SmhSbE84VUU3VEVvaWtvYkhNdG1iZU5rYkY2YnVaT3BKSE1EdSt5aTR2UzVtM1kxRTZUQmo1MytZS1VVQkFHYllSY0pubG9JMVRKakpOaVZMOFJMQ3JVVkMwWEd4SUpoVFE4UkE1L2I4UWRPZ0VZQUdoRUFKTnc0L1NTb2hWTE9vd2VRdkMzODd3WjFaYXFJZFk1aTR4VTBnUzVaSWpNSlphQlBlWmNJSDA5bzJNam1hSUZNQ1JtRmJ6RmtDZ3hjNkJycWFpNzNNSzJTWU9JVnZJWXBHSVdabDZobTJFQ01SWmhSUCswRmdiRzRtVU5GekhUUE4yM1lURzZ1dHpzYWpRTVJCbFljd1pWYU11b0dISGR3WUx1RERkaUlvY1daU294MHpIZ0RpeE1XQlFPWW9xQ0Q2Mm1kbFhIVVFBV3hoWXNkNGVJUlppekU5N0htQVl5L3FFeEVBSFNJK1poekF3YXg0SUpRTUNnRFY0TG9STE1aMjdiVC84UUFLQkVBQWdJQ0FnRURCQU1CQVFBQUFBQUFBQUVDRVFNaEVqRVFFeUpCQkRKUllTQkNjU09CLzlvQUNBRURBUUUvQUU2UmFPU09jRWhTZkxrT2ZOa1lPU0p4Y0VRZXlDOFdrU2xaR1g0SXFmNUk4LzhBVGlwWDhNbkRoZndqb3N4TzBOMHJJemxMNUh6Rkg5aTh2emprb3FpVVl5MHh3aWxvaG90azVQb1JFaWhlR3JST0hGNlRZNVgyWWI0a3FVSFp5aFdpRTdWRlMvQW1peS9FRmIyTEZCdTJ0a29wUzE0eCtMSmRpalFreUg4SnhVa1RnNHE2cEdCKzBrbEtMUmloQ3JKcDFhSVRVa0xFN0hndjdXTDZaL0xGZ1M3WkREQ0tJNWNhMDJOMi9IcXFDc2YxWDRJNW5OMHl0aVhpS29UVC9oa2pIOE0rbmJWcEd4Y283UnlaR20yUG9qS3g1SUx0aXlRYjBMSVN3NUxiSHk3VDdFcE1jSk4weFlrdXlLakY2RUttSlcyelZDaUpGK01qcit4Z21vdGp5UHRGeWZaYk1YT3JhSndianJzbEtUMDJSVjZJUVVGdlpGcVE0a0UydGxGU1Ryc1VHMW9VSFlrcUVxTGFkb1dSYlBVYU9Uc1hpZkpERlVvV05hUFN6U2QxUkNNbDl6SlpYelRST0tVdXpFdmRaRzNzaW5HZHZ4cENhdHBrcWZ5UW1sZER5VzlFR3BJaU5XUEd4WXB0MnlNSzh6cFM2RmlsTXg0dUNlemgraTVSSlpIL0FJS1prYlBwOTJYeFZVTGZZalRSd2xZM2ZUSFoxMllaVyt4THhyK0RkS3lNTGRseGpyNUl1YktuK1I4cTJWRnUva2pBeXRXZlROVzBTbEp1a2U3NVpHZHhiWno5bmRIcXRSb3ZKTFNJUTRrb2NWZlpoazRTK1N4SVNFUHJ3OWwxN1lrWXFJdWloK0V1Q2JiRzdka0pjSFpLRGZ1UW9UY2ZjeFkwWkpVcUZqYzQyekZEZ3REcHF5Tk9MMktrMllzcSsxaS9RcFJvMVJaT2FTRTZWa0kwdkNZaCtNa1pwN0k2SXFMdWtZc25CVXhkTWNrakpPMlZKTHN4UG5IaSt5ZFFWSmliU1pleGQyUXlUZ2lPVk94WlVQSkpvVGJuM1Evd0lzV3hGZUhGU2hzbEhpMm1SYjZNZU5KV3lOVlJsZzZzU2krMEp4eHJSTEpCeElaSEF1V1VjWlJFS05QWSs5RkZNZ3FrS3I4eFd2Q1RLSU9TTXkzMllVbk1XNWY0VGswUmJjZGs0OFphMnlRNE9yRVlrcUtpL3dEMFYyVGx5aUpVaGVJOWlWbEhUUGdVclRISkpEbXhUZGsyK0ZtRDcreUgyN0Z4a3FFblJrcHgyVXFJdTdYaUR0VVIyejBVN3BuQ1NOK0xRcXNqNGFvWmRXamxiOGVueFprMHFNQ3FaT1NYWjZqZlE1M2FMMFRyaUpqb1J4azBMOUVaeldreFpwZktzaTR5dlZIcGFIaVpEMnFtUHdqaWppaXFISk5DWExiRmppMWFPTjdZNGEwS0xqR2g2anM3VzJKeHEwckpOdGlGYjZJNHBNaGdzV09NRmJaNjJLSFNzZjFHUjlhUFVsVldZNXRPbUx3dk5iR29KaWdtaStBMjFzVFRXaVRSS1IyVWtuUkdLbVJ3b1VZd1h1WTgrS1BTc2VmTExvNzIyV2prV3lKaW5TOHJ5azNLeHo0anlLblFwY29VT2JpdER5U1l2Q2x1bVJiUCtyVHRucHUrenI0SEovd1M4Y2pIa2U3K1NNazBKNkxPVCtDRVg4RW9jaDRadEZLRUJ0OUNGcUpUbDBReEpiWnlTMGlNNWZncFBzZVBpT0d6aCtocHhZaU5OSEJIcG85TnBDZENuUjZ0SHFWVEpTY1hTRTVTamJKWlhGMFNtL1A5VEZGdGpmOEFWRVlxS0Y0ZXVpY2VPeTVLT2lWM3NoQk1VWEh3MktSZDY4VkticEVZcDM4bi85az0iIGlkPSJnIiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIvPjwvZGVmcz48L3N2Zz4=", + "description": "Displays changes to rotational speed data over time visualized with color ranges and background.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.rangeChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.rangeChartWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: false,\n defaultDataKeysFunction: function() {\n return [{ name: 'rotationalSpeed', label: 'Rotational speed', type: 'timeseries' }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-range-chart-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-range-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Rotational speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 4000 - 2000;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 4000) {\\n\\tvalue = 4000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":2592000000,\"interval\":86400000,\"fixedTimewindow\":{\"startTimeMs\":1700577227898,\"endTimeMs\":1700663627898},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"dataZoom\":true,\"rangeColors\":[{\"from\":null,\"to\":500,\"color\":\"#2B54CE\"},{\"from\":500,\"to\":1500,\"color\":\"#3B911C\"},{\"from\":1500,\"to\":3000,\"color\":\"#F89E0D\"},{\"from\":3000,\"to\":null,\"color\":\"#F04022\"}],\"outOfRangeColor\":\"#ccc\",\"fillArea\":true,\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm\",\"lastUpdateAgo\":false,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:cm90YXRpb25hbF9zcGVlZF9yYW5nZV9jaGFydF9iYWNrZ3JvdW5kXygxKS5wbmc=:cm90YXRpb25hbF9zcGVlZF9yYW5nZV9jaGFydF9iYWNrZ3JvdW5kLnBuZw==;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAADaCAMAAAArH4/3AAAA7VBMVEUOCwAKCAARDQAUEQIXEwUaFgciHQ4nIhIfGguHfWgcGAkzLR2KgGs6MyI3MB8tJxeNg22CeGMwKhqQhnCTiXOdknygln8qJRWZj3mWjHZ3blmkmYN0a1Z/dmF6cVw/OCZoX0skHxCFe2VrYk60qZFuZVFiWUZCOylxaFNlXEhFPix9c16nnYa7sJitoos8NSSwpY5LRDFaUj9IQS9fV0O3rJVTSzhdVEGqn4hORjTDuKBYTzxVTTrAtZ1QSTbGu6O+spvTx6/KvqbWy7LQxKzNwanm2sHazrXf1Lvi177c0bj57dT16dDq3sXu4slEyS2LAACTb0lEQVR42oSZCUOqaBSGHRcWkUVQcUHBBVHMCCTMNMql1Kbm//+cec+HLbeZO/NqWHkTeL5z3u+cc3MrvlwulZkKpWK+VOr3+zz3i/p9LhO9Vc4ViiWozPW5YiH3xx+5gpKEK6mQy+XxKJTLuo4PLJEKpCJUYE+chot91Q/DcLWK3Ha1WhUNzTBqhlYzDK1SEUXRaDY6nc7VVafRmS4WurlKkvV6PJ2Oh+5kOOlVB4NavXN9e3/Yv57OW6Nzeln2pfk+MnlBKAtlTtV5oSTg8oQ+b/K8IqmqLpkqz/d5PrZ6Vg/n0cQB+xo8vry8Q2d81MvLy/n0Ch2P++3hefd4fz8fzee3o9HN9fX1Ha6n3mzmwj67dTyLhTLdUJ+JsfknLJ4vFwrsvnk/XvwB5XKluKVZjlrI58GqVDZ1oZiJUBHZIt4pZLCETWD6qzRNkuVkVhErhKrWauEJYhp+atVB6uqubi03nr+QJMCKxmPQcp3hpG3NBpVW/epmfr89nk5nusGjb3aP+5DnSsWywPVNtQ9Y5RJ9z/dNXQUxk7Dh2sftnjupaDWsCk6lid2bx90jqDzMb5/mT/Pb2xHQ3GRk6o16zcBFEVsN6zioVrvd3AJnASmoVBI4nOgLFtHqZ/qCZYebKJ5OJu1ee1IkWHkhas4SNyiAFtBwKgdKX7hwyOWJFlgJgpDYerhJ8Bj3tApItWoMVrPZpO+MGgLrChrbgWTanrdYbZIkHk/doetMrPak3Z5Vmp2b2/tnovWCyHipu4fzg8KbqaJLiiQpvFDkcC909RRdZp//0NSyhpMqbh5PWpiBUW80GvVWTawAjGEQGorvQbU7687akDWZDF1o6EwhJ+dnsChwSQD2RQf6+oGEk7dbrWaj1TREa7opMFhFeRgpvlnM54BG4AVidaFfwjclsMpCDGkirEJ9tYkgp2oYSL8WSOFRh8CrCVZM48BW+cD2FmEKrs50HCe+rJsUH6qyiHvN0cPheDyfXyAkUV3lZ/cVSTVhIEK5yAtIeNAROBz7+MIRGuOuLbFCrAwGCwve61nuBGvvAgzIDOlUy2gdLbPDOkmSTbSMsV6I7JwnFLPIAiycoyhwpN/CwsWaqr4Yj9U8/IqlIUKqCH/KsVQrZ6gE+hgEKl6BKoPFrG6RquFmGY/joYjEM1qZmvUGW+Q6iyvRmqSBJysehVaINExlnjmEcPEEgTODSJs/v76e31/+en85a/L6sKtgqXF2XIKJNechuoqvrOhHjjtsEyWjxoKoOt6so/UKSlIEO5SuwoW/CFfJZr2OCFjo+2HU05qwyafHbc5ml0ESIIL1Gwns1HqYrlYbtzuQidQFV46lGRHJw+RLdIE8E9iWchB+fbEtO5XC9RhyB+QIRjMTYSJWBKsbBIGi2B4etpdEScAVwV9ASHnhKqWw3KShp0i2e/O8Pb//BZM+NeevhyVCGanP9eEE2d3QkfvSxhlOLU1j2V7TgCuRg0CWFdlGukMg5eE3/tqqVoxW53r0MHcW3qr119vb25+knIwTCEiZS2RdspHL9BUfeGUvQeixEyjFCymWiQXKuEIeWLA5Cjps1dQlWcaZA+ZrmQiX5OmrmBzbFRmsGosrxgqwOo2ru2tLVQJJxkkoCUOJFlMwJTtM19EYuWD1RI1Co+pu7GX98eX99P6OXDzeqiVuMLU9ILbV8sUHyowXnqRo6I57BqTRzlKraKFOkuTFsMv24bvrru/58TPQZHrrBZKt/fXxY05heUM58mEymTJ4wkWMGAleQCEtgMFFBKFIrIgbWJV52w/TNMVKIapXQe47raKu6OlyDLO0RObtMKo6kpBFVbOOouHuqqYSZ0WRF2mqECpOtxerBKmxhHG4k95AazWuR7fzUau3dEfPexj9+eVc483a80hWebhWCX5AcZbR+tBmOIxnCCkNtBFfmrYwTdWWFb/y/oGn49mLKuB8qOlJ8nSXgfvrr5yE1GG2UmQq/YuyeCuzqFbNvlDOyHzHRQaWuT0KHG+9Xi/HZJRJsvK+wcJ5YHhJDFZOW6OC4SMLGaw63F0cL1NTUkh2aHMMlbeAjZDRji+wkCOjp8fd9rC7NdzufHuE15+eLfG4bXAsxLMAgBgstvJ4JENnaWkVqu1YTSD6XF+1bUVaPlwS7a0ZbxaVb7Ae1mHS68z376832qx7gZXlSFY6korQ1+snw2L+j/8W5SPnr1FcjPFcRptk8QMWr6TL6XjstI1mBguHWoUBo+DyVFlGCuu6InsqS0DZ933QSsHfIVZWt2I070YoHvb7/ev+qdZrHPbH0xmZeNpH5RLdBXlWid0Myw+I6uTUASxWrDTrLaNSqYQcb+qBrEtRY/v6RrBeToeOtv9Adb52PX+q1QbtSbyC8+f0PAmsoAwWHkwE51eRg+eg/4OF24Jgp+MoSqnI/0pDnlNC2membYOVDE1WZuF7Mi7wWpiqoqoqOR6HQqQvkdH7C9qfEJBUmM4oC2/mu+3xdb99PZ22txXj8fWMiut8GplCf7wKAthWYBYLoObFiYRcYBaTjqdLt9VoghXB0oyU01VVVnCQfLc+v3+4ve5gCW8uSfk4XASS6lVr2nLlYR+Qc+p3WOzwD9EbpP9jBZF7CYlj9dqWNUEN4zhj9ieU6WzL7HN2mKTRcmxphKnZQmlKKQFsrIJoY2sALJSXtPHwuMLA9lm1Re7uZqzqV9TvUGsCWEjAjjg/Ub9yrilK7b6BnZLcAhdtLsL1xPH7DFYxHTuRS+vzEdAph3+oYHFMPPyeOBgMUKd3xYfD4/3TqNPrTRNP0dezyPZSGftzjs9fMoRwXcrHCxy8/BQjS0B/Q4zVWHF7INKj0kbJ7RTpUymhmJFwHMJkE0+d9qXe0VjVDHL4sevEoQmpyMNiocQx85JZsYUkdN0JtkJU4NjV54+H7f64ByoqTPe3xuj1BFhHsbPdGcxVccq8EKRhuLEiE7DwG4I1rDVbRg0HWp1UQJ2L1pEVu2ZS7VZJg4GIor03s6xe15pOvY27sP2xvWxe57hLhjDR6y9svl4YJdoVLz3yv8PKAxY3rYpaBZllDLq9Xqz2y1S791Has/428FcReRY8A5ywMzFYFF1dRZcl6uRUSS0hBeFdaF8CjwwLeF3qsWbVSq1+Dcfa79HyUgdMHeLxsXlNP5xO+/1jCjC00vm8oHrrMEiTQCjR71ZIwwmSHqnP2pDWhlMlKlIQiagIlSo5P73X0kRRa1Vm1qBmdOP10DU61eX87S0nfBoKRAdG55vwwwfIIqpNMk4o/x+wnIHIChljMEMD4QfIJcnUdfwtoKmBn1LpgOijLk3sihURUYhjZaBgmRkrHax4vICWjCxM18hbthF2q3D3q5uH3eH4ykTpR7SeO9eHI2gdDxomAzApKoiRW5Fv+oktsM2JeRZY1Ftos8joHSxEGuLD16ug3zernTr6B5p5NJq4tpkFj7SWC9kRa7V2e4s6q/yNFY6/1aWqzP+7ZeU+YJWw+yBhLAspgwN6LfQN6KxQhy4XPFDLsp9QCdAbDEBJRMtKYsE/iHB7lIQfrMjnA6RtGhEsfGp3IBqosW4fnmHvSMETxGILu9jNHaN1iHXVacUhinwUp7qs9/VEyrbyELAc1jFriC7E8ixy4uF0iGbZ8dBwT647mMbcAVfNiuM4WfheYMuqGcbD4Vh8AaxS7iKG4/egLqKd7Tufn7BoMuGt1rHjtskBgGJWHVSQj1WxG6mYncnBYhOj25l1q8DTbVu9HvrZGfxi1hvqJqRLmBuYGSsFgYUeh2oRcveqmLUhj2xCA1hnCKyo4zmMMlqNdevhSfQDnZKASgde6mfbuo/9dGLQLIjSDclfxRyjN3GtqTsOqMKvw/Q1Gjo4vm3LOnIUe03fRJcVjq/fAKvwBeI3tLKp3ndo+d9GVqHPk3DHS8CCZfVmNO2wrOli1XQUvsQpqMYRbLAfvE/vABVZaW8t+57JkrCPOQ+xymB5WY0Fz8qqhrvR/BFVO0E6f4hGNaA12iMvj0+H+3kChyQLoUe5xDFYBXuJMU+NNhXRMKjO0lwnihMvRGHKISV8mjlEG4ygQkVha6XTBVE/Yc/Of/4D1qWO+Hr5mZ1ZbYE/+g9YNEUyEQ5Xd23AgtqWI8vdbksRCqaEInCznk4sCimSBQGYGyBnVGJlljn9g5Vio8rKYA1dCiwakz7sEFgvX2K0UJQebm73sLL97rnXF+BRuUt3AZdlsmOUaWxslsES27LEpoM81RkCJyNxA2y/qLnumjEKEIWqls3aV1RZfCVY+f+NrAujr4ke43dpdP49skDLW6ybt+02UNHIaM0veuMevEiVbFlCR+xiqEYTJHcI0WGashxE6JfRiTNWVJl+wnJoqExjUgQW3B14vkT5SIfn66cDCoptQ+H7tBfR5pstdYHsVh4PJ7MmG81qNYLlli/tCqKQXJKBM/XkCDDvu06tObrf7V9Oo5WuD58I1hel3/K6FPdshbKs/Glc+NUFFpvg0ohOXjkw1EmbwsedhgIXLmK13JeCxcKLp9YUw7U4JtduV5qNaxB4tHRWunMCwop9S/bOYCVkWRPMlBFYN+Tur2eq2Jmy1zNrpk+7q6cDOsVO4k9rbbRb69TvZ3dEsKR46HZbKLPwpdHM3/20ZCQrZ2bLrAajP39Ik/S08/YT1o+Uo6+PmMo2gp+UPuZ/Zin7toiYusDi/Wm8dFktP3QiE8OZeGh1jfrV9e384aHWwn3P5/PD82F7eNwdRjd3RkCWagKodGFFkeWhcqDqHZ9URTl6lyUhHB1P6APZ6UTZeLy/ekRsHSrNh4dOz41XgVpiexeDpS6Hw26jVamhiGettIX9isTG4bweLJL1Zum6zz9hYZZhG++fsIhMtiX+zLwsTH+pQn/CKpvDepr9okSwOOSibnP9aDp2yZkmyLN4OXQMVEdHdL/b7XZ3vTsccFvHV2TN8+PzLvUWAZWgksCpZCQMFvwjC6wl2TtN5AD4HoGVwfpO6/xK0Xbaz292B3zm8/2tJetw+cttMVhmBFidOub8OKDOMqoMFA1XOd7r3ez25+f1tDGrvf2A9RCoSvUTFjUk7O++u3g2bGBdFfIPi4AnxOYxv2yInNJrJjnWMAs8EkmlQfkKTc1y6lKDSH2Ki2SsY6+6/GfTdn7Ybg80N2D0DnObCmlYKl/mJdqEEFpAxwKLCgdUhzNRq8GxUDbsT5R9eF7Exln4ZLL5LVrswx6wDAUmT2b01ZnySwewOmzSjxIewZXPFQtohZR25+GUzWiG4UAc/gytkWJKk/NnZFFgARj0j166wPRlZ4QUsL72w5ygOt1EtZO1M6kanfncoaYzWaKJhtOQZbkWaiRr2DmzMvKVYN0CFOIKQhgcnrczmWjJCCydBZbOAiu4ZCEr21C70/8Y7hBYZ4D6ldb5BFpgCJN/AK1tZ4WtFSbPnP0iIZo6s8Ydm2IbLTSIzVIxKAlctH/71JW3sCLMRn9RR+V15+UCK58Fy4USi6RMeabL2/nPX7F8ZZyggqDqyoCc93Y0N+r3jw8OqnQ/pQZw+DfhVsKWuhVEbasosoUtQhJIJCGBsGUDEpYQUZ729dX//3N6zg3yla6jorbvUzh3Zu6ZOUdzhBgi+KCKW/6DqXUG610kGnLs9a2v68iuZwzPAOtcg0ws18/B4lWIiyBjT7oG6xNfAiuMOuzy7zPrcDy9bdqKbW8DDlbbwpks3mFHq3Riykign13E86NRK1QPXP2d337dVv0kyK4LsYy2ogHAPzPzvH4ZBOVqQORFJ0jDNY/n13fVKEjq6tRaAq04Ox6WdrXquuIGo+gGxPAOtEyZUOWp9cYyZBEi+F1mya7jgBtiOuRIiOByhol1BkupN9sxOtbxDBaRukROT7EvFXNPhgvjOG3P53JHlpsTu/G1Igi3a7OotqApya0u5aTSnXP/1CdWl7e2XtUXysdvTDM8ELXP6LnQKP/49noFVl7diL8n1lUQpYuw84SN0bocQ8y1rHh5PB2ssQBrt+ayzuQ718hArY00AlbIrVf2LMYr42VvJNiCIJ6eGxUKpWfNxXWFLsUfxWUypej9F1giruvwQ8yL7wdvg9zabzy1bK5XYekxrwZ8hEFg96hQQhlhqLaOjiFfsCI6v0Y4KF36/fw98XqrFAoV70PtXujAn2npV5/PH4nVP4KVZ+PzylRMyVvOPM+bzvaHw2xcqYThCvkAEQfv2DDjwTQMlds6ZhYKL3tB4yJgJ1gKVB85FIah/vxUXaDLO0gyQpXv3lfJzjYBVrklU707vVyBBaDwce6Gr+/wLbxYaGzHzdJruTWo+ucXw3vJCaKoThlJnnemKlb+/RTCoPLjKiwHLM9pf+YQ8r24w9zf6krNL7D+hcTn29FrifDmOrN+WYzqo5ZnLT1P9g77w3FjVsA8d5yxEOs13u0IuWXK5y5FsGZIKGK1n7da8nTkYgL04QMpYUyrADfUpOsjEqqfOVgYDLGcyY6nlz+nluDv5zXNO7ZZSF2kVgYHw7KVVgEW2MMFrAr22WgYsjxVuypAU6UVVuu7D2JEVMTH56SEG91VgVYep6A9xcg2gDHkp//Eig9Xece4gIXmjkZ2lzYnrbmFAMvE0R9Gi4UPR8SOu/Z1EGy3wIsNbIqjz/vUyyskGYJ1mrd65e7UU3woZy7Ys4udTEJh2Kd7JKU0DNmYcqHSK7ba8YyZBZ715yBYxOz7r/j5vDCWVrbfzzyrpQVQiMDNH34RHb4BR84IKLXVLlpWq9nu7pIkTFSB0iU+1rVatRR2fs+//d321bhojDVP/SLk13Hhp/wsgBG4EagLg+cDpht6MJCkcxShZe33G2TWqFIBWNDDAgRfLGCLcDWq5FQES1CHd6bW0ZOb3eksi2kq0J9Krltx/N2OMG1FBOsAQHM3RgbfmS5zTnqNlPie9yzAYhvcoFyzDFduW2q2mfMapmrEs5vuRqg/JFaz3WmWW91xioMZ/3pdiCeqJlV38Pr7j89M7ladthejLe+XlxL7O1aMC0n465YBVEuAVa09QIIxm/P5VLVm+w3AypBZSYJXvMXlDax2QvLDjkUFPmfS/uKRlIJgwe8z62jGllaDamOROpg5iDCtBgE+6MmwDe4RoVUgtTyO0SzCC1j5WovkIWchQOsAgDBKZZZwKBys+SQH69bxV3a7jcIneZDgnenvqAZ3flxHpt9j97hIjFi2R9poXEbdzE6b7N8yi8Fi+7e4FcV4C/kFYNXS5nQeqzMYwLJsMxs5TuqvdkQoCAQM24iNnmAJqoAJJ8aIA8sY7FHL+ZD/Aq2pVMIGWE+BEICKgJLQ+REmLC6aKMQuKPzpVYCVX4KA6QLWB8HirHm0KFiTycijcRCW2OXFuevJaitxSYqlQ5Gb2vqaZpDgcMHp+zz79uPHzKX/5qm0NlS1tw0Nj5zleCAef8foHP/AGL7SjJo0wcK6HHa7VVONY7Drw8yDqg5lMsFaBWBFuWknIY2wbZUtC4CRsk83MNcBqszr1scBBppgF5YqSeqAC+FWIE50RAnyYeTEVqQW6pB6PZFi5O1dLE3FMADP3hEfs9gS1rTODjT+kbwxL5QFZKJ+SxoMJmLtiDB39BhoLMQXRfO+jf01L8LjqoAFCRw0MF8Mo2m2XB5wCjf/E1d5d65G0cMeeFJ39zXsrhuVtEyzEFoWnuEyNvQFGDxY6RqaNMswIYtYrwHWu+hUgGsatzSo6YnOlaS+AzqBU4UW4yQU/sk3iJOZW8loJhtyR1PsytZGZNZfNn8flyo8HBH7OS8crwzLkL6AAvk1rzVwhuP6ROMejQcBEcToBSnSDe1JLqvH37+/nzanzx/fpomwWzg9brE3+wMm0u/f8kZ+tSc9B8TliwULcZVeEHoKXG7rKcRSrxj6dVmFAwYmTJxBDJ6F3Eq3tmGTOGxTgoXCkl9eSa7QqLzuqkFpggrhArrgzgZY6HTj0NkhnUhiDYGUJsx3Bv6TEO6ldox1FnoW4kzbgRJrEIGDYB88IDZWbCHecNv2o13tAdZNcTs9h0hyGvlsBFxiJDRaeY0DHSmS6k275eZUVcZm5FaqQrPUEwmvKjtwhfb544wRH/NtTH4EkAhLSZDWBEbXhhncxEjQFMupuiTB3aEem6FbVOO5GnsWLqHlfFyqEizqEsitHYTOVEg83aXa6hnr1K0ATaFvRaKJJ+EK7HVX0be2G0ZmnlCckLizUDSRBIaoQtJSzDLkWQgihff3M2bshSjBDUyimTX1LA+W0ZkstfBqX1/qv+QdPoEXh+4+wWkCUexKL4AuoK/GZn+iDI1wAa4nZq6Kv4qGxTiebQ5v8IHBRUPeyf3LwwLGq9ptIywI/lSNuImq2w/EKAfpvoSbKkLmCpVLwX59QCOrvOmGzmAKCWk+5xU0s2yC5SO52bRSPwTfBF8yx4lD9hTZeO1ItNRdUNuEzTSg5wb2PsfeuakxZKCf40NTsJTWmFiCaE1ItNCzTi/Io7yn8wsBlpg4kbXHA8FCeoPLzMln5ifO6pvuo8iBSpjweUVrcfHwuGhgsZFaFdhGYGKb2DD+OkKuqIJZTSKJzf30/eP1dMxuHh6xq3/S0zFEvrr9ky/56NvPrqlAq4L6OdJrsEuhk5iawu8hWAlfKuoD6hZ1Zzlr+3pvKiS3pQAr4qbfT2nmCIIk98Ck26Fhm2NApFcbgEioxnlGp2D4gC9cuMM03OIoiJP4FX0oacqQYHF7ofQG9Cqr1vGVXUoMTnxg4DsW4YmJRePxzJsK3zH00nJ9GLhV7uJx6k8LPBskMrkKcmwdGfzVUQR/oRtFw16zb9sYJQhWrVqU5ZaEkrE2L6dMhuzPLd7NfRjZEym5L/d/udNW6EcuN5x9asbFHmWYCS0AdWhXPeoPingho1EPYhLAmmF3pAiPMRzjaresuGB0CQ2NK+DlEysw8qRVhP/6UYxq11dtIbHpuHH1RPPRA4UTtk/Rh673npZXJLPZZNc3tF7XO/76+Y3iPdHCY65Ov6Ibng4kbgRLpX1dlssJfikNWzm3LjQqTmpM6iMTCEU8JQYqXTOQaGa/q5g2+sZC+OfXMEbD5o2pYSmLJcVlp377VLj13WdahP2dCTAofgKOAQDqI8uEbMxPQCs/dgX/F6nV8dSkugKvTMMFxhWOdSHIXxJumxoYX8jMoj1joOj/YsH5Wed95zjbkb81KSOeZbIeFOuJhoIUVa9FoLkcBABlyzp+/JqTNsLEGYfraSQWwaLZ31NjVKGs4QlAlM79P1x7w+u8tc2+1OOdgfJmZ0RmIGu19cpQOuXhGGK+LuTVaiDRPG0d5p3lKxZl6k0Bm1d27VqFy022C5wkm1K0itaKVKwjmQRODH5mGyHvAYAAC4bjuboqfW3oYZNP0pAdHeyl3V75vit2Uk4VvLzxT7o/o4SfN3Zde+ivNfx4BI5Em6DM6/0JwANyI+iwXVkFI5g2+6CnbQvU/HAkXyfB4piDvAJxxFCIZmBZ6hRgzT5eD91xqAsBj2Dd1vzRAAuE0aBHjwG18AGoabkI+6QWGZrUHYMRpwSLN7XfxDZnZnkgJadN3L3BulyHoG4gxdlWTSDFnbmRwCislZdWcUKMCBLBmjCzyKdNE6+FWEnNON5ewCo4mOmQT2ngG2Y4TsKQYLmhU7hfJM/XEJ3jfuFsUWXrMBwZ6XgyEKcjHtAGmNR9BWSoM98fZlOky1RuId2QXPPlcjqfgSoK8iYoyZFgZTNuIT2VNNl6P1md4gSaatF4IFi1cVkFG1Qn5qA/qsMqM4ARd6AMI1+RJib88GWsO1bpgtorCtGRWlw5vr+9gY20mze5J348Xu8gbhMrg8k1BlZbrTtpqmVkKLHi24TBAmFjmfSKLEP6O216l0U8uJjqtqmfBovGwuVCzwHnAlyNh0blYti50tTgmmKdbcME3rEh/Q899McJ2mQRBw4LAE68Oc+OL79+/v4xP3dNkIrm3Os0p9xCCMpArMiwABb56By9Bmihx8uce1RpeE9O7Zq9LrS4uGto5RGtmXCmmGsa/EJYOlPYi3ANLsIqNKrSbqgMKDC20QgPWUaw0h2wisgDyfvIl5FekV9Z7UbSZGyD5eIJi8QiUKKVKfyk8eCFxUKOxxewHp0VGHsK9Uq/u091B7FAXtGKiD3JBaQvnQhBUZ0kAU7XerRVAJC4TOo4ItQHPitbsymrsTrfvH77fGkr+PUsy+GkbalFSV1uKKqJdgXWQKxQNogYgwpj2pnLLW23uL/lkOauUWltrBuLxgCCUX9It1EJ6h3IY6PqJG7yXECtNaC2DjtSmb6IepbhyojjZk+6SbfgGryOWHwsQ0gxUVitpuOORDOJk4x6DLb5PlHKvRzKcCLAootONqp3X/I9Nuckvv7WrdUwMEDVeuYVeBGDCNGVgHT32EhH+HnbNCiu1xPxpzOwK/KxWCZYkVFv0kOaxbL1/u1NredPZKQ0rSWqaAq0WH/Ear/fZ6hCgMWbUCX3AyHXn2gS4SmBwa+GUAxly+uCPeCWxjTUuBdWf6j9T3zuNK/TRVAdFmGYlMp1KfPAQ2ZWp9y7WdE9RWmBVEYjFxwaK6dScoJW0bB79cQxz2BNLkYOPArHUO507HajS2b97CT6rbC9gW1SdSzwr2jwRC5aJ+A5e8Vp5coN9wZOYLeKimu7B7JCrIS5TSojpcHOlIHUnh9eP5bF7uH7wesy73qKUow383KzPQfBZgESLFAsYkVCyn0ooBrq9/w15z9oWCRrBSfc6ph6yXcrDVr+hGMdcP1yW+PR3sJy2Hh6vNWDoTbBU+nLgF3ODjNVkm7o1hQ3qIY3EaadOHoJdMR2J+WRvxij/nrCPySQytn7hAfPMuTDuHTRXJ3xuiHWkgi6AJ84aNfIdb6QonJW1ZMtZAjbqBaw930wFPSslT2I7Doyi5HfHVK9rmApVpy9Hd7e0Jesetl6gfcdQJaBaPtwkJF0kMfyGiTDEnnliV5FG1/cMkK2AKQWwaqk0aiP/jGp1hpwJD6JI+VxFQq3j40nmBNBlTH08d6p3N/r/XpvJENhjLPTftrq3oBlR1sMMfkUA8SGRpQ6esNxU4cDTsU3+n2CxYZFsEY5WD3chnzOoNWKw8TKozTWgsIdykx4blH/NWhurEV6+YUBt7JydDfQViEomY/5wN1FJvJ5lxrlwKZzklklKryJLDOjcWv/8p2yTZYdvXLZOp1kZjO9odn3vTyH8IWrkJwhvwctQoUqbHfkeBrLw2i34246reDGfqDpEEMmbHUlB8/qgXq7wOrhflHBNgfjzDke7wDbsNcfdfBE5GV2XKoyb8NIjGuGoMlgWQKsJ7CjKgqpUPHBExET0a0ERVQEWBxx7W24gF8UHRF/WnEnOrw9Ue4LfBL530fWQFhoCiugc/38yz16qbOrNMIdmgX27FhM4LUEvFq2K7McRD32dhQhmoVE09lkPS6q864sz/D3AVjAWVJx87bHrOBhufcHGVfC3rZ1BFmJF4ibIEBcJEGCAAjwvm9KVqW6yff1//+dzjxQsdOuYzuKHUkY7O7bnZ19sz2GhPMPEtU/MM0RbkWsRKqC1D2AZ2Es4UpJyn6+hoypom9Hzll2kZ/ajEAatwbh7+26WDKAh8jcwR0uz2sf5sHF3dP75/viCVaeMxKZ4kuwimV9UAzkLobpGYtqolMG4YRd8PWwzQpV16+btlY8BmgEr2Bh/BpPufNUwStimmrCsQAUZyUIRMpvq3rR7q5jdbhJ1aF2uB6O+FL8v49s1hJjc81ZKfZE0qJnjeLctO8/TsAvQB5Hnzyz3I8fGCExLO/7zz/Gd8wv0OZ+3MeivCo3UWFQyiBpETXPiKPbdtlmLVgdPHD43xBxA10GTAIu7iNV8TarOrqXLFMbFKM3QjAqPZRIGF4jyWFD7NSplK0309YTqigniYKztC2GnY8kgWfRkzBLPQAiMI8I/2q4aev9abudevEGbok+PsXJWVTboZJTzAZ0gBXVezJ1hnApOFx3XTRu5mEQ51pju9bwx8gnrQbYiM0GYB0Pm1xwMcK78EbNdGKdxl9/3pWR1AnmAOhiG/MfbxD4wZ9+zN/+s7j/pNoEYD0jEH4V0ByAJcS0rnHU9JpI8LD6YItSASWyNtS/t+yw8MoJf6u9lNHPCIqlvvQx5JdQ5MWYBeEUC8ZjR6pcCVYuwEqeYGFrFE6Fzb02ZlrxBM60Qbc0rP01QVw2XoZmWG+aSrsVdqJH2o84e4GWO2xXc0nRG5Qdw63UQsz84JqR2gWdhl21EBq/7WHNwWCj3tBI0qCGz24ASznebkK1xgqelIbSixR7huPun2/M9crq6/Ntb5+ggRc6kJ+X9z/e9qjgkbLu8ydSdCYHBs+ysWJm59tujZLJ0ri1BodvQ4Eit7n2CmNmrb++yg2xAE8m7xiDJMCQXwGpmmBp27MwgnI8gwmelKEYtYu+MIJgbk2qqX2Nrtdoo9dalf9pfKXJa2tkNKuGrdU6wbkduWWP1Zscuy/FtHOjmm1z5Ex6AtoDZbAyvYF1jJDlzksNn1zm8mW3u3zI5BJRzV2P19jIr+iUEYl99lcKyqyegsmCu79fPj8CpZdK45+X+8m+/CShheh7f//j43QHLXNH51wiFQQlWEL9gULLyDQdbt4SQzyBVrWGI5oSZFSeQ7Hoio+hv810LPhBydOsan30D5KJAyyQwhjNtMc3B1X/N1gkWCc0lPAbTIIxIhZsybUqvsSv1QuKJX1JfvHd3dlwk5s7O3cVe4dB6OaAxU1AchhJMo7AAxz2eIiPiWSEMUTlWzCmg0zlC+WutYq/C4qw8Yo8Ci8DTzjx4iOlN5NQnL4oHkZTy+hvImWOmnOu9HMzQD+76uyx4AR3wlh7/7HyAmQqxptTGtDigF6sAqGAd3HqFofj8YxoeI4RsOyhbc+bLYlthAB/aWs6YrHVHhQaDm09L5fJQ+Vnp5+EEEf4iVCei94QWO3Ag6Hh8mYo6LYQYJ+v7EKSc0uQztVGlUa6TvUb1cjLl5JInytk1c7RWazRc0ZsoGFZ7PVVWT3gUMXsXVtGEy7TYTyHLkIND90614fhTtwXz6oYT3H+nE5i3w1THsshwTLFmorZGeXRTvL2l8uqByd1f/x8m3dm0Ax+/cAYwbEXTq9HDZ8ldnnLlViOnMU2vxcgf4Fi6CUxJ176d0PaWEJdnq41SpvwswuslvXXWgPSaP2VyUuD61ChEc8dcxJJ+LRmEoEbrJB/osgfinzDDvbMCt1qP+1mt+MR1EvZytW6te+GZWtsXjP0V/R37OiCO0IMnK5Z3InQLMjM2BC/HlX5nK/lx7Goo/KC1ILq1vqwOVTjXNXbFMoMeIp2X1/1QQGM82k47UwjDlyokJ+aYMosSbJ6yd3tLZz5POjHSV/5+JqvDON+x9IWcOj3PStOQ2rtudGCLMPeGCbAsjjMpJd1HnjIImt8g/WiQpXIfCBTCyZ2E/BnQ/Dqw6fwSr7Cblcs+khI8ZYtonrxRWFjsYl8LhC5DqZZnx/vYfIxb3EV9Jdym/xGaV3FUhPxTRGuxaqcZlpXyzkPn9vUA4wfzt2zGDSs1eskqZcrxc065VfXJBuUkkiEoV6X1+0apEAxCAZHIeERgptRehKbWJAa4eQjsMfe7OT6MdQhp3FgJSGPOCTfeBdJ3vGxY/vRp3CEvRfRKm89EGDhXzvKWpO5RCDAoukYluc3dIKc4cgwTa80smL5a49JBlV/8B3bs0xIYjtw2D1W0CrNbkZ+HfIngrWnCuZ9/ma8/J/CiNbSXlpTtM5Pg9CJcC1wZqMVWzaHcCgh3DtAnzxhaZac0V/cxGUsKF1F4On5ZMC/Iy+p0Uf8aS9rVKUdSVq5ODZJJvPqBXoWfplepUnfSJSO4scovi3HCzcRCiC0/Pn1sRtZ5+LKVRWftC5bVdcW+UqAFaCQ96OrjPzxt9Fn9cGWBa+MpuEf4FZs1dZvA+fG9nG0HW6nTPGFDdfZ//z3z8pQ2/BslxSSgpRsioY0K9H5pTgt2+CNob3eABLf12iy4QzLl1DPrICWncQjZ4diHblAnUwlCd1kDL571Cv0pWTdxPUJ2wFU0qMRLq9BZAxwGuySWC4gl5/YlunZfRNElmB+uDFiWAqadNArh0cPDF10PIKAUPJzaowm4szebkZmka3ZrZGxf4JFv+ILRXGE0kEbskCovQj7nhMPAG+4BlrESSVNsi3019ffWclWEXm2Fd0kfEvJ0e8E+/kJMhzeWwKvtx0q01Ebv1/udr0qN34tmJd3S8AeynRgujBrutGG6i0BCSltYiVg72rEM3dbq9YnuayHaIh7/mZ98016hxI4gElDN0YHe8z6WJlZPzDPgGb7mPg8qG1v5M56vHCBXIYhPMuSLEioQDQdeliJSs/nIyioR7aTJmgicIgObtNkAInSMeIRWobhL7AsD5nC3pIyWmayaE7/ipAdYvqQCbBUAVZW0rjP8XK3iKz5v0E2jvuAZmT2U9B+h0ElywkWMhYvVQBYGJDs7+NtNPe5hvn9FUC6sF8Zmh2PpuzkxrrP7dnT/cMDE+guEI6zwBqitEiRqxRjGu027PogRwoQDkm91aRWvKHqyyyc9RPeWoT42S2zKSQaFMRK9h54OYihDoxPjrr9NLaj87VvYvy5WQ4OURIVy+MoP6zhmaqKLVVq3650FF8SWJVhKHbGOxyTjxKqGcDHgHZhMJaYFPg/8mccLqle3bZKDVVd3sbW/p//gajtDxDvb+BHQRaYZrzZqnLlGpcZi6+EEhVIEcCgLebv0yph+jbA/QKaZW2x3R/d5EEy9aNdPrVmEBq9G7ExQ+4KEpQs+hkxaPWv5212zsMpTit7PLbb4CBQXcmPNFOL68pT4D0+HGJTtBGJkotFXMW5KDbC2RX5mduToN+c+X1mmEovLnQwmJB535aACH04HFVeHtIC52pxTROU/ay5CZbrljlLgAXkrMkawLZx58qvJxqmgHcr0FqWQl+11s12Jhp2KnT+/LyPx3geHoN4ZVwC9Cf5sYIH6jM3sk4JxhcuCn2+oxpYqH+r21lshbeazIKmlxjWfrLUUF6qO9PeQ2U060kztLBKnns9dWNisJn0OkbPUPxplPZP+1BX0f5h2LNJIozNrRPrRgvfRZhidovuYorBQecLjK8Ai74BNAHWdHYZo9FI9RaaVd5NA8887BBAXBpfYpBMscSNVf+IOzlAmVjB6J+in8YOJrbCSfLVfqFVeaCK3GWDb7Cw/mWBCcJk+8cbuIsFO4EFGEQ2OVNywoqCqrSia/GUuZH08GkPsQiV1nuMlX9Jl0lO4eu0QiWeYPO5t50oytzBV2gPm1o6WsxR98x6I1Y1vhu4rYZQPpkWgnSGNB8djb19yHs+ZiGgLkbIOBFCFq/MN+HiSEfrKJ1IhiT9nDmLwH3eP4Y+GuSDGa/mC/fYbaJy3BaoZBv64LHlkYvOcnAuKEnIbhHTO7GiESyx+eURLLM/OQxBvDSe619PF9BRWPrbDEjBOCvYxm5Am51QPjKpzEnmoy30ddkseU6rAjV1hDeLM9nCLO6LOpSTae1XmaiZuIaj45yn7Ld2sAxgJd3q/WhtrnaDTMMzbBN3juNz5fkS0Vo5o+yB88yf+lPF9Fae64zCONhDabiSkjjn4sMg3VExjAPVNKTOdIIuDNMJu2P+ObaxVTPqkfsjq8y7ruJ4Ye92Ic7P3VVvDxp1yMbR7lEk0YWCAzW4WhyTfo+JhGYzDAmWZRGsKT2KjK1eJ5/HtCUGJi8bJJ/StQgVztSHz8VuVP0AagWsIEuFfEoJcaxMmTSwDFMBv7fdTSS85ui2OX19Ls7tVlUOVubOlVQayjbSMmBbdd7FoERDObHSnd0pBmRytJu/erugiPfMzimwVwq3eSUTGxFJjukWGZOpL1F80OdNczgtD9s0l+Zo4bxRX1F6m9igCk9BwHy+48BHYcNmR0K/Q05L2qWnjy+0hNaxK2d6q53hHXHZutZE20v9xuARIZTxnBS4w56u1RE34vmyzi1GdjW43e5be42f7cSHaw3QdAm/2oIlQuHC/aBgBqz2d+QipC3XGPV9HDYsSmYViAQnpuEZJhL22fLmXzIPQR/47mf4tuqtb89t1Ls3sDC+1spjr4Mkvh5oDexwZenscqe4wLFceJYFn+3hYDJctM/ZxljN97Y1PY1PZoNvGEswPexmjd7A+Fp0Kf9wDic+yivX8fafLJn7iKnnAJc9n3kE4ey4YdZVz+2mDMabUPFglVWNGWu9m4h61LVpREyAZREspzO5bbfc3ZCR4AEWn6Z0rRsY32NWbiZgLx9DQxTG3DxzT2MG4fjkGmayOzwekWH0tLPhLCozL3mADJ3srqgx/SiZ+3W8/qWD6ffu14IOhlxhsswNa3RrFRIW+dQ81WQQs109i1Zgk+YruJCE4mOWgloxUOY4xiS6rhMHii0H8ehDMfoAA4Mn62+mvT07OKPnR/ktijINZAcK0NmfTkfpkSv9zhK8rqJ33ayXKIUej/YwexTt5x0XTV1TUTdg0yaBFgkGsJxfYFnwLHFzkpUccOzJjVa9vBrjW5muJwjENPIhPfH5dp2ZPcp9cdHLAq049DSiPoElyuhQ+cfEdiq+sTeXKrkTub10Z+t01kYbVVWCIMVvzSEXvClxe01MltahPMSgOx0U0Rqu3Rgi7fbfyL2RT3JNZ7zvnPAXp9DRncw4xchmdZkHnnEyIVmB4Z3EaXt9RR63FIzN8Q1B+aOCzwktT/kzQKLCChnQQllagmU7Zh7mPEoz4Hpu8yqvGrDqIrNDM3dAwpIsZimbYJVWepYNsHjz01lGF99qgs8kWOVQrqVnvZmL9zq+W0k0fd8nm8ctRvWV3s4F3mG6oaYFK9NY5zuaMe7QnGJp4Dw4Kmm3vFhMluxcc7fU1l6dTqbhUFVlHX9InuDcY87dDLdw+QnYX7UL4qyrZcfZnxB63QVRaTksT0Zmej4wkKcQBYTB5zvemd2ZRDdIcR8oH3DgZ4rEwuW2HggaECTYJle8DtYipH7Mjplf7FmRBycvCeFpvjSKt92hzgxaJWVYQCR3hGsoHlDy3N/AAnYwgoWufIrBaZersfWyI8HdhLsOGgxvvgcRuppb18fobb9eqqOLlamU/KU+yCtkGQ5akGeHYKIzdQR9VqvWVf1zuYhZPz+0ZlgX5ILrQjyIPgENMaodFI9yNkU6zoYTJBIJMpewD4ESVGv56l//wTIkXWsceIsV3hcYgHyizE5WmO92CjzL7hseGPVreQfdY61pk0l646uTOX+iuPl2w3lofwVKGOVcmDOJlYekHQTQ2BvxEYxRRjqfYYiXVBxIhiOCmJJJYcEY2gzDsq7tAOdOvcmdw2qDYDWqlAsV0DCc5u4kHe3n8fm2erfPWefjY60uT28dREtiRpszEmOXkdMUBUcDkSf3O1OucjYeowYH6uBx9GrjKgiH15HnYgYV9nkwTW4D3N3XCiUpaWvPdtUJjI1InFl0+vM//3x7B7G7H7NgRq5x3gPHtB0jyaB9HI89M+5LZr+f4m3p6HuqjXaeoqmWdTE6H3KCeHhMg47yA2c12AW4y8igW5VEELc3Tq6iuAsk2IyK1GsoiQsr+J1QL0Peyvmmssqr3jzqz0ZsR4dkQ/kDvFPXHrvmJJgvrmv/fvHXj/FlcV4Hn58bdX25W936YDa7Cr65W0Csux0y0fGypfo5HFTgmq3u6FwT0VyXtXNREhmRh9Y6YWZsEr0qeGHM9I7dK7FiKlGiwxItCKLhqFz+QCG7Am268AwJdAqzZWi6lgom5uwGECrGveRYgPzjfQDD13o33wmsWMnJnPAMtuvc9hRQe/2JkPfhEBdMHqvEFdHCqAuDw8vKYauNNc0VAKKgHEQ7p/X8gKQywSojkVHZmWAgJZMNpcm6el6MMY7q3N/jK8BauMH+fl+sMGYbTUaXi6UPw9XsMGx24wQqBlTddeoksIkMgcILSFBY8zBFccXNX1Dj3QqLkeoNGTKqQerexCvB5RUTqrSmj26I/+5Z3gll+95NBjp1h+no/SeFPij8HVTOtuQnKY5WL+5iwppJVnJcpyG+MHrD5ZKDIzBfj2U5EWZprr/8Qy+2Z8u23E+7nyTo7UHlwa+eYCyYEPfv7BS4lsECiOjNbBvFNqttgVxpZbHluU+wXGWjtsXUt6H7NngeZz+OD/139H6g7qADxy/cTkMigdmbW7BfbOutaNaRh1RIvFKFoOPwg8cQLKg05P5a5/VF5R4Y7+3QNlbnrMvZFhyUiiFRc0Pdkb9uSx3eLjp/e59zq885DlQUOr53/+C7h3u5Aarw/Aa6fWRt4f760p/i6N8lOocD4gKCBuqt21Kvl7PyOt5brYK+r/BtT7osRpOYLYVUgkWoBBoLbHpBUMStDK4m0NGg2ED/RrIWKQBglYMKgsVg5G8up9pZg46AJw5Onfggvb/b1uqNqhuMQQgW0BKLRLDLeIX6allvu47U5NW9LZxgSKyy3C3Xbyho0a/+Ae+5hg9A/t622mCdKhIafIQIyxreknYA6YStHItg7efsCcb79895eACfNOnMLu/sp/b7AOqVeV9vDaNOr93lODOOzwM1ndyWcmkawK+vxTUCtBpeXVVcd3EEPXO5dCB7Yt6mtjegY5Vw7PHJ7xQVcZWQJiQg2M4Rf4gmPoCJQ9FjQBIsFEtxT+m1W0jIGsojZbGKt8n88kQJuNPoWrAv/F4qUk/5IZw5IcLvAX4zQ4XWasvDkl1H1sAUzZ+onAvpnDlsNvJyfTQVf3fmPS3MwSyZJwqE3gc2XbMVEFkt6Es/3pQUQ3fI8D6QO6jmhJ+/v1uJ6XpHOBZET9crZj0RJv9ngM+7Swdt7PyUG6Z4DI5CdJWWGba7+gx6kxBiH2AFmjt4ehYF9sDnHQ0bVwmFETHixd+fM0NHxJ9DJxOlRIIercvC6hD0Dut4PHc7+8svA1AlWjShw7lTLrFwYJvX16XnHcVeeK3dbgrPEkqEdpbMjhrfuoaUvj4gbT/CEXLsg0MZuBYfyodnqTmrvcAjRYFvE7j82HsJ6qL9/j4PRLys0C3g5a+c/oD9dxsrwCBTYiqchfqHt/FBGl0y1a+8rG5Ya3BKpqnhyXa+3o0p5F+S4TJhlY7FVu0CA0pChkeJLeEjYAK08fPeK3gULOAiuU3POjbq4lbXVh4E0SNjKAv7Cyz+wjikZwmJJeEn8M5B70peJxO9ZKvbqIlTD1qIAlvVmyAXczQApmtrZOIspwRrzXAnqLdJAuIllnml0ogdOJSL8KD9eP85DtCSBvf93eGzOa7BxLtwpiAHdA3EwEBtdNcJVjsODQ55hJigFEwRLuik9SbjvI1lYXB19y+n1+89mVsqHYUDj+8Mm4tAiCFI1JDD8As/LsfRzFrkWR1BYzlMXEqaYWiDB9s6Cy8N9wJiYgXA0M1avVBsu8e+5IL4FH71LSzp9BAbShdpQkNkIQ4IFmZ2kMC1l/1Dl6Nixl07qw/15WYSHlSuFNBermI+n7b78Kw+XE7QIOM7PjmoP2M6svC1ThwGuy5CYe8nyvSgqec0VdWi0WzvokZ5LzyuiRYIlVjh4+oQr41u1gVZ0gvs0+deYmvIZy7POoKFJUsYwMGzCt8iTHehIWUsluq1khhl7BIsWMdQwvUhUbayrgj/JM53qGmDjhmflxwxaSp0wmobbWZ2yCUkQPqVuJCXS5yT2ksNo/dW7UlWUPdJHfN5KSQl7Hu6Kje+znEK8uf7bsTuzoejpXKPe42810wKo1tuYXzizMcB/ouzv+9nVCeC4hi/vXnj+wIqxz4Kf63VOIeJhoQu9PZt9FnDmgALrw0Tb/AaerfSILN0xdNePmwIHUgZiAgswcIol551h8EzgBg/EGEE54ateByWg/sZwSqreUMajXbtkWNq+m4xpol9mdn0TOJGzs6bKBGCFjxaL0yxwH4NOwCbh6qgWvNhPfesRLBgBItSKlhjIKgZcbnusN1CQ1HkeYsp+Lk/UpXNkWXGlL5CugrJNhL19hZKKNdXexsehbXkE79x0EEXLD8bIxQCEeSWy2Z9gO2uWKYECbLp2/a1NWyKlwXUhvzcNXzvpCyzB1it0+cYlKDIWIxAsXzNzFRmGqLE9T+hixRTOyDwDRYMEItEzzrCWK6T/uAltYP+tbOCEaxVZ6fqXU4tU1PcfYBKVxhWah1lAoV56DlPWhutfugZylbwOvyHhBx+1Jd67XnrbW2oo01soDRKriAcqMIRaJmr8cefFiS+nPnBiscRBREGULM7V/Xs98VMnOczXBuCIs/dFWDW1oNhYxv2Qn+yUQtowcAyqPUGei6x9NDS2zVOnpYDmA6N8yTwcEFLUN7nwZ28sXjGMVMVIIJdeMCXYIkwnPOY2QMsRiEgKlU0wrFCfG4dTCZGRqcFkx/MOLQZe0XU8xZwVm5oUAwH43UT0EJa4bVIDYBlKNTLol2YNlh7iq3MWpUGKUi9+m3DIT4eqjuMfSJ+LLBqqc5i8fGfP6yi4G0LG1iCyMScwHJn4PY6Boo9++Tg0+PK1dHt658XdwrXLg44GeLmOuEdnDqnd/X2EE5MpWvzpdke8k40HFr1yj9k8Gqu454+9mXKCURyL3MWjz5WpKKK5BEGrOhlXD7haTg+EStWsTO2PsTLm2r1q7IZdBOKS1mDnKyb2HE7RBJS7dcPqOJgWGcBUmLX+uePt33gjvIileBZCs0wdrgk3+/ciILwHICDfkcAAxs2ObdSj6B3HghCEa9Fn8t+b7jKeErRPLfceJcOOGBwiyAKHckETUeVkN8PTrbasilkvCxsDhqtXe1VG7Dnp0qVtQgZsqrefYXUorxWg6YygUAUvH9bESoOWMpMw8z8zuTOyLsz0T/LiFL2DrCoHxBYEZXAodkdxXz4lhGvQwrc8NPOZcgZB4fQOom7NGhodLgthTVYYobNMqx5O9YoPgMuo0Qr0TgzjsXdTjBuXjbKC6jZ8QzZLg6z3XU4fP2+mWAN0qQze8OO4hhjefA9XD5USFDjX7wVo9uyEiHgnaAh7vWhk0XrtcfR4kreSBO3YA5Rm3RLIryKnNUV6kVcGfGUa8Q+WCyUHvYFByttxtBhSgIg5fnHpA7xGquGfeluNEZhIIIvEDWScCxXwWQph4cYgTDHHDQQgUUkBVjwZENIQ4XFgRaEDF9fHGyxjVsFLrbGsGFCDTls2rekEVbaSrAADbpbMX9m4zgUmtRtfnsWDjy2modeX3Iu//y6z6EEIlcNshyD4gnYz2g68+M0tqScmi4/5JXY1uWyu13eVgHKu4akHNkdsB9tEBgW7nXKc1ulglNwvdVNSLC8k9EZv42JFrxBgCWM9REcSvQqokwqjzaaqLoBESkKKkrLEkI5h73eI1EsR6R+N2rDrZa3vo0DSLQ4X1z4AVrPm7yAGP4Lu3TEsY25Sb7rGfCIkVi7TGrEARFB4mRYA1ilXK0+FGqzc56qTFc0eh9msRb6Z7BTvHlUCLs3uPB9Db4/9Xbr7NDvc7g9VUaejYCfzxr18X0nd+z167YXD7RydUCUK/BkjqcpEG7hS7WztYwy+RgVZ9PAsynOB+RXxOpb0wRjxiqLbtaUTOzftWm52SRqUuZxgszj7FhLwryOMtymp1k3FNqgdRV0HqLWIuj4Sc8qF8v448cXuJTVSZSL1sjfJJitwsA+D8AgHipXcdWeaDarpQwSzDr9gGvwVy5GPMHCvjLIT4CuiD3w87oQ965TEraeZu3CnAU9YHjcjJygM1p2Di8tZ7yuTaxrtbpdDzGGfMY0yxEWK+36y6umidv92nXu0zYrryGag4Xt7VHtzr6bHR6FRAVPSANUhKsEClaugSH8iBXL71IEbx2Lfv9RXXZsmrJlCG5zAwtxYjkZcNO/BFjMW7QvsuNzcmOuqLSUaZRzWtzDcG871GOpooT5dTuA9BsmglBrCw5W3VDBLP+l03mdGoZtow8x+llGLRpbShr+7jZpNzbW5Y/V7no7L1OcaKHQUiuB+lpIxxZpIOT27+s5QX0Mh7Wh1q02BlqLyf5bZ1b3uZM3VpQA8fdEC5wiAEEQ8unK1vf+NJHFhGfhCRfC+L8EvJjHsHgJLGZGZLd6S6jo1POkg8U65kCmQHJjROoZhVCpfl6QDsE6zDxDCAEsA+oCRIrJ9eNjYvYqCM5o226KhMWNEX0pOkStOO6W22vzL+1fqnjuIphifJSLkl+YoGwhYzji6shw8fMtwkwk23one9rg02+U1msrf9R+u1aKYAE88hvZWt2udb4j5EwADy4yH7E7cOxpz5pxv5JxWD4ZrMxXLLLgTN/eJpZQmLMW37tgbCpcD7lZ6o/IIGJuC6w0TTsnHTDQJaorRiKweiYs1g6fvCTnCwcHPoFNoUoHzLbST0OkLVMCYACrP9kV+pBbv1hRJ5+oy+Jmwkd65MYErLxtMpdcZxyMsHL7IG3zLbjnQAr7hc3lTsJKmxI9UE1JC9dSjnVEdANnYKP6Nw0h/KhcvVAfcX8atZt6gXETxhZI8po/mpgWhLNSHBp4enEgfoN1eZIpH4IeAFi0kq8BWAIqrttzwwlqo+MENWAErBBOZtZFJBSJ4syepA+cFcgDKoEVoHpWpZ9vIsEHot2RgJVnhNeIvkXzK/GRK6uzPTY1J4WmcZop7nDc5Gn7b9u5DwU0loIFDa1ZKkSh8c2gAMd9RijKQRYFHUwmc5BlObfrlETsWdQJwm/GqzaEhgKaNt8D6bvcpPJzwa3SiI/NgUIiy88T48S8dSplB+9Pv/r6Rut/wXp205Rb0LF6A8jerhuPTy0VOugf1JkWPmK2p2yYQfgdggIscY3Gp5DUj09MWdSWUF4SXScSoeqZ14q3//jBfemPubVRazUQyWveHcf0nsuV320QLByjN01bUHxlmMfcsA595KUS22W3uYx7E2wnYpRWqw92IDqVkNQrTr+/G2IOOnRmW4xnwff08iSvV55OV8FspD4SEu0EiygLkHpkZNEWPrM7D3wW7wItulcJGbAq13ZgPMmgOT2GPf8skTc1zlwF2EZUjjAVCSZuzuxOrJ5gCbwIAwS1ZLQcauHcDkkDozeZiA1HM6zEiy/8pcsi4gV5WO1qb89CGh9xPFv/7R7lygu0wV7/ATXKGWtkxzM0xoVMwkKTm61GflBvkzXaGZAWx/Aqq6JF/l+sKi0IOYRlKvJiLpl+WDx1q3A6uNexnI7aeQr69f6+En00o/Cb/8WBDy8jqfnLmLXKG0KcQIiZ8IS9Hn0UpyKIMrmIoEdzKSeEKJ3Fgyi1fkPreX/qx4X5j7UDmTELRbcCTiKZSNylNyttVJqX2f7Hg1xWs6HSa85X3siKdZTWb09b3SiUcd3a7QEL0eMt3qgsOjCdQWqqb5I8jBtN5Ch5m4dbsTvK87XyN6Sy7YAHKZWc/G0b9ScJRYENmBg7NSRPoko/cPM0NhaXe9kbImpgouzmQrTgar5JU8BGsATN7TBluQbObD/sWYgjI5GRWgapKRg4g9SP2L+7P8F6okVj/f5Bykd4J+ksFN4K5/+9JBxx6aNS310+sRczd7pilStvD+UB9wCjQ+Wl/ru4e8lde3vv365pUUBFcjitjB2kE+pSb+JptWsa31A9qeczdjWOIPRoVVDsf0Wgltj7y7i3FldMw5Abr70ELnmNs4aI2WZzYJiyjPPeBVrHWIFkdT4u+SzkY3F8CQpYkIDvLO55wxl+I1YYLYGoASigMiVza3RIVKoyloUOU45sO1zacJmynmD9DS2WpJc3QZaexICbUI/MDmNxmoe82KKi69KXsrLy+ZGjyGY6wAG3vSJhpcPfb217wbaAFFruvnM7q1htxAqJMZ5JSZSeMU+H8kiDdIcc7iZiox0mcu25+IjlnxKrQZ+L4V//vPtbKlEKGNTMHsr+vq+WtUWt27zJlZeQCteZHUHKHbyBCABFQ7QIFToSGpCCsb8m/we0BFXlnTiocHvDKDQ3Ryo6pbMMvdo2UaBeEuIJyAHZQgHi39GCAStEOIH/BkuQWtxwUkbKFEUbwFJ1zTVPwcGY1tkaarqIoyhN4x1GrKVVhGHQZ3onSBa5SSgXWXwKXGmyU5HIh+3B4QY9GPYt4wQ/sDnSb5S0Yuv7Ei4Zh7nL6+B+/jQ2YA+RGrEmHdsWboi4vTxvnNJ5eLZLOTu2wY+pb99/fpJhYElaOtcXlzS/vscNInsRtcuCYjpyHBZptkghfRfJ6kAFzQd9NiVMFATwJpGyKP2er8LE5XoE653xzD6llIgDXy4hW0p/MgVYG1VXoQmX4/VLpWQcEFFJviviMCNO3+4l/i1yZ+ZoPRRc/bAAS3NYyuJutsF/GbsO9bS5JMqaDpLoTQhRhBCiC0QzWO6b8u3+7/84e85cYTvJtklsJ7Fjw2Hu3KlnLgN476Qit8h3cLxMey2V5v9oPfcxl9ZZLXllvyxwrfvo/eYYwKY32gJQVevJkn7Br2tT0a2NfTyy0fr7260M8wJXEphBwcQ/VUJWI8yW1xcLXoQwrKOJ1auwL9XuG8joHmwOMACtmjRPCFoClzhu4pgKYnAcGA2spKim+rxg/Zhyxmi7CbQST6EHcuTScNJSZTwUYWcm2oMSpYHx9cqHRUlky41Fb1WpZo2srF3kmGqV16GHpRQYepAxVPxy9OSwpAJwFTkDiz5nvcpNKdPAMlvkYsN4oVNZNDSAxRWldMBK0MQe8m1bPq/FvgYajt7iGb3WcX5UlbBiih4I3jHP9b4cdRbSQmPmzvm8f7apWPkt+5mCaZ7bkhAcSJe8VG6V3ZKz+A7slbw/iuNAsIgVBbUG8mDB1s3daS8BDtbzoQy2mow6cHCiLNA9+Wk0QnwSsgGsVCJlNpunvHltRdsWN0SgA/aCSWCwIk1ckxPmnN/nIDXAUsUbTtvT1UfkPOEceqW9euL6ss19Y7LFKQwdBIOruoaurZKoawaTEAnjkE7s2qIE+5UNJpPa00/w4kOZAJDktKAPpMqSgJrc1I9tW9UZGvVL8u5a2SYHbAO11pj09vwR9Eracm5bOlXGmhEP1ZUaq5xdVTcUX01NANFokZOBg6KmayXwP2wQuV/hs7u7IVImxtRGCs+dffJMUjMIXavTzIO5U68ezxyBLmSr0RxpMvSLCcHAiDwZc0U8V1V7x3BLesEO7sIap9q2XLOOND31/OnRBlpgYjnmOw/Q91rPE2bfJIrl8hNNcNs3GOwsOxg7NjtLEP39BF5i2fkUBbd7ehSYcu1pEuU0mUkzShO/NWQVanTQZ2DlYsPqvELFgsjGPiotvXgqKUUAA1a33cgnohXvHwbSkr9jR2IC2rjsTi9GBhxYeTNaRy6ebVB1JnQqbxYnZsGdRKneCq2icGA5Am14fh29LAvm2pixFtKdOcGaOkXFCpIZcpgQOa/INW0XBs3Ndynj1yYmXzHM6WtkAizbIUeycGwJFrt9y+il05psJ1puNNzN0/Lq/fs/uecKKKljCLV4A4/ust2rNOEyUMxjreYHU0yhso1+ukYB2fOhV8SqzCFOiioREy0x8iL3UpWkVjHQPsXVMLVHmvQlEAw1J1YSWjUtlNLACIg+P9O2rHVCH7QSn2S36dSNc/O8WYHUqn9AI/j5EM67S+TKOAKJFlEZPeXioenRtbbskm3tJgGjp2vkBXaz5svUtCvbTx8frOMZY38oBje7la3kr2njqkMplLdwOScHTUksj59Wecf3nV5j+f6NVYVXxblPQuuH7sgaLTZxV0g+kxpoFWPCMwebfNF3kReZI4KVh2LJ/JRscUN7sPjxt0K+pICIlZQqBSxRLYFXOEzwDuHOnv38smuXk6MVHqOBGcI//Mg4pBVYSkboF3h/nnpIGaFTvbGRBhaecOo3XwWAdUlWQ7Fa3v7+SdvudpfpuV9bPrfJdmG5Vg02dvUyxydmqKdhUHTg3d24+0u5lJx/o5VIa4iJT8qB33dM2C4TderNk6RWkCzfNMu2A78Vga/MkeMJGOmMZWFZ2ZxN9KaHfMjMyY/Ix6XRxxIFkdEATuKyWYN4MdcqIoG4Yob4UK0yBFQuKM1Qs/btXP4Eq0/UCda8dykMpqUbl3cM2A0rHd9s+fj6PIiaeIQrKazAXKjXgRm3EWxplJT/kMCNcM9J8OsF3fna+OWhzWne8pwdju17C8NtEHSgaNddthDvM6cfH/+wVmOxNTqbMQQe6cumTsJSrqITrhfLRe+p3Rhz0kb4CUZhveNkkuQKKLganuFED8IgsnF+aKPpldKwKm4mOlxSliRa1CtpCgFMAtZNtdjzLLjwJOJSTKy2SftkjxT1Sx45oJ5/V3W92Frx/ddNA9tV/QxO9m/P7rXJ5DjJI0/CvYRMLJOLUvoxYir5QhEFXwRFoaWfRwvYTrwm2sghu3tlcSxWIWhWDu0dpKi284prpoyl0Sln7ywphiFXSpaQh1O9B8QUC0Wvc3piJBfTqZQzSbdWxm5RsNzO5gDLJkHsbjLSWHMgVqIMAC5euc8t++JAQJRzC9gp8TFkvMMJIuqjYAywHjijXl2j9pPX3GnlnDbNzF3Mh6XWx3zxTBNZu5S0ydv52rs2mAnAwyVYTHs3kD0i3GbPzcpKanhOTlTEKz1zSpM8x5BsTPkfGOX47mimmAJKxauzhszY03+XJFhKLRPpo3eXbIzr89NeWl8oLxIXsoqo/PaxSmMxyFknW6Ny8S6DASZLw20zXc8mx7NNjWB+jVgJ6xu1RJSLITWVVn1r+A17hftJYUXFoh1WYOWplAkyaDf7g9qgZeezRm+bONvFr3tiKPEf4u6z+RO88NfvXfAtvJP0edWNv3uzyTX7Z/geQeamIcp71zNWLY+Kx1n4xzgh6Js6s9JMbUUXburJiPoKd2lSrL1I4bQ53Lny+qvjoqyxJGWU0MS0Z3q74aTvirNkYgjCNbEwYf/sXCd4QKo+I39gNEwWMxouAYs6e6+EITTBUl1xNCoEi7RLhF4WpybelmXUs3p1d1rTqkPbSRaO2d+IlJNp7tsvpJVZaeEG5Gv8/aVirmBrWbUSqMarGo93K1m6ZH/PZSUtjN6ZgyJ3Y7Ekddm6RbiglGzglTJ3tz1ash+HfERKMs368M4cL+wuaoYq7R5DdQNr1Wzwa5x2PpOUQ1B0bGbN52dv6x4s0mFSs6hWghWhYy1QwOKNGHtbTF7ImVadXm0xWAopZevw3xNe0b3e7eZlboX3MN6An/iVSzlTIsPueaZz2EAqi7vlql9jreDt20Onhisb8QECztXDuC1gXQrxoPAvWdI1Jg3MnTQ0oYHlDKpl6YHn5lFkHbPJeJcNJQ6R1AXpecmkth8lPdAMSbzM9Kj8Kcaq0josul5yWKvQMaTkjgTGRK/44IoVlBDqEp9szIcpmGk1ZbQkDXvTLMm3St+UwkpTQvMuYBXTE7fQt4OWWfHP3W3mt40VBTR7E6y1NPjTCBvd4916TLQe334+b7R5BS7E/vFhvABYlSlUKwWz83veL3ktV/xgUKmAnYn+1XWnWLbI0EXmGjgMn4szBGuKOvnl8SXZf7rf9RubjSxcuOVG7WttZacK7WaYSpSozSKFHa2Bs4ucSzgC0ana0Uk62puQtClfE9VS7aqKWZHfUk72iWAJVuJhES6NqpW4pM/W7GKGwdxMbU+HROmcvKVlPisyQ1beGQ+yZIax/APCEAnS3oS0HMXJJbBClsxLu9o2LbWvr5SIhWwhbbIq8rRvVISHG1ycagkZpi2GHJ5mIY7IfGD1ubksqGSgWZtssrzv5a7jsXVsbur65bTSk86inEscG4Pk7cszIBOwcedWwt3BjFz4QpZNkmjSYQplGoRkaRUxWrH3ICE6Y2iFFlQLYCm9ylO0m2b5/XAKwX/Ph6NVcDdapb4YnC+mJxmXv1hhdE6Vw4o1FxFeTxgE52txTrfM8xekUlLfEejX3TFrDIht4Lj6hnRose8IZf10AWCJgxK/SrGaxfjxg37S0iltM0jp+836Lj/WUq3m6pzUG51MorCt3iXjpV07sAwzkJsfgtA8mJpKnkvcKiApikeT9CIES2q490zUPKMaDdlDRLU6dYXUCCKICViyoNkyyRvY2GyLq0V8fL6saVBJlurOuIOSULnmm1lizqPIpy/qi0RgzbFqNsb61OputcGviGTOjZvUlxZXFE7q2vwsrBHMNrZwPabVmtc/NnimWPVVkvODdNruRsnWolm88xejVCq/uN6leuUsGycyXKtN+PsklILTbkZn8AiinDyHsEDKmPXGhsk95/TjGXuOSQsr620eWQLhVSuVQ/jpv4LFYzgQsj8XYFfKTdwt903sxUjffV3dHoO1btezIIqvAiwP93QPTMK0twIWFXeUG2qanpFGNeUHDFnPSN12qxSsPXuEOdDul4hVIYPwkc0SH1vS/wALaMZCxs7AriYz7Vr2zmuCSGHbOLPHWbE60UTI0lBEhnMcOis6VBz08sNJJljEamqJTKkac3pebTZiPr1zMSLXs70K16k6J+SwUWj1SKp3AwsOcXjuM+xwQScLM5S/m22ufNy/OxCtZrvFJevxwsPK/pLtgO6YYO0306jeKSa3ZU8hRUnxgNHfIimEEDZOunwczdqgSqrJEnjk9GDXR6vw8Gv+WqGmTiFFxUEyCQJQspcJ1pxZ11ymiEIchLsQ6ZFkqF0yCjKHOrnRtjyBnw2UhKqZhNAClDDz20L8Jq3CL5zBRcD7/P2VZSM+GWaR0MUIq4WjF3seCqzpZF2Q3j8Q8uWZx1yt2+Nz4k/O6OS62czi8YuRT2arlTFu3ycMf+wn1e7GS5n1c6qwTnH652bl6BFgp7phyEwTohnPHSEvFLVSVbzozhZV721k3GXXcW9TjFj8LnkDSyRNR0PRCCp8IOxMIV4SC6g0I0pxW4bOPpI/R3fEUJVoqcQR83QOOX6FZrXeBp1Mo6KIRfMspki/EeNrDE4KfYa4VwBL0AJYhwxfQmk+2G3nG+lR2VQ/gFKvNxKereyOxzCjVzM0D3bZu5RB6bx56uyxb3h1udvVQ2oV9OlDJ6GN4HfTYEjrJjJmGQMjL+BYvst0ON2+PzXKtn9EfiL7cQ4JDvGG8E8KK9oiGU4rZDiCR7QIUOlXsKTpunQ0SWE9x4Czq2EcG8aLYAmHveMSKoJFC83NwavVyBSC/EaN6ZYymxgW0n1f70hlu5YnUhJ8M+ZJpNnF0qqiUBwgkW4vGO91UgkMSgvBhsr6lTjwhNYXvOA4hdmgkHaas/QIKtt9qq+6j7Wun0yHhjzF9EG/DU8YZ5C+uT0YB0hjNLjoQxnmXzfyQ3M5xoUTtcIoc3cjUrot3r9dggosnvrMrAo23yz57xRYEIJFUWApAr8+N1dzd0R47eWPUzl/ptqOEEM1YEesiewuWvefxssX5t7v9wvEF1id0B4vx9In2GnCj6f/ECcqFNksHyDmxLimAqTwlg3CYoCVCxau2B4lhWxrpsczdshvzbR++tJ0W5VNze0+5EHFb42uQSsdPznvYKQ5HYB5S+78cKwRrh02wbkHcBuQinfWCROHsV0Y5PvJnb9WvawKstQva2FvG3SHh249my7uSjyGCigIcYt1i3OqFGMAreHVF16wPt6sACxYc5sWK0aKC6JQMRDFYncJAugVCEAAML7GrKOZjSRCaESVgphYeY1CtBLQK1xa612Ekr2DzTZdBEtLbbGYfDFbeNF0o4WgV81gGCMrF2AkAPp6Gj+cKuPHSS54GBRS6un1Sf4kq2d35HxyJsSrjT7cQVAcCs95Fk0U54WTjMoXDIau6WLdjiFhU2pFmPhGKVrPm3m2Oo24E1uBFb8RLNEtJdnq2bdpoo7bGhrReAsq50p4MgUr7jqHUtRWLz+5GOrH2+uiLD4nKy69zvipW4P0GqqNkLjxCCqwOJaF+C8CWDwy9RW9TChj6ctKQ1w00tdoYGxzF2UylZo/QjsCU2Uq7W9laveO2BoePuQQDLUiNMJQvevjRZgCr7x10VvidbH6obcvqZLmA2G1t+Rzx/4HZbW4AsLxrbunst0anvuZrMCjpPop6q9yLRZ57MzjseP6pC6DYyX7XGKsYv7aCvg7f7Kv+zsuQfAojRg98stH3ftlA3YDxaKTuI/wTiXsIf9ZgtQN1f6Z20yOGE1u79+fyfppJig3K8tUgOIgboV2Pz0glQlSvgRLsifL2tM9in/EVDEFF4U5aIYoEJOe7NJ3nONg7oIQ8ea8pUMMLpz74likOCv2KUlIWtU7+FPxc/WJ5m5bWYOs1p9S/MSKwBlqFlo3bSB0RAw0oaUX9npGWEqtJiH3UlZWoDZ84qDfasPB63FbODd459X373t0/CP9IFlY1m01IXupAawC+8p2Z7JCO3MNE6ngAMWuOz/ZumTxQG+WKyEuAx66V7HPWkfAYjVBoUXiFu/OG2/JvER3k0OYJE2+nA2c23R6uI5AyHzwMA0Uo/URG6RBtKuXkNLg3+MLWNAiWBK7Q6mxfWXHXevDFof88CZgqd/CYWkYOplpvJC1XJif46Q9cAQsoiWnUGF1PKIVfY9h2DcOVWAZZSV/wlzKHlveVRK5+/KyaG7gxjeWzDRDtQgiVWsEsPBgA2AVgXqjs3nEailsNT01wnp795k4YEKdgSF4mCxOIVHIQQhfZUW0kOAYtBpLh+zmvF6rwhUJ44VNbl6GUUsBLM+tDNIvn2ApitUibBsIC4NWK/WxrEEsFtUUvxHKIK+zI6tzltP+FAFMCX+SwfdrD5OARwBDoz49Dhq+P4cTpfjr5RQKVmg9mzbpSMsy3HeglDc17D16asckZd3lN2wSeWJFkj2TymoBLNosnK3iLGALDNgVIr/2zLXMj7wItNavu9Tw4HVXzxylvYJcLQEauRy7J44/0Oo+Lc+01SxEwx0y0CdDNgIEyTm1ijFb+KQ4VgKsSCuJvjYkn4xihp+gzSJhulTzZQgdHm1uCJ5nHWgQH4NSNKhSFPnInk2QVB4s1Ol596HjZOBDxT7BcggWVoSBoX+z6ZQXKFK9AY2HJ2xPrqxenjcyHoVYETt9GivpcCJYC8nV0H8AWGypEqKfI2JEZ3Kcv/wFYsAN2ueuolaf3iJYwNfo07Nr5JqtgNr2OGcFEOFLbaV6DlboEIfTQCcb6eLZjHUIGVNPKS1K/rFCMYWv60OvvKxxdfR+5sONB1hAq2jNxPcUsDxdcNKNWHRdMOM/yrs1sKz2r04PAE1DB+yWPTTmq1jQIli3U9jpau0n2UHzuN+Q16HJnO8LC2FsRlou//l4un+PwTq1CdaImelRgstLoMX6LnThwmHR5GD81z9/3HcbvXRs41MUniRMM/v8T2gMs9bFsFLrkAV81EBV+sRa0n7RyvgavpukCRHE8Om0yPAV8zgKTDH86j0mOQzwQrhzezgM/HWQJliKOxuWbz1fNXctupwY66MKKbDUByWASYFF7ZJN8P35XDSr6TpMXwlYNjeOOQMF1rTTa5JPA6selitUvJGb6Zq9/cuTmgF9e1x9+4sNFGyqQeqpQ8ViiIhwB625WtjHVEsAvlKLW6q3p79/e0F5t5U2yM2jjLyY+GKr35M6klPUXfCT1vgY0BXUvX9YLelwzKebUx9fT6nKC18CwzoA/yVtCjxisJDc5+y6D6/bzWWvID9PEiylWijVVhbjZlDCFhySGonECiVvkHUMXfy5KsHq4fSZodO1nA+bhQ83zQqPFuIaJBs6c62DsVuwzL8/bJp+ffnEgZcXDKXsH//x8kBazWUDdyLyb7fFSYl6Z/X2DtYk+PBBxGS7YUSr5zGyX+jamWQzt42QyJfoE6PV4x3rtvQpm4pXj/fWBSFNiPbHFS4QllrqEr0AWUPML+efxYylEl/AAiSxGeRMW+TMkc/0s0UndKt3SrPwDZLG0WxgXiIwhlk26BMUBRHfEzCARbQoCkqcQteUUHDidG25DU2TYPVMdwC0uJxya1n1xsM//3qxUfljuvTp7X65qjmnd9ip5zf0Bjw8/PWMlAI0DnOPdU2gsnnJJsxebf/jn03wpCjrSY/4gNYF5nPKl8TnE2wVoop1LIOuyKoWp2iURrr16bUWBOHgaHXAf3Na7nHsr3HYkqvyCbSgWJAEfakv3wvaJogW6AoYgcvNn5Vz1TNtPy1gMQhN6YcBje4ZxCycwSBOeLvBo1N41G/2nnK1LHLv2z3sFh4h6QCXCwKwbJp4ASt05u1HOO/fxxXXglEBckwkO7XHb2Dl5zzd0/Lv30lHDrI5thwJVLIbKYFlP9iA93dQuEPi3IcxKJtlJCgmajhCFWv0XBGTq+hA0bwsm8rr1ryWXzaDCJ3LE7OJ4fvVHjnANaeYWvR7DGwCUWAleBJzhb/9WowUsLiRa3017SnM1tpwRr3ibc4YYJ2PdAd3VUSU7FpdC/0qekS/gLUWOmmIWK/qGcbchEKh2bVZg2ZJ0l0SD5YYrSNsllsfY4E+BidXedfCkpdag5Odmrv85zNGWdEAuNw8/+jiwmd31kjBpNYNASwN//fH61FKntAvBg5nzbVrZTf9mbeUvrxZTwMHWpiJKuzfmdYfMUt54WZF0KGibCxrz5yqW7+Ao4xj0CCPUMuU8AZHK/kVLeXogqdgbY1QtZq6Zn6ig9DsIv8DUkgb1wlOkEWCE7wA0CzSGeLtBtYaQkUjejezhSqbiVaI8tRptJ1eHkomeEHDZA8uFwlXyBTw/gxn6lR2XNsHqcMGbBB+8ztGNN/Q/7dpjF/qc8mO9tR6QsgUkjArda5y+97wWojolW9c9PLWFK3kBWLE7JUUWHGypkgvYu4fM/fULMc6Hk3nEHGdz6CNDGOTu0jAbuKrgjQ4eYbxU6fFbpWoobd5IBlm54KGQ+Ot6fUdTGbaen+anw5TKmWKF+fiMg2FGiwTblWSZBKttTLxEAWWCNGKHdUBwKqZTr3pgohGCmCS/rOQoyFYF1BnS5Wew+cNxz/6lQ7KLSdHewdWj+CQLc8xmDaVvYGmyqxCXICdmM9rT6xnveQjFY3SY4EzZYN0xncGRWQgFQfE3fG6NuG9+8NrRSNjbgXTZOfjrEjra4RNHvJ2l/wmZZY7JZU8/NjTz5pXvxD7DSrWYU3yCl2ovzyVg8hCrtczjj2k2QWsu3QmPZz0eq6XTsr8aFHAotzAEqR4NvlRuQ8Udq91NKxVgb+s9uephVR8vrgODxYmQHF5YZQHdI7mFRuxaic0IE3Nh9d7rMjUYN5G3cqA3plESQ5Z6iVpkWjUTXvJ2YLVdC03M0nbL+C4BSUqOA1mMDpx1tM1TexE1byWFS++Mp3r9lytspLlTU8se3bbTC96cmo5lI6py1siL40k61oKz2LiCWcVBdxhVZ+jxcCa+XPf96qBaa1TSfkvUq2+HEsk+MtydQKx8jwPYP0hxMwQp8s7Tpm/qoM4D/tnaqhLVG5LLKkl/iQEWPPB8u8PaJQCd7Zzxl5mm+0NbtgcNzTYNj/cztvmFssXXP8YL1JzeZUOEjXNOhybJywZWIR9yUKBzJKUTJ3FAimo3GfdRbfmSFxb8Po0CieHbcfg0QWVjbaRTgGaRa1VYGDHjWDYa6gSeBi6hj9+yBArAqaqPxmWLLwpQq+jvi2T6Q1E7Z6ABXMGrPDfmMEXkhxYrBgtOX1i7QWmGCxFhO/YNn2H+irffmtr4EFssHOGQvPDra5Xc2T5L9+RcuAiwEEwi7YWajnaJKov8qCFY6trr+0eou2AzefchRIH4T53WKz1g2VV0Ih8ZaaGm+qdqW+CsnPUT6mLXAHWJ8fjNhfN8yK1Rbsy8fAAdyBxWW1k9rbbQVEkKkYHMD1CB4pkycIveAjIoJcO648bQ1QMART3DPtHpFAnF/dYxAbGbV/lWzOc2+D2810Awn2180IR+8pZ5F/1PlETpPr8QM9rQD8Bs53jmvbYRLNfhUvtYrDMKRvgtnbZhNHYnNrc7OMHoBvkXCIaEcvdHhcxHaJrr+af0ZEcgjNbUjrw/YkbKKG4LQE5Oq3ZNY9cGHzAl0eOObHmbgFAfdRa7goceFy3fHSp8Fe7AdJ37Jxh+P36vFQztyTWz9cQWM4M2bIr6dPhGRsXjVw2ILuNSBz+ZKGVuwBs7ZncDvYRNS6kcQAuwQL5LnnvdMIjt18fMBEshZZ88AgbNU2OpmokvB7hbIP6GiRvtQ7OIaQiW2VZ4BlAW+y6NulgTaI/gRPmc9Zj24PzeN1pTRujD+AnxZTBMQAH6oU72US3wsMBNJ8Jo8/NxduQc551/4pJQzYXr69zZBQG6xaCvLjODmYts9dzSjq8LQio28HPlo9g4aLQXP79TQ0mNOtI/4CSRLpDUCuGB0H1mXlVAzMZZCpR2XblbOUQ8UE/vFZq2PfZKlKdRbO0qFYBEXhBik5cVgr9pxpRv9YKJ0GN8Cmrr4tIJkJnAR9zRZsxKeWli70CoTcPsNwBFgCEbgermK+m40yQ+wm2Zj3v73Za3Q0vZ3zfrQnia/zYKAIcR5w/6NUZW8YSOLI+oyZwUS86lwCJAo+0AmtXX4f4qks2xwozjHzouvPKPMzubBbTaic2ej6/7h082sjPP/58kO5q8JBxOhnlMNLnDkk7gysRI8pYdTVcmzPiEIPFG68lQxrDVK6Ic7tDhhUtO6grUbX4KsnQK7Rrze3v1wgsOGRa1aFMAhiRUh/EjkGKEH3aQ0JvlW++kx8RcYbaxinnkM5WBbT9ju+td4PpBCozq6JHW6tc15E2QnCKgw2wBiRkJmksFJWZaFyiAcDaDriaNDxCtxp16cwrsQRXulRLSKBj9vdGOO+5FsCKshfpd2p2FhsuWnp9mQcz/LTm/ftJZiK6NfR3r1mSAUGvpHwV3OncemgcL6pZ5iOdkYEVQM5+WNLxMvbBLb2OJkO1OwPmXZwPZqohomEBOCdJBctDCZmJdvG9WC9698X+0WKPUXdc6zx36xjWFoGCSf1GFu312CXZ7fRw6LByv1+dXZyKOzMuI/B0rg2yP2AmycNP28mg6pZwHWH3jwn+d//Kdc9n0zTipgHw6wat4QE9mfpnalN34A/vsj63nmHYdLN8ekOP5et7/hpd/PzmftxhfXBRB21WgPIxa5FV5F+8akklpA2sxNpmeaLxxrPNggRdAuap1mCUzgIsMO56/AQDIcknMKKQajNiMdpX1oyuaNlmj4QaxMO/8JTGlt6xZZdz+6FT/rlBvMG+d1Uixc4QKUtbExsRDjbwmVg+2K9WSaY7OOj6xLqAO5V/3277eEBn2PhAVtIzojziQwJbEqnbSPYb12srx34ByhAMrL59+GXz+gzBe7+FlJpUKMcP4/fnn99eXu/z2+3EXOzHpCZUFF9WaLlnajIuS3D8coltDjlRKLyqLsa+VwG6U1Wh3Zprugr4isF8TipVWiuDykN16gtkLXwbicgEWRRDzgH9U0V+D7T6YulDtUm9/NDUXh+R0wVSEOl0ZMMDe2DcaIrxtEVt7nv0STxAcsBksI8VyfxBRoDbyUAhNQBY0ZXL1Lay+/2YqA4RFEqrQHZ2zhCnFCWDdhhHC3kGb5IK5j1z3XIRQYxIv75ZPb9/+/788jzu+Y7dfhg328KdIw3RnfZlp4PgBouSWniW7ELIgHoPL0dawKKkcgX+q8pS9XWwPLfW3OMDjrIsYaEm6TptOQ0abp0qaX/jGJbs5gfgtfNEx9Z9JevZ9RqxOL/Z156+oYGa55BoyaXIqSR45pNzb7FfaL4Ok6kHEfZveSXjMAl0dOyh1BXMDPz7DJy/AAu+AfchC1qJAkRqTrlhP8jIxUcpYDzLz1tbBBtxjgUerd2z9CrBykvdcdRtPPz8iezrGNN8nYf7bnOzkk5fwjXNMt1JvYE+MHPKDbH9S6vwCZYUByX/x6JDIY0UM9xrrstp1MzJjGVdSpWfXpMVQCDTq0xJQoYt6CNxPO+IpKRKVXaQ+dzmuN39uarDRyZaqowq7SEW5Hgejdwesp7n82XrYeEp9tleo77BUlqVF1y1SrACfO8oIliC1jZBnCRWzmAhTk6QEuKjUiF9wNyhO5N/AL2KxeZCpJMx1m5Laaherk/zmnAHLGrN0/J505WMFrJAmh0UOBJWxW3BIXMhzALJzRrHMB1DdUOLlBjCNgZsvKOJEy5MO2QLGzkHfShbcKXmRS+Hgn1HOKlqMRkXTkLwCRou0rcD8hBgdcbdxrd7ZCgBlFItGZWxGBWbVhgMQNMFMzufrFFZAfcoe594tWWLcbEoOBCrc0CwJirHmpAWMF5LOppsb0+El3qhcHZRep/dxkMMxwRMM2OKPgEUHqVJcxCe9/es9y/GT/v3J2Qe2E6BPkg/fjLcFlKI2xbS2aF+IHWvUlVVfZaVsXyMJOJfn+Hw5jlvytZqzuBjOLU2PerS+MFN2ExoiSWJZqRMFg3jT6Gth+x4KqNpnlMCoPF/f0WqE1p+s1pxGdGCoccaAT8P7sNZEaeuVETZThaVI8co4ThNViRYBbgP6TzQxCf44rJqgIsSTDq3njJ0VeUygR9lOK4V2/e7sw2394hGC6TEnG0UsJFhYCHZQXbzh/3jY7ne5CIYZtqqeNFLiiQWaQRVTBxiXdIwHYP1kQBM8eKjPmAP8xYZlOjInRp1ETLJcDlgYyQDxrGGFQ3afTr+fIK3dvoWhEXEAEhx8eZi2Vn9fYPI/oZWnJ7iCQFa4RUnd41rIvKK8Jl5xWV5tFX2Gqf+AKTOkMOWbqkvBr60nuHf4NexuMDnpGwvFlC3dsdAfKzbhVi0Oyh21yZb+IcszG99HEm/1+EMJbrhn94rVgWRxciaciDpuJtBUMxjW5+AlYHDHrfiKLklmQtQQEZ/65LMz8EGYDudnZf154IYy9/dmr31iiWVyy0RFnqN9LFol4eqQYuDwyZ7G3EOl6f2z0eylX5qlqBl2hTzGGG2bwC3KYInSi7boaRtDcWhADJ4xMm0WFcBSx1D0DsiVEUgB07E9EcDWQY2zuD+hFTiQ4YBZ4tev1Ui8iMO4KCZlRpyGhUk/Zc1tH+9zEP+q43gPgSO2x3ur6x8A8U4nQMoOSGAEvk68yJdWJ/zVKm0rO3CqSxTv4iYaFiz0wsDnXoIbHgudZp4asBsLRaHI6FVRNjcw7PZ1/ffMB3YIVTESmw8wZpDkJWfXBkkH88ezvC6GONPASJnWSEB4VJygiWaBQtgFMicjxee8UWakitx1WSETcBB6sawssbCQhSf3749WpOB7IVH0I4MoANcauMRMrcLJzhzR7u8DAMQW8AzRS75AywyQRU+sVLY/CnUrdwNNdh8Kw+4KOpM4lzNw8AgWvRTxVztgNcOWoYLVJF1hTCszX27/fclubuFu0/6jpmokU2bPcY/uMDwUJHPwrQDE6mOL5764Qp3AW/4A+QoiiW+QwIvzJrNsFBgeA8sJbO5iD9+dyU1T/bWsuxBdSsNbBO7X5Ccj30UZfLGXoPDxF4hYXGdb/v9HS9auLxXPcswuATNoghYsFpDSVbFYP3tP8tH4Swp6WyMZnwxYjiUQMBEklZ0jJaKh4fh9kwM9bAEzzlff9qXH9+7UC2AFU/fSIs3y1rsE5WFvsBRtmBIAwgThEzz0U/YctHoRdyGGK1tYs2Tj2PaGuKmXws1X0kHgxHzPIdh0Ut/dCvrjmlrG45dU7G5a33qhtcAfvNuUh4Enje94hFH0FqAFWSk1xqADQmX2EGAVVBgKflcy/mH3HI4VHmRFPpwpmVRMbWcVvG995DaVoWDuH3SoL0ZQG3QqY4Z//2PMcbVWaVRaKmOLQErH3tfI8VN3OWXaex+s9hOOeHtBwkVVgiSeRvSseMgc4m5pIihBZxXQMU0zyGtHnIsB7wozSc4nm2J4H1u5YNTwj0V8yMwGxwQhEbQWpRdry3xn2RDOb6LsvHpDJ97LJ8QqezpL1hRpDSb/nTKksjjnP0eEFNz8bW6WtxQM0NSaQtYWYYja0ADsPKNl3Hn/ZFbycqw7bFmSS+gKeuE5XBQ6egH05eO57xtE+dTdIkhNC0WwZLbUEbU4ckEZhmBxnmGsHYtIf01vMykyey2gDnCKdc4OKxhx+cBNlUoMCAlb3pGJ8wEde3++Yh+beZgD7o6HcWSUq0U2o6YTBT5hdfuT3svIs23ClwWbTNUS6qYfnArGsGi0PxzQhu+hUfEWgyGrLzt08SPH9qLnyvJR1bIQ6fOoRQSEd9y+JfrbdUUj3Qnk2GLzKK2S+bzScjjF2PF+mwixzGI4nXONVUIxLfnGRNEUObzdnK8zsQrUnWr1gSqW6d5tGYeQ1hc4pmctAP2J32s9gg8Or4gVryAMspxQim5ZOLrkBX52CFVCcDfsLr7o8NGHPyYczbHCaBPweJGemNETAlPJeoyYsbQIai3MpMKfOf7U+P9iVwOGmJrCC08WyE4TxKDBbgEK0ldUkAFpQEsxx2wo5LsV5D4NpSSgJd/xWgXSmYVUhzuJH64hMfgeil9rE/M7Hp50KuY7N8RWnQ66dxuMmTXEPIVGXiMiEiusFn4Ea57pNoNU7dnfmtFVqzpv545+Ue4FslfwUqnRackWLodR/kEmVXBxeTYNDyxCNtPHUpxQXUOaWoOAoyX3fHzAqhoBElGWPkHHsSeMD3E/CHC0g+0KECrwqYlF8UNwiTvVOE/Ma3A2vQPXZQO359OZRPVDZBTwBvbTg4gZigkPnrZdDNfXq3m8IDDkqJIZAzekh76PtN7uP10NHoh6MRJrLKeyD7HONPHN/wSQy+H8PPUESmCBR/eyHwZ30mxQ5wDLTdR94RE3tjPC1IIW3aoMG6PV4uppUA1RDMWwIJqoUn/qYbGH4JEuyXdQ2LkK3ne6Lwn2HUtbOoQ9nOXqXwsFOK3CI5huCVYjXFlB1dssuGGzX3dduFqSM0i9K+5eEgkfpIgL9ysKq5j7gCUuGNVVlcYHbSEkxn79GYSHiABmaXXxp5dOYdJ0R31O3Uj84lFYKGBYsJEj0+tUinyJH0KdRNXtuSaDpiyBrEmlhVxWvAJZyIPsCDlW5JBRm42qJI/LhApyhiOqNbNcNkAU4UHJHMgW9teRg2RYsIsGTTrNmXABkt2VwKs9mI54m04WX3/8b5s9OB++4DrfEW/h4cI+zb1IJf4ttEc4aLIMvE5BFJwBHceOzPSibRa+IXWyTCk1hZpaVvScK3qjvGQyZdppk+w1CwTyUurSfW1MWuBQBQLVBdwMld6vrK1vt5YPT2+PJM/BBNwdce1kDAGWBQZXc0DjeUCJAF15pRGJM0BWpxJpXIJWkLU1T0RLOoWKSLob/WYJVRjUUq5tnQgwgSI5Z86IQ6e377HJH0NNPfolrxwP8f1atBqVEGJGGWTwuMBfbZCI+7/8STHRGPfShZmo/oRAxVYQuA7Ur49S/cS2d7iu+3jOedyyo3/cgwp+AxyD8nUF0w/Qu4bUnTWLwggQEOPQZuX15/ff8TygK3oV0TiHL6X3Z1Tk4dugQ1XbysQ73CynJ+hxGihZhzfhIRqc6KCASvkxW01xAm44sM4oaM1SXCRwf3iiDJG6Gggv69ztmd6Pl+3w1Yw45GpGnfVkZG4y+b6NuO0iQSbfRBnzpHEjpBPLOUKRlB5OA1g7YL+xHHFLAZ0ezKpz1lCCZm9ARbUSBHkQxRYxCWpZhUpsQH4itRaDrkPBg1QJC/f0fH/4++Qf+Ltx0u5dcVpgT+THRpBCP+Z46aw8atGefPYRmxG1ZorWgc5iupvpCeGaq0ELs4I0N2PPX0x8w5ErBY1CyVkJO5OgwhmCosVe7wvplfYhEsVvs3dkGwJd8X57q7fKEY0ms25DonMCkpnPRP7LK56Btykh0H7NDrD/IEhAGDB9/UyqvU2fu7sbztb3U15W6WWCkq3j2LNVCz9VeRAylSD1CZ2hy2Uyhy1V2gZeoU2EacfOIT3dTzYZPZwwUVelYTisIgeBKEkaHfLtad9HbV0OYjcbEpRs/gyV87BcnUd3ubIeYbFHwNYPIpIooiRT6BM2uYKwdEF0SDTania1wPkeGBGvlRN9re5on25G5z6w8N6vUWeBFWCEP4bXhgbG1kMBK+ZXbuLTdtwadfrM8GqgqRbVImWh3fp0JhgcRoOZyb51barD5KS+A0pcc4KTB8yQw+WEQbn1hx7++5x+r5j7gZIPT8156GXVXXZjD7ziuyzGHKcR7cAFX6DmLB9v9GUbn3MpRIyRfOgCUEU5JOrRxOwmISGiOFyxdVKlK0GEKX2mQPuX7esMIK1CnJRWCwgI43t6RZmXVxD26yxpip3MbLs/vGpu20LW+0kNdI6m/bENAMP0c8MWE2KsjkmJ+wZqNogEu7Mt6hgELtfuZaT0gOG8/6LiG0DUDjyjF4jLobH0tz2afn2+oNHDyNK7yfQgnAlSSwplosIVWmIWmM4V9a83IXluu/miZa6EiV6jm2XcEXRf6coThVq1k21IIKWGjlIPH97W9RNlaNAEmYaXiPa6SCVuc5yubtS5HNKdlTvbjZGsrZp2f6MxRQLG2PIDdQdDYI+vIfdzgicSYCNk0i0TyDcnconi6bL3RGU/2gvFT9AYfWrxUr8JlRJQarIQhfSPr6D49CrM5sGA0Wl+v6ymGPeUybO+UZRI5ySzgZYUc+OIupRpzEqj5fNER0IsVaiWzceArqm3O3XJFgKK4g0KFNsNSvM3hCC9foODokRPHyHqdPrlVVF9EQUcxhw1tEHgMdp2jBQ4/0gOp0cvCY49msbfFCyZWnZ1WyL21/xlNAMkymhJjzhAS8K72kBkclBbwEndRyVP/p7piG2TzekpDGXSsWM5YVImXOkp15ppP76C2bqsWseyPd/i55EgK/8wIJgla0G63V1ynQD9sbXsaCZ6QVAUYHEaIlPpsCSEolwBN/YseSU2gRLDf4osN4aO0/ToA5sGmEG57jNtLbBEKbIuxx96X2zeVNj8HOhCWfw3g7aq/HDvYy3rNAqPneQPj9XSfLejxBToX9illElwqqRS36cra9ZjN/yffys0qk4Q4UIH1eOz5+utTePP//5l8j3p44zKxW+eqtECvhmOesuYBEt3gozk2BpTU3D3qk2tUhYbUdyCiVGUrkLUat4SWk8YajgmtvK24rBQuM3WAfr84LnHIILygXb4uyop2dRCYnOHR7rZIeMaz8sjzlyYg7g42DPxIPjnziqT1bBVQOhuw3fbMenFl5C9EBlqmdx6jlYaag9WzAowO5Pco347wKoxDJy+GDRz5cQUawg9QIv4Z9//eMff/14qPn9LItQv2CVZvfqbTiRWMWL6Cf0uNAA2NRq4A5sAyz4PgotTcD6qInESMVEf4BLRQG08TewfD/x/Azu1/uyf6c7emEbpQIfWwPOSAsli8Eugm/JDcwcdDVXGGaqt8y6iXrz6sEx98IOxOV8+P4jk7xp8l3p8NK8CzkS5n364qqnECZ+osX3v4HFc0SkpFyAnxeyQ3E6au/f6E5Rp/5+X5/0aaa+CqtGWTgpEAErnrxjsTul92CpoUVAi0u6Ng1ZtqzGxBmBC4k3T6DsNFCAkaA0HqZjuc9U9yGxAlhvAtYYqTLHSB6uKWNSTO2uQ6TlDaQJBztsYFpfBnOyV5/At4jv02PtfvnojmRdCTRLtiCoCMFS8YHj8ZWVMfSsrsBC55E6lwLOn1gVVH6YCXWGl0TKhaV8eIVG/QWwfjzVQiOjwsvYWVULg1osRSqoCkpy0rYDKdq9a8virdduVuZtvKx1wAMBDAIXox2mHEQ+kzQxdZYcQ0HLGTAD4cRgoYn+0Sq2todU+qKjrtIqIluFjqMAPA79q4/Z6cX783L/BGQY26NA+Pg8Hz2RYINcJATLlmvWVsPI/jpuNgLnTz8p7CFZhZaE0b8dwTvWDmHQ1zx9UgD2HROzQdhNxsP3j7+/neyg+EWnbvcAKhY51bT6KezuuqVW16A3WYu31c6bdnssaBGumKsV0MUzppuVyImAKW9LUzZLzQxPWNyrEKznZ4L1+rRYriZ6CT1K7AU8e32E0hmMZp2jLY1H/v7vr0RmWs7vehjKxJK13gYUXQSLZpMpNTtvUmf1g3+RtWCwWZiIT4liMeObKSi0vghTDmLSWdgkUluJ78rt08OzcjzfVtYvBj2+LNCtBKSUfOIk+R+KgAUpHCo01VpnZM3bG+yOYBKHYMRug0rRrDYKKwGrHfNBxWlViXngl/a6AAuZrId3gIXVDO8Pp+0VEoZgfcc96KVzOq9IVM+uR+v0/RUzZBbup6iOkP/xx6bSEJabJjmIK3NEQDYzG5dczivKMsMcwAJdMp1TkWFOofVljCfNepk6ftFly/SOhRxBHRSu1KkfLydz1/pA6sMNKxApovGJleD0iwMiKlec05Fn80PF7AEt9I8JUbwQ+jHFqnJ/nydRkf1J0BO78eLCW5UuwHr7AOsNrNibwZTt32iKYDxsHNg+4qKIww64Qf39HZwj6IafchXE6uHn66LWWClKIOh6vdETp2RQZFTImYiqrMtMVaXCRuoFOYhiT2754qxU/vps6bswnUf6IQxq/RNI/fXc8Gel9J86BboCnj4Bi9qkkLqRSX0w/whY28rI8emK5usV066txqBypmEXiVdV075TbsSItwARwiELiGtqi652A4sDPtyetXc4RoyKAMI+126f0Ay5gG82uSCyPkfm/YkZSdtukvn4hAHZ102HNkxjxrkNrFyq7FXSp8UWg0vRgxbAGiqw+MToCVGyQ1EqNhBEUCvfxHWuobvl5z9g1N+7g7XkLO4+mHw+Th/t/KdaxRQan94t39/A2tmDUtYRx70+Mk2tO8alSLSIE/2rD/MeM5vfgmlaOmkItyB2ebPQLLoOAtYzwMKA9XIKxcmjA+R4wegRpqpq+++v6MsII26AjEYbbmUr9tpen6YRjunbswz87/f05OmSrA10OTNDX0hRkDBv6QZwAlYES5qYYb3UBm7prVVdPWbPBFHASpD6x1snXA+JVPzkY9KHFDay53gfClYCFz8qpGKwflUsdmdmshfVC1/HSNK8AcNVA1pKqU40WJ+2/TOW1vhGkltOhY0ay1XZPCqwnl4AFuah0GzVaFQGl+hARoypVbEvx/zq9cf4OEM5h0uB4dLlnfTBTd6N1LL1ePMiM2YqFVtKrf2qrDGMWwhBrWwUcBkKy5XcjClW3uNsAi4TzDJMWVGft+9/Aqh/vtS31ZzkAD8NEHM1mG7JfQTNFHWaf+Ou+lWx0nToq3MVvFSQLXCt8mK8arO8IY9fbeeMSYKVB18XIWLAig9MO4EnA/Q26jZcPr4psF6WYJhGORoleceB+mH/bWVbe3yxOIdhoMN/3qmU8/3irtqqN/PtmJOZ/BE9W5W+3Qg5COoNwaKBDdDdiD9y7RyzpspBpVpJ8Tg4bGGomFhqjB9e4SU8jM4w6KJHn8JyYQk6xZpHLL8i9SdU8UElWMNghKKPxIQYcSJdyGq/ALUxHz47gEQA1s3Vup1FTWg7YWCW96BxnlwPMVgYnybP4dvL2EHvO3kL0OABqnAUKt+1mblcTAP2B+vepJxvNPOj8rHYaWcCxAesejOxpqY9WlVnwFkbIKUebIE5AEhONfgV4m1hjGmkryfkugt7VN+8/f2vv//cVM6tOAH29UClwDJGpL4KcPojZvoVLEgM1rZir4eDUUXMfM9BwMZ2YQQiROsUr+cUX0vgEleLdku8i/b4ZdnBBAuqOLHNwjEUsB675x2sKp7IOfTRUtR8Qg93qE9PzTDYcSHtdg4t7tbyu+FocJfFJhLSebJtGqVX33Kz4KbsDzOqeyk+B0Qpx2VzJSy9J5uGERilNU5gFLp0+nu10+O3f/7j22nqwXEiCl8FNg8Y03wJAHzH378F4r8Shn7U++M2zGLYzwwD2b6HZFPZcnliQJ0idTCVUKZqQUS5bst++Rtx6cupXpuGqBBeFFj376SRgp/1aBZJ2I22wQN9w1Hj6b5dKXu6uehdQqhcH1NO9U3XxoANXutSs5bZ1TQb05BoqDunQ2uWG84yBCeV/PLKcisBeYEj9tQUSjMr1IPZWVq8LNjOb3/942dzIskpWqqvaAGpYSEeQIwl8e816tdDSFH2XWW30P84yKu0C32IKVbzgzEETZ1qSy4lXswWu/Ai7eYezlRNsEL7FQ38Kw08FYuM+8diiZG/fpmcIx/MSY392K5MjaBzQnOAHekzPL92eytOYLrasJOl2jzX7w1SO/OaKlpbnjhqVSzCqSaTcFU2HyNZg+4mzz3qcKqOKD0g5Hz48dy+lCT9rNTmS1qrVbqVLhUE/3ajlhJ+/A0uKfvzF61AcZ7vRZYk3tHj27MqJxSVH98fxgqqrycxJopebLBVdaUhUsK2F/a0hQTrjWC9CVgPR2mj0XXwlEwwf42cKfrSMSM1bcCfwrYRfWs5Pc1tVdk+U3WDdFabprK2k2qZg0zmfJADE7cP3tCCejGZlxYCAh17JsNd0QjDab6LiX1r6uXoUMbCStgHUnSzRG6aRUz+HVR/APUJFpGCZIPePEhlTaJF5QKt0QLBxwPqHpz1JYHoJ7E1zdjy5dvPl8UIgZ09UFgxRQN5e3qnYu2b9bBUOhtZ2GUPJqvWraBFJLA7IFMJyw3sJdj2iwcTTbfuZHJYG+htQILd9MFk7qTTW4cGRx7vDSdpgxHrgQwENSyd2u30anEHLTuOVm9vK4dkrl/dJNLqiZ0iUp9KFn/8s0nwT6yUEsaN0BQgxnHHy6GQzFmw8cobAH01aQpO99+Q/vnnD+7hY8IJ8gjm+W/fv7+NO715raFZ/pFncAL3IPFKsJYA6942MrtS0sDq6H5QhaNl1UcjTF/pE2yIdibmqeL6hzW2qvlnH2VBv19V1iSLhT27Q+ouU/x4avHWAIosdKDjXZB/H6L5jSBnM16zPimm4zaRrwVXItWiQ6pU6StMf8q/P4CESqW2+AYTygCex7zY61kuIz7p0MU8NEgkQVz48orUIuWfECb4nx9WCI4qHTA4uqiuQ0JveHUFrJen9/umu2ul+3rKOORS21l6e+mfwU6y68/0yNU6U5B3VY4YWhqWgi1GOcPrOq2YRG89MvFAvQiyDR8skMlfMgx6ny1B2CaNoFAhQVox9n/gDylWEJmgUqfvv0Ml998fckOKMFGEH/VjfcGdZ9rr3FVou7nXAqvVm/Q9yT80Jss97jgu3WYbDRabgf5IlcBYu8/cFbYCFrJSx1Jm1k+vPZQXSsPjLhNeRlbkTLdrjMaEdU7HWk7fgDKhhxh8C2QNi/MKt6fz+ZHGIiXdClSxrzwhd0EJz2JIttdkgZQtfNlbijkT0TFZtrLqmf13lYp5yv6tsFgrOKV5DCXf+PGpmRnmUoYMTcdwwSVddMSh0lT/N4dWeijNLtpaz1K5d+FUnCELmmC2ASMSO6Ownnn4hS75aBJk/Im28lFF9DxsPXamV6N6vaKtnjVAr3oOPVoCyd9mCr8QYsVgKc2S6PDDDPMceMhAsaMhg56bAge/2BAaE462siTnSbOL61es/oAsvv7+FCFIIE4iBTVX+jUO0LGKMbDnvjPKi6mXRhwhc12Qe1qq01w03C5DuRiRcJz8WNLd6QA1T4L1vLIPfU+XQUemStDN7A18DJUHB/+6xvZfDC6D0/VcGsKEY+9aa3bcYjKvJTdNAa7Tr7bj7kbwm5N8b8w3kpYZ8io1jtOtOIqoBhlmWCxOJwgSiyTmYdsldK2Y+61t8n+Y9U80CJXiFRetBlS/HVFeOZE7LaXOZPKGMCPElMBCNtPJPlakUdWwXdxy5Hqpu2g6nQ0LApbtlYoAir+QW9hF8EenLvrRWsElQnkOw7hrWGQPWWKSQ2eH3hF1s+JQlnokvjYtfIBF/5JGlXw9SRH1hXj0yEJkhuikqG6xURGPO9uzjdZA0w02aktllTMZik3iP5irfycyqUGUlMTZiK9ycycOzrWQ2jFVFTMBxzUxeStT4ySVTLCm0+PBnfpoI5q4M6jFM45hebZWLADiPOnsqZvOXT1djc4t3k7cuprrF7kTh2Ym43G4E8ytn62hX8ESrPB0s+JA5H4xPvy0ca5mD2CXukyM0mBabVVA9T2Yr+EJ6SXhg2S41MrE/+f/Aov/BAMlOhXLTan+VK0sktSZbaXXzx4reYoUCaVVRGX7KAov85hOHafWuoAV4ewwAN3PprfjXDbQSErjAQrwWHDbsTAgPEsy8QsTnM4ZyvQmAdZsu86W0hLh5pJ/lJZTCqwkwKT8Un5O4OQah1lmFu4QJFy2MAhhRbt6ZkUv+O0dzHwup1JdGTy3/3gL8lt+uVTk69jSKj6CiLyS/+6iVO7X8NizWmkvr9nIczJiVrEQZB4T/WAxVWTPHcR0rnlVvO0ES4tk5l8fyp0rhLzFyLLbGlaftehKI9jLJDNVRh4CVjY4lKg6DJGH8ZqsL2Cp1GXMRvfbE06eZ9hdF1Vn25hd1ZpzKTmSZJFRWZxzRieQHCFsM3m6//a/5PN1uAMpJs9GFcaC8pFr/hMszqmf7e0we9Dyu0K/jAEkqlfctkXuGmdaqUzSKd+2i+lUMA3ifVOJb68j9pR6eo7PnsU+GW0PwLDihF5Kun5yUKoWTpUKbUGTMuNOGFZrft+pLSUIGisxD7+BlcAPmEVG9cw4izcyRoBsWVPSwItrjcpebrAA2zubUWUkc/jfgfplfoVuui7MBbKb5HYjftUwwUo0C8eck02XUW+dK9bqbqaKZMTxAMycACTyx1xrXrHgZFh2AFctW4hL4ADrfsuRl4zigYwnlr3zEYYt0ktkvixg6rAgIS0kNSx4V8xRcW2OTC39/sDvCjewWGr4AlayhcYRI4qiKwdiBC6Clcft3eVQYL09P2jNSTrt6xm1zzWN1+aLzgr0sfP7O3JczcAA1KCs8WobLTWiK0LAPsHiiYBxxPkZWa1sv1zGKGmvDgqdSa3WLxh5zc+lw94c6d6jFcgtdduB8vywIMVIib2IijFV5kWD0MWIpeWjqTxXQA8UOAoVlx+exPnaxzBnLrbvf4IlM4W8xyUo/jRXpUNULQKqSzz9H+Ie4XY99hrUsVqf0zjtY2ZWsQgU+fIBWkbpJJEA+aunHsafWkZTB7oMxfFj6KysefRuFFrqSN7Sp1RbVQA4B6DgRlNMNoMVTcVhgI0lw6yZn2Zya9vWczDrmc8OV7xbbi8R0nrsTKjGTJb8WcEEbFshFjZUkQrKIakrhojVh4Kx3a4L1WJS/Jq7P8GShmwBK/nlEkyB1M5DQ/NFhPV5DK1GZKrGXYKVdv9q7EqXnASDYCoHIAhEoqLRlZBIIGwpwhdJYSJeZekfff/HsXsGvK9RN2VZ7ibNd8zRPXOfHRhQcrnRXd/Yiw8ApGCOQEEth47mttzpb8GybATLHuUvileVU2yeQySulBE1WSfatkWqJRVA3V0bLzhgLmJc3Vy2tnW68RA/5tIIp0nX1AjWy5ZabBsnTZiMYAUAC7XOhyBMGnYQgVm276UuRV52fzz6CyEXUyXyO7CcmZRjviUhsBHS1kRex2ot7Ayhi6cfAvsnDtAnCecWFUcPwJxYnRfW+iJ6XiVbQjMjyQiaxt2/AStm5MTPT0G4LC8iYXpUFKoolVjRGTkjjqClPSnhW4KNh2gO/wu42U50aC36ztw6I0owvry5A6xyhnl+7ovaJpBtuEEVAX76YW+oqktu3blFyl9kpyXkJhLiubKEfnnIMw6snSpY/Iq3R0tNa8z5rBwwA5GmhnBs+0QLq0tBNc0DZN9Wp/i8umI9VrQo+ITw/39kUP4CFs5bfM+YpW2ipS3ttCbZXoT36ccjE4mmbSNCHtIJUngwfDSSOvn8dGIZX34orcEpfbsxxkOcEOaV6iWSroGSM4PDBjJyholuoHAvX715drWBujDywQXqzyW0JqrT/Q1Yln405T/OWEJM/YgNSZAXIlRlqmUXS2us2vQQny7HXD927L17jWa8e4nFLZ1O8YOLSYL4/GeSF69yfAZPe3OqcUOqLp/ceXTerAOCRKMrp69Eq+J6sCTqVs8fBp/6G1b8u2snIODiiVtwEIJc252ZRw+eoU0p5+TjPs+uEZm/eHb18OoGEqy3TjVusotd1Z4c4X8Bi8aXuUyablsv7BWr2h4y8yBUGjBrwakgVQn9hkyVbO5D7QBq8XLZLxzjEyrJUzNucIEuXiw4rAv755yEyviFtAQLaLK+1PwSldCNjAOvKdhXG1HTyV4LYDXqSWCuO0KlaRSoafcTav5zlvUq6OY4H6Dl4PL7r9AQCxf6tshOKMxjQDGuRkTqMlXcYJIMGk3yxPo3WDO2F8Ama1skHGE+vSgXEpxEiIJsP4gHQerPveWaY0EfIOgHbeMctTcf8liHeZ70IqjOxa5owR4PLevn/I3kG4QMJ3ApWINx9gwJnB2lfdnmwj4t4160h+fh4k2NYA3Tx79nA4dm3wf2pChNXcnAZ26HvEy646nvD/ffff744tm9bVa7m9u80leIOK+Y3mnaOhKaK5/4b8CaAqxv2WDSI+0gTTt4C4Cqj8UpTKOkPpOvRiUGKdukTjxFamlZXI4rNDRGh+vdrRuZReKuCwhwqAW+aaCV2fRl5cfur1kbuIKWo4S44Ae0JPtDCwMoTinjujSAHIClYkCKUJF6sRjcBOqUviPjx+bQR3hsk+eZqSMIcu3EB1awrgFz67hfvf/0ESOaD2Ve3F7hXifpQ/KGJixLYcNwE/4BrOkPeZO0ixb+mZuwS+GikHZZsw0Hfqkf/4g9jiE3Isf3ftas2bJsBW1Wy6ojnr0wSrykrtmzsK2Rr53/6GzJEmbSQTQGvtoAVqTBearDZtjGooaOEjSqMolVB6iXMhMeDqCS+Hf8AKLsM6fW43GWTp7fNLU9xXZn4zxcpP2ebVSKpl8//vB2WRy7+tHteg+e/Fp5qEdvGufhnEnu30GlftawR/TP3C2N31HL3lni5IR525Crc5JWS1SYMOaRoZ/Mw93fHZaiV95WaZSVMg2KwRn1OxGdglCPk6/xlRpf50QLYKmNxxaXEG9B/uYtGFV127HFRcXICubQ2DjH0eGbKqJRp8c2vQn540NTT3ZNbaypY5d46zWuQ3Mhi23foVh1//Xy0HQmu3Lt7a1CqAx9dOqdhS2V5ukfwHIHsMZ0w3QW1HCwoPlMpUIc5hfiRCNgA1hMUXKwM1Jwj28cr29zkPGhLLaZKPZJXVpYSh4d3BUFa6qNgNQXUlWGHFpjo1fgq4MZxW0QmhOXKsd3GuO7rpaAXGkBFNrDltCvTJt77am02VUW2arImXA8jjtLT6DWbG+ve+q9YAE0JuFluUKFH0/BTa/200tReYdynjQRvrl4DX9cWfxXgiU2i1wn6dl9IyZWVmQoS2jONKKFZwOun4yEe8oyOlvY39nstkOSqQMWbFjtMJyb/hy0SzpAbaDDi36MUNEIlngFangFYIKWTO2J5ngCjlatpQY05pN0VVlgPSYW3Rz/YoSDsa8plEs6RAft1es7OWlUPPVgQbY9wIfsO+Ov20lSePOmca3a0/To38D6/qqy2wQ5HaAViMcQlhfBCStNQBuW1lJWFk0qnXez4y3RKV2HAkAsiZvAnQwbT1aXeL2CEVsT4/lLa8Hh1JImgASL23gY5zfqCdSCgONJHEd3objYGqLrDZjmbScEQ9cr/VTuycnzh52Zp6XvsSfD1atl7NJE0hE2290Fc56oQPcn8cabJidn7gpU/wbrqyuR910LIWK+EJcB55UgRSNaJHHjlLxJJqGyhvEV92HRPCKLPcOHagKZnjuPW3shWNEDnSLEmUkRIi5Dcnij0GVyiM6WRLc0iRRtTT+wMi4ceT3rXas6dLbDEYR6xhN52d3KxMR/Dh3hl1XC/p3RMX6y7WrXI1Z+filWz/eO1LDoXnsYOnvc3QK5MDtZk5mxJ241/0Na/M9gTeK6p2jTYnRjBYikFSnuTEDFE577EGBhH44Ma/R1Lpo1aehtnV3XFj2gRdmEMRPbYizaapk/ubc0oWOFCeg7VPPFGhXk+fdgaQFDDiiilVqovNRRHKYzTUzMR6wEqggZC2G/4pvGi5G+NJ+sm66+GDYMyvPjDmXoUtj3VPYajD/ZZTfgxu/jqS79cX/9zzbU3R+mad5DVezJ+wyNhtIQgmpeaw8DWI+4Dx8oUCqTRE9aTJuAofNcyCeLpWBCPGzZNTxzx/sqbrbrYxWHSddH5EMLWKxT5bjeB7C0hKlo8aC3pfuV11bp1OWNqGjqJBu3qgPpiTYLkddV+gHN9Sal6cBjSFLu9C5rzPFRxW9oR9AKbIrbGDNSoP6cjqMoHGv2T7AsBUvLOX1ddcCq1BZqSc/eQcPioq81grVdqvp2pEiRIpddClYOivOM11zK4mLMGjMAGQeMMR1LOeO+rPD29wcEfK4V4bTK61qkfV5IsL4WMobYPbX5Hh0/h8CIu3NB07jf8Y2Kg2aRCeSomos5QW2+AOhDpgVbV3vhAAAAAElFTkSuQmCC\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Rotational speed\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"RPM\",\"decimals\":0,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "angular speed", + "spin rate", + "revolutions", + "rotational frequency", + "spin motion" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/round_switch.json b/application/src/main/data/json/system/widget_types/round_switch.json new file mode 100644 index 0000000000..9a29d0150d --- /dev/null +++ b/application/src/main/data/json/system/widget_types/round_switch.json @@ -0,0 +1,21 @@ +{ + "fqn": "control_widgets.round_switch", + "name": "Round switch", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Sends the RPC call to the device when the user toggles the switch. Appearance settings will enable you to configure how to fetch the initial value of the switch.", + "descriptor": { + "type": "rpc", + "sizeX": 2.5, + "sizeY": 2, + "resources": [], + "templateHtml": "", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-round-switch-widget-settings", + "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"title\":\"Round switch\",\"retrieveValueMethod\":\"rpc\",\"valueKey\":\"value\",\"parseValueFunction\":\"return data ? true : false;\",\"convertValueFunction\":\"return value;\"},\"title\":\"Round switch\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/route_map___google.json b/application/src/main/data/json/system/widget_types/route_map___google.json new file mode 100644 index 0000000000..ac2204674d --- /dev/null +++ b/application/src/main/data/json/system/widget_types/route_map___google.json @@ -0,0 +1,29 @@ +{ + "fqn": "maps_v2.route_map", + "name": "Route Map - Google", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Visualize the entity trip on Google Maps. Allows to visualize location history. Use the Trip Animation widget for advanced features.", + "descriptor": { + "type": "timeseries", + "sizeX": 8.5, + "sizeY": 6, + "resources": [], + "templateHtml": "", + "templateCss": ".error {\n color: red;\n}\n.tb-labels {\n color: #222;\n font: 12px/1.5 \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n text-align: center;\n width: 200px;\n white-space: nowrap;\n}", + "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('google-map', true, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.map.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true,\n ignoreDataUpdateOnIntervalTick: true,\n hasAdditionalLatestDataKeys: true\n };\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-route-map-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First route\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.5851719234007373,\"funcBody\":\"var lats = [37.7696499,\\n37.7699074,\\n37.7699536,\\n37.7697242,\\n37.7695189,\\n37.7696889,\\n37.7697153,\\n37.7701244,\\n37.7700604,\\n37.7705491,\\n37.7715705,\\n37.771752,\\n37.7707533,\\n37.769866];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lats[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.9015113051937396,\"funcBody\":\"var lons = [-122.4261215,\\n-122.4219157,\\n-122.4199623,\\n-122.4179074,\\n-122.4155876,\\n-122.4155521,\\n-122.4163203,\\n-122.4193876,\\n-122.4210496,\\n-122.422284,\\n-122.4232717,\\n-122.4235138,\\n-122.4247605,\\n-122.4258812];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lons[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.7253460349565717,\"funcBody\":\"var value = prevValue;\\nif (time % 500 < 100) {\\n value = value + Math.random() * 40 - 20;\\n if (value < 45) {\\n \\tvalue = 45;\\n } else if (value > 130) {\\n \\tvalue = 130;\\n }\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"google-map\",\"gmApiKey\":\"AIzaSyDoEx2kaGz3PxwbI9T7ccTSg5xjdw8Nw8Q\",\"gmDefaultMapType\":\"roadmap\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Speed: ${Speed} MPH
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#1976d2\",\"useColorFunction\":true,\"colorFunction\":\"var speed = dsData[dsIndex]['Speed'];\\nif (typeof speed !== undefined) {\\n var percent = (speed - 45)/85;\\n if (percent < 0.5) {\\n percent *=2*100; \\n return tinycolor.mix('green', 'yellow', percent).toHexString();\\n } else {\\n percent = (percent - 0.5)*2*100;\\n return tinycolor.mix('yellow', 'red', percent).toHexString();\\n }\\n}\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var speed = dsData[dsIndex]['Speed'];\\nvar res = {\\n url: images[0],\\n size: 55\\n};\\nif (typeof speed !== undefined) {\\n var percent = (speed - 45)/85;\\n var index = Math.min(2, Math.floor(3 * percent));\\n res.url = images[index];\\n}\\nreturn res;\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"strokeWeight\":4,\"strokeOpacity\":0.65},\"title\":\"Route Map - Google\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" + }, + "externalId": null, + "tags": [ + "mapping", + "gps", + "navigation", + "geolocation", + "satellite", + "directions" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/route_map___openstreet.json b/application/src/main/data/json/system/widget_types/route_map___openstreet.json new file mode 100644 index 0000000000..1ae048d0b2 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/route_map___openstreet.json @@ -0,0 +1,29 @@ +{ + "fqn": "maps_v2.route_map_openstreetmap", + "name": "Route Map - OpenStreet", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Visualize the entity trip on OpenStreetMap. Allows to visualize location history. Use the Trip Animation widget for advanced features.", + "descriptor": { + "type": "timeseries", + "sizeX": 8.5, + "sizeY": 6, + "resources": [], + "templateHtml": "", + "templateCss": ".leaflet-zoom-box {\n\tz-index: 9;\n}\n\n.leaflet-pane { z-index: 4; }\n\n.leaflet-tile-pane { z-index: 2; }\n.leaflet-overlay-pane { z-index: 4; }\n.leaflet-shadow-pane { z-index: 5; }\n.leaflet-marker-pane { z-index: 6; }\n.leaflet-tooltip-pane { z-index: 7; }\n.leaflet-popup-pane { z-index: 8; }\n\n.leaflet-map-pane canvas { z-index: 1; }\n.leaflet-map-pane svg { z-index: 2; }\n\n.leaflet-control {\n\tz-index: 9;\n}\n.leaflet-top,\n.leaflet-bottom {\n\tz-index: 11;\n}\n\n.tb-marker-label {\n border: none;\n background: none;\n box-shadow: none;\n}\n\n.tb-marker-label:before {\n border: none;\n background: none;\n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('openstreet-map', true, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.map.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true,\n ignoreDataUpdateOnIntervalTick: true,\n hasAdditionalLatestDataKeys: true\n };\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-route-map-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First route\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.5851719234007373,\"funcBody\":\"var lats = [37.7696499,\\n37.7699074,\\n37.7699536,\\n37.7697242,\\n37.7695189,\\n37.7696889,\\n37.7697153,\\n37.7701244,\\n37.7700604,\\n37.7705491,\\n37.7715705,\\n37.771752,\\n37.7707533,\\n37.769866];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lats[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.9015113051937396,\"funcBody\":\"var lons = [-122.4261215,\\n-122.4219157,\\n-122.4199623,\\n-122.4179074,\\n-122.4155876,\\n-122.4155521,\\n-122.4163203,\\n-122.4193876,\\n-122.4210496,\\n-122.422284,\\n-122.4232717,\\n-122.4235138,\\n-122.4247605,\\n-122.4258812];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lons[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.7253460349565717,\"funcBody\":\"var value = prevValue;\\nif (time % 500 < 100) {\\n value = value + Math.random() * 40 - 20;\\n if (value < 45) {\\n \\tvalue = 45;\\n } else if (value > 130) {\\n \\tvalue = 130;\\n }\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"openstreet-map\",\"mapProvider\":\"OpenStreetMap.Mapnik\",\"useCustomProvider\":false,\"customProviderTileUrl\":\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Speed: ${Speed} MPH
See advanced settings for details\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#1976d3\",\"useColorFunction\":true,\"colorFunction\":\"var speed = dsData[dsIndex]['Speed'];\\nif (typeof speed !== undefined) {\\n var percent = (speed - 45)/85;\\n if (percent < 0.5) {\\n percent *=2*100; \\n return tinycolor.mix('green', 'yellow', percent).toHexString();\\n } else {\\n percent = (percent - 0.5)*2*100;\\n return tinycolor.mix('yellow', 'red', percent).toHexString();\\n }\\n}\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var speed = dsData[dsIndex]['Speed'];\\nvar res = {\\n url: images[0],\\n size: 55\\n};\\nif (typeof speed !== undefined) {\\n var percent = (speed - 45)/85;\\n var index = Math.min(2, Math.floor(3 * percent));\\n res.url = images[index];\\n}\\nreturn res;\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"strokeWeight\":4,\"strokeOpacity\":0.65},\"title\":\"Route Map - OpenStreet\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" + }, + "externalId": null, + "tags": [ + "mapping", + "gps", + "navigation", + "geolocation", + "satellite", + "directions" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/route_map___tencent.json b/application/src/main/data/json/system/widget_types/route_map___tencent.json new file mode 100644 index 0000000000..bfe4603e44 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/route_map___tencent.json @@ -0,0 +1,29 @@ +{ + "fqn": "maps_v2.route_map_tencent_maps", + "name": "Route Map - Tencent", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC+lBMVEXy+Pj5/P37V1f9/f7x9/fx+Pj////4/Pzm6uv3kALc7Ozm6+zk6uvu8/Pv9vbx9fb3+vv1+Pjw8/Tu9PXq7u/t8fLr8fLn7e31+frk6en3/Pzq8PDv7/Do7u719/bz9vf6+/vo6+zf4uLs8PDq7e7h5uYrKyvk6Oji5+jo9fn3+fru+P2jpafj5ufw9PXy+fHm7evf5OSgoqPh5OXo6OmRk5STlZbe3+Dc3d7Q09TBwsN/gIG7vL3p7O7a2tvW2NnT1daEpb3w9/JqbG7p7ezl6eXExsdnaWrv8fLu9vq9v8B8fX7f5ebGyMmRqrmpq6zY3d7P0NG1uLivsbKJi4x0dnf6XVyCornb4OCXmZp3enphY2Str7CNj5Cen6Bub3DX8/tpqOnKzM2ZmpyHiInj5OXJycqytbVQUVHc4+S4ubqVl5jd7fZapu388uKmqKlwcnRUVljMzs+PkZJcXl9ZW1uLjI3v+++cnZ6ChIV5suy2ytbU6PG+w8Tk8vfj7/HIzPDh4uKdrNp/kNH6d3b3+Pj09fSFhof6Zmb+/fv99vXm8vGNuuf3goH5bm/6YWH43r+JlMrxrq73kpJfX2BGRUXp8O6wxM/4oi6U1/7n2drtwsOanJ76wnUFAwP59Puz4vvwsXv239zW2tupvcryior09fyYs8T1mpnyqW/4qTx/ksj3olo5ODgZFxf3kwhfsvH89O3u1dSGpcbyu4z5tljz7+3B1d/vubn7z5XK2uP6xcP72q3vnp9rr+7w6ehooNnxpqb8o6Hze3vU4OXs3t/80533mhjj+v3H5vas1PCfx+3N4Ozoy8uQrcF7l7WSprJMTU796urn4eL51dWLmtP5uLj5v20iISFyufLC3Or769Wgusvqt7fy07Xtn1/P7PqIyPayw+H7iYmDs+3Ayer5x4DQaG7Y5euSzelaoeOZjLWJnaztaGuLWlrVurvyyqSthob4umPzZWDzgl7KzeJVqdzFoqfOl6HbiJDGhY3vV1eMbW+rV1d+tNu7AAAwMElEQVR42nyVW08aaRjH30nHnUnnBMNwCuIswxAOCyYMykGw0OBaK1VohaSgQKO0tNoEj0RitfHC+gWaeNO7mvAN1mSv98KLpvF60w+wH2OfYQARDz+iGGLC+5v//3le9Nsji4jjfHLdr/hjpIh1EaetDLrF69bO1Lv5aulQkqTIwnyp1EzUW4XoH48fW5PrrbWUzYSQv1l/tbSdbKy8kmjG5jFzeoD54DGgPgZKIDhSGOvzBcBxlsXh15gGTwatPEEQyOzx2Fn0MBbsN/QI4R1MwanDXG5Cr8OwR/BSmXObDUMiGX8k8GQzLmnU4u/++nP/NDpy7K+sr5eKa2t1hPabK+HytrTeiHXO7AnSPJigQWzUOCGQX/oe+ADXdnPuoJsDFY72uE1oCA7dADQseI9cLmfjOE7U/451ZfSOgBMNkssokcDL6qaksZGZ/WtiPxO1R5vN1fXShrKmIFRprmTj8Xi+0Tv1iyBtFNEAuJnyWUi5Z6EG4fV6Hz/GWWBsgDlrkLQQhE5wD7fDlbMPZYKCVkbzGA/kXDQncsAJBnRiGZtOJs086vEtvZmol5Z3d6cklUpmPhIBEeldzR9JlDY+VDasKNysp6LpdOH63CbaFfTgqA9D2QiG5AfS8H48aD992v4BPuLYILzsEAnCgoYRRWMud9NOT1KaiN2MaBpX/4lzyJpGt2Eustcwplrd2KwCpx0TJdNo7S0XIlK9NiUlShV2IRxmm82Xx4l0NT6Lo3uwURwxTfU92OdH5081Do68g5F0yucMBu23h8Kn0/Fml3XwI5LUZoQyWmgKdZmWJsawfsNkjxFpVIFWtVpUKhKwn2msZFdeQjQ7UelVyc8yfgVE6mub5clw+BDdDTRLB83qeRi83rOnPdqf1UjmVitLb8dS69mV7S9yMBiMZjLxjRQahKfNHexCz1IkzTjAUZRFpK/VTQFpVPXo5TIakrsi/rfVzVw0JwHhTDb1xQd/JKOS1ChlWdZkApGljUQsdbg2i+7GAM3iyTnVQ+xMx8Hi4q+LtmbyywsotWfPWsWw0mg0osVSfCdbKpUzeZervplOp5e/ft3b9SvKZFoj331gRtKIAzbKhhnpweUAIclzJ6DR22Jc6Hgpfpr3YaVCdGbMCeePZxpPotFVycowx5FaKcECaiJTswtDbbi9s3gtENbr/Rs83rx58+NXx+QCRBKTO+VJwDH69kntW/F0+T2wNwtd3t3a2tpLl+IgUvHPry4sbCvFYk5Sv81MclogesxMD9XaQIasc+oO02ROQpHt7xEM2y9ER7GZ0+W9LY20gWUPN8uKnQWE+cJ7lb3SZArdiZmy+Eht+aqBHLUXFy/eqHRCaYNIbAcsWrWdZCQyVdsoKol4Pp/floDI6uzUS/V9SlGU9NbecqZYLI5h3OvXiJRxgKHsmEgLaBiTO+SS1Vg6/C4H5uDtOKXHMOeLD11eUCzw4gXbweQgQ4cLsZzL5TGhu2ApOzFOkt0J6QSy+G/74uDg4Cd4LF599nr977d204XC1tZyq1mrbdTnwyAWTkrSQiUDzUrXQSWrJALJ2dnkUrEowZnGEcnggJ0axwTagG7DkKEQKWqR3OB3HlQo0UyxN0E+H6NasPdsLSPFQ7Ns8KUIfp7DhICI6BO5o0vvxfnV1T9e7+fJ3c35bOFbcX81W6vlEwp4NGvq6UFDpS5FFWUPct+qFmrFnADHQVR31HU+2ozuhBUCIbfasEfaq7+Zpz8ImJ60s4MYjAQg0i63jUV3IlAWHUXhQGcQOyKfLnwX7R+XZ2dnV1f/QSK7u7vLO4Wlyn5su6YUIY1GJnMIIrnys/xC/Fk+mkooylS5XClnd6rF75FpELHhgBFGnaeN6D6M1lDAeYL1IOTj0MwJRtuhYaTphoeDITpwDo/LTZlup4JDszgIRKUjAtVqfzr64+jj5eXHAxA5ghnxl0rZpfB6Yz8VLa80JvOT2UIjJsVa1bRGNQMj4lca2Y1wuFq0sSNCEBlwwNRt1v0YHK4QOXfSS8Q3RwZzLngnzewgM3ARW2SOAHwMBQ0Tbi9fI+EkDXgvkufqsH/6dHZ+eXn5NyRy7vV+WInH45lWc12pJL83n+3ufc0vb8Vi4Vgr3aM5DyaNd4nWTri2NgIwCFcRKD2I2NBDcLZAKMRjKtfjAs/2ZiAzOkImQ5LVZFFdjG4XdWv5igRFsn0RtrN+//358xOYXJxd/fMcJZRifD9WLr+uVJKpjfxSenc5Hve/q8Uq6UwFLHbW0qtRpZINTQCpojKici3C0LSZRQ8xInsEbVL6w6In5UERk8NJ6B3y+Ljg8ZCMDlRkl314+er0cAdrIsBzuNgXB/DCqjxsxim0vY0WGsnkUvN7RrEutOLp/Vjx9HTz9PR0631JDik1KFg8s5kOvxoQMVFGDO5DmhZw9BCUE/PZOiush2g/nmb7CA6eMDoYtUFmj8djF316m22oWQLBkLZrETA5Ou9rHAwtHDmXTJqsbqv2sUPWcMuyQ6kvaLw2DYiwZgpKA62mSeahdtFOzEkfH48OLDBg2tETkR0+n2MG1/LjTXZrLucZupg6y5fBb5oc9DzQLQKSjrdau+MryPKoOEqSsswYRvr0RQDeTNk5AiPGzbT9/pk30TxBUvD9zglSntP3r3z924DD0Jl1nuAdpsHVzaGbyJSlNyIA6gC34n/n7fb52Z37X9TZrb168rL8BQOcIStzlwgwLlCgonZFoO0MPnJ3s2iLnhQQgDvJwNsJrtcxnwy3pmAjHQRhdzzUTpwyExZ4FgMiDzOOLBardbx/D8iy2Gk05SKZu0QA7n9KzKY1aiCM4zMlkgdmspOXyctsDDHNhiyFLIT04EXx4KGHPfQk+wU89HtY8KKC3jwqgmcPUigiKh69iPgF/CI+s9lst9tkXX+9lHZpnl/+zzMzHT9OfIoQyTnpw+IJdSHoanJjOKFIp4LkZprtXPs8FpgOZHt78DqyAyU3apAPMrrEpPe41SeiScpcN4wjF9OeWUm4oDHMyAZ+WU5wcVqq5E1jmnmGnbn75OsAG3UiKelIN77WTBbcjjjfMi7vty52WMheEcTlNegGm5Vn7EZbcHbnDmNbY1M1ykGTFeYxLrc7CJlj2gyC7UTS/hY4rCJbqqBnWvUb17BTFhhLEWZdVwnKGlKKmHnDveuj7lMHtjtHT4u7EtGnrKoAb8eI4KybLqxMrrIgfXmIunZsocJeR7Uq03a5SjwUUXUZ3FBRKUUMKBT6rnPmDvV7qzSqu2ZHlJcF1jkosrzgYZBZ2mN3IqpM7Qj4jAxMbMFjHYtp8ZJLQkVZl/6WiqrVjGqcSbW+HmP8zh0IByr0/dTuXNKsqJL+xycssbWtBAiHguveeB7ZdhQqiwzihWqSahUhOdHVJo3aSsUNiyo/okiUT6vQuqVFXAwkIAMkpZKO2UJRpb/DcOdtP0UmAGQQUS6akY0eCQ7ILtxQMdemCCm10thVp0XobaqIGGPJxZH+ZVlNFVMVSQFGZAgfyjLBIrs1rCrivuiChBdhqlXk27cs7c93fiYj7fGQd3nsSoX7qELY1GqbyFcqEcYmHpvXtdTRuaqqKkVDSHb+zSAEoUuMCMr8+nrxOPN6Xaa1IqYmV3nv2hZHNkKAzci/sUBBSgkiJdWQRCXGdWZlXR9RjchKlzLwyL9gZ4duRNHj4ODg+cu7yut/9tw3W6rqhgtaIGyRWGQvDKaUWK2sc1gOzTieq2vLcVzn1Ml9h2psyPZ5QdMFuUXoswPNi/ev44Gn89OJbkSHEAn3Ni6H4xotDBmS/Rkpxbuu4FixhrD5Ziy8FnYGADLSqUBA9iIo6g8HHX8uIQtE30T7VYjbo26JpikgX7kcxrZt1UyQ/0AqRa6IY9rilt0uKeaCCgitnGVokoNB9kOEGMiaJxePq4ejvqEqi3YvPRcnk0xlMyJysNEjI/+HpdR1sTPW7pZZ03ZY0BhYviWEDxa1WUz25Y0WuPz9fKXy9MvrKu/t7mxCrzg5PjyU6DH0IDRfIMy78XOlbjQFb4/JZcNj168a/W/TrBVxwSP78k4H8ThzL7tkHr360bd0j8ClTnX3bk5b0uPUNhgbDV1/yxSJJA92iyAeq/y2v6BBcNRBCiEkODRMyL58f6QDAYBj9817/b3m2YXVs/kc0TwUk7qpHrpUYxoJ84c6iLnjJU5YSbH5/vuXcS+Jl4f5HCrRdpYL92gKFtmX93rtPQawDKhu//z6dD0s7tYrznJ6DgEmY0lV49MQxoZPDywa254XoQrJinCdmwHqqD9CWXBBl5hZiIHEMKMxM8iefNYhXEDmLuewyW5fPumG5dN361ogDs27jh0lxSmykGQAweLxWHIOgY0qBjTd2SFWFh2aKj5nDkUE5CjCMjsCufbcJ5Cnx12CqFLlv/50w/Luw9VTcNSP4B5ZY4iRIINYbDSecZYwHooxQs/P9a7pxSo0KfEG94LCQhEJrhA5nFwd4L1C37+THXx8gQW/BH/zxrWZvPnaDcu3CylaDx3I+nO3PIPsRIb2OOFYhjeZ4lCgCaX6ikVxh1Lyl1IrC3UaiKITE6cDSUxs0yZNmmdqV7e61X1FDFStuIu44ooLorihgoK7FhdwAxWqKIo/FvVDBFGr4IeKHy5I/RAUBUFREX/0zztJrbbGWs/H63tp05cz5869585MPm1yf1mOE3xQzOOSlIkSTedV5EIII1Fpaubu0JL+NF4/mH37Kleeu5OFepesoYD/jYEgLRcnTg3pnKBU/RpJABfHnisqfUUZNZHgPWWRiMZkSJDm3hj95SeRZY7L5ExFbCLInd9ztQboFEtFOlq/JsvFk4REZUiOCmoRAV7SDSFY42WQRISpAYE6sXTeaw1HgdQYp0VkGESASbiaSYJyD5DNfMIzkR12BFFQDaxzsmEXHm11AjP5phphd29chT8jHl8hMz7PYOdpvyg1XDMyGjWJJnIMoaT+ebaAI1EGZjjNvXEG/d6rc2bYr1NRjUTvPxu4HI2fm0T6jUjy2Pr16/vjoyNkpTNpe3KXSraz8O3JgRtWBJRuBJ5kaV6CKLpfUP6Yyvm8DpBQOM1LTp411YaPEIMRiUiLocQo9VU9lAdr6TRkQl7xEGT6PRP9TiTCUmDM0JdOLN539u6TJ8V9z6YfPLzLgksaA+9pMsayhgHJCNY1uOxDXDybFaujB9XQJB3+7DnTuq6ZvejKShocFgX18KgGFWqHGqW5d5iva2OvzplwMINzW+EgfNsvmhL1VxeJWE9E1jTErsvhef2TffDQZGWD9blUrJzd2W7n2UuritbubZs2RmRm+VxG7r2OrRRLQ9lOs0qXskwkHvG32RNNI6TyvKZ573zoeq98wCl/4bQpO05HWFZ74AwZ7XPsSYz4QZk/Iy8k9A4xDjSU6l3T5ToV5GQU1RHJYoxZ9lgEr1+ataJJix0yFxdKuHAIiBS/3GMXzY13Pbaamd2PkQccw7gUw/jSCJytsB1d8BQBPeCRLDsYAR0J5s+H4qGSu/Cn3YsKPH+MFpEokX1Rz6oOTUzCRIzjZlKToI4DOCrIERKqJ1IE7MaUyCBG7lHJYavNaiuxh/dSIvhNoWhFrOEVyyXCsqWYnq3Aru+xgW0OD00TFSWg66LgRw3IqKauG3UbO+pEf7UPnGHGQiYZrfuiwyRJJDGwWSCptyuDU0wBmaEILs7CvznVDnCVSPVEIjVFRvoQO3JpcjYrAxFwx0eAyM6zpcLe59li2+x+lkskwqwpPQaM6NoxZ8c66i7ckW+0X5yeURviRZjBu1T8BCAxnag9iUPuVWpzzJMLiTtdOhoGPcdOR5BhqIEIzUJVIjLMemvIHNy1xKZ6kLZrRXyoUCzsvfuycnv2HGvXQIeIb9slYI7Zrn7bPm4uEziXSYBXRa4xlWmmynnYGdf4KiTo2hMoWIzWfPGEtnVEcfNFVZBgEyK+wdNYPHsmtoos6yfRXBHfffLpS+HJsS83Ph+z9s0BIsltmE0uwNZcq2vWHk0XFZcZLhNNVPlggyCiGvL0Ab3DEs3GNMYgsgxQJkhoaDanEiVxSuUArRCpMNeEiMbuSV465pfxvNI2DXUCIvErhaOl4qfCkcKnYmXFUdYaXFqO29YlLw1lc3aWq4bLRD5DTa8smZ1rybUDCILUv3isQLi3QFVx7QkPeze82YIRCgKVjoxTtXeeql8EYjCLAD7MMhhrSGMhamTnBfhBnPE9cuy+fWcPtHtzBeMCLrSx8DEZflgdbftXUBvpsIJ0QEb4eYJL4RHM9L/6PiOfd4xvlOZekcm02OKKw4hxpNoMPoJBax1SSEilHKN/cC8k74vD77/qMmbM6AwabR+vW1RMpUO6i6BghjqEeNERpInPhDVszQ+CgO9loOS3jPjNQy6V8yfDHdB/YJQ93D57EW528P3BgwdnxowZY9ux+gcT+ZEjiSML4vPLBFFvIohbIVSmk0QFURgJBGkd4klXFBjXp4IhtcyjZ8+ecHRr2LeP7t0fyuVyNyBSmyJ1/igc1HSKDMx+1WyP/g5TMBheyWR4AlX1dzPVWRElCq4WFdwfVK4eqMaXpqbV1iKM6+kSeVAuf3jnEHn/vry/CxCxe3XwKsR5FVUTctPdCU6ARSFChsUJWZNyNg3PdUDHBwycOGGyXUUqG6MUxo7IbVmDGnHqoKMJ9J98IqV0aE0QILL1Yfn9yu1w69fXJ17MLz8ckzkehjriQUUR0qJOYR5vGh+C4IuP77N2CVkwpP8ICNNU/7Go+4SJg+Yut+1x01zMHILQyCELc6vm/jkQ2QNUEXeLOJ0QxH8KMgqh06fHdCmXLz/4Drd+O3HmxK3y2zHwXgxGbbToGTbLYLEunUFNYAghZtLgVYumDpozbvagzqjD7P4LA90XjZg8lyoyYtrMcYPglN5ihPoMGZzrMwehKctQHbK0J3kuGiLkcTkYToQ99+I0H9j6fSzLWBqiRC50ebHy8gOawXdsfnDmVnnzVqdWBEfbIEtdBHnFdch7y/IH3+Ya21IYBuCiqmOMtaW0aNfSmtala89p6+z0iOnmVFVPu1BqWLWlHWZ16RTLfshcQsRtwiQSEyERROZ+ibjF/fJHXH64JEjwxx8Jfni/77TTanmWdluWNX3yfu/7vV/Pe/T22sU8tUxJjCAIrqMtUFdnIqdOddbOcZOAWTCkORCWdfgXLZrC5Hak5/EhUQeoR6JGAcrr4Lxx9s2vLnTdKSoqutN14dVmaKEhIi9OnfoO//px6dV7h14sXXtRkKZ074SpTmeF4D9Ar9gvf1JDMUxHd2MOmwhgdZs/4OeqploX1TbbaRA5zLRVJ8PieLUrMFOQgwatrF063VCkUsl/RDBucGm2Rq/TB08W/eHkwdO95GPGnDp16itK9bUoIiBSJehGTMVCSUNs4j9FUOc+Va1Bkbt4dPKj1E0hZMiAYeJWOsITNhopLOKHISOurH4RnyGuRV5vW3VHuCK+Yr7tr4ju7wk8VAjRVjdEpxs8rAdGuqQ4vcR6nP5W9DffTg/b8OLUGRTLBhg5Xbt06dqbEyd2q3gpJ+MJJUO2f4oAcjNJlr1+PAvz6Caa4NAZHfNXYaJGoxUtrUTCF2/kOMj1SRRFsW3NXi9r8Zviu10RyV+14grao/v/OYF1380xTq8fIoFwXC4qxOUe90/9QAf+BQte3sUioyZOHDUBv/o4lmqtqVEaQ0m3uLCIpqQPGg4g27EFNpleLJfo4aIhbfPabPVlev1hgogZEwmGbuZsTm61P1xaqlxt8dqsMI47d7fLLVHoBFlUoeq7M6ennjiqnxCfwIaPGlWj6ipKs6/r4MGDXfsyv3ZJ7qNUty7AEVm4dvY0Yf8JE3ELylARbU2NndK21ntrSpa4m/LaDbFuqGaQoHjGrFnt61KTscnRYrkUxsaMIKPH2AiiXhZIOEDErHT4/bTOG/FbvGVav58NJ132MoMl63UlB9Amci3vYq6MPxcP2pJ+48cvLBPxLLtwPC12FvqCG3fv3n1zCEVktnDNmjUCDBWhIxRlopQYx/r19sN/lY/eEilkZfuMWamGhoaXvEn7sGL9bJvXMdNqdXi9S/TRRfEKWR2ItHFKpdo/BxczwqtWzltthEn/oHfevKyQKI6gXkuQj3S6Akw2H+ff82VRNjt4u33PtvX8fPXq1bVrr4LItPHdIiwNLDdFlRkTX7IjmFOmYB5lgNh4dHL7uQbg3CyMviU8pTpDrTHebJXJqDi8Eogoo04vEIxblUo7q1SytbWuREAgztQTIb448vAfvY9exXsc3CzKZfPztMn2pWnevpkGHuPx+rfTCI/J1IQ0GLeNNVAxU2C9IlsETFKTJ3d2nmu4yD1I8SLDpVVmYxgx1VwlNYtlQCsNKAszQqVSZRLiOkp1xb9qywVcbW+L8rmN6/GxWxmRu0hkPC5zlTTdRDOUyR5WIrROljUYDE6nh8oWATpBpH3a2LHT1q1bd+4oiGhkgEajUciyKVep1OaCIiqgXD5ITaoFWpTq12WFPeSnixDgUcikCHH2zSHE3YZpud2A0wTD17RWiWBY1qy2heo8lLHC3ST8IwJZ3tnZblu//uK6VKodRAbLcphJwpNWqkKQrUZzlTIXLdlq1qCojBCTONX3SwqL9OrC60pUGLy6unpNmzYGJPI4HDR5lAirnWVZUqut9ERpq9XrM60vy4jcnPylM7UBR2TDudSTWdNlucSTs2VimqSRiBVBloXNVVolzfAirWSYBFrL4Amnum5A4YAsw3mwWVSYzTjjT8uhHUbkyMgHCYQzOTtEhIyxQChIWYMMvBXObQiFTBwvMuDRl84H6941bFgHEUl9AZHBxS3uttV1PrutAmLh97uXz1uxYn4Q9qSkvxoxfxJnq4feK5bgHI5JQbMvPpfnJ4gc0AkLB+QD3vhE/wJvlB964On/XBMVvmVMMq5GqXTHmli7g+O4eREr0BECD9YAIoiLIPLyzJl3EJGLqS83B8u29m1elWauu3pVtYUgokmDQVPJWCyszdbUZAPYOl/I5epwOBzjdB5PXRBWMfcDpfrIyiXTiyUFRFA+Hxf9m+OoEvQSAEgku1HGIkBJaZPRzjIMx4EG4KQjplBoHojwdD6edQYzGegrkylainevJKLRKGU3j4jMmcMShNMAIhoQqT+sKS8fVCq2cnXO2CSXD0RKxB6Pp63O7Yh9Rbs67Hswip1XucrxyrogEtn3jM7jBAEiuKYtk2MT+MoXwTAxE8f5TBR4eFnGbnfQPiIjMqDz8eO0yKNzMkARXQk3NO2e4rI4Rrgb57h5EW1NmcXiqBhaqSkdVF4+M+6qffrU1byoej7j6fBSrkmTfuFU74EpGaXvl3Mk6oOXDuzne7wikXe+SqSa0v0DU3+Chj0eL70C13lAJHsEqZ9U6vEopCTOe/cIn7MKi6QrF8/rTy2o+MLNKI0ee8Q2f06kYk5jY+M8LKKucVgsh3Vi/cSyKrnSYElqSSfQFqQ9ARsTaZ6LTtzvobnCQ6ADJcUVOknWynqOUh1dANkIT0T6sTEBT/BoC8B3lO7Pe/3pbCr7wQVr8aCBSCQXJmgUSK2s3RZWkj6nGUQyfGp/BOFItbS0KCAglpU8c2sjEUvjpCaCcIOI2UgHLJnTR8xiMdTXg4fb71MbAl4mtmIFSvVLcBIaWiHmZ0G0xsF/RFAKdCERF2XlqsshEFEr5TphSVpOiERNK+EvXSiJsAh/2bdy5J+xT0E+Qd9hEvA5nSCSRcunrQNAZCuI6Jsn+YJRN13dCGcSwuQgiCYQqafpRFKY2cMNFoNh94qEjfT7auoCPmJODKX6phJ+qqICJoow0sysquhOehMZnQfEqNcelUh0EB0ZRQIMfIgjLe/2kPLrNJf1y+tLStVkJGnJFdnaIhyorkSjVP1L4/6VT4G58yOMLWFiidBgj8GwnAq6nIIMjoDBl/AzG7V++8iOQMDF0TfQcaL7ow51CRYZJoVL4QohiJxE1bWwiMtJjWZEog+obPEiwkoxTHl3Iy/pp+8nE/59wHV7gqS8D+37zcu1xLoQheFThnNkzpjRTmemRlV1xpWGZjxDroZElQjxCvEqiXhLvAl78WiCxmvhsaiVChFSQhBBsLIiLAQ7O49gK/GfMy1TrU47Et/uJndu+93vf53//P8gTfjNQ8jlYEBUZTeaYTSvsAyeys+cvgahXbCImUdQ3wB2Df+dayfsm3toeWFzvpA/uGzZlvyaz30AZxu6r6LgjtvHiORVZPfa3Y0xq7f36oiFDYpIxEpjObZ06SbdwBwJGBRoWqZQe6YtWrRoAxoZg12rGiQgIiWjCEWA+eRdwEBfPH0eM8Vt25ZFUc/KVVM2z5vrqWWXLdu/f//EXbuW5xOr1p7aseMH6xzk/5heTHFVZIvav32kd++EQq+Xx7ZCYdvVUQ0+kiUJnIhZiCFKTTntjkePbL7Qnrxw0UI+RmfWVDmTg0WXZBh5MIaiNoiwSkSdOmYp4VjHukC35eZmBj9TGdKwWtTaOWLL2Ktjt3iI9Bb27C70bnGj1gVORCIpnBbrGxdSDPYzDYXtbcE99B+qKMOhN8UaTzSmcSK5nIyTyQzqEhFNygyMxAlh9eLx8X9ZNEawNFPPI/1XApGV/T0o7N5d6F/PIyEuN0njlLtx0ZgYlYzoaS4l0CB3Iprf6kREKnEXgVWv5iEIQehsaJWwLtAlqTVZalOh7/VaZu+fLkwopL1EYDsy/yuzK3yABPbOYn+29MO52pbj1JEeQTgRuxbvs/Npv1wC5ntTKCB0fjS8gf6CKLUt8Hb/WusZtyyTDMJqLN78Z7JhNyQn52ej3Hj4jwqChmZOCXGkLJgNyQreniSUUR3fKojsaPg40q6lPOxOB9XvHUYkkcxivpPUhHDPOBLmIkiEZKOyG9EEHu8dOVRHY6CWZJAx1hmVCDnGXB21w6BiB+eRosJdXcLRmNVa257h41JMhwQbSeaCpOuMzWRUTrMo+eemM/BVoizi+cLkrj4EtYV7IHnd7oT4gVuWRQwsxtwA2nqlOwWqKHIqyxUZlPl9BWrbMJHR5KJ8ccWwRvuqIojsaHgStYdSvOZzZr/GBYkQC3KI2HbhdrSMOTyKcMRtMaT8ub+iUhpPMMaiH5XIC+7qfnSHlXy6KCUuiEokiL1R1A5az3wRcyhkZNzzGXY0JDUd66KEUr4VIpP2VOIn2MW0nwlqZl8oQgCvW/e1oIAx+BgtMRRCBf8AoxsK+z/rI70bfiGs663SXZyEsR8VgRxgR0O/j845SttO4+UjCudLLAgyJuoIrpN4hrszoQTVUEuoI/kSTiI7+m9urz1nR8NbqD0kx4Se6c3WvV/OAyOGJJGx1en9tmzbUQF7vMEIqfRvz0ZgE05mxM2e1qroJ31dHRB3hFA00ffrX7rxX/viWlDKKWmIvR0iLNq2+ksQqocM2mY0APaJegbxSiepa0IT0Rnsrv+lX6px9JDi6On+JfDrJlwr9VcQhwW2HO1mdiIi2r8rVI294KUtcX20lVG4szT9ovmWdYGQD1QHdh6cKNz0FC808bhQhNEUjgwThJB+XRWu9Q4woQIWacTPNKgoQSY1dJoTGpPIY+bqfhtEjmMM1vmTSv/i02ceFs+eFrkcHCYIIoGrB4FERSxQ0bfCjVOgouB0nOpCw7SDe7fTHia4uuzUxFT6soKkzuNIX8WzwqArikgEFARxKoGrax1IaIosryiNTNRLrOEr+Y2Eg6uDu9dXGra+uVb3juvDGlJXBodjKgqCgVTFBqUy6gCCSmkYKzlqeiYdmKuf8zFMzdEHJxz3KfnGl0rlTalGpJRu2CnJYkWMRVAQqObgkEbNTu1QpBo2xN8jn9pt1gWifucVB1R33C9YLVeuXClfd29EqkW1oSCUQZAc6hZLAWz906DiwM6jHTWx8XvPgO+4PdT8Yq8zyHB0dw+gfOXKlTul0hEWvCrFnLeosrggEuoW7KTLJ8qp1k1soBKW6k9kzvKGr+Dn6npIqrn6O9DjXqn0ZuuRKwOuFB95c2yGhywdBSECwCrtyrtMShPYIq6G5n3m6qq/ZQ3WHf7EQRDkC+Px5u71K9dPVr0RykoqCgkgCMq49/isE9gFBJ3GFYPG3Rx0uANXHwiWFXHO8IcvVq5US4CtpaOP+j/69p40ChIOIggS5wN0LdN1nAPj0rhxSeeYqxPfelE3co5Ut6wy6PGu+g5Mss/390lP2EkqmIWs/weN6oqRU0FHlR0N72v+2VB1aknkCyOyFfCIOdd7DxGZSFgGQf4jIMlDmFQzSOBHw7OCb+ELMJFLpFwjAjYJgpR/EzFJGmdjYfQ/IYsgiZpCUdYFOhpHvr4OkN3CrlQul98Bj7tMkMr7qu5ZsMSJwIIENy4JJAnHeRfIN8x4FRtSI8IH0N9Xqr8eTkFXLvWfBeEFWkJJJbmr93QX8y6Wy1VAH0ClXL31S+SkhRXyvwVxXyeHRnNXN7t88ka5Uql855ZVfefZs0tgGULWf4cG/v6Au3rXH36rWnnPnvxefRf+3avMYqzF0f/HQGrhV6zhqweQ88ElvmviTbKsWyMGFaRHQsEhUoN1gd5qKACONrYrEhaBZJgaH/i7jIn/CxH+ToQTs3wiNXuLpoEaAZWmt12RyRISxZnxSQ0FA+wtDk1pA4MSAcsCHL//REAtsWky0sYNZ7AtbfO8Nb8MJ8tGCz6uHcWNKqarBGCFx49eOnnstDGLRvUEIQJUCL8tbQ3TnmNTTWhdcF1cUl9Tf662+gs7CytmTp+5YsVMeIPnQniP5NplM2dO335o15ZTbLTg0+nTG8+vWbMcXlG7ewW8/Xb8+GUrapg5OwARwEFix9TWuswZEzNh41BrFbWocfHkrzX1t7baWpc1vb150GReL2D9kGmLZ05at+4tY79+7NypQ5bmC2sXbF+9eMPeXePHL526qHfNzpkrtq8JogjHz9bON6aNMo7jZ7iTy+6ud9d/d8dRaimXFsbfUTAtrZAKjAINApsiMqewiQOUiYlT5I8wQRPIDGgwcyQuMS4mezM1McYXW8zmC92SvVBMNC5xyxYz3xkzjYkv/D3PtdeWHjGs/S7ZsmzZ+PD9/X7PPXf3fCtGNM1jtqgqKnpE56nLGk0Uw8If8d98/bYRhPCB6dApPXCgF0DCkAjd2dHR2dB3tO3oZbSHeRjUZm3vOq7rYCAw9mjSkt07YqjCaxpX5nisjC+CW4yCm4k4LJn3kGSR1jRtYf3UTqkh8fEUSCNU0NAjkw0NQy3N+NWCf48ff29/73J702tTxw+Phw6AI4G+zZFgV1d70+h9O4K13GqGwsFAEXG+V1VVpDq1sLPVsk/TWLvdLp352EgNOZNuK/dRCiQ4ODjU2IAV/xi1+uyRl4bGipfjr+x/HulwYyDw3MjI5OT+rsHBJ225gIAgNza70skyRZGs6HG91e7hjIQ/m8xoSgLM9oFx1OvU+veMw6S0+gYHB4ebe+CdoqlL76BWfx70UrilpWWq/bWn3ooHg4HA0MihVyr7hocnKx/LDQSzcFaT9GJaoflCLAejAqqF5gi7RqegqUtGhUGIU2KhJcOdBsgIgOwbQK/gPfwjKsODoAOUNNBzeL+uR3vr4Q3FSl2j5O5ABvzOB7JZOuoUW3b8n2LVSUS7BBAsKWo+PvMGwBlYrpMVhsdcZ1PTADnV1NT5cOlzI0+OxB1WX0NDbzG6HvjtSGXjwVGKolSn/tp301QgPoJBsLp3B7IHZGKLyNS7TE5/Kk4xgdICWXKE5shO2/nkcSOXJqBym0eajhzef/zg4Gvjo5vj46XWusZD4058PfD32OihrmMAUjrUhUEaA8GnR5oebWq8TxAQW2utMLGlnrZln5XGLLo00//r0npqHLc09FkNScNxq7WlYfM5Hkfy9I6NHxmiQPs2nx4Id8Y/Ug729AdVwj802t8z2RAndg0CguCDFTGLZMlT7zVJFtBYRwUGsXeb5mhDhV02xvFn1mzpCTAeKiEXoxI0y7K5XcDvSWleLsgWxN7y2bZAKgwiIWmIiTDfOaUW/DM++zYQ3Orfc0kQjuEgPIBlyDyBgFgTkiWI81ctO9nidvnVHZKQjAX/1NeBDI5q/fQ+lQJxAwiUcd5AsC3ZnV9C19X5VDLrsZ0/kTAJ145Amq2OL99MjWNaNUB+wGfJnAaInUGZQE4iryAre/ZsFGavLMVVddr2U7EWl19jcIXJdq3b/Bv6WWrBv1FlTxjyLhppNJnksEDalAiFlVeQFZuzdlkQHjKZx8Cy/Z00UlU01lqEUDiJtcrmFfZOchyvL9hxqyO2da9hiIozgaxEXkFstg2pQhB4n7UgWyWe7FRPjvErDrkQq9pvGqhdfOOUMY6hwrgf8bHRNEMkAT0YzyvIvM1WywmC7KHp0ojbpPE9zc2Kk9/e+H5VRxHZgQGzewAcLPjG/suHW73MMATFBDhYW35BVmxqrSxUlNEMx7u6I2KBidx+f4TMSlpNrJKyg/abZmJ8Y4zjN3GrO5IcpMTIMi0R+QUBQ7yC4KAjlsR+g3/ADMVutnSpHi8vJKaY2adDfGEs+ACj2FKGOGH2cnkFwa3uFkQ6+TYAGfGz3gozW4oXfFkX+5BTfjRSjVgEp6Yx2d1L30gu+OsKmTJEFBgmZwyS2GbIhlARScsR5SN+v2mFLXkWFrxEpmqiG1WQm6ejKN3s9ksCR33d35cfR50SrqISsoEhVtaZM0gNkdHqKhhipTl8xKkGFCUJXmX9EVNXltsGpMx/DNQKtgAKiPAy/owWVtt7ngwFxv75888/GkdT11lEkUTnuIa4P/zQTWw3RPa0QnSBIf01VIYvMJfXh8J8yCjG1tE51eMhTE58AMjTQ6FAaDQcntrHGoaoRTxrJ3LThyAi3ZCNWl7wgiE1KUUJLLXKI5qSuJXSgZpo1t/nWJcj695iXSgU7g8Fwj2VxzIMyXn2nssEgU53Qad7sCGGUi93dRSYK4pCP6I6TdSWnBNSd53GZt7BDIam+vsrK8f8VPrViZvNudVlDJIyRAJDymgnBiGp2N4YFUW1ZcjjKdkRZaM8oZ3LXR3tb+/vD1VubgaN2QuG2PGeJucmITINcdMeEkBiexOKRWvSRtzR+h1cWRKSHDFiR7mCCpxYaWyIx8eSw1cqK8KflJe7xDSQWmwINB61N01E2gFNrr7KDANfLUPcI4M5ll/8tITYQf1D/XBesbLX2IhUw9WJSuRDBohThZHF03S1zjF3axE0EaOI6IkTSZJqtJOiQXwFzod/CH2MSgEKeKcoGX5BllDwkHOZ2EFiWWgSSJzGda+MWj2vIPNcLawhcDyLimGMtfOgtbW1azGIK0iMIksZrHStNBInGKqgQHPffjc9PX376s9zO1WXUufk3KIoUi19w6mZVSSwEpFXkBVXrYoMsSE73tg6j7S1tba2NQd9knBEBRCOtvNuq91/jOUTIDJgrD6b1PTMDiRseKgvKIoElSZJKpJZLq8g8yvwzgsaWbgvtgBk8c4b5TOLa2uL8HtK7xAJvaTlFZHc0pUrqg5CUNcwx8UEyUR5+RWTgWrrhfPOzXwmCOxxGXteQVY2wBAofzyu7pw8uXV+8Q0kILkFs0vf2UrWQq/HLWKhRtpoa+0okSnLKoDcPA26eRNIrpbHBKHK57FlYIweermypcPT/hEbTNus47sOdjJvIIYhLgyydfLkSR3kDrJkAkYXyClJsgiG6MLOcW1tpRby59XVacSBUcCSchn2yh2apjjSSToPNQ7TU+29s7P+tNsn9qIiZ566RDdkpdaJDOFIbAgCuZUEWZvBIG5Jchba6YQhBAahQK2lq6CzV6effeGFn06fBkvmgAMEKD5jW+Lrf/XVYHt7JwJRqbRNFQfBsK56H61acgfBHLAxtNOt0Sh8eYvAAX5gpUA4SRLdEPyX0IkT1F7iHK54HQQ4Xpg+ffoigMgPYS1Zy5QqBrPUhfcBSFUoFJ+d7aVSskiMU4b0dBTQzNhyBpkHEAda1LkkyOLMxMRX95IgsRgOpOYKIb/aAAGSczrI1dsI5Pbt6YurOkgMEjngR8IWjfYooVCoMxTqDYUCs7MslU7iYiSniJIkrShyOBclDKkAQ7yQThCD0sIgcxP37iGQ85/PIA5ecgkyrISZIOco0LXbq9Nnn8H69u7vv0+XAwhIhxGLA8DiWwjpag4HqQyRnMRAfemSBhSVv1+QFQCRajmhhC6LgizYEgDRtXj9+i8zqDhUOEDigMoyAYHa+uvs+79ijrsXLvxUThmOVCRdiYdaIM1k+JFwmKO2C07aPOZKoPAs/qSi3UsmBKgs3CFMFAtAbmWAvD6PO8QLRxhphxkICSAJR87effbCBQAxHNF/Wlp6pBRHqgyHO0nKVFKpwusoSrffvntbCogHHwAODjpkQwdBeTUwf3Wdv379iWs2xOGSIcZYcv8PyK/YkVimI4aOhsOosszF+Y7ZdVeq2W5tl+0CcaL/ATUe8UuszpO0AAAAAElFTkSuQmCC", + "description": "Visualize the entity trip on Tencent Maps. Allows to visualize location history. Use the Trip Animation widget for advanced features.", + "descriptor": { + "type": "timeseries", + "sizeX": 8.5, + "sizeY": 6, + "resources": [], + "templateHtml": "", + "templateCss": ".error {\n color: red;\n}\n.tb-labels {\n color: #222;\n font: 12px/1.5 \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n text-align: center;\n width: 200px;\n white-space: nowrap;\n}", + "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('tencent-map', true, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.map.latestDataUpdate();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true,\n ignoreDataUpdateOnIntervalTick: true,\n hasAdditionalLatestDataKeys: true\n };\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-route-map-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First route\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.5851719234007373,\"funcBody\":\"var lats = [37.7696499,\\n37.7699074,\\n37.7699536,\\n37.7697242,\\n37.7695189,\\n37.7696889,\\n37.7697153,\\n37.7701244,\\n37.7700604,\\n37.7705491,\\n37.7715705,\\n37.771752,\\n37.7707533,\\n37.769866];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lats[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.9015113051937396,\"funcBody\":\"var lons = [-122.4261215,\\n-122.4219157,\\n-122.4199623,\\n-122.4179074,\\n-122.4155876,\\n-122.4155521,\\n-122.4163203,\\n-122.4193876,\\n-122.4210496,\\n-122.422284,\\n-122.4232717,\\n-122.4235138,\\n-122.4247605,\\n-122.4258812];\\n\\nvar i = Math.floor((time/3 % 14000) / 1000);\\n\\nreturn lons[i];\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.7253460349565717,\"funcBody\":\"var value = prevValue;\\nif (time % 500 < 100) {\\n value = value + Math.random() * 40 - 20;\\n if (value < 45) {\\n \\tvalue = 45;\\n } else if (value > 130) {\\n \\tvalue = 130;\\n }\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"tencent-map\",\"tmApiKey\":\"84d6d83e0e51e481e50454ccbe8986b\",\"tmDefaultMapType\":\"roadmap\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"
${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Speed: ${Speed} MPH
See advanced settings for details
\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#1976d3\",\"useColorFunction\":true,\"colorFunction\":\"var speed = dsData[dsIndex]['Speed'];\\nif (typeof speed !== undefined) {\\n var percent = (speed - 45)/85;\\n if (percent < 0.5) {\\n percent *=2*100; \\n return tinycolor.mix('green', 'yellow', percent).toHexString();\\n } else {\\n percent = (percent - 0.5)*2*100;\\n return tinycolor.mix('yellow', 'red', percent).toHexString();\\n }\\n}\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var speed = dsData[dsIndex]['Speed'];\\nvar res = {\\n url: images[0],\\n size: 55\\n};\\nif (typeof speed !== undefined) {\\n var percent = (speed - 45)/85;\\n var index = Math.min(2, Math.floor(3 * percent));\\n res.url = images[index];\\n}\\nreturn res;\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"perimeter\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.2,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":3,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"strokeWeight\":4,\"strokeOpacity\":0.65},\"title\":\"Route Map - Tencent\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{}}" + }, + "externalId": null, + "tags": [ + "mapping", + "gps", + "navigation", + "geolocation", + "satellite", + "directions" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/rpc_button.json b/application/src/main/data/json/system/widget_types/rpc_button.json new file mode 100644 index 0000000000..a5edbcc5a3 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/rpc_button.json @@ -0,0 +1,34 @@ +{ + "fqn": "control_widgets.rpcbutton", + "name": "RPC Button", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAN8SURBVHja7dvbSxRRHAfw/X9+bhcvrJZSmllhZFSCWBDaY4FUhBRZCQXR7akLyoJYlqxU0g0tukF0oYQM3bxsYVRgbuuyu7k7szNn9vSb2Wx9qB56ac72/T7MHGYZmA/nMjvDbzzSTkbpOASPw7CUjkPx2AwhTNNQNKYphE1hiCXMtK5pWkrB8GXraZMl0sMOQ9dEdqApmIzQdIMlDDH1lLIMh5LSTYZYIp1MS6XDAGF5uEPiltoQK85dwhAtKhVPVLMhRj5ADAcyqzpkNgtJqQ9JAQIIIIAAAggggAACCCB/8wog8btfAjd+f9Y3t0GmW5bS8rO/fvavaXB2jWVlZbVHwjLC+7L1x2P8rN1ZQcWtX90EyWws7r7bSuf+BKnz+f1t3norTPV+/0HvNiFP0+5b54sahYsgn+mQrdksZfL6hYcZGe2betoxyG/LtP7OiXlIJW/a6F2Y9nPjKI3MLtrBjbM05CJIsqT6tdOI1BbWFuyRQVpbupw7yGygqqLCBZCTNJqFdNGTAQpwY2bwo5vmyL1iqr+uS3l4yaTsoaEg7bT0VXWyn/xyquQHpDz4JuCrFWHaG48OrSuJXqLHLly1Yl01VDkuq6r7+i5Qd5ABsqlctlMiN0eI0/BZhu09rXggu10J4TVoYMlWWeKr51x2IM3lcp83k4NUfAqVbrQYsn3w/lvuvTt0lQ9Hn027CNJLt3m7YaXctIaXYCHnIWfog5Src3Okg/pldo7Ys8O7S9qHXrgIEllWee35uYJ9MkAHXgVq3s9Dxguan56iHCRZUa3/hPDAa3/U7dtiuGloTTZ7aXFrnO9xPio8Zs5D5JViaqpbsGpdpK4cxDxRRItbpl02R1Jfsje2zIy+8LCI/ekkc0a4b7Lj3y8ggAACCCCAAAIIIIAA8v9C8qaoRosqXngmsmVOXHgWVxsSyxaemXosJFR2mBMxpxRQpOPB0YihKsOIjAbjdnEml8vOhUaGX97s7VEwvTdfDo+E5kyn7pe7JDQWVDZjIbtDMk5JuZGcSyQScQXDlz2XNJyS8vwp8s+bzy6kzJsPYfIigAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggg/yTfATinNXQTM7TQAAAAAElFTkSuQmCC", + "description": "Allows to send RPC command when the user presses the button.", + "descriptor": { + "type": "rpc", + "sizeX": 4, + "sizeY": 2, + "resources": [], + "templateHtml": "
\n
\n {{title}}\n
\n
\n
\n \n
\n
\n
\n {{ error }}\n
\n
", + "templateCss": ".tb-rpc-button {\n width: 100%;\n height: 100%;\n}\n\n.tb-rpc-button .title-container {\n font-weight: 500;\n white-space: nowrap;\n margin: 10px 0;\n}\n\n.tb-rpc-button .button-container div{\n min-width: 80%\n}\n\n.tb-rpc-button .button-container .mat-mdc-button{\n width: 100%;\n margin: 0;\n}\n\n.tb-rpc-button .error-container {\n position: absolute;\n top: 2%;\n right: 0;\n left: 0;\n z-index: 4;\n height: 14px;\n}\n\n.tb-rpc-button .error-container .button-error {\n color: #ff3315;\n white-space: nowrap;\n}", + "controllerScript": "var requestPersistent = false;\nvar persistentPollingInterval = 5000;\n\nself.onInit = function() {\n if (self.ctx.settings.requestPersistent) {\n requestPersistent = self.ctx.settings.requestPersistent;\n }\n if (self.ctx.settings.persistentPollingInterval) {\n persistentPollingInterval = self.ctx.settings.persistentPollingInterval;\n }\n \n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges();\n });\n};\n\nfunction init() {\n let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\n\n self.ctx.$scope.buttonLable = self.ctx.settings.buttonText;\n self.ctx.$scope.showTitle = self.ctx.settings.title &&\n self.ctx.settings.title.length ? true : false;\n self.ctx.$scope.title = self.ctx.settings.title;\n self.ctx.$scope.styleButton = self.ctx.settings.styleButton;\n\n if (self.ctx.settings.styleButton.isPrimary ===\n false) {\n self.ctx.$scope.customStyle = {\n 'background-color': self.ctx.$scope.styleButton.bgColor,\n 'color': self.ctx.$scope.styleButton.textColor\n };\n }\n\n if (!rpcEnabled) {\n self.ctx.$scope.error =\n 'Target device is not set!';\n }\n\n self.ctx.$scope.sendCommand = function() {\n var rpcMethod = self.ctx.settings.methodName;\n var rpcParams = self.ctx.settings.methodParams;\n if (rpcParams.length) {\n try {\n rpcParams = JSON.parse(rpcParams);\n } catch (e) {}\n }\n var timeout = self.ctx.settings.requestTimeout;\n var oneWayElseTwoWay = self.ctx.settings.oneWayElseTwoWay ?\n true : false;\n\n var commandPromise;\n if (oneWayElseTwoWay) {\n commandPromise = self.ctx.controlApi.sendOneWayCommand(\n rpcMethod, rpcParams, timeout, requestPersistent, persistentPollingInterval);\n } else {\n commandPromise = self.ctx.controlApi.sendTwoWayCommand(\n rpcMethod, rpcParams, timeout, requestPersistent, persistentPollingInterval);\n }\n commandPromise.subscribe(\n function success() {\n self.ctx.$scope.error = \"\";\n self.ctx.detectChanges();\n },\n function fail(rejection) {\n if (self.ctx.settings.showError) {\n self.ctx.$scope.error =\n rejection.status + \": \" +\n rejection.statusText;\n self.ctx.detectChanges();\n }\n }\n );\n };\n}\n\nself.onDestroy = function() {\n self.ctx.controlApi.completedCommand();\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-send-rpc-widget-settings", + "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":5000,\"oneWayElseTwoWay\":true,\"buttonText\":\"Send RPC\",\"styleButton\":{\"isRaised\":true,\"isPrimary\":false},\"methodName\":\"rpcCommand\",\"methodParams\":\"{}\"},\"title\":\"RPC Button\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null, + "tags": [ + "command", + "downlink", + "device configuration", + "device control", + "invocation", + "remote method", + "remote function", + "interface", + "subroutine call", + "inter-process communication", + "server request" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/rpc_debug_terminal.json b/application/src/main/data/json/system/widget_types/rpc_debug_terminal.json new file mode 100644 index 0000000000..eed625283a --- /dev/null +++ b/application/src/main/data/json/system/widget_types/rpc_debug_terminal.json @@ -0,0 +1,34 @@ +{ + "fqn": "control_widgets.rpc_debug_terminal", + "name": "RPC debug terminal", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAWcklEQVR42u2dB5QWRZeGi6QgklFUDJhFMCJGRFHMomMAMStiBvUXRTFgRjGLERMoiIBZ1GVXdNcF4yIe5agYdvVnfkAwIAoiIs4+/72na3u+NN8wAzsD73vmzOnur7rCrbfuvV3VfSuUlZXNnDnzmGOOadKkSRCEKgAKlZSUzJgxA1IFWNWyZUsJRagutGjRAlIFdJVkIVQvevbsGWQBhWpH06ZNJQRBEARBEARBEARBEARBEARBEARBEARBEARBEARBEARBEARBEITlRK9evc5KwAdAnTt3rlevXvz1oIMOir+efvrp3bp1a9WqVUYOderUOfDAAy+77LJLL7308MMPX2ONNYop99577+ULyaOPPrpwsvfff59kG264Ya0W8uDBg6+55pqq5PDRRx8hh3XXXbfWtPnzzz8vK4/PPvtsjz328F9ffvnljF9///33q6++Ot6+/vrrv/fee+kEX3zxxeabb15biHXqqafecsstK5S4a6655pIlS/7444+11lprtSPWEUcc0alTp/333//hhx/mdNasWa54nFj9+vXj19122+2MM8747rvvuHLiiSe6rnrrrbc4feyxxzp27Aifbr/9dk6nTp1aW4j10ksvkf8uu+yyQoVM/piC1VFjbbbZZtGu+RUXhBMLgxjTQymuvPrqqxzvs88+HH/44Ydps/jJJ59wccstt8wua+ONN7722mvvuOOOQw89NJtYlAIv7777buwp+aSJtd122w0YMIBbUDDRUjMYsL8bbbSRn+63336ctm/f3k/XXnvtiy66iFsw4htssAE/MWzSlWncuDEXUc/kT7L4cXn9+vVxD+65557bbrttr732iunJ7dxzz91mm22o4QEHHECIA27v0aMHgkKFDxs2jIsk23PPPWng0KFD995773jv3wx+3Ldv34svvrh58+a06L777jv77LMbNGiQtgBcf+CBB8hz6623XnWIBd59990CxOrSpQtX3n77bY6HDBnCMfJNZ4gfRl9uuummGQWhz3788UfS//nnn/z/4Ycf0sR6/PHHOcVkLF26lAMkmybW3LlzMSVuaseOHes/jRkzhlPI7aeuLI8//niOGzVq5PxetmzZX3/9VVpayvFdd92Vrs86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", + "description": "Allows to send any RPC command using its name and parameters to the device. Useful for debugging.", + "descriptor": { + "type": "rpc", + "sizeX": 9.5, + "sizeY": 5.5, + "resources": [], + "templateHtml": "
", + "templateCss": ".cmd .cursor.blink {\n -webkit-animation-name: terminal-underline;\n -moz-animation-name: terminal-underline;\n -ms-animation-name: terminal-underline;\n animation-name: terminal-underline;\n}\n.terminal .inverted, .cmd .inverted {\n border-bottom-color: #aaa;\n}\n\n", + "controllerScript": "var requestTimeout = 500;\nvar requestPersistent = false;\nvar persistentPollingInterval = 5000;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n if (self.ctx.settings.requestPersistent) {\n requestPersistent = self.ctx.settings.requestPersistent;\n }\n if (self.ctx.settings.persistentPollingInterval) {\n persistentPollingInterval = self.ctx.settings.persistentPollingInterval;\n }\n var greetings = 'Welcome to ThingsBoard RPC debug terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = command.trim();\n var requestUUID = uuidv4();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var spaceIndex = localCommand.indexOf(' ');\n if (spaceIndex === -1 && !localCommand.length) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n } else {\n var params;\n if (spaceIndex === -1) {\n spaceIndex = localCommand.length;\n }\n var name = localCommand.substr(0, spaceIndex);\n var args = localCommand.substr(spaceIndex + 1);\n if (args.length) {\n try {\n params = JSON.parse(args);\n } catch (e) {\n params = args;\n }\n }\n performRpc(this, name, params, requestUUID);\n }\n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt,\n enabled: rpcEnabled\n });\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' [params body]]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1:]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2:]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": 2, \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, requestPersistent, persistentPollingInterval, requestUUID).subscribe(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\n\nfunction uuidv4() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\n \nself.onDestroy = function() {\n self.ctx.controlApi.completedCommand();\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-rpc-terminal-widget-settings", + "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\":{}}" + }, + "externalId": null, + "tags": [ + "command", + "downlink", + "device configuration", + "device control", + "invocation", + "remote method", + "remote function", + "interface", + "subroutine call", + "inter-process communication", + "server request" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/rpc_remote_shell.json b/application/src/main/data/json/system/widget_types/rpc_remote_shell.json new file mode 100644 index 0000000000..de9925a054 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/rpc_remote_shell.json @@ -0,0 +1,34 @@ +{ + "fqn": "control_widgets.rpc_remote_shell", + "name": "RPC remote shell", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAU6klEQVR42u2dCbSW0xrHtyZDOpVkyFCKIplCZpLMIVJIhsgYmSUakLniInOGyFRXaCn3ilws89Q1RrpRSq4k15Tx3N96Ht9e73m/4Xzn9EVH//8666z97u99997v3v/9PM8e3meH8vLyWbNmdevWrUGDBkEQFgNQqGvXrtOmTYNUAVatssoqqhShVGjcuDGkCsgq1YVQWnTv3j1IAwolR1lZmSpBEARBEARBEARBEARBEARBEARBEARBEARBEARBEARBEARBEAShmujRo8fxGfAB0NZbb127du3465577hl/7d2796677tqkSZNUCsstt9wee+zRv3//c845p0uXLvXq1ftLVtQ222zDJ50PP/xwlZ7abbfdeOruu+8mXKtWLcKzZ89eJoj1/vvvl1fEe++9t9122/mvEyZMSP26aNGiQYMGxcfXXHPNl156KXnDBx980KpVq6Xh1aD7FVdcsdVWW4lYfxqx9t9//y233JJauPXWW7mcM2eOCx4n1imnnMKv1Oyxxx47b948Yg4//HCXVc888wyXt99+e7t27eDT8OHDuXzttdeWhlcbOnQohTnmmGNErD+NWC1btox6zWPQiZFYKMR4P5QiZuLEiYR32WUXwq+//npSLb711ltEbrDBBqmMDjnkENTluuuue+mllx533HEe2axZs/POO++mm24666yzVl11VY9s27Ytd3bo0GHvvfe+/PLLhw0btummmxJ/0EEHXXfddRdddFGbNm2SKcPpCy+8kET69evXqFEjj6QzPPXUU5Rk3LhxJ554Yry5c+fOJHjttdfisIDS5qyTpk2bnn322eR1/vnnR+kbiUV2Q4YMufLKK7fffvvkU+uss86AAQMoxhlnnBEdIIhYLWPMiy++WIBYO+64IzHPP/884csuu4wwJEgmiB2GQbbeeuulMpo8eTI3z5w5k/8kS8xOO+20cOFCLr/77jv+z507t3nz5sT37NnTpSb/f/75Z/5///33Dz74IIGffvrJ74/pQ5pffvmFyB9++MGbbf311yceLxcew81Tp071m2l1T8TTueOOO7IrZOONN/7qq6/IlxTQ+9y57777RmKRPsl6qX799VdeNhLom2++ie+CH4S11lpLxPqd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", + "description": "Allows to emulate remote shell. Requires custom implementation on the target device to work correctly.", + "descriptor": { + "type": "rpc", + "sizeX": 9.5, + "sizeY": 5.5, + "resources": [], + "templateHtml": "
", + "templateCss": ".cmd .cursor.blink {\n -webkit-animation-name: terminal-underline;\n -moz-animation-name: terminal-underline;\n -ms-animation-name: terminal-underline;\n animation-name: terminal-underline;\n}\n.terminal .inverted, .cmd .inverted {\n border-bottom-color: #aaa;\n}\n", + "controllerScript": "var requestTimeout = 500;\nvar commandStatusPollingInterval = 200;\n\nvar welcome = 'Welcome to ThingsBoard RPC remote shell.\\n';\n\nvar terminal, rpcEnabled, simulated, deviceName, cwd;\nvar commandExecuting = false;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n rpcEnabled = subscription.rpcEnabled;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n } else {\n deviceName = 'Simulated';\n simulated = true;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n \n terminal = $('#device-terminal', self.ctx.$container).terminal(\n function (command) {\n if (command && command.trim().length) {\n try {\n if (simulated) {\n this.echo(command);\n } else {\n sendCommand(this, command);\n }\n } catch(e) {\n this.error(e + '');\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: false,\n enabled: rpcEnabled,\n prompt: rpcEnabled ? currentPrompt : '',\n name: 'shell',\n pauseEvents: false,\n keydown: function (e, term) {\n if ((e.which == 67 || e.which == 68) && e.ctrlKey) { // CTRL+C || CTRL+D\n if (commandExecuting) {\n terminateCommand(term);\n return false;\n }\n }\n },\n onInit: initTerm\n }\n );\n \n};\n\nfunction initTerm(terminal) {\n terminal.echo(welcome);\n if (!rpcEnabled) {\n terminal.error('Target device is not set!\\n');\n } else {\n terminal.echo('Current target device for RPC terminal: [[b;#fff;]' + deviceName + ']\\n');\n if (!simulated) {\n terminal.pause();\n getTermInfo(terminal,\n function (remoteTermInfo) {\n if (remoteTermInfo) {\n terminal.echo('Remote platform info:');\n terminal.echo('OS: [[b;#fff;]' + remoteTermInfo.platform + ']');\n if (remoteTermInfo.release) {\n terminal.echo('OS release: [[b;#fff;]' + remoteTermInfo.release + ']');\n }\n terminal.echo('\\r');\n } else {\n terminal.echo('[[;#f00;]Unable to get remote platform info.\\nDevice is not responding.]\\n');\n }\n terminal.resume();\n });\n }\n }\n}\n\nfunction currentPrompt(callback) {\n if (cwd) {\n callback('[[b;#2196f3;]' + deviceName +']: [[b;#8bc34a;]' + cwd +']> ');\n } else {\n callback('[[b;#8bc34a;]' + deviceName +']> ');\n }\n}\n\nfunction getTermInfo(terminal, callback) {\n self.ctx.controlApi.sendTwoWayCommand('getTermInfo', null, requestTimeout).subscribe(\n function (termInfo) {\n cwd = termInfo.cwd;\n if (callback) {\n callback(termInfo);\n } \n },\n function () {\n if (callback) {\n callback(null);\n }\n }\n );\n}\n\nfunction sendCommand(terminal, command) {\n terminal.pause();\n var sendCommandRequest = {\n command: command,\n cwd: cwd\n };\n self.ctx.controlApi.sendTwoWayCommand('sendCommand', sendCommandRequest, requestTimeout).subscribe(\n function (responseBody) {\n if (responseBody && responseBody.ok) {\n commandExecuting = true;\n setTimeout( pollCommandStatus.bind(null,terminal), commandStatusPollingInterval );\n } else {\n var error = responseBody ? responseBody.error : 'Unhandled error.';\n terminal.error(error);\n terminal.resume();\n }\n },\n function () {\n onRpcError(terminal);\n }\n );\n}\n\nfunction terminateCommand(terminal) {\n self.ctx.controlApi.sendTwoWayCommand('terminateCommand', null, requestTimeout).subscribe(\n function (responseBody) {\n if (!responseBody.ok) {\n commandExecuting = false;\n terminal.error(responseBody.error);\n terminal.resume();\n } \n },\n function () {\n onRpcError(terminal);\n }\n ); \n}\n\nfunction onRpcError(terminal) {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.resume();\n}\n\nfunction pollCommandStatus(terminal) {\n self.ctx.controlApi.sendTwoWayCommand('getCommandStatus', null, requestTimeout).subscribe(\n function (commandStatusResponse) {\n for (var i=0;i", + "templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.gpio-panel section[fxflex] {\n min-width: 0px;\n}\n\n\n.switch-panel {\n margin: 0;\n height: 32px;\n width: 66px;\n min-width: 66px;\n}\n\n.switch-panel mat-slide-toggle {\n margin: 0;\n width: 36px;\n min-width: 36px;\n}\n\n.switch-panel.col-0 mat-slide-toggle {\n margin-left: 8px;\n margin-right: 4px;\n}\n\n.switch-panel.col-1 mat-slide-toggle {\n margin-left: 4px;\n margin-right: 8px;\n}\n\n.gpio-row {\n height: 32px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.switch-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.switch-panel.col-1 .pin {\n margin-right: auto;\n \n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}", + "controllerScript": "\nself.onInit = function() {\n};", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-gateway-service-rpc-settings", + "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"isConnector\":false},\"title\":\"Service RPC\"}" + }, + "tags": [ + "command", + "downlink", + "device configuration", + "device control", + "invocation", + "remote method", + "remote function", + "interface", + "subroutine call", + "inter-process communication", + "server request" + ] +} diff --git a/application/src/main/data/json/system/widget_types/signal_strength.json b/application/src/main/data/json/system/widget_types/signal_strength.json new file mode 100644 index 0000000000..62fc0088b6 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/signal_strength.json @@ -0,0 +1,39 @@ +{ + "fqn": "signal_strength", + "name": "Signal strength", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Presents the current signal strength as WiFi or Cellular Bar.", + "descriptor": { + "type": "latest", + "sizeX": 2.5, + "sizeY": 2.5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.signalStrengthWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.signalStrengthWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '200px',\n previewHeight: '200px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'rssi', label: 'rssi', type: 'timeseries' }];\n }\n };\n};\n\nself.actionSources = function() {\n return {\n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-signal-strength-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-signal-strength-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"rssi\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"if (!prevValue) {\\n prevValue = Math.random() * -96;\\n}\\nvar value = prevValue + (Math.random() * 60 - 30);\\nif (value > 0) {\\n\\tvalue = 0;\\n} else if (value < -96) {\\n value = -96;\\n}\\nlet rand = Math.random();\\nreturn rand < 0.2 ? (rand < 0.1 ? -101 : '') : value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"wifi\",\"showDate\":false,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"activeBarsColor\":{\"color\":\"rgba(92, 223, 144, 1)\",\"type\":\"range\",\"rangeList\":[{\"to\":-85,\"color\":\"rgba(227, 71, 71, 1)\"},{\"from\":-85,\"to\":-70,\"color\":\"rgba(255, 122, 0, 1)\"},{\"from\":-70,\"to\":-55,\"color\":\"rgba(246, 206, 67, 1)\"},{\"from\":-55,\"color\":\"rgba(92, 223, 144, 1)\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"inactiveBarsColor\":\"rgba(224, 224, 224, 1)\",\"showTooltip\":true,\"showTooltipValue\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0,0,0,0.76)\",\"showTooltipDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0,0,0,0.76)\",\"tooltipBackgroundColor\":\"rgba(255,255,255,0.72)\",\"tooltipBackgroundBlur\":3,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Signal strength\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"dBm\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"signal_cellular_alt\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "wifi", + "signal", + "cellular", + "rssi", + "reception", + "connectivity", + "network", + "connection", + "signal-to-noise", + "antenna", + "dbm", + "wireless", + "link", + "quality" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_air_quality_index_chart_card.json b/application/src/main/data/json/system/widget_types/simple_air_quality_index_chart_card.json new file mode 100644 index 0000000000..73e0635a3b --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_air_quality_index_chart_card.json @@ -0,0 +1,35 @@ +{ + "fqn": "simple_air_quality_chart_card", + "name": "Simple air quality index chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical air quality index values as a simplified chart. Optionally may display the corresponding latest air quality index value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'air', label: 'Air Quality Index', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'air', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Air Quality Index\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 320) {\\n\\tvalue = 320;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 320) {\\n\\tvalue = 320;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#80C32C\"},{\"from\":50,\"to\":100,\"color\":\"#FFA600\"},{\"from\":100,\"to\":150,\"color\":\"#F36900\"},{\"from\":150,\"to\":200,\"color\":\"#D81838\"},{\"from\":200,\"to\":300,\"color\":\"#8D268C\"},{\"from\":300,\"to\":null,\"color\":\"#6F113A\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Air Quality Index\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:weather-windy\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":1,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"AQI\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "air", + "aqi", + "pollution", + "emission", + "smog" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_air_quality_index_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_air_quality_index_chart_card_with_background.json new file mode 100644 index 0000000000..efc1b71eb2 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_air_quality_index_chart_card_with_background.json @@ -0,0 +1,35 @@ +{ + "fqn": "simple_air_quality_chart_card_with_background", + "name": "Simple air quality index chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical air quality index values as a simplified chart with background. Optionally may display the corresponding latest air quality index value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'air', label: 'Air Quality Index', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'air', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Air Quality Index\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 320) {\\n\\tvalue = 320;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 320) {\\n\\tvalue = 320;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#7CC322\"},{\"from\":50,\"to\":100,\"color\":\"#F89E0D\"},{\"from\":100,\"to\":150,\"color\":\"#F77410\"},{\"from\":150,\"to\":200,\"color\":\"#DE2343\"},{\"from\":200,\"to\":300,\"color\":\"#7B287A\"},{\"from\":300,\"to\":null,\"color\":\"#791541\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Air Quality Index\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:weather-windy\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":1,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"AQI\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "air", + "aqi", + "pollution", + "emission", + "smog" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_carbon_monoxide__co__chart_card.json b/application/src/main/data/json/system/widget_types/simple_carbon_monoxide__co__chart_card.json new file mode 100644 index 0000000000..e1cc032583 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_carbon_monoxide__co__chart_card.json @@ -0,0 +1,40 @@ +{ + "fqn": "simple_carbon_monoxide_co_chart_card", + "name": "Simple carbon monoxide (CO) chart card", + "deprecated": false, + "image": "tb-image:Q08tc2ltcGxlLXZhbHVlLWFuZC1jaGFydC1jYXJkLnN2Zw==:Q08tc2ltcGxlLXZhbHVlLWFuZC1jaGFydC1jYXJkLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjEwOCIgeT0iMjYiIGZpbGw9IiNmZmYiIHJ4PSI0Ii8+PHJlY3Qgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxMDciIHg9Ii41IiB5PSIyNi41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMTIiIHJ4PSIzLjUiLz48ZyBmaWx0ZXI9InVybCgjYSkiPjxyZWN0IHdpZHRoPSIxODIiIGhlaWdodD0iOTAiIHg9IjkiIHk9IjM1IiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii43NiIgcng9IjMuMDA4Ii8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjg3IiBkPSJNMTYuNjY2IDQzLjA4M0ExLjE3IDEuMTcgMCAwIDAgMTUuNSA0NC4yNXYzLjVhMS4xNjcgMS4xNjcgMCAwIDAgMS4xNjYgMS4xNjdoMS43NVY0Ny43NWgtMS43NXYtMy41aDEuNzV2LTEuMTY3aC0xLjc1Wm0zLjUgMEExLjE3IDEuMTcgMCAwIDAgMTkgNDQuMjV2My41YTEuMTY3IDEuMTY3IDAgMCAwIDEuMTY2IDEuMTY3aDEuMTY3Yy42NDggMCAxLjE2Ny0uNTIgMS4xNjctMS4xNjd2LTMuNWExLjE2NyAxLjE2NyAwIDAgMC0xLjE2Ny0xLjE2N2gtMS4xNjdabTAgMS4xNjdoMS4xNjd2My41aC0xLjE2N3YtMy41Wk0zOC45MDYgNDcuNzZoMS43MDlhMy45NDMgMy45NDMgMCAwIDEtLjU0NyAxLjc0M2MtLjMxLjUwNi0uNzQ1LjkwNS0xLjMwNiAxLjE5Ni0uNTYuMjkyLTEuMjQyLjQzOC0yLjA0NC40MzgtLjYxNSAwLTEuMTY5LS4xMS0xLjY2LS4zMjhhMy42NDEgMy42NDEgMCAwIDEtMS4yNjYtLjk0NGMtLjM1LS40MS0uNjItLjkwNC0uODA2LTEuNDgzYTYuNDQ1IDYuNDQ1IDAgMCAxLS4yNzQtMS45NDJ2LS44MjdjMC0uNzE1LjA5NC0xLjM2Mi4yOC0xLjk0MWE0LjI5IDQuMjkgMCAwIDEgLjgyMS0xLjQ4MyAzLjU0NiAzLjU0NiAwIDAgMSAxLjI3OC0uOTUgNC4xOCA0LjE4IDAgMCAxIDEuNjg5LS4zMjljLjc5MyAwIDEuNDYzLjE0NiAyLjAxLjQzOC41NDYuMjkxLjk3LjY5NSAxLjI3MSAxLjIxLjMwNS41MTUuNDkyIDEuMTA1LjU2IDEuNzdoLTEuNzA4YTMuMTYgMy4xNiAwIDAgMC0uMzAxLTEuMSAxLjU1IDEuNTUgMCAwIDAtLjY3LS42OThjLS4yOTYtLjE2NC0uNjg0LS4yNDYtMS4xNjItLjI0Ni0uMzkyIDAtLjczNC4wNzMtMS4wMjYuMjE5LS4yOTEuMTQ2LS41MzUuMzYtLjczMS42NDJhMy4xIDMuMSAwIDAgMC0uNDQ0IDEuMDQ2IDYuMjMgNi4yMyAwIDAgMC0uMTQ0IDEuNDA5di44NGMwIC41MDIuMDQzLjk1OC4xMyAxLjM2OC4wOTEuNDA1LjIyOC43NTQuNDEgMS4wNDUuMTg3LjI5Mi40MjQuNTE4LjcxMS42NzcuMjg3LjE2LjYzMS4yNCAxLjAzMi4yNC40ODggMCAuODgyLS4wNzggMS4xODMtLjIzM2ExLjU1IDEuNTUgMCAwIDAgLjY5LS42NzdjLjE2LS4zLjI2NS0uNjY3LjMxNS0xLjFabTcuNTIzIDEuNzU3di0zLjUyOGMwLS4yNjQtLjA0OC0uNDkyLS4xNDMtLjY4M2ExLjAwNSAxLjAwNSAwIDAgMC0uNDM4LS40NDVjLS4xOTEtLjEwNC0uNDMzLS4xNTctLjcyNC0uMTU3LS4yNyAwLS41MDIuMDQ2LS42OTguMTM3YTEuMTI3IDEuMTI3IDAgMCAwLS40NTguMzY5Ljg5Ljg5IDAgMCAwLS4xNjQuNTI2aC0xLjY0YzAtLjI5MS4wNy0uNTc0LjIxMi0uODQ3LjE0LS4yNzQuMzQ2LS41MTguNjE1LS43MzIuMjY5LS4yMTQuNTktLjM4My45NjQtLjUwNmE0LjAwOCA0LjAwOCAwIDAgMSAxLjI1Ny0uMTg0Yy41NTYgMCAxLjA0OS4wOTMgMS40NzcuMjguNDMzLjE4Ny43NzIuNDcgMS4wMTkuODQ4LjI1LjM3My4zNzYuODQzLjM3NiAxLjQwOHYzLjI4OGMwIC4zMzcuMDIyLjY0LjA2OC45MS4wNS4yNjQuMTIuNDk0LjIxMi42OVY1MWgtMS42ODlhMi45MTcgMi45MTcgMCAwIDEtLjE4NC0uNjc3IDUuNTI4IDUuNTI4IDAgMCAxLS4wNjItLjgwNlptLjI0LTMuMDE1LjAxMyAxLjAxOEg0NS41Yy0uMzA1IDAtLjU3NC4wMy0uODA2LjA5YTEuNjU3IDEuNjU3IDAgMCAwLS41ODEuMjQ1Yy0uMTU1LjExLS4yNzEuMjQyLS4zNDkuMzk3YTEuMTYgMS4xNiAwIDAgMC0uMTE2LjUyNmMwIC4xOTYuMDQ2LjM3Ni4xMzcuNTQuMDkuMTYuMjIzLjI4NS4zOTYuMzc2LjE3OC4wOTEuMzkyLjEzNy42NDMuMTM3LjMzNyAwIC42My0uMDY4Ljg4Mi0uMjA1LjI1NS0uMTQxLjQ1NS0uMzEyLjYwMS0uNTEzLjE0Ni0uMjA1LjIyNC0uMzk4LjIzMy0uNThsLjUzMy43M2EyLjUwMiAyLjUwMiAwIDAgMS0uMjguNjAyYy0uMTMzLjIxNC0uMzA2LjQyLS41Mi42MTVhMi41ODQgMi41ODQgMCAwIDEtMS43Ny42NTZjLS40ODQgMC0uOTE0LS4wOTUtMS4yOTItLjI4NmEyLjI5NyAyLjI5NyAwIDAgMS0uODktLjc4NyAyLjAzOSAyLjAzOSAwIDAgMS0uMzItMS4xMjhjMC0uMzkxLjA3Mi0uNzM4LjIxOC0xLjAzOC4xNS0uMzA2LjM3LS41NjEuNjU2LS43NjYuMjkyLS4yMDUuNjQ4LS4zNiAxLjA2Ny0uNDY1LjQyLS4xMS44OTgtLjE2NCAxLjQzNS0uMTY0aDEuMjkzWm01LjA0MS0xLjQ5VjUxaC0xLjY0N3YtNy4zOTdoMS41NzJsLjA3NSAxLjQwOVptMi4yNjMtMS40NTYtLjAxNCAxLjUzYTQuMjg5IDQuMjg5IDAgMCAwLS42Ny0uMDU1Yy0uMjgyLjAwMS0uNTMuMDQyLS43NDUuMTI0YTEuNDAzIDEuNDAzIDAgMCAwLS41NC4zNDJjLS4xNC4xNDYtLjI1LjMyNC0uMzI4LjUzMy0uMDc3LjIxLS4xMjMuNDQ1LS4xMzYuNzA0bC0uMzc2LjAyOGMwLS40NjUuMDQ1LS44OTYuMTM2LTEuMjkyYTMuNTMgMy41MyAwIDAgMSAuNDEtMS4wNDZjLjE4Ny0uMzAxLjQyLS41MzYuNjk4LS43MDQuMjgyLS4xNjkuNjA4LS4yNTMuOTc3LS4yNTMuMSAwIC4yMDguMDA5LjMyMi4wMjcuMTE4LjAxOC4yMDcuMDM5LjI2Ni4wNjJabTEuMjktMy4wNTZoMS42NDd2OC45Mkw1Ni43NTIgNTFoLTEuNDlWNDAuNVptNi40NzMgNi43MzN2LjE0NGMwIC41NDctLjA2MiAxLjA1LS4xODUgMS41MWEzLjQ4IDMuNDggMCAwIDEtLjU0NyAxLjE5Yy0uMjQxLjMzNy0uNTQyLjYtLjkwMi43ODYtLjM1NS4xODMtLjc2OC4yNzQtMS4yMzcuMjc0LS40NiAwLS44NjItLjA4Ny0xLjIwMy0uMjZhMi4zOTIgMi4zOTIgMCAwIDEtLjg2Mi0uNzM4IDQuMDk1IDQuMDk1IDAgMCAxLS41NTMtMS4xNDIgNy41MzcgNy41MzcgMCAwIDEtLjMwMS0xLjQ2M3YtLjQ1OGMuMDU5LS41MzguMTYtMS4wMjUuMy0xLjQ2My4xNDItLjQ0Mi4zMjYtLjgyMi41NTQtMS4xNDEuMjMzLS4zMjQuNTE4LS41NzIuODU1LS43NDUuMzQyLS4xNzQuNzQtLjI2IDEuMTk2LS4yNi40NzQgMCAuODkxLjA5IDEuMjUxLjI3My4zNjUuMTgyLjY2OC40NDIuOTEuNzguMjQuMzMyLjQyLjcyOS41NCAxLjE4OS4xMjIuNDYuMTg0Ljk2OC4xODQgMS41MjRabS0xLjY0OC4xNDR2LS4xNDRjMC0uMzMyLS4wMjctLjY0NC0uMDgyLS45MzZhMi40NTggMi40NTggMCAwIDAtLjI3My0uNzggMS4zNyAxLjM3IDAgMCAwLS41MDYtLjUyNmMtLjIxLS4xMzItLjQ3Mi0uMTk4LS43ODYtLjE5OC0uMjkyIDAtLjU0Mi4wNS0uNzUyLjE1LS4yMS4xLS4zODUuMjM3LS41MjYuNDFhMi4wMzEgMi4wMzEgMCAwIDAtLjMzNS42MDJjLS4wNzguMjI4LS4xMy40NzQtLjE1OC43Mzh2MS4yMzhjLjA0MS4zNDIuMTI4LjY1Ni4yNi45NDMuMTM3LjI4My4zMjguNTEuNTc0LjY4NC4yNDcuMTY4LjU2My4yNTIuOTUuMjUyLjMwNiAwIC41NjMtLjA2MS43NzMtLjE4NGExLjM1IDEuMzUgMCAwIDAgLjUtLjUxMyAyLjQ2IDIuNDYgMCAwIDAgLjI3My0uNzc5Yy4wNTktLjI5Ni4wODgtLjYxNS4wODgtLjk1N1ptMi45NzguMDA3di0uMTU3YzAtLjUzNC4wNzctMS4wMjguMjMyLTEuNDg0LjE1NS0uNDYuMzc5LS44NTkuNjctMS4xOTZhMy4wMjQgMy4wMjQgMCAwIDEgMS4wOC0uNzkzIDMuNTE2IDMuNTE2IDAgMCAxIDEuNDUtLjI4N2MuNTQyIDAgMS4wMjUuMDk2IDEuNDQ5LjI4Ny40MjguMTg3Ljc5LjQ1MSAxLjA4Ny43OTMuMjk2LjMzNy41MjEuNzM2LjY3NiAxLjE5Ni4xNTUuNDU2LjIzMy45NS4yMzMgMS40ODR2LjE1N2MwIC41MzMtLjA3OCAxLjAyNy0uMjMzIDEuNDgzYTMuNTUgMy41NSAwIDAgMS0uNjc2IDEuMTk3IDMuMTM2IDMuMTM2IDAgMCAxLTEuMDguNzkyIDMuNTQgMy41NCAwIDAgMS0xLjQ0My4yOCAzLjYwNyAzLjYwNyAwIDAgMS0xLjQ1Ni0uMjggMy4xMzUgMy4xMzUgMCAwIDEtMS4wOC0uNzkzIDMuNTUxIDMuNTUxIDAgMCAxLS42NzctMS4xOTYgNC41ODIgNC41ODIgMCAwIDEtLjIzMi0xLjQ4M1ptMS42NDctLjE1N3YuMTU3YzAgLjMzMy4wMzUuNjQ3LjEwMy45NDNhMi40IDIuNCAwIDAgMCAuMzIxLjc4Yy4xNDYuMjIzLjMzMy4zOTguNTYuNTI2LjIyOS4xMjcuNS4xOTEuODE0LjE5MS4zMDYgMCAuNTctLjA2NC43OTMtLjE5MS4yMjgtLjEyOC40MTUtLjMwMy41Ni0uNTI3LjE0Ny0uMjIzLjI1NC0uNDgzLjMyMi0uNzc5LjA3My0uMjk2LjExLS42MS4xMS0uOTQzdi0uMTU3YzAtLjMyOS0uMDM3LS42MzgtLjExLS45M2EyLjM4NyAyLjM4NyAwIDAgMC0uMzI4LS43ODYgMS41NyAxLjU3IDAgMCAwLS41Ni0uNTM0IDEuNTQzIDEuNTQzIDAgMCAwLS44LS4xOThjLS4zMSAwLS41OC4wNjYtLjgwNy4xOThhMS41ODcgMS41ODcgMCAwIDAtLjU1NC41MzRjLS4xNDYuMjI4LS4yNTMuNDktLjMyMS43ODZhNC4wNjcgNC4wNjcgMCAwIDAtLjEwMy45M1ptOC40OTUtMi4wNDRWNTFINzEuNTZ2LTcuMzk3aDEuNTUybC4wOTYgMS41OFptLS4yOTQgMS44NDUtLjUzNC0uMDA2YTQuODA4IDQuODA4IDAgMCAxIC4yMi0xLjQ0M2MuMTQ1LS40MzcuMzQ2LS44MTMuNi0xLjEyOC4yNi0uMzE0LjU3LS41NTYuOTMtLjcyNC4zNi0uMTc0Ljc2MS0uMjYgMS4yMDQtLjI2LjM1NSAwIC42NzYuMDUuOTYzLjE1LjI5Mi4wOTYuNTQuMjUzLjc0Ni40NzIuMjEuMjE5LjM2OS41MDMuNDc4Ljg1NC4xMS4zNDcuMTY0Ljc3My4xNjQgMS4yNzlWNTFoLTEuNjU0di00Ljc4NWMwLS4zNTYtLjA1My0uNjM2LS4xNTctLjg0MWEuODguODggMCAwIDAtLjQ0NS0uNDQ0Yy0uMTkxLS4wOTEtLjQzLS4xMzctLjcxOC0uMTM3LS4yODIgMC0uNTM1LjA2LS43NTguMTc4LS4yMjQuMTE4LS40MTMuMjgtLjU2OC40ODUtLjE1LjIwNS0uMjY2LjQ0Mi0uMzQ4LjcxMWEyLjkzOCAyLjkzOCAwIDAgMC0uMTIzLjg2MVpNODUgNDUuMTA4VjUxaC0xLjY0OHYtNy4zOTdoMS41NTJMODUgNDUuMTA3Wm0tLjI2NyAxLjkyLS41Ni0uMDA2YzAtLjUxLjA2My0uOTgzLjE5LTEuNDE2YTMuNCAzLjQgMCAwIDEgLjU2MS0xLjEyOGMuMjQ2LS4zMjMuNTUyLS41NzEuOTE2LS43NDUuMzctLjE3Ny43OTYtLjI2NiAxLjI3OS0uMjY2LjMzNyAwIC42NDQuMDUuOTIyLjE1LjI4My4wOTYuNTI3LjI0OS43MzIuNDU4LjIxLjIxLjM3LjQ3OS40NzguODA3LjExNC4zMjguMTcxLjcyNC4xNzEgMS4xOVY1MWgtMS42NDd2LTQuNzg1YzAtLjM2LS4wNTUtLjY0My0uMTY0LS44NDhhLjkxLjkxIDAgMCAwLS40NTgtLjQzNyAxLjY1OCAxLjY1OCAwIDAgMC0uNzA0LS4xMzdjLS4zMSAwLS41NzUuMDYtLjc5My4xNzgtLjIxNS4xMTgtLjM5LjI4LS41MjcuNDg1YTIuMTczIDIuMTczIDAgMCAwLS4zLjcxMSAzLjcyMyAzLjcyMyAwIDAgMC0uMDk2Ljg2MVptNC41ODctLjQzNy0uNzczLjE3YzAtLjQ0Ni4wNjItLjg2Ny4xODUtMS4yNjQuMTI3LS40MDEuMzEyLS43NTIuNTU0LTEuMDUzLjI0Ni0uMzA1LjU0OS0uNTQ0LjkwOS0uNzE3YTIuODIgMi44MiAwIDAgMSAxLjIzNy0uMjZjLjM3OCAwIC43MTYuMDUyIDEuMDEyLjE1Ny4zLjEuNTU2LjI2Ljc2NS40NzguMjEuMjIuMzcuNTA0LjQ3OS44NTUuMTEuMzQ2LjE2NC43NjYuMTY0IDEuMjU4VjUxaC0xLjY1NHYtNC43OTJjMC0uMzc0LS4wNTUtLjY2My0uMTY0LS44NjhhLjg1Ljg1IDAgMCAwLS40NTEtLjQyNCAxLjgyIDEuODIgMCAwIDAtLjcwNS0uMTIzYy0uMjU1IDAtLjQ4LjA0OC0uNjc2LjE0NGExLjM0MyAxLjM0MyAwIDAgMC0uNDg2LjM5IDEuNzM1IDEuNzM1IDAgMCAwLS4zLjU2NyAyLjQzMiAyLjQzMiAwIDAgMC0uMDk2LjY5N1ptNi4xNjMuNzkzdi0uMTU3YzAtLjUzNC4wNzctMS4wMjguMjMyLTEuNDg0LjE1NS0uNDYuMzc5LS44NTkuNjctMS4xOTZhMy4wMjUgMy4wMjUgMCAwIDEgMS4wOC0uNzkzIDMuNTE2IDMuNTE2IDAgMCAxIDEuNDUtLjI4N2MuNTQyIDAgMS4wMjUuMDk2IDEuNDQ5LjI4Ny40MjguMTg3Ljc5LjQ1MSAxLjA4Ny43OTMuMjk2LjMzNy41MjEuNzM2LjY3NiAxLjE5Ni4xNTUuNDU2LjIzMy45NS4yMzMgMS40ODR2LjE1N2MwIC41MzMtLjA3OCAxLjAyNy0uMjMzIDEuNDgzLS4xNTUuNDU2LS4zOC44NTUtLjY3NiAxLjE5N2EzLjE1IDMuMTUgMCAwIDEtMS4wOC43OTJjLS40MjQuMTg3LS45MDUuMjgtMS40NDMuMjhhMy42MDcgMy42MDcgMCAwIDEtMS40NTYtLjI4IDMuMTM2IDMuMTM2IDAgMCAxLTEuMDgtLjc5MyAzLjU1IDMuNTUgMCAwIDEtLjY3Ny0xLjE5NiA0LjU4MiA0LjU4MiAwIDAgMS0uMjMyLTEuNDgzWm0xLjY0Ny0uMTU3di4xNTdjMCAuMzMzLjAzNC42NDcuMTAzLjk0My4wNjguMjk2LjE3NS41NTYuMzIxLjc4LjE0Ni4yMjMuMzMzLjM5OC41Ni41MjYuMjI5LjEyNy41LjE5MS44MTQuMTkxLjMwNSAwIC41Ny0uMDY0Ljc5My0uMTkxYTEuNTkgMS41OSAwIDAgMCAuNTYxLS41MjdjLjE0Ni0uMjIzLjI1My0uNDgzLjMyMS0uNzc5YTMuOTMgMy45MyAwIDAgMCAuMTA5LS45NDN2LS4xNTdjMC0uMzI5LS4wMzYtLjYzOC0uMTA5LS45M2EyLjM5IDIuMzkgMCAwIDAtLjMyOC0uNzg2IDEuNTcgMS41NyAwIDAgMC0uNTYtLjUzNCAxLjU0MyAxLjU0MyAwIDAgMC0uOC0uMTk4Yy0uMzEgMC0uNTguMDY2LS44MDcuMTk4YTEuNTg3IDEuNTg3IDAgMCAwLS41NTQuNTM0Yy0uMTQ2LjIyOC0uMjUzLjQ5LS4zMjEuNzg2YTQuMDYyIDQuMDYyIDAgMCAwLS4xMDMuOTNabTguNDk0LTIuMDQ0VjUxaC0xLjY0N3YtNy4zOTdoMS41NTJsLjA5NSAxLjU4Wm0tLjI5MyAxLjg0NS0uNTM0LS4wMDZhNC44MzQgNC44MzQgMCAwIDEgLjIxOS0xLjQ0MyAzLjU0IDMuNTQgMCAwIDEgLjYwMi0xLjEyOGMuMjU5LS4zMTQuNTY5LS41NTYuOTI5LS43MjQuMzYtLjE3NC43NjEtLjI2IDEuMjAzLS4yNi4zNTYgMCAuNjc3LjA1Ljk2NC4xNS4yOTIuMDk2LjU0LjI1My43NDUuNDcyLjIxLjIxOS4zNy41MDMuNDc5Ljg1NC4xMDkuMzQ3LjE2NC43NzMuMTY0IDEuMjc5VjUxaC0xLjY1NHYtNC43ODVjMC0uMzU2LS4wNTMtLjYzNi0uMTU3LS44NDFhLjg4My44ODMgMCAwIDAtLjQ0NS0uNDQ0Yy0uMTkxLS4wOTEtLjQzLS4xMzctLjcxOC0uMTM3LS4yODIgMC0uNTM1LjA2LS43NTguMTc4LS4yMjQuMTE4LS40MTMuMjgtLjU2OC40ODUtLjE1LjIwNS0uMjY2LjQ0Mi0uMzQ4LjcxMWEyLjkzOCAyLjkzOCAwIDAgMC0uMTIzLjg2MVptNi4zODguMzU2di0uMTU3YzAtLjUzNC4wNzgtMS4wMjguMjMzLTEuNDg0LjE1NS0uNDYuMzc4LS44NTkuNjctMS4xOTZhMy4wMiAzLjAyIDAgMCAxIDEuMDgtLjc5MyAzLjUxMyAzLjUxMyAwIDAgMSAxLjQ0OS0uMjg3Yy41NDIgMCAxLjAyNS4wOTYgMS40NDkuMjg3LjQyOC4xODcuNzkxLjQ1MSAxLjA4Ny43OTMuMjk2LjMzNy41MjIuNzM2LjY3NyAxLjE5Ni4xNTUuNDU2LjIzMi45NS4yMzIgMS40ODR2LjE1N2MwIC41MzMtLjA3NyAxLjAyNy0uMjMyIDEuNDgzYTMuNTU1IDMuNTU1IDAgMCAxLS42NzcgMS4xOTcgMy4xMzIgMy4xMzIgMCAwIDEtMS4wOC43OTJjLS40MjQuMTg3LS45MDUuMjgtMS40NDIuMjhhMy42MTEgMy42MTEgMCAwIDEtMS40NTctLjI4IDMuMTUgMy4xNSAwIDAgMS0xLjA4LS43OTMgMy41NTMgMy41NTMgMCAwIDEtLjY3Ni0xLjE5NiA0LjU3OSA0LjU3OSAwIDAgMS0uMjMzLTEuNDgzWm0xLjY0OC0uMTU3di4xNTdjMCAuMzMzLjAzNC42NDcuMTAyLjk0My4wNjkuMjk2LjE3Ni41NTYuMzIyLjc4LjE0NS4yMjMuMzMyLjM5OC41Ni41MjYuMjI4LjEyNy40OTkuMTkxLjgxNC4xOTEuMzA1IDAgLjU2OS0uMDY0Ljc5Mi0uMTkxYTEuNTkgMS41OSAwIDAgMCAuNTYxLS41MjdjLjE0Ni0uMjIzLjI1My0uNDgzLjMyMS0uNzc5LjA3My0uMjk2LjExLS42MS4xMS0uOTQzdi0uMTU3YzAtLjMyOS0uMDM3LS42MzgtLjExLS45M2EyLjM2OCAyLjM2OCAwIDAgMC0uMzI4LS43ODYgMS41NjggMS41NjggMCAwIDAtLjU2LS41MzQgMS41NDUgMS41NDUgMCAwIDAtLjgtLjE5OGMtLjMxIDAtLjU3OS4wNjYtLjgwNy4xOThhMS41ODEgMS41ODEgMCAwIDAtLjU1My41MzRjLS4xNDYuMjI4LS4yNTMuNDktLjMyMi43ODZhNC4wODQgNC4wODQgMCAwIDAtLjEwMi45M1ptNy45NjctMy42MjMgMS40MDkgMi40NTQgMS40MzUtMi40NTVoMS44MTJsLTIuMjQyIDMuNjNMMTI2LjA4IDUxaC0xLjgxMmwtMS41MDQtMi41NTdMMTIxLjI2IDUxaC0xLjgxOGwyLjMyNC0zLjc2Ny0yLjIzNS0zLjYzaDEuODA0Wm03Ljc5NyAwVjUxaC0xLjY1NHYtNy4zOTdoMS42NTRabS0xLjc2My0xLjk0MmMwLS4yNS4wODItLjQ1OC4yNDYtLjYyMi4xNjgtLjE2OS40MDEtLjI1My42OTctLjI1My4yOTIgMCAuNTIyLjA4NC42OS4yNTNhLjgzLjgzIDAgMCAxIC4yNTMuNjIyLjgyMi44MjIgMCAwIDEtLjI1My42MTVjLS4xNjguMTY0LS4zOTguMjQ2LS42OS4yNDYtLjI5NiAwLS41MjktLjA4Mi0uNjk3LS4yNDZhLjgzNS44MzUgMCAwIDEtLjI0Ni0uNjE1Wm04LjMzIDcuODA3VjQwLjVoMS42NTRWNTFoLTEuNDk3bC0uMTU3LTEuNTMxWm0tNC44MTMtMi4wODV2LS4xNDRjMC0uNTYuMDY2LTEuMDcuMTk4LTEuNTMxLjEzMy0uNDY1LjMyNC0uODY0LjU3NS0xLjE5Ni4yNS0uMzM3LjU1Ni0uNTk1LjkxNi0uNzczLjM2LS4xODIuNzY1LS4yNzMgMS4yMTctLjI3My40NDYgMCAuODM4LjA4NiAxLjE3NS4yNi4zMzguMTczLjYyNS40MjEuODYyLjc0NS4yMzcuMzE5LjQyNi43MDEuNTY3IDEuMTQ4LjE0MS40NDIuMjQyLjkzNC4zMDEgMS40Nzd2LjQ1OGE3LjM5OSA3LjM5OSAwIDAgMS0uMzAxIDEuNDQ5Yy0uMTQxLjQzNy0uMzMuODE2LS41NjcgMS4xMzVhMi40NTMgMi40NTMgMCAwIDEtLjg2OS43MzhjLS4zMzcuMTczLS43MzEuMjYtMS4xODIuMjYtLjQ0NyAwLS44NS0uMDk0LTEuMjEtLjI4YTIuNjYyIDIuNjYyIDAgMCAxLS45MDktLjc4NyAzLjcxMiAzLjcxMiAwIDAgMS0uNTc1LTEuMTkgNS40MTMgNS40MTMgMCAwIDEtLjE5OC0xLjQ5NlptMS42NDgtLjE0NHYuMTQ0YzAgLjMzNy4wMjkuNjUxLjA4OS45NDMuMDYzLjI5Mi4xNjEuNTUuMjk0Ljc3My4xMzIuMjE4LjMwMy4zOTIuNTEyLjUyLjIxNC4xMjIuNDcuMTg0Ljc2Ni4xODQuMzc0IDAgLjY4MS0uMDgyLjkyMy0uMjQ2LjI0MS0uMTY0LjQzLS4zODYuNTY3LS42NjMuMTQxLS4yODMuMjM3LS41OTguMjg3LS45NDR2LTEuMjM3YTIuOTg4IDIuOTg4IDAgMCAwLS4xNzEtLjc1MiAxLjk5NiAxLjk5NiAwIDAgMC0uMzM1LS42MDkgMS40MDUgMS40MDUgMCAwIDAtLjUyNi0uNDEgMS42NSAxLjY1IDAgMCAwLS43MzItLjE1Yy0uMyAwLS41NTUuMDY0LS43NjUuMTkxLS4yMS4xMjgtLjM4My4zMDMtLjUyLjUyN2EyLjYgMi42IDAgMCAwLS4yOTQuNzc5IDQuNTYzIDQuNTYzIDAgMCAwLS4wOTUuOTVabTEwLjA1MiAzLjg5N2EzLjk0NSAzLjk0NSAwIDAgMS0xLjQ4My0uMjY3IDMuMjggMy4yOCAwIDAgMS0xLjEyMS0uNzU5IDMuMzY3IDMuMzY3IDAgMCAxLS43MDQtMS4xNDEgMy45OTkgMy45OTkgMCAwIDEtLjI0Ni0xLjQxNXYtLjI3NGMwLS41NzguMDg0LTEuMTAzLjI1My0xLjU3Mi4xNjgtLjQ3LjQwMy0uODcuNzA0LTEuMjAzYTMuMDI2IDMuMDI2IDAgMCAxIDEuMDY2LS43NzNjLjQxLS4xNzcuODU1LS4yNjYgMS4zMzMtLjI2Ni41MjkgMCAuOTkxLjA4OSAxLjM4OC4yNjYuMzk2LjE3OC43MjQuNDI5Ljk4NC43NTIuMjY1LjMyLjQ2MS43LjU4OCAxLjE0Mi4xMzIuNDQyLjE5OC45My4xOTggMS40NjN2LjcwNGgtNS43MTRWNDYuNjFoNC4wODd2LS4xM2EyLjMxOCAyLjMxOCAwIDAgMC0uMTc3LS44MzQgMS40MTggMS40MTggMCAwIDAtLjQ4Ni0uNjI4Yy0uMjE4LS4xNi0uNTEtLjI0LS44NzUtLjI0LS4yNzMgMC0uNTE3LjA2LS43MzEuMTc4LS4yMS4xMTQtLjM4NS4yOC0uNTI2LjVhMi42MjIgMi42MjIgMCAwIDAtLjMyOS43OTJjLS4wNzMuMzA1LS4xMDkuNjUtLjEwOSAxLjAzMnYuMjc0YzAgLjMyMy4wNDMuNjI0LjEzLjkwMi4wOTEuMjczLjIyMy41MTMuMzk2LjcxOC4xNzQuMjA1LjM4My4zNjcuNjI5LjQ4NS4yNDYuMTE0LjUyNy4xNzEuODQxLjE3MS4zOTcgMCAuNzUtLjA4IDEuMDYtLjI0LjMxLS4xNTkuNTc4LS4zODQuODA2LS42NzZsLjg2OS44NGMtLjE2LjIzMy0uMzY3LjQ1Ny0uNjIzLjY3LS4yNTUuMjEtLjU2Ny4zODEtLjkzNi41MTRhMy43MzIgMy43MzIgMCAwIDEtMS4yNzIuMTk4WiIvPjxwYXRoIGZpbGw9IiNERTIzNDMiIGQ9Ik0yMS44MiA5Ni44MzJoLjM1MnYyLjMwOWgtLjJjLTEuMDA3IDAtMS44NzguMTU2LTIuNjEzLjQ2OC0uNzI2LjMxMy0xLjMyNC43NDMtMS43OTMgMS4yODlhNS40NzggNS40NzggMCAwIDAtMS4wNTQgMS45MjJjLS4yMjcuNzI3LS4zNCAxLjUtLjM0IDIuMzIxdjIuNjgzYzAgLjY4LjA3NCAxLjI4MS4yMjMgMS44MDUuMTQ4LjUxNi4zNTUuOTQ5LjYyIDEuMzAxLjI3NC4zNDMuNTg3LjYwNS45MzguNzg1LjM1Mi4xOC43My4yNjkgMS4xMzcuMjY5LjQyMiAwIC44MDUtLjA4NiAxLjE0OC0uMjU3LjM0NC0uMTguNjM3LS40MjYuODgtLjczOS4yNDEtLjMxMi40MjUtLjY4My41NS0xLjExMy4xMjUtLjQzLjE4OC0uODk4LjE4OC0xLjQwNiAwLS40ODUtLjA2My0uOTM4LS4xODgtMS4zNmEzLjQxMiAzLjQxMiAwIDAgMC0uNTI3LTEuMTI1IDIuNDE1IDIuNDE1IDAgMCAwLS44OC0uNzYxIDIuNDUxIDIuNDUxIDAgMCAwLTEuMTk1LS4yODJjLS41NjIgMC0xLjA3NC4xMzMtMS41MzUuMzk5LS40NTMuMjY1LS44Mi42MTMtMS4xMDEgMS4wNDNhMi42ODQgMi42ODQgMCAwIDAtLjQ0NiAxLjM0N2wtMS4wNzgtLjM1MWE2LjA0OCA2LjA0OCAwIDAgMSAuNDgtMS45NTcgNC43MjYgNC43MjYgMCAwIDEgMS4wNDQtMS40NzdjLjQzLS40MDYuOTI1LS43MTUgMS40ODgtLjkyNWE1LjE4OSA1LjE4OSAwIDAgMSAxLjg3NS0uMzI5Yy44MjggMCAxLjU0Ny4xNTcgMi4xNTYuNDY5LjYxLjMxMyAxLjExMy43MzggMS41MTIgMS4yNzguNDA2LjUzMS43MDcgMS4xNC45MDIgMS44MjguMjAzLjY3OS4zMDUgMS4zOS4zMDUgMi4xMzIgMCAuODIxLS4xMjUgMS41ODYtLjM3NSAyLjI5N2E1LjQwMiA1LjQwMiAwIDAgMS0xLjEwMiAxLjg1MiA0Ljk0OSA0Ljk0OSAwIDAgMS0xLjc0NiAxLjI0MmMtLjY4LjI5Ny0xLjQ1My40NDUtMi4zMi40NDUtLjkxNCAwLTEuNzMtLjE3NS0yLjQ1LS41MjdhNS4zODggNS4zODggMCAwIDEtMS44MTYtMS40NTMgNi42MjMgNi42MjMgMCAwIDEtMS4xMjUtMi4xMzMgOC4zMjQgOC4zMjQgMCAwIDEtLjM4Ni0yLjU1NXYtMS4xNzFjMC0xLjI5Ny4xNjQtMi41Mi40OTItMy42NjguMzI4LTEuMTU3LjgzMi0yLjE3NiAxLjUxMi0zLjA1OS42ODctLjg4MyAxLjU2Ni0xLjU3NCAyLjYzNi0yLjA3NCAxLjA3LS41MDggMi4zNDgtLjc2MiAzLjgzMi0uNzYyWm0xNi4yMyA3LjIwN3YyLjc4OWMwIDEuMzM2LS4xMzIgMi40NzctLjM5OCAzLjQyMi0uMjU4LjkzOC0uNjMzIDEuNjk5LTEuMTI1IDIuMjg1YTQuNDEgNC40MSAwIDAgMS0xLjc3IDEuMjg5Yy0uNjc5LjI3NC0xLjQ0LjQxLTIuMjg0LjQxYTYuNTg0IDYuNTg0IDAgMCAxLTEuODc1LS4yNTcgNC41ODYgNC41ODYgMCAwIDEtMS41NDctLjgwOSA0LjgzMyA0LjgzMyAwIDAgMS0xLjE4NC0xLjQxOGMtLjMyLS41ODYtLjU3LTEuMjg1LS43NS0yLjA5OC0uMTcyLS44MTItLjI1OC0xLjc1NC0uMjU4LTIuODI0di0yLjc4OWMwLTEuMzQ0LjEzMy0yLjQ3Ny4zOTktMy4zOTguMjY1LS45My42NDQtMS42ODQgMS4xMzctMi4yNjJhNC4zMyA0LjMzIDAgMCAxIDEuNzU3LTEuMjc3Yy42ODgtLjI2NiAxLjQ1My0uMzk5IDIuMjk3LS4zOTkuNjggMCAxLjMwNS4wODYgMS44NzUuMjU4YTQuMzkgNC4zOSAwIDAgMSAxLjU0Ny43ODVjLjQ2MS4zNi44NTIuODI4IDEuMTcyIDEuNDA2LjMyOC41Ny41NzggMS4yNjIuNzUgMi4wNzUuMTcyLjgwNC4yNTggMS43NDIuMjU4IDIuODEyWm0tMi44MjMgMy4xODh2LTMuNjFjMC0uNjc5LS4wNC0xLjI3Ny0uMTE4LTEuNzkzLS4wNzgtLjUyMy0uMTk1LS45NjUtLjM1MS0xLjMyNGEyLjY0IDIuNjQgMCAwIDAtLjU2My0uODkgMS45OCAxLjk4IDAgMCAwLS43NzMtLjUwNCAyLjczMSAyLjczMSAwIDAgMC0uOTczLS4xNjVjLS40MzcgMC0uODI4LjA4Ni0xLjE3Mi4yNTgtLjM0My4xNjQtLjYzMy40My0uODY3Ljc5Ny0uMjM0LjM2Ny0uNDE0Ljg1Mi0uNTM5IDEuNDUzLS4xMTcuNTk0LS4xNzYgMS4zMTctLjE3NiAyLjE2OHYzLjYxYzAgLjY4Ny4wNCAxLjI5My4xMTcgMS44MTYuMDc5LjUyMy4xOTYuOTczLjM1MiAxLjM0OC4xNTYuMzY3LjM0NC42NzEuNTYzLjkxNGEyLjEgMi4xIDAgMCAwIC43NzMuNTE1Yy4yOTcuMTEuNjIxLjE2NC45NzMuMTY0LjQ0NSAwIC44NC0uMDg2IDEuMTgzLS4yNTcuMzQ0LS4xNzIuNjMzLS40NDYuODY3LS44MjEuMjM1LS4zODMuNDEtLjg3OS41MjgtMS40ODguMTE3LS42MDkuMTc2LTEuMzQuMTc2LTIuMTkxWk00Ny4yODUgMTAzLjg5OFYxMTRINDQuNDZ2LTEyLjY4aDIuNjZsLjE2NCAyLjU3OFptLS40NTcgMy4yOTMtLjk2MS0uMDExYzAtLjg3NS4xMS0xLjY4NC4zMjgtMi40MjZhNS44MyA1LjgzIDAgMCAxIC45NjEtMS45MzQgNC4yMyA0LjIzIDAgMCAxIDEuNTctMS4yNzdjLjYzMy0uMzA1IDEuMzY0LS40NTcgMi4xOTItLjQ1Ny41NzggMCAxLjEwNS4wODYgMS41ODIuMjU4YTMuMTQgMy4xNCAwIDAgMSAxLjI1NC43ODVjLjM1OS4zNTkuNjMzLjgyLjgyIDEuMzgzLjE5NS41NjIuMjkzIDEuMjQyLjI5MyAyLjAzOVYxMTRoLTIuODI0di04LjIwM2MwLS42MTctLjA5NC0xLjEwMi0uMjgyLTEuNDUzYTEuNTU1IDEuNTU1IDAgMCAwLS43ODUtLjc1Yy0uMzM2LS4xNTYtLjczOC0uMjM1LTEuMjA3LS4yMzUtLjUzMSAwLS45ODQuMTAyLTEuMzYuMzA1YTIuNTYyIDIuNTYyIDAgMCAwLS45MDEuODMyIDMuNzMyIDMuNzMyIDAgMCAwLS41MTYgMS4yMTljLS4xMS40NjEtLjE2NC45NTMtLjE2NCAxLjQ3NlptNy44NjMtLjc1LTEuMzI0LjI5M2MwLS43NjUuMTA1LTEuNDg4LjMxNi0yLjE2OGE1LjUyMSA1LjUyMSAwIDAgMSAuOTUtMS44MDQgNC40NSA0LjQ1IDAgMCAxIDEuNTU4LTEuMjMxYy42MTctLjI5NyAxLjMyNC0uNDQ1IDIuMTIxLS40NDUuNjQ5IDAgMS4yMjcuMDkgMS43MzUuMjY5YTMuMTggMy4xOCAwIDAgMSAxLjMxMi44MjFjLjM2LjM3NS42MzMuODYzLjgyIDEuNDY1LjE4OC41OTMuMjgyIDEuMzEyLjI4MiAyLjE1NlYxMTRoLTIuODM2di04LjIxNWMwLS42NC0uMDk0LTEuMTM3LS4yODItMS40ODhhMS40NTggMS40NTggMCAwIDAtLjc3My0uNzI3Yy0uMzM2LS4xNC0uNzM4LS4yMTEtMS4yMDctLjIxMS0uNDM4IDAtLjgyNC4wODItMS4xNi4yNDZhMi4zMTEgMi4zMTEgMCAwIDAtLjgzMi42NjggMi45ODkgMi45ODkgMCAwIDAtLjUxNi45NzMgNC4xNyA0LjE3IDAgMCAwLS4xNjQgMS4xOTVabTE4Ljc5Ny01LjEyMWgyLjU2NnYxMi4zMjhjMCAxLjE0MS0uMjQyIDIuMTEtLjcyNiAyLjkwN2E0LjY2OCA0LjY2OCAwIDAgMS0yLjAyOCAxLjgxNmMtLjg2Ny40MjItMS44Ny42MzMtMy4wMTEuNjMzLS40ODUgMC0xLjAyNC0uMDctMS42MTctLjIxMWE2LjYyNCA2LjYyNCAwIDAgMS0xLjcxMS0uNjggNC4yMzQgNC4yMzQgMCAwIDEtMS4zNzEtMS4yMDdsMS4zMjQtMS42NjRjLjQ1My41MzkuOTUzLjkzNCAxLjUgMS4xODQuNTQ3LjI1IDEuMTIuMzc1IDEuNzIyLjM3NS42NDkgMCAxLjItLjEyMSAxLjY1My0uMzYzYTIuNDYzIDIuNDYzIDAgMCAwIDEuMDY2LTEuMDQzYy4yNS0uNDYxLjM3NS0xLjAyNC4zNzUtMS42ODh2LTkuNTE2bC4yNTgtMi44NzFabS04LjYxMyA2LjQ4MXYtLjI0NmMwLS45NjEuMTE3LTEuODM2LjM1MS0yLjYyNS4yMzUtLjc5Ny41Ny0xLjQ4MSAxLjAwOC0yLjA1MWE0LjM5IDQuMzkgMCAwIDEgMS41OTQtMS4zMjRjLjYyNS0uMzEzIDEuMzMyLS40NjkgMi4xMjEtLjQ2OS44MiAwIDEuNTIuMTQ4IDIuMDk4LjQ0NWEzLjk2MyAzLjk2MyAwIDAgMSAxLjQ2NSAxLjI3OGMuMzkuNTQ2LjY5NSAxLjIwMy45MTMgMS45NjguMjI3Ljc1OC4zOTUgMS42MDIuNTA0IDIuNTMydi43ODVhMTIuNjc0IDEyLjY3NCAwIDAgMS0uNTE1IDIuNDg0Yy0uMjQyLjc1LS41NjMgMS4zOTktLjk2MSAxLjk0NS0uMzk5LjU0Ny0uODkuOTY5LTEuNDc3IDEuMjY2LS41NzguMjk3LTEuMjYxLjQ0NS0yLjA1LjQ0NS0uNzc0IDAtMS40NzMtLjE2LTIuMDk4LS40OGE0Ljc3MyA0Ljc3MyAwIDAgMS0xLjU5NC0xLjM0OCA2LjM4NCA2LjM4NCAwIDAgMS0xLjAwOC0yLjAzOSA4Ljk3MyA4Ljk3MyAwIDAgMS0uMzUxLTIuNTY2Wm0yLjgyNC0uMjQ2di4yNDZjMCAuNTc4LjA1NSAxLjExNy4xNjQgMS42MTcuMTE3LjUuMjkzLjk0MS41MjcgMS4zMjQuMjQzLjM3NS41NDcuNjcyLjkxNC44OTEuMzc1LjIxMS44MTcuMzE2IDEuMzI1LjMxNi42NjQgMCAxLjIwNy0uMTQgMS42MjktLjQyMi40My0uMjgxLjc1Ny0uNjYuOTg0LTEuMTM2YTUuNzU5IDUuNzU5IDAgMCAwIC40OTItMS42MTh2LTIuMTIxYTUuMTgyIDUuMTgyIDAgMCAwLS4yOTMtMS4yODkgMy40NTcgMy40NTcgMCAwIDAtLjU3NC0xLjA0MyAyLjQwMiAyLjQwMiAwIDAgMC0uOTE0LS43MDNjLS4zNjctLjE3Mi0uOC0uMjU4LTEuMy0uMjU4LS41MDkgMC0uOTUuMTEtMS4zMjUuMzI5YTIuNjggMi42OCAwIDAgMC0uOTI2LjkwMiA0LjQ2IDQuNDYgMCAwIDAtLjUyNyAxLjMzNiA3LjIzMyA3LjIzMyAwIDAgMC0uMTc2IDEuNjI5Wm0xOC43MDMtMTAuNjE4LTYuNjQ0IDE4LjUyOGgtMi4yMTVsNi42NTYtMTguNTI4aDIuMjAzWm00Ljc4MSA2Ljk2MVYxMTRIODguMzZ2LTEyLjY4aDIuNjZsLjE2NCAyLjU3OFptLS40NTcgMy4yOTMtLjk2LS4wMTFjMC0uODc1LjEwOS0xLjY4NC4zMjgtMi40MjYuMjE4LS43NDIuNTM5LTEuMzg3Ljk2LTEuOTM0YTQuMjMgNC4yMyAwIDAgMSAxLjU3LTEuMjc3Yy42MzQtLjMwNSAxLjM2NC0uNDU3IDIuMTkyLS40NTcuNTc4IDAgMS4xMDYuMDg2IDEuNTgyLjI1OC40ODUuMTY0LjkwMy40MjYgMS4yNTQuNzg1LjM2LjM1OS42MzMuODIuODIgMS4zODMuMTk2LjU2Mi4yOTMgMS4yNDIuMjkzIDIuMDM5VjExNGgtMi44MjR2LTguMjAzYzAtLjYxNy0uMDk0LTEuMTAyLS4yODEtMS40NTNhMS41NTUgMS41NTUgMCAwIDAtLjc4NS0uNzVjLS4zMzYtLjE1Ni0uNzM5LS4yMzUtMS4yMDctLjIzNS0uNTMyIDAtLjk4NS4xMDItMS4zNi4zMDVhMi41NjQgMi41NjQgMCAwIDAtLjkwMi44MzIgMy43MzIgMy43MzIgMCAwIDAtLjUxNiAxLjIxOWMtLjEwOS40NjEtLjE2NC45NTMtLjE2NCAxLjQ3NlptNy44NjQtLjc1LTEuMzI1LjI5M2MwLS43NjUuMTA2LTEuNDg4LjMxNy0yLjE2OGE1LjUyIDUuNTIgMCAwIDEgLjk0OS0xLjgwNCA0LjQ0OSA0LjQ0OSAwIDAgMSAxLjU1OS0xLjIzMWMuNjE3LS4yOTcgMS4zMjQtLjQ0NSAyLjEyMS0uNDQ1LjY0OCAwIDEuMjI2LjA5IDEuNzM0LjI2OS41MTYuMTcyLjk1My40NDYgMS4zMTMuODIxLjM1OS4zNzUuNjMyLjg2My44MiAxLjQ2NS4xODcuNTkzLjI4MSAxLjMxMi4yODEgMi4xNTZWMTE0aC0yLjgzNnYtOC4yMTVjMC0uNjQtLjA5NC0xLjEzNy0uMjgxLTEuNDg4YTEuNDYxIDEuNDYxIDAgMCAwLS43NzMtLjcyN2MtLjMzNi0uMTQtLjczOS0uMjExLTEuMjA4LS4yMTEtLjQzNyAwLS44MjQuMDgyLTEuMTYuMjQ2YTIuMzExIDIuMzExIDAgMCAwLS44MzIuNjY4IDIuOTg1IDIuOTg1IDAgMCAwLS41MTUuOTczIDQuMTcgNC4xNyAwIDAgMC0uMTY0IDEuMTk1Wm0xMi4zODYtNS43MDdoMS4wMmMuMzM2IDAgLjYxMy0uMDQ2LjgzMi0uMTQuMjE5LS4xMDIuMzgzLS4yMzkuNDkyLS40MS4xMTctLjE3Mi4xNzYtLjM3NS4xNzYtLjYxYS45OTguOTk4IDAgMCAwLS4zMjgtLjczOGMtLjIxMS0uMjE5LS41NjctLjMyOC0xLjA2Ny0uMzI4LS4zODIgMC0uNzA3LjA4Mi0uOTcyLjI0Ni0uMjU4LjE1Ni0uMzg3LjM3OS0uMzg3LjY2OGgtMi4xMzNjMC0uNTU1LjE1Ni0xLjAyOC40NjktMS40MTguMzEyLS4zOS43My0uNjg4IDEuMjU0LS44OWE0LjY5IDQuNjkgMCAwIDEgMS43MTEtLjMwNWMuNzExIDAgMS4zMzYuMTAxIDEuODc1LjMwNC41MzkuMTk2Ljk1Ny40ODkgMS4yNTQuODguMzA0LjM5LjQ1Ny44NzQuNDU3IDEuNDUyIDAgLjQ0Ni0uMTQxLjg0LS40MjIgMS4xODQtLjI3NC4zNDQtLjY1Ni42MTMtMS4xNDkuODA5LS40ODQuMTg3LTEuMDU0LjI4MS0xLjcxMS4yODFoLTEuMzcxdi0uOTg1Wm0wIDEuNDY1di0uOTYxaDEuMzcxYy43MjcgMCAxLjM0OC4wODYgMS44NjQuMjU4LjUxNS4xNjQuOTA2LjQxOCAxLjE3Mi43NjIuMjczLjM0NC40MS43ODkuNDEgMS4zMzYgMCAuNTc4LS4xNjQgMS4wNy0uNDkyIDEuNDc2LS4zMjkuNDA3LS43NzguNzE1LTEuMzQ4LjkyNi0uNTYzLjIxMS0xLjE5NS4zMTYtMS44OTkuMzE2YTUuMjkgNS4yOSAwIDAgMS0xLjcxLS4yODFjLS41NDctLjE5NS0xLS41LTEuMzYtLjkxNC0uMzUxLS40MTQtLjUyNy0uOTU3LS41MjctMS42MjloMi4xNDRjMCAuMzA1LjE0MS41NjcuNDIyLjc4NS4yODEuMjE5LjY2LjMyOSAxLjEzNy4zMjkuNTA4IDAgLjg4My0uMTEgMS4xMjUtLjMyOWExLjA5IDEuMDkgMCAwIDAgLjM3NS0uODQzYzAtLjI5Ny0uMDctLjUzNS0uMjExLS43MTVhMS4wOTEgMS4wOTEgMCAwIDAtLjU3NC0uMzg3IDIuNzA1IDIuNzA1IDAgMCAwLS44NzktLjEyOWgtMS4wMloiLz48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLW9wYWNpdHk9Ii44NyIgc3Ryb2tlLXdpZHRoPSIyIiBkPSJtMTE3Ljc2IDExNy4xNDggNi43NTYtOC45MDhhNi4yODEgNi4yODEgMCAwIDAgMS4xNzktMi42ODlsMy4wNC0xNi45MjRjLjYzMi0zLjUxNCA1LjY3MS0zLjUxNCA2LjMwMyAwbC4xODUgMS4wM2MuNjE2IDMuNDMgNS41NzcgMy4yOTkgNi4wMTItLjE1OHYwYy40MTEtMy4yNzMgNS4wMDgtMy42NDIgNS45MzctLjQ3N2wyLjEyMiA3LjIzMmMxLjQwMyA0Ljc4MSA4LjM0IDQuMjQ4IDguOTk1LS42OTFsMS4wMzUtNy44Yy45MTUtNi44OTkgMTAuNzA2LTcuNDA0IDEyLjMyNy0uNjM3bDEuMDAyIDQuMTg1Yy43NTIgMy4xNCA0LjUxIDQuNDM1IDcuMDM2IDIuNDI2djBhNC40MTIgNC40MTIgMCAwIDEgNS44MzguMzA1bDQuMDc4IDQuMDA3Ii8+PGRlZnM+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjE5MS4wMjMiIGhlaWdodD0iOTkuMDIzIiB4PSI0LjQ4OSIgeT0iMzAuNDg5IiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIyLjI1NiIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMDczXzIwMCIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA3M18yMDAiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48L2RlZnM+PC9zdmc+", + "description": "A colorless and odorless gas resulting from incomplete combustion, particularly in motor vehicles. Results displayed by combining the latest and aggregated values and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'carbon', label: 'Carbon monoxide', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'carbon', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Carbon monoxide\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#3FA71A\"},{\"from\":5,\"to\":10,\"color\":\"#80C32C\"},{\"from\":10,\"to\":25,\"color\":\"#FFA600\"},{\"from\":25,\"to\":50,\"color\":\"#F36900\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Carbon monoxide\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:molecule-co\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"mg/m³\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "carbon monoxide", + "CO" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_carbon_monoxide__co__chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_carbon_monoxide__co__chart_card_with_background.json new file mode 100644 index 0000000000..b5bf719df4 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_carbon_monoxide__co__chart_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "simple_carbon_monoxide_co_chart_card_with_background", + "name": "Simple carbon monoxide (CO) chart card with background", + "deprecated": false, + "image": "tb-image:Q08tc2ltcGxlLWNoYXJ0LXZhbHVlLWNhcmQtd2l0aC1iYWNrZ3JvdW5kLnN2Zw==:Q08tc2ltcGxlLWNoYXJ0LXZhbHVlLWNhcmQtd2l0aC1iYWNrZ3JvdW5kLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxMTIiIHk9IjI0IiBmaWxsPSJ1cmwoI2EpIiByeD0iNCIvPjxnIGZpbHRlcj0idXJsKCNiKSI+PHJlY3Qgd2lkdGg9IjE4OCIgaGVpZ2h0PSIxMDAiIHg9IjYiIHk9IjMwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii43NiIgcng9IjMuMDA4Ii8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjg3IiBkPSJNMjIuODkgNDUuNzZoMS43MWEzLjk0NCAzLjk0NCAwIDAgMS0uNTQ3IDEuNzQzYy0uMzEuNTA2LS43NDUuOTA1LTEuMzA2IDEuMTk2LS41Ni4yOTItMS4yNDIuNDM4LTIuMDQ0LjQzOC0uNjE1IDAtMS4xNjktLjExLTEuNjYxLS4zMjhhMy42NDMgMy42NDMgMCAwIDEtMS4yNjUtLjk0NGMtLjM1LS40MS0uNjItLjkwNC0uODA2LTEuNDgzYTYuNDQ1IDYuNDQ1IDAgMCAxLS4yNzQtMS45NDJ2LS44MjdjMC0uNzE1LjA5NC0xLjM2Mi4yOC0xLjk0MWE0LjI5IDQuMjkgMCAwIDEgLjgyLTEuNDgzIDMuNTQ1IDMuNTQ1IDAgMCAxIDEuMjgtLjk1IDQuMTc5IDQuMTc5IDAgMCAxIDEuNjg4LS4zMjljLjc5MyAwIDEuNDYyLjE0NiAyLjAxLjQzOC41NDYuMjkxLjk3LjY5NSAxLjI3IDEuMjEuMzA2LjUxNS40OTMgMS4xMDUuNTYxIDEuNzdoLTEuNzA5YTMuMTYzIDMuMTYzIDAgMCAwLS4zLTEuMSAxLjU1IDEuNTUgMCAwIDAtLjY3LS42OThjLS4yOTYtLjE2NC0uNjg0LS4yNDYtMS4xNjItLjI0Ni0uMzkyIDAtLjczNC4wNzMtMS4wMjYuMjE5LS4yOTEuMTQ2LS41MzUuMzYtLjczMS42NDJhMy4wOTUgMy4wOTUgMCAwIDAtLjQ0NCAxLjA0NmMtLjA5Ni40MS0uMTQ0Ljg4LS4xNDQgMS40MDl2Ljg0YzAgLjUwMi4wNDMuOTU4LjEzIDEuMzY4LjA5MS40MDUuMjI4Ljc1NC40MSAxLjA0NS4xODcuMjkyLjQyNC41MTguNzEuNjc3LjI4OC4xNi42MzIuMjQgMS4wMzMuMjQuNDg4IDAgLjg4Mi0uMDc4IDEuMTgzLS4yMzNhMS41NSAxLjU1IDAgMCAwIC42OS0uNjc3Yy4xNi0uMy4yNjQtLjY2Ny4zMTUtMS4xWm03LjUyNCAxLjc1N3YtMy41MjhjMC0uMjY0LS4wNDgtLjQ5Mi0uMTQ0LS42ODNhMS4wMDUgMS4wMDUgMCAwIDAtLjQzNy0uNDQ1Yy0uMTkxLS4xMDQtLjQzMy0uMTU3LS43MjUtLjE1Ny0uMjY4IDAtLjUuMDQ2LS42OTcuMTM3YTEuMTI4IDEuMTI4IDAgMCAwLS40NTguMzY5Ljg5Ljg5IDAgMCAwLS4xNjQuNTI2aC0xLjY0YzAtLjI5MS4wNy0uNTc0LjIxMS0uODQ3LjE0Mi0uMjc0LjM0Ny0uNTE4LjYxNi0uNzMyLjI2OC0uMjE0LjU5LS4zODMuOTYzLS41MDZhNC4wMDcgNC4wMDcgMCAwIDEgMS4yNTgtLjE4NGMuNTU2IDAgMS4wNDguMDkzIDEuNDc3LjI4LjQzMy4xODcuNzcyLjQ3IDEuMDE4Ljg0OC4yNTEuMzczLjM3Ni44NDMuMzc2IDEuNDA4djMuMjg4YzAgLjMzNy4wMjMuNjQuMDY5LjkxLjA1LjI2NC4xMi40OTQuMjEyLjY5VjQ5SDMwLjY2YTIuOTE3IDIuOTE3IDAgMCAxLS4xODQtLjY3NyA1LjUyOCA1LjUyOCAwIDAgMS0uMDYyLS44MDZabS4yNC0zLjAxNS4wMTMgMS4wMThoLTEuMTgzYy0uMzA1IDAtLjU3NC4wMy0uODA2LjA5YTEuNjU2IDEuNjU2IDAgMCAwLS41ODEuMjQ1Yy0uMTU1LjExLS4yNzEuMjQyLS4zNDkuMzk3LS4wNzcuMTU1LS4xMTYuMzMtLjExNi41MjZzLjA0NS4zNzYuMTM3LjU0Yy4wOS4xNi4yMjMuMjg1LjM5Ni4zNzYuMTc4LjA5MS4zOTIuMTM3LjY0My4xMzcuMzM3IDAgLjYzLS4wNjguODgxLS4yMDUuMjU2LS4xNDEuNDU2LS4zMTIuNjAyLS41MTMuMTQ2LS4yMDUuMjIzLS4zOTguMjMyLS41OGwuNTM0LjczYTIuNTAyIDIuNTAyIDAgMCAxLS4yOC42MDJjLS4xMzMuMjE0LS4zMDYuNDItLjUyLjYxNWEyLjU4NCAyLjU4NCAwIDAgMS0xLjc3LjY1NmMtLjQ4NCAwLS45MTQtLjA5NS0xLjI5My0uMjg2YTIuMjk4IDIuMjk4IDAgMCAxLS44ODgtLjc4NyAyLjAzOSAyLjAzOSAwIDAgMS0uMzIyLTEuMTI4YzAtLjM5MS4wNzMtLjczOC4yMi0xLjAzOC4xNS0uMzA2LjM2OC0uNTYxLjY1NS0uNzY2LjI5Mi0uMjA1LjY0Ny0uMzYgMS4wNjctLjQ2NS40Mi0uMTEuODk4LS4xNjQgMS40MzUtLjE2NGgxLjI5MlptNS4wNDEtMS40OVY0OWgtMS42NDd2LTcuMzk3aDEuNTcybC4wNzUgMS40MDlabTIuMjYzLTEuNDU2LS4wMTQgMS41M2E0LjI5MSA0LjI5MSAwIDAgMC0uNjctLjA1NWMtLjI4Mi4wMDEtLjUzLjA0Mi0uNzQ1LjEyNGExLjQwMyAxLjQwMyAwIDAgMC0uNTQuMzQyYy0uMTQxLjE0Ni0uMjUuMzI0LS4zMjguNTMzLS4wNzcuMjEtLjEyMy40NDUtLjEzNy43MDRsLS4zNzYuMDI4YzAtLjQ2NS4wNDYtLjg5Ni4xMzctMS4yOTIuMDkxLS4zOTcuMjI4LS43NDUuNDEtMS4wNDYuMTg3LS4zMDEuNDItLjUzNi42OTgtLjcwNGExLjg3IDEuODcgMCAwIDEgLjk3Ny0uMjUzYy4xIDAgLjIwOC4wMDkuMzIxLjAyNy4xMTkuMDE4LjIwOC4wMzkuMjY3LjA2MlptMS4yOS0zLjA1NmgxLjY0N3Y4LjkyTDQwLjczNiA0OWgtMS40OVYzOC41Wm02LjQ3MyA2LjczM3YuMTQ0YzAgLjU0Ny0uMDYyIDEuMDUtLjE4NSAxLjUxYTMuNDggMy40OCAwIDAgMS0uNTQ3IDEuMTljLS4yNDEuMzM3LS41NDIuNi0uOTAyLjc4Ni0uMzU2LjE4My0uNzY4LjI3NC0xLjIzNy4yNzQtLjQ2IDAtLjg2Mi0uMDg3LTEuMjAzLS4yNmEyLjM5MiAyLjM5MiAwIDAgMS0uODYyLS43MzggNC4wOTUgNC4wOTUgMCAwIDEtLjU1NC0xLjE0MiA3LjU0NCA3LjU0NCAwIDAgMS0uMy0xLjQ2M3YtLjQ1OGMuMDU5LS41MzguMTYtMS4wMjUuMy0xLjQ2My4xNDItLjQ0Mi4zMjYtLjgyMi41NTQtMS4xNDEuMjMzLS4zMjQuNTE3LS41NzIuODU1LS43NDUuMzQxLS4xNzQuNzQtLjI2IDEuMTk2LS4yNi40NzQgMCAuODkuMDkgMS4yNS4yNzMuMzY2LjE4Mi42NjkuNDQyLjkxLjc4LjI0Mi4zMzIuNDIyLjcyOS41NCAxLjE4OS4xMjMuNDYuMTg1Ljk2OC4xODUgMS41MjRabS0xLjY0OC4xNDR2LS4xNDRhNS4wOCA1LjA4IDAgMCAwLS4wODItLjkzNiAyLjQ2IDIuNDYgMCAwIDAtLjI3My0uNzggMS4zNyAxLjM3IDAgMCAwLS41MDYtLjUyNmMtLjIxLS4xMzItLjQ3Mi0uMTk4LS43ODYtLjE5OC0uMjkyIDAtLjU0My4wNS0uNzUyLjE1LS4yMS4xLS4zODUuMjM3LS41MjYuNDFhMi4wMyAyLjAzIDAgMCAwLS4zMzYuNjAyYy0uMDc3LjIyOC0uMTMuNDc0LS4xNTcuNzM4djEuMjM4Yy4wNDEuMzQyLjEyOC42NTYuMjYuOTQzLjEzNy4yODMuMzI4LjUxLjU3NC42ODQuMjQ2LjE2OC41NjMuMjUyLjk1LjI1Mi4zMDYgMCAuNTYzLS4wNjEuNzczLS4xODRhMS4zNSAxLjM1IDAgMCAwIC40OTktLjUxMyAyLjQ2IDIuNDYgMCAwIDAgLjI3My0uNzc5Yy4wNi0uMjk2LjA5LS42MTUuMDktLjk1N1ptMi45NzguMDA3di0uMTU3YzAtLjUzNC4wNzctMS4wMjguMjMyLTEuNDg0LjE1NS0uNDYuMzc4LS44NTkuNjctMS4xOTZhMy4wMjQgMy4wMjQgMCAwIDEgMS4wOC0uNzkzIDMuNTE2IDMuNTE2IDAgMCAxIDEuNDUtLjI4N2MuNTQyIDAgMS4wMjUuMDk2IDEuNDQ5LjI4Ny40MjguMTg3Ljc5LjQ1MSAxLjA4Ny43OTMuMjk2LjMzNy41MjEuNzM2LjY3NiAxLjE5Ni4xNTUuNDU2LjIzMy45NS4yMzMgMS40ODR2LjE1N2MwIC41MzMtLjA3OCAxLjAyNy0uMjMzIDEuNDgzYTMuNTUgMy41NSAwIDAgMS0uNjc2IDEuMTk3IDMuMTM3IDMuMTM3IDAgMCAxLTEuMDguNzkyIDMuNTQgMy41NCAwIDAgMS0xLjQ0My4yOCAzLjYwNyAzLjYwNyAwIDAgMS0xLjQ1Ni0uMjggMy4xMzYgMy4xMzYgMCAwIDEtMS4wOC0uNzkzIDMuNTUgMy41NSAwIDAgMS0uNjc3LTEuMTk2IDQuNTgyIDQuNTgyIDAgMCAxLS4yMzItMS40ODNabTEuNjQ3LS4xNTd2LjE1N2MwIC4zMzMuMDM0LjY0Ny4xMDMuOTQzLjA2OC4yOTYuMTc1LjU1Ni4zMjEuNzguMTQ2LjIyMy4zMzMuMzk4LjU2LjUyNi4yMjguMTI3LjUuMTkxLjgxNC4xOTEuMzA1IDAgLjU3LS4wNjQuNzkzLS4xOTEuMjI4LS4xMjguNDE1LS4zMDMuNTYtLjUyN2EyLjQgMi40IDAgMCAwIC4zMjItLjc3OWMuMDczLS4yOTYuMTEtLjYxLjExLS45NDN2LS4xNTdjMC0uMzI5LS4wMzctLjYzOC0uMTEtLjkzYTIuMzkgMi4zOSAwIDAgMC0uMzI4LS43ODYgMS41NyAxLjU3IDAgMCAwLS41Ni0uNTM0IDEuNTQzIDEuNTQzIDAgMCAwLS44LS4xOThjLS4zMSAwLS41OC4wNjYtLjgwNy4xOThhMS41ODcgMS41ODcgMCAwIDAtLjU1NC41MzRjLS4xNDYuMjI4LS4yNTMuNDktLjMyMS43ODZhNC4wNjIgNC4wNjIgMCAwIDAtLjEwMy45M1ptOC40OTQtMi4wNDRWNDloLTEuNjQ3di03LjM5N2gxLjU1MmwuMDk1IDEuNThabS0uMjk0IDEuODQ1LS41MzMtLjAwNmE0LjgxMiA0LjgxMiAwIDAgMSAuMjE5LTEuNDQzYy4xNDYtLjQzNy4zNDYtLjgxMy42MDEtMS4xMjguMjYtLjMxNC41Ny0uNTU2LjkzLS43MjRhMi43NCAyLjc0IDAgMCAxIDEuMjAzLS4yNmMuMzU2IDAgLjY3Ny4wNS45NjQuMTUuMjkyLjA5Ni41NC4yNTMuNzQ1LjQ3Mi4yMS4yMTkuMzcuNTAzLjQ3OS44NTQuMTEuMzQ3LjE2NC43NzMuMTY0IDEuMjc5VjQ5aC0xLjY1NHYtNC43ODVjMC0uMzU2LS4wNTMtLjYzNi0uMTU4LS44NDFhLjg4Ljg4IDAgMCAwLS40NDQtLjQ0NGMtLjE5MS0uMDkxLS40My0uMTM3LS43MTgtLjEzNy0uMjgyIDAtLjUzNS4wNi0uNzU4LjE3OC0uMjI0LjExOC0uNDEzLjI4LS41NjguNDg1LS4xNS4yMDUtLjI2Ny40NDItLjM0OC43MTFhMi45MzggMi45MzggMCAwIDAtLjEyNC44NjFabTEyLjA4Ny0xLjkyVjQ5aC0xLjY0N3YtNy4zOTdoMS41NTJsLjA5NSAxLjUwNFptLS4yNjYgMS45Mi0uNTYtLjAwNmMwLS41MS4wNjMtLjk4My4xOS0xLjQxNi4xMjgtLjQzMy4zMTUtLjgwOS41NjEtMS4xMjguMjQ2LS4zMjMuNTUyLS41NzEuOTE2LS43NDUuMzctLjE3Ny43OTUtLjI2NiAxLjI3OS0uMjY2LjMzNyAwIC42NDQuMDUuOTIyLjE1LjI4My4wOTYuNTI3LjI0OS43MzIuNDU4LjIxLjIxLjM2OS40NzkuNDc4LjgwNy4xMTQuMzI4LjE3MS43MjQuMTcxIDEuMTlWNDloLTEuNjQ3di00Ljc4NWMwLS4zNi0uMDU1LS42NDMtLjE2NC0uODQ4YS45MS45MSAwIDAgMC0uNDU4LS40MzcgMS42NTggMS42NTggMCAwIDAtLjcwNC0uMTM3Yy0uMzEgMC0uNTc1LjA2LS43OTMuMTc4LS4yMTUuMTE4LS4zOS4yOC0uNTI3LjQ4NWEyLjE3MyAyLjE3MyAwIDAgMC0uMy43MTEgMy43MjMgMy43MjMgMCAwIDAtLjA5Ni44NjFabTQuNTg3LS40MzctLjc3My4xN2MwLS40NDYuMDYyLS44NjcuMTg1LTEuMjY0LjEyNy0uNDAxLjMxMi0uNzUyLjU1My0xLjA1My4yNDctLjMwNS41NS0uNTQ0LjkxLS43MTdhMi44MiAyLjgyIDAgMCAxIDEuMjM3LS4yNmMuMzc4IDAgLjcxNS4wNTIgMS4wMTIuMTU3LjMuMS41NTYuMjYuNzY1LjQ3OC4yMS4yMi4zNy41MDQuNDc5Ljg1NS4xMS4zNDYuMTY0Ljc2Ni4xNjQgMS4yNThWNDloLTEuNjU0di00Ljc5MmMwLS4zNzQtLjA1NS0uNjYzLS4xNjUtLjg2OGEuODUuODUgMCAwIDAtLjQ1LS40MjQgMS44MiAxLjgyIDAgMCAwLS43MDUtLjEyM2MtLjI1NSAwLS40OC4wNDgtLjY3Ny4xNDRhMS4zNDQgMS4zNDQgMCAwIDAtLjQ4NS4zOSAxLjczNSAxLjczNSAwIDAgMC0uMy41NjcgMi40MzIgMi40MzIgMCAwIDAtLjA5Ni42OTdabTYuMTYzLjc5M3YtLjE1N2MwLS41MzQuMDc3LTEuMDI4LjIzMi0xLjQ4NC4xNTUtLjQ2LjM3OC0uODU5LjY3LTEuMTk2YTMuMDIzIDMuMDIzIDAgMCAxIDEuMDgtLjc5MyAzLjUxNiAzLjUxNiAwIDAgMSAxLjQ1LS4yODdjLjU0MiAwIDEuMDI1LjA5NiAxLjQ0OS4yODcuNDI4LjE4Ny43OS40NTEgMS4wODcuNzkzLjI5Ni4zMzcuNTIxLjczNi42NzYgMS4xOTYuMTU1LjQ1Ni4yMzMuOTUuMjMzIDEuNDg0di4xNTdjMCAuNTMzLS4wNzggMS4wMjctLjIzMyAxLjQ4My0uMTU1LjQ1Ni0uMzguODU1LS42NzcgMS4xOTdhMy4xMzUgMy4xMzUgMCAwIDEtMS4wOC43OTIgMy41NCAzLjU0IDAgMCAxLTEuNDQyLjI4IDMuNjA3IDMuNjA3IDAgMCAxLTEuNDU2LS4yOCAzLjEzNyAzLjEzNyAwIDAgMS0xLjA4LS43OTMgMy41NSAzLjU1IDAgMCAxLS42NzctMS4xOTYgNC41NzkgNC41NzkgMCAwIDEtLjIzMi0xLjQ4M1ptMS42NDctLjE1N3YuMTU3YzAgLjMzMy4wMzQuNjQ3LjEwMy45NDMuMDY4LjI5Ni4xNzUuNTU2LjMyMS43OC4xNDYuMjIzLjMzMy4zOTguNTYuNTI2LjIyOS4xMjcuNS4xOTEuODE0LjE5MS4zMDUgMCAuNTctLjA2NC43OTMtLjE5MS4yMjgtLjEyOC40MTUtLjMwMy41Ni0uNTI3YTIuNCAyLjQgMCAwIDAgLjMyMi0uNzc5Yy4wNzMtLjI5Ni4xMS0uNjEuMTEtLjk0M3YtLjE1N2MwLS4zMjktLjAzNy0uNjM4LS4xMS0uOTNhMi4zOSAyLjM5IDAgMCAwLS4zMjgtLjc4NiAxLjU2OCAxLjU2OCAwIDAgMC0uNTYtLjUzNCAxLjU0MyAxLjU0MyAwIDAgMC0uOC0uMTk4Yy0uMzEgMC0uNTguMDY2LS44MDcuMTk4YTEuNTg4IDEuNTg4IDAgMCAwLS41NTQuNTM0IDIuNDggMi40OCAwIDAgMC0uMzIxLjc4NiA0LjA2MiA0LjA2MiAwIDAgMC0uMTAzLjkzWm04LjQ5NC0yLjA0NFY0OWgtMS42NDd2LTcuMzk3aDEuNTUybC4wOTUgMS41OFptLS4yOTQgMS44NDUtLjUzMy0uMDA2YTQuODEyIDQuODEyIDAgMCAxIC4yMTktMS40NDNjLjE0Ni0uNDM3LjM0Ni0uODEzLjYwMi0xLjEyOC4yNi0uMzE0LjU3LS41NTYuOTMtLjcyNGEyLjc0IDIuNzQgMCAwIDEgMS4yMDItLjI2Yy4zNTYgMCAuNjc3LjA1Ljk2NC4xNS4yOTIuMDk2LjU0LjI1My43NDUuNDcyLjIxLjIxOS4zNy41MDMuNDc5Ljg1NC4xMS4zNDcuMTY0Ljc3My4xNjQgMS4yNzlWNDloLTEuNjU0di00Ljc4NWMwLS4zNTYtLjA1My0uNjM2LS4xNTgtLjg0MWEuODguODggMCAwIDAtLjQ0NC0uNDQ0Yy0uMTkxLS4wOTEtLjQzLS4xMzctLjcxOC0uMTM3LS4yODIgMC0uNTM1LjA2LS43NTguMTc4LS4yMjQuMTE4LS40MTMuMjgtLjU2OC40ODUtLjE1LjIwNS0uMjY2LjQ0Mi0uMzQ5LjcxMWEyLjkzNSAyLjkzNSAwIDAgMC0uMTIzLjg2MVptNi4zOS4zNTZ2LS4xNTdjMC0uNTM0LjA3Ny0xLjAyOC4yMzEtMS40ODQuMTU1LS40Ni4zNzktLjg1OS42Ny0xLjE5NmEzLjAyNCAzLjAyNCAwIDAgMSAxLjA4LS43OTMgMy41MTYgMy41MTYgMCAwIDEgMS40NS0uMjg3Yy41NDIgMCAxLjAyNS4wOTYgMS40NDkuMjg3LjQyOC4xODcuNzkxLjQ1MSAxLjA4Ny43OTMuMjk2LjMzNy41MjIuNzM2LjY3NyAxLjE5Ni4xNTUuNDU2LjIzMi45NS4yMzIgMS40ODR2LjE1N2MwIC41MzMtLjA3NyAxLjAyNy0uMjMyIDEuNDgzYTMuNTU1IDMuNTU1IDAgMCAxLS42NzcgMS4xOTcgMy4xMzIgMy4xMzIgMCAwIDEtMS4wOC43OTJjLS40MjQuMTg3LS45MDUuMjgtMS40NDMuMjhhMy42MDcgMy42MDcgMCAwIDEtMS40NTYtLjI4IDMuMTM2IDMuMTM2IDAgMCAxLTEuMDgtLjc5MyAzLjU0OCAzLjU0OCAwIDAgMS0uNjc3LTEuMTk2IDQuNTgyIDQuNTgyIDAgMCAxLS4yMzItMS40ODNabTEuNjQ3LS4xNTd2LjE1N2MwIC4zMzMuMDM0LjY0Ny4xMDIuOTQzLjA2OC4yOTYuMTc1LjU1Ni4zMjEuNzguMTQ2LjIyMy4zMzMuMzk4LjU2LjUyNi4yMjkuMTI3LjUuMTkxLjgxNC4xOTEuMzA2IDAgLjU3LS4wNjQuNzkzLS4xOTEuMjI4LS4xMjguNDE1LS4zMDMuNTYxLS41MjcuMTQ2LS4yMjMuMjUzLS40ODMuMzIxLS43NzkuMDczLS4yOTYuMTEtLjYxLjExLS45NDN2LS4xNTdjMC0uMzI5LS4wMzctLjYzOC0uMTEtLjkzYTIuMzkgMi4zOSAwIDAgMC0uMzI4LS43ODYgMS41NzMgMS41NzMgMCAwIDAtLjU2LS41MzQgMS41NDMgMS41NDMgMCAwIDAtLjgtLjE5OGMtLjMxIDAtLjU4LjA2Ni0uODA3LjE5OGExLjU4NyAxLjU4NyAwIDAgMC0uNTU0LjUzNGMtLjE0NS4yMjgtLjI1My40OS0uMzIuNzg2YTQuMDYyIDQuMDYyIDAgMCAwLS4xMDMuOTNabTcuOTY3LTMuNjIzIDEuNDA5IDIuNDU0IDEuNDM1LTIuNDU1aDEuODEybC0yLjI0MyAzLjYzTDExMC4wNjQgNDloLTEuODExbC0xLjUwNC0yLjU1N0wxMDUuMjQ1IDQ5aC0xLjgxOGwyLjMyNC0zLjc2Ny0yLjIzNS0zLjYzaDEuODA0Wm03Ljc5NyAwVjQ5aC0xLjY1NHYtNy4zOTdoMS42NTRabS0xLjc2My0xLjk0MmMwLS4yNS4wODItLjQ1OC4yNDYtLjYyMi4xNjgtLjE2OS40MDEtLjI1My42OTctLjI1My4yOTIgMCAuNTIyLjA4NC42OS4yNTNhLjgzLjgzIDAgMCAxIC4yNTMuNjIyLjgyMi44MjIgMCAwIDEtLjI1My42MTVjLS4xNjguMTY0LS4zOTguMjQ2LS42OS4yNDYtLjI5NiAwLS41MjktLjA4Mi0uNjk3LS4yNDZhLjgzNS44MzUgMCAwIDEtLjI0Ni0uNjE1Wm04LjMzIDcuODA3VjM4LjVoMS42NTRWNDloLTEuNDk3bC0uMTU3LTEuNTMxWm0tNC44MTMtMi4wODV2LS4xNDRjMC0uNTYuMDY2LTEuMDcuMTk4LTEuNTMxLjEzMi0uNDY1LjMyNC0uODY0LjU3NS0xLjE5Ni4yNS0uMzM3LjU1Ni0uNTk1LjkxNi0uNzczLjM2LS4xODIuNzY1LS4yNzMgMS4yMTYtLjI3My40NDcgMCAuODM5LjA4NiAxLjE3Ni4yNi4zMzcuMTczLjYyNC40MjEuODYxLjc0NS4yMzcuMzE5LjQyNy43MDEuNTY4IDEuMTQ4LjE0MS40NDIuMjQxLjkzNC4zMDEgMS40Nzd2LjQ1OGE3LjQ3MyA3LjQ3MyAwIDAgMS0uMzAxIDEuNDQ5IDMuODg2IDMuODg2IDAgMCAxLS41NjggMS4xMzUgMi40NSAyLjQ1IDAgMCAxLS44NjguNzM4Yy0uMzM3LjE3My0uNzMxLjI2LTEuMTgyLjI2LS40NDcgMC0uODUtLjA5NC0xLjIxLS4yOGEyLjY2MiAyLjY2MiAwIDAgMS0uOTA5LS43ODcgMy42OTIgMy42OTIgMCAwIDEtLjU3NS0xLjE5IDUuNDEzIDUuNDEzIDAgMCAxLS4xOTgtMS40OTZabTEuNjQ4LS4xNDR2LjE0NGMwIC4zMzcuMDI5LjY1MS4wODguOTQzLjA2NC4yOTIuMTYyLjU1LjI5NC43NzMuMTMzLjIxOC4zMDMuMzkyLjUxMy41Mi4yMTQuMTIyLjQ2OS4xODQuNzY2LjE4NC4zNzMgMCAuNjgxLS4wODIuOTIzLS4yNDYuMjQxLS4xNjQuNDMtLjM4Ni41NjctLjY2My4xNDEtLjI4My4yMzctLjU5OC4yODctLjk0NHYtMS4yMzdhMi45ODggMi45ODggMCAwIDAtLjE3MS0uNzUyIDIuMDE4IDIuMDE4IDAgMCAwLS4zMzUtLjYwOSAxLjQxMyAxLjQxMyAwIDAgMC0uNTI2LS40MSAxLjY1IDEuNjUgMCAwIDAtLjczMi0uMTVjLS4zIDAtLjU1Ni4wNjQtLjc2NS4xOTEtLjIxLjEyOC0uMzgzLjMwMy0uNTIuNTI3YTIuNiAyLjYgMCAwIDAtLjI5NC43NzkgNC40OTYgNC40OTYgMCAwIDAtLjA5NS45NVptMTAuMDUyIDMuODk3YTMuOTQxIDMuOTQxIDAgMCAxLTEuNDgzLS4yNjcgMy4yNyAzLjI3IDAgMCAxLTEuMTIxLS43NTkgMy4zNTIgMy4zNTIgMCAwIDEtLjcwNC0xLjE0MSAzLjk5OSAzLjk5OSAwIDAgMS0uMjQ2LTEuNDE1di0uMjc0YzAtLjU3OC4wODQtMS4xMDMuMjUzLTEuNTcyLjE2OC0uNDcuNDAzLS44Ny43MDQtMS4yMDMuMy0uMzM3LjY1Ni0uNTk1IDEuMDY2LS43NzMuNDEtLjE3Ny44NTUtLjI2NiAxLjMzMy0uMjY2LjUyOSAwIC45OTEuMDg5IDEuMzg4LjI2Ni4zOTYuMTc4LjcyNC40MjkuOTg0Ljc1Mi4yNjQuMzIuNDYuNy41ODggMS4xNDIuMTMyLjQ0Mi4xOTguOTMuMTk4IDEuNDYzdi43MDRoLTUuNzE1VjQ0LjYxaDQuMDg4di0uMTNhMi4zMTggMi4zMTggMCAwIDAtLjE3Ny0uODM0IDEuNDE4IDEuNDE4IDAgMCAwLS40ODYtLjYyOGMtLjIxOS0uMTYtLjUxLS4yNC0uODc1LS4yNC0uMjczIDAtLjUxNy4wNi0uNzMxLjE3OC0uMjEuMTE0LS4zODUuMjgtLjUyNy41LS4xNDEuMjE4LS4yNS40ODItLjMyOC43OTItLjA3My4zMDUtLjEwOS42NS0uMTA5IDEuMDMydi4yNzRjMCAuMzIzLjA0My42MjQuMTMuOTAyLjA5MS4yNzMuMjIzLjUxMy4zOTYuNzE4LjE3My4yMDUuMzgzLjM2Ny42MjkuNDg1LjI0Ni4xMTQuNTI3LjE3MS44NDEuMTcxLjM5NiAwIC43NS0uMDggMS4wNi0uMjQuMzA5LS4xNTkuNTc4LS4zODQuODA2LS42NzZsLjg2OC44NGEzLjM0NyAzLjM0NyAwIDAgMS0uNjIyLjY3Yy0uMjU1LjIxLS41NjcuMzgxLS45MzYuNTE0YTMuNzMyIDMuNzMyIDAgMCAxLTEuMjcyLjE5OFoiLz48cGF0aCBmaWxsPSIjREUyMzQzIiBkPSJNMjcuNzMgOTUuOTdoLjQxdjIuNjk0aC0uMjMyYy0xLjE3NiAwLTIuMTkyLjE4Mi0zLjA0OS41NDctLjg0Ny4zNjUtMS41NDUuODY2LTIuMDkxIDEuNTA0YTYuMzggNi4zOCAwIDAgMC0xLjIzIDIuMjQyIDkuMDQ2IDkuMDQ2IDAgMCAwLS4zOTcgMi43MDd2My4xMzFjMCAuNzkzLjA4NiAxLjQ5NS4yNiAyLjEwNS4xNzMuNjAyLjQxNCAxLjEwOC43MjQgMS41MTguMzE5LjQwMS42ODQuNzA2IDEuMDk0LjkxNi40MS4yMS44NTIuMzE0IDEuMzI2LjMxNC40OTIgMCAuOTM5LS4xIDEuMzQtLjMuNC0uMjEuNzQzLS40OTcgMS4wMjUtLjg2Mi4yODMtLjM2NC40OTctLjc5Ny42NDMtMS4yOThhNS44NiA1Ljg2IDAgMCAwIC4yMTgtMS42NDFjMC0uNTY1LS4wNzItMS4wOTQtLjIxOC0xLjU4NmEzLjk3NCAzLjk3NCAwIDAgMC0uNjE2LTEuMzEzIDIuODI1IDIuODI1IDAgMCAwLTEuMDI1LS44ODhjLS40LS4yMTktLjg2Ni0uMzI4LTEuMzk0LS4zMjhhMy41MyAzLjUzIDAgMCAwLTEuNzkxLjQ2NGMtLjUzLjMxLS45NTcuNzE2LTEuMjg2IDEuMjE3YTMuMTM0IDMuMTM0IDAgMCAwLS41MiAxLjU3M2wtMS4yNTctLjQxMWMuMDczLS44NDcuMjYtMS42MDguNTYtMi4yODNhNS40OTEgNS40OTEgMCAwIDEgMS4yMTctMS43MjIgNS4xMyA1LjEzIDAgMCAxIDEuNzM3LTEuMDgxIDYuMDY1IDYuMDY1IDAgMCAxIDIuMTg3LS4zODJjLjk2NiAwIDEuODA1LjE4MiAyLjUxNi41NDcuNzEuMzY0IDEuMjk5Ljg2MSAxLjc2NCAxLjQ5LjQ3NC42Mi44MjQgMS4zMyAxLjA1MiAyLjEzMy4yMzcuNzkzLjM1NiAxLjYyMi4zNTYgMi40ODggMCAuOTU3LS4xNDYgMS44NS0uNDM4IDIuNjgtLjI5MS44Mi0uNzIgMS41NC0xLjI4NSAyLjE2YTUuNzczIDUuNzczIDAgMCAxLTIuMDM3IDEuNDQ5Yy0uNzkzLjM0Ni0xLjY5NS41MTktMi43MDcuNTE5LTEuMDY2IDAtMi4wMTktLjIwNS0yLjg1Ny0uNjE1YTYuMjc2IDYuMjc2IDAgMCAxLTIuMTItMS42OTUgNy43NCA3Ljc0IDAgMCAxLTEuMzEyLTIuNDg4IDkuNzE1IDkuNzE1IDAgMCAxLS40NTEtMi45ODF2LTEuMzY3YzAtMS41MTMuMTkxLTIuOTM5LjU3NC00LjI3OS4zODMtMS4zNDkuOTctMi41MzkgMS43NjQtMy41NjkuODAyLTEuMDMgMS44MjctMS44MzYgMy4wNzYtMi40MiAxLjI0OC0uNTkyIDIuNzM5LS44ODggNC40Ny0uODg4Wm0xOS4yMSA4LjQwOXYzLjI1NGMwIDEuNTU4LS4xNTUgMi44ODktLjQ2NSAzLjk5Mi0uMzAxIDEuMDk0LS43MzkgMS45ODItMS4zMTMgMi42NjZhNS4xNDcgNS4xNDcgMCAwIDEtMi4wNjQgMS41MDRjLS43OTMuMzE5LTEuNjgyLjQ3OC0yLjY2Ni40NzgtLjc4NCAwLTEuNTEzLS4xLTIuMTg4LS4zYTUuMzQzIDUuMzQzIDAgMCAxLTEuODA0LS45NDRjLS41MzgtLjQyOC0uOTk5LS45OC0xLjM4MS0xLjY1NC0uMzc0LS42ODQtLjY2Ni0xLjQ5OS0uODc1LTIuNDQ3LS4yLS45NDgtLjMwMS0yLjA0Ni0uMzAxLTMuMjk1di0zLjI1NGMwLTEuNTY4LjE1NS0yLjg4OS40NjUtMy45NjUuMzEtMS4wODUuNzUyLTEuOTY0IDEuMzI2LTIuNjM5YTUuMDUxIDUuMDUxIDAgMCAxIDIuMDUtMS40OWMuODAzLS4zMSAxLjY5Ni0uNDY1IDIuNjgtLjQ2NS43OTMgMCAxLjUyMi4xIDIuMTg4LjMwMWE1LjEyMiA1LjEyMiAwIDAgMSAxLjgwNS45MTZjLjUzNy40Mi45OTMuOTY2IDEuMzY3IDEuNjQuMzgzLjY2Ni42NzQgMS40NzMuODc1IDIuNDIxLjIuOTM4LjMgMi4wMzIuMyAzLjI4MVptLTMuMjk1IDMuNzE5di00LjIxMWMwLS43OTMtLjA0Ni0xLjQ5MS0uMTM3LTIuMDkyLS4wOTEtLjYxMS0uMjI4LTEuMTI2LS40MS0xLjU0NS0uMTczLS40MjgtLjM5Mi0uNzc1LS42NTctMS4wNGEyLjMxIDIuMzEgMCAwIDAtLjkwMi0uNTg3IDMuMTg3IDMuMTg3IDAgMCAwLTEuMTM1LS4xOTFjLS41MSAwLS45NjYuMS0xLjM2Ny4zLS40LjE5Mi0uNzM4LjUwMi0xLjAxMi45My0uMjczLjQyOC0uNDgzLjk5NC0uNjI4IDEuNjk1LS4xMzcuNjkzLS4yMDYgMS41MzYtLjIwNiAyLjUzdjQuMjExYzAgLjgwMi4wNDYgMS41MDguMTM3IDIuMTE5LjA5MS42MS4yMjggMS4xMzUuNDEgMS41NzIuMTgzLjQyOC40MDEuNzg0LjY1NyAxLjA2Ni4yNjQuMjc0LjU2NS40NzQuOTAyLjYwMmEzLjI1IDMuMjUgMCAwIDAgMS4xMzUuMTkxYy41MiAwIC45OC0uMSAxLjM4LS4zLjQwMi0uMjAxLjczOS0uNTIgMS4wMTItLjk1Ny4yNzQtLjQ0Ny40NzktMS4wMjYuNjE2LTEuNzM3LjEzNi0uNzExLjIwNS0xLjU2My4yMDUtMi41NTZaTTU2LjI3NyAxMDUuODk4VjExNmgtMi44MjR2LTEyLjY4aDIuNjZsLjE2NCAyLjU3OFptLS40NTcgMy4yOTMtLjk2LS4wMTFjMC0uODc1LjEwOS0xLjY4NC4zMjgtMi40MjYuMjE4LS43NDIuNTM5LTEuMzg3Ljk2LTEuOTM0YTQuMjMgNC4yMyAwIDAgMSAxLjU3LTEuMjc3Yy42MzQtLjMwNSAxLjM2NC0uNDU3IDIuMTkyLS40NTcuNTc4IDAgMS4xMDYuMDg2IDEuNTgyLjI1OC40ODUuMTY0LjkwMy40MjYgMS4yNTQuNzg1LjM2LjM1OS42MzMuODIuODIgMS4zODMuMTk2LjU2Mi4yOTMgMS4yNDIuMjkzIDIuMDM5VjExNmgtMi44MjR2LTguMjAzYzAtLjYxNy0uMDk0LTEuMTAyLS4yODEtMS40NTNhMS41NTUgMS41NTUgMCAwIDAtLjc4NS0uNzVjLS4zMzYtLjE1Ni0uNzM5LS4yMzUtMS4yMDctLjIzNS0uNTMyIDAtLjk4NS4xMDItMS4zNi4zMDVhMi41NjQgMi41NjQgMCAwIDAtLjkwMi44MzIgMy43MzIgMy43MzIgMCAwIDAtLjUxNiAxLjIxOWMtLjEwOS40NjEtLjE2NC45NTMtLjE2NCAxLjQ3NlptNy44NjQtLjc1LTEuMzI1LjI5M2MwLS43NjUuMTA2LTEuNDg4LjMxNy0yLjE2OGE1LjUyIDUuNTIgMCAwIDEgLjk0OS0xLjgwNCA0LjQ1IDQuNDUgMCAwIDEgMS41NTktMS4yMzFjLjYxNy0uMjk3IDEuMzI0LS40NDUgMi4xMi0uNDQ1LjY1IDAgMS4yMjcuMDkgMS43MzUuMjY5LjUxNi4xNzIuOTUzLjQ0NiAxLjMxMy44MjEuMzU5LjM3NS42MzIuODYzLjgyIDEuNDY1LjE4Ny41OTMuMjgxIDEuMzEyLjI4MSAyLjE1NlYxMTZoLTIuODM2di04LjIxNWMwLS42NC0uMDk0LTEuMTM3LS4yODEtMS40ODhhMS40NTggMS40NTggMCAwIDAtLjc3My0uNzI3Yy0uMzM2LS4xNC0uNzM5LS4yMTEtMS4yMDctLjIxMS0uNDM4IDAtLjgyNS4wODItMS4xNi4yNDZhMi4zMSAyLjMxIDAgMCAwLS44MzMuNjY4IDIuOTg1IDIuOTg1IDAgMCAwLS41MTUuOTczIDQuMTcgNC4xNyAwIDAgMC0uMTY0IDEuMTk1Wm0xOC43OTctNS4xMjFoMi41NjZ2MTIuMzI4YzAgMS4xNDEtLjI0MiAyLjExLS43MjcgMi45MDdhNC42NjggNC42NjggMCAwIDEtMi4wMjcgMS44MTZjLS44NjcuNDIyLTEuODcxLjYzMy0zLjAxMi42MzMtLjQ4NCAwLTEuMDIzLS4wNy0xLjYxNy0uMjExYTYuNjIzIDYuNjIzIDAgMCAxLTEuNzEtLjY4IDQuMjM0IDQuMjM0IDAgMCAxLTEuMzcyLTEuMjA3bDEuMzI0LTEuNjY0Yy40NTMuNTM5Ljk1My45MzQgMS41IDEuMTg0LjU0Ny4yNSAxLjEyMS4zNzUgMS43MjMuMzc1LjY0OCAwIDEuMi0uMTIxIDEuNjUyLS4zNjNhMi40NjMgMi40NjMgMCAwIDAgMS4wNjctMS4wNDNjLjI1LS40NjEuMzc1LTEuMDI0LjM3NS0xLjY4OHYtOS41MTZsLjI1OC0yLjg3MVptLTguNjE0IDYuNDgxdi0uMjQ2YzAtLjk2MS4xMTctMS44MzYuMzUyLTIuNjI1LjIzNC0uNzk3LjU3LTEuNDgxIDEuMDA4LTIuMDUxYTQuMzkgNC4zOSAwIDAgMSAxLjU5My0xLjMyNGMuNjI1LS4zMTMgMS4zMzItLjQ2OSAyLjEyMS0uNDY5LjgyIDAgMS41Mi4xNDggMi4wOTguNDQ1YTMuOTYyIDMuOTYyIDAgMCAxIDEuNDY1IDEuMjc4Yy4zOS41NDYuNjk1IDEuMjAzLjkxNCAxLjk2OC4yMjYuNzU4LjM5NCAxLjYwMi41MDQgMi41MzJ2Ljc4NWExMi42ODEgMTIuNjgxIDAgMCAxLS41MTYgMi40ODRjLS4yNDIuNzUtLjU2MiAxLjM5OS0uOTYgMS45NDUtLjQuNTQ3LS44OTEuOTY5LTEuNDc3IDEuMjY2LS41NzguMjk3LTEuMjYyLjQ0NS0yLjA1MS40NDUtLjc3NCAwLTEuNDczLS4xNi0yLjA5OC0uNDhhNC43NzIgNC43NzIgMCAwIDEtMS41OTMtMS4zNDggNi4zODIgNi4zODIgMCAwIDEtMS4wMDgtMi4wMzkgOC45NzMgOC45NzMgMCAwIDEtLjM1Mi0yLjU2NlptMi44MjQtLjI0NnYuMjQ2YzAgLjU3OC4wNTUgMS4xMTcuMTY1IDEuNjE3LjExNy41LjI5Mi45NDEuNTI3IDEuMzI0LjI0Mi4zNzUuNTQ3LjY3Mi45MTQuODkxLjM3NS4yMTEuODE2LjMxNiAxLjMyNC4zMTYuNjY0IDAgMS4yMDctLjE0IDEuNjI5LS40MjIuNDMtLjI4MS43NTgtLjY2Ljk4NC0xLjEzNmE1Ljc2MiA1Ljc2MiAwIDAgMCAuNDkzLTEuNjE4di0yLjEyMWE1LjE4NiA1LjE4NiAwIDAgMC0uMjkzLTEuMjg5IDMuNDU1IDMuNDU1IDAgMCAwLS41NzUtMS4wNDMgMi40MDMgMi40MDMgMCAwIDAtLjkxNC0uNzAzYy0uMzY3LS4xNzItLjgtLjI1OC0xLjMtLjI1OC0uNTA4IDAtLjk1LjExLTEuMzI1LjMyOWEyLjY4IDIuNjggMCAwIDAtLjkyNS45MDJjLS4yMzUuMzgzLS40MS44MjgtLjUyOCAxLjMzNmE3LjIzMyA3LjIzMyAwIDAgMC0uMTc2IDEuNjI5Wm0xOC43MDMtMTAuNjE4LTYuNjQ0IDE4LjUyOGgtMi4yMTVsNi42NTYtMTguNTI4aDIuMjAzWm00Ljc4MiA2Ljk2MVYxMTZoLTIuODI0di0xMi42OGgyLjY2bC4xNjQgMi41NzhabS0uNDU3IDMuMjkzLS45NjEtLjAxMWMwLS44NzUuMTEtMS42ODQuMzI4LTIuNDI2YTUuODMgNS44MyAwIDAgMSAuOTYxLTEuOTM0IDQuMjMgNC4yMyAwIDAgMSAxLjU3LTEuMjc3Yy42MzMtLjMwNSAxLjM2My0uNDU3IDIuMTkyLS40NTcuNTc4IDAgMS4xMDUuMDg2IDEuNTgyLjI1OC40ODQuMTY0LjkwMi40MjYgMS4yNTQuNzg1LjM1OS4zNTkuNjMyLjgyLjgyIDEuMzgzLjE5NS41NjIuMjkzIDEuMjQyLjI5MyAyLjAzOVYxMTZoLTIuODI0di04LjIwM2MwLS42MTctLjA5NC0xLjEwMi0uMjgyLTEuNDUzYTEuNTUxIDEuNTUxIDAgMCAwLS43ODUtLjc1Yy0uMzM2LS4xNTYtLjczOC0uMjM1LTEuMjA3LS4yMzUtLjUzMSAwLS45ODQuMTAyLTEuMzU5LjMwNWEyLjU3MiAyLjU3MiAwIDAgMC0uOTAzLjgzMiAzLjc0IDMuNzQgMCAwIDAtLjUxNSAxLjIxOWMtLjExLjQ2MS0uMTY0Ljk1My0uMTY0IDEuNDc2Wm03Ljg2My0uNzUtMS4zMjQuMjkzYzAtLjc2NS4xMDUtMS40ODguMzE2LTIuMTY4YTUuNTI2IDUuNTI2IDAgMCAxIC45NDktMS44MDQgNC40NTUgNC40NTUgMCAwIDEgMS41NTktMS4yMzFjLjYxNy0uMjk3IDEuMzI0LS40NDUgMi4xMjEtLjQ0NS42NDkgMCAxLjIyNy4wOSAxLjczNS4yNjlhMy4xOCAzLjE4IDAgMCAxIDEuMzEyLjgyMWMuMzU5LjM3NS42MzMuODYzLjgyIDEuNDY1LjE4OC41OTMuMjgyIDEuMzEyLjI4MiAyLjE1NlYxMTZoLTIuODM2di04LjIxNWMwLS42NC0uMDk0LTEuMTM3LS4yODItMS40ODhhMS40NTUgMS40NTUgMCAwIDAtLjc3My0uNzI3Yy0uMzM2LS4xNC0uNzM4LS4yMTEtMS4yMDctLjIxMWEyLjYxIDIuNjEgMCAwIDAtMS4xNi4yNDYgMi4zMDYgMi4zMDYgMCAwIDAtLjgzMi42NjggMi45NzYgMi45NzYgMCAwIDAtLjUxNi45NzMgNC4xODQgNC4xODQgMCAwIDAtLjE2NCAxLjE5NVptMTIuMzg3LTUuNzA3aDEuMDE5Yy4zMzYgMCAuNjE0LS4wNDYuODMyLS4xNC4yMTktLjEwMi4zODMtLjIzOS40OTItLjQxYTEuMDUgMS4wNSAwIDAgMCAuMTc2LS42MS45OTQuOTk0IDAgMCAwLS4zMjgtLjczOGMtLjIxMS0uMjE5LS41NjYtLjMyOC0xLjA2Ni0uMzI4LS4zODMgMC0uNzA3LjA4Mi0uOTczLjI0Ni0uMjU4LjE1Ni0uMzg3LjM3OS0uMzg3LjY2OGgtMi4xMzJjMC0uNTU1LjE1Ni0xLjAyNy40NjgtMS40MTguMzEzLS4zOS43MzEtLjY4OCAxLjI1NC0uODlhNC42OTMgNC42OTMgMCAwIDEgMS43MTEtLjMwNWMuNzExIDAgMS4zMzYuMTAxIDEuODc1LjMwNC41MzkuMTk2Ljk1Ny40ODkgMS4yNTQuODguMzA1LjM5LjQ1Ny44NzQuNDU3IDEuNDUyIDAgLjQ0Ni0uMTQxLjg0LS40MjIgMS4xODQtLjI3My4zNDQtLjY1Ni42MTMtMS4xNDguODA5LS40ODUuMTg3LTEuMDU1LjI4MS0xLjcxMS4yODFoLTEuMzcxdi0uOTg1Wm0wIDEuNDY1di0uOTYxaDEuMzcxYy43MjYgMCAxLjM0OC4wODYgMS44NjMuMjU4LjUxNi4xNjQuOTA2LjQxOCAxLjE3Mi43NjIuMjczLjM0NC40MS43ODkuNDEgMS4zMzYgMCAuNTc4LS4xNjQgMS4wNy0uNDkyIDEuNDc2LS4zMjguNDA3LS43NzcuNzE1LTEuMzQ4LjkyNi0uNTYyLjIxMS0xLjE5NS4zMTYtMS44OTguMzE2YTUuMjk1IDUuMjk1IDAgMCAxLTEuNzExLS4yODFjLS41NDctLjE5NS0xLS41LTEuMzU5LS45MTQtLjM1Mi0uNDE0LS41MjgtLjk1Ny0uNTI4LTEuNjI5aDIuMTQ1YzAgLjMwNS4xNC41NjcuNDIyLjc4NS4yODEuMjE5LjY2LjMyOSAxLjEzNi4zMjkuNTA4IDAgLjg4My0uMTEgMS4xMjUtLjMyOWExLjA5IDEuMDkgMCAwIDAgLjM3NS0uODQzYzAtLjI5Ny0uMDctLjUzNS0uMjExLS43MTVhMS4wODcgMS4wODcgMCAwIDAtLjU3NC0uMzg3IDIuNzA1IDIuNzA1IDAgMCAwLS44NzktLjEyOWgtMS4wMTlaIi8+PHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjg3IiBzdHJva2Utd2lkdGg9IjEuNSIgZD0iTTE4NyA1NGMtNy42OTEgMC02Ljc3OSA1Ni4xNDktMTQuNzc4IDU2LjE0OS02Ljc2OCAwLTcuODUzLTIzLjg4LTEzLjA4My0yMy44OC01LjIyOSAwLTcuNTI4IDMyLjczMS0xNC4yOTYgMzIuNzMxLTUuNTM3IDAtOC42MTMtNi42MzgtMTMuODQzLTYuNjM4Ii8+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHBhdHRlcm5Db250ZW50VW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48dXNlIHhsaW5rOmhyZWY9IiNjIiB0cmFuc2Zvcm09Im1hdHJpeCguMDA1MDMgMCAwIC4wMDg5NyAwIC0uMjEzKSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMTk3LjAyMyIgaGVpZ2h0PSIxMDkuMDIzIiB4PSIxLjQ4OSIgeT0iMjUuNDg5IiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIyLjI1NiIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMTA5XzQ5ODkiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzExMDlfNDk4OSIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFJQkFRRUJBUUlCQVFFQ0FnSUNBZ1FEQWdJQ0FnVUVCQU1FQmdVR0JnWUZCZ1lHQndrSUJnY0pCd1lHQ0FzSUNRb0tDZ29LQmdnTERBc0tEQWtLQ2dyLzJ3QkRBUUlDQWdJQ0FnVURBd1VLQndZSENnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ3Ivd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhRQUJBUUFEQVFFQkFRRUFBQUFBQUFBQUF3UUNCUVlCQndBSUNmL0VBQnNCQUFNQkFRRUJBUUFBQUFBQUFBQUFBQUVDQXdBRUJnVUgvOW9BREFNQkFBSVFBeEFBQUFEL0FIMDRxWWJlWTRBSHNPd2tURUJnR0FIRVFXQXVRZkN3SjFGa0dpQzA4Q3BrR1ZNaWVqZmZmTi9ad0RHU0FJbkJzTGFmVERBQ0FLenVvNVRJQnlMNDJKc0RLQTZrNllPdURLUkJaUHZYbVB1am1BR2NrTUp5Z1lBY0IwN2dLZ25tQlF6c2cyT3dVWEFrSFRBZ3lDZFJxaHVoTW4zL0FNajk0UTAyY3d5RFRocFRwWEVUVEY4RExpV0JreURZc0twV3lCMWZSenkxa1Ridzc5aEhXWG1QaEgyanpIMU1RWnhRbGNTM294WWcybGNRVVhFNk9xQTZmc2ZOcDNIcFg4VGlRWkdKeGxSWllMUWt0ei9iUEdlaU1QTVNXUHUwN0hIWW0wdFptMm1vb05qWUJSRFlDd05sRjB4SndPSmx4T0JwelZVS0o5VDhkOTZaOFJ4Z2sySmg0Q0ZGd1lFNG5vaE1uakVhREJrSmw4WVlIRFJBZERJQ2t5WlJaQWVmMVh4dnBjR3hrZzRDaWt5QTJFNG1BVmtUZ1hCdGdkQ2VSdE1iSWJnTWhGU3BJV21GVUNxZzYvYi9BQW5yeWJBK0JzQldaMUFxVHpuZkM0QnNEeUZrSjFGMUtxRVVOcERTWlVRcUFYVUhXZDFuZFB2WGhQWGdkT1FEQUdRU3M5Qk00bWRBWUN5QlZDZFFjRVprMHl0RWFUbnFKM1VHV2FpQldZMVNlaWYwSitlZTBuSUIxRmxuT0IxbWJUbFFaUnBJYUlEcE9aenVnUE9heURVVE9KM0UxWkJhWTBRVE1YbVR3L29md1B1QU9BaVlnR1dkbEZrblpKbVFLTE15elVTWjFDcUEyaHZMWGRFWGxUVDluRythU2tjR1NSNTQwaDR5L3dCQ2VCOXJOUkp5SjNTUjFtSW5ZQ1JPd0YxK2RmVCtkMFhQMTJyVGh2cS9LM2ZOMUZSSlhsK08rZC9UK080MjFuYjU5My9OMXRvV0hidWRQNmIvQUQvM2ZNOW5MK3o3bm42WW1USEV5ZGZTTTdKckx6K0JmYitMMk1xOUhHbXB2enhPS1FmY3BrY1oyOE1GWTlESG80YnI1TlAwY3V0cEhySnYvU2ZodmVjWDE4YkI3UVZaZGR0NFYxVENWMWdaT0Y2bzdvQzVUSFJKMkJzbGVFbEphcXFhUy9Mc0pXRW5WZFBIcUs4OWViNzk0ejJlcmRjTU1XV1lpS3N0V1JleGhlWXNOUlJOU3Jlc0lubTdUeFk1bE5VOGxKMXQrWTUyQU5qYU94dHlRNC9VZkgrdUIxMWo3VU1zekxuaEl5Yk84Qk8wN1BnMGJWdHVMUml3NTVrMTBMYmRrMWFEVmRVRzZlZUpIeXduWmJxdzhPNnZ5bnEvSDNaUGdaZVR5YW9IVnFOUmFTc05tdzBKZkphNFc1K3JXT21OcmJjZkowbkRJbGFZTlBVRTRaZWd4cHlkRFRuNlh6bnJmZXJsNkxwNU9SNHUvbmtmVnNQeEduZWU2R3lwUHIzM3paQnJOWHRhOG5QVjBiUzU5UzJTSnBidVQwOW5HdlR4emMvUjBuemZ0ZG4xOCtQeC90TzJndnhSenJvd1NacDFyT3E1MmprVjNuVHgxWGtZdG9NM044blJRTDlUV1ByL0FEK0FFb21UbkdQMStzL1JUVTgvV0ZoLy84UUFJaEFBQWdNQUFnSURBUUVCQUFBQUFBQUFBUVFDQXdVR0ZCSVRBQWNWRUJFVy85b0FDQUVCQUFFQ0FmNmZoSitINUtSSm1abVJrWm16Mm1Ya1Q1K1htWkg0WmY3NUdabWJUY2JqY2JUYWJEWWJqYVordzJld3pNalB6TmhuN0RhYlpYbTgzRzZWeHRsY2J6ZWJUWjV4ak9yeGxVZjU3RFlaR1JrYit4YlAvZktYd21jcDE5WHF5VmtyQlEwVmZKaXlpYTVVZ3JKU1ZIVkZQYkxKc2w4OTN2bGI1MldTa1p6bjd2YVpuNUc4Tmxuc2htVGszcE5UWTdSdm5jYmkwVzUzZHFiYzNaWGtrZnp6UHp5ODVXZTQzeXZsY2JwTmR1VEhhTG5jTHBkN3BlTDM2QmY3M2U3NWU3cGVrOGRIdjk4dlRlOTV0TnNyamFiZmQ3emViemVXQzBXZXlXU3lXdXlXaXlXaTBXdTJiemVXQ3pKbVRaWkxKYkxSYkxVbUN5V2UwV2kwVzVPRjB2RnlUa25TekpvdEZrM2xpVFVtWk5Gb3Nsb3RGc3Rkb3VGeVRoZExzblM4WEpPU2FMUlpMY215MFdpMFdKTVNaTFpiazVKMlRzblpQRjJUcGNMWmNrMldpMld5M0pzdHliazRYWk9sMHV5ZGs2VzVOdE8xTzJQSzdWajFXaFcrOXJ5MEMvTFFPakxUbHBTMVpha3RPV2tkSHZiMzJKajhqc2ZHNmQyM1NpOWRwVzhoaHpGelR1NWhaeS9QNStkdjlOemwrTnQyNmRtdWQwYi9BUDFGbks3dVVhYm5IZEdqY0RNdGVXN0hVbHJzTnlkL1MxN2M2MzJwYWt0ZGxuOVJmVmsxUFJzMEo2Y3RVNmpqT1pxL3ZXYjFXM0RaR3VkWjNYbnlCWGRhdlVQbXUyTjIvVnR1RHNOVjdsRXVUcjYxYnptMjFPUm5DNXF1enVSMlg5R2IwV2xOKzNrSjVGWFpQU1l1N1RPb05mOEFTdllyYjB0dFBjYTBxK1JJYUczcjVlcHkza1dYekZUUXVhYjFMdDZ2a1k1WDN0WGJXMHYwWU4xNjlqNGZZMEpheldpbnlKdmVTZlkxYTlTVjRTNldmeXFlNjF1Nld2WnIvc1V1NUYrbFRCNmpkeitUYWUydnU5L1VmcmJjME1qNDFndXF3ZHpPY3k1TXh1MWhodWVoMVVEZXhwWm4xNVR5bDdCVzVVMmx3MW5pTnZGbzVMeXFleGs4ZDJjOC9adVp5MDVtM0dISlV1VDhUaHp2QUd4cDZhckNmUGJ2dFJqN2MyK1Uvd0QveEFCRkVBQUJBd0lEQlFNS0FRa0dCd0FBQUFBQkFBSURCQkVTSVRFRkUwRlJZUkFpTWdZVU0wSlNZbkdCa2FFakJ5QkRZNUt4d2RIaEZTUWxNRlB3VkhKemdvT2k4Zi9hQUFnQkFRQURQd0gvQUM3L0FKbDh2endoejdRTzNyMmRlMGR0dVBaMS9QUDVvN2IvQU9SMVhWU1NlRDk2bWJxUG9WZjF3bnR6eHQrcVBNZlhzUFQ2SzQvTWwxM2FrenRDRktkWWg5RWZkVXRzbk0raW05cHFxaDRjS3FqNDQybEczZ2FQbXBIRFZuN2Ftdm5hM1BFdjFpOW1WQWp2ek4rWVRHSEtRL0pNUEg5b0tKd3NXaE92M1NWTWRYSjJwZWZvbXZ5RHltREs3bG5rNjN4VjlaRkV6VnBQeFdWN3BoR2loZG5mN3BsdkNtdDFiODBMK0ZBOTBXVVdtN3o2S2w0dFZOZndPc3FNanhPVWZDVnl0K25QN0t1UFRmWk50NmI3TGxPbnMwbFQvd0RVWDYxeVBGN3ZvbURpbkRSMzBYZHRkcWx6c0dmRlZCOVlvSE95YWMxQmV4VWJUaVk3N29jOGtIT3RmSkRVdXVvdjlUanhYZXlIM1RENG83S0YraElUU1BTaFpYeGhlK0UrMXc1U0hTUUtVZXNFL3N0eFR1YWQvc3A1enVwUGFVdkFvMjlJVkljc1pVdXVNcDR2K0o5MGRONGlQMGlOa2RBbkQvNG5XSXhLYTl0OHBSckw5VnIzbW8rcVFuTjBrUHlWejRqOGd2MTVSc2JTcm05RG12OEFka0Z5S2Q3UVR1WTdSd0tIdEllMGg3UlhKeCtxUHRMNGRudkwzdXhxSHRMMzBQYVh2THIyZFVlenFyY1YxUjVycWp6WHZMcWp6WFhzNm9jMWZpdXE2bzgwZWE5NWRVUnhYVmRWMVhWZTkyOWUzcW04ME9hNjluWHM2bzgxMVhWZGV6cXVia09hQTlaZFYxWFZkVjd5SFBzS3Z4UTVycXVxNnJxaHpRWFZkVjFYVmRVVHhSenU1ZFVUeFZ1SzVGSG4yZFYxWFZkZXdjME9hNnJrNWRWMVhWSG1qelE5cEVSOTEvMFR0N205QmpDNitRQzg0eDQ4ckZBTkphNUY3Q1hIUkNRRTNWUFF4YjJwbXd0T1NEb3hKREpkcmhjRWNVUGF6NVhROXBOOXRNdDQwMzJrT0RsMVI1cnFqelRpb3FGaFpzNXVPVnN0bkNWdVZzMUZ0bWtOWFQ2WTdaaGJ2eG15cXp0RUIxVy9EdmJXdnd1b1h1d1J5Qy9KT2N6QzVBT3ZpVjRuOTcxVlVRT2ZGRTF1dGpkU0J1R1dsQnl6SWN2OE9tM1V0bkZtUkMydHNmWjBYbWpHUzRua2ZpTjArNnI5dGZnMWd3Z1JrNFFQVzVwdEhSTnA2bUIwcGJrM0JsM1Y1M0JGdEtERXplRHdrcU4yczJYUlVJanRTMURybjFuc3RaTXFIdjhBUGF3NGJYYmt0bGdHMVM4OVZzOWc5TVZzb093YnQ1NnFoYURlbmRybFpOYTRoMUt3Y3MxWW5EQXpQcXFrdnhOSWJaUGxxS2w3bmZwZjRsVFE3S3d4U0VmaW0vMGFuaktkeGVtdG44Nk1yYkI5N1hVYmFqR3lVZUpXOWZFRklZdDYzVDRxSXg0UTZ6clozS0puZmJQdkxrUW15UVBaZjFTaTZnaC81M0lpbzE5UjNEcDhGeVp4NWYwVXpLQ09FdXlhTWdtRjRiREpmMnlGbG4xVzRZRzhjSVVkVzNDSk4yVzZ1THNrMXd3dDJoR254SERiL3VjYmZSVHRpRnBBTDhuNnB3dnZaYmtEbWkwV0pOK0F1aExLZSs3bVNVQytheDFmcjlWdWFVNEtpMWpjdEp5dFpVd09jandQZE9hcHNGN2NmQ1hLbm11UlNITHhXVk05OW80bWNpWHlXK2lCbzVab0F3bU53eTFBdmY4QWtvSW1ZcG1ndUp0aGpHWVRHVk1iV3c0bXlTTnNUZFRHcGUwd3R5ZU5CelZQREs5OGoydGUwV0RYTnlQTkRhTVFiUjA1Y0d5SElEb3BJYXN0ZkFXV1k2L2Q2ZkJTWDlHZjJQNktWelREaThQQWxSd3NMQ0hGdzlIaWQvQlNiOWdobkJ4YWtqSUZWVDNZak96VFFYVWtVYjQ5NHczNDVwdExJSkcxRVdQbGV5cTRKaHZaY1ZtOTJ6amtuVFJGa3BOdVpmZk5Pb3BzYldOZGxiQzUzRlN6VG1xcUtyUHhEQ2JsUmVMZVRBbDJaT0U4RlRPakVrQWVYT3o3emhucC9OVllMWFJ4T09JMnd0NzJmeVcwYWFRdWtwWGdjaGtWUExJSUlZbll1WjFzalR5Mk5XNjloNGhZbTZaR3lWa01HOWUrVVdOcjRiWHVuVUVkUlMxa2JtUG13dmp1TXNJdW9xaWNZSnpDVCtrZmNOMFdBVVRwNTI2Z2szK0tZK3BsZTJXNEwyNS9KUlJiUmZUVmpCdThiZ0JoMU4xVzB0U1h3MWhZTG5DMGFLZXBlMHNxYnp1ZUJrYkIxOHMxUEZOTERYVHdzbGpkbTNlYTgxU3dNSWt3RjloaExaTlFqSGFXU2RtZW1hYTUyRjFYR3dFM3hFbitDcUlyNGF2R0NiWW1IVlN0OWZJTEc3TWxQWUc0cDJtNHlzYnFxbmZndTRnWjJBVkhITThlZEM0ZG0wSXp4U2lsZ2tjNFJrV2EyNjJoRkhpcTZNc3dQc2VCdnlSbk1iSTRqaVpsbTRBRlRiT3E0ZjhBRW1IRTRkMXVvK2ZCU1JUVlZUU1NBdUxqWjRJNkRWVFN4UnlWSllYTmxCeHVzUzN2RGlvWXR1U3VrNzdoa0w1aXlwbzVERFVVakJESWZ4QTF4R25KVWRkVHorYjNMV3g5MHVBeXVkTWxGZkJjSHZaZ3Z1blJWR0dId2hvOGRyM1VFRC83ekxoYzZ6aFlMMDlTMFdMbmZodndqTUE2NXB0UysxYzV0UTIrYlhNMXlXeS9QVzFGSSsyQnpYR01FRFRYVXFscWRxeXpSemdpVEZodjFBV3hJNHdOb1NSWWdMV0xibFRUZDEwcmlPRjFDeVVGNUpaNjFqWXFLUmhFT3lvSkFOY05NMDJWT3lMZXo3RmhZMzNxTnVTcDZxZVNjTURJcjZNYUJiNEtqWSs4VG5CaDAzbXYyVDJFTzNodnpDYmZHMXhPZWxsTlNSN3BqemJGZkoxa2E3WmJvekk4dkZuWGMva3BRZDQ2TUVBMzcyaURudURhVWtYMXhKOGJDSUd1akp5YzV6eG90bm5ZbFB2cG5PRXNIcEwrQTR0QUxLbGViUjFraEZ2K0RKL2N0aVU4RHpGSE9aSkFjbnVhQVA0cnplbWRGRFZ5d0QxV3NPcVpESzE3WFpPTjNkZWFnbG1NbWx4eFZGVVRSeU5BZmFQbW9ITndBV3RvQW9qNE9mSmJCYnNWdFZVVkR6TzZjdGZDMXdHRnRzam9xRGFkYkhzbWgySnY1SHZ1MjlRR3VkN292a2VnVkZzWHppamoyUXlZU3lOdkVTWFRRT2JyY2dhSG9WREpRTm5lL3BoNm9VZE1QN09sTFg3Mno4WXViS0Y5VytpYkRUNU5OM3NiWS92dDlrUE41WVgwMFF4dXUyWE82YlROTEhSTmQ4VkpWVldKN2hZdjcxaGtGVHNud2JPbGVZaG1IbGxpU3AzdnRHWEhtSExaTEtJTmhMOTl4YmdHRnZ6NHJaKzBYYnA4aGplMjE4UXl6VU1GTEl6OGRyMkRJYnNaRzNLOTdmSzYycFRWRG13UVR5TjRQWkEreCt5cXBNWDRSUERSUXhiQmtvcXVkckoyTURJck12a0ZXenhqY1V6NUR4ZGdObHRvNVJiUExQOEF4bFNSdi92Rk5JRzg4QkZscytLSU83enJISlF5U05aR3h4WTYxeWNsRkZsYmdPV2k4bSsrZHR4VkEwRWJvckw4bkRKR3hpYmFHWXZpQWJrdkplMGMreHErV1JydGQ2TTc4TFpacnpIOG9Pem9ab1ptRXoydkpFV2c5UENxaGxOVk9obmxCMzV0dTVIZytrUHNpNnBCUlMxWXhsOE00M2QrV1dvMFQ2amFMWXJpNVpsbFpvK1FXMmFtaGwyeHM0NzV3ZFprYmJNRHVlWmNMTHlsY0RpMk82Ly9BRjJaZit5Mi9ENmVnZG4rdFovTmJUZ1phU2lkcnJ2Ry93QTFVYlBMUld3dlpqMDc0UDdsUzBrVDRxbWxFdUwxemtRRlB0a0hhZTlFZE9IV3RmdlBQSWYxVHRpYk9jSUtrNDVYZ2VHMXJLcXFhbWFSKytwdzl1TEtVOFBndHExWlpOSHRDcWJ2ZTluVU9Oc3VWMVNUMSs1bWlaM3k1KzlMTG05c1J5V3o5azdLWnRHVFpUSk44Nnd2Yk1LcjJtNXRCVHh4VWpINlBhM01OVkZHUE5ObitleUROenpXbGh0OE1LZDVUYlNrMlpXMUw0aHVpK1BCRTEzeSs2OG12SmhqRzFtMEo2cUo4bHZ3NGNHQjlsNUpNYUkyeTFmd3dCYkhxZ0hRek9adXNtRXdqdkM2cEl0b3h6a0NhTVNCejJ2Wms0WDBXellXTnBJUEpuWTV3aklPb1NmdVNWUjBEKzc1SjdGeHR6YnU2TENma2VDMnpYVmtUNUtlT1BjMWJKUTZLOThqcG1xaXRtbWlsa0c3ZTdFV0Z2VzYvOFFBSkJBQkFBSUNBZ0lEQUFNQkFRQUFBQUFBQVFBUklURkJVV0Z4Z1pHaEVMSEIwZUgvMmdBSUFRRUFBVDhRV0xOUkRLTk1RM2NaTExqZy93QVFFRzJZVlNxVjJSc3kxTDl4RUh1UDRpRW1HR0lpVmtPUzBTUDhJbHR3Z2Fpb1M1dVV3cVpkYk15MkVYTWJMRk1DM2FRVzZoank2aVVXL3dDSXk4d2U0eUdZTEdlVTNaaHNYL0FYZXB4dEViVGNWOU1mdU9XL2szVXhrUmZtTTNUTWJjclJIU0hORDJrZ3EvT0xMN205TUFYWDNDQWwzcUZRMmVMWWxhSHZTT2cxUnFOQUxwbnN1VVZERVZBMi9Fd3hQVWc4ajRsRklUekQxUWhkRHF5QW9MN0k5TGIrMHVFSU9HRlVnOEFJTlhoOGtZd0MvREYxRHhraUw2MG1uOTRQV2EwaEdPbDRpQ1lmVUdGTnpVcktLZW9qZjhwbkFqdkZnUmUzcVhsN0RSQUQ5eExZczIxVU1yYnM1eEJKOW1DNnFzNVlnTlFhMlZGVzI5T0l1M3RUUWJsZEZQQ1FxWHlEbVlBTDRsaERlVWlEaDJCTmJwM2NsZUUxTWcrS1NYRnRSYWkwaVBYV2pNUk1vNFV5clJqMmpNUjVWQ1IxWEh0Z1NteGtIbVo0SThGU3FCSXROa2FXME9CNXVZQXA4MlhBcS9OS1pWR3ZaRHBZdXlEVmd2Tk9TVXRtTGhrcDh3eFdQcTQvc1dWMTJGV3l3VmZWeEFJUHBnckh4eHN5elU0QjZUOWdsNUViSk56Q3c2a1V0RXZ6TitEQTR2MU5sUy9NR05FdUN5SjZnMVRQdVVhZWtZcHBLc0xBN2pKVkMrN2plRnNXelpLcW5QaVpGSk5XSHM5eFdVK3BtUHVwY1JwZGNSVUFQZVltVHlMV2ZUSzJ2c3BsbHRQVmtLNU5Zc202djZKckJnOTFpT3RqdFRuK05DdUNNMGtJeXdkdFBwbEpwVjdsSnAvY3JOd2FXUUhOZHZiS3hBKzBJdjhBN0J5ZjNtMWdLNFdNRlFPMFloYVA3ZzZqTzhZWnIxanZPeFVnbHQ0YlN4MjkrWVNPY1NydkVxb2RjdG5EeEVseXVtRWJKZXBOUzJwMU03b3pzNGRpQmt3bk82Um94Q3QzSExPb2hlTTRzSTJUMWZLTGFTSVdNSUdDYWk2d1k3Y3pOd0tCRkVxMnNwa0dJcThaNUp0ak1oS3EwM2tKdGtDNlpZc3FXZXlWTVNRM0hDVGlYYVBtZnBhbTBaY01tWUZ4QmFRSXRqZWM3UDhBajdxamJOSTFJYmtpMHduTllBSVFaWktxeUpjS05SbUliUXJOeEI3VGpUNUpsWGVLY3NNR1NuZlVqU3NrRFRCWE9qSlVwR3lGVi90bTRoSEJyRldrdUc1QXl5c0d6Szl4UXZiYzI3M0ZFbFNoRFpBY1RlS2g3VkQzRG10dHoreEhjR0NZaFJWYVhqd1Fmb0dYQTZZc0FEUXdGekxVL2M0NWxoY08xT2JBY3pvZjM5eE1rTVdIYldGZnV1RmFGS2IyUU1HcXJRM1E2dDdoOTh4SXdQaFBCTDZUYXZZa1hLVGVLb0hsQitNQjBNaTF0YTVKZkF0S2UzdXFqbTdoRnBvWVBad1VhZ1VZRXB4OHphamZnVmd6RnFXdlNvME43ZlVSOWF0SlF5WnFaU3hSVnBnejVqdDBFS0JISVp1SjRLVU4yL01UQ2pTQVArekprVUtNWS9aU05EQzNFV2xXbGdiT0o0RUJZU1V3ZUZOMzNNeXgwR1AvQUdLd2JzZkxSUFpNUmtaZmx4cFh4U3l4UGsyU1hMTkFOTGU3aDUwbzBadmZNUUNXR3lBaEtOOGEyNU05UUtlUy93QnpNR1I1R0E0ZEZEQjhUYzRIZzhNMmwzUG04WU9yUlhSa0xjcTA0TXMyb0FCb3kxODVqZHZyZkh6TE9LMk1ZQ1hsQXRxN2YzY2VBOGl0eEtXMk5Vam5lRWJPR09XM0ZjUzF2U1cxTnVCakErb3ZuVHBuZ0J3YUY3WUNXMDBOTk1aT0hEbFVHT200bzVkYjVXYW1adFREaFhoeDYxTlFUZHQ0YnBpOXhFRDJ2QUM4NWhjWWJMWVh3cG4vQUZGVnpDaDZpM0VJK09ySEtsNTlPSGNvTnowSU5MWEJOY2t4bFFBbW11NC9uRUVoZ3RYWkdOZ0VnckxrSDl3dFcvcFRoZ0h4dnlvTVdSUnRKbkZxM0hMbzAxU1VUREtOYTlCVXFwdDI2WnNoWWdQbGRQMUE0Tm9CdFkvY1lIWEk5bFI0L3dCWEEzL3FYSWlWRUlMVG5DY1ppd0w4TlIyd0VKcGFnZ3FTNDFCUEhNRk8xVmxIczNMY0J4WnVBMU9yUzZIZ3ZNYW1oYXlRMWl4SUJoYmNvOWpHYitLaktNS0NRenhkR1lMSjJiVVVyT09iUGtnRzFyMWNDcnErTVhCYnNPWjZNYWNGUXEyQTR1b0hoaXNCYUsxQzBwbUxMRFlxOHh3MGMrOGxSeGpIMlFRaUlzUGt2Y09YQzNiY2N3M0VBZnMva011UEJsVjk1cWNsazRIbWMzRWRHN0pIaHFDY29xeFJlV3BiOGNXTFBHU2JHRENyYXZENXVJdGg5YkloU1ZYcmpwRHRBMUQxdHJGZWpaUmNKa1plVXdlVnF6Y0krZURRU0s2MzNHQ3gzUWd1ajBRWlZsWVJYU2JwYTNqY1ZhMkdZN0JGcWQvRTFaZ0w1ZDlPbU5HcGFyOHJtdzZ4QWZPQU5ncDh3YXBiWGExc2VHSlowaWpGL0xEdUJkMVM0R21EeUZxeE1aSk16Z0ZlWWpHV3pvdDNtc2Y2d2NEQjJVZEVabmtSZEhOYmxOZWgrdmF6SFdwVkZadmk0VkVyT1VGMEhCNjRKV3BOcGorQVRjVk5ZYjhZaldoVXE1ZDNlWXpCYWF5dXEyY2FpYjg1MHBPZkRkUTdWNEwyZkpHK1RSUWZqaVh1bnFhOVdCOThRaklpc0pZeXZEbnVBVmRMWWZWcUNmcFdhQnJDZ3UrSVIwMnhuWEMyWll0ZERVN1lzM3l0eWtUUkVXcXVXdzJFVmh2V0V1R1FuSENLVlhheFJSRk1oQnk2bUR6WHhUVFpjMmZFWGFzNThHMmlOWkM3MTFBRGhncDBWQmx2UjJXUTc5NUEyQ3hRajBuSm5aS1hKVmV0UVYrNGdxRG5Gemd0aGRXazZpY1UzbTg3dUJXTTdVNTVncVdhdUd2T1BVWGtFN0YyTGFsWHl2VXIzRWVtalJiQktOcldZMURJTmxsc2xXN2VtUHRmQ1hGeUN5Wk5nNm1LbXE5Wm5rdlpGcVliZStIa1l0VGRVWmdmZFl1T2I1YkVSYnBNeFoxNHRaMWtxdjN4QnhXYVRxTjZ6RzZuU1IrWnV6dTZObjVGNDNsaXlxaXhIcU5EKzhDcFRhbVhxUDRaMUw3aVg5UXZGeDVsb1UyWFZkeXR1eDVGT1N2MGxkcHN6ci9pSFJkOUk4dHMzZTJ3VzNEazEwaHFxNksvTHpBdjQxQ3VoWmNqVU55V3Vyenh6bE0yb0dQL0FBbFlBSW9ldlBMTVI5cERubzVOdXlIcUxGMlM0S1JmT0lsM2tYdGlnS1F0NXBNUGxFMlY1UmJ4WkNscVM5ZGNDTXloQ0ptRktXRldXUEVya0I1THF5TzBlY1JQSGJBREMzUVdhSUJNZHFMeVpDbDBYTDlDRjlBVlRGMTdpM3dsb0VHaW5GcnVNOUMweFlhQmJmZmlGUkFBTk9zWFhkeGpNQzVMRzZOMGtYRE9RSlpZSW9uU1FZMW8vUGFmc3hQT1dJdDRRTXB5cDY2M0RVMmZzZWh5cTVWdHZiUC94QUFzRVFBREFBSUNBZ0VEQXdRQ0F3QUFBQUFBQVFJREVSSWhCREZCRUJOaEZDSlJNa0p4Z1FVanNjSGgvOW9BQ0FFQ0FRRS9BRGFOb1ZDWnRDZjBUK3ZJNUV0Q2JFeE5vVnY1RmV4TVRRdnJOSEkyYzBjem10SElURTBLeFdLaGZSSmlla0pvVEYzOUZiUXFRbm9UMzlkbS9wczJiRStqWWhNbm8ydnBzZHpQc1dTR0s5ZkFzaUZaelFrSnRFc2xpUHVzNTdRc2g5eG5NNW9WeXhYQytTY3EvaysvSXZJalF2SUY1S0g1T3ZRczdwZWgyMFRsdE1qTjEyTHlKWHdMeWwvQitxcEwwTHlxYUo4Z1hsdEg2bW12Wjl3KzRmY1B1bkk1b1ZmSno2RTZGVkNxeWFyWG9UL0J2OENwL3dBRTMrRDJhUXVsNitxVWt3aVZqZnl5WnhyMGNqbXhVem05ZG14UFNaTjlhRlZhSmUxN0pUK0dMa2hYcjRKeXRNV1VXVmVtaFpKMTZGbGtWSTJKaTBMUXRFdkdMUWpvWEUwS2V2UncvQW83SnhpaE1VTFlvalhvVVQvQXNiL2dXUHJzVUppeHZRb2V2UXNmNEpqOEU0MkxDejdTUHRvV0phSmpvU0VMWkloQ1FraFNLV1NoU0pDUWsyS2Z3UW12Z1gwU0ZMRkd4WXlZUW9TRkFwUXBGSXBGSXBRcFFwRklwRWhDa21Qb2laRkFzWk9NVUlVaWtVTVNFaEltZGlRa1RERk9pWkVoTFlzWW9XaFN2Z1NKbGs0eFkwVENGQXAyVGpiRmlGQ1NGSW9vV0toWW1MR0tFaFNraElTRW1LU1ZvU0VoU3lZSmpRcEZJbHYwVGlaTUpDVEVoSVNFaEk0aWtTRklvRkFvRklvSnhrd2hTaEl3eHVpb1hCNlJHUGt5c0NoTFJNampUNk9IRXhZNnQ2UkVhMm1LR0xFeFlXTERvV0w4Q3hDeEN4Q3hvV0pFd1lQK09xKzdmV3ZneWVPOEZjYUp4Y3ZSK254L1o5ZkF2RmFXMlJpVXZhT0pFYW9uRE5wTm42TmZESXg3eUxZdkd4NXNqMzBZL0ZqQytVbCtIenQxTDBZOFhGMUZmQXNWR0x3cjN1L1I1R0J3a29ST0x5SDdST0RNeGVONUh2WitselAwejlEMS9VeGVBdmxzbnc4U1d2WmpYR0VqSkNxK3g0Vi9hYTNPamczR2hZbG80SVdOa0pjRUpDampTWmk2eXY4QXd2OEF5eCtoSENlVFlvMnR0RWpsYk9BbzBoSVVuQW1ObkRTSVhRNTc5Q2s0djBjZjVHbm9Vcm5wam45MnZnbGJnaUZVbHczMFErTnZiK0RmWDA1Sk1VRWlhRTBiMkpwL0FscERpbXRhWjl2VWs0cFNPUDdSY2ZrWEIrbWFKUS9lMnlVdVhJVDJ4UFNhTUpsbW5MMFlzYWM5K3hLay93QUV2YU5VaUxYcENleEplamo3RW1rZEkvQ0ZVcEcxU2FSTGFYb1ZmZzRyYWJPTTZla1NrKzJpdTI5RlJUWDdmWnh5TlA4QTlDVDNwK3hSdzNwa2FuMmU2TWlibDhYbzdjZXlFOUpiSEx2YTJ6V2wwanJScUY3WnFWM3RpWEpNNGt6VGV5VjBka3Q2ZXgwMThrNkUzclNZN3B2V3hLdmhFTnRkb2kzTyt5ZSsyVmFPVzMwSlY3TlBXdEV1K1RUWFJXU2NYN3FmUkhLbW12OEE0ejkvK2pCVXl1dlpqV0c4amxGYVNleE4vQXRlMmExNkpOdVdRS09Xeld1bVRxU0Y4bzVJNUk1YVRGZHZvVHRKdGl6Wkgya2VQa3h0djd1LzlHL0MzN296NXNjTmZhMjErVHo4bFY0TjlhUEdmL1RQK0VYenhaT0taaWJwYlpHSzJ1VXNjNVgvQUdpaTllaUkzdE5Hc1NYb3gzOGFKVGFKZnZaR1ZTdXV0RTAzMm14ZU5PVysyWlBHNFFtbXh4YTN0aWpQUDliWCt0ajNyb243djIrVFJ6ZWlMWGFIYTJTNWZTTGFYUmVPY3NPSDhtTktWeFh3Zi8vRUFETVJBQUlCQkFBRkF3SUVCQWNBQUFBQUFBQUJBZ01SRWlFRUV6RkJVUkFpWVJReUJYR1JvU0JTZ2JFakpETkN3ZEhoLzlvQUNBRURBUUUvQUN6TE1zS0xMRmpFcy80TGlaWXhNVVlHTmh4TE5GaFJNZEZoS3drV1Fpd2tpeGlZZFJ4RWtXR2hMK0Jvc09DTVdXTVN4YXdvTVVTeXQ2UG9XTGVxUTRqVFIxTVJVcHkyaDBhaVhRVWJqZzBXTVRwNk5HS01iaXB3N001Y2ZKR0tGRVVSUmR0R014d3FOYlJ5V2ZUelBwNmcrR1o5TUxoVTBmVHhnUXBxdzZNYkV1RzhNZkN5OG4wYjhrZUZUN24wa1VQaHJDNFQ1UHBvSytoVU93cU1reU1KZEJSTVBCaVlNVVN5UmlqQldIQmVSSmVSSmQyWXg4bUtTMC9WUnYzRXZrU1ZodVhrYzVkckdWWGRraVhOWXFJcWJGVE1FS0JnWWFGQWEzMEx4N290RXdIU3VqbEk1T2psTTVNakJpZ09EUloyTVpEVXh3YVJhVml6TEZtTFJvVFM3bDlEYlBjV2xaM1BjdTRuSy9VelNSbDNSZHBkUnpYY3lYa1RYWm1TdjFITTVqTXhTMXBqcU1kV2FmWVN1WWlSWXhNREczY3N4dElidUkxNk54RzRqZnlhTkRZNUpGeHN5OUhwQ2JFMzZJdVpzYzVNdXhHU00wU2todjBmb3h5OERabVptUmtaR2FMb3lYWXpISm1Sa3k1bWh5WmV4a3pJeU0wT3FPc09yY2N5N0xtU1E1am1aanFKR2NUTkhNTXk1Y3Y1TWtPYUhVUTVvemJNeHpISXViZm8ybHNkUWNoeXNPZGh6SFVOaUxvelNPYWMwZFVkUXpiTWh6U0hObVRMbHliYWlRbTFOYkoxYkpzcGNUemIzSlRTaTJtVTYwcEozSTFWTzVVclFweHU5SWRTNnZGaXFJNTV6eDFtYzNYVTVwekRtTTVyT1l5N1pXNCtNTHFIVW9WbzE0WlJISlI2bjFGVG5kZFhIeGNYZElsWGJqWVZSSms2aXhZNjg2ZDBoY2ZLMW1qbXVOS1ZuMlBxcXRDanJ5Vk9McVZvNHk2RkhqdVhTeGtyMktsWEtNYWtkWE1rVk9MZ2xhSncxV003dWJIUGhsZmJIVm9MdU92d3ExWVhFVVBBdU1TLzJvZkcrSWk0cVVwYjBUZDV0L0pUbktGTFQ3bFBpWkplN1luYWQva3phbGM1ejJLbytxT2FyRlNXMzZScVpRYStDdC9vcjh5TWVwYlJuTEJMc2hWTzF6dVJsaWptYTBaTXpNdmt6UnpMTFFwdHM3aSszcUpwSnRzeUZMSk1VbGRqazFTY3ZsZjhpbDdma2sza1RuS0x1bVVwcGU0cUxPbGRlUmF2NlJoT2EwS2Z6NnE3OU54N21SR1VQNWwrL3dEMEthNWxoMVpOc1UyMjdscE5YUktGU0t1MEpON1NIcE1YMnRKWGVpVG1vT0RYZS84QWMxR1B5V1pXWGtvU2pHU3lXaXBWOTd0MEZnNHRkMnlTeGJzUmFaYXdteStyMk1rZGZUb1lOa0lOUzJ5Y0lMN1pYTWJKdHNoS1VrNHJvTzZXTGZZVXBwV1QwN2k5aUlWNDlKYmkrcEtkTHNKNkpPNVYyUmYrR1VjY2xrcmtjRTNkRG5HN2RpRldOSjNhWDl4TVFxai9BSkYrNHF1N1lMOXg2WkVreDlXSjJPcFNvenFkdEgrWlMwbVRoSnR1YS9VU3BxU2t0SE84eS9Za2xGKzE2R3FjdXFITEV1cjdZNVUyOW5OcEtGa2M2Q2Z0SnpwY3RTaTl2cXZCU3pyUGx3amRsZXJEaG0xSlh2NFYydjFFaXJHVW52cFluS3RDamsyVTJ4cE10SnUwUnhtdnVMTnV5RzlXc05rT0xuU2RvOURuVCs1T3hueE00UHVtU25lTmlNSnpUYVRzT2xWN1JZcUV1cnZiOGlVS01WZlkzRSttK1AzUlg0WGlMSjBsK3JIUjQyS2YybkQwNXVMNXZYNFB3ZWxHUDRoQjdPS1Y2OC96S1NoT2xrVlVvYVJPdENQdG1RcTByYmx2K29wMCtyWkdxNGJnejZuaWIvY1RsVW5LN1kwbkluQ3k2a3FieHZMZHh3aHRXUkhqblFvMnhUdDVLWDRqR2RWcWRPTEZ4Ym1zRnBmQlM0bWg5c0x2emwvNFJtb3QzYmEvTXI0U3F1TkwrcVlvVm10RTF4RCsyekl3cTQrOVdZNzlTbENVdmNrVXEwNk0xUHhzMVViays1Ly8yUT09IiBpZD0iYyIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiLz48L2RlZnM+PC9zdmc+", + "description": "A colorless and odorless gas resulting from incomplete combustion, particularly in motor vehicles. Results displayed by combining the latest and aggregated values and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'carbon', label: 'Carbon monoxide', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'carbon', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Carbon monoxide\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#3B911C\"},{\"from\":5,\"to\":10,\"color\":\"#7CC322\"},{\"from\":10,\"to\":25,\"color\":\"#F89E0D\"},{\"from\":25,\"to\":50,\"color\":\"#F77410\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:Q08tc2ltcGxlLXZhbHVlLWFuZC1jaGFydC1jYXJkLWJhY2tncm91bmQucG5n:Q08tc2ltcGxlLXZhbHVlLWFuZC1jaGFydC1jYXJkLWJhY2tncm91bmQucG5n;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Carbon monoxide\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:molecule-co\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"mg/m³\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "carbon monoxide", + "CO" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_card.json b/application/src/main/data/json/system/widget_types/simple_card.json new file mode 100644 index 0000000000..b43f4a12af --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_card.json @@ -0,0 +1,24 @@ +{ + "fqn": "cards.simple_card", + "name": "Simple card", + "deprecated": true, + "image": "data:image/png;base64,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", + "description": "Designed to display single value of the selected attribute or timeseries data. Widget styles are customizable.\nWidget is deprecated. Use \"Value card\" widget.", + "descriptor": { + "type": "latest", + "sizeX": 5, + "sizeY": 3, + "resources": [], + "templateHtml": "", + "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\n\n.tbDatasource-table {\n width: 100%;\n height: 100%;\n border-collapse: collapse;\n white-space: nowrap;\n font-weight: 100;\n text-align: right;\n}\n\n.tbDatasource-table td {\n padding: 12px;\n position: relative;\n box-sizing: border-box;\n}\n\n.tbDatasource-data-key {\n opacity: 0.7;\n font-weight: 400;\n font-size: 3.500rem;\n}\n\n.tbDatasource-value {\n font-size: 5.000rem;\n}", + "controllerScript": "self.onInit = function() {\n\n self.ctx.labelPosition = self.ctx.settings.labelPosition || 'left';\n \n if (self.ctx.datasources.length > 0) {\n var tbDatasource = self.ctx.datasources[0];\n var datasourceId = 'tbDatasource' + 0;\n self.ctx.$container.append(\n \"
\"\n );\n \n self.ctx.datasourceContainer = $('#' + datasourceId,\n self.ctx.$container);\n \n var tableId = 'table' + 0;\n self.ctx.datasourceContainer.append(\n \"
\"\n );\n var table = $('#' + tableId, self.ctx.$container);\n if (self.ctx.labelPosition === 'top') {\n table.css('text-align', 'left');\n }\n \n if (tbDatasource.dataKeys.length > 0) {\n var dataKey = tbDatasource.dataKeys[0];\n var labelCellId = 'labelCell' + 0;\n var cellId = 'cell' + 0;\n if (self.ctx.labelPosition === 'left') {\n table.append(\n \"\" +\n dataKey.label +\n \"\");\n } else {\n table.append(\n \"\" +\n dataKey.label +\n \"\");\n }\n self.ctx.labelCell = $('#' + labelCellId, table);\n self.ctx.valueCell = $('#' + cellId, table);\n self.ctx.valueCell.html(0 + ' ' + self.ctx.units);\n }\n }\n \n $.fn.textWidth = function(){\n var html_org = $(this).html();\n var html_calc = '' + html_org + '';\n $(this).html(html_calc);\n var width = $(this).find('span:first').width();\n $(this).html(html_org);\n return width;\n }; \n \n self.onResize();\n};\n\nself.onDataUpdated = function() {\n \n function isNumber(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n }\n\n if (self.ctx.valueCell && self.ctx.data.length > 0) {\n var cellData = self.ctx.data[0];\n if (cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length -\n 1];\n var value = tvPair[1];\n var txtValue;\n if (isNumber(value)) {\n var decimals = self.ctx.decimals;\n var units = self.ctx.units;\n if (self.ctx.datasources.length > 0 && self.ctx.datasources[0].dataKeys.length > 0) {\n dataKey = self.ctx.datasources[0].dataKeys[0];\n if (dataKey.decimals || dataKey.decimals === 0) {\n decimals = dataKey.decimals;\n }\n if (dataKey.units) {\n units = dataKey.units;\n }\n }\n txtValue = self.ctx.utils.formatValue(value, decimals, units, true);\n } else {\n txtValue = value;\n }\n self.ctx.valueCell.html(txtValue);\n var targetWidth;\n var minDelta;\n if (self.ctx.labelPosition === 'left') {\n targetWidth = self.ctx.datasourceContainer.width() - self.ctx.labelCell.width();\n minDelta = self.ctx.width/16 + self.ctx.padding;\n } else {\n targetWidth = self.ctx.datasourceContainer.width();\n minDelta = self.ctx.padding;\n }\n var delta = targetWidth - self.ctx.valueCell.textWidth();\n var fontSize = self.ctx.valueFontSize;\n if (targetWidth > minDelta) {\n while (delta < minDelta && fontSize > 6) {\n fontSize--;\n self.ctx.valueCell.css('font-size', fontSize+'px');\n delta = targetWidth - self.ctx.valueCell.textWidth();\n }\n }\n }\n } \n \n};\n\nself.onResize = function() {\n var labelFontSize;\n if (self.ctx.labelPosition === 'top') {\n self.ctx.padding = self.ctx.height/20;\n labelFontSize = self.ctx.height/4;\n self.ctx.valueFontSize = self.ctx.height/2;\n } else {\n self.ctx.padding = self.ctx.width/50;\n labelFontSize = self.ctx.height/2.5;\n self.ctx.valueFontSize = self.ctx.height/2;\n if (self.ctx.width/self.ctx.height <= 2.7) {\n labelFontSize = self.ctx.width/7;\n self.ctx.valueFontSize = self.ctx.width/6;\n }\n }\n self.ctx.padding = Math.min(12, self.ctx.padding);\n \n if (self.ctx.labelCell) {\n self.ctx.labelCell.css('font-size', labelFontSize+'px');\n self.ctx.labelCell.css('padding', self.ctx.padding+'px');\n }\n if (self.ctx.valueCell) {\n self.ctx.valueCell.css('font-size', self.ctx.valueFontSize+'px');\n self.ctx.valueCell.css('padding', self.ctx.padding+'px');\n } \n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-simple-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-simple-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ff5722\",\"color\":\"rgba(255, 255, 255, 0.87)\",\"padding\":\"16px\",\"settings\":{\"labelPosition\":\"top\"},\"title\":\"Simple card\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true}" + }, + "externalId": null, + "tags": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_co2_chart_card.json b/application/src/main/data/json/system/widget_types/simple_co2_chart_card.json new file mode 100644 index 0000000000..2f8cb1f4fe --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_co2_chart_card.json @@ -0,0 +1,43 @@ +{ + "fqn": "simple_co2_chart_card", + "name": "Simple CO2 chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAkFBMVEUAAAAAAAD19fUAAABvb2/9/f36+vrx8fHk5OTU1NT///+AwywhISHj4+N0dHTx8fGv2XtYWFjHx8eQy0bv9+Xf8Mq/4ZU8PDwvLy/3+/KQkJCCgoKIxzmsrKyQykbV1dWg0mCg0mGenp643Yi6urrP6bDn9NhKSkpmZmaYzlOdnZ2rq6vX7L3H5aPH5aKo1m5LZ5BzAAAACnRSTlMAH8cQMfHjso9z2hGKZwAABOpJREFUeNrsz0kRwCAQADBuKP4Fd+qgT9hJHCQBAAAAAAD8VvP16tdoq+/L9dlqymMHMFp6dggrlR3CjBIpIocROc3Lbr2s1goEURieL+pC1aQbm6ZBBEXf//2OtkJOSGBvCIQK9A+6QBz4DRQHJFoDEq0BidaARGtAovU+ZJayXFvv1UkK48oE3+TE+MXeh1guQjNQsmzXpjSlpMBfg1RiYMpwWs4ldWpo5MAN0Xlm+AK0Cvi8RIGI1H58VDIA9mepgp9n7RBOZpkrKTY7uVvagkCMF95c8JGk/5cmAJwEVyb9qhXNFXQoHXBqMSAr+wVhSw29zxClGdB1VQAdsiazbDBZSJ1WW6nGgBSZVeTQXRx3e1ZgMd3pXCe/SIyeCZK5e4Nn2dBodneNAXliq4o7zUVbsnO31lICSnZmBTpkyqx7g+Z8nCdhlVgQrZXxtCQiY4DvZboSoENUiFZ+PgktEZUgL/u3sd6riq8pf73z541flD/cgERrQKI1INEakGgNSLQGJFoD8hu5mBXHG4WGsFE2yySKl0WGcMrHtRMZXhYZItTQ26niVZEh/9gzt+VkYSAA328CgoRDOIhURWk9vP/b/etmmf50pgQbZ8yF30xhMbnYj81GikUITGwvicci9JLZUEprl3gsUkkYCeQHWPBYJI8Blq8t70TWYVwFpgpfAMvXlm8ipSximQfAv8csX1ueiaxliVkXebvaFyeAB9bWq0VWVS6PDTD820WbS/Mm+YG19WKRIC/CMpbH8VJW5rz/XP+YiKWaZUZEKz2NR5SCZ3Gidvgcswx/f4EfF4ExapqJo1Xk3Akhut5k3tVCJAcgLhjXkQY3pj0c5+ayCOdqV7bBOpZI3gBjFzlgtgmqpBhrDBJM/wJIZgY6eAZ72fLD1No0QjAjHUqkKINgf5QhT7SLJCLRoGpK+EI+O1FrACVERp8c4AmEBbd8XjRtUMlqvn77Zr3i1s/R3C5C+V7hnrzA4yAiPGpKPhM1IFtx+24enaZ6jFWach+Nse5TBb9wPI05niRSPbBJyI+FInTLMxJBKVOkHcANa0SGGyAyscFJIrlgHIkouse9mdxRfK7xFOm5FiHaplk9tG0vEaEqdNQcEUshdLElEWPIgRi2ppkijmuFk/G0xTjBE8bZTIu4YBfpsUmiWiTqW2T7i8gVqJlIpAdIhdhRjylamje6KwP8hFvEgrMI5YBcwS7CJ40iGzNvw+WDlCrFA1O4RSy4itAelR0iIS7LRM5YDMqXP7KL2J8E3UVMBufxRo8iw4wIZcz5HpaKVDIAC84inB856OmuNfCuNRX5Q0VaGYIFd5ELloILMyalxu8RDVSYiUg06ZFhiciXzFdgwVmEtx59E0JR8njcUdzTQEpSLJKhZMK71plW1s4mQh5Hq4e7CCWVdDVmyc9aXUIx+SQD/gGwiLkWir9H7mPKLlLaPNxFmAwzF/WVYnWjmAdQT2w1IFyRBAfPJt/dfUyBVaS0vwZ1Fxn5/xlJU8z0PWuMPdLjIOdLEy2QB9hxF1kOiTAksohSVvB3PBL5/AAHPBJx5/ki9D8H06c9vA6/3mu9Rd4iHvIW8Y23iG+8RXzjLfKvnTuoASCEASDYgyZw+BeMBZ7QzDhYA3sbIbdpMVYJLeYq4Y+sMUH6oud4f0s1s9cZhQEAAAAAAJzZz8keC0HEP5QAAAAASUVORK5CYII=", + "description": "Displays historical CO2 level values as a simplified chart. Optionally may display the corresponding latest CO2 level value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'co2', label: 'CO2 level', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'co2', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"CO2 level\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3FA71A\"},{\"from\":600,\"to\":1000,\"color\":\"#80C32C\"},{\"from\":1000,\"to\":1500,\"color\":\"#F36900\"},{\"from\":1500,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"CO2 level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"co2\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"ppm\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "co2", + "carbon dioxide", + "carbon emission", + "carbon footprint", + "carbon output", + "carbon pollution", + "carbon capture", + "carbon offset", + "carbon reduction", + "carbon neutral", + "climate gas", + "fossil fuel emission", + "carbon cycle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_co2_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_co2_chart_card_with_background.json new file mode 100644 index 0000000000..216cbc748e --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_co2_chart_card_with_background.json @@ -0,0 +1,43 @@ +{ + "fqn": "simple_co2_chart_card_with_background", + "name": "Simple CO2 chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical CO2 level values as a simplified chart with background. Optionally may display the corresponding latest CO2 level value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'co2', label: 'CO2 level', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'co2', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"CO2 level\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 160 - 80;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 400) {\\n\\tvalue = 400;\\n} else if (value > 1600) {\\n\\tvalue = 1600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":600,\"color\":\"#3B911C\"},{\"from\":600,\"to\":1000,\"color\":\"#7CC322\"},{\"from\":1000,\"to\":1500,\"color\":\"#F77410\"},{\"from\":1500,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"CO2 level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"co2\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"ppm\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "co2", + "carbon dioxide", + "carbon emission", + "carbon footprint", + "carbon output", + "carbon pollution", + "carbon capture", + "carbon offset", + "carbon reduction", + "carbon neutral", + "climate gas", + "fossil fuel emission", + "carbon cycle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_efficiency_chart_card.json b/application/src/main/data/json/system/widget_types/simple_efficiency_chart_card.json new file mode 100644 index 0000000000..9d2a33be40 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_efficiency_chart_card.json @@ -0,0 +1,32 @@ +{ + "fqn": "simple_efficiency_chart_card", + "name": "Simple efficiency chart card", + "deprecated": false, + "image": "tb-image:c2ltcGxlX2VmZmljaWVuY3lfY2hhcnRfY2FyZC5zdmc=:c2ltcGxlX2VmZmljaWVuY3lfY2hhcnRfY2FyZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjExMiIgeT0iMjQiIGZpbGw9IiNmZmYiIHJ4PSI0Ii8+PHJlY3Qgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxMTEiIHg9Ii41IiB5PSIyNC41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik0yMy41IDQ3LjZWNDloLTUuM3YtMS40aDUuM1pNMTguNyAzOXYxMEgxN1YzOWgxLjdabTQuMSA0LjJ2MS4zaC00LjZ2LTEuM2g0LjZabS43LTQuMnYxLjRoLTUuM1YzOWg1LjNabTMuNyAxMGgtMS42di04LjFjMC0uNi4xLTEgLjMtMS40LjItLjQuNS0uNy45LS44YTMuNCAzLjQgMCAwIDEgMi4zLS4ydjEuM2wtLjMtLjFhNCA0IDAgMCAwLS40IDBsLS42LjFhMSAxIDAgMCAwLS40LjRsLS4yLjdWNDlabTEuNi03LjR2MS4yaC00LjN2LTEuMmg0LjNabTIuNyA3LjRoLTEuN3YtNy45YzAtLjYuMi0xIC40LTEuNS4yLS40LjYtLjcgMS0xbDEuNi0uMmgxbDEuMS40LS4yIDEuMy0uOC0uMi0xLS4xYy0uNSAwLS44IDAtMSAuMy0uMy4yLS40LjYtLjQgMVY0OVptMS41LTcuNHYxLjJoLTQuM3YtMS4ySDMzWm0yLjcgMFY0OWgtMS42di03LjRoMS42Wm01LjEgNi4yLjctLjFjLjMtLjEuNC0uMy42LS41bC4yLS42aDEuNWMwIC40LS4xLjktLjQgMS4zYTMgMyAwIDAgMS0xIC45Yy0uNS4yLTEgLjMtMS42LjMtLjUgMC0xIDAtMS40LS4zYTMgMyAwIDAgMS0xLS44Yy0uNC0uMy0uNi0uNy0uNy0xLjEtLjItLjUtLjItMS0uMi0xLjV2LS4yYzAtLjUgMC0xIC4yLTEuNS4xLS40LjMtLjguNi0xLjEuMy0uNC42LS42IDEtLjhsMS41LS4zYy42IDAgMS4xIDAgMS42LjMuNC4zLjguNiAxIDEgLjMuNC40LjkuNCAxLjRoLTEuNWwtLjItLjctLjUtLjVjLS4yLS4yLS41LS4yLS44LS4yLS4zIDAtLjYgMC0uOC4ybC0uNS41Yy0uMi4zLS4zLjUtLjMuOHYyLjFjMCAuMi4xLjUuMy43IDAgLjIuMy40LjUuNS4yLjIuNS4yLjguMlptNi4zLTYuMlY0OWgtMS42di03LjRINDdabS0xLjctMmMwLS4yIDAtLjQuMi0uNmwuNy0uMmMuMyAwIC41IDAgLjcuMi4yLjIuMi40LjIuNyAwIC4yIDAgLjQtLjIuNmExIDEgMCAwIDEtLjcuMiAxIDEgMCAwIDEtLjctLjIuOC44IDAgMCAxLS4yLS42Wm03IDkuNWMtLjUgMC0xIDAtMS41LS4yYTMuMyAzLjMgMCAwIDEtMS44LTIgNCA0IDAgMCAxLS4yLTEuM3YtLjNjMC0uNiAwLTEuMS4yLTEuNnMuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS4xLS44bDEuMy0uMmMuNiAwIDEgMCAxLjQuMmwxIC44Yy4zLjMuNS43LjYgMS4xbC4yIDEuNXYuN2gtNS43di0xLjJoNHYtLjFsLS4xLS45LS41LS42LS45LS4yYy0uMiAwLS41IDAtLjcuMi0uMiAwLS40LjItLjUuNWwtLjQuN3YyLjNsLjUuNy42LjUuOC4xYTIuMyAyLjMgMCAwIDAgMS45LS45bC45LjktLjcuNmMtLjIuMi0uNS40LS45LjUtLjQuMi0uOC4yLTEuMy4yWm02LjItNlY0OWgtMS43di03LjRoMS42djEuNlptLS4zIDJoLS42YzAtLjYuMS0xIC4zLTEuNS4xLS41LjMtLjguNi0xLjFhMi43IDIuNyAwIDAgMSAyLTFsMSAuMS44LjUuNS44LjEgMS4zVjQ5aC0xLjZ2LTQuOGMwLS4zIDAtLjYtLjItLjhhLjkuOSAwIDAgMC0uNC0uNWwtLjctLjFhMS42IDEuNiAwIDAgMC0xLjQuN2wtLjMuNy0uMS44Wm05LjcgMi43LjctLjEuNS0uNS4zLS42SDcxYzAgLjQtLjEuOS0uNCAxLjNhMyAzIDAgMCAxLTEgLjljLS41LjItMSAuMy0xLjYuMy0uNiAwLTEgMC0xLjUtLjNhMyAzIDAgMCAxLTEtLjhjLS4zLS4zLS41LS43LS42LTEuMS0uMi0uNS0uMi0xLS4yLTEuNXYtLjJjMC0uNSAwLTEgLjItMS41LjEtLjQuMy0uOC42LTEuMS4zLS40LjYtLjYgMS0uOGwxLjUtLjNjLjYgMCAxLjEgMCAxLjYuMy40LjMuOC42IDEgMSAuMy40LjQuOS40IDEuNGgtMS41bC0uMi0uNy0uNS0uNWMtLjMtLjItLjUtLjItLjgtLjItLjMgMC0uNiAwLS44LjItLjMuMS0uNC4zLS42LjVsLS4yLjgtLjEuOXYxLjJsLjMuN2MuMi4yLjMuNC42LjUuMi4yLjUuMi44LjJabTYuNy40IDItNi42aDEuOGwtMyA4LjUtLjMuNmMwIC4yLS4yLjQtLjQuNmEyIDIgMCAwIDEtLjcuNWwtLjkuMmgtLjRsLS40LS4xdi0xLjNhNi4zIDYuMyAwIDAgMCAuMyAwaC43bC40LS40Yy4yLS4xLjMtLjMuMy0uNmwuNi0xLjRabS0xLjItNi42IDEuOCA1LjUuMyAxLjgtMS4xLjMtMi43LTcuNmgxLjdaIi8+PHBhdGggZmlsbD0iIzNGQTcxQSIgZD0iTTI5LjcgOTZWOThMMjIuMSAxMTZoLTMuM2w3LjYtMTcuM2gtOS42di0yLjZoMTIuOVptMTUuNyA4LjR2My4yYTE1IDE1IDAgMCAxLS41IDQgNyA3IDAgMCAxLTEuMyAyLjdjLS41LjctMS4yIDEuMi0yIDEuNWE3LjcgNy43IDAgMCAxLTQuOS4yIDUuMyA1LjMgMCAwIDEtMy4yLTIuNmMtLjMtLjctLjYtMS41LS44LTIuNS0uMi0xLS4zLTItLjMtMy4zdi0zLjJjMC0xLjYuMS0zIC40LTQgLjMtMSAuOC0yIDEuMy0yLjZhNSA1IDAgMCAxIDItMS41Yy45LS4zIDEuOC0uNSAyLjgtLjUuOCAwIDEuNS4xIDIuMi4zYTUuMSA1LjEgMCAwIDEgMy4xIDIuNmMuNC42LjcgMS41IDEgMi40bC4yIDMuM1ptLTMuMyAzLjd2LTQuMmwtLjEtMi4xLS40LTEuNWEzIDMgMCAwIDAtLjctMWMtLjMtLjQtLjYtLjYtLjktLjdsLTEuMS0uMmEzIDMgMCAwIDAtMS40LjNjLS40LjItLjcuNS0xIDFhNSA1IDAgMCAwLS42IDEuN2MtLjIuNi0uMiAxLjUtLjIgMi41djQuMmwuMSAyLjFjMCAuNi4yIDEuMi40IDEuNi4yLjQuNC44LjcgMSAuMi4zLjUuNS45LjdsMS4xLjFjLjUgMCAxIDAgMS40LS4zLjQtLjIuNy0uNSAxLTEgLjMtLjQuNS0xIC42LTEuNmwuMi0yLjZaTTUyLjIgMTAzLjF2LS45YzAtLjYuMS0xLjIuNC0xLjguMy0uNS43LTEgMS4yLTEuMi42LS4zIDEuMi0uNSAyLS41czEuNC4yIDIgLjVjLjUuMy45LjcgMS4yIDEuMi4zLjYuNCAxLjIuNCAxLjh2MWMwIC42LS4xIDEuMS0uNCAxLjctLjMuNS0uNyAxLTEuMyAxLjItLjUuNC0xLjEuNS0yIC41LS43IDAtMS4zLS4xLTEuOS0uNS0uNS0uMy0xLS43LTEuMi0xLjItLjMtLjYtLjQtMS4xLS40LTEuOFptMi0uOXYxbC4xLjhjLjEuMy4zLjUuNi43LjIuMi41LjIuOS4ycy43IDAgMS0uMmwuNC0uN2MuMi0uMy4yLS42LjItLjl2LS45YzAtLjMgMC0uNi0uMi0uOSAwLS4zLS4zLS41LS41LS43bC0xLS4yYy0uMyAwLS42IDAtLjguMi0uMy4yLS41LjQtLjYuN2wtLjIgMVptNi4yIDEwLjV2LS45YTMuMyAzLjMgMCAwIDEgMS43LTNjLjUtLjMgMS4yLS41IDItLjUuNyAwIDEuMy4yIDEuOS41LjUuMyAxIC43IDEuMiAxLjMuMy41LjQgMSAuNCAxLjd2MWMwIC42LS4xIDEuMi0uNCAxLjctLjMuNS0uNyAxLTEuMiAxLjMtLjUuMy0xLjIuNC0yIC40LS43IDAtMS40LS4xLTItLjQtLjUtLjQtLjktLjgtMS4yLTEuMy0uMy0uNS0uNC0xLjEtLjQtMS44Wm0yLS45djFjMCAuMiAwIC41LjIuOCAwIC4zLjMuNS41LjcuMy4yLjYuMyAxIC4zLjMgMCAuNy0uMS45LS4zbC41LS43LjItLjl2LS45YzAtLjMgMC0uNi0uMi0uOS0uMS0uMy0uMy0uNS0uNi0uN2wtLjktLjJjLS40IDAtLjcgMC0xIC4ybC0uNS43LS4xIDFabTIuMy0xMC40LTguMyAxMy4zLTEuNS0uOCA4LjQtMTMuNCAxLjQuOVoiLz48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuOSIgc3Ryb2tlLXdpZHRoPSIxLjUiIGQ9Ik0xODcgNTRjLTE1LjUgMC0xMy43IDU2LjEtMjkuOCA1Ni4xLTEzLjcgMC0xNS45LTIzLjgtMjYuNC0yMy44LTEwLjYgMC0xNS4yIDMyLjctMjguOSAzMi43LTExLjEgMC0xNy4zLTYuNi0yNy45LTYuNiIvPjwvc3ZnPg==", + "description": "Displays historical efficiency values as a simplified chart. Optionally may display the corresponding efficiency value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'efficiency', label: 'Efficiency', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'efficiency', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Efficiency\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 30;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 30;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":60,\"color\":\"#FFA600\"},{\"from\":60,\"to\":80,\"color\":\"#3FA71A\"},{\"from\":80,\"to\":null,\"color\":\"#305AD7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Efficiency\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"trending_up\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"units\":\"%\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "productivity", + "effectiveness", + "performance", + "capability" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_efficiency_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_efficiency_chart_card_with_background.json new file mode 100644 index 0000000000..fd1172863f --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_efficiency_chart_card_with_background.json @@ -0,0 +1,32 @@ +{ + "fqn": "simple_efficiency_chart_card_with_background", + "name": "Simple efficiency chart card with background", + "deprecated": false, + "image": "tb-image:c2ltcGxlX2VmZmljaWVuY3lfY2hhcnRfY2FyZF93aXRoX2JhY2tncm91bmQuc3Zn:c2ltcGxlX2VmZmljaWVuY3lfY2hhcnRfY2FyZF93aXRoX2JhY2tncm91bmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxMTIiIHk9IjI0IiBmaWxsPSJ1cmwoI2EpIiByeD0iNCIvPjxnIGZpbHRlcj0idXJsKCNiKSI+PHJlY3Qgd2lkdGg9IjE4OCIgaGVpZ2h0PSIxMDAiIHg9IjYiIHk9IjMwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii44IiByeD0iMyIvPjwvZz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNMjMuNSA0Ny42VjQ5aC01LjN2LTEuNGg1LjNaTTE4LjcgMzl2MTBIMTdWMzloMS43Wm00LjEgNC4ydjEuM2gtNC42di0xLjNoNC42Wm0uNy00LjJ2MS40aC01LjNWMzloNS4zWm0zLjcgMTBoLTEuNnYtOC4xYzAtLjYuMS0xIC4zLTEuNC4yLS40LjUtLjcuOS0uOGEzLjQgMy40IDAgMCAxIDIuMy0uMnYxLjNsLS4zLS4xYTQgNCAwIDAgMC0uNCAwbC0uNi4xYTEgMSAwIDAgMC0uNC40bC0uMi43VjQ5Wm0xLjYtNy40djEuMmgtNC4zdi0xLjJoNC4zWm0yLjcgNy40aC0xLjd2LTcuOWMwLS42LjItMSAuNC0xLjUuMi0uNC42LS43IDEtMWwxLjYtLjJoMWwxLjEuNC0uMiAxLjMtLjgtLjItMS0uMWMtLjUgMC0uOCAwLTEgLjMtLjMuMi0uNC42LS40IDFWNDlabTEuNS03LjR2MS4yaC00LjN2LTEuMkgzM1ptMi43IDBWNDloLTEuNnYtNy40aDEuNlptNS4xIDYuMi43LS4xYy4zLS4xLjQtLjMuNi0uNWwuMi0uNmgxLjVjMCAuNC0uMS45LS40IDEuM2EzIDMgMCAwIDEtMSAuOWMtLjUuMi0xIC4zLTEuNi4zLS41IDAtMSAwLTEuNC0uM2EzIDMgMCAwIDEtMS0uOGMtLjQtLjMtLjYtLjctLjctMS4xLS4yLS41LS4yLTEtLjItMS41di0uMmMwLS41IDAtMSAuMi0xLjUuMS0uNC4zLS44LjYtMS4xLjMtLjQuNi0uNiAxLS44bDEuNS0uM2MuNiAwIDEuMSAwIDEuNi4zLjQuMy44LjYgMSAxIC4zLjQuNC45LjQgMS40aC0xLjVsLS4yLS43LS41LS41Yy0uMi0uMi0uNS0uMi0uOC0uMi0uMyAwLS42IDAtLjguMmwtLjUuNWMtLjIuMy0uMy41LS4zLjh2Mi4xYzAgLjIuMS41LjMuNyAwIC4yLjMuNC41LjUuMi4yLjUuMi44LjJabTYuMy02LjJWNDloLTEuNnYtNy40SDQ3Wm0tMS43LTJjMC0uMiAwLS40LjItLjZsLjctLjJjLjMgMCAuNSAwIC43LjIuMi4yLjIuNC4yLjcgMCAuMiAwIC40LS4yLjZhMSAxIDAgMCAxLS43LjIgMSAxIDAgMCAxLS43LS4yLjguOCAwIDAgMS0uMi0uNlptNyA5LjVjLS41IDAtMSAwLTEuNS0uMmEzLjMgMy4zIDAgMCAxLTEuOC0yIDQgNCAwIDAgMS0uMi0xLjN2LS4zYzAtLjYgMC0xLjEuMi0xLjZzLjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEuMS0uOGwxLjMtLjJjLjYgMCAxIDAgMS40LjJsMSAuOGMuMy4zLjUuNy42IDEuMWwuMiAxLjV2LjdoLTUuN3YtMS4yaDR2LS4xbC0uMS0uOS0uNS0uNi0uOS0uMmMtLjIgMC0uNSAwLS43LjItLjIgMC0uNC4yLS41LjVsLS40Ljd2Mi4zbC41LjcuNi41LjguMWEyLjMgMi4zIDAgMCAwIDEuOS0uOWwuOS45LS43LjZjLS4yLjItLjUuNC0uOS41LS40LjItLjguMi0xLjMuMlptNi4yLTZWNDloLTEuN3YtNy40aDEuNnYxLjZabS0uMyAyaC0uNmMwLS42LjEtMSAuMy0xLjUuMS0uNS4zLS44LjYtMS4xYTIuNyAyLjcgMCAwIDEgMi0xbDEgLjEuOC41LjUuOC4xIDEuM1Y0OWgtMS42di00LjhjMC0uMyAwLS42LS4yLS44YS45LjkgMCAwIDAtLjQtLjVsLS43LS4xYTEuNiAxLjYgMCAwIDAtMS40LjdsLS4zLjctLjEuOFptOS43IDIuNy43LS4xLjUtLjUuMy0uNkg3MWMwIC40LS4xLjktLjQgMS4zYTMgMyAwIDAgMS0xIC45Yy0uNS4yLTEgLjMtMS42LjMtLjYgMC0xIDAtMS41LS4zYTMgMyAwIDAgMS0xLS44Yy0uMy0uMy0uNS0uNy0uNi0xLjEtLjItLjUtLjItMS0uMi0xLjV2LS4yYzAtLjUgMC0xIC4yLTEuNS4xLS40LjMtLjguNi0xLjEuMy0uNC42LS42IDEtLjhsMS41LS4zYy42IDAgMS4xIDAgMS42LjMuNC4zLjguNiAxIDEgLjMuNC40LjkuNCAxLjRoLTEuNWwtLjItLjctLjUtLjVjLS4zLS4yLS41LS4yLS44LS4yLS4zIDAtLjYgMC0uOC4yLS4zLjEtLjQuMy0uNi41bC0uMi44LS4xLjl2MS4ybC4zLjdjLjIuMi4zLjQuNi41LjIuMi41LjIuOC4yWm02LjcuNCAyLTYuNmgxLjhsLTMgOC41LS4zLjZjMCAuMi0uMi40LS40LjZhMiAyIDAgMCAxLS43LjVsLS45LjJoLS40bC0uNC0uMXYtMS4zYTYuMyA2LjMgMCAwIDAgLjMgMGguN2wuNC0uNGMuMi0uMS4zLS4zLjMtLjZsLjYtMS40Wm0tMS4yLTYuNiAxLjggNS41LjMgMS44LTEuMS4zLTIuNy03LjZoMS43WiIvPjxwYXRoIGZpbGw9IiMzQjkxMUMiIGQ9Ik0yOS43IDk2Vjk4TDIyLjEgMTE2aC0zLjNsNy42LTE3LjNoLTkuNnYtMi42aDEyLjlabTE1LjcgOC40djMuMmExNSAxNSAwIDAgMS0uNSA0IDcgNyAwIDAgMS0xLjMgMi43Yy0uNS43LTEuMiAxLjItMiAxLjVhNy43IDcuNyAwIDAgMS00LjkuMiA1LjMgNS4zIDAgMCAxLTMuMi0yLjZjLS4zLS43LS42LTEuNS0uOC0yLjUtLjItMS0uMy0yLS4zLTMuM3YtMy4yYzAtMS42LjEtMyAuNC00IC4zLTEgLjgtMiAxLjMtMi42YTUgNSAwIDAgMSAyLTEuNWMuOS0uMyAxLjgtLjUgMi44LS41LjggMCAxLjUuMSAyLjIuM2E1LjEgNS4xIDAgMCAxIDMuMSAyLjZjLjQuNi43IDEuNSAxIDIuNGwuMiAzLjNabS0zLjMgMy43di00LjJsLS4xLTIuMS0uNC0xLjVhMyAzIDAgMCAwLS43LTFjLS4zLS40LS42LS42LS45LS43bC0xLjEtLjJhMyAzIDAgMCAwLTEuNC4zYy0uNC4yLS43LjUtMSAxYTUgNSAwIDAgMC0uNiAxLjdjLS4yLjYtLjIgMS41LS4yIDIuNXY0LjJsLjEgMi4xYzAgLjYuMiAxLjIuNCAxLjYuMi40LjQuOC43IDEgLjIuMy41LjUuOS43bDEuMS4xYy41IDAgMSAwIDEuNC0uMy40LS4yLjctLjUgMS0xIC4zLS40LjUtMSAuNi0xLjZsLjItMi42Wk01Mi4yIDEwMy4xdi0uOWMwLS42LjEtMS4yLjQtMS44LjMtLjUuNy0xIDEuMi0xLjIuNi0uMyAxLjItLjUgMi0uNXMxLjQuMiAyIC41Yy41LjMuOS43IDEuMiAxLjIuMy42LjQgMS4yLjQgMS44djFjMCAuNi0uMSAxLjEtLjQgMS43LS4zLjUtLjcgMS0xLjMgMS4yLS41LjQtMS4xLjUtMiAuNS0uNyAwLTEuMy0uMS0xLjktLjUtLjUtLjMtMS0uNy0xLjItMS4yLS4zLS42LS40LTEuMS0uNC0xLjhabTItLjl2MWwuMS44Yy4xLjMuMy41LjYuNy4yLjIuNS4yLjkuMnMuNyAwIDEtLjJsLjQtLjdjLjItLjMuMi0uNi4yLS45di0uOWMwLS4zIDAtLjYtLjItLjkgMC0uMy0uMy0uNS0uNS0uN2wtMS0uMmMtLjMgMC0uNiAwLS44LjItLjMuMi0uNS40LS42LjdsLS4yIDFabTYuMiAxMC41di0uOWEzLjMgMy4zIDAgMCAxIDEuNy0zYy41LS4zIDEuMi0uNSAyLS41LjcgMCAxLjMuMiAxLjkuNS41LjMgMSAuNyAxLjIgMS4zLjMuNS40IDEgLjQgMS43djFjMCAuNi0uMSAxLjItLjQgMS43LS4zLjUtLjcgMS0xLjIgMS4zLS41LjMtMS4yLjQtMiAuNC0uNyAwLTEuNC0uMS0yLS40LS41LS40LS45LS44LTEuMi0xLjMtLjMtLjUtLjQtMS4xLS40LTEuOFptMi0uOXYxYzAgLjIgMCAuNS4yLjggMCAuMy4zLjUuNS43LjMuMi42LjMgMSAuMy4zIDAgLjctLjEuOS0uM2wuNS0uNy4yLS45di0uOWMwLS4zIDAtLjYtLjItLjktLjEtLjMtLjMtLjUtLjYtLjdsLS45LS4yYy0uNCAwLS43IDAtMSAuMmwtLjUuNy0uMSAxWm0yLjMtMTAuNC04LjMgMTMuMy0xLjUtLjggOC40LTEzLjQgMS40LjlaIi8+PHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjkiIHN0cm9rZS13aWR0aD0iMS41IiBkPSJNMTg3IDU0Yy0xNS41IDAtMTMuNyA1Ni4xLTI5LjggNTYuMS0xMy43IDAtMTUuOS0yMy44LTI2LjQtMjMuOC0xMC42IDAtMTUuMiAzMi43LTI4LjkgMzIuNy0xMS4xIDAtMTcuMy02LjYtMjcuOS02LjYiLz48ZGVmcz48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2MiIHRyYW5zZm9ybT0ibWF0cml4KC4wMDUwMjUxMyAwIDAgLjAwODk3MzQ0IDAgLS4yKSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMTk3IiBoZWlnaHQ9IjEwOSIgeD0iMS41IiB5PSIyNS41IiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIyLjMiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA4OF8yNjI4NyIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA4OF8yNjI4NyIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFJQkFRRUJBUUlCQVFFQ0FnSUNBZ1FEQWdJQ0FnVUVCQU1FQmdVR0JnWUZCZ1lHQndrSUJnY0pCd1lHQ0FzSUNRb0tDZ29LQmdnTERBc0tEQWtLQ2dyLzJ3QkRBUUlDQWdJQ0FnVURBd1VLQndZSENnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ3Ivd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhRQUFBZ0lEQVFFQkFBQUFBQUFBQUFBQUJnY0ZDQU1FQ1FJQUFmL0VBQnNCQUFJREFRRUJBQUFBQUFBQUFBQUFBQU1FQVFJRkFBWUgvOW9BREFNQkFBSVFBeEFBQUFCM1k3TmR4YU5XcnNPRGE4d3owenQrc1dOc09LcmJHTzVNUUhxaGlrcWVUdS9aSjRpZHFlTDZrM080TFNQVXlqY2JidFdlMTVxczVsRGxIWVNCMmUwYzFvSEZ5YXEwMDBtSEpYckdTR0lxWHdLWmUxWnZ1SlNyNVpqNk8rbnN2V0pxazJSRUVWaTFkcXhCekh1ZS9aclhFazFuTjNXVGs0VmxZN1lGellxdzBsVHVPSzJLa2VwUzBhTW41VzB4UzVDWWZtSncwdjRtczJRUnJhSkdKRmxTVnNBOUExSjVJT21ockl0b0VoRTlRVlJtWklLdGZKNXBCYll3Q1RRaVBjTjltT255aHhpTHNnWmxMaitvVFVHYUxFWXZZVWJ1bmx6WGNLS2xxZmxlaGdBUDVJSjkzZXJCOVdxYzYvbVdBUU1mb3FjOXFNSG9yR2RMV0NpdVRvdzFtcGpBaFdKMytKRngwOHMxUExQVDdPZlpBaTlseUNGbHkxZXdmWVFDK2pxQ2IyNlN1Z1BSbER2UDJueVF4bW1kOU9qWEhNYXpOQkpZaXRjL2R1V3BYUWtVZTFNMHNFYlpnaFF1d2FCTTdTcXZ0Y3RnYjBHbFRWcTgxN3ZURTluNjM3WG9kVnVQaSszN0g1Z3l5ZzBYVktZOFl6aVM0RjM2QzBkMWlWbFhsdnFvU0FXdnVzNmhXMlJHaFlJWFJRdXBGL0xDSFZTMXQ4NTd1UEU1c2d0bmpoMEQrcFc0dDdMNWtXT1o1dXl2VDJUbEZaT1ZEUEVCTVU4YlBvOHJ0QlVtQXhCRTQ0Q1NlRVRhaXpaZ1h3N1h2a2NjT3lsVWJXUEY4SzZPUlY2TlZlOTVIb0lMMW56MDliejR2UVFxNlFoVEU3U2pEV1FlSDgzMHJmOEFUZUY1QzcyTHJ4eS90V3cyZG9xSUpweDFOOUxNbG96V3pnVXNxWmVLT2xQUjlNNnFyNExoK2s1MGk5VDBnOUQ4c1lqeUgyaWhWT0RrZmR2Sk5PUE8waVBodys3NStoemRFODBqWDl0VTFFVEZhTjZKZnlHaGFITjBybk5vbXk5MG5uNlduYWRDazZuWGhQRy9RNjhCMXJPL1J2aTQvRXlqQzlhSk1TVjV1bFh0T1FLVXk5Rk5tR2xHUlZhMU1wWG1BWENOT1Z0RDJveVYyYmM1V3hmNXRJaUR3eUErSzFSdWhZWlkwUU12T3VqOSttc21Rbmxnd0pLazZXNkh4dzdTWEVpNzFxSmFNeTdsZmRETlE3aVJTTXN2UzR6Y2JYVWNzam42ck9ZU3NjR3htS01kN0RGQ2d5NStmVFBjNzlGTHJjaVhOWGhpOGJzMzJyUTM2RHNlT3dKRnEyWitub2FtYlVyVngxZXlzV0JQTzF1RkZDMDFYTEk1dXBkNWhGa0F0RGp0clh1SmhMV3JyODlDTU1aWnNEMDhNU0lGWXRCNklaK2hKUndjS2JRS01tVEM0dGdlbVMzcFBMMWQxTWhVc0xGd2p5TWNOM295bFhMSlorcGZ3NkpTdGNiR2ZTdEFJdWRDM25sbnBMM3R6bXVabS9qemlHaVBNZzZTSzNsYXlGTW9XOVFlVW9XWTdGOUdzUFZlTjFpQzZGTE1YRWRVQXhYeDlQT3hSeERNQTYyd1BlWHZUMURVRXlVVkxRYW9idm4xT3JvZFVNYlFyN3FJUjZ4bFlhai9BSUxLMHNKTUkyVnpOSlJ3U1NYYXJqNlB6STFhbmFITzB2dW5tMlFaRGVpYkpRbTd1dnFaUElDMHdVMFF5OVZ1Y1ZUZlFlZFdxbWgxRXlOR2xtNWpTT2U2ZVZ1VUV0dGRJbEt6K3pkSk1zaTBtVmswL25TbEw5VE03U2k2RXBFeXNkMWxPR3BJUk4xMVMybUIxS1U5UVF1RmRNaHEvdDRhSUxEcHhQUnM1L0ZEMkZuOHMxRWpZbDR1Rnl2WlJCNUtOQkk2V3BmdDRXOWVveE5aZ0xCWjBCTTFNS0UvWW5wQ2c3a3J5VFIweVVWMXNZQ0Qxc3BPYUNFZlNiWHB2T1lacHlhcFNMemRTMXBaUW5WV0phOHkxTFZKMThYTDNTL1FmMEx0VmxvTHViMFdycTRpcllNMjFXSnF2ZXVnVk9DS21tVzlKNEJMN282UmZNVWJjU2JRVzVlaEtaYU9YRTF0K1RYYWlSczZ1M0VaWjRwNlNxaEc0bzhObENrV1ZWTlZqWEVlNGk5aXVreDllZzdpZ0k0alJjLy94QUE0RUFBQkJBRURBZ1FEQkFrRkFRQUFBQUFEQVFJRUJRQUdFUklUSVFjVUlqRVZJeVF5TkVGQ0VCWVhKU1l6TlZGaFEwUlNVMVJGLzlvQUNBRUJBQUVNQW8xbkluVFVsVFl2UlA0cFJiK2Q1TnRIRW1FV0JSNnJDWkpFMnFzV2locDllN0xOdjFDNW9SZUl6Wnp3ejhyMTlhcmt0MndsWElIOGxNYitsY2lMc1ZjWXE3NDdKcnV5NFJucmRuSC9BQml0L3dBWjNUdHRtcExSdE5BNndtY2ozb2xqUnEvU2RlclRUYnpWMGFQcnc5TXhIT0o0VDZxZ2FzalNKc0VMeHQ4Uk8yanAyeUprVDc2dmZMQmVNaDJhRFJGR1pjMlRESzNJTzNKVnlZNzVTNVhvbmwycmlkODdmam15WjJ3RzNYeGpzY3ZiSmlic1hGTXhIdVpoSmJHdTQ5c1J5OGxRakVURlVlMlR4TS9YY1JiTWpHaUZxNnFnYW1QYTJST0pwZEpwd1ZqTm56SkNOZjRaNmMwL3A0MWg4RU9odzYvaXhFMHBMZWdWYXNFWDFpNWFKOVcvTkF2QzBKa2U5RVhxeC84QXNUQ0ZnN2QzSmtWUnVlNVJmWm11K1ZrSzBqQ0ExajA3cGR3dnhYUGprSC9rdUxmUXZaTjgrUFJGeXZuZ21uK1ZpSXUrSzN0a3JDU2E4YjNJb2U1SlZjdnNMdVNVRmlxNVJidTFmWCtJVnZZcTZwc1VCRmQ0VzY3dG5xNnl1UnV4L2dwT1h1K2VQbk5lMmJJbXgyT3pRMEphOWh3cTVyczhRWGZ3aE1YSy93Qzk1Y2ZmeVpwV3RXWVFoL01qYmlVaFAvV0xQZ3ptcDNtQ3lBem9vbytXK1dEL0FKZTJSNmtMMjd1a3BqYVlTLzYyT3BBZituRnBRcDdTc1NwYjFPcDF1MVlCa0VxcXd2TEJQVEY5c2w1SWpmVWxKdjI4c251OSsyZVdqL2pJenk0UHdsWWtlTCtlWW1JMkN6L2Y5cERScFpuOU9VSEJDbFJqVlROZkwvQ1VyS3dlODFNdWs0VHlOM3pSY2dJK3NONUVSV3pBcC9ySmhaQTFiMkprUmQ5M1pmNncxVkRsRlltbWZreHZGRFZrZ1NHZzBybk0vYVRyOTN0VEZ5VDRtYXRoeFNUTHlES0dDRDRqYWhteEJ5b0ZkSmVCTmZhazluVmhNazY2dG9NTHo5akFVSXFtMzFmSWtEUTlHeGdmd3lWa2lVeERFSHd4VGMveVp4L3hpTlRiN09UNGtrMHRIQk9SakNpUEhUaDVwNjVZVGVuSk9Sc2RGWFR4eUc1RWN6WmRjci9DOGhPV1Z2MzFtWDZmdlkrYVRnaU1ZcG5xdTdLOEczWlZ4MFVLSjIzeU9GdzJlM2JYVng1U0krRUVuRWxZdGxWeFd3WVZ0SUdGa3E1ZC93RGJsWWNFbWZIV0xQbkhPT3Zqa2l4MnhZc2tveGhqU3lkdlBtdzJnNjYxMHlraDlqTVBta3JaNUdmRFpSVmNUOHVTY2tzYjFuOXNScWYyeE52N1p3My9BQXg0MktxOGt5UUZIcDAyTzJXYjZwWnQ4b0UyRzdOYXFuNnJueXFUNjFtYWcvcXg4MGN4K3huSW1kSnpkbGFpNFF6NHZyZVBJVGtNTkZGQU9tYWpteXBHcUpMVHZjcUVScFBjYmNZTkVUN09hUnJZYyt4WUthSG15MmpCaVR5QmpqUnJSdVZxOWx3YythdnBTUTVFcFRuVzZDOVNac3ZGTU8xZHNsSjlROVAwTjJ4TUoxT3V2QkZ3aHZmMUp0cXEyK0NRNTFuMCthNkExR3kwcFVuVEpNWlg2MldPL1Nobk5rTjVWZjN0bTJYdjlUTm1qWE5RWmVTcGdKTWZmYnJzeVVhTVVPNlNHTGxhWVlLeFpKSHJ0TThuWlh4WmdseXpXR012Q0FucFk4aS9seXRtVDRoVUpEY3FPa1NERmNwWmkrcUlnWHVUbDdTNDlRSVkzeENzM2ErSkdramt4bmI0S2JHZkZZVk9XemlBZDNlVGJKa0NEdTk2VGZVdTM2T1dFTTBQY2prVEdHak9idnlialhCZVZTTEpEbFk2TVVhdEFScnMxb2pHYWFNaWpUZW4yODZQdmwvczIxUHRtbW9acFhXZXhVeURSdFk5L24rQ3RkcGdzY0tCZ0NFd0oxSEIwWEo4dzlyVXE1TENPY2pCRVZMR3hoQWp1akNzbkFja3VzY25KK3FaZktpMWZRdzRIdzZUYWxWMnE3MnRtSFZ0WkllOVBEeStnMERwYjdDYTkySDFmcDA0VERXZEtJaVdjYUVGaWRHUndqV21xbHF3RmkyZm9TZnJkWXJtRWxBYStKTDFvU1Z6c3JVUkE3ZjUvUTVmN1pkS3ZTYi9BSHJ0UTNkaHF2NUJPYlJ2aURaMVhUR0xsTmI2ZmQ2SDJ3M0cxNzIwd2JiS25iem84MUN2NzNrNXBHeUVBaFk2c0lxa3MyOXQ0NXNDOGs0UDBnOXMxeUJ5K0dka0g4K2xhNXlpWHRtcFlqVTgyMWNXRjZTS2lya1NFeHBEODk4YlhrWXhpRmJ4ZENoZmJSZDgwd0JyalRrY21HQzF6ZmJmTk1WLzhPUkhZUWJXdFZ2REh4aTgzY1Jyc3NZLy9VdUtFdjhBd1hGQ2IvZ3VIaEdNNVBSajlGMDlSTStJVjBRWXpGc3JLQTVZWU5NZFppMmt3ZnpDNlhrQ3pXTWtrelNKaitVVUNWRzZ6aFpxTGRiU1JuaFVzeDhpVXdmSGhZMVo1RE9Yc3Myc094M1UyUG1wU3ZKU1NBb0tlNUtSNUFSQ3RFemQxdkpmSVBJWS9raitrWFkzeWtYR2drdkljWWhzUnRqRWZIbCtXQXh2QUlpK3JzMXVWMWtzT1dZU1l3cU9HMXJTYjVwWlYvVnlKdmtuYmpnaytVaTRYN09QeGZmSk1xTEZSRmtTR0R6V3ZpUjhDdmk2Y0JDaHBueE9XR1N3VGVIR1Jac2VGV09JSE5XMk10K25sRzZTMGc2cHJVbmlUTlEvMVdUM3p3c1dSNUdROGNoR0lJOHRqMTZ4ZXBuaUZkbXA0SVREQzBiZFpYODJySDhNalhNU3lUVHdkcEd6dmZWelViWXlkc1VxdGFScU5UQkhYcWtieGJrNlJ2UDNSaUlpRzlLcjJ5aWh4cGN5UTQ0VWRnMGF4R294dmJUTjJ5Y253dS91ejF3dE56WlV1dUtoNVN5R2gva3BoUGJDTHRsalBCWHdqVDVDL0xxZmdzcXAvYVI0aVBRcGJHUlhUWmttZExhUTdoNnAwOGkrbXpJM1AxcnAzZHZqVWpMblVZVHhWcVhXcnpPcTNwNTRPYWhkdlp5RjN6dzF1aFFvQjQ1TmtRbXBZcUo5dEZ5MDFiVklCd3p1WTVwZU5nam1WMVl3SUl1d2pkZHJzMUhQYkxsbkp4Vk1WQmNIWUJBb1EzdGx0MDNUM09WeWJzNDhVWGZLQ1N3QjVPK0k3ZEV3VWFBZlRkZVdYRGFiQ05ZTWZFYlVSQS95VXdpOXNPNzhNOFEzeURhTXRoeGMxaVZUZUcxT3didDBvdEJhS1BYQ1BOakdRbG5BOElvQk9rSzRuR1dZend6REVVOEdWYkxJMCsrWWZVQ1M1TDN2eXFkOVlMTDUzN3hQbW1MS2lkYXBUV1ZpWVJwbW10UHhIaVllUk5WYXF0MHJFZDFSUUVjNnhLemdxdFhLOXd5RmEwaWJwcVN2alI1Y2xRQTQ1OE50Q0RlNXNBanNqMTA5Q0ZjNnVKaDZ1ZThxeUJSRGt3VlBjcWlOZkFPbVVWYjE1a2hMR0l1N2thSnFkTnFOU2tGNWpUVU5uTFpUYnRGeDN3SmhkRk55cGhwVWIvdlpreVpIYWp2bnN5N3VxZDFWSWhubmlUUFBBdGREVjlHc3I1OUZRV0lZVGZNeGhjSzdURTJhUnpCeUhKa3JSdDVEUlN5SlErR21wY2xkUkNodk51bFE1Zk1qeTgzOCtiZGM4T2FxdHNOZm5OTWlzSTdXbGpBcDN3cGM0L1RaWDZwalRDSVlmTm9UV3BqV0hsU2VsRzM4S3JzRURNa3NHOEpZbGhmSTdtaEI2WDBWWnZVRnlGZ25CbHA1V01TUjhKQXFhYzFJM1VEaStUcVJEelZXbVoyb21pUkN4dzVxZUF0TllQaUdlMVZCcWlEYVdMYXVtTWtnMVRIZENxNDhWenQxay9ZVmNsUDJPL0R2N2UrV2J0MlpZbStXNFdQSEdpL0QwSFNxQjFENUJ3ZWNwZlhZNmlxUVJDQlpGNHZERURmMENKS3RCTWFtaDlQVWNsdGxGdTNtUFRyOVNQTHRmcmpkODhMeW96eEJtWnJ4bGZZSkJqMmdYT0dseU1zaEtpdFhpaEdSVzJrTmpCcDB0VnlvbGhPTEthSnVhT0ZadnNXeVJiT2p3Mk4wL0daRWNuVk9SWTFzM3lVcHZUZmN6TlIyV3AwMFZwK2FzUjBqdzZ2WVVWMHVpMTNZUGxUdFFUcjNUUXB2VTZKOUQya2FwbnNucU5GV05KRk1qRGt4eWNtU1YrV3VTM2ZQZmtoM2JKM2RtWE96SkRrVEpsckplc0NNYmkwV2xKdEVlcEhKREg2bUJsRVl5YmVMRFh5OUZaUnlWUG5PQXg1TE4xb1hOT3NqS243eVBMdGRwaHM4TkhxbXZwVGQyYmVJTFpMeHhFajluUUljWVM4SGQ4MVBZa2dqQU1ERXg4RnN3ZnFLNXVVamkwaGZMams3amhhc29yT3ZXZE5zeENPM1VGRkVocFlRYllKU1NpMk5mcXRtcnE4YkRMKzB5SmE5ZU5RMUJYVFJVNGE2cVpUeXBIVUpCcTQwY3ZXRnpWZkRXZkpjWTFjNDI0cEsraGNsZHp2dy93Q09UUHNybDhpZGJmTEpqK0tPR3pmTlBlS2x6cHlwRlRncHdPSFMzTVdkOFNTUUpmSXk2Z0xReGloczJ5TStPVVI5SGtySDZWbnBNb0pBNUNBSU5kMHQzZlZGenc0NktlSVVwL1AxNjVsSTFrTkVUZFlrdVE5M3BoYkpKcDJYREdpbkkxRWtTQ0JzanhvbGk5Qjd6Rjk1aXJrMEJDUm5qNjJhYWlGNlROcEhGYmRwZ2syZEs1WlV3anVjVThNemtJbFplN1BjNjFjbU1peWtsTVllK0k3S3Z4VDhNYWhGYXhYeFhUZkhqdzdSZWt5WEpka0xYT25MeWQ1V3VuN2trUFhiSmoreTVicHV6ZmppczVMN1o1WkZSY3R0REhQQWRQbVNvNFFlSHQxcDNUTXQwVWNIelNXbmphT2lseW1TcUJjMDgvcTlNbkJHWmI5cEpNaXJxR0xxVWxscDdwOVMwSnJPV3FUN0kwWm1WbHBxNmJEYk9yUEk5UDR6NGdON3ZTQW1TR2VjUDFKUGR6WVFHZm1ka1lFZFRJeFI3NEhwN0txSWladzZ6MEh2bGZFYkVGMG05ODZ2RmhIT3luZTBsc2VUeDlVK3FNNFpiTmcySUtnUzFzSzV2a0JVN0dSNlBVRVU3alJMOEFYbmc2b0wyTnF5Uy9DYWZsdjd5TGVRdUxSc0N2M2d5NHlLb3Zza0xnUk5SdnI2bWFQMEJBZnRDdHZuSnFTcjA1NGEyQ1hVWm5UVFdWM0MxVnFNZGtLUHdaU3FqT2lqUGEwWDV6OHNydWRWM0J2SnlYTXlUcWlkT0M0TW1XVnpLL1VsaFdoNk1Ram10L1cyMGV4Zk1IZXFNZnlibTZ2OXNhcm1xMTIrQk0zYlpxNEVuRXplRytSNVBvM2RoNS9PSVhnbVVxOU1aRDRlZE5kR1dQNWgzVHFyV2RUeTBtMTUxWStsMUJDdWdPT2tMaVVrbHBBS3NVTGVUT3AwZnFoc1I1ZURXOGRzY2pkL2JJb3h2Ym4veEFCQkVBQUJBZ01GQkFVSUJ3Z0RBQUFBQUFBQkFBSURFUklUSVNJeFFRUVFVV0VVSURKeGdTTkNVbUp5a2FHeEpETmpjN0xCMFFVVk1IU0Nrc0xTTktMaC85b0FDQUVCQUEwL0FvdXpCMGFFUjJIYWhBUHRPaWpMTE5OaWkwZkZoR2tYak16VmU2ditGTHFWZFNLOFE5bGgrbkVPUzdaYVhTME5UajRsUXpFdEEwQXpBRlhGTkRmclMyZXZBM0todC84QVVGWHVyL2hVOVNwRmFTTzZIc24wQzBNaGFFNHZHVWtIUkJFWVdZbVgwdFlPT0VUOFZHTHFuVWpENHlVZDdEaEF3bTlTYjV2ckt2ZFh1N2xQZEwwVjdLOWxkeTdrQjFLcnpTdE1HNm1RaHNpRDNyemJYYUM3NFNLMUpqT1ArS3Rva016NzA1N1RoVkxmeGhWcWFGMG5MdlhlVURuMU81ZHlieVRodzZoY1YzTHVLOWdyMlN1Y01xMmRPU3V6V0g4YXJVMVZPVythaGsrVkRYRzVhT2JzcElQeFg4bVAxVGUwNWtCbDN4VDJ6aHZmQ1plUGV2NWNmN0pwSGxLUDBLSkZUdVh2M2g1NmtwT0RZa2x6bWc5MHNTZVJNVHVGeXdmaVZZK2FyVTVJYzkyaFVScEJJekUwenNzYkV5WDN4VHUweUpGSkJUTG1zWkZJQVgzeFFmNWVFK1BoNUZReGhjN053MzFucVRYRldoK2FxV0Q4U3RBclJWbzgwZUtQb09UWTBnMXlIQnU2ZDRtZzZRRzdVTnVWb045UjZud1RUSTk2Z1ZrTkFuTXAwUy9vdFZHbDJKVFlKVlo0MVdxMVd2YVdtSUtHeW9tZkJSSWhkbXBabmQzSTY3cFl3Q2hJNXB6WmpMY1RNQzdxSGM1eEo4cHhRdmRkY0ViTytYckJXZ1ZvVlBVb2pESnlZSlEyVjVCTTJOMVJKdXlVOHd3eVFkakpoT0s0NHBLMW5hbUU3RHlSbmZRNVJvTGFhV0UwaWVhYzZsODRKd2NoSVhJQUFHeUtlenlRNk8zQ0ZPYlkwZ1pqaFRKYXNHek5hZXBPNmFaWERvY2ZqNzBQczMvb296ZzJIQVpNa3F1RitJSzBDdFNzOExWOTJnTDY3bDBXY3Y2Z2lKUzhFQ1ZZc0t0QVFmRlRpTklCNHpSMk9YdVZxdy85VkpVSDU3cGxkMjhjVk0xUHRqcm5jaDU4UGJXajVoVkNieHRNTXl2Ump3NVEzUG1jMWFCV3BRSERWRGd2c2Rvcy9lbkNtY1RiU1d5bm5MVk5xTFI0SjNhYVlWTjZzUmVTbnhXVno0Y1UyTS81STdIcHlJVWFQQmFYVDdPRlpWS2cvTTlaeGsydDhwbFBmYVAyM2FCV0FPRklSRjU2TzZRWE9DNVJJalFYc2cwZ21hckN0U3JhWDFVMTZyYVZ0TzFOaFJkcGlDYllBOUowbHRFQXlzV1NNSy9QRGNqbXEwSUI4N21uT2hEdDgwWTU4NzFWMFYyUjVoTXNpMmZzb2FCUVcvUk1ka0l3bm1EcW9VWnpJRzBrU01WbWg2a0NFNkkvdUFtdHJmVnNjR0xNdDJlSFBDR0RqelRvNXNnMDArVDBYcXhTUHpSNFJ6K3FFY1V3NG5tcTBDdFNyWWtYTFhKRVh0ZGZOTzdUNkpBcGdjWGQways4VFZnNzVxVU0rTTEwbTcrMWRIaWZNSjFpQmY2cTFrb0xHT2F3c0J2UUZ3QTZuUUlrL2NyT0hUL1lva0ZoL3dDUWNWd1BCZlp4ei9vZ01FTnpzajdrNklYRjVubXF3clFxTzd5VU5ycE5jb2pwU2J0QnZRODZOakk5NnEvVk9ZNjVOTjBrWVJ5Z25pcFF5M3llczA2SnJzOHBZZTVXTHg5VnJOTWJDYzBQSEpORndDc0czb0JkNjlwUzlKUllMbU94Y1FvTzJHRVFNNVgzcXlGRHlYek9XazdrMjZxd2tQZWhMRmFJUkNPOVZoVmxRTmlyaFZlYWFzMVc4VmVDRnpBSUpKZWVLZVpkenVDYVMxd0xzbEVqWEVITzVPZUhVeEl1WUNoc0xwQWo5RkNsVlcvajRLRlBVbWMxQ21DOXVSVCt6RFlEZW1RZ0R1ck84RlBpbDd0cHRxcmJNVHAwVml3REU4OEpxUkdMTUo4cndSTzVWNFllR1YvY3F3cXl2M2IvQUpoVzdzUEhDbU44cEUwWnk3MURxbjgxbE9XYWhSTWVWMDVxekJqT2UvQ3llZ0NjS1lNZUVTTCtmRkJoZnRtMUR6R3B0OU8weEpzZWVISk1qbUh0RXVLaEhIZDhVOXMya2E3cXp2SzJXS2FYdG5WZWpEQXRPam0rVjNCTXd4eVlmWU9rKzlIdDAvczJjdkhWT2lZUTdaMnNBdnl1VllWYU93ZU9hNlFRMTNERGVwM3l5VVF1MHl1UjFDaVJCVVMxQnZsbXZ1QmxxT0tMcW9VSmw4M1QxNEp6REQydUM1OU5RT29QRk1iTDZTNE5Zdy9tbnhER2pQWThDYmlzalZFVEcxTWJ3djNWbnFCMlNnaVRYUkhPbm10cUJaYXVkUzBuVFBKUkwzYkxzWmM2ZzZMWnpURWlNMmN5VXhlcWwwRzRUNW94blN2a095dnZmL0VMMjBaaE1pRU1EdUc2V1NkTytrSWEwQlZ5SmxvdVNxYTU4SVB2SW1zbkN4bjhpaU8wSUYzeEtmTXRZUnY0NzRPMFVoam9raTRhWElpK3lFeUNvekpRUSs0dlBOUGlWQm8wdlZTYkNwTnB3S2dOSk4raWNMcW0zb0xqTmUwanhYRWFvblZaejRxdEF5cThWYW5PS0o1cGx6eWYyY3g4UWUxTUUrS09zQ0MxcTVQa3VjVXI3MHIyMTNwL3BGU0VMbzhGbmE1bE1oRnM5VEpYS2FMUmtuWnRMcy9ndlJEazdnWmZsdjAzRE85YUs5UGlFcWZZMFErUElwbDhlR0JkM2hTdzFyV2pMcWYveEFBbUVBQUNBZ0lDQVFRQ0F3RUFBQUFBQUFBQkVRQWhNVUZSWVhHQmthR3gwZkRCNGZFUS85b0FDQUVCQUFFL0lUcmpNekxvMFFabCtRNVRDaEg2NGNnc0FBL2lNRzUvc3dnQU5DSm42cUFWRG94azNoaUNmUmpBSndSd0hEWHYrZXdnQU13eHAyKzRUZzR4Q0FTSXoyYThFajQyZWhGamVjU0EvQ1pGNWh2VlZtWVdoNGVadHhFNCtCdE1GR0pzNlNSaW45REVobkUvUkNoQU55d1laRFljRzRjUVRwY3dZNkl6L1lDT3BxL3B3UUptdEZRY0dHRUxiN2dvckVoY3J2anNRZ3pROVlkWTJ4bkpCaDVIMkREMHNLUEJ4aGo0azdUazR6VkVnRytZY2RUZW9lNnk0TUkwbnhjUVhSK1VXTGhDYVVZa2RROWQ0L3FFazMzL0FKaHpCb1FpU1VWQ0FZQ3IySlczSWxER3dhL2U0UXNkN0VHUUdZSk1BMklNMVp6RERvWFlWMmpxQnVyaDhjRVBXOG1qREs5Z21MY2hmWC9va0RGQ1NDQjA0SUhtQlBSZ1R1Smh0ci9VY2pPbGdDTnkrV1RYVXhQYktDZEFBWU5CZ3hDekJNV1NQYUhDeEFhQlJ1QmErLzhBNVF1TjYyU09mZVZacHdodHpnaEFBazhaaFJjMWwwV1l3L01pb0NhNWNhZk1BeVNwSjJTSEJ1Q1E1bVJtNFFRbWEyNXBHMStFT0Fzajl6S3pVekFwSWpwUWhVS1l4TnpaUVg0bk1SNU5HR0xQWHBXVElHeStoL2NoNGZXYkZhYUF4VEl4NU40THU2Ui9qaFFOWm9zbEFXNU1mem1hSVBlZXB4RVVndzREb045d1JFZmVXL3ZBWkMxZ3pySTVTejNPMFlITTU1aGp1UHkzTU5pREJoRnE3ZTJJS3BLWUNtVUJraWxIVU1QdEl6WVBtSndPMUZnd0JkdzZRRkg5a3JoWnphcmtqd2RlUmRod1pDMGdlQUhMNkVjbjhzS25BQTBoYlY1bWRJbllQUFloSkZJV2FqMm5nN2lQNFFjVElDeXhQMkU1Z0loeEJhTHJobDVVZXdpOC93QmpBbkovZEVCUnloRHVObjBJUUR3aGRJMmVGQUN6T2M0aGYxRFBkNTBCN2d3RVZFRkNKQVdNQUdraGo3d3VJK0lsazJGUUh5Qm1IdmN3cnFFL2lqcGhRd2lrSDNCZ2tUVWN6aXJpd2x2QjF3cUJPazZsYVFGYWNPZ1FNQWhrVWtqWnhDdFlMUmRHdldIWHcvY2Q3ZDROeHJVVDBJeUNqck9XcElHQmpvN0E2aEJvMFVKN3dWSlViV1NaL1RJQkNzRVNLdUxDU0RoQ0lGclJoS29yS1Y3d1N3alRvZUlYTW1ISmU4SHFBZE9LTmF3MzNMRlJITUZXRDZUSElsSDBxSk1QQ1UvbVY1NncyeGdoR1ZKaGxLODRoSXdoN25JRnpVL1pGRzJJK3NDSW9YQkRBR28xSDBFL0wvQ0pBaVBrT0FzTW1DeEl3SHk5SGNNSUdUeU9iR0lNTFdVellwUzVwVE5rRXUzcEdaaU1CZ1pOOFFGK3hualpEdVhkSUdERFU3UTN5TndGYXpCdXNsekpRTlhVSGFhS29aVWp5NERIMXB0UkNZRE4zR3hBQ21MM3dOU3RzMm9iTVRES1FMUnBwN1JTSVNzU0hzaEFpNFptck5VUE15WEFuTllSZjVKZUpZUnhjYU5lK0ZId0pCSmFoTkprRklDUG9ONTVJV2NBaDRCQ2F6RytvY3BENFFFc0FSMGNNSUpZRjVrQUxQaUNaNTJnK1FVZDFHYU9RZzJWSVVjR3lqWW5pOFZSamljUXlRN2dzdzcrcUZWOFVLOE5wbnh0YlpDcmNBU1JXU0RoOFhoUVZtOFYxOWt0Y2tWQnVCL2JMRFIvbG1iZ0htYVM3OG5oQllBTXErM0tROUNaSkprU25VeEdLR2FJUjV1TXNqV0xoTTRZckp0RVJsUEF4dWZsRHd1SUthWnFNdGVzS0RQZ2I1Z0FrZ0prUTVDZVpkRzFCQlp5Z0lpQkJ3RGVWZFJvUUNsTjBnOG1iaktBd1k1akN6VUxBem1KUjI4dytPeURoTVkzb3hGNmFhQ1ZiT29RelJmN3hhS1p6NWdiR3daMmh0eEhRa1VVQjZGdzNxaUpRSHhOUTdhR09TWkViYWNLZkhtb2RxQjJ3VCtJc3JGRDVReGo0M1gwWjZoQ25Tby9paWVzN0Q3VVZTM0NIYUFpakVDYUg1bVE3SXhNWFJhZWVYdnZjVWw2aFdnbWNRZElQYUw2aGIyNUNpM2RQYURiRnFZWm5nMnFVdUJmc0tTVlY0WFBlS3hsOE9yQWxQR0crOEhqeCsrSGpZSlhnUmdIakFRaVdVbEE5RUt2WWxIeEJ1S3FCWU9FSzUxNVk1bXZXMitLUTEycmZQSDZOamlUQUlBY1EwSkZ3R3I2WEFreUZ5TlFlc2RnWUVvOUVuMGxackZRRUlEd3hKTXkvd0NVVXdNcmtaZ2VqZ290VUdrVU1Md0ZiRXdyQzNYRUNTd0xiL0NORUxwZFpOR0hFdHZFamN2RVdaZjd3ZlFQdWlxckdhT0xWR0ZWS3VoZU9JTjR0TzdXUUV4TlFWNWc0eXdJTE15QmpCUFlrWDZJUkJadDZ5YXpETVVkdG9tc1B4T1JqeU1IVUVBSk1ndHg2enJpR1JOZ2lxN2xpd1ErWUdOMVd4Z1FYeElkRmVHQXhnb1h5aUJKZ0VXT05QV2RTUHNFSGNIdENob2JTS3Rjb0xESTJ0NG94d1BvZzZPNEFjK1gxaEYyRWVZRk1NdHpYVHVOQjdZRTJldmlBMkZlU1lBSURnUVF1Y1BNZmNpSEVkQWpsaUxSakhtV1F1aCt6MDVrSkpMVUk0MkhKVjFLRzFwakRza3VrQnZ6dWNBNkhFZUs1ZUJHVlBYSml0blVlR0lEbVVFT3Arek15K1lmamtFVEg3aWVLcWdLaWdWS2RHTXlPUUFwbWhuaWMrUWxuS2c4Ym5VV1liUFVWZThsTkQzdGlFdHY4a1cybEh2L0FNS2ovQ1FCVDRQcUgycFJoY29lRUlvZThJMjV4Qk1vMUMwMzVodHFtcFVBWE11cWdab085cm1EWjd1Sm9BT2hoM1ptbGhLUVg3Y3JQNjdxTzNySHFnT09sSExsVnVZQTBkUG1CUGc5Y01jRHFHcC9ReStqQ2RGRmdyZUt5QnlXUngxRS93QTBteEZadFlnRVN6RFF5OGxWQm90V3YyVmNvcml3ckNNb0NtdHl1YmdBWCtwM0toeSs1eFhFOVIrSVYzS25Fa3hHNmh1Rzdpd0k4cC9jV0NoRTZreEU0amxFK0lPUlRDeEhsRzQ5SnZRZVlhc0s5Z3NRT0RFdE8yM1o4QUpnM2dXeVRKQkt2aUcwT2FsTXRnb2NRK3JDS3ZnVE9yL3R6YTRMMElacy93Q2tJSm9WRGxHNGhyMVpCdVUydlpHMTVtQXpqZ3hvWWl3RUZ6MVk0SlBVZE5vaGg1cW94bWdCQmJnL1d3eXpORzBPbTVYUTlLaU5reXJwYkJMZlVOWkEwZmNNWHlpTUhobUxtdzVBbkpEak1wRXBLSjcxRDJrZzB4OENVM3dZZ05hOTRObzV5K29PUWhSQUFkVFBFWUJsMmFFZkR4L0JoR0lhdnlSbUVFb1ZsNDRnelp1a2VPQ3ljdzQ4NFExdCszQ2l2Y3pWUkt6aExpcStEbVlMT0FKd2hzOW9rZUlVZ1ZtbzVnQ1F2bW1GbGJvUXZwazJSUXJFSVp1WmtoVkRYaENUS3krVEFwandZeEJ2QXRTa041RURiRlFleUxqc1l4dWdNcFphejgvNlRkN0ZqWmFFR3kvTVI1SzQ5RVlsTG5YQ0ZRaVdYSS9qVGF6disxUGYzb24yWWVHMWg1am1RTENBWWNacnlFNU1kekxHL0UzRVNGUEJqaHl5Q0FoeEsvaUIyc0UvMGkxVm5RK1JDR2pRaW1Dd25FS1RGNmg2WlA4QUtGT0IxQzNFMndUcGFRS0RGZGtYNWh5NVpJZFN3TmN2QmJQNndPd2JFU3VLQXZieDFDanZSQjlxQWs4NXo5b0JoZUVMWVgwSVVWak0vOFFBSkJBQkFBTUFBZ0lDQXdFQkFRRUFBQUFBQVFBUklURkJVV0Z4a1lHaHNkSEI0ZkQvMmdBSUFRRUFBVDhRQ2ZoTlJycmJBcDRqaVRRdDU1R0ZwcUI4dlo1ZXNnTjVEYWtQVVVzVHhKaUNBS01JbnI5S3hiQzhpUXV3SlhUMkJBQUw0UlRlYlJwSDRSR3JBSWgrWnUzT1lZbHJIOVlwS2ZKRzFsdG1EWHZtQW9tdW51MHRxcXZSSExUM25NckNQWkhHNFV0ZGc1cTc5SkNZcWo2L09tbGE3ZkFsM2V6Z1pWc2dVQVdZSHFlbDI4cUtGMzBqVXZad1FoMVN4YU1hdDh3UW5FS3IxelBKWGd5TFQxQ2FkU3VTaDNxRll1R3BRMUVnL0IrR2FFRXJpZFF6T0l1Yy9jWWE2djdWQ3dQZXgyL0JPWXBEd1EzcU9kNWJReVZzbnV0U3JhcmRoUFoxTGwxSExGelY5amNLb3oyTGlKUVF1ZHBpaGNWQVl1R3RRZXltYXJXVVRLZmhyQ3pwc3Z5aGxGejV4M0IwQzJDc0d3ZEFDc0RJeFZKQ3VVOU1Ud1BDNndSQTFxbGJDSzRqam9pV1ZYRzBzRTVYWE9rTHVEMmpIYzlkQmxvbndUL3NQZVBrSnY2aDNSUVZpcld1Mk9sYXNLcmNLeGh0K1NxZ085WWpZS1B6SG9tb2Rsc1JySUJoS21vZkt5N05oQ0NYbGJuQ2tKZmltVGx0UWF2UmljK2dXY0t6K2xMaXJqRllXbDZISWpKVFJBSFRPVHJDUTNjUENuaWRPOFR1c2c2cGNQWXNwWks5YlBQK1dWYWw0ZFAxRjZhY3Y0TGdHRkZDN2hxUjJJc1V1OXB1QjNWZDlUZi9BQm4rVVEwdmpuL2tXQjNnbHRBYUdEbmlDVlpyc0JGOGswV0xzMFR1VGpMTi9hS2hPRXVPcXM3dU5yQzAwL0FaR2hwbkNOZjRxY0FGMVFmMFF3QkhBYWM5MUI5VG9JZllTNmtGaHRMdURhNnlKWmVsTGxSU3h2SWRYckRzYW9IYk42SkRFZ1M3ZXF3TG1oUEc3VlNucFR4Y0d2QjN6QjVPYk1Lcm5RQ3kyM0pmQVZSTHBJeEI5cGVYSDR3anNVbTNxZGJwM0VVcFQ4eTJwaGFPRDd1L3N2c1RXSEVidmZWV0ZpdExXWkZQQUdpY1E4a2VuVTBOcFF5Nk0zbkZ0VjA4ZUdzWDJiREZZWXdRQXR1U3NCblNDM1F0MjRyazQxd0VYb1FLMktySzdsMUI1SkFOM2U0aXJZWGdFeThOMndnZ2ZUTktnS3dBd01ZY0lVZ3ppSnhtTHpyRnl5YUZzWDJkK29uNkN3UUlGR2xGdjFOaHVndEpCNHRWbTk0UnByNllLRVJqNkxhT2d6MHNVc29LQkNSZGRRVDdoSWU3WjY3MUdMM0tqSjlLQ2wzNSt5SEh3bWFPQkVGdlVYeXRLb0JuamhFaVhqTWxrUDhBbERJQTl3YVg1aWkzQjBWdUZtT0YyaHNHM2ZjTWtwc0swT21EeDJnMzNHeHo5aDladURWMTJjUEJZVVB1WUFKOUZnMjhKUTZZcVlydE12eEQ2Qk42OTVGWTJubndKRmNyZk1TZ1VHRCtPWVdCbmtoaGhoVUUrUm1tUzA2Q2hxckRhbWRreXZkb0djWUQ4cmppTFNrV3Q4UEpHSUY4SXpkSE1vc3ZGWHl3R20yTnNnaEZoQU5kVFI5K0lsOWVmTUllUStLbHRDY2MwVU5MVFlYWFVZZTlnS3BvdTdBUFJZYkQ4L3lOQnRvZ3J2VGo0a0lEUUsrZGlwVllHTmNtc0VmVjA0OWZjZEJrK1JvSXl6dmxJTEYzaXNuTWY1SXJhbzh6Y0ZvTjQ0MjR1UEN0Ujd6TnIvblAxWkZlZnFxZVZPcFF6NitBWHpuVTJNcjJmbGFISFJHejF3bWxGRnUvUHVBdThxalhrNFRjak5rZlczVVlRQkpWWGdYVWVOQitEcnNkMDU1a2JTeXVWSnhWRzJDd2twV2FjZktBcFl3UzFGMXNvTFQrTkN3TGMyaXRDVjJxMCtXdU9xMm4rTTNzQTA3TFF3ZER1VjBlcFJSWVZXbDdab3lsWTZOMFdzTDJ6dUtxdmNWQVdoSEJaWExZR2loN01ydWJHY25rcEEreGt2RWtTdWMyTEFsUXVkMUtNeFJTdGxQTUpPcFZVN0EzR3pGTDNkTGxhRUhVZlBVY3BXeVdDN2NaL1ltZ0JaVnJSczU0ZzhRSzhsQU5kZ3VMK1VFZGtkb3ZpWXoxK05XMVFyMHdydkdrU1VoZElvMk5XZktHZDRwSlR3ZmlCUUpRKzlxOGhCRE14RllXOWpHc3h2anVVR3dPSzlhQUVCV3dZa2RJa3ZQT1paUmtxYTdNekp2UC9hZURtK2NqL3dENGZDWXYxU0F0bUM2K3J1UEhQdFdxS2pwYTRXeTB0Zm1XOWFtVnl1Smp6aUNnQkdwU2UzRjMrdlZ6RklkT1FBNXRENnVJdys5aVY0R2ZZdXdSL1NVV2dLYnQ2SlRwVmZOYk9CQWE3enB3cmkwbHVKWTRaeXFXNXQ5cVdnb05mV1luMVMrR3Yva3RmVlVXdndheitKYlhZUXVtQzVlWlhXelQrZzVTMjBjZHhNSEZSRnh5cjVvNG9nRUZlandabGlJaVp4eWhabHdkcmxoZWlvMXVFNnZEcG1GM3kxVGxnajhzR2NxeEExa3d5TFV1c1plakQyUVNrRitNOERxaDkzTDRNQ0tDSzAxWjZ4N2dYZHFDQ0FDNm11RVBFVytIVmxpUW9VVzdUQktvTWJXU2E4ZjhZWGM1SkRqZDMzY280bDBOSEMweFdJTVZCZWw1cDBuRXVFOWE2L0xCN21Da0NrOXZUQjFXaWNhTDVZNEJiTHV4bWwwYWw4MmRjUVNGdWlxVlkwbG9FTzNxTnNxaHhRVTJjQWE5RVJZV3NEemNFT1Z3bEIrcVk5UWlnS1dsRkdWVGd1c1kwNzRqMFZ0ZDJUT2NHTDh0YklGeEF1b0FxdGF4WENGSlhENVFXeXlFT1RrOFl3aVZWS25hbGRQQnQrb0hxdUUwRzF5eHVLOHdyM0poU000ZG5seXZjSTBmR1pMUDhQRDM0Sm50NjdIcnlpSk15UU05VEdQOUlFcjRZSTFBM1ZuUVlRQnBpTXBjU0JSUCttQXlGVHk1UU1HV3YrV1VncmpZMjhxcWkya1dPZjQ4OENVc2w4cXNzOTZzVm54d3hiU29MQlYrRmhpYm4xeGJCRTRiVVgzQ09iZHYzbVk2clg1M0UweFNsa080VkRGVVBkTTVpQ0hPaGVGWXhmY1Y1bThNTDV6VkhtSWpEK1ZpV21jQVBJRk5IQmE0aVNxNDFYYUliSXp4dklOUGVNSWdTTFRrYmpFNTFORlBlbVJXck1EVktyWHFYZEVsaUM4RGwxVExMcEMwRWdWak1aaldFYnFnNEpmWFArMkF6NUlZK3RYRFJHZWJCUHpCRnF1L0JGMC9GTVR6MVJEbTJOV1NoQzY0V3JIcWxzdVZoZVVhU3JhbWpRMng2eWJ0S3FrY3JTbUJSMG9mckNDSmsrQXRQYnFCUTBKV210cVY0UkdBcHJGVkhXNE1xYnQ1ZkM2UGdJN0gxbGNZZ29uTTBoWm9qeEdqcnlYRU1yNTZsQjlXd1FNK1J4TDdicmVWcVBJRjMzRlBXK2ZyUlhacTNtRGFxbFhDY0N4eldFdDJvRFF4QnpRa0N5SWpEZzRJSzErbzR0bDA4NVpOQkxZK2FITERXd3dBUnR2dUkzWmVYZjhBMkwyb3JVNi9jSHhpVERkaGZtSDZhSUx3V0xFQUZ4eFVjVHdXcDJ0VXcwUXlHUXV1QXhaODdjQW9ZTlcwT0EzNjVXTE1VSlNtdnR4T1liK1RHSEZwVE5QRUc1dTJDcTlBVXVDNVZzZVVLS3hkd2UvVGlWMExMMVdIY0FNRGtPTHdydWxxRWpLTGpHVHh3ZFFMQ3lualFpZXlWOFk5QmIyQ3VMQXFRTUNDZ0hLaVVCRWpXU2pldWNVc2g5ZWxWUXRQY0lOQXUxdmNCSldCNVFWV2dGdTRQbkxGMlhPb3FrZWxRS0xqL1hDT1k3OXcza3RTNVU3YTU3MmQvd0FqQTNDaDkxY1V5bzgyd1lNeFEzYWhJRlZEUGlUVWRsQlZlbUx5ZmMyK01WdmlYakVGc0tGc29HbWtibWZIdDg1bVlWY1RqVTN3MWxlYVNBMHRmSjNGVzk3TE8zYjBxS09MeGZTK1llLzNuQzhVOHFzdktzU090dEFLdXAwT1phS2RhdFlpcnNwRjZzaE9IUUFoTFczRFhJQ2ltclJWQ3BlemkzOUFTS0ZWb0sxUVdFWTQxVkNHd0hrS09FcXB5NnFkSUFVOHBMVlNDREZGS092dE9IaDJlcjdpZUJjT2pQOEEwU25tKzRRMXM4Um1Tais0NWZnb1lSWWRXbExJK01xZEt5TEZsM3VPYnh2OHU4dFJBMHh0WXVpMkEwQUoySjk4bGF5OHNvYU5zTWxwdUJ0cEwzdVc5THFCRlpyNjdRdkVqaHU4YzU1d2JEcTBCT2dhN3pHNDdQQzNMM1l2QnlZZFp6L0NGMFNsdE80bEJCVUhOOGs1dWVHeE9IVjdNYU1CS0pzSWtGR3JUanBNUnd0UGFiNElYcXhuUGZzZHhlWkc2NU9VNi9pNkRZemkwNTV3SWFGVzExQ0p5VHFGdXdEaExJL0MwRDZ1WFQ0NElOK242Y3dRYmY2NEJGd2JHcmRWaEtKYXJDSkVQUnhTa2Y3RjJyaUNpV2h5dW9tdFhNdFo0VHR4Y0xVMHBLUnhGVWhqaVJmZHZoaHJjZDlkVkdnYWRPUm1yYi9kbGVJWnRhVlNtaWpzcUxYQmc2REVXcWF6dGxiejg3RXhVMEhxdU80Z1hVTjgrSlJYT2JIaUJTRThxMGp5ZVJKUXRaZUgrUzloR3NBRjgxQk1ENmtGTkZId2FpZGR4TE5yb2lyN2R4MDJiN0RnUUdpZ3NxZVRTMXk1SEphd0VOZ21PZHlxMmIrZUtob2ZOTVVzWmpINVVSY0RRUkxYRmxoMWNSYjBlQ0s0NnhzRmtvRy9CS3AyN1BBbkZjeE9OalRzcXFDRHl4WXYyMUs0UGdNVlIyMHlaUzZXbTBUY25sa0gwR1JURWExR3gxNFluNU5ZUjRSYWp1NHBRWDVvT1ZGUVpVb1RRYmd1OWRsc3BkS3k0REg2S3cwNWVMOFpuZ2pBbktNNEgxeEZQMkZuU2ZsZzAzbXRHcnlBY0hsSHliZ3YzV0plNVg0NCtBbE9WWlp5eUo2bWk1U3l2cUJweFhZVVhlMzh6ZnJidHFHQjJnL2R0Y3BhVUtlaklQWXhXdjJKekthTkZzT0RBdEkxOU14bitSNm41NmdRL1cwV0hvVWdPMFNGOCs1WkxJYXFMMHJOOW5aaWtvQTdDcmhkZ0JUM1dSdlpycjR1Y2pUNDNWYnRRLzJubW5GdHFWR1ZFVFpiVUdwdU9GL2cxcFJ3dU9aYk8wQ1BtRmlrNE1NVUxWTHVqRjY1cUxldFV2RDh4ZHNlQVdmWjdsWllRZ2RUbFdOYXNBNDJRZVVRRjRNUDR4d3N2TXRicWR5bDRKdnpmQVBXakZqZm5nUTludnBFMnZDd3JpMnErSm8vRXRLWGxjdUl1dEI2WGN4U0hRVVJQZVlBR24zay84UUFLUkVBQXdBREFBRURCQUlDQXdFQUFBQUFBUUlEQUFRUkVoTWhNZ1VpTVRNUUZDQWpCaFZCSlAvYUFBZ0JBZ0VCQlFDamxuZFdiTk9UcnNVL1N2NGY5MkU4V0g1cjdKSDlmK0FKR2UvVGp0d3I4UU1IUi9DSjVzbzhqZUZBMEltQkk0S2ZwUWREL3R4bkF5SjZiZnJqK3Y4QUdkSDhBOHdaM0hZOFlkeVorM3l3RTRHd3NmUU1ydkc1Um0xOWtYQkhNbzNacDhXNTZuVjUyT1M1NWJCNU9WNXFnMnBaL2JnQi9kaG45cUlFYnBaaXVGT2hsOFNIa21ldEk1NjgxTTluU0NKOVIxRUErcGN6WStmMDc3UVB4VDRMbC91SWllZWljZ09adEg3RWdwQTFWeHRWZUhYVUQwenpVVVFQUi9GUFluM0lVRENKSEFaREFZNHRJQVYvYnArSUl5aDZneW50WHpUQzY4ajhmUzBySzgvcCt2VDEvcFhGT2pzVXEraEtoMlBwWE5aZFc3QjAvaWdHUGNBdFh1ZHp5R0d6REdzY29SNmtUMHJqZDhmd2Frc3dtbUVKeVNrTEtIcTJlY0tNWTYyTDZNM29Kc3hXSEpoSklQWTVVKzlmMmZ4MU1JVTV3WlFkYUFBeVp4ajdIODJQSEpZNEN5WkUrU3hSVmdoSUpZNFQvc1BlNStBeCszL3g4ZEdhcmdwaFlzTUo5eTR4QVgybThZMGtvT05uL3V3QjZpdW5YclBtcnd5Qm9rSkJteGdBR0MveTNPTXZRb0luNkpPT2dnb0MwRDZFSHovckQyMmhMWEZxNlVuRHA1ekliSW43bXcvTFpJOHpDSFcwN3RYUUhqck9CaUt4SlE1czZWcW5YME5pYlgxNnVrOVRZVW1iT1Q1cUhld1UwYXlCYzVsZldWL3JyemFXMzlQK25hLzBwVkM1citBRTFBZHU1enJiYzNGYnN5S2hlcWZUZUtkdXZ2ck1Tb3hqN0JnY0xjelpZZ0FuQWdNMkhzcW5uZytjYkNPai9rUDA3WjNDWDM3NmN2Wkg2RmgwbCtZUGxNQWJGb2VxclJNRHJWRFV2eG1rZ1ZBUm5WQWsva25RTWVRZE9jS2ZDbVQrQi9HSDNMSjVDWDBkZGlBdXlsN0Vyck03RmozQjhtSlM2V29yRlJzNXI2c2psVDNOVWp3QXhoN1RUaWVPWG84MHdJZVBrdW1lSDJ3QXUxNk5FNjFWQy8wNmpEcTI4ZFNOVGh4ZmRyT0oyTjB3N0s4T3lUYmhiTmVSU1k3amQ1SG9ubDBMcm5lVGJuSmV5WXhHYS83d1Avc3RXdlk3MjlkQnRiaHlWSStuVG5Kak4vV3JWMTFVUWljMUhmOEFjMzJqV283SjZrZ1dyUGlWUUExa012WHhUM09jN0p2WllrQ1paZUZ1bWM3RmpFamFzTlhsb3o4RWxCOG5xU2sxUndTL0czMHc5K04wbllpd29JUFdaRHByeWk3NGZaWlY5WExSTk1DRlcvcXVpS1Bzb1B0WDhUQTdINVJROUljM29LREk2MWkxcm1GOWJjdmRyWkllMjZRdXZJdFF0OW1PRmF1bWpUVGJNeE1mNmgzMU1oT2NwRGFSc2hCSjdPMGpIR0o1VDJXZmo0cXc3TSsrdWV6T21rOWl5WFNqcU9Xa1d0cko0VnQrTmNEbjFCZXcxdy9rWmpOS0NVWVVLWlpSZER3TE5rS1NaWGo2QVFyVm1wU3p1Ry9OUGdueEFPU3pXUHRkV0tWMUtVZXN5cXlQTTlFaXV3cFU2M3grb2tqWDFGTHM4L0VTczBTZ0RvRVFZVjltVlp6aHdxd21wOG81MUNCcVU0K2pzT3ROUFlnaU1BSmtjMThCOW5iaXkybE9VOVZqT05HelpIQnJmQzhvMTE5WmRMV3lnMUEvTkhvOWgvQkI3M3dEc1hQTVlkWHVVRWd4RVdWWjZxNHBqZ2RPZW9xNDlqeDlwNlVZMnFkT2J6Uy91c1BoNEk2THJ4VXRHVGthOFIvaVIxZVlzeXIvQU1PaTBVbzg4OXdRNExBNFc0TE9RZi9FQUMwUkFBSUJBd0VHQmdNQkFBTUFBQUFBQUFBQkVRSVFJVEVERWlCQlVYRWlNbUdCb2JFVGtkSEJCQlR3LzlvQUNBRUNBUVkvQUhVOWJLcC8rd3hkckx0WmpHK0p4ZDhNY3VaVnRIaEQyajBuN0duWldYYTdIeE83dm16M2V1ZTNJVk5DbFk3ZFd5cFZzem1DVmZQUTF0VkZvNEpHcWJ3YVg4YWxrVVVtRjhGYUtsMjRGU2FtbzdaWnFlWXhVUEkybk41SG5CbGt1bzg0NXIrenpmREt1NVZIcHdKdm9hbW8ySlY3U0gwSFJYdEVtdVVtZHF2Mi93Q0MyZXhyVHFmY2RHMDJpVFd1djhJL0o5L3czZGpWTElWMm9OTHRJeXlydU9EV3pGVHdTOUVPcXFoTnZuQjVGK2lhS1VuMlE2cXFWTDlEeUw5SDVObFNrL1JjRDRISmdmQXhSMHRMUmhDYTVqelpVOGgzYnUwaks0ZHlkV09sU1ROMTJJa2FsRWtNYzJtNmdhRnpKWkRNb3dvUE1UdE5vbEp1cmFKbXFQQ0pXWWw2RzlHU3F2R1Ntam1aWnZOU1lwTVlQRnAzRnUyZVZJaHRIaVY1VHdVS3Q4enhyZGJoeXZxZXhLY2ozdWQyS3BhUVpUWjRFVUprRkoreERmWTl5bU9sa05HaHBlajhhMDFQK3Y4QThqS1VMOUVKUU53UzFGbVkxai9iTnRLQllTRklvRUtTZWVCZHhQb245akZ3c1pWdGFwZWNKY3lFTlN2MGVhVlprcHhnOFRrZExYOVpMVVJaQ0Y3a0h1VXBQV2Z1eWVxSEFyd3VaK0haZkhOaXBiaFI4bWpITlB3YjhlR0xNOFhOV3dMUE95UGNYdWUzK251VSsvM1pRN0t6S2U2S3A2c2lrM3FFbzljZlpGVzdCK09oclRTYk1WU1hoalVqVW1CZC93RFJpa2lSUlVpRzErelZDM0hyTm9ReFpOYkowMHNkVVlhSExmd1pJUytCMXBaZ2RxVnlrZzlEOHFYTTNxVnFQcWtLdmtOd09GWjBqcXJ4WjJ6YVN0YjhycDBIR250MUpid05VVTZEcHFwaERFVTl4eHlOMHBwNkc2eHA2c2hFUDlqMnRlZVNSRmRDZ2RPcWlVUWhETWtYcjJ5ZWFveDJHbS9rcG9uUElkT3Z1Tk9KN3NkcVdPSHlONWpiNUVFdFpRM1hxaFYwajJkZmRFMXZBOXBUMGhHNjdPMkxRZUVkYm1YNkZHNi9FaHRxSDFadnFwYTZ5TzFNRlVkRHpmQk5JblVzbWhCRURjR1RRY1VreEw3alMreWFsZ2gyUm0zaFV0L1p2N1JZSlRUblgwR3JLbmF6SG9PbWlYUFVkTlV5Y3lPR2I3cEZVbTY2VzE2bmtNTGc4R0Q4YmM4eHFyVXFka3ExSktXU1dpVlR3dXltKzdWbEVUaDZIaVo0ZE9EL3hBQW5FUUFEQUFNQUFRTUVBd0VCQVFBQUFBQUJBZ01BQkJFU0JTRXhFeFFpSXhBeU13WkJRdi9hQUFnQkF3RUJCUUJab2krbjFoSWVwYldwVFJUL0FGT2MvWGt4bGZpSHViRGxNN25RQUdCdzV3Y1hFSFJuUi9ET01MY1grbzE2ek03VkZoSS9sUDhBME9mRThSVGxNZ3Y1MjZYK25uZ2M4RGdRNXp4VUR1SUJnNHBLRW42UjU0RGdRNGdIMUVhUXByK1lXMnMwRE1ueW12SE9IdmdBK0tMWS9lYXcvWSt2WXQ5clk0TkxaT0RRMmNHblk1ZldyQlVQUXB3SHVDRjZEN1BaQW5xMmNmWTdpbHRIWU9EUkpHc2VKNmpUNmhRZm5QNU9TUGdQcnJndURsVDVIVEhhUHRVR2ZlTmliOU9KdnZ4ZG5yYjlUc3FBUUJpWkJpaUxaeXExMlFDMjBjSTJqalEzV3lQUkhhSlpFK1VIRDNvQUpUd09LakEwd1YzWk5NNyt5ZzFQVk9HTzdDY05YZnNpZW5lc3NOdGRqV0hCL0M1RFJvMFphaFFLbmlPY3lNcGxKUmxrVVAwcklBcTg4aG4vQU1wN0o1bkF6OWM5TkttY0p2ZWFCOW5QcDBvcy9PU3Exa0ZXZXprWW83aUR1YUlQMjRHY09keVpZS2hJeUo1UFpQVzRlcm54Tk9lQzh6eFY4cW9WbmNtM0FRcUFZM3RPWTZ1RlZ6ZzRNVDJHcmVVdFNkWjFYdUFaSmYxcW5zZUpxY2VpZElaYytVbC9tMGErRTllM2RnRVc4SnZaeUZDTVRuemlIeEhRUXBicW5tRmg1ZlVHVG8xU2p2Tm8rcTdjZ1BXd0JyK3NiRzFtdFBldklxNGxWV0dIQjdZRCt1SHNpN216NFE5WjBaNnZxTkVmY2djWms0cEhOVGJsRE5uMUNGVWh0U1JuM05abEZFUkZXYkdhUUpFNVFmbWN6VysyZVA4QXpFdGxMZW1XOVUyOXQzZHpkS0U5L0hCM3cxbVFwcVFGcVVWTmV2cVBYelNuNHJzcU8vOEFrL254SUNxVG1rUGZISkZaOTc1VEorb21CeGlNQWY4QWsvVnRUUmxPK3BEYXA3c255NENrWVBpeC9SQzRrVm9MaThTSnc2RXF4Wi9maWhqbEVLTW9KRXErRGpnRC93Q2t6K1RjNmc0ZW5GSEJPakl5K3NQSForZ3BFNGNhcW9wR0QrcEFlYnlSbFYvdDh0czFYSmpnMnY3OTlnd0JvMzVnKzJ2TkhmZ0FMZ3NpOE5PaDE3NGpQNnJyYTV1ZHJVdWhHekE0THc3ZXNsWVo4QkpOU1kxM0lUVWM0bW1GZ1BiTmx3ejhIRThjdHcwSHhyT0ViL3puYVQvczZrc01SVGxPZlNnUjl2clU4R3Q2VkRXcjlqQVpiWHVqTC9HanRwSlcyNjFCYXpZRUoxbDk4MlpLcmlWU0VsWHBsUWxZV0kxNGgzNEF2bHlraDB1cDgxVnNRY0RQRlJQWkExdGF1NEthMjVWR2JmMkVHejZqZllqSTlWdmpXOXJMOHFPRDArOGpFN0U1VURwUzlLb29IWHlrdnBpTkJMTzlYN3BXWSs3Ui9zVzRTU2NybG0vR0pVNnNqUHRkcVNKR0kyZGJZMW94U0o5bjlocGp0anlaUStlUUpTRzJ5TzJnS21uRFk4TXhaM3BRNnJybEwwZlYxQ25Najh0L2JIellBRk5mY2FtckJvdE9SSmFMcWt0aW5uS09WK05BL3RyNGVLUHdidDNtRE1VR3NUQnBvelBhVkpWb3JMVVhkd1pLc282ODBaMUhqSEg5bHloellVRjlCNXBXRnRRSktnZG5UOFJZR1dxNGRMSFBUeCs3WlBnRW96WlNDM1YyWktlZERpc3lzck5hdHd5TWhyUlJQWUFBcDA3TXprOXFTR2U5QzM4VTlzMmNaTzRnS2l1bzJTRVZWNnFNMHowVytkYWxwMnVkdll5VGJoVDZtOXdPUm5rY0J4U09jOHNqUHd3TndJVGljeERUaW1vWTIyVGhwYlBOeUNPNU9ZeWVta3BwOUdLN2RVcWRYZ0Yva0YwTFZ2aVZyUEJheHpuUDVHQThNNmN4cWVVKzh5WUdLekllcG5tckw0RUp3REFveUV3MmYvL0VBREFSQUFJQ0FRSUVCUU1EQlFFQkFBQUFBQUFCQWhFaEVERURFbEZ4SUNKQllhRVRnWkV5c2RFRU04SGg4REJ5LzlvQUNBRURBUVkvQU9XRHRMWW45VDJKUWh2aXNMcjM2RDhHZEtIcmEwV2o4T045RkZiaWhXYUUwdHRINHFIL0FPTjZZZWxQcGdibTZGeXJKMHNwajB3YmFLOTlHMXBTTW9wSG44Q2FaYk1HSDhqNW5wQ1hZaTFqUjZPUnNiTXZTbEUyUDBuNlJlWElrMVZlQlV1aGlCVWVGZjRNOEw1UjVlRjhvL3MvS0kxMEZlajBhUnNaV2o1T0hjZXB6OFBodHArdEdlRS9qK1I4VGpjTnFLN2Z5TGljUGh0cDVXWS95V3VGOHgva3Yrb2k0cjdEMWpKUGRKbSt0eTNvMnY4QUF1eG5WRGVtZEhUeXpsanhHa3ZjL3VQOGpVcE5yM2JPVlNkZDJZbTE5MmZUNHJiWHBiZmdqMlhnVkZzWGdReDJVbU5OMk5QWkcyamFFL0E5SU9UOUVYQjNyblRucktSek9pcTBRem1wL2d2bGRkbU5GcG1ESlJrd1BtMFpSU01PbVUzYTl6OUh5Y3ZCNERrMTB5YzArRTR2bzBWVExsb3hEZnVmVFRkZENQQ2xkcjJKejlHelljSXlwbVpzcVRzOHFIekRUN2JNMmRkaDNneWVWamIwYW5IemRUaVM0S2V5L2NmRzRiNTFCT0xUZnZ2WGNwcXZ3VkgweVovN0l4RW8rdGxLU1hkME5UYWZiS0p1T3cyeVRIOWg5eXZaa3I2SW12ZlJyVEwxdEhFWEVlWFErTi9UT3VhN1M5N2ZVNW03MHEvK3ZSRHZycHlwME5KMk5kQ1ZqK3c2NmxMM0gySkxxMXBMUmw2NFpHRVpjdExNbnNzUG9Oc3VuK1RDcmJSRk5Ya3dxK1JQZjlrVkYzZWtoait3Mk4reEsxdFdqUzNNalJuWG5sL29iaXJ0bTZNTmZrY0U4MnRiajExbGowNmFTR1Mrdys1OWlmMjBsbzlaZGhkaDh4eWNSTy9hbit4Y0l5OXNDNGs0Tlp5NjFsRy9OZXc5a2JqL0FQblNUU0hqNEcrVmxwUDhEVlBZbHpyb1Voalk2TmloODBraHBibHhTK1R5eExmN2k0Y202YldqSk5GbnV5WEJidkZEakptTXF4eEtUTXNkNUV5b1pHOUsxYU9ibHpXL1VYWDdqUXVlVldKeG5idGFNbWhlNDVNNGtuNmphRkpiTGNiOUM0N0gwb092VnN1RW5ZcExEVHBqVDFlcm9YQWF4Rk9uM0ZLSytDWEVyRHd5L3dEQmF1dXkxbUs5eWxzUlVmVXlVbmhpaERON0Q0VTFUVytUNmtPektnc24wNVBPN0xWL2t2UjZzZk82VkZLV081eExYbGZxTE5yb3NqaTR1bGlxL3dBaWFLSmtiNm42ZmtxWTR4ZURjc2Nyb3E3UEs5aHV6TWltNlA4QVJ5eHUrMmw2WTB5NlYvQStId25rYWFhcmIzTDZqSlBoN2x6cFVXcUhzUDM4RkZFbnBkbmxPWk5YM056ZndOVDh4OVZLdlNqQWhqbEYwVTVHSkRUbDRjYVNyVzQ0WmZMM0h5bzh5eVZyLzlrPSIgaWQ9ImMiIHdpZHRoPSIxOTkiIGhlaWdodD0iMTU5Ii8+PC9kZWZzPjwvc3ZnPg==", + "description": "Displays historical efficiency values as a simplified chart with background. Optionally may display the corresponding efficiency value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'efficiency', label: 'Efficiency', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'efficiency', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Efficiency\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 30;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 30;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":60,\"color\":\"#F89E0D\"},{\"from\":60,\"to\":80,\"color\":\"#3B911C\"},{\"from\":80,\"to\":null,\"color\":\"#2B54CE\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:c2ltcGxlX2VmZmljaWVuY3lfY2hhcnRfY2FyZF9iYWNrZ3JvdW5kLnBuZw==:c2ltcGxlX2VmZmljaWVuY3lfY2hhcnRfY2FyZF9iYWNrZ3JvdW5kLnBuZw==;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Efficiency\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"trending_up\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"units\":\"%\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "productivity", + "effectiveness", + "performance", + "capability" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_flooding_level_chart_card.json b/application/src/main/data/json/system/widget_types/simple_flooding_level_chart_card.json new file mode 100644 index 0000000000..733b3a1289 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_flooding_level_chart_card.json @@ -0,0 +1,40 @@ +{ + "fqn": "simple_flooding_level_chart_card", + "name": "Simple flooding level chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical flooding level values as a simplified chart. Optionally may display the corresponding latest flooding level value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'flooding', label: 'Flooding level', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'flooding', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flooding level\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 2 - 1;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 5) {\\n\\tvalue = 5;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 2 - 1;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 5) {\\n\\tvalue = 5;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"#234CC7\"},{\"from\":1,\"to\":3,\"color\":\"#F36900\"},{\"from\":3,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Flooding level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"flood\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":1,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"m\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "flood", + "flooding", + "water height", + "flood depth", + "flood stage", + "inundation level", + "water rise", + "overflow level", + "flood peak", + "high water mark" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_flooding_level_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_flooding_level_chart_card_with_background.json new file mode 100644 index 0000000000..259c619f4f --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_flooding_level_chart_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "simple_flooding_level_chart_card_with_background", + "name": "Simple flooding level chart card with background", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC/VBMVEUAAAB3gY42TWPN0NIaNksePVLf396Xn6lke5AVNUfg4N1kcobq7O/s7fDt7vHp6+7w8fPo6u3m6ezh5env8PLj5urk5+vX3eHf4+fe4ub19fby8vTg5Ojl6Ovc4ebc4OTu7/LZ3uPr7e/V2+Da3+TV2t7z9PXf393d3dzR1tvg4N8iSsHO09jP1drT2d7R2N3n6e0fHx/a2trM09fZ2NnT2N3X19jL0db29/fc2tvJz9QKJDJPaH7S0dPU09XW1dc0T2UNJjYJIC3c3NsXM0VMY3lHX3YdOEtCXXMdHR4hO0+Pnq2AkqLIzNSnr7o0UmkxTGLKzdHCxsy6wcpUa4FUVFUWMEE5OToPKTm9vr9ugpYSLT6vuMN7jJ09Vmystb9qgJM8W3HNz9VAWW8nRVra1tVzh5pidotGYnk4Vm0dPFE1Njfj5ObQz9HHys+5vsWqs7skPVLb2Ni1vci1u8SGlKJleoxub3AxSV8oQlYcNEXi4uDDydHT1deMm6uElqZYcodYboTP0taxtr+irrxcdIktRlvNzc/JyszFx8qzucKWoq2HiIm/wceVo7Ned41bcIQ6U2kYNkubqrhne5BKZnwrSF8hQVYTKzsOIi8sLC0GHSoEGiegqrSRobF8j6FmaGp5iplVboU5UWW3w+K/xs++w8tgcobc3+HX1NO6vLyZp7N1ipxjfZFUZ3ywvd6frLeHmKna292ksr6kqredpbKWnatrfY9+gIJ4eXpNTlA5XcaptsJ3jJ+XmJpwhplxgZItU8OusbKJmKZ2hZaRk5VHXHIVJzVQb8qRnKeNl6WhoqOKi4yBj5tPbYM/X3Y4S1ooKSpbbX8rTWRGR0dAQUERERHGz+GUp9e0t7mvtbijpKWHjp4cLTzO1uN/ltKen6F9ipZYWVwvQVC1vb6orLKkqKtjeIBhYWLAyt1mgM1MaXRGWWldXl8bHB0GBgYmOEenttmZqthFZsiDhYjU2+p3jtFueohXYHAXFxdshtJYds1kaXqJndQ8Xsha7PFYAAAADHRSTlMA/SAg77C/v7+/r6+XAQwIAAAn90lEQVR42uyUPevTQBzH/yoo6OYiTiJSkpaocKaRFC4ZOuoFepmUSCDSzTyQpC6miomQmhDxDVRwaLQqUrWti4Ooi4KLm9JF8OFt+Ltr9U2Yz+Xh2mvg+8nvd91raGhoaGhoaGhoaGhoaGhoaGhoaGhoaGj47zhwEHUEoXND6AgdQOjDCceNG/DhBlwZu5sgsjWYC+wJxr81Ad199+vbpYnjJEVcn5dIbfoeoVL37LgvqKIqq1uQGpS5KsNA7ESK7ilIUVRNUcaKrGga1rZM7r6fBqnT1jVNx2OMW8fa2fo7NQqnTQMa+6RVbH5ONJrFq8+fjxwADySKPJgI2US4sqg3tgo84t+7APB7h+mAlLiTEZRORxy6l35UOU6dgenXRCJRZCYkqXsSGSOkApCae8iZO5BlBIcqi4jP4FRcTdZ1DWNdc39cwjrGfrRYBGZNaJSZabp4OKXYraa105JmhWXMeqdP+P7drMrIerP8tdm3f+8wi7/NCRo87fbggA4b3IE78h/yqci1RZgoHUHPB47jjJ2BV/kmldr+U3uWkCKozSRVRSSAArgICNK/hKnOFJS5jHRZVRRZ1tFLRdEVTYeKuD8mrosH6bFZEXwc3Xm1tN8/eHbl+uvPZU3r5WNimb41NSJSVNl6vSTr8lE2uT86tCfsokFI1BEBHk7g+QFhe7BKgSWfQEFAgsPVxRuijvNUao9T0KjAo3u6eGpRQgKjWNQaEhB4IHk7wiH0lczSD8NwGA51AD5BQWQFKuG6+biFcWqRdnfgOZsiieo4/v18VK6sxCSFvfx8e2HZpDVY0k9G5ZfB6MO3eDO9ucfjwQUJPNfudcMF6gAqO0u2Bga8pwDm2ocHEDwBkzm8wpet1KF+XFV00D3tPX1qEtKmfvD0rqZCPUKEQoV7wHyoh+AwnA/n4XCu6QrbBjAUDecVxp4duBM/iJOj+SoZp5EZRdk6s41R5kQ901qZU+P6xVKq6uKDnQSrIvs5HQWb5d6uo5gEA3ZMf5cUasJj8zXIKzOZrShrkVDswxBFVYyrSQ5xci82Mko9py350nEvOd/u+X6FoaWQGs75bghDOHghGPP5XMv5znB15qPjuy/dr1e/PCo/FeTc6W5WJifPRxatx49TL7bpwIlmSWQG07dXrPHPj+tPVWlIj1veanXn02KPBe3vHPoKgsAif/XitrnYMur3uSGTELk4cvMcy1v1/nAyuaQrbcergsAkDhmkJCht2nNa5ykFD2YSygDi5YARcgsAs+3gYt3FGoaauKD07du3aUEp6Z6gsRE/vlYbqSdJqTkzI6W3uPxi+cAikWGeDj4VRhZZBlneK+3M3+yJIk+6KwZCIoy+yvMyDWihPnyLwBZ+hLZ9pbqXvj6Z6KKqCoKq5E+eeK2WRGhkF4mTDqC1a8MfpKdbKXFkxP97oRi6v5qABXMAi2E4/+FPytH6rgvbAjQoZg2aY+3UybSr2GbrZWyeNaUz5c/ewDkTL6azM11a3jKSO28S88In80GwXpHInK0M21haZvKHEDMJdRqKwnDdZ26q1bY2Ro0DLrylxgFbF6IgijiFUHBAXVqr4hAXdqGCbbUVd0JBUMSpCxHniog4gVbBsaBuHFFw2gjiznNObmJrFX/6cm/OS27Pd8+QvBfJUGkgSyY7MTM7OzuLIFgBVDpEaWcQE0no2tm5wo1DL2qQ1PnsqdrC3a1aecqkqcCxaumOUmnx4l1jS0sapTUzduV2weNjPHYsKJEt+y8uBAoIxymMR6625eryrY9fv19fq2AoDt16cP38iruNSrVaPb/t3pb0whMXF+/esDtnLy4tgc3fsnTpjgv7Dq1ZvmrG5gXzN29Zuu/esWPnlm9cfmz5qs1Ljy+PjEMKTPfpE6URXNN6NWJAViKh/CFdl0RRlHQd5gkUWRMWrNVzjw6ig25IpimAZFVVJ6uyIBo6riKZsqZpqoy/kQXToCvhUlGSdC4DJaEMI71l0qpVK1bfK9m1B5EMMEDuwMEKOUYMcPwXxEAOYxCEy0JTP7mB6CaiyLIp6QoHoXMggY+J3kogM1haIRKdjDDq8vy7C1/f3b14cWNjhCo9O3t2VhwR6j8g/U75HkgifhtMEcTiqAkiwTMSWkL5TsFtuPf+nXoQJhBygIAFeGHwOXg40YYgip6GYmw0CrX1lQggwAfyuNfVgdSi4+Du6nwAhxBEDzgIJJyjQgQLFV6BOSZgDlFymiA8UJgEkm/iSYviIEGQao33r+8d2rs4j6mVyeAbAjn5/5BwEL5myAGiPOe+BqjB3hMTFy0SkugSCItBNCEM6LFhCqqGCUY4wCFSifQlpQl2MtZebL1798Ol8+8iAHH2LP6M+DtJEIxBEB3Vw4EBIQwKQg8nTcjOQfgloWOQ91Adk6HKRbjSMGVVAxJVAxEJ5dFvDgIBIUrNLlcr7w7d/RHJZGdnZtnwpjNYC/8IiBV6wOuPc1BAfqeNTuITThhwovp7BbSr1FBNkPDEB9HAMFRTBeSQjPDLfBBIPpBsmrl3u86ePGtv3BqxMza9wmGNDOpvHH0+8IBgehAIeRmkMu81YSvjVd/LweeGqaZiMdU0CArcBA4wIAiQ4NqhDB1BVMRMadqLDw/elcrZ2nsEKWeycPwrCPe9Z9rPgV6gxzqJDOG2IRyZyDwgtIdzQ1BTKU2WdGxd2LdU4OgFIQEEX1sA7iJq29rty65fbCwuRSCpMtlZOfsPEPuaHUbACkjCtOkH0fnGo8LuSKIghQraVb9oSR32GLJIp3ig1NTQaDQGbAQi8X0h4TdAAIvxdPrkyZmfNi3afvTA9QsR+HsmMwtzq5/jUffbl1aQSQkrcx+Vue8SRj9I3dVvOEW/nfC2jwVaZi3OEWLgTjhOYlD0CDExHrop0ONQGxpNJmMQEAIJZSANRWRoMn1y2Nixbw8/nHvk3tZbNyN5xMBqt3pB1n1l7GuXfZnFn2wF9tHzvGrdDQD6QJTqRwQxqJlwmQDiF6hfHUFUHccK3eeibOTpSa1XliFzkvFoCoKEol7Cw0IzUdBi8THDxk6Z8tD5cf3mqqVnjkTgr2Tbho9NnZHL6zJU98ss3nQKrIBfjCDFTh3mCRhaCpxd64DR7iizOuXbr3bJ8snbr2qvOIiitOo3FOVl1rJuVEakXt7PWBaB9PdvmpLoXQtbLwQkDgGRTXo/IRYMBZzh0ph50eEnR0+ZMuHzle9Hrm+9e/10pFLJl8sQEwLh4W9/Y/N+dr/N6+aDvWqxgqIQSNF1P7L7CRpuK7b79GPTVTrMaDH3S9ORd7nNL4wJlFoFpd70XEdxPCvLbmTgUlbgIPzBRu2IOpy/2yYWOaJAQKBCVFMiK4pAeN/F38fTwxDkjuOt3b5o+6aDkVo1X7bL5XyO0phU/Nac123n2z9/3kcOEIA0XdfToRw6zFbqrPiyCUOzWIehjUaxxWrmbSa8YmWxzfAWmxWKrAOxrF5rWi9d62UzZXlOgmoEMNArco93ByotWUMO3noxsdBOCUtXGhI9bqCdRZPD06MwtbZ7b7bv+XRi5dHIhloO/3lRBpBQtfvI8Lz79UvQglqs3uncwIh4jqKAj/5QhQGM0ismPGd58xUTPcfQ6wwzJssKBfbUcdjLIis4XsJrOo7rAghxEAYvZPLUf8XiiaXhM0IWSD4IpRZvu0lUfOSYUcNGj55z5e2eSw9OPNkaWdooVXK5XC9I8dqz7jx2+OXPr92c5KuFlasjSNulivGeKrjZMIBRfMVUAIFBb7tgYBhdAKmweqFQKEJuQZp5LsyrHAQ5/Fd1AkCfAxBVBpJUSgUbnOLgBwQjAkU+tBgfPjyejEaT8fSYUaMenx7y4cKZnZ+2RlZtPL6kWqmsXx++0OrXmm327dmbbvfNm2eEISKIZBBIi90ufnQBgoYbrGM/dQUCKRsdBvZrxY8M28ZZKAjXsateEUrI1fUbcEenQyC8waH/xAEKQKhl4SDAqYaPRIKkNyAEUX2QaGzo0FgSZg/uLt+w/MK+3RciCxdu3FBb0tiwRCf5HW69IJ89q6onT4q/I4I1DyBQGexpNRgSbdZ0AhAolYR1mTWfshEgBMm6rNk2jGLTgzXgjma1F4QnFPgfcoBUEBhEcDoGz0QAMf0K4SBQHtDQUhoWSzS2ZeOGzTd371148RdVZg/aVBSG4euuaIixDTa5xiRDwCZVuDg4iGNJG1BBUCIBF4eIBpIgSFAwATHRLHVwibRDihARpbX+ICLVUloUWrAO2g6tWgel7aaDg9/3nvfetq8S682955znfL/n1qq2WoV6vVDoSPgxQ0weXj88/Hn91av19ZeGhJKxNNtc2gNdMp8yuqxAT6to1W8FAlc/ed3l3ksax5CMdJyHLoSIwaBlwCNLQ/8EDtSKHqRgJgWm54BWyn1ud1wvyRvUXCZXsQq1zoN0IZepIo1gwMmpwNTwj9svh2/durOVA4MhW6oHsh0Fh04tdVlWeURC+vAbtOrbdt7r89niy1zg0JCnOUK6xB5xGjTvkopD/h7/Pt9OvQcPYzK9Db2LUetKrj6ay2YrVvH16+aNhrybYG+hI/q7g3YQhZUG4Z6qTchCYWeNKwBESL6+OYuWyjT4bgS4fT4wDAhIIL1LS2DYtsPBbixTDYQV70ba0uHxmN6oqVliCSni1OlCO1seGxu0KqOni09ypXpWEzZIfFJsHkbsbrGqyXnk4MHAFb+RhYLjADgo9sT4HiB8lgmFiBQ4xI2Ckk+7IhE73C2GCIW02UKIAATjS7YzDXxIHRA92ZV6prD8p9wct0aLpfaVdjuXk/t1PrQxtgzn18KKJbv1ahuJoeCbAvFh9RqKQSDiPWoPHlhoTaRfPI1Pn3CEpSyoBCZmByUhhcU6BkQNohGI5lJuFg6AiGbK5fcz2Vb1pNXodDKjldO5EmfUxCBlU8fYuYvRTaOwdSMGvAbbgmmI4YUAkz+f5A54xuTTOJnrytQeXdF4XP6KFEZlB2U3uQ17XRCtLsphQJYzknxLJ+MPrZHy00Z18HJlC4gMi3TBdxdbUBDxrk+ZZMla5Z5hXcdhFYO4ATQFxDdaItRx4bCFQ5VMJRIpJw7bRAjindkNCE70rJTPy+Ol0ZPxyCnrxoN0rVhJHKrI0pBdNA2iQ2Bs60mVK6Dc9k7u8u0GBuNjv3c4JAhabzypP+Ir+in8CVshw/i14eiKJ5MA6e3tTSScpNglBhAMwxzpnta9914T49XKzbulesZKDw3VsvrLsl0g0Q+YHAHBQ7KR7uqW7WQ/5Dr/fnBgyXQ7bCWyBUUMQKJ+q0LCgRYwmnQcJXESSuIIhyQcvx6tsBDItL/kgMs+ax+9cHP8ZzNtXR/LjtVymVKbU6iYLvV55En8gO3EbuNILRFHDrgNPZj+x4EObpLQLxkbKlAIRiBAECeldoBvJVJqDzuMYIdreYHpcpjXRLvuDrYHq39/P1600tl27Xqz02gB0Fgdb43JoRNzvTSHWgPyaZQThK9TqE2QbYUItvRhE4BBDlF3MBIFgJI4jhOX1KUlRVstkNCw9Cy5yFVe+f7z19zGxsY1K92uvL7RfPbuo9YJonhxrSB61IGniTCSaSX0GgKfN7r5mJtB4b8Gf9dB+aMdrZ4nxC0NBuWXc5KTkNhYvbqQX1iZMhw9EiKsp9wPgug1EB68fGHHt6X7Z44dszqDg69H0k9mlmFDT3QZnAD4WoZupwUJpx61A9YPHlAYI6DPMfLaE5YMDS2/ah8kBAqlnazdlUz0rn5am18ZXsn3598Ih9R4rRZsHMABEOQsqnih+mtubuPanDVUaacHMs/GG4YcgvuJsALZQb+ScCf61B7gYK+lojUoUvggg8OaAQ6t2LJ+tQb+VT+TI3gknppay6860Ui4e3Kh/0WQnoU0rySYAiBYjPmYKIymZ3esTixbtWIj3Smnazl6Hh2DRGiCdD5ZPGbsk0m100Gzi36KwbwNQsZiOGNSvdCnP0pYaGiLRUAhEg69ri9Fol/WFioJp8sWS/hf9H9QEJlHQJDmRdgPjsuU9342M5v9c2pmxhqqPTh3/m1jaJzJgAIGHlQQCLvHxZneh+9GNzkMBCXLJj0zlIqDUeTQSQQkn7/cm0gKiMbMyvxldHsCIsOYTkXcYZtktI+zE5XMx+XfcwLSPP/23fXmE98mCZ2MzQBc2XTYIXoYex+axHAQBEaHMFFAniHF1vDeBKGfiEWm1qYkbyWjsaCcyIO3519ol4RVAASVWuRhaGfjH1mc2LH4c25jyRpoDpwfSTfGsgDxhAOBKRaYXh1bLMwvAbLHBfE4aHqCYCKFZ8UAiBFAhMczSEhAVvKplOTfqLRYwaAdG55PBPpkop38taJEBy0M4Sl/z/Vns8s7vl1bOmHdGHh/vjwiL+sIYtaiJNwBfZBb63PdB8WWXRg4dm/3LAY2QeDVXABNTA4NPVwXkNtrV52UdFkCYtuxWGyy/wNcC4mKBnEjFWg4ij2pZ/78+za9NG3du/hoIL34NlfaBFExVyoHw6sPxywMCxLTF7PUMzVBuIEg9B0DgHEROjAIxMCTPvVN/11pG1HRjV69Agh7QLgYCxBWZVJgq9h6vDE9vXT/P5lm+hNXGYXx+QOMxqWWJiOjdvqBXLoQUptUPlDSAjOgiAtBkApIgGESDVEpTHSoBkiMCVgRW6wyQ8FoIoioUcoqLgUTBVtt6hKpca9LonbRuMXnPOfMG4inFGbu3Jl7fves73nH1zIy3v1q7cO3dGzbJgowwfChgVxmB7U9JIia5GoTVikneMgTSK92xAcaiCslLkb8ECGBSidPysIKDZZUEDhX2ntZn2zDhUyupWcR5EraEZcC/2DHWx8s9C8sxHxz8fj09LO3dPQCcxsuLxyXpZjUO3WvlRwA0crmqqb1XiqG4GLd8pz6Jx1Eu2umj00T0VBoclaqBS5ZmjWbhrWU1HO8S+Tlp362ynaxvc2BcEzEW9K656dD/QBZ8LWPt79Q1BuP3y/ngADmoB9BJGwRFloaAbKmXLAF04tcovQ0JAksuh2HXJ8kopf1BpeXRkOTE/uioQmA4AYks2q175IqpZY999Tz7LMga0HWEQRy2WV/xD5f6D+80J/nG6gaHGlJJKqKmdSpDfTBLyHSRGvth7ordWH/izMFhY8twRqDy7Ms2wiJ0m+//USqKUPPoica/dQPG0yEjpZedunzR7PO8R7QB8zUD2ado90VxDVpyqNp66rvPv+8HxbJ6/eN11dXVycSbxVpeVIUCCuxxYJzqDUNF7AtkuSJEyZaa2pJsu7TyRAk+o4FnILMhmYvF5ANE6GsyY+eyjqnn6/xZ930bU8FBEOLuvR8oiFEcx3t+scCQER8IzOd9fVV42/db4U25VVwFcdxkUW4S0vb8Bl2tsNgmXNN4OXWieDxbCj63u33fJsMJcmuias0OokzGNSlRycnj34Cv6PRbfAODtnMOGotn6zU15mGanIxKpLCH199vgB75OX52qtm5gbr4729bOVgMacxONSsUFzcyKU0uRXU05Q2AYVawUDsWWnoZIbo658ITeC51qV1E6G7ZaQuwrfQvEBR56U/49/RrHe4ykntxfkJAhF/5R7i3yA5tKv/MEAQ7fXxooEiBdGE56JBY1PvJDEkiIkhkkqhrgXUe0ZxR5LRuzUbpaFWp/n5OuiSaWkC4jcRRFw7Jamd+31Z+x6EY8tG2M/R6GTyUwNBvOge4k9///RVWaxsIc83XlTU/UJ3UecSVwlc9aWEGJxk0lTEoAfxXlIHewSSNW0g3c0wjycxogqkwyio1eAhfzJ0N96nCH54iBxnSoXookmWdzL/z8r6/twX7ySfyoomk5PHQ9FPzCCy9bYpI+PE4LtD4a/+/iPmK05033fvQG98gDkG5kW5cCtkWlT3I3T+bRxpckxlA191buW6QCDTWp+F3oRBAgH5PxnNBBHOvz00kaYgcg44g7JAl/XgqhWZznuv2/cRdjOjRz+ha81Gs5K1AiIrMZlTtAy+9EEzVlb9voqRO3pnirqLUEfoppwhnf9r3vP+On8tOHiZQGBxSmVFQKA9BRj615kDuG7mpg6YPE4Oyteh6M0BoLx5PJphHgVJS+esFE3vlcyCKgSxycnDz8v6WtPLuWjW0VqJEIBkbM787sefWru+ivU/7esp7o3XV1XFG668SusCLHL+X8/Djzcs8+wAGwfPZIUbLIATt6fQJGtAND1bIEWTAhIMBCGBz6KhLyf2TYaidzsO3Q2UaVzAT98yIQg5dGmoCR8fX5vMin4LkCBAMh8e+qqtLRwrJ0g8Eb8xHi9ifdOM8af37/AV5896ixwDI6Q2nfG8j8fGxoaHtyHzcmweWI1icQsSswgjS+TN0GegJoY0g48kT4aOT05kCIQzCDjQK+rY3JoSgtgwwFo8NmwsDLU/v7MOIfLy9s2ZtbFYefmh8v7Dh31zxcWDxYlXet9mf6VLjXnvT4y9hz3vDEbLGzehu/4VtgimcRawDVaiU58aWwHLyvCwAoHEQl0bXD8lGcqgaxGE54lvWZyThsNSgmzA0lb7nlVJGMIRLIzMFlCbOIBgEJaZvTMPcvhpiG9mZKA9MdTQ+wpKujXop+BUKB7pnvcbRsscmP3mzWMocHnqSwljnjd2Fo52IXABv6eAIjBKcqUuaC2vRb/kXJ1LDBJYKmYJYQbGJTi2DgJE7pQ1QdSEYl8HYi1jNdakFQRI7c6nDz8tHIdjvkT1TLx4JNFxL7sLlpFhgIiTe95pRNTWDEwwT3tT//yweGHFVgEAmZ9fREI4651dxOMV+JkZBcIwV0VvD82mUXkD4RMahA8IphEStAwMbYXFnIPepZ7lWjUgQQXwb9+cXQsI4Qj/6Kvu6bm1oveVjt/VMTB2I4j4OEAwkc2QWSxuPOU3+CiiZEwYA6dA8nHwmo8971eGjDpYmlUXPnmP9WKDxruBqGORDm9hqBuH3zzTDbM0TNSxACITXx1NS/ACJDP76by8XeWHrt895NtfvX9/YeORxka6hlR2CY6xdSgf0BYGIcjp+amPf/14yptPv1Q6rTGg4h7/5S1iF2NFcARERfW0FB2dpOrqXErC19kv6nsCuhMib9JeBSy6lCAJQS6xHta8TkD8mn67CpqHfjpWl7fLVzG6/0jNUvVodUBnrVBUQPwIaIBs3KIgGVtPIUbGoDKLBM44hXv8A0GCOBpworddYwE9+j0GwV/qf4ylVSBBmkOTGECstRXTMExoEW1izSIEkTqC4B08ceLt90+0AWT/aE5f4+hI4jEDQUwJyAb4NEAkZeF0DjcwOJvy/mS4rQKB68PjhCAoUEHRliDkmKCmQbqVyRrXgggg0KwOucGwgdhiZNUgQWdtzFrYRnnmtfufeeG7IUxROjt79hd2zsz0iJOy379yRZw+SJfZiNm4cnAACBAGiYCgHgNE7iZAoC1X3EDhfRc1M/eFkgzya3BYDbUKBCY30QtTjIMgrEg2OXYjVy5GLWnJ0Hv7nc/ceEfv4FDssK8TrtXYMFM/qilHPur8vHcat/dX8AAEVWTrDs/7ZwNCcd67wM2AMYAgSAGyPpieRhByGIlI4BGsYpl2baSQMoMD2WAYukp34jjcPqFOfFPraOZ2cCDUMzdnDBZ3vNLx3J7mQ74jfYXLfZ05LQPWlYv7LnpTZ4II5an16zeeufDr1q1bcOSUf91p+BzbRgHB5iVANkJFARF1N+KfNE30s9nj0Tf5gIAwHBoyR+LnZehb5ggqa3HcIk+Lhw2eyRGQ3LtjR+bmlteLi9ofizxZ4Is0lpTUDFQ/US03ya+5Enl1alFyK5Q7680DRArHIkrgn1og8PQM6jFAUMok2EVdt7sM9eFWkzcrB0HEIlTedcsCgtcJYhz0K/nDv+pjqr94E8Q2htKQsjdlZO/cuWNHdrjwxZbHcg82hX19uSU1SzOJ9h5N9+rUw1MeNP8nuF7u+tR2zP/OnJUjp89rrlkFAhISKwh2YwXk5pOhfQj8IEHU39gi622gCaAPawsIbbGVEgt7/e30F+Fp7DJhkB07haSybWjvgbbHC2CR3NzCxobR9idYtHBd/ODX8Ngwnq6HtmdOAQTBfmZseAW5TPSigtIhbdFtcXqUkOCAgHx2/Pg3/BT5PD6gutrYKwdbT+0kGSN8TfWna0DkTKIoHIQwuuFIz4JF2nJzc8O7y9vafMuRSF9jxfjcE+nMn7h7/BWE0O9ld5IjWegv2uOAeygQuiNuR3AqZrezoZOPWGhQmHw1MREE+gGE39kgI0gMEGITCREe0VWzW1gDiVvZUtWzEe0vnsi/q7l8125YpKZkaTQxPlPPeNWLmwbAEA5UEt33FtfZTvPwJHUkikb4NQKCF7+ZECryuYScJhwuSGASaWAD4gAkscOisrWVIlYiic/Hl5tBuDjkLvb7b7cc66rcVVnm61su6RudSYx0byCJE6NAZYdslm8jgIFPuHesxqItTBkanCVH320+Rw7NU6YknpOEQeK6ey7beYq1B/SulDepdcyxxKupF/ykqDvxXNuhsrrXfY1Lyz098bmZhJ8kjkJLISkoqx5B140MCFjJaQoF2GFm4KiYToxDJ3SNMSigOFUnCUxiCzTBUFo9iSBEN+VdrlMO3uHtW8QzOp4pbjhQ2XXidV/f0nLNrUVzVfV0PjMEPQqKZzrZrCJ/xSSbqCq9TBtXBcF9gv5AoSUBDA6qKZIqKawqfn4pUQHoWbZKYaSCxEBc2Fj18UNH4RAS7kC89GjV662xwieGfMvLNY0VHTPt1Yw+Wg0CJQUjW2QViv2lo6mbbdmELKWaBCXYIfZ+4V2vHNaoMGwoeIt9vRLiSj5EncKytYa4lc6AAqfTIBQm+u74G0Otra1tzb7lmuWlI70d40vrCELvEwHHDopDMZukYPSp2yvjFbZQUl/ywSu618HbTA6XHNQkbHlF5CSeJ2cRVPCEED8WS9pCS5YxDgEpfj0/vKsgHK70ffhhZLmm45ZXqgScGTTD9EXFgawl4Qt8aiKZgw4EXS3AIYwwaTRTsREwg1hS5KSB1Y1FD0B0Mg1juivnGSp6H5ysVxDL+Y/dtbf8+vLy1jbfwbqSyFL39B3T6elUBRxUNhscFBpFWRQk28QOwihMx6tdx8pN0BKriw9y8BGmWCx1wsEVPk9iOgKJeyd+mz2dGIhlzGPH2g588EvX0AFfU7igJtIzOlfBqzM2UGvAAZDSUoI4/yKHgvAgRUig9FohEwPERbpagiIg+ObP6raDtNdolFncWc8v1cbdHKZL5ktrKF7sqqwM5x44CJCmuprlyIeRkumtFAkOYoBj5w0715AIRsoiehTHaROkEGVJKarr8HQ6DCWoJHYSu2Fy0C40CC3ish2ThFrEgVB7ckD45N62yrKyoa7H87t9dQefzO1rjDQdLHyAwUuDQEdBuAEWoRiJ4yAqxUDoD6SguGov0UEhx/9BnEHMagShSYKsLDYvolhs2BJpu6Bk31V2YHC85qaHprt9rXX5JTWRSFNT000vMQicQcS1YBKKAwEkSchKEol3Xty1Vg5EDQEhiHM5bVtYtW1XSA0CEAw7+GFMrVbrV4NQwAHB9+2erd5zLHEkP7+i4e2bfAcP1kRKSko+/LCurunxWDjcdLDp8aaC3QUFu2OVu/J2Ve5u6zrQXFBZFotVFjSHbyooK4s1txXEKit37w43F+y5qyaSs6c1P/dAYaRxrrihoq+zt6eieqb+yMj9FYn6loamh27trG4sbGnMeW5//eh4fWHfkc5bE3MtAwMd0y/gf9VzOTn7cyKFVY+OV4wc69u7N1IIOdLwQryhqr5ourc93j3dHe9uqFnOqW/Ieahh794n78rfU9ccDnfd1PJc/q31I3uamkeKGwr/a99sdp0EwjDcGE30mpjFcMIMpCUyC0IasAiBAO1KFhLowkIibEzkR0prND2xXXRh22hOYjeansSTGhf+RLduXOtFONXLkCe0E5JJOw/vzEcpoTMb2c13sxk2dtNUFd/lq9GIjyqOq7g+++xM45rhr19Kvz+ff5jPFY5TxhzfzThtPKbVu7tyQZmk05UAhAYb643q9fLJlyleu58H4kSMwUoOijiXsqmc78SNEEqmGasGUCcWkpG7eO/KsSC+WA8WFxcPVGFVQotIpgAe9K6AW4sPXr8bJrVb72RXCGIoJ7rlQ4yEjI/srr3rXa2LFyjiSzyDtPw2M7ua2bPKH/F8xfHciLpRIU3haCJVhfWuws9prWaZpxrPKRp1qXzawe/yvI+ObqzrRSEALMSeM42D5TkANXBz9fzl6xf3dw725EAmBFq6VSBTIhI9A8cPwJjsDuJDMQWh3Swfv+3VrxeqleV7GQomQN+OSM3VB4NDUgS7unYKUFuQlLCMYCZg5BPLhaX80UI799sqgn4NOiMqUo1serFIB6koWr+vVaOq69Mjr/XPvsr8asWzypydswzDzLV5/28sNBXO58e8LaBvxREgFCe610hbYKQeWKbboUnn2KvFwLLxdjsNDNlYmvnfMeLUQ/U7A6JLcbLYpEvYDPPEeZ3Hn93Cyz6XO4tesUqCJyN1MhkYLn027/779wO3LoAFsyXUEYD4EkCik5J+cRJcWaNKk0Gn4mzpO10isxGnVBxL74+ydKgnK02h/3Qz7AxqDMP252yfZRn66p9Q6JoZUyG+PMqgXKHCSXGMQQriuMGpEVjEUs8fq03YLb9MYwCAvFXz6TKWkxRY60WdE7xz1TRNpTCUtncml8C0SWyRRkfS0DQlOMzfTQa91CsWT17de0NNRKFYESKXXrFeB3KYheXRekF/7L59hbnubCR0rs9mI+nLcMZzlD5zNl4XkUanPzdnTx40Al97+pRhGerEsPNTw9KNavUVTVOgh2V6dAfbJMF0w0Seyk6AZB29fPwZIrxLjXNDFFSjNozDVsJgu1U3dY11VCwP8RKUsIHyxohN04QlpJ+W6JJOQlOOc3WwXoLNk4dv795+9OnqfgIs5BYIWpI1RCH0Kx8DL3l/8d4e0Qxg51ZUVdGsomGcRv6Msc/fORmd/YrG0F2i8zQNhhpRDQp91xhWm1PHpyclJUQowQPxx89zQ/amQxEddbxTBVk3Hj7MZsPauBy8vtin58tDkg9UZ7MJ1L2zBNKDxUvDWOaeDs21s5jUAqkAtG1iClOdFlFvqiPjMgb56ycvXz589XLiFMnmSrakWRNmeklFbA7s9ea4DrAU+lz3RudaSE8h3eqUxrNnz87YrbtTV2y0gtoZ3R/P/yn8VZmz/1S4KGKfPz/11kKdJo0uexcXExXphJbM2pNGeChIzhPZj3DvSY2E3N0bwfYg9gbqu30wue+kHjKwCg6fpyZspvJySCBa8kBo9IxOKigNdQHLjqjWQSr2ek8e9x582ss7omcki7qjEpKoq3T1I4x8QkhD7NC/fq3TuXaD+F26sNmz0+D6wnryTTnra/5v5XQr6Oxk0KciDG1oS1PQOCw0z0+dGSsZoqJw0V5U02SHJRQkNZB8JHg4WMQJsXdOInz/vlSDBDgXojHFNJ/BC2CVECbYIySEopM03zM7yxpw1CObYlkCAMX+arK/PHjyXhTvX4n1wcEWxnoW8VGXG3O+j/SQ6FZ2fMr0ufLmtU5LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS8v/xh8DdZmnK37PfgAAAABJRU5ErkJggg==", + "description": "Displays historical flooding level values as a simplified chart with background. Optionally may display the corresponding latest flooding level value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'flooding', label: 'Flooding level', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'flooding', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flooding level\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 2 - 1;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 5) {\\n\\tvalue = 5;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 2 - 1;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 5) {\\n\\tvalue = 5;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"#224AC2\"},{\"from\":1,\"to\":3,\"color\":\"#F77410\"},{\"from\":3,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Flooding level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"flood\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":1,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"m\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "flood", + "flooding", + "water height", + "flood depth", + "flood stage", + "inundation level", + "water rise", + "overflow level", + "flood peak", + "high water mark" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_flow_rate_chart_card.json b/application/src/main/data/json/system/widget_types/simple_flow_rate_chart_card.json new file mode 100644 index 0000000000..cb49ed98b9 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_flow_rate_chart_card.json @@ -0,0 +1,35 @@ +{ + "fqn": "simple_flow_rate_chart_card", + "name": "Simple flow rate chart card", + "deprecated": false, + "image": "tb-image:c2ltcGxlX2Zsb3dfcmF0ZV9jaGFydF9jYXJkXygxKS5zdmc=:c2ltcGxlX2Zsb3dfcmF0ZV9jaGFydF9jYXJkLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjExMiIgeT0iMjQiIGZpbGw9IiNmZmYiIHJ4PSI0Ii8+PHJlY3Qgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxMTEiIHg9Ii41IiB5PSIyNC41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik0xOC43IDM5djEwSDE3VjM5aDEuN1ptNCA0LjR2MS40aC00LjR2LTEuNGg0LjVabS43LTQuNHYxLjRoLTUuMVYzOWg1Wm0zLjItLjVWNDloLTEuN1YzOC41aDEuN1ptMS43IDYuOXYtLjJjMC0uNSAwLTEgLjItMS41bC43LTEuMmEzIDMgMCAwIDEgMS0uN2wxLjUtLjNjLjYgMCAxIDAgMS41LjMuNC4xLjguNCAxIC43LjQuNC42LjguOCAxLjJsLjIgMS41di4yYzAgLjUgMCAxLS4yIDEuNWwtLjcgMS4yYTMuMSAzLjEgMCAwIDEtMi41IDFjLS42IDAtMSAwLTEuNS0uMmEzLjYgMy42IDAgMCAxLTEuOC0ybC0uMi0xLjVabTEuNy0uMnYxLjJsLjQuNy41LjVjLjMuMi41LjIuOS4yYTEuNiAxLjYgMCAwIDAgMS4zLS43Yy4yLS4yLjMtLjUuMy0uOGwuMS0xdi0xbC0uNC0uOGExLjYgMS42IDAgMCAwLTEuNC0uN2MtLjMgMC0uNSAwLS44LjJsLS41LjVjLS4yLjItLjMuNS0uMy44bC0uMSAxWm04LjggMi4yIDEuNy01LjhoMWwtLjIgMS43LTEuNyA1LjdoLTFsLjItMS42Wm0tMS01LjggMS40IDUuOFY0OWgtMWwtMi03LjRoMS42Wm01LjQgNS43IDEuMy01LjdINDZMNDQgNDloLTF2LTEuN1ptLTEuNC01LjcgMS43IDUuNy4yIDEuN2gtMUw0MSA0My4zbC0uMy0xLjdoMVptMTEgMS40djZoLTEuNnYtNy40aDEuNlY0M1ptMi4zLTEuNFY0M2EzLjIgMy4yIDAgMCAwLTEuNCAwIDEuNCAxLjQgMCAwIDAtLjkgMWwtLjEuNmgtLjRsLjEtMS4yLjUtMWMuMS0uNC40LS42LjYtLjhhMS45IDEuOSAwIDAgMSAxLjMtLjJoLjNabTUuMiA2VjQ0bC0uMS0uN2ExIDEgMCAwIDAtLjUtLjRsLS43LS4yLS43LjEtLjQuNC0uMi41aC0xLjZjMC0uMyAwLS41LjItLjguMS0uMy4zLS41LjYtLjdhMyAzIDAgMCAxIDEtLjUgNCA0IDAgMCAxIDEuMi0uMmMuNiAwIDEgMCAxLjUuMi40LjIuOC41IDEgLjkuMi40LjQuOC40IDEuNHY0LjJjMCAuMy4yLjUuMy43di4xaC0xLjdhMyAzIDAgMCAxLS4yLS43di0uOFptLjMtMy4xdjFoLTEuMmwtLjguMS0uNi4zYTEgMSAwIDAgMC0uNS45YzAgLjIgMCAuNC4yLjUgMCAuMi4yLjMuNC40bC42LjFhMS44IDEuOCAwIDAgMCAxLjUtLjdsLjItLjYuNi44LS4zLjZhMyAzIDAgMCAxLS41LjYgMi42IDIuNiAwIDAgMS0xLjguNmMtLjUgMC0xIDAtMS4zLS4zLS40LS4xLS43LS40LS45LS43YTIuNCAyLjQgMCAwIDEtLjEtMi4ybC43LS44IDEtLjQgMS41LS4yaDEuM1ptNi43LTIuOXYxLjJoLTQuMXYtMS4yaDQuMVptLTMtMS44SDY2VjQ3bC4xLjUuMy4yaC40YTIuNSAyLjUgMCAwIDAgLjYgMFY0OWE0LjIgNC4yIDAgMCAxLTEgLjFjLS41IDAtLjggMC0xLjEtLjItLjMtLjEtLjUtLjMtLjctLjZsLS4yLTEuMnYtNy4zWm03LjggOS4zYy0uNSAwLTEgMC0xLjUtLjJhMy4zIDMuMyAwIDAgMS0xLjgtMiA0IDQgMCAwIDEtLjItMS4zdi0uM2MwLS42IDAtMS4xLjItMS42cy40LS45LjctMS4yYTMgMyAwIDAgMSAxLS44bDEuNC0uMmMuNiAwIDEgMCAxLjQuMmwxIC44Yy4zLjMuNS43LjYgMS4xbC4yIDEuNXYuN2gtNS43di0xLjJoNHYtLjFsLS4xLS45LS41LS42LS45LS4yYy0uMyAwLS41IDAtLjcuMi0uMiAwLS40LjItLjUuNWwtLjQuN3YyLjNsLjQuNy43LjUuOC4xYTIuMyAyLjMgMCAwIDAgMS45LS45bC45LjktLjcuNmMtLjIuMi0uNS40LTEgLjUtLjMuMi0uNy4yLTEuMi4yWiIvPjxwYXRoIGZpbGw9IiMzRkE3MUEiIGQ9Ik0zMC44IDExMy40djIuNkgxNy41di0yLjNsNi41LTcgMS43LTIuMWMuNC0uNi43LTEuMS44LTEuNmE0IDQgMCAwIDAgMC0zYy0uMy0uNS0uNi0uOS0xLTEuMWEzIDMgMCAwIDAtMS43LS41Yy0uNyAwLTEuNC4yLTEuOS41YTMgMyAwIDAgMC0xLjEgMS4zYy0uMy42LS40IDEuMi0uNCAyaC0zLjNjMC0xLjIuMy0yLjMuOC0zLjIuNS0xIDEuMy0xLjggMi4zLTIuMyAxLS42IDIuMi0uOSAzLjYtLjlzMi41LjIgMy40LjdjMSAuNCAxLjcgMSAyLjEgMS45LjUuOC44IDEuOC44IDIuOWwtLjMgMS44Yy0uMi43LS41IDEuMy0uOSAxLjktLjMuNi0uOCAxLjItMS4zIDEuOGwtMS42IDEuOC00LjMgNC44aDkuMVptMTUuOS05djMuMmExNSAxNSAwIDAgMS0uNSA0IDcgNyAwIDAgMS0xLjMgMi43Yy0uNi43LTEuMyAxLjItMiAxLjVhNy43IDcuNyAwIDAgMS00LjkuMiA1LjMgNS4zIDAgMCAxLTMuMi0yLjZjLS40LS43LS43LTEuNS0uOS0yLjUtLjItMS0uMy0yLS4zLTMuM3YtMy4yYzAtMS42LjItMyAuNS00cy43LTIgMS4zLTIuNmE1IDUgMCAwIDEgMi0xLjUgNy42IDcuNiAwIDAgMSA0LjktLjIgNS4xIDUuMSAwIDAgMSAzLjIgMi42Yy40LjYuNyAxLjUuOSAyLjQuMiAxIC4zIDIgLjMgMy4zWm0tMy4zIDMuN3YtNC4yYzAtLjggMC0xLjUtLjItMi4xIDAtLjYtLjItMS4xLS40LTEuNWEzIDMgMCAwIDAtLjYtMWMtLjMtLjQtLjYtLjYtMS0uN2wtMS0uMmEzIDMgMCAwIDAtMS40LjNjLS40LjItLjguNS0xIDFhNSA1IDAgMCAwLS43IDEuN2wtLjIgMi41djQuMmMwIC44IDAgMS41LjIgMi4xIDAgLjYuMiAxLjIuNCAxLjYuMS40LjQuOC42IDFsMSAuNyAxIC4xYy42IDAgMSAwIDEuNC0uMy40LS4yLjgtLjUgMS0xIC4zLS40LjUtMSAuNy0xLjZsLjItMi42Wk01Ni4zIDEwNS45VjExNmgtMi44di0xMi43SDU2bC4yIDIuNlptLS41IDMuM2gtMWMwLS45LjItMS43LjQtMi40LjItLjguNS0xLjQgMS0yIC40LS41LjktMSAxLjUtMS4zYTUgNSAwIDAgMSAyLjItLjRjLjYgMCAxLjEgMCAxLjYuMnMuOS41IDEuMi44Yy40LjQuNy44LjkgMS40LjIuNi4zIDEuMy4zIDJ2OC41SDYxdi04LjJjMC0uNiAwLTEuMS0uMi0xLjUtLjItLjMtLjUtLjYtLjgtLjctLjQtLjItLjgtLjItMS4yLS4yLS42IDAtMSAwLTEuNC4zLS40LjItLjcuNC0uOS44bC0uNSAxLjItLjIgMS41Wm03LjktLjgtMS4zLjNjMC0uNyAwLTEuNS4zLTIuMS4yLS43LjUtMS4zIDEtMS44YTQuNSA0LjUgMCAwIDEgMy42LTEuN2MuNyAwIDEuMiAwIDEuNy4zLjYuMSAxIC40IDEuNC44LjMuNC42LjguOCAxLjQuMi42LjMgMS40LjMgMi4ydjguMmgtMi45di04LjJjMC0uNyAwLTEuMi0uMy0xLjUtLjEtLjQtLjQtLjYtLjctLjdhMy4xIDMuMSAwIDAgMC0yLjQgMGwtLjguN2EzIDMgMCAwIDAtLjYgMWwtLjEgMS4xWm0xMi40LTUuN2gxbC44LS4xLjUtLjRhMSAxIDAgMCAwLS4xLTEuNGMtLjItLjItLjYtLjMtMS4xLS4zLS40IDAtLjcgMC0xIC4zLS4yLjEtLjQuMy0uNC42aC0yLjFjMC0uNS4yLTEgLjUtMS40YTMgMyAwIDAgMSAxLjItLjkgNS4zIDUuMyAwIDAgMSAzLjYgMGMuNi4yIDEgLjUgMS4zLjkuMy40LjQuOS40IDEuNCAwIC41LS4xLjktLjQgMS4yLS4zLjQtLjcuNi0xLjEuOC0uNS4yLTEuMS4zLTEuOC4zaC0xLjN2LTFabTAgMS41di0xaDEuM2E2IDYgMCAwIDEgMS45LjNjLjUuMiAxIC40IDEuMi44LjMuMy40LjcuNCAxLjMgMCAuNi0uMiAxLS41IDEuNWEzIDMgMCAwIDEtMS40LjkgNS40IDUuNCAwIDAgMS0zLjYgMGMtLjUtLjItMS0uNS0xLjMtLjktLjQtLjQtLjUtMS0uNS0xLjZoMi4xYzAgLjMuMS42LjQuOC4zLjIuNy4zIDEuMi4zcy44LS4xIDEtLjNjLjMtLjMuNS0uNS41LS45IDAtLjMtLjEtLjUtLjMtLjdhMSAxIDAgMCAwLS41LS40bC0xLS4xaC0xWm0xNC42LTUuM0w4NCAxMTcuNWgtMi4ybDYuNy0xOC42aDIuMlptNC43LS45djE4aC0yLjhWOThoMi44Wm0tLjUgMTEuMkg5NGMwLS45LjEtMS43LjQtMi40LjItLjguNS0xLjQgMS0yYTQuNSA0LjUgMCAwIDEgMy42LTEuN2MuNiAwIDEuMiAwIDEuNy4ycy45LjUgMS4zLjljLjMuNC42LjguOCAxLjUuMi42LjMgMS4zLjMgMi4ydjguMWgtMi44di04LjJjMC0uNi0uMS0xLS4zLTEuNC0uMi0uNC0uNC0uNi0uOC0uOC0uMy0uMi0uNy0uMi0xLjItLjJzLTEgMC0xLjQuM2MtLjMuMi0uNy40LTEgLjhsLS41IDEuMi0uMiAxLjVabTEzLjktMy41VjExNkgxMDZ2LTEyLjdoMi43djIuNFptMy45LTIuNXYyLjdhNS43IDUuNyAwIDAgMC0yLjUgMCAyLjQgMi40IDAgMCAwLTEuNSAxLjZsLS4yIDEuMmgtLjZjMC0uOCAwLTEuNS4yLTIuMmE2IDYgMCAwIDEgLjctMS44Yy4zLS41LjctLjkgMS4yLTEuMmEzLjIgMy4yIDAgMCAxIDIuMi0uNGwuNS4xWiIvPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii45IiBzdHJva2Utd2lkdGg9IjEuNSIgZD0iTTE4NyA1NGMtOS41IDAtOC40IDU2LjEtMTguMiA1Ni4xLTguMyAwLTkuNy0yMy44LTE2LjEtMjMuOC02LjUgMC05LjMgMzIuNy0xNy42IDMyLjctNi45IDAtMTAuNy02LjYtMTcuMS02LjYiLz48L3N2Zz4=", + "description": "Displays historical flow rate values as a simplified chart. Optionally may display the corresponding latest flow rate value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'flowRate', label: 'Flow rate', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'flowRate', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flow rate\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#305AD7\"},{\"from\":10,\"to\":30,\"color\":\"#3FA71A\"},{\"from\":30,\"to\":50,\"color\":\"#F36900\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Flow rate\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:hydro-power\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"units\":\"m³/hr\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "liquid", + "fluid", + "flow rate", + "fluid dynamics", + "velocity", + "mass flow", + "volume flow" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_flow_rate_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_flow_rate_chart_card_with_background.json new file mode 100644 index 0000000000..6c09f9951f --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_flow_rate_chart_card_with_background.json @@ -0,0 +1,35 @@ +{ + "fqn": "simple_flow_rate_chart_card_with_background", + "name": "Simple flow rate chart card with background", + "deprecated": false, + "image": "tb-image:c2ltcGxlX2Zsb3dfcmF0ZV9jaGFydF9jYXJkX3dpdGhfYmFja2dyb3VuZC5zdmc=:c2ltcGxlX2Zsb3dfcmF0ZV9jaGFydF9jYXJkX3dpdGhfYmFja2dyb3VuZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxMTIiIHk9IjI0IiBmaWxsPSJ1cmwoI2EpIiByeD0iNCIvPjxnIGZpbHRlcj0idXJsKCNiKSI+PHJlY3Qgd2lkdGg9IjE4OCIgaGVpZ2h0PSIxMDAiIHg9IjYiIHk9IjMwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii44IiByeD0iMyIvPjwvZz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNMTguNyAzOXYxMEgxN1YzOWgxLjdabTQgNC40djEuNGgtNC40di0xLjRoNC41Wm0uNy00LjR2MS40aC01LjFWMzloNVptMy4yLS41VjQ5aC0xLjdWMzguNWgxLjdabTEuNyA2Ljl2LS4yYzAtLjUgMC0xIC4yLTEuNWwuNy0xLjJhMyAzIDAgMCAxIDEtLjdsMS41LS4zYy42IDAgMSAwIDEuNS4zLjQuMS44LjQgMSAuNy40LjQuNi44LjggMS4ybC4yIDEuNXYuMmMwIC41IDAgMS0uMiAxLjVsLS43IDEuMmEzLjEgMy4xIDAgMCAxLTIuNSAxYy0uNiAwLTEgMC0xLjUtLjJhMy42IDMuNiAwIDAgMS0xLjgtMmwtLjItMS41Wm0xLjctLjJ2MS4ybC40LjcuNS41Yy4zLjIuNS4yLjkuMmExLjYgMS42IDAgMCAwIDEuMy0uN2MuMi0uMi4zLS41LjMtLjhsLjEtMXYtMWwtLjQtLjhhMS42IDEuNiAwIDAgMC0xLjQtLjdjLS4zIDAtLjUgMC0uOC4ybC0uNS41Yy0uMi4yLS4zLjUtLjMuOGwtLjEgMVptOC44IDIuMiAxLjctNS44aDFsLS4yIDEuNy0xLjcgNS43aC0xbC4yLTEuNlptLTEtNS44IDEuNCA1LjhWNDloLTFsLTItNy40aDEuNlptNS40IDUuNyAxLjMtNS43SDQ2TDQ0IDQ5aC0xdi0xLjdabS0xLjQtNS43IDEuNyA1LjcuMiAxLjdoLTFMNDEgNDMuM2wtLjMtMS43aDFabTExIDEuNHY2aC0xLjZ2LTcuNGgxLjZWNDNabTIuMy0xLjRWNDNhMy4yIDMuMiAwIDAgMC0xLjQgMCAxLjQgMS40IDAgMCAwLS45IDFsLS4xLjZoLS40bC4xLTEuMi41LTFjLjEtLjQuNC0uNi42LS44YTEuOSAxLjkgMCAwIDEgMS4zLS4yaC4zWm01LjIgNlY0NGwtLjEtLjdhMSAxIDAgMCAwLS41LS40bC0uNy0uMi0uNy4xLS40LjQtLjIuNWgtMS42YzAtLjMgMC0uNS4yLS44LjEtLjMuMy0uNS42LS43YTMgMyAwIDAgMSAxLS41IDQgNCAwIDAgMSAxLjItLjJjLjYgMCAxIDAgMS41LjIuNC4yLjguNSAxIC45LjIuNC40LjguNCAxLjR2NC4yYzAgLjMuMi41LjMuN3YuMWgtMS43YTMgMyAwIDAgMS0uMi0uN3YtLjhabS4zLTMuMXYxaC0xLjJsLS44LjEtLjYuM2ExIDEgMCAwIDAtLjUuOWMwIC4yIDAgLjQuMi41IDAgLjIuMi4zLjQuNGwuNi4xYTEuOCAxLjggMCAwIDAgMS41LS43bC4yLS42LjYuOC0uMy42YTMgMyAwIDAgMS0uNS42IDIuNiAyLjYgMCAwIDEtMS44LjZjLS41IDAtMSAwLTEuMy0uMy0uNC0uMS0uNy0uNC0uOS0uN2EyLjQgMi40IDAgMCAxLS4xLTIuMmwuNy0uOCAxLS40IDEuNS0uMmgxLjNabTYuNy0yLjl2MS4yaC00LjF2LTEuMmg0LjFabS0zLTEuOEg2NlY0N2wuMS41LjMuMmguNGEyLjUgMi41IDAgMCAwIC42IDBWNDlhNC4yIDQuMiAwIDAgMS0xIC4xYy0uNSAwLS44IDAtMS4xLS4yLS4zLS4xLS41LS4zLS43LS42bC0uMi0xLjJ2LTcuM1ptNy44IDkuM2MtLjUgMC0xIDAtMS41LS4yYTMuMyAzLjMgMCAwIDEtMS44LTIgNCA0IDAgMCAxLS4yLTEuM3YtLjNjMC0uNiAwLTEuMS4yLTEuNnMuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS0uOGwxLjQtLjJjLjYgMCAxIDAgMS40LjJsMSAuOGMuMy4zLjUuNy42IDEuMWwuMiAxLjV2LjdoLTUuN3YtMS4yaDR2LS4xbC0uMS0uOS0uNS0uNi0uOS0uMmMtLjMgMC0uNSAwLS43LjItLjIgMC0uNC4yLS41LjVsLS40Ljd2Mi4zbC40LjcuNy41LjguMWEyLjMgMi4zIDAgMCAwIDEuOS0uOWwuOS45LS43LjZjLS4yLjItLjUuNC0xIC41LS4zLjItLjcuMi0xLjIuMloiLz48cGF0aCBmaWxsPSIjM0I5MTFDIiBkPSJNMzAuOCAxMTMuNHYyLjZIMTcuNXYtMi4zbDYuNS03IDEuNy0yLjFjLjQtLjYuNy0xLjEuOC0xLjZhNCA0IDAgMCAwIDAtM2MtLjMtLjUtLjYtLjktMS0xLjFhMyAzIDAgMCAwLTEuNy0uNWMtLjcgMC0xLjQuMi0xLjkuNWEzIDMgMCAwIDAtMS4xIDEuM2MtLjMuNi0uNCAxLjItLjQgMmgtMy4zYzAtMS4yLjMtMi4zLjgtMy4yLjUtMSAxLjMtMS44IDIuMy0yLjMgMS0uNiAyLjItLjkgMy42LS45czIuNS4yIDMuNC43YzEgLjQgMS43IDEgMi4xIDEuOS41LjguOCAxLjguOCAyLjlsLS4zIDEuOGMtLjIuNy0uNSAxLjMtLjkgMS45LS4zLjYtLjggMS4yLTEuMyAxLjhsLTEuNiAxLjgtNC4zIDQuOGg5LjFabTE1LjktOXYzLjJhMTUgMTUgMCAwIDEtLjUgNCA3IDcgMCAwIDEtMS4zIDIuN2MtLjYuNy0xLjMgMS4yLTIgMS41YTcuNyA3LjcgMCAwIDEtNC45LjIgNS4zIDUuMyAwIDAgMS0zLjItMi42Yy0uNC0uNy0uNy0xLjUtLjktMi41LS4yLTEtLjMtMi0uMy0zLjN2LTMuMmMwLTEuNi4yLTMgLjUtNHMuNy0yIDEuMy0yLjZhNSA1IDAgMCAxIDItMS41IDcuNiA3LjYgMCAwIDEgNC45LS4yIDUuMSA1LjEgMCAwIDEgMy4yIDIuNmMuNC42LjcgMS41LjkgMi40LjIgMSAuMyAyIC4zIDMuM1ptLTMuMyAzLjd2LTQuMmMwLS44IDAtMS41LS4yLTIuMSAwLS42LS4yLTEuMS0uNC0xLjVhMyAzIDAgMCAwLS42LTFjLS4zLS40LS42LS42LTEtLjdsLTEtLjJhMyAzIDAgMCAwLTEuNC4zYy0uNC4yLS44LjUtMSAxYTUgNSAwIDAgMC0uNyAxLjdsLS4yIDIuNXY0LjJjMCAuOCAwIDEuNS4yIDIuMSAwIC42LjIgMS4yLjQgMS42LjEuNC40LjguNiAxbDEgLjcgMSAuMWMuNiAwIDEgMCAxLjQtLjMuNC0uMi44LS41IDEtMSAuMy0uNC41LTEgLjctMS42bC4yLTIuNlpNNTYuMyAxMDUuOVYxMTZoLTIuOHYtMTIuN0g1NmwuMiAyLjZabS0uNSAzLjNoLTFjMC0uOS4yLTEuNy40LTIuNC4yLS44LjUtMS40IDEtMiAuNC0uNS45LTEgMS41LTEuM2E1IDUgMCAwIDEgMi4yLS40Yy42IDAgMS4xIDAgMS42LjJzLjkuNSAxLjIuOGMuNC40LjcuOC45IDEuNC4yLjYuMyAxLjMuMyAydjguNUg2MXYtOC4yYzAtLjYgMC0xLjEtLjItMS41LS4yLS4zLS41LS42LS44LS43LS40LS4yLS44LS4yLTEuMi0uMi0uNiAwLTEgMC0xLjQuMy0uNC4yLS43LjQtLjkuOGwtLjUgMS4yLS4yIDEuNVptNy45LS44LTEuMy4zYzAtLjcgMC0xLjUuMy0yLjEuMi0uNy41LTEuMyAxLTEuOGE0LjUgNC41IDAgMCAxIDMuNi0xLjdjLjcgMCAxLjIgMCAxLjcuMy42LjEgMSAuNCAxLjQuOC4zLjQuNi44LjggMS40LjIuNi4zIDEuNC4zIDIuMnY4LjJoLTIuOXYtOC4yYzAtLjcgMC0xLjItLjMtMS41LS4xLS40LS40LS42LS43LS43YTMuMSAzLjEgMCAwIDAtMi40IDBsLS44LjdhMyAzIDAgMCAwLS42IDFsLS4xIDEuMVptMTIuNC01LjdoMWwuOC0uMS41LS40YTEgMSAwIDAgMC0uMS0xLjRjLS4yLS4yLS42LS4zLTEuMS0uMy0uNCAwLS43IDAtMSAuMy0uMi4xLS40LjMtLjQuNmgtMi4xYzAtLjUuMi0xIC41LTEuNGEzIDMgMCAwIDEgMS4yLS45IDUuMyA1LjMgMCAwIDEgMy42IDBjLjYuMiAxIC41IDEuMy45LjMuNC40LjkuNCAxLjQgMCAuNS0uMS45LS40IDEuMi0uMy40LS43LjYtMS4xLjgtLjUuMi0xLjEuMy0xLjguM2gtMS4zdi0xWm0wIDEuNXYtMWgxLjNhNiA2IDAgMCAxIDEuOS4zYy41LjIgMSAuNCAxLjIuOC4zLjMuNC43LjQgMS4zIDAgLjYtLjIgMS0uNSAxLjVhMyAzIDAgMCAxLTEuNC45IDUuNCA1LjQgMCAwIDEtMy42IDBjLS41LS4yLTEtLjUtMS4zLS45LS40LS40LS41LTEtLjUtMS42aDIuMWMwIC4zLjEuNi40LjguMy4yLjcuMyAxLjIuM3MuOC0uMSAxLS4zYy4zLS4zLjUtLjUuNS0uOSAwLS4zLS4xLS41LS4zLS43YTEgMSAwIDAgMC0uNS0uNGwtMS0uMWgtMVptMTQuNi01LjNMODQgMTE3LjVoLTIuMmw2LjctMTguNmgyLjJabTQuNy0uOXYxOGgtMi44Vjk4aDIuOFptLS41IDExLjJIOTRjMC0uOS4xLTEuNy40LTIuNC4yLS44LjUtMS40IDEtMmE0LjUgNC41IDAgMCAxIDMuNi0xLjdjLjYgMCAxLjIgMCAxLjcuMnMuOS41IDEuMy45Yy4zLjQuNi44LjggMS41LjIuNi4zIDEuMy4zIDIuMnY4LjFoLTIuOHYtOC4yYzAtLjYtLjEtMS0uMy0xLjQtLjItLjQtLjQtLjYtLjgtLjgtLjMtLjItLjctLjItMS4yLS4ycy0xIDAtMS40LjNjLS4zLjItLjcuNC0xIC44bC0uNSAxLjItLjIgMS41Wm0xMy45LTMuNVYxMTZIMTA2di0xMi43aDIuN3YyLjRabTMuOS0yLjV2Mi43YTUuNyA1LjcgMCAwIDAtMi41IDAgMi40IDIuNCAwIDAgMC0xLjUgMS42bC0uMiAxLjJoLS42YzAtLjggMC0xLjUuMi0yLjJhNiA2IDAgMCAxIC43LTEuOGMuMy0uNS43LS45IDEuMi0xLjJhMy4yIDMuMiAwIDAgMSAyLjItLjRsLjUuMVoiLz48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuOSIgc3Ryb2tlLXdpZHRoPSIxLjUiIGQ9Ik0xODcgNTRjLTkuNSAwLTguNCA1Ni4xLTE4LjIgNTYuMS04LjMgMC05LjctMjMuOC0xNi4xLTIzLjgtNi41IDAtOS4zIDMyLjctMTcuNiAzMi43LTYuOSAwLTEwLjctNi42LTE3LjEtNi42Ii8+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHBhdHRlcm5Db250ZW50VW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48dXNlIHhsaW5rOmhyZWY9IiNjIiB0cmFuc2Zvcm09Im1hdHJpeCguMDA1MDI1MTMgMCAwIC4wMDg5NzM0NCAwIC0uMikiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjE5NyIgaGVpZ2h0PSIxMDkiIHg9IjEuNSIgeT0iMjUuNSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMi4zIi8+PGZlQ29tcG9zaXRlIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzEwNzdfMjY4MTYiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzEwNzdfMjY4MTYiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48aW1hZ2UgeGxpbms6aHJlZj0iZGF0YTppbWFnZS9qcGVnO2Jhc2U2NCwvOWovNEFBUVNrWkpSZ0FCQVFBQUFRQUJBQUQvMndCREFBSUNBZ0lDQVFJQ0FnSURBZ0lEQXdZRUF3TURBd2NGQlFRR0NBY0pDQWdIQ0FnSkNnMExDUW9NQ2dnSUN3OExEQTBPRGc4T0NRc1FFUkFPRVEwT0RnNy8yd0JEQVFJREF3TURBd2NFQkFjT0NRZ0pEZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc3L3dnQVJDQUNmQU1jREFSRUFBaEVCQXhFQi84UUFIUUFBQWdNQUF3RUJBQUFBQUFBQUFBQUFCZ2NFQlFnQ0F3a0JBUC9FQUJzQkFBSURBUUVCQUFBQUFBQUFBQUFBQUFFQ0FBTUVCUVlILzlvQURBTUJBQUlRQXhBQUFBQk9vU1V3cEpaUmh1eWs4RTloMXlkYkQ1Sm15dHNYMWh4cVhQVXhBWUhRTEFMc2kxSG5iVGwrTTV6V1BMb2FZc3dkWFlCS3BkYWJFaUVaWndjNUw0dytJT21sNloyUUJLcm1iUGNWQ1ZDd2daZFBOV240ZEtXVVR6RXZHcjZ5UGk3UlR0NThvVHRoV09iV0Nualg4V1NSeE11NERpU1Vwb0FBSldCNjJiTGphRjFPUVhyME5VeUV6MjZCMDBLdXA4OEJ0Q1cwQ3d2MEl0dm5KRXRnN2RleWNZQkkweUl4N2FvY0ZnSmZDSzFIQWthMFEwZXZQcE5ZWnNjdVJkMnFFclRZZDMwcjVZbzQrSkZiVmtmY2lOanl0b0ZiRk9zUjREWXo2Q2QyMUpIdGNZWjI1TTRaYjl2Y0h1T3RvcEtIVTExTHVScXdoVHNtaFRVTzU3MkRaWEZSazlUWkVodEdmUnhiQ3RMVUxqVU56WDcxMVNMRTEwWUo5djQ3T0d2TW91WjBOTU9yYjYzSjlIZkNlekIrTjE4OGFjc05iRGl1MlVzSmxCTldicGdNS2IyUUhFcm8ycmJHODlxVWpBN3AzYU5IYU1xSjE4enpWOWw1VEVtRHBISFQ1Z0ZrME5OREg3bkd1S0wvQUZiK1YvU0wxanpNajJUUE5DMVZVZVNXS2dJbGFYSWJBZE5EQmpxUm16Q3NGUXpCOXg1T1UxM1hweStVL1c0b1Z6K2pic3VwUFYrUElMTStkK2IxMS9ScTJlcWFWOGQ2czU4NzZHcHpYV0xHZTlFL1BkMTNaMTB0bDg3aUNvZHJZektueUIxK1ozNU5QYjlNOEt4T1IzbUZ1NTNqbjBPVmMzVld0bExtNjNDNUFpOUdoRmM3c3RQVGlQOEFrYjJUODArank4N0FLeG42YTNXektLMm9mcmJPUHZ2RDhOK0czOGQ3SnorRzluaEw2ajh2T3VyenR2ZExPMmZGZXZKTy93QXJ5QjJjMEt5NnJmUm0xUjZEemRoRlYvTjZpWDVmWWNIVTVWOTVqMEdqL0ErMkMwQW14ZVZpRk5adnlGN2JLbnJjdWxNaTVkVEc1VzlPVXBNN21Ka2ZWdm02ejVQU2FFdnlkcTUrWitmMElxTTAraHo0MkhYdzBWMW1mUnMveDNxQURoOWpUTUMzWkdDd3FxejBVMjk3VDZyZ050UEY2NjR0ZnBhVUl5QTZHVFIvdS9IZ1dta2R3Ylp0aTVPMjhtdkpET1QxTEhQb01ldnkzenh1aWMrSjlYZHM3aXVyL1o1YzNObmFnOUNTenNsNkdFMlU1WkdmWk5CTzRsVzNucm5iZVhUcFZ6YWMxWmFLVklzOVdQdDlmNWV3U3hpVlhhODUvUW92TWRjS1ZodHBhUFhCVWpKZXNyWmxQU1FOR0JLRncxaVBoN1NTU2g3Y3pnQ3l1a3hpbDZRejBHMVpSbFZoUFloUllsaUJ2aTlzYVpGVVl1VVFyempCcHdBM3RicHJMQWFxM2VMcFJXTWVEcU11bDhZMEtXT0ZiSEZsL2NaK1VQdWtLNE9wSkMwcXhHSHAxcG1MY2lsT3VlYVdVMThHbG5saXRzMVZNaldPeG1wWlRZa3FtdGxiVzFvMHR5YjBScjdtVXRja1psRjRqa1NLRXRZTkpoUG83cG1LNlZ4ZklIMHNKbU1GeTNBYmhJd0NvZTBldHRVYXRwS1dHelVvNE5GVXI0UHF2YmFMMVZmS2xQd3Nvd0ZoQnJXakZOMXUvbnZtaVVndklJTmxhNlczWUJyTW8xcEFjeUI2c3BJVTFYYStub1Y0WUprSFZlNzBDU3JTQkh1RTI0MVM0SlZpbEZHelFsa1JLVlBsaG9DMldVVW5FWHRUcCtzNXFWa2NTbndkbDZSMktBMmdybEdDQWJRUWwyVnRLQnZSWFZGcUFjMlhCM3NqdmN1aUtZVXZRS0UvVllpSzdNM2laNlZ2ekJUQUZEU1dScGU0djhqQU5CUjZ0ZG8wa0hVdlR5dFhSbjBNYW1lVDJWMk8zTmZTVk1HSkZtalp3cXZWVUN0azBjYWNiczQrRGRrQWtCWTB0VEhTODA1RDV6VkVWa2pxWXhuL3hBQXNFQUFDQXdBQ0FnSUNBUU1EQlFBQUFBQURCQUVDQlFBR0VSTVNGUlFoSmlJa0pRY1dOaU1uTlVGQy85b0FDQUVCQUFFRkFvcndkeVU1RHhSOEcrS2VETlMvSThjaXZKcHp4K29yelZXMVpaS0lnVG9MR0hGM21qbWpNb25GK3hpRHdLV2hzYWlPQWdueXNSSE82ei8yN3lmTWRYMVBGajNpUHc3S3ByYTloZGZyYUtjK0hDRC9BS2ZYejR6d1p6MDROd25Cc2ZMbnk4eFh6YTJ6UWtZWDVEREhMSkJVdFRhYk5JT3FNbnVxbXVnSkQ5OWc4Y3JXWjV2b2owK3NqMVVrMER0UE5ORmp4bkpUZWQ4enEvcXJYa1U0ZnhYa1Y4eDhPVFhnNjhIOFlpYjI1Wm1veU9zaXBRYitpN1hQNm9yN0FyaFhYMyt4WHgreFc4UkdaYW41bHA4YzhhcjNPMEpXenVtSXRaeXVJdzhGdlJQSDlnc3V2ZldLYktCV29LVHo4WTN4ZHhTL05QMitxQnhNV0JiZ3dYNVdsT042QVFWcFhaZW92aUtnWjY3NXRnQ3NXbktObmlPekJDMnlyZEFoODR1ZC91SWpDeE5RRE9oZFBheTI5SEdIVFV5VDVtZ3ErVm45cC9VNTZRNWZ4S3FBSDd6Qkg4U2VXWXFNeGZkZUFVVis5ektxZjdyelBPU0N0LzhBVUlHZU9PNWZIaDQ5ZHNBTWg2ckhpS3k0dlhqOHc0ZXFoNTVsakVKeThBKzBLMnBSUzIxbGpKOXFjdHY4clpoeVA3R01oZXZJRlFFQ29DeVgwQnZxNlpabHhtS1ljN1haYm5Qb2h0YTBURTJ4Q2VxNmUyM21YVTNNOTdJMGJWTG1mWk0xVHV5djdRbHRlN1ZIcXZHTVZPZlZZaUtvUFJNSjR3T1YxRXcwTDJNL2xWdzdlbm8rZnhveURHSmJJV2ppK2FaSzVmMTB4aHdNSTluMkMzbjk4VkpTbkhRV1dieVNlbFoyZkJzdDRpVHNDL01VRm1LMHFaTkFaakw5Y0xacG9LWnluMXBzK0l2d2pQTk52eTAvSXIydE1mYi9BQVNwb2ZZYWRabW91dm1teStBa2JmR1M0REEybjMrRW9YMU9rOTJ0SHpwU2c1a3d4RENLYi9FZWdyNnl4NW1jellkRm5YMGxTZ1N2TnVTc0VsTVZwUS9haE5maTlrMXgwa2V0YngxM0FQWVJ6dE1GQzJmNGY2akZiVXMxcUhpdktkay9hN2x2YTQvV2MvcjYvd0Flc3VpdFhNSWtXd3Z4UDcxQmV0RzVyV1ptSXRWeUsvVitBeEtueGlQYjZqQzBZeTVDMis0d1hPMVZPekt2dEc3RnVNUVBqZlk4N1RCbmFXZW0rVXZ0ZS9JajRVdXlaN1NHNTZVRHRNdFg4WDVkQWwwOE1nbCtzTWFGQ3JOVTllajdMU01MRUFjK1h1QmI5MGZORkZQL0FHblBoY0RZcGJwVlJ4WWJuWDEyQ01kZi9KeUcrdVM4ZDNJYVRHZnFrTGxwMVZnbDArcTJnV2IxTVZuRjg4SzlYQnN5cms1NjJyYkVWazUzNHBJWFpDMTY3YzNrL3dDdjVmdi9BT3JsSm5vUnBPa3JlZllPdGZuY1NzQ1RLcVVrWHQ2VVdaaTRnT0l2WnJrL0xzMVhFbU9hRWRkTUNGdXZFR0VHTFRyb1VzSXFLbVg3d1Y2NXJVaW1KMktHSjB0WDhxT3ZQT2F1cmg2S2R4NmI0Z0ZiRXlCMUc0THBDajJGdjdYS1YrVUMvY281MXhEQWRpdW9Jc2k2V0Q1VzRmUDhiNWZwQkkwdDUxOUF0RTlyTTlUcGpIelFIdFZRaW9UMS9GeWJaQzF4aTZnMk8rZDEyTjR2dzQ1bzZtWmlQdXVNZFkvYzlURUlvMHlQbG9sU2lybVVMckx0NUgxTFNtUTlWVFF4TkN0UzZjb1ZweTFWcUpDMWEvWnNiSjY5alJzS1dCMkxIYU5GS0dOUllNS2R2elc0cTdwd0NwdndsWUt1dXZZdDBtQTNvUFlyQkd1eENuWGRLNUZ0bVNLU3VWbGVtYXZUTUxtOVdFRU5ldlV6cDMySzB6OUhVWjNYL3dEaWhnQWQ3RTNtSWZRRHVuUkJ4a2tQS1EwMW9MMHFOd2RJblJZc2Y3TUZEVTJCTi9qYXJEZzJHSk9JdEtrc0s5RDIrSUx4NHIvWERlbVprcXpFVjZXdmNsK3BvNXRLcXRNU05tR3I4WE5jUXNXdjhrZnQvRVBQanNCbVJaWFd5VFl2VWNxYUw5bUE2U25iNkhrdXVPUExUaVpiQnovbFhhVHJGNDdPbFZSSnJyb1Z1ZXBrRTBjYnBXajQ2OCt5VHRYSjY0bTVEbU91a1VRQUFTTE56OEZtanJpR3lWQlhuUGowWkt2NHV1NSsrbmVmSGFkM1gvTzdFbmZ3bUtmOG1vWCtRQ212MmkvbXpKTk1rRFQ5MzNnck1DQnJNVHJBWGNscXNoVzlQd0VMUklPbkNWaWVZMnhLWkdZL0tZaFpHUmdTUStGOHhhYTN5azRqS0tvNDBPNk5OMXUzOFBia24zZGVxWHZmNmQybXU2aVVJRWtteTZ1ZDFiWXZiNmxaSVFVcmZQOEFGUEhDbXlFdU05bnBGVTMyTDZmem1Ra21aUGQwdm1XU1R5R2JWdDd5VHo1V211TmUwTTNpc0F1WVhscGZNWjUrQ3NHOWRLOSt1RkU5bzZtZjF6Yzl2MFBxNVZMRnBQdk9LcDVCZVdOek5TcTUyNmZGZE4zVE5XOXZ6KzZUL2xVOCtuMGh6ZUd6TS84QVJVYStHZFZ2endkWXRjU25zNERNcnhaR291YUxVakRRaENtVzZwc3ZjRjB0VWR3cFlLZFBzckJwT3NhSkxvS2c0MTJ0VWZHdTFORTRiVFlaT3ZZZFNzVDVKblQ0NE9QbHM5MEovS0UvK0Y2TFpKM1N1dGZNVnZDbGIvMUNETmVCdmZ5bm03Ny9BQy9VeUNHRFA2dUN3bkRob1k1QzhzeU1kV05pb3FzZG5yOGlhbW14VS9zbWlxaU5rVEQvQUJOd0ZmbS8rNjhMUGtxWDZTUi84NTIrM3k3YUg5ZE9iQVQ3YVVpMkxlc2p0RTgvLzhRQVNoQUFBZ0VEQVFRRkJ3Z0hCZ1FIQUFBQUFRSURBQVFSRWhNaElqRUZGRUZSWVNNeVFsSnhnYUVRSkdLUnNjSFI4QlV6WTNLQ2tySUdjM1Npd3VFZ05GUFNRMVJrZzRTVDhmL2FBQWdCQVFBR1B3S3VCeXRBbFEvRUIzVnhBcFhDeXQ3Njd2OEFoYzJrWGtqNmFITGZWV2lSU3I5dXFsdTVXRnBGNkx5ZHZzSGJTTEJ0R2tHNUpadU9YK0JPU1VibnBTNkZsbmVkVGE1My9DdXI5QVdHSFA4QTR6RFhJYWJydDJWYlBIcTNrVUNJdHRKNjhueVhuN3kvYlhSdVAvS3gvd0JJcXhPTisxNSs2aDNkWC9HcnllMDZRZEJjWU1qUEdDZmNCeW9tZWE1a1k4MjJlUGxYOTlmdCtYaGtOY1NnK3pkWGI3NjVWZ1ZKSEhjOVZtYkdKYzQwRE84MURESHI2Um5YelpwNC9zWHQ5cHJyUFRWOW9sOVFIVktmKzJqYS93Qm5PampDRHpseGx6NzYyL1M5MldjODBVNVAxMW90SVZnN3lQT1B2cTlQN2Y4QUg1Tnd6VTFsMXRMZlV3eStOV01WYjJrSHpwNG8xUUhHYzRHT1EvR3JUckVMUVFiVGdCM2RuZFEvdzM0MTB4R3NDU3gyc3FqSHBZSXBCQmN4cDZ5bUhhSDZ2bFFhUzNGbmQ0ZktQbDRSNzYwYjVaanlqUVpOSDlKM0FnVGQ4MmlPcVErMDlsR0RvUzBYbzYzTzR5bnpqN1RXMjZTbmE4bTlYMGEyZHZFc1VmY2d4VnBhOVdFOFVxYWp2d1J2clVkd3ErbDFZVXo3alFBNW44bWpzNTBndGZSYkdjKzZwNzBYVXR4ZEJnQXo4aG53cTB6TERITVlFTGhmT0owK0crclZJbGZna3lTNjQ3RFgvd0FYOGE2VkZ4UHM1VWxHMk0vcEh3eDUxRlZta2R2MmFCUHRyZ25SdmZpc3FtdjJHa2tGNGx5ekxsaHBLNlBDdEVvNGgyNXpuNU53elhMVFRIVmtMejhLSW1sMkE3STAvV04rRmFiU0FkRldqYzNmejIrK20ybWJtWlczdkorN25sVHZubk8xY01oSHlkRlN6SjVicklqMWpucDdxMDNrTFN3OTFYelRSRDlIOVlZYWNkblpVcTJ5dXNRdEpkT3NIMWFpanRiYkNoQU5Xajd6Z1ZMYjN2U1VOcm5lTmNoZjRDaDFpM2JwS3dRakppSlRjUEVmZlVYVmVqUllJcjhYQ2RSM0h0N2FBemo1citOVHozOTkxaE9hSUV4cDk1SXpXYlltMlhPNHJ6L3lnVnM0dDcvUzNBZDVQaFUzemphTHREczI4M2RYQkl4SGp2clMwRWIvQU1HS2FTUmRHbGM0VTBKWGtlRDZNeTZUVzdXdzhGcjlJSHBDSVcwazVjbzdGTjNkWFNOMjNsTUhFYkh4N1I4bHhLZVcwLzBWQ0dHQ1NUV1NjRHhvOGV2OTJ1amhqUmk1QlR0MUh1L0pyNXpkUjJ5L3RKZ3Z3RytwRU90dFVoT1lqZ21uRUVRaCtaUzZ2S2FpZmJTQ2VlUmxWUU1aMExSRU51djJrMTgwNk9say93RGEvSE5Xelg5cTl2RnI4bnFid3JHZFB6VWJ6MmM2bTZ3MHZTT1R3WWpNYWdlSk9NMXBROUgyUTlna2I3NmVDeXVJNTg1YTVrWHR4NlBzSHhxUzQyc2VtUGNSdnBKV2RkRDh0TFVoamlYcTR6dHA1V3hvOWc3YUVWditvVitIZGxwRDRVTHlBc1kzODhKaktOMmdzZVFyMFg5aGtrcVJqSnNSai9xdEZuK2NGRDdEVXR5cmVTTW1Oa1V3djFkbnUzVXR3QWl2a0JvL1NGU0czUXlCM2JRTTQ5Q2t0UkNZM1hDZ1JycTdPMnVNelRuMXBIRVNqNjhtc1d5eHMzL3ByY3pOL005ZEhSemljR2VmUXUzbjNIM0x5cUtHRzBXV2FRblIxYVBSdUhiazBXMnNkdkxubk55cVZwTHpyV3VGa0d4dHp3NXJNbG8wN0Qwcm01VWZBWnJGdEJiUkQ5bkFYK0p4V21PZGdQQmxUK2tHb3pLcDBnN21Zc2M3dnBVNmpjM1ZCWGwrbElRZjV6OTlmODY4L3dEbHFWNUxhMmlWb2lOY0NhYy9VYXZmR1g4S2dUUnRKSTg3dXlqQzBuQ0JtVEhZS2N5ZVRZS05zVjlBSGxHdmpVbHRjb3ZWaU1TcDZNUGNQRnFNVXI1OVV2SmdON0ZXcHNTYkptR0JpV1dQUDgyVlBzTlIyNjdoRU96ZHhIN1BzTkFvK2hUdXlPeXJTUGE3WGFGanJQRm5kVEpQY1BrZWNobDBBZTRWQ2tOdkUyMWliUmdibTNqbWFkSll4QTQ3VkpRL0NoQ2l6M0VxTnJoRE5xYVAzbmxTQTIyaldNcnRKUzI3M1ZIRkJjRzJmVG5XQm1uYTd1TGwwRURPcnljaVJXQSsxUGhsdnNGY0hSVWoreTJKKzJ2SmRGenhqeENwU0c5VFo3K0ViWFZVcWc0UFYxSHdyTTErMlBCZjk2dkxhWnBab29NQUhYcHp1OEtkb2swQjEwa0Z0VlhNS3V2VWtielZYZVdwMlpzWUZNNzcwMmpPMzdzZis5SzNPUkYybnRsazVmVlNSUmNUQjluRG4xdlRrTlFveDF1b3dIZm5SeTJGN2EyeWNjY2gvSTlocjcvejd2alZxa1UyaFV6amgzNytkWEFFOHN0eEpINmNlUHZxd1ZvOUlqajRTVDUyVG43cXZMMStjTHFGRlhyYllReVMyeUdJc28zNDUxWW9JNDVCTkNBZG9tY2NSTk0zL1RpMTd2Ymo3NjZNL3VEOTlTeHFRRjZ1V3dCeU5kR2xwblBrSkM1MWMrZEtrazVqZ0I0K1BoeFVJZ2tXWWliemxiczdCVTV6eWhYN0tjYW5iRFlIWWF1cCtzSmJ0SytlT1REVUJiNzVKSkFnMUhIT294bERLenN6OS9QL0FHcWM1OUNwT0pGSnRuRzl1OTZKTXNlT3N4bnp1d0xVRGJaSkdFVDdsN3kyODBDUm5IS3NFWkZTNnR3QXpUY1IzWjdQYlFDOHRacFc1OW04NHBITGk1MndNZ0d2R3o4UGpXMjZQdkJhbHBBREVPTUh4TlJZbGkydzN4dEc0MDEwV2x6ZGRZRXphNE1OblA1eFc4U3ZtTUJSRU9aem5mNFZZV2xwdFROc09SVDIxZENXNVdMRnRqZjM3czFJNlNhbzJQQ1FkMVlWMUUyYzZzOGRRaG1lZHpLT2N0WGNpd3VtSXUyYlBJVm8wYThMbmhGRFYyZU5GSjd5U1FaeXVOMksxbEJyV1VvekhPZThVOGZlTVVZVzVGcElUL0h2RmF3UEtOR2pnZlRqNWo2cVdWZDZSc1hHTzJKL3dOSThNbkNkK1IyMFJuRkMzenFZK2Q3UDk2M244L25OUmtuQkp6UTYxR3hoN294cU5kZWNIcXFqaVo0d3A5Z0FweEoxbTJpazdZSDMrM2ZSMFhOK3lBODNtNS81YWdFYjNRbHRvOHdNWmM0MGpjT1hLb3YwbTd3eHVQUW1LZC9oVFIyeUV5dGp5b2s0OTNaa0xYbDAwN3NGWkxqR2QvUHphd0p1SDFSZjdoL2xySVo4K0YrUCsya3ZPakJLZGxJTnF6VGh3QjdoVXFONlFwMXQ0WGlrQzd5a3hVNG9sWXdGOVV0VFJQR3diM1ViTlZjSkx1NTVIZlcrdHNOeXNNTWU3dU5GajVKdGVUK3lrL0Exd2d4bFRuQTV3bjcxTlJSUWNFcmNiNGoxS1BEd3JHc0QrN2dQMzBzdzNuazJOKy83elFVRG4zZm4zZlhVQTJNRTVVYnkwV3I0MWJLR2oweERpR1BPMzFiSjFaTHBOcitwZHlpbmQzamZSZGY3UDlIMjBtTU5NazdGc2Urdm0zOW1yVzJrMDQ2NGszbSs0aXA3ZzJpU2pRb2djU2FOR1BEdHp2RmJHNzZOV2ZrY2JUR25mdnhVUzJsclBZdUpRVzhvdU5QOE5YQ1N4QXNkUlNWVGsrRlhLM0VDbnBFUnBzT0RjVzlMTmRGTmNCTGVhUzVtVzh3U29WVkdWeDhLdUxXMDZRdGJTVmYxcVR5NkE5S1lyL281M0hkZkNtbUhVcEhJeHczYWZqVHhkVWJVdmJvM2ZYVWppT1dMUG9xbTRVcExTWkhJN0xGQkxsREtxN2dlVmJOZ04vWWFNaWI0OFl5M0xIcXQrTkdhVlNJb1JuRGN4NEE5b3A1WmVGMlBwUnNwK3NWdTQvNFdmK3JkUlU4U3R1Ty9QNTlncnJFdzMram50cUpOWlJHSjFZN2F0Sk1EV0pXYnpjOTlXR2lOTGlaaVdFVXd3aDNiNmhzOENYeURTT29iQUlDNTdLZ2c2SXpxV0VtY2hpVno0WnFPR1ozMXlybEY4QjIxc09ZRy9ESG5VVVV0Z3lBdUR0VzFjVzhERlhkb3lSeHVDVkR0ekh4cUtLS1ZaRHJVc3krZHU4YzEwZXNUMnNrdHEwemFKRGpWckdQaFU1NmJ0SWJnTXVFNFJKMjFxaDZNNFI2bHUyUGhTRVdtTFl3TnIxeHVBRzdLdEZFV0RzU1pDd3h2TGJ2aFY1Y2lYWTNxeDZ3TmoyZXcxcTZUNlNoNitWVmt3dU1kKzZycVdTUVRTZ3JvWURHTTVyWHdrcmphTmpWMkxWd3dJT0F1RlBLZ3VzV2l1MmNGT0UxODFqTW83NFMyUGhTYmUzZUpTd0dxVUhIeHFPN2tZM0Z6MWhVK2lCak5KQ1kyZGZWak9EUlpZeEFjYmpKT0tqdDViNjJqaFRzRW1TYWovUjA3ZVFYVEd5QWxtenpPNFYxcTYycGtXRTVMcVZKcGpETHJTUUU2ZlU4S1E4azJ1UGFLbm5kTXh4djU0Tyt1cjZ0WDZ0Z0FkdzdmcnJwYTFjQWg3Z01nTy9zMys2b0hTTlVZdGh0SzR6UmQ3ZENXSDVOQlBLSUR5MHpNS20yZHhkbUlmcXlrd1B3clVuU1UyZzcxQkhGanhGY01xM1ErTlhRVkdsMXhLRjBLeDVIeEFyUWVpNUI0SEgzMWN3TEYxYU9WdzJwM0J4ajJWY1JUOUpqajg1a2c4M2wzbndwNDdqcGU0azE0OHpRT1ZRd3cydDdjeHh1ZEphVWZIZFFqc0xVV1Nhc2NScTFGMUpHWURMeUhPb2Y4WVA2YUZ2UHFNV0NUcGJTZHdxOE1OdGlaUWNPV0xGYXRad2lJdU43bFBFaW4wT1J2OUUwc1dsaExzOE1EenhpZ3FnTHdubFcxYVJ0UW1DcXVkMkt1dG5laUpUSWN4dU4xV01rajI3Z25BYUhtY2V0VjNJVWs0MklKMDhKMzFDVllIUzIvRmFSdTBqNjZZUjhJUE9qbG1WZkNsOHFXeU56RVZwMUJ5S3VOaFptR0poaUxFckVyNG5OU1J5blhlTExIeFk0anhObjdxdlVaVHROUTA5L2JSZEh0WkVjaDNqeDVNY3QzYjNWSWtKdHhHRHVhQzFSUjlsR1hiczQxNDgzblE4b3dLT3hUZDMxYlNiODdUdDlsVy84QWkvOEFUV1Y5UnZzcVdlY0JuOVgxaWVTMUNxTHhPQmhWOFhhb0RPdTFFZWM1cThsUTdGdFB3MDBHSUM3anVGUXZubmNnWTk5ZGJYelpKaW9IZlZ1RzVnay9BMHZaL3dEdGRFUHV6TVh6OVFvU3FXeTZodHpZeFhCTTQvZlVNS09xenRMZ2R2Q1VQd3J5bGhjdytNVWdjVmhiM3E1N3BvU0ttSFdKVTRjZVlOMWJNU3l2NG10YWFnYytrYTJNV2NlcWdxSkxpSmRRSEZxNTFxaTJiUlp6alZ4Q2crVlNNOG1ac0NyWUFIWjVZaHM1QjUxWStOdzMyVjM4Sm8yOFp6Yld5Tmo2VGRwcTBHY1lRZjZxWmdlK3B0WEp1ZERSNXV6TlcwbWs0RjRCcTk5SkFGM1JNK0Q3VFNQSnZYUytDUDNUUWFXTFk1NXZJUXRXa2NhTkkxdHEwYklIQnozazBVdVlqQzZZVTc4MWhnRFJnUG1zTXJXNnQ5U2VSMndidmZGRjNsU0lmelVGbXVtZGU0WUZlUVovL3NyaWoxZTFqVzZDajBYZE1MTjFrT2tTZWpRVzJsZDlIYXlZQjNWMFg0eXQ5bFBzMjBuU1JVczluTU1hTUdPUTd4U0FXK0lzWURBN3R5MXRHWVkxWXdLWjB0NUdVOGpwb3pTUWkzaDArZEljVUltNlNFN0NmYTRoVFVkVlpnc1NUNjl5MlBoWGxyNFEvUnQxeDhhTFNGWGYxcFcxR3NXMEpZZWljWUZUUEtOTzFQSVZ1YmZXczdtWGtjVjYzdXJsOEtPR3JtYUdUV0NkMWFzMXpvbVlJWDdHN2ErYlRKREt1V2lWNVBQcXhSMk1pd3UydlFvT0R1M1pGZk1MS1JWSjlJY1I4S0JGdTBKSjdlVkE5SWRLd3dmUlRpYXRTV00vU0wrdE1kQzFpQkxibzVQMk1lVytzMXF1NTVMcHYycjBRSFJmQktPd2ovaWM0cVZXdXpFaW9XNEJXL2Z2N2Q5ZEN4N2dGcyt6MjFIZGVrWHAxOGFjZUhmUTNuNVBINWM0clNzaWcrMnRLczBySHNRWm9OMVJvVVBiTTJta2ErdjRJblVlaHhOV21LM212VDlJNEZZdDdhQzBYd0dUUjFYQk5FOENlTllXVGFudVNpSVVFUTcyTk1zdDA4bmdOd3F4Y3FPS1k2L0VZcjMxY245a2Z1cFIzeUNyQWVyYWorbzFaZUxtcG9MZE5vK3J1cG9wY0gzWXBSOG1hR2hNMTgyV09OZlcxQ3RmVEhUam92YWtLazE1R3dtNlRrOWE0a3dLMDJkcmE5SHAreWozMTg0dXBaUERWZ1Z3SlhFU1BZS0loVm5Qalc1aEV0RjVaV2xiUGFhMDlYR29qZWF1cmR6cXh2VTk0cTRLakFXUEordXJFZlNZL0Q1TG8vUisrb1A3MGZiVUgrSFg3VFhSb0E5WTFOY0w2VG1ra2NZRERoclFlZnlmLzhRQUpoQUJBQUlDQVFRQ0FnTUJBUUFBQUFBQUFRQVJJVEZCVVdGeGtZR2hzY0hSNGZBUThmL2FBQWdCQVFBQlB5RnlmVmt6S1Y1SFUxQ01uNHNqL3Bjb3E1OW9DWWJuS0VNOHNaZXN6VS80VVIwTFl3V2RWbkdQMmw0aUs0NFA4QnZjYit5Q1hMTVk4OG5TSEljbVMxeFV3WmZjK29KWDBUZ1ArSjZzQ2FzOXVKbFRTemE3d3E4N1FSNGd6eXJHZWZkUXI4Zjh6c3VGT0M0bFhFb1NyMGNrb0huWmRzZWhsZUMvVU5RdVo2U0JpQTJmWGU1YzFYTmM5VFA4Y1J1OXpWN2JvT3hNQjFBOCs4WloxZCsyTlB6S3RCd1AyN01EWFlKRHJkeDVzcHVqVWRGbjZ1czZ4N1NYdDhhanJhT09xWnlMYVpXL0d2eEtoL3VZTnFCNVdPRjVLK1ljbU9mNkpmTTdQL0ZaOEk3UXJXWlEwd3BETkpoSUw3SFljN29LYnpFZjhTNG0yb1ZMcmcyamxTdHE4cm1VNzlseVBiYkQ1TnFxbWFDYnpReHhNZzFscThRREJiSWNOL3pHcXJ1ZkFGcWNuZW1WSGJsNWlpUmQ1MTFPSS9RMWdXenVRR216SmQ2aHo5UnFodlQzRmVTOVlLWFRpNnhJZnhpdjJaNlJtejdpYTBXS244UUZDTXZib05teDRLUCtKWFhyeEptanZCQ0RZamVIV1kzRnllbGZGL2NlY3RXRDE2dnFZT0ozcnRRTFJLM1B4UFMzZUpsVlBjeEErcVhndnRiUzBZR3JpNzU2azV5dmNDalN2VW9ocnJBYWIzcUo1TFEzUi9rR1dCUUhsRGhNRDB4dURDNThXcTlkRXp0ZHRjOHprOGlndURMZFdhand2blVzUkxGM1VlYUgyd2FyWmRLSnNjRGxqQU83TStQdUhoa1lmdU50djVQcUlzdU8zZEYwWHpLMU4wWDhzMCs2S3pKV2wyVjBydzVxWkJvaUNHMzBiamhOU0t6MDU5dEoyWWxkZFVRdmdkTm52WDNEQjFaNGVCUlVQVzBkZmF2NVNsODQ4emhYQk5JRWJNbTFVd1JFaGlQaS9jVzVya1BvRCtZVS9RM0U5d2I2QlVSdHZlTG85RWRWU3Erd2hUSXhPV3d0WmM0ankyOWU0dUhybHhyZkttUDVtZWtjVm50Rm54N2drcU9aZU8wUFk2UkxpaTI5SncyaFloc1A1ZEVVNmRmMHhFVk1XdjdqN0tKd2hqUGpQcmo1bUppQ1p1dDVua29wS214U1huQjAzTjZhZEZ3UHVZT3FCWUZMUmZUaUNGYjM1QlA2RXlmR3ErdVJLZ0prMUswbGo0YmpRSUQwUmU1cTRVV2xRTDQzYzNLUjBkR0tLcjVoWE9JQXZtWDFLSEYxNzZjdmVpbDM4TSs1ZHJ0UWtybGZvbVRkQzNWTk02L2x0Z0h6UzZXUDFBc014Mjk3VDZpdUc2UHVDdWdaSFpiejRxSTNFOUw0bzdyZ21GNTVRZmM3bVkyRktIOHg1bmQ2ZkQxNDhVK0QxM3RBUm9sZGdFcGtjY3E0N0VYK1N3amx5RDdPbEpHTmRGRzlTODdpUXVlZ0hlbVB0Q1VIelppYkd1ZkJGcFNZSWNtc2IxVm83U2hacE5xckozM3hMc253dG5PSmg5WlR3aGQ0MzN4Qk8wQXZxaGJ1bHNkWmJyazM4VGN5M2owM3ppV0pncjB1TFdLS1QrU0w2V0ZZa09VUTU5NHFoZDh5dE5uN2o1ZW5hSk9YV0Q3elVHQ2NTMXJIblZmd2xkQzZaanYxQ2FJWFdveTN5NWw5eEcycG1aWXd1bHpsNk9qQmdOdjIxbjdjV0pscElCaGU2cXNST1NNOFN1YmRJdzNzRm9xUTRJUTF6NTNmYVpEY0lCYWgzemNEUng3VFlMMHgvVzRWcXppK0l0bGF3K2Y3d3pLcjRQOEFER2JUaCs0MHV1aFc0eUJ4eWdBWlNMeHZiR253YStaWkZOMDNpMERHSzZrNzZqS3RmYkZWcUVzQ2JGME0wenJUREhlWnVPTjVhaVRwRmQyUDFMMEc2M1l3OXgxVm1TMS9va0IwbGZaR0Y0Mk1lS3JnMXFteVY4enZSL1g2aVRWSVorWVlDWU5CT1RxeThMbGlvS0w1d3Jyd3dLbHloN3lHQURpZnhPT0E2K0lTQWcycXZLY2N1WVZZMTdtUmc3QithbFo3WGU4TU1Sbm8xdVIvWk1nNU9kSFdMZ2tBL2Fqd3NiWTY5WXl5bC9nM0JDekx5cjhFZEx4VlNIeUJvNXpXb0tybGkwdkpkMlpaVElwNEhDUEFRL1Bjci9uRk9wTUw2THVqVUtGdHdNMUlLMUpLczRsMFpoNjZ2NVdDVmx1Y0w5UDIrc29pUW92KzZ4WEpDcFc3MHBqdkVnOXhMZE1hRUd1dEdrVTBpM1ZuR3VtbStJcDBIY0xKVFc0MVIrRWI0S0xFd3NpWThWMDBjYWw3eDZTS3llVzNsdmdtNzhVRkI2RXV1K1d2M0plWDBqUVZHNkV6RHlKVE0vb1RoOFJ4aDBsZmNMZzNkby9NclJxV20xL2JpVmwzUGlaR0N1cGpmeHY1aWxTd284UFdkT09HYnR1NklzdVN0YngyN2JpN3VTRjk0a3VobHZkbmczOEY2STl2ZEdsYXgyNGZLWklVM0szWThKaWNwb0d6KzVXQktxak5EalZzOFJKUENTQnROOTh4S0pRTEN6bnJOZk1HbXZydHF1SEFaT3NJS3l0UzZYc2NLbU14RjVXczdXOXkvZ2dGbXBod2M1YlEyWHpkeHNyVnkweGF3bWszVjRYbmNzRFF3VGNkOHpRVTNmOEFaS0tmNXpIeUlwWEtVM0xxeTFWWkZ6bWJaNmpOcDc2aUF6NG8yRDl4Q2tESTB6T3lZSzErQjA0VEZjSnZPT1NhakR5d3N0MjB0UUNRVTUvaElXckQrOE1RTlpZc0QyLzNFSERPT0FRY1FBa09vY3pwM0hpUlpjbFMrS0d4aTM4RjVkQWVxQXRGeG1TNlcxWnVFWmtIK1MzbkVlamhMT21xTXZ4RDFCOUF6T0N0dldjaVRoRnZDbmlIVDFTMkFsSVZqZWZCQ213bVRiOTRlanhCT2NVcTVMZnhpWEZ2TWVBU01OeFV4d1U3Z1dEMHdsSExkVUowMWRrY052SXpwWStZVzdWMmxIeExERHRNR1FycmlVSmVLaUU3aDFmdVhKR0ZzVzVnWDNKeW1NN0RNek0rYldueVNaNkR0aFdqYjlUeXNuamNWSDRzcEJnSFZ4ZTVJdnBaVWVVbzBkeS96SG1HOURmQVRIRXdwbEk2c0ZOSVNzWktOWEY5WXgyeTA5SDNNQ2RGRlBCMmhDbkswelUvUmNIVjlXV3I3Q2o1bG1QNENIeENwUXZQWG81bGNHS1dUNmJyM0VHcFZSZXlqak1MWWFzWXBmQlRLQjJ4ajBVamRpaE0ydExGY2JOaUZQaTB2ajNHTTE0Uzh5dm5KYlZUL1gzbU1EbGV1eXh1V1dZcnNBTE8wR0JMYjdvN0dLZzJsYThqSXpOem42WnRKSUw1Qk1rdmtZQ1FjNVlmYVl4bHltV2JlSmVPKzA3OFZDenQvQk9nOWQyWUI3ejRoeDdsZEp4Rjg5L3Faem04NTJ3UVNibkNnWVpxQnVWU2hUMHpONnJtNnAvaUNiMkJ6S3pjYzdoNGxNMlpBeEsyUGNKZjg3dERJbk1mZFpyYXIrN1g0VFBhTzdSS3BMYjdaZFFoVnZPKzh5QzVNdjhBT2tRNllEbDFxNnJiS3p1U1hUemhMQ3g1NlJQNW1YN3ZxRGVyRXh6RG9pVmZ0djVQbUVFU3dZNEJETUJ3THlEbjRoRDhEZG1BcjVsZmM2Wk5lUEtwM1lCVzJicUlucWhTeDIyVlhkNVFibEt4eWhzOXlzQm0xa0xyY3pablJkOVExUmJFc3VxRHYwVTVoZTNxQmd2SUkzOGtzaWpiZXEraVplZXpRajhNRWIzeS9xZUdmQ3oxbGNNNG9QNVkrM0xhVTdrUU1CT1pTeHlvMWVOWjhFNGJ4dGFmaDBRWDZGNStVSE9HcU5RUDlSZ2d2Y0Z5cEdlOEJkWUhuY2ZOeXgxSnd3ZWdIbTZnemtySmZnZjRndTh5SmE4STQ0bFdXWGlLS2pkSzlaU1FkajEyZzFwTWFocnJLZklIcDcrSm0xZXNyOVRwcHN4ZXd3N3BGK3FtdFB2UDNGQ1krV2RNVlZ1VlJZOXVDVUdLdXFlTVFkMnpEM3o1ZWFkMTNsNi93b3E5ZmptTllDQmRSRGN2NWdKcytaeS95Vk1UZ3FZeUFPR0s2bkdNVjh6cy9oVDVwRExDNUMvZk1hMjViTDdsdk5HMjNYVHJCcjgwMUc5Q21FeEtuSThqcUVWVU9wR1ZJWWNsZk16WkZpYjh3UXpMbURzcFdaWkZaZ0hQUVB5Wmx1bE5vWjRhZUw3OUl5SmtXSjFDOFJvMnZWMGVqb2ZFM1JzWFNPOXl1REc1Z3NYL0FOM01NYXBnR2Q1aEFwNmpndnBabVRJY1VucVBDQ2xGMVdQdUpCa3BuTjl3UVlqQUZHWXJ2TmFrN1N2cXgxUnhtTTRxZEdXWkdWK1ppR1l3aDFFaFpGakZGZlJpc1ZjbHMvcUdmN1ZKdUVoVVZqVjUxT3VnMjcvRUVab2dPUDBBcUM1emF0eTdkNVNMZDVpZlVyeVJiSHNPejJ6UXFNeTZ6SHhmKzRJemcvWi94cUVENDVueEdHanNXcDRBNVlnbml4Y1RvQkE4WlIzOEZ3MUV6cTBUNS9ZcS9jLzlQMGZNVVozejQvcUN1bFl2dEw1OG5lZ21GaDNxTkt5QnVyUmhIYjFtTnNOWWdPS1lSS2pOdnd3bTNEMUprdVkzRzdGZ2lneFdMM0lzc2N6dmZYMG1UZFp2SHdpKzZGOVN6NW5iK0lVYlhFem1NcTZkSWxzLy84UUFKUkFCQVFBQ0FRUURBUUVCQUFNQUFBQUFBUkVBSVRGQlVXRnhnWkdoc2RIQjRmRHgvOW9BQ0FFQkFBRS9FQytFN1V4VytBZVBzNHh1bjlGUUUwNjc3WVBPNS84QWkveklqc2dCOUhOYmE4TXBpR0lYQVZKbEJUQUVwZEdIa3FQdVZ3eGQyeEM4bzd5MWgwQ2Q2UHhoUE9DdHJSOGVZVW51dm5sd3JGLzlmOFRHMW1hWjllUStBNGI1VGIxUmRENFVtSGppMlkvamdZUE1BSUhBTTBIWC9aanVPRHdhODllRkN1M00xaWtWR05RMk14VGozeWJCaUFYT2VNU2ZValk3UW93Z3crRUQxaVlDZjVjTnlIOHlqbjd6MnFQK1RrbzUzbGZXODBnOXAvcGwyQkhhT0JSazkwREg1TlNDMko0Sm9kUU1EMmZwQjFIRmR0R0tsMW8wOWY1ak5qcnYrWS84Wnpld3JlSG8rRENFaGJ5UFovcW9lTUc0UHRPbWN3aGtXd1hZanoyd1c0MnNxdWlLN0dKV1NhTndsWVYybUtTS0F0ajBBR3VxbnprQmdNUHJGYS9sTFFHdTlDeUhFblhZSkJBU2xpdGpDVXdUTytHdE42ZGFpOURCRWRBbnB5clpoS01Hc21zcW91R0p6c1YrRE5hdktqOUhEeXpLOUVGU1VkbTFNTTBSQ0NQZmJQZ3hqWTdtdnovaHdwbGdaOHM1ZkxodUZhNytlMnVEa3h4MnI4QktyaTE3eUF5Q3J4WXdPUSs5dmo2SWUweEVhaW9RNnNUc3NLMllTejk2QzZrRDJ4S3RYbGJqYVd1TUhEd2d5aXI3b01LdTJTTytuQ1RyTWR0MFVDSzRVQU1neWFva2ZYQTZ0OUZmVmc3VWNDcjhzWlBTakhhY3p0eGdpTllBVG90T3VvMk9JNkk1eHdCeXQvV2NjM3J6K3NJN0NtalpkT0pNWVhzYVVlQ2ZoYnlBdHZua1huOGd4T0oyK0duT0JzTXEzbXE5bWI3NXQranJFZ25PTmovTUNJbGx0SXFTN05Za0JvR2FORWllcjhjY2FDcFdoczl5REhaNEZvV2pMdFY5RmNBTXFKZ2xxSGZRNDJ3ZTQyZlpzSVpDb0RJcUlFVkp1WmgrTHBPT0EwQWRSYzB3QkwxS2k0M3N5bitGRmliVmRNRENERkVxazBLV2J5bGtyWmg5NGNPL2dLb0lNN0VJN0IzMEtJOEpsMUU5RmZnT0EzN2JmMU13OHJ5VzBnNVFhTVFuUGVrOVZnMm5uaVg4eG05RkJiRVFxRFhEQkRha3FqWnNHamxHOHVHa2ZzUCtzeEtNUGV6RS9NcEVoVWdmTHJKTmZsb3ZiaHdwYmZOU1JzRjNZZXlZVkRhL0habDZ2RHdBcndDQkVXbHEwSE5mOUdFTkJDSWVYWFhFR1lCQkUxTDQ4dFlycDFIZVlXZm5IeU5OQlEvOWVwa3VQZVAxRk5mYkJ5b2hwS05rckRuUmNDNzdVQjJPWGFBQjB3ZlJUUHk0Tytia25laGpXK09LbnQ2Z3Q5Sm1WUjZQSEtSakR0d05nNnBqc080S0Vpa3RRRURLUGFuS0tBODlsd2JFNUJDS3hRTHdZeXNMc0V2bC9wd0hVSmg5ZEJWZHdQWmtnSW1ocW9rVTZxa1NEbHNEc01BaWxBN1RneGk3REFOc3ZibnZwa2V0TlRLWUJ0TTZPdVVhamlxSzBFWFVLY09sQUpReW13ZEhJWnpDMTgxUnhIblk0d21ETUFzODJ3WnZpT0c2K1ZNNVZ3dk1PcmtjTjJCSFlCMzZqR1ljSTdQQlR4dGpvMzhqUGtmM2hPREFPZW4yQzhOZkdUSnV5dkJ4Z21RWUhna1c5TVNqUXFGZDk0ZFVnSUJON1Z3MU1NTk1oVEVWNFpjM0Y5OUZpTXVtVmtESFBZemdQZFhvbWNLTWdXR1Uvd0FyRExONENIVk9KYVM3U0ZnSWhSUmpzSXZ5d2RVSWp1a0MrNEY2T0YyREljNEExcEJ3TG81TytZS0F3Vk9MUXE2TXhSK2VBYVRJS2R5SGJHOW0waWNJSkVUbnZsS2lFSWhVcDZMdkpUY2tTN0xBWDBZdXRYVUkxSW14S1BKbUs1SVpVT2xRMEc4RWFQdW9Hc0UyaERaZ2tHRUY1QlpWMFllUW40OWFCOFl0SHJYL0FOMXcrc1BZa3QvMFg5d2NjQWFsenF1bzR5MURMM1NBL2JqWlhWLzJhUHpKOFN3Um5zTllZeWpBSzV3VWIwWSszQVBFTnI3QUYrT0dNUFFnT0p0VVI0VitKUzVTT25kVVh1clF5S3dkVENGMWpzeGVBVFQxNUJTdFpnVFpxdG55T25GVXNEZTc3MUh3NWhodkluQ2xSODMyWEFMTFVHd1ZtenFEWmdDM09TaWxJYUFUS0lXR3VUaHE4bTVLL1RuaEJYa3pXQVpRVFZqbmloQkZNR3R0NlRRd0pyYmtxbG5qZVlPdGNNZVVWQXU0R0FPTEc1YUpEa21acGFtd3J0ZHVsRnprTGZaSW9nZXpOYk9nNHhHYkVXR3VOS3RpTkVkTXQrMy9BQTNYTGhMYUsramNEYlFkc1VsdDRwdlFDczZPT3ZJRzFRN2JKcG9ITmxicGlYVGpvSUpOSEkrTks0QnE0SHJvUHU3QjJ4bWNxS1loNENMS29URDFkOEV3U0FvNUZEcThwSUhzTVJmQUNjZjhaK3VHVkdJajB6OHh0VHVpSU42ajV5RWZneTdMRFFEbTJKNnFmQ29Wd0JSY2NXR29hTnJ0Tk54Unh5dnUxRlUySUs2WXRlS05oTVU1NXVvdzM0Z1BMQWxUZUh6Rkx1SmRjaVVPVTlWUXZaRTZNamxoUlN5amFMWjhQR1h1UXFRZEZYVndnODVkWFJ4WU5jVERhQUxkRUJob0wrNGR6TElJN21BSm9qbE5EdCs0S0NFZFdWTmFBYmhUbzJIY1ROcGx4NExYMUl5bEIxRGZiaU1TMUxwVkZlN2RUQjBxbFJpN3dvYzNsZy82MGNBWStBRjFybEk3SkZ4VlU5Vitzbk1zcUtwL3F5UkY4WjhJanpUbTN2a2FMYk1FZmtoa2hzMUozczZkQXdDY0o2bDNOWTV6VHRKdEdrTkphM05zV0V4b3QrbFpsekpOZVE3WTBLakdKRlVzRTVIZWhBd01hQUlaZjQ1ME1XL2Y4K0VYRlo4djVvYXJWRmUyQUZtSzFDTW5ZNUhzdEhSUXk4VG9TYnkyVitNQm5NZ0I3OVV6aHBUbWhsdXh2TEJIQ2ZMRkZrd3pmL2NQNFdGMTRJd29vN1BYamVGekNIL296SkRpNFN3Y2FsNGl3OGVFVUgrOGM5N2pKNkVLQUlJSkRUdVhEWU9RMnJvK2dwMXVGMjZhNHFnbG5rQkpqdjY3Vzg3SWxpbGx4WFYvbXlSQkpJUEl4MnhZdThVS2RLMXhoaDBITlNhdTZiaTh5NHF5QkJRVFNYblF0bFo3S1ZNcUZ1R0lmVUlDclhoYlpvTU1TMmlvandnSTJqTGlza0ZXbnRRZVBJc3lMUnlMSGFLYWpWR0pTZkhQY0thUFVUSjRvSEtUMFk2TWNDeDV3YlY4VGtIemtrQnU4bmYxZTRPRmFxN2Q0b2EwSnVZSjNCRGo0bG1IRUdPUk9SSEE0SmxQYlhsUHQ2c2ZhTzhlKytQMEZ6WDVaWmRMMEJBY0lLYlJGOG9QYm01TnVmaEdXblo5dVRJNnl1elU2YUlHalE2cWcvTE9ZMEdwTytHU3RPaUpkVkJzeUF2T2RqYW5oRWNoREcxWTFGRkpnc0VncHdaTnB0ejh2YjlGbzBZYmloOUJvazZKUVBNY09lL2dlcUtSMDZodUNwWnVRdkZDZ2dGMmF1QWF6RWlBUmdnaERWWE95VGtxVE1OS0ZXeC9MVjkvczBSdVUzYVlpSE84clNPSWlzWUliaWgrYml4aE5KVFdpR2E1dWtxWUxLQmprdzlTWXFtUk5FZVJpQkUyUFVjQ1d4QkJveTQ3VGRrMEhNNERVZmQwazgyMlRhdy92eGpiQVBTYTV4Tk16eUdDZ0JTR3U5eWhGd3ZLNGRjWWxIeHFWRDVuREY1MmpJSFh2MFpVQjRtRkF0QUo5WURnUE1xVkNjRHhjZjJjUVhJOG0xTWpoZXpScnRmN0RrRncyMDV1TFVGQ0ZTVENlS1I1UDdVcmsxaXNuZ1dhaGRRaVhrdHhFSmNBaEJBNTZtNGRxYktpS0RCN2FVd0JRaTJUV29tNUx5c2NTTURGSGNGeGZxeUNBWVJDQWdtdHR3UHBVSUhrSWcrTWJvaFJzYlhOWExoSmhBRXlHdDNYbW9YR1Jib2hiM2hXMTJ4YUk5RWUySlRUQ0Rzc0llTUw3S3VVZHNYQ3ZVMFpMcURzNXdxb3BGbFZnVncwTlZnYTZpSGNadFg4YlB3emgyQmNJWWN6cjJCTGcwSWZrVXplSHZJZFhRWG5FbmYvQUFEd2M1bTVnV2RJQzFvaThoalEzYUt0Z056RnZ6Ymw0MktnN3IwY3BEUjBuU0wzeDRMeVBVL0p3SnY3RWpwU2RYdGpSM29raTBGZG83N1lOZkE3a0lMNG5Gd240SFpuVVNjT25BaHU4VzZwb1k0WElLT2h0UmJweEFNMnJJOXcwa3M2NWMwNkppTk5lOTR4SzVxTHkrOElDQzdId0tSdXBEQm1ac3g1REtMQzhKZ1N4bFRQaFBqRG9WV0VEU3prQ2l2QXhUaEpyOHFiSUhwWmhPZ0IzVk9RUVBCMDV3UUNZQTlEVWJZdnJwaTUwaExHMG1pckVPOVQ3REp6K1lpczYrOGNwTVRaL1pIOEN5VVlqRkloMi81bFBvME5hQ1BOY1l4d3hrQ1UwNkIzamF2d2tHc01zVU5iVjdQUk1QbHlubWRoMk91T3VmcWVEQkdHUm1Fb3VYN1lBaUp0MU1nQWRWdWRDUks5c1V1M0xEczBMOHJnUjNCVEh0VEg3SXdEZXdvUEdMQWZlNnZZcGhwNlJLaEk2UndyNkpZRUdWaGhwd29LNVlwTjVjYW0vZktiNUI1N1kyNDJaQVU4SWpIdmdzVHRyUm93Wmw5ek9pZW9LN2ZoeEVXWlN4QktpMTc1THcvZ3JBa0JvRDZESWVqTDJFOHpaeUJkZEZBMS93Qk0yME1oY3JPY3VWYkE3bi9neXJFRis1a1dBcGJkSDBxRk1CNm0yeXRKM0NobFhTblhhalJzZ0JjdmxKaGJlZ2FuYkIvTmx0WUpBQU1qQ2FFbVVTWHFHc1dvdEc0cWpwZVhmZ2xJZS9HUElwYU5nd0JLK3B2YzJzSmh5YlFWWHBqbGtwQy9lM05wYUI4NXlmaWQxWDl4NVBGUC91UndoT0UrVDJqaHhKV1Ric0pCcmxDam11SCtPTUxRZ25hQUJzT202Tk1idGUrQjE2MEVTUjg0MVo2Uk1TT05zMmJ5dGVCV2wzdzZUQnpyQUphY0x6aTZhcWF2V3NQM051RGFwSnNaVjV3RVVsUkJWV0cvdVVIcytxVFIvZUEyR3lOK1RnYlhtSzhsUEE4Wm8zVmNoTE40Y09Cb1RUM2hsOU5NRDYxbk94d2RITFp5OTFoeDJpWCtzSEtyaFluMzRuSk1xeVBibFdhYW5qa3lFYTJYdE5TSDNpelI0dGs1MXRxQnJRUWNhbkNJSW1naXRJRDN5azgxbVBDZ1FDc0xoK1Z3ZWpZbGc2UlovSG1jY015SjhkSHhrcUJBQVBJTGZXS2lqYWkvVlF3NDNJQmZ6ak9nUDZNNVpFekt0Y3lyZDllSVBNdVpWT1dzQm1uY3VXQmlWWWxTNlYvbU9VREdNdkJnVHFlaW5EbmhVbDd1YWh6ejkyd29Kc1BiSDZqZytvYlYxbkhoRmxEN3hmemFmM3dERVp0OEduZXNOdEQySnhDZytqa243MTFkNFIrT1FGZURxKzZrSEdrQTR2OEFtNEV5NVRLVlZWY21kZEFZOERGNU84WTRxMjdLaE83eml1d0VONUo3UngxTlZKOTRzckErM2djZEFTYzZzdDBKajV4bHdDKzdUeUJPeTJ6aEhCTndpcTkvMTVPemlqb1pBMHZMVGxIbU96L0dQd04wNlpQdHpodyszOFpvQWRSSHhvSDVoOTUrcE82VS9jQUFaQkpQTUEvV0JHUGIrSEdOeEhxcVhHemxjVE9CNkRSUHZPRTNJbzVwRWNLN3M0eFh2MlRoMjlEQWt0Q1YrT21jNHA2S0YwT0E5dUtUWXhlUlJ6V29Gejd3aU8zczluaXRiMk1MeVg5aHNJazU4bmhpV0JBV0lpL3hpNDhyVUVZOVY1RTNpWkJSd2hLNS84UUFOeEVBQVFNQ0JBSUlCQVlCQlFBQUFBQUFBUUFDRVFNaEJCSXhRUkFUQlNJeVVXRnhnWkd4d2VId0lETkNrcUhSRkJVd1VtS0MvOW9BQ0FFQ0FRRS9BT0E0aUZINERJUWdoRXlySUFtLzhvbW0zeFdhbysyZ1RhWUd0K0FXNktjT3NpUU4xbmRvQ2hwK0lIaFBDU2laV1VuVlMzUVhLeXZkcW10YTNSRHRjYkJHcXphNkRuT054Q2ZZb01hMjRDbVJxby8yWmpWRnhPcUR2K0FRcEU5b29BTkVCQUFqaElEZ2VHWjd4YXdRcGplNmdONFA3UzZzWEt6Z2FJT1VoUHBEVU9sTXpBUWVJbFJLSmhDVG9za1BBS2RxZ1NzeFFVQndnb05CTUZXQVBrbW15bUVITUtPWk83U2ZrWTJTYm9PYkpCMktCQ0V5VkFRRUpqWHVNTnVtZEZZeHdsd0RSNG1FT2h5UitZUFlxdDBSaUdBbGhEdkxYMlRtT2JWSWNJamdmekFuYThSS0FUQ1M5T3VDczRpNVdla1BGQ280OWxxSE1QYVI3U05LbjVxQzI5Z20zbGNzb05JVkREMWNRL0pURWtyRFU2UFJvdDFuR3hQZjROSHpXTHB2emN4aE1IdTE5WEhRSnBCT3g5WHVQOFdUM0dBUDcrRHY3VlREVWEyR0hQRTl4M0N4bURxNFI4RzdkaWorWUVjcmpZb3RlTnBSTGdnSEdVVGxWeVlDYUFBUVhLS0xWemFUZEF1ZWRoL0NEM083UVI3VUxLVHE1Y3RnVEdGczZLSmNxV0ZMdTBxVktuZzhOSXNTSko3bS93QmxDYnVkWWdYL0FPbzJhUEVyQ3ZaQm9WQjFlN1p2ZEozSlZacnFEOGxRKzVnZWpXcHJjN2cwYitmd2NxaEdjTUczeCs5dENxbEptSm9taTdRNmVCMlZXbTZuV05OK29KbEJuaWczdVFmc0NuVkhHd1F6dU5rMFpnVVcyTUZBTUd5Qk93WFg3a0E2YnB3NnlGS1AxSU1CY2gwVlJhMGt1S2Roc1BTQURidTNWRnJDOE5BV0lMWDFZT2trbnlaOVVBNW96RzVBemVibmRuMkM1YnMyVnR5REE4WGZxY2ZMWlU2WWFCbXVSdnVpR25WVmFSYTZSY0g3anlPM2NVRDkvZm9mZFB3ZUdyazFDd0VrTEhkR25EMERVQWp2dXFaRUJYdVpUSGRZd2haTUFoT1RMU3BQZXYxYXB6Mk5pNkZWcGZBVHFGWm9FTUprVG9VNm5WcG1YRExQZWl4d2E3TUNJRXFwUmN5c2MzMFdFYTNudDgxVXcxWjJhMnJTUFV1WCtPNDFadDJtblhZQllXZ1d2YVNRWUI5eWJsUUZBS3FnY29vaW1KdjkzVk9OdkZkTUNjQTVNYVcweS8wVUdiSU5JS2JUcWxwZkZnc0xocW1KY1dNaVltNVdJdzFhZzZLZ2hERDF3d3Z5bUN1alcweGcydWdhWHNtdG8xak9XZlFMcDlsTnBwaHJCb2RsaCtyWGFja0dSc3FXUHBVblpzVTlyRzk3aUdnbllYSUVyRllMQzQwQXYyN2xWcUI3T3lGV3gxSnZTNDZPTTVpMHZIY1FEQmp4Q29zTEhncXN3YzdLZERtYis2NFFlNHR6RFVnTy93RFRMRWV5cDFHMG41aG8yVDVzZHY2RlRtYUMwMjRZcXExak9YdWRmTDY2QkFrNi9mM0o5bFI3STkxMHJqNlRzTzZsSUpOb1IwUUtrSU9tMG9GekhTSjlGbVlBbTEzaHNOY1k4MVQ2VXJVcWZMWUJIa3FmU2Rhbm9Bc1ZqSzJMQXp4WkhXVlhiUnhyZVZpbU5xTkJtSE5EaEkzZ2czUTZReFlFWnloU3pOa0xGZEdZYkVZaW5pNmpaZlNuS2J5SnNkN2dvVkdiS3FCV281cDhENFJvVm1PczVUUDdYLzA1QTNnQ0k5MkU2MjNZVlZxdndsSU1wMk9wc1NCOHhLSFNHSmZZRWVqU2ZqWlZpS2pCV2I1R08vNW5ZYkJVeG1PVWZlMzBIcVYwaGk2bFhGRTAzRUFXRUp6M091U2k3U1Z6T3BsaUFzd0lpRTR0dW11RTNNcCtVNkpnQUZ5Z1dMS3l5RFFHU0RkWlhKdE40TW9VV21ubkx4ODFSZlF3d09URVQ1cHVLcE9CbXVCNktwV3dRazgwRXJDWStobnM0SHZDcTByY3ltWkduaEhjNzVGWVZndlVjTE4wblVlQU80S3JWTTFVdWRZK0lMVDdpeFFjSDZYL0FITy9vTEN2Qm1tN1EyKzRzUElMSFl4dUdZYUZNeTg2bnUrdndDSktGMm9OSklEVVdPejVkRTdMZktzdHBRZ1NvYVJvZ0dxeWE2d1RIVTJ0T2JWRGxrTExUVFNCTW9GMEdVQTl6Q2dJYVoxQ2pxTERZL0ZZVjhzZDZIUmY2eGdhek9XUVdqeUJIc2c3QjZzckFlcmgvRjArdGdXZHFwbThwUHhzc1Iway9LVzBSbEhmdjlQUkVxeUVRZzUwMlJMODBuVmZwS2JNSU5rU29JZEJUTkNuQzBvTWxxTElDdU4xMWh1Z1hvekVKeEEwS3p0RFNKWE1ibGhTRUlpQ2kvWUJOY1NVL3NsQUFtQ2cwRUdBdTZFMGxOYWMwSTZGTlRTWU4xNHBqb1RuaHdUS2dBaFpnVkE0UWdXM1F5NURhNkVaQ3BPV0ZCMVFKVXBwdW45bEJOQXBza3JlVTB5VUNXVkxJNkZOUUZrRXh2VlZRQUJGaEFRUUxsbVdZSGRDbUU1b0NQY3JBSVNvVUZOQkRrL3NsVXdNNnFHVEFUZ21DNko2NWxHSUthQ2diUWdFSFd1cWhMeFpNZm1GMWI4SE5NSXZuaENBVUttMEV3YktTRENkMlZKQ0Z3b1FiQ3l1SnNFS2JsRUNDVUxhSzUxVmxQZ210Z3FFQW80UW80UWh3bENGWXNpVUtiaUpoTm9rWEpoQnJGTFJvczRWeXBBV1pYSU1wbHlxdXF5akxLZHFwUktsQ1lRNFFneHgwUXcxWTNpRnlHdDdUa0dVOWhLTHcyMWdqVm5VeXVhU2lWekFzeml2TkNJNGJLbHFGVTFVamxxSlVCUXRreHBLYlFKMUtMYURMT0s1bE52WmI3bzRsK3hqeUNkVm01dXVZU3N4S2xjd2JJRjVRYUV3aFJkRWlGTnVBMEtwNnA1dWoyQXBSMFEwNGYvRUFEY1JBQUVEQWdRQ0NBUUdBUVVBQUFBQUFBRUFBaEVESVFRU01VRVFVUVVUSW1GeGdaSHdNcUd4MFJRZ0k1TEI0VUlWTUVOaWd2L2FBQWdCQXdFQlB3RGhLc281S0ZINU5Rb0t5V3VtdERmaFhaQXVobmRZV1FheGwwWG5nd2RwTkNab1ViQUlYTWtMS09YKzNBZFpORWFLUUZCSTVCQXNib2k1eEdxUHdxRlpNa0hSQ21ZdWdBQVlSMENtVEN2eEFuaU9BNEFFb1FGSE1yTUJvRkpjakk0RVdRQ2hqVTEwb0VrSm02T2dVT25rZ3diM1dYa1ZrY2dTMlJDTUUva2xBSWlOVUNNdGtPSitGR1FVVEYwSithSUpKaE5iQ3lIYTZhanNta3ZkQUNNalJORXEyeUQzRGRaMCtwVFlKZFpQNlR3Z0pEQ1hFY2hLL3dCV2ovalBxUHVxUFN1SGNZcUF0OGRQVUpsUnJtU3d5RHk0UjJTbTNIQ3dVOXlMdGlSOVUrTXZOTTAwaTZESFR1Z3gzY3NuTXBvQW1FNjBJT0lNb21VNzRZMitxMEVyTzJZVmF2U3c3QytvWUFWWjFicEU5cXpkUVA1Y1ZoSHNEVFRlQkk1NlIzQWFsRVFPWGsxdjF1cWJaa3g5RDgycGxlclJ4QjZneHo1SDM2ckI0eG1LWkkxR29VelRsTUQyQ0NFSE1PcGhBTjJSYnpXVzhUL0NNRFZTVG9GTHp5UWEvY3JJM2Nwb2FKaFAyV2J1UWNTRlZmUWMwZFcxd003bVI5QW5hQlBxNVpJVlNvL0dZbU5XdE1BYzNmMXFyUkRlMEp0LzJJMWNlNExFTWVTSzFJOXJudTduSElCVVhOck16c0hvSlBtNXlMc3JYSGw0ZlZ0MVJrdEx6L2w5QjlSMzZoVTZqOFBWRlZtbzEvbjM0Rk1xTnFVZzltaFFkWkYwcUdjbGxiSzdBVHJRZ1hiaFIzcUJ6VUJBUUNuYWpnQWgwbFdKQURSQ1pYeEZVRnpyRFlLdG5ESE9KMFdGQlpSa2F4ODNuN0tXdUphTEQ0UjNOYmQzcXVzYmxMM1dCRW51YnMwZU82cVZYVkhPTGJOT3cwVE13K0ZZZXJtYmxOaVBjK0kzNWhSM2UvY2owVGNWaUtINlllUUFWZ2VrUmlLblZtOXJJU21SQlRRTnlpWmtoSFZOVGlSRUtaWW1XWktZUytZRnJLcTB0aE54Tk1reTRDRHpDWlVaVW5JWlRDMHZibElNbUZTckIxRnVUKzFqWFA4QXc3L0JVc1JSYUJKL3lidHNBdnhET3JnVE9WMjI1S3hWWU9wUERRYmx1MndGZ2dTSlFKR2lvRTljSVRTODdlN0t0TnllUzZJTVl3QkFpWVVBb0VGcWxnTWJsVlhzWUpjcWIydkVnck93bUFicnBBMVB4VGhKUk5XbTBpZm1WMEE5NUZRdmNkdDFpWE1jd2pOTnVmOEFTeEdGaW5GSUVrOGxoc1hpY0lYWkl2elZCaFkremlxYll3UXhFYndmNFZlcUgwbk01ckR2Sm81aHFJUDdUQitTaG9jV25RRWovd0F2MFBxbjAzVmFaWWRYUVBCN2Z1RkdSeER4ZEF3Vmc2VG4xRFVpdyt2OWFsUU52ZnVCNnJFZkVSNUxvekF2WlhGVWdnRGZtZ3JvU29LSWxxQWVWazNJQ2YwZFRxVkM5OHo0cC9SMUorc3JDNFNuaHB5VGRPNXJxV3RHWmx2TWhmZ2NJZFdCZFpEdkJVTVhVYlRkUWJZT2hHaTlVcHc5Y3NONXVPL1lqekNEQm9MaVAzTSs3VkFpNW1SNVBBNzluQlU2VE1YVmM2cGNDd3ZCK3hoRG83RE11UWZNaVBsZFVSMWJ6UlBpTnJiK0E1N2xQT1VGeDkrOWZRTEFZVmxMRGdQQUpkY29NQWJBQ3l4TUpyQm1uZEZvMVZLQWJwemR3bWJ5RkxDTGkveVFickt1Sm51OUVETGtBQ0lsRUFpQVVTUS9MbEtkVHI0azl2RDVmTlB3MVJodFJKODFTYml6MlRTTUxGNE43NmR4SEk4bFNxbWVycUNEcmJuemIvSVdKY1l5Tk4zY3REM2tiRUtqVHkwd3h0d09SRGg2RzRXWEpyYjlyZnVWaW1FQVZHNmkvdWIrWldEd3h4TGhWZUlZTkJ6OS9NcG9CY2ludWExc3VOa3lzM3FnL1c2Yk9XK3FiY3dpQktadmRaK3hsaS9OTkxqWW96TjBQaTdvWDZlNkJaSVJpTGQ2SkFGa01QVnFFNUdrd25OTGJFUVVTQXNSZ3NOaW1FUGI1alZEb3ZHVWFtY09EajRrR1BGUml3WWZTSjhtbjUyVEtXTmY4TlBMNHdQcGRVdWptRHRWam1QeS92elRiQlR5VWxPRGN2YVRXc05PTnVBZ0p6NGRBVkloelpWUnZhQjdsUU15RVh3U0VIb09VaFNGSURsVTZyT2VyY1NQQ0ZjaFpaQWxRQUVTQm9nMElRaW0yQldseXJGRUNFMkMyeUc2RXAwVG9xY0dZQlRtNXQxU1psbEduZFpWb2hCNGFxRENNeGRGd3phSVBNYUFlU0NCSTBXeU1JYUltU3RvUkJJaE5zRk1vSTZwcWNibFVTWFNVMTVQQ0dyS0ZsUUppNnpHRm1NY0pLRXdzelJyWkJ6WEN4UlFzMU5UVktBa0tJUTFWa0hCV09pcGpJbk15OFpRdWcwSXdGZFNWSldaeUxNNGtYVFdBR1VVTkZvWlVnQk5lRFpCd0dxNjF1MTAxemp0Q01IVW9RTkF1MFZsalZPTXR0dHhFSUFJRlNnVTYvQ0VKRndnU2lzN0cybGRjMDJGMTJ6M0xJTnlnQnNGS0RTUWcwSVFOQWlTZ0lhRUxBb0N5RjBBb1FVcVZtUWpkZGRTYnV1dmNmZ2FwcW01TUlOQkZ5U2cxb3NBZ3hzSUFsZFc3VTJXVm83ME81YW0vQnFPNjJXeFFObGRBMlFLc0U2czBMckh1RmdoVHF2MWQ2SVllbnZkQnJXanNpRUpRQ0RDVjFKSHhMOU1kNnpGT0R0aW1teUhjaHJ3Q2l5MENpeDRDSlU4UC8vWiIgaWQ9ImMiIHdpZHRoPSIxOTkiIGhlaWdodD0iMTU5Ii8+PC9kZWZzPjwvc3ZnPg==", + "description": "Displays historical flow rate values as a simplified chart with background. Optionally may display the corresponding latest flow rate value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'flowRate', label: 'Flow rate', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'flowRate', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Flow rate\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#2B54CE\"},{\"from\":10,\"to\":30,\"color\":\"#3B911C\"},{\"from\":30,\"to\":50,\"color\":\"#F77410\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:c2ltcGxlX2Zsb3dfcmF0ZV9jaGFydF9jYXJkX2JhY2tncm91bmQucG5n:c2ltcGxlX2Zsb3dfcmF0ZV9jaGFydF9jYXJkX2JhY2tncm91bmQucG5n;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQoAAACACAMAAADESdfxAAADAFBMVEVHd6Bxb2tta2dlY2BdW1hFdJ6EhIF5d3OCgn5raWRnZmNZVlOAfntjYFpPTEhvbWmPjotcWVOMjIhMSUV2dXBWU1BlYl10cm19fXienp+KiIijoqOkpaaWlJVKR0Knp6iTk4+hop+bm5gtU3l7enWpqauAf35UUk1DQDthXVilpqKZmZppaGdhYF5fXluQj5CRkIyzsbOIhYJ0c3JST0ksT3IQFyKWl5JRT02MiousrK6MjI40V3o+OzZ2dXWJiIRubWxGQz5GcZh0cG8qS23GxMWVmJe1tLZ7enpAPTmcm51oZV8UHitrZ2E6ODOfoqOUkZGCgYKHhYZvcHCal5bKx8m9v7+goJwxMCyRk5U2NDCenZmMiYV3eXjDv8KpqaW4t7kkP14dM01aWVc0W4F6dnavr7EaLUVBbJXAxMSHgn2rrakYJDXCvr4XKT6vr6uGiIUlJSS+ur03U3LKzM2bmpQlRWcgOVZFREK7u7izra/T0dTv7O97fH47ZItAYYQsKyi4vL2Df3i2t7JqhqZpa2wgIB7a1divqquhnZ/Qzc9wa2Sxsq4+aJD+/v8sOk9MTE4/XXyrpaY1X4c2TmjZ3eCBhYiGi4t5cm+AeXclMkZkZ2oNERhaVk0hLDyalJD5+PyyuLiMkZFgXVO8tLMsRmNoYF2rpKEzRFr18/hzj65gY2V9nsGWj4zl6Orl3+JbXV+vs7OTjId2iqRvdHfw8vSMpcFFR0nf4+VQfKRRUlWdq8K4sK2Bk6vQ19uKmq/h2dvDycw5Oj0ZGhlKaozr5eh1cGeXnp9hc4p8gYNWaYFVWVzYzs6Bmrejm5fDubaiqKixqaZ9eW9UdJe2wte6wslZgqqmnZ6uvNKnr7dDVm+apbeUoLBkfZo8P0Niia+no50wNDuiqbCyucZCT19ylbhufY9mbXbHz9ajtc2Nh3zMwr9SYHOnrsKQlp6DjZaRg4U8RVB0ZVyFa2aPeHFcQ0aLUFiqkJOkjIBoVE6DOTzUyoC4p4OBiTSxrie2siVbVv60AABs1ElEQVR42lzXfUjaeRwH8KxIK5+y1XwslTTTmo/0YNgD4UN69VNTy8wMhkHJUYZB2QVZXJcrRgTVoo1dbegfx+5qFI2IcSm3Y2y0Njhi/8h298+xg7vj4OD+vM/399Pdw3vqbPslfV++P9++5lXYUun03XR6Ob+vS6tta/NbLPPzUqkDxenkZtOC5/MuhqUAf97a0vrvKFu523BRq1LpFZhMJoXCyuGIMMwqtsINBb7mbGtL6tn9s7OS7b5C+oysu6rWNh4YNZvNIyMjMY/RHfmYc+N2KryQeTEbg4zsjcgXtX5qIBASw3UQr3FYrtLIx/7J2vuF9HP4nz1s++jFzZuMIm/XJF+i/uKL6KzHI6k29y6fmKOcSVKh7p6NlF9QITN008uuJwbWvaaiBl33wsAgm1uRN1g+kEYJdwBFMOi3AEUOAiScLbBELqwd1s8tueOc74AvwIEJy0bBHeAvdBGiEAhAAgIYVvQAJCKeiMfD9rX1JTsXS2r+Vkdh84xMVl5bKQUhTHKIS3hgQedZCbfRuJUKD2ROCYqRkGaxrU0akIc+iJCERPGz20cR5xjQd6oHF9LznBdfnZ42tKnE+/6vnne1yyXqzS8mgKJ6ZaTbUEG6UVa3eie/oblT1lnx7P5D49QdMp1O15UVV1aFl8NV1+x5g4PhdHrQsDDQ0ZXrBBUvRRFIAAORVqgBl1FWZyvOh38ilm+325XZeJEUUvGiVhAYOERcBBJ8Pl8w33Wd2n+mFjnqK8pnZJ26e7SbJ37iKkxiRm/2MOI4z1pIBxBFzIMoDpntGr/fKRetfRg6hAsF2FxERY2N/a8VJzdra1lFW1t9Xx2cnE52tfNxiqWp2er2rrK6xLPrjAK6rEpXGXx0tRFnSht09FpaCa2wfHBw0HDnoXvqcZ6udiC9XNFQUkyC8fATpZDiEE6iD7lxcDQ8qIM/D8hoECBeiMDrJZ4CFQKC+RDkJNBQAAOkGpMGteO+kDnuqC8t7zbIdA+eMQ9SKb8pF8Wl2ZPL2LnbbRTMZCkg/Sz5EtW/HRDNhoRiz5iHiW1GKKx/HPYut3aB4r33Wv1RITi8CNqjI3eyrVia9fBJxdcZN8uryksZmouNDXE7uaCsora2gkajywydtMX4lPvW3K1bO3mp5ubB8OPHmnGqJQilgEApipCEM+eAHpSOSloikaik0WhsLhMqIYDAsgEDkaBBQUB20EFLE4utaDKQg6R6v61r3DcxapovgUZ0y3R/vFkUKw5SGQv+EkgPyfH1IzE8+LxMnL7PHHg8YOG5dXz7mMtuDwXct2/PQXPs2OYcpYlgiGGt/t22ovb34fRyOpw6yKc71MPG4eER8hBfn6WQl+h0ups7j95tiIRD7RQqlX3NVlb2gJag0erq2HtGo9uNLC7yGsPLVZ0XccjFhesRV+PAp+OfTrQSaamvTCRskESip71FaUcUChTQQO3IWiAWgQDfIzAeOACEab6POr0yobAU1JZ3dneXr2qS0+NWxYtUSmoyweUfS2RVYJJR8+Hh6KheIuE3pE4CHmRxfPv27WMmVRMIRdAzgoLqAgaJd/7zDkoT/9Lrh1aEw8vp9/uWYaPR6BnFtKxqoHgJFLOia+OyhfV+pvDx9MrZL1/MeRRkBunN4ocPi29IPbSSyy4FfEvk1lXe3fBB4UDqmkPpiieTvCvIxcV9FWyWhENOoiTRFvyq8EZBjy1huwMlISTQAkBDYII5QRJ2JKHACwEQEojC0kf1RUdN/oZa+ozBQH92X0QOTI/zRKeplBO+V6AwES/DQYFHqJEkpNdLdlMn+o8Un3HbLAG5m6BoAQqp0vS8r0MqDklMR327fX30cHhhIRwOp8PPJV5pvq3mGqL4FqeQWwUVxWz70suXx9+8CtiHWMJHwvvT00KhUNU+TiX1lDbFjMZhI1Ckw4PpZUZbWzAoLWqxu66uksnkxuuNZFwssOd63+r43fj11z+1UP2WkkJaj7NVaUfvZm4BCgVQQPBd0xpHdYAgie0+qjg6YfU3NNOrug26Ny6JBLsOrRDxTlMZLoI05SBEHAggIg2Cgj96CDPzw+bx7e+off6AaPj28ZPzsTHn90BBpjRVh77f9h8dHVmGeFPGh/cTmV6EsbycYZCK57XMAEHh8chZvtm5J589MT6SBJ8NTQcCEyGe2CQUstrZ2vyCyuLKygKFZ3j4Kq+xEc4UYRI5GMz0oI2CWsS1i682Xq+8hmwkr3wuAbIQRDafPOWo7C1Oh7aAjFpBnB4gcYwXVwigEKgSVhEfRyAevUFBcsU1X3+zttxgqBuSj8IaMYZ8elHEW09llHinCFBMhKEbxsuG/1Vmna+HehwegsaetgMoQj+MeWKXJqriaYTddLnv7zvyT4qjw24Uo2dip3nGMIAw0ql8LplZPZqlmHjy5OfY3qXpQ8JB/ispUlHIjOKChhs36m7caCgosZVWlKwWTI5IWvIa7zY2Ni4AhT+csLCpUgiF4gAPV3/ydTS6EoX762TcJeDp+XEfzwVNsY+32GE8chQY7AoQnoijQPVGqZYgCT02H5cotSU3SssNsjtX+lE9UOg5+RPTizzeSSpjBwS8EwAhyhEQ4fEPUusc8ASLvdgIh0Tyh/rl+u+3LFIh2/TUyJ73H22L1DhDxO0ZtU7W7NTUkArLU4MLaFL6gqrqlSgxIDyBQiDuDyTf/cEofQA7ZYWuvKpTZjD09hq6O8sr6nS1Bfns+yFj3ieNkOXT9zc/TydAAsVRVKQp0mhg52QKoCAr0TOUaDIOP7RXyYVdVYl2BPztxOBUiRaQHQhYKno0bZl48LzVJ9narShrlnXunB2OEtHH69U+gsIFjtnhwD5CVMMdvRyiqEavN2reG7Hm10MrXH6WfHbvku196qYK1z514xnb4+8XjVMs1KJ21bRverzuZGa9sM0bZMol0W+IbbN/ZSLQ1P7mXm93b+89W90DHUjA08GBgUFZRUNx0L728rvv5mJ5dxs/AY33M+GFdIIKFJQiB/6rFElwh/BDN1Pg44HHQ5Sz5IXLZUcSOAQEXwKOgRAIi/nehd71I0xj5M8flHfTf4+CgVpNWHCK1b5xPi+TOlEgCkiWAhHgpnAHisypCDULUezt19db5PK1J0/f/rq1ZYFWUCTQBqiD3qRScSkU1rQ8ENhYmQiJxCbBWmRz0xNkivTqb16uIQrN4rNK2oN7fxo6q3p19Cp6IWlnfEgTLKXfK+iYbDrfnIuJhuB3bN7duzAh4fWZT9LLPVR0tkIQAJA7Xzmc2eOm3RXfOHv4Cvc4e73B46FtgqAg1gBjoUcWgHEUvru8MNCWHON8vj5TugEOAKEmNDj56v4PfCyTWRfDHklI5ByQRDYvUqe8akkI9pb9I62lo14TkGO/be9j6ikqUFAPv3h6DvO/vb0vli/NLsFtRW7l6I1zT3/+4dI72QoUK4hi1jPb31NWRrNV0u4ZBmS1JFbSbNb7WoI9dYU0Btvf1+YnsymqpiaVMO/u8kDJafDzF/Xr6VWqlIIgEAUsnsmE/dJfeqNFSYTJhE8kdqt8Jfrw1atXn3766mF0g5+bb/ytBAiiFruIIkz61FO929x8B5c4hDtOYSWp+8f5nFRm3ccTYSBBgP4XAigyp5gE8z4/CrLkYxztdWEoEDAb0QZpUcBesfcD5t03yZfgMwgcqabMo/qRyNOfYz+a9ltVTe/O3n2k+NTTv2qDmajtqXm0AQocVk0xjVZZWVdWWljA0JJrmOJ+n88nVxvhXJFOyyxSh/MoTW2HudAQEOCAPmQ4dyrvMYV2iADuTHTccCIQzsbZFGC8gpJEoxKi1NDmHMUpTrFjHNbnl9bdxynMZrghDGvNSv94tTWVOY3DTos6AXOW40SeWYr13S1/G6Up9Pbtj789l7JdE6FZ963Nr4+PqRxoRZNobSwSMbpvRTyxw1hk7nzs7a/7XJbQtQYfYIcWKUGVaMWMt8LTz1hNDeya1dViJpW0yii2gUJZoY3MahILWRpxSB9CdeZz8tIwHrLr2mvk3eUdh5OLPnQCAwosX0n6pYH+WJANaChxDdBS2U0cvnp2agpxmNVRvQSW8GMuL1JpOPIwZ2+NsItXV8CAiBpZKBzqd+MSRerkQMTnAQUEy+28Hym+b+2r32WJJJfYr9vb23wxheIKBCLHX369ef7Wgs0ZpZ4IHJYjY7G9sfPz2NtftyeVwnfJiYBviM2w2Xpo5BoVb8L8HT4g1l17eanUd/F6wvdmtbKstBQGQ8NSNcknlqIupzjO8TqdDqpF+zdV5hrVZAHG8W3gNsYGY3EfcxfGcFy34SIHMdRdSFwgbCzagIFcxzZwMGjjUm4oVwungIi0uCQpBxNdBUaYnYLIwqADZhclrCytrNO9Tz0vZJ3+nMPhA1/e3/t/nuf/PC/q8BPPvLXTH4fDJZwoSQsOJiKDFIkNShDN994Ux56pBEXDD00JNAAG0CBn0pTtInjbct3kD2E2Ob90ff3yG2fPtrTcutXScvbzt49+d9WbxW84SDkhQCzxn0ppO2yAIvet7gEqMoD/zegPSEBzaEpNEYy7hzPu9y70hfCNUmmUVsuKijq4Mj8sq7QSM2algtraiqEhCN+TGUkcnFY7NramEeMU4PyRkXTSCMmfzbteNbqJgrkkbX3zTSl9H8HTzwMdjNVqJxbH2hy6oRvzSWnZCWkJmEDPgMjtPqgDzxw4/FACJCx2STCQIOIRYbFNWAiUadde/rBfoIxmQYZAZif8oURgkHcACeoGCfnk/Bdf/PTdxcstq9OoLeVHjjQ3Nx8p34WaXj376u2VVj45SLtJQvfAGTSXfMweP/zW+ewNFCDm5hRFRJX1ujFsHjNjvm+hqZrWZjXmq1rPnVFRyBaWRVNwEGZnKZ4+K+UZh4ZEGcOhZMbEBYGAgg8KHCGN+I2MjJBI3n5ebDGjn6y2/YOiq3ZIlpaKQeOCiYILrsUlh5wePbyQkJqSkh0TG7ff0zvSZ+vOndu2oZ6BXrEdx2anbVqCg0cShUCQYsdiE5Zent9B1EbvaW8PgR/4vScEeACMeNoeCHMg2J5++OnLpz+aRpU3h9fVF5UhKiqqrxMWH5n+6uOeK9FBGvkmCt0DFBprgT2+76Z7AZ4fQZHxD4mwPX1547uxyhBZH8Tp8fN3zEZpR0/jKz3f3JDxGCyLJUxXUXulYhJLn61ITCTDu12a0Wj9FSQgMOg3MjgS6Efy5/Cccqs5n9rPiJKbN1GENKUF4wXaCcuYQ1caMrzgPhRDiAUAnoFcLlDw2br1qZ2PQsrYQBFQAiCIAALsgEwQcWhoiiC06fd31sUcAQXY7IBRuodK3+iPzPboTFo7XVSFZIUbl9+4+PYWVPkxSZHJYCoqrJOAhEKJsOb48ZrkI6uvvno/Q45ItyngkTlmBhS9N/uG6RFh7Q9MkQFbJoZIkQ33ugdSqtv55u7XFlRXVm5k9OXlVSdqXC4NwzK5MrT+YVTIvowGI2/NUcDAB6VzSVw/WLgVGC8clrzHZjUb86UNPecaG+yJTp25EVBYrRoXQHCIAELCBoNBgEYCeoORPmCHR7c99tijj23b6hOJOgwLjF8aEq7AEPDUgs2dNEWbdeGdFTJln4CchQwWLBaPh+WE6myDIO50wrBAkoL805ZpMER4vcFQBhgQW5hAZUWFNU+3fHD566PHULc+W6nIzzGb/2WRuWQs0CctvDb/Q6UIXIHkq2h4fjQ+vg9emHsfrU1nhn8fluXC7hnE5q0xS3VUi0XNY8hYSVixS80JaTBqgzoVnV7wQH7pnUFaDZUvNRqhlhAKjcjGIeu3AApwhQNyBVOWlXfII84zIMCP1NkJrSRy607QVsjfTwED77iYhGB8U/AGCm7aI0jOxGcJxGIxpEtQyo5f3rkCWWKflkehQOwUCwR4LTRLlqad2sYvtdn4cqRlXl5FbZmuMZgKAYPh4VOghyG97kV+lR2/+O23XwuPob66Pdva2trQoco3IyhobcYCcWjTpRvzolI6k7lnOI8QiwtNasoeONSUiWDQTYZU52XHeBBwvAK+1WgM+zCEpWFREy2JizPXHEwGHlxh91KM+HEVQMFp7ejogKna8PK5xnOzYKOk6r7qUC2gsIIrkAJhEeDUNxg4twwUfLZBU9i69SG4aflEehJScWx8FhGaA4fTxEEd2EDxDBGJmYLNnM2L52WW9L85S0OCxQSDDDMDIrhW8IBEFYCw0alRovU3VncdS179SCipNyEUDFAhUB7CcKHw+Cef/PT8yfda3n5RUr56eaVhtqenp7WhVppvZNmka1q1++r6D3J+SG8C9PSsfe7z57NDqXKzmZ8Rv5BNiIvz8I8Z6DWa4V4xKWPJqhkaTYGaRWUQ5+bm7HhqqxGH8dWz2nT5Gxeo1qlzMGSHMob7quEZyJa1GeNaP8tpNm6isMzNdSpGtm/btm0nMPDZHuntHeiVQMRm7RBk7cuqzsJiiVgsh5OGOgB68tKBRdcEWSveCFcAIpOW/f45aAm0zGqlJR5MQqEAjfhNEjZblY2uzGSFfXZ2ulxSVlg+XV4GHMAZYA1oGQaDqay+7mjLtx/f/8lUV5zcjEKdvbFx3FfVwhtst0kL7vLdV2FGDGCI2Oy8Q+OH4Kih08F6meAFPo71542NWmNTq2V9TW4xLEAFM2qWpYCp0cIzBZKC8GGAotQoRc5wPVNTU7O3IV31Qp/TuhbXZqx8enRfCqdfs4S4ogoKhOYDEBAEgwEBJAI6JQ2PxnM4RDwW0qIgFIut5qSVlOz+dScKIQGLGjo40zmzsV0gFwoa9lpjmBJRKEuthGS1gSNTqQlx2kAidzeBXPr6WVRzveG55PDy1eOGegk4438VYnjp3ddffa+sKHx6l6S85fUK2J+kOQCDCa7QZ5y/ev98XobMPe6ZqmwbHaXnLqTGxO73jmW72qw6UXRfWqpHXJyv3s5zqHObCKEMXmVUlN3T186gRmHprUaNtHWqp2d2ZX1+uK8XGyzWLI3ek1ojaNgEL5Kntx8J368ZGzU3qpDNVAk5gxvrhcZxxPBC8Th/f3+0l0cg1zMOtpC81Ji42JKShN1/DiIoDp8mPJLODdjvidldIsgcm7lmbFtq1C2tqdsi6Dqz/P2X35RGwORYXFSDJUARypuPl9x4/daW4qLnX6ypLwtfnS6WFO19UCESIajm6HtXvzAdf/vp8OItzZK65tVPK8AVTZCD29tUGjv+avfVSwPuO+fzOCJRbnYqafsjkXElrhmokKS8VEKcJ9cvjjNqbMuyxnsEBDwSOkF23FNZ+TrIGVZHx+zsN1cgYS4sLKTpXWMz9zo6pHIquYRA4o6cnltevmufI/azxnTGxnuIK5TQELQUHkMd5XA4hhzWLg0R7Tvnvwxlg2wijwTE7NvBo9FcyHeQwxd6f22njbm0eiIOzSV5eMf9cebDHtsovWPIVis1N54501MqP6g616jKkQOIdqUym7oCJJLLTtYITadMyeXTu0zAoV4irCvcrJCiwuTNAik8htpVJAmvO7b6KXzjSMpZ76W0qSz6hUuXEBLj3d3joLjYrbtdS0a+bF9qbKCnt0e/0jZ6s3tBKs1yUP33+wTq4QzZ1dWVU9E6NVvbMbSe29uLxQVr1xAI+SJWdR4mXaHoRC63F+4ud5IGB9Ptds2YNR9QgCtYDFhYjLUNDbWl0ZlYdjAer7cLxGQGTysgsn3T/Ty9BCEREc7rKBqczV3t16lKqnPMCVdN53WXXn9tWKFXKve0Tto6pPlnXnilRySySqfOrQyVVkZo4pX8FYREselkTd3eU4bjxZIjt44+96KwrmzvZoWAoEpOvnf2bE2hcMuuekMhsJj+9EYSQcwKszilGgGroHIPJ3b//u6r4wN3BkB37gyME+K4BCJucdSYny813uzulcpp+dFeAUEXXJpMV1dlgXUItlGGBS+eWJwxdjRIHUxycFBnZ2cQQHC5AAKkLS4pkEDwIIj1migzoKiE7Z1plUflVrPZnB07KJS7FAbDxYOpKMbi2UFeHiQuafD06biMiDYN6hB8P7judDqjnW3/KgsiLHf5envPpK0BUDx7ZlYkkldMTR2crBRNyuk5V25/egtVXHYyue7hUydraspM4dOrzYUGwGA4icwQ0NH3vh64aagvbi7fAp4wbbC4HxKW624CFJYJSE5oX1+0v6D9elLCIeAwcKn7/MCAOzdElI+cnwHFpT4VtUulZgcXOHKYFgu/soDMIWoXx0ZVHVI+MykYt3z3rn55eW7Zbl+2+6dzuaR0AiEWBlI1mcFK1GvGcjZRQIH44+wC/QVXYjAHS8SxcQkYjBcGg0H744J8CX7bt3FP7/TglChQrx0+8MyEchEKBFjYQDP8ENJpRHepPZP8DRQvzJaWDlX0AIow+qSqduX7z1pQzUUIiYefOyoxnDpZfASFKjtlgLyNVIgJGSE1n7/+7cXnioQoVJFJKCyrD69r/uhrpu79N6ucKov2BK4fT3aOQmdI263wiNntcT3rfLb7zp07hwbG79wP00nNNy8N54Q5DpaKPowerlar1TNRmqpr94xdTEqeLwYzZwcAyEAJTPdKT/fw8IrB7Ibcw0hUV1Z1dcH9Tq+OMkqhQLqsDgtvQtu/bGcQ/TEkz0gfCFcwVzciFhIvtj+EhM2nAvZ7o+CS98Rff/3228+//DgBl38nxEkrtJcuDZlHnZqUN0ilCIoI0SS4YrIySl575fb65Zbp8sLnayR7gUTd3odPfpJcd+zW2y/WSCBm/VchZZ98/tHTxbuaw4tMEmEZ+OJIy216bpbaqWLAnhkmo2T5Y7y8gogTTrMTa3RWWW3XM3vzDl1FisYtiy6lh83n9uHT2Gz8hai1tRkNiwELOCkQ2kLQMrhBkQ5BExeMC0KnCSjkRCac9CoLmDIKHpfti6YAinwEBTxK4olOEtzzuIpOUoBCETi43WfztAl6/NGH/Hy4nQEPRcISA5vpF2/9+OMvP//8229/gRAmE1R5Tu03DbOiDRQqBAU9Qg69YpJ/5RuwxBsXp3cJnz+ebAASLyLGEBpM4ajV5iK4nv9TIeFHP7ro/skATXO6vii8EFiY6sIlu971DNRrnFIyLykP3Yk5ARSsyNcsFd1ibBtCckJDR35bJsednRsxj+XAXiSY0MD6zagqWJpJZCi4yPI5GKhIJ6Wno4PYwcQmjoCXqFEz1WoZD8tOQENQJ+oFggsMhl69BiiuVXU5HBOnSQpQp3/nCHwxjHyIpJ8gn4DhEfkUuOPRJwP0J3xj0SQPxd9smwtYk2UUx8eAMWDCGMNxlbELxH2AGwqL4RgXdQUKg4jr5mTjNogBU8EtL4UuJ42B6SLQYoGakBgGmYKGRsRFJSkRUxNvSRft/nQ976aW1Z/t83sQH/l+O+e85z3nvJjnrl1bR4OyWrjHdi/oFR4CKj9a9H1Tf1FhXT5CcRtQFHd3Z56EKAEbsGmMVNHFVbQa9WAYXZpasA6DwXagUdjYzBaI0T5Epmq+fPToiIkNoaLMzEIgYwu4U7cmCq425RNzN+dSM5qq8iE+IomYBaKIfQACCbKwctSH7p04dw5WNSi/uSzYUQEool9zDXCBPcdC3tUsPN4fx+PhKitzI/FkK6yVFZnBKMk91E7NJeXiyfYLedB0yhcBCrAK1krYqsAeDEvGU0usrdqT85MjzNq+1gm66MuW2IfZ7U546U3M86c+d9vg2+Tl04SqyyCmtzdsmz+d+Onnpv6Yqrp87d6d+80o+ltgpzg7ePz41gGdxKhXtZbN0MuAhAlw6Nkq6RU+F54ZPAQJVhDIKvhAQkCXIRb0WhmEi8uxpKvbElk1hYcPx0RYMICqmRXaojQLijGAsZR3rjOihkd+KszF0c3FJcAJ5X5JGRQrXPjZD2kr/YoreNAEtrPCvnQGrSC7S3p7cw8dIjGwCwPhqeGxA0klfkGJCAWsIKzdjMi07duSq4D7hD15oqMKUCRDAa68qiPh86efXf7EEhu7PbvPY547dep+X9/O/ZM9LQ3wm0BBoBDat76ofstsARSZgGLvSW/mvuLbF65fHAQSB6Zt2WVKNueDZr2M06gRCIXIMBR02wE17ESEbSqUZXGnjoxA0OQPiIW1dImEDomYSgUuMlh4dVs5NSuzMPlw4QMO1dpqZmeE96dVqImrBSC+uSgLWuAGqTKqLgVaMdZ3dOyIji7ddzg1yZPoQM2sOG+1m+J8vqT3yz2UM5Csu8C++w302Qe4ui6Gf/ZaZOQjFBE0ZgysB7B5BBbt1lET68thL1+eX45U1UR4xsnVaZFrACUXc+3aKZuxsboG7dDp0/V9+3fu3NvX3XICqoZFzA39MeWPUFy8fv3ipdlBYLF1QC1rlMpajRpVa5vebBi1HMAh5U+L20xstIjIJOAg4weGpXI1bOBr6bCASFR0cBHD1n2d75eTsqDjt28TYAChx/dgBXvXbUum0bwLIang7SBGWTsuWeT6mn3JZisCzEOVd0xWsKhnP8RB4ZEU8npx6Rn85s0MykowgNcCFiMtgu0mMHBZacUg5JJ8MnKDmkRahKJqB22bmQN6/MRD1nYTEJ7ykSww3nzSzR27GOo3TphTpz5/d+Oq1bHQCCzM12obGoZ6zEwAQHJLTH5dVR04yAXvi19dvAQkEIppjKCMK2gV6sEwlAaFhcScplYinR7Q1MrMHiIEKUw6zIAY3EIBLMR0haBWgswi6P31xJxqaHIehmRFBLMUYAdJsUnMBt8KIsmT6ecTHuTlT3ZZQnYnsK6Kguz84zZ6IhREqldB5zmIH5s9RKUrFzs5uS4CwTiRE3Q57Cl4Fs2rAipEEfDcG7bl5AYVWFBAno1AIBLA4ttQ96uQvJtlMYwNTXDpaN/j4op55uXQgwkJjD0Ef2iLrfL0jA1O9S0Cv4JCe3IL04xi7/4LzItA4tLsLMA4AJECGUWXRtbaqBFzjBq2UNgMNzKpfEAnbOW0iQVS9fDw/BwETQxdYWYhULAhVkigzrc1taMjNwsKGC0xGcG++R5QCGloWJoW7K31rSDxEtIT4jx3eBAXrLPKS1lNvFqcQ0k9+8UXgGIDNakyjZVRuFe7hUorWPjE8iecXJ46X1JTsGVL4XrwtESzytdHgCoQiqCHKLIgLiAhGCWhuA3rEZOHOMzKBH/ddgjzzLX33nNwcHghPQ4KGLCHh6I/qmsvjVy7NKLFN19b1bB3//4LX3yBSJj94wo8HkQKMAowDDpHOCMFwwASzzVr2PJpZZdebovBYAYGpq9M8zF0CJoyFDTFCIRABmZxvDBi16aWurqGIBqgyPKPxFc3eAYH+1b7luaS7FJSVmcUxRAdA6saTtcf6szMISdDi7J8snxDRtIZItFn097qLcTKAueF9ocmEusaQBBmq5gxpUV1FR4+4cFMhGJb6cS3j1BU5EQUgiw4NofiOs1368211oii8LNMj6TsWBIUz8BBctPSMsJjk5LSwkm4vDyYx9oIDrN66eq05BO++dWFDfv3n7h08QsEAgRBky82SlVgEJLWLjV6q1Ds5JTNaFQy6cAAcLAI4RgwoUABQVMCLsI201BP3e4/On7n0uxZn+AMZlWQeyThcMtS7+CYOt/SShJ5z+bepphE1gJGS4NW1F6QSSP39+3tK5+8+z4t2pVICgYUESxikB0v5GquV0x9daFX1VDht87UjBUtWTirwDf2eAGJpnM133p5AYoOhIIGWaO5Uwl9/N3WuKsrMnyInoR4ihX2KfuV0C5YEBbmlE5Y5YU5tQ6iyRioA6wlEaoropPFmfsq/IirWEW3txeevHj92OCRWQABa8dxIHEFgqZJWiZUcjlgFOgtLNNzy15s1iAkmH9JJ7YEClmtAIGAaMHmX4ZcdXp8cPbr1A9jqvzscLjCFs8Yj6K60goeMbe3t93Pu4O0EI+suKagONruREt/C0KRk+Pe64msYgeRlWXnju11Jn7YnVlAyG5YX5LgQVvRj1DYRHoXrr9apN3Q6wcoTnfsABREqFXBJ+2DxmGcHSLbPXH+BAbZ6qmV1ojDggUwZLE8JAUXhDnlCssHCIXLniF0M6YVJe67hFKp40e+nh08oFEeN3uGBQXyDy6b06YWtzaqVfA2G4c5dl7TY/4juQmyK4iYAEIskEnBQ3T8YbmcP3z5yIHxwaKqnCh8ZXE/LiK1qDoGUHRC6SjLqyrShoSi/ARTS7VbFc5suXt3bIcfrTy30iezr7oCmiJke6veeF5Sd2EBIXioY3MCLYN2IotHCQxkFY7dbb/Yp/2WlpUoOv0+pFil0d4wOpSdGgPRM2J3CN7LJxa3MQoLJGD7Zo9Fm0LycgfCKl/M5y6nT/cgaScnG3oe6NiBA5A/HIBfdvDy8LAGTMJCAq0ftrVG8IkutYKjNAiFei6nTANkNHRh28x/QNgCC4gSKpnFLGRssZhtMKO4/MntTz755HphMBmHvzCbkuwRISotjSSVj4nSrnpUsaypPUNDYzRm3dK1MGDWcFcLKLISS3DZm/qqd5BI1Cgrcq8VoNgS5L6iZ8MeSlBGUktOJMVmQW+hVuRbFJNTEp1zV3v6/VJA4eOxFBV5Nn6UkhcHT47aPXio7uP9UTG3EuYFWGlPhK4lNGEmh/r2m9UwNDa2f39ffX19T8vgAaQbN44PjvNH9UcuDZo1O3vp0vi0XCI2KIRKqRAQtBp1YrNRNKtlwmb+f1HAlxoyCkVtrRmEii3h0vnD0D6bOnD06J3P7nzhE4XP2xUPKJLzSwvwvMQGkSeLFFSy0uo0fEI5zBOktVhP7zqRaKwiyy9xLS+psB6sgkdMwDJ6nfFJ3Tv8GEl1TWsTfDNW9NPsnlpjwyrU3mUWMSdKqLRybc+5gootBcRXPpr5OBJPcCcw7CgUfAY0Pmk0NHLtt327l2/TtogY5hMOKauaMCILiL19e0Hm2+4Dg+Nm3RgfPD6luXxz1qxLoGPHxqcNCjMGAceoU7Wa1GVCLlfYBobROPzw8R+B4NuiK1fBFkvMIOjgIVIBf1gNKCCmTvH5R7LJJf7eRf6HVyRXBQXheQ1DolWbybzNTzmWn+5pyGCeiM1zwKU2iO6OncsJEr0EVlEvAhSkhHj3XCzh9Z4KPwa1qmltlC/No5uGc3YMSzvZcuLkhX05JaT2Se1QZ+e5iqDIl6zn5sJcFqNEhELwKCxiMiGVRgNxlhHL1RvjnglJWe2FGbOg6G4Y2r/XrJ23r1yyoHgLUFyembuFICAM8Do2PsBVQG4F1sAx6RRCvZTTpqvlgGEIlWYGtjqp7iEJNdeAvidXgVmwVWYQXBWbLZdrdLZ8s4ZvxpLxuFIvXJFHVaGvF443NKS1K7FmbF4ZQBFpq2CgxD3eGu+hLb+r7cyqGKLgkorrRaU8HskuirA5hPH6UKkfI6a7fA+5KDq7uyYS6xaW9k76Cw4Ln+r9vqR38u7QxNXOgqt4bJiqGcqG8/O3bo6M7ML54wl25IT4EAeHhaEwnWbj4rbkeUCRgQH/AA9Bb0Bhfn124NhR0Phbb40Dica5ry9aKFw8BrqBYSvUJo5YJ+Mo1cIyNbtVrJMIuQah0WIUfLVUyreQsDVwuXxb8BEuRAsx2wxCUCumqwEF/wGKkVfiCOHFhz19V2Qml2bhcZsyC6mHws6TAxe7MQ5vqjhZTIwN8w+unrxbd5UWtMl5VdKm+sQCXiUJmsHOWLvshgo/u7y85D0UZu4rLVR36wBHElT3GJuxX353/sv3J8d+/nlioiZPqWSz4Q2V51HI/ALAPECoQYb6Q6hP+ORz6XGr0jA7HwoybYvuDF46atH4kcuj8/OXvvpbgKJWplOBRZQJNVKORC7msHVlCjWbY+I/NAWp7SOr0Ngi6SCdeARCBag0ar5FmhHPPDzF35/ouyI/AlDwNmVCYdyBcibQ1cmGBs1iOBBhTfABq2j4lHb2ApnnUdwNKHBE2JZD1SepAWLFO/N6pV4v1ai5+hnQyODska2jZ3755f7PP//23Xffff/9C0out1mpb+bquXqNZuYJixCD5ZCzAognn36eEUf4FtPd3d1X39Kzd+cjfQIkHqIY7TLeqrpw3cIBjOLoDVuxRCd5YBEcsVwilBqEQETIfbh6auSPQqacb0bBr1VJFXQJG0BIDBKDkq/RPEJRycCFutizvIMTN5TS8FDHxlOZxIDzAYueCKjMfoVBWF1p759dnXh3rCaD52yX63fSjIJkbc0440xJqi7NsptXCrh6qVSjkU5dvnyZa5bSZtE6G8K33wZCeznwbT1ipVTCD6EXAED1PDMJBGL5k1DQisKm+GN8Uj8MjsmshnHXenAVUP1n4B8WHR+ZmZtniq4jDBYdvcJXqXQy8AiOQi7gmOSyMjUXESkzPL6GoisIg158rswgo8M6KjHI1BI1G5yIz7e1oNjFwIW5hvmkBouaSqNxdtDKdHOwXuO6eNnyZaH27nvIZGcr9/DEybtjGdHU1QySd3W/GYWDc8l5CjlVW0AjzyPTl4KGp0APUDi7wZmPn376/vzmPZT3oBdhapaYpEhcafO6de+C7iHNXYON+bVrny9a4hawCNMQkU3YfSZhN/bNN1M807xgs/gIxfjU5ZGZrq8tGBANhALWUrkCTIEjk5s4ArlCoVMiIgrdYyjg6x8yqAyweRdLVWqVTmxBYZFmNM8OH7DcZmm4T6JfKZVHMU+VBqxxW/7kMnRsxcUlcIGVv4+ovFybxmLm21V6V3cXB+H8eSEU3vmoKG9RKaDgog+cK9UMazTDww9Q7HFb/N3Pv/32HejMYmGZsc1YpoLv07kWARO9Xq+R6hvhb55ru4d1hlwLk7rtcPGFRGju9vT39AxpEzM3WVC8dfToVk2XsXHuEhjFna/uWHTsAQqNgSPhmzhsuUKmU5ax5cZG+b9QIBbogqRjq+G/Z6vZOrZcqebyNfp/oHDPpeVh3yZYO77gkM7AnnnKZeVTrzm5Ll7k5LSQbA9FLGtCtmhyUssilk4yKr20PcVB+I28kDgi1G7OisxWYXk2jR6Ja9ZM4CLXN/dAbfL+S/dfcioTIqm49L/1gElzV1ej8eW2a8vRbC/mo5KPPtoIegdK79CIuPnN7PVj48dBs0f5XP3I8Mj1Tz57KEBxQy428RuNw7rGZltll4bfrJwabh7G6DUIwL8SzUe2IWcbuHSpQF0LXwKDUv63VejfXeZE9Qg++3xU6BpoCEatL0xgZBF52LA1a5YEWAeSCekONinhyZPlIioxO/uFW+8klQb7Q/fXAZ/rgI1n5gdF281zQRoAoQGr0FuwKBc/ufyH+/fv/4K0TmiWmE1HQlfzHSJjksga7x18ue1d6LA7YfZ/2HsiueH2vnzzKvLJ+NYrBwaPjIzc/Oab41uHR2elw5AfIwxwQTBu8KUajEYzhdEMD/B1tgNyOYya8OGP/0k04WV561RSQS1bIkVBU8Vly8EwbC0oNOnplLwkj5vGjaGh1vGUuOyU9NzyFu1VZ8dAfHRvSW5E3MaPD3okdnQkRlNvqWqVsnf9n39u+ct4axwhJMw6OyKHRQEUIP1o1wwslqN6LjKPmcAfXjv/888/f//9919+GVgGamtrRg7yNw02vNhdRplx/tbb1159+WXHUMzOfe1nczs+dPjOvHh81VB/e+vlkQEMRKAjfPb8McPw9a+Ag0UIha1OjtFB6gQlbh3dYCtly3W1dL4ARgseA/GY1AquGG3GFAaZRsIVAAo5/wGKF+wI7rTX5z+3dnRcY42NI+HiPg0qPlnsZ4XF7yv9PD41XdVo3JTYsSWxPWNkeHBGJRaYXry2jmRt1zinUrXBEbNT6yxOMdqsN8HwE0AB61Da/7KwpLNzor29faLdGYFoK1M2Q27xuACF0fjOrnfufeQ69+57mPp9505EMm/OfXyyfuf+Y6eRZRyYmp6evnJlnE+fmR2+fHTqKLD4GwVUXwQSqVwi0XHLxHJVmVQtVMnFZdLHcu7H46ZUIZWYYBeiMMDNP1Go9Z8/84xLhtu9j5c/+SyMnz+dwIoq2JadFI5Px9pH+be+sGlX4705Uf6WLeU10cEjR+cau7ranjmVnlOQK+46qJJ1Nb74/OdgCQAA3B5KqhI2V4NQHHzNGt/ZNFHTXpMTvdYSK37/84/fBRbBqJRFjTBEd+ujW/d2bYx92x9TnfrphVsmwGac+6LvRh+gaLnBv3LjrbfeOgYOMjIzP3j52AMU4CR33sIIIItAS0eZ4WGKJVToBByB7WMGYb6R6/jons9WSGXQH0F2gTYhECseotC8ujI43IfgiA0NWRC2MmBdek5UFCHECgsdYIeQkNbg4BfS1zhVF1cUbKmpwaXMrSNbWy9xrdz+cQ7lYPrBa233yp5/9oUZIGF6MAQmVnL1euDydkBoyvZzGe3RNTXRuDYh0mu///6G+J8yNc/f+uadewdjD9575WBC2NuYnbOjo8a2RkAha7t1/A6Yxp0p9QBo+ooOkiLTzFeIg0UIxQBdphaYcyu6GjFoFctlQoMULONhjmmQPyABw0lqMwqThAsplhgKNwouQqF5gEKu0dyi7PBYlWKHXeBOWRjouHxNnIPDkmXLLLnPshej0h2sQ59OzdxRsCOLZr9o3RNRq4jRdeXkECI57/2ZkATyEtd5vYCNLF8lkUjEcMdFEny8eE1cRlM7C1hQSS+j7OHUvbl/SiWev7lr1yuvxGbHer53Ly8Kxt4xJ2Zvgsc0NjebFEbj6PSB8a0QdtQGkO3AsGZ0ZOT67dsWDihqAgr4XLlgDiZOLTCQSjkyHRiJQVFmzrfR3CJEEBAyCinXYEm8TWwFXSZQqVDNAqKLwSAHDnIdhPyPcAwr5zgcEU+AzkVQDoXBWOmGSvHroBpv55jgTN5O5kH9pXODX9Zmf+iIxSatb2gJonjmBL2/JpTkzyr+YmR0dAS2WfOwRRgFjSCN3gpwtCJ6tedSAQWLYGXlDBUaaLCmJCQ4rFmbkL7m47mPdoFSmeFwAHJpXFxUgrM95uLNm42NRj2o2Wjs0mu+ujR4aUqjUav5V2BOeVg5M4sgIAwgyLGm1TK2BrJuhEPVWgubdCmdIzGIgYw5VsDzqx+FS6kOGQW3ja5qRDBMYpVAwuXKpRodGIQUXqOwUfT39/On2AVV5ERTidRi2Aj0d9dVW46c1kE1r76vvzQra8uWDphmBZkzYviZ/m6k/r9l/la9Wd3dIpclIf4+h0qIgIJK6s3lhVOTVpTuS92YHmKTHpfuGBoVvxEq/AUx28OzV+X5hK/NW0vGXBi52dXVNQoLc3OjsVGvHNp74tgNqNpAtIB9OoSLr69fvwMp1jEkyLyuyFViyLzplm2pBApatVDJodOFMoPt3w5iEaTX5qW0kd4mEBu5Cq7EJBbTpTq9WqfjsgUCU/PXhes3VWV6WblHQw84msRj0TYlVhd7Z3sSoBlBoGatSE3d1L/3U0+Wb3SNX+nhTdC1LS48XOSdmnrW2zuLFpyU/WHqWWYqnN8jxYYnfYi+7et71tfb08UpNA5GFiNZ6OBg7/mXrOygdu25GgcDaTDajg5ZV8aGZ2ypyfXwWr0aTkd7kkiYvdcH5xqROSiNYBXNcz07b995C+koYrF1anTXr79+ARCQxkFXbAUytdi8S2dDQU/K5sgMtRyVQSUU6x4k3I9n3dB0V4qNYBICI4oYAoFajVAoYV1kN7/nuCYsLCTBP6MgiAUDY3BYOh7H3LbUCsbNodoGv3pcXuzhE5FwnnAChrMrKgpgcgBNUARFw+morE44I+Lnl0WLppKILCKM0pCIJB6vkheJx9kvWhOfgt/N4BFZ0eGszWfun8ejU2A8XiRoFRpjN5+BIuwmeXkz4RioT2w4pn52ZLQRXMQIl3secPKi4RNAYRE8uI47+uuvfuNIx8dRuRON23DpHAWUeVVgEmJojwmgAC6glwlrUQfknxDMF2lbF73NJGgz13khbpogRAAKvRI09+zTTz+93CHKp6Ag197xjUAXOHAHc4XmQsIy0Lp1rm7xS/fFvxofHBmJtSNRa6jE1TBCSsKnxDF4aTlZBRURMDYQsQVN00N1H3q9qR6gDCLOaYlDQlyCM6GSxGIRv3zp/u5eFigtDV2QqHALZ31IX1YMjcFhwYxYT0x40kyzyYxi7h3fvtQ3szd89tkjFMePTI2M/Pbrb8cPAAWLrgxAFDSUcehcYRmYhEJa2yqTijkKuoAjFMgfNwiAwTfAdI7KyJU0stvYYAhGSCugtmDQNXfdexliY0DAMre4VdsqarABiwMW26x79hno88OGAPQsujz5pOvapBCHkLS4ONflMIO6yM3JaYkbDEzACSgblwBHFzdHRzd4u9jYwBiWtTUswfHxCZ6+zN3LbEKw2DcT4NADi1hyP/B+LxUExkOEN2AIDwcmGUEdnTWnh8onN/ivxv1FxpWHNV2HcXa0+7655uQag22MDWKAjNEQNmnA2AQhBsTEclERHpCKBbXUiCQQNiQhCMkiEsmiyHy0xySzww6zyy477FK7ns4/+vxGdjy9j0wf//CBj+/7ft/j834iXnjxm3ff/Wbfzp37ho/c+9gaf+rEl99ehgLPSc8bz47//vvFxx9//M5F+xAj7+v62+5v62+ES1x35UYsx67F3rAReFy9sef/SIys2ziya+PIujY8pZmNewuXbUXCyIzf9ekXvuR2K8vxnDYpNb2eI+STzOLSmo8/zsuDU8AdlPCLjAIMV2Q0u8kNKAoID2pXSh+UgZDYvUdZgElD3t9GjGHCVgBzp6ZWiHkyHUUzSaIzRALj9w9OfmUQ4JYlPz986BTVEoWrFtB1y2dfr6zr27HCmuSK+GI++5PBzpMfDPeKX7333uWjkZuefOYyFJ89cufWY3e++MfvG+78x96K6L8uE3ug+EK4BL7we3N85tVXtsW3Xdm8cet/oLi+J35kVyERIfsyicyJFePerff1zn337jcn5wc7KLyC0vYZQdKam+OYlAS+kN189e4AhU3t7W1PnjPnq8aeOsUyK6VdKgGjSiRiskvZ0AChyNgOB+4+iGvR8Cgq7EQ1YSv9y3wuV5K+gK2hCoU6poSR7/9eOOknqMpRLcYWGp4OcH8XjxxooRBulWJjbR20CPf8axMnh+asD5yfeenee8deiTzz6MvImGHDa4rdzW3BP/4NxYfgYaGzCrvEtXCNttVtV2LTAyzAJrl2a0TP9ZeB6FldGN+/rHBkZzhCFp8RlO3vjH302j0CH6XIos9w8OzWcu+maNHxioBlzdqF6pUCyqWYbY9tPvfYAGOwc/0428yY/D562piSlRVZIfGU5yc+RFWW3vU9yERZtfSEEjHPJqo3yt3eoALEVypVr2eDkyHVaKiQacBxlBpdvlzxOlc42VVBq4gifn4aAYPLhdwJknvTZH5ODC5dkmz0iEDvbz9ph8zDP2XPvHTR884rZTsQF/94xdPHzh47exYInP0HCsxtMwvhFvjCI3o1NoFXNsaDhdSYiTXgVY2NG+NX9/evBjDNbfFbV4/sXnftyK69I5nAYaRw7+qzg0NMzSWPWF9rUiavEsuNAu8SD2OWIHbc+9TmgXLNaMMWYrz4fJN9rNOr1h0e0Dy0obY+a/SBVsPK9Fu316Zf+r70odHZ1ycO3fFaKMNR4dUfPfIQa9Cr+2Stga3voGZwnXK1mCAciM3qDDHPrHdywT8i2Ugkq5ap1SLzsCjIKiyyyaRlO8tSy9ake3Ro0odP8lnWn6QnlUpf64W+oKco9tEPH/kMMBBQhO/Azj4LIP4TIT1tbXAL4ovIEM2Zmc1hLOAcPRHLribYRzCwvHeuWxbfv7eQIKAQEbILYOzteVHLKtEwmWaezSLOW+XghqY7lng4tgvVJ6YiQcEisf2pK7c9teXhbXbLWI6VRzm9tnvDV10fTc8OpMdiFA2QZr9Svlqvqv3654lt+52OtAXfwMNft5uVjvNbXhEf3BJiUm0Hucg2SLKYQrQXINcij2AUhDE3bPGDx5NSTBarzrZkSXbZWIwLXMGI3g9m5gi6ydwHc73z2Sf8HdZHrroTJVbYPgTD5vr+Y2/8jcTjjzwOT4FbFIJUkQnGzbXAA+mi+crrMjPBM8KZzMa2EbSCzSM7963DFmhjf/+1zfvW7d4Zv3sn0sXGZccGOU10G/jmQqeTnYGT8FC5NzqOIUwmrsRX5dWUJreLxcS3PZNtObCC0m7m6X4gkt6Pr5NMHDD9Dz9xr3/a/GqKyDjx86tHjjiTYxf0jz38tRkCOocJKJ4ItWiaPr/4q8OB4SCSL26hCO4ZeEnmDHMvbL63d+65Xm2gA+dkptyi3CjXYLWVymZTI7KJbEJ8LAr8hJVs3r/zLXSVsKveuKJnazymkgQKgIH4decjcAtkC2QIeAIKiusAA7AAfSBzK1gEjSidwnuHvY3N8IvV/W071zXuXte2e13jTjjF57FRnKYmOj0UCpFsfBKJ1DXdcSZLIcM+F3zUEqoQ8Y4rqAwlW+AuXkoRo7LonvdRmBqNcI9yValDySbzRym3fpQfUgx5+vpIQlou/+4jhyjQADi8ZVR84omhao398/ePTLz20Blcdsz6wXQNgdk+KcRt0McjzTeh1Rq5K5nVccJrsdhdFcGLg5bs0++4xs9FzMz45oEWbO4v650bhD/gEAq949NvPd0fn1kY/zcQ+PU43GI1wSgiMibCA38gOKrAov+K6wnuJkKjGY6BANm1rnDj6v7M3XhQF9PFsmMXY6MS6PQmkI1toZBNHpL7Rysemg05UQgyjDBRl1PM05PyBZ6Ya1Kis0Sg6BoYx+laila1AWchcYZakszZTa81qGi0WlUtJ8QvcZYwnU6hJrnj7lrliQGFim0ffG/LZXs+bPsJO/K1kmAafzA/t4oqZ011DOamXbhQHcwdW3/43FD16QidmV6dtGTK4ai5vZf10Puj1UrP+xceefSzp68gmkcsNzGfjl/2ODbrhD1OGLLF9Zk3ZRZiXhiP8MgMwwBezUaCZbN3J+LjrwBZt/e6kb2rt4az5r5ClJzIFF6OPcluC9HpEsAgD9FDBBQbhpz5Rj/O/oz+rtBkspLCEZRfk1qLi5wEBZ2uwAuwB4lPLqGT+E7Cex4E+6qbK+NLSOAqykq44LZynT9Rx5nJvoAvX+po96qKCHGVmMScTcWtOCpbHyaZfc3tcM6dtPOZXBm9qbOueCL2VHpnXedhQNF04c0IMtuWO3RK1w6qM9tWNPzKKfEra54lmo+renoABQZ7PZjRnr3z2WcIVhrAWHSLHnAFUFPh9YBLAJCr8Xwse3p1801tIPMW4pwQAdJ2U/PuXcsyVwOfESJdZF7bf+wgaD0LZTk55RlkSq2fxOJ3Dxlc0XF+p9+Ik0Y/g5MQkjnaZRyDZ2lcvkokkuOQBaefcHFwNCVyJhNAyEDXnyzBz8PkdzO7mRDhwd9AlchbJHOwyfpxjbi9Xc3GYRSXAjYJhcnsRoMuAaQSit0p1DeVOEOUkPydu4/uX/n8jedXVp5ff/5cqOzNCOIx9vmkZrNaqha38zhe3tBjz778+cw+jJCRLADFsduWZeJBfQ/01TASi1j0I0kSNRVgwI3DtXhKV4OvivMgIjxugiMSAfLpusJGPKiFyBm78aYu6/kCQxIybTxoeCVjnrW9tUKoUuVnu/qu4WgvdoAcRyx25RoHNls0aGdwAE63XC4HU1cCk8sJnwAQYGxrQG2WwTOcAEEjJAvZaj1lcmHL63kXHyMZSF3Wgy731Ml5nw7vpm6YzueC2C0GmVGnaf++Xc/jlWZkcPGaWt12k6XJ4nIFB7WDZREYuavNZmQLHj55ZtI42Zb7+LMrWr7Yt2sdiHZYOd12X3xh4W1v3Akc/razeCoIMib8gvhoy8SP3P90fyHGdoiPxQDBP7B3N/wCG5AwEnsLER50uo1FZ/pC12Tw+UsaKjSqlKWujsgs+6V7N3uia/3dezRdIZ9u6JJRUBvLkYCfi0xQQia++Fwqw09mUatGnXLKLIPs3/5z68ou3nNxccNP/ORoJzlln2w5Xnphi80gnx58B0mCIBGtrGvYtGR7X2qFxFt5Y8OskPTQD70mKt+Mqn3R8OLy1FI2crUuQi01S+cBBPImu+JidVpwENopHSty5z/+7t2w7SL2JmhR7vvm7OOPX7wMxdNXIF2EsQi7BLwj/qorGq8Ox8cuGNpOvCC3fLxvXeG1hf3LkDj37t16rKPJpqVqnbyM7g0831BKer6eKWRy7JFpTdrKtW7v2OzrXP5rh94ZeODeCc9orCIkYUvNAdFXnNdUElADdCdcOhL1QGvXqH1iaddo6y9f79DSexME+pVCNRWCFEef6i4te0yWDyjOPfGvpImE+dKZYMP+/WfY9EPfm6369WD1nTmwfCw99sxQE1NPNYuJlzyC6mNTfdLeOfFcr44g29yJHSzWIJ9jOgbDlOybRTRg971w9vNFO3v2jTeuuL7wprbMtitRZBbiDcnsAb8mXF4134Q5MhEgBBC7Gkd2xvfH795FIPGF3UbRzfNxrhWa5vG6o9PzuXw9K8nuGa1ldqVUBRmYsRx/qP6TpwBFrCBOwZdQfO4WxrT/UBadTpLzmUwyLTg+7h91vZrC6ZL9sEJCJ7fIRawWGcXWVELNTZToLS5zCzXpxDnsfv/zhHxNO7B//89SxaFf53Tso2A4bj995PD+5fsnzndWnq9789vJhXcisH9AsjDPtX/QPvPsyygw34KFP1B0PgnDu3rV1v7Vy8IbpXcxO1y0N676C4urr7xpI2g1mRHXFxaiuEZ8XA6Qd9ft2jlC+AXcYu++244dtJisU1aWG325X5Dh6I7elC9r0g1H7lgeV0WXsQ2VN8yGSiQtHdWA4jWBwKNAJOjT+IrprokNdK7QLKTqxai/xAnTwVdTukSnDkDWJDcYTF3aQibbEpyi/CgZWaeX8ZnOg+eAwWUowmC86m14fv8omzGxp91H1WkpFdGA4sjaIxPriyvPV759bvKT0xFi9P+wuQ/a5y5DgR3Ih/jABuBJYoQHpmYPCCQEFhjChcF4EbPVY8ACnMzCjc33E93HbU+jJbmubS8i43KA4FkFEBiZAgrCJ4z0Jgy0fXqpWDZrFLeTlm4XlVDNPtelh/KNphZV3NqbZ/nCmKNJC089sNxlmPYkMEl6O6qwaf+rG+gouqQlWIVipUyfzn01RcFVS306rclOy60ukvNtXcfHNywXqfJNTJZ80pWamBOmJG+GXMN+wiYYa/fvvyQ1+sViqTDPwafTTm8DFNsm1q85ffTcuXOT75yOkLL1FN2MHlQcn+zCwjvvPfvInSCkQYgC/Lwnn3nmy2defuQsHpH7YETi2Pfpp/Mzi6PlYyg8ljXCJRqxm0V1tXHk6ivD8dHYuPs6OMcttxOOsXOEKLT6tx77PMkosdD5Vp0cE5dZjrI3tKOvliIV872uxCJWy+nTRw+vnXayEwdyF1ba2bZsQ1wCnxTILq0RzgKK1x/kCjUZyaVEJy6frp6oT3CGGEaUXyJaAl9r45teJzHdjLjoxMQDYwdyUlQcOszuJuRaBkGbjo5qSlx59+u8aSF4iXySnyRhHH1pYBugOLoEslvfntMACq8rNjqx2tuUMk4mxxatAWkrp2Hl+aOVlefOb9tCDJcPn39nYXyww25xu61aKuKJzWafXBy0v3HV9f3X3gQOSSP2hJkbC9GEN18d1hzD7dEHn/4VIHCPZT1vXMyiGeVNxIiJ75ZKZy1K9fHI6JYSN4WSVBadrxsEFOfHZkv2HBgYt1KkMolL4JHQJWX6W2o0s8aJuNeVNY48sCC0iiQvXZA6UU9PDh88QbdTw7SZ+KbjTieZq6fw5XyJTc5k4c0pYdKz7oCgXnH90jiPCuuFpBKp15kh1uvYUMCLPfrS4ZfWbju0NqYSJw3v/PrJuYgVdduef+yTivL1QZOtvBo8vxhAgVP6urrNAyBo4SJiW3FkbpLFbUIjR9bh2ZHCMuZPDn9BJIyrejKvuwlT7IitzcTB7V5UV2FDmCBAAMSuXXh8el64sDSL4bfRyUyQwdhS6qxNyT7uWWGU+cQ2V1q0quTim+cPD5w2ljx4dKBMSk1Q2OxRHomCHpSa9aSvsieqEnyOUkcGDoZpwU+GaiMP1ScIxxdw2e/jtaPvMvGbNBk1pQ5HuxTA0JsIyQVGAj2B05KWGpNT3FmHehOU+BvvuGepCtqKx0lRqYaYQ2tf7dy8qS6moRgSUz9VL0Qkbnv+icfKXBVFAfz/5EYvKYteUrxyM97jzrsHHoZtGTgckwY+n8WrUNisLJ8UVYieYnSFKFQCDDwk/XsbMchb1gaHIA6QR3YTU2Psa2//GCXWrn071yE4FjCjTvJbNajnNBqNjDrNV3JDnlgal8q1dUTXF8nGnsJSo9JYwi1KPGXlmdlWiytLwZDopTpyt19wqIrO4mH+wFXofSxKQm0ZoMgow9XKWM6pMhUHdO2mEqm4dFUNoft1OzpRyBGY2bhUbBriMDDkxrQGv6LSUlO2N9yIC4Bbb524+aPRfDq6QYvJFRV0ufbkFkXoWKjIZnxmthkVJ9tevSO1wpUG1aSY7a2ERMy2zXWJ0ZGQf6CytBZXktWXpyTPuU5qpdfQKHsyzMNfHEOn0r86op+4Ddq3e4QQPrjrFsQHFnPInGhEtl5/7POqm+9JKUry88nMEhaEXJ2sWaZYJvGkMagystYVXUUTyhnjqQtpHK6QllVkNBNQ2A1dDAmVLVVOdpUf2iAh68RKDPTd1gLSkCjxUD29YHwAqj+AokxF4zObQt1enzS5BYdjhN+qlXmLQ76/1OG4TFITh+OKwnKAgeIehN5aiHFsP0TYzfVVgqFurjDCHowcHAzMnBwensFxdFJ06+nh9lVNDempfTnVwd7SPG6sZfvpeUdBaYbZ2ldGMZcWaOdc81SlGdUIL5nLn/v0PrQqVyzb3YxjQoTH5fgg/AFA3Nbz7LlBQQqgiAUUEN4iDIIcWp4oPzZKgS7BZqiP4zvxly00BkcmVNUvFZkLeG4Cii65UCitmezyvJYlVyRITCZJUpApVgwxcjbV04Xu9WtxA3yq7FQkg8+0HO8O2EJmEcPrRj4yJaHPlTM1avbiBDSZGFosgqJhkW128Jox7qUhfSjoaPKGkH9Vooi1n1TnHK22mFqCw5iiu5bUHQ3M1SSzdaTRhrKy+dI83qx309GZAmUej2faVKbvLU2m9GafpIr5OFQSJwudq275AGsNLEBX7yLCAuFx2Qgcjp29sGVLNaC4eamK4w/deusdN6dcUy6iMbQ6O5mi07G8VViVctj08LdEp1P3dE2PzlL0UpOFY2AoSpLNlBrNEKAgybUssrtlPKFETetKyLk5JST0dY4dWJMIryhr4bPoIbLWbZ1K8roDVnxprVGqLBXdl2RJpEnIErUyA2ze8FlVwWVUeECF1KRIwuqpwohjAHtEjjepHLKVbteJYbbZnAQohudqpKnXrDhUGbNkvkY6VVHdsNalpUip87ZNCzPi0jxNb4VPbw6ZeRlKh9B5+y0fv7vsm8/P4jXp2Xrbfev+8or7bjt2fc8bn39yru7hJ2IFKTfcUKVSDJGNRgUdI8aEJEWCAh0nMW8VpcREUdkE/T6hi8SUZvi/8vm66G43tBE5nG6xVF0gDKX2bSBhw2HtKCpqkcgUXaE12+slvPaxsRtuOLWwsBAZZMroIf7wMN44tymgDQQoAUg8i+IUDjXFKOHTBXqxzs800hLI7AIHdgNAhIeLK6WDIG1iamZm66xNSRHL3V+91hmEomnHlBRQxIShcMbEQOoyJma+xj92rrOzOMZlZU2fcEdXz/Q6jO7e7BktlYyh2yIUt6x6bmrq4MWLBBp/G7r7F74YS8Qh0wNbFqEQJNBZSFNEo9nl9xvzDSpPHCxyqUGjxhSleryFQWaLk5khc6/KQIKTGxQcroOr93FJkQ9lyQMHg4wA7YaxKKqui7Rme4qkYP6GtcGUMkBRFGTJ5Hyy++BBUyDgdg+zAArD5WopytJlUBNkVH2SgTGu4HPktBYGh8+G0k9Sq6fJxO/es4cHXMK8Xgd6kLtPvPbwUxenpiyWk/h/dpW1ng7MlU6m7UhMXVKW1ls6nd7ZkJMYa5+UTnZYd4xTecm17t40HU1XAiQKSvdMEsqnq/Ro7/WW6gsXsd1HA0O0KheDQfvKdCi6AAoDAYVIQWfmQ3QAARG26KqqyBWxUTQbr0DD5e7h4VvimcUFLHaB+kGkVq3FwGAcf1CmhhDijjNZ8uDCAo3sHjtcpWb7yen1S0NschmtY+ya6qLqohMULr+bf6LDCyQODp+cGZ46OeM1CHINHkERh1+i9KlUWQIOI4EmEtE4LS0S/cEjN545cGhlX+eEwUOmt0sdyXCPiM1jr20bGJyiWL06Pq08NbK1cniu9OeB/dsu4kJgvtSz4xKukX/96aefxCzbjlwqT8my9qbqUqd0xDVfacbk7XcBCp0+WVmjVLOiisZP5RzoXH73NmJ+NrQe8hc3AorylBvq40SKBHl9fUoK9r1VWQI/nVSi2ZMhxoqLyPIF8wcHq6u92BGpk1floYDQgo7IUcgfpPjMGn70Kwb5wkJZkdf9yRjZJ/WTi1Oq5DhsCZxamgqp43EvhUsmyTu8bndgOBCYmplyD58M0AyCFpFogyqfoxBlCUSqrCwVdsuQbKZli2hpHk/OpvVQZhrdVLGj9wsphVdzKmLgxsrqqI4ZnWlHekPxmujI4s6pued+3r9/8yABhbRvU/qSFZcuNblMOp0lOlfPE1utvR7WmmEdpuqOGp7mFrydq6x2qtIpLeBGVccc2RIeE7Smr8nhAIqwV8QCig35QwqyURFycgkA/mJZ48VrZ6vNPLV28MLCQooJi1O/SFkqTi7VMptEuCRNosi0ZH7MKwJ5GUxAXyiCpqvRWZxyzXGhmVIWQwAxHjRBC+Y4v8OLlOcmoJiZCnZ04AjOxWihZYkMUHEQtbhoBlGWR4Vtu9EIIUCRILFvfV1r3YrzfYm4qb4w+MXBiIGGO8bdFWmJDcvX4vsvW1HcOTznGN28eTmgqJ7zbyJksSuLUys6KFRLXzXkhq3u3ljrAZa+vQC37WJqGAqbi5KXrOSnleGKG9phxWOEiuISzvr0u1eufOCxWBVUBDz5CQl6JewvCdRS6CToqRn5CUxhBtwTWZ33OoWpzsiX+Myon7XaJoFCQpJQuSio+14x8IOpp04VWejBoJZspEAoi89jW5EzAUVw0ESl2kj8Eye87uHhqZmT8wiRjpYOryngxsugtXrtHOTnfJFBlFYUl2UQ5RuJqWmUYc2ZumJPX0Vix/gFNEADIDaPKYw7cFjV2pCenhMd2dAZ6HVM19UVn0hNjZqbbU3fdOPyylvXVEVp9ZbocXgFOTBHQMFOxk9UI2aHoSBlB/J4SZEHyiIb6sIacEs95dnZgGL98rsfeKwcUKRkGfFUQmheSiX7BdmavGQ+p0LuFNLkJeFyCBt05XFKiTpDrkPRR7KxtE21mFGok2tWCZmJG0ROrlrNNDGCp84X6btkDUuzuqV6SktREQFF0C0U2kKkwcETB+ETw7AAnCNw0E28KSaLlmU12RkiGkoNRougXKWC1JNIADMIdlyavZTtSYs62GGvHIjYvOZAgt+zBD1tMW7R4RVjw72OS3V1azrKPYNzQ3XLcSfckJ7oiVPZLGdyfWYxi/VBrDZHRyWgKM3QOFBskQxV11TdDJ3JyuUNMXa6jU/RY9VP4qwkoLj3sfLppYCChjnrWijTjJ7ZtL1cU5qsHXLZWEKkDF4yQVS8fVWyk6tTZ2j0TBPZamVp7UYJnblHb3U7mTmjIqFSSFJ0yfUZUgkpQd8aJ5AJ1RTFOKKjZfCEleqzSSSDLnRKgbAhcQKTg1PDAfyB+McsSS2IDAAiiMqHRwigAmTAb6qs2B2zK/p29J2ZLq+I2LwdUJRDE4+QEF4SWdYKrwAUrZEBY2xab+jnTa2bPnpoe/QGFc1qih0Eu+E5Xm+2e4U3KSHfk3IHEiQGAkdwhrjyxvTWhh00G0WtRt1LFHmyoaOA4iVAoVp6A6CQSLqWt6ZM12KRI3LmKXVDSRKWhs/nKh2lq1ZBZ9mxRypUq9k6uZVMDkORQHcKtXZGt3b7hlqm1SfOK1UKnSQ5N4HaGlercQ2OtwQhCuY60WGlUrFkog26Ooj2OTA1NcOemZnRTU2xAgG4BeIElVQSyssW5FKDIUxKQhY1EMk01YDLVBzlRo/+2dSVhjZShuFmZsgxMzkmx0x6JE1DmjRNJu0mgaQtSRPC1hyk2yZxmyZpdyGkYIqYLhZitdCtbG2tiAiuLVI8Vioi1XVRlEXEAxVRvEDEf6J/REHEA8VfPt90PV6ysRTbJk/e773f5+s5645l2nP5fkKhOtifH1y+fCWeE46Dqeulqauv/f7brydf5Ut8mD+0IrthQ1bThXvOfvzAWxC8/R8/fePz50YXKgG4xYm5Fs1g9AOMbFHnSNdn4Q7Pnrl9mUARwwEBFNL2nRfdjcaTe3thOw7K4RLoACkCRU1hZs+hQHXkY1IqFR40m23YPUWhrA3vBzeenKbU5s7jWIrkRqo5vnlpvYEBHYs+ogYx6eZSEOWcA3A4sKSSQHgT5et4fHT9hReuQE+ypk5E3QetwJgJmEIxZgGrOWZyrVdi+M54zD0IctS9jY2ekuyjvWP5vbyjlM2kHMPLTz8R757ce/tt88D4pu34mPQGUAcm8lH5mYGV297YfRQMixeGL/TXTxrtQ02GQm8CvXzOh0A85Md7NIa4kJFl/4HCUUEYEdNqpO17VmLb2zcqFa8G5AKtCFXQU5Q/PXsKxeyIs3t0pPPyybIqRdPZabunICS1aordfXLaCFYzUBJ/9vKXbz+ubV5yTx9ZjqXttqQ3RNkgzQgH+8ZgcmQpkgISNANupRAN5bj+0QupK1eWtGoMTPNqKMNcLAZrARoAgAKVUDQD//VQ9kO+Z0wdaUbA/prKOvr7A1N9c1dhg5/IBF+AIf7oOh44cKBR3yxNLYJsmgyBVQBA+4cffmjc+GEbnDMFPagrjQzI+Xy2ml/0izosskpWtbd8eMfG7csPAAo3tGLcYwUUu67twxsnc46MIbEmoabvr4nXRv6BgouPCIIq4GgZJYqmW1pAcS3i4XXsrmuaSmYx1diZfubcZbO2eed6+MgyUtuaNRQRuW4XGSGDNo7eSapLLCtzHErXshwkbhWA3LwSQUC+BAdqVgMAtzs2M0PgMOOwmOBPUA3THOl/79lMyje568jmYG3wD0+RVyBLH36ISVdC6593Vxwwum1wDv1ww7W+/s03n3zz5Dft948RIFqUM46JKg3NSC2V0cbKAkPhfeisdlXwcHnjLKB43QGW7QWzRzJuX1x1hduNG73D84bZNSs6Xcxa9QimglDBVq883swJR6U793hrhqJFAoXot6qkIrs73tYZU2QKNWI3u608oGj7PR6PRIUMuOQlYYNWXINCWriQjiqXaYahWVpmZFlGxEW8ayebROXJru2z85p5V6wy7oqNT3s9SNZ5O2lZRjydz//qSZUBGCkBYlMnkAclKuFexkxOwDExrbVbJdKVRPyCRp3Ez83vH0XhN0r0uetdZHiJXHXWyahCTVry4P6Estw85IPxQ2wBN4PoAfPnT6GYA2H3egMkVO37B03tdsMxuLISrK1pWFoUAEGuRqB4J3n1cS4nCN8+tqf2HFAi9oG3PXq/laIK9O6JlgKXFdRNZ4zwBZ5Zjm3TWi+aZ51ORx0JNpvCPqEmcRoE/7Ujm0gHUzC8MgNS/yV7pHwlwiPe8k5jGd2rdY2XQAeIsEKNriMaz2DIQuUHJEJ/9QzDdWBbm3jS/rwDNPZ8Eiv86DX6Q6Jos4xYWvLuBxhySByHMnslxoAQszYfCjQ9dvVELwhOv/rkpN1iGDkezTRHajcatOHJGyrGZklzofb5M2fPK1qxsG5Wa6zG7VXQqLdN5E8Ov7N2KIt6AYWeGoGiqo65Y7bEEfftpb25MK8KySrttl1fQO9DT++ObxsplqUhbNDob4XudNmLHVgPqDgISQNWgdMJBuXXJMDtZERjXVKPj8eILpSTVyIUXS7jgGHgCFnINGDogCEPvINNlNTQdiwajTqA0XMu75gqzWeTqSCoDNEuO4hgaONgrz8fSW5OvLa2UxsLLn/7iEXOCYYMAm9cq+KdnQ/NMD9+/Olzf/z5wy9RMhmX5lBtLdpytYMMF7caRZvB6ZPl8Pkzy5ceePgxXD7gRvvfSnmG3IAiv0KY/JfWDhkwru9AZnHM4phAjsUBxdXFsZOTlpFhUoDCT6Gz6RcBBZYpoe+iTLOS3gooPPt9HXVnbGYmNjr5kIbjdKIxHrfoBQvcUbWWFo73rVb7uHsVVB4x7dIVrBRZs0soHIWxjK5BRcBIY9ZRIPVFRk9TKmCh6wmG0BtDp8VHo8g35zi3/F0Z6djte6YMW/a+tvPuzgR96QNAgeupMo8uspaRn4WEN6T2PKnxRWGqfIIcjdoSAuBI+7iqrYkOU1No+gx+imlc3jh/6a2HH8OlElALu6TzfONoH4aHCa3QZHfnkKP1PlL+qwEK7hnXzIxh9shW3uTDh/tsk8lgoveaii7q/DKgkCSwK4NjQaYlvUq+5PJIixOdvr7xmcDw05c9Ntu+6AkxTegDqxM5C4lUZkeifp2EsU3Mue5ecHuzSYDDo1ACo8yKMqb3IAy0AoaezGlRPVS52d2qdp2ZuYGVM7uB/NnLqddqf6w8sVXLcakudG5efvaD15zi7KyFzk+xltkRi3Co1gpGyWogo6MqVIfSs+mEIer3N6so0IlEmvF9GlCcuQQoHpiLueuuabtV5xmd41sVQDE5HFnbObSJTFQ5IIDCMvNMx9udtdnKqRRyVplrZmAXr0k6o1GQd012jTcl43eDmNSoN9L3uDTWsb6JMVeg97On337bbrPpimZ3R0t4KUAkxxaLrGBAzA5A0ja9Crx5E3sXNs5cMMNIRqwsy8D1C0T8jB+t+X0NLKimZ57l4luGVPSgsovR4MXSmc/Y12pRX5oUvaC51WqL2/jptbRQ2+rKE5uMM2GGN3aF7eobCx3YJdGpL0DHOL8nfnTNVhOhaAW8YLTk9aEbFxHFEShc63W3mbfq7EPmw/YAYcTvzW3tHMb1jGVnZytR21rbSifnI2yuFo1q7dDTptjkrF94NMI+5isAhdnDm8NlmFkYJcofZJ8dt0bmO6axmXMDL7/95othm09XrL+8gMK+x0oVi3RBBGwY94Wtg37g7gufn5I0Gr7y1e6jF+paqehnbmHBhEQQWUiHPM/3bEYTlpHjMX1rbwMMwYver3766B1gmZt9J/HOOwnYgWD80We6ue7WSJc2eWXDyProEKILQln3OJkoNlwTDAmUDjlYLItFgBUC5D6nz6gPmW7buO0UClAMT4PScdvdsOcHhiHyLLTCIgOKNUOiCii6MqYIkelGkUayNk7PcQilNUKhpdUKoV2zRjs0YIbhIPV5gQ6+YZKkzXlccwalePOxV8O+eEH32flVtwseEl1mVtQTCcl60vQDg+DpNpbz6DiDXKX9yRtvPFefVhVp5X8Ti/AiVo/d3pPDpHWaa7X6wGPQ18rO7/10852tWreL4b6RnMWWeNcwciNLVM0Slyfm2abTVQcUowia1DicHGQEl00c2TCqECUEXchAALgNZ1A8OYPpn7eeuhdQxMbDHqmodZvHSN46OL+VWNvxdP2CZcd5lCDBSbd5M+LN1ZwGeDadyPg5G6DwHBX4tp0L7TasldsvmoMpTYrSBQV9+XOz1JpXk7WHgTvefPGpdjyu23/57aeHYuNqnBH8CoQVeAGiDO2gMaLBgsreoBR4iYLANEiHJyCWenRdu8/qikaVUcIZ6gkZsIXcpDElnkkym/PevcWbs1sJNJV9sCkIulHC9OvjPg6pZnM+SQctdLCkXl8YclE2BoLq/Oxs1RcX0rkRIR63CZwigggClbmVjTMKFDFAAa1g2/VxsJfj8U41sfaup8twlqr+FIp082Y5mQAU8GRyCKbc1kId+Ejk7UYChTRw7xk+MmGyR1QUxyQ/N6uSGCp7BqbiFhTF/afvvjzgDkzP28m5h2rQIkDQ/U8KLHMURe4HyRkYXQYzcY2vPt/4vB7eF4sY6+qZCAapFu8NO/of7Wcx7nvms4/eqSXQWyKOJtSMK5gIApTewPFJGsMRghSOdSTQmfmRjIP8IbG1NpvDLHqaQICfAmgjNeho4cnJjZVLLwEKnI8YPEih7c7jtsKBc+UdS2Ln3VZOsHV94lGiRqDgbj4h16ppA0KjEIGCAxRWH6JNRmA+D78/ee+QZz5AqjkpTsg+19ACimdmcKnf5e9ffxXV92Jx8vxKb2UGfQVC1U8Y9tCTRLSAsQzsS1EQHUWkyPgs0I8EqSLaRFUkkzlECdRDoSWUL3nN9b25k0r/xgDrb/UNOmQyBxuHmrNsKA6COSXpRizHHeOOBMxOmTpLyRRZoddPT/OSP+rsEpLw9IhTEJpAAhpk2KnhGLKV4Y2V5fcevncOWyzuhkYlat3KjTuL1SqX2NnRJDBKee1alEBRTdviL4Sq1RFLMAgoRM4HrZAAhTEUPfI/Om2cvDtgN7nDHnA12LjNT8LDJSRFJDpeefvNV4tRQ0HsnRyaqTjM2IwxK5mF3UqxRtJbOR3jghiBCOyI3x9iuCi5WQkCPGQjSmAF0djT7+iro+hkenLwtuFNZIU62RaH4O3bxhbpbs4ZJQNrEBvnF1njoWTNZpdSMtJQny6GtN+lpRg9y0ILFHXEG2EEZzoq+/10ZXBjkkDhqKOwG7aq9PzQOXIZCLczEp8lUHBoclwznELhe+16s1pNpINgraZR3I1bpfdVvpBGxdkAhW51uS9rMmtUtEzbfN4n1aq+xxdxc9TMB5N3PPb8sYFAMYw0ywUWcqTd7vp6zDWtCRo9XtIB8wAQBQ5AQRZJ0LaFrun1AgBROBtyUb+K7wk49i6vDOD5trNfb04EFkvZTcxVBmUo+nFJbiLSpYJEUi21GUpg1WRbGeLu4CdU61jamXa57CxzTBmJHu7rCPA6JsrRoFqqDKwMA4oHKwSKjlUlq8+dw5VUm2s7aUNtZ8c6a4uOcIJllhjlbrzLxGuAQg62I6kUoEhKkjGKA6KLMs+Fi6O7WAvKqlARafqipZPpwtIU7m6ZmghMXn7zxWPBXyia6+iqzMxUZioLQ/0orC9UzFZK0ppJ6snzgAPKoagFwUIPemziPegibDSKC+S89EyN3XbHxWFc6JMf82axdVkqLe4N9i5OBfonz6LYO4m4PIvlQ3CiN2D8tIhRkAmzMjIpvWd9fXxa23C7zZIoiCoELxKUsKjPqPVGMt5QvwWFoz60sN6xZvylPG4ZGcPZcDoBhTTrMwAKZ4JA4TR0mxYSAYToFko3sFFJCSj4VUaRQKGzbutUHkpHozruM/SdqAtLfRNIGB7/4Fx+rG2f/mFfv79tivXW8wvu+hDcG7Bwj2uNQQ1SDjTSb2WgMBpF2E89REECzkVH8l2SKzh7IlOTK4EJPom/nymPnVsszU/l+/NjU4HBi3ff++DdFwfyi/OYM1nyeDsuk1qD4YIIRRhzxJC4jUljKCLyqBjP+o8lu9duJx1AsOfi/hZAMbQxvHzfU3fjYxpyQysY9cxiYDG4s/OuJV3d2VHVohZk5f9CwTlR680x9AHFBjlD/ABewICMXzQwz7UL+CgRdwMKVAScUze2xXI2i2wLow7m+oUhkIPBj1+LaGcWhvDHCBBA32W2sxSP1gdaojglChQ6CCsqQiv8xpQRUbcEnNieVjKTOcicSsSL2wg3+wDF1c2JvdsefOz1By4Pn3Pgdk+UG7F5RLJ2hTtIIQ9STQ8N1VFMJ4ezDk4io0YLsfON+gRFwWzq6nuTg4Di9srCaD+gMDIlXN1XqgIKQ+4UCueIH1pBAkKLs3vTWQUUNtS/CixqqBkEGBYBiakl9JxWLBBrJ8PpSSqfc+xneyGzFIksdWZGV4fMGqoQgp8UhPd5rWOISP/pnqFJrUOCO90hpFiAAlgozkSnMKUVdfiKhFf4HhRDEntSKYpICthklqYWJzYjm1OOqaXM5tzw8gMP3L2y5yhFUlCZiB22YhvDGUq6zKI44yIHsh4jnhKGymXWtLzzfbF6fYZE5h4doOgdVqBwEK1AQ1so4Y6iOJB415AAFMaqwZngSHkfRawumrhOQJLwCaKelm3ReASfoNPGLtFOeUjrJ2ZZSUJotA4neE0yqx4bfWh1vY3cCvUqT+PGb8fH+42G2gWbiduzYDYIFBTD8lAKrdejQUqjUdSCIKG8A+hHoQBETmsWxz1BqDusIoyrMZNdDEzNR7yOgXwJunHmwcceu/fSQO8YbEVZFfF4CR86q2NhUWlrBxT6CvTjasUioWdRZDN9qGE+8RHkw8UZr7E3MPwooDgbAxQzaqtOKAUCS1sKFLPVnbVUlayOKUjAi6Ytj8BWbCUMPhHZAXyYZl9XdPrKg2NO9iveryvQorLZk8SQeHZ+bPTp83fFeFCS08GIOoY8YOHkl+Lx9LipgWECPJSiZYfX+RgrIg1YTcVsQtHIsItI40GMp0hyFXyyRVxAxvYElZKITgXJzgcGFkutUgUh+PzU3sV70S682J+H2YyAEdSOXF9DyUhyZKsZ+RVAN7fh5/GNWzme3n8wNPrhC1fKpC+DymC+MjgAKJYrChQSy5UWZ3IEiXfjgGJEByhyIUTvCNUN6D8/Aq1AtTKOKUTk/z5AUUjHy/3hdPATO9OiMK4TQTtx8XHzzCquozw5lDIpSrKbYCWxiDs4FHv/2L9tNjcaDRN5Vo6FpujjdFYcjlvBBU4ErYdqsbRe8aj4mszDGyV+2hTpIVCTublystVXWuzvndicyPcHFFtBoFjZC0wg089kQNPURp7tp1RkEh9+CQJ/QQ6eYov1xKwZ3aN1AEcuGb9yPZh3DPYTKJQla62K5uYD7NopFDCQDFsFzxFCd0SWPkCBNcJTrfDHCd22zaPTiel4pFebTlU8/mQq8uHiBzNjMajDQ+vtA7wro6dvZpQsI6+uDoz2113vC9z72Bfkw+YGGP0VB2otgFAJyW1EkvBS93Uk/IGEoBAIBoFCEWBiKRdCoMDpULDAnItjuBdmc2xvMLCZLOU3HsB9jnCmpVYEsz8a3MWlwZsnZUAemxv4eSSc63Cw+Aw6apxHZEI63j1UV2skzVIkKOt65/r7l1996rzjFIoCp7LrLVvvQuLVqo9jt9AsRbDehE6kAcVrXRyVBIZhfFHUQHwaTPmPRJMBuzPVsEp2bccMFvun76o3NCSMlrSuoQtYyb5rcnV4dHShF0npLwZD0e7V2u0w3si5t/F6JQpeB+btn1CzKEKgDoKSI0AfMPUS7piVHe5MjwwkAEUZ/gN5qcMBfl5Hfs6bnHcMPggoYDbnW+gusCyiWD6Mv08+hVVMB0CUdL1ObBQwQeipVh1MB3rHZ7xZTRJxUv4UisuOhYW6i1dhcCeEilEXUNiqVZahq7jURqbJzV8I60dQSlO0wsbEo+QCcys8cjp+pS+bStrhyAHDywuNFrQB6mB2j64+BMFLGRxV3BiiP/y2ayoNvDnxY8REqkiREzZGRGypiJJKKUBD6ziGBqIaj51UixF9UD0MtOIKmpQEjGQry/PZFq7+5VuHJdNXG2886igdYsfLyjdc7qHRVfDGQ05fAw7oLTDwThHojru0gqgJI996eXLlfMzckdymvf7l55+/iMCb8PXTHCxKUaWRqzVbtabn6K1oLqeso8B1/gNFzsAJ0ShZpLOiiqA1zeCatQCmue9aGOczRth2TTg2BBhuvQaCg8vU8SI7dFZriaifSqI1q9hJxXMaUSWXlWICMIAoKBOmapJHFCiFeJTXeDATByg4rI9A4UhojZCS97Zw0FtWEmkcYOix3W4ju8FhR4HioVE8XYBW3HWX8joUNBQ4oBpuOFWjJV3sG3CcefH7i+/x7QVNYKKXQHHbHKAYb1Eio7Ni+Wl7GwO53Vm6KQMKaAUdJFNDygGpKVBwxFRwrHrxs8uX71p9GSRAD61PtzKwTRHtOIFB+TBWV+HKF9xzpo49Q9ucCJ1R3Y1yYipp9ajVChZKDEGR+jWZNgXcBiLK/Q/IGQnlNUydskDkDYc7wR7uOhu8ospkykCglV1C1YzKHEQyiDJafONk7sQFi4AP3Q13Tf4tKKgADkVOT4rbDb1A3Jm1JHLHlbmzL170BtO2gnGuESBQnJnDj457jLRo3cftDdOucTj+mEkbSfhycdD2X4V8eLP7SDeNsDMXbYbkYPLqB1/jsnQMPTz29kNz/AF5efZp9+oZXPcBXniCw60YymPUR9OYLUvjiWy5hOBtW2Y1gsukRAo4CBnQQ20qSBDBM/kKLVuBE2DoC3ADOCUwMHTPR0Ci3EIU28q2YBSgHEEqo0QZh2GQBitjCOPEOZmIjzKTTnxlAWBAMaAcRDUGlJkiAGVqdnO5KHXpsXwkFI2HgmYUQe58/tUVQFEZt1OiaLfC8G7HRvFG+hegR9PlFBmauXq182HTgD302XScjixdfeaDz16+415yg9btu46SvWXV8A2cirsIDtAIkFgQIObMwAET8MqMncHZ9TnxkccFnXUpAnxR8k0qXSEZbTKyD/bPwi3AwD9k3lEfI4sIvWmgRfCK91zHBhV6RwDi1AtGMqmIciihFbhfok2sMR72LCmcw43CKuEWB2I9Yb7vv+shRTFOwVg3hUZmc/HWGI1mMuOTw4emvYuvvjo5V3FXTGhzhURiozT7J5MXRoEc1BsSe+aq+qpaFYrHb77wytXHnwGf8PnvXwRh9etnH50rkXlXc2wINkqRW+cC0+toMVGMATjknDKVm8UIkC9KKguCLrJUlqwk6INYIwgnghwpwuGIAI8osRc+UnGEgEZEMVXoj+PJAA+CRuESEhskIxBVyggTGsQhS2U0h4ckNkGPQCIpZwonC44EDkgLvwa9UOSu/yxGvTL1hGUk5zy2WWrNUMIX5k29t71637BpHeuh0AeBwoZHUdz3TNcn88TvuEjv0uXqfBjB5wFtmDn39R1v3odt6/seuNQ/19cpmV1wFaBvP9UGBXRYJRPcRYEjpblcOr708pt7tRrmDR+xWDju6Br6zBHq1HnqIAimuMTsCKcUOUNKCwgiNAVOEQACYCDNpuVv3Pv8lVr2OlUAAAAASUVORK5CYII=\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Flow rate\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:hydro-power\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"units\":\"m³/hr\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "liquid", + "fluid", + "flow rate", + "fluid dynamics", + "velocity", + "mass flow", + "volume flow" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_fluid_pressure_chart_card.json b/application/src/main/data/json/system/widget_types/simple_fluid_pressure_chart_card.json new file mode 100644 index 0000000000..53a579daf4 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_fluid_pressure_chart_card.json @@ -0,0 +1,37 @@ +{ + "fqn": "simple_fluid_pressure_chart_card", + "name": "Simple pressure chart card", + "deprecated": false, + "image": "tb-image:c2ltcGxlX3ByZXNzdXJlX2NoYXJ0X2NhcmRfKDEpLnN2Zw==:c2ltcGxlX3ByZXNzdXJlX2NoYXJ0X2NhcmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjExMiIgeT0iMjQiIGZpbGw9IiNmZmYiIHJ4PSI0Ii8+PHJlY3Qgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxMTEiIHg9Ii41IiB5PSIyNC41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxnIGZpbHRlcj0idXJsKCNhKSI+PHJlY3Qgd2lkdGg9IjE4OCIgaGVpZ2h0PSIxMDAiIHg9IjYiIHk9IjMwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii44IiByeD0iMyIvPjwvZz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNMjAuOCA0NS4zaC0yLjZ2LTEuNGgyLjZjLjQgMCAuOCAwIDEtLjIuNC0uMS42LS4zLjctLjZsLjItLjljMC0uMyAwLS42LS4yLS45LS4xLS4yLS4zLS41LS42LS42YTIgMiAwIDAgMC0xLjEtLjNoLTJWNDlIMTdWMzloMy44Yy44IDAgMS40LjIgMiAuNS41LjIgMSAuNiAxLjIgMSAuMy42LjQgMSAuNCAxLjcgMCAuNi0uMSAxLjItLjQgMS43LS4zLjQtLjcuOC0xLjIgMS0uNi4zLTEuMi40LTIgLjRabTYuOS0yLjN2NkgyNnYtNy40aDEuNlY0M1ptMi4zLTEuNFY0M2EzLjIgMy4yIDAgMCAwLTEuNSAwIDEuNCAxLjQgMCAwIDAtLjggMWwtLjIuNmgtLjRjMC0uNCAwLS44LjItMS4yIDAtLjQuMi0uOC40LTEgLjItLjQuNC0uNi43LS44YTEuOSAxLjkgMCAwIDEgMS4zLS4yaC4zWm00LjQgNy41Yy0uNiAwLTEgMC0xLjUtLjJhMy4zIDMuMyAwIDAgMS0xLjgtMiA0IDQgMCAwIDEtLjMtMS4zdi0uM2MwLS42IDAtMS4xLjMtMS42LjEtLjUuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS0uOGwxLjQtLjJjLjUgMCAxIDAgMS40LjJsMSAuOC41IDEuMWMuMi41LjIgMSAuMiAxLjV2LjdoLTUuN3YtMS4yaDQuMXYtLjFjMC0uMyAwLS42LS4yLS45IDAtLjItLjItLjQtLjUtLjZsLS44LS4yYy0uMyAwLS42IDAtLjguMi0uMiAwLS40LjItLjUuNS0uMS4yLS4zLjQtLjMuN2wtLjEgMXYxLjNsLjUuNy42LjUuOS4xYTIuMyAyLjMgMCAwIDAgMS44LS45bC45LjktLjYuNi0xIC41Yy0uMy4yLS44LjItMS4yLjJaTTQzIDQ3bC0uMS0uNGExIDEgMCAwIDAtLjUtLjRsLTEtLjMtMS0uMy0xLS41LS41LS42LS4xLS44YzAtLjMgMC0uNi4yLS45IDAtLjIuMy0uNS41LS43bDEtLjUgMS4xLS4xYy42IDAgMS4yIDAgMS42LjMuNC4yLjguNSAxIC44LjMuNC40LjguNCAxLjJoLTEuN2wtLjEtLjVhMSAxIDAgMCAwLS41LS41bC0uNy0uMS0uNy4xYTEgMSAwIDAgMC0uNC40LjkuOSAwIDAgMCAwIC44bC4yLjIuNC4yLjguMiAxLjQuNWMuNS4yLjguNCAxIC43LjIuMy4zLjcuMyAxLjFhMiAyIDAgMCAxLS44IDEuNiAzIDMgMCAwIDEtMSAuNWwtMS4yLjFjLS42IDAtMS4yIDAtMS43LS4zLS40LS4zLS44LS42LTEtMS0uMy0uMy0uNC0uNy0uNC0xLjFoMS42YzAgLjMuMS41LjMuN2wuNS40LjcuMS44LS4xYy4yIDAgLjMtLjIuNC0uMy4yLS4yLjItLjMuMi0uNVptNy41IDBjMC0uMiAwLS4zLS4yLS40YTEgMSAwIDAgMC0uNC0uNGwtMS0uMy0xLjEtLjMtLjktLjVjLS4yLS4xLS40LS4zLS41LS42bC0uMi0uOGMwLS4zIDAtLjYuMi0uOWwuNi0uNy45LS41IDEuMi0uMWMuNiAwIDEuMSAwIDEuNi4zbDEgLjguMyAxLjJoLTEuNmMwLS4yIDAtLjQtLjItLjVhMSAxIDAgMCAwLS40LS41bC0uNy0uMS0uNy4xYTEgMSAwIDAgMC0uNS40LjkuOSAwIDAgMCAwIC44bC4yLjIuNS4yLjcuMiAxLjUuNSAxIC43Yy4yLjMuMy43LjMgMS4xYTIgMiAwIDAgMS0uOCAxLjYgMyAzIDAgMCAxLTEgLjVsLTEuMi4xYy0uNyAwLTEuMyAwLTEuNy0uMy0uNS0uMy0uOC0uNi0xLTEtLjMtLjMtLjQtLjctLjQtMS4xaDEuNmMwIC4zIDAgLjUuMi43bC42LjQuNy4xLjctLjFjLjIgMCAuNC0uMi41LS4zbC4yLS41Wm03LjcuM3YtNS43SDYwVjQ5aC0xLjZ2LTEuN1ptLjMtMS42aC41YzAgLjUgMCAxLS4yIDEuNCAwIC40LS4yLjgtLjUgMS0uMi40LS41LjYtLjguOGEzIDMgMCAwIDEtMS4zLjIgMyAzIDAgMCAxLTEtLjEgMiAyIDAgMCAxLS44LS41Yy0uMi0uMi0uNC0uNS0uNS0uOWE0IDQgMCAwIDEtLjItMS4ydi00LjhoMS43djUuNWwuMy40LjQuMi41LjFjLjUgMCAuOCAwIDEtLjMuNC0uMi42LS40LjctLjdsLjItMVptNS0yLjd2NmgtMS43di03LjRoMS42VjQzWm0yLjItMS40VjQzYTMuMiAzLjIgMCAwIDAtMS40IDAgMS40IDEuNCAwIDAgMC0uOSAxbC0uMS42aC0uNGMwLS40IDAtLjguMi0xLjIgMC0uNC4yLS44LjQtMSAuMi0uNC40LS42LjctLjhhMS45IDEuOSAwIDAgMSAxLjMtLjJoLjJabTQuNCA3LjVjLS41IDAtMSAwLTEuNC0uMmEzLjMgMy4zIDAgMCAxLTEuOS0yIDQgNCAwIDAgMS0uMi0xLjN2LS4zYzAtLjYgMC0xLjEuMi0xLjZzLjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEuMS0uOGwxLjMtLjJjLjYgMCAxIDAgMS40LjJsMSAuOGMuMy4zLjUuNy42IDEuMWwuMiAxLjV2LjdoLTUuN3YtMS4yaDR2LS4xbC0uMS0uOS0uNS0uNi0uOS0uMmMtLjIgMC0uNSAwLS43LjItLjIgMC0uNC4yLS41LjVsLS40Ljd2Mi4zbC41LjcuNi41LjguMWEyLjMgMi4zIDAgMCAwIDEuOS0uOWwuOS45LS43LjZjLS4yLjItLjUuNC0uOS41LS40LjItLjguMi0xLjMuMloiLz48cGF0aCBmaWxsPSIjRjM2OTAwIiBkPSJNMjUuNSA5NnYyMGgtMy4zdi0xNmwtNC45IDEuNnYtMi43bDcuOC0yLjloLjRabTE4LjIgMTcuNHYyLjZIMzAuNHYtMi4zbDYuNS03IDEuNi0yLjFjLjUtLjYuNy0xLjEgMS0xLjZhNCA0IDAgMCAwLS4yLTNjLS4yLS41LS42LS45LTEtMS4xYTMgMyAwIDAgMC0xLjYtLjVjLS44IDAtMS40LjItMS45LjVhMyAzIDAgMCAwLTEuMSAxLjNjLS4zLjYtLjQgMS4yLS40IDJIMzBjMC0xLjIuMy0yLjMuOC0zLjIuNS0xIDEuMy0xLjggMi4zLTIuMyAxLS42IDIuMi0uOSAzLjYtLjkgMS4zIDAgMi41LjIgMy40LjcgMSAuNCAxLjYgMSAyLjEgMS45LjUuOC44IDEuOC44IDIuOWwtLjMgMS44Yy0uMi43LS41IDEuMy0uOSAxLjlsLTEuMyAxLjgtMS43IDEuOC00LjMgNC44aDkuMlpNNDkuNSA5OGgyLjh2MTUuM0w1MiAxMTZoLTIuNVY5OFptMTEgMTEuNXYuM2MwIDEgMCAxLjgtLjMgMi42YTYgNiAwIDAgMS0uOSAyYy0uNC42LTEgMS0xLjUgMS40LS42LjMtMS40LjQtMi4yLjQtLjcgMC0xLjQtLjEtMi0uNC0uNi0uMy0xLjEtLjctMS41LTEuM2E3IDcgMCAwIDEtMS0yIDEzIDEzIDAgMCAxLS41LTIuNHYtLjhjLjEtMSAuMy0xLjguNi0yLjVhNyA3IDAgMCAxIC45LTJjLjQtLjUuOS0xIDEuNS0xLjMuNi0uMyAxLjItLjQgMi0uNHMxLjUuMSAyLjIuNWMuNi4zIDEuMS43IDEuNSAxLjNhNiA2IDAgMCAxIDEgMmMuMi44LjMgMS43LjMgMi42Wm0tMi44LjN2LS4zbC0uMS0xLjYtLjUtMS4zYy0uMi0uNC0uNS0uNy0uOC0uOS0uNC0uMi0uOC0uMy0xLjQtLjMtLjUgMC0xIDAtMS4zLjJsLS45LjctLjYgMS0uMiAxLjN2Mi4xYzAgLjYuMiAxLjIuNCAxLjdsMSAxLjFjLjQuMyAxIC41IDEuNi41LjYgMCAxLS4xIDEuNC0uNGwuOC0uOC41LTEuNC4xLTEuNlptMTIuNCAzLjd2LTZjMC0uNSAwLTEtLjItMS4zbC0uOC0uN2MtLjMtLjItLjctLjMtMS4yLS4zcy0uOSAwLTEuMi4yYTIgMiAwIDAgMC0uOC43Yy0uMi4yLS4zLjUtLjMuOWgtMi44YzAtLjUuMS0xIC40LTEuNS4yLS40LjUtLjkgMS0xLjIuNS0uNCAxLS43IDEuNy0uOS42LS4yIDEuMy0uMyAyLjEtLjMgMSAwIDEuOC4xIDIuNS41LjguMyAxLjQuOCAxLjggMS40LjQuNy42IDEuNS42IDIuNHY1LjdjMCAuNSAwIDEgLjIgMS41IDAgLjUuMi45LjMgMS4ydi4yaC0yLjlhNSA1IDAgMCAxLS4zLTEuMmwtLjEtMS4zWm0uNC01LjJ2MS43aC0yYy0uNSAwLTEgMC0xLjQuMi0uNCAwLS43LjItMSAuNGExLjggMS44IDAgMCAwLS44IDEuNmwuMyAxIC42LjYgMS4xLjJhMy4xIDMuMSAwIDAgMCAyLjYtMS4yYy4yLS40LjQtLjcuNC0xbC45IDEuMmMwIC4zLS4zLjctLjUgMWE1IDUgMCAwIDEtLjkgMS4xIDQuNCA0LjQgMCAwIDEtMyAxLjFjLS44IDAtMS42LS4xLTIuMi0uNWE0IDQgMCAwIDEtMS41LTEuMyA0IDQgMCAwIDEtLjItMy43Yy4yLS41LjYtMSAxLjEtMS4zLjUtLjQgMS4xLS43IDEuOC0uOC44LS4yIDEuNi0uMyAyLjUtLjNoMi4yWm04LjItMi42VjExNkg3NnYtMTIuN2gyLjdsLjEgMi40Wm00LTIuNS0uMSAyLjdhNS43IDUuNyAwIDAgMC0yLjQgMCAyLjQgMi40IDAgMCAwLTEuNSAxLjYgNCA0IDAgMCAwLS4zIDEuMmgtLjZjMC0uOCAwLTEuNS4yLTIuMmE2IDYgMCAwIDEgLjctMS44Yy4zLS41LjctLjkgMS4yLTEuMmEzLjIgMy4yIDAgMCAxIDIuMi0uNGwuNS4xWiIvPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii45IiBzdHJva2Utd2lkdGg9IjEuNSIgZD0iTTE4NyA1NGMtMTMuNiAwLTEyIDU2LjEtMjYuMSA1Ni4xLTEyIDAtMTQtMjMuOC0yMy4yLTIzLjhTMTI0LjQgMTE5IDExMi41IDExOWMtOS44IDAtMTUuMy02LjYtMjQuNS02LjYiLz48ZGVmcz48ZmlsdGVyIGlkPSJhIiB3aWR0aD0iMTk3IiBoZWlnaHQ9IjEwOSIgeD0iMS41IiB5PSIyNS41IiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIyLjMiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTAyOF8xNjQ0NyIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTAyOF8xNjQ0NyIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjwvZGVmcz48L3N2Zz4=", + "description": "Displays historical fluid pressure values as a simplified chart. Optionally may display the corresponding latest fluid pressure value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pressure', label: 'Pressure', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'pressure', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 15 - 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 15 - 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#305AD7\"},{\"from\":5,\"to\":10,\"color\":\"#3FA71A\"},{\"from\":10,\"to\":15,\"color\":\"#F36900\"},{\"from\":15,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Pressure\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"compress\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"units\":\"bar\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "fluid pressure", + "liquid pressure", + "pump pressure", + "hydraulic", + "pump", + "compressibility", + "compressive stress", + "pipe", + "pipeline" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_fluid_pressure_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_fluid_pressure_chart_card_with_background.json new file mode 100644 index 0000000000..53de396789 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_fluid_pressure_chart_card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "simple_fluid_pressure_chart_card_with_background", + "name": "Simple pressure chart card with background", + "deprecated": false, + "image": "tb-image:c2ltcGxlX3ByZXNzdXJlX2NoYXJ0X2NhcmRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==:c2ltcGxlX3ByZXNzdXJlX2NoYXJ0X2NhcmRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxMTIiIHk9IjI0IiBmaWxsPSJ1cmwoI2EpIiByeD0iNCIvPjxnIGZpbHRlcj0idXJsKCNiKSI+PHJlY3Qgd2lkdGg9IjE4OCIgaGVpZ2h0PSIxMDAiIHg9IjYiIHk9IjMwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii44IiByeD0iMyIvPjwvZz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNMjAuOCA0NS4zaC0yLjZ2LTEuNGgyLjZjLjQgMCAuOCAwIDEtLjIuNC0uMS42LS4zLjctLjZsLjItLjljMC0uMyAwLS42LS4yLS45LS4xLS4yLS4zLS41LS42LS42YTIgMiAwIDAgMC0xLjEtLjNoLTJWNDlIMTdWMzloMy44Yy44IDAgMS40LjIgMiAuNS41LjIgMSAuNiAxLjIgMSAuMy42LjQgMSAuNCAxLjcgMCAuNi0uMSAxLjItLjQgMS43LS4zLjQtLjcuOC0xLjIgMS0uNi4zLTEuMi40LTIgLjRabTYuOS0yLjN2NkgyNnYtNy40aDEuNlY0M1ptMi4zLTEuNFY0M2EzLjIgMy4yIDAgMCAwLTEuNSAwIDEuNCAxLjQgMCAwIDAtLjggMWwtLjIuNmgtLjRjMC0uNCAwLS44LjItMS4yIDAtLjQuMi0uOC40LTEgLjItLjQuNC0uNi43LS44YTEuOSAxLjkgMCAwIDEgMS4zLS4yaC4zWm00LjQgNy41Yy0uNiAwLTEgMC0xLjUtLjJhMy4zIDMuMyAwIDAgMS0xLjgtMiA0IDQgMCAwIDEtLjMtMS4zdi0uM2MwLS42IDAtMS4xLjMtMS42LjEtLjUuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS0uOGwxLjQtLjJjLjUgMCAxIDAgMS40LjJsMSAuOC41IDEuMWMuMi41LjIgMSAuMiAxLjV2LjdoLTUuN3YtMS4yaDQuMXYtLjFjMC0uMyAwLS42LS4yLS45IDAtLjItLjItLjQtLjUtLjZsLS44LS4yYy0uMyAwLS42IDAtLjguMi0uMiAwLS40LjItLjUuNS0uMS4yLS4zLjQtLjMuN2wtLjEgMXYxLjNsLjUuNy42LjUuOS4xYTIuMyAyLjMgMCAwIDAgMS44LS45bC45LjktLjYuNi0xIC41Yy0uMy4yLS44LjItMS4yLjJaTTQzIDQ3bC0uMS0uNGExIDEgMCAwIDAtLjUtLjRsLTEtLjMtMS0uMy0xLS41LS41LS42LS4xLS44YzAtLjMgMC0uNi4yLS45IDAtLjIuMy0uNS41LS43bDEtLjUgMS4xLS4xYy42IDAgMS4yIDAgMS42LjMuNC4yLjguNSAxIC44LjMuNC40LjguNCAxLjJoLTEuN2wtLjEtLjVhMSAxIDAgMCAwLS41LS41bC0uNy0uMS0uNy4xYTEgMSAwIDAgMC0uNC40LjkuOSAwIDAgMCAwIC44bC4yLjIuNC4yLjguMiAxLjQuNWMuNS4yLjguNCAxIC43LjIuMy4zLjcuMyAxLjFhMiAyIDAgMCAxLS44IDEuNiAzIDMgMCAwIDEtMSAuNWwtMS4yLjFjLS42IDAtMS4yIDAtMS43LS4zLS40LS4zLS44LS42LTEtMS0uMy0uMy0uNC0uNy0uNC0xLjFoMS42YzAgLjMuMS41LjMuN2wuNS40LjcuMS44LS4xYy4yIDAgLjMtLjIuNC0uMy4yLS4yLjItLjMuMi0uNVptNy41IDBjMC0uMiAwLS4zLS4yLS40YTEgMSAwIDAgMC0uNC0uNGwtMS0uMy0xLjEtLjMtLjktLjVjLS4yLS4xLS40LS4zLS41LS42bC0uMi0uOGMwLS4zIDAtLjYuMi0uOWwuNi0uNy45LS41IDEuMi0uMWMuNiAwIDEuMSAwIDEuNi4zbDEgLjguMyAxLjJoLTEuNmMwLS4yIDAtLjQtLjItLjVhMSAxIDAgMCAwLS40LS41bC0uNy0uMS0uNy4xYTEgMSAwIDAgMC0uNS40LjkuOSAwIDAgMCAwIC44bC4yLjIuNS4yLjcuMiAxLjUuNSAxIC43Yy4yLjMuMy43LjMgMS4xYTIgMiAwIDAgMS0uOCAxLjYgMyAzIDAgMCAxLTEgLjVsLTEuMi4xYy0uNyAwLTEuMyAwLTEuNy0uMy0uNS0uMy0uOC0uNi0xLTEtLjMtLjMtLjQtLjctLjQtMS4xaDEuNmMwIC4zIDAgLjUuMi43bC42LjQuNy4xLjctLjFjLjIgMCAuNC0uMi41LS4zbC4yLS41Wm03LjcuM3YtNS43SDYwVjQ5aC0xLjZ2LTEuN1ptLjMtMS42aC41YzAgLjUgMCAxLS4yIDEuNCAwIC40LS4yLjgtLjUgMS0uMi40LS41LjYtLjguOGEzIDMgMCAwIDEtMS4zLjIgMyAzIDAgMCAxLTEtLjEgMiAyIDAgMCAxLS44LS41Yy0uMi0uMi0uNC0uNS0uNS0uOWE0IDQgMCAwIDEtLjItMS4ydi00LjhoMS43djUuNWwuMy40LjQuMi41LjFjLjUgMCAuOCAwIDEtLjMuNC0uMi42LS40LjctLjdsLjItMVptNS0yLjd2NmgtMS43di03LjRoMS42VjQzWm0yLjItMS40VjQzYTMuMiAzLjIgMCAwIDAtMS40IDAgMS40IDEuNCAwIDAgMC0uOSAxbC0uMS42aC0uNGMwLS40IDAtLjguMi0xLjIgMC0uNC4yLS44LjQtMSAuMi0uNC40LS42LjctLjhhMS45IDEuOSAwIDAgMSAxLjMtLjJoLjJabTQuNCA3LjVjLS41IDAtMSAwLTEuNC0uMmEzLjMgMy4zIDAgMCAxLTEuOS0yIDQgNCAwIDAgMS0uMi0xLjN2LS4zYzAtLjYgMC0xLjEuMi0xLjZzLjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEuMS0uOGwxLjMtLjJjLjYgMCAxIDAgMS40LjJsMSAuOGMuMy4zLjUuNy42IDEuMWwuMiAxLjV2LjdoLTUuN3YtMS4yaDR2LS4xbC0uMS0uOS0uNS0uNi0uOS0uMmMtLjIgMC0uNSAwLS43LjItLjIgMC0uNC4yLS41LjVsLS40Ljd2Mi4zbC41LjcuNi41LjguMWEyLjMgMi4zIDAgMCAwIDEuOS0uOWwuOS45LS43LjZjLS4yLjItLjUuNC0uOS41LS40LjItLjguMi0xLjMuMloiLz48cGF0aCBmaWxsPSIjRjc3NDEwIiBkPSJNMjUuNSA5NnYyMGgtMy4zdi0xNmwtNC45IDEuNnYtMi43bDcuOC0yLjloLjRabTE4LjIgMTcuNHYyLjZIMzAuNHYtMi4zbDYuNS03IDEuNi0yLjFjLjUtLjYuNy0xLjEgMS0xLjZhNCA0IDAgMCAwLS4yLTNjLS4yLS41LS42LS45LTEtMS4xYTMgMyAwIDAgMC0xLjYtLjVjLS44IDAtMS40LjItMS45LjVhMyAzIDAgMCAwLTEuMSAxLjNjLS4zLjYtLjQgMS4yLS40IDJIMzBjMC0xLjIuMy0yLjMuOC0zLjIuNS0xIDEuMy0xLjggMi4zLTIuMyAxLS42IDIuMi0uOSAzLjYtLjkgMS4zIDAgMi41LjIgMy40LjcgMSAuNCAxLjYgMSAyLjEgMS45LjUuOC44IDEuOC44IDIuOWwtLjMgMS44Yy0uMi43LS41IDEuMy0uOSAxLjlsLTEuMyAxLjgtMS43IDEuOC00LjMgNC44aDkuMlpNNDkuNSA5OGgyLjh2MTUuM0w1MiAxMTZoLTIuNVY5OFptMTEgMTEuNXYuM2MwIDEgMCAxLjgtLjMgMi42YTYgNiAwIDAgMS0uOSAyYy0uNC42LTEgMS0xLjUgMS40LS42LjMtMS40LjQtMi4yLjQtLjcgMC0xLjQtLjEtMi0uNC0uNi0uMy0xLjEtLjctMS41LTEuM2E3IDcgMCAwIDEtMS0yIDEzIDEzIDAgMCAxLS41LTIuNHYtLjhjLjEtMSAuMy0xLjguNi0yLjVhNyA3IDAgMCAxIC45LTJjLjQtLjUuOS0xIDEuNS0xLjMuNi0uMyAxLjItLjQgMi0uNHMxLjUuMSAyLjIuNWMuNi4zIDEuMS43IDEuNSAxLjNhNiA2IDAgMCAxIDEgMmMuMi44LjMgMS43LjMgMi42Wm0tMi44LjN2LS4zbC0uMS0xLjYtLjUtMS4zYy0uMi0uNC0uNS0uNy0uOC0uOS0uNC0uMi0uOC0uMy0xLjQtLjMtLjUgMC0xIDAtMS4zLjJsLS45LjctLjYgMS0uMiAxLjN2Mi4xYzAgLjYuMiAxLjIuNCAxLjdsMSAxLjFjLjQuMyAxIC41IDEuNi41LjYgMCAxLS4xIDEuNC0uNGwuOC0uOC41LTEuNC4xLTEuNlptMTIuNCAzLjd2LTZjMC0uNSAwLTEtLjItMS4zbC0uOC0uN2MtLjMtLjItLjctLjMtMS4yLS4zcy0uOSAwLTEuMi4yYTIgMiAwIDAgMC0uOC43Yy0uMi4yLS4zLjUtLjMuOWgtMi44YzAtLjUuMS0xIC40LTEuNS4yLS40LjUtLjkgMS0xLjIuNS0uNCAxLS43IDEuNy0uOS42LS4yIDEuMy0uMyAyLjEtLjMgMSAwIDEuOC4xIDIuNS41LjguMyAxLjQuOCAxLjggMS40LjQuNy42IDEuNS42IDIuNHY1LjdjMCAuNSAwIDEgLjIgMS41IDAgLjUuMi45LjMgMS4ydi4yaC0yLjlhNSA1IDAgMCAxLS4zLTEuMmwtLjEtMS4zWm0uNC01LjJ2MS43aC0yYy0uNSAwLTEgMC0xLjQuMi0uNCAwLS43LjItMSAuNGExLjggMS44IDAgMCAwLS44IDEuNmwuMyAxIC42LjYgMS4xLjJhMy4xIDMuMSAwIDAgMCAyLjYtMS4yYy4yLS40LjQtLjcuNC0xbC45IDEuMmMwIC4zLS4zLjctLjUgMWE1IDUgMCAwIDEtLjkgMS4xIDQuNCA0LjQgMCAwIDEtMyAxLjFjLS44IDAtMS42LS4xLTIuMi0uNWE0IDQgMCAwIDEtMS41LTEuMyA0IDQgMCAwIDEtLjItMy43Yy4yLS41LjYtMSAxLjEtMS4zLjUtLjQgMS4xLS43IDEuOC0uOC44LS4yIDEuNi0uMyAyLjUtLjNoMi4yWm04LjItMi42VjExNkg3NnYtMTIuN2gyLjdsLjEgMi40Wm00LTIuNS0uMSAyLjdhNS43IDUuNyAwIDAgMC0yLjQgMCAyLjQgMi40IDAgMCAwLTEuNSAxLjYgNCA0IDAgMCAwLS4zIDEuMmgtLjZjMC0uOCAwLTEuNS4yLTIuMmE2IDYgMCAwIDEgLjctMS44Yy4zLS41LjctLjkgMS4yLTEuMmEzLjIgMy4yIDAgMCAxIDIuMi0uNGwuNS4xWiIvPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii45IiBzdHJva2Utd2lkdGg9IjEuNSIgZD0iTTE4NyA1NGMtMTMuNiAwLTEyIDU2LjEtMjYuMSA1Ni4xLTEyIDAtMTQtMjMuOC0yMy4yLTIzLjhTMTI0LjQgMTE5IDExMi41IDExOWMtOS44IDAtMTUuMy02LjYtMjQuNS02LjYiLz48ZGVmcz48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2MiIHRyYW5zZm9ybT0ibWF0cml4KC4wMDUwMjUxMyAwIDAgLjAwODk3MzQ0IDAgLS4yKSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMTk3IiBoZWlnaHQ9IjEwOSIgeD0iMS41IiB5PSIyNS41IiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIyLjMiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA3N180NjQxNiIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA3N180NjQxNiIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFNQ0FnSUNBZ01DQWdJREF3TURCQVlFQkFRRUJBZ0dCZ1VHQ1FnS0Nna0lDUWtLREE4TUNnc09Dd2tKRFJFTkRnOFFFQkVRQ2d3U0V4SVFFdzhRRUJELzJ3QkRBUU1EQXdRREJBZ0VCQWdRQ3drTEVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkQvd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhRQUFBZ01CQVFFQkFRQUFBQUFBQUFBQUJnY0VCUWdEQWdFSkFQL0VBQnNCQUFJREFRRUJBQUFBQUFBQUFBQUFBQUlEQUFFRUJRWUgvOW9BREFNQkFBSVFBeEFBQUFCNzZnelpuSmpGTk43Rjk1TzBucVR4Sld5VVZYUUxMbklRc0FodVcwbnFUblk5S3Rlb2JoYm5QMlAwYzZWeU1mTzBGZWx3dm0xTXJYemFTUko4N1p0M3M0U081SWs5VklseWprb3dJZVdVZWl1REFsWUpKZFRwUHNuT1JEOC9YbGJNV29OK1lDUWVPaDBvU0dTeGY2V055bXIxSnpuYk5FOVBLeUdqS3FmMGtDNE8xQnhiSUlYN2s2M0xVeElqQy9rbGpJaFJDWXRLK0ZacXlFYmFRU1dZZ3o3TktMTnJQNXpOMXBEc2ozSHNXZnVDL3FScWxOSU5DVllOL3dCTDlYZnFUcEJ0akcvS3JtcFVTTEZMS0VsTGJEcGZPOU5wZFlkNVhmRGtkZDM2L0h2TFlBK2cycHVSSnVFa25nWURJYjNhTUtxOHkrZ25GQzdGb2ZicTFNWjhvWlEzMjBLNGw1MzVHM1ZYVnlLYlB2eHBpOWZWajFObTd2bUUxTGw5bWMyTnlHVnJSMXVvaW1Lam5iT3Iwc0hZanpVQ2NqeHhMR2Z1eTJiQjRTd0RGb25nWi8wOFVlQW1PZnAwRHZRdWthTVY4bjJZcGZZMU4xUGx6aGpNNTRkQ3pTeGc2OCtzZHlSTkRWL2gwSTFKT2pkbWRHdEs5enRwRVBzakJvYk02UXlOQWM3bkkwVFhibXZucVFmT2ZxZnBJd3Z6K2prSUdmMFYrcGVySFowU2d3NlZSVm1yS3RXb0lTdGdsTVRZWE1lVzhkMmVPeFZnVmNVYVdTNUtJeXRUNldhYjFKU3VaaElEYnA2dEg5TEorUjNPMUpBbHpDSDlnb1diK2Jzc3dZOWV4elo3bEVaMzhrcUJxd1M0UUN6aldnaHVjNms2eEV3WW1zR2hzUEFmRzdwZytKQk5KbEptbG9qQTZXZkxuNjU2bFo0NTJzT2sxTjFNUEs2N2lVNHh1N3YrcVZxbVFXanlFUlJabmpndGJwQzh6WTk5cUsyWGRGV2NrazhHVUtxc2ljdjhoY1RRWm9mcVhhcEtOYThBOWg5Zm4zVndiQXdnYWI3UkQxdFYySjduMzVyV1ZFb3V0MTlxWWg1Ry9XL1F5M3pSUm1kck1NU3BxdzlaVHJuNWJaVHpxd05mNGFMRTlBeHU5WGRibjJHbEpCZGNxdmpMeXh5ZDREbWR0dnM4NjgwS21TcTRTeTVoMDNKVXdMcTFNYmk3bEZRL1YxYWlVT1IrRjFtbU4yTGZIUDBVaWRHbTlxeDBiTmR1Um82VTE2bUtqSG9BOFdtS0pBV3pHNm9mMHE5TEpJak5jOUhMZUdDZHdhbnR0ejJKVjhFc3I4L1RrZk84VllDWTM0ZjFkVXp4bTBXR3pPVXNESVBMMlhqbDNxMlE3bWwramtnQVZBcGlqeU9xZ0ovOURKWmxaWTlJd041OTVPOXM2ODVqcFNNaGVLdWZzaVhBMXF5RlROcTdjZzVuY3NGV1NtSE1UQlFOOE9CSHBObU5WOXE0d3NDQVo3cUd4WEpOSE9WUVZkMVpVSzdyU0dxcXhVcXFyb3dDNDB2ZmZiNTZpeFBFUW1sZWpuamdRZ2hsT2xpS3pzOVM0NDNMa0s3bk82cHFLOEtyUzZXZ3hyblJLeGQyY2dBWEtTcVdRaXNsc3N0VGRURW9jYno3Ym4wYnNSemtBa21yTU9sTm9aT0VwMUYvU29aamJYSWFqN1NlYXRWRlQ1TWVWMFBvWkVrREtuUzcvcFV1NjFIMDhhQ3lPMHJ2eis1SVlGQkVrSGxiVmdUYU92TkY5a1VnUzhNWHZyVlBNWjExUkJlYU1HbDc2a3N6U3VFTWhVVk1zeGxCalNUYXZSeXAzTXh6bVVrcSt5UXhJWXRTL3dBenJsVHJXN2dEWTB4QkcwV082cnRnVHlydEpuTEV4cTZBYU9sWHk1V0RZMGh3dWtxVVkxZWpqUTJEUnNEcTVQZDE5a3E2dk4vTDF5SHFidXBkV0JlNUZUaWQyb25kMU1kaEtsUzVFckpQSjJ0elVwdzdNL0dTb2wxOVFZU3dVU2YveEFBc0VBQUJCQUlCQkFJQkF3VUJBUUFBQUFBRUFRSURCUUFHRVFjU0V4UVZJU0lXSXpFUUpDVXlRVE5ELzlvQUNBRUJBQUVJQWRDY3lTbGlZblcydW5Fbkh1NE9tVzdOdFFZNFh4eWVSdk9jWTFtZHVQUk1sVkV5WjJTeXFtUlRxcTRQTmtNbk9OeFYvb21iWmIvR0F6VDVGZXg3YnRzRUJOczJNZWhXTE5XMWVBTzI5NkN3UERyWWZLWmFkVGRScm8xOG1rN1pVN05ha3VENlptdGxybXNUckJFazFQSkRtaWpOcUNHTlNzSVNTRk9HNG45Sk1tWEpuL3prdjNqZVVYSUpGd2VYSUg4cG5DWnhpcjJwem5VR2Ryd1pJYzFhbjlQYkJ5czNhZGZnbk5iby9zeFFFRlQ5VCtveDIwbnlCeFB1RHBtK1BOV0kyUnRnbndQVGVhTkMzUlJkUTJSU3dNWTZzQWJCTTFNbzVlMWlOeUplNU9jUk1WZU1rZGhEL3dDY2xkbmJ6blpqZnJJSk9NZ2x5Si9LZjBuZDJzWE50Rzl2SXhZaGpCbGJkbXdKRkUyYlhoUlpJRldMY3VoOVRaelNtMDl4UVQwVnZOU3IwWEVoUStXYWZTR2wxSnNVczI2TW5PU0JJUUE1b3lXcE1CRXNhSXFCVGNweG5LY1k5Y25jcVpQSmlwem5IR2NZamNiOVpCSmtNMk1rK3NOay9GVVN5ajcrZTQ2bWI0R2xJYjdGcXhFaDFjWlI2NWpISHpSRFF2bmwzM2FsSW1aWVV0QmFXOGZKV0gyc29tdy9HNEtKSWFMN3owWXJUa1dlQ0Q5bjZHYzZOMlJ6OHBrNURZbWR5L015dUlkR1E5dmVuYzFXcTNFeDBKTTBibWlBdEpaQ3JUKzNHS3FMa0wrTWVaMk0rZ3p6VENaSXpqeGtkQXE0UUhNK3JkMjFwWkloanhuMXFjQm81ZXFjNU1HcGxTaW5xK1NoYkd0TzVlWFI1dE5HRzIyVTZhdjJtdUhwMnd5MzJ6Z1NJeHd1cTdaQ2NLeklwR1Q5M1kxNnRYTEErQVdKc3BGamJBenVmNGRZMkZoa1hnY2lvOU83SE5SRTV4dTVWZ0Jzb1pFKzhWTG5veks4eHBvN1prNHlVcHNEZVZsMm9QM1c1WWJCVkxGTWlTTjhsWXg2UUZLbGE1cWdqaGtIL2dPenNHYW1ieUhBYnJSc0JGZ1RURTFmcGhCcUdHN3VrcXluYlhybE5iRjJOYUpGRXlNYnFKVkFrQThzbzd5VFhxdjBuZFArcHNvSkV3RjdEY0JFUXRuellIVjFvMk1hWS9YS0kyQjNNRTd0WFllNGJUK29NSndrY0JudmlsQ3E2Q0NLc0hlNlowcmRlT25Yemd6M2RCSHlRR2JDYkVra1BVYlkzVlVvc0xLR3JxTnh1M09QczlRMTU4Q2lNcmJvSDQ1SzlCNXhYMTZxdXVRUG4ydWVYRlRzaFRPdFhVZVlUL0FWVEFvUzRtcVFsWFJNL3dEVkI0dFlvYWVqaW1zRWU1b3BXMUJRVHVScnArbDN5ZGpBV0ZHK0Nta2tyTkRFcjlwZTd6R3hKZkFPUncwVHdkckZmWEViTHBtejZ6c01mdmFob2RwZVdUbHl5OGNNYTYxcm9XakFXYlBKSk5vRllGQTRuSVdXZERPMmNldXJLeVVkYmFsNnJ0aVNzUkdkUGROMk92c2hiY25iSnlhMUd3dy9IN1ZQSWtxTk8zRVNMc1pyZXgxYkxUc3VDRzk4U3RUcS9YeFYyMWx4dFZYSUkrQ1FPcThpY3lidEtjR1Uvd0JCc3h0V1c2M3VLdWFXd2lUdXQ2OXRSVUM2dFdVT3FpUVFNaldrcnlZMk1GTXNLNHdpZHdVTnJSTkhWSng3Y09MWnRYbWhJMGVwTG9ORVpJbERTd3dNYTlJYWp3SFBOd3VyQXVaRUllY0hDUkdxT3AwbUFzcElNM09tbUoyMFJTS2VKYTJxbXVTUUtpYTBuZVNaU0J4dS9zaWJVR2FZbjQrQzgxdHJQM0k5YXVIU0N1RE02aDlMS1N6UGR0TXV6ak5FdENZNHdXZWRyV3B1ZEJCYksxMGhIVGNWMDd2YTZjMElMT3FiV1EyMFpjZGlYS0ZCdE4wTE1zVmlCMUExdTdtU3ZDWnZ1b1F6eFZ6eXk3QjVNZ2lValhlS2RyNm1kQzlRRW1ZNG0xUnJmVE4yRGV2YzdLMjZONm1Hd2VQV3FhbE5Qb2dGMmIwMGdzbzR3T29GOHdQYWhoc3ZrU01FZUZvVnZCRk15dndtMnJtZXhBNC9mTm9iMUNHcHF5enNDR2txQzJtWXJMZEdQc29mYW9Id3YzV3pKa3U1NHBHbXk4dFJLYmNpcTRnNnVscytvRk43VW5sMTZ4NytvMEcwVnRqQjY5bzJkUGFnYlBHSXRsVGdlTDFoMzlNOVBuUEV2RjlpSjQvbmdQS1pTVXBWc1IwWXZHV3RPWlFrRVdUS2dHVnhSUmtFRlM0eXdiNlZYNEpaUGJOaEw3U3ZOQ0RGUGNGQWd1Nmk3QllXT0NtcGZhMHd1UDNoNGcvYkl1QjM3UVZPUlRVdGNFeU5ZNTJrRHpvNUlhOXNmeXp5NVJIOTFaRks3cmJFSS9iNXlnMjgvd0RJUE9UWmlxSzYzc3FZR3ZIbnE5eElyNi8xQU5QdkE5cDE0ZUowSVV0Y2ZJYksyTWhEbkZ1U0NidUo5b1VGc01jbmYxWDNQMzVmaFFORk0vVGl0bGRXWDFYc29iREVnUkZhNWtqbFRqbFpwaFdPNWw2djdqSk5RRXhEZExxeU9xb1IweXJzNGFMWmlLd3JZdGVuTlpIRGpuRXppdy9EVHNPZllqelF4Z2V0UC9qcnFjY09CUTI3VjFCYkcxUkszY2VaWHl6eXg5dlBHVTlJT2xuSVBsblJHa1NFdk4xZlYyUjYySDVOcld3MDRUNStvMG5xdFNiVEQ0WlpiVVgrV1M3WFRoeHVrSXRkKy9VUmEwMnZMcXBIM0k1dERPeGVIRUY3RnJPNWdSMUIzVVN6cG5lSTJuNnN1dFo0Z3hiMnkyaTBqZEN6YU5VMk8wcmMxeWxtRnJob1pkam9CYkNtbWptMUhxNEcxalFMY1RZOWNzVS90M1NnSW5kaU1zRC9BTUtucWRiRDBKWHhvNFZtOHFhUnBGMUhBK1Y2WXFLajFUTkZtUS9ZSmt6Y2xqQXJEVlhwNXRNaEdyaSs5c1JJTjVVRVY4bGV3clZwa1lOdk80RldsVzF0UnJlcnhuNk1RWFk2dE5CcW00UkNzK2ZBL3dDelhsZXFMMnBZMUpKTFo1ckNaTHZialVuSmpqb0NhS2NENThKRis2L1l3Tzc3WGF3V056WmQ2anJxQTBzZlI5WEJzNUt5ZWVtc3RkSGNSWG53YmJSZ3ZkT0xmOVdPd1Y2UTNHeGU2Vk1ZZHFQcTNGbTlGMktxS0N1SnNxYU9GSXU5dWoxMW9Ic3RvM09vWUUva0lTR3JKTGlDaVdHQTA2UlB5ZkU2ZFZjNDdtQ1ZTb0tnbXhoMU9NQkZtTHRkdTdwR2lPYzNsWWdrVmZ6S0VlejhveEtvNHU1TWZEYzBoWXMxZkxNNE1wMzhNcmlHTTVjdGNYL3RsNEVUTFVFc3pTYlhZeDRRNWNuRXZEVHlpU1pLNjJqWHRXYW1zSHB5KzcwMHdnUlh3NnByZHRXc2ZNOGtZK1ozN25odDRXY1JTckpCNUhpNzdGSStsT0pUUUZqc3Rpa0FhM1dBVnhOUkJqZXIyMld1VjRKRE9UaDRFall3ZStIL0FFeWRGYlZydDFIN0U3MDNRZE1adWJDZjdlR25EZ01rbUprMmV0aWlBWVl3SGNwUlkvRWF6Y3c1UDlrMjJ1Y3pqTHZjRnNTWDYyTlZhKzJ1Q2lIamk3YTFIektscFJQKzFJdDZCcisxWFhtdUorS3k3QnJqZVVTeDJXaWlUOW8zYlJ2L0FKbFJYVHJTZUZkdytibkRMWkowcjFLRUlFZlpsUkVSTVYyYlQ3UTRqN01jYXhPc0FJckIyeEZXVjBTR0JERnFiV2pzYk96VlJNZHFjSEg3T3RmTmxTSEptMFEzYVZVMDJDYXMzd284ajlNall1dGpvbVgxRVhUV0xkaGhDdkRqRWpUQ3J5eGxtZFY1OEpFaWNMSnIwTDhzZFpWc3Y0TzF4cm84aDF5QmZ4eDJ0UUovSjBDMnh5b1B0OVBQVXh4K2V2UEJyUW9LNEdhOWtUL1Z0OUwvQU5zTFQzQTV4cGc5VzNVdXRIbGkxTUsyYmVudzNua2J6eG5sWW1lekUxdk9RMUZ1VnNwbzlEUFFiSUlRQkJaUkRpZHZDUmdDUHhLVVJ5WnNHcEIyMUdYWHJySlc3MnJZMEcwK29LT09NTHZKS0lGYytBRHlXbkRUNnljUUtKRlRKSXhJK1hZUVJIM2NOZ0VjRWVRMSsrUnlsejFZcXhheTVqdTdQZ0pGK3NUWDVQNFUzWDVXaXpPU3JMaWpweFdKcUliN1c3dnA0VEtHemlJK21VQlRvOG4xbzVVK3FZZWVyMmdvYWZZWHp6bVU0eklhaVpjanJaWTArbVFUdHg0MHo0WG91Z2xRUkJPZ1hVWjJIV0ZzbzdCMy93RFhRcmtzS0lpNVBYdWxWVnd5cFZmcFBRamgvd0I3SmkvTGxQUzJjNG5iS0FaL2piaU1iaXRabGs1a2RjVS9LMGwvb0Q4ZEs1VytPN1Y1am9YNUdRMlA2eHhiSGZTSHZhN2NIZDF0TC9rYVZtUnB4amNhMU03RTdWelVZbTl4bkdsZU5sdGF3eDhZL2pqSmV4RnlXZU5xWVFVekNaV3Z6Ly9FQUVBUUFBSUJBZ01GQlFRSEJnVUZBQUFBQUFFQ0F3QVJCQkloRXlJeFFWRUZNa0poY1JTQmthRVFJQ05TY3JIQkJpUkFZcEtpRlRNMHd2QkRVMVNDMGYvYUFBZ0JBUUFKUHdIa2xxWC9BRTdiMXVsYVNSZ0Evd0FCNEZ2VjkxcjVNdkMxSExkTFhwMllXdHJXSVNKQU9MRzFkcHh2cHdRMy9LcE5tUU13RDZYRkhoVWQ4NUFyVCtBUGZGcTROY1Z4SUFwM2ZKd0JwREhoY05JUUYrK1J6TlcwMDBwWnBNVVZPNGd2cHpxMXZMaFhpUDhBQkRVTlJGaTF0YUFLUHh0VCt5enlPWGY3cmU2c0swbUpnYndqaUt3RFJUN0syOHZFVWxvNWo4SzExdlh3L2dXN205UkZvanhQV2hxRm8yVkJjMTJTaWhteWUxdU9OZG9CVzZJbFFDTUl3Tit0U1dIQlJUaTk3ZlUxckQ3Sk9UWnE0SDZTaXVCeGFwMGVYbGxGci9VMVBJVmhoaG94M1daK1AwY2N1bElIelMydFE0aXBDakRpZktyNVkzREN1RnFqMXRhOTdXcWZNMmJrT1ZZcE5KQVg2cUtmT3A1MXlvMUlxb0c0bXNaR1QwdlV3WjE1ZlRNZHFWMFJSY21wMmdrUGNFaTJ6VWVJK25FQWJLVEs0SnRZSG5XUGhMV3YzcU43b0tIZEZLdDlvV1BYNkd0R1VOelVoRWlXVmkzQTFqSS82cVNjWWRzU1V2STEya2hEV3ViY3VYb0t3a1NSQWFhVmhrU2JXenJwYzlLbHZpVzZjcU04elN5SFp1RnZwMHBtWE4vSXgvU2laRlJzOXRWMTk5UXZDQjR3YldyRkY4cmxjTzE5V3FPUlpsMGRtR2xQbkxEZFZkU1Q1QVZIaERpMS93QXh0cG1JdnlPVUcxVFE1Z2QzTkJJRkIvRmxycy9iNE10cGljTzRrUlI1MjRVNFBwUmNabTNyZEt4YkNOSXdVVkh5N1ErZFlkemRiTlk4cXhLWnNNTm5xM1NuUWtqa2E3aWs2VnlGVEtOcnV6TjBydFI0MjhWalhhTHVmeDFnSHhKdzBVT0Z5UmdFNTh0MlkzNVh2V1JuZmdWNVZMbUNTWlNvcnRQMlRCeERhWXVlWFZZMEg2OUJYWjMyeWpMUGo1UUduUDRtYmRpL0NBVDZWMjg3RzkyQ3htVCs1aldPMnljNDhRaFVOOE5QbFFPQng1anRsYXhWZ09ZdG93OU5hTzF3enRaWEhjRlkvWllYbmsrN3pwQkJob2ZzcDV1OFNlYS96TjhoNW1zRjdTRllvZHM3R3hHaEdVYUw4SzdKdzBXeVRPU2hZRWFhMnNhamthSXJ2d1NIZVpmSnVaOG1xeUpNTXp4QVdGK2VuaFBVVWk3ZHRFcGJsMzBnSG5VbTNubUdrU1hVVzUzdHZINGdWMjAyRFMrYlpZWlZqK09VYS9Hc2V1SXQ0WmJQZjNPUDFyc1lkbVl1VHZZbUcreFA0MFBjL0VOMm1GaU5DS3hKbGtkOXBZblZiMWgyMnJOY1BmZ0t2UVZuZGVCZkxXTnRJTklzT3JhVk1yNHZGemFKZm1UVTM3eFBkNTVoeFZSeGYxSjBXb2NrU2QyTWNQVTlUWFo2cEhIRjMyYTVMWnJBZjAyMXJCS01MSVl3WlVhekJmSHo5S0RKbGJNTXZoUFVkS2pVNG1PTW4vMkhHM3FLbFpzYmlpVlZ6NGN6SEw4QnJTQ3lpeW5uV09seUdReTdMdzZwbFByMXFXWWJOZG51N3R4bURjeDFVVVFEeHZWOGs3YjYyLzZnOFh2Rk5mQ1FYY0o1OHFpdTVGb0l6NThQaitWVEZpNXUvVnYvQUlLN09tdWl5QnBuRmd4VjhvOTVHdGRtdnNXZUViZGVTRytmNFdBOTlaaGx1Vkk0eG5xRCtsTGFURGJqNVJvUFAwdHI4UnlxNnlwWXlhNk1CUTNBMTA5S1lMcHhwNUkzSzVWWkRhc2JQSUIxT3RTeVpNR2p5aFdhOXlGMHJDSmlzUm5XRlkya3lCbFVjTTNMVW12Mk03UmpqUXZkOFBhWVdIRG1MODZteHZ0TWdKV0xZUEU1dHgxT25MclhiOEF4VFNiRFpQSmVRU1h0bGJ6dlhaRFN4NXlvZTlycmt2ZTUwNDZVTFplSU5HNnd5TkYvVGNWRGo1a3lFWlJFcWN3Ymc1aDBQeHI5aTFNR1E3MCtMVmQ3M1ZFWWhrKzNmR1Jxa2drNWlMbGJ6cDVmOFJURENMRUVQM3Q2K3A2NkNrQ2trRmJra1pxWVdGdHBiK2JTdTRtaTlOQllWZzhSRy8yUy9hcmxKelg1ZVZxN1JXT1dCYnlCZFdUL0FKK3RZV1YreklTc0dKT0xRUmJWbTNpUWVxcnd0NTEyVE5PcEtBT25NTUd1ZW1sZ09QaXBESHQ0cE1QS3AxNGJ5L3FQZlYzT3lLSHpJMHJER0FwdUZHRmpwVWhBV3NZL2FHQndzNHhDRjd2SU1PNHphRTZuS1RXRXg2aEIzOWhvUldEbEhaTFJiS2VYSllLVHpQeXJVT1JLUGVLYjdXUlM2aTNJY2E3Q1IxbGN5c1lmc2lzbjNyalcvSFd1eWY4QUQ4VGdwdzI2MWhJUWRNM1hXcEZkTmJNRHBwVDVRY3hBNjA0RHlTTlBCYzh5Ym41MU1rY2tZSWpFcldCZTJpMU43TW14QmxjZUM0NVZqcG1FN1pGekhOdEdiaFVOc01icXJxdDk3TnUzNkMzem9oRWlRbGI5YWtaWW5ZaUZ2VFFWbUUyRWsyVTY4MWNhTjg2bFdLUFM3T2RCeXFDZkQ5bDdSVGlzWmh5ZHJqV1U4SVJ3QzNBdTQxTnRLd2VKYVlIYm1YRjc3a25TK2JyWWNPbFRvK1cyYksxN1hGeDhxRmxHZHY3YlVMQ1FGOWVoSk5XdGxDdmJyOUVya2tTZTBSd3R2Q3lrQlBXcHlUSm1obEQ2bkp5UHFLN0diRVFhZ3lBMnorNmdZOFJoeHM4c2gza1AzVCtocDhaakhlNm9tNlZnVTIwWG5iU3NZMndNUVFZZktMQnZ2WHJFSEVSVE9wamp0YlpEMTlkYWQ0TURHYzFwUEQxc2VZdlNObFhkeUR3TDUrZExKbVZzNDFySTdKeGJMM1c4eHlQL0FBVlB0MWJ3dW8vS2h3cVJoYTcrdnVwOWxFVTJVWUI3eE9sNjd4UUVtay9kdTFyenc2YnJueHgvaUIzaDYxalFleUdiYVRSaVBON1FvMXlYNWZyVXE0VVJ5cm5Fc0oxakhFQWNxeHdqd3NZWVRRWkw3UW5nYjhyVXM4VzJGcEZmZVVnQ3k2K0VqL25XcDltOG9zN2ZkVHhIMXRvSzB5akwrRVUrZHB0NzZNQkxoOFpsR2FaWkxwTDUyNUdwMVBzaEFXM01kQlVZTE9tZlh6cFFyUS81aWVHUk9ocU5zUGlocTBjbzQrYXNPTk9DbzU1eFdPd3FaZnZ5QS9JWE5UaVJ3dVo1ZnVEK1ZSdzkrdFJabk9wYm1haGFwWFJjVXBFaWNtcnMrN2JQYVhqYkxwK1h5cnNwM2VjRmx6VGtMWWVnRmJPR051OGtTMkI5ZVo5OUs4OG1kYktQV3JncWlnMG05R3UxallIZVJ4d1lIa2FuMytCY3JjUCtOZjhBY0tuMVAvYWNPUGh4K1ZUdVY1M3N2NTFoOWxIenhNbkQzSG43cW5hYWJKbmxjOFNhdm1jMU5jZ0hTLzBMY1JSMmJTbzB6dmlpZTc1Vk1ES3QxOUFEcFJGcEVJcFEwa0w2TjFGSkpIamlBTjF5RFUwOHM3d2xzOGh6RlR6dFM3U1BGNGZaa25UTGJXbVdpdDZqUXlSZDFqeXEwMkdnZ0NScWVBdU5hVlliTUVjcnpVOFJSRkVWS3RaWlhTSTVWSjRtbWx6c1pXUHd2WFlXRFo4SEprV1JvZ1djZFRYWjhNVDhMcWdyT1h0MXB5WGMzdWEwVWREU3NVTGFHc0NqNXRidUt6eDRhU05UbVhqbXJHWXY3SWJVNW4wdlVNbTh0NlNVZTZvV1krbFljczBYRlNLd0VLN1NLMjlMcWI2MWhSRjdLcEJ5dmU5WCtORS9HbFB4cnRGOFBHQW9rQTRsclYydEppSTlxRXlzT0I1VkVLME5QVEMyUzlZSEFoWWcyVzdHNTBwWW9wWkg3c1pOcmNxZGo4YXZyNVZHYzNRQ3NLNk5lc0xuSThxdzVVZWxLb0xXNWNhVEsrVUVXOWFMdGh2WmcrUit1bFFqV29RS2gzWlFiNmNUU2tLQndxSE5MaW1aWlZrMVUrZFliSy9NV3FENVZGa2MrTExVZHBUYk1ldEx2eFNLeTY2QTFIdEc2MnFLM3VwQjhLd3NTUll1MExUSGlNM1FWaXBXV0VaUVR6cEVrSEZzNjNyMmY1VWNQOHFmQ240VjdNUGhTd04rRzFZY0gwcnRIOTN3K1hJVWp0ZTQ1MWo1TmpBQ3hVSUJtSG5XSlo1c2ZoaGRPU2cvVEhCS3VHaFptamtOcitockJ3UjdXUGFDUE1lRllLQVptYkx2RzQwb0JwQjNyVkhSMmJjbXRYYUtSUjRPZllCQkFEbTg2N1VSa2lzZG43T0xIMXB0b3pDOTh0clZHS2pxTkh3OERvNVc5dUZkbVJ4bVFadDZiU3V5NDlxOFJPMEV1N2JoVVl2enFPazA4cWlQd3FIWDBxS3UxY1poR1NNWnRpMjZmY2EvYVB0V2VIRVNoSGpNaTd3SjFwUWtNQ0JFVUhnS0JwVFFPemtqS3RZOHEvYTh4b0w3SmRtTjFPbkRwWGFJeFQ0QzJ4S3FBTmVONllVMU5YYlRZSER1Z21tSGV2SWVZcjlyWmNYZzVwYlN3bEFNdzZYcTFoUUZBVkpzOXBIM2dMa0VWMlQyYWtjUnliU1JqcUJwVWVFam53MzJDSmg5VnluVzVKNTFHdFJpa0ZLS3RTMW9UR3R0YTRTWXFORDcyK2k5Q2h3amI4cUVoSWpIQVVqZ0s2SnZEbldxMFRUbW8zTHpZZEhYM1ZoMkdmRWNhYzBUVjY1cWFrMzBsY0grbzA5d3JwZjRmV0ZjRmpSYU53Y2FwdDZmVThNTG41R3JheERsNVZyKy9mN2FXaFMxeXdhVy9xTmYrVVAxK3FvVzJNbUg5NW9hV2pZK3V2MVI5SC8veEFBakVBRUFBd0FDQWdJREFRRUJBQUFBQUFBQkFCRWhNVUZSY1dHQkVKR2h3YkhSLzlvQUNBRUJBQUUvRU9Ld1Axbld0OXhYNWlQeFVRbHhJWC9BUlIyQ0pFMWppVFlNbzdsYm1PeTVZTTBjalkyNjNVTzRLN292U1dZM0NVQjY0cUswbXp6VnV5SEJLc0kvc1gzclZZdjFhS2RUMVRQSVRhWUJUM0NxTE44bHpER3ArbVg3dXcvRnhKZ1k1ZXpPRmRwL0hFZHlWaGNHcTROcEVTNGh0YW9SNk5wU0VTYVRVSjJYNnBHOS93QWRVbEhqd1FuUUJvaUxSMjIwMXRWRldQR24wWURRcGtVOVhkM0hhY0NBSS9HSWgwem9NZFdDYXdwckRhT3FuUU01eUFNTDBvTnVjWlZmTmJYTXFoZFlXRENPYXJHTnhJcHdDMGViaTNiaHQwMFloM0pyZ0Qwc0FVUUNPcE95YTl5OEh4TVNrTGg2bW9aTmdHZndDSU1SVXVFNFNMenl3WHNsSnp4T01HRzdqc0F3elk4d0xxOHB0SXQ5bmhLcDVoaExsRWpTWWdBRVhEdGx2MmJaQzd6V0UvZ042bDRkUXdjQUFYdkd3dGxVL01haWJ3bWhnODBRWDlBT1ZpRU90Z2YyUXJ1d3NTS1JNZnlCTmcrcGU3ZXF3OVJVd1VXaXNKdDA0Y3JHT2JSQyt4MUNodzl3bkszV2dsSHdWTnh3QVFoNkFBT2JOTVg5Q2kzajJTdHRWaUhLdjhjeWprdkdhRGs1SEhMUU81aG5BR01KRDNxVUNLR1NGYUlPaHhTTGZKcUcwbENSVzZBSW8wbS9vZDBSRzdwMjN3UUtUVXNvV2t1U1lRTEt5aHdjL01rVkYyUW5IRTVENk53ZFphUFJ1QjhMSFZNdkVFUDhQaEFkN0xSTFZhSlJUTlZLTE5GSWh0c0dEMG1nSW4xRmduaEM2RkNReTVlNXZ3dlFmUWJLYVkySCtSUTRybWVCeGRDNHQ4MHB2ODNEYnVvYitIOXl1ckZEdEF6MndyNDZ3c3l1NnRycmFvN3VvUUZyMk5lTERuelJHNG5MWmo3bjdJQ1pRVzBibVhyZ25tTnY0SXJOS08xT1kvUVo3U29PRmhpVTd2dW81cEhDd1lKWkJlTGNoQ2NMR0hMNi9NVDRKekZMaEdMSU04MGdxZUF3TkVBRml3NzdrVlJSb2pibk5ISzgrU09iTE1QS0h5dmdPV1VGL0tYTFNRNytVeFMyUHQ2SC9pQStJUDFLNWlVeGFGcXJ0eVNoQmVRSlJ3c2M0QnFNRHplSGFWeER4RldDeXlsQ1hoczNhYkNjSzI2N0w2a3dFaDZHbzlFZ3c0aEFRYWdleE5sbCtJVlR3bXM1dDlHdzhXWEwvak9xZU1laTVGRWQ3SG0wbkFJdWFIRDdsSS9yQWV6aGJ6OXBnSzZ3UFhpK2pUbHU4c296eGc5WEZJOG5CNWhFd2dFUDFjVzNTUGtrQkdVMVdKNW1iUDJzSytKRmZRcUVaN08yTjk5ckRwS3NUbjFFZzFOMFM2bzhXeDZYRlJYRVo3WThSNVROMFY4TStMV1lEUVdnSEtuTGRycStMU01UR1lVYWRWTnZKbGpsdWczY2VDVUNpNldvdEkyam5vL0tIL3plQXRjdHJRN1hnYkJPVEhjUndQT3Z1RTF4YU5CZlB0NVh6Rkx4UHkxQTgwb0J4U1JCRXJ1bFBMTEVDUzh0Z0t1YnptWGFDRitoWUQyamh3WkF3L09ybmIxQXNCR2M4YWRqeDlIekJOR1V4SGd2Z1RBTUQrRmcxcGxFSEMrWE1sRkJ4dW9zdEtORHVuSGc3V29aZnlqZFdOcFRnRnBwRFNZRkh3MVR3U080Mk05dUpWdUhoVlNsMWxyTkV2YVFhcjhScU1LcWUxY0VWc0xlbHVGd05hNjFzRXVyU3BuRlkySXVKWVhvY0dTMlRUR2JyREtscml1dmgxczR0T0tVZ3Fxc0tIZ3VZTDNxYk9iRXZ2TWdKWElQTC96ak8wcXBGTUd4QUJTMGhRWDBGeDB0dlhvRy9KRll3VFVOaXQvTVB3eXZUQ1ZvMUE3SlpFSWVvQWx0VlY3VjJVYXFkM0NOUVlTb0lRc0xiN1g3akNZUWRKS2kxMVh5TmtIQWtqcVNxS1JXd290c1pLUDN5SzJZSEJRTmhZbWU0UGhYc1VzNitpSk8wR1lOQ2lpbk1SVTNWQnQxL3VvM3JjeVdxNVQxRHd4NmxQZ3NYTEVjOGthQjQ1bnpoSUtiazlBSisxdEtaandlR2tXL2hJSnJUaEkwMnZnNWl5akt3RGpHRmVETjV4OVJ4OFZZaTZ3ZXFia0JTSmZzU1BnZHhyVXVjbFBPYmgvVWFTc0lFOFdxUVplN0xjcmFXNHVSVVBOa0ZDNkJudy9Nc0JGNzJWcUtwbFVyeUxqeHpvalFneDJjajZGcmZXREs0eVBzRkN6NFFHSDZBaUtEYnJXRDQ0TmxLUTA2V01XQzNNdVA3TWZXU2xWU0tMOGhkZ1Z4YUUrSThFOFM3MVFCNzdlREJuQzBQNE0vaVQwNjJ3eXEwM0xNZjNVYlNvc0JwQjk2ZHQ0Slo4TndKVEpKZTJMQmdjaXdRcTE1dXI3SlpSSFZndHdXUFpZNEc0Tk5iMlgxVXZvOWMxS3dHdUMzeE52UTNLdHA1a1M2NDJGalVJSFNwaWVUNXZIUkZGajFOVzhneFdhMFlXSEYvd0RUN0pPTG1BQUR4Zy9wRDBhTVRCcDhrUlZwWUtpRENuSi9JdjhBNVh3TDNQYVpoRmhSNVdLU3QzYkdpaWk5Z09VY0taWGVhS1VJN05MOEhDa1hMb044Sk5GVXhWMVlMN2wzL3BCcEdyaWlrRTJBb0RFc2RvSkVwZnpkWHdLM3NYdU5VQXo0QW9KV1V2WEx5N05NWEhnazZQYkRZN0YzREc1eldiM0J3T3RHMW92NXFXWmNYRHMwenJzZVIwanN2aVZGM1pBOWd6QlRoUWgreGhoaXkyaDhXcC9JVEM1Q1lkMkIvS2ZTUHVtMm0wODNGKzMzYkZxcmhhai9BSXdlSzRtTkxTMGFoZVlXMTVHa004UVRsZWovQUVtSXE5SCtLUWtuNXFMOFZVNGRlYUNQM2E0WEhkbThlWHlRbzFsLzc4aCt4RFI4WS9yVVM2dXhWbXAzWWE5WDNIUzlZN3N2UGo0SW01UTN6TDg2ODhEQ1Z5TEFCenNEemRSMjBVcGJvbFF6NXRzdEFSRFA5dEpmVkI0dnArcGQ4NmdsNXVtbUtoc2hFYUNZdVFFc2I1TWdCVXpJcWs0ZHRRdjBua3VFbEdrUys4dzJhUlE5T1dHZjVnV2c4RnhoeXVsRUtsSHZIWlEyeEZCYTVCUVJMdU9HZ0E5amJIZ1p0dDhrdUFnQXUwRi9Nb1I4U0Z5RVdvdGRiWmJtNnlDSzBENmliWXRrYnkwNGdKZ0NLYnZ0akczNDVROGsxZE1GZ3VlWkljYzVnQS9BalJDZW9vekpZRVRjRWJVZ2p4NWdMV1JpakZMd1ZBdEU1TFRhSjFDN2RmbTVjZ2JqWEpYUUU5c2NabEpWcXlPMmhRNE5ic0pjeElySFZvMk9NYk1pNFZGdUs0aVBDS0pSSDdHMkZGdjRscDl3aGtEcEdMaTZuR2hrRENtNjBhWjlyTHNEaTVrUU1HeEFWdTdYZ2lyOUc3Q3NsV2FTVWxYVHd4RmV3VkpCbGt2NnVGMmVLbzJNaXFnNjdxTDdIbTN5SlMwTGhMbjFOZmR6S29UdVdVc3BMV1FuSjF0MDdZRllncmxSNGVwVGFrT1dRcHZqYXc1WWNKY2dLTDNLdGVlMS9pQVg3QVJjUUZPdlpRYU5RSlVJY0l2UkUzUUlhRjBmZ2hMQXlWY3VwZUYwNTJUUnpxUk9WV2N5OWl5aElXVldIZmpYQzVTcEg2ajZpS0lXamV2WXRvWFpXN25GRy9EV3RYWkVFanFnUW5GRWJLeWpKQzFHdUdCSVZ3KzRaaThOcFV2b3VVWkJkazMxVlhHeWFoN001STB3MHRSc0E3QjVjbFcybWdVQkwvakYrd1Y2VmFzSGZtQk5mSGdZRnJoSllqbjVXWVM1dlRvejduWENVV3czVTc2am9WYVRBNk9rOWJGUUxZaFpGeTErbzJnM0tLQy8xVW9Ud1FmMTZ3Mm1GMUJES0FlaVBhc3MwdHJ1UXVnR241SXJjcFB1dWc0dW9sb1MzVUI3Q21XTlNRdGlFckx3VCtKU05rRjgxRkllUFJDOGJLNWR5c2ozUlJzVWsyTG1zRVRET2RFRDlvTjFXbHZpUEkzcWpoVWd0U3JEU1hUTHhpcVUwNFJJeHkrTnRLQUdGN0RlT0FVTUtlNHYyTU9uVC9vbzIzalpOdDVnbHNwRGpVUkhuOFlERmErcDVnK29nQUEzNlYvN0VLb3k0MHNQREI2a0YxQnh5Qm5aTHk0VGlEaldmOElLcVAxTEttdndJcFBqUHRCd0pxWWdHSkZTRlNLNUpnVWNSRnRMTW5UNngrY0ljNEx0bGdxeHJqTkIzaUtCUC8vRUFDd1JBQUlDQVFRQ0FnSUJBd1VCQUFBQUFBRUNBQU1SQkJJaE1SQVRJa0VnVVNNeU0zRVVRcUhCMGVILzJnQUlBUUlCQVQ4QTAzOXNUVXI4cy91YWE3L1kzNGpud1RNd0dBL2xmWjYxSmk3ckczR1djSkthL2xtRndnNWgxQ2ZYTTA2RTJGcHBXeW1KcXVGekszL2t5WXB5UFBma21Id3BnUGpQblh0aE1UVEU4QXk4L0NVY0FtTVM1eVlFQThhSTlpYWdBcGd3VmdHVkhBOEdEd1RDZkt3UVRIaFpxcS9aRXJDNGxqQURtVkFGZUk5QkhLelA3aVAvQUM0SE0wcEtzQ1plQ3c0Z1E3dVlnZ21JUEIvSlQ0TStvNGpWakVZbDVTdTFSR2JBeVphUmFjejFoZVZoSlY4VGNTTXhXeTNNQTRnR1BCNGd0NXdmd2JPT0pXeEkrWGZnUllUZ1JMQ3hJSXhHV0VaV0J5cmJZT3BxYzdJT29UTDZ3RzNUM3FxNGoyQWppYWJVN2hob0dEZFRNc1lLTW1QWXJFNE0wMSs0YlRCejRiVXFwSU1YVXFjNGlNSEdSQkMyMk5xUnVqM3B5QVlQNllEeEZWV2VBVFVETlppbGhEa3l4ZllBVEhSY2RTMEtEa1FFZ2tpVVgrcnZxQ3dNTXkvYllNUTBKMklEZzhTblVnRGEwM2hoeEFxRHZ1TWxaTXJaNlIreEVZT01pYXBqbmFKU2dzWWhvK2xxSFFsTm9LNC9VVWdyS0Z6Y1Q0dHM5akZCMUF1UHFmTDlSMzlhaGNaaGNIZ3kvQWxha2dqNmlESENELzMvQU9RVnNlelBXeTlHRlJZTUhneXl2MWdxd2xOSnRnUUFiRWlVakVOQ1k2bm85YmIxaUlvNVdhdkhFUUVzQ29semJKdWY2Z05xL2NvdlZTZDR3ZjNCZ2lLdTFpSmc1ejQxTE12OU15Vk9ZU1cveVRBZ1JSV3NTb0FSY2dZalpNYXNTK3ZmV1FleE5PdUsrT3pFVUlKaVl6NDI3VGtUVXFTNC9VcitDYnpFckxuYzBSUjFpTXYxaVcwRDZtbnRLL0F6VW9RQ3l5azVTTzIwUytnV2pudVBwaXA3bWwrVmdCZ0haRUR0OWllNUlMRlAzTW5PTVF6VGtNZ2h6QzFuMHN6Y1J4RkJ3TjNmalUyWXN4SEdCRllEaUZnTTh3Vzg4R0Z1Y1lpajV4dm5YaVVydFdFWmlhamJuN0gvQUZMTlNqSHFVTUErNkw0S2pHTVFWcjMxQWN5MXdpRW1hS3p0VEdZSU1tRmdxN21uQzg1bk9ZN0JWSk1DRyt3d052WE1CR01tUDg4N2VwV0JqR0lDREZIeXpFUHd6RVlNVGp4akN6Y1RtSnFmV05zMGx5MnBrVGJnN29GT2M1Z1VnbkptUW9tcXYzbmFPcFZkc2JkS2JWc1hJbVBCSVh1RzMzV0JQcVVZeVcvY0I5YmtIb3hsTGZmRU81Z1BXY1FxeFlFSGlCY2RTMXdvMnk3VlorS1RUc2M0OEZXUHhoQldWNk1NbVQyWjZXMHpiMGxlcUI0UGNGNEViVUlvN2xtb2F3N1VqYVZ6RjA3Q1UxbEttYjdHSi9xblRzUWF4bU9BUCtaWTFyOFRUVnV0bTVwVlR0RXRyVjBJbFdxd01QRXRSdWpDd2hjdHdrMVEyTUZCNWpManFWWkhpamw4eS9nSC9NMDlvQzdTZXBZVmRTSnVLdG1HMFduNVFKbFN3K3BwbjJQa3ozSkRhbUl0cWs4eTV0N1ppSGEyWjdsbGQ2d1hxUHVXYWdCRGlWcDdId1pROVFYYTY5UVgwcjBJK3RHTUtKOG5PVEdVZ1lNUWpiSGRpZTVwa0pZaWFoTU9SRkJIVURHRUV3UUZnbTBSUjRBakRFMjdoQW0zbWN3S2NUYVJEbUlXUTVXQU56bWMrRU8weDIzSHlUdDZtb1hJTFRTTHVmQjZucFNEVHFPUkxLRVV5eEFPcHRJTTNtYnpONWlEZEhYYU14YlNJTHA3eGpHSUgzQ0JQdUQrUG1lMUREYWs5eVQycCtvYkFlaE1reXoyYnlNOENXTlljZ25pYVNuWU4rZS9OMlFOd2hkaU00bS9jWVVKbnJnck1RSEhFZkpITUZaSW5yaHJpY0RFRnBQQUVhMG5qRTJ6Wk1FUUF3QXphWTZlMXVHSWxxR3ZzNWkycW93SWI0TlJIdTNLUkJTemdORkF6L2licHVFM2lJTi9BaklVd0NZb1NCRW5xUXg2VjJuRVZTeHdzMDZaWWx2cUd0RDlRVkorcHNUOVJnZ2haUkM0TXJUWVRMOHN3RVhURVQwR0RUbVBRd0VSd0VBaVZ0Wm5FZlQyZ3dhVzJIUzJ5bFdyT0dsdVhJVUNMcDJnb0lnUmhDakVHVU1GWWd5ajVzeEVDR2JUTmthZ21OcHpCVHQ3akQ1R0RMWEFIOEQwWXJuRTBYOUpqWWdJaGFXZjNZVC9BQ0wrS0FGek5Kd3pnZmdTQkNZeG4vL0VBQ29SQUFJQ0FnRURCQUVGQUFNQUFBQUFBQUFCQWhFREVpRVFNVUVFRXlBaVVRVVVNa0p4RlNOaC85b0FDQUVEQVFFL0FKL3laakp4OHI1cnBYeG9SaWp1NlJLa3RSZHliSXgyZkJIMDJSK0QxS2NNU1RKOE14OHMwV2xmTktoZGErUHBGY3JNNlZ1aVBja2syWS8rcFd1NSs0bkxnOXlTN21lcjRJT21SbmFKTDRvWHhmd3dUMEp6dGtVUHZ5UXlrSEZMaEdTQ2xpMnJreXEyeEVYd1BuNExvaS9nK3FFNkxJL1VrN1pGTnVrZWgvVHNrK0dmOFZTcVRPNkl4VFk0MHZnbFo3ZEsrcUVTanIxWWxZNFVyUW4waXJqWTFSK25SakxQRlNQVC9YS05jRUhkb2hCdmtqRzJaY09yNEdxRVk0dHNVWEZHVEhYS0tFUnhOazhiWEJLT3ZSS3lPS2tSeHlvZmZwZFE2ZWxsS09WTkhvOEdmZmFmWTdJamFJTjJSYjZUeE9Rb05HSzQ4aXlOcW1lQ2VLK1VLTFFuSWlwMFNpcFAvd0JKSnAwekNpY3RWd1F6VGZjbkJwbFVTZFkxMDlMQ09LUHV5NytDZnJzMHY3cy9jeTh1eUVYTGtTcnNZMDVFcVJPVVlybDhudS9nV1JlVUpwZjRRZDhvbFBWOGpuYnRqbXhaSDRaSDFVbkQyNU8wTnR1bVliNUd1SGJJWElTaWJRZmd5WVhOZlRrcW1TeWJRUy9CZFJycGdWbEprS1ZrcHVUMlk1TWxYZE1qclZ2dVJrekRQV1JrYTJkamJadGFxaE53NEVKMnFNTDRKdTVhSWxMWGlJLzlJOXJzaGtjVE5GVGp1dS9reFUzeVpGVE1jZGpIa2NDR2JZeThRZEYzM0hGVnd5T09UWTROTm1zYXV4UGt6S3B0QzFYY1VZVnl5WHQrQlQxdWhXekRINjJSNVpvNnNVWCtCeGFGSGl5N1F1SkU1Yk1Ra1k4YmlaRmFycFhGa0pOYzJiTmxVNk1jZG5SNm1QOEFZakZ6WWw5cVJibmFvcE9QSGNoRnlsU0hKWTRvclNSVDdJaitHeVRmZXltaDhJa3ZzU2kwcmZSTk9WRUlyVldQQzVjbWZFNFNZcFd0UzFWVVgrQnR5LzA5UGkxVms4VnFpZU9VSFhSSWlteHc5ckc1ZVNkMGtOYnh0ZUNFcTc5eFVyMjVFNDZ0TmNqbGZjeHhjdVRGaHJtUm1pdEgwZ294K3hHYWwyUDNPcnBEeXh6UjFaTEN4NHJJNFpNaGpqRG1SSFBCRHl4Wmt5WGtqSHd6Mm95N0R3UlNzeHFFVFBLTHh0SWxrc2hOeGRrOENmS0hqa2hSaytEVlEvbVlaYnBzVXZCTko5SmNJeEl5UTVzZ3FZa25HalZ3dWhTOEdXTnhOR1FpeVVQS01TMVJOV2hRWk9EbzBaREcyeHkwaVpGTzdpelRJKzdJNEhmSkZLQ0ZKTWZjeHhpdkJrYU1VL3FOcnljQ2xYUk5YWTNhSzZZMG0rUnRRZkFwcVhCckJEYXN0Q1ZGcVM1Sk5DNlBsRVVrdWlkQ1Y5ekU2NE1ycFdLYkZrZEVNalpDWDVOaEswS0pyMGk3ZEdsbnRpeGpWTXZnZjJGQmlpeFFacXhSS1NNZWxja0ZHdURMUGJncnBEdlFvb2NDTXFWR3h0MGlxRTZOeFNIenlhQ2drYkc1ZG14WlpGNkxsV1Fsc1BHMkxFZTBSeFV6M0ZIZ1Z2dWFsTVVXU2VvbmZZcVE5alprWnV4dWx5Wkc2NEU1RnkvSXJJcVFvc1VDY2xJeGNLeDVvc1dTSjdzU09XSlZ0aXlSajNJNXNiUGZnTFBqTXNsTjJqSFViWTg4VDNrT2NSVGpaSk5yZ2s5RXJOMEtadVJ6SkN6STl5eUxIeGpZbVdKaU5VWnU1R3lpaUg4VCtqNkxvbUoxRkdaOEovQkZDUC9aIiBpZD0iYyIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiLz48L2RlZnM+PC9zdmc+", + "description": "Displays historical fluid pressure values as a simplified chart with background. Optionally may display the corresponding latest fluid pressure value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pressure', label: 'Pressure', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'pressure', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 15 - 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 15 - 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":5,\"color\":\"#2B54CE\"},{\"from\":5,\"to\":10,\"color\":\"#3B911C\"},{\"from\":10,\"to\":15,\"color\":\"#F77410\"},{\"from\":15,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:c2ltcGxlX3ByZXNzdXJlX2NoYXJ0X2NhcmRfYmFja2dyb3VuZC5wbmc=:c2ltcGxlX3ByZXNzdXJlX2NoYXJ0X2NhcmRfYmFja2dyb3VuZC5wbmc=;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Pressure\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"compress\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"units\":\"bar\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "fluid pressure", + "liquid pressure", + "pump pressure", + "hydraulic", + "pump", + "compressibility", + "compressive stress", + "pipe", + "pipeline" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_gauge.json b/application/src/main/data/json/system/widget_types/simple_gauge.json new file mode 100644 index 0000000000..1683efee14 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_gauge.json @@ -0,0 +1,21 @@ +{ + "fqn": "digital_gauges.simple_gauge_justgage", + "name": "Simple gauge", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Preconfigured gauge to display any value reading as a circle. Allows to configure value range, gradient colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 2, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", + "controllerScript": "\nself.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-digital-gauge-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#ef6c00\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":0,\"dashThickness\":0,\"decimals\":0,\"gaugeColor\":\"#eeeeee\",\"gaugeType\":\"donut\"},\"title\":\"Simple gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_ground_temperature_chart_card.json b/application/src/main/data/json/system/widget_types/simple_ground_temperature_chart_card.json new file mode 100644 index 0000000000..7e3acf21ff --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_ground_temperature_chart_card.json @@ -0,0 +1,40 @@ +{ + "fqn": "simple_ground_temperature_chart_card", + "name": "Simple ground temperature chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical ground temperature values as a simplified chart. Optionally may display the corresponding latest ground temperature value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'temperature', label: 'Ground temperature', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'temperature', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Ground temperature\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#234CC7\"},{\"from\":-20,\"to\":0,\"color\":\"#305AD7\"},{\"from\":0,\"to\":10,\"color\":\"#7191EF\"},{\"from\":10,\"to\":20,\"color\":\"#FFA600\"},{\"from\":20,\"to\":30,\"color\":\"#F36900\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Ground temperature\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"thermostat\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"°C\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "soil temperature", + "terrestrial temperature", + "subsurface temperature", + "earth temperature", + "below surface temperature", + "surface temp", + "soil warmth", + "land temperature", + "geothermal reading", + "ground warmth" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_ground_temperature_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_ground_temperature_chart_card_with_background.json new file mode 100644 index 0000000000..722c95c709 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_ground_temperature_chart_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "simple_ground_temperature_chart_card_with_background", + "name": "Simple ground temperature chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical ground temperature values as a simplified chart with background. Optionally may display the corresponding latest ground temperature value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'temperature', label: 'Ground temperature', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'temperature', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Ground temperature\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#224AC2\"},{\"from\":-20,\"to\":0,\"color\":\"#2B54CE\"},{\"from\":0,\"to\":10,\"color\":\"#6083EC\"},{\"from\":10,\"to\":20,\"color\":\"#F89E0D\"},{\"from\":20,\"to\":30,\"color\":\"#F77410\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Ground temperature\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"thermostat\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"°C\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "soil temperature", + "terrestrial temperature", + "subsurface temperature", + "earth temperature", + "below surface temperature", + "surface temp", + "soil warmth", + "land temperature", + "geothermal reading", + "ground warmth" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_humidity_chart_card.json b/application/src/main/data/json/system/widget_types/simple_humidity_chart_card.json new file mode 100644 index 0000000000..32dc860380 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_humidity_chart_card.json @@ -0,0 +1,40 @@ +{ + "fqn": "simple_humidity_chart_card", + "name": "Simple humidity chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAhFBMVEUAAADg4ODf39/g4ODg4OD////g4OAhISEjTMfj4+PHx8c8PDysrKzx8fGQkJAvLy/k6fhYWFg+Ys50dHSsvOqenp6CgoLV1dXy9Pt1j9wwV8tohNlmZmZKSkqDmt9aedXI0vGRpeO5ublMbtK6urq6x+6eseeRpuNaeNXW3fXW3vTW3fS+UfxxAAAABXRSTlMA7yC/r1EOHTEAAAR+SURBVHja7M9JAcAgDAAwytEf/u0OGdAlDtIAAAAAAAB+p4/I181+HrHX83b0Ngs8zmS0XCVElUiKXEbkNh/79bIiNwxEYXh1OFSVLohG1ka9MX7/Z8zIYdJm3EFhIMJm9CPjonYfeGFNyNWakKs1IVdrQq7WD4Dc7JryV4g8Aw5pbrsgOCV1JyvF14LB9SFi5FESDIBScapU+YQkRU4YWR/y5Ef5DcQJvPMfxznxbgPEeXwMjuKdaI2uLdrTaQxk4Z6eIXQQqtCS2TMxQSlIMUWK0AVj8sxwFHQaAhFajrEyvSBsHSAZGwuW2CDKDdsO2cEptdNrCGThA6kUsvyBRFXNB4hrAgS2lyOgL0iObeo0BlJJKyWS6+nTeg/xR4iPlR6dxkAejKU8VlJPkPhA/grxDAgvCBIrug2BKG2LS6HhBMlk+gpBaEv5vSSgdOg2BILKPYdzXt4uj9MWMaY+BAtJW/GtFlvQbRQEEME3C8Hj35t/vxNy7Sbkak3I1ZqQqzUhV2tC/k+aQ8h6e0iO3LNwa4g8mZwAfq00uS9ELK6f82omt4VYlKPK/E0hv9gxtx1VYSgMX630Ly20tIgcRtFxO8f3f7/NdKYeCFqICcGE78YGuFhf1gmRTF5//kieVESkdIVV/ClFZPdDP1f2KUXEhjokLHoKkbX8F/V2iCdW6ROI/K4+IX3QQhD1pGQ+ItHK5iveE2Mq+VpuWB7/rcI1UU9K5iLCU8aUYszyrkfiDyKXMldKBgbAAyJ6V1ycdc+dIFypJG5/8s4bB2f2dHxhrWvOqZeN4I+KvFcAGn0+m8xrNADeBnkIH8f6qkhifz34aZarqydjHo8V2QLmA6j0+YwjOUpsdwYF0U4HRBJ2iuL1cijlviHCrJWfB3Fk1U/ybDRGRAMlka6wJcqAvTs35DCHn2tZezCh1WAviiS9qPxk1BuxsCu5ai2U+xVMyOEiR+CLiBociEqYvxxdi7xDj9jVryw5FxyNoZsJ2arkfLCIi7LGB1G2d92x9yJNpVuro0ZJ90nFdZ1Z7uJQIxq4vzci6+dgUKRwRaWBjDwGNTkymAMMNSaQEM5kp2NYaq1iKaeH4VZshjV7VsE0FWryvAHf/l5d7wvXJXexrBMxz1MhfIU8ShwPEylqtFSnYD9dx5/RpqRwq09AqLQMTLYrgczHjQNdUkJ/1k2tA60+If0iGaBduNXJo6ALdthrVLWpbpsEZ9MUIm5ceR/n0elsY2iLr9ZnSzfIgwmZQsTvjjcnUhw6Hm6FNCC6PYLz8NKbQMQZvP9mwq3FTgW5+EsU9H0jI9FmIo9wsx+AygA4/kh5Si+y1UQZ6mONrHfIM/FK0xAev6UB8LFzK70r4nDX9z0DPnHv7tMR/j+iC7pL0fdAJEav7jl+jecpExENY84iK6WGN/mcRV7siOaYs8gYFpFF5FlYRObGIjI3FpG5sYjMjUXkfzv3UQAACANBMLT88G8XAwigzDg4AbenMeQ0GWU8IaNd9mXdm/2dCFLUnrcrrQYAAAAAAMBvFtRAowTiuzzYAAAAAElFTkSuQmCC", + "description": "Displays historical humidity values as a simplified chart. Optionally may display the corresponding latest humidity value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'humidity', label: 'Humidity', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'humidity', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#D81838\"},{\"from\":20,\"to\":40,\"color\":\"#FFA600\"},{\"from\":40,\"to\":60,\"color\":\"#5B7EE6\"},{\"from\":60,\"to\":80,\"color\":\"#305AD7\"},{\"from\":80,\"to\":100,\"color\":\"#234CC7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Humidity\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"%\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "moisture", + "dampness", + "wetness", + "humidness", + "moistness", + "dew", + "water vapor", + "condensation", + "dew point", + "steaminess" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_humidity_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_humidity_chart_card_with_background.json new file mode 100644 index 0000000000..e5f8c1c079 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_humidity_chart_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "simple_humidity_chart_card_with_background", + "name": "Simple humidity chart card with background", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC91BMVEUAAAAEBAQREREEEAbR0tYHDgcDCQTW3N0DEwXMztPMys0HDgPz9PX19vfCx8Ls7OvDyMLu7u7y8/Pt7ez39/jw8PDr6+rDxcLo6Obq6unEysLo6ejn5+Xx8fLk5eLm5+Tl5eMBCAHd3uMCFQHFy8PZ2tYBDAHV1doiSsECEQLk5OnKz8jY3ODM0cjg4d7N0ssABADf4NwfHx/i49/h4uDR1c7j5eHd3trJ0MXf4OTb3djX2dXd4OTY29PP083m5uvGysTj4+DFyMTi4+fW2NQCGQHP1MoBDgHIzcXq6efLz8rGzMTa2t8DCwPg4eXe4NrT1tHJzcnX2NzT09jT2M4DHgLGzcPc3OHN08jQ1czOztLc39jW2dLW19va3uLa3dXL0sW9wsPJzscEIgLFzMLU19PQ1NDU2NHO1NfR0dXX2t69vbzHy8bM0dTJy87n5+zh493N0c0IFAwdHR3V2tAnOCMLGQ7M0cqHiIfT2NsNHBEHGgfZ29nS18zV2t05OTkFKQILJAXEyMsaGxnR1trIy8kgMB4HEQlrbmpTVFMrPSHX2NjQ1soFEAPM1MZrc1wwRBo2WsJeYltmcVJQXks6TCsIFQbJztK8v8ATKwkcKhyvuMN5fHdnaWQQIg/Mz8xea0rp6O2bl46Agn5zd3OAhW96gGhZXlcrLCsLHwwKHgViZWFOUk8uQSaXmZcxQiwkNCIjOBkXNQmjo6ORlJGEiXdyel42SSYdMhUWKBWztLFialVDUDApRBF2jMOtq6ednpxKWTc2Ri9ZdsKsr61BUykMMgO4ubRwdG1XZVBFR0NPXjg5TTdIWi8fPgu/xchdZ0NEUkEGCAKgoqBLW0I+SzAwMjA3ThsKDAS4wMNLasIsUsGkpJ6gnZZWYUNEVTXExMOGmcKysK2qp6GHjHtzemVWW1EXIBQZLhHq6u6erMO4urqkpaVIT0mPocWNj4jl5eXBwb+ztramqaclKCJpg8JveFM0OiVCWh5AYsFifcQyTw6yvce7I7RJAAAADHRSTlMABwXvICXBt7G/ryS3/DEGAAAgSUlEQVR42uzBgQAAAACAoP2pF6kCAAAAmJ06RnEbCKM4rsKwJMeYJiB8AcGAIWqNCqcYEFh9ZJDqbVQY5QxJt/gablL6GkKdS98g731P8cyyR1j9JWu0Xtbrnz/Ja2tra2tra2ufrs2Xrq7rn9aP2Lf3HY/HA+PC2kPbtvv9PjDnXFEUFTYuqGR5vkvrd31/sbDeL9h7hCfxu5yVZcXsZXzhneOupWlcExpry+wEp6ptXzZwkCGJKLGEwQcJSmdwqIUSq+QwScq59GJAcb/cSSAw30WGGSrvfeUdGcw1gjg5mCnCf8YwDOMm+9p1v+toUcYQRAkCgThKDL5ggIMUXygOJVfkiARBb4EBCB1MjMogMDB+KN5hQwWHEihB/F+yyMBtsF6y7lfXYSjqCTEKJThECDYmEiHtMpEtZ5LGoUhBAymAMPPIYUBGhsbh+XHwzZuDCAaBgoKzEAEHBMdhGF5fx2z6fj53KOUIo1IJHVgsXVttcnUtFq7em8QwZb5bQEwQjoNAMWwYusN0PYEAxxPiHGcRghnCVgkxtFSw7PGYaJmmP49zYqk/UCiQA6dUMVNIogyCcHmBwt1EiaS34jQQFXR4c2DhSpbnEY4QYAicScqAQYxxBORk3W7X6+12mjiXN1k0FdM8R7Jw9BNGEocCQ8QoUKySPSE5EIjTUnTws7cH/ooQECgxFFlBk5AEqxyIDpVd0TzPf9E8X0+8zN5qTSa9720Koig+AQk1+6TtYgmUuAKZRBQTLHc3EwPZtwSOetteI8GqswYKRoUSQ4oR88D+jxa7/WmrjuIA3j8AVyhSW1ra3pQ2aRBKL5QuJSMdgfBkQ1FLREiMTMBIZ4ZEVx3aKepsCNFoVDQ+BaM2svBCUCNvxguiyYwPccmiyWbMMJmJJKIQ50u/53d6+7v0rtX48KUtcDfa34fzO+feYhKI7e3tc0z5rPOpooYRDi7Kw5rhSbo9/DAYCDc9Hu67WdQGN25+OBCuiux9NsiAIc4bYlLJaAzaWLdIhxhUFOngmM6BcWn70qXt7Ss+T5/VbK6qqampQyyFVBhyww30UDY33ojbf5AST1+BWOyPQXAvPhiyDcbly5e3m/r6+uzW6qoqQK4vuQE38cEPuJeV/H8QbQV19z385L0IOKZtODYvLy8vp2w2SKxmKTFSJEg+lsr/XxE8VIEgAsgl1IMgR50ksZshMewuaZG1kBX5P4tSRkFlsTx5221UEUCgQABxOrkkkBiKIjGGuvwriNZIhiKWLQcjOFBwTJe/XoYDcbPEbrdasb/KS2RF/o3kr34EMEMKAuboIL9/+a5wvOMhSR/HbjWjKHkJLHXlyvJPdleJ5clxR/cSTV5ckeMM+eP07+T48UUXJASxOd0ep80uJQVPXYnOx/P/NUR8w5vHYOCIJxaHSne4FEgIJLCYPjj9xS/Ly788+8ZRl4c2l9Pjamk66rHZeXcVpdSphV/jeiSdowQABIp4cpaU7HAj5ThCkuOmV1756N13l9969pNQC9XE6TkaikQjTZ6ItaSEKWViGMJy4YZYWIHQq5HEyNUURgcgHIJ8/v0LHz74ykuR0ImjLtfRpsG59q72aOp8mjpeo0yv4mEhvcAUPebWFQd9WlLnHCtvGikUctCSOXoEhxCIGJWWwo8bxwqnFOTxYx/89NwXj/901ReNhJpCgxHfxORk4teLs7MkIQpZ0hnSqNPGE+WbK3MaJPtrxWr2+hZetPxRDSEViJTo55KsQnnIsWOnkh98Gjj10lS7LxqN+roSHSffnMnMIuvYXfz0ErK2ULe7tovb2trC7tqGxbKwtluxu7OxA8jO3NJKbmdux1GBu56Cr3nFxQO9Tseozp++iPl3YhF3CUke+zR5+lgg0BwOJyamJhLhmf6hodfnZym/2c3VCLZYAaKu1S2o0wtqJpvJnM+qWQtKYcnmsjl1bk7dSWfUrENdrQCruBGwYo6USIVg5CWy2OUVFlHU4y+//PLxO/Fg8ga6u73ebsXfH5zp6OgI9o+nUhvzF9/e339v9qzTYUdQmLQqIiGrdVfUIctrOcu0urCkblg2BKQinbFYstkK3PSRDCHRwghWmEWE5GAbXhdExzlgQCEgUHgDfqV5fGiov79hKJWKda7MP99//srG7Oy02+l2udwOczqHt5GrAlJDEHggqEurBFlTLSgMQSwEWc0t4KsDkYuu0YcNQGih4lPKzkfZYATRYgr4e7oVJRBXYrEUEovV1laenZ+/eG5rf3Z2rbWlqSnU6nGkM3h6bK0a9cJBSJ2A7JIHBwVkN7eCAxQ6jTIDAQIfUlKohDUfAWFraQyXQg+5/+X7KaaeQI/Xr/gVpZNSW0l5b35/b++99fnZc5FI1OeLNnnSmSoBqco9U7NaDNlV0/iKIDwUsupKfu8gshHozg+FriCF3WqnCIloE46GYQfdKAbI/Zwn7jf5e3oCAT+21qGbDh06VCmyOn9xff/1vfmLia6uiUSiK9r6dgavMK3+XL2uqtliCD7h4AIfVAHG/pMhRXFkLewODiSiJFoKGlDKhRAiJlTDH4g3x/03AYIQBntL5JeZyfDMzMxkV6TNgUasNuPXF0pUGfvWs6A1s6urpmYrZ1h9jd7ACioGMWyIQYL8PQkcGsTfE/A+5Pc3AyIlK2BcXB9vOIn2bzg56Wvy2PHKDpvbCRFLCidL6eIFv5Z5rapUJAIKweAICCQyklJXjgKCyFemHr8S91/tiR+A4GFrq7IzlRpH+6eGghPRFrejz+ZpaW1zO6ppvbL0hg2UTrfKdVMOOPQKdjidJKE2kdGK85dFAeGrr3A/ZPIqSo//qqIoBUfBUxuLxTorOztT/ZPtkRYXroojkVCb01pNEa9kbARerURw9A5WSAY7ELsWg6RMUWCg3H03IM3xxTeU5mZApIE3GCYxqlMZGw8m2kdDochd7e3Rpjab3Ypm6cMrm6uKHNWGmPGhQZgBBwgyOghZJEZKSlFwEASR70zdPUp88WmcRw5CKslBEKpMqqEjMXbkSPtUItEePYH3X/Tmy+Xi9yw6iMFA4f7V2sPo4B7RRZPIRmEKUsRA7paQbq9y5mpAicseEQouCCfW2xAMh28PdwQ7Er7IiZaWtrYTTaETbTazDlJTcPAKzFqkDCEHLV+EqyEdMrImUsIUqdAgS0JiUjB8H9lXmhWDQ6a2vrehMRhsbGw42TF1JDo6ODg6Go2OnhCNn7fIwSpjhIgTBzncznwYYkhRTSRFHxwhA0O8yZ4HrqLZ/QWIXiCCz7He8cOHD49jHo9MjR1BxvBpFKNMe88iZ5JMfpxKhraxnIBICTtwXEY3jfXneoqegSx9xwVZMuECJb5/NR5XhIEhekY9S+oHhoeHBwbGG4Ij4UfD4fDISHjKN4irMKvok0Iz65NfivRxp6MgWqDh2SvHMYUhcnRJCUc38+/OOwgSAGQRzc4ECRGM+nsgEd/UI/cMH25ovOOOYPBkY2NwZMoXaWpzW3XDtYSDqyEnVsHBEAdDik+PiCwoS4whBUOSyVNnrqEizZUcWRPBQAjCLNSlt/cwMk452THRHg212SCR/WEMzV+tHMJhgJCDiW9uvv323q/kMEjYYoSIbl8CxNudfOPaI/G4V3MgWkHuuQf76SBkmDKATTbQC0m4ay7kcuhnlLXYwQy9Q0IKBbGKf908OyuS2dQ7mFKqKkuc6Z9NgaT3/Y8f+NbvxToZgugg+YrUMgQAHKmnxolRw4RxZewWv84+LIdT1OAUeaUrGYh0IJHs7Nn1GZvth82zamaSjugtxl7hr0EQ+c0UX1w8s/9tc/NiLQIIBysniH5ncccMDJCDJ1mqFxJfyGVziPeRTm1LSIiUcD34DKhnwME/MZnJrTsouGTYzJFExlAWGTIgG1smJeA9c/6BuPdxdhggtXoIAokoSP700jEWaXW52lpDIfQ9rYol0mGcV0YHNdIz6g/0X3j0TubO2xEDxcwW3XhhxdbWHk6IyYeufbt4+rRYG95d4QyPx/xeYohewiWpZ109rsLmQq0toehdd42GWjxOHp2aRDJ4XpV2VF9Q1+m0r+Wcui4lxRf4+nJfEYq9vUt4q3tq8doZ/6lAvkUAAYZKIiOHMUkoeUl9b2MYk2swOjaFMz7+6OrGILIxRa+QBXEfaA92VCPnMzza8rFncxEmEU6QeIJT9E9MjLULFzY3Tf7k+4vXvlHicZ5ZgCCAiOgh0kFzS/yRAs3f0DHli941dvvIyO1HUBOkzeVxy26h1+PXp04vroddK16nekGWkCCfqZvS4ZDFKc7e3s6FzVXEpHiTx149s5hMsoMhUiIjJcO9vb0DsXo6MHw4GJ5qHwt33HHHyNSR0UEK/obscSNYKAzGraVd8pJWuy58Rp3QX5Eh2YzmQP9jJIo4RPrwva3PJoJaQLG+uv4nofb602YZhgG8f0GDMfEUgzaV5m1IgZVjRiCDrNTNEFhCCY0kQxogZn5gAoMghCqZtgTYWhGVUmZStGwguGXGMTbEMeWQzcnYUMDoSBQNyshMMA73wet+7vc0avRyQj0t78/7uZ/Ti8H8msNhxc1Wl1YQhvCJRA23PkNKAXkFkucAseWmF6fn2hJtJKGgW4qSUlPT0jLin5Yl2m5R3e/ysFIcVXYfd69GmbHPaA8vXqURQZsvnqE8++zoec6WweXocnQVmp1dakE46mqiORhCFJQEN2D4WErb4txkG1Z7W1lna4lIOTbImThMJuHEwgxVwgZkZmpqRmYgi/ZXHuFoNWnzswMPj0BCXxnwLCU+Iw/ZamqKNG3hK1Z285kBi7mLIejzGIkewsNr377n6SpP2XzZEiufLy3NyirjFLhLystzsrNbMrGn5FbWNr5QIOeH7EjbIhgibf5HdkOenLLPoAxwCAD9EoWAQBBwdUiJRPojTcBEDGav9cyvdLfFEC3aGq+HUPJxlgeEJOiXrMoscpCEY7MVuFtL0ov3t2Ajlpn0qF7CJ40mu/+34pevztub+Ol9uPmTo52/ModGDjzx8E6AagFFGiH24OiN9Pf3R5DBfoPL6+g9g61WLztiIMqMBQiD4oSEQxDKC9w6HGwrURa0TvbV8BD+v/uojxnCc9mUvYnHVJN9/mpV1aIAcVQzGjpS94fWDhw4wEAhSLF3b0tLy+DgICz4avjK6znzusNq7TXGQPQSDkPiBOQVKssL7MAcRuEFkyQV6Jz0/rqhwfNNQbu9SUA4tBlpg0JksU0MsauxDryTbRkaUR4fvyjxeKOWlyoYcLQkZGdn+2CYncWfhq88nt4zLnNhr3DoKTAwBImBULvj2ksEczEpRHi0kaRgsH22mjb5xUH7eXW1wzefvVjb/R1ZXDyibQHV0h2gtt6qizBACTny5EFFjP3794d9Pl/YN+vzAeL45bTX0/UaKTQIPsWceZUIBwCHDiUmHkxMZEm+DGEHIDPtvuxMbI0xivxDSdoGo9jezwr9DUyM4wlqiMG6SLyaI6Ojvx+BQ60HHFVV0SAyHJ714dWbZ/yYx1P4lkq4Q19CG8uqIrQR0q2JoY2X4MBk1djYeDQZh8WDmLTQLvxP973AjoqK4NKRcr4Fe6waTcGBqq0tjauhSsjEDhmCS02enyJ1baN5lIuDQ3xUCe+jgrCDIOvRqB9/Dg9j+vV2nPN6Cr9jBS4W8+/cmVyVJGklJBjic2BNhoRW8Fdrz2fZqJ2rq9OrcUfUCAk6Jh+VIgdDjO3BTuxakuKfxkw1MrRH2cA22Rcfuk2IudzWQ+JH8TJzZGRkqG4oOnro0JHRcF1dOJ/6PAEjC46qhc3N+U38G1GDx2t+3evwuHoZkk+QbTw5HniVJPJn6S5DdqTtjYCUX1awcaOkpgq/VTHWdZJg9yUcokGQrfbvO1vrczJrM9CuF+t88nkrn9dwLoL+E0PYoUHiMy4O4jHDW/vzEIyrfFCG78ChQOY3Nzf9I/51g9nj/crjchXe1CAhSZo2GkOr0nZc3JokTcbF4fMKd0rgfmnWXeluQWcgUJ6N3w4VJoktkSYv3LJUQiEyv9Rd0FlSj919bW1qRqTOXw1IUtNQW3Hs+wa9gyDcInAAkpqWhD/ShCMtCaPqzjAo9xTIPOrhjwZPnMCrN885bLfMhRrkriRtoNNXpPtxcdNSII7rQt8IklUBSOek9EbOXpGE/TXp7mQbmp6SmMiOmfZwN0toJ1mbMdVmn/fPD9n9xbvPeLs3WdrIojUcECwbaakkSeXlI+HecN0mIEKysLAeDYbDJ6IGS6HD63K6XK6HICEjGVZQkck10SiACMnK6nLFtrRxQ9rJOfxiZlEm/ca4E6ZOgQWx2WwV3RXdW+0z3d1lBa0l9WIXWRt/airo9/f/Jl8DxyhirvBkSB4gLCEIKiK6495FMNDtokeiw8H1YBA3jV6vw+rxOBTHvvyXMKiMcRhgZOAEpGmGrEmB+1KgcyWAghTtoRTh9y4vTncLCkOQ+bZuSidJQN6T98wTB3i3qw8btCtvLogCkUeWDEkDRBQEAAogkNza3IQiug6I2YGX006z16hC8tdW0eCr8rPLBdlgyHNr09OTL2BwlRMkCSEJblDTURQZUkYjK9KtSuohQc/jh6hiJCzgyA55ZKkQuSKpgOBmgCE1BBGShYVNfzQaDQ4bLA5QLA6XSwdZnpaQVS4IBtmG6HiGYG/YvRzYKaGnA4Qk6uiy2eCgVETaZ1AWgkBSUo8jV2o8JNoZODZQKAcwhiBiaIGSRqEN7x59RYjSgzkNFcE6YsLO12IliFGBLAewbmwAA4kRCQXQ9nrIjnTjwfTK9LtqSQ7nAOJOhkTO0ryYhAVE9ElRLUkOqCct/dlbY/AJhOYshlBJUAt2KBBI2EHd0tOz7qelHRB6N22FxMjLIbKGZsfzT0urqmNZg5RmbUiTN6TV6cAqJJQiNEkNNUmZ4hhs/zwLIQgkXL3aPJKoVzz4gF/6qAdChiBCgqLkQZNHIkBYkk0OSs/6OgZW2Bc2WNEjmLdMTrGuk4SmK9r7kocdgRAz+FY+KxDo3pYe1IekyRch4R6hkZVcVqY4Bm02OAApUCAoCc6+dGRVor1pYwcr6CTI7x3UPhHB9EsQlAQSYnAmetaj64D46Ec4cGvqEm+s4jg7UoAgkwKyfJ8dHJyvSiel5e4VqbPkDWl7byaylwpC86+oSFmZMdweZBAdF+GQIbV51PCgHODnPYDTHwKPAuMwBFHmYE4GQ6gkHIYE59Dp2P8aUjCuzGaPKUWDQDDJlQBnhXcqKuS5kLSTVbEjvdL6QJo8jAXxcEIOOxr5yb9favcVoDgUhvBSsodK8gxfA4HB4UfXBtWuiKJwIFEgesncxImJYV94dtCQkmJGXGYNQkWQVgO0v6KyKJmWKaHtB1nYpUxjNribk5OAl1flNcVubIIPHsRLE6Ovfen73OQCJW63PLJQESFRX1XRB1XDDq6FHvK0/myYqp4MFUZ2wsRENIyBRRCsISiJQ33RgyxPE2NlwxgD4cGFmYv+/iQuGaDgCQsHE0gWUY6ncilut1vvYEheBl3fITwrMQoF4usqwVB4UOkl4qxODj0ErTI3EYQCp12DxYllxGy1psAhIJxQaFn+FPtmVEjilo/jIaGgRR0OQBLvhakcHLdIejouhw7vBQTTgqgJKPE8VOi7+s5KN1kpRWGINhFjZWQHIEDwgjg3EZ4VFxCGwhSz00kV0SDq/da/QIwk4WM57n3SkVxqDzAOVU4ttYeNyclgESSdQo1OBeEICsoiJiF8YAo9NjuoufUSvnhgBjsYkoAQo7yqqmfuxGx/BLcPBpMJ85YFgyvGgcQ6jLIEEEgK3MSwgYFz72z70mJjYzIHkFYwampEQV4soj8ooBBG/obqsARhhzbgaJRpi4naHwggfD5EFnqGZ1ESHHW9LpfFZDKZveKZjf8N4RLRnS9JcErEI4tqUEbC9w42KoGklR0EQfbSV4jU4I1KrSiK0hXkUIebUgyEGbKiBWEHFMXFxT1zUR96/cOPDC4PFhKUxMUAjcEQTaIxtcFlQxLh4FRSu1PgSS4gSH05QR6K4LAJEhSFJHJB1MahsIJ3joIBQDYijytSVFdX98zRBBz8+UOD1WEypVicJmssRJPoHTRL49KHIBWq4zhBEpXAw5BygoCSo4Zd/J0oLEEN2EEQkrABf4O7Y49626Bse6uIQZCeWz/88Ofb6xN4GWrFjwZZCmMgqmQXgyRPyZCKLHEwBAIMfWxlgKAg4hIYT11OqRFf8Rf1FFC4KBlyEZRFXK0EKRDeKnINkCqEGJyFhVs/Xrv0zQeXDA6riSDW/4Fo/wRfVIjyyp0cBxFG4BdDakjCa00NbunxFPguPqJaQkIUnr3kuYk/MkFMb7SewyFKwCENfc9Fqm8tLFx755svLn1pwCY+JcWUYjU/To+pOv4Lgr9F85bI85V05Sgcp1hiQxgCiTCIxUYJKdLdvFBiHlCKEk8DicOMvFRKHm95E7Kr8DvkHqXkMuco59q1a2NjY5/91WFwmhBLISAi/KQxkIdqJUqCbgcDAQQSKogK4U0WQXQMWh3xC5GXfDG6qCgkAUUNGMohBN/gaIGjWjBOnTrFAPrMkCsNHW9eOHnSgD7HLsXkJAgjdpeEFTqJXJJSmbEPkOPa0GIGQiUpgUIwsPyrKcCsDUkJSbjn01ii3+jK4QPofnZQzVF5fcbGGj45e+FCx0m8nsbBCrsU5wwzdlckBoJwu4OCm6x9FEVCDkB0G0YoRAiQnMxfKeSEhJcZ9AnWe924StUgPLCq8d8dpX0pfu5dn8cbPunr++Ts+IWzOFilmExOl8dxHg+tRu+IhbAEFGbk57MEYUinKkmX42YAR4VAAgpKQhCtJtQfuwvCDproMdNr2XrzwoW+hjfHB341WCyAWCxmc99VnURfESSWSMu7CDlIIjq+UoZ0gkKFUSFUiaO7IJ0syaHrmFqlJGqjKw61Q5IJclwLoS42/NI83tfXNz4wYLBaC/EzTpiAzX39M9pzx0BiSyIoVBBFUolkVeghkLACjt2QAhlSzhAcZdWCAKIVhEfWUS7IcX3em/qkubl5YLxvvLn5tMGJZrdYxFpiwZLisDqtpkIn2sZ187ubN7tcVqvHbDF7LFavx2OyOlNMtMXEzUuX1fWaC0fL19/vLSx87fXeV48de//r98+d+/rry3///de3Jy//ffnyyZOfXRq7ff3Sldu3L92+MtbQ0fFmR8PZsx0NH3985crYO59df+f69R+/+OHtnz/86NOfPv2Jgu/4IL5EsBf8+cTEHBbvKw0N9B+PNx871jzQ/Crl9OmB02Dg7wz8MtD81qv/sG8Gq2oDURi2lpZCn+ISCGTXlwhZZZknyBsIycJszEYhIAoxgXQhbhQMBBOhJnJ1ESGCpIoVra50ZesDlNtCN/1PtGIp7aqbgr/jTDIzxvPlnzNXLopdC39GwEESWVbI/l/HMQy+L79ardBytBNg6aEVaCKmMjRLrMqyrru6Zq9EeVrSC4WKozt9oIzH376Ng+gU1OtfJ5v5JokPk02v91FRym/flt+WjdCaoCNM5kkyj9cNczAYtN9d1G6jtDsgaXeOtYyj8WHS8wwjGA6LGUEXb1Xpul0HTOApqs1uAY4gNKDQE2IZVhBZtFwVILsqL3PcA0/f1hYEngMUR+ZlpMyDyPASz0wdG0iy1u2qn3WQLKDxdhwEUX27tXqjeWKN4MdmMvEBARIDBNbG8jy0ozTd72e1Qa1D8QOj0+m0j4PBkWCOj7PZrPEJIO97XhAFwyJA4DyqiuaCAAxqQYUAlqPICOMs3Hu66SKB7FY7DaFKsiQhbFQ8AyYWAii16IBPpakrlyS5uqvYQ4A4/TH0/Xs9gCHbb8kmSUawYzKZeLirhuIrYW9ihVYYhvP5aARDGjNzYA46iJyiHxw7tVoNHnU6A7Nx1jrGujydAKKCAW4URFsrNLtqExBF6qWlRRQ/SURREHAMQ3iOt1c7m+HoUJIEBqL7L3CMgGRiAQsaDDOyDBBedrVqxelP+5Qm31FAA0eSxLLCJEl6Paxy3/MUiCojDC3rME/jeN9qPZrm4xMCz9yoQab5ZGK9mS18JPywbqzXca9XHkbR6XzzNV2raJVCV9WahJEZpeaeCxlEhgM/BAEWsdUHRGrvJFlg8PGFZWhQRFowDOwgsYARHwROxF4wnU5LJcl11Z2qT0EydRykSTCOAgOOfPWwlL55iv/lC5YViuIDqOwb1mh+GKXxftlqmWbLfBoMYEan9lgzH2cts4WyTOP30PqQppaivI2ixampNh1N13VNs7WuVtBgCJlCS+vlA0MYtHVlZmQSKfJKVeIlWCBwiBdJD0NkniMwjJIdSBycAGJaglua6Dquq9n2dKEj4aNhVDcMb2tZRh2bmK/4oKAMMXDgG+WyZyF34kO6JM3MJ6DUjtil4A8gYNRyHY/ekw7xCCDlKIgWw6bjaK7m6gDRtIrdxLoqEser3OvnFBcBUCNmGcDbO2y9OxGhAwDxyyWhWkXo4KFxmo8CwSdZKsk8jxQSS3oJ98q1sZU5i/7wcxTUt3Voi6IoIIEUw/P9rA09z7M2h0O8XqfLPVwxnwikZs5a0H6/T9P4YPUO7yejuWV5ilevB4v+yXFk+KHbFVhSUE/DQrGJxHn+Ipd/8Qo/vEBIJfYNCW4IvMaIAFmxsENiqghfJrcknuWQ8OdpDCeISH0ROUK4vFwVbZcvubZtw/qp0z8NIxgACsLZhgoSvez7PqpyEARl/2v41Qs3m/gQx2maLlvLlokkx4YLDHi032NFYXuL08PIQqIZRn0cnaJ+06E9v4v01rD1Dk+VSkF9/vJFPpfPP/v/lc8D5M8keXqeyx+Gb8/+UUjX6jYEVH99DTggwJDQcavz2QX3d6H/OgeP6wk1l3Po0nM7AmVhXXvPj+tVb9/2tpeENkfVZezm8rmLLtZcmNBQFxWqSNcpVBP75ZgeN12oqc10e73fX/xLhUKi4UvnTTm/BKI2a276LlHfddddd911110/2oMDEgAAAABB/1/3I1QAAABYCgQ9zRvDr+3sAAAAAElFTkSuQmCC", + "description": "Displays historical humidity values as a simplified chart with background. Optionally may display the corresponding latest humidity value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'humidity', label: 'Humidity', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'humidity', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#DE2343\"},{\"from\":20,\"to\":40,\"color\":\"#F89E0D\"},{\"from\":40,\"to\":60,\"color\":\"#5579E5\"},{\"from\":60,\"to\":80,\"color\":\"#2B54CE\"},{\"from\":80,\"to\":100,\"color\":\"#224AC2\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Humidity\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"%\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "moisture", + "dampness", + "wetness", + "humidness", + "moistness", + "dew", + "water vapor", + "condensation", + "dew point", + "steaminess" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_illuminance_chart_card.json b/application/src/main/data/json/system/widget_types/simple_illuminance_chart_card.json new file mode 100644 index 0000000000..f2490a6a1c --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_illuminance_chart_card.json @@ -0,0 +1,39 @@ +{ + "fqn": "simple_illuminance_chart_card", + "name": "Simple illuminance chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAolBMVEUAAADg4ODf39/g4ODg4OD////g4ODYGDghISHj4+NYWFisrKx0dHTx8fHHx8c9PT2QkJAvLy/64ubri5vaJkSfn588PDz98fPdNVH1xc26urqCgoJKSkrV1dXxqbTiUmllZWUXFxfnboLzt8Grq6vumqjpfY/kYHYJCQn309rsjJxDQ0ORkZGBgYHfQ13Q0NDAwMD41NmSkpLgRF1SUlLcNVGnmCfWAAAABXRSTlMA7yC/r1EOHTEAAASiSURBVHja7M9JAcAgDAAwytEf/u0OGdAlDtIAAAAAAAB+p4/I181+HrHX83b0Ngs8zmS0XCVElUiKXEbkNh+7ddMiKQyEcfz0SFWSysshHtQGvQndzQzLfv/PtqXdLLuY6dNgR8Y/gVCUl99B9ITU1gmprRNSWyektn4A5P6JI1WGCIBrXu9nlwiQhXHYNma8vzLk2j+WZPHM0gtIh/dXhjQ36ema5Y4txHug83q8vcBb1umiZ5mgs6LYsu0EEKvbZSxC94BoNBFNv1CAOIPlMmloyQxtEoT4mDxicq2Bp2CSAUIylNHR0AbBpn0gWR0qkRcQvZIDU7dCRghlRI+O2CspEywxXEBwurLYtAMkz59uhcwf4/wKogImu0IcQBEXF8IDYgl9gCY0GKOrTTtApJHbAyLSbCHD15DgxP8HERbqvfeMTTtANCFtupfekT4xhzJE1BL/QvSS0cAMLPFtEDQTTSNKEG4pDWWIKsiQf0KWqeXleTLvgyDN8w3lWPBVwoWJi4y9IE2u4Xv9DZCP628cqR/w93uwTkhtnZDaOiG1dUJq64TUVmUQn2PM/vCQnGgtxENDeCBjGZBupMDHhXBIHZ51beDDQkLif1RtkINC/rBnRjuqwkAAfZpMC6XQboUqChtX1+iDyc3N/f9fuwPEiiwEyBoCCedBRsPDnExnWsFnJ6ghWbRQkdCDF4TiixTxmWy0jBKLFAn30CBih0WIHPydbOsQR6C8BYhUW1/oP5IOY/hBxOR8RORGpBvekqPn88Npz9Kg2grblhGVZC4i3GNMKcYEb3pEjyBMfT9V6tRaNibfIKK3SS2+uvhs7eA2UCoKSCdtnDg4E+6WT0auKYdW9iH/rcjNIGKuy/iaU2wzqPhAHHzmiPnDyIMngfu97xE3Vy8mAQ/Ginwj/qP0TWGiDZrcImZjRSLmstgxHxwpOwyv6WMeBFKoonhCjhHRiMfS4BsAjmjJJ0czViQU4Nh7tUkbjToRh2Ljb8hCldeYRt1wkS/EKxTJ3wESxDPFW8TtU0TrhMKELgP36h2LngsOxtCsxIlUUj5cRNPlgrkzIJ/MiRT1IYVbcVsnXvy6zgQv9VTMYRzN3pDCzcE+kaRcVLrMfVtVBxA/ahUx9E0j3dUJZ6dGxzBPCPWWt3BcxPthzZ4ZtLnBC5QiuiFSfhpNzZNAJ4I1MuapF4ZCwlsIgmEiyQUJk3WKJBYNYtbT6hPRLUJp2mx7pEzbRSo/PPYfy7uZRCSrkj+i6egRoq8gMfQwhQiNK+fzB/EL3BR2IjdENN0tkvYWZBIRamMgzoVIQgPqxz5STiwabd0eEfQwiQgZ3AC0LX0uaEjnL8U1kRwtReTWhtxP5NHf7HdEY7FaVNogWkNxTSQrG8Ri3jrkWbyDaegfv8dCI99Cgc4pvpOHE9H2scWcoUEQlWf36ej/P6KTp5fWMAwZjt+6Z/g0nnssljCMOYtslBre5HMW+RQjmmPOImNYRVaRpbCKzI1VZG6sInNjFZkbq8j/du6EAAAQBGAgfvSPbA3EuwYrsGqEVJMxTgsZq8dyZ/eZIMXc+bqxZgAAAAAAAPzmAmkgqmF9nMW/AAAAAElFTkSuQmCC", + "description": "Displays historical illuminance values as a simplified chart. Optionally may display the corresponding latest illuminance value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'illuminance', label: 'Illuminance', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'illuminance', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Illuminance\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":1,\"to\":5,\"color\":\"#FFA600\"},{\"from\":5,\"to\":20,\"color\":\"#F36900\"},{\"from\":20,\"to\":50,\"color\":\"#F04022\"},{\"from\":50,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Illuminance\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:lightbulb-on\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"lx\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "brightness", + "luminance", + "luminosity", + "light", + "light level", + "light intensity", + "lux", + "candela", + "foot-candle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_illuminance_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_illuminance_chart_card_with_background.json new file mode 100644 index 0000000000..bf5727d8d3 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_illuminance_chart_card_with_background.json @@ -0,0 +1,39 @@ +{ + "fqn": "simple_illuminance_chart_card_with_background", + "name": "Simple illuminance chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical illuminance values as a simplified chart with background. Optionally may display the corresponding latest illuminance value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'illuminance', label: 'Illuminance', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'illuminance', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Illuminance\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1,\"color\":\"rgba(0, 0, 0, 0.76)\"},{\"from\":1,\"to\":5,\"color\":\"#F89E0D\"},{\"from\":5,\"to\":20,\"color\":\"#F77410\"},{\"from\":20,\"to\":50,\"color\":\"#F04022\"},{\"from\":50,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Illuminance\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:lightbulb-on\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"lx\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "brightness", + "luminance", + "luminosity", + "light", + "light level", + "light intensity", + "lux", + "candela", + "foot-candle" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_individual_allergy_index__iai__chart_card.json b/application/src/main/data/json/system/widget_types/simple_individual_allergy_index__iai__chart_card.json new file mode 100644 index 0000000000..e0a48b944c --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_individual_allergy_index__iai__chart_card.json @@ -0,0 +1,35 @@ +{ + "fqn": "simple_individual_allergy_index_iai_chart_card", + "name": "Simple individual allergy index (IAI) chart card", + "deprecated": false, + "image": "tb-image:c2ltcGxlLUlBSS12YWx1ZS1hbmQtY2hhcnQtY2FyZC5zdmc=:c2ltcGxlLUlBSS12YWx1ZS1hbmQtY2hhcnQtY2FyZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjEwOCIgeT0iMjYiIGZpbGw9IiNmZmYiIHJ4PSI0Ii8+PHJlY3Qgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxMDciIHg9Ii41IiB5PSIyNi41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMTIiIHJ4PSIzLjUiLz48ZyBmaWx0ZXI9InVybCgjYSkiPjxyZWN0IHdpZHRoPSIxODIiIGhlaWdodD0iOTAiIHg9IjkiIHk9IjM1IiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii43NiIgcng9IjMuMDA4Ii8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjg3IiBkPSJNMjIuNzMzIDQ2LjQzOGExLjQ1OCAxLjQ1OCAwIDAgMC0xLjQ1OC0xLjQ1OWMtLjMwOSAwLS41OTUuMDkzLS44MTYuMjU3di0uMTExYTEuNDU4IDEuNDU4IDAgMSAwLTIuOTE3IDB2LjExYTEuMzc1IDEuMzc1IDAgMCAwLS44MTctLjI1NiAxLjQ1OCAxLjQ1OCAwIDAgMC0xLjQ1OCAxLjQ1OWMwIC41NzcuMzQ0IDEuMDc5LjgzNCAxLjMxMmExLjQ1MyAxLjQ1MyAwIDAgMC0uNDA3IDIuMzQ0Yy4yNzMuMjczLjY0NC40MjcgMS4wMzEuNDI3LjMxIDAgLjU5NS0uMS44MTctLjI1N3YuMTExYTEuNDU4IDEuNDU4IDAgMCAwIDIuOTE2IDB2LS4xMWMuMjIyLjE1Ny41MDguMjU2LjgxNy4yNTZhMS40NTggMS40NTggMCAwIDAgMS40NTktMS40NTljMC0uNTgzLS4zNDUtMS4wNzktLjgzNS0xLjMxMmExLjQ2IDEuNDYgMCAwIDAgLjgzNC0xLjMxM1pNMTkgNDkuMjA4YTEuNDU4IDEuNDU4IDAgMSAxIDAtMi45MTYgMS40NTggMS40NTggMCAwIDEgMCAyLjkxNlptLS41ODMtNi43MDhjMC0uMzIuMjYyLS41ODMuNTgzLS41ODMuMzIxIDAgLjU4NC4yNjIuNTg0LjU4MyAwIC4zMi0uMjYzLjU4My0uNTg0LjU4M2EuNTg1LjU4NSAwIDAgMS0uNTgzLS41ODNabS0yLjMzNCAxLjE2N2MwLS4zMjEuMjYzLS41ODQuNTg0LS41ODQuMzIgMCAuNTgzLjI2My41ODMuNTg0IDAgLjMyLS4yNjIuNTgzLS41ODMuNTgzYS41ODUuNTg1IDAgMCAxLS41ODQtLjU4M1pNMTQuOTE4IDQyLjVhLjU4NS41ODUgMCAwIDEtLjU4My0uNTgzYzAtLjMyMS4yNjItLjU4NC41ODMtLjU4NC4zMiAwIC41ODMuMjYzLjU4My41ODQgMCAuMzItLjI2Mi41ODMtLjU4My41ODNabTEuNzUtMS43NWMwLS4zMi4yNjItLjU4My41ODMtLjU4My4zMjEgMCAuNTg0LjI2Mi41ODQuNTgzIDAgLjMyLS4yNjMuNTgzLS41ODQuNTgzYS41ODUuNTg1IDAgMCAxLS41ODMtLjU4M1ptMy41IDBjMC0uMzIuMjYyLS41ODMuNTgzLS41ODMuMzIxIDAgLjU4NC4yNjIuNTg0LjU4MyAwIC4zMi0uMjYzLjU4My0uNTg0LjU4M2EuNTg1LjU4NSAwIDAgMS0uNTgzLS41ODNabTMuNSAxLjE2N2MwIC4zMi0uMjYzLjU4My0uNTg0LjU4M2EuNTg1LjU4NSAwIDAgMS0uNTgzLS41ODNjMC0uMzIxLjI2My0uNTg0LjU4NC0uNTg0LjMyIDAgLjU4My4yNjMuNTgzLjU4NFptLTIuMzM0IDEuMTY2Yy4zMjEgMCAuNTg0LjI2My41ODQuNTg0IDAgLjMyLS4yNjMuNTgzLS41ODQuNTgzYS41ODUuNTg1IDAgMCAxLS41ODMtLjU4M2MwLS4zMjEuMjYzLS41ODQuNTg0LS41ODRaTTM0Ljg1OSA0MS4wNDdWNTFoLTEuNzE2di05Ljk1M2gxLjcxNlptNi4zODIgMS4zMjZMMzguMjY3IDUxSDM2LjQ3bDMuNzQ2LTkuOTUzaDEuMTQ5bC0uMTIzIDEuMzI2Wk00My43MjkgNTFsLTIuOTgtOC42MjctLjEzLTEuMzI2aDEuMTU1TDQ1LjUzNCA1MWgtMS44MDVabS0uMTQ0LTMuNjkxdjEuMzZoLTUuNDE0di0xLjM2aDUuNDE0Wm01LjE0NS02LjI2MlY1MWgtMS43MTZ2LTkuOTUzaDEuNzE2WiIvPjxwYXRoIGZpbGw9IiM3Q0MzMjIiIGQ9Im0xNi4yNTQgMTA2LjA2Ni0yLjI1LS41NS45MjYtOC41NzloOS4xOTl2Mi4zOTFoLTYuODU2bC0uNDY4IDQuMTg0Yy4yNjUtLjE1Ny42MzMtLjMwOSAxLjEwMS0uNDU3LjQ2OS0uMTU3IDEuMDA0LS4yMzUgMS42MDYtLjIzNS44MDQgMCAxLjUyMy4xMzMgMi4xNTYuMzk5LjY0LjI1OCAxLjE4NC42MzYgMS42MjkgMS4xMzYuNDQ1LjQ5My43ODUgMS4wOTQgMS4wMiAxLjgwNS4yMzQuNzAzLjM1MSAxLjQ5Ni4zNTEgMi4zNzkgMCAuNzg5LS4xMTcgMS41MjctLjM1MiAyLjIxNWE1LjE1OSA1LjE1OSAwIDAgMS0xLjAzIDEuODE2IDQuNzQgNC43NCAwIDAgMS0xLjc0NyAxLjIzMWMtLjY5NS4yODktMS41Mi40MzMtMi40NzMuNDMzYTYuOSA2LjkgMCAwIDEtMi4wNS0uMzA0IDUuNTk3IDUuNTk3IDAgMCAxLTEuNzQ2LS45MjYgNC43MTcgNC43MTcgMCAwIDEtMS4yNDMtMS41MjRjLS4zMTItLjYwOS0uNDkyLTEuMzA4LS41MzktMi4wOTdoMi43NjZjLjA3LjU1NS4yMjMgMS4wMjcuNDU3IDEuNDE4LjI0Mi4zODMuNTYyLjY3Ni45Ni44NzkuNC4yMDMuODYuMzA0IDEuMzg0LjMwNC40NzYgMCAuODg2LS4wODIgMS4yMy0uMjQ2LjM0NC0uMTcyLjYzLS40MTQuODU2LS43MjYuMjM0LS4zMjEuNDA2LS42OTYuNTE1LTEuMTI1LjExNy0uNDMuMTc2LS45MDcuMTc2LTEuNDMgMC0uNS0uMDY2LS45NTctLjItMS4zNzFhMi45NzQgMi45NzQgMCAwIDAtLjU3My0xLjA3OCAyLjQ1MyAyLjQ1MyAwIDAgMC0uOTUtLjcwM2MtLjM4Mi0uMTcyLS44MjQtLjI1OC0xLjMyNC0uMjU4LS42NzIgMC0xLjE4Ny4wOTgtMS41NDcuMjkzLS4zNTEuMTk1LS42OC40MzctLjk4NC43MjZaIi8+PHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1vcGFjaXR5PSIuODciIHN0cm9rZS13aWR0aD0iMiIgZD0ibTMwLjc1MiAxMTcuMTQ4IDE0LjMyMy04LjU0MWE5LjAyNCA5LjAyNCAwIDAgMCAzLjc2NC00LjQxOWw2LjE4OS0xNS41ODRjMi41MDEtNi4yOTggMTEuNDE0LTYuMjk3IDEzLjkxNSAwbC42OTQgMS43NDhjMi40MjQgNi4xMDMgMTEuMjE1IDUuNjMyIDEyLjk3My0uNjk1djBjMS41ODEtNS42OSA5LjEzOC02Ljg1IDEyLjM1My0xLjg5NWw4LjM3IDEyLjkwMWM0LjE5OSA2LjQ3MSAxNC4wNTUgNS4wMzEgMTYuMjI3LTIuMzdsNi42NzgtMjIuNzY2YzIuMjc5LTcuNzY4IDEyLjg0My04LjgzNyAxNi42MzEtMS42ODNsOS4xNTUgMTcuMjg4YTkuMDIyIDkuMDIyIDAgMCAwIDExLjAyNyA0LjI2OGw3Ljk2OC0yLjg2NmE5LjAyIDkuMDIgMCAwIDEgNi43MTcuMjQ0bDExLjg2MSA1LjI3Ii8+PGRlZnM+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjE5MS4wMjMiIGhlaWdodD0iOTkuMDIzIiB4PSI0LjQ4OSIgeT0iMzAuNDg5IiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIyLjI1NiIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMDQ5XzMzNzIiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzEwNDlfMzM3MiIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjwvZGVmcz48L3N2Zz4=", + "description": "Displays the concentration of airborne allergens, including pollen and mold spores, which can trigger allergic reactions in sensitive individuals as a simplified chart. Optionally may display the corresponding latest concentration of allergens value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'IAI_level', label: 'IAI', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'IAI_level', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Air Quality Index\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 12) {\\n\\tvalue = 12;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 12) {\\n\\tvalue = 12;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#3FA71A\"},{\"from\":2,\"to\":6,\"color\":\"#80C32C\"},{\"from\":6,\"to\":9,\"color\":\"#F36900\"},{\"from\":9,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"IAI\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:flower-pollen\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":null,\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "air", + "aqi", + "pollution", + "emission", + "smog" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_individual_allergy_index__iai__chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_individual_allergy_index__iai__chart_card_with_background.json new file mode 100644 index 0000000000..f55fda07c1 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_individual_allergy_index__iai__chart_card_with_background.json @@ -0,0 +1,35 @@ +{ + "fqn": "simple_individual_allergy_index_iai_chart_card_with_background2", + "name": "Simple individual allergy index (IAI) chart card with background", + "deprecated": false, + "image": "tb-image:SUFJLXNpbXBsZS1jaGFydC12YWx1ZS1jYXJkLXdpdGgtYmFja2dyb3VuZC5zdmc=:SUFJLXNpbXBsZS1jaGFydC12YWx1ZS1jYXJkLXdpdGgtYmFja2dyb3VuZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxMTIiIHk9IjI0IiBmaWxsPSJ1cmwoI2EpIiByeD0iNCIvPjxnIGZpbHRlcj0idXJsKCNiKSI+PHJlY3Qgd2lkdGg9IjE4OCIgaGVpZ2h0PSIxMDAiIHg9IjYiIHk9IjMwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii43NiIgcng9IjMuMDA4Ii8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjg3IiBkPSJNMTguODQ0IDM5LjA0N1Y0OWgtMS43MTZ2LTkuOTUzaDEuNzE2Wm02LjM4MiAxLjMyNkwyMi4yNTIgNDloLTEuNzk4bDMuNzQ2LTkuOTUzaDEuMTQ5bC0uMTIzIDEuMzI2Wk0yNy43MTQgNDlsLTIuOTgtOC42MjctLjEzLTEuMzI2aDEuMTU1TDI5LjUxOSA0OWgtMS44MDVabS0uMTQ0LTMuNjkxdjEuMzZoLTUuNDE0di0xLjM2aDUuNDE0Wm01LjE0NS02LjI2MlY0OWgtMS43MTZ2LTkuOTUzaDEuNzE2WiIvPjxwYXRoIGZpbGw9IiM3Q0MzMjIiIGQ9Im0yMC45NjMgMTA4Ljc0NC0yLjYyNS0uNjQyIDEuMDgtMTAuMDA4SDMwLjE1djIuNzg5aC03Ljk5OGwtLjU0NyA0Ljg4MWMuMzEtLjE4My43MzktLjM2IDEuMjg2LS41MzQuNTQ3LS4xODIgMS4xNy0uMjczIDEuODczLS4yNzMuOTM5IDAgMS43NzcuMTU1IDIuNTE1LjQ2NS43NDguMzAxIDEuMzgxLjc0MyAxLjkgMS4zMjYuNTIuNTc0LjkxNyAxLjI3NiAxLjE5IDIuMTA2LjI3NC44Mi40MSAxLjc0NS40MSAyLjc3NSAwIC45Mi0uMTM2IDEuNzgyLS40MSAyLjU4NGE2LjAyIDYuMDIgMCAwIDEtMS4yMDMgMi4xMTljLS41MzguNjExLTEuMjE3IDEuMDg5LTIuMDM3IDEuNDM2LS44MTEuMzM3LTEuNzczLjUwNS0yLjg4NS41MDUtLjgzIDAtMS42MjctLjExOC0yLjM5Mi0uMzU1YTYuNTE3IDYuNTE3IDAgMCAxLTIuMDM4LTEuMDggNS41MTMgNS41MTMgMCAwIDEtMS40NDktMS43NzdjLS4zNjQtLjcxMS0uNTc0LTEuNTI3LS42MjktMi40NDhoMy4yMjdjLjA4Mi42NDcuMjYgMS4xOTkuNTMzIDEuNjU1LjI4My40NDYuNjU2Ljc4OCAxLjEyMSAxLjAyNS40NjUuMjM3IDEuMDAzLjM1NSAxLjYxMy4zNTUuNTU2IDAgMS4wMzUtLjA5NSAxLjQzNi0uMjg3LjQwMS0uMi43MzQtLjQ4My45OTgtLjg0N2EzLjc4IDMuNzggMCAwIDAgLjYwMi0xLjMxMyA2LjMzMSA2LjMzMSAwIDAgMCAuMjA1LTEuNjY4YzAtLjU4My0uMDc4LTEuMTE2LS4yMzMtMS41OTlhMy40NzYgMy40NzYgMCAwIDAtLjY3LTEuMjU4IDIuODY5IDIuODY5IDAgMCAwLTEuMTA3LS44MjFjLS40NDctLjItLjk2Mi0uMy0xLjU0NS0uMy0uNzg0IDAtMS4zODUuMTE0LTEuODA1LjM0MS0uNDEuMjI4LS43OTMuNTExLTEuMTQ4Ljg0OFoiLz48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuODciIHN0cm9rZS13aWR0aD0iMS41IiBkPSJNMTg3IDU0Yy0yMC42IDAtMTguMTU5IDU2LjE0OS0zOS41ODMgNTYuMTQ5LTE4LjEyOCAwLTIxLjAzNi0yMy44OC0zNS4wNDQtMjMuODhDOTguMzY1IDg2LjI3IDkyLjIwOCAxMTkgNzQuMDggMTE5Yy0xNC44MzIgMC0yMy4wNzItNi42MzgtMzcuMDgtNi42MzgiLz48ZGVmcz48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2MiIHRyYW5zZm9ybT0ibWF0cml4KC4wMDUwMyAwIDAgLjAwODk3IDAgLS4yMTMpIi8+PC9wYXR0ZXJuPjxmaWx0ZXIgaWQ9ImIiIHdpZHRoPSIxOTcuMDIzIiBoZWlnaHQ9IjEwOS4wMjMiIHg9IjEuNDg5IiB5PSIyNS40ODkiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjIuMjU2Ii8+PGZlQ29tcG9zaXRlIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzExMDRfMzMzMyIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTEwNF8zMzMzIiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PGltYWdlIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3QkRBQUlCQVFFQkFRSUJBUUVDQWdJQ0FnUURBZ0lDQWdVRUJBTUVCZ1VHQmdZRkJnWUdCd2tJQmdjSkJ3WUdDQXNJQ1FvS0Nnb0tCZ2dMREFzS0RBa0tDZ3IvMndCREFRSUNBZ0lDQWdVREF3VUtCd1lIQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnci93Z0FSQ0FDZkFNY0RBUkVBQWhFQkF4RUIvOFFBSFFBQUFnTUJBUUVCQVFBQUFBQUFBQUFBQkFZREJRY0NBUWdBQ2YvRUFCc0JBQU1CQVFFQkFRQUFBQUFBQUFBQUFBRUNBd0FFQlFjRy85b0FEQU1CQUFJUUF4QUFBQUQ0bi9QZVBBMjRPNWJSYmU3VGJFS3M2NGxGS1ZTVUhlM09NUk1ETU8yRWRnM0lUdENUMXNjbzh6RHZyN2lqQytqWWM1dWlzcTRrS1pNRktoYUtjaW15WDBibkdGbUhwajZHbHBSZm80ZEdGSjZZekRERnhITFY1L1B5MmhZZmdKaGlBQ2xReEVObURVRmpKVFVIN0dBbUp0ZDFEYjNFM29PSzg3KzExQ1hEWng4MVVhUVVkeThuazZ5anZvbU0yeEtUTVJTMEIwOGFvdmtTeDI2T3FzOWpiYVo2aUY5emFmNlRmTm5uR3lvdWRjekJvVk5iTHB0NVJuTDg5eHk1UTZZU3Bsd0ttcGM4YXEyZUI2QmdaVy9wWHVwcWk3dDZUT3ZvbXJ0dGQ5S2k2dVNPZGN4NUJReTJZYzFCeThEdGIvaitiM2F2cmdybm5BeWVKWEc1YjdLeVZRdHk0ZDJ0ZXB4SGZVdlhlNTYydHE2Z0t1TkxWK0NCRk02NTBvcDVoNk5uc3NqL0FES2NiNnZvMWZmUlBpMEVnTm9SbzNSSjA2emFkcnpkQmF1dDJUcHJvSFZaM3RWWDAwdlNhSGM3TmFDcXlaVUR5VU1tVXdsaVB5TkJxQ3ZzOE5BVlZiZ2lETmQxMm1kNld2YyswK3hXMnJTSXM3MDZkSzExK2tvbTE0RFJGVXRwWDg3bjBVS2s2UExrUW44ZC9IZVFXaEVxZTZJNjljM0hwRmZxV0hRV3Z0TG42Rk5BNzdhemJvdFJXMW5aZHkxMUkzSlp6TllGeURYblY1dXRWNTN4Mkp6cW1uL04zNUR3RE5vS0UybzFMME9mU1BWY20xR2JxZS82V1pxMmVLOUR5MWdpcWJMTDB6YVhuY1BqZzNLUG5OT1puRnFPMFNzVEN5d1Yvbkw4bDRZSDNqbTFvbW5lbExTdldkeTduTm96VFdscTl0U2JwSUxLZW5YRlg2UGJuMXVUTnh6anVXdEsxRHpLU3hONXNlZjh4cktTL21WOHE1ZTJCZEE3ZENhLzdJMEQwWGUrbWkwQjFtZnFXMnVuWm5LelRGUnBwcElkZWZQektPNVRHWnpQUXpXV05XUXpOcWVpemxUV2w4Uy9OK2F4Y0ZQdEY3VjF6MWk5OXJQOXJmZ3dRejJlbFFRS1dqbi9BREJpdXR3YnQ1Nit1bmtJVmlOUkNuTjRvOTZ6WkM4TXhFcWlWZm5YOFZ4WHQwbnMyMitrdXcraFJvYTF3WDYyOUpWa1VWSHNNK1c4OG5ib0t5VTJiZW96OUVXUkhVYVRRWktqaWEzbFc2elJsZWRMWXArWTg5ejYwdXVuYnIzblgram9zNTFyMUJMWnBycWRTc1RYbFhXb08wOUtVTFR1SmRqYXpodE91d1ZFS3cwTVBSYk4yRkZzajAxbnd1Vnc2NTY1M3B0UFRYVGFXcUlOU3lEeldqUFhVNEVZTVFCVXJDc21NcEMzTDZmTDA2T3NWV0dYeU0rZU9haFhKcXF0WjRxbW9QR2pwWGZIWXV4TkdzMmsxZEVnblV6dWg3Sy9ZQnBxY2tDeTFjYUowS3MvUXNiNXA0L1ZsWmxXTEMwbmxsWVdLMWk3T1hFenpxU2xMOGxkSDdaZlF2ZkYrcFMxREowWk1UNENXcVpuVzdXalIwNlNLaXJDWGVuNkdSNkxYRjIxUEozK1kxcnpReGc2UnkzdDh3V3NWaFdXL1BqcUhhbjBCM0xvbFRuZkxsV0pWdWQ3cGs4S2F2MGJYNjJ4SGt6cFIxekVzdThqc3JZV3lUZzdRZzY0U3NDYTVmaFNXNXJPb3NlaUtQeHcwZnRucDF4SXJVNjVYaHErYnpxMXR0dHZXdEZGMWhHYjZ1NXZhampZUVAxR3liSmx0R1ZqTlZhY1pCUlNxdENxYVA4QS84UUFKUkFBQWdJQ0FnTUJBQU1CQVFFQUFBQUFBd1FDQlFBQkJoSVJFeFFoQnhVaUpCWWovOW9BQ0FFQkFBRUZBZHh6Y2M2NXVPZE0wUHprUlpBR1JCdklMWkJYSUtmbnk1OG1iVXlTdWJWM2t3ZU1tTEpEenBrSWZzWWVNM2tzM0RlYmhuVE9tYUZrQTRNR1FCa0Y4R3RnMU1pcnJXdm0xNCtiSks1SmJGYVdSNDJ0VVJPYzE5N3lTdThrdHZXRFgvZlhrby9rNDU2czlPZW54bWc1RU9SQmtBNE1HREJnZ1lGWFBsL3pwZjhBSmgxcmVsdGtsdWtuNitLMGtqQjVodzczMUtWUm9zUzhmbkNUbFh1R0RSL3lkYnBxUS8yUU0wRFBtd2kvaklpeUlzR0hCaHlBY0dEQkw3MXROWHRtNi94a0svOEFIMEpDM1NJYklWU24rbUhIdzZWdHkxR21WbWVOdElYRFZKS0liaXQ2NUdra0d2YVcvd0EvTnYyYlczNWdEUFJoQVo2dkc0andZc2dQQWh3Sy9iVVZwUXlySG9tbFVJeWhPcTZDS2dOb1ZSeG43a0pLUlZycU9uSk53QXhmTTN4a0xvclNuOVVXK083YWI1V3AvWHF0SjczR0ZFWXVmMVBybkFHYkQrRUhraDVDT1Fqa2RlTURyRWx0eTJKS2ZWUkwxeVM3YTFNZm1LdFBPYlBIYXo1QzNOS1hSZjZxTmVCbCtZQzErcFNEYVZRbWRFb0NLejVIeCtkZ2NQQXZNaWNZQUZHNTR6dFVVQi9oSS9rNFpPR1FIa0JiOFJqTEs4SFlxTlpycWxUZThldVBFSUtxcml3TE9rOWthemoyanFVbkhaKzFlbkJQTFZiY1pEb1pzbkNrUk1RQkFQbjlRc2NMZkVWNTdaNDhNUTdOR1MwYjJQdTFyV0d5V2RQT3dKOXNoWGRvN3I1NmxWbzYwUk5QY3dVcFg2dHlxQ25QSThaSHFVVUdONzR2VW05a2tSakNXY2daQlNEZTRVd1lKdDEvdDA5dmFlSk96OWM3QmZlTnFlN2RoVCs2RjF4Q1BiVXNMTHptOTRDUGVkWFgrMkMxRjJneFVFR2NOVE9PVmkweHlXVjl1VnFYdVVxSy93RDQ5VWdZNkJKWkViL0p4YWtXeGxpQTV4QWxPSmcyTlhCaU5qVWpHeFlrbURKVzVRTlZkcEFzWnNSSmx1T0JzOW41SW41Mi9VOS83b3R4MUJYcnVFVUlGbWxYRDhMcUtpeFZKVXVxV3UwUFVPb0lzdStZUFdaRHlrSXBEVW9qQm5xNzZQdTM4QWFxZVpmM0tJdWJ0TXNRdEFFQVgxT01qQ2RPWld2QW16RW5HUnZ6MjVxVzl6UmdTUktTTS9GUUl4eWJSbEhGZTQ1Q0VUWTFCekRLbkVUVUdtZHcyOWJEM3V2cm5tR1VPTFFFSGxkaXNHSnVYK3dDekxjYkN0M0l3RWtoOGhiSTJTTXF3L3liRlpRNlEwS1c3VUFYSXlsdmVRaGdSZDkwd0I2MXhhckN6TmRQNVhocXhPRjRFNEZVMlhlNkw3eW5nUUtpZHB5T0dwRzVDSHVueklJaXBjcE5iQnNQWU04MEZxOXhWT0czQWI3WlhGVE5Qa1ZiT0MxSXNhWXZqQ0NGeGIrY0Z5cVlHRngreklpLytkYURaRHExL3dEeWNGWEtOVmNNemxVU244MjZPWkRxMUFTeVJQVjB3YnU4c1cyYlRjU2FMWno3MTdUQXJtZ3RGSkhiQVRGNmJicXF0SXpNandnZ0JKV3lGbER6U1ZSajVLWnF0dHJEeGx0YTFSTnFyQjN1cEhxZTJGdWtLTmJjV0thdmpOUGpsWUlRbEZod2xFZ2ZZaFloUWZ1NjhsSk04aVBTaWFRSk9KamFyN1ZTS2p3cVNUcmxmV0VnNVRJK3hhc0lDc205eWl0aXEyOEUyWEJoNTdBS3IyUE10MXIvQUNia0xrQm9Xb3JaaTVkU2dPdE4wSnZ3Y1ZPbHYyMFNHcENyZzZBb0NNSTZreERSVHBhZHhKcWV3Mk5IQ0RaUjZKczg1b250a0kyaGVQZ2tqak56WUpNZzVESXU3U1BTdXQ1eWhVQlk3SDVSWlJnMWNXS3lGZGRYeVpiR2lIdGxWOFgvQUpxWDhxRStlL3JtZk9Kbm5MZkc5UjhVTW9hMkdXdEx0V2VoYUpaRDNuRHl5ZEs5UDRac3RDM29ldm9tM1UvWEt0L2o1eUJiY2l5cGVXSzJaeVZXMXd6TzZNMUUrWFphT2d0dEVOY2s5L0pmNUJzdTg0cGU5OVJiU2l2TkwxWnE0L2tCajIydFF6MTNVR0hQZExyd0tuYjNzek5sOHFiSEpCR0pYTHV0SG9Ub2NVcjUza0xreDNIQnhGYVdRMjFlVHY2STMvSnJhcFdIcFY5cE5lRml2VmZjb0N2dk42ckdyVFF3aGxGQ3hLZnJhWFB0c2JHdkN2UFhPYndWRng2NElTZVhFOVdxQ3BaUjF4Y3BDbHE5U2t2eDFRMEozVFV0alU2eEtMbDZ0TU4vbEc3aGFoNU02czFXWGltNVdVcHZnc3h4V0JBVlkxUGF4MVRwQzhqbHVPOFhDRU8yYktmMnRNUjNGcDZPNXR1NjBkdS9rS1BOZVNic3BSaG92OGYxRXBhd080eTF3MkF4WlJuaEhJWE1RaTVCZG1JVnEva0daRGxkT2o5MGdndXJoWE9JUW5aTWM0VGxWMWxkeTVkSTBxMVErYUZOWEVMTUk1eHNWV0IzaXZ3Z1BjRUVaemtHaHpjNUxxV21MbU85NnRDVGF1ZEdLVjl1Q01LaGhWMmFncGExUXo5TVUrUXhDTS9JRHp4NS9iZzM5c280eFpPQTBqeVF1b1ZiQkh5VmZJdGNEVWx6Ry9kRmFjcnJiSnZoOTBldHdyM21aM3pqM0srTjJZNUVkaURKWlNuYWJKdlRKTmowNDFMRkpTNkQyRXpkODlXbGNBMmtqSC8veEFBOEVBQUJBd0lEQlFRSUJBVUZBUUFBQUFBQkFBSURFU0VFRWpFVElqSkJVUVVRWVhFVUlDTkNnWkdoc1ZKaXdmQXpRM0xoOFJVbE1GUFJndi9hQUFnQkFRQUdQd0gxZFBVMDd0Ty9UdTA5VFQvanBUdXN0Rm90TzZuZFlLekZ2TnNWcC95YUt3NzlGY0xSYWQyVnJkVndmUlhid21pZStPUGVadk5zcnQ4MGR5eTRWd28yVkZZZXZwM1VwM2FMUmNLcTBMTTVuQ3N4WnVqVXArRm9OOFZUbzNzc1c2S1NJeEhJWG1oV1lzVHNvdDVMYnZISkd5TmxTaTA3dE8rdmZSWEh4UjZoVWVFNTdBaUtYcWc3RFV6c0syQWJ5dWoyaVdjUjNmSlhBelVXMkRMaEVaTEpzVFJ1MTNrTUt3YXF0RnRHeFdQTkZydTZxUHEyN3Q1dFU1ektnOUZsMXNpdzh3bWx3c2RQRk5aVGpRYkcyb2VnTWx3d0JDTWk3eWdSMDBSTFdYVG5tTDQwWEJ3MjBWSjd0VG9Jb3Q0WGJaYmMwcm45WFR2RlFnNm53V2RncWVpTXNSNWFVUnF5dlJaakZRcHJZMlZjMHJQTVBtcVN0clRTcWM1T3hNOGRob2hKS05lWFJWV1Y3QWlkbFRvVVdPYmRaOWx5dXFFMjZlcFJWb3FVV1VOVkhDaXlBQjFPWVZIUmtzWHB3YlROUUZxZGpvR2kxeDBYdEcrSldmSlpIS0tGSG1ORlhWYmZaVnlXYU92aXN6elNqYTFWWVc2bWlySm9oUndxdHBHejVySmwxUjNiQStyb3VCVXk2ck0xaFdaekxvUDJacUVHeHN2bVRZM3QxMVJjNW9vdDJpeWs4NkpyZUt1aFhwVHhiN3Jaekd6aGNKMFFmUVVXOE15TFF5bE9xYkdSOGthM0lWZXFvMHJYMUJWV1ZYODBCUmI4ZjBYc2dnQTFGNkphYVVRZldtV3pnUHVpOTdqUWkvNkowM2FKcERXelR6WHRUYW01R0RhaWtmaFphdXliemVZVDM0YyswaHJZOHdudGxzNWtoYVdua3ZTOFEwVTB2MVJramExcFcwci9BRkxjMUkwNkxNNjNuNmd0b2d6VmJMS21PcHFpd2xhc2NQTkRQR2FkUXM1MHBaRnJ0QnFpMmhBL0tpMmxRNjdEVDkvSk94T05lQU5jcFJQcEdWakxFTlhvY0VWR0grYjd5OUt3MDlYWlJXcHM4ZENuZHE5anMyZUtqaUpud3J1ZFRTdjBVMk5najJUOUpXbmtmRkhDdWljQ0crelpUVHhUcEpIVmtKNjF5cmFTbmlLTThwbzFWaElKNmVvSFBDMjJUVFJOZXozZFVUVHhDM1FmRlVEM0JNaTJwZTBuUTNSRDNEcWlCQlZHUXNpQkIwMVViSXc0WjZDb0ZtdVJnZmpEbUpwZHVuVHpVcGZEblpwUEMvbC9aYkhZU2dIZVpJeDFyK0JSbFppSE1KWlM4YWl3MFlvODc0bGg5MGNrWThLNk44elQ3V1dPbEQ0bGY2dDJmRm1jUDQwb0NJZEU2NVcyeGtnYmxHNnlxelF1c1BnRlF6NXZCdCs0bW1pRFU1ek5XaFo1R28rYUJhQmJ4VFozTnExWjg0SFVCWmk0VjZsQ2JDU09ER0c3QWJVUjJqczFSVmd6SXhSeDVmQUtHWnJzc2hNWkVmSjRvTXlmRlRadUU3VGtMcUFWcnduOUN2VG03K1RwclhsKzlFSHhVTG5iMlVqaC93QTlFV2lBYVVJTGRPcWZGSGg5OS84QUVkRnhPOEIwQzNNT2NPek5Xam5qL0pYb1BhZVhFTmwzUzE3YUtSL1pFN1JMSEpSMGJiV1JreEdJa29QK3FNVVR2OTJjNXg0VzVFWjNZbDViL1Rxcm9saXoxV1I1NHJMS05GdUJCa2hvM1FtaUVHSWxxQkprZTB0MUM5SXdJTWtiblVEdW5ncVYzaHFPcTJNbkQ0cHMwZG5nbW5raGluYnJYNmVKVWZhT0V1K09Kd29EeXlFRDZyMmt6Y294TEdOQXJYZEJxZzlyNlJNOTU5ZCtxRStPeElrM2lSU01OcjhrSkRnQUJPU09WcUl0d3I5MDhxSDlDbmhra1F2dlpiSitPeWt1MGJ5cWRGQkJobk1ZMlllMG9Ma25SSEY0REVGbGRDT2FwMngyUzBrZnpvUmxLYkJnZTBBM25sY1FEUlZCVmt3RUlXVkIwVlQwUkVncTF3b281MkdyOHRHeUQzK254VTJIeGNXMGJ0RzFhZWJTRS9FWUNUTXl0YWMyK0NjMmRwQlVlR2tIczltRzV1WFVwbjVKUlUrQ3hHTkZROTVPSGlGZmpYNTJSN05aY3h3Z3p6UDRXZGRPcWoyVGdHTk9Xd3ViQzZobGJxMTFaRkZKbXRIaVNEOFFvM3NkekZWTkU5clgzMGNGRkJkaGR5SGdQN2dLYmF3NW01R3RhUUxnNjErU2tpeHpBWUkyQXh2cnJJZFBOUkZ6ZytwRHdjMUtqL0NmaGNMN2tjV1FqbU1vUWVENW9aVGJSTmRLTGl5QUMreXl0UG1xbVR5VW1BTGc2TjV6ZjArS2M5OFIyc05ObzBlKzNxamlvc1NZeU5jOGRqMCtDYXlUWnVEbmYxTitCMUNtZkUwMGVhWlR2QXBqTVZpWW9vMzNEM3YvQUEzb3Blek1FSHp2OUhMV1NRamROTDBCL1h4V0Y3YW5tMk1MaDdTQUhSd3Nmb284R3dHK1ExSTVCb3Y4azE3bjF1YzN4Y1Zqb0JxMmZNd2VRUWplYlpnQXBJUmNDbFBuUk1od3BxWFRiT0lkUndqNjVqLzhwMHptVndvNFBHLzdDajdJbk83SS9hWnZ3dEIxVW5ZZDNzamVDNXZObmwvNG9lMW1TazdhSEsvd0xIRnRQa0dyZkthVzY4MW1UUTBvVjVOdlJPaWtsdCtNRDdyWk9pNHVGd05uSjJJeEV3TDZMMDVrbEhFRnV1clUyN1MzK0hJZnluUS9BL2RQZUg3TVFOY1hHOXFmc0kweDh6OG5GUTB6UE9ncjVYWG8rSG16MU8vbnZucmI1S1BId1RQOUVmSUNBSFhoUFR5V0s3TWJJTmR0RUJ5cm80TER2eDduWjQ0SEFTVjFJekQ3UENqanFEbXlDbnpKUW5qcVd5ekhhZys3b2dZanVPbEJaNUxGWXl1OVdqUHQrcVlZcGNvaURtdGYrSGtYZkFmVnlIWi84T0Z0QldsMitYNWxzWTJNYkppSnR4NHZzMk40UUZpZTNtdXlQT0ZaTVhmaGNhTnBYclVFcVFZZHRURGlTNW5pSDMrdGU1cW93TGF5TjAwVkhTWmFhSXlrMzV2R3FJcVB2WDRJbkRZb05BMGE0Mi9zdlFadzZONU81WDNrN0M0bDJiYU5PVUE2ZFFmdXBkcGRzMnpZNXVtaE9iN0JiWEJQOWxHZDgvbksvd0J6d2podlZCaU42ZTZmSlB4c0dJWmlzSExKdmx2dWVmUlJZbkJQOXBCZHJlb09yZjM0cVAyZWFLVmhMQjBQTUlIQ0U1UUs3TWpSTmJHK2dadW11aDZwd2pOMjd6UjA4RVpBNnpyckpteVF0dVQrSXJZNFRjcUtOSTRyMlVXRmdadXREakUvOEFyeTZtMnBYb2UzQmZMalNZcjhWTmZOWlpwQ3dobVNVK0E0WGZjZkpib1dhUkN5eUVocHBxcXh2Qnk2MUt5MExhNkZFdW5kdTNOVm56Nlg4VnMyNEFsaE8vbzdOKy9CUk13bWRrajNBdGlsMHFvWDRUdEtOdTNhNTIzYWFodFRmNC8rTDBKM2FPRGtnSnk3RTVoWHpKMUtaMmpnS0ZqUlRKMDhFWjhJVEdhOGhmeVBWVmVOaTdtR2NKK0hKUlZtYjF0MXJxcGNSaDVtT09lZ3lPcVdySTc2b3Zxc3JMRHpSY1pMOVUrWnNySGJPSXViVjFMOGtHUGNhTll3WlFLRFJZTEFHdVdMQ1ZrQS9FNXhOZnNqS3g3UTRlOE9mdzVJRkNnK0t5VXVyUEtFd0c4TmFwc3JNUy9KS0xiM0NRcU9lSEV1b1NRcUhDeG12bW41TVBDMW9kY2xsYjlGdEltTmRqQVJrT1MwYmpvbWRxNFRIdUlZY3BiSno1bzRUdGZzWnUxLzdJelEvTUxZdzRwOHVGZi9BQzVibHEya1oxT2xOVVhNY012TGRUTjQ4UDZwd20zbUhpc25GcnpUb3N6VmRIS2JINko3WEhXZ29xU01KM3VxbWR2VnFJeGJrTEpzakxtbGVIcXYvOFFBSkJBQkFBTUFBZ0lEQUFNQkFRRUFBQUFBQVFBUklURkJVV0Z4Z1pFUW9jSFJzZUgvMmdBSUFRRUFBVDhRT21LUkRjWFpabTZyZnc0WVIxNUhYSWRKd29aY3VTcTZoWGtteVZ0T2RqbG9UbTFDc01XNVlpL2tOZElzeEpYeVJiU3BicURlcmwrRGlVM0JWZlArTWJ0a1JYbE1HS2l3c2Qxa0JMbU1aWGo1cWVPL3dLbGtpd2NoaS9nWmN4NmgvazIrdjhSYXVmd1UyRVlYalV4YmN2MURBaDJ4WUNMK09FNVk5Q0ptbGIyVHFVUGJxeTdiSFJnYmNvcGhxRUpkZEVUWU93NVJBbnhCV2RkVFJpQXBHZlQ0bUNJRE5Zc0IxVkxJMWd4R2JCNXFGbjBrSDVSWUp5eG5VRGRNQmRZZ09PWmR3b0tJQzVrV0plU3M3Q0lVdENpb1k4RFV2Q0Z4dVV6UFhvbktrWkZhaE45b0pJRnJBMzhUSnNnTU9nSkxkejZpK2pYdU03ZHVMR2NiSGlPRE1ueENvRE9lUTVJWU9EakdVWUY4eTFrUlVIUkd4MjVjanVqd3poMkcxdmI2OVFidHZTK3BZT1VvYWdkcW5ybHhSRGZNeDBsUyttazF4S1JnM2IxNmxsd3cwd0hOTXNpMTRqVVV2SjNETjRyUmtKMEZ1bWtEd3ByeHpLd3BxaUg0MnJ6ejVpYVhjVkVPdFdBNDdpRlVCblVDTDJVcjhlTlI2VzhIdEI0Smd4ZHRFS2hZdWtnTFFDRURwdURaaENBaFVzdnNDeGlOZ3FnWFpVL3RSdk9EbUNBQmRFMWtxRmgxN2w1eHBhM0E2bFlBaHBrdHltTWVzek5JdmdRb0lTcjEydlNHOEhZMU93Vi9oRW1pM2p3NmlsYXNnMHFwQm9GZGdRcE9zbzduUGdLTDlpcXpnblNFbGVlR1VwcGw5ak1SMWgzNkh4RlBDWUF0ck9TVzZCemxKWU1YUk9vWDRZZU45eXJRekJwak1zVnNTSTJqb3FHQUpqbFRYamdDV3JWdWsyTFdMUFpkUmFZMCtJY3c1b0pzOE1BY0Rsc0xGZzE3WTQrRmFPb2JVb0xWbCtmVWtFSnI5UXBWZ2N4UmRkc0FXd2h0U25DaTFoQjZ5emlIU0lvZVRJMWozQkxqQWNUcUdYQ2xtV2RnNHFWV2hvcU5hdlBSd3h3dERaR1M1QWI5cStJRE1CWDZMeVVSZXBYZHk1aUYvU01zQlpGRkd5Nk84d2VWQW9aY1ZJWXFGUDdFQ2gyaS9FUzFaUUVZTWcwQ042K3NsTjl3REMxYUJXa2RIbjdrV1paTC9kTnRrRkVBQ0c3Z2JMT1d4YktjUGhPMS9McHVsK3hLQVU1YUhnalZHRWZ1WHl6TnQ2K1RtcWpWdVZIMExselBkZzJvUTIvMUg4UXYwOUJBTmxEWVBNSnhwWVlQV1FORGc1OXhEMG1ucXA4d0lXR0ZWMUNDVDRRV0tXOXc0SFFwZmNWRjk1WWZxbzlHT1lXNERVUFk5aWZkMUpUM2lyNWZOUi9HcUgyNUw1SHVCOGNGeER2OVJhZTZuMC81QkpBTE9hTHJHY0k1YnRIMitHZkxNSUdEemlpOXgxYVFiRHk0eDhTZ0haOUx5UEw0aFhCQzFRdit3N0NsSkM1MmFYei9BTWxRd28zUlY4eXo5NUdxMlhIU1ZFYkUzYTBvQkxOVUlwS2tZSWl4ZFI0ZkZMeUdzZ1dGQXVBTG9ZNHBnMzZ0TFRYekNhRUNiaDhYRVRBcDcwclBENWcvU1cxZjRDKzQrVVZwbzRVWDhQVFZ4MWpCRlF0S01yTEw1R0dKb0ZnWFFVRkh6MUZHV3JUS3k1aUZiOHM3WElZVG8veVgxT1RRVHV2SHp5eC9GWndWZHlBM0JuNFYyKzViYkhjNFBuZVk2T2tiNG5QTExsUGVIekZPYjV3eW1NY01vRk5UbU5ZTW5mZndRdVY2VExYM0RTTTZXZmtGWHhYT1ljSVpVYWhvdEZaMldpNHVyN3h1TUQ4Z0x1WFlUQktLS25LcnA3NGkvSnYyQldjWHp4ZTQ2bHFiRVY1QnpRbkpxZ1ZyM3FOK3c2TzJqQlBBbjc2Q2JiOEdXRmpkdEcvUzk5eFdqOUFsKzNzbGFsTmJpUHkyL2NiU0RqSElxZUQrMUd2Tk4vdFFFS3lZdi9XSE56UlFQbzNBNGpFeGoweEZsdllhQlltb1Avc3d5VmtHNCtibjRobUQxcUJiVnNWbVoyd3FNV3NTK1NLUEpYMlJIZWR4a05rVTlkR0tvTEI2WkxBMUZLM2dRKys0c2d1ZU1qMVVKVmdQMHJyUGpqbTRLU2V3VUlXZ05GcjZ3STlhQ1ZNWGxuUGxxQy9DMXFzMjYyNzBZcmJsZVhlOEorNm0rMjVhN0gyakVmdWdSUWxEVGV2TTZ6aE5yV2ZZTHN0M01LU1Boeno2WXp0L1NFUzF1c3YwbCs1YnR3UVV0T2NmMkExd1F1WjFkUE1YeUJ1R0x3NmpnYWE3RWROa1dLeHAzQ0syNjZTOWVLWjlWQVBGUWNzSDJJLzJTNmZQeTNsNTlRS3I3RFQ2N0p6OUk1aDFMOUxQY21KNFRsZktWTXZhRDBTVDFKcmk1ZVdLM2RLREE2S09ZcE9NY1ZmWlZwUnZuREpaMnhjOXR0QXZkWkJ2ZFdId2Y4Z2NRZk5QTXM0bWJYZmtwL3VDc29VU3dzVlI2dVNwSmgwQ0duNDFIdUNhMWlxWDBHLzZoS0F4Qkk5THp5eCtJcFlVMkxPQkliNjZoRys2d3IwbVdGRm1Md0Z1UlFyMXdqSGdZb2JjRVN4NmdJaENqVjRUZ3FndDJxSHkzOXFKV2dMaWJxUTdINGpCcUJDbTIzcC9CajNLWmNFcjQweU9IS3JDT0NIYURsWWd1Y1FDbmdpb25hQ2VuTm53aERsZXpzT3V1SE9BRjhtejRZQ3hLRTNSVjlVU3dMbjNvaXZ5NGpsOWc1bkJhSWM2Z0g2ekpwNlZvTm4zOVdBVmdValV0TFBhMVR3TXVFclpjc3RubHoya1FGZ2d6YTVlYW9mSklyTG9SaFdiN255eWt0MHlZTTJ4NWpXUko0Mk1kVFhRTFlwSVRUZEw5T3ZtRHhBcnZCOGpFS3p6T2R5UFEwcUpHc3JIbUI0azJkRjkvbC9KUlF2ZExCcjUxeERCWWpuYTE5QmFybzVabHFjS1FBb2JWc1dSNHhLTDIwRHZSL2VUYmk0NVlBWDVYaER0WTErZjlydHE5bDM2aEI1TThQOEE5a3ZqSjVIQlQ2bzVsa3hpdnN4MUxuN0FJZjI4UHQ5dCtaOHJVejJDd0dSS0VQSzREb1ZhbktBV2lBbmtYNGpUQzR0MjduYjlWNklNOFlwb2kvMHQ5U2twRVliQ3JnbTFuVVRzRXdtWjJyZ3BCNkpmWEVxaEM3UlpldW55Ujc0VTQvWjVYem51TnZWbUk2RXhIMU5ZZVlXRWQ1TkFlR05YQW1ZVWsrYXZ1S3Q3QmViTEh4UmEvbnpjRkR5K1ZSNUZYNFkzbTQzVnhRZVI3NWhYb2oyclNYa2JVUExUYXNzMGIzeUlIclJyeDhUZVgvVXE0UDJGUllCY3JmNlpVM1hYenUzNE5aS1VnbnlhaGMydEEyMTArT241ZVlBR3lEY3VUMXJxL3dCeGgxY0M3NmlIa0F0UnVTNVdiRndtd3ZrdGg1djdNQW9CeUl2dXhYNWdDU01XMVRJWWFrZmpuTDNOM3hqRlBpTEVDNVlnUDNqRXFrSzc0aFFDcnBWL0dxMys2akY4dEsrd1pYNGZtV0JibzBMbExCL1NOYXBVcnl5M0FocTVhQUF1b2FSN3Y0eUVtVUJwZHZ5bmp5Y1JncVhRZ3VpRGgrZVluRDFHdHZOL3F4aVVodCtiaCszOHNzTmhEY0ZuSERMRWh1UEpJTE4xaU1vVUhweWxWcnliSzFqUjFsQUNjMmpLbytOa1N6Zm5YdUZPS1ZaK2hBYWdrcDFyTFhqeVhkSlRGVEFQdGRzWFd3SE1ONXdXTU5jNDFjODNFaURnQ3RIWnc1TG4xY3ZEamlvcXNtbHM0UGk0akU0djlyQUQyL2tCWmxNOWF3dE9iNElSbUFvaWJvbkhGdVZERjJDWmxMRTAvV1lPVC9BcnllbzFWQ2hTU3hkUDFvenZLQ0FOZmtNSEFWNnUxS3VhTk1ObDA1SFlBOG9YN09ZZlZyTEtVYWs4ditRMXBMaTNodVZ3WU1Qb1l4MWhMTGtId2ZVOHEvM3RVaDM0bi8vRUFDUVJBQUlDQWdJQ0F3RUJBUUVBQUFBQUFBQUJBaEVERWhBaEJERVRJa0ZSSUdHQi85b0FDQUVDQVFFL0FJdmhQL05teHVoek56WmltS2RpbUtRbUppWmE0c2p3aFBtMldiRG1XYlVPWnVLWXBpa1JtUlluUkZpWW5mSzRUTExOaXpZY2pZY2h5SElzc1RJc2pHekJpYyt5ZUJxTm9oRmlpMEpEWXZRbGZDa0tZblpaWlk1SWNoc3ZpaFFOR2pEanRtTHg3UjQyS3B0RWNDYUg0MmsyUEI5UjRtbWFPckVoSVN0R3dwa1pDbFNMWlkyWHcweUViUkRIMExFUnc3SXdlSXBSNkk0ZFlVZU5oN3RrWTJ1eDRWSWVJZUcyWmNXc2FSOFJIQ2ZIUlpzUmtSWTJYd2hKc1ViUmp4VlppZ0tCandkbmk0dFNXRWpCUlI2RjZIRzBQRVR3dVRJK0wvUllGVDZKWVN5TElrWHcrSXF5R05lakY0N2tqSDR0TG94WUtZdkhJWUVvMFk0cE1UcmhLMkxpeFV5a1VaSTJ1SUVTSkgwSlhacVFnWWNWb3dSbGpsMllvUjlpd3BPeFIvb2xSWWhzUW0zekJjcElwa2VtUlJFeFF0RWNEUGdka1BITVdOeE1jREdscjF4U0c2TFF1ZGhjUS93b2tVSkVGMllJcG1LQ0k0a3lHSkN4eFJHS29qU0xRaVNZaUNmN3owN29VdnJZaVBvVEU3NFNGRmlSamk3UEhpWW9XNkZEcWtRVFFyWkZQbFNHclJHdGg5SHlJdjhBVWZ0b3E0aWRDOWNSSW9qRVVCUU1jR2VMaFQ3SVk5R0pkRWtLeUZpTG8yVmtacDlEOTJJcElTcCt6MjZFMVExMWFGZjV6dWt1eU1CUlJDRnVqRmk2UEZoU0VpSzZOYlpHS0hTUXB1NkpNMk1jbnVPblppYlJWb1NIN0c1SmltcXBtRnljYloyZGYwbTQvak1jRXo0akRpN01PRzFSQ0NTRVdKMHhwbzlqUTE5UnFtUmhidENoZnNTMVpDWFhaRHFidGpmOEdLa0xKbzZROGpxMFBJbXUwVXZ3eHhJNGswWXNSangwTlV1YTZzaStxSEQ5WERWZERpNU13eFN0aW5Ub3l0L2hCOUQ5aWRNY3FiRTBpVGlZL1RzcW1ab1ZLa1k0a0lzeHhwRUZUR2lRaUY4cnMwc2tvWTJrMzdHOWVrU1VtMU1tbHFSbFVTM1RGays2Rks1c2JmZEg3YjlINVE2VXRUeU9wS1NNYW94cE1naUo2RzR0MHpTVjBPTGlySXpiTmhOcHNqTi8weVpkdWlMYWwyYSs0aXZSSm16U1BtYVhRNTFrdEVaMWJGUHF5TTNOMStDK2tML28wMG5ML2hPcHdhWDRSUmdSRVVhUTVVZlVXU01FVG51aURhZE1qUktEcGpiZ3VoYXk5bytKdFdSdi93QlBYUk5LK2lUZXhWeDY0YzNWSXdzbE51a0x2eDZ2OU1iSW93UTFSQ3VKdXh5YUpXeUxrTEpKTDBRcVhadERMaXVMRlVlcjZIanBYRVRjUzAxeFBWM3FORVJHcGhWZGtwczNwS0ppYWwyUWlZMVNJdWh5WkoyckpweFZqdnNUSXhiWXBMQkJpazliUTU5MFlzbXZYNFNhUnVTbFNzM3IyTmNSUlNQU0lMN0RVZG1ScUNQL3hBQW5FUUFDQWdJQ0FRUUNBZ01BQUFBQUFBQUFBUUlSQXhJUUlURVRJQ0pCQkhFeVlTTXpVZi9hQUFnQkF3RUJQd0QzN0d6Tml6ZENtYkNhWlpiTmx3bVh3bnp0U05tV3paR3hiTFJac2pZMkxZbVdKaWsvczlRakpOQ2RDa0pvdmhPL1phTE5pemIvQUlXWHhaWll1TmhlV1FkTWM2OEN5S2lNMVFtUlloTytOa1dpeTFRNU10K3p3aE8wTHNpN1F4QzVWMlJOcmtSWW1LUzRzVDR2bXhPanlQaEswTGl1aWhHaFZjUlhZamVoU0w1VDlsRjBqOUhiUW9pWFFrUml4NDJPRkNvajBPSW9Nb3Q4Smo0WEs0WmJvVVJJU0ZGMlFneU9PdXhRSEUwdGlqU0VreFFKUU5FTmRGY29aZENraXl4SVJGSml4ZllvTXhRWnFxS284bWxLeHhIL0FFUmRFbWhvMEhEc1NkbGNQeU5pRW1VSkNWa0lXaUVlaFJFaHpKTWlMd09DYUhGWFJYUk45a0dXaGlYTDRpUlhDUkdKQ0lsUTJLVmtvMFZYa1VxTmtRbmNSU2JtMEx3TmJTRkd1eGo4Q1ZsSDF4K3lLRUlpUVZNaWhqcWlDZG5wMUMyVGRDbVg5a1A0bU5YTjN3cXRrZUpjc1pHSWtKT3VJbU5Oc1ZKZGpta2JpeUpHTEp2R2pJcWJSclVoRWVva2Z1dUlvU29zWHlaNDRZaElTSVJwQ2dLS1pqY1lrMjMyaDk4ZG40OHFkR1dOcTBhaVRLU2lLTFJCMHpKNmFmeEd5VDhtS0hWa3VoY0pFSVdSZ2FLeUxwOWpXajY0ak91aHhVa1YyelcxMFFkU01meWlTaWtTa3ZUWFFxb2RIYXRubERzeDQzZGpkS2grT1ZGTWhHa0xoeHNwMDB5a2hvaTZkRldoV2k1SDQwclJsWFhSUC9XaUNzbWlmZ2luUW9OczZpbVJkcXhvYVJSQklqeFpqK1hINkVVWWNNNXB2NlJVVjRKSDQzVG8xdERqY1dqREM0dGtvV1RYWkdEbDRJd1VVMk51ck1LdE5Na2hvb2l1TmhkbUdVWUttVkdUczFrbDBkbXpJWlpRR2xXMGZCVm9nbkdSQkpvOUp0MFk4VlFZOFo2V3pZc1hweHNuOHBVaFY0STNHWGZFMGFzaEZVT0JvMGhXaTJKdUs2STVCRGdtYWFrSjZTL29VRTFjUlkwWXZCamlxRkZLeFlpT0ZSTTlVUHl5djhoSmNOQ1ZQaXlQeU5VT0k4WkNEYm95NDU0WlV5TWlLVWowa1lvdURFa3lFSlFmWkIzd21pVTZSa2RpeGlnaWJwZXhJb2lSUnFmRWluWmMwKzJSVVpLMmlPTWhBV01na1Y4U0RvMlk1c2NteUtUR04wZnk2UC9aIiBpZD0iYyIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiLz48L2RlZnM+PC9zdmc+", + "description": "Displays the concentration of airborne allergens, including pollen and mold spores, which can trigger allergic reactions in sensitive individuals as a simplified chart. Optionally may display the corresponding latest concentration of allergens value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'IAI_level', label: 'IAI', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'IAI_level', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Air Quality Index\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 12) {\\n\\tvalue = 12;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 12) {\\n\\tvalue = 12;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#3B911C\"},{\"from\":2,\"to\":6,\"color\":\"#7CC322\"},{\"from\":6,\"to\":9,\"color\":\"#F77410\"},{\"from\":9,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:c2ltcGxlLUlBSS12YWx1ZS1hbmQtY2hhcnQtY2FyZC1iYWNrZ3JvdW5kLnBuZw==:c2ltcGxlLUlBSS12YWx1ZS1hbmQtY2hhcnQtY2FyZC1iYWNrZ3JvdW5kLnBuZw==;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"IAI\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:flower-pollen\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":null,\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "air", + "aqi", + "pollution", + "emission", + "smog" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_leaf_wetness_chart_card.json b/application/src/main/data/json/system/widget_types/simple_leaf_wetness_chart_card.json new file mode 100644 index 0000000000..f4c88ec08c --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_leaf_wetness_chart_card.json @@ -0,0 +1,35 @@ +{ + "fqn": "simple_leaf_wetness_chart_card", + "name": "Simple leaf wetness chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAllBMVEUAAADf39/g4ODf39/f39/g4OD////k5OQhISHg4ODi4uJLcN2QkJDy8vLHx8c9PT1YWFgvLy90dHQ8PDysrKxhguHV1dVKSkqCgoLo7furq6u6urr19/2enp5mZma7yfK5ubl4lOWdnZ3S2/alt+6ar+yOpepWed/H0vSwwPBti+OOpuqDnefd5PmwwfAtLS3FxcWcnJygPPf3AAAABnRSTlMAIL9AEN/GQiaNAAAEwUlEQVR42uzPyw2AIBQAsMdHDCRs4P5zmjiBRyDtBg0AAAAAAID/atpc/Rolj3tz4yoRaTxte3OUyLMdYObo7QhdZDEiqxF52a23VTthKArDd4PBZM4cNCSiKOILtH3/l6u69truU2lLi0TwR0QzycVHLrS2bkht3ZDauiG1dUNq698gOaOW/hQydPjcxIJa+iXk+w9dM7ykHp8S9vgqKn7fSZAcuDd/hDhNBsioA6xj47alEVgErt+nAmUYTPOoAtioad+l8rLr6AyIRD5KHyAamzZAYtMw2cRpgw40oWKa9qlHw9IL90eUtmk7pNhN0cSvu0YcnQAp/Bpi7JHpJAGlgdA9Fl2KAbHfpkYH6j4aKOu1KqwpMM2OGek4kjMgmWuTJ+neQxyj90ym6/0VgjCGJTpKfkyfEEdReu8puY0hAQ3LaHh2BsSRHD3X8ntIZhIRS1EQDogWWpgK5DF9A1mYRQRirqMzkT52eHYGREjtuNbigLg1tCW7ybqYnT8gjgELGyCUnCdDbOwFYrETpwiTZc5LFAsBr50AQWjtG9cUzxa/FiCFUbd72U7EO+yVHuZnQAJjByibbZS9YG4ZR0jZ1zsyCI5OgMg8kKzoc/e3kKM+8pviQt0/jbV1Q2rrhtTWDamtG1JbN+T/Z9k5uzzENHArpGtDXGRYZjdrSy8XhiiLw6O+jfmykIGN4Zm0US4KEd++e43hohCl4G09x0tCfrJvd7uNwkAYhs8+jTw2mIADBlJCEshv9/4vb5fSirAJC2wqQSWeO3gzmLEiIWiFNu2pnxiyIoG2NUU/MKQZSEOS8/NCmoE0lK9nH+JspEzU/UAkHm1pM++QwKWKK+9evQJPaE9NHqKk9sjTgcCDiPRWAc7q6yLiuB17XFA0dUjikY5kFJKbPJ5hiVrsk5Yy0uQ3Hf3n/fWQ1GKo+OvaJHyK0bK7Ow4iDok8vVPooHxXvR6SmVpRZ9yYzQmDCAoVair0RPvqEWIEh3SrZB0EiRgbcuBaicqVi7M5poC16PPmi8dLU3Osx9g1N3qVrDyquIEYFXLjfF+xqFwMkHEGGNM/kLjrOdckMM7WJx04Yr2pXh5RIIQTa6K3ZERIzgc0TF6H/OJs3HZTviuaqA3GEju/nkPk4JNY+eSuxNCQC5/RqB6rA5+sKcdeN9aeG6AiNEn8j7UTbAVatm9EOhkWwnwo8luG2pnNlQ2uxo6+bojq95ORJq9nHmMnFQ4KSbl2SfEhy01pMz6jh3JX+FusPaJQKnwvNSzkwsVpX/L9SUmNQZ+AHEyieyGm9ZE/3r+RrS2uhcU/uBoT6N/sGbPFJ8sHezxe2KToJCnBYFOFGJMWfMKeD+gSU4RJdIeUbD73e4patUJyBiyX6BBRqDCJ7pAT8xmwhnPUPlbIjVO8d4UELkUTdPRvds7zY/Nk2dxWAyrei+fL3QlJO5hAX0ha8h9mj5aC+elAHE3+RBn9hz2tb4xt73v7PCPG5JqQkZoMT87hA8AXQ+RMMl4OiWaSMYc/6JaQJWTelpC5WULmZgmZmyVkbpaQ3+3cMQ0AIAwAwUDDUg341wkSGEtz5+ANfDVCqmkU0ma5E9mgZOe4W6r1/5ZqRqdRGAAAAAAAwJMDbJv6K5dRMTYAAAAASUVORK5CYII=", + "description": "Displays historical leaf wetness values as a simplified chart. Optionally may display the corresponding latest leaf wetness value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'leaf', label: 'Leaf wetness', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'leaf', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Leaf wetness\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#4B70DD\"},{\"from\":10,\"to\":50,\"color\":\"#FFA600\"},{\"from\":50,\"to\":90,\"color\":\"#F36900\"},{\"from\":90,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Leaf wetness\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:leaf\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"%\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "dew", + "leaf moisture", + "foliage dampness", + "leaf humidity", + "foliar moisture" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_leaf_wetness_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_leaf_wetness_chart_card_with_background.json new file mode 100644 index 0000000000..39fb317de1 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_leaf_wetness_chart_card_with_background.json @@ -0,0 +1,35 @@ +{ + "fqn": "simple_leaf_wetness_chart_card_with_background", + "name": "Simple leaf wetness chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical leaf wetness values as a simplified chart with background. Optionally may display the corresponding latest leaf wetness value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'leaf', label: 'Leaf wetness', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'leaf', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Leaf wetness\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#4369DD\"},{\"from\":10,\"to\":50,\"color\":\"#F89E0D\"},{\"from\":50,\"to\":90,\"color\":\"#F77410\"},{\"from\":90,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Leaf wetness\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:leaf\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"%\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "dew", + "leaf moisture", + "foliage dampness", + "leaf humidity", + "foliar moisture" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_neon_gauge.json b/application/src/main/data/json/system/widget_types/simple_neon_gauge.json new file mode 100644 index 0000000000..fd84396430 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_neon_gauge.json @@ -0,0 +1,21 @@ +{ + "fqn": "digital_gauges.simple_neon_gauge_justgage", + "name": "Simple neon gauge", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Preconfigured gauge to display any value reading as a doughnut. Allows to configure value range, gradient colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "", + "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-digital-gauge-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#388e3c\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#000000\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":1,\"levelColors\":[],\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Segment7Standard\",\"style\":\"normal\",\"weight\":\"500\",\"size\":32},\"minMaxFont\":{\"family\":\"Segment7Standard\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\"},\"neonGlowBrightness\":40,\"dashThickness\":1.5,\"gaugeType\":\"donut\",\"animation\":true,\"animationDuration\":500,\"animationRule\":\"linear\"},\"title\":\"Simple neon gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_nitrogen_dioxide__no2__chart_card.json b/application/src/main/data/json/system/widget_types/simple_nitrogen_dioxide__no2__chart_card.json new file mode 100644 index 0000000000..8ce3983149 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_nitrogen_dioxide__no2__chart_card.json @@ -0,0 +1,40 @@ +{ + "fqn": "simple_nitrogen_dioxide_no2_chart_card", + "name": "Simple nitrogen dioxide (NO2) chart card", + "deprecated": false, + "image": "tb-image:c2ltcGxlLU5PMi12YWx1ZS1hbmQtY2hhcnQtY2FyZC5zdmc=:c2ltcGxlLU5PMi12YWx1ZS1hbmQtY2hhcnQtY2FyZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjEwOCIgeT0iMjYiIGZpbGw9IiNmZmYiIHJ4PSI0Ii8+PHJlY3Qgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxMDciIHg9Ii41IiB5PSIyNi41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMTIiIHJ4PSIzLjUiLz48ZyBmaWx0ZXI9InVybCgjYSkiPjxyZWN0IHdpZHRoPSIxODIiIGhlaWdodD0iOTAiIHg9IjkiIHk9IjM1IiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii43NiIgcng9IjMuMDA4Ii8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjg3IiBkPSJNMTkgNDAuMTY3QTUuODM1IDUuODM1IDAgMCAwIDEzLjE2NyA0NiA1LjgzNSA1LjgzNSAwIDAgMCAxOSA1MS44MzMgNS44MzUgNS44MzUgMCAwIDAgMjQuODMzIDQ2IDUuODM1IDUuODM1IDAgMCAwIDE5IDQwLjE2N1pNMTQuMzMzIDQ2YzAtLjM1Ni4wNDctLjcwNi4xMjMtMS4wMzhsMi43ODggMi43ODh2LjU4M2ExLjE3IDEuMTcgMCAwIDAgMS4xNjcgMS4xNjd2MS4xMjZBNC42NzMgNC42NzMgMCAwIDEgMTQuMzMzIDQ2Wm04LjEwMyAzLjE1YTEuMTYyIDEuMTYyIDAgMCAwLTEuMTA4LS44MTdoLS41ODR2LTEuNzVhLjU4NS41ODUgMCAwIDAtLjU4My0uNTgzaC0zLjV2LTEuMTY3aDEuMTY3Yy4zMiAwIC41ODMtLjI2Mi41ODMtLjU4M3YtMS4xNjdoMS4xNjdhMS4xNyAxLjE3IDAgMCAwIDEuMTY2LTEuMTY2di0uMjRBNC42NjggNC42NjggMCAwIDEgMjMuNjY3IDQ2YTQuNjYyIDQuNjYyIDAgMCAxLTEuMjMxIDMuMTVaTTQwLjkyMiA0MS4wNDdWNTFoLTEuNzE1bC00LjQ2NC03LjEzVjUxaC0xLjcxNnYtOS45NTNoMS43MTZsNC40NzcgNy4xNDN2LTcuMTQzaDEuNzAyWm0zLjg5NCAyLjU1NlY1MWgtMS42NTR2LTcuMzk3aDEuNjU0Wm0tMS43NjQtMS45NGMwLS4yNTIuMDgyLS40NTkuMjQ2LS42MjMuMTY5LS4xNjkuNDAxLS4yNTMuNjk4LS4yNTMuMjkxIDAgLjUyMS4wODQuNjkuMjUzYS44MzEuODMxIDAgMCAxIC4yNTMuNjIyLjgyMy44MjMgMCAwIDEtLjI1My42MTVjLS4xNjkuMTY0LS4zOTkuMjQ2LS42OS4yNDYtLjI5NyAwLS41MjktLjA4Mi0uNjk4LS4yNDZhLjgzNS44MzUgMCAwIDEtLjI0Ni0uNjE1Wm03LjIxIDEuOTR2MS4yMDRoLTQuMTd2LTEuMjA0aDQuMTdabS0yLjk2OC0xLjgxMWgxLjY0OHY3LjE2NGMwIC4yMjguMDMyLjQwMy4wOTYuNTI2YS41MTIuNTEyIDAgMCAwIC4yOC4yNGMuMTE4LjA0LjI1Ny4wNjEuNDE3LjA2MS4xMTQgMCAuMjIzLS4wMDcuMzI4LS4wMmEzLjI5IDMuMjkgMCAwIDAgLjI1My0uMDQxbC4wMDcgMS4yNThhNC4yMTIgNC4yMTIgMCAwIDEtLjQ3OS4xMDkgMy41MTQgMy41MTQgMCAwIDEtLjYxNS4wNDhjLS4zNzggMC0uNzEzLS4wNjYtMS4wMDUtLjE5OWExLjQ4IDEuNDggMCAwIDEtLjY4My0uNjYzYy0uMTY0LS4zMDUtLjI0Ny0uNzEtLjI0Ny0xLjIxNnYtNy4yNjdabTYuMTQzIDMuMjJWNTFINTEuNzl2LTcuMzk3aDEuNTcybC4wNzUgMS40MDlabTIuMjYzLTEuNDU2LS4wMTQgMS41M2E0LjI5MSA0LjI5MSAwIDAgMC0uNjctLjA1NWMtLjI4Mi4wMDEtLjUzLjA0Mi0uNzQ1LjEyNGExLjQwMyAxLjQwMyAwIDAgMC0uNTQuMzQyYy0uMTQxLjE0Ni0uMjUuMzI0LS4zMjguNTMzLS4wNzguMjEtLjEyMy40NDUtLjEzNy43MDRsLS4zNzYuMDI4YzAtLjQ2NS4wNDYtLjg5Ni4xMzctMS4yOTIuMDkxLS4zOTcuMjI4LS43NDUuNDEtMS4wNDYuMTg3LS4zMDEuNDItLjUzNi42OTctLjcwNGExLjg3IDEuODcgMCAwIDEgLjk3OC0uMjUzYy4xIDAgLjIwNy4wMDkuMzIxLjAyNy4xMTkuMDE4LjIwOC4wMzkuMjY3LjA2MlptLjc5IDMuODI4di0uMTU3YzAtLjUzNC4wNzctMS4wMjguMjMyLTEuNDg0LjE1NS0uNDYuMzc4LS44NTkuNjctMS4xOTZhMy4wMjMgMy4wMjMgMCAwIDEgMS4wOC0uNzkzIDMuNTE2IDMuNTE2IDAgMCAxIDEuNDUtLjI4N2MuNTQyIDAgMS4wMjUuMDk2IDEuNDQ5LjI4Ny40MjguMTg3Ljc5LjQ1MSAxLjA4Ny43OTMuMjk2LjMzNy41MjEuNzM2LjY3NiAxLjE5Ni4xNTUuNDU2LjIzMy45NS4yMzMgMS40ODR2LjE1N2MwIC41MzMtLjA3OCAxLjAyNy0uMjMzIDEuNDgzLS4xNTUuNDU2LS4zOC44NTUtLjY3NiAxLjE5N2EzLjEzNSAzLjEzNSAwIDAgMS0xLjA4Ljc5MiAzLjU0IDMuNTQgMCAwIDEtMS40NDMuMjggMy42MDcgMy42MDcgMCAwIDEtMS40NTYtLjI4IDMuMTM2IDMuMTM2IDAgMCAxLTEuMDgtLjc5MyAzLjU1IDMuNTUgMCAwIDEtLjY3Ny0xLjE5NiA0LjU3OSA0LjU3OSAwIDAgMS0uMjMyLTEuNDgzWm0xLjY0Ny0uMTU3di4xNTdjMCAuMzMzLjAzNC42NDcuMTAzLjk0M2EyLjQgMi40IDAgMCAwIC4zMjEuNzhjLjE0Ni4yMjMuMzMzLjM5OC41Ni41MjYuMjI4LjEyNy41LjE5MS44MTQuMTkxLjMwNSAwIC41Ny0uMDY0Ljc5My0uMTkxLjIyOC0uMTI4LjQxNS0uMzAzLjU2LS41MjdhMi40IDIuNCAwIDAgMCAuMzIyLS43NzljLjA3My0uMjk2LjExLS42MS4xMS0uOTQzdi0uMTU3YzAtLjMyOS0uMDM3LS42MzgtLjExLS45M2EyLjM5IDIuMzkgMCAwIDAtLjMyOC0uNzg2IDEuNTY4IDEuNTY4IDAgMCAwLS41Ni0uNTM0IDEuNTQzIDEuNTQzIDAgMCAwLS44LS4xOThjLS4zMSAwLS41OC4wNjYtLjgwNy4xOThhMS41ODggMS41ODggMCAwIDAtLjU1NC41MzRjLS4xNDYuMjI4LS4yNTMuNDktLjMyMS43ODZhNC4wNjcgNC4wNjcgMCAwIDAtLjEwMy45M1ptMTEuNTk4LTMuNjIzaDEuNDk3djcuMTljMCAuNjY2LS4xNDEgMS4yMzEtLjQyNCAxLjY5NmEyLjcyNCAyLjcyNCAwIDAgMS0xLjE4MyAxLjA2Yy0uNTA1LjI0Ni0xLjA5MS4zNjktMS43NTYuMzY5LS4yODMgMC0uNTk3LS4wNDEtLjk0NC0uMTIzYTMuODY1IDMuODY1IDAgMCAxLS45OTgtLjM5NyAyLjQ3NCAyLjQ3NCAwIDAgMS0uOC0uNzA0bC43NzMtLjk3Yy4yNjQuMzE0LjU1Ni41NDQuODc1LjY5LjMxOS4xNDYuNjU0LjIxOSAxLjAwNS4yMTkuMzc4IDAgLjctLjA3Ljk2NC0uMjEyLjI2OS0uMTM3LjQ3Ni0uMzQuNjIyLS42MDguMTQ2LS4yNy4yMTktLjU5Ny4yMTktLjk4NXYtNS41NWwuMTUtMS42NzZabS01LjAyNSAzLjc4di0uMTQ0YzAtLjU2LjA2OS0xLjA3LjIwNi0xLjUzMS4xMzYtLjQ2NS4zMzItLjg2NC41ODctMS4xOTYuMjU2LS4zMzcuNTY2LS41OTUuOTMtLjc3M2EyLjcyOSAyLjcyOSAwIDAgMSAxLjIzNy0uMjczYy40NzkgMCAuODg3LjA4NiAxLjIyNC4yNi4zNDIuMTczLjYyNy40MjEuODU1Ljc0NS4yMjcuMzE5LjQwNS43MDEuNTMzIDEuMTQ4LjEzMi40NDIuMjMuOTM0LjI5NCAxLjQ3N3YuNDU4YTcuNCA3LjQgMCAwIDEtLjMwMSAxLjQ0OSA0IDQgMCAwIDEtLjU2IDEuMTM1Yy0uMjMzLjMxOS0uNTIuNTY1LS44NjIuNzM4LS4zMzcuMTczLS43MzYuMjYtMS4xOTYuMjYtLjQ1MSAwLS44Ni0uMDk0LTEuMjI0LS4yOGEyLjc4NCAyLjc4NCAwIDAgMS0uOTMtLjc4NyAzLjcyMyAzLjcyMyAwIDAgMS0uNTg4LTEuMTkgNS4yMzggNS4yMzggMCAwIDEtLjIwNS0xLjQ5NlptMS42NDgtLjE0NHYuMTQ0YzAgLjMzNy4wMzIuNjUxLjA5Ni45NDMuMDY4LjI5Mi4xNy41NS4zMDcuNzczLjE0Mi4yMTguMzIuMzkyLjUzMy41Mi4yMi4xMjIuNDc3LjE4NC43NzMuMTg0LjM4NyAwIC43MDQtLjA4Mi45NS0uMjQ2LjI1LS4xNjQuNDQyLS4zODYuNTc0LS42NjMuMTM3LS4yODMuMjMzLS41OTguMjg3LS45NDR2LTEuMjM3YTMuMDE4IDMuMDE4IDAgMCAwLS4xNy0uNzUyIDIuMDEzIDIuMDEzIDAgMCAwLS4zMzUtLjYwOSAxLjQgMS40IDAgMCAwLS41MzQtLjQxYy0uMjE0LS4xLS40NjctLjE1LS43NTgtLjE1LS4yOTcgMC0uNTU0LjA2NC0uNzczLjE5MWExLjU2OCAxLjU2OCAwIDAgMC0uNTQuNTI3Yy0uMTM3LjIyMy0uMjQuNDgzLS4zMDguNzc5YTQuMjIyIDQuMjIyIDAgMCAwLS4xMDIuOTVabTEwLjA5NCAzLjg5N2EzLjk0MyAzLjk0MyAwIDAgMS0xLjQ4NC0uMjY3IDMuMjc1IDMuMjc1IDAgMCAxLTEuMTItLjc1OSAzLjM2MSAzLjM2MSAwIDAgMS0uNzA1LTEuMTQxIDMuOTk3IDMuOTk3IDAgMCAxLS4yNDYtMS40MTV2LS4yNzRjMC0uNTc4LjA4NC0xLjEwMy4yNTMtMS41NzIuMTY4LS40Ny40MDMtLjg3LjcwNC0xLjIwMy4zLS4zMzcuNjU2LS41OTUgMS4wNjYtLjc3My40MS0uMTc3Ljg1NS0uMjY2IDEuMzMzLS4yNjYuNTMgMCAuOTkyLjA4OSAxLjM4OC4yNjYuMzk3LjE3OC43MjUuNDI5Ljk4NC43NTIuMjY1LjMyLjQ2LjcuNTg4IDEuMTQyLjEzMy40NDIuMTk5LjkzLjE5OSAxLjQ2M3YuNzA0aC01LjcxNVY0Ni42MWg0LjA4OHYtLjEzYTIuMzEyIDIuMzEyIDAgMCAwLS4xNzgtLjgzNCAxLjQxNyAxLjQxNyAwIDAgMC0uNDg1LS42MjhjLS4yMi0uMTYtLjUxLS4yNC0uODc1LS4yNC0uMjc0IDAtLjUxOC4wNi0uNzMyLjE3OC0uMjEuMTE0LS4zODUuMjgtLjUyNi41LS4xNDIuMjE4LS4yNS40ODItLjMyOC43OTItLjA3My4zMDUtLjExLjY1LS4xMSAxLjAzMnYuMjc0YzAgLjMyMy4wNDQuNjI0LjEzLjkwMi4wOTEuMjczLjIyNC41MTMuMzk3LjcxOC4xNzMuMjA1LjM4Mi4zNjcuNjI5LjQ4NS4yNDYuMTE0LjUyNi4xNzEuODQuMTcxLjM5NyAwIC43NS0uMDggMS4wNi0uMjQuMzEtLjE1OS41NzktLjM4NC44MDctLjY3NmwuODY4Ljg0Yy0uMTYuMjMzLS4zNjcuNDU3LS42MjIuNjctLjI1NS4yMS0uNTY4LjM4MS0uOTM3LjUxNGEzLjcyNiAzLjcyNiAwIDAgMS0xLjI3MS4xOThabTYuMTI5LTUuOTU0VjUxaC0xLjY0OHYtNy4zOTdoMS41NTJsLjA5NiAxLjU4Wm0tLjI5NCAxLjg0NS0uNTM0LS4wMDZhNC44MTIgNC44MTIgMCAwIDEgLjIyLTEuNDQzYy4xNDUtLjQzNy4zNDYtLjgxMy42LTEuMTI4LjI2LS4zMTQuNTctLjU1Ni45My0uNzI0YTIuNzQgMi43NCAwIDAgMSAxLjIwNC0uMjZjLjM1NSAwIC42NzYuMDUuOTYzLjE1LjI5Mi4wOTYuNTQuMjUzLjc0NS40NzIuMjEuMjE5LjM3LjUwMy40NzkuODU0LjExLjM0Ny4xNjQuNzczLjE2NCAxLjI3OVY1MWgtMS42NTR2LTQuNzg1YzAtLjM1Ni0uMDUzLS42MzYtLjE1Ny0uODQxYS44OC44OCAwIDAgMC0uNDQ1LS40NDRjLS4xOTEtLjA5MS0uNDMtLjEzNy0uNzE4LS4xMzctLjI4MiAwLS41MzUuMDYtLjc1OC4xNzgtLjIyNC4xMTgtLjQxMy4yOC0uNTY4LjQ4NS0uMTUuMjA1LS4yNjYuNDQyLS4zNDguNzExYTIuOTM1IDIuOTM1IDAgMCAwLS4xMjMuODYxWm0xNC45NSAyLjQ0VjQwLjVoMS42NTVWNTFoLTEuNDk3bC0uMTU3LTEuNTMxWm0tNC44MTItMi4wODR2LS4xNDRjMC0uNTYuMDY2LTEuMDcuMTk5LTEuNTMxLjEzMi0uNDY1LjMyMy0uODY0LjU3NC0xLjE5Ni4yNS0uMzM3LjU1Ni0uNTk1LjkxNi0uNzczLjM2LS4xODIuNzY1LS4yNzMgMS4yMTctLjI3My40NDYgMCAuODM4LjA4NiAxLjE3NS4yNi4zMzguMTczLjYyNS40MjEuODYyLjc0NS4yMzcuMzE5LjQyNi43MDEuNTY3IDEuMTQ4LjE0MS40NDIuMjQyLjkzNC4zIDEuNDc3di40NThhNy40IDcuNCAwIDAgMS0uMyAxLjQ0OWMtLjE0MS40MzctLjMzLjgxNi0uNTY3IDEuMTM1YTIuNDUzIDIuNDUzIDAgMCAxLS44NjguNzM4Yy0uMzM4LjE3My0uNzMyLjI2LTEuMTgzLjI2LS40NDcgMC0uODUtLjA5NC0xLjIxLS4yOGEyLjY3NCAyLjY3NCAwIDAgMS0uOTEtLjc4NyAzLjcwMSAzLjcwMSAwIDAgMS0uNTczLTEuMTkgNS40MDkgNS40MDkgMCAwIDEtLjE5OS0xLjQ5NlptMS42NDgtLjE0NHYuMTQ0YzAgLjMzNy4wMy42NTEuMDg5Ljk0My4wNjMuMjkyLjE2MS41NS4yOTQuNzczLjEzMi4yMTguMzAzLjM5Mi41MTIuNTIuMjE1LjEyMi40Ny4xODQuNzY2LjE4NC4zNzQgMCAuNjgxLS4wODIuOTIzLS4yNDYuMjQxLS4xNjQuNDMtLjM4Ni41NjctLjY2M2EzLjA4IDMuMDggMCAwIDAgLjI4Ny0uOTQ0di0xLjIzN2EzLjAxOCAzLjAxOCAwIDAgMC0uMTctLjc1MiAyLjAxMyAyLjAxMyAwIDAgMC0uMzM2LS42MDkgMS40MTIgMS40MTIgMCAwIDAtLjUyNi0uNDEgMS42NSAxLjY1IDAgMCAwLS43MzEtLjE1Yy0uMzAxIDAtLjU1Ni4wNjQtLjc2Ni4xOTEtLjIxLjEyOC0uMzgzLjMwMy0uNTIuNTI3YTIuNTkgMi41OSAwIDAgMC0uMjk0Ljc3OSA0LjUwOCA0LjUwOCAwIDAgMC0uMDk1Ljk1Wm04LjU1NS0zLjYzN1Y1MWgtMS42NTR2LTcuMzk3aDEuNjU0Wm0tMS43NjMtMS45NGMwLS4yNTIuMDgyLS40NTkuMjQ2LS42MjMuMTY4LS4xNjkuNDAxLS4yNTMuNjk3LS4yNTMuMjkyIDAgLjUyMi4wODQuNjkxLjI1M2EuODMzLjgzMyAwIDAgMSAuMjUyLjYyMi44MjQuODI0IDAgMCAxLS4yNTIuNjE1Yy0uMTY5LjE2NC0uMzk5LjI0Ni0uNjkxLjI0Ni0uMjk2IDAtLjUyOS0uMDgyLS42OTctLjI0NmEuODM1LjgzNSAwIDAgMS0uMjQ2LS42MTVabTMuNTA0IDUuNzJ2LS4xNTZjMC0uNTM0LjA3Ny0xLjAyOC4yMzItMS40ODQuMTU1LS40Ni4zNzgtLjg1OS42Ny0xLjE5NmEzLjAyIDMuMDIgMCAwIDEgMS4wOC0uNzkzIDMuNTE2IDMuNTE2IDAgMCAxIDEuNDQ5LS4yODdjLjU0MyAwIDEuMDI2LjA5NiAxLjQ1LjI4Ny40MjguMTg3Ljc5LjQ1MSAxLjA4Ni43OTMuMjk3LjMzNy41MjIuNzM2LjY3NyAxLjE5Ni4xNTUuNDU2LjIzMy45NS4yMzMgMS40ODR2LjE1N2MwIC41MzMtLjA3OCAxLjAyNy0uMjMzIDEuNDgzLS4xNTUuNDU2LS4zOC44NTUtLjY3NyAxLjE5N2EzLjEzMiAzLjEzMiAwIDAgMS0xLjA4Ljc5MmMtLjQyMy4xODctLjkwNC4yOC0xLjQ0Mi4yOGEzLjYwNyAzLjYwNyAwIDAgMS0xLjQ1Ni0uMjggMy4xMzIgMy4xMzIgMCAwIDEtMS4wOC0uNzkzIDMuNTU1IDMuNTU1IDAgMCAxLS42NzctMS4xOTYgNC41NzkgNC41NzkgMCAwIDEtLjIzMi0xLjQ4M1ptMS42NDctLjE1NnYuMTU3YzAgLjMzMy4wMzQuNjQ3LjEwMy45NDMuMDY4LjI5Ni4xNzUuNTU2LjMyMS43OC4xNDYuMjIzLjMzMy4zOTguNTYuNTI2LjIyOC4xMjcuNS4xOTEuODE0LjE5MS4zMDUgMCAuNTctLjA2NC43OTMtLjE5MS4yMjgtLjEyOC40MTUtLjMwMy41Ni0uNTI3LjE0Ni0uMjIzLjI1My0uNDgzLjMyMi0uNzc5YTMuOTMgMy45MyAwIDAgMCAuMTA5LS45NDN2LS4xNTdjMC0uMzI5LS4wMzYtLjYzOC0uMTA5LS45M2EyLjQxIDIuNDEgMCAwIDAtLjMyOC0uNzg2IDEuNTcgMS41NyAwIDAgMC0uNTYxLS41MzQgMS41NDMgMS41NDMgMCAwIDAtLjgtLjE5OGMtLjMxIDAtLjU3OC4wNjYtLjgwNi4xOThhMS41ODQgMS41ODQgMCAwIDAtLjU1NC41MzRjLS4xNDYuMjI4LS4yNTMuNDktLjMyMS43ODZhNC4wMjggNC4wMjggMCAwIDAtLjEwMy45M1ptNy45NjgtMy42MjMgMS40MDggMi40NTQgMS40MzYtMi40NTVoMS44MTFsLTIuMjQyIDMuNjNMMTE4LjcyOCA1MWgtMS44MTFsLTEuNTA0LTIuNTU3TDExMy45MDkgNTFoLTEuODE5bDIuMzI1LTMuNzY3LTIuMjM2LTMuNjNoMS44MDVabTcuNzk3IDBWNTFoLTEuNjU1di03LjM5N2gxLjY1NVptLTEuNzY0LTEuOTQyYzAtLjI1LjA4Mi0uNDU4LjI0Ni0uNjIyLjE2OS0uMTY5LjQwMS0uMjUzLjY5Ny0uMjUzLjI5MiAwIC41MjIuMDg0LjY5MS4yNTNhLjgzNC44MzQgMCAwIDEgLjI1My42MjIuODI1LjgyNSAwIDAgMS0uMjUzLjYxNWMtLjE2OS4xNjQtLjM5OS4yNDYtLjY5MS4yNDYtLjI5NiAwLS41MjgtLjA4Mi0uNjk3LS4yNDZhLjgzNS44MzUgMCAwIDEtLjI0Ni0uNjE1Wm04LjMzIDcuODA3VjQwLjVoMS42NTRWNTFoLTEuNDk3bC0uMTU3LTEuNTMxWm0tNC44MTItMi4wODV2LS4xNDRjMC0uNTYuMDY2LTEuMDcuMTk4LTEuNTMxLjEzMi0uNDY1LjMyMy0uODY0LjU3NC0xLjE5Ni4yNTEtLjMzNy41NTYtLjU5NS45MTYtLjc3My4zNi0uMTgyLjc2Ni0uMjczIDEuMjE3LS4yNzMuNDQ3IDAgLjgzOC4wODYgMS4xNzYuMjYuMzM3LjE3My42MjQuNDIxLjg2MS43NDUuMjM3LjMxOS40MjYuNzAxLjU2NyAxLjE0OC4xNDIuNDQyLjI0Mi45MzQuMzAxIDEuNDc3di40NThhNy4zMjcgNy4zMjcgMCAwIDEtLjMwMSAxLjQ0OWMtLjE0MS40MzctLjMzLjgxNi0uNTY3IDEuMTM1YTIuNDUgMi40NSAwIDAgMS0uODY4LjczOGMtLjMzNy4xNzMtLjczMi4yNi0xLjE4My4yNi0uNDQ2IDAtLjg1LS4wOTQtMS4yMS0uMjhhMi42OCAyLjY4IDAgMCAxLS45MDktLjc4NyAzLjY5IDMuNjkgMCAwIDEtLjU3NC0xLjE5IDUuNDEzIDUuNDEzIDAgMCAxLS4xOTgtMS40OTZabTEuNjQ3LS4xNDR2LjE0NGMwIC4zMzcuMDMuNjUxLjA4OS45NDMuMDY0LjI5Mi4xNjIuNTUuMjk0Ljc3My4xMzIuMjE4LjMwMy4zOTIuNTEzLjUyLjIxNC4xMjIuNDY5LjE4NC43NjUuMTg0LjM3NCAwIC42ODItLjA4Mi45MjMtLjI0NmExLjY3IDEuNjcgMCAwIDAgLjU2Ny0uNjYzYy4xNDItLjI4My4yMzctLjU5OC4yODgtLjk0NHYtMS4yMzdhMy4wNzcgMy4wNzcgMCAwIDAtLjE3MS0uNzUyIDIuMDE4IDIuMDE4IDAgMCAwLS4zMzUtLjYwOSAxLjQxNiAxLjQxNiAwIDAgMC0uNTI3LS40MWMtLjIwNS0uMS0uNDQ5LS4xNS0uNzMxLS4xNS0uMzAxIDAtLjU1Ni4wNjQtLjc2Ni4xOTFhMS41NSAxLjU1IDAgMCAwLS41MTkuNTI3IDIuNiAyLjYgMCAwIDAtLjI5NC43NzkgNC40OTUgNC40OTUgMCAwIDAtLjA5Ni45NVptMTAuMDUzIDMuODk3YTMuOTUgMy45NSAwIDAgMS0xLjQ4NC0uMjY3IDMuMjggMy4yOCAwIDAgMS0xLjEyMS0uNzU5IDMuMzY3IDMuMzY3IDAgMCAxLS43MDQtMS4xNDEgMy45OTkgMy45OTkgMCAwIDEtLjI0Ni0xLjQxNXYtLjI3NGMwLS41NzguMDg0LTEuMTAzLjI1My0xLjU3Mi4xNjktLjQ3LjQwMy0uODcuNzA0LTEuMjAzYTMuMDIxIDMuMDIxIDAgMCAxIDEuMDY3LS43NzNjLjQxLS4xNzcuODU0LS4yNjYgMS4zMzMtLjI2Ni41MjggMCAuOTkxLjA4OSAxLjM4Ny4yNjYuMzk3LjE3OC43MjUuNDI5Ljk4NS43NTIuMjY0LjMyLjQ2LjcuNTg4IDEuMTQyLjEzMi40NDIuMTk4LjkzLjE5OCAxLjQ2M3YuNzA0aC01LjcxNVY0Ni42MWg0LjA4OHYtLjEzYTIuMjk3IDIuMjk3IDAgMCAwLS4xNzgtLjgzNCAxLjQxNiAxLjQxNiAwIDAgMC0uNDg1LS42MjhjLS4yMTktLjE2LS41MTEtLjI0LS44NzUtLjI0LS4yNzQgMC0uNTE3LjA2LS43MzIuMTc4YTEuNDUgMS40NSAwIDAgMC0uNTI2LjUgMi42MjEgMi42MjEgMCAwIDAtLjMyOC43OTIgNC40NSA0LjQ1IDAgMCAwLS4xMSAxLjAzMnYuMjc0YzAgLjMyMy4wNDQuNjI0LjEzLjkwMi4wOTIuMjczLjIyNC41MTMuMzk3LjcxOC4xNzMuMjA1LjM4My4zNjcuNjI5LjQ4NS4yNDYuMTE0LjUyNi4xNzEuODQxLjE3MS4zOTYgMCAuNzQ5LS4wOCAxLjA1OS0uMjQuMzEtLjE1OS41NzktLjM4NC44MDctLjY3NmwuODY4Ljg0Yy0uMTYuMjMzLS4zNjcuNDU3LS42MjIuNjctLjI1NS4yMS0uNTY4LjM4MS0uOTM3LjUxNGEzLjcyMiAzLjcyMiAwIDAgMS0xLjI3MS4xOThaIi8+PHBhdGggZmlsbD0iIzNCOTExQyIgZD0iTTIxIDk2Ljg3OVYxMTRoLTIuODI0di0xMy43N2wtNC4xODQgMS40MTh2LTIuMzMybDYuNjY4LTIuNDM3SDIxWm0xNy4wNSA3LjE2djIuNzg5YzAgMS4zMzYtLjEzMiAyLjQ3Ny0uMzk4IDMuNDIyLS4yNTguOTM4LS42MzMgMS42OTktMS4xMjUgMi4yODVhNC40MSA0LjQxIDAgMCAxLTEuNzcgMS4yODljLS42NzkuMjc0LTEuNDQuNDEtMi4yODQuNDFhNi41ODQgNi41ODQgMCAwIDEtMS44NzUtLjI1NyA0LjU4NiA0LjU4NiAwIDAgMS0xLjU0Ny0uODA5IDQuODMzIDQuODMzIDAgMCAxLTEuMTg0LTEuNDE4Yy0uMzItLjU4Ni0uNTctMS4yODUtLjc1LTIuMDk4LS4xNzItLjgxMi0uMjU4LTEuNzU0LS4yNTgtMi44MjR2LTIuNzg5YzAtMS4zNDQuMTMzLTIuNDc3LjM5OS0zLjM5OC4yNjUtLjkzLjY0NC0xLjY4NCAxLjEzNy0yLjI2MmE0LjMzIDQuMzMgMCAwIDEgMS43NTctMS4yNzdjLjY4OC0uMjY2IDEuNDUzLS4zOTkgMi4yOTctLjM5OS42OCAwIDEuMzA1LjA4NiAxLjg3NS4yNThhNC4zOSA0LjM5IDAgMCAxIDEuNTQ3Ljc4NWMuNDYxLjM2Ljg1Mi44MjggMS4xNzIgMS40MDYuMzI4LjU3LjU3OCAxLjI2Mi43NSAyLjA3NS4xNzIuODA0LjI1OCAxLjc0Mi4yNTggMi44MTJabS0yLjgyMyAzLjE4OHYtMy42MWMwLS42NzktLjA0LTEuMjc3LS4xMTgtMS43OTMtLjA3OC0uNTIzLS4xOTUtLjk2NS0uMzUxLTEuMzI0YTIuNjQgMi42NCAwIDAgMC0uNTYzLS44OSAxLjk4IDEuOTggMCAwIDAtLjc3My0uNTA0IDIuNzMxIDIuNzMxIDAgMCAwLS45NzMtLjE2NWMtLjQzNyAwLS44MjguMDg2LTEuMTcyLjI1OC0uMzQzLjE2NC0uNjMzLjQzLS44NjcuNzk3LS4yMzQuMzY3LS40MTQuODUyLS41MzkgMS40NTMtLjExNy41OTQtLjE3NiAxLjMxNy0uMTc2IDIuMTY4djMuNjFjMCAuNjg3LjA0IDEuMjkzLjExNyAxLjgxNi4wNzkuNTIzLjE5Ni45NzMuMzUyIDEuMzQ4LjE1Ni4zNjcuMzQ0LjY3MS41NjMuOTE0YTIuMSAyLjEgMCAwIDAgLjc3My41MTVjLjI5Ny4xMS42MjEuMTY0Ljk3My4xNjQuNDQ1IDAgLjg0LS4wODYgMS4xODMtLjI1Ny4zNDQtLjE3Mi42MzMtLjQ0Ni44NjctLjgyMS4yMzUtLjM4My40MS0uODc5LjUyOC0xLjQ4OC4xMTctLjYwOS4xNzYtMS4zNC4xNzYtMi4xOTFaTTUyLjU5MyAxMDEuMzJoMi44MzZWMTE0aC0yLjYxM2wtLjIyMy0yLjkzdi05Ljc1Wm0uNDEgNi43ODUgMS4xMTQtLjAyM2MwIC44OTEtLjA4NiAxLjcxMS0uMjU4IDIuNDYxLS4xNjQuNzUtLjQyMiAxLjQwMi0uNzczIDEuOTU3YTMuNyAzLjcgMCAwIDEtMS4zMTMgMS4yODljLS41MzEuMzA1LTEuMTYuNDU3LTEuODg3LjQ1N2E0LjYyOSA0LjYyOSAwIDAgMS0xLjU1OC0uMjQ2IDMuMSAzLjEgMCAwIDEtMS4xOTUtLjc5N2MtLjMzNi0uMzU5LS42MTQtLjgyOC0uODMzLTEuNDA2bC0uMzYzLTMuMTk5aDEuNjA2YzAgLjcwMy4wNTggMS4yNzcuMTc1IDEuNzIyLjExOC40NDYuMjgyLjc4OS40OTMgMS4wMzIuMjEuMjM0LjQ1Ny4zOTguNzM4LjQ5Mi4yODkuMDk0LjYwMi4xNC45MzguMTQuNjE3IDAgMS4xMjQtLjA5MyAxLjUyMy0uMjgxLjQwNi0uMTk1LjcyMy0uNDY1Ljk1LS44MDguMjMzLS4zNTIuMzk4LS43NjIuNDkxLTEuMjMxLjEwMi0uNDc2LjE1My0uOTk2LjE1My0xLjU1OVptLTguMjczLTYuNzg1aDIuODEzdjE3LjU1NUg0NC43M1YxMDEuMzJabTIyLjEyNSAwaDIuNTY3djEyLjMyOGMwIDEuMTQxLS4yNDMgMi4xMS0uNzI3IDIuOTA3YTQuNjY4IDQuNjY4IDAgMCAxLTIuMDI3IDEuODE2Yy0uODY4LjQyMi0xLjg3MS42MzMtMy4wMTIuNjMzLS40ODQgMC0xLjAyMy0uMDctMS42MTctLjIxMWE2LjYyNCA2LjYyNCAwIDAgMS0xLjcxMS0uNjggNC4yMzQgNC4yMzQgMCAwIDEtMS4zNzEtMS4yMDdsMS4zMjQtMS42NjRjLjQ1My41MzkuOTUzLjkzNCAxLjUgMS4xODQuNTQ3LjI1IDEuMTIxLjM3NSAxLjcyMy4zNzUuNjQ4IDAgMS4xOTktLjEyMSAxLjY1Mi0uMzYzLjQ2LS4yMzUuODE2LS41ODMgMS4wNjYtMS4wNDMuMjUtLjQ2MS4zNzUtMS4wMjQuMzc1LTEuNjg4di05LjUxNmwuMjU4LTIuODcxWm0tOC42MTMgNi40ODF2LS4yNDZjMC0uOTYxLjExNy0xLjgzNi4zNTItMi42MjUuMjM0LS43OTcuNTctMS40ODEgMS4wMDctMi4wNTFhNC4zOSA0LjM5IDAgMCAxIDEuNTk0LTEuMzI0Yy42MjUtLjMxMyAxLjMzMi0uNDY5IDIuMTIxLS40NjkuODIgMCAxLjUyLjE0OCAyLjA5OC40NDVhMy45NjMgMy45NjMgMCAwIDEgMS40NjUgMS4yNzhjLjM5LjU0Ni42OTUgMS4yMDMuOTE0IDEuOTY4LjIyNi43NTguMzk0IDEuNjAyLjUwNCAyLjUzMnYuNzg1YTEyLjY3NCAxMi42NzQgMCAwIDEtLjUxNiAyLjQ4NGMtLjI0Mi43NS0uNTYzIDEuMzk5LS45NiAxLjk0NS0uNC41NDctLjg5Mi45NjktMS40NzcgMS4yNjYtLjU3OS4yOTctMS4yNjIuNDQ1LTIuMDUxLjQ0NS0uNzc0IDAtMS40NzMtLjE2LTIuMDk4LS40OGE0Ljc3MyA0Ljc3MyAwIDAgMS0xLjU5NC0xLjM0OCA2LjM4NCA2LjM4NCAwIDAgMS0xLjAwOC0yLjAzOSA4Ljk3MyA4Ljk3MyAwIDAgMS0uMzUxLTIuNTY2Wm0yLjgyNC0uMjQ2di4yNDZjMCAuNTc4LjA1NSAxLjExNy4xNjQgMS42MTcuMTE3LjUuMjkzLjk0MS41MjggMS4zMjQuMjQyLjM3NS41NDYuNjcyLjkxNC44OTEuMzc1LjIxMS44MTYuMzE2IDEuMzI0LjMxNi42NjQgMCAxLjIwNy0uMTQgMS42MjktLjQyMi40My0uMjgxLjc1OC0uNjYuOTg0LTEuMTM2YTUuNzU5IDUuNzU5IDAgMCAwIC40OTItMS42MTh2LTIuMTIxYTUuMTgyIDUuMTgyIDAgMCAwLS4yOTMtMS4yODkgMy40NTUgMy40NTUgMCAwIDAtLjU3NC0xLjA0MyAyLjQwMyAyLjQwMyAwIDAgMC0uOTE0LS43MDNjLS4zNjctLjE3Mi0uOC0uMjU4LTEuMy0uMjU4LS41MDggMC0uOTUuMTEtMS4zMjUuMzI5YTIuNjggMi42OCAwIDAgMC0uOTI2LjkwMmMtLjIzNC4zODMtLjQxLjgyOC0uNTI3IDEuMzM2YTcuMjM5IDcuMjM5IDAgMCAwLS4xNzYgMS42MjlaTTc5Ljc3IDk2LjkzN2wtNi42NDQgMTguNTI4SDcwLjkxbDYuNjU2LTE4LjUyOGgyLjIwM1ptNC43ODEgNi45NjFWMTE0aC0yLjgyNHYtMTIuNjhoMi42NmwuMTY1IDIuNTc4Wm0tLjQ1NiAzLjI5My0uOTYxLS4wMTFjMC0uODc1LjEwOS0xLjY4NC4zMjgtMi40MjYuMjE4LS43NDIuNTM5LTEuMzg3Ljk2LTEuOTM0YTQuMjMgNC4yMyAwIDAgMSAxLjU3LTEuMjc3Yy42MzQtLjMwNSAxLjM2NC0uNDU3IDIuMTkyLS40NTcuNTc4IDAgMS4xMDYuMDg2IDEuNTgyLjI1OC40ODUuMTY0LjkwMy40MjYgMS4yNTQuNzg1LjM2LjM1OS42MzMuODIuODIgMS4zODMuMTk2LjU2Mi4yOTQgMS4yNDIuMjk0IDIuMDM5VjExNGgtMi44MjV2LTguMjAzYzAtLjYxNy0uMDkzLTEuMTAyLS4yOC0xLjQ1M2ExLjU1NSAxLjU1NSAwIDAgMC0uNzg2LS43NWMtLjMzNi0uMTU2LS43MzgtLjIzNS0xLjIwNy0uMjM1LS41MzEgMC0uOTg0LjEwMi0xLjM2LjMwNWEyLjU2MyAyLjU2MyAwIDAgMC0uOTAyLjgzMiAzLjczIDMuNzMgMCAwIDAtLjUxNSAxLjIxOWMtLjExLjQ2MS0uMTY0Ljk1My0uMTY0IDEuNDc2Wm03Ljg2My0uNzUtMS4zMjQuMjkzYzAtLjc2NS4xMDUtMS40ODguMzE2LTIuMTY4YTUuNTIgNS41MiAwIDAgMSAuOTUtMS44MDQgNC40NSA0LjQ1IDAgMCAxIDEuNTU4LTEuMjMxYy42MTctLjI5NyAxLjMyNC0uNDQ1IDIuMTItLjQ0NS42NSAwIDEuMjI3LjA5IDEuNzM1LjI2OS41MTYuMTcyLjk1My40NDYgMS4zMTMuODIxLjM2LjM3NS42MzMuODYzLjgyIDEuNDY1LjE4OC41OTMuMjgxIDEuMzEyLjI4MSAyLjE1NlYxMTRIOTYuODl2LTguMjE1YzAtLjY0LS4wOTMtMS4xMzctLjI4LTEuNDg4YTEuNDU4IDEuNDU4IDAgMCAwLS43NzQtLjcyN2MtLjMzNi0uMTQtLjczOS0uMjExLTEuMjA3LS4yMTEtLjQzOCAwLS44MjUuMDgyLTEuMTYuMjQ2YTIuMzEgMi4zMSAwIDAgMC0uODMyLjY2OCAyLjk4NyAyLjk4NyAwIDAgMC0uNTE2Ljk3M2MtLjExLjM2Ny0uMTY0Ljc2Ni0uMTY0IDEuMTk1Wm0xMi4zODctNS43MDdoMS4wMTljLjMzNiAwIC42MTMtLjA0Ni44MzItLjE0LjIxOS0uMTAyLjM4My0uMjM5LjQ5Mi0uNDEuMTE3LS4xNzIuMTc2LS4zNzUuMTc2LS42MWEuOTk0Ljk5NCAwIDAgMC0uMzI4LS43MzhjLS4yMTEtLjIxOS0uNTY2LS4zMjgtMS4wNjYtLjMyOC0uMzgzIDAtLjcwOC4wODItLjk3My4yNDYtLjI1OC4xNTYtLjM4Ny4zNzktLjM4Ny42NjhoLTIuMTMzYzAtLjU1NS4xNTctMS4wMjguNDY5LTEuNDE4LjMxMy0uMzkuNzMxLS42ODggMS4yNTQtLjg5YTQuNjkgNC42OSAwIDAgMSAxLjcxMS0uMzA1Yy43MTEgMCAxLjMzNi4xMDEgMS44NzUuMzA0LjUzOS4xOTYuOTU3LjQ4OSAxLjI1NC44OC4zMDUuMzkuNDU3Ljg3NC40NTcgMS40NTIgMCAuNDQ2LS4xNDEuODQtLjQyMiAxLjE4NC0uMjczLjM0NC0uNjU2LjYxMy0xLjE0OC44MDktLjQ4NS4xODctMS4wNTUuMjgxLTEuNzExLjI4MWgtMS4zNzF2LS45ODVabTAgMS40NjV2LS45NjFoMS4zNzFjLjcyNiAwIDEuMzQ3LjA4NiAxLjg2My4yNTguNTE2LjE2NC45MDYuNDE4IDEuMTcyLjc2Mi4yNzMuMzQ0LjQxLjc4OS40MSAxLjMzNiAwIC41NzgtLjE2NCAxLjA3LS40OTIgMS40NzYtLjMyOC40MDctLjc3OC43MTUtMS4zNDguOTI2LS41NjIuMjExLTEuMTk1LjMxNi0xLjg5OC4zMTZhNS4yOSA1LjI5IDAgMCAxLTEuNzExLS4yODFjLS41NDctLjE5NS0xLS41LTEuMzYtLjkxNC0uMzUxLS40MTQtLjUyNy0uOTU3LS41MjctMS42MjloMi4xNDVjMCAuMzA1LjE0LjU2Ny40MjEuNzg1LjI4Mi4yMTkuNjYxLjMyOSAxLjEzNy4zMjkuNTA4IDAgLjg4My0uMTEgMS4xMjUtLjMyOWExLjA5IDEuMDkgMCAwIDAgLjM3NS0uODQzYzAtLjI5Ny0uMDctLjUzNS0uMjExLS43MTVhMS4wOTEgMS4wOTEgMCAwIDAtLjU3NC0uMzg3IDIuNzA1IDIuNzA1IDAgMCAwLS44NzktLjEyOWgtMS4wMTlaIi8+PHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1vcGFjaXR5PSIuODciIHN0cm9rZS13aWR0aD0iMiIgZD0ibTExMS43NiAxMTcuMTQ4IDcuMjE3LTguNzgzYTYuNzgyIDYuNzgyIDAgMCAwIDEuNDItMy4wMTZsMy4yNTUtMTYuNzIzYy43My0zLjc1MSA2LjA5OC0zLjc1MSA2LjgyOCAwbC4yMDggMS4wNjZjLjcxMyAzLjY2MyA2LjAwMyAzLjUxMiA2LjUwNi0uMTg1djBjLjQ3NS0zLjQ4NSA1LjM0Ni0zLjkwNiA2LjQxMi0uNTU0bDIuMzIyIDcuMzAyYzEuNjE3IDUuMDg4IDkuMDAzIDQuNDc3IDkuNzYyLS44MDdsMS4xMDctNy42OTRjMS4wNTctNy4zNTcgMTEuNDU4LTcuOTM2IDEzLjMyNS0uNzQxbDEuMDIyIDMuOTM4Yy44ODMgMy40MDIgNC45MiA0LjgzOSA3Ljc1MyAyLjc2djBhNC45NzIgNC45NzIgMCAwIDEgNi4yODEuMzI0bDQuNDI3IDQuMDE0Ii8+PGRlZnM+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjE5MS4wMjMiIGhlaWdodD0iOTkuMDIzIiB4PSI0LjQ4OSIgeT0iMzAuNDg5IiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIyLjI1NiIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMDYyXzI3OTciLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzEwNjJfMjc5NyIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjwvZGVmcz48L3N2Zz4=", + "description": "A gas produced from vehicle emissions and the burning of fossil fuels. Results displayed by combining the latest and aggregated values and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'nitrogen', label: 'Nitrogen dioxide', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'nitrogen', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Nitrogen dioxide\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#3FA71A\"},{\"from\":40,\"to\":90,\"color\":\"#80C32C\"},{\"from\":90,\"to\":120,\"color\":\"#FFA600\"},{\"from\":120,\"to\":230,\"color\":\"#F36900\"},{\"from\":230,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Nitrogen dioxide\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"public\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"µg/m³\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "nitrogen", + "nitrogen dioxide" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_nitrogen_dioxide__no2__chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_nitrogen_dioxide__no2__chart_card_with_background.json new file mode 100644 index 0000000000..b93e1570a4 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_nitrogen_dioxide__no2__chart_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "simple_nitrogen_dioxide_no2_chart_card_with_background", + "name": "Simple nitrogen dioxide (NO2) chart card with background", + "deprecated": false, + "image": "tb-image:Tk8yLXNpbXBsZS1jaGFydC12YWx1ZS1jYXJkLXdpdGgtYmFja2dyb3VuZC5zdmc=:Tk8yLXNpbXBsZS1jaGFydC12YWx1ZS1jYXJkLXdpdGgtYmFja2dyb3VuZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxMTIiIHk9IjI0IiBmaWxsPSJ1cmwoI2EpIiByeD0iNCIvPjxnIGZpbHRlcj0idXJsKCNiKSI+PHJlY3Qgd2lkdGg9IjE4OCIgaGVpZ2h0PSIxMDAiIHg9IjYiIHk9IjMwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii43NiIgcng9IjMuMDA4Ii8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjg3IiBkPSJNMjQuOTA3IDM5LjA0N1Y0OWgtMS43MTZsLTQuNDYzLTcuMTNWNDloLTEuNzE2di05Ljk1M2gxLjcxNWw0LjQ3OCA3LjE0M3YtNy4xNDNoMS43MDJabTMuODk0IDIuNTU2VjQ5aC0xLjY1NHYtNy4zOTdIMjguOFptLTEuNzY0LTEuOTRjMC0uMjUyLjA4Mi0uNDU5LjI0Ni0uNjIzLjE2OS0uMTY5LjQwMS0uMjUzLjY5Ny0uMjUzLjI5MiAwIC41MjIuMDg0LjY5LjI1M2EuODMxLjgzMSAwIDAgMSAuMjU0LjYyMi44MjMuODIzIDAgMCAxLS4yNTMuNjE1Yy0uMTY5LjE2NC0uMzk5LjI0Ni0uNjkuMjQ2LS4yOTcgMC0uNTMtLjA4Mi0uNjk4LS4yNDZhLjgzNC44MzQgMCAwIDEtLjI0Ni0uNjE1Wm03LjIxIDEuOTR2MS4yMDRoLTQuMTd2LTEuMjA0aDQuMTdabS0yLjk2OC0xLjgxMWgxLjY0OHY3LjE2NGMwIC4yMjguMDMyLjQwMy4wOTYuNTI2YS41MTEuNTExIDAgMCAwIC4yOC4yNGMuMTE4LjA0LjI1Ny4wNjEuNDE3LjA2MS4xMTQgMCAuMjIzLS4wMDcuMzI4LS4wMmEzLjI5IDMuMjkgMCAwIDAgLjI1My0uMDQxbC4wMDcgMS4yNThhNC4yMDUgNC4yMDUgMCAwIDEtLjQ3OS4xMDkgMy41MTQgMy41MTQgMCAwIDEtLjYxNS4wNDhjLS4zNzggMC0uNzEzLS4wNjYtMS4wMDUtLjE5OWExLjQ4IDEuNDggMCAwIDEtLjY4NC0uNjYzYy0uMTY0LS4zMDUtLjI0Ni0uNzEtLjI0Ni0xLjIxNnYtNy4yNjdabTYuMTQzIDMuMjJWNDloLTEuNjQ4di03LjM5N2gxLjU3M2wuMDc1IDEuNDA5Wm0yLjI2My0xLjQ1Ni0uMDE0IDEuNTNhNC4yOTMgNC4yOTMgMCAwIDAtLjY3LS4wNTVjLS4yODMuMDAxLS41My4wNDItLjc0NS4xMjRhMS40MDQgMS40MDQgMCAwIDAtLjU0LjM0MmMtLjE0Mi4xNDYtLjI1LjMyNC0uMzI4LjUzMy0uMDc4LjIxLS4xMjMuNDQ1LS4xMzcuNzA0bC0uMzc2LjAyOGMwLS40NjUuMDQ2LS44OTYuMTM3LTEuMjkyYTMuNTMgMy41MyAwIDAgMSAuNDEtMS4wNDZjLjE4Ny0uMzAxLjQyLS41MzYuNjk3LS43MDQuMjgzLS4xNjkuNjA4LS4yNTMuOTc4LS4yNTMuMSAwIC4yMDcuMDA5LjMyMS4wMjcuMTE4LjAxOC4yMDcuMDM5LjI2Ny4wNjJabS43OSAzLjgyOHYtLjE1N2MwLS41MzQuMDc3LTEuMDI4LjIzMi0xLjQ4NC4xNTUtLjQ2LjM3OC0uODU5LjY3LTEuMTk2YTMuMDI0IDMuMDI0IDAgMCAxIDEuMDgtLjc5MyAzLjUxNiAzLjUxNiAwIDAgMSAxLjQ1LS4yODdjLjU0MiAwIDEuMDI1LjA5NiAxLjQ0OS4yODcuNDI4LjE4Ny43OS40NTEgMS4wODYuNzkzLjI5Ny4zMzcuNTIyLjczNi42NzcgMS4xOTYuMTU1LjQ1Ni4yMzMuOTUuMjMzIDEuNDg0di4xNTdjMCAuNTMzLS4wNzggMS4wMjctLjIzMyAxLjQ4M2EzLjU1IDMuNTUgMCAwIDEtLjY3NyAxLjE5NyAzLjEzNiAzLjEzNiAwIDAgMS0xLjA4Ljc5MiAzLjU0IDMuNTQgMCAwIDEtMS40NDIuMjggMy42MDcgMy42MDcgMCAwIDEtMS40NTYtLjI4IDMuMTM1IDMuMTM1IDAgMCAxLTEuMDgtLjc5MyAzLjU1MSAzLjU1MSAwIDAgMS0uNjc3LTEuMTk2IDQuNTgyIDQuNTgyIDAgMCAxLS4yMzItMS40ODNabTEuNjQ3LS4xNTd2LjE1N2MwIC4zMzMuMDM0LjY0Ny4xMDMuOTQzYTIuNCAyLjQgMCAwIDAgLjMyLjc4Yy4xNDcuMjIzLjMzNC4zOTguNTYxLjUyNi4yMjguMTI3LjUuMTkxLjgxNC4xOTEuMzA1IDAgLjU3LS4wNjQuNzkzLS4xOTEuMjI4LS4xMjguNDE1LS4zMDMuNTYtLjUyNy4xNDYtLjIyMy4yNTMtLjQ4My4zMjItLjc3OS4wNzMtLjI5Ni4xMS0uNjEuMTEtLjk0M3YtLjE1N2MwLS4zMjktLjAzNy0uNjM4LS4xMS0uOTNhMi4zODcgMi4zODcgMCAwIDAtLjMyOC0uNzg2IDEuNTcgMS41NyAwIDAgMC0uNTYtLjUzNCAxLjU0NCAxLjU0NCAwIDAgMC0uOC0uMTk4Yy0uMzEgMC0uNTguMDY2LS44MDcuMTk4YTEuNTg3IDEuNTg3IDAgMCAwLS41NTQuNTM0Yy0uMTQ2LjIyOC0uMjUzLjQ5LS4zMjEuNzg2YTQuMDYyIDQuMDYyIDAgMCAwLS4xMDMuOTNabTExLjU5OC0zLjYyM2gxLjQ5N3Y3LjE5YzAgLjY2Ni0uMTQyIDEuMjMxLS40MjQgMS42OTZhMi43MjQgMi43MjQgMCAwIDEtMS4xODMgMS4wNmMtLjUwNS4yNDYtMS4wOTEuMzY5LTEuNzU3LjM2OS0uMjgyIDAtLjU5Ni0uMDQxLS45NDMtLjEyM2EzLjg2NSAzLjg2NSAwIDAgMS0uOTk4LS4zOTcgMi40NzQgMi40NzQgMCAwIDEtLjgtLjcwNGwuNzczLS45N2MuMjY0LjMxNC41NTYuNTQ0Ljg3NS42OS4zMTkuMTQ2LjY1NC4yMTkgMS4wMDUuMjE5LjM3OCAwIC43LS4wNy45NjMtLjIxMi4yNy0uMTM3LjQ3Ny0uMzQuNjIzLS42MDguMTQ1LS4yNy4yMTgtLjU5Ny4yMTgtLjk4NXYtNS41NWwuMTUtMS42NzZabS01LjAyNSAzLjc4di0uMTQ0YzAtLjU2LjA2OS0xLjA3LjIwNS0xLjUzMS4xMzctLjQ2NS4zMzMtLjg2NC41ODgtMS4xOTYuMjU1LS4zMzcuNTY1LS41OTUuOTMtLjc3M2EyLjcyOSAyLjcyOSAwIDAgMSAxLjIzNy0uMjczYy40NzkgMCAuODg3LjA4NiAxLjIyNC4yNi4zNDIuMTczLjYyNy40MjEuODU0Ljc0NS4yMjguMzE5LjQwNi43MDEuNTM0IDEuMTQ4LjEzMi40NDIuMjMuOTM0LjI5MyAxLjQ3N3YuNDU4YTcuNCA3LjQgMCAwIDEtLjMgMS40NDkgNCA0IDAgMCAxLS41NiAxLjEzNWMtLjIzMy4zMTktLjUyLjU2NS0uODYyLjczOC0uMzM3LjE3My0uNzM2LjI2LTEuMTk2LjI2LS40NTIgMC0uODYtLjA5NC0xLjIyNC0uMjhhMi43ODIgMi43ODIgMCAwIDEtLjkzLS43ODcgMy43MjMgMy43MjMgMCAwIDEtLjU4OC0xLjE5IDUuMjM4IDUuMjM4IDAgMCAxLS4yMDUtMS40OTZabTEuNjQ4LS4xNDR2LjE0NGMwIC4zMzcuMDMyLjY1MS4wOTUuOTQzLjA2OS4yOTIuMTcxLjU1LjMwOC43NzMuMTQxLjIxOC4zMi4zOTIuNTMzLjUyLjIxOS4xMjIuNDc2LjE4NC43NzMuMTg0LjM4NyAwIC43MDQtLjA4Mi45NS0uMjQ2LjI1LS4xNjQuNDQyLS4zODYuNTc0LS42NjMuMTM3LS4yODMuMjMzLS41OTguMjg3LS45NDR2LTEuMjM3YTMuMDIzIDMuMDIzIDAgMCAwLS4xNy0uNzUyIDIuMDExIDIuMDExIDAgMCAwLS4zMzYtLjYwOSAxLjM5OSAxLjM5OSAwIDAgMC0uNTMzLS40MWMtLjIxNC0uMS0uNDY3LS4xNS0uNzU5LS4xNS0uMjk2IDAtLjU1My4wNjQtLjc3Mi4xOTFhMS41NjkgMS41NjkgMCAwIDAtLjU0LjUyN2MtLjEzNy4yMjMtLjI0LjQ4My0uMzA4Ljc3OWE0LjIyMiA0LjIyMiAwIDAgMC0uMTAyLjk1Wm0xMC4wOTMgMy44OTdhMy45NDQgMy45NDQgMCAwIDEtMS40ODMtLjI2NyAzLjI3NSAzLjI3NSAwIDAgMS0xLjEyMS0uNzU5IDMuMzY0IDMuMzY0IDAgMCAxLS43MDQtMS4xNDEgMy45OTkgMy45OTkgMCAwIDEtLjI0Ni0xLjQxNXYtLjI3NGMwLS41NzguMDg0LTEuMTAzLjI1My0xLjU3Mi4xNjgtLjQ3LjQwMy0uODcuNzA0LTEuMjAzLjMtLjMzNy42NTYtLjU5NSAxLjA2Ni0uNzczLjQxLS4xNzcuODU1LS4yNjYgMS4zMzMtLjI2Ni41MjkgMCAuOTkyLjA4OSAxLjM4OC4yNjYuMzk3LjE3OC43MjUuNDI5Ljk4NC43NTIuMjY1LjMyLjQ2LjcuNTg4IDEuMTQyLjEzMi40NDIuMTk5LjkzLjE5OSAxLjQ2M3YuNzA0aC01LjcxNVY0NC42MWg0LjA4N3YtLjEzYTIuMzEyIDIuMzEyIDAgMCAwLS4xNzctLjgzNCAxLjQxNyAxLjQxNyAwIDAgMC0uNDg2LS42MjhjLS4yMTgtLjE2LS41MS0uMjQtLjg3NS0uMjQtLjI3MyAwLS41MTcuMDYtLjczMS4xNzgtLjIxLjExNC0uMzg1LjI4LS41MjYuNWEyLjYzIDIuNjMgMCAwIDAtLjMyOC43OTJjLS4wNzMuMzA1LS4xMS42NS0uMTEgMS4wMzJ2LjI3NGMwIC4zMjMuMDQzLjYyNC4xMy45MDIuMDkxLjI3My4yMjMuNTEzLjM5Ny43MTguMTczLjIwNS4zODIuMzY3LjYyOC40ODUuMjQ2LjExNC41MjcuMTcxLjg0MS4xNzEuMzk3IDAgLjc1LS4wOCAxLjA2LS4yNC4zMS0uMTU5LjU3OS0uMzg0LjgwNi0uNjc2bC44NjkuODRjLS4xNi4yMzMtLjM2Ny40NTctLjYyMy42Ny0uMjU1LjIxLS41NjcuMzgxLS45MzYuNTE0YTMuNzI3IDMuNzI3IDAgMCAxLTEuMjcxLjE5OFptNi4xMy01Ljk1NFY0OWgtMS42NDh2LTcuMzk3aDEuNTUybC4wOTUgMS41OFptLS4yOTQgMS44NDUtLjUzNC0uMDA2YTQuODEyIDQuODEyIDAgMCAxIC4yMTktMS40NDNjLjE0Ni0uNDM3LjM0Ni0uODEzLjYwMi0xLjEyOC4yNi0uMzE0LjU3LS41NTYuOTMtLjcyNC4zNi0uMTc0Ljc2LS4yNiAxLjIwMi0uMjYuMzU2IDAgLjY3Ny4wNS45NjQuMTUuMjkyLjA5Ni41NC4yNTMuNzQ1LjQ3Mi4yMS4yMTkuMzcuNTAzLjQ3OS44NTQuMTEuMzQ3LjE2NC43NzMuMTY0IDEuMjc5VjQ5aC0xLjY1NHYtNC43ODVjMC0uMzU2LS4wNTMtLjYzNi0uMTU4LS44NDFhLjg4Ljg4IDAgMCAwLS40NDQtLjQ0NGMtLjE5MS0uMDkxLS40My0uMTM3LS43MTgtLjEzNy0uMjgyIDAtLjUzNS4wNi0uNzU4LjE3OC0uMjI0LjExOC0uNDEzLjI4LS41NjguNDg1LS4xNS4yMDUtLjI2Ni40NDItLjM0OS43MTFhMi45MzggMi45MzggMCAwIDAtLjEyMy44NjFabTE0Ljk1IDIuNDRWMzguNWgxLjY1NVY0OUg4MS4zOGwtLjE1Ny0xLjUzMVptLTQuODEyLTIuMDg0di0uMTQ0YzAtLjU2LjA2Ni0xLjA3LjE5OC0xLjUzMS4xMzMtLjQ2NS4zMjQtLjg2NC41NzUtMS4xOTYuMjUtLjMzNy41NTYtLjU5NS45MTYtLjc3My4zNi0uMTgyLjc2NS0uMjczIDEuMjE2LS4yNzMuNDQ3IDAgLjgzOS4wODYgMS4xNzYuMjYuMzM4LjE3My42MjUuNDIxLjg2Mi43NDUuMjM3LjMxOS40MjYuNzAxLjU2NyAxLjE0OC4xNDEuNDQyLjI0Mi45MzQuMyAxLjQ3N3YuNDU4YTcuMzkyIDcuMzkyIDAgMCAxLS4zIDEuNDQ5Yy0uMTQxLjQzNy0uMzMuODE2LS41NjcgMS4xMzVhMi40NTEgMi40NTEgMCAwIDEtLjg2OS43MzhjLS4zMzcuMTczLS43MzEuMjYtMS4xODIuMjYtLjQ0NyAwLS44NS0uMDk0LTEuMjEtLjI4YTIuNjc0IDIuNjc0IDAgMCAxLS45MS0uNzg3IDMuNzAzIDMuNzAzIDAgMCAxLS41NzQtMS4xOSA1LjQwNSA1LjQwNSAwIDAgMS0uMTk4LTEuNDk2Wm0xLjY0OC0uMTQ0di4xNDRjMCAuMzM3LjAzLjY1MS4wODguOTQzLjA2NC4yOTIuMTYyLjU1LjI5NC43NzMuMTMzLjIxOC4zMDMuMzkyLjUxMy41Mi4yMTQuMTIyLjQ3LjE4NC43NjYuMTg0LjM3MyAwIC42ODEtLjA4Mi45MjMtLjI0NmExLjY4IDEuNjggMCAwIDAgLjU2Ny0uNjYzIDMuMDggMy4wOCAwIDAgMCAuMjg3LS45NDR2LTEuMjM3YTMuMDE4IDMuMDE4IDAgMCAwLS4xNy0uNzUyIDIuMDExIDIuMDExIDAgMCAwLS4zMzYtLjYwOSAxLjQxIDEuNDEgMCAwIDAtLjUyNi0uNDEgMS42NSAxLjY1IDAgMCAwLS43MzItLjE1Yy0uMyAwLS41NTYuMDY0LS43NjUuMTkxLS4yMS4xMjgtLjM4My4zMDMtLjUyLjUyN2EyLjU5IDIuNTkgMCAwIDAtLjI5NC43NzkgNC41MDggNC41MDggMCAwIDAtLjA5NS45NVptOC41NTUtMy42MzdWNDlIODQuOTZ2LTcuMzk3aDEuNjU0Wm0tMS43NjMtMS45NGMwLS4yNTIuMDgyLS40NTkuMjQ2LS42MjMuMTY4LS4xNjkuNC0uMjUzLjY5Ny0uMjUzLjI5MiAwIC41MjIuMDg0LjY5LjI1M2EuODMxLjgzMSAwIDAgMSAuMjUzLjYyMi44MjMuODIzIDAgMCAxLS4yNTMuNjE1Yy0uMTY4LjE2NC0uMzk4LjI0Ni0uNjkuMjQ2LS4yOTYgMC0uNTI5LS4wODItLjY5Ny0uMjQ2YS44MzQuODM0IDAgMCAxLS4yNDYtLjYxNVptMy41MDQgNS43MnYtLjE1NmMwLS41MzQuMDc3LTEuMDI4LjIzMi0xLjQ4NC4xNTUtLjQ2LjM3OC0uODU5LjY3LTEuMTk2YTMuMDI0IDMuMDI0IDAgMCAxIDEuMDgtLjc5MyAzLjUxNiAzLjUxNiAwIDAgMSAxLjQ1LS4yODdjLjU0MSAwIDEuMDI1LjA5NiAxLjQ0OC4yODcuNDI5LjE4Ny43OTEuNDUxIDEuMDg3Ljc5My4yOTcuMzM3LjUyMi43MzYuNjc3IDEuMTk2LjE1NS40NTYuMjMzLjk1LjIzMyAxLjQ4NHYuMTU3YzAgLjUzMy0uMDc4IDEuMDI3LS4yMzMgMS40ODNhMy41NSAzLjU1IDAgMCAxLS42NzcgMS4xOTcgMy4xMzYgMy4xMzYgMCAwIDEtMS4wOC43OTIgMy41NCAzLjU0IDAgMCAxLTEuNDQyLjI4IDMuNjA3IDMuNjA3IDAgMCAxLTEuNDU2LS4yOCAzLjEzNSAzLjEzNSAwIDAgMS0xLjA4LS43OTMgMy41NTEgMy41NTEgMCAwIDEtLjY3Ny0xLjE5NiA0LjU4MiA0LjU4MiAwIDAgMS0uMjMzLTEuNDgzWk05MCA0NS4yMjh2LjE1N2MwIC4zMzMuMDM0LjY0Ny4xMDIuOTQzYTIuNCAyLjQgMCAwIDAgLjMyMi43OGMuMTQ2LjIyMy4zMzIuMzk4LjU2LjUyNi4yMjguMTI3LjUuMTkxLjgxNC4xOTEuMzA1IDAgLjU3LS4wNjQuNzkzLS4xOTEuMjI4LS4xMjguNDE1LS4zMDMuNTYtLjUyNy4xNDYtLjIyMy4yNTMtLjQ4My4zMjItLjc3OS4wNzItLjI5Ni4xMDktLjYxLjEwOS0uOTQzdi0uMTU3YzAtLjMyOS0uMDM3LS42MzgtLjExLS45M2EyLjM4NyAyLjM4NyAwIDAgMC0uMzI4LS43ODYgMS41NyAxLjU3IDAgMCAwLS41Ni0uNTM0IDEuNTQzIDEuNTQzIDAgMCAwLS44LS4xOThjLS4zMSAwLS41NzkuMDY2LS44MDYuMTk4YTEuNTg3IDEuNTg3IDAgMCAwLS41NTQuNTM0Yy0uMTQ2LjIyOC0uMjUzLjQ5LS4zMjIuNzg2YTQuMDY3IDQuMDY3IDAgMCAwLS4xMDIuOTNabTcuOTY4LTMuNjIzIDEuNDA4IDIuNDU0IDEuNDM1LTIuNDU1aDEuODEybC0yLjI0MiAzLjYzTDEwMi43MTMgNDloLTEuODEybC0xLjUwNC0yLjU1N0w5Ny44OTQgNDloLTEuODE5bDIuMzI0LTMuNzY3LTIuMjM1LTMuNjNoMS44MDVabTcuNzk3IDBWNDloLTEuNjU1di03LjM5N2gxLjY1NVptLTEuNzY0LTEuOTQyYzAtLjI1LjA4Mi0uNDU4LjI0Ni0uNjIyLjE2OS0uMTY5LjQwMS0uMjUzLjY5Ny0uMjUzLjI5MiAwIC41MjIuMDg0LjY5MS4yNTNhLjgzNC44MzQgMCAwIDEgLjI1My42MjIuODI1LjgyNSAwIDAgMS0uMjUzLjYxNWMtLjE2OS4xNjQtLjM5OS4yNDYtLjY5MS4yNDYtLjI5NiAwLS41MjgtLjA4Mi0uNjk3LS4yNDZhLjgzNS44MzUgMCAwIDEtLjI0Ni0uNjE1Wm04LjMzIDcuODA3VjM4LjVoMS42NTRWNDloLTEuNDk3bC0uMTU3LTEuNTMxWm0tNC44MTItMi4wODV2LS4xNDRjMC0uNTYuMDY2LTEuMDcuMTk4LTEuNTMxLjEzMi0uNDY1LjMyMy0uODY0LjU3NC0xLjE5Ni4yNTEtLjMzNy41NTYtLjU5NS45MTYtLjc3My4zNi0uMTgyLjc2Ni0uMjczIDEuMjE3LS4yNzMuNDQ2IDAgLjgzOC4wODYgMS4xNzYuMjYuMzM3LjE3My42MjQuNDIxLjg2MS43NDUuMjM3LjMxOS40MjYuNzAxLjU2NyAxLjE0OC4xNDIuNDQyLjI0Mi45MzQuMzAxIDEuNDc3di40NThhNy4zMjcgNy4zMjcgMCAwIDEtLjMwMSAxLjQ0OWMtLjE0MS40MzctLjMzLjgxNi0uNTY3IDEuMTM1YTIuNDUgMi40NSAwIDAgMS0uODY4LjczOGMtLjMzNy4xNzMtLjczMi4yNi0xLjE4My4yNi0uNDQ2IDAtLjg1LS4wOTQtMS4yMS0uMjhhMi42OCAyLjY4IDAgMCAxLS45MDktLjc4NyAzLjY5IDMuNjkgMCAwIDEtLjU3NC0xLjE5IDUuNDEzIDUuNDEzIDAgMCAxLS4xOTgtMS40OTZabTEuNjQ3LS4xNDR2LjE0NGMwIC4zMzcuMDMuNjUxLjA4OS45NDMuMDY0LjI5Mi4xNjIuNTUuMjk0Ljc3My4xMzIuMjE4LjMwMy4zOTIuNTEyLjUyLjIxNS4xMjIuNDcuMTg0Ljc2Ni4xODQuMzc0IDAgLjY4MS0uMDgyLjkyMy0uMjQ2YTEuNjcgMS42NyAwIDAgMCAuNTY3LS42NjNjLjE0Mi0uMjgzLjIzNy0uNTk4LjI4Ny0uOTQ0di0xLjIzN2EzLjAzMSAzLjAzMSAwIDAgMC0uMTctLjc1MiAyLjAxOCAyLjAxOCAwIDAgMC0uMzM1LS42MDkgMS40MTYgMS40MTYgMCAwIDAtLjUyNy0uNDFjLS4yMDUtLjEtLjQ0OS0uMTUtLjczMS0uMTUtLjMwMSAwLS41NTYuMDY0LS43NjYuMTkxYTEuNTUgMS41NSAwIDAgMC0uNTE5LjUyNyAyLjU0OCAyLjU0OCAwIDAgMC0uMjk0Ljc3OSA0LjQ5NSA0LjQ5NSAwIDAgMC0uMDk2Ljk1Wm0xMC4wNTMgMy44OTdhMy45NDYgMy45NDYgMCAwIDEtMS40ODQtLjI2NyAzLjI4IDMuMjggMCAwIDEtMS4xMjEtLjc1OSAzLjM2NyAzLjM2NyAwIDAgMS0uNzA0LTEuMTQxIDMuOTk5IDMuOTk5IDAgMCAxLS4yNDYtMS40MTV2LS4yNzRjMC0uNTc4LjA4NC0xLjEwMy4yNTMtMS41NzIuMTY5LS40Ny40MDMtLjg3LjcwNC0xLjIwM2EzLjAyNiAzLjAyNiAwIDAgMSAxLjA2Ni0uNzczIDMuMzIxIDMuMzIxIDAgMCAxIDEuMzMzLS4yNjZjLjUyOSAwIC45OTIuMDg5IDEuMzg4LjI2Ni4zOTcuMTc4LjcyNS40MjkuOTg1Ljc1Mi4yNjQuMzIuNDYuNy41ODcgMS4xNDIuMTMzLjQ0Mi4xOTkuOTMuMTk5IDEuNDYzdi43MDRoLTUuNzE1VjQ0LjYxaDQuMDg4di0uMTNhMi4zMTggMi4zMTggMCAwIDAtLjE3OC0uODM0IDEuNDIzIDEuNDIzIDAgMCAwLS40ODUtLjYyOGMtLjIxOS0uMTYtLjUxMS0uMjQtLjg3NS0uMjQtLjI3NCAwLS41MTguMDYtLjczMi4xNzhhMS40NSAxLjQ1IDAgMCAwLS41MjYuNSAyLjYyMSAyLjYyMSAwIDAgMC0uMzI4Ljc5MiA0LjQ1IDQuNDUgMCAwIDAtLjExIDEuMDMydi4yNzRjMCAuMzIzLjA0NC42MjQuMTMuOTAyLjA5MS4yNzMuMjI0LjUxMy4zOTcuNzE4LjE3My4yMDUuMzgzLjM2Ny42MjkuNDg1LjI0Ni4xMTQuNTI2LjE3MS44NC4xNzEuMzk3IDAgLjc1LS4wOCAxLjA2LS4yNC4zMS0uMTU5LjU3OS0uMzg0LjgwNy0uNjc2bC44NjguODRjLS4xNi4yMzMtLjM2Ny40NTctLjYyMi42Ny0uMjU1LjIxLS41NjguMzgxLS45MzcuNTE0YTMuNzIyIDMuNzIyIDAgMCAxLTEuMjcxLjE5OFoiLz48cGF0aCBmaWxsPSIjM0I5MTFDIiBkPSJNMjUuNTAyIDk2LjAyNVYxMTZoLTMuMjk1Vjk5LjkzNmwtNC44OCAxLjY1NHYtMi43Mmw3Ljc3OC0yLjg0NWguMzk3Wm0xOC4xMjkgOC4zNTR2My4yNTRjMCAxLjU1OC0uMTU1IDIuODg5LS40NjUgMy45OTItLjMgMS4wOTQtLjczOCAxLjk4Mi0xLjMxMyAyLjY2NmE1LjE0NyA1LjE0NyAwIDAgMS0yLjA2NCAxLjUwNGMtLjc5My4zMTktMS42ODIuNDc4LTIuNjY2LjQ3OC0uNzg0IDAtMS41MTMtLjEtMi4xODgtLjNhNS4zNDMgNS4zNDMgMCAwIDEtMS44MDQtLjk0NGMtLjUzOC0uNDI4LS45OTgtLjk4LTEuMzgxLTEuNjU0LS4zNzQtLjY4NC0uNjY1LTEuNDk5LS44NzUtMi40NDctLjItLjk0OC0uMy0yLjA0Ni0uMy0zLjI5NXYtMy4yNTRjMC0xLjU2OC4xNTQtMi44ODkuNDY0LTMuOTY1LjMxLTEuMDg1Ljc1Mi0xLjk2NCAxLjMyNi0yLjYzOWE1LjA1MiA1LjA1MiAwIDAgMSAyLjA1MS0xLjQ5Yy44MDItLjMxIDEuNjk1LS40NjUgMi42OC0uNDY1Ljc5MyAwIDEuNTIyLjEgMi4xODcuMzAxYTUuMTIyIDUuMTIyIDAgMCAxIDEuODA1LjkxNmMuNTM4LjQyLjk5My45NjYgMS4zNjcgMS42NC4zODMuNjY2LjY3NSAxLjQ3My44NzUgMi40MjEuMi45MzguMyAyLjAzMi4zIDMuMjgxWm0tMy4yOTUgMy43MTl2LTQuMjExYzAtLjc5My0uMDQ2LTEuNDkxLS4xMzctMi4wOTItLjA5LS42MTEtLjIyOC0xLjEyNi0uNDEtMS41NDUtLjE3My0uNDI4LS4zOTItLjc3NS0uNjU2LTEuMDRhMi4zMSAyLjMxIDAgMCAwLS45MDMtLjU4NyAzLjE4NyAzLjE4NyAwIDAgMC0xLjEzNC0uMTkxYy0uNTEgMC0uOTY2LjEtMS4zNjcuMy0uNDAxLjE5Mi0uNzM5LjUwMi0xLjAxMi45My0uMjc0LjQyOC0uNDgzLjk5NC0uNjMgMS42OTUtLjEzNi42OTMtLjIwNCAxLjUzNi0uMjA0IDIuNTN2NC4yMTFjMCAuODAyLjA0NSAxLjUwOC4xMzYgMi4xMTkuMDkyLjYxLjIyOCAxLjEzNS40MSAxLjU3Mi4xODMuNDI4LjQwMi43ODQuNjU3IDEuMDY2LjI2NC4yNzQuNTY1LjQ3NC45MDIuNjAyYTMuMjUgMy4yNSAwIDAgMCAxLjEzNS4xOTFjLjUyIDAgLjk4LS4xIDEuMzgtLjMuNDAyLS4yMDEuNzQtLjUyIDEuMDEzLS45NTcuMjczLS40NDcuNDc4LTEuMDI2LjYxNS0xLjczNy4xMzctLjcxMS4yMDUtMS41NjMuMjA1LTIuNTU2Wk01OC41ODYgMTAzLjMyaDIuODM2VjExNmgtMi42MTNsLS4yMjMtMi45M3YtOS43NVptLjQxIDYuNzg1IDEuMTEzLS4wMjNjMCAuODkxLS4wODYgMS43MTEtLjI1NyAyLjQ2MS0uMTY0Ljc1LS40MjIgMS40MDItLjc3NCAxLjk1N2EzLjY5OCAzLjY5OCAwIDAgMS0xLjMxMiAxLjI4OWMtLjUzMi4zMDUtMS4xNi40NTctMS44ODcuNDU3QTQuNjMgNC42MyAwIDAgMSA1NC4zMiAxMTZhMy4xIDMuMSAwIDAgMS0xLjE5NS0uNzk3Yy0uMzM2LS4zNTktLjYxMy0uODI4LS44MzItMS40MDZsLS4zNjMtMy4xOTloMS42MDVjMCAuNzAzLjA1OSAxLjI3Ny4xNzYgMS43MjIuMTE3LjQ0Ni4yODEuNzg5LjQ5MiAxLjAzMi4yMTEuMjM0LjQ1Ny4zOTguNzM4LjQ5Mi4yOS4wOTQuNjAyLjE0LjkzOC4xNC42MTcgMCAxLjEyNS0uMDkzIDEuNTIzLS4yODEuNDA3LS4xOTUuNzIzLS40NjUuOTUtLjgwOC4yMzQtLjM1Mi4zOTgtLjc2Mi40OTItMS4yMzEuMTAxLS40NzYuMTUyLS45OTYuMTUyLTEuNTU5Wm0tOC4yNzMtNi43ODVoMi44MTJ2MTcuNTU1aC0yLjgxMlYxMDMuMzJabTIyLjEyNSAwaDIuNTY2djEyLjMyOGMwIDEuMTQxLS4yNDIgMi4xMS0uNzI3IDIuOTA3YTQuNjY4IDQuNjY4IDAgMCAxLTIuMDI3IDEuODE2Yy0uODY3LjQyMi0xLjg3LjYzMy0zLjAxMi42MzMtLjQ4NCAwLTEuMDIzLS4wNy0xLjYxNy0uMjExYTYuNjI0IDYuNjI0IDAgMCAxLTEuNzEtLjY4IDQuMjM0IDQuMjM0IDAgMCAxLTEuMzcyLTEuMjA3bDEuMzI0LTEuNjY0Yy40NTQuNTM5Ljk1NC45MzQgMS41IDEuMTg0LjU0Ny4yNSAxLjEyMS4zNzUgMS43MjMuMzc1LjY0OCAwIDEuMi0uMTIxIDEuNjUyLS4zNjNhMi40NjMgMi40NjMgMCAwIDAgMS4wNjctMS4wNDNjLjI1LS40NjEuMzc1LTEuMDI0LjM3NS0xLjY4OHYtOS41MTZsLjI1OC0yLjg3MVptLTguNjE0IDYuNDgxdi0uMjQ2YzAtLjk2MS4xMTgtMS44MzYuMzUyLTIuNjI1LjIzNC0uNzk3LjU3LTEuNDgxIDEuMDA4LTIuMDUxYTQuMzkgNC4zOSAwIDAgMSAxLjU5NC0xLjMyNGMuNjI0LS4zMTMgMS4zMzEtLjQ2OSAyLjEyLS40NjkuODIgMCAxLjUyLjE0OCAyLjA5OC40NDVhMy45NjIgMy45NjIgMCAwIDEgMS40NjUgMS4yNzhjLjM5LjU0Ni42OTUgMS4yMDMuOTE0IDEuOTY4LjIyNy43NTguMzk1IDEuNjAyLjUwNCAyLjUzMnYuNzg1YTEyLjY4MSAxMi42ODEgMCAwIDEtLjUxNiAyLjQ4NGMtLjI0Mi43NS0uNTYyIDEuMzk5LS45NiAxLjk0NS0uMzk5LjU0Ny0uODkxLjk2OS0xLjQ3NyAxLjI2Ni0uNTc4LjI5Ny0xLjI2Mi40NDUtMi4wNS40NDUtLjc3NCAwLTEuNDc0LS4xNi0yLjA5OS0uNDhhNC43NzIgNC43NzIgMCAwIDEtMS41OTMtMS4zNDhjLS40MzgtLjU3OC0uNzc0LTEuMjU4LTEuMDA4LTIuMDM5YTguOTc3IDguOTc3IDAgMCAxLS4zNTItMi41NjZabTIuODI1LS4yNDZ2LjI0NmMwIC41NzguMDU0IDEuMTE3LjE2NCAxLjYxNy4xMTcuNS4yOTMuOTQxLjUyNyAxLjMyNC4yNDIuMzc1LjU0Ny42NzIuOTE0Ljg5MS4zNzUuMjExLjgxNy4zMTYgMS4zMjQuMzE2LjY2NCAwIDEuMjA3LS4xNCAxLjYzLS40MjIuNDI5LS4yODEuNzU3LS42Ni45ODQtMS4xMzZhNS43NjUgNS43NjUgMCAwIDAgLjQ5Mi0xLjYxOHYtMi4xMjFhNS4xODYgNS4xODYgMCAwIDAtLjI5My0xLjI4OSAzLjQ1NSAzLjQ1NSAwIDAgMC0uNTc0LTEuMDQzIDIuNDAzIDIuNDAzIDAgMCAwLS45MTUtLjcwM2MtLjM2Ny0uMTcyLS44LS4yNTgtMS4zLS4yNTgtLjUwOCAwLS45NS4xMS0xLjMyNS4zMjlhMi42OCAyLjY4IDAgMCAwLS45MjUuOTAyYy0uMjM1LjM4My0uNDEuODI4LS41MjggMS4zMzZhNy4yMzMgNy4yMzMgMCAwIDAtLjE3NSAxLjYyOVptMTguNzAzLTEwLjYxOC02LjY0NSAxOC41MjhoLTIuMjE1bDYuNjU3LTE4LjUyOGgyLjIwM1ptNC43ODEgNi45NjFWMTE2aC0yLjgyNHYtMTIuNjhoMi42NmwuMTY0IDIuNTc4Wm0tLjQ1NyAzLjI5My0uOTYxLS4wMTFjMC0uODc1LjExLTEuNjg0LjMyOC0yLjQyNi4yMTktLjc0Mi41NC0xLjM4Ny45NjEtMS45MzRhNC4yMyA0LjIzIDAgMCAxIDEuNTctMS4yNzdjLjYzMy0uMzA1IDEuMzY0LS40NTcgMi4xOTItLjQ1Ny41NzggMCAxLjEwNS4wODYgMS41ODIuMjU4LjQ4NC4xNjQuOTAyLjQyNiAxLjI1NC43ODUuMzYuMzU5LjYzMi44Mi44MiAxLjM4My4xOTUuNTYyLjI5MyAxLjI0Mi4yOTMgMi4wMzlWMTE2aC0yLjgyNHYtOC4yMDNjMC0uNjE3LS4wOTQtMS4xMDItLjI4Mi0xLjQ1M2ExLjU1NSAxLjU1NSAwIDAgMC0uNzg1LS43NWMtLjMzNi0uMTU2LS43MzgtLjIzNS0xLjIwNy0uMjM1LS41MyAwLS45ODQuMTAyLTEuMzU5LjMwNWEyLjU2MyAyLjU2MyAwIDAgMC0uOTAyLjgzMiAzLjczMiAzLjczMiAwIDAgMC0uNTE2IDEuMjE5Yy0uMTEuNDYxLS4xNjQuOTUzLS4xNjQgMS40NzZabTcuODYzLS43NS0xLjMyNC4yOTNjMC0uNzY1LjEwNi0xLjQ4OC4zMTYtMi4xNjhhNS41MjMgNS41MjMgMCAwIDEgLjk1LTEuODA0IDQuNDUgNC40NSAwIDAgMSAxLjU1OC0xLjIzMWMuNjE3LS4yOTcgMS4zMjQtLjQ0NSAyLjEyMS0uNDQ1LjY0OSAwIDEuMjI3LjA5IDEuNzM1LjI2OWEzLjE4IDMuMTggMCAwIDEgMS4zMTIuODIxYy4zNi4zNzUuNjMzLjg2My44MjEgMS40NjUuMTg3LjU5My4yODEgMS4zMTIuMjgxIDIuMTU2VjExNmgtMi44MzZ2LTguMjE1YzAtLjY0LS4wOTQtMS4xMzctLjI4MS0xLjQ4OGExLjQ2IDEuNDYgMCAwIDAtLjc3NC0uNzI3Yy0uMzM2LS4xNC0uNzM4LS4yMTEtMS4yMDctLjIxMS0uNDM3IDAtLjgyNC4wODItMS4xNi4yNDZhMi4zMSAyLjMxIDAgMCAwLS44MzIuNjY4IDIuOTg3IDIuOTg3IDAgMCAwLS41MTYuOTczIDQuMTcgNC4xNyAwIDAgMC0uMTY0IDEuMTk1Wm0xMi4zODctNS43MDdoMS4wMTljLjMzNiAwIC42MTQtLjA0Ni44MzMtLjE0YTEuMTQgMS4xNCAwIDAgMCAuNDkyLS40MWMuMTE3LS4xNzIuMTc1LS4zNzUuMTc1LS42MWEuOTk0Ljk5NCAwIDAgMC0uMzI4LS43MzhjLS4yMTEtLjIxOS0uNTY2LS4zMjgtMS4wNjYtLjMyOC0uMzgzIDAtLjcwNy4wODItLjk3My4yNDZhLjczOS43MzkgMCAwIDAtLjM4Ni42NjhoLTIuMTMzYzAtLjU1NS4xNTYtMS4wMjcuNDY5LTEuNDE4LjMxMi0uMzkuNzMtLjY4OCAxLjI1My0uODlhNC42OTMgNC42OTMgMCAwIDEgMS43MTEtLjMwNWMuNzExIDAgMS4zMzYuMTAxIDEuODc1LjMwNC41MzkuMTk2Ljk1Ny40ODkgMS4yNTQuODguMzA1LjM5LjQ1Ny44NzQuNDU3IDEuNDUyIDAgLjQ0Ni0uMTQuODQtLjQyMiAxLjE4NC0uMjczLjM0NC0uNjU2LjYxMy0xLjE0OC44MDktLjQ4NC4xODctMS4wNTUuMjgxLTEuNzExLjI4MWgtMS4zNzF2LS45ODVabTAgMS40NjV2LS45NjFoMS4zNzFjLjcyNyAwIDEuMzQ4LjA4NiAxLjg2My4yNTguNTE2LjE2NC45MDcuNDE4IDEuMTcyLjc2Mi4yNzQuMzQ0LjQxLjc4OS40MSAxLjMzNiAwIC41NzgtLjE2NCAxLjA3LS40OTIgMS40NzYtLjMyOC40MDctLjc3Ny43MTUtMS4zNDguOTI2LS41NjIuMjExLTEuMTk1LjMxNi0xLjg5OC4zMTZhNS4yOTUgNS4yOTUgMCAwIDEtMS43MTEtLjI4MWMtLjU0Ny0uMTk1LTEtLjUtMS4zNTktLjkxNC0uMzUyLS40MTQtLjUyOC0uOTU3LS41MjgtMS42MjloMi4xNDVjMCAuMzA1LjE0MS41NjcuNDIyLjc4NS4yODEuMjE5LjY2LjMyOSAxLjEzNy4zMjkuNTA3IDAgLjg4Mi0uMTEgMS4xMjUtLjMyOWExLjA5IDEuMDkgMCAwIDAgLjM3NS0uODQzYzAtLjI5Ny0uMDcxLS41MzUtLjIxMS0uNzE1YTEuMDk0IDEuMDk0IDAgMCAwLS41NzUtLjM4NyAyLjcwNSAyLjcwNSAwIDAgMC0uODc5LS4xMjloLTEuMDE5WiIvPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii44NyIgc3Ryb2tlLXdpZHRoPSIxLjUiIGQ9Ik0xODcgNTRjLTguOTI3IDAtNy44NjkgNTYuMTQ5LTE3LjE1MyA1Ni4xNDktNy44NTUgMC05LjExNS0yMy44OC0xNS4xODUtMjMuODhTMTQ1LjkyNCAxMTkgMTM4LjA2OCAxMTljLTYuNDI3IDAtOS45OTgtNi42MzgtMTYuMDY4LTYuNjM4Ii8+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHBhdHRlcm5Db250ZW50VW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48dXNlIHhsaW5rOmhyZWY9IiNjIiB0cmFuc2Zvcm09Im1hdHJpeCguMDA1MDMgMCAwIC4wMDg5NyAwIC0uMjEzKSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMTk3LjAyMyIgaGVpZ2h0PSIxMDkuMDIzIiB4PSIxLjQ4OSIgeT0iMjUuNDg5IiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIyLjI1NiIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMTA3XzM2ODYiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzExMDdfMzY4NiIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFJQkFRRUJBUUlCQVFFQ0FnSUNBZ1FEQWdJQ0FnVUVCQU1FQmdVR0JnWUZCZ1lHQndrSUJnY0pCd1lHQ0FzSUNRb0tDZ29LQmdnTERBc0tEQWtLQ2dyLzJ3QkRBUUlDQWdJQ0FnVURBd1VLQndZSENnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ3Ivd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhRQUFBZ0lEQVFFQkFBQUFBQUFBQUFBQUJBVURCZ0lIQ0FFSkFQL0VBQm9CQUFJREFRRUFBQUFBQUFBQUFBQUFBQUVDQUFNRUJRYi8yZ0FNQXdFQUFoQURFQUFBQVBwVjNPUkVZcFlLU3VaazRKc0pRalpHWlEvcEpTU1FHRmNrSi9HQU9vcnJBeTRGV0tPenJjcUVvTWNrMWhZdjRBT0N1dUZWZzlrYXFXNkZtR1pRNFNTU0dMSjFraG42QmJhQm5rWldTSzJyZGd0ak5aTUI2RHEyeEM0MkFBRVN2dkFwR1JoeWxnSjdKSkpQR01CbE1HS3JiUXNzcURaSnBIOWQ5am9leVUyTjBjaEd4WWM4NnNtSWh3TWhBd0k3UUlEMkdPQ0tBOGt1U2N6RjRNNnJuUUY1TEJuRmRLOWlxc3RORmxocXNQcmFlUHlWMU9VZUdqQlByZk1RSjBrRElyNjBjUXBYWkFtMFBPcHowQ0t3Qk1yQkRrQVVETVpZbGV4VTIybWwyQ3ZKQm92Um0vQ2VrQ09vVHFKWlhXYkVSQVRxK0JrUkJLczBwdFptWTJBZGxCYUhWeGhDUVpZRk54ejZMRFU1QmhpclRrYVVnU0FTQ0ZnTTRwMm1tcVcwM0hIc1FXNTlTN00wVUxkTExJbGg0T0ppd1NXNUNsamVTd285a1NPVVp1bGhhbjNOb2tBOUl3Qlhzc0JGTzFVZWlPc21uUjNSNTJuOW1RRmJTQTFrZ3M2MkhWMllxWW1YMkZrNHNFUjRyTVZMSlhZckxkenRtVWJPVE9ESXowRlF5Rm1LNnJlUSsveE5iTUl3cFN1NnRGbnJleDFXNG93c09aUnFHZFdLUXI0Z0ZSWHdmWm5NMmVtWlNTQ0ZpZUNSbVJHS0FlWmV4eXRQdWdqUmdDOXZSaWpuQmNJY0ZacFRZL1N3Mkg4eTRGREdqcEczWHpkNExoUmJWR1VLRGVLZWRxZEhSRitYaXZQdnZIWjROTVVWeWpiV1ZmZGV6bmt4azdLR3lOMHRFV1B5enFwamtzSk1Dc3JPRTZCNWZTOGFBT2tSUWdNSEY1T3g5SHRLL0Z4Rmk2ZTArOTUxTmJVbjUvUTA2VzNmMGVmYXFXY0FneUYxVzZyMTVzM2x0b1ovVGRrVnhKaEkyTG12ZlZYUkVlUlNDOElQS3VQZDI5Yms0T3hkSGIzb1BOK3lVN0owZFpnZEU3TUNld1BWWjZqSFZXVSs2bGE2dXEyc1ZWOE1BejF3RVlSMmRkaldwcnhUZEd3VG1jeTR0L1psK1BnSEwwTkpkUGp5bWR6NDl1c2I2ZDViTU1zaTlnMlFzYTNXTUE3S3lRME1uNFQ4WklDWlJmOEFKVG43Nkd0bjBIMVl1c0w2aHFuMEhoNlhYMW1maUhOdCtXZXJEY0Z0KzAxWitLMXlLdEdUNzkyVmNFdy9MdXE3NnphYy9VRitiNUw0OTl5ZXZ0UHRjVFVlWFZ3MXkrcjFuc3hkeWNycS9GRFBvb1ZsWkMyYlZ1cTFiVGJkaUVLVzdBS3dOWFhnZTd0bUg1dFpOc2RsZmJuby9OcHNXL2g3bDlUcW5zOFZodjUzT2ZsdldXVjYzZnF2SkRjL2VaNXYxRHJyY2o2VStUOURybGJOYm8vaFd5S0dSYW1JdzFpVmdsKzQwcjJ1WHBIUm5xT1BlUTArdW5VNC9DOWR2RjJmVnQvVGl5dXkxRGtlZ25zcVpkZmdXTEIxTFZ5ZXdoMjRlaDZwcUIxdU5SVkUzVlcxb3lXaUZHVTZaejI2eHJmU0hUelhXcTJvdzgxNk0rNjB0Z0kwRFluWVZhNzN3WGN2N0s2bGRLTFpqc09QcFd1blJaYlU1MW1MOEN6RFZRcWVCWHl1TFNuc2x4cmZ3eWFHMjU3bkYxZWFyamZWc2hXVEtMWGkwYVgzMFJRREVXSzFRSzJXR1Y5by93RE1kYkdDSWwxVkpGa1prWkdjRUpKYUdHeVJoZnpHWU5sSU82cG1hbTJwc2FtWXNUNjN3ZFp5S1plb1ZzLy94QUFpRUFBQ0FRUUNBd0VCQVFBQUFBQUFBQUFCQWdNQUJBVVNCaEVRRXhVVUlCYi8yZ0FJQVFFQUFRSURycGw4RHlQUFFBOGFsZXZBb2VCWGRkRU1uZ1VGVmZJODdGOXV4UW9BTHIxc0RSb3g2Q2cyMi9ZUGZleGN5YmgxS0ZmUFcrNGIrT3krNGZmMmV3dnQ0Rkt5c2pnOTlxeG9QN0RKNzFsZVgyQ1FNSDIyTGRoOWhYYWxhWHgzNjlQVVl0UFhLbTFGZllKRmZ2dXRnd2NNakt5dnNHMTBLNjlhemoxcERkVVdFaXlCOWkxQWhsZEhWbGJiYnowVkNUb2xxWTcyVXRzSFNSVy9oYVVxMjRrMzcvbGdLYXJvRTBvV2tZR3V4UzEyRzNEcXdydnZieWZGNnJDb3lLRmIremRTcDc3RkZvMkZOVzRrOW51d0dUOW1Edm1MSmR3WVdKVlpTbW9UZEhyb0xMVVpWeld1aGpNUERvL3o4WlM5aVFYME9KU0pkRmowa2pOb0lrclVLNklxdHQzc0RJZUlUQThUa3VHU0xMeDRHQklaRmlIcUFkdFZRTDA0U2hNSlVkQVZscmlLQk9IQ1RsY2ZMc3JiNEFnUEhHZ0lacmRVOWRkOXRReGwvZldkNXhYSVNTTy9EbkY1eFc0bXU3TnVSRzh2b3NsRU9mS0g0Y1RrWDVOaitTZlF6bVUrM3h1K2c1YzhrRTdTdGVsOGZTQ0VSUnpES2NsdVpMZDRjOXlsUldPdi9yZHdGYng1bGhhVUxQSWFJOWppcEthNWp1cGNiR1dXT0U1emxGNzNiU0duTVVra3NMVmNPYy9QbmpmZmJ1cjJYa2x6bEllUks4ZHhZM3N1U3VzTWIyd3RjaEIzeGw4aG5ja1JqNzdINHlYOSt5NW42MzJIdjVNdittZTRDd1hWdlA4QXRXOGx5dVR5K0l5LytyYlAyK2V1c3ZhWldYTC9BSC90L3Jubk5uK0w1bnh4aVRpZmpqRC9BQnhoemgvaS9GK09NTjhhYkVXZU1HTitkSGl6YUJmekMzLy94QUJFRUFBQkF3SURCQVVJQndNTkFBQUFBQUFCQUFJREJCRVNJVEVGRXlKQkVCUXlVWkVnSTBKVFlYR0JvVEF6UUZKaWNwSVZRK0VXSlRRMVJFVlFZNE9Ub3JIUi85b0FDQUVCQUFNL0EvdGgxL3hlMzBCNkQ1WnVyS3lzc3VnYy9zWTZCNUJZYnEvUVcvWXowazVJSmdDQUYvczNGYXk1OUFKTFFkUHBiZlFCQWxBSzRXQ1F0OGkzMlV1a2M0aFpyQ0xrb09GMm0vdTZiZlFXNkwrUlpXRjF0Q3MzM1htT0ZpTUdKZ0hSWFRiUXFXVFYrOWEwY0l2cG1pNitKT0IwV09qZUNIYWVnTTBHVVdqdTJlMkxIb3NuNnAvY2lCbUZieXdyb2RBS0FhYytTZ2V5b01lNjdiZnFua3B0MXZkcTFtTWsySE9MRHpWcWgyRlh5S3RSU0REZkxUSGgrYWNLRVhCN1IxZGZvQ1paTWFlRlkySEMzTlNBWTNJdEtzcjlGMitTVVVkMjc4cUVrYzlwbk80MjlxSEJ5NkE3YUZZY0VneUhiZmZtaTZURWk3TXFNN09rRXJtQVpaeWRuNG9HZ0dEQll1UFkwV0UyVDR6bXNXcXZvcmNOa01kaUZjM3NnVmtzMGVnZEYwd2hXUjNUcmZkT2lJaW40YmVjSDlvM25MdlJUVFcxdURkOHV4N3lxaGp5T29OMTliL0JWRGpZN1BIKzZueDdOZlB2WE5OaDJJOFZ2aHpRL1pyTHZ2bWN6SGgrU0JOMXZNZ2hiaVFITkQwazU3c1hKRG1nckt5QlRRTG9xdGVURzZza0l1Y3NTclMwanIwOW1uaHRLY2xXVlcvRlRVdWVHT2FHNHZjc0kxVEJTdk1yMmp6WjdTaTZyVVBZWTdid1p4dzRCb3FYbE96OVN3elZrc3NqaU1zNUdCcWl4bDFqYS9jbzN1YTRCUDhBMlBKdUMrK1gxWnNkVnRHQnpXTnJaaHczc1hyYXBlR2phYytmK1lWSEUwVzF0cWgxS0thTjFqdnM3SDJLVDFwL1VtczJSamxmY3VsT3BWTmZEanpXMlJNOW9xOUhIOTJGdGVhc1pGTFVqQ1hjWG13b2o2YWRGczB1cFo4TDhRemF0cW4rOEp2MXFhcmZMMTJzZTRBQzJJcllkUUMyQ3RBZmJoRHhhNkV0Vy8zbFlNV2ZOTzNwenk1cW9pTUliVXZZMFd2WXFSMkxIZlAwa0k4VzhxSGNiU0FBZlluT0pOdWZjbW1tbVk5NXU1bGdQaWk2TENRNFo4bDFleEY4enpXejYvWi9WR1F2Y1MwWHZrRkhMSUxOd2hyTFdRYkxjSFJiYnJYNEdUdGIzY0NyMjBVUFdxckdIUDBJME5sbmE0VWxKVDRNV1dlU2RuWjNKY1dMdlJhZDR6VUoxdUtYd1JtRzd4M1F0b2pUTXVDTTFEZ3lnNWVxQ2daQTh1YitHMkFDeXBHdDA4UTFRYnZlaG5EM2dOVWNadC8xWkZ4dUdrK0NMM1dpWVQvcU5USW5ZSGl4dDZ3Sm9iWnpzL1pML0JiTnFZWDdQbnhDUTNJS3FXUU5xV2plUjNHSjhUc1FIdjdrYjhZR25lbnZkWnJSOEZLWFhNYW91cFJUQnJMbVBDVDl3cDlTNkZqem9DVnhkcFhoc0p1ZmN1NlplMUVFRU9WTUg0aTdFbXk2UmtMOEtNZFB2ZDNlenVhbHFLUjgwUmRacEY3dTcwMlVEaWZlNHVvdXJ0cTMwOTJrODFSVy9vNmlwTUJrZ0xjWXUyeWFBVFoyV3FwS1dYREpCWjVGMXM5ODRiYnRHMkloYklxR1M5WHFZWHZpanh5VzludFZMVXhzbWlrRjN4bksrWXkwVlRSK2RwNW5NSTdsc21yZC9PVWZWMzhwNmNhL21hcWdOZE5ETHZvZldVdVp0N1JxbXNHQ2xqYXdYOUlhcmFsUkdhanJMZDJPNEtSa1crbW5Mclpab0xDMlhpQTAxWmRPaDgxSnN0cm9jclROWllxaHI2dDlUSE51ZzUyVGNIQ1BpRlVENnB6SC9sZi9BT3JhMnoyNzZvbzNzamNiTmRiVk8zVnllYWlNbTdhYmxNbWlkQ1c1RlZUSTkxdzJQS3lsZGR6ekdNdE1DbGtoM2IzZ2QzQ3BjVjQ1eDMvVjJWYk1idnFTUjNZVUhnaDcrMTJqWmRaY1pIeU9KUEx1VnVhTkxpM2J5TVRiSDNJVTh1OWprY0xhRlNPbmRQSk1idjF0a29YUlBFekw0cllTcWVDUVRiTTNyRFlmaHorQ1p0RnJKSFJYZUcrY2U3dlZQc3lHUnNsTHZITzUzeVVXSCtxSUwrMExaaGVYL3dBbmFmMlpxZ2J4blp6R3RPcllsVTFUTjBYV1ozWVUrblpoYkd3MnRrNXQ3cVorR01jREdtOWhycWpqa2twNGNKazczSEpWTGhobmhpbEg0bUxaZ3pPeWpjajBaeUFGVDc4dm8zU1JzT2pUNktwais2K2FwajZEdkZRdU9IajhVQm5ZK0tEZS93QVV6bmZ4VWV0MytLai9BQitJVFQ2VHZrbStrNTU4RXpsajhRdnZTUDhBa28vV1BUUm85L3lUUHZ2K1NpdmJHOURkbmR5T3Z5QkNta09LWVdiYmtWRG9NWGlvTkx2djcxQ3pNbC95VkV3WGM5d0YrNWJONU8vNEtuRW1KazUrSVRlVlQ4bC8vOFFBSmhBQUF3QUNBZ0lDQWdJREFRQUFBQUFBQUFFUklURkJVUkJoY1lHUm9iSFJJTUhoOFAvYUFBZ0JBUUFCUHpKSWZpNmNFNE1KaThaZ2d2SVJEODIwSW9ZUXdoUDhWQ1piS2tPVEJSTVlvb1B6TDkvQmNEbi9BSUJvSmcyaDA4R2FvcTdGZzFDTUM4UmVOejVUMExQeUxvVUl2R0dRdWZCYU9qcFdoTnQvNEpyek1JNk14Vkx3Y1pIeWFLTDBRam1OQ1VQVklyVUdSc1NGbmxsQnA3UEZlQjh4RUVXb3NoQjZqdUZFSVBDSlkrZzUwWG5BdXhtdzVCSzZSdVJUejRhY25xTkRneDVMK0JsSGVjek1IaEEzZ2FQRm9PSzBaSW1JRlI4MEplRE5zNDZJWUpSc2F2Q2tpVks4aVFnZ2lVYVR3NVJXSFk3UnhsS1M4aFB3VkU3NG9tTjVQMkh2RlF2OGpMMFF4R1lrelBYUGdwVFBrRlFiOEdHRmg0SDcrTEg1RVVoVVVveUcxbUVVcnVodUE0eEpKYmJJd3Ayd2xSaWV3WlZQd3FIeE9sTTlzaGhkRW9ZcWxHU01yWWRDWnhvdXFTMEYzQ20vc3p3V0hGdmYwamxHejRndnp1dE51RGtLN2lYUkZoQ3l3d0pqZ2ZnV2dzYjBKMHNuTVl4amtzRWtJMkdhNXp4clkvWkR2Y25XdmQ1T1lhQVJQYy9rVDFyS2ltdDJuMlB3SlNyOTc3R25jSjVzelBncTQrcXZSS0ppOUJtMVlPQUhwYUp2SWthR1Y0QWd6MjR2QTRYSzBFZkR3Ym5hK2EvQTd6dGlpY0pMWFZZeXVHWU1kbVo1dmx4UmRCemgwMExXaEpnZDE5aEp0SGhqSGI1Q0ZnY01Jb2hKanpGTDVYaGZaT1JleG5RbGlVZlpsZXh6L3NFb3hzc3NGdlk4ZjhFVUx5M0pCbEEvM2lNZ0VGODlGalJta0R1bUNpcURRRGdZV0ppM1FzTmtGbWxlU2U4anhMZm9qV2dSU0RSVnFzRFNVK2lnYVkwM3A3UmJIbk5mVlk0dHRjU1Y5bXlOdm96MTEwZDVSb2Y2WDlhMHhJTGVSMVpmOUN1eW9TMlRSVzJ4Wit4MmhWWW5yNTZHNjZlNllTTVpWOUVhbWlYeWo1NVRMVVM1U3YyUHhvbXpNb3NvbUdFYnppQlV5ZTMyU0JTc1MrenBnUXlLV1NaYjhTclY3R3JVcUpzdW1UdWpwbXByMmU5ZjdrZlJOcnBYL28yT0IzQWJYYnRETHg5Y2F5T21jV2Z0aUptc21pbDQvc05wcVk3b00rNEVabXA0RG03N1BFR2xQdHFVVGd4UjFwWDJFaG9QSXZvMklDeW94RzFwODBnNi93QXpOVnoraGxGT2QrMElMdzFJVWNwa1pWbk5lT2pZdE5mOEI2MXhHaDg1MFdpNlE5aVYyMUc4Y1pRcWovMGtEN0RIeFJzVnBYUTBRek5laTU2SUpQSHIxVEVSUXRMdkJLcGFEWE9LSzQ2RlRiYTU3YTBKV1Z3N0dsRTB4b1plNW1uK0IxbEZ1S1UwNG0reDRYbS9rVE95c1g4amw3R2RxWDJjZEt2dlovSm5wMm50VC9vbTB6QzhKOTlNWDkzN1Yxc1NsbGVHNXdKM0RWZ1o4c1l1T1BlbW5ETEw1ZTF5R1EwcGZiNzlHYlRab0tXOVA0Z2h4VzIrZ3FIK2hoWUdTdTI3YmZKSWx6cS9vVjJGb2FjZUJlNkE4QnRpdHVWbWlKV1NxK0Qra2RWSHhOTi9VTXF4TmpDT0NqdkhEdEhZbDZOdC9aS0xZbS9Ub3BXT0NWWGNqNlVHM244QzZ0S3RFaDlIeEtnaStQd1BmL3hibU1BOGtwR0VLZTVOZFBRdmFqVDIyRVZyVEZLdnlWYzBlSExqMGhpTjRPajB1ZjhBM0p0TXczSSt3d1dVYlRiL0FBL2s0Z3RmTjZhUGJPOGkvQXJkZXpmd2l5eDliOVRrZTBhNGhVcy82RjVJYXRNNCt6VDJlUzJsSjlEalgxaldma2ViOTRiK1ZrVitnRGRIeGtxR21mZmpVOGlLVXY4QXoyWmlMSVhMakU2VFM1VWQvcmlSVk4rRkdyL1FET25xRmZXRk0vYVlEdGJxR2swZ21ZL2dFMWpPQ0RzWXp2NEg5bGs1d2pML0FDWDhmc3FzYVZQbkluY2xweGd5RkxwaHA5T0oya04wOHR4aC9nUTdlYnQrai8vRUFDWVFBUUFDQWdJQkF3UURBUUFBQUFBQUFBRUFFU0V4UVZGaEVIR0JrYUd4d2RIaDhQSC8yZ0FJQVFFQUFUOGhqY1JNcDZnV3dvNmhzaDFRcng2QndHcGMxVUNzRTVMbCs1VHYwMWN4cW5oSE1lbUhmb3ZNRUJZSnFab2NKT2N6bXhmYjE0TUlZemNNSVhjcjNCNGhpeDQ0Q0tFS2tLU2hTTDJuRGdMRmJiRTJqQ1VkeENtWUo1SmJ6NklFZk9WZHp6d081ZGNvMlJubGdSR2FqVUtKU29CbHlwdUx1NG1VSUNSdHphSVhLRlhjOGtxNW1NYmN4M2VhMXhNdkV4SVpqRGJISVhhV1ZtWGN3dlF6Q21kZHVxbU8rMDJub0lhSkNKdkwxVVlVT0d6anNlb0M4emlXR1lnTHVNREJ6aVVMdUh5bU5tTHROZ3dwelBMQ0czdGVnSytHSWxvWndKVWx4ekxKVTE3UE1wRU0xWURsaE8yS09VUkhNY0psM1JIY2xXTGlJdVZDRE1BSmRiOUFjS2hYcVowQmFtUmtobkVRMVl4a3lKYzF6RFRQU1hHbVVjK2s0NUc1dUlvZWpBM0tlZlFjSW5lbDZvTm00ZDBJcEsraExlSlpOUWpMZFJiQmM4d0NOTmMvTXBzdVg0aUxjYnVLTXhZWXpkejZER1o1WUZZZzNESGFnTXNsa3YybGppb0VHUzJ2SHBSU3ZGTU1KZTdlNW1iWWVrTFJjUjhWOHg4NFBmb1ZTSkF1b2J1STJ1THpNV2xnY001a0s3dWUrVXUzMFdZeXJpQUFia2xnQ2NneUtnbmhmb1Q3VE5QRXZxNmd4Rm5Qb3hPUm5abmFnYXBBWGNDS05ReHNTWlFaand4WUhZVkdFMkxIYnhPTHB0WEt1QlJuS04yWE12UHQyeXdZb3dNUjJtVVJaZTh4ZU5oYVdydG10YW5obkV3MHJCWlJUWW9WVzhTNkN5Ym5aRTVRdnNsaDV5NHpiaFJ6WDZZWXBIZVJTeER1aFJoNFhVWTJxK2gzeDd6RUhBcE44LzhBRkNsc1hLUWRRV1QraCs1VGlqUkRVT0lRb0VObkhIN1l4YVZXb01xZGZjcjhHcExiNmc0eFdIcVNvdG1NTUdYN1lYaFUzU09veitmWnNQbzRPSnBFVkc3bzIxYmNjKzAzbUFGRnVPenpHeGN2THl0L3R1dW9zMW5aeUZ0c0NwRWZKSm5YajhXUU9yRUpjT1lQekZacnErMlUrYzVoRlFyQVJldzZoZk41bE5Pb1gxWXlyaUprSTBuVjZNc2QzQnlwQW1GOFdDMXRQRFBDclA4QWxQaUc2N0l2c0M1eGZnZnpOb3ZIQ2FpcXFRVThtWlVZM04xTC9OeGI4dE1jL2hHNk00R3NoTHVFUlZlSmhnZ202bUlINlU4c3J0VmpEYlZLK3NrN0ROc01HREV0NUxQbVZmS1d3REtRRm1JZ2lvNjVOQStKeWxJY1Zya0MvbUhubExLU2xFR2RCM0YxS0p6WHR3MythTUNrVzVmMHVjYkxqZzlteTk4UVoxeXNFbFNLU2t6QzhuR2tNbG1zUVNXOTJnckVNOEF3NUd1NFZHaE10S3kzTWwxUDJPbnlRNzZGL1BLaE1pdFJTTDlvdWVMVjJVWUFaMUF4dERKdm5OWW10c0hKZjZsb24zbjdDWnZxbjhzdHA5STcwemovQUdnUlc0Tjg5VFZyMzVnVm9ITmZlVXRsVkxXTWE1bE9acmNWa2d6TXYwWmN0bXJsdGVscXF1UHdFd0cxbXpmcU15OHhIeFRmNDRxRUN3b2JYUGRUUEtJNWRIdE9nYk5nMkc5azNlTHUzWmYzaFdja1gzREpadWdLYzFRemdhdFNiSDJxT3NvT3NEZ2NFZWVZQzI2Mzh3em1xMis3Ris3TDZMakFZYUViK3NXUHF2YWZjVW03MHlwZGZFVmpHU2ZTOEhqVU8ydVNwdldjVmtLRnNqRDQ1bDl3Q2RCNzFBS24zYWZQeERZNVpWNzhFOS85VTNsYjRHSytpQ0dOclN1dXo2L3hFYXIxMjcyckRYUDZpNDF4TWhWMEdCTWxmV1ZuQjBJZDRmWkVyK0M3QzhuWFV0K0tBZlhqY1JFTUNITDRsa0xDSVo0WmhySWxwY1AwbEt6eWl6WGNJVnk3cnhNTHFXc0lacVpCcHdGQkw3UnkxNXpFQmJpMnhXeTNkSDFjKzNjM0NWanRCNFloeUF0bkhtcGVVc3BqWk5McU1QZXhaclRlVWJzRGExRHRTOUkzZitGS1g4SWZlUjdBRE5Db2JZdkN2eUl0bFN4dnRTNC8xelRBbFFMM09EbjVtdkFsR2J3VkZZS1hVdm4vQU44T1E1STJFbk4xS1g0WVgxM1V6UWpIbkwrY0ROY2lDcmZRYlo3cGh3TTB6a0g3TVNwNExjMDZBTm4zZ1Jra1J0ZU1mTTVMYWxyYlJ1VVBrcUQxTHY4QURzcGhqclhNNjRlMUJqWVAzZ3BuYkhzQmZld01wTEg5ekYwdEowMUdNcmJXYU5TNVNsNFFjZXNQRnYxRUt0cVp1bzlyYm5pOE40YXdWcmZVdmtTaHJpcFd2NlNzTGxLTHhZUnZIeG1GVWpOdGFZSEJoam1wWUZ6Z0Q4ZGI1KzB4THdjRktCNkR4S29HUmY0a2JtV0dHQVRJOGRSY1BLclRQU3ErSXE1UkRvY0tvdXRHb2tlYkREV1crTjF5bUhweXFpN2MxeHdONlJONm9OVld5M0tOdnlZT090UWwzdVhVdHdJZG5oMUFDRHhwbmxvSDdTeUJUWGVEZTY1Y3h6b1hQQ21mY0h4Y2ZVc0ZWdG54eWh1bWJ1SDJFckViUGhXdWlyM0Ria1FGbEJpbmZSOTBiZU1Md083NUhpeVhGdFdXRytzbXJxMkRpcmlsQWlrYXJIOTVoSFI1VTN6Ykg2VU1pV3lwYjlSSmJITEkvRTR2MFVIdHkxOHN5ZDg5b0lvZlZFWGpaRE0xdVFDS2hDMnppOTRrTEpiL0FPb1U5RG5aK3NTcVBMUXlFMFgyQk9pNHVPdFRTWDFjUlVKbTJUN1M4LzNGWWxsUk8xRVlhanovQUJndUJBdEc3Mjk4VHdYMFQ0NlRMTnZkV0gvL3hBQWpFQUVCQVFFQkFRQUNBd0VBQXdFQUFBQUJFUUFoTVVGUllSQ0JrWEdoc2ZEUi85b0FDQUVCQUFFL0VCbXJtaCtETlhRZkc2dE9HUVlGQngzOTBrT3Q0eTUzM2VqelExbS94Q1phS3J2UFlPdnVTOHBmY2dHbmN6dTg3Vlc1eGZtS1FaUmVZWFJjenc4dEk2TURET1R6ZGJmdmxnRG5qT0FXYjZHbDB5dFl1TDVxRmNiTTd5LzJhQ0hQNEs2b0hmbVJqZ2JrZjQ1Vysvd3JHb013VGpnb2E5TlB6V0lVNlBYY0hPclM1OHlqRDV5dU1vN2xySTZ2WnZVditYS0RXZndoTlJLL2hwY2puOEV4QmxtNnNVMWM5TzU2RDdOMk0rL1Q4enpJTDQ1d0ZUNXFHSS9lNFVwNXZOd256RXQwajd1NloxSEc2MFNHc2orMTFCSGVoT09UQXhNeFZwWTJKenV3Z3QzVDRlTDVPNUowWHhpYWFIM0JMYmNxV2dYdTZCL3R3ZVQ0WElvaU9yeW53ZFdJejNPUFU5TU5qbmRqZEtPNHREbld3TTI0U0QxNWp4bU4rc3dpbm12Qnlmbk1IcGpQZlc0UTFYelB2Tjh6QjMvNnpnQ0Z6MnExWHIrT3NQMVAxbi80cTUvY0o4YVVKZTRBL3dETEtYWnlQWFhqODZpR3ZzZnQxVGd6anZHVlJ4cnEvY01xL3dCWU9MK004Z1dZVlJ3dFIvV0Z5TVl4WXEwUjVmdmNlMXJRS1YrNEIvQk14U0ZSWlhPaXdQRExLdzhjcWJ0NEVPRk1jYjlkeWFaS3gzTmY4T1hrZWtCUHc2aWJEdng5TU9lQWorOEk1L1Z3RzVOeUpQREtSanRJd01KblVzdWVIV1ZxWFUwWTlvclR4N3N4b2VjVnExeUs1c0cwMUpobVVIK0dwdjhBRDlGOE5CN2kxd0xRelFjRU45TEg1NWhPRG9QTUxsVzlFRENySFN2K05WTG4wekl5Wm1VNFNQelZ5bEo3ODNPWHVkL0c5bHhHUGN4S1hBbVlRb0RXWEw0dWpoWC9BSmpTd2ZXeitMSU5uZVMySFdaVlJaNmlhUlpncVM2dEZ6Vk5oNGZYdVNrZDRLSG5ZNXYrQTBaam9ZZnFyd01PVWxDMUhwVW1lWDZ5Q3BuQ1R6SUc2MkJOVmRlc0ZmRnhuL1kxWE92TWh1TDJHQ2cvc3djSy9kWG1lTEZPZVpGdmJ2ZC9xT2VRMEJhd1UvdEprejh6STFnenhXcGlsWS85NWpNaUlxVFVnNE13KzFWZnRjU01IekdoblJiZWZpcjRPaHlGdDlpS1ZBaHgrOTFENDVuVmkrNUhwQWEwUUIxekNNTzZRWHAzQWdlOCtTVTZpbzVZWFpnY3FJWDlaK3JpZVlpaGNLUGp2QzM5WUx5QnFyajgrWndxSlI3QXYvdDU3bGtvZC9UUEgvZzV3bm1QeDkxUENSaG9IUTVnajlNTkRBSDVPWUlJc1Z5MmxtUis2bis1cUo4UkZ3U2JlSG1iUytCeVo1aENlcnBXMVZ3RS9nb1hKNExaMkRnQ25iYi9BQ3pLVlEwVTkwc0RlTndBaUQ5TVpMUTE1M1NjM0ZPSEt6QzVrcDg5dUlQZCtPT1RURjlXNURTaXJRUGM5UDhBNWVhekJWWnN3SC9tdW5tY0pkRVlkdU9DaUQ4OHhoVDZmRUh6TjdKa0xTOE1CaEJRWXVMRVJFQXcxcXVJZTU3dXhUaUpSUUFPZUdGRS9Pb2QwWlNYZy9oMXVlZkpacnFMZklES2FTQ1oxTHBqNWJGdjEyRE1PSDJjR0dXdlRYTUR0TGtoQVBWWHJ1bENlczN5N2xEa0I2YUcxY2tmbnR4c05tUEJJSGs3Y3A0dkJqb24zVUNrNlVLZm0yMmtPRC9lTGY4QU45MDZkVTIvbXMyMDFMVVg0eHQwUjl4L2VsK1l5WkFlSXFlZVhMNTV4VnQ0NFAzOTNzbGRIZSs2ZVFjM25zQmovY1kxRE9oRWhISCt2N3lBUEJUL0FJdmZtN1lNSCtxWUlNUHlhajRHL3RqTEVYMzdnQU1YN3J3Q2czRzVoSDM2a3p1ekpYRndBSEg4R0pxK2VDUTVJQW1KZGVBaTNYbXNiVGxQU3hhaDNxZkRDQnhKTmF3S2ZRS2xEWFRXTkUvSVYxbmlwbVpYcXN4ajJPWHdyeDhBV0FCbkhVSG9GVUh5R1VzNVB0Q1UrT0w4MWs3eWNmc3I3TUs4OTZDNndlci9BSGZJVzgvQXpWbEUvQUVSZHE5YzZ2c3hvNGpCNDcyS3ZQQXhFS3ArRUkxRHBocHBhQjBYc3c0cE9jRlcvaUdKK0RpK1NFS2w2WVlhREtPbjJianNjVm9xN3hNZFQxZW1XZ3JBRDhjSFpqeHY1b3RlSEFlNWxvU1lIdEQ2NmY4QWM4MEFzNEhYMy91UGpDbTlkRlJ3Q0M5bWJZdzJwRUZJVmZUOVpsbjRuTmRLcldwNXZ4VXdwdTE3VW9nS1BIWDVucVB5eThEOXlPSlQxVGdncE9ORXBscXhHQUdBdGNiNWlYTkFIbFFHSlV1WStEQ2hhMDI4UWptT1VBc0w3KzlHVVV1RkE5L1ZEU0hGelRZblJNcng0emt1SGUzTDN1VkloUHVmTXNEYTBGbkFIUEIxcjZIQU44NkgzOFpUcSttNnV2ZnZYR0xIRCtCVmR5dXk3MTc0bjQrNVoxbFVmaCtMck10MytCYTExT292Vi9JSHhYN3JSMk10dTV3YlB4WVlyTlc2MStNc2dGalJUMUJ3cEFwRWIwOWNjK1BPekRPVTFLQXFDMnNRRXJ6UWgxeTVYbEQ0MWZuelc1UVZUKzRwS2Z3S1BYT1p2NkpVMFE4bjR5ZjVnc2E2QUVVS3NDdkNjNm8wQnE0SnlVbGlINmUwckpodUxCaUFwMmgvWlh6UjE0VDRqK1R2eHkrejJNaXhBc1JlejVxb29BclduVng1NjZPRWUxRDlVcDlZL2pFK1Zjb3M2cUg4WGpyOVk1WWdwNkU1ek1LcGIyMXdyZWs4ZW1nS0FqajljQnJ4T2dZQyszeitndUdRSVgwci9tQjBRL0MzaEkrZU5IU3lxU3JzNGc3K1FuekkxMEdPZUJEL0FOMkNSTjJTYWdjRS9aSi9lbXZtWFBQVGp3L3ZUeEhrZzBvVkcvT3pFczV4a3hSK1ZvUDFlNUc4YUZtaEY2SHZBdjZ4WWRrZWFSenlUeGhZbVNRRWxEL1Y1ay9abHlRRENpcUEzbnJrNkduUjBMVW5TMkZja1ZzbTdaV0lNTWl1dzhNN0Z4NXYwYWxDUVFjUTZKM25MNGtrMWdFTDNodm50TVJBRDRIVG8vUnduR1pSeldFb0ZDaTRWMFpFNWhkREhVVzJZYUlqbnNsNEhzbnBETVVOTFZYeThtL0VMaGpWbENvdFNEWG4xM0JtdnF3TW9YSVFvZUI1UzlPM0NhQ0sxR0FyRjMzV1hvWEdRQlVmeTByOE1MMlg0ZlJRSDRoK3NNZjhjNm9lU0xWVFhiNFpFYUMvdGNkVHRiQ2VvTWpOQ0ZMdXA1RS9EZ2Y3YUZsSDRQMWZVNllaTWkxZFpGbDZTUUlENHNncnprQ21nZy9oWG8ydVBFY1AzUk5QMGxYRWRKN3BXQUdvVzV3a2RFNXV1bWQyTmtJR0RrQStySmRSaGc1ZHZ3Z0F2MzF0Slo2eXVRSVV6QzFPS2txRXZ1VzBOTE9LV2FFRXluQk5ZMUJ3SnJvQm5YR1E4THNBSkVRZlErbnBBSFhqeUtZdCtoWjNXLzhBNjlEbXBDSHJQa3lselc0bERha0FTVm5tTCt0Szc1aS9aYSs2K2V4RWlyK0h5QTVGTllpbWVLdlNMajVkNWR6QkVDU3Ixei9aZGJQbnFLTjhnd0hmTzRieCtFSHZEdzRBY2c1bnhjdUNEOGNGVEo1Z3VvcE5QUnpmNmx6YzdJVHI4VEVrKy9reTdMalZSTlNHR0FhbUFFZ1QyVjliZ3ZWNkpXY2g1TW5zVEpLcTEvb2QzZmo1YjN2MDR3dDBZSG94L2I4NnUxQ0YwdGJsUUcyOGVzYy83amZFb3Y4QXlDNTJQVmxLSDNpNW42OTY3OGZzelhCWkxuMzM4ODdnczVMeVFWOFc0bngwamNDeS9QSERsQkdKaDk1NkhOTDZuZ0JSSWZUaXgxeUNCLzBPbkhINFI2RGxNY2dZeWtnZUhSOXlSdE8vUDdGU1hkdUlFZjAwNTFkU3lkdk44K1crWmMvREVGWGxrTnpYK25MQ0ZhOHBqS09pY1BZRVQ5aHBVZ2dpcEp4bmdjMy94QUF4RVFBQ0FnRUNCQVFGQXdRREFBQUFBQUFBQVFJUkF4SWhCQkF4UVFVVFVhRVVGU0p4Z1NBa3NXR1I4UEVqTXNILzJnQUlBUUlCQVQ4QUxRdVN0OURwejZjN0hMbGJRcE5FWmwySm0xQ3J2emEvUjBGTHNSWkpuUXY5Rk9pdWFUSXdkQ2pzSmJiakw1WCtxT3hxTDVWeVFuWE9xSXBOMExHdXdvbEZJb2t1dy9RcGlZcGxNVVc5Ni9TdHlyRkdiRXBjdElvV0xFUnhwZFJSWFlUU0xOaHgySEVjUnhOS29VU3JSRzVIbHhaNUpIRHU3RmdYcVN3dnNSeDBpbUtESGpWV1RSQmlFbnljVFN1VVUyVWZjWld3bFI1amFGTm9XV1FzeTdpbWpYUzJJUFVoS2k2SXZVVU5XalE0c1d3bTZMUjE1VUpEN2pnMjdOSlJwTExkOGswSVVwVVlhVUxIYko1SGJPSGJhYU1kdGtsUWttVXhPaTl1V3ppYUVhUFFyc09JNG1rMGlkOGsrVVowaldZVzlOanpxbVg5UmdnOUlocDBRU2JHclE4YU9uSk9pMlJZMHVWRGlVLzBxdVVXNDl5bXlDcWJaaGQ0MDBiRC93Q3JNU3RqUzZJcWlTZGxDTnFJMmh2WVRLWlRyazB4UU5ETkxLZk5XUWpUWmhjWXdvMmZRZTBYWmlLWWszZGlnNjNORzFvOHV4d3BpWlpGV1cwS3pwMUhaQmFtYUVLTVRSRTh0SEQvQVBKZDlqeTRuRFM4N0k0c1NpaWxaeHNxNGFYMi9KNGMxNUZtKzRuMTNMc1Zqa3E2aVdwa3NXMW1sVUl2c1FUWTBocXhiR3Rtc1UyaDVHY1BrbEZTSG5ad21TVVp5YWYrV1lKM2pUWTJqajVmdEp0SGhUdmh2eXlWYVJOa3BUYkUzVk1oS1VKN3ZZamxnNU5EbkhUUTNhRkl0RVdySlNRNDJ1U1ZpaVZRNm80VmZUSy9VYU9GVDF5SVJVWTBYVE9QZGNKTm5oc3YydjVacXNnNE5XTlV4eVNWc2R5V3BNaEc0N0d5UktUWFF2Y3RVUmFKSTBubEljSEViYVpHV29mUTRSclJMN2p5UlJ3MDByUGkwbFZDNHBQc2NaeEtudzBvbzhPVCtHL0xOMXNSYWdYZlEwdGpnK3hISkNLb3YwTld3b0tUc2VQME5GTFl4cWJsUXVOajFNMmZLNHUyTEpQUWttWVc1dzNJeHNtMTJPR1VuRmppMGNKRnlUTk45eTZNOFdzTGJSd3M1UXdVblhYb0xKT3VyR3BUUTlVWVV5MnpER2M0dG9od1dSano1WXVyTUU1NWNxaTMxWjhNb3M0dkRMSGljMGVia1hjNEtXV1dydVBoTXNkMmpLcWd6VDlLS2E3am0xRkxsS1VGRnN1UHFLVU5EM0Y1YmRGSzlrY1ZoZndqZDlhTU54eGltMFFqaFM2SEhaTWFnbEgxTFBEOGtWdy81UE8yc3laTG0zL1U0VElvOFRCdjFIbDNQRU1uN1YvZEhtTThIeWFwVFgyLzlKVG0zVGZ1eFFiMnYrUkxmL1pGYTNwWFg4a2Qvd0RHYWRqNlUvOEFaQ0dwWDIreHBTZlQyTVhEemNsbGcwaFJqcTNNMlZ5NE5SUkJVdXBTMDlUREpWZW80cWFsVVVoTFl3WkZpZzB4OFQ5RElwc2hGcWVyMEZ4TXVoeDNFTndTTGI3bkE1SlF5dEo5VWZDWVl1NlJIaFlyc2p5WVcwa2tRNGZUMklZY2MzYVErSHh6ZlJFY0dLZllmRHdVSDlLTkdKYlNnNkpMUTNHSzJzVGkrb29ySmFqNzlDQ2xGZFA3aXh1dnFkbFJ1a2lLS1psaXBkVDRKU2xldHArbG1IRzhTcnFKcGw5amlmcWtRd2FsdUxGb2VwR3BQdUtrcVJ0Yk5tdHhKSlVrSjZWc2hPbDBQTkhjM3VpM0tOTkVYU3BJZVZ4bjlLTVR6SzA2L2toUEpHVFJyVXRtdmMweHJvLzdzbmtsbzByYit0a09KeXJMb2t0dlVqa2NYZEU1U21SZTltUjZuMG91SmJhNjBSbkpScGtmRU0zcVB4SE1sZG56ZklsYm9YanFhMi9nZmpha2lQanZWQzhldDFTOXo1OVhXdmNYajBVcm9YanNiMlNYOXlQajhIMXIzUG4yUHNsN256MnQwdjVQbnNaSzZYdWZQb3AxUzl4ZVBxdTN1THhuSE4xSmJma3plSjRNQ3FMdC9abnplUzYxN256YWY5Q1hqTDZLdmNoNGxtbTZTdG54ZkdlbnVSNDdNNDFLSStPY1Z2RS8vOFFBTEJFQUFnSUJBZ1VFQWdJQ0F3QUFBQUFBQUFFQ0VRTUVJUkFTTVVGUkJSTVVnU0F5Y2FFaXNUQmhrZi9hQUFnQkF3RUJQd0JGSWEyNGRCYnJpaWhKTGd1TkRqWW9EalhDTnNpSzF3U0ZFcC9nbFY4RXJFcS80RzZMTjJtVVFXd2tVVXlpeUwvQkpsQ3RGc3Rsc1Q0Mk5qbFhSamJJeWFPeEhvUkkxWERsUWh0RmJjS3JqWlp6TDhOaHk4RjJWc0w5UktUM1lwOWhTZEVHbWl4UGhHVHRvNlBZV1JpeUljOWg1a1F5SnJjbGtkRVg1T1lqSjNUNExaY0xRdHJMTEV5eE5KQ3FTSThMUXNjVWNpUGJRNGVDbWhRR25BdXhSZEQyT1pFSitSU1RMYlExWXJYQmI4S3NqMDNJdExZUnpDWlJTS1JSUnlxeWR1VEZGRVlKUk15R29wRWVoY2toT3hOOGJwbk15TTBLWW51Y3pzc3REb29vcmdrWkYva1J3bkx0Wm5lN29USWNFNllzakZiL0FBcFVSMll0aVBNeEZzc3NUTFJzSkRTYjRUL1NpY1dwc29WcGtoQ01iN0NkOE5uWnNsUkdLVHM2amt4UGw2TTdYK2EyNE4zRXlXNXQ4RW01YkUwMDZhSEJyb1cwam1vV1FUWkdhbDFObHUrRGJYQ1hTaUs1b1VJYnFMWXNqbzVqbllzaG5TeFZYYzkxbXN3eHdZWXlpbXI4L3dBSGQ4UFQ0M3JJWFhYdjArejFOVnFOcTZMcDlrZDBQWkZTYTNJcFhRa1VpTXBKVVJsUmNmSkdtN1RNaENWYm9XNVcxSEloWW1QRW1oWVY1TlpqY25HL0hjOWcxdUdMeFJXMy90OWpOQlF5TklSNmFxMWtOLzZzOVdWNnY2WGFpS0tUUkdNYXBsSk1sRlNYVGNsaWtsWWxTb1hVVWVWYkhMSzl4cDB5RWIzb2hMZmN0Y0ZLVlVSZG91WGcxbFhHdkhtem1hUnJweGVLTy84QVZkaWJiazJSalo2ZEYvTWh5MzlkVDFXRGVyYTM2THFLRkQySTB4UXZvUjIyYUpPNWJsRVZIdUxvTFowUzNzaFludnNLVDdrV21KUnJqcjc1NC94NG9XT1ROZENUaXRuOS93QUMwcjYyZkZsNU5CZ2NkVEZ1djllZTU2azRyVk5iZEYwTFhrcStoRlZZblJHYXFtZTA1dXhSRkZITW94b1U0MEtVTEp6Z3QwZkVrakRoeHgzU0ZERzVPMFp1VEhPa2h6VldRdG11ZFRqZmp6WkdTYU5hNHdVVkZMNkUwa0tGeHMwazR2VUxkZmU1cTRRbm5iMmZUc2UzQy8xUW5DRDJSQlJjMjBLSm1sR010eDZ6RlZKa2NXS1VlaG1oREZpbE91aUZxbEpNMGVTR1RKeXZjOXJGNFJxbGpnbDJJNmpGTG96SFFuY21KK1VLRnR1aE4xUWxKc1Y3bFNjbHNOWlZHeUhOVzdOSG5jZGJVZXFzeTNQSmNuNFBiUTVaWExxYUNFM050dnNjcDZoaWt0UnMrdzhhNkdIRlVGWnE4TjZXZGVCUWpBOU14dDZwZnd5T0pIckdQa2hCci9zeDQ0MXNoeVNRN1NzYzVSUSthdHhYRzl5cHBkVG5tazFZcHpKYXFDZzhVMDJlNDNIWm1IQzFybkpuY1YzdWpOQndiVFJvb1VwU3ZocThmdVNUVEZwcm1pMGliVW9ORDA4TGJlNTZmaGlzamZMVklpajFIRW5wMUpLNlpIVXphcEQxRDhzamxrbGJib2VmbTdzZWFjZTVIVVNqZE5udnpXN1o3OHJ2bUk1c3R1cEVaKzR1YVQzWkxHK3c1enhieTMvMmU1Q2QyNlNIbmluY0lwZjJ4WmNyamNtbjlDeUo3Y09kMkxVdUVmMFRSNzZsMVI3c096S3UrVjdHaVhMalk5VEM2VEpaRmxnNFNGeitEbXlOMnhTblJlVHlQM0hiRXBiMk8ySkVKT0JGYjJodVNiZGl5WEIyVHlRbnZXNUp4bEhaQzUxK3JGa3p4Ny8wUWMrZTN2OEFRNjVMSjc3RU5ocDA5aUVHaFBJbFZpZTI2UDRGaXhNV0RHOWhhYUwyUGpVaGFaRDA2ZmMrTENtOXhhT0hheGFYSGRIeElueElTOGkwS3ZkaTBVUExJNkNDN2kwTUdmQmhmVVdpaEJXbVEwc3A5ZWg4T0F0REErRkd1cDhMRjNiUGc0dTBtUFJLOW1mQlhrLy8yUT09IiBpZD0iYyIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiLz48L2RlZnM+PC9zdmc+", + "description": "A gas produced from vehicle emissions and the burning of fossil fuels. Results displayed by combining the latest and aggregated values and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'nitrogen', label: 'Nitrogen dioxide', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'nitrogen', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Nitrogen dioxide\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":40,\"color\":\"#3B911C\"},{\"from\":40,\"to\":90,\"color\":\"#7CC322\"},{\"from\":90,\"to\":120,\"color\":\"#F89E0D\"},{\"from\":120,\"to\":230,\"color\":\"#F77410\"},{\"from\":230,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:c2ltcGxlLU5PMi12YWx1ZS1hbmQtY2hhcnQtY2FyZC1iYWNrZ3JvdW5kLnBuZw==:c2ltcGxlLU5PMi12YWx1ZS1hbmQtY2hhcnQtY2FyZC1iYWNrZ3JvdW5kLnBuZw==;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQoAAACACAMAAADESdfxAAADAFBMVEXJ0trO1t6erLzL1NytusqruMjAytSwvc3EzdfN1dy1ws+2w9Gyv82otsa9yNLDzNaktMXFztkSGRvBy9bHz9qvvMygrr6hsMC8yNWzwc4WHyGpt8emtcTH0Ni9yta7xdC5xtOcqroTGyDE0N2hssOZqruQorQaIyS4xdSdrb+yv8+arL2Vp7mNoLO6x9SnuMgOFBequsrO19+kscKfr8GEl6va4uuJnLDX4Omms8IoLjUcJSqSo7YVHyXQ1t6ltsfJ0diXqbrCztu4w87G0t6Gmq4xNj3L1eGLnbKtvczc5O2+zNmwvMmaqLi1wtGTpbfI0+DAzNmer7+4xNCjtMPQ2OBAQkYXHR66x9Y6QEYtMzrV3eg7Qkk9PkO9ythISE1ERUmywdE5RlQ2PUWtuse1xNNHTFJMS08/RUzQ2uRNUFQ0OkI5O0AYIimNnq9CSE6vv8+AkqRcX2RVUVS2wcwhJiIOFx2BlKZWWFxTVFkkKzIcKDHb3+PT2eHBzdtbW2AgKCw1OD7N1+NNSjkcIB1STzmSjIVka3Grt8RbVlg0QlDX3OE9Sll8gIRDSlKJmqyElqhHUVlydnpPU1ng5u6XprVtcHV/kKAfLjljZmtfY2hJRzQ2OCmjsMB8kKRFQzAnMTnm5+hAPy2zv8qIio1FRDeMhoB7jqA/PzUmLCnT3OZYU0QhMz/f4uZQTlF9eHSBlqqemJCDhIZGVGNjXmDN09lnYmOFfnhBWGxIYHRgWltRa4A7PC9xbGtsZ2dOZXlPVl52cnBUW2I5TmIrOEHHztR4e34yRVainpZNWmiEgX5XcIUxSl6Zk4vU2d6Rl54tPEgwMSeQkZJVYWs7U2gqQVSAhotaWDgsKSGZnaNcdo309PGyr6iYmJcsNDIKDg6VrsSBnrdyfIZhXjyPqcBTQkSlpaKHjpVFNjvs7OzBvLNphqCcs8djf5dmdIF6mLKkqrGppJt4h5Skuc1vjqiJpLyssrdIQ0c0PTaco6u4trAVFxJnZEG3u79eTlDJx8Gch1XMAABLEklEQVR42myXX2vTUBjGpSaL5M+szLQLDaZiC20qK3FF3FWZFWQ6hyYdCIIwmBfe7aLXXni1D6CXu/LO7yj4e94k6tQnJ+ecNjXL+eV53/d4Y3t7a2tna2sry7JV6DmO68axp3EwcDRxHcc78jwvDP3VarXZbEIPhZtNto2s2+IWtfTB+kxamTRsQm59HDvOcatv0kZa6aBVFT/0V5Xvr/wPJ4guD/PcQ/S5HwSB7/v5WX7iB82Hk5PJBzSZvH3//uWbN58+PX364Nnr558/f/z+/d27J0+ePK71otaXL1/q8V9x4UbGalBNInYGgygqChdFkeM4sU4v1uJzsfBzFhUirSFrWYiAHTa2KLiuAXzcAcTuwHWAcW5qeWwSgIhwIz9JfA1ikSORiOOY3g+QjxLfUPADJtWEBordey8/weLpA6F4/hwW72AhFH/T+C8LvnpxI9yEqwxVISTcKF2Put3uQZSmA9ckGl5Mz9MkSR56OVRwBdIbz1DN0gAw1BNdstnGHOGm3DGNXPfcPT+/vHTpodLiQBpskmxCSKATPwlx4MXRkePAwmzRorDrmgZBxbfYYm/vvVzxABRi8blhAYxrPB4biP+hwBVx7Bz7K94zf9B10/WiXEzL9XrNgwMjGhR6EMIEKE7oJaEHi8QPEwuTZIUEgNaGRDMkmAKtklj/POKOo4MoujRFl+YRt4EBDoGo+xBpsXnOBEcAQopBYUoQfZ4bEeBwnAT4AhRvngLixusaBSxAcR2EOklL/xeFO5jPC5al1Q6668XscDY+LMv1wfogjepgcWSXQv72kpXiI0gwrTklkTdWTVrY+HnsHft+CCOaWGQruWoQAWI96q/TA5QyDNB84Fqk0L61BjkOYUFLfCNxrD/jFAWDbJFwIYRRjgACCSioDyZv35IrYPEMPUf/ooBEO2lkDNSYC0U0Xy7nrguJNOouRn1AHI5nZZ+5DO3qlCe6UZRGRV7JuJBwjuRaJ9aTJaGfKDuyBN3H4cktxyQggiLpJx2N+r1e/+rqgHawTlHUhovpGCK4AxweQOCY4D/uIg4OqtMF4YlqLjKJUQAJyfMtrmhyRZMs/sgWdNclLGYOqWVxo7vs8lTRIE1JE4vpdDYePzycLuqMEbmYwhSl8/ly7oRVFvjhMQvWBZ7Rk2LZN/Rzz5IjcItijp1iZX6shhaLslOWvU6v1wPH/j7eWM4VgeeNfpyTT2UQ75gzlIyEWxTgJbFgQsSgXlyUPRUwHFQbqyFIJeTmTQuQj6BoIFjGYP5v1mjUohCJottdd0eLsl+W0ykkRnjFFkvYcIBCiTROqiBMHFSwWldvi2a+CSkxoNBP52hJ2NV2Wo5GC9qig8QDd/T75KJI8Ycv3Et0TkNiYYrt5RdwQHRINFqJRZiLBVLMwGJv955g4IubZopfeZNDamEwuV5TfqEgaPkz+N0WQFuCpLsc8BjEgCebalqk8yL2zvCtosPRSUNxnVT1npgVEJjX6nbn0BOJslyU0859jo7Oktbv76d6A5cRuvwlRQpnfVfzBGzRABXGgybV8QIMiho6O1MRuff+PTsLc8WjNkAatVNhseFfGo9v6L5HR2yijgoGh1HDsb68IHURjaxeafWICLYPegv40xK8p7N+TY5QQAUYwgEChNcwBCo7vU6rcc2CuFH+SWkHf8Hg7yM4AAFgdIOlRqCIhXDUex1AhAgWJx+Gu2+F4i4o8IVsAQwg6FTf6ppH/gwQT7oIfStiSWCliQ/aXl54Z/4wq6oqG1ZAyIOATFH5zIMkUKgmidypHMpT4aGQaSEJxmKKGUbKl/1eSRLqEXs0GQMUvf6+WKzXXUbKCjDEQzGCnNoSCHtFkJCWy6UGs4n5AmnD59HysxMqKrnT6ulN5c1TYyF9Z/wfjD9ZMBOKxMvr+qzXPqmyKqBK+Fbes+1G2XYlEpoBZzLcHg6rIZT4qa8g8hA8nCPMZVp2ScHlYjHqIUUIHPACGNqkoZxxBaoRSdR0iUDBShsYgCBlLQdi0TVptxO5klnRAljKLwSD3Pny011QiMWpsRCHj4ihhqFOPqFDTQJFQmEbJtbF2oiG4V6WyQm8dTxSsZM2MW5n1dBQoBYPs2EWBFbk4vAEaxTsDgkeWBTUo9l02pnRys607JV9jg5Zkx4BAz7KIxBBBuNccrUbRXWWkC2ipVDsS7bbMRZWwJqi4l1c5HUZeXPnzt2br2FBukBwMLUWEYZGrTFEw1BYNfIrXjgMmhev6WTiV0ORMGnMdrd3Gbm8KzLMaFK2Uh7PEzpK4MVRHlrkuCNQzGazMUB6nT5hQiWliYRljNm4c3/MD8oFxgGGsaCmyBxIIBAsMIVAXOGhETAsayBt+hqB4kK5c/ceKLTPsr2Fwfj88VRjy+IXiGsoYAGKQLL/PeztTnC+HMC5u5dtXZNc0Ex3OG7v1J9u15eGJ7oRQBDJVh+85WLWkCjHhEQfP1yx41yXnfH9V+NXh2gsAQyzQKMOEpdmp7IlEol0HxIEFGXZfKFLrtRkUWwhX1BF7rHRsn0WMB49Oj09/YzgIRTAqHFcKyhiYRVkbxgMcX4GAtNO4wJau+it27d3brUU6v72rdt8KQFD32bbBNcw+BBgjQBVkw8X8+6CDdvhWDw6Wiwvnp1Eyo6WfRz6+vDVIUTsJxSYqytb5mUtS5GSJqlACBc42LdHSNYwFXU9yUERDHeF4vemU9awDhYtDdNf2dPS5vZeQwIUWtaONRbKe2fW6idbZrPTVBBH8Qvlo7UibUBaU4o10gbSVmKRNE0ItiQkJYD1AxJXJCa6bSQhYe8LyMa4csULsPEReC9/59wZrcbTmXtvayXOz/P/mCGbzfpzwVnSx2YhS+gueFZto7aR3wBK7dm7/MMtUDQl4iGNANLi8Wqh9LbZao0Yo28AGbRkDuoKvtn+sd7rLYABVSILGtgeKAgjs6DkrCJ/w+XV+RM3Uk+flcvqs8TCxsgYR2RhGuJhFE6ekQVKWETIg6DIskKvW2MuWwUAlygD+ENHRGwdsEVRWKxy7QEd+hbZAm0KRbA/+ww68fX6aASI0S7SU+ukVX8CDVaaOqOnjsMTzVZ68kTamWxSlGhXekg44DSDhIL2GxJrbsAJEmCYhhWjxDKM0HfFMw3eJmkeJDzKZbnCi2dUdUH9fpJUi+nzXbXPnxb1Z1XRwiQMEwixZHdsUItkkPnDhdMS3dXm9KYyRY/oPz7mlKby8rQ0GI0auw0Ei8ZuawSQEeZo8j3yIq9VFooAUXj5UuGBIaYDCozBt7a1z/VJQiir7r7fn1NFzvHFm3054+lrYDC/iIYVYDhININgkSzFg7glruQEVhpUNIg+qi5nq32941KFje+yifOESOiOS0yj/Mx55xX5bKFQWn9xulKioVogNC51onOpCDkZNaR2m5nyAIVYNJVRSJEqm2i1IG3jCaS+fYVqBAsI6yvYI7BQG/7B5xYoNcZ+eqYlBWNEBRaTNLBHshgSouLeGYC1ar0sXItOMgli8foo6VeTINGABes3COQMAxCTRY+eby2sstXb4tSm1Kvc/D7WuflY2BwIxbhtDcdt+wNbDJQzWDcZIcAgMfBgEkYxzV5R2lx/gTPU2jt9ehcf9iJmYRiwAEWEEfKnZsygfynB114KScDZklgwhX6m3zcGXQyFi97mMhJPhkEtAZ8LiRSescZc/nlh1rHM7p9z0htnJKG4/HjarLdGjfEYDN3hcIx4xBshf5IVSmg7KBSPaZEAhbUCEKmEL9SOK2EcUkYE4xMwyufnJA2hcPo0iygdA7vVMA4T8S0JaTIqKxR3MkTGIypjFjCYmprK5ZimUfW3ZQ+Q8CLVFrOwkN5xhKOesPL8JTtUo4CEI6Q0aI2EAhDd7kG32zWMcaMBjCcs15VXLBQrjo6IIrJY0Q0S5FBEOmGvog20+m9QYAtwrB0dpbmTMSkDIYvCgnriq5RMkOAWMoXWjrAAD37O2Qm5zFSnMwWJzpTeW2BzrGSLi+kVElKNQz3Zgn8k14fKE5BANzPrZM02KMYpiK9oLClITuoDcJjFyo9tejIUSZhG0yzInxInsd7/FsxiBhSBhXXuqmqBBIkJ0zQma4qVyAZFLprVoL59AIfMTxbMw09QaPGCAAteZiFKSLHinwBMhYxbsAfvONSqVNhPFbYWZmceCQNDKCovBmRLQIwPUn3dCTTa+EI6AQY0zMMyiSj6eca0iayw4wu+eI4O1YC/UsLYUNLAF/rVyP6+G9AAxYUlZtLQluspcekEAgOpUMS0wPIZVo6YYGRyAgEOJt7opHhMo2qebjsAgZbYwutc06dVMzP34oH4JSjeDhqODOaOdC0W1xHGLjjqsEAg+BeFA2gTEJHLC1hsFwKLQ1godyKjeO/THFDAAk145E+kRIHCcoYQh6os37crmILAxSg8ICB1GLkObPyREkefHSE0BIE4I2PU8qBI26TK/P2wpxWK4/XpVhsMsgMEosTi63AoFiP5AgmGxt8couJjqRTTxSwonDrDL82QokQyEJXXSMPBEnm4vCRp7xSqhjmkWdIgRIHwyFkBiEmg1Bx84gtfX15eBkERlxV5Wlwq5+UKnQPO5/Np7yJrXFZou9syxMHBzvUVEKJwR7c7TGsJhfX3uv+2BK/wGJ5IsHIFLMJu5LPOtD4xPonFGpVVXUbk4YARkJA3qCipkmVbwopp0A/GgDIBQVQAYQxcPGwdYIhGlR+YOHuUaxzwebd6Lz+XvbtbDPvb9fpg1HZoXN/eXl/fXqHrlMkB6g5VSuhAqazNplbbnHCCEfhaj6BouOg+nDhnCRFixCxefRCNUFrVjp+vHYkJPKIxqLTRHSiJLbbNYDk6JCXLQME3OcDaO7Mp2APvne1NnZ1BhVhRSCGR4C/2F2v5+zOPamV15GsQvwOGaNxMDwa7uGLn9nYHDtbFxQU4GM6f1FVYpN2n1g2QKIOwHEGitOJG/BQUgvGHxQekm2GotCKImAaRIh6aLisuKtpg8B+J0uYyKM0TExnTlw5vNPdUQTQ7oED+02gNlOGDZDE/T7asLc3BpsgPFAtSyePpk0FrdyhT3ELjCgbMi+8XxqEwgYUyhlEM6g6T/6E4EQw+8h4u1lO27Egs0OfPbkAjDELFNPhF85FgOE4MI7rC6gcWyAxiT/k7KjwZSLaY2jMC2aKjB8lZ1DBUaTLZB/ceztceZKsZCo1scicWc73Bt1Gj3YXE1a0NEVzxncEbjEGMUEnMokX3OYkiUrAiC1A4QJQ4Kz6/AIU1b0HD5dVawx1H1FidagQUMUYSJYplsZAlKATMNFuisHZusWyktnCQCIc9YRq/2Dib0LiqMAyPaZKOk5pJmKSZkGkywiRknGRKzZQwEExTrSQkGpNOpQhGxfoHEqktRBdFXKkLjT8kolA3FbtTkeqi0KKCRVwoKAguuhGiWYkrSxcVn/c953Yi+t07NzOTiXifeb+f851zSr8IIJZHKlSkqXR1tL/Ynk2l7rwLSqnPD2E9p+/TcINIAQcj+IDj5BocsJO8iSwcLgIKDUv+g2I4whj+F4tpsYizOnIS2p42D9RocEkbo4JRHczP25osgoNkHSg4gZH4BiDIGyGTRhRRGUEhAkGgkCyMQhyOYbxr1/FllwLnYDab2uUge6tRdJ5bJEMgCuJk4MDJAQehsCqQxSyyAIW7OkZhDk1RRLNkXKMnxbfa4WYR14YYS2zs3B+1kc/jJRSi6MIwDkdZpCgCnE2xZqiwm0R5JCwQhdOHSfDTsjCJMz6aRgThd7fu+iabPnQovSuk3s+xQ4eGTlRGGIwiCpEwCh6gOG8Wn2IEC3mIZZGIQiVWwMDZNJ5HVQgFRl0LC8WLVhE5MdDmGtTVxh5QoIwQQ0NPI0SMEDpBkaNI9mi0GSt47AyeNrHghu5C7BAwCnuJlNCE8MwZoPiUDqCrP3dIQRVi0XmiokgBCjhwJhZInFwDxcGDsKDujCgqw00UhrHTnEWkCo9QMXc83e7khwq8mFVCy49pI1g0WxqgSPLqA0aRW0pnSSMpDsPgosdOFMhdF1tIqSaC4RgiYrM0ztiO6W34BkXtVAVFtzKpRWEI1sW/SChwTqqJEQKnEubNiCkUGxv3bTRZhAay4qYX9mDJNGt4PeCSHDfRLPNjHaNKJ84m7mdgXEKsyA2u5OhdUSF6/CFrNieSDkXiI5iTKgdnDJsWSVAFYCILUOzq3kfgPRbrMZHAXU73VhZnGYxaFedPfnD+/Hk9YqR40yBEIo7NzKIZMyOADVnwEKsCFMRNUVCLDwJggEGh0dWoBzQHqMmZvbv/MQWMyGKeWBEkEYrOlDLtPE7SHfp3Oy1Kw2Z5mITPaGjfknC5xVNgCAUPRJHa16OEI26kU5Po6WnvLY/MHokOgiY4ZEbxZpCEbXJ9VrII4SJwIEYmJBIWvBVk0SdZaI7AC1kkBxrBanIV6qajpUW9ccpdZg+xKgwCo/BWFaa2pGTRTeSUl4DEJedNNDtgxCyC+ep8EYerjpecSAI6uyQxf4Y/FgmMem6hMg4JHASvCJowEIsioCBWrMMCFB6MuOZkMg1rkvgfWWhGte6pRDUC1RCl6RMG8DgPFQfTRiRVo7AqlEzv2TFhkgokGF3TaOjG9vUgBx3/qw87C1fzMBJDaJrVcewMKDxghZdJQJUCS6P40zOzk4w+1sifgACFT2WQNYUKi8LSmB0noUYWw2iClk7CYQeMUFgwgdIAhWZLDMNISjWjEAcCKj0TsUhyyIpQxNyRoMiTZhkZLOXmlyCS1lA7jKewBIOfxIE7h5GEqsN3byLRQODiQknXJVrs/UECA8XdLURNWKjAhEA0OQiDEKPgVBLReF26CE4yw4U5JNkUNrIxtYPFchkCDfeDPTEgO+t5h5qzrE+mjowCDpEEBUX0j4CiKhbMNDLBttSdVS5JowxlE1Vd5mFLCjDHUfHwd87JYQuhtPncrmRVmIVIeAyyPOVk+qlDps2ysIO4vsJ0Ve8iwhjTocuUMXBgJuEcUvZcfLh/jB+0yGtlGW0dz6Uov7A6WS0uVGESQhFZJCiqgMhjjhieArQ2GG0rqYAjkvhv+WWTNJpRIwmrrryTZ8gjymJfNn26LxQWa2sIIUEhUSATRAEEG60dUGCwEA1OUZCJj2Aksqio3jSHs+6Nm8hyrVLeW2FFR6kAhlIXAZRooXGau8BxNlG2QxXFfLWalzbItAqfGBeyq00dCDcikt5WQiM6S6xIE1XwiK8StcQxq1Gk92WzrRX6/vYQwmU0KguXFTFuGoXHZLPc+JRgBAJCwwgeRLxKdKEEIk1AASTGUtMquLJbGaWG4mkJKzSYIZAsjouFSdggEVEw3ztarJoGKCSMnDrfsFiCRPowz9SUSvUkIJoSSd5I+r8egkDApy92D39GKCjvs7trM8yBjDuHSBQxmwYSocQSByqPyXXfOQAgMi6FzKITHpjVYhbyEN0yNIQBKghCttc5toFBgsVQpQYzlbiIGlyWhVEksjAK5tY69CgGT2ESgyPLsYRxkbtw9Oiuk+5MDyrhEalEFrYoiuZAznHCJKyK9tZKmUWyI+sH3zxoJcRy810OSKjEUh7FPbjo/n3bIjAZMPA+h961kyS6wIzCVmZ+hEmjFrlHozTdVSpr/U+ta0Ao7CKjzqWC4XghFrR5M6yF6M9wFQzHjLBuoJuEspQFRjcSyXUjiyACKNht9OjhlSyE0hgj/cRdHD3lAS+6IkjtG5DmMvv33j6zuAqL0KzRBRRRFSah+7aN2/wUNtKK5eLfutMVo4Ua30lXnCvzqlp8ytqvmtcBAqLCfAHRolWrtrxoS3HzsE3isCxSWiuU8do8r58/Xs23k1FyspUVLvNWBjcuiyTSenRHHDGk3OwL6xFnkjDrSP0hTcli7a2lysyYUYCB/Emv4oO1d7/+6a2v1w5KFJNH4pdvu8lBRZdzi55YIc61AUbo+zb74Z4+27tcKbsIndhbUZu0XKq7togTaCtJ5Q2KOAbRukStfcxoOwHLAarFosTR3o4+YAIVKSNtENE1PH0kAPzoIaakDy8d7jYXxVHMV6PwX6iMTbff3dkJiUxJYXMcEqBwDwtVHPzjy/effuNXmjaoAhbj3Lxcg5/mYAqhRa6L5lnV0VAgJbNsKKFWqMH4YYsd8mGAgGJiokVFCa8mGI14yt0uEkNn7OkJhVe4LbAwERAhaBTzMmcVd79yg/PdDg02CQKx4zyeFJzHjQ6rJEFp3Ycpz1CIG9/7cqngRwHZwitfv7LZ3l5tg8Ti1OzkulEYBChObm1de3BtHe27m7dKG0v3KQ7owPePYPRQ61OOM7kOC2A4etLP0pIuYNxs8mlFHOnUk9C1mUWN6spkEcmiF1mE2lvt7+asSKqtFVSwQhbQkB2tGsVQMfJAGRqghErCqki3L1GU5QZz7QRadGPlcGKuSWB04vFn3++UdMhDvN3+y3NPPvllS+dceUx9fbV5QcGpcHHwyLtb2w8+vIoXYAhiNdziCHkTFLCgI2wLLiQUaoyTX/zJWI66DxqmGEEBi2Wa4WKhKVoGMlrDz61KFvQtnlC8gIWDhVmkpvdj+gTxAoMIy9REgWVEo1UrI78EilhjOlikcxBYqcqDlIbtSCsceWjohMzvm2+/f5p2KpJiBmIlX39ke+u1o08MjKlYXHTBCQaB4FZX139+/aX3VmNs9JdNRcVHuVf0kDTHPzUJPjAyzt8jCxkfVm2urRtWRUThVKoEosBZYbjPnAooWMMf23uw0LotZGEWUgV5lz5Yq1h4zbaW0xMwsA5VHFZFLi3ZG4ZQpLOIoooNCtjoUcipYDU2zC++fP6ld27Xa7PMF+unnvr4QrF/gDUm6lqOzK6vowpYECBwidff3twccVyAhKpL3Z08xHSMAhYh1yperk56xkQ51x/nTjnEg4U5MasmS5m6JoahRbCo1bpK04xFFtwGZzkfLkIaSWCkSo1pWBwY8KJpncCAAbeozTf5o4qiYoEwlChT9pClQbECxtFRf4z/aPV4nr/hPTEpVp97+d6HzlatLnN99bPN964c7ZgbnuJLRxYjCheaFlsHxNQPW9sPb28wTnO57TYF3zMfcjGFBKI0wObUgVxEAocClucJYAEKtbzUu1BiJWTSrODBkkkt9FI6LWmJvfZMzu3GSdg3saKehU0OoqGKVjR59b4urO3FRcQCICF4aHI87ZwQAudKngCrzzAPCLpMR1GqKJKEREZ/9MqVK5vneBKIZjJ/fvHaxcuZ/tby4rh7MkQCEip2hLtd/OXyhUde2FgFhAKB7gy5b1Bvx64eMCaDZEg/U4zM+OMwzarMis70+UhiggX2lBWsl6433LhgPeCYWNVwEFxE+ztaae8RO6sKnKHIwlLqf+n3YRuMlv7tybAopNqZ143gLKxGZZ0Z43cPOGAhVeQtnDzIOryv7QlGMFWWrzI9asv8/tmHm3uA6Vd85vrW9lOv3XHHgenKSIwF3BPZ8sisltpcv/rjZ6+PMLiQ4BEz/+NlL3KVPsgEM1MyIFX6likSqExGQEHowOwwscVTYYzaKJW8eJxnhQK6YPBekb+FWDHt1TlaCa1ooV0TzY43v3WzmKUxoBCRPf3a9jBUzXOnZBVY5CmPhMJmB3FuwUHAZQfpqBIVOjJ32Pr5o68f3n45Ay3E5T0T9Q8vv/Q2O0d3D4yt2r1pP+DhY4JyX/mXixdfO7U6zjcf5V7BlB5JDDO1xjKVs55VKvUTr/aVuc8xRnRxwKLGRoJvmUGXQqU2Xmg1ozYjserxLFhxEMYhBXX2ZHP9FBdsplEK0ShdHW/1NeIeFbRxWyuO1I8WHCKqoMjoeT6XpX66JQwplEHmCY6cwlV0DlF9ms9o54T3t1W/vHT5paLDqVynOlr/cPOrKxk+nFkmGsapL25X31dL7Zet7WvXUIXXFJBhZlpa6LvIkERpPyLvK4famY0z9Ym+5bHZED0YwKnkcnTR5r86OuhqTPTVWPtL/mjEdb99tyOZGjLxLMkcRqNTI5GwjK+nh+UFqKLQmC5gcpMCYzeiZz+ewe6GfGe12lGULvCPXDeRwiSooCiZSJjtpNT5QXAcp8agfMgOVmORBp0ffvzxSi5LCbOE8cu/tra3Lqg91l6aQtCwcDFc0fruUtef31598dIqodAJBDG3MJJinscdOea8yPeFLl41Gm0n2JC6MDE2i4M4s0ICWShbgso70oiVDFOX+856t6zemW4sD1fIKIW64sTu3oXeuT177h8dxCQLC4MMMt1Qf3QAfDL8gxxCZTE0pO8UFxkVFsKmSwuRsKnXpcYGSPiUth4onHosm5vnmt689sjHt6ZAJ49iYHbj0qmPL2v4mmobnlJZFIK6K6GuruuffHI5QSG/ryyXSw2c3pm+99xC77nduw8M6H+VlbDsy62DwgmFj88ahdIp2ZI4icDrdfmGSQx43U8XcbSPlifbBTWZOsfXndGIC81q511MphN9EoRdpMExoC3GOMUQBwjyQ2wKaQeFWcSFNTx60uYBCTyDxxKp1gOzZKjyyKlTHx+jzem5IVqgNz688tXrmjVKtWr9qtu3ZHp/04WuH7YQzeo6sQLFEP/ck9QeR81tqfbT7ug5OQobAxZOLwjFEdb44RnDKtqccgBb61Or34bGp72iU/Nlt5XU2mNJ8X6yKCjm2JAJCrl2GK2LRmqiIf6Qw8tEsc0bpdgwBImcd3okG2RywOBIhmLQAAVH6GhAIFKw/XD16md33RVmh9T6vXFx6+9LvHHsUGuNBIBpTXcJMSqFXf/o0qMXprSuN0q9hhwggSaYyoGDN6WxDZSs7/2XhY2x2t5zr1JQA9Z1t8fhy2dB8apNs4TaTNKmTar0LLA6W268hZVdsUVGZLDQUPy4629QIEMCq1jWB+roiWihIXvGLPyPDnjBpVeBA8CDKz1s3eAxn25TwMyCy88XL34BAbP4/sz339/4+9qFy5ovOlQgHpIQSSAzlRqueWBgf+H6b1e/+u6+sXEMUShSFNityp3Dwls7sf1BJq2g4MviTs8N9Q0vknTNAXNXt1D3fvewfbVNmxP1zwC0Feoc3vdtFv0kvaotKMOdzn+YONtQF8Mwjj+Yje05BzGsDUeNZmkavihZmmiZ1c6mKG/LhihO7APlJV+8JAohyifykUhHqSMUSaGmlKIQxz74IEKUk9//uieuvZ+znvb8n+v9vv63Jz/LncWkzjQdyZiokzHxpRBoDYoiYhFJL+ymu2PG6C1QOFF5wrNeg3va7aujRzi9AImnDwYfHrih96O7emnBIsqSe2ktxWGGPRsaunp7OtkC5kFZxfYAYqmKHsXJ4zUlDolsQOT1cCVUvJS/NGnWDB1LUBgLDSdbxYCKOv963THejd4HcNXEHPGaHfe/BBKuSJBarDOHUfaM4Kgn8UzAAstStgUakWmaqWNrDeFi5DmB4bRijCSoprB8iN248+jgEjzeah0f4aEXDopFv6/19z92viJp1EJyY9HGgIKs/zhF+uaZsxci8hREPXJEqqleq6C6FPBFCEZTtIODH8zDfC3GwWpsIA7nCCREcLahRcg3boeNYp0br/oANbGakL4rNc5XSA+cm+DOgyeDQrspQCZgRt/xOQkinSw81kQxoBZy1JgfUYCdpkzLbAQQxttk+98eOc+I0xoF25eHHt+grmeegKWyfXPnEkyH7g1jrT04khQB/8ZVTPZyBl1c9q7j5y/v3rr9jNFCZgAU2jmff/a6NZ0Eq3wYiOxEsTDM/DgJzKWAfGo9W+0VEnY5lTfU+bEdCaEEaXtlUR1LhN/NRW2CRAYh7pEiE++WM5AkLDy5iqQtqOEpQAJEAo6cFq35MA1FOEbFREHFjzrf+VctaNJNBQwXQwwe5RBCJn+g3d4zZZSwII6wqPr70MDOy0AxN5yYRJAXB1WpQ8KGIeIvb926c0+FAjbDSCpnNIeUgyzLmgzxbBS3L98PGAUfY62E883oyBzWEsjSuuRQ81AbZxHNSl6lgAChzhYUsYIubxzTUugIAwUkQWss0Jgq24grT2UvgVrBX5lHjwPXIklodEWCYijvDhPLpFoRYgmn68TR6IxuJ34RVgcmJvu1rNQ8PDBwSrYCFrrNbQ5efvwapZgQqyZ7CXygMSnBylXUiOAfiaVD07VbhK4JFyOUnm9+kMKhmsNarcUQFkkc7y9iSfelQJco3/FcUmEyiW/JFn12fYhpNw0xRl3LQYG4kI3bd6nOKSqspAYKN764DhESgqKqyfpZ5OzzjZuDkIgoEvET0Se3McJfljZ8S8ei4+kfCQYCqjCxTjl6YVZDTGiRe3imFyyRfOZkr7HWngkke62aRivUdpWEos+uDawfmIjNy2NhDlwDiNxGwaPJkAUIsthSCdJgmDrGD8GKLuTU18elKE5WzaNGL/lInu2D7BfLS+rHE4jleAlFdRxgg8Ip0231gNym/IWgQLyLk1zXR6EIE04CsbTD/EUo5mB1NwVYTtwRggwHtKEbm+NVxzaMRpUNpubAhw93HBQ2e7HvxYcfu86OWemJcTnLwl9vcqT45NobpfD51aNbr6mhFsT5ID0hgU/n+BqQJecUSina8Q1tYTIZPni61peOVOJJGRjxz9y++dXAJX5sZxsl3hXrBR8D12Y0RZHb8TMAxJGUZbrWLbOLbuuk8v7l6zwc+QwJOmtJihQE1cBK1BFV7PEFiPp9LteQBjh9AIZudA0xjFlGkk+VWx3zfe3Bg5vrWkgCCJnI57MX9hzHb8SycaLpDIDIcUif5ppMpHV7aGhgAXl/NF3yoyMLRpLPwkdU4plTbwGhCdcN0Xcab0qRSrqq7HI+UCBz3FXniIoQJMq2ARUGXjHHgYbgdUWrUo5gwUN3VMJNp9GoJt30GKHGWYn5yrMNPlEAgHYCtcAdgwWo6mhhiehfnc2v5CSmdk/mLfqApjiXaVGVNYHjD8+eGsUQr0SLiJ/7dw1dJgX3ySOSqhGzMmSUIh1CCW4QTPsJ44E0FzFkiU1PKuZn8Yj4Dr7KjAi2kQpiiqMm47WoEJvVWRZrFYx5KMIECkrSLVxKM3TelQqffSpRjSwiJFdymRnzExjGfnRCONC+8dQR7KzZA8kZeTRp73xjfscVVH0ClzRDAhKI4ybzihj7Q7QYzt8yC0u5Rj28ffTUKNlHRyjDr95YBhRdY7mUrlFkc5VhacXnwdbN11FKQdJcgoV2LMFEIiEuOOfqWKSRCIoN8PD+1BoI9hSoOHNCoCNpHnxgayLlQk1hUlE2wCd+PveQBtOAQumVbuiDzSuW3eymt2LFbEVzmtAiOJINI2AxSaVhHNVw3F4O5Gj8+Q4YwCDRL5vg2uCIQHCK8X1tu33TWsMODe/z491bnzGSVWQCpJpDsG4kHvUDkItfXr/yuOWDgTbQkFly8bgVxo7FjYpF4fOJlUzzdcFRwTFc2UqaTWkMCCZI+gQW/lR5RwR6mqIpiuF6jnmnz0DCdyI9CnfybCx/2nizBDCWeLDDjf5KN5Vn1pqABUNRsEtalat2R9GPUrAi2uYCF92sWPIpGuIUhctFVOIOCAke49LlwVYLKISEcYl+Pu6/CRTDiuI3kT6p+0hwGBtCK7q6bnxotwdwokQOwoSCYApHX8t20a+eQ2SVm3ItMbkmXNKU4NQePwsSVN3pWrhkoiZZn49LzWtbLTk2deNSDfXiaMzRTSlx67GcW54NdSgv15q6Uw8P+5hNQ1UrCm6QwXREngMrYTohoYYHOylYRxztMGlqczcZCs2tYRrIGzbCag+XfvEY03ry+IYtHCIeELQIppeXDRt25NyXL+9Mvnw5BzWoHoMcMGHw1p0fNzi2Deorgqcs8scC4tYUIuumpjCPHomtY473vHGTI+k5c9IpPxDSpFlPipNEtPEeJ45n1cCqXbdIAydLszbTregDR0QrppmpZYS1m1Wa8OUPvHrTZ82afubMduQvFdiYrzIScj0lchOtJ/bfZhHYi/onqljHYx6LKD0XqSszQkuDroT//qPd7pdW2PQJwfTlnQ3rW2QY71ZvXL1x48ZtS7dt28brmufj5lKk/P41eGjQmoH8/pitVWYsNsUIDIUIqTEYKDVKIdjJ+HHjgyVMP1XOFEIl/k6uID1KTetOGR+mBhihmk12K/pYaSkoaiZ/mbkN3tS+Svp4eKQ6w9EK+iBqKc90KwoUS2xiZSGWwWKqhWqnJYLrcFRf4dHMU5RM8UZzLnNtktm64eY+v9+81v9wikdGITl27Nj3R/13Xq7cd+zj3ZMnN504cWLLli07Tpw4efcnOB479nrow4d79QLusRFBTTgpt247NRVNFNINxWrXKaygHrwKpUikrLaB3xcDI1dSZFKreqQfJc5RapHWFny2nUOtoRSzp9HXZwQJkUV4+9Xk05s3nz69ef/Go9kxcQZLsKxF2yqsWoB4S7XFSHtRj+krFtt0vVJy5YcCw20vaDYCFtSfQKGhCltbVm9vystXj16PGIE6PD2G7N3bur156DVvnp0+fXrrJidbT5++/53/GhS371ByqdCwDXdQggxnPb4cJuUtRTKpBm4QA5DSqO9M22U8johoVarhYpU1AQUQAlgNTdDpIgoqYOHGS8Crj5iby33KfQUGEHjz3uTbt2/vv719+4eJsw9to4zjeHQyre9lQxSdL/iCii+oiCiy6ioR8W0qBm8k3sk0d1wimdf+cScbMf5hLF0jabCSgMWxMUFIFLPWdsUlS7R1W9dAWwetDjToujHbhTk7Kqz4+T3pZL/k0mue3Xq/z/P7/p7nuXue8yFHnq3x9MWM6hjGsmr6Y57ghqnvX72HG7KExosrq9U+h5KaRs6DKBjd7OQBeldzr4gpu2pO4kpQoOROkzzYIpcrhAM2uXz87NGt+fxSWZFIxZosnEVVeu7fo/tnmletGHDhBa3hWzjt2+a7lQU2+HZvc5HHFvwCFZljrayzYMLwFeRYLs6pe5JCgn8rVb6yUEgxuZcN0ZA0HiIGqH/CgDiAwO9ip041P30s8etk9Pr2la+ppvHqTtxj47N9J4PbR7jlpu5FSvec/g6XnXiaxKgR6vn0tqXazCKBsZoBKNqQdVHrZAmyTNBp6MvLo6RTIaHs33/37V/M5/O1srCwhQMkKtnFvCo9vHv3UdLVJ2x3fcrUkZtuuIGH1LVy+EVXvHpnOkh2wTQNSVUWbn3phutfURNDgf4WA1ZZEyXphbBAA8KBIaxSgYBBLQhEqDzYdi2BICgkFpT/Z5p2ij3fwLrmMzhaW7qxifVMQB4YYPXfj5fRlaHHdhX3Fa6TiRsMnhHPZ/WFPlszCkYiEYmG/InYkbm59jXrfM3RKsN09CsTmid/nUQPGI6Kt8c+Gtx/DBRzlYpn2xkv43meG3eS5xSKw7Qvf6ZjlhbcHMFpLHY99c7h27ateu2OlMqwQU0LakErlrym9RlGvGoB9DMbHpf1hStPytn5wN2X36ueRkX1P0co1JNT5bSVCofT6YqTmzotwSAkzoOAxMmmseObmGgR533dW3eI5dmwrd0TwqXF9+Pq9ksvWXmsxo1kzrlcNu66diGQCGg5x3WcuBVmOg0zS9aAYvUtKAbBrGrZO7j87bYLUBzE2aMSFZUyJHiRM1w313cuT2l3tmOvqYebda82q+//JQTPbrh+6ge/ny8VqKB1pFVu7Ul3hbsX7VDSrGAwyKYxc+chIECi7W9hccMUOVqytBJjuvLJX3+3kSLA0ZQEm0IxP69Q5PO4vH5i/dYdJ0788r+dOKG4bAXJhO+aWzoZi1za2c4nKBzXK3SY/oCVy+7JZXOCYjWygMSa9p0f77zlsrUtFy0eP3786wv0QVR0dB3r7gaFa9vCgaDw4g4opHTxp4PffZs638TyTtXVShu15mjjhoWIn29hoUVwd2YDo4bWZ1ZtfIbZAM8TJmEsRfqJxdJ16h0MbX9hpJ2kZVEgpSirXLlry1ukSyKDmPgHFHyIQkAxz+Y70XR5Bz9BcQEM2Xao0u71vjUrY43LJrNJx/MKBd1vWIRELpcz0vTC6V8Ii86dH3eukes1P+46tvuoqtjzueLYdwdnaFFqboYaEvNc13Oy56Sw+/Dy8vJu6lxVfRAZpHexal4tJeF5E7f30hfRCAhYBK3UEmNq6SFtoLvxxjspKxZLxWie2UlPfalSonjLq62tHgtaYlBCI+Xr3nlnC5Sa+QKN/PMHKITE7Dybb2sef/OiC3YuCAz2+FCWJzTUSlMmWNRySaKio+Os34jlnDivWFo9SWpN69prOrng3u7z0bxu2/3Rd5PrQUGnQulECaTlqdWjwc2bNP8T77+v2Z4HikVAdH9FadfBoEaskxIwKz338IesE9+4aiN131oPWhCiyKIk06CPKL1DRh7UcdqSr3HWsoKx8mlaRhzFxN0/6ufLYlYKFm1btjTbUDZQYKIQwYCNkCu68zs4H85IgEBAvGeHPdlVMYNRDpGJmpN1vI5Ch56IKRTkioyMemSI+jb2+GrV1xaB9F70egvD15bXaVInPlouDM6sWr0uZ/n9/i/e/+BdTbftuDO4+NU2HqfR+OlrbxIS0QgugyOWrsmKYHnOgsx2q2sSEJsBstkKZ267ewu9A4ly8bqiadQ6IsHddOX0Kex3kgCfvEYsBZZyiZvyfb8x60S1oYoCc2fJE3A4NHJoZGTEN0CiWDEVy3nxvZt9wXICdSgIpD5yHqU1RwVFhx61YsQEby3d/sILBMZrb39Oh/yFdTtnakuNI25XR8/rF12zuMSkrNGeniO9+/ftn+xcHM1EQwHdCASiIcPQvfiexsMig5mPvh7cpUUiiYSGv5FguLwEgw0o4Xm5vFKPqBwCJiilXnyQS3h3/n7qmzP9wJgCjyWJERyZzDxpkHo+2bT5kgWLpoKIiko/8xD6JTkIARgAARspjZQOse8bGAAGy/4GWgQJFS++g0dgEBJwIBpQPCEjNul5uk1Y6AnLimOOEwrpoVDI8HrmjtR7akdMQzdNA2dDUdvZ5cY9HXBdWDxuN8yoaXjxrBNnOoWY587Q8m5o/f5Psuxmf0L1HfAtXW4wZEQGLJTFFtR4hRKa1HD4k/v/ue/lx77p7z/zDTVblvZVoUiFbXcB/3BQOcl2gAIrLO1V2a1UpmZXvFcAmjY8XCqNjBya5QuFgoWPvAcAIjYhaAbWIxzkkYcFVBQJUcucAwq7o8Ok/jwXs03jbKFwVk/W61nPdG1dNzzPhATm9eq2GSp4BSPucYjZ0KOe7mWTHl7FogGX1Dm3auPam1u/3ad3Hdy8SZpM8deKZZbefOOtB397mmk53FFOSkgQFHJYODV78gwRoVrA/pOz6aCSBwaL8sKhEr7hHDXN+wApOsZf8TJx2v3sgnBADsNSBoQmjfNsfPgu67p4kIfAYBNTqkErYuiCvRXLT+YKppnp6NJBQYNoe1L/homDfeWIP9Fn6EbItM1NT7z/xBNWvGEWzJBeMKO6GdAD5lJvwI55NMBu2S5Th4Zp9yCPq30zh/d9f1SLJKh58r0WS2U4a9wRrxByMULK9Ec0ilPh2MLs/Jf9zVSHBGg4BIUpLMIuDg6zDclrqDRcJCIggaHkbFYYEQiUYxIMKyBKiouPSgfGUz+CokWoELCgELmIKM7TwNT+uV2eaZieC4qoZpvSQzBCUbz1cslyIJHozSAMJIJCQgHHacRBYZimKSfj5eZGdTuWyfVls1mHlOsahjdKqnhl7a6v9+z7llQh/cmApum6d4DzFRue/WZ+OB3xa8gH1FosEOP0Va5TNTycgoSu6yZme/Hq+Ph4dQhnFYuhIrUlGNx43I1nk8ND0/Lt9PQ0/7Giwhs+opDStO/cuaVao1GrnV6o1xcODJfqtZmZxlJtrj5Xq9VmGo2lpUWWLwgJYiO/1OuR7uyujrN0vDkFxAKAREA3K8mKHQiM2oEov4UNWLjJ+JJr6lGj0GF3eIW401vrLdhWOZflpRriQEB3rr778qefTO7RC4WIFpLDYKibbpETplpHZh9ru7O/Ih2KRESLRAOWpUt1iqGCQ6UhxcGQBMUfiW8fr1Y5roSHwjKdUST2kNT2DA5mp6vT1XFAAAQd8bPKF/wmv04P+8bGx8Y4vlqsVsd+xsaqxe3Fse0HlE1NTR04UOQ9B5gGre5iL2nAtLu6DHRtceZhO+Df5A/oei5row0nE0r4ozQRhu3k+uyeQZqagO3Qo9IL2b4jfV1uKpNDtcCgVQ4FDH0Bd6bdPUbCCAXOm6675WJ1aGj29Jf9j92z5cFKSAsRLIGABmWzhHRKwxiwhgWFoWNmoWC7zpTUN6Wq5qvTac9VFLzBfYOD3+/nnxM0FI0Dg+JxCbshoVHiKB/eY8JgbOzncfkBl+3bi0UIAAIUWFHZVH1mqS8a2rQps3dvgO4BJ2fEMoRBCNeTSUERtUPRcMDAHerJMY1B04gaZtyz0ZWbcwb3erobd7NJJAIKyTLpoeJQyd1rnj2b0IivqB8LBcx0UU7z9HuP3vXc5ZeGQ8hOjGrwvKnStITM+HQVHEU4SCwZQsOLV4arVUQyTSHHj6ebJAbVtm9/kaIVq8oLBhIWpdLCQunQwn9cm7+r01AUx1X8hejmj0FERdHFRZzs4iCIf4AULA2VVIxoUHhZWhRKHVIS0mot/uokKBlEeokJjUZSm7aQtBgsVgWhLg4VOhTawUIX/Z5E8cf3vXvuTe7ra87nnnNuAi1Q/C+TfNd+qWuahKWqkTqzr/VcISPmJCVZTOHSuKMXMmJK4DgxuXY/12igKDSkUgObp4PfhqA0JUVymk2r1Wo1W2Rbrusx5j4i1TNJoKCIRu0RlgmxiIgSi2eKSJHKq9ni1es7Z09t2XZ84xmOI0ipVDEJrqM3WHasTpci3EclilEIQiOXa3cpyEmwPlBYllXA++MKCq5n/sYAWIiKl8QtPjObzUbreJ/3fdWHNDLUazyPBqv6PETT9798+RJ8+TKRQwOaB7Zt6ySbMYYxHclhGMyhIJAjYTay0awci/7Olg2jhScxXCOU73Rfvfx4JcddWBaT8JhDS4FF6cnWQwdv79m0d/uuw0dwLAJFIgX8mXx79MbEWvW7XeAwk+BAWzd6AOl8nXUR2VGi+x/6V6QrVKYkycFNoaOY3QgDTZrmrFSK4j4W/uM67Y94CPb3EbVIvqp+msYaR1qN4aMMLCFkGPCd+sAIApCYBzQKDRtODwaMyTbTbdf2bE9v2S3dZeF87ggXaLfPX69dqlCdyitCpiEmaTOCRNyP3l+/btuBW8+2n9txYsMD5EwKSiRSGF29PptpL/uaFrnLA4NIil6avPwSQRGBQNj4Prb1PMLSUizLkRynbVLGjyJFWU8uqqo6jLSOj6VGBjaWFh1CkQGK+6vJeBysQAAMpmOXsVAehJ5uGUxveS3XYIEXWrI315nuyUz2dCYzmw1sFuqDcAAmMqDoLibH02mgX1pD9VzL1msVysecheQqAgIaDKI9t3Pf/m2b1+89hg8jzpAzxCIBFkVRTPuQyfsfUdzULAoTGAJFknTZf6thgu/3ta+ldqUB/x1JsRqKgqRtFkyoGq86/O/1nv+tdWos34eJmUR9bGJh8HT8JyqgeYAAgEGyoAVxSNC5SHSS+ihmoFA2Al0nG1KmTKer6RwooGz9ynmkoZZX4M8ZYIBL2IK45VLYtm/96Yvbri3wZe0OVU3QAIkUrbzZx8WONBUrqiEvuCJCAuJIZlTo4THf42e1vOJAqFhNsLCaa1h7cv8fBA9JZNf98lpD91t/wgT44vmnwEAujFcr5MdEJ8cJBHp4C/8M3dBDuAv/DVd3ZaYjI1y35XnMdh3W9JZsyQRbtDmm/xj/mF8hFGvn67ULWX6oSk6yyKEqom5CKRTOzLvN+3Yv7t1+cG/x9RIHYSaRwgziZlYaQW0K8CoqBAGMSWDfqrbNapUum2BdkpRIkuAoUkOxWn8TiLthD4Idquo6qgnUiAba74GaRR/PqVB5MP4ymRgrQwaF6YBZtocaKLu6Fdq6xzzPYCGTm7pn6DKW3wYVW9apmA5QSHTD0AeooMxGjWX5u+Vvn79//1yur6XrVzLn+WoNZW0pFEXymHhcviTkSydPX5st7iweLB6k4SftH4kUWFwVz7erZr/arkLtzoUMfsQLZ8AIVeN8snPfrA4vD+Ec1j4jKdGzoCU42NwEx+rFDIbZXm/4PFs4Dwx8vzzqPa9qFaDAov8tNdKw93ioDv+o/GIyDibGNJTl94OVwaKyadP+QDkQDlzUUDoxQIggT4wAniNWotgZMBLQuU6r6TpeKtF5+rRU/n7zRzldzwu1SqWSlzJcRmpcwI1po6Dk6cao/un7kZI5Kn1dfC3G2ZFIJSDAAAK8BiQqo0ccRLsLRA/y6WxvSMscqZeXHIqJhiNYNGi20g9vIBfSzUx2rZYuFNLZCnKjx6s9U62qP5mymhW1oTA6z9JN3yHBbR5B8JIhQ4VJMAbBdOEkBdNNg5QIc4UwcZVCmF2VXqwDQmSUNLSB1uKAIHQ2s1DoatpFSwvFns9B2oPJNcSffOd+f/fco3/2f/4fF657cYgiIEFwALsvX7+8T79gK5imdzq0p/pw9vHjCxGiXo6H06vZ+zcv9gli9uJycnX1EUx8nPm4M0tnP8fpeAxOBvWz4WXjOlz9ePUnb521sieGbaN9wrzaWNOXyz1fr2loUdbfb27879u75wDRQKAionZBxnnX6KrImqij1IsAJkRRoxJfxGZ84XYhZ9qdCRqKEQbwAblplDZdy3Vjs9dokNKDJX9lLxyrpCKigmSUS/A6JJE9BRgINNJFlvz+9g3ZckMZcbYsJldFim5ppKXjdJrO0tHJNEXvNJ4Nb2+LAknjChf6GLdg+iQdopJcduxx2hmDrWk7KgR0h2t+d/+ayEBTbqPbrGMdf8K8ktW9oXXLhBqPKAz5vE69l3OgginN5W2yRLxuboMhfveEeGKMuIBJF3EMW2N6aAOeQO5AUYIT/nwEk+A0ZoYP4QAuHkDjkXqw+QC6xMmiMU+S1SpJkiWAk8jDMMjDvFkLeZgDQZKIXNApSYqgKPJgj7zgIhBCBDkXIhFBgvfL2yUiRlBPxlMhoMnW5n3/7v7Zj/AYlus2YoQ5pZKjTfDIE90e6JpmA4PWMZhg0j8q/AUavi+73e7rm6tvu3SfRxQAyk/FiAlulsVZVe8QD8QpodOBa0oOjIPjPLwYi904Y67rxBdZfBQrDObj286eI+JhT5jE8EnOAxjMOXggPkTAYSjnLSPkwSoABEwlbLeJWIlVkON2nkc8SAK6QYQEqz2bi/Visdwmq6AQRQqvaJzW5j21769+Pfsz/YnchxBhjlMfjQfHJAPLj0qWg9GRKifKf1TE8IoVZmjzGyF7lW6+TpEqTOVAhcpcmeYRhwN+9U5nAH/Q7Un7rIUFmdYwq2WofBQemWloKsQgAzyYjlk5styYvmoRSoBlgSS8JGRkhUeR3+d9HoIOX4i83YuiMAprauSHsJlHPETF5GEQioQ48kMfd6JIbwvomSGueejDx8HgAtiCCuJiWhQqBMfWdfe8dtq/u3+5KzoDxnS7Phh1jh3ZKsn0MJ5U6Z2SLlOmSnoAU3rn3OdBaDTDxXq3y8vmngqmAFVVsQ6QdJuyhT06a2tIST3avzrTT2u2buJXz5y4hn0I0ocRUWqslI/gB4QL2O9gPBDiWlIsudf+6kewxl7gKuBbziGwh80g5C0VE9/rt8u8HzRFJKKI++NW2F7zdSRqYc9vl7VW1M77or/sJ60E52CNhmSXRK3ucllMk8sy2uvefH7dCz12wxef3haMnTzXxwMWd03FcmU4Kau3IGxgMvrkFQfESkhKynrbyBrz8+pNc52sg7byALhFCYANj0qglhaDA5vWJzrtTmLJA5lMVUqxFJtSTBJhlTaZDJVwJMuOLFuYBRnHfnQBR3LJN+c3hpPFZkWpOJlT3fLGqWM6VROOW/Va2CZjqtRlagxVshqe8Yp86qiOgoeVHjXmzblX9QxPlZnMSuLL5vem2O02pzWsX6aXl1C5tBq2klmo/dSNDzfbT++EfTzSHdggw8fNRk1viq0fAf0mMeFIdACsn68A3pW92Gn68/Nu5bq5Xt7nzX0ZkR+YeFSqozajOGt1BUxotDkJVdpVkFpBgYFDVWlrDbrYgxB2JOHZLSZ5EuBQjFKcxXgXY7zbqm4mZYxlzJSMe14xPMWjfI0vIQCaUtWryoZUNdnJRvwwQEVVMj2mOI8aYln3mKdaJYJVv+212t6fXeuxVEyHkxcvUAM1iIG61JlIz52SNPe3T5/dqrDCAhEUpLVIrFf+A/BoDyAmWNNHTqLAm6vMPEdJNFW0Gdkc3vLLD37dL2Jybfk5FAxNP1Zky1MaBpTvBrYnmexJMfIjNiWhHJdHs7SjoZiSwPKXJut5cRqIwr16zXGOc3CunmfINX9CQ0OXFgtpSRMKkYRucugIYhDsYlOstnoosoK5eBBREF26YS0q6Lq7UFjYzWEPFTws6mkRRL+J+pK+6bz5lfe9N/MmkwoNvVj3ZCSjSEo1QbBaSQzIlbOcrXpWy+jybteqtzr7E7fH65Zar3nXeLadjC2b42MXgDbsdbqqWx3esaQEbLT+bEKkkBYtSYfrYZB3JztCVG2XJzt+o2rjy2mjeeuW3NjQdb6RTyfHn75N45gL1UD2Btv7/6CY8X8wRCLCspnnaTJLEL/Seivm1tbCsOZGZ5EcjXr379358u3S0IFrW7Vm41rTkRLwWurDM47EF34n0r1yIkDTqPZmF3vFD5vzWHAeV8IQ2odhSGG7Ocw751LQCBR7VE+TZJkO/Vnuj/LhdH/Zc3AelC827U5/NhxOBi+qeLd2rjXar16cTo7tgV994cqoZlvMGvZ1V8JRw5K4R6knBy/buqeeZHw4TIWQdnvcfFWVzapO4Waim/irX2+Pc4GRGdXpfLXKfVAbc7xpeYgpkaVeUbfTMwR1hJF0sefd38N+y8eisZjMEr9+Nlpcxd/j7f1h2rAjQ2LCqQU49gRCZ3drLNUaIIXicd2fpdvDNLeEHgu9op5MMoUTLYbYuXz/8OHmThyihdDlbJKk/Uk+GI2biDpJWu9Pccxgz9RyPEj88aDaxxfsTbdoT2c4cLGLeu26LKLrFrNnCANE0BIKGuq4pWi8dHWKK5Lj3SnBHqJrRY0N9+mra0bj+a22rMWjWTJZThEXWUiN/vLd6fpkvT75+fXnyXp/lSZLtVnxt5P0WZoiRKejrh63RsnJdH3q+MthwvTR+Xtvb7nMR3vAY/849QWQEKJlWBGcH/AjyyCAGF6x6Ey+IBSJGAuCrEg4tNt2bL/jWZHaPTljtx+1anGEiOZft8ZbySKt91wsgA7OMvtOTVrVVs2ybMdu8KDoCsIIIQUnRKNMMMoL+BX60inRiQcgdKETOGmJNmEMJud4DAWOLnXBpYtVvmi+GGy4m+Nezd/d7TeLAhBu/VB0VvLzH5fvgr79PPm9nh4m2OZ0oo1wfsUL4x7Pfekst/JpS4Th+VnSqqeTlO91sT3/nCc4oMYupzc9LKOCXsIBJAgAYjC3WvkgUlRhjOCG+1IVgIgOgm66EhH4ZEgJYYKEITTFRYQyNlqgD4R9nRB4PrKUQkS1siuvRWl0vRkppBlUUnZAKS7XNapZjYnC2XQJRd9Ow9iURb+/Ux18+Pi0XRWSN05PjSLDwMxIfvxIjro5zmAS6zyd9vJkVn184VzbvfXscNUTc8Ep80QhuSVlr1WTNBTh+RFeWbsAXc0MKVvqjGYrOdzur/uy3DXBTKCaPceex2+JvdFibkmPQvFKphQzmUKEQm0Kwn9kCBiyYIzpSARyqhx4dAyOBll0XeKQ35GUcY0ZhRQkKkRmWKK4qEnmOlUuGQmJ6hbVgyxAV1kmsiwqCiGYDNgF1SBtPn6S8d2d50VmEib7F6q2ZKhwMWICEhArAtUDQ/uDBw9fzxK7yLJMZoVgkBVFJgV8T0pRMJ0hUaaHQaGLkHuemuzRzOkg+gGOkKKYwlllPN+6fXRbIYQfqTCimQGwIKapmVQDKUdnSm9N+QKBDM5AIEIdJLCcw0xgY9YgJbzHFDqsvIkZlE4WBAR9oCF0khCRvy1RgkSVlxz9KSvcCIIbByYGBMzoNngYEBCaq7oljrjwz1TWIEF28OjhQSBNkgVqKLSA/gKpIoYIQMGpXpbBI5GBTPkDJmqZUuWmqohiqh7xEC2oh19F067cCG6QTDNNZiKjgcCvgMBMTSVQG0KGMhO3WQqJCRC1wERe/a5oqg5uMGJqjKhng54KYiL+OlzZCzXLEqJSCkaCg4emmWkaxBQjUKpQUHONliLkSwbLEJB5I3hy8DAI/oMKnWAoUdoOOuECR3UYupSW8EFYQoViEIABV1AoZFSrf1D8IbKMjVuJYSCqGi68kAnSyz2/Dqa/CVzsGbOMK8DoQU1cpohq42aU+C0gj1ciOCRAmrsEaMHEh44/QLLpUkHT5ngGvPHiFktxFhSQxJEFaA0+jidXQXvy1bhuuS07oZoi0WRJQkx3rmGYWK/bvi/LbsE0CcL69z+gTU+Vlj2358tfPowsQ2HMSqAYv2ta8VXjMGcSa1XZfEqutssKH6mJ/rJ+yty2XywDvnWvksXdsS8gaYphRtEI0lQtW+XhmJod4b2cf9j2rDl0Uvg6ADo2RbEqMyQ35tPooEJGOBLKpyj0oaXszwKhWor4qobGCvbIcuz1Mr3LlaeaytFdfFKoTT+tdjZsHwtl/49Xla6STsV8g7EXgm+cQefYOD1eQh8ok6aoV3vK5Kl06DSlH50GJVTxEFKpsmrraJlPOP9Ce2e8N05rlXncWAaxR6n6fKO2tKrOROVmzIjV5+Vm8yrM3PkKlfBiobRtMS/c/y+4smpGpqvd7uD7W/Z4HOfjcR4HE/eEo4SfjIgg5rjLx+A4zkA51cyQlHExoAMSlDbdpJ+p+nBVKgipJAFSHBatOJS+lZHdVKkboWhK3Mhnh3LLrCi43qpgzzWJTvtNbKRgWXSbcQEIWzouDJsmYi4x+/z68pLQMMJPY2Vz4zgMQ+E0nQpoVkEBBAgY8CItqAmd7NPkkCYGsA/7kVpjgTnNE0U+8UdSqMzk0bIJCB9vHHnrFttmqioqIcf5DFnNVmDmKIHfkFTbuhVWZGLrW4VQYlK4I4AClRxCfHaZzDuIKdaTTkLG/RTj3/diiOrrd6jr9G+a/zXmO32vueMqshUas/AQM5XQ8CYS4zwby5e7q6iJtSXC3cwCJdLcRfpKJMLWcPEwWZuRFAWBJTUjByi7sY1jxCF4NbmlQOmtOhYurvYSJYxSgTLyAwvzulDgmF8KIhZtvuEGfO3Z+aqsmhsqnDtASynyZ3mCUjfB277GoyV6i+7Y5X2OHm0w+GMZfbRltI6kg0lsphcGq2xCDy+EAVXjlrVwI9MjUC3SScR6JmaVIw1tau57Zou81CT9FlHGVbKI65XHfLzfy7lQxn3xmBh5oDRTqkqMnaqTs7XusloI1I1GVbOUDCKVUq8jj/qheDJSzve5gLM0ZxIcnNu8ru6Ns+CIme4cuuu+X9fn+mCZ7LqrwlA4PlNdOwkf3KyKU4EfgygJcyiKmUFWqCS1pGh62UQXpSSJWp4CEVNXeFbhNTHhf95yPI//GD85+jY8fJLxF1+qDYkD6zAaAAAAAElFTkSuQmCC\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Nitrogen dioxide\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"public\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"µg/m³\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "nitrogen", + "nitrogen dioxide" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_noise_level_chart_card.json b/application/src/main/data/json/system/widget_types/simple_noise_level_chart_card.json new file mode 100644 index 0000000000..7679986f24 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_noise_level_chart_card.json @@ -0,0 +1,38 @@ +{ + "fqn": "simple_noise_level_chart_card", + "name": "Simple noise level chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAhFBMVEUAAADf39/g4ODf39/f39/g4OD////k5OSAwyzg4OAhISE8PDyQkJDHx8fy8vJ0dHSv2Xvv9+UvLy9YWFienp6Qy0asrKyg0mCIxzm6urqrq6v3+/JmZmaCgoLV1dXP6bDf8MrH5aPA4ZaYzlO43YhLS0vn9NhKSkrX7L2w2Xuo1m729vZb/Cn5AAAABnRSTlMAIL9AEN/GQiaNAAAEq0lEQVR42uzPwQ2AIBAAsBMQcx8WYf/9TJzAJ5B2gwYAAAAAAMB/7dpc+xq15LO5vGvElbNvb2SNMvoBZonsR0iRxYisRuRlt25WbQdhMAzPvkBiIqgUFOusrPu/w2Olm7N/OtzsZcGXSpN29ExKZ2tBZmtBZmtBZmtBZusXIUa4iwx3vQuSvQHNE65qwvc2xl3icNffQVz+snEFlPX/28dA2H+FsGJA1CUCUgbymCy57YJci7nWD4GSo3dDyL5DZBc7IS54EYN4OHFjetWQPiCHeNkRHDIThVhZ3wshdj8gFlyHGBf06YTUCnO0MXXcBcl92YLtEX5ciH4+CAon1m4BYj0hTUJtcCwS+IKcizApmxQcQSTEKSASP0MQAytx7oM/IWTqublAvQuSuI0lVCYclXozQqhD8Dpa4XJCZEdmVXak7oJY2McyvnEuZNrLbJB0jkkU9OKQgLijyZhKvxX0spxnPFMpAPbAkRAT7vsLiG0NmyJnPKb1rzVbCzJbCzJbCzJbCzJbCzJbCzJbE0Isb9qeD9HIZ7I9G2KRD5dJS2ShB0NMOF1jEaHnQjwrPqJwPBbyjz2zW04WBsLw2WYnIcHwI6loRaFWvf8L/Db5JAyFItYplRmeAwzGcfLM7pvI+M4DaJA8mKtIKFq3caTmKcJ42LqXfD1PkZAzaBGLuYiodbJuFi98QZqSzEMkiDixvakkXMIXYvFqIuotSWS3lUKp3usDY2MT0i3Ja4kkESdE0n7ztr0yweNgF/NYQYdYqGdFdA6eXHemV9nxkRTHUim5bR0MTGzrUfLJebyBHiTfPiWiK4OYHv+7HFJEc/nikuIKoEotZba/EwUe1IMEPGHExtSSfzQfU+xBEZ2SBqlkQBxJg8Zl3hVZocNP9qNE2BxxULPmyciujMJ3+y0ytP0p6Ga8yAXTwhmcSQrxmsMe8dgrorUurn6ylzWX3fAyIWAcLOQ8EoIuYbIOthG16WgRpFUSBiuwCprGGWZQcypyLwIWg1doGNpD41s/qS1nMBa22YVhIMGhNoLHb+PD7kUqLF1Q0NQaKVIFWiJpY9n/75eHiShhoOQHT+DHkIp4e0DkjHj2pXCFsWiblxLRi7juOwx1Fms3ikVIIJ5Q+RwrcjiubOd3RS6IJ5quRVaEGSoIiBhasCQIJDzNWBHbP6XuiLiUd3Ytsx96xthAH1OJnPcUg1R3RHLXRo1IURRnejkM/qSdgKGMaBf2a1tEI+6BaIU9Q/Nt1EUIfUwpQutMbxcXCvhW5GAt+xnYZn9dxC+WusotMncZL/2e3BG52M/0sht47p6gIsbl/IS4cqGoyM0f3hUa7fQakcKQZR/yY8BjChFapslKdGGHCjHLjB37cyTz58gNc4Iuajd0XEwiAvsUiUyD5ejHzcleXb2Ile7zkILvFAwyRdhPReHbPi+KVpbtVK5zoAtBgz8vhxd5jr8vx6+J7Pjn3XLMQySAu8xC5D6LyCIyFxaRV2MReTUWkVdjEXk1FpF/7dwxDcAwDABB11YjZSmR8ucXChkT647BE/jTCDlNo5BssA4a48uo2WAe9M8not77t1RZnUZhAAAAAAAAWxauHfe/D+cw4gAAAABJRU5ErkJggg==", + "description": "Displays historical noise level values as a simplified chart. Optionally may display the corresponding latest noise level value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'noise', label: 'Noise level', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'noise', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Noise level\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value;\\nif (!prevValue) {\\n value = Math.random() * 120;\\n} else {\\n value = prevValue + Math.random() * 40 - 20;\\n}\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value;\\nif (!prevValue) {\\n value = Math.random() * 120;\\n} else {\\n value = prevValue + Math.random() * 40 - 20;\\n}\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":50,\"color\":\"#80C32C\"},{\"from\":50,\"to\":70,\"color\":\"#FFA600\"},{\"from\":70,\"to\":90,\"color\":\"#F36900\"},{\"from\":90,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Noise level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"bar_chart\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"dB\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "sound level", + "acoustic level", + "decibel level", + "volume", + "loudness", + "ambient noise", + "sound intensity", + "acoustic intensity" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_noise_level_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_noise_level_chart_card_with_background.json new file mode 100644 index 0000000000..f13f2ec236 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_noise_level_chart_card_with_background.json @@ -0,0 +1,38 @@ +{ + "fqn": "simple_noise_level_chart_card_with_background", + "name": "Simple noise level chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical noise level values as a simplified chart with background. Optionally may display the corresponding latest noise level value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'noise', label: 'Noise level', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'noise', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Noise level\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value;\\nif (!prevValue) {\\n value = Math.random() * 120;\\n} else {\\n value = prevValue + Math.random() * 40 - 20;\\n}\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value;\\nif (!prevValue) {\\n value = Math.random() * 120;\\n} else {\\n value = prevValue + Math.random() * 40 - 20;\\n}\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":50,\"color\":\"#7CC322\"},{\"from\":50,\"to\":70,\"color\":\"#F89E0D\"},{\"from\":70,\"to\":90,\"color\":\"#F77410\"},{\"from\":90,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Noise level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"bar_chart\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"dB\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "sound level", + "acoustic level", + "decibel level", + "volume", + "loudness", + "ambient noise", + "sound intensity", + "acoustic intensity" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_ozone__o3__chart_card.json b/application/src/main/data/json/system/widget_types/simple_ozone__o3__chart_card.json new file mode 100644 index 0000000000..9872bb6ffb --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_ozone__o3__chart_card.json @@ -0,0 +1,40 @@ +{ + "fqn": "simple_ozone_o3_chart_card", + "name": "Simple ozone (O3) chart card", + "deprecated": false, + "image": "tb-image:c2ltcGxlLW96b25lLXZhbHVlLWFuZC1jaGFydC1jYXJkLnN2Zw==:c2ltcGxlLW96b25lLXZhbHVlLWFuZC1jaGFydC1jYXJkLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjEwOCIgeT0iMjYiIGZpbGw9IiNmZmYiIHJ4PSI0Ii8+PHJlY3Qgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxMDciIHg9Ii41IiB5PSIyNi41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMTIiIHJ4PSIzLjUiLz48ZyBmaWx0ZXI9InVybCgjYSkiPjxyZWN0IHdpZHRoPSIxODIiIGhlaWdodD0iOTAiIHg9IjkiIHk9IjM1IiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii43NiIgcng9IjMuMDA4Ii8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjg3IiBkPSJNMTkgNDAuMTY3QTUuODM2IDUuODM2IDAgMCAwIDEzLjE2NyA0NiA1LjgzNSA1LjgzNSAwIDAgMCAxOSA1MS44MzMgNS44MzUgNS44MzUgMCAwIDAgMjQuODMzIDQ2IDUuODM1IDUuODM1IDAgMCAwIDE5IDQwLjE2N1pNMTQuMzMzIDQ2YzAtLjM1Ni4wNDctLjcwNi4xMjMtMS4wMzhsMi43ODggMi43ODh2LjU4M2ExLjE3IDEuMTcgMCAwIDAgMS4xNjcgMS4xNjd2MS4xMjZBNC42NzMgNC42NzMgMCAwIDEgMTQuMzMzIDQ2Wm04LjEwMyAzLjE1YTEuMTYyIDEuMTYyIDAgMCAwLTEuMTA4LS44MTdoLS41ODR2LTEuNzVhLjU4NS41ODUgMCAwIDAtLjU4My0uNTgzaC0zLjV2LTEuMTY3aDEuMTY3Yy4zMiAwIC41ODMtLjI2Mi41ODMtLjU4M3YtMS4xNjdoMS4xNjdhMS4xNyAxLjE3IDAgMCAwIDEuMTY2LTEuMTY2di0uMjRBNC42NjggNC42NjggMCAwIDEgMjMuNjY3IDQ2YTQuNjYyIDQuNjYyIDAgMCAxLTEuMjMxIDMuMTVaTTQwLjk2MyA0NS43NXYuNTQ3YzAgLjc1Mi0uMDk4IDEuNDI2LS4yOTQgMi4wMjNhNC4zNjQgNC4zNjQgMCAwIDEtLjg0IDEuNTI1Yy0uMzYuNDE5LS43OTMuNzQtMS4zLjk2NGE0LjE5NCA0LjE5NCAwIDAgMS0xLjY4LjMyOGMtLjYxMSAwLTEuMTctLjExLTEuNjc2LS4zMjhhMy43NiAzLjc2IDAgMCAxLTEuMzA1LS45NjQgNC40NzUgNC40NzUgMCAwIDEtLjg2Mi0xLjUyNWMtLjItLjU5Ny0uMy0xLjI3MS0uMy0yLjAyM3YtLjU0N2MwLS43NTIuMS0xLjQyNC4zLTIuMDE3LjItLjU5Ny40ODMtMS4xMDUuODQ4LTEuNTI0LjM3LS40MjQuODA0LS43NDUgMS4zMDYtLjk2NGE0LjEwMiA0LjEwMiAwIDAgMSAxLjY3NS0uMzM1Yy42MTUgMCAxLjE3NS4xMTIgMS42ODEuMzM1LjUwNi4yMTkuOTQxLjU0IDEuMzA2Ljk2NC4zNjQuNDIuNjQ1LjkyNy44NCAxLjUyNC4yMDEuNTkzLjMwMSAxLjI2NS4zMDEgMi4wMTdabS0xLjcxNS41NDd2LS41NmE1LjkxIDUuOTEgMCAwIDAtLjE2NC0xLjQ3Yy0uMTA1LS40MjktLjI2My0uNzg3LS40NzItMS4wNzRhMS45NzIgMS45NzIgMCAwIDAtLjc1OS0uNjU2Yy0uMy0uMTUtLjY0LS4yMjUtMS4wMTktLjIyNS0uMzc4IDAtLjcxNS4wNzUtMS4wMTEuMjI1LS4yOTcuMTQ2LS41NS4zNjUtLjc1OS42NTZhMy4xODcgMy4xODcgMCAwIDAtLjQ3MiAxLjA3NCA1LjkxIDUuOTEgMCAwIDAtLjE2NCAxLjQ3di41NmMwIC41NTYuMDU1IDEuMDQ4LjE2NCAxLjQ3Ni4xMS40MjkuMjcuNzkxLjQ3OSAxLjA4Ny4yMTQuMjkyLjQ3LjUxMy43NjUuNjYzYTIuMjYgMi4yNiAwIDAgMCAxLjAxMi4yMmMuMzgzIDAgLjcyMy0uMDc0IDEuMDE5LS4yMi4yOTYtLjE1LjU0Ny0uMzcxLjc1Mi0uNjYzLjIwNS0uMjk2LjM2LS42NTguNDY1LTEuMDg3LjEwOS0uNDI4LjE2NC0uOTIuMTY0LTEuNDc2Wm05LjIzOSAzLjM5VjUxaC01LjUxdi0xLjMxM2g1LjUxWm0tLjE1LTUuMDU4TDQzLjUzNiA1MWgtMS4wNjZ2LTEuMDZsNC43NzItNi4zMzdoMS4wOTN2MS4wMjZabS0uNjM2LTEuMDI2djEuMzEzaC01LjE0OHYtMS4zMTNoNS4xNDhabTEuOTMxIDMuNzh2LS4xNTZjMC0uNTM0LjA3OC0xLjAyOC4yMzMtMS40ODQuMTU1LS40Ni4zNzgtLjg1OS42Ny0xLjE5NmEzLjAyNCAzLjAyNCAwIDAgMSAxLjA4LS43OTMgMy41MTYgMy41MTYgMCAwIDEgMS40NDktLjI4N2MuNTQyIDAgMS4wMjUuMDk2IDEuNDUuMjg3LjQyOC4xODcuNzkuNDUxIDEuMDg2Ljc5My4yOTYuMzM3LjUyMi43MzYuNjc3IDEuMTk2LjE1NS40NTYuMjMyLjk1LjIzMiAxLjQ4NHYuMTU3YzAgLjUzMy0uMDc3IDEuMDI3LS4yMzIgMS40ODMtLjE1NS40NTYtLjM4Ljg1NS0uNjc3IDEuMTk3YTMuMTM2IDMuMTM2IDAgMCAxLTEuMDguNzkyIDMuNTQgMy41NCAwIDAgMS0xLjQ0Mi4yOCAzLjYwNyAzLjYwNyAwIDAgMS0xLjQ1Ni0uMjggMy4xMzYgMy4xMzYgMCAwIDEtMS4wOC0uNzkzIDMuNTUxIDMuNTUxIDAgMCAxLS42NzctMS4xOTYgNC41ODIgNC41ODIgMCAwIDEtLjIzMy0xLjQ4M1ptMS42NDgtLjE1NnYuMTU3YzAgLjMzMy4wMzQuNjQ3LjEwMi45NDNhMi40IDIuNCAwIDAgMCAuMzIyLjc4Yy4xNDUuMjIzLjMzMi4zOTguNTYuNTI2LjIyOC4xMjcuNS4xOTEuODE0LjE5MS4zMDUgMCAuNTctLjA2NC43OTMtLjE5MS4yMjctLjEyOC40MTQtLjMwMy41Ni0uNTI3LjE0Ni0uMjIzLjI1My0uNDgzLjMyMS0uNzc5LjA3My0uMjk2LjExLS42MS4xMS0uOTQzdi0uMTU3YzAtLjMyOS0uMDM3LS42MzgtLjExLS45M2EyLjM4NyAyLjM4NyAwIDAgMC0uMzI4LS43ODYgMS41NyAxLjU3IDAgMCAwLS41Ni0uNTM0IDEuNTQzIDEuNTQzIDAgMCAwLS44LS4xOThjLS4zMSAwLS41NzkuMDY2LS44MDcuMTk4YTEuNTg3IDEuNTg3IDAgMCAwLS41NTMuNTM0Yy0uMTQ2LjIyOC0uMjUzLjQ5LS4zMjIuNzg2YTQuMDY3IDQuMDY3IDAgMCAwLS4xMDIuOTNabTguNDk0LTIuMDQ0VjUxaC0xLjY0OHYtNy4zOTdoMS41NTJsLjA5NiAxLjU4Wm0tLjI5NCAxLjg0NS0uNTMzLS4wMDZhNC44MTIgNC44MTIgMCAwIDEgLjIxOC0xLjQ0M2MuMTQ2LS40MzcuMzQ3LS44MTMuNjAyLTEuMTI4LjI2LS4zMTQuNTctLjU1Ni45My0uNzI0LjM2LS4xNzQuNzYtLjI2IDEuMjAzLS4yNi4zNTUgMCAuNjc3LjA1Ljk2NC4xNS4yOTEuMDk2LjU0LjI1My43NDUuNDcyLjIxLjIxOS4zNjkuNTAzLjQ3OC44NTQuMTEuMzQ3LjE2NC43NzMuMTY0IDEuMjc5VjUxaC0xLjY1NHYtNC43ODVjMC0uMzU2LS4wNTItLjYzNi0uMTU3LS44NDFhLjg4Ljg4IDAgMCAwLS40NDQtLjQ0NGMtLjE5Mi0uMDkxLS40MzEtLjEzNy0uNzE4LS4xMzctLjI4MyAwLS41MzYuMDYtLjc1OS4xNzgtLjIyMy4xMTgtLjQxMi4yOC0uNTY3LjQ4NS0uMTUuMjA1LS4yNjcuNDQyLS4zNDkuNzExYTIuOTM4IDIuOTM4IDAgMCAwLS4xMjMuODYxWm05Ljk2NCA0LjEwOWEzLjk0NCAzLjk0NCAwIDAgMS0xLjQ4NC0uMjY3IDMuMjc0IDMuMjc0IDAgMCAxLTEuMTItLjc1OSAzLjM2MSAzLjM2MSAwIDAgMS0uNzA1LTEuMTQxIDMuOTk3IDMuOTk3IDAgMCAxLS4yNDYtMS40MTV2LS4yNzRjMC0uNTc4LjA4NS0xLjEwMy4yNTMtMS41NzIuMTY5LS40Ny40MDMtLjg3LjcwNC0xLjIwMy4zMDEtLjMzNy42NTYtLjU5NSAxLjA2Ny0uNzczLjQxLS4xNzcuODU0LS4yNjYgMS4zMzMtLjI2Ni41MjggMCAuOTkuMDg5IDEuMzg3LjI2Ni4zOTcuMTc4LjcyNS40MjkuOTg1Ljc1Mi4yNjQuMzIuNDYuNy41ODggMS4xNDIuMTMyLjQ0Mi4xOTguOTMuMTk4IDEuNDYzdi43MDRoLTUuNzE1VjQ2LjYxaDQuMDg4di0uMTNhMi4zMTQgMi4zMTQgMCAwIDAtLjE3OC0uODM0IDEuNDE3IDEuNDE3IDAgMCAwLS40ODUtLjYyOGMtLjIxOS0uMTYtLjUxLS4yNC0uODc1LS4yNC0uMjc0IDAtLjUxNy4wNi0uNzMyLjE3OC0uMjEuMTE0LS4zODUuMjgtLjUyNi41LS4xNDEuMjE4LS4yNS40ODItLjMyOC43OTItLjA3My4zMDUtLjExLjY1LS4xMSAxLjAzMnYuMjc0YzAgLjMyMy4wNDQuNjI0LjEzLjkwMi4wOTEuMjczLjIyNC41MTMuMzk3LjcxOC4xNzMuMjA1LjM4My4zNjcuNjI5LjQ4NS4yNDYuMTE0LjUyNi4xNzEuODQuMTcxLjM5NyAwIC43NS0uMDggMS4wNi0uMjQuMzEtLjE1OS41NzktLjM4NC44MDctLjY3NmwuODY4Ljg0Yy0uMTYuMjMzLS4zNjcuNDU3LS42MjIuNjctLjI1NS4yMS0uNTY3LjM4MS0uOTM3LjUxNGEzLjcyNyAzLjcyNyAwIDAgMS0xLjI3MS4xOThaIi8+PHBhdGggZmlsbD0iI0Y4OUUwRCIgZD0iTTIxIDk2Ljg3OVYxMTRoLTIuODI0di0xMy43N2wtNC4xODQgMS40MTh2LTIuMzMybDYuNjY4LTIuNDM3SDIxWm0xMy42NCAwVjExNGgtMi44MjR2LTEzLjc3bC00LjE4MyAxLjQxOHYtMi4zMzJMMzQuMyA5Ni44OGguMzRabTE3LjA1MSA3LjE2djIuNzg5YzAgMS4zMzYtLjEzMiAyLjQ3Ny0uMzk4IDMuNDIyLS4yNTguOTM4LS42MzMgMS42OTktMS4xMjUgMi4yODVhNC40MSA0LjQxIDAgMCAxLTEuNzcgMS4yODljLS42OC4yNzQtMS40NDEuNDEtMi4yODUuNDFhNi41ODQgNi41ODQgMCAwIDEtMS44NzUtLjI1NyA0LjU4NyA0LjU4NyAwIDAgMS0xLjU0Ny0uODA5IDQuODM1IDQuODM1IDAgMCAxLTEuMTgzLTEuNDE4Yy0uMzItLjU4Ni0uNTctMS4yODUtLjc1LTIuMDk4LS4xNzItLjgxMi0uMjU4LTEuNzU0LS4yNTgtMi44MjR2LTIuNzg5YzAtMS4zNDQuMTMzLTIuNDc3LjM5OC0zLjM5OC4yNjYtLjkzLjY0NS0xLjY4NCAxLjEzNy0yLjI2MmE0LjMzIDQuMzMgMCAwIDEgMS43NTgtMS4yNzdjLjY4OC0uMjY2IDEuNDUzLS4zOTkgMi4yOTctLjM5OS42OCAwIDEuMzA0LjA4NiAxLjg3NS4yNThhNC4zOSA0LjM5IDAgMCAxIDEuNTQ3Ljc4NWMuNDYuMzYuODUxLjgyOCAxLjE3MiAxLjQwNi4zMjguNTcuNTc4IDEuMjYyLjc1IDIuMDc1LjE3Mi44MDQuMjU3IDEuNzQyLjI1NyAyLjgxMlptLTIuODI0IDMuMTg4di0zLjYxYzAtLjY3OS0uMDM5LTEuMjc3LS4xMTctMS43OTMtLjA3OC0uNTIzLS4xOTUtLjk2NS0uMzUyLTEuMzI0YTIuNjQgMi42NCAwIDAgMC0uNTYyLS44OSAxLjk3OCAxLjk3OCAwIDAgMC0uNzczLS41MDQgMi43MzEgMi43MzEgMCAwIDAtLjk3My0uMTY1Yy0uNDM4IDAtLjgyOC4wODYtMS4xNzIuMjU4LS4zNDQuMTY0LS42MzMuNDMtLjg2Ny43OTctLjIzNS4zNjctLjQxNC44NTItLjU0IDEuNDUzLS4xMTcuNTk0LS4xNzUgMS4zMTctLjE3NSAyLjE2OHYzLjYxYzAgLjY4Ny4wMzkgMS4yOTMuMTE3IDEuODE2LjA3OC41MjMuMTk1Ljk3My4zNTIgMS4zNDguMTU2LjM2Ny4zNDMuNjcxLjU2Mi45MTQuMjI3LjIzNC40ODUuNDA2Ljc3NC41MTUuMjk3LjExLjYyLjE2NC45NzIuMTY0LjQ0NiAwIC44NC0uMDg2IDEuMTg0LS4yNTcuMzQ0LS4xNzIuNjMzLS40NDYuODY3LS44MjEuMjM0LS4zODMuNDEtLjg3OS41MjctMS40ODguMTE4LS42MDkuMTc2LTEuMzQuMTc2LTIuMTkxWk02NS41OTQgMTAxLjMyaDIuODM1VjExNGgtMi42MTNsLS4yMjItMi45M3YtOS43NVptLjQxIDYuNzg1IDEuMTEzLS4wMjNjMCAuODkxLS4wODYgMS43MTEtLjI1OCAyLjQ2MS0uMTY0Ljc1LS40MjIgMS40MDItLjc3MyAxLjk1N2EzLjcgMy43IDAgMCAxLTEuMzEzIDEuMjg5Yy0uNTMxLjMwNS0xLjE2LjQ1Ny0xLjg4Ni40NTdhNC42MjkgNC42MjkgMCAwIDEtMS41Ni0uMjQ2IDMuMSAzLjEgMCAwIDEtMS4xOTQtLjc5N2MtLjMzNi0uMzU5LS42MTQtLjgyOC0uODMzLTEuNDA2bC0uMzYzLTMuMTk5aDEuNjA2YzAgLjcwMy4wNTggMS4yNzcuMTc1IDEuNzIyLjExOC40NDYuMjgyLjc4OS40OTMgMS4wMzIuMjEuMjM0LjQ1Ny4zOTguNzM4LjQ5Mi4yODkuMDk0LjYwMi4xNC45MzguMTQuNjE3IDAgMS4xMjQtLjA5MyAxLjUyMy0uMjgxLjQwNi0uMTk1LjcyMy0uNDY1Ljk1LS44MDguMjM0LS4zNTIuMzk4LS43NjIuNDkxLTEuMjMxLjEwMi0uNDc2LjE1My0uOTk2LjE1My0xLjU1OVptLTguMjc0LTYuNzg1aDIuODEzdjE3LjU1NUg1Ny43M1YxMDEuMzJabTIyLjEyNSAwaDIuNTY3djEyLjMyOGMwIDEuMTQxLS4yNDMgMi4xMS0uNzI3IDIuOTA3YTQuNjY4IDQuNjY4IDAgMCAxLTIuMDI3IDEuODE2Yy0uODY4LjQyMi0xLjg3MS42MzMtMy4wMTIuNjMzLS40ODQgMC0xLjAyMy0uMDctMS42MTctLjIxMWE2LjYyNCA2LjYyNCAwIDAgMS0xLjcxMS0uNjggNC4yMzQgNC4yMzQgMCAwIDEtMS4zNzEtMS4yMDdsMS4zMjQtMS42NjRjLjQ1My41MzkuOTUzLjkzNCAxLjUgMS4xODQuNTQ3LjI1IDEuMTIxLjM3NSAxLjcyMy4zNzUuNjQ4IDAgMS4xOTktLjEyMSAxLjY1Mi0uMzYzLjQ2LS4yMzUuODE2LS41ODMgMS4wNjYtMS4wNDMuMjUtLjQ2MS4zNzUtMS4wMjQuMzc1LTEuNjg4di05LjUxNmwuMjU4LTIuODcxWm0tOC42MTMgNi40ODF2LS4yNDZjMC0uOTYxLjExNy0xLjgzNi4zNTItMi42MjUuMjM0LS43OTcuNTctMS40ODEgMS4wMDctMi4wNTFhNC4zOSA0LjM5IDAgMCAxIDEuNTk0LTEuMzI0Yy42MjUtLjMxMyAxLjMzMi0uNDY5IDIuMTIxLS40NjkuODIgMCAxLjUyLjE0OCAyLjA5OC40NDVhMy45NjMgMy45NjMgMCAwIDEgMS40NjUgMS4yNzhjLjM5LjU0Ni42OTUgMS4yMDMuOTE0IDEuOTY4LjIyNi43NTguMzk0IDEuNjAyLjUwNCAyLjUzMnYuNzg1YTEyLjY3NCAxMi42NzQgMCAwIDEtLjUxNiAyLjQ4NGMtLjI0Mi43NS0uNTYzIDEuMzk5LS45NiAxLjk0NS0uNC41NDctLjg5Mi45NjktMS40NzcgMS4yNjYtLjU3OS4yOTctMS4yNjIuNDQ1LTIuMDUxLjQ0NS0uNzc0IDAtMS40NzMtLjE2LTIuMDk4LS40OGE0Ljc3MyA0Ljc3MyAwIDAgMS0xLjU5NC0xLjM0OCA2LjM4NCA2LjM4NCAwIDAgMS0xLjAwNy0yLjAzOSA4Ljk3MyA4Ljk3MyAwIDAgMS0uMzUyLTIuNTY2Wm0yLjgyNC0uMjQ2di4yNDZjMCAuNTc4LjA1NSAxLjExNy4xNjQgMS42MTcuMTE3LjUuMjkzLjk0MS41MjggMS4zMjQuMjQyLjM3NS41NDYuNjcyLjkxNC44OTEuMzc1LjIxMS44MTYuMzE2IDEuMzI0LjMxNi42NjQgMCAxLjIwNy0uMTQgMS42MjktLjQyMi40My0uMjgxLjc1OC0uNjYuOTg0LTEuMTM2YTUuNzU5IDUuNzU5IDAgMCAwIC40OTItMS42MTh2LTIuMTIxYTUuMTgyIDUuMTgyIDAgMCAwLS4yOTMtMS4yODkgMy40NTUgMy40NTUgMCAwIDAtLjU3NC0xLjA0MyAyLjQwMyAyLjQwMyAwIDAgMC0uOTE0LS43MDNjLS4zNjctLjE3Mi0uOC0uMjU4LTEuMy0uMjU4LS41MDggMC0uOTUuMTEtMS4zMjUuMzI5YTIuNjggMi42OCAwIDAgMC0uOTI2LjkwMmMtLjIzNC4zODMtLjQxLjgyOC0uNTI3IDEuMzM2YTcuMjM5IDcuMjM5IDAgMCAwLS4xNzYgMS42MjlaTTkyLjc3IDk2LjkzN2wtNi42NDQgMTguNTI4SDgzLjkxbDYuNjU2LTE4LjUyOGgyLjIwM1ptNC43ODEgNi45NjFWMTE0aC0yLjgyNHYtMTIuNjhoMi42NmwuMTY1IDIuNTc4Wm0tLjQ1NiAzLjI5My0uOTYxLS4wMTFjMC0uODc1LjEwOS0xLjY4NC4zMjgtMi40MjYuMjE4LS43NDIuNTM5LTEuMzg3Ljk2LTEuOTM0YTQuMjMgNC4yMyAwIDAgMSAxLjU3LTEuMjc3Yy42MzQtLjMwNSAxLjM2NC0uNDU3IDIuMTkyLS40NTdhNC42MyA0LjYzIDAgMCAxIDEuNTgyLjI1OCAzLjEzIDMuMTMgMCAwIDEgMS4yNTQuNzg1Yy4zNi4zNTkuNjMzLjgyLjgyMSAxLjM4My4xOTUuNTYyLjI5MyAxLjI0Mi4yOTMgMi4wMzlWMTE0aC0yLjgyNXYtOC4yMDNjMC0uNjE3LS4wOTMtMS4xMDItLjI4MS0xLjQ1M2ExLjU1NSAxLjU1NSAwIDAgMC0uNzg1LS43NWMtLjMzNi0uMTU2LS43MzgtLjIzNS0xLjIwNy0uMjM1LS41MzEgMC0uOTg0LjEwMi0xLjM2LjMwNWEyLjU2MyAyLjU2MyAwIDAgMC0uOTAyLjgzMiAzLjczIDMuNzMgMCAwIDAtLjUxNSAxLjIxOWMtLjExLjQ2MS0uMTY0Ljk1My0uMTY0IDEuNDc2Wm03Ljg2My0uNzUtMS4zMjQuMjkzYzAtLjc2NS4xMDUtMS40ODguMzE2LTIuMTY4YTUuNTI2IDUuNTI2IDAgMCAxIC45NDktMS44MDQgNC40NTUgNC40NTUgMCAwIDEgMS41NTktMS4yMzFjLjYxNy0uMjk3IDEuMzI0LS40NDUgMi4xMjEtLjQ0NS42NDggMCAxLjIyNi4wOSAxLjczNC4yNjkuNTE2LjE3Mi45NTMuNDQ2IDEuMzEzLjgyMS4zNTkuMzc1LjYzMy44NjMuODIgMS40NjUuMTg4LjU5My4yODEgMS4zMTIuMjgxIDIuMTU2VjExNGgtMi44MzZ2LTguMjE1YzAtLjY0LS4wOTMtMS4xMzctLjI4MS0xLjQ4OC0uMTgtLjM1Mi0uNDM3LS41OTQtLjc3My0uNzI3LS4zMzYtLjE0LS43MzktLjIxMS0xLjIwNy0uMjExLS40MzggMC0uODI1LjA4Mi0xLjE2LjI0NmEyLjMxIDIuMzEgMCAwIDAtLjgzMy42NjggMi45OTMgMi45OTMgMCAwIDAtLjUxNS45NzMgNC4xNSA0LjE1IDAgMCAwLS4xNjQgMS4xOTVabTEyLjM4Ny01LjcwN2gxLjAxOWMuMzM2IDAgLjYxMy0uMDQ2LjgzMi0uMTQuMjE5LS4xMDIuMzgzLS4yMzkuNDkyLS40MS4xMTctLjE3Mi4xNzYtLjM3NS4xNzYtLjYxYS45OTQuOTk0IDAgMCAwLS4zMjgtLjczOGMtLjIxMS0uMjE5LS41NjYtLjMyOC0xLjA2Ni0uMzI4LS4zODMgMC0uNzA4LjA4Mi0uOTczLjI0Ni0uMjU4LjE1Ni0uMzg3LjM3OS0uMzg3LjY2OGgtMi4xMzNjMC0uNTU1LjE1Ny0xLjAyOC40NjktMS40MTguMzEzLS4zOS43MzEtLjY4OCAxLjI1NC0uODlhNC42OSA0LjY5IDAgMCAxIDEuNzExLS4zMDVjLjcxMSAwIDEuMzM2LjEwMSAxLjg3NS4zMDQuNTM5LjE5Ni45NTcuNDg5IDEuMjU0Ljg4LjMwNS4zOS40NTcuODc0LjQ1NyAxLjQ1MiAwIC40NDYtLjE0MS44NC0uNDIyIDEuMTg0LS4yNzMuMzQ0LS42NTYuNjEzLTEuMTQ4LjgwOS0uNDg1LjE4Ny0xLjA1NS4yODEtMS43MTEuMjgxaC0xLjM3MXYtLjk4NVptMCAxLjQ2NXYtLjk2MWgxLjM3MWMuNzI2IDAgMS4zNDcuMDg2IDEuODYzLjI1OC41MTYuMTY0LjkwNi40MTggMS4xNzIuNzYyLjI3My4zNDQuNDEuNzg5LjQxIDEuMzM2IDAgLjU3OC0uMTY0IDEuMDctLjQ5MiAxLjQ3Ni0uMzI4LjQwNy0uNzc4LjcxNS0xLjM0OC45MjYtLjU2Mi4yMTEtMS4xOTUuMzE2LTEuODk4LjMxNmE1LjI5IDUuMjkgMCAwIDEtMS43MTEtLjI4MWMtLjU0Ny0uMTk1LTEtLjUtMS4zNi0uOTE0LS4zNTEtLjQxNC0uNTI3LS45NTctLjUyNy0xLjYyOWgyLjE0NWMwIC4zMDUuMTQuNTY3LjQyMS43ODUuMjgyLjIxOS42NjEuMzI5IDEuMTM3LjMyOS41MDggMCAuODgzLS4xMSAxLjEyNS0uMzI5YTEuMDkgMS4wOSAwIDAgMCAuMzc1LS44NDNjMC0uMjk3LS4wNy0uNTM1LS4yMTEtLjcxNWExLjA5MSAxLjA5MSAwIDAgMC0uNTc0LS4zODcgMi43MDUgMi43MDUgMCAwIDAtLjg3OS0uMTI5aC0xLjAxOVoiLz48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLW9wYWNpdHk9Ii44NyIgc3Ryb2tlLXdpZHRoPSIyIiBkPSJtMTI0Ljc2IDExNy4xNDggNi4yMDItOS4wNjFhNS42ODMgNS42ODMgMCAwIDAgLjkyMy0yLjMwN2wyLjc4MS0xNy4xNTFjLjUyMy0zLjIyOCA1LjE2NS0zLjIyOCA1LjY4OCAwbC4xNjEuOTljLjUxIDMuMTQ4IDUuMDc0IDMuMDQgNS40MzMtLjEyOXYwYy4zNDItMy4wMTQgNC42MDEtMy4zMjYgNS4zNzgtLjM5M2wxLjg5NSA3LjE1NmMxLjE2OCA0LjQwOCA3LjU2MiAzLjk2IDguMTA1LS41NjdsLjk0Ny03LjkxNGMuNzYtNi4zNDMgOS44MDQtNi43NjcgMTEuMTU0LS41MjNsLjk2NiA0LjQ3MWMuNjEzIDIuODM2IDQuMDUgMy45NjYgNi4yMjYgMi4wNDh2MGEzLjggMy44IDAgMCAxIDUuMzExLjI4bDMuNjc1IDQiLz48ZGVmcz48ZmlsdGVyIGlkPSJhIiB3aWR0aD0iMTkxLjAyMyIgaGVpZ2h0PSI5OS4wMjMiIHg9IjQuNDg5IiB5PSIzMC40ODkiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjIuMjU2Ii8+PGZlQ29tcG9zaXRlIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzEwNTdfMzk1OCIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA1N18zOTU4IiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PC9kZWZzPjwvc3ZnPg==", + "description": "A beneficial layer in the upper atmosphere, but harmful when present near ground level. Results displays mainly from vehicle exhaust and industrial emissions by combining the latest and aggregated values and optional simplified chart. ", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'ozone', label: 'Ozone', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'ozone', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Ozone\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#3FA71A\"},{\"from\":50,\"to\":100,\"color\":\"#80C32C\"},{\"from\":100,\"to\":130,\"color\":\"#FFA600\"},{\"from\":130,\"to\":240,\"color\":\"#F36900\"},{\"from\":240,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Ozone\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"public\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"µg/m³\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "O3", + "ozone" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_ozone__o3__chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_ozone__o3__chart_card_with_background.json new file mode 100644 index 0000000000..4c70766118 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_ozone__o3__chart_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "simple_ozone_o3_chart_card_with_background", + "name": "Simple ozone (O3) chart card with background", + "deprecated": false, + "image": "tb-image:b3pvbmUtY2hhcnQtdmFsdWUtY2FyZC13aXRoLWJhY2tncm91bmQuc3Zn:b3pvbmUtY2hhcnQtdmFsdWUtY2FyZC13aXRoLWJhY2tncm91bmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxMTIiIHk9IjI0IiBmaWxsPSJ1cmwoI2EpIiByeD0iNCIvPjxnIGZpbHRlcj0idXJsKCNiKSI+PHJlY3Qgd2lkdGg9IjE4OCIgaGVpZ2h0PSIxMDAiIHg9IjYiIHk9IjMwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii43NiIgcng9IjMuMDA4Ii8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjg3IiBkPSJNMjQuOTQ4IDQzLjc1di41NDdjMCAuNzUyLS4wOTggMS40MjYtLjI5NCAyLjAyM2E0LjM2NiA0LjM2NiAwIDAgMS0uODQgMS41MjVjLS4zNi40MTktLjc5NC43NC0xLjMuOTY0YTQuMTk0IDQuMTk0IDAgMCAxLTEuNjgxLjMyOGMtLjYxIDAtMS4xNjktLjExLTEuNjc1LS4zMjhhMy43NjIgMy43NjIgMCAwIDEtMS4zMDYtLjk2NCA0LjQ3NyA0LjQ3NyAwIDAgMS0uODYtMS41MjVjLS4yMDEtLjU5Ny0uMzAyLTEuMjcxLS4zMDItMi4wMjN2LS41NDdjMC0uNzUyLjEtMS40MjQuMzAxLTIuMDE3LjItLjU5Ny40ODMtMS4xMDUuODQ4LTEuNTI0LjM2OS0uNDI0LjgwNC0uNzQ1IDEuMzA2LS45NjRhNC4xMDIgNC4xMDIgMCAwIDEgMS42NzQtLjMzNSA0LjEyIDQuMTIgMCAwIDEgMS42ODIuMzM1Yy41MDYuMjE5Ljk0MS41NCAxLjMwNi45NjQuMzY0LjQyLjY0NS45MjcuODQgMS41MjQuMjAxLjU5My4zMDEgMS4yNjUuMzAxIDIuMDE3Wm0tMS43MTYuNTQ3di0uNTZjMC0uNTU3LS4wNTQtMS4wNDctLjE2NC0xLjQ3LS4xMDUtLjQyOS0uMjYyLS43ODctLjQ3MS0xLjA3NGExLjk3MiAxLjk3MiAwIDAgMC0uNzYtLjY1NmMtLjMtLjE1LS42NC0uMjI1LTEuMDE4LS4yMjVzLS43MTUuMDc1LTEuMDExLjIyNWEyLjA0IDIuMDQgMCAwIDAtLjc2LjY1NiAzLjE5IDMuMTkgMCAwIDAtLjQ3IDEuMDc0Yy0uMTEuNDIzLS4xNjUuOTEzLS4xNjUgMS40N3YuNTZjMCAuNTU2LjA1NSAxLjA0OC4xNjQgMS40NzYuMTEuNDI5LjI2OS43OTEuNDc5IDEuMDg3LjIxNC4yOTIuNDcuNTEzLjc2NS42NjNhMi4yNiAyLjI2IDAgMCAwIDEuMDEyLjIyYy4zODMgMCAuNzIyLS4wNzQgMS4wMTktLjIyLjI5Ni0uMTUuNTQ2LS4zNzEuNzUyLS42NjMuMjA1LS4yOTYuMzYtLjY1OC40NjQtMS4wODcuMTEtLjQyOC4xNjQtLjkyLjE2NC0xLjQ3NlptOS4yNCAzLjM5VjQ5aC01LjUxdi0xLjMxM2g1LjUxWm0tLjE1LTUuMDU4TDI3LjUyMSA0OWgtMS4wNjZ2LTEuMDZsNC43NzEtNi4zMzdoMS4wOTR2MS4wMjZabS0uNjM3LTEuMDI2djEuMzEzaC01LjE0N3YtMS4zMTNoNS4xNDhabTEuOTMyIDMuNzh2LS4xNTZjMC0uNTM0LjA3OC0xLjAyOC4yMzMtMS40ODQuMTU1LS40Ni4zNzgtLjg1OS42Ny0xLjE5NmEzLjAyNSAzLjAyNSAwIDAgMSAxLjA4LS43OTMgMy41MTYgMy41MTYgMCAwIDEgMS40NDktLjI4N2MuNTQyIDAgMS4wMjUuMDk2IDEuNDQ5LjI4Ny40MjguMTg3Ljc5LjQ1MSAxLjA4Ny43OTMuMjk2LjMzNy41MjIuNzM2LjY3NyAxLjE5Ni4xNTUuNDU2LjIzMi45NS4yMzIgMS40ODR2LjE1N2MwIC41MzMtLjA3NyAxLjAyNy0uMjMyIDEuNDgzYTMuNTUgMy41NSAwIDAgMS0uNjc3IDEuMTk3IDMuMTM3IDMuMTM3IDAgMCAxLTEuMDguNzkyIDMuNTQgMy41NCAwIDAgMS0xLjQ0My4yOCAzLjYwNyAzLjYwNyAwIDAgMS0xLjQ1Ni0uMjggMy4xMzYgMy4xMzYgMCAwIDEtMS4wOC0uNzkzIDMuNTUgMy41NSAwIDAgMS0uNjc2LTEuMTk2IDQuNTgyIDQuNTgyIDAgMCAxLS4yMzMtMS40ODNabTEuNjQ4LS4xNTZ2LjE1N2MwIC4zMzMuMDM0LjY0Ny4xMDIuOTQzLjA2OS4yOTYuMTc2LjU1Ni4zMjIuNzguMTQ1LjIyMy4zMzIuMzk4LjU2LjUyNi4yMjguMTI3LjQ5OS4xOTEuODEzLjE5MS4zMDYgMCAuNTctLjA2NC43OTQtLjE5MS4yMjctLjEyOC40MTQtLjMwMy41Ni0uNTI3YTIuNCAyLjQgMCAwIDAgLjMyMS0uNzc5Yy4wNzMtLjI5Ni4xMS0uNjEuMTEtLjk0M3YtLjE1N2MwLS4zMjktLjAzNy0uNjM4LS4xMS0uOTNhMi4zOSAyLjM5IDAgMCAwLS4zMjgtLjc4NiAxLjU3IDEuNTcgMCAwIDAtLjU2LS41MzQgMS41NDMgMS41NDMgMCAwIDAtLjgtLjE5OGMtLjMxIDAtLjU3OS4wNjYtLjgwNy4xOThhMS41ODcgMS41ODcgMCAwIDAtLjU1My41MzRjLS4xNDYuMjI4LS4yNTQuNDktLjMyMi43ODZhNC4wNjIgNC4wNjIgMCAwIDAtLjEwMi45M1ptOC40OTQtMi4wNDRWNDlINDIuMTF2LTcuMzk3aDEuNTUybC4wOTYgMS41OFptLS4yOTQgMS44NDUtLjUzMy0uMDA2YTQuODEyIDQuODEyIDAgMCAxIC4yMTgtMS40NDMgMy41NSAzLjU1IDAgMCAxIC42MDItMS4xMjhjLjI2LS4zMTQuNTctLjU1Ni45My0uNzI0YTIuNzQgMi43NCAwIDAgMSAxLjIwMy0uMjZjLjM1NSAwIC42NzcuMDUuOTY0LjE1LjI5MS4wOTYuNTQuMjUzLjc0NS40NzIuMjEuMjE5LjM2OS41MDMuNDc4Ljg1NC4xMS4zNDcuMTY0Ljc3My4xNjQgMS4yNzlWNDloLTEuNjU0di00Ljc4NWMwLS4zNTYtLjA1Mi0uNjM2LS4xNTctLjg0MWEuODguODggMCAwIDAtLjQ0NC0uNDQ0Yy0uMTkyLS4wOTEtLjQzMS0uMTM3LS43MTgtLjEzNy0uMjgzIDAtLjUzNi4wNi0uNzYuMTc4LS4yMjIuMTE4LS40MTEuMjgtLjU2Ni40ODUtLjE1LjIwNS0uMjY3LjQ0Mi0uMzUuNzExYTIuOTM4IDIuOTM4IDAgMCAwLS4xMjIuODYxWm05Ljk2NCA0LjEwOWEzLjk0NCAzLjk0NCAwIDAgMS0xLjQ4NC0uMjY3IDMuMjc1IDMuMjc1IDAgMCAxLTEuMTItLjc1OSAzLjM2NCAzLjM2NCAwIDAgMS0uNzA1LTEuMTQxIDMuOTk5IDMuOTk5IDAgMCAxLS4yNDYtMS40MTV2LS4yNzRjMC0uNTc4LjA4NC0xLjEwMy4yNTMtMS41NzIuMTY5LS40Ny40MDMtLjg3LjcwNC0xLjIwMy4zLS4zMzcuNjU2LS41OTUgMS4wNjYtLjc3My40MS0uMTc3Ljg1NS0uMjY2IDEuMzM0LS4yNjYuNTI4IDAgLjk5LjA4OSAxLjM4Ny4yNjYuMzk3LjE3OC43MjUuNDI5Ljk4NC43NTIuMjY1LjMyLjQ2LjcuNTg4IDEuMTQyLjEzMy40NDIuMTk5LjkzLjE5OSAxLjQ2M3YuNzA0aC01LjcxNVY0NC42MWg0LjA4OHYtLjEzYTIuMzEyIDIuMzEyIDAgMCAwLS4xNzgtLjgzNCAxLjQxNyAxLjQxNyAwIDAgMC0uNDg1LS42MjhjLS4yMi0uMTYtLjUxLS4yNC0uODc1LS4yNC0uMjc0IDAtLjUxOC4wNi0uNzMyLjE3OC0uMjEuMTE0LS4zODUuMjgtLjUyNi41LS4xNDIuMjE4LS4yNS40ODItLjMyOC43OTItLjA3My4zMDUtLjExLjY1LS4xMSAxLjAzMnYuMjc0YzAgLjMyMy4wNDQuNjI0LjEzLjkwMi4wOTEuMjczLjIyNC41MTMuMzk3LjcxOC4xNzMuMjA1LjM4My4zNjcuNjI5LjQ4NS4yNDYuMTE0LjUyNi4xNzEuODQuMTcxLjM5NyAwIC43NS0uMDggMS4wNi0uMjQuMzEtLjE1OS41NzktLjM4NC44MDctLjY3NmwuODY4Ljg0Yy0uMTYuMjMzLS4zNjcuNDU3LS42MjIuNjctLjI1NS4yMS0uNTY4LjM4MS0uOTM3LjUxNGEzLjcyNyAzLjcyNyAwIDAgMS0xLjI3MS4xOThaIi8+PHBhdGggZmlsbD0iI0Y4OUUwRCIgZD0iTTI1LjUwMiA5Ni4wMjVWMTE2aC0zLjI5NVY5OS45MzZsLTQuODggMS42NTR2LTIuNzJsNy43NzgtMi44NDVoLjM5N1ptMTIuODc5IDBWMTE2aC0zLjI5NVY5OS45MzZsLTQuODggMS42NTR2LTIuNzJsNy43NzgtMi44NDVoLjM5N1ptMTguMTI5IDguMzU0djMuMjU0YzAgMS41NTgtLjE1NSAyLjg4OS0uNDY1IDMuOTkyLS4zIDEuMDk0LS43MzggMS45ODItMS4zMTMgMi42NjZhNS4xNDcgNS4xNDcgMCAwIDEtMi4wNjQgMS41MDRjLS43OTMuMzE5LTEuNjgyLjQ3OC0yLjY2Ni40NzgtLjc4NCAwLTEuNTEzLS4xLTIuMTg4LS4zYTUuMzQyIDUuMzQyIDAgMCAxLTEuODA0LS45NDRjLS41MzgtLjQyOC0uOTk4LS45OC0xLjM4MS0xLjY1NC0uMzc0LS42ODQtLjY2NS0xLjQ5OS0uODc1LTIuNDQ3LS4yLS45NDgtLjMtMi4wNDYtLjMtMy4yOTV2LTMuMjU0YzAtMS41NjguMTU0LTIuODg5LjQ2NC0zLjk2NS4zMS0xLjA4NS43NTItMS45NjQgMS4zMjYtMi42MzlhNS4wNTIgNS4wNTIgMCAwIDEgMi4wNS0xLjQ5Yy44MDMtLjMxIDEuNjk2LS40NjUgMi42OC0uNDY1Ljc5NCAwIDEuNTIzLjEgMi4xODguMzAxYTUuMTIgNS4xMiAwIDAgMSAxLjgwNS45MTZjLjUzOC40Mi45OTMuOTY2IDEuMzY3IDEuNjQuMzgzLjY2Ni42NzQgMS40NzMuODc1IDIuNDIxLjIuOTM4LjMgMi4wMzIuMyAzLjI4MVptLTMuMjk1IDMuNzE5di00LjIxMWMwLS43OTMtLjA0Ni0xLjQ5MS0uMTM3LTIuMDkyLS4wOTEtLjYxMS0uMjI4LTEuMTI2LS40MS0xLjU0NS0uMTczLS40MjgtLjM5Mi0uNzc1LS42NTYtMS4wNGEyLjMxIDIuMzEgMCAwIDAtLjkwMy0uNTg3IDMuMTg3IDMuMTg3IDAgMCAwLTEuMTM0LS4xOTFjLS41MSAwLS45NjcuMS0xLjM2OC4zLS40LjE5Mi0uNzM4LjUwMi0xLjAxMS45My0uMjc0LjQyOC0uNDgzLjk5NC0uNjMgMS42OTUtLjEzNi42OTMtLjIwNCAxLjUzNi0uMjA0IDIuNTN2NC4yMTFjMCAuODAyLjA0NSAxLjUwOC4xMzYgMi4xMTkuMDkyLjYxLjIyOCAxLjEzNS40MSAxLjU3Mi4xODMuNDI4LjQwMi43ODQuNjU3IDEuMDY2LjI2NC4yNzQuNTY1LjQ3NC45MDIuNjAyYTMuMjUgMy4yNSAwIDAgMCAxLjEzNS4xOTFjLjUyIDAgLjk4LS4xIDEuMzgtLjMuNDAyLS4yMDEuNzQtLjUyIDEuMDEzLS45NTcuMjczLS40NDcuNDc4LTEuMDI2LjYxNS0xLjczNy4xMzctLjcxMS4yMDUtMS41NjMuMjA1LTIuNTU2Wk03MS41ODYgMTAzLjMyaDIuODM2VjExNmgtMi42MTNsLS4yMjMtMi45M3YtOS43NVptLjQxIDYuNzg1IDEuMTEzLS4wMjNjMCAuODkxLS4wODYgMS43MTEtLjI1NyAyLjQ2MS0uMTY1Ljc1LS40MjIgMS40MDItLjc3NCAxLjk1N2EzLjY5OCAzLjY5OCAwIDAgMS0xLjMxMiAxLjI4OWMtLjUzMi4zMDUtMS4xNi40NTctMS44ODcuNDU3QTQuNjMgNC42MyAwIDAgMSA2Ny4zMiAxMTZhMy4xIDMuMSAwIDAgMS0xLjE5NS0uNzk3Yy0uMzM2LS4zNTktLjYxMy0uODI4LS44MzItMS40MDZsLS4zNjMtMy4xOTloMS42MDVjMCAuNzAzLjA1OSAxLjI3Ny4xNzYgMS43MjIuMTE3LjQ0Ni4yODEuNzg5LjQ5MiAxLjAzMi4yMTEuMjM0LjQ1Ny4zOTguNzM4LjQ5Mi4yOS4wOTQuNjAyLjE0LjkzOC4xNC42MTcgMCAxLjEyNS0uMDkzIDEuNTIzLS4yODEuNDA3LS4xOTUuNzIzLS40NjUuOTUtLjgwOC4yMzQtLjM1Mi4zOTgtLjc2Mi40OTItMS4yMzEuMTAxLS40NzYuMTUyLS45OTYuMTUyLTEuNTU5Wm0tOC4yNzMtNi43ODVoMi44MTJ2MTcuNTU1aC0yLjgxMlYxMDMuMzJabTIyLjEyNSAwaDIuNTY2djEyLjMyOGMwIDEuMTQxLS4yNDIgMi4xMS0uNzI3IDIuOTA3YTQuNjY4IDQuNjY4IDAgMCAxLTIuMDI3IDEuODE2Yy0uODY3LjQyMi0xLjg3LjYzMy0zLjAxMi42MzMtLjQ4NCAwLTEuMDIzLS4wNy0xLjYxNy0uMjExYTYuNjI0IDYuNjI0IDAgMCAxLTEuNzEtLjY4IDQuMjM0IDQuMjM0IDAgMCAxLTEuMzcyLTEuMjA3bDEuMzI0LTEuNjY0Yy40NTQuNTM5Ljk1NC45MzQgMS41IDEuMTg0LjU0Ny4yNSAxLjEyMS4zNzUgMS43MjMuMzc1LjY0OCAwIDEuMi0uMTIxIDEuNjUyLS4zNjNhMi40NjMgMi40NjMgMCAwIDAgMS4wNjctMS4wNDNjLjI1LS40NjEuMzc1LTEuMDI0LjM3NS0xLjY4OHYtOS41MTZsLjI1OC0yLjg3MVptLTguNjE0IDYuNDgxdi0uMjQ2YzAtLjk2MS4xMTgtMS44MzYuMzUyLTIuNjI1LjIzNC0uNzk3LjU3LTEuNDgxIDEuMDA4LTIuMDUxYTQuMzkgNC4zOSAwIDAgMSAxLjU5NC0xLjMyNGMuNjI0LS4zMTMgMS4zMzEtLjQ2OSAyLjEyLS40NjkuODIgMCAxLjUyLjE0OCAyLjA5OC40NDVhMy45NjIgMy45NjIgMCAwIDEgMS40NjUgMS4yNzhjLjM5LjU0Ni42OTUgMS4yMDMuOTE0IDEuOTY4LjIyNy43NTguMzk1IDEuNjAyLjUwNCAyLjUzMnYuNzg1YTEyLjY4MSAxMi42ODEgMCAwIDEtLjUxNiAyLjQ4NGMtLjI0Mi43NS0uNTYyIDEuMzk5LS45NiAxLjk0NS0uMzk5LjU0Ny0uODkxLjk2OS0xLjQ3NyAxLjI2Ni0uNTc4LjI5Ny0xLjI2Mi40NDUtMi4wNS40NDUtLjc3NCAwLTEuNDc0LS4xNi0yLjA5OS0uNDhhNC43NzIgNC43NzIgMCAwIDEtMS41OTMtMS4zNDhjLS40MzgtLjU3OC0uNzc0LTEuMjU4LTEuMDA4LTIuMDM5YTguOTc3IDguOTc3IDAgMCAxLS4zNTItMi41NjZabTIuODI1LS4yNDZ2LjI0NmMwIC41NzguMDU0IDEuMTE3LjE2NCAxLjYxNy4xMTcuNS4yOTMuOTQxLjUyNyAxLjMyNC4yNDIuMzc1LjU0Ny42NzIuOTE0Ljg5MS4zNzUuMjExLjgxNy4zMTYgMS4zMjQuMzE2LjY2NCAwIDEuMjA3LS4xNCAxLjYzLS40MjIuNDI5LS4yODEuNzU3LS42Ni45ODQtMS4xMzZhNS43NjUgNS43NjUgMCAwIDAgLjQ5Mi0xLjYxOHYtMi4xMjFhNS4xODYgNS4xODYgMCAwIDAtLjI5My0xLjI4OSAzLjQ1NSAzLjQ1NSAwIDAgMC0uNTc0LTEuMDQzIDIuNDAzIDIuNDAzIDAgMCAwLS45MTUtLjcwM2MtLjM2Ny0uMTcyLS44LS4yNTgtMS4zLS4yNTgtLjUwOCAwLS45NS4xMS0xLjMyNS4zMjlhMi42OCAyLjY4IDAgMCAwLS45MjUuOTAyYy0uMjM1LjM4My0uNDEuODI4LS41MjggMS4zMzZhNy4yMzMgNy4yMzMgMCAwIDAtLjE3NSAxLjYyOVptMTguNzAzLTEwLjYxOC02LjY0NSAxOC41MjhoLTIuMjE1bDYuNjU3LTE4LjUyOGgyLjIwM1ptNC43ODEgNi45NjFWMTE2aC0yLjgyNHYtMTIuNjhoMi42NmwuMTY0IDIuNTc4Wm0tLjQ1NyAzLjI5My0uOTYxLS4wMTFjMC0uODc1LjEwOS0xLjY4NC4zMjgtMi40MjZhNS44MzQgNS44MzQgMCAwIDEgLjk2MS0xLjkzNCA0LjIzIDQuMjMgMCAwIDEgMS41Ny0xLjI3N2MuNjMzLS4zMDUgMS4zNjQtLjQ1NyAyLjE5Mi0uNDU3LjU3OCAwIDEuMTA1LjA4NiAxLjU4Mi4yNTguNDg0LjE2NC45MDIuNDI2IDEuMjU0Ljc4NS4zNTkuMzU5LjYzMy44Mi44MiAxLjM4My4xOTUuNTYyLjI5MyAxLjI0Mi4yOTMgMi4wMzlWMTE2aC0yLjgyNHYtOC4yMDNjMC0uNjE3LS4wOTQtMS4xMDItLjI4MS0xLjQ1M2ExLjU1NyAxLjU1NyAwIDAgMC0uNzg2LS43NWMtLjMzNi0uMTU2LS43MzgtLjIzNS0xLjIwNy0uMjM1LS41MzEgMC0uOTg0LjEwMi0xLjM1OS4zMDVhMi41NiAyLjU2IDAgMCAwLS45MDIuODMyIDMuNzExIDMuNzExIDAgMCAwLS41MTYgMS4yMTkgNi40IDYuNCAwIDAgMC0uMTY0IDEuNDc2Wm03Ljg2My0uNzUtMS4zMjQuMjkzYzAtLjc2NS4xMDUtMS40ODguMzE2LTIuMTY4YTUuNTI4IDUuNTI4IDAgMCAxIC45NS0xLjgwNCA0LjQ0MiA0LjQ0MiAwIDAgMSAxLjU1OC0xLjIzMWMuNjE3LS4yOTcgMS4zMjQtLjQ0NSAyLjEyMS0uNDQ1LjY0OSAwIDEuMjI3LjA5IDEuNzM1LjI2OWEzLjE4IDMuMTggMCAwIDEgMS4zMTIuODIxYy4zNi4zNzUuNjMzLjg2My44MjEgMS40NjUuMTg3LjU5My4yODEgMS4zMTIuMjgxIDIuMTU2VjExNmgtMi44MzZ2LTguMjE1YzAtLjY0LS4wOTQtMS4xMzctLjI4MS0xLjQ4OGExLjQ2IDEuNDYgMCAwIDAtLjc3NC0uNzI3Yy0uMzM2LS4xNC0uNzM4LS4yMTEtMS4yMDctLjIxMS0uNDM3IDAtLjgyNC4wODItMS4xNi4yNDZhMi4zMDYgMi4zMDYgMCAwIDAtLjgzMi42NjggMi45NzYgMi45NzYgMCAwIDAtLjUxNi45NzMgNC4xODQgNC4xODQgMCAwIDAtLjE2NCAxLjE5NVptMTIuMzg3LTUuNzA3aDEuMDE5Yy4zMzYgMCAuNjE0LS4wNDYuODMzLS4xNGExLjE0IDEuMTQgMCAwIDAgLjQ5Mi0uNDFjLjExNy0uMTcyLjE3NS0uMzc1LjE3NS0uNjFhLjk5NC45OTQgMCAwIDAtLjMyOC0uNzM4Yy0uMjExLS4yMTktLjU2Ni0uMzI4LTEuMDY2LS4zMjgtLjM4MyAwLS43MDcuMDgyLS45NzMuMjQ2YS43MzkuNzM5IDAgMCAwLS4zODYuNjY4aC0yLjEzM2MwLS41NTUuMTU2LTEuMDI3LjQ2OS0xLjQxOC4zMTItLjM5LjczLS42ODggMS4yNTMtLjg5YTQuNjkzIDQuNjkzIDAgMCAxIDEuNzExLS4zMDVjLjcxMSAwIDEuMzM2LjEwMSAxLjg3NS4zMDQuNTM5LjE5Ni45NTcuNDg5IDEuMjU0Ljg4LjMwNS4zOS40NTcuODc0LjQ1NyAxLjQ1MiAwIC40NDYtLjE0Ljg0LS40MjIgMS4xODQtLjI3My4zNDQtLjY1Ni42MTMtMS4xNDguODA5LS40ODQuMTg3LTEuMDU1LjI4MS0xLjcxMS4yODFoLTEuMzcxdi0uOTg1Wm0wIDEuNDY1di0uOTYxaDEuMzcxYy43MjcgMCAxLjM0OC4wODYgMS44NjMuMjU4LjUxNi4xNjQuOTA3LjQxOCAxLjE3Mi43NjIuMjc0LjM0NC40MS43ODkuNDEgMS4zMzYgMCAuNTc4LS4xNjQgMS4wNy0uNDkyIDEuNDc2LS4zMjguNDA3LS43NzcuNzE1LTEuMzQ4LjkyNi0uNTYyLjIxMS0xLjE5NS4zMTYtMS44OTguMzE2YTUuMjk1IDUuMjk1IDAgMCAxLTEuNzExLS4yODFjLS41NDctLjE5NS0xLS41LTEuMzU5LS45MTQtLjM1Mi0uNDE0LS41MjgtLjk1Ny0uNTI4LTEuNjI5aDIuMTQ1YzAgLjMwNS4xNDEuNTY3LjQyMi43ODUuMjgxLjIxOS42Ni4zMjkgMS4xMzcuMzI5LjUwNyAwIC44ODItLjExIDEuMTI1LS4zMjlhMS4wOSAxLjA5IDAgMCAwIC4zNzUtLjg0M2MwLS4yOTctLjA3MS0uNTM1LS4yMTEtLjcxNWExLjA5NCAxLjA5NCAwIDAgMC0uNTc1LS4zODcgMi43MDUgMi43MDUgMCAwIDAtLjg3OS0uMTI5aC0xLjAxOVoiLz48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuODciIHN0cm9rZS13aWR0aD0iMS41IiBkPSJNMTg3IDU0Yy03LjE0MSAwLTYuMjk1IDU2LjE0OS0xMy43MjIgNTYuMTQ5LTYuMjg1IDAtNy4yOTMtMjMuODgtMTIuMTQ5LTIzLjg4LTQuODU2IDAtNi45OSAzMi43MzEtMTMuMjc1IDMyLjczMS01LjE0MSAwLTcuOTk4LTYuNjM4LTEyLjg1NC02LjYzOCIvPjxkZWZzPjxwYXR0ZXJuIGlkPSJhIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBwYXR0ZXJuQ29udGVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCI+PHVzZSB4bGluazpocmVmPSIjYyIgdHJhbnNmb3JtPSJtYXRyaXgoLjAwNTAzIDAgMCAuMDA4OTcgMCAtLjIxMykiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjE5Ny4wMjMiIGhlaWdodD0iMTA5LjAyMyIgeD0iMS40ODkiIHk9IjI1LjQ4OSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMi4yNTYiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTEwM18yNDE3Ii8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMTAzXzI0MTciIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48aW1hZ2UgeGxpbms6aHJlZj0iZGF0YTppbWFnZS9qcGVnO2Jhc2U2NCwvOWovNEFBUVNrWkpSZ0FCQVFBQUFRQUJBQUQvMndCREFBSUJBUUVCQVFJQkFRRUNBZ0lDQWdRREFnSUNBZ1VFQkFNRUJnVUdCZ1lGQmdZR0J3a0lCZ2NKQndZR0NBc0lDUW9LQ2dvS0JnZ0xEQXNLREFrS0Nnci8yd0JEQVFJQ0FnSUNBZ1VEQXdVS0J3WUhDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dyL3dnQVJDQUNmQU1jREFSRUFBaEVCQXhFQi84UUFIZ0FBQVFRREFRRUJBQUFBQUFBQUFBQUFBd0lFQlFZQkJ3Z0FDUXIveEFBY0FRQURBUUVCQVFFQkFBQUFBQUFBQUFBQkFnTUVBQVVHQndqLzJnQU1Bd0VBQWhBREVBQUFBUHFWOUw4MFplT2hPcGNCanJ4bExoWE1wT3BjcVRLNVVMZ001REhSakt4MVl5c1ZXY0tTcXhReEFTOGVaZmI4c3lrODJPQzRWakl4bExoV01wY0lUS3gwZHhOem9YSUpsSlZKdUpReDE0NEpUeHU1WjR4SE1mclp6b3hac2RDWldPaFBOanF4RmM4Mk9oT2p1RUoxTGdGd3BNcE9BWGdRQWhDeUNCVmtLY2MyKzF4bFlzMk5Oekk1cHVaR05OenpjODJjUlp3ck9Kc2RPS29XcTVYbEl1Unl3cENwT1V4VlhBbEU1NCt5WXNuTkdoVWNxVU5DaHAwTWxIRVhjeG80U2haaFNLcGVVZ1VpcW1GQlZqbFNSWERLZ2lLUXFRcm9uOVF5bFJ5em9TZFNUb1dWalFvVktualU4cUdTaTVVVk1MaXF3cTBHWmpLOTVRcVFVdllVS21GaFNjdWdQNkgvQUM1dTRFQktaZFR5R2hsYk94MVppQmpyUnpHajNIc2Q1OVVYcXgrblF5UEdiTUtTbG44bjAyMVlMS29JVE4zbWJTMjBaeHNOY2ZhZk1TZVBaSTVOSGlxN3dpUFU4bUM5YnlhNzZmbWhkWlhIc3Mzait4VFBJOWJXR1MrL0hTKzZzYkNzNm42WG11VnBFWG5IMWxGYVlzcVNLcnpHVFpQZWZ2cTYyY1o5TXhqMHZjOVdnZkM4N2VkRDI0dVphUTVZMHc1N1ZsOFh2TlkwdDJPUjJTOHJvVXFlN1BRZmM4T0szNGZQTjRwbjhtcDE1bnExWEZyMm41ZnJ0KzRxZ3ltQXBMVzJyTG9yWmkwRGNVbU5vRkM0ZFh4RDVhMlNGcnI1bm83ZzhQMitoODFlazc0M0hxZVhUZmQ4V29lbjUzSllma255L1MxWmoydXVmN2VCMGdZSWpUMWZLVW1tZlUrM0x6MXVqVEo5WUt6S3hteXMrcXRrcTN4Yk45L01mVGI4OEQxK2NOdWJwMzBmSHEyM3ovbkhaZFRsMlBjcHVPemZkV1ZFOEFFTmlLcXk2cmVHbmRVZFBhT3FsVUNaeWJwYW5uTUwwNW5wNkY1cnp2VDZGK2M5L2JubmF1U04rUFJ2b2VmWHRtYlRSbkFQelppU3FmZUNidTBaK2pQSnNnTlRxSnI5R29BcnJhOG9qWmtqTDVZK3NrVmpoMHhLakxMcWsvTzlQZWZpZXJ0UEpvNTgxWStOTi9uTU5FYTlxbkJKWkRkK2dmSHA4ckxtWEU2T3U1Zzg2dFFVeHUxKzRwbHB3K25NMTA0b1JwMXgrakdUTU5EZkR2bC9QM2JHeFhhbGRNNnNuTi9UZ1NVMlZycVA2Ry9IM25WaUt4QjJSemFpUkZaUW1pVlJ0T2gxU21VbXhlYzF5eUhNaVZhdkswUG51ZkxwWlJNT1ZxRkUxQ1lhbnJPQWZoT1AwQitmNlpsS2tPRHltRHdkSThxbVdFMHdwdXZQQVdTT0hOa0swTW9Ha1pQbVY2L0N0RG05Q0QxMWFSWENyc0tXMDZKb3ovYVBMck1qWVV0K0FTR3pKNml2bW5NRHBNckY2WXdONU1TbzFPSjB3clpuUjByU2NuWUlkZnBTZ2ltc2VwU25KaXYwTE1ua3lZRktsbjNObVJxeW9vcFdWOFVreUh0SjVaSThpUDdoY1loRzFMbXJUSlBTcG5RRWUxZkpxY2owT1pSeC9RcDcwelRjNmc2c3Z1RHdha05Tb201eTYrTTZMU09xUWxEa2RZemJRZVczT1VIRk52RHNOM2tieEdVZkRCWVA2R1BvVU5Na1RsZ2tIS0hKS2tIVTRweXBIdUE1TFF0bWVlMVpvUERweUdoczFSSXhaVjhoR3k0WHNjY25ra0tVL3dEL3hBQWpFQUFCQlFBQkJBTUJBUUFBQUFBQUFBQUNBUU1FQlFZQUJ3Z1RGQkVTRlJZSi85b0FDQUVCQUFFQ0FSUVVBUUVSRVJRRUZBUUVIZ29IQTRLQ2dJSWlJaUtEd1ZIZzhCUlVVQkJFVUJCNENnbzhIZzhEZzhUZzhIaWNGRVFVSG4yRlJJRkVoVUNFeE5ERlJNWEJNU0JSNEtpYU9JNmp5UEkrTDR2Q1FtSmlhR0RpR0ppUWtLaW9LTGlQSklTVDdIc2V5TWxKS1NVZlI4WEVNVEYxSGhlQjRYd2ZCMFgwaysxN1h0SkpTU2tyMmhscEw5cEpReXhsZzZqb3VJYU9nNkxvdUE2and5UE9qeVBJOEwzbTgzbVI3eit3a2taU1NpZC9iSzhhdFVzLzJudFZIMXhhZG5RSm9QMWl2azEzOVM3dEkyM2IwUjNZMnEzZjlTR3VlMFM2Vm5XTlh3eUVpRld2VmtwbDE4TFdMTmEwRngzUHNkL1dKNjQvcnpMejJuTk03MUZkNnJuMUYvdW05ekYyOGJXSURKeDVLMkM2ZE40MzFTMlBkRHMrL1RYOTJVdXpaVnRpanRjaDNhWWF3ZjZmMkdEbVkzOEFhbG1paFVFMjlZN2hmbGVCSTl1d1M0aDZ1dnVNeE14eDVscklCazQ5REJvNjNGVW1VelhYck1hMmRuSm1DczhydE81ZldkZFBLQnFDS1F2bExuV2R0b2JYUXZCb1daNFZrR1FFUjZBMm5TL1kyZVB2cVBwdjNOZFR1N3ZxajF0YWM4clQ2U2hKV1ZhTm16cTdpdHZxeTRnRkFLQkVtUTVMUEczdkRWUFl2WHpJWFZIbysxc2IzcDdQcVpKSklEZ0kyVFFndy9WMk5MWlp1eXg5amhGeDcrUldsOENLZkZzNjdSNXJWdGF6cWQwOWtMSzFrOGpCSGZ6L0FFaGpCd1Rrak1nMk5WWXg1YmFxNDNLcHBOSjZhUS95SU5uQzFOcGE2a3JtcUtSOElyY2xKQ1NFTHdPd0pFTjlteHFiSFB5cVpxc2pWTGRhOWxaV1JXc0dOSnlwd0xpaXZNclpabFlTOEVrTDU4d3pHckZ1Y2NXWm5KdEc1VHVaWCtjQ3VZZ3RRbk01TTZjMm1GbVJwRmU5WHpxQ3h5TTNIZ1BqVm91SzJ2QmRhbXg3SkhwVks1VWx6NDlQODlJelJnVXltcytuRS9DM0ZLZFovUEJ3RlRoTWt3clJOSWdxeSt6S0IxWWoxQXRQNnRuUHRPNEkrN3ZTOThPcDc1WHU2YTg2eVNyVlpvbUxnT0k1OWlFaEp2eHRwTnNiVHJoUDcyN2YvUXV3L3dCQzlsM1Z5My90eE9EeEJRZmhFUlJJWEJkRi93QTNrRG1wNmlhbnZ0NnFkYksrUS9UdFV6OUdvZlJCK3YwVlBxcGk1NVAveEFCSUVBQUJBd0lDQmdjRENBY0ZDUUFBQUFBQkFBSURCQkVGRWdZVElURlJZUWNRSWpKQmNZRVVZcUVWSXpOQ1VuS0NrU0FrTlpLeHdkRVdKVFJEY3pCRlU0T2lzc0xoOFAvYUFBZ0JBUUFEUHdFY0Z5Ui9USFZ3US8yRjBFQWgxRHFKVHVDUEZjMXo2Z0VFT29JTGdpanlSUnR2UnQxYkVFRUVGYnE4YnJtZ2d1SDZBUVE1cTZIRkRpbThVT0s1cm11YUhGQXJ4NnVmVno2aHdYTkRpaHhRUGloeFhBOVFDSEg5TDNsN3lIVU9LdjFEajFqcUhoMWMxWkRxNW9jVndQVUFodlE0cm1oeFhORGl1YTVvZmFRNHIzbHpYTkg3U1BGZThpTnprZkVxL3dCWmMxelhOY0VWelhOY1Z2NnVhNW9yRzZmdFBZSGozV0tydlp3RGZPSXFzYzZ3bmphUGRHMVRGblpuYmYzd3BteDNreStiUmNMRTdFd1U3SmZ1a1gvSXJHSVRiNUx0OS9Zc1FjZm5xVmpPWmNxNTV0RFYwemZ3M1dLWHM2cnBuZW9XSTN0bForN2YrQ3haN1AxZGtianczZnhXbGtjaC91aVczUEpaWTR3NVpjSm5ITmtOMWl1cjJ3VkE1K3ppNjdCaXFhaXBieDJBTE5JSTJZa2NvM055SCtxcjVHWnFlVmovQUwyeGFWU3V0QUlBUHVyU1ZnelZWVkNQK1dRc2NtK2d4S0Q4SC90cXJxTC9BQlVsUzQ4ZlpiaEdkdXlTUnA1MHhDeFUvd0NHbmMvN3JXaitJV2tjWS9aTmEvbkhxai9CVE8yRnlkTDN6K2FwNmh2YmhZVmhqenRoeTh3aTl0cVN2eSthMGtaOUZYWjFwU0FRK0V1OHBsaWxPZm5hRjkvTlRPNy9BR2ZSVW9PWjlZQitCWWZReDYycHhka1RRTnJwU0dqNHJvVzBXTG9NVzZRcVdXUm0rS2ppZFVPLzZMajRyb25maW9vWDZONDJLUW16cTR3UjdPZVRQbVhSTDBsVkQ2UFEvVEtDU3BidDlscUkzd3lIN29lQm05THFnaGQ4NVhRK2psbzdPd3RyS3FJai93QzRyUk5rNWRTMXpqeVpIc1dDVXpmMmlmTFZnckRhYy9xOFQ1RHhkc1dKYm9OVkVQSll4SWYyamI3b3NzYWYvdmlYOTVZMWY5cVNIOFN4RTdKYWw1VG45NTcwemdzbTV5dDQyVWtRdnZDcFlqWjdpMVlaUzdaSzdaNUxRcHJTNnMwa3BJcmI5Yy9ML0ZkREdodElaSmNZYmljOXV6UzRZek9UNXVObWo4MW83OG1GdWhIUk5tckhmNXVNVE4xVFBTUGE0K29YUzdwYXoyYWpsdzdCbUhmOGtVT1I1L0cvTWZ5c3NUeE9aMCtLNGhQVXlPZGQ3cDUzUEpQSGFuV3lnS2QzZGFzV3dDdGp4R2dmcTVvWGgwYi9BTEo0aFk2OE5wdE9ORTZiRW03QWFpa2ZxSmZQeERqK1M2T2VrMm1OVG9yaWtycEFMdm9xcjV1Vm5wNGptRjJjc2JzbzgxVzVTMkdUNExISW5rT2UwRHpWWEhmUEtUNktydlpwVmM3ZkswZXFhSERYVkYrUUMwTjBhYmJHOUk2T2xzTDJxYWxyRGJ5SzZGWTZuMlgrMk5LVDl2VXlaUHp5b0ZGcDJJdDNyWnZVYzROeHRUdzEyV1FxV1JydFk2NDVxR2NrbUpHM3pkN0p6SFdjdFlic2NxaUU5bU82bkIrY2pJNXFKNUFmS1B4QlVFNURuelJzOVZpK0I2bkVzTXFYalY3WTU0bmJ2VUxTUEE1QkRwaFRHdnBmclRSTUFtWi9KLzhBRmFLYWM0ZjhvNk9Zc3lkZytrYU5qNGp3YzA3V29WZDh0UjhGV2treFM1dlZZbGhkUEpXMVRZMlF4TnpTVFNTQnJXRGlTZHkwVTBhTWxKbzdBY1duYi9teE95d0EvZTN1OUYwbGFZVDJxTklaS1NIZTJsdzRtSm85UWN4OVNwcG51a2tjWE9KMnZjYmxPSGVlbkJjVUNFK1BhRUJlNXNzb09mY29uc2RheEN5eWw5Tk1XSHpXSlVyL0FKNFhDb3NUaCtjWUwrU2RBelgwenI4RTdkVU1WTFVidDZMUG9tcWVKMTg3bFBRemV3VmtZZEEvd2NzSzBncFRXWVNRMTl1NkZwSG9qaW55cmdHSTFHSDFqTjA5TTh0dU9CNGprVm92amRKOGw2ZVN0d25GbzIyT3o1bXE1eDhEN2g5RlRhSWgxTm9ob2hQWFNlRlRpRDlURis2THVQd1hTaDByVkJkcGZqcnpUTU40OFBwUnE2ZVA4QTNubTY1UmN4elhiN2JMcXp1Nk9DYllnOFV5MWsxMjlYOEU4Yms1d3NXSXlNT1VLcml6RGFWVUc3bUIzbFpWWkJhK25PL2VxbmNZRkxFY3pHRnR1Q3E0ZXk1cEtqbUZwRzJLREQyU2JLN0xPVUw3M0tob3BjOXlQSk1qQUJMcmZhQ3dMU3FpTFpneDl4dkc5VFUwVXM5RXpYUThBTnJWcUcvSXVtMVBycWNOeU1yV3NKZTM3NCt0NTcxUlR4SEZkR01RanFJVHRHcmZleXJLYVR0eDJJM2hPekE1TXBBN1hOT0lJTzNtbmtYQnNwYnArNVp0Nmo0S0NWcHMxTXNRK1BNRlFTM09wMnFna0JHb1ZJMTVjMEh5VkxhemQvTk1qT1pqRnFmcElyZVNFUE1JZlZmWmVMVlBUdnNwdGJmMmg3RDdwVmF5TVBOUitOdjgxRFd4YXF0MkUvVzhGaGVPTmtxc0xhMktvY0wySGRlc2IwTXhKN0lOWkE4SHR0OEhmMVRzUWZhcmdZMTNpOW45RU96STlsMnY3cmdnTDVkeDZuaFNjRkkzd1JhZ2c5TWt2ZHFlQVN3SnpMM1ltOEU5cHRhNkV1ellueWRxTWZCVjhlNkUrYXhKbloxWlU3OWtzQ25hOG1DSStxeFBESEJocFgyNUtrcUlja2pzcEtxNElIUGEvV3hINExEOGNpZEhXczIrRHJiUW5ZZEtjcnJ0Qjd5SU53NjQ0TFdBdWpQNFZaeHpiRVQxQlJPM2hSTzNGU083cjFVc3ZkbXprZ1FRNEpzb1BaVy9zS2FPL1pUM214Q2xZTm91b25iSHhxbHFXM2oySGtxcUVsekk4d1VzTGlERTRLSjR5VHdxR2NHV2xteW5rcTZqdkcvdWZXdDRxbnJHblZndGZ4Q3hGalhYaXpEalpWa0xzOGNCdHlDcUlTYnRJSy80bTN6UjZpbk5SYXVhamYzbFJWSTJnSnJ4ZUZ5cUliNTRiaFF5M3pzVWI3NnNLcWhIWjJxVmh0SkVtVzdpZzNPakN3eXE3MFF1b0pnWFVyc3BXT1VGM3dRRWdlTFZpVVJMS21Cd1BOcXF6dGJHcXd0T2VtdW8zOXA5T1FxZDl5eVBhbzcvQUVTSFU0ZFd6WW5OUkIyRlNNOFZmWVNvSjIyY3FXY0Z6TmhVOVAzUmRGbmVZbzM3MnFOMjdxY3c5bHllellVeDQyckQ2NWxwcVpqdlJZZk5kOUxlTThsaVZKbTFlV1FmRlZFVHJUVThqT2VWVkRYbk9MdDRsUjFIZnB4NmRRc21wcmxsM0xpaHdWdXB6UEZjMEhqYXFlZnZNQ2pmdGlObFVRODFNTm1WWVJnekRMak9LMHRJMEM1TlZVdGoyZmlLNkVzR3plMGRKbUd2TGZxMHIzVEUrV1FHNjZDNDNsbjlwS3cyK3UzQ1piTG82d3VJalJmQThUeFdhK3pPd1UwZjV1dWZndWxQRkJxZEY4Qnd6Q1cvYmNEVXZQNzFtL0JkUGxYc20wNnkvd0NuUlFOLzhWMHBhU3NhM0d0UE1Ra0REZHJZcGRYL0FOZ0N4Q3VuZFUxdUwxVXIzZDkwdFE0ay9tVlZYdjdmTmIvVlA5ZXZtdWFCVGR0dXJnbmhjVmhtRDBwcnNXeEdDa2hic00xVE1HTnY1bGRFT0Fsak1TNlJNTWFYc3pOMVUrczJmZ3ZaZENPRjFQcyt1eFdjWCtraG9QNUYxMW9QVEdSbUFhQVlyVjIramtxYW1PRU85Qm1LMDhuQkdFZEhlQzAvT2VvbG0vbTFkTittYm5SVldtajZDRW00cDhIakZPTjF1OE8wZlVxU3RxWFZGYkxKTzl6cnVrbmtMM0UrWlZoczNlYUEzQWZ2SjF1eXpjdkZ6RTdhajlteWNQQk52MzdLM2orbUhMUXJRcW45cDBueHIyWnBMZ1AxYVI5OHUvdXRLNk44TWZKVDZJNk5Zamk3Mk83RXo3VThUeGJmMnJ1MzdPNnVrSHBpeFJsWnBKWHNaVFUwam5VT0gwb3l4UVgrTG5lOFZWVFNoc3NXZTUya1dCODFoWXBIeXpSQnJnMitZWHVEejhDblB2cUtxTStZSS9rc1RwUVd5UkQwZUVHTzdibDJ5YzQ1Sm5qZFI4Rjd4djVwdDlzanZ6UUc1TURiQm8rSzdWZ2ZOTnRuSy8vRUFDVVFBUUFDQVFRQ0FnSURBUUFBQUFBQUFBRUFFU0V4UVZGaGNZR1JvUkN4d2RIaDhQL2FBQWdCQVFBQlB4RFhRNk5aUk81TWFSeWEwdnhmNGJyUkdVSUd0UUxkQ0NtNUNXcjhUTVZYTExsVStZVGlDTTQvQ1dLN2dCbVJtb3lPR2RVMGdqTHNCTkU2STI4Y1VkbFNwVnlnL1YvZzFxZEJMcm9nSEVWWFFSTXd3cXRPWnVvS2hhaENjTUtxWnFJVzNCU3JoTklFYXFmZ0h5eXJkL0E0d3hyY0JNUDVvL0RNNmxRZWMzcHZJMGhCai9hSVFZazlDYTlKRHBOaEVXekRNWE51TlJxQnA5SVJOaEI1aG1raHlPQmI1a0tLbWFGR2JKY3dlZ2lHOXhJUUtDTGc1c1FEU045RFk0eVNWb1pnWmxMSUcvNGM2Q1hBcXdMRDFwRGdtck9xUU9rREYzalpqZlpsaVNRTWJZNGkzRURwQlhiTytRVFcwQ2xuVXVPSnFVcE0wcERjSm1yaVRiaUd6VnFMdUlaQW94TlNVSEpmeFhyTW9XUE1KamtqWFlwZUxtckUyaEcxa0pBM1pSVks5S3d2dHpnL3VDQXMwVzN0TVFVTDVxZllSNDF0bS9WTGk4YkdYc0dXRGJ1RVg3WlMramRRUHpXMzl0SmRYUFNnWVMwTmwvdVMrZUxTM3hHYnZ1UHJUY1c4YUNmbU9kMUQ5Ni8xSEhkb0Yvd1ZOZ1dVYit4S0oxMnpmWi9NY25uTi93Q1pVOEd0SFBZUlJGZzZJWDRTSXc0Vy9YQ1pobzEvWkRGYUsydVpaYUhtK2xmeExwYzBTYjRYS3NMMFRKcGNxVjhSMnhCZ3lmdVhxWk5MUS9VTkduZDlNZk5RNnRxaktFNWpaKzREVjlmMVk4dEdGUnpiSWZEbW1EeFpCZTRxOEptcjFwRUdxSXJhOG9ydnBSYk1ia1p5Vk01L1NEakEzVlI3d1JGUUFhV2ZJeGR4TlFmVVhiN2Vpc2RvMXljSmxROHYrNE1DUHViUDNlV0RlTzJDRlRlOVMxWlBVclZha1FyRkdpbzRKWkdCcVhZVGFydGZ3bWNiZ1A4QTJCVTlSVDVHYWc1UG5nUjhCMEllU3crMGR3bVZoTHVZclNqWVdpTlcvd0F5ZzREM1p0aUdSN0pVZGFxWmVWc215UVBWMk5YT2I1bEpVaEhzTmxZUzgyaGExdXBnZE1jQ2libnRjUkpDKzV0YWRaVXhFcnNkK0xMamtaSTN3dnFqcTJKaU5OeURaUmRxa1dvWEVYRG15VDBNK2J2NXlTeVdOVlJFdFpySlVzZzUyV0pxZTB3aHIwTWNDRFVqZkFuRGpDMWhqc2FjWkNtVVBPSFlIc0hUWGFPS0duejhZOGxPeXd2Q3ZhQ1RjQlVhbm9TWEt1QTdXRDM5Q2pPZ3NvOGE3aDI5M3lGdmFVamRIeGxNN3JtQ0toM0FHdmlYNDF4aXdKVEVKaEM4WWJ3SFNESkVzaXVEQ1VIUDZrWFY5NlVZd0NkdDM5UXJWUEdrRHNFYk85bUdVTllhV293TzVDeHVubTRyRFhkQjlSVVRxZGJyd1llczJEQVlCWmF2TVhkdms1SkJza1JNZkh5bG04TzFmYUVUdXV5WmU4eHhETzhFcUVncW1VbHpHQmkxc2FRT2hNeElEM3h5dVB1V1lKQjZsQjk3SXRxMmNKZXNvYkpEQ2RZcEV3d2IxQkswTlUxaGdSc2FpWVdBYVBVRG5QTGNpYUFJTUtleU1YS0x5ak1FV1Z4STNsSWJEbGJBWnRCcDhybU10d1pQcmZwaVVUQVBFdUpWQzJ5YzFDRkJwQ1VRZHdGcThNTTJnWXVLV3l5cEFZZjBKbHREb2tjNGhrNWVSS29rRG1XVnVNMzJvYkFKQnVQQ1lFRjY3SEVaWjY0dmVBS0NUTkV1VTFnMy9DRnlBVWExTEpFR0grdG1PUkZsbnBrMEVSTk9wVFBLdjRnYXV4eU5TT1VhRFVyczJOb1gwQmxYVW1lazY5djRBN0tJQkxuQjRqdzB1bzY3UXhlME5McVdWdkJsaXM4WVN3VjhBZ3hXT1d6VTNuckVscVZFbExnaTNDY1Vua2d4a2dPcjNPSll1STAvY2wwMkNYR2RZTEtsOHlaSURoVTYrSXdMcmV5SHhJL0NUVFZnRzY0QTU1Ykw5ekM5enBqSUJQRVdxVTdJNGhhOFI3VVRSQ1dFZkFqSDZNZW4yQ1BoTGlLUk96ekdJdk5SY1NaNGZFcVVLQW9FN1V4OVZZQWdnMURoWVNhbkJrU1VuN3FQVkhUOE4yNlNvaXlZeGNzSVc1TXFrUm1tMWltdHdNZXRjaEtjUHNsb1U4UlJNZmlQbi9FMVNHSHlqRDJtdW9KUFlOcHFWWmJmMUFLaHlzUkZLOWtHcS8zRVZiZXRZM2xiQnV4ckNPelhGUjl3S1lnS0VBTENVck11cnRIbG94M0xoeG1KRGNDRWJpWHpWNm1lTmpxQ211SW5FWUF0dW9ubTAvVEtyWGhzaks4Y21PR2UyYmZpQUlCUEVLV1E3aVN0MTZvNi9jcFV6cU40TU5UblJHWENKRTFJMTJIcURLd3FTRUxOd3hOV0dwVmg2STlSQmdQS0NDWEx0NjVqNmNoc0crQ0dBcytJRkY4cFR4clZWOVhEM0dsWWRyS2JVWGhtem15OG1DNmxPYkpPdWRsSDJTOHM0b2k0VnpQZHhKTDJtdWRkU1ZoVGszKzJCaTZMQklQZUVOYXhSQTEwbDlzR1JtYmNKK0lFZFhSYUZ1YU5XVjBaMnhpTFN4c29hV0l6cm90YjVwVTlUUmJ2c3VhSWp0M2JHdlVhR0dROHlEYVhVWGtzdElNcXFyM0tpWnJGWVZMS25talM3Z3QrdzRaZzNEc2hoWk9LekgwQUR2YWcvTXN0TkdWbDZlMGZDVjBXSUN4emVMV3JpV1JGaGZGeTlVQ0VwaXBCU0drRGVwUnRTdmJDMGNzRU1wL3BwcStrQXF2RkVDbG1KdEdPaXg3VFExTm13WDRiUE1ORHdXR0MrVXBBNnNVWTMrWTZHSGlGMmtiaTliOFFEck8zM01EU0ErYXpDeWhVTU40T3VscWovTXRGUUVyVjl5c2tHaWY2bVhuaG9zNzVpQXdmTS8vRUFDSVJBQUlDQVFRREFRRUJBQUFBQUFBQUFBRUNBeE1BQkFZUkVnVVFGQlVXSVAvYUFBZ0JBZ0VCQWdBUzIzWFhXM1cyM1hXMldXV1hYMzMzV1dkKzlsdHRsdHR0L3dCSDBYblVYM1hYWDMzMjNDUVNpVzIwekdhM3ZaYmNaak5iZGRkZGRjSjdyaE9KNzd4UHoyc012Y3lHV3d5R1c0em1hMjYyMjYrNFRpZThUQ2NnNFRoQkdja24wU2ZSd25ua3RaWlpjSlJPWStoanFyYU13bU14MUdHc3hWR013MVY5REhWVlVJekVORitPdmlHOFFmRER3VVcxbjJrdTE1TnVIYmY0QTIzL0FDWTJ2RnRtWGJEYmJUYkEyNy9ObmFwMmRGdGIrWGZSdkNYR3RpMThPdTA4cUtkTk5wWnZIemFMNFVnSGpZdkZEUko0bE52THRoZkJmanQ0bC9GeWVQTWpCNGpCOG55cEJIQU1XZk90Ykltckc0bDNKQnVEVCtWRTNKYVI1R0o5Y0JWVlZpUXd0by9qR2krTTZaNFprbGtBUTZiVXdlVWg4bE41S1RWWWY4REFGTUJCRW94QnlTY21YeU9tN1F6Uk9rcXUyRndRTTV3RUZHUjFsUmppUHljT05rOFdvMHZYVFRLeUpnd0gwU1hNaXpDWHVzOFdwWFVyS2txeWhtRDVPa3NWY0xvNjZnSHFEY1piQ3dZTUg0VkVXTmdBUkxhN1NHUkRGR2kra0t5akMzUFBJWlpFZE1VQmdSTGQzTEdWNUdOb2tFZ2tCUXJuUFlNRDZHQjQ5U2tvSFBibnVXT0hDM3J0WWpyS1NUeUNDR1VnY0tZNWhQaEdFODRjS25PUVFleEI5Y0RCZ0lJT0RBY0RkK3hadFI5UjFaMW4xZlRZRzQ0STQ0d1lNQkdEQXJTL1VkUzA1eXFxcXNqaFQ2SS94d001T3BPbzAycGZUL00ybHdrbnYzN2R3QXFMLzhRQU5SRUFBZ0VDQkFNR0F3WUhBQUFBQUFBQUFBRUNFU0VEQkJBeEJTQmhFakpCVVlHUklqQnhFek9Db2JIUkJpTkNRM0tTOFAvYUFBZ0JBZ0VEUHdCQzFZOU91aTBaSTZuVWlpSXRHU0h0eUxsUkJDRVdHUHowNmlRdVpqb1BXZy9BWXluanBiNW5VNm5VNmo4eXVpNUxiNk1zVzFmbnEwZGRYeUk2NldIcXhqb1BSajBwcTlHaGpKREhRYUg1RDFma0pqR1cySHBUUmpHU0hveGt4a2tNNGRpYlc5VEtVM2I5VVpOS3RHMzFNdTIyNHVuUXdISzFmY3lENzhtamhzNy9BR3JmME1qRmZESnN5TUkxbEdiOWFIRGV6WHNTUnc2amQvZW42bkRJdDl0eVgvZERnTTRmZVgvRlU0VHZIRTkyMGNMVXQxL3M2R1RoTHRZY0l2M2Y2MU1tMDVTd2xYMC9ZNFpXazR0ZlEvaDZLK1B0ZTV3RnVtSGhOK3RUZ2tPL2d2MWY3TTRianA5aUVVdjhxTXlVSlY3TmZ4bkNGSCtaaHIxYmY2TTRFL0dDK3ZiSW9VZGljSFptUEZia0l2NDRWT0ZTNzJIUTRVKzdiME10TmZDeUZMRTVKcWhLN2FJcDJSTGRHSXJPTlRHbXJRWm5ZdXNJczRqMktTaWw5V1pxZThmekp6WHh1aGtsM2szNm1TaXU0aktSL3RyMk1sVHVMMk1xdG9vd3R4TkNrSXFUZDBZeThETUxhTE0zMVgxWm1vUDd4bWFqYjdSa3Bic3FpVEg0bWF5LzNjL2U1bTRWVS95SkozdVYzUmc0cUlTRUpEYWRCTHZOR1hsL1h5TFJFR2hTM1Fuc1I4eUxXNVFvU295VUU2UkU1TkNyNUU0azhMWkRTbzBRa3Q2R0ZDMGJzeDhYZDBYVG5zU3FOYmxyTWFzMFJrVXJRWXFDdUtnbjhTSndkSHBIZEVHaExTcUl0TXFxOHlpSmJFWEdqWkYrSWxkTWV6RXhrcURGSk5GQ1dHeE4wa1E3VnhTVlJsR1dzeGFJZmdNZWswck1tbVRKbGJqR0pvUWhOT3hSdXhSMVExYVJLTjRzcjNrUmxzVVQwUWhVNVBNVDBjU200bm9sdVFhSWVZdk1VaWpzSVZLUFJ4WlMwaFBuaStTdzRvVEtvYXFKRGw0bEU2RFcraVlxNldkU2l0OGhva3R5RXZFYVJUVmplNVlsb3FhVTBhVExYNW1JV2pST0pVVEVNZklocGErUkpMa2FIejIwcG9oSlhNR0c4a1pkK0pncFdZaDN1WXNpYkpOVitZekR3OTJRSFRZeDU3T2hON3M2NlI4V0x6SWVaSHdZblZJNmZKb3FtR2lmZXFZazVVa3FtVmVFM1NqSnBmREpmbVppRmFvb3gxSkVtTHlKTHdKTWJaYXBKV1AveEFBaUVRQUNBZ0VGQVFFQkFRRUFBQUFBQUFBQkF3SVRBQVFGQmhFU0VCVWdGQmIvMmdBSUFRTUJBUUlBQ3dxb0txcENRb0xDcWdvS0NhcVFta0pxRUt4QVE4QlFVRWhRVFJRRVVCSVVFaE5OTklWVlVGMStQUGdSQ1FnSkNncWtJQ0FnSW9DS0FnSnBwcXJyOEdIbng1Q3drSkNRa0pDQWdJQ1FnSnBxcnJycXFLcXFhcTY0ckNvckVJcmlxS2dxS1FtS2FLYWFhYXFxYWFhS0tLQmdJTVNERUFSakVSQUFYVlhWVlZWVlZUVFRSUU5lTjZPOVEzbU8rSGtNK1hxNWllV0w1UXZrNDVLZVVEbC8vVk41S25rZzVGUGt2NzQ1QU9TamxVK1JEa0M5VkNZV2RGUGIzN2U1TXNpOU9xUnVLZFdOWE52NkxOME90L1VPK3k1Q2Q0L1ZodVN0ZXJXeFhESU1EeHFmOUVuVFlSSkF5SjdneWVqUEhUeDF1d2FqYkNuSUJXS0VWL08rNUdlU210MFhCNTFFWGhrR0praGM0U3pVS2RvV2FOTzNxMG95UDhFK2prNHhpVjlHUmlESEZTMEdvR09VNU05T0ZRd1E2d2Z4S0pnWUJaaDRLL0l5T1FPbGNqVWR2VklHV0RQR0NQbnlJZVBFWUJja1QweGdWbFhVQ3JFVFd3VFpDY1NxT1JrWUJZV0ZpUFJBamhZWk1pYzZyQzRCV0xrR1NuUEJnSFdDQUhYa1JyS3o4SXl1Z0tpSXJndUlLeW9vcGlNQUVRT3V2cGpKSmgwQUlpSWhHTVpDUUl5SjgxU1JMVERCblhYbm9qNGNrc3JHQWlZSXdEQVl6QkdkZEQ0TUIvanJ5YzZ6d0lDSXlJQUVRb0tDaERvWUNQZ1BmOEVsa1Q1aEVMR0JsbGdtREU0TUgwWVBveXp2dzFNWEJvYUl4aUlpQWgxNTdCSi8vOFFBTlJFQUFnRUNCQU1HQkFNSkFBQUFBQUFBQUFFQ0VTRURCQkF4QlNCQkVpSlJZWUdSTURKeGdrS3g4UVlUVXBLaG90SGg4UC9hQUFnQkF3RURQd0JqMWRCaktES2kwOGgrQXg2b1MwWEl4akdPbkloTFYrQTlGUVFoQ0Z5dm5YVVZOSHA1RDhOR01ZeGxPWmMxT2Q2UGxzTVo1Y2kwWEl0SDBHTThqeTFZeDhxRUlSQkNFUll0RlRSaldxRm9oQ0VMVGlXSHZmME01dFJMMFpuWlc3U1M4a1psUnRKZXFNeW91dFBZNGlyeGdwVS83eFJ4S0Z2M1NYMS9VNGpMZUVVY1RuYU00TDBxY1cyN2NYNkk0cXJPbnNjV2NlNzJhL1EvYUNPSTMyTGZiUTR4dExEL0FLSEZ1enQvYWpQVGk0NGs1TDJYNVVNL1hzeHhYVDEveWNTUzdzay9xY2ZrNlFVVjZIR2xlZUlsNkhGNS9MaXIyLzBaL0NYZmszOXBtWlJwV24ybkVwUzdrNitpWDVvNHZTOFpQK1VrOXl0bVljbGRHRExvU2FwQ2REaWthOW5FcWNVaXFTZFRNeCtaRXEzSVJaSFpOazVMZGtkbVJwVk13WU84MFpXYXBLU01pcFZqSnYwTXJCV2I5aU1Qa1ZmcVp4N05JenIvQUJNemIvRy9jemY4Yjl6TXZlVE1iYTR5aUdoaU1QcXpBYWZlUmx2cVphVmU0akt0MTdDOWhMNWJDVlJYRXRqS1pqNTRYOHJHVmxlS3FZUkZSc3pFd1d5Y0JqYUVuY2sxM0V6TlJWZXp5dWhJbW1OUnZvNkRRaFVyVVJoemQyTXNJaE1neFIyTVdWNVVTTURDMlZXZWhWOGpFSzlDMVR5RXlVU3F2cEpJbHV6dmJEN05HSjNFN2trMlloMkt1bW5acVNxUGxxVFl5Z3h2UnJSQ2pzT0pWQ2FMTmpLTVY2RlV4ZENnMk1xK1NIVWcwUW9KSFF1NjZORnRIRjFMV1ltcmtXblFyWmxuMldTdVBxTm9lajVXa01SWnVKSVpLV3hpSmt5U1JPSzJFME5GYTZWUlF0YmxXa2xwWFJpbFhTZ3VvbmRhS2dtVkhVbWlTV20vd0V4ZENVZCtYeUdob1dxdVZGUVRMMjFzTG5UR2hqb0o2SnJXbXcweFV0cFRTbnhFOVdVM1pKN0lrdWhQd01VbDRpVzdJSkNWdmhwSzVoK0k1YkluUW0rcGhyZXJJTFpGZWd2QWxUWWZnVDhDWkpmQ2hjazlrS1ZtS0VhcG1OQ2RLMVJIWnBtSExabG14VTNFaUtPcVltdHlLTGZyb3R6LzJRPT0iIGlkPSJjIiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIvPjwvZGVmcz48L3N2Zz4=", + "description": "A beneficial layer in the upper atmosphere, but harmful when present near ground level. Results displays mainly from vehicle exhaust and industrial emissions by combining the latest and aggregated values and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'ozone', label: 'Ozone', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'ozone', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Ozone\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 250) {\\n\\tvalue = 250;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":50,\"color\":\"#3B911C\"},{\"from\":50,\"to\":100,\"color\":\"#7CC322\"},{\"from\":100,\"to\":130,\"color\":\"#F89E0D\"},{\"from\":130,\"to\":240,\"color\":\"#F77410\"},{\"from\":240,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:c2ltcGxlLW96b25lLXZhbHVlLWFuZC1jaGFydC1jYXJkLWJhY2tncm91bmQucG5n:c2ltcGxlLW96b25lLXZhbHVlLWFuZC1jaGFydC1jYXJkLWJhY2tncm91bmQucG5n;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQoAAACACAMAAADESdfxAAADAFBMVEWep8SdpsKgqMUsN1Ouq72jqsYoNVP30L1RY4ewrr+3tsf30bgvOVYmL0soLkT3zL8oMk3cxcf31LUmM1EkME731LiztMm8usuxssbAxNckK0ckLUn45cP46Mb32LW5uMnjxsRHXojFxtf34MLfxsalqb+an7hSZoxMX4O0ssSyrLxEW4SdorzWw8c3SXAqNFBLYYqvu9aur8SysMGuqrqhpr/568qXnLTxy8EmK0HdzMegqsftycL33LY4THWzu9KgordXaIyotdHGxNL45cqao8CqrMErM0d/e4y5wde9wNS3vtR5c4OEo9BseZnYxseqqbz34LhBV4H34clmdpizvtiuuNK+vs/22sPYy8mjrMi2tMXjy8TpzML31Lxca4xyeJSqudb34b2TmLGjstCussj32Lz21MDox8NOZY17d4iHqdeqr8Z0fJn45b6/x9t/otOcqspvaXltksfo0sVWZYinrcaBfo/iz8eQla6MkqpzdIrBwdNrbIArOViNrtqkttb33caEi6e4vNB8g589U31rZXXt0MJLWn5gcZQ0PlqYsdjLx9OHj6q6xd3z0MBzl8qkpbpmZ3w0O1aBptbSwsqWoL1tdZL3276rscro1slTYYE6T3owQWVrcIzd0Mudttp6n9CRnLqAnsxMTWEzOU6Trtfu18amq8QyRWxGSVx0bX3FyduAiKRycYN0gaD33Ll5eI3i08tkbYo3PVEuPl+wtcuer898iqh9f5dhaIViY3eMl7VsfZ+0qrN3m81TapLPzdrMwcyJocwjKESMqNOestX679HYzszz1sRBUHfIzeBTXXxbXnWVs9svN0vEwM++yeCCeYdGVnxhXW/KydinrsmspK5NV3aFhp6Bj66mnqqfmKRcY4BnYXI6Q13y3sl0haZVWnOTq9KZrM5UUWOPpc2LjKOHgY+SjJuIhJQ8QFVBRVlpjsOVqMyHk7GZkp+NiJbt28tUVmtcWGm5yuOkutyywt5DTWnQydBNU2uqvd07R2eWpciTkqd8lcH47fs3AAAwAklEQVR42pSUIW/bYBCGw4YDxtxqUktHp8lSWXgUhcRSGhCQDAUZhAYZhlolgabW/sSaf5HfEFa25/VdYn/O17V77u69c5mfORu8xjgc6JDf6t8x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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Ozone\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"public\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"µg/m³\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "O3", + "ozone" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_pm10_chart_card.json b/application/src/main/data/json/system/widget_types/simple_pm10_chart_card.json new file mode 100644 index 0000000000..1b0c1126df --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_pm10_chart_card.json @@ -0,0 +1,40 @@ +{ + "fqn": "simple_pm10_chart_card", + "name": "Simple PM10 chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAhFBMVEUAAADf39/g4ODf39/f39/g4OD////k5OT/pgDg4OAhISH/9N//3Z90dHT/vEBYWFiQkJD/6b//0oD/sSDy8vIvLy//+u//qxA8PDzHx8eCgoK6urr/x2CsrKz/zXBmZmb/78+rq6v/2JDV1dX/tzCenp7/wk//469KSkr/x1/29vb/2Y/dngZsAAAABnRSTlMAIL9AEN/GQiaNAAAEoElEQVR42uzPwQ2AIBAAsBMQc3+mYP8BjRv4BNJu0AAAAAAAAPivXZtr8akln83lXSOunH17I2uU0Q8wS2Q/QoosRmQ1Ii/7daxiMQhEYbg7AweGsVAwkRAkCHn/J9wkF26ysNYa1r9Suw9UtLcGpLcGpLcGpLcGpLf+BURwl1R13oBFNxypegBTAeCdqaB5dYjsjOk7c3RuZYLSTgEpcOEaa1zW2P6XWYdEksHfEABm0HiuaTghkxngmSGc0Lg6xPMs/YKsBrVQgDBTPrAPggsaV4cg8EieW0tZoOYMS5xeApHAkkk63BAzzYCa52S6vASykhGSNjwguFKDKeUJSfBMaFkd4kiDT/gTkmi4ITDbHNvfv5Uzkp2HsuBbibiaV/g9I0U5VSsAiQwZzTshlUpsv2HGE+W9DUhvDUhvDUhvDUhvDUhvDUhvDUhv9Q1JFhhUXg9R7rPTwPxyiKPDkexhezXkhz07Wk4UhsIAfHf4J4FAEkgQlbKUis6+//stJhS2U6FwwRRn/C80RDqTD3NIqAGLfCPN4qeGnNk7+RRZ+syQt5z6HNj5mSFZNDbjJ4Skh7T/Go5E49x6NkgaMcYKN3jmB+9Rh2eDRCw5RndJkMdfptn+Iek5OaZfb7kJy6MsC4gm5tYqCDdj23DaLH8yxsYfKyMWuM44fxsWwXFurYfw0gJaeUutAXtaZLloQ98jBazgU6ve9UCH/HPtziemkFsT10O4BrQFBHVRgLVAY352cDQPe215g5haLFI3Tg84T174DxYFRGmSs+wtWAy5QUsnaIk4cDEUAupnSI2QvsdITgQ9u1hEvgbyfGbjxeJrxuIkylmyFAKcqItFSXcC79pi4pKaStIQDe4bXFbUpZLGn6QEanqUd3bsa6C7zmkxt88NijguAqL7addgcbEPkBKNv9ggl9A3VP9mAR0CTiMdFlACgOahBVA6l9BaTcyYYLjgecaSpTeI/LgC0gLt8FWEAP8GcfUjNHrIDaGDWNenYYVF12W4IWpg5vdTQRIVB1qYIL8uhdTqBCiah3BAmA7QQzSMg2jfdyHitnuVaKS0mCiRrR91NYCGz0Jcu+ra0kOkryzv7z+6QBCVAGz9uET+bA1pQ6Wh+TxEwdIIuaHtIeP5J/fXvG3N1CPHphAf7or9MgM5QY8QA00TkLlHju0hbpzuZfKuVcKOkBCntZAgjzaFcCAkXx5OYNy8aQYIf1gjAnItpGDBphCyrs4rty4aoHQ21UPcsdFA92rdOC93CIemlZCEJbQt5C9gRQNX7FQCQljX9hB/DPgTtWvLrl+thBSdY2MIhRpd+h2r6tsDRAP2MpQKbHmHNODrIJFzbF7slZRm3PVJPhqB7tgYzj93VaYFKj6MmLsnGfe5f9vesf4J0UNoCK/J1YimKqxoebxjR5AGWikxvcWfrY89QbiG336shnzQb0N4GNJ/kXUdGtpBfv+/KC/IC7LvvCB7ywuyt7wge8sLsrf8a+cObgCEYSAIGltE8p8q6L9AWkDiQRLNdHAF3BoyG0Nm05EbpIPGuDKq/791fnb3EVHn+lmqrJ1CYQAAAAAAAK88ZP333TfpRkYAAAAASUVORK5CYII=", + "description": "Displays historical fine and coarse particulate matter (PM10) values as a simplified chart. Optionally may display the corresponding latest PM10 value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pm10', label: 'PM10', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'pm10', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM10\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#80C32C\"},{\"from\":20,\"to\":50,\"color\":\"#FFA600\"},{\"from\":50,\"to\":150,\"color\":\"#F36900\"},{\"from\":150,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"PM10\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"bubble_chart\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"µg/m³\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "pm10", + "coarse particulates", + "coarse particles", + "particulate matter 10", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_pm10_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_pm10_chart_card_with_background.json new file mode 100644 index 0000000000..59e71a6fa3 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_pm10_chart_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "simple_pm10_chart_card_with_background", + "name": "Simple PM10 chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical fine and coarse particulate matter (PM10) values as a simplified chart with background. Optionally may display the corresponding latest PM10 value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pm10', label: 'PM10', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'pm10', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM10\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#7CC322\"},{\"from\":20,\"to\":50,\"color\":\"#F89E0D\"},{\"from\":50,\"to\":150,\"color\":\"#F77410\"},{\"from\":150,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"PM10\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"bubble_chart\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"µg/m³\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "pm10", + "coarse particulates", + "coarse particles", + "particulate matter 10", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_pm2_5_chart_card.json b/application/src/main/data/json/system/widget_types/simple_pm2_5_chart_card.json new file mode 100644 index 0000000000..e4c1232dd4 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_pm2_5_chart_card.json @@ -0,0 +1,41 @@ +{ + "fqn": "simple_pm2_5_chart_card", + "name": "Simple PM2.5 chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAhFBMVEUAAADf39/g4ODf39/f39/g4OD////k5OSAwyzg4OAhISF0dHTv9+U8PDzP6a+QkJDx8fFYWFjf8Mr3+/Kg0mEvLy+/4ZWQy0aIxzmCgoLHx8e6urqv2XtKSkrV1dWenp7H5aOsrKzn9Nirq6u43YhmZmaYzlOo1m7X7L2n1m719fWQykb4eF0NAAAABnRSTlMAIL9AEN/GQiaNAAAEq0lEQVR42uzPwQ2AIBAAsBMQcz/mYP8BjRv4BNJu0AAAAAAAAPivXZtr8akln83lXSOunH17I2uU0Q8wS2Q/QoosRmQ1Ii+7daxiMQhEYbg7A2cQncIiShQJef9X3GsWcndhmxQbDNcPLHSqHyxmNDNkNDNkNDNkNDNkNB8RInjLqroUoGnBi2qALKaCg+9TXHJfiKx0+bx5er8zQ2kAEilwa1tjQBdt4BBHMoZ3CAAzqOtvGimZBf10TLjsrpDALv8K2Q1qsQJxoeCl8SgVqvqAS24LQeSL/Pxaygo1b2guHRNxHl1ZlxoN19wSIpF1I+nxDjHTDVALTKaN0jsUp8qAK+4J2UkHyQUnTxzUYErpIWFd8a1koA0Z4klDyPgzJNPQQ9aYUxLUhMQq5nDJPSHYfICy4lQdDsuOsG7ITsR1Fc4D3tEEl9wU0lWX8RwfsaI8ygwZzQwZzQwZzQwZzQwZzQwZzQz5d6F53+TxIWHhQeXZIeK4FEAWOnl0iDLhUKI9OeSLPXPbbRwEAujbZITBFzABX+vNxWnU//+/nZgs9taOam+1riPlPGAzUIlTGEBxzqL+97L8iUUCHft3nTyxiAjAE7H4aUVyVoAnZtGTidRF7lcWDEjEU4nsEsaYKIYrq0/3JxKJhY7yQrC6y4riryadbVgkP4sq2A1Tuu502DlIPidFpuNviCgO/5GAaRowO4LDf2qJs0onx4enynKRxiDacpbLxSgYI1O06cO/j1jkPrfWPrCDxyS6/keRPWmQiplhwrGdjNpTiekX50Ssg35CHkPN+i2+3YcTzVjya74IR7wqCBFP8CUNhjBGSQ6AHw/Pidyv/hkTQiYBY1WlWZJFmWDVbq7IO+KBHim2MIU6SPAY5Hd7eQDiIJXrtE+xgWn8OVGz5GZSsAC+IM+Sc+b0j4JF8TyRELvRXTD1dbfg3MMiGgp1NrLrg0jDpigPLd7nkafG7GEace7PCBFECatiWADNjyhmiSh7Gww3VI5FqLSpwbtIiWEnYruYoSaLFFJcAbSopgfCjv37WTOx+Bv9TlTzkr1BvJQWWzUW4YipIoG7iEHViRgXu1IHS6XEVkqLMEX3JfXbzBNRJd44wFgkdGHpRCSW4JYW+KZuRZ4Q0TYwSVJRsY5Ii6YJSUaORKiw0IuU+D4Qcanltl3+/q5gGpbBt1mU7Fc0Y5ESP3oRRT0eiDymcJvvGiIXbL3PZ5HTcEZCLJeLCAHECiL+v94MRPhkjqQoF4tEbs9aRYQMQgBlBkvr1NWRStuN83IT4dRhoUic3W+AK4i4IaepRWy8iKujszTdu6T4fqHIrvIea4gAL9Ftn17EUPXqUwXt6SbSIl8mUgt9BMcqIoSSkg93MaorxfmfW5W6Xce4HzHnikrX7h6THLWowbOGiMeLgIc3bmMzcAgPsIRoeG/9eZEWzX6fusW0iIxl0PPzItz4/Fko8gYjVhbhYQgDZNOECjbGVn4Oeom8RLbKS2RrvES2xktka7xEtsbvdu6gBkAgBoBgacMl/aED/wKxwPOumXGwBlbIboTspiMHrIPWejKqB8yD3r4i6j5/S5U1aRQGAAAAAADwywcmOfeID1PhnQAAAABJRU5ErkJggg==", + "description": "Displays historical fine particulate matter (PM2.5) values as a simplified chart. Optionally may display the corresponding latest PM2.5 value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pm2.5', label: 'PM2.5', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'pm2.5', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM2.5\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 120 - 60;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 120 - 60;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#80C32C\"},{\"from\":10,\"to\":35,\"color\":\"#FFA600\"},{\"from\":35,\"to\":75,\"color\":\"#F36900\"},{\"from\":75,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"PM2.5\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"bubble_chart\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"µg/m³\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "pm2.5", + "particulate", + "matter", + "air", + "fine particulates", + "fine particles", + "particulate matter 2.5", + "airborne particles", + "microscopic particles", + "respirable particles", + "aerosol particles", + "dust particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_pm2_5_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_pm2_5_chart_card_with_background.json new file mode 100644 index 0000000000..20d719cfb4 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_pm2_5_chart_card_with_background.json @@ -0,0 +1,41 @@ +{ + "fqn": "simple_pm2_5_chart_card_with_background", + "name": "Simple PM2.5 chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical fine particulate matter (PM2.5) values as a simplified chart with background. Optionally may display the corresponding latest PM2.5 value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pm2.5', label: 'PM2.5', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'pm2.5', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"PM2.5\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 120 - 60;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 120 - 60;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 500) {\\n\\tvalue = 500;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":10,\"color\":\"#7CC322\"},{\"from\":10,\"to\":35,\"color\":\"#F89E0D\"},{\"from\":35,\"to\":75,\"color\":\"#F77410\"},{\"from\":75,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"PM2.5\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"bubble_chart\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"µg/m³\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "pm2.5", + "particulate", + "matter", + "air", + "fine particulates", + "fine particles", + "particulate matter 2.5", + "airborne particles", + "microscopic particles", + "respirable particles", + "aerosol particles", + "dust particles" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_power_consumption_chart_card.json b/application/src/main/data/json/system/widget_types/simple_power_consumption_chart_card.json new file mode 100644 index 0000000000..c35b7f3388 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_power_consumption_chart_card.json @@ -0,0 +1,35 @@ +{ + "fqn": "simple_power_consumption_chart_card", + "name": "Simple power consumption chart card", + "deprecated": false, + "image": "tb-image:c2ltcGxlX3Bvd2VyX2NvbnN1bXB0aW9uX2NoYXJ0X2NhcmRfKDEpLnN2Zw==:c2ltcGxlX3Bvd2VyX2NvbnN1bXB0aW9uX2NoYXJ0X2NhcmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjExMiIgeT0iMjQiIGZpbGw9IiNmZmYiIHJ4PSI0Ii8+PHJlY3Qgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxMTEiIHg9Ii41IiB5PSIyNC41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik0yMC44IDQ1LjNoLTIuNnYtMS40aDIuNmMuNCAwIC44IDAgMS0uMi40LS4xLjYtLjMuNy0uNmwuMi0uOWMwLS4zIDAtLjYtLjItLjktLjEtLjItLjMtLjUtLjYtLjZhMiAyIDAgMCAwLTEuMS0uM2gtMlY0OUgxN1YzOWgzLjhjLjggMCAxLjQuMiAyIC41LjUuMiAxIC42IDEuMiAxIC4zLjYuNCAxIC40IDEuNyAwIC42LS4xIDEuMi0uNCAxLjctLjMuNC0uNy44LTEuMiAxLS42LjMtMS4yLjQtMiAuNFptNC44IDBjMC0uNi4xLTEuMS4zLTEuNmwuNi0xLjJhMyAzIDAgMCAxIDEuMS0uN2MuNC0uMiAxLS4zIDEuNS0uM3MxIDAgMS40LjNjLjQuMS44LjQgMS4xLjdsLjcgMS4yLjIgMS41di4yYzAgLjUgMCAxLS4yIDEuNWwtLjcgMS4yYTMuMSAzLjEgMCAwIDEtMi41IDFjLS42IDAtMSAwLTEuNS0uMmEzLjYgMy42IDAgMCAxLTEuOC0ybC0uMi0xLjVabTEuNyAwdjFsLjQuOGMuMS4yLjMuNC42LjUuMi4yLjUuMi44LjJhMS42IDEuNiAwIDAgMCAxLjMtLjdjLjItLjIuMy0uNS4zLS44bC4yLTFjMC0uNCAwLS43LS4yLTEgMC0uMy0uMS0uNi0uMy0uOGExLjYgMS42IDAgMCAwLTEuMy0uN2MtLjMgMC0uNiAwLS44LjItLjMuMS0uNS4zLS42LjUtLjEuMi0uMy41LS4zLjhsLS4xIDFabTguOCAyIDEuOC01LjdoMWwtLjMgMS43TDM3IDQ5aC0xbC4yLTEuNlptLTEtNS43IDEuNCA1LjhWNDloLTFsLTItNy40aDEuNlptNS40IDUuNyAxLjMtNS43aDEuNmwtMiA3LjRoLTFsLjEtMS43Wm0tMS40LTUuNyAxLjcgNS43LjIgMS43aC0xbC0xLjctNS43LS4zLTEuN2gxWm05IDcuNWMtLjYgMC0xLjEgMC0xLjUtLjJhMy4zIDMuMyAwIDAgMS0xLjktMiA0IDQgMCAwIDEtLjItMS4zdi0uM2MwLS42IDAtMS4xLjItMS42cy40LS45LjctMS4yYTMgMyAwIDAgMSAxLjEtLjhsMS4zLS4yYy42IDAgMSAwIDEuNC4yYTMuMSAzLjEgMCAwIDEgMS42IDEuOWwuMiAxLjV2LjdoLTUuN3YtMS4yaDR2LS4xbC0uMS0uOS0uNS0uNi0uOS0uMmMtLjIgMC0uNSAwLS43LjItLjIgMC0uNC4yLS41LjVsLS40Ljd2Mi4zbC41LjcuNi41LjguMUEyLjMgMi4zIDAgMCAwIDUwIDQ3bC45LjktLjcuNmMtLjIuMi0uNS40LS45LjUtLjQuMi0uOC4yLTEuMy4yWm02LTZWNDloLTEuNnYtNy40aDEuNlY0M1ptMi40LTEuNVY0M2EzLjIgMy4yIDAgMCAwLTEuNSAwIDEuNCAxLjQgMCAwIDAtLjggMWwtLjIuNmgtLjRjMC0uNCAwLS44LjItMS4yIDAtLjQuMi0uOC40LTEgLjItLjQuNC0uNi43LS44YTEuOSAxLjkgMCAwIDEgMS4zLS4yaC4zWm04IDYuMi43LS4xLjUtLjUuMy0uNmgxLjVjMCAuNC0uMS45LS40IDEuM2EzIDMgMCAwIDEtMSAuOWMtLjUuMi0xIC4zLTEuNi4zLS42IDAtMSAwLTEuNS0uM2EzIDMgMCAwIDEtMS0uOGMtLjMtLjMtLjUtLjctLjYtMS4xLS4yLS41LS4yLTEtLjItMS41di0uMmMwLS41IDAtMSAuMi0xLjUuMS0uNC4zLS44LjYtMS4xLjMtLjQuNi0uNiAxLS44bDEuNS0uM2MuNiAwIDEuMSAwIDEuNi4zLjQuMy44LjYgMSAxIC4zLjQuNC45LjQgMS40SDY2bC0uMi0uNy0uNS0uNWMtLjMtLjItLjUtLjItLjgtLjItLjMgMC0uNiAwLS44LjItLjMuMS0uNC4zLS42LjVsLS4yLjgtLjEuOXYxLjJsLjMuN2MuMi4yLjMuNC42LjUuMi4yLjUuMi44LjJabTQuMi0yLjR2LS4ybC4zLTEuNS42LTEuMmEzIDMgMCAwIDEgMS4xLS43Yy40LS4yIDEtLjMgMS41LS4zczEgMCAxLjQuM2MuNC4xLjguNCAxIC43LjQuNC42LjguOCAxLjJsLjIgMS41di4yYzAgLjUgMCAxLS4yIDEuNWwtLjcgMS4yYTMuMSAzLjEgMCAwIDEtMi41IDFjLS42IDAtMSAwLTEuNS0uMmEzLjUgMy41IDAgMCAxLTEuOC0ybC0uMi0xLjVabTEuNy0uMnYxLjJsLjQuN2MuMS4yLjMuNC42LjUuMi4yLjQuMi44LjJhMS42IDEuNiAwIDAgMCAxLjMtLjdjLjItLjIuMy0uNS4zLS44bC4xLTF2LTFsLS40LS44YTEuNiAxLjYgMCAwIDAtMS4zLS43Yy0uNCAwLS42IDAtLjkuMmwtLjUuNWMtLjIuMi0uMy41LS4zLjhsLS4xIDFabTguNS0yVjQ5aC0xLjd2LTcuNGgxLjZ2MS42Wm0tLjMgMS44SDc4YzAtLjUuMS0xIC4zLTEuNC4xLS41LjMtLjguNi0xLjFhMi43IDIuNyAwIDAgMSAyLTFsMSAuMS44LjUuNS44LjEgMS4zVjQ5aC0xLjZ2LTQuOGMwLS4zIDAtLjYtLjItLjhhLjkuOSAwIDAgMC0uNC0uNWwtLjctLjFhMS42IDEuNiAwIDAgMC0xLjQuN2wtLjMuNy0uMS44Wm0xMC44IDItLjEtLjRhMSAxIDAgMCAwLS41LS40bC0xLS4zLTEtLjNjLS40LS4xLS43LS4zLS45LS41LS4yLS4xLS40LS4zLS41LS42LS4yLS4yLS4yLS41LS4yLS44IDAtLjMgMC0uNi4yLS45bC41LS43IDEtLjUgMS4xLS4xYy42IDAgMS4yIDAgMS42LjMuNS4yLjguNSAxIC44LjMuNC40LjguNCAxLjJoLTEuN2wtLjEtLjVhMSAxIDAgMCAwLS40LS41bC0uOC0uMS0uNy4xYTEgMSAwIDAgMC0uNC40LjkuOSAwIDAgMCAwIC44bC4yLjIuNS4yLjcuMiAxLjUuNWMuNC4yLjcuNC45LjcuMi4zLjMuNy4zIDEuMWEyIDIgMCAwIDEtLjggMS42IDMgMyAwIDAgMS0xIC41TDg4IDQ5Yy0uNiAwLTEuMiAwLTEuNy0uMy0uNC0uMy0uOC0uNi0xLTEtLjMtLjMtLjQtLjctLjQtMS4xaDEuNmMwIC4zLjEuNS4zLjdsLjUuNC44LjEuNy0uMWMuMiAwIC40LS4yLjUtLjNsLjEtLjVabTcuOC4zdi01LjdoMS42VjQ5aC0xLjVsLS4xLTEuN1ptLjItMS42aC42YzAgLjUgMCAxLS4yIDEuNGwtLjUgMWMtLjIuNC0uNS42LS45LjhhMyAzIDAgMCAxLTEuMy4yIDMgMyAwIDAgMS0xLS4xIDIgMiAwIDAgMS0uNy0uNWMtLjItLjItLjQtLjUtLjUtLjlhNCA0IDAgMCAxLS4yLTEuMnYtNC44aDEuNnY0LjhsLjEuNy4zLjQuNC4yLjQuMWMuNSAwIC45IDAgMS4xLS4zLjMtLjIuNS0uNC42LS43bC4yLTFabTUtMi42VjQ5aC0xLjZ2LTcuNGgxLjVsLjEgMS41Wm0tLjMgMmgtLjVjMC0uNiAwLTEgLjItMS41bC41LTEuMSAxLS44YTMgMyAwIDAgMSAxLjItLjJsMSAuMS43LjUuNS44LjEgMS4yVjQ5aC0xLjZ2LTQuOGMwLS4zIDAtLjYtLjItLjhhLjkuOSAwIDAgMC0uNC0uNWwtLjctLjFjLS4zIDAtLjYgMC0uOC4yLS4yIDAtLjQuMy0uNi41bC0uMy43di44Wm00LjYtLjUtLjcuMi4xLTEuMy42LTFhMi42IDIuNiAwIDAgMSAyLjEtMWwxIC4xYy40LjEuNi4zLjguNS4yLjIuNC41LjUuOWwuMiAxLjJWNDloLTEuN3YtNC44YzAtLjQgMC0uNy0uMi0uOWEuOS45IDAgMCAwLS40LS40bC0uNy0uMS0uNy4xLS41LjQtLjMuNnYuN1pNMTE1IDQzdjguOGgtMS43VjQxLjZoMS41bC4yIDEuNFptNC44IDIuMnYuMmMwIC41IDAgMS0uMiAxLjVsLS42IDEuMmMtLjIuMy0uNS42LS45LjhsLTEuMi4yYy0uNCAwLS44IDAtMS4yLS4ybC0uOS0uN2E0IDQgMCAwIDEtLjUtMS4yYy0uMi0uNC0uMy0uOS0uMy0xLjR2LS41YzAtLjYuMS0xIC4zLTEuNWwuNS0xLjFhMi41IDIuNSAwIDAgMSAyLjEtMWMuNSAwIC45IDAgMS4yLjJsMSAuOC41IDEuMi4yIDEuNVptLTEuNy4ydi0xLjJsLS40LS43YzAtLjItLjMtLjQtLjUtLjVsLS43LS4yLS44LjEtLjUuNGMtLjIuMi0uMy40LS4zLjdsLS4yLjdWNDZjMCAuNC4xLjcuMyAxbC41LjYgMSAuMmMuMyAwIC41IDAgLjctLjIuMiAwIC40LS4zLjUtLjUuMi0uMi4zLS41LjMtLjhsLjEtMVptNi43LTMuOHYxLjJoLTQuMXYtMS4yaDQuMVptLTMtMS44aDEuN1Y0N2wuMS41LjMuMmguNGEyLjUgMi41IDAgMCAwIC42IDBWNDlhNC4yIDQuMiAwIDAgMS0xLjEuMWMtLjQgMC0uNyAwLTEtLjItLjMtLjEtLjUtLjMtLjctLjYtLjItLjMtLjItLjctLjItMS4ydi03LjNabTYuMyAxLjhWNDloLTEuNnYtNy40aDEuNlptLTEuOC0yIC4zLS42LjctLjJjLjMgMCAuNSAwIC43LjIuMi4yLjIuNC4yLjcgMCAuMiAwIC40LS4yLjZhMSAxIDAgMCAxLS43LjIgMSAxIDAgMCAxLS43LS4yLjguOCAwIDAgMS0uMy0uNlptMy42IDUuOHYtLjJjMC0uNSAwLTEgLjItMS41LjEtLjQuNC0uOC43LTEuMmEzIDMgMCAwIDEgMS0uN2wxLjUtLjNjLjUgMCAxIDAgMS40LjMuNS4xLjguNCAxLjEuN2wuNyAxLjJjLjIuNS4yIDEgLjIgMS41di4yYzAgLjUgMCAxLS4yIDEuNWwtLjcgMS4yYTMuMiAzLjIgMCAwIDEtMi41IDFjLS41IDAtMSAwLTEuNS0uMmwtMS0uOGMtLjMtLjQtLjYtLjgtLjctMS4yLS4yLS41LS4yLTEtLjItMS41Wm0xLjYtLjJ2LjJsLjEgMSAuMy43LjYuNWMuMi4yLjUuMi44LjJhMS42IDEuNiAwIDAgMCAxLjQtLjdsLjMtLjh2LTJjMC0uMy0uMi0uNi0uNC0uOGExLjYgMS42IDAgMCAwLTEuMy0uN2MtLjMgMC0uNiAwLS44LjJsLS42LjUtLjMuOC0uMSAxWm04LjUtMlY0OWgtMS43di03LjRoMS42bC4xIDEuNlptLS4zIDEuOGgtLjVjMC0uNSAwLTEgLjItMS40LjEtLjUuMy0uOC42LTEuMWEyLjYgMi42IDAgMCAxIDIuMS0xbDEgLjEuNy41LjUuOC4yIDEuM1Y0OWgtMS43di00LjhsLS4xLS44YS45LjkgMCAwIDAtLjUtLjVsLS43LS4xYTEuNiAxLjYgMCAwIDAtMS4zLjdsLS40LjctLjEuOFoiLz48cGF0aCBmaWxsPSIjM0ZBNzFBIiBkPSJNMjUuNSA5NnYyMGgtMy4zdi0xNmwtNC45IDEuNnYtMi43bDcuOC0yLjloLjRaTTM2LjMgOTh2MThoLTIuOFY5OGgyLjhabTcuOSA1LjMtNS41IDYuMS0zIDMuMS0uOC0yLjQgMi4zLTIuOCAzLjYtNGgzLjRaTTQxLjQgMTE2bC00LjEtNiAxLjctMiA1LjYgOGgtMy4yWm05LjEtMy44TDU0IDk4LjloMS44bC0uMiAzLjRMNTIgMTE2SDUwbC41LTMuOFpNNDggOTguOWwyLjcgMTMuMi4zIDMuOWgtMmwtNC0xN2gzWk01OS42IDExMmwyLjctMTNoM2wtNCAxN2gtMmwuMy00Wm0tMy4yLTEzIDMuNCAxMy4yLjUgMy44aC0xLjlsLTMuNi0xMy43LS4yLTMuNGgxLjhaIi8+PHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjkiIHN0cm9rZS13aWR0aD0iMS41IiBkPSJNMTg3IDU0Yy0xNiAwLTE0IDU2LjEtMzAuNiA1Ni4xLTE0IDAtMTYuMy0yMy44LTI3LjEtMjMuOFMxMTMuNyAxMTkgOTkuNyAxMTljLTExLjUgMC0xNy45LTYuNi0yOC43LTYuNiIvPjwvc3ZnPg==", + "description": "Displays historical power consumption values as a simplified chart. Optionally may display the corresponding latest power consumption value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'powerConsumption', label: 'Power consumption', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'powerConsumption', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Power consumption\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":5,\"color\":\"#3FA71A\"},{\"from\":5,\"to\":15,\"color\":\"#F36900\"},{\"from\":15,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Power consumption\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"bolt\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"units\":\"kW\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "power", + "energy", + "energy usage", + "electric load", + "electricity", + "power efficiency", + "load profile" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_power_consumption_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_power_consumption_chart_card_with_background.json new file mode 100644 index 0000000000..0bb9f2d998 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_power_consumption_chart_card_with_background.json @@ -0,0 +1,35 @@ +{ + "fqn": "simple_power_consumption_chart_card_with_background", + "name": "Simple power consumption chart card with background", + "deprecated": false, + "image": "tb-image:c2ltcGxlX3Bvd2VyX2NvbnN1bXB0aW9uX2NoYXJ0X2NhcmRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==:c2ltcGxlX3Bvd2VyX2NvbnN1bXB0aW9uX2NoYXJ0X2NhcmRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxMTIiIHk9IjI0IiBmaWxsPSJ1cmwoI2EpIiByeD0iNCIvPjxnIGZpbHRlcj0idXJsKCNiKSI+PHJlY3Qgd2lkdGg9IjE4OCIgaGVpZ2h0PSIxMDAiIHg9IjYiIHk9IjMwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii44IiByeD0iMyIvPjwvZz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNMjAuOCA0NS4zaC0yLjZ2LTEuNGgyLjZjLjQgMCAuOCAwIDEtLjIuNC0uMS42LS4zLjctLjZsLjItLjljMC0uMyAwLS42LS4yLS45LS4xLS4yLS4zLS41LS42LS42YTIgMiAwIDAgMC0xLjEtLjNoLTJWNDlIMTdWMzloMy44Yy44IDAgMS40LjIgMiAuNS41LjIgMSAuNiAxLjIgMSAuMy42LjQgMSAuNCAxLjcgMCAuNi0uMSAxLjItLjQgMS43LS4zLjQtLjcuOC0xLjIgMS0uNi4zLTEuMi40LTIgLjRabTQuOCAwYzAtLjYuMS0xLjEuMy0xLjZsLjYtMS4yYTMgMyAwIDAgMSAxLjEtLjdjLjQtLjIgMS0uMyAxLjUtLjNzMSAwIDEuNC4zYy40LjEuOC40IDEuMS43bC43IDEuMi4yIDEuNXYuMmMwIC41IDAgMS0uMiAxLjVsLS43IDEuMmEzLjEgMy4xIDAgMCAxLTIuNSAxYy0uNiAwLTEgMC0xLjUtLjJhMy42IDMuNiAwIDAgMS0xLjgtMmwtLjItMS41Wm0xLjcgMHYxbC40LjhjLjEuMi4zLjQuNi41LjIuMi41LjIuOC4yYTEuNiAxLjYgMCAwIDAgMS4zLS43Yy4yLS4yLjMtLjUuMy0uOGwuMi0xYzAtLjQgMC0uNy0uMi0xIDAtLjMtLjEtLjYtLjMtLjhhMS42IDEuNiAwIDAgMC0xLjMtLjdjLS4zIDAtLjYgMC0uOC4yLS4zLjEtLjUuMy0uNi41LS4xLjItLjMuNS0uMy44bC0uMSAxWm04LjggMiAxLjgtNS43aDFsLS4zIDEuN0wzNyA0OWgtMWwuMi0xLjZabS0xLTUuNyAxLjQgNS44VjQ5aC0xbC0yLTcuNGgxLjZabTUuNCA1LjcgMS4zLTUuN2gxLjZsLTIgNy40aC0xbC4xLTEuN1ptLTEuNC01LjcgMS43IDUuNy4yIDEuN2gtMWwtMS43LTUuNy0uMy0xLjdoMVptOSA3LjVjLS42IDAtMS4xIDAtMS41LS4yYTMuMyAzLjMgMCAwIDEtMS45LTIgNCA0IDAgMCAxLS4yLTEuM3YtLjNjMC0uNiAwLTEuMS4yLTEuNnMuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS4xLS44bDEuMy0uMmMuNiAwIDEgMCAxLjQuMmEzLjEgMy4xIDAgMCAxIDEuNiAxLjlsLjIgMS41di43aC01Ljd2LTEuMmg0di0uMWwtLjEtLjktLjUtLjYtLjktLjJjLS4yIDAtLjUgMC0uNy4yLS4yIDAtLjQuMi0uNS41bC0uNC43djIuM2wuNS43LjYuNS44LjFBMi4zIDIuMyAwIDAgMCA1MCA0N2wuOS45LS43LjZjLS4yLjItLjUuNC0uOS41LS40LjItLjguMi0xLjMuMlptNi02VjQ5aC0xLjZ2LTcuNGgxLjZWNDNabTIuNC0xLjVWNDNhMy4yIDMuMiAwIDAgMC0xLjUgMCAxLjQgMS40IDAgMCAwLS44IDFsLS4yLjZoLS40YzAtLjQgMC0uOC4yLTEuMiAwLS40LjItLjguNC0xIC4yLS40LjQtLjYuNy0uOGExLjkgMS45IDAgMCAxIDEuMy0uMmguM1ptOCA2LjIuNy0uMS41LS41LjMtLjZoMS41YzAgLjQtLjEuOS0uNCAxLjNhMyAzIDAgMCAxLTEgLjljLS41LjItMSAuMy0xLjYuMy0uNiAwLTEgMC0xLjUtLjNhMyAzIDAgMCAxLTEtLjhjLS4zLS4zLS41LS43LS42LTEuMS0uMi0uNS0uMi0xLS4yLTEuNXYtLjJjMC0uNSAwLTEgLjItMS41LjEtLjQuMy0uOC42LTEuMS4zLS40LjYtLjYgMS0uOGwxLjUtLjNjLjYgMCAxLjEgMCAxLjYuMy40LjMuOC42IDEgMSAuMy40LjQuOS40IDEuNEg2NmwtLjItLjctLjUtLjVjLS4zLS4yLS41LS4yLS44LS4yLS4zIDAtLjYgMC0uOC4yLS4zLjEtLjQuMy0uNi41bC0uMi44LS4xLjl2MS4ybC4zLjdjLjIuMi4zLjQuNi41LjIuMi41LjIuOC4yWm00LjItMi40di0uMmwuMy0xLjUuNi0xLjJhMyAzIDAgMCAxIDEuMS0uN2MuNC0uMiAxLS4zIDEuNS0uM3MxIDAgMS40LjNjLjQuMS44LjQgMSAuNy40LjQuNi44LjggMS4ybC4yIDEuNXYuMmMwIC41IDAgMS0uMiAxLjVsLS43IDEuMmEzLjEgMy4xIDAgMCAxLTIuNSAxYy0uNiAwLTEgMC0xLjUtLjJhMy41IDMuNSAwIDAgMS0xLjgtMmwtLjItMS41Wm0xLjctLjJ2MS4ybC40LjdjLjEuMi4zLjQuNi41LjIuMi40LjIuOC4yYTEuNiAxLjYgMCAwIDAgMS4zLS43Yy4yLS4yLjMtLjUuMy0uOGwuMS0xdi0xbC0uNC0uOGExLjYgMS42IDAgMCAwLTEuMy0uN2MtLjQgMC0uNiAwLS45LjJsLS41LjVjLS4yLjItLjMuNS0uMy44bC0uMSAxWm04LjUtMlY0OWgtMS43di03LjRoMS42djEuNlptLS4zIDEuOEg3OGMwLS41LjEtMSAuMy0xLjQuMS0uNS4zLS44LjYtMS4xYTIuNyAyLjcgMCAwIDEgMi0xbDEgLjEuOC41LjUuOC4xIDEuM1Y0OWgtMS42di00LjhjMC0uMyAwLS42LS4yLS44YS45LjkgMCAwIDAtLjQtLjVsLS43LS4xYTEuNiAxLjYgMCAwIDAtMS40LjdsLS4zLjctLjEuOFptMTAuOCAyLS4xLS40YTEgMSAwIDAgMC0uNS0uNGwtMS0uMy0xLS4zYy0uNC0uMS0uNy0uMy0uOS0uNS0uMi0uMS0uNC0uMy0uNS0uNi0uMi0uMi0uMi0uNS0uMi0uOCAwLS4zIDAtLjYuMi0uOWwuNS0uNyAxLS41IDEuMS0uMWMuNiAwIDEuMiAwIDEuNi4zLjUuMi44LjUgMSAuOC4zLjQuNC44LjQgMS4yaC0xLjdsLS4xLS41YTEgMSAwIDAgMC0uNC0uNWwtLjgtLjEtLjcuMWExIDEgMCAwIDAtLjQuNC45LjkgMCAwIDAgMCAuOGwuMi4yLjUuMi43LjIgMS41LjVjLjQuMi43LjQuOS43LjIuMy4zLjcuMyAxLjFhMiAyIDAgMCAxLS44IDEuNiAzIDMgMCAwIDEtMSAuNUw4OCA0OWMtLjYgMC0xLjIgMC0xLjctLjMtLjQtLjMtLjgtLjYtMS0xLS4zLS4zLS40LS43LS40LTEuMWgxLjZjMCAuMy4xLjUuMy43bC41LjQuOC4xLjctLjFjLjIgMCAuNC0uMi41LS4zbC4xLS41Wm03LjguM3YtNS43aDEuNlY0OWgtMS41bC0uMS0xLjdabS4yLTEuNmguNmMwIC41IDAgMS0uMiAxLjRsLS41IDFjLS4yLjQtLjUuNi0uOS44YTMgMyAwIDAgMS0xLjMuMiAzIDMgMCAwIDEtMS0uMSAyIDIgMCAwIDEtLjctLjVjLS4yLS4yLS40LS41LS41LS45YTQgNCAwIDAgMS0uMi0xLjJ2LTQuOGgxLjZ2NC44bC4xLjcuMy40LjQuMi40LjFjLjUgMCAuOSAwIDEuMS0uMy4zLS4yLjUtLjQuNi0uN2wuMi0xWm01LTIuNlY0OWgtMS42di03LjRoMS41bC4xIDEuNVptLS4zIDJoLS41YzAtLjYgMC0xIC4yLTEuNWwuNS0xLjEgMS0uOGEzIDMgMCAwIDEgMS4yLS4ybDEgLjEuNy41LjUuOC4xIDEuMlY0OWgtMS42di00LjhjMC0uMyAwLS42LS4yLS44YS45LjkgMCAwIDAtLjQtLjVsLS43LS4xYy0uMyAwLS42IDAtLjguMi0uMiAwLS40LjMtLjYuNWwtLjMuN3YuOFptNC42LS41LS43LjIuMS0xLjMuNi0xYTIuNiAyLjYgMCAwIDEgMi4xLTFsMSAuMWMuNC4xLjYuMy44LjUuMi4yLjQuNS41LjlsLjIgMS4yVjQ5aC0xLjd2LTQuOGMwLS40IDAtLjctLjItLjlhLjkuOSAwIDAgMC0uNC0uNGwtLjctLjEtLjcuMS0uNS40LS4zLjZ2LjdaTTExNSA0M3Y4LjhoLTEuN1Y0MS42aDEuNWwuMiAxLjRabTQuOCAyLjJ2LjJjMCAuNSAwIDEtLjIgMS41bC0uNiAxLjJjLS4yLjMtLjUuNi0uOS44bC0xLjIuMmMtLjQgMC0uOCAwLTEuMi0uMmwtLjktLjdhNCA0IDAgMCAxLS41LTEuMmMtLjItLjQtLjMtLjktLjMtMS40di0uNWMwLS42LjEtMSAuMy0xLjVsLjUtMS4xYTIuNSAyLjUgMCAwIDEgMi4xLTFjLjUgMCAuOSAwIDEuMi4ybDEgLjguNSAxLjIuMiAxLjVabS0xLjcuMnYtMS4ybC0uNC0uN2MwLS4yLS4zLS40LS41LS41bC0uNy0uMi0uOC4xLS41LjRjLS4yLjItLjMuNC0uMy43bC0uMi43VjQ2YzAgLjQuMS43LjMgMWwuNS42IDEgLjJjLjMgMCAuNSAwIC43LS4yLjIgMCAuNC0uMy41LS41LjItLjIuMy0uNS4zLS44bC4xLTFabTYuNy0zLjh2MS4yaC00LjF2LTEuMmg0LjFabS0zLTEuOGgxLjdWNDdsLjEuNS4zLjJoLjRhMi41IDIuNSAwIDAgMCAuNiAwVjQ5YTQuMiA0LjIgMCAwIDEtMS4xLjFjLS40IDAtLjcgMC0xLS4yLS4zLS4xLS41LS4zLS43LS42LS4yLS4zLS4yLS43LS4yLTEuMnYtNy4zWm02LjMgMS44VjQ5aC0xLjZ2LTcuNGgxLjZabS0xLjgtMiAuMy0uNi43LS4yYy4zIDAgLjUgMCAuNy4yLjIuMi4yLjQuMi43IDAgLjIgMCAuNC0uMi42YTEgMSAwIDAgMS0uNy4yIDEgMSAwIDAgMS0uNy0uMi44LjggMCAwIDEtLjMtLjZabTMuNiA1Ljh2LS4yYzAtLjUgMC0xIC4yLTEuNS4xLS40LjQtLjguNy0xLjJhMyAzIDAgMCAxIDEtLjdsMS41LS4zYy41IDAgMSAwIDEuNC4zLjUuMS44LjQgMS4xLjdsLjcgMS4yYy4yLjUuMiAxIC4yIDEuNXYuMmMwIC41IDAgMS0uMiAxLjVsLS43IDEuMmEzLjIgMy4yIDAgMCAxLTIuNSAxYy0uNSAwLTEgMC0xLjUtLjJsLTEtLjhjLS4zLS40LS42LS44LS43LTEuMi0uMi0uNS0uMi0xLS4yLTEuNVptMS42LS4ydi4ybC4xIDEgLjMuNy42LjVjLjIuMi41LjIuOC4yYTEuNiAxLjYgMCAwIDAgMS40LS43bC4zLS44di0yYzAtLjMtLjItLjYtLjQtLjhhMS42IDEuNiAwIDAgMC0xLjMtLjdjLS4zIDAtLjYgMC0uOC4ybC0uNi41LS4zLjgtLjEgMVptOC41LTJWNDloLTEuN3YtNy40aDEuNmwuMSAxLjZabS0uMyAxLjhoLS41YzAtLjUgMC0xIC4yLTEuNC4xLS41LjMtLjguNi0xLjFhMi42IDIuNiAwIDAgMSAyLjEtMWwxIC4xLjcuNS41LjguMiAxLjNWNDloLTEuN3YtNC44bC0uMS0uOGEuOS45IDAgMCAwLS41LS41bC0uNy0uMWExLjYgMS42IDAgMCAwLTEuMy43bC0uNC43LS4xLjhaIi8+PHBhdGggZmlsbD0iIzNCOTExQyIgZD0iTTI1LjUgOTZ2MjBoLTMuM3YtMTZsLTQuOSAxLjZ2LTIuN2w3LjgtMi45aC40Wk0zNi4zIDk4djE4aC0yLjhWOThoMi44Wm03LjkgNS4zLTUuNSA2LjEtMyAzLjEtLjgtMi40IDIuMy0yLjggMy42LTRoMy40Wk00MS40IDExNmwtNC4xLTYgMS43LTIgNS42IDhoLTMuMlptOS4xLTMuOEw1NCA5OC45aDEuOGwtLjIgMy40TDUyIDExNkg1MGwuNS0zLjhaTTQ4IDk4LjlsMi43IDEzLjIuMyAzLjloLTJsLTQtMTdoM1pNNTkuNiAxMTJsMi43LTEzaDNsLTQgMTdoLTJsLjMtNFptLTMuMi0xMyAzLjQgMTMuMi41IDMuOGgtMS45bC0zLjYtMTMuNy0uMi0zLjRoMS44WiIvPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii45IiBzdHJva2Utd2lkdGg9IjEuNSIgZD0iTTE4NyA1NGMtMTYgMC0xNCA1Ni4xLTMwLjYgNTYuMS0xNCAwLTE2LjMtMjMuOC0yNy4xLTIzLjhTMTEzLjcgMTE5IDk5LjcgMTE5Yy0xMS41IDAtMTcuOS02LjYtMjguNy02LjYiLz48ZGVmcz48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2MiIHRyYW5zZm9ybT0ibWF0cml4KC4wMDUwMjUxMyAwIDAgLjAwODk3MzQ0IDAgLS4yKSIvPjwvcGF0dGVybj48ZmlsdGVyIGlkPSJiIiB3aWR0aD0iMTk3IiBoZWlnaHQ9IjEwOSIgeD0iMS41IiB5PSIyNS41IiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIyLjMiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA4NV82ODY0OSIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA4NV82ODY0OSIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFJQkFRRUJBUUlCQVFFQ0FnSUNBZ1FEQWdJQ0FnVUVCQU1FQmdVR0JnWUZCZ1lHQndrSUJnY0pCd1lHQ0FzSUNRb0tDZ29LQmdnTERBc0tEQWtLQ2dyLzJ3QkRBUUlDQWdJQ0FnVURBd1VLQndZSENnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ3Ivd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhRQUFBUVFEQVFFQUFBQUFBQUFBQUFBQUJ3TUZCZ2dCQWdRQUNmL0VBQnNCQUFFRkFRRUFBQUFBQUFBQUFBQUFBQUlBQVFNRUJRWUgvOW9BREFNQkFBSVFBeEFBQUFENk0rL2NlbVFhRUtaaWlUY2tnamFyTlQ3bXRYbWtyM2Y2ckprMXdFaUZNbTFKa3lTWkJvYmF1eVR0NTJ3bThuOGw1TEtjcjhkdkpFMnB4Nk95RGp6U3RYdkl1MWR6WjJHamJ0RHZacGoyYU12dlFwdXlSaW03Sm1LUng2dStpZkR0bE41UDVMeVdFNVc1RGJUSnRYSFFtNHpCWmxWeWhPSUtkdUExWk9wTllmWHFUVzlETExjTXJzMWtqV2hNbVE2RzJIV0UyRTNrdkovSmVTS2ZJN1dISFFsekcyVU9XWVZSR0xLZGdkUkdWYmxlR1F5RENHUmdZN203K1pOcnRmcWxEUjFxNjFkWWRZWnZKWVM1WFpzRnpmeXV4b1NUZGtTRlZQaG1Ga0NJZk9UVncxSWJHN0loSW9BakNRU3pORTczNFpuWWpJbDJDY1dZVlNXcVRTN0pNT2lmVWhYU2NTRXBjbnQ2b2VVa3VsaDFoT0RhN2RPUFhaN09vZEloRjNXWXBER2NQaTBQcnl3R09YZ1pSOTN0SHBaOHFtVHNhODdJa09YYlZMTElvOHRzODdzc3k4NjFUYUpWMFl3dEVmUUEyNXUxM3FjTW1JYWhNcVdCR3B4Q1dONmlaRit6TjZBcjNZU2RQRTR1d3JGRm1SdDJmZGtRZWExZk10M2Z5V2pyVW1CYkVMQ1RIQTl1cnRYeUxzSVlGSExRdkcwcmpiZVdSSlJpTTFXTkJMVkNsTXpzZHk5T3JnbU5DRG5Sb3BFbm50RlJMWk5xbHE2UmR3UEltRm5SQmpYTUR5azJrdUJtb3BuMmI5Yk9lelF6QitLZDFzMHlKTUZXNmNvdGluRktmNm9YcWorejl6dkxjUzlsTHhEcWx6cGNib1ZHNDFUemNGTHA0cGdJckdtdG0rYm1YZStqT3hub01VY0NTV3lSMW1vMnkzYWdaSFlPU0RhaVZwUURLTVVueTdIbVd3cmdkYk91cElFdVFkTTNsQ1h5WXBGQmhLRVJ5VVV4ZDM2TjczT3F1VFM0N09xWFVMTnVMMElNdHBnemJFODFBQWxHUXlzTnNxUzFTNVdYVXk4NkZReWh1Y0JJYXRERUQwaWs2YVFvSU5IUFVmTDZDL3V4ek9EVE1seGtOTm9KclFYNEtsRXF1UnpIS3pGWmNYSVZJeW5VUHRKK2xtWVdUTUxoTUQ3YlFBSWp0VFVVUk16dUlQN2pDM2VvZExvTDE2UE5WNGM2ZUl6ZFlpaGRleWZyY1VuME0zNTA1Vmt5Mkd2Vk5EUWFPZTNtZGN5N1JjZ2tVRmlTRkRVMnRvRmllb0RKVlpHb1FBYWE5N1YvTTQ0TXEyMWRxNmVuelVCMUt2emVyeWZTemRvaDdBMWVJYlFPbHBWa2lleDB0bTJQVGMxUXlPYjY2Y2wwQzhhNFJXMGN6S2NWQWF1Z1c3OUtIUkZjY0ErYkVGcjZRSFZmcFkvbkd4dVRhOWxlazVSeW1qK1lPUmIrbnVoWEdsSzQvd0JTNVRiVnhoN3I1M1puYlRKdVlMa1ovWmZqT3pVQXRXSGVJbDNqb1RCSVJ6aEVaUGFZRjg4YWsvME11MVpSTkRUV0tRWVZOdjZDVzhWclpmSkNrZjJXMXFndG1FRlFXeE1GYXJTbUtMV1YwRWxpc2ZSN3VNcXYrWWN4TTBVM1BFcXlZRm8wMmFBRWFleGRsVjh3ZEc0MjFUbE1vQXVnNEJ6OXI2VmIrRkJRVk5jMnhkblRxaWFBb0UwNEVyeHcrcmROK3BteGZSeHJLNnVmU0dlb1Z0R2hGSjY4Y3hOazI5ZnlOV1BLdlVldld5WjlHL3R6RmIrSjdXdy9vL24rOUsyRHNUYjE1dnM3QytwZU94REUyeEJ6dlNFdnFlWDF5OUdPUmFOZXNlMUxjbmZsL1Y4bndQRWRPazU0TWMxcnlUTHZ0a2xoR1JLNnVDRU9IN3VVNkVFWmhHWjYyVjBZdTR2MGZOT3pCR3FXZzZaZlJyZEx3akhpN2tSbWxaekdUMTRXWldoRWNaVnh0dUs2dURFaWtmSUE3S084NlJHMHBuU1Y4d0dzTHdPSzBVTE1JS2lobThVdlJGTU1MTU82VmdZYlZhcHFlQ2VYTkgwMTVSM0lFbUdTTUZDU1ZJSldEZ0pjc2dmL3hBQXdFQUFDQWdJQkF3TUVBUUlHQXdBQUFBQUNBd0VFQUFVUkJoSVRGQ0VpRUNNeE1oVkJVUWNXSkNVek5DQW1ZZi9hQUFnQkFRQUJDQVBqT000empPTUwrMGJPNGlsVU9UdUw1Q0hYdFFld2l2TnZZVkdWSEk3NlhHY1p4bkdjWnhuR2NaeG5HY1p4bkdjWnhuR2NaeG5IMGdmNnoxRlJYYVVtWjhSN0xhTXREV3Z4WVlLckd2N3FyV1Z4RzJIZDQ3SEdjWnhuR2NmVGpPTTQrdkdjWnhuR2NaeG5HY2N6bkdiL0FGU2JSTGVmeVhaN0w4VjZsK2xKU2x6ZGJUbVFSWVZ0dUpXazdDaGlXTHVJUG1KNHpqT000empPTTR6ak9NNHpqT000empPTW4rMGNaeG0wVEQ0U3FSTHpXNWFhZGFwZFFiWXJDM3IzRTA3U1lzV2djYnp2YWxzbmRxcGk3ZVZzcnNQc0E0UllKQ2Y2OFp4bkdjWnhuR2NaeGhFSWZ0NWhuOU9NNCtrUi9YT000elpyWWZoQlZiVkF5WnZWNnl5R3BGVWhIZ0l6WjFLcGNHTnlqYVRXOVc1ZEV0dTMrWG0weTVVNEI0a3QzYmVxK3JsVFBIWWlSS080ZU00enlxalBKTS9ydytjOEhQN2loUS9qajZjWnh6bkdjWnhteXZWbFdGMTVTOWlMWjE0MTdsSFNFYkd5c0xXc1NxMUsvay8xTGVNT2dxMmRnU3MwcktnR1paU1Zac2crcjZwbXNpYTE4TmxkdEpDMXJoTVNPUWJGVkVlK2R2SDQ0ejIrbnRudG5HVC9BR3pqT000empOMlZhdSt2YnNMbllyWWM1VnRkbXFXM0JEN1VTUS9ybkdSZVJYdHRVNFpFeDdnMkNLVEo1YUdwdEpJckJ4VXQyWWh0U05qWXBNbU5uWGQ0MEI0VnVCbnRrL2puTlQvd2tvdTNPTTQra1JuSDE0elowMTNybGRUUCsxWWFDZFJUWXVzdTVaVEhkWEhGVCtRd3BnWTk2YXZteGg5SVRjdkx2R1NGMnFuM0hKc0lzZXlyTk92Y3M5ajJVSEJZODhkdnB5R2RaNnNHY3EzUHFUaFI5MUtZOG53NHp1Q1B6NVZaeG5IMTR5ZmJMY3krOTZiREFWM1d4R3VMbWlxZ2RJZUt3amhLZ3A3czdCR2ZkWVRMZm4wRXJ0L2xCeGY2OFpZcW9mSExabXpWcy9ZQzE2b29DeVZHcVl3Sk42YkFEV2diU1RwbFBvbGN1a0hXVTFsZUllWVVzZnh4L3dDTXpBNTJ5WHVUZUJjODg3WGc4KzZsRU0xaXJBNjh2OU44dmtXUU1SK0FqNzJmNGRjZWZjREJSSUYzajk0OGlna2JIZmtMRTErSm0rdC81ZHJMdDFaZTFObVBWN1ZGRFlWbG0xTmFWd0F0cWR2cFY5dWNmWGpKbjM3UmdQNnppZVlzT3Zac0xDL1Z1Wml4c2RzT3JhdnVsWitUak9NZTd3ZVJzZERJU3Z6dlZBUkg0NHdvKzRPVEVpWGNQWDN6NmY3bzdTODR0bmJLWEVlVU5iVnMyRDdFb3YwMEs3SFcrbzdNTzdhOVk5NCtGak9majgrNVpFY1ovd0RjYnRWUllWVVRyMHZ1TDdjMmlVVW0yaGdIc1NzYURhQ29UWWVBUHUxNi9zZlpNL3RiV1BZM2pvZ1E5TEJaeG5HTWo1RGpiTlZCZHJ2OFJKQXVrTFdBNENycGJuVkIydlNFNnBwZHJONUhxVzlMcmhoWDB1OUJQbTdvVWRXandEUWFMUjdrd1A4QVdjZTd4UkhIcDViNzJkdE1CdTlmSlZpaXRXUWtOMDNzTjBzanNhbnZUVUI1N05xTHZoV3VPeFVlOGM1YS9CNTBmMndnWWozeTV2bHJiQVVuZFhYcnQxZFdnclh2MkljMnR4Uk9scUpWaXdhZFZSRmFVNzBaQm5ScXZUZFBXWVowMDlQODV1RUQzTGdlUjYzdEdOWkRJR3M4Y0k3U1I1d0xPd05oZVZkejdrSGg3VVErTWJ1Nlo3Q0hIcnJWTXJFSmR0djlROWhpMnBLblRacDlQYkd6dGI2cldSTm52d0hIQS9LeGJxTk0xNTBrd0lURXdWbjQ5dytPbDNTNXQ0UC9BRzRiSUw4aFI3OVR1U1duT1luYnBxMEZwSGM5WU9TSGhucGxGKzZtN1ducHlKaTY4U2JEUHpIV2dRM1RHdXZ6eDdaSDk4NStSUm43SE9NcFVwajMzUzB4dlBEbnBlKzEyTjNNTUswcE9lc0dFem5SUHdzbzRzM0dVRlMreDV5WEplUk8yWEhXbGhwOU92V2lsM3RIWWErZjFKME9qN1c1dFcwZFREYVAwYlJqdVBmMXlqVk41MTNwNTFjZC9YSFkyaXJOSnNWcTIxOUdWV0ZGaTFDdHBiMmF6RlpYYWpWVmZJY0ZHZVQrd2w3ek9DZnpMQ0tKakx2ejZtRWNXK2FETTJuZW5iMTNPdUxNcTdMeDlMUXluc2tJbnFBLzl1amhWbGx6eXhsSnpoNnlzVm9xY0hwN0s1aWxRSUJrb29hMlQ3ZzZyckJYM0pFTjY0NnJWT3lqZCtzYnBuRVhaWWQwdlpsTzRSVkhtYkdwSXE3ZzJJVGJNN0pPRzBWNTZ2VnZzYXpkK0h5TzlURVJ6aXRwdTJiRWE3QTJsdDF0bE5hYnpsa2ZtMlBVRDZuL0FBczJiVTdhYnRvZDNjVXYxQ3JWOHJHM3J6VnY3UHRxR29xN1BIZDhrYmtwZFRBR1VILzltY3FXcTQ5VnNTR3VPVDF6NHhUNGhZUmxHMUhxTEk1dXlFdDZEY3V1U1d2Yng1b2IwNFluckpobWhzSnk2WHdyTlhwUVVtMm1KMmp2OTNXeFp2cjJlbVZDdXRzcC9qbHBmcjlvMmJVK2RWbTQ3Wm5LbDNMWTdocXhxWGJIckVyaTFhTnlUWEcwZTRMaCtRM1d6Smg1YXJHbllwQU5sQmR2WXVyMlF5U0M1ZDd2R0VEZjdGSHdteFA4cXpZaHJwa2FMc204NFRFSXIzSGNNZEQyZWJZeE12Y2h5dkRrRFdtak1UUWVzZGNjdFVVQldXSzllenRlRTViYzJiaVNQV3ZBdGU1V1UycktvdVNpb3NFRW9EbVJoZ2d3UTU5VFB0NWxFdnovQUNJSkxteTZZSUdWNSsxbnFROW1qeUhwaEdYVEEyRG1JMk5kbllBRFpRQ0lNYTd3ajVUV1pIb1c5cmRwWEd4TVQvSUtVbUp6MWNsZURJY21xMXp5ZHVxNlB0WVZqeDYveFpMWXFySWgxYisrRnN4ZTNLOVpHQ08wZEVXZUU5NVpxckFWbHUxbEh4TGRWREdJa3R2WE91S2NqWUxlVStCT3lON1pXcDNkU0h6c1hzZVdqd3VqWWFxQ24xc3BzY21WL3dEa0h6NG0xbTF5aHEzeVlUQzIrcHJlaW10TU5DTldjWklVem52WjR0WmlYMHdjTGl0V0tqcElzbGRTZjBLeVJyZ01kM04vZWhaWFdEdGJOZHBGeU02OHluMy9BSXM4LzhRQVFSQUFBUU1EQVFRSEF3Z0pCQU1BQUFBQUFRQUNFUU1TSVRFRUlrRlJFQk1qTW1GeGdVSlNrUVVnTUdLaHNjSFJGRE5BUTFOVWNwTGhGU1JqZzZLejhQL2FBQWdCQVFBSlB3UDUxVUF4Z1NyUjFadTZ1Y2p3bm54VkUxS1JBTFd0NzdSejhVNXBaOVQ5bVlMaFdFRTh1S04remlLbEVWbTRkNHA3cktQZG9lOEoxOFlWQ3B2UHVacG5pbTlXN0hlL1krSFJ0VDJiK1FYYmh4eENjS1BaM041YTZlQVZCZ1kxeGtrNU9kQithK1VHYlFXUGdOZlUzMkVjdVlXMXNQV01zcU1zeXppcW9PL1k5cEdoVHJDREJhL0g3SG9hMlk4aXRTekxnTk00UGt1eTNPMXRHN01uZ3RpNnh0Vm9sOVBnZkpiSURkVHRZOXRmTWpJZ2oxV3pOckJ4YUdDbDN2WDdsU1lYWFFLTlBBcGNnZkZVdHlwM001bEhUWDZGd0NhNTNrUG45NDFmd0tnTnR0c2pYbW0zVXkxejhCY2tUVHJETGFsTXhIaVZUcjFpeHdkMlozajR4elZUYUtMVzFRS2JHVGVCUHRUOXkyU3BXRis1VlpyNUZzL2NtaUIzaTErbmdnUDYybEdSejZYajBWRjNyaFd0KzFWWEg3RXdmUU83VFcwK1dxZjNHTm1yL2hOTjBrdWprT1NwekluUEJjY2dINytoem1rdkcvVGRCMENGV3ZGUUc1bFhQd1ZBMDVhNjV3clpPbXFwTU5vRHYwaWxtT1VqMFJGTmhmYWFyaE4wbmdQeFZTcVBzQlZPZlBLSDBnWWJiZzFwR1htTzZxbE5yM013eDJmUlU1TmxuZ1IrYU02ZEljTjZiNHdFNEVjd3FRTlQyU05VR1BtTUNrTGtIN00rZTBKcFFabjNmeFd5dWdOeFZaTnBiekk0S28ycGYzWU1nclgzZWh1bFEvUlJ1WFZHazgwYlh2cEMwZTdDcWZ1WWc5MXBQLzJxOTFham9iRG5udkxiS2wxSGJYTlk0SGdxQXJmWHBhajBLcVNScTNpUFJNbnMrZmlyYWpMTFNMZDVQTlBhUTd0R3h1UWM1Q3FHbTV0VFZqK3orUE5aWmkweGxhUnZEb2VQaW4vRDUrR3ZZR2wzcWh1c29pNGpYam56Q0dPckJlN21FNll4S01IbUViaXZoNm90enQ3dTY2VndWT1NOSGNSNnFyMS9aNlBPZGVhSm9Ub3grTHZWVUJqUXhsVFdaN1hXbk5zNkovVk1wMUxPcmMwa09KMHh3VG5peGtsN2VHWTFIaW1oMjczbzFWTWZENkJsOTF0TU00NkxmRGRsa3g3ZVRsT0hXV050UExHaXhEeUNzZFA4L3dEbXM4MnBsdm1wa2pNSFZOQkdoQlFkRDlvYlRMSjNjcWhHNGQrbnZEL0NEYWpSVkVPQjBYYTc5V1ZwWUkraHl3VlNIRDdMazhOTDlrbWw5cW8yMDJzRXVjM1E4U0J4VllWSENwbDQ0OUlrc1lYUjZLWjJ0NXFWTTZaL3o4ejFDeFp0TkoyOWoya2ZaT0ZUZzNDYWxQelQyeFRyazVPWmhWV3NzeHZPaWZMbXRrcnZaellMVy8zRlZhTkc5c2k1cGM3cHdPbmVkV0pGTng3dU1sYlM3dk9OUUFSTW5Db3hic01pMzJUS1p2MjZzOTNtb3RKYTRCdkRDcVo5MGE5QS9jbjdrTjdPZW5tcTdXbjZ4UjBzUC9rRVlCYU0raVpVZGFaY1J3Q3ExWEIxWE5qcmRWc25XZFh0eERTNGFDQWlRZVR1STgwd1VKeHZIWDFSdUhQcGJMbkdHaEcvNnZzajA0cjJhZTBPSDlpYi91ZXFCYjlkTXVQNkgyZzkwM29HZTkxdkpDMHVvaHhGUEExVEFQSWRIdWZnVWM1NkhVYXY4U2FrUWhNdXdLYk5maXQvZHlMaElUS3BCMmdIdHFsM3A1SjNvbmdOT0RIbXFXYU8wT0RkM2lEcW5HM3I2YmcyZE4zb1k0ZFZ0UGVQOUJXelk0V1YxUzJuMGh5clZLWUE0MEZVcHVJYnV5WUtwWG4vQUkzZ3BsblUwSE5ZQ2ZmMVZZRWtXMDhkNGZtbnkxMUpnejd2V0twVUZGbzF1bVBUaUZVYTF6OWp1ZHg5bzRUR094d2RDMlovcEJWVU5lMGFPMTBReG5RS21mTndWQ1RFZnErQzJiSDZXMDZjRFFJL0JPQWJyQ09SVWI5NnAzMVhBV0NNTFpHc2RydkVxcDNkdGRMQzlNazR6b3RwcHNIOVVsVlRVcmx6YmMrUFNlU0hBYXJaYWZlOXhDR0dyU0JoUHFkWEpuZXkxVlIrcnBCdG50aTlOakVlWG55VTVwbnZlU3BnZzFHdDNEek1Lanh4QlZMZFB5Y0I2M0x1Z21TUWlmU1FxM3hNcWhodGVnN2QvcGUxVXlUeWJ2TFFSanF6elZXMDI4U3F6WkY0UFovVS93QUxxMnk1cjJ6b1phakV1eGFmRlZIUTdsL1VCK0tyU1FST1BuUkhYQ1pISnFvdmRUNGZWVFJKNnQxckJwdm93NFVqdWppT1JWU2JNTytDL21hWC9zQ1lPenFsbXVzSVMzL1R3YmVSblZNL2R2MThsczhibXJRUXFyaG1EdjhBNXF2dTlnNjZSd2U2VU9zaGhjTVlUQUIxVW5LcUVXN082STV3ZzUxektVQW4zcWJ3ck42a05XNjRoVzN2R1EwS2s4c2NKRCtxd2VLMmFxV2NkOUZVdG5wVXJTN0JMaVJQMksxaHBnRnp6blZWSFZJcndGOGwxS2dHcjd3MXFwMmx0MTRwR1l4d1ZvRk9sdU5PWS95cUR5OWpodWtSbVRwS21rWE5JSWJrUDhreUhNYzBnZkJSK3Zwem54bGZ6TDQreFVBSEhZQVhQOVZ4WTVwK0Nkb3hPMDJnb1R1alVlSlVmcVRwNUk3M1V1NHB4M3FMaHI0RlZKSTJXZ1FkY2dIODFGck9KYm92NEJHZlZVdDVySGpYbUNGVEpkMVl2TitwVlYzVmlrRGNjKzJSUDRLaTRpblF5ZUdxZGg5dHhIQ0Fxem9yVjZ0MXhpWTBXb2NCanpSaDhtN2Q4VnRUN0ErMDQxVlpycnY0ZkFaVzBQTVJKbEVpUGVjalBhRGlqSmttMEpndnNETzl3NlNKdS9GZUgzcGpNVXlSYTdSQ0REamgwSWFONXAxMXRMM3VVZEZOcmRZdE15b1E5Z0oyOGVyYUNlUWRLYzRPcUdIR2RST2lNRFJXbHpLZUo4VStYVGNXbzNPYzRieFFPVzRsVlR1akJpTUlpU1pjdmFjUGluNXB2emhWTldZK0ovTk94ZWp3VG5kbmhTYitSUTczaXFwemMyejFUS2I5M1dVd082dzJpVjNpTXp3NWRER3R0UEJZYzdFcXF5TEJpRlVMQ3F6eVl6TGRVODRNNkxPNW5DdHVJUkFONFZkazZkMHA3ZkRHRnRrMnU5eFZZYmRkM2NyYTVKOWx3VzJ4QXdJSzJ2TjgzV2xWWW1uckMrVXpKMTdGYmM4LzlhMnh6cmVCWXFqWU00ZzgxVnBmMnVXMjB0MHlPeUsrVVdIL0FLaXRxRHQ2UnVsYmZ4eHF0c2FmaXRvcC9Bci94QUFwRUFFQUFnSUJBd01FQWdNQkFBQUFBQUFCQUJFaE1VRlJZWEdCa2FFUXNjSHdJTkV3NGZGQS85b0FDQUVCQUFFL012NGdIbVpoeWUxRzVsVTBubFlLVzR2cW1Cc3MyaUlLcmpXNGErUFQwZWovQU9UQUFBZ0ZzVFJsbkJJZHF5ZmlTODlBYW9lK25qcnVwbzhNc1pHNXhjZVpYaXV3REl3YyszckRkZ2lLYnVzK244Z1YvQVZLL3dBQUQwWDNmVFdJYzlSRmowS3hHaFlGc0dOcy9RektqSnhDNmRQOUVvcjRkY3RlQTg1cHFaRFEyWlZNTFprY3pNY0ZMQ0hUMy9NMzd1SXIvWCtJZUgrTitQV01LbEgwV2c1SE1BbGs0MTNOYkt2dThzMUZYVTNTWDhMTWxrSTFlZUFSV2RncXV1b25YZXJuUUxvaXUwemdPQ0RsSEdteThPbXBacW44c2gzSkxQR0RZZHZYRXV2Q2NuK0I5YUJYNWgvaHN4d1d6bmJmcXMzYkFWeVpzOFlsNzZBNHBlWDd4S2l4bXlyYUh4OXA0UVplbm1ZK1BjT08rb25LSEhRSEszd3ZCZFFOZko5UmI0L2hjcW9BUU9kTmRVNit5WWVZdVlHQzJPTjVoZ3Q3a01YU1FtWk5CK2hwbGl5cm5wbDlweXA0ZmxQMXlsdmFBMCtKa2ZPcjZLK2pMZkJyK0FONjNSZ3E2RitscHFZOUZZc3BYYm9tTStaaVNIalg5RDd6ZStiVU5jLzFFMlZYSzMxZmc0K2pVaXdCays4dTUrb1BOc2ZFWUo4VG1tTGQxZW1PTzI2UGtYZFZ3Z0JxMlgzUGw3UnF3YVYvMEloaFhWL2xBR2dQQjlHV21VZFo1VHkrZ1hqNnpzL2dHNHZoZUwrbytEbktvWnk2cEJ5Tkk1QXYrMDM4VW9iUGo2UVVEaVh3Qm10ZWRRODVhV3lBaHZzTjk1a3VnVnlqVlhUNlRBdHl5SFFQZlYvU1lrWVducU52NGl0Mm9PQnRhZTBTcTExdHc1clFRb0pMcDN0dCtaVHBQSDZIRXBQb3FWOUJ4ZGtJS0Q5NlhESS9QT3pheSsvaGlJMjFmNXgrSzdGaEU2a2dOamx5Uy9leUlHcnV1dzFWOU1RTThpQWRpYVp0MjcvYXZETG9leFk4aXlUR1BxaUlSTVd5ZW5XWFhmaHYwbVdvbkUyRnJ3Y2xhbUk0YXFFYzZaNk16SmpvR2g2ZFlBTmVvZDB2N1REZjBSZEFQdWh6Zm9xVjlGQmJCRDJvYXlmeDd6V00vd0NPZG42RXFDUWswdlBlMzJpUFllVG1wVUNHR1lhTEczTEJXekNnR205MXFjemxUSzF4WU1lSVpKamdDN2hUZ0RmcktRWFdXOE5HT0ErQWVzWGt3NHp2SXhYcnViRHo1UlNLZG11a3pYTGE3ZGc0UGlKWitsbzhOeldYeDlLcFVxVkNNd1o3Skw1MUE4bVBWamdyTnZYNU43SU5oNVVvQzdkWWVpeFR2ZkhhVnJ3bWxUSTgvd0JUUEpoWWptS1BKSFNNY3dDSU1yUjZvM0NzSFl4cmJhYmk5WFRyMG1ONmJ6YVo2d25YM0xkNHdMdEU2eGVxMXJNckd2R0ZHb1V6TCtEU0xaVTJXeW9UTitGclI0Q3ZlRnp0VHBuM1diOHhkWmJocU9vOTk0NW1hU0tXT1dQcVVhZURxcTM0anFDWXRySENOUyttcHBYYXhrTzdPeGxkYkxUK2dIZW1YVTMyeTlEMWw1eXhWQmNqNzFLUUhPbUVLVmI3SldPbk5tdTliYXJMNWw3ZTZsaDEwYzcraWd0U3BnbWdqUVdhRGJBOXA1bzY3T2NkUGVNbVd2QUxSZG83OFI4N1VkWFZVN3A3OVpxdVBnZnYxbWJJall0dzdZaVFhNkJrczlweTNvUUdNc2ZuSDYwRTZqSDFLSGcwN0ZoVGlWRGUyQ1M0dE1UVzRIMFFwRGtRdjNaUWRqbm5CMW1XblY5NnJBTGtpZ1d4TEs3RmMvZUJKR0pzS252bFNpYU9wUTIyL1N2YjVOWC9BS2wvdlpTdjJPWTFxb0NzWUVVTmNGY1c0WHA5cWczOGEyZXFsMDFxVFYrenpYek56RU0xSWlLVnBhMlRBNmlBcXMzeEdveTM0a09xQnBnZDE4Y3dNSFhKVXRCbWtkS09UZWw2dUh4TzZWN095QTJzcXJhbXZtVU9vSmFJTTNjaFRuN0hBdm1lODJEWFVpUHl6eWpYek1NWWpiUHZPTTdoaDQ3d2NLelZQenl3N3RLaDB0enZ4RnZENGw4SHdvR2s5ZEZUWjRQZ0w5M0FrVnc5cVZmNWxDc29hV2NpMC9IRWF3ZVZVZVYzMHdLdXNESjl4S0pqWS83STRxdzFiSFhyTUtsOGVhcG5aUzZ6UFNZY2VJWlhMMExWb0htS0xETUdHRWpYZThPQVhLN1o2azBsUktBcHVOTHJpeW9QN21YV2czQ3gxZVpuZm91NlIrc1RUZlZYcE1GWU0yM0VnTW9BTFN1QjVnc1ZpNmJ1VlZVbjNWY3luTTRXMVl2K3RRcEU2TVhIMlluOHdxVlY2MStoS3FKQzF0ZFdBV2VYWGlPKzdLdXdaVVdPSUFnUFcrZUlXeDFoUEVjK01zOW9mZjEzV3BZb2JQUSsxenVjTk5kRlB0SGl4ZHNEcnpxT3VOTEQ4U2oyOVBjcDNBcHEwNklOdnZIcUlWOXJyM21HdHM0TUppYkZIOW9hRit1QmNiek1oNGxRZXNZNGdvckFJMGZBcXhGOG5wTGpvNWlIbzZ2ZVcvTUNXRVgzU0xlc3ZYZlhIOVM0alN4K3pqUHpOTEZldUQ4SlFzbHdEWFhMaFpIWWgwamJNK1RoT0lvc2JrR3ZzR1k4MnVxV3hlNTkrcnFNR3lGRWwxdVFIdng5VzEzbDFIUkhHb3VLRlZsbmRjaDZRVTJCcUFaSmpPcjlKWm4zSXZQUzVuT0hWNngrK3gxd040TnU4NWxwYTVWajBtYWVNWVVBUDVsdFdsb0EzVmRmaUt4dE9OQmsxY1lqZ2ovVHVoUkt3QnlOMVZLamxRV3RlSGQ0aWJJeDEwVktVbHNTMnZ6RXA4bDM0akN3UzJkUThUR2M5eU11Y2ZRZkU0VklEeVIzblJwaFM3TWJIZEMrbEdybmk0N0MwM1VVdDFVR0RrWTZWNDREdE10WFlFVXNPa3V1bEh3MWo2VFhSY3QxR0pCQUN0bEZUeFVWTmg5MWRscFNwU1B5TndUOHJoQWlMY2p5ZlNKcTFCYlV2UkxFU1JhVUhES3BzdGF0RnZ4SFBoRkF0NEtKaytCWTdUakdYckZPUU5nY1hEenJZcHl2T3VHV3psVjNlbnBkeGRRNXhLdzgvYVZzRWxLN2lQVGhxNmM1RXNiQWpHcjZHSWRmSUxpNTMxZ3RFWHozTCtzaStXRW5ZSHExeExrb0RWT3JaR3NwdnJyQ1N3RTdMaUFjSXIvNnZ6SzFlRmk0SXg1UXRiWEdJTnNiZFFObjdRS0tRa2RSN3hHODd5d0duUVA3d1V2akRXTkd2aUNWMWd2YkR3MEE2R3N5ckRLQ2k5eThQbThDQzhaVEtpRlRkMFExTlZyckwyZ2tWcDBZemVZSy9RQzZiOUljQWl5NE04elloUysvRFBzQUt4ZUlacVlIWHFIU1hMdS83bWZRZWJNRjBiZUJtLzhBTWhaTVJndVFXQlRSTzVYY3JqM1cxdmpFNlprVFhRUWtGa1haK0p3Mmxkb2UwWjd0SnY4QU0wNCtXcjBsRHBKQmVHQVl3d1c3TnpSVjJqa21xaXRVZWtDVFdwaU9tc3o0N3htdWxxMi9lVW0xNlptREtvRVp5SnJiY1RRRDE1VktMWE9NR1F6SDZWTDRRc0dyMmpVSVg3TXp6bnFmMWpzL1k4VC94QUFvRUFFQUFnSUNBZ0VFQWdNQkFRQUFBQUFCRVNFQU1VRlJZWEdCRUpHaHNjSHdJT0h4TU5ILzJnQUlBUUVBQVQ4aHh2amg4TWVPTFhzT2p2RENxUFVXb09jMkYwWFFZQlZZR0ljQmxkTnoza2NsRnBkNFp4dG1ZS0J5QnZzRzh0OVYrazVUOVB2OU04bms4bmg5WS9RWjZ6TDNjdkIxamRNMkJSMm56NENaYmtXNFRMNEZKZVZIemFKUXFLd1I0MG1NdDdINkFRSkdtVnVQREVTaHBkTVFDbHFyNmRkWS9Ubmovd0NmWUw0dk5maysvd0Rwamphc0xqdXp5U1NKRjNreDFRTEZ3TG91Q0FqSFpCakpWSm9tOUtEWlFlNGQ2RDB4eHlBc25JKythb3lBOFlFTHJlZHUybXlKRktIaXVKbnhuUlNJdUdZUkhabk1qTk1jZXVReEhySVpISTVEL0ErbjBoL29IbkRNcU5mVFd2aUVZbUR5UWZuS0ZPdDFpTnFSbzZpZWNKT2xhN0JLUnJoU0U3eVRGdk9xMFMrT0V5VzhDWDU1eXlBejFoMkt5aEhwRHdFTUxOQ1dVb3h6MWFSYWhibFFaRllRUmdZWmNVUUZTeEFRcHpYT2JmcWhhWHBObVQrcFRmOEFnZkJrRVo2ODBQWXNmZCtvUjBveGJIK0R4OU1NcGZZcUdpL0p2MW1pZnNoTUhuY1h6a1NJbGduTUUzTGpvZVlZSi93NTVsQ1hhaUVzYkN5aGcwdVdZN095cEFLM2tqRG5DQWM0R292STVwUDhtMGxIQlBaVEZ6OEdudWlSbkk1RFdVUmRSa1NHeE1FeVZoTTltYUg2Q0drN2M5MGh6KzBzL0xzQi9YckhySHovQUFHT0x3RUwvdkorN2xpUHB0aFovd0FoN3crc3VIVFNOdkdDTFluSEJ6YkFXaVpZdDZua1dGUjBwVk9UYUk1bmd0b05xelR6SDdtSXlCSG1NT0huK0ErYnhpVVlHS1FvSUhZMG14eVFHYUdDWlNFNjMxeGtGWW5mQUxtMGRUaDJNd2pDdEV2SkZETUdicnlGWEFHNGxwOEl5THEwdnhxZ05xaDV5ZHUweS9KeVBJNml5bHhnMmo1blBEKytNTm43NGRaZXZvL2JIUmdTaEgrRldHVGFxVUlibmlEZDV6aHZvL1AvQUo0RmM1TVJxMzVBU0Jreng3enR3Qk95Sjl4emtwOFRIQi9ocGZuYWZjZTJXNXpOVDVNQ2U4SERTNE5QU005WU03T21CeWk1R09UYzVEMW1aeHAwU1ZSYjJDYzFQZlpOMldkQ3JQbGs5SUZXRVhNRmlwTlo1aGNlZzEzU2E3eUNuUXVOczJOdGl4NlNZZFA3WURqQStUQkNBbGRZUGM4dmVlbVIrc1FHa0hPbXZNeXlZVzFNbTNTTmZWNW9Fd3JYWTlKQkE1Z09RSWh4UFRGaHZVWkhiOFduTjd1cGJ5V3dVSU5hWVRDeFBMTVlDNHY0bndmdmo0enV3MU4yNVMvS3k5Y1ozc2VQaGo1REtSMlRDMExKTUgwaEhrS3JZNmc4TW0raHEvVVQxMnEyemwzd1c3WHZscEJ4WVpPNTBJdVhyaEUvdFk0SmdobTNHR3dtL0dSNmZlTW1GN09jUG96K21OZTEvR0gwZlQ2Qk00RER0NThrMkhoU0E5ZzNocjFlSGlZUEtsZTVlc2o3b1ZicVVhSmNjRVlweHlrbVZMSWt2MndRZklOOGRNN01sZURoeDE2WWEvcWNnZjhBVEs2eVc5NEplNU1OdnN1L0ZId09OQmxkaUdqaDRmT0hEWC8yeVdoZldSdHF6Yk80MnZJK09ZTXp5N01hVVc1eWZRTW9hcUJXazBWTU9XVmVCc1dBRnlXS3dyTVdkVm5iaE5hdmRaK0ptTTFqSTVENkw1TUhmQm1uWnc0alhGc3BJQ1BGcnFJbmpMWW0yanBiaytDL2JLaVk1bjVZaHA1WGpGTUxoa2c1OHVSNFRQMkc3Y1BqNWU4anE1L3Q0TnkwRHFjekZsQitNanc0bVBsQXhtelJJQ1ZUc1l3Q0VEQTFJMXJON0xmYndmdmoxaFN3aThGcUFpOWlYdkE3R1BaWkUyb25GeEJqdGs5SFRIU0JyUGlqT0lhT00yREFhWkdSOUgra0V6NTZMOVBLWUxZaUw1SU9TV3k2OWMxMmdGRGxTT2gxT1NMbUlnRTBwR2tKU0xtUkY3c0RQRWgxQ2ZSQXlPR1Fta2NQR0JNOTJDaUZaQ09mV0RjdmZmMGZLUmtkZ1k1WHBQUGpKMkJ5dlNOMFpzdGNXN0UvQmpsWFBJckFKeWwyT05sU1lRZmpxWW82aHkzaEdoVW9NRWo3TWlwbkY2cUVuY0NCb2F6UzNscVZpWW1FY3NqTlhtZjBFdVVWR0o0d1NqQUdUOHlFRFNTWTI0WWE4V0RrK0NORG8zamN3VW81YVVzZEtOT1Y0OWMyUlBoMVBDczVYc014aXFUMmZ0a1ZBMHpKQlRWOTVBMDVxOGh0SGY4QXp4TnM5NUZmMVFWd2hmWnFwaWI4b1pmMEIyMWc1Y01TZ3VEcnRzTkVMY2xuRStNbEpCZzI0YzZBUXlUSHZBdGNDZFEyTjA3WnllVmtLNk51WHVSbXlKTHlqQkxRbW43WUcyeWY5dUQ3QnZIMFRpN3RMYXJ3QVZmNWNESndaVFo0MlhtZm95RG5yT3YrTjRWZG94ZVhqYzc3aWVhRzA3U0V3bVh3NmlFckZPWWpraFFvVEZTUWtrNXlQaUZHT1ZqTEl5bElrZExLQzdoU0NwN3Q1eVBXampyNDczWVk0U1RsYnN3Y0pqZ0g1Y0hVQWtQZ3g1S3BEdndCSUMzSGVzbHgzR296SVM1RTdkWTJZYkhDSUhTRUlNTmorV3RSd2ZsOG1TVHkrY0lUOTgxeXFYODdDTmRHUVNJbjFUcDRobmp4MnJPUXFyUkFmZHRGd1dUenN2aU5QR0Q0aUovZmllMzRuSjVIYUNHZUl2b29QT1RmSUliSXEwMHpXZkNpaWRLelg3eFVjb0E2T2JmR3ZETDdaaUVUOGlSTWtHSEEyQU5vYlpTdThyTllpZzBnQXRvc0w3WElLdDhabVltS0doMlFtS001SE5YeWVPY0ppN2xhV09KWWhmejQwbWtsMWNUWm9ESzEwYmNnZXJKTVhxRzhZYzhYSWlpcmxOVVpMeGpBV1JlTlhHR3FDcGV5b3c2OXFwNEg5NU5xSUZMQWt3MDR1WENDVXVnT3NrQjRiSGNFeEJNWU5SeWJnUzlYcDhZRTdkRHdUZnEzb2NyQURzWW9uTnpuOU1qS3FIeGVMeW9MS3Z5emxQVDlwcmVYSjhSNVZYV0tIUExJNWRkOFcreVp4VXhlaE1nRFZYcm5XbnJCdWlPanVQbWVpemlvdVliNVM3NHdZR0hDTWxCMFRnRU8yTElKNXE4SnB2U3BrNk5QNk1hWWd3QVE0YXlkZ0ZvOTg0Lzl5cHFFNm5nZ0xLZUtnR0RNV2kyV0lJQkFRcFlZR1ZaVTJ3a2Rrc09qOGdqRkVNTjVKdXdsVGcydCs4bDVIZGhZNVNBZU1iTEFpaFNWdU1GU3BIa0ZPanEvZkRQOE9ZWFV6Z1duMnVBTCszR1dCSWZHV0hUWDdZTXZJNXMrK3V4VWl6K3VRTUljR1lCQnRvUnlWdUtra1JSbmI4QXZ6Nnk2OTN3Z2lyckRUY3VzaEI2TlJzWWZPUDZmNFEvT1J4ZTJNdkJvNlpiek1yRTVVZUVaRUdpTEVISHdpYzNScnNjS0ZJREZEYldoMUo2eTl2ZUlqTzRaNVpHeFpLbllIU2VNcWN3SnlsQ0x5eDdJQ0ZyWlp4bW1MaVlvVmRQeGtyVVJyQ1FPb0lhdzVLcXVRZ013MHM2eXQxK0tpQUtScGdzZUJCSTBOdk9OaHBSd2VHVUJBYnVlVlhyZ0JCY3VtbGVGTjVSamJDU0ZVVkxQUmxBbHQ1QUpaVEthV0F1YWVYR241SUhJSlJZYUZzazVYeTNIbkxWa2lqTVBZeXhUR1hFaTZnVVIwK1RTOTUzVDIzQkdZdW5EV05ockVJZlRoRnFtYW9HeXVKSDdZV1lqc25VMUp5anU4a0g1elIrNWZqRlRKVGU0QmsxK01BUlZ3QzVBNm5GZ0FnR0JUNHZPU0VVNVVyenVJeVlyZ2J0dGltUXBnSVJrdmY4QStaSFdqaWtBSWZUT2ZQb2dVbjc0bldsMkU3UGVNTEZZeENFZFBLVDg1M1AzL3ZGS2RsWU1CNmlLZm9RQlYwWTQ0bTZKYTNIMk1ta216SkErS2t4WUNZYnN2bngrc3VzbWtDcDRVWUV4U2VLZ2NQYzhtU3ZWa3d6TmV6ZkdHQlkyNFJpb3E4VnJCdmd6bnV3Q0ZpTUNaTy8xa3JieUppK0psN09NSUY3S0oyUUhrYzREWXBaMDd3ckhLR2EwWSthd1V2OEFnUlQ5YkNKU3ExZVduMWl1VWFZRkZ0UEt5bkcyT1FHOW9KOTJRK0VTdERFWUJabTBCekVrMWpUb0lUK1Y0MWhISks4aU5CL09JQjg2S3Ricnlac3lKTDFsUVczS1d4ZG0ydHR0WXRCMWk4eDlRUGpyQzFRZzdFTGZIZkdNdWUyTUZGMHNDWTlaZmJkYVpPT2p2RktnRmpNU3ZkUDZ6UVRGeXdOejVRUDN3VDNZR3BSMHgrN2tKTmhqUklSNEl3VUw3NkNIc25HNVVPYm83SWoxZzF3ekFzM1gzeEd5Z0N1Zm5mOEF2STBsOGJvdis4ZVp1VnpPZStUNXc0MitseW80QTVDVmpNSUo5dE9NUzNsdWdpRzczcXNqQmRBSm5iaDBYODVlNmtnVDJDTGF3anFiSzBMd05QbktOeTRPT1VtQldvTzlheUdDeUNtcVJvdjhPRUpTRXpNK2ZHQ2xVTmpmWmsxSkNJUjNPWU1tMW9vdGo0RVBPWEJvNDBISnFUV3NTb1M4SU4vWUJrOFNzVWRiRkozaE1LaTR1SjVieVZsZ0hvckI0dzRBaUh0bUtDY1BYeW5qRk9HSTZrdExOaFM2eHdaaHV4V1BrdVJuWG9ZSEhlUVJrVTJLZkxqR3UvazZyYmVXM0U3Zy9ia2hKcDhJZjVnd29aeERSQWxPTWpqa1NmM25Oang2UUk1OFpBMjRDcXBpdmJLei9EZW41WTMzSUlHM2d4L0tWcnBjRC9NemYvL0VBQ2NRQVFFQUFnTUFBZ0lDQWdNQkFRQUFBQUVSQUNFeFFWRmhjWUdSRUxHaHdTRFI0ZkR4LzlvQUNBRUJBQUUvRUpSaGxmdzYyWXBXTXdBbC93RGRmOVBuRUVpSG1wVTFTdWh3MWJvWGZKVmRuS0J5d2svS1BQTXdZcGF5UHYwb0RiQkFGSXd2SmpER0xuQndJcGprQjZ5QnhFeFdGQnAvNHFxVGJqeXhRNEdKbU5FQTFjQUZhVWVWbjRmMnVBQzgxNGQ0dDIwanZKcHRnVTZSekZRdVlNUzUvc0JhSXFrN1IxQm9HQWVON29uYVNGVElNTXAzTnZrQ21pVE9DWXNVeUV4MVpqS3d4WkVNcUxyR2ZNa3lmK0VTOUdhamk4VjJNMkJNaE9YazBIK3Y3ZldReHUwZHV6eXFTeUMwcG9NLzJzMGhhSzBTaGdMeTFIaVdKRnFjcTg1aXRBSEdybi9QaEJzTWlPanlKQk1CdENVT0lHUWRPQU1BM0JGRWd5dnE4Q0ozUTJwTmxCWmp6RGlCWW1KN3dreEdYM0dmRWZ5dmtaT1Q4ZnJGbVQ0eElPYVRkUXZYdjhQN1RJSmpnZWNLTW5EN0c3QlJtMVdhRGlvOXBXQk1zRFZwR3FNQ1IzdEVjQkNkeUdndzdtWnR0dWluV1p3YkNVdDM5YVVOcExYZ1pIVUE0ckMyR2dZbGo2NnJtYWh2WUNHS3NlTkNwSVNKZ3N1SjRadlBvT0o5bU9TNjZ4ZU95UDRVR0Y1c2tZODUrVXY4VnlaZ21ML1V3NDRjdkZoWFBDSFI2ZEQrSHdZcGtTNXVldDNHTzNEYzZHdjhkMWFpN0JWUmN0OENIcU5UTFVWcmlHQlhTQW53Y1ZUUHJRTkRFa2QyMTBSV3RLa1VRMEpob0pjSjBUeXI1bTVTcHBVZ1VOR016WUNwb0dsT2RyVnA1WEkwTks3V0VHbEtwVkZEWEE0Sm5RRWYwVFRrcndaVDB1ZEorM0dzOHgvd0VXTmg1ZjdqUTRSWS9ELzkxKzhHdnZEVDhBZjg1TVJkMnQ5Mk9Id0oxanJnY0ZyTHp3UFAwT3ZtdUF1aTRtWU1zTUV0TmE0NHdGU0Nwc0xIT1RaQW5EQUVhMHNzUlBFVHZiUlVoekhVM0VWeUFwRU51aEp5RVBUVFhsd0VJRXBxczY3VmJnYmJQY1hLM0czRmtZUVM0OHhEb1k3SkRTM0tISDMxVjFEQUxsdDhMakxlcXJleTJaRGRnd1E4L1hwVnhOTEo0eGh5cWg3bzFJaDBnVkJjL3dEMU9hMlR6bndCK2pFaFFoaDc1SVVSOFIyWlBuOVBCby9UNVlvL1N2OEFYOEhXNG9KZm8rM0I5Y2FBTTdZdVA4Tm9JVGpvQ3JhQnk2SENXL3BkbXNqMUtNWVVvR3ZXYWxqZDk2clVGQTRPdEVpa0JJN2EzK0FCaHlpakowaEUvWmlJM0F3ZkFjSU9XcG9mWGNtSGs2QlgwUkhGSDRqemN1aEJ4dURtUUlpZmJFUTdRWVlJWVFTL1laNHFneUhMYUJoYk1UV2dKelo2YUpPU2c0SzRnZ2o4QVpYTGVrREtQNnJSYWdMTWU1SlF4UUY1Nnd0UUFZdThMK0VKOUZEditqaGpod0ZFeHRvMEhyLzBkNUlEUnJFVmtWY01sam0zdVhtdVlTUTc2V0hzTVk5QzEycWlsbnVLWFl4UXZBdDBqYTIwUmNKZ3VtK1doODNlZXpOKytTenhWbndtTXdFMllHT2VkQWNxNk1ZY2gvOEFMMFZKYzFoZzlING5idHhVRnVQR0RxVFhBaHNwZjlLb1k0L29JdDRUOEl6bnF6Z2tJQkpPVHBFd3dxZm91d2IwNWVVamNMbWhNaG1ZTnI0RjBjdWVnQmlXR2sxNFNYTm0zOHdoUmVtR2pWSzRId0lzRmpqeWplVFM3Y2tWRWZUSGcyU2hVL0M1UlZoLzhKY25HUGxzSGgvQ1Rsb244Q3Z1cGNJV292UXY2NVhEbGhpOC9lQU5BV2hHN0NZTVZEdWhEQUJZb0ZMVVQvY1NpcWRxVXVVNEpZRnRvQ2ZFbXVzV1c2YXVrMlVOYTVEcmtPODROREowNTd3bGs4VElnajhlRmNNcWVvcVl1ZXY3M20rclFaUDUxNE1rS2VyM1RXQ2M5S21lbHYzTTRsaXVleHhPSmlYY2V4eDNxWXkzcVU5Y1dtb3d5Vm5lbjZJdksrWUUxa1lza2ZhM1ZQb3lqOXhhSk96WFFHOVlBRThoL3JqRGtMaGtGbjB3ZFlnM2hyNWMzK0h4US8xL2I4Y1kvYkFpQmpGd1RuUkRZdzVZYTdjMnd2S2tTaGdsNVF1YlplNkx3VGxCREVCc0lZMEFJdFFCWm13Rlg2UDQ4eDN6T1Z0K3ptbDgvd0Q0SDNnMlk1LzBOeDRFSXNGajhNblRwK01TRzNwMTc0WWZkZnB5VXJ2ZzJqbVpmOVlRczVMTGdsR2lVeml4TDd3dm9FM09ZZEhJTTQ0YVR2aEdEcXE3eE9KdllqcURUSXgrMlRNSkNycUh3QmRNZkRTYXlFaDV5S2dueGhGeE9UaGk2VkxiTmQ3ZU1GN3RyeEgxaTVNcytObGNOZHlnUE9aREZjYmxlVGtyVVVyYkhJdW5GVS9hRUUyb1ZHUnRXaDhBY1FnQ1VacGNoWElIdzV3ckNtd0kyZ29XRnpXK3ZXV29RaHl5bmtMbm0vbGpnQU8zSWtkQkdUNWFoQW9ya2syYkV1MFRqQzY0NXpVdzRoaWowZTd6TENLb29BNlFLOC9UWExRU2JtZzVMRW4yeUZPejZRQWdDZ2wwM2t2cEp2Z2Fnck9JTXQyTG9YVkM3OEUyT3E0V054Y0FIYmswQlQ5ZitqUCsyYys4WlJFRUZ5cTZENWNlYnB1Zm95RzZMNGh5ZmpSQlBTVG9raEFMTWxvTFFUZlZwUlZMTGJSdHpJRXhzUE9sWXdtRElkc20ydXNoU1FyQUdKa05JbEh0YVFXREN1NXQ0UHd1WGp2bkF0c3NmbG5WNzJpS0hTaWF4T0tqblFBejl6S2lqb3dIY0FRSzdXR1RaUHJ4TEM2WkJKcXdONWR6Slo1TTlOWlZRS1lOanN5NEVCdkZiaGxLUU8ra2FrbzBtZTBaYjRGaWxVaWtFcmFpVnNSdnhHUTVMRkNpQXpIWUZrRjlQNGNZTVI0VVBvZFpjaEtKWmtyOFpaaFlRRUJMYVgwMWlxZzZmNFlIZEJzcHZpa2hycHd2OW5WeVBlZFFKSFN3bVY4a0ZRbFZVTm9SQ1VnMUVONjVxTU9oaWF4eDVOYU96bm1iT2hnZ2hSeFEwamJnMUxqT0dqZGRKYzBDaW9YemZuUm1yNzBraXRCNWNEVXYwNTY3Z1l0V2hxcHZCdzE4WlcyTHJvTERFOVZjaWVCQkhrQ0ZoTU91TTg4QUFSVnNTNFFIV1dnb0E2eEpQaXdXdlJDVzRKSzZhc3FYR0RzKzZTa2x1QkYyWVNGVzhaZ2RBb1RGNW1ZL0hYbitjaFBsMG01b2w3RXphTXg0SlVZRFYyMDhzMWpucHdzMmxXcmdKZ0xUeWJWbzZvQ2crUmdBY00xZU5FUVdnQlBjV3BnS05SRUFWTlBZZzQzWEFCREEwQTFEYlljRDhhSGVMVnR4OEdRRUFxSzloZThpSUExUzBxc2Mvd0ErZ09SU3FPbTRUK0xkN05aRm02ZE5NVE9NT1ZLMEd3d1g5cEROUmZpZVpWWmpiREsvaFFtbjNHV0Y3VFRNMlRyR2RLZ0NweHlqOGF4Z1I4bW9NRlEwU0tyRjNndU9LZkNwRlRYYkZRZ1JOemRURjhBdzRDTGE4WS9HRFBVcXpxUUF3ZW1jWjBTOG1rUkZMeGdCNE5ETzhLRVdLUXVNMlZlRTlSMjlIaVNHQ1hVZDJmUUlmb3lVbmlQM3dRVGRHSHFtUmRuNWNtaDhZOWZUQkZIVGZFcFg5NDN0SkFIZ0NIMjd3MjdGdVdPdjdNUU1DUjBUYlpQMmJ4VzJrS1pobG9kMkh0NDY1em5xWkREb1d4RXdsY1AwaDNkVW9yUUl6SytLYmNXRkhyVGppWnF4NXNMNGtMRjBPUFUyeUxXSUIwcHhpUUhHQmtOYk5GSDdjTGZxUWZ1MDQwSC9BTjhPclRMSWgweGVndVNPYlRhUGQvR0JrUlBlUEMyUEFNWGdCV0VCVWx0UmdDeHdOajFaU3BlRHNoaXB1VWR2b2RqM2N4V210VFZOdmxBSGJFQmR1VUUyMEVVcHBnQlFzNFVnZkRSVVppZWRtZ1VwNmRNMUNzWDdqTmhXdFJXWWorNUczNXhNQjBTK252dkMxenU2ZzZNNHRBTHZNZC92RUVFMTFPR21OeHhWVEtZQ25PdnBnRWRmeVdJV2xSSjRsaEl2ZWtxWlc3dW9WSUdEQ2xJTk5rQ2xWc2RyRmd4R2swZTJSRkVDaExqb1NKVC9BSFJKQy9GeDdTNlRYRTdVaUp1c29QZ2lwZFBvb1lFZGZaYkthaWpmU1kzbmt5U2lMcFI2NndyVHBuV29FYWs3S3hvdDYreEFSTGFsR1M0UEhCekZRRm90WWxmQ1RDbEE2QThtRkJKY1NwY1NNd3FMaTY0N0FxS2RLVEhTTWVwNXNZQkZiUlV3N1Y4QUxnQVZoeVV4eFpCWWFhQ2sxaFZjc2NCYVlhT3RZM1lRS05RcGNsZjNsT0NjN1BaS0RCa0hKTFZHdG11RHRLS1kzbW9yS3RGUlhnbUVxdWJrQXZLdGJCMW1jZXZMU0xCYkZDNDlxOHVoU1ZWRDdkSW8xT0hJUnJFcWtGakxqS0p5eGdQWlNTVW96M3JnNHFoWUF1cHlEVTFqRjN1OElXUUc4YkNZV2N0VE5BSnlnWG5iTC93bmhxb0NDbXRaZ3MyWWl6STBpbThjQVcxTWxYdnpNZFJNZElldnVHRUpEV0EyalRTeDNqbTMxUkNJaUVqSDJBYng4M1dnZlJnZldTaHRHaTk5WTIyNTA1UzVnaEhTT0hsTFNYbnBKUXNtR3N0blhlcmFYZ2ExMFk4eS9PNmFVVWdsY1JBcENWVU10MzN3RCtYR3BFaUprc0Y0WEMwVGRNcnJwZW9NYWhGak9LVVNIVUxON3dzbGkzYmYxblF2R09Ta0s3V0UrcW5NV1FaRUtXdVFlczJaSnFNbmx3Z1JYUEpNcE82VG1vQUtpem8xbThUeWdYekNFMVlobEM5d3M4bFdDVHhwa05hZVFRSTByeG96bG1IMC93Q2dRSUJlS200WVNXQSt6YThwcjRtQnBNaG9CMmkxL3dBWjUvcUpjQTJnNTBpT09RNGFLdkNQMnhsb1BKQ2JQQkFuT0o3UkloNjBXbElnbU80ekV4NWtyVktIc3hzT01sMUFOQzF3dDk3eldteFlEcml4SUlRYWZJcjZwaXRUMzlnUklxc0dramNKL2dnSmhOQ2krVnlyakNSWWZNSHZjTmJEV3FDSnU0REl2ZzZDU29zUnNjdGVyMUlDSVMzTlR4K1RFMGlSU1BtUWszQTFGVjBDSlplcU9QQlZpWXpKT1NuSURubGNIaHhoQXBaT29JNjV3c2w0d2puVjArdElXOENVSUt6SnZERDZNSVpUTVNUQ2FZR2R5NDdnN2UxUlFRRlZUaTBtRnNnWnJpTjdtTVV6TzN2SlY3UlBRWTgwdXdpQ0hmSEM4emVQOGlJaU9CYUdpODRKRFhYSWFNbXR4RHR3Zy9BMDBtaEZuTUxoRldoOVpVTnNOSzB3dDhoQjFZWFJiVGw0VENxOFdzQnNCQmExZmNsQy93QUFDSXR5ZWxwM2o2U0s4Q3Bucmo1ajZyTkdHTjYvZ0x0b1JIbXg0SFdHelozVUIzVWZqaS9BL3dBcFMwUUtlZFRIaG5JZ2pSUUtCY2JkcGJUMDFRMDBNaXdsVDhZaG82TittU0lEZEVoQ3FjQlV3cythcVltQVdGZmd3U2UvN28yZjVISXFWRklFTk4zT3lMQXlKN0c0TjJBRkJMZ1RiWEM2aWVUZGhUQ2I1ck9DQjRmcHd2OEFkMkE4ZzdsZDd5SSt0U0FYdkVjb09zQUk1TnJyRGNjWmVWRUt2dUVkY3FvdStnVExnc0pqZ3JDT2c0YVhwdmI1TktEUXpMVERNWVhJTnlmT3lFNUo3ajZTQWhxUnJ3c09CdVBnbzZ4TUozU3N4YWZLRGh6Y0pValYzMnhXNVdDK2JILy94QUFsRVFBQ0FnSUNBZ0lEQVFFQkFBQUFBQUFCQWdBREJCSUZFUk1oQmhRaUl6RVFGVEwvMmdBSUFRSUJBUWdBREdienViRVJXaU5Ndk5wb3FZTnlPTlU5aVpGdURibTBZdm15Y0xJeHI2TjZBeEVWekE1Z2N3T1p2QTgzZ2ViR2JHYkdiR2JHZG1CcHVZSGdjeFhoZnIrY25qVlpJUWsxdmtaeHVtSG5ybXQ0Yk9PNnczYXF0TTFGT3RnZUI0TElIbTVnZWJtYm1ibWJtYm1ibWJtYm1CeE5wdUlIZ2ZydUIrcG1ZdGQ3Q3cyT1V2SGsweHNyRTJDbkt4Y2I5ZFdUWG5qb1VYM0FBeXJOcGZzUVBBOEZnbmtFRGdqdWJUZWJUYWJ6YWJRUE40SGl0TjV2MUxTSEhSZXp1NGQxMUpWVHZFTmxMa3k2aEw3Z1M3NWVBK3pvSzhyS1M2NFpGdFZnVmt1VnY1dE5vSDZnc25rRThnZ2VienlnZjN6emJxYlJXTTNtODNsMWhBN0dkemRGTElxWU9WNU1RQjZuWFhvZldTODlqTFpxMURGOFlaekxlMXR0OUtqeUxjbG9GdFl5U0c2WlhCSFkyZ2NDQ3dUeUdCbm14SWk5ZHphYlRhYmFpQjRHbThlNnN2cE1yaDhLekwrd3oyMlU0SmF2Z2NqTXNyM3lUY0NPbERTeXRiRTZqaTFRSTZKWmFDb3ZhZ0ZiRnlyYmtWNlJiNzZJUFVEQXpzUU9CTjV0QzVteGdhYm1CcHRONSt2YnMzQzRIdEZKWEhEeFFPdlFib1JiSmZuSlFWRFYzMTJyc2kwcFllNFQ0dHRTaHRYZW9aVmxMRTJwYWRBVUZvbThSd0FRUElaNUp2QTgzTTJNM01EVGVFYkRzTWZ3OVl4SnFVdHQxQlpOekI3VTdjWlNqbzdBNWVaV29EMVpsTjNZVmxEcDdJWVB0QWpWanREZDJPc2hiUjBZbHFrZXR6QlpOak5wdE5wdEEvVURkeEhOZnVXUCtuczFFQ3NBN0h1YlFPUkE1Q21jWCtEV0NLNE10cnB1WHB0c2loZWtUa0tFQTJaL0tlMmVuOERPL0dTVVA1OUZ4YTNVMkUzRTJtMDJtMDJnY0NBaGdBWGR4WE8rd0FDL2NEZTRHSFVMZmdaeGJkV1c5QnVvTEdNQ0p2dE93UjBiM09JbTFhNVZncU82T2x2NUtQWTlxRHI2T3dNM003TTJuWWdiMU81MllXNlVDTVNhZElyNkwwRUxCQjN0Tm8xblNHY1prdjhBYWRJSDZtNW04MlBVemp0VjJGYnYxUG85cnRNL0xQSEtHZXE1ZWdySExKc1lRWkRIc0QvTzV0TzRXNjdFMkE5RnRQcWVnd1A4QjZYcUYxVmU1dExQYW1ZWGFaSkI3bTBEVHlIK1RPcHRUR1pqajEyZGRpeXl3b2RjUkh6c1BlN2k4UER0TjR0eWE2YUcvU2NpdFA3L0FBKy82SjBmOFhxTTJyanF3YmQyRHZ2SDZsdlpJQjNHZ20zU0dCK3hBU1I3cklHYk94RlZuL2dlc0gwdHRaSFRaN2o2aFJlSHFCdzFOejNjWFU1SzhXdDVvS3JoMFpOT1hjQ0JrUGJxYzNEVHhoZ2VvTlNmYkNrSDhTQjJSQlV4WGFXa3JrSW9aeUYxZ2J2RmlmclVGaXhzVHNBdjFFYzlkSHlBZW92cksybE5OMTltaVZjVmw5NksvR3BUbmhiRytsUW42cy9EeXFhL3p4S1dmRUJML1hvQUJ3Q2JCWldjVEl2cDVNc21IZmszdHEzeVBET1B4aFlIL1A1RC93Q3AyMFBmMmtKYlZsN0NNUlJBTng2cUI4alFici9hY2ZKdFA0clE1eVdRNDYwZmRIbFRqcXJVSGp4YWZwdVZIS1YwNG5KRUQvdHZTdXRPYnpGbVJpc2h3bmQwQU9mam8yT0NlR0ZRNUhJcFY2N2Eva2hSZVVITThjZnk1akJwcTRreHFpQkJVMEZYZjk4WDVtQ3N6eDkzeGlvaFhaQUkxWkFtQ3h0czZsaWRBR1lsajFudU0vbHlXN1lEemR3R2Q5bVhyWVhsR01qMHM3Wkp3SzhSMVRHdnhxdU9lczM1MXVRR0MwWmVUUy9tcnZ6c3E3Sjg5bDJkblpLOU9OOGwrbVBGcVgxamNaeVZ0M2pDY2RsUGFhNVh4cjF1NWJKeDdxUWV1N1Y1QXJaWnh0ZHl2WkhvWVdvbGR5T0tUNWVQUUc4Qk9Tdzdhc2JjWStDWFYra3dzcHVUZElmWVl5akFzc3FWcFh4N2wzVVoxVFkxNFV0eDF2MTJjSmhHM0E4b3c4VTNZNzlIekxZVmVtczJYcWd6T055TVBJUmJLK0d6TXZpUHMxOGY4U2ZPdzB2TlZocHpmdDE1M0w0bkg4c1Z2TkdiYnpGbFZBcDN5Y1N3NTJMalY0VnIzTmJWZnpGbGxWMk1LYjdSVmRiaHJ5ZEhuNTNpTU1WV0d2NC9VcHpuMytVVjRTOFlvcjRSOE43TXlKbDJINUorbXVvK2R3T054TFd3Y2JyaEtySDVQUFdmS2NjMTU2eW5GeDh6anc4K1A0V05rL0dpOW54MUtEaDNtdzEwWDRwZGNUb1pTay9JTWQ2TDZpZmpwc2ZpTWlrZkhzaktiaWtDNWVTK0ZuVXNmazFkZkxjczk5U1pQSTQrVmQ5dkF1UjhqR2FjMXlsVi9IMzF0V0Z4N2VsLzdQRktXcEYvSlU4eFYwRjVLdC9qejR6Y2RrMEpZZHVSK1hZZkw4VUtNYmhPZitxTC9zNFBMdFJsL1p0ZkpXdTgyTGgvS3FLMHh5TUQ1RXVIeU9RN2M1a3ZsMmgyd00ybmo5clp3L3lHdmpzRjZEaThrTUtwcXlIeHlHSzBzVXMyR2J6MlZ5ckR6NC9OVTBoL0RUOGg1WEJ4eFhpUG01TEVzRGxaanQyLzJzbFNTcVgzanN5KysrNGRRNU5WcmFLTWVwU0dnZkV4c1psWHlvdFJJcTB4d0dzcHZ4OHdsS2VrclpwNVFCMExzNUE2SUVlb0wwUEtBZlZtVGJhd0xCbjcyYnlqcUN4ZSt6NWxiM0JaMFo1RVBxQjZoK0lVei8vRUFETVJBQUVEQXdJREJnUUdBd0VBQUFBQUFBRUFBaEVoTVVFU1VRTmhjUkFnSW9HeHdUQ1JvZEVUTWtCQ1V2QUVjdUdDLzlvQUNBRUNBUWsvQU84NEFxaFpVMU14eU8rVU5YQ2dHbjVnTEFrQy9xaUMzbCttSGlCRmZxZm9FWlpFZ0d4aTlOaXBET0grMjJxcGduZUFoQUpKRnZNR3E4SnBmbitsY1JIT2g2aEVDQlRhOXVRVFJGZVI2RDdwL3dDSkZnVDRnUk1SdUl3Y0ZHcEVFUkJIa2lMd1J6SDM5MFlJcEIvUkhzd0krdENocGtWVFpCQXFQc20zb0RNR2s1dW02Mm1MRHhVbStDb0ptMzhUaWVaVmo4MGZnbjRCc2dTL05hUWJDTndyRVU2SHNrSGNicVhhWXRQb2lXd1JFWDZuMWpDQmVNRVhubVBzc1greC9RbXRFenhSRXlteTVvTURtRUNKM3VyZGhqb3BNRUtoZ3pIOXI1b1RFZUllNFJnV25mb0ZUNDEwUjBLQnQzQVlpWkZZNjVUcEhKWXlxanBWU3c3eGM5UGREekZvM0l3cXoySDRkd3JyUGNxVVNIYWpVVS80VUE0RGFoOHhaR294WS9KYnEwUWpwZHRneldvUmpwWldSK0hTdmVPVDdkZ1R0UW14djgwQ0hjN2Y5VlNxMXlqSExFcWhIOXVqOEU4NVcvZTNQYmZzTVNRSXhVcHZtRlg0ZkpGR1VlNEtHdmR3UjZoV1FJTzRRa0V4U2h5akZBZWRVMG1JUStBYzk3YnV0SUErNnFxdUZnS1Ixd25TQWVWeXVJS09wS2ZQSW9RZTl1UFZHaXRQdDJIbDNOdTB5VTI2ZVNKeXAwK3FZU2VxaTVvcnRpNG5DRW42SWlockhROXRlMHlEMkRLcVlSN0IyaXFjMzVncDRjM1VBVC81SjlvVENYV25DSGhUb0ZRQldzYzdCVkpxbWsrSTJGa0NaQkJtdHNubXVDNG5rejNYRERIRWlKL01hOTByQUt1cnoyYkQxUVRaQlZJRXFvaEhTWXc2eTQxZWducmxQL0VhQzA3WG1iYkprRGJWS2JFcW9HT3E0WmJmTWcwSlV0L0s1dFFMdHluUVM2L1VMakhSRjJuRWdXODF4dGZFa1hNbS9lMjdjTGIwVzZGdklxMGU2MjdDdVgwSzRnYnBGamM5SVR0Wk5xRVF1Q0hQTXc0bTNRTGhodGg0UkdDUHFEWGRPMHVJQW5NQVVUcGZ2bFBrSTFpYW5DdnROZmtnR1ozSkFNY29RQTBnU2Vxa3lZK1M0Wkkzb0UyMTQ2SnNUQm10MDJxYkJqK3dxbU4rYUVWSHF2MmtqelJwcEJpUGRZQitnS0ZDQWZtaFlvS3dDTWdpVUpScHBhVnVJUWpVS0p3MENaM3BJSzRnRWpaY1BVQTBDU1pjV25pRURTRDc0QzRyT0hEUUFYWkpkWUFWbUJNSndjQ0cxa05INVo1bE4xT2R4T0xRbW9paWUxdWdqT0pGVEs0Z2N4eE1PRmlJeHlYRklicU9CV0NCS2NXTW1DU0EzSkFQT1UrUnc3bVJjMkFqbFVwd2hyWjYrSVdWU1hzdDFUQ1k0amlMUlVDbmxDNFFPb2FZTjQ5RU1QOEFSeUFxeHZuUlJMWEEyNmhaSHVRdE0vaGt3RG15STFEWG1EU1lRbzBBK3F1R0VYL2lRdHg2b0NETVFab25FQUUwNUZPb0UzVXdONFdxSjFDWFNLWnpKVFFCcThCT0hOSXJzaU9NV2l2N1p2QW9SSmlndEpRRXQ0am9iSkZ5SW5rdE90M0VGQi9FZXhRRUFHMUFLNENCY1hnNlp2RkRqa0Q1bE5qUVMwbTlkeGlRZ1E4T0ZTUlhZcThVUFBVUFFCVWN4MnhGalEyajVGT0FjOHlZQnlJa2NwaTZmWUVUQU9hZlJHaG1QT1U5OE1iRHJWSUg3UVZxSjRzUVFiR1NmRnVGbVlyT1poR3JtbHNkY3IvSER5U2FubnRUQzRZZCtKQS8xNWhYSU4raTNCVEd0RGEwbnp1blE1d2p5Q2NOUE1UVk9nbUs1RVdqb3FpOGttU2NtMXpsVUJ4Mk9nQ01UWlBreEJwQ0FrQ0FZc2pCTmFiMnYwQ3pDbVJzbWx1OFFoVW9VUXVoQXdnc0ozWVNqMkZEcy8vRUFDa1JBQUlDQWdJQ0FnTUFBZ0lEQUFBQUFBRUNBQU1FRVFVU0V5RUdNUkFVSWlCQkZUSWpKRUwvMmdBSUFRTUJBUWdBSU02ejNQY0t6ck1iSHNzY2F4clhGYlZqSng4Zkp0OFZkbEQwTVVjQ2RaMU0wWm82bWorUGMwVDk2TTBab3pSbWpBRCtOQ2FFMFpxRkpoV3RRV01VaXZGNlN6RzhCOGk1UmE4ZDM4REViU2FtdndGbldEODZtaE5DYUUrdndBZnhvUWdiL0dQYTZxVUNCVFFlaStXbTBDYXJ2dS90cW54L3AwUXNSTE1heGZZMU92NDFPczZRcm9UUW1oTkNkWjFNMU5HYW1vRmhXVi93MjRGSXBPdksxbG5XUDFzSHF0dWlFQU5qNUM5Rkl0eDZXVlBHanFURFc2L2ZXZFoxbldkWjFFNmlkUitPamEzT3M2aWFFQSs0RjlRckFBSWxCZWhyRnAyd0ZnS25abG1TMUFJR01GTEdMYTJNcHJXdEtiUjZOYjFnMXNhZXk3WHFRZEhyT3NLK3AwTTZRejdINTZtQVRyT3BtcDRYSTNFeGFpL2ttQldsZVVBK2RYV0NRbGVNUXhaNVNTakV4QXAzRWRrVWlMV0xXN0R4Q3BpdHB4dTIyaW9GM09zNm1GVE9zNndMTkNFZXZYV2RacWFNL3JYcXZ4NjBSdHI5UTcyZDZoU1krSzl5c3kyVXZTM1YydUZRS2l0ZStpeVcrUDhBbHZBbGczVzY3YlQ2bWpDTnpyQWs2enFKMUUwSm9UUW1vakZkaUlnK3pjb1c1dXVwMTlibW8yaG9KbDVEMUJFZ3hzRjJKV3pHdXA5dUdLdVp0ZGFuY09kTUtocnRVS3lUR1RyOTZFVmRqMTBNNnpyT3BuVXpxWjFnWCtTWXZzNkRqK3lRUnZmNDZpRVROT3drMUtycktqL0pYSHVPN0h3TDNKNlZwNHhvSStuaTZiL3NEb0VLYUs1MTE5QVRRbWhOR2FNMFpvd0lSN2xhamZvNkxOR1RSMFB4L3FaMndsWmdHL3NJczhqK1BwTkVIWXBQbmJyY2FLbU8wTlRvZEVqWDQwSUJxYU0wZjhTTmpVb1QzQXFuY3VJNy93QTYyWjFsZGZaaE9Ud2tUQ1N3ZFJPczZ6cUppS1BMQUJvN3J5aWg2eXFxcklCQXlNQzZoaEJpQURVS1VBRXIvaHFMVnRDeGIxclZMV084Q2paakRhZ2hLM2M2QUVxSHNUa2wzeG9hYW5XZFlLMi8xaU92N0M2Y3A3QnFGUVB1MjFNYTRpdk95Y2dHdGxxdDhxNmNVTysrb21wb1RxVHN6US8waUUxdG9BNzBhMTZuY1ZSb3pycXJjVFFiY0lBTVQ3RXpBQnhjNm1Dc2dIc2xZLzhBcGlWOXJndDVjenMzTGJHZTRRVjVML2VYMThuWTNQVytQVzAycURZeEx2N0lPb1JvUkZMamJGTjFtYUc5VEhwUnNWMklRa2JnSFJqTzVaZXBmU0tRU0svOU5XaGJZU2x2c1pnM3h4ak5XaTdaOHhBTmxNaXkyamFxTG5QOThmZFNieHJQNmZ2RXpvUzVNdElSMVlaRlZWdkZqZDYxVjdNNGUveVpvMTBncWkxd3BwQkFDUHBQV0cwRy9xTWc3bWFFdS9yRkVDSS8vVnpVQU5yNnBCWEw3bmpHNkhLc3JKNzJXRzVOekFMM1ltMkdCVlllejRPQ2xPU3JEa2FSNTl5aDI4ckFaNWNZMWRqWTcxUDhkYXhzQzNqTTcwbkY1bVJmeUlFQ2dDYUUxT244Q2RKMjFSMW0xRUEweGdRRXk1T2xSRXE5Tkxha2NSVTYwNkZtL3dCTXJGUnRRVnVZcWdEcWJmOEF4TUFNV3JLT1NyblBvdnR5a3NXdkdGU2dGc0hIdVRxOUdKalU0eHBTdkV3c2NqcVRWU213T1FUWGFEa2NZRFpPZmppc1BQOEFrRlBvTGxxeDFPd2JIREFPbmJVUjFJSmxUMUYvNXltNm9TK005RmxoRWU1RTF2elZpa0dNVmVucVRsVVZzeWxzdWtLRE1TeEwvYS91MG16cEJsTFhrK001dDYxbGRqeHRVR0JQangyWTRQSjQrWFZZMVZuTjRPUHlJeFh6Zm1TWVdhK096NUp0eFRqMjRhWnVYeGl2UU9ib3grRlMvSWZsSFNuTEV4UGtPWmRrMHJSNWI2T0hUeVU4aGZmalY5cTM1RitKdk5QQmMxeU50bGZsNTI5NnNOZkQ4Vnp1VnU1TnhmenQvSTAxWW12RFNueG8yM1cyL3dEb0J6eVhMVnBtNUlQTjUxZVB4UEgyVDRsbERKd1hNdXpic0xsR3JIUDhsZmlmSlJVbnliSXVUSXgxcjczWStRS3BhZFliR2ZITStyTHJ0VmVkTkZYSjBXdDhpbzQ4Y3U1dHg4UmN6QnZWUGpWZC9GOFZYUmYrdmhOeDlQNm5LWTcvQUsyVXA0Ymg3c2ZQeDdBYTJ5YS82VGd1WnNVWG1uak1qaUFPNjhYWW55Qk1sZVJ4ckxTQW5HL0RNbmdPYWUvSzV2NDZNbndDaks0ZXZJNDQ0MVlvTFlJck9WOFZ0eVh5KzJmOGViTTQvRnJUZ3NKZU1wOFE1VEJQSW9xTHpueCs3a3M5YjF6dVBzekdyY01saTZsU216RzZIaXVEcjRndDF5ZUplNEw1Y3o0NWljamY1TGw0L0hHdGpqOFpQYU5pVk1CdDZBK3BUaTFodXBYQ3R4OTJOKzFjeU1rc3g4M0l5RmF3MWhYOTIxZnNnaERYbFVxRGF2a2NBVHByM0VvSnFZd0l4SjJhaVpWaXBXMng0YXo3QW9UL0FGNGY0Nnc0MjExRXI2alUvWGNRMHVUc25IYy9YLy9FQURBUkFBRURBd0VHQlFNRUF3QUFBQUFBQUFFQUFoRWhNVUZSQXhBU0lHRnhJb0dSb2JFd1FNRVRNdEh3Y29MeC85b0FDQUVEQVFrL0FPWVVSbWFXcG4yUmgzc1QwMFRZSSsydEJYN3Bnd3YzT3pwLzFHU0tIM1ZmdFJmMVF5TzlqVkVwcGJPbGpQd1VLQXlEcWgxQlFrZFB0YXdhSXhHRWZMQ2R3a2VpMHZlUm1GUSt5SDJSdCtMNzRNNEtnVHI4cXRET2lQRHFEYnlPNzArd3NyanY4SzNzb25rb3FoR0p3ZndVT3Y4QVNqNy9BRnhjbzhoRXpZMEo3WVRZS3JPRlE5NlhYaUdpTmREK0RsVUkrcmxXbmwvcFFscEZqVkhoUFdvOGpjSVVPUlVldStyZFY3M1ErcWM4dW00cHZDZFIrUWlITkdsL1M0VkZSQldRK2xxT2JUZlpYVmV1ZlgrVTd5UDgyUStwcXJvVTVEVUdPY2dqUXFrRHVGV2RFNElreEhyelk1OVVjODVvbXdTRURVWTdvZWlNL1cxNURWTkVnSWYyRTVhS3huY0R6VU84SnRMS1FpckxCQ0tCanNtMWo4bzA5MWREQW0xS0lvaVl5Z0JCQnBSUEE3bE9MaEIveDNIa3p5YTduVlZhb3daRUtvN0pnOTAzaHVOYldUcTlvUmxhQ3FmTnNSbFFSVUdoTlpUWkFGdXhXeDhVeDRobUNSOExaRnJJT0k1dGVUQldtN1hsWVRPUllKdkNCMUJXMTRXZ1ZFWFR5NjVxZXNwb0lHRTJHNkpzSWVnVnR4ditOeEN6dU5FVUlxUGhGVUJXcXpDZFl3alE3cm1pdk1icnlRc0JHZUc5RTAvcUdJMHJhcTJaUENZbVZ0SUpjYUFBTkRnd0V5UXRpL2FjVGpJYkZJYmVUZ2xNSUlMcVFYRWVMaDZEQ01OWnM5bVpBdnhTYUFyWlBkeGdrVXZBTkJDWnd2RFJMVGNISUsyWGlMUWFUU1FiK2tKbkZ0QUFRQVNacEpIU0FtUWRyWVFhQVhKbnJBQ2FTWE9qdFExS0JEUXg5d2RFOERpMmJRWnZNbXVZbGJZZ3Q4VWlJbkFqOFhSeXo1Q2VmQzl3N1ZLbUh0Y090S3FhT3oyQlhFUU5vQUhHeENhUzN3V0JpcW54T0lvcWppYVFZMUJRd2ZnckVUUG1tU1NCWHFDbVNUQnZHRTR0Zng3V0NiSGhidytXSVR5NGhzdUdvTXFkaUhPaUtFOVQ0Z1lFMUttRHMyUzRYb0RNV3FpLzlOdXlOWEd2RVRuRWdJeTR4VTNQZE9EV3NqaUFtQ2ZFRGVzU1JUb25mdUFjTGluekJSSEFXMkFzYTBsV0pxUDlYQkVrYlJoaW9JZ2lJdkthU0dOZ0VrV0RySHFtVUxnU0NTTUdWV09IMmhiSmt2ZExiekV6VWpKVFd4c3BrRVRBaUR3NlZVWW55RUl4d3VEdlFyYmxnQWJRR2xKT3FlVy9wdW52aUNwdVBsWkJIcW5GM0VtekJrS2hnWlRSQW1tRE41NzVWS1JHSXdPd1Z4TUlwcytjSVFKcFdVNHc0eWVwMVVGb0VWMHZidXJCRzZmeDk1VHFCR3FLS084N2p2S0svOWs9IiBpZD0iYyIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiLz48L2RlZnM+PC9zdmc+", + "description": "Displays historical power consumption values as a simplified chart with background. Optionally may display the corresponding latest power consumption value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'powerConsumption', label: 'Power consumption', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'powerConsumption', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Power consumption\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":5,\"color\":\"#3B911C\"},{\"from\":5,\"to\":15,\"color\":\"#F77410\"},{\"from\":15,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:c2ltcGxlX3Bvd2VyX2NvbnN1bXB0aW9uX2NoYXJ0X2NhcmRfYmFja2dyb3VuZC5wbmc=:c2ltcGxlX3Bvd2VyX2NvbnN1bXB0aW9uX2NoYXJ0X2NhcmRfYmFja2dyb3VuZC5wbmc=;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":4}}},\"title\":\"Power consumption\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"bolt\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"units\":\"kW\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "power", + "energy", + "energy usage", + "electric load", + "electricity", + "power efficiency", + "load profile" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_pressure_chart_card.json b/application/src/main/data/json/system/widget_types/simple_pressure_chart_card.json new file mode 100644 index 0000000000..0fc417e8f3 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_pressure_chart_card.json @@ -0,0 +1,31 @@ +{ + "fqn": "simple_pressure_chart_card", + "name": "Simple pressure chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAgVBMVEUAAADg4ODf39/g4ODg4OD////g4ODYGDghISHj4+M8PDziUmnHx8eQkJDsi5tYWFjx8fH1xc3dNVH64uZ0dHQvLy/nb4LwqbRKSkraJkSenp6CgoL98fKsrKzumqj409rV1dXzt8HpfY+rq6tmZmbkYHbgRF26urq5ubnfQ1398fNuhph/AAAABXRSTlMA7yC/r1EOHTEAAAS8SURBVHja7M9JAcAgDAAwytE3/tUOGdAlDtIAAAAAAAB+p4/I181+HrHX83b0Ngs8zmS0XCVElUiKXEbkNh87dqzqMAxDYXg6IAkJebDBJBgMDSTv/4Q3bUNDB8OdgkP9D0YaPzAkeEB6a0B6a0B6a0B66wcgy4o71YJUz7OHc1czmxj91oKwEBnOLMUopOi2FgQsBpyZAHDHVreKUAoD0LIC+7LyfgRwCVz4Nay4tDakquqqe/wFWTI8pYklOSnmFMVR05KFmRRKrJSEV8qScWVtSJSj7bxapRgZ3ANmCfAFMkPjSxj1A5kAiXiuF9aGFDuqHwiJZAM8Ap7cRWDkM6OKRMUH8hyzOxkurA2Z4pF+XS28IYuoagX4IQJgi6RfEFNVxjW1ISem4uwLMlGFPgJN+xQWR0hToBn2hsAzB+sHYpRqA4KZKG0wEjKwpOQMI/IDwrLP3UA0SRa0Cvw+A/b4WPCJL2e0IWAvPX/I/w9Bvdeb8A/8/d6sAemtAemtAemtAemtAemtAemtriHmRCny3SGcyWeLQo+bQ5weh0dvDfljx9yWk4WBAHy1syGRBIKcEX7w0Nr3f8CfZWOpI6XWC0Y6fhdkXaKz3yQbGN9EwUEgzapFDhIchfDWLBJvwREIf8UiG/E+dku2QhEv4LEUHPDe2qxNpIiFOHgUZQYY3lsrE9kJs/NjGQC8Xz09jHx6kX+74su2kdnQHdIvYy593FvPLRIY0WO8zwXZD3JGxGUAcLW3nltkK3wv2McZMJmEbzCx97BI1QAT1rod003aXNJ1CA6FCTiqcKC574gtaHgXb+7jHr7Bi6VHQymlKbzfiIQq11yYwh7rCtM5oh58LKdvREJkbAo/4rsjNja8PnKml6QwWyPiQ3+Ru3tFoi5HVzB0mOvaoq24YDwnLc3I8VT36byaEDkrpSxiDT9hzEWooOtsI3ilkZkfUCRFGdwhwsUoyyIfiGl/zbFmqRCcHplFdPNWJKLvnUlynkCULsiE3GbCwJ34Qnr3rUhdgULt6qJ6EjyOdVM6bWkgWycSJkqlowi0iA1ApY+UnmYzHqp7I80e7mYjs7ub3YnUmNOg8UyxqtKx8Qez1k22SKSjSIMYQsRpBRPwW8iDBMFvRTRaN1DqRL1jIyCasD3h6TIZddRYVKNI3Y8kWFHUzrTIwzwuckZUtc4p5OJRVeDio1s9EmnDsNVIqTZteAPOtcjyIhYVN0FIqTo5InZfj98UkW47FO+wVGsSmWuR5UU45QY+hNMJkWOSJN0gGykkpkW2MSwoknKzd6gmROCIyYRIBBcsnkL+qakFKZcUaaguV2nCxw8tw9F2rjfmRD54F06KbGIZLCbCZXeDTst9DKCp0g7zaEincyIRoqbMhMheZB4sKlIj2pN7FHT9eBrKiiyHFma3luJZdPeaQmQBLCsCqUXMuwoITXFNUZRgH+pqToRn5brD8807xiGAJUSuiaLqS3wTzlIN373x2MLIev/E9ofz6g+IHHy6/gER4iXyElkLL5Fn4yXybLxEno2XyLPxEvnfzn0TAAACQRB80tf4V4sCesKMgxNwexpDTpNRxhMy2l33io3Z34kgRe15u9JqAAAAAAAA/GYBBOmvbsxu/swAAAAASUVORK5CYII=", + "description": "Displays historical pressure values as a simplified chart. Optionally may display the corresponding latest pressure value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pressure', label: 'Pressure', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'pressure', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 80 - 40;\\nif (value < 980) {\\n\\tvalue = 980;\\n} else if (value > 1040) {\\n\\tvalue = 1040;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 80 - 40;\\nif (value < 980) {\\n\\tvalue = 980;\\n} else if (value > 1040) {\\n\\tvalue = 1040;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1000,\"color\":\"#D81838\"},{\"from\":1000,\"to\":1020,\"color\":\"#80C32C\"},{\"from\":1020,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Pressure\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"compress\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"hPa\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "barometry" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_pressure_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_pressure_chart_card_with_background.json new file mode 100644 index 0000000000..7db7606182 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_pressure_chart_card_with_background.json @@ -0,0 +1,31 @@ +{ + "fqn": "simple_pressure_chart_card_with_background", + "name": "Simple pressure chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical pressure values as a simplified chart with background. Optionally may display the corresponding latest pressure value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'pressure', label: 'Pressure', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'pressure', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 80 - 40;\\nif (value < 980) {\\n\\tvalue = 980;\\n} else if (value > 1040) {\\n\\tvalue = 1040;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 80 - 40;\\nif (value < 980) {\\n\\tvalue = 980;\\n} else if (value > 1040) {\\n\\tvalue = 1040;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":1000,\"color\":\"#DE2343\"},{\"from\":1000,\"to\":1020,\"color\":\"#7CC322\"},{\"from\":1020,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Pressure\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"compress\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"hPa\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "barometry" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_pump_vibration_chart_card.json b/application/src/main/data/json/system/widget_types/simple_pump_vibration_chart_card.json new file mode 100644 index 0000000000..b1325ac7b5 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_pump_vibration_chart_card.json @@ -0,0 +1,36 @@ +{ + "fqn": "simple_pump_vibration_chart_card", + "name": "Simple vibration chart card", + "deprecated": false, + "image": "tb-image:c2ltcGxlX3ZpYnJhdGlvbl9jaGFydF9jYXJkXygxKS5zdmc=:c2ltcGxlX3ZpYnJhdGlvbl9jaGFydF9jYXJkLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjExMiIgeT0iMjQiIGZpbGw9IiNmZmYiIHJ4PSI0Ii8+PHJlY3Qgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxMTEiIHg9Ii41IiB5PSIyNC41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik0yMC40IDQ3LjIgMjMgMzloMmwtMy42IDEwSDIwbC4zLTEuOFpNMTggMzlsMi42IDguMi40IDEuOGgtMS4zTDE2IDM5SDE4Wm05LjkgMi42VjQ5aC0xLjd2LTcuNEgyOFptLTEuOC0yIC4zLS42LjctLjJjLjMgMCAuNSAwIC43LjJsLjIuN2MwIC4yIDAgLjQtLjIuNmExIDEgMCAwIDEtLjcuMiAxIDEgMCAwIDEtLjctLjIuOC44IDAgMCAxLS4zLS42Wm0zLjktMS4xaDEuNnY5bC0uMSAxLjVIMzBWMzguNVptNi40IDYuN3YuMmMwIC41IDAgMS0uMiAxLjUgMCAuNC0uMy44LS41IDEuMi0uMi4zLS41LjYtLjkuOGwtMS4yLjJjLS41IDAtLjkgMC0xLjItLjItLjQtLjItLjctLjQtLjktLjhsLS42LTEuMS0uMy0xLjV2LS40bC4zLTEuNS42LTEuMWEyLjQgMi40IDAgMCAxIDItMWMuNSAwIDEgMCAxLjMuMmwxIC44LjQgMS4yYy4yLjUuMiAxIC4yIDEuNVptLTEuNi4ydi0uMmwtLjEtLjljMC0uMy0uMS0uNi0uMy0uOCAwLS4yLS4zLS40LS41LS41bC0uOC0uMi0uNy4xLS41LjVhMiAyIDAgMCAwLS40LjZsLS4xLjd2MS4ybC4yIDEgLjYuNyAxIC4yYy4zIDAgLjUgMCAuNy0uMmwuNS0uNWMuMi0uMi4yLS41LjMtLjh2LTFabTUtMi40djZIMzh2LTcuNGgxLjZWNDNabTIuMi0xLjRWNDNhMy4yIDMuMiAwIDAgMC0xLjQgMCAxLjQgMS40IDAgMCAwLS45IDFsLS4xLjZoLS40bC4xLTEuMi40LTFjLjItLjQuNS0uNi43LS44YTEuOSAxLjkgMCAwIDEgMS4zLS4yaC4zWm01LjIgNlY0NGwtLjEtLjdhMSAxIDAgMCAwLS41LS40bC0uNy0uMi0uNy4xLS41LjQtLjEuNWgtMS43bC4yLS44LjctLjdhMyAzIDAgMCAxIDEtLjUgNCA0IDAgMCAxIDEuMi0uMmMuNSAwIDEgMCAxLjUuMi40LjIuNy41IDEgLjkuMi40LjQuOC40IDEuNHY0LjJsLjIuN3YuMWgtMS43YTMgMyAwIDAgMS0uMS0uN2wtLjEtLjhabS4yLTMuMXYxaC0xLjFsLS44LjFjLS4zIDAtLjUuMS0uNi4zYTEgMSAwIDAgMC0uNS45YzAgLjIgMCAuNC4yLjUgMCAuMi4yLjMuNC40bC42LjFhMS44IDEuOCAwIDAgMCAxLjUtLjdsLjItLjYuNS44LS4yLjZhMyAzIDAgMCAxLS42LjYgMi42IDIuNiAwIDAgMS0xLjcuNmMtLjUgMC0xIDAtMS4zLS4zLS40LS4xLS43LS40LTEtLjdhMi40IDIuNCAwIDAgMS0uMS0yLjJsLjctLjggMS4xLS40IDEuNC0uMmgxLjNabTYuOC0yLjl2MS4ySDUwdi0xLjJoNC4yWm0tMy0xLjhINTNWNDdsLjEuNS4zLjJoLjRhMi41IDIuNSAwIDAgMCAuNiAwVjQ5YTQuMiA0LjIgMCAwIDEtMS4xLjFjLS40IDAtLjcgMC0xLS4yLS4zLS4xLS41LS4zLS43LS42LS4yLS4zLS4yLS43LS4yLTEuMnYtNy4zWm02LjMgMS44VjQ5aC0xLjd2LTcuNGgxLjdabS0xLjgtMiAuMy0uNi43LS4yYy4zIDAgLjUgMCAuNy4ybC4yLjdjMCAuMiAwIC40LS4yLjZhMSAxIDAgMCAxLS43LjIgMSAxIDAgMCAxLS43LS4yLjguOCAwIDAgMS0uMy0uNlptMy41IDUuOHYtLjJsLjMtMS41LjYtMS4yYTMgMyAwIDAgMSAxLjEtLjdsMS41LS4zYy41IDAgMSAwIDEuNC4zLjUuMS44LjQgMS4xLjdsLjcgMS4yLjIgMS41di4yYzAgLjUgMCAxLS4yIDEuNWwtLjcgMS4yYTMuMSAzLjEgMCAwIDEtMi41IDFjLS42IDAtMSAwLTEuNS0uMmEzLjUgMy41IDAgMCAxLTEuOC0ybC0uMi0xLjVabTEuNy0uMnYxLjJsLjQuN2MuMi4yLjMuNC42LjUuMi4yLjUuMi44LjJhMS42IDEuNiAwIDAgMCAxLjMtLjdsLjQtLjh2LTJsLS40LS44YTEuNiAxLjYgMCAwIDAtMS4zLS43Yy0uMyAwLS42IDAtLjguMi0uMy4xLS40LjMtLjYuNWwtLjMuOC0uMSAxWm04LjUtMlY0OWgtMS43di03LjRoMS42djEuNlpNNjkgNDVoLS41YzAtLjUgMC0xIC4yLTEuNC4xLS41LjMtLjguNi0xLjFhMi43IDIuNyAwIDAgMSAyLjEtMWwxIC4xLjcuNS41LjguMiAxLjNWNDloLTEuN3YtNC44bC0uMS0uOGEuOS45IDAgMCAwLS41LS41bC0uNy0uMWExLjYgMS42IDAgMCAwLTEuMy43bC0uNC43LS4xLjhaIi8+PHBhdGggZmlsbD0iI0ZGQTYwMCIgZD0iTTMxLjIgMTA4Ljl2Mi42SDE3bC0uMS0yIDguNS0xMy40SDI4TDI1IDEwMWwtNSA3LjloMTEuMVptLTIuNS0xMi44VjExNmgtMy4zVjk2aDMuM1pNMzkuMyAxMDUuOVYxMTZoLTIuOHYtMTIuN0gzOWwuMiAyLjZabS0uNSAzLjNoLTFjMC0uOS4yLTEuNy40LTIuNC4yLS44LjUtMS40IDEtMiAuNC0uNS45LTEgMS41LTEuM2E1IDUgMCAwIDEgMi4yLS40Yy42IDAgMS4xIDAgMS42LjJzLjkuNSAxLjIuOGMuNC40LjcuOC45IDEuNC4yLjYuMyAxLjMuMyAydjguNUg0NHYtOC4yYzAtLjYgMC0xLjEtLjItMS41LS4yLS4zLS41LS42LS44LS43LS40LS4yLS44LS4yLTEuMi0uMi0uNiAwLTEgMC0xLjQuMy0uNC4yLS43LjQtLjkuOGwtLjUgMS4yLS4yIDEuNVptNy45LS44LTEuMy4zYzAtLjcgMC0xLjUuMy0yLjEuMi0uNy41LTEuMyAxLTEuOGE0LjUgNC41IDAgMCAxIDMuNi0xLjdjLjcgMCAxLjIgMCAxLjcuMy42LjEgMSAuNCAxLjQuOC4zLjQuNi44LjggMS40LjIuNi4zIDEuNC4zIDIuMnY4LjJoLTIuOXYtOC4yYzAtLjcgMC0xLjItLjMtMS41LS4xLS40LS40LS42LS43LS43YTMuMSAzLjEgMCAwIDAtMi40IDBsLS44LjdhMyAzIDAgMCAwLS42IDFsLS4xIDEuMVptMTMuNS0yLjVWMTE2aC0yLjh2LTEyLjdINjBsLjIgMi42Wm0tLjUgMy4zaC0xYzAtLjkuMi0xLjcuNC0yLjQuMi0uOC41LTEuNCAxLTIgLjQtLjUuOS0xIDEuNS0xLjNhNSA1IDAgMCAxIDIuMi0uNGMuNiAwIDEuMSAwIDEuNi4ybDEuMy44Yy4zLjQuNi44LjggMS40LjIuNi4zIDEuMy4zIDJ2OC41aC0yLjl2LTguMmMwLS42IDAtMS4xLS4yLTEuNS0uMi0uMy0uNS0uNi0uOC0uNy0uNC0uMi0uOC0uMi0xLjItLjItLjYgMC0xIDAtMS40LjMtLjQuMi0uNy40LS45LjhsLS41IDEuMi0uMiAxLjVabTcuOS0uOC0xLjMuM2MwLS43IDAtMS41LjMtMi4xLjItLjcuNS0xLjMgMS0xLjhhNC41IDQuNSAwIDAgMSAzLjYtMS43Yy43IDAgMS4yIDAgMS43LjMuNi4xIDEgLjQgMS40LjguMy40LjYuOC44IDEuNC4yLjYuMyAxLjQuMyAyLjJ2OC4yaC0yLjl2LTguMmMwLS43IDAtMS4yLS4zLTEuNS0uMS0uNC0uNC0uNi0uNy0uN2EzLjEgMy4xIDAgMCAwLTIuNCAwbC0uOC43YTMgMyAwIDAgMC0uNSAxYy0uMi4zLS4yLjctLjIgMS4xWm0xOC05LjVMNzkgMTE3LjVoLTIuMmw2LjctMTguNmgyLjJabTkuMiAxMy43YzAtLjMtLjEtLjYtLjMtLjgtLjEtLjItLjQtLjQtLjgtLjYtLjQtLjItMS0uNC0xLjctLjVhMTUgMTUgMCAwIDEtMS45LS42bC0xLjQtLjctMS0xYTMuMyAzLjMgMCAwIDEgMC0yLjlsMS0xLjMgMS42LS44Yy42LS4yIDEuMy0uMyAyLS4zIDEuMSAwIDIgLjIgMi44LjUuNy40IDEuMy45IDEuNyAxLjUuNC42LjYgMS4yLjYgMmgtMi44YzAtLjMgMC0uNi0uMi0xLS4yLS4yLS40LS41LS44LS42LS4zLS4yLS43LS4zLTEuMi0uM3MtMSAwLTEuMy4ybC0uNy42YTEuNSAxLjUgMCAwIDAgMCAxLjRsLjQuNC44LjQgMS4yLjNjMSAuMiAxLjguNSAyLjUuOGE0IDQgMCAwIDEgMS43IDEuMmMuMy41LjUgMS4yLjUgMmEzLjMgMy4zIDAgMCAxLTEuNCAyLjdsLTEuNi44LTIuMS4yYy0xLjIgMC0yLjEtLjItMy0uNi0uNy0uNC0xLjMtMS0xLjctMS42LS41LS42LS43LTEuMy0uNy0yaDIuOGMwIC42LjEgMSAuNCAxLjMuMy4zLjYuNSAxIC42LjQuMi44LjIgMS4yLjIuNSAwIDEgMCAxLjMtLjIuMyAwIC42LS4zLjgtLjUuMi0uMy4zLS41LjMtLjhaIi8+PHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjkiIHN0cm9rZS13aWR0aD0iMS41IiBkPSJNMTg3IDU0Yy0xMS40IDAtMTAgNTYuMS0yMS45IDU2LjEtMTAgMC0xMS42LTIzLjgtMTkuNC0yMy44LTcuNyAwLTExLjIgMzIuNy0yMS4yIDMyLjctOC4yIDAtMTIuNy02LjYtMjAuNS02LjYiLz48L3N2Zz4=", + "description": "Displays historical vibration values as a simplified chart. Optionally may display the corresponding latest vibration value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'vibration', label: 'Vibration', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'vibration', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Pressure\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 3.3 - 1.7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 3.3 - 1.7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2.8,\"color\":\"#3FA71A\"},{\"from\":2.8,\"to\":4.5,\"color\":\"#FFA600\"},{\"from\":4.5,\"to\":7.1,\"color\":\"#F36900\"},{\"from\":7.1,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Vibration\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"waves\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":1,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"units\":\"mm/s\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "vibration", + "pump vibration", + "motion", + "resonance", + "dynamic balancing", + "mechanical integrity", + "pulsation", + "frequency" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_pump_vibration_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_pump_vibration_chart_card_with_background.json new file mode 100644 index 0000000000..50dd23baa1 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_pump_vibration_chart_card_with_background.json @@ -0,0 +1,36 @@ +{ + "fqn": "simple_pump_vibration_chart_card_with_background", + "name": "Simple vibration chart card with background", + "deprecated": false, + "image": "tb-image:c2ltcGxlX3ZpYnJhdGlvbl9jaGFydF9jYXJkX3dpdGhfYmFja2dyb3VuZC5zdmc=:c2ltcGxlX3ZpYnJhdGlvbl9jaGFydF9jYXJkX3dpdGhfYmFja2dyb3VuZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxMTIiIHk9IjI0IiBmaWxsPSJ1cmwoI2EpIiByeD0iNCIvPjxnIGZpbHRlcj0idXJsKCNiKSI+PHJlY3Qgd2lkdGg9IjE4OCIgaGVpZ2h0PSIxMDAiIHg9IjYiIHk9IjMwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii44IiByeD0iMyIvPjwvZz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNMjAuNCA0Ny4yIDIzIDM5aDJsLTMuNiAxMEgyMGwuMy0xLjhaTTE4IDM5bDIuNiA4LjIuNCAxLjhoLTEuM0wxNiAzOUgxOFptOS45IDIuNlY0OWgtMS43di03LjRIMjhabS0xLjgtMiAuMy0uNi43LS4yYy4zIDAgLjUgMCAuNy4ybC4yLjdjMCAuMiAwIC40LS4yLjZhMSAxIDAgMCAxLS43LjIgMSAxIDAgMCAxLS43LS4yLjguOCAwIDAgMS0uMy0uNlptMy45LTEuMWgxLjZ2OWwtLjEgMS41SDMwVjM4LjVabTYuNCA2Ljd2LjJjMCAuNSAwIDEtLjIgMS41IDAgLjQtLjMuOC0uNSAxLjItLjIuMy0uNS42LS45LjhsLTEuMi4yYy0uNSAwLS45IDAtMS4yLS4yLS40LS4yLS43LS40LS45LS44bC0uNi0xLjEtLjMtMS41di0uNGwuMy0xLjUuNi0xLjFhMi40IDIuNCAwIDAgMSAyLTFjLjUgMCAxIDAgMS4zLjJsMSAuOC40IDEuMmMuMi41LjIgMSAuMiAxLjVabS0xLjYuMnYtLjJsLS4xLS45YzAtLjMtLjEtLjYtLjMtLjggMC0uMi0uMy0uNC0uNS0uNWwtLjgtLjItLjcuMS0uNS41YTIgMiAwIDAgMC0uNC42bC0uMS43djEuMmwuMiAxIC42LjcgMSAuMmMuMyAwIC41IDAgLjctLjJsLjUtLjVjLjItLjIuMi0uNS4zLS44di0xWm01LTIuNHY2SDM4di03LjRoMS42VjQzWm0yLjItMS40VjQzYTMuMiAzLjIgMCAwIDAtMS40IDAgMS40IDEuNCAwIDAgMC0uOSAxbC0uMS42aC0uNGwuMS0xLjIuNC0xYy4yLS40LjUtLjYuNy0uOGExLjkgMS45IDAgMCAxIDEuMy0uMmguM1ptNS4yIDZWNDRsLS4xLS43YTEgMSAwIDAgMC0uNS0uNGwtLjctLjItLjcuMS0uNS40LS4xLjVoLTEuN2wuMi0uOC43LS43YTMgMyAwIDAgMSAxLS41IDQgNCAwIDAgMSAxLjItLjJjLjUgMCAxIDAgMS41LjIuNC4yLjcuNSAxIC45LjIuNC40LjguNCAxLjR2NC4ybC4yLjd2LjFoLTEuN2EzIDMgMCAwIDEtLjEtLjdsLS4xLS44Wm0uMi0zLjF2MWgtMS4xbC0uOC4xYy0uMyAwLS41LjEtLjYuM2ExIDEgMCAwIDAtLjUuOWMwIC4yIDAgLjQuMi41IDAgLjIuMi4zLjQuNGwuNi4xYTEuOCAxLjggMCAwIDAgMS41LS43bC4yLS42LjUuOC0uMi42YTMgMyAwIDAgMS0uNi42IDIuNiAyLjYgMCAwIDEtMS43LjZjLS41IDAtMSAwLTEuMy0uMy0uNC0uMS0uNy0uNC0xLS43YTIuNCAyLjQgMCAwIDEtLjEtMi4ybC43LS44IDEuMS0uNCAxLjQtLjJoMS4zWm02LjgtMi45djEuMkg1MHYtMS4yaDQuMlptLTMtMS44SDUzVjQ3bC4xLjUuMy4yaC40YTIuNSAyLjUgMCAwIDAgLjYgMFY0OWE0LjIgNC4yIDAgMCAxLTEuMS4xYy0uNCAwLS43IDAtMS0uMi0uMy0uMS0uNS0uMy0uNy0uNi0uMi0uMy0uMi0uNy0uMi0xLjJ2LTcuM1ptNi4zIDEuOFY0OWgtMS43di03LjRoMS43Wm0tMS44LTIgLjMtLjYuNy0uMmMuMyAwIC41IDAgLjcuMmwuMi43YzAgLjIgMCAuNC0uMi42YTEgMSAwIDAgMS0uNy4yIDEgMSAwIDAgMS0uNy0uMi44LjggMCAwIDEtLjMtLjZabTMuNSA1Ljh2LS4ybC4zLTEuNS42LTEuMmEzIDMgMCAwIDEgMS4xLS43bDEuNS0uM2MuNSAwIDEgMCAxLjQuMy41LjEuOC40IDEuMS43bC43IDEuMi4yIDEuNXYuMmMwIC41IDAgMS0uMiAxLjVsLS43IDEuMmEzLjEgMy4xIDAgMCAxLTIuNSAxYy0uNiAwLTEgMC0xLjUtLjJhMy41IDMuNSAwIDAgMS0xLjgtMmwtLjItMS41Wm0xLjctLjJ2MS4ybC40LjdjLjIuMi4zLjQuNi41LjIuMi41LjIuOC4yYTEuNiAxLjYgMCAwIDAgMS4zLS43bC40LS44di0ybC0uNC0uOGExLjYgMS42IDAgMCAwLTEuMy0uN2MtLjMgMC0uNiAwLS44LjItLjMuMS0uNC4zLS42LjVsLS4zLjgtLjEgMVptOC41LTJWNDloLTEuN3YtNy40aDEuNnYxLjZaTTY5IDQ1aC0uNWMwLS41IDAtMSAuMi0xLjQuMS0uNS4zLS44LjYtMS4xYTIuNyAyLjcgMCAwIDEgMi4xLTFsMSAuMS43LjUuNS44LjIgMS4zVjQ5aC0xLjd2LTQuOGwtLjEtLjhhLjkuOSAwIDAgMC0uNS0uNWwtLjctLjFhMS42IDEuNiAwIDAgMC0xLjMuN2wtLjQuNy0uMS44WiIvPjxwYXRoIGZpbGw9IiNGODlFMEQiIGQ9Ik0zMS4yIDEwOC45djIuNkgxN2wtLjEtMiA4LjUtMTMuNEgyOEwyNSAxMDFsLTUgNy45aDExLjFabS0yLjUtMTIuOFYxMTZoLTMuM1Y5NmgzLjNaTTM5LjMgMTA1LjlWMTE2aC0yLjh2LTEyLjdIMzlsLjIgMi42Wm0tLjUgMy4zaC0xYzAtLjkuMi0xLjcuNC0yLjQuMi0uOC41LTEuNCAxLTIgLjQtLjUuOS0xIDEuNS0xLjNhNSA1IDAgMCAxIDIuMi0uNGMuNiAwIDEuMSAwIDEuNi4ycy45LjUgMS4yLjhjLjQuNC43LjguOSAxLjQuMi42LjMgMS4zLjMgMnY4LjVINDR2LTguMmMwLS42IDAtMS4xLS4yLTEuNS0uMi0uMy0uNS0uNi0uOC0uNy0uNC0uMi0uOC0uMi0xLjItLjItLjYgMC0xIDAtMS40LjMtLjQuMi0uNy40LS45LjhsLS41IDEuMi0uMiAxLjVabTcuOS0uOC0xLjMuM2MwLS43IDAtMS41LjMtMi4xLjItLjcuNS0xLjMgMS0xLjhhNC41IDQuNSAwIDAgMSAzLjYtMS43Yy43IDAgMS4yIDAgMS43LjMuNi4xIDEgLjQgMS40LjguMy40LjYuOC44IDEuNC4yLjYuMyAxLjQuMyAyLjJ2OC4yaC0yLjl2LTguMmMwLS43IDAtMS4yLS4zLTEuNS0uMS0uNC0uNC0uNi0uNy0uN2EzLjEgMy4xIDAgMCAwLTIuNCAwbC0uOC43YTMgMyAwIDAgMC0uNiAxbC0uMSAxLjFabTEzLjUtMi41VjExNmgtMi44di0xMi43SDYwbC4yIDIuNlptLS41IDMuM2gtMWMwLS45LjItMS43LjQtMi40LjItLjguNS0xLjQgMS0yIC40LS41LjktMSAxLjUtMS4zYTUgNSAwIDAgMSAyLjItLjRjLjYgMCAxLjEgMCAxLjYuMmwxLjMuOGMuMy40LjYuOC44IDEuNC4yLjYuMyAxLjMuMyAydjguNWgtMi45di04LjJjMC0uNiAwLTEuMS0uMi0xLjUtLjItLjMtLjUtLjYtLjgtLjctLjQtLjItLjgtLjItMS4yLS4yLS42IDAtMSAwLTEuNC4zLS40LjItLjcuNC0uOS44bC0uNSAxLjItLjIgMS41Wm03LjktLjgtMS4zLjNjMC0uNyAwLTEuNS4zLTIuMS4yLS43LjUtMS4zIDEtMS44YTQuNSA0LjUgMCAwIDEgMy42LTEuN2MuNyAwIDEuMiAwIDEuNy4zLjYuMSAxIC40IDEuNC44LjMuNC42LjguOCAxLjQuMi42LjMgMS40LjMgMi4ydjguMmgtMi45di04LjJjMC0uNyAwLTEuMi0uMy0xLjUtLjEtLjQtLjQtLjYtLjctLjdhMy4xIDMuMSAwIDAgMC0yLjQgMGwtLjguN2EzIDMgMCAwIDAtLjUgMWMtLjIuMy0uMi43LS4yIDEuMVptMTgtOS41TDc5IDExNy41aC0yLjJsNi43LTE4LjZoMi4yWm05LjIgMTMuN2MwLS4zLS4xLS42LS4zLS44LS4xLS4yLS40LS40LS44LS42LS40LS4yLTEtLjQtMS43LS41YTE1IDE1IDAgMCAxLTEuOS0uNmwtMS40LS43LTEtMWEzLjMgMy4zIDAgMCAxIDAtMi45bDEtMS4zIDEuNi0uOGMuNi0uMiAxLjMtLjMgMi0uMyAxLjEgMCAyIC4yIDIuOC41LjcuNCAxLjMuOSAxLjcgMS41LjQuNi42IDEuMi42IDJoLTIuOGMwLS4zIDAtLjYtLjItMS0uMi0uMi0uNC0uNS0uOC0uNi0uMy0uMi0uNy0uMy0xLjItLjNzLTEgMC0xLjMuMmwtLjcuNmExLjUgMS41IDAgMCAwIDAgMS40bC40LjQuOC40IDEuMi4zYzEgLjIgMS44LjUgMi41LjhhNCA0IDAgMCAxIDEuNyAxLjJjLjMuNS41IDEuMi41IDJhMy4zIDMuMyAwIDAgMS0xLjQgMi43bC0xLjYuOC0yLjEuMmMtMS4yIDAtMi4xLS4yLTMtLjYtLjctLjQtMS4zLTEtMS43LTEuNi0uNS0uNi0uNy0xLjMtLjctMmgyLjhjMCAuNi4xIDEgLjQgMS4zLjMuMy42LjUgMSAuNi40LjIuOC4yIDEuMi4yLjUgMCAxIDAgMS4zLS4yLjMgMCAuNi0uMy44LS41LjItLjMuMy0uNS4zLS44WiIvPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii45IiBzdHJva2Utd2lkdGg9IjEuNSIgZD0iTTE4NyA1NGMtMTEuNCAwLTEwIDU2LjEtMjEuOSA1Ni4xLTEwIDAtMTEuNi0yMy44LTE5LjQtMjMuOC03LjcgMC0xMS4yIDMyLjctMjEuMiAzMi43LTguMiAwLTEyLjctNi42LTIwLjUtNi42Ii8+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHBhdHRlcm5Db250ZW50VW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48dXNlIHhsaW5rOmhyZWY9IiNjIiB0cmFuc2Zvcm09Im1hdHJpeCguMDA1MDI1MTMgMCAwIC4wMDg5NzM0NCAwIC0uMikiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjE5NyIgaGVpZ2h0PSIxMDkiIHg9IjEuNSIgeT0iMjUuNSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMi4zIi8+PGZlQ29tcG9zaXRlIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzEwODVfNjMwMDMiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzEwODVfNjMwMDMiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48aW1hZ2UgeGxpbms6aHJlZj0iZGF0YTppbWFnZS9qcGVnO2Jhc2U2NCwvOWovNEFBUVNrWkpSZ0FCQVFBQUFRQUJBQUQvMndCREFBSUNBZ0lDQVFJQ0FnSURBZ0lEQXdZRUF3TURBd2NGQlFRR0NBY0pDQWdIQ0FnSkNnMExDUW9NQ2dnSUN3OExEQTBPRGc4T0NRc1FFUkFPRVEwT0RnNy8yd0JEQVFJREF3TURBd2NFQkFjT0NRZ0pEZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc0T0RnNE9EZzRPRGc3L3dnQVJDQUNmQU1jREFSRUFBaEVCQXhFQi84UUFIZ0FBQWdJQ0F3RUJBQUFBQUFBQUFBQUFCZ2NGQ0FNRUFBRUNDUXIveEFBYUFRQURBUUVCQVFBQUFBQUFBQUFBQUFBQ0F3UUJBQVVHLzlvQURBTUJBQUlRQXhBQUFBRDd4Wm52aDc3T1ozTjd2ZTY3dVozTTduYjEzZGRuZmJ6c3g5bnJONzNzT1poN091N3ZzNTNZTXlLRk1RQ1dCUjZIck83THVoN3J1NTNkN3ZudTUzYzdzUTVuM283QXc1bWJkamhCWm84K0d3Y2ZCcGlyM3d4MkpnbHo2UXB6OTF5UFUrMzUyK2V6dmM5WnZudTg5M2ZkNnpvdkZjN3ZPOEpxakJKNEY4bno0L0VuVHJNSENMaUE5M0ErQU9ablc3b1owZUN2cTM2MzIvZmQ2N3UrNzF2YzdjR0JvaU9UZXlkc09NNGtxSUhSNTJQRlE0cGdnVEhZSUVwSTUyTFBtclpsWVMycFlrNkkwaVVWL281OUg2VEJtYUdLMEJHU0k4M0VNQktWTXFtamNCSjg5WUk4dkRpOUxCZ3dtS1NwWGFva3JMNStMaVRTZ2dPSmZNcFY5RjQxVGZ0TXBZdlo5OVcxQ0twRjFQNWNvV2t6S2NHOE5DcUI3UEhkTThXSGhXeW9uRy8xMWN1UFd4Y2NLU2ZUWDg4UmFUUVJhWkkzalpiQXY5S0FkYnFNcitsQVJ3Q29kTVZDUzVSZkFBTzFVZ0xBM1Qwemt1TFR4Y2lURjltajVHcStZVGxzMTJ2TGhLc0NZemlKbEdUVEFGekF6YVZXZGRqWlBDbGlNWFdwV0FDRnpWSnA3R0Z1TFE2T1NlYng0WkRnNWlZMnUrTUEzMkNodU9OTjRZdHVnZ2t6T0xsOGlqQVJyQTZGSTIyN3lZQTFhSS9CVU9GWG82bHpyVHRhWEFFNS91YkFvYnpucmhNMm4yd0p0cStkaStvdkRXMXYvaE9nU3hWUlEwOGVBRXhuYm90b1Q5UG9Xa2g4WFBnQ3V0aVcwQmJyQ29udm9aeXBIbWM0MmMrZ0VValVDZGFxV002eE9IU0lVWFZ2cDlSb0VWbHVHTFJKbG5nS0ZTZU03VmE1WTBXMkJoOFplaWZ0ak5KMWtnKzBvTHI4bTlkVGVKSDVwOVJmWHFmenpjVWdLbHdZOHV1MlpvdCtkdEhyUFZ6Mlp4TU1Rc2VxVWFuODBKV3BJRWFUZmRkR1h5R2tiVU9QV0dzOVpMbTlhWWk5VEtrQWp5WUxEYnJISVZhbWtxRlhJUWNkZ3RtVkVkNlFjNnNCb3ZaL2Q5Uk5GcHFtVnFrUnFZYXNrNU9Ob3ZSUDV0ZzZMT2lkcTZRYS93QkNqd1MzQmJkVDVVQ0E1endHYkRnTjBGTTVjOVJJb25jelZPeXFxVC9RWjFOaU1YbDltSGVkVVRsWlZYS1R5cXNaeWNkYThvZkVlN1RWdXRzSStsZE11cVllL1ZTbjJ2akduelVkcnhBSlBwUFA1ZFVpcCt5b3cvSzJLY240Qzl6NktNOUNOYlJJc2EwVVNQWVUyZ1ZDV0V5eTF2cy9ObFV2UFA4QUhJelBLVzJQcjlTVk45UmkyM1ZIcVVXWExWWVZyN1MraXNmaGFtNlFJaWNtWjh5Rk5QRDJ1ZGZyclEzemU4MUV5V2JYR3hrenY1clo1OXh0VDZWYi9MK1ZYK012Slo2RVYzYTVGV2ZsNkIwdXB0eWlYR2h3VzMxUTJmeFNOUWdVUkhpWlZTcDNwYTV0UWI3eUhjZktaR0xMNXRyZFd3bU9XaktZbDlzOUI0VXdSdWRyMW9BMkZxOUtLSjlkVWVNN0tQVjBzVUNJZ2lRVTVkT1dJcTl5ZWRURGFaaGo2NFUrbkhtL1FSTlpBSTdXak10NTUzN1JSQ1B2QTIzT0dUd0srS0t6ak9ITzFtTm9qY0FhWEt4blVzWi9vamlvZ05FVzVwR2pLZFVpcnJORDgxcVBWM1RjYjROZzhXdDRmTWZlcWZibnJMbUxSbEU0KzRqaThKZUNjSDJuTzl0OXR2cS9RWUxyNi9RZk1uNzdpTnRRc3FJS1RMdm0vd0JNb0RUZlZjRnBmRGZRenRNVk1yQkVscWk4d0taUmtmZHBFWXZCNDh3YkYxaFdLTUhjMmhkQXRoRTAwYThHUjV1bHpwaGpSMEpnb2MySFdpaG1aTXFobFRDQ3BUVlNDamNDc1pvRnNDZEdvNnBJS1YvL3hBQTJFQUFCQlFFQUFRUUJBZ01HQkFjQUFBQUZBUUlEQkFZSEVRQUlFaE1VRmlFVkZ6RVFJaU15UVVJSklDVXpKQ1pSVW1KeGdmL2FBQWdCQVFBQkRBQkcvd0JpSi95ZUU4ZjZKNlQ5L3dDbmwzcndyZjYrRytrL2YvM1A5ZUZSUDhxTjlJOVA2Sko1OUkxWEovMm51OUw0WXhmbStLTDBrOFArMldTWlUrYmtYNDFaWGV2aktpZnV5Q0gwNnpESDUrd2hFejErZlVYejhaTGRuMGxsemxYNmhGeVgxNUpPL3dDMEVpWjZTTTJxTDRxaklQVDNrb2s4ekZCVlZKOUZUcW92NVcwRXdlcHVoWlN2NVNib2d4cW9uci85OUlpL3Y0UlYvcy8ra2ExUEtmNnY4K2tSZjMrTGZIcFZhbitlUnp2VEVlcUw4SVBUazhJdjIyRWFqVmpYeWtWWjloVVpiK0txdjFWV1QyeGRmeWx3bWtyNDcwTC9BQ3RBTkxaOUpaTFNLclVTdFZTOWZwRDRIU0dUaktUQy9adVZoWHVaTWZqSXp3OXVzRUdlY2x5elNHMlNkTDZyUDUvNkJsY3BHbXczdHAvaXh2S255VzlyWjIrWmIrdHYrcG95a3lMOThQcXlNSE9WeTNYZ1l2VElNakZOOEhrZ3JIc0lZaUpWYXBtTDE0WC9BT0tldlA4QTZPUlBYbG4rcm5POU5UeW45MkpWOWZzbitacWVrV1gvQUdOYXhQaW5sVmxuK1hwcjIvMGhUeXJvVlJxdW5zZlcyVXNHcVBWckVkY21aY04zMFZLdEpLRU1nQmtxTElWSlQyVUxiSEJaQmpsbXRRTW1zOWYwSmFHUmNoaDdkcUMrYzJ4K1o4SmpvTGFUS25PUjE2ZFo1UVY0eThabUlBMFN1cldCV2VhUU1ZcXE1eWxTdG83UEwwL1AxWExFQXprRTB0L3FPMHNJNWxhcTZnMGxzVENQYzR6cDZ3eG42enlzaXFsblNYeTcxNkRoNmJVY21XTVgwc2QyQTBsZkZXNXlxeXM5d1JpMHg2ak1DTVpGOHE3UDJWN3BGU1JGL3BGNlIwdit5SDBrTjJUL0FET1NOcVZVWW45K2YxK1BBNytzNitwSGlxclZkUE8zMGgrcEk1WXFNU3lyOVVkbEZmZFZWUmxvWlJaL2d3Tmo5WE5ZNXIxaW8wMzJKak5pMHRSWjlRWGpDVkl2amJ1cXpKNW1Hcyt5QXB6UC9JMHhpWTVQUHJzOE1ZK3VLci9uU3lhRGFIVlZndWcycEZheVY2V3UrMXFkSko5Vi9VYzJEU3ZnSHhPMGR5N3M5QmRIdmtyVkJ1V0ZGTmxrV1BmRVIxWjdZMlMvVkdBbGNtZDVPRkNQdWJudStsclBrcmsyaHh4alN4akxVaWFqcHcyM2VpNlNTZzhzQVdFclJVK21haXo0Y1Q3SGRHSzZlaFdSZkxtK3Y0dEIrLzFNOCtyT2tpcnNWcUo4M3N1NkVtditEV2ZYaGlHVzBiNXNXRy9LU21xUXI4cjhjVExNMlZxMnZGNHNsdVlSTFFuaFJhVkw0UXZnaGNpK1VSRTBlb0FBWTFiTFhrSVdwTlJzVDA3cTRtQm9TckdJRkI1WDNUOTFiTjJYUmxEVks1R0VyUTBROE9jSUhwZmtzVjBzeUhPQWdkTkpUVXNMcGozUlNOcFgwY2dQaitCTWJmUGwxWVZ2WDlZU0pZd21CQ3JlMFpNVHp3V1NLZ0xqNXJHYXpWN1lPVVpyZEJXa1JrOSs5VUlWdjRDUWZWcDN4UVFwcHN6UGVldGpVelRRUk1GaVJWVHlJeUJ2UUtJRGREMDZ2VEw1SE5pS29ybU9oUmswcG5vTUVsaWRvZWg1ZVVMTmU4ejFIRmc0Rll5dk1RbWw2MXNTZGxZOHhrNncxbFNMc3BaUHV1MzBId1ZCczlWbmsxdUlYeWtQMC9MRXJKdElSc3RRNWo4NHI1cW95T1NjajI2ZFYrcUUxRFdaUTdRVjhJa0oxbGhxZEpQWC93QjJyV2V0bzdxTHRKeVBzdmtpdjNTbGk2dGQwTm1WdUswS2pjTmNCVmhVQWQrazJXcVUvWUhSd1dTRTF5M1FHYytsSUhvN055MExSQzFOeGZRenRFaGFtM2tKMWI4V1VyMHdRd2Z6Mm50ZFNoVys5UzhDNUhHVkxVenhBSmRpWnRjdjB2U0RMNGpCNld5S29jQXo5Rmt0QnR5V1ZoSGtzOGQrTWRtQXdiRkxaOXNWMkV5NVk4dmQycDdZY2pENUdWamV4ZFVYOC9FOE9LYjhqTmVabTlaejdFV2N6bkt2eWNZTHBKSkpIZ3FVaXlWZ0tYNVhiSjhiWHdDSzhWVkwyaElXWm5mTi93QTVMbDh1dmxFOHhLZXYycTlaenkya2FJZ2tPODN0WG5SeUc3dW5zWitpTGxSSlJmTkxId2JvcVlpRkV2RWN4bkVnM0l1ekY4YXV3UzYrMWZLRTBjMkFycjR2VVRpWTZrbDkrbkpYSG1ZWjlUZjh2aU5CaHhNbzdPNTl6S3RrdmJVYnRDWTQvWjJiTFdoVzhLMnpaTk4rcU5DNHlQcTUvc3VJUFc3ZXpRM1dmenpMYUlIcGFINVJRaFJneklxQ3EraGNvTXJNb2dLbW1IeURKUjFhMHRHcU5JQVpzdytqUkt6cTZ4VHlZMmc2Mk1pdWpTWkNOakNkVTNJZFMzWk83ZlZRYVJERmQ5UVc3c1d5NW4wNG5UczlMeFpvNDFsbVcxTzVxV0Zsa1lJbCtsWmJjZGVqRVgxMk9Gc1d2UGVzbjdCM3BtM2xxUFprK2ZNcDFqNUxxaGg3Mkh1alFnYXhXaG44NC84QU9MdXB5V2h1bzFSS3IvNWZQNktZZFRmbmJ4TklOUGNVelBuYkdOQW9rNHJtVTc1cStxTGxsU09vRW8wbUJoZDFZM1dpVlNpMlRURUs5cVpvNzhGOStONDdOaGN0TVFNWXdjTllkRU4zSjlrdDhOUlhOT29WVCt3cVF4TXFOQTZzbnJ0QnFZOGdPaW9PRVlvbHF0RDFEOG1zSW82WUczZVZkdHQ0Z3czbnFad2ZUSmtMUjZQTTVITjBpQUszMWtlS0hPbzYzbkdqRVMwVDNLdERTYituU3RLd2FrdW9GcytJTlpZR3kyZFRvMHBPbXRnYSttSGxkamdMOXQ5YWE5WXlaTFI1MGpFcnJReTdTTlFNMWR1MHo4Yk1DMndRbVRJQ3NWU3RwZEJNZUwzakV3MFY5cm9CZUZIYVUvZmtveVd4d3EwVGFZWnB6RUZtMWUxZGxCZ2FzSGxOenBpNmtweStOeXNoMHV4Q0RBbHUrTXp0MGF4dE95VUsxNDZ3YllvUGdjS21zREtZeU1PU3ZwU005RnRYSTdWRUtGeVZsNHloWmUwUFVJRXp0MG5PaEtwVzN4T2VUYURNaU52UGlYYnp0eG5HN05obCswOG5tSjN4WkhZNit6Qklsdk1EcUZIR2FUVFRaMWFobmwrMFNEanhNaFF1VGZmeWM2NEg3YURPN20rMkczeTQxemtyaFRGM0s5WE41clFpZDcwWWVZZlltNkxuOXVtcjE2SGJ5WGRkbHFZdU9zWEpVODh0a05mT0RBZFc0KzVETVJFQ0lTem02clVIS2xxc01JNTNzbGU5ck9jMWRMSUtsS20rSDZjcHErZzdLZ3l4WnRKeXZEVWJsREk1R1M0T2RCaVFiZG5mdnhTRThrSmlGQnI1V200VEtjdVhVc1Zydzhmb2FIOGVJT2dzaXhXVW1MNVZoaUxOV05ObnNqeU9IU2dQdk85U0lYd2c3S1pMTjA1cXVhQnoySjcrV3F1eTBGaTZFeXNjZVJvVng4VFhWN0VQam8xeFdQcFF1akhyNndZT3RCbXdzU1ZhYmZRdXFtbDl3VnExYWxyMmZYdU1hNk1JREd0ZTJyVUtaWmduMlRDaGtEYWpHM3NyWUZlem5RTVpXclJUY21EMlp1Q2hJbUpZaW53VTdJUCtIVHpPM1lMaWhZcm1JeTFydUJibkluZWE5S042UEVaVFZnOExOSkZsdWk0QUZuTStYcUdieE1HQ3N5MTQ4M2V2U1NhRVlKVFZTVUtRTFpIMXFFN2NPcUxIZ0d5cldmdkUyUmZhaDVQWDVmZVVKOHE4UkFhSjE4L3FhYmxMWFkwM3U5bnJzczlNL0htWCtiL1R5OGhLZlVyUW9WVjZYdXhlS29YdFJPWjFCeHUvMUlteG90SjE5NEhrK0lUcG04Rnl6R2U1UkphT1A5eEhSN1VHZ0Y4cW9WOUxGUkp6NlFoVHBCUHF0WEhSeFc3YjRjdGhJSzQrcU5lSXN5TGxEMHJyamJYcnBoRjBaSTU4SzhDZXNMWldjSU5lckkwWG1OeGtmVkdSVFdFOCs1R28rQU5SdlJ5TEY2SkxJUzlvZyt5MzRUVHZvRDlMeGQxQ05ZVnI4TEtRZ2FWNFlScHlMYjU0UFBwN1Z1bDg3bktobW51T0lDZjdrT2hZTS8xUW1iSGN5byszWEJhOE5jTTlQMUdZMjJqNDV4VTNzcnVoVFJsTUJvN2REVTUxOU1qY01RZE56blRoN2l1UnJGOVpqWWVpWldTclgwT1ZqbXZtRTZsejdxZWFQRFJGYjliQTV1N1lIWG10VjBqYTNzWGdaSGpBV1F3MlV4M05yQThQQjlWRzBoM3FaeTJGeDZTNVRtY1BPOFJ6RGxNZEEwR1cwWW9pdG4wUE9IOHpsMWc1eHc0Rk1iMCtyZ3p1QWZMaXNQcU11M25QSjhlTkdXV3pCdHpzcXcwUzVLNkxZdTZIYWZoNHJqMXU3ZFNTdG05aFZzWmc3RTJSRTlkYk1NYjBjUlVmT3FONGVYaU5jZEZFSUZYMXN6RCtkZTRpU2VieERRNmVRcmJ2Mjl6WEtrclhyelN1aDdoYzlhQ0x5K2wwWi9LdmNtbUowMDc0c2ZxQTkvSmUvRzNKREJFeXQwWXlRNWgyVUIyVVorWlBtcWpJUmZXUlpvZGMrOCt2TStjNjNmNW5mRHY0WVg1bFd3UUJPWDNjWFNsbFRSM3VNNkFBQmsydkhGWkZkRjcyL2U1MVkwR1FxTXV6R1lCZTAwWm5mOFBzdnduVnJaUFM3RVJQcGVSWC93QkhidmNubUwwWE04VjU3LzB1MC9KNThoalIyVitTd2g4Mll6QXJYV25WYU5aRXlUS0JmcXhYWTZsLzU5TDNGN1NDZjI1NUhCNXFDSWZCWk0zQlpSb3NKTzhXdlNvcnVWOWxQS0h1dFRXVTFla2F1Ym5uK1hsMnMzRnUxN05Nb1hTWlY5Y3I2YlZ2aUFsbVdmOEE4TDM2eVdUMjZpdFpRUmY0OTdVTmtNc3lsUk5WVWVOOXpIT25IK1ZXN05aZk5uaVhRbzN2dTQzVnp1cURlVm1wc3gxVXpqRDd2d0ovZUp5TitseE1tbENWMlFrOG5wZjV3ZTI5YzFlcy9oN3lFbUo2OXhjbGJ2RGxlYjVuTGJwRXY1RzZBN01QUGUybnMxREtNUGNtNmJVakFZd2lLUFZkT0tJUW1ZNkd5eDUya2Z4eEYxQ0Y0bzUwV1lhUDNWYnBRZUphck85YzlQQnRGVjYxenVOSWRyMDBHUjBtTnpudUc0OVZmTWQ0UllsT2RhNlowVzVOOTlqbkhRcHRibGR2cG9IZld6ak9ublBhZnFHbGU5eTFpOHp3bkNzZFRaNVNiTlUzNi8zcjVZUGRaODZIU29YaHZkQnJ4cXA5YkNlUEc5SDlrSVVjN3hKSG9uM0dWeUF5ZnlrK1RxV2RLenEvSS82M1BiN3JKTE5xY0hQSXZ3RFVvTmxoTDRVcjRkWDR3WU1jZTczTm5US1BobnEzQm1vNTR4SnZoYXFkeDVyZndQWXlOcUJGL0NDSEY2Vnk1aG1LdzZydnFmYVJkaGk1M2FvbEtUdUdISjhZN3RGMGpNcTFBQjRvVllUcWRmNW1qSDZIUmp3UGJlS1FiekZ6MllydktOZU43T0taa2RuTFZCZGd4SGI5eHhnaXpudlNLTmt2enZSSDc1UG50UTNpRDdXV3NESHNOM3ptY3VZQWZEV2pjQmxyUU1YU3NXcGFNZVIwb3pIYi9VNVBFcW9lRGhURlo3Tk5uWmcvYTM3Vy9CSDJ3NjJsOC9FdkFNemZIWUxVQ0w4RDY5blFlRFB0NjRub1lmM2h3QXVuQm5zUDBLTmlKZTkxbVRXcDdrcTJvR29qNm50TDZKR2Y1UExnU2t2Z3IxN0N5aFBjanJhUDFlSXRsa0RlWjJBclo1eDYxRFdveTlQTmU1K2h1Y3RVL0J4dUJxdHRDSzBySHBGWDdWeW1YU1VLSFM4L1JiK3FPSTlBbUZqYStidHpTU1Y5M25SZlRPY3pqbXhOc2xTemRKeUxyMWJVaG55Tm42SGpzLzE3akROZmlKRm9Qd25VNUgwU1BIZXJReE1UNFM4WDZ5dWNPckpMenNvTjFYSE9xeGRFeGJGdUQ1c2ZtdXVaU3AwbmtVN3F1ajVGMWZLOU9vQzhQMXNVejlWRWNwc09ONnFlMXptQ1kxbCtjZDBya3VkMmlJR3RhV0xCQ081Ym1ocXFXNG5yV0k3R1I1SGxtTTZCb1F0TFZudVA0ZHdISTdiRnJMOTcvYm5OK2wvYzF2dVprVld0RmREMEgxNUxZNk5rQmJKb01tcEh1ZmtaZnB6b0JsZ0daMGVNTUtnNnBvNXlKdFpzdnBVV014bmF4ckliSWRvd0xsZ0pkT3BpdDVBSjZXTXIrS3hmbmtCTFBTeHRqU1Z0Zm1DWDRiM09yOGFSczQ1VmthRWVESVJ1aWtIVlh3cExKTXhKVjN2TUdDQ3NoUVI1Z0ZSYkVzTmprbGxaTlZLNjhVQTY3aHJlZ0NmWEViRjNEM0grbTJYVld4TUVkcTV3RDZka290ZmpKbmlOUG1ETVhRTWZlNS9xNElxbWg1VHRyUEo5amE1cDFHazh2emsvN2ZkVmtOTi9NdmhsOUN3VE1uY2owcDc0TkNQWmsraGM0MC9TaE9wR2hkcUhqTFpyYzg1TDd0TE52SkhZY3VTTDNkZngzakVHaTZ0MU90YU5YdDl4c0R4WFZKejQ1ZjBPTzJXZ3M4cDYwQzBqNG56NWJjYy9qTWIvQURQVWNUYVI5KzNKSHMrWnp1cFdKS05yUGJSNWMzZXl1eXJNR2F3RW9qWjBtWnJWekpBZTN2RWsyK1hpTkM3c0V1Mkd3a2g5MlNwZnBPU1htOUlCZTJFbzIwOXNOQ2ZIMjg5cWlJcU9SYjlQWFllSzdXV2RrU3NmQ0xrdlhaTkxCNFlkRU9sdDBJYk1EbUpaZlJZOFhKRzZKSGpOeHp1YW5LazlOeVJMcjM3RG1teWJ0Y3d5eFRqSjNzbDJ6bTFza0VXTVhvczhUdFlqYXRCNk85RkV6cVBHYitwSlFiREUxWng1N0VHczcwZU92Zyt4MEpNeHJlWVhOcjdkamJnejZNK3Z3RnprZk0rMDVCbXRGMnZ3clRyKzc1SEE4UnBZTG13dy9PaU9BRnJIcmc1VzRnNUx5ZFBQOUl4ZDdoeEl0QnYrY2RHMHZ0cXoxTGplU3kvTXh0cW9OMStMdjRmVXRqL053KzNPOEE2US9ubSsvSnM0STh0OGJTYnNNYjlaYXBTTTRQc0ltS2FwY1Nyb0FnUXJaSHpVSnBJTGR3Nk8xTWVxVXVFUFc4dHNKZFNYZHEvcDNncDRvNE9xaUNOZGpvMWFzaURyT2dEUGN5ZHk2TDVyTFVXSzNENnUwbkM5REdVR3ZqYk5SaVpXalVtSWtWQlZaSjNRdW1obWlsakowR0lLYXNzLzNpdC9qSzd4cjBiYWphdXc1OXBlYmRVZm9BRm1hclVPUHpYUzhWRkpMV2htTmMwMzVISDNoK1R0QWlaSVZydmFqbCtpWm4rTkVyeWlySVVqcWVHZUJXbHR4YlRHNUt2YjFtZW4wWExOWEJuSHhiWUdPelkwS1RJMTNuTnB4bVMrVXJhck45Qm54QnN6MjJYS2QvQTRJb2NKVkpzSFY1Ymt0R1U1NG42cUsyU1RxSFNlWVZ0WHo4a3lPK00zK1I2aFRzOHc2d05hQTFnNlRxSHQyT3ZySkhMdCtZRWNmak9zVE0zUE1UNjVMY2hldmF2RGs0QW5ZUVV3eGMxMElRYno5VkNTVmRNR3VETXZmNXJZZ3I1NkhUZzRnWTZxVGtRZk5hR3doVk5VTGtGeWlTWStXNVZ1emlaalk2S05HVzVJckZWeVd4ZjdwYnBoN2x4V1JOZlVySlZoenNaaW9UZElIa0lqNjBVc3NaV09XTFRNQVIxSnI0bVo1TUZ1cWVSMFF4bEtWTEYrbFd3MWJtV3p0b0V5TTh0c05udEx2SUt4WmI3TTJWQ1pEU0JkNHlxWjNQNFFwMjA1bmZvdnoyZFVZV3M3RGttMXNoYTFyTW42R1J1R09oNWZuV0lnRmJ5NEdtTWJjcjAzY0FNcEx6elNaaUhPN25wOVlYMWNGa0tlc0VGOVV6R1Z1ZSs0UXYxSXYyYzZKWm9PZGF6aVc0bjFPREpObEFrQkdGOXdlUmVzMVI0SFhVK3A3M2hlaWd5SFVJbTY3S1o3TllmVzBLbXZ4VTB3ajFReTVlL25LOWN1eW1XSFhlUVVzZnQ1Mlp5MWM1K2F6Wi9ZNXUyNnhmR3drRzJOUnpQVjBuV21Xcm9VbGJHay90YzdPYU9PVDFXMnZYY2hMUDhBZUdHblIyWjJKYldISGpCQUZJQ3QzY3dXNDVZN1lCV3Z0Yk9sV0ZUajNVNVpSZWprekdkeGVXTDJJTDBOa2Z1czZOMU5BdFF6azg5aUc5WTIyOXRwaEExR3NTcFo3b1UrdHJ4R2hRZ2NESjRMdTVPL1BXemV5RTBCWUhIZ3NabllMTzdLVmRicGM0eXArc0xOekw0Q2txVUJYdVE2dDBOYVY5b01TTTNPSHdSSHFOYlBHOFhTMUdyNlp6enJjdmJobWVGUjVjSm5lODc4QnhPNVFCR3dJNnlaMW5OK3pkazdsVDA1UFlpd21mOEEvOFFBVFJBQUFnRUNBd1FIQlFRRkNRTU5BQUFBQVFJREJCRUFCUklUSVRGQkJpSXlRbEZoY1JSU1lvR1JJek55Z2djVmtxR3hFQ1JEUkZOam9zSGhJTExSRmlVd05EVkZaR1Z6ZElPVHRQL2FBQWdCQVFBTlB3RC9BS0g0Umo0emp3UVk4V2EyUE0zT1BDTk1lTHRiSHhOZkhnaTQvdW9qajQyQ2o5NXg0elZmL0FISHhGMi95R1AvQUV2K0xEQTQ5YUZmNHZqeW5pL3l2L0o1ZnkrSng1REhpY2VDNDhYT1BkVEhpZjVQY1J2OGhqbEpJdWxmcWNlNmw1Ry9jTURpWjZoSUI5Q2I0QnNJNlNPU29ZL3dHT1ZROUVLYUg5cGhiOStQL05jK0UwZy9KRVdPUDdMSWVqTXN2MGVVcU1lOUxKVDBLZlFCamptYTNwSFBLZm1JOU9QQ1NHV1kvV1NVL3dBTWVFV1YwcFA3MUp4L2RVTUMvd0FJdjlud0dQRTQ4Qi9KNFh3T1NEVWNjcEp1UDB4eEs3WFpwZ2NFcG90cElmbWNEK3Y1aWRuQ3ZtV2F5ajY0UEhMK2pGTzFVNDhqSXVtTWZ0bkhGcWpQczJZZytaamcwZ2VoYzRISDlVNVpFai8vQUdXWnZxK0Y0aXBybWY4QXdndGdjRERSYlZ2cWRYMXRnbXdXU1ZZZjhJTndmeTRKNzgyay93Q0xRYjRCc1JTUVNTMzQ3allNTVc3VXNRUmJqd3ViWVRld3FxbFFRQnhKREc1K1dCdzJjd25iNW91OGY3Zmd6WThRdGhqM2NlSVhmZzhBQVRodXpUcTJxZVR5Q0RmamljenpPRVZGUVI3eW9lcW5xY0ozWjV0cEdua0JjUnI4c1I3dG5TeGlRTDZzYkl1T1VqZmFrZm1heUQ4dDhMdlliWUJWOVdheWo5bkNidE5MRWFrQStidmFOZnBnY0tqTUp0dGI1QXBFTWNxSElvbWlpSjhQc2dvSStadmpnbFgwbXIxTXg4OW1MdWY0Y3I0S2NjcHlOS1NGZkcwdFNWK21tL01FNEFBTUUvU0NXdWtESHhpcGtBZGZJbTR3SDNWRVBSUllsUnI5b1MxY29zRGdFcTFQVFpqRUhCOEFsTEEzMUpJdC9MeVZSZkI3ekMyUGlmRnVPckhLTkd2ams3RCtSdXpESHdQcWNOM0tDSFhNUjV2YmRqaTBLemJXZHo4Ym00VDk1eFIyOXBua0pXbmh2d3ZhN1NPZVEzazR2OTVQR1ZpK1VTbTM3Ykgwd2kzam9vM1JtRnQ1TmgxSXdPWndlcUtvZ21QOHA0eWZJRWVlQ2VyUTBhbVZWUGhwQjBMK1pqNllVYmhJeTFWWjZBa2FFUDRFeEh1ZlB1azFVWXFSRDRndWQvb0xZUGFwZWpkR0tDaUErS29jQXNQT3hHSE8rbnlTa2JPTTBZOE44cjNWVDVqL0FFeGI3enBibkx6eXNCN3RIQVFCeU9rMnQ0amhnQmdzcGlqeVNpSjM4TEFPd1BQaUNPSUhIQ0FBMUZIbFJyekduZ1o1eUZBOG1PNDNzeHh1QmlvOHdMaEJ5VmtvNHlDQndHc3F3K0xBNHBHY2QyT1BlV3dlekxNbTBsK2d3ZS9NQkVvK3B4eldFbC80WTVyVHdsYi9BRFlqQTMrMFpuVmF6NjZSWWZ2d09DVXlLcWpCOTRSdmc5NzJKU2Y5MDRQY0JrVmYyVlFEQWF6TFMwNS8zbjBnZlRFaW1SSzJ1ckZsRzE4WkxnS0xnV0ZodXhHMmw1a1pwWVI2TnVRajBKR0hkUVlHdTRuUEVYMGkxZ2JXVGh6T284SWlIYkxkbTZKTW81VFNqZVYrRVdCeEVoYWZNUlRMQkZZRGdtNFhHQko5cm1lWmtTa0RuN1BURWdOK0p1cjRoc1VvNnRWbTB3ZUNrOEFxQUJGK1F3aHUxTFJTK3daWkFCM1dsQXZLM3dvQ2Z3NFJMeVpYMFpwL1oxMCtNMVFidWZyYzhyNHFIMFJUQ2o5dnpXb1B3THZJUG1UNmdjZ3dEWmgwcXIxaW9vWCtHSzJ6YTNrakQ0c0NNTlM5RGVndVNtc3FnRHdCZVFTTENENUtua1JpSUhZWnRubGZSNWM4dnVnUFVDcW5hL2t5REhORmJVY0RoSlZOWmZwZ2YwZVcwdThlckFFL3Z3ZUllUXp5ZnNwcXQ4eU1mM3poRCt3dXR2NFlBTjJra1dBZldWcm45bkFiZkhRYmFvSHlLQlI2MnVNQTNFMllKSEg4emU1d083UFh4bHIrUTFBNEIvN3B5eDZoZnFxZjU0TnRJajZQbU1OdjVGeUxZamlNVDA5ZWtFTHF1L2NMNlI4NzNHSXdDWmFMcEJUeVJTbnhJdVI4cjRpQjJWRkRtOUp0cDdBN2wzZzRvWE1GWjBZemlqaldzSlpleFQ5YjdRbTUzcndGamJCRFRSWkZXWmROYWxYM1dLMkVqZW8zbmxpZ2N4MTJXMVYwbGlrNEFWRUpSRnN4c1UzdGlhaU1pVTBVNFFpNjNXK2k3QWcyQjREMUdDb2VOcVFNaVZJNDNFbmVVL2hzZkhFZTRDdGpFa1k4ZEtzQnBQbU1OSmVwMk9pUGJlU3R2Q2dlbHppMW5xMFUxVHY1Rmcxd1BTd3hTeWlXS2xxVTJMQmh3YXpteitsOEdZU3l6bkxvYjFGandad3ZQOFY4VTZCWTZYTE9renhVd1B2Q0prSzN4ZmZGUXJ0MnY1dmNSajZ0Z2J5OWJJWjMrbTVCK3ljTHdwYVZESVBraWRYQTRWR2ExQWlRRHhJQkErcllmY2FIbzVTYXBENWErcU9IZ1d3YkgyL3Bybmh1Ky9pSUVzVDZDL2xoZTkwTTZHeFpkU0FmKzlyU290NWpGeHJwczcvU0ZVNXpVSHk5bXk5ZEFKOWNFZmZSNUxEbG9QeENXcllTRWZYQzhJSnVsZFhOSjZhS1dKaGY4d3h4VkkyekhhSW81QjUzQU9KRHM5QVhLNVdBSk4xSW5tMUVuMUdJNGRWcTNMc3VvWG1iNFpJaTZrK1ozWXVTVG1OSmt1WTBFU0M5MUZSRElKb2lSd2EzSEdVa1FaS21ZWmRsK1owZEw4ZnRVRlZITkZ3dU5hSEN5aHk3d1ZsSFMxa3FtLzJHWTAwa2lCcksxa2JIUnZMNGpERFg1bWxQbURLb0FNOGtVa1R4eXFRUUF4WU5wQU5oYzNUUzFkWFJVb3JLRis3czVVU1JYaERFaTBsaW5nT1lwRUR0UWpNb3hNWDViQWtQcVRkYlNXQjhSaE9wN1l0QzlUVHgvak5OckZ2VlZ3eHRKQlFWR2lYeU9rbFhCOGl1T0lqemFJNmYyaU1EK3NaZE9yMkhwdncrN1JWQnFhNS9FRHBiNW5EbTZzMWtiZWVPdU1yZjVnNDdJcUpRQVBsL3A5TU4yY3FvQ1hhL21GNGZ1d3c2bnRBRXRYSjZLT2VCd3pYcEhXK3hVbzgxWHRuMEFHQUx5UjlIb1Z5bWhBSHYxazE1R0Z1YTJ3aCszcU9pZVQrM3lwWWtFeTVwV25aeCtaQndEWXBtVmZWOU1jd1UrR3lqS1VpSHl1Y0FiNlZxbW15aU1BZUZKUXB0Q1BKamlicW9hU2dwcWVUU1BHV3RrTDI4R1pCZ1NBQ2tmcDA3a0hnU3lVU0lnOUJmRmtVUEpWbXJYV2V5UzlVOXJieHh1UmlCelRVMjF5ZkxxaEJwN2Q5SzY3QWdnY04zTENNaVZTMEdiUVVsbmRTNy9aU3F5ZFZBUnA2eHVSdUdLc05HanlUMDlIV0xHZzNndEdnamV6RUFNYkErZUk1UFlLYW9hbHkyR3VqWFMwczFwWUVDVkNGSTlTNnQ0TERldUFwaHFLNGRHNEtTYlE1dXdrcDFrZUtaUWlTTWtnQko0QUcyS2pNbm82YWdPWTE4MldRR1daVkY0SldCaWVOWGRpVTBqU0xrakVrY3B5bXZ6UTVZS2t3eG5RZ2lyS1lIMm9hZXJwa0FJSlFLV3hESzAwZEJTOUkzcloySmZyQ3J5K3FoK3dtWG1JZ0U1TGltdWtCelJxL0pKWkV2L1J6dUdwNVFmd2dFNDVUOUkramZ0OUEvbkhtVkF4TWZxUmpuUGxGYW5TM0k3ZkZFL3dCdkN1RXVaTTUvUmZuTFFWY0pIRnBjdG5JUHFCaUs0cU1rejJrL1UyZXhlSzJZQlhPT011VGRJMGFDRndPSmljblp1UE5XeE1MUjkvTXE5ZkJGSFdzZnlyZ2c3YlBjMmtFbGRWa2NkRitKOGxCdGcvZWROZW1GeTAzaWFhQTlaejRXM2VlRUgyblNmcFcxb3o1MDlQZTJFR3RjKzZhdWZabFgzcWVnV3dJOEM0QVBKc1FzRmo2VGZwSXpIL2svMFdwei93Q0hvSXRMMUhrTUl1K2k2SzVFM1IzSWsrQk5BRTlSK1pySEZyTkhrRklESzl2N1Nwa0pBOVMrSnB0bnRzMG5semFzcDVtc3pLUWJMSzRVRG1VVytJS2Q1RkM1UEhUeEd3dFloUU5JTjdldGhpbHBwcXUxVTdNem1PTm1BS2dXVDF3RVF2TFJGMHVXYTdFZzd6Y0tjU1BVeUxIVjVlckViU29FS2tHM1ZHbUxGcWRkRThBbGlEUExKTGEzUGRHUGtEaXE5b2xrUktjaW5KZHFXUGRiZUZzeHNQUEM1ZlV6TEZCSTVFWldnbVliTjIzMiswWTNPL2lNTzVjeVRzSlFwVTVybzFwZTBxQmtCQ1hBYXhVNGJOUk9YaDZQRTBRRU5SQTM4NGlNbldsWmhFamdIWjdFbzN2QTVYbnRYTlYwSFRDZ2hGZlJoNWpyanA2eEZhT3Naci9DVklVZ1dZdGlXZVNPT3Zpcms2UjVITllrYWFyTHBsMjFMSUFMTXFMYjNTUmhXTzN6MzlFK2Q2Tko4YWpLcGlWOVZBeEU5NTgyNkpodWovU0NsSS90NlUyamtieHVOK0tjZHQ2VTVIMHN5OCtKQ2FSVVc4VnVjVTI0NVpuUldnNlIwbzkyS3FBRzBZZTdLTitLTmcxVjBMNmJVeVUrWVVoQjdTaVVGSkZIdm9SaXQzcExNeEdYVVA0Yi9lVzhSMVQ1amdvdXdrdCtwc2pVY3lEMUNGOFQxZkFIQ3JlYXBxUHM4bXlnRG1TZHhDK2Y3SnhSdlo1cGx0bHVWdU81REVlcUNQRnJzUENPNEpvcGY1M25HYmkyVVpBM3ZTdTMyY2JEam9HcWM4MlhGTExzS25wRFZobDZQWlM0TmpUWmZBdGc0VTdyeGhRZWJOaVloSE5QUlF4d1VGK0VLQWNHUEVuZWNXNnRKU2RXL3J6dzJYeVZJcUpkL3dCbEpNMmtXNXYxU1dQbUJ5d3RORUdYUUJ4bEExajAvd0E4RG8vT3cxbmZKWjRyL1FkYjVZS3hXZUkvQklNV2xVby9pbGRKcVA4QWlHSURTTWJpNEFTZDRtK20wR0taSjFaTGRUVnNZcHgvK2R3UFBGWFE3QndodU5EaVdsWStscFlXUHJqb3RuN3pUeGJRNjJFRlF0WXFFSGtZM3JBRlBPTnhqOUdmU3IybkxwS09tQ2UzTlZTTHNKakVSYTRhbnBuUTlrcklVT09sUFEybTZlMGZSVjVUVXBGbWRaRHRLdjhBVjg4aEJkWTMyd0srNkN2Yml4bmVaelVsZm16d3RTWlZWVllPcEtETkVabmdOUXlXZU9vY0lab3pxRWpFRWlqZ2FkcTJrYzAxWkVpalUwc2NxWEV5RHRNakJ4cDNrYzhTSUhvZWwzUjJNSm1LeG5nOGtVWkt6anhhRWx2R1BHamFVM1NUbzROam05QUIvU0JWdHIwOHloQkhOQmpmSFM5SnFTWnFicERrTDhsbmRMTXdIaXd2aUVnNWJuMlhFUTlKY3JEZGhHZFB2bzI3SllIRUIwOUl1azdvZE1hYzQwNEVzUllCQnZQa04ySVRldnJaV0RQTEx6THVQdkpqNVhXUHNxQzIvRkpVYkxNTTRwSTlNMlpTQTIyRk5iaWJteGNYdGV3Sllsc1QwNGp5em85U1NNb29sYzZWa2xaT3U4N2syV3h2ZGpwc2J2akkxa1RwVm5sR2dwcWFvQ2ZlWmZSYUxCb3diaWFaYm1WN3JxMEM3cEl0TlRHbSt5cmE5YjJCWjErNlErQzhzUkFpb25NdXBwYXh4ZDNQeUZoNEREYytKR0tubzlNVkt0L1NyV1NpVkQrRzZERXVWYlpEZmpzcEVjajltK003eXVyeStFM3NBOGxPK3orcmhSODhleXBKWStNYkJpUDJkV0pNM0kybmhEV0pkVDZDV0kvTjhhM3BwdC9ZOW9XOFpKOEZxSTFHS3FFVHdSRWNLaEMwZ2ovTi9PSThWMnFHbnJIUDNNRWcwTTM1VU5QTjViQjhkTTdHTldzSWhtY0xrR0p1V21Sbm1pL0RXSTJLdU9ISU9uOUFsdzlWUU1BdE5Va2NwVlNJSmZ1ejBhZS9qbzlPL1N6b0ZtZEYvWElWdE5tdVhSKzhzc0pUTVlJL0VUZ2RzNDZmNU1sUG10REtnYW1xWTMrMGlzZUN2REwxNDI3U1hJR09pVWxxWFZMOXVzQUpNYktlSlZlNmVWeU9HSjd6Wi8wRmxPakw4MmZ2eTBnNFUxUWVPa2RSamhaR1hNY2t6UUdHUXlwdWtnbTV4ekx5azRqZHFMSVF5NVlTT2tQUmJNRUNSMXdGN3gxRVE2ckEyTnBGL3dBaUJsMVFhZnBOMEh6UWsweVNrMmQwSEZONURYWGMzTWNHeFgxSDg4cVkrckpUeGpyVFRNUjM3WDM4aVdQRkJnMHAreVEyWktKYmg1RDhjeEJBOEZQbmpKYUlISnNvZ0FFVUY1REJHNUE5NXd5SjVKSytNc0RtRXlkbVdwZFNyVEg4S2txZzdvTzdIU1NvWnhUMHFCRWhvSUR2QVVjbWF3eEFkTWxSR2RNZ1BBMjhEZ3M4MVhOSzVabWtrWGNTVDlNYVRiSFJ2cDVYNWJWK01jZGJUeFZNSHlMUVRnWUxHa3JEcTRSU3FZblB5RDMrV09pbWFxWkI0U1F5WEh5MUpnVEptV1dKNzlGVkx0VUh5VjJqUG1oeFRRbWtsbStCaUhnbTlGY1J0NkU0em1PU2hyREtiR2ttSkNsL0l4ekJYOUM1eE5VeU1qOG9LcEdCbEE4dFFTZGZGWFlZcG5hVkJhNndUb1NYalBpbDJQckhLY2RIQWhwSmF0N1MzaE95aGtrUGlOMUpNZktDWEFSOHA2Y1pCSVJITWxTeDBzOWoyTnU2SzZ2d1NxajhKUmZMSmt6TDlHdlNxRk5NamlPUm5nS0E4SllaTlkyUjhab0QzY1YxZWhNS0VtSG9sbWM1M2JMeHkycWE4bE5Kd1c3d0d6eHJmTFlnbVU1eExJRFNaelRIc1FUdHczamNzaDNNRHBheEFPS1oyRmZrVlJ1cUtHb1hlNmdIZXluaUQ0WXJab3FEcHJSUUFFM2E0cHE5UndKdmRDM0FucXQyempKSXZhWllLWWtSNXRTQmRieGZFclJqV2g0Z0R4aXNaOHVpT1paZEFYak9ZMGt2MllKRWZXRXNFckJUYmVBekx3QnhIR3VXVU5RVGZTWnBBck9ENTMxZk00TExEU2VFY2R5c0MrZ1VRSEF6Q1o2Y0h1UVVkT2tGT255RTVQcmpOY3FueldvUHZXQTAvdmNZeWo5SCtXdFRnOENaWW1uYy9ObnhGVmxrWHlJdmo5VG9ZV0hkYlozQkdJSkhpY2ZFcElQOE1kTE1nTmJrQ25ubStXRnF5blFlY3NRcW9SNXlERXlYUU5qcEJRdFNWUlBjcklsQzZ2enJvZjFMWTZOVmp3dUgzYmJMSjVyMzg5ak81UDRLandYRXRMc2FoZUlrcDMzS2Z5bHJIeUl4VnpLVEllenRDQ0k1VDhNaWpTM3hBKzlqbzFER21iYzVab0lqcGdyUU84OFJPeWw4VU4rZUpZaEJYb1Jxc0VHNmRQZk1ZMzI3OEpQdVlxWmdhOHdEYXd4NjFzSnJEN3lHU002WEhmaklQYVRDVTJ3enpJNW4ya09kMHVnRjZlUzNiY1JpNnVOOGtZUjFPdE1VY3Z0VTBjUTJtWVpUVktvTFNxRjN5T0FvMnFMOS9Hb2xTMGlFWXl1amFHa3pQWkNwcHM0cFpoY2gwNFZWTk9CZDR1OTI0eWtxNFFySEN0Uk43WFZaRnRUWk5uTzFoVlVrbkNPUnJheDltK2laYnNJMGJKODlXWnJ3cURaSVozN1RVNVBVV1k5ZUI3TEoxYjJvSjNnei9KSjZmU0twRys4T3k1eFREZVF2WWtHdERZcmFLVkpzdHA2d2dWVktkWVpvQ1R1a1Fua2VaUGljWkIwbHpGS1dPclc2VWNkVE84OENrSHVGV3JJL0pvUmo5Y0x2SEd3MEJmOEFFMkJuZE1qK1NGS2NBLzRUaXVuek9pR3NXKzAwUWtENnhTZnM0YkpKY2xyZjd1WXhDd1AvQU1sUEl1TXVvamtHYm5uYUYyRVJQNUNNWm5UamE2ZUJaZjhBVEdXdzZJQS9HV0h1a2VuQTRrcm1tai9DL1d4bE5kRlgwRTZqN3VhSnc2SDB1QmNjd1RqcGdnenpLWUY3TkcwamtWZEY2d1ZBbGp0N216UFBGZXFGWkR3aG5YN3QvSVhKVStUbnd4bE40c3pwSFhkVXBZcEpISVBCbExJMk5rWk1wbHF1MUxEdlZvWlBqUTNqY2VJdnpHS2VCelJKSWJOVlE5K0JqNzYyRmo0aFd4UlBya1pvU1JVUUM2c1hUdldGMGxqNWp6Q1lCUmhEVHpYbnllcTdhUmhoeFFrNm9aT0JVNlRpRldocEo2a2FZb1ZadXg1VXp0dkI0MDhoOXc3c3huTVdYMTB0MWJMbjFhOWhLVjNxRlk2Z1J2UW5XbDFKQnIzVnEramkzUTVxcDYrNEp1U3BBNjRDbTBuYmlON3JpRnpWVFpQUjZVblNZblZJOUtMZ0NSbUYzcGlRa3BHcU14eThZaEpCUTV2b0FnekFOMUhSaElCb0xkbVJKQll1TFNxamRmRXJHV3A2TlZBZVVXVVdkb0wza2prVWRxUGUxdUcxUUVDa2owVmVXR1A3ZWp2eDJURDd0aDJsSDNMMnNOQnVvb2lqOUZzMU5VcUROYWVZTnJBM2x3RjB4U0FzTG8ydGNaelNqTE02cU11cVVsb3ErZEdXUjJrWGdKTlVZYjF1ZVp4VUtkbXg3N0tTRWIwWUNFL254bThCa29MN3J2SGVTT3c4VEU3RDFTMk15bld1cG9oWUE1akFHMjhBODVVZVFqeDFqSFNLWDlaZEhhdzhLS3NKMXRINVhjQng1bHhqTzAyVE8zWXBhd0MwY29QdU51RjhaTlV0Qk5ET0xPakRkYnpCNGcrR0tLUU9qTDJYSE5HOFFSaXRSYVBPS2J2ME5XdkJXOG1IQThEaGw4TVZWV2Ezb3hVeWJsbzh5dHBNWlBLT3BWUkczSU9zVGNqaU1sR2ltV3pJUWJGU0R3SU53UmhVQ1ZxRVhFMFhBUytvM0szeVBqaXBtMjhjeUFuMktmZ0pCYnVrQUs0NWl4NHFNVURxYXVLTVhZRmVFMGZKdDNFRGN5bjBPSUVFMmEwZFB2WlRheTFrQTd5a0N6THpGd2JFYnBsSzFWTEpkcVY0bmE1RER2VXptNXVCZUY3a2J0UzRvV0dsWmlCUFR5a2ZjVEhnUXczTEp2U1JTTCtKb1l0aEpSMXhaVUtwY0JTZTFzMVBZY2RlQSs4bkNkeFRoc3hoRWt1VmF6ZEVsQTR4TWQ2c3AwbnR4c0d1TVZTZTBWRkhIT0pGbmo0blVlRCtVMXI4cFJmcjRRb1hxWm9tamFhVUN5N2RRYmxyYmxuUTdRRG5LdU5qYW16cDBWNnFqVk9ETXk3cGxYa3dGaU9VYmJnc1N2SDBteXFxM0JyMzJjclJFU2JOK0lMRFVyVzdRdU1ETW8xa3plbW9aYW9Gb2lUR3dXSUJsa01RdElyS0ZrdHFGaVd2bWRmUTVqUlYxWE1CQWdsUnBFRk1oQVlxQkt5Rm4zalRwN3VKOU5IV3lMdUtSSGRESWZsZUkrREpIaW10VXhTVTM5YmdKTG5UOGNiWGtVY3daVnhPcU01cFQ5cFNWU2RhT2FMZjJsTjJYMzBMSnhGeENEVTFBcHdSRFdLRC93Qm8wbmlqRVhsakhXamE1dGJVRldML0FKc3pPT3hGWW5FQUhnelczMjU4UmpLb1JDdGRBNDA1bENuQ0tid2RlQ2sraHhFMmlXSjFzeWtjY1pyVG1tcnFPWGRIVUE5a0VjbUIzcXczZzRnblpJMms3ZW0rNi9uYkhHNEZpTVV5cit2SWd0dHYzUldLUFBjSkJ5YnJjR05vK0JOamZrUVJ6Qkc0akV6Yk9TRjk1cDNQZHZ6QjRxZlVjUmlQL3FOVWVDamY5aTU5MDhpZXlkM0E3cU9jdG1OSERHV2VqWW5ydUkrL0UzZmkrWTM4YWVQMmlzeTJuSWQ2Wm0vclZML2F3UDNsL2d3eEpFUlExUmpNbE84SllncUJ4bHB5YmhvN2E0bU53QWJxMEVZcWFldHBYTW9VTHdKa1crMmg1Q1VkZE9EQWpoUnhHR2xyVmpRUG9mallOMUpJbTRtTWt4dHhVbzJLaVVQVHlVYnlQbytPblk3d3dIR0ovdEZIQXlMaVJOY2RiUnFxTkF4OTRBV3NUeDNhTDhRaHh0Z0lzem9sWXkwcmpnVHZ1amdjR0RDL0ptNERNQ1dycVFyOW5OckdseTBKQU5qM3doQkIzNkFlS1plWmRlZFRUelJSbUNTMFRJVkNobEt5eU11a2xuSFBIUTdOVGxzRmJYNVRTMDlRMUUwVjBWQlU2dElFeGt1U05UYThDRmtEVEc0a0JYdFhIRUVXSnQ4TGpGVlZFNVZtSnVYb1h2Y0VFZklrRDhhNG5qRWs5TFNtOFZZaDYycVBUelBhMGpuMWszM1hGQk9KMEtWSHM5WlJUZzJFaU9CZUtTNHRyQUtQYXpyZmNJRHFrUXA3TUpEeWtNWXY3TkpmZnFqTFFzZDRNWndxamFQVWxraXJnT0czQzhXNUNkQXdQZUI3V0hBSmtlTlFsU1BmUjE2a2dQdkE0M1dLbXpERkZGZFpHNDFzSTVONHV2anpHQ0xYMDRqSjNNdDFkU0NHUmh6VmdTQ1BBNFp3a3NETnFlaWM4STJQTlRZNlg1OERZakVpYVpvWDdNaStCL2lDTjRPS202amFXSmljY1kyUGp6QjVnM3dGMDA5V3d1b1gremwrRWNtNWNEdTRRem1XZWhwN0NhaFp0N1MwL2RLdHhhTW5RNDhEWnNiVGFtbW95UkRXU2dXTTFMSUFUQlBZZGFOaDVNcEdLdVhWUjE2VTVnYW1tN0xMSWQreW5IQmxJS1NDeEZ4WWllOHRKTEJBS2RxYWZpZkVSdWU4dTlKQmlTWVVrck5Cck1iRGVFZENTQWJieDF2TkdHR3FMK3ltSXU3a2I1SW52WXNiRUVIdGo0eGlyalpDNlVwRU5RUmJVRHBGMVpTZDl4Y2dqZHpLb0o1dllJMWtvYXBibTBoaU81dkJocFBEZ01Wa2lLY3hnb3BIcGFoWmx0c3hMR3dPemRXM01WSlFOeE5zZElUWk12aW5qbHE2TjZjdEtUSWhRRXZZT3BadVJPSVUydFBzMklNREEzS0h2QmIvQUprSjRFR3hqR3lpTlVvUlpqM1NqZVBNV1BtaDVZbFl0bzdiMHFFN3lmQWVMQWFEM2doeEdOYmxEczJkanlsWG5maGNuZU84NHhUdUJUZElhUVBzQWZlMXFOVUorV255QXhPQTBGVWdqWi9VV09pVDFRaHNYTEdDT1k2NlpqeElWK3RHZlE0MTlTT1ZnMWg0SEVSdXVvWEI4aU1JL3dEUHFJNzlpVDNrUE5ENTd4aTNhUUFqRlhDWUt1bWt1b2xRbS9IdWtFQWc4aU1QVWV6c1pXdEpETGJWb2JrYmplR0hIeXhJdHBxZVk2b3BoNE10L3dCL0VjaU1TSUdsU056TEpSSHZSeWdkWUFIZzVGaU9lSXdVaG1wSXkxUlRJU1RwQTRTUmdra0lTQ0xuU1J3TldxdThrTWpHbnpHUHRSenhzYkJUd0t1QnFVM0I1akZPb1JxNlduRzNVY0F0UW82a3FIaHFzUnlJR0tzcXNPVzNDd0ZqM0luNE9wUFpXVGVMZ1hQRXZHc1ZkU3cwNFNQYUxjRm1ZS1U5UnlOOFE5U3JvYVdtRWtMb0RkRGNBb29zZDRBeHNRMHFVbThPYlhFb3VHR2tqM3ZrY1MweVZOZkhIZXBncFZkaVZsTDZHV3p4NkRvVTN2ZkVXV1VnaXpCTXBkS0xMd1lWN09tSXBvYTViMFlpd3hVa3h5MDVxTnBGUk02MjJNSU1vQmpCUFZOdXpiRWhFOVZsODB4Q0ZPQVljL0svYUh4akZJK3pGUkhIWm9aU0NiYWhjRUhqNEh3VTRrY3gwbGRIS0duVUR5SnZjRC9Sc1RneVJ6VTBSaUk5NjZHd1BuYTErWWJFNDJjTlRJdDRuNVdaVFowOVZCSHc0bDY4bExReXBOUlZOK0JhQWd4TVB5bzJJQjl0WFpIVmV6dnArT25uSlUraXlZWVhrSW9lb3g4U3FrajZISEZCSkU2Zy9VYnNUSm9uak5VRkxyNm14QndJbWtGRFd6cFp3b3VRc3FrZy9tVmNLU3JhWlZOaURZNGttMnpVYzREUmlRQzJ0U0NHVnJicmdqR2RVNzFDWmE4b2wwUWhyTEtKQnVzeEI2aDZ3NTRwcDBrRnFuWk13RFhLNmdiaS9ERmZPOHFaWm1rYXdNR1lsaXF5UkIwUGxjSmlHdUM1blRtWDJpeUI3U2xGRmdIdGV6S1FiODhSTzU5cmdwQUpSRmMrK0xucThkMkk1eEtsVjdIS0xBQy9WWHNCMVlBZzJHSmtra2xxSzU0b0NaQ2pGQ0JGZmVXMDc5T0k1Rld0V21rVll5bkE2aURyZmQ0S0w0UXBCUjVja0N3VWo2T3FvbGN0MWh1NGJsSGhpajJjTUdTMEVHbE5rcWk4YlBZZFFIVllDNUF4RlRRK3dJa0cwcDZZTW9JRHNZMmRsREUyQUp3YUJKS0NoZ3Bnb3BvdG5vWGdqTjFpQ1F1bzZRYlh4Ly9FQUNjUkFBSUNBZ0lDQVFRREFRRUFBQUFBQUFFQ0FCRVNJUU14SWtFUUV6SkNVU0JoY1NNdy85b0FDQUVDQVFFSUFQNEEvd0FyL3dEQ3g4Yk0zOFpBZC9WVCtOZkZ5NFBuY3NTLzF1V0IyZWJpRUhOZjIvVTVKbTVsdEthWWlVa3RCQjhYTGxpRDVxV0JBU1pqKzI1T05CUHJzMzJGbmJzY1lNQzFDVTlqbFVkWnNaWjk2bmpMQW1meHY0cjVzU3hBSm9UT0VuOHUvc3h2N3MxR2hrN2RZR3Q1Y1ltWk1ETDdEQ0FrekdZd2d5d0pjeWdMR0FINDhCQlIrR1pWbVR0MEZBN3NuckF0QWlyMjNJZWxJSk84Q28zWTlDekZNVXdRQ0VDZUk3UE1pejY3bjdSOWRvQVIyUXNHQzlmVmc1VFBxRXpKak5tSWFXZ1dhVUF1d0wyY2orSTQ4ek1VOUJHYzdIR0JxQmQwRlZ1b0tHaXU1Z283cmpFK3A2R1JNR1VvKy9HREgxY3pFeUpnSkc1ZHk0Tkc1L1pzWGNGSHNEMU1oMUZLNDBLUWREdTVrM2NMQXk3bU1Mb0ptM29aSHNBQ1hDVEJqTW9vSjJTMXdVTmxUZTR1OXdiZ0VPNVdxQmZWSC9tZXJ4Z1krMUlQUUptZnBTVi9LeVBnQXl2MFpVcHEwSzZKQUEwTEpqSGRSaml0UzhVZ05MOGY0UUlTUkRaNy93QVgrbDMybG5vc3hnME5DQ0FTckV4QmxEMWpTeEJVZUlvcURiemxqOVJ1aFAxQkNObU10UWo5VmNHNHRRYjNLUHNkd2Q2UUQwZ3ZvN01JSVRiRFVyUUVVYU1ZYk1XSnBoT1VSdHFERHRBWURhQm9Pb1lRcEZrb2pRZ2lmMmY5QkFPeVN4SUNxQUlQS0JRb3Rsc2lBWDB5MEtEQ0VhRUhSaEZtTEhHSnVNUTZ4TjhjNGZKQ3M0VFlLbFAwUnNFUUFYQ2dxZ2VNa1pJR0lGZ0VVV0FwQmNZL2dvVlNBczR5b056ajhuTEZtc0FBUWp4bFRIeGxWQkNLYU1Jb3ExUEdhYWpaNHVUS09NR3pIWXVFbFRrQ1JWdzJZakJnWTVGNXdmOEFOcmc4Q1p4N0pNUmkyUmlIeExRZVBFSW15SU5FenNTb0lZRFJqYkZ5d1JIR0p1TU5XRVAxRm84TGQ4VEo0SEVnaTZKdmhhTnBjbEp5R2xETUxpb3BGUUcwMXhmWk9IN0RPTUVvUk8rSlRPTDdRWTJtaUhWUTl6cUV4aExOWEx4TU5PSnhzRk9KSVBFMWhsWGxYTlZKZGQwSEZRUDN4OGh2ak5FaXVrNUtXd2c1SFFobENxTG5HS3RaeGFZcktIcmpOV2hYeEpVaHIwUWFoM3VlcGZxWGVvSTZ3TVZNZGJGamljTU1XVUhoYUdnYmpEV1NuSGtGR3lQRmxMQXdvV05xMTBMSkhHcEFhME5obHk4bHNPc0J5MFVJZnhadUlzTEN2KytNZ21vZEVpTUpkN2dzOVZxUHgxT0p2eExjWlUyRU5pQ2hxRXR4R0VnK1MwT1JadGRGTVZPVVFCeWI4bUVGTU1TcFBHYUxBL2NxbFhFQXNRNWd3dWI4bG95aXdsNjMwYkEvWUZ3cnFOeHpqYlZOVlM2RTlRMmh1RHkyQTZoYVAwNjhvcldhQzRycUVaQzFWZ2ROVGNmUXhmWURNdjNMeUFpbXBDaG9LQjB1UTZJSkZnN0VCQ3dEVmdVSlMrcUVOaVhDRGNMcDBHNG03QVlLS05PYWhZZXRpRUhqTmpUd0ZrZ1ZIMkFqVkNtSnFLWFUzTXVKNWlmeEQ4eVhGWm4wTXdld3dxVW9BTURDWGtkYnZaVi9RQVViV3N0QWMzS2FoVlExQXErVkVqSFVMTWV2LzhRQUtSRUFBZ0VEQWdRR0F3RUFBQUFBQUFBQUFBRVFJQ0V4RVVFQ01GR1JnYUd4d2RIaFlYSHdRUC9hQUFnQkFnRUpQd0QvQUJvVXM0dWNvWXpoWm92RWZaR3A2bWcxMkh6SEhDY1hZV3Y3TEZ4UXhpRUpjeDZDSHJMTHd4Y3QwV2pGRnl4ZXR3bzRoaXFXbEZvV28vZzhpd2h3eFVPRkROUjA0akVPSFRjV2c2Rkh1ZXdxa0l5T2xsanpxZm5IdHlISHllUmN1V1JhbHhwelBzK3o3UHN1WFpkeDNpOHFPbkpZeDZycWR6dVdmV2NIWXh5dHFjTXlqUFNGYmRGK0dQNUcyRGJsN3hoemhtVmo4bWVoNC9KZ3VwL3RJNlY3UnRHWXhGMHk2OUIzMllyN3FObzZuNU9oK3VWaHptanNZUEF2RzV2Nm1IZzNOcThVWW5NWFIzSGVuUDhBWE1tVlhpckZQQ0tNVVBSbG5HYWNWWUdNWTlLTUZuRnhhcUhDaDJIeU1tQmpxczVaZENGRzlPUlFyMGYveEFBcUVRQUNBZ0lDQWdJQ0FBY0JBUUFBQUFBQkFnQURFUkloTVFRVElrRWpNZ1VRRkNBelFsRXdVdi9hQUFnQkF3RUJDQUQrOGYzZ2ljbitXUU85Z1prejV6Rmt5d2h1UmV6NWZqanVEL3h5SjhvV1FkaHMvcmw0em9uN3Q1L2lKQjV6Ti9qUGwrVlA2aTh3dmNZZHoyVUgzK0lUZW9mM0QrZU9PZDBIUU5qZGVzZHRaNVhpMERrK2ZkWVB3dmRmWi9rV2xDY2hRVUVObEgrdzhtdGYwUGszTk11ZTlWK3d0WW54K2lUOWZ6K1V4TUNFcU8vWUQxZ0h2WkZqZVI5TFlXeG0wTnNjVU5XR09iZmRXbkNleTZ6bzFOMjVhcFlMQ2VsUC9VS3hTc0ZaYnIwZ2Rtdi9BSmxSTnhEYUJNMnZBamZlc0xVS2ZsV1ZickFsMTFWVU4va1c4SUtrVExXRjdMRklRVU5ZWVBIU3NaZDcyNnIxWmp5YWluY1V4REszeEZ5ZXdxelZUMStOUDJzODd4cWhEL0U3V09LVlA4U3M1S3F5L3dDUnZTZXc5RlhLdDV0cDZYekxoUDZxMW9iTFdFSkpPRDQ5Z1dzb3RqMms0aFVCQ1dDWitUZTFqa0lsUnNiSksxaklTdXV5MW90UDBBR0RhaEVzQjFnMHI0c1RMeHFxVkg1R1BocjEvVk4xVzFqdDJQWVpxMzNoQkFWK2cySjdSQzVNRHVCbVozZ09Cd3VRMlo5NW5UNWlldHYzVkFBVmd1WFhRVnZTYTlRUjQxYTVRRWJCd0xydHRvYmxzUHl6bUJQc3RaVXZCRnJrZkVaUFlFQUFqRXdGUjN1c1VFOG5hQXFQMlFqdUp5TXhBTWNoUmpJY0Joa0ZDZmlwc0d1R0lvSzhaMFBBc2IvYXM1NVJIUDhBc0xUMVd4WHQ4a1FBSHRRTVRCamtEaUFabnpQUWJKMWo2Z2NkdEdKQjRKMUdJVzFRQ0JpRUFoeWNDTU5qeFlvWHV3a0hKSUp6UHFLQUlvQkhOWWNqS2xpUkJuc0tBT1NvaXFSTlJqTTFRamc4OFFxRnF6S0Z3Y3l6OWhLa0UvWmdKWjJaWTN6ak55c09OMW1Cc0pZcERuRjFmMkdHZjVjamtKbzNhcXdHMHhCbUlPVEU0SEZTTXg0dEpac0cxYlVwdzlvQVhsbElyckFyQjliU3hlV2xVcndIUW00ZHgrY0daMnJSb3h6VWp3OUhGd0I1Sld0Nmd6K3VsOHg2OEhqQjdJK0pCS01vT1Njc1RBb3dZb1VESkNDcGRyVUZqSVlCbmhicXZYV0FIVVIwT2lST0ZZVFRaNHE0Smxpa0tjRnZaZ3hNbXVVSUxhR1VlSy9zcktHbjVLVVpNdldVaW9xdU1QUW9RcW5vYXl2ZFF6S0lwMVhNQVdvY24vNFFWb1ZDQ2cxcXhzUGlzTGJtdHN2dEJxQ3F2SFZpa1VxSnJQWHRWTWFtVm5rU3hSWGRHWFJzRXBqS3hEcStUVTU4YS9hWElLTHZZcDczbGhhc2k1YkNqSnVHMlpoS1hEb3dXMnhIL01LejZYMWF2OExsQ24zaEdaaTdRdGlvc0FEVDRxeWtiMktKMVlSTlExZUppVmpzUzVjaUJzUEx2eVZiRElkT1NTRG1XSUY1bFlOeUZUNGxnZFQ0OWxXYXlhblZoVy9yZHhaNGRtVnNWR3IzckxiNERLdGpzWFNyeDZTZ1U3RjZnNkp3Qkt2MUV4bWdyTFFMUERyY2VLUjYwc2wzeHR6UEhzRExySCtMa1FXYW5Nc0lJbGc1aVdrRE1jK3Q4ak93NG9kVCtObDluaTJ5MnBia0YxU3Q3NjVoZklUUXJlVnpSZTZ2NDc1SEJ5QlhlMVM1V3V1N3lLU3JWcFhVbXpKa0ZsaWNrcE5WWUVqeGJGdzNqdlNUUzdWTjdHUHhhcTBxY2kxZzRGZ0ppMmJmQ0U3Y0VaQkpqalhncVNqWUxLUnpLblc1UFhaU1g4VjhGc0kzc1c1TjE5bFJ0cjhoTmJTWHJCUmxJQkdBak81WVA4dFNMQ2xDRkZJS05zSFVzdVVETFltWXczSE5ESjVBOVYxdmh0WW15SlllbThabEw2bHdhM0t5d2Y4QUN4c0c0VWx1UVYrTXNxSTRQak9EK04ycGFrNUNNSFRtdGd2eFp0L0diYXV3cGNkMFU3S1JQcm10a3FmYzBJSExSVGJZdXhWcy9GbE9rWldCM3JWd1JrTFg3UndXdFY4bDdIM1B0UXFZQTE2SEcyUnlTYTIyVURIelJkdXcxWTFqVllsTnVWOWRnVTFIaHVGekJZeXJHSE9RTW5KZ3RVSmdpcjFqYUlTelpXa1ZnbXNzbVJrQW43d1ZHVkRLM001WG1WK1VNYTNNdERVRUxvb2I0MUcxRzJWMVoxTmlNUVJ5ckNzbUtvQ2JoQ282TmRaNVVCWTdNT0R2OWhzbDhLOXRXdUExRHFNd1lBMWdENEd6TmtqVXN5bUhOY0dHNUFCSEkxUitZbE5tQVpZclZObzlUdldkbGUzd2J4bUdzNStGZC9sVVpsUmU0Nm9iUWV4WUF1c1pLMFJXbGJxcEJHUmE1MDVEWVlyWVNkUUF2YWMyeEU4bnkzeENwU3dxcjFXQjlYZW9vZFFiU2YxLy84UUFMeEVBQWdBREJBa0VBZ01CQUFBQUFBQUFBQUVSSVRFQ0VFRlJZWEdCa2FHeHdkSHdFaUF5NFFNaU1FRHhndi9hQUFnQkF3RUpQd0QraTdrSWdORnRMY2ZsWEQrVmxtNjFEZ1dvNnBuNG0rQXJObld6OG02ejNQVTl5RnZ0UG9lbmkrbzF1UmE0THQvQ3lZb0RpUGNmamxtejh1eXpQNkxEZW0wKzNjdEt6cVEzYTJsbFhNWWl5aEwzdjJLSmE5SzRsamE1c3RSODNDamN5WXZZcmw3V01jUlhUWXZTdEE0dmlTc3JkOWliMTlqZDVKQ0prdmE3MlRMQkpiajhxTGJaWm5wR01hM0R1VU5MSmlLWkVoeThvaFIxbE9BNDZoZFdKOGtOUnlxeU1OeGFMTVhwRkRVaDlYY3hpSkZvYjNGcTkzVXlHb0NndEdPMGNGcG1XbnlJcDUxTFVZYkNRbnNZOTAvb1NXdVk0aWdPSXI1bk5uVWZJWENIS1B0VUJlYUN3VkxXOFVSaUZBY1JlbGNUamY1c1YzTkRndGZZNzh6RFZmaU90OGkxMTQzeUZ0US9VdUpKRys3N3Vud1c4Y0ZvVXZzbnFJYzJuMTVJZE5CaEYzWXZyZGd1M3NqWEFYUjdqczc5NjZvbXMwVHUza2xuaUkvWjhCSngwS1dnZXdVSmMyT05PWXNHYU9wNU15WE5veFhROHdNREFrb1JsbVBiM0xUbGc2clV5MUhJM2tubWgrbDU0Tzd6UnI1RkNkcDBXWDJTV09XcFhVNjNaZFdaR0tQSUdENW1QWDdLMmZPNDZUUEhkVkh4dFlEL0FHc2I0RmNWbnBXVE1lT2g1UFNLTmwxMFB1TDFXR2VhV09IUlo2Mktpa3VCUmN5ajVlekIzVlJRMWRuZGpKNnloOGJWMjArTDRQUHVPRnRVZURXVEpXbFZZcGkwV2x5dEUwK0sraWRtcHFNWEJlYmpQZ3BJeFVUQ3l2ZGp6S284UlZjVDRzcWpaMktZTW95ZGxrN09XV3JSb0hCcWxydm81Qy9aZkpaeDZQbU9HVWFyUVZzdHJZWm1mWXdiUmxBd1VIc01mYmliU2pQOVdSUjBkMVQ1SXFpbVk2NDRQV0xaMnlZNDJlSzdjbU9Md2J4Uk51c0tSTURHaFcxUC9wVlcwcGFtbnB5Sksxd1pWZXpaZHQ3bEh3dTI5MGJ5YVpRcVNlWk5FMXhYYzNycU9GcmVZbUhGRlZORkhYUTgxMHpSV3YydXFIKzJEekhHMHVPdlNTWTVPK3FyMzczVWZBMk82bmxMcUUwVnk4eEtjaDdUYmt5eHBxM1FzcVJWRk9SdFdaNTJlbWpLN3Y4QUh3RzdOclBQWDNRcDg3dmt1S3ZweXVjbWIrOTFDaFIzT1k1NTVqWFFjSWViaE9aaDdNTWZLQzlTODJQWVdQVlo1ZGlLR2JVSW81TkRsUVpOY3JtTWNJblloSEZDbVNXUmFXQmFoN0pNMytUUW9NVzZYQnk0amRsNmkwUWFZcDVQdVdSU3lGWEFSVjRGTVNaVXM3N2xPZ2tKU0ZNLy85az0iIGlkPSJjIiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIvPjwvZGVmcz48L3N2Zz4=", + "description": "Displays historical vibration values as a simplified chart with background. Optionally may display the corresponding latest vibration value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'vibration', label: 'Vibration', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'vibration', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Vibration\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 3.3 - 1.7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 3.3 - 1.7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2.8,\"color\":\"#3B911C\"},{\"from\":2.8,\"to\":4.5,\"color\":\"#F89E0D\"},{\"from\":4.5,\"to\":7.1,\"color\":\"#F77410\"},{\"from\":7.1,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:c2ltcGxlX3ZpYnJhdGlvbl9jaGFydF9jYXJkX2JhY2tncm91bmQucG5n:c2ltcGxlX3ZpYnJhdGlvbl9jaGFydF9jYXJkX2JhY2tncm91bmQucG5n;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Vibration\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"waves\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":1,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"units\":\"mm/s\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "vibration", + "pump vibration", + "motion", + "resonance", + "dynamic balancing", + "mechanical integrity", + "pulsation", + "frequency" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_radon_level_chart_card.json b/application/src/main/data/json/system/widget_types/simple_radon_level_chart_card.json new file mode 100644 index 0000000000..772b3e7b51 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_radon_level_chart_card.json @@ -0,0 +1,32 @@ +{ + "fqn": "simple_radon_level_chart_card", + "name": "Simple radon level chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical radon level values as a simplified chart. Optionally may display the corresponding latest radon level value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'radon', label: 'Radon level', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'radon', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Radon level\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 75 - 37.5;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 300) {\\n\\tvalue = 300;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 75 - 37.5;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 300) {\\n\\tvalue = 300;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#80C32C\"},{\"from\":100,\"to\":200,\"color\":\"#FFA600\"},{\"from\":200,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Radon level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:radioactive\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"Bq/m³\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "indoor", + "air", + "radon" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_radon_level_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_radon_level_chart_card_with_background.json new file mode 100644 index 0000000000..666a71acd1 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_radon_level_chart_card_with_background.json @@ -0,0 +1,32 @@ +{ + "fqn": "simple_radon_level_chart_card_with_background", + "name": "Simple radon level chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical radon level values as a simplified chart with background. Optionally may display the corresponding latest radon level value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'radon', label: 'Radon level', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'radon', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Radon level\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 75 - 37.5;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 300) {\\n\\tvalue = 300;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 75 - 37.5;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 300) {\\n\\tvalue = 300;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#7CC322\"},{\"from\":100,\"to\":200,\"color\":\"#F89E0D\"},{\"from\":200,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Radon level\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:radioactive\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"Bq/m³\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "indoor", + "air", + "radon" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_rainfall_chart_card.json b/application/src/main/data/json/system/widget_types/simple_rainfall_chart_card.json new file mode 100644 index 0000000000..57f8b346af --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_rainfall_chart_card.json @@ -0,0 +1,38 @@ +{ + "fqn": "simple_rainfall_chart_card", + "name": "Simple rainfall chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAgVBMVEUAAADg4ODf39/g4ODg4OD////g4OAjTMchISHj4+M9PT3x8fGsrKxYWFiQkJDI0vHHx8d1j9zj6PitvOp0dHQvLy9aeNWRpeM8PDyCgoKenp7V1dXy9Ps+Ys5mZmYwV8u6urpKSkrW3fXk6fifsedLS0tohNm6x+0xV8qDm99MbtG6RkKAAAAABXRSTlMA7yC/r1EOHTEAAAQeSURBVHja7M9JAcAgDAAwytEP/v0OGdAlDtIAAAAAAAB+p4/I181+HrHX83b0Ngs8zmS0XCVElUiKXEbkNh+79bJaIQxFYXi0YN8S9iABY0B0cN7/FVsF5VApdNZtm3+U6Ogj5DIg0RqQaA1ItAYkWv8AwoqfxYjQdxDfiAq/zXPOds156ddQyIAjySgJv9H3EK6mXF64yrUsk5wSTZepVUZYCC/V8FlyXBABtDbAbQXUFMZuDE/VdP/IASEsbcWeSeE3CGpGq2VKYHKQbJNoniip0fI5jAdZt1NkSzoh1eZUGdlhpAckw6mjCGAGJosHsQ3onVcYmE4IpY060BepxAdk/+sHhNtLIkJ0akjzvHhF3k6IHFtEpandIOnFIVcEbKh9p2B+3yN5F2S0G0QKLCQE8AnUk+HLqZXQiBbyL5CZKNEcEqLuE1xxTxn39Pdv9xNyay54VP/g0fiwBiRaAxKtAYnWgERrQKI1INGKDNHu/Q9ANFcikqwPh7BQW30tJPxoiMrUsec1PRrywZ657bgKQmH4aoUFiudjRZ3a03Te/wE3MILd7phx146pid9F8wterC8slNgDUpuCLYswbmPKNyxSoWuzh852RVRnGSh6mxOhpvwLhwHONyZySBFZoIXw8DiOwaZEPOTuocBKxgQpDDjovbWI45al64CBskYNFph4fFQ5T39FJPI7eAEVwzRFVo32N03k4AH+4gODp0X8e+sDhMQAhnMrr+oclkJZEQAEhf0vlSXTa1cw5ykRkWey2keRGnpyQtqsJuQMCyn7BUhL0LgYwCQBMqqEPM5Y484XIRofDCEJ+3QiKp6+yKeAZVy46X/Hvsqn+SiQJzyVP41MdK5IG/q+FrHVR6axvmNsRkQkIIpvkUx+7H+PRCBuOndqYgL7gA2w1Av0wzagLmfHUt/jFqk3t7UAHkVCcjXRz3MAPduZqTuRnLtaNWCkHeNW5kxkhEy3oIuVfeqysmTowWyoOtvP3uyDyKm1cVCrTSBteFUObZjJ6rUIyUKp9akmJlswKQYnjikP4H9wi+YJkdhUbekIia1IpG+uhb4QSiQHEK1eM9/eOIY1sATHeUKkHhdzqklml0YnXbzeOWb7XPWE0L01fbpdznwRXdnYo45+Ehkmpk63q4tkJIRHxJfyWCjCj7C2iD9ekCtpI1goQrFcWcRWZLmTtoOFIs6loGuLnOxzpwtvAJCr6yUi2gM/YG0R+8YAeSxRw4bsaRHKUhdgZRFR2+dnTrKXiFT9F6sVRCYQ8Arc9N/9scWP2B4ee49ti3iYOH3ctkjj2bhtEcsusotshV3k3dhF3o1d5N3YRd6NXeRPO3dCAAAIAjAQP/pHtgbiXYMVWDVCqskYp4WM1WO5s/tMkGLufN1YMwAAAAAAAH5zAZS0p15sQX4JAAAAAElFTkSuQmCC", + "description": "Displays historical rainfall values as a simplified chart. Optionally may display the corresponding latest rainfall value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'rainfall', label: 'Rainfall', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'rainfall', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Rainfall\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 8) {\\n\\tvalue = 8;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 8) {\\n\\tvalue = 8;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#7191EF\"},{\"from\":0,\"to\":2.5,\"color\":\"#4B70DD\"},{\"from\":2.5,\"to\":7.6,\"color\":\"#305AD7\"},{\"from\":7.6,\"to\":null,\"color\":\"#234CC7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Rainfall\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:weather-pouring\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":1,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"mm\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "rain", + "precipitation", + "downpour", + "rain shower", + "drizzle", + "raindrop", + "cloudburst", + "rainwater" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_rainfall_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_rainfall_chart_card_with_background.json new file mode 100644 index 0000000000..35ea16355e --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_rainfall_chart_card_with_background.json @@ -0,0 +1,38 @@ +{ + "fqn": "simple_rainfall_chart_card_with_background", + "name": "Simple rainfall chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical rainfall values as a simplified chart with background. Optionally may display the corresponding latest rainfall value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'rainfall', label: 'Rainfall', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'rainfall', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Rainfall\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 8) {\\n\\tvalue = 8;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 8) {\\n\\tvalue = 8;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#6083EC\"},{\"from\":0,\"to\":2.5,\"color\":\"#4369DD\"},{\"from\":2.5,\"to\":7.6,\"color\":\"#2B54CE\"},{\"from\":7.6,\"to\":null,\"color\":\"#224AC2\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQoAAACACAMAAADESdfxAAACx1BMVEUSFxezvb0XHR0TGRkQFBQVGhqtt7eqtbW2wL8YHx8bIiIOEBGlr6+ps7OvuLiwurqkrq61vr4VHBy8xcWeqammsbEZISGjra2irKsdJSS6w8MiKSkhKysgJiYQFRagqqq3wcG7xMSyu7saHh8XGxu+x8insrIaIB8fJCQfKCiao6MdJye5wsKstrcnLy8SFRaep6gbJCSosbEiJyecp6clLS0iLS2vubkOEhOXoaGrtLOgq6uapaUoMjIlKyunsK+xu7ycpaYWGRklMTG9xscrNDQyOjq/yck2Q0MVFxgnNDQrMjGstbUQEhMtODgpMDAZHR0sNjaTnp2SnJyVn58eIiOxubk9UFAyPj6QmpovNjaXo6NKXFuira1EV1c7Q0MvOjoYGxyNl5g9TU0xQEA2RkYbISHByso6R0dYa2oMDhAfKio6SkojLzA3SUkwPDyJk5NCUlE8RkYqODiPmZlUaGgtPT21u7s6TUwuNDRHWlqGkZEyQ0IoLS1rfn5OY2NEVFQ+SUlMYGFcbW1NXl4lKSkoNzY3Pz+WoKBIWFeqsrJ6jIx0f39fcXA1QEA0PDyutrVSYmJBVVU6QUF0hoVkeHdBTEyMlZV+iIhne3pfc3NSZWVRXl5neHhidXVIUVFBTk4rOzodISGBi4txfHwzRUXCy8x3iolFT09YZ2dVZWWDjY11iIeJlZVugYFqe3u4wcK4vr5KXl9kbm53goNcampdZ2c/U1MxODiBj499j45zgoKToKCFj49rdHVNWlqKl5djcnJVX19/kpFcb29WYWJ7iIh6hoZmdHSwt7dvf39whINncHFRWlpMV1dseHi5wMCClJRYbW5gampaY2NGW11ARkZveXkuQD9FTExCSUk3PTy1vb1gbW1MVFSKmpl6hIOOnZ1+i4tPV1eFl5ZUamxIVFRPZWiGk5NbcnJVXFwJCwvGz86yekXLAABxY0lEQVR42iSVzWs7RRjHO5fNHJLdDJOwYS6ZGZgcwiyEUFiyCSSBgbnkVMNCqF7iYU9CwcIeCiLooUSFCEp/vl5+suJLdIMvUFslvtAaEYx48BJRi4ce+kf4rD7Z7D6zb8l8nuf7nYOj6fTrb19uNruLHWPliG07KdtRRmlVqKdmYxYz5jhrh9MbPVeNv+iIG3N494jqSq/s0L0nawJTQ6kgNjGJsYnNFBuVLVWIHDuZsiYnjhdkDZM9PIxbLTiRhSp7gPumTZNklfiBuuVEJdkxfRgnWmU2S3CSZRl8lU3go1RibbG/aEJmIGUKXqP0CA6oXVVUN1p1hLnJK96E8ShwsbitKL0Ld4hzrNN+nK6ls+yut511vN7BaLfbFd/iANu62A4uNp3d7Itxc+iXd93FWuzEtirE7vgYLuvDrXB3ElOPchmJWO+nglGX1umtocZ4TwlKMVAAElQZRROhWWZVMQec0CRHgoo8yx9I5mbCUtvwEmYzBCysLamHce2BZUkTZ6MMwKya51kJEjVeUWTgaB+SsrEdkygYYNj1HaQBAVWUaRfTol7c4U9gylR/QisYMUZILt0xpTkrHZW7O80Yx5JzMkmxXETb9bq7TtP1er1fQ6Rrlq73QGPLdul+fbDadJL09rBZXc/jxXILjOh6zdb9nyChgGxejpuIebhxcyLkgCkuEXVOegxjIYyQJ2XmtfTIukqosmeMUUmSJYli/GuqMuNPYZTBCdgsAOl4mbWG13KHWv5UVnCzNsOVCTLTrGqTVuaYduCPskpD2AR6ZCwCZW3tsZ8phb2kwGIooZqNR4gBDE79Et1zwmgd8xZyBLSCS2KEOS/j/YRXgYPGOHa6fiNNyR5AQBcUNKJ5B5Ji2N2u49Pz/cHJzXx4uplPe4fbbTUqGmYi472Kl50UBoJ4K+5QjTus1KT9VyKCMaEtBuXexYzgWuWRoEU4WDOEv37hXCmorJeMbSLEuFeH6WV0lBwfJ32RdR+6hpvGU17bZoeEj1CJPxhQUKYRNZXOCPQCehjRem5PVd0mCiviF7Tq9uHBampUTm1ulAAGQhvmjAQ1gjoChUwwzjjPA+x83cWChM68tuYjiTDGvTNPLvdLjKL1ToIS4jM/TbsfNeR6l8bpEk6s52vKDuYnZ3eb0+7J8aw5bC/3i30Ui3VZ7NLNF+l6+SitaeGRrkZ1RLzkqOJJTsqYUQQfgVjUeUQNiESpqKbZgHmPwUnGWkHNbV9ReMR2M1Rj7dMkzBKe4cTmyCKWBInS416WPcyOMyESlISCWXAaWk6oh5VtPiiFwHwEtaXE0lBbBYrE+fxlRAFFvprTKUGKcg41pxiXR/qJqEwb4aR2OtSIuMPODnOM94LLGSYSETmRbrosZDEsw/4iVQlMcLpdSxgNx5P1wdezj85uNpu/z9uduLloXp3GKd2DWTA1by7yaryMp2SOZTDRjEsuGOFsyOSMyblYPPqgwUSoS2VKKKUzz3n0qKUCkAJRTKiLvyzYR3baBAvsT5KM0SR54JmhWclTXmszryaZyg+RZgYsgxkIkShEJRivITpLgpFlnILalSLWKs1Anm0/N1Sh+arp+1ggbTQdVxDRGtSyd7CHA9wSdS4dfl4pSHAaydrYc5BDyH4PAimiE6ep/7hdLXLg0E53hXMcrFbNy7Ozuz+vzxfbjt89bzdSsdw10nXziWFn21hjchrX7nr7Sh/f/DwhHH6LIY0dcj9xIvSbqLLO1YdCs8I7lQIinhW5gMqNZgPlPGo1nULensGVrDCMRGU1K8ogIoG8rJpULFXEELeODPSJUgrbJ85xUoSiLTNKKBcKXmpGwqj2MVbUFMb550dtgYoyMAmzZrqCOh4DJCVWIdJZNAIfVY8wIQRLv+1VyN6duNVuy9tXm+s4XsTxcpnGcbxO9+k63qbrgsnBR/eru5vbzaA/vNy2Z39ujpqdfbzrpovuyfW8PT0jXOzw0e1+33XVbei4DkGdbxA/L2kUDo8qzKOuedkIQoTuHIZLzLmqPEbGWoIMt9ZYMxg5dsQTJQr3d2h+bagZfO581FKUu6C+xPnzGsiEVsHtiufThy5ktnQ40gXFmqLq6ws3t44KqKBixARvkcIxEcfjc6p7SNCBaCPNgEd04p+TpkN84egwxePGHkcSPV4CiOD2zXd933UbyzSN0rixeG+aQiy26f9xcHZ7uXriZDNuptFi8/efv/552PzvYuQ/8dP9ZtOOhjKaHHVRmuqF38UQDqpq3vQQh6pgTmlnXuJYMN3CWML0kEgMFZQokSuNpqGiLhXkqBf0xhgnWdtyY1mQ57Ygk1PwyQtVzSxhyjClEqWCsoXD+G9AYFTwGMx1Hirrc2wUFQY2VinWEMrIIV4Q4jLBOjnROfGcENV9WOwGWhOMMdlHaxcdIRyPXSIlei/VfTKJ48Ir0/X2o9PrZSpTGMGpbbw4+PtodXl3tWkPx+Px3ffvP/nn4KgLl9rd2p/3n90fd/wve0sgGe3a0eSwNmv6GGMXyaDLptCZLtYU670IaI4dyZV7M885dVRFsPCoRyPiYzeirg1BrWVmA+6jwejspl91qZcDNMQqiR1XkYLij2scFor/ABjBBknOgA4fZxqsoeIhDKT6rMQQjUTevwb65nSAudD6SCN24uX5YW2II4wCFIIwSo1btI84+nwzlPB/EcjhcuU7rux2YYJQ6u2jlO6e6KZAYVuIJT24vLu7vXp5/sTx2erypcv88/ujTW/8wWfTzuzPy1fu/p7J0kngnYzDRV/+2HGl5+Jw6OCYE1nG6PhlxsvaFcjgi19qAiHcewrqBlPISe6FKlQuUqrEwD290cijdhYoGyRZkxhR44nlIVUW1C+sUKrvWW7BY0IeXAeKGFeBgmwvKxm6A+KGuixkzMCLsYgOhaiwGmJaazoigVOiJZ3ndRTuEJE4LeHWmeju+0N0wlBdukXEXuo6bhy7HblYpPH24q/x8rRcQJBxeRgPD36/+enT23duvj6bXa2u7jeP76fvzZbDP/7ore7f+eml68HRE/fNWsep71GDzCWZkHOyR0juCUaYu8PWGNMyK2nuD4asMDisQe8cLiqODbUXEs+VqiBsHSbyp+pnxIaDlsiL0jshPGFZM6FWVDsVpQogmiCGHKzgBlBPB4eMi2I915hphuFBYQIkBBZC7zzBGguhcas8yRE+mYYVn3gEv/rOydx3ml3UCTzp9rkTjqQEBnLyw2VzOYlL8WS7+K8TttvlNu4tt9OrTRwdXL3zy0+XP11+e/n+5ebm28bvm/uvLnudX85Wq9bp079e95v1Twd1D1rMP38KOZ4EBNXnn/nZewr1Tv0G8c4ao7ngHtWF1YPctcgpY7iEXOQ/yhQKOW1hZU3YENezEE2Fy1jdWvMyNiXPJjyZHWul86w+qtjmWf7fKkSQEopxU+TG03TWYiPZYkJ0T9BjQc9CNvOCVtR08rzDsVsKCK+XWh8N5EcuGaHpnIeY1B2/7A1lDAwWLuxl7LonE+3G3aW7cLfb7WIRN+IhwJjG8R2sJfHBN9/efvrtB++889M7NwP2uPbRa1/dvPPc+zd/3tyv/Ffe/+p+VdtcV1Dc3cu+lMsuIci5K90/20IE1iiOWanJMNSMYclDGWjmUMpyQQ1FLfCBanLE847i5lA4ZhAw0reahcTasb2gtHDOZNzNtDVKdDI2aBE9shRhXwjPCGMEJcbgVoMY/dfMkGDsI8kIcsPGBAlc6s4lbiGOmnMkXedigpsVjD3nr7PTPVmCY9+vJkDCc0pXroQk7s4b42Xzm/Z2so1BIedD4LK8AK84nUXL7sFPj3789qubt965+vzFy+Pjo5c//uOVd1779aN/Pv34p6/unnt9c3x8d131JHL8yp64S3Bs/LXR35W8vdwj6eA6bvV4hUd4aDSnVDiYkoZlSHP8dZvqGqZqVqXMq0J1Vd7JER9ZQOCX2nR2eSrotS2EQbV1+oopJVxF1aRvSqptLAB1OLQVDnPWnuVaI8fJdfUu4kSQiuyXR7K9Ej1+XJZBSLCUlaaXSiSnlT2qSC+8wZH0Ysc9iWTkxpVhbexPOi//3XO38XK7jTvlRXe5jSAtxvHBb9++8scLb7/y3Orbtze3d9f375/98cY/P95dXV39dPXcC5/8ejT453rqR33fReiJcRl+reU9fvTIAw6eLHURYu3HZXByLlgJc201NXM0Cl1iSLNPFc17XPcxVhm9iK1bbrOvkUmsTWjglevaGAUyGF41DAtIWUBuODcgEcIxoUpIxjmsTZqKCLyGed5TQeVrHQQTn1SDHKO9hzXGmjhcoojIWQ0hp/PN41R60ULKqY8AhOOuJI7doROHvhNVP9+BStylO1wOtxdbd7ld+tst5NV/OS7X1+apOI7v+OKcI+Sck9DEhKjkgkYMiYRQDU0rSXHiXgRftKVaax+lKj7i0E4mihOdOrDLpnQy3dRNH9i8zDkdblOmTp/H2zYF7+Kbeb+Df4Rntn2RtqHN79fv5dOh5bV35ty1Z965Z/nkA++89un8zKfPPcllUvz1sfmZV5efeen0T602KhslMjCRUbftNChBicDsENgipYrkrAOAJV3C/nGuMXPxByx5bH2/APrmsGUVuDx8IFk8QquhdSF2Cj10Fd/E5ib/125eeCyJgjQh7k54x57w6gAXrAxixCAPitq7BYLZeM/QO6DN9Abop1LDN1yGbUwoYywCzMP8u1OwgQV3OwYQZKiWhNSmtBE3w5jCJo2hLR42+1pzAAU5D4yeIKgBjYX4MPEHg0F4eJiFh/lgYWjtsVfOnFl7Z2aQvDL2yOurjzy3/Mgz78z88svpDz+bX17+e/n07umK4GoV2BMhBJTRVJI4GxRpS4tdRR63M4lh3l/85n6CPX29PQysI9zV9f/R0ER6ZAFmclSw9JSbhNVCWOBiKPAtEK6KYQ7bLeu4Nj0b877UVhsetgp8I1b/925YplHSlb1hs+BxBxwTv+5grPsA000TVxgniC9NnNomExd1Gmfk0LZLMGSsWzL4QhhEanzIEyOumFREs2Wema6mVhO40A0PSw2uibgf5M2FUFsY+uG9raUzjxy8M2FpfCUfrR2sPfLYAzNfbH/16m3/fPTkc0/OzO+2M9gm1NPUOAPOCvRRTQyFGOFYTDGSbd+sMuRlK3apxqh467CFqOUA3cS8SHBkMY8fD/f7IsWFZJ8ngXorGC7wXXAdDHMH5Z5YH/63PGxzCO1jr/4iKcQ8WmRdD9x/BWtzmLoq6OiWGx1F/PM8B3iSj+2GjNLAwYgDDrvnCAh2IrtBRh1cF1lTE+yiAKFtc4qAdKDCgWZXN2BStxOyuDWvDQZJXxsYmpHHYbL/qTio5tn40NrSx1s7B8dKWPj04JVHdp6dfPrTrydOnff0/CevPfDck09+9vpMqyF3wtzIYlrZStoRDRDzqeS7ECk0AABjVmK69kMHqx4fGhvrAfMQb1enTYFpCxbuEMuDPPQKhmZZF7ouLxnPunC4YBYsfij5V/kOP+QGUw29UMLcT0wG1LIQ7x6OU2GEiUQowFEqYawg3+5FOBNsIAEVSTbEXqXncuZOKRInxhExiOoyOh5GlNqwCCkUE5olLQfCRBGVT5R7BqHWzotZnkExHpTcew4129DyoY+XPp+bO1jbeezUXXNn1l555OC9J5792reXTs4/+dw/Xz88dvXVZ2Z+avfDMDYEI8wSZftd4ADa6UTROmA2z05iVw+KuNZuA+yZTmpTLWMcMAuFSCiNm6bPp+HG4X2LOkhyC1nXK6izgTlsQu4ZoBcAVzqQdP6ssKcWsMUD2ISmXOGwYnKYsiSw6fEYAibmpxlpBCLgYwiAlAFfBtIR96EjmMVtVgFNw25mihLlWdPPSOWVfdlBGYLZrJyQ8aSYqWqJ5AvVjAOYqGlhHuRGHK9YgzjOwmxo682Dna2xH6b3Fqcvnbzv+rGHr587M3Wm9+bkR08/8MfXUzsnLz5759V2IJTDjdMdrayFBGqSXLHftYVWHTZt4CNnA+7btAoS5nn4+JqZZG6qkqcUghCkkq5baZa4HEPri2axUOy7lpu2MOJzcnTQsc6bgmeIDXb9I2rZ3qbtb/JKkEwECqWiBwWP4eJ6/cirfNHFklTGHPMk0aEI9cIWgKJt+imAG7bys+o0jLTrOgvvDahDsxVmCKwiRhU/UDKiOk6MjMoKMWCFZNwZodiPQyGJNY2HZ6bGQ1trc++fmfri/Ym8NTY1d+bnW5amrr/v+mfnvn7mmeeeefC2e8Trz5rbLYmlUqdWe1eEQqVKo4CmPkh371DZ7GoVKWnUQICrFfFHus1pmo+nK8AnZlcHlmEx3QMcnkHHR8N8PJsWKi4wPVsHkc5X4VuFtFi2rGAYWUVpBaQESam/MU4xNhNP18FCY9N3gd5ev5Ac2RxsSgu25JdTUVGImMWAqikRECpRldIOI45cXesIA6YIYqMvIIgysaKMM5Rx41SytRlDCeW+KMa9QBVqoZwdo0Uzj+PB0Mc7WzvT17/5+Q+T0/dNnzgzNzU9tzR9zfRtJ267+5nbHp4aWPGVr7aM4sTE6Z9+nzlNfT+BfupQYKDauyuovPteGrG0IvgNmGKEJM/1fGDqvF2BHa0QYFZLC7ruDRewzmfn8MD63BZYagLdDrGq4+hCY3fjKHBy1RexREzWWwd0YftX5DZ1j/vK9tB4lWDP47HTwB2oFF2mYKYg1iE0/gQojLAghT4VWSa6Th0JQq8oqbmIlA5lUBPhxIQhVgjZ/YQ4qLGoqwSJQhKqZS6MnKtC4DEx0A5jd+jPpffHpudOfX5qenRsanr6xHtb719xy/T7O+8/8srk3G3/vPXmn/vvtns/vbO8+/b87xUI6a8NZNtujSKvaqBeo5QCJkGEXBMx6e06iwSUburqwp7gxbTrmZrGq0K3LK/5hXIcGYimSkD8YZlniVXvc/vIpukhX46YZ0WeAUumTdZ+7wSqpJtJ18IYjCfYjpiAKNedj7gKGbWVDguJkqJIzukRCGVQLMYTCFVktUxRF9FO0EYyMshK6AfJfo2ITstMalprwc2MCVhSlbhaFcokzPLFIHeTPImzoStPTS9dfdOp6T8ff//Psamp+5Z2wtWXX1l7dmnu7tuefePBg68/fLf16/LTjz3wyUsfyi5tHmrQpjCitmiHlLE0lVc7KeC3+gSVrPlZMP4x0BgIXWXfEE0zZdwfGEqp1fBOc1MAvx1ZHr8HE2pNMD19E3uE6bqplWWTIWBiZGDeMVdteIq5iWxwzJIY2BjUoip0MWcs4QhkEAGf4x4FtKjYCBowiuR+FtUbUbkOIXUPZssQvdcWNCFsDeIKRbGR8PE7ipIhkmehopKe4Qpykof1QR7n3+9PxEPXXfvU7OrI0l2XjNx85ejU86PvT9+0dNMdbx18Pjd52YmT552cn8n2P3vun2emb94pDZqD2vewaLsOos2YUpv6kNUmZEk65i4FZFV9L0UNsws8arJogbOFCR1b9kw+vB/KLYGlIn8xrWppqQsic9PUQcqnxWo9Ai7irnIsmBK90U0YY8omxpJjm1LUrwC/yoqZlmYZA7MtaDMmB22xLkUI2tCvBTIVSU2YCEQlgBGaLUkZaiJFqSpCNUY0dMRiO09IrMQocNwacoNiRZEXq3Ee5nFVaLxXbWpDTz01PTZy8uPg8Zvqd42NbY3cNHny+dWr773mlfsWvng5WTpn+rO355/57tknS/u39+IBjfoDyN5cFJtCy92jcHE1igBvCRgpsO6AyJYAcFKJNjqYx76UBKYnYre0Domp4+rGFvY2owiSUg+Tvl7qeqlSBBFl8l6vwE/AOmBUcDDPnRRmEkcGyJQVjTodwPyyAtvArnZBs0HKTcwYpnXHT5Us/GBNIy3HL/uGliq0aBC0vW4QinyFEr4OLewoDikSGIYkTDpJXSWK2mh3yuPVnIRhEKp5mCRJOPTyDSMPXX3l3Oc3nZq86eabR0ZGP7/8grGTJ+478cQnq1MX3rN6+QtfX//ss8+eNoI3VdiM3ZoYQLCFIpG21nzf2I/4VccMpRLLGIwRrRxJwPvZxPxXRyYgGI6XurMSjjqm6698DzmBppi5sa7zntvTaam3oENfAKlQl829OuZnygtRNTW8lB/i5hGuUlINAFSZIwAAGp6dNEXGwARrlBAVO2lUV5EKie+0xLJAVEIUmcqOKsOW0y5931Da4awy4G+XnZgUF0VCHF481fJCSw2Fd4Xe7HaSLPSTJM+HTv348v7sje/fef9JvotbbhodC6uPjt7xyInRO544cf383uKvn55Yvvu1T8qyGlK7WY2pu024H32YxaKtgkAQKIE+Qh2sJBTOLmYDz/YlF1Uz2/HdGu8F1K/1K0iSuqzgeemmY9XKxGSKZKYCNrVAbEjA4HtKIlYX6XCKRUHY75v3hEjIAK4PYJPWQcpYkckACCEYHNlN5jgiYchQQ0QcYWKiSWsI2Z3ZYk9GMuGv7jlJu6sG5a5ASoqG4th5b3KCoFWjqNaUstCZrVWpEApi+H0jy/O9al3TtKEbTj209MPjl1x0/ujo2NjY6E0n958feeGFB0+sjd1x79x9D7/x13Mzb348XhaD2AhWyl2DCj2njzI/OhxQUXQjiaDdCHSg79cai02Y1TRbTH1mikDyfR4HXJiRDz04AcpmyeUz+XYEgekThvvrJZNKfDc+86DJ6anDfAdiLOQ0QUCmUeIa9lFMeT47GooQBQqLYKOWZxSKrC/AxZJIqEIiiPw4LAlCrToIe+NQrDmqYmRuve0XlTiUA1kTw1WbEaVXKRMlECvVoCrnqirnoiASITFEV9SqQ38+9e3IyJ97p845d+TxkZs/v3Fk9P7R+yevuXf0lssnr7/+mifeuG15u9v67a7nxzcO5mc22gKiUEVJkcJA+0bmAKfSTiRGLEpLiZpriI7Xvid+mZMQJl9smwTVAKbHYgck6kDdRBJLgSRh4NCWKHmQYslv9UOYcLqEiqHWE1VASAQOA1JQsqEUkbwpdWwJUQZAT6X54qKoIpi2JGLItBwxqsoooYgvpdmpE41kfqOCVgSiiYlayl1VjBNa1jr1ymlBcYqiIv5S/rUkVAXBFdWQ56rhiklR46v4jySz/2mjjuM4Z8z3i+Z7D9U779SZQ0wuenrkrMea6129NmTaYFNDW0fP7qApoBA3qNgRpCqyLdoxwE0HZYJjpphtID5Mps7RuanofNriw5yJ4jadZvpX+Jneb036dJ9++354fUYm5ka6D70rfdJwZvMzZ/qv+fmZNw50dW5ePXw6/kbrtqHegbbJnhVJnG/67us9w+f2H7xgiE5RJctSgSAxTwTIPJbtlz1WUQWUo6gI9hccv1VLU1Qsp8JdsxqXwDrsL9+p8wHDqlLkKiN1oTxdjOilq/4BcSHm3ANAsg5epaBySqE8H4Nqr9pSjFpcOGVTYSxhDigJxQQD5VEeGTxiAppTCOMwCAVrYoIqBVNPYWZ3jBk9GZTUAK2ZFdGUHYcXImdFv98/mhT8AmdwSZzL0IJtVOZTNE/UlYhDJFU1Sc1Ud/9cd/T0XEu66d7Q88Ef4vG7+joHJrp2lF4ebNs09MF8x7bvD664pz+dHG57eev3+567nCHcCSaCJTcmEE1jwNEdlodziutZIU8nHdWnCQCVqDoeOCMK1oUtn56qBUFJ+urqKAKcRYOv6a/D92AG+1B9mNaCt8SEOggYYQpYGU15VJijEEQ3pYqkabKR91EA7AK19dC6g4pVzyTQRg1pAfhUlhBWWyqRAE2P0ryYyCEGRAYlghVOFKYlJ8Obeckvgo06AiGWTAjv6UaEfC6SSEYWHdEvEYmTOCLJNRPtq0/3RSfSnYfU2ScmDo0cqN7f3DXwc+fAG71ts+MdrW2DLx85t3X/Z8+f27Ztz/6Dz01+ZCVM4l5EirfIXpxHlWVEj6a4jZLLmBRH624O/JCmeQ1RNPCMWsA1MBodMb57lhbZMEzIIgGLg6Fw9UmvNleUMS4wZV4Bag+XBockHK5VfNUUVaCghGu1lXxtQMslWa2YZ6mSf5Rj5NhulsnQKg2BUcwIBa0IBBEB7MMSD1XDnIecjSCMknxC8Pt3g6kYBm3RvPDesugIamJxWuF0mYwSXTd0mASIiSNKNdn0VHouFN98a8vCXFNfS7Tz/LfN6e6Bjhfe6Nh0oKt1YLCrtbXrXGvvwrZNg0f3fXrw1Y8++scyNAtrNI9EDy1/QuwgXvrScnOMk3ufJvDbyTEKGxq26lWvvl6jMsgLaJlaytbqwpoOMaTOR/lQrZqwXcpWCY1uIbHznCaAOlwFEhBNykx9WYGxwdtEBEaEdFEssqzqICYWq784L8RY4tdRnQBRj+Dy7BhNijlaxMW140FTDRZSSb7g8IlF+qxyPnqSIxC7DUuRCUmlMjYpkaPHI1dPB29ytiFKquQQP+ckaqYebYln+zrPvxeKx2EU6fjrLU+lX389+lRHuiu60Ne558DmTb2DOz/ouHv90JG3dj332vixz0/C21hMRMOakahUMFYKo0QAbd6IU0iz3C+gn/AIflIJL1OAW1isIDkAh9wd8wn/hUtKKMH6OAyEVqHgcZhazpwfw6s/MuF6OEjI9tgYYikpl6KRgeo1VgU8RTOGjpYNBrs+hJAUJChmkRxnMAXVCupJ2kFGwtIjRkJGSRJZMaS8AkIwb8n+SKRs2MViKSMcK0on1mTxoj4KtdUvlgWLk8BEHHsxIpGalx7YEH84PjGRDTVtack2z22Iv9DSEuqbeuIuEM/Z9zu3f3zDU72DXYMJ8cXel7dOfvVp4TtoZZFEClWXFdqUeU2AWogJq2DOHwluFBDAKzZI1VacfEVHJQFD6xaUuoCOwzxdS8R6UIAFqFSBANx1JkXqZFznyQmD1yQOk7BWy/u0sc5ZUm+hGGQNxGrQeFYY7NdUIR/ksIN/ZZxYLskwjESAOsR4pohUUXAN02/HTHflrA0FSzbsHBE8/EVEJhLnEiNBDEHPE315VIS/RdIg3GKEE6XEe5zML550pIha0/5KdrXhjnj8TCjU1BeNxvvTzd+G2tvj/ddfGx1Id48M3Vge2dnZu3ngcHRw+8xrnyUM49gl/tISV8lHrzBJWoyAgEnIjSjwd1WkJKFo7GJYwUTEYH3Y9l2N0pmwRcI+kREo6lLGF2BNU4C9DoZNI0zp6nI3nIqkKB88iRIEH6uwey5dGkO7I8UAyxYLCo0EXqlHZpAlroxiZ4+WREe2EMEejgFcSzllpGDMO5Ykiap14VLyLC1faXnbtvXMF1VOF0URbl/wrLIpOTwRrekIJ3MeUS1OVcsqLxLTk62UWRPva5mfjm9oago1jqSjG9IPbNnSfCjU9OO7ldvi2cFoZ1f3RPfPmz/p6u1qXe0dXjt6mXePv7nrta+/O2ZWaaWQx0Ks8IkZw7FYcm0RchCcCA2QboCF3Zwzb4d5jIQEG8AlEHsK0E44UE8xcrjEFhxsiGERkljBoEAXWFjPF5YFYEKycP6eB81YfVktU8wolHHRBCiBkB9xFV5AY49jyJiVNV4WNCwThoUkwkse4S9cWJEFs3LwTTuxcppc2XXl5FjSNT9Z5IhoKCspWyTlRMqQkqdyJdF0pWSRtwnQCkkdWwK+l6q5s6Vhw/rGxoZQ0x8NI+tH4n80p7NzE1PZiX/if4z0jTxzIH3g2yc7ntnZOdTd8cGRoT+PL311aubc8Nb9QwcvJ7ix3PsS5kuLuzFNpsVptoyY3AlDVQ+kwVqpKjqhaYrG0JweVmqhs1EIshVs+HHQDdTXFi9BsaiqRMXTtE9RdNYXyNRaV5cZVs4xNzpmAgEQYGKEZ3+3XawxseBSqWyaJjJiaJopaeATQTGIz5pcfswlFz56bu/nhv35q+PPfXdh1/RSz2d75zOXVopE4BOyIuolzlQly0q5QX/OkkROT3pqxPSktfPFgqgbXM1DDze0bAk90NDfHso2pPub0p3rsrc19L/U8sJcc9+3E/HoX9mdH/d93LHjjU29rQeO9Ox/6+2texc6t37/6uRbxz6X3YQUQ4hFCDEo4laO5WXaKwjF2rE8YjmmHLESrJCCQCMICFabPkauFoHFBMIwEKiZlJ9LshS0aypwfFEnlM9TAiwFXIzZmE/wJBILami6wGmIT/KakxL9CX/mYrHE2VjMmaINWYaAXqf8/hx4QOa113reOnbh2Ns7tr/56cHJ/Tu2jh+8fPnYlaTsuitqzuPAMTkxoTslGa4rKZEnnCWuWN/kE37dTtk1jaHG9Q0t8Rf7+7P9U6Et2b5482qloWlLY/zF7DPr0+mJqTPpM7PZ6DNdHQNdO5q7et6a7H4ZHzp35MjavqMbVvIGjUsZIINC1UKKpVpGgCNhuzxtwXjmL2q2BWSFSwpANmSF0JhmLQ7V+jCGdgmr/iAsYxmq7GmUYPJcGNb/yzymaDsA3RJwJNLwD59fimhX/UPDTCzxYxVVnJTs0TyHZeBUusfxtuBwyVHuypuLJ3vG33pub0/PmRNfTe6Z2TM0M7yw9+CbK5ZnLcp+TjT0KpFd2zLLbkn35BXD+9yTT0YSoiTykqjXPNvQMPLASOMvG+ItDfGWDS19zX0jN2zITkSjE+vvjp9pmsi+3h196sepaHQg2ru51PHn9/tOtvacG9429Oqu5PHvbC5X4hIi8eeK9GIiKHgGa2AIHHTYE2iRpzmDGAFBDDiwRQXFUNyMl9S5aV1MlVPVMCsWYhAnsJGHNpsACfBsk7fY8orHIDlIqorCV0lOwGXrxPuQ3rnzGzknJ8kST/sFxU/8SYUofkVM0E7KvLx3177Dr+588u3TC5sW3h7fPvNyT1v69MK+7UtFxf0G/NXdvKMgJTmizycLOl+cTVqWdUX+Xfidt1RZF82axpbQw4em4nPtoZGJB1puuq1xXXZ17Kb0T+vj6354KRsfaWg/1D/ybbZzZ8czH//cunPTtq8/m21ba/1+/6E/33z7010rhmWVp+feozXjxNsTDKMgM0Dxy5RgYOJhGbFcJmEppPqBysLJN11oIHzRMOnMYjVQFIzTp2yQwOl8mA7zO3ZWUb3NYo9LuoqIJYIkJCiQW1dI4MRrn87TJJbS/ZA0nFHsd76p4IifXlK45UXDDHJcZnb/ZzOnZlYrhz9bWBgfP7zjk8PAXI7qW7+yE2pxWhVFaX4vJ2fcXOS91VwuOa1yvCVaGaIXxNzvZWNer2m//6GHb2t/MXTNfaG+dSPTD2XvWtfXn25qbI7/Fu2fyx7qa8k29X3cHm0a6Hp9Nv38+x8cmRnqPXfu7+HXJicP7vruu0sZRXNvP4F3IzX/DQbzjhmLAYQEHWKwgpQANMZAWHH1IAumCBFKCLJmNaDhgoixxlWLbjhBwIckfoy+J88KkJZ1LZczJExnCAD7sQDmiCcAw8snRPAcqehI5xMGnUu5sikES7JgMWWVNsgXlfmtC69uP3oK5rBnfO2znrVNw4NDQ209XcfVlMwlc5ZYjOliqcqNHef+t12DqDIvZ/iIJxUj+bM1tz3UcGvjnS9+W3y48YH16+O3rov+1BfvC63LbmmeaGmJNsdD6ReuazzzVPvAxFPPA91ZTQ92dA4Otf058/fX2yc//eiyLWhsMrJ7N/I7GHuWJ5kGR2wcgcwjBEAicqAmizkVosFGmvA6WsERGBW7QsVoAwWCip6H7ooJG3GT4cBKXTIpeTyFaIUFpQ0qBY3wULc5043ZUEgVOUkneRVKzhe6JNKO8E3OE2QTkyBZ3LVv+7aZHTvGD59f+2p87749w23DR14+9SRfPXZRli1J5kzLtFaTdiGCPQeQUUQ2vuEtA06GaNmGebbmzkfu/+mRDx+766GGu0Lrpx66eV0oHloXuq6hKdt0011bmra8EO34ZnHdSLav76noSFf31PMfd/y156betpmh7dvOPv/11x9llqs8jfggGBPHiUrGE8p6InURRIIkpzFQx2XM0gxcaDfCAUWp5nBBoEHuKvi4KxhljsYsCiAC+oIk5JjT+VEGs4Qj0yoh/NVuTmMIssCrsGN6NDAo4ndoUAta8FeCrqcLii1AujSX9vY8/2PP4dnx8W09+587vbW1re3I8JFNRyePX3RdGIQBJE4ucqKtyxyJlDl+hTcsWfIkI+WdOukV/6XQen/aKOM4dtKmzd12IXftEYr3Yua2HgFLd6DXHpJKIMgIc73Og11bZ6FKBWqRFlnBMiliEeqk0kELVBDIxu+EMTugMBjdWIbbMrfJ1EzoRJ3+FT4296L36vI8+Tyf7+fHk3L2aMbZ48ePqdLTlcXK/JyM4tMVWdfv7daVKM8VpVaU1DVUfFxxv7IAzNaxR1e6Tl+4dK5t1F/6xNLhaU/c9vaH7y471wUOJrXWcg4dvMmYITFhgJDy7Fax0YqtZQt+TAzWWS7mFOUSUF/p1Fx2NoQwt6uOIM+dh1EXSZJ69eFysULzAqIw5F2GYx6Mg04LFBUSsX9OUshqcQ7HDbAOp6EqFL1zFUUgjMXROQyJ4zCulSAsgpO4kUne9e1O//XMN/UsYrf/0+H2RNrbPW673T4x4+RYDtGoKeR/zuVeVKHUII4bUcUyQegpdA51sQKR8u+pE2c+KLn45r33Dr16KLU4veLL3KzcvBLl6crK5lTRcEPF72WVRaXDXecqikvv1z6uXP+5OWbxtCwG3UGw9yC+WL58dyW5N4fCEC3MuJZwGNyTUxyBxWJiHOYgGjYTrx/GxUAbH2ltrXKCSg9h1iSvC6D7QRkKQuhy6MjERPlh+EihQCs0R2hcwWzva4BUAT+dzsrSg5AOBcgYFEhwLhBFK/L6AIZgKCeQmB7FBv3I/5EDzTw3PBwJ2IeGTHaf3d3dfWO6oz0YtESHOjym/uFBmlKDYE8Poa3AglAkEOEGXM2YWd3/Fs5KP5omqJQT54tP/X48o/j6Z9fXMz+S52bJsuS5MtXj9IKatXtpdQ1lsq6buWOjfxQUPPq89lrp56WN9Y1tQc+Urb7liTvhC/ds9IRnd3a2DpJm7vnBlkAbUDJb7JohdRLM5c9GCmHoCHSY1v+IFRohAHXhJYSjJDQq0bZiYsQ5qBBLxGsUooVIEMlzYhw+XP4Nh/x/P0Z8GMCAA+NHp6MRQyukZklyHAxRlDP4ja7HBb2EmjTeMRRqqyhUPbg0Mw8uQdgX7Zb2kK92qPGBu2PIY/H85AlF2xdWb88BCDDjuBXYjl2jQLBASszN0GqUWJrRVznRba2OSvns/KnzZ0SZsvT09N3d1GOys6fePJ6WVaysu5hbsv9xRWXFlXN/nS5qKD13qa2tq4i/Mlbf2SDl+eAU72gymf6M9l/u8YZnwyPhg7iwNduzcpDcBvIHyEFzFZKNZkvUoNWHwdqZ7MLDhSiJ6ViFi2SPlMNibpcRc6jYNS4RM2LdBiXRfoNzjBaFMKgcLsdA+Q5JjH6Kw8GB1k5orCTEKiQwYoBInJIg9+d2GQRjOavaqcDGt9Hnl6M/JRIRgIGO7g69bxFkbh2Li8FF9z92eyhkn151umjCr2cHFBS9q9bN7Zs5CtU92GWsRqNB4acIIiXjxPdnTp787L1T+TKp8s3jorTjF38/W1ySlVuXn5+leq+g4Zc3Cq6oSvPzmyuKmrrKvr40euXw57G+DkeMXz/3t9sy5Q1Ho97Andn+g53LO+GQt+dqfxwIfRKkCwILHCOLQzqJS41pYdQMsWauF0FBQiCBrVX0TKEWBjd2srPB23Ir4AtAAhIjJMGNsN41CDO0AjJChcKjQRyTwIVORkC1ZgpoePLHfdb5IaswKhQGA8as0eusIr7sdY/WjZlMwb5lu893zfLEAh7e1O52JxIWS6c9sfncNYf+MPyC8isQiGERtpWireAwU+NG4RmLz4AJ8v6JX159LSMzVZShSsvPyxDlHpOrct/7fzMq3pPLG/Z/V5UUfK385MuPixofqcau1FTedEzGYqYpz+5+h33/fiCaiCyEwoHp+zveiDe8kIjs9Oyh6mechACmlGslBuANgSE1CkTgSJqjxyEaxXBYC5ar1zC0AQa9fyHgM8p/mEG1zl6QTACoaDlC7ccK1eMQ6tRXoYxZA7koGp+rMvsFinLhkODHYQwuxMGIXKbX9Kyh19fX9KiqrxsQhGnI8yj454XdjpjDZFu0uxufVXdPe/qGVqv0vbhiDTgQVHeVI1iXkdlmaHyZGHehBJvy9luvZuz+kZZ34mu59HiJUn5SdkiVe1GWkatUfnK6dF9WUXBM9V2xsqzi11HZuZtjbWVNtqbaGp6/0c5bgp6/E/ZIIjI/H+0MzN0KR0AEWr+w0DffH0fUM/A+BwkcDpvHSZ0LNHYQjEnm4uztNQk5eHcfMzO0hizUDOKIGTOghHjmcDkYwIDTygvBCFYgZgQhSRfFadBCSRWu1SCIX6GY6HWyLkZDk7dn6EJWA5kRmkm+aF120q6N239Rn64NxSztF256fmh98mffHY8j2NJrMnn4H7v55UW+0zfjZ3B8BkWZDVAXEXEjargWV2/cmqMAhapTXns/82hOQR7gC/lJ6UcymTQnXZmRk6OUXszN+TpHekp2L0OVr0zPy/r4/u9FNaVNXbVttc31DgdfCmqS9nafPXr1awC+zc1wpH8nGnFNR6LP5y9vCTirhV7PRmBSLQhmA42zEtDhZ3NWjiXA8FQQpITWAnGA0+IBoKIkpICUf3W1Fy6XKLQwS5thPYoA4wjhILt3lj9gIS26XXoTogwamhKY9d7+jUEFDiE4zrZur3onbm9bNw62bv7YEbzxly/Gx1paYi3BtiDP82PVfMxUfcHh7uTr++wzjJXCuW0di5h75wy9BOFHBadVrWYpaiZF9kFxruzN/G9TD6Vc7L1+RiqSyfMzRd9mHkuRp8szfj+WnqMqOXVadSVHlH+64Ut57qVL1203avlGvtpi8fBPhhKhTTs4m1FfKBBKBMKhUCQwPzsCJgmk1RIQyYItIJ0kSIkEyAXT6K5Tu9RrxA1aDSWBtQa1goURxGyWkNkSDCEKMReMFmooHa7pnYCx+B5LIAqzAqEFMFTXj0gg/cy+EcP3uZXQzt5cHNUIzom7C9dC01OhkdXL3rB7qLqtk2+7xZv4GO9wxByeNseF6s6YraW6sZq3893bMxSBMwARKFDsKEvRrGFgYGLiFjJBqVPSZblZedJMqfL9j0SioyKRKC/jfEaW7Lf1oxk5F6WikoJzBTkV18+eH1DVNRSt3fu4orneVlPjKHrKt8Sqi4ruD3ns7sjU8OaC/Tag8PB02B32Luxf24oLGgglULRK5y/Ehbn/nYQAoRBY6dLGOElCEq2VBrENikrKEVJCwiRQoDMoHN91XNWwaiPFIS52a2vrRdxAK0iUMBgh9ZqBJFqtRgQaWO332sPe/hesorX181q7L3BnetHX/VNH0OLpjrVMTk7yNxt5R2y0tq2z/ml3p2f6ksNWes9Tu7vPzdBAhbMopdcRLrVxTWcd8FPj5ThloFLkMrkyT5ohXX98/NTJDzIz0k5dP1aSIZNK01XFOV8q3xPJvixR5hXk5FyslFee62r4uLI2v7apdv/bc0HT3QnH9FD7onvIfs1t9xWXPonaA4lQwNsfmH+4lxw3QAzQW04xjOqBOUGrIDBkr8Ialxr275MGxqxjgVS8TWDbZhTsBITBMLAT2x/OYDSKUBAbXxkZ8S6M9Kwk/SAVoc0suBiisGKYEb+7M28fDkSinzxA91Z/4Dqn7Rf+umOxtPAdprZSMDUAIPjPa6tbLI0W3jE0yde3jbZdcKx288E7GxsGA+henBd6CVbN6vUow4HHyQB8pqiU0tdy8lSygnsnZMfPXz90PE12SJopO/Tm2fz0s7np6TnHsnJOpefLsk7m5OZllZWpirqWPn/K/3ipu94xPLXoszwJLtpU3VH78GBlYyKQ6LxVG/FdDj/wJlvpbZfCvwshqFUPG0gENcKg/sa0oKUZ3IcwfA4UdCx7c0A7iKHADXDf/YgBh0FzgxN6kFHohZVZ77XQncjd7QcCzSYF2qwewDXgtBE/NEZ8nSPuyA1mYmZiYcTSZ7eb7njbn4DNGPv632sWiynWbeu1TVa3XACNVsuko80xOdlls5gmed63EN1Z5l5sGxFGIIzgu3pGT+j1rB/Vpyi/3T0kP6M6L88TpaYd+uyVkldSz6alZopyRSdPyKQFmVK5VCq9l5NRpypQXarL/+vLsvzmJnATo9Y2anPcsHgaYy19hok8e+fmxA9ut3sosQuYI7LQs9qzfCAY/LQf15LGid4b63MIpzNLoKoqmIQMwBGyEFgeKgy4dMTus70NYZ+eMFtdBhYkSoJWB+NAKMz3z4bDkYXZh8mH8z1bSdb8yDgIChwnupCwT5d1hTc3n/VNBQILgWj79KKl85ol1kVRHe12j6mFr7dNNj8GOJm01TytrmluGB71eLomg086WhIrrE6NA7HOGfVOivL7Kb1/vNfVlyL74rTsg4zMo7JXZOdfPX/iM9HL5098VPLO8bQrh75NlYoAm8pk8ryshoq6rKzTRcq6rpqyuuqa0rH6srbR5qdtJv7Guc5NEH0OgWMyFEiE7Yk70YA9snB3b3V5CQX55CDQiBwhMBRrJQThxjaJm/eE5N6z1WQyKcTjbBwdvHGtfy8eB3yIYxhLYFoF7SJwdmdzIhSIRrzRnZ6enuEHs7OguhaMCs5ArM6P2EuXoKlQ35T7sjdi986G3ZbORKS9xdYVHOowVQMojFaWliobO+vHyponx2pq+FgUlDnTP81HvFPTDPAjLEf4WUa/NEDpUYISlqNLKR8cPZMmOlOQWfJ2SsrRM6mvvFIiG0yRZhanZVzPTBOJigtSpNKG9FypNLcsp65o98umTx4pu2xNlZe6mp9+rSrjW67FYi0xEG91jpo6Pb7ElKk+ML/5YPVhsn+rV8dajfiSrlWHYZiGMM6pXcQyi23Ek7N7yecHewd7l58n51Y21hSXV/f2kvFkXNBhLsO4fgCHH8e35ucXdsJAvy7cWprfvBsK3NmaTc4ZaXq/PzJiqnl8q6/R3fFT1F5b/Mny82jH338H+bbJxhsWy2TLpA1goa3sx0fV9XWrVvC/xhZ8AnjU4ptPLpV+/ILBB8arKKN6m6aMzn1mryfaaVpMOSp9/9W0t9JSc948eebMa9J/3zjz8ivnRbmpsve+SE1LFeVdlMrlclWJVCbKLZJnFRd1gWag6PPTtuaypqZbw2NPHY3NjXx9dd+Fjge8Z/qndnf/IiDP2dWVldmRDYQC2ROp3mAlXKFCYAQDyNOEja35rb3ZhwsPD5KbPQdb488PHh48fLiysLS19VBgNApgQ0FqqXduzYYCYW/UGw33XO4PhRLRkG9kdnbjxfLygs9UvX7fErzQHVvs7LvaK8zt9QH50NXcbWsGvGAbbmyuLevqKqrpanraVTrpsDUDaQhAYWl7ND+7sm5UM3qKo9StjJ+l0N3bO6GfFjunPf9RbPW/adRh/MJLIoc/GNTThiv+0CUrXgKekwuLUmMvEHpN2ahrYJUUamGhPV6ktyJWZO2ozIa+pCubtltfUMl19YRo7RtVu0rtKrSr+DbUzO6l1un+CZ+1/AAJCXef7/N8ns/zeZ5DjpxsaNE2NDTIRYhY04BcaJe2mFTVf+kwdatUJBYrxMekCiXQhUEmshKEX63WMC5qFB8ngrP2dIfX4/Ew/Z5fIsDbX56L9Hz507mfhs9PTWWLU0Js//qp146fevapJ04339iFGt780daDU6/cLefzqUJhcXauUF4rJLP5Qi7Jlwt8srD2KAVefuL1px5/5XTzE3dS/OK6eWpzcqAzGxNSWYBkYzIL/R5YuL0hX6TP1xsZDHSEbAOnzsZyP08ForvbmUrUnk54vSEuaD8MWhLhxG4FRlz+RCWxOy/yP9oj6nPkHtx77oWv3n2r+c4rC3un92P/fHkx03bxy3PIkfZarVZb3/SwvWZFf+IdyTtSqeiMBhGLdGJEjCBSsViqAFwo9JZJplYqKBwzUN0ydvwC7WII2u2pXF2SmfvGbI7Q4pXteJtth/sbVqIvZUuFz1+BFaenwKD+9qMXT3/43J2duzufbN39+qBQzpeS+dzzHxXK8L+XPyjypYNysrxfPti/+8pL0K0998rxZ/av85OdQ891nrO5PY7NK2BFdEKZnh7bXtua3HbMLXh0670ejAuEZkOOWwNtyxkuPZvORDPhcJRzc3Y7xbG4lYv6uFmGC3qB5i0fvMmF04nKYIdj7a3nX3j+lRde+P6rN1ObXw4uRj4da/MtI+31HzccQ9sl8mNHZmrFzvqWGpESqVpBFWJVjViKdMkUIkxWRZEyzX2CHNcQ6vsIEzTC7MhKA4H4uLT3tRm32+3xfN9h82329Cz2OhzTk+B6FlfffOb4qWfeevHFD0/D6OLs9Qd3m2/sH5Th+JOFssAXUoWDG/lCEkA4KGcPyvsH6+Z5sD3uNj/3punqh59vx+KTY9fWHek5zhvpHZucuApwDK9Bskz39Ua97qHtXu/xq56oJ7Qc9fqAuHyZQK87ADdtDnIcTXO4087SOMkkzOH+RNrlcnMjAesv4XCkb/P62aeaXzv+oOdaz3JbR8ARydiWF3qR9vZj2gaNVK6tQZBaRTX6h3xcIUZUCCIWq2SjrociDXJCphSpZUrxURT0lvq/VooeNygS3M1n71MJtyfa4QnbPJXZwSvx+YttF3+d2uyJLSxdSgmLp1+/8+ETjzc/fQN2GL4Z2rr37tbnB/eS+WRKCGV5/pdSsrzGJ6dWS+UHW6W9ZDm199gtPlXOf338k9ffPn1v+mp2ODd5xWd2c2mXO+i4shg63zPcc7M4da0jEMicb1vuC/gTYb+5Y5AzR8P2oD3qC7s5N0xw7PQ739nN9j8INWH6zGVmvHavIf3InOX66EwCFDj/6e6d43zpH1uktxLIBCKZ5cyUDRIEiGKlQaKVNapU70tF4+8h7YikVoYoVKO1o28dlVZh4vGj1UaySqasI04QODpCUeYl1sow33KJRJoBuujwcFFbB2O7OPjlgCMU74w74pOx15fONjc/89bxZ5979ezpl+9eX32welDIFxd5Ps8/Vbod30jlc1NCvlDmC1Op5LaQL2/dKwuFa9tfrz7YWj0AwgRSiPvcdMBO48xcGkrDXzeH21CMCcGSQ8ZTqUQrZnPCQ3PRaJoe2QvjFu9MP21H1RTjZP1WP0H5cZrxs0G1081QwbSfA6b3p9kEd3E29MtnyZ+WbRFbJGIbtM2CFLN7kXpAQtuirZGeOdkiOiM5eQQRS+BPVa2SNKIKzTjackwqVVQjKsVMXVUdZhqtoiywiWF1UTTtDjJp81W1PW3uiITDCxFb7/lfwSsZiF9dnBzunIKnzp4F5fD6G2fhwYE1PpUvbgmHv1yDFImlStlYUgDGEFKpfD5f4IdL21v54u2t5NInZciiciF7DURkT89VF2s20zRrGFm3RSqReTs2u+qwdUTTmRAXdpnDict0N036EnCbHzpddj9l3LnMUH54WQknbfbjeDDYb3aNsEGOtoYTbi783Xo4bNt5beLfX6OBSsTni5grHp+v4/shpP6IrgGIU4E0SY+pWrQPmxC9StX1UKtHxOiRI5A0smMKk1j8X02XqFVEql00YSSJ31fgV/1e2uKHrGS8YW80HAKA2zqWbW3nlscGzLC3NTw8Ldy4vr9/IKSS+/m75a++muILQm61JPD5VDaZjcVyRcBioliI5Qv5cjJVLO7za/l798qARLk0ER8cDDnMFbPXHaTtlhmKCc1yFUf3p6++EMhEK+G0i3GHaZKkgSBv2OmRQ8bPGIIjFoa2qymIB0OQhsBguwmNhYF3LEMSl91+f5jppjj3o6sNBNzhdF/GV/GZB/vN69EQ8n6DVtt0Uqs1/SnXS7V/neySHIHSoXgoqUNUEuS3RqmyHnlfKt7ZQRQiSBHUqDSadE5CQ1mNuJ8lFhoTXILzzDHhKNTt5civIZtj4NrgYOc85PlwLDY8tTi8dbCfTOb5bDGZBLYs8KlkKhUzYOK+WPL2VjmZL0OWxEpFoSjkU6uF1XIht1bmb4710yM+nx8CAh8ljbQG97v84Uj/F0+uh5mbnIs7tMgoYmaOoRk6aGcZwslQdXBJNI7a3SOX1YzV5bQ4rRR539RN+DlQAWw/y9AQR/Cd0Z8ZCI2w41w4XPGl7e7BCji1EBUN2qaHTXUN77wDbxr/qNGjyv+qVA+7JGKpRFWvWqlxtvwhPoNIW6UYKHClsopQolX3QYVbWSsOE7SEFe8zuGHI7MmMQXuc7msbHBsYGLi9Hl+fB008PJGdKCd5CIPPU8L8HNQPnheE0uFsLOQrCik+d3ujXMiXhDWeL5bzqxtb13O5tb3s5EXOtTM361rwM9gIZcR1C7sGCqcs6Z/30onQXDrBhDnvN5TpDkNQEJ92A31ox3e/tS5a1HM0ReD9BGu1G0c1asygRjESKmAdaTUH3RWa9DPMzggDmHgrae9Y2E+tR8KBQOQQ0TS2N2i75FrtxyC0mrQiiQSp+eyWrqtLolKJUalYLq6trla+X6MQocfeF9ceParB4Mog7DTECmUB7A04bU0HBzOGtOf7gG15PdD2q21g8crA1fl4r/nQN3n+343UTYNlQhBAPRdTwJpCSkh2Nv84lMttpJLC3p5Qzn9bKEDKFPLCqbX8Ab/Pp86HRv2ciTYE1RbapKacRpbS/DanZsIJs5c5TCQYrt/KsAa7C14jVoMRvsla1E7c7twl8MvdbD9qUBNEFUagFEZCTVUvfUJiBJcOk4QrwaWjlJ+ruAHQDm/0I2804vFkzEjtJ7+PGruaVE1NKkk91FAR0OaJU3dUEoleL0GwXVSF6BsQIA6ZogWqqfHYKApmj1+js6Im3IK7SKOBNprTrHkvHbVVPMu+yPJy369t8VBbfNMx+Vl8fuPSZLx7O9r3KBiSWR44YruUzE5vDF0TLqXAJC/yUF9Bb+ZvfZqfypdXFw4KMWFjzHHfPmO3G+yGC0RrlUGHsqYTIgO14iadBo2TslAjLOiHObOVcuGsvxv6Zp3I7NRQNKkENUzhTgOhUVLqakyJ4S7WCnqIAHzAuL9z7zsu7Q/bze5HqyQMQz237a54K3N9bqS98dtGqVbSuiKXNEnOiOsk+ppdeb0JgkOi0qsk+lpxteS32jMycDlVYpHsqKzqWJWGwgkMrSIxujtoNHYbg5a5IBfmMoCFzbZ8sa3vnKP3nONcfH5zeOPKZmcsu/DD8wN8FtqriVI2KWRjfG6aL62lktCFC/nYWqo4VEoegAovp+J8YaiYS6bG4rPhRIVmjYTMgssM3xMKTDaDk0bO2a++fDmIO61Gvwv3GyiGCfqZVpQgg/dJdq7fi+2QJKkgcFKNa9SyqjMKFBVjJFanNpAEpWSCOyMuhqMY1pIweO0jnDlx2wV6NO0JRRHRMXGDuF6LNCHiJh0iPdIkb9I26bWoRA9xIUdUNXpniwj0t0rV2F4jRs5ozshEwKsYq/4d67ayjHmOZfFRO5MAudMfBhfRFvCFMr1j5x2HPT3zoA///anz0sTitbEYX8qCCTc9sbYRz2bj2Xx2TFgKCZeEidugPy/dSubz/PzCXj4/NTC5OV3scVzuc1EX1H5CbSINMpnRisHtkVTQMsqmISVoA27RWA2Un6WCQRNuMNEgP6wEyeIwsLhpx1GiWiyqkv3xyYpoYYlCARwFy/4XpP0sqzYC0x1SGso8QlogOmbsjAdSB6lpb5Ce0YpqFPXVDU16RC/XypuamuRyfb1cjtQoqyV6lQjKq7SlQYWgvyigjCiUQ391HcWsJorWBcHq62ec1pV+ZpdJc5Wwz+H2BTKDIVvflbHzF+etVWB4lqC9TBVLuWx28d9YZywnxKZi706Ucgf8WoovxUpCCbTFdm6RjxU3DraEpbODt24vzl8Jef1qUkOqnahsnMBxezcqwww0yxl1u5YgpaZbSZ2BCJoIjHWN49AdkKSMIMkqEqxIipDh1tGhUfgEgy4Z2mj9c0hEUBiF4yxLWjArYSFEs4zZ/5G9I21Y4hYr/VwUAYUlFTfJRXDy2t9rH6HQBUDUmOR6uV6iqqmtPfFQj0CIyMV/SVpkWLtIKkO7G+GqZCcIUyNJsARkC67pdlmsNGPzeis2s30pEujrhWdJznuO3+x1nLvy72Q2NnVpeiP21XB2MucddKSEbFucT5YmYqWSkBUmDy+l+OGpbGzuOyFVSsZhaWa4bzBSSeCYboh41BEbdBhGou+IRX4dgUN3TBguzLEmOwkUoPnvO/h84tNuEm67tQ4lsHE1MKYSE/2JyYDdwH7CMFGrH3VZ7qtJRFqFYlVS5WXj1KyBYi0jEGCMy9vtc3uQ2pqTdX+tyLUIlFJtPUBRberSy+VdXUgXoNGO6H8/KdEemTHJISukiElRdxRVgLsl0ymxqu4LqE5tqCNuYn/QQZA33dGoN+MOdYQCy5Eo7enpWf52sHOyz/g3eDCx4vZ07FJ2Ijdx58PNUin11UQpNQ1WRAliRrgbTwFauYnbN0CFzufiY/NXQ32+DkbjZHE1riYJQscaZGKEmltQE0BRZKvGcgElYTpRh2GN4MBiuhG1HyecskaUqFKTGFmnw6qpo3BgoxigMgqDjEbCSIxKGykSju7+ITXixSk4PANzeZdm7DP9Ye//HFxbayJnGP6Yb+bim7mbi4FhDr1Q0OnAyCAjllZLFUUdopEim1qJSqKYGBPiklAXszGbRBvaJmRT07rJ5tQGmrS00GOgtKFhe6CHvdgu201KQ+nCQnf/RF87mERvEn2/533e53leDcq4ZRx0gs6CSkAhgCoaXs6v+p9mBuIcXACOcZqbu5ijKYS80rLXEg1akdyGi9VZ06cZgfPoqrda22zHrs7O7s+OjoyC+XtNDlTFGzdvXlm5duPrH+7t/HLnrdvfvXW7t3P9l+u9bu9tQEP3+scfd69f78WWPv6l98knOyDHFr+7c/2nOx9/9sXRafLxzcnSqa9eMyP2fTMvigpFMmev/Ko0BDmSDnc0SWd/u6Bk4ARiKaz9u64LwpQgZO9LGhXlsUZlAVI8VSVp5YHBsF8S2zyxLP7vl+5WdbmagE7ZDGhhORlLpCvJ1al0pIAod8iT9dDcU85D0xBc0P8TxfvnNOfg6DUMxWC48RBVpBHCKnZ5FZXKRx28nxv3U8JFFs6o0PHt6oH6ULpWW92HvGj08KtS8/NXN0s3Vuo3IYlf397qLu58d/vnW5/0YElwfavX60EZem90ex/BrvXzj653u92f3vp55/ZPt4+3x+5u3V1fGSokbrQOZ68WtM1avWMmg5jwovjtryIRQSgYWRO+XEGRl4yOQXQB+MHKnpCkL2/IgqhnG28K5hl0zFpA0EBcCaaetgKBwK+aiyJ6WA7LEa2WlKqJWjJW9+U30/XNoftTaDB+EIo7HA5wH3Q8RDOOuIPOAE3MDfhpxu/g5gZ5F/I7cREVXWWZJ0GFRgyrGiwefyqyUjZty4pp+EAKhuW7hUQi1m4+fPhw9tsvStOHV5ZKV5oj11rTuemVpaPF3p2jxZ+6vY+/ONoBZCyudyfGrnd7UIed46NPvrtz+60vd2fu/r6+/v1SbvTFN6dH9iCFSabbfakIB+yieE2gJCHKBqpwAJRueUmUIjaRo5fE0gQhYhCeF+QLlnfxlEsxsZetRhhW17QGX7d25UQgIl4aLk3WbcPH+kw9EkgPhcPyWjoJ706NVZDTCVB4cu4OPaUVT7zIDBYVkBNOPw22bK7fHeOcqtLAnFABREk49ZvtlaTOc99exP3sgzmhIaVEYPJAVYZqJEcTkClCBDBRisEggYBpcnFi8oPYxKY1vX70ZXfxeOfok7e6S7lu79+djyrHsRvd3npvsTu/eKt7XFkHWz6Wy60P54anD5eulCb2Wnur8AYPHDUoIudZSTakctFQeDPCs2JQCjIkiG2bSfuIaUH8qrNRKcroxu8we3kzxQBWhF3JINoGZclaNQHSMGAmI2G5IWIiVGPyqljTNk7yvjNQrJVTtByNO+aCj97Pcx7nMh2Pl2mOG4DmgKvPnh3X0/5Pxs+Vx8tYgYDczTJ/4sYfnz3JwBPKiiwLrgSYIRyBahCUGG2PtpuTEOhMNkfbrcrN0mjin8rDN39vDS9tjx3fvrO1Pdb94YexHrwrY+eV1xd7v3RnxiDCzY399crtmaWjpfXcyvDw6Mj0a9PN1sTQ7Go1YF7ADOeFiIkolecf7Qa/JbZ4GfEJKomkRFvWpEjDEsU1zYoQWeDXHkmmkDFSwaylRUxTSLGiAeihMC9rrpSVlsUGcZ2l9bBmyfkpeQPofi9Z1qaG0IEzHhr0BAczGAFz0nR/lHJ0vxA0VII7//sE7nvg4X2bjWN8rqZUiQEoBCkXVgljEEMUdVGyquZq5fECQemptLD/EELG5msj30+2Hr5Wquw3IdaYHcmdWvrM8Rc/3dl569ZR79a9brfXG4P/DTF2a+vWBx/cmVlcf3w2PB+42lwpHG03E6Z5uHBVDyYFSs0yJC+4MGU9RqwhudZ42yYBkX/QoT6wRJUSKYCpQDQVVQWW4i0dtE/QCLIsPPCdWHzG6igCb7aqZvS8LmqsVYfpFwjIgbASrhmlhUosOBW0h9CgM5TJIEaIMynO7/Hsev/HQxymab83xqEsDj/ghJ6jaQYxOKtk5xiR51liip9JrBPblEt0EezHvqqefu4dsRbe3t0fhc9NjJQmm4XDpu6rzLZrCX12pHX32dP5mbHW+ti9sZlYbP7fe//+OzMzduvfw8rS10tHV7ahTmO54+fHXjvebraM9x+1I+H0OR8QFnhB4hmGROxGUMLLDsHwiQTNMWXebessMOqGixV4WfhtVcMUo0TNPy9QENf5bJTZICTauK9LFJX/stMR+9WhBFEEQxmETcbaQg2ke0Bbzb7y4f09FAo5ixnKg+P/D5ABuGjARWjAMR4CtQUV+R8gRV6lvcxyw+PAKsNkic3DISFIOzccmSAifz7h6bQJCry+kQivhsNAGLOxzcmr++2rrdj0aDtZ2xx6PHE6fTy/de/74+MZWKEd3Rq7d29reAveH/TTC8MrZzeOx9aPrm1d6S+2JjYfPty9f38v7EvrX1J6VQCeAJPMwA2+URphWHnNTV8wKOtgLOXMRWQLeWVVVVwSg3kMwkfEDSwQuEtBFzuJSyDAuHpdJuKjgCCAbdUEtlrz6eHobvrdzdXUXqmJnKFQ3MnEI4gecDsG+peHPhiw3U8GOLgyIL+LNNAl+FSoSLyDvBjhYtYFCCEqozrhGE4avPnyJSGmNqWlYzWfbYV1lr+o7TYr6cL296VSc6hQKyzM7g2NDPlKN+a3cjfWb6yM3NvaWlo8WplfmumuTJ9+9elEZaX1FcTlrVxp4mEz9nBiNamZFJw/oX4/gbJjexCI26WlGpqY5VVRahyg8rJkCCzRXJA5uqEKEkaQMqQiSmYDZTWM2QiUz5UJNijK8oJMFinoJhPyBkLIhS1V7c86UiCZnEpfHR1dgJjXEY8ve5ygsTIHA3GPF5oB0gsOMgx3ikN9vlA5dRy6wz9+MuBAMEkRYjGLSAHjy3MO0EFYEu3waSEgF4gOBrlW/fFvS/YlAgWwPvr+aDNhBKZisC9IrJ4GWiPD12Lz177Yyo2U1ue3t2CTdPO0dbc0eXQ2fLo9MjLRbB5emdxsLowOJSMymEw4TwZe0JcKgu02gtj5hGHJiZs6lyLUiaJQjEZhRdlNMRhF1BTC0U3CpgA3mFB8hucxNejOYJdKsMITPuVbK7DnWauhe7N1tp69sIzlSLpwvrkHEmuwmHWGnH0I/N8eno0DLuSGRw6ns8gdcNygd5xhHDBZz4LLHK0ihARDZKBcpE8dKYbfcFC8AzGUBSWJpLVoIJ2WA4GCL28pQqSvmwVZTqYDtanIb9rQeyuwR5v4fCTXupk7Xp+fn7kZWwHdMQUhGLTGxGRsOpaI7cMnoEuzsUQY+0A/CAjUFVYkRZWCxE1piO53gMTwCElAF2VVInbW5cpgsM0pNdVYs1OMAr2LyuikDOVD52cZwJSKKKKhoKlvSH2DBsiRtYgsSfZ550knv1mBmHdwsBiKF91lZ4jxeCDPins4uOLOATdXHACucD/ZoIjfwyE6rvgBJQMI4PEU/54xU9ApUQag6c2euBlsynXQdz5fkpVFMc0ali+aBR/d+XtDJ0RPJgAlyf3RidbQaK61NLx+7drN3PyN1sjK8F6g0m7HknsTyVhzNFza/+zw8VA7Udvb98s+QZJTv1KK7UYIYxEEjsozmYiVYouDNkNnEeLAIxOiYoYiTMYNL1/mES9KbiIpCBUxdmHMOIlXnaP9UFL3nyQDoaTL/k3x/nmubVp5206m0muQc+QXkGPQqbjZojPuVOKeupujlwdBeocAARxUgoPxQTu5ccXr4A4GbY6biycNhnuK3QbCboxEXmkEvV4RYwQdaJ2YHU0LdiKaJvIGEdigqJvgIzeZcG2qE9BXExOTC20dhsvQZeTmeu61ldPWei5wLFUKACMjf5ZfWLx6mNgfyrfPHid8l88+cyZLnTmed+cxctMIqElVeRFZhMGY5+usk/aulQWCBMnCqbKNGynAC0I0xp7zC4ywqCgMLrsZ90lR+QDzoD21qINIAQgkA/CspWVjN+u1I7vRyupCDB0cDDqiSW+Rjjvj9OCfAAw6lOHicPowSfoeVQW6oPy0NzPgL6o0h347ZVVV9TAqj7KEKmMgCwrPpcopDfwiMHReM1IkK38O63iCZdolFv54MpXsdHS5UDiEFnizPjqxMnJ6OHLlGnxgYzo3MvLuUNIotNPa6t16uA2Ov+bbnorVa74w2Om8ZLn77AQFwDztYCkkKW7FfabiLEZemkk9EhBgn9ciXpVBCg4Ca1KcYCOVcuBoEBYWCuV64GbVD6PqmotImvEgStiyBjftMm1JqcZu56IT3V2t1NFB2eHw5i/ioTjEvKDBnTA73U4HYALCm3j8/3lapJVMOVQs00X6QKV3fYhmGdoDeMEcTS8z9yEKe6DjjGSJMoVFOSv+uWFEdIsylW8sVtfXIEvTqnyC8l2tLEy+9R5suiHpSl7NXWmLQ6XXKs3LguwDx+wLJ8OJyJmuhxNVMFAsq3ll4kLIJixDsX2e4mlqObWMo3Qwg9lUSoX9rilC57Pg0qAbEOvF5XGV37hELqTYOAssqzCUAr+JIRIRsGG6RFPUpKBlP5Akw/fgAysaWIuuJVdj0CAHjrJjsFh0huIhKkTDLMl6BpxgOMCtcx4wI/BieWgUpm/ZaXCwkOL0SYhGNBG48foFh4A9WSeQBW/rU+kHp7zIBiUW/qDMgjTSddayzixTtMOfnxbasf27kLiDMD/cf/PuAqx1klNDq1PtQiIf1oDGIEpvmynY6QT++KD6OxsRHrkeRYRgP0LsQ1EDoauytMV7y6iBEem4gzalMZRKu/pZoypSNqYdn4lA6I4LxHsRDRM2Izp4Hw4iP7CGaGqsvSHzTlYwMjarpCRDWrY69Wg0kO38R8L1vDZMhuGP78t3SHLLIRDS5PIFklJIHCEjlwbsaLCGLtkhoLl0QQ1Sf2EFsWgOPdR6sofC8CBMq4eBeFKkJw+CN38clOlW2MGBB/dP+ES/lq4r25I83/s+7/M+bztA4SmFUghnMkkPU2skklYS1mgaq0gQpMlcWu8aECmg6GYj0JylHdjWVs2/+G0mQftxpc4GNTNcxtPzaHtnGiZ2EzbwRURTU+/3MqPrJX+c4S3i755++PnrndX+5uvvffbnm+/98dXLL791dY539fT8s8g4ObzQQ0+Fz+icvnB+uuidp7DYNZBj4yUaSwCG1GfE96MDt4W57C4TlMlzSaNxqZeSIW1Bltgm0xfgWbm1m5gqorR0qpHFp9FrXB4t5dyn0dXSsixvk95H97fjxeZq9pCuSLlvvKLY18VSFF2GTFFHuvSAgYEF12IuIUVg+RVJ8BTaQEgjUhesXe4qgkm7PUNTbBGONNY0WRkaqX6fJAMl7OvyMrIM3dgelM1J1T996/Lsg89fhrXz0ut4/9zbkBDvvvLu2YeX78y+36Dv9oev6QODDs/P4BKOe2fm6UbJIm07ZqgcErd8ahMbjOBZkrYt7xGOTY/CWoy5kOJamhOCjYpjPCCcPb708LMLj49triNyUe254P12G8qRnCX9bbUyzd4w8atZtThU1Q5Q7JtG7Ovyv5E6WBNrv55OafNUexy0GYhCgDHggs+f7HYey+ZcFJISJxqhjIcas0PWch7bsqsdyyyaRfrO4EwfbFBVo0g3qWyknu/DFTh7a3Z28cIl3J3nXrl85fLzd1//DP3Jw02knxjKA81O9R9MJZLPt4P//NnKcPvMIyXDNQpZinGbtjE2IX8ItWMhaJwUGuyTyPZgw6q3P4WS4OVtbBMrvjOlpU7uc2JRodnMPsiE+Sw3CRvIlebqSpVYY/OfIQpzO9stPl5dVICi9ETTFEW5H42C/7CYwNpT+BRhIdSONgPypK6ndxaKuB+rhhpr0uy3XELewtPSXYOQsL69i7WcbX2qmYlba/K1QnsaXIbXXDPUq5PftcNrg7dO/cuhrL/w3Nnle88/d/ncC5h8nn8Myz5a5TLCwFDksTk4nX2v92U43bdjjjIarRmRkBkalySEpMAjYaR7CfwBWGwxpGoMmMCoNrppxMS9z2zggd9LGiKZ9lheaZZskARDHElh2sJ4bWj0/Is0GUeA4re73Wz18eXPJPX2oYDX64aOI14Q07pAJZGk537fzMGbwVwI1fsmIYV6/b3HW8lSU6ZG2CImKBW621k6MFaYQqmJhDRatpTB2pVrznAgc4xTyOhrRqT7b12cvtH39aGO9z9g7v/q8y/jgzcvn52Oh5W/AQqnim7DpYJTleFTBz/DuT3ZhLQ2SWDHsS2atYRsFc6IrhtmWqppqiwWssodKU6qWBFz8LlquVJHFrqEjKI4NQBiADgoU5lJpKfQ/aNvhPzkxOx7hhdFVZZl+S7drTY7tFdh6+19IUQN2bm0/k8SIgVTD+L6doe4KOdd147xiAj2UxUHjbEnjAiri0exYS3Ii1nUo7miMZP0aW+gNaVGKRQO8zFwlc8N48QaK5jo/vzX179CnZ89f/kGUBgMXzgfnpz6mm/8LKPtX3wf6oo5bimFhrXQ0wnS7T/ImiMglKWKPacdMHHOUEyoJBteniYslkRoHg6x5HYQIEYCj6gOWaaQgT0tkQkZKiC1RRopdLcxzSN0xSDbfblZrnbnWXWRbrez3Y6s11ZpHbxGdCUVXSpWEAAJSfp/NDR/AhDqtJ4KYB5HceMwPpIBeHfIENvGJeEywragUd6wUPiyHPKdbnq6Rjjw0GQc33iUtRQaID3B1KY3HHyI5mT803gw7KUn45RSuPS8j+RSdO67i1QzYFNyKkioMIKM6K5egtjDzjdmTEyJaWiYbVkDizphU3W6HHTJ8BDHwluwpHqQkCoSZawHSiM56fH1A+pan1pCUcJbsz8w/YuLZXUcH1NQxdVsAyhCWPleXQAKUYxGdT4S4r9RQAb3W+IEjgWWIdQtpAb2ZY7ksG2dKAzx4mF+KLXhSd3lr8G4DIqnCTFKpnm//W67UARuzNGmjs18Z7kKDBNiD1IFmTDAfHuIGvHCifKYKPpuOTAWihWZC8vklQVRb6Ylt5+enpAIHQdAeYfsh406r9ctLniPkS6xZTthRJX+JzW82lKVEhoDPrNLXR40HIwi+7pBKdUKWQQUOko2TDNMkiFax5/T7G6XVddXi+9Ikmzb3PIuwmIv6n0p4HRORu1ECoppnMf+Y6R2K64RKFjQmDM1bm0kFG/ZdwTL6xiKBoZGUuwNR/GXOWe2oR9KBj+RS88rvA9bNsPm24ZCfIUP269MtzcwjL4xiCKjHyFS6NZUjN4FxGGtKCxpJNbgjz89SXEXlkyKj2ZsSTk6xaVdz7VQnScx4AddztUijqeqo+A5vhFhjHCwoYiDvMGpgTAQtMxqlK3JHNnyouS7vF+G6+oY5eMqrcbb7358/OVqQdo2XwOh728FSkkhwt26cCbFhkGDH6YBpFW3unDovkB0hHku2XD/CwTgCGa4qws74mOaVfxBM7f84TucoZdbdsOBBFEU/1ud57IGE4Z/zBTt5sVv9BlGE5E1wFhmjOI5jMQgxYCj97emnp4Y/gV1M0bsR1Nj8VOSR4N5XEo4kOfGc3uEIMVtTlQGRg9rptF0PknVYFrfd2poJCFaFqYlVMaUwAEkNkPgKJwgBbXi4cu7UsnN1Dw++kmYROOrdLWZrX559ermBtxStVUWJfdeWaKQFIUYjXzo2QkJjqhc0+4+DXh3n7ApVQB+QETQ2gjIeifJWjVjlNhN6Gkma/hkvSVroikLO7FF2KaMuMu/4RyZUWIsPooqd7akKPUWlG/WR5GoFGOsH1zPoAk31EuFMgs6Yq85IAFGAql+4dPnCFWTENe/ns+lORYQ1qRcUjv7xJHKQJ2LQ60GcWRzTg6lQUr/xK5Xykizi+slYYhVxyWJHN5See1ZtSWyaHc3Dq//Ob/EP+jYbhazxWx1Q9Isq9o8WR/adRkCDKcMrbAj0FEYLEdBDSimXfNBbg94QoRp6ra9tPehNUcQionEQWyzdM5byhn1xMRbt0xmsI8Q6x67xRVyK0vGY0+WLUy4aL7khFkgRdk17mRoUXkzCE+U2ZgP6EsyUWSNe4x2g4YYBeOTZ5/5NtZPVHDBfwstIogBGqMmKHBFDEbH7Q7NAvZf6KibHGFAmCnpTAsoHTH3NnbGigyq0OqmlA2rvb32o2GS5RFcnqpavbBZXV2tFjOSpmmeZ/ny43G2Tsq9Vew94FDzOiGwfekUGiOI/Pm0gwShQUfqnLiEcHG7l/ZxwEmXjtnvoc9LkkbETizHXDPgwBChjDcHThNL1+VB7hr5NvVn1OP/uJucQnFQeRbKmB5o0alG+4mbfH8DBySna1kd6Eze/mAD5d87DqCqNp/L1zEgKfEwiilMhG5BdJGphFXqBl7J7YauqE8lPVBKhdkJsCHBw884x0mBSXepwfi5H/tL398lw7PqoprtNpvjZgcsyP3t3WKxXC0WyToMQ6/y9iK9ndSOM1pOJkURLCf1qC5GYPAgC6SJuQ2AiEDMwbHYM+bxTK4cW2s8EjaMezxURnwpy8y8p5y7MvSzLPVUS18qruujx8xDl7pOUYSmvbQahzLHYIo00BPF0hTBQ6iTE/Du979zkviM2d6Ikxj5f6+qYRV0tqtdMWnaMSbFNsSPJqCZoqiYdAL+Org491Dcg43ccqKMCKrvxOIyX1lOorhNNizD7TKvxsdqtUnT8+sdpOb1D4u/r67JPz/+8/vj3XXn5iTpel16yV7koAynPtajphNc+/V1OJp3oqsTX6DTQCol3hJWMs9WYbG6fG13zllydJsJD1zmFuvKWSehTELu7pzZ92Hj8ENxzHVKcTpuz3UL77sTuGdu5HBr+X4VQWO3laVAawIoVxZOQWXOwP2UUzujoS0JuDItqjhlyMNjJnuqJXVNIVVHZqESlzm8YkbCLQTCZO0iJRpFLsPjOhEi9xXaCtlT9DT1objH4yxLF9tterNbbH6effPbzermnnyJjxb++tumOqyXyxZxkWdWFXp7a194RRcbqKxB9ngkE29SlBPhTkYZzD9Cgk5J7H8ejWISxCQ/Eqdl0shx9smaqFBpjhXMuFvdycci21c0cR2lpS6kX+F61F0/NpRXPdNDUq+dVvd2tGiKzO1x5jS8cPioHDkCR9cARs04LrlATLoOsTWFIyk8EtS3DgPrOBXNVClXrIaANUsmlpRrXmIYXDRhvjbLvpz1XNPwlGrXt6woCv3b4fiw3W6Oq8VqtbpZXV1f3Vz/9vgvx1bv+jQQhsPFGxK3DoVQzyUHdyJU5Ci4mOGkgRI05xCQINQiBBFB/W1BAoqGIqgdIuKgVKJDVZzUHxVRseAH4segVGqxiIKD/hM+NUMaCGnyvvc8b573I9bzr5+fv99c5EWR5lXMirIoCsWkL5UUss7hjsRoM+gZjV/0FGuLcyntAdc8j7XQkZJf+BbS01QHKeXc2HIr+Rvliusk5pFbZKImkTJZKpQRommEQfOACCwuFbRBnwcZF8KRgfIC7kKjac1r0E1qUEBj3TtK9tDQahipLG71KHIMOvgfJci3PVvsuo1EdGtv/3aFLxUaKEpOGbxnEVYSB7dNEiIj6QY7UEbxVItNT3bmBw90u9Ww39+YnzvXX1STyXjx+9dsZf2aL8ZVv78sptN0/CeP8nZcxsUcWqOYyqIuWc1gdlgOSjMAbGPJDAhUJkIQzQ0vtC4x1GZpauAXP8h0b6tLjlu1PYdDiVRuVGthq1LXW7ihwqLGVKGA8cMJbThU2FnNZStuER7Yze3+nT7hxBAOHlrDsbWr7W6gqTva8HqGE8tivj+FgNw63ljrKXTs8DYXiCTQfrrTB3uxQiSZIAVssUy6Wd9lK4lMXnqyxVCy2mht7N17cFgUo/DKeHJu/u3a71+r3+PZh49fZtZ8OV+ut9FwGobLPI3SaI2MICjXXpCJVL7pG39d3FmvFwNY4AED82tTCw1PlFsCDWqwektofG772dfxLp5p4f8igvFa8joXwsucHaAP6tGIiXNhCAkg1r2O63HBChUxjvD4fEgL6W9Ltmoey4LBwbuo6jm15Qbc0gWkjuwRimZFst0CONagwZycjURg1xYBYZdoveCcC0ldxerW0N2BnIIk5IhSeVtVcbuLSYoj/VtVfzKsxh/HwMJHRIlfm5uv3m9+sRb9eVVVy2rU72OXpoUswqXKozAMVOoXqlgOyhrSC3dgBvRVdY09LzPFEaKtesD31xEmErS2MkDE3sUPOKrXY4QRagB9qWMAH5eP/sCV5Ic7oFzxwNSOP3NdRFHgwQgiQa1eAMNYYW/tUpsakUm1S6eaULgd10smgSmbcwtFEUqlQ5TetVYwlszsXTizi1AlQGHUoGxpatWqWklE2Ah2RAd+dTc/TA+dO3bs3C28N1foJb+4+3Jz8/PHj7e/f//+9tOHu5+sb4s5fFBVAMd0uUxHy1yFOTbo8QLRNypUwr6J2pcStWDfcMRMvgYBus6y9tOBZhUyJSeJRI+TLZYOLUG282gLDajyeCaz2hCYSbmgPuDEXF/oBucu8RKKIOpzlxNKwRxONSM2hZnZOgugLOBZgEvhChkCRnG2IMCV8IXkTacp2q6Dsw1COx8IDcLaHlD4rwx4rVXsJ0nQAifkKo07bHVkY8jSK1euHb022vg4+X3r93g8u/F9Nvkzefn44dt3N68+wtvDWizm82q+7H9Yzits4SrKw2mp8jxKS2xFqfxERTJBsqbRSTNzi8Mk8B4PKXiea951GmWolW+zXz2WAC0WaSZBElOGVXIJQatIGo3HdmBoswGLHc1hdps5NmzfTaAErd1piLuw4r1HvLjhs6lmcHpWCwHQIKJU1BNtogI+TX3fbaEpKRu+pxsDEggiBjZBWr8cRCKCq4YiVklcdLZl26KgHcX+kWMnr/z5cOvWiQsv7jyerT5svPp++dVs8/312ezO80+P3ry9+vPSp9vW4htwMZ9/W81xhIPFsBrNQ5XmRRTHRWDUtJaqlFLUsexlZZ1nyjwMeyXNk6IYDGhumJStlhQxa9hcFpJS1sJTH98qBCSEK1CLSEVXCdP2ujsgfhuDgYrcvO0gnhO5ks1mwxlaTpzb0O5gXRM92dRNdpSUEcIMc0QTbmAxcduiSYTRWQTxSrLdW9ZTFwPHQRuk6AgHXukNnASUZEBDEIBQw06HdcIDt24d+33247XDFz/eePH4xu/q7pfJy6+fPn3ffH7nwaO3l969fvPo0Z37T6zxr8VqjICxGof59Np4sVj0Nz586Y+G5yIQ5VuRT+MgYKVM2J+U+6zMev66X1zmxoDXlBLSLhLlKagZOixsMiCeGwO2+5gHCGfZ7qAlvE4UYPx1R0JIuMaJu7vhYFLGQ6Ky+5toNJui28GEZQf/5u7Nt3U9B/mJ6ANurupgJg0XudtcgZEI15WtjHoezGfRlzYaPJji3eu5O7KWu1MFbuJ3Wp3d7R2o1K2W7bjdWnWvnDuNb2xuxFF47+7jx6/O91cPIR8ePn06e/vp3Zknl97+fPT0wbNTb27/q+iKX5OIo3gU/VA/9kMwZj8VlAiLiPnb+mUUYrCwAi2OW3UKqy6d2Y11WXc7r0JwjV1lS890TEEnMbjTs4hpE2+OcSrceVkLRzSuy/0VvfxFOPS4997n+7jv930+7x3Y293b36+n611pb3dyoZVu5Xd35nYAIJBGb176dOnlxYvPTz6/fP758GXtPNToWhDzEycPnjh85vLZk6dOXj5y+PlRy+3zt1/uwLvCQfDMyWErbLKyGhysW4esNuv/2tSwBXg/R0G8WxgZGh2BkqrrqG343P2jitUyarl45Pix+zarBbiUceeEHSoioAras1hHTj+wHL9vOwJ0t6H7QDG0Pzh+Ds6ArENKd2IOBDFj43DN7hpyjbnGhg5Z7BYgFLumQEoJtL6diVu3pngowzmm8HY+yJK1dLdfLvT5ckcpdrYkid8oNDvZ0jb0Rvr85OOdR4/eTR9o/diva3s7igTpc2Gv3qrLkz/z3d10/sfOw5+XJ18ufHoZv3JhYfTClQXYtn0aGX16/tLoxdefHp/YOWEbjQ9PDg/bhifO5Y9ZDw9dsFqGDtvsQP6228fGrVD1G4fyl3M8NA78a7sr43De8nqdY8tej3dqOeBfDi0vh2ZAF6ulQ4Hci/D4izAomvCLVp9/BtiEznMvHuRfzGS8M0fOOZa9jhmvAwvPhBy55RyO4+HcuBcHypc3cwtb9o24vH4aB3FVOBC2ejA86BjDoixGB/4LdeKrkY0ITBHbSPXFXk9rbXVivexW8Xe8upR98vnrV3GpuHIjtn6gtZsWNU2pZpVOXRK7+2Jf6vZ/7LVkeb8uvtxtfzqfl+V25tXElUnHJSD1BeZeTbg8gQlw+//D2tDYSMDpGbW6HsQfgsLCc7FrezHj8YLuBEh7D45Cr2P/lA/DQhmrb5YOu4N+570D4QpX8WI+4PpWQCDp57gc7fa7dc7NfXcFwvhc3T/LeUvQAcHvxmb9s37vkblc+AHpxkD9zsHNQAI/NlapFNAgxo3YMTs3gWJ9hOTcbHQKy3Dzq6DDWOw0Jkd5YiuSWUwky+WN8jy/2uls81I2thnb7vWanZXYk6+lz5sf/8bk3tePj56sH9D2tKqmiCL4o58Vt6qaKEpKXWrt7MpdudaP53+eb/+hH8bjtfafdno4nvH4gMQdyDmwKffkHxyLg1FYO+iLBxwzfgwkrzkgeONgllDg3GWugiACPKQ+T7E0SaGU0/LKljQHxoAkGMLQCYFBUYxBZx2ckCMZbrHgQzFOp2Y5VNc5EhMYndOFNWGWbOg+N9yIqZBUCPUvtKEvAjmLoFgglKBSuIAQAsHS/i6LsT6apT0J7G4hQryFvjNvUx825tfK/bXUm7XsdjaWlUqlzpdmbWmlqGWnr319VCr+vnbj3dXpA9UuQELqKWJXERUtm93tavuKIiv1bn6v1eprrSvfFvh2gf3zpxxt11uZKI3TtUzagaM0nkGCQTo4Dk3P/RkQ8Tp8GAlxc6MkiaECCtpChrlLIWAzk9N1akANBNPR6XsMlRA4RDUM00gKlI42EMRBc6ZpqjhGJYNkiGPcgs4wFJXUKRT+SZmcauq6c5xyR23ngpSK+N1oMgg/EJgGM1Apgoi4QBA/T6EstMxrJIkUz0aItbVouZxIPUutbW1vpzbeiPGt7dJmcxOwECs2iytLipiFua2Prn+8c2PlyZ0DitJTFLGjxPa7kgT40OBLKUtSd2/3Gy+X+RrP1/l4MJp/Gq3JC9LDWuY1G6VRdhFlESyDgjNmaeCMuAOuOXuF5io5kkRJAgxXjVqFohoGaCBMwjNuqAnTNAZvf6/NzdwdkOTApEIMYZoDQaAgojJTqTEAAHAIuWHoA8ZkGFUFoy26SYFX3IbO0ClKsNunGEEwzAqtuiwDUmBUQSXuAkSm7q6SqEEA21FofCDm386zq2U18jayXc7EQcaZcmS+dJpvUp1NWVz5tVRcby79XZmOTT+69ujGneb0DW16+oBS7fWUDnhA1MR6ta5oMvhCk9t1TarvtPiuLP+It+V0Xebbbb7VYqO1WpSlWaRAsyjO4ogQBHdkcAB5MsrcJWp5CBbmGSVV1cBVo/HeGJgqwTRSxttnA0MdwOXEIGE4LZQqyAg1AKspN2HaRxwUlzCMiCmY8KEMHUBDCkmDClRMk/Ebhs6hpsVr0E0eYTBdNZMMsmU0VJNACRhqg7HJuwzFqObqoqkKi6+QyGJ5ni1HUvxG+1dWrm3B9vNLam1NLGnpfrOjfCmKpSel9aX10q+V3x/vvCvG1mGBdKpKr7rfrUKWqNYlTZHSPyRRbHXrstyFbNGW6y05z8stGV5QZInnaYQdmSQLqwiyyoIqlogEWZaiKZZkkzjBUP+BPde9TBPJRoYkwjrYZzIfBgQjmHpCFwDMKpMgNhKqjgannDhEXwdYWD0OWC66oVOMIaiCbup0A7NBqkginAl+8FtRjhHiBYIhKcENaw+QRCUJzCD1pOpbFBbZeZolBBNliGSQiDuCyOq8XK5NtDbAFh5GcnYkmOSb2up0qqVeLLa52YzFSpux38rS0scn0+vTK+vFd/8AO4LTU2+GzPsAAAAASUVORK5CYII=\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Rainfall\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:weather-pouring\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":1,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"mm\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "rain", + "precipitation", + "downpour", + "rain shower", + "drizzle", + "raindrop", + "cloudburst", + "rainwater" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_rotational_speed_chart_card.json b/application/src/main/data/json/system/widget_types/simple_rotational_speed_chart_card.json new file mode 100644 index 0000000000..ed0674d5b0 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_rotational_speed_chart_card.json @@ -0,0 +1,33 @@ +{ + "fqn": "simple_rotational_speed_chart_card", + "name": "Simple rotational speed chart card", + "deprecated": false, + "image": "tb-image:c2ltcGxlX3JvdGF0aW9uYWxfc3BlZWRfY2hhcnRfY2FyZC5zdmc=:c2ltcGxlX3JvdGF0aW9uYWxfc3BlZWRfY2hhcnRfY2FyZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjExMiIgeT0iMjQiIGZpbGw9IiNmZmYiIHJ4PSI0Ii8+PHJlY3Qgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxMTEiIHg9Ii41IiB5PSIyNC41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik0xNyAzOWgzLjVhNSA1IDAgMCAxIDIgLjRjLjUuMiAxIC42IDEuMiAxIC4zLjQuNCAxIC40IDEuNiAwIC41IDAgMS0uMyAxLjRsLS43IDEtMS4yLjUtLjUuM2gtMy4ydi0xLjRoMi40Yy40IDAgLjcgMCAxLS4ybC42LS42LjItLjljMC0uMyAwLS42LS4yLS45bC0uNi0uNi0xLS4yaC0xLjlWNDlIMTdWMzlabTUuNyAxMC0yLjMtNC41aDEuOGwyLjMgNC40di4xaC0xLjhabTIuOS0zLjZ2LS4yYzAtLjUgMC0xIC4yLTEuNWwuNy0xLjJhMyAzIDAgMCAxIDEtLjdsMS41LS4zYy42IDAgMSAwIDEuNS4zLjQuMS44LjQgMSAuNy40LjQuNi44LjcgMS4ybC4zIDEuNXYuMmwtLjMgMS41LS42IDEuMmEzLjEgMy4xIDAgMCAxLTIuNiAxYy0uNSAwLTEgMC0xLjQtLjJhMy41IDMuNSAwIDAgMS0xLjgtMmwtLjItMS41Wm0xLjYtLjJ2LjJjMCAuMyAwIC42LjIgMSAwIC4yLjEuNS4zLjdsLjUuNWMuMy4yLjUuMi44LjJhMS42IDEuNiAwIDAgMCAxLjQtLjdjLjEtLjIuMy0uNS4zLS44bC4xLTF2LTFsLS40LS44YTEuNiAxLjYgMCAwIDAtMS40LS43Yy0uMyAwLS41IDAtLjguMmwtLjUuNWMtLjIuMi0uMy41LS4zLjhsLS4yIDFabTEwLjMtMy42djEuMmgtNC4ydi0xLjJoNC4yWm0tMy0xLjhoMS43djcuN2wuNC4yaC40YTIuNSAyLjUgMCAwIDAgLjYgMFY0OWE0LjIgNC4yIDAgMCAxLTEuMS4xYy0uNCAwLS43IDAtMS0uMi0uMy0uMS0uNi0uMy0uNy0uNi0uMi0uMy0uMy0uNy0uMy0xLjJ2LTcuM1ptOC43IDcuN1Y0NGMwLS4zIDAtLjUtLjItLjdhMSAxIDAgMCAwLS40LS40bC0uNy0uMi0uNy4xLS41LjQtLjEuNWgtMS43bC4yLS44LjctLjdhMyAzIDAgMCAxIDEtLjUgNCA0IDAgMCAxIDEuMi0uMmMuNSAwIDEgMCAxLjUuMi40LjIuNy41IDEgLjkuMi40LjMuOC4zIDEuNHYzLjNsLjEuOS4yLjd2LjFoLTEuN2EzIDMgMCAwIDEtLjEtLjdsLS4xLS44Wm0uMi0zdjFoLTEuMWwtLjguMWMtLjMgMC0uNS4xLS42LjNhMSAxIDAgMCAwLS41LjlsLjEuNS40LjQuNy4xYTEuOCAxLjggMCAwIDAgMS41LS43bC4yLS42LjUuOC0uMi42YTMgMyAwIDAgMS0uNi42IDIuNiAyLjYgMCAwIDEtMS43LjZjLS41IDAtMSAwLTEuMy0uMy0uNC0uMS0uNy0uNC0xLS43YTIuNCAyLjQgMCAwIDEtLjEtMi4ybC43LS44IDEuMS0uNCAxLjQtLjJoMS4zWm02LjgtMi45djEuMkg0NnYtMS4yaDQuMlptLTMtMS44SDQ5djcuN2wuNC4yaC40YTIuNSAyLjUgMCAwIDAgLjYgMFY0OWE0LjIgNC4yIDAgMCAxLTEuMS4xYy0uNCAwLS43IDAtMS0uMi0uMy0uMS0uNS0uMy0uNy0uNi0uMi0uMy0uMy0uNy0uMy0xLjJ2LTcuM1ptNi4zIDEuOFY0OWgtMS43di03LjRoMS43Wm0tMS44LTIgLjMtLjYuNy0uMmMuMyAwIC41IDAgLjcuMmwuMi43YzAgLjIgMCAuNC0uMi42YTEgMSAwIDAgMS0uNy4yIDEgMSAwIDAgMS0uNy0uMi44LjggMCAwIDEtLjMtLjZabTMuNSA1Ljh2LS4ybC4zLTEuNS42LTEuMmEzIDMgMCAwIDEgMS4xLS43Yy40LS4yIDEtLjMgMS41LS4zczEgMCAxLjQuM2MuNC4xLjguNCAxLjEuN2wuNyAxLjIuMiAxLjV2LjJjMCAuNSAwIDEtLjIgMS41bC0uNyAxLjJhMy4xIDMuMSAwIDAgMS0yLjUgMWMtLjYgMC0xIDAtMS41LS4yYTMuNSAzLjUgMCAwIDEtMS44LTJsLS4yLTEuNVptMS43LS4ydjEuMmwuNC43Yy4yLjIuMy40LjYuNS4yLjIuNS4yLjguMmExLjYgMS42IDAgMCAwIDEuMy0uN2wuNC0uOHYtMmwtLjQtLjhhMS42IDEuNiAwIDAgMC0xLjMtLjdjLS4zIDAtLjYgMC0uOC4yLS4zLjEtLjQuMy0uNi41bC0uMy44LS4xIDFabTguNS0yVjQ5aC0xLjd2LTcuNGgxLjZ2MS42Wk02NSA0NWgtLjZjMC0uNS4xLTEgLjMtMS40LjEtLjUuMy0uOC42LTEuMWEyLjcgMi43IDAgMCAxIDIuMS0xbDEgLjEuNy41LjUuOC4yIDEuM1Y0OWgtMS43di00LjhjMC0uMyAwLS42LS4yLS44YS45LjkgMCAwIDAtLjQtLjVsLS43LS4xYTEuNiAxLjYgMCAwIDAtMS4zLjdsLS40LjctLjEuOFpNNzYgNDcuNVY0NGMwLS4zIDAtLjUtLjItLjdhMSAxIDAgMCAwLS40LS40bC0uOC0uMi0uNi4xLS41LjQtLjIuNWgtMS42YzAtLjMgMC0uNS4yLS44LjEtLjMuMy0uNS42LS43YTMgMyAwIDAgMSAxLS41IDQgNCAwIDAgMSAxLjItLjJjLjYgMCAxIDAgMS41LjIuNC4yLjguNSAxIC45LjMuNC40LjguNCAxLjR2NC4ybC4zLjd2LjFoLTEuN2EzIDMgMCAwIDEtLjItLjd2LS44Wm0uMi0zdjFINzVsLS44LjEtLjYuM2ExIDEgMCAwIDAtLjQuOWwuMS41LjQuNC42LjFhMS44IDEuOCAwIDAgMCAxLjUtLjdsLjMtLjYuNS44LS4zLjZhMyAzIDAgMCAxLS41LjYgMi42IDIuNiAwIDAgMS0xLjguNmMtLjUgMC0uOSAwLTEuMy0uMy0uMy0uMS0uNi0uNC0uOS0uN2EyLjQgMi40IDAgMCAxLS4xLTIuMmwuNy0uOCAxLS40IDEuNS0uMmgxLjNabTUuMS02VjQ5aC0xLjZWMzguNWgxLjZabTEwIDguNS0uMS0uNGExIDEgMCAwIDAtLjUtLjRsLTEtLjMtMS4xLS4zLS44LS41Yy0uMy0uMS0uNC0uMy0uNi0uNmwtLjItLjguMi0uOS42LS43LjktLjUgMS4yLS4xYy42IDAgMS4xIDAgMS42LjMuNC4yLjguNSAxIC44LjIuNC40LjguNCAxLjJoLTEuN2wtLjEtLjVhMSAxIDAgMCAwLS41LS41bC0uNy0uMS0uNy4xYTEgMSAwIDAgMC0uNC40LjkuOSAwIDAgMCAwIC44bC4yLjIuNC4yLjguMiAxLjQuNWMuNC4yLjguNCAxIC43LjIuMy4zLjcuMyAxLjFhMiAyIDAgMCAxLS44IDEuNiAzIDMgMCAwIDEtMSAuNUw5MCA0OWMtLjcgMC0xLjIgMC0xLjctLjNzLS44LS42LTEtMWMtLjMtLjMtLjQtLjctLjQtMS4xaDEuNmMwIC4zIDAgLjUuMi43bC42LjQuNy4xLjgtLjFjLjIgMCAuMy0uMi40LS4zbC4yLS41Wm01LTR2OC44aC0xLjdWNDEuNmgxLjVsLjEgMS40Wm00LjggMi4ydi4ybC0uMiAxLjUtLjYgMS4yYTIuNSAyLjUgMCAwIDEtMi4xIDFjLS41IDAtLjkgMC0xLjItLjJsLS45LS43YTQgNCAwIDAgMS0uNi0xLjJsLS4zLTEuNHYtLjVsLjMtMS41LjYtMS4xYTIuNSAyLjUgMCAwIDEgMi0xYy41IDAgMSAwIDEuMy4ybDEgLjguNSAxLjIuMiAxLjVabS0xLjcuMnYtMS4ybC0uNC0uN2ExLjUgMS41IDAgMCAwLTEuMy0uN2wtLjcuMS0uNS40YTIgMiAwIDAgMC0uNC43bC0uMS43VjQ2bC4yIDEgLjYuNiAxIC4yYy4yIDAgLjUgMCAuNy0uMi4yIDAgLjQtLjMuNS0uNS4yLS4yLjMtLjUuMy0uOGwuMS0xWk0xMDYgNDljLS42IDAtMSAwLTEuNS0uMmEzLjMgMy4zIDAgMCAxLTEuOC0yIDQgNCAwIDAgMS0uMy0xLjN2LS4zYzAtLjYuMS0xLjEuMy0xLjYuMS0uNS40LS45LjctMS4yYTMgMyAwIDAgMSAxLS44bDEuNC0uMmMuNSAwIDEgMCAxLjQuMmwxIC44LjUgMS4xYy4yLjUuMiAxIC4yIDEuNXYuN2gtNS43di0xLjJoNC4xdi0uMWMwLS4zIDAtLjYtLjItLjkgMC0uMi0uMi0uNC0uNS0uNmwtLjgtLjJjLS4zIDAtLjUgMC0uOC4yLS4yIDAtLjMuMi0uNS41bC0uMy43LS4xIDF2LjRsLjEuOS40LjcuNi41LjkuMWEyLjMgMi4zIDAgMCAwIDEuOC0uOWwxIC45LS43LjYtMSAuNWMtLjMuMi0uNy4yLTEuMi4yWm03LjcgMGMtLjUgMC0xIDAtMS41LS4yYTMuMyAzLjMgMCAwIDEtMS44LTIgNCA0IDAgMCAxLS4yLTEuM3YtLjNjMC0uNiAwLTEuMS4yLTEuNnMuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS4xLS44bDEuMy0uMmMuNiAwIDEgMCAxLjQuMmwxIC44Yy4zLjMuNS43LjYgMS4xbC4yIDEuNXYuN0gxMTF2LTEuMmg0di0uMWwtLjEtLjktLjUtLjYtLjktLjJjLS4yIDAtLjUgMC0uNy4yLS4yIDAtLjQuMi0uNS41bC0uNC43djIuM2wuNS43LjYuNS44LjFhMi4zIDIuMyAwIDAgMCAxLjktLjlsLjkuOS0uNy42Yy0uMi4yLS41LjQtLjkuNS0uNC4yLS44LjItMS4zLjJabTktMS42di05aDEuN1Y0OWgtMS41bC0uMi0xLjVabS00LjgtMi4xdi0uMmMwLS41IDAtMSAuMi0xLjVsLjYtMS4yYTIuNSAyLjUgMCAwIDEgMi4xLTFjLjUgMCAuOSAwIDEuMi4ybC45LjguNSAxLjEuMyAxLjV2LjVjMCAuNS0uMSAxLS4zIDEuNGE0IDQgMCAwIDEtLjUgMS4xIDIuNSAyLjUgMCAwIDEtMiAxIDIuNiAyLjYgMCAwIDEtMi4yLTFsLS42LTEuMi0uMi0xLjVabTEuNy0uMnYxLjJsLjQuN2MwIC4yLjMuNC41LjVsLjcuMmMuNCAwIC43IDAgMS0uMmwuNS0uN2MuMi0uMy4zLS42LjMtMXYtMS4yYzAtLjMgMC0uNS0uMi0uN2EyIDIgMCAwIDAtLjMtLjZsLS41LS41LS44LS4xYy0uMyAwLS41IDAtLjcuMmwtLjUuNWMtLjIuMi0uMy41LS4zLjhsLS4xIDFaIi8+PHBhdGggZmlsbD0iIzMwNUFENyIgZD0iTTIxLjQgMTA0LjZoMmE0IDQgMCAwIDAgMS44LS40Yy42LS4zIDEtLjcgMS4yLTEuMS4yLS41LjMtMSAuMy0xLjYgMC0uNyAwLTEuMi0uMy0xLjYtLjItLjUtLjYtLjktMS0xLjEtLjUtLjItMS0uNC0xLjctLjQtLjYgMC0xLjEuMi0xLjYuNGEyLjcgMi43IDAgMCAwLTEuNSAyLjVoLTMuM2MwLTEgLjItMiAuOC0yLjguNi0uOSAxLjMtMS41IDIuMy0yYTguMiA4LjIgMCAwIDEgNi42IDAgNSA1IDAgMCAxIDIuMiAxLjhjLjYuOS45IDIgLjkgMy4yIDAgLjUtLjIgMS4xLS40IDEuN2E1IDUgMCAwIDEtMS4yIDEuNmMtLjUuNS0xLjIuOS0yIDEuMi0uNy4yLTEuNi40LTIuNy40aC0yLjR2LTEuOFptMCAyLjV2LTEuOGgyLjRjMS4yIDAgMi4yLjIgMyAuNC45LjMgMS41LjcgMiAxLjIuNi40IDEgMSAxLjIgMS42YTUuOSA1LjkgMCAwIDEtLjEgNC40IDUgNSAwIDAgMS0xLjUgMS45IDguOCA4LjggMCAwIDEtNy4zIDEuMmwtMi0xLjFjLS42LS41LTEuMS0xLjEtMS41LTEuOC0uNC0uNy0uNS0xLjUtLjUtMi41aDMuM2MwIC42LjEgMS4yLjQgMS42LjMuNS42LjggMS4xIDEgLjYuMyAxLjEuNCAxLjguNGE0IDQgMCAwIDAgMS44LS4zYy41LS4zLjktLjYgMS4xLTEuMS4zLS41LjQtMSAuNC0xLjggMC0uNy0uMS0xLjQtLjQtMS45LS4zLS40LS43LS44LTEuMy0xYTUgNSAwIDAgMC0yLS40aC0yWm0yNS4zLTIuN3YzLjJhMTUgMTUgMCAwIDEtLjUgNCA3IDcgMCAwIDEtMS4zIDIuN2MtLjYuNy0xLjMgMS4yLTIgMS41YTcuNyA3LjcgMCAwIDEtNC45LjIgNS4zIDUuMyAwIDAgMS0zLjItMi42Yy0uNC0uNy0uNy0xLjUtLjktMi41LS4yLTEtLjMtMi0uMy0zLjN2LTMuMmMwLTEuNi4yLTMgLjUtNHMuNy0yIDEuMy0yLjZhNSA1IDAgMCAxIDItMS41IDcuNiA3LjYgMCAwIDEgNC45LS4yIDUuMSA1LjEgMCAwIDEgMy4yIDIuNmMuNC42LjcgMS41LjkgMi40LjIgMSAuMyAyIC4zIDMuM1ptLTMuMyAzLjd2LTQuMmMwLS44IDAtMS41LS4yLTIuMSAwLS42LS4yLTEuMS0uNC0xLjVhMyAzIDAgMCAwLS42LTFjLS4zLS40LS42LS42LTEtLjdsLTEtLjJhMyAzIDAgMCAwLTEuNC4zYy0uNC4yLS44LjUtMSAxYTUgNSAwIDAgMC0uNyAxLjdsLS4yIDIuNXY0LjJjMCAuOCAwIDEuNS4yIDIuMSAwIC42LjIgMS4yLjQgMS42LjEuNC40LjguNiAxbDEgLjcgMSAuMWMuNiAwIDEgMCAxLjQtLjMuNC0uMi44LS41IDEtMSAuMy0uNC41LTEgLjctMS42bC4yLTIuNlptMTkuNy0zLjd2My4yYTE1IDE1IDAgMCAxLS40IDQgNyA3IDAgMCAxLTEuNCAyLjdjLS41LjctMS4yIDEuMi0yIDEuNWE3LjcgNy43IDAgMCAxLTQuOS4yIDUuMyA1LjMgMCAwIDEtMy4yLTIuNmMtLjMtLjctLjYtMS41LS44LTIuNS0uMi0xLS4zLTItLjMtMy4zdi0zLjJjMC0xLjYuMS0zIC40LTQgLjMtMSAuOC0yIDEuNC0yLjZhNSA1IDAgMCAxIDItMS41IDcuNiA3LjYgMCAwIDEgNC45LS4yIDUuMSA1LjEgMCAwIDEgMy4yIDIuNmwuOCAyLjRjLjIgMSAuMyAyIC4zIDMuM1ptLTMuMyAzLjd2LTQuMmwtLjEtMi4xLS40LTEuNWEzIDMgMCAwIDAtLjctMWMtLjItLjQtLjUtLjYtLjktLjdsLTEuMS0uMmEzIDMgMCAwIDAtMS40LjNjLS40LjItLjcuNS0xIDFhNSA1IDAgMCAwLS42IDEuN2MtLjIuNi0uMiAxLjUtLjIgMi41djQuMmwuMSAyLjEuNCAxLjZjLjIuNC40LjguNyAxIC4yLjMuNS41LjkuN2wxLjEuMWMuNSAwIDEgMCAxLjQtLjMuNC0uMi43LS41IDEtMSAuMy0uNC41LTEgLjYtMS42LjItLjguMi0xLjYuMi0yLjZaTTY5LjcgOTloNmMxLjQgMCAyLjUuMSAzLjQuNS45LjQgMS42IDEgMiAxLjguNi43LjggMS43LjggMi44IDAgLjgtLjEgMS42LS40IDIuMi0uNC43LS44IDEuMi0xLjQgMS43LS41LjQtMS4yLjctMiAxbC0uOS40aC01LjR2LTIuM2g0Yy43IDAgMS4zLS4xIDEuOC0uNC40LS4yLjgtLjYgMS0xIC4zLS41LjQtMSAuNC0xLjUgMC0uNi0uMS0xLjEtLjQtMS41LS4yLS41LS41LS44LTEtMWE0IDQgMCAwIDAtMS44LS40aC0zLjFWMTE2aC0zVjk5Wm05LjggMTctNC03LjdoM2w0LjEgNy41di4yaC0zLjFabTExLjgtNi40SDg3di0yLjNoNC40YTQgNCAwIDAgMCAyLS40Yy40LS4yLjctLjYgMS0xIC4yLS40LjMtMSAuMy0xLjUgMC0uNi0uMS0xLS4zLTEuNS0uMy0uNS0uNi0uOS0xLTEuMi0uNi0uMy0xLjItLjQtMi0uNGgtMy41VjExNmgtM1Y5OWg2LjVjMS40IDAgMi41LjIgMy40LjZhNSA1IDAgMCAxIDIuMSAyYy41LjguNyAxLjcuNyAyLjggMCAxLS4yIDItLjcgMi44LS41LjgtMS4yIDEuNC0yLjEgMS44YTggOCAwIDAgMS0zLjQuNlptMTAtMTAuN2gyLjdsNSAxMy4yIDQuOS0xMy4yaDIuNkwxMTAgMTE2aC0yLjFsLTYuNS0xN1ptLTEuMSAwaDIuNWwuNCAxMS40djUuN2gtM1Y5OVptMTUgMGgyLjVWMTE2aC0zdi01LjdsLjUtMTEuNFoiLz48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuOSIgc3Ryb2tlLXdpZHRoPSIxLjUiIGQ9Ik0xODcgNTRjLTguNSAwLTcuNSA1Ni4xLTE2LjQgNTYuMS03LjUgMC04LjctMjMuOC0xNC40LTIzLjgtNS44IDAtOC40IDMyLjctMTUuOSAzMi43LTYuMSAwLTkuNS02LjYtMTUuMy02LjYiLz48L3N2Zz4=", + "description": "Displays historical rotational speed values as a simplified chart. Optionally may display the corresponding latest rotational speed value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'rotationalSpeed', label: 'Rotaional speed', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'rotationalSpeed', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Rotational speed\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 4000 - 2000;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 4000) {\\n\\tvalue = 4000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 4000 - 2000;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 4000) {\\n\\tvalue = 4000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":500,\"color\":\"#305AD7\"},{\"from\":500,\"to\":1500,\"color\":\"#3FA71A\"},{\"from\":1500,\"to\":3000,\"color\":\"#FFA600\"},{\"from\":3000,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Rotational speed\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"360\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"units\":\"RPM\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "angular speed", + "spin rate", + "revolutions", + "rotational frequency", + "spin motion" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_rotational_speed_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_rotational_speed_chart_card_with_background.json new file mode 100644 index 0000000000..f5dd16e022 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_rotational_speed_chart_card_with_background.json @@ -0,0 +1,33 @@ +{ + "fqn": "simple_rotational_speed_chart_card_with_background", + "name": "Simple rotational speed chart card with background", + "deprecated": false, + "image": "tb-image:c2ltcGxlX3JvdGF0aW9uYWxfc3BlZWRfY2hhcnRfY2FyZF93aXRoX2JhY2tncm91bmQuc3Zn:c2ltcGxlX3JvdGF0aW9uYWxfc3BlZWRfY2hhcnRfY2FyZF93aXRoX2JhY2tncm91bmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxMTIiIHk9IjI0IiBmaWxsPSJ1cmwoI2EpIiByeD0iNCIvPjxnIGZpbHRlcj0idXJsKCNiKSI+PHJlY3Qgd2lkdGg9IjE4OCIgaGVpZ2h0PSIxMDAiIHg9IjYiIHk9IjMwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii44IiByeD0iMyIvPjwvZz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNMTcgMzloMy41YTUgNSAwIDAgMSAyIC40Yy41LjIgMSAuNiAxLjIgMSAuMy40LjQgMSAuNCAxLjYgMCAuNSAwIDEtLjMgMS40bC0uNyAxLTEuMi41LS41LjNoLTMuMnYtMS40aDIuNGMuNCAwIC43IDAgMS0uMmwuNi0uNi4yLS45YzAtLjMgMC0uNi0uMi0uOWwtLjYtLjYtMS0uMmgtMS45VjQ5SDE3VjM5Wm01LjcgMTAtMi4zLTQuNWgxLjhsMi4zIDQuNHYuMWgtMS44Wm0yLjktMy42di0uMmMwLS41IDAtMSAuMi0xLjVsLjctMS4yYTMgMyAwIDAgMSAxLS43bDEuNS0uM2MuNiAwIDEgMCAxLjUuMy40LjEuOC40IDEgLjcuNC40LjYuOC43IDEuMmwuMyAxLjV2LjJsLS4zIDEuNS0uNiAxLjJhMy4xIDMuMSAwIDAgMS0yLjYgMWMtLjUgMC0xIDAtMS40LS4yYTMuNSAzLjUgMCAwIDEtMS44LTJsLS4yLTEuNVptMS42LS4ydi4yYzAgLjMgMCAuNi4yIDEgMCAuMi4xLjUuMy43bC41LjVjLjMuMi41LjIuOC4yYTEuNiAxLjYgMCAwIDAgMS40LS43Yy4xLS4yLjMtLjUuMy0uOGwuMS0xdi0xbC0uNC0uOGExLjYgMS42IDAgMCAwLTEuNC0uN2MtLjMgMC0uNSAwLS44LjJsLS41LjVjLS4yLjItLjMuNS0uMy44bC0uMiAxWm0xMC4zLTMuNnYxLjJoLTQuMnYtMS4yaDQuMlptLTMtMS44aDEuN3Y3LjdsLjQuMmguNGEyLjUgMi41IDAgMCAwIC42IDBWNDlhNC4yIDQuMiAwIDAgMS0xLjEuMWMtLjQgMC0uNyAwLTEtLjItLjMtLjEtLjYtLjMtLjctLjYtLjItLjMtLjMtLjctLjMtMS4ydi03LjNabTguNyA3LjdWNDRjMC0uMyAwLS41LS4yLS43YTEgMSAwIDAgMC0uNC0uNGwtLjctLjItLjcuMS0uNS40LS4xLjVoLTEuN2wuMi0uOC43LS43YTMgMyAwIDAgMSAxLS41IDQgNCAwIDAgMSAxLjItLjJjLjUgMCAxIDAgMS41LjIuNC4yLjcuNSAxIC45LjIuNC4zLjguMyAxLjR2My4zbC4xLjkuMi43di4xaC0xLjdhMyAzIDAgMCAxLS4xLS43bC0uMS0uOFptLjItM3YxaC0xLjFsLS44LjFjLS4zIDAtLjUuMS0uNi4zYTEgMSAwIDAgMC0uNS45bC4xLjUuNC40LjcuMWExLjggMS44IDAgMCAwIDEuNS0uN2wuMi0uNi41LjgtLjIuNmEzIDMgMCAwIDEtLjYuNiAyLjYgMi42IDAgMCAxLTEuNy42Yy0uNSAwLTEgMC0xLjMtLjMtLjQtLjEtLjctLjQtMS0uN2EyLjQgMi40IDAgMCAxLS4xLTIuMmwuNy0uOCAxLjEtLjQgMS40LS4yaDEuM1ptNi44LTIuOXYxLjJINDZ2LTEuMmg0LjJabS0zLTEuOEg0OXY3LjdsLjQuMmguNGEyLjUgMi41IDAgMCAwIC42IDBWNDlhNC4yIDQuMiAwIDAgMS0xLjEuMWMtLjQgMC0uNyAwLTEtLjItLjMtLjEtLjUtLjMtLjctLjYtLjItLjMtLjMtLjctLjMtMS4ydi03LjNabTYuMyAxLjhWNDloLTEuN3YtNy40aDEuN1ptLTEuOC0yIC4zLS42LjctLjJjLjMgMCAuNSAwIC43LjJsLjIuN2MwIC4yIDAgLjQtLjIuNmExIDEgMCAwIDEtLjcuMiAxIDEgMCAwIDEtLjctLjIuOC44IDAgMCAxLS4zLS42Wm0zLjUgNS44di0uMmwuMy0xLjUuNi0xLjJhMyAzIDAgMCAxIDEuMS0uN2MuNC0uMiAxLS4zIDEuNS0uM3MxIDAgMS40LjNjLjQuMS44LjQgMS4xLjdsLjcgMS4yLjIgMS41di4yYzAgLjUgMCAxLS4yIDEuNWwtLjcgMS4yYTMuMSAzLjEgMCAwIDEtMi41IDFjLS42IDAtMSAwLTEuNS0uMmEzLjUgMy41IDAgMCAxLTEuOC0ybC0uMi0xLjVabTEuNy0uMnYxLjJsLjQuN2MuMi4yLjMuNC42LjUuMi4yLjUuMi44LjJhMS42IDEuNiAwIDAgMCAxLjMtLjdsLjQtLjh2LTJsLS40LS44YTEuNiAxLjYgMCAwIDAtMS4zLS43Yy0uMyAwLS42IDAtLjguMi0uMy4xLS40LjMtLjYuNWwtLjMuOC0uMSAxWm04LjUtMlY0OWgtMS43di03LjRoMS42djEuNlpNNjUgNDVoLS42YzAtLjUuMS0xIC4zLTEuNC4xLS41LjMtLjguNi0xLjFhMi43IDIuNyAwIDAgMSAyLjEtMWwxIC4xLjcuNS41LjguMiAxLjNWNDloLTEuN3YtNC44YzAtLjMgMC0uNi0uMi0uOGEuOS45IDAgMCAwLS40LS41bC0uNy0uMWExLjYgMS42IDAgMCAwLTEuMy43bC0uNC43LS4xLjhaTTc2IDQ3LjVWNDRjMC0uMyAwLS41LS4yLS43YTEgMSAwIDAgMC0uNC0uNGwtLjgtLjItLjYuMS0uNS40LS4yLjVoLTEuNmMwLS4zIDAtLjUuMi0uOC4xLS4zLjMtLjUuNi0uN2EzIDMgMCAwIDEgMS0uNSA0IDQgMCAwIDEgMS4yLS4yYy42IDAgMSAwIDEuNS4yLjQuMi44LjUgMSAuOS4zLjQuNC44LjQgMS40djQuMmwuMy43di4xaC0xLjdhMyAzIDAgMCAxLS4yLS43di0uOFptLjItM3YxSDc1bC0uOC4xLS42LjNhMSAxIDAgMCAwLS40LjlsLjEuNS40LjQuNi4xYTEuOCAxLjggMCAwIDAgMS41LS43bC4zLS42LjUuOC0uMy42YTMgMyAwIDAgMS0uNS42IDIuNiAyLjYgMCAwIDEtMS44LjZjLS41IDAtLjkgMC0xLjMtLjMtLjMtLjEtLjYtLjQtLjktLjdhMi40IDIuNCAwIDAgMS0uMS0yLjJsLjctLjggMS0uNCAxLjUtLjJoMS4zWm01LjEtNlY0OWgtMS42VjM4LjVoMS42Wm0xMCA4LjUtLjEtLjRhMSAxIDAgMCAwLS41LS40bC0xLS4zLTEuMS0uMy0uOC0uNWMtLjMtLjEtLjQtLjMtLjYtLjZsLS4yLS44LjItLjkuNi0uNy45LS41IDEuMi0uMWMuNiAwIDEuMSAwIDEuNi4zLjQuMi44LjUgMSAuOC4yLjQuNC44LjQgMS4yaC0xLjdsLS4xLS41YTEgMSAwIDAgMC0uNS0uNWwtLjctLjEtLjcuMWExIDEgMCAwIDAtLjQuNC45LjkgMCAwIDAgMCAuOGwuMi4yLjQuMi44LjIgMS40LjVjLjQuMi44LjQgMSAuNy4yLjMuMy43LjMgMS4xYTIgMiAwIDAgMS0uOCAxLjYgMyAzIDAgMCAxLTEgLjVMOTAgNDljLS43IDAtMS4yIDAtMS43LS4zcy0uOC0uNi0xLTFjLS4zLS4zLS40LS43LS40LTEuMWgxLjZjMCAuMyAwIC41LjIuN2wuNi40LjcuMS44LS4xYy4yIDAgLjMtLjIuNC0uM2wuMi0uNVptNS00djguOGgtMS43VjQxLjZoMS41bC4xIDEuNFptNC44IDIuMnYuMmwtLjIgMS41LS42IDEuMmEyLjUgMi41IDAgMCAxLTIuMSAxYy0uNSAwLS45IDAtMS4yLS4ybC0uOS0uN2E0IDQgMCAwIDEtLjYtMS4ybC0uMy0xLjR2LS41bC4zLTEuNS42LTEuMWEyLjUgMi41IDAgMCAxIDItMWMuNSAwIDEgMCAxLjMuMmwxIC44LjUgMS4yLjIgMS41Wm0tMS43LjJ2LTEuMmwtLjQtLjdhMS41IDEuNSAwIDAgMC0xLjMtLjdsLS43LjEtLjUuNGEyIDIgMCAwIDAtLjQuN2wtLjEuN1Y0NmwuMiAxIC42LjYgMSAuMmMuMiAwIC41IDAgLjctLjIuMiAwIC40LS4zLjUtLjUuMi0uMi4zLS41LjMtLjhsLjEtMVpNMTA2IDQ5Yy0uNiAwLTEgMC0xLjUtLjJhMy4zIDMuMyAwIDAgMS0xLjgtMiA0IDQgMCAwIDEtLjMtMS4zdi0uM2MwLS42LjEtMS4xLjMtMS42LjEtLjUuNC0uOS43LTEuMmEzIDMgMCAwIDEgMS0uOGwxLjQtLjJjLjUgMCAxIDAgMS40LjJsMSAuOC41IDEuMWMuMi41LjIgMSAuMiAxLjV2LjdoLTUuN3YtMS4yaDQuMXYtLjFjMC0uMyAwLS42LS4yLS45IDAtLjItLjItLjQtLjUtLjZsLS44LS4yYy0uMyAwLS41IDAtLjguMi0uMiAwLS4zLjItLjUuNWwtLjMuNy0uMSAxdi40bC4xLjkuNC43LjYuNS45LjFhMi4zIDIuMyAwIDAgMCAxLjgtLjlsMSAuOS0uNy42LTEgLjVjLS4zLjItLjcuMi0xLjIuMlptNy43IDBjLS41IDAtMSAwLTEuNS0uMmEzLjMgMy4zIDAgMCAxLTEuOC0yIDQgNCAwIDAgMS0uMi0xLjN2LS4zYzAtLjYgMC0xLjEuMi0xLjZzLjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEuMS0uOGwxLjMtLjJjLjYgMCAxIDAgMS40LjJsMSAuOGMuMy4zLjUuNy42IDEuMWwuMiAxLjV2LjdIMTExdi0xLjJoNHYtLjFsLS4xLS45LS41LS42LS45LS4yYy0uMiAwLS41IDAtLjcuMi0uMiAwLS40LjItLjUuNWwtLjQuN3YyLjNsLjUuNy42LjUuOC4xYTIuMyAyLjMgMCAwIDAgMS45LS45bC45LjktLjcuNmMtLjIuMi0uNS40LS45LjUtLjQuMi0uOC4yLTEuMy4yWm05LTEuNnYtOWgxLjdWNDloLTEuNWwtLjItMS41Wm0tNC44LTIuMXYtLjJjMC0uNSAwLTEgLjItMS41bC42LTEuMmEyLjUgMi41IDAgMCAxIDIuMS0xYy41IDAgLjkgMCAxLjIuMmwuOS44LjUgMS4xLjMgMS41di41YzAgLjUtLjEgMS0uMyAxLjRhNCA0IDAgMCAxLS41IDEuMSAyLjUgMi41IDAgMCAxLTIgMSAyLjYgMi42IDAgMCAxLTIuMi0xbC0uNi0xLjItLjItMS41Wm0xLjctLjJ2MS4ybC40LjdjMCAuMi4zLjQuNS41bC43LjJjLjQgMCAuNyAwIDEtLjJsLjUtLjdjLjItLjMuMy0uNi4zLTF2LTEuMmMwLS4zIDAtLjUtLjItLjdhMiAyIDAgMCAwLS4zLS42bC0uNS0uNS0uOC0uMWMtLjMgMC0uNSAwLS43LjJsLS41LjVjLS4yLjItLjMuNS0uMy44bC0uMSAxWiIvPjxwYXRoIGZpbGw9IiMyQjU0Q0UiIGQ9Ik0yMS40IDEwNC42aDJhNCA0IDAgMCAwIDEuOC0uNGMuNi0uMyAxLS43IDEuMi0xLjEuMi0uNS4zLTEgLjMtMS42IDAtLjcgMC0xLjItLjMtMS42LS4yLS41LS42LS45LTEtMS4xLS41LS4yLTEtLjQtMS43LS40LS42IDAtMS4xLjItMS42LjRhMi43IDIuNyAwIDAgMC0xLjUgMi41aC0zLjNjMC0xIC4yLTIgLjgtMi44LjYtLjkgMS4zLTEuNSAyLjMtMmE4LjIgOC4yIDAgMCAxIDYuNiAwIDUgNSAwIDAgMSAyLjIgMS44Yy42LjkuOSAyIC45IDMuMiAwIC41LS4yIDEuMS0uNCAxLjdhNSA1IDAgMCAxLTEuMiAxLjZjLS41LjUtMS4yLjktMiAxLjItLjcuMi0xLjYuNC0yLjcuNGgtMi40di0xLjhabTAgMi41di0xLjhoMi40YzEuMiAwIDIuMi4yIDMgLjQuOS4zIDEuNS43IDIgMS4yLjYuNCAxIDEgMS4yIDEuNmE1LjkgNS45IDAgMCAxLS4xIDQuNCA1IDUgMCAwIDEtMS41IDEuOSA4LjggOC44IDAgMCAxLTcuMyAxLjJsLTItMS4xYy0uNi0uNS0xLjEtMS4xLTEuNS0xLjgtLjQtLjctLjUtMS41LS41LTIuNWgzLjNjMCAuNi4xIDEuMi40IDEuNi4zLjUuNi44IDEuMSAxIC42LjMgMS4xLjQgMS44LjRhNCA0IDAgMCAwIDEuOC0uM2MuNS0uMy45LS42IDEuMS0xLjEuMy0uNS40LTEgLjQtMS44IDAtLjctLjEtMS40LS40LTEuOS0uMy0uNC0uNy0uOC0xLjMtMWE1IDUgMCAwIDAtMi0uNGgtMlptMjUuMy0yLjd2My4yYTE1IDE1IDAgMCAxLS41IDQgNyA3IDAgMCAxLTEuMyAyLjdjLS42LjctMS4zIDEuMi0yIDEuNWE3LjcgNy43IDAgMCAxLTQuOS4yIDUuMyA1LjMgMCAwIDEtMy4yLTIuNmMtLjQtLjctLjctMS41LS45LTIuNS0uMi0xLS4zLTItLjMtMy4zdi0zLjJjMC0xLjYuMi0zIC41LTRzLjctMiAxLjMtMi42YTUgNSAwIDAgMSAyLTEuNSA3LjYgNy42IDAgMCAxIDQuOS0uMiA1LjEgNS4xIDAgMCAxIDMuMiAyLjZjLjQuNi43IDEuNS45IDIuNC4yIDEgLjMgMiAuMyAzLjNabS0zLjMgMy43di00LjJjMC0uOCAwLTEuNS0uMi0yLjEgMC0uNi0uMi0xLjEtLjQtMS41YTMgMyAwIDAgMC0uNi0xYy0uMy0uNC0uNi0uNi0xLS43bC0xLS4yYTMgMyAwIDAgMC0xLjQuM2MtLjQuMi0uOC41LTEgMWE1IDUgMCAwIDAtLjcgMS43bC0uMiAyLjV2NC4yYzAgLjggMCAxLjUuMiAyLjEgMCAuNi4yIDEuMi40IDEuNi4xLjQuNC44LjYgMWwxIC43IDEgLjFjLjYgMCAxIDAgMS40LS4zLjQtLjIuOC0uNSAxLTEgLjMtLjQuNS0xIC43LTEuNmwuMi0yLjZabTE5LjctMy43djMuMmExNSAxNSAwIDAgMS0uNCA0IDcgNyAwIDAgMS0xLjQgMi43Yy0uNS43LTEuMiAxLjItMiAxLjVhNy43IDcuNyAwIDAgMS00LjkuMiA1LjMgNS4zIDAgMCAxLTMuMi0yLjZjLS4zLS43LS42LTEuNS0uOC0yLjUtLjItMS0uMy0yLS4zLTMuM3YtMy4yYzAtMS42LjEtMyAuNC00IC4zLTEgLjgtMiAxLjQtMi42YTUgNSAwIDAgMSAyLTEuNSA3LjYgNy42IDAgMCAxIDQuOS0uMiA1LjEgNS4xIDAgMCAxIDMuMiAyLjZsLjggMi40Yy4yIDEgLjMgMiAuMyAzLjNabS0zLjMgMy43di00LjJsLS4xLTIuMS0uNC0xLjVhMyAzIDAgMCAwLS43LTFjLS4yLS40LS41LS42LS45LS43bC0xLjEtLjJhMyAzIDAgMCAwLTEuNC4zYy0uNC4yLS43LjUtMSAxYTUgNSAwIDAgMC0uNiAxLjdjLS4yLjYtLjIgMS41LS4yIDIuNXY0LjJsLjEgMi4xLjQgMS42Yy4yLjQuNC44LjcgMSAuMi4zLjUuNS45LjdsMS4xLjFjLjUgMCAxIDAgMS40LS4zLjQtLjIuNy0uNSAxLTEgLjMtLjQuNS0xIC42LTEuNi4yLS44LjItMS42LjItMi42Wk02OS43IDk5aDZjMS40IDAgMi41LjEgMy40LjUuOS40IDEuNiAxIDIgMS44LjYuNy44IDEuNy44IDIuOCAwIC44LS4xIDEuNi0uNCAyLjItLjQuNy0uOCAxLjItMS40IDEuNy0uNS40LTEuMi43LTIgMWwtLjkuNGgtNS40di0yLjNoNGMuNyAwIDEuMy0uMSAxLjgtLjQuNC0uMi44LS42IDEtMSAuMy0uNS40LTEgLjQtMS41IDAtLjYtLjEtMS4xLS40LTEuNS0uMi0uNS0uNS0uOC0xLTFhNCA0IDAgMCAwLTEuOC0uNGgtMy4xVjExNmgtM1Y5OVptOS44IDE3LTQtNy43aDNsNC4xIDcuNXYuMmgtMy4xWm0xMS44LTYuNEg4N3YtMi4zaDQuNGE0IDQgMCAwIDAgMi0uNGMuNC0uMi43LS42IDEtMSAuMi0uNC4zLTEgLjMtMS41IDAtLjYtLjEtMS0uMy0xLjUtLjMtLjUtLjYtLjktMS0xLjItLjYtLjMtMS4yLS40LTItLjRoLTMuNVYxMTZoLTNWOTloNi41YzEuNCAwIDIuNS4yIDMuNC42YTUgNSAwIDAgMSAyLjEgMmMuNS44LjcgMS43LjcgMi44IDAgMS0uMiAyLS43IDIuOC0uNS44LTEuMiAxLjQtMi4xIDEuOGE4IDggMCAwIDEtMy40LjZabTEwLTEwLjdoMi43bDUgMTMuMiA0LjktMTMuMmgyLjZMMTEwIDExNmgtMi4xbC02LjUtMTdabS0xLjEgMGgyLjVsLjQgMTEuNHY1LjdoLTNWOTlabTE1IDBoMi41VjExNmgtM3YtNS43bC41LTExLjRaIi8+PHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjkiIHN0cm9rZS13aWR0aD0iMS41IiBkPSJNMTg3IDU0Yy04LjUgMC03LjUgNTYuMS0xNi40IDU2LjEtNy41IDAtOC43LTIzLjgtMTQuNC0yMy44LTUuOCAwLTguNCAzMi43LTE1LjkgMzIuNy02LjEgMC05LjUtNi42LTE1LjMtNi42Ii8+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHBhdHRlcm5Db250ZW50VW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48dXNlIHhsaW5rOmhyZWY9IiNjIiB0cmFuc2Zvcm09Im1hdHJpeCguMDA1MDI1MTMgMCAwIC4wMDg5NzM0NCAwIC0uMikiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjE5NyIgaGVpZ2h0PSIxMDkiIHg9IjEuNSIgeT0iMjUuNSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMi4zIi8+PGZlQ29tcG9zaXRlIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzEwODdfNzQ3OTMiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzEwODdfNzQ3OTMiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48aW1hZ2UgeGxpbms6aHJlZj0iZGF0YTppbWFnZS9qcGVnO2Jhc2U2NCwvOWovNEFBUVNrWkpSZ0FCQVFBQUFRQUJBQUQvMndCREFBSUJBUUVCQVFJQkFRRUNBZ0lDQWdRREFnSUNBZ1VFQkFNRUJnVUdCZ1lGQmdZR0J3a0lCZ2NKQndZR0NBc0lDUW9LQ2dvS0JnZ0xEQXNLREFrS0Nnci8yd0JEQVFJQ0FnSUNBZ1VEQXdVS0J3WUhDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dyL3dnQVJDQUNmQU1jREFSRUFBaEVCQXhFQi84UUFIZ0FBQWdNQkFRRUJBUUVBQUFBQUFBQUFCd2dGQmdrRUF3RUNDZ0QveEFBYUFRQURBUUVCQVFBQUFBQUFBQUFBQUFBQkFnTUVBQVVHLzlvQURBTUJBQUlRQXhBQUFBREdQNS9kN0EvNGdqY1F2cWxLUmErNTZRdEZxYkxvVmgxdWJDdlh3UXZSRVRVVXZ6YlJQTll2cTJQVy9NbTFvOTRPaGVUU1lWYU80R3liRjlUL0FETGV4NTMzait1SGhSWXU0OU81M1BKMXNUS2lqYUlzL25zeEU2TExhU2FYaVlaVmQ2RmRRODliOXhFQlhBWDBjWU9wTjdzbWt4SytiT3ZLOE9iUWNwbkRQMU1Qb09qdENlWjYyVGZhVHk5b0M0Y25GMVpVZUtGS0V3d08zNUlFOFZFYlJ6TGJSbVZPN3VwUGNqVnBZd2Vsalp6UFp1czlicVF5OGFXc2QvTkY2K0VwQXhaTm1tVDlDcElIQ0RoUXlDSHhyTmtGNVc3d3FYRWJ0N2kzTnRFbzF2cW1pc0FneTVXYnN5OFdtNXVQVE9BWHBXVEc4YmdyUXpjV0FhcVFRVVpCUFJ5V1ROVGlCcGRrczB6WmxacklXTkNQYTE1a3B2VG1Cc1Jxb1ZHcEdlK3VDeGFva2pOVmVyUzl1NXU0V2Z2UGJDcjJNRmJJMUg4M1lZWXRWVzVicXpKS011TlV1eW4zN201aFdZNDhCNFhNbnYzUUo1bDQwbSs1SE5FYzJQVHh0NWgwMGZnUTZyb25nMUlic3orS0hoSWFYTmNIMW5xeGowTExWYWt5aFdpdzNDb3NIYWhXdmR4UlVyUldZWGRHa2xVOHlmbDdzd051WWQwUjRjOWlWSjQ4OElxSWlPaUpRQkk0TGVaTkJEVWtCVEZucytORVFuVkFaYU1Vd2Z2SnBlN0xjdEtiK2pmampYaW92ZGFXd1VEUkpxczlMeUNXRmFvOEZmcXVhZXZMRTNVL3ljbVpxaFIwT1NQNEhsYTJRRDlwMmJQUWtvM0hWZGR2TDJHcFdKeU5DZDBPZUg3S3VOVUZqS1hVZDhvdVRnd25JVHVxWjhhc3lYK2xtdENIUmJ6Tms4TzZBeS8yaUZiVGRQTm9QRVhYU2k1eWVoamJpVnROL08xR1JEMDhZRUJPOU02QWVoK0ROU29lNWt3S3d4ZFFzUmt0NnVKVTlVU0ZDam80N2F0WXRRQ2RjYi9UeERyUlBSYnpkZENITGJlUXIweTVDR1d6VzBMeDNudUt2Mm1yRjVQZGkwa1ZYaWVCV1V0dEpxdTNMKzR1ZkQrZlAydk9uYzd2RmswUGZuc24ybU9mdW1MQnhwR3VvdnZPUm13bnFrU2d2aU9TNTByM2Q5UEVkQzRNTGVnNmh1TkVjOVlqaEVucmtweTMxd0hWNWhHczlKdlAxMU5sYmFOTXN0dVpjcXpnN0xXS3pkRHp0Wk1teUZiTXpiNXRBL0k2QWZ6M1cwYy9HZTllS2podzdjS0JsbE9hT0lYVEhEME1oRFUvS0I2OEdwbTV2S2prOHZMUEhWRDM3aERhUkNsUXV5Y21xMWVQVTdoOVBmT1BZQnEzazBWVmlNQ2tTUXNGcDFzOU9yMFN3b2xrOUhYUTN6OWFrWGxNcVo0RkxObWVvRll5aThkbG1VYmxJTTJTeHVtNVFsUW96WUIxUm00MFlXYmhKMSs5MXhCclhCZmFMQU1vQ3RKb3Awa3B1Y1VLdFZTb01GUjB3NjNGR3ZPQWRaL05TNkl6VFpyUEJudEVPcTNWbXAxRUw4NmFUWmRGY1BSaEJhbWVROEdIVU4wU1pVeVhkZkFXQW02L3VxRjZJQUxSSDFEUlZFWVNGWEN6VzRLS3ZkcHA5YVhKdzhXN2taZWlaZVRKcWVqTGUyQWt3R0U0Q0orejIwNWlnajhYQWNPQUZwanpVV2xKM3FSSWtuTExZaUhsdXZLbXVJU3FjNUh3OSsxNlNtWkNiKzU1bk05WHd5NkdqalRtUENGa3prMHhNcXVGS3lvREFCN0lRTjFzR1Z2dmNjNDJJY0hOaVVNTTJVM1JGUXRrTzN1cU5VNG40N2VibzdPNktaZnllc2syTEtQR09CWFNaVmpSaXBWQTlvcTVya045TXZKdW1vTmJFWXg1TEZPZEN1alByQ2xBYnMxdGVZRlZrT05NN1ZKcGlUaytWUnJXVVVSY1ZjY3ZQZ3FzNmpmL3hBQXJFQUFCQlFBQkJBSUJBd1VCQVFBQUFBQUVBUUlEQlFZSEFCRVNFeFFoRlNJakpBZ1FGakV5TXhmLzJnQUlBUUVBQVFnQ2ptWTFuYnIzTTY5elA5OUQzK1dESFZKbjIwc2xyK1c2dUxxYTZOK2FWVjU4MjVnK1F5K29wNlNKc2t0WVY2akkzOVpBU0dhUVYzVVVUR3QreUgxd01MckV6V2JrcThKY0tNSGRUTi9hcWF4K29sZDVwaXplUW9JKzdiaWhvOWdJVURQeVp4eVRrRFp6WTNLby9kZW5UU3A5eThlUDkxWU8zcTl1RE14V1B1Z01adDlwdHJCNFhWcFRjb2gzNE5LKzA0MXRxa05UcnhITDI2OG5kSjU5KzZ5UDcvMmExZk5FNnhtbmlvNGZ4a3QvU1ZXa2lSaE50bktkbzN0b3NzTVlDeHMwN1hXNVR2NDNJMXZjaXVaVHkrdjNmOTAwS2VTZDh4VmZLZWlkWnluUVFKRTZKQkhLZzlCVjVXUE9HZUZOczhOUFFXMHd0VmEzcEpyUGhtY1dkM1ZjVXFiQjQzK05sTnNqN2JNUEhXSURNYklVOUFLZ2Erd3ZNemlVZ2pSN2V2YXZTdlYzMjd2MVNDRGtIdy9Qck9OTWpkRFFXRnh5Sm1xU2sxMFFkVkVmSERENjA0Y2xqZFpHUzlGZGxualZMdlFwWHhMREhwK3g5bExNeUtsbWQyY3RTRkpGSTFFd2pnMWUxaWlka2dUcDdtdStrTmpaTkNvNytTODFEckt4SlN0ZGo3ZXBpL0pkY01uZVkvd3V0UlhSV2xDWlhPL3AweFdHSXBTelRPWk1yYW41OWw5aytNK1Y2UGUwN0pIamNmMnBKTUxPak9KV0VoL0pvQnVIN0IzMlhIeFJYRHZUNU5GeFpsR3d1am55dkorTHA2a1FJL2MzUTEvcFduVmRoS3NEa2dmUzhuUjRBZDFreTQvcVB2ckZxTkdHNUt2ZGFZMnN0WFY2azJMNDBaVm9uUkFhanFqWXMwNGlzS1NhV2oxMVBaaWRvR2xNZW5tbGdUOUwyTGxSODdrZHlKUlpjR1lpSW5qTXF5YWM0V3RJWFFNSTliNk03VDVVL3dESzVzN2tUVXdCd1dROWVTSnF0U1NkZmFZbDhBSytrRFRHR3lSd2pGNnpPakorOE50YUEwaHNBMEcxYjYwbEFzc0Zxa21rc283NTlwQ3NNZ1FRT2pQYisxTm5id213VUtaTXBYd0oyS29XMWxMYXhrQjF4STBVNkhTRE9nbjdPVE9WRGo3S1ErVW8wTzJyM3hEaFlJa01uOG5CVjE3NitDV0ZTckZGVlVTUW55bWFpY2szUlFoY3czWEhPbUF6cHM3cEw3a084TE1sUERJdHJNNlgzbHlHSEVyRkVtQXJOVUJBcEJXOHdNRmxRcFBWWDl4YmxQZFZIMXdienlHeE1vOHpXNWl1YjdzN2ZWMmpTYXZqMmVSakxFSXNBYUttc0RBZmozYktNdVNaM3hyU3RzaExtVUtNTERHV2NIendoc2xNMHhIbFFabUVtdllPUEFBMFJFNldleHFuUjNkY0Z5Y09OYUgySTFoeS9iajFzSUVFRzJzekxpUWVPdG5KZEUxOHJIdVVocmw1TUl0aHpYZXNuMkpPc3JtTUh0Y3dQWXdLQWtyRlpHTHhGeWxjU01JNngrSnU2TlBuMzkveW5hbTZ1dnVJTnJSRE12MytQSDFVTTI3VTlLU3N0TGx2NXdha3J5OHJ2eHA5UFppd3p6b2NCWk9yUVNubFRVMXpUajMxaGx6dHplZ1djME1zMlQzSUZROG1DRXJrV1lrditOaUwzODVTem95cElrSmxkRTVhTWdobm1IZDhXYWcvek5HQjRpMjB4RGk3VExjY3VxM0tVVTJMMXg5dW1zL2VSM1cvbkdiZVBlNnE0ODBHam03RFpEaXN1cnJweCt2L0FKVjZJcDVoeTU5Tm1KM05aZThzYWVHS0p5RFcvcGpWSWQzWkZUeENsSnc1VnozUHk0Um4yeCtTbi9CT0d2NE5KWEpXNklDY2VyQ2JYVEVSMUYwUEhGUGI0VFVCV3Ivalh4alhlc0VJNldYdTlxUk5iQ3FObDRkdUo0clovcVNsSEZzWGtRRFJJMVVWR3RhOVAwTEF4Ty9zSmphenliRzlpVFA4ZXJ3djhmV2tHTnoySlN5dTIyVTY2UElZaHNOWEJYbmNxWC83NDQxSnlJamZKMTBtb0VnV083TUFxVGo1THNHbno2RnNSR2JsMGFIUnhONE1zaG9MTWdSK3UxV3RPdkpCd1NqTCtPVkk3ZWcwZFZQbjdLeXVCZHRNdVRrSWlONUp1cDZ1T3NYNVY1ZkdPUWF2cDN3U2Z5cnpNajFZakxhdXdsL1BSMnpJbkpkSU8xbnZDYjI3ZWNBLzZPaUdMNWZjNmVUUHFWallXL1Z0TCtUTlpDeTd2Q0syT0xHWlRGWnl0enlJNWF6MlRNN3lReE1ST3RDMU95dGJZaFJ5djg1QTZqL0V3NVM3cXpNZFlIeUZPeTl5Mml1WVN5TnhpNys0bmJwd1JzQnI5SlRJelZKeDZCWFFKQXpkSHgxWWlnaTEvSHQ3YlVETkV2SE5PUmtLaUczRzJROURwZ0o3N3FpTnBpYU0wU1grSU5ZdStIeHR2NGcyTW9qYVZ4QU1heTUrTFUweldlc3FUUVo1MGZkU0xScjJmdzlwczRxOGRZb1lMRmdOTExjd1pPcWtoWXRqSkFVd05VN1UxdjVwMmN5eVZFNnNDM0V2Y2lGcDJYNzVKb05UV0dPL0l3T2hqazd6QkF3WFN5c3ErTXVVbVprUjlWYk1uclpSSGpCYWE3cmEyUDRzZXUwa2x5WXJvdkt3aGdhblhIUkkrNHBQOFlOMW9LNUtHU3FpRmZaUVFTeTlUek9rbVY3bzNQYklrc2VYNUp2YzFENllnT2NnaVgrcDY4dVZIa3hJTkh5elpXUWJtaENXSnR0cFVubXVtT0luSERZeHlRcXJHdk8rL0xyUFRTUGtSZWdYUXlwNjNGQktqMTlsbXNrVDFhdGxuUXRaeDYyUW02cTVxV3lscXlxNDZkVStLTmljVU1MRXk3dHM4VFJQcjRxNkhsZkFXYlJwYnFBYUFXZFdSSEFYd21UWWp4TlJ1YURRMXl5dzVQZjNHVmwvakYyMXZ0SkU3bjFCdFgrdWFGcU9YN2dyL2hsUlBzcmhLMGl5bG1wL1F2U3hkMCs4bVBKQmNOV0Rza2hqNUZsbVhzL3Nzc2pwV3hNeUdlQkhFV2NxNEZIc25wWGlVVmJaK0xnSllxQ1I0M2NyRDNHbnE3U1R0eTFYc0hPaUxqd29jQjk4d2VXTnNGcmN1aGZzNytjUmtBMHVaTnRiUEtSVGFIa0hQUHJ0QzhxQzErUTd3U1dTdW0rSytaZnZ0MjY0N3J3cENuT2xPV25zWkkva0R0ZEdWMXNOSW1qcUFoeFF4MVNGcU84RlJlbk03OVp4a241TnZnUFdTVFJ5Rk1jSjJWVVg0NnNzWWs2Tk1GdEtRSkdWT3JmY1ZSSWhWcHlaUzE5VkdzVjd5UEtRYk9QMG1rc0pDWTE2MEU2azVnWXQzRnErVitxTGwxYUFHc2xoTy9EN3l1V3JtcnhMTVdpZ0Z0T1RoUUNLeGZsT2pHaUVXWHFzTjk3cDR5SG94SGRWcHNmcVVicW9qL0kyUXpYRjhVdUtuY2JRU1liVDFuZnAvd0FtQjNxSTkwcWY3K2FML3A0YnhmbU04c2tpbVFmRW5KZzhKRmE0cU5ZeUVsYWRKV2tCOU5zUzY5Q0lPak5CSlkyRGVvNXlUSE9ObUp5REtleWMxMnhWZzRzWURlTHB2VHFHb3ZJV3NxaHBvU2JRdmtDMUw3ZmpMRGtHenNueTE3Wnk1dngzeUZ0SHh5UTkzUVNyRWk5bjlOKzEra0ZNSWpWNlFPbWpjbm9BM0d1cXV6QmhPWTdtWnZ4cld2bXgrbmY0eXljVmVjWHZyaitON24vMUR3ejVhcDZBbDZSUDNQa3BLejJzNzlDamlsL3RGQ1k2bm5WNmsyR0JxWVkzUFlSVXNqZjRSM09qVXFOM3ZpRFM2S1d6czgveDFUWG8wWmxGTGxqYit5aHQ3VFQ4V0lSWG9UUXcxaDJicFpxSlNDaXEraThUUEZDV09VcUV5Z0VaN0JyR3hsc1p2YTVpb2k5MWlRcVJldzRXWXRDMSs4end4YzNpOTRSZUhNcGtCVU8zSmZObkVtUVo2Y25iL3dCUy9JbG1qbVZIK1YzMGtLdGt6TzNQaUs5TmtHVWhrUHdpcGdDQjVISXJZVWtYdTJ0VWlLUHM2eFdXZHZqMFVGMjYybFhqS25URVY4VlppNmU0YkZJUzdWV1dFdEUvSFd0K1ZYUFRUQ0RhU25PQ2VnTjZiWEROY1N1dHV2YzlZR1ErSlQzTmQ0eE5HZDRCMVQ3QnZzNm9PTkRiQ1pySXgrT3FETU05dTBtNWM0cXluNk16b09mK1ROREdvb1pFMHhreWsySHNpWi95NHZ2MTc1WGRRcTlyMGVtQzFrUWczd2lCTEZyWUVpbGlGaG03RUlQQjJpL1FXazZwMlF5RjZyOXRpTHZybDF0SmFhajhHMVBnV202cklRVEJnUXIyUzh6NjFzbitRV1FVUHBnSzE5d1U5WDlNVnptOTN5T2RILzV3bmVVekdHVTlwWnh2Z25GTjBQSVowTXNrUm1mT0dtZTh5YU53M1Q1bnYvNjhsNjdmMlpBLy9hdGJGRjl1K2Y0citqTThnbSs5WTdIUGJHQjBmdUFnMVVDc1JKeWROVEl6Nk0xODdQMDErYnpkb1VRMlNvMmVNczdFcHJTRCtLTk5FSDNta3JtMDFLc1JKY2p2SDEvMlZldzdPb3gzbVBWc2RGbFViL0lLL1BWZ1QvajE5RHByMWp2RjdXVWxsTXlPMDJISEpkQzlWRW1ydTB2ajFGVkVFUXVkQVFOS00vd2tiMjhrOHErVms4WHA2ZFhoT1Q5eGM4Rk45UXlaSzBIVDJ0cTdRbXBtaGs2cmRxMnJqQ2E2UGt1U0Q0aVR1M05tejRoRVY1WmxaczF3VlhGYzZIVjA0UkYvY2I0dXRLL0ZsWEY4U2ZMK3B5cTc3WHFUL3dBSStzNkZJV2NqMHR5WlpYc29RSzBFZXFnU01ldGtrYk0xNjAwN1pBZkY5NFBHQkY3UU5wblJhK0g4b0F3MjBHci9BREFMOTBrbnNLcUtSTFZraVJmalM2MXNaYXRpVjhLeUtmS3JWN0lLZE14M2xHdG1SWWVWZk0xNmY3VHdMdEMvR0lNRVo2dTcvd0QveEFCRkVBQUNBUU1CQlFRR0J3VUdCUVVBQUFBQkFnTUFCQkVTQlJNaE1VRWlNbEZoRUNOQ2NZR1JCaFJTY3FIQjBUTkRZckhoQjFOemtxTHhZN0xDNHZBZ0pEU0NsUC9hQUFnQkFRQUpQd0wwR3RnMjg4b2o0T1ZiaS9uMjZnaFJ3NFlJaWRnRWVWTEh2RC9kUjZSVExnY05PZVZFSFcrbmhRNVNLZnhvYytPS3dLa0NSUkxxa2M5QlZ6dWJXSjJBTVRrYnhjODJ6Vm9yL3dERmw0TC9BRnJiRVVYK0hhQS96cWF3djQvYWpraE1MZk1aSDRWWnZzcmFsM1lmVkM3UjVJaDFhc0o3Skh1cTFTeXNwYjAyK3o3ZGlaSnBsQzhaUGQrdFJ1QWU2ekxpbFlaNDhSenJwYm5sNzZ5azBHQ254T1B6cjZaMk5oSUVCakY1TVYzbnVyNlV3U1JYakFQZXdaS1FuenIrMUlXNjV3SG1oMExuM2x2VGo0MEUrSHBzbGNQTHFNaDZWY0FvcmF3WVIxeFJua2xTNjNUUTQxSDM4Qld6N2hBbkRVOERDdGwzRDhPa0RWQmRXcFpjblVjTEtoNFVleDBYN1huUXBmd29WSGtBNUJITlQ0aW5qVzlFTWk3TzJnOElZcHFHQ2ZmV3pkcDNzVnE0Ujd5WWFWTWdISEhsVUdsb240RDdQRGxSNVFGZjlWYWhDRXpJVTU4RFZ0UHZmWWRucjZPM0Z6ZlNhSTlRUEJuNWM2K2ljRTZjU3JpOEVnLzFHbG8wTStIR2dLWmtnTWdEeUw3TmJNRjNkTkVxbVV5TnlYZ09BT0syWEZERDlUVnRBSFBpZWRMZ1VnLytNdjhBelVmSCtWV3JTeTdzc21UaE05TXQwcXkrcmZXSldab1ZrTGhQSGo3NlhuMHBldGQ0VU1lamgxUnZzbXJXNnVKYlFIRnJiM0FUSjhlTmZReTgyZmFMd010eEpyREUrZE9PN0lQenFYdFRXN0tPSFhGYkV0Ym5hTWQ1MmpjUkI5MG5zYVFmalNGYjdaZVhXT0ZQMmtYTWozakFZZTZydUtDL1JjWFZxN2dIUDJsOFJXWTQyVE0wa3E0M1o4UFArdGJYM2hVNGszeTluNWl0c3dKNUpHVFcxNW0rN0dCUzNMNVJqaHA4ZHJCeHlyYjZMTWtJamxpRWJFZytIS2pJOEsyeXJsMHdjNU5JTmZEMnMwa016WFhxdDJjblRwNDlLajNHRDM0SVJuL1VUVzA3eG8zUFpETUNwUG1BdEo3U1JqK1pwZXVLSEhxYTJWZHllYVJmclYwQTZEdHh1TkxMVFpIbDZBU0g1aFd3YStqMzBsTjNwYmM5c0dKVDBKMVpZaXJXU1dYVmxSR21mSTFxSjZnZnlyUkJPNmFIV1FkbVVlNDFjb0xpT1p0Mlp0VEZHNjgycS9pc3A1OHZ2TGEzVkF6ZGVYS21JR2UxaW9qRkFnVDF5emFVd09lVjg2MjNiQStBbUIvbFUwczVlVll4dW9UalVlUXpXekpIOVEwdVdrVmV5RzBIOGFzUTQzdW9xamQzcjFxNzNsdGUyb2VJTXZ6SHpxQzRZK0VjVlF6bHh4S3NPVlNPVzdQQkhIREl5ZmxXMFhFY2c3VWU2enFIbWE3cjNEWXdQNFJqMEJtanQvMlVVZU5ibnFSbXBEY1dzWUgxbTBsL2FKL0VQc2tmSTFPclJMMCsybisxYlF6R2prSnE1NDZVM28vdERtaFFualoycTl6NzJLbklMTjJKRDdRcWJkYW53bTdYSFo4L09yMlZpR0dEbmxWMlRodXd1R1ppM3c2MVlXOFVUU0hVSlY5Y2ZENFZzcFBya2NCVzBBazNVZXRzRFcvanBIR3RxcExIYlNGUUxkZXdTRGpJOGExNlY0eWJtSExhQnpiRldQMTI1RVk5WGJwd2NJNVpKU0R5STZtdG5iS21PanRXcXk5b2duT082QjUxUE5DMGJ5VDNVT2pWSVcwOEZHZWxLVUZ2THIwYnYxME1uRHNmRWNhbG50SkpGWkxYNnl4d1dQVS9sVnhmWHU0bTBiNFFCVVpoejU5TTFiN21aVzAzRUREdW54SGthbGFNa0Rlc3krcjBEMmZmVEtkREwyLy9BS245S3h5OGF0b0hqRGFacDVKdEp0eDd2RHJVNGRaNGoyeXZBbXJzQS9WMUJ3T3VLdm5LNFhQYTZnY2FjL0d2dDFEWVcwYk8yaVhHWG0rOVJHUzJjcU9GUjl3NGxBSEkxRzdFK3pHZUxlVmJIanNZd2N4ZldyeEl3bnd6bXZwVmF2Y291bFBxWWxZZkU0RlhkeTBjWnhOQVpzbzBlb2ppbzRaSzg2MmxZV01MTDZtM3RMY2tvZzVGc0tlSnE2dUxsWVR3dTFYZHFoSE5TRHp5RFUyTG02bjF4aGx6cGlVOWdEdy83cU0xcEdKLzJrY0JBWlBMeXFaWk5VZVVrVGs2MDBTUVNQcWsrc3ZvWi9kNG4zMWR0TkR1bWRESis3STQ0cmJVNUc3N05yQzVYM3NkUFBQblZncXhwYmpJUGlDUE9rMElnUEhucDgvT21YVXk5blQ0anREOE0xenpWenVaY1k0eGgxY2VEQmdSVnVseEo3STNxSVBnby9sd3FGWVFoNVNTalA0VVF6NHJwUnJZUDFTVm1ZeVNUUzUxOGVEQ3JmY3hOQVpvNXB4Z09vNTQ4YW11TDJPNnNCY0lGaDBJWCt3Q2V0YkNta0NiTkVrU3RjY1h1T3NmQ3JDTVIyK3pFdXJxV1ZXMHA0eGpqeE5QRllSU29KSVJOcDFPcDVISEdvbFh0THlYMzFGYklKSXhxbFYvV09jZTBLdG1XV1ZER3QxSkppSS93NDhhMk5jNzIyUlluQ1l3MkJ6QjZpdGc3dFhQREw1eC9FUEExZW9yUU53YnlPZjBxNHRKcEd1OFdTUGJhc3lZUGQ4RDcrRmJQdk45T1czbHpjS0ZISHJ3SnJhTm0wTWFkbVRkbmU4T0JCK05Yb2t6d0IwNDRVVkdUeEpQY3FVVHJnYnhndUFHNXIrbnhvZW9sN2NYdVBvN1BqVGpuUStOZU5XMHMyaU00aWg3emU2b3JnbXl1RTlYdFJ0WWtnSzlPZkhuVWIzbDNEcUZ2QkVBOHFCanl6N0lyWXRoc3lFOTAzWmFSL2x3cjZVYk1QaXAyZWNmZzFiSnQ3cUZoMm1zNU04UHVQOEFyVU1mMXpjTENHbVhJalVIbHA2VTRKUFBQQTFCQjJVd0o0M3lXQTRZUGhnMDA0bHdKSW5RZGhjZVBoVGNZRXhOSTQwZHJqNDF0WmtQUFNGYklxWm1tbGxMUlM2U1JGcDVaNkhOYlhPenJpT2R0MWR0YjVEQWp1NDg2dUhLeDJjTUtIV2VEUnRrU2UrbzVaWlpHMU1MZVBpZmxTWXdlMk9vcTlXNWljRFQ2ak1qdVR5eDVVbDIwYk5pRzBqeDZ5UmlCeHJTYlF0Z3lwKzZiOUtJSVlka2prZlFQbjZVMUJHN0RKTmp5YjVWSzdUQmREekZzc3Z4OGZPb3Q3ZVB4a2xhcEthcGE0TVAzaWNHcTkxejdzQlJIeDNjZWRPcnp4d29SNm1QYU1hWURIN1h4cWU1V0VPR25XMWZEU0FjUXZ6cURkaWNScjJwc2J0bUhKL0E4bDhLMndZV2l3aXB1aHhVZWREWHA1bkhlcThnamN4OXFDV0hPOFE4T0htS1Z2cWNqWVdhSERZT2NZWURpdFdUekNXNFpacEkrOWtjTWVRcTBqaXU3YVBVUzR3enFPWWFyL2N1a1plTjRSbGs2WkhuWDFqUnFPbVdic3Y3NmlYUUFrRnZiUnBscDNkKzA1ejc2a2p2clBlRlhzcEplNndPRG9mb2ZLcFp0bnlmM1Y5Q1FQOEFNT0ZiY3NtYitDVFVmd3EyZGgwZVZOQStYTS9oVzBWU2VWSE1GMFYxUmEwNXB3NitWQ0U3OC84QXNSRW1PRGYxeWF5WnB5Y1NIdzhmalF5MVNZcmo4S1hHS0ZSc0l6cXVIalFmc0ZkOTJOVGZ4YUFmalVXOEgyZGVLMll5dEJidE5JRExuc0x6UEdrUnJVeHl5M0dyMWtsMDdGUUY0OE9YU3IvUWtibFhTUVowWTVnRThSajQxZHE4N3RvQmR1R3ZUcVZUanU1RlhFbTV6cldPVEdZMkk3U2crRlg3cUEycElSSWVCOGNWOUlaTm5YRnQ5amcxeXZQUEhyNDF0Rjd1NHVWSUtqaXdYeE9LalpRNjZSNHQxT0ttZC9ONllxeTkwcnpGT1dFRm5OSGFCVHdFanRuZU45cWtSaytzVzBLYXV5V0REMXNoSDhKcTFTUGVTWFVlb3k0Q21MdWs0SEo2MXd0TFp3U1I3cmdZTGdOMjFiN1M0cmFpV0N2ZE5QR29YTWFTSHdIblg3dE5SOS9kL091U0RDNG8xenErdGxmN0JuWE5FQTlPMVhRMHk3UmpoYlZvRXVNdXVkS3YxeDc2bmlhV0k0a0Z2eFVId3o1VXJ4bDFNWjNiNlM2bmhnMUx1aEF5dm1mZ3lGZFN1aC9nTlNYRWx1aUJGTXNUTUdIdkk0NTVWdEJUYWlGcDc2Y25DTnBiVERHRUhVS2NacXpJM1l4cllhZFZXS2F2REFIKzliT1MzdXg3U1FnRSs4aW5KUXU3eTRQYWtKUXFNbnFCenhTOXJSSHFkK0oxcU1Fcmp4cEZaRHdEb2VIb3RYM1dWZDFJeHFTdG43aTNlWFZDb1k5bGNkMzUrbTV0MXlCcTM0N3d6eUhuWGpHUHhKL0wwREpMWUZabWNMMjI0aFA2MWFwWm5IcW95aTZadnVzQnpxUnRIN2wyL2R0K2xjZmZ6QjhLdjNXRzZqMFhJYzhHOEQ4NnVyS0pKVnlJYlpmV3VUeFptK05DSjg4MW5PTlk1RUR6Ni9DdU5uczdTaXg1NFBMamlUNDQ1VnN0dDdISnFpbGpjYXBSajhCeXhWanBsWWR1S1VkOGRDYzB0NWZUenNaNTVYWEVhRm00RGhXTStDdG1rSWFMaTRQaDZOckpEdXJjdkc3cDJHZjdKOHEwNDJsQ3l5L2U5bHZmL1NseUVQR29HY3dKZ3pOSGpUL0Q1MXpvZWkwaGxQUDFweHA0OHg1ME9CQ24vSytQK3F1dERQckFPRkNPMUFVbUdaSisxOFI4T1ZUNmZxaExJMk9JeDErZFdyR1dlMlZpT2dKR2F3b3lHMER6RlRuaElEdzk5WEVHOEpiV0pmMm5mOW55cWUzNWZzNUY3YmZjcGREYjZSNUZsNGNTeDhheEhkSXVZTGdEbDdtSFAzVmNvVGJTYnRaQzNOT2xiVHVCRXJhdHhhRE84Ym9ENVZadkhqdW5xS2sxYTRIVWx1dkQrbm9ac2ROSm9yRGIyNHh2Sk9GYmJ0TG1LUWtpUGZZWWVYRE5iSnVzRDJyZDFsRk9xa2MxdWJjeG1yUWtlTUVvYW4wLzRpa1ZCOVlHcjlsSEx4YW9qRzdER2h2WjFyai9BSnNVbU90Y0RuZ2FOdmE3UlJUNnk2bVlKSng5bm5Vb3pjZG1WOU9BQjF4Uk82aUFDanlIS3U4NzFiNjkyZXp4b3c1R0NVMGRwZXVSNVU4Q2Fsd3J6ZDdWMDArZGJLUjdtTDFPZFhQRldRZ1JXeXBSYXVaTzFER0dWT1dvWXJhWnR5RS9ic002YW5sbElQQnVsZUZjL1JsMVRubHFka09lQlE0cmI4Mmtlek1kNS9PdGpXMSt2M01mclgwSG50WkQ3VUgvQUduOHFuMmhHdjhBeFl0NHY2MDlsYzQ2ajFiVnMrNmdSUmgzdUp0ZW9ubVFmSTBPTW5FNDVhdmFINS9IMElQald6SUpnM1YxelZsSEZqb3EwdktwMFludnVZd0R6eGs0Nlo1MWNHRzFqSWJWSTM4dkVWZHd2Y1FOQ0VGekYzVUVoWm54N1J3ZndxYTN1SVlnTi91b21WaW50TmovQU00VmMyc1BZN2hoL01WczVacHBIMTc5T1pieDh1RlN4UVM2am8zcTZsUFhGWFVxb3E2bllSZmxXd3BMbzU0UGV6ZG4vS242MUJCRU1ZV08zaENLUGw2STI0K0ZJUlZoTElQdFk0VjlKTEt3WCs3ZVFhdmxXd3A5cFRBZnRXWFFuelA2VkJaN09ROGpGQnFZZkZxMmkyOEo0ekljUHp6enE0bHV0UTB3UmtqSlkrZFAyVDErd1J5YjhqUzhSekhqNWl1Qm9VVGp3b1lvSVBXblRBbkhUbkJQTStOUjNPNHRScTB3RVlYendlOGZLcC9yOWhJb2FNdDNKVTh1cW4rVldrOHNERFVyQ1lLcW5LNHptcmtZRWpvVmk0cHFVNGJBNmZEaFJKSVVuTERBQTkzV3BtUmZhR1FWWmVhdVBPcHhHakREYXNzV3FQc29PWGhVVHJxUEE0NEdyU1NSbTVMcHJiOWxzeGRPZEU4bzNuK1FaYXZvMWM3Wm5YbFBlK3BpL3dBdkVtdG9yczIyUDdqWnNlN0dQdmM2djNta1BOMll1Zm1hUWU5dUpyajc2UHlwemtjalc3Z2hnamVXZWVTVExPNVBTa0x3NHltanZSKzd4SGxUYTBQS2FQbDhmQ3BLeFJ6NUNydU9hNWVYWEtVQUJKTC9BS1ZmYnZjdm91VlhJRTJlT0NEVnI2dHlHVFYwMWp0ZmpWb2s0aUFZUXlQZ0U4Um41VmRJcWlPV05JMUhCUkozc2VmQ3BRcmJ6V0N2TUhSb1B6RkUrVk4xcGdZaDNsWHJWZ2trYU53Z21KQ2RyaDcrZGJhRmhDWUN6dzdNRzVHZ05wT2NjVHg4Nm1iVUpDc2pQeDdYaG1vQjkvdlVmbi82QmdlZEdrQjgycVRlelhOMm1XYzRTSk1ZT1BDcnJTczB4ajZLWkdYdysxVnBadjhBZlJvMi93QlBDdG0yMy82M1AvVFZ1a2YrQkZwL0U1TldlY1NCUnBjQWsrWGpVT2ljZDAzTXVudGVIdnFlM1dYVnh0OTUyc2VPZVdQaldqZVNObmRPMk1xUC9NMGZocHdQUjFvOGhtdThEa0h3NmdqeHEwTncvTDFRNGZPdm9mdkZQVGZZSitZcllzMWlTN3NqWFVmWjFzdW5PZTZlSGpXcVdOZHpIRU5PcHAzUE0rVktSanZwbkxWc1dkdFA3eFRuRkxnMXl6eG85ajJkUzFiZkZEVzBGUW5wTXVQeHBCSW4yNG1EajhLSnpidVdpUnVRSjhxZVRkMnp5R1ZOZkdYVlcra01XOCt0Wkk5YUQzZmlLUHI3VlhUZU8yZGFueG9wTGJ5OXEydUhqN2VQeUlxOFY1cmVUQWxrR2RRSEZTZkdyZzNHNlZRWlNuRmpySmIvQUVrZktzOU01UFB6OHVIcDhLeUFudGpIQTlLN0NjbngvS2s3WFYrcDlDaGdSMmxZWkJwY1JEdndkRkg4UGg3cTB3V3FJT1dTOHJsdXByYUVzZVh3VTFjeFV4Wmp6cTRSSlVYVXNUQTVjZGVOUG1OK0E0K21SbCs2YWw3VFIrclprei90UXpTRE5BenNPOTJ0Q2o4Ni84UUFKaEFCQUFJQ0FnSUNBZ01CQVFFQUFBQUFBUUFSSVRGQlVXRnhnYUdSc2NIUjhCRGg4Zi9hQUFnQkFRQUJQeUhBTkVEbld6TVRKcHF3Nkt2b2g3RUhXb3IxeE1BdHBPYVhYekRnNnVvVm1TaUN6akZ5eXQ4ejBKWDA5ampNcHhEK0pWcVZTbG04WmxUd0hUVUx3UHVXUUcyN3IxMkc0T05Md0RmYUZUVXlYOWZjUUVQSTRncXR0WnpsbkpMbGh4bjRVN09mMGxRMUlUdktHaWFYRk95Vml1WlVXZURieVpQMUZWWW55N3JIS2RSaVIycWxBdWlyNGc4S0MxeDF5b09sL3dET2dGNlBES0RSWHdZa3hNNVNXN2syNGZIeEJEZW9YaHZ4ZGV5V3R1MUw0M0JmY2EvbGJBMWdNZFNvQjRGditJOEN1RnBZUTZUbWFkRjl6dDQ2aUN1OGNFcXV0VnFDd2l6TXdKSmhnYVRoOGtmL0FGWEdrZFUzVzl6TGpLbTVWVlpRU1M5dXVqa1BlNVI5VFJHUHV6TElLVjlSdFhERkJucjh3Y1dYNFRWamhubVc4a0wvQU1QUDhTcGRSbE9pZWlhR08yazJyZzQvNFVsWVBmZThBWEFxVUV0MERieEZiY3N5UEFZL1U4L2NyTnU4N3cwWDdUallrZjZOaXpHVnFDSTNaRmxNdnpHdGN2d0piMXVtb0JVbUJ4WXhCT2FQL2ErMk9tVlZFNWVkT0g4eWtuZzlRWXlENmxCc3BIaW1jcHNyUGsrWnQyR3NRY3JSMjNaTUo5UkxxR0hKN2hCZFhvWTdzMjc4YVpTSGxjU2pXK0lJd0RHR2ZJNkhENG55NDMvZFNsTnZOSC9Nb1lpcndOcU9abzl0RlZwRmdVcjJDWUhMc2hwSUxVUUNXVW5pQ3ZiT1M3SkVWOHN2TDdnSFd0L3F3SytzdW5BRVY1WjB0ZzQzOVJIUGlZZ2tnZkN3NXVGNjFmaGN4WnErZUF5aXVqQ3BWWjA0cUVCRzFHNHdtbnpGcmJaaDhwSHE0THFodFN6SlVzVVRhYlBTWmJKQkxtd2NWM3J6RVdkdlNJMUd1K213NHFmQlJnYjJGeTJ6YkNmT0hPblV2T3NJdjk3Q3JsajN5b0g0dExnekQ3TlhLQzRLOFJ6b3RITFZZenB0b0p5Y0N1SlRIT05jNE5NMkQzRDJVZWorSS8wVmZicTg5WDNOY0w3TllPNk5IekFid3pxVjRRTVh5RUJaWDFDWFF3dGtEUEQ5SzF4K2VwbDVvazlybnBFQnI0VVMrcGlzNVJmbCtqTndEektxN1V1VzFQS3Q4T1Y5c3MvQXNueEhQUHpLSHZlMmhWdmxiL0VDSG91cXJ4UjNNZUgrTUNtbFc2dUxiMGQxMVFMcjI4dzY3TWlIRWNSQTQzQS94MHZXQUZNWVlhZExTQ2FtNkdYMzNIVlowcmJkVnpGQ091MVlzN25hck54TnlCTHVHd0xuck56Z2xzbHZBY1UvS1k2WDY0M3c5RFViQjJxQ1J6V0xHZXBXR2xTdWk1MWZoR0l3NXRReVVlRXQzTTJGZ2JMU3YrOEdMZFVZdlIrS2FaRzFLZ2NMSzdPdWk5Lyt6Qm5hdDMwbnMzVGlpL01TTVhMRG9hR085ekJiWThWanlqSkptR3RmRUFSK0RtL1o5a3BFZ2xWelJmT283QS8yT1pMZWF1YWlHQTd3dEV0ZFltZzdtaEZYVnZETkRLcUU2b0FERGRNc3E4aG5hQjFPSXRLZFU3SEt0VnErVUYrVGRsZGhDL0RxRG15emxtNi8ybVhzWSt3ZFRGZ09sVFBIUnpTS0Yxak5ITXI5T2I3czhJY29qUU95TkZkcXU5KzRwVDN2UyswWjRRTnpaUDhBOU5HQU5oamJLamVBcWFTRk9SckV5N203VjlFTWVqbHVXc05YQ2U2dEdhaXpXWUJWNlNtdnE3OVJsQWIyZkI4WHVKeUl6Y0pRVjcwUWZrNVd0c0xHZDFvWXIvV0xjQTM5dzZLM1BjdFB0WU1SY0JiY3JUNXFianZ4R1QvNm5BM1lLOFdhS3d4QzAvQTJxOEZidnE1ZG1wRTZxemx3eTRvN1R2NDVDNTdoenlqNmNhLzg0Zzd6RWxhV25HTGZnZGs0M2NYbm1nQStiWStSSWVobzFYb3VTVlNGUjJwelZjWmpZN3M0UEp6TjBSUEdlVDVVRVpkQitEMk9JbUdPNGMzY0pZbVhSTzVSa0R2dE1wVnRnNGdPaCtaTDI1K0pYVFNjN09XRkZ3OU16NEpYQVBGNHQxT2RSMDd2cWllRkR0K1FNYUpzcXI1YnZoUk1xeHEyTXBuVmtXNFZtM2d3NnhQVGoyWGZrREVoeTZMSDhpcHZEVVYwR3FiSkljazhSTy96eUpua054dWVHdG93WHNWVlhtNVVzK0prcWoyTDR4Q3dUODc4bDg5UGRFdk5NdGJITFJsRGJHeGJzbGQrSVBzUGRCYVdxVHNqVWV4cFFHVHlUTTNWTTVhVnJrNzZaanJTS3NleGwxZzBXUWl6Vm1NSHIzR2xBSDdsbmRuWGx5N1hWOTNDM1JUbDZ0MTN3SlE2RzNiOFhuNTVsSXYwZjhpUTYxdnFYVTdYOEZrZTc3SUl4aGRMVzZodzRpbW9yQjAwdjVtN2ZZWUI0UkpmdUp2dlFzdHBWUG1EVjVaa0lmcW1vc1p4S3J6ZCs0bXVpc3Z4T3pNUUdYV1ErUUR1VjdnUnM3Wm5neHhEcFRGMkZHSEtYWXZVMlVLQlJhMHJiWGdsRnNBTlU3QnB2ek01SkovYkdHc2lhblV3VkR4U2JTcWw3K0VnVWVZZnByWDBnVmxmNUQvd3Y5WTh3TTI0N2d0dDVodzl4aXZsS2xHRy9jOVJXMTdKUytmWlRIUjh2Y0hJMVlDWUd0K29jeEM2b1MrWW81NVRHUjVOVHE0dEg2WDd3bUUwRGtGM2JVY1JwRGhpL3dDY0Y3VzZoc2N2cm0xSUFUa0J6VXhjcnlXTkxBaGhkM0ZTbm5RTlFacW1KdXRFWjJZbWpqTU0rQVMxb0xWUjlvOXpRdXhMU1l2K21ndjlDVzdKeHVTS3dRVlVoemZjQlZpK0hxcmhpY3hya3ZibmNMQXR4MUhLZXN3VFVQRi9NVDdrRHhxY29JNHJxZTkydjVUS2hkam92UXg3VEtjU3Y4QXdRTFAwbTFpMFZEZXR3eS9xNHN0K0cwc2NxbFlJdlRlUGxFT1QyaEVSSTNSYjVyVStwYXMwYkFtNU94bkkxQlRXRENDWmhNRHpGdXgwNVU5VEExREZiN1VzemNCcDU1YXZBd1pVNXdDZTBmeWxTQXY4L0hKQitqcXc3YTdWRmN6SDluQngzamczU05ER3lhL1QwK0hNYnIweXBtMUcxYnN2aE9ZVnV4eUgwRG10aThrdmJQcU8xdmNRbGlGbGd1TkNGaGYvQU51RUp6ZHhHZ1FITGNEVGhJY1pCTSswWmFWWUtzNFQ0TVltbzdQQVc4bkhpYUZGVnJWM0tQekdKMGV6d2RueXE1NHkxTUdSazB1NGRXRGJwZER4R2tMTGN5RExlRW9QTnNXbHFLSVdGREJ5WjNPU0Zob3RpNWMzekxpdmI0RTBad1JqbGVVbEg4NG1JZEFLemF2NGYzQkR0cVpmanU4WG50MlJQdXJkc29ZWG9sWHlRUXY4ZzB5cUNIRENiTDhzWWdrNGt2N3dsblZNcFFLaHdva0pudXI1SDFDVm5HbzJYc29WYzFDU2NJOGEzSThuMUFGTGtZckI4QmhydHhnSCswU2VmOWxndWZkeEk0NE1lMFFDcjdxSUQ1c3p4cHVWQTU2Vno0WWVhNjIrQ3N5MURRZG54ZjhBUVcrUFVjbVRIMHVsOVdZOXp4WDRHVjRoNTRsYnNpMStSeFV2cHZmMGY3aG1RRjU1WllOZ3JCUTZtUlpKcWh2blgvMmJIK1VWdktoOXdpbEovRkEzOVIvK0JzQnFHVSszOVJjellwM3BVL1pIMCtNQ0ZjQWM3aDFTTHBib0Q2RXJaemdtbVkzV3dHbWNmcWdadFZUSjNXT3BpdWRGdGNEbitXVjdUdDFYUStvOHFPL0grcVVDU2xxRjl5a1NXM2UzWjdHR1hQMkpLY0Q0UzE1Sm5zRFl4TjU3enIzT0dKTDArNHYxVEIyNnY0WXRtdHlPK3Y4QWJpUEhhbEh5M3hMWnVVZk1WTnI2UXZhbTAyRFpYd3lxT1dYUjhrdlZjTEQ4WG5ZbWh2OEF3MGhid0pTL2lpNFZpd0V2b2dnV0Z5aDU0L2NOTnM1dFIxeGtKZVRob2FqbytjUEVQVTRmeE1yeU9LWm1KYUdXKzlUalRpbkY5RVdQRTJ0NHBIMXJSR2ZFY0lMU2o1VVFqZDFGQm9qNm9QSE1LNVdMME5DMzIxbW5OUWl5QkZVbFlSS2U3ck1vcUJaYk9CWWN3dDV1WUh1cmtOdnN1THcwd2FyMFdMbEJFeVUrbGZ6TkNpb0pQVzN0dGxiRy9FMExOT2VTWVFyd1poRzdlWDc0SlFvZDVwKzM0R05xd3RtNzJ2eEg5dDZ1K2ZVYmdWVmt0S3hSdGdwMEZoRUFOTG5IU0dkdU44SDRQVW9KYWx3ZnNHV2lvajl5eHBhK0NvdDFBaWVVMmxWOW1PSUhraXk3OWIxV1BFNW5qazNuaU5jS014M2hIdVJIWlN0NXlWekEwcWJtM0NHanV6bmdqZWtOVWkxb3JRajZqcDRTWTJLNDlUaXVPaW8zR002a0hDZitDcnpHcjlzcS9CQ1czbkg0UDFoQ2tJVnV2WjlsVTZOTkE2V3Y3VGZBaVQ4ME1rTC9BS3VQcWJtWDNMMUpCbGpXRGtlNWsrRTRKeU9kNU14WEZzWkU1dVZXTjVaZU96d3hpemxhWW93SzNjdTlyMnpPUE5DUXJScnIzQTQ0L2ZwNFZrVDVnSW5EeW1sUTRzREtadzZCUXQ0WDFMUFFPc2wraW8zNGxnT29WNlF2Z01rdHhXbVRCZFRnc2V2QXlnSm1uUExMZnVxTXVsb2xLR3VwVlRCWTB4ZmxGWks2bEJzZ3Z5T2VYbUxLVkZaUEw2aUZxeGR6RHlnQkVCYXRNNVlzMm5CczBmaVg2OUhmUk52cVdsaTkyWm1qWjI5UW01V1g4dXQ5STdWUG42UVAzTjluWDlyZnFYUlQxdWNCUitFeFBRVW5VYVA3UjhvdE8xSmpGcTZYeEx5VnJlZnliY0pUcWc1ckp6aUUvcEEzbVpjTHJ3RjZyWEFXNTZoVnE0R0JtNnQ1NmhJV08xZy9CT2FDNGY3R1hDc05Fd3Rpekt0SnJqQXpxM25BNXh4Q3RheThlNVZUTkdHbmlack9pRkxLQW9TZmhpUXE5UDZZa2Noblg0UmVmRi8ydytvL2FrbXhxVzJyanpCUHZ0NXZwY1RVWGRCbjJocjVPOXZ2SHgzNGh3dXFSbXJQYUtzeGl5QklYY2VwdTJFdmNlTDZxbGNoMER6RFRjNWRnc0FIS2xoSEY4ci9BTSsyL2NkcHMreGgyWVp4d0doZC9wM0JDdE01a21iZlBjd0tuOU1FWmlJdDIrUTMvcDZxV2dJYUthdFQzTmhvSDV2L0FKUDlqTHFlaDlOVkFJSWQxY0xsdXJ3VFpYektRYU55OTJEY2JKZklUTU5jQXlQTzhQY05WeWR5NXgvVmdBVG4rMXMvTksrQ2YvL0VBQ1VRQVFFQUF3QUNBd0FEQVFFQUF3QUFBQUVSQUNFeFFWRmhjWUdSb2RHeHdSRGg4Zi9hQUFnQkFRQUJQeEEvQUZnZU41eDM2cGdUeUM5Y1ZMbjNaRHI1QWNTdk5mNGdsMlNwNXJpbDJicTBoR3RVSHdCbE00VXpiVGplNEdrVFprTHlQT0M4Z3dGWWgvb3lGZVVMYnNYNW1FRE5YL1dKVGVNRkRBVjA0WTU2SmgxQ0duZ2hLTU9Lb0syd2o3L21OZllmcmxXVU1EOVpGZWJyditHTzhmYjVOekl6QWVXQm5UU2hLU0hka1JCR0srTDRLWGovQUxoUkNjWThGZ28rOFJEZ3ArTlhPb2V5Uk9IdE1Cd1VMa2x5SGRvY0graG1tS1NxUk5ncGpiN1FVM3VGdGhrMFgyNFE2dU5UZ2JxWUlzUFJTb2ZGenptRWVUR3oyNGJjQ1lnQkZ3UStSM0RyR3dBdHFORU5DYVF5c0hhbkkzV0xRcW9qaW5VOGdZRGFiT3NkT29XWnZkZFRCYnFOTFVLeGdpUE5NR2lxQlprUC93QVQxaTMzUUFhUG94VjhLRTdpWjBmSVhKa2VzYTVkbjRnNHpQSURZaG5OQ0lBZVROVWdYVnJ1aHhYWm5ocmx2QXdYU0wyN2NKbUtqSncvNzhjVEpnRzhRY25aVVk4Yk8rc0NRTzFJR1FBQ083eHhtSFg1Z0ZtendYR1VvZWhoS3hHa1R2STRSTU96cGFNTEZZNnVDQTFxaFd3UEdSeDB2ZTZYVFdnQTNDeXRta0FEUTlIL0FKaXUxZ0FnMkJDcTQxNTJYL0ZqQ2lqT0JkZFFDK01vdkRVejN1RkhvUUtZbEVrSTE0akU1dnU0Q3R3a3JxN0RFMmhHdUd1cDhmOEF6QSt0S25RRDkvcFROdUhNM3hJS0JMR09HYWRseVRKajdFSVREWDFrZHloeDFacDBBbkQ2QWJ4YWd4U0ZRQTZXNnNaUjZpYVMrQ0ZPcUJpLytlb3VtYXJwdDBNbjNjSklhcTZFd1FERWNXZnVzVFczQ2gxYWNjaERIdjhBSjhjYmZJalg4dVdEcHVGOFRtSXl4d1dYYnJ0N1lOUERqZTVFRTgwcVM5NUR1a0lDeVVhanpyWjB4dmFBbGZJdktrd0xvNmN0VUJJUjhWZ1grNTljZWw0U0Rnc3RTdkNRL2laQ2pzZytHUUNrQ2dKNEw1d1NudjJwcUg0dVJEUXVqODZhZkk1UTdCQVMrRWZTWWRHN2t2M0R2ejh6VDFNVFRZNHlnMUJ4VXFNcTlqRW1qUTZZQW9Rd2lFb25VbFVDSlNpRDhZUFMrV1ZrMUE5dEpOMkdMMGE1aUoxTVFoeEpqbTZ2cERDd1h0dFhGdElJTlA4QTRlVHpoZVRrcmNnbm5OcFlGdzFHc2Y1akZtNXVET3lEV3hsVGpsc1ozTG5Qa01Uemc5NDF3N0tDUW5sYWtNUXlRNng2cjB6c1V5U2lRcE9FWHQwOXhvMkVicE1uQUFYc0Z5bjhqeVRQYlk4M0dGQjNRM2lKOWlhanE1c3A0RnFhakpwY01aMmxKTTFmZmZFNk8vd1BLSXE3TVZnNUpidldLMFFFZzQ0TmNwVXI4aXNabnJoa0IxZlZuQU9mYlpodW16VCsvd0RtR1oyTUVRNGFrR0RtZ3Q2UGVDMFE3QnZ2SmJSVTZnZzJHTHpyRm92U2RoVzh0bUV4bU9sYTZBMnZZNWRLUHcyaHAzbzk3Qmd3TWFJeWxpU0ZXc1Z3aUdVV3NGR1VwNGFCZ3lZN3A0bUd6bmZveDZiVXAyc01VVlNweGljN3RFM2NrZGtyVGJCR1JyRWxNcTRsUXc1QmloU2dtTG4xQUFIQ1JYbEc0R2FDVVhCeFFUY1FQZ2hJbEpqMUJXaSswd0xFSWdCU1o4WU9JTXFQZW9IQktUNzcrQ0s4T3FpU0U1M0tkVmNKNklPSnB0ZTQxSWRpRUZPZ2hzOFlCTldvOFcvc3N4RzJNV25kcnpXQjVOSk4rTTNGMkFlWmcxYVk3VVNQZ2ZUalVEb0xVMEdndmpOcXNsQW40dmcvTWk3Z0dNcXB5NWhBMGh2cWhUYTIxeHB1dnVIYnJVU3EzaG5iMzh0NmJ5QlU0cm1pS3pjNW5nV3FYTEd0SzFUZjRvNFRDS3NzaEJTR213WEVLUUphTlZlVUlxWVUrTTFQNGFpV0RzSXplTVBDdGN5N1lCQXFnWU1iQXFxcVd6Q1NCd3o0ZkRvcWkzMkh4akg4a2FRNDFWWGRQY1U0U0U5QmNpZUVwV0ppVkc2b293RHdFL2t4amF0cWVydkR4WVJlaFFRN0FsMmRjVmxxYmIrdWRDZzNhRmo1TUV3dmdNV2FRTEc5MnVSNVUrRXYxam1RSkNjenVKTVNRcEFOZ0pSY1JNT25UYThtaERUdkcxMWlpbHkzalRFdjJocllkUWtCZkZvR0h6V1MyUXBMaFBUVkF4MDBSMlVJakppS3pzNW13dnN2MDRmK214MWM0Q0dwdnZGNnhkUm1VZW9JdDRHTXpSWUJOY0loczdzZ29BWXpwbDVzdERRdWMzYnRnZ2ZDYVA4QUxBdnpENXdpeXlkQW13ckpBcmMzRUFyMGlBQmltU2QxS1RRNUFkTVMySm1aUXBIZng5RGpycmk0bDJIcTJjYk1hM0lKRlFJRFpxZU1JTUd2dld2dEkrTXJ6Y1FlSEhxQzVMSDV4WlZxUU1IQk9KZGZMay8xSmVLazc0dWEySVhTYUJxRUNqZXVNMktDVUxBTVBsSlRRdUdMSnZCYThab0Z3NDRhVkFzT29veG1lV0pINTlGTWM5aE42MlNpZE1Bdm1BeHZYTnNUZmpXRFhMdlN4OUpWT2piY3VFY0I1aHNHL2FKaSs2TXZRYVdQQ0lUZ1l2OEE2bEJJZ1M3R1M1NUpJdzZZRFk4d0FzemhPQmdFTnhrNFJLUTAzWUZFYUpZZVh0dXZnUTZDaCtGbHhmRmwvV2ZVTGdxVDFyanROb2pxeHg2Y3lCdlNUcVBNVVNZdStLdnlBbUVYUHJxKzVuVit5bXFZVEgvY0R6Z2NaRW14RGVFaFg2TlBselZYY2ZuL0FQV2JqWlZXditTLzVqQlZSeW9ZVWZHYUdRSEl4amVyNXUzczM4eVQyRURidFI2Qzk2cmVOdktYWUJqdU9wTDBMOFlFSEVBQkZ0aUFUL01NdnRCU0ZUNFBzZlpveHhBQTcxWFZNaFk5cnJibXlIbCtRSDVXak9OUXpKOFVBSWZDeGVVTm9vOW9ZVURZb1h3STE2T0tKZ0N1UEwyNSsrNzVNWllFVVFOSklKTWFCY1A5OWk4UzFTT2VOdzk3WHVKTW9LR3ROd2tjK0ZwQ3NkbHpVeDNabWlBUEpRWmJWS1pyUm1ZaUtKdXNQa015bmphL0g4NG83SEJzd1dYMnpVd3ZCSlhFYXhKaytQdkdHUExJTnUrRDY0bVh0d2U2OVp5KzFlY0h1Z0h1Y2F5YVJhd3pkY2pweWxGSURoQ3FMbTdxSUR2K1F2MU1maTZPN0d3dzlPR3JSK1pHRUFhbk1vR0R2bU1WSnZnTzRzMTBvUkNTN1dzRmhIVnAzN24vQURBd3FEMUtJSzJlVDR5dTJTMjRpaUJTaFNvbUYrdUtSM25vYmFhOERLK0xoZmxCRk9vaTNaT29sR25mTDFjOHpOUWx2OUlOc0tMU3NNTEszQVFYVDloQlFEamN3RzJla2gwVmhsZTdTSVFoWXNEc3dKbW82Y2hTK3RZdjdQN0toRFFKYVpFY3BXcnZMMG5HNGxTZThhTHdBRkdIOG9PaXR3Vmc2VGc0WkNjVlplNVlUZElQV1FqdjIyQk9IbFNZOHlkRjAyaDVyakU1UGFDS0g5WU1sYWxQZDVvUWs2a1BHTm5SOWJCMmo4T0pEbEkxbFlreTNCbmhENndMeFBQYXNEUEVJVENtK2FocEVGUmR0cmJsTVZwbVRTUithQ08zSDBQNUVTaHNFTFdDWkhWdUN0b2Fvclp3U2NFMnFwYXVlWldJZVExV2FIU2hZWFllY1d1d0NkZE45WnhHUkw2Ym5kbXgrbVNxUHRTS3NrQVNjSDRGbmpSMDJhUGNlWUtzZ282aE4wZ0IwbUtBQUMrcmdteTE3TUVLWmcwK003MGI1V0l2OUlITTc1UE1aNDhIckJPdklmd0IralRLaGhCMlZmeTROUEIwTTdVYUhTQS9seFdQelk5QnlCeDU3REZDVFJBSUdCSTZ4Y1o3MWZzczdqc0QydUQzdXF5Y3djZW5kd3d1NXRBckx1RFowR1RaYnpRVStVaFlVNCtaQXFwMmU0ZkRHd3AraWlXMkRWcGZZeGFjU3lRbWwxaG9lMGRhWDR3cEJLS0FFYUdWb0RuVXFCRERVeExTTHN3ZVFTdnZiazVEYlV6WitrY1RxaTBwdkFiRXhSbHFnRzkwdzBvMjR2UE9YUFl0OFpHQnRIcWIvYUg4NXA3WEc4dktCR0x2WmpZNGZ0UG5Dd1VNL3ZBZEFoR21jZDBlQ3hSOFBCRnhpTld5YW1sNndjYWlLcHhFL3dDNG5kNExONU9FejlRRGJ2RUV0ZFRlV0kxK21aZm9nbndwNXdDcEhDSWJWVUV6RmEyUEk2c3ZlSzh2a29BSFdPTDNEaDlmTVVYM3JFM3VEZWdMemJlYlhrNGJYRngwbkdyQ2FSSmhEK3IxWnJCRjh0U0J0bG8rZ05sdlV3SGtZdHlITllnVkFvZHBoaFVlQ0N4U0E4NWZzeUJDRFNyb0w4QmZKamt1QjBManl5blJhM1JzN2h5NUJWMmlzQmRCN1U0anVwd1dVNGl5a3czUHVocDdYZnRHUFR0RkwraFlLYTNXS1BUVytybFRTRy92eVlPc0hxZWxoSzAxTWNZTGFwRzR0OU1XeXpFM3VQeHBqK3dCQ2xzUWRLT0RSMlQyRU1GVlVvVnJDSUZyQ3RvUm9nSlJXQnZISmQ2NkI4MVFYSGZjRHlUdytEVERLOHhvVUYvbHhLSWphU2NNRVBjeDJ3eEk4ck9HUjFNdURIelZSQlNWdHNKd29Ya1lscmE2ekZQeXZybHhaT21iSHNKRElWOHhzL2xqbWZEcUw1VlY0MFRFRVRNUFluL0xoTWRXNWFuMzZ5TmJVaU1iaHpBT2tvS3RQWHJIaXJET3l0Uk9HR2xzVDRSUVlmcGdGWmdzeDhmOHN4UmE2V1gyZy9jRCsydUlnLzNuQmlOUjNEWldnNnk2MnBsV3F5TXQ4WjFzc2orQUZmUTQwSVVrOHF5b1d6eUgyUFRDTDJOeC9yWDhZQm41by9nWlRUZ0RjZk9MTDJVcGxXcEVRVlBPR1F6Q2RQNXJVVmd3TTgyTDdpaW1HVGZIZytXWHUzSWs2TUNNcm5sdGloT0xvZ3hFZUxLSzJKQ2VoSVRSaUJIdlc4QUpvSXZsMWtsL2t0WTlrSUZRTTZ5Ynh6K2U4aVl3TTRiZGRCVmIyaWVYSTR3a3NML09PWFdJbFQyOHdsV2pYWCtHVGw2SVlmYmovT0JFUmY0ZjBmakpCWXFKeDZzdm96VUdNNzZyaTVVTjJzQWcwVk54bHhzZGlqMUhRbWlYZVY5OERrb0k5S241bHdPOWR0ZUlob0d6TDE4U01SOVArWm9vU0xRbnVacDhrMVFQb01wREVpRXpTMzBvOVNhajV3TTNGTDd1b0Z2b0QrQ3JobVloeldHMnpyZHFBNVVvQWVRUUpwb1ZRQThFSmNzZ0wwWFN4UVhaTTduS1EwTUZuRDA0S21velZxTkVZZ1VEQ1psTlZPKzRYdmdNU3c1c3NxeXV5cjdjYzFZdkFHS2hJZE01ZzVnUyt2a3IrR0JDQUU2KzNYNGMyOVZobjRnUllXK0dBZkZKUDZlTzZ1cEdyMVgvQUZ3S1JjU244NmZtS0xEOFRSOWVzV1l2UnBnb0h0d0RRSnNSS0pqMWlhZEFKclJBaWNBY254NXh2WWE5bmpEbmRHeGd5SFE4NE8zWncvM0QyK0lHS0JXME5IN2l5MkIrdXNBdEpnZ2NVeGFFR2xwaTZMUVVENkh2TjdoMnE2aEhvdU13Znk3bS9QQjUwd2NVNmFFVWlMUGxEajhHRktCRnRoREtMdkw1b0FqOFpLd3QxelRyZWIxNHRTUkRhQU5zSFV4NmxHYjdtanVzbWk0c2E3NXk0V0dhWWx4MFJ1b3czYVRDK0FPTjJia1llQzh2NDVvUHpRdEgwR2pFY1hESGEvOEF3S1JPMDBwN0R6aVE4TjEwZnhpd09ZZWhHaWNQN2NJT21ITm9ST2FBZXpsRFpVTEowSUNobGNGREJHUStWZGx4NGIxa3Y3ejlSbDEydHFVSHkzNWsreHcxYXdOT21MbmdPSHpvZFZWQ0MyQTk4dUtzdDNyeWdKOFdLdE1FVFdNME9pR3llQTE2QjZkNFM3eFV0WjVES3Y0NXJaekxhV1FQTGg4cjVSMDdLUXBXRWR5aVhaZTlTMWdRY0hFMVR0NGlZbVAyd1hPS2VTU3JZRkRHN3V2WjBRYk5vWWxZeDZFekJFYitzMHY4L0czUjRENHdZNnk5OStPYmpoSWVybmpBU0JHZ0ZBeTc2ZWMvN0J4ZjQ4RC9BQ1Qrc3RSNkVENXFQM0RzS0pJNjRVSlhFWlRCc0lEbzNQZ2VKa0d2ajhrWDR1QXNOWmI2VTV5RFdMcEtWUTRwY0FwNzNkZk5LaEk2Ti9KOGRCdU5xNEM1S1JvOVo4UUxFbUt1U0NPbk9LYVVJcGNWcTBMbkhFL3grTnFGTlZWWDE4a01WOE51cDdCOUViN1VPRzRjTUN2MCtEMFk3Z0VvS3B2Tk1qRW1laEltRnJIYTBsVktrNjE1Yk9sbXhYdUhDUU1WNFRDNzFtclVJazExUTlEZUZML3Fkcy9vYjhZZXVTUUpkTWFza09Od2h5SWJvMitFUnpUYzFKc282T0Z0TS9XRHpPbHU3NHh5Qzczbk1OS1FDMHJ4TUcwNVZUMCs4RDVnVmVaemV6NEZ4YWs3YWhKdWo5UVBlZi9FQURBUkFBSUNBUU1DQlFNREF3VUFBQUFBQUFFQ0FCRURFaUV4QkVFUUV5SlJjVEpob1lHUjRTT3gwUlFnUWxMQi85b0FDQUVDQVFFL0FQRVljN0g3UTRoNWVnUk1MSXBIYU15b2FtRWpJU0k2VXNmdk9ZTDRFVEdGNW1nZHpENVlIRWNZNHJNaDl4TU9RT0s3am1iR0JSTW4xR0l1czFHeG9pOFhGT01xVFVWMVBDeWhLRUFXSU4rOFV3bjBHT3VyY1JHWk9JcnRmcWpHKzgyRXhLT1lJWXhxWHY0S2FNeFpBNDNJRnhjWVRjR1p0bU14Z2x3RmdSeGR4a0lzM3RGYkZYTUtuM2dVd0tFRkF3Q2hNemxRUXZNSFVaa0JWVFFtSjJaQ1RLbVd3SUJVVmJpYkNheENZOXdUZnd4T1VNeFpGY1ZxQlB0T3BXbXVZMktPR0hhWjhtUW5uYVlXQWFtNE15WW1ScC9xRkNudVpqNndyZXNROWF0YkF4dXNkaHNJL1VaV05tSHBjN2dzRjJtTmRLRUdMdkZ3UG1ORHRFNkE5NC9TakVwWVZMb1RWQVlhSWhTcFhnQk9uTG51SjFTaWdZS3FFSXdvd1kxdW9iVktHOFFXZDRVQ2lMZ3pQd3BoNmZLZ3NpcDVaN21ZdXB3YU5CUGFJRUZnamNHRW9JTXFLdTFUem43Unk3clRTdTNneG9WQnRDKzBKZ1dBVk1DMy93QUwrWjFXTjNVUk1Ld0tvSEVDZ1hNcHgzc1poeTAvcU8zZVlrUk4xWG4zTVk2YkpGL0pqNVd5TWEyL250R0JYZmVZM28xSElEZW4rSnFGUlNDdllRNUsyTTh5MXFhdTh1NVFPeG5rbWdJTUlKbmxnTERYaGhDOTdNeEZTS2pLY2VRckZPODgzcE1ZclZmd0k3b3g5STIrOHhkTXB4TUNhbUpyWGkva3pPM29xZ1B0Q1FOb1NHeGVtTDdRV2RvVmJUcUV4S1JkQ1BqWXhjVkNPcFJ2QUdvdVZlOE9kS3BZMlMvSENMVG00TTY0alptYnFCa0lOVlU4MktFWWM5NmlkT0hKQTNoUzVnVmQrVE9xcEsvdDNnVU52Y3JSdURPZDRDeWsxQmtRckVXckpCaUJmYUFFM1U2bkVVRyswdXgvc0hnZzFOVWJKU1VEejdRSTc3eXNJNzNMeCswWFQyTURzcTZlMGQ2blRBbER6T3NRNmJxSWlCTE1BWHNJeUVNQUl1UCtwUkZ3ZE9qT1c0M1A1aTQ4V0piUDVqNU5WbVkzTG5TZHYxMm1ZTGtUa1g3L0FHRXE1ZmlJSW9vUlJaTHRIWXQ0aUxMODFxVVJGQ3JXL3dDOGJHY3FFQ3YxL3ZNYnF0cVo1bU5XdFJQTkpuVG9XSk5YRG5YRTVBRzh6NURtYzNFMUthaHNPRFZ4eXhYY2o0RTZqQVg5WStUOXZ0RDk1b1B6TkxlMEFtUEdXUEcwbzZxam50S0poSGdCNGRNK0t2VHp4ODkveE4ySDhSeTZDeTB6WWRlNDU4RVV0dk1HSVZWUldTeUFMKy84ek9neFBxQUJCL0V4bnpONjRucEpxVUtvVkNhV28rUEhrTy91TC9pSHB6VThoaDN2ajgvNGk0TWFOdnZ2KzRteTR5S3ZhdjBpR3JQaVpRZ01VUk1qNHN0cnNZakIwMUFiZmVPb0J1N21YSmRnUTZySm5UNVYrbnYyL3dEWVdkQnNZUTJRN21KZ3lJMzJtVEFtUlpveDRPOFJreUNoRExzR290MVI4QlVZMmtISGdBQUl6UlRVSkV1T0VJblNzTkpGWDg4VE9UNWR6NlYrWmpYbmVNQUdOVEU0YkhSb2YzbU9nWml5QU5wbEFtWmliNGk2bDQ3Uy9UTWFhR053K0FqZlRMZ2xnaUFFRXdwUmk0eVRGeDdRWXhSbUpmNmhGVHFWQXhjUnR6dEJyeG01ZSswd0VxMndtNWFybEZlSXBhdG95c0RabkNtRE5RcGhQTVE5eDRVUGVVWlJxR0R3V3g4U2hBbExOaHRBNVpaMC9KTTZxamltSkNRYWd4QWNtTGlBRndBWHhNUU53Z0dJTEgyakdsM2d5b1B0QzRHNTNoVEcrNVg5dG9lbVRrR293ZE9HdURON3daUjMyajBkN2llRmtUV1lITUJKaUpVdlFLSE04OWxzSDhRT0ZCQWlaZDZNc08xM0ZBWm90aHFVUmxjRWhtQStQOG1JaXA5L25lTXRpRUl2TU9aQnhHNmtnY3p6WHliS0xpOUxtZjZqVUhTNGs1M004dFBhUGpIYUgzZ0k4VjhFT1ZrdWF5SUVEcWI1aW9HOUpoUmxPOFVFN1RFb2lrb2JITXRtYmVOa2JGNmJ1Wk9wSkhNRHUreWk0dlM1bTNZMUU2VEJqNTMrWUtVVUJBR2JZUmNKbmxvSTFUSmpKTmlWTDhSTENyVVZDMFhHeElKaFRROFJBNS9iOFFkT2dFWUFHaEVBSk53NC9TU29oVkxPb3dlUXZDMzg3d1oxWmFxSWRZNWk0eFUwZ1M1WklqTUpaYUJQZVpjSUgwOW8yTWptYUlGTUNSbUZiekZrQ2d4YzZCcnFhaTczTUsyU1lPSVZ2SVlwR0lXWmw2aG0yRUNNUlpoUlArMEZnYkc0bVVORnpIVFBOMjNZVEc2dXR6c2FqUU1SQmxZY3daVmFNdW9HSEhkd1lMdUREZGlJb2NXWlNveDB6SGdEaXhNV0JRT1lvcUNENjJtZGxYSFVRQVd4aFlzZDRlSVJaaXpFOTdIbUFZeS9xRXhFQUhTSStaaHpBd2F4NElKUU1DZ0RWNExvUkxNWjI3YlQvOFFBS0JFQUFnSUNBZ0VEQkFNQkFRQUFBQUFBQUFFQ0VRTWhFakVRRXlKQkJESlJZU0JDY1NPQi85b0FDQUVEQVFFL0FFNlJhT1NPY0VoU2ZMa09mTmtZT1NKeGNFUWV5QzhXa1NsWkdYNElxZjVJOC84QVRpcFg4TW5EaGZ3am9zeE8wTjBySXpsTDVIekZIOWk4dnpqa29xaVVZeTB4d2lsb2hvdGs1UG9SRWloZUdyUk9IRjZUWTVYMlliNGtxVUhaeWhXaUU3VkZTL0FtaXkvRUZiMkxGQnUydGtvcFMxNHgrTEpkaWpRa3lIOEp4VWtUZzRxNnBHQiswa2xLTFJpaENySnAxYUlUVWtMRTdIZ3Y3V0w2Wi9MRmdTN1pERENLSTVjYTAyTjIvSHFxQ3NmMVg0STVuTjB5dGlYaUtvVFQvaGtqSDhNK25iVnBHeGNvN1J5WkdtMlBvakt4NUlMdGl5UWIwTElTdzVMYkh5N1Q3RXBNY0pOMHhZa3V5S2pGNkVLbUpXMnpWQ2lKRitNanIreGdtb3RqeVB0RnlmWmJNWE9yYUp3Ympyc2xLVDAyUlY2SVFVRnZaRnFRNGtFMnRsRlNUcnNVRzFvVUhZa3FFcUxhZG9XUmJQVWFPVHNYaWZKREZVb1dOYVBTelNkMVJDTWw5ekpaWHpUUk9LVXV6RXZkWkczc2luR2R2eHBDYXRwa3FmeVFtbGREeVc5RUdwSWlOV1BHeFlwdDJ5TUs4enBTNkZpbE14NHVDZXpoK2k1UkpaSC9BSUtaa2JQcDkyWHhWVUxmWWpUUndsWTNmVEhaMTJZWlcreEx4citEZEt5TUxkbHhqcjVJdWJLbitSOHEyVkZ1L2tqQXl0V2ZUTlcwU2xKdWtlNzVaR2R4Ylp6OW5kSHF0Um92SkxTSVE0a29jVmZaaGs0UytTeElTRVBydzlsMTdZa1lxSXVpaCtFdUNiYkc3ZGtKY0haS0RmdVFvVGNmY3hZMFpKVXFGamM0MnpGRGd0RHBxeU5PTDJLazJZc3ErMWkvUXBSbzFSWk9hU0U2VmtJMHZDWWgrTWtacDdJNklxTHVrWXNuQlV4ZE1ja2pKTzJWSkxzeFBuSGkreWRRVkppYlNaZXhkMlF5VGdpT1ZPeFpVUEpKb1RibjNRL3dJc1d4RmVIRlNoc2xIaTJtUmI2TWVOSld5TlZSbGc2c1NpKzBKeHhyUkxKQnhJWkhBdVdVY1pSRUtOUFkrOUZGTWdxa0tyOHhXdkNUS0lPU015MzJZVW5NVzVmNFRrMFJiY2RrNDhaYTJ5UTRPckVZa3FLaS93RDBWMlRseWlKVWhlSTlpVmxIVFBnVXJUSEpKRG14VGRrMitGbUQ3K3lIMjdGeGtxRW5Sa3B4MlVxSXU3WGlEdFVSMnowVTdwbkNTTitMUXFzajRhb1pkV2psYjhlbnhaazBxTUNxWk9TWFo2amZRNTNhTDBUcmlKam9SeGswTDlFWnpXa3hacGZLc2k0eXZWSHBhSGlaRDJxbVB3amlqaWlxSEpOQ1hMYkZqaTFhT043WTRhMEtMakdoNmpzN1cySnhxMHJKTnRpRmI2STRwTWhnc1dPTUZiWjYyS0hTc2YxR1I5YVBVbFZXWTV0T21Md3ZOYkdvSmlnbWkrQTIxc1RUV2lUUktSMlVrblJHS21Sd29VWXdYdVk4K0tQU3NlZkxMbzcyMldqa1d5SmluUzhyeWszS3h6NGp5S25RcGNvVU9iaXREeVNZdkNsdW1SYlArclR0bnB1K3pyNEhKL3dTOGNqSGtlNytTTWswSjZMT1QrQ0VYOEVvY2g0WnRGS0VCdDlDRnFKVGwwUXhKYlp5UzBpTTVmZ3BQc2VQaU9HemgraHB4WWlOTkhCSHBvOU5wQ2RDblI2dEhxVlRKU2NYU0U1U2piSlpYRjBTbS9QOVRGRnRqZjhBVkVZcUtGNGV1aWNlT3k1S09pVjNzaEJNVVhIdzJLUmQ2OFZLYnBFWXAzOG4vOWs9IiBpZD0iYyIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiLz48L2RlZnM+PC9zdmc+", + "description": "Displays historical rotational speed values as a simplified chart with background. Optionally may display the corresponding latest rotational speed value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'rotationalSpeed', label: 'Rotational speed', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'rotationalSpeed', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Rotational speed\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 4000 - 2000;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 4000) {\\n\\tvalue = 4000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 4000 - 2000;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 4000) {\\n\\tvalue = 4000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0px\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":500,\"color\":\"#2B54CE\"},{\"from\":500,\"to\":1500,\"color\":\"#3B911C\"},{\"from\":1500,\"to\":3000,\"color\":\"#F89E0D\"},{\"from\":3000,\"to\":null,\"color\":\"#F04022\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:c2ltcGxlX3JvdGF0aW9uYWxfc3BlZWRfY2hhcnRfY2FyZF9iYWNrZ3JvdW5kLnBuZw==:c2ltcGxlX3JvdGF0aW9uYWxfc3BlZWRfY2hhcnRfY2FyZF9iYWNrZ3JvdW5kLnBuZw==;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Rotational speed\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"360\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":\"0px\",\"units\":\"RPM\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "angular speed", + "spin rate", + "revolutions", + "rotational frequency", + "spin motion" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_snow_depth_chart_card.json b/application/src/main/data/json/system/widget_types/simple_snow_depth_chart_card.json new file mode 100644 index 0000000000..e9410a7553 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_snow_depth_chart_card.json @@ -0,0 +1,39 @@ +{ + "fqn": "simple_snow_depth_chart_card", + "name": "Simple snow depth chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical snow depth values as a simplified chart. Optionally may display the corresponding latest snow depth value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'snow', label: 'Snow depth', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'snow', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Snow depth\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 120;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 120;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#7191EF\"},{\"from\":1,\"to\":10,\"color\":\"#4B70DD\"},{\"from\":10,\"to\":30,\"color\":\"#305AD7\"},{\"from\":30,\"to\":60,\"color\":\"#234CC7\"},{\"from\":60,\"to\":90,\"color\":\"#F36900\"},{\"from\":90,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Snow depth\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"ac_unit\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":1,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"cm\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "snow", + "snowfall", + "flurry", + "blizzard", + "snowstorm", + "snowflake", + "sleet", + "whiteout", + "snowdrift" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_snow_depth_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_snow_depth_chart_card_with_background.json new file mode 100644 index 0000000000..4b1f0e82b9 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_snow_depth_chart_card_with_background.json @@ -0,0 +1,39 @@ +{ + "fqn": "simple_snow_depth_chart_card_with_background", + "name": "Simple snow depth chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical snow depth values as a simplified chart with background. Optionally may display the corresponding latest snow depth value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'snow', label: 'Snow depth', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'snow', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Snow depth\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 120;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 120;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#6083EC\"},{\"from\":1,\"to\":10,\"color\":\"#4369DD\"},{\"from\":10,\"to\":30,\"color\":\"#2B54CE\"},{\"from\":30,\"to\":60,\"color\":\"#224AC2\"},{\"from\":60,\"to\":90,\"color\":\"#F77410\"},{\"from\":90,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Snow depth\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"ac_unit\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":1,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"cm\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "snow", + "snowfall", + "flurry", + "blizzard", + "snowstorm", + "snowflake", + "sleet", + "whiteout", + "snowdrift" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_soil_moisture_chart_card.json b/application/src/main/data/json/system/widget_types/simple_soil_moisture_chart_card.json new file mode 100644 index 0000000000..b28d7eb204 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_soil_moisture_chart_card.json @@ -0,0 +1,32 @@ +{ + "fqn": "simple_soil_moisture_chart_card", + "name": "Simple soil moisture chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAh1BMVEUAAADf39/g4ODf39/f39/g4OD////k5OTg4OAhISEjTMc8PDx0dHSsrKzHx8dYWFjx8fGQkJAvLy8+Ys6CgoK6urqenp7V1dXj6PhKSkpaedWsvOpmZmby9Pt1j9zI0vHk6fgwV8uRpeODmuCesedMbdG6x+7W3fUxV8r19fXW3fRohNhohNkJtaMzAAAABnRSTlMAIL9AEN/GQiaNAAAFBUlEQVR42uzPsRGAIBAAsBcQv2QN9l/QcwNL4JINEgAAAAAAAP+1a3MtPrXks7m8a8SVs29vZI0y+gFmiexHSJHFiKxG5GW/3lYdBYIoDN+tYlHVp6IPCL7/cw6dnekkA5m7EWX8CbGt9uYDBT1bN+Rs3ZCzdUPO1g05W98hGX9PzvUd8xUiWvFetGSyNlOE2s84zEO3iGfVcHzfIaKk4VWgddWMn3KVJyTQ51AZ1pUdz9IxpAX55iALVvsGZG3IvQQgl/GCBKD5/I+lCNB+r4JuASUiF0FpRX6mq2MgxkdxDQp7nsdNTROEYUFqAswYELjvPmBpDs2luxawQBhBbqPN/YzVEZBIba6VG1bFaYJGQWR8h0RmYXwO0j4htgFlIBkWpACbAd6xOgJSueckgRx4FdSlEADDO0RSCQqG+YPphIh6CviABICu6obVEZCd1OlgW7QAZPbOjMz2Aem6PRTegT09IHlUxj8hbiJy8K1Vp6OOt4ckaZTKlr1IdfmAZKeAAUlH9D4hexJhQ0oCNSlPiOmQMvDqmGekszbqmmRzegeacosQbUgVmOcCM0AbxOgVqDsk0Q3o3BCUpnFuIxuZBKsjIKh8FHGZvkAQlEwD1+k/eGm8WDfkbN2Qs3VDztYNOVs35GzdkH9SbtWsNrk4JBenqzppcmXIUFp8LIw6rgsR17jW6uOqkKwqwJskXxTyi12z3VEVBgLov2lmii2lqIDyjWh27/s/362isrB2rdkNwYSTmNZiIoeZ6QRUoRjGx3tPEca9sRh7S5HV+Lz9QL6lCJcPcu0dRPy1Umv/S0AEwLeQvIGICtAQqHuFxPCNDYr5iIhwL0MBYyTuhQ/Cuz5CFTxg8B0ez0VkyzGIY8SYjdNf3S467pWSlkYucPPHIlEKX0gjcGONl17trzhnw3UFV5jHEbmyND8ZsN+L5J+aIjCkVUlUZtFN4x+Rbt1aA/f72WAd3GCDK+BvhfBfFUkTMkTnmTYaRkVfo5JR3eiTeVM8C4yHbJQk/bqzCSrWWWwknpGr10QORFWedidOO4DGvL8e0QC785LW8DNfe7XsQ7J5JfOZhyhDFUozqLVYK448ZO4iO6IjdCSUdGafcEEnnUhjXnbGrUHgzWrbzZxhYRxgIJWADmHUPOYqoqmGnt7HcE6ritpCZ/AzMR9uVHLb3TtxBr+DeQHKtZNITtTWh0PWV0FBtwA0pDPSkOknJbLF1fiXIy7jAKUPv4ateOwk0hBpMpw+ukSrspL+3dMu0Vm0o8a11Ptv38t9KODvsYtURiSPdiUl17QiGgYgfVrp/rP7iUlEsm7nrboB8l2jTXQGplFRZ3XkUOo2JhLRfWGMir07UBVleSCd2huAhAmxp9ZpLFIRwR2t05payKkCC3tkMCX2XSu/DWW3E9e9yKWFJHQWzSwF4qGCSbH3kSSFVJMGSOgUARS6T61LC/mkFD4sIivu0LynEcmJdFIStQBHMySHksroLpKYaUX1R/2wuQuJUsCk2EWg1URUtmAoDmRIjjCkJnoUED/EYPJw2EUMx/x4D0GeRw8+8GhxE2Dow0vM8Wm8iN3/vzJnkRD5GlyZtYjywZk5i7zCIrKIvAuLyNxYRObGIjI3FpG5sYj8b+cOagAEYiCKljZs9ogN/BvEAuFUmvccjID53RjSzaAhOSAdtNaVUbvX6faTex8Rdf4/S5U1KRQGAAAAAADwygM1cfgKof9tugAAAABJRU5ErkJggg==", + "description": "Displays historical soil moisture values as a simplified chart. Optionally may display the corresponding latest soil moisture value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'soilMoisture', label: 'Soil Moisture', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'soilMoisture', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Soil Moisture\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#D81838\"},{\"from\":20,\"to\":40,\"color\":\"#F36900\"},{\"from\":40,\"to\":60,\"color\":\"#4B70DD\"},{\"from\":60,\"to\":100,\"color\":\"#234CC7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Soil Moisture\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"%\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "soil", + "moisture" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_soil_moisture_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_soil_moisture_chart_card_with_background.json new file mode 100644 index 0000000000..f520d56552 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_soil_moisture_chart_card_with_background.json @@ -0,0 +1,32 @@ +{ + "fqn": "simple_soil_moisture_chart_card_with_background", + "name": "Simple soil moisture chart card with background", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC+lBMVEUAAABegj9WUkY2MjCBd2OljoJuZVV6Z19GQkBAQDxAQD1lVFNjb0Rhd0Ph3Nrd2Nff2tnY1tTX1NPa1tXj3dve2tjk39zb19bV09LT0tHR0M/Pzs7m4N3j69bb2dXe59Tb5dLg6dXn4d8dHRzZ2dTg3tjp4+Dm69rj39rNzMzY39Li4djh4dTa4tLZ4tAqKSfX2dLi5NTf4Nje6NDi7NDl7tYiSsFfhT7Y3NPj5NlCPDo1MjDW3882NjPl59vd3dbn5d7e29g8NzXc3NPe5NFeT0s/Ojfs8tJlVVNjhkHi6NPs881qWlbh7NPR181GPzwuLStJQT59Z2Ll7dvg59jl4dri69l4ZF9aTk1MREDo7t/R1M7r5eLe4dRTSUrl7NNfUVBZTEk5NTLj59je5NVSSETU28/m5tWGb2defz5uXFfo7NXn5Nro6Nh1YVzl49eCa2ViU05QRkJagz3k6uDh591niEWYmZHf49ro7tBQT0syMC7p6N7b3Nfb4NXf3tNWSka+vLpjiz/s8ORZfD9Wfzjo79rr79jk49yOdnCJcWxwlExoWFJrmkLn7OKVfHJxYVhrjkZ+fnlWdj7r8OBxXVt0l1JNSEckJSRrh0vDwL9jfUZGQ0FaeTl1mkplk0BoZ2NRcD1GXzuih37r69rVz8zHxsCwsKlwiVE6XMR+g19XVkvLx8TNzsCagHh0cm+AbF5ReDDQyseJomNkZGGCoV5MZjx2g1Zab0LHyNaPoW1vb1xkY1NBQD58mltbWVR7oE9ypUKmpZ51j1VObMdWUEVdWkSMjIapjoNKWD5IcCuIl2W1utS4t7VJRkRFUT2YmHN9jlxTZEEuU8OJtlKulIyFdV9jXkt/rkR9aWiFimNiaEdPVERof8qMq2eKgFiUkVZxlT7u6enS0Nm3nZSlnX6Bk82KjWvBrKl6eWKapdF0icuawGXY3MG0uaOQfH2XrXqBqVHh0M1ygy2mr9GNnM+Vos7s29ddd8nI0bLO1VWiszKwrWFASyzbA+MyAAAADnRSTlMAIB8gHyBAz79A37+/v53DWNAAACU/SURBVHja7NQ9a9tAGMBx9ZW2tJ/nPsN9g0zeNRs0eBTyJDoIQYqRK+NOJp1MsB0KMQmeHIFrhQ4ldMlQMvcb9P88d65a06FLt/uTmOCX3P3ukRyFQqFQKBQKhUKhUCgUCoVCoVAoFAr9e0+lZ76nf+nRcX88/fiPHv2P/BpPnvBw1BN6qoznr6vV2d3Dfv9d2j883J257uhsJc1mI6piydpkNNu0bbvZzEZJVV1c3N7eLrcfpOVyOZ2WWd+kk8nkrTSZpOkkNf1+RlOaz+fD4bBvjInj9644NtYY3qcfSHn3kPr9vrGWF1gzK6e3H7afXdtv3z5Kg8Hg3bvdbjweX16+ehlFjy6qatV2kD2QLoXASJLEupXjvklGG+JZIHE5ZfsgjiBv6fpaJCQSdlSWJRIPsfYAyWLiM57eQURngTjJdgvlGIIECL2M3lTVaNa2zV4lPDZ3SNp2tYJxmEfC0fD/58QSNpnRqKIsY29AfAcI8xAHEsrzIjWWk8ikoTo6iErYtUJqIEiGkkCMjsTEOvilo+BwAekkL6Kq0kulQaI1TdNKq5lHiCOxekxzJzFJwjTUUZYdRCV+IDB87K7Oi6QoCpmpbFm2aAUW0++QXxLWUi5Zg8RWKmEodICcDAa9d5JIxpcRF02Rn64XzSftvGnW7UaabTYYSOfB+jhubm6QsI+kg4iEUfwGYUvi+CpdX9dXV3UusZJe9LLpNOkgSDL0VkeC5AAxmpUSL1EIDZyk1+s5CUVyVnl9tTg/R3G+WCzW61N1kHck6pirQyXuQKsqZgdOIsE5cnzh98cV1ZQXWsrdnBZSJynLLOOOL4qakOj48ToFwfYSLi6B0MnJSY8U4yH3HBoQWqxhSG4YLEb+uhKHl3CkVk43JsVMfTCM7RzSD3KW05zg6NHxh954SLiVVWITIH4kZMg7jFzKXsJdstsxBiidhKFEtri/r71Ex/FrPUo1HH4eXqLfOrIMP8TLmcbq+i3aOTQk0qmUK8JNSCBIYi8BkteAgRgWSH0cGa+oxEHG491OLL0ubvnI/uTD7n7aKuM4gPcvEBgUUKHATrt2FmypEGn09CWQiLAtBigsYFO0DSCFwcVMoQGWqRn4kkmqi4l6URNsjQ5LtBdGQExN7IVZYrzYLjRTrxYSs+jl7vz+fs9zztPi4nfQ9fX0+Zzf83Z47U+W/INedfcuWzj8rTJAYX7CmafwqUM+e/sHvEW9DaEH33//zz/c/L9U7hrBueJ8TffQhelDZj/+4bvv+EX+bhzMCBrDz2Ii3T84OPgCOeJQQcyaWHAKSYIzdvdBW1t7e3sX5UmVZykzMxfMzMzQM3il3cxTlTlP6RW5RhntHW/tqA49bhsYaMcPJZHoSlDaxpA2FbxE/42JrPg2+rze5kdlmptvHaEuBuR5VI8lXz9pOioU0lENmXkI5PzQQx0MGZUQ5WhtBaSNFQPtbSBIBwevqEjI+Kuvvrqy4ltXEkCazxYJwpWxPM0SdK4HiQQ7wHiiGqIc1RCT0S6aXyERDPqBQ0GUQ0AobRzDMT4+PtYhU+kYF5KNdbu3uZkZnFtgIAR542nkNYzAZwHpQp7gSEa1Y2qqGsKI9iepIj09sZ6eaghnlIJ2mw1jiJKI54z2xsdllIU+QY7Ll00JKMgpujkLxfT0NCSW9155BRIMXhhMBjVUORBSINWSLkDgINJTPbFYDBAlkQ6SjHNB2mQUZMiAcZjhdBJ8HJ9oPd9x3ngJj0cvI1Jih6XOW8e5OH00TZKLlk/eIAimWqEwIU9U96uTEH4DDyYDsrraUy1p7UVz0AblUBCWDCF4IB8JxiTCFkgQ0zEKCVMw4tcRu4ycwKYBQUkoH0lDn5IoBxhGGCIlIlSdgad6VldREvQ0llC4fUY3aVNBKeiV87itDpoLhqT0VkoYcl1KaMybORAZnAaEFiP8wiBTWRjZrZRjChAhwRsEgyQEWQUE1SFKh9GIakU7/rWhCkBSep1IrwlxEuR9lpyAILIkPFI4Ps7+/j5LjiyffCPDBu9JSaXjcUEBhCXiHYytgCCQcA9SRUi0cwYQJRHTsxN9MMYqhkgJ9S3JUKNESjiG5/PPj2GBxvLLLybEiygHpc87M+NV9ah0mO8TkIGenuFhSAZYwlXhEohg/OFWYiAhCDMYAkXsZNfCwKIz8ZQxrHj+FY7LMiHS3KMrWiz5+4D88ivHTg67va+vf4NCDLY1C8Bj0uE1whJ+lwnpURAVMETkA6pJh3S8f23U2RuTDiXhiYsdWNnVYiIrwZLr1/GD/3HlgQso7F4sP/74o4SI9PcHo5SNfuHg9QeZmhKLkOFQpaM7NNqHSTIw8CTa/BAGnpZBTTqEYxIFIQAcAoLOZTrMckpMQlHgMPI3X0PhQhCQO7/+evDrnTt2m91us/UHg52U6FR/P7nqvKeaVU6doqp57Sjch2fRfhRPYLq4IlySrhMQVlBMSAJz7ijK8f7715yAyHTQBAwKM8bGyFFZyQQFgwKaKggkdPXxk+UOglF/546NE3R0upeWljpzeiHYbyNJ3alTsJSKHxSv1tXZOR9+kO/Ti/39/Rv5YmoDKRfzQ6uAYJQUigMiCoI8oRwb0cRYq5Mdk04F6YiPIeOcMTjajXXN/JzISmKFaqIg2FMj31rIMb89f+4cMazhsAOO2dldXdOKQabV1yFlrVh+5pmzNlGzxwsf9gMSDEZTmudCNOrTtVRMQGKpcneCMjCQzop9rbGdlq2BI+4kBqYn0xGPk0MlscEKNfGg9OxgywoVRVUEjrvfWt45d25+m1JTU2O1wuFeml2EA8lYrTZbU1NTfX390WF9/dlnSrbHy5lUMDiVygX1YjDo8wGSWvGXPB5A7t+792B49f797u58JpNLlA7T+UTqVuJC6kJXKp/JddGzcEQT7Hj9dRTEDEO6E3K7nkhQl33oNIpsbPiYwvmbBjtdplvm5+GYGJmYqLEyxLW0sLiY1Th5q7VGJKOVH8fL1kO9qKcdOS11RS86fAQppOPxbFZLJVPawYHnwWo2HceTWU+u7NEzCS2TyGn5qKYd5kva0ZE+FY2uxAOTk3C8Pjm5VgXxy4WOGBvMkOk7mXWmcOCgAHLp0valiYm9vRErUlvLkPuaXvLoBe0wXGsVqcl4tGIuXNJyjryWB6RTzwLiTml5zbuLm9SOvj88/NvB6n56Mfux35+52pnORqMGJNPVdVjs2vCUowl/wDkJBgqyFggEJCQQ97vdPhk41qWjucoibfa+9XUfKBQsIwgklhHO3t5eLRJ2MKSgHXnTN1OadqvWzKcp3ZPLaG63W0vtMgR3AdlNZ1L6GiBaanh4/+Xh/bRzV/ekU25fOuvzaZlOhqS6ujSPrnuycKyhHsh1cgSkI+A3IUHfRnB9XexwMc/U1XHbRehuHT9ch4Xy+fEx/3H3nuWtueXlZThu14bD4VpAIguLR5qm78KhlR5BavFbSD3yyKdauax9ujSjlQDpZogfkLL+cYoh5eHhg98A2dn15gta3i0gPgmJeoq5XO6CPxBQjkoIjuYQCcKxbpPzJe/W2cHBQ/mEbZ2D3cnxMWEsb75Jktu3b+MQ1GZHZGmhQI7CVQwSGDhpPX+zoJWaPZndgmdXQPwIIF6P9lUIkPTLD+57fgZkM53GO0rudDrn07O5DEOi0aJ+K5e5qhyhrS2/X0pQENOB8xlcx2wJiJco3HLpqFMBhbK9vS8slnfffGtuDo7fCYK4UJK8ppe1QknTGwSjoaE569E85cZISdc8pdiuXhpKFwhS0m9uZbOhkF5a2z3QXjgeHj4+2Nz9WPMc+d1l7dBX9mhZPR/VS9HohaKmpXepY0kHaiAkAe5Z/P1hmRq0sa46kgGaoauvt9sPIJnAvxHLlySB43eXyy0kkQhKwsk3NDRAgTQ2NrZQMKMlceERwyVRPI4GbHFCodDa2s7mc8PIc89tbu6Ic4z40DyfL8pJJMbkhAVIaMtFs57fHxcMN/UHCllqw9YaG2b9KgdDVCRlkFePS9vbgKAicPyBr3W50GwXSTI6Tt/VRhCqHCGGQNLT2kotCEHhcjWyBBBDck1K4ECChqM7HnAqB/oQFiIURTDCCEMoJKH1y7Tg3CsIP4uiUAaxDlLmqWstA/LHH0tLgDQAAgmnkVIFOQ1IkiHY5jkJskAQvEiQHQXBZpAlPp8jiBgFgWNSDBDhQHwUYMNWKy3IEoLi4CEkFHbYELtg8XPG6BGOwcFBCw2RZQNSKWlpURCSSMiiCXECEtqKuAyJ6luQjGKN82MupWBPBgg6FhxygMBhtSHYayM8uGsQQKokkiIctPVjBN/IkTI4+M4gMj1teYsgewxZkhCkUTqURI6RBYbg4o4ga4C0NDKkRUFY4iRIFE2kPTRLEuhYwnE91EgODGg7hc810tTEEppbTAkCDBAc+UbuWTIXp0UuUkWWR/ZQEgmhoGUqqiYSkowRxCkgIQHmV9QgEZBENNhHcz9drMmCvI6wA408MZorISwR+6OmmiY4rlCYguBzCiLzkoTs/f676FrmNNWgIIoSwWoJR0xC4DgNHUO2FhZ3qiFj0SDKQSuxnSBx56goyOUtOMBgh9Ei6vonIFKCJ4NXruASiSlsqYCA8JKEYLCPTKAkS26GqDyMEsEWP0kOGuwMMSQgoiQKIgvi5XUYV51cEO5YW64wnVSzHv91SAgonLADDopRFruCvISwRFRkAiVxS8j/UGRJkgyJMwQOIYlQrxMlAQQF8WOk4ysR7IyCPiwhZkHkYoeanOxYtPoqiIzD4e6enZ3tNil01P9AMNSRvdu34UBE6/lICoKIB8YgYUh8a+s0whJBhMSEBPyYekWLcd3pWwlcBoT2vHgBLZEQGcmQCgXBnglxudEPSEKUTkhs9WaPvMiZvjhtgWJujtZEBzvQWglREpWW0wISi8Hh32ppOSFJGn1rh7aBjjCtz7YmGwriD4xO4mqKLm/9UQFB82UMRnO+kM0WSs0KgtAKDQhLmAKIXEdO1ddjwhrkWCZGludoCt6rbDk71GPlOEMrCUNaBYSjIJAIyBrWbuw2aLiijztWArSGUDB6uq+wBINChhkNzbjm0XS6Ns3eFBDRBhwZEJOCknCPhQIBgdb1czcs5OCJiz+lIFJS5QDkzBogsiIRhrDEWGQqIY0ucVrJgYKMCshmsnWIJHY7lYFCt+g/N3Utna/DwfJZTb9qVETOlbOzi4bEKEm9hIBx48aNCQsc6FoYKOqD5HioBO1WED8qQogzkgJViGZgCVkLnZaTBHajuCocxb5lk5LEjnMAI9aGDQnFajj0POYOngdv6h5IaIAYkz4kHCx2gGACNvIOMS5dAmRkjoY7bhgiJ3E+iogpwR1qKx2zFQEEbYeDIwrDqztDIDlzxtgcRLYCKAhLGAIJOoi4bGAIvqtR178KIXCwRP9UncgI+pYZd2dn0KogqAccKAmNkeUR+q0VYYQIMMZAUfuUyMLS7BCCnmU4XhQW/PIWWEJeJF0L1QoO/N0Nc3aSIL1CwsuWCWlIaSli4JAsyWsZE4K4DApfRwaxNTYg8+zAttEyMTEyMoEAwsc8EWkxqxPBFNJNED8gQvIiwhbc24GEwhCuE+aHEDsIkryWdCYx6yUXFxcXFiKQ4DsZoqdbcEnQYibroZI0GBCxkXWxw6EcgNxAzg1ir0X961+6ze6n7SqM4/0LyksL6IbTWNk0w9RO/UVaK66zDSGQlsalNvLSdjYsdQPaGxxMV+4GaQJpsowQLkiQLY0MTMNNo8uShY2ZFG/Iorsg0THJAnFLlm0u0Qu/z3Oew6/z5TsHfdvvdz7neTnPeU4dSKUAoiqCOkiAmEzT8A92+MMukLhch19XQSMkDHKWTSJBoozEHIdw3NHZ2df3+SGIUDSJBhl2XCdHVJU0aVGZBCDMoQUMyoRUpqn0y0V8rDcTtXD+auu4EKkSCQkoTLIK6zQ1NTe7oGZ4hiYxRSCQgAgcxk4c73T2vXT60AeHTiNLf/3112crQa56ebxq6JTN7TCJJiEAERK62nLViqiEj2dm3W6LHw/9n8TSOXA+RyIcoj2YptZzPc0fIQkiVjlq2H20Tp3CUgJR/j2o3iAQPn3CwS/qGug0BJAPBQRXRl/MDlWEhR0mUU4ADHgBQTCFmEOLKvjkrDvgs/hjsXhvJpmMY339PwxG1N7WeuxcD3SuFRfGfdT6Imrk1Z30+ekPJULYIMzxDjhaWk6B5H3aSs/OLcpKY13xag6z2FpxLuAt6oaoVhGvrmwN5qCzAQgcNwDi81ni8Wymy+0ORgEiE18JYhLJW1UHjmF30HOOymmpVhlGL+/9n59BkADklFpaCORdZZBOpDrWmNMxks+PYN2zM8gaB4QEol4BLjny1MNFxdhCm2baijEFe5U65ACK3//NjWT0KiySzUSDAAoEAaIdyATRzwUJv7FLAwmXoDab/AvrXolG9d0ZGOQMQI7XL+ZnnWg6XgEIGYRyNvWArji9Vz7EpqzodeQvWasX0JtlColDYapbce7HXQkEaj6CCpRtIRhMApAbNwASRIxEu4IBXyLhA4iMW1BYe+uKJrOp/QVxkIBi7k9JAIFJGOT1NUz88jIm/nqnBjl8GP7u9V7BFgBanFPF1fLCUXVXEwRPZhzLDQ0onJtbTpxwuRikxsQQ17p5EyCZ2WDQgjjxehIJd6YipitzLxsa1xUH5TYmSFC3yYaI+bVwLCEg/YN5x7WF11Gc4IziOjgIxHUEyjuGW7gXhg3/WH5ubm0BAWA6gIDgOT5IxzHNLgUCDqEQDtJPPxFJFCA+n8/r9QWisSoJin8tiPbSFl9YON4o1dbgmI6vKiSQDqmj51z9Z86QRa44rlGVdeLQofdnnb8rkM/Q+rnkmDty+IvzSsfr8RnAoY+iq3ntA7jfRUcee5kel+sEgmR9eHh4XUAqLUI2ieKrgIEAIr0r2TFUVwliLT8rGCW63tb2hmFsbJbqmKNm/zPDKOzYXtMg4GAQdR4EVzjWDJAzAJnzLmI5Bsi77z5w5BnkZWqreL2XyC6HmeP8cYoaahtxh4TCVK+/uOEb1xwXL7eC5OPLy04HybM8ZmIQ1c2bsAlAfrRQwkomY+ElAtEev7VqQCVcb6sADKAUtuo4gW8au9uFjf02W6lEIAh4xueOCDeajp1z9RHI+445KvZOnEZp0jnCJmn/rOfLL+EvaKK3IhupsgOBjMMdmIQ7iYpEfAtXHfY4nCMr17z4OTwzs76Gc4IVjYLbAwQkNxElGctsVzSZ9aeGJnlWQULXAcd2eYsut2kYO9XWJ3hezb5VKNTUPDWe0gMOkQblVTCWpMSaY83tBDLsGFO7X4C888AxRwb57KsvLztWVBudTiuV+Jiqh0l4XurEudT8DK+MeK+urH0K56N7jK0IiqSvGyD55ka2N2nJZOMxfxvqLRuJQKzWHcMoW1XwrhqriuxZNQd8YdXWQCBPjKfkVzyBAqK+D1b72jkGGfGegrhTgW9yrTm8D+BZn+YdnjfkSAAkCuUIgZB3YbMlA4B0VOKTX0oz0kYjf3FsxQOUWtENEkB6LdlYri2d9scztTUcuXSdgrErKRd/2WfBo65e2Niq2jYelwqbiptXSVU1CMlb7Fs4QmEOAXlnmLyDjooXKbBwIyZhIV5IIMGg6YrWSpIGIiFxnuRvL4zlPVeF44VoNJO8mE32xlGipD+Bi0WDMBfNMS5UNozHu6urCHCIQ78EB7OznhiFTaNQvVko6dRgFRAhEZA5xxUF0gcBBCjLI9fy13EXyXUHsGprIbspkoqWkPIugDAJ5QI9V7gCzKoaEMGurtkfkwTSkculM4j4oHwEs4yIKBjQRplBdrY3N4xnep+4s1rYLO0YTyr3YQCBOEzgWgTS5702OCh9CgGhEHkNI8FHVM5mEKsSFYZNqN6U/e1KKk4IhLrEcn01yhp1Q4BcdV+dhVWyFmwT26IBn8+9bx+jgGQbIOXSzoaxylNOGaxQUt0HtZ/fKhSsIp46qcZ4mak5AJC+B47hQfrigebQIDQWFVtUR/E0aBIub2W7LYKxBYQNIq4lV4CAEqShd8XuxCwpbLCybp8nsY9EKDWbnHmt4CkRSHnnScHYMEnonVJpd3O3ZOeCUXmBjpWGo02u/r45b/8gtdVA0Q4IWQ6p84hIlNg6qgYtJFyk69xrmkTmCMFjExLhkEKjK4Dyyh3smLQMEEnGnRgFBaGABEFgxfgQGE85G2Org2BXvWr6WTK2Sxsbq1hbZBcEFojDE1UeAIYdeRf2X/3tpLc1CMoM3FxyPKmyP4raVoW6MlMFSB1J1++QrMJqbxJFiYU1vWvAkktPRS74k12BF0kEUrttbNRjK7FFSwgJo4V1MGJpLBYKDbvGY+SEbatdt+iExwodH0R5208gvLn/6O23GYVKX5wTagSzRyNhbZMiFNEuEoPoDA8UkSRp+gDV7rSmf2LBqei3kVxbPMsgHCWPsYygNVLGr+oNY7caw901jFd19+eJsVO/alRVlYxNMOIVrVfJXHYc/aJMb6fmJoG4AEIkndhVnRfbyTA1CHFwfOm0CdXpuFO7CwIxhdcxKHZ0fzwZjUaTvSlL7vvvptL+jlR6r4axVRWwCnZXF+BhiPSNUrV9C69g2hlkq7DZWP/M2FdTNjarrADp1lL9reKIY4Q5NAiT4PT09Cl9CKFbu2ayaDADGSTPHyBKQEDmBpYml+58EJ2H1ER4/sK8JY02RDLqz8VqRag6YIvC6gZWE2t1Gb9W8Xhji+cbKFurW/X2bWO3vIsFvtqu+0GaZc3pXOvv1yD9LgHpRN8B23g2m+wLIKmq4FaMQSFKJHsljwS3tEdBAng1BZjB7u67d+/efxjJ+e99e2fegm52LNqVTHcoek4pRx8XaBl5DKNXlbh+XC1JTHPmsldX7eLFzVpbnb2RQA5qrcMcxVNyAoAgwQNF0v7SobNnz3YTh7m9qTILNcbYv1+RwBp4wZQUAzYTBBZ5lTjudv/x6GE6nQ22WTraUhdifn8uzMgsLFVHy+UyTxZUKpdL4sv6YKyuoab8uIxbwrcAolumtmV8LwIdiEGXgEAE8oqAdAMEl1EUWhwdjMEiEhgCz00BhawjFRHY8Rs3BgmhPBqKhAcGLJEpNLYiF1ITVaYO4A8kR9+QWY/yC+ILFJnWxoM8PDLXZa9jubaxEZvZZnBoAQT/n8jLDIJ+sJ0iooIC4nGD4Q0C4XETBj9UeIzCIHoKmIRscvCPR4+u++LpAfqaU0fbQOTOhLgsgMnWNpLas4l0L4Ze0wf2DXVWDrlGsha+isPf+WiSIz9RD4G88vKJD7j3SG5pk5M2uWINzz84oP3m8PUjTYLgbWA31AsJoby6+Ki4cH80EE8i/Q5w6zcs+3OaMfNkC3MuUrMnUcQfoOHUwVUZBNZazqvkjrPLHjm8JDHIy+0nqN3QqDhkAwDpAGEOAcFfk0OeEYiNOVRlyjdHMFc/vHf//v2LgUzWkkn6U5HxpUgaHBpEZwxhB4JISOR42ASpR9SoSWKTHBFj8HEykzDIeRiOOSh4xdYEgqFVGkRzKOknBNLAHDpoOAVMAKR4vTeLlqk/PZQbuHVrvk71pyiSwYFPkhidB45fTFQBIjFCIWIli8HRkJ+Rl9Dn1qd9aOf18FeWcVJ3nK6t8yrbmi2iPAvSjlWBIYCcA2o4m4FZAomJnIGOh7dvZ4MZy/hQRzg9cfveLV4tIWsVPg3hdjrx6fnnZUrAeLOJIKEihT2LDGKniEH/vcXFJK0HCORNAnG1tKBHTQufTJA4B7+y7x/uJOMUDhKBYAQa2rTctDMUCCZ7syHLxLjff/JkOJJTJ5JkfNhaQITkeRAhe0GHO1uxCsJDcNzt7j77Aa0ffEYGEnYtHEPgYEM4KlEk1hWCsMjNhYOk07KAsPg957RztCsXDAQsAxF3fPL2rck7GIkG2bMIpO74DxCdCmgowJfKSCzCIP1ikdfwPRIFgu4txwekaXBNASH9x1IiHARCdIInFKyQ0+kMJRKBBNpBifjk0u1bAzQQkAAEBmQA/FQZ3DwVFw6G0IWQmdZA0qiOdnhJbGqtOkBfDQJIO7UKOUOJFBDPDZEoqYHTmBnE5NBridQxlSCh0PR0yOPrsDhHM+kpf3g8xvWybFw5HhlESGS4JofEfkVFp8prKkrrz6sYaTqK7ZAGwclAHc2DiEgodXHeYixz5MokcIu9CGEQTQuZNgGFcxosCYsnO5WenB9Kehak9VottagYl6+q2z4NehR7ADZZpTQgeZoVh1rkWexLDMKHn3gKEpFyVlp4eN2CYBgBoXuCTHKyuUwKCEtAZuBYTo8v5AxZEunIePhCdHR0mOsPcncGkSmCKIUjkKnc1iC6NUcozCHewszq2zwAgQkA8h6D9NOuij9PFCSmpjwpW2SbgAgJg5gcJOGoJFlDiEx7oxnfqMXd1ZWaHI+lhuaLDXL4JkmSlym6zIsvYOpwx0oQXgdU4SK+QRwQug91rQCBWitBBo+/rspF8S8Or2pyRLve/wkIxI7EtxcupUoQfnPdOz097QkE47m4JdM2Eb5/az6Vnu8YXp+5NDOD70sv4tfM2Bj9XV9fx+NicYFVnNkTPrqIV/BOcbG4eGlxsXhpjESfXVz4HSoWi4v08hV68tECLiEXwYf5n8wUF2xw5lq+Bj48g7spjT2vGfqrfszIK/xfPjQNOT2eaCZoCbSFb91bmgyP357PBmAoOBwaLG6/351Inkz7Ar7RcMozlE6FO8LjA7mQMzg6nYBPxoMd2WAqPRAZGD85FUl9klr67cHVUacneXIyEu+K/PLXL9+2xdsuDkyloz/8/Ouvf/4V9STikSn/1OTSZFsqlhqPBYKJ+NDE0tLkgD8bDvsnwhmPMzRKf7yhkC8X9GKJiIyn42i5JZCb3NEABheI+JyBQAiDDGajCUCEQk78cMc7LF1/s3P1LFfDUFj8Bn/M3RNcAgkZCkm6tAGRJhAoGUKT0SxZWwrt8qKDg9C9vi7q4qjcye2qFwcdFN0UHQQ/wNSvX+HTJm1yzkny5CTpUo5tlzdTsSy9W9eepqOthWlq5bvjYPFcj6YsQdUXpu1tI8StqzxCQshp5bBiuFL9up+S77yEHz9CwpxLmCCZiTzRmt8OZBfR588vvr5mu0j0aLrD9uvLSXe9aar7Dx+T/v3a0WHxvusAjAFoSMx1oTkP8apmdrAnLiGoESKA86tQCw0jIRBBzmEmsu31XeRanZxpWufNWC5L1S+unLAaQ0OPHxpqu2pdPNMcJaI2rxVaSJhtBd8B6Zc3pTl1GLdz61uM1/VxmJ09PQ0qgR15/f2VjPHu+w95FuHnz9++vpKa4AY7xwLTQajuMKN7n59Uh8IN/XQoCyidS/RqrGmNawRDIGkWSWHIGQ+QRBhBHjVHqMSmUScSIkGyM7aldRVFxM54CBFh4zLt9323HxhAwTZ0P7FaG3uYtIAScaKm/aI40pmCLeda4H7qpQpmx7V07TSdlN3ksZ1Z0WzTBB+8nhNTn75cu/GXSElNps8cdodKmMob8PjHE6EmwbulLEdr533rxpEkO2IRNR5LDCOA8VbUiHDIYR73VQhZ6h3GEJnNeRszsoOuFwic0dsii1oY25fl4ZQh+eHN4YN1YKwl7WwA1doXQpZDPxB+CyEmRVnMHjV1R4Nkmo/TYXEeGyL6WUKEpSiArZ4A/Pj9p09fjhyQjcg7WoyUmmma3qw4heFAiSBixwnGad2/kbhi2FX+dI4wCBiFBMxjYoThQNK42yEIyY7oaEZmcSMIINgHWjWYdn1/rEQ6k66DyHekHWrT9OsU+PUPH47H20RACCSRxXHonYgy+W5yQgQsxOrng8BJBaEBsmnaT5PccSYVU1hDjorOeOZrX77/8mmREbGXr799fdZOY2Dtm+m0XY8DhVglggDUInjs2n3H+S4W67osHdmhII3eThweQQRXryqel9CvREhd1ChyEIwD2NcVJpIOa0mdPXOpMhpm9ww4eU+XtlJAxwicl1Aj6PeVZnYORDSjrRiKEJFiFElAoqE4Adfr9Oz0dCa1G6mQsSgVUsu09M9ferN/0/en4+P2MLfT285Uw/qms0r6qh2OqT42oumPN7ER6aQt6YhPKG3LaunL4FqHYoiQ6UwmotuV3bYH2j7hQFRdy7hmIgEolHjEiEwlHU6unLnYUZHPO0MLoxEM1XLAAMnkCyWJEuVy1H7txS2IJEu0SMkALjkUMCIRypTpjERjGrxSlUvX19YOsxrL6XnZtwABYMZ+kJGoru2nVpRRm2U/JDYm12f/908LKpgAuqGlCwEEYKBmS2nxvAyQKedPqFDj0HkS4S1ohJRFWWGMIWNSCNPcpRCaoSmWszks1SUDroqqCppzCKM04+nUYm+pHb21QxUB0FfzWY2QMB4XRpl28LZKSEmMNJYp7sKj+Oj602aouvLNRHjMPY3XqYy7Go9FO5CajEVQBfYFJH5Z9vt2PIqT/bRihoBA2xYue8c1C2h8epvhE2wnt0eM6QjBrIAp7MiABkATIJppnIuUiwExdOcOiFGTyxd/Bwr7HYEr5xvy8+yfmvP/KjN+15w/m68tmljWOpflF85nwaae701y4Z9yTpvo3PkLW3u/itn8XLbI0chyvilnw9/4Z5m72Kyy5tb4Zp3vjFz61eUvYX7/U8jaW37mP/7jP/7jP/7jP/7jP362BwcCAAAAAIL8rVcYoAIAAAC2ADXKRyHu96JPAAAAAElFTkSuQmCC", + "description": "Displays historical soil moisture values as a simplified chart with background. Optionally may display the corresponding latest soil moisture value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'soilMoisture', label: 'Soil Moisture', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'soilMoisture', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Soil Moisture\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#DE2343\"},{\"from\":20,\"to\":40,\"color\":\"#F77410\"},{\"from\":40,\"to\":60,\"color\":\"#4369DD\"},{\"from\":60,\"to\":100,\"color\":\"#224AC2\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Soil Moisture\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"%\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "soil", + "moisture" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_solar_radiation_chart_card.json b/application/src/main/data/json/system/widget_types/simple_solar_radiation_chart_card.json new file mode 100644 index 0000000000..e8eb4e3291 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_solar_radiation_chart_card.json @@ -0,0 +1,33 @@ +{ + "fqn": "simple_solar_radiation_chart_card", + "name": "Simple solar radiation chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical solar radiation values as a simplified chart. Optionally may display the corresponding latest solar radiation value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'radiation', label: 'Solar Radiation', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'radiation', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Solar Radiation\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 1100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 1100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#5B7EE6\"},{\"from\":0,\"to\":250,\"color\":\"#80C32C\"},{\"from\":250,\"to\":500,\"color\":\"#FFA600\"},{\"from\":500,\"to\":1000,\"color\":\"#F36900\"},{\"from\":1000,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Solar Radiation\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:radioactive\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"W/m²\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "solar", + "radiation", + "sunlight" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_solar_radiation_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_solar_radiation_chart_card_with_background.json new file mode 100644 index 0000000000..71b205dda0 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_solar_radiation_chart_card_with_background.json @@ -0,0 +1,33 @@ +{ + "fqn": "simple_solar_radiation_chart_card_with_background", + "name": "Simple solar radiation chart card with background", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC/VBMVEUAAACQTQyVUABvMQWZUgFvMATLfwCgVwTDcAOUTQFrLQHRiAevXAf////04MPy3sP57M714sP679D35sfs2sL25cTx3cPw3MP46Mnu2sL78tP46Mvu3MP568z67dB8wyL46sr89dX78dL148X++dn89NT999b56s3///rv18T36Mf78dC+awL++96yYgGqXwHv38T57s7///SmWwKjWAKoXAH35cWwXwK6aAG0ZQH++9v35MmsYQLUggW2ZgKvYgLDcQPRfwK7awL///Dz48Xx2Mb14cj//eHy28e3aAL//uzGdQGyZALAbwLx4cTKeQLz3ceMyTjp18OFPgLXiQ/OfAK2YgKtXQI7ODHgmSf//un//ubVhgegWAKdVAHotDelWAL468jhnCqrWwLptjvrvUDiny7ckSDUew7y28QhHxuLQgLTeQigUgLZihjPcwTS15r//ePquT3grQ7jpSogHhqcTwN/OgL578nioSjelSXajxkfHRnalwvXkQnlqS2iVQKZUQLboAfLbwSTSQLIaQTCYgL68snWgA/CWgmYRwPe0bHswEQuLCaaTAO5ZALjozHmrTDdnhHSdgSRRAL79Mrl1MLrzSmMhnbnsTPnrjPjshrepgmWTgLYx63mvB/GYwjNhAeqWALtxEjhpxfo1ri+sprg07huZ1vXgxVkKwR6NwLDu6Ss1WWYzEnlpzQhIR6+VAqrTATOeQKJf27dlx9fKAP038VVUEWhSAPoxCPTiwdqLgODxSzitQ6TjXvmrTe0TwdiXFDz5TLHfAdxMgPe56mozl9/eGghISGsrKLw89Tq3r+nnIeiloJ1c2jvyU3v2S7jrSEhISDkvBdcJgXZ3KKg0Fau2XBYV0/j4syyrJaXl5BIRDvj4+HGxrfe7rTL4I+30XLovSm3VgPn4bOqpY/j49fr6brB2IDB45Wnn4rH1Yj470Xx8fDLy8jp88ewo473++/w9eTd2L7O6adKSUK7cxvcu5LVrHr2+NPL0pD8+2nBnHv9+lmIXlQDAAAADXRSTlMA9iDvryAgv7+/v7CvW+81JQAAGw5JREFUeNrs1LFu4kAUhWESRVrtuyAosAWWYgjuVtiisRUhmRAh1nQUURpsKUCXYgtc2hLVVqkoIqpteIQ8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", + "description": "Displays historical solar radiation values as a simplified chart with background. Optionally may display the corresponding latest solar radiation value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'radiation', label: 'Solar Radiation', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'radiation', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Solar Radiation\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 1100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 1100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#5579E5\"},{\"from\":0,\"to\":250,\"color\":\"#7CC322\"},{\"from\":250,\"to\":500,\"color\":\"#F89E0D\"},{\"from\":500,\"to\":1000,\"color\":\"#F77410\"},{\"from\":1000,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQoAAACACAMAAADESdfxAAADAFBMVEX////SgwPOewLNeAPPgAO0ZQHNfQK2ZwLThgTCcQK5aQHnszS+bgK8agHBbwHquzvIcwHUhgTRgALFdAHXjQXVigfmrjCzYgGkWQK+bAHmsTHKdQHKegLRfQHotTiuXgHquDvtw0HlrC/akgq4ZwLrvkCwYAHFbwPkqCvZjwfLdwLEcgLViATIeALgoh7swT/rvzvgnhvhoiXgmyPuxkPnrzThpCDclw7elyPpuTfemxjotTvjpSvvykbbkw68ZwKrYALhnybdmBWwYwG3ZAHwyk3nsTjXjAvUgwSqXQGnWgLipibPfQe/aAOtYgKnXQHBbALswEThoiyiVgKfUgK7bAHZjw6qWQLblROyZALdlh302VHotjTlqzTlqS6WTALy1U/krCv13VTemxHtw0fHcAPxzknhnyHjoyj//+nx0U3gmijfmh7HdgHIcQjTgAPuxkrUewzKdQiyYAL//+HQewOfVwLbkhr772Druj+aUgL34lf6613dkyLXiRCNQQLblQrEbAfhnSrkqybfnhScTgORRQLxzk7iqCLCaQW2YgGtXAHbjx7GYRL++IDZjhWIPwLRgQf///jjpTLDXA7y1EvKaBPBVgzTiQaEPQL99XLTdwXXigX+/Jf451vmryvdogrWhgnMbxT+//DVgwjZixr1207VfxHWgxXZnAfYhxnMbwX//9bhsA///8zgqBLJaQZnLAOVRgJeKATQcwP++4/GZAf772b88WT45lX02Et/OgL88WzszB7uxDrrvTbiny/CYQP+/aD230///8P661f++Yjy0Uj+/af341HeqQe8VAj+93ngohj+/rXu1SjktRvTfhmvXALQdhf+/q/oxCSoVQPy3SjmvB22UQZ6NwL99GzntSucSAPktg/szi3Qdg3jrhy7YgLVlAjAWwT+/rv05S7kvRTxzUPwyT/pui9xMQPKgwmkSwPpxBawTgXCdwmqTQPPjAvx2ze2WgL37j2vVAL88nj47S/rxS/1427364H69k38+mD47Jbx1llRHj+PAAA5NElEQVR42mTXXUuTcRgG8GZz2twmrtZeWDZHRi8ToSUsNmJrNbcVrmZGW20VREk2DMKkIA88qJPBEAk68EQwCj+FH63rvu7n/7zUZQ4V45m/Xff9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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Solar Radiation\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:radioactive\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"W/m²\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "solar", + "radiation", + "sunlight" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_sulfur_dioxide__so2__chart_card.json b/application/src/main/data/json/system/widget_types/simple_sulfur_dioxide__so2__chart_card.json new file mode 100644 index 0000000000..1a5ff89d05 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_sulfur_dioxide__so2__chart_card.json @@ -0,0 +1,40 @@ +{ + "fqn": "simple_sulfur_dioxide_so2_chart_card", + "name": "Simple sulfur dioxide (SO2) chart card", + "deprecated": false, + "image": "tb-image:U08yLXNpbXBsZS12YWx1ZS1hbmQtY2hhcnQtY2FyZC5zdmc=:U08yLXNpbXBsZS12YWx1ZS1hbmQtY2hhcnQtY2FyZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjEwOCIgeT0iMjYiIGZpbGw9IiNmZmYiIHJ4PSI0Ii8+PHJlY3Qgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxMDciIHg9Ii41IiB5PSIyNi41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMTIiIHJ4PSIzLjUiLz48ZyBmaWx0ZXI9InVybCgjYSkiPjxyZWN0IHdpZHRoPSIxODIiIGhlaWdodD0iOTAiIHg9IjkiIHk9IjM1IiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii43NiIgcng9IjMuMDA4Ii8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjg3IiBkPSJNMTkgNDAuMTY3QTUuODM2IDUuODM2IDAgMCAwIDEzLjE2NyA0NiA1LjgzNSA1LjgzNSAwIDAgMCAxOSA1MS44MzMgNS44MzUgNS44MzUgMCAwIDAgMjQuODMzIDQ2IDUuODM1IDUuODM1IDAgMCAwIDE5IDQwLjE2N1pNMTQuMzMzIDQ2YzAtLjM1Ni4wNDctLjcwNi4xMjMtMS4wMzhsMi43ODggMi43ODh2LjU4M2ExLjE3IDEuMTcgMCAwIDAgMS4xNjcgMS4xNjd2MS4xMjZBNC42NzMgNC42NzMgMCAwIDEgMTQuMzMzIDQ2Wm04LjEwMyAzLjE1YTEuMTYyIDEuMTYyIDAgMCAwLTEuMTA4LS44MTdoLS41ODR2LTEuNzVhLjU4NS41ODUgMCAwIDAtLjU4My0uNTgzaC0zLjV2LTEuMTY3aDEuMTY3Yy4zMiAwIC41ODMtLjI2Mi41ODMtLjU4M3YtMS4xNjdoMS4xNjdhMS4xNyAxLjE3IDAgMCAwIDEuMTY2LTEuMTY2di0uMjRBNC42NjggNC42NjggMCAwIDEgMjMuNjY3IDQ2YTQuNjYyIDQuNjYyIDAgMCAxLTEuMjMxIDMuMTVaTTM4LjI2MyA0OC40MzdjMC0uMjA2LS4wMzItLjM4OC0uMDk2LS41NDdhMS4wNjkgMS4wNjkgMCAwIDAtLjMyLS40MzggMi42NTUgMi42NTUgMCAwIDAtLjY1Ny0uMzgzIDguNzAyIDguNzAyIDAgMCAwLTEuMDY2LS4zOSAxMS44MSAxMS44MSAwIDAgMS0xLjMxMy0uNDg1IDQuOTk1IDQuOTk1IDAgMCAxLTEuMDM5LS42MjkgMi42ODkgMi42ODkgMCAwIDEtLjY5LS44MjcgMi4zMTcgMi4zMTcgMCAwIDEtLjI0Ny0xLjA4N2MwLS40MDUuMDg1LS43NzQuMjUzLTEuMTA3LjE3NC0uMzMzLjQxNy0uNjIuNzMyLS44NjFhMy41IDMuNSAwIDAgMSAxLjEyOC0uNTY4IDQuNzUgNC43NSAwIDAgMSAxLjQzNS0uMjA1Yy43MzkgMCAxLjM3NC4xMzcgMS45MDcuNDEuNTM4LjI3NC45NS42NCAxLjIzOCAxLjEuMjkxLjQ2MS40MzcuOTcuNDM3IDEuNTI1aC0xLjcwMmMwLS4zMjgtLjA3LS42MTctLjIxMi0uODY4YTEuNDM3IDEuNDM3IDAgMCAwLS42MjktLjYwMWMtLjI3OC0uMTQ2LS42My0uMjItMS4wNi0uMjItLjQwNSAwLS43NDIuMDYyLTEuMDEuMTg1YTEuMzUgMS4zNSAwIDAgMC0uNjAyLjVjLS4xMzMuMjEtLjE5OS40NDYtLjE5OS43MSAwIC4xODcuMDQ0LjM1OC4xMy41MTMuMDg3LjE1LjIxOS4yOTIuMzk3LjQyNC4xNzcuMTI3LjQuMjQ4LjY3LjM2Mi4yNjguMTE0LjU4NS4yMjMuOTUuMzI4YTkuNDExIDkuNDExIDAgMCAxIDEuNDQyLjU0N2MuNDEuMTk2Ljc1Mi40MiAxLjAyNi42Ny4yNzMuMjUuNDc4LjUzNi42MTUuODU1LjEzNi4zMTQuMjA1LjY3Mi4yMDUgMS4wNzMgMCAuNDItLjA4NC43OTctLjI1MyAxLjEzNS0uMTY5LjMzMi0uNDEuNjE3LS43MjUuODU0LS4zMS4yMzMtLjY4My40MTMtMS4xMi41NGE1LjI5NCA1LjI5NCAwIDAgMS0xLjQ1LjE4NWMtLjQ3OSAwLS45NS0uMDY0LTEuNDE1LS4xOTJhNC4xNzYgNC4xNzYgMCAwIDEtMS4yNTgtLjU4IDMuMDA0IDMuMDA0IDAgMCAxLS45MDItLjk4NWMtLjIyNC0uMzk3LS4zMzUtLjg2LS4zMzUtMS4zODhoMS43MTZjMCAuMzI0LjA1NC42LjE2NC44MjcuMTE0LjIyOC4yNy40MTUuNDcxLjU2LjIuMTQyLjQzMy4yNDcuNjk4LjMxNS4yNjkuMDY5LjU1Ni4xMDMuODYxLjEwMy40MDEgMCAuNzM2LS4wNTcgMS4wMDUtLjE3MS4yNzMtLjExNC40NzgtLjI3My42MTUtLjQ3OC4xMzctLjIwNi4yMDUtLjQ0My4yMDUtLjcxMlptNy43NzcuODJ2LTUuNjU0aDEuNjU0VjUxaC0xLjU1OWwtLjA5NS0xLjc0M1ptLjIzMi0xLjUzOC41NTQtLjAxNGMwIC40OTctLjA1NS45NTUtLjE2NCAxLjM3NC0uMTEuNDE1LS4yNzguNzc3LS41MDYgMS4wODctLjIyOC4zMDUtLjUyLjU0NS0uODc1LjcxOC0uMzU2LjE2OC0uNzgyLjI1My0xLjI3OS4yNTMtLjM2IDAtLjY5LS4wNTMtLjk5LS4xNTctLjMwMi0uMTA1LS41NjEtLjI2Ny0uNzgtLjQ4NmEyLjIwOCAyLjIwOCAwIDAgMS0uNS0uODU0Yy0uMTE4LS4zNTEtLjE3Ny0uNzctLjE3Ny0xLjI1OHYtNC43NzloMS42NDh2NC43OTJjMCAuMjcuMDMxLjQ5NS4wOTUuNjc3LjA2NC4xNzguMTUuMzIyLjI2LjQzLjExLjExLjIzNy4xODguMzgzLjIzMy4xNDYuMDQ2LjMuMDY5LjQ2NS4wNjkuNDcgMCAuODM4LS4wOTEgMS4xMDctLjI3NC4yNzQtLjE4Ny40NjctLjQzNy41ODEtLjc1Mi4xMTktLjMxNC4xNzgtLjY2Ny4xNzgtMS4wNlptNS4xMjQtNy4yMTlWNTFoLTEuNjU0VjQwLjVoMS42NTRaTTU1LjY3OSA1MWgtMS42NDd2LTguMTA3YzAtLjU1Mi4xMDItMS4wMTQuMzA3LTEuMzg4LjIxLS4zNzguNTA4LS42NjMuODk2LS44NTUuMzg3LS4xOTYuODQ1LS4yOTQgMS4zNzQtLjI5NC4xNjQgMCAuMzI2LjAxMi40ODUuMDM1LjE2LjAxOC4zMTUuMDQ3LjQ2NS4wODlsLS4wNCAxLjI3MWExLjg4NyAxLjg4NyAwIDAgMC0uMzAyLS4wNDggMy45NyAzLjk3IDAgMCAwLS4zNDEtLjAxNGMtLjI1MSAwLS40NjguMDQ4LS42NS4xNDRhLjk0Ny45NDcgMCAwIDAtLjQxLjQwMyAxLjQyOCAxLjQyOCAwIDAgMC0uMTM3LjY1N1Y1MVptMS41MjUtNy4zOTd2MS4yMDRoLTQuMzA3di0xLjIwNGg0LjMwN1ptNS44OTYgNS42NTR2LTUuNjU0aDEuNjU0VjUxaC0xLjU1OGwtLjA5Ni0xLjc0M1ptLjIzMy0xLjUzOC41NTMtLjAxNGMwIC40OTctLjA1NS45NTUtLjE2NCAxLjM3NC0uMTEuNDE1LS4yNzguNzc3LS41MDYgMS4wODctLjIyOC4zMDUtLjUyLjU0NS0uODc1LjcxOC0uMzU1LjE2OC0uNzgxLjI1My0xLjI3OC4yNTMtLjM2IDAtLjY5LS4wNTMtLjk5MS0uMTU3YTIuMDMgMi4wMyAwIDAgMS0uNzgtLjQ4NiAyLjIwNyAyLjIwNyAwIDAgMS0uNDk4LS44NTRjLS4xMTktLjM1MS0uMTc4LS43Ny0uMTc4LTEuMjU4di00Ljc3OWgxLjY0N3Y0Ljc5MmMwIC4yNy4wMzIuNDk1LjA5Ni42NzcuMDY0LjE3OC4xNS4zMjIuMjYuNDMuMTA5LjExLjIzNy4xODguMzgzLjIzMy4xNDUuMDQ2LjMuMDY5LjQ2NC4wNjkuNDcgMCAuODM5LS4wOTEgMS4xMDgtLjI3NC4yNzMtLjE4Ny40NjctLjQzNy41OC0uNzUyLjEyLS4zMTQuMTc4LS42NjcuMTc4LTEuMDZabTUuMDE0LTIuNzA3VjUxSDY2Ljd2LTcuMzk3aDEuNTcybC4wNzUgMS40MDlabTIuMjYzLTEuNDU2LS4wMTQgMS41M2E0LjI5MyA0LjI5MyAwIDAgMC0uNjctLjA1NWMtLjI4Mi4wMDEtLjUzLjA0Mi0uNzQ1LjEyNGExLjQwNCAxLjQwNCAwIDAgMC0uNTQuMzQyYy0uMTQxLjE0Ni0uMjUuMzI0LS4zMjguNTMzLS4wNzguMjEtLjEyMy40NDUtLjEzNy43MDRsLS4zNzYuMDI4YzAtLjQ2NS4wNDYtLjg5Ni4xMzctMS4yOTJhMy41MyAzLjUzIDAgMCAxIC40MS0xLjA0NmMuMTg3LS4zMDEuNDItLjUzNi42OTctLjcwNC4yODMtLjE2OS42MDktLjI1My45NzgtLjI1My4xIDAgLjIwNy4wMDkuMzIxLjAyNy4xMTkuMDE4LjIwOC4wMzkuMjY3LjA2MlptOS41MyA1LjkxM1Y0MC41aDEuNjU0VjUxaC0xLjQ5N2wtLjE1Ny0xLjUzMVptLTQuODEyLTIuMDg1di0uMTQ0YzAtLjU2LjA2Ni0xLjA3LjE5OC0xLjUzMS4xMzItLjQ2NS4zMjMtLjg2NC41NzQtMS4xOTYuMjUtLjMzNy41NTYtLjU5NS45MTYtLjc3My4zNi0uMTgyLjc2Ni0uMjczIDEuMjE3LS4yNzMuNDQ3IDAgLjgzOC4wODYgMS4xNzYuMjYuMzM3LjE3My42MjQuNDIxLjg2MS43NDUuMjM3LjMxOS40MjYuNzAxLjU2NyAxLjE0OC4xNDIuNDQyLjI0Mi45MzQuMzAxIDEuNDc3di40NThhNy40IDcuNCAwIDAgMS0uMyAxLjQ0OWMtLjE0Mi40MzctLjMzMS44MTYtLjU2OCAxLjEzNWEyLjQ1MiAyLjQ1MiAwIDAgMS0uODY4LjczOGMtLjMzNy4xNzMtLjczMi4yNi0xLjE4My4yNi0uNDQ2IDAtLjg1LS4wOTQtMS4yMS0uMjhhMi42NzMgMi42NzMgMCAwIDEtLjkwOS0uNzg3IDMuNjk5IDMuNjk5IDAgMCAxLS41NzQtMS4xOSA1LjQwNSA1LjQwNSAwIDAgMS0uMTk4LTEuNDk2Wm0xLjY0Ny0uMTQ0di4xNDRjMCAuMzM3LjAzLjY1MS4wODkuOTQzLjA2NC4yOTIuMTYyLjU1LjI5NC43NzMuMTMyLjIxOC4zMDMuMzkyLjUxMy41Mi4yMTQuMTIyLjQ2OS4xODQuNzY1LjE4NC4zNzQgMCAuNjgxLS4wODIuOTIzLS4yNDZhMS42OCAxLjY4IDAgMCAwIC41NjctLjY2M2MuMTQyLS4yODMuMjM3LS41OTguMjg4LS45NDR2LTEuMjM3YTMuMDIzIDMuMDIzIDAgMCAwLS4xNzEtLjc1MiAyLjAxMyAyLjAxMyAwIDAgMC0uMzM1LS42MDkgMS40MSAxLjQxIDAgMCAwLS41MjctLjQxYy0uMjA1LS4xLS40NDgtLjE1LS43MzEtLjE1LS4zIDAtLjU1Ni4wNjQtLjc2Ni4xOTEtLjIxLjEyOC0uMzgzLjMwMy0uNTIuNTI3LS4xMzEuMjIzLS4yMy40ODMtLjI5My43NzlhNC41MDggNC41MDggMCAwIDAtLjA5Ni45NVptOC41NTYtMy42MzdWNTFoLTEuNjU1di03LjM5N2gxLjY1NVptLTEuNzY0LTEuOTRjMC0uMjUyLjA4Mi0uNDU5LjI0Ni0uNjIzLjE2OS0uMTY5LjQwMS0uMjUzLjY5Ny0uMjUzLjI5MiAwIC41MjIuMDg0LjY5LjI1M2EuODMxLjgzMSAwIDAgMSAuMjU0LjYyMi44MjMuODIzIDAgMCAxLS4yNTMuNjE1Yy0uMTY5LjE2NC0uMzk5LjI0Ni0uNjkuMjQ2LS4yOTcgMC0uNTMtLjA4Mi0uNjk4LS4yNDZhLjgzNC44MzQgMCAwIDEtLjI0Ni0uNjE1Wm0zLjUwNCA1Ljcydi0uMTU2YzAtLjUzNC4wNzgtMS4wMjguMjMyLTEuNDg0LjE1NS0uNDYuMzc5LS44NTkuNjctMS4xOTZhMy4wMjUgMy4wMjUgMCAwIDEgMS4wOC0uNzkzIDMuNTE2IDMuNTE2IDAgMCAxIDEuNDUtLjI4N2MuNTQyIDAgMS4wMjUuMDk2IDEuNDQ5LjI4Ny40MjguMTg3Ljc5LjQ1MSAxLjA4Ny43OTMuMjk2LjMzNy41MjIuNzM2LjY3NiAxLjE5Ni4xNTUuNDU2LjIzMy45NS4yMzMgMS40ODR2LjE1N2MwIC41MzMtLjA3OCAxLjAyNy0uMjMzIDEuNDgzYTMuNTUgMy41NSAwIDAgMS0uNjc2IDEuMTk3IDMuMTM3IDMuMTM3IDAgMCAxLTEuMDguNzkyIDMuNTQgMy41NCAwIDAgMS0xLjQ0My4yOCAzLjYwNyAzLjYwNyAwIDAgMS0xLjQ1Ni0uMjggMy4xMzUgMy4xMzUgMCAwIDEtMS4wOC0uNzkzIDMuNTUxIDMuNTUxIDAgMCAxLS42NzctMS4xOTYgNC41ODIgNC41ODIgMCAwIDEtLjIzMi0xLjQ4M1ptMS42NDctLjE1NnYuMTU3YzAgLjMzMy4wMzUuNjQ3LjEwMy45NDNhMi40IDIuNCAwIDAgMCAuMzIxLjc4Yy4xNDYuMjIzLjMzMy4zOTguNTYuNTI2LjIyOS4xMjcuNS4xOTEuODE0LjE5MS4zMDYgMCAuNTctLjA2NC43OTMtLjE5MS4yMjgtLjEyOC40MTUtLjMwMy41Ni0uNTI3LjE0Ny0uMjIzLjI1NC0uNDgzLjMyMi0uNzc5LjA3My0uMjk2LjExLS42MS4xMS0uOTQzdi0uMTU3YzAtLjMyOS0uMDM3LS42MzgtLjExLS45M2EyLjM4NyAyLjM4NyAwIDAgMC0uMzI4LS43ODYgMS41NjkgMS41NjkgMCAwIDAtLjU2LS41MzQgMS41NDMgMS41NDMgMCAwIDAtLjgtLjE5OGMtLjMxIDAtLjU4LjA2Ni0uODA3LjE5OGExLjU4NyAxLjU4NyAwIDAgMC0uNTU0LjUzNGMtLjE0NS4yMjgtLjI1My40OS0uMzIxLjc4NmE0LjA2MiA0LjA2MiAwIDAgMC0uMTAzLjkzWm03Ljk2OC0zLjYyMyAxLjQwOCAyLjQ1NCAxLjQzNi0yLjQ1NWgxLjgxMmwtMi4yNDMgMy42M0wxMDEuNjMgNTFoLTEuODExbC0xLjUwNC0yLjU1N0w5Ni44MSA1MWgtMS44MThsMi4zMjQtMy43NjctMi4yMzUtMy42M2gxLjgwNFptNy43OTcgMFY1MWgtMS42NTR2LTcuMzk3aDEuNjU0Wm0tMS43NjQtMS45NDJhLjg0Ljg0IDAgMCAxIC4yNDctLjYyMmMuMTY4LS4xNjkuNDAxLS4yNTMuNjk3LS4yNTMuMjkxIDAgLjUyMi4wODQuNjkuMjUzYS44My44MyAwIDAgMSAuMjUzLjYyMi44MjIuODIyIDAgMCAxLS4yNTMuNjE1Yy0uMTY4LjE2NC0uMzk5LjI0Ni0uNjkuMjQ2LS4yOTYgMC0uNTI5LS4wODItLjY5Ny0uMjQ2YS44MzIuODMyIDAgMCAxLS4yNDctLjYxNVptOC4zMzEgNy44MDdWNDAuNWgxLjY1NFY1MWgtMS40OTdsLS4xNTctMS41MzFabS00LjgxMy0yLjA4NXYtLjE0NGMwLS41Ni4wNjYtMS4wNy4xOTgtMS41MzEuMTMyLS40NjUuMzI0LS44NjQuNTc0LTEuMTk2LjI1MS0uMzM3LjU1Ni0uNTk1LjkxNi0uNzczYTIuNjYgMi42NiAwIDAgMSAxLjIxNy0uMjczYy40NDcgMCAuODM5LjA4NiAxLjE3Ni4yNi4zMzcuMTczLjYyNC40MjEuODYxLjc0NS4yMzcuMzE5LjQyNy43MDEuNTY4IDEuMTQ4LjE0MS40NDIuMjQxLjkzNC4zMDEgMS40Nzd2LjQ1OGE3LjQ3MyA3LjQ3MyAwIDAgMS0uMzAxIDEuNDQ5IDMuODg2IDMuODg2IDAgMCAxLS41NjggMS4xMzUgMi40NSAyLjQ1IDAgMCAxLS44NjguNzM4Yy0uMzM3LjE3My0uNzMxLjI2LTEuMTgyLjI2LS40NDcgMC0uODUtLjA5NC0xLjIxLS4yOGEyLjY3NSAyLjY3NSAwIDAgMS0uOTEtLjc4NyAzLjcxIDMuNzEgMCAwIDEtLjU3NC0xLjE5IDUuNDEzIDUuNDEzIDAgMCAxLS4xOTgtMS40OTZabTEuNjQ3LS4xNDR2LjE0NGMwIC4zMzcuMDMuNjUxLjA4OS45NDMuMDY0LjI5Mi4xNjIuNTUuMjk0Ljc3My4xMzIuMjE4LjMwMy4zOTIuNTEzLjUyLjIxNC4xMjIuNDY5LjE4NC43NjYuMTg0LjM3MyAwIC42ODEtLjA4Mi45MjItLjI0NmExLjY4IDEuNjggMCAwIDAgLjU2OC0uNjYzYy4xNDEtLjI4My4yMzctLjU5OC4yODctLjk0NHYtMS4yMzdhMi45ODggMi45ODggMCAwIDAtLjE3MS0uNzUyIDIuMDE4IDIuMDE4IDAgMCAwLS4zMzUtLjYwOSAxLjQxMyAxLjQxMyAwIDAgMC0uNTI2LS40MSAxLjY1IDEuNjUgMCAwIDAtLjczMi0uMTVjLS4zMDEgMC0uNTU2LjA2NC0uNzY1LjE5MS0uMjEuMTI4LS4zODMuMzAzLS41Mi41MjdhMi42IDIuNiAwIDAgMC0uMjk0Ljc3OSA0LjQ5NSA0LjQ5NSAwIDAgMC0uMDk2Ljk1Wm0xMC4wNTMgMy44OTdhMy45NDEgMy45NDEgMCAwIDEtMS40ODMtLjI2NyAzLjI3IDMuMjcgMCAwIDEtMS4xMjEtLjc1OSAzLjM1MiAzLjM1MiAwIDAgMS0uNzA0LTEuMTQxIDMuOTk5IDMuOTk5IDAgMCAxLS4yNDYtMS40MTV2LS4yNzRjMC0uNTc4LjA4NC0xLjEwMy4yNTItMS41NzIuMTY5LS40Ny40MDQtLjg3LjcwNS0xLjIwMy4zLS4zMzcuNjU2LS41OTUgMS4wNjYtLjc3My40MS0uMTc3Ljg1NC0uMjY2IDEuMzMzLS4yNjYuNTI5IDAgLjk5MS4wODkgMS4zODguMjY2LjM5Ni4xNzguNzI0LjQyOS45ODQuNzUyLjI2NC4zMi40Ni43LjU4OCAxLjE0Mi4xMzIuNDQyLjE5OC45My4xOTggMS40NjN2LjcwNGgtNS43MTVWNDYuNjFoNC4wODh2LS4xM2EyLjI5NyAyLjI5NyAwIDAgMC0uMTc4LS44MzQgMS40MDkgMS40MDkgMCAwIDAtLjQ4NS0uNjI4Yy0uMjE5LS4xNi0uNTEtLjI0LS44NzUtLjI0LS4yNzMgMC0uNTE3LjA2LS43MzEuMTc4LS4yMS4xMTQtLjM4NS4yOC0uNTI3LjUtLjE0MS4yMTgtLjI1LjQ4Mi0uMzI4Ljc5Mi0uMDczLjMwNS0uMTA5LjY1LS4xMDkgMS4wMzJ2LjI3NGMwIC4zMjMuMDQzLjYyNC4xMy45MDIuMDkxLjI3My4yMjMuNTEzLjM5Ni43MTguMTczLjIwNS4zODMuMzY3LjYyOS40ODUuMjQ2LjExNC41MjYuMTcxLjg0MS4xNzEuMzk2IDAgLjc1LS4wOCAxLjA1OS0uMjQuMzEtLjE1OS41NzktLjM4NC44MDctLjY3NmwuODY4Ljg0YTMuMzc2IDMuMzc2IDAgMCAxLS42MjIuNjdjLS4yNTUuMjEtLjU2Ny4zODEtLjkzNi41MTRhMy43MzIgMy43MzIgMCAwIDEtMS4yNzIuMTk4WiIvPjxwYXRoIGZpbGw9IiMzQjkxMUMiIGQ9Im0xNi4yNTQgMTA2LjA2Ni0yLjI1LS41NS45MjYtOC41NzloOS4xOTl2Mi4zOTFoLTYuODU2bC0uNDY4IDQuMTg0Yy4yNjUtLjE1Ny42MzMtLjMwOSAxLjEwMS0uNDU3LjQ2OS0uMTU3IDEuMDA0LS4yMzUgMS42MDYtLjIzNS44MDQgMCAxLjUyMy4xMzMgMi4xNTYuMzk5LjY0LjI1OCAxLjE4NC42MzYgMS42MjkgMS4xMzYuNDQ1LjQ5My43ODUgMS4wOTQgMS4wMiAxLjgwNS4yMzQuNzAzLjM1MSAxLjQ5Ni4zNTEgMi4zNzkgMCAuNzg5LS4xMTcgMS41MjctLjM1MiAyLjIxNWE1LjE1OSA1LjE1OSAwIDAgMS0xLjAzIDEuODE2IDQuNzQgNC43NCAwIDAgMS0xLjc0NyAxLjIzMWMtLjY5NS4yODktMS41Mi40MzMtMi40NzMuNDMzYTYuOSA2LjkgMCAwIDEtMi4wNS0uMzA0IDUuNTk3IDUuNTk3IDAgMCAxLTEuNzQ2LS45MjYgNC43MTcgNC43MTcgMCAwIDEtMS4yNDMtMS41MjRjLS4zMTItLjYwOS0uNDkyLTEuMzA4LS41MzktMi4wOTdoMi43NjZjLjA3LjU1NS4yMjMgMS4wMjcuNDU3IDEuNDE4LjI0Mi4zODMuNTYyLjY3Ni45Ni44NzkuNC4yMDMuODYuMzA0IDEuMzg0LjMwNC40NzYgMCAuODg2LS4wODIgMS4yMy0uMjQ2LjM0NC0uMTcyLjYzLS40MTQuODU2LS43MjYuMjM0LS4zMjEuNDA2LS42OTYuNTE1LTEuMTI1LjExNy0uNDMuMTc2LS45MDcuMTc2LTEuNDMgMC0uNS0uMDY2LS45NTctLjItMS4zNzFhMi45NzQgMi45NzQgMCAwIDAtLjU3My0xLjA3OCAyLjQ1MyAyLjQ1MyAwIDAgMC0uOTUtLjcwM2MtLjM4Mi0uMTcyLS44MjQtLjI1OC0xLjMyNC0uMjU4LS42NzIgMC0xLjE4Ny4wOTgtMS41NDcuMjkzLS4zNTEuMTk1LS42OC40MzctLjk4NC43MjZabTIxLjc5Ny0yLjAyN3YyLjc4OWMwIDEuMzM2LS4xMzMgMi40NzctLjM5OSAzLjQyMi0uMjU4LjkzOC0uNjMzIDEuNjk5LTEuMTI1IDIuMjg1YTQuNDEgNC40MSAwIDAgMS0xLjc3IDEuMjg5Yy0uNjc5LjI3NC0xLjQ0LjQxLTIuMjg0LjQxYTYuNTg0IDYuNTg0IDAgMCAxLTEuODc1LS4yNTcgNC41ODYgNC41ODYgMCAwIDEtMS41NDctLjgwOSA0LjgzMyA0LjgzMyAwIDAgMS0xLjE4NC0xLjQxOGMtLjMyLS41ODYtLjU3LTEuMjg1LS43NS0yLjA5OC0uMTcyLS44MTItLjI1OC0xLjc1NC0uMjU4LTIuODI0di0yLjc4OWMwLTEuMzQ0LjEzMy0yLjQ3Ny4zOTktMy4zOTguMjY1LS45My42NDQtMS42ODQgMS4xMzctMi4yNjJhNC4zMyA0LjMzIDAgMCAxIDEuNzU3LTEuMjc3Yy42ODgtLjI2NiAxLjQ1My0uMzk5IDIuMjk3LS4zOTkuNjggMCAxLjMwNS4wODYgMS44NzUuMjU4YTQuMzkgNC4zOSAwIDAgMSAxLjU0Ny43ODVjLjQ2MS4zNi44NTIuODI4IDEuMTcyIDEuNDA2LjMyOC41Ny41NzggMS4yNjIuNzUgMi4wNzUuMTcyLjgwNC4yNTggMS43NDIuMjU4IDIuODEyWm0tMi44MjQgMy4xODh2LTMuNjFjMC0uNjc5LS4wNC0xLjI3Ny0uMTE4LTEuNzkzLS4wNzgtLjUyMy0uMTk1LS45NjUtLjM1MS0xLjMyNGEyLjY0IDIuNjQgMCAwIDAtLjU2My0uODkgMS45OCAxLjk4IDAgMCAwLS43NzMtLjUwNCAyLjczMSAyLjczMSAwIDAgMC0uOTczLS4xNjVjLS40MzcgMC0uODI4LjA4Ni0xLjE3Mi4yNTgtLjM0My4xNjQtLjYzMy40My0uODY3Ljc5Ny0uMjM0LjM2Ny0uNDE0Ljg1Mi0uNTM5IDEuNDUzLS4xMTcuNTk0LS4xNzYgMS4zMTctLjE3NiAyLjE2OHYzLjYxYzAgLjY4Ny4wNCAxLjI5My4xMTcgMS44MTYuMDc5LjUyMy4xOTYuOTczLjM1MiAxLjM0OC4xNTYuMzY3LjM0NC42NzEuNTYzLjkxNGEyLjEgMi4xIDAgMCAwIC43NzMuNTE1Yy4yOTcuMTEuNjIxLjE2NC45NzMuMTY0LjQ0NSAwIC44NC0uMDg2IDEuMTgzLS4yNTcuMzQ0LS4xNzIuNjMzLS40NDYuODY3LS44MjEuMjM1LS4zODMuNDEtLjg3OS41MjgtMS40ODguMTE3LS42MDkuMTc2LTEuMzQuMTc2LTIuMTkxWk01Mi41OTMgMTAxLjMyaDIuODM2VjExNGgtMi42MTNsLS4yMjMtMi45M3YtOS43NVptLjQxIDYuNzg1IDEuMTE0LS4wMjNjMCAuODkxLS4wODYgMS43MTEtLjI1OCAyLjQ2MS0uMTY0Ljc1LS40MjIgMS40MDItLjc3MyAxLjk1N2EzLjcgMy43IDAgMCAxLTEuMzEzIDEuMjg5Yy0uNTMxLjMwNS0xLjE2LjQ1Ny0xLjg4Ny40NTdhNC42MjkgNC42MjkgMCAwIDEtMS41NTgtLjI0NiAzLjEgMy4xIDAgMCAxLTEuMTk1LS43OTdjLS4zMzYtLjM1OS0uNjE0LS44MjgtLjgzMy0xLjQwNmwtLjM2My0zLjE5OWgxLjYwNmMwIC43MDMuMDU4IDEuMjc3LjE3NSAxLjcyMi4xMTguNDQ2LjI4Mi43ODkuNDkzIDEuMDMyLjIxLjIzNC40NTcuMzk4LjczOC40OTIuMjg5LjA5NC42MDIuMTQuOTM4LjE0LjYxNyAwIDEuMTI0LS4wOTMgMS41MjMtLjI4MS40MDYtLjE5NS43MjMtLjQ2NS45NS0uODA4LjIzMy0uMzUyLjM5OC0uNzYyLjQ5MS0xLjIzMS4xMDItLjQ3Ni4xNTMtLjk5Ni4xNTMtMS41NTlabS04LjI3My02Ljc4NWgyLjgxM3YxNy41NTVINDQuNzNWMTAxLjMyWm0yMi4xMjUgMGgyLjU2N3YxMi4zMjhjMCAxLjE0MS0uMjQzIDIuMTEtLjcyNyAyLjkwN2E0LjY2OCA0LjY2OCAwIDAgMS0yLjAyNyAxLjgxNmMtLjg2OC40MjItMS44NzEuNjMzLTMuMDEyLjYzMy0uNDg0IDAtMS4wMjMtLjA3LTEuNjE3LS4yMTFhNi42MjQgNi42MjQgMCAwIDEtMS43MTEtLjY4IDQuMjM0IDQuMjM0IDAgMCAxLTEuMzcxLTEuMjA3bDEuMzI0LTEuNjY0Yy40NTMuNTM5Ljk1My45MzQgMS41IDEuMTg0LjU0Ny4yNSAxLjEyMS4zNzUgMS43MjMuMzc1LjY0OCAwIDEuMTk5LS4xMjEgMS42NTItLjM2My40Ni0uMjM1LjgxNi0uNTgzIDEuMDY2LTEuMDQzLjI1LS40NjEuMzc1LTEuMDI0LjM3NS0xLjY4OHYtOS41MTZsLjI1OC0yLjg3MVptLTguNjEzIDYuNDgxdi0uMjQ2YzAtLjk2MS4xMTctMS44MzYuMzUyLTIuNjI1LjIzNC0uNzk3LjU3LTEuNDgxIDEuMDA3LTIuMDUxYTQuMzkgNC4zOSAwIDAgMSAxLjU5NC0xLjMyNGMuNjI1LS4zMTMgMS4zMzItLjQ2OSAyLjEyMS0uNDY5LjgyIDAgMS41Mi4xNDggMi4wOTguNDQ1YTMuOTYzIDMuOTYzIDAgMCAxIDEuNDY1IDEuMjc4Yy4zOS41NDYuNjk1IDEuMjAzLjkxNCAxLjk2OC4yMjYuNzU4LjM5NCAxLjYwMi41MDQgMi41MzJ2Ljc4NWExMi42NzQgMTIuNjc0IDAgMCAxLS41MTYgMi40ODRjLS4yNDIuNzUtLjU2MyAxLjM5OS0uOTYgMS45NDUtLjQuNTQ3LS44OTIuOTY5LTEuNDc3IDEuMjY2LS41NzkuMjk3LTEuMjYyLjQ0NS0yLjA1MS40NDUtLjc3NCAwLTEuNDczLS4xNi0yLjA5OC0uNDhhNC43NzMgNC43NzMgMCAwIDEtMS41OTQtMS4zNDggNi4zODQgNi4zODQgMCAwIDEtMS4wMDgtMi4wMzkgOC45NzMgOC45NzMgMCAwIDEtLjM1MS0yLjU2NlptMi44MjQtLjI0NnYuMjQ2YzAgLjU3OC4wNTUgMS4xMTcuMTY0IDEuNjE3LjExNy41LjI5My45NDEuNTI4IDEuMzI0LjI0Mi4zNzUuNTQ2LjY3Mi45MTQuODkxLjM3NS4yMTEuODE2LjMxNiAxLjMyNC4zMTYuNjY0IDAgMS4yMDctLjE0IDEuNjI5LS40MjIuNDMtLjI4MS43NTgtLjY2Ljk4NC0xLjEzNmE1Ljc1OSA1Ljc1OSAwIDAgMCAuNDkyLTEuNjE4di0yLjEyMWE1LjE4MiA1LjE4MiAwIDAgMC0uMjkzLTEuMjg5IDMuNDU1IDMuNDU1IDAgMCAwLS41NzQtMS4wNDMgMi40MDMgMi40MDMgMCAwIDAtLjkxNC0uNzAzYy0uMzY3LS4xNzItLjgtLjI1OC0xLjMtLjI1OC0uNTA4IDAtLjk1LjExLTEuMzI1LjMyOWEyLjY4IDIuNjggMCAwIDAtLjkyNi45MDJjLS4yMzQuMzgzLS40MS44MjgtLjUyNyAxLjMzNmE3LjIzOSA3LjIzOSAwIDAgMC0uMTc2IDEuNjI5Wk03OS43NyA5Ni45MzdsLTYuNjQ0IDE4LjUyOEg3MC45MWw2LjY1Ni0xOC41MjhoMi4yMDNabTQuNzgxIDYuOTYxVjExNGgtMi44MjR2LTEyLjY4aDIuNjZsLjE2NSAyLjU3OFptLS40NTYgMy4yOTMtLjk2MS0uMDExYzAtLjg3NS4xMDktMS42ODQuMzI4LTIuNDI2LjIxOC0uNzQyLjUzOS0xLjM4Ny45Ni0xLjkzNGE0LjIzIDQuMjMgMCAwIDEgMS41Ny0xLjI3N2MuNjM0LS4zMDUgMS4zNjQtLjQ1NyAyLjE5Mi0uNDU3LjU3OCAwIDEuMTA2LjA4NiAxLjU4Mi4yNTguNDg1LjE2NC45MDMuNDI2IDEuMjU0Ljc4NS4zNi4zNTkuNjMzLjgyLjgyIDEuMzgzLjE5Ni41NjIuMjk0IDEuMjQyLjI5NCAyLjAzOVYxMTRoLTIuODI1di04LjIwM2MwLS42MTctLjA5My0xLjEwMi0uMjgtMS40NTNhMS41NTUgMS41NTUgMCAwIDAtLjc4Ni0uNzVjLS4zMzYtLjE1Ni0uNzM4LS4yMzUtMS4yMDctLjIzNS0uNTMxIDAtLjk4NC4xMDItMS4zNi4zMDVhMi41NjMgMi41NjMgMCAwIDAtLjkwMi44MzIgMy43MyAzLjczIDAgMCAwLS41MTUgMS4yMTljLS4xMS40NjEtLjE2NC45NTMtLjE2NCAxLjQ3NlptNy44NjMtLjc1LTEuMzI0LjI5M2MwLS43NjUuMTA1LTEuNDg4LjMxNi0yLjE2OGE1LjUyIDUuNTIgMCAwIDEgLjk1LTEuODA0IDQuNDUgNC40NSAwIDAgMSAxLjU1OC0xLjIzMWMuNjE3LS4yOTcgMS4zMjQtLjQ0NSAyLjEyLS40NDUuNjUgMCAxLjIyNy4wOSAxLjczNS4yNjkuNTE2LjE3Mi45NTMuNDQ2IDEuMzEzLjgyMS4zNi4zNzUuNjMzLjg2My44MiAxLjQ2NS4xODguNTkzLjI4MSAxLjMxMi4yODEgMi4xNTZWMTE0SDk2Ljg5di04LjIxNWMwLS42NC0uMDkzLTEuMTM3LS4yOC0xLjQ4OGExLjQ1OCAxLjQ1OCAwIDAgMC0uNzc0LS43MjdjLS4zMzYtLjE0LS43MzktLjIxMS0xLjIwNy0uMjExLS40MzggMC0uODI1LjA4Mi0xLjE2LjI0NmEyLjMxIDIuMzEgMCAwIDAtLjgzMi42NjggMi45ODcgMi45ODcgMCAwIDAtLjUxNi45NzNjLS4xMS4zNjctLjE2NC43NjYtLjE2NCAxLjE5NVptMTIuMzg3LTUuNzA3aDEuMDE5Yy4zMzYgMCAuNjEzLS4wNDYuODMyLS4xNC4yMTktLjEwMi4zODMtLjIzOS40OTItLjQxLjExNy0uMTcyLjE3Ni0uMzc1LjE3Ni0uNjFhLjk5NC45OTQgMCAwIDAtLjMyOC0uNzM4Yy0uMjExLS4yMTktLjU2Ni0uMzI4LTEuMDY2LS4zMjgtLjM4MyAwLS43MDguMDgyLS45NzMuMjQ2LS4yNTguMTU2LS4zODcuMzc5LS4zODcuNjY4aC0yLjEzM2MwLS41NTUuMTU3LTEuMDI4LjQ2OS0xLjQxOC4zMTMtLjM5LjczMS0uNjg4IDEuMjU0LS44OWE0LjY5IDQuNjkgMCAwIDEgMS43MTEtLjMwNWMuNzExIDAgMS4zMzYuMTAxIDEuODc1LjMwNC41MzkuMTk2Ljk1Ny40ODkgMS4yNTQuODguMzA1LjM5LjQ1Ny44NzQuNDU3IDEuNDUyIDAgLjQ0Ni0uMTQxLjg0LS40MjIgMS4xODQtLjI3My4zNDQtLjY1Ni42MTMtMS4xNDguODA5LS40ODUuMTg3LTEuMDU1LjI4MS0xLjcxMS4yODFoLTEuMzcxdi0uOTg1Wm0wIDEuNDY1di0uOTYxaDEuMzcxYy43MjYgMCAxLjM0Ny4wODYgMS44NjMuMjU4LjUxNi4xNjQuOTA2LjQxOCAxLjE3Mi43NjIuMjczLjM0NC40MS43ODkuNDEgMS4zMzYgMCAuNTc4LS4xNjQgMS4wNy0uNDkyIDEuNDc2LS4zMjguNDA3LS43NzguNzE1LTEuMzQ4LjkyNi0uNTYyLjIxMS0xLjE5NS4zMTYtMS44OTguMzE2YTUuMjkgNS4yOSAwIDAgMS0xLjcxMS0uMjgxYy0uNTQ3LS4xOTUtMS0uNS0xLjM2LS45MTQtLjM1MS0uNDE0LS41MjctLjk1Ny0uNTI3LTEuNjI5aDIuMTQ1YzAgLjMwNS4xNC41NjcuNDIxLjc4NS4yODIuMjE5LjY2MS4zMjkgMS4xMzcuMzI5LjUwOCAwIC44ODMtLjExIDEuMTI1LS4zMjlhMS4wOSAxLjA5IDAgMCAwIC4zNzUtLjg0M2MwLS4yOTctLjA3LS41MzUtLjIxMS0uNzE1YTEuMDkxIDEuMDkxIDAgMCAwLS41NzQtLjM4NyAyLjcwNSAyLjcwNSAwIDAgMC0uODc5LS4xMjloLTEuMDE5WiIvPjxwYXRoIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2Utb3BhY2l0eT0iLjg3IiBzdHJva2Utd2lkdGg9IjIiIGQ9Im0xMTEuNzYgMTE3LjE0OCA3LjIxNy04Ljc4M2E2Ljc4MiA2Ljc4MiAwIDAgMCAxLjQyLTMuMDE2bDMuMjU1LTE2LjcyM2MuNzMtMy43NTEgNi4wOTgtMy43NTEgNi44MjggMGwuMjA4IDEuMDY2Yy43MTMgMy42NjMgNi4wMDMgMy41MTIgNi41MDYtLjE4NXYwYy40NzUtMy40ODUgNS4zNDYtMy45MDYgNi40MTItLjU1NGwyLjMyMiA3LjMwMmMxLjYxNyA1LjA4OCA5LjAwMyA0LjQ3NyA5Ljc2Mi0uODA3bDEuMTA3LTcuNjk0YzEuMDU3LTcuMzU3IDExLjQ1OC03LjkzNiAxMy4zMjUtLjc0MWwxLjAyMiAzLjkzOGMuODgzIDMuNDAyIDQuOTIgNC44MzkgNy43NTMgMi43NnYwYTQuOTcyIDQuOTcyIDAgMCAxIDYuMjgxLjMyNGw0LjQyNyA0LjAxNCIvPjxkZWZzPjxmaWx0ZXIgaWQ9ImEiIHdpZHRoPSIxOTEuMDIzIiBoZWlnaHQ9Ijk5LjAyMyIgeD0iNC40ODkiIHk9IjMwLjQ4OSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMi4yNTYiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTA3MV8yNzE2Ii8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMDcxXzI3MTYiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48L2RlZnM+PC9zdmc+", + "description": "Produced by volcanoes and industrial processes, particularly the burning of coal and oil. Results displayed by combining the latest and aggregated values and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'sulfur', label: 'Sulfur dioxide', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'sulfur', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sulfur dioxide\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 600) {\\n\\tvalue = 600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 600) {\\n\\tvalue = 600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#3FA71A\"},{\"from\":100,\"to\":200,\"color\":\"#80C32C\"},{\"from\":200,\"to\":350,\"color\":\"#FFA600\"},{\"from\":350,\"to\":500,\"color\":\"#F36900\"},{\"from\":500,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Sulfur dioxide\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"public\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"µg/m³\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "sulfur", + "sulfur dioxide" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_sulfur_dioxide__so2__chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_sulfur_dioxide__so2__chart_card_with_background.json new file mode 100644 index 0000000000..32cf5f7027 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_sulfur_dioxide__so2__chart_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "simple_sulfur_dioxide_so2_chart_card_with_background", + "name": "Simple sulfur dioxide (SO2) chart card with background", + "deprecated": false, + "image": "tb-image:U08yLXNpbXBsZS1jaGFydC12YWx1ZS1jYXJkLXdpdGgtYmFja2dyb3VuZC5zdmc=:U08yLXNpbXBsZS1jaGFydC12YWx1ZS1jYXJkLXdpdGgtYmFja2dyb3VuZC5zdmc=;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxMTIiIHk9IjI0IiBmaWxsPSJ1cmwoI2EpIiByeD0iNCIvPjxnIGZpbHRlcj0idXJsKCNiKSI+PHJlY3Qgd2lkdGg9IjE4OCIgaGVpZ2h0PSIxMDAiIHg9IjYiIHk9IjMwIiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9Ii43NiIgcng9IjMuMDA4Ii8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjg3IiBkPSJNMjIuMjQ4IDQ2LjQzN2MwLS4yMDYtLjAzMi0uMzg4LS4wOTYtLjU0N2ExLjA2OCAxLjA2OCAwIDAgMC0uMzItLjQzOCAyLjY1NSAyLjY1NSAwIDAgMC0uNjU3LS4zODMgOC43MDggOC43MDggMCAwIDAtMS4wNjctLjM5IDExLjc5NiAxMS43OTYgMCAwIDEtMS4zMTItLjQ4NSA0Ljk5NiA0Ljk5NiAwIDAgMS0xLjA0LS42MjkgMi42OSAyLjY5IDAgMCAxLS42OS0uODI3IDIuMzE2IDIuMzE2IDAgMCAxLS4yNDYtMS4wODdjMC0uNDA1LjA4NS0uNzc0LjI1My0xLjEwNy4xNzMtLjMzMy40MTctLjYyLjczMi0uODYxYTMuNSAzLjUgMCAwIDEgMS4xMjgtLjU2OCA0Ljc1MSA0Ljc1MSAwIDAgMSAxLjQzNS0uMjA1Yy43MzggMCAxLjM3NC4xMzcgMS45MDcuNDEuNTM4LjI3NC45NS42NCAxLjIzOCAxLjEuMjkxLjQ2MS40MzcuOTcuNDM3IDEuNTI1aC0xLjcwMmMwLS4zMjgtLjA3LS42MTctLjIxMi0uODY4YTEuNDM2IDEuNDM2IDAgMCAwLS42MjktLjYwMWMtLjI3OC0uMTQ2LS42MzEtLjIyLTEuMDYtLjIyLS40MDUgMC0uNzQyLjA2Mi0xLjAxMS4xODVhMS4zNSAxLjM1IDAgMCAwLS42MDIuNWMtLjEzMi4yMS0uMTk4LjQ0Ni0uMTk4LjcxIDAgLjE4Ny4wNDMuMzU4LjEzLjUxMy4wODcuMTUuMjE5LjI5Mi4zOTYuNDI0LjE3OC4xMjcuNDAxLjI0OC42Ny4zNjIuMjcuMTE0LjU4Ni4yMjMuOTUuMzI4YTkuNDE2IDkuNDE2IDAgMCAxIDEuNDQzLjU0N2MuNDEuMTk2Ljc1Mi40MiAxLjAyNS42Ny4yNzQuMjUuNDc5LjUzNi42MTYuODU1LjEzNi4zMTQuMjA1LjY3Mi4yMDUgMS4wNzMgMCAuNDItLjA4NS43OTctLjI1MyAxLjEzNWEyLjQ1IDIuNDUgMCAwIDEtLjcyNS44NTRjLS4zMS4yMzMtLjY4My40MTMtMS4xMi41NGE1LjI5NCA1LjI5NCAwIDAgMS0xLjQ1LjE4NWMtLjQ3OSAwLS45NS0uMDY0LTEuNDE1LS4xOTJhNC4xNzUgNC4xNzUgMCAwIDEtMS4yNTgtLjU4IDMuMDAzIDMuMDAzIDAgMCAxLS45MDItLjk4NWMtLjIyNC0uMzk3LS4zMzUtLjg2LS4zMzUtMS4zODhoMS43MTVjMCAuMzI0LjA1NS42LjE2NS44MjcuMTEzLjIyOC4yNy40MTUuNDcxLjU2LjIuMTQyLjQzMy4yNDcuNjk3LjMxNS4yNy4wNjkuNTU2LjEwMy44NjIuMTAzLjQgMCAuNzM2LS4wNTcgMS4wMDUtLjE3MS4yNzMtLjExNC40NzgtLjI3My42MTUtLjQ3OC4xMzctLjIwNi4yMDUtLjQ0My4yMDUtLjcxMlptNy43NzYuODJ2LTUuNjU0aDEuNjU1VjQ5SDMwLjEybC0uMDk2LTEuNzQzWm0uMjMzLTEuNTM4LjU1My0uMDE0YzAgLjQ5Ny0uMDU0Ljk1NS0uMTYzIDEuMzc0LS4xMS40MTUtLjI3OC43NzctLjUwNiAxLjA4Ny0uMjI4LjMwNS0uNTIuNTQ1LS44NzUuNzE4LS4zNTYuMTY4LS43ODIuMjUzLTEuMjc5LjI1My0uMzYgMC0uNjktLjA1My0uOTktLjE1N2EyLjAzIDIuMDMgMCAwIDEtLjc4LS40ODYgMi4yMDggMi4yMDggMCAwIDEtLjUtLjg1NGMtLjExOC0uMzUxLS4xNzctLjc3LS4xNzctMS4yNTh2LTQuNzc5aDEuNjQ4djQuNzkyYzAgLjI3LjAzMS40OTUuMDk1LjY3Ny4wNjQuMTc4LjE1LjMyMi4yNi40My4xMS4xMS4yMzcuMTg4LjM4My4yMzMuMTQ2LjA0Ni4zLjA2OS40NjUuMDY5LjQ2OSAwIC44MzgtLjA5MSAxLjEwNy0uMjc0LjI3My0uMTg3LjQ2Ny0uNDM3LjU4MS0uNzUyLjExOS0uMzE0LjE3OC0uNjY3LjE3OC0xLjA2Wk0zNS4zOCAzOC41VjQ5aC0xLjY1NFYzOC41aDEuNjU0Wk0zOS42NjQgNDloLTEuNjQ3di04LjEwN2MwLS41NTIuMTAyLTEuMDE0LjMwNy0xLjM4OGEyLjAzIDIuMDMgMCAwIDEgLjg5Ni0uODU1Yy4zODctLjE5Ni44NDUtLjI5NCAxLjM3NC0uMjk0LjE2NCAwIC4zMjYuMDEyLjQ4NS4wMzUuMTYuMDE4LjMxNS4wNDcuNDY1LjA4OWwtLjA0MSAxLjI3MWExLjg4NSAxLjg4NSAwIDAgMC0uMy0uMDQ4IDMuOTY3IDMuOTY3IDAgMCAwLS4zNDMtLjAxNGMtLjI1IDAtLjQ2Ny4wNDgtLjY1LjE0NGEuOTQ4Ljk0OCAwIDAgMC0uNDEuNDAzIDEuNDI3IDEuNDI3IDAgMCAwLS4xMzYuNjU3VjQ5Wm0xLjUyNC03LjM5N3YxLjIwNGgtNC4zMDZ2LTEuMjA0aDQuMzA2Wm01Ljg5NyA1LjY1NHYtNS42NTRoMS42NTRWNDloLTEuNTU4bC0uMDk2LTEuNzQzWm0uMjMyLTEuNTM4LjU1NC0uMDE0YzAgLjQ5Ny0uMDU1Ljk1NS0uMTY0IDEuMzc0LS4xMS40MTUtLjI3OC43NzctLjUwNiAxLjA4Ny0uMjI4LjMwNS0uNTIuNTQ1LS44NzUuNzE4LS4zNTUuMTY4LS43ODEuMjUzLTEuMjc4LjI1My0uMzYgMC0uNjktLjA1My0uOTkxLS4xNTctLjMwMS0uMTA1LS41Ni0uMjY3LS43OC0uNDg2YTIuMjEgMi4yMSAwIDAgMS0uNDk5LS44NTRjLS4xMTgtLjM1MS0uMTc3LS43Ny0uMTc3LTEuMjU4di00Ljc3OWgxLjY0N3Y0Ljc5MmMwIC4yNy4wMzIuNDk1LjA5Ni42NzcuMDY0LjE3OC4xNS4zMjIuMjYuNDMuMTA5LjExLjIzNi4xODguMzgyLjIzMy4xNDYuMDQ2LjMwMS4wNjkuNDY1LjA2OS40NyAwIC44MzktLjA5MSAxLjEwOC0uMjc0LjI3My0uMTg3LjQ2Ny0uNDM3LjU4LS43NTIuMTItLjMxNC4xNzgtLjY2Ny4xNzgtMS4wNlptNS4wMTUtMi43MDdWNDloLTEuNjQ3di03LjM5N2gxLjU3MmwuMDc1IDEuNDA5Wm0yLjI2My0xLjQ1Ni0uMDE0IDEuNTNhNC4yOSA0LjI5IDAgMCAwLS42Ny0uMDU1Yy0uMjgyLjAwMS0uNTMuMDQyLS43NDUuMTI0YTEuNDAzIDEuNDAzIDAgMCAwLS41NC4zNDJjLS4xNDEuMTQ2LS4yNS4zMjQtLjMyOC41MzMtLjA3OC4yMS0uMTIzLjQ0NS0uMTM3LjcwNGwtLjM3Ni4wMjhjMC0uNDY1LjA0Ni0uODk2LjEzNy0xLjI5Mi4wOTEtLjM5Ny4yMjgtLjc0NS40MS0xLjA0Ni4xODctLjMwMS40Mi0uNTM2LjY5Ny0uNzA0YTEuODcgMS44NyAwIDAgMSAuOTc4LS4yNTNjLjEgMCAuMjA3LjAwOS4zMjEuMDI3LjExOS4wMTguMjA3LjAzOS4yNjcuMDYyWm05LjUzIDUuOTEzVjM4LjVoMS42NTRWNDloLTEuNDk3bC0uMTU3LTEuNTMxWm0tNC44MTMtMi4wODV2LS4xNDRjMC0uNTYuMDY3LTEuMDcuMTk5LTEuNTMxLjEzMi0uNDY1LjMyMy0uODY0LjU3NC0xLjE5Ni4yNS0uMzM3LjU1Ni0uNTk1LjkxNi0uNzczLjM2LS4xODIuNzY2LS4yNzMgMS4yMTctLjI3My40NDYgMCAuODM4LjA4NiAxLjE3Ni4yNi4zMzcuMTczLjYyNC40MjEuODYuNzQ1LjIzOC4zMTkuNDI3LjcwMS41NjggMS4xNDguMTQxLjQ0Mi4yNDIuOTM0LjMwMSAxLjQ3N3YuNDU4YTcuNCA3LjQgMCAwIDEtLjMgMS40NDljLS4xNDIuNDM3LS4zMzEuODE2LS41NjggMS4xMzVhMi40NTMgMi40NTMgMCAwIDEtLjg2OC43MzhjLS4zMzcuMTczLS43MzIuMjYtMS4xODMuMjYtLjQ0NyAwLS44NS0uMDk0LTEuMjEtLjI4YTIuNjc0IDIuNjc0IDAgMCAxLS45MDktLjc4NyAzLjcwMSAzLjcwMSAwIDAgMS0uNTc0LTEuMTkgNS40MDkgNS40MDkgMCAwIDEtLjE5OS0xLjQ5NlptMS42NDgtLjE0NHYuMTQ0YzAgLjMzNy4wMy42NTEuMDg5Ljk0My4wNjQuMjkyLjE2Mi41NS4yOTQuNzczLjEzMi4yMTguMzAzLjM5Mi41MTIuNTIuMjE1LjEyMi40Ny4xODQuNzY2LjE4NC4zNzQgMCAuNjgxLS4wODIuOTIzLS4yNDYuMjQyLS4xNjQuNDMtLjM4Ni41NjctLjY2M2EzLjA4IDMuMDggMCAwIDAgLjI4Ny0uOTQ0di0xLjIzN2EzLjAxOCAzLjAxOCAwIDAgMC0uMTctLjc1MiAyLjAxMSAyLjAxMSAwIDAgMC0uMzM1LS42MDkgMS40MTIgMS40MTIgMCAwIDAtLjUyNy0uNDEgMS42NSAxLjY1IDAgMCAwLS43MzEtLjE1Yy0uMzAxIDAtLjU1Ni4wNjQtLjc2Ni4xOTEtLjIxLjEyOC0uMzgzLjMwMy0uNTIuNTI3YTIuNTkgMi41OSAwIDAgMC0uMjkzLjc3OSA0LjUwOCA0LjUwOCAwIDAgMC0uMDk2Ljk1Wm04LjU1Ni0zLjYzN1Y0OUg2Ny44NnYtNy4zOTdoMS42NTVabS0xLjc2NC0xLjk0YzAtLjI1Mi4wODItLjQ1OS4yNDYtLjYyMy4xNjktLjE2OS40MDEtLjI1My42OTctLjI1My4yOTIgMCAuNTIyLjA4NC42OS4yNTNhLjgzMS44MzEgMCAwIDEgLjI1NC42MjIuODIzLjgyMyAwIDAgMS0uMjUzLjYxNWMtLjE2OS4xNjQtLjM5OS4yNDYtLjY5LjI0Ni0uMjk3IDAtLjUzLS4wODItLjY5OC0uMjQ2YS44MzUuODM1IDAgMCAxLS4yNDYtLjYxNVptMy41MDQgNS43MnYtLjE1NmMwLS41MzQuMDc3LTEuMDI4LjIzMi0xLjQ4NC4xNTUtLjQ2LjM3OS0uODU5LjY3LTEuMTk2YTMuMDI0IDMuMDI0IDAgMCAxIDEuMDgtLjc5MyAzLjUxNiAzLjUxNiAwIDAgMSAxLjQ1LS4yODdjLjU0MiAwIDEuMDI1LjA5NiAxLjQ0OS4yODcuNDI4LjE4Ny43OS40NTEgMS4wODcuNzkzLjI5Ni4zMzcuNTIxLjczNi42NzYgMS4xOTYuMTU1LjQ1Ni4yMzMuOTUuMjMzIDEuNDg0di4xNTdjMCAuNTMzLS4wNzggMS4wMjctLjIzMyAxLjQ4M2EzLjU1IDMuNTUgMCAwIDEtLjY3NiAxLjE5NyAzLjEzNiAzLjEzNiAwIDAgMS0xLjA4Ljc5MmMtLjQyNC4xODctLjkwNS4yOC0xLjQ0My4yOGEzLjYwNyAzLjYwNyAwIDAgMS0xLjQ1Ni0uMjggMy4xMzcgMy4xMzcgMCAwIDEtMS4wOC0uNzkzIDMuNTUgMy41NSAwIDAgMS0uNjc3LTEuMTk2IDQuNTc5IDQuNTc5IDAgMCAxLS4yMzItMS40ODNabTEuNjQ3LS4xNTZ2LjE1N2MwIC4zMzMuMDM0LjY0Ny4xMDMuOTQzLjA2OC4yOTYuMTc1LjU1Ni4zMjEuNzguMTQ2LjIyMy4zMzMuMzk4LjU2LjUyNi4yMjkuMTI3LjUuMTkxLjgxNC4xOTEuMzA2IDAgLjU3LS4wNjQuNzkzLS4xOTEuMjI4LS4xMjguNDE1LS4zMDMuNTYtLjUyN2EyLjQgMi40IDAgMCAwIC4zMjItLjc3OWMuMDczLS4yOTYuMTEtLjYxLjExLS45NDN2LS4xNTdjMC0uMzI5LS4wMzctLjYzOC0uMTEtLjkzYTIuMzkyIDIuMzkyIDAgMCAwLS4zMjgtLjc4NiAxLjU3IDEuNTcgMCAwIDAtLjU2LS41MzQgMS41NDMgMS41NDMgMCAwIDAtLjgtLjE5OGMtLjMxIDAtLjU4LjA2Ni0uODA3LjE5OGExLjU4OCAxLjU4OCAwIDAgMC0uNTU0LjUzNCAyLjQ4IDIuNDggMCAwIDAtLjMyMS43ODYgNC4wNjIgNC4wNjIgMCAwIDAtLjEwMy45M1ptNy45NjgtMy42MjMgMS40MDggMi40NTQgMS40MzYtMi40NTVoMS44MTFsLTIuMjQyIDMuNjNMODUuNjE1IDQ5aC0xLjgxMUw4Mi4zIDQ2LjQ0MyA4MC43OTYgNDloLTEuODE5bDIuMzI1LTMuNzY3LTIuMjM2LTMuNjNoMS44MDVabTcuNzk3IDBWNDloLTEuNjU0di03LjM5N2gxLjY1NFptLTEuNzY0LTEuOTQyYzAtLjI1LjA4Mi0uNDU4LjI0Ni0uNjIyLjE2OS0uMTY5LjQwMS0uMjUzLjY5OC0uMjUzLjI5MSAwIC41MjIuMDg0LjY5LjI1M2EuODMxLjgzMSAwIDAgMSAuMjUzLjYyMi44MjMuODIzIDAgMCAxLS4yNTMuNjE1Yy0uMTY4LjE2NC0uMzk5LjI0Ni0uNjkuMjQ2LS4yOTcgMC0uNTI5LS4wODItLjY5OC0uMjQ2YS44MzQuODM0IDAgMCAxLS4yNDYtLjYxNVptOC4zMyA3LjgwN1YzOC41aDEuNjU1VjQ5aC0xLjQ5N2wtLjE1OC0xLjUzMVptLTQuODEyLTIuMDg1di0uMTQ0YzAtLjU2LjA2Ni0xLjA3LjE5OC0xLjUzMS4xMzItLjQ2NS4zMjQtLjg2NC41NzQtMS4xOTYuMjUxLS4zMzcuNTU2LS41OTUuOTE2LS43NzMuMzYtLjE4Mi43NjYtLjI3MyAxLjIxNy0uMjczLjQ0NyAwIC44MzkuMDg2IDEuMTc2LjI2LjMzNy4xNzMuNjI0LjQyMS44NjEuNzQ1LjIzNy4zMTkuNDI2LjcwMS41NjggMS4xNDguMTQuNDQyLjI0MS45MzQuMyAxLjQ3N3YuNDU4YTcuMzkyIDcuMzkyIDAgMCAxLS4zIDEuNDQ5Yy0uMTQyLjQzNy0uMzMuODE2LS41NjggMS4xMzVhMi40NTMgMi40NTMgMCAwIDEtLjg2OC43MzhjLS4zMzcuMTczLS43MzEuMjYtMS4xODIuMjYtLjQ0NyAwLS44NS0uMDk0LTEuMjEtLjI4YTIuNjc0IDIuNjc0IDAgMCAxLS45MS0uNzg3IDMuNzAzIDMuNzAzIDAgMCAxLS41NzQtMS4xOSA1LjQwOSA1LjQwOSAwIDAgMS0uMTk4LTEuNDk2Wm0xLjY0Ny0uMTQ0di4xNDRjMCAuMzM3LjAzLjY1MS4wOS45NDMuMDYzLjI5Mi4xNjEuNTUuMjkzLjc3My4xMzIuMjE4LjMwMy4zOTIuNTEzLjUyLjIxNC4xMjIuNDcuMTg0Ljc2Ni4xODQuMzczIDAgLjY4LS4wODIuOTIyLS4yNDYuMjQyLS4xNjQuNDMxLS4zODYuNTY4LS42NjNhMy4wOCAzLjA4IDAgMCAwIC4yODctLjk0NHYtMS4yMzdhMy4wMTggMy4wMTggMCAwIDAtLjE3MS0uNzUyIDIuMDEzIDIuMDEzIDAgMCAwLS4zMzUtLjYwOSAxLjQxMiAxLjQxMiAwIDAgMC0uNTI2LS40MSAxLjY1IDEuNjUgMCAwIDAtLjczMi0uMTVjLS4zIDAtLjU1Ni4wNjQtLjc2Ni4xOTEtLjIxLjEyOC0uMzgyLjMwMy0uNTE5LjUyN2EyLjU5IDIuNTkgMCAwIDAtLjI5NC43NzkgNC41MDggNC41MDggMCAwIDAtLjA5Ni45NVptMTAuMDUzIDMuODk3YTMuOTQxIDMuOTQxIDAgMCAxLTEuNDgzLS4yNjcgMy4yNzEgMy4yNzEgMCAwIDEtMS4xMjEtLjc1OSAzLjM2MSAzLjM2MSAwIDAgMS0uNzA0LTEuMTQxIDMuOTk3IDMuOTk3IDAgMCAxLS4yNDctMS40MTV2LS4yNzRjMC0uNTc4LjA4NS0xLjEwMy4yNTMtMS41NzIuMTY5LS40Ny40MDQtLjg3LjcwNC0xLjIwMy4zMDEtLjMzNy42NTctLjU5NSAxLjA2Ny0uNzczLjQxLS4xNzcuODU0LS4yNjYgMS4zMzMtLjI2Ni41MjggMCAuOTkxLjA4OSAxLjM4OC4yNjYuMzk2LjE3OC43MjQuNDI5Ljk4NC43NTIuMjY0LjMyLjQ2LjcuNTg4IDEuMTQyLjEzMi40NDIuMTk4LjkzLjE5OCAxLjQ2M3YuNzA0aC01LjcxNVY0NC42MWg0LjA4OHYtLjEzYTIuMjk3IDIuMjk3IDAgMCAwLS4xNzgtLjgzNCAxLjQwOSAxLjQwOSAwIDAgMC0uNDg1LS42MjhjLS4yMTktLjE2LS41MS0uMjQtLjg3NS0uMjQtLjI3MyAwLS41MTcuMDYtLjczMS4xNzgtLjIxLjExNC0uMzg2LjI4LS41MjcuNS0uMTQxLjIxOC0uMjUuNDgyLS4zMjguNzkyLS4wNzMuMzA1LS4xMDkuNjUtLjEwOSAxLjAzMnYuMjc0YzAgLjMyMy4wNDMuNjI0LjEzLjkwMi4wOTEuMjczLjIyMy41MTMuMzk2LjcxOC4xNzMuMjA1LjM4My4zNjcuNjI5LjQ4NS4yNDYuMTE0LjUyNi4xNzEuODQxLjE3MS4zOTYgMCAuNzQ5LS4wOCAxLjA1OS0uMjQuMzEtLjE1OS41NzktLjM4NC44MDctLjY3NmwuODY4Ljg0YTMuMzc2IDMuMzc2IDAgMCAxLS42MjIuNjdjLS4yNTUuMjEtLjU2Ny4zODEtLjkzNi41MTRhMy43MzIgMy43MzIgMCAwIDEtMS4yNzIuMTk4WiIvPjxwYXRoIGZpbGw9IiMzQjkxMUMiIGQ9Im0yMC45NjMgMTA2Ljc0NC0yLjYyNS0uNjQyIDEuMDgtMTAuMDA4SDMwLjE1djIuNzg5aC03Ljk5OGwtLjU0NyA0Ljg4MWMuMzEtLjE4My43MzktLjM2IDEuMjg2LS41MzQuNTQ3LS4xODIgMS4xNy0uMjczIDEuODczLS4yNzMuOTM5IDAgMS43NzcuMTU1IDIuNTE1LjQ2NS43NDguMzAxIDEuMzgxLjc0MyAxLjkgMS4zMjYuNTIuNTc0LjkxNyAxLjI3NiAxLjE5IDIuMTA2LjI3NC44Mi40MSAxLjc0NS40MSAyLjc3NSAwIC45Mi0uMTM2IDEuNzgyLS40MSAyLjU4NGE2LjAyIDYuMDIgMCAwIDEtMS4yMDMgMi4xMTljLS41MzguNjExLTEuMjE3IDEuMDg5LTIuMDM3IDEuNDM2LS44MTEuMzM3LTEuNzczLjUwNS0yLjg4NS41MDUtLjgzIDAtMS42MjctLjExOC0yLjM5Mi0uMzU1YTYuNTE3IDYuNTE3IDAgMCAxLTIuMDM4LTEuMDggNS41MTMgNS41MTMgMCAwIDEtMS40NDktMS43NzdjLS4zNjQtLjcxMS0uNTc0LTEuNTI3LS42MjktMi40NDhoMy4yMjdjLjA4Mi42NDcuMjYgMS4xOTkuNTMzIDEuNjU1LjI4My40NDYuNjU2Ljc4OCAxLjEyMSAxLjAyNS40NjUuMjM3IDEuMDAzLjM1NSAxLjYxMy4zNTUuNTU2IDAgMS4wMzUtLjA5NSAxLjQzNi0uMjg3LjQwMS0uMi43MzQtLjQ4My45OTgtLjg0N2EzLjc4IDMuNzggMCAwIDAgLjYwMi0xLjMxMyA2LjMzMSA2LjMzMSAwIDAgMCAuMjA1LTEuNjY4YzAtLjU4My0uMDc4LTEuMTE2LS4yMzMtMS41OTlhMy40NzYgMy40NzYgMCAwIDAtLjY3LTEuMjU4IDIuODY5IDIuODY5IDAgMCAwLTEuMTA3LS44MjFjLS40NDctLjItLjk2Mi0uMy0xLjU0NS0uMy0uNzg0IDAtMS4zODUuMTE0LTEuODA1LjM0MS0uNDEuMjI4LS43OTMuNTExLTEuMTQ4Ljg0OFptMjUuNzAzLTIuMzY1djMuMjU0YzAgMS41NTgtLjE1NSAyLjg4OS0uNDY1IDMuOTkyLS4zIDEuMDk0LS43MzggMS45ODItMS4zMTIgMi42NjZhNS4xNDcgNS4xNDcgMCAwIDEtMi4wNjUgMS41MDRjLS43OTMuMzE5LTEuNjgxLjQ3OC0yLjY2Ni40NzgtLjc4NCAwLTEuNTEzLS4xLTIuMTg3LS4zYTUuMzQzIDUuMzQzIDAgMCAxLTEuODA1LS45NDRjLS41MzgtLjQyOC0uOTk4LS45OC0xLjM4LTEuNjU0LS4zNzUtLjY4NC0uNjY2LTEuNDk5LS44NzYtMi40NDctLjItLjk0OC0uMy0yLjA0Ni0uMy0zLjI5NXYtMy4yNTRjMC0xLjU2OC4xNTQtMi44ODkuNDY0LTMuOTY1LjMxLTEuMDg1Ljc1Mi0xLjk2NCAxLjMyNi0yLjYzOWE1LjA1MSA1LjA1MSAwIDAgMSAyLjA1MS0xLjQ5Yy44MDItLjMxIDEuNjk2LS40NjUgMi42OC0uNDY1Ljc5MyAwIDEuNTIyLjEgMi4xODcuMzAxYTUuMTIxIDUuMTIxIDAgMCAxIDEuODA1LjkxNmMuNTM4LjQyLjk5NC45NjYgMS4zNjcgMS42NC4zODMuNjY2LjY3NSAxLjQ3My44NzUgMi40MjEuMi45MzguMzAxIDIuMDMyLjMwMSAzLjI4MVptLTMuMjk1IDMuNzE5di00LjIxMWMwLS43OTMtLjA0Ni0xLjQ5MS0uMTM3LTIuMDkyLS4wOS0uNjExLS4yMjctMS4xMjYtLjQxLTEuNTQ1LS4xNzMtLjQyOC0uMzkyLS43NzUtLjY1Ni0xLjA0YTIuMzExIDIuMzExIDAgMCAwLS45MDItLjU4NyAzLjE4NiAzLjE4NiAwIDAgMC0xLjEzNS0uMTkxYy0uNTEgMC0uOTY2LjEtMS4zNjcuMy0uNDAxLjE5Mi0uNzM5LjUwMi0xLjAxMi45My0uMjc0LjQyOC0uNDgzLjk5NC0uNjI5IDEuNjk1LS4xMzcuNjkzLS4yMDUgMS41MzYtLjIwNSAyLjUzdjQuMjExYzAgLjgwMi4wNDYgMS41MDguMTM3IDIuMTE5LjA5LjYxLjIyOCAxLjEzNS40MSAxLjU3Mi4xODIuNDI4LjQuNzg0LjY1NiAxLjA2Ni4yNjQuMjc0LjU2NS40NzQuOTAyLjYwMmEzLjI1IDMuMjUgMCAwIDAgMS4xMzUuMTkxYy41MiAwIC45OC0uMSAxLjM4MS0uMy40MDEtLjIwMS43MzgtLjUyIDEuMDEyLS45NTcuMjczLS40NDcuNDc4LTEuMDI2LjYxNS0xLjczNy4xMzctLjcxMS4yMDUtMS41NjMuMjA1LTIuNTU2Wk02MS41ODYgMTAzLjMyaDIuODM2VjExNmgtMi42MTNsLS4yMjMtMi45M3YtOS43NVptLjQxIDYuNzg1IDEuMTEzLS4wMjNjMCAuODkxLS4wODYgMS43MTEtLjI1NyAyLjQ2MS0uMTY0Ljc1LS40MjIgMS40MDItLjc3NCAxLjk1N2EzLjY5OCAzLjY5OCAwIDAgMS0xLjMxMiAxLjI4OWMtLjUzMi4zMDUtMS4xNi40NTctMS44ODcuNDU3QTQuNjMgNC42MyAwIDAgMSA1Ny4zMiAxMTZhMy4xIDMuMSAwIDAgMS0xLjE5NS0uNzk3Yy0uMzM2LS4zNTktLjYxMy0uODI4LS44MzItMS40MDZsLS4zNjMtMy4xOTloMS42MDVjMCAuNzAzLjA1OSAxLjI3Ny4xNzYgMS43MjIuMTE3LjQ0Ni4yODEuNzg5LjQ5MiAxLjAzMi4yMTEuMjM0LjQ1Ny4zOTguNzM4LjQ5Mi4yOS4wOTQuNjAyLjE0LjkzOC4xNC42MTcgMCAxLjEyNS0uMDkzIDEuNTIzLS4yODEuNDA3LS4xOTUuNzIzLS40NjUuOTUtLjgwOC4yMzQtLjM1Mi4zOTgtLjc2Mi40OTItMS4yMzEuMTAxLS40NzYuMTUyLS45OTYuMTUyLTEuNTU5Wm0tOC4yNzMtNi43ODVoMi44MTJ2MTcuNTU1aC0yLjgxMlYxMDMuMzJabTIyLjEyNSAwaDIuNTY2djEyLjMyOGMwIDEuMTQxLS4yNDIgMi4xMS0uNzI3IDIuOTA3YTQuNjY4IDQuNjY4IDAgMCAxLTIuMDI3IDEuODE2Yy0uODY3LjQyMi0xLjg3LjYzMy0zLjAxMi42MzMtLjQ4NCAwLTEuMDIzLS4wNy0xLjYxNy0uMjExYTYuNjI0IDYuNjI0IDAgMCAxLTEuNzEtLjY4IDQuMjM0IDQuMjM0IDAgMCAxLTEuMzcyLTEuMjA3bDEuMzI0LTEuNjY0Yy40NTQuNTM5Ljk1NC45MzQgMS41IDEuMTg0LjU0Ny4yNSAxLjEyMS4zNzUgMS43MjMuMzc1LjY0OCAwIDEuMi0uMTIxIDEuNjUyLS4zNjNhMi40NjMgMi40NjMgMCAwIDAgMS4wNjctMS4wNDNjLjI1LS40NjEuMzc1LTEuMDI0LjM3NS0xLjY4OHYtOS41MTZsLjI1OC0yLjg3MVptLTguNjE0IDYuNDgxdi0uMjQ2YzAtLjk2MS4xMTgtMS44MzYuMzUyLTIuNjI1LjIzNC0uNzk3LjU3LTEuNDgxIDEuMDA4LTIuMDUxYTQuMzkgNC4zOSAwIDAgMSAxLjU5NC0xLjMyNGMuNjI0LS4zMTMgMS4zMzEtLjQ2OSAyLjEyLS40NjkuODIgMCAxLjUyLjE0OCAyLjA5OC40NDVhMy45NjIgMy45NjIgMCAwIDEgMS40NjUgMS4yNzhjLjM5LjU0Ni42OTUgMS4yMDMuOTE0IDEuOTY4LjIyNy43NTguMzk1IDEuNjAyLjUwNCAyLjUzMnYuNzg1YTEyLjY4MSAxMi42ODEgMCAwIDEtLjUxNiAyLjQ4NGMtLjI0Mi43NS0uNTYyIDEuMzk5LS45NiAxLjk0NS0uMzk5LjU0Ny0uODkxLjk2OS0xLjQ3NyAxLjI2Ni0uNTc4LjI5Ny0xLjI2Mi40NDUtMi4wNS40NDUtLjc3NCAwLTEuNDc0LS4xNi0yLjA5OS0uNDhhNC43NzIgNC43NzIgMCAwIDEtMS41OTMtMS4zNDhjLS40MzgtLjU3OC0uNzc0LTEuMjU4LTEuMDA4LTIuMDM5YTguOTc3IDguOTc3IDAgMCAxLS4zNTItMi41NjZabTIuODI1LS4yNDZ2LjI0NmMwIC41NzguMDU0IDEuMTE3LjE2NCAxLjYxNy4xMTcuNS4yOTMuOTQxLjUyNyAxLjMyNC4yNDIuMzc1LjU0Ny42NzIuOTE0Ljg5MS4zNzUuMjExLjgxNy4zMTYgMS4zMjQuMzE2LjY2NCAwIDEuMjA3LS4xNCAxLjYzLS40MjIuNDI5LS4yODEuNzU3LS42Ni45ODQtMS4xMzZhNS43NjUgNS43NjUgMCAwIDAgLjQ5Mi0xLjYxOHYtMi4xMjFhNS4xODYgNS4xODYgMCAwIDAtLjI5My0xLjI4OSAzLjQ1NSAzLjQ1NSAwIDAgMC0uNTc0LTEuMDQzIDIuNDAzIDIuNDAzIDAgMCAwLS45MTUtLjcwM2MtLjM2Ny0uMTcyLS44LS4yNTgtMS4zLS4yNTgtLjUwOCAwLS45NS4xMS0xLjMyNS4zMjlhMi42OCAyLjY4IDAgMCAwLS45MjUuOTAyYy0uMjM1LjM4My0uNDEuODI4LS41MjggMS4zMzZhNy4yMzMgNy4yMzMgMCAwIDAtLjE3NSAxLjYyOVptMTguNzAzLTEwLjYxOC02LjY0NSAxOC41MjhoLTIuMjE1bDYuNjU3LTE4LjUyOGgyLjIwM1ptNC43ODEgNi45NjFWMTE2aC0yLjgyNHYtMTIuNjhoMi42NmwuMTY0IDIuNTc4Wm0tLjQ1NyAzLjI5My0uOTYxLS4wMTFjMC0uODc1LjExLTEuNjg0LjMyOC0yLjQyNi4yMTktLjc0Mi41NC0xLjM4Ny45NjEtMS45MzRhNC4yMyA0LjIzIDAgMCAxIDEuNTctMS4yNzdjLjYzMy0uMzA1IDEuMzY0LS40NTcgMi4xOTItLjQ1Ny41NzggMCAxLjEwNS4wODYgMS41ODIuMjU4LjQ4NC4xNjQuOTAyLjQyNiAxLjI1NC43ODUuMzU5LjM1OS42MzMuODIuODIgMS4zODMuMTk1LjU2Mi4yOTMgMS4yNDIuMjkzIDIuMDM5VjExNmgtMi44MjR2LTguMjAzYzAtLjYxNy0uMDk0LTEuMTAyLS4yODItMS40NTNhMS41NTUgMS41NTUgMCAwIDAtLjc4NS0uNzVjLS4zMzYtLjE1Ni0uNzM4LS4yMzUtMS4yMDctLjIzNS0uNTMgMC0uOTg0LjEwMi0xLjM1OS4zMDVhMi41NjMgMi41NjMgMCAwIDAtLjkwMi44MzIgMy43MzIgMy43MzIgMCAwIDAtLjUxNiAxLjIxOWMtLjExLjQ2MS0uMTY0Ljk1My0uMTY0IDEuNDc2Wm03Ljg2My0uNzUtMS4zMjQuMjkzYzAtLjc2NS4xMDYtMS40ODguMzE2LTIuMTY4YTUuNTIgNS41MiAwIDAgMSAuOTUtMS44MDQgNC40NDIgNC40NDIgMCAwIDEgMS41NTgtMS4yMzFjLjYxNy0uMjk3IDEuMzI0LS40NDUgMi4xMjEtLjQ0NS42NDkgMCAxLjIyNy4wOSAxLjczNS4yNjlhMy4xOCAzLjE4IDAgMCAxIDEuMzEyLjgyMWMuMzYuMzc1LjYzMy44NjMuODIxIDEuNDY1LjE4Ny41OTMuMjgxIDEuMzEyLjI4MSAyLjE1NlYxMTZoLTIuODM2di04LjIxNWMwLS42NC0uMDk0LTEuMTM3LS4yODEtMS40ODhhMS40NiAxLjQ2IDAgMCAwLS43NzQtLjcyN2MtLjMzNi0uMTQtLjczOC0uMjExLTEuMjA3LS4yMTEtLjQzNyAwLS44MjQuMDgyLTEuMTYuMjQ2YTIuMzA2IDIuMzA2IDAgMCAwLS44MzIuNjY4IDIuOTc2IDIuOTc2IDAgMCAwLS41MTYuOTczIDQuMTg0IDQuMTg0IDAgMCAwLS4xNjQgMS4xOTVabTEyLjM4Ny01LjcwN2gxLjAxOWMuMzM2IDAgLjYxNC0uMDQ2LjgzMy0uMTRhMS4xNCAxLjE0IDAgMCAwIC40OTItLjQxYy4xMTctLjE3Mi4xNzUtLjM3NS4xNzUtLjYxYS45OTQuOTk0IDAgMCAwLS4zMjgtLjczOGMtLjIxMS0uMjE5LS41NjYtLjMyOC0xLjA2Ni0uMzI4LS4zODMgMC0uNzA3LjA4Mi0uOTczLjI0NmEuNzM5LjczOSAwIDAgMC0uMzg2LjY2OGgtMi4xMzNjMC0uNTU1LjE1Ni0xLjAyNy40NjktMS40MTguMzEyLS4zOS43My0uNjg4IDEuMjUzLS44OWE0LjY5MyA0LjY5MyAwIDAgMSAxLjcxMS0uMzA1Yy43MTEgMCAxLjMzNi4xMDEgMS44NzUuMzA0LjUzOS4xOTYuOTU3LjQ4OSAxLjI1NC44OC4zMDUuMzkuNDU3Ljg3NC40NTcgMS40NTIgMCAuNDQ2LS4xNC44NC0uNDIyIDEuMTg0LS4yNzMuMzQ0LS42NTYuNjEzLTEuMTQ4LjgwOS0uNDg0LjE4Ny0xLjA1NS4yODEtMS43MTEuMjgxaC0xLjM3MXYtLjk4NVptMCAxLjQ2NXYtLjk2MWgxLjM3MWMuNzI3IDAgMS4zNDguMDg2IDEuODYzLjI1OC41MTYuMTY0LjkwNy40MTggMS4xNzIuNzYyLjI3NC4zNDQuNDEuNzg5LjQxIDEuMzM2IDAgLjU3OC0uMTY0IDEuMDctLjQ5MiAxLjQ3Ni0uMzI4LjQwNy0uNzc3LjcxNS0xLjM0OC45MjYtLjU2Mi4yMTEtMS4xOTUuMzE2LTEuODk4LjMxNmE1LjI5NSA1LjI5NSAwIDAgMS0xLjcxMS0uMjgxYy0uNTQ3LS4xOTUtMS0uNS0xLjM1OS0uOTE0LS4zNTItLjQxNC0uNTI4LS45NTctLjUyOC0xLjYyOWgyLjE0NWMwIC4zMDUuMTQxLjU2Ny40MjIuNzg1LjI4MS4yMTkuNjYuMzI5IDEuMTM3LjMyOS41MDcgMCAuODgyLS4xMSAxLjEyNS0uMzI5YTEuMDkgMS4wOSAwIDAgMCAuMzc1LS44NDNjMC0uMjk3LS4wNzEtLjUzNS0uMjExLS43MTVhMS4wOTQgMS4wOTQgMCAwIDAtLjU3NS0uMzg3IDIuNzA1IDIuNzA1IDAgMCAwLS44NzktLjEyOWgtMS4wMTlaIi8+PHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjg3IiBzdHJva2Utd2lkdGg9IjEuNSIgZD0iTTE4NyA1NGMtOC41MTUgMC03LjUwNiA1Ni4xNDktMTYuMzYxIDU2LjE0OS03LjQ5MyAwLTguNjk1LTIzLjg4LTE0LjQ4NS0yMy44OC01Ljc5IDAtOC4zMzUgMzIuNzMxLTE1LjgyOCAzMi43MzEtNi4xMyAwLTkuNTM2LTYuNjM4LTE1LjMyNi02LjYzOCIvPjxkZWZzPjxwYXR0ZXJuIGlkPSJhIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBwYXR0ZXJuQ29udGVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCI+PHVzZSB4bGluazpocmVmPSIjYyIgdHJhbnNmb3JtPSJtYXRyaXgoLjAwNTAzIDAgMCAuMDA4OTcgMCAtLjIxMykiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjE5Ny4wMjMiIGhlaWdodD0iMTA5LjAyMyIgeD0iMS40ODkiIHk9IjI1LjQ4OSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMi4yNTYiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTEwOV8yNjMzIi8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMTA5XzI2MzMiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48aW1hZ2UgeGxpbms6aHJlZj0iZGF0YTppbWFnZS9qcGVnO2Jhc2U2NCwvOWovNEFBUVNrWkpSZ0FCQVFBQUFRQUJBQUQvMndCREFBSUJBUUVCQVFJQkFRRUNBZ0lDQWdRREFnSUNBZ1VFQkFNRUJnVUdCZ1lGQmdZR0J3a0lCZ2NKQndZR0NBc0lDUW9LQ2dvS0JnZ0xEQXNLREFrS0Nnci8yd0JEQVFJQ0FnSUNBZ1VEQXdVS0J3WUhDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dyL3dnQVJDQUNmQU1jREFSRUFBaEVCQXhFQi84UUFIUUFBQWdJREFRRUJBQUFBQUFBQUFBQUFCQVVEQmdJSENBRUFDZi9FQUJzQkFBSURBUUVCQUFBQUFBQUFBQUFBQUFJREFRUUZBQVlILzlvQURBTUJBQUlRQXhBQUFBQ0QzM3puUGd4aVJZSUxtUTl4RUJITStSMzBkRk1naXhiQnJSWUowd0NZWWtIQnhoT0l6aHhleHhVY3lGWkhSdGJkeFdrcm1nVzBwWVFFUGQ1TVk5QXNFRHh6akdIQ0REVlF1U1JZVkF3R0NnZzhJNkFXUXJrVUdEQ1hnbkpIYng5RjV5Ym9sa0RwVzFoTzBxakxNa2xoTE1YWjN6aitnMkJUdDhGN21EUkxLRXJ4d3JPWEEycHRrSnE0Um1FVEZFeHdZTUxJZ0xGWmRlNy9BSkVab3k4SjBDNkErL1BFZlFqVWxSTE9lK1RhSUVxU3lsb085UnBMSVdUMVZhTk9zcHJsK292TUY4TUZBd2xzSFdZNE5pRnNJSCtpVi96TS9DZUpIQWZSdm5QWmJZeWQ2cVdLVk1kUjFqWm9hYjFjbXR3ZGJncWxKTFlPdzJLZDhzWm9CaUp3anpDU2VxOHNXcmFFcG9JczlVLzlBSlJQd00wMmQ5NFByM3FMUzZWYTJ0NVdycm1lbnNWYWs5RWhySUdXWkpHbVZaTFhtQzdoS0VzSUVmaEI3bFVFam1WWUdwVzdyaHNKYkZXNjUrdjB2NTcyVUhCUmJHZnpMcllucnF0ejRZQ2hITWFvbGppUnY4MUlKVXZJVk13SnhBY2FJMVZoZGcrVWlxZG1ERTR6ZkN2SUVQRVEvcTZ2YWhKZlVHWnU2YzBNWG5UV3hXOFRyeHlxNFVSOFNKVmlwMTdGbFpWRzRwVXZxeTdDaGI2NnR1dFYzclEybnVlM2xiTVdhZVY4MEoxbllETUJJbk0ydzJwM0pVYUxJZm04MTZlWU5GWXNFZmszQ011OHBjTk91cEZJaExBWEIxV0E1KzR2ZWd0Y1ZvM01LRmpZU0FSN2RNdFJWWWlnWUwrUS9WM00wZEVYY2ZtTG5hWDVwMWM3bGpOcHRoWktuRmNBVHBwbGdtNityejVqY0p6Z2ptZk83S0luODlwVWpNMC8waDlaNDdXWGxOemEzdHZQK3pGZWlheVVReEt5RHJtVFpDeWJHdHRLdlBIVit3NjExVXBYR29mSTB0c1ZjYXBZbjB5c1RVRXNyc0pTY21hbmdibmVXUS9SMm5VN1Azc2pmdWhUSkFvcGhNTWNvWVdoeVBRdmE2MGhBY3V3cEd2UFkrUWlyV25HY0NZMzI2dXBBMHdTTENaa2tieGNwT1dEV2ZMYm5ROUtWQWp1elNvZFNlcHdCKzZ2Y0d0UTdURloxSUZ1dklhQ2trVXVZcWxSQnJHTnk2UFo0Y2lQaGZ4Y2N5TUI0WWlMcVhUc1cvMWRWb1VEeTNvdDFlbHdkOWZRdkpzbUwramxVY3Y2S2NKZXhCa2NxRTdWMGZUQ1dZVkVBOHhGUFhDU2NqSG5UU0VXNHMrM3d2NWIzTmE4OW8yZTNoZVViQzNUcmZveDc3eVcwTlhPeTdpSTRPUmJRUlVTSDBZZDJjZGpNU1JLY2hHZ2NPNzZaZzd1WjhIMXNaVWhxazg0NHZwZFk1RnB2VkhEWHByckNldDkzRzdHOUZpdXpVWkV6endkZHJhaGNFYXJDN1YrTUlvNWRNQ1pOOVZzNWtIZEJFck9uVWVWdGFEODc2RDdIdTZMWnRWeFVEM2ZQaVNjd1RmN2RUdnJZejNFaTJzVjl2YTJNdXo3MUUrVmUrMHBsN09mc01GLzZMejl0Mk1aM1pxNlorWCs0MmI3THpZSHBNV2paK25GSTgvZVM5MW9uTTAxTEFyMmxSYVU3YURVd3ZPSlhaNjNWRDNSVmRaYWV0ZjczbTNVWmRYZkZjK2NmUkd1OTVtdDdYS3Fsd1hOUWpZcjBiRjU5aDU1N2JacXZNM2RmRDBOUFdFaWtMWVZPczhlaXM2K2JNdHIycVVVVE9UTG9hN2NpdFR0ZlBVME5UZHZqZGNIWHJVYTNPMXZGdG9QbzZ0VzBrcklmWVVVYW9kK3Q2QUtycXJCbVhTRWlxdGpKQzg0V2swVU5hajJOWm1QQ1V6Z0w5WEVsV2pFdkx0eWh2UHllanE3VUJmM1Q1MTBVcTZiMFZCRUJrb09GaGpzN0lKTVJJRnBlQlNiWEwwZS84UUFMaEFBQWdJQkF3TURCQUVGQUFNQUFBQUFBZ01CQkFVQUVSTUdFaFFoSWpFSEZTTkJNaFlrUWxGU0VCYzEvOW9BQ0FFQkFBRUlBb3JmNjRZMTQzK3BRVWFsTVQ4K0tFNmlodnFhTzJ2Rm1OY0crcHFhS25vcWs2SkU2bE91SFJJMXdhNHRjZXV5ZFJ2b1owRWhIenV1ZExhNGRRL2Y1RTk5QnZQb1hqeFA4ZkgyMXd4cmhqVW9qWFp0b1VsMjkrdUxVcTBheDBheDBTeDFLNDF4YTRvMXhEcVZScVZhNHAxeGxxSUxVRE91UFVMMEFUR2x4T3NMaThqbGpsZEsxZ2JWSXUyNU9HTmhDdUQ2UHV4WWl0TGVoOE02a3FvdTdqOGY0SGgxK284R05COEJVYmpuaEhzaXBhTXR0T285dW5VQmdPOFRWSS9ISE91elhIcVluL3hPMnR0UnZxY1QyL0hnRkdvb0ZvY2R2cXRoYk5rdXl0MDdoZ3hPSkJhYmRIbVhNbktLOVFvTVZYQmEzdll4cWlLWkMzWW5hWkRLM0Z1S1JNMlZlejBJVTdieGNEWU82Q1AwMjBheU9mYVNIUjZ6S2oxS2kxSzUxeHpyajF4RnJpTFhoVC9rTkFORGoxYVhqdjhBbnBuRmhSWExHR3lJamFHWE52YkZqalp1UjM3b3IzaGRuSTNTaWVOR1Z1SlhJVFpzRzR2YzFOaGZ2MURiWEp0S2lKMDhlbDRsSkJ0SjRHdEU2KzFWUjlOSGlrenB1TlFFZXAxYTBhbW5YMGRJUDE0WXo2Ujl1LzZqaDBQSHF1Z0h0aGVrVjFlZ2FqWlhwRDN4dHEzZlV0bTBQeWZrMi9FVS9Ibk1iYSsxc0U5eXNZbmxqWUY0UmExUm9LeSt6MWJUUTMrZjJ1cVA4R1krWS9pTll4blhaSS9NbVA3TGpMWDlzR2pZcjlNa1owVWpvd2lkVzdxcDlpUXNXUDhBSEhOY3Q4RzZzMENWQmFOME1udEhNZVVwTThOcTFreDJGdU41MHRLeTd5cG5YTFA3dFpBYTRUczdxdklnV3k2UFZQbEh4V2ZJL3dCeTJOR1U2THVuUkErSTdwY213b1JZWkdlck9WcVZHOEZtRDd4N29rdHZtQVl3Sk5aYXluVXRQR2lQYXZydFBaK05mVzl5WGRyY2IxeFJPci9jQjFsUSs0ak5hamRxWkpmS25xbkc3TG15cXRiUlo5RXNDMG9PZHo4K2U4OWpjeEpiVE1aaEtoa2pMcXF6OEwvcThqbjNqMWJRV3VKMS9XUElXclBWZU9DWmhCOVdXSUtJbHZXT1FMY1V0eithc2JpTWc5eFM1dU92MjhUUGVnT3FzYllqdGJqM1hiOWVmdHhyc1JNaFlHUTlZbFVrY2JzYzNqbmVvYnI4RnlPd3o2TkxpdlpmRC9VT2lib3JVdnFEbTcyTnhBV2FsWEQ1UE1HYjBreXhVT1ZXZnVZeU8yaXNjeDdRTEYxL3h1aHlTblZoQi9iVTcyOWlua0NJQmxYbUVYMnE3SkVrWkpUQTdUT3ludmt3RzBBN3RBckNqWnpMQWx2VE8wdklOeDBsemxzOHRFZFNacmpqU0ZrVWNkZXlsa2UxSkZhVjg3bWZ5aFJoTyt2cDFndW5lbktRWnExOVI3dU50a3FZdFoxVmRaRFV0T2M5c2s2alZzV21iTDJwVkZTS09iOHM3dkR0c2hRWGRzbHh5M1RsaWRYOGN3U284aGNoRjJoRmtlMFdlNFJaK2ppTi9qYlVUSS9FeVpsM1NNU2ErSVhPT3UvdEN6MFY0cUphNktGQTJSUkNuOU9yTGg1VkY5TnN2K2orblhVQUgzYUg2ZDlSTk11US9wdDFId2lvRy9USHFvWjlHNER4cnNyZmJ0cEN2MkpiWUowejIxSWxUcHRQcG14OXVabTkyeFJNTmVUS0JXYTJsTUR0cFRKcm51R1NxQkVSZXBqQTdibEJrb3Z4K1hWYkg1dTNtTGNCcmR2OFp4aHVEdVNHSWRBUyt6a2NtWlhHNHBDd3JYM0JHUTZOeURHVi90eTRrZFIyYUt3a2ZsK1JyS0dTTHJEckJVS2xGWkxlOXZrV0xseUxMRDJFaUFPSmQ3anJZOVNaeFVGTjZKQmg4dktHcGp1cUNXbVFKUjJUTWIvR05lcVJtdllZTVYyeHhka3p2cVBicGZlSDVWVjgyWWVqWXk1TTlvM0dXckxKQ0ZRRmt2dFFVc01sdytmZndUamJZVWtTeUJEOC9kZ240dFpoUXh2cTlscHN4TWFabHBTQmNmbXBzejI2OGNRS1dRZFlCOXlwcTg4Ym5YRkttY05ZWkVUS1FFUzhZOU4zNWZWaVNNWWNzNjdleUg2SVRmWDlEcXVjVVJwZUZ5aGpKUkdJeVhydkdKeUpldXZCc0lad1BYV0N6VGF6UU9HdFlMN1pSeXpibVEzWGhNelVvUGVtMGxvV1VEWVFBdmtkaCszaDY5LzJlbHR0cHVDcXNIajFIUk5Idjd4SG9xbERPV1k2S3hzNzkvOEE2M3dzTTd3VjBSaEZmd2pvekNKTHZYWjZRd2hSdVJkTVlyOUJoS29icVFycCtoOGFxWVdwV2orMzhkSS9KVmdtSlpPS3lPTXpKdFhqczVicWRQMUNzV2JPUnUzY21wbHR0c3loMTQ4WFZRMFRFVVc3elhSSTR4M0s2M2FQcHJKYzlFYWxvV04veGlXRjZUd25yWXhuUys3OW5CL3J0UDhBWkEzOWNUSjF3ZjhBVXJYTWJhWW9COVFGZ2pxWkhmZU83ZlhkTWE2cTZteU9LekFMcjlQWldvai9BT2g5UlBGdTR0ZG5TS2xTYUVYOHc2OU5JdjdlamJta3Z2QmQycXAwdjFWc1ZScThUT2tjZzNHVWZKZGpzZ2R1dkRtYzJnY1U2OVo5ZFRaV294QTNOU2xYSzJiQ2dMaktiS3VUaDF6NmwraWR2cGxoYTBrVENMYlVNajlteUI5WW0zcnFmRlJtcXNrQXFLa3RvVVl6NS9idkhPOW5yc3BsRGU2U3JSTENaMklPRzJFT3FMQ2JOWmFyallVakYycUM0bFRLZVV0VTZRV2RmMUtWZkdzdFB4ZlVodEtLOTZHNnRGUWxNK2Zsc3NvOFNDdFo3cWJKTlREYTJOeTJkU1BZQmRSNU5OeUlJT3FsM01vR05xWGNxaXBYTjVXODk5OXVzd3FpdW1reVZZTzhNZWsyK3BxbGE0TkJ0bk5CWG44dVk2MXVjcEx4NzhuWVl5SHNiWmt2ZkhxejRJRG10RWlVako3Nk94M1JQYjNuTysrTWF4TGhzeGhlcWJDdVZoNVN4YmFzQ3NuWTdCVldyMnVwc3JSRHhVWGVzNzdESldRcDNDVGlXM2d5ZVNsZGF0TDZtZHQ1UEllekw1Ry81aGlxZy9KM3Q2OWF5dnhVYnhnOGtZM1k3eXo5Tk5scXd4ZlV0ZGxFb2RuczFRdnl1elhibmJqZzR6SytEWFMwMVA3QmxodWE4ZmZLR3lzK1hVeXVQY3VkOXQ5TVcxTy9KU3JoMjh4SVZEb1l3d2FjMlpsUlpzT1B4QURZVzdtN05QTzlKMVlzM3dQemJXT2kyV0hzd2VVVWpLWnQ3eGVsS0xJdUhEcVJkeGR3YldNdlg2U3hWanNsWnIyNDhpYXVWOTYxRmJ1cFVISUlUWkdaY3hreEJ3a2w3d3pZUkh1Q0MxWWR5ZTRvc1FJekVEL0hacEJBUnZxZG9uWVpaeUloV29CYWs4MnE5Z1pBcTR3YVEvSkRlMFI3OVJhYWV3Tm92QUxrckcxTk5zejIvZVU0L0N6aXF1UnVSMnkrcFVzK1YzUXk1WUN3cWZEcmd4MG1xRjVFVk1qeDN6c1h0VzdlQzNpeWNCRVRMakwzRUgvZWxFeVB4QUc1ejI2NHAzMjBzcDNrSlp0RzhhLy94QUEvRUFBQ0FRSUVBd1VGQlFRS0F3QUFBQUFCQWdBREVSSWhNVUVFSWxFUUV6SmhjUVVqUW9HUklGS2hzY0VrWW5MUkZEQXpRMFJUc3VIdzhZS1Nvdi9hQUFnQkFRQUpQd0s4SGFPMHhvZTBkb2cvcUJERmhnZ2l3UVFkZ051dG9aYUxCL1VIc0VIYU95aDNtRWMzbE9Fd204bzJMK0h6bE01MjVocEV3NFdCZC9pYVVrQzRjSVhESHYxeWozbExzUFlQdHRGTVF4T3lpem5vQkVzeFc3K3NHTDFsSmJycEJvSlVqOWxvL3dCaFlJTzBRUVE5aE1hTHp2djlneXNZZDk0WXBpSDBpUmsra3EvU0xlVVpTYVUyK2twTVp3cGxJektDTFBuRUZ1dzlqbTdkSVl4OUl0cndjd040a1VmU1pTcktrUFlZb1B5aWo2ZllvQWVZaDdUQVRCejIwdEtadTJYUzAvT0xCZHVrNFA4QVdjSTFJL2V3a2ovYVcrd2h0MWkrTFRzNHFtam5abWh1RG9SQkVKQTFJRVV6aTE0Z3ZvS05qYWNOV0o4d3NSYy9DcWJSaFRJVzdaM2o0c3dIejJoeEJkY3Q0M2gzV2UzS1pjZUtuM2d1RFBhZUZSOFJhVjZqVzBKeXZLNXVkUWRwWGJ5d2lWUC9BTGlzUmZXY1RWeGZjdCtzcDhua3h2Ty9jL3ZIREtldW5NWlNzUjV6aXlwQXZkSVN6YXNUcktuSnVtekNCcUoyeEM0UHpudEJTbytHblVFck9QbkRjL2RFd2hSNFVFR0o5emVjUWc2SktiT0ZxODlDbmF6TGFVcm83aFVGS25ja3ppKzRlb1d0aUEyMnpsRy9Oek8rUXZLSlZsMnhXalcvaU1iRWZLTGlkdEVVeGIyRjNicjVmV1pQeEJ4R1U3WTFCVzNXTlpnOW0vUXcrb00vNk1QcEdzNnZkUmJVUnNQNlJWRlRGbW16UVpYME8wcjRiYmhyRVQyb3RSYlpkNkIrY1Q1em5QeGJSV1g1UzVsN2VrNC9oUngxZWx6SU9KVzFNSGEzV2UxZUY0akJXYkNuZWozWU1xSmlJdzhzMUg0UWdDM083YUFTK2Z4L0UzOGhDRGNhOWZTVkxuRURWWWJ0L3NKbDNkWlZwclA4SytFbnFEbkJlbStURDlJZmZVY204MW1SMzh2c21HelgxOG9XeUZqUGFJQ0tiR3lITDZDY2RTZGFqS0ZyVWlDQmZURk9KTHBzMDR4TlpVb3NCb01lc28wOExhOCs4NElZbGJKOFkwNlRnNlZyZjV3bFZNc2l0RjczK2M5MVEwOE9zdUZ0ekVuV0ppVkV1QitVWWxtQk4vT0FBTTQrY1BpU3p6SkQrY3Zuck0wWWM0SHdtSExmeWp5a1EyNVhReXhIVVJNUWxPemVZaWhGVElZbTFuR1BRVFN2Wnc2MFQ4TEZ6b0o3UlIydmhEVmFaUm45TFpiYnl3VGh6b1VVY3UzaEpCZ2dFWVNvTENWSGNmZHA2Zk14YkRDUlpjckF6d2psb29OaFBGY0Q1d3NYeEhFYitLZERlVy9zU1I2M253dE5EWnZTYTZSMlVXenR2TDM4eEZ0NWRqMkk2UlBtb2xadi9XTGp5NVcybEduUWQ3OHZFczFUUFg0dFBYU2NhNFJDQXRBNDcxMTYzMzN0UFovN0t0WDltcThDV1h1anZpRzl6LzFGUFlsNVRKQUdnTTRDL3dENDVUaExIVnBUVzBQOFp0S3VGZGhCOE4yYzd6L0x3aTgzY2JUWVd6bWdpblB5Z09KZGNwVGU1WFRET0NxRzM3azRSaGw5MmNJLzBnd3RzcG5HcFNOTzJNUHFjN1RnS0hBcndkLzZSVlVLd2R0aGl2RjRpbWJDblU0Y1ZyOTR3MXRmNWRMU3J3MUIzOTRhTkREaFR5dmJXVmtxbzR1R1JyZ3ltQktJejFuRGdla1RsNlFmSzhvaGlOTDZTa0xIWVJtSFVTaVBwS0djOW5ycmNrYnpnbFB5bkFxUFJad21mbk9GVmV1VW9ybDVRWkFaeFJVRkszT1Z5TTd0V1llNkZ0VE9KN2xsdS9ESHhZVHFKV1hHYkxVWlJtYzV4ZFhtbys4VlY4YjMvd0JYU1ZjSnBjUWFwRlJnUHIxbFRma29yYTVxWC9BZVlsU2tlSVJMdFRwVXl1RmRvaGdQWXBnN1RHam5zTUhZZXppVlVJdDBvazJGUy81NXlyKzJEaU1CY1poaVIrVnRlaGlWR3FVcW9ISWZEZnJLTlNtQmRhUnRoeGVlbWZyS05JcmF6ZDRtc2RDRnEzRjF5TnVvdkVTdzVSeW5DNXNUYy9nSjdQVHhaMUtSc3luZnJFcWh3QUtkU3R4SVphaVl0QU5yU2ozZDlzZUw4ZnNOWXViTGZjeW9GRnN5eGh6TFlWOHpHR1BEaXcrWDJHdDU5aGpkam52YVk5MkdleStjNHdYbzFRTzd4ZThEK3AxSG5LbmVWc0JBcm5wMGFWV3FLYkVCM3ZobVFjM1FiU2dEY0RuSnNSL09CVlU1cXFQdEhDODJmZld5bkNBMWxxRHUrVithM1hET0wvcGJjUVFiVi9kc25YTHAvd0F6aTBucVVxd1NvRmF5aTg0ZGFkVEs1RlVXei81K1BZVkZQY3NiV2xSYTE2eTRMZkZuZENmSXppaVFicFRyS2RldjFNR0xpcTNOV3ExeFkvdWdEcGtZNnRTb29NZUgrOE9XVit0NXc3TUxFMUsyeTVYamVFYkM4b25BbmRzeFZzMkJoUWRNK3h5SGNYWGx5aC91eTU5QlBCaDNHeGlqTTNMTEhDZmRWdFprK21ZMWhQTGxnNlFZZWJPTTJ0K1pwdWN6T0pzY1ZtejBFOW9sYXJ2eU95M3hlWDVUMmhqcXNGdGI4L1BYV2NTeTFVcEphb3JqQ21XL1F6MjFSSndXSGRqR2NwUVprWjB5Q2VHeW0vMU1xM1JhR0JYRGVBazJzQitNcVVyY0hTVmFRQXcrWTN6bGJ1N2dZNmp0L3BsSVVhYjJ4VzZkWnh2ZEk1SnFzbVZ2TXhxdG1KOTdVNmljYmdKWEQzZzlOWlFyY1NqTHo5K21iZGM5cFhQdXNrTlFjekQrY3hyVVVFWXZ5amxpVHJlOFYxR0czTEI0MDVmV2ZGRTV0cjZRTmZjdHZCZUxZa2FHYzE4c0VzTUpCSTY1eEJoWFVvTkJCL0ZVdGFaTGdCR0U4cjNHUmlMbDRkaDY1VDJobytTaDVVU3FqVnFkU3c2NTd5NjBqWDErNExmem5GaGxJeGQzaTZkWlFGUzYyU3Q0aXJXeTg1Um8wcTFRL3dCcmJFVDlZOVZxZ0h2UWNoaThyUkJsU2RCZGQ5b0FUVlRuVzNoeTFCZzd6SGw1K3NPV1dMT0M4ejZtVXhrTFduVEtjM1NJeEdtSXcvaEI0Y3dETjlZdk94NVQwbDhOckcwSGxZaVZEaFJiQWRCRUhkdFQ5NEcwSG5LVGNvSnhZczdmcktlSjNVTlVjL0NkUjZ4MnhPQjNwTytVUzR2NFJwUGRyOFNqSzVsVTNUbU51bWtvMnp5OHBwcUlUZkJHUEtMTEFQTXpMempaT0xIc0ltMXJTcXdCMm4vL3hBQW5FQUVBQWdJQ0FnSUJCUUVCQVFBQUFBQUJBQkVoTVVGUllYR0JrYUVRc2NIUjhPSHhJUC9hQUFnQkFRQUJQeUVRdVRSUzR4MnhjeEVlZ3lyWXdWaWVURjhMTUhkUU9UOFFoWkErR0liSm5xVmw4SmorbW1FYmxHLy9BSThxbmk0SWp3Qk81WHNJdmE4NlpXS1hEbUllb2ltS3U3Sys0MjR5cmxTcGtmVUhxZW16anpPUFBVOVU0SkFqeFJSK3VBZU1mOUtFMHBEMHE1S0I5cHhTRENKRytDeGpyOUt3bFlFVC9MYkJONnhDaU9RdjRvRnpVeU1lSDVMTlV5OHo2ejUzenhVZVdFOVJ3d1FVUmc3VDJsRVB1cGVscS90amJINGprQkxiVXJQODY1V1p1S0J0M1NXKzFsVXJ1TUJVTUlmTlFWeHJiY0hFVTVnS0llNDJodm9ZQ0JkeWwxaTEvWkRyQk9aSFlUck1ZTDFEVk4wQ2JQdUw3L1JRM25sdnpaVFIxRjRVVFBDWmlJR01WTm9Qek1XemVOY1YxUkJLcm5EdVlGQTVHNFRmTG1ac2laWlN0bnhoTHRuOG9GaFQ5bWFPVGJCSi9MOHM1R0p1MUZNQnhvaGY0UjRSVXhpZElPSVhybjJDWm1jczhBSjJQNW1vSzlKcVd5Z0duWU1CUWhIVlRmNk9KK0pDN0NXNWcxTjlRYnBIdUV1eS9laTdqK29VMUhXVGJsd3BaWEt6Uy9VWGpXc1REUzYvVDZ6NUs0aVdUWmJKZWxQU2g5b203K0VYMmZjWUZEVjYvTVVVVHZObzlybWtpL0VQZGFLNS9FM0J1TFVuM0syUzFTVjB3M21YL3dBblJnd09XVm12NGdrTWxvc1ltMlRuRTdOUjN1TmZ2V21oMnVqMU9iMVZBTDl4emtXd3ZzOHd2WGdOUnJpR29zcU1MZWZ6TUFZcm9oTTFpK2hsM2gweGRhbG1NSGFOZjlnYXZ4SEFudFYvL1VZQWFzY256TFNJOFpNVTNGRHBHTS9KaWo3VEIrci9BSnNYUEYwRFAyeTZxY2lUM1BKdFF2QXFOV2QrcGQ0dHZhOHNNdGJDN2ZxZnlUdml6NDhJVTc3dFBvekZvaDdTNnR4amwvcVpBcmxjK1dZT1AxU0dVNFVEL1ViSHRwZFlWK2RSVjd1aTJzYmNVUzRPM2FjajJnME10OFJ0L1B4SFpSbXlOOGp5c3JVcndWaVJWQjJQbDBCS1NJTTZqQXdPcm9CRFdsRE40by91TWRlZUdxWi9JM0x0cWpIVi93RGhFTUtlYk9DVzZkOENRNnJXVlZ6ZGZIRXBsNEpFMmZjeTIxdC96cjlvOWtkbjVEcDhrdk5MdDI1ZTUvSEhabUF1ck9hVHdxWTczbC96S053REFhRDQ4UkN2VVFJd2J1b1RiVFNpL3dCdVk2Q0dRWlliWXJ1UDAwRWlBemh6a21Fa1FXLzZqdk5sVXNQY05oL01qcHdVMS95UEV2bDd5L1h3L2VMNXBBREtla3Iyc3dZRjJ5Z2NmalB1T3FJZG1RaCtJeDlRUzNmOUp4d3g1NG40bWJXV25idjFGUDNJb3Y4QUNmNzVqVnVxZG5FWVgxMnpuVHB4MnNtRjFsTTNYSjRpcVRFb1ZOMGxmTW80c0ZBNUdxMC84bGxJYVNqWFVlRmV0VTJUbmVpYlBPSUo3QWc3ZFlpVnBqYUZ1VVk2eTNpUGxncy9NaHpZNEdQY0g0QzYvWXdIclMvNEJHUXBqY1dINFhXSlpLSk51ekZhTVpSbDVXY0pzVjdSM1V4b3B6NjRXV2dBS3l6R1gyc3Z0K0dkL3RVM0RUUmVFNWhjY1NHVDF4QUg2WTZseTdEZ0xtaTAxZ2Z1TkliVDJlSm9nS3doVldTOHI2aFRGbUpUWEhMYmhkL0d1Y0JEYmhTS3JYTG55VExET1FQaVlRa0F0NUdMZEdIRy93Q0x4RW9IZEdNZk5YOTNDckZaUnZwVUhsdGlzS1pyZzhISDh5OTdId0toMkZENFdqWHdmVXpUbGpVVTJHUWZCajRnbzJPL010T0FkVHFwN0VoZ2ZFdFVKN21vRGFtL0dvbHZmVFFqZXJQOGFIRzRacmFxMG9Vdk45c1NMTFF2ZUs4YjBCN21LY01HVjQ3MzBVc04yTU9IZFZZQTIzWHlsbWlPNFpLdjFNUTZ6SXlEU2c1NlZBZmlXWlN0OTh4R0ZkckY1ajFCL0c2bW5aZk5SY1cxYlhTalIwVGFhWURGY3lpR0tFRUNxaGZGaUZnb1ZkT2E0akZFcjVlWUFWTm91NGRTQnR0WmRUa0x5ODViVFpDMnVNREw2Z3JJdWxJMkVTRzlnQm5iRVovU0VhSEZxcHBYRnNHcVdGaTVERkZNS0VMZjFiR0l0QVhsdTZlSjlLSkxtOGtyU3VnZ0lrc2xzT2hLN2VTV1N6MndFQXE0SHhLc1hhZEV4UGx0czNCNkZpbHJkU2pVN3l2ZjdnOHhlQkRWMmpnNzFIWjQrbHloMnZYbVhSdWFOY1RPOWlucVZObU9hdDZ2T0tsdmVOdkQ2d2VZeU5kaUpObGNmNm9mVFlhM1cxb3lmaVorK3lJZ3p5ZUlPc2tSUUdTWFZhWVRWSitOd3hUS0xGQkFDK3l0Rk1ZUHNEMXhnNzltSjBCSG9HQ3dmdjhBVkRhSEdGK1pGVEJacERPMWVKbURIVTBiOEV5bjJqM1ZtSWc4UStTQi9neW8wejJPbXJNUy9yMzM4V1lQM2h5eW9TTlArZ3lZak9Oem4wWElQRDFCaHRLbTUzOHlxOUZmY1A4QUx1SmtWcG9UaGVWdEE4R29GcnFnclRobkxXT1NhZEpvall1ZUhxV3NDU3BEVHNFV2FZRE1XVTJ5NTNCZGZVSVVhd2diL3FFNlN3NmkydlUrUjRaeXEvVng2U2ppZG1FMEpXZkV6a1pYcjlJazJwN20yNFh3Mm1MbU44U3ZUUkhDZHZDWTFybkp5RkRrUDlxV1pNR0dQSFRYUFVwTmVBb1FZVTZscGVXcGdjY3Jqd1JndUdTYjRhNXZ1WWtNeWdzWmZQOEFHNVMxODNVVjdzMjFqQlY1bVJ2TTh1OGh3NFB6SHhyTkxybWp6bWRLWmNSZDVwdlJSZVJEVGNQTHdmWWc4TUNpalczZkFQczh3cXoxczEzeDRESEZadGpZWnFlUGE2SmhMem1QaHEyMkdTbUJhN3hXY1I2eFpjV0lyZkJmSHlSQ1hBcmxtcnFmSWFrS3JOVlRYekdvZ25FcFhOeDJ6OHphNjNNTTgvRTBWQ2s0L2Vpa2JkTENxS3o1dVpnN1V5R3FZRTBFTHlmWS93QXdXemVKK0JGR2NPRHkxNWxoVlhWZVNQbmx6V1gydm1YVXhiRU9abVJNVm1EazVHRmZub0F6Z3hEd0RqcFFuTGpnNlVrb2pqUytZT1IzZFpMeVJTVlpmblZoeGQ1ekF1dWF1bzVjb1BvbEZUMnUzN0dJQU4yYkdFVEtqeDQ0bUNUTm9NZ0h3Q3VpKzJOTndML2UvdXIvQUl4Q3JyVXFISy9QN1M0cE9iUFRRK2VkL0VZTHRWK0RTbmd2Nmlqb3UxTHBiYmkvVXU2V29EZzM0YTgxR1piaS93QXgyZVl0ZlhOWWRlb3NJNUNDNDg4U2xWc0s4ZG9vNTBYaklIK0lrMDIxb0dVTnE3UG5xTXAwTFdaU2pLQXVFc1NaZnY2R2VXSlM3RHg0SHFKMzVOL1BuamlVOVpLd2NETjBRbkpmdnZNbTl1T014SUtuc2F0eW1iSjNYa2IxTVZMWjVUaXF1YVljOXlUWGgwKzJ1V001NExBNEYvdi9BRExIZWljbnIvNG1wTVd3QmhjczdvRDk0T3l3azdtQ3UzdVYwTzVLckQvbnVZZ3RuVlI2VE4vWG1GUW9iWHNtUEx4REVWS0RsRXhGTG9tWjk3NEMzVlRXb05Gci9ITThJUVZxTVcxS2FQY3p6T0NUWWZuTWJ1T1JZRHgxNmhVRjRCdXM5RU94c04vOFdrMEtRQkpZV1crWXlDOTZLMzVoYmUwbjRQekVtODhOSFdzaWVKeXBSY2pudE9FR3FCTnZsZFFsWnBnaUJkSHRsZE1MWWhLM1h5N2drT2wwN1BMNWZFSVFPbjhCNDZqWll6VFdDVlg1aFZ2S3FDdnhDQmxHZS9FcUg2dzRoMXg4RXlXK05Xc3hGcSs0am1XNGRkd2UreE1QOFF1NEF5MUc0ME9vLzhRQUl4QUJBUUFDQWdNQkFRRUJBUUVCQUFBQUFSRUFJVEZCVVdGeGdaR2hzY0hoOFAvYUFBZ0JBUUFCUHhEUkllRGVCeXdJdGYzRFZVWXc0elZQMndsaXhBcUk0QVJIRzdNamg3d2xIKzRiTFRMV3JLQStKTFpjMjVHS1c0NU1zc1hGdEhLUXNCNk84MndFVGlZM29mV2FnYzhPQnlUNUhCV0F2Tm02NW5Gelk1alltZXVNWC9XeDh6eDBoNmhONXdScXNyK0poRm1ZWFBVVEx6ZjVNVklacy84QUxFT3k0VzQvcmd0TExGM2c1SzU0c3gvQmhqRHB2MU1aeVhIVHJoOWhpU1VmTWIzR0Q5QUZUY3F1WEV3dXhreTZUQjUyZU9hbFBXVzBMWG9QTCtieXVMeDhISGszL21HSmpveWNhbVdjbmNBSG9oeGltWUtPTjllc1FBeGlJTVpoQVNYL0FIRWEzVWt4WGdqa1liMVlaM3I4eWRaTXBoS09VT1ZkWlZ1T0lmeFNaU0RKTFQrdUw5RDRzcXUyTkJoZEhqSGJDZkRvT2d4cDkxV3piN1o3UjdsNC9NYVBvWTBubkUrNkE0R1pOZzNrUCtHdk9YcU5iNURraHMrUmwycEJjWGM4T0NzNE1LQVhzelpId3EzSU51VXJyR3JXNEZHWGl4bGZROUxPZWY3eFJzZjNGU2tmSmNCTVV0d2VNQ2VBMEJ3WU1TOU56aE9tUXFlOC9jRVZGanpaNk1lYmV6cHZqTmZseld4QlIyUWh3SWtDcEl4cFVRYlFOOTVwblNwWDh1QVB5UUJpcW4xRjJjUlQ0d1FleXNaaHpkNWJVM2dKaE5CMWJXS1IxUUFjSEJQamx4bWlWemhVZm1GSlNoWER0dkNyQVFIb3l4Q1BMRlFpdVhMbE44cnhpd2FvNkJ0ZkJpNEZWeWNLYUFLa0VyemtLb2NPVnh5U3BKZm56RGx3am9mL0FIQWthNmdKKzRDMHY3WWsxUFd4eEVFZmJUSUNPZDRnRVByZ1RvT0l3MDlrNHBQaFVURktoOFpwQi9tZUhYR3MzRDEwQ2VzU1ROZ0FHNGNQWFNCZk9QWklDUFlZY0kwdjVsQ1JzLzVNQjA3WFlhNzFrbzQxQlhkcTR3Z3IwMk1ydmVtOEovQnZoZllFTVB1THNML0hIL00xMUllTlFvdjYxN3pvcmVCTTBKOXpENys0ekJuQUo4NENCK09XeGNORUhhQ3ZwWmMyaWhNa0xEWG1iUjRmWmNKSmNDRHlKcE1FWFlRMk4vM0N4elVBZmFZb1ZwNTJHSGFEbHowTGRqbzVlc0JiNEdoOUJ1NHlRT1JSWVBIVHJRWk1Dd25xcWlEcXpxbUh3SEk3OFhhQ0hERlRTQ2RtMkQrSTR6cFN5bjZuZm5Hbmo4ekE2S1BySE5jcXRQZ0JWZWdYQ1BscGo0Tm9mY1U3Sm9yN1I1WVU2OFJQUVZhd0RmMy9BUEl3NmkzcVBvcmkxV3huTC84QUhGd0JXR2dwUjVzRS9TWVVjanRaNmRHSm0yV2MvTXBXeUs1RmRGM1FabWdPdVduRjROWjdiN28xZkY2eEtQYi9BR210Qk5kcDJaUitBblVkQi82R0tYVkdRSmF0UDJZc3hxaEozekppU2dUWlB2ZXRmcks4cjAzd3ZlekJEbCtSZXY4QTVqSUVPNUhYQVQ5WEdkYnpCeVZhcm9QRFpjZjV0a2VJTzBGQzc5R0JtVlJoc0JhTk85WXVzWGJFS0wyZXhDNUVodzJHMG9GRTltSVRBZzY3ZmVFNERCZzZzUHpIUzc1VjJWc0FSWGUvaTRWQm9YUUpJR0k0TnRiQ0tNelZKMDljNmV6QW1XVUFJUjVvUFpNWmhDdTFGSDFhanZYWnVLUCtjZzdGM3gvbU5XVzc0NS9WUTlKemx5VkNORzA5YTJ2VWNWWEFsUzc2eHBEcFhOVTJwZDE2OXRKMnFjWklMN201WFcxanMzM2dBMlZ6WHY4QUgvY1VnRGIzK0NBNTArT2MzOGRaTXJvQlpQZThESlhtZjFYUm1yWEpZSHR6L0RrTXg3MFA2WWk5TU5jbmt3ME5zUlIrcGJnclhISXhsckFzOHBsRzRydEpWTGZHbmpvaWRGNWdEYzQzaW9iRzVkblM4dSsrOGRpMDNZcThHczREZVcvSnNKRldUaGh3Mm5LNGs2aUIxMm5Ob3ZsN0FjWW1XbjBoZ3ZZQjgzc3g3U1FWdTN4QTI4a0dRckNuZ0poN1k3TUtzMUNwNnRmQ0dFQ2ljNFhYNUd2WU9HaWtFS0ZIVDNmNHVXRytuREtVUVQvMFk4b2Mvd0ErTW9xcUdwZi9BSmprdjNIM0hJSlhJS0FpOE94THJYSjNUdUJ5MFRvcVZwSnJuQ2o1dkJSV2syTDM3Y2VuSy9HVVczY1Rlc0ViREdDSkZOYWU3ajF5b3krK01QbU5sdnBHSEJXSExoUlJkakQwY2FYbFdlYzFOQ1g5Y0FTM2Q1N0NISHZyQmJkcWRnWWNTTEZDZ1lJYmFiK1NBald6bnZiaUpuR3FUUnMwRjBCKzNCSEphQnFOaUlwTzB1OEloVlZWYkgydGNyV2xGYlVxcUFlT2FXYXhKd3REYllOdklNT3AxbElpWk9JYU9nWFdzNEppc29wNDQ0d0xJUVRzd000L2hjbk1WLzBjTy9tTUx4MXhEcDN4azJVVUI4amhYekQ1Z2wranNYL0hmdkdLOExlaDlkNXVORnFKeG9sR0ZzbFNlb2hpb2U4bWZrZ3VBU1JRY29SZDRjT1FZTjJ3MkRXZE9uRFk5NGhKcm5FTENDQ1J3eTIreHdaOTJjRnZqZ29ZSlJLcE1LSWp0d2RudUxZeG9NbHJwVGp1dEUyN0hWeFprMmdocU92TDI4dUxRUkx0SFloNUdCZU40SHUzcXJ3SEE1clVKd1lXUUVvWkdqL2JNVU04ZWlJZmJUMWh6RjhpclRmOC91RDF0QjNZTFBDNy9jTkpYQi9BZXh6emFFSGJ5Y3g2N3VMRmc2Skowb2VmRzhidWxycXo3eGk2TU5VYWZhZC9NcW5Bcm83VDlNUkRHQ0lMMWVCcnhsU3BRc01CVlhpQVZjRzRyUUhvSG1zalNGNXVQdVpFWU9xVW5vYWxYQlFvUDFqR21UWU9zMkd6NWFXcVJGbDVOZ0NxSEhsR0FMU00xeUlzN1RaenQ4WVoyb1VmWDNDVGhvcnloOG4yNXQxSWRRRE56ZUlRUU9VTGpldU1LdUJvUmZBK1hEQXJBOVNiL0xybkdZcGhDM1RxbzdjRWZZZXlnR24rbUs1dGc2S0R3dkhUbFNmQ09ZcG5qV0ljNjdsTFg4UndiTkRTTkxCcjFta2I4NVRod2FWeFJkQVlIMU84azhFRWJmWnJPMFFMQ2ZRR0x3U0JzWEJ1cFVsdjdHNTVtc2gzejJXTkdmUndqZ1FVNGtrL1VxS1FUSW5FSjZpRVJ6eUZzd3JyNlFIZ2I1Skl2UUU3ZkJwdko5bUNmWGF3N0FTNXZKTDJ6by8zSzZ3aUl4SWYrUlBETWtaaUZRd0tMWEdEblVaNS9JWGpHaU51MWRnMXJHdmxXODJrVGpENFVGSFJ3NHRHYlExZ3h1S2t1VHlVYWF6NnVPVDdzQVAzVEVOWEEyL1V3R1VTdUFLdjRZem15MDNJa1NvVkFycGNWSUxkQTBnb3ZjaVk0SFkzS3EwR3NUeHZCclFSMWpWQVNuR3RjRTQ4ME82cHBZZytBdWFyN2dvcTJReDJjaHRRY0tESm9PdXpZVGJIa0d2SXFDSEZIbllnWE1Wblk3dDY0TUN3dCs1clA5TVE2RmprL254S0NjWXVhcDFqZ0lueGx0NG84aHhSbEhPRHpPOE04NXZpYlhOb1c0b2pzd081Q1I0dG1sQ3RMVzZsY1NER3EwbXZjR2dtNE1jMk4vTllFS0V1RVkrc0E2TG93UkVGUTJ5Wml0aU93dlBMd3djUDc1NUpnVGFyeitoN21JT1llZHJ1TVpUbGpjWU55WUJJczBFRThMbGp4Z3JNWUVteXFMVFpDcWl4b1FKSWpPd1RZbU9aVGhsVHllTUV1bEZBRXo3aGZZTXd1WlFkaTZWK3RjNmN1YndqbzN5alQ0ZFpzQWFCT3ZnK3lYSmF3SVVZNDZwTHhpY3FWd0lHMStaTUI0UXl2a0hxUDNEMVArNUhsMm5lWWxjMmpDbmpOUDhBT1NyYW1zeTBYUzZPTVVra0VCVlZpMUdiTUllRTdjUkRiUk50NnVhNTlvZzlrMzRkUEo3ZFR3OGtEQnMzWm9FMVRMVHFxT09RcnBkcnA2d2Z6VjY1aEdjaFBRcDI0MVlRbFdFUlVCeDFCbUJNeGRjS0N2Rm9FQm1DM3FBS0Fpb3pNbzROemZKSGJZME5SWlZDRmJHeUZETFMwYit1cVJCMkpqeDRBVVNqZWNuaU5YRnVCbkk0VUFMZ2xtc0ppaTNueTJhYVVHenQ1VXJVVUNRanFIaWtLQWh3ZTBDc09BbzZza3BCTFJEZ2VDNm9EZ1UycVloS0xha0hFVjNRdEdhU09zb2xZWElzMXBwaUFBbkppM2tLdFBkZUp6L3BqUlZwdmNOUTdLZ0J1dUIzM1dtQmJXaGRTVkpUQVZEWUgrU1M4MDhVeFhRWnZub0p4SGI4T0N4U3lXTkdFRXJ4eDNnbE8wY1RYK0FmcW1MWGdoU0Q1K1N3eEdValppa0U5Z0RXQnVxTHJzeHZSdkFaODQ2SEFpQWtQUmM0a3Fia0NzUWRZNEJBc291VGJFSDZDV0FpcnROWWwyZXhJRmtoeWlEekNGeHdTWUJDaTI0d0l1bHVrZTlZaG1MSTFTR2IrK1g2b0ZWVU41aUJpeklUVS91THQ2cDRaVXk5NFRXNW8wa3Z6V1g2d3JBbUc2S2diRzkzQmNJMXExaEJwQkFxMEhvRTFJb3NHSEF2Um9Gc3c4WVBHZ2hCdHdGdEVFOXdiMEFVTjlDWW5xWHRUcWQ2dGhiSk9jMHRmVjg2YlI3SVhDc1VxeGNVam9xdWlxdHhTelZRbXNLQkp2eWVIQlBpeHFrblhncE4zbktkM3JxaktkSGJLb2I1a0dkNjBOOWF5QWlxa0xGRUFBQi9jWmNKYW9JRmRQNWZEZ2QwRXJ1RitHRFFkY3plRkYyZGNKNzNNQStaamVnUEFMUXhRazYvWW9JYVJZRE4zM1JTcTlGZUdpUHJDODNPb2dPZGxVUkNxdXFETEJ0NjZCWFFYTkZDbE1Xb1VBcFk4OHVSWk40M1VTQVdzU21uT0t1TFRpalVRRXJrMUlsNWR6NTFJSWJhZ2NhbkJDUWtWWmRnTngzSE5aam5DUkFSVElUVklMcE1KaUNLMERvQURFVWVqaFVyaVJXWWdJMVM5YmJ5WnZKRUlPNHJZM3I1WWd2R21pVUY4V1Y4d3U3cWpsclEwLzNOTElxTzVkNjM3dzZlaUE1V0x0ZmVqQWVsYzFkaDE2YVl2NnU0TjZIL0FML2NaNmptZ3BaUFE3dUNhNGxZbzJIaklwQjIzRFhROWpnM0JsVThCWGJ5dTBYZVBNQ0VXaTJCcXlYbUxreURqaFBEQTI1Q2lCWkZ1Smw0ZGhFSHRTNmRMaTNKcVdvVVl5MDE3VXNzZXFuRjlmNmlPQllpSGIxbWd6cENFcFVYZzhHM2VCYW1kMDBSTHpDcjQ0REY2RFhGWStzbHQydFladjREbEhHd3B6b250ZU1IVUNBcWVsSVZtMURXSk9kbEhXdCsyaGFzdUh3Qlh5Z1FJbkhMbDF4TXR5RG9BZDc1UjZ5N2NCZXdUOUJNYWtJMFFWeTRDcERaUzh6bmxjdmN6Uk9WVGd3SWluVUpPSHBrZXBnWTlGN1dVN3Vwd1p5cm9GOWg5WWdxTVpkMktmaE1xaUlKWWx0M3pya3ovOFFBS3hFQUFnSUNBZ0lCQXdRQkJRQUFBQUFBQVFJQUF3UVJCUklUSVRFUUlrRUdGQ05STWhVV0lETkMvOW9BQ0FFQ0FRRUlBQWtDem9KMG5TZEowblF6b1lGblNkWjFFNlFwUEdKNHhPaG5RenJBby9IU2ExOUJBcGdFMXFCRE9rQ1RvSjBFNnp4R2FHOVRvWjFuU2RKMG1wcWRKMEVLQ2RCK09obWpPczZpZFJBdXZqcE9reWI2TVlic3J5cUxodEJrVmdFa2NoU2F5MFhrc2hiU3hydHQ4blk0MlUydHNtVXJFN04xUUc0THQ3aTNIZnRIN25RS0dkSjFtb1Yvc29ab0dkSUdCZzZtYlVUWUVzeUtxUnQ4N0xPVGtzU2x2VTZIZDNCRUtrRFFBYlVRZS9kUTZpQmlZTzI1WHZjR1AybE5JcldkUVoxaFFRcE9zS2tUUW5YK2hhSjVqUE8wYkk5VGtjazN0cFZXQ3VKc1NwTi9OTkZaWGNiRkRIYzhHdlVGWkU2bmNwVUQ1OHVvTWdtZVpvTFRBNVAwUHhOK29mUTNDM3FEdVllNGx0eHJRbU03ZXpQbUtzcnJKV1ZVZ0RaQ3JFZlE5QzJDMEdkdFFOQTVFVzBmRUxDZHY2MllPdytEM0lnN2FoK0lmaUg0bE5URDJ4VkpsS2pKcFhVaGlJcWtEM2o5R1B0YXEySDJpc0FhZ1ZJQXV2U3FKMVdhV0JCT3NVZlFGRDZpMlZzeFdkUkd0cVE5VzlhMkQ4UXNCRERlaVJzcEltU3pFN05QZGlRY2ZkWlV1ajFIUndyL0FQelBJbnhBeU42QzA2RVZRc0lPdmJOUWk3WVoySURxRE5vUHdNdXFITUg0L2RuOGZ1YkRQTmNmcFZmYlVmdEdVamoyNXBMYm5VRVRjL3gzQVQ4UlBVOW1za1dZeXN2WTQ2Sjl3aGNBK3dmeUNRVHNsa1FibCtiWmFOVXVidDdOVmdiSWNTb2FHaUdaYmVzUzA2aXNyZkFIMTlnd0FHWm5JNHVEWWxkZzUyb1pKb1hjTGdEWkJBK1E0SW14THJYdWJvSy9hZWdCK2Rrekp5cThWT3pOZGs1Uis0SnF2MVVlNkcxcUt3RzFLMkt2N0dtKzBxZkZjVmcySWxuOS9UUUlnMXFjNWpxUUwzcG9HWlNXWjh5am83UnN6S0hNZ2xEVFl2WmVpd0p1QkNKMG5UUW1WeVZWU2tWVnBaYllXZEt3ZzkyZmVuUlhDSlZvVTdOd2dyREZnVkFoVVBLTEc5by92WHBXWUNDMEdBZ2lhSitMY2Y4QWNWR3RrWk1mSTZPdkltM203cVY4RmZSbXR3VUNWaXV2WHI2YWhQWDV6ckxzeitLcTJvaXZvdEZKcVFiS0FuWnAyOTdOTHlCU1pXdlVBd2I3a1JZSmNyYjdLUHZYM0JBU0pWa3I4TTE5U0FiNVBqTTNMNUR5cmhjYWk1ZmR1VTcxOFMxNzhIbFc1V0lqUDBuU2FBK1RvL093ZmdxbXAxR3BZdXhLNnRDWHFoRTZFcUpyNzl3RDFGL0t3QSt4QXJCdEJhMitJS0xmd2FuRVdwcGZaMFBVWjNKVldZb3ZvSExabUtMYmI4bktmTXphUW5HZnFWT2wxZ29jUFVHbnFiRTBKNm5qcmdSTncxSVo0NnhPcUNhWFVDSWZrSWcrT2lmTUNnQ0QxQVoyQUd6ajVWV1RzMTh6eVl3Y2ZwWGo0M2dzSk50SlM1TWV1NjNzeVZ0azRZeE1Qb2NYRHc2c1d1dTdpTW81V0dwYlErbS9xTlRZbXR6UW1ocWFnbW9Qcm41bVJqNUgyWU4xWTJINSttcDZsdG1RMVdIalZGamhKbWY5bVdyWkZqVkhHZDM2SmZtZDhnZ0x4TFY0K01HaU96RFpCbnFlb2NsRnlCUzFmSzQvN3l6R3RvNUxEeUxXclR6VnJZSy9yb3pNdHV4K1J4Mi80YkU1SENHVFgzR1ZrSmkxc1Z5djFHK1RndlEySUd0bzZzeFVaQlVVcldmU1kyVGo1VnJyVGxYV1lkQlo4RjBSTzl1WHlWbU1GU2wrV2ZGd1RZL0haOXVVbTdKeVp4a3cyZTdtdWU3UFZsS2J1U05sV1ZaZ1pITjNCcVZ4OHZKVzN1MUdjOTkvVkxzaGFxaTAvVWZPWnZKUFVtUC9BTGt4c2UxYWNqdUNOdzhsVU13NHh5ZVNveE4rVE81WE15YjJDakNXenVzcHhncENucDQ0TFUvY3NHUlc4ZnF2SEN0N0NJcTZHVWxUMWRZS25vdkxqSndCZS9kMXo4WVlLL3Q2dVc1RnY0eG1QbDNZNVczT3hXeStmV2grSGZMNSsreTY3TVZNV2xXc3lmNW1aeGxaMS9ISUZ1eC9MazZjOGp4MVFxQUZYVEgyS3h5YVVZaEV2eS8zR1NMYTdjYXpKdEQySER0V3ZxdHRCc2NWcmpwUWZzVzRlUk9rUVdFRlhYUjlDbStxL3dEd3o3YmV1bHZ2OFBqclRScnhBV3BWbXh3cDQrcGM3c1N0ZE5CMHR2UXQwSE0wVlhjblZrTCttYXY5Ti9UMWZmUHR2MjZUT3V6OFBrYVBCYmlZL01Ya1hZMkpUaWZaajVXQ1VGbHlVbXpKc0lGcHhpQlNsS25vYlZmWGoyMXRoRGxaaDQ1cTJGT0d6dUdMQWg5MTEybHlWaWdzTm1xdndaTFdGcnJjaklhaVpXTXlPTGpqQjNZK1d0ZjZUeFl3SlRrNkwzb0ZwcVBlbGUyUmpXNVc3RGlWTjUxUTVsZmp0RmtxcmRNa0hKdHRxeDFTdzAxMm9tbXhoM0IzWlNWMFFjV3Z1ekJNZXVzOVZjOWZzTnFWdi9JejZyVGM4cWhkeTZzYURTaFN3QlAveEFBeUVRQUJBd01CQmdRR0FnSURBQUFBQUFBQkFBSVJJVEZCVVJBU0lDSmhjUU9Ca2FFd01rS3h3ZkFUNFVEUlVGSmkvOW9BQ0FFQ0FRay9BUDhBalI4SXduSTBHdzNSMkNFZG8rR2RoaEh0c1A4QWpXSENPRWJCeEhpUHhCOEkvRENQQ1ViYkhDZHAyZ29DT0pzb2JTRVVVVUUxUUU3WWFhYkRQR1ZqYVZaVUF1VCtBamUzNzBDczJpTXd0RlVjTnRrZ3V0Q0JrRXpUVFRXZnR0RzJ3Vk50VG9MbFU2Q3cvd0I3QjI3ZjJWa0VsZlYrRmNLeCsvR0pZMFFkYjBoQzVrSW1HNWpTK0tKMjk0WnRCTUFHS0VSY1ZNbE9rSW9vb29oSGVPdVA3UjNuSzZNRWxXQzBLeGJiZjc4UWtIeVJoMlFETURCSTBOZ1VTR09BYlF5TjRHQVFkTlU3ZG5KcGU4WUE3bzhvdGFDT2tjUHk1T1A3K3k2WDZLK1QxVmxiWnJ3Q2VFZWlKMHNyQ3hFVkhkY3ZpRUZvQnFKMWlacUJteWNCdS9OUnhrQW14cG14WGc3cmNSOVA3blRnQ0VJY092Q05nMnU1dEZ6T1pITHFEN1VRYTFyVFFNNWladDM1UmdxdmhtOE9OUVRtYkNOWkJRZ1dib1hEU21iREVJZzlyY0lRUVFUVUVFT0l5QmxQQThWM3kwbk1UQ2REZ1BlWlVrR1hSTURyVVZRaGtpeEFQWWcvU2NxalMzZEVDdGV0YkJPSTVhRVRYU1RIcVNyNEhTd3RTZTMrRFcwRFdVSWZNSHFtMUZDZi9KQm4zVEM1emhEUUJBSXRuOUtjNEc5RGIyMHdnUklHYWl1REJUWjNSUFdoZ1RycVU0QUNzUmZ2MDlsU0JyVHRHT0VYRWc2eGNkOG9ocm11QWJYNXBidkFqMFBvblZEaTN1UmVOWXNqVThEbzhNNzRlQ2FSQUlQY0dubndubWJhVFJQZ2pHWnJlOGhlR1p0T3NsRWt6Y3lmU2JJeVd4UG5yZ3ErUFgwQ2t1RkNTTStlSTBUU1RCK1VHc1dxamVJa1c2WUtPODk5WUlOQmFhWTdxQzVwQU0wbWNnSURGUm55T3gyNkJVT3lEZ2hSL0szZWE4Z1djd0V0Y0FmcGMxeFRDd3ZBaTRQVTR2Y3AyNElCRHZxZDNjWm9BTURNcnhRNXJScjZ6ckJ5bThzWC93QkJHeWFQNGc4QTFna3pFQTJxVmNtSE9iVmpUR1QzT05nTWlLNHFqWVQ3eDZrcCs3NFpwR1QzVHBKaTlpYnlodlRjaUNQZENXOUVBSnpxcTBVZVFWZ3ZKRTd4RVNQek1weEpONUtjWGJsSkVnVTdBVFZFd0tVRWU1a29qZDY2elJDSmZPN2d0YTJabnJhRTdrOE1ickJUVWp6SkFtZENzUlVYZ1ZLZElkOHNXT2dvbk9JZ1FKZ0djQUNwODFBSC9VYUhXWTloUk01ZDVwalRtcWdITmlzMzZ3VmRzUk9SM3RNSmhCc2V1aWRRQ3lnbVpnekNQeWtUMUdpTkcwNnAzTDB2SVVRTk1hWHlpaklFMTZneENvQlVuUFlJVHZTSjBwYzkwNGcyZ20vOW02YUFEb2hEQVk2aHdNRUg3K2FDRW5yWDdxV3ZJYzBrNHBReDZoRG10M0pKQTlkZEUzU29FM3ZDSkRSZHRBQ0pxYng2Rk9QOGJCSnMzdlVZRVZLYkRZRlR6RW51YXdLVVYzRnBQYXg5cTl3Z1FHbTVzWnhDTzdFSHBRMUhUcXIxaW15bVBRSnhNbVpqOXJoYXFscFRnRGVObGpUejY2OTFBaW9LK1VDdlgrZ2dESDUwMkNONHllcC9RbkVQYTZXa1hyTkx3aHpRSjdvd0FhZm50bENqQkk4eVFQYVVZL1Q5a0E0NE9nVGFPcDUzcVBkR3VTc0VoQ2tvQ1hHU2pDcWhWc0VlVlBmS0Nzc3pLYUNSKzBYL3hBQXFFUUFDQWdJQ0FnSUNBUU1GQUFBQUFBQUJBZ0FEQkJFRkVoTWhGREVRUVNJR0kxRVZJREpDWWYvYUFBZ0JBd0VCQ0FBM0g5K1VpQzcvQUNMbGdzbm42d1pNR1ZQUEJkcjNCa1Jid1JGdDNCWkE4RmtGa0Qrb0dFM0FaMkg0WU1mb2Q0ZXBuV0VRdG96eUdlVXp5SFU4aEVGcGdmWWhMYTNCWnFDeUk1aTJ3T1o1REJaNjNCWk81Z2ZVRm1oQlpBNG5ZVHlUeWlHMkZ3WXpDWTJMa1pqRVZYWW1Uam5Uakh1SkFoNDdLRm9TSGk4Y29xTGZUVDRlcVBqSUdLeHNmWDBxSGVvRjlSVmpmd0h2eXdXQ0N5SzhEaUF3R2Jua2YzdnluOStXR3hwVWwrUVN0Zkc0WHhjTUJiY1l1dXowU2x0aExRejdZdldTU0hmMzZKN051RDErTjZFK1NGOVN6STdHQzRRV2lDd1FXUVdRV1FXRFVGb25Rd1VnbWVCWXVOdjY0VENYRnJMTXpqckd2K3hMT3JiSnRzME5LK1M0WWdyazlSb0MyQzFUNmdaT3N5R092UVEvcnFacHB0L2M4akNMYThGemlMYzM3VzR4TGlSQjR4UFV4cUJrV2FsZEZmcFlvRmN0dEd0UzI1US9xMjl2WUJkdFJ2NVRvUWZSVmpBUHd3RGZmajBKME02YW5vUTZpaFIrQjdpN0VEYTNMNzBQcFZkek9QWXBZV05EcVUzSHNEblF5L0lnOU5iWUQvTjdDeDlFMkdhZUJYYUpYSHFjRFlMa2Zma0U4azd0QjVOYkxWV3FnWTcvQU0xWTJWZFdYckJPeUNGQlBwVUozb05xVjRqV21KeHpwUGhxcTdDWlhRZFNtUVV1RGltMnJJWHNPUXh1dXlQRmFvN1RUZ2JQbGNtYWN4VWNuVVNpNXZvOFhra2JKNHEvZXlPTDBKWHhsZ0hzY2Q2aWNmU1ArU1ltSW85cXlvT3E1T0hqNW8vbU9KdG9PMVJMRUJCTnBVNmdCMUNudjB0V3hvK0tycjZJQXQ2aW5KdXJib0xtZDNXQUJWMVBFalQ0d0I5TFV3OUNuRlFIZGltcldnVktvSVB1ZE5DR210eHVHbXlvNmlveCsraDFxQkNBUWZhTkFnTXB4TGI2MktuaFZlcnl0MTJmYXArZ05hbjFHOS9lUFNtT25jZ09MZllVbjdRQlJvSWdjOVpxdFFkZHpveG1DcjFpc3ptQWtydUtlMzhaNlU5WnZyQ2szQTNxYUJFVUtCcVlGeERGRUwrQnRLUE1Pc1NwVGdIYlhXcHNOOGtpREpVRDM4bXY5REpXZktTVTF2WjlrQURRZ1pkZ1M0ZjI5eWcrb1Bld0UwVENPLzNXeWtkVHZSSW1neSt6VS84QTFMRkJvK2J0OTBaaHB0M0RZYnFkb3VLZjlKcnNnZC9RcXlXWjNMdUNCOWpVTGlkZ1poMWloalpaVTZiMlgyVzJOS1c3RkFIYll2YlZKRW9HaHNnOWJpSW0xOXhEcmUzR2oyQ0UyS2V3WUFUNyttNnQvRnJNTUgybGVBejcxeDJaaTRXR2EyeU0wbkQ2Smd2dlBGVmZJMHBqM3ZPOERRdEJ1TFdkN0lWeEM3YTBVc0pHaUwrbm9kM1pPemduVTJQSUloQldJNnFTcFMydnVVZ1phclNJbDFhQXhzdkhEYUh5cVAwY3FoZlV4c1pyNlRkS2VKZXZrUGozMTRkTjRWRm9ycnhjZTZaUEVNNXJTV3Awc1lRYm5RVHJPdnZjQnQxUDd1dEVLOERXNjFOTXc5Z01JV3NYNkRQdUYzZ0ptdC9lZ1BwVUxuUXplUHZ3R0NXOE54aHpzamRsU2kvalRvZEVZdWRNVll5aTh2a2VRWkZtUTdzNmNqamlpOWdvM1BjNi9nZlh1RGM5elJtdjg2RTBQOXZEY2RnNWZITWJlVm9jdDNyNEM2d1dQVE1GY3pOdHRvcWRtUmpFMGxhMkM2c0tqbXFvQkNUT1FWcnJpQXlCRzBKcWFpMHMxSnNEOGRmOFZMNjdzREpvclYzRmJGQzQvSHVZbGRkdURlQ1JBUHdGbkM4bWNDd28xZE51VFlwdXdlQitOeUNYakpSRXVGMVFvMWppeGlHN2JMVnNpKzZhZkt4RXZxdHNPcThIaTZjbnZiZW5EcGxjZ0txdVI0MmpFc0lxbkcwNU9SbUxWUnh2RXZWWGRpMllvNHFpaTJzNVNmMHpSV3Q1eWNURWFub2x1RlhSakYzcm9zc2NMT0I0akdvUnpiYnd0cjlub0ZaRVRpTDJ3UGxqRDR2SXpuQ1ZZSDlPNEdQZ2hyVG1NdEtJdHhhMUN6VitUM0NXK0tGSDJaLzVDQVpXWFJpUjNydnhoVy9IY29jV29wVm4wWmxPZlljbDhTbnNiWHc2VThuYWxWT0x3d3ZUT3BwNCtsYXF1SHF0c3VLVjBNdUxVdGJ2WFZhNWVzTy9ZZzRtVGRWWXhRWXozYVowNGV6SXl4ckg0NThQRmJIdXhyS2NXdnJVTTZoejdHUjQwMmJicmlOdlZaY0hEQW1nRXN1bzlMMCtubzZnbmRTQit6c3ZSOGpVdVZFeVdaZVE3ME5vK0FXcHQ2cXl0ZmRjTE90SEZ0aW5sRUdmelZpVjhOajBrSzV5THhtOWpZOWZ3YU81TGhxUjNveVBNaVVXVjFlRkFaaStWTjNTeHRNRWFuYTJla1pTZ0l2dDg1MlV2Ukt5SXByQ25zZTFmc3huTjFJU0dtdXZIVzBVT0NoU1hCQW0wOS91d1czblRZOXZqUnFsQzJLeG1JNlVXNkx0NDZXbExxdUthUjRhM3h0NDdyYXhLdFlpTnN4d1VQcXMvY3FzMGgzM3NJN251M3Q0cGRRUUVMSFlIaVlpVmxCc05ZQXJIci8veEFBeUVRQUJCQUVDQkFNSEJBSURBQUFBQUFBQkFBSVJJVEZCVVJBU1lYRWdJb0VETWtDUm9iSEJNTkhoOFJQd00wS1MvOW9BQ0FFREFRay9BUDBBaDhDZmpSS2FobkNHZFZwazdySHdiU1QwUXM1N3EwTWNEOEdVTFBoS1BBb29vb284U2lpajRNSWZwRkg5TnFLUDZMbGZoQ0ZIZ3drYmdMS01vSGcyTzZqNUk4QlhFVWpDUEdVVTBJWDZLQUU1T0pUWjcycUNGNzZxK3liQ0hISEFTRTFDblRQRUlLMEtIaHh3d2dzY0I3cUFneEY3L2J3aEdYRUs1OFhyMzQ1QzE4Wmh4aU50Vlc2QWs5ZmttY3J4dU1rVFlNNjBJVFlLYWdnVUVDZ1I0dEZud25nVWNJU040aWVnT0oxSVVGekNTSkVHRFpCRyt5YjZBN1dKbjhXaFo3elBXZkRuUWEveHd3c0ljZFYyV09KbmdPQUZDY29XVFlPL1plYjJiWWNTS090VEVWUHFteVhZdG9nbmNkczZyMnBMNHVZOHcyL2J3bEZIZ0o0N2NTalJSQ2NuU2lxWkdUcjBIcW5BQjArYTZJME90b3VjNTBqelMwQ00vWGRSemFTMEdDTnQ3OVZrVzRuSWFUMzlTTW9SMzhKUlJUazVGRW9sRkhnRU1xbkhRWEhjNHRObjJiZmV1UFNkMEpZNTFqQk1WU3lLazJWYnMzK0kxeWhKR2Y2UUJ1NVZqODU3L05ENEFabVhhamxPQnRJNlhkci9BSXkyUU1RSnhHOHpHNFRvQmdnYmtWOWs5cld0SUx5Y2d6Z2Q5dEZCNnJJSlJpYS9yWlNTcmszNWRkNTFSOEdoZyt1Q2dYTkxaTUQzWWR5MzZ4ODAyaTBPN0E0bmFjckE4RFplQ3psUHFRUjZqN2VFZVIyWUVrN2VnTnd2WnlIQ1ovNnhwQUdIZExoT0VUSUZ4TUpvQTVZZ0FENXhaNklRRE1kWTRhNFJXazYvMkUzbDlreXBhUlowRm5PNUE3d2dlVndKQUZrUm9UajE3SW5XUVJnN1NLK2NjR2N6anB1TlVEeXlDSk5FT0k1bW1OUVdoT0R5MG1xSXZHOGN1R3B2K1J4SjVtM3lzSFJvaXlUcWRJWHNpMXpqcU1DNEl1Z1JsdTZmNTVnQVl6a243UVBWREtjUTh0Sm1DUTBDNWdYcEtCNWN0RHFjUkpHTk1UQjRFY3N4R3N6Q0Vra045VGQ3QURKVEM3MnVTWm9FRWo1WXBNQWlwR1FNUitVNkRvRFJLTUhxbmU3VWVBVWczbGFaNVNEM3FQM0ZGTURRTVFJdWNSM0Uydlp3NTVtREJOMkxKTVZwa0pvRW1laU1PdkVwM056TkFuWG1KSUk5TjAzelB0eEJQK3dvSmRJZ3hFbWhLYkRtbTVGamZQMFRSTzhXbldPNk5rRWZRcDhPMElrOXFXSFpnNFBiTVN2YUJ6WmtSTmJwbm1uUDhJa0NJblcwS0lyZ01iNFV6MWpoUjIrc3ExcEZiMmdJbWFGQy93QUp4T2duNy93akxqZlNEZGROdUZDS2lzOWtKYXdod0VhYWo3RkdHNXhRQXQzeTIzVGhVbmxKZzFpVGVVSnVqay8rb3RObHh4cjlFWmVTYUZBTkgzSkt3MEdLQXZJdnZWN28zNjBoSXgySndWalhoL3NsREF4d3REZ2ZkKzNUOWxOb1djS3BSUm1CSG9oNVhBVDNHczVScENaUnQ1ZzloZjNoWko2VU0vWG9wRVpSeGY0V0ZydzIvSzExNFlLUEFiUWpTLy9aIiBpZD0iYyIgd2lkdGg9IjE5OSIgaGVpZ2h0PSIxNTkiLz48L2RlZnM+PC9zdmc+", + "description": "Produced by volcanoes and industrial processes, particularly the burning of coal and oil. Results displayed by combining the latest and aggregated values and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'sulfur', label: 'Sulfur dioxide', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'sulfur', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sulfur dioxide\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 600) {\\n\\tvalue = 600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 600) {\\n\\tvalue = 600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#3B911C\"},{\"from\":100,\"to\":200,\"color\":\"#7CC322\"},{\"from\":200,\"to\":350,\"color\":\"#F89E0D\"},{\"from\":350,\"to\":500,\"color\":\"#F77410\"},{\"from\":500,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:U08yLXNpbXBsZS12YWx1ZS1hbmQtY2hhcnQtY2FyZC1iYWNrZ3JvdW5kLnBuZw==:U08yLXNpbXBsZS12YWx1ZS1hbmQtY2hhcnQtY2FyZC1iYWNrZ3JvdW5kLnBuZw==;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Sulfur dioxide\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"public\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"µg/m³\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "sulfur", + "sulfur dioxide" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_temperature_chart_card.json b/application/src/main/data/json/system/widget_types/simple_temperature_chart_card.json new file mode 100644 index 0000000000..6645b0e427 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_temperature_chart_card.json @@ -0,0 +1,31 @@ +{ + "fqn": "simple_temperature_chart_card", + "name": "Simple temperature chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAe1BMVEUAAADg4ODf39/g4ODg4OD////g4OAjTMchISHHx8c8PDzj4+Px8fGQkJCsrKx0dHRaedXk6fhYWFgvLy8+Ys7I0vGCgoKsvOry9PuRpeN1j9yenp5KSkrV1dWfsedohNkwV8tmZma6urqDmt+RpuNMbtLW3fW6x+65ubn1i4vJAAAABXRSTlMA7yC/r1EOHTEAAATQSURBVHja7M9JAcAgDAAwytEX/vUOGdAlDtIAAAAAAAB+p4/I181+HrHX83b0Ngs8zmS0XCVElUiKXEbkNh979q7qOAxFYbhasPZFEkjgxnJhV3n/JxwfJwzKSQwDw3iUg34wEtuNvsJg0ID01oD01oD01oD01oD01oD01inEpohP6hSSkzYSD0cbuu0UorGGBpJSZUoRf5yo4crOIbcc0BZ4iOJmgJhE27eAy7EA+wCwRQQeo8Fn3twFMIHLsgESBW2XQbZ3ECtV1UHdl5qZIdTMBKwlc0YqXE3zSlkq6xIJBEVkqQgl64SmyyCpvoHkDJQABmzcMBPCBYG+0RAUSQ1LAGqC0PAbYjDGY9R0DcQLmV4hqjmXhONUgkjIcUgJJWclUgYwr1q+QQBhzpWCpmsgiXvyCllFxL5BFkqg7B2QSPH8BhJFxNF0DYRfpRfIpIb5GRKR1IUzZD4goZhperxffL1DvEzwgLZrIIV70wvEV1KfIcoyA4Esd4gpS81wZfSv7R2Cm5L/42OfSRbDa/48FJr5fe54ZP5Y7k8zbrsIgpgmw2ktBF10QP4iC53cSvz8v99Pa0B6a0B6a0B6a0B6a0B6a0D+cb7Ns/jHQ36xa25LjoJAAH3qAgFFvBITo1k35v//cMHWwR3NxBqtmVjleZjh9tAnDaSpCpfUEmdi3yIli31PCE9SxvcswtkQfxkzsWMRGXP3kva3EkmLMKyu6qPbmG6u3HxehTn06GuFs6vgNHOdKBabiKiQdNR9sH+xm7SA4ECD7Wvdz17XJYRyQDAlW4joxDhUlflL0iHs0HZr3YuSRKs7aYfJ+t6ZrzJhEhCXknUiGFuiAEyopOq9bGoqQk7QkdqY824wtwLGT5+c51LEWUYZx3ZAPRjh0fMGIkmfidQIAcDD5AMMrQkVOlorWHSLkg+7EFvL4YyyiOImEozBf0RsvYgmhIBFYaMxEQ6Cagg6ScgdXYexnJiR53g3xm4BIH3snrHJqPR4wGj5aTn9sz4jSmFsKYYWdjsLG4/etajrRtuTbrOFqKIovjrLsbyx8c3kU97/N1inzynhK0UcBZ7fEHcaGk2XNLCADHeQ70w4lYAILyhhQhkzvokIHhH1QmTZwcCoUUgAwhh/WbHI0mpKRulF8uUiqkN/dPGy2kTkTPs4BM2GkWBRDWkkYun7t5hKvlSEdIQjjwY2ErlE0JPhverFtyWJDDLpe9j0mVH5hoi+E1LAS5FikUjpPn4R0cj3I1fgLgRVskUiaUeLncpdptXk1nI0VhfRSqmnR30UdnChlPkCHMtVzq9FpgEmenx5We6YmjG5WefuhhrmYRFsjxN5XaQgV0KK4YsS5Ry6Rjn0rWAWz+2sHxZxHr1ArVAonE2dGsqA/NlLQ8AKVhWNpDghDzwbySO1o/lspZyc8rypJ5pzNfnPioTEUeBNjJxggpskoZ73GD9cf1cEVPHFy0mdkk5jflZk9PIDv0VZTtsqeIpKWw2zeIxmAhy/L/ItRDYpa3cpMpeOPYr4c+nYo8htLh17FJnhEDlE9sIh8m4cIu/GIfJuHCLvxiHyr507NwIghIEgKJ6TdfnHSxA4ArozWH9rqjGkmoz2XSFjFDm37/nnPRGk6DNP10YPAAAAAACA1yz9maqJM0bCeAAAAABJRU5ErkJggg==", + "description": "Displays historical temperature values as a simplified chart. Optionally may display the corresponding latest temperature value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'temperature', label: 'Temperature', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'temperature', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#234CC7\"},{\"from\":-20,\"to\":0,\"color\":\"#305AD7\"},{\"from\":0,\"to\":10,\"color\":\"#7191EF\"},{\"from\":10,\"to\":20,\"color\":\"#FFA600\"},{\"from\":20,\"to\":30,\"color\":\"#F36900\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Temperature\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"thermostat\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"°C\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "temperature", + "weather", + "environment" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_temperature_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_temperature_chart_card_with_background.json new file mode 100644 index 0000000000..9c8d069c8e --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_temperature_chart_card_with_background.json @@ -0,0 +1,31 @@ +{ + "fqn": "simple_temperature_chart_card_with_background", + "name": "Simple temperature chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical temperature values as a simplified chart with background. Optionally may display the corresponding latest temperature value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'temperature', label: 'Temperature', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'temperature', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#224AC2\"},{\"from\":-20,\"to\":0,\"color\":\"#2B54CE\"},{\"from\":0,\"to\":10,\"color\":\"#6083EC\"},{\"from\":10,\"to\":20,\"color\":\"#F89E0D\"},{\"from\":20,\"to\":30,\"color\":\"#F77410\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Temperature\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"thermostat\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"°C\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "temperature", + "weather", + "environment" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_uv_index_chart_card.json b/application/src/main/data/json/system/widget_types/simple_uv_index_chart_card.json new file mode 100644 index 0000000000..35e5b73c92 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_uv_index_chart_card.json @@ -0,0 +1,33 @@ +{ + "fqn": "simple_uv_index_chart_card", + "name": "Simple UV Index chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAXVBMVEUAAADg4ODf39/g4ODg4OD////g4OAhISHj4+PHx8c8PDysrKzx8fFYWFh0dHSQkJAvLy+Awyzf8MrV1dW6urqCgoJKSkqdnZ2enp5mZmbv7++43Yj3+/KIxzmg0mFA+mXkAAAABXRSTlMA7yC/r1EOHTEAAAOxSURBVHja7dztVtpAEIBhQIeZ2dkvyRdo2/u/zG6wNJSQhlAbNzjvOUKC/PBxd5McQVaapmmapmmapmmapmmapmmapmlfrs3Terv0njfJsX6FxWfXm9WzhQfo9Wm1hYdo/SiQrUIySyG5pZDcUkhuKSS3FJJbXwDSFLCkhiFO4Kxyn25EbOB0XwRKt5RuuwgZ5m4cUgYAIaCDhV9509ocRHfaAUbOHkK1tcwgHi4hISab8SdISSzUbsuRxVIClOlLZlKNTy0yiB2jgzBKKzhBjKlNolXo6rQX0CWjYCFIMNZMkBpT+z4EnG+3f0M8EAqlZxbIdEQSOGcqGGsOSIkOsM0XGC8hIVrjO0gAQGEskocZ6+PAEEYL480wImTfR0TS1iXEYkC6gBTvkCKRmC0Izrbwx6dWYRCd7fblOG2k1WANFxAbK2iQ2zsbgExI4rHmgdDBUskgoXuoQsQKUg3KJQQkqZGP+nYJWYp7GGsWiPj380h9NiaWLAxm6feknL/xM/uSGoYEhiX1Ba5+F5ZCckshuaWQ3MoBYqmwy4dQMJgynpYNCRFdI9IcEMOSIR69hWOUNpcLqc6HIaBfKkRax7lkv0wIGQN/5CItEuKRLmTRLRFCWF2ZawuEGGOvrH5eHCSgQC+bdAuDUPRwJcZqYRAfaeDUUuYL4X0IJcF5zeCydpE+C2JZuBj+boh4zMn54ckNzzn3ORBx+LfLVzbo2tEoGoOnp9gKawtDNbj/BAjV6JqSWA5o5BoTDXfHKTzsuRQfsbIw3AGL2SEUI3ek/rQv4vmvnrzBVKwYuiYdg6dDdm/fdzddLVG357HsOQxdricmGIv718GFiPBkyLe3Hy8vL7vJV0t1tH3nPYU/5h61k7Et+nIaJClughCG/tns3x2t5LTibFmZViBERelNa5kA+f62290C8Zc/aIUy3TF0sIvO+xrTXVN0D3eWm6YWwC0Q7q/uswXPJhL8Q1w5Yw6BLXT1LCMQuA3iDPSq0OyLdlo7NAQfXmexHwcRFOhXOjwWg4X/GH3giBgDV6MyhHzewjEOEczqRca7IWQM5NS9EDKRIKfuhEiMmb3B7h6IDQZdXuNxD8SGiC6/1+CnQviAOTJ6kJHYYQx5/pfcBIgNJlvGFEiT5dK4A3LImdFCPv/FUIUoJOcUklsKyS2F5JZCckshuaWQ3Nqu1vAQbVdPD/DRQQD2ebVZ5/r3hAm9rtuPc3reLr3102alaZqmaZqmaZqmaZqmaZqmaZr21foJlfySPp88UEgAAAAASUVORK5CYII=", + "description": "Displays historical UV index values as a simplified chart. Optionally may display the corresponding latest UV index value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'uv', label: 'UV Index', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'uv', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"UV Index\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.ceil(Math.random() * 4 - 2);\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 14) {\\n\\tvalue = 14;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.ceil(Math.random() * 4 - 2);\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 14) {\\n\\tvalue = 14;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#80C32C\"},{\"from\":2,\"to\":5,\"color\":\"#FFA600\"},{\"from\":5,\"to\":7,\"color\":\"#F36900\"},{\"from\":7,\"to\":10,\"color\":\"#F04022\"},{\"from\":10,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"UV Index\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"light_mode\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":null,\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "uv", + "ultraviolet", + "sunburn" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_uv_index_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_uv_index_chart_card_with_background.json new file mode 100644 index 0000000000..011d9b6ee7 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_uv_index_chart_card_with_background.json @@ -0,0 +1,33 @@ +{ + "fqn": "simple_uv_index_chart_card_with_background", + "name": "Simple UV Index chart card with background", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC8VBMVEUAAACgl2Tx0o7vz4+pm2bx0o+1pWekmmWbkmLs0JD10oz/9Nr/9d7/8tP/8s//8tH/+OH/89f/9Nz/8sz/8tX/9tr/+ef/9Nf/9NP/9NH/+eT/9eH/99798dP/9tb/9tz/9NX/8sP/88j/9M7+9OD/+uv/88X/8cn///8gHxv/++7/88r+9N7/+/H//PT//vr+8c3//ffy6tX98NH57c/98MP/yFH878/+9eL169Tn2sP679Pd18v47tX37dL+1H/k2snh2sz27df/9Mz8x1L779H00or88NXk2sT16c3/2H/7x1X47Mv77szr3sP3wk3/1ob/3oTw48X/2nr4w0/404r/3IHGql7MrV7w6df67cP7xE/t0Y/604SyoGL/237bslDo3MbHwa761Ib70nvz7Njv4MP/1Xz+0XH/zFz8yVve1cXh18T36sP5y2Px69ju6dj204v/4In/14Lz0H7AqGK7o168qGfCplzz5sT/1nf16Mn5zWz+0Gr/ylXw59H/4Y7+1IKun2bz0o3r4Mf71Yn91IX/zmTgt1T3z3GzomasnGP/7br/13n/5Zn90njIqVnKqFHx5sv+6rL/45PEpVXUrlHw5Mn20oT/2He/o1nOrFY8OjB8wyK4pWewnF7HpEv+6ay2n13Pqk8vLSb/6KT+5p/v0Y/GrWbRsF/679X3z3bjtUjr4crm3cqspZX10IDovFJYVU3y0YW3omHWslr00Xu+oFLPqEY9OzXNtniQiXTrwlvZvHPxzmqmmWKcloXyz3h0cGXYrkfk2bn61Y2onGaspIpzbl23nFXFtH7QtGzbt1xLSUCPin3oxG2BfGzj2bJZVUb88tuql1vsvkbVzry/sH332Hne5qe5s6THvqLlu1lmYlnxyUfe5af33Y7AnEL346j43Zb224ntx2X424HvyV70zlXx5bs2NDCuqJvHvZm5sJUqKCVBMwitjT/VzKri2bymhjmZeTPWzLChnZSWi1ql0lm+23y12HE//selAAAAC3RSTlMA/e8gIL+3v7CvrwEsfwEAABdKSURBVHja7NS9btpQGMZxBMrQ8mFUih05leIFUaEmgcESG5sHJlBkpiIGhsw2GxMoMpPtDjDAyAC9ktxXn/ccvz5poVeQ81eUjOGn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", + "description": "Displays historical UV index values as a simplified chart with background. Optionally may display the corresponding latest UV index value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'uv', label: 'UV Index', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'uv', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"UV Index\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.ceil(Math.random() * 4 - 2);\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 14) {\\n\\tvalue = 14;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.ceil(Math.random() * 4 - 2);\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 14) {\\n\\tvalue = 14;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#7CC322\"},{\"from\":2,\"to\":5,\"color\":\"#F89E0D\"},{\"from\":5,\"to\":7,\"color\":\"#F77410\"},{\"from\":7,\"to\":10,\"color\":\"#F04022\"},{\"from\":10,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"UV Index\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"light_mode\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":null,\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "uv", + "ultraviolet", + "sunburn" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_value_and_chart_card.json b/application/src/main/data/json/system/widget_types/simple_value_and_chart_card.json new file mode 100644 index 0000000000..e19ff918be --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_value_and_chart_card.json @@ -0,0 +1,27 @@ +{ + "fqn": "simple_value_and_chart_card", + "name": "Simple Value and chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a single entity historical telemetry values as a simplified chart. Optionally may display the corresponding latest telemetry value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'temperature', label: 'Temperature', type: 'timeseries', color: 'rgba(63, 82, 221, 1)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'temperature', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"rgb(63, 82, 221)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgb(63, 82, 221)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Simple Value and chart card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"°C\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_vibration_chart_card.json b/application/src/main/data/json/system/widget_types/simple_vibration_chart_card.json new file mode 100644 index 0000000000..f8acfb9361 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_vibration_chart_card.json @@ -0,0 +1,38 @@ +{ + "fqn": "simple_vibration_chart_card", + "name": "Simple vibration chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAh1BMVEUAAADf39/g4ODf39/f39/g4OD////k5OTYGDjg4OAhISH1xc3j4+OQkJD64uY8PDzdNVFYWFisrKx0dHTiUmr98fPy8vLaJkXnboLsi5vHx8cvLy+enp7V1dXwqLSCgoL409rkYHbzt8HpfY9KSkpmZma6urq5ubni4uLumqjtmqjfQ13gRF1p+9dsAAAABnRSTlMAIL9AEN/GQiaNAAAE7UlEQVR42uzPQQ3AIBAAsBswFgyg4fwrXOZgTyCtgwYAAAAAAMB/7dpci08t+Wwu7xpx5ezbm1mjHPDofZYY/QhDZDEiqxF52a133YZhGArD2xkIEgQlgIMu0BLb7/+GtRu3zdAARQeDafVPkjh94KJoTUi0JiRaExKtCYnWLyDOCNgTCOcjebgXAGvmkSGEbyo3x2X9HDJM9mrHR9UAmGJr30AKMXoOCOH1vgxNK86kOpwGtgShIgkorTR4kxWcaXAXB9oxcOEmjMt6DimbnhBR7ucbFSRiZIOQLSSQWjNXy9TYaOnH0DSrgalmVVzWcwilL4hk3LOMxXCHAFLfd8QJsAWF+FAW6uhU+DT/qOshUp3SJyQRiwIQVbIPSCIAJMEhToP4AeKi7x7YA8QBanEgXL4g7jgzMpyQxnrDAWm1FzV0Wj0Rex28a+NA4P2ErB2fJU0AxoJ2W0gZzQA30sXgStumjE2pbmAt+ykEBBi3vFc7XqYnEJajhNfpP38aYzYh0ZqQaE1ItCYkWhMSrQmJ1oRcGa/F/wCEjfbk9SFapZdM49Uhb+yabZOiMAyAv2UyLZairQVB3sS7Uff//77bNJbDWbrj7I53cufzAZTYnTybBBh0Lda0a0SycJFsAGIrqoWLlBvep8dli+yps4imXLbID7EPs7JfpkhVimPChQhDskiRtdisSnoamwGEIVmkSJYCVCIteUR4SL4poiyMWAWPZJelAyeeiIbKkh13kxrtvyBiz0b6feEQdQGe3iC6XE0c9RX4jIu28Bt7cPjWRwY73QyiimQdHZK4iKWM0YsYRO0QD/S6RnQacZKWxCvwCRJzmJCjKZyz8+20fd+U/HIunFCRxLC+V8QhITn3E8CB32jsLLQOCwj06KQHPiHHHiZ0BqDD2RXcTmLHnfWBfZkeB5E1g1jdKYLm3HPuHV6oQj75HrEFstIQOFP4Ftv6HJVs4YrTIWCBUU7b2WvfT+qgNK2qtExmf8YwZFSNlWjubC0AGYpw8F2OHUCB7tpgnIWXMrf/++5EI1Urak2tgKh9Z9kO3zGKJ8v10Wsf37YPe4hDJps7hz2IIBac4YU2+hpRY5ugKTpzVqMI4sXQJDmj0UuGNnpDl3faz1frdAtzNOKuX7uwyfaLIpo3Y4Qx6AmJ5T5UczE6LqBCPS6SNHHSuVpKNTfrGXyN74t0zvSyQNRBhIpgeVHNlat9c7a8iARYvoCPlM1fE2EKPhJE4EbEcCNSp50lkJMk1Mwoi+oPiJipSAs3KMQ6JqLwwp/JfQ/2EGcndg8TCWctg2Y8a/WIFiZw5jGRM9ZBt891KF1k1rePE+k4NYeHkNl4HWEnxQPQx0Q0d5asJbk4ikRI0gweJ3L2u5PfWq+jNOYUL5Q/kntbVBERyUeomvz5uMhQJg8UsTSkb0j5eh9tHKeE2I1HKPGISE6dxX9H5wdNayOsRAUPFAHV4TsHC0ThENG0nBjfR2pEdCeIiegwT8ogr41QiRU8RASUCilIOQ63Hc+bNqTUyvZ3WKmwlN9Mb3wVrY2RlMNTP/ttawX3kKRp8tQi97IR+3/jaXxT/R9fK7xElshL5Nl4iTwbL5Fn4yXybLxEno1f7dwxDQAgEATBwIcC/4ZBAuVzmXGwBlZIN0K62TnLncqYII27pVr/b6lmJY3CAAAAAAAAnhww9gaQpe5WwgAAAABJRU5ErkJggg==", + "description": "Displays historical vibration values as a simplified chart. Optionally may display the corresponding latest vibration value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'vibration', label: 'Vibration', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'vibration', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Vibration\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"let factor = 1000;\\nif (prevValue < 1) {\\n factor = 1;\\n} else if (prevValue < 10) {\\n factor = 10;\\n} else if (prevValue < 100) {\\n factor = 100;\\n}\\nlet value = prevValue + Math.random() * factor;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"let factor = 1000;\\nif (prevValue < 1) {\\n factor = 1;\\n} else if (prevValue < 10) {\\n factor = 10;\\n} else if (prevValue < 100) {\\n factor = 100;\\n}\\nlet value = prevValue + Math.random() * factor;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":0.1,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0.1,\"to\":1,\"color\":\"#FFA600\"},{\"from\":1,\"to\":10,\"color\":\"#F36900\"},{\"from\":10,\"to\":100,\"color\":\"#F04022\"},{\"from\":100,\"to\":1000,\"color\":\"#D81838\"},{\"from\":1000,\"to\":null,\"color\":\"#6F113A\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Vibration\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"vibration\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":1,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"m/s²\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "vibration", + "tremor", + "shake", + "quiver", + "jolt", + "oscillation", + "pulsation", + "resonance" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_vibration_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_vibration_chart_card_with_background.json new file mode 100644 index 0000000000..8155689ea8 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_vibration_chart_card_with_background.json @@ -0,0 +1,38 @@ +{ + "fqn": "simple_vibration_chart_card_with_background", + "name": "Simple vibration chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical vibration values as a simplified chart with background. Optionally may display the corresponding latest vibration value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'vibration', label: 'Vibration', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'vibration', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Vibration\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"let factor = 1000;\\nif (prevValue < 1) {\\n factor = 1;\\n} else if (prevValue < 10) {\\n factor = 10;\\n} else if (prevValue < 100) {\\n factor = 100;\\n}\\nlet value = prevValue + Math.random() * factor;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"let factor = 1000;\\nif (prevValue < 1) {\\n factor = 1;\\n} else if (prevValue < 10) {\\n factor = 10;\\n} else if (prevValue < 100) {\\n factor = 100;\\n}\\nlet value = prevValue + Math.random() * factor;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":0.1,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0.1,\"to\":1,\"color\":\"#F89E0D\"},{\"from\":1,\"to\":10,\"color\":\"#F77410\"},{\"from\":10,\"to\":100,\"color\":\"#F04022\"},{\"from\":100,\"to\":1000,\"color\":\"#DE2343\"},{\"from\":1000,\"to\":null,\"color\":\"#791541\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQoAAACACAMAAADESdfxAAAAflBMVEXe5e/d5O7Z4Ori5/DU2uTc4+3b4uzg5vDX3ujV3Obj6PDc4ujW2+Pl6fLf5evh5e7h5u3V2eLe4+nf4+zb4OrZ3ubb4Obg5ezY3OXj5+7v8vfx8/nq7vTn6/H29/z3+f309vvz9frl6vDs7/Xt8fbo7fPk6vTn6/T5+/7i6PLvsfbCAAAtr0lEQVR42myZiZqqOBCFVUa7b2ghE5YYFlu2b97/DefUEiPMHBWifb+2+Tl1quCe/k76ebii1bV+8pPfTQM98CT9eTRhCXnLavCA8qErHJSLim4tSI5U2H7x/NaHabCqobcenwxLQQqDLUSmyFUOK9X7F9ckfDVWUU3Tsur68v3dNHV9Y12aP3++/3x/46+9Xa/X20VFy9sVOp+xiYtTZozJvs5AkQQUzc+OBVDcGxGT+NM0dp3zvFUaBCMfe+tY8pdvk1UU2PjhpQfqlylYFqjMtJtX/pE9onAgcUShLOibI5WqxrfX/GhBRUlcLrfL9+PxDRB4CBslcRMSt9tZKZwZyVdmsiwrjyj8HsUPo3goCpAA52LZcpJwoJMU+llJOHqE11iwHG3CNHp5O782RWHXhfahM4Ii7FEwROVQ1zsUaeWqik2hMC6X77a61tEURAJqbzh0/GMlccWGdAUBvARFBRQmK0/nfYEUtjkUSHsf22iL5kEo3DLkwkKFkhhiQbC3Y0043i69nnS7whaiUVbLwJ9vc6Gwipx4fqJw+wKhHS3AoYYpGETNG5iiUgs8fkCBPIEDhwciCTKEksCDNoIiM4zitHMFo/iB9D0W7RxRPICC1A6LUxQqty6ucCqgQE34hCK84pGOr9GKQj8wkc4SinGMKKyUByImFcjBFMqkwhsiQWoZSw4SpLph+77L45JMQRiIiLiChIUHCqqPPYqmsCgQRkEvWgFF3kApOttt9QmF5mZ0RawQQcEw/Np5L7boJqsa2BZGKmTebEThSAmFO7gi7is81RTsikvdtFUtJNoHKDCLRki078xkY0hWKgq8LTPKitOp/E+BJFMwi8aMrlGJK5p5Dbkoopinufi0RY+znTQ/Z1vw6Z+fY5AaMdOGyAjLwhGxWEH1icJprUDJC4wHFCo8QUGelAXYMooLmQIYONVqQUOmQKJGFGCgoUm6VSCRUKTjLgKyIpHA8rFHQU+7jrlKUdh+K0hqi2JD17CRhrf9Km9s2fWEorSA0KcKCYv5LwomwGte1PQQODVnRSVdrJZQbPL6hsOl1UNRNMjMhEKSQhuo7ggHmwKt9HTO9ih8IFdgxTxo2YTNJxJcJdWy7FDQKCEVoip0hFAWdnvOymJ+DSHLQhnCOG1cISN9vIz64+BYWKobFAVUA4F0KDgCqtOAcUGN5ByYeAGEJEVTX7k8mmSKq5iCe0cMTa8oTjsUfwNFqyQEBejYze4KpGncsLoPFjg7bug9R4OqWCcmobJTZ0mcFiaEkJXBrH0GfwwdfToMhceumE0hJBQF6eCKhEKbB0RU2BI3MsW3sqjrlJk1sKSk+AhNrQ9CcXBFKa4QW3CAPvwWmrdgO/TyrfORhKCQsCiovkXjc/RQLJHtGbyFyBZLgOAMtJMSFdIHaiGLumLW0cwG2n1OGByVjlWRnFpCUPBaSQAFTRVN/c7MepcUu/ZxO2dGXFH+HwpaMQtGUWxmhwKauzmPanmL8br4ZKE+YBbXwgfkqGp5ziZkQBF4yggYtmgbiJOdtati6Q4oqkgi/z8U7ABJCnZEaqTte9CEoinOKnyIqVtS8+sDxQ+hMC0RkHd4QW57DxaE4hsL2w0JBb9QEV6DU6JueFKnEGdUHrZIM3dvAmRhi8GyLQjQ6KnJmLFwEYX+KkXBWVnsXJFYtC3nREoKkFAU0GGmkNnqukfxpSiiBIUg0PKA2hEoVDTbY+e6xSUSJLe9gpAopJ2G12IhRnGtigCXqMbfLWRUI7CFyazpKT6HRVrs2xXBRXEvESsUDEKE2hQU4v/PyzC8mASEn6b6UBDSOdJUEVGYPYpwbwUAjPHXX39xbo6ba5IoOPOlLyIKhRFeW8HSs+mX19sW5wpN5He2otC95ixkEHIDTAayxdwF/lmctQLA8mHrhIGodIyiihIOGpuuUhSSmUBxEVPEzIwj93vcTlGhDQQXIscCybFLNGCNZhyAIgnma+HqNwoRpipfqBxpfm7RFnhaO/VWNT87wyjM8jJZmPuN+IwyhEYUhrZxwhBVTlBggU1qpMkU9TfXh5rikq5IWWmsgCKKUwYZjN0JhcRDkwkKLFlki2YebNM2O5X9cEDhBlSIiP9c53XI9p6bKOpitKrhdzMZaZ465OdCtkB6Uo/Z9JcYo1cwikLq48MV+SeKSg70gvqIphAOKuWAh5oioSiZRFaiPg6uyO5OUCQ9whCag1y3pgqRV3gNRRT/2einaouy4pFzsqrQo0RIZnuOANJvJcZOwyjGhIIRHFAUSsLl9TspckZBNmgaDYqGSKTM1Oah9cFKUcEq4Q5ziE2gUEUiDzuM7cEW+dr7dMWoFdJrDyEUfPuqj12jOAPF/LtZ1fzqjLDoJywoLeaewY2bogiMIrqiEhJQQqEkZArnA62FBNTG8tCR+4AipeZZUSArDige5b3ao4CKYct3uYkha5xGl1CkChE5fm5UEmqME92tQJCqxufALNBQlwBbDDYsK0OSsLBA8Tl3VgzX71GoaOSQ1Gy/ozQ8BAWgqKSRqgRJJjpRTz26wgMAPzgq6MN8GPL2EBa213aqRSIV4pWEnNGUlN6fT95zf1Wtz9GgjQSzoETMMBnMF4a66ZJQQIwCOz565XCYKnASlEQyxeVj5MY6YjiEptzW1GETprjvsgKusMkOQEGLdlvcEYXrejpr+wqZkieYRUpKoDh7xOUMX8CSdI0+zZKcfW+wWYLMF2EJMoGbgqRZUbEKW0VxUNWq6o2CSOCpR68oUmT+DwoQgA4ofhRF9pMkk0WzLZZRfODIhym4PYsK15+FVRr4TrZFUFeccI/IBrwvQaLUjpoZQ8m5YDPNMl+EQXJzniMKy7EpUVGp/AHFjdU24omIAqXBEk/EO5qMAGfmjNcNKCCau3coNCtC84FCWsi8zi2kFPBdoDJPw8cNJpr0c/vqvKLgydZ5pEVEUZYe/QGzBud1WXJHJRRUIsZ0XZinkVrIYI8osBNTJBQukXDv+mjlOkxnCiiRkKvz5ImTbK5nQWGyU2nuhwLxd/NfFGHdwKFNIxbeYKTCFJh8ARTF+gyCQo2BcXvSAgGJkgapZ8iUBdZ3Q7r302zG1xaAA4EqeRJR5AWhqHYo/GdUVIriihkbFNQUon0nJRDKAhblF6JCXfGVKYrEorrPguLPR40UK3ITiqaQdooKyemRblrMz8FbG+MOX4smslE1lCWeRARlERgFNE9AAAFDB29MZiNbzCvSM12i5gXTFRReQPgiuaKCrpW73aqqPbSPWB5X1i3eqPhAwTe7E4qDK6r72CRDCAu0kNW1rISixXkUFCx8JwXnK92wuUIF5qoQS0JK5HfUt1k5Ii5Yw3MzY79SlSA3R0FhxRaEQlhwU65IeKdThaDIb0BRtwdT1J8zt4ZmcgUEMOXBFZ+TN1C0P0c1wxoSCaiRCvksD5r6HIbHeL8G3wPoHgOEFRAKo3th4KcPvsow/A5SIh3iYniNZIsg16h8D9wSCsNbTygqFgORmBBX8Cmv8ksyxQFFcsWuPk7Xc0TxVWb3DxSs+j7m6oo0fjf/IDdJBIJetHOYmIQFXgBBvgivnk6dpdy8Mgs6dDUBiTrHGgLPd2VpMF1wXoyolbnrZ7LFuFDXMUDB8kAhhWFRILIqC8LgYoFo5zyg+OykZ2KhUXFEoRJXqCciCvfQjBAUmpt5ywIJFSpk4IivHdmCn255zoTCFgwD8nToGUtZDFoiX8Rimgxre3XgscIZWiFhCDCFVRSeAIRAuzOhqLSDViJ2RUSBK/IUmrJWU6SkiKl5TiR2rpCbuvNYSWz+pSIUxbpIWEhuypjv6aLDQTmz4LQwz85DhZcSgeyCQ1eVp5MvQ/8y7Aqw+Lr/dlIiw3O5D6+h7wIqpCQUBkApae/cQfA7Q0lIzmdFQQfphASEqBAUiA+9IJfMTPUhHE67BvIukOQKLgdF4WNcJhj50vk8Bmd0Rb6+0PHekwW+F8HZ/c4eSigKFE3ItCJOXyd0jt8u6BXQVxbjYl5+h3s3DeipI9/F2WaQIGHPZH3GKLxHYwYCKQMmoSiOplAUeCSBgLripL1UUXypK3C/iitE/i/sX8LNRUtxGwiiBgW8IwJyYBhjW37bB/L/P5jqhyRDNic9s+aVzYwv1dUlmXVoECRY5aB5s65OW99EtPj5mYZZSKQhcp1IFgKDSRxpVeYVBVgAhdEWyVHOwDFZFlPdPduxqRsruzgrDoICSZyFZhwOjKKMKMoNir2IAg+2KCIJbtgoincUNqEgTciG/908K6B4MImgDeTNev0JdRe/wAwZx/L6NkVYFlVZ0vrrEraWi6az0S1yaKHCE0EWSBfDWXTRYIQMEEY1zxBEuyqKthAUBVBIGUJxY8lnuGUSVwyQhOIAQEoiTVH6UlVIIWsqiugVjEFV8TBPe1ev+PoDBzyHPxfa11UUMcqckAeuyiK5Rb8oCiaB7+SczlCLZA7SCbKgB41lGM+hmZauHqa2tiDgq3dVVFZROEFBAG5BFRywFAUZBVTDKG5BDmKaicQOvwgtxjR3O0EhitBp8XBre38IClEEo/hZklmg1C3sUEuHqFtw+ka2BgpUWPzdHJzTKgtGUXjumZzL+X4WXaxd3TbDWE+1B4HZSkJtFUVhCyEBFDJCeZbcdqIKQbHX64GKIuiBSlZhURTvKIyiiPmK4+bant68gpvn7sksBEZCQae9YUFvA9yiXwrUpYy7IzcsRWKL5GBRVnV/Fl3gCbP0q7AAlHYYB+8hCzufMT4UBYcV65yIglBQC0ja2pEuShql+NzNHve5GIUEiqSKDQoZqIribBnFEyj0Qpgk72d7emzHB/7gxUkuAml/aJ3abmOckmmuZYNsQVOQMfChbPvFSJUki8zZYbRBFpnF3xC7ABTfjWPbtJWdn+oV6BZAcJWloyMUHCWoKxC66IbOnCQBGAEFu2ZYlasmIoocJMQrrKBgVTyf8AruAa3v53rFpQ4VhdxAMtearhh/mAUZZ6EodJ7KEAGIbYscykXDBV5wGcqs/YyUpdXCJc5inV27dF1T17byfovCMQrpD3NRh0CsVatgFCAB0xTb5AlyFFHIERUCVq6qEBRWVaHDNGXLfeuLr9QfAcV9boooixBx72qcKEaBr5Nki3At6KKyqMbBCoqC2zQz8I9Kp0huoBppkXYc2qbr6rGtEMAFxcQoSmsd1xaFuxzlzC8sSUZBzygK7Y84ReWYUY/ioKqw5pwaRNpBzeI++Qqq+GDx52Nt2n93yE81IHEmFAjfLIumBAtgIBSszRIOUjEKlgJapEJXVCGBA4xXFl2zDl03LuiWaYuiEBSkChcGCFCwJBSFnjyjkOWHooiiiCjosGNVWIqZJhcULArmQIe79dOdGoSnqRSc5FE182nLQlbrV5xQmKbH75O8M5flxWdR0g/WcV7611OzN6HAcRpGE0aqoaQVxsjiO7CY7NxuUMA1z0EVTtIbRlPBp3nEql1FcVOLPDAKhRDmxwYFiu4aQaFeEXOFHh/Gt0CBYRqLZPOgfV1FgdoHWcAYLro+PWrs/77abuQhgh+nRVOktzIzxCvzXL3UMR0bkpaFQOa+7xbrfaUoHGqL4oCKKFARxTG65pFNM6FQEtsGwT1DDQIUf+OGbDNu0ggKt64/X2gR5aD19TOPE18Ik0+uBhSQBYadfoo37LZf5ldbUCaQnCtdSlFKGkJZoCvWpAt0Bo8RY5durfse0TPsZyUUPECso2jFKNwt221RxPNWFGoSfEwoWBQu26CgG4lYapohWPjTu29yr9zbcJn0PXGuPE+v0iE6UU9FN1A8cixDnWPF+pqNUWHIRG36cwgbjmaKoMAYWcdXv0w1QShaRXEWVTAKsGDXdAesVDl0RqsQFLzNG6foO4o85974RBFtMyXO4+pvX1TvqniU2NeNKPbROMtxSJ8gIx4kiyvCUiKhfVosL0zUIAzuimEMQslyO7+8sEDM8t2ra+e5wmtQRfGJAoX/IVCI7ABG+mOnKGRBJuef4/ChCjo4bRCGIEfKFWQHcc9i//QVgWAUqR4/9VgpiO0UwW7NKrLgnVegQF0vY2fEHlFBF9XYWSMlr8Auan6YSdLq27+FRVf7/lV7vrA6wTKc+RcKkChNIO2OIoosotjjKwwNWra9N0hEwZVUof3BHHD/V4sRAhTvukCHrCM6JK5O46Zq1dE8hSZQ/BuxLOgklUREkbV9Y1UWwsn4l2cyVI6t09CEhaiWVz/XK6OwisIwCaCgpTotQHCHsUQUpaCQPT4Cs1M9EAqGoA2SVIH6bBApmaYtUKA+ZFFgXzfIIsWs/Q1zUKPFMV7JP17q15Q7ARFZlHzmVYLhKsRQQaHWaQpHal1637zGZbG4LyjcBFXQrbVYdwBFRkvUUngoipuefHlQFKhMURySHjhWmNQgdoPirzcUxq/fnywI1il1yB4Xw6Jx2q7Rj0IcD/KBWtS16sfKZVqMApUDkKBQWVBX2DBVHDmrtfx71Z0fXmMzEQryTUMoDDARCncrqUEMo4AqFMV3mfHPURSZYxT5VhU7QZFFFGhHRmEFBW1J4KA563Hz/vgl9aaKux99UMVJSeg8nRQFL4NUFjMUkMxCo5YdBiu6yB2/mJ+HoapyTuDOVdE6p3HwXT/OaJh1ZaVM1CDsby4DiawkFK6koegyEuPxKJkOKFgUOxfXoqoK0NdYsXOGsBAHgnAOqkjrMQ4Wx+dcJhQJx6MaeYYwCnRIWp9uZYGSqFcMnezhKQpNF30tKJxy4q4wTrZ9DUTyZBbmOdRIWs3ZVK1nHU1PFoWxNtuVVI5VQfu+ZifXfVzGewIu40c3VgWeg07yTYPkSRUJhaqCSjY3uU/261xtSCQUmw45/drM0+vYVYICJLi4cdfXAkmXvAqMLAqv6cJAFWKd+O8IDg1USZ3SI2u/wDrXCusyKyhEFTRJNigcDdUbi2InKKAGRlE6PXsUHyVhZmaD4qws7BYFKUJC9r2dn7+UBb43M8QP82f21phVl6yLtMUK/yzr/hksQTiQYAusW4WFvuYM6BRhiuQIpWfpkZmsEyNnWqBfYmMNo5gciSHLCCdFCwqdkiOcNIM8ZD3gbDMZHcklPlCgNd5RgES8FGJnv//SektZRdNwh4h1Ruf8KVkWgHFAMQpmYfvRGUVBJXutBu/7hgWKtrG0Z1ArLdjZ2eveD70Hk5VHXosnoyocypJXQHUBRemkCd2BUUh/EJ6EAtMsvAEcOdUwGX5EkepRzvN3RLFlcaqH6aS10cX+hF+fLlUICZS4GFpkFhRphoAG24VmTE3gNUZqHi+NsHXSSZN1jmfrZ1EFEXQBBVzTAgedLVDEjuDHBx6lgsJwfxhFgeOHKtgwbVJFksVfNELmpQwgPjtkSRuZqUX25dBVhEJYxClSjGgGlzpEkxbOVYYIQPCv5WikKhxkDYARFu3Q+K62bT3htQooVBUZy4JROEAxTtza5REFvpyqYoOCAkVE4RgFRLFpENrVTFeLYY/ffrHJLBIJpKxxLKVBTnscklv4brmgJP5HYRwmbhEiwXNd140OuUrjtmqGrobY0DI5wEyEghP43PlpWYvC2S0KXaJmmazawQEdYaIqDjxOPlDoRklCkUmu+LRNhAoU36EL5+39d2bxta+HVlGcZOdbUagsUouwKjRc5HGIaItYWoxUspmX6WqkG60TnfCujrKgBN6t9DlGNEi7QVEyCtzKBgYlmpuioIcRhUkoIgReCnL+M+dPFGF2BFnYBev037G4t0N9U6ug5XiSxdwvF6ogi8OevZM2bPS9321YYExUlYpAyoFF2rygxYq1IYEPfplIIgEF7/ZIg4g8nFwpCyiyA5X4paJwqTWSVzAKm2yzFRRcNE9592aer1+JxebyKba4q/hRze0mJ2RhSgKB0g6RFnk1csaKQipb9ZpQYpE/+zqt3/1r0Qs1tHkx1LR0adcNChogdrOtlVBgfHLWVBQmoBAIu6iK/0IhIOK1EL9UvxKJTYf8LEPcwPn1li385xDh+i7RIuF0BYXaxWtlFlmoXU7zplJuBn/tb0OFZlmHbgGX1oerFkLAWu0PUx5ZBiaMT86aiiKnp3JDi0JB4RhFUsXnMBUSPEJwH77Z4vQCjG20mIYYLWiIRBT7C9winmtKF6dq4BYJKEKLFDIz8igMloJPy/c6/OuAta8xUidASSjKgMIJikNAkRGKG5EpTULBetGm0AYxhEVQnO1vULCBct7EhfNHdM6tLK5NiBYUst5lQZEzsAjK+L5hiuDkkltEuxgrGZ9KAtKdX2uuY4U3ciASfENvGFAVUEjqfLIqMGisERTaIDmrIuNYATCMwokq9BVxif9XBYmCwzd/4Gi+/tPXGTA4S8NguAw3dtXK6e78XAsFBgz2//+gaZI2XbczU/FueNrne/MmTdlnIgEApJ6eZ7/8HVssCDyeQ1m0o9eUIGIXyGLeZ3aLPEUu/b6QTSYUVWtRQegWyviJqmdIlsX3Vx1RsBreoKDVM4qKUJA2+AtVvdqmERTRKqjRQhR/QvmCR/xFFgKjGafP1GXlReQryELFtYpznn65/cbrjW3WiTqtVVDQjOfqvI46kZmXtn51o9HrFlHUKUHqhKICFOGHMJin3qqmIff13R4EglEYREEsUo783tk+J5FlyJf1K2eIwKCWE3rLiiI2naiLo/a4QAh5G5C0NPDmSkvC0OOU6DRdPHSGBmwdnd7ILDae7IUSQ6qo0Dbra/AKcIELfsUdd8NywOUjkKyk1oTgfk8o0tEodt/YZNnuH8JQ9hbfYTqBmmAOIgswOFWxW5ySLnC7bttGiS7oljDHuTIK7sFQCi2hoDKCKK54fGi3GUfTEYVBFHVAcaTF47+kQKFKFNJdpHmFoIirpJ0IrleLWZSy+HMar4LigF7BRcQNRpyRdMGDTvRDQpGzGFyZIk2PzsIsqORCrME6LaLAPBEUbURR1bjSqm44QRSh4JEVXDJVNAikSigMofgQENhXhAVXy/LJGEoWBzJOhsHWyeOswVEOSD2lTSpU1Fp6Tuk612GW5oJHb6GMRKU8Qhl5BF3MwzoPdsWNSUBRIwokgSgqqJ5XWCKaAIPBDktxBckMs2YUuEk3EIRCp5N05IB2gZ2FvX3/kCE6GqekiMhiUzmKCOsIkHB9BQs6JWogBEVtB5OEoqeRrdONvR1mRLHWLaHAC6GAkkSqUKQK3nzUnBPkIoRCB/jsFWiYEUXyCrzEKnLeXPfPuawgdB8YZ/c3HQAVOXIIPURcKO9E6FXN+yIo+IbYaTUU8b3r5OskFDNMNbEYJzPhBxEBBXfgggL7bkyGhKJRhKJS6BXPqhAUpkCBK00DToh/tbWX8JsppRCzOPej4w9klLr40w5dniFiF59u7/PZHqJg6yxkEQ6aW6X4lHflB17r3i8bNuC3UFNb2owQFPyRrSYrEBQhA7KiEVURn2OIKCBBtjuhwMfx4hFyXPBxsVf8PbYkEouj8z2iiDkistB+rAWFCAMq6iBHh4iCn7vucaksC4ax7l1i01AZaZtgF53vAEUYchKK4JuMomp0UEDYp7KJkipEDpIgJQqz3U1E8W/0zVRNDp1bD6wKDjHObrR/vrjFAUd7y2D5OAxRiC6gu3TFvgxD4YlZm6OA5Q8m3VlbPkDT1m/LuAGKwOAHFLxPrRQmSGol2ryMklcwCm4sHj+jOG92/io1war4UKGe/s2P5IkqQilp/VCLG2CwXfy5yK81pggH7F37TBUYjfNpKKroIZWmgU2qu7nxpgUFho4ocE7KCaKwm0RtEIMyQXQd/pZEISigrXxB8dFY28b8KGH8g/UUjgRzXdBfYFfmVOEWFKdm2k3cbjALmrxht/3EQmnvyCyqYJ2eWlCwi7kHHOv2HkWsFuFHkz387BV0t6DgBMFFCgmK02xv4puv9fTKn3xgFln7vW+lc/IIPBhJUUbQPTUtNZeM6oeOb4XXNiyU37PfOr+sa4aizVC0tOzwA1qxzbeqoDSpEUQqIYziI1ZTKCJkCG7GD26+k8XJ+vnEj9iIKjC++mEs+yyO47o7lRotSRFYt5UU4aci5oH2cNJ2QVzdaBZoPDWi2FgVjxwFwMF6Ql5RoOA+M6JoPrXB2Kj1VvIIGpHg5Rq7/HmGQBpFx3m++RE+1SerBBDSZ+2zsEiiCLeiXcgjF0/WGUmIXYyRDe3c0Fpv3ho3LPCFoGBVwFUxCmowKUHaDIXGAVlSBYQ2GivIxqr4N0fxV1yuWpxGEu9G3ycHbZb8igsNLKg+c065CXC0obvI1ktmccKuM9lFRaH9IkcokENXNP5uWPvR96SK0itY/qKKiEJHFKp58gpAQSxMRCEPrGZucZhddy7GeiKL1YfPgsCTmqdIIsVp2S0keMqA1FtwdxGNU1gBi2l8wEpzTKobehl+9cNMNdCOphssoeCTQ0Sh4NoQClYFoyi9oqa+Ark0iIFkQSggfnuJs3H2AiQy7wwQmEU1+fVvEQVNs2Rw0VevW1ScdW771Mp60/vAyIlbULTW18lI23k3KBI92nAigCjMzyhq8opYVWpWBX7xQFUgCpNQoCqCKChHPp5QtNZdoyjwAjeILDrvLl9PLKSibsPYxDYLXogifoBp3pfCLuiwl85Xn05Y9Qh4opE204ip3qxDZyYPyaETCvxZ4aoaQnEkFGH1ryhyrzAxSBXxIeYiPr6WaT3QAdibFIHTc3P6oueNSusU5xRVxPgV7aJSPNyDAETVzHYhKCAr1ggNT53pTNX6++pHIygo5QpV6NhXCApcfURBeaLvzyiQxUehC9yd2r+/z9/vnfN3kMWRQIguojaufqjV0686w6LZxSN5QDbw+nRySsAY2wVThApv1e0bbVZHW8+7BRTcakExZRQQTyhUjqJhFPoZhU4o6oBCxtp/ZKsNGYLnom+nWZ+jv/3Odlh2F6d5d7giQZHidBtG8QAEwdbppzovIrhvtU0SSe1GSp91WI3buzqqwkRVYH3QzyjabFrDtUOzbWaquEfbpHLKKCQwQyhBzuIWUmEGW8VFlilSTfsaaohkiMSx211KgxORQhah02rTYfORUmRTSRfaL7StsqPuve+3LUOhEEUT2gb1VhX4FtzQap2r4k4osMdS4TH/EgVnyAQ1BOK9Lhqo7/LUqpRTHuL4psKIvYVEBZ0WyyK8l7pzsIs1znhZFmoZGhFJNxgcgAKTazdM66oZRZVQ/KgKiEIVtRIU1Hor+iDMhzTfslgXashPLedhGewlW+SZXtJccPPANYReGM209wUL3KS0js/dxSCu4yK7+iamSOd77Xa3mmuuCv2EIjplXkGoajCKcKkNotBQTe+kCoBRtlicIR1+8vrtlBPd4nj6QRa//N5HWZTCOD3AViVFEgpIgVHsAilW/W6kpOihwy9qN117vy+3+gkFSKr9AQVRekUBaqCGk1FAyBKLGnLhtb2RxffiXZWtUVTBzUWrchQHkcWp38dGdBEr6ilUTPh+GnOyEtIX1TxozIab7+pu92uGoqHn4YlBQqELFNhsc4IQigfKglCAKMgRhYcMLSbDi3s3zvoTZCEgCmEc7b5kBZNvE+u0RIIziEN1fGIizqn9TMhCtJNTIT5nryFFrM5RaPxkiaapf46iDeLg1QOKR5Eg2uiAwiAKKqZCQqrENH9FEomGnCUvg6tQ3IJCZFH7waQ0lyknP3fhaJOaNmUc7bLf4uERcKDHUur0Yyoz9ApnO+PSbn7vEEXzjKKqiIGgCBYJ8YSCxAF+yfNNRIG++T5Deud+4cIERq6a1nuDys5UIc65DqOgKLqLr2YUE8i7i8b74hGEZnJyBq8s/Uy1etMsuzf1NaEwdGiGk124yGa05XEmozD5JmQjFCALQVGw4PbaTv13aRYSB5IFbrQiComj25e2GOIkGifepMa3Ews9WJaFKGENU3KKepgRTOtccxt3ZwAFTe34MEA/EAUsO40oWp33mYICQjMKIyiyRT6fCk7LV2kWeAe3nH5Y8ww55zAOTUiRPAUOWZzCTCtjQfUWWMTRf4KoFh+IcprNHvNF9b5vVj/MYbMeVREqiNFUT3IUYJsoC1aFEEHf1CEiClnhH4UszIQDnCcaeQbNw5R6C9mIyNm6fy4iua9c7N49s6BbftndIAs5T/Sz3PQ5khs31jUP58ceFtEgiq2myiA9BKKgBIFgVcTNCqFAsxAUEQZ0GCWKox271Hy/ygKKyNDJIssUOVlqtPLxt0RLI/AcFVnnNDZPc/EKnJNKDapm0MjC+F4Z711A0QgKE4RIvSeKA22T9veYNMEwVULR3ELLzTlCKBhEGnNKazG6S5LEqy7OHbQPssSXFBn3XtaKuDIa2g+NKmUDOIzYBb2hJqdSmilnyTmXqWkW75ftzihY+JQuaJiokYa2HYrtAS6EQnMJ0fwEq1H029cDCIpie/rppl5880UY8LiF76TlLGVxMLvsRQpd4EzLtS8F9RDsYhMWoR/t901u6ocbDTzHTZlxGjtIEHRAjWcBBrsSrUkBNXpFZECZkV/0BoJgs1C/BwxBEwSDp/9SJEYwTgrhkNdbqASnH1kcl93lbsAey8OLat5npfIUIkTKDryPZxa/Fq9SilzcRLKYp6adx3HamkYhihZRIEStlWJfKFCAv2aNRUDAZrGpC2D4jgnykiNnM01tRAEvZiFucXDDImt8YaHGvSt3IsJNud3kU05Gcaq9U6IKeNVDJ7tcPZhQXFEW9eT8Ure4bFPnKBpFhkBDDBICMjOxhFDrjWZBKK70/wiCEBRZXErjLI9Qb4PXMsklEhIHPQxaUgC7zizqYWiKPoufTemeHLXqfJtwHu1EA88Z8qsb7Yi+SaOchMJQr/2QQsJTbpWqKQICFOlhxR7MAlAktyiNsxPjFBh5CtnBHQvnzFl0O+pZpt95nPps3iXi+sKNl1KCsBmXY8Kp/YbVpR63qnZ2srheajcTCjEEKq1KsTgYxYNQPDIU3e8oCmCBz7qD+pGGdNdu3HBxQuJpEBr6rC1vJc/PME5uX+QkoDxHuoQ5TjHYwO5immS7hrIYmoQLZQEo1OwqtY6z7xV6QNZYaJrkEIrUcDED84zizo9vqvkXquIDKIAwAotnFGCc9vJ+ewrYuM9SpXNyTn1g07nfxQ4YWPJONe0G1S6qQBY3ThHaxoMsJov3YFw9VRQ9mqq2i3W0n6hrdMqoCqXScOKRoagNK4bNYrvTR8juarlihlBBlYMyYaGn0ZxjZDmCL+zDxmE+FSlSVFSdULywqPm8Ofom64tSRN44buA5z7IA57S2qvpxHbuGUNQRBXdQRkcGquHvKSohyT7NllSx9P/8GwOdoozfrV8OUk9lXxZhnLfBX1OCiCo+YgO+gF3ku7JyM2IzXz1Epu0YUiRvrZx4bz0Y/HnG1+AWyzLVoZryb1ojKLB+IgqFF6wngoJuSdsQZecLN5w/jLO+13G6FuUUEdAFrl/Tbo9vd2UfiOMy7WkXUaAAaJXdVyWywBdcoa+ac+M8mt3IY03W0W8DZWeQxdRPM6GoSxSGvIJKCKIQBnChDHkYVgVkyBlhRBRlKOfnQ6GKmBz0gorZv6sizA+SYN+K5kFQQHs+1EXLiSxw3C3frnD5/K72+hiin9qqdXMHBbWOKPhjYZwEbBkkAEFB5QWtlWRRazXb/h/8v+MwilezCPX0VxK8OCe/grXafaoK53zajGy7r4uCKbOerxslkKQI9lkw3nGEQlormWtYi28otx5BFjdnAwq0SEZhBIXCL1REQYohJHwCgL/Ti+psdwnGeS5R5KcAvosrK7yCcfwa99w5iwTBw4BJjsNyZOGPU7fPYpBcQnCP0islKC7WiW7M2GBb1oGDNK4Lzrnhhszwnt1wEihRBRZV3JmHr1pzDygUNpx18JPNztdvKiKCopxxend5meAIi+Cc+3CVs1FmITl1Pk7BHIsMSTSqadfZaXI8NDm6QVqLQ5DFTQ7TXIe315OGXbyr3WTM9gjuSSUkotABxUNmFApejCJWVXqEta7VY7E9/Pf+n1l81NPYU5v1ukNl53TBOU/vnDP8ifv1riwiDAJnwr49VgnUiV/1sCQU8HW1TCKLflQoi2Wpjp9u2/xyz1HwFoNQsENyNVUgB2kwAFTYhuAJ42y7LyZRdhbSZnl7ikvLQ+xCD8Vsj2Hw/XB2uA/63ZjzjNYJFTXuPSMIOiQIs/zMLG9p2qXGHr+vJ8iUzjZ27LawfC4h7I30BaPg5FGP+CWLBX0TUHyvdvnEZwfiPv0Ni9s4mmJ/KiBQFudlH9vyrEyEBCw6uIFRlHYBi7b7Lew2iVNiWk2OAcUiklCcOodvKLeFY3azede9oOCjAZ11VVxpOX0kQwDFH2ax5kAF5MeDgIP1clCWBYLgntMPIUVKVQiLkwPrLDZl8vRS5Ye2Su8leRk8b0yV5zro9InextfYhq4ubFKXxvplq8EB7ndc7j0rIUBEaoaKZqE0XwEFJshfmCEyy3rvFmYcb1nzLYHeyUOcoX8uIk9VBzste3mzJ6MdXL/bPH/idt16JsS9RpLF4csu+EYzARLt6t6PK7SbCcWdS0jWaz8YBWcPIwGz2IwOKM6dXdrvM7KQo+QiTlbc4lyaBTunxfG3qD4DQWUVOq2VzotzTeAL6+1W5ZKhSz3MgiIUEZ3y6zYqlIXtQBa2a91AJ4f3UEDbu6GmgVHQ4IJQKE2SaVEltA9BFCFD+gOvrARRyiInUfZaymNzUWaIuMbBROs8Pd+HElLj0Ka35EyxG1q2EG6tktdU04a3GwdIetf0ftoaQsFJAA55x8XWcAkRvmLJkGZi/0ko/lKzi781wc8HZUf3Vhbce/PTnMNgyjYrF8b5MO8j2sULCfSFwT21Fuyco0Wz4DBDk2h1Fr9/cfBvba2pnZ9BFY/trhRDUA0uWjGKFpausNGI5XTDmwCNQRSQIbYFCDzYe6+L8330vawtC2FxsMOoZJFPqkAYaJ2xTRAW8gjXmgxS7lh3LWYBbdeS/vF6bPC7nT0dq26p1nECl7xu9/A4gwZxNA2hELNAxVBnIVai8Bg5oPi4La4HDHJi+JNbSBEpDg7ZLlq/L5dSFnkRDmPfWT5Zlm6jSzUNn/BmORefXP7Nm1dpXuw2/En1pE5QTxvoBOc69NW1gmkfucSdLrz2B3kq1w6FVsImax6A4q9qcfMXGsX/2MXZjH6TCpINv1N/wSkiligY+PURrLMvWgt4g5etB1o11w8OvRtRxeF316V/uHcE1QKSatmqbnT3gEKDKlrUQ/sQFCQSMgtGUd/JPVowTkBBGXL9TixKCCILd3wdZolv0hYVUuSly0o0IPods13ab3SKNBNeK6kw/N6Xm/7MWPQ+ZUg11fhGbytwEatqNy74nK9SVUOrTXJg38QMYZXIVeltQxS/GWu3UEO+4+reFhFwi1W66aK5oFdIESvj/SJBaNYZNqmvbRbFcaIqwsWUK+112LIm9TJuEcVp6fC7ytXwF6urZXLgf9sGqwNVEAO5MBLVUDlVAOERrgrt9a7+grgsbrl85PEOxcnKBrUwzvQKjdb2rv3mF21SF1lY4bB6xxRhFgzjYMfMOL+6Kenm5ir87txBvwJ/MZObA4qaSwilRLoUGYII2EiB1X8RobWCpdNOHwAAAABJRU5ErkJggg==\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Vibration\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"vibration\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":1,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"m/s²\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "vibration", + "tremor", + "shake", + "quiver", + "jolt", + "oscillation", + "pulsation", + "resonance" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_visibility_chart_card.json b/application/src/main/data/json/system/widget_types/simple_visibility_chart_card.json new file mode 100644 index 0000000000..c008edda34 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_visibility_chart_card.json @@ -0,0 +1,39 @@ +{ + "fqn": "simple_visibility_chart_card", + "name": "Simple visibility chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAhFBMVEUAAADf39/g4ODf39/f39/g4OD////k5OTg4OCAwywhISE8PDzj4+NYWFjv9+V0dHSsrKyQkJCg0mDy8vLHx8cvLy/V1dWv2Xuenp7P6a+Qy0b3+/OIxzmCgoK6urpmZmaQykbH5aPf8MpKSkrA4Za+4ZSYzlPn9Ni43YhLS0vY7L2o1m7UZuoPAAAABnRSTlMAIL9AEN/GQiaNAAAEm0lEQVR42uzPuxGAIBQAsMdHpKCjdP89PTewBC7ZIAEAAAAAAPBfTZur8Sl53JsbV4lI42nbm6NEnu0AM0dvR+giixFZjcjLbr2rSA4DURjOTnCqCkmUJJTYDjry+7/gIvdlepcF98yAkcF/VMZKPlSBRuuCjNYFGa0LMloXZLR+BVHHI1f8pOMhGjKAOegUcM9vEXL/WBqSoK6Oo9uHeFvMQsarYgBMMDc8muoT0jaIBoUFHNk+pApLWMjgeJSKwzlhjkBOSYHUIa1Pcd4gyaOsMSegJsdeh0AqaV5VC1/rkpkRqQiGyCmwghFSFmOGhQ7J1FYk9YNBsNshECF1InMiEx5ZwGLokFQU6Q5ZAJm+IH1CmSAN+x0AySQ7JkXy9rZbjBtEpVjEBgnour8hSfq02xEQ3MjcKBrebsQ5Ue8Qr4nzE2LLP5DM9Yb9DoGoUNQ9kgGvjIYNsqyqJW4QqZnxDbKYAsaIQ9qHQI00YUn4KkoEMC39ZzGHzLAgXRcmNEMVReMNSIdt1j4EyCmE5vhJwfCNhnii/KePN2t8SMr4pPEhn3VBLshZuiCjdUFG64KM1gUZrSEhXrOfH+LJSHKNfm5IFVqLczNKPTOkFslPUannhaiIvs1+Wsgf9sywV1kQCsDf2A4gooaaoL1at1r9///3AqZmsS7eNqebz4fQoI3HA+c4Gh1cpDharQgJ0ROhSFYqwvFufOQZrVQkIC/3IlmNyI7zPgoU85dOHK1EZEewpstUIaZvIVqgCK2q3asHJpymnAjepqzoTRTzpYmkAdYQ/lI1krbFQVQGOHTtmuRLESZRj2Sop47v6A9UWERpWh1HD73slxI/YhFw19rD5Tci8qQAioy1Uy8AVNa5ZPAPTYcKkjwqXj586TPJCJfJ8ItkmggrtIZWOZuo/GgNe/2NyLCNA4E6CKFexZ5waiTsW7E48AkiGajGGtRaCuAu0R7g1It8VbF3fc3LMffeXYIIjIOS5yHRXt4iCrKuMQpmVcUQO0XkpbExbC4fsyyuUMfxEYdUhN6JIg+Hf2u4UfETkVlmZ1fAFaErnO1GAfUsEgNc9EdWm710YzHohn1YWeQ5VYkopWkktNBUBpVqQvplAPsuFDYwg8j10QVw1h+qUHEB7Tg3o4dPQ2wIE/RnODlOEMmgQG6RH4BTFxZUt8GIQfm+CyJa5dxozHL4YGZ7c4tYj75LAtT9ADcHghzMJFKb2TpFFMAdPXUB7D+LUFfJnkvk1uXb+5uIoZkikuMUOZhF5KIgG6XbGqC7h6yAQvqLUGeenUeEFRAPS0za+Z/7YokagKu3CHUX8FlEmH3kLdJOmo0qu00EniK5cJ9PzSJyNrXBcrE1I44VFKwXsSMU8xGhAQ7c8ZhFpICOxuZhTTx++2UKYg+RXIgczYN7afVIpJFNM8xRMvZouks7yt68kBw8w7Hw0/jqczhWI1L+Eo71iPwSjtWIfGYT2UTWwiayNDaRpbGJLI1NZGlsIv/buWMaAEAYAIKBhq0O8O8TJDCW5s7BG/hqhFTTKKTNcieyQcnOcbdU6/8t1YxOozAAAAAAAIAnB7h9+fIl+d57AAAAAElFTkSuQmCC", + "description": "Displays historical visibility values as a simplified chart. Optionally may display the corresponding latest visibility value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'visibility', label: 'Visibility', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'visibility', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Visibility\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#D81838\"},{\"from\":1,\"to\":4,\"color\":\"#FFA600\"},{\"from\":4,\"to\":null,\"color\":\"#80C32C\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Visibility\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"visibility\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"km\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "visibility", + "sight", + "view", + "clarity", + "transparency", + "perceptibility", + "discernibility", + "range of view", + "clearness" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_visibility_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_visibility_chart_card_with_background.json new file mode 100644 index 0000000000..02ef4bfa14 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_visibility_chart_card_with_background.json @@ -0,0 +1,39 @@ +{ + "fqn": "simple_visibility_chart_card_with_background", + "name": "Simple visibility chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical visibility values as a simplified chart with background. Optionally may display the corresponding latest visibility value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'visibility', label: 'Visibility', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'visibility', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Visibility\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#DE2343\"},{\"from\":1,\"to\":4,\"color\":\"#F89E0D\"},{\"from\":4,\"to\":null,\"color\":\"#7CC322\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Visibility\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"visibility\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"km\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "visibility", + "sight", + "view", + "clarity", + "transparency", + "perceptibility", + "discernibility", + "range of view", + "clearness" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_volatile_organic_compounds_chart_card.json b/application/src/main/data/json/system/widget_types/simple_volatile_organic_compounds_chart_card.json new file mode 100644 index 0000000000..be845efd2b --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_volatile_organic_compounds_chart_card.json @@ -0,0 +1,40 @@ +{ + "fqn": "simple_volatile_organic_compounds_chart_card", + "name": "Simple volatile organic compounds chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAilBMVEUAAADf39/g4ODf39/f39/g4OD////k5OTYGDjg4OAhISHdNVH1xc08PDzHx8fmboIvLy/j4+Px8fH64uZYWFiQkJDiUmrsi5t0dHSsrKxKSkr98fOenp6CgoK6urr409rV1dXaJkXnb4LpfY/kYHZmZmbfQ13wqLTi4uLzt8Humqj19fXgRF3tmqjcQRJrAAAABnRSTlMAIL9AEN/GQiaNAAAEn0lEQVR42uzPwQ2AIBAAsBMQw5cpSNh/PxMn8Amk3aABAAAAAADwX7k2V75GTuPZ3LhzxDVm3V4fOVKvB5gpWj1CE1mMyGpEXvbrZtVWGAbD8OwbBBKS0kJbrHTS+7/F4w9nOXHtqXHvPoiCo7wYQb2ZId7MEG9miDczxJu/EZI7bgTDyTz9kv0U0ql8hqcGQKjAIhGvADoTcYcXtyGmglJKTb0FnCIDyGRglj60w3RYr+rmodyGhBqLxsgtfwZdSAAeWPdr0IZEBggLJNZseNxtCIJqAYyoX3fSsVmJsIkVg3EwasIRj7sPQWJsRsVHrGgK5DOE9+PQaYEJHvclJPACdNJrQiHjDCQKe0hEZpySUnS7WoAQs6Z0lQQdJIDsJ9N8FgmtwSBV8bhvIQuLjBxix3+NGJvK61rVELTKWhnr1pIZj/sSIrSc638RbthYJjryrBJFO1arOn5HkgEoDnZ/fmu91QzxZoZ4M0O8mSHezBBvZog3zkPMwi8ICUmJKMrbQ4xplJKY0stDWAW7TPLqkH/smeGOqjAQhf9NJqVVoFCqKCyKEM3d93+9O23XCd5AJO7NihtPsuGUNpvz7XTKiht+87zdvjTISQHw69pXBoktN73IXxhkNShDFr8eSPrxEYz14XlvvRrILhZCJc6p04Au3r8YSC5stVGOZBfOLN5biwdZ5XkFLGX992XqlInt7VG8+q8geuhLuCtj+jsP70yQVHKbN7FK5SmweG89AlJ+GhmcLLqQ3CDi9WbRIEYF3BHinSUqrii+UunXUMGEbJw8AlKeXWjpXEEu8hwRGX+XdERsCOX4TZCV2PCrf1JFwwklSlVuZRYLZZPZIJQxRJbeeJA1Rhp0hx156RMWtOR7IHsRrnHG3xxPkwiVKRHvcyvi3VwQNJ/nANKsZRtAEC8BofZQnndYklLW7LXzAYS8hill2fU5kQOkey7IqDanzO7S0Fg2mbm1fGLpXUjNY8QeoMMWSK0rT6jW4egq5+JHeDTOaw+ydr6dQElFzqWJs5hG80TgqpoFwsGdGKTm3UJheSYYxI4iN5rmnO+IpAQg23R0w8CoKrFiazO7gtlK1PZRkDOiHoAU/4JIv4T4Il+Mwu9E9P5Is1Mt8rgeBemnQdgYd4nwAOAu7bXZy2bieNtmCwUpED0I3wogrqUmWuQJIPIW5DgG0k+AGDQTLfIEkJpPrQuHbdFMVqS9D2IVPAHEEfDtNXYAnJlBWuwGPWIYZLxHdiL/YRDuZO3/0Dx5QTzzqVX7oh0cQaP9XH89tQpaN8axhaeA1A1GpqF8gQrNH/oBBqFxiB1dPYTniPdjnzyy9DkgICOX6+K9XiOpLYGXDJ7spnUs2oNcIu9HOCzAj4CA1mUwpb7mqGVd8rSUzjMI/39FPeIW8u+opR6rx344XMxL7K+iMcg9WdfnvwBk7zl+A8gOSEsEkf2Z/bmXMEtLBFmC3iBL0xtkaXqDLE1vkKXpDbI0vUH+tnNHNQACMRAFSxtOACouwb8/LBC+SjPjYAXs68aQbgYNyQHpoLWujNptTpHf3fuIqPP/WaqsSaEwAAAAAACAVx6ocPyABDUGtwAAAABJRU5ErkJggg==", + "description": "Displays historical volatile organic compounds (VOCs) values as a simplified chart. Optionally may display the corresponding latest VOCs value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'voc', label: 'VOCs', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'voc', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"VOCs\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 2000) {\\n\\tvalue = 2000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 2000) {\\n\\tvalue = 2000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":500,\"color\":\"#80C32C\"},{\"from\":500,\"to\":1000,\"color\":\"#FFA600\"},{\"from\":1000,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"VOCs\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:molecule\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"ppb\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "indoor", + "air", + "vocs", + "voc", + "organic solvents", + "hydrocarbons", + "emissions", + "fumes", + "gaseous organics", + "contaminants", + "air pollutants" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_volatile_organic_compounds_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_volatile_organic_compounds_chart_card_with_background.json new file mode 100644 index 0000000000..f804056f98 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_volatile_organic_compounds_chart_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "simple_volatile_organic_compounds_chart_card_with_background", + "name": "Simple volatile organic compounds chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical volatile organic compounds (VOCs) values as a simplified chart with background. Optionally may display the corresponding latest VOCs value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'voc', label: 'VOCs', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'voc', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"VOCs\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 2000) {\\n\\tvalue = 2000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 2000) {\\n\\tvalue = 2000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":500,\"color\":\"#7CC322\"},{\"from\":500,\"to\":1000,\"color\":\"#F89E0D\"},{\"from\":1000,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"VOCs\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:molecule\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":0,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"ppb\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "environment", + "indoor", + "air", + "vocs", + "voc", + "organic solvents", + "hydrocarbons", + "emissions", + "fumes", + "gaseous organics", + "contaminants", + "air pollutants" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_wind_speed_chart_card.json b/application/src/main/data/json/system/widget_types/simple_wind_speed_chart_card.json new file mode 100644 index 0000000000..cf9725b100 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_wind_speed_chart_card.json @@ -0,0 +1,35 @@ +{ + "fqn": "simple_wind_speed_chart_card", + "name": "Simple wind speed chart card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAgVBMVEUAAADg4ODf39/g4ODg4OD////h4eEjTMchISHHx8c8PDx0dHQvLy9YWFjx8fHI0vGQkJDk6fisrKxaeNWCgoJ1j9w+Ys7y9PtKSkqenp6tvOrV1dVaedW6urqRpeOfsecwV8tmZmbW3fSDmt+rq6tohNhMbtK6x+0xV8rw8PDW3vVvo8R5AAAABXRSTlMA7yC/r1EOHTEAAATVSURBVHja7M9JAcAgDAAwyvXEB/4NbjKgJA5SAAAAAAAAnlNbzNv1+j9ij+utqKWvkcBuZY4UIktkihxG5DQfu3Ww4kgIRWF4dS7n6lVEXCjiIpD3f8ZxqphM6GSgpxeJAX9EQd18FEJtyGptyGptyGptyGptyGo9hUjGl2LqQB6ANrSGu1w6ll58EvwzZjz0Aog6DI24zwpgFQgB5QmkMyTn4nIQdlpw9VKa4MwHKBlhaRqhqlkBaJYT4g2IbDFLHpidxzFnOW+974vQw3MkJpw1Qws2OhVhHlkNVGRWd95IbJjJ4QeKBSaIc8E6Emt9JyReWC43SGcPyZfBeEAq4DyqR7TzRqHzAuGAUueY8ugdEDysQN4FsU5Xfcr976Fl01xLxQEJx8J2e+yQqzMRKsCcGUKlBAtzCPWdjz3iS6EaImu5h9gN0gYQmW6QoaoIYc5dON4JeSjxAjiOe0ioIpYOputQXoUFjTJJkIZkgmuHXaALQZRXoFDuIeLo3AGJnuTcYCATcHXH4knrGI7V1oE8T/CnKBEQaoznQTw3b/NjL4BIwc8SKv63Jf+1YhJ8s7Uh329DNuRT2pDV2pDV2pDV2pDV2pAXpC0l/XxID/ydyx8OGUYrqTgyfTREjCVilkj9ZMgv9sx1V1EYCMC/hhluBVZoFREQ1kv2/R9wh1bkHAIH2E0IJH4JtIDE+dJpB9QlFwwh+XsW8aiNS3hk71fEpiO0xBTvV8Sl+IuUtzcRy/wNwTh0hjcO2fsSiYmJhR4EBzpCcnclEpPnXqkJWhy/hS7IE1sWsV3neL10kRBZun44Pm/fovLptGGRkDRhr3JYDhH534M6kf/PIkFdBsBkaEjhjSqyA/yIfM5ZYCk+n09Epy8Dojnb/ZCE9/a1rCUiKm/i1yKyLxIkJeLPIg9MYBLfxHYhrxuQUX4TuRbLXz0iulqzRVATmG4aNSgw5MhMiCR86xSCSHRrqxmQiTz0GgvH0SvbTJEyCQIjokzTkR9ylb1FFAtG6SPiXpAG7UmUuq3SdFzo0qZ9qCe2dZxYYi3XY5VQcC9kncvM1AJ4iVSIqn8JOpEEbzUyxR/JexlBQ6ozK8qQkdVo6T6B4Uh05I11JrBE23GIXDFHBIyIafK7zFJ40xPBMrk1AZfJATF7Xa705JJ1LvGpoEe/WouYGN9auOLZy0QKNNSjIpH+rFT6gPcQoTT3V00fCxjCIg867LNY/DADC0fkIIMouPPBsIjpIeY6pTDSTaFzUjd6lRjgF13h/1goYlAlJrNF7rw3BUjWDxjhSr/XF2FueJ8rUr0uqlrP/2L0vWldEU6NhSLJl9pZsEswnFlHWFdEYg26uc0VkSazHnlhcjIf+lKHfq8sUuOz0uU85VPJY1okeCkX+GShqBxatWyPYlhZRElEydtdz+LntIjJLHNjdit1kexxIl3F1xWBKEHEMld6dJJpkVIC096Ih2iomIXArCDSI2qDUTBJ2srCWBGJqfeWtM0fsavgZ1vhk3fRvY2LTOGQcwbDrkWE1z3k7loEhICWfYu8+Yh8RPbCR2RrfES2xkdka3xEtsbfdu6gAEAYBmBg26174gP/BrEB5c5BDETI2wwKyR7hxBqwDuq+dlQOmAfdWRG1z9flqgAAAAAAAPibBywlq1F+6U0fAAAAAElFTkSuQmCC", + "description": "Displays historical wind speed values as a simplified chart. Optionally may display the corresponding latest wind speed value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'speed', label: 'Wind Speed', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'speed', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind Speed\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 16 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 26) {\\n\\tvalue = 26;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 16 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 26) {\\n\\tvalue = 26;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0.2,\"color\":\"#7191EF\"},{\"from\":0.2,\"to\":3.4,\"color\":\"#5B7EE6\"},{\"from\":3.4,\"to\":8,\"color\":\"#4B70DD\"},{\"from\":8,\"to\":10.8,\"color\":\"#305AD7\"},{\"from\":10.8,\"to\":17.2,\"color\":\"#234CC7\"},{\"from\":17.2,\"to\":24.5,\"color\":\"#F04022\"},{\"from\":24.5,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Wind Speed\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:windsock\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":1,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"m/s\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "wind", + "speed", + "airspeed", + "flow", + "gust" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/simple_wind_speed_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/simple_wind_speed_chart_card_with_background.json new file mode 100644 index 0000000000..f7f2453f00 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/simple_wind_speed_chart_card_with_background.json @@ -0,0 +1,35 @@ +{ + "fqn": "simple_wind_speed_chart_card_with_background", + "name": "Simple wind speed chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays historical wind speed values as a simplified chart with background. Optionally may display the corresponding latest wind speed value.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 2, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueChartCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.valueChartCardWidget.onLatestDataUpdated();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.valueChartCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.valueChartCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '300px',\n previewHeight: '150px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'speed', label: 'Wind Speed', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)'}\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent) {\n return [{ name: 'speed', label: 'Latest', type: 'timeseries'}];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-value-chart-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-value-chart-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind Speed\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 16 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 26) {\\n\\tvalue = 26;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 16 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 26) {\\n\\tvalue = 26;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"layout\":\"left\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":28,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0.2,\"color\":\"#6083EC\"},{\"from\":0.2,\"to\":3.4,\"color\":\"#5579E5\"},{\"from\":3.4,\"to\":8,\"color\":\"#4369DD\"},{\"from\":8,\"to\":10.8,\"color\":\"#2B54CE\"},{\"from\":10.8,\"to\":17.2,\"color\":\"#224AC2\"},{\"from\":17.2,\"to\":24.5,\"color\":\"#F04022\"},{\"from\":24.5,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Wind Speed\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:windsock\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"18px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":true,\"decimals\":1,\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null,\"units\":\"m/s\",\"displayTimewindow\":true,\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1697382151041,\"endTimeMs\":1697468551041},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "wind", + "speed", + "airspeed", + "flow", + "gust" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/slide_toggle_control.json b/application/src/main/data/json/system/widget_types/slide_toggle_control.json new file mode 100644 index 0000000000..208c5536c4 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/slide_toggle_control.json @@ -0,0 +1,34 @@ +{ + "fqn": "control_widgets.slide_toggle_control", + "name": "Slide Toggle Control", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Sends the RPC call to the device when the user toggles the slider. Appearance widget settings will enable you to configure how to fetch the initial value of the slider.", + "descriptor": { + "type": "rpc", + "sizeX": 3, + "sizeY": 1, + "resources": [], + "templateHtml": "", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-slide-toggle-widget-settings", + "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"title\":\"Slide toggle control\",\"retrieveValueMethod\":\"rpc\",\"valueKey\":\"value\",\"parseValueFunction\":\"return data ? true : false;\",\"convertValueFunction\":\"return value;\",\"requestPersistent\":false,\"labelPosition\":\"after\",\"sliderColor\":\"accent\"},\"title\":\"Slide Toggle Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2,\"widgetCss\":\"\",\"noDataDisplayMessage\":\"\"}" + }, + "externalId": null, + "tags": [ + "command", + "downlink", + "device configuration", + "device control", + "invocation", + "remote method", + "remote function", + "interface", + "subroutine call", + "inter-process communication", + "server request" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/snow_depth_card.json b/application/src/main/data/json/system/widget_types/snow_depth_card.json new file mode 100644 index 0000000000..424b2b17a3 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/snow_depth_card.json @@ -0,0 +1,36 @@ +{ + "fqn": "snow_depth_card", + "name": "Snow depth card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest snow depth telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'snow', label: 'Snow depth', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Snow depth\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 120;\\n}\\nreturn value;\\n\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"ac_unit\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#7191EF\"},{\"from\":1,\"to\":10,\"color\":\"#4B70DD\"},{\"from\":10,\"to\":30,\"color\":\"#305AD7\"},{\"from\":30,\"to\":60,\"color\":\"#234CC7\"},{\"from\":60,\"to\":90,\"color\":\"#F36900\"},{\"from\":90,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#7191EF\"},{\"from\":1,\"to\":10,\"color\":\"#4B70DD\"},{\"from\":10,\"to\":30,\"color\":\"#305AD7\"},{\"from\":30,\"to\":60,\"color\":\"#234CC7\"},{\"from\":60,\"to\":90,\"color\":\"#F36900\"},{\"from\":90,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Snow depth card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"cm\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "snow", + "snowfall", + "flurry", + "blizzard", + "snowstorm", + "snowflake", + "sleet", + "whiteout", + "snowdrift" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/snow_depth_card_with_background.json b/application/src/main/data/json/system/widget_types/snow_depth_card_with_background.json new file mode 100644 index 0000000000..2b614183fd --- /dev/null +++ b/application/src/main/data/json/system/widget_types/snow_depth_card_with_background.json @@ -0,0 +1,36 @@ +{ + "fqn": "snow_depth_card_with_background", + "name": "Snow depth card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest snow depth telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'snow', label: 'Snow depth', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Snow depth\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 120;\\n}\\nreturn value;\\n\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"ac_unit\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#6083EC\"},{\"from\":1,\"to\":10,\"color\":\"#4369DD\"},{\"from\":10,\"to\":30,\"color\":\"#2B54CE\"},{\"from\":30,\"to\":60,\"color\":\"#224AC2\"},{\"from\":60,\"to\":90,\"color\":\"#F77410\"},{\"from\":90,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#6083EC\"},{\"from\":1,\"to\":10,\"color\":\"#4369DD\"},{\"from\":10,\"to\":30,\"color\":\"#2B54CE\"},{\"from\":30,\"to\":60,\"color\":\"#224AC2\"},{\"from\":60,\"to\":90,\"color\":\"#F77410\"},{\"from\":90,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Snow depth card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"cm\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "snow", + "snowfall", + "flurry", + "blizzard", + "snowstorm", + "snowflake", + "sleet", + "whiteout", + "snowdrift" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/snow_depth_chart_card.json b/application/src/main/data/json/system/widget_types/snow_depth_chart_card.json new file mode 100644 index 0000000000..f85abe154d --- /dev/null +++ b/application/src/main/data/json/system/widget_types/snow_depth_chart_card.json @@ -0,0 +1,39 @@ +{ + "fqn": "snow_depth_chart_card", + "name": "Snow depth chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a snow depth data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'snow', label: 'Snow depth', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'cm', decimals: 1 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'snow', 'cm', 1);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Snow depth\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 120;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"cm\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#7191EF\"},{\"from\":1,\"to\":10,\"color\":\"#4B70DD\"},{\"from\":10,\"to\":30,\"color\":\"#305AD7\"},{\"from\":30,\"to\":60,\"color\":\"#234CC7\"},{\"from\":60,\"to\":90,\"color\":\"#F36900\"},{\"from\":90,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 120;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"cm\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nif (value < -10) {\\n\\tvalue = -10;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"cm\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Snow depth\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"ac_unit\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "snow", + "snowfall", + "flurry", + "blizzard", + "snowstorm", + "snowflake", + "sleet", + "whiteout", + "snowdrift" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/snow_depth_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/snow_depth_chart_card_with_background.json new file mode 100644 index 0000000000..e5c80ad5db --- /dev/null +++ b/application/src/main/data/json/system/widget_types/snow_depth_chart_card_with_background.json @@ -0,0 +1,39 @@ +{ + "fqn": "snow_depth_chart_card_with_background", + "name": "Snow depth chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a snow depth data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'snow', label: 'Snow depth', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'cm', decimals: 1 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'snow', 'cm', 1);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Snow depth\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 120;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"cm\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#6083EC\"},{\"from\":1,\"to\":10,\"color\":\"#4369DD\"},{\"from\":10,\"to\":30,\"color\":\"#2B54CE\"},{\"from\":30,\"to\":60,\"color\":\"#224AC2\"},{\"from\":60,\"to\":90,\"color\":\"#F77410\"},{\"from\":90,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 120) {\\n\\tvalue = 120;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"cm\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nif (value < -10) {\\n\\tvalue = -10;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"cm\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Snow depth\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"ac_unit\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "snow", + "snowfall", + "flurry", + "blizzard", + "snowstorm", + "snowflake", + "sleet", + "whiteout", + "snowdrift" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/soil_moisture_card.json b/application/src/main/data/json/system/widget_types/soil_moisture_card.json new file mode 100644 index 0000000000..d5053e8a87 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/soil_moisture_card.json @@ -0,0 +1,29 @@ +{ + "fqn": "soil_moisture_card", + "name": "Soil moisture card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest soil moisture telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'soilMoisture', label: 'Soil Moisture', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Soil Moisture\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:water-percent\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#D81838\"},{\"from\":20,\"to\":40,\"color\":\"#F36900\"},{\"from\":40,\"to\":60,\"color\":\"#4B70DD\"},{\"from\":60,\"to\":100,\"color\":\"#234CC7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#D81838\"},{\"from\":20,\"to\":40,\"color\":\"#F36900\"},{\"from\":40,\"to\":60,\"color\":\"#4B70DD\"},{\"from\":60,\"to\":100,\"color\":\"#234CC7\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Soil moisture card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "soil", + "moisture" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/soil_moisture_card_with_background.json b/application/src/main/data/json/system/widget_types/soil_moisture_card_with_background.json new file mode 100644 index 0000000000..a5d42b7593 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/soil_moisture_card_with_background.json @@ -0,0 +1,29 @@ +{ + "fqn": "soil_moisture_card_with_background", + "name": "Soil moisture card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest soil moisture telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'soilMoisture', label: 'Soil Moisture', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Soil Moisture\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:water-percent\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#DE2343\"},{\"from\":20,\"to\":40,\"color\":\"#F77410\"},{\"from\":40,\"to\":60,\"color\":\"#4369DD\"},{\"from\":60,\"to\":100,\"color\":\"#224AC2\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#DE2343\"},{\"from\":20,\"to\":40,\"color\":\"#F77410\"},{\"from\":40,\"to\":60,\"color\":\"#4369DD\"},{\"from\":60,\"to\":100,\"color\":\"#224AC2\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Soil moisture card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "soil", + "moisture" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/soil_moisture_chart_card.json b/application/src/main/data/json/system/widget_types/soil_moisture_chart_card.json new file mode 100644 index 0000000000..501f62b8c7 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/soil_moisture_chart_card.json @@ -0,0 +1,32 @@ +{ + "fqn": "soil_moisture_chart_card", + "name": "Soil moisture chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a soil moisture data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'soilMoisture', label: 'Soil Moisture', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: '%', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'soilMoisture', '%', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Soil Moisture\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#D81838\"},{\"from\":20,\"to\":40,\"color\":\"#F36900\"},{\"from\":40,\"to\":60,\"color\":\"#4B70DD\"},{\"from\":60,\"to\":100,\"color\":\"#234CC7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Soil Moisture\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "soil", + "moisture" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/soil_moisture_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/soil_moisture_chart_card_with_background.json new file mode 100644 index 0000000000..d7179ebdc1 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/soil_moisture_chart_card_with_background.json @@ -0,0 +1,32 @@ +{ + "fqn": "soil_moisture_chart_card_with_background", + "name": "Soil moisture chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a soil moisture data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'soilMoisture', label: 'Soil Moisture', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: '%', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'soilMoisture', '%', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Soil Moisture\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#DE2343\"},{\"from\":20,\"to\":40,\"color\":\"#F77410\"},{\"from\":40,\"to\":60,\"color\":\"#4369DD\"},{\"from\":60,\"to\":100,\"color\":\"#224AC2\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Soil Moisture\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "soil", + "moisture" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/soil_moisture_progress_bar.json b/application/src/main/data/json/system/widget_types/soil_moisture_progress_bar.json new file mode 100644 index 0000000000..946390b231 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/soil_moisture_progress_bar.json @@ -0,0 +1,30 @@ +{ + "fqn": "soil_moisture_progress_bar", + "name": "Soil moisture progress bar", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAq1BMVEXg4ODf39/g4OAAAADg4ODf39/////g4OAjTMf19fUhISHk5OQ9PT1YWFh0dHTx8fGsrKzk6fgvLy/Hx8e6urqQkJDI0vF1j9xaeNXv7++enp7y9PutvOrV1dU+Ys6GhoaDmt+Xl5cwV8vW3fX39/efsee5ubmpqamCgoLLy8tmZmZKSkq6x+2RpeNLbdGRpuPCwsKOjo7c3NxohNl+fn51dXXU1NSxsbHb4O/m3dkHAAAABnRSTlPvIL8Ar7DvmsykAAAEw0lEQVR42uzPwREAQAQDwDiGp7f+G702wmQ7WLg91HZhDsPkeg1DdB4wD5Un4EqkFCGjCBtF2CjCRhE2irBRhI0in9062JUVhMEAvOvib6GkyIaErTHj+7/fpeKNnmPOLMks5l8wENH0i3Tip+UL+bR8IZ+WdxBNpSx0T4pMCXEs1nRezCnRFY6JZuc9hAU9wnTlhfWCBCCTRwC60rDTlVLpTeZABEW1INxtjX5ANv/NuEF8rXQFQm8yBwLzcRftgBIL+2kqfIfIsSNCDsgaNz9rWpJrtpiYWoGVNl5LLUqptK3PtY7H0RyIodLIYhBY85rzHbL1JSleDtEdZn6JEYg6YIfoKoCs47UImAIMgXg8bhokAVK5T1SMqZnpb0iz4NtWhxQU76F0QNRL31BpGG6QV24kyNQQpkGII4CotHjpjnhAXlAKQg7ZweTlDYhZJmV9Qpr31It8v86CeJIgUEEl6mP6DckZlfsSOIv10SF+n4VED4hrF4zwJEgruY9q0IRyQNYHhCxs4ANi5ruxDwjpGgXLH5CQPDoJkhFGy+uYCfgJ8aYmAsbKrx2QHBcvOJ4Gwx2isG5gpUkQ78ya+uCzmCIiPSEKpAHJkFoNfPaIpCUiuaG28/7/ENoQUpV5za7RAIvqs9H1T8goDRjtBMnn328TwPxAVqAQCxDkhBwSIPAsiIdZT9SYvA8zPW9VHqs/t36/fr+Qf+zPMQrAIBTA0O3zVxcpFErRA3j/21Wws64Z8qasYXOExhEaR2gcoXGExhEaR2gcoXGExhEaR2gcoXGExhEaR2gcoXGExhGa80gp8btrrUF1GOlvyywrr5yeEUz7kZHTGuktlyuQ5sj242O/jlkbhoEwDG/Hl/EWSaBDCLvabGXwkv//yyo7anBbEBhjuBA9WzLlRb5TPMSJaRWA6IkDoPPxaoUwEJiexnoUE2BJo1aIA5gqL2K2uDcM2X4zG+NpxwCRNGqETEByKO67uICB6xmRKo2QGT8SbaILwDDSxmY3kiKNEAsMJSEB8HVmioVpZRyZhVjPLm6HSD2ZRCsjyQJhqzKWRkd2Ii3aIUyF2S8qqcPuwxSjKFpg7ZDx/8YdEGjFc2LHpEYjJAHy2rjGWlNv+OFVKmZW82w1QjzgXrMi9Vi4fllw5iBZSIdGCC1AZH8HQqkagDuTCWvVU2CZadIyJq0QH1DUq0NQOXpKM0kk0XLPt0KILYp68bHbouZfpUvQMu+NkPpS6Pcf+G+qmj8qH/TO/iZ6iDY9RJseok0P0aaHaNNDtPmAEHa4Rn7cTjgeknEVdybk63AIrnM743CIw1XymY7jJ8IZ13CPMx0fvLXeTA/Rpod8t3fHLAzCQBiGY20/EYcuWc7lOBDELYrV///PWkvmQlxypvdC9nvIcONpyyDa+gEJs3hkbggjABIRigOlQ4gJsiBrLe8MYAyYBAsPtFI6ZOsBn/1L6IAwoV0RdmCc0iHTqAby+j7vDaIFwgSsCCchtLboN2QtQmTBJlgE4C4dAs+zIG8R0rEcgn2e/amFOAzQEsWB/nuzXyuDaMsg2jKItgyiLYNoyyDaMoi2DKItg2jLINoqCOJQRI27FXB6Gng+XOUKOD5NH0Zd3Zur525V/QaCIDJe0ogfNAAAAABJRU5ErkJggg==", + "description": "Displays soil moisture reading as a horizontal progress bar. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'soilMoisture', label: 'Soil Moisture', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Soil Moisture\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#D81838\"},{\"from\":20,\"to\":40,\"color\":\"#F36900\"},{\"from\":40,\"to\":60,\"color\":\"#4B70DD\"},{\"from\":60,\"to\":100,\"color\":\"#234CC7\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"tickMin\":0,\"tickMax\":100,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#D81838\"},{\"from\":20,\"to\":40,\"color\":\"#F36900\"},{\"from\":40,\"to\":60,\"color\":\"#4B70DD\"},{\"from\":60,\"to\":100,\"color\":\"#234CC7\"}]},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Soil Moisture\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "progress", + "weather", + "environment", + "soil", + "moisture" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/soil_moisture_progress_bar_with_background.json b/application/src/main/data/json/system/widget_types/soil_moisture_progress_bar_with_background.json new file mode 100644 index 0000000000..5747df6085 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/soil_moisture_progress_bar_with_background.json @@ -0,0 +1,30 @@ +{ + "fqn": "soil_moisture_progress_bar_with_background", + "name": "Soil moisture progress bar with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays soil moisture reading as a horizontal progress bar with background. Allows to configure value range, bar colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 2, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.progressBarWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.progressBarWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '280px',\n previewHeight: '180px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'soilMoisture', label: 'Soil Moisture', type: 'timeseries' }];\n }\n };\n};\n\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-progress-bar-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-progress-bar-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Soil Moisture\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 7;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"autoScale\":true,\"showValue\":true,\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#DE2343\"},{\"from\":20,\"to\":40,\"color\":\"#F77410\"},{\"from\":40,\"to\":60,\"color\":\"#4369DD\"},{\"from\":60,\"to\":100,\"color\":\"#224AC2\"}]},\"tickMin\":0,\"tickMax\":100,\"showTicks\":true,\"ticksFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"ticksColor\":\"rgba(0,0,0,0.54)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"barColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#DE2343\"},{\"from\":20,\"to\":40,\"color\":\"#F77410\"},{\"from\":40,\"to\":60,\"color\":\"#4369DD\"},{\"from\":60,\"to\":100,\"color\":\"#224AC2\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"barBackground\":\"rgba(0, 0, 0, 0.04)\"},\"title\":\"Soil Moisture\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"%\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:water-percent\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"18px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "progress", + "weather", + "environment", + "soil", + "moisture" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/solar_radiation_card.json b/application/src/main/data/json/system/widget_types/solar_radiation_card.json new file mode 100644 index 0000000000..94fee0da10 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/solar_radiation_card.json @@ -0,0 +1,30 @@ +{ + "fqn": "solar_radiation_card", + "name": "Solar radiation card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest solar radiation telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'radiation', label: 'Solar Radiation', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Solar Radiation\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 1100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:radioactive\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#5B7EE6\"},{\"from\":0,\"to\":250,\"color\":\"#80C32C\"},{\"from\":250,\"to\":500,\"color\":\"#FFA600\"},{\"from\":500,\"to\":1000,\"color\":\"#F36900\"},{\"from\":1000,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":28,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#5B7EE6\"},{\"from\":0,\"to\":250,\"color\":\"#80C32C\"},{\"from\":250,\"to\":500,\"color\":\"#FFA600\"},{\"from\":500,\"to\":1000,\"color\":\"#F36900\"},{\"from\":1000,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Solar radiation card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"W/m²\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "solar", + "radiation", + "sunlight" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/solar_radiation_card_with_background.json b/application/src/main/data/json/system/widget_types/solar_radiation_card_with_background.json new file mode 100644 index 0000000000..2e8317c561 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/solar_radiation_card_with_background.json @@ -0,0 +1,30 @@ +{ + "fqn": "solar_radiation_card_with_background", + "name": "Solar radiation card with background", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC91BMVEW/cAWSRAPLewq4ZwHBbgKPQwTOfQjLeQPAcwP////67tD57M378NH57c/46cr358r78tP46Mz35sb25Md8wyL89NT35sj25MT46Mj56sz56s7999b///n14sT568v++df++9rv1sT89db78tHUhgT///Ty28f578r14Mf//ubx2cb//+/25MnViAf//uP//Nz57s3z3MPShAT//eD24sjTgAT468j//+z+/N/468b99tTx2MTw2sPalAz//+ny28SMyjn14MXRfQPblg7s1cPOfALTdwb03cfWiwT03sTakQnZjwbXjQXVgwTt18LQdwLnrjH68cvWggzz38LKeQLSdALlpinUeg7ZiBXflSXnrC757cfkpCbbjBrcjx7YhhDWignUegbWiATLcQPQfwLosTTekiLCZQLjoSPPcwLEvqbq1MM8OjLYjQzhmim9ZAL89dDdmhP7882iUALlqSvbkwvEbAPRgQLjnSThmiLsxX8hIRzoszncoQylVgKppY/GdgKEPgLtyYHqwn3GcAT89dnKawLpvH20YQPflByrWQLRewHSiQTn08PYnga1sZruzITgmRnGZwLl0sLjoCvuzCPcmQ7YkwnXhAi/aQLqvYLpv3yt12ye0lfeqQ3BYQKdTALw9eGdmYXvyo66XwHUz7PerHuOiXfptTy5ZgKZzkzq5MbinR1XVUnTlwfVfQevXgLv6sri3cDe17vQkAXf8cerq6W52XjLgQbz2Znx0JGExy4hHxvqxBbirBbmuxLOiATP6au64IrsxIjy1ZPv6cDKxqxKRz6aSAKLQgLMzMTCwr+zVgL4+/K/3YD47C7ktRvisxDn5tvRonqpTgF9OgHy8tS/v7W0tLDd6anisnpzcGEvLSfn57Xwz43GnHqBfGzY4aDmt3tybV/03izv0ivZ7rnF5Z7K3o/x2oTy8uzT086goJnXqXrm9NXx9Mj025r39dL5+eDirVVlYlX4+uj691by31buz0qvfFMgHxveyLaVUhoSesfDAAAACXRSTlP9/vwVv7ewv6/8oWFpAAAj8UlEQVR42oTUsYpTQRTG8YsRFTWYJZAlAbstTRO8D+CFgI0gbhCvxERkEmxMJVwXLAQLWawEC9HSxmLBWlt9Dp9ByAv4nTNz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", + "description": "Displays the latest solar radiation telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'radiation', label: 'Solar Radiation', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Solar Radiation\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 1100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:radioactive\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#5579E5\"},{\"from\":0,\"to\":250,\"color\":\"#7CC322\"},{\"from\":250,\"to\":500,\"color\":\"#F89E0D\"},{\"from\":500,\"to\":1000,\"color\":\"#F77410\"},{\"from\":1000,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":28,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#5579E5\"},{\"from\":0,\"to\":250,\"color\":\"#7CC322\"},{\"from\":250,\"to\":500,\"color\":\"#F89E0D\"},{\"from\":500,\"to\":1000,\"color\":\"#F77410\"},{\"from\":1000,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMwAAADMCAMAAAAI/LzAAAADAFBMVEX////krC7otzfptzvmsjbrvjvgox7rvj/jqSrgoB3lsDDjpyztwUDquzvipCntxELhoijquTzmszLmrTLntDXotDjtxEjgniPhpCTotDvux0PfmyPfnRvlri/lqS7hnynipyTtwUXnsDLvx0vdliDswT3jpinx0E7quz/fmCLnrzTclBPTgQbsvkPhoSHUew7lrCrWhAbvyEXblAvkqiXhoC7emxLy1FDwy0bajBvgmSfwy07bkxvWhA7aixXYiw/y1EvipiD341jSeAr451rwzknfmhz02FD77mDquTfdlxbTdwX121PQeAPckiDfnBjpvDnioy/fmRjYhxTOdAP6617bjxngnhb231XhmyrYiBrWgxjakQzy0UnlqjTLbBTVgBLmrTfAZgLmrC7bjx////j//+njoSX02k3otjLhnCb13lHclxPSdALdlx3JZhP//+TBWA23YALhnCHFZQPekyPLbAXUewT99HHfliXz1027YwL+9XjosjbQfQTinRz88WzGYRLDXRDmsCrwy0r//7rinyTUfgbkpjPZkBT+/+3tw0D+/q377mXxzk3NcAL88WTXhgnZhw7dmA/Zjwr34VHlpiixXQP+//LWgAr//8f45VP56VX88mjhqhT+/qb//9/JaAf//ZrkpCTnri/CYQLlqSr++IHBXAXGawPOchb++HzvxTzAVQz//83++YbTfBn+/JX+/rP77VnwyT/Vign//9njrx3fkxvOcg3dpwrbmArQdxbGYQr//8DeoA/++4usWQPBbATHcAO3UQatTgTswTbXjAf//9P//qDntSrckBTgpBS9UwrZnwe7WASgSgPovyeyTwX010js0CzpySHmvxu0VgKaRgP+/JDyzkPltyDkoR7irxHquTHJdAaSQwLsvDX+93TqxivitxOnSgT23kzUmQf99Wzw1Tbu2Sny4S7UkQbNhATKfAWlUgLGdwL88V715zLQigTtyDb+94/9+KD89IX48EP37jb79k7x2mP8+l3253f47Zb14z/6IjwPAABAGUlEQVR42lzXz0vTcRzH8cxRpjSsZRHmVjakrZIiOxjL0WWHoMkSFkpEoTDw4EU87LB2szxOQujSxeF/IAw6dOzgv+DfEP0JPd+v9+fjZ/ns67Z+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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Solar radiation card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"W/m²\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "solar", + "radiation", + "sunlight" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/solar_radiation_chart_card.json b/application/src/main/data/json/system/widget_types/solar_radiation_chart_card.json new file mode 100644 index 0000000000..87817b6d3b --- /dev/null +++ b/application/src/main/data/json/system/widget_types/solar_radiation_chart_card.json @@ -0,0 +1,33 @@ +{ + "fqn": "solar_radiation_chart_card", + "name": "Solar radiation chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a solar radiation data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'radiation', label: 'Solar Radiation', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'W/m²', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'radiation', 'W/m²', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Solar Radiation\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 1100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"W/m²\",\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":48,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#5B7EE6\"},{\"from\":0,\"to\":250,\"color\":\"#80C32C\"},{\"from\":250,\"to\":500,\"color\":\"#FFA600\"},{\"from\":500,\"to\":1000,\"color\":\"#F36900\"},{\"from\":1000,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 1100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"W/m²\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 80 - 40;\\nif (value < -120) {\\n\\tvalue = -120;\\n} else if (value > 120) {\\n\\tvalue = 120;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"W/m²\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Solar Radiation\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:radioactive\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "solar", + "radiation", + "sunlight" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/solar_radiation_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/solar_radiation_chart_card_with_background.json new file mode 100644 index 0000000000..fbe4faa7c1 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/solar_radiation_chart_card_with_background.json @@ -0,0 +1,33 @@ +{ + "fqn": "solar_radiation_chart_card_with_background", + "name": "Solar radiation chart card with background", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC/VBMVEW8awLAbgKzZASuWgCbTAG/aAC3aAD////57M714sP25cX68NH358j67s/5683679D46Mv78tT46Mnz4MP35sr46sz35sb89dX46sr78dH25MP999b89NP///r03sP04sR8wyL46M325Mf///T78dP35cnw2cP14Mf++9ny2sb24sX579D//+j//+z///D24sj++dj++db57cjw18XSggP14MPr18LQfwL//N3ThQP36Mbw1avWiwby3cP25MX46sjz3Mf138X57Mv68crMfALKeQLy28Pv0qv//d//+9vu1sTq1MLUhwTHdgH//uPOfgL//uXEcwL46sXQgALw28Pu2cL//eHPewLm08IgHhrMdQPe0LX57svx2Kv99tXj0cKwXQO0YAPUiQirWgPWggjTewT68szlpynkoCHhnBrSfwO+awPXjwvmqyvkpCPglxnVfgfMeQL++9/k2r7nrjDbiRXXhA7QeALOw6pWUkelVAP79c/13bbJcgPBcgPf1brcjRrepwvekhy+bwPm1brDbgT79MvahxHSyK735Lrt18LDu6PDtJ6+ZgK4ZgTlqCS8YwG4YQHszqr89tvayrE7NzHlz7vJvKO+r5ry2bDaoAjCZQHr2cO5qJfUiQO9tp6cTQTSdQLg8crn3sGu2nTflCLWmQfp4cSqoI3hmiDhrBUuLCbRhATz7M1+dmjcjxPy4sWNh3VJRDzgy7jX0LWd0lnekxXQkAXr3L/IwqiyoZPZiAru5cjs4MNjXVE8OTLhoRfbmQ+GPwG1rZbpvxSKQQL899Dz58myp5KLgnHuzR7JgQTv9+KORAP3+/LYwrDRvKqmm4iflYNxbF726Cnz3SWUSQXJbAPP6ayMyz3isxHPgQ7gxarJtaaZj3zgmCTkyKu13YKDPQHBrqC+4pBvZFvZ7bj14zK4spv57zQ6Ni/v1Cv57rVwaVv046+He2/CeQnI5px+OgH89bil1meVzku94YrZo1rjwJTpxpLnwin3++royZ7FjUspJyKJpBaoAAAAB3RSTlPPvyDPvyAgC6rIiQAAKVZJREFUeNq021tom2UYB/B6oabm0DZrbHqQ2FpBjIcWqnVOK0jFCWqVwSQEvZCJWo9rnHwBMTInRlLwwoZWLxZY1VVr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", + "description": "Displays a solar radiation data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'radiation', label: 'Solar Radiation', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'W/m²', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'radiation', 'W/m²', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Solar Radiation\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 1100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"W/m²\",\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":48,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0,\"color\":\"#5579E5\"},{\"from\":0,\"to\":250,\"color\":\"#7CC322\"},{\"from\":250,\"to\":500,\"color\":\"#F89E0D\"},{\"from\":500,\"to\":1000,\"color\":\"#F77410\"},{\"from\":1000,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 1100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"W/m²\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 80 - 40;\\nif (value < -120) {\\n\\tvalue = -120;\\n} else if (value > 120) {\\n\\tvalue = 120;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"W/m²\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAU8AAAELCAMAAABu2vS6AAADAFBMVEX////VhwXRggPPfwLVigXThgTmszTptjrnszjRfgLqvD/krS7msDHquDztw0HWiwjPewLjpSnswD/ThAPTgATkpyrblAvlqi7XjQXhox/sv0PnrjLNfALakQzhoinpuDftw0fYjgnrvjzwy03fmiLuxkPkqC3otjTLeALpvDrOdwLHdgLvx0vfnRfblg/flyXosTThoCTclR/gnCTgmingoyXTdgPz107goRzZkgncjx7y00vdmBPvyEXfmxzhnivclBPfnx/wzEf13VPjqiTxz0njqyrfmhjEcwLemxPx0E7SeArjoiXipiDipiXUewTdmBf77WHelx/PcwLKcwLrujj02VLbkRnioC/ekyL34lfVggTinibnrC7Uew7hnRuyXwPZixzy01DdmA3YhxPbkRLZjg/BVw3nsDfdlBvYkAX45lr//+XYixb0207mrTblqjT24VL+/qvBcQP99HHKawXhnSDVgBPXiw7///jWgxjDXBDJZhPXhAfFbgOFPgL88WzGYRK8YwHBbAPSiQX+/aK2YwS/ZwLVggv//8L66l3IZwWkUwLswDiuXQPuwz3jpC/cjRjAXAXLaxT+/rr+/Zu9VAr//+n++YbmqyrMbwP+9XjkpjPmry7772fXgw7Zhwz//9PgqhT+/JTnsy7DZgKZSAP45VPUhwySRQP+//LFYgjCYgL++43JfAT+/rPXhxrSexj//8r++IGrWQL+/+2LQQL//9qpVgK2UQa4XwG8bQLVfgzNcQz010nlpiSeTgPlryf88WT//+D23k66ZwTboQf661jPdRbdnQ/dqAj++HzqujDjrxn77lzwyUDmvB7goxb56VXfow6vTgTbihPjoR7ryirvxzru0ynNcBby3DPyzkP982j15jTowyOpTAPjtA7ryBvktRu4VwP57zTx3CX88WD15yjhrgrSlAbluxLYmAb+93TtzyB+OgGwVgKhSAPmtifMhQbqwi3XnQbu0TTowRP99o759Ub992v460X7+FXy3mr36oT58JokoBOXAAB6ZUlEQVR42mzYu2tTYRgGcK13o9jSesMYKEgpisY2HSRI2qFYhyghSy1NKcELjRAchFiETjpkNGMHsThlku4KnRQcHESaP0Lc/Ad8nvdy3nMan/OdY73bn8/7fUcPff38GSvJyyT3kRuS6enp/Ojo6CnJhKaLxXxI8kSz7+n1ek+TLKRyd2Fp6e7dJcvN9fWB5gGyh4WbT6zF8+cXFxe3U1ncW5Tg+/hk+GMZfjRg7qyns8RUKuvrlfXK0nqn06lU6vVai+ktLy/3evtYzI7l3LNWsfx08tLkJj7e2dna2ho/+nZy8sLNn4s/Pn7T/PrzdmR6WnXoBC2IvUEOKWN48qKmBJbqqZwm+gVXd4hzBxfims8ElBf+1AsLIbrUoqeT3jTPO7gGly8P1NOJzm8L573tRVwMv3qe2ZMsuidu/jT8ApqEs4IsRfi1esuyTE9EPRPUzR6ogcmI5+rRc1ev3nr8/fuv73+Y338eP//b/xSeBGWGPKOcGrEkJ+KaDDFxh6h10zh7Ws9n0EQ3wcm0eKGbrCcWs27B5w9N5HJ170EVNKZJz+1MVHN7j08TpSWLLbnsosGJWvJmashSjZ64y9pPJsMppEZJzNXVrc3NZ5Pnzk1OTl59fu7o0aPHxt8e7ffz9AxQJaWnW8qdKSdDzevkdE+tJxOcpokIJ4IHMZefLuB6KpxMbeFujPq6g/LzJ0K1CsyqYVqMcS08I/esorZFkJMj76CdjqOSdalew+9Zr5MTmliSniUhjXS3VtVzZ3/z8OHDW304HxsfmRgdPXLk2EgeAx+iMMt6iqRogpqYsXe6J254SkO7HwIzds72/pM2PTXLy5z11rKVs9VaqNXukrPmoMHpmtWxsTFSei4iQLy4tnaRjzVHNVlgPlRPfaKcng44NeuVTucaUq/UyUnQlfoKUy5jkXS4pPi0ul1q0hNVHR9fXR3fWp0YOXLkVP7U6HR+mp4BymQ8YSkP76dhklPimFxez7Rm+8nuk3az3e55ljWtFhY4kRogwWn9ZHGynHsQzXpObV+EIpYnO/kMPC3s9qA6GOxZQU3zGp/krGNZwjPdUXSBU7aPc4Affehy3GXhAcuJ1YlTI0fyp05BQ2iCMwF1T7U8MO3BGePOpCc9aSfuZrPZC88yNJ8CU7PEKwJOj2Bqxug55pqoJAJFMc3lRPS9PPnNaGmyjT58qKJViCKo56DTaTQaoFRPpGOexSJAIyLabEpDdeFzYdhQVtQyoYEBPfP5PEGVFFr/8WTwNE33DE1m+E1JMTHru7vKWRbLcutAiNnybVPfYAaG6Z57qjmmgy6cawdy5Qo9t+HpZTVQfdLUM4AnUupISvAsydRfC88AtZKaJ7LzAaAMu8lqIsEpoEMVdc8YdybDyX1iyBOW6tl1zjbmvd1kek2IIgFab9Xr7CZe+uR8ZTjonEe1FE9QQBKcU5xzNpJxRoZfShw3mXqpdFp0ZmYGt3qW2NJSqXQNnAw8V4ppTgP1kedN0g/w3DFQS3gi8HTQqCg934gnQDVZTvHESeRvngwto5xMG6El7rJrriScfFbqtVql4v0UTsSaKY8xatKTnBoC0vDMGWHkB2cMGDfnX04r/AyNYEYaHuG8VirNFcnpSZOaZ1OWDLz0EwuglOTyeWcCNFvRQ3gH/fzmDThdNMM5au3kOYSTTeuJhCYDS4b7EO5l5eTiYVq3VBCOORYwXdNDSfQTnFNTB4tJxswH9pX3UPUii+cYFhKojSowG9ZTeM7BtTg3LGqe3KxkI7WZx9IdFB3txsB7R5UzRNXTHNUypn068bRBzx5GWPw7ZLhvRjPFkqHnSoIpnEJpnHEKMQ91ZGHJuJgj3o7g4+SbZfTDkxn2jIBTk/XUL8oe9dTzle9NWx56MuEpHR32tOhbPpNwZvZOscRtWyct+8DcNc6yesLRPGE5Oztbl9cUHq4VTcdT9VPI2+lzvmaekLM4psrat6Cr5DTSe9sOmh36mTQo7rkkRlosl4u4XLSJnjbpyXD7FNDoZzTUPK9fJ6gHnsGpmPdN0w/3NCfzBZzcWF6zmu32bls5LYScpWORxyg4Carhq4tmZoCbR0aVAQA03TMXiHxsbNz2nNw4efKkfriB7znDrF1xT+6k90zUScfkbkRLs54egsLTQfHZtJ9o0E901Ptpe6gXTE958QzRQ+l6Hhx2ScI5MjH0Er8LUHqaJW8DLTJByTRkzTRAidi2uYeb8Y0zp56wfLGBnMSKnDhBUCyP76c4nHI5oKpnkip/m0KjYJbzyNwcliUNyoJ6CNpuGyg5Pe5px7wCXc/ns57BySSa+exrfHh2jROWNux6Dq0kKXpoWVLPEh4YuBmPOe5BMspp5w8pmcCMHOeDzuYJeYaectif9wgoOOcLhUZ1npyNOQGNFJ20nNpEmwTNdDTjiYgGQT0QhdqQZ+Zkp2fePflLjIx0Ja94FHHY20zMOv44syuzoTk3B88Sc61EzxI0EVKiN5fhyeP4YaKJkJIRSV6RE8gB2Sgpk0uOpqmLzolHgWFF5zXDoFHQmPl2GhQjH54EZTKHEj2nzTM0gzNvnsIZ5/pIl6R91HN396Anhrw8m9WkJ+pJyJJIimX2TCepn+pra9wvNQmjUGIhZ3Ej/s0BeoWcGHxyEnVKOLEkhXfv3j0qzD9i5h8RlJclU1AXFUysGHm82dseimWcfKBxrF2820+LZ1aToWeW07vJl6RdevrOGQcRd03nlNDSe4lSVn3OC2MMKfkUBWoyGxZFU8ATZ0nJR1b1eLahcjLlclQVUgd990gDS95M2pOkZV4Oqqb0dM7sP5O0n7xj6M2T/cy2k9/KfsbuGe3k1tn/0O9j54xqYq1gzdLTLT3BicS+WRDP1H8hEROcjGtGLWmoORGsDEA1BL0iou/fn8m9z6knap94FsjJBz3pOtzQeLlPn0n78BTStCfuOOZ95E300P3wVM5PWk73HJmIevbhiaCau8ap0UMoGfUQbeAcMFC2U050Dac8/mVJTOe0QeflkhGHJW2YJiXN5bBQdm6iUxp6vsPCnSQqGtsoPA01DnlkP+koPN3UQH0TVVMVPRQHe/Y1nsnWk/3sP2FYzuDExsl+KmXUc55rXlPgGctWpj2ZizlcGNMsp1ARzUBPa/BBmDqoBJgmin7Sk0k8GfGMYPLlTxiexPSSuieDd/vNTXY0PMnpgSeWnUpZT+f8lA9PaKrnuPazL5zh2VRRjvpKWOJLkwxPTrlTaqRFOfaJL+9YG7fdMiR5y8UHk+IMUZ96LHoSlDFPT2jq0TQsmh353j8+7eU3pjCM47hBS1wqMRHiMqhbigRtEwsRIrGxlDBN2GiYtItGl934H2o/C8uu/I1+3+dynvfMwW/OuAv95Pe+z3tmZm/PTA8ODnQrKFBPeaqiJyTaeT4sz0eeGkVtPT+Nx65J9gRKsp4aQ7qinxXKGdERcGtiiUPMfeIL3Y7vwiR0E0osiSnCWFkoKpy56IkV9Bprni0kGtp5zrc60k2uOt4/4Un0hdSS/ynQ9LQFz7seBFKpcLLPsz1p+lmaNdrLcyxPYpvnAZw/pz+tmF/WKWcNoruJ2Vh2lM6pgEm6u3O+QTMpk7Mwh6aRaikDXw0l6iieXtH5XJTUc65Hm00/Ow3O9llR3vxMTwcdK+JMT9J48uLmMTCH/eydPY/jaZqqfXjqVTkCJ4s9QTf6nExVzn+AtvumitnMIL87J77OSTTz6vmr+ubMVYtD6nugixTNEGXNJ+g1BdL5fH4fz/kc0MN2E+UBpycr6icnTi3TqGj086hd8WgmaInKE8uOszwTs++pHOwp0y/8a+virHqKE8/qpv3f4TysIZTrXKkXPliwaLbN5MLOJXkAGaqGTBYqypK3XCNw7pO5JUTTUmHNay6pqOkZoukp0K6gR+I80mOMpzWU8G17cJInBe2f5E/UyVPvkIrzOJ7S5KhE9JqW+ilMOFOTfE7OrVzphxP3vG8vvKelzonnHFOBMoIkScE2IVvGQVqm8lQY9emJZpGSqqi7AhuaPEgteEVf7Uygig2k0dGPJRqad0riOX5C6Xu25cSzKSeeypHla3hOp/xjxVmHzjoh8cSTqJgdp0a6v3PRYaanNTMD1oJjfZ/YA88HPukT9Oz+977nlj8qmzGX+q+JrpvoLqASnc7cUxmNjkZLI/bRpZFq5oGTlKdzlueJzvOEcy4Z58EennBOv6x7P2vvjCRnYpL7GcqpE1KuSWnKQA52scwTs59bw54mJylPXdwKnMPznHkOC3qotOcmXQ3o3VBdJ95QfcwlGnp04cLIswTK8fQUUoIeG7QTTgP1xX6CvbPz9EwVts7kLMqGkylUml5Mkp5eThDyvFmYPNNSVy9XeZBa82vnz68lqWJ3Szt48nKVaX6XZ0WMPDOARlsV7ajrBmorng+NyXNGRS9YjBNPQDPl2Wvn7/KMdipez1/iPLCtU5z8M2D2Pctya9JazuFsPBNT8WrmQCeFiRp4/0iSrgKq4NkOJkDxBJRlP29Ie4d7Rb7aVhlYasThq7vZ0CnBcwjaF/UFf33Yzw7UPW256y8f/folzugn7dRFhks9Nc0Ty/ll57wW71i65rJzelIzJVHsQT7Wo4/ZgV68eIassepL1Cb9zs53C5rG6uX0bwFtImCxHuK59Wpj0fPbT0gNlA/YtSt+3HiqhsdS8xGYv7EkgvTZNVbE6f00TPdUqOfdBc4c6lXOfWbsNZ7+jiSYeILpScziLMfHevLgih9UQL+ouGcLSvGX9Ro+oqDiCWivollT9VLHOmVj49WzZ7GXhuhueE7dE1FpkvF41FbUXh0RW3i2s8hJ2WbTc1SeiIYnone7tV71pJvEVro0Fb4cPMlTRJtm8lzUDFEIF4JvE/VzxUHxtKSnsuOxiir9QQ9nd3h6Zfv9M2XjX56AzvQ50JMuioua1nkq6VmcJD09whyN/KiEZ2mCyYeBsOSZ90O11Fnp+56zvIlhlsvkwcIBaXWVmi1Usyjv9B4E6PBcWRFolLStaICeTlICZ+XQRBN0a/59/1AVxdOCJ6K7gBITtQ00PZUoKJrez9L8XZ4F6uXEU2U3zgK9SzaVmuvyLE3uTjwaDNesl8vu+aDHubZ6Bk1SmEE5TJF6VmQaokx6bzycBM/vnefCYTROoiKdMPH5jTyLEr09i6finnCGp7bQsQ2l5tyEWeMZmmC2nktxUmK4wynQPTjJezw3lLol0tPXukQ1h1jpVFOeT/E0ywfLD27CCabHOelaDp+B5iBFyt8MT0QhLVBMm32UC7ckzfU+kartqNzRu2Z5vm48b99mIsmzN+WHnlCWZ9VzJE9S7VSKE83N8Iz/VtTTy8n/XwVRM6ubN2/ePG9JTKW6Wav8v9Fvm3p0dKVAmzum8iSADtc9k12GE2JfQ3Oz9F7p+jkTqD5Hf/vkFYnKclQFXXKs1jOneyYWu/6WcV4wz2kz3P1zH2iaZ9xqYKnkUs93f5dPP90RpTDx/HDzg86LbgnDxZUVND2Lmu+UO9vv3m1vb9/hak0t7mmi1LMF9duFTjQ1I72j6MTjniGq+UBF9SHL169zA71wO8+geAaonuVZ95ktJ5hkpMcS7QRUc84izXXnJM0dZnDO9yMa6uLE05vJUr/54YMKqiGUoOTqSlnKKSnl6DFJE327/VbALWnsogTR1XbJ6/IkaHkORePNuvTcgFPPXXHmkr+tqJ9a7yx58dhqp58WeXLyVP6y2uGMfqI5m4nTQHfX0VzveTLVjZN9M0e6v4K0rGrqgC1KBU71M5c6oVzVzSomoZi9SFN5WzXtgdZhtE5OmJZniRZpM+Uzmxt4coBRdaKfs87zpECF6sf63q08nslJUpM45+jCSTydcybQ3dg7+/3synnpfrfWdzi+76C5fOq05lBy6m57tcFMz1rnLHDnHIRfcmjFPEl5klzy9SJJTnrioh2qewI64aGGqp9MBS13kp4SvfFtdsM9I+65hGfGPP862k0TT8X6uYcm7XxPP4Upzs+6glNvfN2PzbMO8Mvk9KkHRJbmubaqoOmY9ywvXzomRm754sULAJ8vZPv59rZ+5x15qz/9pjk8lWe7iWLqqYoqWDpoigK6BSjRVxae772fr03UQa9cueKg4797/g5Onrl5kvKkn7bes5/v8eSt9XqDCEvLvorJYgcTTysnnMraKp7RTTBd9PHLrprsmzIryo8V/cRQaannjXf05b17TPkBqBe0HfW9iurKLVQX82gC5oYWfHiyfwqUfLuhzPAksd4DtDzbaaRn/PqCJ6BoKnBSz/ScPAtOPP2zM2f948N+QmLIWi/lqbG+ahm2c2HPDMhhDPTjH9buLcTzOYzjuMOg/B2SDHJcZ2kmkyammZ0MwzZiWhdciJSNGMJoUu5crAuuXBCTZkvUlEKR3JFdtWlri/u5nqu5lyuf93P4Pd/f/2ecP3adjy/P832e7+//G6jR+Yey5/Hk3wwxUIZSXZdsyGeqQtsTVMGTuCec5ekFWp6I0u7NEtp6fhEFmt3OyPKYp87fGkcv5K7k79Lo1TVSnrww7K9j83ICzQ4nnhydaVnVefP1aAYnmqShvL+iX+BXeJ3OP6Pv1vQ3UqKABmnUZ3hm07vo4PoJZ7Y8ninagerLkd2T+rQcULdLk3YvUWKe+YJN37M0iXFK045Pn+6Kcaam4k+TLm88qzpp9gujOEMTzuv1gzjvuxHPxLQCVPAbRr/65ZdfRtTbXofoA/fdB+hB/csBdNDyePKtWl7fBpsoR2hq9j0XD3X9rm8H8IxLUoFKE1A8r75tOI1CM6vTy/M5faWwbguaR3h6s0d9ommc8iSsSVacVZ2FKU156p/+oCJNOGKeo4RkA/jE+vr6E+tPkPufiF8nT8VE51WhD1CggJonj5xIXudr0tfi1N/t/RN6cm2QPvlkeL4e9Yko9RmeRlqiVaB4kuG25BeqCTwFyjSiPvHM4Q7ocBiN0Hw+z06JxpbEIIKzqtM1iWFWn3eUw9yforQ9+vOKgepPc1CecVciN4RnvuZIEHXTGEpVoOZ5SbT8kzN3Ifo6noDiOX1NgkbLl6eS/c50jzTl2XlOdP2u3ItnVOcfe+ZXuDzvmPomTC6YpFq9OLM65+E0z4byxSdefPHFbX3/Rd/1S8p6hOP0ZYlqd5Kng3ZnaH/M52uO1fP61vOElGVPr7AASmYM1DwXF8WJJ6CTTccf7YOO7Z9/0O5Hj04cleZzJDw17VrPGdMMz1vwHNnnwPS6xVdOQrejmZ5oEjSlAiYpzMg7L5486T+nn2Dqqt728tQ3Nb3V50ET5TQJzwKtrgezVtF44MTTsHfxdExxuueihfrEc3IyPMmEp+342pcsDSgxUHle/ByizinPmO5kprcryRNOPGl2ykGeWpIY66RdkpLTOj01sUQzJclJ+xGhSCPr5Mf7NZmq5+9jtb+5xjxPWrg58GmdDfpmcWoO0SxPwktWAeqeS0vGqSx4fc6JlJ0ezxC1h8tWn+GZlrF85jqPOpoTjSeH56I4X5eovW0cnvkJ5uVXjWLvZIMntXOOtTpzXY0aU6ipzI7y0XcebfKOR+VK/29vb0eNhqemPD0fFRqgeOoUVVSgiA7u81WgigpUcVDFPNXxgCK6oAK1hp8z0Al9S888QC8tTxKacEfDi1Oe3u8qedp98abwJLOzePo1czR69XL70E3N3noOi9NPTjjnvThd06Z4WO4XR/2lAXVRLU6ssPYvCtDwJHo2mIM+p3yOpeYqr4ZXoj5nZyyve33ynx5ZOCxOKpQgWgWaD+7ImCeiePJb8ZyAk+r08ryXu8LiIl9KlJ6A4kl1KvahGzdMODk5dXaWZtvraDKGQrNqs8M8Zj88evzRYxZ+IUnV/JAiaj3/HUcooByiA8983mSgxUnwBFSlAGjWp3PetaTgSXW65xwt755KeALKxZN+/0LT/YtmGhF5QipORf9dF0DFqRxuPeFU1O68mgTmaAQmxenVKc4bitOStYmnNDn+Xi5NUYWk5c037SeV48ePd6SAUqE/SvS7u2NtesA9AQ1TB9W3C5NzcIRWffK677WzZEaB0wrUuv3wNA1vJ6gfoSF6NAfSmWfhqViF9j3RxPOoQCUqTkBZa91z6abX0XTPy9KTJ0pts3dTPTxrDlltcnJq63HNqsxjiZnZ2NjwnwF0l9/l9OnT7zDtt5n08sxDlPq0telgc5uXJ6A14SN5T6ry9K9cUrsrDwdo9ruOTzzn5ijROUDLs26d5dmeoM7p9alN64rwfJb6PGRfJKy/UHhadUZ5XgVnDaNm6axWz+Kcj1b3bdNHkGsWY5tA3Tn2qEwh1e8fTe9nKJ5296RA8SStZ4CSaPko0BGgcPIOoEAfngnPWTiffhrQhYWVaT9A5bm2xkhaixO08zwLT9Krz7y7nyNORf8SDlwT/d56zgw9qU/ntE+J8r5+Q/V6zSFrdcNc72nCGZQfDeKmOzvR96xRCYqoPGMP7XsqkJ6vIT/4dP6VBOXl8PwysOz4pcaTsIFOzqnn19bMsyZ8ed4mzirP8tS38rzmOZ6vmCeci4t4GijLJ55wNp6cnfX8A0s4wQxONINTmCQxS/LrU6e+Jvop4VftOSlHKaDybEB1hCoGOvAkF143XqD5+E7H54MPChTP2T/1VPCcWztwxcAz+33oSWK+X6HTUwcwns8eds/kxJPDk+Ge781dEN2emoNmR1OcTCGdnL0pVJinTPLDDKbu2ic9rSCq3T5A59OzW+svAjRJH4udqTz93XCV54OKea7CGaC5L03Jc7o8dYiuKXB69vE0Ttb58lTkqdDvCxSoe2Z9rq7aS/H1HiKfCztnahIsTVMxTSVbHU0diuK0Lg/Jzz/89XN+qDgtqJhK1M7RBG1E8QQUzsaTeIUGqSanXgHiTjcieBro6tBzamXliDi93yfTs0BjBdV8v6PGe8/Tx5EeRatA4TTPhfRU8FxVqjzD87pbw7M48dTkrV4vTjSdM0rTMJVPPvmE7xF+PkxDVAcppyigLPeAsohWhdbSFJ4ETzSvi69e0LfRVX/hKc5D8jwyreDJQDJPZR/P2pZ8Mz3TPQG9wuvz3vA8lJ76YsxZQB+UJ6BouqeSnjWKSlORJpymadVJq9PmlKZbvv++vvViqooXqYYToLu7u0wlW+3xLFAKdHiI8rGLQHnth5hpedrC1POU6NIinCucn0eOhOdcgmJ0FE93w/PqgWeNd1uWvN+nxzyvnHl41Tw5yZ8KT6aRZlHdimrrdE6vTh2d6+KUp830nZ2NjT1rdMdE8q1hAhVSqpQapUR1hnqJMpUAnQe0RPGspr9OOe86Xq4ITjJyUIKni1555dI99+gfdIpuN0+V6KTCAtr3tIGU5+fQM54theeBZ/E0zhU84cQz2l1/D5ubW9bvt+Zs92tRc8fMo1OhOF9WOW2/qEdH2el72ehgpuZr/aSpV6mKVKCIJihTCc+2QhvS8ESU4Om5s++5ajHPe/C8JzyP4DlJpFGeZB/Peo2B7+bJ8ckfPmeeqs9DnSfdTnni+dTWFvd2xe/taPY55Ynmdz6KpLn9y0nr9GNweqdTmliW5JeR1/jB0pBykFKiGktZoQylbPmDBM+2Qn0mNZ4XpCcZeAJ6z9OA4nnEPKfxXPOMe/r9fejJa7TpCagGmjynw3PJPVcFOpueF4zgxLM+x0zNvGHGYP/xR+c8eZrq3OHgpDbBxDIc3xvEZZUkRVQl+ptEbW/yqcRMqilPxjpef3/lCenA09td/S5RfU9Qy1x4orm25iv6BJqdJ6J9Tvo9t3k8lcnJ8PT9U//irhSmeW4++OCI+vTTMw/P2pR6oyg4f/nl5Gk4vddPRW2CiWVgvt3kvbc71KxSB0VUoLuUqIN+56DNGgrosOVvVfpH6KY8ly9D9MoIFdr35A5PskAxijfA6fdhu9e2pMDZeS70PeP4JBye5mnNHp5gRrOj2ePM6nwTzSpONEvyq7e/qrhqkYaozyUGvYGuJ2itTYCGaa2hCRqcrecqnBnjxDP2JR9I6Skd7vDpqQK1+ux/sClleSrlCeekOPGcmtINDE73dE48z5XndUrO9hrtTXUy17M6d4/v/CZODs6epiDJx2MpVCMtUUCp0Nrs/erZgo55VoH2PTfD85Errd0HntTnmGc+EmHHjPr8c0/Wz7nyhDM8Z2O6y3NUntXuwZmzqOPcjmZXr0ero5mYb0P5LTlBXtIPJ058e+JbYqpuWqJ2itptSaI+lHwPrZZHk+831FB6DNDcmUbhKdBlOJVhvy+4KCs9GfeUWvW7SKvf0/OKfBrSei7iuWSegPY98+Zeq2d+TDQfW7w0bRTtahI5Zx6c0gzMj0NyPCeA9VIt0QDdE6imEhX6Y7s24UnMsx3yZOhJfS4v4wmme06Zp8LZyYh3TzhJN97LszePSHlSnmQy9qXyzAIl0e/dc6XbO8+qTia7Nzucu8eP7WzsBWd0epZmSn7QT5pSpyXqoLou7ewcrykfWxOgSh6heHrkqQptBtKWOLee4vxUzNNFy9NAtS0RPPNFETgRxXO4z1OfXI6IOOv8vNc8p6zfGUctp1+O6mrkF/fs9jg79d4RmuLk6OTstOrM4kQTy4T8Rvmswi+6Kq6UKaSIRs9ziO6qQv3uCWiUKKBgkvJkq6sTdATo1hacluh4aeJJ1O6kzk+e2HkuBvSc9CzO8iT+YRyeAp3sed7jnjXe4TRP35ac0z3vK08mu/U61an7eludVpv0OZop+cYbb5xB9FMlXaNOrUip0er5DS6fDhp7fZ2hMeKVsS0UUObRVuyfQ88EhVOp+nRQMQmrPGu+48k4Sk/W+fBUphcUrfNTjKP0XE5PacLZebb3ongEUntnnZ2aRFmcXpsUJpJQmqV+iKSqFSqd/7GJVs/vecufzDMU0bblUxRPJbZ6eZItQEd4EvMk6WntbpGmD/jynFDDYzrYP/MLNgn3I73MQL+7J6CHD0/JE03z/N7q06aRQnk2ntntD7knnFwyxXns+M7eXnC+FcUZtZmYw5QqpCZK2yOaoBtqeUD3ucuPP72L8vSoQMmmg8IZ4QAtz6bhxXmA5RNPso/nOa3nxeFJjsB5aNxT/zcIPOF0zxpGKgv/4E2azhmjaGfnt71Tp8RJr+NJq1ujW4//ZYpUogkai6gdoYOWp1e6nr89PUmsoOm5tbmMqHM+Uhv9yk8/0fDmqVh9eseD5A1/lOd1zknqVZvwBLRb57V5Md7dU6CP2D4vz5F7Zr/HKl/lCefjeNoTpVzjk/PL0vygNP8WKaKAMpfoeSo0QHPID56NkP7OVDu9eT6Fp61MeOYBKs+Vn1ZW0pPg6Q1vnsz48CzO1lOayoHuurmgrBw6vFj1ieeyPBXWJTj38/Rbpj3ujMMzJzu9Hpzq9PL6m0XaL1Fa/s28KbUzXhkf8p2nwn8I69P0bBsezzw/9c2fKIfnAUDLkww87SWcrE8K2TwnO8+Vw1OLi7l+2nVzH88c7rQ7n2Rat/tnG+JUeXJlF6dXJwP7m3+gWSepSrRAqVC7y7cVWjfPdibJk6Snpumnn6rZt8KTyNNIw5MsIDo9nZ4a8XAq3UecZ+TtqPX06S5PG+5gXjOtWIGW5yN46n9MBGfnyXvIdTPKVel+v7XT7b3FMzk/oDgh+reiAWpnqK6e2fLyHIDiSQJUCc8LthR5BugqmFGif+jJQ8ysTx/v456AEve0aWSeKtDJ8lyi35fcc3mz+w8AaZn321F6HqzyxNM57Zq51+M8cYKhDs+/EmXTtxKNCgWUh00BmqLhWVtoePI3PeYZdyTCDan6ncjTJzyeXDnbd0T+1FPpPJNTnlOadjHev+f4fGoLT5Z5vhBb5Ymnko+VknM7ZpE4PypOrUk2h7D596If9EFjaxq7yh8kHWiNpMfk6Tv9p/Lc2twKz1U47X+gBuhP6emg6WkFiqeL4nl2eQZocDLd3RPOe81zpRtHj6TnU+J0T0Wc4568ueCbp3G+ad3eNDtLUizu/yoaS/T8tx9Hy+vmWU/veNbUzqR81OQFSvBUVJ8qUDyjPuuZiDybI/SIRpK/FuZPmCR6ceNZ4yjne3V7lSefFofn0+7J0/nlVW2fAq3LO+Mol0+uRrxCx72dD9q9PDey23PtzEH03yoUUDtDGfIfbfxWHV9vOSjydFDOT0999CFQLaE/5KVTnHFF+hlNf0KvRyIkDlA84ST7e1KgLir8bHf3nKI89SoD25Jd3uFsPPP4rOFubyk5J6P9TT2Oz02Js5NJ9MZ/9GQsRYW+HaB7HKF+ky/Q1rMBfazxVMpToGOekAJaB2h+NefQMzh7nrKv41Pxh/O8+YnnZeF5VXnyrC6nuz+Uh1PlyaqkJ3Q+i3JTYrJTnf89ttyfoEL9CGVryg9A8AS0u3dWw9Pz8Zhp4Alod+G07OMpJPckQ08w4aSKWT/hxLN7Ov+6ec6aJ+vnFm/V4WnLUk73vGrKc91eBPGPMqM8k1PNDsf/Ahprkx2hHWh2fH0Ako/r6wR1T6KGH9Hwm3WC5sL0O3Pn8jpyFERx5JX3I++8H6UQSSjkEUoSCxZKSpFQSpKlBRt7pUTJRkoWkj1KsmLhv7C3dM7MHOdeX6+F1/RjQ6mPmTlz586d77vg+aZ4SpCgR4r4gCae3Why4kyezJ/Eie5SfqzsZPonLHmiCJ7U8gRQFUtxci+eN3WXaS26//ICj0S/BydVCREPoNQkptBrOni+osiTpzXJJZMzqG+OzROWPN9tWQt9zzNnyzP1KDY24Hge/kkzTrVDpEblnrAaZjhJ/yTQmATbN5H1pw9H5rl7+V68IFT2jEEQaVFJO73zd9B0DsXZM0Se56SqQt87hYKnylCXTFk05fDNegJtmnak2TeVsyciPWLbjm8PzwVQ4Ez/7LtLmraZanmvXl3OJl+BHJnnRPNcDFO80z357q14MtoZ7oj2TJ5Zx/+O3NmrfIk8TvJIoZzEYQrN4VBm0CFP9+3Mc9i0c095m3hunU1TU5nJkTydP82TpnIef7l4YjR5P8yj3sA5vXgy3hPoavNcvjteaqV7xkEzK89InlV4Wtl/Uw5lXa8UynMnhuyl8eYpjf+KJw5J7tMnUCu8eMKIs+cZijTkSaSEqUFvXcaJJ9yTPGPy8yCGojlIy4ZMno5infQEyRH3LZBn1Uqvo1ZqkicLTxVKvxEoT55ModUZwUz4za4ITZ4q6gW079op4odNes/Z1ZgyBpUZ7Xod2/OM5amJk28MAyfCnZY8kT7lnuQZg94TYcUTw/7cMZHqviZ4svis7IkLo+qCCCeSp3H+RpHnwTM7I3BQAg2eAgpBWlO3SUqgBgoTz2HTrmia51aA4RxTpk/xBM0hT8hRdkPknztbnrs6nnXaHNfyjAUWKUfHkyfFPVvIly5Ri5Q8fx9LR3xpPBsjBFoOap7wUPWVm+mb4jk2M6h6TNG0syLtrxN8O7bIkGdTBP1i8dRtXEgRfuidNTq/NEY/Fe72z018JiP/hHsmz2XY4qlwV/YEzhSjp5k8n1CLdMr87Q4KnqXx7C3jkWJk0D0O+Ng1QpzzzTOBiuddXXQOFancM4w8F4KmVoYR6GjzlHu2x/cK+Hh6VDw37Kp3xfnIkCacXKKGzX5Uo3RPvsuUuF8Dz3LP6Hi6kP8jkqQilDxfx7NEAhXPAOozp+dBSVSDtXDP4rkl7KSAkqdeyi3NhUzJ87p59jusNFy3tOe5g3KUPPnuiDwZ7S3PcE+s+iJOqFH6J7SoESOei+yef8JDo6qvzh1rpvfck6GanjwZ8POjZkqe3mKtCtQ9EU2JmCdp8pFHAP2a57R+B6BmP81TOMHzMHnq3TsfvZsn5WgCjIsSV1b2VCckj0Y8aBLnIHn+CUlyzfQCr76S5+as6XNfU0o8LLcNTBZPCTyJHjr07Sb9toz2BLq043mdPO2g7UMEBLx5AifNPMM9e//sebJYKnWPvtKdOyo91fKU/REHZQaVxB95HXtcjjnguZ/tWzxh5pmTdklzyHMd322LJ3GK57f2hgTPOm5KjkiTcoTTZn4Nm++O4sH7ePvnDPLcznAvnuqEZCmvc7tx/hGeF74U9Zi647PE8wQaW7C0XCg2CGodI1c0Cah5wkFhjve2/iTPukFKoOTJI/wIn45cLjl7njmNcsk8T4LnBvFcVZ/HrbVVaCXHim505pU+VXyyEZLZk7XSHyg9+3MnJSmK+gr4m+RJoPViVjsuv3yxAjQnj6epxdSM0jdzoLuKJyweGme806YuDQF/CJ72Tt8esThtLjuAU+8MO561Hog4eToSzyVW91Ij8PwI96yTEdtKfwCngdYxPmrQ4nk+/fNUzxPmD4AET7eYxBONNM2Byj/VYVpYgsQX7ZE/v+Y5s+GZ5TySp8Qde0PMk4+Kc5HiRW6f1Vb+XIPcps/zwfPyHTZC0j1ZK/1BnM6gly6xZGoC3jxBlDzDxLONd1rDc1d/QjJPRjsvPUKQuCi14almiO82r/J0VGsZQtxVLi1itVTueXbIkzgl7xnu4BldZLrnHxR3S3wekhTw4sktguIp/zRPAO14YrNA8mS479o14LmQPGF550Eb8MxOHXhqKZj2XCTPFcVzk3nCxJPqDpzmKXUnzzxq6qTZA/gDQPO2sxS+4XlK8d74pxdbpsCb56yGJ9/J7WdTOaeY1FDOgj5ukHqetOJJPfIS1dJ3FvMrrognw108Xc3PN89T5nkv0meE+x/Pnn1ND4WvM6d5wsxTgtT7px8bkyaBbtgVPNUPaS6QuBDUPK+PIU8DJU6f3s8Ez53xULvkCAaasRGMOE+Ee4onFxg3PJU+gTOLT9TyfzTcDbRq+hu3PjGBiicFifHe8QyitetqbKtHFe8HD9I/Ge5bYkjE6XPd1sB5RgU9W6APB/Ge6fNhrbVRvKO3VDw3biqeqpbqq5n0z+S5RjyjMx9rF1R8Itz/Jk/eHd+5dq3lmRttB/4JG9af3+O5rXhC24vnVFoU9OJJnF4TpIfFgbPKT/OENTzHwwLnT3iyWpK6/2nD5TEVnjyzJwKg5HnMPLsCtHD2+XOWee4iT8xt7e94zg6elCQGO92TPGWNvpe6L009Um8pcDJ5yj8XgOfFnucU8VT6PE+cVcxHH1np84+aFB4B//ZHPOuEZPc0T7hn6JH9c9cOdkSAUzx5Hwei4LSUOMPM07M2xfMMbKF4slUX7kn/zGYd1WjAc0nwPNDz5PRsDNj8pfQ5ovr0TyKBfpfnfPM0TjeYiifkaFO8M95Ffd9vnltpgRMxXzy/1veYuePZiHJEE88dyXMDP+NePPGvJk/Je/SSzTN7dR3PP18tuQ2aAf84K/pv8JzveHe4w3L7BfVIPLVYAHtEclGYRph03sRnfBjyLU8vZhiDLKD6k0BjcCmyJ3nCDpInrt5XxWpfbU4FzxkzkufynidfaZYc5bXRiD9v4nkDPCVIqe+ncsO69aiv58lTehSbWQ4WTubP/VwcFPnTNx4BFDwrgYqnm0viebXlKXnfKJ6rcNfB4+b6nmemT+bPY9/kKTn608YmUyTQxxb48E/zhP2Y56yWJ/3zpBzUPIOmClAK/IhBsw5FfuTPinbx3G+esfFTG9a+yfNA8HT5KZ5/RY5cMYHnp695VryTph10yNN6dCh4ajMgzokq6AV0avG8/nOeLOezmu95Tme8J85Jfbx/4bnZPNX7ZOvzb/N88COeKudB0/0Q8FzQ67t44hUSedbeNQV80JTAm6fjnTyn/gpP2KQ+3qVHHc97cXoXz2f/D08fj9I7xVOCJD1qeSreRTN5ZgXKjl2bP0f1W9YifwZP3XVEtw7/YfFZfMb7XfH05bv90zwv8/Qu//zD8m6BJ89L5qnzZuA0UPEcb57Eyd2Ayp+bNszLzapIoM6fvH1faP+EDw7ifZQ/iqD6s7Yqws01y3BwQ+q7eapdZ54uPxnv/5jn5eJ5DCb37PrJq80TNhEBf2LOnNy9Bp4Amotq8VKI8V7pM2CKZ1rEO1l23WQYtB0/wRM0hzw3xWUHA77nOUM8a1Kx4/l38+cjneDRsTPPzS1Pd+sanpPOQhW8CxRrVaOhnP6JgAeI4ikTT7STg6emFZs1v9cfqvpUOb9tf9we5ahN8lyVWy46noN4V730L3i+bHiej/ypcsnVknm24U6eindY8QQA6fs2XhcXUtdL1/FluBHd9Gd6JyQp4t08tyVPjn7S/+uuGNYNM/Q8Nzf584706Plf5sl4N0/i7HnSzHMSeJZ7mmfoUbnnfvO0HIFm1kvFU18r1vDn1JoFy2q+4bnDo7QAap7jzHPonznLoHoJkyF/M97JUw1Qu2fPczK6n/bPVKMFxAkrnOS5Yy3r+dicroB3uFOPUH+OeWh917QNfuFPyDv1yLNLOfvJeF/EYTDx5IJ084zZOvHUdQfP7zpv/tX8+aR4xoWx5Qjm247mc7Jn2f0MdRfPvO5Q9txhnAuL57mM9rksl9CeN0+3Q6JeEs/THU/gFE+0PzN/kufixeYZw3UV79n/VD/EPP84Utef4pn++XU3OXm6/ORy1cS5Su5pnjweuR3i7Gk9us552sE4GHlmwC+t4SW97Cie0yPe94lnrAmKaZt62pE8s2DSdZyvO/5+PX+k47mmk/eM9vLP9Qtg5In2mXji7Vqp0f6I94bn7KrmxRM3meY509OK15Mnhz9n55rfnuehluf6IU8n0BgG0yxY9j//fMB7puHSgOew+tSHkc7CQLN4wvCpBPOkeoBB65/V/bS+4yJzah/vqUexSaDnua3nOf0HPAEUT2UOuADlI8M4wOfs0l9xUKRP8ES/7tPT4rkn9egbPOWexXMi9T3FqHjm0l/xxJpJGMt58kQQAxRg8mdkx1P6rs0MOQ4mnujYgSeAbhryRP48yuFkvdyM2flqgErgb0vg/zhPj9Wi/PzEcknzNh3PweEdu/2JU4dN1PK9f1rdyROW7nk1O588HWE0UfOKcUgKnvqqzNwar4s16cXzw4ddqB7Ec07V8+tzh2qstamXm808g4c//959HJ/Hvsz7jpSj89VNXt7KUVN80gJno+5aSv3hCnmuFU/g3AmeROq7o+tEBqrmqfqzfXtknjuD54fiCaDU9xNzyBMGnnq7OeQJoB3Pv3HhwffbKUfgec3yXjzV+5S6yz3DP4Vz+nTx/BA811LdzbMskydw8Tew63mWINVT2OCZe9ZC4U8yg5aD4ukmeJ7QIhbxzHhnAsXTzQTqiv6Pz4c43C1H9M9wT8vRElfz5nm25TlrunnO+7BhwDO/kVA43ZxHtdnyHGWe5E2e2gOYHVB46Gfazj22zjmM46xuZYYp1pjbXDpqDZUFY8I2U5dOV20Wl7psiGESM5TEsujKSI4TyRYrGVtXWf+xSkdINm2KksPh6ARnaZFYXDKbP9qart059f0+z/uc5z09jdv0UZfM5Y+P73P5/d73/T5XXRLwFKA1NSN4WoMXr2R/PdkmprEuoM7Tyqc8jiNPwZnLU3AaT09324pSiNDpE0ArKoKXQ/hSrHd3aUZmApr9/rwPTJO0IQVeAsYT+gyMgsgzsBJwE2opoPptMXhyAn2Y3xrKp4Y6MY39+2AM/XZbeS7DaRM4ffo0nngaazw939mNMMsLT06fjhPP3lWcweEo4BnsNc1YBh2UPzmX5yFqXOc87dti8AyMbcyb4agcnhOUJ4HCjdYmJk14TqBjL1Cmuz890sO788x5tEmaylPlqTxJU3lyJx/OmplHcX63lPESEKLK89jAmxY4nadZUd+P/wsAOpKnGCebtU2wzVAavPPUrw1pzaA8MYLakXPM36clT0t355mV7llv0hpO4TlXeWr1vAE0GeA5zZ+9Y1ACT/32iDyZ8MIT9MhTIpenvCt6P79HuDyb53TybBeetEkXnlZArcOjxWvCo4LOD4/0ud/GjZk8l8r73qMfjlyevtd4fA5P4BSi7MTGk0DtybvxdMtfrZ+5nx9punNm9Xy/xvo7cZLnOoY6K3rCuxcLeVqH/1Yc6/wOdAyBmjyD7q48OXxmvRkyEc85bJW53dQJT117BpzZV0vOE+/H6QYPwylP4oxnWJ/+8bs2eLdn0BPnNO1HgTXYnLk1NRmgxKnmDG6anJmYnocrGCuodCSe4ccOqDuJtEg3suETTzpsWiJP4OTeWOUJmgh7rgmaiECeynMaqmf2mwyCk2GfImR45q7Z5YWdhg70l5YgwjwJlDzRkDThzUnVec4MbFTBk0A/h/ESRyYdQcXoYuy+57KzJod553mxf9mBZHeekKfyDOQJnjosHWfyBE/GNTosmdcF7RUnZfE8xnn6JlM7wGs/wr+DSYsKVZ5upYoIeDaBJ4I8mfBRALWE95EJPPlBbHBGGsMKSnnq2/MiT6Z78GmHvOdt3Z1fwuooT5zizMCrTzzX9B1INyhOls8Mz0XyYp0+2yQfDp8aufr0hPf+jq+7pYKC57Won+RZSGvFGzThyRPPjIu0IUnKR6NR5akVVLzB6GXFL2K1I5HnS2MmUB7dKU8dllyeLJ9GU3iG75HHa5CnPzeibvS9JfCUmzrlqR/K6LQkNM2glpHLk+VTrGnZ3rFo0nm6NW15IXmaQJuKMkCnRMDTbdLF55dWgLQP+VYec/r3xWMClEcjae4mT/LkWnOfPfnlO9LdecJNlSGPjbS5Z/bLqDkteRJnLk/0o7/kGRzhyXOS8ZR8XxSYAU5TL0AEJiaY13WsE6IooNaSKNAJ7v0JZ1paVbKCssX7IZ48xwSnVk/I81OXp/OcgMi8RGvpbvsRRJ3A6TwpT47yeKzpPPVsJBdwnu45PBkZnmqVriO9eFEvyuLZTqDzAp5NjFyeArSNvt63bNoOF2pr8WPpz/CEWlfqm95ydIc3A3hyFa9+p02cytNxgqcGyqdku/OU2fMaCbpRa/1UN2o6WZEnhnlfqW9+VuP8EbzwFKIMKBShPKeRpyQ8A/YFyHfnGa8O34moQRiA6lYEmvijglqLH4vPPOwixE7ur/Dojs37xMld0WrMIMZLrk8UKlRP29Cl7WiebuD0z96hTvIUo0rleT8D1VMLaJjnsceO9LMynIfIcp9duy69dDfWgOwmz2sz+Y4fFtAOCQWa2XOGCppgS+qhISAFSqDgKabz1pLGIOOJU78ttlkJNyHAKeZg0KdbrfnRSCZPCNT2H7F8qj7deh6jPEKmpUVZ24946snhiRjJ81DBuZLq3FVSUVGy+8RLSxbt3g23MQIlUfKcwwDNdcrTdqFE2JISibMTs3oAVCvoh2xJ4pMe3Cub7+fY4OQ9sp3cxYqFm6LdXHF0niP2ndGXFkChTvIkTi7ryebJkhjmKecjhvG0IyeAwmuAOCOYM4oOn17B/1YsFmPKd4V5mj57m6wjgScCPAOgbeGWNLjHHyT93xfLnOS9tyPb9aRpviFZxmCZfM/yuLB1kXQNIU/q012CFiHEtC57+xHD9xc7Tx9AyRMxaf29sZkD9fVYOTJ8dMnuEtUnQo+cCAcaVNA4amgEQHVoIlD2eM5M3uPly0Pt8VDo/6tO4tTebtku7p/u/Snd3czn/QtYy3YEaQY8kYnu/Kk4GaPzJLVx2Mc32XkGQGkBSp7HlPR/uGzL0P6l2IWXilVAoCpPZvzXzpM0A6DG0zI+AZ4AqhlvQOVd5RYH+n+6AQYfdWRGT7e1CRt/jlzewWxfVwNjb+J0kzXwNONk16fidJ56PlJ1Up/k6R0JgNXQn+lesndTW/fQT91bNq5ZvfT88kWx38mzK5tnh4TxJNAIwzNeLH8BlFP9x+SJjNdjJzP+f0v5J6S120dx8pGMFc82kadvmzB9+vYjGtMSJz2CZBI0ngjf1MMoyeVp6W75TqBZphck3XdM7aW92+NdX+z44Yvvk/OXtrakjorF7phzVBd44ncZmQrbHWgvSmjcBIogTxdoW6aEyiaUzMtMDvTAB09eeqJ4Wi+SbM84qXq2B8a01e4BqJ/Amq830t3y3eUpKIN8J0vn6e0ohydZWo/f1RuNPNRzaUljY+zEU65IrnmuteXMxq7PUukjY13UKHCCamPjZ+TZoB2pVxOeQKMS7PFQKIdQAtWM12NnaApVoEb1gD3o0YvkFdrnXZ7E6dUTEfJUZIhHpfn4l0uYpc0luqlHcZ4InoE2QdRxkufxHJfATnn6HahG7cI33liz4qK8Ww8tyOsbt77xlpbW1WsG0un61jO7YuDJ0JkeyqyuLhrf0dvR2wukcUREI5pMJBIzZ0rGt8kUakC1hJpZw4HXUNIMJiUd5F+hOoHzoazeHrbyr3Z9wsUfQctP5Zm7CCXmu6BVn7682HnK9k32I/LMNv3N6ztj4eDQp7OX5OXL07pVJf3L3tgz+PyyFUNrzowhhGchiJ529sLZs9Pp1Pm9GnEJ4wmgyHgABU8ckyBQTKHoSZhC5dxJoNqUYH984OJk7Qxa+/z5rs7cbEcITgRxjj8KPMGSYTzDBiymT1/UE/CUfYUZnug9EOhBU6dOPm+yhjst3nvZ0G/d0/Mm5x+bB4H2HfNZ9/593Tu/+KZ7INUFnl+KPhuLJuDTotSEs2evHT5/SiQeb4prKE7wFKAzZQiVjOe6s8+pUHEPMltVJXpgnUhy3XDaID/bViPk4KRJOlciYKmM7JjogKd3h+P0k6bz5NlIgur02snthmF9kifkyZhKoKTat7L/4f7GWP5k1IIb8/MLCsaV7Ozu7t1d0tW9Jl0Y+xI8QXTOmSuWfdgTLy+cm9xej3VOw6mL56cWTojH++OR5KwJ4EmFJmcGCkUQqK7eRE8yMzsM9gcE1HIdOOkfYDg3ySDPbHfLZJR04HSeRU0MWs6DJ35nc8/maUClszvO7F6kPAlTeDJAE/A2b87DT8G4a/s/7L2ndtzU885DKcinYg++p7Dw8kOOuTJZPzw39lHsS1TQ9gmvpJJzp+NJ6qkNkdRw+oEtWx54IIXcnxjvT6SWplOphUkGMp4hJZRNaTuvmuTgudGBwtDuPxJlrvMCOaida23whGfyxY6T8owiIlMiEXXwF5yHqT4pT0t2CnSEh39sEQMorb0bT9+LIMuLWSrJU+M8rjnsW7Jk+pRZm/aWjJt87Gb8GnnmM+mfaq5df/yh66fNGji//MuPuuZMWHjx9ui8wisqsa/rkhtOKIr09w/1XLCu6bTU6nQyNTywZXh49epU6rpkwoiGpyYC3RMCiqSXRv8fUt06UaueMjEpGU43pBWe0YAnR/mAp+OENt1xPuCpS2WEZyznaGQ8idNW8RlPA1o8tW/i7Iff3/N5vK+4uPgm/hKBCtMCnUxrr0mkIztPn3XL4NpPe65ZXLq4EhdZWAd/ake8/9ya9nntDYeln0sPpNt6klenBlqeG154dhJTE3myyztQUahtRdmgOf8vNcpdUrpMiheeeirSZPfBkzgTOKkZTsS5kXNPI1DlyVlJebpfcjbPGIM8JXJ4CkxbVk6excXOs/Tnwf37B5N9ZcurqqqKq5woi+it+KdXvlj+ykMPLN3y6tD2hzvkQ1nENdfecRv80284Yfqp7XNOOvONjbckpsTjp0V6Xq3HMSAZtRpKnm02hvLyjkBtaw/6vG3e/Jfa1NKJMT4XJ7M9gbBsz14TiXzXAuo4jaf7eQMlg83ILpINJ3j6KO88LaqKp07fv2/f4OflVXXLlz9SVlYlQEk0n5E3ruD4lZM6BgbqWx76vndexb1X4uOPBYsrKpDzlfhUFvv1+SJOvG1TMl7UVHRYdSSxae1zLdch4RlaQ3Wu1+v6wcFPkfNcNAOJkiiRCtF/votPGxEbu3nVfazJ/lDWst2ohK+FJU/C9HQP69Obu/H0o1H4YGQ8iVJxKk+IEX8sK546d9/+/UP9fXWI5WVl+BvgOVWA8giVX3D8oc1NuHBa9s3O8gXytdeCBQsWL15ceUVlJb0wHsO79e3tRYme6ibc2cfjUy77ec07w9dFMzX0ahubVKF6X28SJVFLe4m/WWjqOGUhwsCnwdy5yRZvuns/WKo+vXgitHqSZ0OD0eRFua3c1IS3c7vqkzid58EhnlY/wa1MeVZt7h3au+OPXWVbyRNACdpynhplAZ0zf+nGy3aWV9xTWxsCWulWlu1zjyq6YB1OoHhDrGew/p0NqSQGpxEKNaDsSiyiGaIiUl1fPBpR4ewLd0mzVUvnWrkC+Rw4fWcczrqzvLVPjBhP4nSecmBWd/R5Vj6DJfqsno4zR59WPB2n8LTI6+3uboytLKsTngRaxZCUZyDjD10f27txRX/jtHtrm/UDTwNKhYo9E60subWLj+UTr7Q8+FwqAp4KFBFSqLZ53UARJkrdMfERitXDNkIrTFu2C3HKnPT+57IyzuTJXmQ4I9GJUeFp646atHrKrASgyrM9Z78EeY5+8enXntk8wQ3k8FPWt33fr7tXFtc921nHcKAkSqR5q5prr907OLTz8nubn3rq4PW1tUu+WvKiEFWNgqeagzYg8Jwu8nzLg8OJSJIROnoGt6HB3IT7OyeqSAlU4+3sEGFKnps2WwJxAqevQggv1laeEyNBtp/m6U6agT5D7UiIupt3QDSnudtNiDWiAGceeFKKjOahHZ98CXl2dj77rABlCFHgJNH8bauaT/ms7YtFzX0Ft956fPP6u+9esoRABefi0srKsy667QYG3j/H/ULTEW0b3xmO9htQIZoZm2xuokSdqGyLtY36ZDcipKEjKE2lqX1dTKtklYzXTj8WUZvEyWzP5tnBre9ePn1lse3QH70dGVHvRc6zru4RBHj+sqOxcWXZs+QZBqpE0ZjAc9WtzaW1BTfmcSBd9dprd98NgQIokSrPx267DUjFD2Nux/jkng3pSMSB9jACoKZQSFTdQXWZMYCKSgmVSvVQWZIlYCpNijOD8wGrneFWpNmuOBlxwNTZ0/Up155+Mc/PicmTAZ5C1HlOmjQqz4w+64iTPIs/27ej8crNdZ3PdCpR8mQxqApC+jxEyt50Y8EqwAVQleiLLy6eAY2WVp51FoooNUqBNoxP/tyaToBnJAmkOTU0U0R9sz57/WpjSqjEqoG/Upa2Rl/26PP+QyxTZS/P7DBOEyd7kU7yXMAImHY0Gs+rEJRP3C2dIDzDZvO+TgbdyPM9uFhisH5m8ywoyCfP5Y/UgWf5d998tnvz1s5nyNMlGtboVP5cz9Z0Y/62/FUIAr1bgM6YwawHUOQ8JYovFjoO69mzof6Bnv7+mTzLu0TtciS4HRGJOlGolEgJVbF6tFKXgAlvZMl0q5y6ynC2j53B3CnqdH2eFrFrOuHJTR3SjjzdJUY+hzOehnM0nuOM51Z2cwDt695R29dXBpwEygBQhPO86aabtJBCqNdDo9u2bTOFsooSaKUCvYhAwbO6bc/qlpb0wtRwaxoKRfjYFC6iHEWVqCIlU4MaCkXZ0kKYsu9dacpGCdkFiyuQNj+ya7aTZSS8q9hwqj6lF/nw6dO843SedjZi6FO4LJz5Ac+t4Ll1147G5VsfqROeLtEsoC+DqOKkSIETQB9/7UlL+RkLFt9cKkCVZ0NN0d6fVzw8e6AVG4pbZuECj6FtPgxUk/5bmgIbUmVKqsJVg3+9VFmu2cgRiRM8thiirwvN0P2cLY3S4hle9u48cXKHPhvCvX0UeVrtJM5cnsJSr0Kcpyh0a2xHLSvnM3feyR8SJVDTKII8qVEivR5x++3bbt+27fHHHwfQN5fctYQpf3NpaSmBoitdeE7RfScnP0zEq68b3vDgg63plKe8F1G5YiZQme5VpGvJVKCSqkU9OIIkgjApTaWJXOeCI/Bs8w1c/rAoIseiiPNkYy+y6snNZsZzzjzHmX31aTd1o/PE2d1x5h/U2SlAtzZ9sfVOxDMvyB8J9OmnFajztKQnUPzcjhCgJAqNAmjpjFJKlEZiJ0WPvKCpuqljXdHVK1a/9EHr6pTyZJCnFlGTKBdvQ6IgCpEqUwpVQikqSfwyYVKbsmFTFsQFe5/Dt8fgyZgijUgiXuT6bHKeGaCwAhOcuTxdn/7USMsnaBpPhPCkDpHwZd8vAMoXJBSoS1SBvkycJCpBhRIocALom28KT8SCUgKVEnrcObTBwUdf4yMrNmx44436NLtSVBTqh095dUQ0yo8WVghSMGXqI4CVBPEbAu2HKKHMPcugzVeDDbC+EHKW79uLSijNah3ji/CjERzcSbOmYY49JfbdPDhrerqXsHx6M2KwI+nJXS6WiFIiX/XZ+ezyq3r7N9/5wlv4zYG6RF9fvvzRR1+WIM/z3rsePN8jTgOqRO+iQku1huocykG05qjD2gaGPx5au2HpmZ7z2W2JQKlRfFgDpAilCh1uYZAs/wzMryDPBeZ86eqckmyjLmBm45wYnNhDU7w+NZI1hr7maB7DH2uGeTLC6gy9ZeM8EQJTeCLqdnX/GOu88613X3jrLeEpRJWnEAVQRYofCvS990YqVIjeddeMGQvA8+ZKnZrIk071NUdEJ+7t/pOue4+tc47jOO5ObC7DDKWCWdVEb2swoRji0og1mLGJOyPutyVMmMvcajOXECpMxRJMZP4Yoi5xGeusc2uPw+l6th6t2skyTY9uc3l/fpd+n57V55zTCXHZy/f7+/2e5/k955n6zqt/N84Xp10EwVOgcV7SSRL6nty3SabrUbWsf/F2KLFMaIrTP4knXsc0TiaiRnEqcexEE86kZ+CsrbUnm/3kQOOFo608uaUjesIZ6vNiROWprl6Szqxd8vCTTy57j3hRgujcuSIV6OOPCxRP8pHP2Wcv5y1R80R02uTp0ysqKtTyPJlCpBPG8T31568aXPEonv1DFz4D6DFDop6UKlUgVW6PAVJh48dnhAdrPkavq9X9c3hs00KY1uPpJB1i4ilNe0oxSXpKVDs+lZO9Z9izZNc1uW9InuJUgqcPlsR7UoYP/L459ce8J5/EcpmB8vYlOhfO1wG9g8hToUAR9aDvAoqnROVJpk+vRnRSDd9u6WuUW7srO3579pnLOVRiBE2KhlFUoq5INZBOpfymelQVIz8pypf5lZ/8BWoTSzYsoMkREZxo2o4v4ovTzoCQyFlJQnXqzmxA0SR2MzHtzpb5M+NXT9Pv0sTzkDB0EjDlyVQUPdEM9fnwN+l015KHGT7h9En0vDwDKKQ+cBKBfrR8+fJ33333rrvevStWqDyp0AZ5ziClY/g225fGlfyy9FF5KipRxxlAo+hFbmbiYbLqfEhZRcGoipSiD4WpKZ1GJzcSNIs4uSrMyElcq+MpyuAZ7+UInm47cvTk/K0XxZMNtPKMN3CFe7P5yDOcqnO7GHgP95z7RXvm2CVfXCpPicam954fJDxfDyUaPZfrJVCSbHlAJUomTQK0rm5B6YQJN3c8/8y/vzQ6UDitQt1dIGr6KVOo0YsuEilBVZlqAVK5nlxEaU4hlGYYOU/zrX64L023hj9UmOYJZpFnvDumVpres4p7iTkVIk84ydaecJIdxSlPjZ7GuSf1+WcmvdytlRBd5lIMiqeCZ8xHd8QSDZ4E0OEl6kTlyfeFVo0bNeWhW/86KxRoqFAiUIJorFH1/fXf0c/cVxdikliCKc0wbupGGI2cQ9uT4AzjZmj02OoKlPZAHjThxFM3Eytw4um+J5nEL/YVp/eUZgQFU5EnmHBGzyVtgwO3vbcMvoed5xNP8MNA8YwjqECj59Oy/JnXchI9rUQToJMAreN8002/vPbMo/9czjxBOoZA3V0LVqMUKaYh6FkkKcxYmrE2/X1FtqFmvp3pdJzyTGra5m6vOXZMKYmceN6Jpzjx5LuWDvKecEpTmHCGxac8GT3d1B49P1zbPdg/dxl+Dz/8HphwBlFA5QkoeestGl6R5tO8EJUpniZ617AKJb5Ca+ouZBS9ufX6+1575qGjbAPe/FWxRE2UO5JvhJQ3ciNGloQbCI+/XLfBBE6HSaKmLTkDZmx14pvdcxIej1VaX1pfHzwBjZ6ABk7f74rH9He+eUwSOQ/cZkl6sHvlB8vA85peNOFJzFOgnpQ31cln+fJEzwdPIlBF8/yMOtb2L407eP7tr9463zaMkbAt/JQIyh3eTDGQJeuUP4hNHjGPEedZ/F3x6+gCJpyahZQROa3Zw61wir7gsEoR5/jxqk92FQTPOHzy3JMdhp1XQhNOeSpJz57VX7gjo9juBkrDm+ebbw6JPv344/fc8fTTP3+k/PyzPJdLM46hCVBVqPOsn3Bz5d+P/ttoO/AcqXYxh7tniUgBhVSoFi/JW5bC1IMjeJQmnPHpueJ0CZxo2hrJ30Hs98nb2Ok8Q6srPAszeIbyVH0q4TGb/gaZsLc7cKrZzXPPba5MD6Zmz7vULzmZfy59z0AlmvSMJYom5SlRTBWBEjh5jzzNc+FzQsn1t/5jnoiS3R3oCYBGUaIajLXIDwHzR8JUjkeTCFMJI2coTj+tK1ETTzJ0CzGg4lRCdTJ8Os6aqpqakzmXw9Gy+66lk5TwWB409wjVSWXK03OKMnjCieeiXHf+49l/TnzgC134mHjGA3OfxJMAGj0RfYtQn6pRcT5+zz0MogRQSAG1lieA6uCT2CAK6OmbV1zfUSZQ22Yf70f2NWqmI8csoyatroR9ncSKM3raqElmipJmD5x4AlrvOOVZU+QJqHnqbnc7JW+esTrlme/uzmX72hY9OHHJn+ywO3DJbUzzzvM9Yh3/liJNp3oPnrx9fIWaqM1KJHjW4Dl29FdL/7q+sazMkxpovN+TSDRGjR0daXFFmtFTmAqWxFp9ZE5bwxPTLJVnleYiYYpyPD8a0CTmqcUSmiRuYOBH8LTJSJ65nu6enp5Mz9rrFnX9se1xq1f/vuQL5xk6fmtPQnny3grUa/I20VigNQLdZcumh16cz/ky55msUIWz6ngqgCnik6tBJjGN08WmITJsxalW55C9xDwZOYc8S608nWeNSBuM86r4Jcn7DWkmD9u38uzJdbtke9v6er9f2Zlqm/1FaHhAN2zYkPBEUphSHcZZVKF4FoFOr+A/tGpBZWbTs1Mbm+GMd9ZYhRJfooeBetiQXfQ1SWGKc/gztvYKje48RVkWNPGUJMPmuJlEB+yBc4Hz9PGcspTm+IZZ4jRPOBOeOxqndTuWgFKfePbgme7s7+zry2e6U79f+sQnT3zyiW95PB3owoVwxtyjF6L0fBB1Mc84iEZQeU66cGbrG689e8v5eJbFO2tsWpqfFGUdpAAZHA0TTcNk4IQTT27IdJx2/ZIZPc5CTaNGS9LHT+xwEk9Z5+cj80S0eta5s4Ln1Txyz76Almg+cv0ePAEVp4JnW7anJy/TXCYj2+6Vty37hARPga6LoIiaaYx5WonaPO9Bq3V6pLlx1bevPXtMsyJTREnwJOG7GYlMR45ZKt6S7EYips3pNmwSPLEMMU6fGcK0+myQ57lOk2jtKc/k9tmLbTYKiZ5f9/Xk8rke1WhG79TVc59wnAGUjl+3zjytQiMnr9D00dM6ngyNoWMGvvtqy/Nvn7bRczbjebDreeYRX6KARtGjaOnhkHyCpdc0TyyDpzB3wRNMcZqnkvSk1xfIsdZhwlk1I2qapzgVdr3xbK0wGRF2gF08B0xinOZJfeZ6eroz6WyhJ5PrWTv73icBJX5Koj4BXbfQRZR3v/nm/SrP+++/35fn/0zzArUKbZg0enDp0l/vu4BGh5PQ83zrnXIooUo1iupFAI0BlrclUNqzcuMXyIuzsgzHZGU2RU0O120mGhuLs06afGbU4Em8Jpk1S5zm6UFf8TvlgyXv4vo88shterO5XI7KzHV9U2CiTy1avfgBD+qnpA1k3aWABlFnCqkSK9RGUS9qY2hcNtHxo757++/+/o0bG/FscqCIhnG0nHO/nLXc3UeiPEbJslVdEuMMnmp0KM3TzUKhNHUm3jrdWt1zkiRndTX1OW3WtKuuEiiazhNOPBk7Y2lupSnQbfpS6VwPnvmu3k5+Sbet/Xp7FkzDPel4D+rq835eiEZPlWic5Yt6nvhJaXpDQ/3hm1rWrtzYWt7MDkHvSc8TLj9qJd64l0sA9XAoRkvTNE5hDn0/904hovSebj7n7TLWWU6YsIBWZ2aPnD7J0bMaz+pZ3nOaPImvTvPce0RPOPHM5xk+ST6Vygx2d6c6Vy8+8LZLNcETPAMoniGIOk2BQhpNbVqylahVaHVFVf+vU9s/3XhCedMLgDaNjpNSq1Y6SuOhuw6RSs0ATXKo0ZMPyvWelUWDZok0lQl0eZjTXXXimZiJPKl5VlQ0oInoNEWc8rSxE08f0ySBE8+uQqGQT/cwdOaYkhhLU20f/7HHRCpUnE9Ez4XW8Aud5913yzOREVeiEbS6pm7sqvUrBja2tsx39elKlIV94wmXT7mg43xHWs5BDpyKQ9uaMyRihge/gbmzx+QtSz4EyuAJJhkzoZR5KDjWETDrqoQJpyJMceKpT9LzFTgD6N5JTv2w6pRnIZvPKpDiSZju+9q+/nBDXNSPUJ93380nFqje0iweQ+O6SaCTx4+a0Pzd+lv6O/o7zm9mS9sLM9mg0TrQMuU+7Qj5e6/GMsbQ3Q5VDBS/4KiXZdfkN0m74qQ6XX1an4+aGTypTECZgXxxljrQC+sUlac/aE96qtsVMEn0VORpp+hI0dip7YfbdA4MpLJ9fYW8NMUJaC7XudidXF4D6PCGv5u3cr/FFk5FojbLT67baVTrG5t++/y3gU9P5fVpU3NT+fHXv/zrpqnHdBw/df3tp1Ge1CjDKKLSQnSEWJ/bY2Hc901GS9IkTj2sJSzdXcIstMD6HE9Kk/r0lHoN87R2n42m51SvzwmeBmrVKU/VZ+H7K3qzzlNFurknk+m86gG2NyCK5w8O1ArUQG0MLT6Y/7loCK2urT218fPBzd/eyGZLPEc17f7381NbVv3S0dha1tjy26+nxFEUTzeO6hdHaLJYRs4QatN7Xus9RzWVlMQ5KFKaZuz1C9Xt3tOGTkWawRNMwl1BN8wm3lMLT5eiI03jlGdfX18vl5j7ehwmEevafS+ZOO/h96jQNcvwhFOgz5mnREcgtaOloiGUbXelrQOtA9/14/lC8ylT7ltx0Sotnc6vnHlT5eH3rZjiFk67AXr+obvycpGdXCW5m16qTLNMPgVKnDYHadCMjuGihkWtHj1t4amlEpjKtMh5w1M34CnQOXPcmZA5QdM8jfMI55nNFrLZfOfKtZlMiilJmKRt9dfH/bnk4WXL1iyjQL3nc3hK8xpSRBo8tz49YqCTq8efPvqcVd92NH+6sfWUFc++2MJddDNfcL/1cdee8tn1ja0HE5GS8nIkiwOlRZg7S1MDZ4kCJnH/vNDnvizjT+v0kKHinBFaXWtP82R3G5z0uzTnLOZDikZP4jmD53nbZFPZQiGXzmQy+UJbJ6A6Wsp1rly56JvZX/ywRvX5ww8b3n8fTwKlNIc8k0Uq0GJS50nkWTHp5Ppjnn/x8taOW25/e8V3HXD63zHPrih74429WpmTynTFXGFhWYSpICpJJT6QUJp8PKaVpvW5IkzeSVAcIydDJ9HIGTzFKU9Vpxs+0Xzkfz1tMnKe+TwTfC7D8WbvN12pXFq++VQh27Z68dwNa5jggyegrkSvWQhojJZNkZM3KT4jaqDTJ1dXtTx/69tTH1vx9vNbBkazIKyvdw+bXTBudMu3h5UfXFlGfJH+T+ICKXACeW0ozoBppWmSVfxMHg4BKVCb1iMniZzEcRI8Kc6oiWdM0hNN75lPp9P5VJoMdHZxsJTq7Orq6kwNZLsOOgLPZbT8kKdyzXMCTZKS/1vbxwK9y69Cp51Y/u3bDz27dOn6LS2N41j/uWC64Oa9Xv7rvsv30tXyckB3LkKMB5XEf4N0yLWhOBPLzViYidPFY+rH1IejIf/Gs6ZuUs0kMCsi51ae4rzhR3HKM2r6s3SmaZx4nncenqk8yZE8I2kqlS/0dg5ks6n2tQdNnPsD4+eaNRusPinQ0PG8RhhE+dhSNDkpAYrnrLqOr5Y++/b6LemyBWNoON76DdaWLrj28r8eeu2fwwDV27k5QSHqVxsyhSlKpcQnemrkHJp/+Pj6r1difYZYYfrzx3hyEizJiaY8fyzyZDeIYRonoESefcxGOedZKGgZ2p7tax/IFtrb1y4+4wNanfLUBO88AUVUmiJN1qiZKsWg2i2G6A2XTauo7b9l/dL1gwMlpeNP9HeC6GJyVemC0VPYK7/0tF3oeJZAI8cqkz43z5leMza63/HBh6rkRThbzL/GTe1VNREz4emP2c0zzO3BU6BDnlabRZ5o8sYTywwzkOoz29eVTdH17YVse2HR9g98sIE4znXvxxlJ9fkcmNLkFxtFEy1vc5JNSn4L8+SKsR26KbzjnJOpCnaJqkZRZU14wPpnb33tgsqdduFSRRlBrVjT/SnHSSInnvEY3SrTB8iYWJnxcEiONYGTyJHLMn5yD3PRUL8vjnM7Mc3itTyRZ06LTs4wkXQql8+nsr2FbCGf7f3yvLe47v7BOh88DZSEtg+celmBGqlNSm4/o5q+YiMjdf8YTjHyOxCoLi1pfnhp1cuvPX9KZRmgBE8fOYbsFPvczUE36Xkbip+FSFy3k6BpcUtNWRpnxEQzBMzqacpkOBU0r6Q4Q6Q5cnkmPDmTzNVNzoASmebycGbbqc9L7nj9vHvnsUX5gw/w5LXufXFGy+KFk5Wo0ywGDZvsOZT/tHXjp/XjdcOn33eLp4bSupJVL7990W5uBQRo5S4jxzU66/c4qztPLCOnHzb5JDnpchJ6oYgzMRNNnixLlWdYe4pTo+e2YI7kWawpT6J2F6sDTbXT+O2Mn3xl0D57TuSbL+YtXKj6VKKnpXjhROxQyY7lHWg4VKoprRqvOtD9X5g2IApp3YKbTtv08l5sb69UxMrp9qhImM2pTD6uMG/mEy6mB0rTVCKkL015Dq9Na/WG5LxeHVeeKk5lsW93WyrZ9gWrzhB5MnziqR9+VkqnUzR9KlXo6u1atO8h12235Ox731p4dwBlRvJJeHpSq1BKFE0SQa1C74qn7/wYNbma6OKSE619af5Xm1oad6ncdWc99Wt05fCMdo6jXZ/PjMOmYtu6sJRksjTxs9Is0qzwnrHVYy4b7kltKslTnhI1TytOeWahzOScJ+v4XCZFdabSmZ58W1dfV++iRdf9eeC9bzGMvv9DECU2gAp0xGMlUtzyQ7fWXAmp7lZy55n9MqUBz6qxrS1bBj9f1dHyi98L1wwqy8smlao74VFisZNxus1pbG09nvWJyoykIrRTSAlOLPlUM477cXN6nNav4iyIJna6nYRv6nfNXnS9iHfSE1F5FrT45JPpzhX6UqznWT/hmUm3F9ra2IdzxR5LHmD8xNNzetDnRhQlI85JH0n0kuiJKKEA4qUleQp0bFNHZvPmQbKqcnQTfvR+Eyc7hKvniAPKJSF9HKatkNj+Xj+mqtY48ZyhRofT9bnK372IeYYrb5xOmkyDTJ8uzcmu2zmt5A+OeITuHHfS8xG2zuKZmIl4F9UnHzwL7ogzz7xEYaYoT0z1Szbb2d7Z9s3X5058HU+WTVagATRyRlGb5ROe2iUKqUC5E4SmJyLlLlq1/lNBFNAZY5ta+T/ZvWXLlhbd06I0U53M8GXlZSWsjjjxPnNcrM04BekzTg/L5P5Lq8s7Y2bcGfxsxVmhuiTu0gYbF2aNL+1Pp9s3nloxixnpKqqTsyDV+5968lWvzN7jlTmvLL7qhu0u1j75kerTWSbqk9N1nGJKpXI96SyyQA6k896TSantmz/+vHce61A4DRRPvQ001ic/tipQ7WK2nnddH2cmF0AZROn4qqay/oE02bzl83Lk4Bx17V58I+Zjj73x3Ru/6GnNo+IpD+cZi7N2TMnOldxSwI1DP7GJUwHSEkXxtMKsJuKsPrdh/KRmzgMNduc2jqkddU71ZZTn1Se3tafaV57EsPTK7JNOXbnHPnOoTjV85LT48oz12dvb2wUpkul2V6no0vED7Uq2r/ePifPmzZWngY7Q8kHUGj6QipNttwI9W55Ftyhexi2KT/mbFCtmjKk8v5GlaWdnevNgSxlXgXYpP/SU31asX/9Yy8DA59/euPvBZVzLcJpxFlLqz6kde3rZ+XyVNKAnwxlvITrRNEN8XQoyguJ554UTWgfgHOzWIrw703n6sUc/ddLaVCbHabZPP129+vu2fL7r6kd0W3b0NEu9iNXnl9/0fv99b1+hL+sWSoAWuroKWf64vbPQ9c33R2/3+LwP1m1YY6AaQrdq+qIDpSHQOwD9j7DzDW2jjOP42m3C8EVX7GqLq1KoWiatbjpppcwNQ7VsaUunDKmySWxhBV/IUItQfeHQvnATfaXCaDnG3JgMQRnC6mxAI4k5dvHs0pDZI43WZYkdPbOUrn3h53d/+swE8Zu7tL3UzX3y/f2555578gZAubWGLApUHHpnIiWJzuBReD7xUi2r4J0oWsnS3MrqidMPf3QFp97+ee2HEzarD8+ufsG6VA/cBdBtagyJj8zbfT95gtmPpz7r3beuPb6YfqjO0l2GA+4Tkh64JT5bcppv6XLkVJEAsfeES3MpazHFAUKV53m9p73ncDNA7+w72Ry51nR5NhQKN44fj4hLl0XYM5+XZ5QxC501G995j4YelmUh7/f2PKnhEYiWd6Fy74LcurAfoHLH536AQtRBKos7uEHvTBk70H06XkyW0rgkOZumNv3516df3/7BDtvXPoonV7+4lr7ywMOPCczAtm1UJizZeOBAd2mV8rlaip/iZsGrHZhUVtFsg+lAi4sUmGxIcqbqkF48tGsgcIK/LE0ddvpEy7BSfF/if4HzGsOQGXILSAqKlVnWmiem6rZs39KnQh0xrsTo0vOtLs9Lz104dqz1kU2mZppAJfKTTuYUrrHhuq3bH3nnHYcnY8vKoJX+BKgvBZQ5eOAUj65LkCqgQvQrD6jc5l17ajYNyJWVVQo95uSOWE7243bxygOX127/trZ67cRsnM+AwaIvnCjNxmt3B3fPEqsrGKxUPN3NR970huOnaw80dhDwzyJ4ApOngT1HSdJuoHvXiPb2B5Jzf1xPJR1ZWV1j2d2wYfEDrY3BmLqp6dl5ohZJ5Bo5s35cFXal1q07PJ5nuM7+zftHfmwwQ1XN9SHT4A8rlUqSR/Ox6ueff48lMH6X/ClEKw2qoH7iET0vPBVQd5r9Gx5MeSCFFJ5vO3UeoExyemK3Ta+2SolfmZtbAebaGvYpseJtaU3w3l5jKczHHqrtfejeF66lL18L9/beXfoDezot3kLSluVxk6hoBxp37mnrJ4viU94qcShAByDpSpj2n04xqzCVLBqGYQcHaD6Huto05gcEO6IJWcI8EonE9GyWiKWoOGU6Exrcut2HqeL91bewqPB8F507Nz39/oXN27dWV3XGliFpGZlYLhYrXHjuvQ/eP8kQKPKAqpJUmUHLLiyddxIoPL9TN9QhcCIf6ISaQ0JzP/DS/UXezcuXk4T9KlTZMGuaAFzBr3/9dfv2T8/MPvzyZ90PX0lSvO66+3RpbrWULMZtK0XkSrRKr3IduosGHzVeu+3lbrl9g4H5nVQj79I6e8tLT7TsarFTJE2cGQ4Gdx6dmTg72RSNRhLBxvaRkVAimIjeEp74NJaTmgLPxeVM5O8ynNSirW9/NeX68+a7N9999+LFc9Mnfzz2SGv1SCRHOcqYo2erL0ATcx75dmlp6UMB6hV51diXQS23qDft1r1baZ2mbD5Qd/0RgDqFCaADjz7ee1rW5gyHqRPEPZLYn1uFpxC9DdM/X3/wxGUOrv1Smk1STNKEJ0D05IIUaOcC2GIyzdy2cG/ANvCqZWfDAUzXv+tfPAf694Wl5FiGHuzY0z/KNMTh+h6MGehoboCnZhYKhWhC13OxSCxGKnTLSqah/LQdnm+9tVVwHnT8eZMdoh+evHRs46bOQiESGWn94Mg3JAK5Br905szSkjjUj/hfyx3qPmFPpjus+9OdMCZzw8EpKneoEAUoUqsRMHFs3+7ul+8LBPV4EYfiTuI9jT9JkpgVnI5HIT0nr/IytauYNIKJw4fDxdT166urUqkX84uSGC2r5AgDIzscOOBFO19kCcN9uDpF4dETbaF6rrqNHurvSAQCerShobPHifZbkIzliFUBStijfPuWMp7kz+0S7o/gT2CyITx65sNvjvx4DF8eu8SEsDOOpt1nPOpCrazyLk6PKEx9niKXJwb1gLqLERxUWdQlqvp7LPr4/bXbAgF6JxoZuux0KTk7y7c4D6Irf6yCcBWOKcPiZQ4szGeLdqIr2hYwUmlpeaRiLy8SyIyVUWtkQhGl2rKKJMti4PGjA4f27jq699mBnYF40rIoRMKzvZ7FIo+2NAYCWiRSKNSH4CkyYzFqEh6lWAtRBjVGpsDZ55QhYCL32eOJfIuSR6lOJ498Mw3Fc47OePJ5ArQsh6L1rukTHlhUIh6eWFRuBfEN+h375x5Qd3EHgLKVA2X8buduwtPGoDKAOFtkBXYaGL6Xeb/u6EKaAZusnCU7PDUJzWggXuLaYjIp53rEMWfOqSQBOp+iMuctK591LoeneiXgd+0LhG08a9GfkT+DwY42Hh1BLStV3kNpmqz3rWnwRBlwQjOTizSMU5CA6V2EUzw/h6drzYvsGNSXHHAOKaZLriqJwpJ9XdB0iYqcBApRkHqCJzRV0CMhKgLoemHas7s3GAgXpdMoxuPClg5ReM6tpqkLaSYMpOBgMY5DvNPXRKKRiKnPi9kMThXpylO0fQT6spUkXyyk8CpXchbJBfGrAy3horW4mLRoi/LyS7aOLQMQNmw7HKYABQEsQBOaKKZpGUjiUIn8yCbi/Um/9xSeShs3gI1AB6EPUkh6kp+USV2aPlA2HygqH7P35t0iD6cLlHgXh8KUmPeER700qjzqrOK0b9+27lNx5uSENc0uwlaI0hulDCM1l8aiIKJILFxP2loi2oihYpliPMu/OSsDO5Z00ZgvP5/mRCe1KIeyxnxqoVS0rQXp4SHMoKQlsm1d08IW6aFoG4bvUBNBE4SxHNaMFEyTjFo4vpGOvgqg6kTT0/bEBoeZxLfiiRTQ/4z5sqhf9ydEnRQKUFfgRFjT2VyiB5HPs2KVnK/8+71bWp4+0H0qHBDn2Jw5YUwcSkIVmrKnsgaEsJeZ+L4xGNYztp1DsTxklvPLSYsxsnyesjRvZI2kOJFDPDl5JMUPHEZ5wy7ampaEuNPMW1qop6snVF9AUZjqWcNCuRubGgoRinVN3cbjiejGVg+oJ1b8uqET7w7PaZ+nSqe84vEEtuK5tF6UXIfyUDilxsOTOi8GVUBFXsCjg86iQ+4qY35z7447+UhFgvSpFx99/M0ndtbyWYq06iAtpVApLWDwXokMCIEiAU9Q2og0J3HJv99YXgYVGNgpMZzp8JvYnCOSYmUOh9T2fJ7X8xk7bnFMa0tQe4ykURibHJ2sksW/+AjDTs2wRPN6Z6dGEs0lotFbmXyEKeAyDCoR/5xTjs4Gb7j9vBC76Elg3oSnAsqrKom6GRSi4CyrS5494elKObSMpwieSAW9KvSKKEgd7UJ7Drg2LSJpKV1WwDKgSrTSc+tx4alTPQQSlOSLIUVHJ05pe7LxosExw8hmbZIsHhez898bGT2bnEstR0IzM8Nnh4JFbVLmzVZX1WyWweR6kzfIsHh/5I9ENE38mNfMG4VC6O/W5w9S2nfcSATaLrj1aN2ZbvIUnIqnq8qIr+yboCm7Mwuvwp6iCp7IA6pO6uHpEGW7Q1ClX6Te6kCFmycJxaRly0FActx5NlBWfJollkUYmDSoZwGPYE+SFZtKmXLeHZ25W4v5WH1N3Xjf+HC42MVo5+DYUP+z7Q33jDVFGYAz6JQyefEz36G87JSnXGTTxoO4c8jQRi885/IU+VEuUjwrgUrEIz/i1WU6djfaBeh52csDXngqoI4qgXIG6rsUeVZ1nj5+5RA9OFBFCdTYSMnAjkAEqK7l5NtMJpsVnJCLmLmMI17H3GF+T6NmJ7SY8DQsnGvBEmUZtMySTaNNdX19fVP9libjx2M1NZ1XezZXNbdLMw/PmIg/L3ar4LT6/IU6P41sb92/w8wXGGSivrsiyD0pnIqnyOdZ6VDkTRDl4Qc7D+6nqwx4AaqIPukwVd09cqCWMUWvuWxZcvCoUD26Vy44c5Nl/8DhNtOMmqCNaDpQYaUjan4oJNVEMxE9ZFg6oYTDk99zobPLc1aTXwnyniQAuqVv2NAnGeqsbg6N9ByuqR48HjIBCs7CSKFgUvAKBDo8Ee8YP24+fiuTubFjR/UGL9BvrkvRrOS5VOZQn6fAlB2avgSnCKaysMN5jygbJBVQl6kLFJoKqA9ViTTgysHKBSgG+9DEqzOM8c+MDg3NjDb1H+4JIfkIMXDWNw1zR9az/dwhXB+NaSLItyWCuu1mBZTJokQnayh2tbfh4e+HWcx4s55t3jK45fhhM3q1rXlzFR+XG5E6x5kTwpnCUt45eIpikUgmHytUbypsmCZ1Kp5lRC9WAPVwsv2qOntgOjThqQRGFydyVxdTJlU4HYc6ZX7r/v1T4lDFUxE96329E/HEhJsd1sV/OfHqU4dGq6omh5u6EDhnJidkSeKzk5PDIRNFgdzelQhIsnUcKg1BVo92sjAdy6ZGNT3QNba1r0o3mrf03dP5vcbvb6oZZikWxomEG2F+iz3idPnA9IDylUEkKG84yYk6FhWOCqhsPk90B09FFLk8wQnIMn3J5gueZUCRIrpD7EnMS8Q7LkVA/X9NKfVN9TmC6DiVZAx+osmxOo6JxsfPzozOUL1raprqE1QkXYjCkwSoaz1Nziezt0fBFG3fNNiZNYa21A0xYlfghqPhpz6uqgmZOcfeEJUnYIpJXeVQxshL5Mt6gCenz5UBLedZVpLKQ97DqVgicLIBk00ky7V5UCuAglPVpSnxqFebeKqAyMORl27ZwYmeZGP3VFc3jikHB/vWtR+ycB3cXNMUaiONSg4V5XJmtL2ppnlv194e0oQZLYRGNPJndXPEJF/yIbpVr44PjlWNFEx4QhPBE4oxjOrglCQq7RlEN7CQ/yWAEvHlUiGPFFB0p0MR6dMH6fnR+QLQ8xzxop6DmBSilQ4l5p9k3+87tA9SnpRNFcRKCc075RL9p66zCWnliuK4+VpklYAiLuLCXchglo8G3sKuGnFh4WUntIIfi0BXoSgFZ2HCJNAitEs3itCSQhkohCKmQruINILQEF4X3ZV20zTQByH7/v73znitff07M8ZJouaXc88598zNvReXl0vbeXcWQfSke7ha0dLW0FHcEhU5Wn34tQzPoQrKP15NkjmI/3Z/NZylkotZrholW8t3T3T/G1nsw/z+N1MpUbqLgcIz7DcaA6pzUQbqYEZJ1L9wuiaPXJMHaExUTRtgHA1QdhTx/RLFBuqLqAMqxd1P8Dzj9RaS3z6y/GCbZ714wfZU8OxcdJYizu40kA834CnJC46xx0qF+QKONw5yK9yBeY4eRq3W2ojUVt2sYSq5zVIwK7lZJl2fIh3u/gQj6dKVaKq1G+Pk5kIj6FebAXMvMaMVJeSvQIgokUiC7ICi/zR4Nuwz4mmR+TWKLr4PUHtOEk3dy2ah+r4z0Zip5QmcLcPJCpD/J/twCft0ehWZKDzdeZOYyUA7F68PyuVNMoC9ep3FJKZrGqHEzhSpuFCu9tZHV8P02p7Sot9I3setRGJtPvr5dyLRcF5Ps1L2ZH5FhjWfKsiPJ/w80U2w4j/7wQADHfTZ+h7T+fORuB8oLNuZwnp0652eV5oczxtDjlkDtTZAlWmHwkLNh6c2o1O+voEmDzHykeXpElEaPEhd1emtCO2B/aliipKoIYWmCxyrOYXsQba/c3lYLr8k0FcSiludjp2Qagel02urh63EyvBOxjskyeQ6+t7k6orI9RciT12vVIZ3/Px7paulc3PdkLn7WayjkFmr7+EGFjDNgZZAgaqW5Gr0UYCtEqakHnhVondVkR8E89p+Yo5RYm/A+QVmR44JoRoqfTBjJqeib1s8sjh1gCcCu53u9lMHNIr0W2q31uIs0+ckHxHar1jOCCVrn9AEp2Wpu/SLOZc9udx4ub5eXtvZNm2Ce/n7dhmN5GK3m8wXMyuTaaWeTGXzJ7nK8EFZ6v2cmPMwSbWGmObVfL4XVs99rEfrcwRMqzaAaThL1xfMLGuDgB2QIGVpFB+gu3Du2fnsIBrJ4OQjSZpE6LTN0GXGidHYT43Z4RP9c6/EYrMsihp61K/kRIEJSe3Oh0qfPk4a7jueQloSTgRPiCIdJYfTAUUcS0+IPiJ1Z5xwHgxFwEIPXh50s3B00t2YAuWiUrFazKcSmWSpxH+TB+D4jtRdVzrnqVevCq1yOl1vFRrNNgRuwIRkk03QeQXxPAsGjzPOY73Nfl8/D6xtWomq+/w2NE/bvg+tP6DpHVdmjS/bIPLNoohhyCJpXhGgH38qpOwxTchbmsCUYClRvosEGjaT/sRAObom7mAC0hF7NE+Hr/QcJq396Gh7mzdsm+VEO3lofqwvK8cVrFS8t/LF2rkP4VJmLZG4e7gi2xzXi5w6LxW9bJYL6QNw3uyK365wySgH/YUesyrqRDQN9QBfCljT4K9/uLaNPWaKudoZ2MSz2WzrF37Rb3GpYZL0eIdm3dALvar8BmBr/jkmiyAfq9lu2jMfS9xtFl0QznMxiBSnkmr1zqE6mmxOjqd+g6NXeuV4ijTOE3da4jRAv//ghXhKLh/WMU49qA77COC1Qr4zN5nnNFPy41fUlI+8geeZwSaZGwtf0T/6IfaNQKv2e7aFc2BzOO10DZF93oCzdts8/dL/JpzM5+qGzbzZfD6ZzFuh78PU92p+Mdn66Ij5QQHaNoJm1cN68QnJTqEolLraailEKNmMSB0BKkEPBhFES/SZyLCA9VjvL0HyKW+zk9ZinQa+Og8v4AdMJ9322ZvyRE3a2m2tifyPX9Ho6Q89jCdJeDblNe1rweEFliPmF02Pzngbd5FDpfqgegZDBGbJ4hRbvELP8rxp+7MZWUKLo7IFEeWr3JpN5uM7Tkshvnt8Pylhi9A8NWrjJbgSlzQpRsJ8iJTRap08TrNkWCmMHHWWOjtxrycb5er69n/Ss8yjAfVox9930A5paFZZk+7RYyArnQMzpon34aDvbE5tv/bTTwac7xdn83sMdJIqempsTZHEX4I9AKhFKZpoweTxbJG+/qw/uI6AGjnjbP7K1IvCKeOcCYk01QGYss2QEDc0N6fj4WRCunzf8olVliayy34VU63KdD6frrRas0LnOLfcKuSJEyWiL5lL96Pu8f5Fp0O2w4GvuD/5/0BFWzRtNRUbxKS+36e3fpxbTNDrNDy38zzG4qxZ24x4PtM3gin5jLG0baq2iPOcT1pJfJgRQNs+zcwTUPG8jvTZggrylic7ZL86q4a7Aio5nPAMBnRNg4APy5l3J5xNpHkseM4EFLjmmuBcumvVvuRDYdoi4cZ9b7GsLvAdDzR1xHEls3qQXlzqUAzqHB8yyPqis8MPl6+lC2SZuoSdPdo4uG57CSmenEM2e7F/uKaFYFqZpTznQRr70ltwGpYxvzjxaOugzAUL4DXe4tGMgaamenmV4yRR6hbJH/i1VCcs9gcCajMhaSGqe0TXNtg+C8Kw3/vKOc64vfd6gyrhZhcDxUJ9L7RE57JQfaf9F8IUfmAuVPfUX+g6ZG7F0+rRSBtejqRYNV9dOZ+PH664jjhcXq7n0qvLmxTPhutri4lul/kQji8pub3+/PX+fifKzh1KsUFYY1wE2Ra4mskYMNHO8QF1o9XD451s3i56qZaupVwAKpwC6SSO7JJrTuywK9SnqHzYxUIB6qPbWjFMFY5M+N51c0/DUyDtNSQgfg3Q3XDWf5IpsdsG/4YRY2FxEECm3WjQE4ptVG6UW9hnmMlFRiugk5kX+oqCTiwfMJBx17FKemsUeSE6xtdPpvPxyFQpSE3uH0bD6fqHrUyazt7OzgVEP3+NzS4pNsVyISfCmYV4npBjgAI73z3Y2FhlDuTDk6UsA7PhGS8/cg7MuHlD8i26YR8EgsoKpKkV9dtbs26nWPXlPrEnThfCIwV6AX3CEw/Khuhm0n/n6nGvOguuB+3APCi25OueGrxHqk7SgzPWm8hgJ6GchTOQghP7jb2AYE1n1UabGBgLnGiA7zGPq9R54FipHXYKyYdI1H0o3XD5bEiFdy+9g04uX+MPKWh2iC6xNxVHp8dS6JZp8/KhWWpzFTMZ6uWxvCg8TXrkCyQctZEZPwcp9xQL0yle7Hdbepmh1iKX70Qy5n4VCiYkBETqmOff35ErEeQVjAjnxkzPwjDohyHuchf2uzzWTFA/YOEpD5fMG8xEy/q7TcCIJ0fM04O3uTVXe8fyWG2Wj3+qCxYJnDzLpE0FUgONDxiPeTQscbroCoEUnn9ym2rjcC2xJKAnmUMGFJcPcidysgJHioQeYcZEdc+WEqRtLbqaWKGAkctpDhBMFJ7ynJFlIsBwcCTZrIL4m2yme8zgTRJrnyQQnlZwhQfhgAxqQKB2/lOmCU+1eYuT0bU836Q9QB00ACqe6gQEbSauvtUVfN/4FgGEaNWbTfQGonC2gvdWLeFumPHag9ObMytLVDx5Hm9Mkvl30vWJrmsxaBWakiqLAEUCyxXK0WYiyVoZXA5PbKwe7324uZrARuNqvDA+l4CStFKyP9nJZoG6dHKitZYzye1tgKquHQNF7Sc8n2oXYUw36poXuqmQZZ1BZ7NPu2IZBiXnJZ7YnSKS5ekEUGRyzrN+1dAM1ZirZ/GSCb03pnfU9KOsVgt3enrvDFb+LB1N0iFMteFxssEbYOOfY2pWVVI3ySukUiwxnahbY77/UyZ6R20xbvkCyhjh+gr19Je5k0y6fDD8ZbQ5WUsvmXzfAI1Sfxv8SbokEln102NxgW0pkzs4OOZpSj5ND0idtraiupKjt9K0QJFKG+0A4fzlNwntWx8UQNznabIp1igGqOUJRBfho06SDtdnKkN5oAqrYWAdKRKZN2oGbRNnTHRpNAXVNnxPwqgFWyilXqSYJ/+WLJQ6qecVeKnTOQaKfarRj/dIoAhKsUYjBSnGsq/nDleWf9krv8PAt+WkM0aAsgmnYlH+Rckpj+K+VrZLvXjJvA8kALWtkgZ1qEN5a1uwAfrEK+mIbD/SnVc3G3/nhYVCl0nmeaqvl1FtYKG8QvJP2wf6Oi4ZC2cMlBpUQ1sz7MeRyU5YGwUX7ZKxt6qQepEazVPzz/SeKQI6iICiGgWpTIXwZYFeYZ97U402Mv4XjUbrI6PN1frye+W90Tvv4AKy2YgTe3TLpaekRfY70g3dlT/BgzJGgWpnici8Q+KwtEQ2WaI7VYRpuy2eDhy7JJzqRqpHaZzVAPb6xxt41e5RwSNEpVJhWPAUa7DRBfs2fEZMEsaIp+gRf/pNpMV4gyjEG4nom9gfxkxNjIG9WaxbHYhgl0SVfyVSDHQXYZ+WZ9NXyfQ2n1ypqMCtajdZLEQJUlb4YFZyG0rrlXqFTtkQe908xNCeK58XPXaONit1hko6oFUhTrgCQqdhu5haXSa7yGTz1A+KXE759ie/aeImknNyVE2vXIrbl6nCEVVxAo1Gar/bbRmR2zQUlrQeH9gCVplQ8RiUtk0HnJd9Vj3UB7z9hchZv5EcNrK+VPiryioI//qr6jzEenyOXAQwJTMu7Xark1tjJAfCJM166xqOEAEdg0+Ljm2+u/5yr1KZctdw82BfQZ5dR/rpXF6+cNHdKKr2ubjPAtb75LDd/c8/6nTfH/I+rXU7BW9r6wieBqj4PVVggD7VbmB4WnGjWf2Ej84ebJQrh91ZoUA1/h91aDvjTGTS3AAAAABJRU5ErkJggg==\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Solar Radiation\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:radioactive\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "solar", + "radiation", + "sunlight" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/speed_gauge.json b/application/src/main/data/json/system/widget_types/speed_gauge.json new file mode 100644 index 0000000000..4872073ab2 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/speed_gauge.json @@ -0,0 +1,22 @@ +{ + "fqn": "analogue_gauges.speed_gauge_canvas_gauges", + "name": "Speed gauge", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Preconfigured gauge to display speed. Allows to configure speed range, gradient colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 7, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'speed', label: 'Speed', type: 'timeseries' }];\n }\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n", "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-analogue-radial-gauge-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-radial-gauge-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 220) {\\n\\tvalue = 220;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"startAngle\":45,\"ticksAngle\":270,\"needleCircleSize\":7,\"defaultColor\":\"#e65100\",\"minValue\":0,\"maxValue\":180,\"majorTicksCount\":9,\"colorMajorTicks\":\"#444\",\"minorTicks\":9,\"colorMinorTicks\":\"#666\",\"numbersFont\":{\"family\":\"Roboto\",\"size\":22,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"numbersColor\":\"#616161\",\"showUnitTitle\":false,\"unitTitle\":null,\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#888\"},\"titleColor\":\"#888\",\"unitsFont\":{\"family\":\"Roboto\",\"size\":28,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"unitsColor\":\"#616161\",\"valueBox\":true,\"valueInt\":3,\"valueFont\":{\"size\":32,\"style\":\"normal\",\"weight\":\"normal\",\"shadowColor\":\"rgba(0, 0, 0, 0.49)\",\"color\":\"#444\",\"family\":\"Segment7Standard\"},\"valueColor\":\"#444\",\"valueColorShadow\":\"rgba(0, 0, 0, 0.49)\",\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\",\"showBorder\":false,\"colorPlate\":\"#fff\",\"colorNeedle\":null,\"colorNeedleEnd\":null,\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"colorNeedleShadowDown\":\"rgba(188, 143, 143, 0.78)\",\"highlightsWidth\":15,\"highlights\":[{\"from\":80,\"to\":120,\"color\":\"#fdd835\"},{\"color\":\"#e57373\",\"from\":120,\"to\":180}],\"animation\":true,\"animationDuration\":1500,\"animationRule\":\"linear\"},\"title\":\"Speed gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"decimals\":0,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\",\"units\":\"mph\"}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/state_chart.json b/application/src/main/data/json/system/widget_types/state_chart.json new file mode 100644 index 0000000000..a78fc06f64 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/state_chart.json @@ -0,0 +1,26 @@ +{ + "fqn": "charts.state_chart", + "name": "State Chart", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAB9VBMVEUAAAAhlvMilvMymeE+nNVDoetInslNq/VZqOdpuPd3d3d5p5Z5suB6enp8fHyBgYGDg4ODqoqEhISIiIiKioqKuN2MjIyNjY2Ojo6QkJCRkZGSkpKUlJSVlZWWlpaXl5eYmJiZmZmampqcnJydnZ2enp6goKCgr3KhoaGioqKisXSjo6OjvsmkpKSlpaWlwdempqanp6eoqKiosGOo1vqpqampsGOqqqqqsWOrq6usrKysw9Wurq6wsLCysrK0tLS1tbW1wbK2tra3t7e4wau5ubm6urq7u7u8vLy9vb2+vr6/xbTAwMDAxbjCwsLC3ejDw8PExMTFxcXGxsbHx8fIv3jIyMjJycnKysrK5vzMzc7Nzc3Ozs7Pz8/QuDnRzcHS0tLT09PT39HU1NTU6/3VzLLV1dXV3sjW1tbX19fYuTHY2NjZ2dna0Ira2trb29vc3Nzex4De3t7f39/guyvhvCfhvCvh4eHh6Nbi4uLjvCTj4+PkyXbk5OTlvCTm5ubm693o6Ojpxl7q6urr6+vswTTs7Ozt7e3uvhju7u7vvhjv7+/wvxjw8PDx8fHyvxX0yTv09PT19fX29vb39/f4wyL4+Pj5+fn6+vr75J37+/v8whP8/Pz9/f39/v/+/v7/wQf/xRb/3HT/5JH/9tz/++/////APs7XAAAAAWJLR0Smt7AblQAAA1RJREFUeNrt3dlT01AUBvAEd8UNl2oLrdrFolZArUul1hWlVhQXFAUF1xaxIqi4FUQUrDsUClZi4nb+Th96S9NQkjDOOKZ+3wuZw8md++M25OXMlKOccJQnTUYocdRqdw8UAqTfOjG4lvr7uowOqbtAtG6o5OiGFoNDapuJHO8tFK4xOKS9msTVgoUiaQjPclnSl01snZ/y4ly2yBNZbRarbc+3yvdpt/hLawOPJp9ur7O0mRiEmzFkY1M6N/4I8qJpulzR2sBx1sgRjQuyA2pk+STdylw2VqR/FPFfWdcCvjhduiM7kVF2db1Nuspu7JGes+I76SRba7f0Wfnn/6F6It+mfo7m8TvYvh7KTiT3PQIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggPwXkFa7e7AQIP3WiUFzdl7LuBDFvJZxIYp5LeNCFPNaBn7Yc+e1KlheSYcrFCniL7LZqPl8cbp0TjavNcqu9rZJB9gdPdIJVnwjbWG1ndI95UzWM9V5rS9Ti3P4VWy1+5jXAgQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAMK+FeS3Ma2FeC/NamNfCCxEQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBA/hZkTKSkwSGn7VUJOmOLV241NiRSRZ2uYcvt+Io+Y0N83USm+MqGWEnU2JCqXqI1ZE+QhYiIy36tXR5INpOQbB5kftcmK85mtdey2iJekeWqX6/3kc+TLCQTrq6eEuZCgKTsbnNXBsIZOERJMedNQnry73XpW8UAKVhIyBPScVfQE9RqEaP7iMT6g+pdw7vKbxKl3IJqV8K7OUqXPHvG9UMGykk2lz1d4g5yvNXo6QqZiA41aHT5OwQTUWBJSrWrvlMwj1jFU2f1Q8K1dCysCUmVhdenNLvMRKZt3hGNriEnxfxlGqt9aPYRkb9bP6Q1SMFrmltMukKuMT2QpckWjc/WhP2lYB/TgjzdHyCKVM/gGXnsp+qY5hbba+hIVA/ETL0+9SepsoNiTs/igGpXZIhKqVv9QVJARIfXIWqfiM1nS+qBnHdae1V7Qss8ngEijRO5a6sMCAtdqv+HfgPwpNPbU6ipOwAAAABJRU5ErkJggg==", + "description": "Displays changes to the state of the entity over time. For example, online and offline.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.flotWidget.onDataUpdated();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.flotWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.flotWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.flotWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.flotWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries', units: '°C', decimals: 0 }];\n }\n };\n}\n\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "settingsDirective": "tb-flot-line-widget-settings", + "dataKeySettingsDirective": "tb-flot-line-key-settings", + "latestDataKeySettingsDirective": "tb-flot-latest-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-flot-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false,\"axisPosition\":\"left\",\"showSeparateAxis\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"return Math.random() > 0.5 ? 1 : 0;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false,\"axisPosition\":\"left\"},\"_hash\":0.12775350966079668,\"funcBody\":\"return Math.random() <= 0.5 ? 1 : 0;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"tooltipValueFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\",\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":0,\"max\":1.2,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"timeForComparison\":\"previousInterval\",\"comparisonCustomIntervalValue\":7200000,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"right\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"dataKeysListForLabels\":[]},\"title\":\"State Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"configMode\":\"basic\",\"showTitleIcon\":false,\"titleIcon\":\"waterfall_chart\",\"iconColor\":\"#1F6BDD\"}" + }, + "externalId": null, + "tags": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/sulfur_dioxide__so2__card.json b/application/src/main/data/json/system/widget_types/sulfur_dioxide__so2__card.json new file mode 100644 index 0000000000..f2b8510c73 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/sulfur_dioxide__so2__card.json @@ -0,0 +1,37 @@ +{ + "fqn": "sulfur_dioxide_so2_card", + "name": "Sulfur dioxide (SO2) card", + "deprecated": false, + "image": "tb-image:U08yLXZhbHVlLWNhcmQuc3Zn:U08yLXZhbHVlLWNhcmQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMTIiIHJ4PSIzLjUiLz48cGF0aCBmaWxsPSIjM0ZBNzFBIiBkPSJNMzQuNTI4IDI0LjI1NWMtNy4yMiAwLTEzLjA3OSA1Ljg1OS0xMy4wNzkgMTMuMDc4IDAgNy4yMiA1Ljg2IDEzLjA3OSAxMy4wNzkgMTMuMDc5IDcuMjIgMCAxMy4wNzktNS44NiAxMy4wNzktMTMuMDc5IDAtNy4yMi01Ljg2LTEzLjA3OC0xMy4wOC0xMy4wNzhaTTI0LjA2NSAzNy4zMzNjMC0uNzk3LjEwNS0xLjU4Mi4yNzUtMi4zMjhsNi4yNTEgNi4yNTJ2MS4zMDhhMi42MjMgMi42MjMgMCAwIDAgMi42MTYgMi42MTZ2Mi41MjRjLTUuMTQtLjY1NC05LjE0Mi01LjA0OS05LjE0Mi0xMC4zNzJabTE4LjE2NiA3LjA2M2MtLjM0LTEuMDYtMS4zMDgtMS44MzEtMi40ODUtMS44MzFoLTEuMzA4VjM4LjY0YzAtLjcyLS41ODgtMS4zMDgtMS4zMDctMS4zMDhoLTcuODQ4di0yLjYxNUgzMS45Yy43MiAwIDEuMzA4LS41ODkgMS4zMDgtMS4zMDh2LTIuNjE2aDIuNjE2YTIuNjIzIDIuNjIzIDAgMCAwIDIuNjE1LTIuNjE2di0uNTM2YzMuODMyIDEuNTQzIDYuNTUzIDUuMzEgNi41NTMgOS42OTEgMCAyLjcyLTEuMDYgNS4yMDYtMi43NiA3LjA2M1oiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii44NyIgZD0iTTYyLjMwMiAzMi40NTNjMC0uMTktLjAzLS4zNi0uMDktLjUwOGEuOTkyLjk5MiAwIDAgMC0uMjk3LS40MDYgMi40NjUgMi40NjUgMCAwIDAtLjYxLS4zNTYgOC4wODcgOC4wODcgMCAwIDAtLjk5LS4zNjFjLS40NC0uMTM2LS44NDYtLjI4Ni0xLjIxOS0uNDUxYTQuNjM3IDQuNjM3IDAgMCAxLS45NjUtLjU4NCAyLjUgMi41IDAgMCAxLS42NC0uNzY4IDIuMTUxIDIuMTUxIDAgMCAxLS4yMy0xLjAxYzAtLjM3Ni4wNzktLjcxOS4yMzYtMS4wMjguMTYtLjMwOS4zODctLjU3NS42NzktLjhhMy4yNDcgMy4yNDcgMCAwIDEgMS4wNDctLjUyNiA0LjQxIDQuNDEgMCAwIDEgMS4zMzMtLjE5Yy42ODYgMCAxLjI3Ni4xMjYgMS43NzEuMzguNS4yNTQuODgzLjU5NSAxLjE1IDEuMDIyLjI3LjQyNy40MDUuOS40MDUgMS40MTVoLTEuNThjMC0uMzA0LS4wNjYtLjU3My0uMTk3LS44MDZhMS4zMzQgMS4zMzQgMCAwIDAtLjU4NC0uNTU4Yy0uMjU4LS4xMzYtLjU4Ni0uMjAzLS45ODQtLjIwMy0uMzc3IDAtLjY5LjA1Ny0uOTQuMTcxLS4yNDkuMTE0LS40MzUuMjY5LS41NTguNDYzYTEuMjExIDEuMjExIDAgMCAwLS4xODQuNjZjMCAuMTc0LjA0LjMzMy4xMi40NzcuMDgxLjE0LjIwNC4yNy4zNjkuMzkzLjE2NS4xMTkuMzcyLjIzLjYyMi4zMzcuMjUuMTA1LjU0NC4yMDcuODgyLjMwNC41MTIuMTUzLjk1OS4zMjIgMS4zNC41MDguMzguMTgyLjY5OC4zOS45NTIuNjIyLjI1NC4yMzMuNDQ0LjQ5Ny41Ny43OTQuMTI4LjI5Mi4xOTEuNjI0LjE5MS45OTYgMCAuMzktLjA3OC43NC0uMjM1IDEuMDU0LS4xNTYuMzA5LS4zOC41NzMtLjY3Mi43OTNhMy4yMiAzLjIyIDAgMCAxLTEuMDQxLjUwMiA0LjkyIDQuOTIgMCAwIDEtMS4zNDYuMTcxYy0uNDQ0IDAtLjg4Mi0uMDU5LTEuMzE0LS4xNzhhMy44NzYgMy44NzYgMCAwIDEtMS4xNjgtLjUzOSAyLjc5IDIuNzkgMCAwIDEtLjgzOC0uOTE0Yy0uMjA3LS4zNjgtLjMxLS43OTgtLjMxLTEuMjg5aDEuNTkyYzAgLjMuMDUxLjU1Ny4xNTMuNzY4LjEwNS4yMTIuMjUyLjM4Ni40MzguNTIuMTg2LjEzMi40MDIuMjMuNjQ3LjI5My4yNS4wNjMuNTE2LjA5NS44LjA5NS4zNzIgMCAuNjgzLS4wNTMuOTMzLS4xNTlhMS4yMyAxLjIzIDAgMCAwIC41NzEtLjQ0NGMuMTI3LS4xOS4xOS0uNDEuMTktLjY2Wm03LjIzOC43NjJ2LTUuMjVoMS41Mzd2Ni44NjhoLTEuNDQ4bC0uMDg5LTEuNjE4Wm0uMjE2LTEuNDI5LjUxNC0uMDEyYzAgLjQ2MS0uMDUuODg2LS4xNTIgMS4yNzZhMi45NTkgMi45NTkgMCAwIDEtLjQ3IDEuMDA5IDIuMTk3IDIuMTk3IDAgMCAxLS44MTIuNjY2Yy0uMzMuMTU3LS43MjYuMjM1LTEuMTg3LjIzNS0uMzM0IDAtLjY0MS0uMDQ4LS45Mi0uMTQ2YTEuODg4IDEuODg4IDAgMCAxLS43MjQtLjQ1IDIuMDUgMi4wNSAwIDAgMS0uNDYzLS43OTRjLS4xMS0uMzI2LS4xNjYtLjcxNS0uMTY2LTEuMTY4di00LjQzN2gxLjUzdjQuNDVjMCAuMjUuMDMuNDU5LjA5LjYyOC4wNTguMTY1LjEzOS4yOTkuMjQuNC4xMDIuMTAyLjIyLjE3NC4zNTYuMjE2LjEzNS4wNDIuMjguMDYzLjQzMS4wNjMuNDM2IDAgLjc4LS4wODQgMS4wMjktLjI1My4yNTQtLjE3NC40MzMtLjQwNy41NC0uNjk5LjExLS4yOTIuMTY0LS42Mi4xNjQtLjk4NFptNC43NzYtNi43MDN2OS43NWgtMS41MzZ2LTkuNzVoMS41MzZabTMuOTk1IDkuNzVoLTEuNTN2LTcuNTI4YzAtLjUxMi4wOTYtLjk0Mi4yODYtMS4yODkuMTk1LS4zNS40NzItLjYxNS44MzItLjc5My4zNi0uMTgyLjc4NS0uMjczIDEuMjc2LS4yNzMuMTUyIDAgLjMwMi4wMS40NS4wMzIuMTQ4LjAxNy4yOTIuMDQ0LjQzMi4wODJsLS4wMzggMS4xOGExLjc2OCAxLjc2OCAwIDAgMC0uMjgtLjA0MyAzLjY2NiAzLjY2NiAwIDAgMC0uMzE3LS4wMTMgMS4yOCAxLjI4IDAgMCAwLS42MDMuMTMzLjg4Ljg4IDAgMCAwLS4zOC4zNzUgMS4zMjYgMS4zMjYgMCAwIDAtLjEyOC42MDl2Ny41MjhabTEuNDE2LTYuODY4djEuMTE3aC00di0xLjExN2g0Wm01LjQ5MyA1LjI1di01LjI1aDEuNTM2djYuODY4aC0xLjQ0N2wtLjA4OS0xLjYxOFptLjIxNi0xLjQyOS41MTQtLjAxMmMwIC40NjEtLjA1Ljg4Ni0uMTUyIDEuMjc2YTIuOTU5IDIuOTU5IDAgMCAxLS40NyAxLjAwOSAyLjE5NiAyLjE5NiAwIDAgMS0uODEzLjY2NmMtLjMzLjE1Ny0uNzI1LjIzNS0xLjE4Ny4yMzUtLjMzNCAwLS42NC0uMDQ4LS45Mi0uMTQ2YTEuODg4IDEuODg4IDAgMCAxLS43MjQtLjQ1IDIuMDUgMi4wNSAwIDAgMS0uNDYzLS43OTRjLS4xMS0uMzI2LS4xNjUtLjcxNS0uMTY1LTEuMTY4di00LjQzN2gxLjUzdjQuNDVjMCAuMjUuMDMuNDU5LjA4OS42MjguMDU5LjE2NS4xNC4yOTkuMjQuNC4xMDIuMTAyLjIyLjE3NC4zNTYuMjE2LjEzNi4wNDIuMjguMDYzLjQzMi4wNjMuNDM2IDAgLjc3OS0uMDg0IDEuMDI4LS4yNTMuMjU0LS4xNzQuNDM0LS40MDcuNTQtLjY5OS4xMS0uMjkyLjE2NS0uNjIuMTY1LS45ODRabTQuNjc0LTIuNTEzdjUuNTZoLTEuNTN2LTYuODY4aDEuNDZsLjA3IDEuMzA4Wm0yLjEwMS0xLjM1Mi0uMDEyIDEuNDIyYTMuOTYyIDMuOTYyIDAgMCAwLS42MjItLjA1Yy0uMjYzLS4wMDEtLjQ5NC4wMzctLjY5Mi4xMTNhMS4zMDQgMS4zMDQgMCAwIDAtLjUwMi4zMTdjLS4xMzEuMTM2LS4yMzMuMy0uMzA1LjQ5NmEyLjIxNiAyLjIxNiAwIDAgMC0uMTI2LjY1M2wtLjM1LjAyNmMwLS40MzIuMDQzLS44MzIuMTI3LTEuMi4wODUtLjM2OC4yMTItLjY5Mi4zODEtLjk3MS4xNzQtLjI4LjM5LS40OTcuNjQ4LS42NTQuMjYyLS4xNTcuNTY1LS4yMzUuOTA3LS4yMzUuMDkzIDAgLjE5My4wMDkuMjk5LjAyNi4xMS4wMTcuMTkyLjAzNi4yNDcuMDU3Wm04Ljg4NSA1LjQ5di04LjMyOGgxLjUzN3Y5Ljc1aC0xLjM5MWwtLjE0Ni0xLjQyMlptLTQuNDY4LTEuOTM2di0uMTMzYzAtLjUyLjA2MS0uOTk0LjE4NC0xLjQyMi4xMjMtLjQzMS4zLS44MDIuNTMzLTEuMTEuMjMzLS4zMTQuNTE2LS41NTMuODUtLjcxOC4zMzUtLjE3LjcxMi0uMjU0IDEuMTMtLjI1NC40MTUgMCAuNzc5LjA4IDEuMDkyLjI0MS4zMTMuMTYxLjU4LjM5Mi44LjY5Mi4yMi4yOTYuMzk2LjY1Mi41MjcgMS4wNjcuMTMxLjQxLjIyNC44NjcuMjc5IDEuMzd2LjQyNmMtLjA1NS40OTEtLjE0OC45NC0uMjc5IDEuMzQ2YTMuNjExIDMuNjExIDAgMCAxLS41MjcgMS4wNTMgMi4yOCAyLjI4IDAgMCAxLS44MDYuNjg2Yy0uMzEzLjE2LS42OC4yNDEtMS4wOTguMjQxLS40MTUgMC0uNzktLjA4Ny0xLjEyNC0uMjZhMi40ODQgMi40ODQgMCAwIDEtLjg0NC0uNzMgMy40MzkgMy40MzkgMCAwIDEtLjUzMy0xLjEwNSA1LjAyIDUuMDIgMCAwIDEtLjE4NC0xLjM5Wm0xLjUzLS4xMzN2LjEzM2MwIC4zMTQuMDI3LjYwNS4wODIuODc2LjA2LjI3MS4xNS41MS4yNzMuNzE4LjEyMy4yMDMuMjgxLjM2NC40NzYuNDgyLjE5OS4xMTQuNDM2LjE3MS43MTEuMTcxLjM0NyAwIC42MzMtLjA3Ni44NTctLjIyOC4yMjQtLjE1Mi40LS4zNTguNTI3LS42MTZhMi44NSAyLjg1IDAgMCAwIC4yNjYtLjg3NnYtMS4xNDlhMi44MDYgMi44MDYgMCAwIDAtLjE1OC0uNjk4IDEuODcgMS44NyAwIDAgMC0uMzExLS41NjUgMS4zMTUgMS4zMTUgMCAwIDAtLjQ4OS0uMzggMS41MzQgMS41MzQgMCAwIDAtLjY4LS4xNGMtLjI3OSAwLS41MTYuMDU5LS43MS4xNzdhMS40NDEgMS40NDEgMCAwIDAtLjQ4My40OSAyLjQwMyAyLjQwMyAwIDAgMC0uMjczLjcyM2MtLjA1OS4yNzUtLjA4OC41NjktLjA4OC44ODJabTcuOTYyLTMuMzc3djYuODY4SDEwNC44di02Ljg2OGgxLjUzNlptLTEuNjM4LTEuODAzYS43OC43OCAwIDAgMSAuMjI5LS41NzdjLjE1Ni0uMTU3LjM3Mi0uMjM1LjY0Ny0uMjM1LjI3MSAwIC40ODUuMDc4LjY0MS4yMzVhLjc3Ljc3IDAgMCAxIC4yMzUuNTc3Ljc2My43NjMgMCAwIDEtLjIzNS41NzJjLS4xNTYuMTUyLS4zNy4yMjgtLjY0MS4yMjgtLjI3NSAwLS40OTEtLjA3Ni0uNjQ3LS4yMjhhLjc3My43NzMgMCAwIDEtLjIyOS0uNTcyWm0zLjI3MiA1LjMxM3YtLjE0NmMwLS40OTUuMDcyLS45NTQuMjE2LTEuMzc3LjE0My0uNDI3LjM1MS0uNzk4LjYyMi0xLjExYTIuODAzIDIuODAzIDAgMCAxIDEuMDAzLS43MzcgMy4yNiAzLjI2IDAgMCAxIDEuMzQ1LS4yNjdjLjUwNCAwIC45NTIuMDkgMS4zNDYuMjY3YTIuNzkgMi43OSAwIDAgMSAxLjAwOS43MzZjLjI3NS4zMTMuNDg1LjY4NC42MjkgMS4xMTEuMTQ0LjQyMy4yMTUuODgyLjIxNSAxLjM3N3YuMTQ2YzAgLjQ5NS0uMDcxLjk1NS0uMjE1IDEuMzc4YTMuMyAzLjMgMCAwIDEtLjYyOSAxLjExIDIuOTA3IDIuOTA3IDAgMCAxLTEuMDAzLjczN2MtLjM5My4xNzMtLjg0LjI2LTEuMzM5LjI2YTMuMzUgMy4zNSAwIDAgMS0xLjM1Mi0uMjYgMi45MDcgMi45MDcgMCAwIDEtMS4wMDMtLjczNiAzLjI4MSAzLjI4MSAwIDAgMS0uNjI4LTEuMTExIDQuMjUgNC4yNSAwIDAgMS0uMjE2LTEuMzc4Wm0xLjUzLS4xNDZ2LjE0NmMwIC4zMS4wMzEuNjAxLjA5NS44NzYuMDYzLjI3NS4xNjMuNTE3LjI5OC43MjQuMTM1LjIwNy4zMDkuMzcuNTIxLjQ4OS4yMTEuMTE4LjQ2My4xNzguNzU1LjE3OC4yODMgMCAuNTI5LS4wNi43MzYtLjE3OC4yMTItLjExOS4zODUtLjI4Mi41MjEtLjQ4OS4xMzUtLjIwNy4yMzUtLjQ0OS4yOTgtLjcyNGEzLjY0IDMuNjQgMCAwIDAgLjEwMi0uODc2di0uMTQ2YzAtLjMwNC0uMDM0LS41OTItLjEwMi0uODYzYTIuMjAxIDIuMjAxIDAgMCAwLS4zMDUtLjczIDEuNDUgMS40NSAwIDAgMC0xLjI2My0uNjhjLS4yODggMC0uNTM3LjA2Mi0uNzQ5LjE4NWExLjQ3NCAxLjQ3NCAwIDAgMC0uNTE0LjQ5NSAyLjMwMiAyLjMwMiAwIDAgMC0uMjk4LjczYy0uMDY0LjI3MS0uMDk1LjU1OS0uMDk1Ljg2M1ptNy40MTYtMy4zNjQgMS4zMDggMi4yNzkgMS4zMzMtMi4yNzloMS42ODJsLTIuMDgyIDMuMzcgMi4xNjQgMy40OThoLTEuNjgybC0xLjM5Ni0yLjM3NC0xLjM5NyAyLjM3NGgtMS42ODhsMi4xNTgtMy40OTctMi4wNzYtMy4zN2gxLjY3NlptNy4yNTggMHY2Ljg2OGgtMS41MzZ2LTYuODY4aDEuNTM2Wm0tMS42MzgtMS44MDNhLjc4Ljc4IDAgMCAxIC4yMjktLjU3N2MuMTU2LS4xNTcuMzcyLS4yMzUuNjQ3LS4yMzUuMjcxIDAgLjQ4NS4wNzguNjQxLjIzNWEuNzcuNzcgMCAwIDEgLjIzNS41NzcuNzYzLjc2MyAwIDAgMS0uMjM1LjU3MmMtLjE1Ni4xNTItLjM3LjIyOC0uNjQxLjIyOC0uMjc1IDAtLjQ5MS0uMDc2LS42NDctLjIyOGEuNzczLjc3MyAwIDAgMS0uMjI5LS41NzJabTcuNzUzIDcuMjV2LTguMzI5aDEuNTM2djkuNzVoLTEuMzlsLS4xNDYtMS40MjJabS00LjQ2OS0xLjkzN3YtLjEzM2MwLS41Mi4wNjItLjk5NC4xODQtMS40MjIuMTIzLS40MzEuMzAxLS44MDIuNTM0LTEuMTEuMjMyLS4zMTQuNTE2LS41NTMuODUtLjcxOC4zMzQtLjE3LjcxMS0uMjU0IDEuMTMtLjI1NC40MTUgMCAuNzc5LjA4IDEuMDkyLjI0MS4zMTMuMTYxLjU4LjM5Mi44LjY5Mi4yMi4yOTYuMzk1LjY1Mi41MjcgMS4wNjcuMTMxLjQxLjIyNC44NjcuMjc5IDEuMzd2LjQyNmMtLjA1NS40OTEtLjE0OC45NC0uMjc5IDEuMzQ2YTMuNjM2IDMuNjM2IDAgMCAxLS41MjcgMS4wNTMgMi4yOCAyLjI4IDAgMCAxLS44MDYuNjg2Yy0uMzE0LjE2LS42OC4yNDEtMS4wOTkuMjQxLS40MTQgMC0uNzg5LS4wODctMS4xMjMtLjI2YTIuNDc2IDIuNDc2IDAgMCAxLS44NDQtLjczIDMuNDM4IDMuNDM4IDAgMCAxLS41MzQtMS4xMDUgNS4wNDcgNS4wNDcgMCAwIDEtLjE4NC0xLjM5Wm0xLjUzLS4xMzN2LjEzM2MwIC4zMTQuMDI4LjYwNS4wODMuODc2LjA1OS4yNzEuMTUuNTEuMjczLjcxOC4xMjIuMjAzLjI4MS4zNjQuNDc2LjQ4Mi4xOTkuMTE0LjQzNi4xNzEuNzExLjE3MS4zNDcgMCAuNjMyLS4wNzYuODU3LS4yMjhhMS41NiAxLjU2IDAgMCAwIC41MjYtLjYxNmMuMTMyLS4yNjIuMjItLjU1NC4yNjctLjg3NnYtMS4xNDlhMi43NjIgMi43NjIgMCAwIDAtLjE1OS0uNjk4IDEuODcgMS44NyAwIDAgMC0uMzExLS41NjUgMS4zMDcgMS4zMDcgMCAwIDAtLjQ4OS0uMzggMS41MjggMS41MjggMCAwIDAtLjY3OS0uMTRjLS4yNzkgMC0uNTE2LjA1OS0uNzExLjE3N2ExLjQ0NyAxLjQ0NyAwIDAgMC0uNDgyLjQ5IDIuMzkyIDIuMzkyIDAgMCAwLS4yNzMuNzIzIDQuMiA0LjIgMCAwIDAtLjA4OS44ODJabTkuMzUzIDMuNjE4Yy0uNTA4IDAtLjk2Ny0uMDgyLTEuMzc4LS4yNDdhMy4wNDYgMy4wNDYgMCAwIDEtMS4wNDEtLjcwNSAzLjE0MiAzLjE0MiAwIDAgMS0uNjU0LTEuMDYgMy43MiAzLjcyIDAgMCAxLS4yMjgtMS4zMTR2LS4yNTRjMC0uNTM3LjA3OC0xLjAyNC4yMzUtMS40Ni4xNTYtLjQzNi4zNzQtLjgwOC42NTQtMS4xMTcuMjc5LS4zMTMuNjA5LS41NTIuOTktLjcxN2EzLjA3NyAzLjA3NyAwIDAgMSAxLjIzOC0uMjQ4Yy40OSAwIC45Mi4wODMgMS4yODguMjQ4LjM2OC4xNjUuNjczLjM5OC45MTQuNjk4LjI0Ni4yOTYuNDI4LjY1LjU0NiAxLjA2LjEyMy40MS4xODQuODYzLjE4NCAxLjM1OHYuNjU0aC01LjMwNnYtMS4wOThoMy43OTV2LS4xMmEyLjEyOSAyLjEyOSAwIDAgMC0uMTY1LS43NzUgMS4zMTMgMS4zMTMgMCAwIDAtLjQ1LS41ODRjLS4yMDMtLjE0OC0uNDc0LS4yMjItLjgxMy0uMjIyLS4yNTQgMC0uNDguMDU1LS42NzkuMTY1YTEuMzQgMS4zNCAwIDAgMC0uNDg5LjQ2MyAyLjQyIDIuNDIgMCAwIDAtLjMwNC43MzcgNC4xMiA0LjEyIDAgMCAwLS4xMDIuOTU4di4yNTRjMCAuMy4wNC41OC4xMjEuODM4LjA4NC4yNTQuMjA3LjQ3Ni4zNjguNjY2LjE2MS4xOS4zNTUuMzQxLjU4NC40NTEuMjI4LjEwNi40ODkuMTU5Ljc4MS4xNTkuMzY4IDAgLjY5Ni0uMDc0Ljk4My0uMjIyLjI4OC0uMTQ4LjUzOC0uMzU4Ljc0OS0uNjI5bC44MDcuNzgxYTMuMTcgMy4xNyAwIDAgMS0uNTc4LjYyMiAyLjg5IDIuODkgMCAwIDEtLjg3LjQ3NiAzLjQ1NiAzLjQ1NiAwIDAgMS0xLjE4LjE4NFoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii4zOCIgZD0iTTYxLjM3NCA1MC45MTh2LjkxNWgtMy4zNjF2LS45MTVoMy4zNlptLTMuMDQtNS43OHY2LjY5NUg1Ny4xOFY0NS4xNGgxLjE1NVptNi44MjIgNS42OTh2LTIuMzczYzAtLjE3OC0uMDMyLS4zMzEtLjA5Ni0uNDZhLjY3Ny42NzcgMCAwIDAtLjI5NC0uMjk5IDEuMDA2IDEuMDA2IDAgMCAwLS40ODgtLjEwNWMtLjE4IDAtLjMzNy4wMy0uNDY5LjA5MmEuNzU4Ljc1OCAwIDAgMC0uMzA4LjI0OC41OTkuNTk5IDAgMCAwLS4xMS4zNTRoLTEuMTA0YzAtLjE5Ni4wNDgtLjM4Ni4xNDMtLjU3LjA5NS0uMTg0LjIzMy0uMzQ4LjQxNC0uNDkyLjE4LS4xNDQuMzk3LS4yNTguNjQ4LS4zNC4yNTEtLjA4My41MzMtLjEyNS44NDYtLjEyNS4zNzQgMCAuNzA1LjA2My45OTMuMTg5LjI5MS4xMjYuNTIuMzE2LjY4NS41Ny4xNjkuMjUxLjI1My41NjcuMjUzLjk0N3YyLjIxMmMwIC4yMjcuMDE1LjQzLjA0Ni42MTEuMDM0LjE3OC4wODEuMzMzLjE0My40NjV2LjA3M2gtMS4xMzZhMS45NjUgMS45NjUgMCAwIDEtLjEyNC0uNDU1IDMuNzI5IDMuNzI5IDAgMCAxLS4wNDEtLjU0MlptLjE2MS0yLjAyOC4wMS42ODVoLS43OTZjLS4yMDUgMC0uMzg2LjAyLS41NDIuMDZhMS4xMTUgMS4xMTUgMCAwIDAtLjM5MS4xNjUuNzE4LjcxOCAwIDAgMC0uMzEzLjYyMWMwIC4xMzIuMDMuMjUzLjA5Mi4zNjMuMDYxLjEwOC4xNS4xOTIuMjY3LjI1My4xMi4wNjEuMjYzLjA5Mi40MzIuMDkyLjIyNyAwIC40MjUtLjA0Ni41OTMtLjEzOGExLjIyIDEuMjIgMCAwIDAgLjQwNS0uMzQ1Ljc1Ljc1IDAgMCAwIC4xNTYtLjM5bC4zNTkuNDkxYy0uMDM3LjEyNi0uMS4yNjEtLjE4OS40MDVhMS45NTkgMS45NTkgMCAwIDEtLjM1LjQxNCAxLjczNSAxLjczNSAwIDAgMS0xLjE5LjQ0MWMtLjMyNSAwLS42MTUtLjA2NC0uODctLjE5M2ExLjU0NiAxLjU0NiAwIDAgMS0uNTk3LS41MjkgMS4zNzEgMS4zNzEgMCAwIDEtLjIxNi0uNzU4YzAtLjI2NC4wNDktLjQ5Ny4xNDctLjdhMS4zNyAxLjM3IDAgMCAxIC40NDItLjUxNGMuMTk2LS4xMzguNDM1LS4yNDIuNzE3LS4zMTMuMjgyLS4wNzMuNjA0LS4xMS45NjUtLjExaC44N1ptNS4wOTggMS42NzhjMC0uMTEtLjAyOC0uMjEtLjA4My0uMjk5LS4wNTUtLjA5Mi0uMTYtLjE3NC0uMzE3LS4yNDhhMy4wNzggMy4wNzggMCAwIDAtLjY4LS4yMDIgNS44NjQgNS44NjQgMCAwIDEtLjcyNy0uMjA3IDIuMzA1IDIuMzA1IDAgMCAxLS41Ni0uMyAxLjI1OSAxLjI1OSAwIDAgMS0uMzY0LS40MTMgMS4xNDYgMS4xNDYgMCAwIDEtLjEyOS0uNTUyYzAtLjIwMi4wNDQtLjM5NC4xMzMtLjU3NS4wOS0uMTguMjE2LS4zNC4zODItLjQ3OC4xNjYtLjEzOC4zNjYtLjI0Ni42MDItLjMyNi4yNC0uMDguNTA2LS4xMi44LS4xMi40MTcgMCAuNzc0LjA3IDEuMDcyLjIxMi4zLjEzOC41My4zMjYuNjkuNTY1LjE1OS4yMzYuMjM5LjUwMy4yMzkuOGgtMS4xMDlhLjcwNy43MDcgMCAwIDAtLjEtLjM2Ny43MDUuNzA1IDAgMCAwLS4yOTUtLjI4IDEuMDEgMS4wMSAwIDAgMC0uNDk3LS4xMTFjLS4xOSAwLS4zNDcuMDMtLjQ3My4wOTJhLjY0OC42NDggMCAwIDAtLjI3Ni4yMy41ODcuNTg3IDAgMCAwLS4wNDEuNTM4LjUzLjUzIDAgMCAwIC4xNjUuMTc5Yy4wNzcuMDUyLjE4MS4xMDEuMzEzLjE0Ny4xMzUuMDQ2LjMwMy4wOS41MDYuMTMzLjM4LjA4LjcwNi4xODMuOTc5LjMwOC4yNzYuMTIzLjQ4Ny4yODIuNjM0LjQ3OS4xNDguMTkzLjIyMS40MzguMjIxLjczNSAwIC4yMjEtLjA0Ny40MjMtLjE0Mi42MDctLjA5Mi4xODEtLjIyNy4zMzktLjQwNS40NzRhMi4wMyAyLjAzIDAgMCAxLS42NC4zMDhjLS4yNDQuMDc0LS41Mi4xMS0uODI3LjExLS40NSAwLS44MzItLjA4LTEuMTQ1LS4yMzlhMS44MjYgMS44MjYgMCAwIDEtLjcxMi0uNjIgMS40NyAxLjQ3IDAgMCAxLS4yNC0uNzkxaDEuMDcyYS44My44MyAwIDAgMCAuMTcuNDkyYy4xMDQuMTIuMjMzLjIwNi4zODYuMjYyLjE1Ny4wNTIuMzE4LjA3OC40ODMuMDc4LjIgMCAuMzY2LS4wMjYuNTAxLS4wNzhhLjcyMS43MjEgMCAwIDAgLjMwOC0uMjIxLjUyNy41MjcgMCAwIDAgLjEwNi0uMzIyWm00LjUzMi0zLjYyOHYuODFoLTIuODA1di0uODFoMi44MDVabS0xLjk5NS0xLjIxOGgxLjEwOHY0LjgxOWMwIC4xNTMuMDIxLjI3LjA2NC4zNTQuMDQ2LjA4LjEwOS4xMzMuMTg5LjE2LjA4LjAyOC4xNzMuMDQyLjI4LjA0MmEyLjIwOSAyLjIwOSAwIDAgMCAuMzktLjA0MWwuMDA1Ljg0NmMtLjA5MS4wMjctLjE5OS4wNTItLjMyMS4wNzMtLjEyLjAyMS0uMjU4LjAzMi0uNDE0LjAzMi0uMjU1IDAtLjQ4LS4wNDQtLjY3Ni0uMTMzYS45OTUuOTk1IDAgMCAxLS40Ni0uNDQ2Yy0uMTEtLjIwNS0uMTY1LS40NzgtLjE2NS0uODE4VjQ1LjY0Wm04LjU4IDUuMDJ2LTMuODAyaDEuMTE0djQuOTc1aC0xLjA0OWwtLjA2NC0xLjE3MlptLjE1Ny0xLjAzNC4zNzMtLjAwOWMwIC4zMzQtLjAzNy42NDItLjExLjkyNGEyLjE0MyAyLjE0MyAwIDAgMS0uMzQuNzMxYy0uMTU0LjIwNi0uMzUuMzY3LS41OS40ODNhMS45OSAxLjk5IDAgMCAxLS44Ni4xN2MtLjI0MSAwLS40NjQtLjAzNS0uNjY2LS4xMDVhMS4zNjcgMS4zNjcgMCAwIDEtLjUyNC0uMzI3IDEuNDg2IDEuNDg2IDAgMCAxLS4zMzYtLjU3NSAyLjY1NSAyLjY1NSAwIDAgMS0uMTItLjg0NnYtMy4yMTRoMS4xMDl2My4yMjRjMCAuMTguMDIxLjMzMi4wNjQuNDU1YS43Ny43NyAwIDAgMCAuMTc1LjI5Yy4wNzMuMDczLjE2LjEyNS4yNTcuMTU2LjA5OS4wMy4yMDMuMDQ2LjMxMy4wNDYuMzE2IDAgLjU2NC0uMDYyLjc0NS0uMTg0LjE4NC0uMTI2LjMxNC0uMjk0LjM5LS41MDYuMDgtLjIxMi4xMi0uNDUuMTItLjcxM1ptMy40MDEtMS44MTF2NS45MzFoLTEuMTA4di02Ljg4OGgxLjAybC4wODguOTU3Wm0zLjI0MiAxLjQ4NXYuMDk2YzAgLjM2Mi0uMDQzLjY5OC0uMTI5IDEuMDA3YTIuNDYgMi40NiAwIDAgMS0uMzcyLjgwNSAxLjcxNSAxLjcxNSAwIDAgMS0uNjAzLjUyOWMtLjIzOS4xMjUtLjUxNS4xODgtLjgyNy4xODgtLjMxIDAtLjU4MS0uMDU2LS44MTQtLjE3YTEuNjY3IDEuNjY3IDAgMCAxLS41ODQtLjQ5MiAyLjY2OSAyLjY2OSAwIDAgMS0uMzg2LS43NDUgNC43OCA0Ljc4IDAgMCAxLS4yMDMtLjk0N1Y0OS4yYy4wNC0uMzY1LjEwOC0uNjk2LjIwMy0uOTkzYTIuNjkgMi42OSAwIDAgMSAuMzg2LS43NjhjLjE2LS4yMTUuMzU0LS4zOC41ODQtLjQ5Ny4yMy0uMTE2LjQ5OC0uMTc1LjgwNC0uMTc1LjMxMyAwIC41OS4wNjIuODMzLjE4NC4yNDIuMTIuNDQ2LjI5MS42MTEuNTE1LjE2Ni4yMi4yOS40ODguMzczLjguMDgyLjMxLjEyNC42NTUuMTI0IDEuMDM1Wm0tMS4xMDguMDk2VjQ5LjNjMC0uMjMtLjAyMi0uNDQzLS4wNjUtLjY0LS4wNDMtLjE5OC0uMTEtLjM3My0uMjAyLS41MjNhLjk5Ljk5IDAgMCAwLS4zNTQtLjM1Ljk2NC45NjQgMCAwIDAtLjUxLS4xMjljLS4xOTcgMC0uMzY1LjAzNC0uNTA2LjEwMmEuOTcuOTcgMCAwIDAtLjM1NC4yNyAxLjI4MiAxLjI4MiAwIDAgMC0uMjIuNDFjLS4wNTMuMTUzLS4wOS4zMi0uMTExLjUwMXYuODkyYy4wMzcuMjIuMS40MjMuMTg4LjYwNy4wOS4xODQuMjE1LjMzMS4zNzcuNDQyLjE2Ni4xMDcuMzc3LjE2LjYzNS4xNi4xOTkgMCAuMzctLjA0My41MS0uMTI4LjE0MS0uMDg2LjI1Ni0uMjA0LjM0NS0uMzU0LjA5Mi0uMTU0LjE2LS4zMy4yMDItLjUzLjA0My0uMTk4LjA2NS0uNDEuMDY1LS42MzRabTUuMjgxIDEuNDA3di02LjAzMmgxLjExM3Y3LjA2MmgtMS4wMDdsLS4xMDYtMS4wM1ptLTMuMjM3LTEuNDAydi0uMDk3YzAtLjM3Ny4wNDUtLjcyLjEzNC0xLjAzLjA4OC0uMzEyLjIxNy0uNTguMzg2LS44MDQuMTY4LS4yMjcuMzc0LS40LjYxNi0uNTIuMjQyLS4xMjIuNTE1LS4xODQuODE4LS4xODQuMyAwIC41NjQuMDU5Ljc5MS4xNzUuMjI3LjExNi40Mi4yODQuNTguNTAxYTIuNiAyLjYgMCAwIDEgLjM4MS43NzNjLjA5NS4yOTcuMTYzLjYyOC4yMDMuOTkzdi4zMDhjLS4wNC4zNTUtLjEwOC42OC0uMjAzLjk3NWEyLjYyIDIuNjIgMCAwIDEtLjM4MS43NjNjLS4xNi4yMTUtLjM1NC4zOC0uNTg0LjQ5N2ExLjcxOSAxLjcxOSAwIDAgMS0uNzk2LjE3NGMtLjMgMC0uNTcyLS4wNjMtLjgxNC0uMTg4YTEuNzk5IDEuNzk5IDAgMCAxLS42MTEtLjUyOSAyLjQ4OSAyLjQ4OSAwIDAgMS0uMzg2LS44IDMuNjM2IDMuNjM2IDAgMCAxLS4xMzQtMS4wMDdabTEuMTA4LS4wOTd2LjA5N2MwIC4yMjcuMDIuNDM4LjA2LjYzNC4wNDMuMTk3LjEwOS4zNy4xOTguNTIuMDg5LjE0Ny4yMDQuMjY0LjM0NS4zNS4xNDQuMDgyLjMxNi4xMjQuNTE1LjEyNC4yNTEgMCAuNDU4LS4wNTYuNjItLjE2Ni4xNjMtLjExLjI5LS4yNTkuMzgyLS40NDYuMDk1LS4xOS4xNi0uNDAxLjE5My0uNjM0di0uODMzYTIuMDMgMi4wMyAwIDAgMC0uMTE1LS41MDUgMS4zNSAxLjM1IDAgMCAwLS4yMjUtLjQxLjk0OC45NDggMCAwIDAtLjM1NC0uMjc1IDEuMTEgMS4xMSAwIDAgMC0uNDkyLS4xMDIuOTczLjk3MyAwIDAgMC0uNTE1LjEzIDEuMDQzIDEuMDQzIDAgMCAwLS4zNS4zNTNjLS4wODguMTUtLjE1NC4zMjUtLjE5Ny41MjQtLjA0My4yLS4wNjUuNDEzLS4wNjUuNjRabTcuNDAyIDEuNTMydi0yLjM3M2MwLS4xNzgtLjAzMy0uMzMxLS4wOTctLjQ2YS42NzcuNjc3IDAgMCAwLS4yOTQtLjI5OSAxLjAwNyAxLjAwNyAwIDAgMC0uNDg4LS4xMDVjLS4xOCAwLS4zMzcuMDMtLjQ2OS4wOTJhLjc1OS43NTkgMCAwIDAtLjMwOC4yNDguNi42IDAgMCAwLS4xMS4zNTRoLTEuMTAzYzAtLjE5Ni4wNDctLjM4Ni4xNDItLjU3LjA5NS0uMTg0LjIzMy0uMzQ4LjQxNC0uNDkyLjE4LS4xNDQuMzk3LS4yNTguNjQ4LS4zNC4yNTItLjA4My41MzQtLjEyNS44NDYtLjEyNS4zNzQgMCAuNzA1LjA2My45OTMuMTg5LjI5MS4xMjYuNTIuMzE2LjY4NS41Ny4xNjkuMjUxLjI1My41NjcuMjUzLjk0N3YyLjIxMmMwIC4yMjcuMDE2LjQzLjA0Ni42MTEuMDM0LjE3OC4wODEuMzMzLjE0My40NjV2LjA3M2gtMS4xMzZhMS45NiAxLjk2IDAgMCAxLS4xMjQtLjQ1NSAzLjcxNSAzLjcxNSAwIDAgMS0uMDQxLS41NDJabS4xNi0yLjAyOC4wMS42ODVoLS43OTZjLS4yMDUgMC0uMzg2LjAyLS41NDIuMDZhMS4xMTYgMS4xMTYgMCAwIDAtLjM5MS4xNjUuNzIuNzIgMCAwIDAtLjMxMy42MjFjMCAuMTMyLjAzLjI1My4wOTIuMzYzLjA2MS4xMDguMTUuMTkyLjI2Ny4yNTMuMTIuMDYxLjI2My4wOTIuNDMyLjA5Mi4yMjcgMCAuNDI1LS4wNDYuNTkzLS4xMzguMTcyLS4wOTUuMzA3LS4yMS40MDUtLjM0NWEuNzUuNzUgMCAwIDAgLjE1Ni0uMzlsLjM1OS40OTFjLS4wMzcuMTI2LS4xLjI2MS0uMTg5LjQwNWExLjk2MSAxLjk2MSAwIDAgMS0uMzUuNDE0IDEuNzM2IDEuNzM2IDAgMCAxLTEuMTkuNDQxYy0uMzI1IDAtLjYxNS0uMDY0LS44NjktLjE5M2ExLjU0NiAxLjU0NiAwIDAgMS0uNTk4LS41MjkgMS4zNzIgMS4zNzIgMCAwIDEtLjIxNi0uNzU4YzAtLjI2NC4wNS0uNDk3LjE0Ny0uNy4xMDEtLjIwNC4yNDktLjM3Ni40NDItLjUxNC4xOTYtLjEzOC40MzUtLjI0Mi43MTctLjMxMy4yODItLjA3My42MDQtLjExLjk2Ni0uMTFoLjg2OVptNC41ODgtMS45NXYuODFoLTIuODA1di0uODFoMi44MDVabS0xLjk5Ni0xLjIxOGgxLjEwOHY0LjgxOWMwIC4xNTMuMDIyLjI3LjA2NS4zNTQuMDQ2LjA4LjEwOS4xMzMuMTg4LjE2YS44Ni44NiAwIDAgMCAuMjgxLjA0MiAyLjIxIDIuMjEgMCAwIDAgLjM5MS0uMDQxbC4wMDQuODQ2YTIuODIgMi44MiAwIDAgMS0uMzIyLjA3MyAyLjM2MiAyLjM2MiAwIDAgMS0uNDE0LjAzMmMtLjI1NCAwLS40NzktLjA0NC0uNjc1LS4xMzNhLjk5Ljk5IDAgMCAxLS40Ni0uNDQ2Yy0uMTExLS4yMDUtLjE2Ni0uNDc4LS4xNjYtLjgxOFY0NS42NFptNS4yNCA2LjI4NWMtLjM2NyAwLS43LS4wNi0uOTk3LS4xNzlhMi4xOTMgMi4xOTMgMCAwIDEtLjc1NC0uNTEgMi4yNDkgMi4yNDkgMCAwIDEtLjQ3NC0uNzY4IDIuNjk3IDIuNjk3IDAgMCAxLS4xNjYtLjk1MnYtLjE4NGMwLS4zOS4wNTctLjc0Mi4xNzEtMS4wNTcuMTEzLS4zMTYuMjcxLS41ODYuNDczLS44MS4yMDMtLjIyNy40NDItLjQuNzE3LS41Mi4yNzYtLjExOS41NzUtLjE3OS44OTctLjE3OS4zNTYgMCAuNjY3LjA2LjkzMy4xOC4yNjcuMTIuNDg4LjI4OC42NjMuNTA1LjE3Ny4yMTUuMzA5LjQ3MS4zOTUuNzY4LjA4OS4yOTguMTMzLjYyNi4xMzMuOTg0di40NzRoLTMuODQ0di0uNzk2aDIuNzV2LS4wODdhMS41NDQgMS41NDQgMCAwIDAtLjEyLS41Ni45NS45NSAwIDAgMC0uMzI2LS40MjRjLS4xNDctLjEwNy0uMzQzLS4xNi0uNTg5LS4xNmEuOTk4Ljk5OCAwIDAgMC0uNDkxLjExOS45NzUuOTc1IDAgMCAwLS4zNTUuMzM1IDEuNzY0IDEuNzY0IDAgMCAwLS4yMi41MzQgMi45OTkgMi45OTkgMCAwIDAtLjA3NC42OTR2LjE4NGMwIC4yMTguMDI5LjQyLjA4OC42MDcuMDYxLjE4NC4xNS4zNDUuMjY2LjQ4My4xMTcuMTM4LjI1OC4yNDYuNDIzLjMyNi4xNjYuMDc3LjM1NC4xMTUuNTY2LjExNS4yNjYgMCAuNTA0LS4wNTQuNzEyLS4xNi4yMDktLjEwOC4zOS0uMjYuNTQzLS40NTZsLjU4NC41NjZhMi4yODcgMi4yODcgMCAwIDEtLjQxOC40NSAyLjEwMyAyLjEwMyAwIDAgMS0uNjMuMzQ1IDIuNTEzIDIuNTEzIDAgMCAxLS44NTYuMTMzWm04LjU1NC02LjgxdjYuNzE4aC0xLjEwOHYtNS40MDJsLTEuNjQxLjU1NnYtLjkxNWwyLjYxNi0uOTU2aC4xMzNabTUuNjIyIDUuNjg4di02LjAzMmgxLjExM3Y3LjA2MmgtMS4wMDdsLS4xMDYtMS4wM1ptLTMuMjM3LTEuNDAydi0uMDk3YzAtLjM3Ny4wNDQtLjcyLjEzMy0xLjAzYTIuNDIgMi40MiAwIDAgMSAuMzg3LS44MDRjLjE2OC0uMjI3LjM3NC0uNC42MTYtLjUyLjI0Mi0uMTIyLjUxNS0uMTg0LjgxOC0uMTg0LjMwMSAwIC41NjQuMDU5Ljc5MS4xNzUuMjI3LjExNi40Mi4yODQuNTc5LjUwMS4xNi4yMTUuMjg3LjQ3Mi4zODIuNzczLjA5NS4yOTcuMTYyLjYyOC4yMDIuOTkzdi4zMDhjLS4wNC4zNTUtLjEwNy42OC0uMjAyLjk3NWEyLjYwMyAyLjYwMyAwIDAgMS0uMzgyLjc2M2MtLjE1OS4yMTUtLjM1NC4zOC0uNTg0LjQ5N2ExLjcxNiAxLjcxNiAwIDAgMS0uNzk1LjE3NGMtLjMgMC0uNTcyLS4wNjMtLjgxNC0uMTg4YTEuNzk0IDEuNzk0IDAgMCAxLS42MTEtLjUyOSAyLjQ4NCAyLjQ4NCAwIDAgMS0uMzg3LS44IDMuNjMyIDMuNjMyIDAgMCAxLS4xMzMtMS4wMDdabTEuMTA4LS4wOTd2LjA5N2MwIC4yMjcuMDIuNDM4LjA2LjYzNC4wNDMuMTk3LjEwOS4zNy4xOTguNTIuMDg5LjE0Ny4yMDMuMjY0LjM0NC4zNS4xNDUuMDgyLjMxNi4xMjQuNTE1LjEyNC4yNTIgMCAuNDU5LS4wNTYuNjIxLS4xNjYuMTYzLS4xMS4yOS0uMjU5LjM4Mi0uNDQ2YTIuMDggMi4wOCAwIDAgMCAuMTkzLS42MzR2LS44MzNhMi4wMjUgMi4wMjUgMCAwIDAtLjExNS0uNTA1IDEuMzUgMS4zNSAwIDAgMC0uMjI1LS40MS45NDguOTQ4IDAgMCAwLS4zNTQtLjI3NSAxLjExIDEuMTEgMCAwIDAtLjQ5Mi0uMTAyLjk3MS45NzEgMCAwIDAtLjUxNS4xMyAxLjA0NiAxLjA0NiAwIDAgMC0uMzUuMzUzYy0uMDg5LjE1LS4xNTUuMzI1LS4xOTcuNTI0LS4wNDMuMi0uMDY1LjQxMy0uMDY1LjY0Wm05Ljk0MyAxLjUzMnYtMi4zNzNjMC0uMTc4LS4wMzMtLjMzMS0uMDk3LS40NmEuNjc4LjY3OCAwIDAgMC0uMjk0LS4yOTkgMS4wMSAxLjAxIDAgMCAwLS40ODgtLjEwNWMtLjE4MSAwLS4zMzcuMDMtLjQ2OS4wOTJhLjc2Ljc2IDAgMCAwLS4zMDguMjQ4LjYwMy42MDMgMCAwIDAtLjExLjM1NGgtMS4xMDRjMC0uMTk2LjA0OC0uMzg2LjE0My0uNTcuMDk1LS4xODQuMjMzLS4zNDguNDE0LS40OTJhMi4wNyAyLjA3IDAgMCAxIC42NDgtLjM0Yy4yNTEtLjA4My41MzMtLjEyNS44NDYtLjEyNS4zNzQgMCAuNzA1LjA2My45OTMuMTg5LjI5MS4xMjYuNTIuMzE2LjY4NS41Ny4xNjkuMjUxLjI1My41NjcuMjUzLjk0N3YyLjIxMmMwIC4yMjcuMDE2LjQzLjA0Ni42MTEuMDM0LjE3OC4wODEuMzMzLjE0My40NjV2LjA3M2gtMS4xMzZhMS45ODYgMS45ODYgMCAwIDEtLjEyNC0uNDU1IDMuNjU5IDMuNjU5IDAgMCAxLS4wNDEtLjU0MlptLjE2LTIuMDI4LjAxLjY4NWgtLjc5NmMtLjIwNSAwLS4zODYuMDItLjU0Mi4wNmExLjExIDEuMTEgMCAwIDAtLjM5MS4xNjUuNzIyLjcyMiAwIDAgMC0uMjM1LjI2Ny43ODIuNzgyIDAgMCAwLS4wNzguMzU0YzAgLjEzMi4wMzEuMjUzLjA5Mi4zNjNhLjY0LjY0IDAgMCAwIC4yNjcuMjUzLjkzNC45MzQgMCAwIDAgLjQzMi4wOTIgMS4yMTcgMS4yMTcgMCAwIDAgLjk5OC0uNDgzLjc1Ljc1IDAgMCAwIC4xNTYtLjM5bC4zNTkuNDkxYy0uMDM3LjEyNi0uMS4yNjEtLjE4OS40MDVhMS45NjIgMS45NjIgMCAwIDEtLjM0OS40MTQgMS43MyAxLjczIDAgMCAxLTEuMTkxLjQ0MWMtLjMyNSAwLS42MTUtLjA2NC0uODY5LS4xOTNhMS41NDIgMS41NDIgMCAwIDEtLjU5OC0uNTI5IDEuMzcyIDEuMzcyIDAgMCAxLS4yMTYtLjc1OGMwLS4yNjQuMDQ5LS40OTcuMTQ3LS43LjEwMS0uMjA0LjI0OC0uMzc2LjQ0Mi0uNTE0LjE5Ni0uMTM4LjQzNS0uMjQyLjcxNy0uMzEzLjI4Mi0uMDczLjYwNC0uMTEuOTY1LS4xMWguODY5Wm01LjQ5NC0xLjk1aDEuMDA2djQuODM3YzAgLjQ0OC0uMDk1LjgyOC0uMjg1IDEuMTQtLjE5LjMxMy0uNDU1LjU1LS43OTUuNzEzYTIuNzcxIDIuNzcxIDAgMCAxLTIuNDg4LS4xMDEgMS42OCAxLjY4IDAgMCAxLS41MzgtLjQ3M2wuNTItLjY1M2MuMTc4LjIxMS4zNzQuMzY2LjU4OS40NjQuMjE0LjA5OC40MzkuMTQ3LjY3NS4xNDcuMjU1IDAgLjQ3MS0uMDQ3LjY0OS0uMTQyYS45NjEuOTYxIDAgMCAwIC40MTgtLjQxYy4wOTgtLjE4LjE0Ny0uNDAxLjE0Ny0uNjYydi0zLjczM2wuMTAyLTEuMTI3Wm0tMy4zOCAyLjU0M3YtLjA5N2MwLS4zNzcuMDQ2LS43Mi4xMzgtMS4wM2EyLjQ1IDIuNDUgMCAwIDEgLjM5NS0uODA0Yy4xNzItLjIyNy4zOC0uNC42MjYtLjUyLjI0NS0uMTIyLjUyMi0uMTg0LjgzMi0uMTg0LjMyMiAwIC41OTYuMDU5LjgyMy4xNzUuMjMuMTE2LjQyMS4yODQuNTc1LjUwMS4xNTMuMjE1LjI3My40NzIuMzU4Ljc3My4wODkuMjk3LjE1NS42MjguMTk4Ljk5M3YuMzA4Yy0uMDQuMzU1LS4xMDcuNjgtLjIwMi45NzVhMi42OTcgMi42OTcgMCAwIDEtLjM3Ny43NjNjLS4xNTcuMjE1LS4zNS4zOC0uNTguNDk3YTEuNzM3IDEuNzM3IDAgMCAxLS44MDQuMTc0Yy0uMzA0IDAtLjU3OC0uMDYzLS44MjMtLjE4OGExLjg4IDEuODggMCAwIDEtLjYyNi0uNTI5IDIuNTE3IDIuNTE3IDAgMCAxLS4zOTUtLjggMy41MiAzLjUyIDAgMCAxLS4xMzgtMS4wMDdabTEuMTA4LS4wOTd2LjA5N2MwIC4yMjcuMDIyLjQzOC4wNjQuNjM0LjA0Ni4xOTcuMTE1LjM3LjIwNy41Mi4wOTUuMTQ3LjIxNS4yNjQuMzU5LjM1LjE0Ny4wODIuMzIuMTI0LjUyLjEyNC4yNiAwIC40NzMtLjA1Ni42MzktLjE2Ni4xNjgtLjExLjI5Ny0uMjU5LjM4Ni0uNDQ2LjA5Mi0uMTkuMTU2LS40MDEuMTkzLS42MzR2LS44MzNhMi4wMjUgMi4wMjUgMCAwIDAtLjExNS0uNTA1IDEuMzUgMS4zNSAwIDAgMC0uMjI1LS40MS45NDIuOTQyIDAgMCAwLS4zNTktLjI3NSAxLjE5MyAxLjE5MyAwIDAgMC0uNTEtLjEwMmMtLjE5OSAwLS4zNzMuMDQzLS41Mi4xM2ExLjA1NSAxLjA1NSAwIDAgMC0uMzYzLjM1MyAxLjc1IDEuNzUgMCAwIDAtLjIwNy41MjRjLS4wNDYuMi0uMDY5LjQxMy0uMDY5LjY0Wm00LjQxMy4wOTd2LS4xMDZjMC0uMzU4LjA1Mi0uNjkxLjE1Ni0uOTk4LjEwNC0uMzEuMjU0LS41NzcuNDUxLS44MDRhMi4wMyAyLjAzIDAgMCAxIC43MjYtLjUzNGMuMjg4LS4xMjguNjEzLS4xOTMuOTc1LS4xOTMuMzY1IDAgLjY5LjA2NS45NzUuMTk0LjI4OC4xMjUuNTMxLjMwMy43MzEuNTMzLjE5OS4yMjcuMzUxLjQ5NS40NTUuODA0LjEwNC4zMDcuMTU2LjY0LjE1Ni45OTh2LjEwNmMwIC4zNTktLjA1Mi42OTEtLjE1Ni45OThhMi4zODcgMi4zODcgMCAwIDEtLjQ1NS44MDQgMi4xMiAyLjEyIDAgMCAxLS43MjcuNTM0IDIuMzgxIDIuMzgxIDAgMCAxLS45Ny4xODhjLS4zNjUgMC0uNjkxLS4wNjMtLjk3OS0uMTg4YTIuMTEgMi4xMSAwIDAgMS0uNzI3LS41MzQgMi4zODcgMi4zODcgMCAwIDEtLjQ1NS0uODA0IDMuMDg2IDMuMDg2IDAgMCAxLS4xNTYtLjk5OFptMS4xMDgtLjEwNnYuMTA2YzAgLjIyNC4wMjMuNDM1LjA2OS42MzQuMDQ2LjIuMTE4LjM3NS4yMTYuNTI1LjA5OC4xNS4yMjQuMjY4LjM3Ny4zNTQuMTUzLjA4NS4zMzYuMTI4LjU0Ny4xMjguMjA1IDAgLjM4My0uMDQzLjUzMy0uMTI4LjE1NC0uMDg2LjI3OS0uMjA0LjM3Ny0uMzU0LjA5OC0uMTUuMTcxLS4zMjUuMjE3LS41MjUuMDQ5LS4xOTkuMDczLS40MS4wNzMtLjYzNHYtLjEwNmMwLS4yMi0uMDI0LS40MjktLjA3My0uNjI1LS4wNDYtLjItLjEyLS4zNzYtLjIyMS0uNTI5YTEuMDU3IDEuMDU3IDAgMCAwLS45MTUtLjQ5MmMtLjIwOSAwLS4zODkuMDQ1LS41NDMuMTMzLS4xNS4wODYtLjI3NC4yMDYtLjM3Mi4zNi0uMDk4LjE1Mi0uMTcuMzI5LS4yMTYuNTI4YTIuNzMzIDIuNzMzIDAgMCAwLS4wNjkuNjI1WiIvPjxwYXRoIGZpbGw9IiMzRkE3MUEiIGQ9Im00Ni4xNzQgOTguNTc4LTIuNjI1LS42NDMgMS4wOC0xMC4wMDhINTUuMzZ2Mi43OWgtNy45OThsLS41NDcgNC44OGMuMzEtLjE4Mi43MzktLjM2IDEuMjg1LS41MzMuNTQ3LS4xODIgMS4xNzItLjI3NCAxLjg3My0uMjc0Ljk0IDAgMS43NzguMTU1IDIuNTE2LjQ2NS43NDguMzAxIDEuMzgxLjc0MyAxLjkgMS4zMjYuNTIuNTc1LjkxNyAxLjI3NiAxLjE5IDIuMTA2LjI3My44Mi40MSAxLjc0NS40MSAyLjc3NSAwIC45MjEtLjEzNyAxLjc4Mi0uNDEgMi41ODRhNi4wMTMgNi4wMTMgMCAwIDEtMS4yMDMgMi4xMTljLS41MzguNjExLTEuMjE3IDEuMDktMi4wMzcgMS40MzYtLjgxMS4zMzctMS43NzMuNTA2LTIuODg1LjUwNi0uODMgMC0xLjYyNy0uMTE5LTIuMzkzLS4zNTZhNi41MTggNi41MTggMCAwIDEtMi4wMzctMS4wOCA1LjUwMyA1LjUwMyAwIDAgMS0xLjQ0OS0xLjc3N2MtLjM2NC0uNzExLS41NzQtMS41MjctLjYyOS0yLjQ0N2gzLjIyN2MuMDgyLjY0Ny4yNiAxLjE5OC41MzMgMS42NTQuMjgyLjQ0Ny42NTYuNzg4IDEuMTIxIDEuMDI1LjQ2NS4yMzcgMS4wMDMuMzU2IDEuNjEzLjM1Ni41NTYgMCAxLjAzNS0uMDk2IDEuNDM2LS4yODcuNC0uMjAxLjczNC0uNDgzLjk5OC0uODQ4YTMuNzcgMy43NyAwIDAgMCAuNjAyLTEuMzEzIDYuMzI5IDYuMzI5IDAgMCAwIC4yMDUtMS42NjdjMC0uNTg0LS4wNzgtMS4xMTctLjIzMy0xLjZhMy40NzUgMy40NzUgMCAwIDAtLjY3LTEuMjU4IDIuODY2IDIuODY2IDAgMCAwLTEuMTA3LS44MmMtLjQ0Ny0uMi0uOTYyLS4zMDEtMS41NDUtLjMwMS0uNzg0IDAtMS4zODYuMTE0LTEuODA1LjM0Mi0uNDEuMjI4LS43OTMuNTEtMS4xNDguODQ3Wm0yNS43MDMtMi4zNjZ2My4yNTRjMCAxLjU1OS0uMTU1IDIuODg5LS40NjUgMy45OTItLjMgMS4wOTQtLjczOCAxLjk4My0xLjMxMyAyLjY2NmE1LjE0NyA1LjE0NyAwIDAgMS0yLjA2NCAxLjUwNGMtLjc5My4zMTktMS42ODIuNDc5LTIuNjY2LjQ3OWE3LjY2MiA3LjY2MiAwIDAgMS0yLjE4Ny0uMzAxIDUuMzU4IDUuMzU4IDAgMCAxLTEuODA1LS45NDNjLS41MzgtLjQyOS0uOTk4LS45OC0xLjM4MS0xLjY1NS0uMzc0LS42ODMtLjY2NS0xLjQ5OS0uODc1LTIuNDQ3LS4yLS45NDgtLjMtMi4wNDYtLjMtMy4yOTV2LTMuMjU0YzAtMS41NjguMTU0LTIuODkuNDY0LTMuOTY1LjMxLTEuMDg0Ljc1Mi0xLjk2NCAxLjMyNi0yLjYzOGE1LjA1MSA1LjA1MSAwIDAgMSAyLjA1MS0xLjQ5Yy44MDItLjMxIDEuNjk1LS40NjUgMi42OC0uNDY1Ljc5MyAwIDEuNTIyLjEgMi4xODcuM2E1LjEyMiA1LjEyMiAwIDAgMSAxLjgwNS45MTZjLjUzOC40Mi45OTMuOTY3IDEuMzY3IDEuNjQxLjM4My42NjUuNjc1IDEuNDcyLjg3NSAyLjQyLjIuOTM5LjMgMi4wMzIuMyAzLjI4MVptLTMuMjk1IDMuNzE5Vjk1LjcyYzAtLjc5My0uMDQ2LTEuNDktLjEzNy0yLjA5Mi0uMDktLjYxLS4yMjgtMS4xMjUtLjQxLTEuNTQ1LS4xNzMtLjQyOC0uMzkyLS43NzQtLjY1Ni0xLjAzOWEyLjMwOSAyLjMwOSAwIDAgMC0uOTAzLS41ODggMy4xODcgMy4xODcgMCAwIDAtMS4xMzQtLjE5MWMtLjUxIDAtLjk2Ni4xLTEuMzY3LjMtLjQwMS4xOTItLjczOS41MDItMS4wMTIuOTMtLjI3NC40MjktLjQ4My45OTQtLjYzIDEuNjk2LS4xMzYuNjkyLS4yMDQgMS41MzUtLjIwNCAyLjUyOXY0LjIxMWMwIC44MDIuMDQ1IDEuNTA4LjEzNyAyLjExOS4wOS42MTEuMjI3IDEuMTM1LjQxIDEuNTcyLjE4Mi40MjkuNC43ODQuNjU2IDEuMDY3LjI2NC4yNzMuNTY1LjQ3NC45MDIuNjAxYTMuMjUgMy4yNSAwIDAgMCAxLjEzNS4xOTJjLjUyIDAgLjk4LS4xMDEgMS4zOC0uMzAxLjQwMi0uMjAxLjc0LS41MiAxLjAxMy0uOTU3LjI3My0uNDQ3LjQ3OC0xLjAyNS42MTUtMS43MzYuMTM3LS43MTEuMjA1LTEuNTY0LjIwNS0yLjU1N1ptMjMuMTYtNi44OWgzLjMwOXYxNC43OTJIOTJsLS4yNTktMy40MThWOTMuMDRabS40NzkgNy45MTUgMS4yOTgtLjAyN2MwIDEuMDM5LS4xIDEuOTk2LS4zIDIuODcxLS4xOTIuODc1LS40OTIgMS42MzYtLjkwMyAyLjI4M2E0LjMxMyA0LjMxMyAwIDAgMS0xLjUzIDEuNTA0Yy0uNjIuMzU2LTEuMzU0LjUzMy0yLjIwMi41MzMtLjY3NSAwLTEuMjgtLjA5NS0xLjgxOS0uMjg3YTMuNjA3IDMuNjA3IDAgMCAxLTEuMzk0LS45MjljLS4zOTItLjQyLS43MTUtLjk2Ny0uOTctMS42NDFsLS40MjQtMy43MzJoMS44NzNjMCAuODIuMDY4IDEuNDkuMjA1IDIuMDA5LjEzNi41Mi4zMjguOTIxLjU3NCAxLjIwMy4yNDYuMjc0LjUzMy40NjUuODYxLjU3NS4zMzcuMTA5LjcwMi4xNjQgMS4wOTQuMTY0LjcyIDAgMS4zMTItLjExIDEuNzc3LS4zMjhhMi42ODYgMi42ODYgMCAwIDAgMS4xMDgtLjk0NGMuMjczLS40MS40NjUtLjg4OC41NzQtMS40MzVhOC43MjggOC43MjggMCAwIDAgLjE3OC0xLjgxOVptLTkuNjUzLTcuOTE2aDMuMjgydjIwLjQ4MWgtMy4yODJ2LTIwLjQ4Wm0yNS44MTMgMGgyLjk5NHYxNC4zODNjMCAxLjMzMS0uMjgzIDIuNDYxLS44NDggMy4zOTFhNS40NSA1LjQ1IDAgMCAxLTIuMzY1IDIuMTE5Yy0xLjAxMi40OTItMi4xODMuNzM4LTMuNTE0LjczOGE4LjI0OCA4LjI0OCAwIDAgMS0xLjg4Ni0uMjQ2IDcuNzA3IDcuNzA3IDAgMCAxLTEuOTk2LS43OTMgNC45NDkgNC45NDkgMCAwIDEtMS42LTEuNDA4bDEuNTQ1LTEuOTQxYy41MjkuNjI4IDEuMTEyIDEuMDg5IDEuNzUgMS4zOGE0Ljc3NSA0Ljc3NSAwIDAgMCAyLjAxLjQzOGMuNzU2IDAgMS4zOTktLjE0MSAxLjkyNy0uNDI0YTIuODYzIDIuODYzIDAgMCAwIDEuMjQ1LTEuMjE3Yy4yOTEtLjUzNy40MzctMS4xOTQuNDM3LTEuOTY4Vjk2LjM5bC4zMDEtMy4zNVptLTEwLjA0OSA3LjU2MXYtLjI4N2MwLTEuMTIxLjEzNy0yLjE0Mi40MS0zLjA2My4yNzMtLjkzLjY2NS0xLjcyNyAxLjE3Ni0yLjM5MmE1LjEyNyA1LjEyNyAwIDAgMSAxLjg1OS0xLjU0NWMuNzI5LS4zNjUgMS41NTQtLjU0NyAyLjQ3NS0uNTQ3Ljk1NyAwIDEuNzczLjE3MyAyLjQ0Ny41MmE0LjYyMiA0LjYyMiAwIDAgMSAxLjcwOSAxLjQ5Yy40NTYuNjM4LjgxMSAxLjQwMyAxLjA2NyAyLjI5Ni4yNjQuODg1LjQ2IDEuODY5LjU4NyAyLjk1NHYuOTE2YTE0Ljc5OCAxNC43OTggMCAwIDEtLjYwMSAyLjg5OGMtLjI4My44NzUtLjY1NiAxLjYzMi0xLjEyMSAyLjI3YTQuNzc5IDQuNzc5IDAgMCAxLTEuNzIzIDEuNDc2Yy0uNjc0LjM0Ny0xLjQ3Mi41Mi0yLjM5Mi41Mi0uOTAzIDAtMS43MTktLjE4Ny0yLjQ0OC0uNTYxYTUuNTYyIDUuNTYyIDAgMCAxLTEuODYtMS41NzJjLS41MS0uNjc1LS45MDEtMS40NjgtMS4xNzUtMi4zNzlhMTAuNDggMTAuNDggMCAwIDEtLjQxLTIuOTk0Wm0zLjI5NS0uMjg3di4yODdjMCAuNjc0LjA2NCAxLjMwMy4xOTEgMS44ODcuMTM3LjU4My4zNDIgMS4wOTguNjE2IDEuNTQ1LjI4Mi40MzcuNjM4Ljc4MyAxLjA2NiAxLjAzOS40MzcuMjQ2Ljk1Mi4zNjkgMS41NDUuMzY5Ljc3NSAwIDEuNDA4LS4xNjQgMS45LS40OTJhMy4xOTUgMy4xOTUgMCAwIDAgMS4xNDktMS4zMjcgNi43MTMgNi43MTMgMCAwIDAgLjU3NC0xLjg4NnYtMi40NzVhNi4wNjUgNi4wNjUgMCAwIDAtLjM0Mi0xLjUwNCA0LjAxNCA0LjAxNCAwIDAgMC0uNjctMS4yMTcgMi43OTUgMi43OTUgMCAwIDAtMS4wNjYtLjgyYy0uNDI5LS4yLS45MzQtLjMtMS41MTgtLjMtLjU5MiAwLTEuMTA3LjEyNy0xLjU0NS4zODJhMy4xNDYgMy4xNDYgMCAwIDAtMS4wOCAxLjA1MyA1LjIyMyA1LjIyMyAwIDAgMC0uNjE1IDEuNTU4IDguNDIgOC40MiAwIDAgMC0uMjA1IDEuOTAxWm0yMS44Mi0xMi4zODctNy43NTIgMjEuNjE1aC0yLjU4NGw3Ljc2Ni0yMS42MTVoMi41N1ptNS41NzggOC4xMjF2MTEuNzg1aC0zLjI5NVY5My4wNGgzLjEwNGwuMTkxIDMuMDA4Wm0tLjUzMyAzLjg0Mi0xLjEyMS0uMDE0YzAtMS4wMi4xMjgtMS45NjQuMzgzLTIuODMuMjU1LS44NjYuNjI5LTEuNjE4IDEuMTIxLTIuMjU2YTQuOTI4IDQuOTI4IDAgMCAxIDEuODMyLTEuNDljLjczOC0uMzU1IDEuNTktLjUzMyAyLjU1Ny0uNTMzLjY3NCAwIDEuMjg5LjEgMS44NDUuM2EzLjY1OCAzLjY1OCAwIDAgMSAxLjQ2My45MTdjLjQxOS40MTkuNzM4Ljk1Ny45NTcgMS42MTMuMjI4LjY1Ni4zNDIgMS40NS4zNDIgMi4zNzl2OS44NTdoLTMuMjk1di05LjU3YzAtLjcyLS4xMDktMS4yODUtLjMyOC0xLjY5NWExLjgyIDEuODIgMCAwIDAtLjkxNi0uODc1Yy0uMzkyLS4xODMtLjg2MS0uMjc0LTEuNDA4LS4yNzQtLjYyIDAtMS4xNDkuMTE5LTEuNTg2LjM1Ni0uNDI5LjIzNy0uNzc5LjU2LTEuMDUzLjk3LS4yNzMuNDEtLjQ3NC44ODQtLjYwMSAxLjQyMmE3LjQyNCA3LjQyNCAwIDAgMC0uMTkyIDEuNzIzWm05LjE3NC0uODc1LTEuNTQ1LjM0MmMwLS44OTQuMTIzLTEuNzM3LjM2OS0yLjUzYTYuNDQ1IDYuNDQ1IDAgMCAxIDEuMTA4LTIuMTA1IDUuMTk0IDUuMTk0IDAgMCAxIDEuODE4LTEuNDM2Yy43Mi0uMzQ2IDEuNTQ1LS41MiAyLjQ3NS0uNTIuNzU2IDAgMS40MzEuMTA2IDIuMDIzLjMxNS42MDIuMiAxLjExMi41MiAxLjUzMS45NTcuNDIuNDM4LjczOSAxLjAwOC45NTcgMS43MS4yMTkuNjkyLjMyOCAxLjUzLjMyOCAyLjUxNXY5LjU3aC0zLjMwOHYtOS41ODRjMC0uNzQ3LS4xMS0xLjMyNi0uMzI4LTEuNzM2LS4yMS0uNDEtLjUxMS0uNjkzLS45MDMtLjg0OC0uMzkyLS4xNjQtLjg2MS0uMjQ2LTEuNDA4LS4yNDYtLjUxIDAtLjk2MS4wOTYtMS4zNTMuMjg3YTIuNjgxIDIuNjgxIDAgMCAwLS45NzEuNzggMy40ODQgMy40ODQgMCAwIDAtLjYwMiAxLjEzNCA0Ljg4OCA0Ljg4OCAwIDAgMC0uMTkxIDEuMzk1Wm0xNC40NTEtNi42NThoMS4xOWMuMzkxIDAgLjcxNS0uMDU1Ljk3LS4xNjQuMjU1LS4xMTkuNDQ3LS4yNzguNTc0LS40NzkuMTM3LS4yLjIwNi0uNDM3LjIwNi0uNzEgMC0uMzI5LS4xMjgtLjYxNi0uMzgzLS44NjItLjI0Ni0uMjU1LS42NjEtLjM4My0xLjI0NC0uMzgzLS40NDcgMC0uODI1LjA5Ni0xLjEzNS4yODctLjMwMS4xODItLjQ1MS40NDItLjQ1MS43OGgtMi40ODljMC0uNjQ4LjE4My0xLjIuNTQ3LTEuNjU1LjM2NS0uNDU2Ljg1Mi0uODAyIDEuNDYzLTEuMDM5YTUuNDc0IDUuNDc0IDAgMCAxIDEuOTk2LS4zNTVjLjgzIDAgMS41NTkuMTE4IDIuMTg4LjM1NS42MjkuMjI4IDEuMTE2LjU3IDEuNDYzIDEuMDI1LjM1NS40NTYuNTMzIDEuMDIxLjUzMyAxLjY5NiAwIC41Mi0uMTY0Ljk4LS40OTIgMS4zOC0uMzE5LjQwMi0uNzY2LjcxNi0xLjM0Ljk0NC0uNTY1LjIxOS0xLjIzMS4zMjgtMS45OTYuMzI4aC0xLjZ2LTEuMTQ4Wm0wIDEuNzA5di0xLjEyMWgxLjZjLjg0NyAwIDEuNTcyLjEgMi4xNzQuMy42MDEuMTkyIDEuMDU3LjQ4OCAxLjM2Ny44OS4zMTkuNC40NzguOTIuNDc4IDEuNTU4IDAgLjY3NC0uMTkxIDEuMjQ4LS41NzQgMS43MjItLjM4My40NzQtLjkwNy44MzQtMS41NzIgMS4wOC0uNjU2LjI0Ni0xLjM5NS4zNy0yLjIxNS4zNy0uNjg0IDAtMS4zNDktLjExLTEuOTk2LS4zMjlhMy43MDYgMy43MDYgMCAwIDEtMS41ODYtMS4wNjZjLS40MS0uNDgzLS42MTUtMS4xMTctLjYxNS0xLjloMi41MDJjMCAuMzU1LjE2NC42Ni40OTIuOTE2LjMyOC4yNTUuNzcuMzgyIDEuMzI2LjM4Mi41OTIgMCAxLjAzLS4xMjcgMS4zMTItLjM4Mi4yOTItLjI2NS40MzgtLjU5My40MzgtLjk4NSAwLS4zNDYtLjA4Mi0uNjI0LS4yNDYtLjgzNGExLjI3NSAxLjI3NSAwIDAgMC0uNjctLjQ1Yy0uMjkyLS4xMDEtLjYzNC0uMTUxLTEuMDI1LS4xNTFoLTEuMTlaIi8+PC9zdmc+", + "description": "Produced by volcanoes and industrial processes, particularly the burning of coal and oil.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'sulfur', label: 'Sulfur dioxide', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sulfur dioxide\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 600) {\\n\\tvalue = 600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"public\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#3FA71A\"},{\"from\":100,\"to\":200,\"color\":\"#80C32C\"},{\"from\":200,\"to\":350,\"color\":\"#FFA600\"},{\"from\":350,\"to\":500,\"color\":\"#F36900\"},{\"from\":500,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":28,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#3FA71A\"},{\"from\":100,\"to\":200,\"color\":\"#80C32C\"},{\"from\":200,\"to\":350,\"color\":\"#FFA600\"},{\"from\":350,\"to\":500,\"color\":\"#F36900\"},{\"from\":500,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Sulfur dioxide\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "enviroment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "sulfur", + "sulfur dioxide" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/sulfur_dioxide__so2__card_with_background.json b/application/src/main/data/json/system/widget_types/sulfur_dioxide__so2__card_with_background.json new file mode 100644 index 0000000000..84f304183c --- /dev/null +++ b/application/src/main/data/json/system/widget_types/sulfur_dioxide__so2__card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "sulfur_dioxide_so2_card_with_background", + "name": "Sulfur dioxide (SO2) card with background", + "deprecated": false, + "image": "tb-image:U08yLXZhbHVlLWNhcmQtd2l0aC1iYWNrZ3JvdW5kLnN2Zw==:U08yLXZhbHVlLWNhcmQtd2l0aC1iYWNrZ3JvdW5kLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuNzYiIHJ4PSI0Ii8+PC9nPjxwYXRoIGZpbGw9IiMzQjkxMUMiIGQ9Ik0yNyAxNy41Yy02LjkgMC0xMi41IDUuNi0xMi41IDEyLjVTMjAuMSA0Mi41IDI3IDQyLjUgMzkuNSAzNi45IDM5LjUgMzAgMzMuOSAxNy41IDI3IDE3LjVaTTE3IDMwYzAtLjc2Mi4xLTEuNTEyLjI2Mi0yLjIyNWw1Ljk3NiA1Ljk3NVYzNWMwIDEuMzc1IDEuMTI1IDIuNSAyLjUgMi41djIuNDEzQzIwLjgyNSAzOS4yODggMTcgMzUuMDg3IDE3IDMwWm0xNy4zNjIgNi43NWMtLjMyNS0xLjAxMy0xLjI1LTEuNzUtMi4zNzUtMS43NWgtMS4yNXYtMy43NWMwLS42ODgtLjU2Mi0xLjI1LTEuMjUtMS4yNWgtNy41di0yLjVoMi41Yy42ODggMCAxLjI1LS41NjMgMS4yNS0xLjI1di0yLjVoMi41YzEuMzc2IDAgMi41LTEuMTI1IDIuNS0yLjV2LS41MTJDMzQuNCAyMi4yMTIgMzcgMjUuODEzIDM3IDMwYzAgMi42LTEuMDEzIDQuOTc1LTIuNjM4IDYuNzVaIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuODciIGQ9Ik01NC4yNDggMjUuOTM2YzAtLjIwNS0uMDMyLS4zODctLjA5Ni0uNTQ2YTEuMDY4IDEuMDY4IDAgMCAwLS4zMi0uNDM4IDIuNjU1IDIuNjU1IDAgMCAwLS42NTctLjM4MyA4LjcwOCA4LjcwOCAwIDAgMC0xLjA2Ny0uMzkgMTEuNzk2IDExLjc5NiAwIDAgMS0xLjMxMi0uNDg1IDQuOTk2IDQuOTk2IDAgMCAxLTEuMDQtLjYyOSAyLjY5IDIuNjkgMCAwIDEtLjY5LS44MjcgMi4zMTYgMi4zMTYgMCAwIDEtLjI0Ni0xLjA4N2MwLS40MDUuMDg1LS43NzQuMjUzLTEuMTA3LjE3My0uMzMzLjQxNy0uNjIuNzMyLS44NjFhMy41IDMuNSAwIDAgMSAxLjEyOC0uNTY4IDQuNzUxIDQuNzUxIDAgMCAxIDEuNDM1LS4yMDVjLjczOCAwIDEuMzc0LjEzNyAxLjkwNy40MS41MzguMjc0Ljk1LjY0IDEuMjM4IDEuMS4yOTEuNDYxLjQzNy45Ny40MzcgMS41MjVoLTEuNzAyYzAtLjMyOC0uMDctLjYxNy0uMjEyLS44NjhhMS40MzYgMS40MzYgMCAwIDAtLjYyOS0uNjAxYy0uMjc4LS4xNDYtLjYzMS0uMjItMS4wNi0uMjItLjQwNSAwLS43NDIuMDYyLTEuMDExLjE4NWExLjM1IDEuMzUgMCAwIDAtLjYwMi41Yy0uMTMyLjIxLS4xOTguNDQ2LS4xOTguNzEgMCAuMTg3LjA0My4zNTguMTMuNTEzLjA4Ny4xNS4yMTkuMjkyLjM5Ny40MjQuMTc3LjEyNy40LjI0OC42Ny4zNjIuMjY4LjExNC41ODUuMjI0Ljk1LjMyOGE5LjQxNiA5LjQxNiAwIDAgMSAxLjQ0Mi41NDdjLjQxLjE5Ni43NTIuNDIgMS4wMjUuNjcuMjc0LjI1LjQ3OS41MzYuNjE2Ljg1NS4xMzYuMzE0LjIwNS42NzIuMjA1IDEuMDczIDAgLjQyLS4wODUuNzk3LS4yNTMgMS4xMzVhMi40NSAyLjQ1IDAgMCAxLS43MjUuODU0Yy0uMzEuMjMzLS42ODMuNDEzLTEuMTIuNTRhNS4yOTQgNS4yOTQgMCAwIDEtMS40NS4xODVjLS40NzkgMC0uOTUtLjA2NC0xLjQxNS0uMTkyYTQuMTc1IDQuMTc1IDAgMCAxLTEuMjU4LS41OCAzLjAwMyAzLjAwMyAwIDAgMS0uOTAyLS45ODVjLS4yMjQtLjM5Ny0uMzM1LS44Ni0uMzM1LTEuMzg4aDEuNzE1YzAgLjMyNC4wNTUuNi4xNjUuODI3LjExNC4yMjguMjcuNDE1LjQ3MS41Ni4yLjE0Mi40MzMuMjQ3LjY5OC4zMTUuMjY4LjA2OS41NTYuMTAzLjg2LjEwMy40MDIgMCAuNzM3LS4wNTcgMS4wMDYtLjE3MS4yNzMtLjExNC40NzgtLjI3NC42MTUtLjQ3OS4xMzctLjIwNS4yMDUtLjQ0Mi4yMDUtLjcxWm03Ljc3Ni44MnYtNS42NTJoMS42NTVWMjguNUg2Mi4xMmwtLjA5Ni0xLjc0M1ptLjIzMy0xLjUzNy41NTMtLjAxNGMwIC40OTctLjA1NC45NTUtLjE2MyAxLjM3NC0uMTEuNDE1LS4yNzkuNzc3LS41MDYgMS4wODctLjIyOC4zMDUtLjUyLjU0NS0uODc1LjcxOC0uMzU2LjE2OC0uNzgyLjI1My0xLjI3OS4yNTMtLjM2IDAtLjY5LS4wNTMtLjk5LS4xNTdhMi4wMyAyLjAzIDAgMCAxLS43OC0uNDg2IDIuMjA4IDIuMjA4IDAgMCAxLS41LS44NTRjLS4xMTgtLjM1MS0uMTc3LS43Ny0uMTc3LTEuMjU4di00Ljc3OWgxLjY0OHY0Ljc5MmMwIC4yNy4wMzEuNDk1LjA5NS42NzcuMDY0LjE3OC4xNS4zMjIuMjYuNDMuMTEuMTEuMjM3LjE4OC4zODMuMjMzLjE0Ni4wNDYuMy4wNjkuNDY1LjA2OS40NjkgMCAuODM4LS4wOTEgMS4xMDctLjI3NC4yNzQtLjE4Ny40NjctLjQzNy41ODEtLjc1Mi4xMTktLjMxNC4xNzgtLjY2Ny4xNzgtMS4wNlpNNjcuMzggMTh2MTAuNWgtMS42NTRWMThoMS42NTRabTQuMjgzIDEwLjVoLTEuNjQ3di04LjEwN2MwLS41NTIuMTAyLTEuMDE0LjMwNy0xLjM4OGEyLjAzIDIuMDMgMCAwIDEgLjg5Ni0uODU1Yy4zODctLjE5Ni44NDUtLjI5NCAxLjM3NC0uMjk0LjE2NCAwIC4zMjYuMDEyLjQ4NS4wMzUuMTYuMDE4LjMxNS4wNDguNDY1LjA4OWwtLjA0MSAxLjI3MWExLjg4NSAxLjg4NSAwIDAgMC0uMy0uMDQ4IDMuOTY3IDMuOTY3IDAgMCAwLS4zNDMtLjAxNGMtLjI1IDAtLjQ2Ny4wNDgtLjY1LjE0NGEuOTQ4Ljk0OCAwIDAgMC0uNDEuNDAzIDEuNDI3IDEuNDI3IDAgMCAwLS4xMzYuNjU3VjI4LjVabTEuNTI1LTcuMzk2djEuMjAzaC00LjMwN3YtMS4yMDNoNC4zMDZabTUuODk2IDUuNjUzdi01LjY1NGgxLjY1NFYyOC41aC0xLjU1OGwtLjA5Ni0xLjc0M1ptLjIzMi0xLjUzOC41NTQtLjAxNGMwIC40OTctLjA1NS45NTUtLjE2NCAxLjM3NC0uMTEuNDE1LS4yNzguNzc3LS41MDYgMS4wODctLjIyOC4zMDUtLjUyLjU0NS0uODc1LjcxOC0uMzU1LjE2OC0uNzgxLjI1My0xLjI3OC4yNTMtLjM2IDAtLjY5LS4wNTMtLjk5MS0uMTU3LS4zMDEtLjEwNS0uNTYtLjI2Ny0uNzgtLjQ4NmEyLjIxIDIuMjEgMCAwIDEtLjQ5OS0uODU0Yy0uMTE4LS4zNTEtLjE3Ny0uNzctLjE3Ny0xLjI1OHYtNC43NzloMS42NDd2NC43OTJjMCAuMjcuMDMyLjQ5NS4wOTYuNjc3LjA2NC4xNzguMTUuMzIyLjI2LjQzLjEwOS4xMS4yMzcuMTg4LjM4Mi4yMzMuMTQ2LjA0Ni4zMDEuMDY5LjQ2NS4wNjkuNDcgMCAuODM5LS4wOTEgMS4xMDgtLjI3NC4yNzMtLjE4Ny40NjctLjQzNy41OC0uNzUyLjEyLS4zMTQuMTc4LS42NjcuMTc4LTEuMDZabTUuMDE1LTIuNzA3VjI4LjVoLTEuNjQ3di03LjM5NmgxLjU3MmwuMDc1IDEuNDA4Wm0yLjI2My0xLjQ1Ni0uMDE0IDEuNTNhNC4yOSA0LjI5IDAgMCAwLS42Ny0uMDU1Yy0uMjgyLjAwMS0uNTMuMDQyLS43NDUuMTI0YTEuNDAzIDEuNDAzIDAgMCAwLS41NC4zNDJjLS4xNDEuMTQ2LS4yNS4zMjQtLjMyOC41MzMtLjA3OC4yMS0uMTIzLjQ0NS0uMTM3LjcwNGwtLjM3Ni4wMjhjMC0uNDY1LjA0Ni0uODk2LjEzNy0xLjI5Mi4wOTEtLjM5Ny4yMjgtLjc0NS40MS0xLjA0Ni4xODctLjMwMS40Mi0uNTM2LjY5Ny0uNzA0YTEuODcgMS44NyAwIDAgMSAuOTc4LS4yNTNjLjEgMCAuMjA3LjAwOS4zMjEuMDI3LjExOS4wMTguMjA3LjAzOS4yNjcuMDYyWm05LjUzIDUuOTEzVjE4aDEuNjU0djEwLjVoLTEuNDk3bC0uMTU3LTEuNTMxWm0tNC44MTMtMi4wODV2LS4xNDRjMC0uNTYuMDY3LTEuMDcuMTk5LTEuNTMxLjEzMi0uNDY1LjMyMy0uODY0LjU3NC0xLjE5Ni4yNS0uMzM4LjU1Ni0uNTk1LjkxNi0uNzczLjM2LS4xODIuNzY2LS4yNzMgMS4yMTctLjI3My40NDYgMCAuODM4LjA4NiAxLjE3Ni4yNi4zMzcuMTczLjYyNC40MjEuODYuNzQ1LjIzOC4zMTkuNDI3LjcwMS41NjggMS4xNDguMTQxLjQ0Mi4yNDIuOTM0LjMwMSAxLjQ3N3YuNDU4YTcuNCA3LjQgMCAwIDEtLjMgMS40NDljLS4xNDIuNDM3LS4zMzEuODE2LS41NjggMS4xMzVhMi40NTMgMi40NTMgMCAwIDEtLjg2OC43MzhjLS4zMzguMTczLS43MzIuMjYtMS4xODMuMjYtLjQ0NyAwLS44NS0uMDk0LTEuMjEtLjI4YTIuNjc0IDIuNjc0IDAgMCAxLS45MDktLjc4NyAzLjcwMSAzLjcwMSAwIDAgMS0uNTc0LTEuMTkgNS40MDkgNS40MDkgMCAwIDEtLjE5OC0xLjQ5NlptMS42NDgtLjE0NHYuMTQ0YzAgLjMzNy4wMy42NTEuMDg5Ljk0My4wNjQuMjkyLjE2Mi41NS4yOTQuNzczLjEzMi4yMTguMzAzLjM5MS41MTMuNTIuMjE0LjEyMi40NjkuMTg0Ljc2NS4xODQuMzc0IDAgLjY4MS0uMDgyLjkyMy0uMjQ2LjI0Mi0uMTY0LjQzLS4zODYuNTY3LS42NjNhMy4wOCAzLjA4IDAgMCAwIC4yODctLjk0NHYtMS4yMzdhMy4wMTggMy4wMTggMCAwIDAtLjE3LS43NTIgMi4wMTEgMi4wMTEgMCAwIDAtLjMzNS0uNjA4IDEuNDEyIDEuNDEyIDAgMCAwLS41MjctLjQxIDEuNjUgMS42NSAwIDAgMC0uNzMxLS4xNTFjLS4zMDEgMC0uNTU2LjA2NC0uNzY2LjE5MS0uMjEuMTI4LS4zODMuMzAzLS41Mi41MjdhMi41OSAyLjU5IDAgMCAwLS4yOTMuNzc5IDQuNTA4IDQuNTA4IDAgMCAwLS4wOTYuOTVabTguNTU2LTMuNjM2VjI4LjVoLTEuNjU1di03LjM5NmgxLjY1NVptLTEuNzY0LTEuOTQyYzAtLjI1LjA4Mi0uNDU4LjI0Ni0uNjIyLjE2OS0uMTY5LjQwMS0uMjUzLjY5Ny0uMjUzLjI5MiAwIC41MjIuMDg0LjY5MS4yNTNhLjgzNC44MzQgMCAwIDEgLjI1My42MjIuODI1LjgyNSAwIDAgMS0uMjUzLjYxNWMtLjE2OS4xNjQtLjM5OS4yNDYtLjY5MS4yNDYtLjI5NiAwLS41MjgtLjA4Mi0uNjk3LS4yNDZhLjgzNS44MzUgMCAwIDEtLjI0Ni0uNjE1Wm0zLjUwNCA1LjcyMnYtLjE1N2MwLS41MzQuMDc3LTEuMDI4LjIzMi0xLjQ4NC4xNTUtLjQ2LjM3OS0uODU5LjY3LTEuMTk2YTMuMDIgMy4wMiAwIDAgMSAxLjA4LS43OTMgMy41MiAzLjUyIDAgMCAxIDEuNDUtLjI4N2MuNTQyIDAgMS4wMjUuMDk2IDEuNDQ5LjI4Ny40MjguMTg3Ljc5LjQ1MSAxLjA4Ny43OTMuMjk2LjMzNy41MjEuNzM2LjY3NiAxLjE5Ni4xNTUuNDU2LjIzMy45NS4yMzMgMS40ODR2LjE1N2MwIC41MzMtLjA3OCAxLjAyNy0uMjMzIDEuNDgzLS4xNTUuNDU2LS4zOC44NTUtLjY3NiAxLjE5N2EzLjE1IDMuMTUgMCAwIDEtMS4wOC43OTJjLS40MjQuMTg3LS45MDUuMjgtMS40NDMuMjhhMy42MSAzLjYxIDAgMCAxLTEuNDU2LS4yOCAzLjEzMiAzLjEzMiAwIDAgMS0xLjA4LS43OTMgMy41NTUgMy41NTUgMCAwIDEtLjY3Ny0xLjE5NiA0LjU3OSA0LjU3OSAwIDAgMS0uMjMyLTEuNDgzWm0xLjY0Ny0uMTU3di4xNTdjMCAuMzMyLjAzNS42NDcuMTAzLjk0My4wNjguMjk2LjE3NS41NTYuMzIxLjc4LjE0Ni4yMjMuMzMzLjM5OC41NjEuNTI2LjIyOC4xMjcuNDk5LjE5MS44MTMuMTkxLjMwNiAwIC41Ny0uMDY0Ljc5My0uMTkxYTEuNTkgMS41OSAwIDAgMCAuNTYxLS41MjdjLjE0Ni0uMjIzLjI1My0uNDgzLjMyMS0uNzc5YTMuOTMgMy45MyAwIDAgMCAuMTA5LS45NDN2LS4xNTdjMC0uMzI5LS4wMzYtLjYzOS0uMTA5LS45M2EyLjM5IDIuMzkgMCAwIDAtLjMyOC0uNzg2IDEuNTcgMS41NyAwIDAgMC0uNTYxLS41MzQgMS41NDIgMS41NDIgMCAwIDAtLjc5OS0uMTk4Yy0uMzEgMC0uNTc5LjA2Ni0uODA3LjE5OGExLjU5MiAxLjU5MiAwIDAgMC0uNTU0LjUzNGMtLjE0Ni4yMjgtLjI1My40OS0uMzIxLjc4NmE0LjA4NCA0LjA4NCAwIDAgMC0uMTAzLjkzWm03Ljk2OC0zLjYyMyAxLjQwOCAyLjQ1NCAxLjQzNi0yLjQ1NGgxLjgxMWwtMi4yNDIgMy42MyAyLjMzMSAzLjc2NmgtMS44MTFsLTEuNTA0LTIuNTU3LTEuNTA0IDIuNTU3aC0xLjgxOGwyLjMyNC0zLjc2Ny0yLjIzNi0zLjYzaDEuODA1Wm03Ljc5NyAwVjI4LjVoLTEuNjU0di03LjM5NmgxLjY1NFptLTEuNzY0LTEuOTQyYzAtLjI1LjA4Mi0uNDU4LjI0Ni0uNjIyLjE2OS0uMTY5LjQwMS0uMjUzLjY5OC0uMjUzLjI5MSAwIC41MjEuMDg0LjY5LjI1M2EuODMuODMgMCAwIDEgLjI1My42MjIuODIyLjgyMiAwIDAgMS0uMjUzLjYxNWMtLjE2OS4xNjQtLjM5OS4yNDYtLjY5LjI0Ni0uMjk3IDAtLjUyOS0uMDgyLS42OTgtLjI0NmEuODM1LjgzNSAwIDAgMS0uMjQ2LS42MTVabTguMzMgNy44MDdWMThoMS42NTV2MTAuNWgtMS40OTdsLS4xNTgtMS41MzFabS00LjgxMi0yLjA4NXYtLjE0NGMwLS41Ni4wNjYtMS4wNy4xOTgtMS41MzEuMTMyLS40NjUuMzI0LS44NjQuNTc0LTEuMTk2LjI1MS0uMzM4LjU1Ni0uNTk1LjkxNi0uNzczLjM2LS4xODIuNzY2LS4yNzMgMS4yMTctLjI3My40NDcgMCAuODM5LjA4NiAxLjE3Ni4yNi4zMzcuMTczLjYyNC40MjEuODYxLjc0NS4yMzcuMzE5LjQyNi43MDEuNTY4IDEuMTQ4LjE0MS40NDIuMjQxLjkzNC4zIDEuNDc3di40NThhNy40IDcuNCAwIDAgMS0uMyAxLjQ0OSAzLjkxIDMuOTEgMCAwIDEtLjU2OCAxLjEzNSAyLjQ1IDIuNDUgMCAwIDEtLjg2OC43MzhjLS4zMzcuMTczLS43MzEuMjYtMS4xODMuMjYtLjQ0NiAwLS44NDktLjA5NC0xLjIwOS0uMjhhMi42NzUgMi42NzUgMCAwIDEtLjkxLS43ODcgMy43MSAzLjcxIDAgMCAxLS41NzQtMS4xOSA1LjQxMyA1LjQxMyAwIDAgMS0uMTk4LTEuNDk2Wm0xLjY0Ny0uMTQ0di4xNDRjMCAuMzM3LjAzLjY1MS4wODkuOTQzLjA2NC4yOTIuMTYyLjU1LjI5NC43NzMuMTMyLjIxOC4zMDMuMzkxLjUxMy41Mi4yMTQuMTIyLjQ2OS4xODQuNzY1LjE4NC4zNzQgMCAuNjgyLS4wODIuOTIzLS4yNDZhMS42OCAxLjY4IDAgMCAwIC41NjgtLjY2M2MuMTQxLS4yODMuMjM3LS41OTguMjg3LS45NDR2LTEuMjM3YTMuMDMyIDMuMDMyIDAgMCAwLS4xNzEtLjc1MiAyLjAxOCAyLjAxOCAwIDAgMC0uMzM1LS42MDggMS40MTMgMS40MTMgMCAwIDAtLjUyNi0uNDEgMS42NSAxLjY1IDAgMCAwLS43MzItLjE1MWMtLjMwMSAwLS41NTYuMDY0LS43NjUuMTkxLS4yMS4xMjgtLjM4My4zMDMtLjUyLjUyN2EyLjYgMi42IDAgMCAwLS4yOTQuNzc5IDQuNDk1IDQuNDk1IDAgMCAwLS4wOTYuOTVabTEwLjA1MyAzLjg5N2EzLjk0MSAzLjk0MSAwIDAgMS0xLjQ4My0uMjY3IDMuMjYxIDMuMjYxIDAgMCAxLTEuMTIxLS43NTkgMy4zNyAzLjM3IDAgMCAxLS43MDUtMS4xNDEgMy45OTkgMy45OTkgMCAwIDEtLjI0Ni0xLjQxNXYtLjI3NGMwLS41NzguMDg1LTEuMTAzLjI1My0xLjU3Mi4xNjktLjQ3LjQwNC0uODcuNzA0LTEuMjAzYTMuMDMgMy4wMyAwIDAgMSAxLjA2Ny0uNzczYy40MS0uMTc3Ljg1NC0uMjY2IDEuMzMzLS4yNjYuNTI4IDAgLjk5MS4wODkgMS4zODguMjY2LjM5Ni4xNzguNzI0LjQyOS45ODQuNzUyLjI2NC4zMi40Ni43LjU4OCAxLjE0Mi4xMzIuNDQyLjE5OC45My4xOTggMS40NjN2LjcwNGgtNS43MTVWMjQuMTFoNC4wODh2LS4xM2EyLjI5NyAyLjI5NyAwIDAgMC0uMTc4LS44MzQgMS40MDkgMS40MDkgMCAwIDAtLjQ4NS0uNjI4Yy0uMjE5LS4xNi0uNTEtLjI0LS44NzUtLjI0LS4yNzMgMC0uNTE3LjA2LS43MzEuMTc4LS4yMS4xMTQtLjM4Ni4yOC0uNTI3LjUtLjE0MS4yMTgtLjI1LjQ4Mi0uMzI4Ljc5Mi0uMDczLjMwNS0uMTA5LjY1LS4xMDkgMS4wMzJ2LjI3NGMwIC4zMjMuMDQzLjYyNC4xMy45MDIuMDkxLjI3My4yMjMuNTEzLjM5Ni43MTguMTczLjIwNS4zODMuMzY3LjYyOS40ODUuMjQ2LjExNC41MjYuMTcxLjg0MS4xNzEuMzk2IDAgLjc0OS0uMDggMS4wNTktLjI0LjMxLS4xNTkuNTc5LS4zODQuODA3LS42NzZsLjg2OC44NGEzLjM3NiAzLjM3NiAwIDAgMS0uNjIyLjY3Yy0uMjU1LjIxLS41NjcuMzgxLS45MzYuNTE0YTMuNzMyIDMuNzMyIDAgMCAxLTEuMjcyLjE5OFoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii4zOCIgZD0iTTUyLjg3NCA0MS41ODV2LjkxNWgtMy4zNjF2LS45MTVoMy4zNlptLTMuMDQtNS43OFY0Mi41SDQ4LjY4di02LjY5NWgxLjE1NVptNi44MjIgNS42OTdWMzkuMTNjMC0uMTc4LS4wMzItLjMzMS0uMDk2LS40NmEuNjc2LjY3NiAwIDAgMC0uMjk0LS4yOTkgMS4wMDYgMS4wMDYgMCAwIDAtLjQ4OC0uMTA2Yy0uMTggMC0uMzM3LjAzLS40NjkuMDkyYS43NTguNzU4IDAgMCAwLS4zMDguMjQ4LjU5OS41OTkgMCAwIDAtLjExLjM1NWgtMS4xMDRjMC0uMTk3LjA0OC0uMzg3LjE0My0uNTcuMDk1LS4xODQuMjMzLS4zNDkuNDE0LS40OTMuMTgtLjE0NC4zOTctLjI1Ny42NDgtLjM0LjI1MS0uMDgzLjUzMy0uMTI0Ljg0Ni0uMTI0LjM3NCAwIC43MDUuMDYzLjk5My4xODguMjkxLjEyNi41Mi4zMTYuNjg1LjU3LjE2OS4yNTIuMjUzLjU2OC4yNTMuOTQ4djIuMjExYzAgLjIyNy4wMTYuNDMxLjA0Ni42MTIuMDM0LjE3OC4wODEuMzMzLjE0My40NjR2LjA3NGgtMS4xMzZhMS45NjUgMS45NjUgMCAwIDEtLjEyNC0uNDU1IDMuNzMgMy43MyAwIDAgMS0uMDQxLS41NDNabS4xNjEtMi4wMjguMDEuNjg2aC0uNzk2Yy0uMjA1IDAtLjM4Ni4wMi0uNTQyLjA2YTEuMTE1IDEuMTE1IDAgMCAwLS4zOTEuMTY1LjcxOC43MTggMCAwIDAtLjMxMy42MmMwIC4xMzMuMDMuMjUzLjA5Mi4zNjQuMDYxLjEwNy4xNS4xOTEuMjY3LjI1My4xMi4wNjEuMjYzLjA5Mi40MzIuMDkyLjIyNyAwIC40MjUtLjA0Ni41OTMtLjEzOGExLjIyIDEuMjIgMCAwIDAgLjQwNS0uMzQ1Ljc1Ljc1IDAgMCAwIC4xNTYtLjM5bC4zNTkuNDkxYy0uMDM3LjEyNi0uMS4yNi0uMTg5LjQwNWExLjk2MSAxLjk2MSAwIDAgMS0uMzUuNDE0IDEuNzM1IDEuNzM1IDAgMCAxLTEuMTkuNDQxYy0uMzI1IDAtLjYxNS0uMDY0LS44Ny0uMTkzYTEuNTQ0IDEuNTQ0IDAgMCAxLS41OTctLjUyOSAxLjM3MSAxLjM3MSAwIDAgMS0uMjE2LS43NTljMC0uMjYzLjA0OS0uNDk2LjE0Ny0uNjk4YTEuMzcgMS4zNyAwIDAgMSAuNDQyLS41MTVjLjE5Ni0uMTM4LjQzNS0uMjQzLjcxNy0uMzEzLjI4Mi0uMDc0LjYwNC0uMTEuOTY1LS4xMWguODdabTUuMDk4IDEuNjc5YzAtLjExLS4wMjgtLjIxLS4wODMtLjMtLjA1NS0uMDkxLS4xNi0uMTc0LS4zMTctLjI0N2EzLjA3OCAzLjA3OCAwIDAgMC0uNjgtLjIwMyA1Ljg2NCA1Ljg2NCAwIDAgMS0uNzI3LS4yMDcgMi4zMDUgMi4zMDUgMCAwIDEtLjU2LS4yOTggMS4yNTcgMS4yNTcgMCAwIDEtLjM2NC0uNDE0IDEuMTQ2IDEuMTQ2IDAgMCAxLS4xMjktLjU1MmMwLS4yMDIuMDQ0LS4zOTQuMTMzLS41NzUuMDktLjE4LjIxNi0uMzQuMzgyLS40NzguMTY2LS4xMzguMzY2LS4yNDcuNjAyLS4zMjYuMjQtLjA4LjUwNi0uMTIuOC0uMTIuNDE3IDAgLjc3NC4wNyAxLjA3Mi4yMTIuMy4xMzcuNTMuMzI2LjY5LjU2NS4xNTkuMjM2LjIzOS41MDMuMjM5LjhoLTEuMTA5YS43MDcuNzA3IDAgMCAwLS4xLS4zNjguNzA1LjcwNSAwIDAgMC0uMjk1LS4yOCAxLjAwOCAxLjAwOCAwIDAgMC0uNDk3LS4xMWMtLjE5IDAtLjM0OC4wMy0uNDczLjA5MWEuNjQ4LjY0OCAwIDAgMC0uMjc2LjIzLjU4Ny41ODcgMCAwIDAtLjA0MS41MzguNTMuNTMgMCAwIDAgLjE2NS4xOGMuMDc3LjA1Mi4xODEuMS4zMTMuMTQ3LjEzNS4wNDYuMzAzLjA5LjUwNi4xMzMuMzguMDguNzA2LjE4My45NzkuMzA4LjI3Ni4xMjMuNDg3LjI4Mi42MzQuNDc4LjE0OC4xOTQuMjIxLjQzOS4yMjEuNzM2IDAgLjIyLS4wNDcuNDIzLS4xNDIuNjA3LS4wOTIuMTgtLjIyNy4zMzktLjQwNS40NzNhMi4wMyAyLjAzIDAgMCAxLS42NC4zMDljLS4yNDQuMDczLS41Mi4xMS0uODI3LjExLS40NSAwLS44MzItLjA4LTEuMTQ1LS4yNGExLjgyNiAxLjgyNiAwIDAgMS0uNzEyLS42MiAxLjQ3IDEuNDcgMCAwIDEtLjI0LS43OWgxLjA3MmEuODMuODMgMCAwIDAgLjE3LjQ5MWMuMTA0LjEyLjIzMy4yMDcuMzg2LjI2Mi4xNTcuMDUzLjMxNy4wNzguNDgzLjA3OC4yIDAgLjM2Ni0uMDI2LjUwMS0uMDc4YS43MjEuNzIxIDAgMCAwIC4zMDgtLjIyLjUyNy41MjcgMCAwIDAgLjEwNi0uMzIyWm00LjUzMi0zLjYyOHYuODFoLTIuODA1di0uODFoMi44MDVabS0xLjk5NS0xLjIxOGgxLjEwOHY0LjgxOGMwIC4xNTQuMDIxLjI3Mi4wNjQuMzU0LjA0Ni4wOC4xMDkuMTM0LjE4OS4xNjEuMDguMDI4LjE3My4wNDIuMjguMDQyYTIuMjA5IDIuMjA5IDAgMCAwIC4zOS0uMDQxbC4wMDUuODQ1Yy0uMDkxLjAyOC0uMTk5LjA1Mi0uMzIxLjA3NC0uMTIuMDIxLS4yNTguMDMyLS40MTQuMDMyLS4yNTUgMC0uNDgtLjA0NC0uNjc2LS4xMzNhLjk5NS45OTUgMCAwIDEtLjQ2LS40NDZjLS4xMS0uMjA2LS4xNjUtLjQ3OS0uMTY1LS44MTl2LTQuODg4Wm04LjU4IDUuMDJ2LTMuODAyaDEuMTE0VjQyLjVoLTEuMDQ5bC0uMDY0LTEuMTcyWm0uMTU3LTEuMDM0LjM3My0uMDFjMCAuMzM1LS4wMzcuNjQzLS4xMS45MjVhMi4xNDIgMi4xNDIgMCAwIDEtLjM0LjczMSAxLjU5IDEuNTkgMCAwIDEtLjU5LjQ4MyAxLjk5IDEuOTkgMCAwIDEtLjg2LjE3Yy0uMjQxIDAtLjQ2NC0uMDM1LS42NjYtLjEwNmExLjM2NiAxLjM2NiAwIDAgMS0uNTI0LS4zMjYgMS40ODUgMS40ODUgMCAwIDEtLjMzNi0uNTc1IDIuNjU1IDIuNjU1IDAgMCAxLS4xMi0uODQ2di0zLjIxNGgxLjEwOXYzLjIyM2MwIC4xODEuMDIxLjMzMy4wNjQuNDU1YS43Ny43NyAwIDAgMCAuMTc1LjI5Yy4wNzMuMDc0LjE2LjEyNi4yNTcuMTU2LjA5OS4wMzEuMjAzLjA0Ni4zMTMuMDQ2LjMxNiAwIC41NjQtLjA2MS43NDUtLjE4NC4xODQtLjEyNS4zMTQtLjI5NC4zOS0uNTA1LjA4LS4yMTIuMTItLjQ1LjEyLS43MTNabTMuNDAxLTEuODEydjUuOTMyaC0xLjEwOHYtNi44ODhoMS4wMmwuMDg4Ljk1NlptMy4yNDIgMS40ODZ2LjA5NmMwIC4zNjItLjA0My42OTctLjEyOSAxLjAwN2EyLjQ2IDIuNDYgMCAwIDEtLjM3Mi44MDUgMS43MTQgMS43MTQgMCAwIDEtLjYwMy41MjhjLS4yMzkuMTI2LS41MTUuMTg5LS44MjcuMTg5LS4zMSAwLS41ODEtLjA1Ny0uODE0LS4xN2ExLjY2NyAxLjY2NyAwIDAgMS0uNTg0LS40OTIgMi42NyAyLjY3IDAgMCAxLS4zODYtLjc0NSA0Ljc4MiA0Ljc4MiAwIDAgMS0uMjAzLS45NDd2LS4zNzNjLjA0LS4zNjQuMTA4LS42OTYuMjAzLS45OTNhMi42OSAyLjY5IDAgMCAxIC4zODYtLjc2OGMuMTYtLjIxNC4zNTQtLjM4LjU4NC0uNDk2YTEuNzYgMS43NiAwIDAgMSAuODA0LS4xNzVjLjMxMyAwIC41OS4wNjEuODMzLjE4NC4yNDIuMTIuNDQ2LjI5MS42MTEuNTE1LjE2Ni4yMi4yOS40ODcuMzczLjguMDgyLjMxLjEyNC42NTQuMTI0IDEuMDM1Wm0tMS4xMDguMDk2di0uMDk2YzAtLjIzLS4wMjItLjQ0My0uMDY1LS42NC0uMDQzLS4xOTktLjExLS4zNzQtLjIwMi0uNTI0YS45OS45OSAwIDAgMC0uMzU0LS4zNS45NjMuOTYzIDAgMCAwLS41MS0uMTI4Yy0uMTk3IDAtLjM2NS4wMzQtLjUwNi4xMDFhLjk3Ljk3IDAgMCAwLS4zNTQuMjcxIDEuMjgyIDEuMjgyIDAgMCAwLS4yMi40MWMtLjA1My4xNTMtLjA5LjMyLS4xMTEuNXYuODkzYy4wMzcuMjIuMS40MjMuMTg4LjYwNy4wOS4xODQuMjE1LjMzLjM3Ny40NDEuMTY2LjEwOC4zNzcuMTYxLjYzNS4xNjEuMTk5IDAgLjM3LS4wNDMuNTEtLjEyOS4xNDEtLjA4NS4yNTYtLjIwMy4zNDUtLjM1NC4wOTItLjE1My4xNi0uMzMuMjAyLS41MjguMDQzLS4yLjA2NS0uNDExLjA2NS0uNjM1Wm01LjI4MSAxLjQwN3YtNi4wMzJoMS4xMTNWNDIuNWgtMS4wMDdsLS4xMDYtMS4wM1ptLTMuMjM3LTEuNDAydi0uMDk3YzAtLjM3Ny4wNDUtLjcyLjEzNC0xLjAzLjA4OC0uMzEzLjIxNy0uNTguMzg2LS44MDUuMTY4LS4yMjYuMzc0LS40LjYxNi0uNTIuMjQyLS4xMjIuNTE1LS4xODMuODE4LS4xODMuMyAwIC41NjQuMDU4Ljc5MS4xNzUuMjI3LjExNi40Mi4yODMuNTguNWEyLjYgMi42IDAgMCAxIC4zODEuNzczYy4wOTUuMjk4LjE2My42MjkuMjAzLjk5NHYuMzA4Yy0uMDQuMzU1LS4xMDguNjgtLjIwMy45NzRhMi42MiAyLjYyIDAgMCAxLS4zODEuNzY0Yy0uMTYuMjE0LS4zNTQuMzgtLjU4NC40OTZhMS43MTkgMS43MTkgMCAwIDEtLjc5Ni4xNzVjLS4zIDAtLjU3Mi0uMDYzLS44MTQtLjE4OWExLjc5OCAxLjc5OCAwIDAgMS0uNjExLS41MjggMi40OSAyLjQ5IDAgMCAxLS4zODYtLjggMy42MzYgMy42MzYgMCAwIDEtLjEzNC0xLjAwN1ptMS4xMDgtLjA5N3YuMDk3YzAgLjIyNy4wMi40MzguMDYuNjM0LjA0My4xOTYuMTA5LjM3LjE5OC41Mi4wODkuMTQ3LjIwNC4yNjMuMzQ1LjM1LjE0NC4wODIuMzE2LjEyMy41MTUuMTIzLjI1MSAwIC40NTgtLjA1NS42Mi0uMTY1LjE2My0uMTEuMjktLjI2LjM4Mi0uNDQ2LjA5NS0uMTkuMTYtLjQwMi4xOTMtLjYzNXYtLjgzMmEyLjAzIDIuMDMgMCAwIDAtLjExNS0uNTA2IDEuMzUgMS4zNSAwIDAgMC0uMjI1LS40MDkuOTQ4Ljk0OCAwIDAgMC0uMzU0LS4yNzYgMS4xMSAxLjExIDAgMCAwLS40OTItLjEwMS45NzIuOTcyIDAgMCAwLS41MTUuMTI5IDEuMDQzIDEuMDQzIDAgMCAwLS4zNS4zNTRjLS4wODguMTUtLjE1NC4zMjUtLjE5Ny41MjQtLjA0My4yLS4wNjUuNDEyLS4wNjUuNjRabTcuNDAyIDEuNTMxVjM5LjEzYzAtLjE3OC0uMDMzLS4zMzEtLjA5Ny0uNDZhLjY3Ni42NzYgMCAwIDAtLjI5NC0uMjk5IDEuMDA3IDEuMDA3IDAgMCAwLS40ODgtLjEwNmMtLjE4IDAtLjMzNy4wMy0uNDY5LjA5MmEuNzU5Ljc1OSAwIDAgMC0uMzA4LjI0OC42LjYgMCAwIDAtLjExLjM1NWgtMS4xMDNjMC0uMTk3LjA0Ny0uMzg3LjE0Mi0uNTcuMDk1LS4xODQuMjMzLS4zNDkuNDE0LS40OTMuMTgtLjE0NC4zOTctLjI1Ny42NDgtLjM0LjI1Mi0uMDgzLjUzNC0uMTI0Ljg0Ni0uMTI0LjM3NCAwIC43MDUuMDYzLjk5My4xODguMjkxLjEyNi41Mi4zMTYuNjg1LjU3LjE2OS4yNTIuMjUzLjU2OC4yNTMuOTQ4djIuMjExYzAgLjIyNy4wMTYuNDMxLjA0Ni42MTIuMDM0LjE3OC4wODEuMzMzLjE0My40NjR2LjA3NGgtMS4xMzZhMS45NiAxLjk2IDAgMCAxLS4xMjQtLjQ1NSAzLjcxNiAzLjcxNiAwIDAgMS0uMDQxLS41NDNabS4xNi0yLjAyOC4wMS42ODZoLS43OTZjLS4yMDUgMC0uMzg2LjAyLS41NDIuMDZhMS4xMTYgMS4xMTYgMCAwIDAtLjM5MS4xNjUuNzIuNzIgMCAwIDAtLjMxMy42MmMwIC4xMzMuMDMuMjUzLjA5Mi4zNjQuMDYxLjEwNy4xNS4xOTEuMjY3LjI1My4xMi4wNjEuMjYzLjA5Mi40MzIuMDkyLjIyNyAwIC40MjUtLjA0Ni41OTMtLjEzOC4xNzItLjA5NS4zMDctLjIxLjQwNS0uMzQ1YS43NTEuNzUxIDAgMCAwIC4xNTYtLjM5bC4zNTkuNDkxYy0uMDM3LjEyNi0uMS4yNi0uMTg5LjQwNWExLjk2NCAxLjk2NCAwIDAgMS0uMzUuNDE0IDEuNzM2IDEuNzM2IDAgMCAxLTEuMTkuNDQxYy0uMzI1IDAtLjYxNS0uMDY0LS44NjktLjE5M2ExLjU0NSAxLjU0NSAwIDAgMS0uNTk4LS41MjkgMS4zNzIgMS4zNzIgMCAwIDEtLjIxNi0uNzU5YzAtLjI2My4wNS0uNDk2LjE0Ny0uNjk4LjEwMS0uMjA2LjI0OS0uMzc3LjQ0Mi0uNTE1LjE5Ni0uMTM4LjQzNS0uMjQzLjcxNy0uMzEzLjI4Mi0uMDc0LjYwNC0uMTEuOTY2LS4xMWguODY5Wm00LjU4OC0xLjk0OXYuODFIOTEuMjJ2LS44MWgyLjgwNVptLTEuOTk2LTEuMjE4aDEuMTA4djQuODE4YzAgLjE1NC4wMjIuMjcyLjA2NS4zNTQuMDQ2LjA4LjEwOS4xMzQuMTg4LjE2MS4wOC4wMjguMTczLjA0Mi4yOC4wNDJhMi4yMDkgMi4yMDkgMCAwIDAgLjM5LS4wNDFsLjAwNi44NDVjLS4wOTIuMDI4LS4yLjA1Mi0uMzIyLjA3NC0uMTIuMDIxLS4yNTcuMDMyLS40MTMuMDMyLS4yNTUgMC0uNDgtLjA0NC0uNjc2LS4xMzNhLjk5NC45OTQgMCAwIDEtLjQ2LS40NDZjLS4xMS0uMjA2LS4xNjYtLjQ3OS0uMTY2LS44MTl2LTQuODg4Wm01LjI0IDYuMjg1Yy0uMzY3IDAtLjctLjA2LS45OTctLjE4YTIuMjAyIDIuMjAyIDAgMCAxLS43NTQtLjUxIDIuMjYzIDIuMjYzIDAgMCAxLS40NzQtLjc2OCAyLjY5IDIuNjkgMCAwIDEtLjE2Ni0uOTUxdi0uMTg0YzAtLjM5LjA1Ny0uNzQyLjE3LTEuMDU4YTIuNCAyLjQgMCAwIDEgLjQ3NC0uODFjLjIwMy0uMjI2LjQ0Mi0uNC43MTgtLjUxOS4yNzUtLjEyLjU3NC0uMTc5Ljg5Ni0uMTc5LjM1NiAwIC42NjcuMDYuOTMzLjE4LjI2Ny4xMTkuNDg4LjI4OC42NjMuNTA1LjE3Ny4yMTUuMzEuNDcuMzk1Ljc2OC4wODkuMjk3LjEzMy42MjUuMTMzLjk4NHYuNDc0aC0zLjg0NHYtLjc5NmgyLjc1di0uMDg3YTEuNTU0IDEuNTU0IDAgMCAwLS4xMi0uNTYxLjk1My45NTMgMCAwIDAtLjMyNi0uNDIzYy0uMTQ3LS4xMDctLjM0My0uMTYxLS41ODktLjE2MWExIDEgMCAwIDAtLjQ5Mi4xMi45NzMuOTczIDAgMCAwLS4zNTMuMzM1IDEuNzY3IDEuNzY3IDAgMCAwLS4yMjEuNTMzYy0uMDUuMjA2LS4wNzQuNDM3LS4wNzQuNjk1di4xODRjMCAuMjE3LjAzLjQyLjA4OC42MDcuMDYuMTg0LjE1LjM0NC4yNjYuNDgyLjExNy4xMzguMjU4LjI0Ny40MjMuMzI3LjE2Ni4wNzYuMzU0LjExNS41NjYuMTE1LjI2NyAwIC41MDQtLjA1NC43MTMtLjE2MS4yMDgtLjEwNy4zODktLjI2LjU0Mi0uNDU1bC41ODQuNTY1YTIuMjggMi4yOCAwIDAgMS0uNDE4LjQ1IDIuMDk1IDIuMDk1IDAgMCAxLS42My4zNDZjLS4yNDYuMDg4LS41My4xMzMtLjg1Ni4xMzNabTguNTU0LTYuODFWNDIuNWgtMS4xMDh2LTUuNDAzbC0xLjY0MS41NTd2LS45MTVsMi42MTYtLjk1N2guMTMzWm01LjYyMiA1LjY4OHYtNi4wMzJoMS4xMTNWNDIuNWgtMS4wMDdsLS4xMDYtMS4wM1ptLTMuMjM3LTEuNDAydi0uMDk3YzAtLjM3Ny4wNDQtLjcyLjEzMy0xLjAzYTIuNDIgMi40MiAwIDAgMSAuMzg3LS44MDVjLjE2OC0uMjI2LjM3NC0uNC42MTYtLjUyLjI0Mi0uMTIyLjUxNS0uMTgzLjgxOC0uMTgzLjMwMSAwIC41NjQuMDU4Ljc5MS4xNzUuMjI3LjExNi40Mi4yODMuNTc5LjUuMTYuMjE2LjI4Ny40NzMuMzgyLjc3My4wOTUuMjk4LjE2Mi42MjkuMjAyLjk5NHYuMzA4Yy0uMDQuMzU1LS4xMDcuNjgtLjIwMi45NzRhMi42MDMgMi42MDMgMCAwIDEtLjM4Mi43NjRjLS4xNTkuMjE0LS4zNTQuMzgtLjU4NC40OTZhMS43MTYgMS43MTYgMCAwIDEtLjc5NS4xNzVjLS4zIDAtLjU3Mi0uMDYzLS44MTQtLjE4OWExLjc5MiAxLjc5MiAwIDAgMS0uNjExLS41MjggMi40ODUgMi40ODUgMCAwIDEtLjM4Ny0uOCAzLjYzMiAzLjYzMiAwIDAgMS0uMTMzLTEuMDA3Wm0xLjEwOC0uMDk3di4wOTdjMCAuMjI3LjAyLjQzOC4wNi42MzQuMDQzLjE5Ni4xMDkuMzcuMTk4LjUyLjA4OS4xNDcuMjAzLjI2My4zNDQuMzUuMTQ1LjA4Mi4zMTYuMTIzLjUxNS4xMjMuMjUyIDAgLjQ1OS0uMDU1LjYyMS0uMTY1LjE2My0uMTEuMjktLjI2LjM4Mi0uNDQ2YTIuMDggMi4wOCAwIDAgMCAuMTkzLS42MzV2LS44MzJhMi4wMjUgMi4wMjUgMCAwIDAtLjExNS0uNTA2IDEuMzQ5IDEuMzQ5IDAgMCAwLS4yMjUtLjQwOS45NDguOTQ4IDAgMCAwLS4zNTQtLjI3NiAxLjExIDEuMTEgMCAwIDAtLjQ5Mi0uMTAxLjk3Ljk3IDAgMCAwLS41MTUuMTI5IDEuMDQ2IDEuMDQ2IDAgMCAwLS4zNS4zNTRjLS4wODkuMTUtLjE1NS4zMjUtLjE5Ny41MjQtLjA0My4yLS4wNjUuNDEyLS4wNjUuNjRabTkuOTQzIDEuNTMxVjM5LjEzYzAtLjE3OC0uMDMzLS4zMzEtLjA5Ny0uNDZhLjY3OC42NzggMCAwIDAtLjI5NC0uMjk5IDEuMDEgMS4wMSAwIDAgMC0uNDg4LS4xMDZjLS4xODEgMC0uMzM3LjAzLS40NjkuMDkyYS43Ni43NiAwIDAgMC0uMzA4LjI0OC42MDMuNjAzIDAgMCAwLS4xMS4zNTVoLTEuMTA0YzAtLjE5Ny4wNDgtLjM4Ny4xNDMtLjU3LjA5NS0uMTg0LjIzMy0uMzQ5LjQxNC0uNDkzYTIuMDcgMi4wNyAwIDAgMSAuNjQ4LS4zNGMuMjUxLS4wODMuNTMzLS4xMjQuODQ2LS4xMjQuMzc0IDAgLjcwNS4wNjMuOTkzLjE4OC4yOTEuMTI2LjUyLjMxNi42ODUuNTcuMTY5LjI1Mi4yNTMuNTY4LjI1My45NDh2Mi4yMTFjMCAuMjI3LjAxNi40MzEuMDQ2LjYxMi4wMzQuMTc4LjA4MS4zMzMuMTQzLjQ2NHYuMDc0aC0xLjEzNmExLjk4NiAxLjk4NiAwIDAgMS0uMTI0LS40NTUgMy42NTkgMy42NTkgMCAwIDEtLjA0MS0uNTQzWm0uMTYtMi4wMjguMDEuNjg2aC0uNzk2Yy0uMjA1IDAtLjM4Ni4wMi0uNTQyLjA2YTEuMTEgMS4xMSAwIDAgMC0uMzkxLjE2NS43MjIuNzIyIDAgMCAwLS4yMzUuMjY3Ljc4Mi43ODIgMCAwIDAtLjA3OC4zNTRjMCAuMTMyLjAzMS4yNTIuMDkyLjM2M2EuNjQuNjQgMCAwIDAgLjI2Ny4yNTMuOTM0LjkzNCAwIDAgMCAuNDMyLjA5MiAxLjIxNyAxLjIxNyAwIDAgMCAuOTk4LS40ODMuNzUuNzUgMCAwIDAgLjE1Ni0uMzlsLjM1OS40OTFjLS4wMzcuMTI2LS4xLjI2LS4xODkuNDA1YTEuOTY0IDEuOTY0IDAgMCAxLS4zNDkuNDE0IDEuNzMgMS43MyAwIDAgMS0xLjE5MS40NDFjLS4zMjUgMC0uNjE1LS4wNjQtLjg2OS0uMTkzYTEuNTQxIDEuNTQxIDAgMCAxLS41OTgtLjUyOSAxLjM3MiAxLjM3MiAwIDAgMS0uMjE2LS43NTljMC0uMjYzLjA0OS0uNDk2LjE0Ny0uNjk4LjEwMS0uMjA2LjI0OC0uMzc3LjQ0Mi0uNTE1LjE5Ni0uMTM4LjQzNS0uMjQzLjcxNy0uMzEzLjI4Mi0uMDc0LjYwNC0uMTEuOTY1LS4xMWguODY5Wm01LjQ5NC0xLjk0OWgxLjAwNnY0LjgzN2MwIC40NDgtLjA5NS44MjgtLjI4NSAxLjE0LS4xOS4zMTMtLjQ1NS41NS0uNzk1LjcxMy0uMzQuMTY2LS43MzQuMjQ4LTEuMTgyLjI0OGEyLjc3IDIuNzcgMCAwIDEtMS4zMDYtLjM1IDEuNjggMS42OCAwIDAgMS0uNTM4LS40NzNsLjUyLS42NTNjLjE3OC4yMTIuMzc0LjM2Ny41ODkuNDY1LjIxNC4wOTguNDM5LjE0Ny42NzUuMTQ3LjI1NSAwIC40NzEtLjA0OC42NDktLjE0M2EuOTYxLjk2MSAwIDAgMCAuNDE4LS40MDljLjA5OC0uMTguMTQ3LS40MDEuMTQ3LS42NjJ2LTMuNzMzbC4xMDItMS4xMjdabS0zLjM4IDIuNTQzdi0uMDk3YzAtLjM3Ny4wNDYtLjcyLjEzOC0xLjAzYTIuNDUgMi40NSAwIDAgMSAuMzk1LS44MDVjLjE3Mi0uMjI2LjM4LS40LjYyNi0uNTIuMjQ1LS4xMjIuNTIyLS4xODMuODMyLS4xODMuMzIyIDAgLjU5Ni4wNTguODIzLjE3NS4yMy4xMTYuNDIxLjI4My41NzUuNS4xNTMuMjE2LjI3My40NzMuMzU4Ljc3My4wODkuMjk4LjE1NS42MjkuMTk4Ljk5NHYuMzA4Yy0uMDQuMzU1LS4xMDcuNjgtLjIwMi45NzRhMi42OTcgMi42OTcgMCAwIDEtLjM3Ny43NjRjLS4xNTcuMjE0LS4zNS4zOC0uNTguNDk2YTEuNzM3IDEuNzM3IDAgMCAxLS44MDQuMTc1Yy0uMzA0IDAtLjU3OC0uMDYzLS44MjMtLjE4OWExLjg3OSAxLjg3OSAwIDAgMS0uNjI2LS41MjggMi41MTggMi41MTggMCAwIDEtLjM5NS0uOCAzLjUyIDMuNTIgMCAwIDEtLjEzOC0xLjAwN1ptMS4xMDgtLjA5N3YuMDk3YzAgLjIyNy4wMjIuNDM4LjA2NC42MzQuMDQ2LjE5Ni4xMTUuMzcuMjA3LjUyLjA5NS4xNDcuMjE1LjI2My4zNTkuMzUuMTQ3LjA4Mi4zMi4xMjMuNTIuMTIzLjI2IDAgLjQ3My0uMDU1LjYzOS0uMTY1LjE2OC0uMTEuMjk3LS4yNi4zODYtLjQ0Ni4wOTItLjE5LjE1Ni0uNDAyLjE5My0uNjM1di0uODMyYTIuMDI1IDIuMDI1IDAgMCAwLS4xMTUtLjUwNiAxLjM0OSAxLjM0OSAwIDAgMC0uMjI1LS40MDkuOTQyLjk0MiAwIDAgMC0uMzU5LS4yNzYgMS4xOTMgMS4xOTMgMCAwIDAtLjUxLS4xMDFjLS4xOTkgMC0uMzczLjA0My0uNTIuMTI5YTEuMDU1IDEuMDU1IDAgMCAwLS4zNjMuMzU0IDEuNzUgMS43NSAwIDAgMC0uMjA3LjUyNGMtLjA0Ni4yLS4wNjkuNDEyLS4wNjkuNjRabTQuNDEzLjA5N3YtLjEwNmMwLS4zNTkuMDUyLS42OTEuMTU2LS45OTguMTA0LS4zMS4yNTQtLjU3OC40NTEtLjgwNGEyLjAzIDIuMDMgMCAwIDEgLjcyNi0uNTM0Yy4yODgtLjEyOS42MTMtLjE5My45NzUtLjE5My4zNjUgMCAuNjkuMDY0Ljk3NS4xOTMuMjg4LjEyNi41MzEuMzA0LjczMS41MzQuMTk5LjIyNi4zNTEuNDk1LjQ1NS44MDQuMTA0LjMwNy4xNTYuNjQuMTU2Ljk5OHYuMTA2YzAgLjM1OC0uMDUyLjY5LS4xNTYuOTk3YTIuMzg3IDIuMzg3IDAgMCAxLS40NTUuODA1Yy0uMi4yMjctLjQ0Mi40MDUtLjcyNy41MzNhMi4zOCAyLjM4IDAgMCAxLS45Ny4xODljLS4zNjUgMC0uNjkxLS4wNjMtLjk3OS0uMTg5YTIuMTEgMi4xMSAwIDAgMS0uNzI3LS41MzMgMi4zODcgMi4zODcgMCAwIDEtLjQ1NS0uODA1IDMuMDg2IDMuMDg2IDAgMCAxLS4xNTYtLjk5N1ptMS4xMDgtLjEwNnYuMTA2YzAgLjIyMy4wMjMuNDM1LjA2OS42MzQuMDQ2LjIuMTE4LjM3NC4yMTYuNTI0LjA5OC4xNS4yMjQuMjY5LjM3Ny4zNTQuMTUzLjA4Ni4zMzYuMTMuNTQ3LjEzLjIwNSAwIC4zODMtLjA0NC41MzMtLjEzLjE1NC0uMDg1LjI3OS0uMjAzLjM3Ny0uMzU0LjA5OC0uMTUuMTcxLS4zMjUuMjE3LS41MjQuMDQ5LS4yLjA3My0uNDEuMDczLS42MzR2LS4xMDZjMC0uMjItLjAyNC0uNDMtLjA3My0uNjI1YTEuNjEgMS42MSAwIDAgMC0uMjIxLS41MyAxLjA1NyAxLjA1NyAwIDAgMC0uOTE1LS40OTFjLS4yMDkgMC0uMzg5LjA0NC0uNTQzLjEzMy0uMTUuMDg2LS4yNzQuMjA2LS4zNzIuMzU5YTEuNjcgMS42NyAwIDAgMC0uMjE2LjUyOSAyLjczMyAyLjczMyAwIDAgMC0uMDY5LjYyNVoiLz48cGF0aCBmaWxsPSIjM0I5MTFDIiBkPSJtNDYuMTc0IDk5Ljc0NC0yLjYyNS0uNjQyIDEuMDgtMTAuMDA4SDU1LjM2djIuNzg5aC03Ljk5OGwtLjU0NyA0Ljg4Yy4zMS0uMTgyLjczOS0uMzYgMS4yODYtLjUzMi41NDYtLjE4MyAxLjE3LS4yNzQgMS44NzMtLjI3NC45MzggMCAxLjc3Ny4xNTUgMi41MTUuNDY1Ljc0OC4zIDEuMzgxLjc0MyAxLjkgMS4zMjYuNTIuNTc0LjkxNyAxLjI3NiAxLjE5IDIuMTA1LjI3My44MjEuNDEgMS43NDYuNDEgMi43NzZhNy45NiA3Ljk2IDAgMCAxLS40MSAyLjU4NCA2LjAyMiA2LjAyMiAwIDAgMS0xLjIwMyAyLjExOWMtLjUzOC42MTEtMS4yMTcgMS4wODktMi4wMzcgMS40MzYtLjgxMS4zMzctMS43NzMuNTA1LTIuODg1LjUwNS0uODMgMC0xLjYyNy0uMTE4LTIuMzkyLS4zNTVhNi41MTggNi41MTggMCAwIDEtMi4wMzgtMS4wOCA1LjUxMiA1LjUxMiAwIDAgMS0xLjQ0OS0xLjc3N2MtLjM2NC0uNzExLS41NzQtMS41MjctLjYyOS0yLjQ0OGgzLjIyN2MuMDgyLjY0Ny4yNiAxLjE5OS41MzMgMS42NTUuMjgzLjQ0Ni42NTYuNzg4IDEuMTIxIDEuMDI1LjQ2NS4yMzcgMS4wMDMuMzU1IDEuNjEzLjM1NS41NTYgMCAxLjAzNS0uMDk1IDEuNDM2LS4yODcuNDAxLS4yLjczNC0uNDgzLjk5OC0uODQ3LjI3My0uMzc0LjQ3NC0uODEyLjYwMi0xLjMxM2E2LjMzMSA2LjMzMSAwIDAgMCAuMjA1LTEuNjY4YzAtLjU4My0uMDc4LTEuMTE2LS4yMzMtMS41OTlhMy40NzYgMy40NzYgMCAwIDAtLjY3LTEuMjU4IDIuODY3IDIuODY3IDAgMCAwLTEuMTA3LS44MmMtLjQ0Ny0uMjAxLS45NjItLjMwMS0xLjU0NS0uMzAxLS43ODQgMC0xLjM4Ni4xMTQtMS44MDUuMzQxLS40MS4yMjgtLjc5My41MS0xLjE0OC44NDhabTI1LjcwMy0yLjM2NXYzLjI1NGMwIDEuNTU4LS4xNTUgMi44ODktLjQ2NSAzLjk5Mi0uMyAxLjA5NC0uNzM4IDEuOTgyLTEuMzEyIDIuNjY2YTUuMTQ3IDUuMTQ3IDAgMCAxLTIuMDY1IDEuNTA0Yy0uNzkzLjMxOS0xLjY4MS40NzgtMi42NjYuNDc4LS43ODQgMC0xLjUxMy0uMS0yLjE4Ny0uM2E1LjM0MyA1LjM0MyAwIDAgMS0xLjgwNS0uOTQ0Yy0uNTM4LS40MjgtLjk5OC0uOTgtMS4zOC0xLjY1NC0uMzc1LS42ODQtLjY2Ni0xLjQ5OS0uODc2LTIuNDQ3LS4yLS45NDgtLjMtMi4wNDYtLjMtMy4yOTV2LTMuMjU0YzAtMS41NjguMTU0LTIuODkuNDY0LTMuOTY1LjMxLTEuMDg1Ljc1Mi0xLjk2NCAxLjMyNi0yLjYzOWE1LjA1MSA1LjA1MSAwIDAgMSAyLjA1MS0xLjQ5Yy44MDItLjMxIDEuNjk1LS40NjUgMi42OC0uNDY1Ljc5MyAwIDEuNTIyLjEgMi4xODcuMzAxYTUuMTIyIDUuMTIyIDAgMCAxIDEuODA1LjkxNmMuNTM4LjQyLjk5My45NjYgMS4zNjcgMS42NC4zODMuNjY2LjY3NSAxLjQ3My44NzUgMi40Mi4yLjk0LjMwMSAyLjAzMy4zMDEgMy4yODJabS0zLjI5NSAzLjcxOXYtNC4yMTFjMC0uNzkzLS4wNDUtMS40OS0uMTM3LTIuMDkyLS4wOS0uNjEtLjIyOC0xLjEyNi0uNDEtMS41NDUtLjE3My0uNDI4LS4zOTItLjc3NS0uNjU2LTEuMDRhMi4zMSAyLjMxIDAgMCAwLS45MDItLjU4NyAzLjE4NyAzLjE4NyAwIDAgMC0xLjEzNS0uMTkxYy0uNTEgMC0uOTY2LjEtMS4zNjcuMy0uNDAxLjE5Mi0uNzM5LjUwMi0xLjAxMi45My0uMjczLjQyOC0uNDgzLjk5NC0uNjI5IDEuNjk1LS4xMzcuNjkzLS4yMDUgMS41MzYtLjIwNSAyLjUzdjQuMjExYzAgLjgwMi4wNDYgMS41MDguMTM3IDIuMTE5LjA5LjYxLjIyNyAxLjEzNS40MSAxLjU3Mi4xODIuNDI4LjQuNzg0LjY1NiAxLjA2Ni4yNjQuMjc0LjU2NS40NzQuOTAyLjYwMmEzLjI1IDMuMjUgMCAwIDAgMS4xMzUuMTkxYy41MiAwIC45OC0uMSAxLjM4MS0uMy40MDEtLjIwMS43MzgtLjUyIDEuMDEyLS45NTcuMjczLS40NDcuNDc4LTEuMDI2LjYxNS0xLjczNy4xMzctLjcxMS4yMDUtMS41NjMuMjA1LTIuNTU2Wm0yMy4xNi02Ljg5MWgzLjMwOVYxMDloLTMuMDQ5bC0uMjYtMy40MThWOTQuMjA3Wm0uNDc5IDcuOTE2IDEuMjk4LS4wMjdjMCAxLjAzOS0uMSAxLjk5Ni0uMyAyLjg3MS0uMTkyLjg3NS0uNDkyIDEuNjM2LS45MDMgMi4yODNhNC4zMjIgNC4zMjIgMCAwIDEtMS41MyAxLjUwNGMtLjYyLjM1NS0xLjM1NC41MzMtMi4yMDIuNTMzLS42NzUgMC0xLjI4LS4wOTYtMS44MTgtLjI4N2EzLjYwOSAzLjYwOSAwIDAgMS0xLjM5NS0uOTNjLS4zOTItLjQxOS0uNzE1LS45NjYtLjk3LTEuNjRsLS40MjQtMy43MzNoMS44NzNjMCAuODIxLjA2OCAxLjQ5MS4yMDUgMi4wMS4xMzYuNTIuMzI4LjkyMS41NzQgMS4yMDMuMjQ2LjI3NC41MzMuNDY1Ljg2MS41NzQuMzM4LjExLjcwMi4xNjQgMS4wOTQuMTY0LjcyIDAgMS4zMTMtLjEwOSAxLjc3Ny0uMzI4YTIuNjg0IDIuNjg0IDAgMCAwIDEuMTA4LS45NDNjLjI3My0uNDEuNDY1LS44ODkuNTc0LTEuNDM2YTguNzI1IDguNzI1IDAgMCAwIC4xNzgtMS44MThabS05LjY1My03LjkxNmgzLjI4MnYyMC40ODFoLTMuMjgyVjk0LjIwN1ptMjUuODEzIDBoMi45OTR2MTQuMzgzYzAgMS4zMzEtLjI4MyAyLjQ2MS0uODQ4IDMuMzlhNS40NDUgNS40NDUgMCAwIDEtMi4zNjUgMi4xMmMtMS4wMTIuNDkyLTIuMTgzLjczOC0zLjUxNC43MzhhOC4yNDggOC4yNDggMCAwIDEtMS44ODYtLjI0NiA3Ljc0MiA3Ljc0MiAwIDAgMS0xLjk5Ni0uNzkzIDQuOTYgNC45NiAwIDAgMS0xLjYtMS40MDhsMS41NDUtMS45NDJjLjUyOS42MjkgMS4xMTIgMS4wODkgMS43NSAxLjM4MWE0Ljc3NSA0Ljc3NSAwIDAgMCAyLjAxLjQzOGMuNzU2IDAgMS4zOTktLjE0MiAxLjkyNy0uNDI0YTIuODY4IDIuODY4IDAgMCAwIDEuMjQ1LTEuMjE3Yy4yOTEtLjUzOC40MzctMS4xOTQuNDM3LTEuOTY5Vjk3LjU1N2wuMzAxLTMuMzVabS0xMC4wNDkgNy41NjF2LS4yODhjMC0xLjEyMS4xMzctMi4xNDIuNDEtMy4wNjIuMjc0LS45My42NjYtMS43MjcgMS4xNzYtMi4zOTNhNS4xMjUgNS4xMjUgMCAwIDEgMS44NTktMS41NDRjLjczLS4zNjUgMS41NTQtLjU0NyAyLjQ3NS0uNTQ3Ljk1NyAwIDEuNzczLjE3MyAyLjQ0Ny41MmE0LjYyMiA0LjYyMiAwIDAgMSAxLjcwOSAxLjQ5Yy40NTYuNjM3LjgxMSAxLjQwMyAxLjA2NyAyLjI5Ni4yNjQuODg0LjQ2IDEuODY5LjU4NyAyLjk1M3YuOTE2YTE0Ljc5MyAxNC43OTMgMCAwIDEtLjYwMSAyLjg5OWMtLjI4My44NzUtLjY1NiAxLjYzMS0xLjEyMSAyLjI2OWE0Ljc4MSA0Ljc4MSAwIDAgMS0xLjcyMyAxLjQ3N2MtLjY3NC4zNDYtMS40NzIuNTE5LTIuMzkyLjUxOS0uOTAzIDAtMS43MTgtLjE4Ni0yLjQ0OC0uNTZhNS41ODIgNS41ODIgMCAwIDEtMS44NTktMS41NzJjLS41MS0uNjc1LS45MDItMS40NjgtMS4xNzYtMi4zNzlhMTAuNDggMTAuNDggMCAwIDEtLjQxLTIuOTk0Wm0zLjI5NS0uMjg4di4yODhjMCAuNjc0LjA2NCAxLjMwMy4xOTEgMS44ODYuMTM3LjU4NC4zNDIgMS4wOTkuNjE2IDEuNTQ1LjI4Mi40MzguNjM4Ljc4NCAxLjA2NiAxLjAzOS40MzguMjQ2Ljk1Mi4zNjkgMS41NDUuMzY5Ljc3NSAwIDEuNDA4LS4xNjQgMS45LS40OTIuNTAyLS4zMjguODg0LS43NyAxLjE0OS0xLjMyNmE2LjcxIDYuNzEgMCAwIDAgLjU3NC0xLjg4N3YtMi40NzRhNi4wNjQgNi4wNjQgMCAwIDAtLjM0Mi0xLjUwNCA0LjAxNCA0LjAxNCAwIDAgMC0uNjctMS4yMTcgMi43OTUgMi43OTUgMCAwIDAtMS4wNjYtLjgyYy0uNDI5LS4yLS45MzQtLjMwMS0xLjUxOC0uMzAxLS41OTIgMC0xLjEwNy4xMjctMS41NDUuMzgzYTMuMTQ2IDMuMTQ2IDAgMCAwLTEuMDggMS4wNTMgNS4yMjMgNS4yMjMgMCAwIDAtLjYxNSAxLjU1OCA4LjQyMSA4LjQyMSAwIDAgMC0uMjA1IDEuOVptMjEuODItMTIuMzg2LTcuNzUyIDIxLjYxNWgtMi41ODRsNy43NjYtMjEuNjE1aDIuNTdabTUuNTc4IDguMTJWMTA5aC0zLjI5NVY5NC4yMDdoMy4xMDRsLjE5MSAzLjAwOFptLS41MzMgMy44NDMtMS4xMjEtLjAxNGMwLTEuMDIxLjEyOC0xLjk2NC4zODMtMi44My4yNTUtLjg2Ni42MjktMS42MTggMS4xMjEtMi4yNTZhNC45MjggNC45MjggMCAwIDEgMS44MzItMS40OWMuNzM4LS4zNTYgMS41OTEtLjUzMyAyLjU1Ny0uNTMzLjY3NCAwIDEuMjg5LjEgMS44NDUuM2EzLjY1OCAzLjY1OCAwIDAgMSAxLjQ2My45MTZjLjQyLjQyLjczOS45NTcuOTU3IDEuNjE0LjIyOC42NTYuMzQyIDEuNDQ5LjM0MiAyLjM3OVYxMDloLTMuMjk1di05LjU3YzAtLjcyLS4xMDktMS4yODYtLjMyOC0xLjY5NmExLjgyIDEuODIgMCAwIDAtLjkxNi0uODc1Yy0uMzkyLS4xODItLjg2MS0uMjczLTEuNDA4LS4yNzMtLjYyIDAtMS4xNDkuMTE4LTEuNTg2LjM1NS0uNDI4LjIzNy0uNzc5LjU2MS0xLjA1My45NzEtLjI3My40MS0uNDc0Ljg4NC0uNjAxIDEuNDIyYTcuNDI0IDcuNDI0IDAgMCAwLS4xOTIgMS43MjNabTkuMTc0LS44NzUtMS41NDUuMzQxYzAtLjg5My4xMjMtMS43MzYuMzY5LTIuNTI5YTYuNDQ1IDYuNDQ1IDAgMCAxIDEuMTA4LTIuMTA1IDUuMTk0IDUuMTk0IDAgMCAxIDEuODE4LTEuNDM2Yy43Mi0uMzQ2IDEuNTQ1LS41MiAyLjQ3NS0uNTIuNzU2IDAgMS40MzEuMTA1IDIuMDIzLjMxNS42MDIuMiAxLjExMi41MiAxLjUzMS45NTcuNDIuNDM4LjczOSAxLjAwNy45NTcgMS43MS4yMTkuNjkyLjMyOCAxLjUzLjMyOCAyLjUxNVYxMDloLTMuMzA4di05LjU4NGMwLS43NDctLjExLTEuMzI2LS4zMjgtMS43MzYtLjIxLS40MS0uNTExLS42OTMtLjkwMy0uODQ4LS4zOTItLjE2NC0uODYxLS4yNDYtMS40MDgtLjI0Ni0uNTEgMC0uOTYxLjA5Ni0xLjM1My4yODdhMi42ODMgMi42ODMgMCAwIDAtLjk3MS43OCAzLjQ4NSAzLjQ4NSAwIDAgMC0uNjAyIDEuMTM0IDQuODg4IDQuODg4IDAgMCAwLS4xOTEgMS4zOTVabTE0LjQ1MS02LjY1OWgxLjE5Yy4zOTIgMCAuNzE1LS4wNTQuOTctLjE2NC4yNTYtLjExOC40NDctLjI3OC41NzUtLjQ3OC4xMzYtLjIuMjA1LS40MzguMjA1LS43MTEgMC0uMzI4LS4xMjgtLjYxNS0uMzgzLS44NjEtLjI0Ni0uMjU2LS42NjEtLjM4My0xLjI0NC0uMzgzLS40NDcgMC0uODI1LjA5Ni0xLjEzNS4yODctLjMwMS4xODItLjQ1MS40NDItLjQ1MS43OGgtMi40ODljMC0uNjQ4LjE4My0xLjIuNTQ3LTEuNjU1LjM2NS0uNDU2Ljg1My0uODAyIDEuNDYzLTEuMDRhNS40NzIgNS40NzIgMCAwIDEgMS45OTYtLjM1NWMuODMgMCAxLjU1OS4xMTkgMi4xODguMzU2LjYyOS4yMjggMS4xMTYuNTcgMS40NjMgMS4wMjUuMzU1LjQ1Ni41MzMgMS4wMjEuNTMzIDEuNjk1IDAgLjUyLS4xNjQuOTgtLjQ5MiAxLjM4MS0uMzE5LjQwMS0uNzY2LjcxNi0xLjM0Ljk0NC0uNTY1LjIxOS0xLjIzMS4zMjgtMS45OTYuMzI4aC0xLjZ2LTEuMTQ5Wm0wIDEuNzFWOTQuMTFoMS42Yy44NDcgMCAxLjU3Mi4xIDIuMTc0LjMwMS42MDEuMTkxIDEuMDU3LjQ4OCAxLjM2Ny44ODkuMzE5LjQuNDc4LjkyLjQ3OCAxLjU1OCAwIC42NzUtLjE5MSAxLjI1LS41NzQgMS43MjMtLjM4My40NzQtLjkwNy44MzQtMS41NzIgMS4wOC0uNjU2LjI0Ni0xLjM5NS4zNjktMi4yMTUuMzY5LS42ODQgMC0xLjM0OS0uMTEtMS45OTYtLjMyOGEzLjcwNiAzLjcwNiAwIDAgMS0xLjU4Ni0xLjA2NmMtLjQxLS40ODMtLjYxNS0xLjExNy0uNjE1LTEuOWgyLjUwMmMwIC4zNTUuMTY0LjY2LjQ5Mi45MTUuMzI4LjI1Ni43Ny4zODMgMS4zMjYuMzgzLjU5MiAwIDEuMDMtLjEyNyAxLjMxMy0uMzgzLjI5MS0uMjY0LjQzNy0uNTkyLjQzNy0uOTg0IDAtLjM0Ni0uMDgyLS42MjQtLjI0Ni0uODM0YTEuMjc1IDEuMjc1IDAgMCAwLS42Ny0uNDUxYy0uMjkyLS4xLS42MzMtLjE1LTEuMDI1LS4xNWgtMS4xOVoiLz48ZGVmcz48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2MiIHRyYW5zZm9ybT0ibWF0cml4KC4wMDUwMyAwIDAgLjAwNjI5IDAgMCkiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjE5Ny40MTciIGhlaWdodD0iMTU3LjQxNyIgeD0iMS4yOTIiIHk9IjEuMjkyIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIyLjM1NCIvPjxmZUNvbXBvc2l0ZSBpbjI9IlNvdXJjZUFscGhhIiBvcGVyYXRvcj0iaW4iIHJlc3VsdD0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl8xMTA5XzM2MzMiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzExMDlfMzYzMyIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxpbWFnZSB4bGluazpocmVmPSJkYXRhOmltYWdlL2pwZWc7YmFzZTY0LC85ai80QUFRU2taSlJnQUJBUUFBQVFBQkFBRC8yd0JEQUFJQkFRRUJBUUlCQVFFQ0FnSUNBZ1FEQWdJQ0FnVUVCQU1FQmdVR0JnWUZCZ1lHQndrSUJnY0pCd1lHQ0FzSUNRb0tDZ29LQmdnTERBc0tEQWtLQ2dyLzJ3QkRBUUlDQWdJQ0FnVURBd1VLQndZSENnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ3Ivd2dBUkNBQ2ZBTWNEQVJFQUFoRUJBeEVCLzhRQUhRQUFBZ0lEQVFFQkFBQUFBQUFBQUFBQUJBVURCZ0lIQ0FFQUNmL0VBQnNCQUFJREFRRUJBQUFBQUFBQUFBQUFBQUlEQVFRRkFBWUgvOW9BREFNQkFBSVFBeEFBQUFDRDMzem5QZ3hpUllJTG1ROXhFQkhNK1IzMGRGTWdpeGJCclJZSjB3Q1lZa0hCeGhPSXpoeGV4eFVjeUZaSFJ0YmR4V2tybWdXMHBZUUVQZDVNWTlBc0VEeHpqR0hDRERWUXVTUllWQXdHQ2dnOEk2QVdRcmtVR0RDWGduSkhieDlGNXlib2xrRHBXMWhPMHFqTE1rbGhMTVhaM3pqK2cyQlR0OEY3bURSTEtFcnh3ck9YQTJwdGtKcTRSbUVURkV4d1lNTElnTEZaZGU3L0FKRVpveThKMEM2QSsvUEVmUWpVbFJMT2UrVGFJRXFTeWxvTzlScExJV1QxVmFOT3Nwcmwrb3ZNRjhNRkF3bHNIV1k0TmlGc0lIK2lWL3pNL0NlSkhBZlJ2blBaYll5ZDZxV0tWTWRSMWpab2FiMWNtdHdkYmdxbEpMWU93MktkOHNab0JpSndqekNTZXE4c1dyYUVwb0lzOVUvOUFKUlB3TTAyZDk0UHIzcUxTNlZhMnQ1V3JybWVuc1ZhazlFaHJJR1daSkdtVlpMWG1DN2hLRXNJRWZoQjdsVUVqbVZZR3BXN3Joc0piRlc2NSt2MHY1NzJVSEJSYkdmekxyWW5ycXR6NFlDaEhNYW9samlSdjgxSUpVdklWTXdKeEFjYUkxVmhkZytVaXFkbURFNHpmQ3ZJRVBFUS9xNnZhaEpmVUdadTZjME1YblRXeFc4VHJ4eXE0VVI4U0pWaXAxN0ZsWlZHNHBVdnF5N0NoYjY2dHV0VjNyUTJudWUzbGJNV2FlVjgwSjFuWURNQkluTTJ3MnAzSlVhTElmbTgxNmVZTkZZc0VmazNDTXU4cGNOT3VwRkloTEFYQjFXQTUrNHZlZ3RjVm8zTUtGallTQVI3ZE10UlZZaWdZTCtRL1YzTTBkRVhjZm1MbmFYNXAxYzdsak5wdGhaS25GY0FUcHBsZ202K3J6NWpjSnpnam1mTzdLSW44OXBVak0wLzBoOVo0N1dYbE56YTN0dlArekZlaWF5VVF4S3lEcm1UWkN5Ykd0dEt2UEhWK3c2MTFVcFhHb2ZJMHRzVmNhcFluMHlzVFVFc3JzSlNjbWFuZ2JuZVdRL1IyblU3UDNzamZ1aFRKQW9waE1NY29ZV2h5UFF2YTYwaEFjdXdwR3ZQWStRaXJXbkdjQ1kzMjZ1cEEwd1NMQ1pra2J4Y3BPV0RXZkxiblE5S1ZBanV6U29kU2Vwd0IrNnZjR3RRN1RGWjFJRnV2SWFDa2tVdVlxbFJCckdOeTZQWjRjaVBoZnhjY3lNQjRZaUxxWFRzVy8xZFZvVUR5M290MWVsd2Q5ZlF2SnNtTCtqbFVjdjZLY0pleEJrY3FFN1YwZlRDV1lWRUE4eEZQWENTY2pIblRTRVc0cyszd3Y1YjNOYTg5bzJlM2hlVWJDM1RyZm94Nzd5VzBOWE95N2lJNE9SYlFSVVNIMFlkMmNkak1TUktjaEdnY083NlpnN3VaOEgxc1pVaHFrODQ0dnBkWTVGcHZWSERYcHJyQ2V0OTNHN0c5Rml1elVaRXp6d2RkcmFoY0VhckM3VitNSW81ZE1DWk45VnM1a0hkQkVyT25VZVZ0YUQ4NzZEN0h1NkxadFZ4VUQzZlBpU2N3VGY3ZFR2cll6M0VpMnNWOXZhMk11ejcxRStWZSswcGw3T2ZzTUYvNkx6OXQyTVozWnE2WitYKzQyYjdMellIcE1XalorbkZJOC9lUzkxb25NMDFMQXIybFJhVTdhRFV3dk9KWFo2M1ZEM1JWZFphZXRmNzNtM1VaZFhmRmMrY2ZSR3U5NW10N1hLcWx3WE5RallyMGJGNTloNTU3Ylpxdk0zZGZEME5QV0Vpa0xZVk9zOGVpczYrYk10cjJxVVVUT1RMb2E3Y2l0VHRmUFUwTlRkdmpkY0hYclVhM08xdkZ0b1BvNnRXMGtySWZZVVVhb2QrdDZBS3JxckJtWFNFaXF0akpDODRXazBVTmFqMk5abVBDVXpnTDlYRWxXakV2THR5aHZQeWVqcTdVQmYzVDUxMFVxNmIwVkJFQmtvT0ZoanM3SUpNUklGcGVCU2JYTDBlLzhRQUxoQUFBZ0lCQXdNREJBRUZBQU1BQUFBQUFnTUJCQVVBRVJNR0VoUWhJakVIRlNOQk1oWWtRbEZTRUJjMS85b0FDQUVCQUFFSUFvcmY2NFkxNDMrcFFVYWxNVDgrS0U2aWh2cWFPMnZGbU5jRytwcWFLbm9xazZKRTZsT3VIUkkxd2E0dGNldXlkUnZvWjBFaEh6dXVkTGE0ZFEvZjVFOTlCdlBvWGp4UDhmSDIxd3hyaGpVb2pYWnRvVWwyOSt1TFVxMGF4MGF4MFN4MUs0MXhhNG8xeERxVlJxVmE0cDF4bHFJTFVET3VQVUwwQVRHbHhPc0xpOGpsamxkSzFnYlZJdTI1T0dOaEN1RDZQdXhZaXRMZWg4TTZrcW91N2o4ZjRIaDErbzhHTkI4QlViam5oSHNpcGFNdHRPbzl1blVCZ084VFZJL0hIT3V6WEhxWW4veE8ydHRSdnFjVDIvSGdGR29vRm9jZHZxdGhiTmt1eXQwN2hneE9KQmFiZEhtWE1uS0s5UW9NVlhCYTN2WXhxaUtaQzNZbmFaREszRnVLUk0yVmV6MElVN2J4Y0RZTzZDUDAyMGF5T2ZhU0hSNnpLajFLaTFLNTF4enJqMXhGcmlMWGhUL2tOQU5EajFhWGp2OEFucG5GaFJYTEdHeUlqYUdYTnZiRmpqWnVSMzdvcjNoZG5JM1NpZU5HVnVKWElUWnNHNHZjMU5oZnYxRGJYSnRLaUowOGVsNGxKQnRKNEd0RTYrMVZSOU5IaWt6cHVOUUVlcDFhMGFtblgwZElQMTRZejZSOXUvNmpoMFBIcXVnSHRoZWtWMWVnYWpaWHBEM3h0cTNmVXRtMFB5ZmsyL0VVL0huTWJhKzFzRTl5c1lubGpZRjRSYTFSb0t5K3oxYlRRMytmMnVxUDhHWStZL2lOWXhuWFpJL01tUDdMakxYOXNHallyOU1rWjBVam93aWRXN3FwOWlRc1dQOEFISE5jdDhHNnMwQ1ZCYU4wTW50SE1lVXBNOE5xMWt4MkZ1TjUwdEt5N3lwblhMUDd0WkFhNFRzN3F2SWdXeTZQVlBsSHhXZkkvd0J5Mk5HVTZMdW5SQStJN3BjbXdvUllaR2VyT1ZxVkc4Rm1EN3g3b2t0dm1BWXdKTlpheW5VdFBHaVBhdnJ0UForTmZXOXlYZHJjYjF4Uk9yL2NCMWxRKzRqTmFqZHFaSmZLbnFuRzdMbXlxdGJSWjlFc0Mwb09kejgrZTg5amN4SmJUTVpoS2hrakxxcXo4TC9xOGpuM2oxYlFXdUoxL1dQSVdyUFZlT0NaaEI5V1dJS0lsdldPUUxjVXR6K2FzYmlNZzl4UzV1T3YyOFRQZWdPcXNiWWp0YmozWGI5ZWZ0eHJzUk1oWUdROVlsVWtjYnNjM2puZW9icjhGeU93ejZOTGl2WmZEL1VPaWJvclV2cURtNzJOeEFXYWxYRDVQTUdiMGt5eFVPVldmdVl5TzJpc2N4N1FMRjEveHVoeVNuVmhCL2JVNzI5aW5rQ0lCbFhtRVgycTdKRWtaSlRBN1RPeW52a3dHMEE3dEFyQ2paekxBbHZUTzB2SU54MGx6bHM4dEVkU1pyampTRmtVY2RleWxrZTFKRmFWODdtZnloUmhPK3ZwMWd1bmVuS1FacTE5Ujd1TnRrcVl0WjFWZFpEVXRPYzlzazZqVnNXbWJMMnBWRlNLT2I4czd2RHRzaFFYZHNseHkzVGxpZFg4Y3dTbzhoY2hGMmhGa2UwV2U0UloramlOL2piVVRJL0V5WmwzU01TYStJWE9PdS90Q3owVjRxSmE2S0ZBMlJSQ245T3JMaDVWRjlOc3YraituWFVBSDNhSDZkOVJOTXVRL3B0MUh3aW9HL1RIcW9aOUc0RHhyc3JmYnRwQ3YySmJZSjB6MjFJbFRwdFBwbXg5dVptOTJ4Uk1OZVRLQldhMmxNRHRwVEpybnVHU3FCRVJlcGpBN2JsQmtvdngrWFZiSDV1M21MY0JyZHY4WnhodUR1U0dJZEFTK3prY21aWEc0cEN3clgzQkdRNk55REdWL3R5NGtkUjJhS3drZmwrUnJLR1NMckRyQlVLbEZaTGU5dmtXTGx5TExEMkVpQU9KZDdqclk5U1p4VUZONkpCaDh2S0dwanVxQ1dtUUpSMlRNYi9HTmVxUm12WVlNVjJ4eGRrenZxUGJwZmVINVZWODJZZWpZeTVNOW8zR1dyTEpDRlFGa3Z0UVVzTWx3K2Zmd1RqYllVa1N5QkQ4L2RnbjR0WmhReHZxOWxwc3hNYVpscFNCY2ZtcHN6MjY4Y1FLV1FkWUI5eXBxODhiblhGS21jTllaRVRLUUVTOFk5TjM1ZlZpU01ZY3M2N2V5SDZJVGZYOURxdWNVUnBlRnloakpSR0l5WHJ2R0p5SmV1dkJzSVp3UFhXQ3pUYXpRT0d0WUw3WlJ5emJtUTNYaE16VW9QZW0wbG9XVURZUUF2a2RoKzNoNjkvMmVsdHRwdUNxc0hqMUhSTkh2N3hIb3FsRE9XWTZLeHM3OS84QTYzd3NNN3dWMFJoRmZ3am96Q0pMdlhaNlF3aFJ1UmRNWXI5QmhLb2JxUXJwK2g4YXFZV3BXaiszOGRJL0pWZ21KWk9LeU9Nekp0WGpzNWJxZFAxQ3NXYk9SdTNjbXBsdHRzeWgxNDhYVlEwVEVVVzd6WFJJNHgzSzYzYVBwckpjOUVhbG9XTi94aVdGNlR3bnJZeG5TKzc5bkIvcnRQOEFaQTM5Y1RKMXdmOEFVclhNYmFZb0I5UUZnanFaSGZlTzdmWGRNYTZxNm15T0t6QUxyOVBaV29qL0FPaDlSUEZ1NHRkblNLbFNhRVg4dzY5Tkl2N2VqYm1rdnZCZDJxcDB2MVZzVlJxOFRPa2NnM0dVZkpkanNnZHV2RG1jMmdjVTY5WjlkVFpXb3hBM05TbFhLMmJDZ0xqS2JLdVRoMXo2bCtpZHZwbGhhMGtUQ0xiVU1qOW15QjlZbTNycWZGUm1xc2tBcUtrdG9VWXo1L2J2SE85bnJzcGxEZTZTclJMQ1oySU9HMkVPcUxDYk5aYXJqWVVqRjJxQzRsVEtlVXRVNlFXZGYxS1ZmR3N0UHhmVWh0S0s5Nkc2dEZRbE0rZmxzc284U0N0WjdxYkpOVERhMk55MmRTUFlCZFI1Tk55SUlPcWwzTW9HTnFYY3FpcFhONVc4OTk5dXN3cWl1bWt5VllPOE1lazIrcHFsYTROQnRuTkJYbjh1WTYxdWNwTHg3OG5ZWXlIc2Jaa3ZmSHF6NElEbXRFaVVqSjc2T3gzUlBiM25PKytNYXhMaHN4aGVxYkN1Vmg1U3hiYXNDc25ZN0JWV3IydXBzclJEeFVYZXM3N0RKV1FwM0NUaVczZ3llU2xkYXRMNm1kdDVQSWV6TDVHLzVoaXFnL0ozdDY5YXl2eFVieGc4a1kzWTd5ejlOTmxxd3hmVXRkbEVvZG5zMVF2eXV6WGJuYmpnNHpLK0RYUzAxUDdCbGh1YThmZktHeXMrWFV5dVBjdWQ5dDlNVzFPL0pTcmgyOHhJVkRvWXd3YWMyWmxSWnNPUHhBRFlXN203TlBPOUoxWXMzd1B6YldPaTJXSHN3ZVVVaktadDd4ZWxLTEl1SERxUmR4ZHdiV012WDZTeFZqc2xacjI0OGlhdVY5NjFGYnVwVUhJSVRaR1pjeGt4QndrbDd3ellSSHVDQzFZZHllNG9zUUl6RUQvSFpwQkFSdnFkb25ZWlp5SWhXb0JhazgycTlnWkFxNHdhUS9KRGUwUjc5UmFhZXdOb3ZBTGtyRzFOTnN6Mi9lVTQvQ3ppcXVSdVIyeStwVXMrVjNReTVZQ3dxZkRyZ3gwbXFGNUVWTWp4M3pzWHRXN2VDM2l5Y0JFVExqTDNFSC9lbEV5UHhBRzV6MjY0cDMyMHNwM2tKWnRHOGEvL3hBQS9FQUFDQVFJRUF3VUZCUVFLQXdBQUFBQUJBZ0FERVJJaE1VRUVJbEVRRXpKaGNRVWpRb0dSSUZLaHNjRWtZbkxSRkRBelEwUlRzdUh3OFlLU292L2FBQWdCQVFBSlB3SzhIYU8weG9lMGRvZy9xQkRGaGdnaXdRUWRnTnV0b1phTEIvVUhzRUhhT3loM21FYzNsT0V3bThvMkwrSHpsTTUyNWhwRXc0V0JkL2lhVWtDNGNJWERIdjF5ajNsTHNQWVB0dEZNUXhPeWl6bm9CRXN4Vzcrc0dMMWxKYnJwQm9KVWo5bG8vd0JoWUlPMFFRUTloTWFMenZ2OWd5c1lkOTRZcGlIMGlSaytrcS9TTGVVWlNhVTIra3BNWndwbEl6S0NMUG5FRnV3OWptN2RJWXg5SXRyd2N3TjRrVWZTWlNyS2tQWVlvUHlpajZmWW9BZVloN1RBVEJ6MjB0S1p1MlhTMC9PTEJkdWs0UDhBV2NJMUkvZXdrai9hVyt3aHQxaStMVHM0cW1qblptaHVEb1JCRUpBMUlFVXppMTRndm9LTmphY05XSjh3c1JjL0NxYlJoVElXN1ozajRzd0h6Mmh4QmRjdDQzaDNXZTNLWmNlS24zZ3VEUGFlRlI4UmFWNmpXMEp5dks1dWRRZHBYYnl3aVZQL0FMaXNSZldjVFZ4ZmN0K3NwOG5reHZPL2MvdkhES2V1bk1aU3NSNXppeXBBdmRJU3phc1RyS25KdW16Q0JxSjJ4QzRQem50QlNvK0duVUVyT1BuRGMvZEV3aFI0VUVHSjl6ZWNRZzZKS2JPRnE4OUNuYXpMYVVybzdoVUZLbmNremkrNGVvV3RpQTIyemxHL056TytRdktKVmwyeFdqVy9pTWJFZktMaWR0RVV4YjJGM2JyNWZXWlB4QnhHVTdZMUJXM1dOWmc5bS9RdytvTS82TVBwR3M2dmRSYlVSc1A2UlZGVEZtbXpRWlgwTzByNGJiaHJFVDJvdFJiWmQ2QitjVDV6blB4YlJXWDVTNWw3ZWs0L2hSeDFlbHpJT0pXMU1IYTNXZTFlRjRqQldiQ25lajNZTXFKaUl3OHMxSDRRZ0MzTzdhQVMrZngvRTM4aENEY2E5ZlNWTG5FRFZZYnQvc0psM2RaVnByUDhLK0VucURuQmVtK1REOUlmZlVjbTgxbVIzOHZzbUd6WDE4b1d5RmpQYUlDS2JHeUhMNkNjZFNkYWpLRnJVaUNCZlRGT0pMcHMwNHhOWlVvc0JvTWVzbzA4TGE4Kzg0SVlsYko4WTA2VGc2VnJmNXdsVk1zaXRGNzMrYzkxUTA4T3N1RnR6RW5XSmlWRXVCK1VZbG1CTi9PQUFNNCtjUGlTenpKRCtjdm5yTTBZYzRId21ITGZ5anlrUTI1WFF5eEhVUk1RbE96ZVlpaEZUSVltMW5HUFFUU3ZadzYwVDhMRnpvSjdSUjJ2aERWYVpSbjlMWmJieXdUaHpvVVVjdTNoSkJnZ0VZU29MQ1ZIY2ZkcDZmTXhiRENSWmNyQXp3amxvb05oUEZjRDV3c1h4SEViK0tkRGVXL3NTUjYzbnd0TkRadlNhNlIyVVd6dHZMMzh4RnQ1ZGoySTZSUG1vbFp2L1dMank1VzJsR25RZDc4dkVzMVRQWDR0UFhTY2E0UkNBdEE0NzExNjMzM3RQWi83S3RYOW1xOENXWHVqdmlHOXovMUZQWWw1VEpBR2dNNEMvd0Q0NVRoTEhWcFRXMFA4WnRLdUZkaEI4TjJjN3ovTHdpODNjYlRZV3ptZ2luUHlnT0pkY3BUZTVYVERPQ3FHMzdrNFJobDkyY0kvMGd3dHNwbkdwU05PMk1QcWM3VGdLSEFyd2QvNlJWVUt3ZHRoaXZGNGltYkNuVTRjVnI5NHcxdGY1ZExTcncxQjM5NGFORERoVHl2YldWa3FvNHVHUnJneW1CS0l6MW5EZ2VrVGw2UWZLOG9oaU5MNlNrTEhZUm1IVVNpUHBLR2M5bnJyY2tiemdsUHluQXFQUlp3bWZuT0ZWZXVVb3JsNVFaQVp4UlVGSzNPVnlNN3RXWWU2RnRUT0o3bGx1L0RIeFlUcUpXWEdiTFVaUm1jNXhkWG1vKzhWVjhiMy93QlhTVmNKcGNRYXBGUmdQcjFsVGZrb3JhNXFYL0FlWWxTa2VJUkx0VHBVeXVGZG9oZ1BZcGc3VEdqbnNNSFllemlWVUl0MG9rMkZTLzU1eXIrMkRpTUJjWmhpUitWdGVoaVZHcVVxb0hJZkRmcktOU21CZGFSdGh4ZWVtZnJLTklyYXpkNG1zZENGcTNGMXlOdW92RVN3NVJ5bkM1c1RjL2dKN1BUeFoxS1JzeW5mckVxaHdBS2RTdHhJWmFpWXRBTnJTajNkOXNlTDhmc05ZdWJMZmN5b0ZGc3l4aHpMWVY4ekdHUERpdytYMkd0NTloamRqbnZhWTkyR2V5K2M0d1hvMVFPN3hlOEQrcDFIbktuZVZzQkFybnAwYVZXcUtiRUIzdmhtUWMzUWJTZ0RjRG5Kc1IvT0JWVTVxcVB0SEM4MmZmV3luQ0ExbHFEdStWK2EzWERPTC9wYmNRUWJWL2RzblhMcC93QXppMG5xVXF3U29GYXlpODRkYWRUSzVGVVd6LzUrUFlWRlBjc2JXbFJhMTZ5NExmRm5kQ2ZJemlpUWJwVHJLZGV2MU1HTGlxM05XcTF4WS91Z0Rwa1k2dFNvb01lSCs4T1dWK3Q1dzdNTEUxSzJ5NVhqZUViQzhvbkFuZHN4VnMyQmhRZE0reHlIY1hYbHloL3V5NTlCUEJoM0d4aWpNM0xMSENmZFZ0WmsrbVkxaFBMbGc2UVllYk9NMnQrWnB1Y3pPSnNjVm16MEU5b2xhcnZ5T3kzeGVYNVQyaGpxc0Z0YjgvUFhXY1N5MVVwSmFvcmpDbVcvUXoyMVJKd1dIZGpHY3BRWmtaMHlDZUd5bS8xTXEzUmFHQlhEZUFrMnNCK01xVXJjSFNWYVFBdytZM3psYnU3Z1k2anQvcGxJVWFiMnhXNmRaeHZkSTVKcXNtVnZNeHF0bUo5N1U2aWNiZ0pYRDNnOU5aUXJjU2pMejkrbWJkYzlwWFB1c2tOUWN6RCtjeHJVVUVZdnlqbGlUcmU4VjFHRzNMQjQwNWZXZkZFNXRyNlFOZmN0dkJlTFlrYUdjMThzRXNNSkJJNjV4QmhYVW9OQkIvRlV0YVpMZ0JHRThyM0dSaUxsNGRoNjVUMmhvK1NoNVVTcWpWcWRTdzY1N3k2MGpYMSs0TGZ6bkZobEl4ZDNpNmRaUUZTNjJTdDRpcld5ODVSbzBxMVEvd0JyYkVUOVk5VnFnSHZRY2hpOHJSQmxTZEJkZDlvQVRWVG5XM2h5MUJnN3pIbDUrc09XV0xPQzh6Nm1VeGtMV25US2MzU0l4R21Jdy9oQjRjd0ROOVl2T3g1VDBsOE5yRzBIbFlpVkRoUmJBZEJFSGR0VDk0RzBIbktUY29KeFlzN2ZyS2VKM1VOVWMvQ2RSNngyeE9CM3BPK1VTNHY0UnBQZHI4U2pLNWxVM1RtTnVta28yenk4cHBxSVRmQkdQS0xMQVBNekx6alpPTEhzSW0xclNxd0Iybi8veEFBbkVBRUFBZ0lDQWdJQkJRRUJBUUFBQUFBQkFCRWhNVUZSWVhHQmthRVFzY0hSOE9IeElQL2FBQWdCQVFBQlB5RVF1VFJTNHgyeGN4RWVneXJZd1ZpZVRGOExNSGRRT1Q4UWhaQStHSWJKbnFWbDhKaittbUVibEcvL0FJOHFuaTRJandCTzVYc0l2YTg2WldLWERtSWVvaW1LdTdLKzQyNHlybFNwa2ZVSHFlbXpqek9QUFU5VTRKQWp4UlIrdUFlTWY5S0UwcEQwcTVLQjlweFNEQ0pHK0N4anI5S3dsWUVUL0xiQk42eENpT1F2NG9GelV5TWVINUxOVXk4ejZ6NTN6eFVlV0U5Und3UVVSZzdUMmxFUHVwZWxxL3RqYkg0amtCTGJVclA4NjVXWnVLQnQzU1crMWxVcnVNQlVNSWZOUVZ4cmJjSEVVNWdLSWU0Mmh2b1lDQmR5bDFpMS9aRHJCT1pIWVRyTVlMMURWTjBDYlB1TDcvUlEzbmx2elpUUjFGNFVUUENaaUlHTVZOb1B6TVd6ZU5jVjFSQktybkR1WUZBNUc0VGZMbVpzaVpaU3RueGhMdG44b0ZoVDltYU9UYkJKL0w4czVHSnUxRk1CeG9oZjRSNFJVeGlkSU9JWHJuMkNabWNzOEFKMlA1bW9LOUpxV3lnR25ZTUJRaEhWVGY2T0orSkM3Q1c1ZzFOOVFicEh1RXV5L2VpN2orb1UxSFdUYmx3cFpYS3pTL1VYaldzVERTNi9UNno1SzRpV1RaYkplbFBTaDlvbTcrRVgyZmNZRkRWNi9NVVVUdk5vOXJta2kvRVBkYUs1L0UzQnVMVW4zSzJTMVNWMHczbVgvd0FuUmd3T1dWbXY0Z2tNbG9zWW0yVG5FN05SM3VOZnZXbWgydWoxT2IxVkFMOXh6a1d3dnM4d3ZYZ05ScmlHb3NxTUxlZnpNQVlyb2hNMWkraGwzaDB4ZGFsbU1IYU5mOWdhdnhIQW50Vi8vVVlBYXNjbnpMU0k4Wk1VM0ZEcEdNL0ppajdUQityL0FKc1hQRjBEUDJ5NnFjaVQzUEp0UXZBcU5XZCtwZDR0dmE4c010YkM3ZnFmeVR2aXo0OElVNzd0UG96Rm9oN1M2dHhqbC9xWkFybGMrV1lPUDFTR1U0VUQvVWJIdHBkWVYrZFJWN3VpMnNiY1VTNE8zYWNqMmcwTXQ4UnQvUHhIWlJteU44anlzclVyd1ZpUlZCMlBsMEJLU0lNNmpBd09yb0JEV2xETjRvL3VNZGVlR3FaL0kzTHRxakhWL3dEaEVNS2ViT0NXNmQ4Q1E2cldWVnpkZkhFcGw0SkUyZmN5MjF0L3pyOW85a2RuNURwOGt2Tkx0MjVlNS9ISFptQXVyT2FUd3FZNzNsL3pLTndEQWFENDhSQ3ZVUUl3YnVvVGJUU2kvd0J1WTZDR1FaWWJZcnVQMDBFaUF6aHprbUVrUVcvNmp2TmxVc1BjTmgvTWpwd1UxL3lQRXZsN3kvWHcvZUw1cEFES2VrcjJzd1lGMnlnY2ZqUHVPcUlkbVFoK0l4OVFTM2Y5Snh3eDU0bjRtYldXbmJ2MUZQM0lvdjhBQ2Y3NWpWdXFkbkVZWDEyem5UcHgyc21GMWxNM1hKNGlxVEVvVk4wbGZNbzRzRkE1R3EwLzhsbElhU2pYVWVGZXRVMlRuZWliUE9JSjdBZzdkWWlWcGphRnVVWTZ5M2lQbGdzL01oelk0R1BjSDRDNi9Zd0hyUy80QkdRcGpjV0g0WFdKWktKTnV6RmFNWlJsNVdjSnNWN1IzVXhvcHo2NFdXZ0FLeXpHWDJzdnQrR2QvdFUzRFRSZUU1aGNjU0dUMXhBSDZZNmx5N0RnTG1pMDFnZnVOSWJUMmVKb2dLd2hWV1M4cjZoVEZtSlRYSExiaGQvR3VjQkRiaFNLclhMbnlUTERPUVBpWVFrQXQ1R0xkR0hHL3dDTHhFb0hkR01mTlg5M0NyRlpSdnBVSGx0aXNLWnJnOEhIOHk5N0h3S2gyRkQ0V2pYd2ZVelRsalVVMkdRZkJqNGdvMk8vTXRPQWRUcXA3RWhnZkV0VUo3bW9EYW0vR29sdmZUUWplclA4YUhHNFpyYXEwb1V2TjlzU0xMUXZlSzhiMEI3bUtjTUdWNDczMFVzTjJNT0hkVllBMjNYeWxtaU80Wkt2MU1RNnpJeURTZzU2VkFmaVdaU3Q5OHhHRmRyRjVqMUIvRzZtblpmTlJjVzFiWFNqUjBUYWFZREZjeWlHS0VFQ3FoZkZpRmdvVmRPYTRqRkVyNWVZQVZOb3U0ZFNCdHRaZFRrTHk4NWJUWkMydU1ETDZnckl1bEkyRVNHOWdCbmJFWi9TRWFIRnFwcFhGc0dxV0ZpNURGRk1LRUxmMWJHSXRBWGx1NmVKOUtKTG04a3JTdWdnSWtzbHNPaEs3ZVNXU3oyd0VBcTRIeEtzWGFkRXhQbHRzM0I2RmlscmRTalU3eXZmN2c4eGVCRFYyamc3MUhaNCtseWgydlhtWFJ1YU5jVE85aW5xVk5tT2F0NnZPS2x2ZU52RDZ3ZVl5TmRpSk5sY2Y2b2ZUWWEzVzFveWZpWisreUlnenllSU9za1JRR1NYVmFZVFZKK053eFRLTEZCQUMreXRGTVlQc0QxeGc3OW1KMEJIb0dDd2Z2OEFWRGFIR0YrWkZUQlpwRE8xZUptREhVMGI4RXluMmozVm1JZzhRK1NCL2d5bzB6Mk9tck1TL3IzMzhXWVAzaHl5b1NOUCtneVlqT056bjBYSVBEMUJodEttNTM4eXE5RmZjUDhBTHVKa1Zwb1RoZVZ0QThHb0ZycWdyVGhuTFdPU2FkSm9qWXVlSHFXc0NTcERUc0VXYVlETVdVMnk1M0JkZlVJVWF3Z2IvcUU2U3c2aTJ2VStSNFp5cS9WeDZTamlkbUUwSldmRXprWlhyOUlrMnA3bTI0WHcybUxtTjhTdlRSSENkdkNZMXJuSnlGRGtQOXFXWk1HR1BIVFhQVXBOZUFvUVlVNmxwZVdwZ2Njcmp3Umd1R1NiNGE1dnVZa015Z3NaZlA4QUc1UzE4M1VWN3MyMWpCVjVtUnZNOHU4aHc0UHpIeHJOTHJtanptZEtaY1JkNXB2UlJlUkRUY1BMd2ZZZzhNQ2lqVzNmQVBzOHdxejFzMTN4NERIRlp0allacWVQYTZKaEx6bVBocTIyR1NtQmE3eFdjUjZ4WmNXSXJmQmZIeVJDWEFybG1ycWZJYWtLck5WVFh6R29nbkVwWE54Mno4emE2M01NOC9FMFZDazQvZWlrYmRMQ3FLejV1Wmc3VXlHcVlFMEVMeWZZL3dBd1d6ZUorQkZHY09EeTE1bGhWWFZlU1BubHpXWDJ2bVhVeGJFT1ptUk1WbURrNUdGZm5vQXpneER3RGpwUW5Mamc2VWtvampTK1lPUjNkWkx5UlNWWmZuVmh4ZDV6QXV1YXVvNWNvUG9sRlQydTM3R0lBTjJiR0VUS2p4NDRtQ1ROb01nSHdDdWkrMk5Od0wvZS91ci9BSXhDcnJVcUhLL1A3UzRwT2JQVFErZWQvRVlMdFYrRFNuZ3Y2aWpvdTFMcGJiaS9VdTZXb0RnMzRhODFHWmJpL3dBeDJlWXRmWE5ZZGVvc0k1Q0M0ODhTbFZzSzhkb281MFhqSUgrSWswMjFvR1VOcTdQbnFNcDBMV1pTaktBdUVzU1pmdjZHZVdKUzdEeDRIcUozNU4vUG5qaVU5Wkt3Y0ROMFFuSmZ2dk1tOXVPTXhJS25zYXR5bWJKM1hrYjFNVkxaNVRpcXVhWWM5eVRYaDArMnVXTTU0TEE0Ri92L0FETEhlaWNuci80bXBNV3dCaGNzN29EOTRPeXdrN21DdTN1VjBPNUtyRC9udVlndG5WUjZUTi9YbUZRb2JYc21QTHhERVZLRGxFeEZMb21aOTc0QzNWVFdvTkZyL0hNOElRVnFNVzFLYVBjenpPQ1RZZm5NYnVPUllEeDE2aFVGNEJ1czlFT3hzTi84V2swS1FCSllXVytZeUM5NkszNWhiZTBuNFB6RW04OE5IV3NpZUp5cFJjam50T0VHcUJOdmxkUWxacGdpQmRIdGxkTUxZaEszWHk3Z2tPbDA3UEw1ZkVJUU9uOEI0NmpaWXpUV0NWWDVoVnZLcUN2eENCbEdlL0VxSDZ3NGgxeDhFeVcrTldzeEZxKzRqbVc0ZGR3ZSt4TVA4UXU0QXkxRzQwT28vOFFBSXhBQkFRQUNBZ01CQVFFQkFRRUJBQUFBQVJFQUlURkJVV0Z4Z1pHaHNjSGg4UC9hQUFnQkFRQUJQeERSSWVEZUJ5d0l0ZjNEVlVZdzR6VlAyd2xpeEFxSTRBUkhHN01qaDd3bEgrNGJMVExXcktBK0pMWmMyNUdLVzQ1TXNzWEZ0SEtRc0I2Tzgyd0VUaVkzb2ZXYWdjOE9CeVQ1SEJXQXZObTY1bkZ6WTVqWW1ldU1YL1d4OHp4MGg2aE41d1Jxc3IrSmhGbVlYUFVUTHpmNU1WSVpzLzhBTEVPeTRXNC9yZ3RMTEYzZzVLNTRzeC9CaGpEcHYxTVp5WEhUcmg5aGlTVWZNYjNHRDlBRlRjcXVYRXd1eGt5NlRCNTJlT2FsUFdXMExYb1BMK2J5dUx4OEhIazMvbUdKam95Y2FtV2NuY0FIb2h4aW1ZS09OOWVzUUF4aUlNWmhBU1gvQUhFYTNVa3hYZ2prWWIxWVozcjh5ZFpNcGhLT1VPVmRaVnVPSWZ4U1pTREpMVCt1TDlENHNxdTJOQmhkSGpIYkNmRG9PZ3hwOTFXemI3WjdSN2w0L01hUG9ZMG5uRSs2QTRHWk5nM2tQK0d2T1hxTmI1RGtocytSbDJwQmNYYzhPQ3M0TUtBWHN6Wkh3cTNJTnVVcnJHclc0RkdYaXhsZlE5TE9lZjd4UnNmM0ZTa2ZKY0JNVXR3ZU1DZUEwQndZTVM5TnpoT21RcWU4L2NFVkZqelo2TWViZXpwdmpOZmx6V3hCUjJRaHdJa0NwSXhwVVFiUU45NXBuU3BYOHVBUHlRQmlxbjFGMmNSVDR3UWV5c1poemQ1YlUzZ0poTkIxYldLUjFRQWNIQlBqbHhtaVZ6aFVmbUZKU2hYRHR2Q3JBUUhveXhDUExGUWl1WExsTjhyeGl3YW82QnRmQmk0RlZ5Y0thQUtrRXJ6a0tvY09WeHlTcEpmbnpEbHdqb2YvQUhBa2E2Z0orNEMwdjdZazFQV3h4RUVmYlRJQ09kNGdFUHJnVG9PSXcwOWs0cFBoVVRGS2g4WnBCL21lSFhHczNEMTBDZXNTVE5nQUc0Y1BYU0JmT1BaSUNQWVljSTB2NWxDUnMvNU1CMDdYWWE3MWtvNDFCWGRxNHdncjAyTXJ2ZW04Si9CdmhmWUVNUHVMc0wvSEgvTTExSWVOUW92NjE3em9yZUJNMEo5ekQ3KzR6Qm5BSjg0Q0IrT1d4Y05FSGFDdnBaYzJpaE1rTERYbWJSNGZaY0pKY0NEeUpwTUVYWVEyTi8zQ3h6VUFmYVlvVnA1MkdIYURsejBMZGpvNWVzQmI0R2g5QnU0eVFPUlJZUEhUclFaTUN3bnFxaURxenFtSHdISTc4WGFDSERGVFNDZG0yRCtJNHpwU3luNm5mbkduajh6QTZLUHJITmNxdFBnQlZlZ1hDUGxwajROb2ZjVTdKb3I3UjVZVTY4UlBRVmF3RGYzL0FQSXc2aTNxUG9yaTFXeG5MLzhBSEZ3QldHZ3BSNXNFL1NZVWNqdFo2ZEdKbTJXYy9NcFd5SzVGZEYzUVptZ091V25GNE5aN2I3bzFmRjZ4S1BiL0FHbXRCTmRwMlpSK0FuVWRCLzZHS1hWR1FKYXRQMllzeHFoSjN6SmlTZ1RaUHZldGZySzhyMDN3dmV6QkRsK1JldjhBNWpJRU81SFhBVDlYR2RiekJ5VmFyb1BEWmNmNXRrZUlPMEZDNzlHQm1WUmhzQmFOTzlZdXNYYkVLTDJleEM1RWh3Mkcwb0ZFOW1JVEFnNjdmZUU0REJnNnNQekhTNzVWMlZzQVJYZS9pNFZCb1hRSklHSTROdGJDS016VkowOWM2ZXpBbVdVQUlSNW9QWk1aaEN1MUZIMWFqdlhadUtQK2NnN0YzeC9tTldXNzQ1L1ZROUp6bHlWQ05HMDlhMnZVY1ZYQWxTNzZ4cERwWE5VMnBkMTY5dEoycWNaSUw3bTVYVzFqczMzZ0EyVnpYdjhBSC9jVWdEYjMrQ0E1MCtPYzM4ZFpNcm9CWlBlOERKWG1mMVhSbXJYSllIdHovRGtNeDcwUDZZaTlNTmNua3cwTnNSUitwYmdyWEhJeGxyQXM4cGxHNHJ0SlZMZkduam9pZEY1Z0RjNDNpb2JHNWRuUzh1Kys4ZGkwM1lxOEdzNERlVy9Kc0pGV1RoaHcybks0azZpQjEybk5vdmw3QWNZbVduMGhndllCODNzeDdTUVZ1M3hBMjhrR1FyQ25nSmg3WTdNS3MxQ3A2dGZDR0VDaWM0WFg1R3ZZT0dpa0VLRkhUM2Y0dVdHK25ES1VRVC8wWThvYy93QStNb3FxR3BmL0FKamt2M0gzSElKWElLQWk4T3hMclhKM1R1QnkwVG9xVnBKcm5DajV2QlJXazJMMzdjZW5LL0dVVzNjVGVzRWJER0NKRk5hZTdqMXlveSsrTVBtTmx2cEdIQldITGhSUmRqRDBjYVhsV2VjMU5DWDljQVMzZDU3Q0hIdnJCYmRxZGdZY1NMRkNnWUliYWIrU0FqV3pudmJpSm5HcVRSczBGMEIrM0JISmFCcU5pSXBPMHU4SWhWVlZiSDJ0Y3JXbEZiVXFxQWVPYVdheEp3dERiWU52SU1PcDFsSWlaT0lhT2dYV3M0Smlzb3A0NDR3TElRVHN3TTQvaGNuTVYvMGNPL21NTHgxeERwM3hrMlVVQjhqaFh6RDVnbCtqc1gvSGZ2R0s4TGVoOWQ1dU5GcUp4b2xHRnNsU2VvaGlvZThtZmtndUFTUlFjb1JkNGNPUVlOMncyRFdkT25EWTk0aEpybkVMQ0NDUnd5Mit4d1o5MmNGdmpnb1lKUktwTUtJanR3ZG51TFl4b01scnBUanV0RTI3SFZ4WmsyZ2hxT3ZMMjh1TFFSTHRIWWg1R0JlTjRIdTNxcndIQTVyVUp3WVdRRW9aR2ovYk1VTThlaUlmYlQxaHpGOGlyVGY4L3VEMXRCM1lMUEM3L2NOSlhCL0FleHp6YUVIYnljeDY3dUxGZzZKSjBvZWZHOGJ1bHJxejd4aTZNTlVhZmFkL01xbkFybzdUOU1SREdDSUwxZUJyeGxTcFFzTUJWWGlBVmNHNHJRSG9IbXNqU0Y1dVB1WkVZT3FVbm9hbFhCUW9QMWpHbVRZT3MyR3o1YVdxUkZsNU5nQ3FISGxHQUxTTTF5SXM3VFp6dDhZWjJvVWZYM0NUaG9yeWg4bjI1dDFJZFFETnplSVFRT1VMamV1TUt1Qm9SZkErWERBckE5U2IvTHJuR1lwaEMzVHFvN2NFZllleWdHbittSzV0ZzZLRHd2SFRsU2ZDT1lwbmpXSWM2N2xMWDhSd2JORFNOTEJyMW1rYjg1VGh3YVZ4UmRBWUgxTzhrOEVFYmZack8wUUxDZlFHTHdTQnNYQnVwVWx2N0c1NW1zaDN6MldOR2ZSd2pnUVU0a2svVXFLUVRJbkVKNmlFUnp5RnN3cnI2UUhnYjVKSXZRRTdmQnB2SjltQ2ZYYXc3QVM1dkpMMnpvLzNLNndpSXhJZitSUERNa1ppRlF3S0xYR0RuVVo1L0lYakdpTnUxZGcxckd2bFc4MmtUakQ0VUZIUnc0dEdiUTFneHVLa3VUeVVhYXo2dU9UN3NBUDNURU5YQTIvVXdHVVN1QUt2NFl6bXkwM0lrU29WQXJwY1ZJTGRBMGdvdmNpWTRIWTNLcTBHc1R4dkJyUVIxalZBU25HdGNFNDgwTzZwcFlnK0F1YXI3Z29xMlF4MmNodFFjS0RKb091ellUYkhrR3ZJcUNIRkhuWWdYTVZuWTd0NjRNQ3d0KzVyUDlNUTZGamsvbnhLQ2NZdWFwMWpnSW54bHQ0bzhoeFJsSE9Eek84TTg1dmliWE5vVzRvanN3TzVDUjR0bWxDdExXNmxjU0RHcTBtdmNHZ200TWMyTi9OWUVLRXVFWStzQTZMb3dSRUZRMnlaaXRpT3d2UEx3d2NQNzU1SmdUYXJ6K2g3bUlPWWVkcnVNWlRsamNZTnlZQklzMEVFOExsanhnck1ZRW15cUxUWkNxaXhvUUpJak93VFltT1pUaGxUeWVNRXVsRkFFejdoZllNd3VaUWRpNlYrdGM2Y3Vid2pvM3lqVDRkWnNBYUJPdmcreVhKYXdJVVk0NnBMeGljcVZ3SUcxK1pNQjRReXZrSHFQM0QxUCs1SGwybmVZbGMyakNuak5QOEFPU3JhbXN5MFhTNk9NVWtrRUJWVmkxR2JNSWVFN2NSRGJSTnQ2dWE1OW9nOWszNGRQSjdkVHc4a0RCczNab0UxVExUcXFPT1FycGRycDZ3ZnpWNjVoR2NoUFFwMjQxWVFsV0VSVUJ4MUJtQk14ZGNLQ3ZGb0VCbUMzcUFLQWlvek1vNE56ZkpIYlkwTlJaVkNGYkd5RkRMUzBiK3VxUkIySmp4NEFVU2plY25pTlhGdUJuSTRVQUxnbG1zSmlpM255MmFhVUd6dDVVclVVQ1FqcUhpa0tBaHdlMENzT0FvNnNrcEJMUkRnZUM2b0RnVTJxWWhLTGFrSEVWM1F0R2FTT3NvbFlYSXMxcHBpQUFuSmkza0t0UGRlSnovcGpSVnB2Y05RN0tnQnV1QjMzV21CYldoZFNWSlRBVkRZSCtTUzgwOFV4WFFadm5vSnhIYjhPQ3hTeVdOR0VFcnh4M2dsTzBjVFgrQWZxbUxYZ2hTRDUrU3d4R1VqWmlrRTlnRFdCdXFMcnN4dlJ2QVo4NDZIQWlBa1BSYzRrcWJrQ3NRZFk0QkFzb3VUYkVINkNXQWlydE5ZbDJleElGa2h5aUR6Q0Z4d1NZQkNpMjR3SXVsdWtlOVlobUxJMVNHYisrWDZvRlZVTjVpQml6SVRVL3VMdDZwNFpVeTk0VFc1bzBrdnpXWDZ3ckFtRzZLZ2JHOTNCY0kxcTFoQnBCQXEwSG9FMUlvc0dIQXZSb0ZzdzhZUEdnaEJ0d0Z0RUU5d2IwQVVOOUNZbnFYdFRxZDZ0aGJKT2MwdGZWODZiUjdJWENzVXF4Y1Vqb3F1aXF0eFN6VlFtc0tCSnZ5ZUhCUGl4cWtuWGdwTjNuS2QzcnFqS2RIYktvYjVrR2Q2ME45YXlBaXFrTEZFQUFCL2NaY0phb0lGZFA1ZkRnZDBFcnVGK0dEUWRjemVGRjJkY0o3M01BK1pqZWdQQUxReFFrNi9Zb0lhUllETjMzUlNxOUZlR2lQckM4M09vZ09kbFVSQ3F1cURMQnQ2NkJYUVhORkNsTVdvVUFwWTg4dVJaTjQzVVNBV3NTbW5PS3VMVGlqVVFFcmsxSWw1ZHo1MUlJYmFnY2FuQkNRa1ZaZGdOeDNITlpqbkNSQVJUSVRWSUxwTUppQ0swRG9BREVVZWpoVXJpUldZZ0kxUzliYnladkpFSU80clkzcjVZZ3ZHbWlVRjhXVjh3dTdxamxyUTAvM05MSXFPNWQ2Mzd3NmVpQTVXTHRmZWpBZWxjMWRoMTZhWXY2dTRONkgvQUwvY1o2am1ncFpQUTd1Q2E0bFlvMkhqSXBCMjNEWFE5amczQmxVOEJYYnl1MFhlUE1DRVdpMkJxeVhtTGt5RGpoUERBMjVDaUJaRnVKbDRkaEVIdFM2ZExpM0pxV29VWXkwMTdVc3NlcW5GOWY2aU9CWWlIYjFtZ3pwQ0VwVVhnOEczZUJhbWQwMFJMekNyNDRERjZEWEZZK3NsdDJ0WVp2NERsSEd3cHpvbnRlTUhVQ0FxZWxJVm0xRFdKT2RsSFd0KzJoYXN1SHdCWHlnUUluSExsMXhNdHlEb0FkNzVSNnk3Y0Jld1Q5Qk1ha0kwUVZ5NENwRFpTOHpubGN2Y3pST1ZUZ3dJaW5VSk9IcGtlcGdZOUY3V1U3dXB3Wnlyb0Y5aDlZZ3FNWmQyS2ZoTXFpSUpZbHQzenJrei84UUFLeEVBQWdJQ0FnSUJBd1FCQlFBQUFBQUFBUUlBQXdRUkJSSVRJVEVRSWtFR0ZDTlJNaFVXSUROQy85b0FDQUVDQVFFSUFBa0N6b0owblNkSjBuUXpvWUZuU2RaMUU2UXBQR0o0eE9oblF6ckFvL0hTYTE5QkFwZ0UxcUJET2tDVG9KMEU2enhHYUc5VG9aMW5TZEowbXBxZEowRUtDZEIrT2htak9zNmlkUkF1dmpwT2t5YjZNWWJzcnlxTGh0QmtWZ0VrY2hTYXkwWGtzaGJTeHJ0dDhuWTQyVTJ0c21VckU3TjFRRzRMdDdpM0hmdEg3blFLR2RKMW1vVi9zb1pvR2RJR0JnNm1iVVRZRXN5S3FSdDg3TE9Ua3NTbHZVNkhkM0JFS2tEUUFiVVFlL2RRNmlCaVlPMjVYdmNHUDJsTklyV2RRWjFoUVFwT3NLa1RRblgraGFKNWpQTzBiSTlUa2NrM3RwVldDdUpzU3BOL05ORlpYY2JGREhjOEd2VUZaRTZuY3BVRDU4dW9NZ21lWm9MVEE1UDBQeE4rb2ZRM0MzcUR1WWU0bHR4clFtTTdlelBtS3NyckpXVlVnRFpDckVmUTlDMkMwR2R0UU5BNUVXMGZFTENkdjYyWU93K0QzSWc3YWgrSWZpSDRsTlREMnhWSmxLakpwWFVoaUlxa0QzajlHUHRhcTJIMmlzQWFnVklBdXZTcUoxV2FXQkJPc1VmUUZENmkyVnN4V2RSR3RxUTlXOWEyRDhRc0JERGVpUnNwSW1TekU3TlBkaVFjZmRaVXVqMUhSd3IvQVB6UElueEF5TjZDMDZFVlFzSU92Yk5RaTdZWjJJRHFETm9Qd011cUhNSDQvZG44ZnViRFBOY2ZwVmZiVWZ0R1VqajI1cExiblVFVGMveDNBVDhSUFU5bXNrV1l5c3ZZNDZKOXdoY0Erd2Z5Q1FUc2xrUWJsK2JaYU5VdWJ0N05WZ2JJY1NvYUdpR1piZXNTMDZpc3JmQUgxOWd3QUdabkk0dURZbGRnNTJvWkpvWGNMZ0RaQkErUTRJbXhMclh1Ym9LL2FlZ0IrZGt6SnlxOFZPek5kazVSKzRKcXYxVWU2RzFxS3dHMUsyS3Y3R20rMHFmRmNWZzJJbG45L1RRSWcxcWM1anFRTDNwb0daU1daOHlqbzdSc3pLSE1nbERUWXZaZWl3SnVCQ0owblRRbVZ5VlZTa1ZWcFpiWVdkS3dnOTJmZW5SWENKVm9VN053Z3JERmdWQWhVUEtMRzlvL3ZYcFdZQ0MwR0FnaWFKK0xjZjhBY1ZHdGtaTWZJNk92SW0zbTdxVjhGZlJtdHdVQ1ZpdXZYcjZhaFBYNXpyTHN6K0txMm9pdm90RkpxUWJLQW5acDI5N05MeUJTWld2VUF3YjdrUllKY3JiN0tQdlgzQkFTSlZrcjhNMTlTQWI1UGpNM0w1RHlyaGNhaTVmZHVVNzE4UzE3OEhsVzVXSWpQMG5TYUErVG8vT3dmZ3FtcDFHcFl1eEs2dENYcWhFNkVxSnI3OXdEMUYvS3dBK3hBckJ0QmEyK0lLTGZ3YW5FV3BwZlowUFVaM0pWV1lvdm9ITFptS0xiYjhuS2ZNemFRbkdmcVZPbDFnb2NQVUducWJFMEo2bmpyZ1JOdzFJWjQ2eE9xQ2FYVUNJZmtJZytPaWZNQ2dDRDFBWjJBR3pqNVZXVHMxOHp5WXdjZnBYajQzZ3NKTnRKUzVNZXU2M3N5VnRrNFl4TVBvY1hEdzZzV3V1N2lNbzVXR3BiUSttL3FOVFltdHpRbWhxYWdtb1BybjVtUmo1SDJZTjFZMkg1K21wNmx0bVExV0hqVkZqaEptZjltV3JaRmpWSEdkMzZKZm1kOGdnTHhMVjQrTUdpT3pEWkJucWVvY2xGeUJTMWZLNC83eXpHdG81TER5TFdyVHpWcllLL3Jvek10dXgrUngyLzRiRTVIQ0dUWDNHVmtKaTFzVnl2MUcrVGd2UTJJR3RvNnN4VVpCVVVyV2ZTWTJUajVWcnJUbFhXWWRCWjhGMFJPOXVYeVZtTUZTbCtXZkZ3VFkvSFo5dVVtN0p5WnhrdzJlN211ZTdQVmxLYnVTTmxXVlpnWkhOM0JxVng4dkpXM3UxR2M5OS9WTHNoYXFpMC9VZk9adkpQVW1QL0FMa3hzZTFhY2p1Q053OGxVTXc0eHllU294TitUTzVYTXliMkNqQ1d6dXNweGdwQ25wNDRMVS9jc0dSVzhmcXZIQ3Q3Q0lxNkdVbFQxZFlLbm92TGpKd0JlL2QxejhZWUsvdDZ1VzVGdjR4bVBsM1k1VzNPeFd5K2ZXaCtIZkw1Kyt5NjdNVk1XbFdzeWY1bVp4bFoxL0hJRnV4L0xrNmM4angxUXFBRlhUSDJLeHlhVVloRXZ5LzNHU0xhN2Nhekp0RDJIRHRXdnF0dEJzY1ZyanBRZnNXNGVST2tRV0VGWFhSOUNtK3Evd0R3ejdiZXVsdnY4UGpyVFJyeEFXcFZteHdwNCtwYzdzU3RkTkIwdHZRdDBITTBWWGNuVmtMK21hdjlOL1QxZmZQdHYyNlRPdXo4UGthUEJiaVkvTVhrWFkySlRpZlpqNVdDVUZseVVtekpzSUZweGlCU2xLbm9iVmZYajIxdGhEbFpoNDVxMkZPR3p1R0xBaDkxMTJseVZpZ3NObXF2d1pMV0ZycmNqSWFpWldNeU9MampCM1krV3RmNlR4WXdKVGs2TDNvRnBxUGVsZTJSalc1VzdEaVZONTFRNWxmanRGa3FyZE1rSEp0dHF4MVN3MDEyb21teGgzQjNaU1YwUWNXdnV6Qk1ldXM5VmM5ZnNOcVZ2L0l6NnJUYzhxaGR5NnNhRFNoU3dCUC94QUF5RVFBQkF3TUJCZ1FHQWdJREFBQUFBQUFCQUFJUklURkJVUkFTSUNKaGNRT0JrYUV3TWtLeHdmQVQ0VURSVUZKaS85b0FDQUVDQVFrL0FQOEFqUjhJd25JMEd3M1IyQ0VkbytHZGhoSHRzUDhBaldIQ09FYkJ4SGlQeEI4SS9EQ1BDVWJiSENkcDJnb0NPSnNvYlNFVVVVRTFRRTdZYWFiRFBHVmphVlpVQXVUK0FqZTM3MENzMmlNd3RGVWNOdGtndXRDQmtFelRUVFdmdHRHMndWTnRUb0xsVTZDdy93QjdCMjdmMlZrRWxmVitGY0t4Ky9HSlkwUWRiMGhDNWtJbUc1alMrS0oyOTRadEJNQUdLRVJjVk1sT2tJb29vb2hIZU91UDdSM25LNk1FbFdDMEt4YmJmNzhRa0h5UmgyUURNREJJME5nVVNHT0FiUXlONEdBUWROVTdkbkpwZThZQTdvOG90YUNPa2NQeTVPUDcreTZYNksrVDFWbGJacndDZUVlaUowc3JDeEVWSGRjdmlFRm9CcUoxaVpxQm15Y0J1L05SeGtBbXhwbXhYZzdyY1I5UDduVGdDRUljT3ZDTmcydTV0RnpPWkhMcUQ3VVFhMXJUUU01aVp0MzVSZ3F2aG04T05RVG1iQ05aQlFnV2JvWERTbWJERUlnOXJjSVFRUVRVRUVPSXlCbFBBOFYzeTBuTVRDZERnUGVaVWtHWFJNRHJVVlFoa2l4QVBZZy9TY3FqUzNkRUN0ZXRiQk9JNWFFVFhTVEhxU3I0SFN3dFNlMytEVzBEV1VJZk1IcW0xRkNmL0pCbjNUQzV6aERRQkFJdG45S2M0RzlEYjIwd2dSSUdhaXVEQlRaM1JQV2hnVHJxVTRBQ3NSZnYwOWxTQnJUdEdPRVhFZzZ4Y2Q4b2hybXVBYlg1cGJ2QWowUG9uVkRpM3VSZU5Zc2pVOERvOE03NGVDYVJBSVBjR25ud25tYmFUUlBnakdacmU4aGVHWnRPc2xFa3pjeWZTYkl5V3hQbnJncStQWDBDa3VGQ1NNK2VJMFRTVEIrVUdzV3FqZUlrVzZZS084OTlZSU5CYWFZN3FDNXBBTTBtY2dJREZSbnlPeDI2QlVPeURnaFIvSzNlYThnV2N3RXRjQWZwYzF4VEN3dkFpNFBVNHZjcDI0SUJEdnFkM2Nab0FNRE1yeFE1clJyNnpyQnltOHNYL3dCQkd5YVA0ZzhBMWdrekVBMnFWY21IT2JWalRHVDNPTmdNaUs0cWpZVDd4NmtwKzc0WnBHVDNUcEppOWlieWh2VGNpQ1BkQ1c5RUFKenFxMFVlUVZndkpFN3hFU1B6TXB4Sk41S2NYYmxKRWdVN0FUVkV3S1VFZTVrb2pkNjZ6UkNKZk83Z3RhMlpucmFFN2s4TWJyQlRVanpKQW1kQ3NSVVhnVktkSWQ4c1dPZ29uT0lnUUpnR2NBQ3A4MUFIL1VhSFdZOWhSTTVkNXBqVG1xZ0hOaXMzNndWZHNST1IzdE1KaEJzZXVpZFFDeWdtWmd6Q1B5a1QxR2lORzA2cDNMMHZJVVFOTWFYeWlqSUUxNmd4Q29CVW5QWUlUdlNKMHBjOTA0ZzJnbS85bTZhQURvaERBWTZod01FSDcrYUNFbnJYN3FXdkljMGs0cFF4NmhEbXQzSkpBOWRkRTNTb0UzdkNKRFJkdEFDSnFieDZGT1A4YkJKczN2VVlFVktiRFlGVHpFbnVhd0tVVjNGcFBheDlxOXdnUUdtNXNaeENPN0VIcFExSFRxcjFpbXltUFFKeE1tWmo5cmhhcWxwVGdEZU5salR6NjY5MUFpb0srVUN2WCtnZ0RINTAyQ040eWVwL1FuRVBhNldrWHJOTHdoelFKN293QWFmbnRsQ2pCSTh5UVBhVVkvVDlrQTQ0T2dUYU9wNTNxUGRHdVNzRWhDa29DWEdTakNxaFZzRWVWUGZLQ3NzekthQ1IrMFgveEFBcUVRQUNBZ0lDQWdJQ0FRTUZBQUFBQUFBQkFnQURCQkVGRWhNaEZERVFRU0lHSTFFVklESkNZZi9hQUFnQkF3RUJDQUEzSDkrVWlDNy9BQ0xsZ3NubjZ3Wk1HVlBQQmRyM0JrUmJ3UkZ0M0JaQThGa0ZrRCtvR0UzQVoySDRZTWZvZDRlcG5XRVF0b3p5R2VVenlIVThoRUZwZ2ZZaExhM0JacUN5STVpMndPWjVEQlo2M0JaTzVnZlVGbWhCWkE0bllUeVR5aUcyRndZekNZMkxrWmpFVlhZbVRqblRqSHVKQWg0N0tGb1NIaThjb3FMZlRUNGVxUGpJR0t4c2ZYMHFIZW9GOVJWamZ3SHZ5d1dDQ3lLOERpQXdHYm5rZjN2eW45K1dHeHBVbCtRU3RmRzRYeGNNQmJjWXV1ejBTbHRoTFF6N1l2V1NTSGYzNko3TnVEMStONkUrU0Y5U3pJN0dDNFFXaUN3UVdRV1FXUVdEVUZvblF3VWdtZUJZdU52NjRUQ1hGckxNempyR3YreExPcmJKdHMwTksrUzRZZ3JrOVJvQzJDMVQ2Z1pPc3lHT3ZRUS9ycVpwcHQvYzhqQ0xhOEZ6aUxjMzdXNHhMaVJCNHhQVXhxQmtXYWxkRmZwWW9GY3R0R3RTMjVRL3EyOXZZQmR0UnY1VG9RZlJWakFQd3dEZmZqMEowTTZhbm9RNmloUitCN2k3RURhM0w3MFBwVmR6T1BZcFlXTkRxVTNIc0RuUXkvSWc5TmJZRC9ON0N4OUUyR2FlQlhhSlhIcWNEWUxrZmZrRThrN3RCNU5iTFZXcWdZNy9BTTFZMlZkV1hyQk95Q0ZCUHBVSjNvTnFWNGpXbUp4enBQaHFxN0NaWFFkU21RVXVEaW0ycklYc09ReHV1eVBGYW83VFRnYlBsY21hY3hVY25VU2k1dm84WGtrYko0cS9leU9MMEpYeGxnSHNjZDZpY2ZTUCtTWW1JbzlxeW9PcTVPSGo1by9tT0p0b08xUkxFQkJOcFU2Z0IxQ252MHRXeG8rS3JyNklBdDZpbkp1cmJvTG1kM1dBQlYxUEVqVDR3QjlMVXc5Q25GUUhkaW1yV2dWS29JUHVkTkNHbXR4dUdteW82aW94KytoMXFCQ0FRZmFOQWdNcHhMYjYyS25oVmVyeXQxMmZhcCtnTmFuMUc5L2VQU21PbmNnT0xmWVVuN1FCUm9JZ2M5WnF0UWRkem94bUNyMWlzem1Ba3J1S2UzOFo2VTladnJDazNBM3FhQkVVS0JxWUZ4REZFTCtCdEtQTU9zU3BUZ0hiWFdwc044a2lESlVEMzhtdjlESldmS1NVMXZaOWtBRFFnWmRnUzRmMjl5ZytvUGV3RTBUQ08vM1d5a2RUdlJJbWd5K3pVLzhBMUxGQm8rYnQ5MFpocHQzRFlicWRvdUtmOUpyc2dkL1FxeVdaM0x1Q0I5alVMaWRnWmgxaWhqWlpVNmIyWDJXMk5LVzdGQUhiWXZiVkpFb0doc2c5YmlJbTE5eERyZTNHajJDRTJLZXdZQVQ3K202dC9Gck1NSDJsZUF6NzF4MlppNFdHYTJ5TTBuRDZKZ3Z2UEZWZkkwcGozdk84RFF0QnVMV2Q3SVZ4QzdhMFVzSkdpTCtub2QzWk96Z25VMlBJSWhCV0k2cVNwUzJ2dVVnWmFyU0lsMWFBeHN2SERhSHlxUDBjcWhmVXhzWnI2VGRLZUpldmtQajMxNGRONFZGb3JyeGNlNlpQRU01clNXcDBzWVFiblFUck92dmNCdDFQN3V0RUs4RFc2MU5NdzlnTUlXc1g2RFB1RjNnSm10L2VnUHBVTG5RemVQdndHQ1c4TnhoenNqZGxTaS9qVG9kRVl1ZE1WWXlpOHZrZVFaRm1RN3M2Y2pqaWk5Z28zUGM2L2dmWHVEYzl6Um12ODZFMFA5dkRjZGc1ZkhNYmVWb2N0M3I0QzZ3V1BUTUZjek50dG9xZG1SakUwbGEyQzZzS2ptcW9CQ1RPUVZycmlBeUJHMEpxYWkwczFKc0Q4ZGY4Vkw2N3NESm9yVjNGYkZDNC9IdVlsZGR1RGVDUkFQd0ZuQzhtY0N3bzFkTnVUWXB1d2VCK055Q1hqSlJFdUYxUW8xaml4aUc3YkxWc2krNmFmS3hFdnF0c09xOEhpNmNudmJlbkRwbGNnS3F1UjQyakVzSXFuRzA1T1JtTFZSeHZFdlZYZGkyWW80cWlpMnM1U2YwelJXdDV5Y1RFYW5vbHVGWFJqRjNyb3NzY0xPQjRqR29SemJid3RyOW5vRlpFVGlMMndQbGpENHZJem5DVllIOU80R1BnaHJUbU10S0l0eGExQ3pWK1QzQ1crS0ZIMlovNUNBWldYUmlSM3J2eGhXL0hjb2NXb3BWbjBabE9mWWNsOFNuc2JYdzZVOG5hbFZPTHd3dlRPcHA0K2xhcXVIcXRzdUtWME11TFV0YnZYVmE1ZXNPL1lnNG1UZFZZeFFZejNhWjA0ZXpJeXhySDQ1OFBGYkh1eHJLY1d2clVNNmh6N0dSNDAyYmJyaU52VlpjSERBbWdFc3VvOUwwK25vNmduZFNCK3pzdlI4alV1VkV5V1plUTcwTm8rQVdwdDZxeXRmZGNMT3RIRnRpbmxFR2Z6VmlWOE5qMGtLNXlMeG05alk5ZndhTzVMaHFSM295UE1pVVdWMWVGQVppK1ZOM1N4dE1FYW5hMmVrWlNnSXZ0ODUyVXZSS3lJcHJDbnNlMWZzeG5OMUlTR211dkhXMFVPQ2hTWEJBbTA5L3V3VzNuVFk5dmpScWxDMkt4bUk2VVc2THQ0NldsTHF1S2FSNGEzeHQ0N3JheEt0WWlOc3h3VVBxcy9jcXMwaDMzc0k3bnUzdDRwZFFRRUxIWUhpWWlWbEJzTllBckhyLy94QUF5RVFBQkJBRUNCQU1IQkFJREFBQUFBQUFCQUFJUklURkJVUkFTWVhFZ0lvRURNa0NSb2JIQk1OSGg4UlB3TTBLUy85b0FDQUVEQVFrL0FQMEFoOENmalJLYWhuQ0dkVnBrN3JId2JTVDBRczU3cTBNY0Q4R1VMUGhLUEFvb29vbzhTaWlqNE1JZnBGSDlOcUtQNkxsZmhDRkhnd2tiZ0xLTW9IZzJPNmo1SThCWEVVakNQR1VVMElYNktBRTVPSlRaNzJxQ0Y3NnEreWJDSEhIQVNFMUNuVFBFSUswS0hoeHd3Z3NjQjdxQWd4RjcvYndoR1hFSzU4WHIzNDVDMThaaHhpTnRWVzZBazlma21jcnh1TWtUWU02MElUWUthZ2dVRUNnUjR0Rm53bmdVY0lTTjRpZWdPSjFJVUZ6Q1NKRUdEWkJHK3liNkE3V0puOFdoWjd6UFdmRG5RYS94d3dzSWNkVjJXT0puZ09BRkNjb1dUWU8vWmViMmJZY1NLT3RURVZQcW15WFl0b2duY2RzNnIycEw0dVk4dzIvYndsRkhnSjQ3Y1NqUlJDY25TaXFaR1RyMEhxbkFCMCthNkkwT3RvdWM1MGp6UzBDTS9YZFJ6YVMwR0NOdDc5VmtXNG5JYVQzOVNNb1IzOEpSUlRrNUZFb2xGSGdFTXFuSFFYSGM0dE5uMmJmZXVQU2QwSlk1MWpCTVZTeUtrMlZiczMrSTF5aEpHZjZRQnU1Vmo4NTcvTkQ0QVptWGFqbE9CdEk2WGRyL0FJeTJRTVFKeEc4ekc0VG9CZ2dia1Y5azlyV3RJTHljZ3pnZDl0RkI2cklKUmlhL3JaU1NyazM1ZGQ1MVI4R2hnK3VDZ1hOTFpNRDNZZHkzNng4MDJpME83QTRuYWNyQThEWmVDemxQcVFSNmo3ZUVlUjJZRWs3ZWdOd3ZaeUhDWi82eHBBR0hkTGhPRVRJRnhNSm9BNVlnQUQ1eFo2SVFETWRZNGE0UldrNi8yRTNsOWt5cGFSWjBGbk81QTd3Z2VWd0pBRmtSb1RqMTdJbldRUmc3U0srY2NHY3pqcHVOVUR5eUNKTkVPSTVtbU5RV2hPRHkwbXFJdkc4Y3VHcHYrUnhKNW0zeXNIUm9peVRxZElYc2kxempxTUM0SXVnUmx1NmY1NWdBWXprbjdRUFZES2NROHRKbUNRMEM1Z1hwS0I1Y3REcWNSSkdOTVRCNEVjc3hHc3pDRWtrTjlUZDdBREpUQzcydVNab0VFajVZcE1BaXBHUU1SK1U2RG9EUktNSHFuZTdVZUFVZzNsYVo1U0QzcVAzRkZNRFFNUUl1Y1IzRTJ2Wnc1NW1EQk4yTEpNVnBrSm9FbWVpTU92RXAzTnpOQW5YbUpJSTlOMDN6UHR4QlArd29KZElneEVtaEtiRG1tNUZqZlAwVFJPOFduV082TmtFZlFwOE8wSWs5cVdIWmc0UGJNU3ZhQnpaa1JOYnBubW5QOElrQ0luVzBLSXJnTWI0VXoxamhSMitzcTFwRmIyZ0ltYUZDL3dBSnhPZ243L3dqTGpmU0RkZE51RkNLaXM5a0phd2h3RWFhajdGR0c1eFFBdDN5MjNUaFVubEpnMWlUZVVKdWprLytvdE5seHhyOUVaZVNhRkFOSDNKS3cwR0tBdkl2dlY3bzM2MGhJeDJKd1ZqWGgvc2xEQXh3dERnZmQrM1Q5bE5vV2NLcFJSbUJIb2g1WEFUM0dzNVJwQ1pSdDVnOWhmM2haSjZVTS9Yb3BFWlJ4ZjRXRnJ3Mi9LMTE0WUtQQWJRalMvL1oiIGlkPSJjIiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIvPjwvZGVmcz48L3N2Zz4=", + "description": "Produced by volcanoes and industrial processes, particularly the burning of coal and oil.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'sulfur', label: 'Sulfur dioxide', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sulfur dioxide\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 600) {\\n\\tvalue = 600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"public\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#3B911C\"},{\"from\":100,\"to\":200,\"color\":\"#7CC322\"},{\"from\":200,\"to\":350,\"color\":\"#F89E0D\"},{\"from\":350,\"to\":500,\"color\":\"#F77410\"},{\"from\":500,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":28,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#3B911C\"},{\"from\":100,\"to\":200,\"color\":\"#7CC322\"},{\"from\":200,\"to\":350,\"color\":\"#F89E0D\"},{\"from\":350,\"to\":500,\"color\":\"#F77410\"},{\"from\":500,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:U08yLXZhbHVlLWNhcmQtYmFja2dyb3VuZC5wbmc=:U08yLXZhbHVlLWNhcmQtYmFja2dyb3VuZC5wbmc=;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Sulfur dioxide\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"µg/m³\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "enviroment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "sulfur", + "sulfur dioxide" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/sulfur_dioxide__so2__chart_card.json b/application/src/main/data/json/system/widget_types/sulfur_dioxide__so2__chart_card.json new file mode 100644 index 0000000000..8ee0589964 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/sulfur_dioxide__so2__chart_card.json @@ -0,0 +1,39 @@ +{ + "fqn": "sulfur_dioxide_so2_chart_card", + "name": "Sulfur dioxide (SO2) chart card", + "deprecated": false, + "image": "tb-image:U08yLXZhbHVlLWFuZC1jaGFydC1jYXJkLnN2Zw==:U08yLXZhbHVlLWFuZC1jaGFydC1jYXJkLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiByeD0iNCIgZmlsbD0id2hpdGUiLz4KPHJlY3QgeD0iMC41IiB5PSIwLjUiIHdpZHRoPSIxOTkiIGhlaWdodD0iMTU5IiByeD0iMy41IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC4xMiIvPgo8cGF0aCBkPSJNMTkuOTk5OSAxMy4zMzMzQzE2LjMxOTkgMTMuMzMzMyAxMy4zMzMzIDE2LjMyIDEzLjMzMzMgMjBDMTMuMzMzMyAyMy42OCAxNi4zMTk5IDI2LjY2NjcgMTkuOTk5OSAyNi42NjY3QzIzLjY3OTkgMjYuNjY2NyAyNi42NjY2IDIzLjY4IDI2LjY2NjYgMjBDMjYuNjY2NiAxNi4zMiAyMy42Nzk5IDEzLjMzMzMgMTkuOTk5OSAxMy4zMzMzWk0xNC42NjY2IDIwQzE0LjY2NjYgMTkuNTkzMyAxNC43MTk5IDE5LjE5MzMgMTQuODA2NiAxOC44MTMzTDE3Ljk5MzMgMjJWMjIuNjY2N0MxNy45OTMzIDIzLjQgMTguNTkzMyAyNCAxOS4zMjY2IDI0VjI1LjI4NjdDMTYuNzA2NiAyNC45NTMzIDE0LjY2NjYgMjIuNzEzMyAxNC42NjY2IDIwWk0yMy45MjY2IDIzLjZDMjMuNzUzMyAyMy4wNiAyMy4yNTk5IDIyLjY2NjcgMjIuNjU5OSAyMi42NjY3SDIxLjk5MzNWMjAuNjY2N0MyMS45OTMzIDIwLjMgMjEuNjkzMyAyMCAyMS4zMjY2IDIwSDE3LjMyNjZWMTguNjY2N0gxOC42NTk5QzE5LjAyNjYgMTguNjY2NyAxOS4zMjY2IDE4LjM2NjcgMTkuMzI2NiAxOFYxNi42NjY3SDIwLjY1OTlDMjEuMzkzMyAxNi42NjY3IDIxLjk5MzMgMTYuMDY2NyAyMS45OTMzIDE1LjMzMzNWMTUuMDZDMjMuOTQ2NiAxNS44NDY3IDI1LjMzMzMgMTcuNzY2NyAyNS4zMzMzIDIwQzI1LjMzMzMgMjEuMzg2NyAyNC43OTMzIDIyLjY1MzMgMjMuOTI2NiAyMy42WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNMzguMjQ4IDIyLjQzNjVDMzguMjQ4IDIyLjIzMTQgMzguMjE2MSAyMi4wNDkyIDM4LjE1MjMgMjEuODg5NkMzOC4wOTMxIDIxLjczMDEgMzcuOTg2IDIxLjU4NDMgMzcuODMxMSAyMS40NTIxQzM3LjY3NjEgMjEuMzIgMzcuNDU3NCAyMS4xOTI0IDM3LjE3NDggMjEuMDY5M0MzNi44OTY4IDIwLjk0MTcgMzYuNTQxMyAyMC44MTE4IDM2LjEwODQgMjAuNjc5N0MzNS42MzQ0IDIwLjUzMzkgMzUuMTk2OSAyMC4zNzIxIDM0Ljc5NTkgMjAuMTk0M0MzNC4zOTk0IDIwLjAxMiAzNC4wNTMxIDE5LjgwMjQgMzMuNzU2OCAxOS41NjU0QzMzLjQ2MDYgMTkuMzIzOSAzMy4yMzA1IDE5LjA0ODIgMzMuMDY2NCAxOC43MzgzQzMyLjkwMjMgMTguNDIzOCAzMi44MjAzIDE4LjA2MTUgMzIuODIwMyAxNy42NTE0QzMyLjgyMDMgMTcuMjQ1OCAzMi45MDQ2IDE2Ljg3NjYgMzMuMDczMiAxNi41NDM5QzMzLjI0NjQgMTYuMjExMyAzMy40OTAyIDE1LjkyNDIgMzMuODA0NyAxNS42ODI2QzM0LjEyMzcgMTUuNDM2NSAzNC40OTk3IDE1LjI0NzQgMzQuOTMyNiAxNS4xMTUyQzM1LjM2NTYgMTQuOTc4NSAzNS44NDQxIDE0LjkxMDIgMzYuMzY4MiAxNC45MTAyQzM3LjEwNjQgMTQuOTEwMiAzNy43NDIyIDE1LjA0NjkgMzguMjc1NCAxNS4zMjAzQzM4LjgxMzIgMTUuNTkzOCAzOS4yMjU2IDE1Ljk2MDYgMzkuNTEyNyAxNi40MjA5QzM5LjgwNDQgMTYuODgxMiAzOS45NTAyIDE3LjM4OTMgMzkuOTUwMiAxNy45NDUzSDM4LjI0OEMzOC4yNDggMTcuNjE3MiAzOC4xNzc0IDE3LjMyNzggMzguMDM2MSAxNy4wNzcxQzM3Ljg5OTQgMTYuODIxOSAzNy42ODk4IDE2LjYyMTQgMzcuNDA3MiAxNi40NzU2QzM3LjEyOTIgMTYuMzI5OCAzNi43NzYgMTYuMjU2OCAzNi4zNDc3IDE2LjI1NjhDMzUuOTQyMSAxNi4yNTY4IDM1LjYwNDggMTYuMzE4NCAzNS4zMzU5IDE2LjQ0MTRDMzUuMDY3MSAxNi41NjQ1IDM0Ljg2NjUgMTYuNzMwOCAzNC43MzQ0IDE2Ljk0MDRDMzQuNjAyMiAxNy4xNTAxIDM0LjUzNjEgMTcuMzg3IDM0LjUzNjEgMTcuNjUxNEMzNC41MzYxIDE3LjgzODIgMzQuNTc5NCAxOC4wMDkxIDM0LjY2NiAxOC4xNjQxQzM0Ljc1MjYgMTguMzE0NSAzNC44ODQ4IDE4LjQ1NTcgMzUuMDYyNSAxOC41ODc5QzM1LjI0MDIgMTguNzE1NSAzNS40NjM1IDE4LjgzNjMgMzUuNzMyNCAxOC45NTAyQzM2LjAwMTMgMTkuMDY0MSAzNi4zMTggMTkuMTczNSAzNi42ODI2IDE5LjI3ODNDMzcuMjM0IDE5LjQ0MjQgMzcuNzE0OCAxOS42MjQ3IDM4LjEyNSAxOS44MjUyQzM4LjUzNTIgMjAuMDIxMiAzOC44NzcgMjAuMjQ0NSAzOS4xNTA0IDIwLjQ5NTFDMzkuNDIzOCAyMC43NDU4IDM5LjYyODkgMjEuMDMwNiAzOS43NjU2IDIxLjM0OTZDMzkuOTAyMyAyMS42NjQxIDM5Ljk3MDcgMjIuMDIxOCAzOS45NzA3IDIyLjQyMjlDMzkuOTcwNyAyMi44NDIxIDM5Ljg4NjQgMjMuMjIwNCAzOS43MTc4IDIzLjU1NzZDMzkuNTQ5MiAyMy44OTAzIDM5LjMwNzYgMjQuMTc1MSAzOC45OTMyIDI0LjQxMjFDMzguNjgzMyAyNC42NDQ1IDM4LjMwOTYgMjQuODI0NSAzNy44NzIxIDI0Ljk1MjFDMzcuNDM5MSAyNS4wNzUyIDM2Ljk1NjEgMjUuMTM2NyAzNi40MjI5IDI1LjEzNjdDMzUuOTQ0MyAyNS4xMzY3IDM1LjQ3MjcgMjUuMDcyOSAzNS4wMDc4IDI0Ljk0NTNDMzQuNTQ3NSAyNC44MTc3IDM0LjEyODMgMjQuNjI0IDMzLjc1IDI0LjM2NDNDMzMuMzcxNyAyNC4wOTk5IDMzLjA3MSAyMy43NzE4IDMyLjg0NzcgMjMuMzc5OUMzMi42MjQzIDIyLjk4MzQgMzIuNTEyNyAyMi41MjA4IDMyLjUxMjcgMjEuOTkyMkgzNC4yMjg1QzM0LjIyODUgMjIuMzE1OCAzNC4yODMyIDIyLjU5MTUgMzQuMzkyNiAyMi44MTkzQzM0LjUwNjUgMjMuMDQ3MiAzNC42NjM3IDIzLjIzNCAzNC44NjQzIDIzLjM3OTlDMzUuMDY0OCAyMy41MjEyIDM1LjI5NzIgMjMuNjI2IDM1LjU2MTUgMjMuNjk0M0MzNS44MzA0IDIzLjc2MjcgMzYuMTE3NSAyMy43OTY5IDM2LjQyMjkgMjMuNzk2OUMzNi44MjM5IDIzLjc5NjkgMzcuMTU4OSAyMy43Mzk5IDM3LjQyNzcgMjMuNjI2QzM3LjcwMTIgMjMuNTEyIDM3LjkwNjIgMjMuMzUyNSAzOC4wNDMgMjMuMTQ3NUMzOC4xNzk3IDIyLjk0MjQgMzguMjQ4IDIyLjcwNTQgMzguMjQ4IDIyLjQzNjVaTTQ2LjAyNDQgMjMuMjU2OFYxNy42MDM1SDQ3LjY3ODdWMjVINDYuMTIwMUw0Ni4wMjQ0IDIzLjI1NjhaTTQ2LjI1NjggMjEuNzE4OEw0Ni44MTA1IDIxLjcwNTFDNDYuODEwNSAyMi4yMDE4IDQ2Ljc1NTkgMjIuNjU5OCA0Ni42NDY1IDIzLjA3OTFDNDYuNTM3MSAyMy40OTM4IDQ2LjM2ODUgMjMuODU2MSA0Ni4xNDA2IDI0LjE2NkM0NS45MTI4IDI0LjQ3MTQgNDUuNjIxMSAyNC43MTA2IDQ1LjI2NTYgMjQuODgzOEM0NC45MTAyIDI1LjA1MjQgNDQuNDg0IDI1LjEzNjcgNDMuOTg3MyAyNS4xMzY3QzQzLjYyNzMgMjUuMTM2NyA0My4yOTY5IDI1LjA4NDMgNDIuOTk2MSAyNC45Nzk1QzQyLjY5NTMgMjQuODc0NyA0Mi40MzU1IDI0LjcxMjkgNDIuMjE2OCAyNC40OTQxQzQyLjAwMjYgMjQuMjc1NCA0MS44MzYzIDIzLjk5MDYgNDEuNzE3OCAyMy42Mzk2QzQxLjU5OTMgMjMuMjg4NyA0MS41NCAyMi44Njk1IDQxLjU0IDIyLjM4MThWMTcuNjAzNUg0My4xODc1VjIyLjM5NTVDNDMuMTg3NSAyMi42NjQ0IDQzLjIxOTQgMjIuODkgNDMuMjgzMiAyMy4wNzIzQzQzLjM0NyAyMy4yNSA0My40MzM2IDIzLjM5MzYgNDMuNTQzIDIzLjUwMjlDNDMuNjUyMyAyMy42MTIzIDQzLjc3OTkgMjMuNjg5OCA0My45MjU4IDIzLjczNTRDNDQuMDcxNiAyMy43ODA5IDQ0LjIyNjYgMjMuODAzNyA0NC4zOTA2IDIzLjgwMzdDNDQuODYgMjMuODAzNyA0NS4yMjkyIDIzLjcxMjYgNDUuNDk4IDIzLjUzMDNDNDUuNzcxNSAyMy4zNDM0IDQ1Ljk2NTIgMjMuMDkyOCA0Ni4wNzkxIDIyLjc3ODNDNDYuMTk3NiAyMi40NjM5IDQ2LjI1NjggMjIuMTEwNyA0Ni4yNTY4IDIxLjcxODhaTTUxLjM4MDkgMTQuNVYyNUg0OS43MjY2VjE0LjVINTEuMzgwOVpNNTUuNjY0MSAyNUg1NC4wMTY2VjE2Ljg5MjZDNTQuMDE2NiAxNi4zNDExIDU0LjExOTEgMTUuODc4NiA1NC4zMjQyIDE1LjUwNDlDNTQuNTMzOSAxNS4xMjY2IDU0LjgzMjQgMTQuODQxOCA1NS4yMTk3IDE0LjY1MDRDNTUuNjA3MSAxNC40NTQ0IDU2LjA2NTEgMTQuMzU2NCA1Ni41OTM4IDE0LjM1NjRDNTYuNzU3OCAxNC4zNTY0IDU2LjkxOTYgMTQuMzY3OCA1Ny4wNzkxIDE0LjM5MDZDNTcuMjM4NiAxNC40MDg5IDU3LjM5MzYgMTQuNDM4NSA1Ny41NDM5IDE0LjQ3OTVMNTcuNTAyOSAxNS43NTFDNTcuNDExOCAxNS43MjgyIDU3LjMxMTUgMTUuNzEyMiA1Ny4yMDIxIDE1LjcwMzFDNTcuMDk3MyAxNS42OTQgNTYuOTgzNCAxNS42ODk1IDU2Ljg2MDQgMTUuNjg5NUM1Ni42MDk3IDE1LjY4OTUgNTYuMzkzMiAxNS43MzczIDU2LjIxMDkgMTUuODMzQzU2LjAzMzIgMTUuOTI0MiA1NS44OTY1IDE2LjA1ODYgNTUuODAwOCAxNi4yMzYzQzU1LjcwOTYgMTYuNDE0MSA1NS42NjQxIDE2LjYzMjggNTUuNjY0MSAxNi44OTI2VjI1Wk01Ny4xODg1IDE3LjYwMzVWMTguODA2Nkg1Mi44ODE4VjE3LjYwMzVINTcuMTg4NVpNNjMuMDg1IDIzLjI1NjhWMTcuNjAzNUg2NC43MzkzVjI1SDYzLjE4MDdMNjMuMDg1IDIzLjI1NjhaTTYzLjMxNzQgMjEuNzE4OEw2My44NzExIDIxLjcwNTFDNjMuODcxMSAyMi4yMDE4IDYzLjgxNjQgMjIuNjU5OCA2My43MDcgMjMuMDc5MUM2My41OTc3IDIzLjQ5MzggNjMuNDI5IDIzLjg1NjEgNjMuMjAxMiAyNC4xNjZDNjIuOTczMyAyNC40NzE0IDYyLjY4MTYgMjQuNzEwNiA2Mi4zMjYyIDI0Ljg4MzhDNjEuOTcwNyAyNS4wNTI0IDYxLjU0NDYgMjUuMTM2NyA2MS4wNDc5IDI1LjEzNjdDNjAuNjg3OCAyNS4xMzY3IDYwLjM1NzQgMjUuMDg0MyA2MC4wNTY2IDI0Ljk3OTVDNTkuNzU1OSAyNC44NzQ3IDU5LjQ5NjEgMjQuNzEyOSA1OS4yNzczIDI0LjQ5NDFDNTkuMDYzMiAyNC4yNzU0IDU4Ljg5NjggMjMuOTkwNiA1OC43NzgzIDIzLjYzOTZDNTguNjU5OCAyMy4yODg3IDU4LjYwMDYgMjIuODY5NSA1OC42MDA2IDIyLjM4MThWMTcuNjAzNUg2MC4yNDhWMjIuMzk1NUM2MC4yNDggMjIuNjY0NCA2MC4yNzk5IDIyLjg5IDYwLjM0MzggMjMuMDcyM0M2MC40MDc2IDIzLjI1IDYwLjQ5NDEgMjMuMzkzNiA2MC42MDM1IDIzLjUwMjlDNjAuNzEyOSAyMy42MTIzIDYwLjg0MDUgMjMuNjg5OCA2MC45ODYzIDIzLjczNTRDNjEuMTMyMiAyMy43ODA5IDYxLjI4NzEgMjMuODAzNyA2MS40NTEyIDIzLjgwMzdDNjEuOTIwNiAyMy44MDM3IDYyLjI4OTcgMjMuNzEyNiA2Mi41NTg2IDIzLjUzMDNDNjIuODMyIDIzLjM0MzQgNjMuMDI1NyAyMy4wOTI4IDYzLjEzOTYgMjIuNzc4M0M2My4yNTgxIDIyLjQ2MzkgNjMuMzE3NCAyMi4xMTA3IDYzLjMxNzQgMjEuNzE4OFpNNjguMzMyIDE5LjAxMTdWMjVINjYuNjg0NlYxNy42MDM1SDY4LjI1NjhMNjguMzMyIDE5LjAxMTdaTTcwLjU5NDcgMTcuNTU1N0w3MC41ODExIDE5LjA4NjlDNzAuNDgwOCAxOS4wNjg3IDcwLjM3MTQgMTkuMDU1IDcwLjI1MjkgMTkuMDQ1OUM3MC4xMzkgMTkuMDM2OCA3MC4wMjUxIDE5LjAzMjIgNjkuOTExMSAxOS4wMzIyQzY5LjYyODYgMTkuMDMyMiA2OS4zODAyIDE5LjA3MzIgNjkuMTY2IDE5LjE1NTNDNjguOTUxOCAxOS4yMzI3IDY4Ljc3MTggMTkuMzQ2NyA2OC42MjYgMTkuNDk3MUM2OC40ODQ3IDE5LjY0MjkgNjguMzc1MyAxOS44MjA2IDY4LjI5NzkgMjAuMDMwM0M2OC4yMjA0IDIwLjIzOTkgNjguMTc0OCAyMC40NzQ2IDY4LjE2MTEgMjAuNzM0NEw2Ny43ODUyIDIwLjc2MTdDNjcuNzg1MiAyMC4yOTY5IDY3LjgzMDcgMTkuODY2MiA2Ny45MjE5IDE5LjQ2OTdDNjguMDEzIDE5LjA3MzIgNjguMTQ5NyAxOC43MjQ2IDY4LjMzMiAxOC40MjM4QzY4LjUxODkgMTguMTIzIDY4Ljc1MTMgMTcuODg4MyA2OS4wMjkzIDE3LjcxOTdDNjkuMzExOCAxNy41NTExIDY5LjYzNzcgMTcuNDY2OCA3MC4wMDY4IDE3LjQ2NjhDNzAuMTA3MSAxNy40NjY4IDcwLjIxNDIgMTcuNDc1OSA3MC4zMjgxIDE3LjQ5NDFDNzAuNDQ2NiAxNy41MTI0IDcwLjUzNTUgMTcuNTMyOSA3MC41OTQ3IDE3LjU1NTdaTTgwLjEyNSAyMy40Njg4VjE0LjVIODEuNzc5M1YyNUg4MC4yODIyTDgwLjEyNSAyMy40Njg4Wk03NS4zMTI1IDIxLjM4MzhWMjEuMjQwMkM3NS4zMTI1IDIwLjY3OTcgNzUuMzc4NiAyMC4xNjkzIDc1LjUxMDcgMTkuNzA5Qzc1LjY0MjkgMTkuMjQ0MSA3NS44MzQzIDE4Ljg0NTQgNzYuMDg1IDE4LjUxMjdDNzYuMzM1NiAxOC4xNzU1IDc2LjY0MSAxNy45MTggNzcuMDAxIDE3Ljc0MDJDNzcuMzYxIDE3LjU1NzkgNzcuNzY2NiAxNy40NjY4IDc4LjIxNzggMTcuNDY2OEM3OC42NjQ0IDE3LjQ2NjggNzkuMDU2MyAxNy41NTM0IDc5LjM5MzYgMTcuNzI2NkM3OS43MzA4IDE3Ljg5OTcgODAuMDE3OSAxOC4xNDgxIDgwLjI1NDkgMTguNDcxN0M4MC40OTE5IDE4Ljc5MDcgODAuNjgxIDE5LjE3MzUgODAuODIyMyAxOS42MjAxQzgwLjk2MzUgMjAuMDYyMiA4MS4wNjM4IDIwLjU1NDQgODEuMTIzIDIxLjA5NjdWMjEuNTU0N0M4MS4wNjM4IDIyLjA4MzMgODAuOTYzNSAyMi41NjY0IDgwLjgyMjMgMjMuMDAzOUM4MC42ODEgMjMuNDQxNCA4MC40OTE5IDIzLjgxOTcgODAuMjU0OSAyNC4xMzg3QzgwLjAxNzkgMjQuNDU3NyA3OS43Mjg1IDI0LjcwMzggNzkuMzg2NyAyNC44NzdDNzkuMDQ5NSAyNS4wNTAxIDc4LjY1NTMgMjUuMTM2NyA3OC4yMDQxIDI1LjEzNjdDNzcuNzU3NSAyNS4xMzY3IDc3LjM1NDIgMjUuMDQzMyA3Ni45OTQxIDI0Ljg1NjRDNzYuNjM4NyAyNC42Njk2IDc2LjMzNTYgMjQuNDA3NiA3Ni4wODUgMjQuMDcwM0M3NS44MzQzIDIzLjczMzEgNzUuNjQyOSAyMy4zMzY2IDc1LjUxMDcgMjIuODgwOUM3NS4zNzg2IDIyLjQyMDYgNzUuMzEyNSAyMS45MjE1IDc1LjMxMjUgMjEuMzgzOFpNNzYuOTYgMjEuMjQwMlYyMS4zODM4Qzc2Ljk2IDIxLjcyMSA3Ni45ODk2IDIyLjAzNTUgNzcuMDQ4OCAyMi4zMjcxQzc3LjExMjYgMjIuNjE4OCA3Ny4yMTA2IDIyLjg3NjMgNzcuMzQyOCAyMy4wOTk2Qzc3LjQ3NDkgMjMuMzE4NCA3Ny42NDU4IDIzLjQ5MTUgNzcuODU1NSAyMy42MTkxQzc4LjA2OTcgMjMuNzQyMiA3OC4zMjQ5IDIzLjgwMzcgNzguNjIxMSAyMy44MDM3Qzc4Ljk5NDggMjMuODAzNyA3OS4zMDI0IDIzLjcyMTcgNzkuNTQzOSAyMy41NTc2Qzc5Ljc4NTUgMjMuMzkzNiA3OS45NzQ2IDIzLjE3MjUgODAuMTExMyAyMi44OTQ1QzgwLjI1MjYgMjIuNjEyIDgwLjM0ODMgMjIuMjk3NSA4MC4zOTg0IDIxLjk1MTJWMjAuNzEzOUM4MC4zNzExIDIwLjQ0NSA4MC4zMTQxIDIwLjE5NDMgODAuMjI3NSAxOS45NjE5QzgwLjE0NTUgMTkuNzI5NSA4MC4wMzM5IDE5LjUyNjcgNzkuODkyNiAxOS4zNTM1Qzc5Ljc1MTMgMTkuMTc1OCA3OS41NzU4IDE5LjAzOTEgNzkuMzY2MiAxOC45NDM0Qzc5LjE2MTEgMTguODQzMSA3OC45MTczIDE4Ljc5MyA3OC42MzQ4IDE4Ljc5M0M3OC4zMzQgMTguNzkzIDc4LjA3ODggMTguODU2OCA3Ny44NjkxIDE4Ljk4NDRDNzcuNjU5NSAxOS4xMTIgNzcuNDg2MyAxOS4yODc0IDc3LjM0OTYgMTkuNTEwN0M3Ny4yMTc0IDE5LjczNCA3Ny4xMTk1IDE5Ljk5MzggNzcuMDU1NyAyMC4yOUM3Ni45OTE5IDIwLjU4NjMgNzYuOTYgMjAuOTAzIDc2Ljk2IDIxLjI0MDJaTTg1LjUxNTYgMTcuNjAzNVYyNUg4My44NjEzVjE3LjYwMzVIODUuNTE1NlpNODMuNzUyIDE1LjY2MjFDODMuNzUyIDE1LjQxMTUgODMuODM0IDE1LjIwNDEgODMuOTk4IDE1LjA0Qzg0LjE2NjcgMTQuODcxNCA4NC4zOTkxIDE0Ljc4NzEgODQuNjk1MyAxNC43ODcxQzg0Ljk4NyAxNC43ODcxIDg1LjIxNzEgMTQuODcxNCA4NS4zODU3IDE1LjA0Qzg1LjU1NDQgMTUuMjA0MSA4NS42Mzg3IDE1LjQxMTUgODUuNjM4NyAxNS42NjIxQzg1LjYzODcgMTUuOTA4MiA4NS41NTQ0IDE2LjExMzMgODUuMzg1NyAxNi4yNzczQzg1LjIxNzEgMTYuNDQxNCA4NC45ODcgMTYuNTIzNCA4NC42OTUzIDE2LjUyMzRDODQuMzk5MSAxNi41MjM0IDg0LjE2NjcgMTYuNDQxNCA4My45OTggMTYuMjc3M0M4My44MzQgMTYuMTEzMyA4My43NTIgMTUuOTA4MiA4My43NTIgMTUuNjYyMVpNODcuMjU1OSAyMS4zODM4VjIxLjIyNjZDODcuMjU1OSAyMC42OTM0IDg3LjMzMzMgMjAuMTk4OSA4Ny40ODgzIDE5Ljc0MzJDODcuNjQzMiAxOS4yODI5IDg3Ljg2NjUgMTguODg0MSA4OC4xNTgyIDE4LjU0NjlDODguNDU0NCAxOC4yMDUxIDg4LjgxNDUgMTcuOTQwOCA4OS4yMzgzIDE3Ljc1MzlDODkuNjY2NyAxNy41NjI1IDkwLjE0OTcgMTcuNDY2OCA5MC42ODc1IDE3LjQ2NjhDOTEuMjI5OCAxNy40NjY4IDkxLjcxMjkgMTcuNTYyNSA5Mi4xMzY3IDE3Ljc1MzlDOTIuNTY1MSAxNy45NDA4IDkyLjkyNzQgMTguMjA1MSA5My4yMjM2IDE4LjU0NjlDOTMuNTE5OSAxOC44ODQxIDkzLjc0NTQgMTkuMjgyOSA5My45MDA0IDE5Ljc0MzJDOTQuMDU1MyAyMC4xOTg5IDk0LjEzMjggMjAuNjkzNCA5NC4xMzI4IDIxLjIyNjZWMjEuMzgzOEM5NC4xMzI4IDIxLjkxNyA5NC4wNTUzIDIyLjQxMTUgOTMuOTAwNCAyMi44NjcyQzkzLjc0NTQgMjMuMzIyOSA5My41MTk5IDIzLjcyMTcgOTMuMjIzNiAyNC4wNjM1QzkyLjkyNzQgMjQuNDAwNyA5Mi41Njc0IDI0LjY2NSA5Mi4xNDM2IDI0Ljg1NjRDOTEuNzE5NyAyNS4wNDMzIDkxLjIzODkgMjUuMTM2NyA5MC43MDEyIDI1LjEzNjdDOTAuMTU4OSAyNS4xMzY3IDg5LjY3MzUgMjUuMDQzMyA4OS4yNDUxIDI0Ljg1NjRDODguODIxMyAyNC42NjUgODguNDYxMyAyNC40MDA3IDg4LjE2NSAyNC4wNjM1Qzg3Ljg2ODggMjMuNzIxNyA4Ny42NDMyIDIzLjMyMjkgODcuNDg4MyAyMi44NjcyQzg3LjMzMzMgMjIuNDExNSA4Ny4yNTU5IDIxLjkxNyA4Ny4yNTU5IDIxLjM4MzhaTTg4LjkwMzMgMjEuMjI2NlYyMS4zODM4Qzg4LjkwMzMgMjEuNzE2NSA4OC45Mzc1IDIyLjAzMDkgODkuMDA1OSAyMi4zMjcxQzg5LjA3NDIgMjIuNjIzNCA4OS4xODEzIDIyLjg4MzEgODkuMzI3MSAyMy4xMDY0Qzg5LjQ3MyAyMy4zMjk4IDg5LjY1OTggMjMuNTA1MiA4OS44ODc3IDIzLjYzMjhDOTAuMTE1NiAyMy43NjA0IDkwLjM4NjcgMjMuODI0MiA5MC43MDEyIDIzLjgyNDJDOTEuMDA2NSAyMy44MjQyIDkxLjI3MDggMjMuNzYwNCA5MS40OTQxIDIzLjYzMjhDOTEuNzIyIDIzLjUwNTIgOTEuOTA4OSAyMy4zMjk4IDkyLjA1NDcgMjMuMTA2NEM5Mi4yMDA1IDIyLjg4MzEgOTIuMzA3NiAyMi42MjM0IDkyLjM3NiAyMi4zMjcxQzkyLjQ0ODkgMjIuMDMwOSA5Mi40ODU0IDIxLjcxNjUgOTIuNDg1NCAyMS4zODM4VjIxLjIyNjZDOTIuNDg1NCAyMC44OTg0IDkyLjQ0ODkgMjAuNTg4NSA5Mi4zNzYgMjAuMjk2OUM5Mi4zMDc2IDIwLjAwMDcgOTIuMTk4MiAxOS43Mzg2IDkyLjA0NzkgMTkuNTEwN0M5MS45MDIgMTkuMjgyOSA5MS43MTUyIDE5LjEwNTEgOTEuNDg3MyAxOC45Nzc1QzkxLjI2NCAxOC44NDU0IDkwLjk5NzQgMTguNzc5MyA5MC42ODc1IDE4Ljc3OTNDOTAuMzc3NiAxOC43NzkzIDkwLjEwODcgMTguODQ1NCA4OS44ODA5IDE4Ljk3NzVDODkuNjU3NiAxOS4xMDUxIDg5LjQ3MyAxOS4yODI5IDg5LjMyNzEgMTkuNTEwN0M4OS4xODEzIDE5LjczODYgODkuMDc0MiAyMC4wMDA3IDg5LjAwNTkgMjAuMjk2OUM4OC45Mzc1IDIwLjU4ODUgODguOTAzMyAyMC44OTg0IDg4LjkwMzMgMjEuMjI2NlpNOTYuODcxMSAxNy42MDM1TDk4LjI3OTMgMjAuMDU3Nkw5OS43MTQ4IDE3LjYwMzVIMTAxLjUyNkw5OS4yODQyIDIxLjIzMzRMMTAxLjYxNSAyNUg5OS44MDM3TDk4LjI5OTggMjIuNDQzNEw5Ni43OTU5IDI1SDk0Ljk3NzVMOTcuMzAxOCAyMS4yMzM0TDk1LjA2NjQgMTcuNjAzNUg5Ni44NzExWk0xMDQuNjY4IDE3LjYwMzVWMjVIMTAzLjAxNFYxNy42MDM1SDEwNC42NjhaTTEwMi45MDQgMTUuNjYyMUMxMDIuOTA0IDE1LjQxMTUgMTAyLjk4NiAxNS4yMDQxIDEwMy4xNSAxNS4wNEMxMDMuMzE5IDE0Ljg3MTQgMTAzLjU1MSAxNC43ODcxIDEwMy44NDggMTQuNzg3MUMxMDQuMTM5IDE0Ljc4NzEgMTA0LjM2OSAxNC44NzE0IDEwNC41MzggMTUuMDRDMTA0LjcwNyAxNS4yMDQxIDEwNC43OTEgMTUuNDExNSAxMDQuNzkxIDE1LjY2MjFDMTA0Ljc5MSAxNS45MDgyIDEwNC43MDcgMTYuMTEzMyAxMDQuNTM4IDE2LjI3NzNDMTA0LjM2OSAxNi40NDE0IDEwNC4xMzkgMTYuNTIzNCAxMDMuODQ4IDE2LjUyMzRDMTAzLjU1MSAxNi41MjM0IDEwMy4zMTkgMTYuNDQxNCAxMDMuMTUgMTYuMjc3M0MxMDIuOTg2IDE2LjExMzMgMTAyLjkwNCAxNS45MDgyIDEwMi45MDQgMTUuNjYyMVpNMTExLjIzNCAyMy40Njg4VjE0LjVIMTEyLjg4OVYyNUgxMTEuMzkyTDExMS4yMzQgMjMuNDY4OFpNMTA2LjQyMiAyMS4zODM4VjIxLjI0MDJDMTA2LjQyMiAyMC42Nzk3IDEwNi40ODggMjAuMTY5MyAxMDYuNjIgMTkuNzA5QzEwNi43NTIgMTkuMjQ0MSAxMDYuOTQ0IDE4Ljg0NTQgMTA3LjE5NCAxOC41MTI3QzEwNy40NDUgMTguMTc1NSAxMDcuNzUgMTcuOTE4IDEwOC4xMSAxNy43NDAyQzEwOC40NyAxNy41NTc5IDEwOC44NzYgMTcuNDY2OCAxMDkuMzI3IDE3LjQ2NjhDMTA5Ljc3NCAxNy40NjY4IDExMC4xNjYgMTcuNTUzNCAxMTAuNTAzIDE3LjcyNjZDMTEwLjg0IDE3Ljg5OTcgMTExLjEyNyAxOC4xNDgxIDExMS4zNjQgMTguNDcxN0MxMTEuNjAxIDE4Ljc5MDcgMTExLjc5IDE5LjE3MzUgMTExLjkzMiAxOS42MjAxQzExMi4wNzMgMjAuMDYyMiAxMTIuMTczIDIwLjU1NDQgMTEyLjIzMiAyMS4wOTY3VjIxLjU1NDdDMTEyLjE3MyAyMi4wODMzIDExMi4wNzMgMjIuNTY2NCAxMTEuOTMyIDIzLjAwMzlDMTExLjc5IDIzLjQ0MTQgMTExLjYwMSAyMy44MTk3IDExMS4zNjQgMjQuMTM4N0MxMTEuMTI3IDI0LjQ1NzcgMTEwLjgzOCAyNC43MDM4IDExMC40OTYgMjQuODc3QzExMC4xNTkgMjUuMDUwMSAxMDkuNzY1IDI1LjEzNjcgMTA5LjMxMyAyNS4xMzY3QzEwOC44NjcgMjUuMTM2NyAxMDguNDY0IDI1LjA0MzMgMTA4LjEwNCAyNC44NTY0QzEwNy43NDggMjQuNjY5NiAxMDcuNDQ1IDI0LjQwNzYgMTA3LjE5NCAyNC4wNzAzQzEwNi45NDQgMjMuNzMzMSAxMDYuNzUyIDIzLjMzNjYgMTA2LjYyIDIyLjg4MDlDMTA2LjQ4OCAyMi40MjA2IDEwNi40MjIgMjEuOTIxNSAxMDYuNDIyIDIxLjM4MzhaTTEwOC4wNjkgMjEuMjQwMlYyMS4zODM4QzEwOC4wNjkgMjEuNzIxIDEwOC4wOTkgMjIuMDM1NSAxMDguMTU4IDIyLjMyNzFDMTA4LjIyMiAyMi42MTg4IDEwOC4zMiAyMi44NzYzIDEwOC40NTIgMjMuMDk5NkMxMDguNTg0IDIzLjMxODQgMTA4Ljc1NSAyMy40OTE1IDEwOC45NjUgMjMuNjE5MUMxMDkuMTc5IDIzLjc0MjIgMTA5LjQzNCAyMy44MDM3IDEwOS43MyAyMy44MDM3QzExMC4xMDQgMjMuODAzNyAxMTAuNDEyIDIzLjcyMTcgMTEwLjY1MyAyMy41NTc2QzExMC44OTUgMjMuMzkzNiAxMTEuMDg0IDIzLjE3MjUgMTExLjIyMSAyMi44OTQ1QzExMS4zNjIgMjIuNjEyIDExMS40NTggMjIuMjk3NSAxMTEuNTA4IDIxLjk1MTJWMjAuNzEzOUMxMTEuNDggMjAuNDQ1IDExMS40MjQgMjAuMTk0MyAxMTEuMzM3IDE5Ljk2MTlDMTExLjI1NSAxOS43Mjk1IDExMS4xNDMgMTkuNTI2NyAxMTEuMDAyIDE5LjM1MzVDMTEwLjg2MSAxOS4xNzU4IDExMC42ODUgMTkuMDM5MSAxMTAuNDc2IDE4Ljk0MzRDMTEwLjI3MSAxOC44NDMxIDExMC4wMjcgMTguNzkzIDEwOS43NDQgMTguNzkzQzEwOS40NDMgMTguNzkzIDEwOS4xODggMTguODU2OCAxMDguOTc5IDE4Ljk4NDRDMTA4Ljc2OSAxOS4xMTIgMTA4LjU5NiAxOS4yODc0IDEwOC40NTkgMTkuNTEwN0MxMDguMzI3IDE5LjczNCAxMDguMjI5IDE5Ljk5MzggMTA4LjE2NSAyMC4yOUMxMDguMTAxIDIwLjU4NjMgMTA4LjA2OSAyMC45MDMgMTA4LjA2OSAyMS4yNDAyWk0xMTguMTIyIDI1LjEzNjdDMTE3LjU3NSAyNS4xMzY3IDExNy4wODEgMjUuMDQ3OSAxMTYuNjM5IDI0Ljg3MDFDMTE2LjIwMSAyNC42ODc4IDExNS44MjcgMjQuNDM0OSAxMTUuNTE4IDI0LjExMTNDMTE1LjIxMiAyMy43ODc4IDExNC45NzggMjMuNDA3MiAxMTQuODEzIDIyLjk2OTdDMTE0LjY0OSAyMi41MzIyIDExNC41NjcgMjIuMDYwNSAxMTQuNTY3IDIxLjU1NDdWMjEuMjgxMkMxMTQuNTY3IDIwLjcwMjUgMTE0LjY1MiAyMC4xNzg0IDExNC44MiAxOS43MDlDMTE0Ljk4OSAxOS4yMzk2IDExNS4yMjQgMTguODM4NSAxMTUuNTI0IDE4LjUwNTlDMTE1LjgyNSAxOC4xNjg2IDExNi4xODEgMTcuOTExMSAxMTYuNTkxIDE3LjczMzRDMTE3LjAwMSAxNy41NTU3IDExNy40NDUgMTcuNDY2OCAxMTcuOTI0IDE3LjQ2NjhDMTE4LjQ1MiAxNy40NjY4IDExOC45MTUgMTcuNTU1NyAxMTkuMzEyIDE3LjczMzRDMTE5LjcwOCAxNy45MTExIDEyMC4wMzYgMTguMTYxOCAxMjAuMjk2IDE4LjQ4NTRDMTIwLjU2IDE4LjgwNDQgMTIwLjc1NiAxOS4xODQ5IDEyMC44ODQgMTkuNjI3QzEyMS4wMTYgMjAuMDY5IDEyMS4wODIgMjAuNTU2NiAxMjEuMDgyIDIxLjA4OThWMjEuNzkzOUgxMTUuMzY3VjIwLjYxMTNIMTE5LjQ1NVYyMC40ODE0QzExOS40NDYgMjAuMTg1MiAxMTkuMzg3IDE5LjkwNzIgMTE5LjI3NyAxOS42NDc1QzExOS4xNzMgMTkuMzg3NyAxMTkuMDExIDE5LjE3ODEgMTE4Ljc5MiAxOS4wMTg2QzExOC41NzMgMTguODU5IDExOC4yODIgMTguNzc5MyAxMTcuOTE3IDE4Ljc3OTNDMTE3LjY0NCAxOC43NzkzIDExNy40IDE4LjgzODUgMTE3LjE4NiAxOC45NTdDMTE2Ljk3NiAxOS4wNzEgMTE2LjggMTkuMjM3MyAxMTYuNjU5IDE5LjQ1NjFDMTE2LjUxOCAxOS42NzQ4IDExNi40MDkgMTkuOTM5MSAxMTYuMzMxIDIwLjI0OUMxMTYuMjU4IDIwLjU1NDQgMTE2LjIyMiAyMC44OTg0IDExNi4yMjIgMjEuMjgxMlYyMS41NTQ3QzExNi4yMjIgMjEuODc4MyAxMTYuMjY1IDIyLjE3OSAxMTYuMzUyIDIyLjQ1N0MxMTYuNDQzIDIyLjczMDUgMTE2LjU3NSAyMi45Njk3IDExNi43NDggMjMuMTc0OEMxMTYuOTIxIDIzLjM3OTkgMTE3LjEzMSAyMy41NDE3IDExNy4zNzcgMjMuNjYwMkMxMTcuNjIzIDIzLjc3NDEgMTE3LjkwMyAyMy44MzExIDExOC4yMTggMjMuODMxMUMxMTguNjE0IDIzLjgzMTEgMTE4Ljk2NyAyMy43NTEzIDExOS4yNzcgMjMuNTkxOEMxMTkuNTg3IDIzLjQzMjMgMTE5Ljg1NiAyMy4yMDY3IDEyMC4wODQgMjIuOTE1TDEyMC45NTIgMjMuNzU1OUMxMjAuNzkzIDIzLjk4ODMgMTIwLjU4NSAyNC4yMTE2IDEyMC4zMyAyNC40MjU4QzEyMC4wNzUgMjQuNjM1NCAxMTkuNzYzIDI0LjgwNjMgMTE5LjM5NCAyNC45Mzg1QzExOS4wMjkgMjUuMDcwNiAxMTguNjA1IDI1LjEzNjcgMTE4LjEyMiAyNS4xMzY3WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC44NyIvPgo8cGF0aCBkPSJNNjEuMzgwOSA3Mi4wOTk2TDU4LjAwNTkgNzEuMjczNEw1OS4zOTQ1IDU4LjQwNjJINzMuMTkzNFY2MS45OTIySDYyLjkxMDJMNjIuMjA3IDY4LjI2NzZDNjIuNjA1NSA2OC4wMzMyIDYzLjE1NjIgNjcuODA0NyA2My44NTk0IDY3LjU4MkM2NC41NjI1IDY3LjM0NzcgNjUuMzY1MiA2Ny4yMzA1IDY2LjI2NzYgNjcuMjMwNUM2Ny40NzQ2IDY3LjIzMDUgNjguNTUyNyA2Ny40Mjk3IDY5LjUwMiA2Ny44MjgxQzcwLjQ2MjkgNjguMjE0OCA3MS4yNzczIDY4Ljc4MzIgNzEuOTQ1MyA2OS41MzMyQzcyLjYxMzMgNzAuMjcxNSA3My4xMjMgNzEuMTczOCA3My40NzQ2IDcyLjI0MDJDNzMuODI2MiA3My4yOTQ5IDc0LjAwMiA3NC40ODQ0IDc0LjAwMiA3NS44MDg2Qzc0LjAwMiA3Ni45OTIyIDczLjgyNjIgNzguMDk5NiA3My40NzQ2IDc5LjEzMDlDNzMuMTM0OCA4MC4xNjIxIDcyLjYxOTEgODEuMDcwMyA3MS45Mjc3IDgxLjg1NTVDNzEuMjM2MyA4Mi42NDA2IDcwLjM2MzMgODMuMjU1OSA2OS4zMDg2IDgzLjcwMTJDNjguMjY1NiA4NC4xMzQ4IDY3LjAyOTMgODQuMzUxNiA2NS41OTk2IDg0LjM1MTZDNjQuNTMzMiA4NC4zNTE2IDYzLjUwNzggODQuMTk5MiA2Mi41MjM0IDgzLjg5NDVDNjEuNTUwOCA4My41NzgxIDYwLjY3NzcgODMuMTE1MiA1OS45MDQzIDgyLjUwNTlDNTkuMTMwOSA4MS44ODQ4IDU4LjUwOTggODEuMTIzIDU4LjA0MSA4MC4yMjA3QzU3LjU3MjMgNzkuMzA2NiA1Ny4zMDI3IDc4LjI1NzggNTcuMjMyNCA3Ny4wNzQySDYxLjM4MDlDNjEuNDg2MyA3Ny45MDYyIDYxLjcxNDggNzguNjE1MiA2Mi4wNjY0IDc5LjIwMTJDNjIuNDI5NyA3OS43NzU0IDYyLjkxMDIgODAuMjE0OCA2My41MDc4IDgwLjUxOTVDNjQuMTA1NSA4MC44MjQyIDY0Ljc5NjkgODAuOTc2NiA2NS41ODIgODAuOTc2NkM2Ni4yOTY5IDgwLjk3NjYgNjYuOTEyMSA4MC44NTM1IDY3LjQyNzcgODAuNjA3NEM2Ny45NDM0IDgwLjM0OTYgNjguMzcxMSA3OS45ODYzIDY4LjcxMDkgNzkuNTE3NkM2OS4wNjI1IDc5LjAzNzEgNjkuMzIwMyA3OC40NzQ2IDY5LjQ4NDQgNzcuODMwMUM2OS42NjAyIDc3LjE4NTUgNjkuNzQ4IDc2LjQ3MDcgNjkuNzQ4IDc1LjY4NTVDNjkuNzQ4IDc0LjkzNTUgNjkuNjQ4NCA3NC4yNSA2OS40NDkyIDczLjYyODlDNjkuMjYxNyA3My4wMDc4IDY4Ljk3NDYgNzIuNDY4OCA2OC41ODc5IDcyLjAxMTdDNjguMjEyOSA3MS41NTQ3IDY3LjczODMgNzEuMjAzMSA2Ny4xNjQxIDcwLjk1N0M2Ni41ODk4IDcwLjY5OTIgNjUuOTI3NyA3MC41NzAzIDY1LjE3NzcgNzAuNTcwM0M2NC4xNjk5IDcwLjU3MDMgNjMuMzk2NSA3MC43MTY4IDYyLjg1NzQgNzEuMDA5OEM2Mi4zMzAxIDcxLjMwMjcgNjEuODM3OSA3MS42NjYgNjEuMzgwOSA3Mi4wOTk2Wk05NC41MTc3IDY5LjA1ODZWNzMuMjQyMkM5NC41MTc3IDc1LjI0NjEgOTQuMzE4NSA3Ni45NTcgOTMuOTIwMSA3OC4zNzVDOTMuNTMzNCA3OS43ODEyIDkyLjk3MDkgODAuOTIzOCA5Mi4yMzI2IDgxLjgwMjdDOTEuNDk0MyA4Mi42ODE2IDkwLjYwOTUgODMuMzI2MiA4OS41NzgzIDgzLjczNjNDODguNTU4OCA4NC4xNDY1IDg3LjQxNjIgODQuMzUxNiA4Ni4xNTA1IDg0LjM1MTZDODUuMTQyNyA4NC4zNTE2IDg0LjIwNTIgODQuMjIyNyA4My4zMzggODMuOTY0OEM4Mi40ODI2IDgzLjcwNyA4MS43MDkxIDgzLjMwMjcgODEuMDE3NyA4Mi43NTJDODAuMzI2MyA4Mi4yMDEyIDc5LjczNDUgODEuNDkyMiA3OS4yNDIzIDgwLjYyNUM3OC43NjE5IDc5Ljc0NjEgNzguMzg2OSA3OC42OTczIDc4LjExNzMgNzcuNDc4NUM3Ny44NTk1IDc2LjI1OTggNzcuNzMwNiA3NC44NDc3IDc3LjczMDYgNzMuMjQyMlY2OS4wNTg2Qzc3LjczMDYgNjcuMDQzIDc3LjkyOTggNjUuMzQzOCA3OC4zMjgzIDYzLjk2MDlDNzguNzI2NyA2Mi41NjY0IDc5LjI5NTEgNjEuNDM1NSA4MC4wMzM0IDYwLjU2ODRDODAuNzcxNiA1OS42ODk1IDgxLjY1MDUgNTkuMDUwOCA4Mi42NzAxIDU4LjY1MjNDODMuNzAxMyA1OC4yNTM5IDg0Ljg0OTggNTguMDU0NyA4Ni4xMTU0IDU4LjA1NDdDODcuMTM0OSA1OC4wNTQ3IDg4LjA3MjQgNTguMTgzNiA4OC45Mjc5IDU4LjQ0MTRDODkuNzk1MSA1OC42ODc1IDkwLjU2ODUgNTkuMDgwMSA5MS4yNDgyIDU5LjYxOTFDOTEuOTM5NiA2MC4xNTgyIDkyLjUyNTUgNjAuODYxMyA5My4wMDYgNjEuNzI4NUM5My40OTgyIDYyLjU4NCA5My44NzMyIDYzLjYyMTEgOTQuMTMxIDY0LjgzOThDOTQuMzg4OCA2Ni4wNDY5IDk0LjUxNzcgNjcuNDUzMSA5NC41MTc3IDY5LjA1ODZaTTkwLjI4MTQgNzMuODM5OFY2OC40MjU4QzkwLjI4MTQgNjcuNDA2MiA5MC4yMjI4IDY2LjUwOTggOTAuMTA1NiA2NS43MzYzQzg5Ljk4ODQgNjQuOTUxMiA4OS44MTI3IDY0LjI4OTEgODkuNTc4MyA2My43NUM4OS4zNTU2IDYzLjE5OTIgODkuMDc0NCA2Mi43NTM5IDg4LjczNDUgNjIuNDE0MUM4OC4zOTQ3IDYyLjA2MjUgODguMDA4IDYxLjgxMDUgODcuNTc0NCA2MS42NTgyQzg3LjE0MDggNjEuNDk0MSA4Ni42NTQ1IDYxLjQxMjEgODYuMTE1NCA2MS40MTIxQzg1LjQ1OTEgNjEuNDEyMSA4NC44NzMyIDYxLjU0MSA4NC4zNTc2IDYxLjc5ODhDODMuODQyIDYyLjA0NDkgODMuNDA4NCA2Mi40NDM0IDgzLjA1NjggNjIuOTk0MUM4Mi43MDUyIDYzLjU0NDkgODIuNDM1NyA2NC4yNzE1IDgyLjI0ODIgNjUuMTczOEM4Mi4wNzI0IDY2LjA2NDUgODEuOTg0NSA2Ny4xNDg0IDgxLjk4NDUgNjguNDI1OFY3My44Mzk4QzgxLjk4NDUgNzQuODcxMSA4Mi4wNDMxIDc1Ljc3OTMgODIuMTYwMyA3Ni41NjQ1QzgyLjI3NzUgNzcuMzQ5NiA4Mi40NTMzIDc4LjAyMzQgODIuNjg3NyA3OC41ODU5QzgyLjkyMiA3OS4xMzY3IDgzLjIwMzMgNzkuNTkzOCA4My41MzE0IDc5Ljk1N0M4My44NzEzIDgwLjMwODYgODQuMjU4IDgwLjU2NjQgODQuNjkxNiA4MC43MzA1Qzg1LjEzNjkgODAuODk0NSA4NS42MjMyIDgwLjk3NjYgODYuMTUwNSA4MC45NzY2Qzg2LjgxODUgODAuOTc2NiA4Ny40MTAzIDgwLjg0NzcgODcuOTI1OSA4MC41ODk4Qzg4LjQ0MTYgODAuMzMyIDg4Ljg3NTIgNzkuOTIxOSA4OS4yMjY3IDc5LjM1OTRDODkuNTc4MyA3OC43ODUyIDg5Ljg0MiA3OC4wNDEgOTAuMDE3NyA3Ny4xMjdDOTAuMTkzNSA3Ni4yMTI5IDkwLjI4MTQgNzUuMTE3MiA5MC4yODE0IDczLjgzOThaIiBmaWxsPSIjM0ZBNzFBIi8+CjxwYXRoIGQ9Ik0xMDQuNjEyIDc2LjYwMzVIMTA1Ljg3N1Y4NEgxMDQuNzM1TDEwNC42MTIgODIuMjkxVjc2LjYwMzVaTTEwNC44MzggODAuNTYxNUwxMDUuMzEgODAuNTQ3OUMxMDUuMzEgODEuMDY3NCAxMDUuMjYyIDgxLjU0NTkgMTA1LjE2NiA4MS45ODM0QzEwNS4wNzUgODIuNDIwOSAxMDQuOTMxIDgyLjgwMTQgMTA0LjczNSA4My4xMjVDMTA0LjUzOSA4My40NDg2IDEwNC4yODkgODMuNjk5MiAxMDMuOTgzIDgzLjg3N0MxMDMuNjc4IDg0LjA1NDcgMTAzLjMxMSA4NC4xNDM2IDEwMi44ODMgODQuMTQzNkMxMDIuNTM2IDg0LjE0MzYgMTAyLjIyNyA4NC4wOTggMTAxLjk1MyA4NC4wMDY4QzEwMS42ODQgODMuOTIwMiAxMDEuNDUyIDgzLjc4MTIgMTAxLjI1NiA4My41ODk4QzEwMS4wNjQgODMuMzkzOSAxMDAuOTA1IDgzLjE0MSAxMDAuNzc3IDgyLjgzMTFMMTAwLjUyNCA4MC44NTU1SDEwMS4zMTdDMTAxLjMxNyA4MS4zMjQ5IDEwMS4zNTYgODEuNzA3NyAxMDEuNDM0IDgyLjAwMzlDMTAxLjUxMSA4Mi4zMDAxIDEwMS42MTggODIuNTI4IDEwMS43NTUgODIuNjg3NUMxMDEuODk2IDgyLjg0NyAxMDIuMDU4IDgyLjk1NjQgMTAyLjI0IDgzLjAxNTZDMTAyLjQyMyA4My4wNzQ5IDEwMi42MTggODMuMTA0NSAxMDIuODI4IDgzLjEwNDVDMTAzLjIzNCA4My4xMDQ1IDEwMy41NjYgODMuMDQzIDEwMy44MjYgODIuOTE5OUMxMDQuMDkgODIuNzkyMyAxMDQuMjk2IDgyLjYxNDYgMTA0LjQ0MSA4Mi4zODY3QzEwNC41ODcgODIuMTU4OSAxMDQuNjkgODEuODkgMTA0Ljc0OSA4MS41ODAxQzEwNC44MDggODEuMjcwMiAxMDQuODM4IDgwLjkzMDcgMTA0LjgzOCA4MC41NjE1Wk0xMDAuMDYgNzYuNjAzNUgxMDEuMzE3Vjg2Ljg0MzhIMTAwLjA2Vjc2LjYwMzVaTTExMi44OTkgNzYuNjAzNUgxMTQuMDQ4VjgzLjg0MjhDMTE0LjA0OCA4NC40OTQ1IDExMy45MTUgODUuMDUwNSAxMTMuNjUxIDg1LjUxMDdDMTEzLjM4NyA4NS45NzEgMTEzLjAxOCA4Ni4zMTk3IDExMi41NDQgODYuNTU2NkMxMTIuMDc0IDg2Ljc5ODIgMTExLjUzMiA4Ni45MTg5IDExMC45MTcgODYuOTE4OUMxMTAuNjYyIDg2LjkxODkgMTEwLjM2MSA4Ni44Nzc5IDExMC4wMTQgODYuNzk1OUMxMDkuNjczIDg2LjcxODQgMTA5LjMzNSA4Ni41ODQgMTA5LjAwMyA4Ni4zOTI2QzEwOC42NzUgODYuMjA1NyAxMDguMzk5IDg1Ljk1MjggMTA4LjE3NiA4NS42MzM4TDEwOC44MzkgODQuODgxOEMxMDkuMTQ5IDg1LjI1NTUgMTA5LjQ3MiA4NS41MTUzIDEwOS44MDkgODUuNjYxMUMxMTAuMTUxIDg1LjgwNyAxMTAuNDg4IDg1Ljg3OTkgMTEwLjgyMSA4NS44Nzk5QzExMS4yMjIgODUuODc5OSAxMTEuNTY4IDg1LjgwNDcgMTExLjg2IDg1LjY1NDNDMTEyLjE1MiA4NS41MDM5IDExMi4zNzcgODUuMjgwNiAxMTIuNTM3IDg0Ljk4NDRDMTEyLjcwMSA4NC42OTI3IDExMi43ODMgODQuMzMyNyAxMTIuNzgzIDgzLjkwNDNWNzguMjMwNUwxMTIuODk5IDc2LjYwMzVaTTEwNy44MDYgODAuMzgzOFY4MC4yNDAyQzEwNy44MDYgNzkuNjc1MSAxMDcuODczIDc5LjE2MjQgMTA4LjAwNSA3OC43MDIxQzEwOC4xNDEgNzguMjM3MyAxMDguMzM1IDc3LjgzODUgMTA4LjU4NiA3Ny41MDU5QzEwOC44NDEgNzcuMTczMiAxMDkuMTQ5IDc2LjkxOCAxMDkuNTA5IDc2Ljc0MDJDMTA5Ljg2OSA3Ni41NTc5IDExMC4yNzQgNzYuNDY2OCAxMTAuNzI1IDc2LjQ2NjhDMTExLjE5IDc2LjQ2NjggMTExLjU5NiA3Ni41NDg4IDExMS45NDIgNzYuNzEyOUMxMTIuMjkzIDc2Ljg3MjQgMTEyLjU4OSA3Ny4xMDcxIDExMi44MzEgNzcuNDE3QzExMy4wNzcgNzcuNzIyMyAxMTMuMjcxIDc4LjA5MTUgMTEzLjQxMiA3OC41MjQ0QzExMy41NTMgNzguOTU3NCAxMTMuNjUxIDc5LjQ0NzMgMTEzLjcwNiA3OS45OTQxVjgwLjYyM0MxMTMuNjU2IDgxLjE2NTQgMTEzLjU1OCA4MS42NTMgMTEzLjQxMiA4Mi4wODU5QzExMy4yNzEgODIuNTE4OSAxMTMuMDc3IDgyLjg4OCAxMTIuODMxIDgzLjE5MzRDMTEyLjU4OSA4My40OTg3IDExMi4yOTMgODMuNzMzNCAxMTEuOTQyIDgzLjg5NzVDMTExLjU5MSA4NC4wNTcgMTExLjE4MSA4NC4xMzY3IDExMC43MTIgODQuMTM2N0MxMTAuMjcgODQuMTM2NyAxMDkuODY5IDg0LjA0MzMgMTA5LjUwOSA4My44NTY0QzEwOS4xNTMgODMuNjY5NiAxMDguODQ4IDgzLjQwNzYgMTA4LjU5MyA4My4wNzAzQzEwOC4zMzcgODIuNzMzMSAxMDguMTQxIDgyLjMzNjYgMTA4LjAwNSA4MS44ODA5QzEwNy44NzMgODEuNDIwNiAxMDcuODA2IDgwLjkyMTUgMTA3LjgwNiA4MC4zODM4Wk0xMDkuMDcxIDgwLjI0MDJWODAuMzgzOEMxMDkuMDcxIDgwLjc1MjkgMTA5LjEwOCA4MS4wOTkzIDEwOS4xOCA4MS40MjI5QzEwOS4yNTggODEuNzQ2NCAxMDkuMzc0IDgyLjAzMTIgMTA5LjUyOSA4Mi4yNzczQzEwOS42ODkgODIuNTIzNCAxMDkuODkxIDgyLjcxNzEgMTEwLjEzNyA4Mi44NTg0QzExMC4zODQgODIuOTk1MSAxMTAuNjc4IDgzLjA2MzUgMTExLjAxOSA4My4wNjM1QzExMS40MzkgODMuMDYzNSAxMTEuNzg1IDgyLjk3NDYgMTEyLjA1OCA4Mi43OTY5QzExMi4zMzIgODIuNjE5MSAxMTIuNTQ4IDgyLjM4NDQgMTEyLjcwOCA4Mi4wOTI4QzExMi44NzIgODEuODAxMSAxMTIuOTk5IDgxLjQ4NDQgMTEzLjA5MSA4MS4xNDI2Vjc5LjQ5NTFDMTEzLjA0IDc5LjI0NDUgMTEyLjk2MyA3OS4wMDI5IDExMi44NTggNzguNzcwNUMxMTIuNzU4IDc4LjUzMzUgMTEyLjYyNiA3OC4zMjM5IDExMi40NjIgNzguMTQxNkMxMTIuMzAyIDc3Ljk1NDggMTEyLjEwNCA3Ny44MDY2IDExMS44NjcgNzcuNjk3M0MxMTEuNjMgNzcuNTg3OSAxMTEuMzUyIDc3LjUzMzIgMTExLjAzMyA3Ny41MzMyQzExMC42ODcgNzcuNTMzMiAxMTAuMzg4IDc3LjYwNjEgMTEwLjEzNyA3Ny43NTJDMTA5Ljg5MSA3Ny44OTMyIDEwOS42ODkgNzguMDg5MiAxMDkuNTI5IDc4LjMzOThDMTA5LjM3NCA3OC41ODU5IDEwOS4yNTggNzguODczIDEwOS4xOCA3OS4yMDEyQzEwOS4xMDggNzkuNTI0NyAxMDkuMDcxIDc5Ljg3MTEgMTA5LjA3MSA4MC4yNDAyWk0xMjAuNTcxIDc0LjA0NjlMMTE2LjQyMSA4NC44NTQ1SDExNS4zMzVMMTE5LjQ5MSA3NC4wNDY5SDEyMC41NzFaTTEyMy40MSA3OC4wNzMyVjg0SDEyMi4xMzhWNzYuNjAzNUgxMjMuMzQxTDEyMy40MSA3OC4wNzMyWk0xMjMuMTUgODAuMDIxNUwxMjIuNTYyIDgwLjAwMUMxMjIuNTY2IDc5LjQ5NTEgMTIyLjYzMyA3OS4wMjggMTIyLjc2IDc4LjU5OTZDMTIyLjg4OCA3OC4xNjY3IDEyMy4wNzcgNzcuNzkwNyAxMjMuMzI4IDc3LjQ3MTdDMTIzLjU3OCA3Ny4xNTI3IDEyMy44OSA3Ni45MDY2IDEyNC4yNjQgNzYuNzMzNEMxMjQuNjM4IDc2LjU1NTcgMTI1LjA3MSA3Ni40NjY4IDEyNS41NjMgNzYuNDY2OEMxMjUuOTA5IDc2LjQ2NjggMTI2LjIyOCA3Ni41MTY5IDEyNi41MiA3Ni42MTcyQzEyNi44MTIgNzYuNzEyOSAxMjcuMDY1IDc2Ljg2NTYgMTI3LjI3OSA3Ny4wNzUyQzEyNy40OTMgNzcuMjg0OCAxMjcuNjU5IDc3LjU1MzcgMTI3Ljc3OCA3Ny44ODE4QzEyNy44OTYgNzguMjEgMTI3Ljk1NSA3OC42MDY0IDEyNy45NTUgNzkuMDcxM1Y4NEgxMjYuNjkxVjc5LjEzMjhDMTI2LjY5MSA3OC43NDU0IDEyNi42MjUgNzguNDM1NSAxMjYuNDkzIDc4LjIwMzFDMTI2LjM2NSA3Ny45NzA3IDEyNi4xODMgNzcuODAyMSAxMjUuOTQ2IDc3LjY5NzNDMTI1LjcwOSA3Ny41ODc5IDEyNS40MzEgNzcuNTMzMiAxMjUuMTEyIDc3LjUzMzJDMTI0LjczOCA3Ny41MzMyIDEyNC40MjYgNzcuNTk5MyAxMjQuMTc1IDc3LjczMTRDMTIzLjkyNSA3Ny44NjM2IDEyMy43MjQgNzguMDQ1OSAxMjMuNTc0IDc4LjI3ODNDMTIzLjQyMyA3OC41MTA3IDEyMy4zMTQgNzguNzc3MyAxMjMuMjQ2IDc5LjA3ODFDMTIzLjE4MiA3OS4zNzQzIDEyMy4xNSA3OS42ODg4IDEyMy4xNSA4MC4wMjE1Wk0xMjcuOTQyIDc5LjMyNDJMMTI3LjA5NCA3OS41ODRDMTI3LjA5OSA3OS4xNzg0IDEyNy4xNjUgNzguNzg4NyAxMjcuMjkyIDc4LjQxNUMxMjcuNDI1IDc4LjA0MTMgMTI3LjYxNCA3Ny43MDg3IDEyNy44NiA3Ny40MTdDMTI4LjExIDc3LjEyNTMgMTI4LjQxOCA3Ni44OTUyIDEyOC43ODMgNzYuNzI2NkMxMjkuMTQ3IDc2LjU1MzQgMTI5LjU2NCA3Ni40NjY4IDEzMC4wMzQgNzYuNDY2OEMxMzAuNDMgNzYuNDY2OCAxMzAuNzgxIDc2LjUxOTIgMTMxLjA4NiA3Ni42MjRDMTMxLjM5NiA3Ni43Mjg4IDEzMS42NTYgNzYuODkwNiAxMzEuODY2IDc3LjEwOTRDMTMyLjA4IDc3LjMyMzYgMTMyLjI0MiA3Ny41OTkzIDEzMi4zNTEgNzcuOTM2NUMxMzIuNDYgNzguMjczOCAxMzIuNTE1IDc4LjY3NDggMTMyLjUxNSA3OS4xMzk2Vjg0SDEzMS4yNDRWNzkuMTI2QzEzMS4yNDQgNzguNzExMyAxMzEuMTc3IDc4LjM5IDEzMS4wNDUgNzguMTYyMUMxMzAuOTE4IDc3LjkyOTcgMTMwLjczNSA3Ny43Njc5IDEzMC40OTggNzcuNjc2OEMxMzAuMjY2IDc3LjU4MTEgMTI5Ljk4OCA3Ny41MzMyIDEyOS42NjQgNzcuNTMzMkMxMjkuMzg2IDc3LjUzMzIgMTI5LjE0IDc3LjU4MTEgMTI4LjkyNiA3Ny42NzY4QzEyOC43MTIgNzcuNzcyNSAxMjguNTMyIDc3LjkwNDYgMTI4LjM4NiA3OC4wNzMyQzEyOC4yNCA3OC4yMzczIDEyOC4xMjkgNzguNDI2NCAxMjguMDUxIDc4LjY0MDZDMTI3Ljk3OCA3OC44NTQ4IDEyNy45NDIgNzkuMDgyNyAxMjcuOTQyIDc5LjMyNDJaTTEzNS40ODQgNzYuMzAyN0gxMzYuMDU4QzEzNi4yODEgNzYuMzAyNyAxMzYuNDY2IDc2LjI3MDggMTM2LjYxMiA3Ni4yMDdDMTM2Ljc1NyA3Ni4xNDMyIDEzNi44NjcgNzYuMDU2NiAxMzYuOTQgNzUuOTQ3M0MxMzcuMDE3IDc1LjgzMzMgMTM3LjA1NiA3NS43MDM1IDEzNy4wNTYgNzUuNTU3NkMxMzcuMDU2IDc1LjM1NzEgMTM2Ljk4MyA3NS4xODg1IDEzNi44MzcgNzUuMDUxOEMxMzYuNjkxIDc0LjkxNSAxMzYuNDYxIDc0Ljg0NjcgMTM2LjE0NyA3NC44NDY3QzEzNS44ODcgNzQuODQ2NyAxMzUuNjcgNzQuOTAzNiAxMzUuNDk3IDc1LjAxNzZDMTM1LjMyOSA3NS4xMzE1IDEzNS4yNDQgNzUuMjg2NSAxMzUuMjQ0IDc1LjQ4MjRIMTM0LjE3MUMxMzQuMTcxIDc1LjE3MjUgMTM0LjI1OCA3NC45MDU5IDEzNC40MzEgNzQuNjgyNkMxMzQuNjA0IDc0LjQ1NDggMTM0LjgzOSA3NC4yNzkzIDEzNS4xMzUgNzQuMTU2MkMxMzUuNDMxIDc0LjAzMzIgMTM1Ljc2MiA3My45NzE3IDEzNi4xMjYgNzMuOTcxN0MxMzYuNTIzIDczLjk3MTcgMTM2Ljg3MSA3NC4wMzA5IDEzNy4xNzIgNzQuMTQ5NEMxMzcuNDczIDc0LjI2MzMgMTM3LjcwOCA3NC40MzQyIDEzNy44NzYgNzQuNjYyMUMxMzguMDQ1IDc0Ljg5IDEzOC4xMjkgNzUuMTcwMiAxMzguMTI5IDc1LjUwMjlDMTM4LjEyOSA3NS43NDQ1IDEzOC4wNTQgNzUuOTY3OCAxMzcuOTA0IDc2LjE3MjlDMTM3Ljc1OCA3Ni4zNzM0IDEzNy41NDMgNzYuNTM1MiAxMzcuMjYxIDc2LjY1ODJDMTM2Ljk4MyA3Ni43NzY3IDEzNi42NDMgNzYuODM1OSAxMzYuMjQyIDc2LjgzNTlIMTM1LjQ4NFY3Ni4zMDI3Wk0xMzUuNDg0IDc3LjA4ODlWNzYuNTU1N0gxMzYuMjQyQzEzNi42OTggNzYuNTU1NyAxMzcuMDcyIDc2LjYxMDQgMTM3LjM2MyA3Ni43MTk3QzEzNy42NTUgNzYuODI5MSAxMzcuODcyIDc2Ljk4NjMgMTM4LjAxMyA3Ny4xOTE0QzEzOC4xNTQgNzcuMzk2NSAxMzguMjI1IDc3LjY0NDkgMTM4LjIyNSA3Ny45MzY1QzEzOC4yMjUgNzguMjY5MiAxMzguMTM0IDc4LjU1NCAxMzcuOTUxIDc4Ljc5MUMxMzcuNzY5IDc5LjAyOCAxMzcuNTE4IDc5LjIwOCAxMzcuMTk5IDc5LjMzMTFDMTM2Ljg4NSA3OS40NTQxIDEzNi41MjkgNzkuNTE1NiAxMzYuMTMzIDc5LjUxNTZDMTM1Ljc5MSA3OS41MTU2IDEzNS40NjMgNzkuNDYwOSAxMzUuMTQ5IDc5LjM1MTZDMTM0LjgzOSA3OS4yMzc2IDEzNC41ODYgNzkuMDYyMiAxMzQuMzkgNzguODI1MkMxMzQuMTk0IDc4LjU4ODIgMTM0LjA5NiA3OC4yODUyIDEzNC4wOTYgNzcuOTE2SDEzNS4xNjlDMTM1LjE2OSA3OC4xMjExIDEzNS4yNiA3OC4yOTQzIDEzNS40NDMgNzguNDM1NUMxMzUuNjI1IDc4LjU3MjMgMTM1Ljg2NiA3OC42NDA2IDEzNi4xNjcgNzguNjQwNkMxMzYuNDg2IDc4LjY0MDYgMTM2LjcyOCA3OC41NyAxMzYuODkyIDc4LjQyODdDMTM3LjA2IDc4LjI4NzQgMTM3LjE0NSA3OC4xMDUxIDEzNy4xNDUgNzcuODgxOEMxMzcuMTQ1IDc3LjY5MDQgMTM3LjA5OSA3Ny41Mzc4IDEzNy4wMDggNzcuNDIzOEMxMzYuOTIxIDc3LjMwNTMgMTM2Ljc5NiA3Ny4yMjEgMTM2LjYzMiA3Ny4xNzA5QzEzNi40NzMgNzcuMTE2MiAxMzYuMjgxIDc3LjA4ODkgMTM2LjA1OCA3Ny4wODg5SDEzNS40ODRaIiBmaWxsPSIjM0ZBNzFBIi8+CjxwYXRoIGQ9Ik0xNDcuNDQ0IDcxLjc3NzhMMTU0LjU1NSA3MS43Nzc4TDE1NC41NTUgODAuNjY2N0wxNTEuMDA0IDgzLjMzMzNMMTQ3LjQ0NCA4MC42NjY3TDE0Ny40NDQgNzEuNzc3OFoiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl8xMTMxXzI3NzkpIiBmaWxsLW9wYWNpdHk9IjAuMyIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE1MC42NzQgODMuODk2QzE1MC44NTQgODQuMDc2MiAxNTEuMTQ2IDg0LjA3NjIgMTUxLjMyNiA4My44OTZMMTU0LjU1NSA4MC42NjY3TDE1My45MDMgODAuMDE0MkwxNTIuNDg0IDgxLjQzMzFDMTUyLjEwNyA4MS44MTA2IDE1MS40NjEgODEuNTQzMiAxNTEuNDYxIDgxLjAwOTNMMTUxLjQ2MSA3Ny4xMTExTDE1MC41MzkgNzcuMTExMUwxNTAuNTM5IDgxLjAwOTNDMTUwLjUzOSA4MS41NDMyIDE0OS44OTMgODEuODEwNiAxNDkuNTE2IDgxLjQzMzFMMTQ4LjA5NyA4MC4wMTQyTDE0Ny40NDQgODAuNjY2N0wxNTAuNjc0IDgzLjg5NloiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPHBhdGggZD0iTTE2Mi41NjggNzkuNzkzVjgwLjkxOEgxNTkuNDY5Vjc5Ljc5M0gxNjIuNTY4Wk0xNjcuODI4IDc1LjQzOTVWODRIMTY2LjQxNlY3Ny4xMTUyTDE2NC4zMjQgNzcuODI0MlY3Ni42NTgyTDE2Ny42NTggNzUuNDM5NUgxNjcuODI4Wk0xNzUuMzA5IDc1LjQxNkgxNzUuNDg0Vjc2LjU3MDNIMTc1LjM4NUMxNzQuODgxIDc2LjU3MDMgMTc0LjQ0NSA3Ni42NDg0IDE3NC4wNzggNzYuODA0N0MxNzMuNzE1IDc2Ljk2MDkgMTczLjQxNiA3Ny4xNzU4IDE3My4xODIgNzcuNDQ5MkMxNzIuOTQ3IDc3LjcyMjcgMTcyLjc3MSA3OC4wNDMgMTcyLjY1NCA3OC40MTAyQzE3Mi41NDEgNzguNzczNCAxNzIuNDg0IDc5LjE2MDIgMTcyLjQ4NCA3OS41NzAzVjgwLjkxMjFDMTcyLjQ4NCA4MS4yNTIgMTcyLjUyMSA4MS41NTI3IDE3Mi41OTYgODEuODE0NUMxNzIuNjcgODIuMDcyMyAxNzIuNzczIDgyLjI4OTEgMTcyLjkwNiA4Mi40NjQ4QzE3My4wNDMgODIuNjM2NyAxNzMuMTk5IDgyLjc2NzYgMTczLjM3NSA4Mi44NTc0QzE3My41NTEgODIuOTQ3MyAxNzMuNzQgODIuOTkyMiAxNzMuOTQzIDgyLjk5MjJDMTc0LjE1NCA4Mi45OTIyIDE3NC4zNDYgODIuOTQ5MiAxNzQuNTE4IDgyLjg2MzNDMTc0LjY4OSA4Mi43NzM0IDE3NC44MzYgODIuNjUwNCAxNzQuOTU3IDgyLjQ5NDFDMTc1LjA3OCA4Mi4zMzc5IDE3NS4xNyA4Mi4xNTIzIDE3NS4yMzIgODEuOTM3NUMxNzUuMjk1IDgxLjcyMjcgMTc1LjMyNiA4MS40ODgzIDE3NS4zMjYgODEuMjM0NEMxNzUuMzI2IDgwLjk5MjIgMTc1LjI5NSA4MC43NjU2IDE3NS4yMzIgODAuNTU0N0MxNzUuMTc0IDgwLjMzOTggMTc1LjA4NiA4MC4xNTIzIDE3NC45NjkgNzkuOTkyMkMxNzQuODUyIDc5LjgyODEgMTc0LjcwNSA3OS43MDEyIDE3NC41MjkgNzkuNjExM0MxNzQuMzU3IDc5LjUxNzYgMTc0LjE1OCA3OS40NzA3IDE3My45MzIgNzkuNDcwN0MxNzMuNjUgNzkuNDcwNyAxNzMuMzk1IDc5LjUzNzEgMTczLjE2NCA3OS42Njk5QzE3Mi45MzggNzkuODAyNyAxNzIuNzU0IDc5Ljk3NjYgMTcyLjYxMyA4MC4xOTE0QzE3Mi40NzcgODAuNDAyMyAxNzIuNDAyIDgwLjYyNyAxNzIuMzkxIDgwLjg2NTJMMTcxLjg1MiA4MC42ODk1QzE3MS44ODMgODAuMzI2MiAxNzEuOTYzIDgwIDE3Mi4wOTIgNzkuNzEwOUMxNzIuMjI1IDc5LjQyMTkgMTcyLjM5OCA3OS4xNzU4IDE3Mi42MTMgNzguOTcyN0MxNzIuODI4IDc4Ljc2OTUgMTczLjA3NiA3OC42MTUyIDE3My4zNTcgNzguNTA5OEMxNzMuNjQzIDc4LjQwMDQgMTczLjk1NSA3OC4zNDU3IDE3NC4yOTUgNzguMzQ1N0MxNzQuNzA5IDc4LjM0NTcgMTc1LjA2OCA3OC40MjM4IDE3NS4zNzMgNzguNTgwMUMxNzUuNjc4IDc4LjczNjMgMTc1LjkzIDc4Ljk0OTIgMTc2LjEyOSA3OS4yMTg4QzE3Ni4zMzIgNzkuNDg0NCAxNzYuNDgyIDc5Ljc4OTEgMTc2LjU4IDgwLjEzMjhDMTc2LjY4MiA4MC40NzI3IDE3Ni43MzIgODAuODI4MSAxNzYuNzMyIDgxLjE5OTJDMTc2LjczMiA4MS42MDk0IDE3Ni42NyA4MS45OTIyIDE3Ni41NDUgODIuMzQ3N0MxNzYuNDIgODIuNjk5MiAxNzYuMjM2IDgzLjAwNzggMTc1Ljk5NCA4My4yNzM0QzE3NS43NTYgODMuNTM5MSAxNzUuNDY1IDgzLjc0NjEgMTc1LjEyMSA4My44OTQ1QzE3NC43ODEgODQuMDQzIDE3NC4zOTUgODQuMTE3MiAxNzMuOTYxIDg0LjExNzJDMTczLjUwNCA4NC4xMTcyIDE3My4wOTYgODQuMDI5MyAxNzIuNzM2IDgzLjg1MzVDMTcyLjM4MSA4My42Nzc3IDE3Mi4wNzggODMuNDM1NSAxNzEuODI4IDgzLjEyN0MxNzEuNTgyIDgyLjgxODQgMTcxLjM5NSA4Mi40NjI5IDE3MS4yNjYgODIuMDYwNUMxNzEuMTM3IDgxLjY1ODIgMTcxLjA3MiA4MS4yMzI0IDE3MS4wNzIgODAuNzgzMlY4MC4xOTczQzE3MS4wNzIgNzkuNTQ4OCAxNzEuMTU0IDc4LjkzNzUgMTcxLjMxOCA3OC4zNjMzQzE3MS40ODIgNzcuNzg1MiAxNzEuNzM0IDc3LjI3NTQgMTcyLjA3NCA3Ni44MzRDMTcyLjQxOCA3Ni4zOTI2IDE3Mi44NTcgNzYuMDQ2OSAxNzMuMzkzIDc1Ljc5NjlDMTczLjkyOCA3NS41NDMgMTc0LjU2NiA3NS40MTYgMTc1LjMwOSA3NS40MTZaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjg3Ii8+CjxwYXRoIGQ9Ik0xNzguNTg2IDc3LjU2MDVWNzcuMTA5NEMxNzguNTg2IDc2Ljc4NTIgMTc4LjY1NiA3Ni40OTAyIDE3OC43OTcgNzYuMjI0NkMxNzguOTM4IDc1Ljk1OSAxNzkuMTQzIDc1Ljc0NjEgMTc5LjQxMiA3NS41ODU5QzE3OS42ODIgNzUuNDI1OCAxODAuMDA2IDc1LjM0NTcgMTgwLjM4NSA3NS4zNDU3QzE4MC43NzUgNzUuMzQ1NyAxODEuMTA0IDc1LjQyNTggMTgxLjM2OSA3NS41ODU5QzE4MS42MzkgNzUuNzQ2MSAxODEuODQ0IDc1Ljk1OSAxODEuOTg0IDc2LjIyNDZDMTgyLjEyNSA3Ni40OTAyIDE4Mi4xOTUgNzYuNzg1MiAxODIuMTk1IDc3LjEwOTRWNzcuNTYwNUMxODIuMTk1IDc3Ljg3NyAxODIuMTI1IDc4LjE2OCAxODEuOTg0IDc4LjQzMzZDMTgxLjg0OCA3OC42OTkyIDE4MS42NDUgNzguOTEyMSAxODEuMzc1IDc5LjA3MjNDMTgxLjEwOSA3OS4yMzI0IDE4MC43ODMgNzkuMzEyNSAxODAuMzk2IDc5LjMxMjVDMTgwLjAxNCA3OS4zMTI1IDE3OS42ODYgNzkuMjMyNCAxNzkuNDEyIDc5LjA3MjNDMTc5LjE0MyA3OC45MTIxIDE3OC45MzggNzguNjk5MiAxNzguNzk3IDc4LjQzMzZDMTc4LjY1NiA3OC4xNjggMTc4LjU4NiA3Ny44NzcgMTc4LjU4NiA3Ny41NjA1Wk0xNzkuNTY0IDc3LjEwOTRWNzcuNTYwNUMxNzkuNTY0IDc3LjcxNjggMTc5LjU5NCA3Ny44NjUyIDE3OS42NTIgNzguMDA1OUMxNzkuNzE1IDc4LjE0NjUgMTc5LjgwOSA3OC4yNTk4IDE3OS45MzQgNzguMzQ1N0MxODAuMDU5IDc4LjQzMTYgMTgwLjIxMyA3OC40NzQ2IDE4MC4zOTYgNzguNDc0NkMxODAuNTg0IDc4LjQ3NDYgMTgwLjczNiA3OC40MzE2IDE4MC44NTQgNzguMzQ1N0MxODAuOTc1IDc4LjI1OTggMTgxLjA2NCA3OC4xNDY1IDE4MS4xMjMgNzguMDA1OUMxODEuMTgyIDc3Ljg2NTIgMTgxLjIxMSA3Ny43MTY4IDE4MS4yMTEgNzcuNTYwNVY3Ny4xMDk0QzE4MS4yMTEgNzYuOTQ5MiAxODEuMTggNzYuNzk4OCAxODEuMTE3IDc2LjY1ODJDMTgxLjA1OSA3Ni41MTM3IDE4MC45NjkgNzYuMzk4NCAxODAuODQ4IDc2LjMxMjVDMTgwLjcyNyA3Ni4yMjY2IDE4MC41NzIgNzYuMTgzNiAxODAuMzg1IDc2LjE4MzZDMTgwLjIwNSA3Ni4xODM2IDE4MC4wNTMgNzYuMjI2NiAxNzkuOTI4IDc2LjMxMjVDMTc5LjgwNyA3Ni4zOTg0IDE3OS43MTUgNzYuNTEzNyAxNzkuNjUyIDc2LjY1ODJDMTc5LjU5NCA3Ni43OTg4IDE3OS41NjQgNzYuOTQ5MiAxNzkuNTY0IDc3LjEwOTRaTTE4Mi42OTkgODIuMzY1MlY4MS45MDgyQzE4Mi42OTkgODEuNTg3OSAxODIuNzcgODEuMjk0OSAxODIuOTEgODEuMDI5M0MxODMuMDU1IDgwLjc2MzcgMTgzLjI2MiA4MC41NTA4IDE4My41MzEgODAuMzkwNkMxODMuODAxIDgwLjIzMDUgMTg0LjEyNSA4MC4xNTA0IDE4NC41MDQgODAuMTUwNEMxODQuODk1IDgwLjE1MDQgMTg1LjIyMyA4MC4yMzA1IDE4NS40ODggODAuMzkwNkMxODUuNzU4IDgwLjU1MDggMTg1Ljk2MSA4MC43NjM3IDE4Ni4wOTggODEuMDI5M0MxODYuMjM4IDgxLjI5NDkgMTg2LjMwOSA4MS41ODc5IDE4Ni4zMDkgODEuOTA4MlY4Mi4zNjUyQzE4Ni4zMDkgODIuNjg1NSAxODYuMjM4IDgyLjk3ODUgMTg2LjA5OCA4My4yNDQxQzE4NS45NjEgODMuNTA5OCAxODUuNzYgODMuNzIyNyAxODUuNDk0IDgzLjg4MjhDMTg1LjIyOSA4NC4wNDMgMTg0LjkwNCA4NC4xMjMgMTg0LjUyMSA4NC4xMjNDMTg0LjEzNSA4NC4xMjMgMTgzLjgwNSA4NC4wNDMgMTgzLjUzMSA4My44ODI4QzE4My4yNjIgODMuNzIyNyAxODMuMDU1IDgzLjUwOTggMTgyLjkxIDgzLjI0NDFDMTgyLjc3IDgyLjk3ODUgMTgyLjY5OSA4Mi42ODU1IDE4Mi42OTkgODIuMzY1MlpNMTgzLjY4NCA4MS45MDgyVjgyLjM2NTJDMTgzLjY4NCA4Mi41MjE1IDE4My43MTUgODIuNjY5OSAxODMuNzc3IDgyLjgxMDVDMTgzLjg0NCA4Mi45NTEyIDE4My45MzkgODMuMDY2NCAxODQuMDY0IDgzLjE1NjJDMTg0LjE4OSA4My4yNDIyIDE4NC4zNCA4My4yODUyIDE4NC41MTYgODMuMjg1MkMxODQuNzE1IDgzLjI4NTIgMTg0Ljg3NSA4My4yNDIyIDE4NC45OTYgODMuMTU2MkMxODUuMTE3IDgzLjA2NjQgMTg1LjIwMyA4Mi45NTMxIDE4NS4yNTQgODIuODE2NEMxODUuMzA5IDgyLjY3NTggMTg1LjMzNiA4Mi41MjU0IDE4NS4zMzYgODIuMzY1MlY4MS45MDgyQzE4NS4zMzYgODEuNzQ4IDE4NS4zMDUgODEuNTk3NyAxODUuMjQyIDgxLjQ1N0MxODUuMTg0IDgxLjMxNjQgMTg1LjA5MiA4MS4yMDMxIDE4NC45NjcgODEuMTE3MkMxODQuODQ2IDgxLjAzMTIgMTg0LjY5MSA4MC45ODgzIDE4NC41MDQgODAuOTg4M0MxODQuMzIgODAuOTg4MyAxODQuMTY4IDgxLjAzMTIgMTg0LjA0NyA4MS4xMTcyQzE4My45MjYgODEuMjAzMSAxODMuODM0IDgxLjMxNjQgMTgzLjc3MSA4MS40NTdDMTgzLjcxMyA4MS41OTc3IDE4My42ODQgODEuNzQ4IDE4My42ODQgODEuOTA4MlpNMTg0Ljg1NSA3Ni42ODc1TDE4MC42ODkgODMuMzU1NUwxNzkuOTY5IDgyLjkzOTVMMTg0LjEzNSA3Ni4yNzE1TDE4NC44NTUgNzYuNjg3NVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzExMzFfMjc3OSkiPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE4OC4wMDQgMTI0Ljk5OUwxMiAxMjUuMDAxTDEyIDEyNC4wMDFMMTg4LjAwNCAxMjMuOTk5TDE4OC4wMDQgMTI0Ljk5OVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuMTIiLz4KPHBhdGggZD0iTTEyIDEzMy43NzVDMTMuMDE2NiAxMzMuNzc1IDE4LjUwOTggMTMzLjc3NSAyMS4yNjMyIDEzMy43NzVMMzQuNjQzMyAxNDNMNDQuOTM1NyAxMTIuMjVMNjYuNTQ5NyAxMzQuOEw5MC4yMjIyIDExMi4yNUw5Ny40MjY5IDExMy4yNzVMMTExLjgzNiAxNDNIMTI2LjI0NkwxMzguNTk2IDExNi4zNUgxNDcuODZMMTU3LjEyMyAxMDJMMTY5LjQ3NCAxMDYuMUwxODggMTE2LjM1IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC44NyIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KPC9nPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzExMzFfMjc3OSIgeDE9IjE1MC42ODMiIHkxPSI4NC4wMjk4IiB4Mj0iMTUwLjY4MyIgeTI9IjcxLjc3NzgiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojZmZmO3N0b3Atb3BhY2l0eToxIiAvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiNmZmY7c3RvcC1vcGFjaXR5OjAiIC8+CjwvbGluZWFyR3JhZGllbnQ+CjxjbGlwUGF0aCBpZD0iY2xpcDBfMTEzMV8yNzc5Ij4KPHJlY3Qgd2lkdGg9IjE3NiIgaGVpZ2h0PSI2MCIgZmlsbD0id2hpdGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyIDkwKSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo=", + "description": "Produced by volcanoes and industrial processes, particularly the burning of coal and oil. Results displayed by combining the latest and aggregated values and optional simplified chart. ", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'sulfur', label: 'Sulfur dioxide', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'µg/m³', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'sulfur', 'µg/m³', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sulfur dioxide\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 600) {\\n\\tvalue = 600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#3FA71A\"},{\"from\":100,\"to\":200,\"color\":\"#80C32C\"},{\"from\":200,\"to\":350,\"color\":\"#FFA600\"},{\"from\":350,\"to\":500,\"color\":\"#F36900\"},{\"from\":500,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 600) {\\n\\tvalue = 600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nif (value < -10) {\\n\\tvalue = -10;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Sulfur dioxide\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"public\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "sulfur", + "sulfur dioxide" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/sulfur_dioxide__so2__chart_card_with_background.json b/application/src/main/data/json/system/widget_types/sulfur_dioxide__so2__chart_card_with_background.json new file mode 100644 index 0000000000..2d517b98b2 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/sulfur_dioxide__so2__chart_card_with_background.json @@ -0,0 +1,39 @@ +{ + "fqn": "sulfur_dioxide_so2_chart_card_with_background", + "name": "Sulfur dioxide (SO2) chart card with background", + "deprecated": false, + "image": "tb-image:U08yLXZhbHVlLWNoYXJ0LWNhcmQtd2l0aC1iYWNrZ3JvdW5kLnN2Zw==:U08yLXZhbHVlLWNoYXJ0LWNhcmQtd2l0aC1iYWNrZ3JvdW5kLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiIHRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDYgMTU0KSIvPjwvZz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBkPSJNMjAgMTMuM2E2LjcgNi43IDAgMCAwLTYuNyA2LjcgNi43IDYuNyAwIDAgMCA2LjcgNi43IDYuNyA2LjcgMCAwIDAgNi43LTYuNyA2LjcgNi43IDAgMCAwLTYuNy02LjdaTTE0LjcgMjBsLjEtMS4yTDE4IDIydi43YzAgLjcuNiAxLjMgMS4zIDEuM3YxLjNhNS4zIDUuMyAwIDAgMS00LjYtNS4zWm05LjIgMy42YTEuMyAxLjMgMCAwIDAtMS4yLTFIMjJ2LTJjMC0uMy0uMy0uNi0uNy0uNmgtNHYtMS4zaDEuNGMuMyAwIC42LS4zLjYtLjd2LTEuM2gxLjRjLjcgMCAxLjMtLjYgMS4zLTEuNHYtLjJhNS4zIDUuMyAwIDAgMSAzLjMgNC45YzAgMS40LS41IDIuNy0xLjQgMy42Wm0xNC4zLTEuMlYyMmExIDEgMCAwIDAtLjQtLjQgMi43IDIuNyAwIDAgMC0uNi0uNCA4LjcgOC43IDAgMCAwLTEtLjQgMTEuOCAxMS44IDAgMCAxLTEuNC0uNSA1IDUgMCAwIDEtMS0uNiAyLjcgMi43IDAgMCAxLS43LS45IDIuMyAyLjMgMCAwIDEtLjMtMSAyLjQgMi40IDAgMCAxIDEtMkEzLjUgMy41IDAgMCAxIDM1IDE1YTQuOCA0LjggMCAwIDEgMS41LS4yYy43IDAgMS4zLjEgMS45LjQuNS4zIDEgLjcgMS4yIDEuMS4zLjUuNSAxIC41IDEuNWgtMS44YzAtLjMgMC0uNi0uMi0uOGExLjQgMS40IDAgMCAwLS42LS42Yy0uMy0uMi0uNi0uMi0xLS4ybC0xIC4xYTEuNCAxLjQgMCAwIDAtLjcuNWwtLjIuOGMwIC4xIDAgLjMuMi41bC40LjQuNi40IDEgLjNhOS40IDkuNCAwIDAgMSAxLjQuNWMuNC4yLjguNCAxIC43YTIuNSAyLjUgMCAwIDEgLjkgMmMwIC4zLS4xLjctLjMgMWEyLjUgMi41IDAgMCAxLS43IDFsLTEuMS41YTUuMyA1LjMgMCAwIDEtMS41LjFjLS41IDAtMSAwLTEuNC0uMmE0LjIgNC4yIDAgMCAxLTEuMi0uNSAzIDMgMCAwIDEtMS0xYy0uMi0uNC0uMy0uOS0uMy0xLjRoMS43YzAgLjMgMCAuNi4yLjhsLjUuNi43LjNhMy40IDMuNCAwIDAgMCAxLjggMGMuMy0uMi41LS4zLjYtLjYuMi0uMi4yLS40LjItLjdabTcuOC45di01LjdoMS43VjI1SDQ2di0xLjdabS4zLTEuNmguNWMwIC41IDAgMS0uMiAxLjQgMCAuNC0uMi44LS41IDEtLjIuNC0uNS42LS44LjhhMyAzIDAgMCAxLTEuMy4yIDMgMyAwIDAgMS0xLS4xIDIgMiAwIDAgMS0uOC0uNSAyLjIgMi4yIDAgMCAxLS41LS45IDQgNCAwIDAgMS0uMi0xLjJ2LTQuOGgxLjd2NS41bC4zLjQuNC4yLjUuMWMuNSAwIC44IDAgMS4xLS4zLjMtLjIuNS0uNC42LS43bC4yLTFabTUtNy4yVjI1aC0xLjZWMTQuNWgxLjdaTTU1LjggMjVINTR2LTguMWMwLS42LjEtMSAuMy0xLjRhMiAyIDAgMCAxIDEtLjggMy40IDMuNCAwIDAgMSAyLjMtLjJsLS4xIDEuM2ExLjkgMS45IDAgMCAwLS4zLS4xIDQgNCAwIDAgMC0uMyAwbC0uNy4xYTEgMSAwIDAgMC0uNC40IDEuNCAxLjQgMCAwIDAtLjEuN1YyNVptMS41LTcuNHYxLjJoLTQuM3YtMS4yaDQuM1ptNS45IDUuN3YtNS43aDEuNlYyNWgtMS41bC0uMS0xLjdabS4yLTEuNmguNmMwIC41IDAgMS0uMiAxLjRsLS41IDFjLS4yLjQtLjUuNi0uOS44YTMgMyAwIDAgMS0xLjMuMiAzIDMgMCAwIDEtMS0uMSAyIDIgMCAwIDEtLjctLjUgMi4yIDIuMiAwIDAgMS0uNS0uOSA0IDQgMCAwIDEtLjItMS4ydi00LjhoMS42djQuOGwuMS43LjMuNC40LjIuNS4xYy40IDAgLjggMCAxLS4zLjMtLjIuNS0uNC42LS43LjItLjMuMi0uNy4yLTFabTUtMi43djZoLTEuNnYtNy40aDEuNlYxOVptMi4zLTEuNFYxOWE0LjMgNC4zIDAgMCAwLS43IDBoLS43YTEuNCAxLjQgMCAwIDAtLjYuNGwtLjMuNS0uMS43aC0uNGwuMS0xLjJjLjEtLjQuMi0uOC40LTEgLjItLjQuNS0uNi43LS44YTEuOSAxLjkgMCAwIDEgMS0uMiAyIDIgMCAwIDEgLjYgMFptOS41IDUuOXYtOWgxLjdWMjVoLTEuNWwtLjItMS41Wm0tNC44LTIuMXYtLjJjMC0uNSAwLTEgLjItMS41LjEtLjUuMy0uOS42LTEuMi4yLS4zLjUtLjYuOS0uOGwxLjItLjJjLjUgMCAuOSAwIDEuMi4ybC45LjguNSAxLjEuMyAxLjV2LjVhNy40IDcuNCAwIDAgMS0uMyAxLjQgNCA0IDAgMCAxLS41IDEuMSAyLjUgMi41IDAgMCAxLTEgLjhjLS4zLjItLjYuMi0xLjEuMi0uNCAwLS44IDAtMS4yLS4yYTIuNyAyLjcgMCAwIDEtMS0uOCAzLjcgMy43IDAgMCAxLS41LTEuMiA1LjQgNS40IDAgMCAxLS4yLTEuNVptMS43LS4ydjEuMmwuMy43Yy4yLjIuMy40LjYuNWwuNy4yYy40IDAgLjcgMCAxLS4ybC41LS43YTMgMyAwIDAgMCAuMy0xdi0xLjJhMyAzIDAgMCAwLS4yLS43IDIgMiAwIDAgMC0uMy0uNiAxLjQgMS40IDAgMCAwLS41LS41IDEuNiAxLjYgMCAwIDAtLjgtLjFjLS4zIDAtLjUgMC0uNy4ybC0uNi41YTIuNiAyLjYgMCAwIDAtLjIuOCA0LjUgNC41IDAgMCAwLS4xIDFabTguNS0zLjZWMjVIODR2LTcuNGgxLjZabS0xLjctMmMwLS4yIDAtLjQuMi0uNmwuNy0uMmMuMyAwIC41IDAgLjcuMmEuOC44IDAgMCAxIC4yLjcuOC44IDAgMCAxLS4yLjYgMSAxIDAgMCAxLS43LjIgMSAxIDAgMCAxLS43LS4yLjguOCAwIDAgMS0uMi0uNlptMy41IDUuOHYtLjJjMC0uNSAwLTEgLjItMS41LjEtLjQuNC0uOC43LTEuMmEzIDMgMCAwIDEgMS0uNyAzLjUgMy41IDAgMCAxIDEuNS0uM2MuNSAwIDEgMCAxLjQuMy41LjEuOC40IDEuMS43bC43IDEuMmMuMi41LjIgMSAuMiAxLjV2LjJjMCAuNSAwIDEtLjIgMS41YTMuNSAzLjUgMCAwIDEtLjcgMS4yIDMuMSAzLjEgMCAwIDEtMSAuOGwtMS41LjJhMy42IDMuNiAwIDAgMS0xLjUtLjIgMy4xIDMuMSAwIDAgMS0xLS44IDMuNSAzLjUgMCAwIDEtLjctMS4yIDQuNiA0LjYgMCAwIDEtLjItMS41Wm0xLjYtLjJ2LjJsLjEgMSAuMy43LjYuNWMuMi4yLjUuMi44LjJhMS42IDEuNiAwIDAgMCAxLjQtLjcgMi40IDIuNCAwIDAgMCAuMy0uOHYtMmEyLjQgMi40IDAgMCAwLS40LS44IDEuNiAxLjYgMCAwIDAtLjUtLjUgMS41IDEuNSAwIDAgMC0uOC0uMmMtLjMgMC0uNiAwLS44LjJhMS42IDEuNiAwIDAgMC0uNi41IDIuNSAyLjUgMCAwIDAtLjMuOCA0IDQgMCAwIDAtLjEgMVptOC0zLjYgMS40IDIuNSAxLjQtMi41aDEuOGwtMi4yIDMuNiAyLjMgMy44aC0xLjhsLTEuNS0yLjYtMS41IDIuNkg5NWwyLjMtMy44LTIuMi0zLjZoMS44Wm03LjggMFYyNUgxMDN2LTcuNGgxLjdabS0xLjgtMmMwLS4yIDAtLjQuMy0uNmwuNi0uMmMuMyAwIC42IDAgLjcuMmEuOC44IDAgMCAxIC4zLjcuOC44IDAgMCAxLS4zLjYgMSAxIDAgMCAxLS43LjIgMSAxIDAgMCAxLS42LS4yLjguOCAwIDAgMS0uMy0uNlptOC4zIDcuOXYtOWgxLjdWMjVoLTEuNWwtLjItMS41Wm0tNC44LTIuMXYtLjJjMC0uNSAwLTEgLjItMS41cy4zLS45LjYtMS4yYTIuNSAyLjUgMCAwIDEgMi4xLTFjLjUgMCAuOSAwIDEuMi4ybC45LjguNSAxLjEuMyAxLjV2LjVhNy40IDcuNCAwIDAgMS0uMyAxLjQgNCA0IDAgMCAxLS41IDEuMSAyLjUgMi41IDAgMCAxLS45LjhjLS4zLjItLjcuMi0xLjIuMi0uNCAwLS44IDAtMS4yLS4yYTIuNyAyLjcgMCAwIDEtMS0uOCAzLjcgMy43IDAgMCAxLS41LTEuMiA1LjQgNS40IDAgMCAxLS4yLTEuNVptMS43LS4ydjEuMmwuNC43YzAgLjIuMy40LjUuNWwuNy4yYy40IDAgLjcgMCAxLS4yYTEuNyAxLjcgMCAwIDAgLjUtLjdjLjItLjMuMy0uNi4zLTF2LTEuMmEzIDMgMCAwIDAtLjItLjcgMiAyIDAgMCAwLS4zLS42IDEuNCAxLjQgMCAwIDAtLjUtLjUgMS42IDEuNiAwIDAgMC0uOC0uMWMtLjMgMC0uNSAwLS43LjJsLS41LjVhMi42IDIuNiAwIDAgMC0uMy44IDQuNSA0LjUgMCAwIDAtLjEgMVptMTAgNGE0IDQgMCAwIDEtMS41LS4zIDMuMyAzLjMgMCAwIDEtMS0uOCAzLjQgMy40IDAgMCAxLS44LTEuMSA0IDQgMCAwIDEtLjItMS40di0uM2MwLS42IDAtMS4xLjItMS42cy40LS45LjctMS4yYTMgMyAwIDAgMSAxLS44bDEuNC0uMmMuNiAwIDEgMCAxLjQuMmwxIC44Yy4zLjMuNS43LjYgMS4xbC4yIDEuNXYuN2gtNS43di0xLjJoNHYtLjFhMi4zIDIuMyAwIDAgMC0uMS0uOSAxLjQgMS40IDAgMCAwLS41LS42bC0uOS0uMmMtLjMgMC0uNSAwLS43LjItLjIgMC0uNC4yLS41LjVsLS40Ljd2Mi4zbC40LjcuNy41LjguMWEyLjMgMi4zIDAgMCAwIDEuOS0uOWwuOS45YTMuNCAzLjQgMCAwIDEtLjcuNmMtLjIuMi0uNS40LTEgLjVhMy43IDMuNyAwIDAgMS0xLjIuMloiLz48cGF0aCBmaWxsPSIjM0I5MTFDIiBkPSJtNjEuNCA3Mi4xLTMuNC0uOCAxLjQtMTIuOWgxMy44VjYySDYyLjlsLS43IDYuM2E4LjMgOC4zIDAgMCAxIDQtMWMxLjMgMCAyLjQuMSAzLjMuNSAxIC40IDEuOCAxIDIuNCAxLjcuNy44IDEuMiAxLjcgMS42IDIuN2ExMS4zIDExLjMgMCAwIDEgMCA3IDcuNyA3LjcgMCAwIDEtMS42IDIuN2MtLjcuNy0xLjUgMS40LTIuNiAxLjhhMTAuMyAxMC4zIDAgMCAxLTYuOC4yIDguNCA4LjQgMCAwIDEtMi42LTEuNCA3IDcgMCAwIDEtMS45LTIuM2MtLjQtLjktLjctMi0uOC0zLjFoNC4yYzAgLjguMyAxLjUuNyAyLjFhMy42IDMuNiAwIDAgMCAxLjQgMS4zYy42LjMgMS4zLjUgMiAuNS44IDAgMS40LS4xIDItLjRhMy41IDMuNSAwIDAgMCAxLjItMWMuNC0uNi42LTEuMS44LTEuOGE4LjEgOC4xIDAgMCAwIDAtNC4yIDQuNSA0LjUgMCAwIDAtMS0xLjYgMy43IDMuNyAwIDAgMC0xLjMtMWMtLjYtLjMtMS4zLS40LTItLjRhNSA1IDAgMCAwLTIuMy40IDcuMyA3LjMgMCAwIDAtMS41IDEuMVptMzMuMS0zdjQuMWMwIDItLjIgMy44LS42IDUuMmE5IDkgMCAwIDEtMS43IDMuNCA2LjYgNi42IDAgMCAxLTIuNiAyYy0xIC4zLTIuMi42LTMuNC42YTkuOCA5LjggMCAwIDEtMi45LS40IDYuOSA2LjkgMCAwIDEtMi4zLTEuMiA3LjIgNy4yIDAgMCAxLTEuOC0yLjJjLS40LS45LS44LTEuOS0xLTMuMS0uMy0xLjItLjUtMi43LS41LTQuM3YtNC4xYzAtMiAuMi0zLjguNi01LjFhOSA5IDAgMCAxIDEuNy0zLjRjLjgtMSAxLjctMS41IDIuNy0yIDEtLjMgMi4yLS41IDMuNC0uNSAxIDAgMiAwIDIuOC4zYTYuNiA2LjYgMCAwIDEgNC4xIDMuM2MuNS45LjkgMiAxLjEgMy4xLjMgMS4yLjQgMi43LjQgNC4zWm0tNC4yIDQuN3YtNS40YzAtMSAwLTEuOS0uMi0yLjdhOCA4IDAgMCAwLS41LTIgNCA0IDAgMCAwLS45LTEuMyAzIDMgMCAwIDAtMS4xLS43IDQuMSA0LjEgMCAwIDAtMS41LS4zYy0uNiAwLTEuMi4xLTEuNy40LS42LjItMSAuNi0xLjMgMS4yLS40LjUtLjcgMS4zLS45IDIuMmwtLjIgMy4ydjUuNGMwIDEgMCAyIC4yIDIuOCAwIC44LjMgMS40LjUgMmwuOCAxLjRjLjQuMy44LjYgMS4yLjdhNC4yIDQuMiAwIDAgMCAxLjUuMyA0IDQgMCAwIDAgMS43LS40Yy41LS4zIDEtLjcgMS4zLTEuMi40LS42LjYtMS40LjgtMi4zLjItLjkuMy0yIC4zLTMuM1ptMTQuMyAyLjhoMS4zVjg0aC0xLjJ2LTcuNFptLjIgNGguNWE2LjcgNi43IDAgMCAxLS4xIDEuNCAzLjYgMy42IDAgMCAxLS41IDEuMSAyLjEgMi4xIDAgMCAxLS43LjhjLS4zLjItLjcuMi0xLjEuMmEzIDMgMCAwIDEtMSAwIDEuOCAxLjggMCAwIDEtLjYtLjUgMi40IDIuNCAwIDAgMS0uNS0uOGwtLjMtMmguOGwuMSAxLjIuNC43LjQuMy42LjFjLjQgMCAuOCAwIDEtLjIuMy0uMS41LS4zLjYtLjUuMi0uMi4zLS41LjMtLjhsLjEtMVptLTQuNy00aDEuMnYxMC4yaC0xLjJWNzYuNlptMTIuOCAwaDEuMXY3LjJjMCAuNyAwIDEuMi0uMyAxLjdzLS43LjgtMS4yIDFhMy41IDMuNSAwIDAgMS0yLjUuMyAzLjYgMy42IDAgMCAxLTEtLjQgMi41IDIuNSAwIDAgMS0uOC0uOGwuNi0uN2MuMy40LjcuNiAxIC44bDEgLjIgMS0uMmMuNC0uMi42LS40LjctLjcuMi0uMy4zLS43LjMtMS4xdi01LjdsLjEtMS42Wm0tNSAzLjh2LS4ybC4xLTEuNWMuMS0uNS4zLS45LjYtMS4yYTIuNiAyLjYgMCAwIDEgMi4xLTFjLjUgMCAuOSAwIDEuMi4ybDEgLjcuNSAxLjEuMyAxLjV2LjZhNi40IDYuNCAwIDAgMS0uMyAxLjUgMy40IDMuNCAwIDAgMS0uNiAxYy0uMi40LS41LjYtLjkuOGEzIDMgMCAwIDEtMi40IDAgMi43IDIuNyAwIDAgMS0xLS44IDMuNyAzLjcgMCAwIDEtLjUtMS4yIDUuNCA1LjQgMCAwIDEtLjItMS41Wm0xLjItLjJ2MS4ybC40LjkuNi42IDEgLjJjLjMgMCAuNy0uMSAxLS4zbC42LS43LjQtMXYtMS42YTMuNCAzLjQgMCAwIDAtLjItLjcgMi4yIDIuMiAwIDAgMC0uNC0uNyAxLjcgMS43IDAgMCAwLS42LS40IDIgMiAwIDAgMC0uOS0uMmwtLjkuM2ExLjcgMS43IDAgMCAwLS42LjUgMi44IDIuOCAwIDAgMC0uMy45bC0uMSAxWm0xMS41LTYuMkwxMTYuNCA4NWgtMWw0LTEwLjloMS4yWm0yLjggNHY2aC0xLjN2LTcuNGgxLjJsLjEgMS41Wm0tLjMgMmgtLjVjMC0uNSAwLTEgLjItMS40IDAtLjQuMy0uOC41LTEuMWwxLS44YTMgMyAwIDAgMSAxLjMtLjJsMSAuMWEyIDIgMCAwIDEgMS4yIDEuM2wuMiAxLjJWODRoLTEuM3YtNC45YzAtLjQgMC0uNy0uMi0uOWExLjEgMS4xIDAgMCAwLS42LS41IDIgMiAwIDAgMC0uOC0uMiAyIDIgMCAwIDAtMSAuMmMtLjIuMi0uNC4zLS41LjZsLS40LjhhNC41IDQuNSAwIDAgMCAwIDFabTQuOC0uNy0uOC4zYzAtLjQgMC0uOC4yLTEuMi4xLS40LjMtLjcuNi0xIC4yLS4zLjUtLjUuOS0uN2wxLjItLjIgMSAuMWEyIDIgMCAwIDEgMS40IDEuM2wuMSAxLjJWODRoLTEuM3YtNC45YzAtLjQgMC0uNy0uMi0xYTEgMSAwIDAgMC0uNS0uNCAyLjIgMi4yIDAgMCAwLS44LS4yYy0uMyAwLS42IDAtLjguMmExLjUgMS41IDAgMCAwLS41LjQgMS43IDEuNyAwIDAgMC0uMy41IDIuMSAyLjEgMCAwIDAtLjIuN1ptNy42LTNoMS4xYS43LjcgMCAwIDAgLjUtLjdjMC0uMi0uMS0uNC0uMy0uNS0uMS0uMi0uMy0uMy0uNy0uM2wtLjYuMmEuNS41IDAgMCAwLS4zLjVoLTFjMC0uMyAwLS42LjItLjhsLjctLjUgMS0uMiAxIC4xYy40LjIuNi4zLjguNmwuMi44YzAgLjIgMCAuNS0uMi43bC0uNi41LTEgLjFoLS44di0uNVptMCAuOHYtLjVoLjdsMS4yLjFjLjMuMS41LjMuNi41LjIuMi4yLjQuMi43IDAgLjQgMCAuNy0uMi45YTEuNyAxLjcgMCAwIDEtLjguNSAzIDMgMCAwIDEtMiAwIDEuNyAxLjcgMCAwIDEtLjgtLjVjLS4yLS4yLS4zLS41LS4zLS45aDFjMCAuMi4yLjQuMy41LjIuMi41LjIuOC4yLjMgMCAuNSAwIC43LS4yYS43LjcgMCAwIDAgLjItLjUuNy43IDAgMCAwIDAtLjUuNy43IDAgMCAwLS41LS4yIDEuOCAxLjggMCAwIDAtLjUtLjFoLS42WiIvPjxwYXRoIGZpbGw9InVybCgjYykiIGZpbGwtb3BhY2l0eT0iLjMiIGQ9Ik0xNDcuNCA3MS44aDcuMnY4LjlsLTMuNiAyLjYtMy42LTIuNnYtOVoiLz48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii45IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNTAuNyA4My45Yy4yLjIuNC4yLjYgMGwzLjMtMy4yLS43LS43LTEuNCAxLjRhLjYuNiAwIDAgMS0xLS40di0zLjloLTF2NGEuNi42IDAgMCAxLTEgLjNsLTEuNC0xLjQtLjcuNyAzLjMgMy4yWiIgY2xpcC1ydWxlPSJldmVub2RkIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuOSIgZD0iTTE2Mi42IDc5Ljh2MS4xaC0zLjF2LTEuMWgzWm01LjItNC40Vjg0aC0xLjR2LTYuOWwtMiAuN3YtMS4xbDMuMy0xLjNoLjFabTcuNSAwaC4ydjEuMmgtLjFjLS41IDAtMSAwLTEuMy4yYTIuNCAyLjQgMCAwIDAtMSAuNiAyLjcgMi43IDAgMCAwLS40IDFjLS4yLjQtLjIuOC0uMiAxLjJ2MS4zbC4xIDEgLjMuNi41LjRoMS4xbC41LS40Yy4yLS4yLjItLjMuMi0uNmEyLjUgMi41IDAgMCAwIC4xLS43di0uNmExLjcgMS43IDAgMCAwLS4zLS42IDEuMiAxLjIgMCAwIDAtLjUtLjQgMS4yIDEuMiAwIDAgMC0uNi0uMSAxLjUgMS41IDAgMCAwLS43LjJjLS4zLjEtLjQuMy0uNi41bC0uMi43LS41LS4yYzAtLjQgMC0uNy4yLTFsLjUtLjcuOC0uNS45LS4yYy40IDAgLjguMSAxIC4zLjQuMS42LjQuOC42bC41IDFhMy43IDMuNyAwIDAgMSAwIDIuMWwtLjYgMWEyLjUgMi41IDAgMCAxLS45LjYgMi45IDIuOSAwIDAgMS0xLjEuMmMtLjUgMC0uOSAwLTEuMy0uMmEyLjcgMi43IDAgMCAxLS45LS44IDMuMyAzLjMgMCAwIDEtLjUtMSA0LjIgNC4yIDAgMCAxLS4yLTEuM3YtLjZjMC0uNyAwLTEuMy4yLTEuOGE0LjUgNC41IDAgMCAxIC44LTEuNiAzLjUgMy41IDAgMCAxIDEuMy0xYy41LS4zIDEuMi0uNCAyLS40Wm0zLjMgMi4yVjc3YzAtLjMgMC0uNi4yLS45LjEtLjIuMy0uNS42LS42LjMtLjIuNi0uMyAxLS4zcy43LjEgMSAuM2wuNi42LjIgMXYuNGMwIC4zIDAgLjYtLjIuOGExLjYgMS42IDAgMCAxLS42LjdsLTEgLjJhMiAyIDAgMCAxLTEtLjIgMS42IDEuNiAwIDAgMS0uNi0uNyAxLjggMS44IDAgMCAxLS4yLS44Wm0xLS41di45bC4zLjNhLjguOCAwIDAgMCAuNS4yYy4yIDAgLjMgMCAuNS0uMmwuMi0uMy4xLS40di0uOWEuOC44IDAgMCAwLS40LS40LjguOCAwIDAgMC0uNC0uMS44LjggMCAwIDAtLjUuMS44LjggMCAwIDAtLjIuNGwtLjEuNFptMy4xIDUuM3YtLjVjMC0uMyAwLS42LjItLjlhMS43IDEuNyAwIDAgMSAuNi0uNmMuMy0uMi42LS4yIDEtLjJzLjcgMCAxIC4ybC42LjYuMiAxdi40YzAgLjMgMCAuNi0uMi44YTEuNiAxLjYgMCAwIDEtLjYuN2wtMSAuMmEyIDIgMCAwIDEtMS0uMiAxLjcgMS43IDAgMCAxLS42LS43IDEuOSAxLjkgMCAwIDEtLjItLjhabTEtLjV2LjlsLjQuNGEuOC44IDAgMCAwIC40IDBoLjVhLjcuNyAwIDAgMCAuMy0uNHYtMS4zYS44LjggMCAwIDAtLjMtLjQuOC44IDAgMCAwLS41LS4xLjguOCAwIDAgMC0uNS4xLjguOCAwIDAgMC0uMi40bC0uMS40Wm0xLjItNS4yLTQuMiA2LjctLjctLjUgNC4xLTYuNi44LjRaIi8+PGcgY2xpcC1wYXRoPSJ1cmwoI2QpIj48cGF0aCBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9Ii4xIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xODggMTI1SDEydi0xaDE3NnYxWiIgY2xpcC1ydWxlPSJldmVub2RkIi8+PHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjkiIHN0cm9rZS13aWR0aD0iMS41IiBkPSJNMTIgMTMzLjhoOS4zbDEzLjMgOS4yTDQ1IDExMi4ybDIxLjYgMjIuNiAyMy43LTIyLjYgNy4yIDEgMTQuNCAyOS44aDE0LjRsMTIuNC0yNi42aDkuM0wxNTcgMTAybDEyLjQgNC4xIDE4LjUgMTAuMyIvPjwvZz48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImMiIHgxPSIxNTAuNyIgeDI9IjE1MC43IiB5MT0iODQiIHkyPSI3MS44IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojZmZmO3N0b3Atb3BhY2l0eToxIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdHlsZT0ic3RvcC1jb2xvcjojZmZmO3N0b3Atb3BhY2l0eTowIi8+PC9saW5lYXJHcmFkaWVudD48Y2xpcFBhdGggaWQ9ImQiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0xMiA5MGgxNzZ2NjBIMTJ6Ii8+PC9jbGlwUGF0aD48cGF0dGVybiBpZD0iYSIgd2lkdGg9IjEiIGhlaWdodD0iMSIgcGF0dGVybkNvbnRlbnRVbml0cz0ib2JqZWN0Qm91bmRpbmdCb3giPjx1c2UgeGxpbms6aHJlZj0iI2UiIHRyYW5zZm9ybT0ic2NhbGUoLjAwNTAzIC4wMDYyOSkiLz48L3BhdHRlcm4+PGZpbHRlciBpZD0iYiIgd2lkdGg9IjE5NS4yIiBoZWlnaHQ9IjE1NS4yIiB4PSIyLjQiIHk9IjIuNCIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+PGZlR2F1c3NpYW5CbHVyIGluPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHN0ZERldmlhdGlvbj0iMS44Ii8+PGZlQ29tcG9zaXRlIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzExMDlfMjg0OCIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfMTEwOV8yODQ4IiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PGltYWdlIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3QkRBQUlCQVFFQkFRSUJBUUVDQWdJQ0FnUURBZ0lDQWdVRUJBTUVCZ1VHQmdZRkJnWUdCd2tJQmdjSkJ3WUdDQXNJQ1FvS0Nnb0tCZ2dMREFzS0RBa0tDZ3IvMndCREFRSUNBZ0lDQWdVREF3VUtCd1lIQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnci93Z0FSQ0FDZkFNY0RBUkVBQWhFQkF4RUIvOFFBSFFBQUFnSURBUUVCQUFBQUFBQUFBQUFBQkFVREJnSUhDQUVBQ2YvRUFCc0JBQUlEQVFFQkFBQUFBQUFBQUFBQUFBSURBUVFGQUFZSC85b0FEQU1CQUFJUUF4QUFBQUNEMzN6blBneGlSWUlMbVE5eEVCSE0rUjMwZEZNZ2l4YkJyUllKMHdDWVlrSEJ4aE9Jemh4ZXh4VWN5RlpIUnRiZHhXa3JtZ1cwcFlRRVBkNU1ZOUFzRUR4empHSENERFZRdVNSWVZBd0dDZ2c4STZBV1Fya1VHRENYZ25KSGJ4OUY1eWJvbGtEcFcxaE8wcWpMTWtsaExNWFozemorZzJCVHQ4RjdtRFJMS0VyeHdyT1hBMnB0a0pxNFJtRVRGRXh3WU1MSWdMRlpkZTcvQUpFWm95OEowQzZBKy9QRWZRalVsUkxPZStUYUlFcVN5bG9POVJwTElXVDFWYU5Pc3BybCtvdk1GOE1GQXdsc0hXWTROaUZzSUgraVYvek0vQ2VKSEFmUnZuUFpiWXlkNnFXS1ZNZFIxalpvYWIxY210d2RiZ3FsSkxZT3cyS2Q4c1pvQmlKd2p6Q1NlcThzV3JhRXBvSXM5VS85QUpSUHdNMDJkOTRQcjNxTFM2VmEydDVXcnJtZW5zVmFrOUVocklHV1pKR21WWkxYbUM3aEtFc0lFZmhCN2xVRWptVllHcFc3cmhzSmJGVzY1K3YwdjU3MlVIQlJiR2Z6THJZbnJxdHo0WUNoSE1hb2xqaVJ2ODFJSlV2SVZNd0p4QWNhSTFWaGRnK1VpcWRtREU0emZDdklFUEVRL3E2dmFoSmZVR1p1NmMwTVhuVFd4VzhUcnh5cTRVUjhTSlZpcDE3RmxaVkc0cFV2cXk3Q2hiNjZ0dXRWM3JRMm51ZTNsYk1XYWVWODBKMW5ZRE1CSW5NMncycDNKVWFMSWZtODE2ZVlORllzRWZrM0NNdThwY05PdXBGSWhMQVhCMVdBNSs0dmVndGNWbzNNS0ZqWVNBUjdkTXRSVllpZ1lMK1EvVjNNMGRFWGNmbUxuYVg1cDFjN2xqTnB0aFpLbkZjQVRwcGxnbTYrcno1amNKemdqbWZPN0tJbjg5cFVqTTAvMGg5WjQ3V1hsTnphM3R2UCt6RmVpYXlVUXhLeURybVRaQ3liR3R0S3ZQSFYrdzYxMVVwWEdvZkkwdHNWY2FwWW4weXNUVUVzcnNKU2NtYW5nYm5lV1EvUjJuVTdQM3NqZnVoVEpBb3BoTU1jb1lXaHlQUXZhNjBoQWN1d3BHdlBZK1FpclduR2NDWTMyNnVwQTB3U0xDWmtrYnhjcE9XRFdmTGJuUTlLVkFqdXpTb2RTZXB3Qis2dmNHdFE3VEZaMUlGdXZJYUNra1V1WXFsUkJyR055NlBaNGNpUGhmeGNjeU1CNFlpTHFYVHNXLzFkVm9VRHkzb3QxZWx3ZDlmUXZKc21MK2psVWN2NktjSmV4QmtjcUU3VjBmVENXWVZFQTh4RlBYQ1NjakhuVFNFVzRzKzN3djViM05hODlvMmUzaGVVYkMzVHJmb3g3N3lXME5YT3k3aUk0T1JiUVJVU0gwWWQyY2RqTVNSS2NoR2djTzc2Wmc3dVo4SDFzWlVocWs4NDR2cGRZNUZwdlZIRFhwcnJDZXQ5M0c3RzlGaXV6VVpFenp3ZGRyYWhjRWFyQzdWK01JbzVkTUNaTjlWczVrSGRCRXJPblVlVnRhRDg3NkQ3SHU2TFp0VnhVRDNmUGlTY3dUZjdkVHZyWXozRWkyc1Y5dmEyTXV6NzFFK1ZlKzBwbDdPZnNNRi82THo5dDJNWjNacTZaK1grNDJiN0x6WUhwTVdqWituRkk4L2VTOTFvbk0wMUxBcjJsUmFVN2FEVXd2T0pYWjYzVkQzUlZkWmFldGY3M20zVVpkWGZGYytjZlJHdTk1bXQ3WEtxbHdYTlFqWXIwYkY1OWg1NTdiWnF2TTNkZkQwTlBXRWlrTFlWT3M4ZWlzNitiTXRyMnFVVVRPVExvYTdjaXRUdGZQVTBOVGR2amRjSFhyVWEzTzF2RnRvUG82dFcwa3JJZllVVWFvZCt0NkFLcnFyQm1YU0VpcXRqSkM4NFdrMFVOYWoyTlptUENVemdMOVhFbFdqRXZMdHlodlB5ZWpxN1VCZjNUNTEwVXE2YjBWQkVCa29PRmhqczdJSk1SSUZwZUJTYlhMMGUvOFFBTGhBQUFnSUJBd01EQkFFRkFBTUFBQUFBQWdNQkJBVUFFUk1HRWhRaElqRUhGU05CTWhZa1FsRlNFQmMxLzlvQUNBRUJBQUVJQW9yZjY0WTE0MytwUVVhbE1UOCtLRTZpaHZxYU8ydkZtTmNHK3BxYUtub3FrNkpFNmxPdUhSSTF3YTR0Y2V1eWRSdm9aMEVoSHp1dWRMYTRkUS9mNUU5OUJ2UG9YanhQOGZIMjF3eHJoalVvalhadG9VbDI5K3VMVXEwYXgwYXgwU3gxSzQxeGE0bzF4RHFWUnFWYTRwMXhscUlMVURPdVBVTDBBVEdseE9zTGk4amxqbGRLMWdiVkl1MjVPR05oQ3VENlB1eFlpdExlaDhNNmtxb3U3ajhmNEhoMStvOEdOQjhCVWJqbmhIc2lwYU10dE9vOXVuVUJnTzhUVkkvSEhPdXpYSHFZbi94TzJ0dFJ2cWNUMi9IZ0ZHb29Gb2NkdnF0aGJOa3V5dDA3aGd4T0pCYWJkSG1YTW5LSzlRb01WWEJhM3ZZeHFpS1pDM1luYVpESzNGdUtSTTJWZXowSVU3YnhjRFlPNkNQMDIwYXlPZmFTSFI2ektqMUtpMUs1MXh6cmoxeEZyaUxYaFQva05BTkRqMWFYanY4QW5wbkZoUlhMR0d5SWphR1hOdmJGampadVIzN29yM2hkbkkzU2llTkdWdUpYSVRac0c0dmMxTmhmdjFEYlhKdEtpSjA4ZWw0bEpCdEo0R3RFNisxVlI5Tkhpa3pwdU5RRWVwMWEwYW1uWDBkSVAxNFl6NlI5dS82amgwUEhxdWdIdGhla1YxZWdhalpYcEQzeHRxM2ZVdG0wUHlmazIvRVUvSG5NYmErMXNFOXlzWW5sallGNFJhMVJvS3krejFiVFEzK2YydXFQOEdZK1kvaU5ZeG5YWkkvTW1QN0xqTFg5c0dqWXI5TWtaMFVqb3dpZFc3cXA5aVFzV1A4QUhITmN0OEc2czBDVkJhTjBNbnRITWVVcE04TnExa3gyRnVONTB0S3k3eXBuWExQN3RaQWE0VHM3cXZJZ1d5NlBWUGxIeFdmSS93QnkyTkdVNkx1blJBK0k3cGNtd29SWVpHZXJPVnFWRzhGbUQ3eDdva3R2bUFZd0pOWmF5blV0UEdpUGF2cnRQWitOZlc5eVhkcmNiMXhST3IvY0IxbFErNGpOYWpkcVpKZktucW5HN0xteXF0YlJaOUVzQzBvT2R6OCtlODlqY3hKYlRNWmhLaGtqTHFxejhML3E4am4zajFiUVd1SjEvV1BJV3JQVmVPQ1poQjlXV0lLSWx2V09RTGNVdHorYXNiaU1nOXhTNXVPdjI4VFBlZ09xc2JZanRiajNYYjllZnR4cnNSTWhZR1E5WWxVa2Nic2Mzam5lb2JyOEZ5T3d6Nk5MaXZaZkQvVU9pYm9yVXZxRG03Mk54QVdhbFhENVBNR2Iwa3l4VU9WV2Z1WXlPMmlzY3g3UUxGMS94dWh5U25WaEIvYlU3MjlpbmtDSUJsWG1FWDJxN0pFa1pKVEE3VE95bnZrd0cwQTd0QXJDalp6TEFsdlRPMHZJTngwbHpsczh0RWRTWnJqalNGa1VjZGV5bGtlMUpGYVY4N21meWhSaE8rdnAxZ3VuZW5LUVpxMTlSN3VOdGtxWXRaMVZkWkRVdE9jOXNrNmpWc1dtYkwycFZGU0tPYjhzN3ZEdHNoUVhkc2x4eTNUbGlkWDhjd1NvOGhjaEYyaEZrZTBXZTRSWitqaU4vamJVVEkvRXlabDNTTVNhK0lYT091L3RDejBWNHFKYTZLRkEyUlJDbjlPckxoNVZGOU5zditqK25YVUFIM2FINmQ5Uk5NdVEvcHQxSHdpb0cvVEhxb1o5RzREeHJzcmZidHBDdjJKYllKMHoyMUlsVHB0UHBteDl1Wm05MnhSTU5lVEtCV2EybE1EdHBUSnJudUdTcUJFUmVwakE3YmxCa292eCtYVmJINXUzbUxjQnJkdjhaeGh1RHVTR0lkQVMremtjbVpYRzRwQ3dyWDNCR1E2TnlER1YvdHk0a2RSMmFLd2tmbCtScktHU0xyRHJCVUtsRlpMZTl2a1dMbHlMTEQyRWlBT0pkN2pyWTlTWnhVRk42SkJoOHZLR3BqdXFDV21RSlIyVE1iL0dOZXFSbXZZWU1WMnh4ZGt6dnFQYnBmZUg1VlY4Mlllall5NU05bzNHV3JMSkNGUUZrdnRRVXNNbHcrZmZ3VGpiWVVrU3lCRDgvZGduNHRaaFF4dnE5bHBzeE1hWmxwU0JjZm1wc3oyNjhjUUtXUWRZQjl5cHE4OGJuWEZLbWNOWVpFVEtRRVM4WTlOMzVmVmlTTVljczY3ZXlINklUZlg5RHF1Y1VScGVGeWhqSlJHSXlYcnZHSnlKZXV2QnNJWndQWFdDelRhelFPR3RZTDdaUnl6Ym1RM1hoTXpVb1BlbTBsb1dVRFlRQXZrZGgrM2g2OS8yZWx0dHB1Q3FzSGoxSFJOSHY3eEhvcWxET1dZNkt4czc5LzhBNjN3c003d1YwUmhGZndqb3pDSkx2WFo2UXdoUnVSZE1ZcjlCaEtvYnFRcnAraDhhcVlXcFdqKzM4ZEkvSlZnbUpaT0t5T016SnRYanM1YnFkUDFDc1diT1J1M2NtcGx0dHN5aDE0OFhWUTBURVVXN3pYUkk0eDNLNjNhUHBySmM5RWFsb1dOL3hpV0Y2VHducll4blMrNzluQi9ydFA4QVpBMzljVEoxd2Y4QVVyWE1iYVlvQjlRRmdqcVpIZmVPN2ZYZE1hNnE2bXlPS3pBTHI5UFpXb2ovQU9oOVJQRnU0dGRuU0tsU2FFWDh3NjlOSXY3ZWpibWt2dkJkMnFwMHYxVnNWUnE4VE9rY2czR1VmSmRqc2dkdXZEbWMyZ2NVNjlaOWRUWldveEEzTlNsWEsyYkNnTGpLYkt1VGgxejZsK2lkdnBsaGEwa1RDTGJVTWo5bXlCOVltM3JxZkZSbXFza0FxS2t0b1VZejUvYnZITzlucnNwbERlNlNyUkxDWjJJT0cyRU9xTENiTlphcmpZVWpGMnFDNGxUS2VVdFU2UVdkZjFLVmZHc3RQeGZVaHRLSzk2RzZ0RlFsTStmbHNzbzhTQ3RaN3FiSk5URGEyTnkyZFNQWUJkUjVOTnlJSU9xbDNNb0dOcVhjcWlwWE41Vzg5OTl1c3dxaXVta3lWWU84TWVrMitwcWxhNE5CdG5OQlhuOHVZNjF1Y3BMeDc4bllZeUhzYlprdmZIcXo0SURtdEVpVWpKNzZPeDNSUGIzbk8rK01heExoc3hoZXFiQ3VWaDVTeGJhc0Nzblk3QlZXcjJ1cHNyUkR4VVhlczc3REpXUXAzQ1RpVzNneWVTbGRhdEw2bWR0NVBJZXpMNUcvNWhpcWcvSjN0NjlheXZ4VWJ4ZzhrWTNZN3l6OU5ObHF3eGZVdGRsRW9kbnMxUXZ5dXpYYm5iamc0eksrRFhTMDFQN0JsaHVhOGZmS0d5cytYVXl1UGN1ZDl0OU1XMU8vSlNyaDI4eElWRG9Zd3dhYzJabFJac09QeEFEWVc3bTdOUE85SjFZczN3UHpiV09pMldIc3dlVVVqS1p0N3hlbEtMSXVIRHFSZHhkd2JXTXZYNlN4VmpzbFpyMjQ4aWF1Vjk2MUZidXBVSElJVFpHWmN4a3hCd2tsN3d6WVJIdUNDMVlkeWU0b3NRSXpFRC9IWnBCQVJ2cWRvbllaWnlJaFdvQmFrODJxOWdaQXE0d2FRL0pEZTBSNzlSYWFld05vdkFMa3JHMU5Oc3oyL2VVNC9DemlxdVJ1UjJ5K3BVcytWM1F5NVlDd3FmRHJneDBtcUY1RVZNangzenNYdFc3ZUMzaXljQkVUTGpMM0VIL2VsRXlQeEFHNXoyNjRwMzIwc3Aza0padEc4YS8veEFBL0VBQUNBUUlFQXdVRkJRUUtBd0FBQUFBQkFnQURFUkloTVVFRUlsRVFFekpoY1FValFvR1JJRktoc2NFa1luTFJGREF6UTBSVHN1SHc4WUtTb3YvYUFBZ0JBUUFKUHdLOEhhTzB4b2UwZG9nL3FCREZoZ2dpd1FRZGdOdXRvWmFMQi9VSHNFSGFPeWgzbUVjM2xPRXdtOG8yTCtIemxNNTI1aHBFdzRXQmQvaWFVa0M0Y0lYREh2MXlqM2xMc1BZUHR0Rk1ReE95aXpub0JFc3hXNytzR0wxbEpicnBCb0pVajlsby93QmhZSU8wUVFROWhNYUx6dnY5Z3lzWWQ5NFlwaUgwaVJrK2txL1NMZVVaU2FVMitrcE1ad3BsSXpLQ0xQbkVGdXc5am03ZElZeDlJdHJ3Y3dONGtVZlNaU3JLa1BZWW9QeWlqNmZZb0FlWWg3VEFUQnoyMHRLWnUyWFMwL09MQmR1azRQOEFXY0kxSS9ld2tqL2FXK3dodDFpK0xUczRxbWpuWm1odURvUkJFSkExSUVVemkxNGd2b0tOamFjTldKOHdzUmMvQ3FiUmhUSVc3WjNqNHN3SHoyaHhCZGN0NDNoM1dlM0taY2VLbjNndURQYWVGUjhSYVY2alcwSnl2SzV1ZFFkcFhieXdpVlAvQUxpc1JmV2NUVnhmY3Qrc3A4bmt4dk8vYy92SERLZXVuTVpTc1I1eml5cEF2ZElTemFzVHJLbkp1bXpDQnFKMnhDNFB6bnRCU28rR25VRXJPUG5EYy9kRXdoUjRVRUdKOXplY1FnNkpLYk9GcTg5Q25hekxhVXJvN2hVRktuY2t6aSs0ZW9XdGlBMjJ6bEcvTnpPK1F2S0pWbDJ4V2pXL2lNYkVmS0xpZHRFVXhiMkYzYnI1ZldaUHhCeEdVN1kxQlczV05aZzltL1F3K29NLzZNUHBHczZ2ZFJiVVJzUDZSVkZURm1telFaWDBPMHI0YmJockVUMm90UmJaZDZCK2NUNXpuUHhiUldYNVM1bDdlazQvaFJ4MWVseklPSlcxTUhhM1dlMWVGNGpCV2JDbmVqM1lNcUppSXc4czFINFFnQzNPN2FBUytmeC9FMzhoQ0RjYTlmU1ZMbkVEVllidC9zSmwzZFpWcHJQOEsrRW5xRG5CZW0rVEQ5SWZmVWNtODFtUjM4dnNtR3pYMThvV3lGalBhSUNLYkd5SEw2Q2NkU2RhaktGclVpQ0JmVEZPSkxwczA0eE5aVW9zQm9NZXNvMDhMYTgrODRJWWxiSjhZMDZUZzZWcmY1d2xWTXNpdEY3MytjOTFRMDhPc3VGdHpFbldKaVZFdUIrVVlsbUJOL09BQU00K2NQaVN6ekpEK2N2bnJNMFljNEh3bUhMZnlqeWtRMjVYUXl4SFVSTVFsT3plWWloRlRJWW0xbkdQUVRTdlp3NjBUOExGem9KN1JSMnZoRFZhWlJuOUxaYmJ5d1Roem9VVWN1M2hKQmdnRVlTb0xDVkhjZmRwNmZNeGJEQ1JaY3JBendqbG9vTmhQRmNENXdzWHhIRWIrS2REZVcvc1NSNjNud3RORFp2U2E2UjJVV3p0dkwzOHhGdDVkajJJNlJQbW9sWnYvV0xqeTVXMmxHblFkNzh2RXMxVFBYNHRQWFNjYTRSQ0F0QTQ3MTE2MzMzdFBaLzdLdFg5bXE4Q1dYdWp2aUc5ei8xRlBZbDVUSkFHZ000Qy93RDQ1VGhMSFZwVFcwUDhadEt1RmRoQjhOMmM3ei9Md2k4M2NiVFlXem1naW5QeWdPSmRjcFRlNVhURE9DcUczN2s0UmhsOTJjSS8wZ3d0c3BuR3BTTk8yTVBxYzdUZ0tIQXJ3ZC82UlZVS3dkdGhpdkY0aW1iQ25VNGNWcjk0dzF0ZjVkTFNydzFCMzk0YU5ERGhUeXZiV1ZrcW80dUdScmd5bUJLSXoxbkRnZWtUbDZRZks4b2hpTkw2U2tMSFlSbUhVU2lQcEtHYzlucnJja2J6Z2xQeW5BcVBSWndtZm5PRlZldVVvcmw1UVpBWnhSVUZLM09WeU03dFdZZTZGdFRPSjdsbHUvREh4WVRxSldYR2JMVVpSbWM1eGRYbW8rOFZWOGIzL3dCWFNWY0pwY1FhcEZSZ1ByMWxUZmtvcmE1cVgvQWVZbFNrZUlSTHRUcFV5dUZkb2hnUFlwZzdUR2puc01IWWV6aVZVSXQwb2syRlMvNTV5cisyRGlNQmNaaGlSK1Z0ZWhpVkdxVXFvSElmRGZyS05TbUJkYVJ0aHhlZW1mcktOSXJhemQ0bXNkQ0ZxM0YxeU51b3ZFU3c1UnluQzVzVGMvZ0o3UFR4WjFLUnN5bmZyRXFod0FLZFN0eElaYWlZdEFOclNqM2Q5c2VMOGZzTll1YkxmY3lvRkZzeXhoekxZVjh6R0dQRGl3K1gyR3Q1OWhqZGpudmFZOTJHZXkrYzR3WG8xUU83eGU4RCtwMUhuS25lVnNCQXJucDBhVldxS2JFQjN2aG1RYzNRYlNnRGNEbkpzUi9PQlZVNXFxUHRIQzgyZmZXeW5DQTFscUR1K1YrYTNYRE9ML3BiY1FRYlYvZHNuWExwL3dBemkwbnFVcXdTb0ZheWk4NGRhZFRLNUZVV3ovNStQWVZGUGNzYldsUmExNnk0TGZGbmRDZkl6aWlRYnBUcktkZXYxTUdMaXEzTldxMXhZL3VnRHBrWTZ0U29vTWVIKzhPV1YrdDV3N01MRTFLMnk1WGplRWJDOG9uQW5kc3hWczJCaFFkTSt4eUhjWFhseWgvdXk1OUJQQmgzR3hpak0zTExIQ2ZkVnRaayttWTFoUExsZzZRWWViT00ydCtacHVjek9Kc2NWbXowRTlvbGFydnlPeTN4ZVg1VDJoanFzRnRiOC9QWFdjU3kxVXBKYW9yakNtVy9RejIxUkp3V0hkakdjcFFaa1oweUNlR3ltLzFNcTNSYUdCWERlQWsyc0IrTXFVcmNIU1ZhUUF3K1kzemxidTdnWTZqdC9wbElVYWIyeFc2ZFp4dmRJNUpxc21Wdk14cXRtSjk3VTZpY2JnSlhEM2c5TlpRcmNTakx6OSttYmRjOXBYUHVza05RY3pEK2N4clVVRVl2eWpsaVRyZThWMUdHM0xCNDA1ZldmRkU1dHI2UU5mY3R2QmVMWWthR2MxOHNFc01KQkk2NXhCaFhVb05CQi9GVXRhWkxnQkdFOHIzR1JpTGw0ZGg2NVQyaG8rU2g1VVNxalZxZFN3NjU3eTYwalgxKzRMZnpuRmhsSXhkM2k2ZFpRRlM2MlN0NGlyV3k4NVJvMHExUS93QnJiRVQ5WTlWcWdIdlFjaGk4clJCbFNkQmRkOW9BVFZUblczaHkxQmc3ekhsNStzT1dXTE9DOHo2bVV4a0xXblRLYzNTSXhHbUl3L2hCNGN3RE45WXZPeDVUMGw4TnJHMEhsWWlWRGhSYkFkQkVIZHRUOTRHMEhuS1Rjb0p4WXM3ZnJLZUozVU5VYy9DZFI2eDJ4T0IzcE8rVVM0djRScFBkcjhTaks1bFUzVG1OdW1rbzJ6eThwcHFJVGZCR1BLTExBUE16THpqWk9MSHNJbTFyU3F3QjJuLy94QUFuRUFFQUFnSUNBZ0lCQlFFQkFRQUFBQUFCQUJFaE1VRlJZWEdCa2FFUXNjSFI4T0h4SVAvYUFBZ0JBUUFCUHlFUXVUUlM0eDJ4Y3hFZWd5cll3VmllVEY4TE1IZFFPVDhRaFpBK0dJYkpucVZsOEpqK21tRWJsRy8vQUk4cW5pNElqd0JPNVhzSXZhODZaV0tYRG1JZW9pbUt1N0srNDI0eXJsU3BrZlVIcWVtemp6T1BQVTlVNEpBanhSUit1QWVNZjlLRTBwRDBxNUtCOXB4U0RDSkcrQ3hqcjlLd2xZRVQvTGJCTjZ4Q2lPUXY0b0Z6VXlNZUg1TE5VeTh6Nno1M3p4VWVXRTlSd3dRVVJnN1QybEVQdXBlbHEvdGpiSDRqa0JMYlVyUDg2NVdadUtCdDNTVysxbFVydU1CVU1JZk5RVnhyYmNIRVU1Z0tJZTQyaHZvWUNCZHlsMWkxL1pEckJPWkhZVHJNWUwxRFZOMENiUHVMNy9SUTNubHZ6WlRSMUY0VVRQQ1ppSUdNVk5vUHpNV3plTmNWMVJCS3JuRHVZRkE1RzRUZkxtWnNpWlpTdG54aEx0bjhvRmhUOW1hT1RiQkovTDhzNUdKdTFGTUJ4b2hmNFI0UlV4aWRJT0lYcm4yQ1ptY3M4QUoyUDVtb0s5SnFXeWdHbllNQlFoSFZUZjZPSitKQzdDVzVnMU45UWJwSHVFdXkvZWk3aitvVTFIV1RibHdwWlhLelMvVVhqV3NURFM2L1Q2ejVLNGlXVFpiSmVsUFNoOW9tNytFWDJmY1lGRFY2L01VVVR2Tm85cm1raS9FUGRhSzUvRTNCdUxVbjNLMlMxU1YwdzNtWC93QW5SZ3dPV1ZtdjRna01sb3NZbTJUbkU3TlIzdU5mdldtaDJ1ajFPYjFWQUw5eHprV3d2czh3dlhnTlJyaUdvc3FNTGVmek1BWXJvaE0xaStobDNoMHhkYWxtTUhhTmY5Z2F2eEhBbnRWLy9VWUFhc2NuekxTSThaTVUzRkRwR00vSmlqN1RCK3IvQUpzWFBGMERQMnk2cWNpVDNQSnRRdkFxTldkK3BkNHR2YThzTXRiQzdmcWZ5VHZpejQ4SVU3N3RQb3pGb2g3UzZ0eGpsL3FaQXJsYytXWU9QMVNHVTRVRC9VYkh0cGRZVitkUlY3dWkyc2JjVVM0TzNhY2oyZzBNdDhSdC9QeEhaUm15TjhqeXNyVXJ3VmlSVkIyUGwwQktTSU02akF3T3JvQkRXbERONG8vdU1kZWVHcVovSTNMdHFqSFYvd0RoRU1LZWJPQ1c2ZDhDUTZyV1ZWemRmSEVwbDRKRTJmY3kyMXQvenI5bzlrZG41RHA4a3ZOTHQyNWU1L0hIWm1BdXJPYVR3cVk3M2wvektOd0RBYUQ0OFJDdlVRSXdidW9UYlRTaS93QnVZNkNHUVpZYllydVAwMEVpQXpoemttRWtRVy82anZObFVzUGNOaC9NanB3VTEveVBFdmw3eS9Ydy9lTDVwQURLZWtyMnN3WUYyeWdjZmpQdU9xSWRtUWgrSXg5UVMzZjlKeHd4NTRuNG1iV1duYnYxRlAzSW92OEFDZjc1alZ1cWRuRVlYMTJ6blRweDJzbUYxbE0zWEo0aXFURW9WTjBsZk1vNHNGQTVHcTAvOGxsSWFTalhVZUZldFUyVG5laWJQT0lKN0FnN2RZaVZwamFGdVVZNnkzaVBsZ3MvTWh6WTRHUGNINEM2L1l3SHJTLzRCR1FwamNXSDRYV0paS0pOdXpGYU1aUmw1V2NKc1Y3UjNVeG9wejY0V1dnQUt5ekdYMnN2dCtHZC90VTNEVFJlRTVoY2NTR1QxeEFINlk2bHk3RGdMbWkwMWdmdU5JYlQyZUpvZ0t3aFZXUzhyNmhURm1KVFhITGJoZC9HdWNCRGJoU0tyWExueVRMRE9RUGlZUWtBdDVHTGRHSEcvd0NMeEVvSGRHTWZOWDkzQ3JGWlJ2cFVIbHRpc0tacmc4SEg4eTk3SHdLaDJGRDRXalh3ZlV6VGxqVVUyR1FmQmo0Z28yTy9NdE9BZFRxcDdFaGdmRXRVSjdtb0RhbS9Hb2x2ZlRRamVyUDhhSEc0WnJhcTBvVXZOOXNTTExRdmVLOGIwQjdtS2NNR1Y0NzMwVXNOMk1PSGRWWUEyM1h5bG1pTzRaS3YxTVE2ekl5RFNnNTZWQWZpV1pTdDk4eEdGZHJGNWoxQi9HNm1uWmZOUmNXMWJYU2pSMFRhYVlERmN5aUdLRUVDcWhmRmlGZ29WZE9hNGpGRXI1ZVlBVk5vdTRkU0J0dFpkVGtMeTg1YlRaQzJ1TURMNmdySXVsSTJFU0c5Z0JuYkVaL1NFYUhGcXBwWEZzR3FXRmk1REZGTUtFTGYxYkdJdEFYbHU2ZUo5S0pMbThrclN1Z2dJa3Nsc09oSzdlU1dTejJ3RUFxNEh4S3NYYWRFeFBsdHMzQjZGaWxyZFNqVTd5dmY3Zzh4ZUJEVjJqZzcxSFo0K2x5aDJ2WG1YUnVhTmNUTzlpbnFWTm1PYXQ2dk9LbHZlTnZENndlWXlOZGlKTmxjZjZvZlRZYTNXMW95ZmlaKyt5SWd6eWVJT3NrUlFHU1hWYVlUVkorTnd4VEtMRkJBQyt5dEZNWVBzRDF4Zzc5bUowQkhvR0N3ZnY4QVZEYUhHRitaRlRCWnBETzFlSm1ESFUwYjhFeW4yajNWbUlnOFErU0IvZ3lvMHoyT21yTVMvcjMzOFdZUDNoeXlvU05QK2d5WWpPTnpuMFhJUEQxQmh0S201Mzh5cTlGZmNQOEFMdUprVnBvVGhlVnRBOEdvRnJxZ3JUaG5MV09TYWRKb2pZdWVIcVdzQ1NwRFRzRVdhWURNV1UyeTUzQmRmVUlVYXdnYi9xRTZTdzZpMnZVK1I0WnlxL1Z4NlNqaWRtRTBKV2ZFemtaWHI5SWsycDdtMjRYdzJtTG1OOFN2VFJIQ2R2Q1kxcm5KeUZEa1A5cVdaTUdHUEhUWFBVcE5lQW9RWVU2bHBlV3BnY2NyandSZ3VHU2I0YTV2dVlrTXlnc1pmUDhBRzVTMTgzVVY3czIxakJWNW1Sdk04dThodzRQekh4ck5Mcm1qem1kS1pjUmQ1cHZSUmVSRFRjUEx3ZllnOE1DaWpXM2ZBUHM4d3F6MXMxM3g0REhGWnRqWVpxZVBhNkpoTHptUGhxMjJHU21CYTd4V2NSNnhaY1dJcmZCZkh5UkNYQXJsbXJxZklha0tyTlZUWHpHb2duRXBYTngyejh6YTYzTU04L0UwVkNrNC9laWtiZExDcUt6NXVaZzdVeUdxWUUwRUx5Zlkvd0F3V3plSitCRkdjT0R5MTVsaFZYVmVTUG5seldYMnZtWFV4YkVPWm1STVZtRGs1R0Zmbm9Bemd4RHdEanBRbkxqZzZVa29qalMrWU9SM2RaTHlSU1ZaZm5WaHhkNXpBdXVhdW81Y29Qb2xGVDJ1MzdHSUFOMmJHRVRLang0NG1DVE5vTWdId0N1aSsyTk53TC9lL3VyL0FJeENyclVxSEsvUDdTNHBPYlBUUStlZC9FWUx0VitEU25ndjZpam91MUxwYmJpL1V1NldvRGczNGE4MUdaYmkvd0F4MmVZdGZYTllkZW9zSTVDQzQ4OFNsVnNLOGRvbzUwWGpJSCtJazAyMW9HVU5xN1BucU1wMExXWlNqS0F1RXNTWmZ2NkdlV0pTN0R4NEhxSjM1Ti9QbmppVTlaS3djRE4wUW5KZnZ2TW05dU9NeElLbnNhdHltYkozWGtiMU1WTFo1VGlxdWFZYzl5VFhoMCsydVdNNTRMQTRGL3YvQURMSGVpY25yLzRtcE1Xd0JoY3M3b0Q5NE95d2s3bUN1M3VWME81S3JEL251WWd0blZSNlROL1htRlFvYlhzbVBMeERFVktEbEV4RkxvbVo5NzRDM1ZUV29ORnIvSE04SVFWcU1XMUthUGN6ek9DVFlmbk1idU9SWUR4MTZoVUY0QnVzOUVPeHNOLzhXazBLUUJKWVdXK1l5Qzk2SzM1aGJlMG40UHpFbTg4TkhXc2llSnlwUmNqbnRPRUdxQk52bGRRbFpwZ2lCZEh0bGRNTFloSzNYeTdna09sMDdQTDVmRUlRT244QjQ2alpZelRXQ1ZYNWhWdktxQ3Z4Q0JsR2UvRXFINnc0aDF4OEV5VytOV3N4RnErNGptVzRkZHdlK3hNUDhRdTRBeTFHNDBPby84UUFJeEFCQVFBQ0FnTUJBUUVCQVFFQkFBQUFBUkVBSVRGQlVXRnhnWkdoc2NIaDhQL2FBQWdCQVFBQlB4RFJJZURlQnl3SXRmM0RWVVl3NHpWUDJ3bGl4QXFJNEFSSEc3TWpoN3dsSCs0YkxUTFdyS0ErSkxaYzI1R0tXNDVNc3NYRnRIS1FzQjZPODJ3RVRpWTNvZldhZ2M4T0J5VDVIQldBdk5tNjVuRnpZNWpZbWV1TVgvV3g4engwaDZoTjV3UnFzcitKaEZtWVhQVVRMemY1TVZJWnMvOEFMRU95NFc0L3JndExMRjNnNUs1NHN4L0JoakRwdjFNWnlYSFRyaDloaVNVZk1iM0dEOUFGVGNxdVhFd3V4a3k2VEI1MmVPYWxQV1cwTFhvUEwrYnl1THg4SEhrMy9tR0pqb3ljYW1XY25jQUhvaHhpbVlLT045ZXNRQXhpSU1aaEFTWC9BSEVhM1VreFhnamtZYjFZWjNyOHlkWk1waEtPVU9WZFpWdU9JZnhTWlNESkxUK3VMOUQ0c3F1Mk5CaGRIakhiQ2ZEb09neHA5MVd6YjdaN1I3bDQvTWFQb1kwbm5FKzZBNEdaTmcza1ArR3ZPWHFOYjVEa2hzK1JsMnBCY1hjOE9DczRNS0FYc3paSHdxM0lOdVVyckdyVzRGR1hpeGxmUTlMT2VmN3hSc2YzRlNrZkpjQk1VdHdlTUNlQTBCd1lNUzlOemhPbVFxZTgvY0VWRmp6WjZNZWJlenB2ak5mbHpXeEJSMlFod0lrQ3BJeHBVUWJRTjk1cG5TcFg4dUFQeVFCaXFuMUYyY1JUNHdRZXlzWmh6ZDViVTNnSmhOQjFiV0tSMVFBY0hCUGpseG1pVnpoVWZtRkpTaFhEdHZDckFRSG95eENQTEZRaXVYTGxOOHJ4aXdhbzZCdGZCaTRGVnljS2FBS2tFcnprS29jT1Z4eVNwSmZuekRsd2pvZi9BSEFrYTZnSis0QzB2N1lrMVBXeHhFRWZiVElDT2Q0Z0VQcmdUb09JdzA5azRwUGhVVEZLaDhacEIvbWVIWEdzM0QxMENlc1NUTmdBRzRjUFhTQmZPUFpJQ1BZWWNJMHY1bENScy81TUIwN1hZYTcxa280MUJYZHE0d2dyMDJNcnZlbThKL0J2aGZZRU1QdUxzTC9ISC9NMTFJZU5Rb3Y2MTd6b3JlQk0wSjl6RDcrNHpCbkFKODRDQitPV3hjTkVIYUN2cFpjMmloTWtMRFhtYlI0ZlpjSkpjQ0R5SnBNRVhZUTJOLzNDeHpVQWZhWW9WcDUyR0hhRGx6MExkam81ZXNCYjRHaDlCdTR5UU9SUllQSFRyUVpNQ3ducXFpRHF6cW1Id0hJNzhYYUNIREZUU0NkbTJEK0k0enBTeW42bmZuR25qOHpBNktQckhOY3F0UGdCVmVnWENQbHBqNE5vZmNVN0pvcjdSNVlVNjhSUFFWYXdEZjMvQVBJdzZpM3FQb3JpMVd4bkwvOEFIRndCV0dncFI1c0UvU1lVY2p0WjZkR0ptMldjL01wV3lLNUZkRjNRWm1nT3VXbkY0Tlo3YjdvMWZGNnhLUGIvQUdtdEJOZHAyWlIrQW5VZEIvNkdLWFZHUUphdFAyWXN4cWhKM3pKaVNnVFpQdmV0ZnJLOHIwM3d2ZXpCRGwrUmV2OEE1aklFTzVIWEFUOVhHZGJ6QnlWYXJvUERaY2Y1dGtlSU8wRkM3OUdCbVZSaHNCYU5POVl1c1hiRUtMMmV4QzVFaHcyRzBvRkU5bUlUQWc2N2ZlRTREQmc2c1B6SFM3NVYyVnNBUlhlL2k0VkJvWFFKSUdJNE50YkNLTXpWSjA5YzZlekFtV1VBSVI1b1BaTVpoQ3UxRkgxYWp2WFp1S1ArY2c3RjN4L21OV1c3NDUvVlE5SnpseVZDTkcwOWEydlVjVlhBbFM3NnhwRHBYTlUycGQxNjl0SjJxY1pJTDdtNVhXMWpzMzNnQTJWelh2OEFIL2NVZ0RiMytDQTUwK09jMzhkWk1yb0JaUGU4REpYbWYxWFJtclhKWUh0ei9Ea014NzBQNllpOU1OY25rdzBOc1JSK3BiZ3JYSEl4bHJBczhwbEc0cnRKVkxmR25qb2lkRjVnRGM0M2lvYkc1ZG5TOHUrKzhkaTAzWXE4R3M0RGVXL0pzSkZXVGhodzJuSzRrNmlCMTJuTm92bDdBY1ltV24waGd2WUI4M3N4N1NRVnUzeEEyOGtHUXJDbmdKaDdZN01LczFDcDZ0ZkNHRUNpYzRYWDVHdllPR2lrRUtGSFQzZjR1V0crbkRLVVFULzBZOG9jL3dBK01vcXFHcGYvQUpqa3YzSDNISUpYSUtBaThPeExyWEozVHVCeTBUb3FWcEpybkNqNXZCUldrMkwzN2NlbksvR1VXM2NUZXNFYkRHQ0pGTmFlN2oxeW95KytNUG1ObHZwR0hCV0hMaFJSZGpEMGNhWGxXZWMxTkNYOWNBUzNkNTdDSEh2ckJiZHFkZ1ljU0xGQ2dZSWJhYitTQWpXem52YmlKbkdxVFJzMEYwQiszQkhKYUJxTmlJcE8wdThJaFZWVmJIMnRjcldsRmJVcXFBZU9hV2F4Snd0RGJZTnZJTU9wMWxJaVpPSWFPZ1hXczRKaXNvcDQ0NHdMSVFUc3dNNC9oY25NVi8wY08vbU1MeDF4RHAzeGsyVVVCOGpoWHpENWdsK2pzWC9IZnZHSzhMZWg5ZDV1TkZxSnhvbEdGc2xTZW9oaW9lOG1ma2d1QVNSUWNvUmQ0Y09RWU4ydzJEV2RPbkRZOTRoSnJuRUxDQ0NSd3kyK3h3WjkyY0Z2amdvWUpSS3BNS0lqdHdkbnVMWXhvTWxycFRqdXRFMjdIVnhaazJnaHFPdkwyOHVMUVJMdEhZaDVHQmVONEh1M3Fyd0hBNXJVSndZV1FFb1pHai9iTVVNOGVpSWZiVDFoekY4aXJUZjgvdUQxdEIzWUxQQzcvY05KWEIvQWV4enphRUhieWN4Njd1TEZnNkpKMG9lZkc4YnVscnF6N3hpNk1OVWFmYWQvTXFuQXJvN1Q5TVJER0NJTDFlQnJ4bFNwUXNNQlZYaUFWY0c0clFIb0htc2pTRjV1UHVaRVlPcVVub2FsWEJRb1AxakdtVFlPczJHejVhV3FSRmw1TmdDcUhIbEdBTFNNMXlJczdUWnp0OFlaMm9VZlgzQ1Rob3J5aDhuMjV0MUlkUUROemVJUVFPVUxqZXVNS3VCb1JmQStYREFyQTlTYi9Mcm5HWXBoQzNUcW83Y0VmWWV5Z0duK21LNXRnNktEd3ZIVGxTZkNPWXBualdJYzY3bExYOFJ3Yk5EU05MQnIxbWtiODVUaHdhVnhSZEFZSDFPOGs4RUViZlpyTzBRTENmUUdMd1NCc1hCdXBVbHY3RzU1bXNoM3oyV05HZlJ3amdRVTRray9VcUtRVEluRUo2aUVSenlGc3dycjZRSGdiNUpJdlFFN2ZCcHZKOW1DZlhhdzdBUzV2Skwyem8vM0s2d2lJeElmK1JQRE1rWmlGUXdLTFhHRG5VWjUvSVhqR2lOdTFkZzFyR3ZsVzgya1RqRDRVRkhSdzR0R2JRMWd4dUtrdVR5VWFhejZ1T1Q3c0FQM1RFTlhBMi9Vd0dVU3VBS3Y0WXpteTAzSWtTb1ZBcnBjVklMZEEwZ292Y2lZNEhZM0txMEdzVHh2QnJRUjFqVkFTbkd0Y0U0ODBPNnBwWWcrQXVhcjdnb3EyUXgyY2h0UWNLREpvT3V6WVRiSGtHdklxQ0hGSG5ZZ1hNVm5ZN3Q2NE1Dd3QrNXJQOU1RNkZqay9ueEtDY1l1YXAxamdJbnhsdDRvOGh4UmxIT0R6TzhNODV2aWJYTm9XNG9qc3dPNUNSNHRtbEN0TFc2bGNTREdxMG12Y0dnbTRNYzJOL05ZRUtFdUVZK3NBNkxvd1JFRlEyeVppdGlPd3ZQTHd3Y1A3NTVKZ1RhcnoraDdtSU9ZZWRydU1aVGxqY1lOeVlCSXMwRUU4TGxqeGdyTVlFbXlxTFRaQ3FpeG9RSklqT3dUWW1PWlRobFR5ZU1FdWxGQUV6N2hmWU13dVpRZGk2Vit0YzZjdWJ3am8zeWpUNGRac0FhQk92Zyt5WEphd0lVWTQ2cEx4aWNxVndJRzErWk1CNFF5dmtIcVAzRDFQKzVIbDJuZVlsYzJqQ25qTlA4QU9TcmFtc3kwWFM2T01Va2tFQlZWaTFHYk1JZUU3Y1JEYlJOdDZ1YTU5b2c5azM0ZFBKN2RUdzhrREJzM1pvRTFUTFRxcU9PUXJwZHJwNndmelY2NWhHY2hQUXAyNDFZUWxXRVJVQngxQm1CTXhkY0tDdkZvRUJtQzNxQUtBaW96TW80TnpmSkhiWTBOUlpWQ0ZiR3lGRExTMGIrdXFSQjJKang0QVVTamVjbmlOWEZ1Qm5JNFVBTGdsbXNKaWkzbnkyYWFVR3p0NVVyVVVDUWpxSGlrS0Fod2UwQ3NPQW82c2twQkxSRGdlQzZvRGdVMnFZaEtMYWtIRVYzUXRHYVNPc29sWVhJczFwcGlBQW5KaTNrS3RQZGVKei9walJWcHZjTlE3S2dCdXVCMzNXbUJiV2hkU1ZKVEFWRFlIK1NTODA4VXhYUVp2bm9KeEhiOE9DeFN5V05HRUVyeHgzZ2xPMGNUWCtBZnFtTFhnaFNENStTd3hHVWpaaWtFOWdEV0J1cUxyc3h2UnZBWjg0NkhBaUFrUFJjNGtxYmtDc1FkWTRCQXNvdVRiRUg2Q1dBaXJ0TllsMmV4SUZraHlpRHpDRnh3U1lCQ2kyNHdJdWx1a2U5WWhtTEkxU0diKytYNm9GVlVONWlCaXpJVFUvdUx0NnA0WlV5OTRUVzVvMGt2eldYNndyQW1HNktnYkc5M0JjSTFxMWhCcEJBcTBIb0UxSW9zR0hBdlJvRnN3OFlQR2doQnR3RnRFRTl3YjBBVU45Q1lucVh0VHFkNnRoYkpPYzB0ZlY4NmJSN0lYQ3NVcXhjVWpvcXVpcXR4U3pWUW1zS0JKdnllSEJQaXhxa25YZ3BOM25LZDNycWpLZEhiS29iNWtHZDYwTjlheUFpcWtMRkVBQUIvY1pjSmFvSUZkUDVmRGdkMEVydUYrR0RRZGN6ZUZGMmRjSjczTUErWmplZ1BBTFF4UWs2L1lvSWFSWUROMzNSU3E5RmVHaVByQzgzT29nT2RsVVJDcXVxRExCdDY2QlhRWE5GQ2xNV29VQXBZODh1UlpONDNVU0FXc1Ntbk9LdUxUaWpVUUVyazFJbDVkejUxSUliYWdjYW5CQ1FrVlpkZ054M0hOWmpuQ1JBUlRJVFZJTHBNSmlDSzBEb0FERVVlamhVcmlSV1lnSTFTOWJieVp2SkVJTzRyWTNyNVlndkdtaVVGOFdWOHd1N3FqbHJRMC8zTkxJcU81ZDYzN3c2ZWlBNVdMdGZlakFlbGMxZGgxNmFZdjZ1NE42SC9BTC9jWjZqbWdwWlBRN3VDYTRsWW8ySGpJcEIyM0RYUTlqZzNCbFU4QlhieXUwWGVQTUNFV2kyQnF5WG1Ma3lEamhQREEyNUNpQlpGdUpsNGRoRUh0UzZkTGkzSnFXb1VZeTAxN1Vzc2VxbkY5ZjZpT0JZaUhiMW1nenBDRXBVWGc4RzNlQmFtZDAwUkx6Q3I0NERGNkRYRlkrc2x0MnRZWnY0RGxIR3dwem9udGVNSFVDQXFlbElWbTFEV0pPZGxIV3QrMmhhc3VId0JYeWdRSW5ITGwxeE10eURvQWQ3NVI2eTdjQmV3VDlCTWFrSTBRVnk0Q3BEWlM4em5sY3ZjelJPVlRnd0lpblVKT0hwa2VwZ1k5RjdXVTd1cHdaeXJvRjloOVlncU1aZDJLZmhNcWlJSllsdDN6cmt6LzhRQUt4RUFBZ0lDQWdJQkF3UUJCUUFBQUFBQUFRSUFBd1FSQlJJVElURVFJa0VHRkNOUk1oVVdJRE5DLzlvQUNBRUNBUUVJQUFrQ3pvSjBuU2RKMG5Rem9ZRm5TZFoxRTZRcFBHSjR4T2huUXpyQW8vSFNhMTlCQXBnRTFxQkRPa0NUb0owRTZ6eEdhRzlUb1oxblNkSjBtcHFkSjBFS0NkQitPaG1qT3M2aWRSQXV2anBPa3liNk1ZYnNyeXFMaHRCa1ZnRWtjaFNheTBYa3NoYlN4cnR0OG5ZNDJVMnRzbVVyRTdOMVFHNEx0N2kzSGZ0SDduUUtHZEoxbW9WL3NvWm9HZElHQmc2bWJVVFlFc3lLcVJ0ODdMT1Rrc1NsdlU2SGQzQkVLa0RRQWJVUWUvZFE2aUJpWU8yNVh2Y0dQMmxOSXJXZFFaMWhRUXBPc0trVFFuWCtoYUo1alBPMGJJOVRrY2szdHBWV0N1SnNTcE4vTk5GWlhjYkZESGM4R3ZVRlpFNm5jcFVENTh1b01nbWVab0xUQTVQMFB4TitvZlEzQzNxRHVZZTRsdHhyUW1NN2V6UG1Lc3JySldWVWdEWkNyRWZROUMyQzBHZHRRTkE1RVcwZkVMQ2R2NjJZT3crRDNJZzdhaCtJZmlINGxOVEQyeFZKbEtqSnBYVWhpSXFrRDNqOUdQdGFxMkgyaXNBYWdWSUF1dlNxSjFXYVdCQk9zVWZRRkQ2aTJWc3hXZFJHdHFROVc5YTJEOFFzQkREZWlSc3BJbVN6RTdOUGRpUWNmZFpVdWoxSFJ3ci9BUHpQSW54QXlONkMwNkVWUXNJT3ZiTlFpN1laMklEcUROb1B3TXVxSE1INC9kbjhmdWJEUE5jZnBWZmJVZnRHVWpqMjVwTGJuVUVUYy94M0FUOFJQVTltc2tXWXlzdlk0Nko5d2hjQSt3ZnlDUVRzbGtRYmwrYlphTlV1YnQ3TlZnYkljU29hR2lHWmJlc1MwNmlzcmZBSDE5Z3dBR1puSTR1RFlsZGc1Mm9aSm9YY0xnRFpCQStRNElteExyWHVib0svYWVnQitka3pKeXE4Vk96TmRrNVIrNEpxdjFVZTZHMXFLd0cxSzJLdjdHbSswcWZGY1ZnMklsbjkvVFFJZzFxYzVqcVFMM3BvR1pTV1o4eWpvN1JzektITWdsRFRZdlplaXdKdUJDSjBuVFFtVnlWVlNrVlZwWmJZV2RLd2c5MmZlblJYQ0pWb1U3TndnckRGZ1ZBaFVQS0xHOW8vdlhwV1lDQzBHQWdpYUorTGNmOEFjVkd0a1pNZkk2T3ZJbTNtN3FWOEZmUm10d1VDVml1dlhyNmFoUFg1enJMc3orS3Eyb2l2b3RGSnFRYktBblpwMjk3Tkx5QlNaV3ZVQXdiN2tSWUpjcmI3S1B2WDNCQVNKVmtyOE0xOVNBYjVQak0zTDVEeXJoY2FpNWZkdVU3MThTMTc4SGxXNVdJalAwblNhQStUby9Pd2ZncW1wMUdwWXV4SzZ0Q1hxaEU2RXFKcjc5d0QxRi9Ld0EreEFyQnRCYTIrSUtMZndhbkVXcHBmWjBQVVozSlZXWW92b0hMWm1LTGJiOG5LZk16YVFuR2ZxVk9sMWdvY1BVR25xYkUwSjZuanJnUk53MUlaNDZ4T3FDYVhVQ0lma0lnK09pZk1DZ0NEMUFaMkFHemo1VldUczE4enlZd2NmcFhqNDNnc0pOdEpTNU1ldTYzc3lWdGs0WXhNUG9jWER3NnNXdXU3aU1vNVdHcGJRK20vcU5UWW10elFtaHFhZ21vUHJuNW1SajVIMllOMVkySDUrbXA2bHRtUTFXSGpWRmpoSm1mOW1XclpGalZIR2QzNkpmbWQ4Z2dMeExWNCtNR2lPekRaQm5xZW9jbEZ5QlMxZks0Lzd5ekd0bzVMRHlMV3JUelZyWUsvcm96TXR1eCtSeDIvNGJFNUhDR1RYM0dWa0ppMXNWeXYxRytUZ3ZRMklHdG82c3hVWkJVVXJXZlNZMlRqNVZyclRsWFdZZEJaOEYwUk85dVh5Vm1NRlNsK1dmRndUWS9IWjl1VW03SnlaeGt3MmU3bXVlN1BWbEtidVNObFdWWmdaSE4zQnFWeDh2SlczdTFHYzk5L1ZMc2hhcWkwL1VmT1p2SlBVbVAvQUxreHNlMWFjanVDTnc4bFVNdzR4eWVTb3hOK1RPNVhNeWIyQ2pDV3p1c3B4Z3BDbnA0NExVL2NzR1JXOGZxdkhDdDdDSXE2R1VsVDFkWUtub3ZMakp3QmUvZDF6OFlZSy90NnVXNUZ2NHhtUGwzWTVXM094V3krZldoK0hmTDUrK3k2N01WTVdsV3N5ZjVtWnhsWjEvSElGdXgvTGs2YzhqeDFRcUFGWFRIMkt4eWFVWWhFdnkvM0dTTGE3Y2F6SnREMkhEdFd2cXR0QnNjVnJqcFFmc1c0ZVJPa1FXRUZYWFI5Q20rcS93RHd6N2JldWx2djhQanJUUnJ4QVdwVm14d3A0K3BjN3NTdGROQjB0dlF0MEhNMFZYY25Wa0wrbWF2OU4vVDFmZlB0djI2VE91ejhQa2FQQmJpWS9NWGtYWTJKVGlmWmo1V0NVRmx5VW16SnNJRnB4aUJTbEtub2JWZlhqMjF0aERsWmg0NXEyRk9HenVHTEFoOTExMmx5Vmlnc05tcXZ3WkxXRnJyY2pJYWlaV015T0xqakIzWStXdGY2VHhZd0pUazZMM29GcHFQZWxlMlJqVzVXN0RpVk41MVE1bGZqdEZrcXJkTWtISnR0cXgxU3cwMTJvbW14aDNCM1pTVjBRY1d2dXpCTWV1czlWYzlmc05xVnYvSXo2clRjOHFoZHk2c2FEU2hTd0JQL3hBQXlFUUFCQXdNQkJnUUdBZ0lEQUFBQUFBQUJBQUlSSVRGQlVSQVNJQ0poY1FPQmthRXdNa0t4d2ZBVDRVRFJVRkppLzlvQUNBRUNBUWsvQVA4QWpSOEl3bkkwR3czUjJDRWRvK0dkaGhIdHNQOEFqV0hDT0ViQnhIaVB4QjhJL0RDUENVYmJIQ2RwMmdvQ09Kc29iU0VVVVVFMVFFN1lhYWJEUEdWamFWWlVBdVQrQWplMzcwQ3MyaU13dEZVY050a2d1dENCa0V6VFRUV2Z0dEcyd1ZOdFRvTGxVNkN3L3dCN0IyN2YyVmtFbGZWK0ZjS3grL0dKWTBRZGIwaEM1a0ltRzVqUytLSjI5NFp0Qk1BR0tFUmNWTWxPa0lvb29vaEhlT3VQN1Izbks2TUVsV0MwS3hiYmY3OFFrSHlSaDJRRE1EQkkwTmdVU0dPQWJReU40R0FRZE5VN2RuSnBlOFlBN284b3RhQ09rY1B5NU9QNyt5Nlg2SytUMVZsYlpyd0NlRWVpSjBzckN4RVZIZGN2aUVGb0JxSjFpWnFCbXljQnUvTlJ4a0FteHBteFhnN3JjUjlQN25UZ0NFSWNPdkNOZzJ1NXRGek9aSExxRDdVUWExclRRTTVpWnQzNVJncXZobThPTlFUbWJDTlpCUWdXYm9YRFNtYkRFSWc5cmNJUVFRVFVFRU9JeUJsUEE4VjN5MG5NVENkRGdQZVpVa0dYUk1EclVWUWhraXhBUFlnL1NjcWpTM2RFQ3RldGJCT0k1YUVUWFNUSHFTcjRIU3d0U2UzK0RXMERXVUlmTUhxbTFGQ2YvSkJuM1RDNXpoRFFCQUl0bjlLYzRHOURiMjB3Z1JJR2FpdURCVFozUlBXaGdUcnFVNEFDc1JmdjA5bFNCclR0R09FWEVnNnhjZDhvaHJtdUFiWDVwYnZBajBQb25WRGkzdVJlTllzalU4RG84TTc0ZUNhUkFJUGNHbm53bm1iYVRSUGdqR1pyZThoZUdadE9zbEVremN5ZlNiSXlXeFBucmdxK1BYMENrdUZDU00rZUkwVFNUQitVR3NXcWplSWtXNllLTzg5OVlJTkJhYVk3cUM1cEFNMG1jZ0lERlJueU94MjZCVU95RGdoUi9LM2VhOGdXY3dFdGNBZnBjMXhUQ3d2QWk0UFU0dmNwMjRJQkR2cWQzY1pvQU1ETXJ4UTVyUnI2enJCeW04c1gvd0JCR3lhUDRnOEExZ2t6RUEycVZjbUhPYlZqVEdUM09OZ01pSzRxallUN3g2a3ArNzRacEdUM1RwSmk5aWJ5aHZUY2lDUGRDVzlFQUp6cXEwVWVRVmd2SkU3eEVTUHpNcHhKTjVLY1hibEpFZ1U3QVRWRXdLVUVlNWtvamQ2NnpSQ0pmTzdndGEyWm5yYUU3azhNYnJCVFVqekpBbWRDc1JVWGdWS2RJZDhzV09nb25PSWdRSmdHY0FDcDgxQUgvVWFIV1k5aFJNNWQ1cGpUbXFnSE5pczM2d1Zkc1JPUjN0TUpoQnNldWlkUUN5Z21aZ3pDUHlrVDFHaU5HMDZwM0wwdklVUU5NYVh5aWpJRTE2Z3hDb0JVblBZSVR2U0owcGM5MDRnMmdtLzltNmFBRG9oREFZNmh3TUVINythQ0Vuclg3cVd2SWMwazRwUXg2aERtdDNKSkE5ZGRFM1NvRTN2Q0pEUmR0QUNKcWJ4NkZPUDhiQkpzM3ZVWUVWS2JEWUZUekVudWF3S1VWM0ZwUGF4OXE5d2dRR201c1p4Q083RUhwUTFIVHFyMWlteW1QUUp4TW1aajlyaGFxbHBUZ0RlTmxqVHo2NjkxQWlvSytVQ3ZYK2dnREg1MDJDTjR5ZXAvUW5FUGE2V2tYck5Md2h6UUo3b3dBYWZudGxDakJJOHlRUGFVWS9UOWtBNDRPZ1RhT3A1M3FQZEd1U3NFaENrb0NYR1NqQ3FoVnNFZVZQZktDc3N6S2FDUiswWC94QUFxRVFBQ0FnSUNBZ0lDQVFNRkFBQUFBQUFCQWdBREJCRUZFaE1oRkRFUVFTSUdJMUVWSURKQ1lmL2FBQWdCQXdFQkNBQTNIOStVaUM3L0FDTGxnc25uNndaTUdWUFBCZHIzQmtSYndSRnQzQlpBOEZrRmtEK29HRTNBWjJINFlNZm9kNGVwbldFUXRvenlHZVV6eUhVOGhFRnBnZlloTGEzQlpxQ3lJNWkyd09aNURCWjYzQlpPNWdmVUZtaEJaQTRuWVR5VHlpRzJGd1l6Q1kyTGtaakVWWFltVGpuVGpIdUpBaDQ3S0ZvU0hpOGNvcUxmVFQ0ZXFQaklHS3hzZlgwcUhlb0Y5UlZqZndIdnl3V0NDeUs4RGlBd0dibmtmM3Z5bjkrV0d4cFVsK1FTdGZHNFh4Y01CYmNZdXV6MFNsdGhMUXo3WXZXU1NIZjM2SjdOdUQxK042RStTRjlTekk3R0M0UVdpQ3dRV1FXUVdRV0RVRm9uUXdVZ21lQll1TnY2NFRDWEZyTE16anJHdit4TE9yYkp0czBOSytTNFlncms5Um9DMkMxVDZnWk9zeUdPdlFRL3JxWnBwdC9jOGpDTGE4RnppTGMzN1c0eExpUkI0eFBVeHFCa1dhbGRGZnBZb0ZjdHRHdFMyNVEvcTI5dllCZHRSdjVUb1FmUlZqQVB3d0RmZmowSjBNNmFub1E2aWhSK0I3aTdFRGEzTDcwUHBWZHpPUFlwWVdORHFVM0hzRG5ReS9JZzlOYllEL043Q3g5RTJHYWVCWGFKWEhxY0RZTGtmZmtFOGs3dEI1TmJMVldxZ1k3L0FNMVkyVmRXWHJCT3lDRkJQcFVKM29OcVY0aldtSnh6cFBocXE3Q1pYUWRTbVFVdURpbTJySVhzT1F4dXV5UEZhbzdUVGdiUGxjbWFjeFVjblVTaTV2bzhYa2tiSjRxL2V5T0wwSlh4bGdIc2NkNmljZlNQK1NZbUlvOXF5b09xNU9IajVvL21PSnRvTzFSTEVCQk5wVTZnQjFDbnYwdFd4bytLcnI2SUF0NmluSnVyYm9MbWQzV0FCVjFQRWpUNHdCOUxVdzlDbkZRSGRpbXJXZ1ZLb0lQdWROQ0dtdHh1R215bzZpb3grK2gxcUJDQVFmYU5BZ01weExiNjJLbmhWZXJ5dDEyZmFwK2dOYW4xRzkvZVBTbU9uY2dPTGZZVW43UUJSb0lnYzlacXRRZGR6b3htQ3IxaXN6bUFrcnVLZTM4WjZVOVp2ckNrM0EzcWFCRVVLQnFZRnhERkVMK0J0S1BNT3NTcFRnSGJYV3BzTjhraURKVUQzOG12OURKV2ZLU1Uxdlo5a0FEUWdaZGdTNGYyOXlnK29QZXdFMFRDTy8zV3lrZFR2UkltZ3krelUvOEExTEZCbytidDkwWmhwdDNEWWJxZG91S2Y5SnJzZ2QvUXF5V1ozTHVDQjlqVUxpZGdaaDFpaGpaWlU2YjJYMlcyTktXN0ZBSGJZdmJWSkVvR2hzZzliaUltMTl4RHJlM0dqMkNFMktld1lBVDcrbTZ0L0ZyTU1IMmxlQXo3MXgyWmk0V0dhMnlNMG5ENkpndnZQRlZmSTBwajN2TzhEUXRCdUxXZDdJVnhDN2EwVXNKR2lMK25vZDNaT3pnblUyUElJaEJXSTZxU3BTMnZ1VWdaYXJTSWwxYUF4c3ZIRGFIeXFQMGNxaGZVeHNacjZUZEtlSmV2a1BqMzE0ZE40VkZvcnJ4Y2U2WlBFTTVyU1dwMHNZUWJuUVRyT3Z2Y0J0MVA3dXRFSzhEVzYxTk13OWdNSVdzWDZEUHVGM2dKbXQvZWdQcFVMblF6ZVB2d0dDVzhOeGh6c2pkbFNpL2pUb2RFWXVkTVZZeWk4dmtlUVpGbVE3czZjamppaTlnbzNQYzYvZ2ZYdURjOXpSbXY4NkUwUDl2RGNkZzVmSE1iZVZvY3QzcjRDNndXUFRNRmN6TnR0b3FkbVJqRTBsYTJDNnNLam1xb0JDVE9RVnJyaUF5QkcwSnFhaTBzMUpzRDhkZjhWTDY3c0RKb3JWM0ZiRkM0L0h1WWxkZHVEZUNSQVB3Rm5DOG1jQ3dvMWROdVRZcHV3ZUIrTnlDWGpKUkV1RjFRbzFqaXhpRzdiTFZzaSs2YWZLeEV2cXRzT3E4SGk2Y252YmVuRHBsY2dLcXVSNDJqRXNJcW5HMDVPUm1MVlJ4dkV2VlhkaTJZbzRxaWkyczVTZjB6Uld0NXljVEVhbm9sdUZYUmpGM3Jvc3NjTE9CNGpHb1J6YmJ3dHI5bm9GWkVUaUwyd1BsakQ0dkl6bkNWWUg5TzRHUGdoclRtTXRLSXR4YTFDelYrVDNDVytLRkgyWi81Q0FaV1hSaVIzcnZ4aFcvSGNvY1dvcFZuMFpsT2ZZY2w4U25zYlh3NlU4bmFsVk9Md3d2VE9wcDQrbGFxdUhxdHN1S1YwTXVMVXRidlhWYTVlc08vWWc0bVRkVll4UVl6M2FaMDRlekl5eHJINDU4UEZiSHV4cktjV3ZyVU02aHo3R1I0MDJiYnJpTnZWWmNIREFtZ0VzdW85TDArbm82Z25kU0IrenN2UjhqVXVWRXlXWmVRNzBObytBV3B0NnF5dGZkY0xPdEhGdGlubEVHZnpWaVY4Tmowa0s1eUx4bTlqWTlmd2FPNUxocVIzb3lQTWlVV1YxZUZBWmkrVk4zU3h0TUVhbmEyZWtaU2dJdnQ4NTJVdlJLeUlwckNuc2UxZnN4bk4xSVNHbXV2SFcwVU9DaFNYQkFtMDkvdXdXM25UWTl2alJxbEMyS3htSTZVVzZMdDQ2V2xMcXVLYVI0YTN4dDQ3cmF4S3RZaU5zeHdVUHFzL2NxczBoMzNzSTdudTN0NHBkUVFFTEhZSGlZaVZsQnNOWUFySHIvL3hBQXlFUUFCQkFFQ0JBTUhCQUlEQUFBQUFBQUJBQUlSSVRGQlVSQVNZWEVnSW9FRE1rQ1JvYkhCTU5IaDhSUHdNMEtTLzlvQUNBRURBUWsvQVAwQWg4Q2ZqUkthaG5DR2RWcGs3ckh3YlNUMFFzNTdxME1jRDhHVUxQaEtQQW9vb29vOFNpaWo0TUlmcEZIOU5xS1A2TGxmaENGSGd3a2JnTEtNb0hnMk82ajVJOEJYRVVqQ1BHVVUwSVg2S0FFNU9KVFo3MnFDRjc2cSt5YkNISEhBU0UxQ25UUEVJSzBLSGh4d3dnc2NCN3FBZ3hGNy9id2hHWEVLNThYcjM0NUMxOFpoeGlOdFZXNkFrOWZrbWNyeHVNa1RZTTYwSVRZS2FnZ1VFQ2dSNHRGbnduZ1VjSVNONGllZ09KMUlVRnpDU0pFR0RaQkcreWI2QTdXSm44V2haN3pQV2ZEblFhL3h3d3NJY2RWMldPSm5nT0FGQ2NvV1RZTy9aZWIyYlljU0tPdFRFVlBxbXlYWXRvZ25jZHM2cjJwTDR1WTh3Mi9id2xGSGdKNDdjU2pSUkNjblNpcVpHVHIwSHFuQUIwK2E2STBPdG91YzUwanpTMENNL1hkUnphUzBHQ050NzlWa1c0bklhVDM5U01vUjM4SlJSVGs1RkVvbEZIZ0VNcW5IUVhIYzR0Tm4yYmZldVBTZDBKWTUxakJNVlN5S2syVmJzMytJMXloSkdmNlFCdTVWajg1Ny9ORDRBWm1YYWpsT0J0STZYZHIvQUl5MlFNUUp4Rzh6RzRUb0JnZ2JrVjlrOXJXdElMeWNnemdkOXRGQjZySUpSaWEvclpTU3JrMzVkZDUxUjhHaGcrdUNnWE5MWk1EM1lkeTM2eDgwMmkwTzdBNG5hY3JBOERaZUN6bFBxUVI2ajdlRWVSMllFazdlZ053dlp5SENaLzZ4cEFHSGRMaE9FVElGeE1Kb0E1WWdBRDV4WjZJUURNZFk0YTRSV2s2LzJFM2w5a3lwYVJaMEZuTzVBN3dnZVZ3SkFGa1JvVGoxN0luV1FSZzdTSytjY0djempwdU5VRHl5Q0pORU9JNW1tTlFXaE9EeTBtcUl2RzhjdUdwditSeEo1bTN5c0hSb2l5VHFkSVhzaTF6anFNQzRJdWdSbHU2ZjU1Z0FZemtuN1FQVkRLY1E4dEptQ1EwQzVnWHBLQjVjdERxY1JKR05NVEI0RWNzeEdzekNFa2tOOVRkN0FESlRDNzJ1U1pvRUVqNVlwTUFpcEdRTVIrVTZEb0RSS01IcW5lN1VlQVVnM2xhWjVTRDNxUDNGRk1EUU1RSXVjUjNFMnZadzU1bURCTjJMSk1WcGtKb0VtZWlNT3ZFcDNOek5BblhtSklJOU4wM3pQdHhCUCt3b0pkSWd4RW1oS2JEbW01RmpmUDBUUk84V25XTzZOa0VmUXA4TzBJazlxV0haZzRQYk1TdmFCelprUk5icG5tblA4SWtDSW5XMEtJcmdNYjRVejFqaFIyK3NxMXBGYjJnSW1hRkMvd0FKeE9nbjcvd2pMamZTRGRkTnVGQ0tpczlrSmF3aHdFYWFqN0ZHRzV4UUF0M3kyM1RoVW5sSmcxaVRlVUp1amsvK290Tmx4eHI5RVplU2FGQU5IM0pLdzBHS0F2SXZ2VjdvMzYwaEl4Mkp3VmpYaC9zbERBeHd0RGdmZCszVDlsTm9XY0twUlJtQkhvaDVYQVQzR3M1UnBDWlJ0NWc5aGYzaFpKNlVNL1hvcEVaUnhmNFdGcncyL0sxMTRZS1BBYlFqUy8vWiIgaWQ9ImUiIHdpZHRoPSIxOTkiIGhlaWdodD0iMTU5Ii8+PC9kZWZzPjwvc3ZnPg==", + "description": "Produced by volcanoes and industrial processes, particularly the burning of coal and oil. Results displayed by combining the latest and aggregated values and optional simplified chart. ", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'sulfur', label: 'Sulfur dioxide', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'µg/m³', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'sulfur', 'µg/m³', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sulfur dioxide\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 600) {\\n\\tvalue = 600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":100,\"color\":\"#3B911C\"},{\"from\":100,\"to\":200,\"color\":\"#7CC322\"},{\"from\":200,\"to\":350,\"color\":\"#F89E0D\"},{\"from\":350,\"to\":500,\"color\":\"#F77410\"},{\"from\":500,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 600) {\\n\\tvalue = 600;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nif (value < -10) {\\n\\tvalue = -10;\\n} else if (value > 10) {\\n\\tvalue = 10;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"µg/m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:U08yLXZhbHVlLWFuZC1jaGFydC1jYXJkLWJhY2tncm91bmQucG5n:U08yLXZhbHVlLWFuZC1jaGFydC1jYXJkLWJhY2tncm91bmQucG5n;data:image/png;base64,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\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Sulfur dioxide\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"public\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "tags": [ + "environment", + "particulate", + "matter", + "air", + "coarse particulates", + "coarse particles", + "inhalable particles", + "larger particulates", + "dust", + "airborne coarse particles", + "sulfur", + "sulfur dioxide" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/switch_control.json b/application/src/main/data/json/system/widget_types/switch_control.json new file mode 100644 index 0000000000..7172cfe0c6 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/switch_control.json @@ -0,0 +1,34 @@ +{ + "fqn": "control_widgets.switch_control", + "name": "Switch Control", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Sends the RPC call to the device when the user toggles the switch. Appearance widget settings will enable you to configure how to fetch the initial value of the switch.", + "descriptor": { + "type": "rpc", + "sizeX": 4, + "sizeY": 2.5, + "resources": [], + "templateHtml": "", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-switch-control-widget-settings", + "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"showOnOffLabels\":true,\"title\":\"Switch control\"},\"title\":\"Switch Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}" + }, + "externalId": null, + "tags": [ + "command", + "downlink", + "device configuration", + "device control", + "invocation", + "remote method", + "remote function", + "interface", + "subroutine call", + "inter-process communication", + "server request" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/temperature_card.json b/application/src/main/data/json/system/widget_types/temperature_card.json new file mode 100644 index 0000000000..0e3c7b0530 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/temperature_card.json @@ -0,0 +1,28 @@ +{ + "fqn": "temperature_card", + "name": "Temperature card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAApVBMVEXg4ODg4ODf398AAAD///8jTMfh4eEhISGrq6vCwsKenp7k6fi2trbIyMhaedXI0vF0dHRYWFg/Ys4+Ys7Ozs67u7vn5+c9PT11j9zb29utvOqkpKT5+fmRpePz8/Py9PvV1dUwV8svLy+QkJCesefx8fFMbtFmZmbW3fXt7e2Dmt+svOqsu+qwsLCCgoK6x+6RpuNng9kxV8pKSko8PDx2j9ydnZ0akTsKAAAABHRSTlPvtyAAaBr7wwAABsRJREFUeNrsz0ERADAIA7DScTjhgX+Ds1G4xkHwGKjtgg/E5HoNIjoPmEDlCbgSKUfEOKLGETWOqHFEjSNqHFHjiJpPfhmtthEDUfRlpFHlyh4VhEJImkXKWiUpelnj//+0XluYlGS9xph13Oa8jHbmCnTQ7sN+JZHFakEj9LpxI7/8J0WelkqpuxEVxw1P56KNJrq6CDzAcuRGnAvsnevoXBwbomuLrJRa36+VWo1uZtmXWqomyjaUonX1hshJJ6jAloIaxKE48QiGwhhVu0tpynlbeuoqJrOK3MPh14tSv4+L6JhkE3vy7EtKgwxsyDB6bIkqi+dAmVMMgX1GMCBoKHpCymEXR63TUDZxdpFHNSkirLGy5BPRFhI9ugZ113BsUT1EUENuZ3cYvokkjcRGo+vmFqHFpMiQRAQrH9vRiH2rlXVmLxIZIhpJV8V/EIkYcBIpnD9ZJCYB5qNI3okUAU3EsBgZF9kICLN/I8+TIiX1RD29EwloJFRLGDaRFjiIDH+JxLRPzS3y/GNSxHE0NoV3ItFWTPtNssaXJiKcbYRhxyW0h4PIlovJqfu8VytWAsFzzETisYwQiQUidUilR0RS8pq2Ue/XQ46WqHIh7RkPbrcLmIExuLLIadrNnM3cInePL/+HCDhbxElH0/wjIqe5ssjTWjXWC7ptTogs1YEl3TbTIq/qjVe6hIeHCyKXi+BCjl9J50YO85PG+fb9WOQPe2aw4kYMg+GLMsiqzUKxRY21xr0sdcGH7fs/XGVnp5OWWQodSl3wB4kj/5KTL3ObYZf3lr8ncnvk/d92YPC3IpF+0rBEk4iIN1EA2LgIW0seFNb3LQKXaCL0rAQ7WjbgQF6gOu3ucNxUpGfxfp5GbzN+i3t1WeTz7eDbOyLYClqo5B3xLuKpx7oEQ14zY8hCJG+Ih0hOusMw4C4S7y2KRiWVfWM0lusir7eD13MRYQGPUFKGrYJBeBRJAC5BaKOqFUA9Y890J4QHkWDHS8nqZ8MojB3n+XRd5Ol28OVcRBUwIYih5uFXEa0igTWjyjYh3UUsKfYQ2VsUMYgatbEx9jfKl0Xg+bbzDOciTAxeRURi8icihX78u00A7yKhZeXxioT9vJgyWAthiIzRQnJdRE3OPRSDMUauVBgRfKoVHbjEoEQqG6pIX4xWsVcmVT+uCIvm7MohouOjRSmJGe2+0Rv1hMsiyodPLx9fvj6dJA4Vp1/ZPIpo5QRqwzdJa4JGumZQvV5lm2xwIC1l8C1pMKjYP7gxoEgftWPG6qKNRv75vV+P8KcIg6CZ5Sb2BRFGmzDPIpIrXLgkLOuxwv/MEpmNJTIbS2Q2lshsLJHZWCKzsURmY4nMxhKZjSUyG0tkNpbIbCyR2Vgi39k3g1XHQSgMbzyrAwpxI4EgggRJB9IG7vu/2tiY9jcT5zYzAyYd+i1KarWcr8cYTSywY+v9GErlXaus+A3K+00bbmOTMRwioiQltOFiuQzFZpoiN87Frw5fVVvEfhGYPD7Iy69iw0gJjYR5R3mbuiK2J5BvvBno+6g0kWx9fO3QYo2pKmLojhu6QaecWOwqmstNKlebhMyBsnu2uNKMk0ZqmhkqiqjsybXPjoXG8VdpZ1G3uBkizsz7JBxgX0lELvGib2iE1aMXISjUHZctIgGVerv6YllNxNIdXr3DFpZRJHyhn7RL19JEMNeMYdjBvoKIWv9uzVPLJaUEP9MDrIsm3uvHOd1jpMhSMlQQwRhq1iI25QahJ62p0BRZYKQWKSOSB01RKDIVMtUjSjDquRJO9XXYQUbMMSLIj1pnSkIkR/k25OOEOXyutdA/z8+2IKLEq/1t3UlE0sAj/lQENX6cQyQNmPz2Ina5lr+NiGweyG0oWryPSEMJRI0Jlea3F7lSxLH4JxF/vEjyCOKvRcw5ht/ogTningsiONUF8UK/dgwuTFGs+IZ2c9IpjCeVRLC4AhZxYdK4Zz0AfPVJY3Dw2MyDZwJi2rdCQ0FFEdalhcOQh9FRxO/onhOvOydxHRF43IrdbbKo8jImvboQsa6w1N166MAZ6Bn9/Zj7VyMS1ElfbHajjuuJ3GiLQlwkZU87Y+oo0TQOfyuoJiLLIhG/uW+3zwR04hQi+Q1Q98IDd4OBVqKmiGm2PCJgkwJrOit2cpEumd/UuZ6PWKUCLHbBsQl/HvT8F3xEzsZH5Gx8RM7Gz/bugAYAEAZi4EjInMy/RSajfFoHp6BCaAmhJYSWEFpCaAmhJYSWEFpCaAmhJYSWEFpCaAmhJYSWEFpCaAVB6kbUIcvmOXufDpDMMnZr3r9XuzV/7odnfTPNiLwAAAAASUVORK5CYII=", + "description": "Displays the latest temperature telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"thermostat\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#234CC7\"},{\"from\":-20,\"to\":0,\"color\":\"#305AD7\"},{\"from\":0,\"to\":10,\"color\":\"#7191EF\"},{\"from\":10,\"to\":20,\"color\":\"#FFA600\"},{\"from\":20,\"to\":30,\"color\":\"#F36900\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#234CC7\"},{\"from\":-20,\"to\":0,\"color\":\"#305AD7\"},{\"from\":0,\"to\":10,\"color\":\"#7191EF\"},{\"from\":10,\"to\":20,\"color\":\"#FFA600\"},{\"from\":20,\"to\":30,\"color\":\"#F36900\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Temperature card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "temperature", + "weather", + "environment" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/temperature_card_with_background.json b/application/src/main/data/json/system/widget_types/temperature_card_with_background.json new file mode 100644 index 0000000000..c7de39d536 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/temperature_card_with_background.json @@ -0,0 +1,28 @@ +{ + "fqn": "temperature_card_with_background", + "name": "Temperature card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest temperature telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"thermostat\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#224AC2\"},{\"from\":-20,\"to\":0,\"color\":\"#2B54CE\"},{\"from\":0,\"to\":10,\"color\":\"#6083EC\"},{\"from\":10,\"to\":20,\"color\":\"#F89E0D\"},{\"from\":20,\"to\":30,\"color\":\"#F77410\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#224AC2\"},{\"from\":-20,\"to\":0,\"color\":\"#2B54CE\"},{\"from\":0,\"to\":10,\"color\":\"#6083EC\"},{\"from\":10,\"to\":20,\"color\":\"#F89E0D\"},{\"from\":20,\"to\":30,\"color\":\"#F77410\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMwAAADMCAMAAAAI/LzAAAADAFBMVEXp6Ovy8e719fDi4ujk5Onz8vDg4Ofr6+3Z2eTw7+/e3ubx8Ovp5+nX2OPb2+Xv7evt7e7n5uni4eTr6uvm5efh3+Pt6+nO0eHm5uvLz9/T1OHx8PDk4ubd2d7d3ObR0uDV1uLd3OLm4+bGy97f3uLJzd7V1uDDyd3q5uXb1tzt7OzY1Nrs6OfUz9fo5OTW0dnb2uK7xNvQ0uLf2+C+xdzAx93SzdbIxdBNU0nZ2d/Qy9XKx9LEw88dLjDMyNTBwc1QV0wbKyxdZVtHT0YhMzS3wdpgY1XIxtIlNzdeYFFWWk1aXU9CS0JmZlVjZ1krPDpqaVhbYVXAvstNUEI3REBlaV2zvtjOytRyc2VRU0Wno39ITEDW1txZZ2EyQD3T091pbF5XXlRUXFJsbl9gaF9PWlNbWkjQ0NjKvKFXYlttcWVpbmTAtZWxq4B1c2AYJietqIPGup7FyNOqpYW7soKempe8s5JkYk9DSD1ubFrU1Nq8vcpka2E8SkU9R0AzPDSztsRQZGLP0du2r4J7eGRVV0k4QDe5ucVRX1mkqbhUcXpCTkjLy9O4r5J5e2xWVkU+RDq1uMaqrbq2rnxgXkyzq4+TkYirp3Z2d2jMztiFhXexq4aLin17fnJwcF9KX1yMjYeyq3mmo3KCgXCJhm+AfWpfbGXDt5qHiYQtRUVzeG1GVE6wtMKwsb1fd4CsqH3Jy9edmHotNzKBhYCZmWg9V1dkcGpJWFN8gHorTFAiP0OrsMC4tr6jnpxBXFy1rodsdW2RjHKYlpKup4uWkn24r41pe4ORjXxraFKdl4yjnoA2TUyepbWak3OUk2W+vMSUna3BtoWjn3mgn26enaSspJfBxNKmn5SGg2Y0UlUdOj64vMqrqKyqpJ+clYQlRUknMSxldXZze3Whoq6Lladzf39/fV8VISCknY2zsLRyb1l2goxWa2uQjWSAjKCwoHW9s5+DipOIhl52dVnFwsqUlp+zq527so1KaG16eVs/Uk9+h4mNkJFdcHKmlmjEvbJr9p+OAABJRUlEQVR42oSaP4/rRBTFR/Josoxi69lJhN8qJGuxDAKEGBB/ZFiRCm1PAU0kl+ld7jYrN09PShM6f4cguc/7CCtBlSbQ0dBRINrHuTP2TOzliZOZ62Vp9se5984dBzbLpEjTharuC+ydippjVedCBIJkn83LP/54fDg2RV2Xu8Ovh/vTpX7v/nA4Hf74Y7/bnY6XzxGOq2N5dUEKaSNcXV3hEV5dXV5eUfjss+VnpLc/exv6jOLzt59bvYf1HuK7pPdNaPXF+1988cXnn3/6+adW3zn9MBC7CHgWVU1zU8kLpYpjJcVCBEYtDP1UnH766bB//OPl7rB/CYjD/vASz7IKo4XRRfV8GUbRxQVWJwABJkQ0KAYGH8TlEkAWBssQWRbo3RbE0xiYLwADIqs3wZwudHUshKz3DycluSiPEW9JEFq1v9A/NXhI/M1RBAgphSQQuYgiCoj0bzoeUGBbFnKnJVpiE4gxCRwAaWHsAg1pCEMCjKPBIhz6eJjdy9PqcHg4PeyOq91hd6yKpuA88BLCoclyf+kApRVgwED+IFprEJ1CIwuDZDMo1iQswJA5XgTzLnAGMGDBp0djYQDSgyn3vz4eHh/URZBmt+VxddqfdveF8CzVgnhoXyzD4qfVpTQkBsY+LA62McX45lAcDEQwpCXtJWAMDQE5GND4VCMhWhwrpFrnjKPxYke1PaEmjqUQ4aVe4Mfy9FgKxxJeBFZSKSmCBg5WWkDE4d3pnOk4HI2NFoa2l2HpwwxKBzpvAnCnn2pDHFbUWXmfpk1Tb1SxkJxUPJ4WPKBcW1yG6nmlClVUpQIaVoGm1SgZ2FQzhUNJBiI8fKKdQ3XmQEq1KEuYY41Zvk1yZQM5GKg1BttVzptovmOLvKp3VVTJoGpCwa1ud6dS8iC6CpcV/hCtdZZJSjVaUf18Wd4XUkDeGWkrJ/LyQMRiZZDOcZZL8DgYhPfsBzROBoeWo/FlA4Gig5EXMqxOp0tVVfivXkRIJR0FdfiwP5UXsroUZBC263BYYa1UU9YhTAFPGBGP9CRDGt+kTaYRmYFZdifP214204zQCjyMtedzJBoWoZhgYVwfYHWOcrh5eGiKgHNkTFhUODzKY1PeoxMowvASLU50ei4XVVlWi6h6bxf2KseTUIAQXVdT1pyzuvE9zcE4GtcJLJBPtY7lLNPowfC3N8fj/rA7VZGWC6UiqVVVr45KBNWhpCIKnko3zYVAPOyqC3AIS4Pd5Zpr1BYoPCseyPRpbHIHLBZn2KWJxcsfOFjDI4dQ8GTR8bbcn8LNsdyv6qbGTFOEEiblZS24LIHYJVkHJSiopm6qojru2j4t3KGD8ObmZmloeXOsPUDqwziDHEzPGw/TCj8xVZVVU8ZjziWmMplyq4CLvBCcL2okE0zoGSQwNFRBhK7W6NCRGBispzC+dIDjO4HLNGyQYD8fDmvDvkYiGM8DBiemahXEhU5J2X2zcDCgUxGH0JWPVO3GGoEkrMvmUhIUeRIpKc76Wrc9DlA8DDbhtOmmCAiDmj90/KRmZWDeH8D48cZ3NYPE1kGasuCOG5oFWq4Q3CkMORcbdIiwblbHpmlK7FpFbtABRaVEJ+llLIqcPVjDxgZvjC4NT3eCAsTVDh50hBqQHhBYWjlzzGZGsdTXIklElmcbLXNPEykuI24k9AXGSQD0OgLx9GC8NbLf3Pyp46ZP3wsIxvVo4DgiP94QzmDy9N5YsSRmsARlkAfc2MN7ikIdcC9wYPU0gPHlY1fU6UkfUOFl64yhWXZtjXa/TZtRejjeDGcbbDaPwSA4cFISP4cJZjzNajGAQRhY41nsxo8Lg+NgvDd+ukHoBBh/5GDDGArnB6in8almJs8zb9iEpXPgOPFzoX50JHlfQ2cczwDJyteNRYlsU3NIWG6W9tNA+/iPaaA9cLCNN3YWoAci6mXKoHMaAmqjUIXlG7rTu+8kHmWAtTDBzaC0rDcULI+VbWqfWTkcb827WPgQDBZghn2AFsu0jj1KbB9BQDDEVSvEIczQoDewCOeQP3V68izGHQ9j5Wn6ueZgQNNVDgJgrC3YzMBYR7TgVD6QXvyHMy3L//DI8wbXb9LDvqYucTcwXQAO+ekGZTNgGRaOP0KJh3GLg0Ua8yBN8oyMMeLrEOHN6re1fp/uH6ZEgw0aj2NRrEDTHjlAeqM5Qxgj4w55w4ymCUILw/OPpdRBWz5aU6RPah9PWBBEx5OIp+oyLVoMrwZhJwvTXgywzmH8rEbh6dzZ8pC+YKPRBDA8HVusmOtKJ60xiFLjYVer9E2tQGD9t6w5hNO2ATfceBySu7Q5nCW2PWpIA28MhjcHMBqnOuwAx4ilfKHrUqdxkHYSykRCsET/26eF6GIXoHYwQKY5Z1yq0W5ZOhwP4xOtg2l5/JiG0DlzV9wV1fZ2nUdZGOrFRcS5zATvuhtXCGlGdNzspzA8v/Mw/acYvJiiFt3aA/XsMTTqyuHYQbqXaENzsNABaEFgeZ+xIAsiJWZ53RSF1gEVzzyYeRiexrGQmcXzPJ5FbNZPDQow5s36OMKfOGfJFjoeVzZ+9DRZRtvryTjgEg0wQcplFE8YqAKbbrYXYBvlgEmLpnDeQD2gQEl6kzPU33/+/s0MLINzByIYg+RvOp6HaGgvsQEDFFI3Qg/va1jWGSsYsy7UiJE455mh8UCMadCuKwGiVtwGx6KlefTFk7//+f3VNqPpIOlgtPJnDoK/t/mLDsyx9iytPV2qeXdaGBPOXxEiUDdLeAwY4knzXzLPgoVnluUbnQ7UtWtA5NJhtUQ2917//urVw51PNBhTV601kHXmya0aIMRDMIqAegfOfw8E3p73AcOnkwmz2r5K2ECyCJPxeAjjck4tRNIbDiyOnP/y55+/bQkFm5SJBb1qsywUXO34Yc3hXEKq16OfmuNgvAATzyejkWnNo08+ZkOJY8QczEA60lEwC5BqNtEckXh198mfr7887we10LqbqRfd6+nWnacjgVIGaHl+BzVUhIJtx05CoXUGM5rzFoaNYzbUuJIItiGcDdcMm2tViCCO06DgfSV/vtj8fR13tZQInYVSiYX2d4OeM65uPIyfBwwEiPrmgATLycGwZJ7MmNFkhJTrSypmRUOC4HjypO3bqpScOoPI4m4esPr4t1f5O19uriVvrVE6VCqsq27AcTS9adqPA9jK0Bgi0nC6IYOIxsvCAGfMJmwyAQp4GJRyhPbHImVWgMkjRJ5z8mY8A4sZSYUwLXudB5mFmf/96uvXf/20XQcdTaaKMJJnR2jvUj28UKNsjDdKWZYhzXseqbOlg4EmkIlshMVYkrJOo1wypyBLEfl1AJj1rQYq7EhlQDC6CIS2MOlHr/96/XEl/F2hWuVtZ/N3nP8cCK664wY02IpYDE+/C3iYlocWezZyYiaQUs285mvmFF+PWcpQFJyr1SZgcRxP+eIYchSICkWipT1V56//eictFhxqCDB8qAOIzpyZp4GIx9H4RAvVVWhYfCPofV+A4O46vn48DHyhaFItGbMzFYI5XccwJs950Khslo6heaBrtS5VJrgIcp0SS5r99nGchsanQsgkb7RcEIzwRygBORgrovE3NrIGgbyxl2rnTv89oRc7p8GmpjaZo2J8I+ChmrAZs0o4izVH0RfiekZVNOZ8+/26UaUpIC0tTL6O0bkLDmXzqGpkEEgdnI2dvdmzf8kxQF1Ls0VDATp/09E/dUy+PWdJAgRvTkAn6JxNKOUszyQL60Am1A2AyjmbEdg4zzJtX4Xk3+SFzmWhgrXIghxM8bQIACW1gZFlZfqAAg1EGC3SYB7oYGi7G5uyowDU1s3S5hlo+ixINTbSzPsCGJygnDk2Wjwvyzsxwt+Y0OUN3pDye8kJ5TpfB5uch0e9/ua+KNAhglmcNUGccimp/otmzUmWpFVi7XFX6rZu/Dc59mF4uvPGnqD9XoAGfQ7zLI59yUzGfBrH8+4XDL+CIxnflsFonWV3o0mQW5Y4WWXwKdPhOs4rvdYy+OCr4vhSk3n5N3AGR2lG9b+SMReZpbE8Fkn2rXFfTnXOWKFkuuNz6dQBDesGJkxZ25zpMU7SfD5iHQwhsumIbe83PJ0hz9bZhE05LNL3iiVIqdtjfVOLmPHqLufqsUjyKlzpmIZsJZkstwE47u48DEQ9zSWasCytwi7bHM35V9QDGNB0uUYbzjxjc5tlVmyqdfsPjBlCBuuSogxyIGaCTIE7E17SlwH1NtdFEVJnaPBfXx8bfa1/vEuJhUlZvyh4zOHf4Ird9rTzHt2bBZw51AVCRWphPI+967hvpegH8oASzeNMWC6tMeM5iwkmmYyy6KZJR0yLERrbdUyc9/siV5vNfPZtzqAs1HpdVFEhinqW0fmqTi9K0whEblBy2U1q2LAmAYebBkjDYcBKQbYTXGL1Bullv0fDmcmHo5jZmmmV5iNAIJ1YPKVf3s3zShU1i7Pr0XTCc0bc65eP+1Ld3Ko6nNNwvdnUKqvqTC6KmIl8e9qdNmLM0d9ECKLYfs3Qf90OY3zdSJdqPRiFZduaUm0jcCxLx2EfyJoPUTbjCeTOnAytl345ggeQ1rfb4Nl6Pc1oipulDLzNz4AT39w0q++rTNf3+18KHcQ6YcFdoHb7F7tSMkbJNpOFSOmyKvupNnyv1iXaYBxQ+JhLjk010Pjr59K/IrBlw0bPQDBBDxszGETCf9j4GSqDjeKM6iXe3G454/mKJ89GccKzZvXw8vG+yoK7LL8tm/v9qs4TjWpCe5ht6tV9LVKW8mAa0/Uggy/iQgcWhbs3bI5H9pqa69HeG2x/KbCnZ2dOtTxPNfYWwYywIYAlcbpmPJ/ytURruNbbelPc5Ghr07QosxFqeXN4fHxZrzfb8sX+4XFXFrnmIzafzpFtm/rmxY0excaUeB7jGJrBFlVIRLB0ONAZzBkPnOm/J/QiIAgwjqbtA6CxDgGm4xg94+zZfI4GfK0xXaZxtt7cZZkQIUri7i6/OYj5VO//+HW/nl0L6rciq9c/Zhw5yq+nmAv4i8P9Js+rbbndlqtyhf85sJCBzjOZxrGlMcH1NI/jZxt5Vjf6jIasMSxu6kTsvR7oYKDpV//ybcYsjhthGB6wYkVGSaTRTHDi2cCYYSEKIYKDmLk1uNx+m2sELtW7lBuzzWJQY3f+Dz7Y3n/BcK622TLuAlvdD8j7jWxp7YS8lnS35g70+P3mnW/G2g+aRV7u+V7xi9Teh5JzbbW1emryqsq6vXHf18vDa1FxpEPHTno+84aqq3VHdDMV9ma7t8Osei6m+D8SA97K7LEoRvhm91EqDzec52d7SA6F7JEYNfLcDNTevN+EOncDt8Rzez11nnGcMxELo5gDhn2tRMTsd1xqi0rPAXE/mVRamo+Zjn3EXsGy/bLIvsu6MaKii4GvvT4GWqcs88fZYrmbvMwzWq1S7xYyjzOowzFkeo/TvLISHLWaEOCddtwAyZjTRtTFfNOqteaaxlUdTYxpJATqLPxVS51nPJK6/DR+fXmuqjusWOxsw9IUWRdX88OL0VzW7fXm2WMs8xgvxqvlfvm6McwH4W1HGetWpe6iZNin7RDbbbYOL571aCqt0ff/8c3UmYisqSOgbW9OQUClxlI3XkJ0Ap2P09yLmK52i/H0e5vrMZ7Z0ro3sqxzk2eS794m+NhDhrjwurzIfV0tFrv1ej6uLECox0aJTnu6Xf8IBY8A43T51UGry1AjHADh0sI07pz7ToJpPHJR8PgTnEmkiKIoRX+SFT3fK6uiWI0fs2I6nT0W1B+aquId3gmT2XFneAd1xYRG1/W6XO+Xi1lpTZpGPjspwQZIzuOGBSgMMA1OoyuU677z6lGVppGmQoNO3Q2dZyGlWfgrj6CsyEcbXVR53N0td6vJbPP6mE97t+OqNFZx9DWvxwlVUDGfjcfTzepwWD73vDD2hMcwvYICctfs9mVa08SKak0I/C10rWddatfFdj1/kkDT8jTrnAanBWrNAYz547cbW46Krq7GhYkiPK11r00xWb2M7xBLRdnpfuKo/tVhZjpaKsFjNtsft7lCysWusY5ZLJgTbYiobr/XNVmGjrOgeuvnklGt1eFMQFfmwHeuahgp5RmmpXFqaerZxg2eGusdTPj7bz59hOXdxz7mms1usdFchHjnU2W0F9uyyGLV3R3nBTItgvhiv8gYTbSxlLFbKnwQCaGEkiWGM5DxOOvhs6cY6GZgIRjoqtbaSjvbAxpDLBetGo73jfSp9yRrarUw0Z9VECRRsZ/k1trR1vbrO5b2rsiNiLXOuM33y8p2Ff8QR1H+17KbeLFQlGmxkSymYU7DP5aKeWVHacGlknANcgHd0pCEuIKBOD9v3FzFGg6Sg7l4TBoGtTQnseDmaxDfvW5ntmts96HnJwFYvMze5p2+0EZ9HOnFYTctOUBQ/7P9Bq4B2LVAaaKs5nE3FlxwzhTOWHdNaUCXcEFZERJLCwOJa2/aOZQTkJtDYdApCfQ1C2jaDWmcDQ0bJKWeL0aGPtxBNfY1C1Ke3TCzsSY0ZVzhEe63/Z1Gq0MqDyUt5wBGIm+EZ6XGAGdwKuOJZ0vuNkT6SoFddhgpPMmx4HwH0wKBhV/4Q87oX8400DXP+301RBsT+e7pzmIEDxXLt55AwywzbScPePh8vtwfjujFNpj5YAb043ISYLQkPHYsqe8JxLKASySd94qSM7ffK1BnaLiLWBrmdKbBQTT/m9I1i8GcozVV2rftTlQjRADVW0sDmNfFthpl/QgM4+USSbx5mU/WxzdA4FiuKs6igN14fgKaYLykdgUYCm9TAij8gHj+qlF26nUymjwXpdW4BwSfzIpPveGHm7CFad1pUXA0qocOcqCNaedMC0M8Fi8IIKc0IBggsYwZY2I5LeZL/AbJdHx4I47daqNllbFAcGBGsZtYo6A83gYqgSMgkFyaTEWAEeFwOYELc2CIPjro/H5TbcpMKwo5RIVkJFC0MALHlTW8DgInx1SzUKE5XRcaRCgOphYLbBbjV3wOgICOONfz+Xb93JUlxS7uQ6kojIIg8tTssLIcb0ZUaT5D5TmZfvx5PXvaPRVaeGjsZZ7d0haGUlLxTua9N4aATjEwFKIfC2J5D1SjOHeMIRjiad25xoE5LQ27XS2XR+I4HPZoUN7+fjseQIYfF8/W456yhd1simK82x/eJlxyBDEKTNCgYVnWs+X4fjb/UhnPo2DTNutohanSjQ4BB5AMDoY5OXdAggPCtbYG4Or9wOGyd7aGeNpnVbKrKHA5fX4Ygh0wPKC3ZZV5wyDKn4778WS13+F9wkIC0BVa79bocx5mFhiY9CNmx9vFw3h0/7XMZRr4Q4ZokzHzMPLj+LTdc/HlVYjXyZ6m1KChUig55bVTDr0gaU40NAQvWd4tDNoqu2V7/DrZ6ulu9LTer60sRsXDfPnZokfxZsuXWVFt8lxSi5XCDtYtqsnn9eqhtLP55Aumng5X2kMIK/NrXO8hKstVHPqtLmBOk46LtH495YghavPUrDl7aJFDRFK2y+keBs51RLvT4fx86nCYsp/m2+2X+eR+dTxMtIiCQVh8nnSiJCqfimgQoN+XGDMR/EgCCDPJbH1YzPKyLIq8zAtbZEYqsKS0v6vipN28bvZ5r9S6A1GwAYo4FMeVIwma/WhIOmt6YIEun4NoU7qGGQxwk/wmL+aH9ethXYgUNyxG61UvCMKHT/RTpEx0VkBn0C8MLJBxKPoR85t2wI+YVn4rqrLaG/9fLCEElqvuRp0jjZ+DQPakkYZ42k7tEgfL6ZMYAhdLzViFoiz04jgfjR6expj8FuuxDaP89xg06QnmlF5p6Cc8l/00JQ5nAGCcbnRY/02wd19gseSSJDz3avWoqUneJRpIanFJqpsb3SxAL3cHMHU2MEEoIpZEMXW9sVwvR6Myr8b3T/PR5MvnudZ/3gyCNCE7AiZQZMMoCQlKZXkmMXHWzhABLjeG2jVACkVvt+40NE0SMGfOOQaGjohSQAGGvKFDEhAupg5prRuWyxA4sXzHgkHKkiSkHjdNYvP5MPNDX/1eTrvmpvz6ZbHbVaUIHEyK+2WeUENXb/jAlTIdP0xq09I05Zr+/DGNjTiNGfLnP7w599EE4zQcYtwoz0WAompzOOSQy2jQOBjtQuBMVIPg1cAMcJNJGsaBWzyn/HW58SMesrzE7Q8iiZT7Kw/gjtIK9tHHGZESH3yRz/PcS8na6IfE/upF5E8slBtBEEVzDZVcwrhSa51pBg5QnE4wndOkQ9L0atY4DQ1EIDgIZjBIaYVI9z6IWJD/tS78wcCngMJbPuPVdvvYDfPMp38QJIxSIGYBYBBvzN5YasRC81uVc264sdzmjKrsKtIanKu2k2g8h9OmwLnW6jIDDJlTzzauu7kMaCJxzhADRkPiu9wNfZbawyFLIoYb5oJFgyT4hm3/2q7m81m1qSqrbSaYym35sUQw22Ka5Tq7+b00iqUxoGhPgNbZsOdC/87nmF7QlTdOyvHAFccDGhyoNeIBDNTQgAeVBp1gBv+wbf44TwNBFF8LDDJY2PGu09gpYm3zLSA+KQWKQKKkp06fntIcACGl4R5wA+4Ah+EAvDezf0jgfbtjYxDklzczO4nExqN0ekDtWj/bT1/O7u7oWoxfbphnvo3rh0/n0/3+dPn0+dP3d5ev39++PR/nRzhk1uNpff789dpuTGiNddLugtTJ3yS6gfAXUxnTVNcz9DCwrcmwNkqi+Sd+T5U0K01NRZiqsq1t/d39uJ4wE4fX7y7It9Ytw+Lc/eWyDsNmfH2eUV0Ohu18355cMH50tm8ofkY47g5wxZkIE7YcGbJSn75JtUyjPNrV1Bn2gFmAoJHfdXho72PhlNMTzmSY9dV6Oh3c+c0eM9LuckEOvTt/DX2wBnrsBnC2rdsM6+/Pay8v/bAbN35smsebeX9anEFGHTbtgKbotDl0gGHr4yrWqFuZpMwBEiMM9/ww5RlbAGmYaXCIQBxtnqk/+AYo4UQY2zp/OT8/IsmmqjaYcf2P37/GXmopOAffmmXp2LlOP0+moazb+XVF67JAxts6o8Ph1uLlBfkAiphOH15vmkCRzgGJBXFhl55F9Efd4SCg3iDTYIzHAs39fh9huImEcabCQDNNU0Mx7Y9v3bezMSGA004NGP0sbFMzvLwPmljD7uWn18eB/7ppzYwMMDwsrWWWQWDAonjDrfpnRiswGoUGS+tmJgynG/LETNsThUr+5PIxFSnawMjXHdx4/vj4fG7kbW4aPl/GpmpEVbhbhybK7i+D95gxZ5wZ1pmuT+q6DmG7VZprZ9r/0HArSj5DHVm0rRFH54HhiQfQvuhAc0CTYapqmlDCRevH2v+e9LloME3WZO4ue7fRJ2xdcoNLzwQrOAFVRxEhZBR1hqGwZHtcAVpcyjV2gqJRJMMNWdje8N+u7yF2hHszRVViEY/Q9e3Ufngw4deaWzealt1il5YUErCFSBzBZgyhY3oRRgLNKTDXn9foC2kIFJHUHPKkLwcURWj0/ASMF2/4czjcg0VgmkZQ5Aaqjz8O0+tLCBVzTCBvaMJuqTqpMEMQleYWN0WfojFkiTAIEaYoeZN7gculQxxgcCKQuqFi4Yw+woCEi94AplIEdUB4RrOa0/dh0/EhNmkiEBGoYeQTWmMCLCFLlgJ1lFY+iYhEGJtJinKiScAqMCXd0AnY10ADEmhPe5RGI3lQMzHJQCVczTi5Nbw74jBv8PQmzxS5Gu4cYmO0eBRGQiceYVEKJ94ITeoBtzC31ZN4Sqd+wKCp9ohL+1oR7IEIkyQslXFT/dwP7/Gi0GhDIcji0ePuBkuaFgVi1BgJHXDEHPKEAHNivgUc/1xpoPnXoCxlyUBs1JQ0aeGRLkAeEnmyaPEYzJHAECLxpTv0VdW+CZdZRnxnbAeSDKFiOW2GZcExxMTBH2TtEEOVzYEvOt7ExmYyTGG4+uKGF50KijVk0TTDoiINpDRJpmlJQxgGa83IlHs5jJfgTCAIBFr8cMWWp5fWNFm9EHFdnzfSyUiCxU14Etk2wmjQ1RaHtBGgaBINQRgST5bCeCxTh03uzhjIpuBQQLV/F37fuV5PG0btaUpxa1ThIUtRRyHCFYatNjcLHOqmbNSaPH9elw5TTWCgBDMoieYbaTxg6r5PPasJAXlju6oefrr1YucNLOHvKBATUQCE8D+1JD2gVD5AsrYigYFebJlwRYUiOpSGT241R+0hUzlzEpDoIDA1BgC2oGaqtWzgzPbVuPvhK9eiyil1LjJVscSodFN4OkTBSUQFx0rCwRjc3hRPguElSc4cZcHOQDHLGKJ8FA5N0KhqrhqrqrrnLT4O+N7unGaZmocLwWLM3eBGvYYCpDAdCB4DAxeA0RuuaxalKUrjALdIOkHOswIjG4NmwqlqFaGq53Z5vcPfNQxgo4DCyJBNUiSsK5WZgBZ1hImCLdukF8Zst1y06Qbm30NnwYo0VE60AsOLIcKUlW9fts3w1lTh4Ie2UZgsopBFmKIzer1Vp60ts4Q+EITuUMrxQqJVgkKVYZI7meavhpaIhIbO0AolkgAn8Gzn6urlq77u5+C8Q0+ggrpRoPS+yY9uQHSebgRGQiBQ2MKgEBKMtAMEm1wxmQSr0BR35NQpaVZkUmpRese0qpdDXYVXo+kHyy+gHO0JbQXVGSaRTVBESTy4dJmIR2hqczKnEcaGoP7QGCC9+O/HnCscVWkDiWcoMPQj+SI/8mr7501dt3etX2zg6WLHoQ/ijMBmoCuw0g2eEqZYJOnGQD2mObFbByQYs+4FrUkcJdnKAJpIEgxLJndp5SnOaMxkOGk8btyuc0PTOIDZQ9sIzERYLAaBKxZp4UzYSlQlMB3bongX8tmDXCu9zcAhzbZ0+pQmXZxRmgKTNBgyMHciSkUsqe47L0hhdssYWEgNEkOcSS+fdkra4ZKBwEMgEGnicce6KTAUYbBEgV0NMqR58aLAcF2XjcKwDWQiAVEYoVFDkjVQ9YdTM8htGwaiaGXAC0MGiMaNNm03Rhayo6gLA144QLc9Qu9/hx6g73MojUiwVp2v4VBREoTPf0jRire7l+ewZe4cdmwoD+B8DeO5qcpgJqCWnJLUYA0KbENJbcjULTWU79oQvaGIpjAHealJmjOJJFpiveWX10/ccN7g2gU+sL2L0z+GnTmAZ+OgEW3ULglDgHFRal0B0zlMPmuQETmM82ARvhiMyUhQskdqPr1cvl+u16eDru/Debd1hAIIyWCjsTwJZxRO4jp3uWwTavWlbfVUZhAZjHBokSVmQJI/9FZm1mw+GJLRdN1+f/nz+nxgbp+DLWSlM1wlpHjqatUmf0JQIqNlkQWWspIJj6Imb2DpeyCEIaR+7EfpOIomKcJsFtZEmG2h8/Hy5fTyLaTxWqJDIvGvpJymopBNG1hkzzmjOajgDEYZGrGAIY4etl4aTV5vDmPDrEhD7Tv+7Pi8Zw5vpfZS/dlNszEYV5WGyL2hqwieQ2IZhr7/nDDoEkxf0BhMXc4Ynhjp9vBN8zUw5nBpQwXHXJKg+DdSKCQc0j9w4JFgEAwcswRiOMAQNM2ZujZOsx/iC78f25Y/vT90uzAPu6nQf81QiEKhFDh1GCLRDKDgzBKGZkS670SYjXTXG+IptA10w8A4nq+7oi4LnsblQJV6I6QZzQDCksVRejK+ZDT95E1aCEoYH1u6SCa1XdvBwws4tEyhlcIkULkY1CxC+XpAy2Fi6olekbGY5AuhShuBqXmzYTaLJ6VtaOhZqiNo1zcJVL9I6CAKa0oY60tlxVZIMBymvhRl5rYoOYxCh0Wjgz52AumUNoJAbeq5MB9E8rUwZYZQrspMMRU0xKS+guPuuDOuOPBE12RDDebeZoYmOYd6t2XudebWcNR1Zx3IfAEoQ8runjmMI1gFOZxa6ERSEFroWFZagomtWKCrC9sCplBmjCFVWJCXWU3Opix1XfIiITqTIXkVglC9d67vCO4Y48taXmW+DKzAGEgzmdS4E1xOacKa7bHyguu/aIyFvq2xoNoKUMVZg7Fh0qUvdav07zjDHLrsa9kqi7uzPm1QZc5kWnNm61niBSd5YZEcSYffalyrQKDcX6BdWZUpPQLjLKUSAXJvfP/8EIxw7m7WFizV9czE58bvjHt1KikmFDwTSuLJMNTWFEpz0qS3Edvb/G/Hp3EkyjoT0PPl++nkMA9TNWqEuyKIqi//gbJwpxt6HvbHB/sXAn3PxWPW0QUgJK8ImA9qa81YOFvuOEuUNRpHwZIjPkAihoLihFJPliLpCZIfyGE+yEOAYEgwPOqKwyiGfoREEooNNUMp9JoklLcfb28fgcGEJQvJmkjmbcxDNCotSOy//EZSh0hDj8EZGUFyvf2+3W4PwtRnDZG9/eFYsSd0vt/XND/q2VcsLQfx8S9GLV0RmURHf7v9/vn+/v7r14dg0kTXGefmSvEoYQkyA6XhywRp/iwM7S8lZ4zbNhBEUbYGDARQIldRI6iyLKsgkAVCQizTs9Fdokadah0kN8hNco0cIO/P53IA2kX8xZ0VXc3jzCzp3aXanRnmOgkOtWAICpNYhCJ0QgHCUT4MQ2W8/ZdR4agsCxg/XgLB1jo93DKJggEjkyllDMdkLodkoKskBEMCAYgfpfR9Kf8LoyjYRXtnCq9aqHbzj8kQlRDTKbFe7LlhB6KNQLTpftiUIKzgMAPp5EM6mwQVWo/ehYnrWn1/776Wz+25nOzM/8J9jokhybMn27r+iNzvoFgICvGIoYJEjSwTKzDckWNGcXqBMkDTN/NciibuH7zDivbg6VP5qV7N0maLT9pNqpm7aYZ1elOhbhnLuWBYDGP57pHBCAZMxgNlUFTfMtV/WkQlEwyQrisFlkE8TV01jmWgurVycluKrWOpz8BIWs+PpTzm5Lxwl4vDwuDIoFgiyTJpZxodmVwYowhGwiqpTJAYEwo6naFBwIhCDtpLO8q+hie/j0CLDvvsxSDk7Unqcz+FMYIkWda1RmR2QVNBjAVGqkZkrvmDwjFx4LQwXPAyHUenHpllHOqvszzbaTou91Mcdh5LFxRYmzipJFSHMQxiGCnDMoEsx68KEXKviNAFC7Xu0ddEOQgLy6VSJDhIM1h6YHSJJUegidTBIsHY8sklRxnnVOSVWF4DCApIcr2kxsUgGw7DzBA/FYhJieTAHAmMi4VmGvOEdXgYxsZhRJdxoPcvLBinkbBsycbXOQ5PiwXgfPMtWq7M5ZocVh0YVo7BeLusc+eWsRycOmBhI9MwdInjeilk1uVyGeEYhHP7M+fRy2P+VARutLttw7z1mzfE1ExhrVZArENiyaxyuS/G4WRxhTivao34G8nlWNh9jLuudCJRqqnwhSIEOrEEFq7a3VfDcLrFhW/nI2+UvWQwkAGwzqSIg/PJBHwloeqQVTF05rzCZoI5DE4qi6yChA8kgAio+k7TGAZMCC6jQFHKXzjuN6fZ1/X2seGKr6IA5PZqfziW02a1Xez4ijEqEKayyPp2INRMQJeq/mNTiogDk08qkEiuE9/jXehQKBT1Vt8VpGLBEJL79X693kbU/P7VtfvDrl078XFwU/py+75b6VSCRL7TJBWETMVYu7jxfylTmWNvLcfhgyT/gwfJ6DRq5R9XZ+ziNBiG8f4NgptuLlKObg1cDx3du/RP0KGLQ7vccluoWOEC1+E8oYNwNzSCuAimiNVrKShxaOlgKkgsGA6OFpIqtIO/5/saEJ8mX5Kver6/PO/75sPjehWkXiWpzjnQvwxMavsYBWNwjsFBhdXVajKZBEG4V+SfZrFRLDu+55Zv37mhOFULAuQbgbfyR5+NHTPEYpoUAsmG+e+CS2bBYx3IK9w+HIlZm5X8oXdhBqWCxELIyi/ZAI9kF2D3rS8p7zCmrls9hKPXe+d5heHw1XCz2oAURVESBmEYBPF166BUJJzibS0Kb94uV9M9hUVce6RAmkRhDXZeElxAygQB5CzWB4B0g0o5j2QLRILGroGhYFPDpULss6Si2Gv7cka+2PYrf1T1abWaOtWjY2Zd9yh0nNCVM0+evHoFEMOV1WoL2nWc+FCFwdNi6WHF8bLMq9RKpQpzyWSSrVaTVrhfLNp7XzLm/F8W+apRgIKBIF/6SrKAVCrv//PocIChLKQHDjGTUaYqDr+5h2mVZHLdb67NKffomPCZYENyxvcLYDyRgBqKSmfMCWqwWm2zKImInUs+6cD349XPrS6uwI38cK9EqEVMMjKB5yS7/stL1/QUwZBMVuJgZMcGhBG4cbCz4f6x77ue13O5+Ga6rstj5Pio965HQrGxq+oPw8MwPHT185dkGTCzxmw22G43V4DkMJaHK5ODmysBCw/xx3Y+rq6vM89z1f0JWCrXzNEuFxl3RCbpBCOZvqWDeNR5cUMINK3aPS3kpaofxy1+X6e586QVI4IBQaG54171qOeGyA0CH/WCwC08enR+/gg9Xi7X02l/sN38S2S23DgjJiThDOHLstgPDyqVcvFuqVazAFpfqV3lY1HOGHHFDgrZVSHFVOYVx3YpiWqn/ElmL/7stbye53k+8rADCGsITMchieW6sFRV5K54RFQQx2MGtDtANW8AtaEvYEse/h/2fyV7wIFnEqEkTNPUqfAYlwmm3msOsSl8+oCMAQuQEp23tIcr++pldDDnABgKWwcDFCa+xyctxK04/vo1Nmp5vqsa75FP794BIil+MCRPe6tQPz9/LBo2y5NLRq3XS9DW6+kMuIWpqRztj+BsNm4ajUGj0afDx5EXhNWqs08q1cIgSYLAKd8rqzNCA4lATPXc27dPl5p6F23rPr3KkUfVg9CL40k2mWjI0ER7TO0mxO57Em24RcHzSWScaIIzVKjXm83f9TpMQKHcJ3Tebq+nJ+slM5bOajbAsIWwLMuVPBzM1/1+fzDIMsMERhJHUnBAo0od41kZP2q8ePAd6Bmi+k9TGHZN1y4c/Yjwr7PtllLerrbbLBtkjc5g9n3Wj5J5Y4JRn/kMGYW/2/FFJ58LzcvLdrt9cnLCKIlKSNpk1flyWWeCyRyWKWTs6l5sB40pp9PpbDafz/u5GkaTCVvkJz6pHzg8ORynGgRV838pSOWfhoFRaFPfTYIwwWNazaLT6V5cLTZAdTuds9HrT59G41/fv38f875NPRxpxV7EwPnnn9c4g6CRGD+ao3Eq9woi+xIbOs9dZFjWlxiHOIWu0d3O+0AZLHaln6AkucUrIvspjgcig42JaE4+J7zrM6yjCH8Hm8XF4s3Fm8VwuHjT7XTPRmdfvnz4cHo2+jAajT51ttmEr4/vc75wHyPjCJqvhebvZrN9edlsYpBkYJhsN8m/+mUbshwMGin3zKLBhOxbAprjkMUZCEcmZQaIXZe6q75ZF7qBGyYiHo/Hv2bTGaHN9ffH3a44fizevF0MoTk7Oz09ffn6y4cXL148e3E6Gp11LrpjLGL/NO6Mf425ZyLrF06en7x/f9luSrCYQRhIkCAC+JfLsmlxGgqjcH+bv6IEAoJ1kdZCpOqqmzCbIJiNTB1uExLJTKxZWEpIGgXT268JTZmFhVK76ZBAIYtsXFXUgudN1IXHyU20jua555z3Dj6nPyKsJy/e/RWZRDRULwKseHY0NgCz8wYr2EQ4JNwqzeAWfEKhUa8Zknq8TW89z7J6z5/nyGu+onylSpoej0csIWOGq91t7q5bnAeBaxtMD0P97vrmBmBxD1otmI5SLWrzTifpCEhXH46cXi2rpBFCBQQAMg0RJA/L35cqyV6Qk2CnfL6iJuG0Ws1mnxG3WQ/xQOS24ALIf0AgwjNICOX8vSgKGGFhl/P8MLAs62hG56hI0zTLMt02DBhzrYmF4zgFeLgbGnbTERuuYYQQs42QxYe7WkdIOqS5jGueY+nM+4BZXi5P8IhMIv1dQfFXhAaKbZ5jN2lPV9id3Wr169u39Pzy9jiwkJ7dlgQmQqhoqnVWCsgWRSo9pzBhgMAtWOYBUYm+76NzmqVpiG2fTu/sm7qoiKKjkJwgcBTTNBXRcXiWhQag4sW0JiTJaJEkQqeDL4hgSNVEANSLUujQkt7/BZYS5PJVlT18Mp/jrWeDY1rQK90ecQqRijQcIAKr3Q5MUN6bYehRiEJvgKyhK0Cxer96MOgnkI4/geGFWTjwUiXy93slAyRD4xd3U+1Ga4mSSAx+BIzCjHzgAJAHHF0ybKbVRqNESEYkQSAegXgqIDzQEwWQ2CqhR6fT8sk7AkOlkL2ySMsE+/u9gjjT+oGICmxt+Idnm+v6IZ9vDzYvoqjIrCS23RBoFlgGum5BQPG8Y+idieXjx31xPp9DpiNibvfqqiHCCdEEpe9HkO/7gCG73Cu37rq8VtcMTYt1thhthI1A6shy5VKFlc9LbbedTl/uVDwAAkulE1QadrmyMvznt6gtcMieovheYDkOBhaIDguokx+mGldMBU22XdftUd7Q4VgPwTEgLs8L0+hjKQX/XsbY9EZril3JjEwxMEG539PlgwoeRZEq1pui2G05NVEyx49RLG80WkDTeASBajQUBLkj9wHUhwgLkvsypt+cBiCKRVFbTqDSrRNGxfxg4WWsGX4KPyI21k+EJ/OqvY91jJ5DfBhNjaDMPGcG84CyQJC+ahnPwtAiUYtg3h4waVE4bhxrN/WugnR1my2CKVUh+TBJcThXfFUa14IAJA1YpTWDMGaoz5/QdTYbWZBhFFDabTJLJigBSgRhOJljZPQnfYAQSSXwPH1ymS+Qll+951vSrlSeP0egdEsvxQzDNVyasgG3ejGsurFbklMyU2vIpDDLMOIg5Yc91ZAxvLZar6uRX5Lc32MhFlxl+PZj06+ZkqiKAS5JlRzJbOrx9SjZ3I1oMGySBO4ASR7K5JAsEBJYKjB8AJhnExKwIJmWJTL3Kl/FehUbBKwaaiDardD/xWGhxyGbxrbGuMuQcgdFFsXw0CMNPIzjCmiA7w8DJfNwWjZQeOlKjPx/MOTQj/X79Toy0bD1j7Vfk7rq2h+bIlC4aKoi5LjXLF6wA6Y2pgMAAAaKvpxQzGTgQe0+sZFRidxvAwU8YJLlsnST5QlTbnXQkSicFcdB74AzBFBYDkQU8oy5TVczyCWUGn3gHOOgShksLC3yerMZ6OgR/XaaLEBRShaI1vUat5JvvV/va48eiipBgAYcUnnrPlDVsSoyezqdggUkcn8ot4cCjCLhsfRLBmWyqcaGPCQkGbT4hoRqd3kJotM8NoJzoShBkDGd6TGM6aE7OmNGICLsrq1pQdBsjs2GpLie7mGuIR3XLPRgTY/IgYd2gVpf6TyIInSfYEjv338iLLrW63WtVeddro7N8VhV1cfqeCxJAEOPgIUuNeyyRWjN5AI07fbFZHIBTdpV0JDA4RtZ2Mjtdh+/qGJt2IU7QWETTsjcfGEEhRLRFC2CjF43Zgw5CgOe8pA3cVCwbhNneuQrjmvYtmbbHGXmGHdGyPHouhi3iKeu44ziirJ/f3//6dM9raUICzBXrUazKXUfPJDo5btqo6Gaqolbq+E4ksgbD7pX9ZZ2vUiEzWYItdvDYcWD9dmXL1/wOHzTfvv6gtTuXxDmkOBkxJR4KHPLecK4qJh0QiiYUJxndHIzXR8YGeQCSLMROFNqtaTGVVMaPx7jS+p2G+oPCJT4yzhQwRP8JrpsXpwGwjDeP8ujjFJcP8IGHBiEyqQzETJDi4eCCOlh2RwCC7kINtQ2SzdJIc1hWXRb7UFbEPwo9bBeqgcFD0VEEfQgFhSfWQXf4qTGHuY3z/M+79jvz4a140VRvJ++fQsW82daLCrUDR3tU16rSSKR1bWaOwGMr3yYblgbS0L6I+mFPpjybP7O7O8Qclw7RMgdNkyjQC5QHOKt0Q0atlq7uyC92TBxeIJDOGk8eYLEu/eyN6oZl+x83sEdqzb+gN6ESL3uB7z4vOPjrjWWiF9PYquTcIEDXxyjyWGmxSas4XRD3UNjIR+GOo6V9txNARaU4alIz/UkhTKsy+JUU9+PfNeVciY9aORBs9pspKGZBwV9mCEe5dnToyy7doRGR5Mg5PBswIhGOvSR6Z/OAHCHYDTePMH1fYkmOmyA/ObLHoLGEO3Dy7M+QEajMQb5Y0i2441HaI5huLP/5v2C1DfuAhb6Z6UCXbHZuHrUvQseuNP3eRKjdAgig1NRZES0UipmisWxQ/tVqYlTLz3o5IdYobfrEsimZ1r7Nd1XYhRHvoxl/+lNiPCk9ePJj9YuxGi1fhjztdBPjd070AvK/a1DEC2XUAicDQC+fNof7mPa4Y5lJt4Q6xg4hmf8ARNoWNt5XCzK9pXvdmE6YorNogxRGYZe1H1mgqH3EKfgEy7imPnY5qYiDAqVHhU2ZczS3HV03SVkiNhMu6likSJeTTL8hniwnq+1Mh/Aaan7T4+wR2yvM+jstoCEuvOve+A30JzygABL5+AEEr0+GDTutHYb156mKjx+gwJRuDP8C7OPnAAL0k+TTWFvnd08mrr1dmFYTnkWZTkJtYY73r37CJ4PM9+rnymJZSlREZISrQlJdFl3QknqISEOUY7nS5+pSKm+8jxCCCVUSKSbxntophRXI+3jw5SPXjppnJjOAQ8KD5T5C8CAcWcwgFCdTgcCYUXBgyYxrh2lwjTRo8WxuQCjfn0+fgOcca2mmJZ+vdybTq/8vGJgII5ZisVmr9wLg1WeZb1375710tGMo03qrl3JRWpfLzwS0fDsqbmIV594rusQKSPha2Jr4sJsEgqiN6O064f96NR20ocVBZcSPacjls/NLchEHtTA9k3WgcdodarXANYzYJ3XR0fGcweAGgwOD0+W3VlobsAw3X7N2O/4VJ2ajvpsRCbtre9nijZSGCSG5v2iQDlJkAsevcB/np91U7ioH4tKu97n7t6V9W2L5bpsb+qeJ120iVboeYKo8yN9On6oZFQpzSKhVRxFksJrICcmPBQjPvdFnLBmlpkUb6BjWoABDRazGsnu7A4AdQe6QKKDg4PXrw8OYL4OLLjMYl3zwGOG0T7shrSbfej3u7j0BFf2yq3yzaPpZlo4m7dvi/a5y+2rJYsFaY8hzgvog9GbRxVpl24YPPj9cyvuCeojl4kgjkPQ/TCXW7oet/F0LMpZzOE4Do/xbi4iJUAnJYzGEIKcaS6ECLqMMMXzVYazX+4CAyz/6v/Xv0oNwIGPYeqg8Pt5rjT06OPqAsvN7ve6aYojZ5Js2cePFpvptIThNp+2L+0VxQSpW7Zd3X2G9sFATaNKzmg5WX358vt3TAW3CLW2BbGqdj3migIGk4sQ7WkO1/Uj5W7K0AEmo5TCqQr6MZ7GtifiPKfcUswiXNlcsICxJJjDeI3d/zz/sUBp7GeQOsCBRgdAAtlynkUYfOPa8fHOrPcUcyiKFEW7hu5kU5hwm7gXzyESFvuL0sWjtFn+7Cl++KyytVV1rWC9+jZPbemdteJ3qyCfZzn8kjM0i825g0kEIznctyQMp6kN+0kxiqAEU0hykaUUL4VlUgLRwBy4L0I2UgHjNYMgm8+PYKhOB9s3AIYChS+Df2VYAAQcw/Zl1eTSw/3RG6XpPEuZz0Yj7U58DAocb1G2r55/+2bxdhEWF4qirCZJnqa9XuXGluNuNefrW5Z11haJsLPgy/Lr0WoeYA9JmjiOY5dth0E1uC+syYlkoVsnmiP1bOxeaZoEQjNBLRYzLrgiRFD4lFIiRMJZlIgkScSn5vf1EkGOrm+AAvUf5y8DcFB4mDr5EsgJWn2xP/RHMcTR3MfUj9IshRfPti+cL8z1H9nmxUFwe52vV/NK9ezeuerzJFvlfOOmc7FHP335eufr169ZFjRTYZ1RecAndfjKcQmnlhXa9qT0FK8j/RwKIO1xyyFCkm3ogMGb8oRbAm+F5STM5oGwBCfVajWJm0nSzFdzM2w6BuGUyUhlYLAYngcAeoAFWK9fBWSyt1ggZM1g1FzFKQyT5oyjr8Oy3ODfHArpPzXX6/WqEjdv25eubH/5luS+SxkjzfWrrzivrzdfLl++C27H2VGGPRDb1tqFexBdlmUjEFz3bEgTUrfdkpCJU68TmwpgxDGNIVLVjfANr0XMAVu3qsRyrO0k2a7evr293fzDg9m8OE/Ecbx/Vi6F6QtDJ+lMZ2gCiYWQTExgV5wVFGLA1IOJLAS3ggWLQuBxt4fHHuSRhQUvsqgYlO3FS/EgnnIolEJ7kHpY/KWP+rv0bbvkM9+XX9vjadfszwammdc6QaF/3sh0D9MANWzrmqEU0tFdgCbQlbePSxuQstulsrvguQccZ5miScyC1t2GI1IfnuOhx77uWrbI1s/7F+/t90+bH758vHt89c6XT3fw/subm8XXF8i+eQtK7BqqW4RdHx7510UYQE+EIZSHFNAacCLoAV1YwrKYaGJTWp4H5RIkQvqC6JhyjK0yUSVN1Am65wm6Gphez5kIMgTqrNdAtL7/7av7XR34K9TtXl98AJ/fbz0Pdhpw2eIhvJZCWlnGK0pk60gTmlT1IUdmet3Fmaif988wYLS394+v9vu/9tAvy+XNF9BP3Ut18+ZbTb59hMLu9YMML+RK4tX1heiez9++vA5BD18EnHhdZiuvpAGGBac4L4GNxxTDMKywwB5mXrKlHE62brr8c1ihsGT/67izLuvdGqBApvXmhDVz9PLhrdtbSAmoBFfFwgtmCVHCf88S2jKHRlzVVUykFprpMKnX68P6cNrtX+xhPvsIDPfinVdfPJaqzCxmaQ9ycQPmWQhU/H0hpIB0SIxDSI9chdKHUmPhg88CLHwsV15CCPVYlkE9Jom3oCQoOcdC4AQbqU4EZoxSpjiYr0y2dbZ5bKAgQCDPN5Clptvud7vD4cwEF8ZREcLF3yzKQGV3myzwYElA5Wyz5bFluh9rx01C755eLcV0SvbPhyqKqsP9Ho4H/Pb57vDVT5/8sll4PFvaC3hn8270QVf4D1LqXbhkX0JGbPmwwvBBSDIZSlFyqArg1QRTCfwFqTIwQKISHnDA4lT4XApaMqxpwIWpIhQsUsXwWrXdHk/HzW5zhDY/czVAwAI89W59OBzq0/ZYK6+pz+XSflNQ7IHoqm65pgglWAB+innMyuT04rl2PjWFWj59D6LsP1vHef309hP18WolrCzw/dBjls5YwC0Py0v5gZAyZMrzvACCfw2vCpEw7GUlYwbDSCQ2QnTOq6q0mc5LytUyowbOsNFjlGkInglUkhGUEJg55VsOXJxXyTI71cfTend//9V5sQLT/0mCHDHmeZeXTEi9WIW/prhu9aRfjAqUsowZGOfHx8dymA7c1Khf7T97b/9cCwYybRTXQF4mNdMdFNiGsuU6ArvZ4CxrUd4penurAu51A6z7AusWPB0KLdd7vtCw5hvIVlTTfGzoZ2l4qZTKqUqUwhEOGOYGyxKYbUW3lFKDxnGVzCmNI17F/Hg8Htb3573aUAENzOF0OpXQj564gA/H8oG1CtN1RwN3MFqZIzftaZZ+Aft1NBpWTW6e97uFZ2zWuw0tzF6xSlO3P0gFm/oG8wuEijQtQuRxWyVLpZqduwQ/bTkRvrbydU2TtphCq6yQNLZUx7JXFHDDKcFKbaGjk5KiOKFBgknCCeE8bljgJp7P50ZuxNGM5NEsn8d5XG1P23oHjQ00MGfnHTabzalUdXZZepet4WAw6Pf77f4oXZnudBimI9d0X45MTSTLzdPTYaNUBvlThl6hdJUW6UsXvlGEK6RJrSggH75ECGHd49TqIg2rBELAGWaCK1n4qFj1esOwy6WRS8RsQYiGCVWUU2wI7COD6IRnpNdUgsqSOI4NQkl1jGHmOa2qyGigYieKjQgI4+Oxhoq6P0+DAwIdYbbszda3g3a7AzDt9uAlzKA9GA3gbjF0zbTQeBVxxbdVfToSlRATdDHNFLmFjYQWyiL1gSgtfMvooQRZCjPGOIWqJZhSfTgqLOZj1Bv20unw/eHItBkiWBeGRXrg174pkUGZbuCZk2POIU16lWxVQqnaVnMaz+M4ieczZ5yPHSeaT8azBnAeRXFUnerDocnPOUc7ECizWwO33blqpLnqtPvfvTQbrn5/MACrDUauOx2jgALLKdv8rEgKgHYJLdWTQ3AZVAf2DSMVK18wzgktsUTCoHBXD2zCCjP1KeGUI4p0zrVerwcn4fmrqemO+m0Y1zU1LHWaE2JgQqiuGSRWxwob89ioVLKdKzKLZnoczfPZPB9HgDcZNzAOcDlxdTyddv/S1Let0eDPUSNLv9PpgCpFarYbnPaooYEB9/W2B+jKzHN6mubdbX4JCt3u9XSiGBHWYnkHXSCGBtd0HAhCGcYCg41YITDSCgT+S0ANHsgiH04NqkM2p42VXQhmbzQyi2JKYgPpBkHacOo4JM/jsTPLh47D+aQEY1VxVcXOZDZ/38nzyBlHswmINB5PnFk0duJ5VTUq7Xat9rfDb686nQEQdTr95upBmM7r+6+pXLOaV0dYe+Pp14tb+MJ7Z203QZVlNz7SHd3+eZmpRwMvywxLkUDpJoaWwjlLYvlMHw7dHjIFGvc0xIlG9cLt94epOZlO3dGHcHjANjJz6KAPXdc1JkZjSt3InfEknzjjT6P5rNrO420cj+cVGGwWOdFsNga53o9mzhloNhnDY1Cp9enkz87V1dWoYQCC3/tQBwABfFedfgMIqpkTh2KVd9Iff3j3Ddhdp/WrPzaPT/+0YO2sUsRQ+P6sNEKWLMtOknFWEsEQGHzggI0iFpMFN2lMmikmxQhiYaMiYmVKGxEVBLGxEqupVGyt7Pyizu6dx50d7vnyPc65y3v8VXj/2oN3b0DOzUeH488jo4fj4cpV3PGM4+aOMwGPUWEE3UGA7UCYlxi46wcqMbrquatSOHlPFLGMMCKIQKFcGTK7bJxT7QKFYVvmcR6HcTl+ACfGWOCy4Ag7M5szi6LxLnuUjmOF8hVYvt/C7ly3vYUjSNIvTjRszunDpy8Iwzvv799+9voz78mVG1cphuTjkyfnX+zaXdsg2UiDcN09vcTRm8RVLhSjEOV5YQnc3hDBGyEEoRe5pBy6A5xp0jJ7DdUxxdR83CH/OFsUc9k5J51SoMIMHwYXTBygqr8MwTb1Pau6jwrb2b5COQcQ33FyDrap4voLq7sFlja4BxH2ndyRAn1EckBeP3739sfLN+eBcdK6uUg98f2LFzvUKLGoUwtY1whpjQa284I1nJ4/7ECWaKnW3seGAljklBMqPSr3kmBpU08o52IRzGsmlRHOO+MGC4PYrKKZbQ5JAce8DAohjaMBDmw4yWqezzr4ou8nhk4DKKi8IthiXyFVeFVoyDZ27zxsK+HanpJuuonpEdKaruv+5lGUcwB8uiJ7pDlKFqgSiUAlOc9PE5wdzVE0JDaMnSTd0VbQhiOQo+D8Kh8UQ7MGY3CVT3ry5dw+SB+tNVIG6Mwla5N1wZqcE+C55UO2gxoNvG/UUtEYo0ATBk124S56PjD9Kx44gKqrltnCTft9vXG6Ynku53qma0bAsoxoyZo3HNfkyhTw5FZPcPFErjSCI5aCJ/w65UPTcCYp9tITtHTCl4FQHAVF6kuJUFRxUITMCwjSodtrH4q3veZRkdIF6ZzNcDjg4AcWgYEGC/0pa2tCK9CywFADEuAMo4lHcznta5/pthBZjbFqln+KQz4D4PTw6RCdhGPrsAAqT5zR5XjpChYBj5EexoL493vJ259KT+zC5PtECL3JPUJKMxKJvEAOnDNOGAUJY2RRyoZ4kC69Uj5l3deXN9Yym2WSLKdgZo5ChZ3XNfHkFgV2TF7NApqADnx8gOwGO1TFnXkqwxazDBa8EgFIQNNhEoBupvq1Qee7fWHi5683P4+i/0fgth8hMio7QMVD+ChwgDF9QnohmS8Q6EZSAst4GhteR5aImDrgnCjR2BPfycQPWfZegp7eW3intxILwg7GedWSEgJYQJ1FyADFpRDSkpwKONmsJVjrbMjZzdHOQ1KLcfNZAAnner8HJ8iw798rnv3zj4CDDko7eJPr6xcOPx99+bbkDAOBGhikGRXTQHyrUnJu0yNgqzi7F6ScdjdbV2BrrQk9sWa04t5PIkc08GsXlJemAmUxRi63+54x70JPzcAyHpGCS25wtCHJpLLMxqa02YSEsu2QNyAoJLeum1JyKikAFO7kejfPZ/q0fr+1hm0P+VQwFQ+Y2uOFTV8144404rg0zZjTWp1U0WxPhwinyw75PfVgcSKR9fs6neitU7LJhBDIiMbWGLo7HmDwsaU14RrBJka89F5CiArXhrHEGktYYE1s4EamKDEKsVzW4NaSUsGGGJChQ/lOuU3JuMRpWYua05pSAhh3BoGVfkWj2UD9t/7CqSlQpYZX36vjzUGBURLCCtaA5t9cgG8Q3t0jfYcRCI9tOqzmLsrUUCObtNGManLzQMXNdiS19SyUj3wi4tAcOWPIg5OuLXdQIkotG2FdgsTko52QvifSKDtEVByCs6rIslZMDpdo/BkyS8CS0nprBYawlryuK8g7O9dLqbf/zAIUFcwGNkCNHWZouJqMI+yWZFiBFGC+V26ARqkL937b/tR1G/waXHmsJkMcQwYSCdvLQ4w741UonYdVTGzbYVCqbZThhnvVGLF8GImETaxCEPvKI2/8vpPUexJzSRYCmssGYiqbknK4VbJKuCgwFDAhH1xYXao2KmvOZ4lNHoFVwYCQ2vpvbVF0rXj/Ck7a63tfll8jcYxV0irq//8B9dvSfPvZQjYdmhGMd9JwcUZ6kei7si9S1NmEMtf56AAxtsvSita0IqodM1y5uEQg13GO7Q4aRNrmxhtjGZxlASHV8LJhBY4uFKRYWoGiOJsy6gdVZHOrhDWoinEezg701PWgBk4p6wp2UG0F879/YsqBKMgFVsiVf4aqYOr8XvmDfa+2v3mBiyqX/Sn4y1eSb44N8x1heu2xwvzQcMe8BRPDaBxB1dxSO5q+Wj6j9mWMWWc6f1iiMxAf19mgrSuVoSXyMOWaaylZq47wvq3wLFbHDDUJQgWT5oT28wenrEK/QzXSxwAAAABJRU5ErkJggg==\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Temperature card with background\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "temperature", + "weather", + "environment" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/temperature_chart_card.json b/application/src/main/data/json/system/widget_types/temperature_chart_card.json new file mode 100644 index 0000000000..235a24eb15 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/temperature_chart_card.json @@ -0,0 +1,31 @@ +{ + "fqn": "temperature_chart_card", + "name": "Temperature chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a temperature data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'temperature', label: 'Temperature', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: '°C', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'temperature', '°C', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"°C\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#234CC7\"},{\"from\":-20,\"to\":0,\"color\":\"#305AD7\"},{\"from\":0,\"to\":10,\"color\":\"#7191EF\"},{\"from\":10,\"to\":20,\"color\":\"#FFA600\"},{\"from\":20,\"to\":30,\"color\":\"#F36900\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"°C\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"°C\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Temperature\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"device_thermostat\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "temperature", + "weather", + "environment" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/temperature_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/temperature_chart_card_with_background.json new file mode 100644 index 0000000000..613a9d71f1 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/temperature_chart_card_with_background.json @@ -0,0 +1,31 @@ +{ + "fqn": "temperature_chart_card_with_background", + "name": "Temperature chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a temperature data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'temperature', label: 'Temperature', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: '°C', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'temperature', '°C', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"°C\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":-20,\"color\":\"#224AC2\"},{\"from\":-20,\"to\":0,\"color\":\"#2B54CE\"},{\"from\":0,\"to\":10,\"color\":\"#6083EC\"},{\"from\":10,\"to\":20,\"color\":\"#F89E0D\"},{\"from\":20,\"to\":30,\"color\":\"#F77410\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"°C\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"°C\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Temperature\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"device_thermostat\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "temperature", + "weather", + "environment" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/temperature_gauge.json b/application/src/main/data/json/system/widget_types/temperature_gauge.json new file mode 100644 index 0000000000..09514b2e16 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/temperature_gauge.json @@ -0,0 +1,29 @@ +{ + "fqn": "temperature_gauge", + "name": "Temperature gauge", + "deprecated": false, + "image": "tb-image:VGVtcGVyYXR1cmVfZ2F1Z2Uuc3Zn:VGVtcGVyYXR1cmVfZ2F1Z2Uuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxnIGZpbHRlcj0idXJsKCNhKSI+PHJlY3Qgd2lkdGg9IjE4OCIgaGVpZ2h0PSIxNDgiIHg9IjYiIHk9IjYiIGZpbGw9IiNmZmYiIGZpbGwtb3BhY2l0eT0iLjgiIHJ4PSI0Ii8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjkiIGQ9Ik0yMS4xIDE1djEwaC0xLjdWMTVoMS43Wm0zLjEgMHYxLjRoLTcuOVYxNWg4Wm00IDEwLjFjLS41IDAtMSAwLTEuNC0uMmEzLjMgMy4zIDAgMCAxLTEuOS0yIDQgNCAwIDAgMS0uMi0xLjN2LS4zYzAtLjYgMC0xLjEuMi0xLjZzLjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEuMS0uOGwxLjMtLjJjLjYgMCAxIDAgMS40LjJsMSAuOGMuMy4zLjUuNy42IDEuMWwuMiAxLjV2LjdoLTUuN3YtMS4yaDR2LS4xbC0uMS0uOS0uNS0uNi0uOS0uMmMtLjIgMC0uNSAwLS43LjItLjIgMC0uNC4yLS41LjVsLS40Ljd2Mi4zbC41LjcuNi41LjguMWEyLjMgMi4zIDAgMCAwIDEuOS0uOWwuOS45LS43LjZjLS4yLjItLjUuNC0uOS41LS40LjItLjguMi0xLjMuMlptNi4yLTZWMjVoLTEuN3YtNy40aDEuNnYxLjVabS0uMyAyaC0uNmwuMi0xLjUuNi0xLjFjLjItLjMuNi0uNiAxLS44YTMgMyAwIDAgMSAxLjItLjJsMSAuMS42LjVjLjMuMi40LjUuNS44bC4yIDEuMlYyNWgtMS42di00LjhsLS4yLS44YTEgMSAwIDAgMC0uNS0uNWwtLjctLjFjLS4zIDAtLjUgMC0uOC4yLS4yIDAtLjQuMy0uNS41bC0uMy43di44Wm00LjYtLjUtLjguMmMwLS41IDAtMSAuMi0xLjMuMS0uNC4zLS44LjYtMWEyLjYgMi42IDAgMCAxIDIuMS0xbDEgLjEuOC41Yy4yLjIuNC41LjUuOWwuMSAxLjJWMjVoLTEuNnYtNC44YzAtLjQgMC0uNy0uMi0uOWEuOS45IDAgMCAwLS40LS40bC0uNy0uMS0uNy4xLS41LjQtLjMuNi0uMS43Wm04LjEtMS42djguOGgtMS42VjE3LjZoMS41bC4xIDEuNFptNC44IDIuMnYuMmwtLjEgMS41LS42IDEuMmMtLjIuMy0uNS42LS45LjhsLTEuMi4yYy0uNSAwLS45IDAtMS4yLS4yLS40LS4yLS43LS40LS45LS43YTQgNCAwIDAgMS0uNi0xLjJsLS4zLTEuNHYtLjVjMC0uNi4yLTEgLjMtMS41bC42LTEuMWEyLjUgMi41IDAgMCAxIDItMWMuNSAwIDEgMCAxLjMuMmwxIC44LjUgMS4yLjEgMS41Wm0tMS42LjJ2LS4ybC0uMS0xLS4zLS43YTEuNSAxLjUgMCAwIDAtMS4zLS43bC0uNy4xLS42LjRhMiAyIDAgMCAwLS4zLjdsLS4yLjdWMjJsLjMgMSAuNi42IDEgLjJjLjIgMCAuNSAwIC43LS4yLjIgMCAuNC0uMy41LS41bC4zLS44LjEtMVptNi42IDMuN2MtLjYgMC0xIDAtMS41LS4yYTMuMyAzLjMgMCAwIDEtMS44LTIgNCA0IDAgMCAxLS4zLTEuM3YtLjNjMC0uNiAwLTEuMS4zLTEuNi4xLS41LjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEtLjhsMS40LS4yYy41IDAgMSAwIDEuMy4yYTMuMSAzLjEgMCAwIDEgMS42IDEuOWMuMi41LjIgMSAuMiAxLjV2LjdoLTUuN3YtMS4yaDR2LTFsLS42LS42LS44LS4yYy0uMyAwLS42IDAtLjguMi0uMiAwLS40LjItLjUuNS0uMS4yLS4zLjQtLjMuN2wtLjEgMXYxLjNsLjUuNy42LjUuOS4xYTIuMyAyLjMgMCAwIDAgMS44LS45bC45LjktLjYuNi0xIC41Yy0uMy4yLS44LjItMS4yLjJabTYuMS02VjI1aC0xLjZ2LTcuNGgxLjVsLjEgMS40Wm0yLjMtMS41VjE5YTMuMiAzLjIgMCAwIDAtMS41IDAgMS40IDEuNCAwIDAgMC0uOCAxbC0uMi42aC0uM2wuMS0xLjJjMC0uNC4yLS44LjQtMSAuMi0uNC40LS42LjctLjhhMS45IDEuOSAwIDAgMSAxLjMtLjJoLjNabTUuMiA2VjIwYzAtLjMgMC0uNS0uMi0uN2ExIDEgMCAwIDAtLjQtLjRsLS43LS4yLS43LjEtLjUuNC0uMS41aC0xLjdjMC0uMyAwLS41LjItLjhsLjYtLjdhMyAzIDAgMCAxIDEtLjUgNCA0IDAgMCAxIDEuMy0uMmMuNSAwIDEgMCAxLjQuMi41LjIuOC41IDEgLjkuMy40LjQuOC40IDEuNHYzLjNsLjEuOS4yLjd2LjFoLTEuN2EzIDMgMCAwIDEtLjItLjd2LS44Wm0uMi0zLjF2MWgtMS4ybC0uOC4xYy0uMiAwLS40LjEtLjUuM2ExIDEgMCAwIDAtLjUuOWwuMS41LjQuNC43LjFhMS44IDEuOCAwIDAgMCAxLjUtLjdsLjItLjYuNS44LS4zLjZhMyAzIDAgMCAxLS41LjYgMi42IDIuNiAwIDAgMS0xLjguNmMtLjQgMC0uOSAwLTEuMi0uMy0uNC0uMS0uNy0uNC0xLS43YTIuNCAyLjQgMCAwIDEtLjEtMi4ybC43LS44IDEtLjQgMS41LS4yaDEuM1ptNi44LTIuOXYxLjJINzN2LTEuMmg0LjJabS0zLTEuOEg3NnY3LjdsLjQuMmguNGEyLjUgMi41IDAgMCAwIC41IDBWMjVhNC4yIDQuMiAwIDAgMS0xIC4xYy0uNCAwLS44IDAtMS0uMi0uMy0uMS0uNi0uMy0uNy0uNi0uMi0uMy0uMy0uNy0uMy0xLjJ2LTcuM1ptOSA3LjV2LTUuN2gxLjZWMjVoLTEuNWwtLjEtMS43Wm0uMi0xLjZoLjZsLS4yIDEuNC0uNSAxYy0uMi40LS41LjYtLjkuOGEzIDMgMCAwIDEtMS4zLjIgMyAzIDAgMCAxLTEtLjEgMiAyIDAgMCAxLS43LS41Yy0uMy0uMi0uNC0uNS0uNS0uOWE0IDQgMCAwIDEtLjItMS4ydi00LjhoMS42djQuOGwuMS43LjMuNC40LjIuNC4xYy41IDAgLjkgMCAxLjEtLjMuMy0uMi41LS40LjYtLjdsLjItMVptNS0yLjd2NmgtMS42di03LjRoMS41bC4xIDEuNFptMi4zLTEuNFYxOWEzLjIgMy4yIDAgMCAwLTEuNSAwIDEuNCAxLjQgMCAwIDAtLjggMWwtLjIuNkg4OGwuMS0xLjJjLjEtLjQuMi0uOC40LTEgLjItLjQuNC0uNi43LS44YTEuOSAxLjkgMCAwIDEgMS4zLS4yaC4zWk05NSAyNWMtLjYgMC0xIDAtMS41LS4yYTMuMyAzLjMgMCAwIDEtMS44LTIgNCA0IDAgMCAxLS4zLTEuM3YtLjNjMC0uNi4xLTEuMS4zLTEuNi4xLS41LjQtLjkuNy0xLjJhMyAzIDAgMCAxIDEtLjhsMS40LS4yYy41IDAgMSAwIDEuNC4ybDEgLjguNSAxLjFjLjIuNS4yIDEgLjIgMS41di43aC01Ljd2LTEuMmg0LjF2LS4xYzAtLjMgMC0uNi0uMi0uOSAwLS4yLS4yLS40LS40LS42bC0xLS4yYy0uMiAwLS40IDAtLjcuMi0uMiAwLS4zLjItLjUuNWwtLjMuNy0uMSAxdi40bC4xLjkuNC43LjYuNS45LjFBMi4zIDIuMyAwIDAgMCA5NyAyM2wxIC45LS43LjYtMSAuNWMtLjMuMi0uNy4yLTEuMi4yWiIvPjxnIGNsaXAtcGF0aD0idXJsKCNiKSI+PHJlY3Qgd2lkdGg9IjI2LjIiIGhlaWdodD0iMTIuNyIgeD0iODYuOSIgeT0iMTMwIiBmaWxsPSIjRjNGM0YzIiByeD0iMy4yIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTk2LjEgMTM2Ljd2LjloLTIuNXYtMWgyLjVabTUuNyAyLjR2MWgtNC42di0uOGwyLjItMi41LjYtLjcuMy0uNWExLjQgMS40IDAgMCAwIDAtMWwtLjQtLjRhMSAxIDAgMCAwLS41LS4yYy0uMyAwLS41IDAtLjcuMmExIDEgMCAwIDAtLjQuNWwtLjEuNmgtMS4xYzAtLjQgMC0uOC4zLTEgLjEtLjQuNC0uNy43LS45LjQtLjIuOC0uMyAxLjMtLjMuNCAwIC44LjEgMS4xLjMuMy4xLjYuMy43LjYuMi4zLjMuNi4zIDFsLS4xLjZjMCAuMy0uMi41LS4zLjdsLS40LjYtLjYuNi0xLjUgMS42aDMuMlptNS41LTN2MWwtLjIgMS40LS41IDFjLS4xLjItLjQuMy0uNy41bC0uOS4xaC0uN2ExLjggMS44IDAgMCAxLTEuMS0xbC0uMy0uOC0uMS0xLjFWMTM2YzAtLjYgMC0xIC4yLTEuNCAwLS40LjItLjcuNC0uOWwuNy0uNSAxLS4yYy4yIDAgLjQgMCAuNy4yYTEuOCAxLjggMCAwIDEgMSAuOGMuMi4yLjMuNS4zLjhsLjIgMS4yWm0tMS4yIDEuMlYxMzUuMmMwLS4yIDAtLjQtLjItLjVhMSAxIDAgMCAwLS4yLS40bC0uMy0uMmExIDEgMCAwIDAtLjQgMCAxIDEgMCAwIDAtLjQgMGwtLjQuNC0uMi41VjEzOC4xbC4xLjUuMi40LjMuMmguOWwuMy0uNC4zLS42di0uOVoiLz48L2c+PHBhdGggZmlsbD0iIzIzNENDNyIgZD0iTTE0MyAxMzEuNGE1NS42IDU1LjYgMCAxIDAtODYuNy4ybDcuNC02YTQ2LjEgNDYuMSAwIDEgMSA3Mi0uMWw3LjQgNloiLz48cGF0aCBmaWxsPSIjMzA1QUQ3IiBkPSJNMTQzLjIgMTMxLjJhNTUuNiA1NS42IDAgMSAwLTkzLjctNTguN2w4LjYgNGE0Ni4xIDQ2LjEgMCAxIDEgNzcuNyA0OC44bDcuNCA2WiIvPjxwYXRoIGZpbGw9IiM3MTkxRUYiIGQ9Ik0xNDMuMiAxMzEuMmE1NS42IDU1LjYgMCAwIDAtNDMtOTAuMmwtLjEgOS41YTQ2LjEgNDYuMSAwIDAgMSAzNS43IDc0LjhsNy40IDZaIi8+PHBhdGggZmlsbD0iI0ZGQTYwMCIgZD0iTTE0My4yIDEzMS4yYTU1LjYgNTUuNiAwIDAgMC0xMy44LTgxLjZsLTUgOGE0Ni4xIDQ2LjEgMCAwIDEgMTEuNCA2Ny43bDcuNCA2WiIvPjxwYXRoIGZpbGw9IiNGMzY5MDAiIGQ9Ik0xNDMuMiAxMzEuMmE1NS42IDU1LjYgMCAwIDAgNi42LTU4LjZsLTguNSA0LjFhNDYuMSA0Ni4xIDAgMCAxLTUuNSA0OC42bDcuNCA2WiIvPjxwYXRoIGZpbGw9IiNGMDQwMjIiIGQ9Ik0xNDMuMSAxMzEuNGE1NS42IDU1LjYgMCAwIDAgMTEuOS0yOC44bC05LjUtMWMtMSA4LjctNC4zIDE3LTkuOCAyMy44bDcuNCA2WiIvPjxwYXRoIGZpbGw9IiM2MzYzNjMiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0ibTEzNS42IDEyNS42LjQtLjQgMy43IDIuOSAzLjcgMi45LS40LjUtNy40LTUuOVpNMTU0LjkgMTAzLjNsLTQuNi0uNi00LjgtLjV2LS43bDQuOC41IDQuNy41LS4xLjhaTTE1MCA3M2wtLjQtLjgtNC4yIDItNC4yIDIuMi4yLjYgNC4zLTIgNC4zLTJaTTEyOS43IDQ5LjhsLTIuNiA0LTIuNiA0LS41LS40IDIuNS00IDIuNS00IC43LjRaTTEwMC4xIDQ1LjhWNDFhNTYuNyA1Ni43IDAgMCAwLS43IDB2OS42YTQ0LjMgNDQuMyAwIDAgMSAuNyAwdi00LjhaTTczIDUzLjRsLTIuNS00Yy0uMyAwLS41LjItLjcuNGwyLjYgNCAyLjUgNCAuNi0uNC0yLjUtNFpNNTQgNzQuM2wtNC4yLTIuMS0uNC43IDQuMyAyIDQuNCAyIC4yLS41LTQuMi0yLjFaTTQ5LjEgMTAybC00LjcuNXYuOGw0LjgtLjYgNC44LS41LS4xLS43LTQuOC41Wk01OS43IDEyOC4xbC0zLjcgMyAuNS42IDMuNy0zIDMuNy0zYTQ3IDQ3IDAgMCAxLS40LS41bC0zLjggMi45WiIgY2xpcC1ydWxlPSJldmVub2RkIi8+PHBhdGggZmlsbD0iIzYzNjM2MyIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTU1IDEwM2E1NS42IDU1LjYgMCAwIDEtMTIgMjguNWwtLjYtLjRhNTQuOCA1NC44IDAgMSAwLTg1LjQgMGwtLjUuNWE1NS42IDU1LjYgMCAxIDEgOTguNC0yOC42WiIgY2xpcC1ydWxlPSJldmVub2RkIi8+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTU3LjUgMTAxLjJ2LjhoLTJ2LS44aDJabTEuOS0uNGguNWwuNi0uMS4zLS4zLjEtLjV2LS41bC0uNC0uM2ExIDEgMCAwIDAtLjUgMCAxIDEgMCAwIDAtLjQgMCAuOC44IDAgMCAwLS40LjdoLTFjMC0uMyAwLS41LjItLjhsLjctLjVhMiAyIDAgMCAxIDEtLjJjLjMgMCAuNiAwIC45LjIuMyAwIC41LjMuNi41LjIuMi4zLjUuMy45IDAgLjIgMCAuMy0uMi41IDAgLjItLjEuMy0uMy40bC0uNS40LS44LjFoLS43di0uNVptMCAuN3YtLjVoLjdsLjguMWMuMyAwIC41LjIuNi4zLjIuMi4zLjMuMy41bC4xLjYtLjEuN2MwIC4yLS4yLjMtLjQuNWwtLjYuM2EyLjUgMi41IDAgMCAxLTEuNSAwIDIgMiAwIDAgMS0uNi0uM2wtLjQtLjUtLjEtLjdoLjlsLjEuNS4zLjNoMS4xbC4zLS4zLjEtLjUtLjEtLjYtLjQtLjNoLTEuMVptNy4xLS44djFsLS4xIDEuMWMwIC4zLS4yLjYtLjQuOC0uMS4yLS4zLjMtLjYuNGEyLjIgMi4yIDAgMCAxLTEuNCAwIDEuNSAxLjUgMCAwIDEtLjktLjdsLS4yLS43LS4xLTF2LS45bC4xLTEuMS40LS44LjYtLjQuOC0uMWguNmExLjUgMS41IDAgMCAxIC45LjhsLjIuNy4xIDFabS0xIDF2LTIuMmwtLjMtLjItLjItLjJhMSAxIDAgMCAwLS4zIDBoLS40bC0uMy4zLS4yLjV2Mi41bC4xLjQuMi40LjIuMWguOGwuMy0uMy4xLS41di0uN1pNNjYuMyAxMjIuN3YuOGgtMi4xdi0uOGgyWm00LjYuOHYuN2gtNGwtLjEtLjUgMi41LTMuOWguN2wtLjggMS40LTEuNCAyLjNoMy4xWm0tLjctMy43djUuN2gtMXYtNS43aDFabTUgMi40djJsLS40LjgtLjYuNWEyLjIgMi4yIDAgMCAxLTEuNCAwIDEuNSAxLjUgMCAwIDEtMS0uN2wtLjItLjd2LTNsLjUtLjguNS0uNC44LS4yLjYuMWExLjUgMS41IDAgMCAxIDEgLjhsLjIuN3YuOVptLS45IDF2LTEuN2wtLjEtLjUtLjItLjMtLjMtLjJINzNsLS4zLjMtLjIuNXYyLjZsLjEuNC4yLjMuMy4yaC43bC4zLS4zLjItLjV2LS43Wk02MS41IDc5di43aC0yVjc5aDJabTQuNiAydi44aC0zLjhWODFsMS44LTIgLjUtLjYuMi0uNGExLjEgMS4xIDAgMCAwIDAtLjlsLS4zLS4zYS45LjkgMCAwIDAtLjUtLjEgMSAxIDAgMCAwLS41LjFsLS4zLjQtLjEuNWgtMWwuMy0uOWMuMS0uMi4zLS41LjYtLjZhMiAyIDAgMCAxIDEtLjNjLjQgMCAuOCAwIDEgLjIuMy4xLjUuMy42LjYuMi4yLjIuNS4yLjh2LjVsLS4zLjVjMCAuMi0uMi40LS40LjZsLS40LjUtMS4zIDEuM2gyLjdabTQuNC0yLjV2LjlsLS4xIDEuMS0uNC44LS42LjRhMi4yIDIuMiAwIDAgMS0xLjQgMCAxLjUgMS41IDAgMCAxLS45LS43bC0uMi0uNy0uMS0uOXYtMWwuMS0xIC40LS44Yy4xLS4yLjMtLjQuNi0uNGwuNy0uMi43LjFhMS41IDEuNSAwIDAgMSAuOS43bC4yLjcuMSAxWm0tMSAxVjc3LjNsLS4zLS4zLS4yLS4yYTEgMSAwIDAgMC0uNCAwaC0uNGwtLjIuMy0uMi41djNsLjMuMy4yLjJoLjdsLjMtLjMuMi0uNXYtLjhaTTc1LjggNjEuNXYuOGgtMnYtLjhoMlptMy4zLTIuOXY1LjdoLTF2LTQuNmwtMS4zLjV2LS44bDIuMi0uOGguMVptNS43IDIuNHYxbC0uMSAxLS40LjhjLS4yLjItLjQuNC0uNi40YTIuMiAyLjIgMCAwIDEtMS40IDAgMS41IDEuNSAwIDAgMS0uOS0uNmwtLjItLjctLjEtMXYtMWwuMS0xIC40LS44LjYtLjQuNy0uMi43LjFhMS41IDEuNSAwIDAgMSAuOS44bC4yLjYuMSAxWm0tMSAxVjU5LjhsLS4zLS4zLS4yLS4yYTEgMSAwIDAgMC0uNCAwaC0uM2wtLjMuMy0uMi41VjYzLjFsLjMuMy4yLjJoLjdsLjMtLjMuMi0uNVY2MlpNMTI5LjMgMTIzLjV2LjdoLTRsLS4xLS41IDIuNC0zLjloLjhsLS44IDEuNC0xLjQgMi4zaDMuMVptLS43LTMuN3Y1LjdoLTF2LTUuN2gxWm01IDIuNHYybC0uNC44LS42LjVhMi4yIDIuMiAwIDAgMS0xLjQgMCAxLjUgMS41IDAgMCAxLTEtLjdsLS4yLS43di0zbC40LS44LjYtLjQuOC0uMi42LjFhMS41IDEuNSAwIDAgMSAxIC44bC4yLjd2LjlabS0uOSAxdi0xLjdsLS4xLS41LS4yLS4zLS4zLS4yaC0uN2wtLjMuMy0uMi41djIuNmwuMS40LjIuMy4zLjJoLjdsLjMtLjMuMi0uNXYtLjdaTTEzNi44IDEwMC44aC42bC42LS4xLjMtLjN2LTFsLS4zLS4zYTEgMSAwIDAgMC0uNSAwIDEgMSAwIDAgMC0uNCAwIC44LjggMCAwIDAtLjUuN2gtMWMwLS4zLjItLjUuMy0uOGwuNy0uNWEyIDIgMCAwIDEgLjktLjJjLjQgMCAuNyAwIDEgLjIuMiAwIC40LjMuNi41LjIuMi4yLjUuMi45di41bC0uNC40LS42LjQtLjcuMWgtLjh2LS41Wm0wIC43di0uNWguOGwuOC4xLjYuMy4zLjUuMS42LS4xLjctLjQuNS0uNi4zYTIuNSAyLjUgMCAwIDEtMS41IDAgMiAyIDAgMCAxLS42LS4zbC0uNC0uNS0uMi0uN2gxdi41Yy4yIDAgLjMuMi40LjNoMWwuNC0uM3YtMS4xbC0uNC0uM2gtMS4yWm03LjItLjh2MWwtLjEgMS4xLS40LjgtLjYuNGEyLjIgMi4yIDAgMCAxLTEuNCAwIDEuNSAxLjUgMCAwIDEtLjktLjdsLS4yLS43LS4xLTF2LS45bC4xLTEuMWMwLS4zLjItLjYuNC0uOC4xLS4yLjMtLjMuNi0uNGwuNy0uMWguN2ExLjUgMS41IDAgMCAxIC45LjhsLjIuNy4xIDFabS0xIDFWMTAwbC0uMS0uNS0uMi0uMi0uMi0uMmExIDEgMCAwIDAtLjQgMGgtLjRsLS4zLjMtLjEuNXYyLjlsLjMuNC4yLjFoLjdsLjMtLjMuMi0uNXYtLjdaTTEzNS42IDgxdi44aC0zLjhWODFsMS44LTIgLjUtLjYuMi0uNC4xLS40di0uNWwtLjQtLjNhLjguOCAwIDAgMC0uNC0uMSAxIDEgMCAwIDAtLjYuMWwtLjMuNC0uMS41aC0xbC4zLS45LjYtLjZhMiAyIDAgMCAxIDEtLjNjLjUgMCAuOCAwIDEgLjIuMy4xLjUuMy43LjZsLjIuOC0uMS41YzAgLjItLjIuNC0uMy41bC0uMy42LS41LjVMMTMzIDgxaDIuNlptNC40LTIuNXYuOWwtLjEgMS4xLS40LjhjLS4xLjItLjMuMy0uNi40YTIuMiAyLjIgMCAwIDEtMS40IDAgMS41IDEuNSAwIDAgMS0uOS0uN2wtLjItLjctLjEtLjl2LTFsLjEtMSAuNC0uOGMuMi0uMi40LS40LjYtLjRsLjgtLjIuNi4xYTEuNSAxLjUgMCAwIDEgLjkuN2wuMi43LjEgMVptLTEgMVY3Ny4zbC0uMy0uMy0uMi0uMmExIDEgMCAwIDAtLjMgMGgtLjRsLS4zLjMtLjIuNXYzbC4zLjMuMi4yaC44bC4yLS4zLjItLjV2LS44Wk0xMTcuNiA1OC42djUuN2gtLjl2LTQuNmwtMS40LjV2LS44bDIuMi0uOGguMVptNS43IDIuNHYxbC0uMSAxYzAgLjQtLjIuNi0uNC44LS4xLjItLjMuNC0uNi40YTIuMiAyLjIgMCAwIDEtMS40IDAgMS41IDEuNSAwIDAgMS0uOS0uNmwtLjItLjctLjEtMXYtMWwuMS0xIC40LS44LjYtLjQuOC0uMi42LjFhMS41IDEuNSAwIDAgMSAuOS44bC4yLjYuMSAxWm0tMSAxVjU5LjhsLS4zLS4zLS4yLS4yYTEgMSAwIDAgMC0uMyAwaC0uNGwtLjMuMy0uMi41VjYyLjdsLjEuNC4yLjMuMi4yaC44bC4zLS4zLjEtLjVWNjJaTTEwMy45IDU1LjR2MWMwIC40IDAgLjgtLjIgMSAwIC40LS4yLjctLjQuOWwtLjUuNGEyLjIgMi4yIDAgMCAxLTEuNCAwIDEuNSAxLjUgMCAwIDEtMS0uN2wtLjItLjd2LTNsLjQtLjguNi0uNC44LS4xaC42YTEuNSAxLjUgMCAwIDEgMSAuOGwuMi43djFabS0xIDFWNTQuOGwtLjEtLjUtLjItLjMtLjMtLjFhMSAxIDAgMCAwLS4zIDBoLS40bC0uMy4zLS4yLjVWNTdsLjEuNGMwIC4yLjEuMy4yLjNsLjMuMmguN2wuMy0uMy4yLS41di0uN1pNOTUuNCAxMTRsLjEtLjZhMS4yIDEuMiAwIDAgMSAxLS42Yy4zIDAgLjUgMCAuNi4yLjIgMCAuNC4yLjUuNGwuMS42LS4xLjYtLjUuNC0uNS4yYTEuMiAxLjIgMCAwIDEtMS4yLTEuMlptLjYgMCAuMS40LjUuMi40LS4yLjEtLjQtLjEtLjRhLjUuNSAwIDAgMC0uNC0uMmMtLjIgMC0uMyAwLS41LjJsLS4xLjRabTcgMy4zaDEuMWMwIC40LS4xLjgtLjMgMS4xLS4yLjQtLjUuNi0uOS44LS40LjItLjguMy0xLjMuMy0uNCAwLS44IDAtMS4xLS4ybC0uOC0uNmMtLjMtLjMtLjQtLjYtLjYtMWwtLjEtMS4zdi0uNWwuMS0xLjNjLjItLjQuMy0uNy42LTFsLjgtLjYgMS4xLS4yYy41IDAgMSAuMSAxLjMuMy40LjIuNy40LjkuOC4yLjMuMy43LjMgMS4xaC0xbC0uMy0uN2ExIDEgMCAwIDAtLjQtLjRsLS44LS4yLS43LjEtLjQuNWEyIDIgMCAwIDAtLjMuNmwtLjEgMXYxLjVsLjMuNi41LjQuNy4yLjctLjEuNS0uNS4yLS43WiIvPjxwYXRoIGZpbGw9IiNFNjUxMDAiIGQ9Ik0xMDIgOTYgNTQuOSA3NSA5OS4yIDEwMWwxMC44IDQuM2MuMi4xLjQgMCAuNS0uMmwxLjItMi4ydi0uNWwtOS42LTYuNFoiLz48ZyBmaWx0ZXI9InVybCgjYykiPjxjaXJjbGUgY3g9Ijk5LjYiIGN5PSI5OC4yIiByPSI0LjgiIGZpbGw9IiNmZmYiLz48L2c+PGRlZnM+PGZpbHRlciBpZD0iYSIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIHg9IjAiIHk9IjAiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUdhdXNzaWFuQmx1ciBpbj0iQmFja2dyb3VuZEltYWdlRml4IiBzdGREZXZpYXRpb249IjMiLz48ZmVDb21wb3NpdGUgaW4yPSJTb3VyY2VBbHBoYSIgb3BlcmF0b3I9ImluIiByZXN1bHQ9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfNDY1MF84MTE1Ii8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9iYWNrZ3JvdW5kQmx1cl80NjUwXzgxMTUiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48ZmlsdGVyIGlkPSJjIiB3aWR0aD0iMzguMiIgaGVpZ2h0PSIzOC4yIiB4PSI4MC42IiB5PSI3OS4yIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVDb2xvck1hdHJpeCBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0iaGFyZEFscGhhIiB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDEyNyAwIi8+PGZlTW9ycGhvbG9neSBpbj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJkaWxhdGUiIHJhZGl1cz0iMS42IiByZXN1bHQ9ImVmZmVjdDFfZHJvcFNoYWRvd180NjUwXzgxMTUiLz48ZmVPZmZzZXQvPjxmZUdhdXNzaWFuQmx1ciBzdGREZXZpYXRpb249IjYuNCIvPjxmZUNvbXBvc2l0ZSBpbjI9ImhhcmRBbHBoYSIgb3BlcmF0b3I9Im91dCIvPjxmZUNvbG9yTWF0cml4IHZhbHVlcz0iMCAwIDAgMCAxIDAgMCAwIDAgMC4yNDE2NjcgMCAwIDAgMCAwLjI0MTY2NyAwIDAgMCAwLjE1IDAiLz48ZmVCbGVuZCBpbjI9IkJhY2tncm91bmRJbWFnZUZpeCIgcmVzdWx0PSJlZmZlY3QxX2Ryb3BTaGFkb3dfNDY1MF84MTE1Ii8+PGZlQmxlbmQgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9kcm9wU2hhZG93XzQ2NTBfODExNSIgcmVzdWx0PSJzaGFwZSIvPjwvZmlsdGVyPjxjbGlwUGF0aCBpZD0iYiI+PHJlY3Qgd2lkdGg9IjI2LjIiIGhlaWdodD0iMTIuNyIgeD0iODYuOSIgeT0iMTMwIiBmaWxsPSIjZmZmIiByeD0iMy4yIi8+PC9jbGlwUGF0aD48L2RlZnM+PC9zdmc+", + "description": "Temperature gauge.", + "descriptor": { + "type": "latest", + "sizeX": 6, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries' }];\n }\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-analogue-radial-gauge-widget-settings", + "dataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-radial-gauge-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 15) {\\n\\tvalue = 15;\\n} else if (value > 30) {\\n\\tvalue = 30;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"startAngle\":45,\"ticksAngle\":270,\"needleCircleSize\":8,\"defaultColor\":\"#e65100\",\"minValue\":-40,\"maxValue\":40,\"majorTicksCount\":8,\"colorMajorTicks\":\"#444\",\"minorTicks\":8,\"colorMinorTicks\":\"#666\",\"numbersFont\":{\"family\":\"Roboto\",\"size\":18,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"numbersColor\":\"#616161\",\"showUnitTitle\":false,\"unitTitle\":\"\",\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#888\"},\"titleColor\":\"#888\",\"unitsFont\":{\"size\":26,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"color\":\"#616161\"},\"unitsColor\":\"#616161\",\"valueBox\":true,\"valueInt\":3,\"valueFont\":{\"size\":27,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"color\":\"rgba(0, 0, 0, 0.54)\",\"shadowColor\":\"#FFFFFF01\"},\"valueColor\":\"rgba(0, 0, 0, 0.54)\",\"valueColorShadow\":\"#FFFFFF01\",\"colorValueBoxRect\":\"#88888800\",\"colorValueBoxRectEnd\":\"#66666600\",\"colorValueBoxBackground\":\"rgba(243, 243, 243, 0.54)\",\"colorValueBoxShadow\":\"rgba(0, 0, 0, 0)\",\"showBorder\":false,\"colorPlate\":\"#FFFFFF\",\"colorNeedle\":null,\"colorNeedleEnd\":null,\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"colorNeedleShadowDown\":\"rgba(188,143,143,0.45)\",\"highlightsWidth\":15,\"highlights\":[{\"from\":-40,\"to\":-20,\"color\":\"#224AC2\"},{\"from\":-20,\"to\":0,\"color\":\"#2B54CE\"},{\"from\":0,\"to\":10,\"color\":\"#6083EC\"},{\"from\":10,\"to\":20,\"color\":\"#F89E0D\"},{\"from\":20,\"to\":30,\"color\":\"#F77410\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"}],\"animation\":true,\"animationDuration\":500,\"animationRule\":\"cycle\"},\"title\":\"Temperature\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"decimals\":0,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\",\"units\":\"°C\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":null,\"lineHeight\":\"24px\"},\"showTitleIcon\":true,\"titleTooltip\":\"\",\"titleIcon\":\"device_thermostat\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"actions\":{},\"margin\":\"0px\",\"borderRadius\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "temperature", + "environment", + "weather" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/temperature_radial_gauge.json b/application/src/main/data/json/system/widget_types/temperature_radial_gauge.json new file mode 100644 index 0000000000..cb12ebe9b5 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/temperature_radial_gauge.json @@ -0,0 +1,22 @@ +{ + "fqn": "analogue_gauges.temperature_radial_gauge_canvas_gauges", + "name": "Temperature radial gauge", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Preconfigured gauge to display temperature. Allows to configure temperature range, gradient colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 6, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueRadialGauge(self.ctx, 'radialGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries' }];\n }\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n", "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-analogue-radial-gauge-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-radial-gauge-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"startAngle\":67.5,\"ticksAngle\":225,\"needleCircleSize\":7,\"defaultColor\":\"#e65100\",\"minValue\":-60,\"maxValue\":60,\"majorTicksCount\":12,\"colorMajorTicks\":\"#444\",\"minorTicks\":12,\"colorMinorTicks\":\"#666\",\"numbersFont\":{\"family\":\"Roboto\",\"size\":20,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#263238\"},\"numbersColor\":\"#263238\",\"showUnitTitle\":true,\"unitTitle\":\"Temperature\",\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#263238\"},\"titleColor\":\"#263238\",\"unitsFont\":{\"family\":\"Roboto\",\"size\":28,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#616161\"},\"unitsColor\":\"#616161\",\"valueBox\":true,\"valueInt\":3,\"valueFont\":{\"family\":\"Segment7Standard\",\"size\":30,\"style\":\"normal\",\"weight\":\"normal\",\"shadowColor\":\"rgba(0, 0, 0, 0.49)\",\"color\":\"#444\"},\"valueColor\":\"#444\",\"valueColorShadow\":\"rgba(0, 0, 0, 0.49)\",\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\",\"showBorder\":true,\"colorPlate\":\"#cfd8dc\",\"colorNeedle\":null,\"colorNeedleEnd\":null,\"colorNeedleShadowUp\":\"rgba(2, 255, 255, 0)\",\"colorNeedleShadowDown\":\"rgba(188, 143, 143, 0.78)\",\"highlightsWidth\":15,\"highlights\":[{\"from\":-60,\"to\":-50,\"color\":\"#42a5f5\"},{\"from\":-50,\"to\":-40,\"color\":\"rgba(66, 165, 245, 0.83)\"},{\"from\":-40,\"to\":-30,\"color\":\"rgba(66, 165, 245, 0.66)\"},{\"from\":-30,\"to\":-20,\"color\":\"rgba(66, 165, 245, 0.5)\"},{\"from\":-20,\"to\":-10,\"color\":\"rgba(66, 165, 245, 0.33)\"},{\"from\":-10,\"to\":0,\"color\":\"rgba(66, 165, 245, 0.16)\"},{\"from\":0,\"to\":10,\"color\":\"rgba(229, 115, 115, 0.16)\"},{\"from\":10,\"to\":20,\"color\":\"rgba(229, 115, 115, 0.33)\"},{\"from\":20,\"to\":30,\"color\":\"rgba(229, 115, 115, 0.5)\"},{\"from\":30,\"to\":40,\"color\":\"rgba(229, 115, 115, 0.66)\"},{\"from\":40,\"to\":50,\"color\":\"rgba(229, 115, 115, 0.83)\"},{\"from\":50,\"to\":60,\"color\":\"#e57373\"}],\"animation\":true,\"animationDuration\":1000,\"animationRule\":\"bounce\"},\"title\":\"Temperature radial gauge\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"decimals\":0,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\",\"units\":\"°C\"}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/temperature_range_chart.json b/application/src/main/data/json/system/widget_types/temperature_range_chart.json new file mode 100644 index 0000000000..dc8b402625 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/temperature_range_chart.json @@ -0,0 +1,31 @@ +{ + "fqn": "temperature_range_chart", + "name": "Temperature range chart", + "deprecated": false, + "image": "tb-image:dGVtcGVyYXR1cmVfcmFuZ2VfY2hhcnQuc3Zn:dGVtcGVyYXR1cmVfcmFuZ2VfY2hhcnQuc3Zn;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJub25lIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0iI2ZmZiIgcng9IjQiLz48cmVjdCB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIgeD0iLjUiIHk9Ii41IiBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuMSIgcng9IjMuNSIvPjxnIGNsaXAtcGF0aD0idXJsKCNhKSI+PHBhdGggZmlsbD0idXJsKCNiKSIgZD0ibTQzLjkgNzAuNiAyMi44LTMzLjUgMTUgMTkuNSA1IDEuM0wxMDcuNiAxNmw0LjQgMTYuOCAyLjEgNDIuN2gxMS4zbDguOC0yNi4zYzMuNSA3LjUgNy43IDIxLjEgMTEuNiAzNS44aDIyLjhsMS41LTcuNyAyLjcgNy43aDEwLjhsNS40LTIzLjhWODVoLTUuNGwtNC4zIDE5LTYuNS0xOWgtNC4ybC0xMC4xIDUyYy0yLjQtMTAuMi03LjMtMzItMTIuNy01MkgzNnYtNGw3LjktMTAuNFoiLz48cGF0aCBzdHJva2U9InVybCgjYykiIHN0cm9rZS13aWR0aD0iLjkiIGQ9Im0zNiA4MSA4LTEwLjQgMjMtMzMuNSAxNS4yIDE5LjUgNSAxLjMgMjEtNDEuOSA0LjMgMjEuMSAyIDM4LjRoMTFsOC43LTI2LjkgMTEuOSAzOCAxMi40IDUwLjQgMTEuNi01OS41IDkuNyAyNy4yIDIuNS0xMS41IDYuNy0zMS44Ii8+PC9nPjxwYXRoIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjUiIGQ9Ik0xOC4zIDE3di43aC00di0uNGwyLjUtNGguNWwtLjYgMS4yTDE1IDE3aDMuM1ptLS44LTMuN1YxOWgtLjd2LTUuN2guN1ptNS4yIDIuNHYuOWMwIC40IDAgLjgtLjIgMS4xIDAgLjQtLjIuNi0uMy44LS4yLjItLjQuNC0uNi40YTIgMiAwIDAgMS0uNy4ybC0uNi0uMWMtLjIgMC0uNC0uMS0uNS0uMy0uMiAwLS4zLS4yLS40LS40bC0uMy0uOHYtM2wuNS0uN2MuMS0uMi4zLS40LjUtLjRsLjgtLjIuNi4xYTEuNCAxLjQgMCAwIDEgLjguN2wuMy43djFabS0uOCAxdi0xLjhsLS4xLS41YTEgMSAwIDAgMC0uMy0uM2MwLS4xLS4xLS4yLS4zLS4yYTEgMSAwIDAgMC0uMyAwIDEgMSAwIDAgMC0uNSAwbC0uMy4zLS4yLjYtLjEuOHYxLjhsLjIuNS4yLjMuMy4yaC44Yy4yIDAgLjMtLjIuNC0uM2wuMi0uNnYtLjhabTEuOC0yLjVjMC0uMSAwLS4zLjItLjVsLjMtLjMuNS0uMmMuMiAwIC40IDAgLjUuMmExIDEgMCAwIDEgMCAxLjcgMSAxIDAgMCAxLS41LjIgMSAxIDAgMCAxLS41LS4yIDEgMSAwIDAgMS0uMy0uMyAxIDEgMCAwIDEtLjItLjZabS41IDBjMCAuMiAwIC4zLjIuNGwuMy4yLjQtLjIuMS0uNC0uMS0uMy0uNC0uMmMtLjEgMC0uMiAwLS4zLjJsLS4yLjNabTYuMiAzaC44YzAgLjQtLjIuNy0uMyAxLS4yLjItLjQuNS0uNy42LS4zLjItLjcuMy0xLjIuMy0uMyAwLS42IDAtLjktLjJhMiAyIDAgMCAxLS43LS41bC0uNC0uOS0uMi0xdi0uNmMwLS40IDAtLjguMi0xLjEgMC0uMy4yLS42LjQtLjguMi0uMy41LS41LjgtLjZsLjktLjJjLjQgMCAuOC4xIDEgLjMuNC4xLjYuNC44LjZsLjMgMWgtLjhhMiAyIDAgMCAwLS4yLS42IDEgMSAwIDAgMC0uNC0uNWwtLjctLjEtLjcuMWMtLjIgMC0uMy4yLS40LjRhMiAyIDAgMCAwLS4zLjZsLS4xLjl2MS4zbC4zLjcuNS40LjYuMmMuNCAwIC42IDAgLjgtLjIuMiAwIC4zLS4yLjQtLjRsLjItLjdaTTE1LjUgMzNoLjVsLjctLjIuMy0uMy4yLS41LS4xLS42LS40LS4zLS41LS4xaC0uNmEuOS45IDAgMCAwLS41IDFoLS43YzAtLjMgMC0uNi4yLS44LjItLjMuNC0uNS43LS42YTIgMiAwIDAgMSAuOS0uMmMuMyAwIC42IDAgLjkuMi4yIDAgLjQuMy42LjVsLjIuOS0uMS40YzAgLjItLjIuNC0uMy41LS4yLjEtLjMuMy0uNi4zbC0uOC4yaC0uNnYtLjVabTAgLjV2LS40aC42bDEgLjEuNS40LjMuNFYzNS4zbC0uNC41LS42LjMtLjcuMWgtLjdsLS42LS40YTEuNCAxLjQgMCAwIDEtLjUtMS4yaC43bC4xLjYuNC4zLjYuMWguNWwuNC0uNC4xLS42LS4xLS42YS45LjkgMCAwIDAtLjQtLjNsLS43LS4yaC0uNVptNy4yLS43djFjMCAuNCAwIC44LS4yIDEgMCAuNC0uMi43LS4zLjlsLS42LjRhMiAyIDAgMCAxLS43LjFoLS42bC0uNS0uMy0uNC0uNS0uMy0uN3YtM2wuNS0uOC41LS40LjgtLjFoLjZhMS40IDEuNCAwIDAgMSAuOC44bC4zLjd2MVptLS44IDF2LTEuN2wtLjEtLjVhMSAxIDAgMCAwLS4zLS40bC0uMy0uMmExIDEgMCAwIDAtLjMgMCAxIDEgMCAwIDAtLjUgMGwtLjMuNC0uMi41LS4xLjh2MS44bC4yLjUuMi40LjMuMmguOGwuNC0uNC4yLS41di0uOVptMS44LTIuNGMwLS4yIDAtLjQuMi0uNSAwLS4yLjItLjMuMy0uNGwuNS0uMS41LjFhMSAxIDAgMCAxIDAgMS44IDEgMSAwIDAgMS0uNSAwIDEgMSAwIDAgMS0uNSAwIDEgMSAwIDAgMS0uMy0uNCAxIDEgMCAwIDEtLjItLjVabS41IDBjMCAuMSAwIC4zLjIuM2wuMy4yYy4yIDAgLjMgMCAuNC0uMmwuMS0uMy0uMS0uNC0uNC0uMS0uMy4xLS4yLjRabTYuMiAzaC44bC0uMy45LS43LjctMS4yLjJjLS4zIDAtLjYgMC0uOS0uMmEyIDIgMCAwIDEtLjctLjVsLS40LS44LS4yLTEuMVYzM2MwLS40IDAtLjcuMi0xIDAtLjQuMi0uNy40LS45bC44LS41Yy4yLS4yLjUtLjIuOS0uMnMuOCAwIDEgLjJjLjQuMi42LjQuOC43LjEuMy4zLjYuMyAxaC0uOGEyIDIgMCAwIDAtLjItLjcgMSAxIDAgMCAwLS40LS40Yy0uMi0uMi0uNC0uMi0uNy0uMmwtLjcuMS0uNC40YTIgMiAwIDAgMC0uMy43bC0uMS44djEuNGwuMy42LjUuNS42LjEuOC0uMS40LS41LjItLjdaTTE4LjIgNTIuN3YuNmgtMy43di0uNWwxLjgtMi4xLjYtLjcuMi0uNC4xLS41LS4xLS41LS4zLS4zYTEgMSAwIDAgMC0uNi0uMmMtLjIgMC0uNCAwLS42LjJhMSAxIDAgMCAwLS40LjRsLS4xLjZoLS43YzAtLjQgMC0uNi4yLTEgLjEtLjIuMy0uNC42LS41YTIgMiAwIDAgMSAxLS4zYy40IDAgLjcgMCAxIC4ybC41LjVjLjIuMy4yLjUuMi44di41bC0uMy42YTcuOCA3LjggMCAwIDEtLjggMWwtMS41IDEuNmgyLjlabTQuNS0yLjd2LjljMCAuNCAwIC44LS4yIDEuMSAwIC40LS4yLjYtLjMuOGwtLjYuNGEyIDIgMCAwIDEtLjcuMmwtLjYtLjFjLS4yIDAtLjQtLjItLjUtLjNsLS40LS41LS4zLS43di0zYy4yLS4zLjMtLjYuNS0uN2wuNS0uNWgxLjRhMS40IDEuNCAwIDAgMSAuOC43bC4zLjd2MVptLS44IDF2LTEuOGwtLjEtLjVhMSAxIDAgMCAwLS4zLS4zYzAtLjEtLjEtLjItLjMtLjJhMSAxIDAgMCAwLS4zIDAgMSAxIDAgMCAwLS41IDBsLS4zLjMtLjIuNi0uMS44djEuN2wuMi42LjIuMy4zLjJoLjhsLjQtLjMuMi0uNlY1MVptMS44LTIuNWMwLS4yIDAtLjMuMi0uNWwuMy0uMy41LS4yYy4yIDAgLjQgMCAuNS4yYTEgMSAwIDAgMSAwIDEuNyAxIDEgMCAwIDEtLjUuMSAxIDEgMCAwIDEtLjUtLjEgMSAxIDAgMCAxLS4zLS40IDEgMSAwIDAgMS0uMi0uNVptLjUgMGMwIC4yIDAgLjMuMi40bC4zLjEuNC0uMS4xLS40LS4xLS4zLS40LS4yYS41LjUgMCAwIDAtLjUuNVptNi4yIDNoLjhsLS4zIDFjLS4yLjItLjQuNS0uNy42LS4zLjItLjcuMy0xLjIuM2wtLjktLjJhMiAyIDAgMCAxLS43LS42bC0uNC0uOC0uMi0xdi0uNmMwLS40IDAtLjguMi0xLjEgMC0uMy4yLS42LjQtLjlsLjgtLjUuOS0uMmMuNCAwIC44LjEgMSAuMy40LjEuNi4zLjguNi4xLjMuMy42LjMgMWgtLjhhMiAyIDAgMCAwLS4yLS43IDEgMSAwIDAgMC0uNC0uNGwtLjctLjJjLS4zIDAtLjUgMC0uNy4yLS4yIDAtLjMuMi0uNC40YTIgMiAwIDAgMC0uMy42bC0uMS45djEuM2wuMy43LjUuNC42LjIuOC0uMi40LS40LjItLjdaTTE2LjggNjQuN3Y1LjdoLS43di00LjhsLTEuNC41di0uNmwyLS44aC4xWm01LjkgMi40di45YzAgLjUgMCAuOS0uMiAxLjIgMCAuMy0uMi42LS4zLjhsLS42LjRhMiAyIDAgMCAxLS43LjFoLS42bC0uNS0uMy0uNC0uNS0uMy0uN3YtM2wuNS0uOC41LS40LjgtLjFoLjZhMS40IDEuNCAwIDAgMSAuOC44bC4zLjd2MVptLS44IDF2LTEuOGwtLjEtLjVhMSAxIDAgMCAwLS4zLS4zbC0uMy0uMmExIDEgMCAwIDAtLjMgMCAxIDEgMCAwIDAtLjUgMGwtLjMuNC0uMi41LS4xLjh2MS44bC4yLjUuMi4zLjMuM2guOGwuNC0uNC4yLS42di0uOFptMS44LTIuNGMwLS4yIDAtLjQuMi0uNSAwLS4yLjItLjMuMy0uNGwuNS0uMS41LjFhMSAxIDAgMCAxIDAgMS43IDEgMSAwIDAgMS0uNS4yIDEgMSAwIDAgMS0uNS0uMiAxIDEgMCAwIDEtLjMtLjMgMSAxIDAgMCAxLS4yLS41Wm0uNSAwYzAgLjEgMCAuMi4yLjNsLjMuMmMuMiAwIC4zIDAgLjQtLjJsLjEtLjMtLjEtLjQtLjQtLjEtLjMuMS0uMi40Wm02LjIgM2guOGwtLjMuOS0uNy43LTEuMi4yYy0uMyAwLS42IDAtLjktLjJhMiAyIDAgMCAxLS43LS41TDI3IDY5bC0uMi0xLjF2LS42YzAtLjQgMC0uOC4yLTEgMC0uNC4yLS43LjQtMWwuOC0uNGMuMi0uMi41LS4yLjktLjJzLjggMCAxIC4yYy40LjIuNi40LjguNy4xLjMuMy42LjMgMWgtLjhhMiAyIDAgMCAwLS4yLS43IDEgMSAwIDAgMC0uNC0uNWwtLjctLjEtLjcuMS0uNC40YTIgMiAwIDAgMC0uMy43bC0uMS44djEuNGwuMy42LjUuNC42LjJjLjQgMCAuNiAwIC44LS4yLjIgMCAuMy0uMi40LS40bC4yLS43Wk0yMyA4NC4zdjJsLS40LjgtLjYuNGEyIDIgMCAwIDEtLjguMWgtLjZsLS41LS4zLS4zLS41LS4zLS43di0zbC40LS44LjYtLjRoMS40YTEuNCAxLjQgMCAwIDEgLjguN2wuMy43djFabS0uNyAxdi0xLjhMMjIgODNhMSAxIDAgMCAwLS4yLS40bC0uMy0uMWExIDEgMCAwIDAtLjQtLjEgMSAxIDAgMCAwLS40IDBsLS40LjQtLjIuNVY4NmwuMi41YzAgLjIgMCAuMy4yLjRsLjMuMmguOGMuMS0uMS4zLS4yLjMtLjRsLjMtLjV2LS44Wm0xLjgtMi41YzAtLjIgMC0uMy4yLS41bC4zLS40LjUtLjEuNS4xYTEgMSAwIDAgMSAwIDEuOCAxIDEgMCAwIDEtLjUuMSAxIDEgMCAwIDEtLjUtLjEgMSAxIDAgMCAxLS4zLS40IDEgMSAwIDAgMS0uMi0uNVptLjUgMGMwIC4yIDAgLjMuMi40bC4zLjEuNC0uMS4xLS40LS4xLS4zLS40LS4yYS41LjUgMCAwIDAtLjUuNVptNi4yIDNoLjhsLS4zIDEtLjcuNmMtLjMuMi0uNy4yLTEuMi4ybC0uOS0uMWEyIDIgMCAwIDEtLjctLjZsLS40LS44Yy0uMi0uMy0uMi0uNy0uMi0xdi0uN2MwLS40IDAtLjcuMi0xIDAtLjQuMi0uNi40LS45bC43LS41IDEtLjJjLjQgMCAuOCAwIDEgLjIuNC4yLjYuNC44LjdsLjMgMWgtLjhhMiAyIDAgMCAwLS4yLS43IDEgMSAwIDAgMC0uNC0uNGwtLjctLjJjLS4zIDAtLjUgMC0uNy4yLS4yIDAtLjMuMi0uNS40YTIgMiAwIDAgMC0uMy42djIuMmwuMy42LjUuNS42LjEuOC0uMS40LS41LjItLjZaTTE0IDEwMnYuNmgtMS45di0uNmgyWm0zLjItM3Y1LjdoLS43VjEwMGwtMS41LjV2LS42bDItLjhoLjJabTUuOCAyLjR2LjlsLS4xIDEuMi0uNC43Yy0uMi4yLS4zLjQtLjYuNWEyIDIgMCAwIDEtLjcgMGgtLjZjLS4yIDAtLjQtLjEtLjUtLjMtLjIgMC0uMy0uMi0uNC0uNGwtLjItLjctLjEtMXYtLjlsLjEtMS4yLjQtLjcuNS0uNC44LS4yLjYuMWExLjQgMS40IDAgMCAxIC45LjdsLjIuN3YxWm0tLjcgMXYtMS44bC0uMi0uNWExIDEgMCAwIDAtLjItLjNsLS4zLS4yYTEgMSAwIDAgMC0uNCAwIDEgMSAwIDAgMC0uNSAwbC0uMy4zLS4yLjZ2Mi42bC4xLjUuMi4zLjMuMi40LjEuNS0uMS4zLS4zLjItLjZ2LS44Wk0yNCAxMDBsLjEtLjUuNC0uNC41LS4yYy4yIDAgLjMgMCAuNS4yYTEgMSAwIDAgMSAwIDEuNyAxIDEgMCAwIDEtLjUuMiAxIDEgMCAwIDEtLjUtLjIgMSAxIDAgMCAxLS40LS4zIDEgMSAwIDAgMS0uMS0uNVptLjUgMCAuMS4zLjQuMmMuMSAwIC4yIDAgLjMtLjJsLjItLjNjMC0uMiAwLS4zLS4yLS40bC0uMy0uMmMtLjIgMC0uMyAwLS40LjJsLS4xLjRabTYuMiAzaC43bC0uMy45Yy0uMi4zLS40LjUtLjcuNi0uMy4yLS43LjMtMS4xLjMtLjQgMC0uNyAwLTEtLjJhMiAyIDAgMCAxLS42LS41Yy0uMi0uMy0uNC0uNS0uNS0uOWwtLjItMXYtLjZsLjItMSAuNS0xIC43LS41IDEtLjJjLjQgMCAuNy4xIDEgLjNsLjcuN2MuMi4yLjMuNi4zIDFoLS43YTIgMiAwIDAgMC0uMi0uNyAxIDEgMCAwIDAtLjUtLjVsLS43LS4xLS42LjEtLjUuNGEyIDIgMCAwIDAtLjMuN2wtLjEuOFYxMDIuOWwuMy43LjUuNC43LjJjLjMgMCAuNSAwIC43LS4yLjIgMCAuNC0uMi41LS40bC4yLS43Wk0xNCAxMTkuMXYuNmgtMS45di0uNmgyWm00LjUgMi4ydi42aC0zLjd2LS42bDEuOS0yIC41LS43LjMtLjV2LS45bC0uNC0uNGExIDEgMCAwIDAtLjUtLjFsLS43LjFhMSAxIDAgMCAwLS40LjR2LjZoLS44YzAtLjMgMC0uNi4yLS44LjEtLjMuNC0uNS42LS43YTIgMiAwIDAgMSAxLS4yYy40IDAgLjcgMCAxIC4ybC42LjUuMi44LS4xLjVjMCAuMi0uMi40LS4zLjVhNy44IDcuOCAwIDAgMS0uOCAxbC0xLjQgMS43aDIuOFptNC41LTIuN3YuOGwtLjEgMS4yLS40LjgtLjYuNGEyIDIgMCAwIDEtLjcuMWgtLjZsLS41LS4zLS40LS41LS4yLS43LS4xLTF2LS44bC4xLTEuMi40LS44LjUtLjQuOC0uMWguNmExLjQgMS40IDAgMCAxIC45LjhsLjIuN3YxWm0tLjcgMVYxMTcuN2wtLjItLjVhMSAxIDAgMCAwLS4yLS40bC0uMy0uMmExIDEgMCAwIDAtLjQgMCAxIDEgMCAwIDAtLjUgMGwtLjMuNC0uMi41djIuNmwuMS41LjIuNC4zLjJoLjlsLjMtLjQuMi0uNXYtLjlaTTI0IDExN2wuMS0uNS40LS40YTEgMSAwIDAgMSAxIDAgMSAxIDAgMCAxIDAgMS44IDEgMSAwIDAgMS0uNS4xIDEgMSAwIDAgMS0uNS0uMSAxIDEgMCAwIDEtLjQtLjQgMSAxIDAgMCAxLS4xLS41Wm0uNSAwIC4xLjQuNC4xLjMtLjEuMi0uNGMwLS4xIDAtLjMtLjItLjRsLS4zLS4xLS40LjEtLjEuNFptNi4yIDNoLjdsLS4zIDEtLjcuNmMtLjMuMi0uNy4yLTEuMS4yLS40IDAtLjcgMC0xLS4yYTIgMiAwIDAgMS0uNi0uNWwtLjUtLjgtLjItMS4xdi0uNmwuMi0xYy4xLS40LjMtLjcuNS0uOWwuNy0uNSAxLS4yYy40IDAgLjcgMCAxIC4ybC43LjdjLjIuMy4zLjYuMyAxaC0uN2EyIDIgMCAwIDAtLjItLjcgMSAxIDAgMCAwLS41LS40bC0uNy0uMmMtLjIgMC0uNCAwLS42LjItLjIgMC0uNC4yLS41LjRhMiAyIDAgMCAwLS4zLjZsLS4xLjh2MS40bC4zLjYuNS41LjcuMS43LS4xLjUtLjUuMi0uN1pNMTQgMTM2LjN2LjZoLTEuOXYtLjZoMlptMS45LS41aC41bC42LS4xLjQtLjR2LTFjMC0uMi0uMi0uMy0uMy0uNEgxNmwtLjQuNC0uMS41aC0uOGMwLS4zLjEtLjYuMy0uOGwuNi0uNmEyIDIgMCAwIDEgLjktLjJsLjkuMmMuMy4xLjUuMy42LjZsLjIuOHYuNWwtLjQuNS0uNS4zLS44LjFoLS42di0uNFptMCAuNnYtLjRoMS41bC41LjRjLjIuMi4zLjMuMy41bC4xLjUtLjEuNy0uNC41LS42LjRoLTEuNGwtLjYtLjNhMS40IDEuNCAwIDAgMS0uNS0xLjJoLjdjMCAuMiAwIC40LjIuNSAwIC4yLjIuMy4zLjRoMS4yYy4xLS4xLjMtLjIuMy0uNGwuMi0uNmMwLS4yIDAtLjQtLjItLjZhLjkuOSAwIDAgMC0uNC0uM2wtLjYtLjFoLS41Wm03LS43djIuMWwtLjQuN2MtLjIuMi0uMy40LS42LjRhMiAyIDAgMCAxLS43LjJsLS42LS4xYy0uMiAwLS40LS4xLS41LS4zLS4yIDAtLjMtLjItLjQtLjRsLS4yLS44LS4xLTF2LS44bC4xLTEuMi40LS43Yy4xLS4yLjMtLjQuNS0uNGwuOC0uMi42LjFhMS40IDEuNCAwIDAgMSAuOS43bC4yLjd2MVptLS42IDF2LTEuOGwtLjItLjVhMSAxIDAgMCAwLS4yLS4zbC0uMy0uMmExIDEgMCAwIDAtLjQgMCAxIDEgMCAwIDAtLjUgMGwtLjMuMy0uMi42djIuNmwuMS41LjIuMy4zLjJoLjlsLjMtLjMuMi0uNnYtLjhabTEuOC0yLjUuMS0uNS40LS4zLjUtLjJjLjIgMCAuMyAwIC41LjJhMSAxIDAgMCAxIDAgMS43IDEgMSAwIDAgMS0uNS4yIDEgMSAwIDAgMS0uNS0uMiAxIDEgMCAwIDEtLjQtLjMgMSAxIDAgMCAxLS4xLS42Wm0uNSAwIC4xLjQuNC4yLjMtLjIuMi0uNGEuNS41IDAgMCAwLS41LS41Yy0uMiAwLS4zIDAtLjQuMmwtLjEuM1ptNi4yIDNoLjdjMCAuNC0uMS43LS4zIDEtLjIuMi0uNC41LS43LjYtLjMuMi0uNy4zLTEuMS4zLS40IDAtLjcgMC0xLS4yYTIgMiAwIDAgMS0uNi0uNWwtLjUtLjktLjItMXYtLjZsLjItMS4xLjUtLjhjLjItLjMuNC0uNS43LS42bDEtLjJjLjQgMCAuNy4xIDEgLjMuMy4xLjUuNC43LjYuMi4zLjMuNy4zIDFoLS43YTIgMiAwIDAgMC0uMi0uNiAxIDEgMCAwIDAtLjUtLjVsLS43LS4xLS42LjFjLS4yIDAtLjQuMi0uNS40YTIgMiAwIDAgMC0uMy42bC0uMS45djEuM2wuMy43LjUuNC43LjJjLjMgMCAuNSAwIC43LS4yLjIgMCAuNC0uMi41LS40bC4yLS43Wk00MC44IDE0MS4zdjUuN2gtLjd2LTQuOGwtMS41LjV2LS42bDIuMS0uOGguMVptNC41IDB2NS43aC0uN3YtNC44bC0xLjUuNXYtLjZsMi4xLS44aC4xWm04LjcgMHY1LjdoLS44bC0yLjgtNC40djQuNGgtLjh2LTUuN2guOGwyLjggNC40di00LjRoLjhabTEgMy42YzAtLjQgMC0uNi4yLS45IDAtLjMuMi0uNS4zLS43bC42LS40Yy4zLS4yLjUtLjIuOC0uMi4zIDAgLjYgMCAuOC4yLjMgMCAuNS4yLjYuNGwuNC43LjIuOHYuMWMwIC4zIDAgLjYtLjIuOWwtLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM3YtLjlabS43IDAgLjEuNi4yLjUuNC40aDEuMWwuMy0uNC4zLS41di0xLjJjMC0uMi0uMi0uNC0uMy0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLS41LjFsLS40LjQtLjIuNXYuNVptNS4zIDEuNCAxLjItMy41aC43bC0xLjUgNC4yaC0uNWwuMS0uN1ptLTEtMy41IDEuMiAzLjYuMS42aC0uNWwtMS41LTQuMmguN1pNNjkgMTQxLjN2NS43aC0uN3YtNC44bC0xLjQuNXYtLjZsMi0uOGguMlptNiA1LjF2LjZoLTMuOHYtLjVsMS45LTIgLjUtLjcuMy0uNXYtMWwtLjQtLjNhMSAxIDAgMCAwLS42LS4yYy0uMiAwLS40IDAtLjYuMmExIDEgMCAwIDAtLjQuNGwtLjEuNmgtLjdjMC0uMyAwLS42LjItLjkuMS0uMy4zLS41LjYtLjZhMiAyIDAgMCAxIDEtLjNsMSAuMmMuMi4yLjQuMy41LjYuMi4yLjMuNC4zLjhsLS4xLjUtLjMuNWE3LjggNy44IDAgMCAxLS44IDFsLTEuNCAxLjZoMi44Wm03LjItNXY1LjZoLS43bC0yLjktNC40djQuNEg3OHYtNS43aC43bDIuOSA0LjR2LTQuNGguN1ptMSAzLjUuMi0uOWMwLS4zLjItLjUuNC0uN2wuNi0uNGMuMi0uMi41LS4yLjgtLjIuMyAwIC42IDAgLjguMi4yIDAgLjQuMi42LjRsLjQuNy4xLjh2LjFsLS4xLjktLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM2wtLjEtLjlabS44IDB2LjZsLjMuNWMwIC4yLjIuMy40LjRoMWwuNC0uNC4yLS41di0xLjJsLS4yLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41LS4xLjVabTUuMiAxLjQgMS4yLTMuNWguN2wtMS41IDQuMmgtLjVsLjEtLjdabS0xLTMuNSAxLjMgMy42di42aC0uNGwtMS42LTQuMmguOFpNOTcuMyAxNDEuM3Y1LjdoLS43di00LjhsLTEuNS41di0uNmwyLjEtLjhoLjFabTMuMiAyLjVoLjVsLjYtLjEuNC0uNC4xLS41di0uNWwtLjQtLjRoLTEuMWEuOS45IDAgMCAwLS41LjloLS43YzAtLjMgMC0uNi4yLS44bC42LS42YTIgMiAwIDAgMSAxLS4ybC44LjJjLjMuMS41LjMuNi42LjIuMi4zLjUuMy44bC0uMS41LS40LjUtLjUuMy0uOC4xaC0uNnYtLjRabTAgLjZ2LS40aDEuNmwuNS40LjMuNXYxLjJsLS41LjUtLjUuNGgtMS41bC0uNS0uM2ExLjQgMS40IDAgMCAxLS42LTEuMmguOHYuNWwuNS40aDEuMWwuNC0uNC4xLS42LS4xLS42YS45LjkgMCAwIDAtLjUtLjNsLS42LS4xaC0uNVptMTAtM3Y1LjZoLS44bC0yLjgtNC40djQuNGgtLjh2LTUuN2guOGwyLjggNC40di00LjRoLjhabTEgMy41YzAtLjQgMC0uNi4yLS45IDAtLjMuMi0uNS4zLS43bC42LS40Yy4zLS4yLjUtLjIuOC0uMi4zIDAgLjYgMCAuOC4yLjMgMCAuNS4yLjYuNGwuNC43LjIuOHYuMWMwIC4zIDAgLjYtLjIuOWwtLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM3YtLjlabS43IDAgLjEuNi4yLjUuNC40aDEuMWwuMy0uNC4zLS41di0xLjJjMC0uMi0uMi0uNC0uMy0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLS41LjFsLS40LjQtLjIuNXYuNVptNS4zIDEuNCAxLjItMy41aC43bC0xLjUgNC4yaC0uNWwuMS0uN1ptLTEtMy41IDEuMiAzLjYuMS42aC0uNWwtMS41LTQuMmguN1pNMTI1LjYgMTQxLjN2NS43aC0uOHYtNC44bC0xLjQuNXYtLjZsMi0uOGguMlptNiAzLjh2LjZoLTQuMnYtLjRsMi42LTRoLjZsLS43IDEuMi0xLjcgMi42aDMuM1ptLS45LTMuOHY1LjdoLS43di01LjdoLjdabTggMHY1LjdoLS43bC0yLjktNC40djQuNGgtLjd2LTUuN2guN2wyLjkgNC40di00LjRoLjdabTEgMy42LjItLjljMC0uMy4yLS41LjQtLjdsLjYtLjRjLjItLjIuNS0uMi44LS4yLjMgMCAuNiAwIC44LjIuMiAwIC40LjIuNi40bC40LjcuMS44di4xbC0uMS45LS40LjdhMS44IDEuOCAwIDAgMS0xLjQuNiAxLjggMS44IDAgMCAxLTEuOC0xLjNsLS4xLS45Wm0uOCAwdi42bC4zLjVjMCAuMi4yLjMuNC40aDFsLjQtLjQuMi0uNXYtMS4ybC0uMi0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLS41LjFsLS40LjQtLjIuNS0uMS41Wm01LjIgMS40IDEuMi0zLjVoLjdsLTEuNSA0LjJoLS41bC4xLS43Wm0tMS0zLjUgMS4zIDMuNnYuNmgtLjRsLTEuNi00LjJoLjhaTTE1My44IDE0MS4zdjUuN2gtLjd2LTQuOGwtMS41LjV2LS42bDIuMS0uOGguMVptMyAzLS41LS4yLjMtMi44aDIuOXYuN2gtMi4zbC0uMiAxLjUuNC0uMS41LS4xLjguMS41LjQuMy42YTIuNSAyLjUgMCAwIDEgMCAxLjVjMCAuMy0uMi41LS4zLjYtLjEuMi0uMy4zLS42LjRhMiAyIDAgMCAxLS44LjJsLS42LS4xLS42LS4zLS4zLS41YTIgMiAwIDAgMS0uMi0uN2guN2wuMS41LjQuNGgxbC4zLS4zLjItLjRhMS44IDEuOCAwIDAgMCAwLTFjMC0uMiAwLS4zLS4yLS40YTEgMSAwIDAgMC0uMy0uM2gtMS4xbC0uMy4zWm0xMC4yLTN2NS43aC0uOGwtMi44LTQuNHY0LjRoLS44di01LjdoLjhsMi44IDQuNHYtNC40aC44Wm0xIDMuNmMwLS40IDAtLjYuMi0uOSAwLS4zLjItLjUuMy0uN2wuNi0uNGMuMy0uMi41LS4yLjgtLjIuMyAwIC42IDAgLjguMi4zIDAgLjUuMi42LjRsLjQuNy4yLjh2LjFjMCAuMyAwIC42LS4yLjlsLS40LjdhMS44IDEuOCAwIDAgMS0xLjQuNiAxLjggMS44IDAgMCAxLTEuOC0xLjN2LS45Wm0uNyAwIC4xLjYuMi41LjQuNGgxLjFsLjMtLjQuMy0uNXYtMS4yYzAtLjItLjItLjQtLjMtLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0uNS4xbC0uNC40LS4yLjV2LjVabTUuMyAxLjQgMS4yLTMuNWguN2wtMS41IDQuMmgtLjVsLjEtLjdabS0xLTMuNSAxLjIgMy42LjEuNmgtLjVsLTEuNS00LjJoLjdaIi8+PHBhdGggc3Ryb2tlPSIjMDAwIiBzdHJva2Utb3BhY2l0eT0iLjUiIHN0cm9rZS13aWR0aD0iLjQiIGQ9Ik0zNiAxNmgxNTJNMzYgMzMuM2gxNTJNMzYgNTAuNmgxNTJNMzYgNjcuOWgxNTJNMzYgODUuMWgxNTJNMzYgMTAyLjRoMTUyTTM2IDExOS43aDE1Mk0zNiAxMzdoMTUyIi8+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJiIiB4MT0iMTA4LjQiIHgyPSIxMDguNiIgeTE9IjEzNy41IiB5Mj0iLTQuMSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iLjEiIHN0b3AtY29sb3I9IiM0MDYzQ0MiLz48c3RvcCBvZmZzZXQ9Ii4xIiBzdG9wLWNvbG9yPSIjNjA4NUYwIi8+PHN0b3Agb2Zmc2V0PSIuNCIgc3RvcC1jb2xvcj0iIzYwODVGMCIvPjxzdG9wIG9mZnNldD0iLjQiIHN0b3AtY29sb3I9IiM4OUE0RjciLz48c3RvcCBvZmZzZXQ9Ii41IiBzdG9wLWNvbG9yPSIjODlBNEY3Ii8+PHN0b3Agb2Zmc2V0PSIuNSIgc3RvcC1jb2xvcj0iI0ZGQjY0MSIvPjxzdG9wIG9mZnNldD0iLjYiIHN0b3AtY29sb3I9IiNGRkI2NDEiLz48c3RvcCBvZmZzZXQ9Ii42IiBzdG9wLWNvbG9yPSIjRkI4NzJFIi8+PHN0b3Agb2Zmc2V0PSIuNyIgc3RvcC1jb2xvcj0iI0ZCODcyRSIvPjxzdG9wIG9mZnNldD0iLjciIHN0b3AtY29sb3I9IiNGNTU0MzgiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iYyIgeDE9IjEwOC40IiB4Mj0iMTA3LjkiIHkxPSIxMzciIHkyPSItNC4xIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIuMSIgc3RvcC1jb2xvcj0iIzIyNEFDMiIvPjxzdG9wIG9mZnNldD0iLjEiIHN0b3AtY29sb3I9IiMyQjU0Q0UiLz48c3RvcCBvZmZzZXQ9Ii40IiBzdG9wLWNvbG9yPSIjMkI1NENFIi8+PHN0b3Agb2Zmc2V0PSIuNCIgc3RvcC1jb2xvcj0iIzYwODNFQyIvPjxzdG9wIG9mZnNldD0iLjUiIHN0b3AtY29sb3I9IiM2MDgzRUMiLz48c3RvcCBvZmZzZXQ9Ii41IiBzdG9wLWNvbG9yPSIjRjg5RTBEIi8+PHN0b3Agb2Zmc2V0PSIuNiIgc3RvcC1jb2xvcj0iI0Y4OUUwRCIvPjxzdG9wIG9mZnNldD0iLjYiIHN0b3AtY29sb3I9IiNGNzc0MTAiLz48c3RvcCBvZmZzZXQ9Ii43IiBzdG9wLWNvbG9yPSIjRjc3NDEwIi8+PHN0b3Agb2Zmc2V0PSIuNyIgc3RvcC1jb2xvcj0iI0YwNDAyMiIvPjwvbGluZWFyR3JhZGllbnQ+PGNsaXBQYXRoIGlkPSJhIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMzYgMTZoMTUydjEyMUgzNnoiLz48L2NsaXBQYXRoPjwvZGVmcz48L3N2Zz4=", + "description": "Displays changes to temperature data over time visualized with color ranges.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.rangeChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.rangeChartWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: false,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries' }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-range-chart-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-range-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":2592000000,\"interval\":86400000,\"fixedTimewindow\":{\"startTimeMs\":1700577227898,\"endTimeMs\":1700663627898},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"dataZoom\":true,\"rangeColors\":[{\"from\":null,\"to\":-20,\"color\":\"#234CC7\"},{\"from\":-20,\"to\":0,\"color\":\"#305AD7\"},{\"from\":0,\"to\":10,\"color\":\"#7191EF\"},{\"from\":10,\"to\":20,\"color\":\"#FFA600\"},{\"from\":20,\"to\":30,\"color\":\"#F36900\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#D81838\"}],\"outOfRangeColor\":\"#ccc\",\"fillArea\":true,\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm\",\"lastUpdateAgo\":false,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Temperature\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"°C\",\"decimals\":0,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "temperature", + "environment", + "weather" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/temperature_range_chart_with_background.json b/application/src/main/data/json/system/widget_types/temperature_range_chart_with_background.json new file mode 100644 index 0000000000..bedb6dcb49 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/temperature_range_chart_with_background.json @@ -0,0 +1,31 @@ +{ + "fqn": "temperature_range_chart_with_background", + "name": "Temperature range chart with background", + "deprecated": false, + "image": "tb-image:dGVtcGVyYXR1cmVfcmFuZ2VfY2hhcnRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==:dGVtcGVyYXR1cmVfcmFuZ2VfY2hhcnRfd2l0aF9iYWNrZ3JvdW5kLnN2Zw==;data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgZmlsbD0ibm9uZSI+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9InVybCgjYSkiIHJ4PSI0Ii8+PGcgZmlsdGVyPSJ1cmwoI2IpIj48cmVjdCB3aWR0aD0iMTg4IiBoZWlnaHQ9IjE0OCIgeD0iNiIgeT0iNiIgZmlsbD0iI2ZmZiIgZmlsbC1vcGFjaXR5PSIuOCIgcng9IjQiLz48L2c+PGcgY2xpcC1wYXRoPSJ1cmwoI2MpIj48cGF0aCBmaWxsPSJ1cmwoI2QpIiBkPSJtNDMuOSA3MC42IDIyLjgtMzMuNSAxNSAxOS41IDUgMS4zTDEwNy42IDE2bDQuNCAxNi44IDIuMSA0Mi43aDExLjNsOC44LTI2LjNjMy41IDcuNSA3LjcgMjEuMSAxMS42IDM1LjhoMjIuOGwxLjUtNy43IDIuNyA3LjdoMTAuOGw1LjQtMjMuOFY4NWgtNS40bC00LjMgMTktNi41LTE5aC00LjJsLTEwLjEgNTJjLTIuNC0xMC4yLTcuMy0zMi0xMi43LTUySDM2di00bDcuOS0xMC40WiIvPjxwYXRoIHN0cm9rZT0idXJsKCNlKSIgc3Ryb2tlLXdpZHRoPSIuOSIgZD0ibTM2IDgxIDgtMTAuNCAyMy0zMy41IDE1LjIgMTkuNSA1IDEuMyAyMS00MS45IDQuMyAyMS4xIDIgMzguNGgxMWw4LjctMjYuOSAxMS45IDM4IDEyLjQgNTAuNCAxMS42LTU5LjUgOS43IDI3LjIgMi41LTExLjUgNi43LTMxLjgiLz48L2c+PHBhdGggZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIuNSIgZD0iTTE4LjMgMTd2LjdoLTR2LS40bDIuNS00aC41bC0uNiAxLjJMMTUgMTdoMy4zWm0tLjgtMy43VjE5aC0uN3YtNS43aC43Wm01LjIgMi40di45YzAgLjQgMCAuOC0uMiAxLjEgMCAuNC0uMi42LS4zLjgtLjIuMi0uNC40LS42LjRhMiAyIDAgMCAxLS43LjJsLS42LS4xYy0uMiAwLS40LS4xLS41LS4zLS4yIDAtLjMtLjItLjQtLjRsLS4zLS44di0zbC41LS43Yy4xLS4yLjMtLjQuNS0uNGwuOC0uMi42LjFhMS40IDEuNCAwIDAgMSAuOC43bC4zLjd2MVptLS44IDF2LTEuOGwtLjEtLjVhMSAxIDAgMCAwLS4zLS4zYzAtLjEtLjEtLjItLjMtLjJhMSAxIDAgMCAwLS4zIDAgMSAxIDAgMCAwLS41IDBsLS4zLjMtLjIuNi0uMS44djEuOGwuMi41LjIuMy4zLjJoLjhjLjIgMCAuMy0uMi40LS4zbC4yLS42di0uOFptMS44LTIuNWMwLS4xIDAtLjMuMi0uNWwuMy0uMy41LS4yYy4yIDAgLjQgMCAuNS4yYTEgMSAwIDAgMSAwIDEuNyAxIDEgMCAwIDEtLjUuMiAxIDEgMCAwIDEtLjUtLjIgMSAxIDAgMCAxLS4zLS4zIDEgMSAwIDAgMS0uMi0uNlptLjUgMGMwIC4yIDAgLjMuMi40bC4zLjIuNC0uMi4xLS40LS4xLS4zLS40LS4yYy0uMSAwLS4yIDAtLjMuMmwtLjIuM1ptNi4yIDNoLjhjMCAuNC0uMi43LS4zIDEtLjIuMi0uNC41LS43LjYtLjMuMi0uNy4zLTEuMi4zLS4zIDAtLjYgMC0uOS0uMmEyIDIgMCAwIDEtLjctLjVsLS40LS45LS4yLTF2LS42YzAtLjQgMC0uOC4yLTEuMSAwLS4zLjItLjYuNC0uOC4yLS4zLjUtLjUuOC0uNmwuOS0uMmMuNCAwIC44LjEgMSAuMy40LjEuNi40LjguNmwuMyAxaC0uOGEyIDIgMCAwIDAtLjItLjYgMSAxIDAgMCAwLS40LS41bC0uNy0uMS0uNy4xYy0uMiAwLS4zLjItLjQuNGEyIDIgMCAwIDAtLjMuNmwtLjEuOXYxLjNsLjMuNy41LjQuNi4yYy40IDAgLjYgMCAuOC0uMi4yIDAgLjMtLjIuNC0uNGwuMi0uN1pNMTUuNSAzM2guNWwuNy0uMi4zLS4zLjItLjUtLjEtLjYtLjQtLjMtLjUtLjFoLS42YS45LjkgMCAwIDAtLjUgMWgtLjdjMC0uMyAwLS42LjItLjguMi0uMy40LS41LjctLjZhMiAyIDAgMCAxIC45LS4yYy4zIDAgLjYgMCAuOS4yLjIgMCAuNC4zLjYuNWwuMi45LS4xLjRjMCAuMi0uMi40LS4zLjUtLjIuMS0uMy4zLS42LjNsLS44LjJoLS42di0uNVptMCAuNXYtLjRoLjZsMSAuMS41LjQuMy40VjM1LjNsLS40LjUtLjYuMy0uNy4xaC0uN2wtLjYtLjRhMS40IDEuNCAwIDAgMS0uNS0xLjJoLjdsLjEuNi40LjMuNi4xaC41bC40LS40LjEtLjYtLjEtLjZhLjkuOSAwIDAgMC0uNC0uM2wtLjctLjJoLS41Wm03LjItLjd2MWMwIC40IDAgLjgtLjIgMSAwIC40LS4yLjctLjMuOWwtLjYuNGEyIDIgMCAwIDEtLjcuMWgtLjZsLS41LS4zLS40LS41LS4zLS43di0zbC41LS44LjUtLjQuOC0uMWguNmExLjQgMS40IDAgMCAxIC44LjhsLjMuN3YxWm0tLjggMXYtMS43bC0uMS0uNWExIDEgMCAwIDAtLjMtLjRsLS4zLS4yYTEgMSAwIDAgMC0uMyAwIDEgMSAwIDAgMC0uNSAwbC0uMy40LS4yLjUtLjEuOHYxLjhsLjIuNS4yLjQuMy4yaC44bC40LS40LjItLjV2LS45Wm0xLjgtMi40YzAtLjIgMC0uNC4yLS41IDAtLjIuMi0uMy4zLS40bC41LS4xLjUuMWExIDEgMCAwIDEgMCAxLjggMSAxIDAgMCAxLS41IDAgMSAxIDAgMCAxLS41IDAgMSAxIDAgMCAxLS4zLS40IDEgMSAwIDAgMS0uMi0uNVptLjUgMGMwIC4xIDAgLjMuMi4zbC4zLjJjLjIgMCAuMyAwIC40LS4ybC4xLS4zLS4xLS40LS40LS4xLS4zLjEtLjIuNFptNi4yIDNoLjhsLS4zLjktLjcuNy0xLjIuMmMtLjMgMC0uNiAwLS45LS4yYTIgMiAwIDAgMS0uNy0uNWwtLjQtLjgtLjItMS4xVjMzYzAtLjQgMC0uNy4yLTEgMC0uNC4yLS43LjQtLjlsLjgtLjVjLjItLjIuNS0uMi45LS4ycy44IDAgMSAuMmMuNC4yLjYuNC44LjcuMS4zLjMuNi4zIDFoLS44YTIgMiAwIDAgMC0uMi0uNyAxIDEgMCAwIDAtLjQtLjRjLS4yLS4yLS40LS4yLS43LS4ybC0uNy4xLS40LjRhMiAyIDAgMCAwLS4zLjdsLS4xLjh2MS40bC4zLjYuNS41LjYuMS44LS4xLjQtLjUuMi0uN1pNMTguMiA1Mi43di42aC0zLjd2LS41bDEuOC0yLjEuNi0uNy4yLS40LjEtLjUtLjEtLjUtLjMtLjNhMSAxIDAgMCAwLS42LS4yYy0uMiAwLS40IDAtLjYuMmExIDEgMCAwIDAtLjQuNGwtLjEuNmgtLjdjMC0uNCAwLS42LjItMSAuMS0uMi4zLS40LjYtLjVhMiAyIDAgMCAxIDEtLjNjLjQgMCAuNyAwIDEgLjJsLjUuNWMuMi4zLjIuNS4yLjh2LjVsLS4zLjZhNy44IDcuOCAwIDAgMS0uOCAxbC0xLjUgMS42aDIuOVptNC41LTIuN3YuOWMwIC40IDAgLjgtLjIgMS4xIDAgLjQtLjIuNi0uMy44bC0uNi40YTIgMiAwIDAgMS0uNy4ybC0uNi0uMWMtLjIgMC0uNC0uMi0uNS0uM2wtLjQtLjUtLjMtLjd2LTNjLjItLjMuMy0uNi41LS43bC41LS41aDEuNGExLjQgMS40IDAgMCAxIC44LjdsLjMuN3YxWm0tLjggMXYtMS44bC0uMS0uNWExIDEgMCAwIDAtLjMtLjNjMC0uMS0uMS0uMi0uMy0uMmExIDEgMCAwIDAtLjMgMCAxIDEgMCAwIDAtLjUgMGwtLjMuMy0uMi42LS4xLjh2MS43bC4yLjYuMi4zLjMuMmguOGwuNC0uMy4yLS42VjUxWm0xLjgtMi41YzAtLjIgMC0uMy4yLS41bC4zLS4zLjUtLjJjLjIgMCAuNCAwIC41LjJhMSAxIDAgMCAxIDAgMS43IDEgMSAwIDAgMS0uNS4xIDEgMSAwIDAgMS0uNS0uMSAxIDEgMCAwIDEtLjMtLjQgMSAxIDAgMCAxLS4yLS41Wm0uNSAwYzAgLjIgMCAuMy4yLjRsLjMuMS40LS4xLjEtLjQtLjEtLjMtLjQtLjJhLjUuNSAwIDAgMC0uNS41Wm02LjIgM2guOGwtLjMgMWMtLjIuMi0uNC41LS43LjYtLjMuMi0uNy4zLTEuMi4zbC0uOS0uMmEyIDIgMCAwIDEtLjctLjZsLS40LS44LS4yLTF2LS42YzAtLjQgMC0uOC4yLTEuMSAwLS4zLjItLjYuNC0uOWwuOC0uNS45LS4yYy40IDAgLjguMSAxIC4zLjQuMS42LjMuOC42LjEuMy4zLjYuMyAxaC0uOGEyIDIgMCAwIDAtLjItLjcgMSAxIDAgMCAwLS40LS40bC0uNy0uMmMtLjMgMC0uNSAwLS43LjItLjIgMC0uMy4yLS40LjRhMiAyIDAgMCAwLS4zLjZsLS4xLjl2MS4zbC4zLjcuNS40LjYuMi44LS4yLjQtLjQuMi0uN1pNMTYuOCA2NC43djUuN2gtLjd2LTQuOGwtMS40LjV2LS42bDItLjhoLjFabTUuOSAyLjR2LjljMCAuNSAwIC45LS4yIDEuMiAwIC4zLS4yLjYtLjMuOGwtLjYuNGEyIDIgMCAwIDEtLjcuMWgtLjZsLS41LS4zLS40LS41LS4zLS43di0zbC41LS44LjUtLjQuOC0uMWguNmExLjQgMS40IDAgMCAxIC44LjhsLjMuN3YxWm0tLjggMXYtMS44bC0uMS0uNWExIDEgMCAwIDAtLjMtLjNsLS4zLS4yYTEgMSAwIDAgMC0uMyAwIDEgMSAwIDAgMC0uNSAwbC0uMy40LS4yLjUtLjEuOHYxLjhsLjIuNS4yLjMuMy4zaC44bC40LS40LjItLjZ2LS44Wm0xLjgtMi40YzAtLjIgMC0uNC4yLS41IDAtLjIuMi0uMy4zLS40bC41LS4xLjUuMWExIDEgMCAwIDEgMCAxLjcgMSAxIDAgMCAxLS41LjIgMSAxIDAgMCAxLS41LS4yIDEgMSAwIDAgMS0uMy0uMyAxIDEgMCAwIDEtLjItLjVabS41IDBjMCAuMSAwIC4yLjIuM2wuMy4yYy4yIDAgLjMgMCAuNC0uMmwuMS0uMy0uMS0uNC0uNC0uMS0uMy4xLS4yLjRabTYuMiAzaC44bC0uMy45LS43LjctMS4yLjJjLS4zIDAtLjYgMC0uOS0uMmEyIDIgMCAwIDEtLjctLjVMMjcgNjlsLS4yLTEuMXYtLjZjMC0uNCAwLS44LjItMSAwLS40LjItLjcuNC0xbC44LS40Yy4yLS4yLjUtLjIuOS0uMnMuOCAwIDEgLjJjLjQuMi42LjQuOC43LjEuMy4zLjYuMyAxaC0uOGEyIDIgMCAwIDAtLjItLjcgMSAxIDAgMCAwLS40LS41bC0uNy0uMS0uNy4xLS40LjRhMiAyIDAgMCAwLS4zLjdsLS4xLjh2MS40bC4zLjYuNS40LjYuMmMuNCAwIC42IDAgLjgtLjIuMiAwIC4zLS4yLjQtLjRsLjItLjdaTTIzIDg0LjN2MmwtLjQuOC0uNi40YTIgMiAwIDAgMS0uOC4xaC0uNmwtLjUtLjMtLjMtLjUtLjMtLjd2LTNsLjQtLjguNi0uNGgxLjRhMS40IDEuNCAwIDAgMSAuOC43bC4zLjd2MVptLS43IDF2LTEuOEwyMiA4M2ExIDEgMCAwIDAtLjItLjRsLS4zLS4xYTEgMSAwIDAgMC0uNC0uMSAxIDEgMCAwIDAtLjQgMGwtLjQuNC0uMi41Vjg2bC4yLjVjMCAuMiAwIC4zLjIuNGwuMy4yaC44Yy4xLS4xLjMtLjIuMy0uNGwuMy0uNXYtLjhabTEuOC0yLjVjMC0uMiAwLS4zLjItLjVsLjMtLjQuNS0uMS41LjFhMSAxIDAgMCAxIDAgMS44IDEgMSAwIDAgMS0uNS4xIDEgMSAwIDAgMS0uNS0uMSAxIDEgMCAwIDEtLjMtLjQgMSAxIDAgMCAxLS4yLS41Wm0uNSAwYzAgLjIgMCAuMy4yLjRsLjMuMS40LS4xLjEtLjQtLjEtLjMtLjQtLjJhLjUuNSAwIDAgMC0uNS41Wm02LjIgM2guOGwtLjMgMS0uNy42Yy0uMy4yLS43LjItMS4yLjJsLS45LS4xYTIgMiAwIDAgMS0uNy0uNmwtLjQtLjhjLS4yLS4zLS4yLS43LS4yLTF2LS43YzAtLjQgMC0uNy4yLTEgMC0uNC4yLS42LjQtLjlsLjctLjUgMS0uMmMuNCAwIC44IDAgMSAuMi40LjIuNi40LjguN2wuMyAxaC0uOGEyIDIgMCAwIDAtLjItLjcgMSAxIDAgMCAwLS40LS40bC0uNy0uMmMtLjMgMC0uNSAwLS43LjItLjIgMC0uMy4yLS41LjRhMiAyIDAgMCAwLS4zLjZ2Mi4ybC4zLjYuNS41LjYuMS44LS4xLjQtLjUuMi0uNlpNMTQgMTAydi42aC0xLjl2LS42aDJabTMuMi0zdjUuN2gtLjdWMTAwbC0xLjUuNXYtLjZsMi0uOGguMlptNS44IDIuNHYuOWwtLjEgMS4yLS40LjdjLS4yLjItLjMuNC0uNi41YTIgMiAwIDAgMS0uNyAwaC0uNmMtLjIgMC0uNC0uMS0uNS0uMy0uMiAwLS4zLS4yLS40LS40bC0uMi0uNy0uMS0xdi0uOWwuMS0xLjIuNC0uNy41LS40LjgtLjIuNi4xYTEuNCAxLjQgMCAwIDEgLjkuN2wuMi43djFabS0uNyAxdi0xLjhsLS4yLS41YTEgMSAwIDAgMC0uMi0uM2wtLjMtLjJhMSAxIDAgMCAwLS40IDAgMSAxIDAgMCAwLS41IDBsLS4zLjMtLjIuNnYyLjZsLjEuNS4yLjMuMy4yLjQuMS41LS4xLjMtLjMuMi0uNnYtLjhaTTI0IDEwMGwuMS0uNS40LS40LjUtLjJjLjIgMCAuMyAwIC41LjJhMSAxIDAgMCAxIDAgMS43IDEgMSAwIDAgMS0uNS4yIDEgMSAwIDAgMS0uNS0uMiAxIDEgMCAwIDEtLjQtLjMgMSAxIDAgMCAxLS4xLS41Wm0uNSAwIC4xLjMuNC4yYy4xIDAgLjIgMCAuMy0uMmwuMi0uM2MwLS4yIDAtLjMtLjItLjRsLS4zLS4yYy0uMiAwLS4zIDAtLjQuMmwtLjEuNFptNi4yIDNoLjdsLS4zLjljLS4yLjMtLjQuNS0uNy42LS4zLjItLjcuMy0xLjEuMy0uNCAwLS43IDAtMS0uMmEyIDIgMCAwIDEtLjYtLjVjLS4yLS4zLS40LS41LS41LS45bC0uMi0xdi0uNmwuMi0xIC41LTEgLjctLjUgMS0uMmMuNCAwIC43LjEgMSAuM2wuNy43Yy4yLjIuMy42LjMgMWgtLjdhMiAyIDAgMCAwLS4yLS43IDEgMSAwIDAgMC0uNS0uNWwtLjctLjEtLjYuMS0uNS40YTIgMiAwIDAgMC0uMy43bC0uMS44VjEwMi45bC4zLjcuNS40LjcuMmMuMyAwIC41IDAgLjctLjIuMiAwIC40LS4yLjUtLjRsLjItLjdaTTE0IDExOS4xdi42aC0xLjl2LS42aDJabTQuNSAyLjJ2LjZoLTMuN3YtLjZsMS45LTIgLjUtLjcuMy0uNXYtLjlsLS40LS40YTEgMSAwIDAgMC0uNS0uMWwtLjcuMWExIDEgMCAwIDAtLjQuNHYuNmgtLjhjMC0uMyAwLS42LjItLjguMS0uMy40LS41LjYtLjdhMiAyIDAgMCAxIDEtLjJjLjQgMCAuNyAwIDEgLjJsLjYuNS4yLjgtLjEuNWMwIC4yLS4yLjQtLjMuNWE3LjggNy44IDAgMCAxLS44IDFsLTEuNCAxLjdoMi44Wm00LjUtMi43di44bC0uMSAxLjItLjQuOC0uNi40YTIgMiAwIDAgMS0uNy4xaC0uNmwtLjUtLjMtLjQtLjUtLjItLjctLjEtMXYtLjhsLjEtMS4yLjQtLjguNS0uNC44LS4xaC42YTEuNCAxLjQgMCAwIDEgLjkuOGwuMi43djFabS0uNyAxVjExNy43bC0uMi0uNWExIDEgMCAwIDAtLjItLjRsLS4zLS4yYTEgMSAwIDAgMC0uNCAwIDEgMSAwIDAgMC0uNSAwbC0uMy40LS4yLjV2Mi42bC4xLjUuMi40LjMuMmguOWwuMy0uNC4yLS41di0uOVpNMjQgMTE3bC4xLS41LjQtLjRhMSAxIDAgMCAxIDEgMCAxIDEgMCAwIDEgMCAxLjggMSAxIDAgMCAxLS41LjEgMSAxIDAgMCAxLS41LS4xIDEgMSAwIDAgMS0uNC0uNCAxIDEgMCAwIDEtLjEtLjVabS41IDAgLjEuNC40LjEuMy0uMS4yLS40YzAtLjEgMC0uMy0uMi0uNGwtLjMtLjEtLjQuMS0uMS40Wm02LjIgM2guN2wtLjMgMS0uNy42Yy0uMy4yLS43LjItMS4xLjItLjQgMC0uNyAwLTEtLjJhMiAyIDAgMCAxLS42LS41bC0uNS0uOC0uMi0xLjF2LS42bC4yLTFjLjEtLjQuMy0uNy41LS45bC43LS41IDEtLjJjLjQgMCAuNyAwIDEgLjJsLjcuN2MuMi4zLjMuNi4zIDFoLS43YTIgMiAwIDAgMC0uMi0uNyAxIDEgMCAwIDAtLjUtLjRsLS43LS4yYy0uMiAwLS40IDAtLjYuMi0uMiAwLS40LjItLjUuNGEyIDIgMCAwIDAtLjMuNmwtLjEuOHYxLjRsLjMuNi41LjUuNy4xLjctLjEuNS0uNS4yLS43Wk0xNCAxMzYuM3YuNmgtMS45di0uNmgyWm0xLjktLjVoLjVsLjYtLjEuNC0uNHYtMWMwLS4yLS4yLS4zLS4zLS40SDE2bC0uNC40LS4xLjVoLS44YzAtLjMuMS0uNi4zLS44bC42LS42YTIgMiAwIDAgMSAuOS0uMmwuOS4yYy4zLjEuNS4zLjYuNmwuMi44di41bC0uNC41LS41LjMtLjguMWgtLjZ2LS40Wm0wIC42di0uNGgxLjVsLjUuNGMuMi4yLjMuMy4zLjVsLjEuNS0uMS43LS40LjUtLjYuNGgtMS40bC0uNi0uM2ExLjQgMS40IDAgMCAxLS41LTEuMmguN2MwIC4yIDAgLjQuMi41IDAgLjIuMi4zLjMuNGgxLjJjLjEtLjEuMy0uMi4zLS40bC4yLS42YzAtLjIgMC0uNC0uMi0uNmEuOS45IDAgMCAwLS40LS4zbC0uNi0uMWgtLjVabTctLjd2Mi4xbC0uNC43Yy0uMi4yLS4zLjQtLjYuNGEyIDIgMCAwIDEtLjcuMmwtLjYtLjFjLS4yIDAtLjQtLjEtLjUtLjMtLjIgMC0uMy0uMi0uNC0uNGwtLjItLjgtLjEtMXYtLjhsLjEtMS4yLjQtLjdjLjEtLjIuMy0uNC41LS40bC44LS4yLjYuMWExLjQgMS40IDAgMCAxIC45LjdsLjIuN3YxWm0tLjYgMXYtMS44bC0uMi0uNWExIDEgMCAwIDAtLjItLjNsLS4zLS4yYTEgMSAwIDAgMC0uNCAwIDEgMSAwIDAgMC0uNSAwbC0uMy4zLS4yLjZ2Mi42bC4xLjUuMi4zLjMuMmguOWwuMy0uMy4yLS42di0uOFptMS44LTIuNS4xLS41LjQtLjMuNS0uMmMuMiAwIC4zIDAgLjUuMmExIDEgMCAwIDEgMCAxLjcgMSAxIDAgMCAxLS41LjIgMSAxIDAgMCAxLS41LS4yIDEgMSAwIDAgMS0uNC0uMyAxIDEgMCAwIDEtLjEtLjZabS41IDAgLjEuNC40LjIuMy0uMi4yLS40YS41LjUgMCAwIDAtLjUtLjVjLS4yIDAtLjMgMC0uNC4ybC0uMS4zWm02LjIgM2guN2MwIC40LS4xLjctLjMgMS0uMi4yLS40LjUtLjcuNi0uMy4yLS43LjMtMS4xLjMtLjQgMC0uNyAwLTEtLjJhMiAyIDAgMCAxLS42LS41bC0uNS0uOS0uMi0xdi0uNmwuMi0xLjEuNS0uOGMuMi0uMy40LS41LjctLjZsMS0uMmMuNCAwIC43LjEgMSAuMy4zLjEuNS40LjcuNi4yLjMuMy43LjMgMWgtLjdhMiAyIDAgMCAwLS4yLS42IDEgMSAwIDAgMC0uNS0uNWwtLjctLjEtLjYuMWMtLjIgMC0uNC4yLS41LjRhMiAyIDAgMCAwLS4zLjZsLS4xLjl2MS4zbC4zLjcuNS40LjcuMmMuMyAwIC41IDAgLjctLjIuMiAwIC40LS4yLjUtLjRsLjItLjdaTTQwLjggMTQxLjN2NS43aC0uN3YtNC44bC0xLjUuNXYtLjZsMi4xLS44aC4xWm00LjUgMHY1LjdoLS43di00LjhsLTEuNS41di0uNmwyLjEtLjhoLjFabTguNyAwdjUuN2gtLjhsLTIuOC00LjR2NC40aC0uOHYtNS43aC44bDIuOCA0LjR2LTQuNGguOFptMSAzLjZjMC0uNCAwLS42LjItLjkgMC0uMy4yLS41LjMtLjdsLjYtLjRjLjMtLjIuNS0uMi44LS4yLjMgMCAuNiAwIC44LjIuMyAwIC41LjIuNi40bC40LjcuMi44di4xYzAgLjMgMCAuNi0uMi45bC0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zdi0uOVptLjcgMCAuMS42LjIuNS40LjRoMS4xbC4zLS40LjMtLjV2LTEuMmMwLS4yLS4yLS40LS4zLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41di41Wm01LjMgMS40IDEuMi0zLjVoLjdsLTEuNSA0LjJoLS41bC4xLS43Wm0tMS0zLjUgMS4yIDMuNi4xLjZoLS41bC0xLjUtNC4yaC43Wk02OSAxNDEuM3Y1LjdoLS43di00LjhsLTEuNC41di0uNmwyLS44aC4yWm02IDUuMXYuNmgtMy44di0uNWwxLjktMiAuNS0uNy4zLS41di0xbC0uNC0uM2ExIDEgMCAwIDAtLjYtLjJjLS4yIDAtLjQgMC0uNi4yYTEgMSAwIDAgMC0uNC40bC0uMS42aC0uN2MwLS4zIDAtLjYuMi0uOS4xLS4zLjMtLjUuNi0uNmEyIDIgMCAwIDEgMS0uM2wxIC4yYy4yLjIuNC4zLjUuNi4yLjIuMy40LjMuOGwtLjEuNS0uMy41YTcuOCA3LjggMCAwIDEtLjggMWwtMS40IDEuNmgyLjhabTcuMi01djUuNmgtLjdsLTIuOS00LjR2NC40SDc4di01LjdoLjdsMi45IDQuNHYtNC40aC43Wm0xIDMuNS4yLS45YzAtLjMuMi0uNS40LS43bC42LS40Yy4yLS4yLjUtLjIuOC0uMi4zIDAgLjYgMCAuOC4yLjIgMCAuNC4yLjYuNGwuNC43LjEuOHYuMWwtLjEuOS0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zbC0uMS0uOVptLjggMHYuNmwuMy41YzAgLjIuMi4zLjQuNGgxbC40LS40LjItLjV2LTEuMmwtLjItLjVhMSAxIDAgMCAwLS45LS41IDEgMSAwIDAgMC0uNS4xbC0uNC40LS4yLjUtLjEuNVptNS4yIDEuNCAxLjItMy41aC43bC0xLjUgNC4yaC0uNWwuMS0uN1ptLTEtMy41IDEuMyAzLjZ2LjZoLS40bC0xLjYtNC4yaC44Wk05Ny4zIDE0MS4zdjUuN2gtLjd2LTQuOGwtMS41LjV2LS42bDIuMS0uOGguMVptMy4yIDIuNWguNWwuNi0uMS40LS40LjEtLjV2LS41bC0uNC0uNGgtMS4xYS45LjkgMCAwIDAtLjUuOWgtLjdjMC0uMyAwLS42LjItLjhsLjYtLjZhMiAyIDAgMCAxIDEtLjJsLjguMmMuMy4xLjUuMy42LjYuMi4yLjMuNS4zLjhsLS4xLjUtLjQuNS0uNS4zLS44LjFoLS42di0uNFptMCAuNnYtLjRoMS42bC41LjQuMy41djEuMmwtLjUuNS0uNS40aC0xLjVsLS41LS4zYTEuNCAxLjQgMCAwIDEtLjYtMS4yaC44di41bC41LjRoMS4xbC40LS40LjEtLjYtLjEtLjZhLjkuOSAwIDAgMC0uNS0uM2wtLjYtLjFoLS41Wm0xMC0zdjUuNmgtLjhsLTIuOC00LjR2NC40aC0uOHYtNS43aC44bDIuOCA0LjR2LTQuNGguOFptMSAzLjVjMC0uNCAwLS42LjItLjkgMC0uMy4yLS41LjMtLjdsLjYtLjRjLjMtLjIuNS0uMi44LS4yLjMgMCAuNiAwIC44LjIuMyAwIC41LjIuNi40bC40LjcuMi44di4xYzAgLjMgMCAuNi0uMi45bC0uNC43YTEuOCAxLjggMCAwIDEtMS40LjYgMS44IDEuOCAwIDAgMS0xLjgtMS4zdi0uOVptLjcgMCAuMS42LjIuNS40LjRoMS4xbC4zLS40LjMtLjV2LTEuMmMwLS4yLS4yLS40LS4zLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41di41Wm01LjMgMS40IDEuMi0zLjVoLjdsLTEuNSA0LjJoLS41bC4xLS43Wm0tMS0zLjUgMS4yIDMuNi4xLjZoLS41bC0xLjUtNC4yaC43Wk0xMjUuNiAxNDEuM3Y1LjdoLS44di00LjhsLTEuNC41di0uNmwyLS44aC4yWm02IDMuOHYuNmgtNC4ydi0uNGwyLjYtNGguNmwtLjcgMS4yLTEuNyAyLjZoMy4zWm0tLjktMy44djUuN2gtLjd2LTUuN2guN1ptOCAwdjUuN2gtLjdsLTIuOS00LjR2NC40aC0uN3YtNS43aC43bDIuOSA0LjR2LTQuNGguN1ptMSAzLjYuMi0uOWMwLS4zLjItLjUuNC0uN2wuNi0uNGMuMi0uMi41LS4yLjgtLjIuMyAwIC42IDAgLjguMi4yIDAgLjQuMi42LjRsLjQuNy4xLjh2LjFsLS4xLjktLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM2wtLjEtLjlabS44IDB2LjZsLjMuNWMwIC4yLjIuMy40LjRoMWwuNC0uNC4yLS41di0xLjJsLS4yLS41YTEgMSAwIDAgMC0uOS0uNSAxIDEgMCAwIDAtLjUuMWwtLjQuNC0uMi41LS4xLjVabTUuMiAxLjQgMS4yLTMuNWguN2wtMS41IDQuMmgtLjVsLjEtLjdabS0xLTMuNSAxLjMgMy42di42aC0uNGwtMS42LTQuMmguOFpNMTUzLjggMTQxLjN2NS43aC0uN3YtNC44bC0xLjUuNXYtLjZsMi4xLS44aC4xWm0zIDMtLjUtLjIuMy0yLjhoMi45di43aC0yLjNsLS4yIDEuNS40LS4xLjUtLjEuOC4xLjUuNC4zLjZhMi41IDIuNSAwIDAgMSAwIDEuNWMwIC4zLS4yLjUtLjMuNi0uMS4yLS4zLjMtLjYuNGEyIDIgMCAwIDEtLjguMmwtLjYtLjEtLjYtLjMtLjMtLjVhMiAyIDAgMCAxLS4yLS43aC43bC4xLjUuNC40aDFsLjMtLjMuMi0uNGExLjggMS44IDAgMCAwIDAtMWMwLS4yIDAtLjMtLjItLjRhMSAxIDAgMCAwLS4zLS4zaC0xLjFsLS4zLjNabTEwLjItM3Y1LjdoLS44bC0yLjgtNC40djQuNGgtLjh2LTUuN2guOGwyLjggNC40di00LjRoLjhabTEgMy42YzAtLjQgMC0uNi4yLS45IDAtLjMuMi0uNS4zLS43bC42LS40Yy4zLS4yLjUtLjIuOC0uMi4zIDAgLjYgMCAuOC4yLjMgMCAuNS4yLjYuNGwuNC43LjIuOHYuMWMwIC4zIDAgLjYtLjIuOWwtLjQuN2ExLjggMS44IDAgMCAxLTEuNC42IDEuOCAxLjggMCAwIDEtMS44LTEuM3YtLjlabS43IDAgLjEuNi4yLjUuNC40aDEuMWwuMy0uNC4zLS41di0xLjJjMC0uMi0uMi0uNC0uMy0uNWExIDEgMCAwIDAtLjktLjUgMSAxIDAgMCAwLS41LjFsLS40LjQtLjIuNXYuNVptNS4zIDEuNCAxLjItMy41aC43bC0xLjUgNC4yaC0uNWwuMS0uN1ptLTEtMy41IDEuMiAzLjYuMS42aC0uNWwtMS41LTQuMmguN1oiLz48cGF0aCBzdHJva2U9IiMwMDAiIHN0cm9rZS1vcGFjaXR5PSIuNSIgc3Ryb2tlLXdpZHRoPSIuNCIgZD0iTTM2IDE2aDE1Mk0zNiAzMy4zaDE1Mk0zNiA1MC42aDE1Mk0zNiA2Ny45aDE1Mk0zNiA4NS4xaDE1Mk0zNiAxMDIuNGgxNTJNMzYgMTE5LjdoMTUyTTM2IDEzN2gxNTIiLz48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImQiIHgxPSIxMDguNCIgeDI9IjEwOC42IiB5MT0iMTM3LjUiIHkyPSItNC4xIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIuMSIgc3RvcC1jb2xvcj0iIzQwNjNDQyIvPjxzdG9wIG9mZnNldD0iLjEiIHN0b3AtY29sb3I9IiM2MDg1RjAiLz48c3RvcCBvZmZzZXQ9Ii40IiBzdG9wLWNvbG9yPSIjNjA4NUYwIi8+PHN0b3Agb2Zmc2V0PSIuNCIgc3RvcC1jb2xvcj0iIzg5QTRGNyIvPjxzdG9wIG9mZnNldD0iLjUiIHN0b3AtY29sb3I9IiM4OUE0RjciLz48c3RvcCBvZmZzZXQ9Ii41IiBzdG9wLWNvbG9yPSIjRkZCNjQxIi8+PHN0b3Agb2Zmc2V0PSIuNiIgc3RvcC1jb2xvcj0iI0ZGQjY0MSIvPjxzdG9wIG9mZnNldD0iLjYiIHN0b3AtY29sb3I9IiNGQjg3MkUiLz48c3RvcCBvZmZzZXQ9Ii43IiBzdG9wLWNvbG9yPSIjRkI4NzJFIi8+PHN0b3Agb2Zmc2V0PSIuNyIgc3RvcC1jb2xvcj0iI0Y1NTQzOCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJlIiB4MT0iMTA4LjQiIHgyPSIxMDcuOSIgeTE9IjEzNyIgeTI9Ii00LjEiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9Ii4xIiBzdG9wLWNvbG9yPSIjMjI0QUMyIi8+PHN0b3Agb2Zmc2V0PSIuMSIgc3RvcC1jb2xvcj0iIzJCNTRDRSIvPjxzdG9wIG9mZnNldD0iLjQiIHN0b3AtY29sb3I9IiMyQjU0Q0UiLz48c3RvcCBvZmZzZXQ9Ii40IiBzdG9wLWNvbG9yPSIjNjA4M0VDIi8+PHN0b3Agb2Zmc2V0PSIuNSIgc3RvcC1jb2xvcj0iIzYwODNFQyIvPjxzdG9wIG9mZnNldD0iLjUiIHN0b3AtY29sb3I9IiNGODlFMEQiLz48c3RvcCBvZmZzZXQ9Ii42IiBzdG9wLWNvbG9yPSIjRjg5RTBEIi8+PHN0b3Agb2Zmc2V0PSIuNiIgc3RvcC1jb2xvcj0iI0Y3NzQxMCIvPjxzdG9wIG9mZnNldD0iLjciIHN0b3AtY29sb3I9IiNGNzc0MTAiLz48c3RvcCBvZmZzZXQ9Ii43IiBzdG9wLWNvbG9yPSIjRjA0MDIyIi8+PC9saW5lYXJHcmFkaWVudD48Y2xpcFBhdGggaWQ9ImMiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0zNiAxNmgxNTJ2MTIxSDM2eiIvPjwvY2xpcFBhdGg+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHBhdHRlcm5Db250ZW50VW5pdHM9Im9iamVjdEJvdW5kaW5nQm94Ij48dXNlIHhsaW5rOmhyZWY9IiNmIiB0cmFuc2Zvcm09Im1hdHJpeCguMDA1MDMxNDUgMCAwIC4wMDYyODkzMSAwIDApIi8+PC9wYXR0ZXJuPjxmaWx0ZXIgaWQ9ImIiIHdpZHRoPSIxOTIiIGhlaWdodD0iMTUyIiB4PSI0IiB5PSI0IiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PGZlRmxvb2QgZmxvb2Qtb3BhY2l0eT0iMCIgcmVzdWx0PSJCYWNrZ3JvdW5kSW1hZ2VGaXgiLz48ZmVHYXVzc2lhbkJsdXIgaW49IkJhY2tncm91bmRJbWFnZUZpeCIgc3RkRGV2aWF0aW9uPSIxIi8+PGZlQ29tcG9zaXRlIGluMj0iU291cmNlQWxwaGEiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJlZmZlY3QxX2JhY2tncm91bmRCbHVyXzQ2NTBfODA3OSIvPjxmZUJsZW5kIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfYmFja2dyb3VuZEJsdXJfNDY1MF84MDc5IiByZXN1bHQ9InNoYXBlIi8+PC9maWx0ZXI+PGltYWdlIHhsaW5rOmhyZWY9ImRhdGE6aW1hZ2UvanBlZztiYXNlNjQsLzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3QkRBQUlCQVFFQkFRSUJBUUVDQWdJQ0FnUURBZ0lDQWdVRUJBTUVCZ1VHQmdZRkJnWUdCd2tJQmdjSkJ3WUdDQXNJQ1FvS0Nnb0tCZ2dMREFzS0RBa0tDZ3IvMndCREFRSUNBZ0lDQWdVREF3VUtCd1lIQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnb0tDZ29LQ2dvS0Nnci93Z0FSQ0FDZkFNY0RBUkVBQWhFQkF4RUIvOFFBSFFBQUFnTUJBUUVCQVFBQUFBQUFBQUFBQXdRQ0JRWUhBUUFJQ2YvRUFCc0JBQU1CQVFFQkFRQUFBQUFBQUFBQUFBQUJBZ01FQlFZSC85b0FEQU1CQUFJUUF4QUFBQUQreDNVUmFpTXlQVUdTZ3lGTlluNXBjUG1EcEwwQnNCYVhZdGNLNnl2U1d1UVhLZHlyY2kxUDBMOG42WVFiUkJsWGN2VFVRcmRFekpFTUZqdGJiNVd0UU8xSmk5QWFGYUY2UzJrcmFKZTRXcEs2WnIwZm9YNWYwUEVMMEhSbmRvc1phdzBiVkhhT0ZobGNOSU1OdEFXZ1dsNkY2RjZBVWxxUzJxWHRBdVFNN3o4MzFJVUpXVmxTQ2hHcHI2U0ZxN3p1cHFMcUJ4TTVWak5RYUF3RmdLUUtGMmdVSjZDMnNDb0N6bzNoOU5IdG5VM05ncXEzTmxNK29hVHQ0cTJpcSs1c0lxWURwaFlHa0NnSWw2UU5RTFN0QzlBS0FXdVlSZ2VDbjBuWFkzMExtMGd6VzQ2UWJSYU9Gakl6TlBTUWJFd1ZBYUFNQXdOSmJRQlNDMEdtcloxYnhxd3ZUbm50RFNZMFJxMGl2UlJZOURrTnBCMlJZRUlDQU1WQUxBZ0toZGkyZ0hSTGdGdnBmbFB4R0o2STMvTlM0Q2FEVDlhWWxzeUVDRmtLQW9IU0VBeGpBTkFiQU1YWUNoZXlGSFZ2THFJOHZjdEMwR1I4QzRlTUtqNEJoRmtHQ1lJSVVEQ0ZBd0N3TGE5TmVtSmdtZFE0endhaEdkWmRwdHlqeWVDaUhqUEVRYkdNWXh1UmhCdURVR0NvQlZCR0VBTWc2NlR5eEVYbzBwbXNwMmNJaURpK0R3SWpnaURJRmpaQUJOUWJHd1ZBMnhVQ1ZDb0hSMFRqeWlqNFh3aGppRWdtMTRqeE9BZURnbkZzYUlNRzJOMElRaWhBTmdxRjZxRkxvV0dYaVh3ZkplaThEeG5pSUpSVGlIZzREaU1ZUWRyc3JkU3MwRkthdEF4a0dJZFJxZG80c0JzaUhndkpCMFJIQUFpRXlBS1VMVUlWU1ZVcFFCaTdhZGlPa0tXdzBLMGxtSTNOdjUzTjBETG9vdG4wVEdnaTlDbkN5SEFGcVEweGduVkJiVXBMdEFiRXhLeFBTRXFtU3BaRlByQ2Jqa2Y1MzNWdlpoUzlmS1hWVm0rSXVpTUQyZWYwUTZlKzhYdDZEYXJ1aFZWcTlGTUJhU3BOVXUrUzB2aDFjc0U3Yms2WE9pYlByd0xGMCsySE52eWoxOVh6OWJnV2VlamRwdUJ1U0E2M2JMalgxbmk4NTlyeUxqazd0MXplclBHODU2SG1lNTlPUHZEUzQ5LzV0OXo0N1Y4UHAzdnpmdGNwK2k4QXBPbXk3ZWhjUGZ3SHE4cXlTMW5GMDdMaTZPaWVaM2FyajIwUEk3V2JKbFJYTkoyNVYzYVpEMGVUSGQzUGgrdmhieDJ3L1R3N0U3OC9yblRiNTFNRHRSeWYwdkV5OHFwMHcvUzN5WDJnYnF2MnhzcUtmWEszejFNVm9lVFhhZWZ0ci9QNmIva3ZaY1dtbDRkWndWRytYTHZXODNuZnJjR1I5WGp5UG9aR3c2dU8rajV1UzdjTzZlQjYyczA3OUx3Nzg3NDlITzNQdi93ZjBtdjdYemYydlA0OTdmazBXMk5aV2JxcHZMU1NQVmRoaHZiWWFYdklYL0hXejRlbTM1V2ZFcXVua3lIVnkxWG9aNTd0ZUc5UERWK1YxLy9FQUNRUUFBRUVBZ0lEQVFFQUF3QUFBQUFBQUFFQ0F3UVJCUVlBRXhBU0ZCVWdCeFpRLzlvQUNBRUJBQUVDQXU4eUVKZUNsMTRCcFpUd3BxaVBXaUNLS1NLcnJlWlBQV08yZkh1T0pPZlhpV0hsb1RWVVJSU1FRUVUrbzRlSlBIWFBkNUxiNlVJa2dMZFMxVlZSQkJGRk5WUjU5Q24xWkRqbVFZNDFBWHhVL3dDZG9vY0hLcWlDS3BRb2luWi9hakV3MkZaYUJqMjF0WVdOQmRMSGlxcXFxcW9wOWZXbDdLMU1iTVdMQUw4ZzVuOUw2UGpESmoxeXVVUlZWVkVBUHR6OWNiQXg2TW1NdXVmOUtaNFdGZjFWVVJWVlJpS2crdlNJaUkzbzIwcGtOLzFWVlZFVjYxNG5zUTVQaFRmcFg5VlhLcXZVcEtmWHcrMzBvY1NyeFhpcXFxcnpWVlZlWEdHV1VLUXYrNnFxcXFxcXFxcWlrSkNQK0RaY1ZMVk4rNzd2MFAwVGtWNUx2NysvNk8vdjdlNzZmcSt3ekRMVStYUzUyZGluUzkyOXFuTDlvMytTMmRva2JNaVk3UFZLT1g3Kzd2N2V4VHZZcHozN2ZvTGluVEo3eXRUaW5sNnVkT09nalY1ZXRPWTFlR2diTkMyNXZjVTdTenRmNjY1UmRmZFRLWE1sWjl2Ykc5aUdXUmtQMFhKcU5uR1FibUNYOVNab2tkeTVPUjJxVHRzUGFFU2creHVqdVREcjJYMlRPNGlka2NpclBIZFllNG8ycFlhZVoyaVB1amUzTTUxcVJ3T2R5NHIwSnhpWE1kemtUa25qR1prNTg3SWM1TnhFN1ZDd010SFRQYmRnU1hlSG5mSHliRzJNNzNIM1pqWWt1Smd5c1ZMMGVSb24rb1M0YTljeTJzUjQyTTNDSnNEdUR3c3lVM0F5VCt0NVBYakdkUXlwc3N6KzVxV0Z0UHAyWnJma2J5amJuTTIvcWJ1c05zU3NjTWExSy8vRUFFVVFBQUVDQkFNRENRVUVDQVFIQUFBQUFBRUNBd0FFRVJJRklURVRRVkVHSWpKU1lYR0JrYUVRRkNOQ1lqQlR3ZEVWTXpSQWNxS3g4Q0JEWTRKUVpIT0RrdUh4LzlvQUNBRUJBQU0vQWpVRFlPZUdjSk5VcGJYWHRUU0hicXUrUTNSOEZYZENFa1hHbDJuK0FLclRqN0VOb0xqcXdrRFVrd2xhYmtLcURvUjlud2pqN0VvV1ZKYjEzaUM3YlJ3cHRYVTArYnM5bFJTT2JjNW12UW1LZU1aMGphdUtiUjBSMGxkdkNEVzJtUUVKVm9ZT3k5eldLTlBJVmM3d0kwaGxpU0NHU3VuK29tMzAzUnM2ZHA4NElUUWcrUDJoMFZyQnBsQWNQOFBzZGxYUzZzZkNVYzFENVl1VFd3NjVkc1ZiY0lKS3JkME1zU3FWQlBTRlFoSTlJY2Mya3dsd2pPcFZwdWhhWmxMTHJGdFVxS0ZEU21Xc056cnFWRnFxV2pVRTlhRVVVd2xGNjFFL0NydXJ2NFJjdTUzbkxHaXFkSHVqajlvT3REZFZwcityNlZSU0FrN1dueWF3M2NsS0ZKS3oyd3A5MVNuRUtXUHVVRExUZkg2TllWN3cwb3JTdlpzSVRtcGZjSXg2ZlpJT3lsVW41UnpsK2RhZVVQYk5EY2cyMDBnYUJEaXErUi9PRzVkdll6bUxXVStUSkg5ZFlRN05DVnc2VFd1cUs3Y3RxdDEzVjFoMmxIRVRLdnBRdEtFK2hnTXQydDRhdHRQRkFINFFsUW9zTFFPcXBPWmdIUWVuMm51N1M5dTYya2gwcFNGQTFQY044UEZCbkgwT1ZYUU5vMlhPN0Jua0luMVhUczIrMkZLNkxmVEE4WWVTRnFiVUs2QmRtUjhLeEtTcVRMTWZHZmNKVTNMb2MxN1R3RWU1YlRGY1FmQWZjVDhaNVE1cUVqY09BakdaNC9BV0dtYVpPdXM4NDlvUlhUdjhvRGJhVXpjN01QaEFvS3VsSS84QUZOSXd1WFZXWGxHUVFPbUVDdjV3aHdGSzBYUW9BaHBZWHYwZzZtbjBnUWQvMnJ6VXd0V0Y4bjVoYnhyenB4NmhQZ1k1VHo3aW53L0k3Wm9WVEw3VnhKVFg2YVo5OGNzWjBwWW52ZUM2ZW15MjZsa2tmU2JmeGlUZGNXek80SmpHMFJxMHFZS3dPM0pXY015Z3R3L2tpK3oySWJiUlgrYU1XblgwdHp1Q3VNeXlGQlNrZ2gwdW5jRGJvTi9iR0Y2T1RxVXIzQmRRcnlPY0lKcWxwNWFlTkVwSHFheFB6RHlIWmF4dElyZWxhN3Jzc3VqRDlTN09PM3EraFpTbnlpWldmMmxWdTVOQitVRW00cXFhVUJ1aW4ycUZpamdDcTlhSkY1RzFsa3BsM3I3ME9vVFRuUjc2VjRWUHpUaVhrSUNuTnFibXlPc2cvd0JtSnBEYWRuaUhPUVBoN2ROMVA5MnNQU2REalVxRUhRUFM5Vm9QZjFZa2lpOVV3RVYwdkIvS01OZFNGT3pqWlB5bTdNZDBTamFhb2RRckxLcUtmaERKb25uVk82a0tXdTViU2hUb0Q4WVVUbWhRSGQ5dUQvbnZVNGJTSllpbXp6R2h1TllVakZ2ZVZ2VXNjUzBvZGlrNWZ6UUZhZ2VFTWlxUXlrSXBvTjhLbDg1ZDFSSDNiaXlSNGNJRHBOQXRwZGZMdGlhVFVLY1IzcHFuMGhhK21RZXd3RTZmdUhHT0VCVW9WT0pybno2RGNUK0dSOEkyN1ZTYlNEUlhmdjhBYUY2MXJ1SWcxNXg3c3YzVGFNcWJHcEdVSUY3amFlWTdxT3FxQ2VpcTdzT3NYYnFkaC9kMHJxck1WR2ROOE9zL0Iyb1dQbHY0UUJ6U0NEMndsZlJ6SFdIN3ZVUWs3b0NkUCtCRGpEWTFYRFEwemc3Z0lWOU1MNndoVzhpRTcwanpodnErc1o1S0FnUjJSMlIyUVlNSDJIZEMreUY4QkR2R0h1dkRuV01MUHpRZXRCNHdZUEdPMzJkOEdNdWxIYkhKZVpUZDcycEhHOUVZRzhnTGJ4Ums1ZmVSSys5b2xKZVlSVnc4MDE4NFFRQmZVd3d6VGFPaE54b0tuV0VKNlRnODRrQTRHak90M0s2S2I5WTdZVnU5aDR3VDgzckNocUlCM3drZk5DZDBmVkErOGpLdDhFSmdmMllSdlByRmEvRUFwQXBVWjk1alo4MTFwWGhISmRXc2lSM0tNY25TS0lmZVQvM2RQU0plNitUeHg1Tk5LZ0dNZWE2T1B1YzNSU1FmempsVE5tOWVPb1dmcnVFY3VsdGJCVTBoU1IvekVjc21IYnhLM2tiMHVBeHk0dzQyS2szejlLMDFFWXNFRDN2Q0ZDdXBNTWhkcjBvdlBnUVl3d2dseGEwOWhSRWc0N1lybWppb3hoejlFSm1rWGNJbEVkSndRMmRQNlF5Mk9jNVR2aHZjNkZlTVN5RUVxTmFDdlNoSHZKU1ZyRmFjMjZMbk5rS0hLSmRhYVBHbmRFbGtFdWcxRVM3aG8yamR1Z2IwVytNTXNwdktCVGpyRWl0WE54QnBXK2w4SklxYmZHQU9yNFFoV1JCOElSdVd1RTZiVlVWR1I4NGNPaWdQQ01RUnptVXRxb05Fbk0rY1Q3S3kxT2NtSFU3a09VakZFTHQvUlZFZFlwMThZa3l5Qk9NUER0VW12ckVnNm01bC9YU3NYTkZ3TE5SdXJGajVsQysrelRlOE9iNXhPWWxjbHFlZG9uZi9BUFl4bVNOVGl2TnJvc1JNcmw3RXQzSGVhNVE5S1RkR2JrMmpRbzF6MWhFMnRMZ1YwMFZFTnljdXFXUzhhbjUxUk5TTFlldnJkdU8rSnJaQkREdHBoOVZMeU0rRUtZQ21ucHIvQUcxcFNDU2RpL2ZUSlNUbDZ4aXpTZG8yNDRLWjEyaG9xTVlRNXNsMnFVZDYyL3lwbEV3bWpjMUxObFZlY1VMcFh3TVlZcEtWT2JkS1ZiOW5HRXpCQ1dwOUZEcFhLc1NheHRXcHBHdVJyQ3ExUTZrZDhPalZRUGJDOUVQZC9PaHQxVnltR1A0aGtmU0N0TnFKcVphNEtCQ3g2eGphRFNYbnBHWitoNUd6TVlwSmlzNXlVY3Q2OHViL0FPa1lQTWtVR3pWWE5EcUFLK0JoOVNWUHR1b05FNmcybUN6TUpYTVlqL20zRkN1Y21IWGVZNUtKYy8xV0ZhK0c2TU5iUWxHd1VvcVZTeGVzY21pMVk5SktRdEJxbjVOSTVOWTJ2WnpMcmpXZXBqRHNiY1VNTnhiTktCUkJqR0pKZVFiVlVaVlZRK3NZM0pMRHY2T2RvbmVFMUhwQzNWbjNsdzFPdFltSnliRXFoMURTUWlycWxHN2Y4cEVUY281WnRrbFJYWFlrNmRXaDN4UDArSktPVVNPZGNrbTJIcFJGamttRFU4MzRjWFRGekxLYW1sVTIwcDRReTJ5bVllV1ZKM2hLODRhY0JHMklKTlVoWTZYL0FMakZNUDhBMk9aV2tVNklNWTZ5aTUxOURpYnFWS2RPK2tPWklmbEVWdDFTcytrWWE2cXg1cHhDajlNWUJNNWUvQkJIM2dJakM1cFB3cDVoZGZyQmhvbTlwNm44SzRST3BzbkdwWjlQQnh1c1lPK2IyNWR4aFc0eTB3UjZRK2tsVXJpNmlEcWlaYS9FUmprdFgzYTMvcU5MaHlWSkdLTWhWTlZXSCsvS01PeGFYYmRrNS9hSWFJb2dFS3k0Y1llTHJqelRkb0Nza0kwVEdJeUMwcFR0ZWN2TzVHVk54ckUxSXZtUm41eEw2U2txQ3dOUE9PVDArNXNKNW95N3B6UzQwS0JYQTVhUkxQQzVxWjJ2OFJDai9ORWs5TUxuNVZoWVEza3RxN09KL0M1NUxPSnlDRzFUQTJkV1ZDaEJ5MDNSSVlNUmdqNlN0ODB2M2dIK3hFcE90aHVnU0hEVlJRU0tqaEdIQ1cvU21IQUJ0dm1WM2lFRTdlWFF0YVBtekZmV05rcExyZVh3L3dBTkl0QlF0SnZKNlJWbUluWGVqTWRDdDZvUDZvQTAxUUR2enBEazFWYVVnRHNHWlBad2dPUGxDUVFwSkJzVG9ZWW1Bc21aSUtjN1l4Sm9nUzg0cElQUXB1akg1VmRGeml0TWttaHJHSk1KVDczS3RHODBUVk8vd01JUFBtTUp5MEpRNUdBT2ZySmQ1R2ZDc1lBKzJxeWZWYUJVaHhvbU1Hbm5ObzNKTTNFZE5tcmF2U0haZFZCT3UwNmt3QXNlZVJqR1pkUlpNa3c2M3hZZEtTUEJYNXhoaTA3U2Z3eEpYb1hOOEp3eDB6Y3M2bWhQTk52cEQ3amxrck0yS3BjRWtWai94QUFuRUFFQUFnSUJCQU1BQVFVQkFBQUFBQUFCQUJFaE1VRlJZWEdCRUpHaHdTQ3gwZkR4NGYvYUFBZ0JBUUFCUHlIV1pYTkNoOXl4ZnBTLzN6TzlLNHpIdExSdm5LMGJObmZiNFZLbDZMUlM2dytPdng0OEJNSHVJcERXNnhQZ1pmZ3RPSXowNFBNYW1TZEw0VEF5bEN0bklGeFJqRDBvNWRwUTNFSXRPR2RMTnY0VHAwSXdsOHFKWXZuTDBCeGpBaDBVY3kxTHFYOGxPSWdqeGtZeTNtTzYzVzFWMzlIYWJLdEFQTG9Tb0ZUdDc3eGhoK0M3VGsrVlg0S2NFZVZGT2tyVHZXSTFlcXE3eXVKZ04ycGJoMDZkNDMrSXFJMnZSS09TOE5LN1kvN0t5UExDM05EbDhTaDdDSEVyd3RyUG5NNWxHVlJURFl0Mkg3T0dsZWZYbWhmZmlkRFFSWmdYb2J5d1ZUd3piaitVdFZaTlpqTHRIK2dLelJjWmVzaFROVEtWbGREMW14YjZJTVgvQURGaklaTnFBdkZ2M0Q4UWh4WHNGdGY1amw0UUZXYUZ0VEVQV0FFT2hBSG9sNW9yTEthcklXMStJeTY1UzNJOWNCaThobjZlWlZVTDZldDUyKzBPbFl5RjJzQyt0ZmN6eW9sSFZjVmI5czVFZFpxK0ZhK1pRb3VVZVhyNmxKRUF3WFNNc3Z6UmxtaVduRENib3lNaW1jV2NOU2tZM0o3NVUrNnpFZ2ZMUklhRFpkdGxlSUJNOFpsZlFlbXVPWWVBQzRXdXJpaXczWGkyTG51eWU4ZXZ1MjdXVjdqTC9HZ0xoY1hwNEFBOUEvWnJPbHlIckRkZFhQbmZyckdKRmRxMTc1bDBiN2RRRkJqZFpxY284SXcvRXl5eGRLMXFQUVF5aWIrQVR2TnJySVVTMVY0TkIrbHlzN2t0QmZ1TThHenNLSmdGWkhmcmRkY1N0eEhLRG92WmhMN3VFYnNXdHJsWURVWUYwYlQ5RnZRekhsR3JIb3Z4VXU4OHpzdEJWbXh6czNHKzZONHBXamJYZGJpaFM1T0QyNVMyL0xDbzlGRUtxdGZNcU1KOGg3SXd3dy9PWEp4MVF2MUdCZ2V4RE5WWThsd2l1cUJ1NlFZTDhDTGQrUHpOY0hUemZjZ3F5S1QwRXRYMUs3elBod0FxaTNqR285Qi95aGNFYkIwL1g4SnF4Z0JlMlZGUzRjOTFQNUc5U3FyMm05UkpVekVsUjdJdy9BWVlWRExVY3dXUTIvZVpTMVVFY2p6ZHhXM0E5aVd6ejBlNTAxZWVjZncrVmN0eFp3eGpIeWMvc21HYnRtbTN0ejE1aUN4RXJPK1JaQS9lcXhxOGZVcjRTSkt6RWpETExMOFZsNFFNRk9uTytzdm92TWJuVEhrb1hYbFIzakVoWTlsWC9vN01Cck1DRTVBYmRKQmJXMXNVKzRJS1c1VXFWS2xjeE8wZmdxTU1NTUJBbTByNGZ6MHVUSit5bDZaSG1QSFo1OHd1Z1ZYVlhoTG1iS3ZrSlVxVWxUVWZoZm5NVkt4RWlJd3d5VFJLZ1JheGFtNnIzbVVCbDgvWVBidjJtTFlNa1duaVpCbFNwVTdTb2twK0RIajhYdmpONVdWakpYNURENEhxTDR1WkpxbkRDY2FPbkVxVjhVVEh6WHdud3l2aElrWWYxZXY2V2RvL0NodG5ZVFZ4T1NSdzFHdUhiK01xemc5b1RBWkMxZXNqMEpYaWY5N2x1SjdSUEg5VHJScHQvWlQvQUpUL0FHRWVENkowazhFWi9oSC9BT2FYZHovMEkxTWNkemt3ai81aXRablBGb2p0NW5JL3FkTGFQMGtVREZlKzhzUlZqQS92TDBSQ2lrVVFvY1R0TGhMUzJRK1lORnN3TGpJc2xUUmkzVWQwYmRINlJ5bXhPeis1cnJ4Yi93Qnhwdys0cFlmUWhpeUhhV3k5NnRuUSs2VG9BUVdBUXkzQzJST1RKY1hZT3lXNTMwOWZzWm4rREw5UUMwRndqNlNNbDUydFVOeWh3SFAxMXVYamhqUGZVdkpFTEczbDM3TnY1ajlCRnRYSzZIMlhGeHRyMWtlaThMZ0RQYWt1V1JpanYzRWptWWJtQWZsZ3E1VWNLcnpHZmVhRzBIWVd1OS9pcGd4alJ6VWFMdFM3a1ZzMEpaQm5DRk1YdVl2TzJ4cmZxVmlrTkN3OFQ4NmRTeVV5NmlmdVBnWVZTaWppUGZhZ2RnNVJ4dnZBd1YvaTRqU1dLekRMN0hYN0FvV1hPUHFzR291djYvOEFBekswU2w5ajZsWVhzNitJZXM1Vi91WWc5UFF1R3UzS00zdVREdjZuczRFb1lma1lDcHR6akViNmI0emRhaFgxeW03MVIrekVtMFBneE5TMjJYNjlyVzNjWTQwQ1BZWTcwa0I0VnpoY3NHYU1kbzJVa04yNEpSSyt5RTlnbzBleng1bWROS3BhckdhV0pSRVV2K2t2eG1oY3p4dUJ1bG15c3dSUndac0dmbjZsaHF6L0FOOWtTY3MraFp2OWpROTEzOHBPNmNGQjNuSzZyMUlZTWUxRnFqWVZ4MGlFRnVFa1o2ZjJsR1VkMy9KcmN1cEtLYmU0TE5QR3ExZUtNMUg3bkk2anZOekFreW1qUlJTMXRpVi81b1FGKzVhSkRmOEE3bVBZdUovSStvQ1ViVjBlZFBkeEd1bXpaVmtOaTVoYzh4c2xtNzd5d1B5RURYVW9ub1pYTnZsaU1YTktyUytObjFONTVMTnJQNVhFYTJjMnE2MDJYQjlwdTNlVks3WEcyRVdWTGFzNWIvbUhEb1hZZjBaY0JGU1dKMW5ZanY0MDFFdkJmZW9nVGJzSDZZaEt0SS9heWFadXpiL3YwanJsYWI3anBUdVdnYi9MMDBqQW5xWkRUZFdoY05lb0R4ZGJmMkVtTHMwUFE3OHl5S2MwQWNuQ1V1a0pnUHNLNGd1bmROWTdndjhBWlV2amxYUzI3dmtLUE1FWnNXRUlzN1oyZEpRcDhIQVl1M1lNV2d0Nk1NRjQ3OVlteFJIRGFjSm0waERTVzNicTlLNnRlbytQT3R4aXp6clc2amg0VHdCN2VwWVdLV0hIY0hpdmMyQ3BRbVhKMmh3SWJUWXRYL1RCRWk0akJ2dkZQU1hDZm9HdnVvdXhFMDc2dzFsNXJ6SGpybFZxWjc4WEYrZ050YlErQ1AxU3h5bXVqTXZXOEJSKzRxWHFkNjhKTzZUVHVkNFJlMlcvUTFFSmRzS0hmRTRXRTVTb2dvL2IrTXhkZEpIc2NUTTE3UERQLzhRQUpSQUJBUUFEQUFJQ0F3RUJBQU1CQUFBQUFSRUFJVEZCVVdGeEVJR1JvYkhCMGZIdy85b0FDQUVCQUFFL0VGMTZuUnpxcHh6WmU2eEhZM1lEelRQM2tyZWVVKzlleWdWaDhmUE9wU2p5Yi84QUdQcFVHWlhZOHJ2eGdvMGppU3pFV1lWUUt6RVBLOFBGOWpqWkRoZzRicFI3TnBoZ0RiZzB1SW1relU0MVJQR3FZMks5OTRnWitGQll4bkdBa01sWEhjV2VFYU03M0I3NUFOOUg0ME9EL1I5ZGlKN0tMOVRCdFp0QS9lREhSZm9hWk1zUFNMUWt0ZVF3OTd3RkRwa2FyWmZYTXFJd1VwMGZYdkZWd1dkYk5zODhzNXkxZ0pGUkJwVlU0YUNCaVFjRFF4cU0vZUlmOEhCMDJnTXdLNkJ1WG43YllCQnlFZ1A2OXdiN0RWTHUxZFZsMWRYR2FwTkZXdGVtRlhUejFoekZETzJINmpNbkY3bGh4STMzOE1UeVROVGZXSHdXTjN6Ym1DVmorei9iaEQ2VktkY0U5MFVPTXRuMC93Q3VUdkZ5SWJianBKVkFDTEpXZHl5RkRUQjJnK1BMenZyQ1FRUU5Jb1BsSlRoY0VlaGdFRkNLWG9MN3hMbDRDb1VGQ0lLTk5RU0tzSWxiS0E2ZFFKRGRZemhldU5ZSjMxQ1FldDBCY3A1b0Y0Q3RlVGdLekpsU3BSdjEzWXdLNmlEQm1NdmdDa0Y5enhreE1HNDdPYjdEZWZabHFZNkltSit6SE1Ma0k0S21ULzdoSlJ0MVBrbUFpYlVuUkhUMFFJbE1kMVU5Q0NWMWEwY0RDSHQ3eTlIU2RNTEs2d21KT01wQll4cUFIYUxqd1RJN0VuQkJKZ3NzWnNhdFNkcVFoM1lOQkV1TlpoaDdnTzZIRGhzcVVldnlRRU9nMDI4d1c3TU56WUJJNEVHQ0dnTnhoWWNBc2VFeU4zY3Zsd1lCVVVZNE5iOFAyNGc1WHZ3M1NQVHNYMjU1OTlBU2FsNlpSMjRDVnpXeHlBM0ovaTdNVXhqelBSblR5eXJyczVraGNoVWdBMW9nNnNaZ0FCbGxVS1dvcStLdkRnTzR6M05vK2tQVTR5bldEMExZV0ZpRGFXcXhUR2dscUIwNjJkaHBHQzJ1M0VVTFFaWFlsRHhZK1REYzBYb0ZKL1JSMzFqSHpiNldsTUVDdG9qc1JZOWUzQUpSRTFxSWxVUTZmNHVEakdIVE9xbGxTK3dodkJ5ZHVpNzJSVTdidjFnR0VmZ2o1bDNqWkdCNGJPNDZKbCtnNGxPT05HVFI5M042bk1qM2VHZmd4QlVaWWNEWFMrbis1SFFrT1FnN0F0QThUZHFuSG5oTjYyaUhTQTQwWkNNMVJOb2pWMFJRd0dWRmp3MFJDR2FXM3djU04rN3k1Vm04MTF6Smlwd1RoRVVnbkVBMU1oNmlQa0tqR2dzWDE2UkREcHB1TFl3S1gwMkhKQkc0VFE0NE81SUZVQ01oNEZWNmt2aWV3STlLcWZrNU40a0gyU2k5aGhmWkhGK0VMRi96T1psakptaHptak1IZUVETTNFNG40eXJxdjRCS3pLcVR1RUU0UjAzVzNXV1k1MmhpQ1FGb1dqRk5td2NSNFo1d09WQW5xeE9MZjRNZStJNHBvZEtCZ21TWVpvTFFNRENpYVRnM2JBcVdib0M5MjU3Q3VpTktyRGlLZUhPeTNESDZkeXFOZkxHMWJTUkIyQTc5cjR3RzNrYTQrd1RpZGR4M0JWSHVqZGk4OWU4QU5YVFRobHhSY2lGbUhqSXc1bmpNSjcxajFBc3pwaHpMV0dXdVRoV1NMZ0RZaVNFS0o0amhjWUtFRHpORjRxN3U4STdZUVZURUkwRGV6NG9XdnFBMGZhNi9XTmJWeWc5a2g0NFJ0N2kzS3FCVFhaOUhRNFkwMUpSZDFBSWtjUThqcGlYbEh5dDJpbHAxSHNNMGhEb1A3T2JsM05ZY01BU0JrK00yZDRuZnlUT082bjRFYk1tT01YZjZ4bW1NRmNGTE1LdFlOeXNHaFR3K1h4aW1CaEJYZ1ltUEVIVUVndmFLNkhFVGdqZzRCNnJvMW9Rd1VidCtzamNLaXJhTCtIMCtSMDVLZCtlUDBzZkhneEJXOXlmaHl6OFh2RXlIN00wcmp0ajM4WTJQNFMyWSs0ZHpWY1JZM0JZVmh5Q0RpWUpzZE54czhhR0pjTGxnNUtLYVNiMEZTWVdsQmNoZDdlL2hCOSs4ZGh3UDJxMzhOdndwYXVTR0dVTjRxWlFacjhIR3FaSmpQK01BM214TVBmOEEzRzRaUXk3UEdLVkdlbkFOTUUzTURualR4U1ZSOWU1anpVNUNFT2c3ZkMwenJCa2pyUlEzTmkwK2NUdnhXSFhZbnFUTnk0WlNVTzVPNFRNUTZaNmo5ZmgweFJVcTRwdVN0KzF3TjcwOE1lV01hNHc1aExyQVZ4eDVZRk5ETjJZZ0ZqMEtmL01aSitiblc3LzA3and0TkRvTmJEeGNIenVSaUVZNnlnbHlZY1NHQTNUSEFtM1dKNERLbU1XWWtQV0ZITlcvMW1sREZPWWxvbVRiZ2RKbXMyMzV6WDJPT0QrTjVlNHFLWmRPRmpZMjd4YUNQcGMwcXcrOHVmM3NzRFRoNE1OWjN0cmhZLzRmL2VIVWR1WnBWVCttSktoOWs0d1Q5VTRzRG5nWC9YRHFGWnJjUXJzKzlZdHNETnptMVpwaWxWaW1LNjZZYXgzNzNKcXhCNnplL3dCU3p0QlhnNTFtZkFERkhaKytMQnVkdVVLcDFmOEFXUjI1T3JjdGFucWt1U0pKSDFoaUs5YVdQV01wbEpXaVlYeGZGV05vNVBlQmc3QjRibUdXeUNFc1VVbmhIQnc1MldrdkVURWFxUUlIMGVoSjd1RjBZMmhyK3NPTW9FL2lGNjRTTyt6QnpmcEphSzJGOFRFd2pTNjNjQVdBK2RHRG9hOFViY0NhUURzNWdTRlhRN1A3aWZTTjRtMi9wZ0JKRTl0d2FyOXVURVNxU1ZoY2FhaWR3S0pPaVRDVzRUVWMraG9LaGxoVTM3bjNsdFJEZVB1M083OXlTdk5lYzFCbHFhbnZTNURyckc2ZmtoL1l6Skt6Q0pyc0d3bXQ2LzdobWFDNVh3RHFFSnY3d1loMW1qa0tEQzh1Vm04T2tPQnlhemgyNlFOeGcwU2d0Q1Fybjd5cVVUS1ZsOXo2eXJUaG8zb0JzTVhCUm5hbmdTbUJjK2p4NnQ1dlZ3WlF3QUpSdzR3akhZVXhmaVc0OEswbTdQanppa2IyTUN6SENoZVZULzNnRHc1Q1I2c3pVWEFOQnFpRkVnK3NCb3ZqS3Z3L091NUl0b2V4cGR6a1Z5VVFWMW03L3VDVnltcVVmOGZlRnVxbk1uMjl3OGhzczBmUmNmYnJudkVybzlUL0FOWVZrSFlqQjdsd3AyTDRoN1o2M2lxdERZc1ErTGpjNklLeCs3aGk5NFdYWG53WXpMTzFFWHA2d25XaWlqN25NQ2JPcEJ6Z0lEUG5QUUxJTm9oTnQ4TUhYb0M0eGRBSkd0N3R6bmdzdjY2SUp0ZDRCOVR1UFl4RGczQzdhRlE3YXM0Y3U4SGNTUlplYUwrdDRZMEo3cXgwUmZQU21Yb2Fkb0gybDBYY21KNklBWXRySnZDY0Z5czRuaUF4ZFc0Y3l1bWJZQXRZOTdjSHU3YVlRUDhBZkdQVlhLTVFSUFE3dUpEZmRhdE56eEs4eGRHRkZCQmFOZzl3ZldLZlYxZUdLekg1VUcwM01BU2ZvMm1VT0lDR2RnZUJ4OU9NcUJUMHVpa3JYb3c2M2NHQmJvQThnWkczSUcrWEswUlVQZkpoNGhybGRvUmY3MG1KQm9GWVJvUlNxRTl1c1VvMklvemswbWVZdEppZm9UL0RDUmJKejQ4b2o5WVhVcHBzYnIxUHU0UnQ0OGw1SWovOGNDWCs2RjVOYk1Cbi93Qi9telpjSERPcEdrcUwvd0FkYXk2SG5ldWkwMnZUb3plOUJaUkNVYldDdTR3YnhyVkYxbjdxWlpYYnRVa1N5czBadVdPZ1RJaFE0WUVvN1doMklLb0hxYjVobDRTeGVzRXI5V2Jsd2Z4Y3JLeE5pSDE5TGprMmpwSlhiOG5jTXhnQnR4V2pUZ3Q5Rm14eGpVSmlJRVNyZ21jUHVFeEFUNUY4RFo4ckI1NEVTUlB2bURIUVhhZHUwQ0l1N0dQREdtQ1JkeUN6S1hEYjFSUEdYMFhFS3FiRUw2d1c3Q0JqQ29IaDg4SUQzdWZaRldxZDRDc0IzZTQxeEdDMXQxR0RSMFpwVysxa1FBR0o1N2g3TG91MmtBcWlKSm15bW5DQk5oSkU3eTh5ZEJKQXoxTmxIM2lZQzk0RlRTQ25qSUZvVHU5bTNPQXp2RStYK0JNbFUrQ2g2aVAzaGgyOXpndFJDSDFIT3hXQkFCZFJBcTFPdkdiY3NmTkZGMHpTYk1WS09HTjEwM3IrZ1l0cTFldCtFUnFHaTV2ZE1UVFRlV1JzdTVpemQ1MlA1Q0RmQk81TkNYc1JzUkVRK1BKbE1IZEtTZ25BSFJDbmhpV0FHSEFaQVFGV0t4Ymd2NW93U2liV2FkcXJnejBmcGdqS1VpM2NuSUJxMnM4eGt5cVQxdXVTblJnbWdoTmRsQ2NCaGhKcUx1K1NxQWFEcGVZZFRSU0JpbkNOaXRGY3MrSWYxVmxCRWdEUzNCeFBhdnZpaFFUVGwvNno3eXJhVHJRTlRsMGJYRDZzOVYxVlRBaUxlbmZqdmlDa1dydDRjOFNhdE1IbkVwYVFjbGZvTHRsbm9HQmJxb2dRdmREQ1RBRnFwVWgxYndtckl3bTVJTitIKzVWY0VOVGRSZ29ZajJoZC9jbjZ4MFNlRUttMFFNVHBpNHJKR1BJRWozYmw3S0xsTEswK2dYQXV2S29MejlEb2l3eUt0WWVzVkJSR2FYVGpobWlDcUZzMXltL3ZQLy9FQURVUkFBRURBZ01HQkFVREJRRUJBQUFBQUFFQUFoRURJUVFTTVFVUUUwRlJZU0tCa2FFR0ZDQngwVEpDd1JVellySHdKREQvMmdBSUFRSUJBVDhBRFlreWd6dWlXeFpNczVBRTdncDNDeXpvT2xBeWdTZ2diSUZBOUV4eE5pVUpRUUtCVEN1U0RyWEthNkpRVWxPZGZzZ2dvRFd6elhkWFZWMUp0TThUUkNZRTlFd1RaRTNzZ1VFTElGQk5keVRETTdnVklDekdGYmtnb2dJSm9hOXNEVlJDQnVFV2x6ektrTmhwV1VGcGNDbnNhUjRrQVpub3Mxb0Z0d01JSUcyNEdFQ3BDRHdRZzVBcVNnU0pUUktETFF1RnJheUVNYmIxUXBjVjFqOXowKzZjTVBUZDFQc2pVRW5NWlBraEx5U3h2OHByU0dGemp6MHNnNGR2Y3FKUDZwOVVlMTBFSVFNSU9sTlBKQ1R1bE5LekFwaUpzbVU4K2dLeWdlQnZKY2VtUEEwZWVpZTV1aFRhTG5pVFlEbkNmVkQ0WXdXR2cvUGRSUnBpNGs5Si93Qm44SjFlU2NvQW4vdFNuUHFrWEtaTFU2Q2JybXBRY2dnVktrb0ZUdUJRS0RvUlh5MU9CeGFnai9FU2kzQzBtUkJnODdML0FNVENTMkkreFByZFBlOENXdmJIV0kvaFBsMTMxUWZYOEpqS05OcExYU2ZTUFhWQ2pWNUNRaFRQL1NzcldnemRDbzNScWx2UlRJaFNUdUJoQWhBd2dkd041UWR1Q21OekhFWENwNGlvREJ1RitrQ28wVzkvTkI3U1RJMTZmaGNOdFQrMmZJMlhDZk1JVTZyWnNVQTRuUkZoaEFXc2ZyQjNpQ21uS21sQkN5a29PZ1hBUXFPQlF2U3krZm9VSGRGeEg4eXN3T3FCSTdvdmFRZzZ4S0RsS0RyS1pDbjZKaEE5VUVDZ1VIRWMwSDJRTmxsdFpBQk1jUStFNXNid1NGb1BvQmhTRk1oQXhZS1ZtUU1vSGNDVUNGSlFjcFFJRnR3TVhLemRWRTZxRWJINkFnVllvWlZZSUZCU2hLQVAwQXdwUWR5UUppVm1LRGxuQkVnSWxHeW0yNmQ4bmNEQ0JVaEI2RDBIcHIwSHJPZzcvd0NjcXdDQ2xTcFFLQm5jRktsVHVCaEF6dkc0S1VOMC9WRmxDQWRDRkozTk5vV3V1QUVLRElYQUMrVzF1bTRaeFRjTDJScGxjUHV1SDNYQ2ptaFNrSVV4MVFwaGNKQ2paY0FBYW9VQjFRb3RDRk5pRk5uUkNteEJxaFpRbXRCWERJUVlJV1Fwck9xbUR1TkNvMnhXUncxVEtNdEpLeUdVMWg1SU1QSUtMd2dFR2haVUFvQ0FFV1dValJCcUFkb3N2WkJscGhCbDRoTkFrb011b0lGa0hIcGRDU1lLQXphRlUvaUxhN2YzK3dUUGliYVdwRFQ1S244Vk9pS2xFRk0rSThFNEVpbDdxbDhSN09wMnlPOVFVTnY3SzF6SDBWTGJPeDN0dFVqeVh6V3kzaVJVYkNkanNIbUlhNEh6VEtsRjRrRlo2ZlZBaDZheDRraFE4aEJyZ0UycGxOMDE3SDZFSXZEZjNBSVYyVTJ5NjZPSVlHem9GVTJ2aEtSRTZwdTFNRTU1Wm5nb09EaFp3UmFHbU15WUhWRFp5K1F4QTFwa0tYTWxwbXlMcVo1K3loc25LNUFQTDRKSHFpS2pTZEZ4SE5zYklWSU53cWI4SSt6M0VkMWc4QmhhamMxSEVnOXBLWnMrZ2FaQXFYVHNKaXFUdkFRVWEySnB6bUMvcVlaMUMrYnE4SGlBejlybjBUTVFLM2lxQk5yN1BMOHJXdzdzamhzMWFTK3l3RkJqMmVMbXRvaDlPZzREcXNQaHF1S3hJcXVFZ2FEOHFuaEtGZDJXSUlWVFpJYy9pT2NUQXNuWUFVaHFVNXVNcU5pazh0bm5jcGxVTlo0eGRFWVZ4TFNCNkoyeThDNFpoYjdIOHlxbXdxVHBkVE1mY0oreHNaZktHa2p1bjRMSHNrR21aN0o3TVMwdzVoV2FiRUZGNUFtQ0NtdklOd1ZTMm5Yb05nUEk3YWozVE50UEZ5R3U5UWZaVTlxN1BxL3JhOW5jWEg4S2kvQjRrNWFXSmI5blduMVh5TmRnTTNIVUUyVlZsRm5na3llb2xVYURpdzhKZzBzUlkrNmRGT25jMzZHM3ZvVlErWnp1SmdOSE1JVThVL1E1bW56OVY4cmlzTDRxVFFzSlhxWVVROW1wVGRwWWQ0aHdJOHZ4S2JpY0ZXQkFxQkhEdFlQQm9zVGltWVRDbXFRWG5rQnFmSllPdFJ4TkVWQUNBUll3ZmNja3l2UW16bFRjMm9EbGQ5N3FmQVE0eUV3dk10YW5BdE9ub1ZVdzJGeFA5eG9KNnFyc2JaMVFrQUVFZENuZkQ3Sk9SNTgxVzJCaTJ0bGpnNkZWMlh0R2xmSlAyaEVZdWorcHJoNUZIRVZBeUhLanRCK0ZFMG51YWV4VkQ0cTJqU0VPY0hqL0FDYUQ3cWo4WVVaaXBSanUwL3dWVCtLTmsxeEZRK1JIOHFoaXNMakd4UWZFOHBIL0FIcXZtS3VDZlViR1Z6NWs2WDY5Rmdkb3ViU0RLaG1CcWRTVTZ0U3JneUJZV3ZlZWtMNVdwaWFmRm9Bc0kxQjVwK0d4SEJ6RVpoejdKanord3g5cEgrbHRCbUtiUkZLUm1PaGl3NjI3Y3BWT3JRcVlRdnB1SnkzdnJidXE3c1p0REVPeFZMOUhMVHFxZTNYVUFaRndBUE5iTzJwV3JFdHFjK1NiVi9hNjNyL0NhOEVPYXJGRTA4Mm1zUWpSRWs5TlY0YVpqbW5zTGFlYnJLZVhVZ0FScW4wc0xVWWN6QWVxZnNqWjFadG1lWWtLcDhPNFIwOE54Q2Q4T1dodFQxQ093OFlKeXVCWDlQMmhRY0NSZlN4ai9SVlBiZTFzRzNMbk1Ea1ljUGU2cGZFam5EeFV4UFZwSTlyaE0ycnMrc0pMbk5QY1Q3ajhMRDQrczRrVWFsdWwwN0dPeE5ISlVCc24wMk1PZHdrYWRGLy94QUEzRVFBQkF3TUJCUVVIQXdNRkFBQUFBQUFCQUFJUkF3UWhFZ1VRSURGUkV5SkJZYUVVTW5HQmtiSEJCakR4RlRQd1FFSmkwZUgvMmdBSUFRTUJBVDhBM25oaVFvUUJPQW9NeHdRb2hRaTA3b3dnMGtLRG1GQ0FCTzRiMmpHVU4zTXh1d0ZRWTV6cGJsUElMaXZCREFoRGdDTFpKVVlXTjJrbFJ3NUN4dUJ4aFJPVklCMHByM0FIU2VhR0FRZzNxdEtqaXpFSU5ubXRNQkNaUUIzUVFKTzZRdWZOYWd3WkNIYUVad210TWQxREh2RmF4cWdJQWxDZWlDakN5b1VjUGdnMHJUbFFPU2RBVW9OSnlVQVFFWGhoMGpKNkpqZE11Y2NxWHU1SmpNWk1vTmFpRUJ6UTVJY01iZ0Nqd211L09oaG56d2dibHhrUlBUS2Fib3Q3M1B5TWZoTUFreTEzMS84QVUzU3ozV2tmUkJ6M0h2Q0F0Yk9VcVZKV2srSlFDR09PTjBTb1FoUWdEdWN4cEM5NldsQnBBUWNSN3lscWxxeHdSSDdnUUNqSEJHOGZzWi9haVFvQ2ovUkRLaFFvQ0hraHZoUW93aW80QW9RQ2pjQnVoUVVPQUFBSURnRVJLNXFNY0FDR0VPRENHVkc2Q2dGcFVCQUFMbnVqcnVIQ0FOd3dnM1BBMkVBb0tBamdBVUlDTjBLQkcrRkc0SUlEZ0NCVWhUd0JCVHdRRkFVRGVCQ2dCRGVKM0F5Z0J2bGFndGVFU1loQVNPYUJ3cWQ3YTFRU0hqQ2E1amhJSVd0alNHbm1WQlVSdXlvVUZSS0RjSUErS21CdUtnSUxCeEtrUk1va2dTalVkQ0R1cWRwaVFWalRoVHBHUVVkZzdIaURUOVNqK245bWFlNlhENXAzNlhZRE5PczRmSEsvb3QrM0hiK2lyYkUydlZ5S3dQeUlSMmJ0NkMzdS9WVk5uYmVZK2RFL0FoTXVOcjB6cGZSZEtwQ3U5bzFzSW5xaUMxMmtoUVl5c0F3Z1JNSTRXb2NscDFOVU9iNEpvbndSTXYwOGswazFOSE5VN2FwVkJBWHNkY00xQVNGZ0dJS0ZYVi90aFBlMm1KSVEyaFI1QndYYk1Ja1FtMUlrbzFHdUdWckFiSWxkdTFkcTB6QlhiWXdVNTliTG1qNmVLdWRyWE5JbHI3WjNrY2ZkVmRzM3JYNmV5TWRlcXQ5clVqVDc3VFBtRXlyYjFCZ29EdEduSEx3VE5vV3ZiR2s5cGI4ZVgxV3J0QVJTY1FnMitwU1hWWkhRcjJ0M1lhUTJTdHJYOWFoWGxvSUE4dlZiTnJNcjFRN3FGWHVhZHJRZFRZNkpuSlQ5cFhOdFQxek1xbitvS2dwNkdBQWtxbHRqdFQzb1F2TGNhalVnd24wWmQzSFlRRncxc2dGQzd1V25TY3o0a0tudENvRHBlSlBrbVh0RWpKTUZOcTI3OGgyRTF0SEpCWFptZTY2RUcxQmlRVjJoR0pXaW04ekFQcDlvUm9TSURuRDErNmRUdlFlNDlqdklnZy9sVmJtNm9mM0xjeDFhWlQ5cDJOV0NSQjZFRDdGVUNYZ3ZFZlpWRDJUNXFWZkdZT1FxTjI5K0hObnphZng0S3BlMnNoc2MrcTlyMldXNUVFY3VZeU9pOXIyZGY5eW9TRmRXRksvY2V5ZnlDZnNXN291T21ENko5QzdvbWRCeDQvd20zRHlTSGtxblFmV3JhSjBqeFBQNkVLNnBWcUx5MlFUUEw3UWZGRzF1UzNMZnp6LzdUOWRJUTVub29QYXkwRDdlaWNHTmJxY2NmeW1PWThFRS9DUW1WSzFETEV5NnVXaVhHUktiZVl5Qk1KbDVTY1lJaENwYW5rVUdVM1RCQlF0bVRoeXEyZmF0aW9HdUhRaFZ0aldiNUlicFAvRWtlaWZzTEJMYWhnK0RoK1F2NkplMHdleVB6QlRxRmUyemNOQkhXRDlmNFRiV3p2YVRDMTBoc1k1L0xPVmUyRGc5em1qRThoNEJVUmRVQ0EwRVQ1SGwxbFU5cjFMY2xyM2FoMVZDOTJkWE9tcU5ManlJOFZVMmN4K1dPbjcrcXNxdEY3blBaSW1NSnh1YVZhSHRpY1lqN2VDdDdpMnNBMjJmbDZyV2xHcTBpSUJWeFoyaFpycDRqRS81elJwTXlXNUh5L0tOSXNndDZKZ09RUi9nVFJXbUE3bEtwMVNSRWZCTm1yTUNCK1ZTZUh2TFpqa21NYlYxZDdrbUNxRHBsR3ZYcHV5bTN4WUJyYU1yMmhoR1dyVlJjT1JSYXd0TU94NXFyc20xcU9KTEJQVVlQb25XQnBrZ09NZERCSDRLcG03cDkwTkJIbGcrcWUyMGZUbXN6NXFsYnR0SDY2WkNGWnRlZEpnODEvL1oiIGlkPSJmIiB3aWR0aD0iMTk5IiBoZWlnaHQ9IjE1OSIvPjwvZGVmcz48L3N2Zz4=", + "description": "Displays changes to temperature data over time visualized with color ranges and background.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.rangeChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.rangeChartWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: false,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries' }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-range-chart-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-range-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":2592000000,\"interval\":86400000,\"fixedTimewindow\":{\"startTimeMs\":1700577227898,\"endTimeMs\":1700663627898},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"dataZoom\":true,\"rangeColors\":[{\"from\":null,\"to\":-20,\"color\":\"#234CC7\"},{\"from\":-20,\"to\":0,\"color\":\"#305AD7\"},{\"from\":0,\"to\":10,\"color\":\"#7191EF\"},{\"from\":10,\"to\":20,\"color\":\"#FFA600\"},{\"from\":20,\"to\":30,\"color\":\"#F36900\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"to\":null,\"color\":\"#D81838\"}],\"outOfRangeColor\":\"#ccc\",\"fillArea\":true,\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm\",\"lastUpdateAgo\":false,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"image\",\"imageUrl\":\"tb-image:dGVtcGVyYXR1cmVfcmFuZ2VfY2hhcnRfd2l0aF9iYWNrZ3JvdW5kX3N5c3RlbV93aWRnZXRfYmFja2dyb3VuZC5wbmc=:IlRlbXBlcmF0dXJlIHJhbmdlIGNoYXJ0IHdpdGggYmFja2dyb3VuZCIgc3lzdGVtIHdpZGdldCBiYWNrZ3JvdW5k;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAU8AAAELCAMAAABu2vS6AAADAFBMVEX09O/n5+rw8O7p6ery8fDs6+3z8+7u7u3y8Ozl5Ori4ufq6uzk5Ojg4OXl4+bm5ujs6+ra2ePw7ur29fHu7Ono6Ozs7e3d3OPe3ufW1eHp5+ji4eTb2+XY2OLd3OXf3uTT0+Du7u/t6ufn4+Tr5+Xi4+na1dvz8vLLzuDW0djR0uDf3eDY09rN0OHh3+Lc193p5eTf2t/d2d7g4Ofq6u/P0N7Kzd7b2t/Y2eTSzNbPytXU1uLDwc7LxtPv7/LX1+POyNPUztjQ0uLAvsvGw9DIxdPCyNzk4OPFyt1cZFrs7PHPz9fHy93Uz9V2dmpjZ1tpa13Lxc+0tsXMvqPm5u3V1dxtbmKwq4ZeaWBSXVW/xdxyc2Rka2LY2Nyrp4PHuqDT09hYYVbDt5tsc2q0s7+0r4i9s5JmcGd7eGLGw8y5so25sYSuscFMWU9Zdn5RcHi6ucbCt5WtqX1VZ2JAV1WKi4lWYl12e3M0U1V9f3ZLW1e+vMfLytaxqY5GVE6wrICEgnBPYl2rqbPh3OGTkIqlooMiQUShm5mur7u1roC5t8F/e2yoq7uJhXVca2eiprbIzN+fnXuwrHhzb1y0rpHLytCrp3VveHBYXVEnR0qopH0ePD+9s5e+tYeHhoCkoXticm0xS0tmfIRFXlx9iZy6vMqjoq25rZCPjINfYFKrpIqnoJulo3Cdl5M9Uk5TWE13fWuChnjFx9KKjH07S0TRzNGYmJy5sX19gnB1gIFlZFSVmqmgnaKBhoWjm49ETkbEwMeYj4G1rngqTVK+wM6uppmaobEvREGpoJKWk5Kfnmy+tIB/gn7HupmRiXo7Wl2ZmXR/ipFuentTam2XmGeck4dzgplzgYxralOMkZljdHeFgGfEuYpveWiPlKOSkXmamIyOjnGFkKOrpKNqdF+kmYdJY2SZmH6Phm1OUkW2sLW/usC8s6KajnJGaG5ealeOkF8ZNjuyq6e0q5w9RTosPDddXEaklHqvnG6BhVvHuqqlk2G8pnbGs4F6dVGOgVQglcMIAACnhUlEQVR42mybu4szZRSHdwhB5pKNAceZmGQnIiO6630mm9V8sup6WXbWwmXSCMEg2whbbG9sVFBE8Gv2HxDXxi6gjSIWqyDYWKjYWCg2aqWdlc85Z2Y+b+e9JisWj7/znnPeN26EYR46YWOO42pnhK4Mxy23ei7myGDC1qXMgRsEDrNsAxYGXSdtqevJ96kXeEGaeinmdTws64jFWZayTbM4jtMsY8TM0rAslWlLtluVDaNouFW14bBHGzH12I729vb39kaj3v5sjya2z8DG473xWJbt8RPY9vb2E2L33ivjrnsZ9Lts3r3rruNd7K7d4+NdxvXD4+vXj7HDw8Pr1w8Pn3766eefvv78008fsnmeleW/tpGHLUyJ5hVXWGJlyuIFTjksUvfv5pTKs2Yc6Mxa83SVIzjTNAjY8pGmOFMPC4DpefCMActqlgETY7KVZjjjaMugxhCVPgQoHAGJCdvhaA/rjXp7s/29faAqU+Pa74/3Z+CcCc3Z9liAzp5QnkJVSTIJRVAe3wVLTLbXj6F4/Xj3+BCY7J4GKSh1gaxO/8PTDVot33FaFUo6BtGuW8BzZxpGC9Hl3wkWq3/wDex7uOqukajyVJyu6pPBFHiwpKHVLBOwjSzTtNolBli7IYVmJCRjJBrF8VAFGg+VJmgVJyswQQrFfZkMqKiTCZVu0ybbE4MJSjaQVK6iUIiiUMACE7UCVGz3GH4gZZZWGyQNJ6Mx+bgRhL7oswVHQ6o4Q0bkuqH70MtJ6ZZZKV+aENmcpzXbsEFqC91MBQk9V4m6xhOI4IQhuw6dkXa2RJmpCTTD8WuYaS3crRoqOKOtHh974vJbMkSjlUBH4vCsOD9Akagy7fdn/bHQZJhAaZBErhOxe+9lVadnKEfRKiZUzd+ZDw0oA4Nh0wwn8w1/932ICq4iNFOufE4TJ5zf85zr9uYroUirmD5Z1odpt8aneGF3A6/6OygVKQT5LNYJvAyYWIcupppUoB2PJlzlA3L9myFLaEKU4zNmYLDc6ok+FSLrqD9jA0tjOjOeEFWaypJlPMHpITqZQHIXpOwmnKF0rMLJsnsvHq92SMdgikJrogayQdroM8+hl7fCFv5ameJsBY7XnR6kRTKMBWWoMIVi11mXossSnIHGLGOoq+1rsLoAFZ5ykqo80aR0dXkjmsKUheElzF7mZSxQ5ZtEYdJliqAKSno0ZI/PQxikQIUmXEdKcrgvXPs4/Tae3zdx0kSbqlGGnp7i5Dg7SHcnuLzI0xTKDp7oE6KwPD48pkMUnHQzg/kff/fF3X0Qdlz19SrYt0Iv7JRP9vsLOFnIN6CyTNT96WKhxXgz4WuLWSnebgFKZ6+jMIFqfl/LlI7Zot+Bl4aZWNEmODUkoVAwYtEogqWemxrugalUYQjV/p74PPrcN33298fYTIAqVzwelGbbT9y73fg76rTNMTzvoonTC1L0Cdrq8KxMGf6DZ5j7JEyt1m2RyTI0y/HOydVJWfKlUTR6ivHJ2BHjw/+ZgcYs2FckJcZjQWry1EXZsYWf0EsbuPWCCUy1juZOdISJOo1rJCTpWyRNsnKGikLRJ2JFoPvg7Kvno1M1ZApNwpKYhKW7OEklMD2hEoUjPNUMJrIk5tc8MY3xDU00elizZBxuXDu4Vkz2ylxcOvQNZjfMioPx6nIv9tyyzOauHqoGCncv47kcnGymy/9QDP4d+plqluLxNE5S5QgwzlM2smQG00tlw1JB7VSxCpZZpNlTHGFKlQWGYNWghDrN6wEqY3802gOsnZ8A1Q3SlDgPUcOpSehkm4W4ZIkT8+7xvbtoUzortgtUjlHs6Tom2Wgcn2b6fLwo49784tr5ydurGGyOXxarInLz4QTdBtlypOlS6VS5FDzDclU6pROUTj+Gd43TqTY1Sxrdqagaz8545LmmzyBlgMzc3/hJNFLp1kz5AuvEeowmloYSjmjsBWuPbZOM6gRJDoARNKXtq4m/60lqAsW2J0oUT68SfOEqUUg1ymKxSaFKDmoxiWBPUEKqilHJMv89Pm20O/Ne4qb5ejKcF9euzk7OTifDtHSXEYKFYZB4naX6u4akUPTpwtPtdsNux+vqERA4fA5rkdZcAwPpmrO77ibe/vEErtIbS9U6Mgwsw1jSGRqpkkw9HpiWijJYtzQN3VKUYAUnJrFekicL830EalkTJjC3ZUeQx+eFoVZNrFIqWd5EU9udCFgUustCdg9T7Qz2ptC6mbGBZx4mRRT1x76EeT+ZkrocPFIUndAMfu6i4hlWUMurUvZlV9SzTOvIXqVU/zarmKC6GWRFX0UZQPbvPBVjpdXkzmSgG5GsdYbVT3aEijgjNU1BmSxzMn+vHX9kuRNJPlQtJo1tHvcl1INUlGk8GzOXNwOupPjgpENReWIi0UOI0uuy0yY5P8u87edpPndbvu+Evh9EKer0NMRjilBxsjNgiPZJ13w/W1+uHnXrWP+fCOUYT0xkyrTespKpEWhQ8wSmrFkw+OyjXz/46UH5JtMDQEM9PBEnRJOqBK2MCM+MRhGqyVSZjpBpT/VJ3rQ36vdHgKxhQhOe1q2qb1juTsiSiFCizOtNvKdj4IQnKG3TRCVQ1qvwnC+DPF/Eo1wSJ3hSF4F1cyeo5KlDJ6HGagko022Oc3C1LtFvk04xapCNNRcmJAw9dmqAE5lCtwn48MSCB5/75Pdvfr9zkWlkymLLoliqKqk2CU5gZaYMBSogDSifWcTxh3KAYvAd9Zs7EuM5M7DQnOH3KBHX35W5viTR2pPVWB7TOUDprEoVq6GqMpmsPir7j5yvzlcUSXV89zy4paJPEPtdKNJlqAnPVSq78nxWBl14uiZXq1SNJiN0GqSO4HSLJDWenkR4bRaLrF7SIDW4/89ffv/g1Y9eNYdP6yCPZSyJpKKUoOLxAjUCJJOcAspSJhzetArGIb6uuT1NnJ6JxW6czFSkdpBqlN+WvEnMZlMnqyT2dkdCU3/X6UYFeqhxfgNvz6M8dVnCkOEL02T+8sup02qU6bSYxEKzZRam8frleWnHQWjRnS3zfxNTC/VEOBb1/bQ29XeafO/ZN9Fvf375djFdvfWrFlCK0+6iMMmZvuhDr9YpJDGd4WeN81MTe2wEWvV6eGodr0ChiSpFoUrVAjw0ZZK4PxGN4viVmb9z9SSR/roJFYc3pgxQVucoA55tf5r7LeGZy6mJ5aH7/qOe4yNZtfq2xICyRr1psVrlFp1qklDFZG4CVLMAsegFNVqAGtMAU4pQrg7S+z///JfVL+//8GRm6iXLR58YOFmT9V3CkmHdDJyQbC5IhGple5gRreukmig0+zJvz8iaLGFSgRL52dCwuqKnHWvJZK4vdmh+T6/MUih4+n4SUnHSfOXpt8ros/cfczlOtRDlNg/GTSnaYk6LYbZkB7om9KsJTCUb/rOicsp5X2BaZAKmDQ+Q6ubgVJ5ukPzy6qu/f/b9m8MgzTqmXnF3eNIh+rJ8qFCKMBuRWhkvk7g7YEca4kcNUSQqDg9RFafylGVbUydpdGrP7apQMpY6RJ86iWlkolqSpRIpjY5Rv7fbfsdrAw45YipS763TwCGdhyiD1VwfSrIH30lcaGQKTaHMtVX+Xnm+qZOsf1S4bufCVW12A8AKyQADGZNr62LeeebXH4Z//PQIfxR1ZpnWTILTS9ZEpWEfrjBMgCh9yH5oLj9kZTJTlYK0yUjxeMolGTMTKHNFdFsMt1ebCE/ZgFSxNpeiEAUhG7JPdhyimjjxSeO+GvpEoO7Ub+d5rgq1mPTIy7roJxUnC705T0+uyht3UdCTnWCkiylhFvuqf1BuzV036bhYdzis8nxg2sTHwNZfvxyltz33wZ/PftYBslqqPYu9QXwSd7JVnCRynHaSJFOBxk2lFMlls4pTYMp1MzT3UanQlIaJRvdp8GyM6lOyUc5OdCom5Ty9Mo1J2lg1+6xCPVbdkKhGcXYaPAHquVyLtEGGOg2k2xKSKFaJqdfbDqVi6Zs5nCuiwk7mWqxMuoRVSkrG/1kZRQGfILd+smQ2iLqR7hnau1/funrmk99fK7vdADOetIGs8X6WjvugxJJOpzlBmSEJVSmWJMSza6qlIaFJ83pYSrEkTP9uxtMeQmbC0m5KqvelWqSY5fXEdyiqmcM316J0Pqg+AbpQKToQRKY+HOEr7i8jZIGVoOUvjgo2v+byvZEmvjeplIEVb1f65vEF936J5zqkVl23O+9t1igRKVODNnWfe/2T8cPPJfKVAHVNnxqR1p14FWe7MbfRkywBaZ2GWnjXvcjSXJ7dnhBlrW/thSVD09AbOGXMJmOpne4VmopTk6cJZjytYDKd6jMdHs9M8sSKu18/ZgInSjV9ItBFMnDumYZ+ZY4ftvkAvladNoGWYSq9LSwK9pqU1uBYypL5hj51C9r+yZOpEyhPSZsMJKOWpzUnSC8+eON09GwZLLOM7+3CxG6g4JmlRdpfxfHq6gy/byI7zyE0zEAaT1hihnUkTPdHOPwIkJKKzgRtn011iKJO4r4coSAkvrNAlEhvgV6MS3zNQu0FFJRViLcFw+WZnkefCpQqvjN1VZtoEpRsrLXYEaB8PTx9/YC1UgTKAu2W8ZT5Jd5F1F6aglGDl4Mkd6/KBH61WutHEtDGcLW4pPx2nv3p+71nB7/8+er7yUtLjfde/cocdbJ5PN6brPbiTCxWpH/LQ3F5gwhDSZ328X+Imsldc5/7O65J0Cey1KOUsLQ/biL9dn2SjkWk1UNdHegtqZflXop6iVEqT2mgtEG/bjxvboOysnaLKTeaiszHgT3ZMAQzfo58nyr1Kxi1VKhCNurDsMs2WFgo0mpqdVYCtQNRyUvLHn+ovPytRVlOtGaqivz7fv3l8Q8/fOWFnz7+bDrXJLWporhiAuY8g2bKiHU0RjyyLB+IkKU1ganHTkITPLX0RKJjgtL4xgl6I87TFeekgtm80AtMhpkeo+r1pk/OTo1OWK1PovvNLMBkmGcDju8ByBypOtmJfoVnmC8LkWkd4dmwuHOuotXcJTWrhv3p8tRSgaATKNGtun5Kl6BE54BkGNBfP/vtw58//zW9LEW1aW0SmnbGxydJBtksrd7qrOa8sbNzFKwa3GnSrUaiYtIbUWwfbVpmLw2bAViwNhLF6euIxNgVukIRqIKUvdwyK0q7JjFpHgrY48bfc2Y6UztXbjSCuib1wRS+FpIkNKFQktRLYW5h3b7m0zJodbXaJzuaOqLV/NWDvqusgbYD1mypORSj6D8ZOMjW7pwDliB46M8X//hz4KYrGBrPzFOgOPzbZ+lgAE7M0/oTdlkDFCPMK1NzfXaUTJaE2kHKjkGEH6FP6GlwgiTN1Gl5vtzkVeKUZbKNi1e/etgVnBPA6ss8PHF7FqnnWeSdvuLJoLFRiUKLb1WaWohelKHFeobtUOYyvo18wIhjOkUoNSxh7AXl1ANofHk67rPKNarEo7h3xt1UwLabXCtBu3BnWV04YeGHn//x4X1O98lMAAvRd4ZM8pz/9texxHpgVg/NCTQ7SY0ziuwtOcLlI4tQljPZ0GummfDk9s5elkjwKUEtKLFoLa88a5E2t8ymT8Gqsd7ikqWkpE3cjQIVoBL0N26HnNC0RZUJ0ra5vR2hiRvefLPQdOQL3bAEB2BiK1DF40WsSdS6TROnqVs6Xidfn6ZFOa0u+WAaTkdnV2735WtIcpq6nACJ+2bqdPX8lGPg/t8/vTMgXp3PXTsBspM0KOV2f32Z9mK5IdUAhaWag2I2Q1OgwpTVeFo1z8SiZ+ee0cTnsX5dgMqoLpnVxmoa3evXkImGeeNqOj0GLCrlDdQkylD/37gdoEBskIIs5yMNgym+u8PrZ8vxVYq+Fvjq6pdpaBU+A8DyjwZLNiHqfGt+LXTSt1d5p/OQlztYC6IL11sUZb/MViseVJzuIPI8d5sky8TpEJC++uUZlyQ1LbSox8auF0Wb6eWEM7TfE5qazWMeINP4xq9y6BgK1Wf6WPImGyT46u+Ik65X97wmW6okh+eYzIk9Zf0EjhaazOvv0preNGoyVaXSrAtYhiSjxCjaRHg2JlQtW6IjU3DyaQ5HY8uiWzTJ2LvSdFX1aaDDcK75aC8KtY4qhuU83JFj1ywqw/Pd4vTqzezy/XXmJZyKjnvi1bfQgJwPOQqE55OKk77nkY66pzPNRRc9cXmFKSKNE6XJzGK3I+b3dobuWQ5qMpWd8GSi9/cRqurTbkZVnjPRJl29nc5jMjRFpnyALgBh2/yIhC2L8LyORAlReP5kI8n9GzwZFuKld83/dzbZU4gqaHV5Df/pwdGlCPZikAv+6lpvGoLVW5YWx1bDztwdh3X5H3pO54crN+wPOeXeHGZh0Em6zhz/trqfrOql0qJ/+jIs9fxceEE8O9uDphSg80yA2rOSJwy1+sTnG4EyqtikDj+EnsV6WcnqVad62wRHcDZ1kmSjEo8MaV/z+ru0AsXfNcVXmryFyApRbWrHQlXT+rs2Bou8fcPqIA9Tb+G0WcMdn0W/zcO2wMbz4emuRqsivHlUAlhcHWKgTBjuchHCkD6fZ51ptkCfllp58enK8YrogteM+QpHR4489nUveibPIDvQ11LN+kv9/VMSuen5SSpoPUZ2Gts9qUdYIvhbNc+ilmjdqeJsDJRq9XXoSHliWszj7lInwRaE1ElisOVaVCslff4Uqao2gbkLTwtOTMbTmOrhydi4pXw5bP9Dokxi0x2Z8wRl2gGrs9BTC/vu7HS4LDzfTgc6lpRhsGM5VOiArnDvu08zfnX59UE4SM+f2nTS2SopV0+lp7NsPnU6FyUA5eVkUuglX+CkS3t12pwm8em6ZMMHD2seQxEoOFPjiT5BWru8Io3sDIWsZvOWOI36vdEQpBjTTN/rzOH1BDV1bvdt0SdleI7Z1Jk9Dai7QNXCE4BWO9ldM33jpqPekf93lLqCJ5w64Asj5Wk0GWby5/l6dHqZv+WKblO/neTg9L0Al/ctW2Wc/iClAOaEZVrMebYfncS+U87ePOtFd/bffGcy4o1uKU4euZybb8eu5FW4+dzlaO1uurPZm7EbdNFmVZoKU3v11Gt77u0kLmUgrMO8gpUFoKrPSJ1ddvoKsq9lEjFecNb3TftGVGp5ubIXlBDl5p4FIwkVwdqZSniCIJ3GTonKrT3TveRLwSBXjmYwVAe/L0+czVZ7x8+r6F8L03ZhXq6c8M31CHb5eZHn78c5x6rnLvViWmunTT99Z2ugZ2s+nxTlS0HLKVZhd/3ZIu+44TO/PFyMZz/2EvJ9qifX8d66vECf+H2xNw8wHuzfO0sD+OL70FSewAakpxLF45Ur8oSf/uqhpll5uyT1e3UhX/9IdB+sFpPYkjjBkwSf8ES817zJfixql8zQhCNzzfMuifZWgprnmzStSz6/Q0XUnJ43M2RubQ780M0Xvs83RrTFggalgyzohG3v5J2U1+aI0yHK5TFvec0BNQ6vcSncOh9LKVWOlsi86Oet9K299aMpzu+l5XPPTZ308t23h3zsdspw/P6y/hFfejliIft8Z1L90ORgVf+SlN4RmDbXlqhG44Q1Nn+3FJQOTzobePbEcHqZNHuy9L4/g2DfFnDCU7rsnpBynoaxG4NSTV7rLH3afYKfO1ViRaMbtyK36ZHSVJAy05kG0zxfRbJraGPMJlTHad/avnX1zttFJ8+9qV49uQclc7jguUiygOD0ZHUfR0aRUdHHH5+crU/PC1evAL2rVfRs6uzkX3xctpx4crk9PyvkVd+K0UmM15fFOxOvq3E+OnWxCqdyJCLZWqWjMT3LVJeV3+vzh/3iHovwdoK7aBWaAlNZ6orPc5PHrMUnfTZpfuVkd05MmPK0tJ7+RK1Phnk9beOmm25tb3p1jG8pUDOfGjyb5wDk+7ocbbVp6vKOK6V+UZ5cHeXuhaSieHOpjyMdZ9lh4zvL9RWuThKVdubn61naHw0vS71KdXm8oDqdhieHlxfr9XJdRq+WpJ6UD2JF5jjlyVns3tF1Ma9IVZzAVLNw5BlaDKRU9JY/1acoMBmS3HN0YuL49v8t0Ht0WNIEKFEe0zu88T48RaxyJQpPHF8dvjpF9WKUSZBWyb0Z7q9QN9o33dRuT0O01r6dizuWG9ZyvPOjCq/RbM5Q9BnIJkrSt8v0Vy2p8mUg/0jXc+dTqwEGk3V+wd1peXbycp7fNppczGPB6bei03zp+kGenZ1MyhB42atJjjCrJxPSpfV361RSfPRJteTqC0jz/4uw6G902Fls8hLS+ky93syumWOrQKEJ1qECba5ElSVIJRgxjUeIdF7/bIwG0yc0NuHomGykgdWunKBJEkVnUZZMxPdbQUn0MZev5dnkTVvzPNetfVzkzBifHUcAz/v4auHfSlTK164v1p1ON2HmMLrX3l7v+OHFtfGq7zwzePJyqI8nTKvywsmdi/PZJA3lyn78TpoUy1J/vfNAK788+/487jmwJMaXs4WAFStjUAKzkqZJFUsYkMTnG54alYbM1Skaiz4lu6cbU1BaoMfRgQlI0NJYGZZASde8aULDFOmkGlYpqVm8hyc4b721HXZqnrTG4JaPDnL7w82tm32nk/sfIi/NRBHlUf5SOXzziyMhG7wkIkWl0VSVm/OPHb357ttPn13lR8H8yV6S9sdFmNuLaBGVy7cuUk7o/uTqcpiuC76n1mzR45PvP15zS3L/nFgvJefEsn226wOPBVnW1pGmxkWevnk2uX1UlUxIFKC4u5XyVi+ZOPeZ4VnfkwhIvb9XlEyY4tTEHj0qVkCORaB/f1fSxAnjPkSAto+CQAECDSh/t6PlUXvgWDByKE5v9jQF4A/nR97Lo9HyJXfvwM+9EFTtwYBwdI1sFNwUqEeXX7/9xQ9n61H25PnBivfenqcvUk4+X65X8zJezq+dj7KyLOeF09WbKe/qix8+PonLQM7SNFqAPi5SvSF1XH6qmkqwV5KWiWaGVXni7wgUsypJ1khEiqdb9dkToFbBN5PlT8CUgZOD01D2Lbu3hyXBqdU8NDWDsrsm8/kbg/zz1u4OPIlJVJfw5GKucvZar0v0OWjJl2HCH1wkiqHct9Jp/6nTZc4ud+4rL1C4jzLnZV6Vp+k77+0l7bBcFKcfX+0PV1dvLjuDAT7L58cpB+bTIFxkUjxFLzlk/OfvfPzxj+9dpQhVDVFOl+Xo5dIemqPRhWWhrhINrJBnx1qVS5qBNhapOvW+yXjGTDR7R25uRzgFakOdVeakEmWoaZkEUWwCTUmhFOgE4xxFoDK0VtpotYB26623t5NA9KkXIa32UcPTX7osgcTyqcQcqlPL/m+fTt233sqPOAkmRRIuSj0XuiVPcSX/HlKp78+3M+DffFRwSTf7nsgTp0m6+/HHX5wcnEa5vtcviOhh5y32Vz/++HTccspesEk+as3lP8aqJBYBdPHmGJi4PYkorf5fbyRfsgQffXpscPimRLKfMzb/A6g5vFZLBlWRzkZ/57ln+oSlwGToFR5Ad/XOHo+XiFTfQAH1ny92G06bG7tbbwLFYHPzvsHmoLvJhZKwEVKozFn48sd2EuUEoaM5NA3odPbOtaM87A52Fj8O2wvI8moyGLXCVd576urgi2+PR4+SI9zcXhVt/+Dk4OhoePXjxz+cxLF/c3dBmJeXqMIbtHqnXvjy9++UI/uZSdeLMqeycBRpwfQXF+fu2z4VxXEcNwp+JE4RjvNLWoyBgCAQHnFxBcE8yks2DETxaMlDl0gV6t5MZYmqSpnyD6Ckf0AkpjB0iLKztGLJEDb4D5j4nnPiBjj2fSQUhg/n3HPuuefGaiXZoJXfbiCuhBKtSyTpTq0OoLs4lJHmp0mMVYJRZ89SglAhKiJHIciSyPrJTklSTXJ7gTafMPUPgZHAClWJ6qmTs5AT5D9Dr1aCxRdrQglCc5EodvyIQ1B78FFEX+tOtTnFViiyp7/332rpFm3T2x8mfkL/C5y40x68NNpuLmbbycXk9816svr4rcsoGsZv9Sbr/urrEXZRZqWtJmWd/k11VbeHQ+3s956iDhIxdG1ga6SdOM1HUR6f5WN9daToAb0uiTy8XCEqhyCy8WyV8/3nv/PMMHlCe+jUgZLUFUkRkCWEQhWKKYETFzbifIm2SnwdROJ78j8n4EnBJ3kksOX0E9CyZsLwqcMLf1QqlsAJROmBGeNlc0f+3O8OQW50XHxbjy7JnksRtuIv3ZyPz8+G9d6AYoHId/XIXy1uEMLqWZQm8Xq5WG83dfJbAy2dTibr5exhfTHSfNdUsIk1qpRj9rU25Z7nL60SrK5Un9uwVJUsHdJ411KBtJuYRBgZu5MUiPPyceEpQji5M2WzlO8+RbqcJ3HrjsuxPQdPfPsTQIGR3Py/NkvwTOKaZM+U6ygfIkNRsYQC6c7S0dEM1s4vBDSBGTwJJQQ00Yn44OQgM5l6IL3wdS1KFy2jVouOFlf38fFLth/5SVxj7loWRd3JpG5XKmU9+GQ1W6+3q1ZrlJUHWG5r9V7L8Y/686N40VRsU0UIYbhw8822jdDfXZ/NN5d8jwyLxWCAg+DTL7CEfftFvT6Y0tIJ888Sug1qgieMfX/bVkPDmJc3m3yhritMGSbTZP2su1zT5Lh8ZCdrKMF8EtnKI67PXT0eCJfcQj05bw/tBE/0TBQ08cARQWUBlNAi/wyeXqkEkKUi8wQ/o1YcBMTJNlMMTX/YMqJyrNuWHw+nWnhv1CpK1GmaR37FdUa9cW8xjm/XXaerq7XV+uFhu77yAgXnJFlynB4vXYWCUqOZ9YKg5XOxRKyoaYBJpa0tl/2B7mSr+fh2crueXF/fXd+tz8ek1Jv1yZHmD4J4AM4D3oxaCtt8S8p0+ExZ36XwIOX6vvAWCFGIh67uSvjkOEST7idDwFOCehmIJTq8oMg0GeaOJ8MEO3mgpPQVO3vYO/BSdkTsHljBU1hGoEnjm/dNbMOjGux4GkVRzRkeJVGiJV7x+z91r/ZL1IpwgDxMk+HIMQ/j+LruR3guEDa9YixmD5uPu8GfATP0AWM0t32ImfJFsTQLIvyDRSttYUPafOnD5Y8P2+XdxSo5avkDe1cL5R7rNEVcdXl5vVneLvur264KnLRLarnTa50dPFz8TiQ5wgW3tJCKuNy5+FhnJXXF6DE/Oqwj4cQg2c3zYTKEa0hyPaXdPAvRFJxs42BJNSR5zT15Jj5J5gwJ9LNE3qjmKc0dzx/++MtIi5EHlcwAItAv674buHp43y7b4WH9q8V4fFzXwnA0SoIsKxcbHQpO+1HJi+CFTlzf1D+KtWKFN/n+ahC7PrLDvq+0FWSeD7NhEmhlFdHpavb4+DibDIxGtaGSKCQqHu0+sHmi2OUywq/4ZNRbJEH3NI6niVOWAh2xe53elozdltymRSc8XTQIR/euizgUKHf36AmoeHhA5TMQgMwdk+xA8QEj1zcISeK6TzbBLcHEn37qQdpb2B+paa1UsgInBFEg/etvw4G2trsngZO5bSWJFC3KtMvBwD9b9C9X89EiLmeBG7fSVIFWVw1aRm+04uD2Yb14c3E0Ou4idURBl1EdXDrdOI5bvq1rvE+N75Ps2cv10Xj782Z+DB2+vDjVpA5ldxnHQg+RI5OuQzdy6cLjp0fdZ0k8IFuHanKTEh0uFTc5vJcL9HnH6klUKX4CXtcB1t0RCPpTocm7eYErbgk9vJLIs3yXxEJkaSpKKjE9eEKQIcH7Ba7WYn+klqolLJyxUmSgr/7ybjcym5XpiR5EXi3z1SCcLq7785PR9fFicRPrlPgMokU84L+HckbN2tVpaz0bRcGhrk8R0tK3VMIzGJcDaHk5CfyiXS3qetT69PZ2+/NsvTApYQqNfWdxGugKWKd0Rp/XlkmdmRPIBR1y8kc3A9JJzodKM0UkJVp+aoQuV0sauy4tqoCJGnGwlBs2nMBziCJU9pTGfx3Ps75S+p4FmftcM4FROvh7zt4hAiUfjyno0pHIc03AAVKQsSzGY9vT+jTNMr3rpLYffjXonfcv5vFqMU9efnb8VRJ5oJzO12u/ZhRLRahhzdeLZ+vtQ990O+WywpkAWT/bi0nZoKAqjfFJm2M3//jz47Z/alfZwLn+xL1s1Q+xdtxI+aNiMENC2gxAkkt4KIdn4oOtPQnz1IglN4CUK2H5jh4UW2AI/XSgpV0opksXlsTs818j4LhenBMLD+AJruLlwfS/tYzioMRDSaUo31AmoOT2nyOGsHe81SZFol702eXLLw+jCKaMi8Tr2fLiKMaV2eOr1qB3lb0cRXY5WwTYQ/rwWVp8lQ2zePr25OGhH0Vwa5Ha5A0BHeglercfYTcbHX+x6K+3s9+WF9/VKY7lUigp4YMcXy9aLR8nHsxYNdjaMTGbcp+ZRZMyfQYJoYmuM1HmKUKVd2aeIOEiUVZW8vQkAIm0E7n6XV8nflyCR/0R5uAowhPZyItaoonIFPBYKCoF1h1PrJ+FpoKQiRW0UYMbevMmOmySi/Ja55Pe/MNFK8auMuuEYXC1Or/7JJ52fbNUfDZq/TJ6VnciVytZobudfV2o1ijuQi60Cq7uKDBq77nzUFstH37++XF9PjysJ8YrtIW1uTwCQkgB3u/2ut1pswnttNB4LTUUlOOJ3QtPqYj8n+RuCU9+1Q5Gzw0v2bkwLYOlbJbY3Ln4jqhKpaik8PIoVEbO5LFjkiV0f0uRZkKXT0OYJfRT5LmmWpkWgJOiJsszsnKYjBKvFHZvxuNy6J33xt2Xsy8vF72v5pM66oxLkVdsh7hAt35WC6MmnebV1JPZw+zsh1GA3annUeokSWKsjYPDi8cHoLw4HcA3qaAhp1QkApNoMtH4aoDFU8SW0sg24EJ4TZVjJcaJ3sztnQYIB0/Ck4d8n8SxqHgjjkLl/AO+HnNgJRFbzwseiCKE7tKyvdO1ED6kk3ockBXZ50ZFAPRj5vrycw31p89M0k+v5ulpGnXH5+Wal4z7WPFs7Xb77Xqznj/zo9rhFdK4buh5UZCdnWfR0I78tFp0gHMxm819w24FSTyK+xe9ybw1aFn+x8uHx/WkHkUasiyDstkJFLBroyHNKsV7xFKlb1stpPM0cMxLyNU2uyW8UFmLegXtf1cY926J3b0soyS5gye0Uj7iACzcvUNUXXRSiyeVosIUjmm3/6QTJfFLp/mxUr5bkpELcp5xMTOaxPk7nrXKL53QC0M0u3f+0XR8gqOI65tWWPKKH81nk7vz0bA3nqxvN/3FeX/y5dnqYj1BNF7LBs2pVVQbJW/1sA1KNYgepMnVuNfN5re96WT7OLud91cngd40rYqjV/2iZXaaVMUDF0U8Sfi81LaGrrXqxanNpY8GiJbN/FJeqtiWITzJ7HOcwrO1vyjC62gZjXnuhXJQUufkcFiK0J74whmhSZko5Jg6Nnp6xeqhpjuLF6eE9inUlLVVPoMj55mQwSc/BZ7pu9//MJ1Of7i/Gt/5ZujH2vnkGXx4qdRarm7Hl0oYqcF0vrp45+jkpKuFsEV/euyE4XBRsWq2WUMUvzxKWm6STR0zgr0Xq5735vzh5/VpVKpFg0XsGIa4KIx4zI7K+qlWaS+PBp5ZZCg67AGxaoZUkpNd3mi28EyQaxb9VAmpZu3vhOb6ScEoOkGp8VDmxvx24jp4SF5i43df4rQ9hIDi2QvvO3mDj18bk4uKkl7GgBEPdfR+CoKcZoJAVUk/zcb3f0E7I1hlWrIUfXhXj+BQsIJOlutFFmTTjxzF9l7NCtHNsyl2Q4d+KzvsDReXyIqYQRTdPsy63/eOFfwXyIvRJsu7f/i5r0dFqdTxy78EvuSmKaFqd0xWTUyhqOLpaSFwMdoQv3WEKjr4LHH2rTMbymkxTEC1MNXwME80yP6GfX7uCRGkO4vPhewduSeX1bLu7CrD93cU0U4lbKJICSPUEzjlADR5GQNecCWknGYmkSoHMnlC+1zY+OUHr1DCLt4reaPe3ZeRB60y/OJqvbq9TVqIgEphzW6HUTu4ij/SE9+qhlk4nYZO0w5Kg+VsM+/3lCJvW4tezS9Fo+3jPK4HzTyVakRa1pYpTkKCDIzB0SBTt+5Rqot6csMwy0CbucgwJyqJRTTRUloALKAUnlz7CKb7K8xIi7Cta+gwEM/c5qmRSA+1hMm7rJ2SbeaUPeHkXx5jngRTfhFPDP9TjFzRjKNkkvx0iUJRIsthaJ6wh8U/Vyj98ke7QEkm7wVtsq5HpKxh+f58OzcjD5hL6IqJFv3knp8Fzjk0CfnzzFW0ppuE6eahn5mp76RB1laL0NCSN36ctaKq0lQ6lXZq1MAOWtqOm4j/m9WqrhFalXkWU0dB+JSctywr8w1fcZA4CeSao0RTdmISTMIoYjFYaCmaSe/uQphODXPmiFmZJ/RI16WqW7cLqrJ4wvyBlotv2S3xbonE2YdOu0wzddDS4x1PqnLiq965ju42T4QT+nlQ+v7mbTAz0vClu9nF5dXZ8ObqZuSsA2gcVLVE4gXu8PP4sD7VNeWDZuSk5ttXQTo8/nK7XXTfjiNCBMVzraKnXP82PylXS8VOsQiAtImXY/048RXLUDVfZ/0ERy3o+KZtODrSospUd4PASUxZAGymiXSIC/e/52lBmOkOqmlCU3fLJ5pYvJzOc7Xoztvnq6iMBFbEgZDlH0lUKgXixFOuL6ARTHTE8pQ9/ZNI0EQPMs2I7pko9DMsvPrntOD58QI+vBeqFXb14c1lLTQsYkkql+hXij26N6NQMREuJV74119pZbp+2Hbtq0WYWfRHXmS33ZPN7LRUrSAQ5Zy/gUY0EeHbdjx1fcv3HRh3knba7Rir6ve+U7aNiuEMfC35K+GyHbRpUzUajYaeQVnzuOl/+ilctX3OnpSTnbwwpYFZSgNJtN35vOOCLLMknESUJo5z6Ehh+LGk8niUPIkU3HOTggc5pSeuWD7F/kGVeL4wdErhaHO7rEdqpeKFXqngna7roTeMQrL2gjlNQiOcz8OKDmOMh2H4wZ/jWFvNHraryc0w6vAhSQ1SGsxmrRImZlDdHUIZzJM6s+x3x1kwzMq65bOG4p+23TJ5KX38LJ76QFnlnzEpWwoK/Gwl0/huDoACHbv3nCk+06sySwh3Yvi5hnINM54W9XmaxHWp0IkmlBR15cDOQc88AwdseQcqGGnxJNOHvWMJPeY7yiJUNCaBfR40cQ+eXjita9d38Y3mTwPFC5vfwLcvv1Ot9k84+nzBi4fPMs8rT1vjJEsNL41Dz++vZ9uHh9lsHHe9kqqApUetVlrPylIgYb+tgDIfnHpelaY2nY9ahu0QXjSpIL9vUYnEVcz3yTjSp3VAAVO1bbtln9IjagNEcwFDdCYzpQWUdHW/8RQlzROj+zNll3gKVRcwyeLlSjKffrKQs3eQvWeQUFL272i70jEWWUSPX6ZEHov4JCnF4+G5914NVhebWBnaxQ/ssPi9GrSKxu05Vr2ggjA0DT4yK5Vw0f98cDJYvBOP1rfr5XY7w7HGeFq2vCjSlTyhUnohfVxgJQZZEEtNzlDzS/F7GpFCWs3IZ25cslP7ywxU+z6z+WQa39hqsZymRBPp+7QHc3gVSS7efpLZC1HpLW4mUf3fT7/wBlSEP9DE7bLhi7hEtc4K6gAtOycHNB2XgyYxeSl0IpC75L1kmukqLbCycOktMZUDO3JRzx0eL7bL4wy3qhSv4HlNr20bi4eVr5U9fPrgmw5F7pMtHVKC4nYGwQnRPO7SYtD04kRHQE5rJYA2llubZjXu2m0Pwb0noXwl5aFomr5US1DeJEoWg5OL7TzTWz6fLKlWp11RSbCl710nx4rxy5sNCpvkFZz5IDaf70K5F9FpOX3aKmEo83nIvgDXFYMHS3FOMHS3TkDh5uVeHTMV5ZTk/b7igWBKVTPej3P95Bwz9HOyvhtfLa7j3ts+PJGJY8bR5vft9u7uvJecnKwWt/3+Ziay3SwfZsvJPDuyKIyirbybIRmKyKdEGhpe/lb3vEoVNHk1LceWwET+s23UGHQGF//nue37BiKp9G4zuQlai8XZ5dkiwZflFEkBOulsJdPzYRnVFZ3X3ye6+UkIKIrsw6a9fpr7RRSNBJPuvnTsvwch6Ggh7cpZiOtSSaP8njApKG4wCM689GGHk0bgpLtLQhY85WqdrKbPzQfhGx8EmR/691mmvBvGm+V33fcabVfrLbFEkmFvN+vlZJEZXnjzsl+AWrqJJwum90MEsBQD0KdPZ+MQx3bASQLdNJLMB9F2dhPwqgoxa41vrn8bFemgxLleDFrTJLDA2h9Mx+fLy9X4+nZydXW1MG2fdqWksWk5dW3O2hsc2jesPVKBKjfAFUDcOyedcbpye148vWinvrd5fMWpEegqJUd2taKACUU9lozo6dEuZiKvhCYvgP5b3mKkGOCPokIYBMhtFhth+O3NYvm4+ni1Wr1cf9bfAOdvM5RoraPFvW8j0D9Pah5EjSPgs8u1mtrxInzBKelC6+Gx7tUik4LWIgQIPSVNMuwFMBWeUeKrnevtAjvawer3gZEESq1WpX9KzunUjyLfjyRmkkCUrzPuL4WzgYsIS1FSs2KBHjS1UjHBVrCyrYMnRCOiHC8JTFFVEOW3Dp5SyZz/SKuDhoc2SvSKgj7bH4Ay2/xUWVDyCHs/CLNF2NC/Ou6uNrPJjORBGrp514f0rm+j0utK+coPawq2QEHkwYR/Qa6zXfLge0qeUqmFvd8eV1FsK0QXiElnZe0EWfQl3o92kJuupY8fx7e//bb+fX7kUxEP7DzyreTyKpveOHS8bPua47If0pNzJc9EKY2GmH1Oct9LbI9kCS+mHC7ttvSM0txbPEQGgOQzOgj5JodToojs64cE8xAPeyWaMU8wxENYcZOBa3Ig6LGhl+0neJK9v9rpxUmwWs84AmJ5fHiSGbmgJeL2/uLDRd03KghXkMZUPnCDhDCWPMrtDxb13vJxc1TOpoHt1eB7WCFRYYaBZvgr+sazspLqJFeP65uTQWQFx+dIJ2XTs7h3nL30UssByiB1A7fdbupK0nbjpm7rfD2UQyZ6CR/YAmEOljviiUfTLNl3iktCq+gVwN2LxPc7YX11JXQCUciu/BZCWVGgpeVT1PNoH9tTxSiPNIjB5zn857qj280WQuoI2SzXF+eX19nlegaaW1CGwWMK2U76se6FNTMMSxH2w9rhKsnieHQ52W6xNiwHnbatB1ZT15pFDwSNjiWeyfNd00fAkKxuV/3ru+WNQ6V7ilusKgEd3imOFkWB24TP5zpocv47keMQW/TTyoMmbube6EU/MZiAuvdNpJgVCAZQFf1kwcgcu/n28yUXwjSBNf/Z2yMCyje9iaIE+U83bE65fIzrGymR93G++3xuTTCJG8GbtAqGZ4aBHx4U2uPN7LbvR/bxyXbWX7O2Aup6ctGfMOyHXH6DEuPTelHXjsq8P63hsdpeBENvR9nFDKvwBrV2t5NhMlU8DkvNTok8fzkwgM+JgsDABJ3BUPc8GaX0EjEJU34ZJURwUkdAIUSQphVTeO5N3tRznPUuBBtPRKDlLnBSCh+2j4f08ynb5GBkraQmOGmA0WOUIqdc2N6XzHO92VxvAGU5wW9QOs2wUKh2wgiLwO1yeX0VH2W9t0aL7Wyy2a0B+NvlzeUiybL4uGlHmq82W6PN42zz+3yhUfrPbKtcd2Le/L6+PIlT1Ss1gtSC0auktVW1Y0g2z46bzaLm6LRBair5tfu9qCIGcikWMc1FFaZP+RECqpHWElCCqDPbf99Z2ps79Y5oJ/hJDNV9iVkSUrJ6gHyJ60n26XqyeUYqPp9xUpNjEBmF5zLWtMSrtv8aL3+brW+1MEqvr69vzhfTu1nfPigUCrh2FHZ/uwi7Cc7cY/h68vRRgbKmJCr5G9j0cXz2++Ns3V+0whfMS5TC/3498EovUqCaRgGg1izTQOo6vmeYyJMYfkc1P8rKQWbKjXvgJNkDxSsPREDuqTJQfkxb0SR40giokDR560nzvUei7ikMdaXggSZoDi2ibsDOHk38Uh7Wi8j9ZKkUl3g0AUx++RdJnktG/fHFHP58c/eL7odKfTzbrsZ/4Ugj8sLCQSm73vQHHqZtL+yON4ODEB+KDRX7G73jMU/6RunAo7+IVfN5Sz+dT/B/ZT3DhbdQAXdnoDVtz3Lx/+dqvkC5zveZ6dfAkq+N6FaxHYDfvxnK9F8dAyVPL1q5p2rkUagYPSkoBno4ArUqgrasV1B+nQOltvdLdOc7v6pIaXtZP+GK8O5rHI6oY56nsqWnIBQvuSU8Yu7gWTs4UJLsJn4TJbGbze14sVjOfrsYGC+8UPAKIaGK179/6QNhqdRWpstV5L2AGD4qprr+tuVRME8JqaqKGe0vARgZKvvDzfZidX42nV6dn83H0+ymF8flDv4l9vx4mWURj4UDJYFWzS87CtD/GD13xJNFwP47jSc8qRHOXWhPLFk9gZBp4lPul/5TfguWEj6JwYuCAidX3co6ik6UUow+X0LxUNiUsI5CnivAmi3voNh+P021LD6fPW5Pzn///fbjMCyAKI7mCwcfLbfzI3CKCgflzcptYH0sgJ2fdIgniBJU5ikdVDXS5rYXIf5B7E8xahRGkWFxTC84gVKk0XTFvvOvauKU8FllsmDIL5ryRJI5CtK9SNSkAGNeF86i87M/SqaX9DIHysaOIc/ac5MlFJtQ7DzlQBlCN+fRC9lnaBA6S2bdFJ4lD9Ca6Dr+9/C12mKMmwNT2P/y0gohvtpolPzRbw+34YuFwvMF+3r6BoWTgB1W3ixg5oXUefDrudRQ2eT5i7RWxZd4q422lI5xj9FDg3CfJt8bPBExiCoTrcpA37F6Uq8YwhBNHBIGSz6x4ZsmQ8X79KOYsqFnqvT+60ypSyyfiMoHxunUJX0HmtgkCcu88HZ/z+bpp3JINdnoyd6bQBN6FShc8w2rYnc6QVaewrN/eDz+/Hp5t7w+Q6lrWNHmv23mekQ5kO//tMOCRxgPfhF9xLKQy45np1h6Iar3nkEti0AQRQQ2x1linmL3nvqDKw5IetVC6i7/Il9Mc3vnFwKq1DNBEQn0uWQMKMHzqcpJz31SWRfJK0S7/7F3YCyjSfkYuSW+VAuGsHtCuWd6ulNPDERU7idSL/rp4VIblKsJoFbFqyihr1jm68F6m5g3h0dfLebL/tnV3e2Flcxvl79OkE86qNwloQWchVKHbFmsPKMRzDBVS68WCWzBukd5o00hPf+ZWvx3lX6JoHrNoWlU868ADq4pt3YZ8jBUeIIjtWJu52r+5sJHSlYFQDGD5OUj1En4iQa2zFKXYV/O7Liin1ITnvOU3ScpqxQzs39nlqShUjImz6e0fnqpUQTVWiHsGF7N6nioY2qghKY/vbrxwzc89/zYOVpefnmWKsnlcnmFqqZf/jSgkpZXIBuHtUPazFPJAFKl02OM7J9SVN+pnISKKuCGpRRfYoKqO4zKMCKqxSoEo5XKUrpvoqHSxB8RP34w38ehAlNUloiaHIbugbKQVyqbT4soHymhywVzAMWDihwsnyJIhopPym+AEVGJ78UbiSQyHD5XAFCr2qjB5NuGUlHJCAuqZby0/K3fC/0U0bY//CseBCHO0+5/SK6Rs5z359f4DJ7MjGjV1ALNIqo7mnaAk6VE9EIlTRMF/CtFDvE9Dz1mYGpkHV5FJcdUSTty3GQw1SqPuyWULX5v8JAd2T1QUOSeyFkkCOt5HbXIP4EmNvNNgMRsX3ibp/CAEh1ZPKRbB0+4JOZJUSifhHCSWRLMEKA8fCY8ZZAJeEIaXqNasNoHBZBUEcJ0rNderK/hggrQzzD6Psw+Ok3CUiGEFIzeerb+9nZzFzfJE1HD5sdDTNqotM02knSSEQ3BrWJhCqQ2oqUQqXfai0qMVQRCI9MtTpcw1E4lI47i5gWh4MRDLME5N3w5nt8vntIsS5rJ+3hQJKRYSNGbLBjI5EVYHzHIg2XzKS3q1NkfcT1jAKSHQjYPm/JwSYTsPnnCefrcAfGsqZ5V8LKiV9iplVHzmvrZ7xMHOPXvgaaY3IX4Q4+JFq42v//+20XkiRTdaSVoNykaUiIqy/V2elhU8G/iZXWM/0oipYQ5VUOzjoaxNQXIKmsosidG878/XCTmXpVRRcfCIKXuVrUYrCCVjRL1Jho5ehDFlGHK2XwTNDU0YepiHX2qEifTF5x462CKxkAZJNQTPcOUuP7pWJ6d0hNO0k8CelCrNooFqKZHsTkecE0Pnk+Ws4ndqIQs7e9tJlqAluIrbbT8/aKFT4Zbfja1VApUS7UqDpFSDpw8lDRUSuCG8L5J1EPjE1xG8MOoo0Qe/s6Lr66HgxArAumy5kRG2d+7JjF8iCGeSVQUwosod2LpPLBiqmzsGBip6Cg6S+57yp5TWIKgdGL16GkQpyQVY1wXzkpKdbdykVZY7vp8zylOSZqsnxAwxaUE7CgV1TvwPGZWwZ7nwJzMNhdaiGwTAn6vnbaLYUi8gc47KHQnj+tVbHtJonzgiRh0yhEaxDOKMo9GdNXqCy+UCG+pnU3jNE2nbWU4dU1dCdJ0GCOHfzYM2un1vV4xi3sxhOX+454nwSWQop/7bRJarqgceQpTjOhoyHWTujzCZ6fEIKWVOcXMPHe/jOVKLCo0dzx3xSMM9h+6zp+3eSoK44n/xIUU25SQgg1EbgELuTJQQJEQspBAqgTqAKJjpAxdKjGwl6ksryokpnwD6AdAylSGDoidBcTSgRG+BM85z73HCYFj+9zrpBXw4zl/7nXyvheEqf1nkASixgTNTBkjBwpPQTZEmHbx7NvffvvyGegyQcUKOnxPY7/q5AfaCr39J5e/3T9++8HPL33w6fzi8It39pEOkyIpzirwnjRnQXW4XL2MLzU8NcWOCL7Tuj9fPi8B+M7VVYp0miPo0zn2+bG/Elf4OH4cooSUaUN+ODYsFKAkycWSoygTnJs8WeBZlIaaM/2uKGZ8/EGeNAiUZcnxhHHnXi7Jn+/oHyrKDTzpQMUY8tqLaqi753QDoFJ+06aNASrf73ATVotZ2DRlGr3y/f0PQ+hyOK50Od81F82w65IMqTQoyuXlC59//ys2ob/77u7x/s/f73/56vMvv72+vsCj6o+efPfk9vbTD558++Tup8u7ux+u5e+qWyyvPv3xet7Fyww0s1J8rqvQCQoTV0rzPoNWnMCF4GsZlKHPrglEWZzY4ZMiDiKFRJUkYablvowwa5v2fS8K8zFvrZMYF/LuL6PFSa5+bxkH8yijnvUogs1WlUwr4ZlFq3fqdFHioxl//Pz4dJyuLi8Om7FUaihpcd6k6bgrmue7aXNz/conUVBXyBnV9JmL6x+/vn/v+sUP7v749b0VPh07Q21CB4Clq2hw+sVivt+uLrtkKCvQcToEv6Ws8GGTSczvObNbIkjzoYV8DIQAKN4tO0nWNEqgzJ0wmcjwdEqU/BTz9gYeLjgHkzEPoFAoob4kSO2jDoJUcEKZVuy13mMAT0c0RX47SCJA6rp6mQZl2QXT7PgxfPxEmqJxNW4X8wVsiPZnKQ/MnxpLW/njDaqSWoIij6G86oKkG37QFdOuW6+H0i/49p6ffyyXq2dQ4PbDKVJrM+Y6lDqN4VGiCHGT5nj7zyTk4fpPHJspFBwx0tA7wacyUwNSkal0ojjLsrRP24vTBTyX84ISNuPjJW2aRKHPyveTaaDKbWZ5Nuck+uwgIc69o3GQFfH86uHh74er65sf34/r8TTZ6x67m08VeQoRdeOsi9N5+unFRxcXF3OwTePu2fubdYYedniYBwmOrn1IMGafvhsGsgmFTfzhJ12gQJVpV81nczztb2HLfTDUFh/EY438HDiN5QZYnmpcfYIkmRKmEbWg35eRNQkDt+6wIcpKn+IwkboBHImUpYn9PWo80GrMw8laiZWJMS8UwVSLOyaDh89WD8vD8SezIKi/vnt4GhUcxSld3V1fXbxbddl9dftlFCR5iG5dEj16oHq4HiOMx9E4LJuyOR6Xt3dPfgwBEUIVYuX1RYr2a/10EGiGaMv59apVbJWoExGehZAqxBoWSQKgRCryRIskN7G36VZ5d5NQROqrO88eZmZQpbGXrt619npCm1aXJHdSnpgy8sUEJ8XKcAdPTaNzdk54ovzyIVmKh3GAIX9m5Xy+vn1yd3N99fDktQ5K3BOL5vgga3Myf/gy/PZ+Px1rEwWddtMkW6eq6UkYq7Tzt87Ki+s/3uukskGEeClO15+dBPnVHIqUKjYdVsNysbxYzOefLvHpOSTUWGjql/LAVS138Q6cm109WOvEtpvs63XG0oKeHOHp3KpTPZhi4tfx+iRZRhxmWuG5y6Qmk5k2ofpFm8PGdaJzGKKfppW9/9OHpL5LOWme/2QszaUacHUl/iuQVW8+W91/38mueyQQQSxsphV/qEJdkgFXEK0fLpFH5dmHwoYMP8u61S1e6+Lm3YobfNzPCzOkzSmGAuUdMzBMQDdPMOEuKRCqZtkzYfB4PVTbD3V9E+DGfhFPbSpYi/YhnBiggmwqA1mKY8SrHuG5tQyPQ5y0TXP2o77Nh1L5ySY43+IT6ksDh686wwB1KrMEomqq6hgp4P6T4PDx80w4ifDQVqWVMb+qsk7Q6S9F3eUVnoWwMmmgr/5erW7uPlgtiqnCVEHC5VNsmaRZMQ0k5pe5RLzTaIwj4Wzj0yVbJvWdCh06kVKi8GRJI1At8TrVUYjSuL9syyWpS23J/RGnUlfrSRIcOYClXHM2TgKUIF0rivruCnyZV5GCAE1dMJVVOStPH6u3utuvw7RFosT6qG3Bw4l4L2wSkvWWPnk/ErlL7QFQScQvfH376buzdj4/We5jr6pZLObL5UWb2nMSdZww4iFUypQkq7ER9flTjrywKi8tvS2TMr8xYmnUJdLUF6VUkEKj/PNErRr1mVROxj10ipmTJE52o6bQV3AdUp008oxIsYT6NukA56dPP/X47lvp8q6GDKdYSk/5LhU4Xo85E+NYXaFTN/0CqyxNP7rE1+YWzbxdNG1T4FWoNV1miUDnp/OkuDOFYuCMpWjqPUHCc4As85xJlNuhzjzFkKN4e/4ho0Y/jPqUR0yWPzmxlTwcZgx7CnPHIFI14nR9KXg6ucnaHRSMmMhsXDx2UffuY9ex5yc8/tj0qompROJEIpXgf//vp6tISXrI07aDVXKDBpe/QXnqFC6uElZ7BVqpSqXdj3E688r0hnajkAd2jqNMtj+8zNm+jNwPFZVmpAqc4vSZJ+bKkkslcMRkRq4MepgPdo6zZ1HvUZJOVZ5w4Aiv+6Sqz0RZMQkmG3oDhBg8o+TXpyK1DfkCWZhQg3L2Mu3Gf/30/vpUVq98rQrKilMKMrGBgc6JP3KqMwdYKlP6p9wzzcWAVnjmXHMSJwu95xnSObLa0/sH86pX4FSoPpOiu2ceLUvuN83KZ0qtSzSRKHdHCFT8bK4ShcGTK+wQPM1AyfSJmfAs7lGj8ttPOyI3mGbExArk3uyG6zoI4mXh0wFKm1Z8ciRZh/EAt0KVF5w/4wTJNGZDyhrvjLv25ChE5dvJylM8ObIFNaaWT91qSeiyf5IjtedLgMoOX4C6Ly74zl7FaQaEcHphCtP1vdicPKtISYozVph20Vu/PtOFwfD+MOEP8PXIcdTRK1qgkVY1Xo0jycg5a/8w548xuKsOlmxbUAlPnBOwJM9ctVrFcLEZ5ar6hPnIt6D3+AiQc6tNlKYMup5P4bQusdQjk/bG9hNMy9Itk3Q5L2ZMHUyayhPLpvnhoLZAJiZ1JAtpvv3kZoF0+OfLYVwQKWwTO0M94oR1XcbVSSRDRdANRpOydqLJjk01/E2i7rESd+95i2uch+AJc/WIjRM3RIFsHHonl5nlVObQrK9OzKO4oE1tmkqr9faxWxJ15uoSh7kRRfKUL4BhGCARGkzyqjYUetJ9chsH05tvs0UoP7IT8gkHE6CzbnkR9OLNcmTbMrDkqTNOnSlEYlWgoKimGJUnhIorVp7yvS8CxUw8gdqDebakJkzPlx8Q9c+UHU05U7GSgy9NpZYkC3idNT3QuR449QLROavToK8yprqeWb0KotlDN1r+/sIwzWqmVXqTMZEqHdOprNLXnQeaVG2Qt1FFcvqqGQsa9RlbVuWXlfVM+PQzz3Ex3J0+Y/DkidTZP1ZSty3PrJ/igDTF8YEdRoGIqRCVAxW/LFM4EagCpURxsiUlQcMJTbpcKvJ8e0AmkXg1i3ihVZ8HUf3QBvFXt11wvthBrjSIMMKMB/PqXrMa+gLXLVBe2DD1Ro6UNTFSpxb7xKmhDqAJNKvG/KmE5WPg8NQpd0FVmh6rGcXqk6hiZRflWnwr9yxIOkCoM+RPt2uPdWhJssijXpnw4Gl1CZPG6juZWnkn3XmKsbqtk+rukzoO8uNppMlyO9cqm92gr/5+iPXtoIrbk4D4Al6cGsUtoh4nHA3kBC4GpciFUi7KpMtdefJVnx9yckCd60uV38BnpU9ZmuAyIpW+ScECIQqSW87rZThnVpF6sdION3j6A+bvTroO/nw1HV2v6iAMorwM0X0Ti7eIHMmTNwTdtesFsvGesA0zRZgbzT5z/k9lwqCreVxKkadSpShzsQIz/1EHBj2RUqZboW8zpcmPikqoEyqzJyoTfImLa6VUi/1WK+pg+lpPrc4c1lcGRYQKL1dgPRO1iZDNFrXcVzfvBtlNHRURLKnGaZpMoVTKmI4AHdKEQHHl5Xre8n9PMn3rrMia0GdMXjssc/U4JxgFJZtRkINjeQqnglJgiROh8iFIXrB1Kgru1heYKUfw2zaQZMS73TxcgOptXxJoiZIvK/oSBphwwhJT+AYOvmn6Qu+z6mxQpaZP6y3dbX5V8a3ldV3fPhtNYtm1y8Oiwb90ouCpSnqau3e5o6urZrlcR/qT7KWssdKDtiPWCUOfONk3cXcUCgVBijXk0ZuTZ4GDBrJmIed261afZIqD+bPEqQMTqfAUsCW5+kIPsALUfVSUWGmDOmlFnmY1eSanRZgsVLVwd11U3nSj46xBgFGQ7nQKNYss4g1t1Y0r+ylbIVk6/U+DPplAfTNKkG7NZFEvWI0odOqIGk1Vqs+jPVA38Xl0iJDn1j2J0gCQwa/tPRQrhkkzgz5VshBqo+tPK07QZ13HrfHsXF3Cv2VSR9Xcxf7o4cVRcDN/tWgI0Qc6vekzovNv9BZ5b0so+IhM/xeoEsXBsJe5qFLRYoaDtUgjnsYlvROqxj1M6Pkn9Nbpb/PcV8vcTnNpOMETAk017CWdQqrCE3RNpyJR1+ara7E+ql9Pot46xRllotHWU1ved8GbPy3mcV0zuXp2pEumpkDKcxtl4qZWyawwmeO4E/uuxruFaAgfu04USL1MiXNDmf6PaC3EQr2cPuGzAof/8hKRptzLY4knT9fe73PpRGs1hUKcAlEafJpbMzWzM+gThSYmJaJKJlWU5NBskoZVohn14nF5VFwO8/Z0QZjGyfG0ZaqRjazyE1zEDEqYpC53/6fOnucBhKrL+lxO39BDoLSw4Kg4gY1sC9zCZRiVpo4W/GGRZcXQ4VSTUg8TkZpESzoMihK+xQGeMJHpobb6TfuNe7jcIKG2g0iABmGeONklscyqjGjPjvXV/Y+X9RsfIovWpylyQ0bwMDpnOkk8aYL2UK3UkehWlf9/sBQl3AEO3IAnTBfzmFmZjxHz8KZSP2aZTkBUqXK7GR6qzcI+4t1WHnOoPRExoghzRL2UJYEpPGVoYZCqFHtkgIY203gXixLpk/OjSb4Jx+ZXiza/frICyxLZtojMPE0MnJlWSVD0ypTKe7/g5LUNMtidEanroORJCIFq40SGYmTJ0mSYzZhJQbDoMyek62ZKEurkpr2cKWbbRnlK1CPSAbYF2RYn16Lg2ArKWfNNMwNPVeh/UNx4tX6oF/n+1V9LvBSeno17nNyMtvJkSZLKNHK8MVNiCnQzd9qdma2Xck5lBnbuMT1xwhQieDLOGe9mmwHPIi9oYQx5MeVpnxBXtFsiZTptSqGICeSpUwj0UHgi5nHMvhG4A6LTMtQ5hnU/BmAtnG7r6KG6fffhKgS25rZViHQbU9Y1a2KNKeaCzmiSZ+SlqoMBhu2ApUABUVw8ibXBx228UY/0hXAscwW8rU863mVCOFOkhEmTCZ/V0TiWOOVyXFslSWv9551Und/A2mcGgo3waHovp90pzzgqDn96/jRP52lUn8SbuYBGiH7Se0a5gSVMChP8MVZRhYEcefJNHac9TDdnXw9PC/2AFb1ZsYWzECNIw1uIY0r1SFnpfVnatpIKlTN1LIWs8lScGNr2TOLdMyNAminU4v12GKTV8u8OQKB75B/yN6Y75oN/S699JYr6LSmswSjIzXzK+bYJSCELIRq7nuiYadNW9hr5vBjvtFicCFRfZYXHaTpVVWY7PEvydBdcq1DFdKXUgOc3Z+03jqcXqOlxq8NHz3n5TFzV9c0H1d6ortObsDn5H6BEaAFvPLlRumvKEWYAA4ezV2clLJ1EJ6CJaE9ixjvRkqdipJEjZ7lXohK15SiYStwTpcY6ZzDi3CXqxEnXGlEskjSJnmH+Tak8aUZI4337lcWJ+PZyFQd48zSus3XZa3TXAptYpYLt+Vzqbsjdrz9pxBr1ZauqrCpN3Mpe0yjQxsoznHiMdE6Ueu+6pc0410Et9GkUPsOEYZ8qU3b1u0xbcX0GlRPtUwtD9gRS6ZeMpk3roN6+Tx50XK3nHXjGp3VXh+eLNksMXB5HZlvb/USacABFa0vNNmAeHMBZwMugOA8OwFLNin2cJzEXoPkEY5KbuXgnXOnwFWiuwswJ1pFVroQqrWomBp9SotsqBUZxSrRxNFvMdMXUqjjhdH0k4W4XncNpqOsriulhdhpG0XiRBfJe8lbb+H71+JhL0f8WrfuEjqlXbsg02dsDNwImQT37mlXBH/SBP5EkChOQwnMqMLkzambRTrACbRz2hld8kcoo0lCJariH2osODSi9omS898q0Ce0b4Cypz63+syZUo0plpvpe+BA1TRifvXUUjOS9bvxmqkvS42P4zQRsfgPhCJ53e77aY8q5mS/yBlT0SZqbFsPYjCpLFazKlQHfR7/eCU4mUxf4r2LWizOEtyQqAh0CI8N+6PVpMt023IMjDsEpPMnOoNbKczunKkid1SnWSMtV3QRF4XCH528cx8Jgk56eVt8lyo0mLbAKZTwjD9N2VIwqjRVqMsFlVCFYtekkhwdPBQlvUj3CEeZESYKuyx9TpPA03zXBQDct0mK4qU8VqJofjS1Agij6Jc8TrlelIjUzgY6DqB5FdXM+Gg2/ybBAHddn+k7x9rmwPAakUTTaFLsNVKfTKbhywnaUcR9ZxNszvgPjecD6v2lAOsl1BEQlCnhg6vpS3MKOipyma1EAPLLGCcOYRB3mIXf1gVKgFkWq+tyt9MRpA0P+rFWNYg6eW8mSzo2bUo3/DtiWnq9rCX+44nXWrDI/y6tkA6RDpyed9U6U614vXjailKsVeJvZyGFCdeLiWSXInZgZ0MlmGpVXBFsoI48j104ZVcoV3m89yyp0CH/MMv8fuyO86WnikNp+dgyq6UCAjUbyCW8Yhdpbfwd6f3UyDurz5aBeZ20WRG/Hyhv04iLKM/crjuGIOqRRn8b235mVhV+wWuG3kOdU3IEo14o9LRfEBhRxD3d0hDlcTtP8iaGQ5uno6KggP8jTOn0h6Z8kp8IzBU7TJie7eVS860LPjnGBaarxPhiNNgHa+l2sl1yzEgx7e/XJoi6j6PX16XEQJpGQw49Ojt/abUF3bzi1PsqM6Mzwj9kLKg16XFLhHU82TwGAsuZTrHBKMhfHWBcPqNaYjmMqFQhxmUBtJo/tdI9E95qPmUN5yPJz10yfuM5orEe0nQpkcP2bg+N1WNco66M32mEVJcVxcZycud+Ztk1wZL8bgPH2csnmMgIWiWLEQX3CgpGbiT84OMDUpU4ewnfCRCpvg5zTZs6BU0pVSXJgHvUFHp4ot4wrJrAMhelQCzxqO5v7nUaUOMmSTT6ZSv9JM37/5tqzRmt1u+pqQfqwRlmpQTE/ej2N6iTM6+eUFDCOcAGHAsV8b7swkatWJXLd0zv5BRy4wQVqcAIT1JQnhs1d6Ak0KjxxMJfCiwlIoCRV4sSJ+Cfc2FZP1srT4fKEs0x46sqeKNk97Vp5pl4vX+OP03LgmBlATEebaElabQAVrhcd1kfDm3PrA+riHEgF5SAa7Q2cLsGGSvyHkrPLcRoIgvAQwDsSUlg5fsobY0OEgsBwiTxxBCTuwSNn4CjwxIngElR1jcsMIyQoPN098RLYT9Xu2eVHWMnRAx6Rd5l0P+ANhwEXUjg14ogrCAomNanPBTSDqacRHqWSel0WRSqqqi1JlIEQRVDxCb8vSjGj02PDo6j+zkMECxuzVKr+BE8ZVDyRaqnkCoGc8SHH+w9fz+8/XH+8eXj/4U6H1cPTR4d1JVyZE4narMhtM/X3eURPYkN6dTHyGmnSnDdjPgqu1Bg8aU6JtvQRiiiDpA3KWBj1R8xa+goJBZH6+3mBkP9DAUkG1bMw0qE6OrVW9bRnDJ4z+l0QGYfNqsPFQLdKt9Ojuxfz/OLV+XT8/un93cb6cBuRk7hdeAqFgI2Oyxr1AYkFEErVjbyFixt6ki/JraTJpWcp0YIwftCalgyq3t/1GABlUXkWRKWicY/+J7uCrezZHOu3Yt3+oTJ0Bs75eJzPWBTikS/cxLQOJNxIp4u09bkbnMk8sdDJiUQPd9Pl/O7Ll+fvXr59xpuH5eEhyYXkqQIrgGZ5M9AFyRwrSsLUCwSaQ8rkqxSPUnU+hNzCnIJkVFJh4AlfRCOzKJSQcsVOHO+rRZcN47qeV2BFQiTeIzbQ3Om2l+j1eCGRYBJIKvHwY54Xl9rLqNTju5/r5fPLF/P9/SFdCNIaJBXkSQmiECpbqsOeiEjSCJiqYWDCQxBNAvQ8EkptPOPvEHDOrzbVoibRPJMmvlgqMYYWvIRSaj06k+MDaAXn2HU8j4w3rVv1Z7oMSSbEUBn8uMz7SEp26yle4D9Ivp3Xr9d3Xz/QnITGn03sKQ15HPhOqAbzbBq9UQZicYwdM3scS7OJKLXZppBOSyK7pUmBS84EzxjvgTC6Hnr8cAqa3Og0yu0Cxn3HkyFciv/15Rj+Fc6I57kKTU+WtmpKSb0MqsBK2ZwsNK8wukGHQo0NP/HxHSBhP17fvw6geBuIHzhcr3lgRrRVWZongKlwAlMoClZcnvLseY0n1MqNJmeZc+cqW8qmgKaJ/9BOja+WQJPzCUSpZTlb67KAp0WMYGmTrk+Vb0RKpuHPJKHriQ8cEcmPGFleGAKVYPGP10gylRfbT/34tZT19fk+Jb6OOL0fLJG0DJSBMVJCEEMWISRL3gy0SFDLsZrVU0k5QikMfrgWcoyicC106lIfoIU017IAr3laFew6G+ZsEeSxVgkOpPWC285TFo1ErigCXDQ+vrUUeC8v5kfhwnR5eM34sLKu8U7j8HYd8utAY6BwsEi2oiMtcRRK7Cy5Ey41v7wXUy02pBbKVqY7IpFnaIIxmUmRMLHsyAV7A53V8AhPYdROdb4TaHzGPhlBqhRwl+5lTMSJn3EHP3H/4UKa8RDA9+nBPM8L0i2/HfD7TL1Bk4l6x8vngEqRwT71F6SCmCF3vSzayvO+51kUzXJiul+iliuDJ9lK1ZmKM5DGnMf1V4FF+ObU89SWrkRNnkG0NvTIurwO28adr+/x0M853eb1hrM3DS3PIZueHqpBFCExcjGKp3zZNzx5qtcrzyywlq0pooLKVCbkViM5iqtIVpxlk/t9LX92/DKbKJZ1VPLzk1T89INgPJVYbPwoEamB9SiIS9lev0zXQ7qblofzt6ePtncTRu9E1J2egN8jPgfSyGSpJbxkLoyUOGq17W/D1myqfc8b5hmXqD7EDfY3fkTiQxUQd5rLjIAUHBejjK25/sbTTLUQIVE1R1U+C43zqZTXhdvLo1M65CWf8rv1YPhKVjeZmn3e1g6Ue8oc28IcnSyR7R6ncqYqa7mvLl0r0MIcQWzd9MhzYDVKA71F2X62LUa7q0NjQ48FcSzsq/KujMuNH78uueFp2fKQ7kQWs5hX4rjzrUzd8r2IUShRMhinItM+9O3NSlaNruPoVDlSKiWB3Ka9ep2BuQpfw6tY4c8T1Hy2dqF2LY0/+C9ZNd7llvU+Q1puxEz17yCOgcsf0Ll0VxbUqA4oWo1u/Lb1hVOW9EGqVcBU2+PMf75HFMOWpeoVPJk0lFD82ek6MPn82T5CbcpWfs2sGJa6PaWRQRqu48lPz+r27k22X6rteze5Ol75EPWupv/9IFXq2x0lA5c2ciYijakC2vGxlisLlrtdDc9L4wjJZyUGVjuCv7MT3V7BLy9DksGJxRKr9mf6/u5/9buwawmmipx8bmJwbiq70pXkWYTAwirOsObUDHnPI6uQIZd+zDap5L4XTvPsNPzzy2Xye/QSMeQW99CY3GSlbKYp5drvf9OopdIwG6I+R1WGSljSpGWiuFgp4+KGnmSTMwEpMxZI2qFn8wxrYSEjdjy6qtcy2KyK/yDAE0xT9/Az2NawfxtFBtp3vI+luRI1zMJFsSpWQBXQalRldboiYa4CqWWZpyn08h2c+P92M1/Tv2pQbJgmw9Peasjm/st69TiSaqPsGl7jqJeAyqBCaHw7zFa0KYUIllTPs6oF1dPTSaAjJKD/rpN5OpmeSgN0yogNS5c+hopor1yfnup2lnKlgSpaHu8ILUZv9PURfzAz3DSSMOTF00D7njdqRBXmsmOfSk69hL8ZRD1IZqvnmVTmlmjf9ELJrUS6EwsljXhT1dpJTguCFnlFLr0xIyCSZshTHhehIuqT1hXBmLRwtYB6agGU56P/khnvMJMYWuSnwr3eK1iq6iXSQVQqCnJo//ystnTTWy3ewLnYpn6M3sQTMkgX4onCLIeIfxh0M3D+xdgZ9KYRA1E4qtSUUytFPdFbW1EhEHT3Ui0KEkiJOKAc+gP6T7jtb+r/63vz7GeMu2ln8Xi8IZdPbzz2BpOP9xj81/TpXe0tz1uWvO4kVG/la5QlcCkyVg/LH0U+W5eZbGNZqBNGjGqhUnmhlDmha2XaatQprqUs//Bl5l+WV//6GoEUpUsDbfdJxngVthv6qc28aLLx1RQkL+09ebZLUOU5TSiNUzDRndjRJYWxMxtYzbipRnq73ms9zx6+flTkd8Z70iSJ+DbNq6lTSmyzPSjKoTUSrZ7ecVCluRt9u92ktyhl5YmI4ixL92zOd0+ivOiETzCtL4VG4sDqq2867wt9Dlpzkt9mO0d/lSbpsVPYWLWWZ4NJnYzDC6ilWUN1yhurIN7UevnM0pWJZqZSapvv5lE4h5ccnfj2t0TlxH6qBOXe8BQbZmsuSO0Eerui17B+LqLWrj/pFFwJNPtmEUqEcnG51LvIo5lnduLhZlQu9oZWb6vM2z+VEziFEqZJGqIHrTnlW6unzqJUutzqR3cI6kf08LcWctTlbJc8FZUpFKHMO/hTNyaNyczAiZ/oNhBhlqS6Sqv/3B3c/7dp7rQhbOXpocHSNxslNA39OKQy0CMTHNRYjePpdFW156U2kaqRimY4nEfYLhbr9Z1JmiedIVl6pmhCVekxUWvYk+29Id5uisr02WKMSyRj1Jqg1c+YOHYnlOYqXfIKemDATxTrMAHPYq02Yzd2q37bdx1c3+FihPGKfCVJ6lSRDdy7BWy9LDwLv2sq0q7JKHRMXyvY8vZm4C+r0ia3m1LEXkBzjZ8yLz0d6xLVGF8BlVADJQynLjvbOHbk2G276AFRTDu0Rb/AeIv3QK+mWGCOPVjKtiVbDSRRbITrJnMdR7NVP3PcWmZ4p9aaxOmUV2tYOqhxJsi51Nu4PPoKNcKgRmQ1BLkCOHEMvyVAAl1AmNsFMIIqmaJD4zDxHzfgO+KN6wVea7zYTKNos6r0SXj18lMMM1E5xWqt+WYCqKBR6MwIQ6HGOUnTeixKtTQz1pLtXwmTSQ6cMDhYvxqDUB8taXKbbQ2I5EiJLtho8IS4DidtLpdLNvO0uYpbfr5joo7Lur4lJ+DJFBeGGnhc79xnRZ/tKmkCa/0AT04mmsrxDY9iSJxiiasDzlG61GwJF0BJj46hUJrnOvGUF0/Y8XgUGrGpUl/OhOEzW5s5od0Xjq+W9bu40CuYNm+L6CdI1jJ1okcknnqU/Jl1GwaacXRglSwQynqkejIWJGGzT70oSp2MlnoR5svLMADnUPA4sgSFuAwADcX6+jdyLIwKbHrHzfYoo1TcqLIdeZNpqBqbpc15r4s0WcaDJSmmJN/0rEOjsnurYo7Wg1C/NbqtUjlgSXrraEuKUSiZ4exAcdgdDvvDDv6molRl28wK0BiXlG9mhLx+B3cOapz1Sv619bsFmruGZrWgt3EQYy4vhZKGymFFhu9g4vlra+2Rk0gpfcO/LI8AthzCjsMRMnwZJEb+YDfsguX+ef9zD/8sLubnzFez0oTRbzNRAytcGwNbabPW6bS5rJeRObrzHAkNlifoeb/y/sSPGESCrzZkF5NlR132q9BkFJ0Fr5LLYCgx0tkGYCNLwARIADyQ7S5ADmS5OzzvCfMJrybfTdJTo3N5Anu1nZczL/GL5iqkW68D1cvzp28pnSm/jBH4knFnw0PTY1YjWHYbqpEgO5WeUQVHFcczJAvMMnC+UJPHEOUAR6MwFTCxkdV7UISBJJI8VPkkoAju2tJuZOxr/elms0M3csU3+0OBkL0h1ZamxtPzJzr2PCczJ0G8Tn6oc4pNIsCRV7Keqd2jsWeJ0UIIhi4Kd7+QJnPnEg0LfucBI8W6uQPJkOP+cgh8uJ7AEDSfYJAnRkLXmvlZqwruk3jdwVWCrnczD7I8+BiD/965295AkXOSnOPcL06p8CPWOupHkmPAHINWTIqdGHIdTqp0xAmMfZ8VKY5QJJRplkMS4fl83p2HCNEiu8/Aie6w2+8vl+cLYJImLtF8fARMxNM8xcwVnlaEqqHguoqb5QMRiAQQvtMZ7BmPYmllLcAWYPn7kUtR/KayeQ4rh1J4HA3dJ351BzIapYaJjZDqy+B6EgW8Hyg3Pe9JkT0rNiPWncDoiRLrneUQOGHR7xgBn3I8GxP8Ap5kB4CP6PdBFCHv/KwmRU+GzmeTLRug5lEw7KrGSk+wXGrfzUgUJPlVIP5jhD7brgPA/vgGbumxz1yTIsiBoM+acgRJMlxRkkhmImQLbCNAQphM5RBoAkyCa68jI15DlkpuOXHcISRNWmBkQxRFiDDB9aLpEvPlE0SKnkUI/wcTMAn1UWjMxyvy6jbuWH/VswXB8aNvQSFDnVKNA+o4EcwBjoE98PVOJPVxtQ9zpvAcjWOVZ3KEFEkTl75A6rTRgVPEMCqzrH6MjR3khzgAB1fCNMo1eqLUMhxAARKvc9TwITNFaKoHqJQ4f2eNHi57qhLGfIcp2YGURDG4w3mib2Wu0nHLdFAyABFA+V4kklLK6mcM4EjuLa4wfE8KsUZIquk0kEjiW/yqc1MfvtDDAPbD97lO8fmL4/xlXJHfm01Eqj0jxNknef5h6wxWI6miMBxFQVCDuHUhGgiEFGkjSEhTDWmqpRdDLSqQZYOL3hQErY2r6lW3YBMGZpUXCHYeQOiATIReNILL3hjiIgvdJTufwO//T1mjjKeqzr11MxlmvvnPPefenlTt0gMnCH2K8Kvk7bOph3B4LDFWWPqApRGiwoCIGu0xczVJC7SHKI1T+acoBoMMnAiUZqCef34TiExZb71Dh9e/6SESrQmKEQHlAxoExwEYOH4AQg3RRIfTXUcwA3Re/cwfpkj+l7XvgwErl3T56qd4IRk0w8TR82SgjEU3Bj2lmZCoOI4QqudLR7mMGyuTEI/U4xOT74Ozn1Na9pd53+QoLyfHk9DjxEAtSxsMYUlLT22BnXyZAhKDKvGOHmH4JiRpP2kJtgY1jdBwyhqKYR6JNp4vpb4xMkwnmubnes3wP4/nQpuY/M5rT4/iDIxy+0Q5PJsdIS6zjK02SqEZTm94Ooy0DkozTbjcJJhV2eR0rB9ZCJp9mJHM97TwWZrjZNJDi/ge4V38U1rCcqKMrjM13kKLzJMIeUf8erD1pt8yGnBsan3XdMxEJx1gyuGxhmEDLr7scXdMnujWF2FnmrSWJRcw49VOfqzE68LEt1OlQzykCdIACkmdjbGV+bl4Yso/ds5BmBCaIC3mfi6eSSQjqiPMBREQvySgOSYnUagrvAXzR9eZdqkVyql45zsKyTO+skafPLgiYAaBeLq7OaFHHaD48O0P9fBSbmjM1/DC3DJMi+fUmK2FLJatwZELRZrm/z0NVleEuuhJm1z0G5mKpynat0iPjkYA1XTpI2maWJk7k9uaft8GRUJ7Ykli0OupUXkJT4ETRa4fAUphFCMTs+4VHCdcZHUMqNk6zbYCZSAIWi0R90wIfn7Wo24xGIM3vhYnI9Gl1SGLOPeP7nPinHT8qChpUhxpXnvega2ZMh3kbkQ2It2tYLYLHrI6HpxHEDRQQ8QxZ5Lkm3jPJUZkGan9WKaKaGLrErhqesfEsHB2Fetgs9ER4GYOtQGXybMX6T1l6iwH2SKDp+M66NkjyyAIZk+FkDBX5RReK2Woxur3vNhEszF+OdzFnPbddx3jRLizuNaJ25ImE+ZnSu0BTxWSraVpMTYM6Vil8lpHsgafWZkz8VTxjgPpKEnORiNz7BhgUI2THukpyfcMV9V7P5j2vewBq8y+V3SF18V71wCFkns63AROT6flQNNphjbXi/UirWFabDUvYKTBB0PxdGi7Cw7fvwL2Ln0QM863mmnwxXuOBGIoEoxY88x3cdxpljvodEdCxSxSozRHV5s7zWJSPNGmiAIR85QpN1MSUj5Cf6x8Esd5Jzk72+M91f3d5CwKIsyd4Bk77J4/dXkaAGmk9XYG7YLvS6ZFdIp1G55KUEEz1IrPavSYLbB1tchcg2aleIolVKAprOgTZMEVRMCJtzgwaBnCmfNT95gY9a3O1KKoVB0QydphUBNKQaMiokGcbfaJWVK8uOhGkQlPFuiR0UOhs3ZxrrNj18Y14csVSfsM3enCEi+CcHjMMo3tYZXotpwyPRaWZglJKRWK4FOAG2fXJDGvLHVE3Zmt1+KZpXILmjKrTk/NU4EZmUhEQ2gaovHjXSQvvy5YqgSjMjailARbU7bmvn37ALegk5MEfX2mWxY+5iinRg6WbiErpBHqNiPlzsJUKcTVkAyOJsUdTZ8bC1Q4fW+msk5inFakczqMxO2SRqFN2NPosiaX9HxbQFJylTnMey6NsnSxqDgWqc1I6yqrqmphnqDDYRLcu/D0pSSDrg6G2sXZ+Wib+OYEGVfUklwtTngB0fUknju6Ec3/2NAbaywdMdMLoNx7zMXQfz8rEz6n7fYTRxrAMhUCSociOKbEMHXcF1Rx1don9xAuV2Avxc+aJIZlPSEMegz4pD+ApL4mJ5MuuUOfKHOx4MxQZFplSkbModCF6mrL7wDWNAhBx7Y7ng259Gk9/y2HSadzOIwfHA3aWDxzQwiNziaOcq9ZzIxWoCt0e8ZjFTls9SgdUknu7x9xkH5ixgyesZEZFXxkHPtRVJVfJQGUudEA+4mEqCvPBbXPyudZ30KcBLceCYjGu+saGHhNXthLixqmJJLRKfGFF0IAzcSwKEp41ll2CkpPpcyfzctqkaLwfWZ5xqWvbM8+J4/uFUU/n5FKRPQNxvXaZU+QkGsf5apLjfHFYWeDYwC1NTr0WHCMWlxb6QfiGRb/b8M1uoXYFOrqacTekmyxxsrSe+ymKbg5U+SetGmxesPjJEwpCGjFZRdgFmFBERTh/aOoimST6gvolUo5mMrNOitPypXvqgqgLJCEdIsHNEXG2QbpcNvvCZLDmAW3eYQNf+bH45POkIiXOVNjNK89Cbt9uhOiiy5tG93cbRPTXonHZxKObaVpSRLnyKbFFOfsYAIYMQIpGY0QpGkqp9Oqgxe+ryLYrVMbFPsMJwCFJwqNehOeZCBNj8S9G06yObywyaDH7ghGYTp59sWzL/oTyZTyHprgxBzadZ31VG8S7vVpYzUDJUC3dmbJzlCbEgfbb7zBXAknUeIJTsP3mDw7BX+49Mvu7uFwyDhHmDttDS5cTVQ33bhaXUqGH1P/cEKUEhIVOqglQsJYpjXOoYAGTd5sLnohTJESTyMl/JuvBN/k7NnZVwQ0IjVJrcSR4QT5eR9TPHOpNe6WzuJLrDcg2C8tTIwyMv1nKU7CARyd9ZpFj0r3spD1UoYzBXpW1XVFQj8PnuSiWlrdKi9Xe8vt4f5wtn3w/qGqay7bjEpwdpymx8X8dMAs+p95EenKHLDumV7EdJNyrE6LkWuGJ8VYlVATyCNDpD4HomRKB5hyjnB3vcsmhvizXU4xE1ZxNEyvzyVOLdCdgr6SEJfeA8YU9Ip6+Vy2tGCXJ8ulwh5G4HSSXpudJkflb7wju1eCOOQJUnVpy4x5c7UqhdV2Oj4dj8V162Gzua1X+dO0nuV5p6Ot8B04spd7iO0uq/Gkukif8fGgZz872MmM7mDIF2KCbBHa4pMdG4uaoWpIbbMdvbIR8Rr1T9Bh4SitajHOsPfkGBXCsDMHdVsrWZ1YpHdB6yvt0G1CPk59DxhboMum9PTciD6dcFypp+syxVjqZAKbMtLTkpLE08Y7jaSaVatyVSNJelCUTM9lz7fu766uNve3tzcvnx6nq2nyedIhHjsEZKfDBN5Nr1/M0+wZf3VIs9Dj8N6uMwm/aO9wX3S5D5KsYrTKRnOUBcQ1I5KncGIzQMn0PpZ9Mgj4RmfsWZqrWHIwSp9v9TOzyUcNUdGyEHdxEfvqBbvAlMMTA2IeHBEn8yQR7hUO6MK61u+J0jWxLJWeFF8OJgp6jFEMWQIwQpzG2aiui8vL4pJwr8tVWp+fVmlZjc9R5/z8+fPnL1483/r56rerq6u7n7/dbO439/fTp+njarUq9hQY3bRa9dIfHl7My2S2ewgpFKvJb3YAkSFDu8dF/rnryv2jj3k5XeeIlV7OuXx6KpJ8V2xnsf9rWrELfDAUKvSJwVdGG3WRYxzDKeRn2lXqaOkNwFBdP2Jbxi30NE+GEcrsaHZdsRPzSjeyY2j5g0jnbAW4cjujkuAAWLuu0zHJkhV5RnqxIdgatAxAdFXVP5bFcjlhicmvWGWL6qKqxhfZqsw8mSLQrauvvxbP++8k1LvN5ubmlvP2z+vp7ePj9PF6er3ZvJxnx3l3T5tgnTwBx+wAuvzNL+fTi7q7yzgb40dfnSUny8nq8um4+Ov2+1+/5d8lTzrMlHAB50iskK/mU8iNZCB2Omr3LjHzdEoP3ppgmTQZMU8C2kjjfxpox2gZH+ZqtUjyzvPY2tBSkiH4NSvvVKmaMt37biX7xIQyXeXuIyFLXajTEsZEcZUxwlBFw2IoW5fK51VdlAvAnWZ1mgFzQSoibC7ri/oCuNXW1+bJhal/d3d3dffT3d3m7icUe7PZbOjfXj8+zZmAn566xTjrz5KO13jHFw8PDy/n5XHSlzFHj8fVdMo/x69878vr6fhpudc524UU+Ildqq/Z0JMBeEdsX4hYm8FhaYwYvoPXKd3OQKl+U6Bj8LKXhffmG5VPjtdH5D3GPDWKYlHCLaiCDi2iVCNEhwKaNfAAWmUgqys4ncKrYoA+hvjw43GGDtW/UBLCKkCO58T8fH4+P4fnN998A0g5d642CNWMjfp33Yjs/e2ff35/c3N9ezMfUxQMxtfT6cv7zeaHP/i9+PC0m9Xzh+vnL271b8DwDz9sHn6ZP63QR0c4pczDouh4gsRGeEU596HLhuwI5xsHNI1ZN+udRpSQghw9YwSocov4mS3cZDFjFuhwoBxNooZm5BnS86DUzDdeiGiRapuo4YkwiwE8hRBgC5om38Bu7LtxVtYZAV7XiBgR/c2k2bM4EUVhOL9GEATBRiIrrKhlilHESiISAxHBWKSK2wwLGYbANm63WIhOxJBGyEDIByOY4IAWEVRmiyAGmyRYJIWwlc97blz37HzcucnC7JP3vOfMzQZNAGOfTeoRFBWOpQOq+P6SK9t0sJnhMEuz79MYm4UsJ2mXAF3Wn0yCIIim36c27Xh++9aPBpEfLPMX7uAIQMLWLougc0ogOdu0CgRFEKr4WMm5eL5gBml00TP5zRX6pLU8r/UgRhQhQeTHaoz0ybXlM15JQJSDd4UyowAYP6xn8McHpTA4aAaPH1tnxALmDoSgagLd4lTYQD+6FGLkiXDDelLXG2HKC01eCEQ1yMX7tUajVjuWDh1P7Q7oqWyZkFJPSfdqMWi3HvFdAcEsy17ZHJ5hnvzq+7Sf9Wf9KNipBrv6PzX+fOTnviFHdYVdkvjeVo7Q0sZApy3L85cuWhUE9bYFgqnxzKsxynfPo1UqjxPrLomdlyJdWkPu5i7CzOe5tKA1F9JqMOn3SSWi+diEifOpjlNmRFIZbhwtLKerj+vvqjcQ9NUbzIjksp5cr4cl5XrzgFyXfdaT3O2j8Xi8OimerDbjdppOe70vxy+Pj4XV6ZYwwrYLoEL4v3N26uWXnFQNp5t9+QTQTsKvZtEkKuHr9B2HBViBTwALlQRkd+5hjLJGrbbB17p2hGutvLmqLEICNaTApB8qIE4F9c6apW6+i1G61SCkSfPjUtsW2K48o4gnFSm1Ys/hYeD70UHTcEINFCDCBD5A7rXwaTeWWKRS/HVy69YOumbmOi+UUGeCbYpmGAalbTAIc7c/3y4+ePDgtkXx5ORkpVjP41pt2Dt+cgxNF//MQLsGutTGGc0SMlsONusC6DDWjnb7/YPmrZ2KepguK5Jas/I80trWgq2/hJXWz6/dxx6usbLhunSxVZViaJv16G6XQpEoqr95Q/9pRKNp3zde3XUdJHL1nIPS5ewmCUfAHyYJQKNJH6K30Ce+h7yU/4Ef3PofLtcN8WN6zHq1VJJ3htXwen0JTyTKaalTCE32ICjlRPEzQItFRhaMdCG06/W4E0uyX14any3Gf8ZwRsAG0pE+nZNNKMwOeuyzKPIX3Pgy8TxlvweDPBq0bLdGHFEC8z5qhWNBu/MATAKUjiJvUfBitwtFTFTfTF69AmoB73qqQ5In3WPl+lX1l6A87B4edruHXe8wWYSIqn9wUAoOoujAn0yanCLujQgwVkKl3bSJItlNlwDkk6guwReGS7BKnTA2ZWrUxEJzt085mk5t/8/WsEqv7biWZWkcx2ktq9V6vWPD5uL3k9+/uTgTW9ZG3dQ7lUzlsIP5gFCVCpNKNfESD+eDQt6MUZAI8pfetZJ418AmrPrfj3O8S2PhlTAL9wssr+sjuOYdXvWAVdgGOe++cqRffF3HRW3Is449LiYB2EroMwqimQ/Sfr8/m0VUTva+7sy3jxxvRI20QISoAo8AHNFsUsoZAjOAYiCFo05KfO6zxe0zDInPDiuIz1Adjz+ORpsVnmBXnVG7kaafekjX0Org8Gp3I8d5q9Nj+tpeY72GKL8YDyICQQS0yPgaPqCKfh5DzFeWy8ViwU77WkB4yJPnhwsXtLQh3EQe03BfddAI7GKmomkv4aQe/REESXhb2IAmAVemvOuhL1EOZrOJPyv5g2wW9Wd80NwPz9ycBhFIF+uF8hftWSfEgd1KuM1hvbhE06dN8PEKOE640GyuXD6C3dOnD4riWiTOZD6SLR4dCa1dHq02G1WvVbF4YpDZzBVi+vchFR7hDqlnT84Kl+Op/5L6NSIbtNvrThq7GAygWvLDEH+jOhd2eeTl70FDi7CSv+Ix25Ur6Bt0EKJefY0GeaxWvTtVRtVd+Yywu1r/IeX1z8LUpp0PLMJdIcS0DtlSk1o0i/HzbJDGWcw24ABQBUc+7cgff+zMA78ZsKbhAwpUOlgJe2z4JowMoCLQBBuTOeEsl8uPyuVWC6qW8oBV0kunjquhlR/sPX++GY9+vf24OfoHXqfi6myMG9MentlTm+DCFSnpk204nKbpGgtpS6dpFgN2IK4Ig7SpL6MBIuZCblvnaRFm11mp1KOzabBLQjPtEXrs1nazwubBvVDwMOfuFY91oioMlep0QYzUXnIk08nwaJb9zNLhcB8bs4fAms6xbqWRvskGoz9fR20+58W6PeoMIvkrAM9wnBhbd4WQNWW4c6B81Go9f1huEVA9IsAKZajC1lie2oFBXB2h0r29lpTLJvSYgF50Ad3NirdsGj/ANQSsFgaGw9r+j4/tduNHp91odBrz8Wa9BmlsPAcQbXScXNkInefzRXIVF6zjTmFYp2eHF2tB9EPJMtwBIizfsWhxs1JRt4SYVXwoGfSGPFIjSD2L0z6GXCxDuIa+0nqazbK410v3U0AOucUhCk07tf007ey/eDP68+f9+/dfG+32x1+/vjYajfbcfAAJiiHmqWDgiycSBikinb16lWu17t5t7bXK5b3nDPf2YMv2fLWnOYMlXtDVLrr/uIKSI7PMMzawzPAeB3+LVvA7+7Xa/gjAm80D9Avq0Q/udTwft9tzgpsFXXvebkgindQRZtxAtL6F7jqoV4RL+VzBXEvhDrkucUKSzoilNJwT75X5/mXL/F6XBqMw3h/WRdBVN94aFAb90DKozGwtkDQmRjYjEoIwk4wcipAIkZQjCUPBZjkksqKVTKWyC7swuupzXiuCOu3d3s253MfnPOfM72UG/svDNa261BEG/vH588V69vHFezzntY1p24OnkcCi2p3Ea1hapYTRtUrOZtldbRyzO/F9ZzLp+n5/pqooIeYrlUdsA/0CGcE/u8pHvPro492PO8BQDLMIxWSBqLJmqSWFLSvk+nxrAjisADsg81828AedcgROk6vFCskiofAS6rTiQbz3y3aP40K132h40o29FqbrWZZ1HfPK1kGdBedgAFYGIfci+jh8CtWJ/A5/WuO56O0YpYenIPVL7y66oZ2QFvud1Smfa7iySGWR2WxNSGo8fvAahAYUO3TZY4xpzLw0mSDNltNyNqvVaumYKcvarByny9IFNkbLx6vf4/IXrwhF+VBwpjt49AjK4gLPdsRQIZxAFhaimWSy9u1boQDIIINtrVoULyjGwrFYDEicLGJU+AioCaR/44DAhWoszIpQaEXukFevC1R8P5GYZQfAVFQhCU3ozpg1GoNGQxSrBoQV2Iu8yM3MVG9wWYoMz0PHCDospVu4of1+v2/34feltz58CR9cf8JgZuu+16AgCsd2ojNFlsPxeDwdt9tMu9Bsda2WqXiardRQS5mm6UhYBm978Ni2fbu//XASg2y9kaWIzciWT4evXHn0bEcU+wRksHqkyp1LtUdaNXRaRJzQLFSL2CqngDPMKhwNVZO1WpGALRELC1vmihH6jW3Z/Y2WF9S3wOWrot0DPJVxjKFk26P+sACSYNOAKTETmWJeDxiE7BDciczFClDsZWL3E/mxHBV+PvZ5Tf/BBeoNe+JNbFu4oqjHXO0b01l90iWnE8PhsIRC3VcdXXUkCunQaBIpM7XcbOAZ0HKaFtB03UShJcNKlBIDfJWY4K1odjKwurY9sfnvJlmlfW5hR/FIFExHjoggKUhHotUwBFW2iry+FcK8zL4SaCgZBH0UxW19tir8UbZo9iCYRLiKlKpoROy5Asvz7P9j67HfDMvufVP1SXjaTa3R7wnYLUbGrPGbauPPRqxAQUUclz4fvnfvAbNZ3zO8htFpt4djCqDRMCbGAAexbfKfBtr3qJGGZSFOl44YYcJ1OmXdKZnmQ00/pJub2/DM3Yho2g1C002zqVmWfAddEe5y6TiTrtO1jMnE9rqTfl9427Y969nOjlowVKsFwVhEnDBDhAVIiXmyqBrOgAxL7HkxHA1GCUSGOdSoWTKDOKFOQ3/yHjAyZ0hny1Xl2G/bRcDIlTmpv/VgOtg1LcGXH+usUPSarWarAc0eJoBISfvsoPGAI3WFkuVXDAghLS3sY2keDYMK05EeeOq6aE4ssmNxkmeT/72+b4OXk0qJ9mg0vcMpKHOal4TXrZSpa5F3geV3kWdubz4SiZTz+cgNHchWU9OR6mr15vq++4GA6awcs2VZmINhoNZm02oZnj1Z7kj6mWTt/bVrmWuqCKFRhSdWhCd5DbMkYiSwUYCiRJizHHweTgKe1/HcKojFXqu1YkwVONmhyhV4D5Q5IgJPyuFqkaMFiSL+ydOW2Kg9NBqJdZ3+yngw8FodD7dar8ncHknP8gci7kpsFWrI7uCBYIUoB8DbGQJzOp2WT5505ydd92SZSoNZDibCk+ana1HQhbh7buS6o/kUovmpkD/Udcz7esDMbb6vlm9MLZ7Px8t51vlDh8o39EOHtKH+MJXadyiy9+zeQC5lak1dLzWbQ3St68wt+oDVDkpQTSJDgE82UIUlrLYWGsI/8Vji+C22omNwClPp/yXhY4T0sexjnzQC+CPBlKyHrpDn2t9UA9Hz/Yy/yBRoUeVpS/VPdoKfs0RY4zGdtWdYnufZPcipHmvWU8mPUIFoIFPlo4qrwGQgwK2zIr5EB0ucij7nrvvVZSZADd/2aSWtkt4plQA6HZ07d6L8dMQ55XH5BjLVF2bgofnGWaJO08ztORtP54lyeh6Px8vlSITUvxF4tzd+Pp4+fzay/xCh6RqyTd0P6Lqm5zb+arUDfu9vh+CJGP1a7WaQG2fP96/xymJxDSuIFsUqGfIQpaQH1thztjAlkZnBTjhzkIIFb+ZKxYykWAjNQrVg+32v1utlyb2eNPxZAzqNuu3ZnmHwl8CXJ0VR03Ei0bI4xwPoFyTah2pP0Nt1O2tDz4YrBoA8lR2IH0ieQ5X1gAWo0lQiPS7HBZGoQXGikyxpNzRL9Dl2RyN39PTcSATKqeWxvmmlHHOzXJLTgVzu0N742Xz+bCQfn8/L8co8DVqonk3PK/Mz59NxN793fz6iDbU2OtWa93XdwSWEJxwXi/eLTC1DuTly88LpUK2KYENBAauGr/K+B5IC1FgkuX8Ri8JO7QMWroRA5DykeKRQjEUxhfBzAS6Gu1jYJcPwKLpeiTsuk2jEcPj4AY+m6vcTVq47HhootGUZkCbn+zalhMXD9L1BVjkACPkeeNjysqJdJg3PMyQUVWlv0CFSxUrhKRI1WiVNK7dLpU6iU46T7KPRU/TJx0DEmjkh4R1/udosjwZyR/ftRYln4QnAePzMqJKez+duPl45U6mciafTlfjZven8Ie3GoRu6FnhIJTMdZL0jdC3DvwUOumBChEIgXtQywduQloZp2zax6UGVOVNhhL+GiziBssPoQdKdADJ0IYrLPg+DlG6gEK5Gb8H4LYelFxN/8aA4db8S7smvbE9M2f6K0devc5KwnfAIG9Pz4NlX41vfbzW9HoS9uu23Ws0b8fy43Gl5JHu/RkK3Ep1EaTgQpjKDqEEpl5AOczjsDCk8ot25C81zT58+PVeR4lUuj0tdx6KT/7BZpe4fzeGT+9Px9N48Fnq+cubcuVGlMsIdUOvozJn0mTOVdHxv5Xwksj+f17T72v37gZzWDNzYcfvai2s3Qzev3Xz/XqC+z2RuB0OhmjDc+ukvfarCwx4BUoloUB0ENKutIEPJQpQGgBEEKu4JQmkYFOifRJrrbyJ1FIb7782XgU8wEyYYnOA0JcTIsDOdSRaYpNSQhkmoTadFuckHsSoLmSaOJltTjVkvWElrE11LKAktmniJtxj95PPD24EO993tM+855z2HbaFmhLp+phpeXrLM/+G/INEFSsi++hSedBPy9PFl9/LBix99+CEIPxI4P7yrjsfzu7OzhycffXQdzd7xofJ53+wGO9fVRmUs6zYAESByRJ6Tdrt9MYCpoDu5fMemW4n29I5/hdjgKYB+3n3qXz296rbHdfzQfJSSHSVRSaU0xbdMoPmed79NgJTo3/fh2R8WPdfv9u99SoIfyLI9kuM2ZivYmNdXSPOu0ZlW76rwnHY6Te5X0W0Hzmu0gmIaB0VroqFTVqtzfiOq7kcnByfYOpqNqA//gH4o8HEFH7fcCL5sXNYPuFAbwPqwunPZvaJjUDTXbePyh3uAcu/V53ksGKMG3gCAAXV2Ldbb67twUVnM5xTzk7v5rBt0+R2RTTBerRZLfbb0H5P7okPtIM7BoH4etun5wAUwlCeMQyj16mr7g+0PPv/g+AOuV6iTEH36YvHNXkL3PUlO6Z7bd/t4JpME7/Hu7eLnIOXSLwq5Fl3f7xOe5xvxhG4EiZScCIyNuDIa6WFpNA/HY3s0X3UanQ5MgQpLmlE2jVwbjU6zKapBsvl9EkXSpQ+Alzy5E0L5ULiD5Fa2enKwxbNrqIKhUOXBmiHCxF9t0dqyWVFtP7u5Qaev/HIWBjibtU8k/+EqYo2YZ+EJWIrWJbJitSZK6vldeL5anYtqf36xqo/0se/1acDvLKNROBqNZ7q9I9YCxNnOzvXd/K7i0DL4A9oghSs6FXXb3P7g+FjgFAL1/St4ti8qlfo349TFpqJImut699CyDN2FHjQJ1MwBlIJrkbZUpJi6ngJBzfIsNZFQ/A2ja3meNXSNcWAYY/rSqtppgG4+bawa0+m003yz0yhsTTvJwpoymUy3alQRI6iTr52Ab10c0lV4JpM0oQOBbesgC9K0KAKMWJDlyNOolOcJIVfh9U92HC14vPOALs2+/3EXpV6hWBgLp9192vWfvkPmCpG1sc93zHq319er8+tr3N6iMtMDvzvzzaJndqPxaByGbVwTA9D1+TVd9uu9aDwJbNueTMj6x20GI4Q7cSig28fb2wLp9j15QiZ09dXFxTyVqlcSumata6Sp6Lph9mEIdqFRQjAt9nhAXhQhin1iBtDpU65kecUNr+T2+yXPzVuWq9g7pDL/KLQaRVEFuPPKfLFqrAVaKyez9P9GWsRJOot8y+VCoZluNKsUYKFjFMyzvFRN04hgilifo7Am08mHfDiZ5UMghagIsbd64ZUXnnn2l7cml3738quP6eXsmEV//sders33Wzu3EALqxAnbYwLliQF6Pp+PHQOcRtc0+33fDmejaOZ8jJc6X83nUThfbMqaESiaM3EcIU4GmjZH6ivf0/gmPQk+V5y0K8KV+SP35EoqHjO9IdrztUjW0D84CQ7/8CR6Iv35KZIbvjFbKl5x6HIobigPdNu0Ak6JL+wWhlV94JydrG6RG1dESvNfINhkp/E9+uwU0p1mg5EoC6AmQIGUBSGMAAXTJkyTlOPGNJ1EuVnenFwn+r8O4D95wnddDdYm6+Ak1HF4Tx8wG2I38ZsEtfkM+ym8EvEiRj/cedB2BhPmv8FgXK9TowJntLSjiOEwGkczzZyFZ7f8i+czbZRaLOQfLc8f7PApxm/600Coc0KQ+BOmdvP+SiQBjsk89i6+X9RlObGpWEVQFa24PFKEPfqH5/E6uMMDWIKVt3meaTAxeVQA14en6au6xAxlPPBNRGqqXdMyEnrJDKtVqCymDbRZpWARnU4afSUBJAQqUj/ZRJ5TyAmLIGhzaKyoEuVCJ5ssJzkNST5TzqabhVoLfGuWz6VBD2MO5a3sCwRMWZ38chfhtydBG5N5hlV6kVkIlmAlBN6TjzDlO2+9OGiTte3wgk1POB7UnXCxqITj2agSzhSDLcY4+rqy9D1jNDMU0zM+Rt+onk6PxB1bpwCgWC4PsKJYKbGz63rHPediL6alKhcZaiasTHD63PsXJ0d+yHyg9ob9Ya8nkHoQdd3+dq9IbG8MXSvI0/z1jFX0qBbkv9d1S5wdOVxVourDamP6zeZqikfFWUF30QBpOgnJbPqkyf2HpPUBC6q0sK5NsrrDjUBfABYPESxNKPtC64aHzTKRXEc2fZCeJrEQnWqznNx65YWbz1jjPfvwbhzZ/lO8IuFM2oNbZm9s6MmaKXc43n30FojOKaJsLAdh4MwvFnSnKApny2gnDOxZNFL6uBpmQ8u45W1IVEAFqWNjnJwHE7HfmDBKYLA+Fs/i8N+ZYAYcOaZYLmiKhjxS7+GJTyWAuO5h5DtAe8PTYe+4d0xfEn9PsQfg3nFxuOGq8aMSj4YUVMUzrbyv5H23aJmW6vmWKxnjxcni7GHy4O7DanrV6DQoqus6Kdx/A87pRUM41nV+C80BTdwpN9NoEpzCc4Ea19BsLHgl26qJvlRLQryZywnivEj95TXhTVssnl75MJSjsRMErDNoJahJDKCCJSsNDgQ3ZwL03fn14B3TcOrzemI0i6JRFNajaMycbXrb2118txVGs8kAST8gANqecMHrE9RkB6JidqVAc1fsRnnO1m1OhCbvYT0BC8RjYpdbSIJ1DZTD9m6vj31ai5bY3h5uSFpCz9PNXNe8NJiwFG3HkXxTVXXFy1td13V1xQi0WSBp0QEWKI2fqlICgJnmR8Aqi0aDlUeyTQTcbPJTKFAOBFOqLL1JeFawo0lBdCudzOXoR1zK9DSAEk0+Nxe1lssWi5UXXquuxgrbiC5TKX4msMfh7fXqjgU58wWentXwLRKlNU0uDd2eKb6vBY5sswTiZNhK3+sPvT5+MaOVgvDs7K2BaEUfixp6fo5V4CEeynbabTam7PEoCMQE1dq6057orOf+NMjhIQTXdVMw/ZcsQHvHu71jmPZ5QTweFnvF3sZmKq64xaFJGVA137I8UPqur6iaqiosWYKYoVIDhpwIF0MSrRqLSrqD3hooqkO6Z+nxyTIPp+gRdASSK5TTAg2sqa2UCaIqTkO6M51SZZMUhCSf49U0V4YHPktZXs2n0waea92pqKoMUrrfx4jiQ02WkcEkJOq09vPrOVTIdpBetNu4IjYXXjcw9IDlmTwKNGU2m0UuUvlR8fqKM7i+FiV3IOJi8XU9bENOSHSAXQDoGRq9RaV8uNLeSwQ23318x8TzWAyXCPB+e/d4d3fNlOPfPP++D1WCJ9DsxuuxuGz7k8DkRHgWh2ER52T5RSAqekyRZprVtUQdcocly1J+1IJYna7dhCXSLGQLh+WyYEodLNdq2WyOREaDZWwAahW4cKCgpD5MC50GpksokutWFqCUBHFOEC6VedGgMtDSmHRpZFiBm5sbhta7ue2LtY6IPs2YwFGOBwh0gD5vz+uOIOu889Tvi3nbsNvLmW50Z1F9vvSs5WzpmkM6ByKmwlZYA1e+2ayMdNBfBpQTFArpAU2KVubgGu6u736vb14I20iXM2hf/v1V/3Nw7r7Mzz9UeUj895iGNISnJsuOnskYZqlU8ngm0DKQG7ouxbykqqWu63n5wNZnEmtUS5pIRU+VEherO/J9Ok9v1Qq5XK5WK9cKXzRrh4e1XC3XuhGHVuvmprWVK2eTtUJzCr0muNleNTuNJs/htGDK2cBB4F0LomYwky2oxdiCfyJZ3mrdtMR4+uwqMGEqhks2EfSAe6COQzD8bZZZTlITLy2QQEhnO2l2jaCyGElBNBjPZgousl/sasZoNLNtvVIfjwx8jqdoeioFWU6QoRmsj7t8Vt67+/3kl49Yu6P/x/wdHz7u9n1ylA4EPKAeP+n1gMv1f6oIdFjcqNRjypF9q7mKoeffU6z31JIW2Bk9X/IzipYXXIuulWEGy0uXbFXzSqDrGTUjBXt7cT36ulE9eI5CWe50arVCQUCq1T75olCoFWpgpkqWs61y6zOU9sJNrrBfKxx+Mp023zwsIFQCsQr3Ct1OE5kWeGLtHpD2N/PGtAlmMVG1ttbb/xX524fnPVyJfv/qyteDWdfWhUsyqHyDHZzpA0YipMdWPQpMQ45GgPdpHCzeKAKBYVpQpVmUPKtUVC2tZGp2LKbppCf2Z8imA9e0/pa4zQRBMQlt041uB5GuSiXSXIBEnj+9TAiSHESg0Q82YpaWiEt78W8zr6cS2gNEyF+Qt20cKV7OLLoIFJ6OaryhlRQFc5pxEoalWUpCtlRLycTejddXnU3G1GahSRlYQ6VvI9HcfgG1lffhShCIjQN33n77/VxW6LqVu0HJuRreCpp8NsuJ2MpBO/2wKkovTDvpMnJfzwKfPfvL9Uz1vb/XPeuj133H9IPxOLjs4u7leXgxF1MUGewEXUpuN9AN4UzHjgYud2ZIw6EifjUSsOgOt9HcNqZR0gyXfH3y5Am5+9R1DfpRRVcsmzEXpMJHjaIxKTHSJXd4+kRg/IenOHIRyu1t7KmnrpvPBOP4Uk9kPE+K6aoh5Ifpt6z3Tj3yHdeEJjOqalh+V7G6upTRMkG4ZygqjgDy+iie2tucb548TFMo05ROHCUeqIk/zaFemg1CQ3sQg6KoD+XDQrac41LItnIFYfZbLVC/ndtqfbpfTlN+ESxnp1lgPTPFG+QIPlYT30GtxoE3BCdMr/yrvtcV4fUNmsh8xYUaGPLF8OSpaa57mccg7kShY2SUeELsOgzPNIVnNF3AwIH0Lfa2ezRscpnEBbBl+brloyV58BZuF6K0fn+o7VXGYRTNJDVfgurLiJQrAU9iQ/IV6T01n1ePMu9xjpSRHEvo0UWkKZaqxGTd8kp5/G2wo6mKwz/FErZEykiS4oSBgK7pHk/GU/EUAt+rhIlRpTLHVC2m88U0jf1MQje7TmIc1bqfU0fTVNHOYjH9osDERU3FX7Vatf0398u0qDKRqwEV/oeHb4q1zAKk63OCYUXJfMO6Gnf7EF1vJDn2zcAXqxGm9nkY2VgR2+je33uf9z+/FyWXsXAZOe1K5DhKXkyJRZT5RFn+qPagCQwwrhsOV2F+SqLQlVT31JVkLNY13R+VvmOamppXlqlNtob10VKySk96ECVQKEw37JL76FQN4omEk5AyR6+fXe/t8e2dlonLcnxUr3zrljK67iulx65nZdRS31VLUmCZpqHH2RY81gwNH6Dt2XE5oym6k6IVyKnR+bx6pkUPDyrXr5ysd35lklc0fsETz7mFIMnvNDVCTFK4JDERsB3Yp1ocHh4WKA0UBlYETLG0foLgbahWuAeKdXbr2dtwmR/2GF8AeoXlYwGg2+MK3Wmm6N2uSbsXpXYb2iaJ5uvjcQXQvkkNK6LFl70SUsqP8qK5IFOYEASSg/KQMQelapomt8Pz69sX3xLjvx0YvpWRjl4fLSOSIbQtVQiVEDzf1aWdn987GiR2UolxKI/ePYqlnHjiV08LGWaNRBwxZvJDl1ZvnVpSqehJlm5J/unQUt4r5VXVsqgGmhxo7HHeY6ESOgk55ci6HPetuK2qzuqhKKU12KwrJc1lXU9bNR6ytb9Bby0iB156mAholQtfHJaFayigVhA3C+tZ9+upiDRVllpN8uf2swe3Uaa0zXj9uckqHSWaZmA7AaWf++yDPawOGu5D3O8642hkiAHFd1mB7g7zR7+dLpd/fOmSuPAUSFEpPLkgVtHIi6S+q8YwvecX/N8FIVPHpgGKnH70aLncrLPnipZSfvjb6ZMnG7KuHimie2ckJX8k70lSDFVW4o+kL/XTU9dov54pvffesGfYsXBnUK8ESiKQJLZ7Q/JhqMXiiXellKTrJL6k6Gom9ljPADgV02f5vKIpiqYtK18/zL6wbkWoDhcl+OJZW2/nAEK3ylI5ud6ssQOPTiX6vkC2lmJLEOZMdD4pUDuYCL7mBq+wj5j3+Sir7Ehh4sBOMR4S6HJidxVUyHrUZy3HK4TZNXGuaFdR0LLZP80/yR+ffvnllz+Wdrd3hSSFLefOvwlMwFNVXVfV5FSFOYL/YsOcOxBMg0mgKFDNHB0dLWejzVG0WP64UXlkaDar+nhK0jKZROpIe/Q6yY97CB9JKimcL6nyu3oghbr8Uj1Vv9ipxBN7qYqc0CQMHHqUY5ImSYxUuqUaugFbWY7JmRiNkL6VSFgZQ1lWLs7vNqedebpFT3nzk8PDTzpQ+zQnGMP37cN9Ml0IsvY+0isU4Iky9w8L3FJuO28WkC3diz7Gfnv6xx+L6SfTN/d5GS2vDUWyUUmpQ0ETsfa2hTphZuJavT6kt9cDQVFA9T0THfv81lq+eNyTXn+UOc33d396svwy8+QUVaJOkK5jd7dXOhV+PK8ZUiqq1Flf3IF0p10fc2pYzIm+dJrP/Phj5sflcrkRU/jNU85OncaHUXdUiXJiHcHxUclSJF3KP8qn6hhOtnryIw13B869Dz+q7xkZbCmOSZNpXAklE4+hcEPT8sZMl4y9WEqOUimpHtpaRlX0eIKPpaTUnPmnjGFCjyQ3shNlEo3WPq21Pv201Xr7U8oBr2Cb9t8XCgQZI9hheX//kHtvftJZ3zTwsI1OoXZY4C28YX9fuLGt6mKZOT0FqAjhr/+q4XpfW6fC8P698yXngHBMOEGJRRsc+eCcrS0YI/gDCbngIuvx53X4wUVn02FgNR8Mm0hl0zpkg0Ass9jrbcDRiW3XuV8+p+pJl7br7b3rk+d93ud937P7IqBDKY00j5npg09f/ulbiGmrgxlR7UHd7wq92TZdkiXG+gcfJHdXo5CBk//j+QFuvQ96vXXk+8yquY4piouh5z3GxiXMA/sKUq1VNaUDmjZd17L0FX9eodJJmg+aJK749sfJlssdRCpjjg5/TyxELMX10zUXxvSl2DHrpknjjDo994EB2HVC4rjDiFlDkqpUGdVhuIhj1ky8kWuMDSgXyISWJ7lVpVrIRDF8+FRhv7AKgu4hjlWVCn4BRXUA0tWlKf1lD9gqsFcBMiBXbmlDAYyqVUntJhDGDEYtIKqy2AYKWCS/s061tiSq6viApp8omj6Ak8aUA1YAYwrkLR3iqtMBOicesuz6bOvPD7bSuzB03vtzmaiXoCp+Alqk+3YPa8sdFFf2YDQcqHg7VR2ZwMcWPDQKxWg0id1ssvKoP9rWKj0IxQNKuE2aryUzx2m36tSkxKAkc10dVad0iNn6hETbAlqgtNipGYZhmsS1KAdtMeD7wZKa5/39ulZzsTnA/MR4YBhQYx3e1eS7VS5aUFhICjW1quY1WDE8Rov0/AXknPcPdzaRyJU93dlZO1CW/wg6u7e38fbR0dHexoFC9u0DaO2Sudhysay0Nk+UkSq8YzjUQyCMcmJzYxOQr649e+bp6ygQIajt5mvrn0ItEf6fKGullhrAPWgD5ZHwPa1TaWUuiDi3bQn39CbQ+OnPN1W4A9YlnutwVFt4yZnYdsfrTOREAM3+6XcoSuGHAKgqezsTttKzSu8PX6UmS8ayIS1BTbPW6zWTjDdMxzQhxSRrV0kKbAh0lhoV0/21/OJXX2+69eaDlg6xFMQoNfHVrucLnxGUXH495lrr64oGchvwE4bDc1YhECETdzW9KykEwQts4QHXIdr4a3CqgGhZpgKb/5QVbEUiA2vVsVSCg4M9PEKFBTYenmBeiLYL1g4uCcRgbQ2kVfq8toqGnydcZE0giZpEWabXoKDor32ryqrWzyDpi6oM6mhn+0HgC8bsEs7Hcl7rbf2ORL+uMlSvrRiK3lGvN5vNIJSDTjAYdUZOBVtrMR/AlpX9QMNaDty6K7OEOA0G1Uyi46vjNPo4tH+NsnYCigMHJwU/3SzW3V3IKzcdtJl7Sc2NeYWZU3jeXts1GHFaGvuKVighjehjxjSiaUI7jco4/krXWhoISoAqsLTcZotrdcvgAlmM+hqlWmT7gWBnZ++8+yQwfP5tRUZoIRh5sAcoYQhAVVUcLaVhY29vdfPwYA+wHm3sbCqWvvpwZxPGH4juPHx4crj29qubiu+QVijwwzN/MskMyCh0VMX9Ax0DSFX7Q1BbVWyXaWmNXe+s72m+b1tuzx057rrCc30dtdNoMOlBSwFm7EwdZ9ZrW4MuOlZOb1YT/dNvHgHSU5w8fGDsERErOoxlYrk66YwP7q+nrBo8+l40SFVP2pnxmtmvlIbrcOZ8zKtVGiPG9U/WDccSInWcupW5NYsyh3oaAw/hRrMgqLe6A0m1fcQBFxriu291NKKTKtF5BwaAiS6vaYJXdWGnGuNh0BdVwjQmafB4OB6vgYrPvvPRESBV+okHAPRIoQqzdQty4v4WT9SrR1DgA4B8sIE6CutCsRTqgeP9hxBUfBv2C4MBWzDyWk35JURmvQVJfU3h2/oBfeVGAzWkh9pf41xHoo5lbetlJZpQUMdC+YPU795d5fMJ2LuF4n/iOpbTa+um8ACpQhRU7XsdxP0K0+bTmfOgF1wurq/v87zsChqlDAnGNVpGzATXCCmpoVezzNJapOUkCUy8YYKVwEpCBbpVmordbQHrX6FG1kQe/NgkQaBJcRpJlFmERC2L1OBKmWYLaljE4rieEaVpx4v8b/ztkggNvpZLRZLCfjzOh2tvv/DL3iqAPNr7CGje/ofn7RJdxd7lc3UC4nuQWfRYNvbeBk03QdSTk5PDHeC5s3OIR5vwXE++Eewy2umOsOsBsVnRUT4hWdXRkf4KGzi1RlerawGzJmwyQW3fQ2mk4h0nwNoeAU6Jj9774M+t6oufAAKj9x52LXSFp3qwfdjBb/Y9X1vZ7gevN5ykHRQ7d4c3i9s7kyInx2UUtyplzOZlRqRpIcH7u/j4lJKaAZvvJq7Om5W6AonxKnmrTre/DwVJ2moZteRF6vqcRaQ2sGTFdb6SYuCYDUZkcFpBMcA7ouPtM9MEKXkjshxCKMtcpkWChWFIBeN2cfYYE5QXAJiiImA9ugWuuCmC4sDD5Uu3t0dq4REMAf4kGHuAbssS0pOdTayNncND3FQZe4yRfGegTyYTvfXJi2rY9qC6e6rmJo8xexfagFJpWcDamLzWTFS3c1knrdfccpIATojp+otoQ7lZU1VUW4lBRWj3sVG3H+w/Ou2v2DyiMplRGl+F99eLow9DwNaojLjwRf/7MPjVDIswjKS0+37gpVXXNeAnyW6WEEIYydDDzxwn2f6GswbN1pOtWr3OLMcgvYzUGdH9RiPdRnXQ8S3Hqu9Wzvq2xxxC60k3YKbDAKSqHzijticd+N16YIPGnHKG2AhzOw/fefIFlPurCqcD+CtEN7DFAn5LGcWzPcC9t6HWgfo6AHSKnTirYweQgrdYOzuvYkxlh8orm3oVyADRT3aDM7XJAJ/P09DZR28aexExDaqarru17D992v4gczDSnP3553ufbsFbAW5Aq0ZIGGiGo44qRwMv+HIljmmpxfgwj2y7uCvCR55DUJ0DjR/k04/GoTZ88hK/1OQXuAC2Lxqz2VcRMSXK/QyVk94y6Xao16SMTYvDDhlNV6dU2SRDR6bqIn4Fo4LTelegS21N4yA4DUzGjCzmFNorJQ81vsv5AOVCrNOYaGgAaBUrrksmQo+HNs0kCyED45P34YmO1w53NvYUmIBxCfDR0cHbB4h1BfcBKi08UJiqG7A8xG0Z+3uAFwugAtrzcRhNsNfg93WUmCrHDLrYUVZ4HYMMBtpXu90RDLoplO4ZDjHQRkrcXrunTP6nn8Kgok3ywQxpv7eexVqY57avaX14p5V5KWO0ndl8fFnYc6ZHsSyppCXjtY/fD9NL+/4G+/Pz8fnc9uziFVt8E6SS0VhaZua2M7SStFCwaq0FjcYU36y5BvdBYAU21yIOF2tZhBqG5NU64ZQFkBNUpDABZswDrdOAvsi6TnB5mE9Ul5WBnFbs+9TnUG9f4JziR/ZscHV8NT/G1BRZHUZTAYQSFABuLKFcxUl5gOWLSzwVXxU11YKM7uAbeIqFdz48tkewiapTp6bnzdpIjZsxdBKmzpaZE5OPjhZ446HfQO2oT2c9ZfHbbXSmUDIBzK2kqXdxqUNRFHYq7OB0ZR5RK0sSOS+886uwLDVpGaYYF39F2axre+f3vyyu376+v7kMvTwN/TDytMCcJhK8Jm6v5wgTcFWQYmJKDVfCERndroH2oSIxB2ZWzUB4Vy0qTI61nXImiY9gpgyaEnYZoRwM3qVfCd/PSMQ1Re+Uoh0YUhn5PhO25kF9KP4FOk/T0B4Pw7vzY+RvtKEBECwUgFStFbATpeer8P0q5oGhgnAJ3hLTkwtoKu7xDDe8/s4bQ2/kZD1o5BYqREDa6XRsdIsZUWMKPhp1va439Lrn47O+ECzuzZaNvJff/LSH5jNKHeaNC7sI86Io0E8shisig6nKMhFOQ2wdH5/uW7Pe9Kuzy3H54ovxVVpc39zePHFzfXtZYDe0Pe97eVRmSWXfK2xOsT3HqfsaelWZLhmjWLpDJIyp26aNhqBwAs7UUH2WmMRW9SXiwvwb0mSi6lpRmHao7fNdWwRneZymmUG536kbFUlT4sa+x2MahT4zSy7ExJJpRAQLbE1EocZy9QmOi5O1V+H+FRXXlGLuwei/vQeBhRgoJgI2xdDD/yQUiCL3Hx9DACAESlCx3hkyI5mh/tladyaWFY06gFVw5KwRsmbQDzwBa8mjCB0JdNVrCHwc6+sJ2niUgZ02UqhdoBQtLlZKWkYTi84/H087xfB8P+w2HszimW/vz9x4nM8Xi+ub6+vb1evnzv+y7fH4L59NKy3pX47DUEQooUyfS8JMDO8aqN1grGLZgi4mmSNDmiSma+ktIil0wJTVDKMV9GsszUApz0ac4Udhacr6AbxqX2v4fZH0po2/toXgbJcY8P1BHjuMSZboXESxJgSgxLv2GfIo4sVLU/xYFycKTwgoYMRa2icceKJ09N8YV6GuEAegV1cnWEttVaeTQ/SyR8jKMO5b60j9I9gqz/c6Q2Soodpm69le0deYPQ8D32YDMdCTZP21tmuZKcLdthG6YRpx+E+/Pzy+ktF0Nh9dXNznUutzyzJ3BUqXH0L78u4G3Ly9vVksPvvs/PPLJz5/5Hnh0AZ//8JlSb2IxZkTW3+UkD80WBlt6ts+kxqrWTXHioglVX3VEB0DvM3W24m2qzcNkk1nkmRGl6tFOde4U3Y06ZgRklT8cYzKKZYz1MJyWwSMeulUt1J4OIfLLIm1Oa4BZWUU5iEvWSdN5+ncRnhhsIJCFcH/f/pf5v8D0BVnlZAODnbAYkVbBP7FMvyXkB4qHRgOOxNgOluH4xt10UdSQw1AaQ+HY4EOCFp1Y7sIoJadAWm6zSa8DpunfhACzDRPI0roymUBEk7jyL+8uz64KmdM01L+A4EwxHH/bnGNeFfr/u3LsT1+8qPnCtsvLsdvPPnZo/HjAr+Wx6lJPJY2ooqRZHGs6SBWWMZZ02o6A9OAUbVIfdvfrzjUam41zbB0sQyzJnlsxaSkLJa8winZ9XnWMzLX+JhmDWJy1fyLuNDKQFLB4MxUr4qyVFJobMQllYLnIk3NMopSO0UVEM3zMoRHOd5Z21zd/I+oOJZ0hcQqUP9N+6DsEtCTqwsF6tKoKmDRD0BP2EXiSazBAC0jENUbDs8ee6PJBFJ5/s47Y/QcPJqp3QpNJNlJ6TA/BEGx5jI2VvK8YPlgCs7e7dznGWp1w/0ty2jHjzLt/vp6geOX1dvFYnF3/tQT75/b+/vnq4vPnri5xJUY9z0/eJ19P0dx+Q01uf/N/oAbyMvI1822zil8a6BlWZ0QaHcTldVEqHh3jcQtvVREFTfR+iLIKOWiS9xPUGgbaRZHtIL8FaEAlKHwIk1yrzgLBGot39biaZbFWKBrhEUjzrQiYmHOGOBNlRjMyyLHLwMdH6+9iv70Hsqn/6MfX7j/15jiBmJiXah1BYbiGc7o/48QBllPTY/btQHs6e7uYDJBIjy7PB8Pz4dFRxC36RAYF+LESkJDBHweSWfloiiteR4FYZpfL6ZW3Osls6Tl8/lYyPHdza1i6PUFcEXEr94sHhbnl5eb1x89t1jcF+O+7Php4/vgK7gKOa3y1A7g1TVmoSGlZUicJc0yWeslWdXA1CnLHJ3PXG5WZMXcNQdVk0gGEeaOMXUboLdjlfBJIqZmSYWGOKJpHxVrFIOX8HgsSlPOicW5yCGb0VTEZhQxH+9Jp5nGuJC0wzVwNYWgFXmUh3Pw9uFTKt1jkAIjADFQXFW9PxX3yFM4AdGTC1AVwALOpQXAN68mzgz+HV4TG+QfB6hR8xzQFY/HIdg3Qc/SpJRRC1feDvM0L3DOV5Jk2gjP5tNpGt2lcjrozdytD9adbHosy/Prm3tQ9P7ubnFxA2gvFovVxe35C7dHUNRfFqufF/72Iz8N8b5IWpJWTUzpTPqNR62KEZ8yNXnaehFyXHUsy0J8mm7JJUl6hEjDQDVsuUTGjYbRtDh1nVbMuW+DdaAjT3pVVEciTqeRHzOI1mX4yLfBQVgALwIRx0gEjBJiUbgTuySdXsLSkshonmpCykj4eSrmAJ7bZR5CXW37yRd2VOhDYdX6r5Za2tOl5wecV0uuLkm7RLbIywSY9l5sTVDueI+RNie5PUbiLpCNYRRhBKMJoRMWRiE8aL7SmyGbT+d3x+d38zztlCRuJGBokvhX95v3iztctLv8/u765mbx5OIXJac3q4sDsHZz4/mnntnXvoviUOx7n9slqzvTrNY2pBbobjztxUJYVoaI0VVxRyqMaloWg2PSyEi1hS6r404t16IklpEpjWRqyJDTrBKDqdJtG4CnTKJwHFmaeF30z3xYz7kdgrl0KoWQaZxyXMhYY2loETmbTWMCtyJkmcKKZXbIWRRJ4SMWc24XOPIIqJwg1tdQB0BNUQKAqygHsJZEvb+4UutCLUC6JCyyFNJnJkfwUaPRwBywfOQNEfe2GFATCX4EB84hobBMdyuz3iyZWuLqKpxH5fyd4p3HHicZAJ1u3izu7+/m8xzHxfn19S+L28Uvi5vFwdEvB9cX94eLFz778fz8sjjVIF5FsM9TXsmSj9XnAaeYIX+lWakZSTMpranMHIosZVAywcffFUz1amacGBgGZJRFKKcIwA7nksapFqNMQoHHGevbPkxsHE1jWDxkVpFGCC87YikLx0XOUzySccbDEtEHkqQ8ZVN3FIVhiauRB5GchzYuA83LHPG4TMS+rf6qu/H9+8fzfG1v9VWowX9LkfV/Pb3CV748XwzxdTeK5oBsNFFL2l7qIerBlDSHTx0xnPC8WHFnahlwx3kQvvPCC5dvPA4jip9/enRzfX9xPy2z6Xx8BTOyOLq5+UjhebO3uMjzq4dPvf/Ec0/lsSExH6SU+sFj2/PA8Nf9R14alVZsIfVajANgQ6salqbF1XLq6kRosEOk6jBvGicYWJecx8pP2QIpPGA5jyhcWGJkPJZwRdANh/rlvEjnUw50POAa2ulcVSZjG6SjLg19I4Lb340om/Z6U5rOPc5D38Mfj0uwtYTgAu08T+15Cc2FxQpzCGx0h/8ioHgIDB+Cpjj+zVTL+nS5lKguY395D5m1bbDUFiM66NqC6m4WOQ4QdnqJBNz/ADbVVGeEtqoPAAAAAElFTkSuQmCC\",\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Temperature\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"advanced\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"°C\",\"decimals\":0,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}" + }, + "externalId": null, + "tags": [ + "temperature", + "environment", + "weather" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/tencent_map.json b/application/src/main/data/json/system/widget_types/tencent_map.json new file mode 100644 index 0000000000..625b598490 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/tencent_map.json @@ -0,0 +1,29 @@ +{ + "fqn": "maps_v2.tencent_maps", + "name": "Tencent Map", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the location of the entities on Tencent maps. Requires the Tencent map key to work properly. Highly customizable via custom markers, marker tooltips, and widget actions.", + "descriptor": { + "type": "latest", + "sizeX": 9, + "sizeY": 6, + "resources": [], + "templateHtml": "", + "templateCss": ".error {\n color: red;\n}\n.tb-labels {\n color: #222;\n font: 12px/1.5 \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n text-align: center;\n width: 200px;\n white-space: nowrap;\n}", + "controllerScript": "self.onInit = function() {\n self.ctx.map = new TbMapWidgetV2('tencent-map', false, self.ctx);\n}\n\nself.onDataUpdated = function() {\n self.ctx.map.update();\n}\n\nself.onResize = function() {\n self.ctx.map.resize();\n}\n\nself.actionSources = function() {\n return TbMapWidgetV2.actionSources();\n}\n\nself.onDestroy = function() {\n self.ctx.map.destroy();\n}\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true\n };\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-map-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"First point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue || 15.833293;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.24727730589425012,\"funcBody\":\"var value = prevValue || -90.454350;\\nif (time % 5000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#f44336\",\"settings\":{},\"_hash\":0.8437014651129422,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#ffc107\",\"settings\":{},\"_hash\":0.7558240907832925,\"funcBody\":\"return \\\"colorpin\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]},{\"type\":\"function\",\"name\":\"Second Point\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#607d8b\",\"settings\":{},\"_hash\":0.19266205227372524,\"funcBody\":\"var value = prevValue || 14.450463;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#9c27b0\",\"settings\":{},\"_hash\":0.7995830793603149,\"funcBody\":\"var value = prevValue || -84.845334;\\nif (time % 4000 < 500) {\\n value += Math.random() * 0.05 - 0.025;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"temperature\",\"color\":\"#8bc34a\",\"settings\":{},\"_hash\":0.04902495467943502,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Type\",\"color\":\"#3f51b5\",\"settings\":{},\"_hash\":0.44120841439482095,\"funcBody\":\"return \\\"thermometer\\\";\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"provider\":\"tencent-map\",\"tmApiKey\":\"84d6d83e0e51e481e50454ccbe8986b\",\"tmDefaultMapType\":\"roadmap\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"xPosKeyName\":\"xPos\",\"yPosKeyName\":\"yPos\",\"defaultCenterPosition\":\"0,0\",\"disableScrollZooming\":false,\"disableDoubleClickZooming\":false,\"disableZoomControl\":false,\"fitMapBounds\":true,\"useDefaultCenterPosition\":false,\"mapPageSize\":16384,\"markerOffsetX\":0.5,\"markerOffsetY\":1,\"posFunction\":\"return {x: origXPos, y: origYPos};\",\"draggableMarker\":false,\"showLabel\":true,\"useLabelFunction\":false,\"label\":\"${entityName}\",\"showTooltip\":true,\"showTooltipAction\":\"click\",\"autocloseTooltip\":true,\"useTooltipFunction\":false,\"tooltipPattern\":\"
${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
Temperature: ${temperature} °C
See advanced settings for details
\",\"tooltipOffsetX\":0,\"tooltipOffsetY\":-1,\"color\":\"#fe7569\",\"useColorFunction\":true,\"colorFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'colorpin') {\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120 * 100;\\n\\t return tinycolor.mix('blue', 'red', percent).toHexString();\\n\\t}\\n\\treturn 'blue';\\n}\\n\",\"useMarkerImageFunction\":true,\"markerImageSize\":34,\"markerImageFunction\":\"var type = dsData[dsIndex]['Type'];\\nif (type == 'thermometer') {\\n\\tvar res = {\\n\\t url: images[0],\\n\\t size: 40\\n\\t}\\n\\tvar temperature = dsData[dsIndex]['temperature'];\\n\\tif (typeof temperature !== undefined) {\\n\\t var percent = (temperature + 60)/120;\\n\\t var index = Math.min(3, Math.floor(4 * percent));\\n\\t res.url = images[index];\\n\\t}\\n\\treturn res;\\n}\",\"markerImages\":[\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\",\"data:image/png;base64,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\"],\"showPolygon\":false,\"polygonKeyName\":\"coordinates\",\"editablePolygon\":false,\"showPolygonLabel\":false,\"usePolygonLabelFunction\":false,\"polygonLabel\":\"${entityName}\",\"showPolygonTooltip\":false,\"showPolygonTooltipAction\":\"click\",\"autoClosePolygonTooltip\":true,\"usePolygonTooltipFunction\":false,\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonColor\":\"#3388ff\",\"polygonOpacity\":0.5,\"usePolygonColorFunction\":false,\"polygonStrokeColor\":\"#3388ff\",\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"usePolygonStrokeColorFunction\":false,\"showCircle\":false,\"circleKeyName\":\"perimeter\",\"editableCircle\":false,\"showCircleLabel\":false,\"useCircleLabelFunction\":false,\"circleLabel\":\"${entityName}\",\"showCircleTooltip\":false,\"showCircleTooltipAction\":\"click\",\"autoCloseCircleTooltip\":true,\"useCircleTooltipFunction\":false,\"circleTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"circleFillColor\":\"#3388ff\",\"circleFillColorOpacity\":0.2,\"useCircleFillColorFunction\":false,\"circleStrokeColor\":\"#3388ff\",\"circleStrokeOpacity\":1,\"circleStrokeWeight\":3,\"useCircleStrokeColorFunction\":false,\"useClusterMarkers\":false,\"zoomOnClick\":true,\"maxClusterRadius\":80,\"animate\":true,\"spiderfyOnMaxZoom\":false,\"showCoverageOnHover\":true,\"chunkedLoading\":false,\"removeOutsideVisibleBounds\":true,\"useIconCreateFunction\":false},\"title\":\"Tencent Map\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null, + "tags": [ + "mapping", + "gps", + "navigation", + "geolocation", + "satellite", + "directions" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/thermometer_scale.json b/application/src/main/data/json/system/widget_types/thermometer_scale.json new file mode 100644 index 0000000000..2a0023eda4 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/thermometer_scale.json @@ -0,0 +1,30 @@ +{ + "fqn": "analogue_gauges.temperature_gauge_canvas_gauges", + "name": "Thermometer scale", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Preconfigured widget to display temperature. Allows to configure temperature range, gradient colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 7, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbAnalogueLinearGauge(self.ctx, 'linearGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries' }];\n }\n };\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-analogue-linear-gauge-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-thermometer-scale-gauge-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 30 - 15;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"startAngle\":45,\"ticksAngle\":270,\"needleCircleSize\":10,\"defaultColor\":\"#e64a19\",\"minValue\":-60,\"maxValue\":100,\"majorTicksCount\":8,\"colorMajorTicks\":\"#444\",\"minorTicks\":8,\"colorMinorTicks\":\"#666\",\"numbersFont\":{\"family\":\"Arial\",\"size\":18,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#263238\"},\"numbersColor\":\"#263238\",\"showUnitTitle\":true,\"unitTitle\":\"Temperature\",\"titleFont\":{\"family\":\"Roboto\",\"size\":24,\"style\":\"normal\",\"weight\":\"normal\",\"color\":\"#78909c\"},\"titleColor\":\"#78909c\",\"unitsFont\":{\"family\":\"Roboto\",\"size\":26,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#37474f\"},\"unitsColor\":\"#37474f\",\"valueBox\":true,\"valueInt\":3,\"valueFont\":{\"family\":\"Roboto\",\"size\":40,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#444\",\"shadowColor\":\"rgba(0,0,0,0.3)\"},\"valueColor\":\"#444\",\"valueColorShadow\":\"rgba(0,0,0,0.3)\",\"colorValueBoxRect\":\"#888\",\"colorValueBoxRectEnd\":\"#666\",\"colorValueBoxBackground\":\"#babab2\",\"colorValueBoxShadow\":\"rgba(0,0,0,1)\",\"showBorder\":false,\"colorPlate\":\"#fff\",\"colorNeedle\":null,\"colorNeedleEnd\":null,\"colorNeedleShadowUp\":\"rgba(2,255,255,0.2)\",\"colorNeedleShadowDown\":\"rgba(188,143,143,0.45)\",\"highlightsWidth\":10,\"highlights\":[{\"from\":-60,\"to\":-40,\"color\":\"#90caf9\"},{\"from\":-40,\"to\":-20,\"color\":\"rgba(144, 202, 249, 0.66)\"},{\"from\":-20,\"to\":0,\"color\":\"rgba(144, 202, 249, 0.33)\"},{\"from\":0,\"to\":20,\"color\":\"rgba(244, 67, 54, 0.2)\"},{\"from\":20,\"to\":40,\"color\":\"rgba(244, 67, 54, 0.4)\"},{\"from\":40,\"to\":60,\"color\":\"rgba(244, 67, 54, 0.6)\"},{\"from\":60,\"to\":80,\"color\":\"rgba(244, 67, 54, 0.8)\"},{\"from\":80,\"to\":100,\"color\":\"#f44336\"}],\"animation\":true,\"animationDuration\":1500,\"animationRule\":\"linear\",\"barStrokeWidth\":2.5,\"colorBarStroke\":\"#b0bec5\",\"colorBar\":\"rgba(255, 255, 255, 0.4)\",\"colorBarEnd\":\"rgba(221, 221, 221, 0.38)\",\"colorBarProgress\":\"#90caf9\",\"colorBarProgressEnd\":\"#f44336\"},\"title\":\"Thermometer scale\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"decimals\":0,\"noDataDisplayMessage\":\"\",\"configMode\":\"basic\",\"units\":\"°C\"}" + }, + "externalId": null, + "tags": [ + "pyrometer", + "temp probe", + "heat indicator", + "mercury column", + "clinical indicator" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/timeseries_bar_chart.json b/application/src/main/data/json/system/widget_types/timeseries_bar_chart.json new file mode 100644 index 0000000000..ff9c7971f0 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/timeseries_bar_chart.json @@ -0,0 +1,26 @@ +{ + "fqn": "charts.timeseries_bars_flot", + "name": "Timeseries Bar Chart", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAABOFBMVEUAAAA3oPR3d3d6enp8fHyBgYGDg4OGhoaNjY2RkZGSkpKTk5OUlJSVlZWWlpaXl5eYmJiZmZmampqcnJydnZ2enp6goKChoaGioqKjo6OkpKSlpaWmpqanp6eoqKipqamqqqqrq6usrKytra2urq6wsLCxsbGysrK0tLS1tbW2tra3t7e4uLi5ubm6urq8vLy9vb2+vr6/v7/AwMDBwcHDw8PExMTHx8fIyMjJycnLy8vNzc3Ozs7Pz8/S0tLT09PU1NTV1dXW1tbX19fZ2dna2trb29vc3Nzd3d3e3t7f39/h4eHi4uLj4+Pk5OTl5eXm5ubn5+fo6Ojq6urr6+vs7Ozt7e3u7u7v7+/w8PDx8fH09PT19fX29vb39/f4+Pj5+fn6+vr7+/v8/Pz9/f3+/v7/xx////8KXFhiAAAAAWJLR0RnW9PpswAAAvtJREFUeNrt3GtXElEUBuDpZlAqylVHCyMwyi4SpkaWAl4qk8wwUhJlmOn9//+gLzbKbc6AAm5991p82WvPOedZZw6zYM3aGupCQ4tYkZDSsKDrocNVvz8jHQKYY1ZiT/6OAJllRPSpsnzIqIG9SjYmHpKfAzI4CIuHjJeBtyHfZwCatiI1/m+BYV2Dw35NniOEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQUp/6Wx+DgbRbxOAhEb/fXJ98akiH1AIAHtSWlqRDSvdDGcOHnYR0SPV7zVsaQyHeO0jTJapEV5C9bUz9fIjsvHRIefhRxHqpjxzA3ftajXOudHGJOtHV+1rV04/wHenDc2QQkFbLJISQwUDaP90IIYQQQgghpAvIxRO9gFxG4lZ9XBFIx6sihJD+QVRjEEIIIYQQcoMgahkhhBDSe0iLX3ydQdSLIKR9ghCJkIYEIYQQIhPS8d+AhPQboppFw9HMxBrs/lqCIbObpgezP67YjnQ8iwagHDzrryUZUgsUz/prCYZY05vn+msJhiwM6XrR7q/V9OJU4xQuEpcxRsezNPbXEn3Yzz9Hulg3IYT0C3LxxI2CNCQIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQlpA7P5a0iF2fy3hkLP+WsIhVbu/lvRby+6vJR1i99eSG6ffu1XHxlRNcfWq3I0iIK4tJK2nXVyV0lOqEmvjGWAtvnCu+jMzkQWMiOlYdRSd3MAH/UnVPWR/ApFfSkcpiOBvRc3XtAeYW1ZUJbZMD5C8azhWLX4xvZVR692Se0huHm9ySogxkhs3lFVewPM4WlFUlUMoJEYUox1+jAFIbLuHrKaQWlMu8TicDp+4gdw7/qS4t2qBohk4UUF2nieBfLyDM/ItgXhBucT113i14QbixW7M+SRNb6EQ0u8kHavyZQxj2/kgNUCsYDRoqXfEF/Mdu4G8D43uOtakh3R9H1DsyKZvOmneDjt+D/0DTzolrPMHmggAAAAASUVORK5CYII=", + "description": "Displays changes to time-series data over time—for example, daily water consumption for the last month.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.flotWidget.onDataUpdated();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.flotWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.flotWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.flotWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.flotWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries', units: '°C', decimals: 0 }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "settingsDirective": "tb-flot-bar-widget-settings", + "dataKeySettingsDirective": "tb-flot-bar-key-settings", + "latestDataKeySettingsDirective": "tb-flot-latest-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-flot-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000},\"aggregation\":{\"limit\":200,\"type\":\"AVG\"}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"stack\":true,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"defaultBarWidth\":600,\"barAlignment\":\"left\",\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false},\"title\":\"Timeseries Bar Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"configMode\":\"basic\",\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\"}" + }, + "externalId": null, + "tags": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/timeseries_line_chart.json b/application/src/main/data/json/system/widget_types/timeseries_line_chart.json new file mode 100644 index 0000000000..e83b213ae0 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/timeseries_line_chart.json @@ -0,0 +1,27 @@ +{ + "fqn": "charts.basic_timeseries", + "name": "Timeseries Line Chart", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays changes to time-series data over time—for example, temperature or humidity readings.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "templateHtml": "\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.flotWidget.onDataUpdated();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.flotWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.flotWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.flotWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.flotWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries', units: '°C', decimals: 0 }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-flot-line-widget-settings", + "dataKeySettingsDirective": "tb-flot-line-key-settings", + "latestDataKeySettingsDirective": "tb-flot-latest-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-flot-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false},\"title\":\"Timeseries Line Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\"}" + }, + "externalId": null, + "tags": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/timeseries_table.json b/application/src/main/data/json/system/widget_types/timeseries_table.json new file mode 100644 index 0000000000..d267d42763 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/timeseries_table.json @@ -0,0 +1,27 @@ +{ + "fqn": "cards.timeseries_table", + "name": "Timeseries table", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays time series data for one or more entities. Data for each entity is displayed in a separate tab. Columns are configured to display entity fields, attributes, or telemetry data. Highly customizable via cell content functions and row style functions.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 6.5, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.timeseriesTableWidget.onDataUpdated();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.timeseriesTableWidget.onLatestDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n ignoreDataUpdateOnIntervalTick: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries', units: '°C', decimals: 0 }];\n }\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true,\n hasShowCondition: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "latestDataKeySettingsSchema": "", + "settingsDirective": "tb-timeseries-table-widget-settings", + "dataKeySettingsDirective": "tb-timeseries-table-key-settings", + "latestDataKeySettingsDirective": "tb-timeseries-table-latest-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-timeseries-table-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = (value + 60)/120 * 100;\\n var color = tinycolor.mix('blue', 'red', percent);\\n color.setAlpha(.5);\\n return {\\n paddingLeft: '20px',\\n color: '#ffffff',\\n background: color.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = value;\\n var backgroundColor = tinycolor('blue');\\n backgroundColor.setAlpha(value/100);\\n var color = 'blue';\\n if (value > 50) {\\n color = 'white';\\n }\\n \\n return {\\n paddingLeft: '20px',\\n color: color,\\n background: backgroundColor.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}],\"latestDataKeys\":null}],\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showTimestamp\":true,\"displayPagination\":true,\"defaultPageSize\":10},\"title\":\"Timeseries table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"displayTimewindow\":true,\"configMode\":\"basic\"}" + }, + "externalId": null, + "tags": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/trip_animation.json b/application/src/main/data/json/system/widget_types/trip_animation.json new file mode 100644 index 0000000000..b97701cc15 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/trip_animation.json @@ -0,0 +1,29 @@ +{ + "fqn": "maps_v2.test", + "name": "Trip Animation", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the trip of the entity on the OpenStreetMap or other map providers. Allows to scroll and animate the movement of the entity. Highly customizable via custom markers, marker tooltips, and widget actions.", + "descriptor": { + "type": "timeseries", + "sizeX": 10, + "sizeY": 6.5, + "resources": [], + "templateHtml": "", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n var $scope = self.ctx.$scope;\n $scope.self = self;\n}\n\nself.actionSources = function() {\n return {\n 'tooltipAction': {\n name: 'widget-action.tooltip-tag-action',\n multiple: false\n }\n }\n};\n\nself.typeParameters = function() {\n return {\n hasDataPageLink: true,\n ignoreDataUpdateOnIntervalTick: true,\n hasAdditionalLatestDataKeys: true\n };\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-trip-animation-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"latitude\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var gpsData = [\\n37.771210000, -122.510960000,\\n 37.771990000, -122.497070000,\\n 37.772730000, -122.480740000,\\n 37.773360000, -122.466870000,\\n 37.774270000, -122.458520000,\\n 37.771980000, -122.454110000,\\n 37.768250000, -122.453380000,\\n 37.765920000, -122.456810000,\\n 37.765930000, -122.467680000,\\n 37.765500000, -122.477180000,\\n 37.765300000, -122.481660000,\\n 37.764780000, -122.493350000,\\n 37.764120000, -122.508360000,\\n 37.766410000, -122.510260000,\\n 37.770010000, -122.510830000,\\n 37.770980000, -122.510930000\\n];\\n let value = gpsData.indexOf(prevValue); \\nreturn gpsData[(value == -1 ? 0 : (value + 2) % gpsData.length)];\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"longitude\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var gpsData = [\\n37.771210000, -122.510960000,\\n 37.771990000, -122.497070000,\\n 37.772730000, -122.480740000,\\n 37.773360000, -122.466870000,\\n 37.774270000, -122.458520000,\\n 37.771980000, -122.454110000,\\n 37.768250000, -122.453380000,\\n 37.765920000, -122.456810000,\\n 37.765930000, -122.467680000,\\n 37.765500000, -122.477180000,\\n 37.765300000, -122.481660000,\\n 37.764780000, -122.493350000,\\n 37.764120000, -122.508360000,\\n 37.766410000, -122.510260000,\\n 37.770010000, -122.510830000,\\n 37.770980000, -122.510930000\\n];\\n let value = gpsData.indexOf(prevValue); \\nreturn gpsData[(value == -1 ? 1 : (value + 2) % gpsData.length)];\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"history\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":500}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"mapProvider\":\"OpenStreetMap.Mapnik\",\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showLabel\":true,\"label\":\"${entityName}\",\"showTooltip\":true,\"tooltipColor\":\"#fff\",\"tooltipFontColor\":\"#000\",\"tooltipOpacity\":1,\"tooltipPattern\":\"${entityName}

Latitude: ${latitude:7}
Longitude: ${longitude:7}
End Time: ${maxTime}
Start Time: ${minTime}\",\"strokeWeight\":2,\"strokeOpacity\":1,\"pointSize\":10,\"markerImageSize\":34,\"rotationAngle\":180,\"provider\":\"openstreet-map\",\"normalizationStep\":1000,\"decoratorSymbol\":\"arrowHead\",\"decoratorSymbolSize\":10,\"decoratorCustomColor\":\"#000\",\"decoratorOffset\":\"20px\",\"endDecoratorOffset\":\"20px\",\"decoratorRepeat\":\"20px\",\"polygonTooltipPattern\":\"${entityName}

TimeStamp: ${ts:7}\",\"polygonOpacity\":0.5,\"polygonStrokeOpacity\":1,\"polygonStrokeWeight\":1,\"pointTooltipOnRightPanel\":true,\"autocloseTooltip\":true,\"useCustomProvider\":false,\"useLabelFunction\":false,\"useTooltipFunction\":false,\"useMarkerImageFunction\":false,\"useColorFunction\":false,\"usePolylineDecorator\":false,\"useDecoratorCustomColor\":false,\"showPoints\":false,\"showPolygon\":false},\"title\":\"Trip Animation\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":false,\"showLegend\":false,\"actions\":{},\"legendConfig\":{\"position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false},\"displayTimewindow\":true}" + }, + "externalId": null, + "tags": [ + "mapping", + "gps", + "navigation", + "geolocation", + "satellite", + "directions" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_boolean_timeseries.json b/application/src/main/data/json/system/widget_types/update_boolean_timeseries.json new file mode 100644 index 0000000000..7054be76be --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_boolean_timeseries.json @@ -0,0 +1,21 @@ +{ + "fqn": "input_widgets.update_boolean_timeseries", + "name": "Update boolean timeseries", + "deprecated": true, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAIQSURBVHja7d3PSxRhHMfx+VcWgmg3ltKD7BJshyBIQusQ3vIYCILCRMyhH6xShlGWMGq1p1gIibxFULRR1F4ixcNqdNhmUXFipWnTYvfZ/TrjJqFQ13ge3p/L8MztBQ/P8/0eZr6WNHyvrHk8vy5WoxIo0TwqqDQsPxADEviWp0yAKM8qixEpAwECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEC5H9DSiP2noyUNIXsc9j2qKYQ2/732lzI7LyukN4jUV7uLrtndIWcuP4lzKYBELf9rD66/bzZhqw9vFcM37ReTzz5pR3kc8fAWPriDmT58NDl5F0R51j2zOm6PpB+13VfyNNbIm8O/Iwg430iz87Lu/iaNDKz+kB6Hcd5HH60PDedjfkR5FXi2oeWyI3j+Xy+56puW2vh6KXcnTZEFq90pQripAbC5HSDDDoin9qQjxVRkwk1dUrLU2v4bLAxGFuNIEPnanK/s7WSeNDcct7rBvFOxg7djBUjSLX/YDJdEHmbiccv1PQrUb7++T/B9/Xfd8sPikbK+L81VqP7HEu0ukCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAADEUYsyAYDNGNn/zrboZQ7SVZcZYcyXbTFGWg8aBYQQAAAAASUVORK5CYII=", + "description": "Simple form to input new boolean value for pre-defined timeseries key.\nThe widget is deprecated. Use \"Update Multiple Attributes\" widget. Timeseries key type and boolean value type can be selected in widgets data key configuration.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "
\n
\n
\n
\n
\n \n {{currentValue}}\n \n
\n
\n\n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-timeseries-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.attribute-not-allowed' | translate }}\n
\n
\n
\n
", + "templateCss": ".attribute-update-form {\r\n overflow: hidden;\r\n height: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n.attribute-update-form__grid {\r\n display: flex;\r\n}\r\n.grid__element:first-child {\r\n flex: 1;\r\n}\r\n\r\n.grid__element {\r\n display: flex;\r\n}\r\n\r\n.attribute-update-form .mat-button.mat-icon-button {\r\n width: 32px;\r\n min-width: 32px;\r\n height: 32px;\r\n min-height: 32px;\r\n padding: 0 !important;\r\n margin: 0 !important;\r\n line-height: 20px;\r\n}\r\n\r\n.attribute-update-form .mat-icon-button mat-icon {\r\n width: 20px;\r\n min-width: 20px;\r\n height: 20px;\r\n min-height: 20px;\r\n font-size: 20px;\r\n}\r\n\r\n.tb-toast {\r\n font-size: 14px!important;\r\n}", + "controllerScript": "let settings;\nlet utils;\nlet translate;\nlet http;\nlet $scope;\nlet map;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init();\n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n http = $scope.$injector.get(self.ctx.servicesMap.get('http'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n\n settings.trueValue = utils.defaultValue(utils.customTranslation(settings.trueValue, settings.trueValue), true);\n settings.falseValue = utils.defaultValue(utils.customTranslation(settings.falseValue, settings.falseValue), false);\n\n map = {\n true: settings.trueValue,\n false: settings.falseValue\n };\n \n $scope.checkboxValue = false;\n $scope.currentValue = map[$scope.checkboxValue];\n\n $scope.attributeUpdateFormGroup = $scope.fb.group({checkboxValue: [$scope.checkboxValue]});\n\n $scope.changed = function() {\n $scope.checkboxValue = $scope.attributeUpdateFormGroup.get('checkboxValue').value;\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.updateAttribute();\n };\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"timeseries\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function() {\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n let observable = saveEntityTimeseries(\n datasource.entityType,\n datasource.entityId,\n [{\n key: $scope.currentKey,\n value: $scope.checkboxValue\n }]\n );\n\n if (observable) {\n observable.subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('checkboxValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n }\n };\n\n function saveEntityTimeseries(entityType, entityId, telemetries) {\n var telemetriesData = {};\n for (var a = 0; a < telemetries.length; a++) {\n if (typeof telemetries[a].value !== 'undefined' && telemetries[a].value !== null) {\n telemetriesData[telemetries[a].key] = telemetries[a].value;\n }\n }\n if (Object.keys(telemetriesData).length) {\n var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/scope';\n return http.post(url, telemetriesData);\n }\n return null;\n }\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n $scope.checkboxValue = self.ctx.data[0].data[0][1] === 'true';\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.attributeUpdateFormGroup.get('checkboxValue').patchValue($scope.checkboxValue);\n self.ctx.detectChanges();\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {}", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-update-boolean-attribute-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update boolean timeseries\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_device_attribute.json b/application/src/main/data/json/system/widget_types/update_device_attribute.json new file mode 100644 index 0000000000..3cb54b9d84 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_device_attribute.json @@ -0,0 +1,34 @@ +{ + "fqn": "control_widgets.update_attributes", + "name": "Update device attribute", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Allows to send shared attribute update when user press the button.", + "descriptor": { + "type": "rpc", + "sizeX": 4, + "sizeY": 2, + "resources": [], + "templateHtml": "
\n
\n {{title}}\n
\n
\n
\n \n
\n
\n
\n {{ error }}\n
\n
", + "templateCss": ".tb-rpc-button {\n width: 100%;\n height: 100%;\n}\n\n.tb-rpc-button .title-container {\n font-weight: 500;\n white-space: nowrap;\n margin: 10px 0;\n}\n\n.tb-rpc-button .button-container div{\n min-width: 80%\n}\n\n.tb-rpc-button .button-container .mat-mdc-button{\n width: 100%;\n margin: 0;\n}\n\n.tb-rpc-button .error-container {\n position: absolute;\n top: 2%;\n right: 0;\n left: 0;\n z-index: 4;\n height: 14px;\n}\n\n.tb-rpc-button .error-container .button-error {\n color: #ff3315;\n white-space: nowrap;\n}", + "controllerScript": "self.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges();\n });\n};\n\nfunction init() {\n self.ctx.$scope.buttonLable = self.ctx.settings.buttonText;\n self.ctx.$scope.showTitle = self.ctx.settings.title &&\n self.ctx.settings.title.length ? true : false;\n self.ctx.$scope.title = self.ctx.settings.title;\n self.ctx.$scope.styleButton = self.ctx.settings.styleButton;\n let entityAttributeType = self.ctx.settings.entityAttributeType;\n let entityParameters = JSON.parse(self.ctx.settings.entityParameters);\n\n if (self.ctx.settings.styleButton.isPrimary ===\n false) {\n self.ctx.$scope.customStyle = {\n 'background-color': self.ctx.$scope.styleButton\n .bgColor,\n 'color': self.ctx.$scope.styleButton.textColor\n };\n }\n\n let attributeService = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n\n self.ctx.$scope.sendUpdate = function() {\n let attributes = [];\n for (let key in entityParameters) {\n attributes.push({\n \"key\": key,\n \"value\": entityParameters[key]\n });\n }\n \n let entityId = {\n entityType: \"DEVICE\",\n id: self.ctx.defaultSubscription.targetDeviceId\n };\n attributeService.saveEntityAttributes(entityId,\n entityAttributeType, attributes).subscribe(\n function success() {\n self.ctx.$scope.error = \"\";\n self.ctx.detectChanges();\n },\n function fail(rejection) {\n if (self.ctx.settings.showError) {\n self.ctx.$scope.error =\n rejection.status + \": \" +\n rejection.statusText;\n self.ctx.detectChanges();\n }\n }\n\n );\n };\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-update-device-attribute-widget-settings", + "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"styleButton\":{\"isRaised\":true,\"isPrimary\":false},\"entityParameters\":\"{}\",\"entityAttributeType\":\"SERVER_SCOPE\",\"buttonText\":\"Update device attribute\"},\"title\":\"Update device attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"targetDeviceAliases\":[]}" + }, + "externalId": null, + "tags": [ + "command", + "downlink", + "device configuration", + "device control", + "invocation", + "remote method", + "remote function", + "interface", + "subroutine call", + "inter-process communication", + "server request" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_double_timeseries.json b/application/src/main/data/json/system/widget_types/update_double_timeseries.json new file mode 100644 index 0000000000..38b5a8e501 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_double_timeseries.json @@ -0,0 +1,21 @@ +{ + "fqn": "input_widgets.update_double_timeseries", + "name": "Update double timeseries", + "deprecated": true, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAANMSURBVHja7d3fS5NRGAfw/SsrhSCKKIugburCi6KCLrropi4izME0c1kLUxquHybmKNPAi4qMJkm9hOkswhVkpaLMfhmlbjHYu+3Vaf7a3r3fLjad5nZh6NiZ3+diO+fAe/HhOc85Z+/Fjg4R2T0ieLjlMHQRT0iF4KGGPBGdHEIWREjWudVsgKhu3QiyIkYIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQghZS8jbbwDQOT9slJc/5HGJAOmoAaAUjKWERCO9z7RI5kPGC8cBxy3A5/wUBoyy1qNirg+I9jtlAPBcvGkt7xWgRmyvgKpu9JR3NF4HjHLk9DSCBmi2eqnsFwCEK8vGRSj2j9fgM85h0Ito4VgC0m/R4GwEgHZ72xMRIOHiwIv7wJ/HN6oKfAmIdL62tsoCAMps1CvE8vuwvWIIaHgehTEGmULQgPYGv9+vCLWP/Cg1a4ClG18LvDDKKPmJPgNGzwXgdQkF0S5JAPrPXqgzDcAo402R2WYAukorzUMi7uzR6YWqmY0BJzQeUQghhBBC1h9kZllDDIj2cumBaKZaijWk6hmRIFqLybl0RDJJi75EgWitJse/Y20mKf4hDkRrWe4AJJOUPB8ZC0mWj1hOUjgyDjJ9zw1Ae5rcsUKIbK9rmVzovatv+hJvfh5ec4hirfidYl6teGp1bd1/Km9XfDhasOnksZxGAIGmA/rWtU/J2NXL7tT5WEmxz+0sUqHsNsR69lwXcDc3AC13b0U6IFCsZanzsZLld1D/AYApP/5wDYBBfTfwPupPCwSKNbnj/zbE44WLOvYcLwCkCYLpVTyiODb2zDe/P7qypQnphKzioXF0myUxMw9t3+MUFBLcd2LxP1qo1bnDQkKmjhydWjqw4YGIkMiJ/MT5ufggAL9eEhCilWzudLlcLgWvbYBDfyfoPpOnCAiZ1MeiGeYdKtCcp9cfHhB11YoXeewFo2+Cv9kJIYQQQghZf5DbaQ1mhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCEkMyFZc0FwdlzZPC7rwtlxibaqy45rzVX8BUWnY6Q1/jMIAAAAAElFTkSuQmCC", + "description": "Simple form to input new double value for pre-defined timeseries key.\nThe widget is deprecated. Use \"Update Multiple Attributes\" widget. Timeseries key type and double value type can be selected in widgets data key configuration.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-timeseries-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.attribute-not-allowed' | translate }}\n
\n
\n
\n
", + "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", + "controllerScript": "let $scope;\nlet settings;\nlet utils;\nlet translate;\nlet http;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n http = $scope.$injector.get(self.ctx.servicesMap.get('http'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-timeseries-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n\n $scope.attributeUpdateFormGroup = $scope.fb.group(\n {currentValue: [undefined, [$scope.validators.required,\n $scope.validators.min(settings.minValue),\n $scope.validators.max(settings.maxValue)]]}\n );\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"timeseries\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n let observable = saveEntityTimeseries(\n datasource.entityType,\n datasource.entityId,\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n );\n if (observable) {\n observable.subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n\n function saveEntityTimeseries(entityType, entityId, telemetries) {\n var telemetriesData = {};\n for (var a = 0; a < telemetries.length; a++) {\n if (typeof telemetries[a].value !== 'undefined' && telemetries[a].value !== null) {\n telemetriesData[telemetries[a].key] = telemetries[a].value;\n }\n }\n if (Object.keys(telemetriesData).length) {\n var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/scope';\n return http.post(url, telemetriesData);\n }\n return null;\n }\n}\n\nself.onDataUpdated = function() {\n\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(correctValue($scope.originalValue));\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nfunction correctValue(value) {\n if (typeof value !== \"number\") {\n return 0;\n }\n return value;\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n dataKeyOptional: true\n }\n}\n\nself.onDestroy = function() {\n\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-update-double-attribute-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update double timeseries\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_integer_timeseries.json b/application/src/main/data/json/system/widget_types/update_integer_timeseries.json new file mode 100644 index 0000000000..52039b96e9 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_integer_timeseries.json @@ -0,0 +1,21 @@ +{ + "fqn": "input_widgets.update_integer_timeseries", + "name": "Update integer timeseries", + "deprecated": true, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAANBSURBVHja7d3dS1NhHAfw/Sum1IUQBZYJIdSF4VVQIOSNXZiJg5nmWi18CXG9WJgSqd1IBUWu10PY1EBcIFYqyoTCLOZWA8/0uGmbbjs73y7mS+Z2YdjYs76/i3Oe88C5+PB73jhwnkeHiOxyCh4uOQxdxO1XIXiofndEJ/uRBuGXdS41HSCqS+dEWoSTEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYSQfwl59xkA+teqDfLWl9wOESC9twAo5QsJIdHI6EstkvoQX4UP6LsNzNo/hgGDrI2oCI0B0XG7DADuSzcttaMC9JG2t0DTMEZqezuvAwY5UhbEvB5a213J9A0Awg0mnwid/cM1zBpCmPQgWrGwARlv1GDvBABbd88TESDhc3Ov7wM/H99oKp/dgEgXWlqaGgFAWYl6hBh+H9rqp4COV1EYYpAA5vWwdXi9XkWoeeRLjVkDGofxqdwDg4zqrxjTY+b8HDwOoSDaZQnAeNXFVuMEDDIGKs1temCwpsE8JeLMHg2u95qVGHBR4xKFEEIIIeT/gyxvKYgB0d5sXhAtN0uxgtS8LBJEsxrtm2sko/TbTRSI9tzY92ddj1FavYgD0axbHYBklOLnI2Uh8fIRy0kCR8pBgvdcALRn8R3bhMjdrdYlAFh62mpd/Uk81J8ciGKp/56gXW27aQ3uPVqac9AJTOUePpOfMw1gom5/VpJSsnC1zpU4H9vp7KEDlSqUQ3qg6GQI4cKzwIvMU6eTBYFiMSXOx3aG38mM9wCMBUDDAIAr+YDzB9qTBoFiie/4uwmxuGJ1+CgqAYBkQhDcwSVKX+YIANi6SvKmkw7ZwUXjzL5GAEDlsT2l8wJD5o+UrO1o4S0sFhcSOH4isP7wYFdAVEikpEABgGBWF4D27KigEK06u9/hcDgUlOUNLQ7lmkRtWksZsXiEhaqsjN3mgMij1lqEPcJsY8OPD4QQQgghhOws5E5SgxkhhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCElNSNocEJweRzb7ZF04PQ7RVnXpcay5il+KVmW7YpZ2rQAAAABJRU5ErkJggg==", + "description": "Simple form to input new integer value for pre-defined timeseries key.\nThe widget is deprecated. Use \"Update Multiple Attributes\" widget. Timeseries key type and integer value type can be selected in widgets data key configuration.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n\n
\n \n \n
\n
\n\n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-timeseries-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.attribute-not-allowed' | translate }}\n
\n
\n
\n
", + "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}\n", + "controllerScript": "let $scope;\nlet settings;\nlet utils;\nlet translate;\nlet http;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n http = $scope.$injector.get(self.ctx.servicesMap.get('http'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-timeseries-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n\n $scope.attributeUpdateFormGroup = $scope.fb.group(\n {currentValue: [undefined, [$scope.validators.required,\n $scope.validators.min(settings.minValue),\n $scope.validators.max(settings.maxValue),\n $scope.validators.pattern(/^-?[0-9]+$/)]]}\n );\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"timeseries\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n let observable = saveEntityTimeseries(\n datasource.entityType,\n datasource.entityId,\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n );\n if (observable) {\n observable.subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n\n function saveEntityTimeseries(entityType, entityId, telemetries) {\n var telemetriesData = {};\n for (var a = 0; a < telemetries.length; a++) {\n if (typeof telemetries[a].value !== 'undefined' && telemetries[a].value !== null) {\n telemetriesData[telemetries[a].key] = telemetries[a].value;\n }\n }\n if (Object.keys(telemetriesData).length) {\n var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/scope';\n return http.post(url, telemetriesData);\n }\n return null;\n }\n}\n\nself.onDataUpdated = function() {\n\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(correctValue($scope.originalValue));\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nfunction correctValue(value) {\n if (typeof value !== \"number\") {\n return 0;\n }\n return value;\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n dataKeyOptional: true\n }\n}\n\nself.onDestroy = function() {\n\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-update-integer-attribute-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update integer timeseries\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_json_attribute.json b/application/src/main/data/json/system/widget_types/update_json_attribute.json new file mode 100644 index 0000000000..3ca5f234ee --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_json_attribute.json @@ -0,0 +1,21 @@ +{ + "fqn": "input_widgets.update_json_attribute", + "name": "Update JSON attribute", + "deprecated": true, + "image": "data:image/png;base64,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", + "description": "Simple form to input new JSON value for pre-defined attribute/timeseries key.\nThe widget is deprecated. Use \"Update Multiple Attributes\" widget. JSON value type can be selected in widgets data key configuration.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", + "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.jsonInputWidget.onDataUpdated();\n}\n\nself.onResize = function() {\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "{}", + "settingsDirective": "tb-update-json-attribute-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"widgetMode\":\"ATTRIBUTE\",\"attributeScope\":\"SERVER_SCOPE\",\"showLabel\":true,\"attributeRequired\":true,\"showResultMessage\":true},\"title\":\"Update JSON attribute\",\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"showLegend\":false}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_location_timeseries.json b/application/src/main/data/json/system/widget_types/update_location_timeseries.json new file mode 100644 index 0000000000..f868683a6f --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_location_timeseries.json @@ -0,0 +1,21 @@ +{ + "fqn": "input_widgets.update_location_timeseries", + "name": "Update location timeseries", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Simple form to input new location for pre-defined timeseries keys.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? latLabel : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n \n \n {{ settings.showLabel ? lngLabel : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n \n
\n
\n \n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-timeseries-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-coordinate-specified' | translate }}\n
\n
\n
\n
", + "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex-direction: column;\n flex: 1;\n}\n\n.grid__element.horizontal-alignment {\n flex-direction: row;\n}\n\n.grid__element:last-child {\n align-items: center;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n margin: 0;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-button.getLocation {\n margin-right: 10px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.attribute-update-form mat-form-field{\n width: 100%;\n padding-right: 5px;\n}\n\n.attribute-update-form.small-width mat-form-field{\n width: 150px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", + "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet utils;\r\nlet translate;\r\n\r\nself.onInit = function() {\r\n self.ctx.ngZone.run(function() {\r\n init(); \r\n self.ctx.detectChanges(true);\r\n });\r\n};\r\n\r\n\r\nfunction init() {\r\n $scope = self.ctx.$scope;\r\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n $scope.toastTargetId = 'input-widget' + utils.guid();\r\n settings = utils.deepClone(self.ctx.settings) || {};\r\n \r\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\r\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\r\n settings.showGetLocation = utils.defaultValue(settings.showGetLocation, true);\r\n settings.enableHighAccuracy = utils.defaultValue(settings.enableHighAccuracy, false);\r\n $scope.settings = settings;\r\n $scope.isValidParameter = true;\r\n $scope.dataKeyDetected = false; \r\n\r\n $scope.isHorizontal = (settings.inputFieldsAlignment === 'row');\r\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-coordinate-required');\r\n $scope.latLabel = utils.customTranslation(settings.latLabel, settings.latLabel) || translate.instant('widgets.input-widgets.latitude');\r\n $scope.lngLabel = utils.customTranslation(settings.lngLabel, settings.lngLabel) || translate.instant('widgets.input-widgets.longitude');\r\n\r\n $scope.attributeUpdateFormGroup = $scope.fb.group(\r\n {currentLat: [undefined, [$scope.validators.required,\r\n $scope.validators.min(-90),\r\n $scope.validators.max(90)]],\r\n currentLng: [undefined, [$scope.validators.required,\r\n $scope.validators.min(-180),\r\n $scope.validators.max(180)]]}\r\n );\r\n\r\n if (self.ctx.datasources && self.ctx.datasources.length) {\r\n var datasource = self.ctx.datasources[0];\r\n if (datasource.type === 'entity') {\r\n if (datasource.entityType && datasource.entityId) {\r\n $scope.entityName = datasource.entityName;\r\n if (settings.widgetTitle && settings.widgetTitle.length) {\r\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n } else {\r\n $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n }\r\n\r\n $scope.entityDetected = true;\r\n }\r\n }\r\n if (datasource.dataKeys.length > 1) {\r\n $scope.dataKeyDetected = true;\r\n for (let i = 0; i < datasource.dataKeys.length; i++) {\r\n if (datasource.dataKeys[i].type != \"timeseries\"){\r\n $scope.isValidParameter = false;\r\n }\r\n if (datasource.dataKeys[i].name !== settings.latKeyName && datasource.dataKeys[i].name !== settings.lngKeyName){\r\n $scope.dataKeyDetected = false;\r\n }\r\n }\r\n }\r\n }\r\n\r\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n\r\n $scope.updateAttribute = function () {\r\n $scope.isFocused = false;\r\n if ($scope.entityDetected) {\r\n var datasource = self.ctx.datasources[0];\r\n\r\n attributeService.saveEntityTimeseries(\r\n datasource.entity.id,\r\n 'scope',\r\n [\r\n {\r\n key: settings.latKeyName,\r\n value: $scope.attributeUpdateFormGroup.get('currentLat').value\r\n },{\r\n key: settings.lngKeyName,\r\n value: $scope.attributeUpdateFormGroup.get('currentLng').value\r\n }\r\n ]\r\n ).subscribe(\r\n function success() {\r\n $scope.originalLat = $scope.attributeUpdateFormGroup.get('currentLat').value;\r\n $scope.originalLng = $scope.attributeUpdateFormGroup.get('currentLng').value;\r\n if (settings.showResultMessage) {\r\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n },\r\n function fail() {\r\n if (settings.showResultMessage) {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n }\r\n );\r\n }\r\n };\r\n\r\n $scope.changeFocus = function () {\r\n if ($scope.attributeUpdateFormGroup.get('currentLat').value === $scope.originalLat && $scope.attributeUpdateFormGroup.get('currentLng').value === $scope.originalLng) {\r\n $scope.isFocused = false;\r\n }\r\n };\r\n \r\n $scope.discardChange = function() {\r\n $scope.attributeUpdateFormGroup.setValue({\r\n 'currentLat': $scope.originalLat,\r\n 'currentLng': $scope.originalLng\r\n });\r\n $scope.isFocused = false;\r\n $scope.attributeUpdateFormGroup.markAsPristine();\r\n self.onDataUpdated();\r\n };\r\n \r\n $scope.disableButton = function () {\r\n return $scope.attributeUpdateFormGroup.get('currentLat').value === $scope.originalLat && $scope.attributeUpdateFormGroup.get('currentLng').value === $scope.originalLng || $scope.currentLng === null || $scope.currentLat === null;\r\n };\r\n \r\n $scope.getCoordinate = function() {\r\n if (navigator.geolocation) {\r\n navigator.geolocation.getCurrentPosition(showPosition, function (){\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.blocked-location'), \r\n 'bottom', 'left', $scope.toastTargetId);\r\n }, {\r\n enableHighAccuracy: settings.enableHighAccuracy\r\n });\r\n } else {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.no-support-geolocation'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n };\r\n \r\n function showPosition(position) {\r\n $scope.attributeUpdateFormGroup.setValue({\r\n currentLat: correctValue(position.coords.latitude),\r\n currentLng: correctValue(position.coords.longitude)\r\n });\r\n $scope.attributeUpdateFormGroup.markAsDirty();\r\n $scope.isFocused = true;\r\n }\r\n \r\n self.onResize();\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n try {\r\n if ($scope.dataKeyDetected) {\r\n if (!$scope.isFocused) {\r\n for(let i = 0; i < self.typeParameters().maxDataKeys; i++){\r\n if(self.ctx.data[i].dataKey.name === self.ctx.settings.latKeyName && $scope.attributeUpdateFormGroup.get('currentLat').pristine){\r\n $scope.originalLat = self.ctx.data[i].data[0][1];\r\n $scope.attributeUpdateFormGroup.get('currentLat').patchValue(correctValue($scope.originalLat));\r\n } else if(self.ctx.data[i].dataKey.name === self.ctx.settings.lngKeyName && $scope.attributeUpdateFormGroup.get('currentLng').pristine){\r\n $scope.originalLng = self.ctx.data[i].data[0][1];\r\n $scope.attributeUpdateFormGroup.get('currentLng').patchValue(correctValue($scope.originalLng));\r\n }\r\n }\r\n self.ctx.detectChanges();\r\n }\r\n }\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n};\r\n\r\nfunction correctValue(value) {\r\n if (typeof value !== \"number\") {\r\n return 0;\r\n }\r\n return value;\r\n}\r\n\r\nself.onResize = function() {\r\n $scope.smallWidthContainer = (self.ctx.$container && self.ctx.$container[0].offsetWidth < 320);\r\n $scope.changeAlignment = ($scope.isHorizontal && self.ctx.$container && self.ctx.$container[0].offsetWidth < 480);\r\n self.ctx.detectChanges();\r\n};\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1,\r\n maxDataKeys: 2,\r\n singleEntity: true\r\n };\r\n};\r\n\r\nself.onDestroy = function() {\r\n\r\n};", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-update-location-attribute-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"widgetTitle\":\"\",\"showResultMessage\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showGetLocation\":true,\"enableHighAccuracy\":false,\"showLabel\":true,\"latLabel\":\"\",\"lngLabel\":\"\",\"inputFieldsAlignment\":\"column\",\"isLatRequired\":true,\"isLngRequired\":true,\"requiredErrorMessage\":\"\"},\"title\":\"Update location timeseries\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_multiple_attributes.json b/application/src/main/data/json/system/widget_types/update_multiple_attributes.json new file mode 100644 index 0000000000..69a44074f0 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_multiple_attributes.json @@ -0,0 +1,22 @@ +{ + "fqn": "input_widgets.update_multiple_attributes", + "name": "Update Multiple Attributes", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Allows to create an input form and set multiple values of the entity. Each input value may be stored as an attribute or time series data.\nEach input field is highly customizable via input types, value conversion functions, etc.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": ".tb-toast {\n min-width: 0;\n font-size: 14px !important;\n}", + "controllerScript": "self.onInit = function() {\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n self.ctx.$scope.multipleInputWidget.onDataUpdated();\r\n}\r\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-update-multiple-attributes-widget-settings", + "dataKeySettingsDirective": "tb-update-multiple-attributes-key-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update Multiple Attributes\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_server_boolean_attribute.json b/application/src/main/data/json/system/widget_types/update_server_boolean_attribute.json new file mode 100644 index 0000000000..cf5d300751 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_server_boolean_attribute.json @@ -0,0 +1,21 @@ +{ + "fqn": "input_widgets.update_server_boolean_attribute", + "name": "Update server boolean attribute", + "deprecated": true, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAIQSURBVHja7d3PSxRhHMfx+VcWgmg3ltKD7BJshyBIQusQ3vIYCILCRMyhH6xShlGWMGq1p1gIibxFULRR1F4ixcNqdNhmUXFipWnTYvfZ/TrjJqFQ13ge3p/L8MztBQ/P8/0eZr6WNHyvrHk8vy5WoxIo0TwqqDQsPxADEviWp0yAKM8qixEpAwECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEC5H9DSiP2noyUNIXsc9j2qKYQ2/732lzI7LyukN4jUV7uLrtndIWcuP4lzKYBELf9rD66/bzZhqw9vFcM37ReTzz5pR3kc8fAWPriDmT58NDl5F0R51j2zOm6PpB+13VfyNNbIm8O/Iwg430iz87Lu/iaNDKz+kB6Hcd5HH60PDedjfkR5FXi2oeWyI3j+Xy+56puW2vh6KXcnTZEFq90pQripAbC5HSDDDoin9qQjxVRkwk1dUrLU2v4bLAxGFuNIEPnanK/s7WSeNDcct7rBvFOxg7djBUjSLX/YDJdEHmbiccv1PQrUb7++T/B9/Xfd8sPikbK+L81VqP7HEu0ukCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAADEUYsyAYDNGNn/zrboZQ7SVZcZYcyXbTFGWg8aBYQQAAAAASUVORK5CYII=", + "description": "Simple form to input new boolean value for pre-defined server-side attribute key.\nThe widget is deprecated. Use \"Update Multiple Attributes\" widget. Attribute type and boolean value type can be selected in widgets data key configuration.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "
\r\n
\r\n
\r\n
\r\n
\r\n \r\n {{currentValue}}\r\n \r\n
\r\n
\r\n\r\n
\r\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\r\n
\r\n
\r\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\r\n
\r\n
\r\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\r\n
\r\n
\r\n
\r\n
", + "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", + "controllerScript": "let settings;\nlet attributeService;\nlet utils;\nlet translate;\nlet $scope;\nlet map;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init();\n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n\n settings.trueValue = utils.defaultValue(utils.customTranslation(settings.trueValue, settings.trueValue), true);\n settings.falseValue = utils.defaultValue(utils.customTranslation(settings.falseValue, settings.falseValue), false);\n\n map = {\n true: settings.trueValue,\n false: settings.falseValue\n };\n \n $scope.checkboxValue = false;\n $scope.currentValue = map[$scope.checkboxValue];\n\n $scope.attributeUpdateFormGroup = $scope.fb.group({checkboxValue: [$scope.checkboxValue]});\n\n $scope.changed = function() {\n $scope.checkboxValue = $scope.attributeUpdateFormGroup.get('checkboxValue').value;\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.updateAttribute();\n };\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function() {\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SERVER_SCOPE',\n [\n {\n key: $scope.currentKey,\n value: $scope.checkboxValue\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('checkboxValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n $scope.checkboxValue = self.ctx.data[0].data[0][1] === 'true';\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.attributeUpdateFormGroup.get('checkboxValue').patchValue($scope.checkboxValue);\n self.ctx.detectChanges();\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {}", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-update-boolean-attribute-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update server boolean attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_server_date_attribute.json b/application/src/main/data/json/system/widget_types/update_server_date_attribute.json new file mode 100644 index 0000000000..3a1cdabc86 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_server_date_attribute.json @@ -0,0 +1,21 @@ +{ + "fqn": "input_widgets.update_server_date_attribute", + "name": "Update server date attribute", + "deprecated": true, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAdGSURBVHja7d37VxTnGcBx/pUXAfGCGqNSEpVUT6yxamtso41NlXjN4XjPYOLipXgBUxVBOSAJWJWCRleUNJEgWsPxiheiA1IT9bhABVlcWGBd2Nu3P8wKi0hO9WgFzvP8NPvM++7sZ2femXeeH2aCcNdX3e/jUVXvIshdbffQx8Njr3YH1dvpB2GvD6ry9AeIpyroPv0i7gtEIAIRiEAEIhCBCEQgAhGIQAQiEIEIRCACEYhABCIQgQhEIAIRiEAEIhCBCEQgAhGIQAQiEIEI5HVCfGbziUttPa0tO1joAuCY2Ww2m6+3HfH20LD2dUO8iwrPZJqsTz5aNwWuvPdpWeYxAEpKvl5dUvJT+zfPhLT7Dle4XzvEBgWp4L7xrxoai5debcd54WIrAOf2cG6/v2FlPOC6hvNmQ8lNX9WZCsB5/qID4Ny6hJTPbL0A0rzY6UraX/hp+cMjsYXO5nV5h9Y6ABpWFa+rC4TYF1MXm1pg2rm7YM0P2OPz8kyPAR6u3N4r9gixdc2X4NsD1K0Acy5kFwN4dy628DRkqYtrqz2UpHH0EGSdAdh9PfVab4C4FjfyY0rimizqVkDyhuRkUy5AZvaRHZ7Ehq6QlVC5AUqT2bExOXntIYA6Glt6A+RWnK9mjZVTBiTtpNVqbQbsS9p8aXtXe3uC7PnearW29JLTr3eRzXcv/gd+3OBxp2ZijfVydlsbNx8AnuV3eRSbTU+QM1+0c6O210CWr9h0Edwpq+PSt+DdtKbBm6utS7IB3Pw8QTsap/cE8eZo8UmNve7K7vACeFsBd8fx0uL7xT7uVpmiCEQgAhGIQPoORNOevSyQ1wKp3Kw9FZsr+ySkm0PTtvRJiPaM6JeQxtzteU0ArfkpufX+5HELgO28USQ5viO7xr/in3eg8WjKIRuAu2BnlgXAU5SWfdtfX7rc0GVO/fX2g1aAtm9Sc6o7bh5O7T5wD4Dre1IuvBxI1ZjIj0dH/Qcqo6IWjBt6GYA7ofV4Tn8SNgfAMW3YvOjBpQDUhFRxfdS4BVEjdGifOXTehIEl0PL7iJipIfuA6pRoVRbw7Y+iR8a8NeI21LwzasGE8O+NrPOPEfOnh+YDacEzZ4Vs/F8hv5hZObYJW5QGv/uDk7bpvwUgcSF8NmjZpDkAmWF3cH0wFYBdc2DCXBeOd2dDTkgFnr9MhISRD8AUascyYJLWBbJ5RC2OSTEw/91mPHMjjWxqRDVsGOahLjQZcoIrXwZk9BfAlrdpDT4MpA30Ae4xRaA3EzMHYG4MkK8aAe+4E9Sq74CkN2HJLKBQPWBKHHBJ3cCuU9oF8p4GZIb7fEPTgMOqibvpHnK+BIqVhQLVAG2Ds14CxD0gD9gf4n8wgWkKQNFoo0plQCavB66oSuDccKe/37JZ8L4GlCt/Icgc1gg8BRm5G/hO+YfNzkgf6cH+kZIS4SBzCED05pcAqVcFgFkZdcLycDPAwkQCIGO2AZXqPLBsnb/blZAiiN4IWNQpAOzRa+kG8QZnASXqJwAswzPAXQNQlrN2yAnYFgkwdfULQh4Udy7XBUKafj3fBzwM/TkQMsqAnIOmcKMQgTVqOTDegBQBeJeMb+oO8SgDchvAOf39jnLk36cOn1QGiQZk1QtCAsOm8oEjygE4P5jcApAxk0BI1FagQl2FA1OMfOu0GU5gYjxwT5UAJAz3n367QHyhmcBZZQE8i8Y+DNhwe9wwG9vfBHhv7QtAytNNpoyKgE2FZwMZEYBnyfg6AN/EQ10gMzSgRFlgmlHjcn00yQbwYSxwVf0bSB9UyjMgjN0GmNVj4PNR97r8rruqiJwQLxC56/kh3xrXkZOdmclLgU+mAabRRucrQ1q7QFZNBFIHt1M+0AbgXT7WqMrFv+2Fr0JbwRx2mmdCPpoNrI8CdkXoHdnZ8wFdXeG8ug41A/KfG1KuaVpRkaZptzpSWWHHHh0L3QcpIYd1XddrAzoYkAtql/XCyDjYEGtcG8JO6LquP6QsOKm+NHIpnA3dpOu6frcTcvHPxp9RELzvUdGQv8E/gr/UdV2v5maCi4MD8mx3/jSxDXf0jJ9rPn6j+bkh6U8gGZ3jcX2YCvurB0YrpZRSia1DSrtCyB6qBixsoe2NswCEGw13Q+4wFTzPBkuMzMxOSL5y+M+4g1TIqnaYYjRZxVehtfjSIlTwh3eByt8oNf7y81/ZTZqmFZ3WNC0+cDpkeRzY/vCEbtVFd5UdKHir21Nw3NU9VEwT3+kY1JanqpEOAF+t48lps9bHi0H2tGR0hbyC8Pzq+KuY/XYuZ2iapqW3bNX2vuJpeMMrvbEqpkLTNE3bGjjY+9Ct7pYnkAQnJ42lwr5fDrqVYTLt7e37Qwp0Avk/Qvb0kZBDSyACEYhABCIQgQhEIAIRiEAEIhCBCEQgAhGIQAQiEIEIRCACEYhABCIQgQhEIAIRiEAEIhCBCORVQvrNC4L7xyubm+qDXP3jJdqeoP7xWnMP/wVO3ZdhzKZxhAAAAABJRU5ErkJggg==", + "description": "Simple form to input new date value for pre-defined server-side attribute key.\nThe widget is deprecated. Use \"Update Multiple Attributes\" widget. Attribute type and date value type can be selected in widgets data key configuration.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "
\n
\n
\n
\n
\n \n {{ labelValue }}\n \n \n \n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", + "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", + "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet utils;\r\nlet translate;\r\n\r\nself.onInit = function() {\r\n self.ctx.ngZone.run(function() {\r\n init(); \r\n self.ctx.detectChanges(true);\r\n });\r\n};\r\n\r\nfunction init() {\r\n\r\n $scope = self.ctx.$scope;\r\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n $scope.toastTargetId = 'input-widget' + utils.guid();\r\n settings = utils.deepClone(self.ctx.settings) || {};\r\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\r\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\r\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\r\n $scope.settings = settings;\r\n $scope.isValidParameter = true;\r\n $scope.dataKeyDetected = false;\r\n $scope.entityDetected = false;\r\n\r\n $scope.datePickerType = settings.showTimeInput ? 'datetime' : 'date'; \r\n \r\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\r\n $scope.labelValue = translate.instant('widgets.input-widgets.date');\r\n \r\n if (settings.showTimeInput) {\r\n $scope.labelValue += \" & \" + translate.instant('widgets.input-widgets.time');\r\n }\r\n \r\n var validators = [];\r\n \r\n if (settings.isRequired) {\r\n validators.push($scope.validators.required);\r\n }\r\n \r\n $scope.attributeUpdateFormGroup = $scope.fb.group(\r\n {currentValue: [undefined, validators]}\r\n );\r\n\r\n if (self.ctx.datasources && self.ctx.datasources.length) {\r\n var datasource = self.ctx.datasources[0];\r\n \r\n if (datasource.type === 'entity') {\r\n if (datasource.entityType && datasource.entityId) {\r\n $scope.entityName = datasource.entityName;\r\n if (settings.widgetTitle && settings.widgetTitle.length) {\r\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n } else {\r\n $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n }\r\n\r\n $scope.entityDetected = true;\r\n }\r\n }\r\n if (datasource.dataKeys.length) {\r\n if (datasource.dataKeys[0].type !== \"attribute\") {\r\n $scope.isValidParameter = false;\r\n } else {\r\n $scope.currentKey = datasource.dataKeys[0].name;\r\n $scope.dataKeyType = datasource.dataKeys[0].type;\r\n $scope.dataKeyDetected = true;\r\n }\r\n }\r\n }\r\n\r\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n \r\n $scope.clear = function(event) {\r\n event.stopPropagation();\r\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(undefined);\r\n }\r\n \r\n $scope.updateAttribute = function () {\r\n if ($scope.entityDetected) {\r\n var datasource = self.ctx.datasources[0];\r\n var currentValueInMilliseconds;\r\n \r\n if (!$scope.attributeUpdateFormGroup.get('currentValue').value) {\r\n currentValueInMilliseconds = undefined;\r\n } else {\r\n currentValueInMilliseconds = $scope.attributeUpdateFormGroup.get('currentValue').value.getTime();\r\n }\r\n \r\n attributeService.saveEntityAttributes(\r\n datasource.entity.id,\r\n 'SERVER_SCOPE',\r\n [\r\n {\r\n key: $scope.currentKey,\r\n value: currentValueInMilliseconds\r\n }\r\n ]\r\n ).subscribe(\r\n function success() {\r\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\r\n if (settings.showResultMessage) {\r\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n },\r\n function fail() {\r\n if (settings.showResultMessage) {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n }\r\n );\r\n }\r\n };\r\n \r\n $scope.isValidDate = function(date) {\r\n return date instanceof Date && !isNaN(date);\r\n }\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n try {\r\n if ($scope.dataKeyDetected) {\r\n if (!$scope.isFocused) {\r\n $scope.originalValue = moment(self.ctx.data[0].data[0][1]).toDate();\r\n\r\n if (!$scope.isValidDate($scope.originalValue)) {\r\n $scope.originalValue = undefined;\r\n }\r\n \r\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\r\n self.ctx.detectChanges();\r\n }\r\n }\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n};\r\n\r\nself.onResize = function() {\r\n};\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1,\r\n maxDataKeys: 1,\r\n singleEntity: true\r\n };\r\n};\r\n\r\nself.onDestroy = function() {\r\n}\r\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-update-date-attribute-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update server date attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_server_double_attribute.json b/application/src/main/data/json/system/widget_types/update_server_double_attribute.json new file mode 100644 index 0000000000..ddfe7b36d1 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_server_double_attribute.json @@ -0,0 +1,21 @@ +{ + "fqn": "input_widgets.update_server_double_attribute", + "name": "Update server double attribute", + "deprecated": true, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAANMSURBVHja7d3fS5NRGAfw/SsrhSCKKIugburCi6KCLrropi4izME0c1kLUxquHybmKNPAi4qMJkm9hOkswhVkpaLMfhmlbjHYu+3Vaf7a3r3fLjad5nZh6NiZ3+diO+fAe/HhOc85Z+/Fjg4R2T0ieLjlMHQRT0iF4KGGPBGdHEIWREjWudVsgKhu3QiyIkYIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQghZS8jbbwDQOT9slJc/5HGJAOmoAaAUjKWERCO9z7RI5kPGC8cBxy3A5/wUBoyy1qNirg+I9jtlAPBcvGkt7xWgRmyvgKpu9JR3NF4HjHLk9DSCBmi2eqnsFwCEK8vGRSj2j9fgM85h0Ito4VgC0m/R4GwEgHZ72xMRIOHiwIv7wJ/HN6oKfAmIdL62tsoCAMps1CvE8vuwvWIIaHgehTEGmULQgPYGv9+vCLWP/Cg1a4ClG18LvDDKKPmJPgNGzwXgdQkF0S5JAPrPXqgzDcAo402R2WYAukorzUMi7uzR6YWqmY0BJzQeUQghhBBC1h9kZllDDIj2cumBaKZaijWk6hmRIFqLybl0RDJJi75EgWitJse/Y20mKf4hDkRrWe4AJJOUPB8ZC0mWj1hOUjgyDjJ9zw1Ae5rcsUKIbK9rmVzovatv+hJvfh5ec4hirfidYl6teGp1bd1/Km9XfDhasOnksZxGAIGmA/rWtU/J2NXL7tT5WEmxz+0sUqHsNsR69lwXcDc3AC13b0U6IFCsZanzsZLld1D/AYApP/5wDYBBfTfwPupPCwSKNbnj/zbE44WLOvYcLwCkCYLpVTyiODb2zDe/P7qypQnphKzioXF0myUxMw9t3+MUFBLcd2LxP1qo1bnDQkKmjhydWjqw4YGIkMiJ/MT5ufggAL9eEhCilWzudLlcLgWvbYBDfyfoPpOnCAiZ1MeiGeYdKtCcp9cfHhB11YoXeewFo2+Cv9kJIYQQQghZf5DbaQ1mhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCEkMyFZc0FwdlzZPC7rwtlxibaqy45rzVX8BUWnY6Q1/jMIAAAAAElFTkSuQmCC", + "description": "Simple form to input new double value for pre-defined server-side attribute key.\nThe widget is deprecated. Use \"Update Multiple Attributes\" widget. Attribute type and double value type can be selected in widgets data key configuration.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", + "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", + "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false; \n\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [$scope.validators.min(settings.minValue),\n $scope.validators.max(settings.maxValue)];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n \n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SERVER_SCOPE',\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(correctValue($scope.originalValue));\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nfunction correctValue(value) {\n if (typeof value !== \"number\") {\n return 0;\n }\n return value;\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-update-double-attribute-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showResultMessage\":true,\"showLabel\":true,\"isRequired\":true},\"title\":\"Update server double attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_server_image_attribute.json b/application/src/main/data/json/system/widget_types/update_server_image_attribute.json new file mode 100644 index 0000000000..79af76660c --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_server_image_attribute.json @@ -0,0 +1,21 @@ +{ + "fqn": "input_widgets.update_server_image_attribute", + "name": "Update server image attribute", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Simple form to input new image for pre-defined server-side attribute key.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "
\n
\n
\n
\n
\n \n \n
\n \n
\n \n \n
\n
\n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", + "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n align-items: center;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.tb-image-preview-container div,\n.tb-flow-drop label {\n font-size: 16px !important;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", + "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet utils;\r\nlet translate;\r\n\r\nself.onInit = function() {\r\n self.ctx.ngZone.run(function() {\r\n init(); \r\n self.ctx.detectChanges(true);\r\n });\r\n};\r\n\r\nfunction init() {\r\n\r\n $scope = self.ctx.$scope;\r\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n $scope.toastTargetId = 'input-widget' + utils.guid();\r\n settings = utils.deepClone(self.ctx.settings) || {};\r\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\r\n settings.displayPreview = utils.defaultValue(settings.displayPreview, true);\r\n settings.displayClearButton = utils.defaultValue(settings.displayClearButton, false);\r\n settings.displayApplyButton = utils.defaultValue(settings.displayApplyButton, true);\r\n settings.displayDiscardButton = utils.defaultValue(settings.displayDiscardButton, true);\r\n $scope.settings = settings;\r\n $scope.isValidParameter = true;\r\n $scope.dataKeyDetected = false;\r\n $scope.entityDetected = false;\r\n \r\n $scope.attributeUpdateFormGroup = $scope.fb.group(\r\n {currentValue: [undefined, []]}\r\n );\r\n \r\n $scope.attributeUpdateFormGroup.valueChanges.subscribe( () => {\r\n self.ctx.detectChanges();\r\n if (!settings.displayApplyButton) {\r\n $scope.updateAttribute();\r\n }\r\n });\r\n\r\n if (self.ctx.datasources && self.ctx.datasources.length) {\r\n var datasource = self.ctx.datasources[0];\r\n \r\n if (datasource.type === 'entity') {\r\n if (datasource.entityType && datasource.entityId) {\r\n $scope.entityName = datasource.entityName;\r\n if (settings.widgetTitle && settings.widgetTitle.length) {\r\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n } else {\r\n $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n }\r\n\r\n $scope.entityDetected = true;\r\n }\r\n }\r\n if (datasource.dataKeys.length) {\r\n if (datasource.dataKeys[0].type !== \"attribute\") {\r\n $scope.isValidParameter = false;\r\n } else {\r\n $scope.currentKey = datasource.dataKeys[0].name;\r\n $scope.dataKeyType = datasource.dataKeys[0].type;\r\n $scope.dataKeyDetected = true;\r\n }\r\n }\r\n }\r\n\r\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n \r\n $scope.updateAttribute = function () {\r\n if ($scope.entityDetected) {\r\n var datasource = self.ctx.datasources[0];\r\n\r\n attributeService.saveEntityAttributes(\r\n datasource.entity.id,\r\n 'SERVER_SCOPE',\r\n [\r\n {\r\n key: $scope.currentKey,\r\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\r\n }\r\n ]\r\n ).subscribe(\r\n function success() {\r\n if (settings.displayApplyButton) {\r\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\r\n }\r\n if (settings.showResultMessage) {\r\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n },\r\n function fail() {\r\n if (settings.showResultMessage) {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n }\r\n );\r\n }\r\n };\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n try {\r\n if ($scope.dataKeyDetected) {\r\n var value = self.ctx.data[0].data[0][1];\r\n if (settings.displayApplyButton || !$scope.originalValue) {\r\n $scope.originalValue = value;\r\n }\r\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(value, {emitEvent: false});\r\n self.ctx.detectChanges();\r\n }\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n};\r\n\r\nself.onResize = function() {\r\n\r\n};\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1,\r\n maxDataKeys: 1,\r\n singleEntity: true\r\n };\r\n};\r\n\r\nself.onDestroy = function() {\r\n $scope.attributeUpdateFormGroup.valueChanges.unsubscribe();\r\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-update-image-attribute-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showResultMessage\":true,\"displayPreview\":true,\"displayClearButton\":false,\"displayApplyButton\":true,\"displayDiscardButton\":true},\"title\":\"Update server image attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_server_integer_attribute.json b/application/src/main/data/json/system/widget_types/update_server_integer_attribute.json new file mode 100644 index 0000000000..ea4fecdb42 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_server_integer_attribute.json @@ -0,0 +1,21 @@ +{ + "fqn": "input_widgets.update_server_integer_attribute", + "name": "Update server integer attribute", + "deprecated": true, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAANBSURBVHja7d3dS1NhHAfw/Sum1IUQBZYJIdSF4VVQIOSNXZiJg5nmWi18CXG9WJgSqd1IBUWu10PY1EBcIFYqyoTCLOZWA8/0uGmbbjs73y7mS+Z2YdjYs76/i3Oe88C5+PB73jhwnkeHiOxyCh4uOQxdxO1XIXiofndEJ/uRBuGXdS41HSCqS+dEWoSTEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYSQfwl59xkA+teqDfLWl9wOESC9twAo5QsJIdHI6EstkvoQX4UP6LsNzNo/hgGDrI2oCI0B0XG7DADuSzcttaMC9JG2t0DTMEZqezuvAwY5UhbEvB5a213J9A0Awg0mnwid/cM1zBpCmPQgWrGwARlv1GDvBABbd88TESDhc3Ov7wM/H99oKp/dgEgXWlqaGgFAWYl6hBh+H9rqp4COV1EYYpAA5vWwdXi9XkWoeeRLjVkDGofxqdwDg4zqrxjTY+b8HDwOoSDaZQnAeNXFVuMEDDIGKs1temCwpsE8JeLMHg2u95qVGHBR4xKFEEIIIeT/gyxvKYgB0d5sXhAtN0uxgtS8LBJEsxrtm2sko/TbTRSI9tzY92ddj1FavYgD0axbHYBklOLnI2Uh8fIRy0kCR8pBgvdcALRn8R3bhMjdrdYlAFh62mpd/Uk81J8ciGKp/56gXW27aQ3uPVqac9AJTOUePpOfMw1gom5/VpJSsnC1zpU4H9vp7KEDlSqUQ3qg6GQI4cKzwIvMU6eTBYFiMSXOx3aG38mM9wCMBUDDAIAr+YDzB9qTBoFiie/4uwmxuGJ1+CgqAYBkQhDcwSVKX+YIANi6SvKmkw7ZwUXjzL5GAEDlsT2l8wJD5o+UrO1o4S0sFhcSOH4isP7wYFdAVEikpEABgGBWF4D27KigEK06u9/hcDgUlOUNLQ7lmkRtWksZsXiEhaqsjN3mgMij1lqEPcJsY8OPD4QQQgghhOws5E5SgxkhhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCElNSNocEJweRzb7ZF04PQ7RVnXpcay5il+KVmW7YpZ2rQAAAABJRU5ErkJggg==", + "description": "Simple form to input new integer value for pre-defined server attribute key.\nThe widget is deprecated. Use \"Update Multiple Attributes\" widget. Attribute type and integer value type can be selected in widgets data key configuration.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", + "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", + "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false; \n\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [\n $scope.validators.min(settings.minValue),\n $scope.validators.max(settings.maxValue),\n $scope.validators.pattern(/^-?[0-9]+$/)\n ];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n\n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n \n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SERVER_SCOPE',\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(correctValue($scope.originalValue));\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nfunction correctValue(value) {\n if (typeof value !== \"number\") {\n return 0;\n }\n return value;\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-update-integer-attribute-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update server integer attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_server_location_attribute.json b/application/src/main/data/json/system/widget_types/update_server_location_attribute.json new file mode 100644 index 0000000000..0d007ca955 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_server_location_attribute.json @@ -0,0 +1,21 @@ +{ + "fqn": "input_widgets.update_server_location_attribute", + "name": "Update server location attribute", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Simple form to input new location for pre-defined server attribute key.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? latLabel : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n \n \n {{ settings.showLabel ? lngLabel : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n \n
\n
\n \n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-coordinate-specified' | translate }}\n
\n
\n
\n
", + "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex-direction: column;\n flex: 1;\n}\n\n.grid__element.horizontal-alignment {\n flex-direction: row;\n}\n\n.grid__element:last-child {\n align-items: center;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-button.getLocation {\n margin-right: 10px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.attribute-update-form mat-form-field{\n width: 100%;\n padding-right: 5px;\n}\n\n.attribute-update-form.small-width mat-form-field{\n width: 150px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", + "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet utils;\r\nlet translate;\r\n\r\nself.onInit = function() {\r\n self.ctx.ngZone.run(function() {\r\n init(); \r\n self.ctx.detectChanges(true);\r\n });\r\n};\r\n\r\nfunction init() {\r\n $scope = self.ctx.$scope;\r\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n $scope.toastTargetId = 'input-widget' + utils.guid();\r\n settings = utils.deepClone(self.ctx.settings) || {};\r\n \r\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\r\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\r\n settings.showGetLocation = utils.defaultValue(settings.showGetLocation, true);\r\n settings.enableHighAccuracy = utils.defaultValue(settings.enableHighAccuracy, false);\r\n settings.isLatRequired = utils.defaultValue(settings.isLatRequired, true);\r\n settings.isLngRequired = utils.defaultValue(settings.isLngRequired, true);\r\n $scope.settings = settings;\r\n $scope.isValidParameter = true;\r\n $scope.dataKeyDetected = false; \r\n\r\n $scope.isHorizontal = (settings.inputFieldsAlignment === 'row');\r\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-coordinate-required');\r\n $scope.latLabel = utils.customTranslation(settings.latLabel, settings.latLabel) || translate.instant('widgets.input-widgets.latitude');\r\n $scope.lngLabel = utils.customTranslation(settings.lngLabel, settings.lngLabel) || translate.instant('widgets.input-widgets.longitude');\r\n \r\n var validatorsLat = [$scope.validators.min(-90), $scope.validators.max(90)];\r\n var validatorsLng = [$scope.validators.min(-180), $scope.validators.max(180)];\r\n \r\n if (settings.isLatRequired) {\r\n validatorsLat.push($scope.validators.required);\r\n }\r\n \r\n if (settings.isLngRequired) {\r\n validatorsLng.push($scope.validators.required);\r\n }\r\n\r\n $scope.attributeUpdateFormGroup = $scope.fb.group({\r\n currentLat: [undefined, validatorsLat],\r\n currentLng: [undefined, validatorsLng]\r\n });\r\n\r\n if (self.ctx.datasources && self.ctx.datasources.length) {\r\n var datasource = self.ctx.datasources[0];\r\n if (datasource.type === 'entity') {\r\n if (datasource.entityType && datasource.entityId) {\r\n $scope.entityName = datasource.entityName;\r\n if (settings.widgetTitle && settings.widgetTitle.length) {\r\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n } else {\r\n $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n }\r\n\r\n $scope.entityDetected = true;\r\n }\r\n }\r\n if (datasource.dataKeys.length > 1) {\r\n $scope.dataKeyDetected = true;\r\n for (let i = 0; i < datasource.dataKeys.length; i++) {\r\n if (datasource.dataKeys[i].type != \"attribute\") {\r\n $scope.isValidParameter = false;\r\n }\r\n if (datasource.dataKeys[i].name !== settings.latKeyName && datasource.dataKeys[i].name !== settings.lngKeyName){\r\n $scope.dataKeyDetected = false;\r\n }\r\n }\r\n }\r\n }\r\n\r\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n\r\n $scope.updateAttribute = function () {\r\n $scope.isFocused = false;\r\n if ($scope.entityDetected) {\r\n var datasource = self.ctx.datasources[0];\r\n\r\n attributeService.saveEntityAttributes(\r\n datasource.entity.id,\r\n 'SERVER_SCOPE',\r\n [\r\n {\r\n key: settings.latKeyName,\r\n value: $scope.attributeUpdateFormGroup.get('currentLat').value\r\n },{\r\n key: settings.lngKeyName,\r\n value: $scope.attributeUpdateFormGroup.get('currentLng').value\r\n }\r\n ]\r\n ).subscribe(\r\n function success() {\r\n $scope.originalLat = $scope.attributeUpdateFormGroup.get('currentLat').value;\r\n $scope.originalLng = $scope.attributeUpdateFormGroup.get('currentLng').value;\r\n if (settings.showResultMessage) {\r\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n },\r\n function fail() {\r\n if (settings.showResultMessage) {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n }\r\n );\r\n }\r\n };\r\n\r\n $scope.changeFocus = function () {\r\n if ($scope.attributeUpdateFormGroup.get('currentLat').value === $scope.originalLat && $scope.attributeUpdateFormGroup.get('currentLng').value === $scope.originalLng) {\r\n $scope.isFocused = false;\r\n }\r\n };\r\n \r\n $scope.discardChange = function() {\r\n $scope.attributeUpdateFormGroup.setValue({\r\n 'currentLat': $scope.originalLat,\r\n 'currentLng': $scope.originalLng\r\n });\r\n $scope.isFocused = false;\r\n $scope.attributeUpdateFormGroup.markAsPristine();\r\n self.onDataUpdated();\r\n };\r\n \r\n $scope.disableButton = function () {\r\n return $scope.attributeUpdateFormGroup.get('currentLat').value === $scope.originalLat && $scope.attributeUpdateFormGroup.get('currentLng').value === $scope.originalLng || $scope.currentLng === null || $scope.currentLat === null;\r\n };\r\n \r\n $scope.getCoordinate = function() {\r\n if (navigator.geolocation) {\r\n navigator.geolocation.getCurrentPosition(showPosition, function (){\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.blocked-location'), \r\n 'bottom', 'left', $scope.toastTargetId);\r\n }, {\r\n enableHighAccuracy: settings.enableHighAccuracy\r\n });\r\n } else {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.no-support-geolocation'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n };\r\n \r\n function showPosition(position) {\r\n $scope.attributeUpdateFormGroup.setValue({\r\n currentLat: correctValue(position.coords.latitude),\r\n currentLng: correctValue(position.coords.longitude)\r\n });\r\n $scope.attributeUpdateFormGroup.markAsDirty();\r\n $scope.isFocused = true;\r\n }\r\n \r\n self.onResize();\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n try {\r\n if ($scope.dataKeyDetected) {\r\n if (!$scope.isFocused) {\r\n for(let i = 0; i < self.typeParameters().maxDataKeys; i++){\r\n if(self.ctx.data[i].dataKey.name === self.ctx.settings.latKeyName && $scope.attributeUpdateFormGroup.get('currentLat').pristine){\r\n $scope.originalLat = self.ctx.data[i].data[0][1];\r\n $scope.attributeUpdateFormGroup.get('currentLat').patchValue(correctValue($scope.originalLat));\r\n } else if(self.ctx.data[i].dataKey.name === self.ctx.settings.lngKeyName && $scope.attributeUpdateFormGroup.get('currentLng').pristine){\r\n $scope.originalLng = self.ctx.data[i].data[0][1];\r\n $scope.attributeUpdateFormGroup.get('currentLng').patchValue(correctValue($scope.originalLng));\r\n }\r\n }\r\n self.ctx.detectChanges();\r\n }\r\n }\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n};\r\n\r\nfunction correctValue(value) {\r\n if (typeof value !== \"number\") {\r\n return 0;\r\n }\r\n return value;\r\n}\r\n\r\nself.onResize = function() {\r\n $scope.smallWidthContainer = (self.ctx.$container && self.ctx.$container[0].offsetWidth < 320);\r\n $scope.changeAlignment = ($scope.isHorizontal && self.ctx.$container && self.ctx.$container[0].offsetWidth < 480);\r\n self.ctx.detectChanges();\r\n};\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1,\r\n maxDataKeys: 2,\r\n singleEntity: true\r\n };\r\n};\r\n\r\nself.onDestroy = function() {\r\n\r\n};", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-update-location-attribute-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"widgetTitle\":\"\",\"showResultMessage\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showGetLocation\":true,\"enableHighAccuracy\":false,\"showLabel\":true,\"latLabel\":\"\",\"lngLabel\":\"\",\"inputFieldsAlignment\":\"column\",\"isLatRequired\":true,\"isLngRequired\":true,\"requiredErrorMessage\":\"\"},\"title\":\"Update server location attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_server_string_attribute.json b/application/src/main/data/json/system/widget_types/update_server_string_attribute.json new file mode 100644 index 0000000000..193300cb06 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_server_string_attribute.json @@ -0,0 +1,21 @@ +{ + "fqn": "input_widgets.update_server_string_attribute", + "name": "Update server string attribute", + "deprecated": true, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAOkSURBVHja7d3bSxRRHAfw/VdG6yklCbPsRk+GhVLYDSXMohC1tczVUkxFXDULzW4a+SA9VIZRm5haIJqBmMqW2cUoddUVR1sved2dnV/nuJaX3X0Q1pjZvj/YM4fDmYfP/uZcdvbhaMgmmnpVHibRShpb/4REKg9pot+mESfIC2JC1Jgkb4BIJk0veUX0AgIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAsp6QN194+epPs1Z0vqm/Uw2QuuussMSOuYXYbe3PZJvyIeNx40T1xUTDTe+sHCK3STTfwQDGpgVU/+Vr+ox2FYyRktdEuS3UllFXVsAhtrMz9DOe5JI7htQfvIM1K3VcDYO9NZ+GtfP00Uz2uLEliDFHpqYy3qG2suaxGiDW86PVFURTD6/mxg4vQQwpRUW5ObyDZc5uVsX0+6A2s5uo9LmdtA7INIfUlo6MjFhUtY58S06TiXJa6HOsmc9aSd+pI576Lo6SuVNVEDndwErjhUs3dO85pCExrSSeqDE5K61bjSu7febvqJlzACdlbFEAAQQQQP4/yKxTRR0Q+eXKDdFsocFRMRTOqgkiP9E1rWwx6AzLLmqByE919avbajihxqVDsRCWj3rnVpYM1/lQLMRVPhw5ceNQHGTmnok7qlw7PAuJOrWOEIs+c8DNc+XxR2tdITSWd8XkPh8eHezrC2E5SXWfjzVNvxUdRn0Bf29V3sZvq2GVD82FuUYy5ukbFiBdN7MbeU+pOvvuV96xoyrT4kGJa8eaF8Sg/cHaEN9WIr/ixe9/8+7QxD0bsgITDgqPWMu2refChXIi28mApIiNzOYX7B826MEB76EtStC+SbLuSFkOiZZoaos/ewcUdYh9AodJ1gX8ovubekhO2Ms6hk8pcdMYlM+KmBPLIUWscoy1UF7Q4hjpEpopTMsq9cKYo6NCIacjV0MiY1hRELgImRQqKUBYiC5VQ4aEatqZ2MljWvGQYP6C9LAz5CirvBA+UUyYndXmSPGQ2GDjwG0fZ4hQPPh21xH2l5BP+oCoD7UrHtITIvgej3CGnIkWhAN9fELfLggh7YrNyLIQp92sWUOOq31QxG92QAABBBBAvBNy658GMgIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAokyI1xwQ7B1HNo+LGqt3HKItabzjWHOJfgM5tA7UOw6xeQAAAABJRU5ErkJggg==", + "description": "Simple form to input new string value for pre-defined server-side attribute key.\nThe widget is deprecated. Use \"Update Multiple Attributes\" widget. Attribute type and string value type can be selected in widgets data key configuration.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", + "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", + "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [$scope.validators.minLength(settings.minLength),\n $scope.validators.maxLength(settings.maxLength)];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n \n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n \n var value = $scope.attributeUpdateFormGroup.get('currentValue').value;\n \n if (!$scope.attributeUpdateFormGroup.get('currentValue').value.length) {\n value = null;\n }\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SERVER_SCOPE',\n [\n {\n key: $scope.currentKey,\n value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-update-string-attribute-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showResultMessage\":true,\"showLabel\":true,\"isRequired\":true},\"title\":\"Update server string attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_shared_boolean_attribute.json b/application/src/main/data/json/system/widget_types/update_shared_boolean_attribute.json new file mode 100644 index 0000000000..765c6381f2 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_shared_boolean_attribute.json @@ -0,0 +1,21 @@ +{ + "fqn": "input_widgets.update_shared_boolean_attribute", + "name": "Update shared boolean attribute", + "deprecated": true, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAIQSURBVHja7d3PSxRhHMfx+VcWgmg3ltKD7BJshyBIQusQ3vIYCILCRMyhH6xShlGWMGq1p1gIibxFULRR1F4ixcNqdNhmUXFipWnTYvfZ/TrjJqFQ13ge3p/L8MztBQ/P8/0eZr6WNHyvrHk8vy5WoxIo0TwqqDQsPxADEviWp0yAKM8qixEpAwECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAEC5H9DSiP2noyUNIXsc9j2qKYQ2/732lzI7LyukN4jUV7uLrtndIWcuP4lzKYBELf9rD66/bzZhqw9vFcM37ReTzz5pR3kc8fAWPriDmT58NDl5F0R51j2zOm6PpB+13VfyNNbIm8O/Iwg430iz87Lu/iaNDKz+kB6Hcd5HH60PDedjfkR5FXi2oeWyI3j+Xy+56puW2vh6KXcnTZEFq90pQripAbC5HSDDDoin9qQjxVRkwk1dUrLU2v4bLAxGFuNIEPnanK/s7WSeNDcct7rBvFOxg7djBUjSLX/YDJdEHmbiccv1PQrUb7++T/B9/Xfd8sPikbK+L81VqP7HEu0ukCAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAAAECBAgQIECAADEUYsyAYDNGNn/zrboZQ7SVZcZYcyXbTFGWg8aBYQQAAAAASUVORK5CYII=", + "description": "Simple form to input new boolean value for pre-defined shared attribute key.\nThe widget is deprecated. Use \"Update Multiple Attributes\" widget. Attribute type and boolean value type can be selected in widgets data key configuration.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "
\r\n
\r\n
\r\n
\r\n
\r\n \r\n {{currentValue}}\r\n \r\n
\r\n
\r\n\r\n
\r\n
\r\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\r\n
\r\n
\r\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\r\n
\r\n
\r\n
\r\n
", + "templateCss": ".attribute-update-form {\r\n overflow: hidden;\r\n height: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n.attribute-update-form__grid {\r\n display: flex;\r\n}\r\n.grid__element:first-child {\r\n flex: 1;\r\n}\r\n\r\n.grid__element {\r\n display: flex;\r\n}\r\n\r\n.attribute-update-form .mat-button.mat-icon-button {\r\n width: 32px;\r\n min-width: 32px;\r\n height: 32px;\r\n min-height: 32px;\r\n padding: 0 !important;\r\n margin: 0 !important;\r\n line-height: 20px;\r\n}\r\n\r\n.attribute-update-form .mat-icon-button mat-icon {\r\n width: 20px;\r\n min-width: 20px;\r\n height: 20px;\r\n min-height: 20px;\r\n font-size: 20px;\r\n}\r\n\r\n.tb-toast {\r\n font-size: 14px!important;\r\n}", + "controllerScript": "let settings;\nlet attributeService;\nlet utils;\nlet translate;\nlet $scope;\nlet map;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init();\n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\n\n settings.trueValue = utils.defaultValue(utils.customTranslation(settings.trueValue, settings.trueValue), true);\n settings.falseValue = utils.defaultValue(utils.customTranslation(settings.falseValue, settings.falseValue), false);\n\n map = {\n true: settings.trueValue,\n false: settings.falseValue\n };\n \n $scope.checkboxValue = false;\n $scope.currentValue = map[$scope.checkboxValue];\n\n $scope.attributeUpdateFormGroup = $scope.fb.group({checkboxValue: [$scope.checkboxValue]});\n\n $scope.changed = function() {\n $scope.checkboxValue = $scope.attributeUpdateFormGroup.get('checkboxValue').value;\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.updateAttribute();\n };\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType === 'DEVICE') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n \n $scope.entityDetected = true;\n }\n } else {\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function() {\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SHARED_SCOPE',\n [\n {\n key: $scope.currentKey,\n value: $scope.checkboxValue || false\n }\n ]\n ).subscribe(\n function success() {\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n $scope.checkboxValue = self.ctx.data[0].data[0][1] === 'true';\n $scope.currentValue = map[$scope.checkboxValue];\n $scope.attributeUpdateFormGroup.get('checkboxValue').patchValue($scope.checkboxValue);\n self.ctx.detectChanges();\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {}", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-update-boolean-attribute-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update shared boolean attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_shared_date_attribute.json b/application/src/main/data/json/system/widget_types/update_shared_date_attribute.json new file mode 100644 index 0000000000..2225111ff7 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_shared_date_attribute.json @@ -0,0 +1,21 @@ +{ + "fqn": "input_widgets.update_shared_date_attribute", + "name": "Update shared date attribute", + "deprecated": true, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAdGSURBVHja7d37VxTnGcBx/pUXAfGCGqNSEpVUT6yxamtso41NlXjN4XjPYOLipXgBUxVBOSAJWJWCRleUNJEgWsPxiheiA1IT9bhABVlcWGBd2Nu3P8wKi0hO9WgFzvP8NPvM++7sZ2femXeeH2aCcNdX3e/jUVXvIshdbffQx8Njr3YH1dvpB2GvD6ry9AeIpyroPv0i7gtEIAIRiEAEIhCBCEQgAhGIQAQiEIEIRCACEYhABCIQgQhEIAIRiEAEIhCBCEQgAhGIQAQiEIEI5HVCfGbziUttPa0tO1joAuCY2Ww2m6+3HfH20LD2dUO8iwrPZJqsTz5aNwWuvPdpWeYxAEpKvl5dUvJT+zfPhLT7Dle4XzvEBgWp4L7xrxoai5debcd54WIrAOf2cG6/v2FlPOC6hvNmQ8lNX9WZCsB5/qID4Ny6hJTPbL0A0rzY6UraX/hp+cMjsYXO5nV5h9Y6ABpWFa+rC4TYF1MXm1pg2rm7YM0P2OPz8kyPAR6u3N4r9gixdc2X4NsD1K0Acy5kFwN4dy628DRkqYtrqz2UpHH0EGSdAdh9PfVab4C4FjfyY0rimizqVkDyhuRkUy5AZvaRHZ7Ehq6QlVC5AUqT2bExOXntIYA6Glt6A+RWnK9mjZVTBiTtpNVqbQbsS9p8aXtXe3uC7PnearW29JLTr3eRzXcv/gd+3OBxp2ZijfVydlsbNx8AnuV3eRSbTU+QM1+0c6O210CWr9h0Edwpq+PSt+DdtKbBm6utS7IB3Pw8QTsap/cE8eZo8UmNve7K7vACeFsBd8fx0uL7xT7uVpmiCEQgAhGIQPoORNOevSyQ1wKp3Kw9FZsr+ySkm0PTtvRJiPaM6JeQxtzteU0ArfkpufX+5HELgO28USQ5viO7xr/in3eg8WjKIRuAu2BnlgXAU5SWfdtfX7rc0GVO/fX2g1aAtm9Sc6o7bh5O7T5wD4Dre1IuvBxI1ZjIj0dH/Qcqo6IWjBt6GYA7ofV4Tn8SNgfAMW3YvOjBpQDUhFRxfdS4BVEjdGifOXTehIEl0PL7iJipIfuA6pRoVRbw7Y+iR8a8NeI21LwzasGE8O+NrPOPEfOnh+YDacEzZ4Vs/F8hv5hZObYJW5QGv/uDk7bpvwUgcSF8NmjZpDkAmWF3cH0wFYBdc2DCXBeOd2dDTkgFnr9MhISRD8AUascyYJLWBbJ5RC2OSTEw/91mPHMjjWxqRDVsGOahLjQZcoIrXwZk9BfAlrdpDT4MpA30Ae4xRaA3EzMHYG4MkK8aAe+4E9Sq74CkN2HJLKBQPWBKHHBJ3cCuU9oF8p4GZIb7fEPTgMOqibvpHnK+BIqVhQLVAG2Ds14CxD0gD9gf4n8wgWkKQNFoo0plQCavB66oSuDccKe/37JZ8L4GlCt/Icgc1gg8BRm5G/hO+YfNzkgf6cH+kZIS4SBzCED05pcAqVcFgFkZdcLycDPAwkQCIGO2AZXqPLBsnb/blZAiiN4IWNQpAOzRa+kG8QZnASXqJwAswzPAXQNQlrN2yAnYFgkwdfULQh4Udy7XBUKafj3fBzwM/TkQMsqAnIOmcKMQgTVqOTDegBQBeJeMb+oO8SgDchvAOf39jnLk36cOn1QGiQZk1QtCAsOm8oEjygE4P5jcApAxk0BI1FagQl2FA1OMfOu0GU5gYjxwT5UAJAz3n367QHyhmcBZZQE8i8Y+DNhwe9wwG9vfBHhv7QtAytNNpoyKgE2FZwMZEYBnyfg6AN/EQ10gMzSgRFlgmlHjcn00yQbwYSxwVf0bSB9UyjMgjN0GmNVj4PNR97r8rruqiJwQLxC56/kh3xrXkZOdmclLgU+mAabRRucrQ1q7QFZNBFIHt1M+0AbgXT7WqMrFv+2Fr0JbwRx2mmdCPpoNrI8CdkXoHdnZ8wFdXeG8ug41A/KfG1KuaVpRkaZptzpSWWHHHh0L3QcpIYd1XddrAzoYkAtql/XCyDjYEGtcG8JO6LquP6QsOKm+NHIpnA3dpOu6frcTcvHPxp9RELzvUdGQv8E/gr/UdV2v5maCi4MD8mx3/jSxDXf0jJ9rPn6j+bkh6U8gGZ3jcX2YCvurB0YrpZRSia1DSrtCyB6qBixsoe2NswCEGw13Q+4wFTzPBkuMzMxOSL5y+M+4g1TIqnaYYjRZxVehtfjSIlTwh3eByt8oNf7y81/ZTZqmFZ3WNC0+cDpkeRzY/vCEbtVFd5UdKHir21Nw3NU9VEwT3+kY1JanqpEOAF+t48lps9bHi0H2tGR0hbyC8Pzq+KuY/XYuZ2iapqW3bNX2vuJpeMMrvbEqpkLTNE3bGjjY+9Ct7pYnkAQnJ42lwr5fDrqVYTLt7e37Qwp0Avk/Qvb0kZBDSyACEYhABCIQgQhEIAIRiEAEIhCBCEQgAhGIQAQiEIEIRCACEYhABCIQgQhEIAIRiEAEIhCBCORVQvrNC4L7xyubm+qDXP3jJdqeoP7xWnMP/wVO3ZdhzKZxhAAAAABJRU5ErkJggg==", + "description": "Simple form to input new date for pre-defined shared attribute key.\nThe widget is deprecated. Use \"Update Multiple Attributes\" widget. Attribute type and date value type can be selected in widgets data key configuration.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "
\n
\n
\n
\n
\n \n {{ labelValue }}\n \n \n \n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", + "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", + "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet utils;\r\nlet translate;\r\n\r\nself.onInit = function() {\r\n self.ctx.ngZone.run(function() {\r\n init(); \r\n self.ctx.detectChanges(true);\r\n });\r\n};\r\n\r\nfunction init() {\r\n\r\n $scope = self.ctx.$scope;\r\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n $scope.toastTargetId = 'input-widget' + utils.guid();\r\n settings = utils.deepClone(self.ctx.settings) || {};\r\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\r\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\r\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\r\n $scope.settings = settings;\r\n $scope.isValidParameter = true;\r\n $scope.dataKeyDetected = false;\r\n $scope.entityDetected = false;\r\n\r\n $scope.datePickerType = settings.showTimeInput ? 'datetime' : 'date'; \r\n \r\n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\r\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\r\n $scope.labelValue = translate.instant('widgets.input-widgets.date');\r\n \r\n \r\n if (settings.showTimeInput) {\r\n $scope.labelValue += \" & \" + translate.instant('widgets.input-widgets.time');\r\n }\r\n \r\n var validators = [];\r\n \r\n if (settings.isRequired) {\r\n validators.push($scope.validators.required);\r\n }\r\n \r\n $scope.attributeUpdateFormGroup = $scope.fb.group(\r\n {currentValue: [undefined, validators]}\r\n );\r\n\r\n if (self.ctx.datasources && self.ctx.datasources.length) {\r\n var datasource = self.ctx.datasources[0];\r\n \r\n if (datasource.type === 'entity') {\r\n if (datasource.entityType === 'DEVICE') {\r\n if (datasource.entityType && datasource.entityId) {\r\n $scope.entityName = datasource.entityName;\r\n if (settings.widgetTitle && settings.widgetTitle.length) {\r\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n } else {\r\n $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n }\r\n \r\n $scope.entityDetected = true;\r\n }\r\n } else {\r\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\r\n }\r\n }\r\n if (datasource.dataKeys.length) {\r\n if (datasource.dataKeys[0].type !== \"attribute\") {\r\n $scope.isValidParameter = false;\r\n } else {\r\n $scope.currentKey = datasource.dataKeys[0].name;\r\n $scope.dataKeyType = datasource.dataKeys[0].type;\r\n $scope.dataKeyDetected = true;\r\n }\r\n }\r\n }\r\n\r\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n \r\n $scope.clear = function(event) {\r\n event.stopPropagation();\r\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(undefined);\r\n }\r\n \r\n $scope.updateAttribute = function () {\r\n if ($scope.entityDetected) {\r\n var datasource = self.ctx.datasources[0];\r\n var currentValueInMilliseconds;\r\n \r\n if (!$scope.attributeUpdateFormGroup.get('currentValue').value) {\r\n currentValueInMilliseconds = undefined;\r\n } else {\r\n currentValueInMilliseconds = $scope.attributeUpdateFormGroup.get('currentValue').value.getTime();\r\n }\r\n\r\n attributeService.saveEntityAttributes(\r\n datasource.entity.id,\r\n 'SHARED_SCOPE',\r\n [\r\n {\r\n key: $scope.currentKey,\r\n value: currentValueInMilliseconds\r\n }\r\n ]\r\n ).subscribe(\r\n function success() {\r\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\r\n if (settings.showResultMessage) {\r\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n },\r\n function fail() {\r\n if (settings.showResultMessage) {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n }\r\n );\r\n }\r\n };\r\n \r\n $scope.isValidDate = function(date) {\r\n return date instanceof Date && !isNaN(date);\r\n }\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n try {\r\n if ($scope.dataKeyDetected) {\r\n if (!$scope.isFocused) {\r\n $scope.originalValue = moment(self.ctx.data[0].data[0][1]).toDate();\r\n \r\n if (!$scope.isValidDate($scope.originalValue)) {\r\n $scope.originalValue = undefined;\r\n }\r\n \r\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\r\n self.ctx.detectChanges();\r\n }\r\n }\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n};\r\n\r\nself.onResize = function() {\r\n\r\n};\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1,\r\n maxDataKeys: 1,\r\n singleEntity: true\r\n };\r\n};\r\n\r\nself.onDestroy = function() {\r\n\r\n}\r\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-update-date-attribute-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showResultMessage\":true,\"isRequired\":true,\"showLabel\":true,\"showTimeInput\":true},\"title\":\"Update shared date attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_shared_double_attribute.json b/application/src/main/data/json/system/widget_types/update_shared_double_attribute.json new file mode 100644 index 0000000000..29c96f71b4 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_shared_double_attribute.json @@ -0,0 +1,21 @@ +{ + "fqn": "input_widgets.update_shared_double_attribute", + "name": "Update shared double attribute", + "deprecated": true, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAANMSURBVHja7d3fS5NRGAfw/SsrhSCKKIugburCi6KCLrropi4izME0c1kLUxquHybmKNPAi4qMJkm9hOkswhVkpaLMfhmlbjHYu+3Vaf7a3r3fLjad5nZh6NiZ3+diO+fAe/HhOc85Z+/Fjg4R2T0ieLjlMHQRT0iF4KGGPBGdHEIWREjWudVsgKhu3QiyIkYIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQghZS8jbbwDQOT9slJc/5HGJAOmoAaAUjKWERCO9z7RI5kPGC8cBxy3A5/wUBoyy1qNirg+I9jtlAPBcvGkt7xWgRmyvgKpu9JR3NF4HjHLk9DSCBmi2eqnsFwCEK8vGRSj2j9fgM85h0Ito4VgC0m/R4GwEgHZ72xMRIOHiwIv7wJ/HN6oKfAmIdL62tsoCAMps1CvE8vuwvWIIaHgehTEGmULQgPYGv9+vCLWP/Cg1a4ClG18LvDDKKPmJPgNGzwXgdQkF0S5JAPrPXqgzDcAo402R2WYAukorzUMi7uzR6YWqmY0BJzQeUQghhBBC1h9kZllDDIj2cumBaKZaijWk6hmRIFqLybl0RDJJi75EgWitJse/Y20mKf4hDkRrWe4AJJOUPB8ZC0mWj1hOUjgyDjJ9zw1Ae5rcsUKIbK9rmVzovatv+hJvfh5ec4hirfidYl6teGp1bd1/Km9XfDhasOnksZxGAIGmA/rWtU/J2NXL7tT5WEmxz+0sUqHsNsR69lwXcDc3AC13b0U6IFCsZanzsZLld1D/AYApP/5wDYBBfTfwPupPCwSKNbnj/zbE44WLOvYcLwCkCYLpVTyiODb2zDe/P7qypQnphKzioXF0myUxMw9t3+MUFBLcd2LxP1qo1bnDQkKmjhydWjqw4YGIkMiJ/MT5ufggAL9eEhCilWzudLlcLgWvbYBDfyfoPpOnCAiZ1MeiGeYdKtCcp9cfHhB11YoXeewFo2+Cv9kJIYQQQghZf5DbaQ1mhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCEkMyFZc0FwdlzZPC7rwtlxibaqy45rzVX8BUWnY6Q1/jMIAAAAAElFTkSuQmCC", + "description": "Simple form to input new double value for pre-defined shared attribute key.\nThe widget is deprecated. Use \"Update Multiple Attributes\" widget. Attribute type and double value type can be selected in widgets data key configuration.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", + "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", + "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false; \n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\n \n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [\n $scope.validators.min(settings.minValue),\n $scope.validators.max(settings.maxValue)\n ];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n\n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n \n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType === 'DEVICE') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n \n $scope.entityDetected = true;\n }\n } else {\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SHARED_SCOPE',\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(correctValue($scope.originalValue));\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nfunction correctValue(value) {\n if (typeof value !== \"number\") {\n return 0;\n }\n return value;\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-update-double-attribute-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update shared double attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_shared_image_attribute.json b/application/src/main/data/json/system/widget_types/update_shared_image_attribute.json new file mode 100644 index 0000000000..895ef845c9 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_shared_image_attribute.json @@ -0,0 +1,21 @@ +{ + "fqn": "input_widgets.update_shared_image_attribute", + "name": "Update shared image attribute", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Simple form to input new image for pre-defined shared attribute key.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "
\n
\n
\n
\n
\n \n \n
\n \n
\n \n \n
\n
\n \n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", + "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n align-items: center;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.tb-image-preview-container div,\n.tb-flow-drop label {\n font-size: 16px !important;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", + "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet utils;\r\nlet translate;\r\n\r\nself.onInit = function() {\r\n self.ctx.ngZone.run(function() {\r\n init(); \r\n self.ctx.detectChanges(true);\r\n });\r\n};\r\n\r\nfunction init() {\r\n\r\n $scope = self.ctx.$scope;\r\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n $scope.toastTargetId = 'input-widget' + utils.guid();\r\n settings = utils.deepClone(self.ctx.settings) || {};\r\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\r\n settings.displayPreview = utils.defaultValue(settings.displayPreview, true);\r\n settings.displayClearButton = utils.defaultValue(settings.displayClearButton, false);\r\n settings.displayApplyButton = utils.defaultValue(settings.displayApplyButton, true);\r\n settings.displayDiscardButton = utils.defaultValue(settings.displayDiscardButton, true);\r\n $scope.settings = settings;\r\n $scope.isValidParameter = true;\r\n $scope.dataKeyDetected = false;\r\n $scope.entityDetected = false;\r\n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\r\n \r\n $scope.attributeUpdateFormGroup = $scope.fb.group(\r\n {currentValue: [undefined, []]}\r\n );\r\n \r\n $scope.attributeUpdateFormGroup.valueChanges.subscribe( () => {\r\n self.ctx.detectChanges();\r\n if (!settings.displayApplyButton) {\r\n $scope.updateAttribute();\r\n }\r\n });\r\n\r\n if (self.ctx.datasources && self.ctx.datasources.length) {\r\n var datasource = self.ctx.datasources[0];\r\n \r\n if (datasource.type === 'entity') {\r\n if (datasource.entityType === 'DEVICE') {\r\n if (datasource.entityType && datasource.entityId) {\r\n $scope.entityName = datasource.entityName;\r\n if (settings.widgetTitle && settings.widgetTitle.length) {\r\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n } else {\r\n $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n }\r\n \r\n $scope.entityDetected = true;\r\n }\r\n } else {\r\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\r\n }\r\n }\r\n if (datasource.dataKeys.length) {\r\n if (datasource.dataKeys[0].type !== \"attribute\") {\r\n $scope.isValidParameter = false;\r\n } else {\r\n $scope.currentKey = datasource.dataKeys[0].name;\r\n $scope.dataKeyType = datasource.dataKeys[0].type;\r\n $scope.dataKeyDetected = true;\r\n }\r\n }\r\n }\r\n\r\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n \r\n $scope.updateAttribute = function () {\r\n if ($scope.entityDetected) {\r\n var datasource = self.ctx.datasources[0];\r\n\r\n attributeService.saveEntityAttributes(\r\n datasource.entity.id,\r\n 'SHARED_SCOPE',\r\n [\r\n {\r\n key: $scope.currentKey,\r\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\r\n }\r\n ]\r\n ).subscribe(\r\n function success() {\r\n if (settings.displayApplyButton) {\r\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\r\n }\r\n if (settings.showResultMessage) {\r\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n },\r\n function fail() {\r\n if (settings.showResultMessage) {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n }\r\n );\r\n }\r\n };\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n try {\r\n if ($scope.dataKeyDetected) {\r\n var value = self.ctx.data[0].data[0][1];\r\n if (settings.displayApplyButton || !$scope.originalValue) {\r\n $scope.originalValue = value;\r\n }\r\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(value, {emitEvent: false});\r\n self.ctx.detectChanges();\r\n }\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n};\r\n\r\nself.onResize = function() {\r\n\r\n};\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1,\r\n maxDataKeys: 1,\r\n singleEntity: true\r\n };\r\n};\r\n\r\nself.onDestroy = function() {\r\n $scope.attributeUpdateFormGroup.valueChanges.unsubscribe();\r\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-update-image-attribute-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showResultMessage\":true,\"displayPreview\":true,\"displayClearButton\":false,\"displayApplyButton\":true,\"displayDiscardButton\":true},\"title\":\"Update shared image attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_shared_integer_attribute.json b/application/src/main/data/json/system/widget_types/update_shared_integer_attribute.json new file mode 100644 index 0000000000..c4dd5b2dba --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_shared_integer_attribute.json @@ -0,0 +1,21 @@ +{ + "fqn": "input_widgets.update_shared_integer_attribute", + "name": "Update shared integer attribute", + "deprecated": true, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAANBSURBVHja7d3dS1NhHAfw/Sum1IUQBZYJIdSF4VVQIOSNXZiJg5nmWi18CXG9WJgSqd1IBUWu10PY1EBcIFYqyoTCLOZWA8/0uGmbbjs73y7mS+Z2YdjYs76/i3Oe88C5+PB73jhwnkeHiOxyCh4uOQxdxO1XIXiofndEJ/uRBuGXdS41HSCqS+dEWoSTEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYSQfwl59xkA+teqDfLWl9wOESC9twAo5QsJIdHI6EstkvoQX4UP6LsNzNo/hgGDrI2oCI0B0XG7DADuSzcttaMC9JG2t0DTMEZqezuvAwY5UhbEvB5a213J9A0Awg0mnwid/cM1zBpCmPQgWrGwARlv1GDvBABbd88TESDhc3Ov7wM/H99oKp/dgEgXWlqaGgFAWYl6hBh+H9rqp4COV1EYYpAA5vWwdXi9XkWoeeRLjVkDGofxqdwDg4zqrxjTY+b8HDwOoSDaZQnAeNXFVuMEDDIGKs1temCwpsE8JeLMHg2u95qVGHBR4xKFEEIIIeT/gyxvKYgB0d5sXhAtN0uxgtS8LBJEsxrtm2sko/TbTRSI9tzY92ddj1FavYgD0axbHYBklOLnI2Uh8fIRy0kCR8pBgvdcALRn8R3bhMjdrdYlAFh62mpd/Uk81J8ciGKp/56gXW27aQ3uPVqac9AJTOUePpOfMw1gom5/VpJSsnC1zpU4H9vp7KEDlSqUQ3qg6GQI4cKzwIvMU6eTBYFiMSXOx3aG38mM9wCMBUDDAIAr+YDzB9qTBoFiie/4uwmxuGJ1+CgqAYBkQhDcwSVKX+YIANi6SvKmkw7ZwUXjzL5GAEDlsT2l8wJD5o+UrO1o4S0sFhcSOH4isP7wYFdAVEikpEABgGBWF4D27KigEK06u9/hcDgUlOUNLQ7lmkRtWksZsXiEhaqsjN3mgMij1lqEPcJsY8OPD4QQQgghhOws5E5SgxkhhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCElNSNocEJweRzb7ZF04PQ7RVnXpcay5il+KVmW7YpZ2rQAAAABJRU5ErkJggg==", + "description": "Simple form to input new integer value for pre-defined shared attribute key.\nThe widget is deprecated. Use \"Update Multiple Attributes\" widget. Attribute type and integer value type can be selected in widgets data key configuration.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", + "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", + "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false; \n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\n \n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [\n $scope.validators.min(settings.minValue),\n $scope.validators.max(settings.maxValue),\n $scope.validators.pattern(/^-?[0-9]+$/)\n ];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n\n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType === 'DEVICE') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n \n $scope.entityDetected = true;\n }\n } else {\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SHARED_SCOPE',\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue(correctValue($scope.originalValue));\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nfunction correctValue(value) {\n if (typeof value !== \"number\") {\n return 0;\n }\n return value;\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-update-integer-attribute-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update shared integer attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_shared_location_attribute.json b/application/src/main/data/json/system/widget_types/update_shared_location_attribute.json new file mode 100644 index 0000000000..fa053fed72 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_shared_location_attribute.json @@ -0,0 +1,21 @@ +{ + "fqn": "input_widgets.update_shared_location_attribute", + "name": "Update shared location attribute", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Simple form to input new location for pre-defined shared attribute key.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? latLabel : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n \n \n {{ settings.showLabel ? lngLabel : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n \n
\n
\n \n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-coordinate-specified' | translate }}\n
\n
\n
\n
", + "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex-direction: column;\n flex: 1;\n}\n\n.grid__element.horizontal-alignment {\n flex-direction: row;\n}\n\n.grid__element:last-child {\n align-items: center;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n margin: 0;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-button.getLocation {\n margin-right: 10px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.attribute-update-form mat-form-field{\n width: 100%;\n padding-right: 5px;\n}\n\n.attribute-update-form.small-width mat-form-field{\n width: 150px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", + "controllerScript": "let $scope;\r\nlet settings;\r\nlet attributeService;\r\nlet utils;\r\nlet translate;\r\n\r\nself.onInit = function() {\r\n self.ctx.ngZone.run(function() {\r\n init(); \r\n self.ctx.detectChanges(true);\r\n });\r\n};\r\n\r\n\r\nfunction init() {\r\n $scope = self.ctx.$scope;\r\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\r\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\r\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\r\n $scope.toastTargetId = 'input-widget' + utils.guid();\r\n settings = utils.deepClone(self.ctx.settings) || {};\r\n \r\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\r\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\r\n settings.showGetLocation = utils.defaultValue(settings.showGetLocation, true);\r\n settings.enableHighAccuracy = utils.defaultValue(settings.enableHighAccuracy, false);\r\n settings.isLatRequired = utils.defaultValue(settings.isLatRequired, true);\r\n settings.isLngRequired = utils.defaultValue(settings.isLngRequired, true);\r\n $scope.settings = settings;\r\n $scope.isValidParameter = true;\r\n $scope.dataKeyDetected = false; \r\n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\r\n\r\n $scope.isHorizontal = (settings.inputFieldsAlignment === 'row');\r\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-coordinate-required');\r\n $scope.latLabel = utils.customTranslation(settings.latLabel, settings.latLabel) || translate.instant('widgets.input-widgets.latitude');\r\n $scope.lngLabel = utils.customTranslation(settings.lngLabel, settings.lngLabel) || translate.instant('widgets.input-widgets.longitude');\r\n\r\n var validatorsLat = [$scope.validators.min(-90), $scope.validators.max(90)];\r\n var validatorsLng = [$scope.validators.min(-180), $scope.validators.max(180)];\r\n \r\n if (settings.isLatRequired) {\r\n validatorsLat.push($scope.validators.required);\r\n }\r\n \r\n if (settings.isLngRequired) {\r\n validatorsLng.push($scope.validators.required);\r\n }\r\n\r\n $scope.attributeUpdateFormGroup = $scope.fb.group({\r\n currentLat: [undefined, validatorsLat],\r\n currentLng: [undefined, validatorsLng],\r\n });\r\n\r\n if (self.ctx.datasources && self.ctx.datasources.length) {\r\n var datasource = self.ctx.datasources[0];\r\n if (datasource.type === 'entity') {\r\n if (datasource.entityType === 'DEVICE') {\r\n if (datasource.entityType && datasource.entityId) {\r\n $scope.entityName = datasource.entityName;\r\n if (settings.widgetTitle && settings.widgetTitle.length) {\r\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\r\n } else {\r\n $scope.titleTemplate = self.ctx.widgetConfig.title;\r\n }\r\n \r\n $scope.entityDetected = true;\r\n }\r\n } else {\r\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\r\n }\r\n }\r\n if (datasource.dataKeys.length > 1) {\r\n $scope.dataKeyDetected = true;\r\n for (let i = 0; i < datasource.dataKeys.length; i++) {\r\n if (datasource.dataKeys[i].type != \"attribute\"){\r\n $scope.isValidParameter = false;\r\n }\r\n if (datasource.dataKeys[i].name !== settings.latKeyName && datasource.dataKeys[i].name !== settings.lngKeyName){\r\n $scope.dataKeyDetected = false;\r\n }\r\n }\r\n }\r\n }\r\n\r\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\r\n\r\n $scope.updateAttribute = function () {\r\n $scope.isFocused = false;\r\n if ($scope.entityDetected) {\r\n var datasource = self.ctx.datasources[0];\r\n\r\n attributeService.saveEntityAttributes(\r\n datasource.entity.id,\r\n 'SHARED_SCOPE',\r\n [\r\n {\r\n key: settings.latKeyName,\r\n value: $scope.attributeUpdateFormGroup.get('currentLat').value\r\n },{\r\n key: settings.lngKeyName,\r\n value: $scope.attributeUpdateFormGroup.get('currentLng').value\r\n }\r\n ]\r\n ).subscribe(\r\n function success() {\r\n $scope.originalLat = $scope.attributeUpdateFormGroup.get('currentLat').value;\r\n $scope.originalLng = $scope.attributeUpdateFormGroup.get('currentLng').value;\r\n if (settings.showResultMessage) {\r\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n },\r\n function fail() {\r\n if (settings.showResultMessage) {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n }\r\n );\r\n }\r\n };\r\n\r\n $scope.changeFocus = function () {\r\n if ($scope.attributeUpdateFormGroup.get('currentLat').value === $scope.originalLat && $scope.attributeUpdateFormGroup.get('currentLng').value === $scope.originalLng) {\r\n $scope.isFocused = false;\r\n }\r\n };\r\n \r\n $scope.discardChange = function() {\r\n $scope.attributeUpdateFormGroup.setValue({\r\n 'currentLat': $scope.originalLat,\r\n 'currentLng': $scope.originalLng\r\n });\r\n $scope.isFocused = false;\r\n $scope.attributeUpdateFormGroup.markAsPristine();\r\n self.onDataUpdated();\r\n };\r\n \r\n $scope.disableButton = function () {\r\n return $scope.attributeUpdateFormGroup.get('currentLat').value === $scope.originalLat && $scope.attributeUpdateFormGroup.get('currentLng').value === $scope.originalLng || $scope.currentLng === null || $scope.currentLat === null;\r\n };\r\n \r\n $scope.getCoordinate = function() {\r\n if (navigator.geolocation) {\r\n navigator.geolocation.getCurrentPosition(showPosition, function (){\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.blocked-location'), \r\n 'bottom', 'left', $scope.toastTargetId);\r\n }, {\r\n enableHighAccuracy: settings.enableHighAccuracy\r\n });\r\n } else {\r\n $scope.showErrorToast(translate.instant('widgets.input-widgets.no-support-geolocation'), 'bottom', 'left', $scope.toastTargetId);\r\n }\r\n };\r\n \r\n function showPosition(position) {\r\n $scope.attributeUpdateFormGroup.setValue({\r\n currentLat: correctValue(position.coords.latitude),\r\n currentLng: correctValue(position.coords.longitude)\r\n });\r\n $scope.attributeUpdateFormGroup.markAsDirty();\r\n $scope.isFocused = true;\r\n }\r\n \r\n self.onResize();\r\n}\r\n\r\nself.onDataUpdated = function() {\r\n try {\r\n if ($scope.dataKeyDetected) {\r\n if (!$scope.isFocused) {\r\n for(let i = 0; i < self.typeParameters().maxDataKeys; i++){\r\n if(self.ctx.data[i].dataKey.name === self.ctx.settings.latKeyName && $scope.attributeUpdateFormGroup.get('currentLat').pristine){\r\n $scope.originalLat = self.ctx.data[i].data[0][1];\r\n $scope.attributeUpdateFormGroup.get('currentLat').patchValue(correctValue($scope.originalLat));\r\n } else if(self.ctx.data[i].dataKey.name === self.ctx.settings.lngKeyName && $scope.attributeUpdateFormGroup.get('currentLng').pristine){\r\n $scope.originalLng = self.ctx.data[i].data[0][1];\r\n $scope.attributeUpdateFormGroup.get('currentLng').patchValue(correctValue($scope.originalLng));\r\n }\r\n }\r\n self.ctx.detectChanges();\r\n }\r\n }\r\n } catch (e) {\r\n console.log(e);\r\n }\r\n};\r\n\r\nfunction correctValue(value) {\r\n if (typeof value !== \"number\") {\r\n return 0;\r\n }\r\n return value;\r\n}\r\n\r\nself.onResize = function() {\r\n $scope.smallWidthContainer = (self.ctx.$container && self.ctx.$container[0].offsetWidth < 320);\r\n $scope.changeAlignment = ($scope.isHorizontal && self.ctx.$container && self.ctx.$container[0].offsetWidth < 480);\r\n self.ctx.detectChanges();\r\n};\r\n\r\nself.typeParameters = function() {\r\n return {\r\n maxDatasources: 1,\r\n maxDataKeys: 2,\r\n singleEntity: true\r\n };\r\n};\r\n\r\nself.onDestroy = function() {\r\n\r\n};", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-update-location-attribute-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"widgetTitle\":\"\",\"showResultMessage\":true,\"latKeyName\":\"latitude\",\"lngKeyName\":\"longitude\",\"showGetLocation\":true,\"enableHighAccuracy\":false,\"showLabel\":true,\"latLabel\":\"\",\"lngLabel\":\"\",\"inputFieldsAlignment\":\"column\",\"isLatRequired\":true,\"isLngRequired\":true,\"requiredErrorMessage\":\"\"},\"title\":\"Update shared location attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_shared_string_attribute.json b/application/src/main/data/json/system/widget_types/update_shared_string_attribute.json new file mode 100644 index 0000000000..4c5333ec59 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_shared_string_attribute.json @@ -0,0 +1,21 @@ +{ + "fqn": "input_widgets.update_shared_string_attribute", + "name": "Update shared string attribute", + "deprecated": true, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAOkSURBVHja7d3bSxRRHAfw/VdG6yklCbPsRk+GhVLYDSXMohC1tczVUkxFXDULzW4a+SA9VIZRm5haIJqBmMqW2cUoddUVR1sved2dnV/nuJaX3X0Q1pjZvj/YM4fDmYfP/uZcdvbhaMgmmnpVHibRShpb/4REKg9pot+mESfIC2JC1Jgkb4BIJk0veUX0AgIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAsp6QN194+epPs1Z0vqm/Uw2QuuussMSOuYXYbe3PZJvyIeNx40T1xUTDTe+sHCK3STTfwQDGpgVU/+Vr+ox2FYyRktdEuS3UllFXVsAhtrMz9DOe5JI7htQfvIM1K3VcDYO9NZ+GtfP00Uz2uLEliDFHpqYy3qG2suaxGiDW86PVFURTD6/mxg4vQQwpRUW5ObyDZc5uVsX0+6A2s5uo9LmdtA7INIfUlo6MjFhUtY58S06TiXJa6HOsmc9aSd+pI576Lo6SuVNVEDndwErjhUs3dO85pCExrSSeqDE5K61bjSu7febvqJlzACdlbFEAAQQQQP4/yKxTRR0Q+eXKDdFsocFRMRTOqgkiP9E1rWwx6AzLLmqByE919avbajihxqVDsRCWj3rnVpYM1/lQLMRVPhw5ceNQHGTmnok7qlw7PAuJOrWOEIs+c8DNc+XxR2tdITSWd8XkPh8eHezrC2E5SXWfjzVNvxUdRn0Bf29V3sZvq2GVD82FuUYy5ukbFiBdN7MbeU+pOvvuV96xoyrT4kGJa8eaF8Sg/cHaEN9WIr/ixe9/8+7QxD0bsgITDgqPWMu2refChXIi28mApIiNzOYX7B826MEB76EtStC+SbLuSFkOiZZoaos/ewcUdYh9AodJ1gX8ovubekhO2Ms6hk8pcdMYlM+KmBPLIUWscoy1UF7Q4hjpEpopTMsq9cKYo6NCIacjV0MiY1hRELgImRQqKUBYiC5VQ4aEatqZ2MljWvGQYP6C9LAz5CirvBA+UUyYndXmSPGQ2GDjwG0fZ4hQPPh21xH2l5BP+oCoD7UrHtITIvgej3CGnIkWhAN9fELfLggh7YrNyLIQp92sWUOOq31QxG92QAABBBBAvBNy658GMgIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAokyI1xwQ7B1HNo+LGqt3HKItabzjWHOJfgM5tA7UOw6xeQAAAABJRU5ErkJggg==", + "description": "Simple form to input new string value for pre-defined shared attribute key.\nThe widget is deprecated. Use \"Update Multiple Attributes\" widget. Attribute type and string value type can be selected in widgets data key configuration.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", + "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", + "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false; \n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\n \n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [$scope.validators.minLength(settings.minLength),\n $scope.validators.maxLength(settings.maxLength)];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n \n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType === 'DEVICE') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n \n $scope.entityDetected = true;\n }\n } else {\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n var value = $scope.attributeUpdateFormGroup.get('currentValue').value;\n \n if (!$scope.attributeUpdateFormGroup.get('currentValue').value.length) {\n value = null;\n }\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SHARED_SCOPE',\n [\n {\n key: $scope.currentKey,\n value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-update-string-attribute-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.23592248334107624,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update shared string attribute\",\"dropShadow\":true,\"enableFullscreen\":false,\"enableDataExport\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/update_string_timeseries.json b/application/src/main/data/json/system/widget_types/update_string_timeseries.json new file mode 100644 index 0000000000..43a96d6f69 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/update_string_timeseries.json @@ -0,0 +1,21 @@ +{ + "fqn": "input_widgets.update_string_timeseries", + "name": "Update string timeseries", + "deprecated": true, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAOkSURBVHja7d3bSxRRHAfw/VdG6yklCbPsRk+GhVLYDSXMohC1tczVUkxFXDULzW4a+SA9VIZRm5haIJqBmMqW2cUoddUVR1sved2dnV/nuJaX3X0Q1pjZvj/YM4fDmYfP/uZcdvbhaMgmmnpVHibRShpb/4REKg9pot+mESfIC2JC1Jgkb4BIJk0veUX0AgIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAsp6QN194+epPs1Z0vqm/Uw2QuuussMSOuYXYbe3PZJvyIeNx40T1xUTDTe+sHCK3STTfwQDGpgVU/+Vr+ox2FYyRktdEuS3UllFXVsAhtrMz9DOe5JI7htQfvIM1K3VcDYO9NZ+GtfP00Uz2uLEliDFHpqYy3qG2suaxGiDW86PVFURTD6/mxg4vQQwpRUW5ObyDZc5uVsX0+6A2s5uo9LmdtA7INIfUlo6MjFhUtY58S06TiXJa6HOsmc9aSd+pI576Lo6SuVNVEDndwErjhUs3dO85pCExrSSeqDE5K61bjSu7febvqJlzACdlbFEAAQQQQP4/yKxTRR0Q+eXKDdFsocFRMRTOqgkiP9E1rWwx6AzLLmqByE919avbajihxqVDsRCWj3rnVpYM1/lQLMRVPhw5ceNQHGTmnok7qlw7PAuJOrWOEIs+c8DNc+XxR2tdITSWd8XkPh8eHezrC2E5SXWfjzVNvxUdRn0Bf29V3sZvq2GVD82FuUYy5ukbFiBdN7MbeU+pOvvuV96xoyrT4kGJa8eaF8Sg/cHaEN9WIr/ixe9/8+7QxD0bsgITDgqPWMu2refChXIi28mApIiNzOYX7B826MEB76EtStC+SbLuSFkOiZZoaos/ewcUdYh9AodJ1gX8ovubekhO2Ms6hk8pcdMYlM+KmBPLIUWscoy1UF7Q4hjpEpopTMsq9cKYo6NCIacjV0MiY1hRELgImRQqKUBYiC5VQ4aEatqZ2MljWvGQYP6C9LAz5CirvBA+UUyYndXmSPGQ2GDjwG0fZ4hQPPh21xH2l5BP+oCoD7UrHtITIvgej3CGnIkWhAN9fELfLggh7YrNyLIQp92sWUOOq31QxG92QAABBBBAvBNy658GMgIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAokyI1xwQ7B1HNo+LGqt3HKItabzjWHOJfgM5tA7UOw6xeQAAAABJRU5ErkJggg==", + "description": "Simple form to input new string value for pre-defined timeseries key.\nThe widget is deprecated. Use \"Update Multiple Attributes\" widget. Timeseries key type and string value type can be selected in widgets data key configuration.", + "descriptor": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "resources": [], + "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-timeseries-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.attribute-not-allowed' | translate }}\n
\n
\n
\n
", + "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", + "controllerScript": "let $scope;\nlet settings;\nlet utils;\nlet translate;\nlet http;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n http = $scope.$injector.get(self.ctx.servicesMap.get('http'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-timeseries-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n\n $scope.attributeUpdateFormGroup = $scope.fb.group(\n {currentValue: [undefined, [$scope.validators.required,\n $scope.validators.minLength(settings.minLength),\n $scope.validators.maxLength(settings.maxLength)]]}\n );\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"timeseries\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n let observable = saveEntityTimeseries(\n datasource.entityType,\n datasource.entityId,\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n );\n if (observable) {\n observable.subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n\n function saveEntityTimeseries(entityType, entityId, telemetries) {\n var telemetriesData = {};\n for (var a = 0; a < telemetries.length; a++) {\n if (typeof telemetries[a].value !== 'undefined' && telemetries[a].value !== null) {\n telemetriesData[telemetries[a].key] = telemetries[a].value;\n }\n }\n if (Object.keys(telemetriesData).length) {\n var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/scope';\n return http.post(url, telemetriesData);\n }\n return null;\n }\n}\n\nself.onDataUpdated = function() {\n\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}\n", + "settingsSchema": "", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-update-string-attribute-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Update string timeseries\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/usage_info.json b/application/src/main/data/json/system/widget_types/usage_info.json new file mode 100644 index 0000000000..1f8f969a47 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/usage_info.json @@ -0,0 +1,24 @@ +{ + "fqn": "home_page_widgets.usage_info", + "name": "Usage info", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Display statistics about the number of entities and API usage.", + "descriptor": { + "type": "static", + "sizeX": 7.5, + "sizeY": 6.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n}", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "", + "defaultConfig": "{\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Usage info\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showLegend\":false}" + }, + "externalId": null, + "tags": [ + "limits" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/uv_index_card.json b/application/src/main/data/json/system/widget_types/uv_index_card.json new file mode 100644 index 0000000000..0032d17280 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/uv_index_card.json @@ -0,0 +1,30 @@ +{ + "fqn": "uv_index_card", + "name": "UV Index card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAnFBMVEXg4ODf39/g4ODg4OAAAAD////g4OCAwywhISGqqqr5+fm/4ZXj4+Oenp7P6a/Ozs62trbz8/PCwsJ0dHSkpKRYWFg9PT3n5+fv9+Xx8fH3+/LIyMisrKyQy0ag0mDu7u7f8Mra2trV1dWwsLCv2XsvLy+8vLyQkJCIxznn9NiYzlOCgoJKSkrH5aOrq6tmZmbX7L3H5aK43Yio1m6jslkrAAAABXRSTlPvIL+vAC9A4IoAAARXSURBVHja7M9JEQAwCAPAUA4PvPDvsjYCk3WwCHuo7dwChsn1GgbvPGAeKk/AlUgpQkYRNoqwUYSNImwUYaMIG0XYfGbraMVRGAyg8NUhPzTKQogYHaUUs9rqtLPM+7/bpo6zinVKb1ZyQDExNx8RyQuQvKsKou8FSKrUkej7z5C3Kz8UEUTLG5AZJz1wGu+YjGXW8qSdIIcDY0fV5dzLm3QLgvVALzpSyFGpZnoqvhyVUsUWJJEShoEJ4szvm23BtdYMAVLerHG0LZiEud0gRaXUgUUXpS5sQZy0YXD9hmjJkluwGd8asWFoksxSStmKZm43CHk1bUlRTJBLsQnBZiSiZ8jbeImBwd5nwlvHLfMti/aDkH8UcG46paomD8O0YBvyS/RgWUHG19Zi5Z7GiWfZLpC5d/VVdWadlgSQHpw3cl1DnCQj5DboELQ+TCzaF5Kq77qcdTY76X4U9N67NQQ76DKzXCWhTMJM23vH3K6QvFL/qlmnBxGfECrF8AA5ZeKthfa+KqhxvmduV0iqFh15yGmepd20yvHYLpDzZ13XZ/hcQv7Aex2K8wS5DWlUqIF6CTnARYVSYuyHHblUVXUOoPWOhPk6J8YC5IWfbyj2s/xzSNHNjoqou0Ne25I4v6hXIRwmRvdB3P1lzwxWG4ZhMHxShgzGkhGkNFATu5htp63v/3CTGxGWlhV6yJpDfoJi2X+IvhhD4jwCObZVcTg1jMvB0iflHPwptrH/ePu9TI2Dvf2eF+OJboru4U4h2lj65cvTLWlcH8Q4lpWfbvLe20O30OMC6xpCnMZGsi61CYaWGIgZrbnSF+Ln0WZkJjvegQTEAB1SZfFV8xj0YCkyAAtihKFgTqIpIfYAkwWCFFSQdiUBowo6DW4FEDifp7K/3t4N6PQN9yCcBOoA2fUIM4h3LKy9Q3QNE0ZqR0cziDKVBjImkJRr82AHFF6wi2IgsVYPiXCABUgrlUYIUQ10BQmCOIM4b2skY5Q+hnaxZ7W/DISVwkNOLGkGqdcZ8VyVKSaB/gqSSd3zjIjNCHbazKSe1hWH14AgosNSvMtYCJy0MjpfREFIk4QFo0MiUW9wFSXPIBlJxmmJSaeeorBYiNcE+Xi898stOG7BgUX2YB2zwVlqssTOiTM15+Z24xXkGbmCJe2/FXaQbWoH2Zp2kK1pB9madpAf9udYAAAAAGCQv/U0dpRBNyI3IjciNyI3IjciNyI3IjciNyK1d/c4CMNAEIWdH2cLS9TEElZKUkRCcP+7YTpSpptdvVdM/51grrbt+ZxLyL08rORz/iD1aGbmHrK1p5l7yP7+KQJAilkkSPtEgLSj5pt7yKvUvgEgPSBA/gICBAgQIECA9IAAAQIECBAgTgOiFhC1gKgFRC0gagFRC4haQNQCohYQtYCoBUQtIGoBUQuIWkDUAqIWELWAqAVELSBqAVFrSSmHaEljiMvmdUpDCiBZO2MepsV7aRzmL/oPrqtS4mUjAAAAAElFTkSuQmCC", + "description": "Displays the latest UV index telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'uv', label: 'UV Index', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"UV Index\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.ceil(Math.random() * 4 - 2);\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 14) {\\n\\tvalue = 14;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"light_mode\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#80C32C\"},{\"from\":2,\"to\":5,\"color\":\"#FFA600\"},{\"from\":5,\"to\":7,\"color\":\"#F36900\"},{\"from\":7,\"to\":10,\"color\":\"#F04022\"},{\"from\":10,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#80C32C\"},{\"from\":2,\"to\":5,\"color\":\"#FFA600\"},{\"from\":5,\"to\":7,\"color\":\"#F36900\"},{\"from\":7,\"to\":10,\"color\":\"#F04022\"},{\"from\":10,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"UV Index card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "uv", + "ultraviolet", + "sunburn" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/uv_index_card_with_background.json b/application/src/main/data/json/system/widget_types/uv_index_card_with_background.json new file mode 100644 index 0000000000..5bef7a1909 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/uv_index_card_with_background.json @@ -0,0 +1,30 @@ +{ + "fqn": "uv_index_card_with_background", + "name": "UV Index card with background", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC+lBMVEW6o3DUs2IAAADs0ZTfvGTnz5Pvz4/20o+4qHP/9+D/9d7/8tL/9Nr/9Nf/9NT/9eH/+OP/+ef/993/883/8sj/88v/9tr/88//8dD/+uv/8sP/9Nz/8cz//fj/9dHz6tT37NP///7/++7/+/F8wyL169T//PP//PX/88X+8dXv6djx6tb//vr/9tf27db/1n/99ePz7Nj879D57tb57tH91ITz6ND88NPu48v679P789zv59P+2n7k28j99d7/1YPz58z61If67c38xU3r4s/n2sP/6q//3ID36sv778Pw5c7/1Xbi2sv98tjs5NLw0pD/2X3j2MPr4Mv68Njt59b/x0v90XL/1Ybh2s3x6tj8783/3oXrwWD/yVT26tDe2Mz/7rzd1sj88Nf504r/02r+0Gj00o3/2IHx5cb/zF3n38357tPl3cz/5a//4Y7903//1HD+zmT/1nv80nvg2cj06Mbv4MP/5qP/5Zzb1s31yWn2x2Dt0ZL10orp38nm3Mj47MT/0WD+zVXa1cn/45Xs3sP+0G3/7LWno5n/34n/yE/p3MTu4sjp0JXm4NGynmDxw17/yET/6ajxxWbCqGL+3I/3xlusqaH304z80Xbe1cP5zW3lvV8hIB/twFr/0FngsUJcPwPs4Mb/3pj+xj36y2barkzitlH+zU35w0z61IzTqEbbqjn9+e+4tKratFbLnj3+xjP65La3oV/878jctmCtml7Rrli7pGHHqlnhvGjpwGa+pFzDv7TLrWDSpDTJo0xAPzz40oC923Tyw1Tqt0XmsTSemo7zznx0cmyPyj3wuzrBmUDNyLvVtWhYV1XirCj91orXoiLO4Yjv78dMOAyTXwLz7d/k496e0FLTu30pHwX59erJrWr9wyTMy8XDkyaHVwDf6a+2kEX/zD4+Lgau1mnQwpovLi2tgyrutSWEZCDP45iQj42JdkPy8e3F34q93HrVz76gj2KceC6hbAfo7LuCf3j/2XZqZVtvVyXa5qPZyJifg0jRlAKlAAAACXRSTlP7IAC1tSAgv7+sbVSmAAAhlklEQVR42qzUP0tbURzG8Yui5cbEaCNNjLZQBbsEbpdsl16yGDIZ6JDBJRCIkw24tIPZhODQxSl7NsfgG8k76nN+589z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", + "description": "Displays the latest UV index telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'uv', label: 'UV Index', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"UV Index\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.ceil(Math.random() * 4 - 2);\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 14) {\\n\\tvalue = 14;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"light_mode\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#7CC322\"},{\"from\":2,\"to\":5,\"color\":\"#F89E0D\"},{\"from\":5,\"to\":7,\"color\":\"#F77410\"},{\"from\":7,\"to\":10,\"color\":\"#F04022\"},{\"from\":10,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#7CC322\"},{\"from\":2,\"to\":5,\"color\":\"#F89E0D\"},{\"from\":5,\"to\":7,\"color\":\"#F77410\"},{\"from\":7,\"to\":10,\"color\":\"#F04022\"},{\"from\":10,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"UV Index card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":null,\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "uv", + "ultraviolet", + "sunburn" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/uv_index_chart_card.json b/application/src/main/data/json/system/widget_types/uv_index_chart_card.json new file mode 100644 index 0000000000..a8def5a36f --- /dev/null +++ b/application/src/main/data/json/system/widget_types/uv_index_chart_card.json @@ -0,0 +1,33 @@ +{ + "fqn": "uv_index_chart_card", + "name": "UV Index chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a UV index data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'uv', label: 'UV Index', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: '', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'uv', '', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"UV Index\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.ceil(Math.random() * 4 - 2);\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 14) {\\n\\tvalue = 14;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#80C32C\"},{\"from\":2,\"to\":5,\"color\":\"#FFA600\"},{\"from\":5,\"to\":7,\"color\":\"#F36900\"},{\"from\":7,\"to\":10,\"color\":\"#F04022\"},{\"from\":10,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.ceil(Math.random() * 4 - 2);\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 14) {\\n\\tvalue = 14;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = Math.ceil(prevValue + Math.random() * 2 - 1);\\nif (value < -2) {\\n\\tvalue = -2;\\n} else if (value > 2) {\\n\\tvalue = 2;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"UV Index\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"light_mode\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "uv", + "ultraviolet", + "sunburn" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/uv_index_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/uv_index_chart_card_with_background.json new file mode 100644 index 0000000000..121e6f31a9 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/uv_index_chart_card_with_background.json @@ -0,0 +1,33 @@ +{ + "fqn": "uv_index_chart_card_with_background", + "name": "UV Index chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a UV index data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'uv', label: 'UV Index', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: '', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'uv', '', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"UV Index\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.ceil(Math.random() * 4 - 2);\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 14) {\\n\\tvalue = 14;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":2,\"color\":\"#7CC322\"},{\"from\":2,\"to\":5,\"color\":\"#F89E0D\"},{\"from\":5,\"to\":7,\"color\":\"#F77410\"},{\"from\":7,\"to\":10,\"color\":\"#F04022\"},{\"from\":10,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.ceil(Math.random() * 4 - 2);\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 14) {\\n\\tvalue = 14;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = Math.ceil(prevValue + Math.random() * 2 - 1);\\nif (value < -2) {\\n\\tvalue = -2;\\n} else if (value > 2) {\\n\\tvalue = 2;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"UV Index\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"light_mode\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "uv", + "ultraviolet", + "sunburn" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/value_and_chart_card.json b/application/src/main/data/json/system/widget_types/value_and_chart_card.json new file mode 100644 index 0000000000..93300135bd --- /dev/null +++ b/application/src/main/data/json/system/widget_types/value_and_chart_card.json @@ -0,0 +1,27 @@ +{ + "fqn": "cards.aggregated_value_card", + "name": "Value and chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a single entity telemetry as a combination of the latest and aggregated values. Optionally may display the corresponding historical values as a simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'watermeter', label: 'Watermeter', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'm³', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'watermeter', 'm³', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"watermeter\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 80) {\\n\\tvalue = 80;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 80) {\\n\\tvalue = 80;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m³\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m³\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Value and chart card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"water_drop\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/value_card.json b/application/src/main/data/json/system/widget_types/value_card.json new file mode 100644 index 0000000000..9abfb9a2f8 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/value_card.json @@ -0,0 +1,24 @@ +{ + "fqn": "cards.value_card", + "name": "Value card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a single entity attribute or the latest telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"thermostat\",\"iconColor\":{\"type\":\"constant\",\"color\":\"#5469FF\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":52,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"valueColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Value card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"°C\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/vertical_bar.json b/application/src/main/data/json/system/widget_types/vertical_bar.json new file mode 100644 index 0000000000..31d3b3fdd9 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/vertical_bar.json @@ -0,0 +1,21 @@ +{ + "fqn": "digital_gauges.vertical_bar_justgage", + "name": "Vertical bar", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAABQVBMVEVqampra2tsbGxtbW1ubm5vb29wcHBycnJzc3N0dHR2dnZ3d3d5eXl6enp7e3t8fHx+fn6BgYGDg4OFhYWHh4eIiIiKioqNjY2Ojo6RkZGTk5OWlpaZmZmbm5uenp6fn5+goKCkpKSlpaWmpqanp6eoqKiqqqqrq6usrKytra2xsbGysrK0tLS1tbW2tra4uLi6urq7u7u9vb2+vr7AwMDBwcHExMTFxcXHx8fIyMjJycnKysrMzMzNzc3Ozs7Pz8/Q0NDR0dHS0tLT09PU1NTV1dXX19fY2NjZ2dnb29vc3Nzd3d3f39/g4ODh4eHj4+Pk5OTl5eXm5ubn5+fo6Ojp6enq6urr6+vs7Ozt7e3u7u7v7+/w8PDx8fHy8vLz8/P09PT1fAD19fX29vb39/f6+vr7+/v8/Pz9/f3+/v7///8uFkw3AAAAAWJLR0RqJWKVDgAAAkBJREFUeNrt3ElTE3EQhvGebKMIiUExGkSNuCEiKAqKRKJsIuISN0giwQFC5v3+H8BD5uDBE2WV9tTTp1x/lTyd/6lNKRkDAgQIECBA/jtIHEXx4FOv3fMMWTSLJOn9eN4+OoZ0zg4gjZxduvXDMWQim7FIaoe5hutGXtn90CJp2mZcx340cv44tEgas9kLufKyW8ht21BokVSwTPWKBdtOIdvBDSm0SH2zFWnaxn1CeqPB03o9b88+9C2IpV0r+YTsWDJ3NWQdqWNDPiGH9Xp98I3ous1Ji1b1/NYKLZI+FzKTd3LBG/cQbZXNii9T8frttGKe8UCAAJGaS5J6j2/O96TuzOTz2CfkxZhVJF0tTY1MqF++eK/w0Cdkar5akdrBa60EnTXb04NzXn9a1yrSln1V094+yUvLduQYsmp7+m5rs2ekhrUdQ9ZtV99sYy6UGrbvGPLJdvTOmkvBsRay8T+CtE45v0NOirVurdRvZR/tX67KMUSbwza8KS2ENvrFKySZA0lSfMgTBUjqIH+pESBAiB0IjQBhawEBQuxAaAQIWwsIEGIHQiNA2FpAgBA7EBoBwtYCAoTYgdAIELYWECDEDoRGgKQK8vOUAwQIsQOhESBsLWIHQuxAaAQIW4vYgRA7EBoBwtYidiDEDoRGgLC1iB0IsQOhESBsrXTHnlws9g8ZXCz230hysdg/JLlY7B+SXCz230hysdg/JLlY7B9yUqx1a8V+CtZvcrE4Df/sBzxRgAABAgQIECB/mF+DKtRemhqPQgAAAABJRU5ErkJggg==", + "description": "Preconfigured gauge to display any value reading as a bar. Allows to configure value range, gradient colors, and other settings.", + "descriptor": { + "type": "latest", + "sizeX": 2, + "sizeY": 3.5, + "resources": [], + "templateHtml": "", + "templateCss": "#gauge {\n text-align: center;\n /* margin-left: -100px;\n margin-right: -100px;*/\n /*margin-top: -50px;*/\n \n}\n", + "controllerScript": "self.onInit = function() {\n self.ctx.gauge = new TbCanvasDigitalGauge(self.ctx, 'digitalGauge'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.gauge.update();\n}\n\nself.onResize = function() {\n self.ctx.gauge.resize();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n };\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.gauge.mobileModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.gauge.destroy();\n}\n\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-digital-gauge-widget-settings", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temp\",\"color\":\"#f57c00\",\"settings\":{},\"_hash\":0.7282710489093589,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"maxValue\":100,\"minValue\":0,\"donutStartAngle\":90,\"showValue\":true,\"showMinMax\":true,\"gaugeWidthScale\":0.75,\"levelColors\":[],\"refreshAnimationType\":\">\",\"refreshAnimationTime\":700,\"startAnimationType\":\">\",\"startAnimationTime\":700,\"titleFont\":{\"family\":\"Roboto\",\"size\":12,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#999999\"},\"labelFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\"},\"valueFont\":{\"family\":\"Roboto\",\"style\":\"normal\",\"weight\":\"500\",\"size\":12,\"color\":\"#666666\"},\"minMaxFont\":{\"family\":\"Roboto\",\"size\":8,\"style\":\"normal\",\"weight\":\"500\",\"color\":\"#666666\"},\"neonGlowBrightness\":0,\"decimals\":0,\"dashThickness\":1.5,\"gaugeColor\":\"#eeeeee\",\"showTitle\":false,\"gaugeType\":\"verticalBar\"},\"title\":\"Vertical bar\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}" + }, + "externalId": null +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/vertical_capsule_tank.json b/application/src/main/data/json/system/widget_types/vertical_capsule_tank.json new file mode 100644 index 0000000000..39abea9b47 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/vertical_capsule_tank.json @@ -0,0 +1,36 @@ +{ + "fqn": "vertical_capsule_tank", + "name": "Vertical capsule tank", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Widget indicates the level of liquid in Vertical capsule tank.", + "descriptor": { + "type": "latest", + "sizeX": 4, + "sizeY": 4, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.liquidLevelWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.liquidLevelWidget.update();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n}\n\nself.actionSources = function() { \n return { \n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false \n } \n };\n}", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-liquid-level-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-liquid-level-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.floor(Math.random() * 101);\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"tankSelectionType\":\"static\",\"selectedShape\":\"Vertical Capsule\",\"shapeAttributeName\":\"tankShape\",\"tankColor\":{\"type\":\"range\",\"color\":\"#242770\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E73535DE\"},{\"from\":20,\"to\":null,\"color\":\"#242770\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E73535DE';\\n }\\n}\\nreturn '#242770';\"},\"datasourceUnits\":\"%\",\"layout\":\"percentage\",\"volumeSource\":\"static\",\"volumeConstant\":500,\"volumeAttributeName\":\"volume\",\"volumeUnits\":\"L\",\"volumeFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"volumeColor\":\"rgba(0, 0, 0, 0.18)\",\"units\":\"%\",\"widgetUnitsSource\":\"static\",\"widgetUnitsAttributeName\":\"units\",\"liquidColor\":{\"type\":\"range\",\"color\":\"#7A8BFF\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E27C7CDE\"},{\"from\":20,\"to\":null,\"color\":\"#7A8BFF\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FF0000DE\"},{\"from\":20,\"to\":null,\"color\":\"rgba(0,0,0,0.87)\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FF0000DE';\\n }\\n}\\nreturn '#000000DE';\"},\"showBackgroundOverlay\":true,\"backgroundOverlayColor\":{\"type\":\"range\",\"color\":\"#FFFFFFC2\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FFEFEFDE\"},{\"from\":20,\"to\":null,\"color\":\"#FFFFFFC2\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FFEFEFDE';\\n }\\n}\\nreturn '#FFFFFFC2';\"},\"showTooltip\":true,\"showTooltipLevel\":true,\"tooltipUnits\":\"%\",\"tooltipLevelDecimals\":0,\"tooltipLevelFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipLevelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.76)\",\"rangeList\":[],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"showTooltipDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":3,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Liquid level\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"titleFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1.5\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"showTitleIcon\":false,\"titleIcon\":\"water_drop\",\"iconColor\":\"#5469FF\",\"decimals\":0,\"enableDataExport\":false,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" + }, + "externalId": null, + "tags": [ + "reservoir", + "container", + "vessel", + "storage unit", + "cistern", + "canister", + "vat", + "basin", + "repository", + "bin", + "hopper" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/vertical_cylinder_tank.json b/application/src/main/data/json/system/widget_types/vertical_cylinder_tank.json new file mode 100644 index 0000000000..9bd2a149cf --- /dev/null +++ b/application/src/main/data/json/system/widget_types/vertical_cylinder_tank.json @@ -0,0 +1,36 @@ +{ + "fqn": "vertical_cylinder_tank", + "name": "Vertical cylinder tank", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Widget indicates the level of liquid in Vertical cylinder tank.", + "descriptor": { + "type": "latest", + "sizeX": 4, + "sizeY": 4, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.liquidLevelWidget.onInit();\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.liquidLevelWidget.update();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n}\n\nself.actionSources = function() { \n return { \n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false \n } \n };\n}", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-liquid-level-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-liquid-level-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.floor(Math.random() * 101);\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"tankSelectionType\":\"static\",\"selectedShape\":\"Vertical Cylinder\",\"shapeAttributeName\":\"tankShape\",\"tankColor\":{\"type\":\"range\",\"color\":\"#242770\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E73535DE\"},{\"from\":20,\"to\":null,\"color\":\"#242770\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E73535DE';\\n }\\n}\\nreturn '#242770';\"},\"datasourceUnits\":\"%\",\"layout\":\"percentage\",\"volumeSource\":\"static\",\"volumeConstant\":500,\"volumeAttributeName\":\"volume\",\"volumeUnits\":\"L\",\"volumeFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"volumeColor\":\"rgba(0, 0, 0, 0.18)\",\"units\":\"%\",\"widgetUnitsSource\":\"static\",\"widgetUnitsAttributeName\":\"units\",\"liquidColor\":{\"type\":\"range\",\"color\":\"#7A8BFF\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E27C7CDE\"},{\"from\":20,\"to\":null,\"color\":\"#7A8BFF\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FF0000DE\"},{\"from\":20,\"to\":null,\"color\":\"rgba(0,0,0,0.87)\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FF0000DE';\\n }\\n}\\nreturn '#000000DE';\"},\"showBackgroundOverlay\":true,\"backgroundOverlayColor\":{\"type\":\"range\",\"color\":\"rgba(255, 255, 255, 0.76)\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FFEFEFDE\"},{\"from\":20,\"to\":null,\"color\":\"#FFFFFFC2\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FFEFEFDE';\\n }\\n}\\nreturn '#FFFFFFC2';\"},\"showTooltip\":true,\"showTooltipLevel\":true,\"tooltipUnits\":\"%\",\"tooltipLevelDecimals\":0,\"tooltipLevelFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipLevelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.76)\",\"rangeList\":[],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"showTooltipDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":3,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Liquid level\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"titleFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1.5\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"showTitleIcon\":false,\"titleIcon\":\"water_drop\",\"iconColor\":\"#5469FF\",\"decimals\":0,\"enableDataExport\":false,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" + }, + "externalId": null, + "tags": [ + "reservoir", + "container", + "vessel", + "storage unit", + "cistern", + "canister", + "vat", + "basin", + "repository", + "bin", + "hopper" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/vertical_oval_tank.json b/application/src/main/data/json/system/widget_types/vertical_oval_tank.json new file mode 100644 index 0000000000..55da0eb66e --- /dev/null +++ b/application/src/main/data/json/system/widget_types/vertical_oval_tank.json @@ -0,0 +1,36 @@ +{ + "fqn": "vertical_oval_tank", + "name": "Vertical oval tank", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Widget indicates the level of liquid in Vertical oval tank.", + "descriptor": { + "type": "latest", + "sizeX": 4, + "sizeY": 4, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.liquidLevelWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.liquidLevelWidget.update();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n}\n\nself.actionSources = function() { \n return { \n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false \n } \n };\n}", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}\n", + "settingsDirective": "tb-liquid-level-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-liquid-level-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.floor(Math.random() * 101);\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"tankSelectionType\":\"static\",\"selectedShape\":\"Vertical Oval\",\"shapeAttributeName\":\"tankShape\",\"tankColor\":{\"type\":\"range\",\"color\":\"#242770\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E73535DE\"},{\"from\":20,\"to\":null,\"color\":\"#242770\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E73535DE';\\n }\\n}\\nreturn '#242770';\"},\"datasourceUnits\":\"%\",\"layout\":\"percentage\",\"volumeSource\":\"static\",\"volumeConstant\":500,\"volumeAttributeName\":\"volume\",\"volumeUnits\":\"L\",\"volumeFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"volumeColor\":\"rgba(0, 0, 0, 0.18)\",\"units\":\"%\",\"widgetUnitsSource\":\"static\",\"widgetUnitsAttributeName\":\"units\",\"liquidColor\":{\"type\":\"range\",\"color\":\"#7A8BFF\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E27C7CDE\"},{\"from\":20,\"to\":null,\"color\":\"#7A8BFF\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FF0000DE\"},{\"from\":20,\"to\":null,\"color\":\"rgba(0,0,0,0.87)\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FF0000DE';\\n }\\n}\\nreturn '#000000DE';\"},\"showBackgroundOverlay\":true,\"backgroundOverlayColor\":{\"type\":\"range\",\"color\":\"rgba(255, 255, 255, 0.76)\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FFEFEFDE\"},{\"from\":20,\"to\":null,\"color\":\"#FFFFFFC2\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FFEFEFDE';\\n }\\n}\\nreturn '#FFFFFFC2';\"},\"showTooltip\":true,\"showTooltipLevel\":true,\"tooltipUnits\":\"%\",\"tooltipLevelDecimals\":0,\"tooltipLevelFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipLevelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.76)\",\"rangeList\":[],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"showTooltipDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":3,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Liquid level\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"titleFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1.5\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"showTitleIcon\":false,\"titleIcon\":\"water_drop\",\"iconColor\":\"#5469FF\",\"decimals\":0,\"enableDataExport\":false,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" + }, + "externalId": null, + "tags": [ + "reservoir", + "container", + "vessel", + "storage unit", + "cistern", + "canister", + "vat", + "basin", + "repository", + "bin", + "hopper" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/vibration_card.json b/application/src/main/data/json/system/widget_types/vibration_card.json new file mode 100644 index 0000000000..a80662ecac --- /dev/null +++ b/application/src/main/data/json/system/widget_types/vibration_card.json @@ -0,0 +1,35 @@ +{ + "fqn": "vibration_card", + "name": "Vibration card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest vibration telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'vibration', label: 'Vibration ', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Vibration\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"let factor = 1000;\\nif (prevValue < 1) {\\n factor = 1;\\n} else if (prevValue < 10) {\\n factor = 10;\\n} else if (prevValue < 100) {\\n factor = 100;\\n}\\nlet value = prevValue + Math.random() * factor;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"vibration\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":0.1,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0.1,\"to\":1,\"color\":\"#FFA600\"},{\"from\":1,\"to\":10,\"color\":\"#F36900\"},{\"from\":10,\"to\":100,\"color\":\"#F04022\"},{\"from\":100,\"to\":1000,\"color\":\"#D81838\"},{\"from\":1000,\"to\":null,\"color\":\"#6F113A\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":28,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":0.1,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0.1,\"to\":1,\"color\":\"#FFA600\"},{\"from\":1,\"to\":10,\"color\":\"#F36900\"},{\"from\":10,\"to\":100,\"color\":\"#F04022\"},{\"from\":100,\"to\":1000,\"color\":\"#D81838\"},{\"from\":1000,\"to\":null,\"color\":\"#6F113A\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Vibration card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"m/s²\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "vibration", + "tremor", + "shake", + "quiver", + "jolt", + "oscillation", + "pulsation", + "resonance" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/vibration_card_with_background.json b/application/src/main/data/json/system/widget_types/vibration_card_with_background.json new file mode 100644 index 0000000000..c1f471b93b --- /dev/null +++ b/application/src/main/data/json/system/widget_types/vibration_card_with_background.json @@ -0,0 +1,35 @@ +{ + "fqn": "vibration_card_with_background", + "name": "Vibration card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest vibration telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'vibration', label: 'Vibration ', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Vibration\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"let factor = 1000;\\nif (prevValue < 1) {\\n factor = 1;\\n} else if (prevValue < 10) {\\n factor = 10;\\n} else if (prevValue < 100) {\\n factor = 100;\\n}\\nlet value = prevValue + Math.random() * factor;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"vibration\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":0.1,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0.1,\"to\":1,\"color\":\"#F89E0D\"},{\"from\":1,\"to\":10,\"color\":\"#F77410\"},{\"from\":10,\"to\":100,\"color\":\"#F04022\"},{\"from\":100,\"to\":1000,\"color\":\"#DE2343\"},{\"from\":1000,\"to\":null,\"color\":\"#791541\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":28,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":null,\"to\":0.1,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0.1,\"to\":1,\"color\":\"#F89E0D\"},{\"from\":1,\"to\":10,\"color\":\"#F77410\"},{\"from\":10,\"to\":100,\"color\":\"#F04022\"},{\"from\":100,\"to\":1000,\"color\":\"#DE2343\"},{\"from\":1000,\"to\":null,\"color\":\"#791541\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Vibration card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"m/s²\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "vibration", + "tremor", + "shake", + "quiver", + "jolt", + "oscillation", + "pulsation", + "resonance" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/vibration_chart_card.json b/application/src/main/data/json/system/widget_types/vibration_chart_card.json new file mode 100644 index 0000000000..341fa94646 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/vibration_chart_card.json @@ -0,0 +1,38 @@ +{ + "fqn": "vibration_chart_card", + "name": "Vibration chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a vibration data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'vibration', label: 'Vibration', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'm/s²', decimals: 1 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'vibration', 'm/s²', 1);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Vibration\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"let factor = 1000;\\nif (prevValue < 1) {\\n factor = 1;\\n} else if (prevValue < 10) {\\n factor = 10;\\n} else if (prevValue < 100) {\\n factor = 100;\\n}\\nlet value = prevValue + Math.random() * factor;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m/s²\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":0.1,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0.1,\"to\":1,\"color\":\"#FFA600\"},{\"from\":1,\"to\":10,\"color\":\"#F36900\"},{\"from\":10,\"to\":100,\"color\":\"#F04022\"},{\"from\":100,\"to\":1000,\"color\":\"#D81838\"},{\"from\":1000,\"to\":null,\"color\":\"#6F113A\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"let factor = 1000;\\nif (prevValue < 1) {\\n factor = 1;\\n} else if (prevValue < 10) {\\n factor = 10;\\n} else if (prevValue < 100) {\\n factor = 100;\\n}\\nlet value = prevValue + Math.random() * factor;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m/s²\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 200 - 100;\\nif (value < -100) {\\n\\tvalue = -100;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m/s²\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Vibration\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"vibration\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "vibration", + "tremor", + "shake", + "quiver", + "jolt", + "oscillation", + "pulsation", + "resonance" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/vibration_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/vibration_chart_card_with_background.json new file mode 100644 index 0000000000..0e91bc68c5 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/vibration_chart_card_with_background.json @@ -0,0 +1,38 @@ +{ + "fqn": "vibration_chart_card_with_background", + "name": "Vibration chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a vibration data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'vibration', label: 'Vibration', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'm/s²', decimals: 1 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'vibration', 'm/s²', 1);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Vibration\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"let factor = 1000;\\nif (prevValue < 1) {\\n factor = 1;\\n} else if (prevValue < 10) {\\n factor = 10;\\n} else if (prevValue < 100) {\\n factor = 100;\\n}\\nlet value = prevValue + Math.random() * factor;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m/s²\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":null,\"to\":0.1,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0.1,\"to\":1,\"color\":\"#F89E0D\"},{\"from\":1,\"to\":10,\"color\":\"#F77410\"},{\"from\":10,\"to\":100,\"color\":\"#F04022\"},{\"from\":100,\"to\":1000,\"color\":\"#DE2343\"},{\"from\":1000,\"to\":null,\"color\":\"#791541\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"let factor = 1000;\\nif (prevValue < 1) {\\n factor = 1;\\n} else if (prevValue < 10) {\\n factor = 10;\\n} else if (prevValue < 100) {\\n factor = 100;\\n}\\nlet value = prevValue + Math.random() * factor;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1100) {\\n\\tvalue = 0;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m/s²\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 200 - 100;\\nif (value < -100) {\\n\\tvalue = -100;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m/s²\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Vibration\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"vibration\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "vibration", + "tremor", + "shake", + "quiver", + "jolt", + "oscillation", + "pulsation", + "resonance" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/visibility_card.json b/application/src/main/data/json/system/widget_types/visibility_card.json new file mode 100644 index 0000000000..40680948f5 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/visibility_card.json @@ -0,0 +1,36 @@ +{ + "fqn": "visibility_card", + "name": "Visibility card", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAolBMVEXg4ODf39/g4OAAAADg4ODf39////+Awyzh4eHv9+WQy0bP6a+rq6vv7+8hISG/4ZXIyMiIxznf8Mqv2Xvz8/PCwsKenp7n5+f3+/Kg0mGQykb5+fnOzs5YWFi7u7u2trY8PDywsLC43YiYzlPY7L10dHSo1m7V1dXH5aOg0mDn9NiQkJDa2tqkpKSw2XvU1NSCgoJKSkpmZmYvLy/H5aLb29teurMWAAAABnRSTlPvIL8Ar7DvmsykAAAGW0lEQVR42uzPyQ0AMAgDsHAI8WH/ddsxAoo3MMIcvV1awDC13sCQBx5/4ug6AVcirQgZRdgowkYRNoqwUYSNImwUeezVzW6jMBSG4d1nO+ATk4w8zcKRlUKcDlLEz/3f25QDJEwtFqk0Fos+Czhk9yrHsDU/IVvz/RAtx6vWZ0yGqRqfgkSoJIzEqq2EGLIAcjKWAkY5BVDNY9fCkEHfQeYaKxKGqNt1V4ji/XpT+Mo7AM4DAbOAOURqDpluK5KFHE7i6ZThXzVpSKpgnMTZeSd5oq71DWAbLqjq0FKb1xVQd4ikCVGcsXBVWJJkeZlykpqa4PphAvmzpRyV4xBfaUtWWtKfIyJJQspCDIpLdiiPu3EuseQcOo8pRGo5htRA2z1D+KbJhmjDEoVkgt3fMFBHwW5YsCTJcgjylvz5EeLclxA4V3tEUoSUgl24Ap8ugpV40uRIcog2kE2v10MsRZuVJkSNu7QDsC9EMfRM26Xw5MiBQwxZWfnpjJh6eUYQqJHQfbRZaULugmXzP3EHboKd8HT2OV+H1eqd4cnXbV8DTYfgDVwDNL0FOo9IgpCDGCmoaTrg7TF9R7RZaUI+xIjPPMuAaTrhdabtJSIJQi5RSPkIOeJ1sg6IpAhRxWOhpknhj5inTeKQ2F6wPZDNm3Wff9qmlRC8P1626vhxPABKsB02agxZ/5Co6PkVvzRW/f6vIXHJX3bsoLWNGAjD8O2rg2fIGo9WDbNoEC12AxJ7qf//X6skO6mJc/HBiQ56WYRYrRYedNMWte3u1mGM6/SEmxaHFvsrQOBmnL8IgqfDj9buZX8ojNrrtQPkUZI6qlf4WepM6yN6flshzNApKJQFpaPLHlD2ZwhzHUS1jo+BANtyKFft/gI3kIUCKccQ2c1TO4P6zCFGbHIghxho9ZE2QiHy+z6y2CBUVus/chIyWh8CaZRf74zXPz/xCYTFZ79G9iIz/kOAzLZicuplofpqClLGyz5PaJ8ry2LIjJhSAGd9FAR4+v283++ft03x2YnEJXucjD5CyFsqU6XJNYiLzi1v+5KdIVJWDbPAUlXO8i3XQZRElDaS/boiLDIrgDXAFUhhyTSVKWes1HgGkTcIZ/EV4glrRDwpJR8lEb4FYkS0OUWzjZiZwBEAiWQTZkfHy9SRi+B4VEfGqJkHFmpf62U1UsJCxt1d0M24p8RMvs+bRsM9sYXjuDIdkF4bkN4akN4akN4akN4akN4akN4akN4akN4akN4akN4akN4akN4akN4akDt7KR1wXReQf+zbTW+sIBQG4B3mwIKAG7hLnRh1Mav5/7/tCh58nQs3qW2qTOK7aQ+xE57yIUkpKd1p/SDxHheaRysKaZb82TfUAMEt2ueeMqdmqd0nQNyz2UWLFGXQKukDIH3zFrW/zQmJqx6im5BJWdvG7wx3WYaiH5dmE0eqdgjxMOB62gvXhAfHzcFXOWTk24AYnQETjnbNr8ohE3c4xm3dIyah2R+BOHIoiM6AxGmDKnXvgUHg4TH/hwx+ydMJG76SsF34GLlu5cqbuNbotyHtEv0G6bfZZLeHMGwFSJcuGar4mG44chbkt4LOPKLMvGC46zM3g1WC6GZ9liG+2WJmiUK6cyC4eP7Ix0Bzewmise2p1OepZ8m+eJ0HIVw7nXKIKkJGPg8A0s+7e4e9DUWk+PMgnvt7AGLZwRBMIWdiQSjMORC8RQ5A+BXaiQ2CGdTiZ/BWOgUy8q/wAIRksgOi9tfxLVSnQWbefI5A+rgMXBGiL4HgnHUAwlGiJghJLNqvbr8cQxVB4ChDCi9ExNcDWR2dQDosGFQ5RMYfHGuBuLhon9kWplI1hMrlEEl2PYpUAhnS5oPYtyGS+T+apNXRRZCrAoK+II4P9Di6tOVjvJORXANEF0/ZLTYqnCVzSDqk2OshuQNzy1g8IYoQDOjVkEeTBwun8W1r8NrLIDhIDVdDVBHCkx/RogzBoWCsFSJoyhw5BHu1oYunlsyzIVeK6azIIpf4VAyhatfPeiRdKCwXXSh+APl5ZjuT+F4+5O8j/+aG3JBPyQ2pLTekttyQv+3dAREAIAjAQAT16J/YGpPbN1iC0RhCYwiNITSG0BhCYwiNITSG0BhCYwiNITSG0BhCYwiNITSG0BhCMygk7ggdOWPZfOZMtGvt/l3kqgetqU27/4yQzgAAAABJRU5ErkJggg==", + "description": "Displays the latest visibility telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'visibility', label: 'Visibility', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Visibility\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"visibility\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#D81838\"},{\"from\":1,\"to\":4,\"color\":\"#FFA600\"},{\"from\":4,\"to\":null,\"color\":\"#80C32C\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#D81838\"},{\"from\":1,\"to\":4,\"color\":\"#FFA600\"},{\"from\":4,\"to\":null,\"color\":\"#80C32C\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Air quality card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"km\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "visibility", + "sight", + "view", + "clarity", + "transparency", + "perceptibility", + "discernibility", + "range of view", + "clearness" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/visibility_card_with_background.json b/application/src/main/data/json/system/widget_types/visibility_card_with_background.json new file mode 100644 index 0000000000..4bdda7b2ae --- /dev/null +++ b/application/src/main/data/json/system/widget_types/visibility_card_with_background.json @@ -0,0 +1,36 @@ +{ + "fqn": "visibility_card_with_background", + "name": "Visibility card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest visibility telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'visibility', label: 'Visibility', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Visibility\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"visibility\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#DE2343\"},{\"from\":1,\"to\":4,\"color\":\"#F89E0D\"},{\"from\":4,\"to\":null,\"color\":\"#7CC322\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#DE2343\"},{\"from\":1,\"to\":4,\"color\":\"#F89E0D\"},{\"from\":4,\"to\":null,\"color\":\"#7CC322\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Air quality card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"km\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "visibility", + "sight", + "view", + "clarity", + "transparency", + "perceptibility", + "discernibility", + "range of view", + "clearness" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/visibility_chart_card.json b/application/src/main/data/json/system/widget_types/visibility_chart_card.json new file mode 100644 index 0000000000..86d821f3b4 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/visibility_chart_card.json @@ -0,0 +1,39 @@ +{ + "fqn": "visibility_chart_card", + "name": "Visibility chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a visibility data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'visibility', label: 'Visibility', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'km', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'visibility', 'km', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Visibility\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"km\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#D81838\"},{\"from\":1,\"to\":4,\"color\":\"#FFA600\"},{\"from\":4,\"to\":null,\"color\":\"#80C32C\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"km\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -4) {\\n\\tvalue = -4;\\n} else if (value > 4) {\\n\\tvalue = 4;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"km\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Visibility\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"visibility\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "visibility", + "sight", + "view", + "clarity", + "transparency", + "perceptibility", + "discernibility", + "range of view", + "clearness" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/visibility_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/visibility_chart_card_with_background.json new file mode 100644 index 0000000000..d2cc8ace86 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/visibility_chart_card_with_background.json @@ -0,0 +1,39 @@ +{ + "fqn": "visibility_chart_card_with_background", + "name": "Visibility chart card with background", + "deprecated": false, + "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAADAFBMVEUsKiGrt78wKCC/x8+ZqrM0MSQkJB/AyNCaqbbx8/Tv8fLy9Pbs6+zr6ejs7/Dt8PHk5OHq5+fr6urp5ubm5+Xr7e3p6+zo5eTn6Ojm4+Pp6urV1M/T0c3t7+/n6ern6+3u8PDl5ePk5ubq7e/p7O7EzdTT087V087V1dDm5+fl4uHb2tXZ1tHZ2NLa2NPm5eR8wyLb29fd3Nff39rX1tLj4+DX1dDh4dzi4t7H0NXp6Ojt6urh39vMzMrt7e3d3dnj5eTAytHR0M3i397f3tjh4+LQz8zX19Ph3dzOzcvj4eDBy9Lk4t7Y1tPZ2dXu7Ovf3Nvf39zf4eDj4N4lJR/c29bZ2NSZp7Dm6Ona2NXk5+goJyG/yM/Gz9bI0dfZ2tfb3Nnd397h4d7X2NXJyMO7xs3d2thSRjA9OSnh5OXb2dg1MyavvMU6Nyfb3d2aqbOXpa1MQy7d29pIPyrDwbujkoxISTjEw7+biYM4ODi4wsqrucI+Oy7v7e3d5tK+vbmzwMiejId+bWnS0dCntb9kZWDW2NqdnZuVh4SRg4BXTDW+wMCts7eOgHyPfHdSUkRDPCmGeXV1a1bGxsMsKiK6vL24t7WYhH50ZGBgYF1SUlLd4uXLzc2Ol5twZE60treLyT0xLyWxuL6ksr2Vl5OCfWzS1daUoKgeHx/CxMW5ubiRnKKeoKGTlo2MeHN9cW+Fc27h29qdrriprLCsq6qmpqWJfXpsbW2vr6+AdmZSVkorKyugsLptX1xfVkBbUTybq7aKk5WFh4eVf3l8aWQ1MzHGyMqzsrB1fHtpamiFjY61vcOxs7ShpqmYmpeAdnR3dXR8cl9pXkkeHh5FPTRFPy2joqF2amdOTz63tbNaXlNDQjGqqairmZKQj45eUk+EhHRlWFSZnJqDgoGNjIBvd3RXTUi0oZl+hoWm0m6YzVWDxS95gYBOTUvC3aGKh3pQR0FQSzaXzVRDQ0NmWULV5MPM4LR9fHpXWFO5qKG014eOkYet1XobGxuf0GG72pTrXafmAAAACXRSTlO/ICDPz8/Pv7/V1y8LAAAk3klEQVR42sTYv4vTYBgHcEVBG+HNZHE64dIb/BMcT+jkdouHXAs3OBSk5xpdBCVL5wzNFQIqMSmd/EEP6qAdQsHMTv4XN7j6fZ/3ffsmeU3TSsRv3r5py12bT57nzYW7cvXm169fP/B8Qd5+efv27WueFypP13mJQXku8kTkBDnk45DnIc+BTq8nRq/fl9P9dXpq7vfXL5CDYuTHYdZv8eDL+IN/98m1q1eun8GhIeSQEY6ChBgvhQKDMwgCBB78gwsMhTgQh9vrIwrSN3YCVZQ8pKHTw0spIYzMyY0rZ2eQCApBEOVQVSkXBOESjDzFLAgIqEGPok43DlVB+NABEZNg9rSCJHgUbVIhJdQQV+AoQSCpai5IVEl0Z8kAwbfcKZTlkAJMup3yArFJCJ4S96AmiiIdhwQBRfeWjJAUilIsCNWkCIGi0Fk9DB70lAgpenmCfCKiOPcP8MhXoEpyKLuLII9IgggJKPmFUq5JwaGiG4sYxaUuCtEnAmYtkQIz5pI3MSRZlwVFAQQStUwg2bxOXpoSDTGvWNRY6tjJoxh9MZAKCX61rrkUAxu+HRDh+PJweD5Y53Exx5Q95O7e3Vw6Mo4IYxhGbGbbmGnCDqG9fGZG/mR9HGfP4+2MKAia6xyHpeOoGEe1jlVIa0NutW5hYM93fKLXdWltlbsHa8gZ5fndDiCGpMwwIfUOEhBHUFQaoVjn6CwJIYpHrWJUhFVKrF0l2GRBmnMgx1gnuiLoLHKUmx5poLWIIjWUJiUOGBh8jRAFEIqCVNdk99ai6HWCfYMUR1XkGRzVEKSasQvFIDQFYcKBiiDUWjI1y93vCkg6KChqHbocmJutCDrLaC2k6LicOmw2ThdCsZqzOILi3dh2/QvXSn1N2SY7lwT6egg5TtBaSL61loF7OY9kc+HQWTBjU8bOU8aiiMVpatuDCJCBP1iNB55lDQbbU/4F5JAoZciry3g1nbmLmUOZLo6iOIpi9s4du90lKuIGbncWAOK7qbt4Fyyt4AcpGneQpB6CzsIABJJca43j+Ud3NY0XqAnPwp8FDJDxil1EHJLa7oWEWMu59SPwYg+QrSW7WbZuLYIgCpLE8XIex1EcrxzKchXMOCSaL+ZUkcgOZjnIAEWBY4c021saAoWEUOZxkATfZrErK5Iu4nMOWaXM9akiXdfPQaxx/IMuW/8PQo6HWCMFyCl6a7ZcLBI4KMGcccjs8jJgHHK5mNsK4se/8NhlrSPNMcyrFqIvv7NxMF6eVt1u2eV7rXdzYmCz/gUFP11bEQl5VoRU3W6Zd42U5cLDTBaahKnmrqvRqxbaajtIsSAb71KEo77PGr1plP/aQmtVQep6y4iuCLKpJA0sDw2BIgc5K0OQLW7kbdOBoduritNoRU6Q+oqYzZVPdUU2L5SGFIjlcAX+SVi1RhTlODIWe2VFvNyapwyjv4O0uKPSsj5JFgIIBRAhqWitaDS5cMzF7ps1aU+sj+XiZH9VksqTj6HPkurgDjGeUEU2QC4cxzv22athEg5Cfy8cnXqhn3ozz7bTMPT91E6T1PKTrJ18an9OMnx6NBp1omm2j2efj6JRdjvNuiZk178c+tBpFhtPB22FFCtirpKfXBM6E280ZCGbpF7mTezQ/oRaJEf7iZ21Qyv1Qiua7CfWJyt5bFnJZGhl1tEIXzJtDaPpMOnW1KSlpg1LgQ+rVBEJQQQEMSFgcEj42Jk8DhkgA0DSkXeqIfuATNu+DYjtacjRMOsISGvaGnXTYXLnzxChWa+FKkbh2AlThjznWwFiluT8pz9ykvCngBx/9CcEyTxABgNA0FqhxSHtrA0IGipFWz0YZftUkTS7iJJ71VcwvagrFGKTu3L4Gr37XKTQWuYyqbhPMWP9IcZpbW2bYv+UX9l82DIEefn9uwGhmA7EgFRL1HmUUS9rKBpND70ZjFz2vlMAoRCkpigMY1sJpXTN0fOGRuJRFDM5gwl5vxHidLYtirW26JbOt5WcMei9PxcCI1eRMsJkMCYgb75feQ8IAkgpCmJKVGobDCN3SPm65A6Xot/SU3VDMQBU9qDAeAMIUTSkbTRXob3YUH/GHj7wtAQxb+nNimgGvSy+Vd9QLJ/jNyKAIID8ZrXudduGoTAMdy4MZaIgGCAhKFp6E0WX3IAmwVtHLb6JAlroVVMdGOiQQog3N4DXLBnaZO3ki+l3DkmxJC03QPqKsvLjOHpyZMdKKQtBk0Trun7WLBmt5ZSZ8B+lx2GntlkCCTyvn0gUPVwkYEQC+eUh3wHxFAoHgpzq+qjr8bi+3go9EOZhGOS2Xfc9IGrQgKhxuMuehnHpLWnBNZYyEkRSVsYGSdvKT4TiiTgLIQwm1+Pd06iru6/4b403Xkchrh+ux37X9VvUP63bXYct+6Ges6G7Kgny1oAIHGVZQhEw5JSH3BtJo/4OEpPuj8+Nbp6/AFLv8K5LiJPovzjI0EmC6OxHN2S6y8q3SyZEyZlDOAYUQjbfNrt39/dMadpWtcrnII0eG70+6mPNlPpanMbjzVYPDOl3miHyJHfbhy5DRHnzHNzpl9F1hBUlVhtybDaAQHL//XOLVNgyzyssbE3V5KuatrpublZCrFtO3GBXUslO9uujkpIkoJTv/xnfBfcN33EwAluokNhjgBS0BCDIQRAgXERB+VSNZRNRUmz1F4nYcnWWUtLJ0zlexXmBKQuS5xKuykFuAwgkbUhRRMHy1a4Y4zJTOeOwhtJ1VfpPLSF9OqSjoGYh69anZseSagRWlKGcmQk5ptPFngLK2SEILF8KOby7RQRZoXZGwxKsNKs5YylTinkR8n9ngEHGXWXU5SnMTcRDIksoIcz5anAWKQVDSUeC6MTszoc0+nFevsuQw+aAibDkFhBbsWqLIrYsp/KZFvnCZSR8daWSTIr/WnXgAOEY4ikkSYeiUkoae4wE11HsQP8DUtPKuc5BHi2kguADlqGgZChYtnz5L4sZCSBhzrGIE9j+LncJWghnXvNl7Ishjx4CByDmg4IxhaOkl1g4m6VZS0dhCEnKGCLhSOlU/spSyMvhBAgiSFWtqtUHSKylMLWEiSnzOR4gsuQgwY6AspAJn0x36bo8enzH3d68mBjCE0HOguxzxRXN5TxHecrCQHwEMRnI8kIKG9brmiA/HzlATFbCoyn4AvOaItD4k1emonBfTiEYh4d4R/BCwo+hCpt9PKjso7obf1AzEC8JMKHH/Qpf/M0AkibxFHGG4nWpCzWk2L/s3/1EgPSfGOExUcWFUgm9bn2MJOFE5g0f7H6ZNd02+/3+N3ZAEEH+kGrHrk2EYRzH8wec8BIvUg4qPLdlqiLBEkg8hCCICMng4mC0U5f+Cekg2sHRSenQoTi1Y3Fw6xIjEvoXdEgUbK0VKinS1urved73vTfXNxcRv3fXXK5a75PnLs2g5Di4Ry5aPJL8k573qtwjgIjEh9RLQRTlMv719Xu8sbGyYSHo+eJiKnGV8xoDhGEchzZ8RySl6RAeSDksh2nYn8Gii+2hKSAHGaxIhe1tC4FEuiCJseQFA5eVwBHIRNAEByBXInZ4GUWMlTfz80LkOGUPsjKQCl+2P2wbiJ2JpbhyGVidXkAzKURLZn0IbnZ2OL+f+W7s5pOyGCSb7jEr+n1AEKbyvNEQiuuiRXOMKbYlaYQE0jk+HgGSSmbD0WgUMmFWvpQ0xMDnaC4kDs+IJop8MTy2pUEfjP4KIBIgaBGL08STk+n7Cv4axzdPvl5DX/c6dcSSzr4c2T9kFKrXgwCXVhiTbo4k92r4FNlyWurrCgcHBwxZaLgW0RRLVtFAeCAuoc7aNduhdozA0J3Mcg6Cv5Rb+NdiB1nnABHJm0Y2tizmW9IpKPxZhRJSRHDINM7locMSObK2t8+SkYHg2roqE/kvSQpZ15D3B9KbVqvR8jCJbjIDqbEYsofTPeOPsrJTR7xzYgazxgw4AIkEYlNkfoAap1zEyBEfsm4hSCC2DMVZEouwCuVFN/jVjxhCfN58v+PxXO6WEx4JO3giRQNRCIpsOdNJDM+DbK5j3QSEY4hHsSWufAaiY5zsb0DQOXbxMYWPHJbqqMOjqUuBhShbLmX+MUpMC48SSmq1LKUNBFf4/FkkGuIsTpOMY8gh/Ojw6OhoFF0pWkhQ+s1z4MnY2aCShajcyFrad1AjAYBqvXddSoavt4i6W8DwitpAGIhInjK9lc3HqOlRWJ6pagju8X1AzgDpBCLBkTXcHxYyE06FGMmd2upqrUHco4+vu7TVe7GQ0M6gNiCUhegA0Y3NxFpcqqJTCis23nMRhfi1HhUBkUvK3PQBwvnvA1KS8PxKFRBSUyN0++Wzly9bRLVujQiQIf778QINIfEgb9+mEJc/mnmslcpdU2UsJSQ3kAgOxS9/B+cvEJ6Ig+Ae8UaSC6kt/1xebpLq97ZIAbKzS70BDg9TyXUgTk83Tws9xJAlBEIu5wmaH6sCTdZDc/h8EgnkTAbiJuIgLClpSSgSPd68Vna/rffmlSZ/61K/Rz+6pIZDNVS6R6e6wjMEyypDfEyz2Wy1mk+8DKaC1WQHAkjxhH+LBBMggZkOHqPqjIagDCT77Nmvh8vKtLNMaufHLvYWXuxupZBPUgEMtqy22+0lV8Ziu2myGJtoZCDVS4CIY62YgQQWguw7cJUhFZM2+BPC2aspVdR97fgOCNertXVL4Ny65SzNyQnHUXCZ6YHwRA7Z0cFgigYiTYLMjUHM4lRZjztk7krbAwt5pSWAuGBJu2FrNm/kS+TKugzIpWgkDp4M0m+/yLwhu4rFajnECaXdU/eUwoaFn+kqWNyJ+929e+c7FKjw6g/j9ffS1hnGAdw/wEBMBk7jTW/aYBpD07SnaFzADstkmYYSQXaxYkMp3k2kErARFiQXuyg0IOiF9GIFKWVXinWCguBKb0arXdFapEwYaKmjXeek9Me+z/O873nP65ljX9N4TmK399Pv+yNOjE0wZOCSlUwGD4MxURAOS+hJFYJGSi/EIZKPBJFZ9kJBSgoii0QrMNwvQWBHUC7cmHGfO4cHPZlcAEIgTycmJgD5Bjl1yY5Q/Jx0GhBNoS/UnRBI6RZ97sVFgCF8noSQ2hJtAHT1OiQBROYWRiPjBkA77GDopJBk1Pd+SfT58z+eIzVPEVgAYQrnPywZJUk7nqXPhWBmsQOfSQIIOxqD1EOIQitnP9TYCEiwVCrtl/b38GviMYIgxDB9+Bw6o/fPXRiHQcchCKIhaEUgRqNESSSTTNoioRgJzawEQcSxF6DAQZIPNH5c4B3sZNxIabe083bv1v7bt3yUCATRDgPwJTMOyHD76MadjZlLDsdAJD8rhNGIBxBywGRRuBQjCSrIa/qF6jVnd3f3AwHoF8QPb9+94COSILdu/RXaKaGY0g5NrmOAGIpHcVGCt+gZj+z48YvjTqpy35lxNjq+b3dylOO3JTUHBwwpfMU5hHGDa4vyuayUdI4hXcFjAqFCTHi3/ejevlO719672p1SLUECrWENQasehePNReeimw0nOzubBCQ11O6wJHL7uUB+ohw8LVzmgOKXxGJ8rWcaR60U3r9oZiU+CYdb/RBkX1588bFWslv77iMgu7WlnUYFcRymoAaqggefvpjmOLp4gQw52aFUdcMZylc7HIQgiIEcVC/reCmkQFCV3DBEKGb/MoW0luzUSv7a39vb5yvZfj8rHeNdOBgKhGmR9DsIU/CvT0kfijAcK0zEF0N+vD1fcwMOBBAdNcdiOnwf+yYma4ZjJIDwCgmTo1WWOR/qEnX1mVbwDaLeVZX0M8Qa47/Fy3BzQjUCCFOqFy5oh6Qv1ud1uMnnbUqun7YsMNghEEgAYIKITGxViFZJkCBHI6j2wyB5A2HI/PzteYIgBDESxvQhROnrwyUTvoGHJWy5JBIUQg6t0I0g/rH7I5+4zugyzPDUFX/TsXk6TfMcTC1EIJCIpRchiAk3w0BcYo7lMcdEksMvCzakEfE5jMTWhUIBqkQQMjzJ53hwcGXigaQ15NH8/HfzjwSC6QWIBA6GtFgMOAAxEg3BxGKHhuDoVo3YYxaK/ZKCfAoIMYzhyCiTJZZGHj0ChAJI6pDkq5aWPtJAIBB52UjEkUiYdR7iWBC1VBjiW/FmapmRqXMqYwvseLkEuU2Q9Ru//06QVAoSmV+9ash2ejVESwBxuhq800oc+MLgXYlAEINQ4ULw6xVBZGxk0EdVUvZ6jDUj8ds0BA6aWuS4MZxCpBDmxHtNGKWvjCRDC0Q7QuwwO66KPXQ/RD4CC0QGzGUfEVD8EGagETgEAoc/gEFmodSqIYgTTITV2oACD9/wrZHrO7uRT1FJfw5zSmo4JRFPXsWl+ByZzkccQNZ5tQNiUWyVl9IiEqyR3LkGcYR0jMQWubFdIgmLhBinfMnLE2uMRYHotvMmO27WrEsAiQJCmBTHhmXjmoEtgCUopL85AYdA/JTuNh/If7Lo36/OOKe1w5+YMimNnS9uIgS5xo4bw1EKYeg5qiGaEo/HgdBhyGmnGRMLEhNFweN/JgQITa5Pgl2oROaV32E+WUDih3AAQSABxIpdSjZLkhYjieUzuS5Z6ZbFnmfqmx1zrzbrVlnwOUBoVfggcHBci8XpJMWUQBAXsjE7E703M2o7AIGkxQSF9GCl19eBIZRWP8WAjoxHcgaVsATxMXSEInEhppF1/BmOINFoZChSiT7Bl78R7qSX/hAkk+7CzBKIIbg7mDpJfA5/WIIFHzyH/ZcpxqE/UUi0RGP01JriTrgRgUhmKhGCICkzxaQSwfQSJJ9xmhv0Z3c73hr8lbRxDkl4D+5yIFGQGCO04wJFUVoMJcmUdiAQhrzCnyHlKERmI7OAUCpPRnUz3ImWkCPNH3qpEs/o/Q6Jq6hlRbdIFAd/DY1gbvWn6VwgSUxlowUH8WW1jZLFQIZmN+DIa8jqqjSy6UJmZp5E7s3M8DVxUjpawn0ke/qbjxEEhVg9WA4/BOlu667tlv2ZKSE+FMO03NOnFURJxt6//3Ns+1l7iuYGU+jDrKZUqZM8IFJJzSZLAPHnvg0RCS2QJI6QhkTCLeTQ2rYgjR5KG2Mk6qRhPRohiAOIWu/sWBl6tjz2EpqD7VmR9MbhUOmUVd8+NSWQa0dCwLAkGhJL9pyHgyZWHTssizuLrEZwjzKkFfMDHEAgoePdXSQIO1rw/8um7m/Nvfz664kKDUFD5FkgHEytb799hWViQ04wRHI2ajfCR0hDot7rMBrPkvZCoDBNGA8H2xZJEkEsEtsBCCTZFDI7sbIy8cw6BKSYgm5k85Us9uPHIxE8IvhuV6JPenEAcuX0+WZx1AWQVo63F3LQgva2RG0YBxa8tW1xJYBklCIPxzBGyRTWIJWxr1fGnrV4wpDV1dWpVYJQCGJywq4EIclJ1Qh2LC5kcGsQEnLUtaIbhREI4kIqy5WZO9UyySik0qBuFxLgkwSQmEQ7pAF846v41sF7WOI69EZhapVTszkyQhQvJHJCHPgyGEAogKCQhvr68PZacSOA1FFEIZA2tTvxfjw+uTA5WVyYnv4zpxvSm1e3bF/8YxqSP6UcQ7wSaLB22rdevl+Zu4+OVAoohCEjiEBMADExlfAyiSVlhXSuVbbXHpR5btVpBBQq2FshKU9OL+f4vlyco0EbCUFwoQoJtNZ9Evzlb5UDOKD4Cg4/JT5Elko2JakuEuTNG0CugkIQO/7dCxQqpIcKCdc9eBAIlIvFSihkE7oRPCG1ueXpyTL64btycVmvC3lBGsFTSO1bWwsvVQ6wX0kEYgdtDG1VMJyzZ89Go8Pri3AQRDJ6vOlIiKbgfEch6S/JsbFWDmDsy9NzZR6/Hv51ijwvL0zeqcUrks+qC3dhECm/qDVtsgN3rP125jwmV0yN9mgKXqE+opTI8LX1G4uLgDx+TJVsjjY1HSkxEBTyuRRSfAgHRlWeLN7tNrnu5k6xeBfvaxne+2GhiiPQ/SFDaSNIT/FhfeLM+dPJGElshb+SLEtQSHSYfgtZEggCCHIIUh22Uq1WC4X2znIEZ8rWyvcDAwNXrgwODmytTFSvDFBwh/Qgg/cmFx524iX+mQGVuSK9NKjSoy/kL8+t9Gaz0XJne+H/BGNpl8mFqfVmcX1pqebxCEtGmzqY0mRRPOVQJdm+fJq3rM5ihRYGT5S28tzCVpu6uc5JzU3Pncc7snRCbiYncevpjtpok92tstCBza++oSudjB05r05y4idpbnFwV1ikLC3WjPyKbI6MdnSQRHoBx45sx6nemDpDHhbDvFOF1CiKxQ7cqVH20+KgIeIdiT5kysVte1PQykB5YQuOcH2iOZeJxe2QwBf9OSNeWKIAggVy9derLqRDLP4Aku07RVtWQ+KLtQ2c7GE6D+Uk6Xiwts0w5O5C8Q4PNaQZctLUwTK+Nu6eNqxgJVJ8gP8eHA3NTgaN2LEJJmQ8WXizCMbiEkNoaokDHIG4D1PKP4TasWsTYRgG8P4J1RNCqIuLPVJtEEsRNQgJhPTg4EJsIXQrIQkNGRrFI1DoIBKISyElULdCp8wKgQ6CUEKWUggNiCVLZodSQRfB532/78t7x7X4aEhySnu/Pu/33bV0ZeOJly8kIBkOk8lYDBZgEHqmUuCiC+BEnSkbVOSaOTl5z/cCxkjBP02b2xZ+BTiZnE9sPV0vYmhEcbNjdZWflvVoYbGD8W73wjSSNhIZMx1dSMZxEmejVJySBEcnGSufj84b6U8n0wadNBeloh366OE1w80hi9MarZECHzKReEazJRBaElGFygq9LtECwXqfq9Vr1V1eI4hySM4OdSYlFLLdyGUQ53QKDcIaIDi0A5yMPmUt7ikafazRPLd06CCjss2pFVOS+Xln69X6MjexbAhRh40QhSA0WDRa9fru/sWPi71s9gbJ+FzndPNwUCx7eYJMmg5HS5LUhxq1yXfL5GYI4p+0LB5L+S/XQ66GKQ/wcyHsW2S41WHrcC3+1zYovwhSre7vVwGBJEoxK+TL4d/rYy8PSen0TEGkE4osmYiDpkwyOdGtmYUyxZbH88eWhLPlrctGdbsDURBIYJkDo1qt1QliJJ1+iRE+QcxKd/3fp6cfOpnMh2ZGGAgJwIj0YM5ebWzP9TEcPW9iGYmjO+qYzU3tW5l8eSkIWTXRbyCgi0GJIbZ/1b6CpT2HQnar9V0FUZSz1CVDWlwJO1bdYrnhjf+Mfn4blYjBDgyUIcgXWSIOjrnVb1wP0/yZ3lM6zanZpZ8riJN5hUrCDJsiLxeQtzbHbwPSZsguIDWGqFayrXSLp+v4cg8OhuCa3vBy+Xxn2BxqhnYYAkKQiEQc+KOSbo4C+aQZVIiGeGWZrRnDSDTjc1+98NuVqzZBqlgkDJEwxIQhtGN5+RxvWgWZK4boxfGfRnjS5LDBS5gRSzLkZXlJVvqshJlkQQLR1ZWC1Gq1ej0MGfuttEjgsN0l9AEEOyizRiwwJNFGblCxghJCsOMeHIDkX4UhiD1ziEFBMFgMOUJqtaMgpNNNpZEZZGEFOy/VIRJphGPxeleQsEQbdPTBaBvsUJB5gqxjA14ViGRBW6KNkAKt9NbWRJJWMRDbxaUwH27EVDILXgdm626gBksSclmhOpL8MZIPqJGMh9kiwYzBz9qgH8giQSo7O5VKBRAwGEIRSQCy4FIhCDsKZs+SJFViRiLh0yQjcu8eSSQBIBs48XmCbOVxKZEqQhDJIuctKSo7FTRyJBBEIIpCK2S9IQ5ABBGSsOPO86iDQreEDLHCUQTDiAPioJG8Vy4KhHI7ZIcleo0cHSlIpJZQIQW5NZHQO0aQAwk6EH2qeiFJDyIJQ9C1U8BsNZYem1O3zeqQecJDGlEBBDk4AEQkQqEVsrHODtSBzyIQiRoqixRCMBCOMghO5ouhgYVGFCcAMT8xCPXAT1qCNaIgB2AgXQCiFkCwZdElBAEEgQMRBfcR7gImRF87ZLeShGoTFxcEiYJsPGKGlhjCoolBCUSlm0oRoBRoRkFsunvn9FxHSxyi4HuSlUJMhRe5OKL7MPJCRb+R42ETX9oLL7FtbbjseGggiwiuaZLZGtmhvwqySRCi+C3S+Hh01kqtVPp+1i26xaKdy23bl26h6CVKewnHpfka9+K9MhBlaPD2TtyjAsgRC0ICfWiGJMqI8WwVcljsLhWCU2cJiVQiECDeVN68mdvc3DzAAxAKIAAMWmvHg7OPk176YddPD0qt3jg3/ni8sud/8Qafna7/GZJ+d7u3HY+nvvez9ve3fetLZ8CzQRGI+X4wctom4TYQaiTfWFp2ycEQ5DbIfTSycwHK/txrKMKQtf4/QuxfRXEoCgP4YwyiDyBuK9OlMbAksoEMihC2FYt0moWUFhYWNgMueQKr6RYSyBukk20MBHyY/c7JPTm5iuwHzpiMDOfHuf9McfWLOvWP32eHLB1V6713j0rvPp2VBT6yu+SXYLHIwyKLJpM4y9NsOChn57dmIxsKl8iAVqbnSv1NjdOVjJsxafPDpYYAwrVCwgJk9OCgezy08EJHbIjjp4V/XTfrK0GG2ckBJLxH97AeF17jOKc4dwNsKfnifMEYywMn3fthOf4alK5sjRxzpOxucbWye0j0aiCOOUZWjIYAIvVqLMeIINhEiNJ2ZCmQc10U/vFY+Hmd+3l1GB+KJl3X1T5Mqyo+l+UsLjbn8gQIXs3OdfEBZ1dl+WT/tR/YewIu4VAcWLbChkCikCiYvo87SXsKf+qHQpKEIEuBSPw2GFnjIIo36IiJ7O94fbht5ohfVSmvyVwuI8wiLSaWoGQ5q+txkyIQSgvZRKsAFEuiDLo9asNDCxR0BAxYGqefDjIlyC4AQQIG4ro4RbJlTgmjiUJahkBMgVK5VKwKxPoTFi06osQRpslYSm+L5ndyLZBku4UDc2S5pNGlEHUYCCShBJAFQ5RFhy+OqW/Qh2ikfvywJbweSLgj9N93m803DK9hX0JjSQAKud2SW5IkBDGTXR2OgRwACcxT947icry+xK5bHS+iEjCQBwj3OwzxlH7Va8mr/Lp9ItsWsjQQjWnIAU/gVyzpd8WKQCjPDpRr69ShEG0J7yJzknyQRFsyeg3BZviZ0NCCBBR7sotjOCUIYiB/niWu5XiTmqVcySNEFGYvVIhIPI8l78P/QPa09iboCEN+/6yfGkIONCSIOoZnL16eKw4tT6rRYjkdpLORwjqeMEYmmGkKSx5bIs/azRemO3Z1omDV4tn+14bwBDlMjQMMlExhiy5deAPHvzLO3+VpIIzjIuhf4e6gs3+Ak4OC4CSugkWnUgsZBLkDISfkaES86NbVIUlBzqlT++KYV7EG0VKKYCexm7r5eZ40YPHb/LhL+7bP577Pk6YXeA+hHcIjoiMKTR4F6TH+n6ZQlGPzrmvJ88X4nyUwMC2mIm3ejygRih0Q9WT65ghEDQGj57hJ5OgAQqcHwRBuKSoA6mu3H/d/QVB/9MgOriWPQXr35NPEkv9AFEFWlndML8qc70od0RPXk0+fX/R6/frx48cP5bbhXRFkF/TNGAIYMVR7MjIXrly+fZtzwazXtdmMrkoa9+UZDt3uX8UqIitvXbs2u3ntlormTP/kJvtLN24wQXfj8pWLV+VS/OGRHv+rrz+/yGTWgPWQWiyduKJH05OPaL/fb7abzY/Ntliq7NLWdv7y95PW2hpZ3VdvG59lWUiSJKSBHTJJqvsQm3Q8HhvjojPjTobGWhtp/MWzKu2abh+yPM996b3P25fz/VCuQLgQGfHzaTRaoRHFPeA+G/dDYEBMOkhqHVjuKI5gTAdgiL5vNqC8ms/nRcdirV0Wc1tVVV7XpUcuQ0ScGJMgF4zRUE0ibIg2PZdH51IDYKqMqcuci40czWLawQp4FnlPRqgoCsZoOd9u9h+HgMAxFA6+MlariYrZdxUs0ykgBykFCxzPHzx6JJAnePIdFtbtFhTRUoGsbsrFYlHbulyEsGAAF2Vdcsizq6VXsuGpIKuvKzHPy7NVe8pA1L72CbhJKKuq+fUr+gBS3uSZw5AYvYwVPO12PwQBVzCCqh6shpMVwXcE7CDqQYi/rxQB4egJIrvAEAYW9YQFktqqWlYMquoyLz0BxgZFCTsSi0vH6/Vu90e1I5FwgAjxDdN4XUbPJbwGI0wKwTfHlhXX1BwWl5W2aG3xAQwhkcjFhIkmE3faZAOU9CW1eikMIMxFPIIBiD0UQiCP1rZta6umIX1li7pmdKnKZYR3aJk056gRFkJdI4JNiTMhTkeTOPNKd0maRfEgpgk4+taxLHMKMC9rjC9ILiZESayR5NW9kVhAdT8YCAiODBUKEEgE4siSAdUBxw/Saos6GEDAgYdhIjXoVsAA4hhsiaGweBMjzRBiSa5lC0k08iSTxAvy8DXhWb9AHntILeopSRbes0gOgpXlFZ/45EkLXFLa+QdmqYcTMEABicZgxB0dfBCS1eCB6NyZs+f/IaFK+AE/PZHqwA8kHNQHKJY84oGWS6lF2DiJEa4LiQxtCKR7ScKfNoTsA/W7xo/EV6enuTfk2W63C9RDlRtJu/XuD9udpp7DiMaN6ZoUuljxzxve5nIKSLP62Yenww4EUSuATCDBEhE06MzZv/Ds8AQFdTqGAAAAAElFTkSuQmCC", + "description": "Displays a visibility data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'visibility', label: 'Visibility', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'km', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'visibility', 'km', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Visibility\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"km\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":1,\"color\":\"#DE2343\"},{\"from\":1,\"to\":4,\"color\":\"#F89E0D\"},{\"from\":4,\"to\":null,\"color\":\"#7CC322\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 10 - 5;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 20) {\\n\\tvalue = 20;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"km\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -4) {\\n\\tvalue = -4;\\n} else if (value > 4) {\\n\\tvalue = 4;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"km\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Visibility\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"visibility\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "visibility", + "sight", + "view", + "clarity", + "transparency", + "perceptibility", + "discernibility", + "range of view", + "clearness" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/volatile_organic_compounds_card.json b/application/src/main/data/json/system/widget_types/volatile_organic_compounds_card.json new file mode 100644 index 0000000000..8d15799031 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/volatile_organic_compounds_card.json @@ -0,0 +1,37 @@ +{ + "fqn": "volatile_organic_compounds_card", + "name": "Volatile organic compounds card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest volatile organic compounds (VOCs) telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'voc', label: 'VOCs', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"VOCs\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 2000) {\\n\\tvalue = 2000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:molecule\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":500,\"color\":\"#80C32C\"},{\"from\":500,\"to\":1000,\"color\":\"#FFA600\"},{\"from\":1000,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":32,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":500,\"color\":\"#80C32C\"},{\"from\":500,\"to\":1000,\"color\":\"#FFA600\"},{\"from\":1000,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Volatile organic compounds card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"ppb\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "indoor", + "air", + "vocs", + "voc", + "organic solvents", + "hydrocarbons", + "emissions", + "fumes", + "gaseous organics", + "contaminants", + "air pollutants" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/volatile_organic_compounds_card_with_background.json b/application/src/main/data/json/system/widget_types/volatile_organic_compounds_card_with_background.json new file mode 100644 index 0000000000..d01eb7d50e --- /dev/null +++ b/application/src/main/data/json/system/widget_types/volatile_organic_compounds_card_with_background.json @@ -0,0 +1,37 @@ +{ + "fqn": "volatile_organic_compounds_card_with_background", + "name": "Volatile organic compounds card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest volatile organic compounds (VOCs) telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'voc', label: 'VOCs', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"VOCs\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 2000) {\\n\\tvalue = 2000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:molecule\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":500,\"color\":\"#7CC322\"},{\"from\":500,\"to\":1000,\"color\":\"#F89E0D\"},{\"from\":1000,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":32,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":500,\"color\":\"#7CC322\"},{\"from\":500,\"to\":1000,\"color\":\"#F89E0D\"},{\"from\":1000,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Volatile organic compounds card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"ppb\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "environment", + "indoor", + "air", + "vocs", + "voc", + "organic solvents", + "hydrocarbons", + "emissions", + "fumes", + "gaseous organics", + "contaminants", + "air pollutants" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/volatile_organic_compounds_chart_card.json b/application/src/main/data/json/system/widget_types/volatile_organic_compounds_chart_card.json new file mode 100644 index 0000000000..b2d9147888 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/volatile_organic_compounds_chart_card.json @@ -0,0 +1,40 @@ +{ + "fqn": "volatile_organic_compounds_chart_card", + "name": "Volatile organic compounds chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a volatile organic compounds (VOCs) data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'voc', label: 'VOCs', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'ppb', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'voc', 'ppb', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"VOCs\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 2000) {\\n\\tvalue = 2000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"ppb\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":500,\"color\":\"#80C32C\"},{\"from\":500,\"to\":1000,\"color\":\"#FFA600\"},{\"from\":1000,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 2000) {\\n\\tvalue = 2000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"ppb\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nif (value < -100) {\\n\\tvalue = -100;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"ppb\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"VOCs\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:molecule\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "environment", + "indoor", + "air", + "vocs", + "voc", + "organic solvents", + "hydrocarbons", + "emissions", + "fumes", + "gaseous organics", + "contaminants", + "air pollutants" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/volatile_organic_compounds_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/volatile_organic_compounds_chart_card_with_background.json new file mode 100644 index 0000000000..c1d88af5c5 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/volatile_organic_compounds_chart_card_with_background.json @@ -0,0 +1,40 @@ +{ + "fqn": "volatile_organic_compounds_chart_card_with_background", + "name": "Volatile organic compounds chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a volatile organic compounds (VOCs) data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'voc', label: 'VOCs', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'ppb', decimals: 0 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'voc', 'ppb', 0);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"VOCs\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 2000) {\\n\\tvalue = 2000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"ppb\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":500,\"color\":\"#7CC322\"},{\"from\":500,\"to\":1000,\"color\":\"#F89E0D\"},{\"from\":1000,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 500 - 250;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 2000) {\\n\\tvalue = 2000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"ppb\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 50 - 25;\\nif (value < -100) {\\n\\tvalue = -100;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"ppb\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"VOCs\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:molecule\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "environment", + "indoor", + "air", + "vocs", + "voc", + "organic solvents", + "hydrocarbons", + "emissions", + "fumes", + "gaseous organics", + "contaminants", + "air pollutants" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/wind_speed_and_direction.json b/application/src/main/data/json/system/widget_types/wind_speed_and_direction.json new file mode 100644 index 0000000000..8cfa5e3db2 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/wind_speed_and_direction.json @@ -0,0 +1,34 @@ +{ + "fqn": "wind_speed_and_direction", + "name": "Wind speed and direction", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest values of the wind speed and direction.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.windSpeedDirectionWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.windSpeedDirectionWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 2,\n singleEntity: true,\n previewWidth: '270px',\n previewHeight: '270px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'direction', label: 'Wind Direction', type: 'timeseries' },\n { name: 'speed', label: 'Wind Speed', type: 'timeseries',\n units: 'm/s', decimals: 1 }];\n }\n };\n};\n\nself.actionSources = function() {\n return {\n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-wind-speed-direction-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-wind-speed-direction-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind Direction\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.7227918773301678,\"funcBody\":\"if (prevValue === 0) {\\n prevValue = Math.random() * 360;\\n}\\nvar value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 360) {\\n\\tvalue = 360;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 16 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 26) {\\n\\tvalue = 26;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m/s\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"centerValueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"centerValueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0.2,\"color\":\"#7191EF\"},{\"from\":0.2,\"to\":3.4,\"color\":\"#5B7EE6\"},{\"from\":3.4,\"to\":8,\"color\":\"#5B7EE6\"},{\"from\":8,\"to\":10.8,\"color\":\"#305AD7\"},{\"from\":10.8,\"to\":17.2,\"color\":\"#234CC7\"},{\"from\":17.2,\"to\":24.5,\"color\":\"#F04022\"},{\"from\":24.5,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"ticksColor\":\"rgba(0, 0, 0, 0.12)\",\"directionalNamesElseDegrees\":true,\"majorTicksFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"20px\"},\"majorTicksColor\":\"rgba(158, 158, 158, 1)\",\"minorTicksFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"20px\"},\"minorTicksColor\":\"rgba(0, 0, 0, 0.12)\",\"arrowColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Wind Speed\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"headerButton\":[]},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":true,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:windsock\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "wind", + "weather", + "compass", + "degrees", + "environment", + "speed", + "airspeed", + "flow", + "gust" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/wind_speed_and_direction_with_background.json b/application/src/main/data/json/system/widget_types/wind_speed_and_direction_with_background.json new file mode 100644 index 0000000000..c013e568e7 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/wind_speed_and_direction_with_background.json @@ -0,0 +1,34 @@ +{ + "fqn": "wind_speed_and_direction_with_background", + "name": "Wind speed and direction with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest values of the wind speed and direction with background.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.windSpeedDirectionWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.windSpeedDirectionWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 2,\n singleEntity: true,\n previewWidth: '270px',\n previewHeight: '270px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'direction', label: 'Wind Direction', type: 'timeseries' },\n { name: 'speed', label: 'Wind Speed', type: 'timeseries',\n units: 'm/s', decimals: 1 }];\n }\n };\n};\n\nself.actionSources = function() {\n return {\n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-wind-speed-direction-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-wind-speed-direction-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind Direction\",\"color\":\"#4caf50\",\"settings\":{},\"_hash\":0.7227918773301678,\"funcBody\":\"if (prevValue === 0) {\\n prevValue = Math.random() * 360;\\n}\\nvar value = prevValue + Math.random() * 20 - 10;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 360) {\\n\\tvalue = 360;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 16 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 26) {\\n\\tvalue = 26;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m/s\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"layout\":\"default\",\"centerValueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"32px\"},\"centerValueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0.2,\"color\":\"#6083EC\"},{\"from\":0.2,\"to\":3.4,\"color\":\"#5579E5\"},{\"from\":3.4,\"to\":8,\"color\":\"#4369DD\"},{\"from\":8,\"to\":10.8,\"color\":\"#2B54CE\"},{\"from\":10.8,\"to\":17.2,\"color\":\"#224AC2\"},{\"from\":17.2,\"to\":24.5,\"color\":\"#F04022\"},{\"from\":24.5,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"ticksColor\":\"rgba(0, 0, 0, 0.12)\",\"directionalNamesElseDegrees\":true,\"majorTicksFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"20px\"},\"majorTicksColor\":\"rgba(158, 158, 158, 1)\",\"minorTicksFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"20px\"},\"minorTicksColor\":\"rgba(0, 0, 0, 0.12)\",\"arrowColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Wind Speed\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"\",\"decimals\":0,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{\"headerButton\":[]},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":true,\"titleTooltip\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleIcon\":\"mdi:windsock\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null},\"titleColor\":\"rgba(0, 0, 0, 0.87)\"}" + }, + "externalId": null, + "tags": [ + "wind", + "weather", + "compass", + "degrees", + "environment", + "speed", + "airspeed", + "flow", + "gust" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/wind_speed_card.json b/application/src/main/data/json/system/widget_types/wind_speed_card.json new file mode 100644 index 0000000000..2eefedd2b9 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/wind_speed_card.json @@ -0,0 +1,32 @@ +{ + "fqn": "wind_speed_card", + "name": "Wind speed card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest wind speed telemetry in a scalable rectangle card.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'speed', label: 'Wind Speed', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 16 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 26) {\\n\\tvalue = 26;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:windsock\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0.2,\"color\":\"#7191EF\"},{\"from\":0.2,\"to\":3.4,\"color\":\"#5B7EE6\"},{\"from\":3.4,\"to\":8,\"color\":\"#4B70DD\"},{\"from\":8,\"to\":10.8,\"color\":\"#305AD7\"},{\"from\":10.8,\"to\":17.2,\"color\":\"#234CC7\"},{\"from\":17.2,\"to\":24.5,\"color\":\"#F04022\"},{\"from\":24.5,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":0.2,\"color\":\"#7191EF\"},{\"from\":0.2,\"to\":3.4,\"color\":\"#5B7EE6\"},{\"from\":3.4,\"to\":8,\"color\":\"#4B70DD\"},{\"from\":8,\"to\":10.8,\"color\":\"#305AD7\"},{\"from\":10.8,\"to\":17.2,\"color\":\"#234CC7\"},{\"from\":17.2,\"to\":24.5,\"color\":\"#F04022\"},{\"from\":24.5,\"to\":null,\"color\":\"#D81838\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Wind speed card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"m/s\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "wind", + "speed", + "airspeed", + "flow", + "gust" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/wind_speed_card_with_background.json b/application/src/main/data/json/system/widget_types/wind_speed_card_with_background.json new file mode 100644 index 0000000000..9aa904b558 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/wind_speed_card_with_background.json @@ -0,0 +1,32 @@ +{ + "fqn": "wind_speed_card_with_background", + "name": "Wind speed card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays the latest wind speed telemetry in a scalable rectangle card with the background image.", + "descriptor": { + "type": "latest", + "sizeX": 3, + "sizeY": 3, + "resources": [], + "templateHtml": "\n", + "templateCss": "", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.valueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.valueCardWidget.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'speed', label: 'Wind Speed', type: 'timeseries' }];\n }\n };\n};\n\nself.onDestroy = function() {\n};\n", + "settingsSchema": "", + "dataKeySettingsSchema": "", + "settingsDirective": "tb-value-card-widget-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind Speed\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.2392660816082064,\"funcBody\":\"var value = prevValue + Math.random() * 16 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 26) {\\n\\tvalue = 26;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"labelPosition\":\"top\",\"layout\":\"square\",\"showLabel\":true,\"labelFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"labelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showIcon\":true,\"iconSize\":40,\"iconSizeUnit\":\"px\",\"icon\":\"mdi:windsock\",\"iconColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0.2,\"color\":\"#6083EC\"},{\"from\":0.2,\"to\":3.4,\"color\":\"#5579E5\"},{\"from\":3.4,\"to\":8,\"color\":\"#4369DD\"},{\"from\":8,\"to\":10.8,\"color\":\"#2B54CE\"},{\"from\":10.8,\"to\":17.2,\"color\":\"#224AC2\"},{\"from\":17.2,\"to\":24.5,\"color\":\"#F04022\"},{\"from\":24.5,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"valueFont\":{\"size\":36,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\"},\"valueColor\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\",\"rangeList\":[{\"from\":0,\"to\":0.2,\"color\":\"#6083EC\"},{\"from\":0.2,\"to\":3.4,\"color\":\"#5579E5\"},{\"from\":3.4,\"to\":8,\"color\":\"#4369DD\"},{\"from\":8,\"to\":10.8,\"color\":\"#2B54CE\"},{\"from\":10.8,\"to\":17.2,\"color\":\"#224AC2\"},{\"from\":17.2,\"to\":24.5,\"color\":\"#F04022\"},{\"from\":24.5,\"to\":null,\"color\":\"#DE2343\"}]},\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\"},\"dateColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMwAAADMCAMAAAAI/LzAAAADAFBMVEX1+fvZ4enx9vnf5+7V3uTp8fb2+v3L1d3G0Nnd5ezy9/rk7PLp7/Xc5Ov0+Prh6O/v9Pjj6/Dj6fDa4+re5u3n7vTx9ffu8/fT3OPX4Ofm7fPW3+bu8vbCzdbO2eDDztfBzNW/ytPAy9TI0tu+ydLJ09zs8fXFz9i9x9G8xtDR2+Hq7/K5xM7r8/azv8rh6vDM1t/S2+PN1+C7xc/g6fCuusbQ2uLP2eLo7fG4w821wMu3w8zl6u3s8PPr8fexvcng6O3Z3+Ln7O/O193c5OnZ4ebc4eSsucWwvMj4/P3e4+bj6OzQ2eDi5+mqt8TR2uDY3eC1wsva4+jU3OHu8vTW297V3eLl6/Dg5ejX4OWacU3j6vLR2N2fdlLU2d2ie1aheFWkc02icUqedE7j6u6ecUve5+yebEaXbUmheVGabkqbakKWcE6eeVWlm5mifFmWakbm7PLW3uGqhWOlf1ydb0iptsOsn5mac1CaaD+qm5Wsop+hdU+hb0aonpyrhV7f5ummnp6lf1ilfFicdlKUZ0SkdlKog1yod0+YZTyNYTymmJGddFK3wsuwopyviWKaeVypgFqpoJ+bkpGTbEuPZEKmgl+VYjm0jmargFamfFOGXTuqelWUZT6igGOQaEeabEbQ09entcK/wMSdlZa0kWy6rqawjGiupaOZdFSSXjbo7fPIy8+4kmitimawhV3f4uLd39+4u8DAta2jmJahkoutgluleVXPzMmzi2Hk5OTKz9OhnJ6ffV+lc0mVaT+6uLmwrKu5lnCfbUA4LSbR0NDY19fDxcjJw8DDu7a5jmDDyMyxsbW0iFuNa1Cpp6uppKW1qKJjTDt+WTeEXTKzt7y4s7GIY0Xr7u7d29qrrLKkoaXLx8bUyb/Cvr1VQjiNXTXo6emXj45ENSzY1NDNvrLGtaeSclhOOy14Vyu8m3ivknuxg1Otm5CCZ1HCsJ68ppODfn2miGx5XUhwVkONiYycin1tUTq6oIqzmoKaf2m6h1KufUqttLxwaGdeV1cT9trwAAA5CUlEQVR42nyawU5bVxCGXaUSVBCh0CKliYCQgAALJTW0tsCLLMnCSuVVFtnmBdjkLfwS3eYl+838Z2bOva46d66zzad//pk55zL5x+P79+/2fv78+fGz/Sj+evyLJD59+uvTx48f3yseHt4/rJcPS4/7m5u7u7vrK+L8/Ozs7O2b4+Pj09PLy93L3d3LiN13u+/evbu4uNi52NnZ+eOPP25vyZPFYrGar1ar2XQ2e/ny55e8Fr+/fv36d4uffiIzfvV4/svz579Y/Ebw84y0ODh4BowHME5h+UgCESEahxHOAzjLzfJ+eU+I5frqGpTzt7AYyukx/39Y+CHAEorBBAuxMBZiOp29hIUQCyiwiAacJBEL0VgcBxKx8EzAaDSwBAoxgEEYQ2ksFhtwIIEFGMNBl3N0AQaWyyaNaSKgC1h2IkwXwZwYzHwGjQnjJLBAwQsBPw4CikgUkJBCIeFAFiKUAYVEGIMxYYgk4QWFN2CkjMfd3c01QZWdEW+PgeE5vpQs9maJBQohFmK6mk+lzM/gSBiqLHURDM9Pz0Ehi+UZoqQuTjT55/N3AhKZRSxErwpVJpQHHtdFfrlZIgt5dZ2GeQMNgTAG4yj63UkWaLzI0MRx0MUckyygFIxYeOHIIiNB8cd54CCBac6XMoRYhnahxtL+glmDYqZphiFUZFSZWIQiDh7pErKY929XJytI5itDmRVJFBkkpQzSQIEwASOv8GMcPKKhzNTFgBFIGSb7GJHW93AQdCHEcuYw0OAYonRx6/dFdgtKFNkKFBOm8z440mXcxALEQiyOIxqLb8CAAo+zePQogGAYsXwUzPu1oWwosXtkCWHOCS8yhFEHMBjSWZr5hQOKujIxW2CYkTDplwwDsQyWwmkFpvfw8GBiECozFRkkA+fjFuzC4yjWxh7Wa5lfugBzzmO6eF8mTwOFAMRYSNyyI784y2oxn8+AEcpWUw6SoilloiMLR7p8O/h2OJFZjKeqLFDIvsZIYvmwsU6GNKZL0pzljNGUgSWEKcMkjNXYbD5nWs5FE8qM7JIorkvQ9G2sxeE3lZn8QsTU753P03sfDgtNGFDC/jJMo+kamftlZ9iUFydeZPOF6dI6WfUxGzCliz1OEwOmurI6crAQB6aM6/LY9zGAYEmcRuNdbGOzEhaL67ubq+uwjGDenAaNuhgoxSJZQDlZWFNGGbk/zW8oQ2HkfI0YSAJmKIrpYjTA9M6HJWCgIIwjUYChiVkPu8f6+AVhrthjroUCC44hVGSA2Cvzh/edZSXHUGKz2VQsCvllu8AUgskl5qDnUVBmhTLsZGhC9rq0CiNhkV3QhTgXC8K4/wXThBnqgiqgkCYMMAhThhmNfdHwyC9BEkUWIEUzia7sIJ1lrIfJLqRk2WzUxJr1i+asKXNczUyxvcU4CeMSErHMGsl/s4QogrE1edjGngmHx3JSsiSJ9pdOFicxYaolC4Uiy02GZhbdLP1PV24kPOrJ0FiRrQCZbk8YzB8ksBSMzKKmXOYvbZDlSw8z2vfJbrFUH4s1mVRL5nH3a8hozCSMJn+VmAyDMnPjiT5GBMzW2O83GEAUGvsVhwnzZfI4sAuvdNnaYRqLYMz8cHhmJyOMxD0j/4tFMHI/LL7GEFZiQxQiC4x/IQmUmvokb89CEt++EBMgwi9RZAQcPFBImTYnq8RUZNmUWy/D/8CUMCgjEnmfXHhAoi0mzpa1w4hmeHohdRhrpxfPghFNwAxZxh2ZNOcjjElTw9K0gcRDMFIG+6dhYOlWGHVkSKrEwNmqMaEkTPVjIqxf+xhvDyOK6mI6uwwMsxSKerJQJMxVoxELIyYm5viUrFF5wtxnjelX5SoygUgZsiuxQlGIBA7Lr4byAssETIhiKbtYjcXdBX7ZLNd54rfRj/N9vLBfaisjRmcyUKKRae7LMChTwnQDZjj1MUx32g8ct37fxr4CA0kpAwtPrfwoU7LwctpvAQqRfsEwKBMlNurL2vubMCcLV4UHHvd+rws5GPyQGNDQL3UJU6Eqky4WTxPb+TFMsIQuCOMsOlWu196USdUY4+W84fjwbyjk7mhgShgUAYbTpUlDmdUxmYgiG3sm12Q5f8QCR7MLdSYYa83jCyXRuDDrdndBSBVLzX0/wmhanr0pYRKlWBqMyUJImW3DVJW1yPGSx5cxDC91phDMwP5UGfulSHjWHMPI+4IhGDE6wThKbJh2LVObjI2Y1pfjMDZvw3LlfVkkOfq3z5XZlqPIEqaXJeLFF/wPjFBSGFKGacrAE40sWHLCCIXJf+ybjC7MBNNNf819InQpmNfB0suSNHVzSbjxS5c/jYRXKLDgGDzzCIucHyx1c2k9LK5hxKI+BgzeFwoksr+P/tgxE8WXSzUyXrSZATNcYjrHaPbXjNGoHC38kiY0ocJeOI2UUQAiklAGTTbIAgoRLF5iEuZtbTGgyDKCCcNElYmlnfp1hJkxLMP7FFkH49dK6RhQSN/HGC7SxB6PDx9sjwEmlWltTDBO4TBUmIQBxJ6uj7UrP3CikRGpTHWymv1l/dyVxSKaEqZkGVyOj73vcUD6hPlqyjzBYjBAqMjUkgFRT4aHAgth5JaaMG1YtgkzuvszkoRpJKTjzPujZcBkjNeY8H4ZpjOL9+UXxN4e0gADhSLMolbm1pdd1MhA0ZcLwwFFa/9Za8qyv8HogAnKRTjGNeGdTmPAJM2YpdnfQWpabutyII4DVDFZgHl6evrxNPHrpFrI6uqCkCy2+DNdzP2hS9z4k8cyPykWFVndK4cw8MwI3ff/34gZHfgLRjxNkA/2g/+hwTNPX6BBma4jh/dhQRe0aSxEbv1ZZQ5DV7YaAyTWZX2+iCrjMvbkVijTtvfXRrY9+7f90kDqwC/zw/GVH6uxwxd7lBkkDgNJllfAMCvJaMqa/S6MrTG5KYOjRla6lDAK68vyCiDTWpXHfblI+IElaba2MaFkY/YqU5EBoy8W9SWJNL9ovPTK8IYuZ5r+0Za1lkEzPizL/l5gGB+Y6RZMsWi5JIslr8ZzUW4sH1wZSAi8v2cor55eTdz5HYyfxqgxWDYOEl1ZnTnMHyy1lJ3Wfpl+UVvG+2rLRI8imHGNCaSO+9swbP68yAKJtzJJ8wMYZxGKFuX1MpS5ycOYZr9gNPpzYKopSxYJU53M7H8Ch45jhrMlS7EUjWCqK+cSIxhZn1RXJvdgIQxGVknvL4GxPkaC049+8lzhPIDwNJboysTggIkuHnZ9kboUTeHoQ3I1MkAEEyCa/wO7iObVXsOZgFIsa9PGnL8GBZL2OUmjX3eXeU0mUcL89W2pP5RZAPO3OaYt/qMai9CAYbrwpDCKwT6mJvahWQZVjMZ4sAzK5OmFxPzvw/xgtGMyMN3sJ2Fxy6grH6csvBcpzI4OyzYs/5ZhxKIoWcr+tfPXfOk3Ze/IsNjrmviPByTExHXhBcSjscBRG1nBVJH5iIn7mDrFsMnEiCEWJ3IMCcqYpWBqvig6YXpZuLzwRob5SQJlXBeEgWXfYBQGQhLN/UvRwJJnS17jiXnZzmOkCSPD5BflvMOABf//10k5JwxZODpabm1kLgxIBgJJ2F80hCtT5t88aOzbU9PSZVFXbhcY9mrvtzXGYlfmz78nuaUtG8rcWFZog/fJIEnvR8TnpHLMqMLUlJEkvc9b5rcy299/NWlF5rE0Uci47qsb8rzA0IW/YN4O1xixVCNTK6POxhcYW4ZpLFVh5RdAsinjFnXk6smEaBDm6GjSzB9+8SIzjhgy13lHLuuT4Gj3l1t4c8B4gOKzn4BmtnIYlkvRjOY+UZOfCithVGVw8NiCbOEHGHk/7KJ4OjraBya/vEKy1N2lCROyiEbKGAuPWPB/62POQpYuYtFJeWqr//DKj8QvGcPJrxGjME1AMRZfk/eMpnk/rc/PETj7+/vAEILxMtP9RW8YsbjzmywSps39KDKEkfe9xGR9fYXtv8FKGKJu/FwYXZE1WQYw8n5UWQvjEAgBzI8jPPNDyrQSQ5J1kpRh0vxxTQZMu4yFpgkzGPzgWEyhQRVQssRIkUSMTzBEsUR81dYfDblgFJB4TLzI+jUGx9TW7zQ1+GnKHtTYaOvf1UZWX5TrXqmsXzdL/bQc3vXHR/4ShmydjBxVmJfY0VGjmajA6MnmGOxSH2FKFtICFCsyDRlChhnfxcYWs5hqwxwcx9wvPQnZdTKyigyIRmIce77A8KtJCYhEaTT7vK6MSoxH1vdjchimv/KzlDAyDONFZlGVqcZgWfzLt/mz/hxFcVxJEYtBWaSIIiVllsmkyOQBmK3KIJ6AwTOQ1WizMGCgeEhe97yd87q+/pzv/fzMr973fc+55x5bxx+/OBeXgGUcQ4jCB4qlsqWlTbJiAcWAhYgysAQHv5T1YfEBtn7ssfF+DJMyBhTf+1bqb++HBVlWuiREcQAjkREfM8xUylBYxmSXlWFA2WHAgCa6AKP5vVmii0VMSIKSLRZhfLq8BAosNX1VogSmN9l/SuVJ/b9fYSIJa+VKQ8O4xaIKX8HA0tVYhedYhLkiSyj4CqaLyyph+jZWGAunMj8ZplHmWBbFprI3mIaZd7EcY3F/UPjcYvwAaRqUWd6PKtljbX0+J3zt+G26pOYPyqJJelnKVI75e4YRxucxO2Qm/uJZLHGLZTLFy4FjgIlnkvV9S64bzK8KZp75gwNMVCFqOta7WJQphrvQEJklUxfLS4VJugyKeX+/jpH3Cfqwp8n9FslA1FckJU1g2GSkGEdhfYCF5LdnGBtLdpXnGZZAlppUrEsMYTOmUHrMpx9iPcoc7Iv9+0ieo+wR+XK/vfwTBpAoY5VcMJNcrGLs99siB8Vh5TU/eu8eqvSMHyB2yJ2LO3RMaCa/2PBbMLkgpxcDScEAweJfQIxbR7YuLOFJBksKZR+V+M45UdYwFy367+L4dX0JTCbirS4P78nCsMe88XehDAiL2z5/unEpTEV8bxzp7njPwxGT9sGAxUMZVVhh0TE2le+xt+aeXGXMmN/RS4VJxDDiAALOqpRxDCgVbrKpYAhRjrUy1SDbu0qdLTvv94MycT412bBsQxg45R7FPhQ9hBGWwOCVw3kSU/++yW7bikWWjq6U+QGSJQ0wxK1TRxAmJOwvYOYU0y8XtuqyAhgN0ywcyPfuXSUY8LtXnWVIJuxfHPb7PZYVJt6HBaDllQWjKqzOlay4ZaHcugWMfmGX1ctF7sj6RWUaxuYlJMmWlfEDc5c/u2FWYPyB8aFfmImpx0B5AIU9WFiaJDC9x6IMMIAE5uAxOfYHJThUMd3t5zuedkzMj12S+iGBBU3Wz/Ky50lEYZ+FxZf+gNwsYeDIFjv99vH9xRHjm/NZSGIgC0BLmWuiXOvXsUWSuDDCsEoWwlGf7Q22dClJrp51ngQWrzCs/X3sT13K+ZtbTr559+VkR5djfYix2jCtDDDxi9d9dOnoG0zhBIXInX9gostyPhgsfontGWZXhqVfYn6VgYd78tj+y7s3hTF+abvo/Py5dRkYWA6zpeYHBJy2f4qyacVCY9m/CktgiLPusRV/Kfx9uKzQ/KgS53dRVjTfnne5b3qRJDT8bh0DJomydtg2rzSpn9W6ELPH7F7+uvEDwroRnlbloBXr/4UpmOAoCzBHcX153/P47dMfdwbE8zgM/IhQLWUSppfAaJli6ceL2WTbM8w4pj512fLl3wp/YLqz1DR1hUGSeKZblp+evHr/R8YPSeySaBisn5ZyrD8sKfwDs9m/YS7lCbYKTPRIcokuooxlCNv9tpVzjbF/wdgFCxKbMK+fPP5Shm+viEPIUjDe9k0ve03GInyJSep3l9Xlcu2vxUIMi5tM9xdL9ljyfqNwjiXt9w2mWLD+mUXz4XQABGENR7EAE+tbwYwwwniN2SYws8tSYMKyGOo8Pny6kIUQZm+SmWPYZNT7ZHx7/HWAfX7y+OOJBUBofQiUJTCLxFZfw2j9DF9bx4TFYdK2f1GQZJyIJTyWQ+KTcub7hMH8U12GpFjQZRnl9cvH3x/GI2N5QpbAmPTBOLA+NBYyqf2lcZQ81/1FktAvB7dL32HUpQOS6+giytQvxNLm2Y/XQXB35Q8ofJcLBlkqwmJM+9I5n/0ZNiRmzDrFWKA4fpUMY+r38cIaRpbryzVlFuvKaYq9fvnsxdeRQhR+CWiAcYok3m/zF4kwjiwTv+2yuP+XKqNMb7Jw7H6J+4/ushQLkVtlNWGq3j+z0/yk42xCZwqjMH7DVkbRbG6Kcot/tyEpK2VLsiGKrKztNPWfGqzVXYyNrUgWyneNj8SCKGIWJJ8bsdDkowghzzmP8z7XizNzh6Vfz/uc877nPdfg4LHF+FcbBtQhQrBUVRcwlAUkGr3y0F6ZN8pGYi1MhN4iS46JJPZnLwYfFUslZY2RJu8bCy8uEOEX9ZLMKItvNv2D1zc5DCHaspQQpizWpHMYhaEokZMRAZPdkLUvYjgQ6zC5X+h9HZRjwg8wYqFfer6LSdZPLctUU0AzunFAayuWV9k1w5RlWUR1IYnmYkIW2kXn/tar8DF+aTC/7fLXO72BwxZ5bn7i0C8L0iIDS/uorwI5NpqNNVCCpsafdV1VZV0bDEGYlYGhRaYGJnXhIYYweo1kiQujppJp422lkCWtMi0ytS8ljCkTO2Vt+AXDFPasGYwe7K6qujItDKECA0CA4so4i5meD7dj0fGLPEZhsiF/V8bv+lduUdnXCsvyGFmydgxYenR/ThKZjDTMYNW4aUajK1VlPDU0cUHwOEy3oCbGQRoEMCALJ+OigxH34xZ6r5dpOXKZg+BHMBEOErPKXGOSJVAII5rsdOwwps1gdMqkqKv1ddktGd0uYdi1BAUjK/vRXNIoOWE0grFkpcOIRihyfigD66vEKJFJGILkJMzFcEgNmmb0ADQMQBGla1GsYBILDp2RCcPJOKFEWvY7Mk/LSxxFF5deKmX8/FLpT2EMhTC9QOEFH0Byw1gxqeuZ25PByfunSpeCGAz8xRMAUHiqTHt+bi1zv5BFFcamFpxmdbBQF3Uvs2IJkn9WS+wu2ytMMDqBOY75vXoxOek0wBFIxz5F1P1UXEAjFL1HwjkfwXD62iK7UYrDi4SZn2/ItMS0xjRspb7Fn50kfKoKNpl50Ry//2SDkSg6ePYURInwFcaIWqkRfwqjaqmWjKEkmnzsWplMdlEi6+3s9Rb2Yndpq0ynfaJoM1ZjmYFh5s6Fw/cfrSNEsMzsunu1iCMlvrI+UBKMzL+cx5hVpgs+zoKWrPVj2tVSbhENC0wuTCwywkiYzPwR8H/tTjn38ueN61cMhE+nu+PY04PDvsMYR2u7n0+Rc5Y0H1hEuP3jpavsVbjEk5s/94sJAxCXhUES+CVQmMlQWpi6Nn36cHHy6tZWs4nFzNG7V4f9pmkKTSmzbWmhc+XvFZaOyhwlSyVmi2fl/7zTmzaYqdufDZN44QdIz3uvLkxWKsUCUaraFhnixcXpdPbk/aNbAdLtboAoo8biToEa47ooJfM+iXd9ulUCTezIXBkmMthf5kfkN+NJFnWVpItpYiwxcCXvAwYgbRZ4HzBMYC+az7P94YdPDx/PWXf07tOBgfQn402dgoksdWHcMiGLkjIt054m4yyp5WWxKJUxVCrtkV9U+aGKJvpofhUXseCHuniM+x+mX6fD0acH48P95v3795Pzz86WczoGozsLRyFLON9haJmcxau/WSZOMf8s/ezEmDRqxWgXEw0l/JDEUzLsQpbg4S4MMIbT2Tx9s//SkbfTwYXP3768f/38zDp4Byydgn6JG0slMrGwsWyGYel3mNjHcJI0e1Mhb/cFDEnIMtdLf3Qutmsbo4wsv5hlysqU8eS1+d2Re9+/7/s2/fz29rrOHARYDAYg9ItQkJDFogaGWhiA8bxM+2uRzcvqvk7Ji1j7FUQJFrmfKIuFgsfri2dlssD2l99fO/3x88dvLxwFJP5T/K4w9L2ukzz01lW270dwU/b//zZCwuimTyxcY6gvXGPBok6yc6i+yDAd4sy8fXvo0Ns7gPDQMnO/KCHjSxCEWATDpGx2MZg/u/2qL+BgaB5WdqH5OQNLXdJlpZp8oMGXzneYkrq4Qarp9NDe5sycgHGaIrFIlTSuaB+AAEYo7TVm871Z71KywPiqL1JFMD2DSXNw7VmrxfQKpQFHxSXGAA30KJvZQ8MTZcc4iIOnEIudxJa23+oFj3KyWCKPrebUAkPeZygl6z5JMKyV4Rh1+1KFIY3XF6MpLcBBGo/J7Oxo7Bz0i32LZbp+QdNy6do/8rHeH2fHL9Iypvtdlv/M9mdvWwVKLksvmZ8k2pGBxC3Dul/WOn8BhW4/Mbt3tE2O8Si8WOow9kNtGI+wi3RhxOR1Oyfjm8OkK4sIsiDII+8HB2Ha3gdPt04o+LpBHg33HtxBBqAEDEhiHA7PL77O5zWuKorjz1hTGetoq0arNSpilcbfqAQxkGRiF7VhJLGtxeIqdBloNgUDoVkXXWT+gjBhyCILm1EInSySRZMMGUiySDE/DLSIrly4sAuh4Pec7733vHdf9Mx9Q3GVD9/zPefc+948H+pgSZjoqVg+3g8cdhiFyf0UJkPjn1bCyo4x32AxyezmvtGkJ34E9saQhWXZK0OYPk9hMN76egS7+6rIYjBUJvRKNQyqssKExm9Hygy7aWHmj4ShY6LnR5TEdCEKlpnfhcJMTF44FMYOxx6+nZlgLMmoC8eYM52ZDpO1vh1ceuvHLCoNniATkOPRQEYSBlmgSwiSSAyOT01MTscwnJThfVaxTz9OqcKbsOkfXYEEoT9Rthv9mTfG+BwjSX4zRl2+sRxjlvHGvjV+oCiOljFZAYY4O7OAmYthOFuGX/S9tetlsdN+CaJooF/6IwyrybFbgl/ysjj/c8S0raXpwnsuDgXm59FLlmZhCzCreRjesWDLf/zlNIy+As/etORYPmGLySdZ9h2kEQqFoTJ4gJTC+FHZd36S8IuVzJKMBzBCIgsHThOT1bZCFoZ+oS5i/T0PE7bJdD8wyAJZzn+QdQyCSRZYGIBJNUtLMpDoxd4fK2NFmfNlQOEoQ//XBWZJEQwo4b7S9mF7rxCGKOH5/jBeujomm5j4BCMIAwy/fwEOVREa6zDgMGWIgsVuyQvBmcxqMkPbSn+9NjUxuksOg6Ewdgiz8bJsX3DZo6S+8Zd5X4ne96oQJl3GouNkRmD56LhuL9MP9+SLcthZwjVRYWa7v1WrQZmNGAbC+CQjzFsQRRwTUNhiSppjZffCKHwOG2LCG+KwMhweBQWZ+33ScFROP88Xj/2GAnVcv8RaEZjRzacL2TRTXewx5cd3v3fGtwTzryc6c54ozDErZPhYHYsnS7N+NMJYjlEXXFaWQ7CQcY4JbQb+1zTb68oIU0hcjpEForTmIYmwWIaVVBe+W5VJZix2z9JYLMVoFV+Tv2Hfjzq/0eijiY7Hs2hEhgFMs16fGh3d+6LQVsAyZYREWQizve108eewpVJJq/IH3IylTsmOOBYsa5VayeL7+xSGLGFjSV1whX5pYcJYIWOaaZyq1etIs8mzYAEMibwynMcI8/D1o/Z2AoB0gga66HOXiOyTZI9QGbIEGMswhdAvO75wdlEYfGgZSsN7r2DB59CZzMF8XftdYBa/BYvqgm/8K3kzusW33UqXMcCABCxlwEhEP+m1xq/TZe4IlhyhvcgVYACCsKGMjZ9JxoZpjT/4hXHLKdMoMBxNQhR7I+n8runyfun9UqfKArtoIVMURubBK2cZ31/sJt/I8dzmxapy2jEMEQYgElbInCpYPhZq9ea4DGeFYiEEYdAm7R2+gCEIaUoloKCOEcXafnYiG8I9GHz4AFmkCzBG7Lgv02DYYjJTGbOMKKfiFAuxVmsu4N7mZNVRGIyNyWABjJqfumiOQRkW5Rdy50rRbYtckvW+1yt6cIJheByTJWqYFMaTpIdLi/rvzQvj45OT8yAopmCYYzZbvrv7BiU5+iFkERI1DA/74+fIYpS4kjG/GBmSJyhLTheO/rYjI0+cZk/X6guD4xgBWpEyvPti+30oQ8M4FCx0mGySxW8li56EJwiuXsKcsHpMlJBl3MWYMEwxDpjml5wwg7X6Sr/A7HphinpRGbIQpgUYsrCOAaYcNsmH3x/jOWwWBii9hAFGxAIQf0hu7re5n6cxTDQHIizGs7NVH+wRmI1TxWKRJD7NTBl4f75qumCxkuWECY9f2Fm/sKQPlQILSAhDEAbbJWGMRWicY96JdbEoLNSazz8vMHtdRYSgEMZ0eUVny+0qWRBkcSRY/20Yex8ZgrI82jviLOONYjAKYubPd/6okMUwzdp+4elZNJq9z4CSAEWAAGP3kv7ell65vVo6WrIkw06MW+RM5798+fLnn+cOMIhiOUYW27r49AosjP+ZYrA8SZoHlim0zc6OT+2dKyaFpOC1SUKObd+vrjb6+rbnOtlfCJP/v3aQ5eRJ0Aw9ElLMzvo9DIKjGHku+V2lZpeRYFmKKc6prDCew4T5bKt+r1hYFpgvi6BpB496JwlnsK2fF//66+/Wzr2vIQwNA/MzyQwFnyMqDOKkwAy5XQyfiAtJhg6DLiOlzGSxBPO6hK6PryjFrFnGLIUft5ptAgPTfJsk8Eq740lCHav+/ODnm1OT9+8vV6e9MDyHIYsFUARmiAGaePSHKILCMTkN411v3QVXZJd3sihRFBALtX3ocFdgphOE0BRUoNSJcmPpnwcP/vhz+bvK5vybyDAzDMtyYKEwDmVgYIC7ZU4yZMH1HmhsrJQr1GIGVTEUhnFYpzQSCfj/FrLqQGDmimBJgIEP0swOLuH+mcr1PxtHD3DvY2ajqjTutO+ikBDlJchiygwMAQY4AwNel2d7GSKMPXjB5mK2VxCTxavCfo91uF/YH2v1fsCsCcw6YNq1KsuHMAiBaVQqtSYSrDG//N3Nm4vzDTzghzHm4sWLmSQDCVkA8gyUkYAsj2IfIyiijExlNu+TxHyP7IpnMboFJHkOQyFL/1az4GGqgEnEM5AFi0cXFGa9MvtrAzBYr6/eHb/50/L68Bmogo+hCMxJsHyuECSRC7pcvdp71Qljh5YStAs9j0/WKliaXlgUxmAiFsKsbC3A68V9gZlPigKDi55JHY5XK3fvsFuqXxoHsz9tVsvDw6oMUbCgSkgyFyjLV7EAYzkmHNHDCmFzbLJg8T6yTfxtJMkL43D2a7eLoLkjMNtgoWl0NtNHYhzN/P2lMRHmQzfElMvTq8uL82cuHiGNkWDB+x4GJBKGIjDpQkYWk8VgNDiJ+XE/QxGbHxj1eocgrAhMC/9CaDErtgPGlJmvHIz5sR+7sTJohoevrA8ThGXMhVcFliEIAklGVYiCLGPkDmAjlOgMpu1QuwSYjq01ESbZEZiNtqRdaNhqEkXhdqzUqqwN++GS0iDDJIJZWJMFZciJwugFiQ+yWFXOsKQ0Oa3LNvpudtErhxKUKdze2hGW5Da2mhibVRnVR2BeDyyPL1YWymAhjbKUyUJZgiisyep8M4sPohDHdInPk8AhPHB9fARzeIqZMndqXQpzb3xqamqjhzDu8jkGmOpM5U5ZUgxRRgwjBIWGUWFolwGyBJqYBSDaYuyhK7KYV04j3MbFdKEkmS6pJKkkQzSb/MN/kTTb7E/aFYShLMTZrFRWcHqBD2HOqC6pmswMi3Isr8sJLHqf1qcwXce6HuuyFOPWWIUBRv4ExniUIsC0ba0UE2mVPQKzN9iunsnBrE7MzOwAphNbfoBQmCOkoSoID2Mo8It3zFPvMexVfakxueuYspwWmlgW65U5FJOFMLd/uEchOgRm6mzSrjiUByCKUiotL83M3Dp/vlSmLkJj3qcwl5FkysKgLo9eRdMXFAklMe+DRoWBLCBRjJBiUo9JErFEOUYKfsMy9QSBv7+4PD41OnkBLF4bwkwLy9zU+szMj2WmGFloF+Wh/0u4QGJ+0YJsryMZOX5ixGS59kT3c91C8lxXF3TRZkmU8Ni7qWIl2YgIYrqoZRaSInHuypnmtCmD/wRZGLtVgQEKFlEAo1mGcDlWHoYw3jBPOpZe92KlEf0xj7XK7m4hERiEqnLsNAMcRIlgsDxNQS/D0KuIa+tcBqYBFMLIN0kgzCaq2ejtcvmKCeNbjDmmoSQMokhQFawwJV+7dKkbQRR4H+ZXFMqiNEiy7DCmX6aNwSgJI/mxBvvzL2/+AJh1E0ZhSLO7XtqemQRMigUr0HiYITM/ZaEqIgwyTAMgEi92B1m6jvX0HDNR7DcJabtkXBPVY5KIZfZ9TrUfXJ8YHa0CxnBUFgizW+psVaYGPYrA+Cbzkg39DTcr3xjQ7uJqMkgU5QRANK4pjIIwAEMWg0k7BkzW/aMyhuwyYZKlQc/Svnb9+ujoEmEcDlnebzU6O3crswEGLESxsjyEuNAgDGUhiaBgUReA9F/r7ldpFKOnqweqgKXHYASEwqSjg67J9fxMHASWZCHACIoCgQWxvl0qXdmoLH/tUQhjnd93mPVzrGNK86yD6QUKYEZUmG6imCggIQu8YqrQLTENwsxvLKZMm4dJkt8A81MrcSy6Shqto6UrnXuVu+eURWjyfjkrMKsCcwMkEiARnBGyEKW/X0iMBdKARLPM7GLn+0YiEc/7sS4kcdKs/ABllouURi/CrK7j6/xiZems+YVBmj6BUefPDYNFQ3OMLAYjKNTFYBCZgkwa2MQCGB6kwA9ZPEyiHCkYAdiRNNvsUAwGYV6TryuLlbU+wniWMbD0KYnvL2NzjuVqL3FGvmCKAQQozDAsI/Ew5hbBEYRTHcAIklCXQtResrI4Gv71tyTN9r7yIB5GUUo4m9nvG2aSEWXsSN8YWcIe+cbqDRdK4mT57JKIEmQxFuIoCpYPVq+OTEQFOfZLLAzinigz2U8WU4Y0/7JdPqtNBWEUv7hQcFGy0o1rN76Ai3SRioEmXLhgIhFCHsGFG6GC1HXBhXmCkCAusqgxQmmyaBdNKxFaFy1WS8Hg1kcQPN+cmfmcmXvunyS7/DjfmTkzwwngKeMPZ8AC1SCbmBCGvjQFpopHXPG+BCOG576KTPCDKO4zrZUpi5JYPXj7ErtmHsDcwiUs3eWov2D2SUMYsNAbCzNuCAqmrAkWqCraqG4QZsOQqDP0hWOGy7vD+cJD2dTzXYISRN/h3BBnntcDmMEjsnSHOAE8cSwvRBiyx6Dxh0tUzO1ejyPWtIIzVYdSFn6QeGsUJh6wZN8P01LqTPZWYNoBDFmgCU4ADuYFBRKff7MmnzYavTzP6Yr1BTSECQOzjgssuO9ZkJazxcBUhIM0zpngUKmmeBa3oVh9kD4zCGCIgnp5Ppruk0U4CPPMoNTrDQg0tf6n68XVeDwbQO26wAgJbjUmnjFRSx7CAORuJfWlBCTzngRDpv/9G2Cm4wCGLKD52p8DxtEwMPVnIAGKYelsbx+9//xnbzk5Pz+fiIbLcVc4lIXmAMSy4AYHYfyI3cNFhRuME0mS3BNFaS4wZtPjEKZrYX6gZypLURPVjRpUZ7uTH+2c5BwwLwdDHO1kygIawshTERBVWVxoTRYEHygEUX15ucXarDBdq95Z/zAvLAmkMCQxyscXhaYlWMe4LmtkIMHwSqPP0CgKIIK4BO7QF5WtzVNpmpu4A2d6vZ/9w/0XLwBDktgYw5IfHNAY4igQaNIW01pvGU9EipLAYElO234UFqLEMGya6ow3potqtv+kKMCSOtOh8vqrawujJDpjbotZd/FPfIlGTNcxlZIYAop9JdIexmx6uhaNGWFG/etG4UhCFogwR69PLEqUGV/JGBdKUVRpXDQrsTHqSql+C8zPG/J1Exed4ZTNUM3qBUWYehyZPP/y+qoZ0+hSFs4Yoy9iL+MaVtEJU1/iZklb4gmLmS7fbm1N5y0hIVBmTOm12+0xqlmNJN4YysFs57X3H64sikrrZdQuW1zHQOIToyhUaX+xnfK23ycTMfT7LwVmYxO6uSkyMFC7LdWMLAGKgxGcfO/k7CiOf+yL22Eo8NAX0igLnhgmGrE7GVVOAv0yMLmBMTQZUAgzHPUvmXs8CqMrAGrMRTEnTLUZBUZnzO+VKrO53KuUZv9GdKK8nYUwpa4wJOtmzD66KcOY0RfooD/a1bwgMDpl1pq8uK7PBwoDFA2/0rD1q8oX5aDEOBDnCmFSlMxGnVa0BOb5QjzhoGUIjMD0lqf95/sOhoqHLL9qF4TREYvTrzNGuSGrBOFPK0zQjhmVe+me71j4uSYw08GmV+aMKX6s3uUAURKq+G8xG+bt+UITE/TL5ADDDsPoEyOpY+EalvH2ehCQZBYlkz8tzuDrN3FmTF8URnS2+vAR34hi1+X2eMApM4npDfLu/GNkzMMEJpgwQ6MoyqIVJkk+rbndinwxfzmTW6Dk56cdODMkjMkMWaD56vDy9LhdKzzK4Hisuwxolnk+mO8CxRlTjVlYLuMZI0sFl24vQWRSGlC01siSuQ9YgcvfhNlBOSOJuNZr05veaHXR6Z2fztyczSbLesDSGeTN8ZsOwt/UGuOk+0uS/QrPLxVtYwQxr6rbJmOY7D7j79jI40AYmy+A2fkqI8YHzhCmO1pdN2rF7Oe5mbTx32Mxxw8ZhFdzOQdLEH6qZIfR4340ZU7GloYexaKNvypEXoaFNNYhaZpbWzufP2TcaADnYWb9lbSZWnsyLIaDybIgirIAJW8ev6taJSeYdMagVgUw/4GYR0AME2hqhPE1RrW7i/9ODH44JMZHvuwJzLs1eEJlAmLazAptxqxmg7NRtxbtlx3SNIdnGpfQl3KYijXGASkVJHEpdMPUYzK0tntxQgAg8OXAdI3+LTBvsFBQhBENV/2FrWZnV3Etyy1Lc3KYtstgWY7zwsAkus03YGCKtkuP0qquH/1uhWVTs+NeC4F5/p1kMMsvzZPV6tIWs/ki6GWeBTo43CitMeukYRmjzIzdrdCZVP8INX+XxsEwjoeOHTrmlvsP7l+4Dr3B4SDgkIAcd95wHBzccoscN9w/0OEInptUEF0Eh/dFxRJ/pEVaCxZUDJoqipWmoEt0CLSb3zdP+yb2bfGb5EnWD988P/KQnB4XMgsoKWNI2SxCnfmgUUQkBNOKYZI1IKoZWdOMVnkBwmu2ixJAJDhTxkDNc5kzSuNXKxnVMh0yR43BkRHOzOYQU7vkhIlzzfQVFOS/4CEq5zdgNjkwyBrNGugyGk4zOwSTjDJDGOBUK2rrB0+8jSGWAZBs/hOMAUIcLaDoYxfKfBbjWV6hScP4MYw19EWTMFfRvEND89EuA4xkeeHMyQBmXPN/Nzosg+YNbfxMU4WhxCmAZfznJaOfSRVnZPqAVcD8rw2TKC9hVqOqQ91/5YoRC2OUMQmLcXIzMpYleUNEyVKGzDHf4pzoTMao+tIaqmXSH2ddIxoFRxqUEzBbh4MtlJHRrGkhy4ruztlnSyRN5XiqwEEiRCkj9XG+Ed9nGOcdqFxuNIKgG4RhN8QFBeIIgmuo02k57bbv501oLE+7NlNjH7J6ugbIYdMgGABO1j8Bc0Pv3Ps8cga9BVp+uqusoPnjsdLkiLzAOZ/m9U6tXL5ubIThY7/X79t7e7a9ZNtrPy6Kc7/O3G/Fouvdex7i2b1b8vC86P38c1s6cEvu3/257e9fPn1dWHp46PUewy4AW44EM+tc13d0Y4pKAOEkZaB9iNsrOhUwz4ScS6sTMRTHwQ/g0m9zN27l7lwVCjcpwykTFwMaNNCs6oRkFKXiOJRJcED0ahe+Fla7GB+jqBsRR2wFhV614qUL5Qrq3Ygno4Ko6L/NKXm0nF//JPNknjUwK6voztcT5589vH3v6tdXz25df3ju2MFPy0/bX7buXFzuO1R/zCu3ubmZYI5z5kT2FFXneZ3X3FUyf5omSZrEeV3nSZ6n8c2oG7d4IKWWJaVQllUFEjQnWgIAd65mNzuD/etby/dvFy8Xk9neXfdx+3l5947fr2Bg2X2/wfqnO+c8zO3VyerKbNXX76LevX737snnR9N682mSJPN5JiqRsSiKw6IoVCvGjPMk7qZR1GofXev02p04diNRBHaslOqMrbaaKKp6BIuiWLFKE3xpySRQLJJqahlQYoFhINbSstTDG+v7v7x9iZpMNn6uAz+xZmcw/Mechx7mPB6S7r3e3LYx/VxPp51WJwzDsQrjqN/H6ALVboXdbhfZ8jRH1ViQyFvh0sS5JHMuGzGAUhOiKQZqCSXoBxAgpAiI9DWqCJWEMSYZAGitr+mSepuYybJRZoyRpZbWWqXubH/Y+rJYTFZWdjRIs1u/n0D/Uw88zONdfhPbOHN0bV8atdYGh9K4d+l4Ky7Gg7UojwMedOM4DOIUcfD9ncfN52I+yjIQwlSYT4XpMCEECClBgAFGOeUMMTyeh9N2iLiaQgNLpTRGEEa1zx+7G6oRymSGoZ/XlB3315fLD4uXb85tzHCR+A7zd4t2vmj2NGfY9/yyH3DqwIHDca+91j+ZRJ2w305cUCjVdayQlhM7tMOCOVNVjLk0YsKn3oSqMhhMJfDDZCjJmASlPIjH4Ez+QEBpbPAopMlf3HTCAAkEAA1I8w1mjP8RYbwEo0RJUBD018+e3d7GtcOvj88nSPfLYQKadr3Z09yDLVc2Ztiy7/S+C3mdHg3brTTmw0s2ETAeDEL0n4DGOSyoxDR89pqyqqwkSkisygrKgtNCccI5p5TjQB4AB00ACl5IzhjhBAM0FgBIqkgPp9lYjXF68dihKcIxx0khwPYUoYyBBESS2OOhwDP6UZn/C3mnf2pric9nQ9882sq9g0fQmSto35XVDYSZ/lB6+mSSx5G9VhgDxF4aEiakLHVJSyYBqGQVLauypEERhCHvhq3ut5Kr9rV1Kg7v7zxwONkOwZwsIYGkGDESSNJl7gaTSBrT3Lu2X2rthbaKkNoPtVD8NGg/9ArDT5tM3LyWgW44vBP1if5OQ15Wmt9znpdzJqsnCpGJK7uu2xrAdKYsy6bcPmt3FBWj3zE7Kkppm+3BB4f4U+u45XKVc4j40IU78X31GCnXNgdHJyZvDQamqYLJ1ol7BPSHp99i5o6OoWoo8fo1CkYDdf9NjRovf8Wq+eLTb3768scfvmo2crdpepvebreX/fzyzUuk1PHp8WfnR98e1q8OV8dobuXyVmvunmC4aEZFL1wKGJekmGhE5YQoitlWuIIiuWIqmhaauUZiShwaU1UihMUqUU1F4icqiWPiUoUDhexSKjFmE4pnkkolKtkGlWKMLMhmh/M6C+aLRVLO6nr26vSooQmImvEa4m6knYKZFx9+/i5Vxumvf/588HtvqQGLNtYdfYym2oPjj1bnxx+cAxbRuODB3J+7akwIkhoJp5EOcRgl8VzSNQcP22aokX6uhQ6lzCgMOwj8TPKZ77CmP4nhkIyAGTEZtE7QfUA5ZkSiqsnRvl0UFmpq2VYxtSwpkOyAuy6dt+Yns3ruDevEfVUnWTKZzerNBsL/CGHx+uPXn11fX+fv//33iw+/fpcayjm8clBYtm4XId6oKEJut1posEWUQQvz6Pq+kKtqLlw5kgjTNeKMNSXXzI4y1jRHG2u5BhB5P9RDXdcp9906y+SWJ5gvBCUUvM3J3A/ApUoYd+EWaLQtuUpMYvBIbC/yPCCZAgfKtucqIZJidmRzMFDlufwqK4ezkTVLFpvNxcXF8/Pz5vmv/X7zvL/+a/9X/P4//7z45GvtXc+e1WVy4FsByA7NlZzBDbxtamkessgPut2sOytHo8SLMpYJFNENZ9y0bej62GlOjlSEoSRFwCwCPXOr6tXpqewSsao4r6pKCC4Gg7nrnuAhESeqHAT1HCChLCmwHUycy4PMiyK7MPCxConGRHWJYsqK2moNDg/xg7NZNEzKi80zsPz1/f74F/xuXa6j8u3kxW+/vfji68+0W6390cevD+SWyisS8AGyQnVDaHg6SpJuMiwXizLBqcETAVCGmdOnehFZVmgYlLqi9mPoxBesyqK6213U3QrjSGSujHxrzN0KhELlVpvIFae2ZLZNk8s0iwLGMiZZViBJtpRlHmbLplJhF+PxdmtIBmxDQZ2pyq1XR1ho66xOJhclqJns98eVWB2vzrqLzcX6j9+A5vNKpKl3zPhBE0VIUOXsfBUyv/JGyXA9TABkWG72i7Lb7SYjjGg4xcnTmZC5KrywrZ63P3st7DAkwjOo3M1E3cxhV4Ak5eQ9N9C5NBcU3qBE05RVhzucOUgDzgkzmC/5BpOkAK/MQE5Ag8Iybp+enrZLyy6WYyPuGHoHMu47RG2Epyj9y8fH3e7u7gqfN7u7N+T6+5e7pze/NPU9xur7g9X5OSZM6ecKZ1HkTYbDxWKWJLNyUZazbuJ5XdATJR7UBt34gTyI805b06jgZ6uWP7UKZjELX6vqCshd9OoIpAVjhmGjL9sxwtAQJu8QSvAOm4UaRVhIQATjwEgBimj50273sE0tg+qGDcUZRtrrgafl7f3D4+7q5gZ7yJu7q8eH7Xac9iAQBF59irjHvv36+uUbnFYHIRcRxKUXSbmYTBYLYFh43nQ0GtXDdemPcDWcDqfGKPINik0xcTp5v9+Pc13hNPrPvxEslgm/qtwKSR4IGhJDD6kHVwOOExo6N4nkaLqmCSZ0YiggiIImxzYkYpray6e7m6vHrR5LuhHaABProQEU97urq7ub726urnaP99s0NXRJcfqaQpAtFKlHzrjJrVFWZ16S2XN6YEce3lqWYGSzB5TJZFiu3759u14DwxpIRqNy5g2nFrJ2nGuOojtaDnE5xrSwpsNCCM/3mRCVL1xXVlzKVy3u94lwzYADLwtJ6NhNaithAQhM96nC9JA5mhHqyMPLp7vvbnbbbdMs8nEM2zw87QCiIWL3cH9/m457ed9xKFagxmO2E+P9NFQgJ2zn+52zN2qnrZqkfTAtoaxytthPZpuLSVNrAAEaHMO3Q5A1LJMqicC9bTi2Hg1EhKUBiwPyVKewv6wih8OOaTKuOpTKgsuR5Sc+MBpUhOgfcHyLCeYxxiIdmahbtoHzUnvYAcnjbboFiGVvuX2AoBo9gQqA6KW9Za/XS3/vpWnqNP7RnAYQjhhe0vp5v9NXOpqW98+wUOYH68nFRXex3yMoGhQT3K4bRMP1utzMFhMYoeqKrPbYtIiYHnlJAtWNvCnA+YHPGJDwgUzh7iInVlH4mW9Fo6iKAMYRDnNCTg2djayR54mgcqLI8AFkPL592KHvq/t0iX9f4QzgQjVkpO9+R/9LAEnT5X+Afu/1rKVtGI4mIdUdO46bVRpg+pcdyB5nHPnBZIMCK8CwbsJuswdBJW7h/1M4uq6wt0RWBb5vIw6q0RCx3/VwJbhpVkLpk9w86wxIiA1Bs0CxJJmUiUcj6Lfgnl3oVIqYN7UQ+HWGFLcMvbDS+11jhvt36PT2cfc/HY+PWy0fL60eagkyergEFKu5WxoIFH1cFCzWdTsEs4UOFzY6xVY577y87Pf/BZWTw47ba6AvAAAAAElFTkSuQmCC\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Wind speed card\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"units\":\"m/s\",\"decimals\":1,\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"configMode\":\"basic\",\"displayTimewindow\":true,\"margin\":\"0px\",\"borderRadius\":\"0px\",\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleFont\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1.6\"},\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"14px\",\"icon\":\"query_builder\",\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":null,\"weight\":null,\"style\":null,\"lineHeight\":\"1\"},\"color\":null}}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "wind", + "speed", + "airspeed", + "flow", + "gust" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/wind_speed_chart_card.json b/application/src/main/data/json/system/widget_types/wind_speed_chart_card.json new file mode 100644 index 0000000000..c72691a6c4 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/wind_speed_chart_card.json @@ -0,0 +1,35 @@ +{ + "fqn": "wind_speed_chart_card", + "name": "Wind speed chart card", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a wind speed data by combining the latest and aggregated values with an optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'speed', label: 'Wind Speed', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'm/s', decimals: 1 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'speed', 'm/s', 1);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind Speed\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 16 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 26) {\\n\\tvalue = 26;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m/s\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0.2,\"color\":\"#7191EF\"},{\"from\":0.2,\"to\":3.4,\"color\":\"#5B7EE6\"},{\"from\":3.4,\"to\":8,\"color\":\"#4B70DD\"},{\"from\":8,\"to\":10.8,\"color\":\"#305AD7\"},{\"from\":10.8,\"to\":17.2,\"color\":\"#234CC7\"},{\"from\":17.2,\"to\":24.5,\"color\":\"#F04022\"},{\"from\":24.5,\"to\":null,\"color\":\"#D81838\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 16 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 26) {\\n\\tvalue = 26;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m/s\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m/s\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Wind Speed\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:windsock\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "wind", + "speed", + "airspeed", + "flow", + "gust" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/wind_speed_chart_card_with_background.json b/application/src/main/data/json/system/widget_types/wind_speed_chart_card_with_background.json new file mode 100644 index 0000000000..ca85d9ee8b --- /dev/null +++ b/application/src/main/data/json/system/widget_types/wind_speed_chart_card_with_background.json @@ -0,0 +1,35 @@ +{ + "fqn": "wind_speed_chart_card_with_background", + "name": "Wind speed chart card with background", + "deprecated": false, + "image": "data:image/png;base64,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", + "description": "Displays a wind speed data by combining the latest and aggregated values with the background image and optional simplified chart.", + "descriptor": { + "type": "timeseries", + "sizeX": 4.5, + "sizeY": 3.5, + "resources": [], + "templateHtml": "\n\n", + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", + "controllerScript": "self.onInit = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDataUpdated();\n};\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.aggregatedValueCardWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '400px',\n previewHeight: '300px',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [\n { name: 'speed', label: 'Wind Speed', type: 'timeseries', color: 'rgba(0, 0, 0, 0.87)', units: 'm/s', decimals: 1 }\n ];\n },\n defaultLatestDataKeysFunction: function(configComponent, configData) {\n return configComponent.createDefaultAggregatedValueLatestDataKeys(configData, 'speed', 'm/s', 1);\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-aggregated-value-card-widget-settings", + "dataKeySettingsDirective": "", + "latestDataKeySettingsDirective": "tb-aggregated-value-card-key-settings", + "hasBasicMode": true, + "basicModeDirective": "tb-aggregated-value-card-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"Main building\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Wind Speed\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 16 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 26) {\\n\\tvalue = 26;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m/s\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Latest\",\"color\":\"#4caf50\",\"settings\":{\"position\":\"center\",\"font\":{\"size\":52,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"1\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"rangeList\":[{\"from\":0,\"to\":0.2,\"color\":\"#6083EC\"},{\"from\":0.2,\"to\":3.4,\"color\":\"#5579E5\"},{\"from\":3.4,\"to\":8,\"color\":\"#4369DD\"},{\"from\":8,\"to\":10.8,\"color\":\"#2B54CE\"},{\"from\":10.8,\"to\":17.2,\"color\":\"#224AC2\"},{\"from\":17.2,\"to\":24.5,\"color\":\"#F04022\"},{\"from\":24.5,\"to\":null,\"color\":\"#DE2343\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.9408410830697858,\"funcBody\":\"var value = prevValue + Math.random() * 16 - 8;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 26) {\\n\\tvalue = 26;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m/s\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta percent\",\"color\":\"#f44336\",\"settings\":{\"position\":\"rightTop\",\"font\":{\"size\":14,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"20px\"},\"color\":{\"type\":\"range\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[{\"from\":null,\"to\":0,\"color\":\"#198038\"},{\"from\":0,\"to\":0,\"color\":\"rgba(0, 0, 0, 0.87)\"},{\"from\":0,\"to\":null,\"color\":\"#D12730\"}],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":true},\"_hash\":0.06392321853157967,\"funcBody\":\"var value = prevValue + Math.random() * 6 - 3;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -25) {\\n\\tvalue = -25;\\n} else if (value > 25) {\\n\\tvalue = 25;\\n} \\nreturn value;\",\"aggregationType\":null,\"units\":\"%\",\"decimals\":0,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Delta absolute\",\"color\":\"#607d8b\",\"settings\":{\"position\":\"rightBottom\",\"font\":{\"size\":11,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.38)\",\"rangeList\":[],\"colorFunction\":\"var temperature = value;\\nif (typeof temperature !== undefined) {\\n var percent = (temperature + 60)/120 * 100;\\n return tinycolor.mix('blue', 'red', percent).toHexString();\\n}\\nreturn 'blue';\"},\"showArrow\":false},\"_hash\":0.44695098620509865,\"funcBody\":\"var value = prevValue + Math.random() * 4 - 2;\\nif (value < -6) {\\n\\tvalue = -6;\\n} else if (value > 6) {\\n\\tvalue = 6;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":\"m/s\",\"decimals\":1,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":43200000,\"fixedTimewindow\":{\"startTimeMs\":1691927717318,\"endTimeMs\":1692014117318},\"quickInterval\":\"CURRENT_MONTH_SO_FAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000}},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":null,\"padding\":\"0\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"showSubtitle\":true,\"subtitle\":\"${entityName}\",\"subtitleFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"subtitleColor\":\"rgba(0, 0, 0, 0.38)\",\"showDate\":true,\"dateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"dateFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"dateColor\":\"rgba(0, 0, 0, 0.38)\",\"showChart\":true,\"chartColor\":\"rgba(0, 0, 0, 0.87)\",\"background\":{\"type\":\"image\",\"imageBase64\":\"data:image/png;base64,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\",\"imageUrl\":null,\"color\":\"#fff\",\"overlay\":{\"enabled\":true,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"autoScale\":true},\"title\":\"Wind Speed\",\"dropShadow\":true,\"enableFullscreen\":false,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":true,\"titleIcon\":\"mdi:windsock\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"noDataDisplayMessage\":\"\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"decimals\":0,\"timewindowStyle\":{\"showIcon\":true,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":false},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"borderRadius\":null}" + }, + "externalId": null, + "tags": [ + "weather", + "environment", + "wind", + "speed", + "airspeed", + "flow", + "gust" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/tenant/dashboards/gateways.json b/application/src/main/data/json/tenant/dashboards/gateways.json new file mode 100644 index 0000000000..0ddfc98705 --- /dev/null +++ b/application/src/main/data/json/tenant/dashboards/gateways.json @@ -0,0 +1,6744 @@ +{ + "title": "ThingsBoard IoT Gateways", + "image": null, + "mobileHide": false, + "mobileOrder": null, + "configuration": { + "description": "", + "widgets": { + "23b9adc5-b3bd-040f-e5eb-a45925c73257": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680624762213, + "endTimeMs": 1680711162213 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "entitiesTitle": "Gateways list", + "enableSearch": true, + "enableSelectColumnDisplay": false, + "enableStickyHeader": true, + "enableStickyAction": false, + "showCellActionsMenu": true, + "reserveSpaceForHiddenAction": "true", + "displayEntityName": false, + "displayEntityLabel": false, + "displayEntityType": false, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityName", + "useRowStyleFunction": false + }, + "title": "Gateways list", + "dropShadow": true, + "enableFullscreen": false, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 600, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "9b7a1caa-3510-3daa-e05a-fc39b851d2c1", + "filterId": "abdfdcf9-75d4-001d-783a-920cb8e2a7e1", + "dataKeys": [ + { + "name": "createdTime", + "type": "entityField", + "label": "Created time", + "color": "#2196f3", + "settings": { + "columnWidth": "140px", + "useCellStyleFunction": false, + "useCellContentFunction": false, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.4570900758216545, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "name", + "type": "entityField", + "label": "Gateway name", + "color": "#4caf50", + "settings": { + "columnWidth": "20%", + "useCellStyleFunction": false, + "useCellContentFunction": false, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.45934623767875316, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "active", + "type": "attribute", + "label": "Status", + "color": "#f44336", + "settings": { + "columnWidth": "20%", + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "var newValue = value == 'true' ? \"Active\" : \"Inactive\";\r\nvar bgColor = value == 'true' ? 'rgba(25,128,56, .06)': 'rgba(203,37,48, .06)',\r\ncolor = value == 'true' ? 'rgb(25,128,56)': 'rgb(203,37,48)';\r\nreturn `
${newValue}
`", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.7189327146035449, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": false, + "postFuncBody": "" + }, + { + "name": "active_connectors", + "type": "attribute", + "label": "Active Connectors", + "color": "#3f51b5", + "settings": { + "columnWidth": "20%", + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "return value?JSON.parse(value).length:0;", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.3929515708982403, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "Version", + "type": "attribute", + "label": "Version", + "color": "#8bc34a", + "settings": { + "columnWidth": "40%", + "useCellStyleFunction": false, + "useCellContentFunction": false, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.4705163811361781, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "displayTimewindow": true, + "actions": { + "actionCellButton": [ + { + "name": "Docker commands", + "icon": "terminal", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "customPretty", + "customHtml": "
\n \n

Docker commands

\n \n
\n \n
\n \n
\n \n
\n
\n", + "customCss": ".container {\n display: grid;\n grid-template-rows: min-content minmax(auto, 1fr) min-content;\n height: 100%;\n max-height: 100vh;\n width: 600px;\n max-width: 100%;\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\n\nopenCommands();\n\nfunction openCommands() {\n customDialog.customDialog(htmlTemplate, CommandsDialogController, {panelClass: \"test\"}).subscribe();\n}\n\nfunction CommandsDialogController(instance) {\n let vm = instance;\n \n vm.entityId = entityId.id;\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n}\n", + "customResources": [], + "openInSeparateDialog": false, + "openInPopover": false, + "id": "ae2e5995-505f-a241-5fb2-6cbaf08b1b55" + }, + { + "name": "Gateway configuration", + "icon": "settings", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "customPretty", + "customHtml": "\n

gateway.gateway-configuration

\n \n \n
\n\n\n
\n\n", + "customCss": ".gateway-config {\n width: 800px !important;\n padding: 0 !important;\n min-height: 75vh;\n max-width: 100%;\n display: grid !important;\n}\n\n@media screen and (max-width: 599px) {\n .mat-mdc-dialog-content {\n max-height: calc(100% - 60px) !important;\n }\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\n\nopenAddEntityDialog();\n\nfunction openAddEntityDialog() {\n customDialog.customDialog(htmlTemplate, AddEntityDialogController).subscribe();\n}\n\nfunction AddEntityDialogController(instance) {\n let vm = instance;\n \n vm.device = additionalParams.entity.id;\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n}\n", + "customResources": [], + "openInSeparateDialog": false, + "openInPopover": false, + "id": "a54acd30-5c5f-d709-b892-5fc14f780e34" + }, + { + "name": "Connectors", + "icon": "private_connectivity", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "openDashboardState", + "targetDashboardStateId": "connectors", + "setEntityId": true, + "stateEntityParamName": "", + "openRightLayout": false, + "openInSeparateDialog": false, + "openInPopover": false, + "id": "e27eea56-c57c-0a45-eb1d-f48f19e5356b" + } + ], + "rowClick": [ + { + "name": "Show geteway details", + "icon": "more_horiz", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "openDashboardState", + "targetDashboardStateId": "gateway_details", + "setEntityId": true, + "stateEntityParamName": "", + "openRightLayout": false, + "openInSeparateDialog": false, + "openInPopover": false, + "id": "40cd37f1-6d1c-38a4-aba5-7dc408376647" + } + ], + "headerButton": [ + { + "name": "Add Gateway", + "icon": "add", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "customPretty", + "customHtml": "
\r\n \r\n

Add gateway

\r\n \r\n \r\n
\r\n \r\n \r\n
\r\n
\r\n
\r\n \r\n Name\r\n \r\n \r\n Gateway name is required.\r\n \r\n \r\n
\r\n
\r\n \r\n
\r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n", + "customCss": ".add-entity-form {\r\n min-width: 400px !important;\r\n}\r\n\r\n.add-entity-form .boolean-value-input {\r\n padding-left: 5px;\r\n}\r\n\r\n.add-entity-form .boolean-value-input .checkbox-label {\r\n margin-bottom: 8px;\r\n color: rgba(0,0,0,0.54);\r\n font-size: 12px;\r\n}\r\n\r\n.relations-list .header {\r\n padding-right: 5px;\r\n padding-bottom: 5px;\r\n padding-left: 5px;\r\n}\r\n\r\n.relations-list .header .cell {\r\n padding-right: 5px;\r\n padding-left: 5px;\r\n font-size: 12px;\r\n font-weight: 700;\r\n color: rgba(0, 0, 0, .54);\r\n white-space: nowrap;\r\n}\r\n\r\n.relations-list .mat-form-field-infix {\r\n width: auto !important;\r\n}\r\n\r\n.relations-list .body {\r\n padding-right: 5px;\r\n padding-bottom: 15px;\r\n padding-left: 5px;\r\n}\r\n\r\n.relations-list .body .row {\r\n padding-top: 5px;\r\n}\r\n\r\n.relations-list .body .cell {\r\n padding-right: 5px;\r\n padding-left: 5px;\r\n}\r\n\r\n.relations-list .body .md-button {\r\n margin: 0;\r\n}\r\n\r\n", + "customFunction": "let $injector = widgetContext.$scope.$injector;\r\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\r\nlet assetService = $injector.get(widgetContext.servicesMap.get('assetService'));\r\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\r\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\r\nlet entityRelationService = $injector.get(widgetContext.servicesMap.get('entityRelationService'));\r\nlet userSettingsService = $injector.get(widgetContext.servicesMap.get('userSettingsService'));\r\n\r\nopenAddEntityDialog();\r\n\r\nfunction openAddEntityDialog() {\r\n customDialog.customDialog(htmlTemplate, AddEntityDialogController).subscribe();\r\n}\r\n\r\nfunction AddEntityDialogController(instance) {\r\n let vm = instance;\r\n let userSettings;\r\n userSettingsService.loadUserSettings().subscribe(settings=> {\r\n userSettings = settings;\r\n if (!userSettings.createdGatewaysCount) userSettings.createdGatewaysCount = 0;\r\n });\r\n \r\n\r\n vm.addEntityFormGroup = vm.fb.group({\r\n entityName: ['', [vm.validators.required]],\r\n entityType: ['DEVICE'],\r\n entityLabel: [''],\r\n type: ['', [vm.validators.required]],\r\n });\r\n\r\n vm.cancel = function() {\r\n vm.dialogRef.close(null);\r\n };\r\n\r\n\r\n vm.save = function($event) {\r\n vm.addEntityFormGroup.markAsPristine();\r\n saveEntityObservable().subscribe(\r\n function (device) {\r\n widgetContext.updateAliases();\r\n userSettingsService.putUserSettings({ createdGatewaysCount: ++userSettings.createdGatewaysCount }).subscribe(_=>{\r\n });\r\n vm.dialogRef.close(null);\r\n openCommandDialog(device, $event);\r\n }\r\n );\r\n };\r\n \r\n function openCommandDialog(device, $event) {\r\n vm.device = device;\r\n let openCommandAction = widgetContext.actionsApi.getActionDescriptors(\"actionCellButton\").find(action => action.name == \"Docker commands\");\r\n widgetContext.actionsApi.handleWidgetAction($event, openCommandAction, device.id, device.name, {newDevice: true});\r\n setTimeout(function() {\r\n document.querySelector(\".dashboard-state-dialog .mat-mdc-button-touch-target\").addEventListener('click', goToConfigState);\r\n document.querySelector(\".dashboard-state-dialog .mat-mdc-button.mat-primary\").addEventListener('click', goToConfigState);\r\n }, 500);\r\n }\r\n\r\n \r\n function goToConfigState() {\r\n document.querySelector(\".dashboard-state-dialog .mat-mdc-button-touch-target\").removeEventListener('click', goToConfigState);\r\n document.querySelector(\".dashboard-state-dialog .mat-mdc-button.mat-primary\").removeEventListener('click', goToConfigState);\r\n const stateParams = {};\r\n stateParams.entityId = vm.device.id;\r\n stateParams.entityName = vm.device.name;\r\n const newStateParams = {\r\n targetEntityParamName: 'default',\r\n new_gateway: {\r\n entityId: vm.device.id,\r\n entityName: vm.device.name\r\n }\r\n }\r\n const params = {...stateParams, ...newStateParams};\r\n widgetContext.stateController.openState('gateway_details', params, false);\r\n }\r\n\r\n function saveEntityObservable() {\r\n const formValues = vm.addEntityFormGroup.value;\r\n let entity = {\r\n name: formValues.entityName,\r\n type: formValues.type,\r\n label: formValues.entityLabel,\r\n additionalInfo: {\r\n gateway: true\r\n }\r\n };\r\n return deviceService.saveDevice(entity);\r\n }\r\n}\r\n", + "customResources": [], + "openInSeparateDialog": false, + "openInPopover": false, + "id": "9aab301f-a305-520b-4842-b02ca92a5970" + } + ] + }, + "showTitleIcon": false, + "titleTooltip": "", + "enableDataExport": false, + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "configMode": "advanced", + "titleFont": null, + "titleColor": null, + "titleIcon": null, + "iconColor": null, + "borderRadius": "4px" + }, + "row": 0, + "col": 0, + "id": "23b9adc5-b3bd-040f-e5eb-a45925c73257", + "typeFullFqn": "system.cards.entities_table" + }, + "d4a1ffc4-c9f6-7b15-bca0-17feb58bac99": { + "type": "latest", + "sizeX": 9.5, + "sizeY": 6, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a2f01c66-96cf-49c5-303f-e6f21c559ee8", + "filterId": null, + "dataKeys": [] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680685647526, + "endTimeMs": 1680772047526 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": {}, + "title": "New Gateway configuration", + "showTitleIcon": false, + "titleTooltip": "", + "dropShadow": true, + "enableFullscreen": false, + "enableDataExport": false, + "widgetStyle": {}, + "widgetCss": "", + "titleStyle": { + "fontSize": "16px", + "fontWeight": 500 + }, + "pageSize": 1024, + "noDataDisplayMessage": "", + "showLegend": false, + "useDashboardTimewindow": true, + "borderRadius": "4px" + }, + "row": 0, + "col": 0, + "id": "d4a1ffc4-c9f6-7b15-bca0-17feb58bac99", + "typeFullFqn": "system.gateway_widgets.gateway_general_configuration" + }, + "02a05113-d878-b086-328e-75bd6f02b2bb": { + "type": "latest", + "sizeX": 11, + "sizeY": 8, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a2f01c66-96cf-49c5-303f-e6f21c559ee8", + "filterId": null, + "dataKeys": [ + { + "name": "activeConnectors", + "type": "attribute", + "label": "activeConnectors", + "color": "#2196f3", + "settings": {}, + "_hash": 0.7454705362378311 + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680769601167, + "endTimeMs": 1680856001167 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#FFFFFF01", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": {}, + "title": "New Gateway connector", + "showTitleIcon": false, + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "enableDataExport": false, + "widgetStyle": {}, + "widgetCss": "", + "titleStyle": { + "fontSize": "16px", + "fontWeight": 500 + }, + "pageSize": 1024, + "noDataDisplayMessage": "", + "showLegend": false, + "useDashboardTimewindow": true + }, + "row": 0, + "col": 0, + "id": "02a05113-d878-b086-328e-75bd6f02b2bb", + "typeFullFqn": "system.gateway_widgets.gateway_connectors" + }, + "79f59106-758f-c428-8b93-4341faea705d": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a2f01c66-96cf-49c5-303f-e6f21c559ee8", + "filterId": null, + "dataKeys": [ + { + "name": "name", + "type": "entityField", + "label": "Name", + "color": "#2196f3", + "settings": {}, + "_hash": 0.1375998537558516 + }, + { + "name": "type", + "type": "entityField", + "label": "Type", + "color": "#4caf50", + "settings": {}, + "_hash": 0.9806146353493157 + }, + { + "name": "activeDevices", + "type": "attribute", + "label": "activeDevices", + "color": "#ffc107", + "settings": {}, + "_hash": 0.09421132942226995 + }, + { + "name": "inactiveDevices", + "type": "attribute", + "label": "inactiveDevices", + "color": "#607d8b", + "settings": {}, + "_hash": 0.35218481621567044 + }, + { + "name": "active_connectors", + "type": "attribute", + "label": "active_connectors", + "color": "#3f51b5", + "settings": {}, + "_hash": 0.45076316106117176 + }, + { + "name": "inactive_connectors", + "type": "attribute", + "label": "inactive_connectors", + "color": "#e91e63", + "settings": {}, + "_hash": 0.36755550699981954 + }, + { + "name": "active", + "type": "attribute", + "label": "Status", + "color": "#e91e63", + "settings": {}, + "_hash": 0.10301612696488416, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "var newValue = value == 'true' ? \"Active\" : \"Inactive\";\nreturn newValue;" + }, + { + "name": "ALL_ERRORS_COUNT", + "type": "timeseries", + "label": "ALL_ERRORS_COUNT", + "color": "#ffeb3b", + "settings": {}, + "_hash": 0.2770587478725004 + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + }, + { + "type": "entityCount", + "name": null, + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "filterId": "bd33a006-fd40-3efc-7863-505c6e64f2b8", + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "count", + "color": "#3f51b5", + "settings": {}, + "_hash": 0.6470244865405319 + } + ] + }, + { + "type": "entityCount", + "name": null, + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "filterId": "956cf341-7cdf-82a7-ea2a-50cc9586f1f1", + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "count 2", + "color": "#e91e63", + "settings": {}, + "_hash": 0.46058668157030436 + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1683116949383, + "endTimeMs": 1683203349383 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "useMarkdownTextFunction": true, + "markdownTextFunction": "var blockData = '';\nvar connectorsIndex = ctx.actionsApi.getActionDescriptors('elementClick').findIndex(action=>action.name==\"Connectors\");\nvar logsIndex = ctx.actionsApi.getActionDescriptors('elementClick').findIndex(action=>action.name==\"Logs\");\nfunction generateMatHeader(index) {\n if( index !== undefined && index > -1) {\n return ``\n } else {\n return \"\" \n }\n}\nfunction createDataBlock(value, label, dividerStyle, mobile, index) {\n blockData += `\n \n
\n \n ${generateMatHeader(index)}\n ${label}\n
\n ${value}\n `;\n}\ncreateDataBlock(data[0].Status, \"Status\", data[0].Status === \"Active\"? 'divider-green' : 'divider-red');\ncreateDataBlock(data[0].Name, \"Gateway Name\", '', ctx.isMobile);\ncreateDataBlock(data[0].Type, \"Gateway Type\", '');\ncreateDataBlock(\n `${(data[1]?data[1].count:0)} `\n + \" | \" + \n `${(data[2]?data[2][\"count 2\"]:0)} `\n , \"Devices (Active | Inactive)\", '');\ncreateDataBlock(\n `${(data[0].active_connectors?JSON.parse(data[0].active_connectors).length:0)} `\n + \" | \" + \n `${(data[0].inactive_connectors?JSON.parse(data[0].inactive_connectors).length:0)} `\n , \"Connectors (Active | Inactive)\", '', '', connectorsIndex);\ncreateDataBlock(data[0].ALL_ERRORS_COUNT || 0, \"Errors\", (data[0].ALL_ERRORS_COUNT || 0) === 0 ? 'divider-green' : 'divider-red', '', logsIndex);\nreturn `
${blockData}
`;", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".divider {\n position: absolute;\n width: 3px;\n top: 8px;\n border-radius: 2px;\n bottom: 8px;\n border: 1px solid rgba(31, 70, 144, 1);\n background-color: rgba(31, 70, 144, 1);\n left: 10px;\n}\n.divider-green .divider {\n border: 1px solid rgb(25,128,56);\n background-color: rgb(25,128,56);\n}\n\n.divider-green .mat-mdc-card-content {\n color: rgb(25,128,56);\n}\n\n.divider-red .divider {\n border: 1px solid rgb(203,37,48);\n background-color: rgb(203,37,48);\n}\n\n.divider-red .mat-mdc-card-content {\n color: rgb(203,37,48);\n}\n\n.mdc-card {\n position: relative;\n padding-left: 10px;\n margin-bottom: 1px;\n}\n\n.mat-mdc-card-subtitle {\n font-weight: 400;\n font-size: 12px;\n}\n\n.mat-mdc-card-header {\n padding: 8px 16px 0;\n}\n\n.mat-mdc-card-content:last-child {\n padding-bottom: 8px;\n font-size: 16px;\n}\n\n.cards-container {\n height: calc(100% - 1px);\n justify-content: stretch;\n align-items: center;\n margin-bottom: 1px;\n}\n\n::ng-deep.tb-home-widget-link > div {\n flex-grow: 1;\n cursor: pointer;\n}\n\n .tb-home-widget-link {\n width: 100%;\n }\n\n .tb-home-widget-link:hover::after{\n color: inherit;\n }\n \n .tb-home-widget-link::after{\n content: 'arrow_forward';\n display: inline-block;\n transform: rotate(315deg);\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n font-size: 18px;\n color: rgba(0, 0, 0, 0.12);\n vertical-align: bottom;\n margin-left: 6px;\n}" + }, + "title": "Connectors", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "margin": "0", + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "displayTimewindow": true, + "borderRadius": "4px", + "actions": { + "elementClick": [ + { + "name": "Connectors", + "icon": "more_horiz", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "openDashboardState", + "targetDashboardStateId": "connectors", + "setEntityId": true, + "stateEntityParamName": null, + "openRightLayout": false, + "openInSeparateDialog": false, + "openInPopover": false, + "id": "ee7216d8-ad7a-20db-1abc-2531e221f24c" + }, + { + "name": "Logs", + "icon": "more_horiz", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "openDashboardState", + "targetDashboardStateId": "logs", + "setEntityId": true, + "stateEntityParamName": null, + "openRightLayout": false, + "openInSeparateDialog": false, + "openInPopover": false, + "id": "ceec0446-c25f-2b9b-b1b1-58ed751476ff" + } + ] + } + }, + "row": 0, + "col": 0, + "id": "79f59106-758f-c428-8b93-4341faea705d", + "typeFullFqn": "system.cards.markdown_card" + }, + "60dcf518-8fc3-3539-8ff8-ce94bda39f3a": { + "type": "alarm", + "sizeX": 10.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "timewindowMs": 2592000000, + "quickInterval": "CURRENT_DAY", + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "enableSelection": true, + "enableSearch": true, + "enableSelectColumnDisplay": false, + "enableFilter": true, + "enableStickyHeader": true, + "enableStickyAction": false, + "reserveSpaceForHiddenAction": "true", + "displayDetails": true, + "allowAcknowledgment": true, + "allowClear": true, + "allowAssign": true, + "displayActivity": true, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "-createdTime", + "useRowStyleFunction": false + }, + "title": "Alarms", + "dropShadow": true, + "enableFullscreen": false, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "alarmSource": { + "type": "entity", + "name": null, + "entityAliasId": "a2f01c66-96cf-49c5-303f-e6f21c559ee8", + "filterId": null, + "dataKeys": [ + { + "name": "createdTime", + "type": "alarm", + "label": "Created time", + "color": "#2196f3", + "settings": { + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "" + }, + "_hash": 0.021092237451093787 + }, + { + "name": "type", + "type": "alarm", + "label": "Type", + "color": "#f44336", + "settings": { + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "" + }, + "_hash": 0.7323586880398418 + }, + { + "name": "severity", + "type": "alarm", + "label": "Severity", + "color": "#ffc107", + "settings": { + "useCellStyleFunction": false, + "useCellContentFunction": false + }, + "_hash": 0.09927019860088193 + }, + { + "name": "details", + "type": "alarm", + "label": "details", + "color": "#9c27b0", + "settings": {}, + "_hash": 0.10903701402251387 + }, + { + "name": "status", + "type": "alarm", + "label": "Status", + "color": "#607d8b", + "settings": {}, + "_hash": 0.5260411335842929 + } + ] + }, + "alarmsPollingInterval": 5, + "showTitleIcon": false, + "titleIcon": "more_horiz", + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "widgetStyle": {}, + "displayTimewindow": false, + "actions": {}, + "datasources": [], + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "alarmFilterConfig": { + "statusList": [], + "severityList": [], + "typeList": [], + "searchPropagatedAlarms": false + }, + "enableDataExport": false, + "borderRadius": "4px" + }, + "row": 0, + "col": 0, + "id": "60dcf518-8fc3-3539-8ff8-ce94bda39f3a", + "typeFullFqn": "system.alarm_widgets.alarms_table" + }, + "27e6b37c-085e-824d-fb13-69651a545ec1": { + "type": "timeseries", + "sizeX": 7.5, + "sizeY": 3, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a2f01c66-96cf-49c5-303f-e6f21c559ee8", + "filterId": null, + "dataKeys": [] + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "timewindowMs": 43200000, + "quickInterval": "CURRENT_DAY", + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 25000 + }, + "timezone": null + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": {}, + "title": "Logs", + "showTitleIcon": false, + "dropShadow": true, + "enableFullscreen": true, + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "showLegend": false, + "useDashboardTimewindow": false, + "displayTimewindow": true, + "titleTooltip": "", + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "borderRadius": "4px" + }, + "row": 0, + "col": 0, + "id": "27e6b37c-085e-824d-fb13-69651a545ec1", + "typeFullFqn": "system.gateway_widgets.gateway_logs" + }, + "bcd04dbf-c82a-ca13-6e88-a74f32b4348a": { + "type": "timeseries", + "sizeX": 7.5, + "sizeY": 3, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a2f01c66-96cf-49c5-303f-e6f21c559ee8", + "filterId": null, + "dataKeys": [] + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "timewindowMs": 43200000, + "quickInterval": "CURRENT_DAY", + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 25000 + }, + "timezone": null + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "isConnectorLog": true, + "connectorLogState": "connector_logs" + }, + "title": "Gateway Logs", + "showTitleIcon": false, + "dropShadow": true, + "enableFullscreen": true, + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "showLegend": false, + "useDashboardTimewindow": false, + "displayTimewindow": true, + "titleTooltip": "", + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "borderRadius": "4px" + }, + "row": 0, + "col": 0, + "id": "bcd04dbf-c82a-ca13-6e88-a74f32b4348a", + "typeFullFqn": "system.gateway_widgets.gateway_logs" + }, + "9b88dc51-b186-90ef-5afb-9648e6e2ac9a": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a2f01c66-96cf-49c5-303f-e6f21c559ee8", + "filterId": null, + "dataKeys": [], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "timewindowMs": 604800000, + "quickInterval": "CURRENT_DAY", + "interval": 300000 + }, + "aggregation": { + "type": "NONE", + "limit": 25000 + }, + "timezone": null + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "stack": false, + "fontSize": 10, + "fontColor": "#545454", + "showTooltip": true, + "tooltipIndividual": false, + "tooltipCumulative": false, + "hideZeros": false, + "grid": { + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 0, + "color": "#545454", + "backgroundColor": null, + "tickColor": "#DDDDDD" + }, + "xaxis": { + "title": null, + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "min": null, + "max": null, + "title": null, + "showLabels": true, + "color": "#545454", + "tickSize": null, + "tickDecimals": 0, + "ticksFormatter": "" + }, + "shadowSize": 4, + "smoothLines": true, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "xaxisSecond": { + "axisPosition": "top", + "title": null, + "showLabels": true + }, + "customLegendEnabled": false, + "dataKeysListForLabels": [] + }, + "title": "Gateway Custom Statistics", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 600 + }, + "useDashboardTimewindow": true, + "displayTimewindow": false, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "showLegend": false, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": true, + "showTotal": false, + "showLatest": false + }, + "borderRadius": "4px" + }, + "row": 0, + "col": 0, + "id": "9b88dc51-b186-90ef-5afb-9648e6e2ac9a", + "typeFullFqn": "system.gateway_widgets.gateway_custom_statistics" + }, + "2a318e56-5c83-4f82-bf23-df72dd7e3edf": { + "type": "rpc", + "sizeX": 9.5, + "sizeY": 5.5, + "config": { + "targetDeviceAliases": [], + "showTitle": false, + "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 remote shell", + "dropShadow": true, + "enableFullscreen": true, + "widgetStyle": { + "border-radius": "10px" + }, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "useDashboardTimewindow": true, + "showLegend": false, + "actions": {}, + "datasources": [], + "targetDeviceAliasIds": [ + "a2f01c66-96cf-49c5-303f-e6f21c559ee8" + ], + "showTitleIcon": false, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "2a318e56-5c83-4f82-bf23-df72dd7e3edf", + "typeFullFqn": "system.control_widgets.rpc_remote_shell" + }, + "c50e294a-265a-d13c-c772-b56c4df953fe": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a2f01c66-96cf-49c5-303f-e6f21c559ee8", + "filterId": null, + "dataKeys": [ + { + "name": "allBytesSentToDevices", + "type": "timeseries", + "label": "Sent To Devices (bytes)", + "color": "#2196f3", + "settings": {}, + "_hash": 0.6608774125800538, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "allBytesSentToTB", + "type": "timeseries", + "label": "Send To ThingsBoard (bytes)", + "color": "#4caf50", + "settings": {}, + "_hash": 0.05525630093240763, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "allReсeivedBytesFromTB", + "type": "timeseries", + "label": "Received From ThingsBoard (bytes)", + "color": "#f44336", + "settings": {}, + "_hash": 0.4539040118567914, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "convertedBytesFromDevice", + "type": "timeseries", + "label": "Converted From Devices (bytes)", + "color": "#ffc107", + "settings": {}, + "_hash": 0.36601736007496366, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "eventsProduced", + "type": "timeseries", + "label": "Events Produced", + "color": "#607d8b", + "settings": {}, + "_hash": 0.6227465527701406, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "eventsSent", + "type": "timeseries", + "label": "Events Sent", + "color": "#9c27b0", + "settings": {}, + "_hash": 0.6043328458525739, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "receivedBytesFromDevices", + "type": "timeseries", + "label": "Received From Devices (bytes)", + "color": "#8bc34a", + "settings": {}, + "_hash": 0.7829761270709421, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "timewindowMs": 604800000, + "quickInterval": "CURRENT_DAY", + "interval": 1800000 + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + }, + "timezone": null + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "stack": false, + "fontSize": 10, + "fontColor": "#545454", + "showTooltip": true, + "tooltipIndividual": false, + "tooltipCumulative": false, + "hideZeros": false, + "grid": { + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 0, + "color": "#545454", + "backgroundColor": null, + "tickColor": "#DDDDDD" + }, + "xaxis": { + "title": null, + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "min": null, + "max": null, + "title": null, + "showLabels": true, + "color": "#545454", + "tickSize": null, + "tickDecimals": 0, + "ticksFormatter": "" + }, + "shadowSize": 4, + "smoothLines": true, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "xaxisSecond": { + "axisPosition": "top", + "title": null, + "showLabels": true + }, + "customLegendEnabled": false, + "dataKeysListForLabels": [] + }, + "title": "Gateway General Chart Statistics", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 600 + }, + "useDashboardTimewindow": true, + "displayTimewindow": false, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "showLegend": false, + "legendConfig": { + "direction": "row", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": false, + "showLatest": false + }, + "borderRadius": "4px" + }, + "row": 0, + "col": 0, + "id": "c50e294a-265a-d13c-c772-b56c4df953fe", + "typeFullFqn": "system.gateway_widgets.gateway_general_chart_statistics" + }, + "da01e13f-c108-39fd-2a74-b40da088769a": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 6.5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a2f01c66-96cf-49c5-303f-e6f21c559ee8", + "filterId": null, + "dataKeys": [ + { + "name": "SERVICE_LOGS", + "type": "timeseries", + "label": "Status", + "color": "#4caf50", + "settings": { + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let cssClass;\r\nswitch (value) {\r\n case \"DEBUG\":\r\n cssClass = \"status status-debug\";\r\n break;\r\n case \"WARNING\":\r\n cssClass = \"status status-warning\";\r\n break;\r\n case \"ERROR\":\r\n case \"EXCEPTION\":\r\n cssClass = \"status status-error\";\r\n break;\r\n case \"INFO\":\r\n default:\r\n cssClass = \"status status-info\";\r\n break;\r\n }\r\n \r\n return `${value}`;" + }, + "_hash": 0.6667576563240645, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "if (value.includes(\"RPC\")&&!value.includes(\"GRPC\")) {\n let parsedValue = value.match(/\\|(\\w+)\\|/);\n parsedValue = parsedValue ? parsedValue[1] :\n parsedValue;\n return parsedValue || value;\n} else return '';" + }, + { + "name": "SERVICE_LOGS", + "type": "timeseries", + "label": "Details", + "color": "#2196f3", + "settings": { + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let statusValue = value.match(/\\|(\\w+)\\|/); \r\nstatusValue = statusValue? statusValue[1] : statusValue;\r\nlet cssClass = statusValue === \"EXCEPTION\" ?\r\n \"msg-status-exception\" : 'msg';\r\nlet parsedValue = /\\[(.*)/.exec(value);\r\nparsedValue = parsedValue ? parsedValue[0] : parsedValue;\r\nreturn `${parsedValue || value}`;" + }, + "_hash": 0.6931154680365814, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "if (value.includes(\"RPC\")&&!value.includes(\"GRPC\")) {\n return value;\n} else return '';" + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "timewindowMs": 604800000, + "quickInterval": "CURRENT_DAY", + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "enableSearch": true, + "enableSelectColumnDisplay": true, + "enableStickyHeader": true, + "enableStickyAction": true, + "showCellActionsMenu": true, + "reserveSpaceForHiddenAction": "true", + "showTimestamp": true, + "showMilliseconds": false, + "displayPagination": true, + "useEntityLabel": false, + "defaultPageSize": 10, + "hideEmptyLines": true, + "disableStickyHeader": false, + "useRowStyleFunction": false + }, + "title": "RPC Logs", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 600, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "widgetStyle": {}, + "actions": {}, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "displayTimewindow": true, + "titleTooltip": "", + "widgetCss": ".status {\r\n border-radius: 20px;\r\n font-weight: 500;\r\n padding: 5px 15px;\r\n}\r\n\r\n.status-debug {\r\n color: green;\r\n background: rgba(0, 128, 0, 0.1);\r\n}\r\n\r\n.status-warning {\r\n color: orange;\r\n background: rgba(255, 165, 0, 0.1);\r\n}\r\n\r\n.status-error {\r\n color: red;\r\n background: rgba(255, 0, 0, 0.1);\r\n}\r\n\r\n.status-info {\r\n color: blue;\r\n background: rgba(0, 0, 128, 0.1);\r\n}\r\n\r\n.msg-status-exception {\r\n color: red;\r\n}", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "borderRadius": "4px" + }, + "row": 0, + "col": 0, + "id": "da01e13f-c108-39fd-2a74-b40da088769a", + "typeFullFqn": "system.cards.timeseries_table" + }, + "ce3065bf-d898-2c03-6236-ce1b6cdf1f3f": { + "type": "rpc", + "sizeX": 9.5, + "sizeY": 5.5, + "config": { + "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": {}, + "datasources": [], + "targetDeviceAliasIds": [ + "a2f01c66-96cf-49c5-303f-e6f21c559ee8" + ], + "showTitleIcon": false, + "titleTooltip": "", + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "borderRadius": "4px" + }, + "row": 0, + "col": 0, + "id": "ce3065bf-d898-2c03-6236-ce1b6cdf1f3f", + "typeFullFqn": "system.control_widgets.rpc_debug_terminal" + }, + "46b68eb5-7e2d-bca1-c2ef-061b64575218": { + "type": "rpc", + "sizeX": 4, + "sizeY": 2, + "config": { + "targetDeviceAliases": [], + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "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": "Service RPC", + "datasources": [], + "targetDeviceAliasIds": [ + "a2f01c66-96cf-49c5-303f-e6f21c559ee8" + ], + "showTitleIcon": false, + "titleTooltip": "", + "dropShadow": true, + "enableFullscreen": true, + "widgetStyle": {}, + "widgetCss": "", + "titleStyle": { + "fontSize": "16px", + "fontWeight": 600 + }, + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "showLegend": false, + "borderRadius": "4px" + }, + "row": 0, + "col": 0, + "id": "46b68eb5-7e2d-bca1-c2ef-061b64575218", + "typeFullFqn": "system.gateway_widgets.service_rpc" + }, + "878c68fd-71cb-21d4-684a-b5b5a4526b43": { + "type": "rpc", + "sizeX": 4, + "sizeY": 2, + "config": { + "targetDeviceAliases": [], + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "isConnector": true + }, + "title": "Service RPC", + "datasources": [], + "targetDeviceAliasIds": [ + "a2f01c66-96cf-49c5-303f-e6f21c559ee8" + ], + "showTitleIcon": false, + "titleTooltip": "", + "dropShadow": true, + "enableFullscreen": true, + "widgetStyle": {}, + "widgetCss": "", + "titleStyle": { + "fontSize": "16px", + "fontWeight": 600 + }, + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "showLegend": false, + "borderRadius": "4px" + }, + "row": 0, + "col": 0, + "id": "878c68fd-71cb-21d4-684a-b5b5a4526b43", + "typeFullFqn": "system.gateway_widgets.service_rpc" + }, + "0fc7dcf3-32a2-0159-738e-96956bf0d450": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 6.5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a2f01c66-96cf-49c5-303f-e6f21c559ee8", + "filterId": null, + "dataKeys": [ + { + "name": "SERVICE_LOGS", + "type": "timeseries", + "label": "Status", + "color": "#4caf50", + "settings": { + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let cssClass;\r\nswitch (value) {\r\n case \"DEBUG\":\r\n cssClass = \"status status-debug\";\r\n break;\r\n case \"WARNING\":\r\n cssClass = \"status status-warning\";\r\n break;\r\n case \"ERROR\":\r\n case \"EXCEPTION\":\r\n cssClass = \"status status-error\";\r\n break;\r\n case \"INFO\":\r\n default:\r\n cssClass = \"status status-info\";\r\n break;\r\n }\r\n \r\n return `${value}`;" + }, + "_hash": 0.6667576563240645, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "if (value.includes(\"RPC\")&&!value.includes(\"GRPC\")) {\n let parsedValue = value.match(/\\|(\\w+)\\|/);\n parsedValue = parsedValue ? parsedValue[1] :\n parsedValue;\n return parsedValue || value;\n} else return '';" + }, + { + "name": "SERVICE_LOGS", + "type": "timeseries", + "label": "Details", + "color": "#2196f3", + "settings": { + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let statusValue = value.match(/\\|(\\w+)\\|/); \r\nstatusValue = statusValue? statusValue[1] : statusValue;\r\nlet cssClass = statusValue === \"EXCEPTION\" ?\r\n \"msg-status-exception\" : 'msg';\r\nlet parsedValue = /\\[(.*)/.exec(value);\r\nparsedValue = parsedValue ? parsedValue[0] : parsedValue;\r\nreturn `${parsedValue || value}`;" + }, + "_hash": 0.6931154680365814, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "if (value.includes(\"RPC\")&&!value.includes(\"GRPC\")) {\n return value;\n} else return '';" + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + }, + "latestDataKeys": [] + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "timewindowMs": 604800000, + "quickInterval": "CURRENT_DAY", + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "enableSearch": true, + "enableSelectColumnDisplay": true, + "enableStickyHeader": true, + "enableStickyAction": true, + "showCellActionsMenu": true, + "reserveSpaceForHiddenAction": "true", + "showTimestamp": true, + "showMilliseconds": false, + "displayPagination": true, + "useEntityLabel": false, + "defaultPageSize": 10, + "hideEmptyLines": true, + "disableStickyHeader": false, + "useRowStyleFunction": false + }, + "title": "RPC Logs", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 600, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "widgetStyle": {}, + "actions": {}, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "displayTimewindow": true, + "titleTooltip": "", + "widgetCss": ".status {\r\n border-radius: 20px;\r\n font-weight: 500;\r\n padding: 5px 15px;\r\n}\r\n\r\n.status-debug {\r\n color: green;\r\n background: rgba(0, 128, 0, 0.1);\r\n}\r\n\r\n.status-warning {\r\n color: orange;\r\n background: rgba(255, 165, 0, 0.1);\r\n}\r\n\r\n.status-error {\r\n color: red;\r\n background: rgba(255, 0, 0, 0.1);\r\n}\r\n\r\n.status-info {\r\n color: blue;\r\n background: rgba(0, 0, 128, 0.1);\r\n}\r\n\r\n.msg-status-exception {\r\n color: red;\r\n}", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "borderRadius": "4px" + }, + "row": 0, + "col": 0, + "id": "0fc7dcf3-32a2-0159-738e-96956bf0d450", + "typeFullFqn": "system.cards.timeseries_table" + }, + "6e136fbc-545b-4926-c0a9-79348f35628a": { + "type": "rpc", + "sizeX": 9.5, + "sizeY": 5.5, + "config": { + "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": "New RPC debug terminal", + "dropShadow": true, + "enableFullscreen": true, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "useDashboardTimewindow": true, + "showLegend": false, + "actions": {}, + "datasources": [], + "targetDeviceAliasIds": [ + "a2f01c66-96cf-49c5-303f-e6f21c559ee8" + ], + "showTitleIcon": false, + "titleTooltip": "", + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "borderRadius": "4px" + }, + "row": 0, + "col": 0, + "id": "6e136fbc-545b-4926-c0a9-79348f35628a", + "typeFullFqn": "system.control_widgets.rpc_debug_terminal" + }, + "fb9df382-6ef3-4aa6-bc13-8bf8e300ba19": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a2f01c66-96cf-49c5-303f-e6f21c559ee8", + "filterId": null, + "dataKeys": [] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1685437116892, + "endTimeMs": 1685523516892 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "
\r\n \r\n
", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".action-buttons-container {\r\n display: flex;\r\n flex-wrap: wrap;\r\n flex-direction: row;\r\n height: 100%;\r\n width: 100%;\r\n align-content: center;\r\n}\r\n\r\nbutton {\r\n flex-grow: 1;\r\n margin: 10px;\r\n min-width: 150px;\r\n height: auto;\r\n}" + }, + "title": "Service command", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "actions": { + "elementClick": [ + { + "name": "Launch command", + "icon": "more_horiz", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "customPretty", + "customHtml": "
\n \n

Launch command

\n \n
\n \n
\n \n
\n \n
\n
\n", + "customCss": ".container {\n display: grid;\n grid-template-rows: min-content minmax(auto, 1fr) min-content;\n height: 100%;\n max-height: 100vh;\n width: 600px;\n max-width: 100%;\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\n\nopenCommands();\n\nfunction openCommands() {\n customDialog.customDialog(htmlTemplate, CommandsDialogController, {panelClass: \"test\"}).subscribe();\n}\n\nfunction CommandsDialogController(instance) {\n let vm = instance;\n \n vm.entityId = entityId.id;\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n}\n", + "customResources": [], + "openInSeparateDialog": false, + "openInPopover": false, + "id": "337c767b-3217-d3d3-b955-7b0bd0858a1d" + } + ] + }, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "borderRadius": "4px" + }, + "row": 0, + "col": 0, + "id": "fb9df382-6ef3-4aa6-bc13-8bf8e300ba19", + "typeFullFqn": "system.cards.markdown_card" + }, + "61d149e8-b249-5526-e5d7-6ad58413982e": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a2f01c66-96cf-49c5-303f-e6f21c559ee8", + "filterId": null, + "dataKeys": [ + { + "name": "general_configuration", + "type": "attribute", + "label": "general_configuration", + "color": "#2196f3", + "settings": {}, + "_hash": 0.739858588321151 + }, + { + "name": "RemoteLoggingLevel", + "type": "attribute", + "label": "RemoteLoggingLevel", + "color": "#4caf50", + "settings": {}, + "_hash": 0.20230761305210687 + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1685437116892, + "endTimeMs": 1685523516892 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "useMarkdownTextFunction": true, + "markdownTextFunction": "let buttonsHtml = \"\" \nctx.actionsApi.getActionDescriptors('elementClick').forEach((btn, index)=>{\n let disabled =false;\n if (index == 2) {\n disabled = data[0] && data[0].RemoteLoggingLevel ? data[0].RemoteLoggingLevel == \"NONE\" : true;\n } else if (index == 4) {\n const conf = data[0].general_configuration? JSON.parse(data[0].general_configuration): {};\n disabled = !conf.remoteShell;\n }\n buttonsHtml += ``\n});\n\nreturn `
${buttonsHtml}
`;", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".action-buttons-container {\r\n display: flex;\r\n flex-wrap: wrap;\r\n flex-direction: row;\r\n height: 100%;\r\n width: 100%;\r\n align-content: start;\r\n}\r\n\r\nbutton {\r\n flex-grow: 1;\r\n margin: 10px;\r\n min-width: 150px;\r\n height: auto;\r\n}" + }, + "title": "General configuration", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "actions": { + "elementClick": [ + { + "name": "General configuration", + "icon": "more_horiz", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "openDashboardState", + "targetDashboardStateId": "configuration", + "setEntityId": true, + "stateEntityParamName": null, + "openRightLayout": false, + "openInSeparateDialog": false, + "openInPopover": false, + "id": "337c767b-3217-d3d3-b955-7b0bd0858a1d" + }, + { + "name": "Connectors configuration", + "icon": "more_horiz", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "openDashboardState", + "targetDashboardStateId": "connectors", + "setEntityId": true, + "stateEntityParamName": null, + "openRightLayout": false, + "openInSeparateDialog": false, + "openInPopover": false, + "id": "00469ea7-f4c4-e39d-a735-c74b6ba6a026" + }, + { + "name": "Logs", + "icon": "more_horiz", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "openDashboardState", + "targetDashboardStateId": "logs", + "setEntityId": true, + "stateEntityParamName": null, + "openRightLayout": false, + "openInSeparateDialog": false, + "openInPopover": false, + "id": "d8607421-28b4-7c3b-949b-f69c3a46b461" + }, + { + "name": "Statistics", + "icon": "more_horiz", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "openDashboardState", + "targetDashboardStateId": "statistics", + "setEntityId": true, + "stateEntityParamName": null, + "openRightLayout": false, + "openInSeparateDialog": false, + "openInPopover": false, + "id": "425ba0d8-8e26-18a5-881d-bebe27fb2a7a" + }, + { + "name": "Remote Shell", + "icon": "more_horiz", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "openDashboardState", + "targetDashboardStateId": "remote_shell", + "setEntityId": true, + "stateEntityParamName": null, + "openRightLayout": false, + "openInSeparateDialog": false, + "openInPopover": false, + "id": "2c4c7dcc-009f-64eb-5f1b-a991df6d25a2" + }, + { + "name": "RPC", + "icon": "more_horiz", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "openDashboardState", + "targetDashboardStateId": "rpc", + "setEntityId": true, + "stateEntityParamName": null, + "openRightLayout": false, + "openInSeparateDialog": false, + "openInPopover": false, + "id": "35705df8-69cf-7f76-b52f-d53c5a1931c0" + } + ] + }, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "borderRadius": "4px" + }, + "row": 0, + "col": 0, + "id": "61d149e8-b249-5526-e5d7-6ad58413982e", + "typeFullFqn": "system.cards.markdown_card" + }, + "3d661190-7463-ba61-6793-503c85af67ec": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entity", + "name": "function", + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "dataKeys": [], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1686652103417, + "endTimeMs": 1686738503417 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".mat-mdc-form-field-subscript-wrapper {\n display: none !important;\n}" + }, + "title": "Gateway devices", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "displayTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "borderRadius": "4px" + }, + "row": 0, + "col": 0, + "id": "3d661190-7463-ba61-6793-503c85af67ec", + "typeFullFqn": "system.cards.markdown_card" + }, + "1615bd4e-c0a4-c32c-3706-3c83214cb8d7": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1684327643501, + "endTimeMs": 1684414043501 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "entitiesTitle": "Devices", + "enableSearch": true, + "enableSelectColumnDisplay": true, + "enableStickyHeader": true, + "enableStickyAction": true, + "reserveSpaceForHiddenAction": "true", + "displayEntityName": true, + "entityNameColumnTitle": "Device Name", + "displayEntityLabel": false, + "displayEntityType": false, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityName", + "useRowStyleFunction": false + }, + "title": "Devices", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "filterId": null, + "dataKeys": [ + { + "name": "type", + "type": "entityField", + "label": "Device Type", + "color": "#2196f3", + "settings": {}, + "_hash": 0.3129929097366162, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "active", + "type": "attribute", + "label": "Status", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let cssClass;\r\nswitch (value) {\r\n case \"Active\":\r\n cssClass = \"status status-active\";\r\n break;\r\n case \"Inactive\":\r\n default:\r\n cssClass = \"status status-inactive\";\r\n break;\r\n }\r\n \r\n return `${value}`;", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5969880627410065, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value == 'true' ? \"Active\": \"Inactive\";" + }, + { + "name": "connectorName", + "type": "attribute", + "label": "Connector Name", + "color": "#f44336", + "settings": {}, + "_hash": 0.012483045440007778, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "connectorType", + "type": "attribute", + "label": "Connector Type", + "color": "#ffc107", + "settings": {}, + "_hash": 0.6004192233378134, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": ".status {\r\n border-radius: 20px;\r\n font-weight: 500;\r\n padding: 5px 15px;\r\n }\r\n\r\n .status-active {\r\n color: green;\r\n background: rgba(0, 128, 0, 0.1);\r\n }\r\n\r\n .status-inactive {\r\n color: red;\r\n background: rgba(255, 0, 0, 0.1);\r\n }\r\n", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "borderRadius": "", + "actions": { + "actionCellButton": [ + { + "name": "Show Device Info", + "icon": "info", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "custom", + "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "openInSeparateDialog": false, + "openInPopover": false, + "id": "45e4507d-3adc-bb31-8b2b-1ba09bbd56ac" + } + ] + } + }, + "row": 0, + "col": 0, + "id": "1615bd4e-c0a4-c32c-3706-3c83214cb8d7", + "typeFullFqn": "system.cards.entities_table" + }, + "aafba3d8-a381-21c0-ecbe-446da3cdc041": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1684327643501, + "endTimeMs": 1684414043501 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "entitiesTitle": "Devices", + "enableSearch": true, + "enableSelectColumnDisplay": true, + "enableStickyHeader": true, + "enableStickyAction": true, + "reserveSpaceForHiddenAction": "true", + "displayEntityName": true, + "entityNameColumnTitle": "Device Name", + "displayEntityLabel": false, + "displayEntityType": false, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityName", + "useRowStyleFunction": false + }, + "title": "Devices", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "filterId": "bd9176e1-9e04-3e9b-d5a5-07b72bb9ae90", + "dataKeys": [ + { + "name": "type", + "type": "entityField", + "label": "Device Type", + "color": "#2196f3", + "settings": {}, + "_hash": 0.3129929097366162, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "active", + "type": "attribute", + "label": "Status", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let cssClass;\r\nswitch (value) {\r\n case \"Active\":\r\n cssClass = \"status status-active\";\r\n break;\r\n case \"Inactive\":\r\n default:\r\n cssClass = \"status status-inactive\";\r\n break;\r\n }\r\n \r\n return `${value}`;", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5969880627410065, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value == 'true' ? \"Active\": \"Inactive\";" + }, + { + "name": "connectorName", + "type": "attribute", + "label": "Connector Name", + "color": "#f44336", + "settings": {}, + "_hash": 0.012483045440007778, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "connectorType", + "type": "attribute", + "label": "Connector Type", + "color": "#ffc107", + "settings": {}, + "_hash": 0.6004192233378134, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": ".status {\r\n border-radius: 20px;\r\n font-weight: 500;\r\n padding: 5px 15px;\r\n }\r\n\r\n .status-active {\r\n color: green;\r\n background: rgba(0, 128, 0, 0.1);\r\n }\r\n\r\n .status-inactive {\r\n color: red;\r\n background: rgba(255, 0, 0, 0.1);\r\n }\r\n", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "borderRadius": "4px", + "actions": { + "actionCellButton": [ + { + "name": "Show Device Info", + "icon": "info", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "custom", + "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "openInSeparateDialog": false, + "openInPopover": false, + "id": "852eccce-98eb-24db-c783-bdd62566f906" + } + ] + } + }, + "row": 0, + "col": 0, + "id": "aafba3d8-a381-21c0-ecbe-446da3cdc041", + "typeFullFqn": "system.cards.entities_table" + }, + "7f676bb2-bde2-10e7-80d8-25dd734e8e22": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1684327643501, + "endTimeMs": 1684414043501 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "entitiesTitle": "Devices", + "enableSearch": true, + "enableSelectColumnDisplay": true, + "enableStickyHeader": true, + "enableStickyAction": true, + "reserveSpaceForHiddenAction": "true", + "displayEntityName": true, + "entityNameColumnTitle": "Device Name", + "displayEntityLabel": false, + "displayEntityType": false, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityName", + "useRowStyleFunction": false + }, + "title": "Devices", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "filterId": "44038462-1bae-e075-7b31-283341cb2295", + "dataKeys": [ + { + "name": "type", + "type": "entityField", + "label": "Device Type", + "color": "#2196f3", + "settings": {}, + "_hash": 0.3129929097366162, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "active", + "type": "attribute", + "label": "Status", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let cssClass;\r\nswitch (value) {\r\n case \"Active\":\r\n cssClass = \"status status-active\";\r\n break;\r\n case \"Inactive\":\r\n default:\r\n cssClass = \"status status-inactive\";\r\n break;\r\n }\r\n \r\n return `${value}`;", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5969880627410065, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value == 'true' ? \"Active\": \"Inactive\";" + }, + { + "name": "connectorName", + "type": "attribute", + "label": "Connector Name", + "color": "#f44336", + "settings": {}, + "_hash": 0.012483045440007778, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "connectorType", + "type": "attribute", + "label": "Connector Type", + "color": "#ffc107", + "settings": {}, + "_hash": 0.6004192233378134, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": ".status {\r\n border-radius: 20px;\r\n font-weight: 500;\r\n padding: 5px 15px;\r\n }\r\n\r\n .status-active {\r\n color: green;\r\n background: rgba(0, 128, 0, 0.1);\r\n }\r\n\r\n .status-inactive {\r\n color: red;\r\n background: rgba(255, 0, 0, 0.1);\r\n }\r\n", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "actions": { + "actionCellButton": [ + { + "name": "Show Device Info", + "icon": "info", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "custom", + "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "openInSeparateDialog": false, + "openInPopover": false, + "id": "3c31ba62-e760-2bea-4c8d-d32784a86c24" + } + ] + } + }, + "row": 0, + "col": 0, + "id": "7f676bb2-bde2-10e7-80d8-25dd734e8e22", + "typeFullFqn": "system.cards.entities_table" + }, + "d64482d8-001a-6f33-9b56-665530098fe5": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1684327643501, + "endTimeMs": 1684414043501 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "entitiesTitle": "Devices", + "enableSearch": true, + "enableSelectColumnDisplay": true, + "enableStickyHeader": true, + "enableStickyAction": true, + "reserveSpaceForHiddenAction": "true", + "displayEntityName": true, + "entityNameColumnTitle": "Device Name", + "displayEntityLabel": false, + "displayEntityType": false, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityName", + "useRowStyleFunction": false + }, + "title": "Devices", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "filterId": "884e9c34-7534-a483-99be-81b56cd91185", + "dataKeys": [ + { + "name": "type", + "type": "entityField", + "label": "Device Type", + "color": "#2196f3", + "settings": {}, + "_hash": 0.3129929097366162, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "active", + "type": "attribute", + "label": "Status", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let cssClass;\r\nswitch (value) {\r\n case \"Active\":\r\n cssClass = \"status status-active\";\r\n break;\r\n case \"Inactive\":\r\n default:\r\n cssClass = \"status status-inactive\";\r\n break;\r\n }\r\n \r\n return `${value}`;", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5969880627410065, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value == 'true' ? \"Active\": \"Inactive\";" + }, + { + "name": "connectorName", + "type": "attribute", + "label": "Connector Name", + "color": "#f44336", + "settings": {}, + "_hash": 0.012483045440007778, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "connectorType", + "type": "attribute", + "label": "Connector Type", + "color": "#ffc107", + "settings": {}, + "_hash": 0.6004192233378134, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": ".status {\r\n border-radius: 20px;\r\n font-weight: 500;\r\n padding: 5px 15px;\r\n }\r\n\r\n .status-active {\r\n color: green;\r\n background: rgba(0, 128, 0, 0.1);\r\n }\r\n\r\n .status-inactive {\r\n color: red;\r\n background: rgba(255, 0, 0, 0.1);\r\n }\r\n", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "actions": { + "actionCellButton": [ + { + "name": "Show Device Info", + "icon": "info", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "custom", + "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "openInSeparateDialog": false, + "openInPopover": false, + "id": "4b55ea81-93bf-4206-9166-3e0bdc1dd9f3" + } + ] + } + }, + "row": 0, + "col": 0, + "id": "d64482d8-001a-6f33-9b56-665530098fe5", + "typeFullFqn": "system.cards.entities_table" + }, + "bb27723a-989c-2327-5808-b56d490b93ab": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1684327643501, + "endTimeMs": 1684414043501 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "entitiesTitle": "Devices", + "enableSearch": true, + "enableSelectColumnDisplay": true, + "enableStickyHeader": true, + "enableStickyAction": true, + "reserveSpaceForHiddenAction": "true", + "displayEntityName": true, + "entityNameColumnTitle": "Device Name", + "displayEntityLabel": false, + "displayEntityType": false, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityName", + "useRowStyleFunction": false + }, + "title": "Devices", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "filterId": "e91ca0e9-1653-4fbc-5f3d-3da021b1b415", + "dataKeys": [ + { + "name": "type", + "type": "entityField", + "label": "Device Type", + "color": "#2196f3", + "settings": {}, + "_hash": 0.3129929097366162, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "active", + "type": "attribute", + "label": "Status", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let cssClass;\r\nswitch (value) {\r\n case \"Active\":\r\n cssClass = \"status status-active\";\r\n break;\r\n case \"Inactive\":\r\n default:\r\n cssClass = \"status status-inactive\";\r\n break;\r\n }\r\n \r\n return `${value}`;", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5969880627410065, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value == 'true' ? \"Active\": \"Inactive\";" + }, + { + "name": "connectorName", + "type": "attribute", + "label": "Connector Name", + "color": "#f44336", + "settings": {}, + "_hash": 0.012483045440007778, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "connectorType", + "type": "attribute", + "label": "Connector Type", + "color": "#ffc107", + "settings": {}, + "_hash": 0.6004192233378134, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": ".status {\r\n border-radius: 20px;\r\n font-weight: 500;\r\n padding: 5px 15px;\r\n }\r\n\r\n .status-active {\r\n color: green;\r\n background: rgba(0, 128, 0, 0.1);\r\n }\r\n\r\n .status-inactive {\r\n color: red;\r\n background: rgba(255, 0, 0, 0.1);\r\n }\r\n", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "actions": { + "actionCellButton": [ + { + "name": "Show Device Info", + "icon": "info", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "custom", + "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "openInSeparateDialog": false, + "openInPopover": false, + "id": "babf88d0-a118-e2b5-f10e-3a5970c8a65b" + } + ] + } + }, + "row": 0, + "col": 0, + "id": "bb27723a-989c-2327-5808-b56d490b93ab", + "typeFullFqn": "system.cards.entities_table" + }, + "e3a9539d-2ccb-96e0-2afa-36f93e40233d": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1684327643501, + "endTimeMs": 1684414043501 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "entitiesTitle": "Devices", + "enableSearch": true, + "enableSelectColumnDisplay": true, + "enableStickyHeader": true, + "enableStickyAction": true, + "reserveSpaceForHiddenAction": "true", + "displayEntityName": true, + "entityNameColumnTitle": "Device Name", + "displayEntityLabel": false, + "displayEntityType": false, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityName", + "useRowStyleFunction": false + }, + "title": "Devices", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "filterId": "3931abd5-2205-9386-6ea9-8e8a8131bb9d", + "dataKeys": [ + { + "name": "type", + "type": "entityField", + "label": "Device Type", + "color": "#2196f3", + "settings": {}, + "_hash": 0.3129929097366162, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "active", + "type": "attribute", + "label": "Status", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let cssClass;\r\nswitch (value) {\r\n case \"Active\":\r\n cssClass = \"status status-active\";\r\n break;\r\n case \"Inactive\":\r\n default:\r\n cssClass = \"status status-inactive\";\r\n break;\r\n }\r\n \r\n return `${value}`;", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5969880627410065, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value == 'true' ? \"Active\": \"Inactive\";" + }, + { + "name": "connectorName", + "type": "attribute", + "label": "Connector Name", + "color": "#f44336", + "settings": {}, + "_hash": 0.012483045440007778, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "connectorType", + "type": "attribute", + "label": "Connector Type", + "color": "#ffc107", + "settings": {}, + "_hash": 0.6004192233378134, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": ".status {\r\n border-radius: 20px;\r\n font-weight: 500;\r\n padding: 5px 15px;\r\n }\r\n\r\n .status-active {\r\n color: green;\r\n background: rgba(0, 128, 0, 0.1);\r\n }\r\n\r\n .status-inactive {\r\n color: red;\r\n background: rgba(255, 0, 0, 0.1);\r\n }\r\n", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "actions": { + "actionCellButton": [ + { + "name": "Show Device Info", + "icon": "info", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "custom", + "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "openInSeparateDialog": false, + "openInPopover": false, + "id": "94de7690-f91d-b032-6771-85af99abd749" + } + ] + } + }, + "row": 0, + "col": 0, + "id": "e3a9539d-2ccb-96e0-2afa-36f93e40233d", + "typeFullFqn": "system.cards.entities_table" + }, + "cf2eba6b-44f6-9cc2-6089-35c735f54898": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1684327643501, + "endTimeMs": 1684414043501 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "entitiesTitle": "Devices", + "enableSearch": true, + "enableSelectColumnDisplay": true, + "enableStickyHeader": true, + "enableStickyAction": true, + "reserveSpaceForHiddenAction": "true", + "displayEntityName": true, + "entityNameColumnTitle": "Device Name", + "displayEntityLabel": false, + "displayEntityType": false, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityName", + "useRowStyleFunction": false + }, + "title": "Devices", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "filterId": "3f74cbaa-6353-5e88-a7e8-708fc0e18039", + "dataKeys": [ + { + "name": "type", + "type": "entityField", + "label": "Device Type", + "color": "#2196f3", + "settings": {}, + "_hash": 0.3129929097366162, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "active", + "type": "attribute", + "label": "Status", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let cssClass;\r\nswitch (value) {\r\n case \"Active\":\r\n cssClass = \"status status-active\";\r\n break;\r\n case \"Inactive\":\r\n default:\r\n cssClass = \"status status-inactive\";\r\n break;\r\n }\r\n \r\n return `${value}`;", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5969880627410065, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value == 'true' ? \"Active\": \"Inactive\";" + }, + { + "name": "connectorName", + "type": "attribute", + "label": "Connector Name", + "color": "#f44336", + "settings": {}, + "_hash": 0.012483045440007778, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "connectorType", + "type": "attribute", + "label": "Connector Type", + "color": "#ffc107", + "settings": {}, + "_hash": 0.6004192233378134, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": ".status {\r\n border-radius: 20px;\r\n font-weight: 500;\r\n padding: 5px 15px;\r\n }\r\n\r\n .status-active {\r\n color: green;\r\n background: rgba(0, 128, 0, 0.1);\r\n }\r\n\r\n .status-inactive {\r\n color: red;\r\n background: rgba(255, 0, 0, 0.1);\r\n }\r\n", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "actions": { + "actionCellButton": [ + { + "name": "Show Device Info", + "icon": "info", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "custom", + "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "openInSeparateDialog": false, + "openInPopover": false, + "id": "18414f44-1c65-536a-14de-eaf21a7d56bd" + } + ] + } + }, + "row": 0, + "col": 0, + "id": "cf2eba6b-44f6-9cc2-6089-35c735f54898", + "typeFullFqn": "system.cards.entities_table" + }, + "3f6ed61b-f5af-13e3-7505-f69fd53f8211": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1684327643501, + "endTimeMs": 1684414043501 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "entitiesTitle": "Devices", + "enableSearch": true, + "enableSelectColumnDisplay": true, + "enableStickyHeader": true, + "enableStickyAction": true, + "reserveSpaceForHiddenAction": "true", + "displayEntityName": true, + "entityNameColumnTitle": "Device Name", + "displayEntityLabel": false, + "displayEntityType": false, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityName", + "useRowStyleFunction": false + }, + "title": "Devices", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "filterId": "c08eee84-64ee-73c4-8d96-c0df813a92cd", + "dataKeys": [ + { + "name": "type", + "type": "entityField", + "label": "Device Type", + "color": "#2196f3", + "settings": {}, + "_hash": 0.3129929097366162, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "active", + "type": "attribute", + "label": "Status", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let cssClass;\r\nswitch (value) {\r\n case \"Active\":\r\n cssClass = \"status status-active\";\r\n break;\r\n case \"Inactive\":\r\n default:\r\n cssClass = \"status status-inactive\";\r\n break;\r\n }\r\n \r\n return `${value}`;", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5969880627410065, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value == 'true' ? \"Active\": \"Inactive\";" + }, + { + "name": "connectorName", + "type": "attribute", + "label": "Connector Name", + "color": "#f44336", + "settings": {}, + "_hash": 0.012483045440007778, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "connectorType", + "type": "attribute", + "label": "Connector Type", + "color": "#ffc107", + "settings": {}, + "_hash": 0.6004192233378134, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": ".status {\r\n border-radius: 20px;\r\n font-weight: 500;\r\n padding: 5px 15px;\r\n }\r\n\r\n .status-active {\r\n color: green;\r\n background: rgba(0, 128, 0, 0.1);\r\n }\r\n\r\n .status-inactive {\r\n color: red;\r\n background: rgba(255, 0, 0, 0.1);\r\n }\r\n", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "actions": { + "actionCellButton": [ + { + "name": "Show Device Info", + "icon": "info", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "custom", + "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "openInSeparateDialog": false, + "openInPopover": false, + "id": "794974da-c9d2-a9f7-be47-c9eb642094e8" + } + ] + } + }, + "row": 0, + "col": 0, + "id": "3f6ed61b-f5af-13e3-7505-f69fd53f8211", + "typeFullFqn": "system.cards.entities_table" + }, + "b06cecaa-2806-65a9-782d-4f2d8cf95a6c": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1684327643501, + "endTimeMs": 1684414043501 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "entitiesTitle": "Devices", + "enableSearch": true, + "enableSelectColumnDisplay": true, + "enableStickyHeader": true, + "enableStickyAction": true, + "reserveSpaceForHiddenAction": "true", + "displayEntityName": true, + "entityNameColumnTitle": "Device Name", + "displayEntityLabel": false, + "displayEntityType": false, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityName", + "useRowStyleFunction": false + }, + "title": "Devices", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "filterId": "2f0af7f5-22ea-c0d5-3aef-7f2bb1b534ec", + "dataKeys": [ + { + "name": "type", + "type": "entityField", + "label": "Device Type", + "color": "#2196f3", + "settings": {}, + "_hash": 0.3129929097366162, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "active", + "type": "attribute", + "label": "Status", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let cssClass;\r\nswitch (value) {\r\n case \"Active\":\r\n cssClass = \"status status-active\";\r\n break;\r\n case \"Inactive\":\r\n default:\r\n cssClass = \"status status-inactive\";\r\n break;\r\n }\r\n \r\n return `${value}`;", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5969880627410065, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value == 'true' ? \"Active\": \"Inactive\";" + }, + { + "name": "connectorName", + "type": "attribute", + "label": "Connector Name", + "color": "#f44336", + "settings": {}, + "_hash": 0.012483045440007778, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "connectorType", + "type": "attribute", + "label": "Connector Type", + "color": "#ffc107", + "settings": {}, + "_hash": 0.6004192233378134, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": ".status {\r\n border-radius: 20px;\r\n font-weight: 500;\r\n padding: 5px 15px;\r\n }\r\n\r\n .status-active {\r\n color: green;\r\n background: rgba(0, 128, 0, 0.1);\r\n }\r\n\r\n .status-inactive {\r\n color: red;\r\n background: rgba(255, 0, 0, 0.1);\r\n }\r\n", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "actions": { + "actionCellButton": [ + { + "name": "Show Device Info", + "icon": "info", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "custom", + "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "openInSeparateDialog": false, + "openInPopover": false, + "id": "2add705b-3e53-8559-8126-380cac686fb0" + } + ] + } + }, + "row": 0, + "col": 0, + "id": "b06cecaa-2806-65a9-782d-4f2d8cf95a6c", + "typeFullFqn": "system.cards.entities_table" + }, + "c3d39b60-a668-7f5e-e6f4-cae27151f4aa": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1684327643501, + "endTimeMs": 1684414043501 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "entitiesTitle": "Devices", + "enableSearch": true, + "enableSelectColumnDisplay": true, + "enableStickyHeader": true, + "enableStickyAction": true, + "reserveSpaceForHiddenAction": "true", + "displayEntityName": true, + "entityNameColumnTitle": "Device Name", + "displayEntityLabel": false, + "displayEntityType": false, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityName", + "useRowStyleFunction": false + }, + "title": "Devices", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "filterId": "92a7d208-c143-ea20-5162-1da584532830", + "dataKeys": [ + { + "name": "type", + "type": "entityField", + "label": "Device Type", + "color": "#2196f3", + "settings": {}, + "_hash": 0.3129929097366162, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "active", + "type": "attribute", + "label": "Status", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let cssClass;\r\nswitch (value) {\r\n case \"Active\":\r\n cssClass = \"status status-active\";\r\n break;\r\n case \"Inactive\":\r\n default:\r\n cssClass = \"status status-inactive\";\r\n break;\r\n }\r\n \r\n return `${value}`;", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5969880627410065, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value == 'true' ? \"Active\": \"Inactive\";" + }, + { + "name": "connectorName", + "type": "attribute", + "label": "Connector Name", + "color": "#f44336", + "settings": {}, + "_hash": 0.012483045440007778, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "connectorType", + "type": "attribute", + "label": "Connector Type", + "color": "#ffc107", + "settings": {}, + "_hash": 0.6004192233378134, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": ".status {\r\n border-radius: 20px;\r\n font-weight: 500;\r\n padding: 5px 15px;\r\n }\r\n\r\n .status-active {\r\n color: green;\r\n background: rgba(0, 128, 0, 0.1);\r\n }\r\n\r\n .status-inactive {\r\n color: red;\r\n background: rgba(255, 0, 0, 0.1);\r\n }\r\n", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "actions": { + "actionCellButton": [ + { + "name": "Show Device Info", + "icon": "info", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "custom", + "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "openInSeparateDialog": false, + "openInPopover": false, + "id": "7e1ba820-9992-d52a-579b-20485abb3926" + } + ] + } + }, + "row": 0, + "col": 0, + "id": "c3d39b60-a668-7f5e-e6f4-cae27151f4aa", + "typeFullFqn": "system.cards.entities_table" + }, + "f78a0d66-60cb-188f-857f-9acd4d24bd5a": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1684327643501, + "endTimeMs": 1684414043501 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "entitiesTitle": "Devices", + "enableSearch": true, + "enableSelectColumnDisplay": true, + "enableStickyHeader": true, + "enableStickyAction": true, + "reserveSpaceForHiddenAction": "true", + "displayEntityName": true, + "entityNameColumnTitle": "Device Name", + "displayEntityLabel": false, + "displayEntityType": false, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityName", + "useRowStyleFunction": false + }, + "title": "Devices", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "filterId": "498f090c-b1e5-df74-35d1-3ecf89d33f1c", + "dataKeys": [ + { + "name": "type", + "type": "entityField", + "label": "Device Type", + "color": "#2196f3", + "settings": {}, + "_hash": 0.3129929097366162, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "active", + "type": "attribute", + "label": "Status", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let cssClass;\r\nswitch (value) {\r\n case \"Active\":\r\n cssClass = \"status status-active\";\r\n break;\r\n case \"Inactive\":\r\n default:\r\n cssClass = \"status status-inactive\";\r\n break;\r\n }\r\n \r\n return `${value}`;", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5969880627410065, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value == 'true' ? \"Active\": \"Inactive\";" + }, + { + "name": "connectorName", + "type": "attribute", + "label": "Connector Name", + "color": "#f44336", + "settings": {}, + "_hash": 0.012483045440007778, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "connectorType", + "type": "attribute", + "label": "Connector Type", + "color": "#ffc107", + "settings": {}, + "_hash": 0.6004192233378134, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": ".status {\r\n border-radius: 20px;\r\n font-weight: 500;\r\n padding: 5px 15px;\r\n }\r\n\r\n .status-active {\r\n color: green;\r\n background: rgba(0, 128, 0, 0.1);\r\n }\r\n\r\n .status-inactive {\r\n color: red;\r\n background: rgba(255, 0, 0, 0.1);\r\n }\r\n", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "actions": { + "actionCellButton": [ + { + "name": "Show Device Info", + "icon": "info", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "custom", + "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "openInSeparateDialog": false, + "openInPopover": false, + "id": "91af27c1-b37c-2276-6022-a332e41b2b33" + } + ] + } + }, + "row": 0, + "col": 0, + "id": "f78a0d66-60cb-188f-857f-9acd4d24bd5a", + "typeFullFqn": "system.cards.entities_table" + }, + "b81a171c-77c0-b857-21d2-cff02a1cb733": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1684327643501, + "endTimeMs": 1684414043501 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "entitiesTitle": "Devices", + "enableSearch": true, + "enableSelectColumnDisplay": true, + "enableStickyHeader": true, + "enableStickyAction": true, + "reserveSpaceForHiddenAction": "true", + "displayEntityName": true, + "entityNameColumnTitle": "Device Name", + "displayEntityLabel": false, + "displayEntityType": false, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityName", + "useRowStyleFunction": false + }, + "title": "Devices", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "filterId": "9175179d-a8db-848b-0762-e78da150e768", + "dataKeys": [ + { + "name": "type", + "type": "entityField", + "label": "Device Type", + "color": "#2196f3", + "settings": {}, + "_hash": 0.3129929097366162, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "active", + "type": "attribute", + "label": "Status", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let cssClass;\r\nswitch (value) {\r\n case \"Active\":\r\n cssClass = \"status status-active\";\r\n break;\r\n case \"Inactive\":\r\n default:\r\n cssClass = \"status status-inactive\";\r\n break;\r\n }\r\n \r\n return `${value}`;", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5969880627410065, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value == 'true' ? \"Active\": \"Inactive\";" + }, + { + "name": "connectorName", + "type": "attribute", + "label": "Connector Name", + "color": "#f44336", + "settings": {}, + "_hash": 0.012483045440007778, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "connectorType", + "type": "attribute", + "label": "Connector Type", + "color": "#ffc107", + "settings": {}, + "_hash": 0.6004192233378134, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": ".status {\r\n border-radius: 20px;\r\n font-weight: 500;\r\n padding: 5px 15px;\r\n }\r\n\r\n .status-active {\r\n color: green;\r\n background: rgba(0, 128, 0, 0.1);\r\n }\r\n\r\n .status-inactive {\r\n color: red;\r\n background: rgba(255, 0, 0, 0.1);\r\n }\r\n", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "actions": { + "actionCellButton": [ + { + "name": "Show Device Info", + "icon": "info", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "custom", + "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "openInSeparateDialog": false, + "openInPopover": false, + "id": "26cf8696-054b-13ec-7984-6fc5df20e6f1" + } + ] + } + }, + "row": 0, + "col": 0, + "id": "b81a171c-77c0-b857-21d2-cff02a1cb733", + "typeFullFqn": "system.cards.entities_table" + }, + "0e399bef-01d2-4e4e-02d2-e254ebe91e56": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1684327643501, + "endTimeMs": 1684414043501 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "entitiesTitle": "Devices", + "enableSearch": true, + "enableSelectColumnDisplay": true, + "enableStickyHeader": true, + "enableStickyAction": true, + "reserveSpaceForHiddenAction": "true", + "displayEntityName": true, + "entityNameColumnTitle": "Device Name", + "displayEntityLabel": false, + "displayEntityType": false, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityName", + "useRowStyleFunction": false + }, + "title": "Devices", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "filterId": "1b70460b-428b-2aed-f23a-65927d3e67bb", + "dataKeys": [ + { + "name": "type", + "type": "entityField", + "label": "Device Type", + "color": "#2196f3", + "settings": {}, + "_hash": 0.3129929097366162, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "active", + "type": "attribute", + "label": "Status", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let cssClass;\r\nswitch (value) {\r\n case \"Active\":\r\n cssClass = \"status status-active\";\r\n break;\r\n case \"Inactive\":\r\n default:\r\n cssClass = \"status status-inactive\";\r\n break;\r\n }\r\n \r\n return `${value}`;", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5969880627410065, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value == 'true' ? \"Active\": \"Inactive\";" + }, + { + "name": "connectorName", + "type": "attribute", + "label": "Connector Name", + "color": "#f44336", + "settings": {}, + "_hash": 0.012483045440007778, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "connectorType", + "type": "attribute", + "label": "Connector Type", + "color": "#ffc107", + "settings": {}, + "_hash": 0.6004192233378134, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": ".status {\r\n border-radius: 20px;\r\n font-weight: 500;\r\n padding: 5px 15px;\r\n }\r\n\r\n .status-active {\r\n color: green;\r\n background: rgba(0, 128, 0, 0.1);\r\n }\r\n\r\n .status-inactive {\r\n color: red;\r\n background: rgba(255, 0, 0, 0.1);\r\n }\r\n", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "actions": { + "actionCellButton": [ + { + "name": "Show Device Info", + "icon": "info", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "custom", + "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "openInSeparateDialog": false, + "openInPopover": false, + "id": "1dcfaf24-32be-cd19-62d6-86d12cc6a7ef" + } + ] + } + }, + "row": 0, + "col": 0, + "id": "0e399bef-01d2-4e4e-02d2-e254ebe91e56", + "typeFullFqn": "system.cards.entities_table" + }, + "819c1d39-de7c-8ac3-858e-0040d286823e": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1684327643501, + "endTimeMs": 1684414043501 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "entitiesTitle": "Devices", + "enableSearch": true, + "enableSelectColumnDisplay": true, + "enableStickyHeader": true, + "enableStickyAction": true, + "reserveSpaceForHiddenAction": "true", + "displayEntityName": true, + "entityNameColumnTitle": "Device Name", + "displayEntityLabel": false, + "displayEntityType": false, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityName", + "useRowStyleFunction": false + }, + "title": "Devices", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "filterId": "ae357478-b4c2-eee8-dde6-a6942fe6202f", + "dataKeys": [ + { + "name": "type", + "type": "entityField", + "label": "Device Type", + "color": "#2196f3", + "settings": {}, + "_hash": 0.3129929097366162, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "active", + "type": "attribute", + "label": "Status", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let cssClass;\r\nswitch (value) {\r\n case \"Active\":\r\n cssClass = \"status status-active\";\r\n break;\r\n case \"Inactive\":\r\n default:\r\n cssClass = \"status status-inactive\";\r\n break;\r\n }\r\n \r\n return `${value}`;", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5969880627410065, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value == 'true' ? \"Active\": \"Inactive\";" + }, + { + "name": "connectorName", + "type": "attribute", + "label": "Connector Name", + "color": "#f44336", + "settings": {}, + "_hash": 0.012483045440007778, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "connectorType", + "type": "attribute", + "label": "Connector Type", + "color": "#ffc107", + "settings": {}, + "_hash": 0.6004192233378134, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": ".status {\r\n border-radius: 20px;\r\n font-weight: 500;\r\n padding: 5px 15px;\r\n }\r\n\r\n .status-active {\r\n color: green;\r\n background: rgba(0, 128, 0, 0.1);\r\n }\r\n\r\n .status-inactive {\r\n color: red;\r\n background: rgba(255, 0, 0, 0.1);\r\n }\r\n", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "actions": { + "actionCellButton": [ + { + "name": "Show Device Info", + "icon": "info", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "custom", + "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "openInSeparateDialog": false, + "openInPopover": false, + "id": "ad2bc817-f3c4-150c-4672-8fe0c38aee8d" + } + ] + } + }, + "row": 0, + "col": 0, + "id": "819c1d39-de7c-8ac3-858e-0040d286823e", + "typeFullFqn": "system.cards.entities_table" + }, + "d4f73f32-f719-98bb-d427-b5c8957e8f47": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1684327643501, + "endTimeMs": 1684414043501 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "entitiesTitle": "Devices", + "enableSearch": true, + "enableSelectColumnDisplay": true, + "enableStickyHeader": true, + "enableStickyAction": true, + "reserveSpaceForHiddenAction": "true", + "displayEntityName": true, + "entityNameColumnTitle": "Device Name", + "displayEntityLabel": false, + "displayEntityType": false, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityName", + "useRowStyleFunction": false + }, + "title": "Devices", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "filterId": "6eef4979-369f-c2cc-4894-96a84b6a668a", + "dataKeys": [ + { + "name": "type", + "type": "entityField", + "label": "Device Type", + "color": "#2196f3", + "settings": {}, + "_hash": 0.3129929097366162, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "active", + "type": "attribute", + "label": "Status", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let cssClass;\r\nswitch (value) {\r\n case \"Active\":\r\n cssClass = \"status status-active\";\r\n break;\r\n case \"Inactive\":\r\n default:\r\n cssClass = \"status status-inactive\";\r\n break;\r\n }\r\n \r\n return `${value}`;", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5969880627410065, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value == 'true' ? \"Active\": \"Inactive\";" + }, + { + "name": "connectorName", + "type": "attribute", + "label": "Connector Name", + "color": "#f44336", + "settings": {}, + "_hash": 0.012483045440007778, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "connectorType", + "type": "attribute", + "label": "Connector Type", + "color": "#ffc107", + "settings": {}, + "_hash": 0.6004192233378134, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": ".status {\r\n border-radius: 20px;\r\n font-weight: 500;\r\n padding: 5px 15px;\r\n }\r\n\r\n .status-active {\r\n color: green;\r\n background: rgba(0, 128, 0, 0.1);\r\n }\r\n\r\n .status-inactive {\r\n color: red;\r\n background: rgba(255, 0, 0, 0.1);\r\n }\r\n", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "actions": { + "actionCellButton": [ + { + "name": "Show Device Info", + "icon": "info", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "custom", + "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "openInSeparateDialog": false, + "openInPopover": false, + "id": "d1ad84cd-bd9c-4dca-e4a0-f444ae8598bd" + } + ] + } + }, + "row": 0, + "col": 0, + "id": "d4f73f32-f719-98bb-d427-b5c8957e8f47", + "typeFullFqn": "system.cards.entities_table" + }, + "f16a258c-3f6c-9317-fda7-48b33d8fe8b9": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1684327643501, + "endTimeMs": 1684414043501 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "entitiesTitle": "Devices", + "enableSearch": true, + "enableSelectColumnDisplay": true, + "enableStickyHeader": true, + "enableStickyAction": true, + "reserveSpaceForHiddenAction": "true", + "displayEntityName": true, + "entityNameColumnTitle": "Device Name", + "displayEntityLabel": false, + "displayEntityType": false, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityName", + "useRowStyleFunction": false + }, + "title": "Devices", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "filterId": "9c8e3a63-01a1-64b5-fe44-4f58f8350340", + "dataKeys": [ + { + "name": "type", + "type": "entityField", + "label": "Device Type", + "color": "#2196f3", + "settings": {}, + "_hash": 0.3129929097366162, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "active", + "type": "attribute", + "label": "Status", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let cssClass;\r\nswitch (value) {\r\n case \"Active\":\r\n cssClass = \"status status-active\";\r\n break;\r\n case \"Inactive\":\r\n default:\r\n cssClass = \"status status-inactive\";\r\n break;\r\n }\r\n \r\n return `${value}`;", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5969880627410065, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value == 'true' ? \"Active\": \"Inactive\";" + }, + { + "name": "connectorName", + "type": "attribute", + "label": "Connector Name", + "color": "#f44336", + "settings": {}, + "_hash": 0.012483045440007778, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "connectorType", + "type": "attribute", + "label": "Connector Type", + "color": "#ffc107", + "settings": {}, + "_hash": 0.6004192233378134, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": ".status {\r\n border-radius: 20px;\r\n font-weight: 500;\r\n padding: 5px 15px;\r\n }\r\n\r\n .status-active {\r\n color: green;\r\n background: rgba(0, 128, 0, 0.1);\r\n }\r\n\r\n .status-inactive {\r\n color: red;\r\n background: rgba(255, 0, 0, 0.1);\r\n }\r\n", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "actions": { + "actionCellButton": [ + { + "name": "Show Device Info", + "icon": "info", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "custom", + "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "openInSeparateDialog": false, + "openInPopover": false, + "id": "bf80eef9-b879-9a08-40a4-488dbdefa125" + } + ] + } + }, + "row": 0, + "col": 0, + "id": "f16a258c-3f6c-9317-fda7-48b33d8fe8b9", + "typeFullFqn": "system.cards.entities_table" + }, + "d1951ec7-ab13-87e4-bc05-ce2318dca353": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1684327643501, + "endTimeMs": 1684414043501 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "entitiesTitle": "Devices", + "enableSearch": true, + "enableSelectColumnDisplay": true, + "enableStickyHeader": true, + "enableStickyAction": true, + "reserveSpaceForHiddenAction": "true", + "displayEntityName": true, + "entityNameColumnTitle": "Device Name", + "displayEntityLabel": false, + "displayEntityType": false, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityName", + "useRowStyleFunction": false + }, + "title": "Devices", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "filterId": "c6501413-d823-29c4-992f-9ae6e8e25549", + "dataKeys": [ + { + "name": "type", + "type": "entityField", + "label": "Device Type", + "color": "#2196f3", + "settings": {}, + "_hash": 0.3129929097366162, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "active", + "type": "attribute", + "label": "Status", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let cssClass;\r\nswitch (value) {\r\n case \"Active\":\r\n cssClass = \"status status-active\";\r\n break;\r\n case \"Inactive\":\r\n default:\r\n cssClass = \"status status-inactive\";\r\n break;\r\n }\r\n \r\n return `${value}`;", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5969880627410065, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value == 'true' ? \"Active\": \"Inactive\";" + }, + { + "name": "connectorName", + "type": "attribute", + "label": "Connector Name", + "color": "#f44336", + "settings": {}, + "_hash": 0.012483045440007778, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "connectorType", + "type": "attribute", + "label": "Connector Type", + "color": "#ffc107", + "settings": {}, + "_hash": 0.6004192233378134, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": ".status {\r\n border-radius: 20px;\r\n font-weight: 500;\r\n padding: 5px 15px;\r\n }\r\n\r\n .status-active {\r\n color: green;\r\n background: rgba(0, 128, 0, 0.1);\r\n }\r\n\r\n .status-inactive {\r\n color: red;\r\n background: rgba(255, 0, 0, 0.1);\r\n }\r\n", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "actions": { + "actionCellButton": [ + { + "name": "Show Device Info", + "icon": "info", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "custom", + "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "openInSeparateDialog": false, + "openInPopover": false, + "id": "b5a406b3-cc0a-8a09-9aec-3f8befae5fb8" + } + ] + } + }, + "row": 0, + "col": 0, + "id": "d1951ec7-ab13-87e4-bc05-ce2318dca353", + "typeFullFqn": "system.cards.entities_table" + }, + "75b6372d-4def-42b4-8774-4edf413a8b83": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1684327643501, + "endTimeMs": 1684414043501 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "entitiesTitle": "Devices", + "enableSearch": true, + "enableSelectColumnDisplay": true, + "enableStickyHeader": true, + "enableStickyAction": true, + "reserveSpaceForHiddenAction": "true", + "displayEntityName": true, + "entityNameColumnTitle": "Device Name", + "displayEntityLabel": false, + "displayEntityType": false, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityName", + "useRowStyleFunction": false + }, + "title": "Devices", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", + "filterId": "2f04d6e5-8438-857a-ca78-ae78cc8b0c03", + "dataKeys": [ + { + "name": "type", + "type": "entityField", + "label": "Device Type", + "color": "#2196f3", + "settings": {}, + "_hash": 0.3129929097366162, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "active", + "type": "attribute", + "label": "Status", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "let cssClass;\r\nswitch (value) {\r\n case \"Active\":\r\n cssClass = \"status status-active\";\r\n break;\r\n case \"Inactive\":\r\n default:\r\n cssClass = \"status status-inactive\";\r\n break;\r\n }\r\n \r\n return `${value}`;", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5969880627410065, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value == 'true' ? \"Active\": \"Inactive\";" + }, + { + "name": "connectorName", + "type": "attribute", + "label": "Connector Name", + "color": "#f44336", + "settings": {}, + "_hash": 0.012483045440007778, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "connectorType", + "type": "attribute", + "label": "Connector Type", + "color": "#ffc107", + "settings": {}, + "_hash": 0.6004192233378134, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": ".status {\r\n border-radius: 20px;\r\n font-weight: 500;\r\n padding: 5px 15px;\r\n }\r\n\r\n .status-active {\r\n color: green;\r\n background: rgba(0, 128, 0, 0.1);\r\n }\r\n\r\n .status-inactive {\r\n color: red;\r\n background: rgba(255, 0, 0, 0.1);\r\n }\r\n", + "pageSize": 1024, + "noDataDisplayMessage": "", + "enableDataExport": false, + "actions": { + "actionCellButton": [ + { + "name": "Show Device Info", + "icon": "info", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "custom", + "customFunction": "const url = `${window.location.origin}/entities/devices/${entityId.id}`;\nwindow.open(url, '_blank');", + "openInSeparateDialog": false, + "openInPopover": false, + "id": "ec1dfba3-4b43-2491-8948-f602337f8a3b" + } + ] + } + }, + "row": 0, + "col": 0, + "id": "75b6372d-4def-42b4-8774-4edf413a8b83", + "typeFullFqn": "system.cards.entities_table" + } + }, + "states": { + "default": { + "name": "Gateway List", + "root": true, + "layouts": { + "main": { + "widgets": { + "23b9adc5-b3bd-040f-e5eb-a45925c73257": { + "sizeX": 48, + "sizeY": 23, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 48, + "margin": 12, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } + }, + "configuration": { + "name": "Configuration", + "root": false, + "layouts": { + "main": { + "widgets": { + "d4a1ffc4-c9f6-7b15-bca0-17feb58bac99": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0, + "mobileHeight": null + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 12, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } + }, + "connectors": { + "name": "Connectors", + "root": false, + "layouts": { + "main": { + "widgets": { + "02a05113-d878-b086-328e-75bd6f02b2bb": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 0, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } + }, + "gateway_details": { + "name": "${entityName}", + "root": false, + "layouts": { + "main": { + "widgets": { + "79f59106-758f-c428-8b93-4341faea705d": { + "sizeX": 40, + "sizeY": 3, + "row": 0, + "col": 0, + "mobileHeight": 5, + "mobileOrder": 2 + }, + "60dcf518-8fc3-3539-8ff8-ce94bda39f3a": { + "sizeX": 40, + "sizeY": 10, + "row": 12, + "col": 0 + }, + "fb9df382-6ef3-4aa6-bc13-8bf8e300ba19": { + "sizeX": 8, + "sizeY": 3, + "row": 0, + "col": 40, + "mobileOrder": 0, + "mobileHeight": 1 + }, + "61d149e8-b249-5526-e5d7-6ad58413982e": { + "sizeX": 8, + "sizeY": 19, + "row": 3, + "col": 40, + "mobileOrder": 1, + "mobileHeight": 3 + }, + "3d661190-7463-ba61-6793-503c85af67ec": { + "sizeX": 40, + "sizeY": 9, + "row": 3, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 48, + "margin": 10, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } + }, + "logs": { + "name": "Logs", + "root": false, + "layouts": { + "main": { + "widgets": { + "27e6b37c-085e-824d-fb13-69651a545ec1": { + "sizeX": 48, + "sizeY": 16, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 48, + "margin": 12, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } + }, + "connector_logs": { + "name": "Logs", + "root": false, + "layouts": { + "main": { + "widgets": { + "bcd04dbf-c82a-ca13-6e88-a74f32b4348a": { + "sizeX": 24, + "sizeY": 10, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 12, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } + }, + "statistics": { + "name": "Statistics", + "root": false, + "layouts": { + "main": { + "widgets": { + "9b88dc51-b186-90ef-5afb-9648e6e2ac9a": { + "sizeX": 48, + "sizeY": 12, + "row": 10, + "col": 0 + }, + "c50e294a-265a-d13c-c772-b56c4df953fe": { + "sizeX": 48, + "sizeY": 10, + "row": 0, + "col": 0, + "mobileOrder": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 48, + "margin": 12, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } + }, + "remote_shell": { + "name": "Remote Shell", + "root": false, + "layouts": { + "main": { + "widgets": { + "2a318e56-5c83-4f82-bf23-df72dd7e3edf": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 10, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } + }, + "rpc": { + "name": "RPC", + "root": false, + "layouts": { + "main": { + "widgets": { + "da01e13f-c108-39fd-2a74-b40da088769a": { + "sizeX": 45, + "sizeY": 10, + "row": 12, + "col": 0, + "mobileOrder": 1 + }, + "ce3065bf-d898-2c03-6236-ce1b6cdf1f3f": { + "sizeX": 45, + "sizeY": 9, + "row": 22, + "col": 0, + "mobileOrder": 2 + }, + "46b68eb5-7e2d-bca1-c2ef-061b64575218": { + "sizeX": 45, + "sizeY": 12, + "row": 0, + "col": 0, + "mobileOrder": 0, + "mobileHeight": 8 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 45, + "margin": 12, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } + }, + "connector_rpc": { + "name": "Connector RPC", + "root": false, + "layouts": { + "main": { + "widgets": { + "878c68fd-71cb-21d4-684a-b5b5a4526b43": { + "sizeX": 48, + "sizeY": 13, + "row": 0, + "col": 0, + "mobileOrder": 0, + "mobileHeight": 8 + }, + "0fc7dcf3-32a2-0159-738e-96956bf0d450": { + "sizeX": 48, + "sizeY": 10, + "row": 13, + "col": 0, + "mobileOrder": 1, + "mobileHeight": null + }, + "6e136fbc-545b-4926-c0a9-79348f35628a": { + "sizeX": 48, + "sizeY": 9, + "row": 23, + "col": 0, + "mobileOrder": 2 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 48, + "margin": 12, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } + }, + "gateway_devices_0": { + "name": "Gateway devices", + "root": false, + "layouts": { + "main": { + "widgets": { + "1615bd4e-c0a4-c32c-3706-3c83214cb8d7": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 0, + "outerMargin": true, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "gateway_devices_1": { + "name": "gateway_devices_mqtt", + "root": false, + "layouts": { + "main": { + "widgets": { + "aafba3d8-a381-21c0-ecbe-446da3cdc041": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 0, + "outerMargin": true, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "gateway_devices_2": { + "name": "gateway_devices_modbus", + "root": false, + "layouts": { + "main": { + "widgets": { + "7f676bb2-bde2-10e7-80d8-25dd734e8e22": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 0, + "outerMargin": true, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "gateway_devices_3": { + "name": "gateway_devices_grpc", + "root": false, + "layouts": { + "main": { + "widgets": { + "d64482d8-001a-6f33-9b56-665530098fe5": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 0, + "outerMargin": true, + "backgroundSizeMode": "100%", + "autoFillHeight": false, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "gateway_devices_4": { + "name": "gateway_devices_opcua", + "root": false, + "layouts": { + "main": { + "widgets": { + "bb27723a-989c-2327-5808-b56d490b93ab": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 0, + "outerMargin": true, + "backgroundSizeMode": "100%", + "autoFillHeight": false, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "gateway_devices_5": { + "name": "gateway_devices_opcua_async", + "root": false, + "layouts": { + "main": { + "widgets": { + "e3a9539d-2ccb-96e0-2afa-36f93e40233d": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 0, + "outerMargin": true, + "backgroundSizeMode": "100%", + "autoFillHeight": false, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "gateway_devices_6": { + "name": "gateway_devices_ble", + "root": false, + "layouts": { + "main": { + "widgets": { + "cf2eba6b-44f6-9cc2-6089-35c735f54898": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 0, + "outerMargin": true, + "backgroundSizeMode": "100%", + "autoFillHeight": false, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "gateway_devices_7": { + "name": "gateway_devices_request", + "root": false, + "layouts": { + "main": { + "widgets": { + "3f6ed61b-f5af-13e3-7505-f69fd53f8211": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 0, + "outerMargin": true, + "backgroundSizeMode": "100%", + "autoFillHeight": false, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "gateway_devices_8": { + "name": "gateway_devices_can", + "root": false, + "layouts": { + "main": { + "widgets": { + "b06cecaa-2806-65a9-782d-4f2d8cf95a6c": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 0, + "outerMargin": true, + "backgroundSizeMode": "100%", + "autoFillHeight": false, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "gateway_devices_9": { + "name": "gateway_devices_bacnet", + "root": false, + "layouts": { + "main": { + "widgets": { + "c3d39b60-a668-7f5e-e6f4-cae27151f4aa": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 0, + "outerMargin": true, + "backgroundSizeMode": "100%", + "autoFillHeight": false, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "gateway_devices_10": { + "name": "gateway_devices_odbc", + "root": false, + "layouts": { + "main": { + "widgets": { + "f78a0d66-60cb-188f-857f-9acd4d24bd5a": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 0, + "outerMargin": true, + "backgroundSizeMode": "100%", + "autoFillHeight": false, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "gateway_devices_11": { + "name": "gateway_devices_rest", + "root": false, + "layouts": { + "main": { + "widgets": { + "b81a171c-77c0-b857-21d2-cff02a1cb733": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 0, + "outerMargin": true, + "backgroundSizeMode": "100%", + "autoFillHeight": false, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "gateway_devices_12": { + "name": "gateway_devices_snmp", + "root": false, + "layouts": { + "main": { + "widgets": { + "0e399bef-01d2-4e4e-02d2-e254ebe91e56": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 0, + "outerMargin": true, + "backgroundSizeMode": "100%", + "autoFillHeight": false, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "gateway_devices_13": { + "name": "gateway_devices_ftp", + "root": false, + "layouts": { + "main": { + "widgets": { + "819c1d39-de7c-8ac3-858e-0040d286823e": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 0, + "outerMargin": true, + "backgroundSizeMode": "100%", + "autoFillHeight": false, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "gateway_devices_14": { + "name": "gateway_devices_socket", + "root": false, + "layouts": { + "main": { + "widgets": { + "d4f73f32-f719-98bb-d427-b5c8957e8f47": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 0, + "outerMargin": true, + "backgroundSizeMode": "100%", + "autoFillHeight": false, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "gateway_devices_15": { + "name": "gateway_devices_xmpp", + "root": false, + "layouts": { + "main": { + "widgets": { + "f16a258c-3f6c-9317-fda7-48b33d8fe8b9": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 0, + "outerMargin": true, + "backgroundSizeMode": "100%", + "autoFillHeight": false, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "gateway_devices_16": { + "name": "gateway_devices_occp", + "root": false, + "layouts": { + "main": { + "widgets": { + "d1951ec7-ab13-87e4-bc05-ce2318dca353": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 0, + "outerMargin": true, + "backgroundSizeMode": "100%", + "autoFillHeight": false, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "gateway_devices_17": { + "name": "gateway_devices_custom", + "root": false, + "layouts": { + "main": { + "widgets": { + "75b6372d-4def-42b4-8774-4edf413a8b83": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 24, + "margin": 0, + "outerMargin": true, + "backgroundSizeMode": "100%", + "autoFillHeight": false, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + } + }, + "entityAliases": { + "9b7a1caa-3510-3daa-e05a-fc39b851d2c1": { + "id": "9b7a1caa-3510-3daa-e05a-fc39b851d2c1", + "alias": "Devices", + "filter": { + "type": "entityType", + "resolveMultiple": true, + "entityType": "DEVICE" + } + }, + "a2f01c66-96cf-49c5-303f-e6f21c559ee8": { + "id": "a2f01c66-96cf-49c5-303f-e6f21c559ee8", + "alias": "Selected Gateway", + "filter": { + "type": "stateEntity", + "resolveMultiple": false, + "stateEntityParamName": "", + "defaultStateEntity": null + } + }, + "a75d9031-ba51-8da4-81be-de65061b72f4": { + "id": "a75d9031-ba51-8da4-81be-de65061b72f4", + "alias": "GatewayDevices", + "filter": { + "type": "relationsQuery", + "resolveMultiple": true, + "rootStateEntity": true, + "stateEntityParamName": null, + "defaultStateEntity": null, + "rootEntity": null, + "direction": "FROM", + "maxLevel": 1, + "fetchLastLevelOnly": false, + "filters": [ + { + "relationType": "Created", + "entityTypes": [ + "DEVICE" + ] + } + ] + } + }, + "e3ea8714-bd05-67aa-8413-e07e93d53930": { + "id": "e3ea8714-bd05-67aa-8413-e07e93d53930", + "alias": "Current User", + "filter": { + "type": "singleEntity", + "resolveMultiple": false, + "singleEntity": { + "entityType": "CURRENT_USER", + "id": "13814000-1dd2-11b2-8080-808080808080" + } + } + } + }, + "filters": { + "abdfdcf9-75d4-001d-783a-920cb8e2a7e1": { + "id": "abdfdcf9-75d4-001d-783a-920cb8e2a7e1", + "filter": "Gateway", + "keyFilters": [ + { + "key": { + "type": "ENTITY_FIELD", + "key": "additionalInfo" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "CONTAINS", + "value": { + "defaultValue": "\"gateway\":true", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": true + }, + "bd33a006-fd40-3efc-7863-505c6e64f2b8": { + "id": "bd33a006-fd40-3efc-7863-505c6e64f2b8", + "filter": "isActive", + "keyFilters": [ + { + "key": { + "type": "ATTRIBUTE", + "key": "active" + }, + "valueType": "BOOLEAN", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": true, + "dynamicValue": null + }, + "type": "BOOLEAN" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": true + }, + "956cf341-7cdf-82a7-ea2a-50cc9586f1f1": { + "id": "956cf341-7cdf-82a7-ea2a-50cc9586f1f1", + "filter": "isInactive", + "keyFilters": [ + { + "key": { + "type": "ATTRIBUTE", + "key": "active" + }, + "valueType": "BOOLEAN", + "predicates": [ + { + "keyFilterPredicate": { + "type": "BOOLEAN", + "operation": "EQUAL", + "value": { + "defaultValue": false + } + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": true + }, + "bd9176e1-9e04-3e9b-d5a5-07b72bb9ae90": { + "id": "bd9176e1-9e04-3e9b-d5a5-07b72bb9ae90", + "filter": "gateway_devices_mqtt", + "keyFilters": [ + { + "key": { + "type": "ATTRIBUTE", + "key": "connectorType" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "mqtt", + "dynamicValue": null + }, + "ignoreCase": true, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": true + }, + "44038462-1bae-e075-7b31-283341cb2295": { + "id": "44038462-1bae-e075-7b31-283341cb2295", + "filter": "gateway_devices_modbus", + "keyFilters": [ + { + "key": { + "type": "ATTRIBUTE", + "key": "connectorType" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "modbus", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": true + }, + "884e9c34-7534-a483-99be-81b56cd91185": { + "id": "884e9c34-7534-a483-99be-81b56cd91185", + "filter": "gateway_devices_grpc", + "keyFilters": [ + { + "key": { + "type": "ATTRIBUTE", + "key": "connectorType" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "grpc", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": true + }, + "e91ca0e9-1653-4fbc-5f3d-3da021b1b415": { + "id": "e91ca0e9-1653-4fbc-5f3d-3da021b1b415", + "filter": "gateway_devices_opcua", + "keyFilters": [ + { + "key": { + "type": "ATTRIBUTE", + "key": "connectorType" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "opcua", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": true + }, + "3931abd5-2205-9386-6ea9-8e8a8131bb9d": { + "id": "3931abd5-2205-9386-6ea9-8e8a8131bb9d", + "filter": "gateway_devices_opcua_asyncio", + "keyFilters": [ + { + "key": { + "type": "ATTRIBUTE", + "key": "connectorType" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "opcua_asyncio", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": true + }, + "3f74cbaa-6353-5e88-a7e8-708fc0e18039": { + "id": "3f74cbaa-6353-5e88-a7e8-708fc0e18039", + "filter": "gateway_devices_ble", + "keyFilters": [ + { + "key": { + "type": "ATTRIBUTE", + "key": "connectorType" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "ble", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": true + }, + "c08eee84-64ee-73c4-8d96-c0df813a92cd": { + "id": "c08eee84-64ee-73c4-8d96-c0df813a92cd", + "filter": "gateway_devices_request", + "keyFilters": [ + { + "key": { + "type": "ATTRIBUTE", + "key": "connectorType" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "request", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": true + }, + "2f0af7f5-22ea-c0d5-3aef-7f2bb1b534ec": { + "id": "2f0af7f5-22ea-c0d5-3aef-7f2bb1b534ec", + "filter": "gateway_devices_can", + "keyFilters": [ + { + "key": { + "type": "ATTRIBUTE", + "key": "connectorType" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "can", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": true + }, + "92a7d208-c143-ea20-5162-1da584532830": { + "id": "92a7d208-c143-ea20-5162-1da584532830", + "filter": "gateway_devices_bacnet", + "keyFilters": [ + { + "key": { + "type": "ATTRIBUTE", + "key": "connectorType" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "bacnet", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": true + }, + "498f090c-b1e5-df74-35d1-3ecf89d33f1c": { + "id": "498f090c-b1e5-df74-35d1-3ecf89d33f1c", + "filter": "gateway_devices_odbc", + "keyFilters": [ + { + "key": { + "type": "ATTRIBUTE", + "key": "connectorType" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "odbc", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": true + }, + "9175179d-a8db-848b-0762-e78da150e768": { + "id": "9175179d-a8db-848b-0762-e78da150e768", + "filter": "gateway_devices_rest", + "keyFilters": [ + { + "key": { + "type": "ATTRIBUTE", + "key": "connectorType" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "rest", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": true + }, + "1b70460b-428b-2aed-f23a-65927d3e67bb": { + "id": "1b70460b-428b-2aed-f23a-65927d3e67bb", + "filter": "gateway_devices_snmp", + "keyFilters": [ + { + "key": { + "type": "ATTRIBUTE", + "key": "connectorType" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "snmp", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": true + }, + "ae357478-b4c2-eee8-dde6-a6942fe6202f": { + "id": "ae357478-b4c2-eee8-dde6-a6942fe6202f", + "filter": "gateway_devices_ftp", + "keyFilters": [ + { + "key": { + "type": "ATTRIBUTE", + "key": "connectorType" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "ftp", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": true + }, + "6eef4979-369f-c2cc-4894-96a84b6a668a": { + "id": "6eef4979-369f-c2cc-4894-96a84b6a668a", + "filter": "gateway_devices_socket", + "keyFilters": [ + { + "key": { + "type": "ATTRIBUTE", + "key": "connectorType" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "socket", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": true + }, + "9c8e3a63-01a1-64b5-fe44-4f58f8350340": { + "id": "9c8e3a63-01a1-64b5-fe44-4f58f8350340", + "filter": "gateway_devices_xmpp", + "keyFilters": [ + { + "key": { + "type": "ATTRIBUTE", + "key": "connectorType" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "xmpp", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": true + }, + "c6501413-d823-29c4-992f-9ae6e8e25549": { + "id": "c6501413-d823-29c4-992f-9ae6e8e25549", + "filter": "gateway_devices_ocpp", + "keyFilters": [ + { + "key": { + "type": "ATTRIBUTE", + "key": "connectorType" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "ocpp", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": true + }, + "2f04d6e5-8438-857a-ca78-ae78cc8b0c03": { + "id": "2f04d6e5-8438-857a-ca78-ae78cc8b0c03", + "filter": "gateway_devices_custom", + "keyFilters": [ + { + "key": { + "type": "ATTRIBUTE", + "key": "connectorType" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "custom", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": true + } + }, + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 300000, + "interval": 120000, + "fixedTimewindow": { + "startTimeMs": 1686306375309.7058, + "endTimeMs": 1686307998839.1177 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 25000 + }, + "timezone": null + }, + "settings": { + "stateControllerId": "entity", + "showTitle": false, + "showDashboardsSelect": false, + "showEntitiesSelect": false, + "showDashboardTimewindow": true, + "showDashboardExport": false, + "toolbarAlwaysOpen": true, + "titleColor": "rgba(0,0,0,0.870588)", + "showDashboardLogo": false, + "dashboardLogoUrl": null, + "hideToolbar": false, + "showFilters": false, + "showUpdateDashboardImage": false, + "dashboardCss": "" + } + }, + "externalId": null, + "name": "Gateway" +} diff --git a/application/src/main/data/json/tenant/device_profile/rule_chain_template.json b/application/src/main/data/json/tenant/device_profile/rule_chain_template.json index 4776ef2aae..0256a2ccf2 100644 --- a/application/src/main/data/json/tenant/device_profile/rule_chain_template.json +++ b/application/src/main/data/json/tenant/device_profile/rule_chain_template.json @@ -32,8 +32,12 @@ "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", "name": "Save Client Attributes", "debugMode": false, + "configurationVersion": 2, "configuration": { - "scope": "CLIENT_SCOPE" + "scope": "CLIENT_SCOPE", + "notifyDevice": false, + "sendAttributesUpdatedNotification": false, + "updateAttributesOnlyOnValueChange": true } }, { diff --git a/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json b/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json index 88ef27e2f8..0b70d087e7 100644 --- a/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json +++ b/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json @@ -31,9 +31,12 @@ "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", "name": "Save Client Attributes", "debugMode": false, + "configurationVersion": 2, "configuration": { "scope": "CLIENT_SCOPE", - "notifyDevice": "false" + "notifyDevice": false, + "sendAttributesUpdatedNotification": false, + "updateAttributesOnlyOnValueChange": true } }, { diff --git a/application/src/main/data/upgrade/3.5.1/schema_update.sql b/application/src/main/data/upgrade/3.5.1/schema_update.sql index 58031ce5c0..fab91a4e67 100644 --- a/application/src/main/data/upgrade/3.5.1/schema_update.sql +++ b/application/src/main/data/upgrade/3.5.1/schema_update.sql @@ -53,9 +53,144 @@ $$; -- NOTIFICATION CONFIGS VERSION CONTROL END +-- EDGE EVENTS MIGRATION START +DO +$$ + DECLARE table_partition RECORD; + BEGIN + -- in case of running the upgrade script a second time: + IF NOT (SELECT exists(SELECT FROM pg_tables WHERE tablename = 'old_edge_event')) THEN + ALTER TABLE edge_event RENAME TO old_edge_event; + CREATE INDEX IF NOT EXISTS idx_old_edge_event_created_time_tmp ON old_edge_event(created_time); + ALTER INDEX IF EXISTS idx_edge_event_tenant_id_and_created_time RENAME TO idx_old_edge_event_tenant_id_and_created_time; + + FOR table_partition IN SELECT tablename AS name, split_part(tablename, '_', 3) AS partition_ts + FROM pg_tables WHERE tablename LIKE 'edge_event_%' + LOOP + EXECUTE format('ALTER TABLE %s RENAME TO old_edge_event_%s', table_partition.name, table_partition.partition_ts); + END LOOP; + ELSE + RAISE NOTICE 'Table old_edge_event already exists, leaving as is'; + END IF; + END; +$$; + +CREATE TABLE IF NOT EXISTS edge_event ( + seq_id INT GENERATED ALWAYS AS IDENTITY, + id uuid NOT NULL, + created_time bigint NOT NULL, + edge_id uuid, + edge_event_type varchar(255), + edge_event_uid varchar(255), + entity_id uuid, + edge_event_action varchar(255), + body varchar(10000000), + tenant_id uuid, + ts bigint NOT NULL +) PARTITION BY RANGE (created_time); +CREATE INDEX IF NOT EXISTS idx_edge_event_tenant_id_and_created_time ON edge_event(tenant_id, created_time DESC); +CREATE INDEX IF NOT EXISTS idx_edge_event_id ON edge_event(id); +ALTER TABLE IF EXISTS edge_event ALTER COLUMN seq_id SET CYCLE; + +CREATE OR REPLACE PROCEDURE migrate_edge_event(IN start_time_ms BIGINT, IN end_time_ms BIGINT, IN partition_size_ms BIGINT) + LANGUAGE plpgsql AS +$$ +DECLARE + p RECORD; + partition_end_ts BIGINT; +BEGIN + IF (SELECT exists(SELECT FROM pg_tables WHERE tablename = 'old_edge_event')) THEN + FOR p IN SELECT DISTINCT (created_time - created_time % partition_size_ms) AS partition_ts FROM old_edge_event + WHERE created_time >= start_time_ms AND created_time < end_time_ms + LOOP + partition_end_ts = p.partition_ts + partition_size_ms; + RAISE NOTICE '[edge_event] Partition to create : [%-%]', p.partition_ts, partition_end_ts; + EXECUTE format('CREATE TABLE IF NOT EXISTS edge_event_%s PARTITION OF edge_event ' || + 'FOR VALUES FROM ( %s ) TO ( %s )', p.partition_ts, p.partition_ts, partition_end_ts); + END LOOP; + INSERT INTO edge_event (id, created_time, edge_id, edge_event_type, edge_event_uid, entity_id, edge_event_action, body, tenant_id, ts) + SELECT id, created_time, edge_id, edge_event_type, edge_event_uid, entity_id, edge_event_action, body, tenant_id, ts + FROM old_edge_event + WHERE created_time >= start_time_ms AND created_time < end_time_ms; + ELSE + RAISE NOTICE 'Table old_edge_event does not exists, skipping migration'; + END IF; +END; +$$; +-- EDGE EVENTS MIGRATION END + ALTER TABLE resource ADD COLUMN IF NOT EXISTS etag varchar; UPDATE resource SET etag = encode(sha256(decode(resource.data, 'base64')),'hex') WHERE resource.data is not null; +ALTER TABLE notification_request ALTER COLUMN info SET DATA TYPE varchar(1000000); + +DELETE FROM alarm WHERE tenant_id NOT IN (SELECT id FROM tenant); + +CREATE TABLE IF NOT EXISTS alarm_types ( + tenant_id uuid NOT NULL, + type varchar(255) NOT NULL, + CONSTRAINT tenant_id_type_unq_key UNIQUE (tenant_id, type), + CONSTRAINT fk_entity_tenant_id FOREIGN KEY (tenant_id) REFERENCES tenant(id) ON DELETE CASCADE +); + +INSERT INTO alarm_types (tenant_id, type) SELECT DISTINCT tenant_id, type FROM alarm ON CONFLICT (tenant_id, type) DO NOTHING; + +ALTER TABLE widgets_bundle ALTER COLUMN description SET DATA TYPE varchar(1024); +ALTER TABLE widget_type ALTER COLUMN description SET DATA TYPE varchar(1024); + +ALTER TABLE widget_type + ADD COLUMN IF NOT EXISTS fqn varchar(512); +ALTER TABLE widget_type + ADD COLUMN IF NOT EXISTS deprecated boolean NOT NULL DEFAULT false; +DO +$$ + BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'uq_widget_type_fqn') THEN + UPDATE widget_type SET fqn = concat(widget_type.bundle_alias, '.', widget_type.alias); + ALTER TABLE widget_type ADD CONSTRAINT uq_widget_type_fqn UNIQUE (tenant_id, fqn); + ALTER TABLE widget_type DROP COLUMN IF EXISTS alias; + END IF; + END; +$$; + +ALTER TABLE widget_type + ADD COLUMN IF NOT EXISTS external_id UUID; +DO +$$ + BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'widget_type_external_id_unq_key') THEN + ALTER TABLE widget_type ADD CONSTRAINT widget_type_external_id_unq_key UNIQUE (tenant_id, external_id); + END IF; + END; +$$; + +DO +$$ + BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'uq_widgets_bundle_alias') THEN + ALTER TABLE widgets_bundle ADD CONSTRAINT uq_widgets_bundle_alias UNIQUE (tenant_id, alias); + END IF; + END; +$$; + +CREATE TABLE IF NOT EXISTS widgets_bundle_widget ( + widgets_bundle_id uuid NOT NULL, + widget_type_id uuid NOT NULL, + widget_type_order int NOT NULL DEFAULT 0, + CONSTRAINT widgets_bundle_widget_pkey PRIMARY KEY (widgets_bundle_id, widget_type_id), + CONSTRAINT fk_widgets_bundle FOREIGN KEY (widgets_bundle_id) REFERENCES widgets_bundle(id) ON DELETE CASCADE, + CONSTRAINT fk_widget_type FOREIGN KEY (widget_type_id) REFERENCES widget_type(id) ON DELETE CASCADE +); + +DO +$$ + BEGIN + IF EXISTS(SELECT 1 FROM information_schema.columns WHERE table_name = 'widget_type' and column_name='bundle_alias') THEN + INSERT INTO widgets_bundle_widget SELECT wb.id as widgets_bundle_id, wt.id as widget_type_id from widget_type wt left join widgets_bundle wb ON wt.bundle_alias = wb.alias AND wt.tenant_id = wb.tenant_id ON CONFLICT (widgets_bundle_id, widget_type_id) DO NOTHING; + ALTER TABLE widget_type DROP COLUMN IF EXISTS bundle_alias; + END IF; + END; +$$; diff --git a/application/src/main/data/upgrade/3.6.0/schema_update.sql b/application/src/main/data/upgrade/3.6.0/schema_update.sql new file mode 100644 index 0000000000..b0a64268ef --- /dev/null +++ b/application/src/main/data/upgrade/3.6.0/schema_update.sql @@ -0,0 +1,36 @@ +-- +-- Copyright © 2016-2023 The Thingsboard Authors +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +ALTER TABLE widget_type + ADD COLUMN IF NOT EXISTS tags text[]; + +ALTER TABLE widgets_bundle + ADD COLUMN IF NOT EXISTS widgets_bundle_order int; + +ALTER TABLE api_usage_state ADD COLUMN IF NOT EXISTS tbel_exec varchar(32); +UPDATE api_usage_state SET tbel_exec = js_exec WHERE tbel_exec IS NULL; + +ALTER TABLE notification DROP CONSTRAINT IF EXISTS fk_notification_request_id; +ALTER TABLE notification DROP CONSTRAINT IF EXISTS fk_notification_recipient_id; +CREATE INDEX IF NOT EXISTS idx_notification_notification_request_id ON notification(request_id); +CREATE INDEX IF NOT EXISTS idx_notification_request_tenant_id ON notification_request(tenant_id); + +-- DELETE invalid records from M:N widgets_bundle_widget table caused by the bug in previous upgrade script; +DELETE +FROM widgets_bundle_widget wbw +WHERE (SELECT tenant_id FROM widgets_bundle wb WHERE wb.id = wbw.widgets_bundle_id) != + (SELECT tenant_id FROM widget_type wt WHERE wt.id = wbw.widget_type_id); + diff --git a/application/src/main/data/upgrade/3.6.1/save_attributes_node_update.sql b/application/src/main/data/upgrade/3.6.1/save_attributes_node_update.sql new file mode 100644 index 0000000000..6a27c8ebc2 --- /dev/null +++ b/application/src/main/data/upgrade/3.6.1/save_attributes_node_update.sql @@ -0,0 +1,26 @@ +-- +-- Copyright © 2016-2023 The Thingsboard Authors +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +UPDATE rule_node SET + configuration = (configuration::jsonb || jsonb_build_object( + 'notifyDevice', + CASE WHEN configuration::jsonb ->> 'notifyDevice' = 'false' THEN false ELSE true END, + 'sendAttributesUpdatedNotification', + CASE WHEN configuration::jsonb ->> 'sendAttributesUpdatedNotification' = 'true' THEN true ELSE false END, + 'updateAttributesOnlyOnValueChange', + CASE WHEN configuration::jsonb ->> 'updateAttributesOnlyOnValueChange' = 'false' THEN false ELSE true END)::jsonb)::varchar, + configuration_version = 2 +WHERE type = 'org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode' AND configuration_version = 1; diff --git a/application/src/main/data/upgrade/3.6.1/schema_update.sql b/application/src/main/data/upgrade/3.6.1/schema_update.sql new file mode 100644 index 0000000000..98e78df5a9 --- /dev/null +++ b/application/src/main/data/upgrade/3.6.1/schema_update.sql @@ -0,0 +1,59 @@ +-- +-- Copyright © 2016-2023 The Thingsboard Authors +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +-- RESOURCES UPDATE START + +ALTER TABLE resource ADD COLUMN IF NOT EXISTS descriptor varchar; +ALTER TABLE resource ADD COLUMN IF NOT EXISTS preview bytea; +ALTER TABLE resource ADD COLUMN IF NOT EXISTS external_id uuid; +ALTER TABLE resource ADD COLUMN IF NOT EXISTS is_public boolean default true; +ALTER TABLE resource ADD COLUMN IF NOT EXISTS public_resource_key varchar(32) unique; + +CREATE INDEX IF NOT EXISTS idx_resource_etag ON resource(tenant_id, etag); +CREATE INDEX IF NOT EXISTS idx_resource_type_public_resource_key ON resource(resource_type, public_resource_key); + +CREATE OR REPLACE FUNCTION generate_resource_public_key() +RETURNS text AS $$ +DECLARE + chars text := 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + result text := ''; +BEGIN + FOR i IN 1..32 LOOP + result := result || substr(chars, floor(random()*62)::int + 1, 1); + END LOOP; + RETURN result; +END; +$$ LANGUAGE plpgsql; + +DO +$$ + BEGIN + IF NOT EXISTS(SELECT 1 FROM information_schema.columns WHERE table_name = 'resource' AND column_name = 'data' AND data_type = 'bytea') THEN + ALTER TABLE resource RENAME COLUMN data TO base64_data; + ALTER TABLE resource ADD COLUMN data bytea; + UPDATE resource SET data = decode(base64_data, 'base64') WHERE base64_data IS NOT NULL; + ALTER TABLE resource DROP COLUMN base64_data; + ELSE + UPDATE resource SET public_resource_key = generate_resource_public_key() WHERE resource_type = 'IMAGE' AND public_resource_key IS NULL; + END IF; + END; +$$; + +DROP FUNCTION generate_resource_public_key; + +-- RESOURCES UPDATE END + +CREATE INDEX IF NOT EXISTS idx_edge_event_tenant_id_edge_id_created_time ON edge_event(tenant_id, edge_id, created_time DESC); \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index ce2c359ec1..b973fb833c 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -29,6 +29,7 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.service.executors.PubSubRuleNodeExecutorProvider; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.NotificationCenter; import org.thingsboard.rule.engine.api.SmsService; @@ -48,6 +49,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.tools.TbRateLimits; @@ -66,6 +68,7 @@ import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeService; +import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.dao.nosql.CassandraBufferedRateReadExecutor; @@ -90,7 +93,6 @@ import org.thingsboard.server.dao.widget.WidgetsBundleService; import org.thingsboard.server.queue.discovery.DiscoveryService; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; -import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.queue.util.DataDecodingEncodingService; import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.component.ComponentDiscoveryService; @@ -246,6 +248,7 @@ public class ActorSystemContext { private RuleNodeStateService ruleNodeStateService; @Autowired + @Getter private PartitionService partitionService; @Autowired @@ -321,6 +324,11 @@ public class ActorSystemContext { @Getter private NotificationExecutorService notificationExecutor; + @Lazy + @Autowired + @Getter + private PubSubRuleNodeExecutorProvider pubSubRuleNodeExecutorProvider; + @Autowired @Getter private SharedEventLoopGroupService sharedEventLoopGroupService; @@ -448,6 +456,11 @@ public class ActorSystemContext { @Getter private WidgetTypeService widgetTypeService; + @Lazy + @Autowired(required = false) + @Getter + private EntityService entityService; + @Value("${actors.session.max_concurrent_sessions_per_device:1}") @Getter private long maxConcurrentSessionsPerDevice; @@ -527,14 +540,22 @@ public class ActorSystemContext { @Getter private String debugPerTenantLimitsConfiguration; - @Value("${actors.rpc.sequential:false}") + @Value("${actors.rpc.submit_strategy:BURST}") @Getter - private boolean rpcSequential; + private String rpcSubmitStrategy; + + @Value("${actors.rpc.response_timeout_ms:30000}") + @Getter + private long rpcResponseTimeout; @Value("${actors.rpc.max_retries:5}") @Getter private int maxRpcRetries; + @Value("${actors.rule.external.force_ack:false}") + @Getter + private boolean externalNodeForceAck; + @Getter @Setter private TbActorSystem actorSystem; diff --git a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java index fb6fbbdff2..98e0bf7c25 100644 --- a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java @@ -46,6 +46,7 @@ import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; import java.util.HashSet; +import java.util.Optional; import java.util.Set; @Slf4j @@ -73,10 +74,14 @@ public class AppActor extends ContextAwareActor { @Override protected boolean doProcess(TbActorMsg msg) { if (!ruleChainsInitialized) { - initTenantActors(); - ruleChainsInitialized = true; - if (msg.getMsgType() != MsgType.APP_INIT_MSG && msg.getMsgType() != MsgType.PARTITION_CHANGE_MSG) { - log.warn("Rule Chains initialized by unexpected message: {}", msg); + if (MsgType.APP_INIT_MSG.equals(msg.getMsgType())) { + initTenantActors(); + ruleChainsInitialized = true; + } else { + if (!msg.getMsgType().isIgnoreOnStart()) { + log.warn("Attempt to initialize Rule Chains by unexpected message: {}", msg); + } + return true; } } switch (msg.getMsgType()) { @@ -125,8 +130,11 @@ public class AppActor extends ContextAwareActor { PageDataIterable tenantIterator = new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT); for (Tenant tenant : tenantIterator) { log.debug("[{}] Creating tenant actor", tenant.getId()); - getOrCreateTenantActor(tenant.getId()); - log.debug("[{}] Tenant actor created.", tenant.getId()); + getOrCreateTenantActor(tenant.getId()).ifPresentOrElse(tenantActor -> { + log.debug("[{}] Tenant actor created.", tenant.getId()); + }, () -> { + log.debug("[{}] Skipped actor creation", tenant.getId()); + }); } } log.info("Main system actor started."); @@ -139,11 +147,9 @@ public class AppActor extends ContextAwareActor { if (TenantId.SYS_TENANT_ID.equals(msg.getTenantId())) { msg.getMsg().getCallback().onFailure(new RuleEngineException("Message has system tenant id!")); } else { - if (!deletedTenants.contains(msg.getTenantId())) { - getOrCreateTenantActor(msg.getTenantId()).tell(msg); - } else { - msg.getMsg().getCallback().onSuccess(); - } + getOrCreateTenantActor(msg.getTenantId()).ifPresentOrElse(actor -> { + actor.tell(msg); + }, () -> msg.getMsg().getCallback().onSuccess()); } } @@ -160,12 +166,13 @@ public class AppActor extends ContextAwareActor { log.info("[{}] Handling tenant deleted notification: {}", msg.getTenantId(), msg); deletedTenants.add(tenantId); ctx.stop(new TbEntityActorId(tenantId)); - } else { - target = getOrCreateTenantActor(msg.getTenantId()); + return; } - } else { - target = getOrCreateTenantActor(msg.getTenantId()); } + target = getOrCreateTenantActor(msg.getTenantId()).orElseGet(() -> { + log.debug("Ignoring component lifecycle msg for tenant {} because it is not managed by this service", msg.getTenantId()); + return null; + }); } if (target != null) { target.tellWithHighPriority(msg); @@ -175,24 +182,28 @@ public class AppActor extends ContextAwareActor { } private void onToDeviceActorMsg(TenantAwareMsg msg, boolean priority) { - if (!deletedTenants.contains(msg.getTenantId())) { - TbActorRef tenantActor = getOrCreateTenantActor(msg.getTenantId()); + getOrCreateTenantActor(msg.getTenantId()).ifPresentOrElse(tenantActor -> { if (priority) { tenantActor.tellWithHighPriority(msg); } else { tenantActor.tell(msg); } - } else { + }, () -> { if (msg instanceof TransportToDeviceActorMsgWrapper) { ((TransportToDeviceActorMsgWrapper) msg).getCallback().onSuccess(); } - } + }); } - private TbActorRef getOrCreateTenantActor(TenantId tenantId) { - return ctx.getOrCreateChildActor(new TbEntityActorId(tenantId), + private Optional getOrCreateTenantActor(TenantId tenantId) { + if (deletedTenants.contains(tenantId)) { + return Optional.empty(); + } + return Optional.ofNullable(ctx.getOrCreateChildActor(new TbEntityActorId(tenantId), () -> DefaultActorService.TENANT_DISPATCHER_NAME, - () -> new TenantActor.ActorCreator(systemContext, tenantId)); + () -> new TenantActor.ActorCreator(systemContext, tenantId), + () -> systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE) || + systemContext.getPartitionService().isManagedByCurrentService(tenantId))); } private void onToEdgeSessionMsg(EdgeSessionMsg msg) { @@ -200,7 +211,7 @@ public class AppActor extends ContextAwareActor { if (ModelConstants.SYSTEM_TENANT.equals(msg.getTenantId())) { log.warn("Message has system tenant id: {}", msg); } else { - target = getOrCreateTenantActor(msg.getTenantId()); + target = getOrCreateTenantActor(msg.getTenantId()).orElse(null); } if (target != null) { target.tellWithHighPriority(msg); diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java index 2779a83979..d69b9aec0b 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java @@ -16,9 +16,9 @@ package org.thingsboard.server.actors.device; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; -import org.thingsboard.rule.engine.api.msg.DeviceEdgeUpdateMsg; -import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceAttributesEventNotificationMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceEdgeUpdateMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceNameOrTypeUpdateMsg; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.TbActorCtx; import org.thingsboard.server.actors.TbActorException; @@ -27,9 +27,9 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; -import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg; -import org.thingsboard.server.service.rpc.RemoveRpcActorMsg; -import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponseActorMsg; +import org.thingsboard.server.common.msg.rpc.RemoveRpcActorMsg; +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequestActorMsg; import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; @Slf4j @@ -64,6 +64,9 @@ public class DeviceActor extends ContextAwareActor { case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG: processor.processAttributesUpdate((DeviceAttributesEventNotificationMsg) msg); break; + case DEVICE_DELETE_TO_DEVICE_ACTOR_MSG: + ctx.stop(ctx.getSelf()); + break; case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG: processor.processCredentialsUpdate(msg); break; diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java index 0ce0ae7884..50e237ac73 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java @@ -25,10 +25,10 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.LinkedHashMapRemoveEldest; -import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; -import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg; -import org.thingsboard.rule.engine.api.msg.DeviceEdgeUpdateMsg; -import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceAttributesEventNotificationMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceCredentialsUpdateNotificationMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceEdgeUpdateMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceNameOrTypeUpdateMsg; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.TbActorCtx; import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor; @@ -87,9 +87,10 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto; import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; -import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg; -import org.thingsboard.server.service.rpc.RemoveRpcActorMsg; -import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponseActorMsg; +import org.thingsboard.server.common.msg.rpc.RemoveRpcActorMsg; +import org.thingsboard.server.service.rpc.RpcSubmitStrategy; +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequestActorMsg; import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; import javax.annotation.Nullable; @@ -106,6 +107,9 @@ import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -124,22 +128,27 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso private final Map rpcSubscriptions; private final Map toDeviceRpcPendingMap; private final boolean rpcSequential; + private final RpcSubmitStrategy rpcSubmitStrategy; + private final ScheduledExecutorService scheduler; private int rpcSeq = 0; private String deviceName; private String deviceType; private TbMsgMetaData defaultMetaData; private EdgeId edgeId; + private ScheduledFuture awaitRpcResponseFuture; DeviceActorMessageProcessor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) { super(systemContext); this.tenantId = tenantId; this.deviceId = deviceId; - this.rpcSequential = systemContext.isRpcSequential(); + this.rpcSubmitStrategy = RpcSubmitStrategy.parse(systemContext.getRpcSubmitStrategy()); + this.rpcSequential = !rpcSubmitStrategy.equals(RpcSubmitStrategy.BURST); this.attributeSubscriptions = new HashMap<>(); this.rpcSubscriptions = new HashMap<>(); this.toDeviceRpcPendingMap = new LinkedHashMap<>(); this.sessions = new LinkedHashMapRemoveEldest<>(systemContext.getMaxConcurrentSessionsPerDevice(), this::notifyTransportAboutClosedSessionMaxSessionsLimit); + this.scheduler = systemContext.getScheduler(); if (initAttributes()) { restoreSessions(); } @@ -183,7 +192,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso ToDeviceRpcRequest request = msg.getMsg(); UUID rpcId = request.getId(); log.debug("[{}][{}] Received RPC request to process ...", deviceId, rpcId); - ToDeviceRpcRequestMsg rpcRequest = creteToDeviceRpcRequestMsg(request); + ToDeviceRpcRequestMsg rpcRequest = createToDeviceRpcRequestMsg(request); long timeout = request.getExpirationTime() - System.currentTimeMillis(); boolean persisted = request.isPersisted(); @@ -225,24 +234,28 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso if (persisted) { ObjectNode response = JacksonUtil.newObjectNode(); response.put("rpcId", rpcId.toString()); - systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), JacksonUtil.toString(response), null)); + systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(rpcId, JacksonUtil.toString(response), null)); } if (!persisted && request.isOneway() && sent) { log.debug("[{}] RPC command response sent [{}][{}]!", deviceId, rpcId, requestId); - systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), null, null)); + systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(rpcId, null, null)); } else { registerPendingRpcRequest(context, msg, sent, rpcRequest, timeout); } - if (sent) { - log.debug("[{}][{}][{}] RPC request is sent!", deviceId, rpcId, requestId); - } else { - log.debug("[{}][{}][{}] RPC request is NOT sent!", deviceId, rpcId, requestId); - } + String rpcSent = sent ? "sent!" : "NOT sent!"; + log.debug("[{}][{}][{}] RPC request is {}", deviceId, rpcId, requestId, rpcSent); } private boolean isSendNewRpcAvailable() { - return !rpcSequential || toDeviceRpcPendingMap.values().stream().filter(md -> !md.isDelivered()).findAny().isEmpty(); + switch (rpcSubmitStrategy) { + case SEQUENTIAL_ON_ACK_FROM_DEVICE: + return toDeviceRpcPendingMap.values().stream().filter(md -> !md.isDelivered()).findAny().isEmpty(); + case SEQUENTIAL_ON_RESPONSE_FROM_DEVICE: + return toDeviceRpcPendingMap.values().stream().filter(ToDeviceRpcRequestMetadata::isDelivered).findAny().isEmpty(); + default: + return true; + } } private void createRpc(ToDeviceRpcRequest request, RpcStatus status) { @@ -257,7 +270,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso systemContext.getTbRpcService().save(tenantId, rpc); } - private ToDeviceRpcRequestMsg creteToDeviceRpcRequestMsg(ToDeviceRpcRequest request) { + private ToDeviceRpcRequestMsg createToDeviceRpcRequestMsg(ToDeviceRpcRequest request) { ToDeviceRpcRequestBody body = request.getBody(); return ToDeviceRpcRequestMsg.newBuilder() .setRequestId(rpcSeq++) @@ -283,28 +296,31 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } void processRemoveRpc(RemoveRpcActorMsg msg) { - UUID requestId = msg.getRequestId(); - log.debug("[{}][{}] Received remove RPC request ...", deviceId, requestId); + UUID rpcId = msg.getRequestId(); + log.debug("[{}][{}] Received remove RPC request ...", deviceId, rpcId); Map.Entry entry = null; for (Map.Entry e : toDeviceRpcPendingMap.entrySet()) { - if (e.getValue().getMsg().getMsg().getId().equals(requestId)) { + if (e.getValue().getMsg().getMsg().getId().equals(rpcId)) { entry = e; break; } } if (entry != null) { - Integer key = entry.getKey(); + Integer requestId = entry.getKey(); if (entry.getValue().isDelivered()) { - toDeviceRpcPendingMap.remove(key); + toDeviceRpcPendingMap.remove(requestId); + if (rpcSubmitStrategy.equals(RpcSubmitStrategy.SEQUENTIAL_ON_RESPONSE_FROM_DEVICE)) { + clearAwaitRpcResponseScheduler(); + sendNextPendingRequest(rpcId, requestId, "Removed pending RPC!"); + } } else { Optional> firstRpc = getFirstRpc(); - if (firstRpc.isPresent() && key.equals(firstRpc.get().getKey())) { - toDeviceRpcPendingMap.remove(key); - log.debug("[{}][{}][{}] Removed pending RPC! Going to send next pending request ...", deviceId, requestId, key); - sendNextPendingRequest(); + if (firstRpc.isPresent() && requestId.equals(firstRpc.get().getKey())) { + toDeviceRpcPendingMap.remove(requestId); + sendNextPendingRequest(rpcId, requestId, "Removed pending RPC!"); } else { - toDeviceRpcPendingMap.remove(key); + toDeviceRpcPendingMap.remove(requestId); } } } @@ -321,9 +337,9 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso void processServerSideRpcTimeout(DeviceActorServerSideRpcTimeoutMsg msg) { Integer requestId = msg.getId(); - ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(requestId); + var requestMd = toDeviceRpcPendingMap.remove(requestId); if (requestMd != null) { - ToDeviceRpcRequest toDeviceRpcRequest = requestMd.getMsg().getMsg(); + var toDeviceRpcRequest = requestMd.getMsg().getMsg(); UUID rpcId = toDeviceRpcRequest.getId(); log.debug("[{}][{}][{}] RPC request timeout detected!", deviceId, rpcId, requestId); if (toDeviceRpcRequest.isPersisted()) { @@ -332,8 +348,12 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(rpcId, null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION)); if (!requestMd.isDelivered()) { - log.debug("[{}][{}][{}] Pending RPC timeout detected! Going to send next pending request ...", deviceId, rpcId, requestId); - sendNextPendingRequest(); + sendNextPendingRequest(rpcId, requestId, "Pending RPC timeout detected!"); + return; + } + if (rpcSubmitStrategy.equals(RpcSubmitStrategy.SEQUENTIAL_ON_RESPONSE_FROM_DEVICE)) { + clearAwaitRpcResponseScheduler(); + sendNextPendingRequest(rpcId, requestId, "Pending RPC timeout detected!"); } } } @@ -363,10 +383,25 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } private Optional> getFirstRpc() { + if (rpcSubmitStrategy.equals(RpcSubmitStrategy.SEQUENTIAL_ON_RESPONSE_FROM_DEVICE)) { + return toDeviceRpcPendingMap.entrySet().stream() + .findFirst().filter(entry -> { + var md = entry.getValue(); + if (md.isDelivered()) { + if (awaitRpcResponseFuture == null || awaitRpcResponseFuture.isCancelled()) { + var toDeviceRpcRequest = md.getMsg().getMsg(); + awaitRpcResponseFuture = scheduleAwaitRpcResponseFuture(toDeviceRpcRequest.getId(), entry.getKey()); + } + return false; + } + return true; + }); + } return toDeviceRpcPendingMap.entrySet().stream().filter(e -> !e.getValue().isDelivered()).findFirst(); } - private void sendNextPendingRequest() { + private void sendNextPendingRequest(UUID rpcId, int requestId, String logMessage) { + log.debug("[{}][{}][{}] {} Going to send next pending request ...", deviceId, rpcId, requestId, logMessage); if (rpcSequential) { rpcSubscriptions.forEach((id, s) -> sendPendingRequests(id, s.getNodeId())); } @@ -591,12 +626,13 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso boolean success = requestMd != null; if (success) { ToDeviceRpcRequest toDeviceRequestMsg = requestMd.getMsg().getMsg(); + UUID rpcId = toDeviceRequestMsg.getId(); boolean delivered = requestMd.isDelivered(); boolean hasError = StringUtils.isNotEmpty(responseMsg.getError()); try { String payload = hasError ? responseMsg.getError() : responseMsg.getPayload(); systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor( - new FromDeviceRpcResponse(toDeviceRequestMsg.getId(), payload, null)); + new FromDeviceRpcResponse(rpcId, payload, null)); if (toDeviceRequestMsg.isPersisted()) { RpcStatus status = hasError ? RpcStatus.FAILED : RpcStatus.SUCCESSFUL; JsonNode response; @@ -605,13 +641,17 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } catch (IllegalArgumentException e) { response = JacksonUtil.newObjectNode().put("error", payload); } - systemContext.getTbRpcService().save(tenantId, new RpcId(toDeviceRequestMsg.getId()), status, response); + systemContext.getTbRpcService().save(tenantId, new RpcId(rpcId), status, response); } } finally { - if (!delivered) { - String errorResponse = hasError ? "error" : ""; - log.debug("[{}][{}][{}] Received {} response for undelivered RPC! Going to send next pending request ...", deviceId, sessionId, requestId, errorResponse); - sendNextPendingRequest(); + if (rpcSubmitStrategy.equals(RpcSubmitStrategy.SEQUENTIAL_ON_RESPONSE_FROM_DEVICE)) { + clearAwaitRpcResponseScheduler(); + String errorResponse = hasError ? "error response" : "response"; + String rpcState = delivered ? "" : "undelivered "; + sendNextPendingRequest(rpcId, requestId, String.format("Received %s for %sRPC!", errorResponse, rpcState)); + } else if (!delivered) { + String errorResponse = hasError ? "error response" : "response"; + sendNextPendingRequest(rpcId, requestId, String.format("Received %s for undelivered RPC!", errorResponse)); } } } else { @@ -626,36 +666,47 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso int requestId = responseMsg.getRequestId(); log.debug("[{}][{}][{}][{}] Processing RPC command response status: [{}]", deviceId, sessionId, rpcId, requestId, status); ToDeviceRpcRequestMetadata md = toDeviceRpcPendingMap.get(requestId); - if (md != null) { + var toDeviceRpcRequest = md.getMsg().getMsg(); + boolean persisted = toDeviceRpcRequest.isPersisted(); + boolean oneWayRpc = toDeviceRpcRequest.isOneway(); JsonNode response = null; if (status.equals(RpcStatus.DELIVERED)) { - if (md.getMsg().getMsg().isOneway()) { + if (oneWayRpc) { toDeviceRpcPendingMap.remove(requestId); if (rpcSequential) { - systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(rpcId, null, null)); + var fromDeviceRpcResponse = new FromDeviceRpcResponse(rpcId, null, null); + systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(fromDeviceRpcResponse); } } else { md.setDelivered(true); + if (rpcSubmitStrategy.equals(RpcSubmitStrategy.SEQUENTIAL_ON_RESPONSE_FROM_DEVICE)) { + awaitRpcResponseFuture = scheduleAwaitRpcResponseFuture(rpcId, requestId); + } } } else if (status.equals(RpcStatus.TIMEOUT)) { - Integer maxRpcRetries = md.getMsg().getMsg().getRetries(); - maxRpcRetries = maxRpcRetries == null ? systemContext.getMaxRpcRetries() : Math.min(maxRpcRetries, systemContext.getMaxRpcRetries()); + Integer maxRpcRetries = toDeviceRpcRequest.getRetries(); + maxRpcRetries = maxRpcRetries == null ? + systemContext.getMaxRpcRetries() : Math.min(maxRpcRetries, systemContext.getMaxRpcRetries()); if (maxRpcRetries <= md.getRetries()) { toDeviceRpcPendingMap.remove(requestId); status = RpcStatus.FAILED; - response = JacksonUtil.newObjectNode().put("error", "There was a Timeout and all retry attempts have been exhausted. Retry attempts set: " + maxRpcRetries); + response = JacksonUtil.newObjectNode().put("error", "There was a Timeout and all retry " + + "attempts have been exhausted. Retry attempts set: " + maxRpcRetries); } else { md.setRetries(md.getRetries() + 1); } } - if (md.getMsg().getMsg().isPersisted()) { + if (persisted) { systemContext.getTbRpcService().save(tenantId, new RpcId(rpcId), status, response); } - if (status != RpcStatus.SENT) { - log.debug("[{}][{}][{}][{}] RPC was {}! Going to send next pending request ...", deviceId, sessionId, rpcId, requestId, status.name().toLowerCase()); - sendNextPendingRequest(); + if (rpcSubmitStrategy.equals(RpcSubmitStrategy.SEQUENTIAL_ON_RESPONSE_FROM_DEVICE) + && status.equals(RpcStatus.DELIVERED) && !oneWayRpc) { + return; + } + if (!status.equals(RpcStatus.SENT)) { + sendNextPendingRequest(rpcId, requestId, String.format("RPC was %s!", status.name().toLowerCase())); } } else { log.warn("[{}][{}][{}][{}] RPC has already been removed from pending map.", deviceId, sessionId, rpcId, requestId); @@ -688,6 +739,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso if (subscribeCmd.getUnsubscribe()) { log.debug("[{}] Canceling RPC subscription for session: [{}]", deviceId, sessionId); rpcSubscriptions.remove(sessionId); + clearAwaitRpcResponseScheduler(); } else { SessionInfoMetaData sessionMD = sessions.get(sessionId); if (sessionMD == null) { @@ -722,6 +774,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso sessions.remove(sessionId); attributeSubscriptions.remove(sessionId); rpcSubscriptions.remove(sessionId); + clearAwaitRpcResponseScheduler(); if (sessions.isEmpty()) { reportSessionClose(); } @@ -729,6 +782,27 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso } } + private ScheduledFuture scheduleAwaitRpcResponseFuture(UUID rpcId, int requestId) { + return scheduler.schedule(() -> { + var md = toDeviceRpcPendingMap.remove(requestId); + if (md == null) { + return; + } + sendNextPendingRequest(rpcId, requestId, "RPC was removed from pending map due to await timeout on response from device!"); + var toDeviceRpcRequest = md.getMsg().getMsg(); + if (toDeviceRpcRequest.isPersisted()) { + var responseAwaitTimeout = JacksonUtil.newObjectNode().put("error", "There was a timeout awaiting for RPC response from device."); + systemContext.getTbRpcService().save(tenantId, new RpcId(rpcId), RpcStatus.FAILED, responseAwaitTimeout); + } + }, systemContext.getRpcResponseTimeout(), TimeUnit.MILLISECONDS); + } + + private void clearAwaitRpcResponseScheduler() { + if (rpcSubmitStrategy.equals(RpcSubmitStrategy.SEQUENTIAL_ON_RESPONSE_FROM_DEVICE) && awaitRpcResponseFuture != null) { + awaitRpcResponseFuture.cancel(true); + } + } + private void handleSessionActivity(SessionInfoProto sessionInfoProto, SubscriptionInfoProto subscriptionInfo) { UUID sessionId = getSessionId(sessionInfoProto); Objects.requireNonNull(sessionId); @@ -974,7 +1048,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso rpc.setStatus(RpcStatus.EXPIRED); systemContext.getTbRpcService().save(tenantId, rpc); } else { - registerPendingRpcRequest(ctx, new ToDeviceRpcRequestActorMsg(systemContext.getServiceId(), msg), false, creteToDeviceRpcRequestMsg(msg), timeout); + registerPendingRpcRequest(ctx, new ToDeviceRpcRequestActorMsg(systemContext.getServiceId(), msg), false, createToDeviceRpcRequestMsg(msg), timeout); } }); if (pageData.hasNext()) { diff --git a/application/src/main/java/org/thingsboard/server/actors/device/ToDeviceRpcRequestMetadata.java b/application/src/main/java/org/thingsboard/server/actors/device/ToDeviceRpcRequestMetadata.java index f876408d24..0a39b892ea 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/ToDeviceRpcRequestMetadata.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/ToDeviceRpcRequestMetadata.java @@ -16,7 +16,7 @@ package org.thingsboard.server.actors.device; import lombok.Data; -import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequestActorMsg; /** * @author Andrew Shvayka diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index bcf67e0f0d..d91b4d3c0a 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -33,7 +33,7 @@ import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.ScriptEngine; import org.thingsboard.rule.engine.api.SmsService; import org.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.rule.engine.api.TbRelationTypes; +import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.notification.SlackService; import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; import org.thingsboard.rule.engine.util.TenantIdLoader; @@ -41,7 +41,6 @@ import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.TbActorRef; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.Customer; -import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; @@ -60,6 +59,8 @@ import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rule.RuleNode; @@ -83,6 +84,7 @@ import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeService; +import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.nosql.CassandraStatementTask; import org.thingsboard.server.dao.nosql.TbResultSetFuture; @@ -103,6 +105,7 @@ import org.thingsboard.server.dao.widget.WidgetsBundleService; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueMsgMetadata; +import org.thingsboard.server.service.executors.PubSubRuleNodeExecutorProvider; import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; import org.thingsboard.server.service.script.RuleNodeTbelScriptEngine; @@ -113,6 +116,10 @@ import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import java.util.function.Consumer; +import static org.thingsboard.server.common.data.msg.TbMsgType.ATTRIBUTES_DELETED; +import static org.thingsboard.server.common.data.msg.TbMsgType.ATTRIBUTES_UPDATED; +import static org.thingsboard.server.common.data.msg.TbMsgType.ENTITY_CREATED; + /** * Created by ashvayka on 19.03.18. */ @@ -131,7 +138,7 @@ class DefaultTbContext implements TbContext { @Override public void tellSuccess(TbMsg msg) { - tellNext(msg, Collections.singleton(TbRelationTypes.SUCCESS), null); + tellNext(msg, Collections.singleton(TbNodeConnectionType.SUCCESS), null); } @Override @@ -210,7 +217,13 @@ class DefaultTbContext implements TbContext { @Override public void enqueueForTellFailure(TbMsg tbMsg, String failureMessage) { TopicPartitionInfo tpi = resolvePartition(tbMsg); - enqueueForTellNext(tpi, tbMsg, Collections.singleton(TbRelationTypes.FAILURE), failureMessage, null, null); + enqueueForTellNext(tpi, tbMsg, Collections.singleton(TbNodeConnectionType.FAILURE), failureMessage, null, null); + } + + @Override + public void enqueueForTellFailure(TbMsg tbMsg, Throwable th) { + TopicPartitionInfo tpi = resolvePartition(tbMsg); + enqueueForTellNext(tpi, tbMsg, Collections.singleton(TbNodeConnectionType.FAILURE), getFailureMessage(th), null, null); } @Override @@ -308,20 +321,11 @@ class DefaultTbContext implements TbContext { @Override public void tellFailure(TbMsg msg, Throwable th) { if (nodeCtx.getSelf().isDebugMode()) { - mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, TbRelationTypes.FAILURE, th); - } - String failureMessage; - if (th != null) { - if (!StringUtils.isEmpty(th.getMessage())) { - failureMessage = th.getMessage(); - } else { - failureMessage = th.getClass().getSimpleName(); - } - } else { - failureMessage = null; + mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, TbNodeConnectionType.FAILURE, th); } + String failureMessage = getFailureMessage(th); nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getRuleChainId(), - nodeCtx.getSelf().getId(), Collections.singleton(TbRelationTypes.FAILURE), + nodeCtx.getSelf().getId(), Collections.singleton(TbNodeConnectionType.FAILURE), msg, failureMessage)); } @@ -344,67 +348,104 @@ class DefaultTbContext implements TbContext { return TbMsg.transformMsg(origMsg, type, originator, metaData, data); } + @Override + public TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data) { + return newMsg(queueName, type, originator, null, metaData, data); + } + + @Override + public TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { + return TbMsg.newMsg(queueName, type, originator, customerId, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId()); + } + + @Override + public TbMsg transformMsg(TbMsg origMsg, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data) { + return TbMsg.transformMsg(origMsg, type, originator, metaData, data); + } + + @Override + public TbMsg transformMsg(TbMsg origMsg, TbMsgMetaData metaData, String data) { + return TbMsg.transformMsg(origMsg, metaData, data); + } + + @Override + public TbMsg transformMsgOriginator(TbMsg origMsg, EntityId originator) { + return TbMsg.transformMsgOriginator(origMsg, originator); + } + + @Override public TbMsg customerCreatedMsg(Customer customer, RuleNodeId ruleNodeId) { - return entityActionMsg(customer, customer.getId(), ruleNodeId, DataConstants.ENTITY_CREATED); + return entityActionMsg(customer, customer.getId(), ruleNodeId, ENTITY_CREATED); } + @Override public TbMsg deviceCreatedMsg(Device device, RuleNodeId ruleNodeId) { DeviceProfile deviceProfile = null; if (device.getDeviceProfileId() != null) { deviceProfile = mainCtx.getDeviceProfileCache().find(device.getDeviceProfileId()); } - return entityActionMsg(device, device.getId(), ruleNodeId, DataConstants.ENTITY_CREATED, deviceProfile); + return entityActionMsg(device, device.getId(), ruleNodeId, ENTITY_CREATED, deviceProfile); } + @Override public TbMsg assetCreatedMsg(Asset asset, RuleNodeId ruleNodeId) { AssetProfile assetProfile = null; if (asset.getAssetProfileId() != null) { assetProfile = mainCtx.getAssetProfileCache().find(asset.getAssetProfileId()); } - return entityActionMsg(asset, asset.getId(), ruleNodeId, DataConstants.ENTITY_CREATED, assetProfile); + return entityActionMsg(asset, asset.getId(), ruleNodeId, ENTITY_CREATED, assetProfile); } + @Override public TbMsg alarmActionMsg(Alarm alarm, RuleNodeId ruleNodeId, String action) { + EntityId originator = alarm.getOriginator(); + HasRuleEngineProfile profile = getRuleEngineProfile(originator); + return entityActionMsg(alarm, originator, ruleNodeId, action, profile); + } + + @Override + public TbMsg alarmActionMsg(Alarm alarm, RuleNodeId ruleNodeId, TbMsgType actionMsgType) { + EntityId originator = alarm.getOriginator(); + HasRuleEngineProfile profile = getRuleEngineProfile(originator); + return entityActionMsg(alarm, originator, ruleNodeId, actionMsgType, profile); + } + + private HasRuleEngineProfile getRuleEngineProfile(EntityId originator) { HasRuleEngineProfile profile = null; - if (EntityType.DEVICE.equals(alarm.getOriginator().getEntityType())) { - DeviceId deviceId = new DeviceId(alarm.getOriginator().getId()); + if (EntityType.DEVICE.equals(originator.getEntityType())) { + DeviceId deviceId = new DeviceId(originator.getId()); profile = mainCtx.getDeviceProfileCache().get(getTenantId(), deviceId); - } else if (EntityType.ASSET.equals(alarm.getOriginator().getEntityType())) { - AssetId assetId = new AssetId(alarm.getOriginator().getId()); + } else if (EntityType.ASSET.equals(originator.getEntityType())) { + AssetId assetId = new AssetId(originator.getId()); profile = mainCtx.getAssetProfileCache().get(getTenantId(), assetId); } - return entityActionMsg(alarm, alarm.getOriginator(), ruleNodeId, action, profile); + return profile; } + @Override public TbMsg attributesUpdatedActionMsg(EntityId originator, RuleNodeId ruleNodeId, String scope, List attributes) { ObjectNode entityNode = JacksonUtil.newObjectNode(); if (attributes != null) { attributes.forEach(attributeKvEntry -> JacksonUtil.addKvEntry(entityNode, attributeKvEntry)); } - return attributesActionMsg(originator, ruleNodeId, scope, DataConstants.ATTRIBUTES_UPDATED, JacksonUtil.toString(entityNode)); + return attributesActionMsg(originator, ruleNodeId, scope, ATTRIBUTES_UPDATED, JacksonUtil.toString(entityNode)); } + @Override public TbMsg attributesDeletedActionMsg(EntityId originator, RuleNodeId ruleNodeId, String scope, List keys) { ObjectNode entityNode = JacksonUtil.newObjectNode(); ArrayNode attrsArrayNode = entityNode.putArray("attributes"); if (keys != null) { keys.forEach(attrsArrayNode::add); } - return attributesActionMsg(originator, ruleNodeId, scope, DataConstants.ATTRIBUTES_DELETED, JacksonUtil.toString(entityNode)); + return attributesActionMsg(originator, ruleNodeId, scope, ATTRIBUTES_DELETED, JacksonUtil.toString(entityNode)); } - private TbMsg attributesActionMsg(EntityId originator, RuleNodeId ruleNodeId, String scope, String action, String msgData) { + private TbMsg attributesActionMsg(EntityId originator, RuleNodeId ruleNodeId, String scope, TbMsgType actionMsgType, String msgData) { TbMsgMetaData tbMsgMetaData = getActionMetaData(ruleNodeId); tbMsgMetaData.putValue("scope", scope); - HasRuleEngineProfile profile = null; - if (EntityType.DEVICE.equals(originator.getEntityType())) { - DeviceId deviceId = new DeviceId(originator.getId()); - profile = mainCtx.getDeviceProfileCache().get(getTenantId(), deviceId); - } else if (EntityType.ASSET.equals(originator.getEntityType())) { - AssetId assetId = new AssetId(originator.getId()); - profile = mainCtx.getAssetProfileCache().get(getTenantId(), assetId); - } - return entityActionMsg(originator, tbMsgMetaData, msgData, action, profile); + HasRuleEngineProfile profile = getRuleEngineProfile(originator); + return entityActionMsg(originator, tbMsgMetaData, msgData, actionMsgType, profile); } @Override @@ -412,10 +453,11 @@ class DefaultTbContext implements TbContext { mainCtx.getClusterService().onEdgeEventUpdate(tenantId, edgeId); } - public TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action) { - return entityActionMsg(entity, id, ruleNodeId, action, null); + public TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, TbMsgType actionMsgType) { + return entityActionMsg(entity, id, ruleNodeId, actionMsgType, null); } + @Deprecated(since = "3.6.0", forRemoval = true) public TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action, K profile) { try { return entityActionMsg(id, getActionMetaData(ruleNodeId), JacksonUtil.toString(JacksonUtil.valueToTree(entity)), action, profile); @@ -424,6 +466,7 @@ class DefaultTbContext implements TbContext { } } + @Deprecated(since = "3.6.0", forRemoval = true) private TbMsg entityActionMsg(I id, TbMsgMetaData msgMetaData, String msgData, String action, K profile) { String defaultQueueName = null; RuleChainId defaultRuleChainId = null; @@ -434,6 +477,24 @@ class DefaultTbContext implements TbContext { return TbMsg.newMsg(defaultQueueName, action, id, msgMetaData, msgData, defaultRuleChainId, null); } + public TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, TbMsgType actionMsgType, K profile) { + try { + return entityActionMsg(id, getActionMetaData(ruleNodeId), JacksonUtil.toString(JacksonUtil.valueToTree(entity)), actionMsgType, profile); + } catch (IllegalArgumentException e) { + throw new RuntimeException("Failed to process " + id.getEntityType().name().toLowerCase() + " " + actionMsgType.name() + " msg: " + e); + } + } + + private TbMsg entityActionMsg(I id, TbMsgMetaData msgMetaData, String msgData, TbMsgType actionMsgType, K profile) { + String defaultQueueName = null; + RuleChainId defaultRuleChainId = null; + if (profile != null) { + defaultQueueName = profile.getDefaultQueueName(); + defaultRuleChainId = profile.getDefaultRuleChainId(); + } + return TbMsg.newMsg(defaultQueueName, actionMsgType, id, msgMetaData, msgData, defaultRuleChainId, null); + } + @Override public RuleNodeId getSelfId() { return nodeCtx.getSelf().getId(); @@ -479,6 +540,11 @@ class DefaultTbContext implements TbContext { return mainCtx.getNotificationExecutor(); } + @Override + public PubSubRuleNodeExecutorProvider getPubSubRuleNodeExecutorProvider() { + return mainCtx.getPubSubRuleNodeExecutorProvider(); + } + @Override @Deprecated public ScriptEngine createJsScriptEngine(String script, String... argNames) { @@ -723,6 +789,11 @@ class DefaultTbContext implements TbContext { return mainCtx.getSlackService(); } + @Override + public boolean isExternalNodeForceAck() { + return mainCtx.isExternalNodeForceAck(); + } + @Override public RuleEngineRpcService getRpcService() { return mainCtx.getTbRuleEngineDeviceRpcService(); @@ -826,6 +897,11 @@ class DefaultTbContext implements TbContext { return mainCtx.getApiUsageStateService(); } + @Override + public EntityService getEntityService() { + return mainCtx.getEntityService(); + } + private TbMsgMetaData getActionMetaData(RuleNodeId ruleNodeId) { TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("ruleNodeId", ruleNodeId.toString()); @@ -839,10 +915,24 @@ class DefaultTbContext implements TbContext { } @Override - public void checkTenantEntity(EntityId entityId) { + public void checkTenantEntity(EntityId entityId) throws TbNodeException { if (!this.getTenantId().equals(TenantIdLoader.findTenantId(this, entityId))) { - throw new RuntimeException("Entity with id: '" + entityId + "' specified in the configuration doesn't belong to the current tenant."); + throw new TbNodeException("Entity with id: '" + entityId + "' specified in the configuration doesn't belong to the current tenant.", true); + } + } + + private static String getFailureMessage(Throwable th) { + String failureMessage; + if (th != null) { + if (!StringUtils.isEmpty(th.getMessage())) { + failureMessage = th.getMessage(); + } else { + failureMessage = th.getClass().getSimpleName(); + } + } else { + failureMessage = null; } + return failureMessage; } private class SimpleTbQueueCallback implements TbQueueCallback { diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java index 4bd082ee38..cbfd78180b 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java @@ -16,7 +16,7 @@ package org.thingsboard.server.actors.ruleChain; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.rule.engine.api.TbRelationTypes; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.TbActorCtx; import org.thingsboard.server.actors.TbActorRef; @@ -167,7 +167,8 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor DefaultActorService.RULE_DISPATCHER_NAME, - () -> new RuleNodeActor.ActorCreator(systemContext, tenantId, entityId, ruleChainName, ruleNode.getId())); + () -> new RuleNodeActor.ActorCreator(systemContext, tenantId, entityId, ruleChainName, ruleNode.getId()), + () -> true); } private void initRoutes(RuleChain ruleChain, List ruleNodeList) { @@ -232,7 +233,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor DefaultActorService.RULE_DISPATCHER_NAME, () -> { RuleChain ruleChain = provider.apply(ruleChainId); - return new RuleChainActor.ActorCreator(systemContext, tenantId, ruleChain); - }); + if (ruleChain == null) { + return new RuleChainErrorActor.ActorCreator(systemContext, tenantId, + new RuleEngineException("Rule Chain with id: " + ruleChainId + " not found!")); + } else { + return new RuleChainActor.ActorCreator(systemContext, tenantId, ruleChain); + } + }, + () -> true); } protected TbActorRef getEntityActorRef(EntityId entityId) { diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleEngineComponentActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleEngineComponentActor.java index e1918466a8..dc7529200e 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleEngineComponentActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleEngineComponentActor.java @@ -22,9 +22,9 @@ import org.thingsboard.server.actors.shared.ComponentMsgProcessor; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.rule.trigger.RuleEngineComponentLifecycleEventTrigger; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.msg.TbActorStopReason; -import org.thingsboard.server.common.msg.notification.trigger.RuleEngineComponentLifecycleEventTrigger; public abstract class RuleEngineComponentActor> extends ComponentActor { diff --git a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java index dd5bd4f1ed..87831cb8f6 100644 --- a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java +++ b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java @@ -74,7 +74,7 @@ public class DefaultActorService extends TbApplicationEventListener deletedDevices; + private TenantActor(ActorSystemContext systemContext, TenantId tenantId) { super(systemContext, tenantId); + this.deletedDevices = new HashSet<>(); } boolean cantFindTenant = false; @@ -82,21 +87,21 @@ public class TenantActor extends RuleChainManagerActor { cantFindTenant = true; log.info("[{}] Started tenant actor for missing tenant.", tenantId); } else { - TenantProfile tenantProfile = systemContext.getTenantProfileCache().get(tenant.getTenantProfileId()); - isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE); isRuleEngine = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE); if (isRuleEngine) { - try { - if (getApiUsageState().isReExecEnabled()) { - log.debug("[{}] Going to init rule chains", tenantId); - initRuleChains(); - } else { - log.info("[{}] Skip init of the rule chains due to API limits", tenantId); + if (systemContext.getPartitionService().isManagedByCurrentService(tenantId)) { + try { + if (getApiUsageState().isReExecEnabled()) { + log.debug("[{}] Going to init rule chains", tenantId); + initRuleChains(); + } else { + log.info("[{}] Skip init of the rule chains due to API limits", tenantId); + } + } catch (Exception e) { + log.info("Failed to check ApiUsage \"ReExecEnabled\"!!!", e); + cantFindTenant = true; } - } catch (Exception e) { - log.info("Failed to check ApiUsage \"ReExecEnabled\"!!!", e); - cantFindTenant = true; } } log.debug("[{}] Tenant actor started.", tenantId); @@ -222,6 +227,10 @@ public class TenantActor extends RuleChainManagerActor { if (!isCore) { log.warn("RECEIVED INVALID MESSAGE: {}", msg); } + if (deletedDevices.contains(msg.getDeviceId())) { + log.debug("RECEIVED MESSAGE FOR DELETED DEVICE: {}", msg); + return; + } TbActorRef deviceActor = getOrCreateDeviceActor(msg.getDeviceId()); if (priority) { deviceActor.tellWithHighPriority(msg); @@ -241,7 +250,8 @@ public class TenantActor extends RuleChainManagerActor { log.info("[{}] Received API state update. Going to ENABLE Rule Engine execution.", tenantId); initRuleChains(); } - } else if (msg.getEntityId().getEntityType() == EntityType.EDGE) { + } + if (msg.getEntityId().getEntityType() == EntityType.EDGE) { EdgeId edgeId = new EdgeId(msg.getEntityId().getId()); EdgeRpcService edgeRpcService = systemContext.getEdgeRpcService(); if (msg.getEvent() == ComponentLifecycleEvent.DELETED) { @@ -250,7 +260,13 @@ public class TenantActor extends RuleChainManagerActor { Edge edge = systemContext.getEdgeService().findEdgeById(tenantId, edgeId); edgeRpcService.updateEdge(tenantId, edge); } - } else if (isRuleEngine) { + } + if (msg.getEntityId().getEntityType() == EntityType.DEVICE && ComponentLifecycleEvent.DELETED == msg.getEvent()) { + DeviceId deviceId = (DeviceId) msg.getEntityId(); + onToDeviceActorMsg(new DeviceDeleteMsg(tenantId, deviceId), true); + deletedDevices.add(deviceId); + } + if (isRuleEngine) { TbActorRef target = getEntityActorRef(msg.getEntityId()); if (target != null) { if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) { @@ -270,7 +286,8 @@ public class TenantActor extends RuleChainManagerActor { private TbActorRef getOrCreateDeviceActor(DeviceId deviceId) { return ctx.getOrCreateChildActor(new TbEntityActorId(deviceId), () -> DefaultActorService.DEVICE_DISPATCHER_NAME, - () -> new DeviceActorCreator(systemContext, tenantId, deviceId)); + () -> new DeviceActorCreator(systemContext, tenantId, deviceId), + () -> true); } private void onToEdgeSessionMsg(EdgeSessionMsg msg) { diff --git a/application/src/main/java/org/thingsboard/server/config/CryptoConfig.java b/application/src/main/java/org/thingsboard/server/config/CryptoConfig.java new file mode 100644 index 0000000000..e960d62f8e --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/config/CryptoConfig.java @@ -0,0 +1,30 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +@Configuration +public class CryptoConfig { + + @Bean + protected BCryptPasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java b/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java index 4d227345b1..e0d5d3e04e 100644 --- a/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java +++ b/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java @@ -38,6 +38,7 @@ import org.springframework.web.util.UriComponentsBuilder; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.dao.oauth2.OAuth2Configuration; import org.thingsboard.server.dao.oauth2.OAuth2Service; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.auth.oauth2.TbOAuth2ParameterNames; import org.thingsboard.server.service.security.model.token.OAuth2AppTokenFactory; import org.thingsboard.server.utils.MiscUtils; @@ -51,6 +52,7 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; +@TbCoreComponent @Service @Slf4j public class CustomOAuth2AuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver { diff --git a/application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java b/application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java index 89f9b751fd..66c1c9081d 100644 --- a/application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java +++ b/application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java @@ -24,10 +24,10 @@ import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.exception.TenantProfileNotFoundException; +import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.msg.tools.TbRateLimitsException; -import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; -import org.thingsboard.server.dao.util.limits.LimitedApi; import org.thingsboard.server.dao.util.limits.RateLimitService; +import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; import org.thingsboard.server.service.security.model.SecurityUser; import javax.servlet.FilterChain; @@ -49,7 +49,7 @@ public class RateLimitProcessingFilter extends OncePerRequestFilter { SecurityUser user = getCurrentUser(); if (user != null && !user.isSystemAdmin()) { try { - if (!rateLimitService.checkRateLimit(LimitedApi.REST_REQUESTS, user.getTenantId())) { + if (!rateLimitService.checkRateLimit(LimitedApi.REST_REQUESTS_PER_TENANT, user.getTenantId())) { rateLimitExceeded(EntityType.TENANT, response); return; } @@ -60,7 +60,7 @@ public class RateLimitProcessingFilter extends OncePerRequestFilter { } if (user.isCustomerUser()) { - if (!rateLimitService.checkRateLimit(LimitedApi.REST_REQUESTS, user.getTenantId(), user.getCustomerId())) { + if (!rateLimitService.checkRateLimit(LimitedApi.REST_REQUESTS_PER_CUSTOMER, user.getTenantId(), user.getCustomerId())) { rateLimitExceeded(EntityType.CUSTOMER, response); return; } diff --git a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java index 4415f1a0bf..304cea5032 100644 --- a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java @@ -193,7 +193,7 @@ public class SwaggerConfiguration { .defaultModelExpandDepth(1) .defaultModelRendering(ModelRendering.EXAMPLE) .displayRequestDuration(false) - .docExpansion(DocExpansion.NONE) + .docExpansion(DocExpansion.LIST) .filter(false) .maxDisplayedTags(null) .operationsSorter(OperationsSorter.ALPHA) diff --git a/application/src/main/java/org/thingsboard/server/config/TbRuleEngineSecurityConfiguration.java b/application/src/main/java/org/thingsboard/server/config/TbRuleEngineSecurityConfiguration.java new file mode 100644 index 0000000000..7b3a752f57 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/config/TbRuleEngineSecurityConfiguration.java @@ -0,0 +1,44 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.boot.autoconfigure.security.SecurityProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +@Order(SecurityProperties.BASIC_AUTH_ORDER) +@ConditionalOnExpression("'${service.type:null}'=='tb-rule-engine'") +public class TbRuleEngineSecurityConfiguration { + + @Bean + SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.headers().cacheControl().and().frameOptions().disable() + .and().cors().and().csrf().disable() + .authorizeRequests() + .antMatchers("/actuator/prometheus").permitAll() + .anyRequest().authenticated(); + return http.build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java index 793670f0ab..61b99e78e3 100644 --- a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java @@ -19,9 +19,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.security.SecurityProperties; +import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; +import org.springframework.http.HttpHeaders; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.DefaultAuthenticationEventPublisher; import org.springframework.security.config.annotation.ObjectPostProcessor; @@ -29,17 +31,16 @@ import org.springframework.security.config.annotation.authentication.builders.Au import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.header.writers.StaticHeadersWriter; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; +import org.springframework.web.filter.ShallowEtagHeaderFilter; import org.thingsboard.server.dao.oauth2.OAuth2Configuration; import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -60,7 +61,7 @@ import java.util.List; @Configuration @EnableWebSecurity -@EnableGlobalMethodSecurity(prePostEnabled=true) +@EnableGlobalMethodSecurity(prePostEnabled = true) @Order(SecurityProperties.BASIC_AUTH_ORDER) @TbCoreComponent public class ThingsboardSecurityConfiguration { @@ -74,10 +75,11 @@ public class ThingsboardSecurityConfiguration { public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login"; public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public"; public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token"; - protected static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/assets/**", "/static/**", "/api/noauth/**", "/webjars/**", "/api/license/**"}; + protected static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/assets/**", "/static/**", "/api/noauth/**", "/webjars/**", "/api/license/**", "/api/images/public/**"}; public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**"; - public static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**"; + public static final String WS_ENTRY_POINT = "/api/ws/**"; public static final String MAIL_OAUTH2_PROCESSING_ENTRY_POINT = "/api/admin/mail/oauth2/code"; + public static final String DEVICE_CONNECTIVITY_CERTIFICATE_DOWNLOAD_ENTRY_POINT = "/api/device-connectivity/mqtts/certificate/download"; @Autowired private ThingsboardErrorResponseHandler restAccessDeniedHandler; @@ -111,14 +113,21 @@ public class ThingsboardSecurityConfiguration { @Qualifier("jwtHeaderTokenExtractor") private TokenExtractor jwtHeaderTokenExtractor; - @Autowired - @Qualifier("jwtQueryTokenExtractor") - private TokenExtractor jwtQueryTokenExtractor; - @Autowired private AuthenticationManager authenticationManager; @Autowired private RateLimitProcessingFilter rateLimitProcessingFilter; + @Bean + protected FilterRegistrationBean buildEtagFilter() throws Exception { + ShallowEtagHeaderFilter etagFilter = new ShallowEtagHeaderFilter(); + etagFilter.setWriteWeakETag(true); + FilterRegistrationBean filterRegistrationBean + = new FilterRegistrationBean<>( etagFilter); + filterRegistrationBean.addUrlPatterns("*.js","*.css","*.ico","/assets/*","/static/*"); + filterRegistrationBean.setName("etagFilter"); + return filterRegistrationBean; + } + @Bean protected RestLoginProcessingFilter buildRestLoginProcessingFilter() throws Exception { RestLoginProcessingFilter filter = new RestLoginProcessingFilter(FORM_BASED_LOGIN_ENTRY_POINT, successHandler, failureHandler); @@ -135,8 +144,9 @@ public class ThingsboardSecurityConfiguration { protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception { List pathsToSkip = new ArrayList<>(Arrays.asList(NON_TOKEN_BASED_AUTH_ENTRY_POINTS)); - pathsToSkip.addAll(Arrays.asList(WS_TOKEN_BASED_AUTH_ENTRY_POINT, TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_ENTRY_POINT, - PUBLIC_LOGIN_ENTRY_POINT, DEVICE_API_ENTRY_POINT, WEBJARS_ENTRY_POINT, MAIL_OAUTH2_PROCESSING_ENTRY_POINT)); + pathsToSkip.addAll(Arrays.asList(WS_ENTRY_POINT, TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_ENTRY_POINT, + PUBLIC_LOGIN_ENTRY_POINT, DEVICE_API_ENTRY_POINT, WEBJARS_ENTRY_POINT, MAIL_OAUTH2_PROCESSING_ENTRY_POINT, + DEVICE_CONNECTIVITY_CERTIFICATE_DOWNLOAD_ENTRY_POINT)); SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT); JwtTokenAuthenticationProcessingFilter filter = new JwtTokenAuthenticationProcessingFilter(failureHandler, jwtHeaderTokenExtractor, matcher); @@ -151,15 +161,6 @@ public class ThingsboardSecurityConfiguration { return filter; } - @Bean - protected JwtTokenAuthenticationProcessingFilter buildWsJwtTokenAuthenticationProcessingFilter() throws Exception { - AntPathRequestMatcher matcher = new AntPathRequestMatcher(WS_TOKEN_BASED_AUTH_ENTRY_POINT); - JwtTokenAuthenticationProcessingFilter filter - = new JwtTokenAuthenticationProcessingFilter(failureHandler, jwtQueryTokenExtractor, matcher); - filter.setAuthenticationManager(this.authenticationManager); - return filter; - } - @Bean public AuthenticationManager authenticationManager(ObjectPostProcessor objectPostProcessor) throws Exception { DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor @@ -172,17 +173,22 @@ public class ThingsboardSecurityConfiguration { return auth.build(); } - @Bean - protected BCryptPasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } - @Autowired private OAuth2AuthorizationRequestResolver oAuth2AuthorizationRequestResolver; @Bean - public WebSecurityCustomizer webSecurityCustomizer() { - return (web) -> web.ignoring().antMatchers("/*.js","/*.css","/*.ico","/assets/**","/static/**"); + @Order(0) + SecurityFilterChain resources(HttpSecurity http) throws Exception { + http + .requestMatchers((matchers) -> matchers.antMatchers("/*.js","/*.css","/*.ico","/assets/**","/static/**")) + .headers().defaultsDisabled() + .addHeaderWriter(new StaticHeadersWriter(HttpHeaders.CACHE_CONTROL, "max-age=0, public")) + .and() + .authorizeHttpRequests((authorize) -> authorize.anyRequest().permitAll()) + .requestCache().disable() + .securityContext().disable() + .sessionManagement().disable(); + return http.build(); } @Bean @@ -204,10 +210,11 @@ public class ThingsboardSecurityConfiguration { .antMatchers(PUBLIC_LOGIN_ENTRY_POINT).permitAll() // Public login end-point .antMatchers(TOKEN_REFRESH_ENTRY_POINT).permitAll() // Token refresh end-point .antMatchers(MAIL_OAUTH2_PROCESSING_ENTRY_POINT).permitAll() // Mail oauth2 code processing url + .antMatchers(DEVICE_CONNECTIVITY_CERTIFICATE_DOWNLOAD_ENTRY_POINT).permitAll() // Mail oauth2 code processing url .antMatchers(NON_TOKEN_BASED_AUTH_ENTRY_POINTS).permitAll() // static resources, user activation and password reset end-points .and() .authorizeRequests() - .antMatchers(WS_TOKEN_BASED_AUTH_ENTRY_POINT).authenticated() // Protected WebSocket API End-points + .antMatchers(WS_ENTRY_POINT).permitAll() // WebSocket API End-points .antMatchers(TOKEN_BASED_AUTH_ENTRY_POINT).authenticated() // Protected API End-points .and() .exceptionHandling().accessDeniedHandler(restAccessDeniedHandler) @@ -216,7 +223,6 @@ public class ThingsboardSecurityConfiguration { .addFilterBefore(buildRestPublicLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class) .addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class) .addFilterBefore(buildRefreshTokenProcessingFilter(), UsernamePasswordAuthenticationFilter.class) - .addFilterBefore(buildWsJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class) .addFilterAfter(rateLimitProcessingFilter, UsernamePasswordAuthenticationFilter.class); if (oauth2Configuration != null) { http.oauth2Login() diff --git a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java index 9a6ffb0e32..467e20adb8 100644 --- a/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/WebSocketConfiguration.java @@ -19,25 +19,13 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpStatus; -import org.springframework.http.server.ServerHttpRequest; -import org.springframework.http.server.ServerHttpResponse; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; -import org.springframework.web.socket.server.HandshakeInterceptor; import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean; -import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; -import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; -import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.controller.plugin.TbWebSocketHandler; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.security.model.SecurityUser; - -import java.util.Map; @Configuration @TbCoreComponent @@ -46,8 +34,9 @@ import java.util.Map; @Slf4j public class WebSocketConfiguration implements WebSocketConfigurer { - public static final String WS_PLUGIN_PREFIX = "/api/ws/plugins/"; - private static final String WS_PLUGIN_MAPPING = WS_PLUGIN_PREFIX + "**"; + public static final String WS_API_ENDPOINT = "/api/ws"; + public static final String WS_PLUGINS_ENDPOINT = "/api/ws/plugins/"; + private static final String WS_API_MAPPING = "/api/ws/**"; private final WebSocketHandler wsHandler; @@ -65,39 +54,7 @@ public class WebSocketConfiguration implements WebSocketConfigurer { log.error("TbWebSocketHandler expected but [{}] provided", wsHandler); throw new RuntimeException("TbWebSocketHandler expected but " + wsHandler + " provided"); } - registry.addHandler(wsHandler, WS_PLUGIN_MAPPING).setAllowedOriginPatterns("*") - .addInterceptors(new HttpSessionHandshakeInterceptor(), new HandshakeInterceptor() { - - @Override - public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, - Map attributes) throws Exception { - SecurityUser user = null; - try { - user = getCurrentUser(); - } catch (ThingsboardException ex) { - } - if (user == null) { - response.setStatusCode(HttpStatus.UNAUTHORIZED); - return false; - } else { - return true; - } - } - - @Override - public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, - Exception exception) { - //Do nothing - } - }); + registry.addHandler(wsHandler, WS_API_MAPPING).setAllowedOriginPatterns("*"); } - protected SecurityUser getCurrentUser() throws ThingsboardException { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication != null && authentication.getPrincipal() instanceof SecurityUser) { - return (SecurityUser) authentication.getPrincipal(); - } else { - throw new ThingsboardException("You aren't authorized to perform this operation!", ThingsboardErrorCode.AUTHENTICATION); - } - } } diff --git a/application/src/main/java/org/thingsboard/server/controller/AdminController.java b/application/src/main/java/org/thingsboard/server/controller/AdminController.java index 4019efe8ee..5e62bff43f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AdminController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AdminController.java @@ -170,7 +170,7 @@ public class AdminController extends BaseController { @ResponseBody public SecuritySettings getSecuritySettings() throws ThingsboardException { accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); - return checkNotNull(systemSecurityService.getSecuritySettings(TenantId.SYS_TENANT_ID)); + return checkNotNull(systemSecurityService.getSecuritySettings()); } @ApiOperation(value = "Update Security Settings (saveSecuritySettings)", @@ -182,7 +182,7 @@ public class AdminController extends BaseController { @ApiParam(value = "A JSON value representing the Security Settings.") @RequestBody SecuritySettings securitySettings) throws ThingsboardException { accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.WRITE); - securitySettings = checkNotNull(systemSecurityService.saveSecuritySettings(TenantId.SYS_TENANT_ID, securitySettings)); + securitySettings = checkNotNull(systemSecurityService.saveSecuritySettings(securitySettings)); return securitySettings; } diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java index 44263d19bd..6192a88e51 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -29,6 +29,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; @@ -44,6 +45,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.alarm.TbAlarmService; @@ -284,7 +286,7 @@ public class AlarmController extends BaseController { } TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(entityId, pageLink, alarmSearchStatus, alarmStatus, assigneeUserId, fetchOriginator)).get()); + return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(entityId, pageLink, alarmSearchStatus, alarmStatus, assigneeUserId, fetchOriginator))); } @ApiOperation(value = "Get All Alarms (getAllAlarms)", @@ -333,9 +335,9 @@ public class AlarmController extends BaseController { TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); if (getCurrentUser().isCustomerUser()) { - return checkNotNull(alarmService.findCustomerAlarms(getCurrentUser().getTenantId(), getCurrentUser().getCustomerId(), new AlarmQuery(null, pageLink, alarmSearchStatus, alarmStatus, assigneeUserId, fetchOriginator)).get()); + return checkNotNull(alarmService.findCustomerAlarms(getCurrentUser().getTenantId(), getCurrentUser().getCustomerId(), new AlarmQuery(null, pageLink, alarmSearchStatus, alarmStatus, assigneeUserId, fetchOriginator))); } else { - return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(null, pageLink, alarmSearchStatus, alarmStatus, assigneeUserId, fetchOriginator)).get()); + return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(null, pageLink, alarmSearchStatus, alarmStatus, assigneeUserId, fetchOriginator))); } } @@ -400,7 +402,7 @@ public class AlarmController extends BaseController { } TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - return checkNotNull(alarmService.findAlarmsV2(getCurrentUser().getTenantId(), new AlarmQueryV2(entityId, pageLink, alarmTypeList, alarmStatusList, alarmSeverityList, assigneeUserId)).get()); + return checkNotNull(alarmService.findAlarmsV2(getCurrentUser().getTenantId(), new AlarmQueryV2(entityId, pageLink, alarmTypeList, alarmStatusList, alarmSeverityList, assigneeUserId))); } @ApiOperation(value = "Get All Alarms (getAllAlarmsV2)", @@ -459,9 +461,9 @@ public class AlarmController extends BaseController { TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); if (getCurrentUser().isCustomerUser()) { - return checkNotNull(alarmService.findCustomerAlarmsV2(getCurrentUser().getTenantId(), getCurrentUser().getCustomerId(), new AlarmQueryV2(null, pageLink, alarmTypeList, alarmStatusList, alarmSeverityList, assigneeUserId)).get()); + return checkNotNull(alarmService.findCustomerAlarmsV2(getCurrentUser().getTenantId(), getCurrentUser().getCustomerId(), new AlarmQueryV2(null, pageLink, alarmTypeList, alarmStatusList, alarmSeverityList, assigneeUserId))); } else { - return checkNotNull(alarmService.findAlarmsV2(getCurrentUser().getTenantId(), new AlarmQueryV2(null, pageLink, alarmTypeList, alarmStatusList, alarmSeverityList, assigneeUserId)).get()); + return checkNotNull(alarmService.findAlarmsV2(getCurrentUser().getTenantId(), new AlarmQueryV2(null, pageLink, alarmTypeList, alarmStatusList, alarmSeverityList, assigneeUserId))); } } @@ -498,4 +500,21 @@ public class AlarmController extends BaseController { alarmStatus, assigneeId); } + @ApiOperation(value = "Get Alarm Types (getAlarmTypes)", + notes = "Returns a set of unique alarm types based on alarms that are either owned by the tenant or assigned to the customer which user is performing the request.", produces = MediaType.APPLICATION_JSON_VALUE) + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/alarm/types", method = RequestMethod.GET) + @ResponseBody + public PageData getAlarmTypes(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) + @RequestParam int pageSize, + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) + @RequestParam int page, + @ApiParam(value = ALARM_QUERY_TEXT_SEARCH_DESCRIPTION) + @RequestParam(required = false) String textSearch, + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) + @RequestParam(required = false) String sortOrder) throws ThingsboardException, ExecutionException, InterruptedException { + PageLink pageLink = createPageLink(pageSize, page, textSearch, "type", sortOrder); + return checkNotNull(alarmService.findAlarmTypesByTenantId(getTenantId(), pageLink)); + } + } diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java index 510fefe305..58b9376f1f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -153,7 +153,7 @@ public class AssetController extends BaseController { checkParameter(ASSET_ID, strAssetId); AssetId assetId = new AssetId(toUUID(strAssetId)); Asset asset = checkAssetId(assetId, Operation.DELETE); - tbAssetService.delete(asset, getCurrentUser()).get(); + tbAssetService.delete(asset, getCurrentUser()); } @ApiOperation(value = "Assign asset to customer (assignAssetToCustomer)", @@ -397,10 +397,12 @@ public class AssetController extends BaseController { } @ApiOperation(value = "Get Asset Types (getAssetTypes)", - notes = "Returns a set of unique asset types based on assets that are either owned by the tenant or assigned to the customer which user is performing the request.", produces = MediaType.APPLICATION_JSON_VALUE) + notes = "Deprecated. See 'getAssetProfileNames' API from Asset Profile Controller instead." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, + produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/asset/types", method = RequestMethod.GET) @ResponseBody + @Deprecated(since = "3.6.2") public List getAssetTypes() throws ThingsboardException, ExecutionException, InterruptedException { SecurityUser user = getCurrentUser(); TenantId tenantId = user.getTenantId(); diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java b/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java index 47a7eede4b..59c9670a0c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java @@ -29,22 +29,30 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.asset.AssetProfileInfo; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.AssetProfileId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.asset.profile.TbAssetProfileService; +import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; +import java.util.List; + import static org.thingsboard.server.controller.ControllerConstants.ASSET_PROFILE_ID; import static org.thingsboard.server.controller.ControllerConstants.ASSET_PROFILE_ID_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.ASSET_PROFILE_INFO_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.ASSET_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES; import static org.thingsboard.server.controller.ControllerConstants.ASSET_PROFILE_TEXT_SEARCH_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.INLINE_IMAGES; +import static org.thingsboard.server.controller.ControllerConstants.INLINE_IMAGES_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.NEW_LINE; import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS; import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; @@ -64,6 +72,7 @@ import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LI public class AssetProfileController extends BaseController { private final TbAssetProfileService tbAssetProfileService; + private final ImageService imageService; @ApiOperation(value = "Get Asset Profile (getAssetProfileById)", notes = "Fetch the Asset Profile object based on the provided Asset Profile Id. " + @@ -74,10 +83,16 @@ public class AssetProfileController extends BaseController { @ResponseBody public AssetProfile getAssetProfileById( @ApiParam(value = ASSET_PROFILE_ID_PARAM_DESCRIPTION) - @PathVariable(ASSET_PROFILE_ID) String strAssetProfileId) throws ThingsboardException { + @PathVariable(ASSET_PROFILE_ID) String strAssetProfileId, + @ApiParam(value = INLINE_IMAGES_DESCRIPTION) + @RequestParam(value = INLINE_IMAGES, required = false) boolean inlineImages) throws ThingsboardException { checkParameter(ASSET_PROFILE_ID, strAssetProfileId); AssetProfileId assetProfileId = new AssetProfileId(toUUID(strAssetProfileId)); - return checkAssetProfileId(assetProfileId, Operation.READ); + var result = checkAssetProfileId(assetProfileId, Operation.READ); + if (inlineImages) { + imageService.inlineImage(result); + } + return result; } @ApiOperation(value = "Get Asset Profile Info (getAssetProfileInfoById)", @@ -202,4 +217,19 @@ public class AssetProfileController extends BaseController { PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); return checkNotNull(assetProfileService.findAssetProfileInfos(getTenantId(), pageLink)); } + + @ApiOperation(value = "Get Asset Profile names (getAssetProfileNames)", + notes = "Returns a set of unique asset profile names owned by the tenant." + + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/assetProfile/names", method = RequestMethod.GET) + @ResponseBody + public List getAssetProfileNames( + @ApiParam(value = "Flag indicating whether to retrieve exclusively the names of asset profiles that are referenced by tenant's assets.") + @RequestParam(value = "activeOnly", required = false, defaultValue = "false") boolean activeOnly) throws ThingsboardException { + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + return checkNotNull(assetProfileService.findAssetProfileNamesByTenantId(tenantId, activeOnly)); + } + } diff --git a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java index 387f0d014b..642b744d80 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java @@ -99,7 +99,7 @@ public class AuditLogController extends BaseController { @RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException { checkParameter("CustomerId", strCustomerId); TenantId tenantId = getCurrentUser().getTenantId(); - TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, getStartTime(startTime), getEndTime(endTime)); List actionTypes = parseActionTypesStr(actionTypesStr); return checkNotNull(auditLogService.findAuditLogsByTenantIdAndCustomerId(tenantId, new CustomerId(UUID.fromString(strCustomerId)), actionTypes, pageLink)); } @@ -133,7 +133,7 @@ public class AuditLogController extends BaseController { @RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException { checkParameter("UserId", strUserId); TenantId tenantId = getCurrentUser().getTenantId(); - TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, getStartTime(startTime), getEndTime(endTime)); List actionTypes = parseActionTypesStr(actionTypesStr); return checkNotNull(auditLogService.findAuditLogsByTenantIdAndUserId(tenantId, new UserId(UUID.fromString(strUserId)), actionTypes, pageLink)); } @@ -171,7 +171,7 @@ public class AuditLogController extends BaseController { checkParameter("EntityId", strEntityId); checkParameter("EntityType", strEntityType); TenantId tenantId = getCurrentUser().getTenantId(); - TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, getStartTime(startTime), getEndTime(endTime)); List actionTypes = parseActionTypesStr(actionTypesStr); return checkNotNull(auditLogService.findAuditLogsByTenantIdAndEntityId(tenantId, EntityIdFactory.getByTypeAndId(strEntityType, strEntityId), actionTypes, pageLink)); } @@ -202,7 +202,7 @@ public class AuditLogController extends BaseController { @RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException { TenantId tenantId = getCurrentUser().getTenantId(); List actionTypes = parseActionTypesStr(actionTypesStr); - TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, getStartTime(startTime), getEndTime(endTime)); return checkNotNull(auditLogService.findAuditLogsByTenantId(tenantId, actionTypes, pageLink)); } @@ -214,4 +214,18 @@ public class AuditLogController extends BaseController { } return result; } + + private Long getStartTime(Long startTime) { + if (startTime == null) { + return 1L; + } + return startTime; + } + + private Long getEndTime(Long endTime) { + if (endTime == null) { + return System.currentTimeMillis(); + } + return endTime; + } } diff --git a/application/src/main/java/org/thingsboard/server/controller/AuthController.java b/application/src/main/java/org/thingsboard/server/controller/AuthController.java index 566b6900a4..efb05cfdc3 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuthController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AuthController.java @@ -38,19 +38,18 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; -import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.security.event.UserCredentialsInvalidationEvent; import org.thingsboard.server.common.data.security.event.UserSessionInvalidationEvent; import org.thingsboard.server.common.data.security.model.JwtPair; import org.thingsboard.server.common.data.security.model.SecuritySettings; import org.thingsboard.server.common.data.security.model.UserPasswordPolicy; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.dao.util.limits.LimitedApi; import org.thingsboard.server.dao.util.limits.RateLimitService; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails; import org.thingsboard.server.service.security.model.ActivateUserRequest; import org.thingsboard.server.service.security.model.ChangePasswordRequest; @@ -116,15 +115,13 @@ public class AuthController extends BaseController { if (!passwordEncoder.matches(currentPassword, userCredentials.getPassword())) { throw new ThingsboardException("Current password doesn't match!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } - systemSecurityService.validatePassword(securityUser.getTenantId(), newPassword, userCredentials); + systemSecurityService.validatePassword(newPassword, userCredentials); if (passwordEncoder.matches(newPassword, userCredentials.getPassword())) { throw new ThingsboardException("New password should be different from existing!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } userCredentials.setPassword(passwordEncoder.encode(newPassword)); userService.replaceUserCredentials(securityUser.getTenantId(), userCredentials); - sendEntityNotificationMsg(getTenantId(), userCredentials.getUserId(), EdgeEventActionType.CREDENTIALS_UPDATED); - eventPublisher.publishEvent(new UserCredentialsInvalidationEvent(securityUser.getId())); ObjectNode response = JacksonUtil.newObjectNode(); response.put("token", tokenFactory.createAccessJwtToken(securityUser).getToken()); @@ -138,7 +135,7 @@ public class AuthController extends BaseController { @ResponseBody public UserPasswordPolicy getUserPasswordPolicy() throws ThingsboardException { SecuritySettings securitySettings = - checkNotNull(systemSecurityService.getSecuritySettings(TenantId.SYS_TENANT_ID)); + checkNotNull(systemSecurityService.getSecuritySettings()); return securitySettings.getPasswordPolicy(); } @@ -240,7 +237,7 @@ public class AuthController extends BaseController { HttpServletRequest request) throws ThingsboardException { String activateToken = activateRequest.getActivateToken(); String password = activateRequest.getPassword(); - systemSecurityService.validatePassword(TenantId.SYS_TENANT_ID, password, null); + systemSecurityService.validatePassword(password, null); String encodedPassword = passwordEncoder.encode(password); UserCredentials credentials = userService.activateUserCredentials(TenantId.SYS_TENANT_ID, activateToken, encodedPassword); User user = userService.findUserById(TenantId.SYS_TENANT_ID, credentials.getUserId()); @@ -259,8 +256,6 @@ public class AuthController extends BaseController { } } - sendEntityNotificationMsg(user.getTenantId(), user.getId(), EdgeEventActionType.CREDENTIALS_UPDATED); - return tokenFactory.createTokenPair(securityUser); } @@ -279,7 +274,7 @@ public class AuthController extends BaseController { String password = resetPasswordRequest.getPassword(); UserCredentials userCredentials = userService.findUserCredentialsByResetToken(TenantId.SYS_TENANT_ID, resetToken); if (userCredentials != null) { - systemSecurityService.validatePassword(TenantId.SYS_TENANT_ID, password, userCredentials); + systemSecurityService.validatePassword(password, userCredentials); if (passwordEncoder.matches(password, userCredentials.getPassword())) { throw new ThingsboardException("New password should be different from existing!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 68a987a0bc..f12a20094d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -21,7 +21,7 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import lombok.Getter; -import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; @@ -31,6 +31,7 @@ import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.context.request.async.AsyncRequestTimeoutException; import org.springframework.web.context.request.async.DeferredResult; +import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; @@ -60,8 +61,6 @@ import org.thingsboard.server.common.data.asset.AssetInfo; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; -import org.thingsboard.server.common.data.edge.EdgeEventActionType; -import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.edge.EdgeInfo; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -126,6 +125,7 @@ import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rpc.RpcService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.ConstraintValidator; @@ -142,14 +142,11 @@ import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.action.EntityActionService; import org.thingsboard.server.service.component.ComponentDiscoveryService; -import org.thingsboard.server.service.edge.instructions.EdgeInstallService; -import org.thingsboard.server.service.edge.rpc.EdgeRpcService; import org.thingsboard.server.service.entitiy.TbNotificationEntityService; import org.thingsboard.server.service.entitiy.user.TbUserSettingsService; import org.thingsboard.server.service.ota.OtaPackageStateService; import org.thingsboard.server.service.profile.TbAssetProfileCache; import org.thingsboard.server.service.profile.TbDeviceProfileCache; -import org.thingsboard.server.service.resource.TbResourceService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.AccessControlService; import org.thingsboard.server.service.security.permission.Operation; @@ -178,10 +175,11 @@ import static org.thingsboard.server.common.data.query.EntityKeyType.ENTITY_FIEL import static org.thingsboard.server.controller.UserController.YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION; import static org.thingsboard.server.dao.service.Validator.validateId; -@Slf4j @TbCoreComponent public abstract class BaseController { + private final Logger log = org.slf4j.LoggerFactory.getLogger(getClass()); + /*Swagger UI description*/ @Autowired @@ -275,7 +273,7 @@ public abstract class BaseController { protected PartitionService partitionService; @Autowired - protected TbResourceService resourceService; + protected ResourceService resourceService; @Autowired protected OtaPackageService otaPackageService; @@ -301,12 +299,6 @@ public abstract class BaseController { @Autowired(required = false) protected EdgeService edgeService; - @Autowired(required = false) - protected EdgeRpcService edgeRpcService; - - @Autowired(required = false) - protected EdgeInstallService edgeInstallService; - @Autowired protected TbNotificationEntityService notificationEntityService; @@ -596,7 +588,7 @@ public abstract class BaseController { checkWidgetTypeId(new WidgetTypeId(entityId.getId()), operation); return; case TB_RESOURCE: - checkResourceId(new TbResourceId(entityId.getId()), operation); + checkResourceInfoId(new TbResourceId(entityId.getId()), operation); return; case OTA_PACKAGE: checkOtaPackageId(new OtaPackageId(entityId.getId()), operation); @@ -824,18 +816,6 @@ public abstract class BaseController { } } - protected void sendEntityNotificationMsg(TenantId tenantId, EntityId entityId, EdgeEventActionType action) { - sendNotificationMsgToEdge(tenantId, null, entityId, null, null, action); - } - - protected void sendEntityAssignToEdgeNotificationMsg(TenantId tenantId, EdgeId edgeId, EntityId entityId, EdgeEventActionType action) { - sendNotificationMsgToEdge(tenantId, edgeId, entityId, null, null, action); - } - - private void sendNotificationMsgToEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, EdgeEventType type, EdgeEventActionType action) { - tbClusterService.sendNotificationMsgToEdge(tenantId, edgeId, entityId, body, type, action); - } - protected void processDashboardIdFromAdditionalInfo(ObjectNode additionalInfo, String requiredFields) throws ThingsboardException { String dashboardId = additionalInfo.has(requiredFields) ? additionalInfo.get(requiredFields).asText() : null; if (dashboardId != null && !dashboardId.equals("null")) { @@ -854,18 +834,14 @@ public abstract class BaseController { } protected DeferredResult wrapFuture(ListenableFuture future) { - final DeferredResult deferredResult = new DeferredResult<>(); - Futures.addCallback(future, new FutureCallback<>() { - @Override - public void onSuccess(T result) { - deferredResult.setResult(result); - } + DeferredResult deferredResult = new DeferredResult<>(); // Timeout of spring.mvc.async.request-timeout is used + DonAsynchron.withCallback(future, deferredResult::setResult, deferredResult::setErrorResult); + return deferredResult; + } - @Override - public void onFailure(Throwable t) { - deferredResult.setErrorResult(t); - } - }, MoreExecutors.directExecutor()); + protected DeferredResult wrapFuture(ListenableFuture future, long timeoutMs) { + DeferredResult deferredResult = new DeferredResult<>(timeoutMs); + DonAsynchron.withCallback(future, deferredResult::setResult, deferredResult::setErrorResult); return deferredResult; } diff --git a/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java b/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java index 1cb794c2ec..5c1842b4b4 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java +++ b/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java @@ -24,6 +24,7 @@ public class ControllerConstants { protected static final String CUSTOMER_ID = "customerId"; protected static final String TENANT_ID = "tenantId"; protected static final String DEVICE_ID = "deviceId"; + protected static final String PROTOCOL = "protocol"; protected static final String EDGE_ID = "edgeId"; protected static final String RPC_ID = "rpcId"; protected static final String ENTITY_ID = "entityId"; @@ -31,9 +32,13 @@ public class ControllerConstants { protected static final String PAGE_DATA_PARAMETERS = "You can specify parameters to filter the results. " + "The result is wrapped with PageData object that allows you to iterate over result set using pagination. " + "See the 'Model' tab of the Response Class for more details. "; + + protected static final String INLINE_IMAGES = "inlineImages"; + protected static final String INLINE_IMAGES_DESCRIPTION = "Inline images as a data URL (Base64)"; protected static final String DASHBOARD_ID_PARAM_DESCRIPTION = "A string value representing the dashboard id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; protected static final String RPC_ID_PARAM_DESCRIPTION = "A string value representing the rpc id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; protected static final String DEVICE_ID_PARAM_DESCRIPTION = "A string value representing the device id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; + protected static final String PROTOCOL_PARAM_DESCRIPTION = "A string value representing the device connectivity protocol. Possible values: 'mqtt', 'mqtts', 'http', 'https', 'coap', 'coaps'"; protected static final String ENTITY_VIEW_ID_PARAM_DESCRIPTION = "A string value representing the entity view id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; protected static final String DEVICE_PROFILE_ID_PARAM_DESCRIPTION = "A string value representing the device profile id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; @@ -74,6 +79,7 @@ public class ControllerConstants { protected static final String ASSET_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the asset name."; protected static final String DASHBOARD_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the dashboard title."; protected static final String WIDGET_BUNDLE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the widget bundle title."; + protected static final String WIDGET_TYPE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the widget type name."; protected static final String RPC_TEXT_SEARCH_DESCRIPTION = "Not implemented. Leave empty."; protected static final String DEVICE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the device name."; protected static final String ENTITY_VIEW_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the entity view name."; @@ -110,6 +116,7 @@ public class ControllerConstants { protected static final String EDGE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, type, label, customerTitle"; protected static final String RULE_CHAIN_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, root"; protected static final String WIDGET_BUNDLE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, title, tenantId"; + protected static final String WIDGET_TYPE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, deprecated, tenantId"; protected static final String AUDIT_LOG_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, entityType, entityName, userName, actionType, actionStatus"; protected static final String SORT_ORDER_DESCRIPTION = "Sort order. ASC (ASCENDING) or DESC (DESCENDING)"; protected static final String SORT_ORDER_ALLOWABLE_VALUES = "ASC, DESC"; @@ -138,6 +145,8 @@ public class ControllerConstants { protected static final String RESOURCE_INFO_DESCRIPTION = "Resource Info is a lightweight object that includes main information about the Resource excluding the heavyweight data. "; protected static final String RESOURCE_DESCRIPTION = "Resource is a heavyweight object that includes main information about the Resource and also data. "; + protected static final String RESOURCE_INCLUDE_SYSTEM_IMAGES_DESCRIPTION = "Use 'true' to include system images. Disabled by default. Ignored for requests by users with system administrator authority."; + protected static final String RESOURCE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the resource title."; protected static final String RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, title, resourceType, tenantId"; protected static final String RESOURCE_TYPE_PROPERTY_ALLOWABLE_VALUES = "LWM2M_MODEL, JKS, PKCS_12, JS_MODULE"; @@ -207,42 +216,222 @@ public class ControllerConstants { protected static final String IS_BOOTSTRAP_SERVER_PARAM_DESCRIPTION = "A Boolean value representing the Server SecurityInfo for future Bootstrap client mode settings. Values: 'true' for Bootstrap Server; 'false' for Lwm2m Server. "; - protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_DESCRIPTION = + protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_ACCESS_TOKEN_PARAM_DESCRIPTION = "{\n" + " \"device\": {\n" + - " \"name\": \"LwRpk00000000\",\n" + - " \"type\": \"lwm2mProfileRpk\"\n" + - " },\n" + + " \"name\":\"Name_DeviceWithCredantial_AccessToken\",\n" + + " \"label\":\"Label_DeviceWithCredantial_AccessToken\",\n" + + " \"deviceProfileId\":{\n" + + " \"id\":\"9d9588c0-06c9-11ee-b618-19be30fdeb60\",\n" + + " \"entityType\":\"DEVICE_PROFILE\"\n" + + " }\n" + + " },\n" + " \"credentials\": {\n" + - " \"id\": \"null\",\n" + - " \"createdTime\": 0,\n" + - " \"deviceId\": \"null\",\n" + - " \"credentialsType\": \"LWM2M_CREDENTIALS\",\n" + - " \"credentialsId\": \"LwRpk00000000\",\n" + - " \"credentialsValue\": {\n" + - " \"client\": {\n" + - " \"endpoint\": \"LwRpk00000000\",\n" + - " \"securityConfigClientMode\": \"RPK\",\n" + - " \"key\": \"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUEBxNl/RcYJNm8mk91CyVXoIJiROYDlXcSSqK6e5bDHwOW4ZiN2lNnXalyF0Jxw8MbAytnDMERXyAja5VEMeVQ==\"\n" + - " },\n" + - " \"bootstrap\": {\n" + - " \"bootstrapServer\": {\n" + - " \"securityMode\": \"RPK\",\n" + - " \"clientPublicKeyOrId\": \"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUEBxNl/RcYJNm8mk91CyVXoIJiROYDlXcSSqK6e5bDHwOW4ZiN2lNnXalyF0Jxw8MbAytnDMERXyAja5VEMeVQ==\",\n" + - " \"clientSecretKey\": \"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgd9GAx7yZW37autew5KZykn4IgRpge/tZSjnudnZJnMahRANCAARQQHE2X9Fxgk2byaT3ULJVeggmJE5gOVdxJKorp7lsMfA5bhmI3aU2ddqXIXQnHDwxsDK2cMwRFfICNrlUQx5V\"\n" + - " },\n" + - " \"lwm2mServer\": {\n" + - " \"securityMode\": \"RPK\",\n" + - " \"clientPublicKeyOrId\": \"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUEBxNl/RcYJNm8mk91CyVXoIJiROYDlXcSSqK6e5bDHwOW4ZiN2lNnXalyF0Jxw8MbAytnDMERXyAja5VEMeVQ==\",\n" + - " \"clientSecretKey\": \"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgd9GAx7yZW37autew5KZykn4IgRpge/tZSjnudnZJnMahRANCAARQQHE2X9Fxgk2byaT3ULJVeggmJE5gOVdxJKorp7lsMfA5bhmI3aU2ddqXIXQnHDwxsDK2cMwRFfICNrlUQx5V\"\n" + - " }\n" + - " }\n" + - " }\n" + - " }\n" + + " \"credentialsType\": \"ACCESS_TOKEN\",\n" + + " \"credentialsId\": \"6hmxew8pmmzng4e3une2\"\n" + + " }\n" + + "}"; + + protected static final String DEVICE_UPDATE_CREDENTIALS_ACCESS_TOKEN_PARAM_DESCRIPTION = + "{\n" + + " \"id\": {\n" + + " \"id\":\"c886a090-168d-11ee-87c9-6f157dbc816a\"\n" + + " },\n" + + " \"deviceId\": {\n" + + " \"id\":\"c5fb3ac0-168d-11ee-87c9-6f157dbc816a\",\n" + + " \"entityType\":\"DEVICE\"\n" + + " },\n" + + " \"credentialsType\": \"ACCESS_TOKEN\",\n" + + " \"credentialsId\": \"6hmxew8pmmzng4e3une4\"\n" + "}"; - protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_DESCRIPTION_MARKDOWN = - MARKDOWN_CODE_BLOCK_START + DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_DESCRIPTION + MARKDOWN_CODE_BLOCK_END; + protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_ACCESS_TOKEN_DEFAULT_PARAM_DESCRIPTION = + "{\n" + + " \"device\": {\n" + + " \"name\":\"Name_DeviceWithCredantial_AccessToken_Default\",\n" + + " \"label\":\"Label_DeviceWithCredantial_AccessToken_Default\",\n" + + " \"type\": \"default\"\n" + + " },\n" + + " \"credentials\": {\n" + + " \"credentialsType\": \"ACCESS_TOKEN\",\n" + + " \"credentialsId\": \"6hmxew8pmmzng4e3une3\"\n" + + " }\n" + + "}"; + + protected static final String certificateValue = "\"-----BEGIN CERTIFICATE----- " + + "MIICMTCCAdegAwIBAgIUI9dBuwN6pTtK6uZ03rkiCwV4wEYwCgYIKoZIzj0EAwIwbjELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE5ldyBZb3JrMRowGAYDVQQKDBFUaGluZ3NCb2FyZCwgSW5jLjEwMC4GA1UEAwwnZGV2aWNlQ2VydGlmaWNhdGVAWDUwOVByb3Zpc2lvblN0cmF0ZWd5MB4XDTIzMDMyOTE0NTYxN1oXDTI0MDMyODE0NTYxN1owbjELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE5ldyBZb3JrMRowGAYDVQQKDBFUaGluZ3NCb2FyZCwgSW5jLjEwMC4GA1UEAwwnZGV2aWNlQ2VydGlmaWNhdGVAWDUwOVByb3Zpc2lvblN0cmF0ZWd5MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9Zo791qKQiGNBm11r4ZGxh+w+ossZL3xc46ufq5QckQHP7zkD2XDAcmP5GvdkM1sBFN9AWaCkQfNnWmfERsOOKNTMFEwHQYDVR0OBBYEFFFc5uyCyglQoZiKhzXzMcQ3BKORMB8GA1UdIwQYMBaAFFFc5uyCyglQoZiKhzXzMcQ3BKORMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIhANbA9CuhoOifZMMmqkpuld+65CR+ItKdXeRAhLMZuccuAiB0FSQB34zMutXrZj1g8Gl5OkE7YryFHbei1z0SveHR8g== " + + "-----END CERTIFICATE-----\""; + + protected static final String certificateId = "\"84f5911765abba1f96bf4165604e9e90338fc6214081a8e623b6ff9669aedb27\""; + + protected static final String certificateValueUpdate = "\"-----BEGIN CERTIFICATE----- " + + "MIICMTCCAdegAwIBAgIUUEKxS9hTz4l+oLUMF0LV6TC/gCIwCgYIKoZIzj0EAwIwbjELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE5ldyBZb3JrMRowGAYDVQQKDBFUaGluZ3NCb2FyZCwgSW5jLjEwMC4GA1UEAwwnZGV2aWNlUHJvZmlsZUNlcnRAWDUwOVByb3Zpc2lvblN0cmF0ZWd5MB4XDTIzMDMyOTE0NTczNloXDTI0MDMyODE0NTczNlowbjELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE5ldyBZb3JrMRowGAYDVQQKDBFUaGluZ3NCb2FyZCwgSW5jLjEwMC4GA1UEAwwnZGV2aWNlUHJvZmlsZUNlcnRAWDUwOVByb3Zpc2lvblN0cmF0ZWd5MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAECMlWO72krDoUL9FQjUmSCetkhaEGJUfQkdSfkLSNa0GyAEIMbfmzI4zITeapunu4rGet3EMyLydQzuQanBicp6NTMFEwHQYDVR0OBBYEFHpZ78tPnztNii4Da/yCw6mhEIL3MB8GA1UdIwQYMBaAFHpZ78tPnztNii4Da/yCw6mhEIL3MA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIgJ7qyMFqNcwSYkH6o+UlQXzLWfwZbNjVk+aR7foAZNGsCIQDsd7v3WQIGHiArfZeDs1DLEDuV/2h6L+ZNoGNhEKL+1A== " + + "-----END CERTIFICATE-----\""; + + protected static final String certificateIdUpdate = "\"6b8adb49015500e51a527acd332b51684ab9b49b4ade03a9582a44c455e2e9b6\""; + + protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_X509_CERTIFICATE_PARAM_DESCRIPTION = + "{\n" + + " \"device\": {\n" + + " \"name\":\"Name_DeviceWithCredantial_X509_Certificate\",\n" + + " \"label\":\"Label_DeviceWithCredantial_X509_Certificate\",\n" + + " \"deviceProfileId\":{\n" + + " \"id\":\"9d9588c0-06c9-11ee-b618-19be30fdeb60\",\n" + + " \"entityType\":\"DEVICE_PROFILE\"\n" + + " }\n" + + " },\n" + + " \"credentials\": {\n" + + " \"credentialsType\": \"X509_CERTIFICATE\",\n" + + " \"credentialsId\": " + certificateId + ",\n" + + " \"credentialsValue\": " + certificateValue + "\n" + + " }\n" + + "}"; + + protected static final String DEVICE_UPDATE_CREDENTIALS_X509_CERTIFICATE_PARAM_DESCRIPTION = + "{\n" + + " \"id\": {\n" + + " \"id\":\"309bd9c0-14f4-11ee-9fc9-d9b7463abb63\"\n" + + " },\n" + + " \"deviceId\": {\n" + + " \"id\":\"3092b200-14f4-11ee-9fc9-d9b7463abb63\",\n" + + " \"entityType\":\"DEVICE\"\n" + + " },\n" + + " \"credentialsType\": \"X509_CERTIFICATE\",\n" + + " \"credentialsId\": " + certificateIdUpdate + ",\n" + + " \"credentialsValue\": " + certificateValueUpdate + "\n" + + "}"; + + protected static final String MQTT_BASIC_VALUE = "\"{\\\"clientId\\\":\\\"5euh5nzm34bjjh1efmlt\\\",\\\"userName\\\":\\\"onasd1lgwasmjl7v2v7h\\\",\\\"password\\\":\\\"b9xtm4ny8kt9zewaga5o\\\"}\""; + + protected static final String MQTT_BASIC_VALUE_UPDATE = "\"{\\\"clientId\\\":\\\"juy03yv4owqxcmqhqtvk\\\",\\\"userName\\\":\\\"ov19fxca0cyjn7lm7w7u\\\",\\\"password\\\":\\\"twy94he114dfi9usyk1o\\\"}\""; + + protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_MQTT_BASIC_PARAM_DESCRIPTION = + "{\n" + + " \"device\": {\n" + + " \"name\":\"Name_DeviceWithCredantial_MQTT_Basic\",\n" + + " \"label\":\"Label_DeviceWithCredantial_MQTT_Basic\",\n" + + " \"deviceProfileId\":{\n" + + " \"id\":\"9d9588c0-06c9-11ee-b618-19be30fdeb60\",\n" + + " \"entityType\":\"DEVICE_PROFILE\"\n" + + " }\n" + + " },\n" + + " \"credentials\": {\n" + + " \"credentialsType\": \"MQTT_BASIC\",\n" + + " \"credentialsValue\": " + MQTT_BASIC_VALUE + "\n" + + " }\n" + + "}"; + + protected static final String DEVICE_UPDATE_CREDENTIALS_MQTT_BASIC_PARAM_DESCRIPTION = + "{\n" + + " \"id\": {\n" + + " \"id\":\"d877ffb0-14f5-11ee-9fc9-d9b7463abb63\"\n" + + " },\n" + + " \"deviceId\": {\n" + + " \"id\":\"d875dcd0-14f5-11ee-9fc9-d9b7463abb63\",\n" + + " \"entityType\":\"DEVICE\"\n" + + " },\n" + + " \"credentialsType\": \"MQTT_BASIC\",\n" + + " \"credentialsValue\": " + MQTT_BASIC_VALUE_UPDATE + "\n" + + "}"; + + protected static final String CREDENTIALS_VALUE_LVM2M_RPK_DESCRIPTION = + " \"{" + + "\\\"client\\\":{ " + + "\\\"endpoint\\\":\\\"LwRpk00000000\\\", " + + "\\\"securityConfigClientMode\\\":\\\"RPK\\\", " + + "\\\"key\\\":\\\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUEBxNl/RcYJNm8mk91CyVXoIJiROYDlXcSSqK6e5bDHwOW4ZiN2lNnXalyF0Jxw8MbAytnDMERXyAja5VEMeVQ==\\\"" + + " }, " + + "\\\"bootstrap\\\":{ " + + "\\\"bootstrapServer\\\":{ " + + "\\\"securityMode\\\":\\\"RPK\\\", " + + "\\\"clientPublicKeyOrId\\\":\\\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUEBxNl/RcYJNm8mk91CyVXoIJiROYDlXcSSqK6e5bDHwOW4ZiN2lNnXalyF0Jxw8MbAytnDMERXyAja5VEMeVQ==\\\", " + + "\\\"clientSecretKey\\\":\\\"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgd9GAx7yZW37autew5KZykn4IgRpge/tZSjnudnZJnMahRANCAARQQHE2X9Fxgk2byaT3ULJVeggmJE5gOVdxJKorp7lsMfA5bhmI3aU2ddqXIXQnHDwxsDK2cMwRFfICNrlUQx5V\\\"" + + "}, " + + "\\\"lwm2mServer\\\":{ \\\"securityMode\\\":\\\"RPK\\\", " + + "\\\"clientPublicKeyOrId\\\":\\\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUEBxNl/RcYJNm8mk91CyVXoIJiROYDlXcSSqK6e5bDHwOW4ZiN2lNnXalyF0Jxw8MbAytnDMERXyAja5VEMeVQ==\\\", " + + "\\\"clientSecretKey\\\":\\\"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgd9GAx7yZW37autew5KZykn4IgRpge/tZSjnudnZJnMahRANCAARQQHE2X9Fxgk2byaT3ULJVeggmJE5gOVdxJKorp7lsMfA5bhmI3aU2ddqXIXQnHDwxsDK2cMwRFfICNrlUQx5V\\\"" + + "}" + + "} " + + "}\""; + + protected static final String CREDENTIALS_VALUE_UPDATE_LVM2M_RPK_DESCRIPTION = + " \"{" + + "\\\"client\\\":{ " + + "\\\"endpoint\\\":\\\"LwRpk00000000\\\", " + + "\\\"securityConfigClientMode\\\":\\\"RPK\\\", " + + "\\\"key\\\":\\\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEdvBZZ2vQRK9wgDhctj6B1c7bxR3Z0wYg1+YdoYFnVUKWb+rIfTTyYK9tmQJx5Vlb5fxdLnVv1RJOPiwsLIQbAA==\\\"" + + " }, " + + "\\\"bootstrap\\\":{ " + + "\\\"bootstrapServer\\\":{ " + + "\\\"securityMode\\\":\\\"RPK\\\", " + + "\\\"clientPublicKeyOrId\\\":\\\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUEBxNl/RcYJNm8mk91CyVXoIJiROYDlXcSSqK6e5bDHwOW4ZiN2lNnXalyF0Jxw8MbAytnDMERXyAja5VEMeVQ==\\\", " + + "\\\"clientSecretKey\\\":\\\"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgd9GAx7yZW37autew5KZykn4IgRpge/tZSjnudnZJnMahRANCAARQQHE2X9Fxgk2byaT3ULJVeggmJE5gOVdxJKorp7lsMfA5bhmI3aU2ddqXIXQnHDwxsDK2cMwRFfICNrlUQx5V\\\"" + + "}, " + + "\\\"lwm2mServer\\\":{ \\\"securityMode\\\":\\\"RPK\\\", " + + "\\\"clientPublicKeyOrId\\\":\\\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUEBxNl/RcYJNm8mk91CyVXoIJiROYDlXcSSqK6e5bDHwOW4ZiN2lNnXalyF0Jxw8MbAytnDMERXyAja5VEMeVQ==\\\", " + + "\\\"clientSecretKey\\\":\\\"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgd9GAx7yZW37autew5KZykn4IgRpge/tZSjnudnZJnMahRANCAARQQHE2X9Fxgk2byaT3ULJVeggmJE5gOVdxJKorp7lsMfA5bhmI3aU2ddqXIXQnHDwxsDK2cMwRFfICNrlUQx5V\\\"" + + "}" + + "} " + + "}\""; + + protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_LVM2M_RPK_DESCRIPTION = + "{\n" + + " \"device\": {\n" + + " \"name\":\"Name_LwRpk00000000\",\n" + + " \"label\":\"Label_LwRpk00000000\",\n" + + " \"deviceProfileId\":{\n" + + " \"id\":\"a660bd50-10ef-11ee-8737-b5634e73c779\",\n" + + " \"entityType\":\"DEVICE_PROFILE\"\n" + + " }\n" + + " },\n" + + " \"credentials\": {\n" + + " \"credentialsType\": \"LWM2M_CREDENTIALS\",\n" + + " \"credentialsId\": \"LwRpk00000000\",\n" + + " \"credentialsValue\":\n" + CREDENTIALS_VALUE_LVM2M_RPK_DESCRIPTION + "\n" + + " }\n" + + "}"; + + protected static final String DEVICE_UPDATE_CREDENTIALS_PARAM_LVM2M_RPK_DESCRIPTION = + "{\n" + + " \"id\": {\n" + + " \"id\":\"e238d4d0-1689-11ee-98c6-1713c1be5a8e\"\n" + + " },\n" + + " \"deviceId\": {\n" + + " \"id\":\"e232e160-1689-11ee-98c6-1713c1be5a8e\",\n" + + " \"entityType\":\"DEVICE\"\n" + + " },\n" + + " \"credentialsType\": \"LWM2M_CREDENTIALS\",\n" + + " \"credentialsId\": \"LwRpk00000000\",\n" + + " \"credentialsValue\":\n" + CREDENTIALS_VALUE_UPDATE_LVM2M_RPK_DESCRIPTION + "\n" + + "}"; + + protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_ACCESS_TOKEN_DESCRIPTION_MARKDOWN = + MARKDOWN_CODE_BLOCK_START + DEVICE_WITH_DEVICE_CREDENTIALS_ACCESS_TOKEN_PARAM_DESCRIPTION + MARKDOWN_CODE_BLOCK_END; + + protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_ACCESS_TOKEN_DEFAULT_DESCRIPTION_MARKDOWN = + MARKDOWN_CODE_BLOCK_START + DEVICE_WITH_DEVICE_CREDENTIALS_ACCESS_TOKEN_DEFAULT_PARAM_DESCRIPTION + MARKDOWN_CODE_BLOCK_END; + + protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_X509_CERTIFICATE_DESCRIPTION_MARKDOWN = + MARKDOWN_CODE_BLOCK_START + DEVICE_WITH_DEVICE_CREDENTIALS_X509_CERTIFICATE_PARAM_DESCRIPTION + MARKDOWN_CODE_BLOCK_END; + + protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_MQTT_BASIC_DESCRIPTION_MARKDOWN = + MARKDOWN_CODE_BLOCK_START + DEVICE_WITH_DEVICE_CREDENTIALS_MQTT_BASIC_PARAM_DESCRIPTION + MARKDOWN_CODE_BLOCK_END; + + protected static final String DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_LVM2M_RPK_DESCRIPTION_MARKDOWN = + MARKDOWN_CODE_BLOCK_START + DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_LVM2M_RPK_DESCRIPTION + MARKDOWN_CODE_BLOCK_END; + + protected static final String DEVICE_UPDATE_CREDENTIALS_PARAM_ACCESS_TOKEN_DESCRIPTION_MARKDOWN = + MARKDOWN_CODE_BLOCK_START + DEVICE_UPDATE_CREDENTIALS_ACCESS_TOKEN_PARAM_DESCRIPTION + MARKDOWN_CODE_BLOCK_END; + + protected static final String DEVICE_UPDATE_CREDENTIALS_PARAM_X509_CERTIFICATE_DESCRIPTION_MARKDOWN = + MARKDOWN_CODE_BLOCK_START + DEVICE_UPDATE_CREDENTIALS_X509_CERTIFICATE_PARAM_DESCRIPTION + MARKDOWN_CODE_BLOCK_END; + + protected static final String DEVICE_UPDATE_CREDENTIALS_PARAM_MQTT_BASIC_DESCRIPTION_MARKDOWN = + MARKDOWN_CODE_BLOCK_START + DEVICE_UPDATE_CREDENTIALS_MQTT_BASIC_PARAM_DESCRIPTION + MARKDOWN_CODE_BLOCK_END; + + protected static final String DEVICE_UPDATE_CREDENTIALS_PARAM_LVM2M_RPK_DESCRIPTION_MARKDOWN = + MARKDOWN_CODE_BLOCK_START + DEVICE_UPDATE_CREDENTIALS_PARAM_LVM2M_RPK_DESCRIPTION + MARKDOWN_CODE_BLOCK_END; + protected static final String FILTER_VALUE_TYPE = NEW_LINE + "## Value Type and Operations" + NEW_LINE + @@ -254,7 +443,7 @@ public class ControllerConstants { " * 'BOOLEAN' - used for boolean values. Operations: EQUAL, NOT_EQUAL;\n" + " * 'DATE_TIME' - similar to numeric, transforms value to milliseconds since epoch. Operations: EQUAL, NOT_EQUAL, GREATER, LESS, GREATER_OR_EQUAL, LESS_OR_EQUAL; \n"; - protected static final String DEVICE_PROFILE_ALARM_SCHEDULE_SPECIFIC_TIME_EXAMPLE = MARKDOWN_CODE_BLOCK_START + + protected static final String DEVICE_PROFILE_ALARM_SCHEDULE_SPECIFIC_TIME_EXAMPLE = MARKDOWN_CODE_BLOCK_START + "{\n" + " \"schedule\":{\n" + " \"type\":\"SPECIFIC_TIME\",\n" + @@ -269,7 +458,7 @@ public class ControllerConstants { " }\n" + "}" + MARKDOWN_CODE_BLOCK_END; - protected static final String DEVICE_PROFILE_ALARM_SCHEDULE_CUSTOM_EXAMPLE = MARKDOWN_CODE_BLOCK_START + + protected static final String DEVICE_PROFILE_ALARM_SCHEDULE_CUSTOM_EXAMPLE = MARKDOWN_CODE_BLOCK_START + "{\n" + " \"schedule\":{\n" + " \"type\":\"CUSTOM\",\n" + @@ -321,9 +510,9 @@ public class ControllerConstants { " }\n" + "}" + MARKDOWN_CODE_BLOCK_END; - protected static final String DEVICE_PROFILE_ALARM_SCHEDULE_ALWAYS_EXAMPLE = MARKDOWN_CODE_BLOCK_START + "\"schedule\": null" + MARKDOWN_CODE_BLOCK_END; + protected static final String DEVICE_PROFILE_ALARM_SCHEDULE_ALWAYS_EXAMPLE = MARKDOWN_CODE_BLOCK_START + "\"schedule\": null" + MARKDOWN_CODE_BLOCK_END; - protected static final String DEVICE_PROFILE_ALARM_CONDITION_REPEATING_EXAMPLE = MARKDOWN_CODE_BLOCK_START + + protected static final String DEVICE_PROFILE_ALARM_CONDITION_REPEATING_EXAMPLE = MARKDOWN_CODE_BLOCK_START + "{\n" + " \"spec\":{\n" + " \"type\":\"REPEATING\",\n" + @@ -339,7 +528,8 @@ public class ControllerConstants { " }\n" + "}" + MARKDOWN_CODE_BLOCK_END; - protected static final String DEVICE_PROFILE_ALARM_CONDITION_DURATION_EXAMPLE = MARKDOWN_CODE_BLOCK_START + + + protected static final String DEVICE_PROFILE_ALARM_CONDITION_DURATION_EXAMPLE = MARKDOWN_CODE_BLOCK_START + "{\n" + " \"spec\":{\n" + " \"type\":\"DURATION\",\n" + diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java index afc737ab88..502e40e929 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -51,6 +51,7 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.dashboard.TbDashboardService; import org.thingsboard.server.service.security.model.SecurityUser; @@ -74,6 +75,8 @@ import static org.thingsboard.server.controller.ControllerConstants.EDGE_ID; import static org.thingsboard.server.controller.ControllerConstants.EDGE_ID_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.EDGE_UNASSIGN_ASYNC_FIRST_STEP_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.INLINE_IMAGES; +import static org.thingsboard.server.controller.ControllerConstants.INLINE_IMAGES_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS; import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; @@ -94,6 +97,7 @@ import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LI public class DashboardController extends BaseController { private final TbDashboardService tbDashboardService; + private final ImageService imageService; public static final String DASHBOARD_ID = "dashboardId"; private static final String HOME_DASHBOARD_ID = "homeDashboardId"; @@ -153,10 +157,16 @@ public class DashboardController extends BaseController { @ResponseBody public Dashboard getDashboardById( @ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION) - @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException { + @PathVariable(DASHBOARD_ID) String strDashboardId, + @ApiParam(value = INLINE_IMAGES_DESCRIPTION) + @RequestParam(value = INLINE_IMAGES, required = false) boolean inlineImages) throws ThingsboardException { checkParameter(DASHBOARD_ID, strDashboardId); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); - return checkDashboardId(dashboardId, Operation.READ); + var result = checkDashboardId(dashboardId, Operation.READ); + if (inlineImages) { + imageService.inlineImages(result); + } + return result; } @ApiOperation(value = "Create Or Update Dashboard (saveDashboard)", diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceConnectivityController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceConnectivityController.java new file mode 100644 index 0000000000..d93dc0cc6d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceConnectivityController.java @@ -0,0 +1,138 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.controller; + +import com.fasterxml.jackson.databind.JsonNode; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.dao.device.DeviceConnectivityService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.permission.Operation; +import org.thingsboard.server.service.security.system.SystemSecurityService; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.net.URISyntaxException; + +import static org.thingsboard.server.controller.ControllerConstants.DEVICE_ID; +import static org.thingsboard.server.controller.ControllerConstants.DEVICE_ID_PARAM_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.PROTOCOL; +import static org.thingsboard.server.controller.ControllerConstants.PROTOCOL_PARAM_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.CA_ROOT_CERT_PEM; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.DOCKER_COMPOSE_YML; + +@RestController +@TbCoreComponent +@RequestMapping("/api") +@RequiredArgsConstructor +@Slf4j +public class DeviceConnectivityController extends BaseController { + + private final DeviceConnectivityService deviceConnectivityService; + private final SystemSecurityService systemSecurityService; + + @ApiOperation(value = "Get commands to publish device telemetry (getDevicePublishTelemetryCommands)", + notes = "Fetch the list of commands to publish device telemetry based on device profile " + + "If the user has the authority of 'Tenant Administrator', the server checks that the device is owned by the same tenant. " + + "If the user has the authority of 'Customer User', the server checks that the device is assigned to the same customer. " + + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK", + examples = @io.swagger.annotations.Example( + value = { + @io.swagger.annotations.ExampleProperty( + mediaType = "application/json", + value = "{\"http\":\"curl -v -X POST http://localhost:8080/api/v1/0ySs4FTOn5WU15XLmal8/telemetry --header Content-Type:application/json --data {temperature:25}\"," + + "\"mqtt\":\"mosquitto_pub -d -q 1 -h localhost -t v1/devices/me/telemetry -i myClient1 -u myUsername1 -P myPassword -m {temperature:25}\"," + + "\"coap\":\"coap-client -m POST coap://localhost:5683/api/v1/0ySs4FTOn5WU15XLmal8/telemetry -t json -e {temperature:25}\"}")}))}) + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/device-connectivity/{deviceId}", method = RequestMethod.GET) + @ResponseBody + public JsonNode getDevicePublishTelemetryCommands(@ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION) + @PathVariable(DEVICE_ID) String strDeviceId, HttpServletRequest request) throws ThingsboardException, URISyntaxException { + checkParameter(DEVICE_ID, strDeviceId); + DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); + Device device = checkDeviceId(deviceId, Operation.READ_CREDENTIALS); + + String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); + return deviceConnectivityService.findDevicePublishTelemetryCommands(baseUrl, device); + } + + @ApiOperation(value = "Download server certificate using file path defined in device.connectivity properties (downloadServerCertificate)", notes = "Download server certificate.") + @RequestMapping(value = "/device-connectivity/{protocol}/certificate/download", method = RequestMethod.GET) + @ResponseBody + public ResponseEntity downloadServerCertificate(@ApiParam(value = PROTOCOL_PARAM_DESCRIPTION) + @PathVariable(PROTOCOL) String protocol) throws ThingsboardException, IOException { + checkParameter(PROTOCOL, protocol); + var pemCert = + checkNotNull(deviceConnectivityService.getPemCertFile(protocol), protocol + " pem cert file is not found!"); + + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + CA_ROOT_CERT_PEM) + .header("x-filename", CA_ROOT_CERT_PEM) + .contentLength(pemCert.contentLength()) + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(pemCert); + } + + @ApiOperation(value = "Download generated docker-compose.yml file for gateway (downloadGatewayDockerCompose)", notes = "Download generated docker-compose.yml for gateway.") + @RequestMapping(value = "/device-connectivity/gateway-launch/{deviceId}/docker-compose/download", method = RequestMethod.GET) + @ResponseBody + public ResponseEntity downloadGatewayDockerCompose(@ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION) + @PathVariable(DEVICE_ID) String strDeviceId, HttpServletRequest request) throws ThingsboardException, URISyntaxException, IOException { + checkParameter(DEVICE_ID, strDeviceId); + DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); + Device device = checkDeviceId(deviceId, Operation.READ_CREDENTIALS); + + if (!checkIsGateway(device)) { + throw new ThingsboardException("The device must be a gateway!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); + } + + String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); + var dockerCompose = checkNotNull(deviceConnectivityService.createGatewayDockerComposeFile(baseUrl, device), "Failed to create docker-compose.yml file!"); + + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + DOCKER_COMPOSE_YML) + .header("x-filename", DOCKER_COMPOSE_YML) + .contentLength(dockerCompose.contentLength()) + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(dockerCompose); + } + + private static boolean checkIsGateway(Device device) { + return device.getAdditionalInfo().has(DataConstants.GATEWAY_PARAMETER) && + device.getAdditionalInfo().get(DataConstants.GATEWAY_PARAMETER).asBoolean(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index bb34f6d5b2..1761056051 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -75,6 +75,7 @@ import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import javax.annotation.Nullable; +import javax.validation.Valid; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -93,7 +94,15 @@ import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFI import static org.thingsboard.server.controller.ControllerConstants.DEVICE_SORT_PROPERTY_ALLOWABLE_VALUES; import static org.thingsboard.server.controller.ControllerConstants.DEVICE_TEXT_SEARCH_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.DEVICE_TYPE_DESCRIPTION; -import static org.thingsboard.server.controller.ControllerConstants.DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_DESCRIPTION_MARKDOWN; +import static org.thingsboard.server.controller.ControllerConstants.DEVICE_UPDATE_CREDENTIALS_PARAM_ACCESS_TOKEN_DESCRIPTION_MARKDOWN; +import static org.thingsboard.server.controller.ControllerConstants.DEVICE_UPDATE_CREDENTIALS_PARAM_LVM2M_RPK_DESCRIPTION_MARKDOWN; +import static org.thingsboard.server.controller.ControllerConstants.DEVICE_UPDATE_CREDENTIALS_PARAM_MQTT_BASIC_DESCRIPTION_MARKDOWN; +import static org.thingsboard.server.controller.ControllerConstants.DEVICE_UPDATE_CREDENTIALS_PARAM_X509_CERTIFICATE_DESCRIPTION_MARKDOWN; +import static org.thingsboard.server.controller.ControllerConstants.DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_ACCESS_TOKEN_DEFAULT_DESCRIPTION_MARKDOWN; +import static org.thingsboard.server.controller.ControllerConstants.DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_ACCESS_TOKEN_DESCRIPTION_MARKDOWN; +import static org.thingsboard.server.controller.ControllerConstants.DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_LVM2M_RPK_DESCRIPTION_MARKDOWN; +import static org.thingsboard.server.controller.ControllerConstants.DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_MQTT_BASIC_DESCRIPTION_MARKDOWN; +import static org.thingsboard.server.controller.ControllerConstants.DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_X509_CERTIFICATE_DESCRIPTION_MARKDOWN; import static org.thingsboard.server.controller.ControllerConstants.EDGE_ASSIGN_ASYNC_FIRST_STEP_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.EDGE_ID_PARAM_DESCRIPTION; @@ -182,18 +191,29 @@ public class DeviceController extends BaseController { @ApiOperation(value = "Create Device (saveDevice) with credentials ", notes = "Create or update the Device. When creating device, platform generates Device Id as " + UUID_WIKI_LINK + - "Requires to provide the Device Credentials object as well. Useful to create device and credentials in one request. " + - "You may find the example of LwM2M device and RPK credentials below: \n\n" + - DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_DESCRIPTION_MARKDOWN + + "Requires to provide the Device Credentials object as well as an existing device profile ID or use \"default\".\n" + + "You may find the example of device with different type of credentials below: \n\n" + + "- Credentials type: \"Access token\" with device profile ID below: \n\n" + + DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_ACCESS_TOKEN_DESCRIPTION_MARKDOWN + "\n\n" + + "- Credentials type: \"Access token\" with device profile default below: \n\n" + + DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_ACCESS_TOKEN_DEFAULT_DESCRIPTION_MARKDOWN + "\n\n" + + "- Credentials type: \"X509\" with device profile ID below: \n\n" + + "Note: credentialsId - format Sha3Hash, certificateValue - format PEM (with \"--BEGIN CERTIFICATE----\" and -\"----END CERTIFICATE-\").\n\n" + + DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_X509_CERTIFICATE_DESCRIPTION_MARKDOWN + "\n\n" + + "- Credentials type: \"MQTT_BASIC\" with device profile ID below: \n\n" + + DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_MQTT_BASIC_DESCRIPTION_MARKDOWN + "\n\n" + + "- You may find the example of LwM2M device and RPK credentials below: \n\n" + + "Note: LwM2M device - only existing device profile ID (Transport configuration -> Transport type: \"LWM2M\".\n\n" + + DEVICE_WITH_DEVICE_CREDENTIALS_PARAM_LVM2M_RPK_DESCRIPTION_MARKDOWN + "\n\n" + "Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Device entity. " + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/device-with-credentials", method = RequestMethod.POST) @ResponseBody public Device saveDeviceWithCredentials(@ApiParam(value = "The JSON object with device and credentials. See method description above for example.") - @RequestBody SaveDeviceWithCredentialsRequest deviceAndCredentials) throws ThingsboardException { - Device device = checkNotNull(deviceAndCredentials.getDevice()); - DeviceCredentials credentials = checkNotNull(deviceAndCredentials.getCredentials()); + @Valid @RequestBody SaveDeviceWithCredentialsRequest deviceAndCredentials) throws ThingsboardException { + Device device = deviceAndCredentials.getDevice(); + DeviceCredentials credentials = deviceAndCredentials.getCredentials(); device.setTenantId(getCurrentUser().getTenantId()); checkEntity(device.getId(), device, Resource.DEVICE); return tbDeviceService.saveDeviceWithCredentials(device, credentials, getCurrentUser()); @@ -209,7 +229,7 @@ public class DeviceController extends BaseController { checkParameter(DEVICE_ID, strDeviceId); DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); Device device = checkDeviceId(deviceId, Operation.DELETE); - tbDeviceService.delete(device, getCurrentUser()).get(); + tbDeviceService.delete(device, getCurrentUser()); } @ApiOperation(value = "Assign device to customer (assignDeviceToCustomer)", @@ -277,10 +297,27 @@ public class DeviceController extends BaseController { return tbDeviceService.getDeviceCredentialsByDeviceId(device, getCurrentUser()); } - @ApiOperation(value = "Update device credentials (updateDeviceCredentials)", notes = "During device creation, platform generates random 'ACCESS_TOKEN' credentials. " + - "Use this method to update the device credentials. First use 'getDeviceCredentialsByDeviceId' to get the credentials id and value. " + - "Then use current method to update the credentials type and value. It is not possible to create multiple device credentials for the same device. " + - "The structure of device credentials id and value is simple for the 'ACCESS_TOKEN' but is much more complex for the 'MQTT_BASIC' or 'LWM2M_CREDENTIALS'." + TENANT_AUTHORITY_PARAGRAPH) + @ApiOperation(value = "Update device credentials (updateDeviceCredentials)", + notes = "During device creation, platform generates random 'ACCESS_TOKEN' credentials. \" +\n" + + "Use this method to update the device credentials. First use 'getDeviceCredentialsByDeviceId' to get the credentials id and value.\n" + + "Then use current method to update the credentials type and value. It is not possible to create multiple device credentials for the same device.\n" + + "The structure of device credentials id and value is simple for the 'ACCESS_TOKEN' but is much more complex for the 'MQTT_BASIC' or 'LWM2M_CREDENTIALS'.\n" + + "You may find the example of device with different type of credentials below: \n\n" + + "- Credentials type: \"Access token\" with device ID and with device ID below: \n\n" + + DEVICE_UPDATE_CREDENTIALS_PARAM_ACCESS_TOKEN_DESCRIPTION_MARKDOWN + "\n\n" + + "- Credentials type: \"X509\" with device profile ID below: \n\n" + + "Note: credentialsId - format Sha3Hash, certificateValue - format PEM (with \"--BEGIN CERTIFICATE----\" and -\"----END CERTIFICATE-\").\n\n" + + DEVICE_UPDATE_CREDENTIALS_PARAM_X509_CERTIFICATE_DESCRIPTION_MARKDOWN + "\n\n" + + "- Credentials type: \"MQTT_BASIC\" with device profile ID below: \n\n" + + DEVICE_UPDATE_CREDENTIALS_PARAM_MQTT_BASIC_DESCRIPTION_MARKDOWN + "\n\n" + + "- You may find the example of LwM2M device and RPK credentials below: \n\n" + + "Note: LwM2M device - only existing device profile ID (Transport configuration -> Transport type: \"LWM2M\".\n\n" + + DEVICE_UPDATE_CREDENTIALS_PARAM_LVM2M_RPK_DESCRIPTION_MARKDOWN + "\n\n" + + "Update to real value:\n" + + " - 'id' (this is id of Device Credentials -> \"Get Device Credentials (getDeviceCredentialsByDeviceId)\",\n" + + " - 'deviceId.id' (this is id of Device).\n" + + "Remove 'tenantId' and optionally 'customerId' from the request body example (below) to create new Device entity." + + TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequestMapping(value = "/device/credentials", method = RequestMethod.POST) @ResponseBody @@ -497,11 +534,12 @@ public class DeviceController extends BaseController { } @ApiOperation(value = "Get Device Types (getDeviceTypes)", - notes = "Returns a set of unique device profile names based on devices that are either owned by the tenant or assigned to the customer which user is performing the request." - + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) + notes = "Deprecated. See 'getDeviceProfileNames' API from Device Profile Controller instead." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, + produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/device/types", method = RequestMethod.GET) @ResponseBody + @Deprecated(since = "3.6.2") public List getDeviceTypes() throws ThingsboardException, ExecutionException, InterruptedException { SecurityUser user = getCurrentUser(); TenantId tenantId = user.getTenantId(); diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java index c888d67252..c16e201bad 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java @@ -32,14 +32,18 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileInfo; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.device.profile.TbDeviceProfileService; +import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; @@ -52,6 +56,8 @@ import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFI import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFILE_INFO_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES; import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFILE_TEXT_SEARCH_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.INLINE_IMAGES; +import static org.thingsboard.server.controller.ControllerConstants.INLINE_IMAGES_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.NEW_LINE; import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS; import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; @@ -72,6 +78,7 @@ import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LI public class DeviceProfileController extends BaseController { private final TbDeviceProfileService tbDeviceProfileService; + private final ImageService imageService; @Autowired private TimeseriesService timeseriesService; @@ -85,10 +92,16 @@ public class DeviceProfileController extends BaseController { @ResponseBody public DeviceProfile getDeviceProfileById( @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION) - @PathVariable(DEVICE_PROFILE_ID) String strDeviceProfileId) throws ThingsboardException { + @PathVariable(DEVICE_PROFILE_ID) String strDeviceProfileId, + @ApiParam(value = INLINE_IMAGES_DESCRIPTION) + @RequestParam(value = INLINE_IMAGES, required = false) boolean inlineImages) throws ThingsboardException { checkParameter(DEVICE_PROFILE_ID, strDeviceProfileId); DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId)); - return checkDeviceProfileId(deviceProfileId, Operation.READ); + var result = checkDeviceProfileId(deviceProfileId, Operation.READ); + if (inlineImages) { + imageService.inlineImage(result); + } + return result; } @ApiOperation(value = "Get Device Profile Info (getDeviceProfileInfoById)", @@ -263,4 +276,19 @@ public class DeviceProfileController extends BaseController { PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); return checkNotNull(deviceProfileService.findDeviceProfileInfos(getTenantId(), pageLink, transportType)); } + + @ApiOperation(value = "Get Device Profile names (getDeviceProfileNames)", + notes = "Returns a set of unique device profile names owned by the tenant." + + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/deviceProfile/names", method = RequestMethod.GET) + @ResponseBody + public List getDeviceProfileNames( + @ApiParam(value = "Flag indicating whether to retrieve exclusively the names of device profiles that are referenced by tenant's devices.") + @RequestParam(value = "activeOnly", required = false, defaultValue = "false") boolean activeOnly) throws ThingsboardException { + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + return checkNotNull(deviceProfileService.findDeviceProfileNamesByTenantId(tenantId, activeOnly)); + } + } diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java index 9a021643cc..b780789b26 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java @@ -39,7 +39,7 @@ import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeInfo; -import org.thingsboard.server.common.data.edge.EdgeInstallInstructions; +import org.thingsboard.server.common.data.edge.EdgeInstructions; import org.thingsboard.server.common.data.edge.EdgeSearchQuery; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -59,6 +59,9 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.edge.EdgeBulkImportService; +import org.thingsboard.server.service.edge.instructions.EdgeInstallInstructionsService; +import org.thingsboard.server.service.edge.instructions.EdgeUpgradeInstructionsService; +import org.thingsboard.server.service.edge.rpc.EdgeRpcService; import org.thingsboard.server.service.entitiy.edge.TbEdgeService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; @@ -67,6 +70,7 @@ import org.thingsboard.server.service.security.permission.Resource; import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; @@ -94,8 +98,12 @@ import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LI @RequestMapping("/api") @RequiredArgsConstructor public class EdgeController extends BaseController { + private final EdgeBulkImportService edgeBulkImportService; private final TbEdgeService tbEdgeService; + private final Optional edgeRpcServiceOpt; + private final Optional edgeInstallServiceOpt; + private final Optional edgeUpgradeServiceOpt; public static final String EDGE_ID = "edgeId"; public static final String EDGE_SECURITY_CHECK = "If the user has the authority of 'Tenant Administrator', the server checks that the edge is owned by the same tenant. " + @@ -497,13 +505,13 @@ public class EdgeController extends BaseController { @PathVariable("edgeId") String strEdgeId) throws ThingsboardException { checkParameter("edgeId", strEdgeId); final DeferredResult response = new DeferredResult<>(); - if (isEdgesEnabled()) { + if (isEdgesEnabled() && edgeRpcServiceOpt.isPresent()) { EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); edgeId = checkNotNull(edgeId); SecurityUser user = getCurrentUser(); TenantId tenantId = user.getTenantId(); ToEdgeSyncRequest request = new ToEdgeSyncRequest(UUID.randomUUID(), tenantId, edgeId); - edgeRpcService.processSyncRequest(request, fromEdgeSyncResponse -> reply(response, fromEdgeSyncResponse)); + edgeRpcServiceOpt.get().processSyncRequest(request, fromEdgeSyncResponse -> reply(response, fromEdgeSyncResponse)); } else { throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL); } @@ -547,19 +555,61 @@ public class EdgeController extends BaseController { return edgeBulkImportService.processBulkImport(request, user); } - @ApiOperation(value = "Get Edge Docker Install Instructions (getEdgeDockerInstallInstructions)", - notes = "Get a docker install instructions for provided edge id." + TENANT_AUTHORITY_PARAGRAPH, + @ApiOperation(value = "Get Edge Install Instructions (getEdgeInstallInstructions)", + notes = "Get an install instructions for provided edge id." + TENANT_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/edge/instructions/{edgeId}", method = RequestMethod.GET) + @RequestMapping(value = "/edge/instructions/install/{edgeId}/{method}", method = RequestMethod.GET) @ResponseBody - public EdgeInstallInstructions getEdgeDockerInstallInstructions( + public EdgeInstructions getEdgeInstallInstructions( @ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("edgeId") String strEdgeId, + @ApiParam(value = "Installation method ('docker', 'ubuntu' or 'centos')", allowableValues = "docker, ubuntu, centos") + @PathVariable("method") String installationMethod, HttpServletRequest request) throws ThingsboardException { - EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); - edgeId = checkNotNull(edgeId); - Edge edge = checkEdgeId(edgeId, Operation.READ); - return checkNotNull(edgeInstallService.getDockerInstallInstructions(getTenantId(), edge, request)); + if (isEdgesEnabled() && edgeInstallServiceOpt.isPresent()) { + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + edgeId = checkNotNull(edgeId); + Edge edge = checkEdgeId(edgeId, Operation.READ); + return checkNotNull(edgeInstallServiceOpt.get().getInstallInstructions(edge, installationMethod, request)); + } else { + throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL); + } + } + + @ApiOperation(value = "Get Edge Upgrade Instructions (getEdgeUpgradeInstructions)", + notes = "Get an upgrade instructions for provided edge version." + TENANT_AUTHORITY_PARAGRAPH, + produces = MediaType.APPLICATION_JSON_VALUE) + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/edge/instructions/upgrade/{edgeVersion}/{method}", method = RequestMethod.GET) + @ResponseBody + public EdgeInstructions getEdgeUpgradeInstructions( + @ApiParam(value = "Edge version", required = true) + @PathVariable("edgeVersion") String edgeVersion, + @ApiParam(value = "Upgrade method ('docker', 'ubuntu' or 'centos')", allowableValues = "docker, ubuntu, centos") + @PathVariable("method") String method) throws Exception { + if (isEdgesEnabled() && edgeUpgradeServiceOpt.isPresent()) { + return checkNotNull(edgeUpgradeServiceOpt.get().getUpgradeInstructions(edgeVersion, method)); + } else { + throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL); + } + } + + @ApiOperation(value = "Is edge upgrade enabled (isEdgeUpgradeAvailable)", + notes = "Returns 'true' if upgrade available for connected edge, 'false' - otherwise.") + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/edge/{edgeId}/upgrade/available", method = RequestMethod.GET) + @ResponseBody + public boolean isEdgeUpgradeAvailable( + @ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true) + @PathVariable("edgeId") String strEdgeId) throws Exception { + if (isEdgesEnabled() && edgeUpgradeServiceOpt.isPresent()) { + EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); + edgeId = checkNotNull(edgeId); + Edge edge = checkEdgeId(edgeId, Operation.READ); + return edgeUpgradeServiceOpt.get().isUpgradeAvailable(edge.getTenantId(), edge.getId()); + } else { + throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL); + } } } diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java index fc85f439e0..386b1eab01 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java @@ -85,6 +85,6 @@ public class EdgeEventController extends BaseController { EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); checkEdgeId(edgeId, Operation.READ); TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); - return checkNotNull(edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, false)); + return checkNotNull(edgeEventService.findEdgeEvents(tenantId, edgeId, 0L, null, pageLink)); } } diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java index b5c8e18ed2..bfd874186a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java @@ -21,6 +21,7 @@ import com.google.common.util.concurrent.MoreExecutors; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -83,6 +84,8 @@ public class EntitiesVersionControlController extends BaseController { private final EntitiesVersionControlService versionControlService; + @Value("${queue.vc.request-timeout:180000}") + private int vcRequestTimeout; @ApiOperation(value = "Save entities version (saveEntitiesVersion)", notes = "" + "Creates a new version of entities (or a single entity) by request.\n" + @@ -515,4 +518,9 @@ public class EntitiesVersionControlController extends BaseController { }, MoreExecutors.directExecutor())); } + @Override + protected DeferredResult wrapFuture(ListenableFuture future) { + return wrapFuture(future, vcRequestTimeout); + } + } diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java index 08448f1d28..e1e89734b9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java @@ -117,8 +117,8 @@ public class EntityRelationController extends BaseController { tbEntityRelationService.delete(getTenantId(), getCurrentUser().getCustomerId(), relation, getCurrentUser()); } - @ApiOperation(value = "Delete Relations (deleteRelations)", - notes = "Deletes all the relation (both 'from' and 'to' direction) for the specified entity. " + + @ApiOperation(value = "Delete common relations (deleteCommonRelations)", + notes = "Deletes all the relations ('from' and 'to' direction) for the specified entity and relation type group: 'COMMON'. " + SECURITY_CHECKS_ENTITY_DESCRIPTION) @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/relations", method = RequestMethod.DELETE, params = {"entityId", "entityType"}) @@ -129,7 +129,7 @@ public class EntityRelationController extends BaseController { checkParameter("entityType", strType); EntityId entityId = EntityIdFactory.getByTypeAndId(strType, strId); checkEntityId(entityId, Operation.WRITE); - tbEntityRelationService.deleteRelations(getTenantId(), getCurrentUser().getCustomerId(), entityId, getCurrentUser()); + tbEntityRelationService.deleteCommonRelations(getTenantId(), getCurrentUser().getCustomerId(), entityId, getCurrentUser()); } @ApiOperation(value = "Get Relation (getRelation)", diff --git a/application/src/main/java/org/thingsboard/server/controller/ImageController.java b/application/src/main/java/org/thingsboard/server/controller/ImageController.java new file mode 100644 index 0000000000..199e80dc64 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/ImageController.java @@ -0,0 +1,346 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.controller; + +import io.swagger.annotations.ApiParam; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.http.CacheControl; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.util.Base64Utils; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import org.thingsboard.server.common.data.ImageDescriptor; +import org.thingsboard.server.common.data.ImageExportData; +import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.TbImageDeleteResult; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.util.ThrowingSupplier; +import org.thingsboard.server.dao.resource.ImageCacheKey; +import org.thingsboard.server.dao.resource.ImageService; +import org.thingsboard.server.dao.service.validator.ResourceDataValidator; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.resource.TbImageService; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.permission.Operation; +import org.thingsboard.server.service.security.permission.Resource; + +import java.util.concurrent.TimeUnit; + +import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_INCLUDE_SYSTEM_IMAGES_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES; +import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_TEXT_SEARCH_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES; +import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION; + +@Slf4j +@RestController +@TbCoreComponent +@RequiredArgsConstructor +public class ImageController extends BaseController { + + private final ImageService imageService; + private final TbImageService tbImageService; + private final ResourceDataValidator resourceValidator; + + @Value("${cache.image.systemImagesBrowserTtlInMinutes:0}") + private int systemImagesBrowserTtlInMinutes; + @Value("${cache.image.tenantImagesBrowserTtlInMinutes:0}") + private int tenantImagesBrowserTtlInMinutes; + + private static final String IMAGE_URL = "/api/images/{type}/{key}"; + private static final String SYSTEM_IMAGE = "system"; + private static final String TENANT_IMAGE = "tenant"; + + private static final String IMAGE_TYPE_PARAM_DESCRIPTION = "Type of the image: tenant or system"; + private static final String IMAGE_TYPE_PARAM_ALLOWABLE_VALUES = "tenant, system"; + private static final String IMAGE_KEY_PARAM_DESCRIPTION = "Image resource key, for example thermostats_dashboard_background.jpeg"; + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PostMapping("/api/image") + public TbResourceInfo uploadImage(@RequestPart MultipartFile file, + @RequestPart(required = false) String title) throws Exception { + SecurityUser user = getCurrentUser(); + TbResource image = new TbResource(); + image.setTenantId(user.getTenantId()); + accessControlService.checkPermission(user, Resource.TB_RESOURCE, Operation.CREATE, null, image); + resourceValidator.validateResourceSize(user.getTenantId(), null, file.getSize()); + + image.setFileName(file.getOriginalFilename()); + if (StringUtils.isNotEmpty(title)) { + image.setTitle(title); + } else { + image.setTitle(file.getOriginalFilename()); + } + image.setResourceType(ResourceType.IMAGE); + ImageDescriptor descriptor = new ImageDescriptor(); + descriptor.setMediaType(file.getContentType()); + image.setDescriptorValue(descriptor); + image.setData(file.getBytes()); + image.setPublic(true); + return tbImageService.save(image, user); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PutMapping(IMAGE_URL) + public TbResourceInfo updateImage(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true) + @PathVariable String type, + @ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true) + @PathVariable String key, + @RequestPart MultipartFile file) throws Exception { + TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.WRITE); + resourceValidator.validateResourceSize(getTenantId(), imageInfo.getId(), file.getSize()); + + TbResource image = new TbResource(imageInfo); + image.setData(file.getBytes()); + image.setFileName(file.getOriginalFilename()); + image.updateDescriptor(ImageDescriptor.class, descriptor -> { + descriptor.setMediaType(file.getContentType()); + return descriptor; + }); + return tbImageService.save(image, getCurrentUser()); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PutMapping(IMAGE_URL + "/info") + public TbResourceInfo updateImageInfo(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true) + @PathVariable String type, + @ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true) + @PathVariable String key, + @RequestBody TbResourceInfo request) throws ThingsboardException { + TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.WRITE); + TbResourceInfo newImageInfo = new TbResourceInfo(imageInfo); + newImageInfo.setTitle(request.getTitle()); + return tbImageService.save(newImageInfo, imageInfo, getCurrentUser()); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PutMapping(IMAGE_URL + "/public/{isPublic}") + public TbResourceInfo updateImagePublicStatus(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true) + @PathVariable String type, + @ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true) + @PathVariable String key, + @PathVariable boolean isPublic) throws ThingsboardException { + TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.WRITE); + TbResourceInfo newImageInfo = new TbResourceInfo(imageInfo); + newImageInfo.setPublic(isPublic); + return tbImageService.save(newImageInfo, imageInfo, getCurrentUser()); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @GetMapping(value = IMAGE_URL, produces = "image/*") + public ResponseEntity downloadImage(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true) + @PathVariable String type, + @ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true) + @PathVariable String key, + @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception { + return downloadIfChanged(type, key, etag, false); + } + + @GetMapping(value = "/api/images/public/{publicResourceKey}", produces = "image/*") + public ResponseEntity downloadPublicImage(@PathVariable String publicResourceKey, + @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception { + ImageCacheKey cacheKey = ImageCacheKey.forPublicImage(publicResourceKey); + return downloadIfChanged(cacheKey, etag, () -> imageService.getPublicImageInfoByKey(publicResourceKey)); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @GetMapping(value = IMAGE_URL + "/export") + public ImageExportData exportImage(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true) + @PathVariable String type, + @ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true) + @PathVariable String key) throws Exception { + TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.READ); + ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class); + byte[] data = imageService.getImageData(imageInfo.getTenantId(), imageInfo.getId()); + return ImageExportData.builder() + .mediaType(descriptor.getMediaType()) + .fileName(imageInfo.getFileName()) + .title(imageInfo.getTitle()) + .resourceKey(imageInfo.getResourceKey()) + .isPublic(imageInfo.isPublic()) + .publicResourceKey(imageInfo.getPublicResourceKey()) + .data(Base64Utils.encodeToString(data)) + .build(); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PutMapping("/api/image/import") + public TbResourceInfo importImage(@RequestBody ImageExportData imageData) throws Exception { + SecurityUser user = getCurrentUser(); + TbResource image = new TbResource(); + image.setTenantId(user.getTenantId()); + accessControlService.checkPermission(user, Resource.TB_RESOURCE, Operation.CREATE, null, image); + + image.setFileName(imageData.getFileName()); + if (StringUtils.isNotEmpty(imageData.getTitle())) { + image.setTitle(imageData.getTitle()); + } else { + image.setTitle(imageData.getFileName()); + } + image.setResourceType(ResourceType.IMAGE); + image.setResourceKey(imageData.getResourceKey()); + image.setPublic(imageData.isPublic()); + image.setPublicResourceKey(imageData.getPublicResourceKey()); + ImageDescriptor descriptor = new ImageDescriptor(); + descriptor.setMediaType(imageData.getMediaType()); + image.setDescriptorValue(descriptor); + image.setData(Base64Utils.decodeFromString(imageData.getData())); + return tbImageService.save(image, user); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @GetMapping(value = IMAGE_URL + "/preview", produces = "image/png") + public ResponseEntity downloadImagePreview(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true) + @PathVariable String type, + @ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true) + @PathVariable String key, + @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception { + return downloadIfChanged(type, key, etag, true); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @GetMapping(IMAGE_URL + "/info") + public TbResourceInfo getImageInfo(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true) + @PathVariable String type, + @ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true) + @PathVariable String key) throws ThingsboardException { + return checkImageInfo(type, key, Operation.READ); + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @GetMapping("/api/images") + public PageData getImages(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) + @RequestParam int pageSize, + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) + @RequestParam int page, + @ApiParam(value = RESOURCE_INCLUDE_SYSTEM_IMAGES_DESCRIPTION) + @RequestParam(required = false) boolean includeSystemImages, + @ApiParam(value = RESOURCE_TEXT_SEARCH_DESCRIPTION) + @RequestParam(required = false) String textSearch, + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES) + @RequestParam(required = false) String sortProperty, + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) + @RequestParam(required = false) String sortOrder) throws ThingsboardException { + // PE: generic permission + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + TenantId tenantId = getTenantId(); + if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN || !includeSystemImages) { + return checkNotNull(imageService.getImagesByTenantId(tenantId, pageLink)); + } else { + return checkNotNull(imageService.getAllImagesByTenantId(tenantId, pageLink)); + } + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @DeleteMapping(IMAGE_URL) + public ResponseEntity deleteImage(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true) + @PathVariable String type, + @ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true) + @PathVariable String key, + @RequestParam(name = "force", required = false) boolean force) throws ThingsboardException { + TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.DELETE); + TbImageDeleteResult result = tbImageService.delete(imageInfo, getCurrentUser(), force); + return (result.isSuccess() ? ResponseEntity.ok() : ResponseEntity.badRequest()).body(result); + } + + private ResponseEntity downloadIfChanged(String type, String key, String etag, boolean preview) throws Exception { + ImageCacheKey cacheKey = ImageCacheKey.forImage(getTenantId(type), key, preview); + return downloadIfChanged(cacheKey, etag, () -> checkImageInfo(type, key, Operation.READ)); + } + + private ResponseEntity downloadIfChanged(ImageCacheKey cacheKey, String etag, ThrowingSupplier imageInfoSupplier) throws Exception { + if (StringUtils.isNotEmpty(etag)) { + etag = StringUtils.remove(etag, '\"'); // etag is wrapped in double quotes due to HTTP specification + if (etag.equals(tbImageService.getETag(cacheKey))) { + return ResponseEntity.status(HttpStatus.NOT_MODIFIED).build(); + } + } + + TbResourceInfo imageInfo = checkNotNull(imageInfoSupplier.get()); + String fileName = imageInfo.getFileName(); + ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class); + byte[] data; + if (cacheKey.isPreview()) { + descriptor = descriptor.getPreviewDescriptor(); + data = imageService.getImagePreview(imageInfo.getTenantId(), imageInfo.getId()); + } else { + data = imageService.getImageData(imageInfo.getTenantId(), imageInfo.getId()); + } + tbImageService.putETag(cacheKey, descriptor.getEtag()); + var result = ResponseEntity.ok() + .header("Content-Type", descriptor.getMediaType()) + .contentLength(data.length) + .eTag(descriptor.getEtag()); + if (!cacheKey.isPublic()) { + result + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + fileName) + .header("x-filename", fileName); + } + if (systemImagesBrowserTtlInMinutes > 0 && imageInfo.getTenantId().isSysTenantId()) { + result.cacheControl(CacheControl.maxAge(systemImagesBrowserTtlInMinutes, TimeUnit.MINUTES)); + } else if (tenantImagesBrowserTtlInMinutes > 0 && !imageInfo.getTenantId().isSysTenantId()) { + result.cacheControl(CacheControl.maxAge(tenantImagesBrowserTtlInMinutes, TimeUnit.MINUTES)); + } else { + result.cacheControl(CacheControl.noCache()); + } + return result.body(new ByteArrayResource(data)); + } + + private TbResourceInfo checkImageInfo(String imageType, String key, Operation operation) throws ThingsboardException { + TenantId tenantId = getTenantId(imageType); + TbResourceInfo imageInfo = imageService.getImageInfoByTenantIdAndKey(tenantId, key); + checkEntity(getCurrentUser(), checkNotNull(imageInfo), operation); + return imageInfo; + } + + private TenantId getTenantId(String imageType) throws ThingsboardException { + TenantId tenantId; + if (imageType.equals(TENANT_IMAGE)) { + tenantId = getTenantId(); + } else if (imageType.equals(SYSTEM_IMAGE)) { + tenantId = TenantId.SYS_TENANT_ID; + } else { + throw new IllegalArgumentException("Invalid image URL"); + } + return tenantId; + } + +} \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java index 0f4a288e33..24dc41f199 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java @@ -44,6 +44,8 @@ import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.notification.NotificationRequestInfo; import org.thingsboard.server.common.data.notification.NotificationRequestPreview; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; +import org.thingsboard.server.common.data.notification.settings.UserNotificationSettings; +import org.thingsboard.server.common.data.notification.targets.MicrosoftTeamsNotificationTargetConfig; import org.thingsboard.server.common.data.notification.targets.NotificationRecipient; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.NotificationTargetType; @@ -269,6 +271,7 @@ public class NotificationController extends BaseController { @ApiParam(value = "Amount of the recipients to show in preview") @RequestParam(defaultValue = "20") int recipientsPreviewSize, @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { + // PE: generic permission NotificationTemplate template; if (request.getTemplateId() != null) { template = checkEntityId(request.getTemplateId(), notificationTemplateService::findNotificationTemplateById, Operation.READ); @@ -294,15 +297,27 @@ public class NotificationController extends BaseController { int recipientsCount; List recipientsPart; NotificationTargetType targetType = target.getConfiguration().getType(); - if (targetType == NotificationTargetType.PLATFORM_USERS) { - PageData recipients = notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(), - (PlatformUsersNotificationTargetConfig) target.getConfiguration(), new PageLink(recipientsPreviewSize, 0, null, - new SortOrder("createdTime", SortOrder.Direction.DESC))); - recipientsCount = (int) recipients.getTotalElements(); - recipientsPart = recipients.getData().stream().map(r -> (NotificationRecipient) r).collect(Collectors.toList()); - } else { - recipientsCount = 1; - recipientsPart = List.of(((SlackNotificationTargetConfig) target.getConfiguration()).getConversation()); + switch (targetType) { + case PLATFORM_USERS: { + PageData recipients = notificationTargetService.findRecipientsForNotificationTargetConfig(user.getTenantId(), + (PlatformUsersNotificationTargetConfig) target.getConfiguration(), new PageLink(recipientsPreviewSize, 0, null, + SortOrder.BY_CREATED_TIME_DESC)); + recipientsCount = (int) recipients.getTotalElements(); + recipientsPart = recipients.getData().stream().map(r -> (NotificationRecipient) r).collect(Collectors.toList()); + break; + } + case SLACK: { + recipientsCount = 1; + recipientsPart = List.of(((SlackNotificationTargetConfig) target.getConfiguration()).getConversation()); + break; + } + case MICROSOFT_TEAMS: { + recipientsCount = 1; + recipientsPart = List.of(((MicrosoftTeamsNotificationTargetConfig) target.getConfiguration())); + break; + } + default: + throw new IllegalArgumentException("Target type " + targetType + " not supported"); } firstRecipient.putIfAbsent(targetType, !recipientsPart.isEmpty() ? recipientsPart.get(0) : null); for (NotificationRecipient recipient : recipientsPart) { @@ -372,7 +387,7 @@ public class NotificationController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION) @RequestParam(required = false) String sortOrder, @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { - // generic permission + // PE: generic permission PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); return notificationRequestService.findNotificationRequestsInfosByTenantIdAndOriginatorType(user.getTenantId(), EntityType.USER, pageLink); } @@ -431,10 +446,23 @@ public class NotificationController extends BaseController { notes = "Returns the list of delivery methods that are properly configured and are allowed to be used for sending notifications." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @GetMapping("/notification/deliveryMethods") - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") public Set getAvailableDeliveryMethods(@AuthenticationPrincipal SecurityUser user) throws ThingsboardException { - accessControlService.checkPermission(user, Resource.ADMIN_SETTINGS, Operation.READ); return notificationCenter.getAvailableDeliveryMethods(user.getTenantId()); } + + @PostMapping("/notification/settings/user") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + public UserNotificationSettings saveUserNotificationSettings(@RequestBody @Valid UserNotificationSettings settings, + @AuthenticationPrincipal SecurityUser user) { + return notificationSettingsService.saveUserNotificationSettings(user.getTenantId(), user.getId(), settings); + } + + @GetMapping("/notification/settings/user") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + public UserNotificationSettings getUserNotificationSettings(@AuthenticationPrincipal SecurityUser user) { + return notificationSettingsService.getUserNotificationSettings(user.getTenantId(), user.getId(), true); + } + } diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationRuleController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationRuleController.java index ccf2124113..4321be8122 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationRuleController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationRuleController.java @@ -34,7 +34,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; @@ -161,7 +161,7 @@ public class NotificationRuleController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION) @RequestParam(required = false) String sortOrder, @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { - // generic permission + // PE: generic permission PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); return notificationRuleService.findNotificationRulesInfosByTenantId(user.getTenantId(), pageLink); } diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java index a4c829796b..51ed86441b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java @@ -139,7 +139,7 @@ public class NotificationTargetController extends BaseController { @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) @RequestParam int page, @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { - // generic permission + // PE: generic permission NotificationTargetConfig targetConfig = notificationTarget.getConfiguration(); if (targetConfig.getType() == NotificationTargetType.PLATFORM_USERS) { checkTargetUsers(user, targetConfig); @@ -159,7 +159,7 @@ public class NotificationTargetController extends BaseController { public List getNotificationTargetsByIds(@ApiParam(value = "Comma-separated list of uuids representing targets ids", required = true) @RequestParam("ids") UUID[] ids, @AuthenticationPrincipal SecurityUser user) { - // generic permission + // PE: generic permission List targetsIds = Arrays.stream(ids).map(NotificationTargetId::new).collect(Collectors.toList()); return notificationTargetService.findNotificationTargetsByTenantIdAndIds(user.getTenantId(), targetsIds); } @@ -181,7 +181,7 @@ public class NotificationTargetController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION) @RequestParam(required = false) String sortOrder, @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { - // generic permission + // PE: generic permission PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); return notificationTargetService.findNotificationTargetsByTenantId(user.getTenantId(), pageLink); } @@ -199,6 +199,7 @@ public class NotificationTargetController extends BaseController { @RequestParam(required = false) String sortOrder, @RequestParam(required = false) NotificationType notificationType, @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { + // PE: generic permission PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); return notificationTargetService.findNotificationTargetsByTenantIdAndSupportedNotificationType(user.getTenantId(), notificationType, pageLink); } @@ -219,7 +220,7 @@ public class NotificationTargetController extends BaseController { if (user.isSystemAdmin()) { return; } - // generic permission for users + // PE: generic permission for users UsersFilter usersFilter = ((PlatformUsersNotificationTargetConfig) targetConfig).getUsersFilter(); switch (usersFilter.getType()) { case USER_LIST: diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationTemplateController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationTemplateController.java index 4d5e24f765..a66b4bce10 100644 --- a/application/src/main/java/org/thingsboard/server/controller/NotificationTemplateController.java +++ b/application/src/main/java/org/thingsboard/server/controller/NotificationTemplateController.java @@ -150,7 +150,7 @@ public class NotificationTemplateController extends BaseController { @ApiParam(value = "Comma-separated list of notification types to filter the templates") @RequestParam(required = false) NotificationType[] notificationTypes, @AuthenticationPrincipal SecurityUser user) throws ThingsboardException { - // generic permission + // PE: generic permission PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); if (notificationTypes == null || notificationTypes.length == 0) { notificationTypes = NotificationType.values(); @@ -180,7 +180,7 @@ public class NotificationTemplateController extends BaseController { @ApiParam(value = "Slack bot token. If absent - system Slack settings will be used") @RequestParam(required = false) String token, @AuthenticationPrincipal SecurityUser user) { - // generic permission + // PE: generic permission if (StringUtils.isEmpty(token)) { NotificationSettings settings = notificationSettingsService.findNotificationSettings(user.getTenantId()); SlackNotificationDeliveryMethodConfig slackConfig = (SlackNotificationDeliveryMethodConfig) diff --git a/application/src/main/java/org/thingsboard/server/controller/QueueController.java b/application/src/main/java/org/thingsboard/server/controller/QueueController.java index 0e79ae7955..faeea1a4aa 100644 --- a/application/src/main/java/org/thingsboard/server/controller/QueueController.java +++ b/application/src/main/java/org/thingsboard/server/controller/QueueController.java @@ -126,7 +126,6 @@ public class QueueController extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @RequestMapping(value = "/queues", params = {"serviceType"}, method = RequestMethod.POST) @ResponseBody - public Queue saveQueue(@ApiParam(value = "A JSON value representing the queue.") @RequestBody Queue queue, @ApiParam(value = QUEUE_SERVICE_TYPE_DESCRIPTION, allowableValues = QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES, required = true) diff --git a/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java b/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java index 464dc528a5..dd9141adad 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.RpcId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rpc.Rpc; @@ -46,13 +47,12 @@ import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.exception.ToErrorResponseEntity; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.rpc.RemoveRpcActorMsg; +import org.thingsboard.server.common.msg.rpc.RemoveRpcActorMsg; import org.thingsboard.server.service.security.permission.Operation; import javax.annotation.Nullable; import java.util.UUID; -import static org.thingsboard.server.common.data.DataConstants.RPC_DELETED; import static org.thingsboard.server.controller.ControllerConstants.DEVICE_ID; import static org.thingsboard.server.controller.ControllerConstants.DEVICE_ID_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.MARKDOWN_CODE_BLOCK_END; @@ -230,7 +230,7 @@ public class RpcV2Controller extends AbstractRpcController { Rpc rpc = checkRpcId(rpcId, Operation.DELETE); if (rpc != null) { - if (rpc.getStatus().equals(RpcStatus.QUEUED)) { + if (rpc.getStatus().isPushDeleteNotificationToCore()) { RemoveRpcActorMsg removeMsg = new RemoveRpcActorMsg(getTenantId(), rpc.getDeviceId(), rpc.getUuidId()); log.trace("[{}] Forwarding msg {} to queue actor!", rpc.getDeviceId(), rpc); tbClusterService.pushMsgToCore(removeMsg, null); @@ -239,7 +239,7 @@ public class RpcV2Controller extends AbstractRpcController { rpcService.deleteRpc(getTenantId(), rpcId); rpc.setStatus(RpcStatus.DELETED); - TbMsg msg = TbMsg.newMsg(RPC_DELETED, rpc.getDeviceId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(rpc)); + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_DELETED, rpc.getDeviceId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(rpc)); tbClusterService.pushMsgToRuleEngine(getTenantId(), rpc.getDeviceId(), msg, null); } } diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index 50abb3de1d..e43a365cf8 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -460,7 +460,7 @@ public class RuleChainController extends BaseController { @ApiParam(value = "Enables overwrite for existing rule chains with the same name.") @RequestParam(required = false, defaultValue = "false") boolean overwrite) throws ThingsboardException { TenantId tenantId = getCurrentUser().getTenantId(); - List importResults = ruleChainService.importTenantRuleChains(tenantId, ruleChainData, overwrite); + List importResults = ruleChainService.importTenantRuleChains(tenantId, ruleChainData, overwrite, tbRuleChainService::updateRuleNodeConfiguration); for (RuleChainImportResult importResult : importResults) { if (importResult.getError() == null) { tbClusterService.broadcastEntityStateChangeEvent(importResult.getTenantId(), importResult.getRuleChainId(), diff --git a/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java b/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java index 3633cbbdeb..5460fea3f6 100644 --- a/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java +++ b/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java @@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.settings.UserSettings; import org.thingsboard.server.common.data.settings.UserSettingsType; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.UserPrincipal; @@ -130,6 +131,10 @@ public class SystemInfoController extends BaseController { } systemParams.setUserSettings(userSettingsNode); systemParams.setMaxDatapointsLimit(maxDatapointsLimit); + if (!currentUser.isSystemAdmin()) { + DefaultTenantProfileConfiguration tenantProfileConfiguration = tenantProfileCache.get(tenantId).getDefaultProfileConfiguration(); + systemParams.setMaxResourceSize(tenantProfileConfiguration.getMaxResourceSize()); + } return systemParams; } diff --git a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java index 94e318f37c..5a1758f860 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java @@ -19,6 +19,7 @@ import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.core.io.ByteArrayResource; import org.springframework.http.CacheControl; import org.springframework.http.HttpHeaders; @@ -26,16 +27,17 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.ResourceType; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.TbResourceInfoFilter; @@ -50,8 +52,11 @@ import org.thingsboard.server.service.resource.TbResourceService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; -import java.util.Base64; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashSet; import java.util.List; +import java.util.Set; import static org.thingsboard.server.controller.ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER; import static org.thingsboard.server.controller.ControllerConstants.LWM2M_OBJECT_DESCRIPTION; @@ -87,15 +92,14 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Download Resource (downloadResource)", notes = "Download Resource based on the provided Resource Id." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/resource/{resourceId}/download", method = RequestMethod.GET) - @ResponseBody - public ResponseEntity downloadResource(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) - @PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { + @GetMapping(value = "/resource/{resourceId}/download") + public ResponseEntity downloadResource(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) + @PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); TbResource tbResource = checkResourceId(resourceId, Operation.READ); - ByteArrayResource resource = new ByteArrayResource(Base64.getDecoder().decode(tbResource.getData().getBytes())); + ByteArrayResource resource = new ByteArrayResource(tbResource.getData()); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + tbResource.getFileName()) .header("x-filename", tbResource.getFileName()) @@ -106,42 +110,38 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Download LWM2M Resource (downloadLwm2mResourceIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/resource/lwm2m/{resourceId}/download", method = RequestMethod.GET, produces = "application/xml") - @ResponseBody - public ResponseEntity downloadLwm2mResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) - @PathVariable(RESOURCE_ID) String strResourceId, - @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { + @GetMapping(value = "/resource/lwm2m/{resourceId}/download", produces = "application/xml") + public ResponseEntity downloadLwm2mResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) + @PathVariable(RESOURCE_ID) String strResourceId, + @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { return downloadResourceIfChanged(ResourceType.LWM2M_MODEL, strResourceId, etag); } @ApiOperation(value = "Download PKCS_12 Resource (downloadPkcs12ResourceIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/resource/pkcs12/{resourceId}/download", method = RequestMethod.GET, produces = "application/x-pkcs12") - @ResponseBody - public ResponseEntity downloadPkcs12ResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) - @PathVariable(RESOURCE_ID) String strResourceId, - @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { + public ResponseEntity downloadPkcs12ResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) + @PathVariable(RESOURCE_ID) String strResourceId, + @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { return downloadResourceIfChanged(ResourceType.PKCS_12, strResourceId, etag); } @ApiOperation(value = "Download JKS Resource (downloadJksResourceIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/resource/jks/{resourceId}/download", method = RequestMethod.GET, produces = "application/x-java-keystore") - @ResponseBody - public ResponseEntity downloadJksResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) - @PathVariable(RESOURCE_ID) String strResourceId, - @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { + @GetMapping(value = "/resource/jks/{resourceId}/download", produces = "application/x-java-keystore") + public ResponseEntity downloadJksResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) + @PathVariable(RESOURCE_ID) String strResourceId, + @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { return downloadResourceIfChanged(ResourceType.JKS, strResourceId, etag); } @ApiOperation(value = "Download JS Resource (downloadJsResourceIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + AVAILABLE_FOR_ANY_AUTHORIZED_USER) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/resource/js/{resourceId}/download", method = RequestMethod.GET, produces = "application/javascript") - @ResponseBody - public ResponseEntity downloadJsResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) - @PathVariable(RESOURCE_ID) String strResourceId, - @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { + @GetMapping(value = "/resource/js/{resourceId}/download", produces = "application/javascript") + public ResponseEntity downloadJsResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) + @PathVariable(RESOURCE_ID) String strResourceId, + @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException { return downloadResourceIfChanged(ResourceType.JS_MODULE, strResourceId, etag); } @@ -150,8 +150,7 @@ public class TbResourceController extends BaseController { RESOURCE_INFO_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH, produces = "application/json") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/resource/info/{resourceId}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/resource/info/{resourceId}") public TbResourceInfo getResourceInfoById(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) @PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); @@ -162,10 +161,10 @@ public class TbResourceController extends BaseController { @ApiOperation(value = "Get Resource (getResourceById)", notes = "Fetch the Resource object based on the provided Resource Id. " + RESOURCE_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH, - produces = "application/json") + produces = "application/json", hidden = true) + @Deprecated // resource's data should be fetched with a download request @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/resource/{resourceId}", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/resource/{resourceId}") public TbResource getResourceById(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) @PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); @@ -184,13 +183,12 @@ public class TbResourceController extends BaseController { produces = "application/json", consumes = "application/json") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/resource", method = RequestMethod.POST) - @ResponseBody - public TbResource saveResource(@ApiParam(value = "A JSON value representing the Resource.") - @RequestBody TbResource resource) throws Exception { + @PostMapping(value = "/resource") + public TbResourceInfo saveResource(@ApiParam(value = "A JSON value representing the Resource.") + @RequestBody TbResource resource) throws Exception { resource.setTenantId(getTenantId()); checkEntity(resource.getId(), resource, Resource.TB_RESOURCE); - return tbResourceService.save(resource, getCurrentUser()); + return new TbResourceInfo(tbResourceService.save(resource, getCurrentUser())); } @ApiOperation(value = "Get Resource Infos (getResources)", @@ -198,8 +196,7 @@ public class TbResourceController extends BaseController { PAGE_DATA_PARAMETERS + RESOURCE_INFO_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH, produces = "application/json") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/resource", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/resource") public PageData getResources(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) @@ -215,9 +212,14 @@ public class TbResourceController extends BaseController { PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); TbResourceInfoFilter.TbResourceInfoFilterBuilder filter = TbResourceInfoFilter.builder(); filter.tenantId(getTenantId()); + Set resourceTypes = new HashSet<>(); if (StringUtils.isNotEmpty(resourceType)) { - filter.resourceType(ResourceType.valueOf(resourceType)); + resourceTypes.add(ResourceType.valueOf(resourceType)); + } else { + Collections.addAll(resourceTypes, ResourceType.values()); + resourceTypes.remove(ResourceType.IMAGE); } + filter.resourceTypes(resourceTypes); if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) { return checkNotNull(resourceService.findTenantResourcesByTenantId(filter.build(), pageLink)); } else { @@ -225,13 +227,36 @@ public class TbResourceController extends BaseController { } } + @ApiOperation(value = "Get All Resource Infos (getAllResources)", + notes = "Returns a page of Resource Info objects owned by tenant. " + + PAGE_DATA_PARAMETERS + RESOURCE_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH, + produces = "application/json") + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @GetMapping(value = "/resource/tenant") + public PageData getTenantResources(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) + @RequestParam int pageSize, + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) + @RequestParam int page, + @ApiParam(value = RESOURCE_TEXT_SEARCH_DESCRIPTION) + @RequestParam(required = false) String textSearch, + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES) + @RequestParam(required = false) String sortProperty, + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) + @RequestParam(required = false) String sortOrder) throws ThingsboardException { + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + TbResourceInfoFilter filter = TbResourceInfoFilter.builder() + .tenantId(getTenantId()) + .resourceTypes(EnumSet.allOf(ResourceType.class)) + .build(); + return checkNotNull(resourceService.findTenantResourcesByTenantId(filter, pageLink)); + } + @ApiOperation(value = "Get LwM2M Objects (getLwm2mListObjectsPage)", notes = "Returns a page of LwM2M objects parsed from Resources with type 'LWM2M_MODEL' owned by tenant or sysadmin. " + PAGE_DATA_PARAMETERS + LWM2M_OBJECT_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH, produces = "application/json") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/resource/lwm2m/page", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/resource/lwm2m/page") public List getLwm2mListObjectsPage(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) @RequestParam int pageSize, @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) @@ -243,7 +268,7 @@ public class TbResourceController extends BaseController { @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { PageLink pageLink = new PageLink(pageSize, page, textSearch); - return checkNotNull(resourceService.findLwM2mObjectPage(getTenantId(), sortProperty, sortOrder, pageLink)); + return checkNotNull(tbResourceService.findLwM2mObjectPage(getTenantId(), sortProperty, sortOrder, pageLink)); } @ApiOperation(value = "Get LwM2M Objects (getLwm2mListObjects)", @@ -251,22 +276,20 @@ public class TbResourceController extends BaseController { "You can specify parameters to filter the results. " + LWM2M_OBJECT_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH, produces = "application/json") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/resource/lwm2m", method = RequestMethod.GET) - @ResponseBody + @GetMapping(value = "/resource/lwm2m") public List getLwm2mListObjects(@ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES, required = true) @RequestParam String sortOrder, @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = LWM2M_OBJECT_SORT_PROPERTY_ALLOWABLE_VALUES, required = true) @RequestParam String sortProperty, @ApiParam(value = "LwM2M Object ids.", required = true) @RequestParam(required = false) String[] objectIds) throws ThingsboardException { - return checkNotNull(resourceService.findLwM2mObject(getTenantId(), sortOrder, sortProperty, objectIds)); + return checkNotNull(tbResourceService.findLwM2mObject(getTenantId(), sortOrder, sortProperty, objectIds)); } @ApiOperation(value = "Delete Resource (deleteResource)", notes = "Deletes the Resource. Referencing non-existing Resource Id will cause an error." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/resource/{resourceId}", method = RequestMethod.DELETE) - @ResponseBody + @DeleteMapping(value = "/resource/{resourceId}") public void deleteResource(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION) @PathVariable("resourceId") String strResourceId) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); @@ -275,12 +298,12 @@ public class TbResourceController extends BaseController { tbResourceService.delete(tbResource, getCurrentUser()); } - private ResponseEntity downloadResourceIfChanged(ResourceType type, String strResourceId, String etag) throws ThingsboardException { + private ResponseEntity downloadResourceIfChanged(ResourceType resourceType, String strResourceId, String etag) throws ThingsboardException { checkParameter(RESOURCE_ID, strResourceId); TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); - if (etag != null) { TbResourceInfo tbResourceInfo = checkResourceInfoId(resourceId, Operation.READ); + etag = StringUtils.remove(etag, '\"'); // etag is wrapped in double quotes due to HTTP specification if (etag.equals(tbResourceInfo.getEtag())) { return ResponseEntity.status(HttpStatus.NOT_MODIFIED) .eTag(tbResourceInfo.getEtag()) @@ -289,15 +312,16 @@ public class TbResourceController extends BaseController { } TbResource tbResource = checkResourceId(resourceId, Operation.READ); - ByteArrayResource resource = new ByteArrayResource(Base64.getDecoder().decode(tbResource.getData().getBytes())); + ByteArrayResource resource = new ByteArrayResource(tbResource.getData()); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + tbResource.getFileName()) .header("x-filename", tbResource.getFileName()) .contentLength(resource.contentLength()) - .header("Content-Type", type.getMediaType()) + .header("Content-Type", resourceType.getMediaType()) .cacheControl(CacheControl.noCache()) .eTag(tbResource.getEtag()) .body(resource); } + } \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java index 05f56e93bd..55f4f3afbd 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -45,7 +45,7 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.async.DeferredResult; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardThreadFactory; -import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceAttributesEventNotificationMsg; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; @@ -74,7 +74,7 @@ import org.thingsboard.server.common.data.kv.ReadTsKvQuery; import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.exception.InvalidParametersException; import org.thingsboard.server.exception.UncheckedApiException; @@ -201,7 +201,7 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope) throws ThingsboardException { return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, - (result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope)); + (result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope)); } @ApiOperation(value = "Get attributes (getAttributes)", @@ -219,9 +219,9 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType, @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { - SecurityUser user = getCurrentUser(); + SecurityUser user = getCurrentUser(); return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, - (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr)); + (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr)); } @@ -245,7 +245,7 @@ public class TelemetryController extends BaseController { @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { SecurityUser user = getCurrentUser(); return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, - (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr)); + (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr)); } @ApiOperation(value = "Get time-series keys (getTimeseriesKeys)", @@ -259,7 +259,7 @@ public class TelemetryController extends BaseController { @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType, @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr) throws ThingsboardException { return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, - (result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result), MoreExecutors.directExecutor())); + (result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result), MoreExecutors.directExecutor())); } @ApiOperation(value = "Get latest time-series value (getLatestTimeseries)", @@ -462,7 +462,9 @@ public class TelemetryController extends BaseController { notes = "Delete time-series for selected entity based on entity id, entity type and keys." + " Use 'deleteAllDataForKeys' to delete all time-series data." + " Use 'startTs' and 'endTs' to specify time-range instead. " + - " Use 'rewriteLatestIfDeleted' to rewrite latest value (stored in separate table for performance) after deletion of the time range. " + + " Use 'deleteLatest' to delete latest value (stored in separate table for performance) if the value's timestamp matches the time-range. " + + " Use 'rewriteLatestIfDeleted' to rewrite latest value (stored in separate table for performance) if the value's timestamp matches the time-range and 'deleteLatest' param is true." + + " The replacement value will be fetched from the 'time-series' table, and its timestamp will be the most recent one before the defined time-range. " + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE) @ApiResponses(value = { @@ -486,14 +488,16 @@ public class TelemetryController extends BaseController { @RequestParam(name = "startTs", required = false) Long startTs, @ApiParam(value = "A long value representing the end timestamp of removal time range in milliseconds.") @RequestParam(name = "endTs", required = false) Long endTs, + @ApiParam(value = "If the parameter is set to true, the latest telemetry can be removed, otherwise, in case that parameter is set to false the latest value will not removed.") + @RequestParam(name = "deleteLatest", required = false, defaultValue = "true") boolean deleteLatest, @ApiParam(value = "If the parameter is set to true, the latest telemetry will be rewritten in case that current latest value was removed, otherwise, in case that parameter is set to false the new latest value will not set.") @RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); - return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted); + return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted, deleteLatest); } private DeferredResult deleteTimeseries(EntityId entityIdStr, String keysStr, boolean deleteAllDataForKeys, - Long startTs, Long endTs, boolean rewriteLatestIfDeleted) throws ThingsboardException { + Long startTs, Long endTs, boolean rewriteLatestIfDeleted, boolean deleteLatest) throws ThingsboardException { List keys = toKeysList(keysStr); if (keys.isEmpty()) { return getImmediateDeferredResult("Empty keys: " + keysStr, HttpStatus.BAD_REQUEST); @@ -517,7 +521,7 @@ public class TelemetryController extends BaseController { return accessValidator.validateEntityAndCallback(user, Operation.WRITE_TELEMETRY, entityIdStr, (result, tenantId, entityId) -> { List deleteTsKvQueries = new ArrayList<>(); for (String key : keys) { - deleteTsKvQueries.add(new BaseDeleteTsKvQuery(key, deleteFromTs, deleteToTs, rewriteLatestIfDeleted)); + deleteTsKvQueries.add(new BaseDeleteTsKvQuery(key, deleteFromTs, deleteToTs, rewriteLatestIfDeleted, deleteLatest)); } tsSubService.deleteTimeseriesAndNotify(tenantId, entityId, keys, deleteTsKvQueries, new FutureCallback<>() { @Override diff --git a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java index a7fe973ec0..1fe492d692 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java @@ -133,6 +133,7 @@ public class TenantProfileController extends BaseController { " \"maxRuleChains\": 0,\n" + " \"maxResourcesInBytes\": 0,\n" + " \"maxOtaPackagesInBytes\": 0,\n" + + " \"maxResourceSize\": 0,\n" + " \"transportTenantMsgRateLimit\": \"1000:1,20000:60\",\n" + " \"transportTenantTelemetryMsgRateLimit\": \"1000:1,20000:60\",\n" + " \"transportTenantTelemetryDataPointsRateLimit\": \"1000:1,20000:60\",\n" + @@ -151,6 +152,8 @@ public class TenantProfileController extends BaseController { " \"defaultStorageTtlDays\": 0,\n" + " \"alarmsTtlDays\": 0,\n" + " \"rpcTtlDays\": 0,\n" + + " \"queueStatsTtlDays\": 0,\n" + + " \"ruleEngineExceptionsTtlDays\": 0,\n" + " \"warnThreshold\": 0\n" + " }\n" + " },\n" + diff --git a/application/src/main/java/org/thingsboard/server/controller/UserController.java b/application/src/main/java/org/thingsboard/server/controller/UserController.java index d49b2b04df..002b27d265 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -105,6 +105,7 @@ import static org.thingsboard.server.controller.ControllerConstants.USER_ID_PARA import static org.thingsboard.server.controller.ControllerConstants.USER_SORT_PROPERTY_ALLOWABLE_VALUES; import static org.thingsboard.server.controller.ControllerConstants.USER_TEXT_SEARCH_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK; +import static org.thingsboard.server.dao.entity.BaseEntityService.NULL_CUSTOMER_ID; @RequiredArgsConstructor @RestController @@ -439,32 +440,28 @@ public class UserController extends BaseController { @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - checkParameter("alarmId", strAlarmId); - AlarmId alarmEntityId = new AlarmId(toUUID(strAlarmId)); - Alarm alarm = checkAlarmId(alarmEntityId, Operation.READ); - SecurityUser currentUser = getCurrentUser(); - TenantId tenantId = currentUser.getTenantId(); - CustomerId originatorCustomerId = entityService.fetchEntityCustomerId(tenantId, alarm.getOriginator()).get(); - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - PageData pageData; - if (Authority.TENANT_ADMIN.equals(currentUser.getAuthority())) { - if (alarm.getCustomerId() == null) { - pageData = userService.findTenantAdmins(tenantId, pageLink); - } else { - ArrayList customerIds = new ArrayList<>(Collections.singletonList(new CustomerId(CustomerId.NULL_UUID))); - if (!CustomerId.NULL_UUID.equals(originatorCustomerId.getId())) { - customerIds.add(originatorCustomerId); - } - pageData = userService.findUsersByCustomerIds(tenantId, customerIds, pageLink); - } + checkParameter("alarmId", strAlarmId); + AlarmId alarmEntityId = new AlarmId(toUUID(strAlarmId)); + Alarm alarm = checkAlarmId(alarmEntityId, Operation.READ); + SecurityUser currentUser = getCurrentUser(); + TenantId tenantId = currentUser.getTenantId(); + CustomerId originatorCustomerId = entityService.fetchEntityCustomerId(tenantId, alarm.getOriginator()).orElse(NULL_CUSTOMER_ID); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + PageData pageData; + if (Authority.TENANT_ADMIN.equals(currentUser.getAuthority())) { + if (alarm.getCustomerId() == null) { + pageData = userService.findTenantAdmins(tenantId, pageLink); } else { - pageData = userService.findCustomerUsers(tenantId, alarm.getCustomerId(), pageLink); + ArrayList customerIds = new ArrayList<>(Collections.singletonList(NULL_CUSTOMER_ID)); + if (!CustomerId.NULL_UUID.equals(originatorCustomerId.getId())) { + customerIds.add(originatorCustomerId); + } + pageData = userService.findUsersByCustomerIds(tenantId, customerIds, pageLink); } - return pageData.mapData(user -> new UserEmailInfo(user.getId(), user.getEmail(), user.getFirstName(), user.getLastName())); - } catch (Exception e) { - throw handleException(e); + } else { + pageData = userService.findCustomerUsers(tenantId, alarm.getCustomerId(), pageLink); } + return pageData.mapData(user -> new UserEmailInfo(user.getId(), user.getEmail(), user.getFirstName(), user.getLastName())); } @ApiOperation(value = "Save user settings (saveUserSettings)", diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java index d1ecd0441a..fa18fe4265 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java @@ -17,7 +17,7 @@ package org.thingsboard.server.controller; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; -import lombok.extern.slf4j.Slf4j; +import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; @@ -28,38 +28,66 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; -import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetTypeId; +import org.thingsboard.server.common.data.id.WidgetsBundleId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.widget.DeprecatedFilter; import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetTypeInfo; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.entitiy.widgets.type.TbWidgetTypeService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import static org.thingsboard.server.controller.ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER; +import static org.thingsboard.server.controller.ControllerConstants.INLINE_IMAGES; +import static org.thingsboard.server.controller.ControllerConstants.INLINE_IMAGES_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS; +import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES; +import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH; import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK; import static org.thingsboard.server.controller.ControllerConstants.WIDGET_TYPE_ID_PARAM_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.WIDGET_TYPE_SORT_PROPERTY_ALLOWABLE_VALUES; +import static org.thingsboard.server.controller.ControllerConstants.WIDGET_TYPE_TEXT_SEARCH_DESCRIPTION; -@Slf4j @RestController @TbCoreComponent @RequestMapping("/api") +@RequiredArgsConstructor public class WidgetTypeController extends AutoCommitController { + private final TbWidgetTypeService tbWidgetTypeService; + private final ImageService imageService; + private static final String WIDGET_TYPE_DESCRIPTION = "Widget Type represents the template for widget creation. Widget Type and Widget are similar to class and object in OOP theory."; private static final String WIDGET_TYPE_DETAILS_DESCRIPTION = "Widget Type Details extend Widget Type and add image and description properties. " + "Those properties are useful to edit the Widget Type but they are not required for Dashboard rendering. "; private static final String WIDGET_TYPE_INFO_DESCRIPTION = "Widget Type Info is a lightweight object that represents Widget Type but does not contain the heavyweight widget descriptor JSON"; - + private static final String TENANT_ONLY_PARAM_DESCRIPTION = "Optional boolean parameter indicating whether only tenant widget types should be returned"; + private static final String FULL_SEARCH_PARAM_DESCRIPTION = "Optional boolean parameter indicating whether search widgets by description not only by name"; + private static final String DEPRECATED_FILTER_ALLOWABLE_VALUES = "ALL, ACTUAL, DEPRECATED"; + private static final String DEPRECATED_FILTER_PARAM_DESCRIPTION = "Optional string parameter indicating whether to include deprecated widgets"; + private static final String UPDATE_EXISTING_BY_FQN_PARAM_DESCRIPTION = "Optional boolean parameter indicating whether to update existing widget type by FQN if present instead of creating new one"; + private static final String WIDGET_TYPE_ARRAY_DESCRIPTION = "A list of string values separated by comma ',' representing one of the widget type value"; + private static final String WIDGET_TYPE_ALLOWABLE_VALUES = "timeseries, latest, control, alarm, static"; @ApiOperation(value = "Get Widget Type Details (getWidgetTypeById)", notes = "Get the Widget Type Details based on the provided Widget Type Id. " + WIDGET_TYPE_DETAILS_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @@ -67,11 +95,30 @@ public class WidgetTypeController extends AutoCommitController { @RequestMapping(value = "/widgetType/{widgetTypeId}", method = RequestMethod.GET) @ResponseBody public WidgetTypeDetails getWidgetTypeById( + @ApiParam(value = WIDGET_TYPE_ID_PARAM_DESCRIPTION, required = true) + @PathVariable("widgetTypeId") String strWidgetTypeId, + @ApiParam(value = INLINE_IMAGES_DESCRIPTION) + @RequestParam(value = INLINE_IMAGES, required = false) boolean inlineImages) throws ThingsboardException { + checkParameter("widgetTypeId", strWidgetTypeId); + WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId)); + var result = checkWidgetTypeId(widgetTypeId, Operation.READ); + if (inlineImages) { + imageService.inlineImages(result); + } + return result; + } + + @ApiOperation(value = "Get Widget Type Info (getWidgetTypeInfoById)", + notes = "Get the Widget Type Info based on the provided Widget Type Id. " + WIDGET_TYPE_DETAILS_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @RequestMapping(value = "/widgetTypeInfo/{widgetTypeId}", method = RequestMethod.GET) + @ResponseBody + public WidgetTypeInfo getWidgetTypeInfoById( @ApiParam(value = WIDGET_TYPE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("widgetTypeId") String strWidgetTypeId) throws ThingsboardException { checkParameter("widgetTypeId", strWidgetTypeId); WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId)); - return checkWidgetTypeId(widgetTypeId, Operation.READ); + return new WidgetTypeInfo(checkWidgetTypeId(widgetTypeId, Operation.READ)); } @ApiOperation(value = "Create Or Update Widget Type (saveWidgetType)", @@ -80,7 +127,7 @@ public class WidgetTypeController extends AutoCommitController { "The newly created Widget Type Id will be present in the response. " + "Specify existing Widget Type id to update the Widget Type. " + "Referencing non-existing Widget Type Id will cause 'Not Found' error." + - "\n\nWidget Type alias is unique in the scope of Widget Bundle. " + + "\n\nWidget Type fqn is unique in the scope of System or Tenant. " + "Special Tenant Id '13814000-1dd2-11b2-8080-808080808080' is automatically used if the create request is sent by user with 'SYS_ADMIN' authority." + "Remove 'id', 'tenantId' rom the request body example (below) to create new Widget Type entity." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @@ -89,7 +136,9 @@ public class WidgetTypeController extends AutoCommitController { @ResponseBody public WidgetTypeDetails saveWidgetType( @ApiParam(value = "A JSON value representing the Widget Type Details.", required = true) - @RequestBody WidgetTypeDetails widgetTypeDetails) throws Exception { + @RequestBody WidgetTypeDetails widgetTypeDetails, + @ApiParam(value = UPDATE_EXISTING_BY_FQN_PARAM_DESCRIPTION) + @RequestParam(required = false) Boolean updateExistingByFqn) throws Exception { var currentUser = getCurrentUser(); if (Authority.SYS_ADMIN.equals(currentUser.getAuthority())) { widgetTypeDetails.setTenantId(TenantId.SYS_TENANT_ID); @@ -98,19 +147,7 @@ public class WidgetTypeController extends AutoCommitController { } checkEntity(widgetTypeDetails.getId(), widgetTypeDetails, Resource.WIDGET_TYPE); - WidgetTypeDetails savedWidgetTypeDetails = widgetTypeService.saveWidgetType(widgetTypeDetails); - - if (!Authority.SYS_ADMIN.equals(currentUser.getAuthority())) { - WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(widgetTypeDetails.getTenantId(), widgetTypeDetails.getBundleAlias()); - if (widgetsBundle != null) { - autoCommit(currentUser, widgetsBundle.getId()); - } - } - - sendEntityNotificationMsg(getTenantId(), savedWidgetTypeDetails.getId(), - widgetTypeDetails.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); - - return checkNotNull(savedWidgetTypeDetails); + return tbWidgetTypeService.save(widgetTypeDetails, updateExistingByFqn != null && updateExistingByFqn, currentUser); } @ApiOperation(value = "Delete widget type (deleteWidgetType)", @@ -122,27 +159,57 @@ public class WidgetTypeController extends AutoCommitController { @ApiParam(value = WIDGET_TYPE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("widgetTypeId") String strWidgetTypeId) throws Exception { checkParameter("widgetTypeId", strWidgetTypeId); - var currentUser = getCurrentUser(); WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId)); WidgetTypeDetails wtd = checkWidgetTypeId(widgetTypeId, Operation.DELETE); - widgetTypeService.deleteWidgetType(currentUser.getTenantId(), widgetTypeId); + tbWidgetTypeService.delete(wtd, getCurrentUser()); + } - if (wtd != null && !Authority.SYS_ADMIN.equals(currentUser.getAuthority())) { - WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(wtd.getTenantId(), wtd.getBundleAlias()); - if (widgetsBundle != null) { - autoCommit(currentUser, widgetsBundle.getId()); + @ApiOperation(value = "Get Widget Types (getWidgetTypes)", + notes = "Returns a page of Widget Type objects available for current user. " + WIDGET_TYPE_DESCRIPTION + " " + + PAGE_DATA_PARAMETERS + AVAILABLE_FOR_ANY_AUTHORIZED_USER) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/widgetTypes", params = {"pageSize", "page"}, method = RequestMethod.GET) + @ResponseBody + public PageData getWidgetTypes( + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) + @RequestParam int pageSize, + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) + @RequestParam int page, + @ApiParam(value = WIDGET_TYPE_TEXT_SEARCH_DESCRIPTION) + @RequestParam(required = false) String textSearch, + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = WIDGET_TYPE_SORT_PROPERTY_ALLOWABLE_VALUES) + @RequestParam(required = false) String sortProperty, + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) + @RequestParam(required = false) String sortOrder, + @ApiParam(value = TENANT_ONLY_PARAM_DESCRIPTION) + @RequestParam(required = false) Boolean tenantOnly, + @ApiParam(value = FULL_SEARCH_PARAM_DESCRIPTION) + @RequestParam(required = false) Boolean fullSearch, + @ApiParam(value = DEPRECATED_FILTER_PARAM_DESCRIPTION, allowableValues = DEPRECATED_FILTER_ALLOWABLE_VALUES) + @RequestParam(required = false) String deprecatedFilter, + @ApiParam(value = WIDGET_TYPE_ARRAY_DESCRIPTION, allowableValues = WIDGET_TYPE_ALLOWABLE_VALUES) + @RequestParam(required = false) String[] widgetTypeList) throws ThingsboardException { + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + List widgetTypes = widgetTypeList != null ? Arrays.asList(widgetTypeList) : Collections.emptyList(); + boolean fullSearchBool = fullSearch != null && fullSearch; + DeprecatedFilter widgetTypeDeprecatedFilter = StringUtils.isNotEmpty(deprecatedFilter) ? DeprecatedFilter.valueOf(deprecatedFilter) : DeprecatedFilter.ALL; + if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) { + return checkNotNull(widgetTypeService.findSystemWidgetTypesByPageLink(getTenantId(), fullSearchBool, widgetTypeDeprecatedFilter, widgetTypes, pageLink)); + } else { + if (tenantOnly != null && tenantOnly) { + return checkNotNull(widgetTypeService.findTenantWidgetTypesByTenantIdAndPageLink(getTenantId(), fullSearchBool, widgetTypeDeprecatedFilter, widgetTypes, pageLink)); + } else { + return checkNotNull(widgetTypeService.findAllTenantWidgetTypesByTenantIdAndPageLink(getTenantId(), fullSearchBool, widgetTypeDeprecatedFilter, widgetTypes, pageLink)); } } - - sendEntityNotificationMsg(getTenantId(), widgetTypeId, EdgeEventActionType.DELETED); } - @ApiOperation(value = "Get all Widget types for specified Bundle (getBundleWidgetTypes)", + @ApiOperation(value = "Get all Widget types for specified Bundle (getBundleWidgetTypesByBundleAlias) (Deprecated)", notes = "Returns an array of Widget Type objects that belong to specified Widget Bundle." + WIDGET_TYPE_DESCRIPTION + " " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/widgetTypes", params = {"isSystem", "bundleAlias"}, method = RequestMethod.GET) @ResponseBody - public List getBundleWidgetTypes( + public List getBundleWidgetTypesByBundleAlias( @ApiParam(value = "System or Tenant", required = true) @RequestParam boolean isSystem, @ApiParam(value = "Widget Bundle alias", required = true) @@ -153,15 +220,28 @@ public class WidgetTypeController extends AutoCommitController { } else { tenantId = getCurrentUser().getTenantId(); } - return checkNotNull(widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(tenantId, bundleAlias)); + WidgetsBundle widgetsBundle = checkNotNull(widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(tenantId, bundleAlias)); + return checkNotNull(widgetTypeService.findWidgetTypesByWidgetsBundleId(getTenantId(), widgetsBundle.getId())); } - @ApiOperation(value = "Get all Widget types details for specified Bundle (getBundleWidgetTypes)", + @ApiOperation(value = "Get all Widget types for specified Bundle (getBundleWidgetTypes)", + notes = "Returns an array of Widget Type objects that belong to specified Widget Bundle." + WIDGET_TYPE_DESCRIPTION + " " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/widgetTypes", params = {"widgetsBundleId"}, method = RequestMethod.GET) + @ResponseBody + public List getBundleWidgetTypes( + @ApiParam(value = "Widget Bundle Id", required = true) + @RequestParam("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException { + WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); + return checkNotNull(widgetTypeService.findWidgetTypesByWidgetsBundleId(getTenantId(), widgetsBundleId)); + } + + @ApiOperation(value = "Get all Widget types details for specified Bundle (getBundleWidgetTypesDetailsByBundleAlias) (Deprecated)", notes = "Returns an array of Widget Type Details objects that belong to specified Widget Bundle." + WIDGET_TYPE_DETAILS_DESCRIPTION + " " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/widgetTypesDetails", params = {"isSystem", "bundleAlias"}, method = RequestMethod.GET) @ResponseBody - public List getBundleWidgetTypesDetails( + public List getBundleWidgetTypesDetailsByBundleAlias( @ApiParam(value = "System or Tenant", required = true) @RequestParam boolean isSystem, @ApiParam(value = "Widget Bundle alias", required = true) @@ -172,15 +252,47 @@ public class WidgetTypeController extends AutoCommitController { } else { tenantId = getCurrentUser().getTenantId(); } - return checkNotNull(widgetTypeService.findWidgetTypesDetailsByTenantIdAndBundleAlias(tenantId, bundleAlias)); + WidgetsBundle widgetsBundle = checkNotNull(widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(tenantId, bundleAlias)); + return checkNotNull(widgetTypeService.findWidgetTypesDetailsByWidgetsBundleId(getTenantId(), widgetsBundle.getId())); } - @ApiOperation(value = "Get Widget Type Info objects (getBundleWidgetTypesInfos)", - notes = "Get the Widget Type Info objects based on the provided parameters. " + WIDGET_TYPE_INFO_DESCRIPTION + AVAILABLE_FOR_ANY_AUTHORIZED_USER) + @ApiOperation(value = "Get all Widget types details for specified Bundle (getBundleWidgetTypes)", + notes = "Returns an array of Widget Type Details objects that belong to specified Widget Bundle." + WIDGET_TYPE_DETAILS_DESCRIPTION + " " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/widgetTypesDetails", params = {"widgetsBundleId"}, method = RequestMethod.GET) + @ResponseBody + public List getBundleWidgetTypesDetails( + @ApiParam(value = "Widget Bundle Id", required = true) + @RequestParam("widgetsBundleId") String strWidgetsBundleId, + @ApiParam(value = INLINE_IMAGES_DESCRIPTION) + @RequestParam(value = INLINE_IMAGES, required = false) boolean inlineImages + ) throws ThingsboardException { + WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); + var result = checkNotNull(widgetTypeService.findWidgetTypesDetailsByWidgetsBundleId(getTenantId(), widgetsBundleId)); + if (inlineImages) { + result.forEach(imageService::inlineImages); + } + return result; + } + + @ApiOperation(value = "Get all Widget type fqns for specified Bundle (getBundleWidgetTypeFqns)", + notes = "Returns an array of Widget Type fqns that belong to specified Widget Bundle." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @RequestMapping(value = "/widgetTypeFqns", params = {"widgetsBundleId"}, method = RequestMethod.GET) + @ResponseBody + public List getBundleWidgetTypeFqns( + @ApiParam(value = "Widget Bundle Id", required = true) + @RequestParam("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException { + WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); + return checkNotNull(widgetTypeService.findWidgetFqnsByWidgetsBundleId(getTenantId(), widgetsBundleId)); + } + + @ApiOperation(value = "Get Widget Type Info objects (getBundleWidgetTypesInfosByBundleAlias) (Deprecated)", + notes = "Get the Widget Type Info objects based on the provided parameters. " + WIDGET_TYPE_INFO_DESCRIPTION + AVAILABLE_FOR_ANY_AUTHORIZED_USER) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/widgetTypesInfos", params = {"isSystem", "bundleAlias"}, method = RequestMethod.GET) @ResponseBody - public List getBundleWidgetTypesInfos( + public List getBundleWidgetTypesInfosByBundleAlias( @ApiParam(value = "System or Tenant", required = true) @RequestParam boolean isSystem, @ApiParam(value = "Widget Bundle alias", required = true) @@ -191,15 +303,49 @@ public class WidgetTypeController extends AutoCommitController { } else { tenantId = getCurrentUser().getTenantId(); } - return checkNotNull(widgetTypeService.findWidgetTypesInfosByTenantIdAndBundleAlias(tenantId, bundleAlias)); + WidgetsBundle widgetsBundle = checkNotNull(widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(tenantId, bundleAlias)); + return checkNotNull(widgetTypeService.findWidgetTypesInfosByWidgetsBundleId(getTenantId(), widgetsBundle.getId(), false, DeprecatedFilter.ALL, + null, new PageLink(1024))).getData(); } - @ApiOperation(value = "Get Widget Type (getWidgetType)", + @ApiOperation(value = "Get Widget Type Info objects (getBundleWidgetTypesInfos)", + notes = "Get the Widget Type Info objects based on the provided parameters. " + WIDGET_TYPE_INFO_DESCRIPTION + AVAILABLE_FOR_ANY_AUTHORIZED_USER) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/widgetTypesInfos", params = {"widgetsBundleId", "pageSize", "page"}, method = RequestMethod.GET) + @ResponseBody + public PageData getBundleWidgetTypesInfos( + @ApiParam(value = "Widget Bundle Id", required = true) + @RequestParam("widgetsBundleId") String strWidgetsBundleId, + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) + @RequestParam int pageSize, + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) + @RequestParam int page, + @ApiParam(value = WIDGET_TYPE_TEXT_SEARCH_DESCRIPTION) + @RequestParam(required = false) String textSearch, + @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = WIDGET_TYPE_SORT_PROPERTY_ALLOWABLE_VALUES) + @RequestParam(required = false) String sortProperty, + @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) + @RequestParam(required = false) String sortOrder, + @ApiParam(value = FULL_SEARCH_PARAM_DESCRIPTION) + @RequestParam(required = false) Boolean fullSearch, + @ApiParam(value = DEPRECATED_FILTER_PARAM_DESCRIPTION, allowableValues = DEPRECATED_FILTER_ALLOWABLE_VALUES) + @RequestParam(required = false) String deprecatedFilter, + @ApiParam(value = WIDGET_TYPE_ARRAY_DESCRIPTION, allowableValues = WIDGET_TYPE_ALLOWABLE_VALUES) + @RequestParam(required = false) String[] widgetTypeList) throws ThingsboardException { + WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + List widgetTypes = widgetTypeList != null ? Arrays.asList(widgetTypeList) : Collections.emptyList(); + DeprecatedFilter widgetTypeDeprecatedFilter = StringUtils.isNotEmpty(deprecatedFilter) ? DeprecatedFilter.valueOf(deprecatedFilter) : DeprecatedFilter.ALL; + return checkNotNull(widgetTypeService.findWidgetTypesInfosByWidgetsBundleId(getTenantId(), widgetsBundleId, fullSearch != null && fullSearch, + widgetTypeDeprecatedFilter, widgetTypes, pageLink)); + } + + @ApiOperation(value = "Get Widget Type (getWidgetTypeByBundleAliasAndTypeAlias) (Deprecated)", notes = "Get the Widget Type based on the provided parameters. " + WIDGET_TYPE_DESCRIPTION + AVAILABLE_FOR_ANY_AUTHORIZED_USER) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @RequestMapping(value = "/widgetType", params = {"isSystem", "bundleAlias", "alias"}, method = RequestMethod.GET) @ResponseBody - public WidgetType getWidgetType( + public WidgetType getWidgetTypeByBundleAliasAndTypeAlias( @ApiParam(value = "System or Tenant", required = true) @RequestParam boolean isSystem, @ApiParam(value = "Widget Bundle alias", required = true) @@ -212,10 +358,37 @@ public class WidgetTypeController extends AutoCommitController { } else { tenantId = getCurrentUser().getTenantId(); } - WidgetType widgetType = widgetTypeService.findWidgetTypeByTenantIdBundleAliasAndAlias(tenantId, bundleAlias, alias); + WidgetType widgetType = widgetTypeService.findWidgetTypeByTenantIdAndFqn(tenantId, bundleAlias + "." + alias); checkNotNull(widgetType); accessControlService.checkPermission(getCurrentUser(), Resource.WIDGET_TYPE, Operation.READ, widgetType.getId(), widgetType); return widgetType; } + @ApiOperation(value = "Get Widget Type (getWidgetType)", + notes = "Get the Widget Type by FQN. " + WIDGET_TYPE_DESCRIPTION + AVAILABLE_FOR_ANY_AUTHORIZED_USER) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/widgetType", params = {"fqn"}, method = RequestMethod.GET) + @ResponseBody + public WidgetType getWidgetType( + @ApiParam(value = "Widget Type fqn", required = true) + @RequestParam String fqn) throws ThingsboardException { + String[] parts = fqn.split("\\."); + String scopeQualifier = parts.length > 0 ? parts[0] : null; + if (parts.length < 2 || (!scopeQualifier.equals("system") && !scopeQualifier.equals("tenant"))) { + throw new ThingsboardException("Invalid fqn!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); + } + TenantId tenantId; + if ("system".equals(scopeQualifier)) { + tenantId = TenantId.fromUUID(ModelConstants.NULL_UUID); + } else { + tenantId = getCurrentUser().getTenantId(); + } + String typeFqn = fqn.substring(scopeQualifier.length() + 1); + WidgetType widgetType = widgetTypeService.findWidgetTypeByTenantIdAndFqn(tenantId, typeFqn); + checkNotNull(widgetType); + + accessControlService.checkPermission(getCurrentUser(), Resource.WIDGET_TYPE, Operation.READ, widgetType.getId(), widgetType); + return widgetType; + } + } diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java index 737e6d54d5..1fe203249f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java @@ -30,19 +30,26 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.widgets.bundle.TbWidgetsBundleService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; +import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; import static org.thingsboard.server.controller.ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER; +import static org.thingsboard.server.controller.ControllerConstants.INLINE_IMAGES; +import static org.thingsboard.server.controller.ControllerConstants.INLINE_IMAGES_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS; import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; @@ -62,8 +69,11 @@ import static org.thingsboard.server.controller.ControllerConstants.WIDGET_BUNDL public class WidgetsBundleController extends BaseController { private final TbWidgetsBundleService tbWidgetsBundleService; + private final ImageService imageService; private static final String WIDGET_BUNDLE_DESCRIPTION = "Widget Bundle represents a group(bundle) of widgets. Widgets are grouped into bundle by type or use case. "; + private static final String FULL_SEARCH_PARAM_DESCRIPTION = "Optional boolean parameter indicating extended search of widget bundles by description and by name / description of related widget types"; + private static final String TENANT_BUNDLES_ONLY_DESCRIPTION = "Optional boolean parameter to include only tenant-level bundles without system"; @ApiOperation(value = "Get Widget Bundle (getWidgetsBundleById)", notes = "Get the Widget Bundle based on the provided Widget Bundle Id. " + WIDGET_BUNDLE_DESCRIPTION + AVAILABLE_FOR_ANY_AUTHORIZED_USER) @@ -72,10 +82,16 @@ public class WidgetsBundleController extends BaseController { @ResponseBody public WidgetsBundle getWidgetsBundleById( @ApiParam(value = WIDGET_BUNDLE_ID_PARAM_DESCRIPTION, required = true) - @PathVariable("widgetsBundleId") String strWidgetsBundleId) throws ThingsboardException { + @PathVariable("widgetsBundleId") String strWidgetsBundleId, + @ApiParam(value = INLINE_IMAGES_DESCRIPTION) + @RequestParam(value = INLINE_IMAGES, required = false) boolean inlineImages) throws ThingsboardException { checkParameter("widgetsBundleId", strWidgetsBundleId); WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); - return checkWidgetsBundleId(widgetsBundleId, Operation.READ); + var result = checkWidgetsBundleId(widgetsBundleId, Operation.READ); + if (inlineImages) { + imageService.inlineImage(result); + } + return result; } @ApiOperation(value = "Create Or Update Widget Bundle (saveWidgetsBundle)", @@ -106,6 +122,49 @@ public class WidgetsBundleController extends BaseController { return tbWidgetsBundleService.save(widgetsBundle, currentUser); } + @ApiOperation(value = "Update widgets bundle widgets types list (updateWidgetsBundleWidgetTypes)", + notes = "Updates widgets bundle widgets list." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @RequestMapping(value = "/widgetsBundle/{widgetsBundleId}/widgetTypes", method = RequestMethod.POST) + @ResponseStatus(value = HttpStatus.OK) + public void updateWidgetsBundleWidgetTypes( + @ApiParam(value = WIDGET_BUNDLE_ID_PARAM_DESCRIPTION, required = true) + @PathVariable("widgetsBundleId") String strWidgetsBundleId, + @ApiParam(value = "Ordered list of widget type Ids to be included by widgets bundle") + @RequestBody List strWidgetTypeIds) throws Exception { + checkParameter("widgetsBundleId", strWidgetsBundleId); + WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); + checkNotNull(strWidgetTypeIds); + Set widgetTypeIds = new LinkedHashSet<>(); + var currentUser = getCurrentUser(); + TenantId tenantId = currentUser.getTenantId(); + for (String strWidgetTypeId : strWidgetTypeIds) { + WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId)); + if (!widgetTypeIds.contains(widgetTypeId) && + widgetTypeService.widgetTypeExistsByTenantIdAndWidgetTypeId(tenantId, widgetTypeId)) { + widgetTypeIds.add(widgetTypeId); + } + } + tbWidgetsBundleService.updateWidgetsBundleWidgetTypes(widgetsBundleId, new ArrayList<>(widgetTypeIds), currentUser); + } + + @ApiOperation(value = "Update widgets bundle widgets list from widget type FQNs list (updateWidgetsBundleWidgetFqns)", + notes = "Updates widgets bundle widgets list from widget type FQNs list." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @RequestMapping(value = "/widgetsBundle/{widgetsBundleId}/widgetTypeFqns", method = RequestMethod.POST) + @ResponseStatus(value = HttpStatus.OK) + public void updateWidgetsBundleWidgetFqns( + @ApiParam(value = WIDGET_BUNDLE_ID_PARAM_DESCRIPTION, required = true) + @PathVariable("widgetsBundleId") String strWidgetsBundleId, + @ApiParam(value = "Ordered list of widget type FQNs to be included by widgets bundle") + @RequestBody List widgetTypeFqns) throws Exception { + checkParameter("widgetsBundleId", strWidgetsBundleId); + WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); + checkNotNull(widgetTypeFqns); + var currentUser = getCurrentUser(); + tbWidgetsBundleService.updateWidgetsBundleWidgetFqns(widgetsBundleId, widgetTypeFqns, currentUser); + } + @ApiOperation(value = "Delete widgets bundle (deleteWidgetsBundle)", notes = "Deletes the widget bundle. Referencing non-existing Widget Bundle Id will cause an error." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @@ -136,13 +195,21 @@ public class WidgetsBundleController extends BaseController { @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = WIDGET_BUNDLE_SORT_PROPERTY_ALLOWABLE_VALUES) @RequestParam(required = false) String sortProperty, @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) - @RequestParam(required = false) String sortOrder) throws ThingsboardException { + @RequestParam(required = false) String sortOrder, + @ApiParam(value = TENANT_BUNDLES_ONLY_DESCRIPTION) + @RequestParam(required = false) Boolean tenantOnly, + @ApiParam(value = FULL_SEARCH_PARAM_DESCRIPTION) + @RequestParam(required = false) Boolean fullSearch) throws ThingsboardException { PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) { - return checkNotNull(widgetsBundleService.findSystemWidgetsBundlesByPageLink(getTenantId(), pageLink)); + return checkNotNull(widgetsBundleService.findSystemWidgetsBundlesByPageLink(getTenantId(), fullSearch != null && fullSearch, pageLink)); } else { TenantId tenantId = getCurrentUser().getTenantId(); - return checkNotNull(widgetsBundleService.findAllTenantWidgetsBundlesByTenantIdAndPageLink(tenantId, pageLink)); + if (tenantOnly != null && tenantOnly) { + return checkNotNull(widgetsBundleService.findTenantWidgetsBundlesByTenantIdAndPageLink(tenantId, fullSearch != null && fullSearch, pageLink)); + } else { + return checkNotNull(widgetsBundleService.findAllTenantWidgetsBundlesByTenantIdAndPageLink(tenantId, fullSearch != null && fullSearch, pageLink)); + } } } diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java index bc51c5fdd1..3af6ff2ed0 100644 --- a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java +++ b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java @@ -15,12 +15,17 @@ */ package org.thingsboard.server.controller.plugin; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.RemovalCause; +import lombok.RequiredArgsConstructor; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.BeanCreationNotAllowedException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; -import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.PongMessage; @@ -28,60 +33,71 @@ import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.adapter.NativeWebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.config.WebSocketConfiguration; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; -import org.thingsboard.server.dao.util.limits.LimitedApi; import org.thingsboard.server.dao.util.limits.RateLimitService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.auth.jwt.JwtAuthenticationProvider; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.UserPrincipal; +import org.thingsboard.server.service.subscription.SubscriptionErrorCode; +import org.thingsboard.server.service.ws.AuthCmd; import org.thingsboard.server.service.ws.SessionEvent; import org.thingsboard.server.service.ws.WebSocketMsgEndpoint; import org.thingsboard.server.service.ws.WebSocketService; import org.thingsboard.server.service.ws.WebSocketSessionRef; import org.thingsboard.server.service.ws.WebSocketSessionType; +import org.thingsboard.server.service.ws.WsCommandsWrapper; +import org.thingsboard.server.service.ws.notification.cmd.NotificationCmdsWrapper; +import org.thingsboard.server.service.ws.telemetry.cmd.TelemetryCmdsWrapper; +import javax.annotation.PostConstruct; import javax.websocket.RemoteEndpoint; import javax.websocket.SendHandler; import javax.websocket.SendResult; import javax.websocket.Session; import java.io.IOException; -import java.net.URI; import java.security.InvalidParameterException; import java.util.Optional; import java.util.Queue; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import static org.thingsboard.server.service.ws.DefaultWebSocketService.NUMBER_OF_PING_ATTEMPTS; @Service @TbCoreComponent @Slf4j +@RequiredArgsConstructor public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocketMsgEndpoint { private final ConcurrentMap internalSessionMap = new ConcurrentHashMap<>(); private final ConcurrentMap externalSessionMap = new ConcurrentHashMap<>(); - @Autowired @Lazy private WebSocketService webSocketService; - @Autowired private TbTenantProfileCache tenantProfileCache; - @Autowired private RateLimitService rateLimitService; + @Autowired + private JwtAuthenticationProvider authenticationProvider; @Value("${server.ws.send_timeout:5000}") private long sendTimeout; @@ -89,6 +105,8 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke private long pingTimeout; @Value("${server.ws.max_queue_messages_per_session:1000}") private int wsMaxQueueMessagesPerSession; + @Value("${server.ws.auth_timeout_ms:10000}") + private int authTimeoutMs; private final ConcurrentMap blacklistedSessions = new ConcurrentHashMap<>(); @@ -97,28 +115,97 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke private final ConcurrentMap> regularUserSessionsMap = new ConcurrentHashMap<>(); private final ConcurrentMap> publicUserSessionsMap = new ConcurrentHashMap<>(); + private Cache pendingSessions; + + @PostConstruct + private void init() { + pendingSessions = Caffeine.newBuilder() + .expireAfterWrite(authTimeoutMs, TimeUnit.MILLISECONDS) + .removalListener((sessionId, sessionMd, removalCause) -> { + if (removalCause == RemovalCause.EXPIRED && sessionMd != null) { + try { + close(sessionMd.sessionRef, CloseStatus.POLICY_VIOLATION); + } catch (IOException e) { + log.warn("IO error", e); + } + } + }) + .build(); + } + @Override public void handleTextMessage(WebSocketSession session, TextMessage message) { try { - SessionMetaData sessionMd = internalSessionMap.get(session.getId()); - if (sessionMd != null) { - log.trace("[{}][{}] Processing {}", sessionMd.sessionRef.getSecurityCtx().getTenantId(), session.getId(), message.getPayload()); - webSocketService.handleWebSocketMsg(sessionMd.sessionRef, message.getPayload()); - } else { + SessionMetaData sessionMd = getSessionMd(session.getId()); + if (sessionMd == null) { log.trace("[{}] Failed to find session", session.getId()); session.close(CloseStatus.SERVER_ERROR.withReason("Session not found!")); + return; } + sessionMd.onMsg(message.getPayload()); } catch (IOException e) { log.warn("IO error", e); } } + void processMsg(SessionMetaData sessionMd, String msg) throws IOException { + WebSocketSessionRef sessionRef = sessionMd.sessionRef; + WsCommandsWrapper cmdsWrapper; + try { + switch (sessionRef.getSessionType()) { + case GENERAL: + cmdsWrapper = JacksonUtil.fromString(msg, WsCommandsWrapper.class); + break; + case TELEMETRY: + cmdsWrapper = JacksonUtil.fromString(msg, TelemetryCmdsWrapper.class).toCommonCmdsWrapper(); + break; + case NOTIFICATIONS: + cmdsWrapper = JacksonUtil.fromString(msg, NotificationCmdsWrapper.class).toCommonCmdsWrapper(); + break; + default: + return; + } + } catch (Exception e) { + log.debug("{} Failed to decode subscription cmd: {}", sessionRef, e.getMessage(), e); + if (sessionRef.getSecurityCtx() != null) { + webSocketService.sendError(sessionRef, 1, SubscriptionErrorCode.BAD_REQUEST, "Failed to parse the payload"); + } else { + close(sessionRef, CloseStatus.BAD_DATA.withReason(e.getMessage())); + } + return; + } + + if (sessionRef.getSecurityCtx() != null) { + log.trace("{} Processing {}", sessionRef, msg); + webSocketService.handleCommands(sessionRef, cmdsWrapper); + } else { + AuthCmd authCmd = cmdsWrapper.getAuthCmd(); + if (authCmd == null) { + close(sessionRef, CloseStatus.POLICY_VIOLATION.withReason("Auth cmd is missing")); + return; + } + log.trace("{} Authenticating session", sessionRef); + SecurityUser securityCtx; + try { + securityCtx = authenticationProvider.authenticate(authCmd.getToken()); + } catch (Exception e) { + close(sessionRef, CloseStatus.BAD_DATA.withReason(e.getMessage())); + return; + } + sessionRef.setSecurityCtx(securityCtx); + pendingSessions.invalidate(sessionMd.session.getId()); + establishSession(sessionMd.session, sessionRef, sessionMd); + + webSocketService.handleCommands(sessionRef, cmdsWrapper); + } + } + @Override protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception { try { - SessionMetaData sessionMd = internalSessionMap.get(session.getId()); + SessionMetaData sessionMd = getSessionMd(session.getId()); if (sessionMd != null) { - log.trace("[{}][{}] Processing pong response {}", sessionMd.sessionRef.getSecurityCtx().getTenantId(), session.getId(), message.getPayload()); + log.trace("{} Processing pong response {}", sessionMd.sessionRef, message.getPayload()); sessionMd.processPongMessage(System.currentTimeMillis()); } else { log.trace("[{}] Failed to find session", session.getId()); @@ -139,23 +226,9 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke nativeSession.getAsyncRemote().setSendTimeout(sendTimeout); } } - String internalSessionId = session.getId(); WebSocketSessionRef sessionRef = toRef(session); - String externalSessionId = sessionRef.getSessionId(); - - if (!checkLimits(session, sessionRef)) { - return; - } - var tenantProfileConfiguration = getTenantProfileConfiguration(sessionRef); - int wsTenantProfileQueueLimit = tenantProfileConfiguration != null ? - tenantProfileConfiguration.getWsMsgQueueLimitPerSession() : wsMaxQueueMessagesPerSession; - internalSessionMap.put(internalSessionId, new SessionMetaData(session, sessionRef, - (wsTenantProfileQueueLimit > 0 && wsTenantProfileQueueLimit < wsMaxQueueMessagesPerSession) ? - wsTenantProfileQueueLimit : wsMaxQueueMessagesPerSession)); - - externalSessionMap.put(externalSessionId, internalSessionId); - processInWebSocketService(sessionRef, SessionEvent.onEstablished()); - log.info("[{}][{}][{}] Session is opened from address: {}", sessionRef.getSecurityCtx().getTenantId(), externalSessionId, session.getId(), session.getRemoteAddress()); + log.debug("[{}][{}] Session opened from address: {}", sessionRef.getSessionId(), session.getId(), session.getRemoteAddress()); + establishSession(session, sessionRef, null); } catch (InvalidParameterException e) { log.warn("[{}] Failed to start session", session.getId(), e); session.close(CloseStatus.BAD_DATA.withReason(e.getMessage())); @@ -165,10 +238,36 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke } } + private void establishSession(WebSocketSession session, WebSocketSessionRef sessionRef, SessionMetaData sessionMd) throws IOException { + if (sessionRef.getSecurityCtx() != null) { + if (!checkLimits(session, sessionRef)) { + return; + } + int maxMsgQueueSize = Optional.ofNullable(getTenantProfileConfiguration(sessionRef)) + .map(DefaultTenantProfileConfiguration::getWsMsgQueueLimitPerSession) + .filter(profileLimit -> profileLimit > 0 && profileLimit < wsMaxQueueMessagesPerSession) + .orElse(wsMaxQueueMessagesPerSession); + if (sessionMd == null) { + sessionMd = new SessionMetaData(session, sessionRef); + } + sessionMd.setMaxMsgQueueSize(maxMsgQueueSize); + + internalSessionMap.put(session.getId(), sessionMd); + externalSessionMap.put(sessionRef.getSessionId(), session.getId()); + processInWebSocketService(sessionRef, SessionEvent.onEstablished()); + log.info("[{}][{}][{}][{}] Session established from address: {}", sessionRef.getSecurityCtx().getTenantId(), + sessionRef.getSecurityCtx().getId(), sessionRef.getSessionId(), session.getId(), session.getRemoteAddress()); + } else { + sessionMd = new SessionMetaData(session, sessionRef); + pendingSessions.put(session.getId(), sessionMd); + externalSessionMap.put(sessionRef.getSessionId(), session.getId()); + } + } + @Override public void handleTransportError(WebSocketSession session, Throwable tError) throws Exception { super.handleTransportError(session, tError); - SessionMetaData sessionMd = internalSessionMap.get(session.getId()); + SessionMetaData sessionMd = getSessionMd(session.getId()); if (sessionMd != null) { processInWebSocketService(sessionMd.sessionRef, SessionEvent.onError(tError)); } else { @@ -181,111 +280,131 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { super.afterConnectionClosed(session, closeStatus); SessionMetaData sessionMd = internalSessionMap.remove(session.getId()); + if (sessionMd == null) { + sessionMd = pendingSessions.asMap().remove(session.getId()); + } if (sessionMd != null) { - cleanupLimits(session, sessionMd.sessionRef); externalSessionMap.remove(sessionMd.sessionRef.getSessionId()); - processInWebSocketService(sessionMd.sessionRef, SessionEvent.onClosed()); - log.info("[{}][{}][{}] Session is closed", sessionMd.sessionRef.getSecurityCtx().getTenantId(), sessionMd.sessionRef.getSessionId(), session.getId()); + if (sessionMd.sessionRef.getSecurityCtx() != null) { + cleanupLimits(session, sessionMd.sessionRef); + processInWebSocketService(sessionMd.sessionRef, SessionEvent.onClosed()); + } + log.info("{} Session is closed", sessionMd.sessionRef); } else { log.info("[{}] Session is closed", session.getId()); } } private void processInWebSocketService(WebSocketSessionRef sessionRef, SessionEvent event) { + if (sessionRef.getSecurityCtx() == null) { + return; + } try { - webSocketService.handleWebSocketSessionEvent(sessionRef, event); + webSocketService.handleSessionEvent(sessionRef, event); } catch (BeanCreationNotAllowedException e) { - log.warn("[{}] Failed to close session due to possible shutdown state", sessionRef.getSessionId()); + log.warn("{} Failed to close session due to possible shutdown state", sessionRef); } } - private WebSocketSessionRef toRef(WebSocketSession session) throws IOException { - URI sessionUri = session.getUri(); - String path = sessionUri.getPath(); - path = path.substring(WebSocketConfiguration.WS_PLUGIN_PREFIX.length()); - if (path.length() == 0) { - throw new IllegalArgumentException("URL should contain plugin token!"); + private WebSocketSessionRef toRef(WebSocketSession session) { + String path = session.getUri().getPath(); + WebSocketSessionType sessionType; + if (path.equals(WebSocketConfiguration.WS_API_ENDPOINT)) { + sessionType = WebSocketSessionType.GENERAL; + } else { + String type = StringUtils.substringAfter(path, WebSocketConfiguration.WS_PLUGINS_ENDPOINT); + sessionType = WebSocketSessionType.forName(type) + .orElseThrow(() -> new InvalidParameterException("Unknown session type")); } - String[] pathElements = path.split("/"); - String serviceToken = pathElements[0]; - WebSocketSessionType sessionType = WebSocketSessionType.forName(serviceToken) - .orElseThrow(() -> new InvalidParameterException("Can't find plugin with specified token!")); - SecurityUser currentUser = (SecurityUser) ((Authentication) session.getPrincipal()).getPrincipal(); + SecurityUser securityCtx = null; + String token = StringUtils.substringAfter(session.getUri().getQuery(), "token="); + if (StringUtils.isNotEmpty(token)) { + securityCtx = authenticationProvider.authenticate(token); + } return WebSocketSessionRef.builder() .sessionId(UUID.randomUUID().toString()) - .securityCtx(currentUser) + .securityCtx(securityCtx) .localAddress(session.getLocalAddress()) .remoteAddress(session.getRemoteAddress()) .sessionType(sessionType) .build(); } - private class SessionMetaData implements SendHandler { + private SessionMetaData getSessionMd(String internalSessionId) { + SessionMetaData sessionMd = internalSessionMap.get(internalSessionId); + if (sessionMd == null) { + sessionMd = pendingSessions.getIfPresent(internalSessionId); + } + return sessionMd; + } + + class SessionMetaData implements SendHandler { private final WebSocketSession session; private final RemoteEndpoint.Async asyncRemote; private final WebSocketSessionRef sessionRef; - private final AtomicBoolean isSending = new AtomicBoolean(false); - private final Queue> msgQueue; + final AtomicBoolean isSending = new AtomicBoolean(false); + private final Queue> outboundMsgQueue = new ConcurrentLinkedQueue<>(); + private final AtomicInteger outboundMsgQueueSize = new AtomicInteger(); + @Setter + private int maxMsgQueueSize = wsMaxQueueMessagesPerSession; + + private final Queue inboundMsgQueue = new ConcurrentLinkedQueue<>(); + private final Lock inboundMsgQueueProcessorLock = new ReentrantLock(); private volatile long lastActivityTime; - SessionMetaData(WebSocketSession session, WebSocketSessionRef sessionRef, int maxMsgQueuePerSession) { + SessionMetaData(WebSocketSession session, WebSocketSessionRef sessionRef) { super(); this.session = session; Session nativeSession = ((NativeWebSocketSession) session).getNativeSession(Session.class); this.asyncRemote = nativeSession.getAsyncRemote(); this.sessionRef = sessionRef; - this.msgQueue = new LinkedBlockingQueue<>(maxMsgQueuePerSession); this.lastActivityTime = System.currentTimeMillis(); } - synchronized void sendPing(long currentTime) { + void sendPing(long currentTime) { try { long timeSinceLastActivity = currentTime - lastActivityTime; if (timeSinceLastActivity >= pingTimeout) { - log.warn("[{}] Closing session due to ping timeout", session.getId()); + log.warn("{} Closing session due to ping timeout", sessionRef); closeSession(CloseStatus.SESSION_NOT_RELIABLE); } else if (timeSinceLastActivity >= pingTimeout / NUMBER_OF_PING_ATTEMPTS) { sendMsg(TbWebSocketPingMsg.INSTANCE); } } catch (Exception e) { - log.trace("[{}] Failed to send ping msg", session.getId(), e); + log.trace("{} Failed to send ping msg", sessionRef, e); closeSession(CloseStatus.SESSION_NOT_RELIABLE); } } - private void closeSession(CloseStatus reason) { + void closeSession(CloseStatus reason) { try { close(this.sessionRef, reason); } catch (IOException ioe) { - log.trace("[{}] Session transport error", session.getId(), ioe); + log.trace("{} Session transport error", sessionRef, ioe); + } finally { + outboundMsgQueue.clear(); } } - synchronized void processPongMessage(long currentTime) { + void processPongMessage(long currentTime) { lastActivityTime = currentTime; } - synchronized void sendMsg(String msg) { + void sendMsg(String msg) { sendMsg(new TbWebSocketTextMsg(msg)); } - synchronized void sendMsg(TbWebSocketMsg msg) { - if (isSending.compareAndSet(false, true)) { - sendMsgInternal(msg); + void sendMsg(TbWebSocketMsg msg) { + if (outboundMsgQueueSize.get() < maxMsgQueueSize) { + outboundMsgQueue.add(msg); + outboundMsgQueueSize.incrementAndGet(); + processNextMsg(); } else { - try { - msgQueue.add(msg); - } catch (RuntimeException e) { - if (log.isTraceEnabled()) { - log.trace("[{}][{}] Session closed due to queue error", sessionRef.getSecurityCtx().getTenantId(), session.getId(), e); - } else { - log.info("[{}][{}] Session closed due to queue error", sessionRef.getSecurityCtx().getTenantId(), session.getId()); - } - closeSession(CloseStatus.POLICY_VIOLATION.withReason("Max pending updates limit reached!")); - } + log.info("{} Session closed due to updates queue size exceeded", sessionRef); + closeSession(CloseStatus.POLICY_VIOLATION.withReason("Max pending updates limit reached!")); } } @@ -294,13 +413,15 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke if (TbWebSocketMsgType.TEXT.equals(msg.getType())) { TbWebSocketTextMsg textMsg = (TbWebSocketTextMsg) msg; this.asyncRemote.sendText(textMsg.getMsg(), this); + // isSending status will be reset in the onResult method by call back } else { TbWebSocketPingMsg pingMsg = (TbWebSocketPingMsg) msg; - this.asyncRemote.sendPing(pingMsg.getMsg()); + this.asyncRemote.sendPing(pingMsg.getMsg()); // blocking call + isSending.set(false); processNextMsg(); } } catch (Exception e) { - log.trace("[{}] Failed to send msg", session.getId(), e); + log.trace("{} Failed to send msg", sessionRef, e); closeSession(CloseStatus.SESSION_NOT_RELIABLE); } } @@ -308,27 +429,55 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke @Override public void onResult(SendResult result) { if (!result.isOK()) { - log.trace("[{}] Failed to send msg", session.getId(), result.getException()); + log.trace("{} Failed to send msg", sessionRef, result.getException()); closeSession(CloseStatus.SESSION_NOT_RELIABLE); - } else { - processNextMsg(); + return; } + + isSending.set(false); + processNextMsg(); } private void processNextMsg() { - TbWebSocketMsg msg = msgQueue.poll(); + if (outboundMsgQueue.isEmpty() || !isSending.compareAndSet(false, true)) { + return; + } + TbWebSocketMsg msg = outboundMsgQueue.poll(); if (msg != null) { + outboundMsgQueueSize.decrementAndGet(); sendMsgInternal(msg); } else { isSending.set(false); } } + + public void onMsg(String msg) throws IOException { + inboundMsgQueue.add(msg); + tryProcessInboundMsgs(); + } + + void tryProcessInboundMsgs() throws IOException { + while (!inboundMsgQueue.isEmpty()) { + if (inboundMsgQueueProcessorLock.tryLock()) { + try { + String msg; + while ((msg = inboundMsgQueue.poll()) != null) { + processMsg(this, msg); + } + } finally { + inboundMsgQueueProcessorLock.unlock(); + } + } else { + return; + } + } + } } @Override public void send(WebSocketSessionRef sessionRef, int subscriptionId, String msg) throws IOException { + log.debug("{} Sending {}", sessionRef, msg); String externalId = sessionRef.getSessionId(); - log.debug("[{}] Processing {}", externalId, msg); String internalId = externalSessionMap.get(externalId); if (internalId != null) { SessionMetaData sessionMd = internalSessionMap.get(internalId); @@ -336,13 +485,12 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke TenantId tenantId = sessionRef.getSecurityCtx().getTenantId(); if (!rateLimitService.checkRateLimit(LimitedApi.WS_UPDATES_PER_SESSION, tenantId, (Object) sessionRef.getSessionId())) { if (blacklistedSessions.putIfAbsent(externalId, sessionRef) == null) { - log.info("[{}][{}][{}] Failed to process session update. Max session updates limit reached" - , tenantId, sessionRef.getSecurityCtx().getId(), externalId); + log.info("{} Failed to process session update. Max session updates limit reached", sessionRef); sessionMd.sendMsg("{\"subscriptionId\":" + subscriptionId + ", \"errorCode\":" + ThingsboardErrorCode.TOO_MANY_UPDATES.getErrorCode() + ", \"errorMsg\":\"Too many updates!\"}"); } return; } else { - log.debug("[{}][{}][{}] Session is no longer blacklisted.", tenantId, sessionRef.getSecurityCtx().getId(), externalId); + log.debug("{} Session is no longer blacklisted.", sessionRef); blacklistedSessions.remove(externalId); } sessionMd.sendMsg(msg); @@ -373,10 +521,10 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke @Override public void close(WebSocketSessionRef sessionRef, CloseStatus reason) throws IOException { String externalId = sessionRef.getSessionId(); - log.debug("[{}] Processing close request", externalId); + log.debug("{} Processing close request", sessionRef.toString()); String internalId = externalSessionMap.get(externalId); if (internalId != null) { - SessionMetaData sessionMd = internalSessionMap.get(internalId); + SessionMetaData sessionMd = getSessionMd(internalId); if (sessionMd != null) { sessionMd.session.close(reason); } else { @@ -387,68 +535,72 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements WebSocke } } - private boolean checkLimits(WebSocketSession session, WebSocketSessionRef sessionRef) throws Exception { + private boolean checkLimits(WebSocketSession session, WebSocketSessionRef sessionRef) throws IOException { var tenantProfileConfiguration = getTenantProfileConfiguration(sessionRef); if (tenantProfileConfiguration == null) { return true; } - + boolean limitAllowed; String sessionId = session.getId(); if (tenantProfileConfiguration.getMaxWsSessionsPerTenant() > 0) { Set tenantSessions = tenantSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getTenantId(), id -> ConcurrentHashMap.newKeySet()); synchronized (tenantSessions) { - if (tenantSessions.size() < tenantProfileConfiguration.getMaxWsSessionsPerTenant()) { + limitAllowed = tenantSessions.size() < tenantProfileConfiguration.getMaxWsSessionsPerTenant(); + if (limitAllowed) { tenantSessions.add(sessionId); - } else { - log.info("[{}][{}][{}] Failed to start session. Max tenant sessions limit reached" - , sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), sessionId); - session.close(CloseStatus.POLICY_VIOLATION.withReason("Max tenant sessions limit reached!")); - return false; } } + if (!limitAllowed) { + log.info("{} Failed to start session. Max tenant sessions limit reached", sessionRef.toString()); + session.close(CloseStatus.POLICY_VIOLATION.withReason("Max tenant sessions limit reached!")); + return false; + } } if (sessionRef.getSecurityCtx().isCustomerUser()) { if (tenantProfileConfiguration.getMaxWsSessionsPerCustomer() > 0) { Set customerSessions = customerSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getCustomerId(), id -> ConcurrentHashMap.newKeySet()); synchronized (customerSessions) { - if (customerSessions.size() < tenantProfileConfiguration.getMaxWsSessionsPerCustomer()) { + limitAllowed = customerSessions.size() < tenantProfileConfiguration.getMaxWsSessionsPerCustomer(); + if (limitAllowed) { customerSessions.add(sessionId); - } else { - log.info("[{}][{}][{}] Failed to start session. Max customer sessions limit reached" - , sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), sessionId); - session.close(CloseStatus.POLICY_VIOLATION.withReason("Max customer sessions limit reached")); - return false; } } + if (!limitAllowed) { + log.info("{} Failed to start session. Max customer sessions limit reached", sessionRef.toString()); + session.close(CloseStatus.POLICY_VIOLATION.withReason("Max customer sessions limit reached")); + return false; + } } if (tenantProfileConfiguration.getMaxWsSessionsPerRegularUser() > 0 && UserPrincipal.Type.USER_NAME.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) { Set regularUserSessions = regularUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet()); synchronized (regularUserSessions) { - if (regularUserSessions.size() < tenantProfileConfiguration.getMaxWsSessionsPerRegularUser()) { + limitAllowed = regularUserSessions.size() < tenantProfileConfiguration.getMaxWsSessionsPerRegularUser(); + if (limitAllowed) { regularUserSessions.add(sessionId); - } else { - log.info("[{}][{}][{}] Failed to start session. Max regular user sessions limit reached" - , sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), sessionId); - session.close(CloseStatus.POLICY_VIOLATION.withReason("Max regular user sessions limit reached")); - return false; } } + if (!limitAllowed) { + log.info("{} Failed to start session. Max regular user sessions limit reached", sessionRef.toString()); + session.close(CloseStatus.POLICY_VIOLATION.withReason("Max regular user sessions limit reached")); + return false; + } } if (tenantProfileConfiguration.getMaxWsSessionsPerPublicUser() > 0 && UserPrincipal.Type.PUBLIC_ID.equals(sessionRef.getSecurityCtx().getUserPrincipal().getType())) { Set publicUserSessions = publicUserSessionsMap.computeIfAbsent(sessionRef.getSecurityCtx().getId(), id -> ConcurrentHashMap.newKeySet()); synchronized (publicUserSessions) { - if (publicUserSessions.size() < tenantProfileConfiguration.getMaxWsSessionsPerPublicUser()) { + limitAllowed = publicUserSessions.size() < tenantProfileConfiguration.getMaxWsSessionsPerPublicUser(); + if (limitAllowed) { publicUserSessions.add(sessionId); - } else { - log.info("[{}][{}][{}] Failed to start session. Max public user sessions limit reached" - , sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), sessionId); - session.close(CloseStatus.POLICY_VIOLATION.withReason("Max public user sessions limit reached")); - return false; } } + if (!limitAllowed) { + log.info("{} Failed to start session. Max public user sessions limit reached", sessionRef.toString()); + session.close(CloseStatus.POLICY_VIOLATION.withReason("Max public user sessions limit reached")); + return false; + } } } return true; diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardCredentialsViolationResponse.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardCredentialsViolationResponse.java new file mode 100644 index 0000000000..421ca04a34 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardCredentialsViolationResponse.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.exception; + +import io.swagger.annotations.ApiModel; +import org.springframework.http.HttpStatus; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; + +@ApiModel +public class ThingsboardCredentialsViolationResponse extends ThingsboardErrorResponse { + + protected ThingsboardCredentialsViolationResponse(String message) { + super(message, ThingsboardErrorCode.PASSWORD_VIOLATION, HttpStatus.UNAUTHORIZED); + } + + public static ThingsboardCredentialsViolationResponse of(final String message) { + return new ThingsboardCredentialsViolationResponse(message); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java index ed641c2d4f..fbb2f1d8e2 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java @@ -16,7 +16,6 @@ package org.thingsboard.server.exception; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -42,6 +41,7 @@ import org.thingsboard.server.common.msg.tools.TbRateLimitsException; import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException; import org.thingsboard.server.service.security.exception.JwtExpiredTokenException; import org.thingsboard.server.service.security.exception.UserPasswordExpiredException; +import org.thingsboard.server.service.security.exception.UserPasswordNotValidException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -192,6 +192,9 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand UserPasswordExpiredException expiredException = (UserPasswordExpiredException) authenticationException; String resetToken = expiredException.getResetToken(); JacksonUtil.writeValue(response.getWriter(), ThingsboardCredentialsExpiredResponse.of(expiredException.getMessage(), resetToken)); + } else if (authenticationException instanceof UserPasswordNotValidException) { + UserPasswordNotValidException expiredException = (UserPasswordNotValidException) authenticationException; + JacksonUtil.writeValue(response.getWriter(), ThingsboardCredentialsViolationResponse.of(expiredException.getMessage())); } else { JacksonUtil.writeValue(response.getWriter(), ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)); } diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index 51d7435f0e..5ab3b7b28e 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -108,10 +108,12 @@ public class ThingsboardInstallService { log.info("Migrating ThingsBoard entities data from cassandra to SQL database ..."); entitiesMigrateService.migrate(); log.info("Updating system data..."); - systemDataLoaderService.updateSystemWidgets(); + systemDataLoaderService.loadSystemWidgets(); } else if ("3.0.1-cassandra".equals(upgradeFromVersion)) { log.info("Migrating ThingsBoard latest timeseries data from cassandra to SQL database ..."); latestMigrateService.migrate(); + } else if (upgradeFromVersion.equals("3.6.2-images")) { + installScripts.updateImages(); } else { switch (upgradeFromVersion) { case "1.2.3": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion @@ -258,8 +260,22 @@ public class ThingsboardInstallService { log.info("Upgrading ThingsBoard from version 3.5.0 to 3.5.1 ..."); databaseEntitiesUpgradeService.upgradeDatabase("3.5.0"); case "3.5.1": - log.info("Upgrading ThingsBoard from version 3.5.1 to 3.5.2 ..."); + log.info("Upgrading ThingsBoard from version 3.5.1 to 3.6.0 ..."); databaseEntitiesUpgradeService.upgradeDatabase("3.5.1"); + dataUpdateService.updateData("3.5.1"); + systemDataLoaderService.updateDefaultNotificationConfigs(); + case "3.6.0": + log.info("Upgrading ThingsBoard from version 3.6.0 to 3.6.1 ..."); + databaseEntitiesUpgradeService.upgradeDatabase("3.6.0"); + dataUpdateService.updateData("3.6.0"); + case "3.6.1": + log.info("Upgrading ThingsBoard from version 3.6.1 to 3.6.2 ..."); + databaseEntitiesUpgradeService.upgradeDatabase("3.6.1"); + if (!getEnv("SKIP_IMAGES_MIGRATION", false)) { + installScripts.setUpdateImages(true); + } else { + log.info("Skipping images migration. Run the upgrade with fromVersion as '3.6.2-images' to migrate"); + } //TODO DON'T FORGET to update switch statement in the CacheCleanupService if you need to clear the cache break; default: @@ -268,8 +284,13 @@ public class ThingsboardInstallService { entityDatabaseSchemaService.createOrUpdateViewsAndFunctions(); entityDatabaseSchemaService.createOrUpdateDeviceInfoView(persistToTelemetry); log.info("Updating system data..."); - systemDataLoaderService.updateSystemWidgets(); + dataUpdateService.upgradeRuleNodes(); + systemDataLoaderService.loadSystemWidgets(); installScripts.loadSystemLwm2mResources(); + installScripts.loadSystemImages(); + if (installScripts.isUpdateImages()) { + installScripts.updateImages(); + } } log.info("Upgrade finished successfully!"); @@ -312,6 +333,7 @@ public class ThingsboardInstallService { // systemDataLoaderService.loadSystemPlugins(); // systemDataLoaderService.loadSystemRules(); installScripts.loadSystemLwm2mResources(); + installScripts.loadSystemImages(); if (loadDemo) { log.info("Loading demo data..."); diff --git a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java index 99b508a12d..77cc275ab2 100644 --- a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java +++ b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java @@ -26,31 +26,32 @@ import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmComment; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.audit.ActionType; -import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.notification.rule.trigger.AlarmAssignmentTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.AlarmCommentTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.EntitiesLimitTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.EntityActionTrigger; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; -import org.thingsboard.server.common.msg.notification.trigger.AlarmAssignmentTrigger; -import org.thingsboard.server.common.msg.notification.trigger.AlarmCommentTrigger; -import org.thingsboard.server.common.msg.notification.trigger.EntitiesLimitTrigger; -import org.thingsboard.server.common.msg.notification.trigger.EntityActionTrigger; import org.thingsboard.server.dao.audit.AuditLogService; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; @Service @@ -63,85 +64,8 @@ public class EntityActionService { public void pushEntityActionToRuleEngine(EntityId entityId, HasName entity, TenantId tenantId, CustomerId customerId, ActionType actionType, User user, Object... additionalInfo) { - String msgType = null; - switch (actionType) { - case ADDED: - msgType = DataConstants.ENTITY_CREATED; - break; - case DELETED: - msgType = DataConstants.ENTITY_DELETED; - break; - case UPDATED: - msgType = DataConstants.ENTITY_UPDATED; - break; - case ASSIGNED_TO_CUSTOMER: - msgType = DataConstants.ENTITY_ASSIGNED; - break; - case UNASSIGNED_FROM_CUSTOMER: - msgType = DataConstants.ENTITY_UNASSIGNED; - break; - case ATTRIBUTES_UPDATED: - msgType = DataConstants.ATTRIBUTES_UPDATED; - break; - case ATTRIBUTES_DELETED: - msgType = DataConstants.ATTRIBUTES_DELETED; - break; - case ALARM_ACK: - msgType = DataConstants.ALARM_ACK; - break; - case ALARM_CLEAR: - msgType = DataConstants.ALARM_CLEAR; - break; - case ALARM_ASSIGNED: - msgType = DataConstants.ALARM_ASSIGNED; - break; - case ALARM_UNASSIGNED: - msgType = DataConstants.ALARM_UNASSIGNED; - break; - case ALARM_DELETE: - msgType = DataConstants.ALARM_DELETE; - break; - case ADDED_COMMENT: - msgType = DataConstants.COMMENT_CREATED; - break; - case UPDATED_COMMENT: - msgType = DataConstants.COMMENT_UPDATED; - break; - case ASSIGNED_FROM_TENANT: - msgType = DataConstants.ENTITY_ASSIGNED_FROM_TENANT; - break; - case ASSIGNED_TO_TENANT: - msgType = DataConstants.ENTITY_ASSIGNED_TO_TENANT; - break; - case PROVISION_SUCCESS: - msgType = DataConstants.PROVISION_SUCCESS; - break; - case PROVISION_FAILURE: - msgType = DataConstants.PROVISION_FAILURE; - break; - case TIMESERIES_UPDATED: - msgType = DataConstants.TIMESERIES_UPDATED; - break; - case TIMESERIES_DELETED: - msgType = DataConstants.TIMESERIES_DELETED; - break; - case ASSIGNED_TO_EDGE: - msgType = DataConstants.ENTITY_ASSIGNED_TO_EDGE; - break; - case UNASSIGNED_FROM_EDGE: - msgType = DataConstants.ENTITY_UNASSIGNED_FROM_EDGE; - break; - case RELATION_ADD_OR_UPDATE: - msgType = DataConstants.RELATION_ADD_OR_UPDATE; - break; - case RELATION_DELETED: - msgType = DataConstants.RELATION_DELETED; - break; - case RELATIONS_DELETED: - msgType = DataConstants.RELATIONS_DELETED; - break; - } - if (!StringUtils.isEmpty(msgType)) { + Optional msgType = actionType.getRuleEngineMsgType(); + if (msgType.isPresent()) { try { TbMsgMetaData metaData = new TbMsgMetaData(); if (user != null) { @@ -247,7 +171,7 @@ public class EntityActionService { if (tenantId != null && !tenantId.isSysTenantId()) { processNotificationRules(tenantId, entityId, entity, actionType, user, additionalInfo); } - TbMsg tbMsg = TbMsg.newMsg(msgType, entityId, customerId, metaData, TbMsgDataType.JSON, JacksonUtil.toString(entityNode)); + TbMsg tbMsg = TbMsg.newMsg(msgType.get(), entityId, customerId, metaData, TbMsgDataType.JSON, JacksonUtil.toString(entityNode)); tbClusterService.pushMsgToRuleEngine(tenantId, entityId, tbMsg, null); } catch (Exception e) { log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e); @@ -255,7 +179,8 @@ public class EntityActionService { } } - private void processNotificationRules(TenantId tenantId, EntityId entityId, HasName entity, ActionType actionType, User user, Object... additionalInfo) { + private void processNotificationRules(TenantId tenantId, EntityId originatorId, HasName entity, ActionType actionType, User user, Object... additionalInfo) { + EntityId entityId = entity instanceof HasId ? ((HasId) entity).getId() : originatorId; switch (actionType) { case ADDED: notificationRuleProcessor.process(EntitiesLimitTrigger.builder() @@ -313,10 +238,6 @@ public class EntityActionService { auditLogService.logEntityAction(user.getTenantId(), customerId, user.getId(), user.getName(), entityId, entity, actionType, e, additionalInfo); } - public void sendEntityNotificationMsgToEdge(TenantId tenantId, EntityId entityId, EdgeEventActionType action) { - tbClusterService.sendNotificationMsgToEdge(tenantId, null, entityId, null, null, action); - } - private T extractParameter(Class clazz, int index, Object... additionalInfo) { T result = null; if (additionalInfo != null && additionalInfo.length > index) { diff --git a/application/src/main/java/org/thingsboard/server/service/apiusage/BaseApiUsageState.java b/application/src/main/java/org/thingsboard/server/service/apiusage/BaseApiUsageState.java index 1215bef0bf..9ae25fe2ef 100644 --- a/application/src/main/java/org/thingsboard/server/service/apiusage/BaseApiUsageState.java +++ b/application/src/main/java/org/thingsboard/server/service/apiusage/BaseApiUsageState.java @@ -145,6 +145,8 @@ public abstract class BaseApiUsageState { return apiUsageState.getDbStorageState(); case JS: return apiUsageState.getJsExecState(); + case TBEL: + return apiUsageState.getTbelExecState(); case EMAIL: return apiUsageState.getEmailExecState(); case SMS: @@ -171,6 +173,9 @@ public abstract class BaseApiUsageState { case JS: apiUsageState.setJsExecState(value); break; + case TBEL: + apiUsageState.setTbelExecState(value); + break; case EMAIL: apiUsageState.setEmailExecState(value); break; diff --git a/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java b/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java index 7e2719598c..34b617f1d3 100644 --- a/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java @@ -48,7 +48,7 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.common.data.tenant.profile.TenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; -import org.thingsboard.server.common.msg.notification.trigger.ApiUsageLimitTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.ApiUsageLimitTrigger; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; @@ -353,6 +353,9 @@ public class DefaultTbApiUsageStateService extends AbstractPartitionBasedService String email = tenantService.findTenantById(state.getTenantId()).getEmail(); result.forEach((apiFeature, stateValue) -> { ApiUsageRecordState recordState = createApiUsageRecordState((TenantApiUsageState) state, apiFeature, stateValue); + if (recordState == null) { + return; + } notificationRuleProcessor.process(ApiUsageLimitTrigger.builder() .tenantId(state.getTenantId()) .state(recordState) diff --git a/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java index 1f5e41aeed..d5f150ce24 100644 --- a/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java +++ b/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java @@ -30,8 +30,12 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.NodeConfiguration; import org.thingsboard.rule.engine.api.NodeDefinition; import org.thingsboard.rule.engine.api.RuleNode; -import org.thingsboard.rule.engine.api.TbRelationTypes; +import org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode; +import org.thingsboard.rule.engine.filter.TbOriginatorTypeSwitchNode; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.rule.RuleChainType; @@ -48,12 +52,14 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; @Service @Slf4j public class AnnotationComponentDiscoveryService implements ComponentDiscoveryService { public static final int MAX_OPTIMISITC_RETRIES = 3; + @Value("${plugins.scan_packages}") private String[] scanPackages; @@ -63,11 +69,13 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @Autowired private ComponentDescriptorService componentDescriptorService; - private Map components = new HashMap<>(); + private final Map ruleNodeClasses = new HashMap<>(); + + private final Map components = new HashMap<>(); - private Map> coreComponentsMap = new HashMap<>(); + private final Map> coreComponentsMap = new HashMap<>(); - private Map> edgeComponentsMap = new HashMap<>(); + private final Map> edgeComponentsMap = new HashMap<>(); private boolean isInstall() { return environment.acceptsProfiles(Profiles.of("install")); @@ -75,28 +83,54 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @PostConstruct public void init() { + for (var def : discoverBeansByAnnotationType(RuleNode.class)) { + String clazzName = def.getBeanClassName(); + try { + var clazz = Class.forName(clazzName); + RuleNode annotation = clazz.getAnnotation(RuleNode.class); + ruleNodeClasses.put(clazzName, new RuleNodeClassInfo(clazz, annotation)); + } catch (Exception e) { + log.warn("Failed to create instance of rule node type: {} due to: ", clazzName, e); + } + } if (!isInstall()) { discoverComponents(); } } + private Set discoverBeansByAnnotationType(Class annotationType) { + ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); + scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType)); + Set defs = new HashSet<>(); + for (String scanPackage : scanPackages) { + defs.addAll(scanner.findCandidateComponents(scanPackage)); + } + return defs; + } + + @Override + public Optional getRuleNodeInfo(String clazz) { + return Optional.ofNullable(ruleNodeClasses.get(clazz)); + } + + @Override + public List getVersionedNodes() { + return ruleNodeClasses.values().stream().filter(RuleNodeClassInfo::isVersioned).collect(Collectors.toList()); + } + private void registerRuleNodeComponents() { - Set ruleNodeBeanDefinitions = getBeanDefinitions(RuleNode.class); - for (BeanDefinition def : ruleNodeBeanDefinitions) { + for (RuleNodeClassInfo def : ruleNodeClasses.values()) { int retryCount = 0; Exception cause = null; while (retryCount < MAX_OPTIMISITC_RETRIES) { try { - String clazzName = def.getBeanClassName(); - Class clazz = Class.forName(clazzName); - RuleNode ruleNodeAnnotation = clazz.getAnnotation(RuleNode.class); - ComponentType type = ruleNodeAnnotation.type(); + ComponentType type = def.getAnnotation().type(); ComponentDescriptor component = scanAndPersistComponent(def, type); components.put(component.getClazz(), component); - putComponentIntoMaps(type, ruleNodeAnnotation, component); + putComponentIntoMaps(type, def.getAnnotation(), component); break; } catch (Exception e) { - log.trace("Can't initialize component {}, due to {}", def.getBeanClassName(), e.getMessage(), e); + log.trace("Can't initialize component {}, due to {}", def.getClassName(), e.getMessage(), e); cause = e; retryCount++; try { @@ -107,7 +141,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe } } if (cause != null && retryCount == MAX_OPTIMISITC_RETRIES) { - log.error("Can't initialize component {}, due to {}", def.getBeanClassName(), cause.getMessage(), cause); + log.error("Can't initialize component {}, due to {}", def.getClassName(), cause.getMessage(), cause); throw new RuntimeException(cause); } } @@ -144,17 +178,18 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe return false; } - private ComponentDescriptor scanAndPersistComponent(BeanDefinition def, ComponentType type) { + private ComponentDescriptor scanAndPersistComponent(RuleNodeClassInfo def, ComponentType type) { ComponentDescriptor scannedComponent = new ComponentDescriptor(); - String clazzName = def.getBeanClassName(); + String clazzName = def.getClassName(); try { scannedComponent.setType(type); - Class clazz = Class.forName(clazzName); + Class clazz = def.getClazz(); RuleNode ruleNodeAnnotation = clazz.getAnnotation(RuleNode.class); + scannedComponent.setConfigurationVersion(def.getCurrentVersion()); scannedComponent.setName(ruleNodeAnnotation.name()); scannedComponent.setScope(ruleNodeAnnotation.scope()); scannedComponent.setClusteringMode(ruleNodeAnnotation.clusteringMode()); - NodeDefinition nodeDefinition = prepareNodeDefinition(ruleNodeAnnotation); + NodeDefinition nodeDefinition = prepareNodeDefinition(clazz, ruleNodeAnnotation); ObjectNode configurationDescriptor = JacksonUtil.newObjectNode(); JsonNode node = JacksonUtil.valueToTree(nodeDefinition); configurationDescriptor.set("nodeDefinition", node); @@ -162,7 +197,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe scannedComponent.setClazz(clazzName); log.debug("Processing scanned component: {}", scannedComponent); } catch (Exception e) { - log.error("Can't initialize component {}, due to {}", def.getBeanClassName(), e.getMessage(), e); + log.error("Can't initialize component {}, due to {}", clazzName, e.getMessage(), e); throw new RuntimeException(e); } ComponentDescriptor persistedComponent = componentDescriptorService.findByClazz(TenantId.SYS_TENANT_ID, clazzName); @@ -181,13 +216,13 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe return scannedComponent; } - private NodeDefinition prepareNodeDefinition(RuleNode nodeAnnotation) throws Exception { + private NodeDefinition prepareNodeDefinition(Class clazz, RuleNode nodeAnnotation) throws Exception { NodeDefinition nodeDefinition = new NodeDefinition(); nodeDefinition.setDetails(nodeAnnotation.nodeDetails()); nodeDefinition.setDescription(nodeAnnotation.nodeDescription()); nodeDefinition.setInEnabled(nodeAnnotation.inEnabled()); nodeDefinition.setOutEnabled(nodeAnnotation.outEnabled()); - nodeDefinition.setRelationTypes(getRelationTypesWithFailureRelation(nodeAnnotation)); + nodeDefinition.setRelationTypes(getRelationTypesWithFailureRelation(clazz, nodeAnnotation)); nodeDefinition.setCustomRelations(nodeAnnotation.customRelations()); nodeDefinition.setRuleChainNode(nodeAnnotation.ruleChainNode()); Class configClazz = nodeAnnotation.configClazz(); @@ -202,22 +237,19 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe return nodeDefinition; } - private String[] getRelationTypesWithFailureRelation(RuleNode nodeAnnotation) { + private String[] getRelationTypesWithFailureRelation(Class clazz, RuleNode nodeAnnotation) { List relationTypes = new ArrayList<>(Arrays.asList(nodeAnnotation.relationTypes())); - if (!relationTypes.contains(TbRelationTypes.FAILURE)) { - relationTypes.add(TbRelationTypes.FAILURE); + if (TbOriginatorTypeSwitchNode.class.equals(clazz)) { + relationTypes.addAll(EntityType.NORMAL_NAMES); } - return relationTypes.toArray(new String[relationTypes.size()]); - } - - private Set getBeanDefinitions(Class componentType) { - ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); - scanner.addIncludeFilter(new AnnotationTypeFilter(componentType)); - Set defs = new HashSet<>(); - for (String scanPackage : scanPackages) { - defs.addAll(scanner.findCandidateComponents(scanPackage)); + if (TbMsgTypeSwitchNode.class.equals(clazz)) { + relationTypes.addAll(TbMsgType.NODE_CONNECTIONS); + relationTypes.add(TbNodeConnectionType.OTHER); } - return defs; + if (!relationTypes.contains(TbNodeConnectionType.FAILURE)) { + relationTypes.add(TbNodeConnectionType.FAILURE); + } + return relationTypes.toArray(new String[relationTypes.size()]); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java index 731009575d..d0b0804f9b 100644 --- a/application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java +++ b/application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java @@ -19,7 +19,9 @@ import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.rule.RuleChainType; +import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; @@ -30,6 +32,10 @@ public interface ComponentDiscoveryService { void discoverComponents(); + Optional getRuleNodeInfo(String clazz); + + List getVersionedNodes(); + List getComponents(ComponentType type, RuleChainType ruleChainType); List getComponents(Set types, RuleChainType ruleChainType); diff --git a/application/src/main/java/org/thingsboard/server/service/component/RuleNodeClassInfo.java b/application/src/main/java/org/thingsboard/server/service/component/RuleNodeClassInfo.java new file mode 100644 index 0000000000..317ec4ab9d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/component/RuleNodeClassInfo.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.component; + +import lombok.Data; +import org.thingsboard.rule.engine.api.RuleNode; + +@Data +public class RuleNodeClassInfo { + + private final Class clazz; + private final RuleNode annotation; + + public String getClassName(){ + return clazz.getName(); + } + + public String getSimpleName() { + return clazz.getSimpleName(); + } + + public int getCurrentVersion() { + return annotation.version(); + } + + public boolean isVersioned() { + return annotation.version() > 0; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java index 509e0fdbef..a37e03de1b 100644 --- a/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java @@ -33,7 +33,10 @@ import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredential; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode; +import org.thingsboard.server.common.data.device.data.DefaultDeviceConfiguration; +import org.thingsboard.server.common.data.device.data.DeviceData; import org.thingsboard.server.common.data.device.data.PowerMode; +import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; @@ -45,6 +48,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportColumnType; +import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion; import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; @@ -76,18 +80,18 @@ public class DeviceBulkImportService extends AbstractBulkImportService { private final Lock findOrCreateDeviceProfileLock = new ReentrantLock(); @Override - protected void setEntityFields(Device entity, Map fields) { - ObjectNode additionalInfo = getOrCreateAdditionalInfoObj(entity); + protected void setEntityFields(Device device, Map fields) { + ObjectNode additionalInfo = getOrCreateAdditionalInfoObj(device); fields.forEach((columnType, value) -> { switch (columnType) { case NAME: - entity.setName(value); + device.setName(value); break; case TYPE: - entity.setType(value); + device.setType(value); break; case LABEL: - entity.setLabel(value); + device.setLabel(value); break; case DESCRIPTION: additionalInfo.set("description", new TextNode(value)); @@ -96,16 +100,17 @@ public class DeviceBulkImportService extends AbstractBulkImportService { additionalInfo.set("gateway", BooleanNode.valueOf(Boolean.parseBoolean(value))); break; } - entity.setAdditionalInfo(additionalInfo); + device.setAdditionalInfo(additionalInfo); }); + setUpDeviceConfiguration(device, fields); } @Override @SneakyThrows - protected Device saveEntity(SecurityUser user, Device entity, Map fields) { + protected Device saveEntity(SecurityUser user, Device device, Map fields) { DeviceCredentials deviceCredentials; try { - deviceCredentials = createDeviceCredentials(entity.getTenantId(), entity.getId(), fields); + deviceCredentials = createDeviceCredentials(device.getTenantId(), device.getId(), fields); deviceCredentialsService.formatCredentials(deviceCredentials); } catch (Exception e) { throw new DeviceCredentialsValidationException("Invalid device credentials: " + e.getMessage()); @@ -113,15 +118,15 @@ public class DeviceBulkImportService extends AbstractBulkImportService { DeviceProfile deviceProfile; if (deviceCredentials.getCredentialsType() == DeviceCredentialsType.LWM2M_CREDENTIALS) { - deviceProfile = setUpLwM2mDeviceProfile(entity.getTenantId(), entity); - } else if (StringUtils.isNotEmpty(entity.getType())) { - deviceProfile = deviceProfileService.findOrCreateDeviceProfile(entity.getTenantId(), entity.getType()); + deviceProfile = setUpLwM2mDeviceProfile(device.getTenantId(), device); + } else if (StringUtils.isNotEmpty(device.getType())) { + deviceProfile = deviceProfileService.findOrCreateDeviceProfile(device.getTenantId(), device.getType()); } else { - deviceProfile = deviceProfileService.findDefaultDeviceProfile(entity.getTenantId()); + deviceProfile = deviceProfileService.findDefaultDeviceProfile(device.getTenantId()); } - entity.setDeviceProfileId(deviceProfile.getId()); + device.setDeviceProfileId(deviceProfile.getId()); - return tbDeviceService.saveDeviceWithCredentials(entity, deviceCredentials, user); + return tbDeviceService.saveDeviceWithCredentials(device, deviceCredentials, user); } @Override @@ -136,6 +141,22 @@ public class DeviceBulkImportService extends AbstractBulkImportService { entity.setCustomerId(user.getCustomerId()); } + private void setUpDeviceConfiguration(Device device, Map fields) { + if (fields.containsKey(BulkImportColumnType.SNMP_HOST)) { + SnmpDeviceTransportConfiguration transportConfiguration = new SnmpDeviceTransportConfiguration(); + transportConfiguration.setHost(fields.get(BulkImportColumnType.SNMP_HOST)); + transportConfiguration.setPort(Optional.ofNullable(fields.get(BulkImportColumnType.SNMP_PORT)) + .map(Integer::parseInt).orElse(161)); + transportConfiguration.setProtocolVersion(Optional.ofNullable(fields.get(BulkImportColumnType.SNMP_VERSION)) + .map(version -> SnmpProtocolVersion.valueOf(version.toUpperCase())).orElse(SnmpProtocolVersion.V2C)); + transportConfiguration.setCommunity(fields.getOrDefault(BulkImportColumnType.SNMP_COMMUNITY_STRING, "public")); + + DeviceData deviceData = new DeviceData(); + deviceData.setTransportConfiguration(transportConfiguration); + device.setDeviceData(deviceData); + } + } + @SneakyThrows private DeviceCredentials createDeviceCredentials(TenantId tenantId, DeviceId deviceId, Map fields) { DeviceCredentials credentials = new DeviceCredentials(); diff --git a/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java b/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java index beecebe2ba..763ce01116 100644 --- a/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java +++ b/application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java @@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.msg.TbMsg; @@ -162,7 +163,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { if (targetProfile.getProfileData().getProvisionConfiguration().getProvisionDeviceSecret().equals(provisionRequestSecret)) { if (targetDevice != null) { log.warn("[{}] The device is present and could not be provisioned once more!", targetDevice.getName()); - notify(targetDevice, provisionRequest, DataConstants.PROVISION_FAILURE, false); + notify(targetDevice, provisionRequest, TbMsgType.PROVISION_FAILURE, false); throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); } else { return createDevice(provisionRequest, targetProfile); @@ -190,11 +191,11 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { Optional provisionState = attributesService.find(device.getTenantId(), device.getId(), DataConstants.SERVER_SCOPE, DEVICE_PROVISION_STATE).get(); if (provisionState != null && provisionState.isPresent() && !provisionState.get().getValueAsString().equals(PROVISIONED_STATE)) { - notify(device, provisionRequest, DataConstants.PROVISION_FAILURE, false); + notify(device, provisionRequest, TbMsgType.PROVISION_FAILURE, false); throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); } else { saveProvisionStateAttribute(device).get(); - notify(device, provisionRequest, DataConstants.PROVISION_SUCCESS, true); + notify(device, provisionRequest, TbMsgType.PROVISION_SUCCESS, true); } } catch (InterruptedException | ExecutionException e) { throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); @@ -206,7 +207,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { return processCreateDevice(provisionRequest, profile); } - private void notify(Device device, ProvisionRequest provisionRequest, String type, boolean success) { + private void notify(Device device, ProvisionRequest provisionRequest, TbMsgType type, boolean success) { pushProvisionEventToRuleEngine(provisionRequest, device, type); logAction(device.getTenantId(), device.getCustomerId(), device, success, provisionRequest); } @@ -222,14 +223,14 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { clusterService.onDeviceUpdated(savedDevice, null); saveProvisionStateAttribute(savedDevice).get(); pushDeviceCreatedEventToRuleEngine(savedDevice); - notify(savedDevice, provisionRequest, DataConstants.PROVISION_SUCCESS, true); + notify(savedDevice, provisionRequest, TbMsgType.PROVISION_SUCCESS, true); return new ProvisionResponse(getDeviceCredentials(savedDevice), ProvisionResponseStatus.SUCCESS); } catch (Exception e) { log.warn("[{}] Error during device creation from provision request: [{}]", provisionRequest.getDeviceName(), provisionRequest, e); Device device = deviceService.findDeviceByTenantIdAndName(profile.getTenantId(), provisionRequest.getDeviceName()); if (device != null) { - notify(device, provisionRequest, DataConstants.PROVISION_FAILURE, false); + notify(device, provisionRequest, TbMsgType.PROVISION_FAILURE, false); } throw new ProvisionFailedException(ProvisionResponseStatus.FAILURE.name()); } @@ -253,7 +254,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { return deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId()); } - private void pushProvisionEventToRuleEngine(ProvisionRequest request, Device device, String type) { + private void pushProvisionEventToRuleEngine(ProvisionRequest request, Device device, TbMsgType type) { try { JsonNode entityNode = JacksonUtil.valueToTree(request); TbMsg msg = TbMsg.newMsg(type, device.getId(), device.getCustomerId(), createTbMsgMetaData(device), JacksonUtil.toString(entityNode)); @@ -266,10 +267,10 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { private void pushDeviceCreatedEventToRuleEngine(Device device) { try { ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(device); - TbMsg msg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, device.getId(), device.getCustomerId(), createTbMsgMetaData(device), JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode)); + TbMsg msg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, device.getId(), device.getCustomerId(), createTbMsgMetaData(device), JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode)); sendToRuleEngine(device.getTenantId(), msg, null); } catch (JsonProcessingException | IllegalArgumentException e) { - log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), DataConstants.ENTITY_CREATED, e); + log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), TbMsgType.ENTITY_CREATED.name(), e); } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java index 110760acbc..64645fc209 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java @@ -15,45 +15,40 @@ */ package org.thingsboard.server.service.edge; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; -import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.common.util.ThingsBoardThreadFactory; -import org.thingsboard.server.cluster.TbClusterService; -import org.thingsboard.server.common.data.EdgeUtils; +import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; -import org.thingsboard.server.common.data.edge.EdgeEvent; -import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; -import org.thingsboard.server.common.data.id.EdgeId; -import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.queue.TbCallback; -import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeService; +import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.edge.rpc.processor.alarm.AlarmEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.asset.AssetEdgeProcessor; -import org.thingsboard.server.service.edge.rpc.processor.asset.AssetProfileEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.processor.asset.profile.AssetProfileEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.customer.CustomerEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.dashboard.DashboardEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.device.DeviceEdgeProcessor; -import org.thingsboard.server.service.edge.rpc.processor.device.DeviceProfileEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.processor.device.profile.DeviceProfileEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.edge.EdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.ota.OtaPackageEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.queue.QueueEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.processor.resource.ResourceEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.rule.RuleChainEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.processor.tenant.TenantEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.processor.tenant.TenantProfileEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.user.UserEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.widget.WidgetBundleEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.widget.WidgetTypeEdgeProcessor; @@ -62,7 +57,7 @@ import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.util.UUID; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; @Service @TbCoreComponent @@ -75,19 +70,19 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { private EdgeService edgeService; @Autowired - private EdgeEventService edgeEventService; + private EdgeProcessor edgeProcessor; @Autowired - private TbClusterService clusterService; + private AssetEdgeProcessor assetProcessor; @Autowired - private EdgeProcessor edgeProcessor; + private AssetProfileEdgeProcessor assetProfileEdgeProcessor; @Autowired - private AssetEdgeProcessor assetProcessor; + private DeviceEdgeProcessor deviceProcessor; @Autowired - private DeviceEdgeProcessor deviceProcessor; + private DeviceProfileEdgeProcessor deviceProfileEdgeProcessor; @Autowired private EntityViewEdgeProcessor entityViewProcessor; @@ -104,12 +99,6 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { @Autowired private CustomerEdgeProcessor customerProcessor; - @Autowired - private DeviceProfileEdgeProcessor deviceProfileProcessor; - - @Autowired - private AssetProfileEdgeProcessor assetProfileProcessor; - @Autowired private OtaPackageEdgeProcessor otaPackageProcessor; @@ -122,134 +111,138 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { @Autowired private QueueEdgeProcessor queueProcessor; + @Autowired + private TenantEdgeProcessor tenantEdgeProcessor; + + @Autowired + private TenantProfileEdgeProcessor tenantProfileEdgeProcessor; + @Autowired private AlarmEdgeProcessor alarmProcessor; @Autowired private RelationEdgeProcessor relationProcessor; - private ExecutorService dbCallBackExecutor; + @Autowired + private ResourceEdgeProcessor resourceEdgeProcessor; + + @Autowired + protected ApplicationEventPublisher eventPublisher; + + @Value("${actors.system.edge_dispatcher_pool_size:4}") + private int edgeDispatcherSize; + + private ExecutorService executor; @PostConstruct public void initExecutor() { - dbCallBackExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("edge-notifications")); + executor = ThingsBoardExecutors.newWorkStealingPool(edgeDispatcherSize, "edge-notifications"); } @PreDestroy public void shutdownExecutor() { - if (dbCallBackExecutor != null) { - dbCallBackExecutor.shutdownNow(); + if (executor != null) { + executor.shutdownNow(); } } @Override - public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws Exception { + public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) { edge.setRootRuleChainId(ruleChainId); Edge savedEdge = edgeService.saveEdge(edge); ObjectNode isRootBody = JacksonUtil.newObjectNode(); isRootBody.put(EDGE_IS_ROOT_BODY_KEY, Boolean.TRUE); - saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN, EdgeEventActionType.UPDATED, ruleChainId, isRootBody).get(); + eventPublisher.publishEvent(ActionEntityEvent.builder().tenantId(tenantId).edgeId(edge.getId()).entityId(ruleChainId) + .body(JacksonUtil.toString(isRootBody)).actionType(ActionType.UPDATED).build()); return savedEdge; } - private ListenableFuture saveEdgeEvent(TenantId tenantId, - EdgeId edgeId, - EdgeEventType type, - EdgeEventActionType action, - EntityId entityId, - JsonNode body) { - log.debug("Pushing edge event to edge queue. tenantId [{}], edgeId [{}], type [{}], action[{}], entityId [{}], body [{}]", - tenantId, edgeId, type, action, entityId, body); - - EdgeEvent edgeEvent = EdgeUtils.constructEdgeEvent(tenantId, edgeId, type, action, entityId, body); - - return Futures.transform(edgeEventService.saveAsync(edgeEvent), unused -> { - clusterService.onEdgeEventUpdate(tenantId, edgeId); - return null; - }, dbCallBackExecutor); - } - @Override public void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) { - log.debug("Pushing notification to edge {}", edgeNotificationMsg); + TenantId tenantId = TenantId.fromUUID(new UUID(edgeNotificationMsg.getTenantIdMSB(), edgeNotificationMsg.getTenantIdLSB())); + log.debug("[{}] Pushing notification to edge {}", tenantId, edgeNotificationMsg); + final long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(60); try { - TenantId tenantId = TenantId.fromUUID(new UUID(edgeNotificationMsg.getTenantIdMSB(), edgeNotificationMsg.getTenantIdLSB())); - EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); - ListenableFuture future; - switch (type) { - case EDGE: - future = edgeProcessor.processEdgeNotification(tenantId, edgeNotificationMsg); - break; - case ASSET: - future = assetProcessor.processAssetNotification(tenantId, edgeNotificationMsg); - break; - case DEVICE: - future = deviceProcessor.processDeviceNotification(tenantId, edgeNotificationMsg); - break; - case ENTITY_VIEW: - future = entityViewProcessor.processEntityViewNotification(tenantId, edgeNotificationMsg); - break; - case DASHBOARD: - future = dashboardProcessor.processDashboardNotification(tenantId, edgeNotificationMsg); - break; - case RULE_CHAIN: - future = ruleChainProcessor.processRuleChainNotification(tenantId, edgeNotificationMsg); - break; - case USER: - future = userProcessor.processUserNotification(tenantId, edgeNotificationMsg); - break; - case CUSTOMER: - future = customerProcessor.processCustomerNotification(tenantId, edgeNotificationMsg); - break; - case DEVICE_PROFILE: - future = deviceProfileProcessor.processDeviceProfileNotification(tenantId, edgeNotificationMsg); - break; - case ASSET_PROFILE: - future = assetProfileProcessor.processAssetProfileNotification(tenantId, edgeNotificationMsg); - break; - case OTA_PACKAGE: - future = otaPackageProcessor.processOtaPackageNotification(tenantId, edgeNotificationMsg); - break; - case WIDGETS_BUNDLE: - future = widgetBundleProcessor.processWidgetsBundleNotification(tenantId, edgeNotificationMsg); - break; - case WIDGET_TYPE: - future = widgetTypeProcessor.processWidgetTypeNotification(tenantId, edgeNotificationMsg); - break; - case QUEUE: - future = queueProcessor.processQueueNotification(tenantId, edgeNotificationMsg); - break; - case ALARM: - future = alarmProcessor.processAlarmNotification(tenantId, edgeNotificationMsg); - break; - case RELATION: - future = relationProcessor.processRelationNotification(tenantId, edgeNotificationMsg); - break; - default: - log.warn("Edge event type [{}] is not designed to be pushed to edge", type); - future = Futures.immediateFuture(null); - } - Futures.addCallback(future, new FutureCallback<>() { - @Override - public void onSuccess(@Nullable Void unused) { - callback.onSuccess(); - } - - @Override - public void onFailure(Throwable throwable) { - callBackFailure(edgeNotificationMsg, callback, throwable); + executor.submit(() -> { + try { + if (deadline < System.nanoTime()) { + log.warn("[{}] Skipping notification message because deadline reached {}", tenantId, edgeNotificationMsg); + return; + } + EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); + switch (type) { + case EDGE: + edgeProcessor.processEdgeNotification(tenantId, edgeNotificationMsg); + break; + case ASSET: + assetProcessor.processEntityNotification(tenantId, edgeNotificationMsg); + break; + case ASSET_PROFILE: + assetProfileEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg); + break; + case DEVICE: + deviceProcessor.processEntityNotification(tenantId, edgeNotificationMsg); + break; + case DEVICE_PROFILE: + deviceProfileEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg); + break; + case ENTITY_VIEW: + entityViewProcessor.processEntityNotification(tenantId, edgeNotificationMsg); + break; + case DASHBOARD: + dashboardProcessor.processEntityNotification(tenantId, edgeNotificationMsg); + break; + case RULE_CHAIN: + ruleChainProcessor.processEntityNotification(tenantId, edgeNotificationMsg); + break; + case USER: + userProcessor.processEntityNotification(tenantId, edgeNotificationMsg); + break; + case CUSTOMER: + customerProcessor.processCustomerNotification(tenantId, edgeNotificationMsg); + break; + case OTA_PACKAGE: + otaPackageProcessor.processEntityNotification(tenantId, edgeNotificationMsg); + break; + case WIDGETS_BUNDLE: + widgetBundleProcessor.processEntityNotification(tenantId, edgeNotificationMsg); + break; + case WIDGET_TYPE: + widgetTypeProcessor.processEntityNotification(tenantId, edgeNotificationMsg); + break; + case QUEUE: + queueProcessor.processEntityNotification(tenantId, edgeNotificationMsg); + break; + case ALARM: + alarmProcessor.processAlarmNotification(tenantId, edgeNotificationMsg); + break; + case RELATION: + relationProcessor.processRelationNotification(tenantId, edgeNotificationMsg); + break; + case TENANT: + tenantEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg); + break; + case TENANT_PROFILE: + tenantProfileEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg); + break; + case TB_RESOURCE: + resourceEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg); + break; + default: + log.warn("[{}] Edge event type [{}] is not designed to be pushed to edge", tenantId, type); + } + } catch (Exception e) { + callBackFailure(tenantId, edgeNotificationMsg, callback, e); } - }, dbCallBackExecutor); + }); + callback.onSuccess(); } catch (Exception e) { - callBackFailure(edgeNotificationMsg, callback, e); + callBackFailure(tenantId, edgeNotificationMsg, callback, e); } } - private void callBackFailure(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback, Throwable throwable) { - log.error("Can't push to edge updates, edgeNotificationMsg [{}]", edgeNotificationMsg, throwable); + private void callBackFailure(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback, Throwable throwable) { + log.error("[{}] Can't push to edge updates, edgeNotificationMsg [{}]", tenantId, edgeNotificationMsg, throwable); callback.onFailure(throwable); } - } - - diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java index 474c7ba233..881e22eb69 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.edge; -import freemarker.template.Configuration; import lombok.Data; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; @@ -33,28 +32,44 @@ import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; +import org.thingsboard.server.dao.resource.ResourceService; 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; import org.thingsboard.server.dao.user.UserService; +import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetsBundleService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.edge.rpc.EdgeEventStorageSettings; -import org.thingsboard.server.service.edge.rpc.constructor.EdgeMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.edge.EdgeMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.alarm.AlarmEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.processor.alarm.AlarmEdgeProcessorFactory; import org.thingsboard.server.service.edge.rpc.processor.asset.AssetEdgeProcessor; -import org.thingsboard.server.service.edge.rpc.processor.asset.AssetProfileEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.processor.asset.AssetEdgeProcessorFactory; +import org.thingsboard.server.service.edge.rpc.processor.asset.profile.AssetProfileEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.processor.asset.profile.AssetProfileEdgeProcessorFactory; import org.thingsboard.server.service.edge.rpc.processor.customer.CustomerEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.dashboard.DashboardEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.processor.dashboard.DashboardEdgeProcessorFactory; import org.thingsboard.server.service.edge.rpc.processor.device.DeviceEdgeProcessor; -import org.thingsboard.server.service.edge.rpc.processor.device.DeviceProfileEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.processor.device.DeviceEdgeProcessorFactory; +import org.thingsboard.server.service.edge.rpc.processor.device.profile.DeviceProfileEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.processor.device.profile.DeviceProfileEdgeProcessorFactory; import org.thingsboard.server.service.edge.rpc.processor.edge.EdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewProcessorFactory; import org.thingsboard.server.service.edge.rpc.processor.ota.OtaPackageEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.queue.QueueEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgeProcessorFactory; +import org.thingsboard.server.service.edge.rpc.processor.resource.ResourceEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.processor.resource.ResourceEdgeProcessorFactory; import org.thingsboard.server.service.edge.rpc.processor.rule.RuleChainEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.settings.AdminSettingsEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.telemetry.TelemetryEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.processor.tenant.TenantEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.processor.tenant.TenantProfileEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.user.UserEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.widget.WidgetBundleEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.widget.WidgetTypeEdgeProcessor; @@ -80,9 +95,6 @@ public class EdgeContextComponent { @Autowired private AdminSettingsService adminSettingsService; - @Autowired - private Configuration freemarkerConfig; - @Autowired private DeviceService deviceService; @@ -113,6 +125,9 @@ public class EdgeContextComponent { @Autowired private CustomerService customerService; + @Autowired + private WidgetTypeService widgetTypeService; + @Autowired private WidgetsBundleService widgetsBundleService; @@ -122,9 +137,18 @@ public class EdgeContextComponent { @Autowired private OtaPackageService otaPackageService; + @Autowired + private TenantService tenantService; + + @Autowired + private TenantProfileService tenantProfileService; + @Autowired private QueueService queueService; + @Autowired + private ResourceService resourceService; + @Autowired private AlarmEdgeProcessor alarmProcessor; @@ -179,9 +203,45 @@ public class EdgeContextComponent { @Autowired private QueueEdgeProcessor queueEdgeProcessor; + @Autowired + private TenantEdgeProcessor tenantEdgeProcessor; + + @Autowired + private TenantProfileEdgeProcessor tenantProfileEdgeProcessor; + + @Autowired + private ResourceEdgeProcessor resourceEdgeProcessor; + @Autowired private EdgeMsgConstructor edgeMsgConstructor; + @Autowired + private AlarmEdgeProcessorFactory alarmEdgeProcessorFactory; + + @Autowired + private AssetEdgeProcessorFactory assetEdgeProcessorFactory; + + @Autowired + private AssetProfileEdgeProcessorFactory assetProfileEdgeProcessorFactory; + + @Autowired + private DashboardEdgeProcessorFactory dashboardEdgeProcessorFactory; + + @Autowired + private DeviceEdgeProcessorFactory deviceEdgeProcessorFactory; + + @Autowired + private DeviceProfileEdgeProcessorFactory deviceProfileEdgeProcessorFactory; + + @Autowired + private EntityViewProcessorFactory entityViewProcessorFactory; + + @Autowired + private RelationEdgeProcessorFactory relationEdgeProcessorFactory; + + @Autowired + private ResourceEdgeProcessorFactory resourceEdgeProcessorFactory; + @Autowired private EdgeEventStorageSettings edgeEventStorageSettings; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java new file mode 100644 index 0000000000..58edc971b8 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java @@ -0,0 +1,176 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge; + +import com.fasterxml.jackson.databind.node.NullNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionalEventListener; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainType; +import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.dao.edge.EdgeSynchronizationManager; +import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent; +import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; +import org.thingsboard.server.dao.eventsourcing.RelationActionEvent; +import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; +import org.thingsboard.server.dao.user.UserServiceImpl; + +import javax.annotation.PostConstruct; + +import static org.thingsboard.server.service.entitiy.DefaultTbNotificationEntityService.edgeTypeByActionType; + +/** + * This event listener does not support async event processing because relay on ThreadLocal + * Another possible approach is to implement a special annotation and a bunch of classes similar to TransactionalApplicationListener + * This class is the simplest approach to maintain edge synchronization within the single class. + *

+ * For async event publishers, you have to decide whether publish event on creating async task in the same thread where dao method called + * @Autowired + * EdgeEventSynchronizationManager edgeSynchronizationManager + * ... + * //some async write action make future + * if (!edgeSynchronizationManager.isSync()) { + * future.addCallback(eventPublisher.publishEvent(...)) + * } + * */ +@Component +@RequiredArgsConstructor +@Slf4j +public class EdgeEventSourcingListener { + + private final TbClusterService tbClusterService; + private final EdgeSynchronizationManager edgeSynchronizationManager; + + @PostConstruct + public void init() { + log.info("EdgeEventSourcingListener initiated"); + } + + @TransactionalEventListener(fallbackExecution = true) + public void handleEvent(SaveEntityEvent event) { + try { + if (!isValidSaveEntityEventForEdgeProcessing(event.getEntity(), event.getOldEntity())) { + return; + } + log.trace("[{}] SaveEntityEvent called: {}", event.getTenantId(), event); + EdgeEventActionType action = Boolean.TRUE.equals(event.getAdded()) ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED; + tbClusterService.sendNotificationMsgToEdge(event.getTenantId(), null, event.getEntityId(), + null, null, action, edgeSynchronizationManager.getEdgeId().get()); + } catch (Exception e) { + log.error("[{}] failed to process SaveEntityEvent: {}", event.getTenantId(), event, e); + } + } + + @TransactionalEventListener(fallbackExecution = true) + public void handleEvent(DeleteEntityEvent event) { + try { + log.trace("[{}] DeleteEntityEvent called: {}", event.getTenantId(), event); + tbClusterService.sendNotificationMsgToEdge(event.getTenantId(), null, event.getEntityId(), + JacksonUtil.toString(event.getEntity()), null, EdgeEventActionType.DELETED, + edgeSynchronizationManager.getEdgeId().get()); + } catch (Exception e) { + log.error("[{}] failed to process DeleteEntityEvent: {}", event.getTenantId(), event, e); + } + } + + @TransactionalEventListener(fallbackExecution = true) + public void handleEvent(ActionEntityEvent event) { + try { + log.trace("[{}] ActionEntityEvent called: {}", event.getTenantId(), event); + tbClusterService.sendNotificationMsgToEdge(event.getTenantId(), event.getEdgeId(), event.getEntityId(), + event.getBody(), null, edgeTypeByActionType(event.getActionType()), + edgeSynchronizationManager.getEdgeId().get()); + } catch (Exception e) { + log.error("[{}] failed to process ActionEntityEvent: {}", event.getTenantId(), event, e); + } + } + + @TransactionalEventListener(fallbackExecution = true) + public void handleEvent(RelationActionEvent event) { + try { + EntityRelation relation = event.getRelation(); + if (relation == null) { + log.trace("[{}] skipping RelationActionEvent event in case relation is null: {}", event.getTenantId(), event); + return; + } + if (!RelationTypeGroup.COMMON.equals(relation.getTypeGroup())) { + log.trace("[{}] skipping RelationActionEvent event in case NOT COMMON relation type group: {}", event.getTenantId(), event); + return; + } + log.trace("[{}] RelationActionEvent called: {}", event.getTenantId(), event); + tbClusterService.sendNotificationMsgToEdge(event.getTenantId(), null, null, + JacksonUtil.toString(relation), EdgeEventType.RELATION, edgeTypeByActionType(event.getActionType()), + edgeSynchronizationManager.getEdgeId().get()); + } catch (Exception e) { + log.error("[{}] failed to process RelationActionEvent: {}", event.getTenantId(), event, e); + } + } + + private boolean isValidSaveEntityEventForEdgeProcessing(Object entity, Object oldEntity) { + if (entity instanceof OtaPackageInfo) { + OtaPackageInfo otaPackageInfo = (OtaPackageInfo) entity; + return otaPackageInfo.hasUrl() || otaPackageInfo.isHasData(); + } else if (entity instanceof RuleChain) { + RuleChain ruleChain = (RuleChain) entity; + return RuleChainType.EDGE.equals(ruleChain.getType()); + } else if (entity instanceof User) { + User user = (User) entity; + if (Authority.SYS_ADMIN.equals(user.getAuthority())) { + return false; + } + if (oldEntity != null) { + User oldUser = (User) oldEntity; + cleanUpUserAdditionalInfo(oldUser); + cleanUpUserAdditionalInfo(user); + return !user.equals(oldUser); + } + } else if (entity instanceof AlarmApiCallResult || entity instanceof Alarm) { + return false; + } + // Default: If the entity doesn't match any of the conditions, consider it as valid. + return true; + } + + private void cleanUpUserAdditionalInfo(User user) { + // reset FAILED_LOGIN_ATTEMPTS and LAST_LOGIN_TS - edge is not interested in this information + if (user.getAdditionalInfo() instanceof NullNode) { + user.setAdditionalInfo(null); + } + if (user.getAdditionalInfo() instanceof ObjectNode) { + ObjectNode additionalInfo = ((ObjectNode) user.getAdditionalInfo()); + additionalInfo.remove(UserServiceImpl.FAILED_LOGIN_ATTEMPTS); + additionalInfo.remove(UserServiceImpl.LAST_LOGIN_TS); + if (additionalInfo.isEmpty()) { + user.setAdditionalInfo(null); + } else { + user.setAdditionalInfo(additionalInfo); + } + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeInstallInstructionsService.java b/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeInstallInstructionsService.java new file mode 100644 index 0000000000..ba2cd06ee1 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeInstallInstructionsService.java @@ -0,0 +1,134 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.instructions; + +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeInstructions; +import org.thingsboard.server.dao.util.DeviceConnectivityUtil; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.install.InstallScripts; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +@Service +@Slf4j +@RequiredArgsConstructor +@ConditionalOnProperty(prefix = "edges", value = "enabled", havingValue = "true") +@TbCoreComponent +public class DefaultEdgeInstallInstructionsService implements EdgeInstallInstructionsService { + + private static final String EDGE_DIR = "edge"; + private static final String INSTRUCTIONS_DIR = "instructions"; + private static final String INSTALL_DIR = "install"; + + private final InstallScripts installScripts; + + @Value("${edges.rpc.port}") + private int rpcPort; + + @Value("${edges.rpc.ssl.enabled}") + private boolean sslEnabled; + + @Value("${app.version:unknown}") + @Setter + private String appVersion; + + @Override + public EdgeInstructions getInstallInstructions(Edge edge, String installationMethod, HttpServletRequest request) { + switch (installationMethod.toLowerCase()) { + case "docker": + return getDockerInstallInstructions(edge, request); + case "ubuntu": + return getUbuntuInstallInstructions(edge, request); + case "centos": + return getCentosInstallInstructions(edge, request); + default: + throw new IllegalArgumentException("Unsupported installation method for Edge: " + installationMethod); + } + } + + private EdgeInstructions getDockerInstallInstructions(Edge edge, HttpServletRequest request) { + String dockerInstallInstructions = readFile(resolveFile("docker", "instructions.md")); + String baseUrl = request.getServerName(); + + if (DeviceConnectivityUtil.isLocalhost(baseUrl)) { + dockerInstallInstructions = dockerInstallInstructions.replace("${EXTRA_HOSTS}", "extra_hosts:\n - \"host.docker.internal:host-gateway\"\n"); + dockerInstallInstructions = dockerInstallInstructions.replace("${BASE_URL}", "host.docker.internal"); + } else { + dockerInstallInstructions = dockerInstallInstructions.replace("${EXTRA_HOSTS}", ""); + dockerInstallInstructions = dockerInstallInstructions.replace("${BASE_URL}", baseUrl); + } + String edgeVersion = appVersion + "EDGE"; + edgeVersion = edgeVersion.replace("-SNAPSHOT", ""); + dockerInstallInstructions = dockerInstallInstructions.replace("${TB_EDGE_VERSION}", edgeVersion); + dockerInstallInstructions = replacePlaceholders(dockerInstallInstructions, edge); + return new EdgeInstructions(dockerInstallInstructions); + } + + private EdgeInstructions getUbuntuInstallInstructions(Edge edge, HttpServletRequest request) { + String ubuntuInstallInstructions = readFile(resolveFile("ubuntu", "instructions.md")); + ubuntuInstallInstructions = replacePlaceholders(ubuntuInstallInstructions, edge); + ubuntuInstallInstructions = ubuntuInstallInstructions.replace("${BASE_URL}", request.getServerName()); + String edgeVersion = appVersion.replace("-SNAPSHOT", ""); + ubuntuInstallInstructions = ubuntuInstallInstructions.replace("${TB_EDGE_VERSION}", edgeVersion); + return new EdgeInstructions(ubuntuInstallInstructions); + } + + + private EdgeInstructions getCentosInstallInstructions(Edge edge, HttpServletRequest request) { + String centosInstallInstructions = readFile(resolveFile("centos", "instructions.md")); + centosInstallInstructions = replacePlaceholders(centosInstallInstructions, edge); + centosInstallInstructions = centosInstallInstructions.replace("${BASE_URL}", request.getServerName()); + String edgeVersion = appVersion.replace("-SNAPSHOT", ""); + centosInstallInstructions = centosInstallInstructions.replace("${TB_EDGE_VERSION}", edgeVersion); + return new EdgeInstructions(centosInstallInstructions); + } + + private String replacePlaceholders(String instructions, Edge edge) { + instructions = instructions.replace("${CLOUD_ROUTING_KEY}", edge.getRoutingKey()); + instructions = instructions.replace("${CLOUD_ROUTING_SECRET}", edge.getSecret()); + instructions = instructions.replace("${CLOUD_RPC_PORT}", Integer.toString(rpcPort)); + instructions = instructions.replace("${CLOUD_RPC_SSL_ENABLED}", Boolean.toString(sslEnabled)); + return instructions; + } + + private String readFile(Path file) { + try { + return Files.readString(file); + } catch (IOException e) { + log.warn("Failed to read file: {}", file, e); + throw new RuntimeException(e); + } + } + + private Path resolveFile(String subDir, String... subDirs) { + return getEdgeInstallInstructionsDir().resolve(Paths.get(subDir, subDirs)); + } + + private Path getEdgeInstallInstructionsDir() { + return Paths.get(installScripts.getDataDir(), InstallScripts.JSON_DIR, EDGE_DIR, INSTRUCTIONS_DIR, INSTALL_DIR); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeInstallService.java b/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeInstallService.java deleted file mode 100644 index 2c4a14635d..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeInstallService.java +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.instructions; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.edge.Edge; -import org.thingsboard.server.common.data.edge.EdgeInstallInstructions; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.install.InstallScripts; - -import javax.servlet.http.HttpServletRequest; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; - -@Service -@Slf4j -@RequiredArgsConstructor -@ConditionalOnProperty(prefix = "edges", value = "enabled", havingValue = "true") -@TbCoreComponent -public class DefaultEdgeInstallService implements EdgeInstallService { - - private static final String EDGE_DIR = "edge"; - - private static final String EDGE_INSTALL_INSTRUCTIONS_DIR = "install_instructions"; - - private final InstallScripts installScripts; - - @Value("${edges.rpc.port}") - private int rpcPort; - - @Value("${edges.rpc.ssl.enabled}") - private boolean sslEnabled; - - @Value("${app.version:unknown}") - private String appVersion; - - @Override - public EdgeInstallInstructions getDockerInstallInstructions(TenantId tenantId, Edge edge, HttpServletRequest request) { - String dockerInstallInstructions = readFile(resolveFile("docker", "instructions.md")); - String baseUrl = request.getServerName(); - if (baseUrl.contains("localhost") || baseUrl.contains("127.0.0.1")) { - String localhostWarning = readFile(resolveFile("docker", "localhost_warning.md")); - dockerInstallInstructions = dockerInstallInstructions.replace("${LOCALHOST_WARNING}", localhostWarning); - dockerInstallInstructions = dockerInstallInstructions.replace("${BASE_URL}", "!!!REPLACE_ME_TO_HOST_IP_ADDRESS!!!"); - } else { - dockerInstallInstructions = dockerInstallInstructions.replace("${LOCALHOST_WARNING}", ""); - dockerInstallInstructions = dockerInstallInstructions.replace("${BASE_URL}", baseUrl); - } - String edgeVersion = appVersion + "EDGE"; - edgeVersion = edgeVersion.replace("-SNAPSHOT", ""); - dockerInstallInstructions = dockerInstallInstructions.replace("${TB_EDGE_VERSION}", edgeVersion); - dockerInstallInstructions = dockerInstallInstructions.replace("${CLOUD_ROUTING_KEY}", edge.getRoutingKey()); - dockerInstallInstructions = dockerInstallInstructions.replace("${CLOUD_ROUTING_SECRET}", edge.getSecret()); - dockerInstallInstructions = dockerInstallInstructions.replace("${CLOUD_RPC_PORT}", Integer.toString(rpcPort)); - dockerInstallInstructions = dockerInstallInstructions.replace("${CLOUD_RPC_SSL_ENABLED}", Boolean.toString(sslEnabled)); - return new EdgeInstallInstructions(dockerInstallInstructions); - } - - private String readFile(Path file) { - try { - return new String(Files.readAllBytes(file), StandardCharsets.UTF_8); - } catch (IOException e) { - log.warn("Failed to read file: {}", file, e); - throw new RuntimeException(e); - } - } - - private Path resolveFile(String subDir, String... subDirs) { - return getEdgeInstallInstructionsDir().resolve(Paths.get(subDir, subDirs)); - } - - private Path getEdgeInstallInstructionsDir() { - return Paths.get(installScripts.getDataDir(), InstallScripts.JSON_DIR, EDGE_DIR, EDGE_INSTALL_INSTRUCTIONS_DIR); - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeUpgradeInstructionsService.java b/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeUpgradeInstructionsService.java new file mode 100644 index 0000000000..d4fa1f31a7 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeUpgradeInstructionsService.java @@ -0,0 +1,193 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.instructions; + +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.EdgeUpgradeInfo; +import org.thingsboard.server.common.data.edge.EdgeInstructions; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.install.InstallScripts; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +@Service +@Slf4j +@RequiredArgsConstructor +@ConditionalOnProperty(prefix = "edges", value = "enabled", havingValue = "true") +@TbCoreComponent +public class DefaultEdgeUpgradeInstructionsService implements EdgeUpgradeInstructionsService { + + private static final Map upgradeVersionHashMap = new HashMap<>(); + + private static final String EDGE_DIR = "edge"; + private static final String INSTRUCTIONS_DIR = "instructions"; + private static final String UPGRADE_DIR = "upgrade"; + + private final InstallScripts installScripts; + private final AttributesService attributesService; + + @Value("${app.version:unknown}") + @Setter + private String appVersion; + + @Override + public EdgeInstructions getUpgradeInstructions(String edgeVersion, String upgradeMethod) { + String tbVersion = appVersion.replace("-SNAPSHOT", ""); + String currentEdgeVersion = convertEdgeVersionToDocsFormat(edgeVersion); + switch (upgradeMethod.toLowerCase()) { + case "docker": + return getDockerUpgradeInstructions(tbVersion, currentEdgeVersion); + case "ubuntu": + case "centos": + return getLinuxUpgradeInstructions(tbVersion, currentEdgeVersion, upgradeMethod.toLowerCase()); + default: + throw new IllegalArgumentException("Unsupported upgrade method for Edge: " + upgradeMethod); + } + } + + @Override + public void updateInstructionMap(Map map) { + for (String key : map.keySet()) { + upgradeVersionHashMap.put(key, map.get(key)); + } + } + + @Override + public boolean isUpgradeAvailable(TenantId tenantId, EdgeId edgeId) throws Exception { + Optional attributeKvEntryOpt = attributesService.find(tenantId, edgeId, DataConstants.SERVER_SCOPE, DataConstants.EDGE_VERSION_ATTR_KEY).get(); + if (attributeKvEntryOpt.isPresent()) { + String edgeVersionFormatted = convertEdgeVersionToDocsFormat(attributeKvEntryOpt.get().getValueAsString()); + return isVersionGreaterOrEqualsThan(edgeVersionFormatted, "3.6.0") && !isVersionGreaterOrEqualsThan(edgeVersionFormatted, appVersion); + } + return false; + } + + private boolean isVersionGreaterOrEqualsThan(String version1, String version2) { + String[] v1 = version1.split("\\."); + String[] v2 = version2.split("\\."); + + int length = Math.max(v1.length, v2.length); + for (int i = 0; i < length; i++) { + int num1 = i < v1.length ? Integer.parseInt(v1[i]) : 0; + int num2 = i < v2.length ? Integer.parseInt(v2[i]) : 0; + + if (num1 < num2) { + return false; + } else if (num1 > num2) { + return true; + } + } + return true; + } + + private EdgeInstructions getDockerUpgradeInstructions(String tbVersion, String currentEdgeVersion) { + EdgeUpgradeInfo edgeUpgradeInfo = upgradeVersionHashMap.get(currentEdgeVersion); + if (edgeUpgradeInfo == null || edgeUpgradeInfo.getNextEdgeVersion() == null || tbVersion.equals(currentEdgeVersion)) { + return new EdgeInstructions("Edge upgrade instruction for " + currentEdgeVersion + "EDGE is not available."); + } + StringBuilder result = new StringBuilder(readFile(resolveFile("docker", "upgrade_preparing.md"))); + while (edgeUpgradeInfo.getNextEdgeVersion() != null && !tbVersion.equals(currentEdgeVersion)) { + String edgeVersion = edgeUpgradeInfo.getNextEdgeVersion(); + String dockerUpgradeInstructions = readFile(resolveFile("docker", "instructions.md")); + if (edgeUpgradeInfo.isRequiresUpdateDb()) { + String upgradeDb = readFile(resolveFile("docker", "upgrade_db.md")); + dockerUpgradeInstructions = dockerUpgradeInstructions.replace("${UPGRADE_DB}", upgradeDb); + } else { + dockerUpgradeInstructions = dockerUpgradeInstructions.replace("${UPGRADE_DB}", ""); + } + dockerUpgradeInstructions = dockerUpgradeInstructions.replace("${TB_EDGE_VERSION}", edgeVersion + "EDGE"); + dockerUpgradeInstructions = dockerUpgradeInstructions.replace("${FROM_TB_EDGE_VERSION}", currentEdgeVersion + "EDGE"); + currentEdgeVersion = edgeVersion; + edgeUpgradeInfo = upgradeVersionHashMap.get(edgeUpgradeInfo.getNextEdgeVersion()); + result.append(dockerUpgradeInstructions); + } + String startService = readFile(resolveFile("docker", "start_service.md")); + startService = startService.replace("${TB_EDGE_VERSION}", currentEdgeVersion + "EDGE"); + result.append(startService); + return new EdgeInstructions(result.toString()); + } + + private EdgeInstructions getLinuxUpgradeInstructions(String tbVersion, String currentEdgeVersion, String os) { + EdgeUpgradeInfo edgeUpgradeInfo = upgradeVersionHashMap.get(currentEdgeVersion); + if (edgeUpgradeInfo == null || edgeUpgradeInfo.getNextEdgeVersion() == null || tbVersion.equals(currentEdgeVersion)) { + return new EdgeInstructions("Edge upgrade instruction for " + currentEdgeVersion + "EDGE is not available."); + } + String upgrade_preparing = readFile(resolveFile("upgrade_preparing.md")); + upgrade_preparing = upgrade_preparing.replace("${OS}", os.equals("centos") ? "RHEL/CentOS 7/8" : "Ubuntu"); + StringBuilder result = new StringBuilder(upgrade_preparing); + while (edgeUpgradeInfo.getNextEdgeVersion() != null && !tbVersion.equals(currentEdgeVersion)) { + String edgeVersion = edgeUpgradeInfo.getNextEdgeVersion(); + String linuxUpgradeInstructions = readFile(resolveFile(os, "instructions.md")); + if (edgeUpgradeInfo.isRequiresUpdateDb()) { + String upgradeDb = readFile(resolveFile("upgrade_db.md")); + linuxUpgradeInstructions = linuxUpgradeInstructions.replace("${UPGRADE_DB}", upgradeDb); + } else { + linuxUpgradeInstructions = linuxUpgradeInstructions.replace("${UPGRADE_DB}", ""); + } + linuxUpgradeInstructions = linuxUpgradeInstructions.replace("${TB_EDGE_TAG}", getTagVersion(edgeVersion)); + linuxUpgradeInstructions = linuxUpgradeInstructions.replace("${FROM_TB_EDGE_TAG}", getTagVersion(currentEdgeVersion)); + linuxUpgradeInstructions = linuxUpgradeInstructions.replace("${TB_EDGE_VERSION}", edgeVersion); + linuxUpgradeInstructions = linuxUpgradeInstructions.replace("${FROM_TB_EDGE_VERSION}", currentEdgeVersion); + currentEdgeVersion = edgeVersion; + edgeUpgradeInfo = upgradeVersionHashMap.get(edgeUpgradeInfo.getNextEdgeVersion()); + result.append(linuxUpgradeInstructions); + } + String startService = readFile(resolveFile("start_service.md")); + result.append(startService); + return new EdgeInstructions(result.toString()); + } + + private String getTagVersion(String version) { + return version.endsWith(".0") ? version.substring(0, version.length() - 2) : version; + } + + private String convertEdgeVersionToDocsFormat(String edgeVersion) { + return edgeVersion.replace("_", ".").substring(2); + } + + private String readFile(Path file) { + try { + return Files.readString(file); + } catch (IOException e) { + log.warn("Failed to read file: {}", file, e); + throw new RuntimeException(e); + } + } + + private Path resolveFile(String subDir, String... subDirs) { + return getEdgeInstallInstructionsDir().resolve(Paths.get(subDir, subDirs)); + } + + private Path getEdgeInstallInstructionsDir() { + return Paths.get(installScripts.getDataDir(), InstallScripts.JSON_DIR, EDGE_DIR, INSTRUCTIONS_DIR, UPGRADE_DIR); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/instructions/EdgeInstallInstructionsService.java b/application/src/main/java/org/thingsboard/server/service/edge/instructions/EdgeInstallInstructionsService.java new file mode 100644 index 0000000000..9df1f86e86 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/instructions/EdgeInstallInstructionsService.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.instructions; + +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeInstructions; + +import javax.servlet.http.HttpServletRequest; + +public interface EdgeInstallInstructionsService { + + EdgeInstructions getInstallInstructions(Edge edge, String installationMethod, HttpServletRequest request); + + void setAppVersion(String version); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/instructions/EdgeInstallService.java b/application/src/main/java/org/thingsboard/server/service/edge/instructions/EdgeInstallService.java deleted file mode 100644 index 2a030fc4fa..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/instructions/EdgeInstallService.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.instructions; - -import org.thingsboard.server.common.data.edge.Edge; -import org.thingsboard.server.common.data.edge.EdgeInstallInstructions; -import org.thingsboard.server.common.data.id.TenantId; - -import javax.servlet.http.HttpServletRequest; - -public interface EdgeInstallService { - - EdgeInstallInstructions getDockerInstallInstructions(TenantId tenantId, Edge edge, HttpServletRequest request); - -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/instructions/EdgeUpgradeInstructionsService.java b/application/src/main/java/org/thingsboard/server/service/edge/instructions/EdgeUpgradeInstructionsService.java new file mode 100644 index 0000000000..30f8082bd3 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/instructions/EdgeUpgradeInstructionsService.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.instructions; + +import org.thingsboard.server.common.data.EdgeUpgradeInfo; +import org.thingsboard.server.common.data.edge.EdgeInstructions; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.TenantId; + +import java.util.Map; + +public interface EdgeUpgradeInstructionsService { + + EdgeInstructions getUpgradeInstructions(String edgeVersion, String upgradeMethod); + + void updateInstructionMap(Map upgradeVersions); + + void setAppVersion(String version); + + boolean isUpgradeAvailable(TenantId tenantId, EdgeId edgeId) throws Exception; +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java index bda510a762..50b80a82f7 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java @@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -71,10 +72,6 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; -import static org.thingsboard.server.common.data.DataConstants.CONNECT_EVENT; -import static org.thingsboard.server.common.data.DataConstants.DISCONNECT_EVENT; -import static org.thingsboard.server.common.data.DataConstants.SERVER_SCOPE; - @Service @Slf4j @ConditionalOnProperty(prefix = "edges", value = "enabled", havingValue = "true") @@ -198,17 +195,17 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i switch (msg.getMsgType()) { case EDGE_EVENT_UPDATE_TO_EDGE_SESSION_MSG: EdgeEventUpdateMsg edgeEventUpdateMsg = (EdgeEventUpdateMsg) msg; - log.trace("[{}] onToEdgeSessionMsg [{}]", edgeEventUpdateMsg.getTenantId(), msg); + log.trace("[{}] onToEdgeSessionMsg [{}]", tenantId, msg); onEdgeEvent(tenantId, edgeEventUpdateMsg.getEdgeId()); break; case EDGE_SYNC_REQUEST_TO_EDGE_SESSION_MSG: ToEdgeSyncRequest toEdgeSyncRequest = (ToEdgeSyncRequest) msg; - log.trace("[{}] toEdgeSyncRequest [{}]", toEdgeSyncRequest.getTenantId(), msg); + log.trace("[{}] toEdgeSyncRequest [{}]", tenantId, msg); startSyncProcess(tenantId, toEdgeSyncRequest.getEdgeId(), toEdgeSyncRequest.getId()); break; case EDGE_SYNC_RESPONSE_FROM_EDGE_SESSION_MSG: FromEdgeSyncResponse fromEdgeSyncResponse = (FromEdgeSyncResponse) msg; - log.trace("[{}] fromEdgeSyncResponse [{}]", fromEdgeSyncResponse.getTenantId(), msg); + log.trace("[{}] fromEdgeSyncResponse [{}]", tenantId, msg); processSyncResponse(fromEdgeSyncResponse); break; } @@ -266,7 +263,8 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i } private void onEdgeConnect(EdgeId edgeId, EdgeGrpcSession edgeGrpcSession) { - log.info("[{}] edge [{}] connected successfully.", edgeGrpcSession.getSessionId(), edgeId); + TenantId tenantId = edgeGrpcSession.getEdge().getTenantId(); + log.info("[{}][{}] edge [{}] connected successfully.", tenantId, edgeGrpcSession.getSessionId(), edgeId); sessions.put(edgeId, edgeGrpcSession); final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock()); newEventLock.lock(); @@ -275,10 +273,10 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i } finally { newEventLock.unlock(); } - save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, true); + save(tenantId, edgeId, DefaultDeviceStateService.ACTIVITY_STATE, true); long lastConnectTs = System.currentTimeMillis(); - save(edgeId, DefaultDeviceStateService.LAST_CONNECT_TIME, lastConnectTs); - pushRuleEngineMessage(edgeGrpcSession.getEdge().getTenantId(), edgeId, lastConnectTs, CONNECT_EVENT); + save(tenantId, edgeId, DefaultDeviceStateService.LAST_CONNECT_TIME, lastConnectTs); + pushRuleEngineMessage(tenantId, edgeId, lastConnectTs, TbMsgType.CONNECT_EVENT); cancelScheduleEdgeEventsCheck(edgeId); scheduleEdgeEventsCheck(edgeGrpcSession); } @@ -337,11 +335,14 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i newEventLock.lock(); try { if (Boolean.TRUE.equals(sessionNewEvents.get(edgeId))) { - log.trace("[{}] Set session new events flag to false", edgeId.getId()); + log.trace("[{}][{}] Set session new events flag to false", tenantId, edgeId.getId()); sessionNewEvents.put(edgeId, false); Futures.addCallback(session.processEdgeEvents(), new FutureCallback<>() { @Override - public void onSuccess(Void result) { + public void onSuccess(Boolean newEventsAdded) { + if (Boolean.TRUE.equals(newEventsAdded)) { + sessionNewEvents.put(edgeId, true); + } scheduleEdgeEventsCheck(session); } @@ -380,53 +381,61 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i } } - private void onEdgeDisconnect(EdgeId edgeId) { - log.info("[{}] edge disconnected!", edgeId); - EdgeGrpcSession removed = sessions.remove(edgeId); - final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock()); - newEventLock.lock(); - try { - sessionNewEvents.remove(edgeId); - } finally { - newEventLock.unlock(); + private void onEdgeDisconnect(EdgeId edgeId, UUID sessionId) { + log.info("[{}][{}] edge disconnected!", edgeId, sessionId); + EdgeGrpcSession toRemove = sessions.get(edgeId); + if (toRemove.getSessionId().equals(sessionId)) { + toRemove = sessions.remove(edgeId); + final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock()); + newEventLock.lock(); + try { + sessionNewEvents.remove(edgeId); + } finally { + newEventLock.unlock(); + } + TenantId tenantId = toRemove.getEdge().getTenantId(); + save(tenantId, edgeId, DefaultDeviceStateService.ACTIVITY_STATE, false); + long lastDisconnectTs = System.currentTimeMillis(); + save(tenantId, edgeId, DefaultDeviceStateService.LAST_DISCONNECT_TIME, lastDisconnectTs); + pushRuleEngineMessage(toRemove.getEdge().getTenantId(), edgeId, lastDisconnectTs, TbMsgType.DISCONNECT_EVENT); + cancelScheduleEdgeEventsCheck(edgeId); + } else { + log.debug("[{}] edge session [{}] is not available anymore, nothing to remove. most probably this session is already outdated!", edgeId, sessionId); } - save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, false); - long lastDisconnectTs = System.currentTimeMillis(); - save(edgeId, DefaultDeviceStateService.LAST_DISCONNECT_TIME, lastDisconnectTs); - pushRuleEngineMessage(removed.getEdge().getTenantId(), edgeId, lastDisconnectTs, DISCONNECT_EVENT); - cancelScheduleEdgeEventsCheck(edgeId); } - private void save(EdgeId edgeId, String key, long value) { - log.debug("[{}] Updating long edge telemetry [{}] [{}]", edgeId, key, value); + private void save(TenantId tenantId, EdgeId edgeId, String key, long value) { + log.debug("[{}][{}] Updating long edge telemetry [{}] [{}]", tenantId, edgeId, key, value); if (persistToTelemetry) { tsSubService.saveAndNotify( - TenantId.SYS_TENANT_ID, edgeId, + tenantId, edgeId, Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry(key, value))), - new AttributeSaveCallback(edgeId, key, value)); + new AttributeSaveCallback(tenantId, edgeId, key, value)); } else { - tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, edgeId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(edgeId, key, value)); + tsSubService.saveAttrAndNotify(tenantId, edgeId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(tenantId, edgeId, key, value)); } } - private void save(EdgeId edgeId, String key, boolean value) { - log.debug("[{}] Updating boolean edge telemetry [{}] [{}]", edgeId, key, value); + private void save(TenantId tenantId, EdgeId edgeId, String key, boolean value) { + log.debug("[{}][{}] Updating boolean edge telemetry [{}] [{}]", tenantId, edgeId, key, value); if (persistToTelemetry) { tsSubService.saveAndNotify( - TenantId.SYS_TENANT_ID, edgeId, + tenantId, edgeId, Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry(key, value))), - new AttributeSaveCallback(edgeId, key, value)); + new AttributeSaveCallback(tenantId, edgeId, key, value)); } else { - tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, edgeId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(edgeId, key, value)); + tsSubService.saveAttrAndNotify(tenantId, edgeId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(tenantId, edgeId, key, value)); } } private static class AttributeSaveCallback implements FutureCallback { + private final TenantId tenantId; private final EdgeId edgeId; private final String key; private final Object value; - AttributeSaveCallback(EdgeId edgeId, String key, Object value) { + AttributeSaveCallback(TenantId tenantId, EdgeId edgeId, String key, Object value) { + this.tenantId = tenantId; this.edgeId = edgeId; this.key = key; this.value = value; @@ -434,19 +443,19 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i @Override public void onSuccess(@Nullable Void result) { - log.trace("[{}] Successfully updated attribute [{}] with value [{}]", edgeId, key, value); + log.trace("[{}][{}] Successfully updated attribute [{}] with value [{}]", tenantId, edgeId, key, value); } @Override public void onFailure(Throwable t) { - log.warn("[{}] Failed to update attribute [{}] with value [{}]", edgeId, key, value, t); + log.warn("[{}][{}] Failed to update attribute [{}] with value [{}]", tenantId, edgeId, key, value, t); } } - private void pushRuleEngineMessage(TenantId tenantId, EdgeId edgeId, long ts, String msgType) { + private void pushRuleEngineMessage(TenantId tenantId, EdgeId edgeId, long ts, TbMsgType msgType) { try { ObjectNode edgeState = JacksonUtil.newObjectNode(); - if (msgType.equals(CONNECT_EVENT)) { + if (msgType.equals(TbMsgType.CONNECT_EVENT)) { edgeState.put(DefaultDeviceStateService.ACTIVITY_STATE, true); edgeState.put(DefaultDeviceStateService.LAST_CONNECT_TIME, ts); } else { @@ -456,7 +465,7 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i String data = JacksonUtil.toString(edgeState); TbMsgMetaData md = new TbMsgMetaData(); if (!persistToTelemetry) { - md.putValue(DataConstants.SCOPE, SERVER_SCOPE); + md.putValue(DataConstants.SCOPE, DataConstants.SERVER_SCOPE); } TbMsg tbMsg = TbMsg.newMsg(msgType, edgeId, md, TbMsgDataType.JSON, data); clusterService.pushMsgToRuleEngine(tenantId, edgeId, tbMsg, null); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java index 33aed92682..32ab28b09a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java @@ -24,6 +24,7 @@ import io.grpc.stub.StreamObserver; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.checkerframework.checker.nullness.qual.Nullable; +import org.springframework.data.util.Pair; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.edge.Edge; @@ -33,15 +34,22 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.page.SortOrder; +import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; +import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; import org.thingsboard.server.gen.edge.v1.AttributesRequestMsg; import org.thingsboard.server.gen.edge.v1.ConnectRequestMsg; import org.thingsboard.server.gen.edge.v1.ConnectResponseCode; import org.thingsboard.server.gen.edge.v1.ConnectResponseMsg; +import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsRequestMsg; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg; import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; @@ -50,11 +58,13 @@ import org.thingsboard.server.gen.edge.v1.EdgeConfiguration; import org.thingsboard.server.gen.edge.v1.EdgeUpdateMsg; import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.EntityDataProto; +import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; import org.thingsboard.server.gen.edge.v1.EntityViewsRequestMsg; import org.thingsboard.server.gen.edge.v1.RelationRequestMsg; import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; import org.thingsboard.server.gen.edge.v1.RequestMsg; import org.thingsboard.server.gen.edge.v1.RequestMsgType; +import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; import org.thingsboard.server.gen.edge.v1.ResponseMsg; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataRequestMsg; import org.thingsboard.server.gen.edge.v1.SyncCompletedMsg; @@ -65,20 +75,27 @@ import org.thingsboard.server.gen.edge.v1.WidgetBundleTypesRequestMsg; import org.thingsboard.server.service.edge.EdgeContextComponent; import org.thingsboard.server.service.edge.rpc.fetch.EdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.GeneralEdgeEventFetcher; +import org.thingsboard.server.service.edge.rpc.processor.alarm.AlarmProcessor; +import org.thingsboard.server.service.edge.rpc.processor.asset.AssetProcessor; +import org.thingsboard.server.service.edge.rpc.processor.asset.profile.AssetProfileProcessor; +import org.thingsboard.server.service.edge.rpc.processor.dashboard.DashboardProcessor; +import org.thingsboard.server.service.edge.rpc.processor.device.DeviceProcessor; +import org.thingsboard.server.service.edge.rpc.processor.device.profile.DeviceProfileProcessor; +import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewProcessor; +import org.thingsboard.server.service.edge.rpc.processor.relation.RelationProcessor; +import org.thingsboard.server.service.edge.rpc.processor.resource.ResourceProcessor; import java.io.Closeable; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.UUID; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.stream.Collectors; @Slf4j @Data @@ -89,20 +106,28 @@ public final class EdgeGrpcSession implements Closeable { private static final int MAX_DOWNLINK_ATTEMPTS = 10; // max number of attemps to send downlink message if edge connected private static final String QUEUE_START_TS_ATTR_KEY = "queueStartTs"; + private static final String QUEUE_START_SEQ_ID_ATTR_KEY = "queueStartSeqId"; private final UUID sessionId; private final BiConsumer sessionOpenListener; - private final Consumer sessionCloseListener; + private final BiConsumer sessionCloseListener; private final EdgeSessionState sessionState = new EdgeSessionState(); private EdgeContextComponent ctx; private Edge edge; + private TenantId tenantId; private StreamObserver inputStream; private StreamObserver outputStream; private boolean connected; private boolean syncCompleted; + private Long newStartTs; + private Long previousStartTs; + private Long newStartSeqId; + private Long previousStartSeqId; + private Long seqIdEnd; + private EdgeVersion edgeVersion; private int maxInboundMessageSize; @@ -111,7 +136,7 @@ public final class EdgeGrpcSession implements Closeable { private ScheduledExecutorService sendDownlinkExecutorService; EdgeGrpcSession(EdgeContextComponent ctx, StreamObserver outputStream, BiConsumer sessionOpenListener, - Consumer sessionCloseListener, ScheduledExecutorService sendDownlinkExecutorService, int maxInboundMessageSize) { + BiConsumer sessionCloseListener, ScheduledExecutorService sendDownlinkExecutorService, int maxInboundMessageSize) { this.sessionId = UUID.randomUUID(); this.ctx = ctx; this.outputStream = outputStream; @@ -135,7 +160,7 @@ public final class EdgeGrpcSession implements Closeable { outputStream.onError(new RuntimeException(responseMsg.getErrorMsg())); } else { if (requestMsg.getConnectRequestMsg().hasMaxInboundMessageSize()) { - log.debug("[{}] Client max inbound message size: {}", sessionId, requestMsg.getConnectRequestMsg().getMaxInboundMessageSize()); + log.debug("[{}][{}] Client max inbound message size: {}", tenantId, sessionId, requestMsg.getConnectRequestMsg().getMaxInboundMessageSize()); clientMaxInboundMessageSize = requestMsg.getConnectRequestMsg().getMaxInboundMessageSize(); } connected = true; @@ -143,8 +168,8 @@ public final class EdgeGrpcSession implements Closeable { } if (connected) { if (requestMsg.getMsgType().equals(RequestMsgType.SYNC_REQUEST_RPC_MESSAGE)) { - if (requestMsg.hasSyncRequestMsg() && requestMsg.getSyncRequestMsg().getSyncRequired()) { - boolean fullSync = true; + if (requestMsg.hasSyncRequestMsg()) { + boolean fullSync = false; if (requestMsg.getSyncRequestMsg().hasFullSync()) { fullSync = requestMsg.getSyncRequestMsg().getFullSync(); } @@ -166,13 +191,13 @@ public final class EdgeGrpcSession implements Closeable { @Override public void onError(Throwable t) { - log.error("[{}] Stream was terminated due to error:", sessionId, t); + log.error("[{}][{}] Stream was terminated due to error:", tenantId, sessionId, t); closeSession(); } @Override public void onCompleted() { - log.info("[{}] Stream was closed and completed successfully!", sessionId); + log.info("[{}][{}] Stream was closed and completed successfully!", tenantId, sessionId); closeSession(); } @@ -180,7 +205,7 @@ public final class EdgeGrpcSession implements Closeable { connected = false; if (edge != null) { try { - sessionCloseListener.accept(edge.getId()); + sessionCloseListener.accept(edge.getId(), sessionId); } catch (Exception ignored) { } } @@ -193,7 +218,7 @@ public final class EdgeGrpcSession implements Closeable { } public void startSyncProcess(boolean fullSync) { - log.trace("[{}][{}][{}] Staring edge sync process", edge.getTenantId(), edge.getId(), this.sessionId); + log.trace("[{}][{}][{}] Staring edge sync process", this.tenantId, edge.getId(), this.sessionId); syncCompleted = false; interruptGeneralProcessingOnSync(); doSync(new EdgeSyncCursor(ctx, edge, fullSync)); @@ -203,17 +228,17 @@ public final class EdgeGrpcSession implements Closeable { if (cursor.hasNext()) { EdgeEventFetcher next = cursor.getNext(); log.info("[{}][{}] starting sync process, cursor current idx = {}, class = {}", - edge.getTenantId(), edge.getId(), cursor.getCurrentIdx(), next.getClass().getSimpleName()); - ListenableFuture uuidListenableFuture = startProcessingEdgeEvents(next); - Futures.addCallback(uuidListenableFuture, new FutureCallback<>() { + this.tenantId, edge.getId(), cursor.getCurrentIdx(), next.getClass().getSimpleName()); + ListenableFuture> future = startProcessingEdgeEvents(next); + Futures.addCallback(future, new FutureCallback<>() { @Override - public void onSuccess(@Nullable UUID result) { + public void onSuccess(@Nullable Pair result) { doSync(cursor); } @Override public void onFailure(Throwable t) { - log.error("[{}][{}] Exception during sync process", edge.getTenantId(), edge.getId(), t); + log.error("[{}][{}] Exception during sync process", tenantId, edge.getId(), t); } }, ctx.getGrpcCallbackExecutorService()); } else { @@ -230,7 +255,7 @@ public final class EdgeGrpcSession implements Closeable { @Override public void onFailure(Throwable t) { - log.error("[{}][{}] Exception during sending sync complete", edge.getTenantId(), edge.getId(), t); + log.error("[{}][{}] Exception during sending sync complete", tenantId, edge.getId(), t); } }, ctx.getGrpcCallbackExecutorService()); } @@ -266,39 +291,40 @@ public final class EdgeGrpcSession implements Closeable { try { if (msg.getSuccess()) { sessionState.getPendingMsgsMap().remove(msg.getDownlinkMsgId()); - log.debug("[{}] Msg has been processed successfully!Msd Id: [{}], Msg: {}", edge.getRoutingKey(), msg.getDownlinkMsgId(), msg); + log.debug("[{}][{}] Msg has been processed successfully!Msg Id: [{}], Msg: {}", this.tenantId, edge.getRoutingKey(), msg.getDownlinkMsgId(), msg); } else { - log.error("[{}] Msg processing failed! Msd Id: [{}], Error msg: {}", edge.getRoutingKey(), msg.getDownlinkMsgId(), msg.getErrorMsg()); + log.error("[{}][{}] Msg processing failed! Msg Id: [{}], Error msg: {}", this.tenantId, edge.getRoutingKey(), msg.getDownlinkMsgId(), msg.getErrorMsg()); } if (sessionState.getPendingMsgsMap().isEmpty()) { - log.debug("[{}] Pending msgs map is empty. Stopping current iteration", edge.getRoutingKey()); + log.debug("[{}][{}] Pending msgs map is empty. Stopping current iteration", this.tenantId, edge.getRoutingKey()); stopCurrentSendDownlinkMsgsTask(false); } } catch (Exception e) { - log.error("[{}] Can't process downlink response message [{}]", this.sessionId, msg, e); + log.error("[{}][{}] Can't process downlink response message [{}]", this.tenantId, this.sessionId, msg, e); } } private void sendDownlinkMsg(ResponseMsg downlinkMsg) { - log.trace("[{}] Sending downlink msg [{}]", this.sessionId, downlinkMsg); + log.trace("[{}][{}] Sending downlink msg [{}]", this.tenantId, this.sessionId, downlinkMsg); if (isConnected()) { downlinkMsgLock.lock(); try { outputStream.onNext(downlinkMsg); } catch (Exception e) { - log.error("[{}] Failed to send downlink message [{}]", this.sessionId, downlinkMsg, e); + log.error("[{}][{}] Failed to send downlink message [{}]", this.tenantId, this.sessionId, downlinkMsg, e); connected = false; - sessionCloseListener.accept(edge.getId()); + sessionCloseListener.accept(edge.getId(), sessionId); } finally { downlinkMsgLock.unlock(); } - log.trace("[{}] Response msg successfully sent [{}]", this.sessionId, downlinkMsg); + log.trace("[{}][{}] Response msg successfully sent [{}]", this.tenantId, this.sessionId, downlinkMsg); } } void onConfigurationUpdate(Edge edge) { log.debug("[{}] onConfigurationUpdate [{}]", this.sessionId, edge); this.edge = edge; + this.tenantId = edge.getTenantId(); EdgeUpdateMsg edgeConfig = EdgeUpdateMsg.newBuilder() .setConfiguration(ctx.getEdgeMsgConstructor().constructEdgeConfiguration(edge)).build(); ResponseMsg edgeConfigMsg = ResponseMsg.newBuilder() @@ -307,78 +333,100 @@ public final class EdgeGrpcSession implements Closeable { sendDownlinkMsg(edgeConfigMsg); } - ListenableFuture processEdgeEvents() throws Exception { - SettableFuture result = SettableFuture.create(); - log.trace("[{}] starting processing edge events", this.sessionId); + ListenableFuture processEdgeEvents() throws Exception { + SettableFuture result = SettableFuture.create(); + log.trace("[{}][{}] starting processing edge events", this.tenantId, this.sessionId); if (isConnected() && isSyncCompleted()) { - Long queueStartTs = getQueueStartTs().get(); + Pair startTsAndSeqId = getQueueStartTsAndSeqId().get(); + this.previousStartTs = startTsAndSeqId.getFirst(); + this.previousStartSeqId = startTsAndSeqId.getSecond(); GeneralEdgeEventFetcher fetcher = new GeneralEdgeEventFetcher( - queueStartTs, + this.previousStartTs, + this.previousStartSeqId, + this.seqIdEnd, + false, + Integer.toUnsignedLong(ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount()), ctx.getEdgeEventService()); - ListenableFuture ifOffsetFuture = startProcessingEdgeEvents(fetcher); - Futures.addCallback(ifOffsetFuture, new FutureCallback<>() { + Futures.addCallback(startProcessingEdgeEvents(fetcher), new FutureCallback<>() { @Override - public void onSuccess(@Nullable UUID ifOffset) { - if (ifOffset != null) { - Long newStartTs = Uuids.unixTimestamp(ifOffset); - ListenableFuture> updateFuture = updateQueueStartTs(newStartTs); + public void onSuccess(@Nullable Pair newStartTsAndSeqId) { + if (newStartTsAndSeqId != null) { + ListenableFuture> updateFuture = updateQueueStartTsAndSeqId(newStartTsAndSeqId); Futures.addCallback(updateFuture, new FutureCallback<>() { @Override public void onSuccess(@Nullable List list) { - log.debug("[{}] queue offset was updated [{}][{}]", sessionId, ifOffset, newStartTs); - result.set(null); + log.debug("[{}][{}] queue offset was updated [{}]", tenantId, sessionId, newStartTsAndSeqId); + if (fetcher.isSeqIdNewCycleStarted()) { + seqIdEnd = fetcher.getSeqIdEnd(); + boolean newEventsAvailable = isNewEdgeEventsAvailable(); + result.set(newEventsAvailable); + } else { + seqIdEnd = null; + boolean newEventsAvailable = isSeqIdStartedNewCycle(); + if (!newEventsAvailable) { + newEventsAvailable = isNewEdgeEventsAvailable(); + } + result.set(newEventsAvailable); + } } @Override public void onFailure(Throwable t) { - log.error("[{}] Failed to update queue offset [{}]", sessionId, ifOffset, t); + log.error("[{}][{}] Failed to update queue offset [{}]", tenantId, sessionId, newStartTsAndSeqId, t); result.setException(t); } }, ctx.getGrpcCallbackExecutorService()); } else { - log.trace("[{}] ifOffset is null. Skipping iteration without db update", sessionId); + log.trace("[{}][{}] newStartTsAndSeqId is null. Skipping iteration without db update", tenantId, sessionId); result.set(null); } } @Override public void onFailure(Throwable t) { - log.error("[{}] Failed to process events", sessionId, t); + log.error("[{}][{}] Failed to process events", tenantId, sessionId, t); result.setException(t); } }, ctx.getGrpcCallbackExecutorService()); } else { - log.trace("[{}] edge is not connected or sync is not completed. Skipping iteration", sessionId); + log.trace("[{}][{}] edge is not connected or sync is not completed. Skipping iteration", tenantId, sessionId); result.set(null); } return result; } - private ListenableFuture startProcessingEdgeEvents(EdgeEventFetcher fetcher) { - SettableFuture result = SettableFuture.create(); + private ListenableFuture> startProcessingEdgeEvents(EdgeEventFetcher fetcher) { + SettableFuture> result = SettableFuture.create(); PageLink pageLink = fetcher.getPageLink(ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount()); processEdgeEvents(fetcher, pageLink, result); return result; } - private void processEdgeEvents(EdgeEventFetcher fetcher, PageLink pageLink, SettableFuture result) { + private void processEdgeEvents(EdgeEventFetcher fetcher, PageLink pageLink, SettableFuture> result) { try { PageData pageData = fetcher.fetchEdgeEvents(edge.getTenantId(), edge, pageLink); if (isConnected() && !pageData.getData().isEmpty()) { - log.trace("[{}] [{}] event(s) are going to be processed.", this.sessionId, pageData.getData().size()); + log.trace("[{}][{}][{}] event(s) are going to be processed.", this.tenantId, this.sessionId, pageData.getData().size()); List downlinkMsgsPack = convertToDownlinkMsgsPack(pageData.getData()); Futures.addCallback(sendDownlinkMsgsPack(downlinkMsgsPack), new FutureCallback<>() { @Override public void onSuccess(@Nullable Boolean isInterrupted) { if (Boolean.TRUE.equals(isInterrupted)) { - log.debug("[{}][{}][{}] Send downlink messages task was interrupted", edge.getTenantId(), edge.getId(), sessionId); + log.debug("[{}][{}][{}] Send downlink messages task was interrupted", tenantId, edge.getId(), sessionId); result.set(null); } else { if (isConnected() && pageData.hasNext()) { processEdgeEvents(fetcher, pageLink.nextPageLink(), result); } else { - UUID ifOffset = pageData.getData().get(pageData.getData().size() - 1).getUuidId(); - result.set(ifOffset); + EdgeEvent latestEdgeEvent = pageData.getData().get(pageData.getData().size() - 1); + UUID idOffset = latestEdgeEvent.getUuidId(); + if (idOffset != null) { + Long newStartTs = Uuids.unixTimestamp(idOffset); + long newStartSeqId = latestEdgeEvent.getSeqId(); + result.set(Pair.of(newStartTs, newStartSeqId)); + } else { + result.set(null); + } } } } @@ -417,14 +465,14 @@ public final class EdgeGrpcSession implements Closeable { if (isConnected() && sessionState.getPendingMsgsMap().values().size() > 0) { List copy = new ArrayList<>(sessionState.getPendingMsgsMap().values()); if (attempt > 1) { - log.warn("[{}] Failed to deliver the batch: {}, attempt: {}", this.sessionId, copy, attempt); + log.warn("[{}][{}] Failed to deliver the batch: {}, attempt: {}", this.tenantId, this.sessionId, copy, attempt); } - log.trace("[{}] [{}] downlink msg(s) are going to be send.", this.sessionId, copy.size()); + log.trace("[{}][{}][{}] downlink msg(s) are going to be send.", this.tenantId, this.sessionId, copy.size()); for (DownlinkMsg downlinkMsg : copy) { if (this.clientMaxInboundMessageSize != 0 && downlinkMsg.getSerializedSize() > this.clientMaxInboundMessageSize) { log.error("[{}][{}][{}] Downlink msg size [{}] exceeds client max inbound message size [{}]. Skipping this message. " + "Please increase value of CLOUD_RPC_MAX_INBOUND_MESSAGE_SIZE env variable on the edge and restart it." + - "Message {}", edge.getTenantId(), edge.getId(), this.sessionId, downlinkMsg.getSerializedSize(), + "Message {}", this.tenantId, edge.getId(), this.sessionId, downlinkMsg.getSerializedSize(), this.clientMaxInboundMessageSize, downlinkMsg); sessionState.getPendingMsgsMap().remove(downlinkMsg.getDownlinkMsgId()); } else { @@ -436,15 +484,15 @@ public final class EdgeGrpcSession implements Closeable { if (attempt < MAX_DOWNLINK_ATTEMPTS) { scheduleDownlinkMsgsPackSend(attempt + 1); } else { - log.warn("[{}] Failed to deliver the batch after {} attempts. Next messages are going to be discarded {}", - this.sessionId, MAX_DOWNLINK_ATTEMPTS, copy); + log.warn("[{}][{}] Failed to deliver the batch after {} attempts. Next messages are going to be discarded {}", + this.tenantId, this.sessionId, MAX_DOWNLINK_ATTEMPTS, copy); stopCurrentSendDownlinkMsgsTask(false); } } else { stopCurrentSendDownlinkMsgsTask(false); } } catch (Exception e) { - log.warn("[{}] Failed to send downlink msgs. Error msg {}", this.sessionId, e.getMessage(), e); + log.warn("[{}][{}] Failed to send downlink msgs. Error msg {}", this.tenantId, this.sessionId, e.getMessage(), e); stopCurrentSendDownlinkMsgsTask(true); } }; @@ -461,113 +509,163 @@ public final class EdgeGrpcSession implements Closeable { } } - private DownlinkMsg convertToDownlinkMsg(EdgeEvent edgeEvent) { - log.trace("[{}][{}] converting edge event to downlink msg [{}]", edge.getTenantId(), this.sessionId, edgeEvent); - DownlinkMsg downlinkMsg = null; - try { - switch (edgeEvent.getAction()) { - case UPDATED: - case ADDED: - case DELETED: - case ASSIGNED_TO_EDGE: - case UNASSIGNED_FROM_EDGE: - case ALARM_ACK: - case ALARM_CLEAR: - case CREDENTIALS_UPDATED: - case RELATION_ADD_OR_UPDATE: - case RELATION_DELETED: - case ASSIGNED_TO_CUSTOMER: - case UNASSIGNED_FROM_CUSTOMER: - case CREDENTIALS_REQUEST: - case RPC_CALL: - downlinkMsg = convertEntityEventToDownlink(edgeEvent); - log.trace("[{}][{}] entity message processed [{}]", edgeEvent.getTenantId(), this.sessionId, downlinkMsg); - break; - case ATTRIBUTES_UPDATED: - case POST_ATTRIBUTES: - case ATTRIBUTES_DELETED: - case TIMESERIES_UPDATED: - downlinkMsg = ctx.getTelemetryProcessor().convertTelemetryEventToDownlink(edgeEvent); - break; - default: - log.warn("[{}][{}] Unsupported action type [{}]", edge.getTenantId(), this.sessionId, edgeEvent.getAction()); + private List convertToDownlinkMsgsPack(List edgeEvents) { + List result = new ArrayList<>(); + for (EdgeEvent edgeEvent : edgeEvents) { + log.trace("[{}][{}] converting edge event to downlink msg [{}]", this.tenantId, this.sessionId, edgeEvent); + DownlinkMsg downlinkMsg = null; + try { + switch (edgeEvent.getAction()) { + case UPDATED: + case ADDED: + case DELETED: + case ASSIGNED_TO_EDGE: + case UNASSIGNED_FROM_EDGE: + case ALARM_ACK: + case ALARM_CLEAR: + case CREDENTIALS_UPDATED: + case RELATION_ADD_OR_UPDATE: + case RELATION_DELETED: + case CREDENTIALS_REQUEST: + case RPC_CALL: + case ASSIGNED_TO_CUSTOMER: + case UNASSIGNED_FROM_CUSTOMER: + downlinkMsg = convertEntityEventToDownlink(edgeEvent); + log.trace("[{}][{}] entity message processed [{}]", this.tenantId, this.sessionId, downlinkMsg); + break; + case ATTRIBUTES_UPDATED: + case POST_ATTRIBUTES: + case ATTRIBUTES_DELETED: + case TIMESERIES_UPDATED: + downlinkMsg = ctx.getTelemetryProcessor().convertTelemetryEventToDownlink(edgeEvent); + break; + default: + log.warn("[{}][{}] Unsupported action type [{}]", this.tenantId, this.sessionId, edgeEvent.getAction()); + } + } catch (Exception e) { + log.error("[{}][{}] Exception during converting edge event to downlink msg", this.tenantId, this.sessionId, e); + } + if (downlinkMsg != null) { + result.add(downlinkMsg); + } + } + return result; + } + + private ListenableFuture> getQueueStartTsAndSeqId() { + ListenableFuture> future = + ctx.getAttributesService().find(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, Arrays.asList(QUEUE_START_TS_ATTR_KEY, QUEUE_START_SEQ_ID_ATTR_KEY)); + return Futures.transform(future, attributeKvEntries -> { + long startTs = 0L; + long startSeqId = 0L; + for (AttributeKvEntry attributeKvEntry : attributeKvEntries) { + if (QUEUE_START_TS_ATTR_KEY.equals(attributeKvEntry.getKey())) { + startTs = attributeKvEntry.getLongValue().isPresent() ? attributeKvEntry.getLongValue().get() : 0L; + } + if (QUEUE_START_SEQ_ID_ATTR_KEY.equals(attributeKvEntry.getKey())) { + startSeqId = attributeKvEntry.getLongValue().isPresent() ? attributeKvEntry.getLongValue().get() : 0L; + } } + if (startSeqId == 0L) { + startSeqId = findStartSeqIdFromOldestEventIfAny(); + } + return Pair.of(startTs, startSeqId); + }, ctx.getGrpcCallbackExecutorService()); + } + + private boolean isSeqIdStartedNewCycle() { + try { + TimePageLink pageLink = new TimePageLink(ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount(), 0, null, null, this.newStartTs, System.currentTimeMillis()); + PageData edgeEvents = ctx.getEdgeEventService().findEdgeEvents(edge.getTenantId(), edge.getId(), 0L, this.previousStartSeqId == 0 ? null : this.previousStartSeqId - 1, pageLink); + return !edgeEvents.getData().isEmpty(); } catch (Exception e) { - log.error("[{}][{}] Exception during converting edge event to downlink msg", edge.getTenantId(), this.sessionId, e); + log.error("[{}][{}][{}] Failed to execute isSeqIdStartedNewCycle", this.tenantId, edge.getId(), sessionId, e); } - return downlinkMsg; + return false; } - private List convertToDownlinkMsgsPack(List edgeEvents) { - return edgeEvents - .stream() - .map(this::convertToDownlinkMsg) - .filter(Objects::nonNull) - .collect(Collectors.toList()); + private boolean isNewEdgeEventsAvailable() { + try { + TimePageLink pageLink = new TimePageLink(ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount(), 0, null, null, this.newStartTs, System.currentTimeMillis()); + PageData edgeEvents = ctx.getEdgeEventService().findEdgeEvents(edge.getTenantId(), edge.getId(), this.newStartSeqId, null, pageLink); + return !edgeEvents.getData().isEmpty(); + } catch (Exception e) { + log.error("[{}][{}][{}] Failed to execute isNewEdgeEventsAvailable", this.tenantId, edge.getId(), sessionId, e); + } + return false; } - private ListenableFuture getQueueStartTs() { - ListenableFuture> future = - ctx.getAttributesService().find(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, QUEUE_START_TS_ATTR_KEY); - return Futures.transform(future, attributeKvEntryOpt -> { - if (attributeKvEntryOpt != null && attributeKvEntryOpt.isPresent()) { - AttributeKvEntry attributeKvEntry = attributeKvEntryOpt.get(); - return attributeKvEntry.getLongValue().isPresent() ? attributeKvEntry.getLongValue().get() : 0L; - } else { - return 0L; + private long findStartSeqIdFromOldestEventIfAny() { + long startSeqId = 0L; + try { + TimePageLink pageLink = new TimePageLink(1, 0, null, new SortOrder("createdTime"), null, null); + PageData edgeEvents = ctx.getEdgeEventService().findEdgeEvents(edge.getTenantId(), edge.getId(), null, null, pageLink); + if (!edgeEvents.getData().isEmpty()) { + startSeqId = edgeEvents.getData().get(0).getSeqId() - 1; } - }, ctx.getGrpcCallbackExecutorService()); + } catch (Exception e) { + log.error("[{}][{}][{}] Failed to execute findStartSeqIdFromOldestEventIfAny", this.tenantId, edge.getId(), sessionId, e); + } + return startSeqId; } - private ListenableFuture> updateQueueStartTs(Long newStartTs) { - log.trace("[{}] updating QueueStartTs [{}][{}]", this.sessionId, edge.getId(), newStartTs); - List attributes = Collections.singletonList( - new BaseAttributeKvEntry( - new LongDataEntry(QUEUE_START_TS_ATTR_KEY, newStartTs), System.currentTimeMillis())); + private ListenableFuture> updateQueueStartTsAndSeqId(Pair pair) { + this.newStartTs = pair.getFirst(); + this.newStartSeqId = pair.getSecond(); + log.trace("[{}] updateQueueStartTsAndSeqId [{}][{}][{}]", this.sessionId, edge.getId(), this.newStartTs, this.newStartSeqId); + List attributes = Arrays.asList( + new BaseAttributeKvEntry(new LongDataEntry(QUEUE_START_TS_ATTR_KEY, this.newStartTs), System.currentTimeMillis()), + new BaseAttributeKvEntry(new LongDataEntry(QUEUE_START_SEQ_ID_ATTR_KEY, this.newStartSeqId), System.currentTimeMillis())); return ctx.getAttributesService().save(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, attributes); } private DownlinkMsg convertEntityEventToDownlink(EdgeEvent edgeEvent) { - log.trace("Executing convertEntityEventToDownlink, edgeEvent [{}], action [{}]", edgeEvent, edgeEvent.getAction()); + log.trace("[{}] Executing convertEntityEventToDownlink, edgeEvent [{}], action [{}]", this.tenantId, edgeEvent, edgeEvent.getAction()); switch (edgeEvent.getType()) { case EDGE: return ctx.getEdgeProcessor().convertEdgeEventToDownlink(edgeEvent); case DEVICE: - return ctx.getDeviceProcessor().convertDeviceEventToDownlink(edgeEvent); + return ctx.getDeviceProcessor().convertDeviceEventToDownlink(edgeEvent, this.edge.getId(), this.edgeVersion); case DEVICE_PROFILE: - return ctx.getDeviceProfileProcessor().convertDeviceProfileEventToDownlink(edgeEvent); + return ctx.getDeviceProfileProcessor().convertDeviceProfileEventToDownlink(edgeEvent, this.edge.getId(), this.edgeVersion); case ASSET_PROFILE: - return ctx.getAssetProfileProcessor().convertAssetProfileEventToDownlink(edgeEvent); + return ctx.getAssetProfileProcessor().convertAssetProfileEventToDownlink(edgeEvent, this.edge.getId(), this.edgeVersion); case ASSET: - return ctx.getAssetProcessor().convertAssetEventToDownlink(edgeEvent); + return ctx.getAssetProcessor().convertAssetEventToDownlink(edgeEvent, this.edge.getId(), this.edgeVersion); case ENTITY_VIEW: - return ctx.getEntityViewProcessor().convertEntityViewEventToDownlink(edgeEvent); + return ctx.getEntityViewProcessor().convertEntityViewEventToDownlink(edgeEvent, this.edgeVersion); case DASHBOARD: - return ctx.getDashboardProcessor().convertDashboardEventToDownlink(edgeEvent); + return ctx.getDashboardProcessor().convertDashboardEventToDownlink(edgeEvent, this.edgeVersion); case CUSTOMER: - return ctx.getCustomerProcessor().convertCustomerEventToDownlink(edgeEvent); + return ctx.getCustomerProcessor().convertCustomerEventToDownlink(edgeEvent, this.edgeVersion); case RULE_CHAIN: - return ctx.getRuleChainProcessor().convertRuleChainEventToDownlink(edgeEvent); + return ctx.getRuleChainProcessor().convertRuleChainEventToDownlink(edgeEvent, this.edgeVersion); case RULE_CHAIN_METADATA: return ctx.getRuleChainProcessor().convertRuleChainMetadataEventToDownlink(edgeEvent, this.edgeVersion); case ALARM: - return ctx.getAlarmProcessor().convertAlarmEventToDownlink(edgeEvent); + return ctx.getAlarmProcessor().convertAlarmEventToDownlink(edgeEvent, this.edgeVersion); case USER: - return ctx.getUserProcessor().convertUserEventToDownlink(edgeEvent); + return ctx.getUserProcessor().convertUserEventToDownlink(edgeEvent, this.edgeVersion); case RELATION: - return ctx.getRelationProcessor().convertRelationEventToDownlink(edgeEvent); + return ctx.getRelationProcessor().convertRelationEventToDownlink(edgeEvent, this.edgeVersion); case WIDGETS_BUNDLE: - return ctx.getWidgetBundleProcessor().convertWidgetsBundleEventToDownlink(edgeEvent); + return ctx.getWidgetBundleProcessor().convertWidgetsBundleEventToDownlink(edgeEvent, this.edgeVersion); case WIDGET_TYPE: - return ctx.getWidgetTypeProcessor().convertWidgetTypeEventToDownlink(edgeEvent); + return ctx.getWidgetTypeProcessor().convertWidgetTypeEventToDownlink(edgeEvent, this.edgeVersion); case ADMIN_SETTINGS: - return ctx.getAdminSettingsProcessor().convertAdminSettingsEventToDownlink(edgeEvent); + return ctx.getAdminSettingsProcessor().convertAdminSettingsEventToDownlink(edgeEvent, this.edgeVersion); case OTA_PACKAGE: - return ctx.getOtaPackageEdgeProcessor().convertOtaPackageEventToDownlink(edgeEvent); + return ctx.getOtaPackageEdgeProcessor().convertOtaPackageEventToDownlink(edgeEvent, this.edgeVersion); + case TB_RESOURCE: + return ctx.getResourceEdgeProcessor().convertResourceEventToDownlink(edgeEvent, this.edgeVersion); case QUEUE: - return ctx.getQueueEdgeProcessor().convertQueueEventToDownlink(edgeEvent); + return ctx.getQueueEdgeProcessor().convertQueueEventToDownlink(edgeEvent, this.edgeVersion); + case TENANT: + return ctx.getTenantEdgeProcessor().convertTenantEventToDownlink(edgeEvent, this.edgeVersion); + case TENANT_PROFILE: + return ctx.getTenantProfileEdgeProcessor().convertTenantProfileEventToDownlink(edgeEvent, this.edgeVersion); default: - log.warn("Unsupported edge event type [{}]", edgeEvent); + log.warn("[{}] Unsupported edge event type [{}]", this.tenantId, edgeEvent); return null; } } @@ -580,24 +678,63 @@ public final class EdgeGrpcSession implements Closeable { result.addAll(ctx.getTelemetryProcessor().processTelemetryMsg(edge.getTenantId(), entityData)); } } + if (uplinkMsg.getDeviceProfileUpdateMsgCount() > 0) { + for (DeviceProfileUpdateMsg deviceProfileUpdateMsg : uplinkMsg.getDeviceProfileUpdateMsgList()) { + result.add(((DeviceProfileProcessor) ctx.getDeviceProfileEdgeProcessorFactory().getProcessorByEdgeVersion(this.edgeVersion)) + .processDeviceProfileMsgFromEdge(edge.getTenantId(), edge, deviceProfileUpdateMsg)); + } + } if (uplinkMsg.getDeviceUpdateMsgCount() > 0) { for (DeviceUpdateMsg deviceUpdateMsg : uplinkMsg.getDeviceUpdateMsgList()) { - result.add(ctx.getDeviceProcessor().processDeviceMsgFromEdge(edge.getTenantId(), edge, deviceUpdateMsg)); + result.add(((DeviceProcessor) ctx.getDeviceEdgeProcessorFactory().getProcessorByEdgeVersion(this.edgeVersion)) + .processDeviceMsgFromEdge(edge.getTenantId(), edge, deviceUpdateMsg)); } } if (uplinkMsg.getDeviceCredentialsUpdateMsgCount() > 0) { for (DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg : uplinkMsg.getDeviceCredentialsUpdateMsgList()) { - result.add(ctx.getDeviceProcessor().processDeviceCredentialsMsg(edge.getTenantId(), deviceCredentialsUpdateMsg)); + result.add(((DeviceProcessor) ctx.getDeviceEdgeProcessorFactory().getProcessorByEdgeVersion(this.edgeVersion)) + .processDeviceCredentialsMsgFromEdge(edge.getTenantId(), edge.getId(), deviceCredentialsUpdateMsg)); + } + } + if (uplinkMsg.getAssetProfileUpdateMsgCount() > 0) { + for (AssetProfileUpdateMsg assetProfileUpdateMsg : uplinkMsg.getAssetProfileUpdateMsgList()) { + result.add(((AssetProfileProcessor) ctx.getAssetProfileEdgeProcessorFactory().getProcessorByEdgeVersion(this.edgeVersion)) + .processAssetProfileMsgFromEdge(edge.getTenantId(), edge, assetProfileUpdateMsg)); + } + } + if (uplinkMsg.getAssetUpdateMsgCount() > 0) { + for (AssetUpdateMsg assetUpdateMsg : uplinkMsg.getAssetUpdateMsgList()) { + result.add(((AssetProcessor) ctx.getAssetEdgeProcessorFactory().getProcessorByEdgeVersion(edgeVersion)) + .processAssetMsgFromEdge(edge.getTenantId(), edge, assetUpdateMsg)); } } if (uplinkMsg.getAlarmUpdateMsgCount() > 0) { for (AlarmUpdateMsg alarmUpdateMsg : uplinkMsg.getAlarmUpdateMsgList()) { - result.add(ctx.getAlarmProcessor().processAlarmMsg(edge.getTenantId(), alarmUpdateMsg)); + result.add(((AlarmProcessor) ctx.getAlarmEdgeProcessorFactory().getProcessorByEdgeVersion(this.edgeVersion)) + .processAlarmMsgFromEdge(edge.getTenantId(), edge.getId(), alarmUpdateMsg)); + } + } + if (uplinkMsg.getEntityViewUpdateMsgCount() > 0) { + for (EntityViewUpdateMsg entityViewUpdateMsg : uplinkMsg.getEntityViewUpdateMsgList()) { + result.add(((EntityViewProcessor) ctx.getEntityViewProcessorFactory().getProcessorByEdgeVersion(this.edgeVersion)) + .processEntityViewMsgFromEdge(edge.getTenantId(), edge, entityViewUpdateMsg)); } } if (uplinkMsg.getRelationUpdateMsgCount() > 0) { for (RelationUpdateMsg relationUpdateMsg : uplinkMsg.getRelationUpdateMsgList()) { - result.add(ctx.getRelationProcessor().processRelationMsg(edge.getTenantId(), relationUpdateMsg)); + result.add(((RelationProcessor) ctx.getRelationEdgeProcessorFactory().getProcessorByEdgeVersion(this.edgeVersion)) + .processRelationMsgFromEdge(edge.getTenantId(), edge, relationUpdateMsg)); + } + } + if (uplinkMsg.getDashboardUpdateMsgCount() > 0) { + for (DashboardUpdateMsg dashboardUpdateMsg : uplinkMsg.getDashboardUpdateMsgList()) { + result.add(((DashboardProcessor) ctx.getDashboardEdgeProcessorFactory().getProcessorByEdgeVersion(this.edgeVersion)) + .processDashboardMsgFromEdge(edge.getTenantId(), edge, dashboardUpdateMsg)); + } + } + if (uplinkMsg.getResourceUpdateMsgCount() > 0) { + for (ResourceUpdateMsg resourceUpdateMsg : uplinkMsg.getResourceUpdateMsgList()) { + result.add(((ResourceProcessor) ctx.getResourceEdgeProcessorFactory().getProcessorByEdgeVersion(this.edgeVersion)).processResourceMsgFromEdge(edge.getTenantId(), edge, resourceUpdateMsg)); } } if (uplinkMsg.getRuleChainMetadataRequestMsgCount() > 0) { @@ -627,7 +764,8 @@ public final class EdgeGrpcSession implements Closeable { } if (uplinkMsg.getDeviceRpcCallMsgCount() > 0) { for (DeviceRpcCallMsg deviceRpcCallMsg : uplinkMsg.getDeviceRpcCallMsgList()) { - result.add(ctx.getDeviceProcessor().processDeviceRpcCallFromEdge(edge.getTenantId(), edge, deviceRpcCallMsg)); + result.add(((DeviceProcessor) ctx.getDeviceEdgeProcessorFactory().getProcessorByEdgeVersion(this.edgeVersion)) + .processDeviceRpcCallFromEdge(edge.getTenantId(), edge, deviceRpcCallMsg)); } } if (uplinkMsg.getWidgetBundleTypesRequestMsgCount() > 0) { @@ -641,7 +779,7 @@ public final class EdgeGrpcSession implements Closeable { } } } catch (Exception e) { - log.error("[{}] Can't process uplink msg [{}]", this.sessionId, uplinkMsg, e); + log.error("[{}][{}] Can't process uplink msg [{}]", this.tenantId, this.sessionId, uplinkMsg, e); return Futures.immediateFailedFuture(e); } return Futures.allAsList(result); @@ -652,10 +790,12 @@ public final class EdgeGrpcSession implements Closeable { Optional optional = ctx.getEdgeService().findEdgeByRoutingKey(TenantId.SYS_TENANT_ID, request.getEdgeRoutingKey()); if (optional.isPresent()) { edge = optional.get(); + this.tenantId = edge.getTenantId(); try { if (edge.getSecret().equals(request.getEdgeSecret())) { sessionOpenListener.accept(edge.getId(), this); this.edgeVersion = request.getEdgeVersion(); + processSaveEdgeVersionAsAttribute(request.getEdgeVersion().name()); return ConnectResponseMsg.newBuilder() .setResponseCode(ConnectResponseCode.ACCEPTED) .setErrorMsg("") @@ -681,24 +821,32 @@ public final class EdgeGrpcSession implements Closeable { .setConfiguration(EdgeConfiguration.getDefaultInstance()).build(); } + private void processSaveEdgeVersionAsAttribute(String edgeVersion) { + AttributeKvEntry attributeKvEntry = new BaseAttributeKvEntry(new StringDataEntry(DataConstants.EDGE_VERSION_ATTR_KEY, edgeVersion), System.currentTimeMillis()); + ctx.getAttributesService().save(this.tenantId, this.edge.getId(), DataConstants.SERVER_SCOPE, attributeKvEntry); + } + @Override public void close() { - log.debug("[{}] Closing session", sessionId); + log.debug("[{}][{}] Closing session", this.tenantId, sessionId); connected = false; try { outputStream.onCompleted(); } catch (Exception e) { - log.debug("[{}] Failed to close output stream: {}", sessionId, e.getMessage()); + log.debug("[{}][{}] Failed to close output stream: {}", this.tenantId, sessionId, e.getMessage()); } } private void interruptPreviousSendDownlinkMsgsTask() { - log.debug("[{}][{}][{}] Previous send downlink future was not properly completed, stopping it now!", edge.getTenantId(), edge.getId(), this.sessionId); - stopCurrentSendDownlinkMsgsTask(true); + if (sessionState.getSendDownlinkMsgsFuture() != null && !sessionState.getSendDownlinkMsgsFuture().isDone() + || sessionState.getScheduledSendDownlinkTask() != null && !sessionState.getScheduledSendDownlinkTask().isCancelled()) { + log.debug("[{}][{}][{}] Previous send downlink future was not properly completed, stopping it now!", this.tenantId, edge.getId(), this.sessionId); + stopCurrentSendDownlinkMsgsTask(true); + } } private void interruptGeneralProcessingOnSync() { - log.debug("[{}][{}][{}] Sync process started. General processing interrupted!", edge.getTenantId(), edge.getId(), this.sessionId); + log.debug("[{}][{}][{}] Sync process started. General processing interrupted!", this.tenantId, edge.getId(), this.sessionId); stopCurrentSendDownlinkMsgsTask(true); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java index bf3e804f7e..0fc5c93388 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java @@ -25,6 +25,7 @@ import org.thingsboard.server.service.edge.rpc.fetch.AssetsEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.CustomerEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.CustomerUsersEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.DashboardsEdgeEventFetcher; +import org.thingsboard.server.service.edge.rpc.fetch.DefaultProfilesEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.DeviceProfilesEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.DevicesEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.EdgeEventFetcher; @@ -32,8 +33,12 @@ import org.thingsboard.server.service.edge.rpc.fetch.EntityViewsEdgeEventFetcher import org.thingsboard.server.service.edge.rpc.fetch.OtaPackagesEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.QueuesEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.RuleChainsEdgeEventFetcher; +import org.thingsboard.server.service.edge.rpc.fetch.SystemWidgetTypesEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.SystemWidgetsBundlesEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.TenantAdminUsersEdgeEventFetcher; +import org.thingsboard.server.service.edge.rpc.fetch.TenantEdgeEventFetcher; +import org.thingsboard.server.service.edge.rpc.fetch.TenantResourcesEdgeEventFetcher; +import org.thingsboard.server.service.edge.rpc.fetch.TenantWidgetTypesEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.TenantWidgetsBundlesEdgeEventFetcher; import java.util.LinkedList; @@ -48,11 +53,10 @@ public class EdgeSyncCursor { public EdgeSyncCursor(EdgeContextComponent ctx, Edge edge, boolean fullSync) { if (fullSync) { + fetchers.add(new TenantEdgeEventFetcher(ctx.getTenantService())); fetchers.add(new QueuesEdgeEventFetcher(ctx.getQueueService())); fetchers.add(new RuleChainsEdgeEventFetcher(ctx.getRuleChainService())); - fetchers.add(new AdminSettingsEdgeEventFetcher(ctx.getAdminSettingsService(), ctx.getFreemarkerConfig())); - fetchers.add(new DeviceProfilesEdgeEventFetcher(ctx.getDeviceProfileService())); - fetchers.add(new AssetProfilesEdgeEventFetcher(ctx.getAssetProfileService())); + fetchers.add(new AdminSettingsEdgeEventFetcher(ctx.getAdminSettingsService())); fetchers.add(new TenantAdminUsersEdgeEventFetcher(ctx.getUserService())); Customer publicCustomer = ctx.getCustomerService().findOrCreatePublicCustomer(edge.getTenantId()); fetchers.add(new CustomerEdgeEventFetcher(publicCustomer.getId())); @@ -61,14 +65,20 @@ public class EdgeSyncCursor { fetchers.add(new CustomerUsersEdgeEventFetcher(ctx.getUserService(), edge.getCustomerId())); } } + fetchers.add(new DashboardsEdgeEventFetcher(ctx.getDashboardService())); + fetchers.add(new DefaultProfilesEdgeEventFetcher(ctx.getDeviceProfileService(), ctx.getAssetProfileService())); + fetchers.add(new DeviceProfilesEdgeEventFetcher(ctx.getDeviceProfileService())); + fetchers.add(new AssetProfilesEdgeEventFetcher(ctx.getAssetProfileService())); fetchers.add(new DevicesEdgeEventFetcher(ctx.getDeviceService())); fetchers.add(new AssetsEdgeEventFetcher(ctx.getAssetService())); fetchers.add(new EntityViewsEdgeEventFetcher(ctx.getEntityViewService())); - fetchers.add(new DashboardsEdgeEventFetcher(ctx.getDashboardService())); if (fullSync) { + fetchers.add(new SystemWidgetTypesEdgeEventFetcher(ctx.getWidgetTypeService())); + fetchers.add(new TenantWidgetTypesEdgeEventFetcher(ctx.getWidgetTypeService())); fetchers.add(new SystemWidgetsBundlesEdgeEventFetcher(ctx.getWidgetsBundleService())); fetchers.add(new TenantWidgetsBundlesEdgeEventFetcher(ctx.getWidgetsBundleService())); fetchers.add(new OtaPackagesEdgeEventFetcher(ctx.getOtaPackageService())); + fetchers.add(new TenantResourcesEdgeEventFetcher(ctx.getResourceService())); } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java deleted file mode 100644 index 79754b867c..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import org.springframework.stereotype.Component; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.AdminSettings; -import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg; -import org.thingsboard.server.queue.util.TbCoreComponent; - -@Component -@TbCoreComponent -public class AdminSettingsMsgConstructor { - - public AdminSettingsUpdateMsg constructAdminSettingsUpdateMsg(AdminSettings adminSettings) { - AdminSettingsUpdateMsg.Builder builder = AdminSettingsUpdateMsg.newBuilder() - .setKey(adminSettings.getKey()) - .setJsonValue(JacksonUtil.toString(adminSettings.getJsonValue())); - if (adminSettings.getId() != null) { - builder.setIsSystem(true); - } - return builder.build(); - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java deleted file mode 100644 index 69a83da0a0..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.alarm.Alarm; -import org.thingsboard.server.common.data.id.AssetId; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.EntityViewId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.asset.AssetService; -import org.thingsboard.server.dao.device.DeviceService; -import org.thingsboard.server.dao.entityview.EntityViewService; -import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.queue.util.TbCoreComponent; - -@Component -@TbCoreComponent -public class AlarmMsgConstructor { - - @Autowired - private DeviceService deviceService; - - @Autowired - private AssetService assetService; - - @Autowired - private EntityViewService entityViewService; - - public AlarmUpdateMsg constructAlarmUpdatedMsg(TenantId tenantId, UpdateMsgType msgType, Alarm alarm) { - String entityName = null; - switch (alarm.getOriginator().getEntityType()) { - case DEVICE: - entityName = deviceService.findDeviceById(tenantId, new DeviceId(alarm.getOriginator().getId())).getName(); - break; - case ASSET: - entityName = assetService.findAssetById(tenantId, new AssetId(alarm.getOriginator().getId())).getName(); - break; - case ENTITY_VIEW: - entityName = entityViewService.findEntityViewById(tenantId, new EntityViewId(alarm.getOriginator().getId())).getName(); - break; - } - AlarmUpdateMsg.Builder builder = AlarmUpdateMsg.newBuilder() - .setMsgType(msgType) - .setIdMSB(alarm.getId().getId().getMostSignificantBits()) - .setIdLSB(alarm.getId().getId().getLeastSignificantBits()) - .setName(alarm.getName()) - .setType(alarm.getType()) - .setOriginatorName(entityName) - .setOriginatorType(alarm.getOriginator().getEntityType().name()) - .setSeverity(alarm.getSeverity().name()) - .setStatus(alarm.getStatus().name()) - .setStartTs(alarm.getStartTs()) - .setEndTs(alarm.getEndTs()) - .setAckTs(alarm.getAckTs()) - .setClearTs(alarm.getClearTs()) - .setDetails(JacksonUtil.toString(alarm.getDetails())) - .setPropagate(alarm.isPropagate()) - .setPropagateToOwner(alarm.isPropagateToOwner()) - .setPropagateToTenant(alarm.isPropagateToTenant()); - return builder.build(); - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java deleted file mode 100644 index 86ffc6dd5c..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import org.springframework.stereotype.Component; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.asset.Asset; -import org.thingsboard.server.common.data.id.AssetId; -import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.queue.util.TbCoreComponent; - -@Component -@TbCoreComponent -public class AssetMsgConstructor { - - public AssetUpdateMsg constructAssetUpdatedMsg(UpdateMsgType msgType, Asset asset) { - AssetUpdateMsg.Builder builder = AssetUpdateMsg.newBuilder() - .setMsgType(msgType) - .setIdMSB(asset.getUuidId().getMostSignificantBits()) - .setIdLSB(asset.getUuidId().getLeastSignificantBits()) - .setName(asset.getName()) - .setType(asset.getType()); - if (asset.getLabel() != null) { - builder.setLabel(asset.getLabel()); - } - if (asset.getCustomerId() != null) { - builder.setCustomerIdMSB(asset.getCustomerId().getId().getMostSignificantBits()); - builder.setCustomerIdLSB(asset.getCustomerId().getId().getLeastSignificantBits()); - } - if (asset.getAssetProfileId() != null) { - builder.setAssetProfileIdMSB(asset.getAssetProfileId().getId().getMostSignificantBits()); - builder.setAssetProfileIdLSB(asset.getAssetProfileId().getId().getLeastSignificantBits()); - } - if (asset.getAdditionalInfo() != null) { - builder.setAdditionalInfo(JacksonUtil.toString(asset.getAdditionalInfo())); - } - return builder.build(); - } - - public AssetUpdateMsg constructAssetDeleteMsg(AssetId assetId) { - return AssetUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(assetId.getId().getMostSignificantBits()) - .setIdLSB(assetId.getId().getLeastSignificantBits()).build(); - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java deleted file mode 100644 index f5b921a32b..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetProfileMsgConstructor.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import com.google.protobuf.ByteString; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.asset.AssetProfile; -import org.thingsboard.server.common.data.id.AssetProfileId; -import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.queue.util.TbCoreComponent; - -import java.nio.charset.StandardCharsets; - -@Component -@TbCoreComponent -public class AssetProfileMsgConstructor { - - public AssetProfileUpdateMsg constructAssetProfileUpdatedMsg(UpdateMsgType msgType, AssetProfile assetProfile) { - AssetProfileUpdateMsg.Builder builder = AssetProfileUpdateMsg.newBuilder() - .setMsgType(msgType) - .setIdMSB(assetProfile.getId().getId().getMostSignificantBits()) - .setIdLSB(assetProfile.getId().getId().getLeastSignificantBits()) - .setName(assetProfile.getName()) - .setDefault(assetProfile.isDefault()); - if (assetProfile.getDefaultDashboardId() != null) { - builder.setDefaultDashboardIdMSB(assetProfile.getDefaultDashboardId().getId().getMostSignificantBits()) - .setDefaultDashboardIdLSB(assetProfile.getDefaultDashboardId().getId().getLeastSignificantBits()); - } - if (assetProfile.getDefaultQueueName() != null) { - builder.setDefaultQueueName(assetProfile.getDefaultQueueName()); - } - if (assetProfile.getDescription() != null) { - builder.setDescription(assetProfile.getDescription()); - } - if (assetProfile.getImage() != null) { - builder.setImage(ByteString.copyFrom(assetProfile.getImage().getBytes(StandardCharsets.UTF_8))); - } - if (assetProfile.getDefaultEdgeRuleChainId() != null) { - builder.setDefaultRuleChainIdMSB(assetProfile.getDefaultEdgeRuleChainId().getId().getMostSignificantBits()) - .setDefaultRuleChainIdLSB(assetProfile.getDefaultEdgeRuleChainId().getId().getLeastSignificantBits()); - } - return builder.build(); - } - - public AssetProfileUpdateMsg constructAssetProfileDeleteMsg(AssetProfileId assetProfileId) { - return AssetProfileUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(assetProfileId.getId().getMostSignificantBits()) - .setIdLSB(assetProfileId.getId().getLeastSignificantBits()).build(); - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/BaseMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/BaseMsgConstructorFactory.java new file mode 100644 index 0000000000..e0982e3fba --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/BaseMsgConstructorFactory.java @@ -0,0 +1,46 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public abstract class BaseMsgConstructorFactory { + + @Autowired + protected T v1Constructor; + + @Autowired + protected U v2Constructor; + + public MsgConstructor getMsgConstructorByEdgeVersion(EdgeVersion edgeVersion) { + switch (edgeVersion) { + case V_3_3_0: + case V_3_3_3: + case V_3_4_0: + case V_3_6_0: + case V_3_6_1: + return v1Constructor; + case V_3_6_2: + default: + return v2Constructor; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java deleted file mode 100644 index 0465dd5a94..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import org.springframework.stereotype.Component; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.Customer; -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.queue.util.TbCoreComponent; - -@Component -@TbCoreComponent -public class CustomerMsgConstructor { - - public CustomerUpdateMsg constructCustomerUpdatedMsg(UpdateMsgType msgType, Customer customer) { - CustomerUpdateMsg.Builder builder = CustomerUpdateMsg.newBuilder() - .setMsgType(msgType) - .setIdMSB(customer.getId().getId().getMostSignificantBits()) - .setIdLSB(customer.getId().getId().getLeastSignificantBits()) - .setTitle(customer.getTitle()); - if (customer.getCountry() != null) { - builder.setCountry(customer.getCountry()); - } - if (customer.getState() != null) { - builder.setState(customer.getState()); - } - if (customer.getCity() != null) { - builder.setCity(customer.getCity()); - } - if (customer.getAddress() != null) { - builder.setAddress(customer.getAddress()); - } - if (customer.getAddress2() != null) { - builder.setAddress2(customer.getAddress2()); - } - if (customer.getZip() != null) { - builder.setZip(customer.getZip()); - } - if (customer.getPhone() != null) { - builder.setPhone(customer.getPhone()); - } - if (customer.getEmail() != null) { - builder.setEmail(customer.getEmail()); - } - if (customer.getAdditionalInfo() != null) { - builder.setAdditionalInfo(JacksonUtil.toString(customer.getAdditionalInfo())); - } - return builder.build(); - } - - public CustomerUpdateMsg constructCustomerDeleteMsg(CustomerId customerId) { - return CustomerUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(customerId.getId().getMostSignificantBits()) - .setIdLSB(customerId.getId().getLeastSignificantBits()).build(); - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java deleted file mode 100644 index edfdefb603..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import org.springframework.stereotype.Component; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.Dashboard; -import org.thingsboard.server.common.data.id.DashboardId; -import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.queue.util.TbCoreComponent; - -@Component -@TbCoreComponent -public class DashboardMsgConstructor { - - public DashboardUpdateMsg constructDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard) { - DashboardUpdateMsg.Builder builder = DashboardUpdateMsg.newBuilder() - .setMsgType(msgType) - .setIdMSB(dashboard.getId().getId().getMostSignificantBits()) - .setIdLSB(dashboard.getId().getId().getLeastSignificantBits()) - .setTitle(dashboard.getTitle()) - .setConfiguration(JacksonUtil.toString(dashboard.getConfiguration())); - if (dashboard.getAssignedCustomers() != null) { - builder.setAssignedCustomers(JacksonUtil.toString(dashboard.getAssignedCustomers())); - } - return builder.build(); - } - - public DashboardUpdateMsg constructDashboardDeleteMsg(DashboardId dashboardId) { - return DashboardUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(dashboardId.getId().getMostSignificantBits()) - .setIdLSB(dashboardId.getId().getLeastSignificantBits()).build(); - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java deleted file mode 100644 index 78b785a5d8..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java +++ /dev/null @@ -1,152 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import com.fasterxml.jackson.databind.JsonNode; -import com.google.protobuf.ByteString; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.security.DeviceCredentials; -import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; -import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg; -import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; -import org.thingsboard.server.gen.edge.v1.RpcRequestMsg; -import org.thingsboard.server.gen.edge.v1.RpcResponseMsg; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.queue.util.DataDecodingEncodingService; -import org.thingsboard.server.queue.util.TbCoreComponent; - -import java.util.UUID; - -@Component -@TbCoreComponent -public class DeviceMsgConstructor { - - @Autowired - private DataDecodingEncodingService dataDecodingEncodingService; - - public DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device) { - DeviceUpdateMsg.Builder builder = DeviceUpdateMsg.newBuilder() - .setMsgType(msgType) - .setIdMSB(device.getId().getId().getMostSignificantBits()) - .setIdLSB(device.getId().getId().getLeastSignificantBits()) - .setName(device.getName()) - .setType(device.getType()); - if (device.getLabel() != null) { - builder.setLabel(device.getLabel()); - } - if (device.getCustomerId() != null) { - builder.setCustomerIdMSB(device.getCustomerId().getId().getMostSignificantBits()); - builder.setCustomerIdLSB(device.getCustomerId().getId().getLeastSignificantBits()); - } - if (device.getDeviceProfileId() != null) { - builder.setDeviceProfileIdMSB(device.getDeviceProfileId().getId().getMostSignificantBits()); - builder.setDeviceProfileIdLSB(device.getDeviceProfileId().getId().getLeastSignificantBits()); - } - if (device.getAdditionalInfo() != null) { - builder.setAdditionalInfo(JacksonUtil.toString(device.getAdditionalInfo())); - } - if (device.getFirmwareId() != null) { - builder.setFirmwareIdMSB(device.getFirmwareId().getId().getMostSignificantBits()) - .setFirmwareIdLSB(device.getFirmwareId().getId().getLeastSignificantBits()); - } - if (device.getSoftwareId() != null) { - builder.setSoftwareIdMSB(device.getSoftwareId().getId().getMostSignificantBits()) - .setSoftwareIdLSB(device.getSoftwareId().getId().getLeastSignificantBits()); - } - if (device.getDeviceData() != null) { - builder.setDeviceDataBytes(ByteString.copyFrom(dataDecodingEncodingService.encode(device.getDeviceData()))); - } - return builder.build(); - } - - public DeviceCredentialsUpdateMsg constructDeviceCredentialsUpdatedMsg(DeviceCredentials deviceCredentials) { - DeviceCredentialsUpdateMsg.Builder builder = DeviceCredentialsUpdateMsg.newBuilder() - .setDeviceIdMSB(deviceCredentials.getDeviceId().getId().getMostSignificantBits()) - .setDeviceIdLSB(deviceCredentials.getDeviceId().getId().getLeastSignificantBits()); - if (deviceCredentials.getCredentialsType() != null) { - builder.setCredentialsType(deviceCredentials.getCredentialsType().name()) - .setCredentialsId(deviceCredentials.getCredentialsId()); - } - if (deviceCredentials.getCredentialsValue() != null) { - builder.setCredentialsValue(deviceCredentials.getCredentialsValue()); - } - return builder.build(); - } - - public DeviceUpdateMsg constructDeviceDeleteMsg(DeviceId deviceId) { - return DeviceUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(deviceId.getId().getMostSignificantBits()) - .setIdLSB(deviceId.getId().getLeastSignificantBits()).build(); - } - - public DeviceRpcCallMsg constructDeviceRpcCallMsg(UUID deviceId, JsonNode body) { - DeviceRpcCallMsg.Builder builder = constructDeviceRpcMsg(deviceId, body); - if (body.has("error") || body.has("response")) { - RpcResponseMsg.Builder responseBuilder = RpcResponseMsg.newBuilder(); - if (body.has("error")) { - responseBuilder.setError(body.get("error").asText()); - } else { - responseBuilder.setResponse(body.get("response").asText()); - } - builder.setResponseMsg(responseBuilder.build()); - } else { - RpcRequestMsg.Builder requestBuilder = RpcRequestMsg.newBuilder(); - requestBuilder.setMethod(body.get("method").asText()); - requestBuilder.setParams(body.get("params").asText()); - builder.setRequestMsg(requestBuilder.build()); - } - return builder.build(); - } - - private DeviceRpcCallMsg.Builder constructDeviceRpcMsg(UUID deviceId, JsonNode body) { - DeviceRpcCallMsg.Builder builder = DeviceRpcCallMsg.newBuilder() - .setDeviceIdMSB(deviceId.getMostSignificantBits()) - .setDeviceIdLSB(deviceId.getLeastSignificantBits()) - .setRequestId(body.get("requestId").asInt()); - if (body.get("oneway") != null) { - builder.setOneway(body.get("oneway").asBoolean()); - } - if (body.get("requestUUID") != null) { - UUID requestUUID = UUID.fromString(body.get("requestUUID").asText()); - builder.setRequestUuidMSB(requestUUID.getMostSignificantBits()) - .setRequestUuidLSB(requestUUID.getLeastSignificantBits()); - } - if (body.get("expirationTime") != null) { - builder.setExpirationTime(body.get("expirationTime").asLong()); - } - if (body.get("persisted") != null) { - builder.setPersisted(body.get("persisted").asBoolean()); - } - if (body.get("retries") != null) { - builder.setRetries(body.get("retries").asInt()); - } - if (body.get("additionalInfo") != null) { - builder.setAdditionalInfo(JacksonUtil.toString(body.get("additionalInfo"))); - } - if (body.get("serviceId") != null) { - builder.setServiceId(body.get("serviceId").asText()); - } - if (body.get("sessionId") != null) { - builder.setSessionId(body.get("sessionId").asText()); - } - return builder; - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java deleted file mode 100644 index d8e19ee854..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import com.google.protobuf.ByteString; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.queue.util.DataDecodingEncodingService; -import org.thingsboard.server.queue.util.TbCoreComponent; - -import java.nio.charset.StandardCharsets; - -@Component -@TbCoreComponent -public class DeviceProfileMsgConstructor { - - @Autowired - private DataDecodingEncodingService dataDecodingEncodingService; - - public DeviceProfileUpdateMsg constructDeviceProfileUpdatedMsg(UpdateMsgType msgType, DeviceProfile deviceProfile) { - DeviceProfileUpdateMsg.Builder builder = DeviceProfileUpdateMsg.newBuilder() - .setMsgType(msgType) - .setIdMSB(deviceProfile.getId().getId().getMostSignificantBits()) - .setIdLSB(deviceProfile.getId().getId().getLeastSignificantBits()) - .setName(deviceProfile.getName()) - .setDefault(deviceProfile.isDefault()) - .setType(deviceProfile.getType().name()) - .setProfileDataBytes(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfile.getProfileData()))); - if (deviceProfile.getDefaultQueueName() != null) { - builder.setDefaultQueueName(deviceProfile.getDefaultQueueName()); - } - if (deviceProfile.getDescription() != null) { - builder.setDescription(deviceProfile.getDescription()); - } - if (deviceProfile.getTransportType() != null) { - builder.setTransportType(deviceProfile.getTransportType().name()); - } - if (deviceProfile.getProvisionType() != null) { - builder.setProvisionType(deviceProfile.getProvisionType().name()); - } - if (deviceProfile.getProvisionDeviceKey() != null) { - builder.setProvisionDeviceKey(deviceProfile.getProvisionDeviceKey()); - } - if (deviceProfile.getImage() != null) { - builder.setImage(ByteString.copyFrom(deviceProfile.getImage().getBytes(StandardCharsets.UTF_8))); - } - if (deviceProfile.getFirmwareId() != null) { - builder.setFirmwareIdMSB(deviceProfile.getFirmwareId().getId().getMostSignificantBits()) - .setFirmwareIdLSB(deviceProfile.getFirmwareId().getId().getLeastSignificantBits()); - } - if (deviceProfile.getSoftwareId() != null) { - builder.setSoftwareIdMSB(deviceProfile.getSoftwareId().getId().getMostSignificantBits()) - .setSoftwareIdLSB(deviceProfile.getSoftwareId().getId().getLeastSignificantBits()); - } - if (deviceProfile.getDefaultEdgeRuleChainId() != null) { - builder.setDefaultRuleChainIdMSB(deviceProfile.getDefaultEdgeRuleChainId().getId().getMostSignificantBits()) - .setDefaultRuleChainIdLSB(deviceProfile.getDefaultEdgeRuleChainId().getId().getLeastSignificantBits()); - } - if (deviceProfile.getDefaultDashboardId() != null) { - builder.setDefaultDashboardIdMSB(deviceProfile.getDefaultDashboardId().getId().getMostSignificantBits()) - .setDefaultDashboardIdLSB(deviceProfile.getDefaultDashboardId().getId().getLeastSignificantBits()); - } - return builder.build(); - } - - public DeviceProfileUpdateMsg constructDeviceProfileDeleteMsg(DeviceProfileId deviceProfileId) { - return DeviceProfileUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(deviceProfileId.getId().getMostSignificantBits()) - .setIdLSB(deviceProfileId.getId().getLeastSignificantBits()).build(); - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EdgeMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EdgeMsgConstructor.java deleted file mode 100644 index 3416240b2c..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EdgeMsgConstructor.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import org.springframework.stereotype.Component; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.edge.Edge; -import org.thingsboard.server.gen.edge.v1.EdgeConfiguration; -import org.thingsboard.server.queue.util.TbCoreComponent; - -@Component -@TbCoreComponent -public class EdgeMsgConstructor { - - public EdgeConfiguration constructEdgeConfiguration(Edge edge) { - EdgeConfiguration.Builder builder = EdgeConfiguration.newBuilder() - .setEdgeIdMSB(edge.getId().getId().getMostSignificantBits()) - .setEdgeIdLSB(edge.getId().getId().getLeastSignificantBits()) - .setTenantIdMSB(edge.getTenantId().getId().getMostSignificantBits()) - .setTenantIdLSB(edge.getTenantId().getId().getLeastSignificantBits()) - .setName(edge.getName()) - .setType(edge.getType()) - .setRoutingKey(edge.getRoutingKey()) - .setSecret(edge.getSecret()) - .setAdditionalInfo(JacksonUtil.toString(edge.getAdditionalInfo())) - .setCloudType("CE"); - if (edge.getCustomerId() != null) { - builder.setCustomerIdMSB(edge.getCustomerId().getId().getMostSignificantBits()) - .setCustomerIdLSB(edge.getCustomerId().getId().getLeastSignificantBits()); - } - return builder.build(); - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityDataMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityDataMsgConstructor.java deleted file mode 100644 index 242a15ba56..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityDataMsgConstructor.java +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; -import com.google.gson.reflect.TypeToken; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.DataConstants; -import org.thingsboard.server.common.data.StringUtils; -import org.thingsboard.server.common.data.edge.EdgeEventActionType; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; -import org.thingsboard.server.gen.edge.v1.AttributeDeleteMsg; -import org.thingsboard.server.gen.edge.v1.EntityDataProto; -import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.queue.util.TbCoreComponent; - -import java.util.List; - -@Component -@Slf4j -@TbCoreComponent -public class EntityDataMsgConstructor { - - public EntityDataProto constructEntityDataMsg(EntityId entityId, EdgeEventActionType actionType, JsonElement entityData) { - EntityDataProto.Builder builder = EntityDataProto.newBuilder() - .setEntityIdMSB(entityId.getId().getMostSignificantBits()) - .setEntityIdLSB(entityId.getId().getLeastSignificantBits()) - .setEntityType(entityId.getEntityType().name()); - switch (actionType) { - case TIMESERIES_UPDATED: - try { - JsonObject data = entityData.getAsJsonObject(); - long ts; - if (data.get("ts") != null && !data.get("ts").isJsonNull()) { - ts = data.getAsJsonPrimitive("ts").getAsLong(); - } else { - ts = System.currentTimeMillis(); - } - builder.setPostTelemetryMsg(JsonConverter.convertToTelemetryProto(data.getAsJsonObject("data"), ts)); - } catch (Exception e) { - log.warn("[{}] Can't convert to telemetry proto, entityData [{}]", entityId, entityData, e); - } - break; - case ATTRIBUTES_UPDATED: - try { - JsonObject data = entityData.getAsJsonObject(); - TransportProtos.PostAttributeMsg attributesUpdatedMsg = JsonConverter.convertToAttributesProto(data.getAsJsonObject("kv")); - builder.setAttributesUpdatedMsg(attributesUpdatedMsg); - builder.setPostAttributeScope(getScopeOfDefault(data)); - } catch (Exception e) { - log.warn("[{}] Can't convert to AttributesUpdatedMsg proto, entityData [{}]", entityId, entityData, e); - } - break; - case POST_ATTRIBUTES: - try { - JsonObject data = entityData.getAsJsonObject(); - TransportProtos.PostAttributeMsg postAttributesMsg = JsonConverter.convertToAttributesProto(data.getAsJsonObject("kv")); - builder.setPostAttributesMsg(postAttributesMsg); - builder.setPostAttributeScope(getScopeOfDefault(data)); - } catch (Exception e) { - log.warn("[{}] Can't convert to PostAttributesMsg, entityData [{}]", entityId, entityData, e); - } - break; - case ATTRIBUTES_DELETED: - try { - AttributeDeleteMsg.Builder attributeDeleteMsg = AttributeDeleteMsg.newBuilder(); - attributeDeleteMsg.setScope(entityData.getAsJsonObject().getAsJsonPrimitive("scope").getAsString()); - JsonArray jsonArray = entityData.getAsJsonObject().getAsJsonArray("keys"); - List keys = new Gson().fromJson(jsonArray.toString(), new TypeToken<>(){}.getType()); - attributeDeleteMsg.addAllAttributeNames(keys); - attributeDeleteMsg.build(); - builder.setAttributeDeleteMsg(attributeDeleteMsg); - } catch (Exception e) { - log.warn("[{}] Can't convert to AttributeDeleteMsg proto, entityData [{}]", entityId, entityData, e); - } - break; - } - return builder.build(); - } - - private String getScopeOfDefault(JsonObject data) { - JsonPrimitive scope = data.getAsJsonPrimitive("scope"); - String result = DataConstants.SERVER_SCOPE; - if (scope != null && StringUtils.isNotBlank(scope.getAsString())) { - result = scope.getAsString(); - } - return result; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java deleted file mode 100644 index 235199d75e..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import org.springframework.stereotype.Component; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.EntityView; -import org.thingsboard.server.common.data.id.EntityViewId; -import org.thingsboard.server.gen.edge.v1.EdgeEntityType; -import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.queue.util.TbCoreComponent; - -@Component -@TbCoreComponent -public class EntityViewMsgConstructor { - - public EntityViewUpdateMsg constructEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView) { - EdgeEntityType entityType; - switch (entityView.getEntityId().getEntityType()) { - case DEVICE: - entityType = EdgeEntityType.DEVICE; - break; - case ASSET: - entityType = EdgeEntityType.ASSET; - break; - default: - throw new RuntimeException("Unsupported entity type [" + entityView.getEntityId().getEntityType() + "]"); - } - EntityViewUpdateMsg.Builder builder = EntityViewUpdateMsg.newBuilder() - .setMsgType(msgType) - .setIdMSB(entityView.getId().getId().getMostSignificantBits()) - .setIdLSB(entityView.getId().getId().getLeastSignificantBits()) - .setName(entityView.getName()) - .setType(entityView.getType()) - .setEntityIdMSB(entityView.getEntityId().getId().getMostSignificantBits()) - .setEntityIdLSB(entityView.getEntityId().getId().getLeastSignificantBits()) - .setEntityType(entityType); - if (entityView.getCustomerId() != null) { - builder.setCustomerIdMSB(entityView.getCustomerId().getId().getMostSignificantBits()); - builder.setCustomerIdLSB(entityView.getCustomerId().getId().getLeastSignificantBits()); - } - if (entityView.getAdditionalInfo() != null) { - builder.setAdditionalInfo(JacksonUtil.toString(entityView.getAdditionalInfo())); - } - return builder.build(); - } - - public EntityViewUpdateMsg constructEntityViewDeleteMsg(EntityViewId entityViewId) { - return EntityViewUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(entityViewId.getId().getMostSignificantBits()) - .setIdLSB(entityViewId.getId().getLeastSignificantBits()).build(); - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/MsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/MsgConstructor.java new file mode 100644 index 0000000000..071f2bc25d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/MsgConstructor.java @@ -0,0 +1,20 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor; + +public interface MsgConstructor { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/OtaPackageMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/OtaPackageMsgConstructor.java deleted file mode 100644 index 253305a5ab..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/OtaPackageMsgConstructor.java +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import com.google.protobuf.ByteString; -import org.springframework.stereotype.Component; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.OtaPackage; -import org.thingsboard.server.common.data.id.OtaPackageId; -import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.queue.util.TbCoreComponent; - -@Component -@TbCoreComponent -public class OtaPackageMsgConstructor { - - public OtaPackageUpdateMsg constructOtaPackageUpdatedMsg(UpdateMsgType msgType, OtaPackage otaPackage) { - OtaPackageUpdateMsg.Builder builder = OtaPackageUpdateMsg.newBuilder() - .setMsgType(msgType) - .setIdMSB(otaPackage.getId().getId().getMostSignificantBits()) - .setIdLSB(otaPackage.getId().getId().getLeastSignificantBits()) - .setType(otaPackage.getType().name()) - .setTitle(otaPackage.getTitle()) - .setVersion(otaPackage.getVersion()) - .setTag(otaPackage.getTag()); - - if (otaPackage.getDeviceProfileId() != null) { - builder.setDeviceProfileIdMSB(otaPackage.getDeviceProfileId().getId().getMostSignificantBits()) - .setDeviceProfileIdLSB(otaPackage.getDeviceProfileId().getId().getLeastSignificantBits()); - } - - if (otaPackage.getUrl() != null) { - builder.setUrl(otaPackage.getUrl()); - } - if (otaPackage.getAdditionalInfo() != null) { - builder.setAdditionalInfo(JacksonUtil.toString(otaPackage.getAdditionalInfo())); - } - if (otaPackage.getFileName() != null) { - builder.setFileName(otaPackage.getFileName()); - } - if (otaPackage.getContentType() != null) { - builder.setContentType(otaPackage.getContentType()); - } - if (otaPackage.getChecksumAlgorithm() != null) { - builder.setChecksumAlgorithm(otaPackage.getChecksumAlgorithm().name()); - } - if (otaPackage.getChecksum() != null) { - builder.setChecksum(otaPackage.getChecksum()); - } - if (otaPackage.getDataSize() != null) { - builder.setDataSize(otaPackage.getDataSize()); - } - if (otaPackage.getData() != null) { - builder.setData(ByteString.copyFrom(otaPackage.getData().array())); - } - return builder.build(); - } - - public OtaPackageUpdateMsg constructOtaPackageDeleteMsg(OtaPackageId otaPackageId) { - return OtaPackageUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(otaPackageId.getId().getMostSignificantBits()) - .setIdLSB(otaPackageId.getId().getLeastSignificantBits()).build(); - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/QueueMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/QueueMsgConstructor.java deleted file mode 100644 index 2644a5409b..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/QueueMsgConstructor.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.id.QueueId; -import org.thingsboard.server.common.data.queue.ProcessingStrategy; -import org.thingsboard.server.common.data.queue.Queue; -import org.thingsboard.server.common.data.queue.SubmitStrategy; -import org.thingsboard.server.gen.edge.v1.ProcessingStrategyProto; -import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; -import org.thingsboard.server.gen.edge.v1.SubmitStrategyProto; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.queue.util.TbCoreComponent; - -@Component -@TbCoreComponent -public class QueueMsgConstructor { - - public QueueUpdateMsg constructQueueUpdatedMsg(UpdateMsgType msgType, Queue queue) { - QueueUpdateMsg.Builder builder = QueueUpdateMsg.newBuilder() - .setMsgType(msgType) - .setIdMSB(queue.getId().getId().getMostSignificantBits()) - .setIdLSB(queue.getId().getId().getLeastSignificantBits()) - .setTenantIdMSB(queue.getTenantId().getId().getMostSignificantBits()) - .setTenantIdLSB(queue.getTenantId().getId().getLeastSignificantBits()) - .setName(queue.getName()) - .setTopic(queue.getTopic()) - .setPollInterval(queue.getPollInterval()) - .setPartitions(queue.getPartitions()) - .setConsumerPerPartition(queue.isConsumerPerPartition()) - .setPackProcessingTimeout(queue.getPackProcessingTimeout()) - .setSubmitStrategy(createSubmitStrategyProto(queue.getSubmitStrategy())) - .setProcessingStrategy(createProcessingStrategyProto(queue.getProcessingStrategy())); - return builder.build(); - } - - private ProcessingStrategyProto createProcessingStrategyProto(ProcessingStrategy processingStrategy) { - return ProcessingStrategyProto.newBuilder() - .setType(processingStrategy.getType().name()) - .setRetries(processingStrategy.getRetries()) - .setFailurePercentage(processingStrategy.getFailurePercentage()) - .setPauseBetweenRetries(processingStrategy.getPauseBetweenRetries()) - .setMaxPauseBetweenRetries(processingStrategy.getMaxPauseBetweenRetries()) - .build(); - } - - private SubmitStrategyProto createSubmitStrategyProto(SubmitStrategy submitStrategy) { - return SubmitStrategyProto.newBuilder() - .setType(submitStrategy.getType().name()) - .setBatchSize(submitStrategy.getBatchSize()) - .build(); - } - - public QueueUpdateMsg constructQueueDeleteMsg(QueueId queueId) { - return QueueUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(queueId.getId().getMostSignificantBits()) - .setIdLSB(queueId.getId().getLeastSignificantBits()).build(); - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationMsgConstructor.java deleted file mode 100644 index 24786a4f7c..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationMsgConstructor.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import org.springframework.stereotype.Component; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.relation.EntityRelation; -import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.queue.util.TbCoreComponent; - -@Component -@TbCoreComponent -public class RelationMsgConstructor { - - public RelationUpdateMsg constructRelationUpdatedMsg(UpdateMsgType msgType, EntityRelation entityRelation) { - RelationUpdateMsg.Builder builder = RelationUpdateMsg.newBuilder() - .setMsgType(msgType) - .setFromIdMSB(entityRelation.getFrom().getId().getMostSignificantBits()) - .setFromIdLSB(entityRelation.getFrom().getId().getLeastSignificantBits()) - .setFromEntityType(entityRelation.getFrom().getEntityType().name()) - .setToIdMSB(entityRelation.getTo().getId().getMostSignificantBits()) - .setToIdLSB(entityRelation.getTo().getId().getLeastSignificantBits()) - .setToEntityType(entityRelation.getTo().getEntityType().name()) - .setType(entityRelation.getType()); - if (entityRelation.getAdditionalInfo() != null) { - builder.setAdditionalInfo(JacksonUtil.toString(entityRelation.getAdditionalInfo())); - } - if (entityRelation.getTypeGroup() != null) { - builder.setTypeGroup(entityRelation.getTypeGroup().name()); - } - return builder.build(); - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java deleted file mode 100644 index 8f91f5d19e..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.id.RuleChainId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.rule.RuleChain; -import org.thingsboard.server.common.data.rule.RuleChainMetaData; -import org.thingsboard.server.gen.edge.v1.EdgeVersion; -import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; -import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMetadataConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMetadataConstructorFactory; - -@Component -@Slf4j -@TbCoreComponent -public class RuleChainMsgConstructor { - - public RuleChainUpdateMsg constructRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain, boolean isRoot) { - RuleChainUpdateMsg.Builder builder = RuleChainUpdateMsg.newBuilder() - .setMsgType(msgType) - .setIdMSB(ruleChain.getId().getId().getMostSignificantBits()) - .setIdLSB(ruleChain.getId().getId().getLeastSignificantBits()) - .setName(ruleChain.getName()) - .setRoot(isRoot) - .setDebugMode(ruleChain.isDebugMode()) - .setConfiguration(JacksonUtil.toString(ruleChain.getConfiguration())); - if (ruleChain.getFirstRuleNodeId() != null) { - builder.setFirstRuleNodeIdMSB(ruleChain.getFirstRuleNodeId().getId().getMostSignificantBits()) - .setFirstRuleNodeIdLSB(ruleChain.getFirstRuleNodeId().getId().getLeastSignificantBits()); - } - return builder.build(); - } - - public RuleChainMetadataUpdateMsg constructRuleChainMetadataUpdatedMsg(TenantId tenantId, - UpdateMsgType msgType, - RuleChainMetaData ruleChainMetaData, - EdgeVersion edgeVersion) { - RuleChainMetadataConstructor ruleChainMetadataConstructor - = RuleChainMetadataConstructorFactory.getByEdgeVersion(edgeVersion); - return ruleChainMetadataConstructor.constructRuleChainMetadataUpdatedMsg(tenantId, msgType, ruleChainMetaData); - } - - public RuleChainUpdateMsg constructRuleChainDeleteMsg(RuleChainId ruleChainId) { - return RuleChainUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(ruleChainId.getId().getMostSignificantBits()) - .setIdLSB(ruleChainId.getId().getLeastSignificantBits()).build(); - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java deleted file mode 100644 index fbd3b31688..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import org.springframework.stereotype.Component; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.id.UserId; -import org.thingsboard.server.common.data.security.UserCredentials; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg; -import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; -import org.thingsboard.server.queue.util.TbCoreComponent; - -@Component -@TbCoreComponent -public class UserMsgConstructor { - - public UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user) { - UserUpdateMsg.Builder builder = UserUpdateMsg.newBuilder() - .setMsgType(msgType) - .setIdMSB(user.getId().getId().getMostSignificantBits()) - .setIdLSB(user.getId().getId().getLeastSignificantBits()) - .setEmail(user.getEmail()) - .setAuthority(user.getAuthority().name()); - if (user.getCustomerId() != null) { - builder.setCustomerIdMSB(user.getCustomerId().getId().getMostSignificantBits()); - builder.setCustomerIdLSB(user.getCustomerId().getId().getLeastSignificantBits()); - } - if (user.getFirstName() != null) { - builder.setFirstName(user.getFirstName()); - } - if (user.getLastName() != null) { - builder.setLastName(user.getLastName()); - } - if (user.getAdditionalInfo() != null) { - builder.setAdditionalInfo(JacksonUtil.toString(user.getAdditionalInfo())); - } - return builder.build(); - } - - public UserUpdateMsg constructUserDeleteMsg(UserId userId) { - return UserUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(userId.getId().getMostSignificantBits()) - .setIdLSB(userId.getId().getLeastSignificantBits()).build(); - } - - public UserCredentialsUpdateMsg constructUserCredentialsUpdatedMsg(UserCredentials userCredentials) { - UserCredentialsUpdateMsg.Builder builder = UserCredentialsUpdateMsg.newBuilder() - .setUserIdMSB(userCredentials.getUserId().getId().getMostSignificantBits()) - .setUserIdLSB(userCredentials.getUserId().getId().getLeastSignificantBits()) - .setEnabled(userCredentials.isEnabled()) - .setPassword(userCredentials.getPassword()); - return builder.build(); - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java deleted file mode 100644 index a574b8bb4b..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import org.springframework.stereotype.Component; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.id.WidgetTypeId; -import org.thingsboard.server.common.data.widget.WidgetTypeDetails; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg; -import org.thingsboard.server.queue.util.TbCoreComponent; - -@Component -@TbCoreComponent -public class WidgetTypeMsgConstructor { - - public WidgetTypeUpdateMsg constructWidgetTypeUpdateMsg(UpdateMsgType msgType, WidgetTypeDetails widgetTypeDetails) { - WidgetTypeUpdateMsg.Builder builder = WidgetTypeUpdateMsg.newBuilder() - .setMsgType(msgType) - .setIdMSB(widgetTypeDetails.getId().getId().getMostSignificantBits()) - .setIdLSB(widgetTypeDetails.getId().getId().getLeastSignificantBits()); - if (widgetTypeDetails.getBundleAlias() != null) { - builder.setBundleAlias(widgetTypeDetails.getBundleAlias()); - } - if (widgetTypeDetails.getAlias() != null) { - builder.setAlias(widgetTypeDetails.getAlias()); - } - if (widgetTypeDetails.getName() != null) { - builder.setName(widgetTypeDetails.getName()); - } - if (widgetTypeDetails.getDescriptor() != null) { - builder.setDescriptorJson(JacksonUtil.toString(widgetTypeDetails.getDescriptor())); - } - if (widgetTypeDetails.getTenantId().equals(TenantId.SYS_TENANT_ID)) { - builder.setIsSystem(true); - } - if (widgetTypeDetails.getImage() != null) { - builder.setImage(widgetTypeDetails.getImage()); - } - if (widgetTypeDetails.getDescription() != null) { - builder.setDescription(widgetTypeDetails.getDescription()); - } - return builder.build(); - } - - public WidgetTypeUpdateMsg constructWidgetTypeDeleteMsg(WidgetTypeId widgetTypeId) { - return WidgetTypeUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(widgetTypeId.getId().getMostSignificantBits()) - .setIdLSB(widgetTypeId.getId().getLeastSignificantBits()) - .build(); - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java deleted file mode 100644 index 36e43b9579..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.constructor; - -import com.google.protobuf.ByteString; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.id.WidgetsBundleId; -import org.thingsboard.server.common.data.widget.WidgetsBundle; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg; -import org.thingsboard.server.queue.util.TbCoreComponent; - -import java.nio.charset.StandardCharsets; - -@Component -@TbCoreComponent -public class WidgetsBundleMsgConstructor { - - public WidgetsBundleUpdateMsg constructWidgetsBundleUpdateMsg(UpdateMsgType msgType, WidgetsBundle widgetsBundle) { - WidgetsBundleUpdateMsg.Builder builder = WidgetsBundleUpdateMsg.newBuilder() - .setMsgType(msgType) - .setIdMSB(widgetsBundle.getId().getId().getMostSignificantBits()) - .setIdLSB(widgetsBundle.getId().getId().getLeastSignificantBits()) - .setTitle(widgetsBundle.getTitle()) - .setAlias(widgetsBundle.getAlias()); - if (widgetsBundle.getImage() != null) { - builder.setImage(ByteString.copyFrom(widgetsBundle.getImage().getBytes(StandardCharsets.UTF_8))); - } - if (widgetsBundle.getDescription() != null) { - builder.setDescription(widgetsBundle.getDescription()); - } - if (widgetsBundle.getTenantId().equals(TenantId.SYS_TENANT_ID)) { - builder.setIsSystem(true); - } - return builder.build(); - } - - public WidgetsBundleUpdateMsg constructWidgetsBundleDeleteMsg(WidgetsBundleId widgetsBundleId) { - return WidgetsBundleUpdateMsg.newBuilder() - .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) - .setIdMSB(widgetsBundleId.getId().getMostSignificantBits()) - .setIdLSB(widgetsBundleId.getId().getLeastSignificantBits()) - .build(); - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructor.java new file mode 100644 index 0000000000..fd3b4ef95d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructor.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.alarm; + +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; + +public interface AlarmMsgConstructor extends MsgConstructor { + + AlarmUpdateMsg constructAlarmUpdatedMsg(UpdateMsgType msgType, Alarm alarm, String entityName); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorFactory.java new file mode 100644 index 0000000000..63c9ad8bf5 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.alarm; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.BaseMsgConstructorFactory; + +@Component +@TbCoreComponent +public class AlarmMsgConstructorFactory extends BaseMsgConstructorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorV1.java new file mode 100644 index 0000000000..b1294f5904 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorV1.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.alarm; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class AlarmMsgConstructorV1 implements AlarmMsgConstructor { + + @Override + public AlarmUpdateMsg constructAlarmUpdatedMsg(UpdateMsgType msgType, Alarm alarm, String entityName) { + return AlarmUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(alarm.getId().getId().getMostSignificantBits()) + .setIdLSB(alarm.getId().getId().getLeastSignificantBits()) + .setName(alarm.getName()) + .setType(alarm.getType()) + .setOriginatorName(entityName) + .setOriginatorType(alarm.getOriginator().getEntityType().name()) + .setSeverity(alarm.getSeverity().name()) + .setStatus(alarm.getStatus().name()) + .setStartTs(alarm.getStartTs()) + .setEndTs(alarm.getEndTs()) + .setAckTs(alarm.getAckTs()) + .setClearTs(alarm.getClearTs()) + .setDetails(JacksonUtil.toString(alarm.getDetails())) + .setPropagate(alarm.isPropagate()) + .setPropagateToOwner(alarm.isPropagateToOwner()) + .setPropagateToTenant(alarm.isPropagateToTenant()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorV2.java new file mode 100644 index 0000000000..9034755db6 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/alarm/AlarmMsgConstructorV2.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.alarm; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class AlarmMsgConstructorV2 implements AlarmMsgConstructor { + + @Override + public AlarmUpdateMsg constructAlarmUpdatedMsg(UpdateMsgType msgType, Alarm alarm, String entityName) { + return AlarmUpdateMsg.newBuilder().setMsgType(msgType) + .setEntity(JacksonUtil.toString(alarm)) + .setIdMSB(alarm.getId().getId().getMostSignificantBits()) + .setIdLSB(alarm.getId().getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructor.java new file mode 100644 index 0000000000..338375ac37 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructor.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.asset; + +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.AssetProfileId; +import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; + +public interface AssetMsgConstructor extends MsgConstructor { + + AssetUpdateMsg constructAssetUpdatedMsg(UpdateMsgType msgType, Asset asset); + + AssetUpdateMsg constructAssetDeleteMsg(AssetId assetId); + + AssetProfileUpdateMsg constructAssetProfileUpdatedMsg(UpdateMsgType msgType, AssetProfile assetProfile); + + AssetProfileUpdateMsg constructAssetProfileDeleteMsg(AssetProfileId assetProfileId); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorFactory.java new file mode 100644 index 0000000000..bd756cb139 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.asset; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.BaseMsgConstructorFactory; + +@Component +@TbCoreComponent +public class AssetMsgConstructorFactory extends BaseMsgConstructorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorV1.java new file mode 100644 index 0000000000..ddf975d4ab --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorV1.java @@ -0,0 +1,92 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.asset; + +import com.google.protobuf.ByteString; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.dao.resource.ImageService; +import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import java.nio.charset.StandardCharsets; + +@Component +@TbCoreComponent +public class AssetMsgConstructorV1 extends BaseAssetMsgConstructor { + + @Autowired + private ImageService imageService; + + @Override + public AssetUpdateMsg constructAssetUpdatedMsg(UpdateMsgType msgType, Asset asset) { + AssetUpdateMsg.Builder builder = AssetUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(asset.getUuidId().getMostSignificantBits()) + .setIdLSB(asset.getUuidId().getLeastSignificantBits()) + .setName(asset.getName()) + .setType(asset.getType()); + if (asset.getLabel() != null) { + builder.setLabel(asset.getLabel()); + } + if (asset.getCustomerId() != null) { + builder.setCustomerIdMSB(asset.getCustomerId().getId().getMostSignificantBits()); + builder.setCustomerIdLSB(asset.getCustomerId().getId().getLeastSignificantBits()); + } + if (asset.getAssetProfileId() != null) { + builder.setAssetProfileIdMSB(asset.getAssetProfileId().getId().getMostSignificantBits()); + builder.setAssetProfileIdLSB(asset.getAssetProfileId().getId().getLeastSignificantBits()); + } + if (asset.getAdditionalInfo() != null) { + builder.setAdditionalInfo(JacksonUtil.toString(asset.getAdditionalInfo())); + } + return builder.build(); + } + + @Override + public AssetProfileUpdateMsg constructAssetProfileUpdatedMsg(UpdateMsgType msgType, AssetProfile assetProfile) { + imageService.inlineImageForEdge(assetProfile); + AssetProfileUpdateMsg.Builder builder = AssetProfileUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(assetProfile.getId().getId().getMostSignificantBits()) + .setIdLSB(assetProfile.getId().getId().getLeastSignificantBits()) + .setName(assetProfile.getName()) + .setDefault(assetProfile.isDefault()); + if (assetProfile.getDefaultDashboardId() != null) { + builder.setDefaultDashboardIdMSB(assetProfile.getDefaultDashboardId().getId().getMostSignificantBits()) + .setDefaultDashboardIdLSB(assetProfile.getDefaultDashboardId().getId().getLeastSignificantBits()); + } + if (assetProfile.getDefaultQueueName() != null) { + builder.setDefaultQueueName(assetProfile.getDefaultQueueName()); + } + if (assetProfile.getDescription() != null) { + builder.setDescription(assetProfile.getDescription()); + } + if (assetProfile.getImage() != null) { + builder.setImage(ByteString.copyFrom(assetProfile.getImage().getBytes(StandardCharsets.UTF_8))); + } + if (assetProfile.getDefaultEdgeRuleChainId() != null) { + builder.setDefaultRuleChainIdMSB(assetProfile.getDefaultEdgeRuleChainId().getId().getMostSignificantBits()) + .setDefaultRuleChainIdLSB(assetProfile.getDefaultEdgeRuleChainId().getId().getLeastSignificantBits()); + } + return builder.build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorV2.java new file mode 100644 index 0000000000..b7e00473b5 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorV2.java @@ -0,0 +1,44 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.asset; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class AssetMsgConstructorV2 extends BaseAssetMsgConstructor { + + @Override + public AssetUpdateMsg constructAssetUpdatedMsg(UpdateMsgType msgType, Asset asset) { + return AssetUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(asset)) + .setIdMSB(asset.getUuidId().getMostSignificantBits()) + .setIdLSB(asset.getUuidId().getLeastSignificantBits()).build(); + } + + @Override + public AssetProfileUpdateMsg constructAssetProfileUpdatedMsg(UpdateMsgType msgType, AssetProfile assetProfile) { + return AssetProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(assetProfile)) + .setIdMSB(assetProfile.getId().getId().getMostSignificantBits()) + .setIdLSB(assetProfile.getId().getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/BaseAssetMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/BaseAssetMsgConstructor.java new file mode 100644 index 0000000000..b8fe8ad870 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/BaseAssetMsgConstructor.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.asset; + +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.AssetProfileId; +import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public abstract class BaseAssetMsgConstructor implements AssetMsgConstructor { + + @Override + public AssetUpdateMsg constructAssetDeleteMsg(AssetId assetId) { + return AssetUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(assetId.getId().getMostSignificantBits()) + .setIdLSB(assetId.getId().getLeastSignificantBits()).build(); + } + + @Override + public AssetProfileUpdateMsg constructAssetProfileDeleteMsg(AssetProfileId assetProfileId) { + return AssetProfileUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(assetProfileId.getId().getMostSignificantBits()) + .setIdLSB(assetProfileId.getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/BaseCustomerMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/BaseCustomerMsgConstructor.java new file mode 100644 index 0000000000..6d532a5e1c --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/BaseCustomerMsgConstructor.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.customer; + +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public abstract class BaseCustomerMsgConstructor implements CustomerMsgConstructor { + + @Override + public CustomerUpdateMsg constructCustomerDeleteMsg(CustomerId customerId) { + return CustomerUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(customerId.getId().getMostSignificantBits()) + .setIdLSB(customerId.getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructor.java new file mode 100644 index 0000000000..7351822a3f --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructor.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.customer; + +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; + +public interface CustomerMsgConstructor extends MsgConstructor { + + CustomerUpdateMsg constructCustomerUpdatedMsg(UpdateMsgType msgType, Customer customer); + + CustomerUpdateMsg constructCustomerDeleteMsg(CustomerId customerId); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorFactory.java new file mode 100644 index 0000000000..ba66a69cab --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.customer; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.BaseMsgConstructorFactory; + +@Component +@TbCoreComponent +public class CustomerMsgConstructorFactory extends BaseMsgConstructorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorV1.java new file mode 100644 index 0000000000..e6bb95defa --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorV1.java @@ -0,0 +1,65 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.customer; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class CustomerMsgConstructorV1 extends BaseCustomerMsgConstructor { + + @Override + public CustomerUpdateMsg constructCustomerUpdatedMsg(UpdateMsgType msgType, Customer customer) { + CustomerUpdateMsg.Builder builder = CustomerUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(customer.getId().getId().getMostSignificantBits()) + .setIdLSB(customer.getId().getId().getLeastSignificantBits()) + .setTitle(customer.getTitle()); + if (customer.getCountry() != null) { + builder.setCountry(customer.getCountry()); + } + if (customer.getState() != null) { + builder.setState(customer.getState()); + } + if (customer.getCity() != null) { + builder.setCity(customer.getCity()); + } + if (customer.getAddress() != null) { + builder.setAddress(customer.getAddress()); + } + if (customer.getAddress2() != null) { + builder.setAddress2(customer.getAddress2()); + } + if (customer.getZip() != null) { + builder.setZip(customer.getZip()); + } + if (customer.getPhone() != null) { + builder.setPhone(customer.getPhone()); + } + if (customer.getEmail() != null) { + builder.setEmail(customer.getEmail()); + } + if (customer.getAdditionalInfo() != null) { + builder.setAdditionalInfo(JacksonUtil.toString(customer.getAdditionalInfo())); + } + return builder.build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorV2.java new file mode 100644 index 0000000000..94764e1178 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/customer/CustomerMsgConstructorV2.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.customer; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class CustomerMsgConstructorV2 extends BaseCustomerMsgConstructor { + + @Override + public CustomerUpdateMsg constructCustomerUpdatedMsg(UpdateMsgType msgType, Customer customer) { + return CustomerUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(customer)) + .setIdMSB(customer.getId().getId().getMostSignificantBits()) + .setIdLSB(customer.getId().getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/BaseDashboardMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/BaseDashboardMsgConstructor.java new file mode 100644 index 0000000000..f8e42c1461 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/BaseDashboardMsgConstructor.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.dashboard; + +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public abstract class BaseDashboardMsgConstructor implements DashboardMsgConstructor { + + @Override + public DashboardUpdateMsg constructDashboardDeleteMsg(DashboardId dashboardId) { + return DashboardUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(dashboardId.getId().getMostSignificantBits()) + .setIdLSB(dashboardId.getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructor.java new file mode 100644 index 0000000000..96504e5036 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructor.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.dashboard; + +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; + +public interface DashboardMsgConstructor extends MsgConstructor { + + DashboardUpdateMsg constructDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard); + + DashboardUpdateMsg constructDashboardDeleteMsg(DashboardId dashboardId); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorFactory.java new file mode 100644 index 0000000000..b559e0f454 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.dashboard; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.BaseMsgConstructorFactory; + +@Component +@TbCoreComponent +public class DashboardMsgConstructorFactory extends BaseMsgConstructorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorV1.java new file mode 100644 index 0000000000..548b1e1fd0 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorV1.java @@ -0,0 +1,55 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.dashboard; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.dao.resource.ImageService; +import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class DashboardMsgConstructorV1 extends BaseDashboardMsgConstructor { + + @Autowired + private ImageService imageService; + + @Override + public DashboardUpdateMsg constructDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard) { + imageService.inlineImagesForEdge(dashboard); + DashboardUpdateMsg.Builder builder = DashboardUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(dashboard.getId().getId().getMostSignificantBits()) + .setIdLSB(dashboard.getId().getId().getLeastSignificantBits()) + .setTitle(dashboard.getTitle()) + .setConfiguration(JacksonUtil.toString(dashboard.getConfiguration())) + .setMobileHide(dashboard.isMobileHide()); + if (dashboard.getAssignedCustomers() != null) { + builder.setAssignedCustomers(JacksonUtil.toString(dashboard.getAssignedCustomers())); + } + if (dashboard.getImage() != null) { + builder.setImage(dashboard.getImage()); + } + if (dashboard.getMobileOrder() != null) { + builder.setMobileOrder(dashboard.getMobileOrder()); + } + return builder.build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorV2.java new file mode 100644 index 0000000000..0940423119 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorV2.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.dashboard; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class DashboardMsgConstructorV2 extends BaseDashboardMsgConstructor { + + @Override + public DashboardUpdateMsg constructDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard) { + return DashboardUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(dashboard)) + .setIdMSB(dashboard.getId().getId().getMostSignificantBits()) + .setIdLSB(dashboard.getId().getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/BaseDeviceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/BaseDeviceMsgConstructor.java new file mode 100644 index 0000000000..21d761dc26 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/BaseDeviceMsgConstructor.java @@ -0,0 +1,102 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.device; + +import com.fasterxml.jackson.databind.JsonNode; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg; +import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; +import org.thingsboard.server.gen.edge.v1.RpcRequestMsg; +import org.thingsboard.server.gen.edge.v1.RpcResponseMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +import java.util.UUID; + +public abstract class BaseDeviceMsgConstructor implements DeviceMsgConstructor { + + @Override + public DeviceUpdateMsg constructDeviceDeleteMsg(DeviceId deviceId) { + return DeviceUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(deviceId.getId().getMostSignificantBits()) + .setIdLSB(deviceId.getId().getLeastSignificantBits()).build(); + } + + @Override + public DeviceProfileUpdateMsg constructDeviceProfileDeleteMsg(DeviceProfileId deviceProfileId) { + return DeviceProfileUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(deviceProfileId.getId().getMostSignificantBits()) + .setIdLSB(deviceProfileId.getId().getLeastSignificantBits()).build(); + } + + @Override + public DeviceRpcCallMsg constructDeviceRpcCallMsg(UUID deviceId, JsonNode body) { + DeviceRpcCallMsg.Builder builder = constructDeviceRpcMsg(deviceId, body); + if (body.has("error") || body.has("response")) { + RpcResponseMsg.Builder responseBuilder = RpcResponseMsg.newBuilder(); + if (body.has("error")) { + responseBuilder.setError(body.get("error").asText()); + } else { + responseBuilder.setResponse(body.get("response").asText()); + } + builder.setResponseMsg(responseBuilder.build()); + } else { + RpcRequestMsg.Builder requestBuilder = RpcRequestMsg.newBuilder(); + requestBuilder.setMethod(body.get("method").asText()); + requestBuilder.setParams(body.get("params").asText()); + builder.setRequestMsg(requestBuilder.build()); + } + return builder.build(); + } + + private DeviceRpcCallMsg.Builder constructDeviceRpcMsg(UUID deviceId, JsonNode body) { + DeviceRpcCallMsg.Builder builder = DeviceRpcCallMsg.newBuilder() + .setDeviceIdMSB(deviceId.getMostSignificantBits()) + .setDeviceIdLSB(deviceId.getLeastSignificantBits()) + .setRequestId(body.get("requestId").asInt()); + if (body.get("oneway") != null) { + builder.setOneway(body.get("oneway").asBoolean()); + } + if (body.get("requestUUID") != null) { + UUID requestUUID = UUID.fromString(body.get("requestUUID").asText()); + builder.setRequestUuidMSB(requestUUID.getMostSignificantBits()) + .setRequestUuidLSB(requestUUID.getLeastSignificantBits()); + } + if (body.get("expirationTime") != null) { + builder.setExpirationTime(body.get("expirationTime").asLong()); + } + if (body.get("persisted") != null) { + builder.setPersisted(body.get("persisted").asBoolean()); + } + if (body.get("retries") != null) { + builder.setRetries(body.get("retries").asInt()); + } + if (body.get("additionalInfo") != null) { + builder.setAdditionalInfo(JacksonUtil.toString(body.get("additionalInfo"))); + } + if (body.get("serviceId") != null) { + builder.setServiceId(body.get("serviceId").asText()); + } + if (body.get("sessionId") != null) { + builder.setSessionId(body.get("sessionId").asText()); + } + return builder; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructor.java new file mode 100644 index 0000000000..4636dfd5ae --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructor.java @@ -0,0 +1,46 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.device; + +import com.fasterxml.jackson.databind.JsonNode; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg; +import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; + +import java.util.UUID; + +public interface DeviceMsgConstructor extends MsgConstructor { + + DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device); + + DeviceUpdateMsg constructDeviceDeleteMsg(DeviceId deviceId); + + DeviceCredentialsUpdateMsg constructDeviceCredentialsUpdatedMsg(DeviceCredentials deviceCredentials); + + DeviceProfileUpdateMsg constructDeviceProfileUpdatedMsg(UpdateMsgType msgType, DeviceProfile deviceProfile); + + DeviceProfileUpdateMsg constructDeviceProfileDeleteMsg(DeviceProfileId deviceProfileId); + + DeviceRpcCallMsg constructDeviceRpcCallMsg(UUID deviceId, JsonNode body); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorFactory.java new file mode 100644 index 0000000000..98add6af2c --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.device; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.BaseMsgConstructorFactory; + +@Component +@TbCoreComponent +public class DeviceMsgConstructorFactory extends BaseMsgConstructorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorV1.java new file mode 100644 index 0000000000..04f23f25b4 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorV1.java @@ -0,0 +1,143 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.device; + +import com.google.protobuf.ByteString; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.dao.resource.ImageService; +import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.DataDecodingEncodingService; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import java.nio.charset.StandardCharsets; + +@Component +@TbCoreComponent +public class DeviceMsgConstructorV1 extends BaseDeviceMsgConstructor { + + @Autowired + private DataDecodingEncodingService dataDecodingEncodingService; + + @Autowired + private ImageService imageService; + + @Override + public DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device) { + DeviceUpdateMsg.Builder builder = DeviceUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(device.getId().getId().getMostSignificantBits()) + .setIdLSB(device.getId().getId().getLeastSignificantBits()) + .setName(device.getName()) + .setType(device.getType()); + if (device.getLabel() != null) { + builder.setLabel(device.getLabel()); + } + if (device.getCustomerId() != null) { + builder.setCustomerIdMSB(device.getCustomerId().getId().getMostSignificantBits()); + builder.setCustomerIdLSB(device.getCustomerId().getId().getLeastSignificantBits()); + } + if (device.getDeviceProfileId() != null) { + builder.setDeviceProfileIdMSB(device.getDeviceProfileId().getId().getMostSignificantBits()); + builder.setDeviceProfileIdLSB(device.getDeviceProfileId().getId().getLeastSignificantBits()); + } + if (device.getAdditionalInfo() != null) { + builder.setAdditionalInfo(JacksonUtil.toString(device.getAdditionalInfo())); + } + if (device.getFirmwareId() != null) { + builder.setFirmwareIdMSB(device.getFirmwareId().getId().getMostSignificantBits()) + .setFirmwareIdLSB(device.getFirmwareId().getId().getLeastSignificantBits()); + } + if (device.getSoftwareId() != null) { + builder.setSoftwareIdMSB(device.getSoftwareId().getId().getMostSignificantBits()) + .setSoftwareIdLSB(device.getSoftwareId().getId().getLeastSignificantBits()); + } + if (device.getDeviceData() != null) { + builder.setDeviceDataBytes(ByteString.copyFrom(dataDecodingEncodingService.encode(device.getDeviceData()))); + } + return builder.build(); + } + + @Override + public DeviceCredentialsUpdateMsg constructDeviceCredentialsUpdatedMsg(DeviceCredentials deviceCredentials) { + DeviceCredentialsUpdateMsg.Builder builder = DeviceCredentialsUpdateMsg.newBuilder() + .setDeviceIdMSB(deviceCredentials.getDeviceId().getId().getMostSignificantBits()) + .setDeviceIdLSB(deviceCredentials.getDeviceId().getId().getLeastSignificantBits()); + if (deviceCredentials.getCredentialsType() != null) { + builder.setCredentialsType(deviceCredentials.getCredentialsType().name()) + .setCredentialsId(deviceCredentials.getCredentialsId()); + } + if (deviceCredentials.getCredentialsValue() != null) { + builder.setCredentialsValue(deviceCredentials.getCredentialsValue()); + } + return builder.build(); + } + + @Override + public DeviceProfileUpdateMsg constructDeviceProfileUpdatedMsg(UpdateMsgType msgType, DeviceProfile deviceProfile) { + imageService.inlineImageForEdge(deviceProfile); + DeviceProfileUpdateMsg.Builder builder = DeviceProfileUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(deviceProfile.getId().getId().getMostSignificantBits()) + .setIdLSB(deviceProfile.getId().getId().getLeastSignificantBits()) + .setName(deviceProfile.getName()) + .setDefault(deviceProfile.isDefault()) + .setType(deviceProfile.getType().name()) + .setProfileDataBytes(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfile.getProfileData()))); + if (deviceProfile.getDefaultQueueName() != null) { + builder.setDefaultQueueName(deviceProfile.getDefaultQueueName()); + } + if (deviceProfile.getDescription() != null) { + builder.setDescription(deviceProfile.getDescription()); + } + if (deviceProfile.getTransportType() != null) { + builder.setTransportType(deviceProfile.getTransportType().name()); + } + if (deviceProfile.getProvisionType() != null) { + builder.setProvisionType(deviceProfile.getProvisionType().name()); + } + if (deviceProfile.getProvisionDeviceKey() != null) { + builder.setProvisionDeviceKey(deviceProfile.getProvisionDeviceKey()); + } + if (deviceProfile.getImage() != null) { + builder.setImage(ByteString.copyFrom(deviceProfile.getImage().getBytes(StandardCharsets.UTF_8))); + } + if (deviceProfile.getFirmwareId() != null) { + builder.setFirmwareIdMSB(deviceProfile.getFirmwareId().getId().getMostSignificantBits()) + .setFirmwareIdLSB(deviceProfile.getFirmwareId().getId().getLeastSignificantBits()); + } + if (deviceProfile.getSoftwareId() != null) { + builder.setSoftwareIdMSB(deviceProfile.getSoftwareId().getId().getMostSignificantBits()) + .setSoftwareIdLSB(deviceProfile.getSoftwareId().getId().getLeastSignificantBits()); + } + if (deviceProfile.getDefaultEdgeRuleChainId() != null) { + builder.setDefaultRuleChainIdMSB(deviceProfile.getDefaultEdgeRuleChainId().getId().getMostSignificantBits()) + .setDefaultRuleChainIdLSB(deviceProfile.getDefaultEdgeRuleChainId().getId().getLeastSignificantBits()); + } + if (deviceProfile.getDefaultDashboardId() != null) { + builder.setDefaultDashboardIdMSB(deviceProfile.getDefaultDashboardId().getId().getMostSignificantBits()) + .setDefaultDashboardIdLSB(deviceProfile.getDefaultDashboardId().getId().getLeastSignificantBits()); + } + return builder.build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorV2.java new file mode 100644 index 0000000000..189251f8c4 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorV2.java @@ -0,0 +1,51 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.device; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class DeviceMsgConstructorV2 extends BaseDeviceMsgConstructor { + + @Override + public DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device) { + return DeviceUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(device)) + .setIdMSB(device.getId().getId().getMostSignificantBits()) + .setIdLSB(device.getId().getId().getLeastSignificantBits()).build(); + } + + @Override + public DeviceCredentialsUpdateMsg constructDeviceCredentialsUpdatedMsg(DeviceCredentials deviceCredentials) { + return DeviceCredentialsUpdateMsg.newBuilder().setEntity(JacksonUtil.toString(deviceCredentials)).build(); + } + + @Override + public DeviceProfileUpdateMsg constructDeviceProfileUpdatedMsg(UpdateMsgType msgType, DeviceProfile deviceProfile) { + return DeviceProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(deviceProfile)) + .setIdMSB(deviceProfile.getId().getId().getMostSignificantBits()) + .setIdLSB(deviceProfile.getId().getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/edge/EdgeMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/edge/EdgeMsgConstructor.java new file mode 100644 index 0000000000..c3741ee8bf --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/edge/EdgeMsgConstructor.java @@ -0,0 +1,46 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.edge; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.gen.edge.v1.EdgeConfiguration; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class EdgeMsgConstructor { + + public EdgeConfiguration constructEdgeConfiguration(Edge edge) { + EdgeConfiguration.Builder builder = EdgeConfiguration.newBuilder() + .setEdgeIdMSB(edge.getId().getId().getMostSignificantBits()) + .setEdgeIdLSB(edge.getId().getId().getLeastSignificantBits()) + .setTenantIdMSB(edge.getTenantId().getId().getMostSignificantBits()) + .setTenantIdLSB(edge.getTenantId().getId().getLeastSignificantBits()) + .setName(edge.getName()) + .setType(edge.getType()) + .setRoutingKey(edge.getRoutingKey()) + .setSecret(edge.getSecret()) + .setAdditionalInfo(JacksonUtil.toString(edge.getAdditionalInfo())) + .setCloudType("CE"); + if (edge.getCustomerId() != null) { + builder.setCustomerIdMSB(edge.getCustomerId().getId().getMostSignificantBits()) + .setCustomerIdLSB(edge.getCustomerId().getId().getLeastSignificantBits()); + } + return builder.build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/BaseEntityViewMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/BaseEntityViewMsgConstructor.java new file mode 100644 index 0000000000..a037e3ee09 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/BaseEntityViewMsgConstructor.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.entityview; + +import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public abstract class BaseEntityViewMsgConstructor implements EntityViewMsgConstructor { + + @Override + public EntityViewUpdateMsg constructEntityViewDeleteMsg(EntityViewId entityViewId) { + return EntityViewUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(entityViewId.getId().getMostSignificantBits()) + .setIdLSB(entityViewId.getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructor.java new file mode 100644 index 0000000000..7f14f62c68 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructor.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.entityview; + +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; + +public interface EntityViewMsgConstructor extends MsgConstructor { + + EntityViewUpdateMsg constructEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView); + + EntityViewUpdateMsg constructEntityViewDeleteMsg(EntityViewId entityViewId); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorFactory.java new file mode 100644 index 0000000000..1a4ae9ef33 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.entityview; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.BaseMsgConstructorFactory; + +@Component +@TbCoreComponent +public class EntityViewMsgConstructorFactory extends BaseMsgConstructorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorV1.java new file mode 100644 index 0000000000..da369ecf90 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorV1.java @@ -0,0 +1,63 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.entityview; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.gen.edge.v1.EdgeEntityType; +import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class EntityViewMsgConstructorV1 extends BaseEntityViewMsgConstructor { + + @Override + public EntityViewUpdateMsg constructEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView) { + EdgeEntityType edgeEntityType = checkEntityType(entityView.getEntityId().getEntityType()); + EntityViewUpdateMsg.Builder builder = EntityViewUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(entityView.getId().getId().getMostSignificantBits()) + .setIdLSB(entityView.getId().getId().getLeastSignificantBits()) + .setName(entityView.getName()) + .setType(entityView.getType()) + .setEntityIdMSB(entityView.getEntityId().getId().getMostSignificantBits()) + .setEntityIdLSB(entityView.getEntityId().getId().getLeastSignificantBits()) + .setEntityType(edgeEntityType); + if (entityView.getCustomerId() != null) { + builder.setCustomerIdMSB(entityView.getCustomerId().getId().getMostSignificantBits()); + builder.setCustomerIdLSB(entityView.getCustomerId().getId().getLeastSignificantBits()); + } + if (entityView.getAdditionalInfo() != null) { + builder.setAdditionalInfo(JacksonUtil.toString(entityView.getAdditionalInfo())); + } + return builder.build(); + } + + private EdgeEntityType checkEntityType(EntityType entityType) { + switch (entityType) { + case DEVICE: + return EdgeEntityType.DEVICE; + case ASSET: + return EdgeEntityType.ASSET; + default: + throw new RuntimeException("Unsupported entity type [" + entityType + "]"); + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorV2.java new file mode 100644 index 0000000000..c1eae91097 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/entityview/EntityViewMsgConstructorV2.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.entityview; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class EntityViewMsgConstructorV2 extends BaseEntityViewMsgConstructor { + + @Override + public EntityViewUpdateMsg constructEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView) { + return EntityViewUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(entityView)) + .setIdMSB(entityView.getId().getId().getMostSignificantBits()) + .setIdLSB(entityView.getId().getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/BaseOtaPackageMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/BaseOtaPackageMsgConstructor.java new file mode 100644 index 0000000000..110c24caca --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/BaseOtaPackageMsgConstructor.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.ota; + +import org.thingsboard.server.common.data.id.OtaPackageId; +import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public abstract class BaseOtaPackageMsgConstructor implements OtaPackageMsgConstructor { + + @Override + public OtaPackageUpdateMsg constructOtaPackageDeleteMsg(OtaPackageId otaPackageId) { + return OtaPackageUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(otaPackageId.getId().getMostSignificantBits()) + .setIdLSB(otaPackageId.getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructor.java new file mode 100644 index 0000000000..500500a7fc --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructor.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.ota; + +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.id.OtaPackageId; +import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; + +public interface OtaPackageMsgConstructor extends MsgConstructor { + + OtaPackageUpdateMsg constructOtaPackageUpdatedMsg(UpdateMsgType msgType, OtaPackage otaPackage); + + OtaPackageUpdateMsg constructOtaPackageDeleteMsg(OtaPackageId otaPackageId); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorFactory.java new file mode 100644 index 0000000000..2427174892 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.ota; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.BaseMsgConstructorFactory; + +@Component +@TbCoreComponent +public class OtaPackageMsgConstructorFactory extends BaseMsgConstructorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorV1.java new file mode 100644 index 0000000000..5e1f297c01 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorV1.java @@ -0,0 +1,72 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.ota; + +import com.google.protobuf.ByteString; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class OtaPackageMsgConstructorV1 extends BaseOtaPackageMsgConstructor { + + @Override + public OtaPackageUpdateMsg constructOtaPackageUpdatedMsg(UpdateMsgType msgType, OtaPackage otaPackage) { + OtaPackageUpdateMsg.Builder builder = OtaPackageUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(otaPackage.getId().getId().getMostSignificantBits()) + .setIdLSB(otaPackage.getId().getId().getLeastSignificantBits()) + .setType(otaPackage.getType().name()) + .setTitle(otaPackage.getTitle()) + .setVersion(otaPackage.getVersion()) + .setTag(otaPackage.getTag()); + + if (otaPackage.getDeviceProfileId() != null) { + builder.setDeviceProfileIdMSB(otaPackage.getDeviceProfileId().getId().getMostSignificantBits()) + .setDeviceProfileIdLSB(otaPackage.getDeviceProfileId().getId().getLeastSignificantBits()); + } + + if (otaPackage.getUrl() != null) { + builder.setUrl(otaPackage.getUrl()); + } + if (otaPackage.getAdditionalInfo() != null) { + builder.setAdditionalInfo(JacksonUtil.toString(otaPackage.getAdditionalInfo())); + } + if (otaPackage.getFileName() != null) { + builder.setFileName(otaPackage.getFileName()); + } + if (otaPackage.getContentType() != null) { + builder.setContentType(otaPackage.getContentType()); + } + if (otaPackage.getChecksumAlgorithm() != null) { + builder.setChecksumAlgorithm(otaPackage.getChecksumAlgorithm().name()); + } + if (otaPackage.getChecksum() != null) { + builder.setChecksum(otaPackage.getChecksum()); + } + if (otaPackage.getDataSize() != null) { + builder.setDataSize(otaPackage.getDataSize()); + } + if (otaPackage.getData() != null) { + builder.setData(ByteString.copyFrom(otaPackage.getData().array())); + } + return builder.build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorV2.java new file mode 100644 index 0000000000..69d7f12874 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/ota/OtaPackageMsgConstructorV2.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.ota; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class OtaPackageMsgConstructorV2 extends BaseOtaPackageMsgConstructor { + + @Override + public OtaPackageUpdateMsg constructOtaPackageUpdatedMsg(UpdateMsgType msgType, OtaPackage otaPackage) { + return OtaPackageUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(otaPackage)) + .setIdMSB(otaPackage.getId().getId().getMostSignificantBits()) + .setIdLSB(otaPackage.getId().getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/BaseQueueMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/BaseQueueMsgConstructor.java new file mode 100644 index 0000000000..486a143d85 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/BaseQueueMsgConstructor.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.queue; + +import org.thingsboard.server.common.data.id.QueueId; +import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public abstract class BaseQueueMsgConstructor implements QueueMsgConstructor { + + @Override + public QueueUpdateMsg constructQueueDeleteMsg(QueueId queueId) { + return QueueUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(queueId.getId().getMostSignificantBits()) + .setIdLSB(queueId.getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructor.java new file mode 100644 index 0000000000..847f19419e --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructor.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.queue; + +import org.thingsboard.server.common.data.id.QueueId; +import org.thingsboard.server.common.data.queue.Queue; +import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; + +public interface QueueMsgConstructor extends MsgConstructor { + + QueueUpdateMsg constructQueueUpdatedMsg(UpdateMsgType msgType, Queue queue); + + QueueUpdateMsg constructQueueDeleteMsg(QueueId queueId); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorFactory.java new file mode 100644 index 0000000000..9d322a8ad7 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.queue; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.BaseMsgConstructorFactory; + +@Component +@TbCoreComponent +public class QueueMsgConstructorFactory extends BaseMsgConstructorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorV1.java new file mode 100644 index 0000000000..8621ce1011 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorV1.java @@ -0,0 +1,74 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.queue; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.QueueId; +import org.thingsboard.server.common.data.queue.ProcessingStrategy; +import org.thingsboard.server.common.data.queue.Queue; +import org.thingsboard.server.common.data.queue.SubmitStrategy; +import org.thingsboard.server.gen.edge.v1.ProcessingStrategyProto; +import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; +import org.thingsboard.server.gen.edge.v1.SubmitStrategyProto; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class QueueMsgConstructorV1 extends BaseQueueMsgConstructor { + + @Override + public QueueUpdateMsg constructQueueUpdatedMsg(UpdateMsgType msgType, Queue queue) { + return QueueUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(queue.getId().getId().getMostSignificantBits()) + .setIdLSB(queue.getId().getId().getLeastSignificantBits()) + .setTenantIdMSB(queue.getTenantId().getId().getMostSignificantBits()) + .setTenantIdLSB(queue.getTenantId().getId().getLeastSignificantBits()) + .setName(queue.getName()) + .setTopic(queue.getTopic()) + .setPollInterval(queue.getPollInterval()) + .setPartitions(queue.getPartitions()) + .setConsumerPerPartition(queue.isConsumerPerPartition()) + .setPackProcessingTimeout(queue.getPackProcessingTimeout()) + .setSubmitStrategy(createSubmitStrategyProto(queue.getSubmitStrategy())) + .setProcessingStrategy(createProcessingStrategyProto(queue.getProcessingStrategy())).build(); + } + + private ProcessingStrategyProto createProcessingStrategyProto(ProcessingStrategy processingStrategy) { + return ProcessingStrategyProto.newBuilder() + .setType(processingStrategy.getType().name()) + .setRetries(processingStrategy.getRetries()) + .setFailurePercentage(processingStrategy.getFailurePercentage()) + .setPauseBetweenRetries(processingStrategy.getPauseBetweenRetries()) + .setMaxPauseBetweenRetries(processingStrategy.getMaxPauseBetweenRetries()) + .build(); + } + + private SubmitStrategyProto createSubmitStrategyProto(SubmitStrategy submitStrategy) { + return SubmitStrategyProto.newBuilder() + .setType(submitStrategy.getType().name()) + .setBatchSize(submitStrategy.getBatchSize()) + .build(); + } + + public QueueUpdateMsg constructQueueDeleteMsg(QueueId queueId) { + return QueueUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(queueId.getId().getMostSignificantBits()) + .setIdLSB(queueId.getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorV2.java new file mode 100644 index 0000000000..1bf80eea64 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/queue/QueueMsgConstructorV2.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.queue; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.queue.Queue; +import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class QueueMsgConstructorV2 extends BaseQueueMsgConstructor { + + @Override + public QueueUpdateMsg constructQueueUpdatedMsg(UpdateMsgType msgType, Queue queue) { + return QueueUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(queue)) + .setIdMSB(queue.getId().getId().getMostSignificantBits()) + .setIdLSB(queue.getId().getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructor.java new file mode 100644 index 0000000000..f7fac6ce71 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructor.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.relation; + +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; + +public interface RelationMsgConstructor extends MsgConstructor { + + RelationUpdateMsg constructRelationUpdatedMsg(UpdateMsgType msgType, EntityRelation entityRelation); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorFactory.java new file mode 100644 index 0000000000..f0ddebc880 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.relation; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.BaseMsgConstructorFactory; + +@Component +@TbCoreComponent +public class RelationMsgConstructorFactory extends BaseMsgConstructorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorV1.java new file mode 100644 index 0000000000..394086fdd0 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorV1.java @@ -0,0 +1,48 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.relation; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class RelationMsgConstructorV1 implements RelationMsgConstructor { + + @Override + public RelationUpdateMsg constructRelationUpdatedMsg(UpdateMsgType msgType, EntityRelation entityRelation) { + RelationUpdateMsg.Builder builder = RelationUpdateMsg.newBuilder() + .setMsgType(msgType) + .setFromIdMSB(entityRelation.getFrom().getId().getMostSignificantBits()) + .setFromIdLSB(entityRelation.getFrom().getId().getLeastSignificantBits()) + .setFromEntityType(entityRelation.getFrom().getEntityType().name()) + .setToIdMSB(entityRelation.getTo().getId().getMostSignificantBits()) + .setToIdLSB(entityRelation.getTo().getId().getLeastSignificantBits()) + .setToEntityType(entityRelation.getTo().getEntityType().name()) + .setType(entityRelation.getType()); + if (entityRelation.getAdditionalInfo() != null) { + builder.setAdditionalInfo(JacksonUtil.toString(entityRelation.getAdditionalInfo())); + } + if (entityRelation.getTypeGroup() != null) { + builder.setTypeGroup(entityRelation.getTypeGroup().name()); + } + return builder.build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorV2.java new file mode 100644 index 0000000000..4510909490 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/relation/RelationMsgConstructorV2.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.relation; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class RelationMsgConstructorV2 implements RelationMsgConstructor { + + + @Override + public RelationUpdateMsg constructRelationUpdatedMsg(UpdateMsgType msgType, EntityRelation entityRelation) { + return RelationUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(entityRelation)).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/BaseResourceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/BaseResourceMsgConstructor.java new file mode 100644 index 0000000000..620f71c9e7 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/BaseResourceMsgConstructor.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.resource; + +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public abstract class BaseResourceMsgConstructor implements ResourceMsgConstructor { + + @Override + public ResourceUpdateMsg constructResourceDeleteMsg(TbResourceId tbResourceId) { + return ResourceUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(tbResourceId.getId().getMostSignificantBits()) + .setIdLSB(tbResourceId.getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructor.java new file mode 100644 index 0000000000..cfeedced80 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructor.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.resource; + +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; + +public interface ResourceMsgConstructor extends MsgConstructor { + + ResourceUpdateMsg constructResourceUpdatedMsg(UpdateMsgType msgType, TbResource tbResource); + + ResourceUpdateMsg constructResourceDeleteMsg(TbResourceId tbResourceId); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorFactory.java new file mode 100644 index 0000000000..20443e4244 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.resource; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.BaseMsgConstructorFactory; + +@Component +@TbCoreComponent +public class ResourceMsgConstructorFactory extends BaseMsgConstructorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorV1.java new file mode 100644 index 0000000000..f58f0b7c25 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorV1.java @@ -0,0 +1,56 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.resource; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class ResourceMsgConstructorV1 extends BaseResourceMsgConstructor { + + @Override + public ResourceUpdateMsg constructResourceUpdatedMsg(UpdateMsgType msgType, TbResource tbResource) { + if (ResourceType.IMAGE.equals(tbResource.getResourceType())) { + // Exclude support for a recently added resource type when dealing with older Edges + // to maintain compatibility and avoid potential issues. + return null; + } + ResourceUpdateMsg.Builder builder = ResourceUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(tbResource.getId().getId().getMostSignificantBits()) + .setIdLSB(tbResource.getId().getId().getLeastSignificantBits()) + .setTitle(tbResource.getTitle()) + .setResourceKey(tbResource.getResourceKey()) + .setResourceType(tbResource.getResourceType().name()) + .setFileName(tbResource.getFileName()); + if (tbResource.getData() != null) { + builder.setData(tbResource.getEncodedData()); + } + if (tbResource.getEtag() != null) { + builder.setEtag(tbResource.getEtag()); + } + if (TenantId.SYS_TENANT_ID.equals(tbResource.getTenantId())) { + builder.setIsSystem(true); + } + return builder.build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorV2.java new file mode 100644 index 0000000000..094e5636bb --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/resource/ResourceMsgConstructorV2.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.resource; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class ResourceMsgConstructorV2 extends BaseResourceMsgConstructor { + + @Override + public ResourceUpdateMsg constructResourceUpdatedMsg(UpdateMsgType msgType, TbResource tbResource) { + return ResourceUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(tbResource)) + .setIdMSB(tbResource.getId().getId().getMostSignificantBits()) + .setIdLSB(tbResource.getId().getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java deleted file mode 100644 index 28c1b1c458..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.constructor.rule; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.node.ObjectNode; -import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.rule.NodeConnectionInfo; -import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; -import org.thingsboard.server.common.data.rule.RuleChainMetaData; -import org.thingsboard.server.common.data.rule.RuleNode; -import org.thingsboard.server.gen.edge.v1.NodeConnectionInfoProto; -import org.thingsboard.server.gen.edge.v1.RuleChainConnectionInfoProto; -import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; -import org.thingsboard.server.gen.edge.v1.RuleNodeProto; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; - -import java.util.ArrayList; -import java.util.List; -import java.util.NavigableSet; - -@Slf4j -@AllArgsConstructor -public abstract class AbstractRuleChainMetadataConstructor implements RuleChainMetadataConstructor { - - @Override - public RuleChainMetadataUpdateMsg constructRuleChainMetadataUpdatedMsg(TenantId tenantId, - UpdateMsgType msgType, - RuleChainMetaData ruleChainMetaData) { - try { - RuleChainMetadataUpdateMsg.Builder builder = RuleChainMetadataUpdateMsg.newBuilder(); - builder.setRuleChainIdMSB(ruleChainMetaData.getRuleChainId().getId().getMostSignificantBits()) - .setRuleChainIdLSB(ruleChainMetaData.getRuleChainId().getId().getLeastSignificantBits()); - constructRuleChainMetadataUpdatedMsg(tenantId, builder, ruleChainMetaData); - builder.setMsgType(msgType); - return builder.build(); - } catch (JsonProcessingException ex) { - log.error("Can't construct RuleChainMetadataUpdateMsg", ex); - } - return null; - } - - protected abstract void constructRuleChainMetadataUpdatedMsg(TenantId tenantId, - RuleChainMetadataUpdateMsg.Builder builder, - RuleChainMetaData ruleChainMetaData) throws JsonProcessingException; - - protected List constructConnections(List connections) { - List result = new ArrayList<>(); - if (connections != null && !connections.isEmpty()) { - for (NodeConnectionInfo connection : connections) { - result.add(constructConnection(connection)); - } - } - return result; - } - - private NodeConnectionInfoProto constructConnection(NodeConnectionInfo connection) { - return NodeConnectionInfoProto.newBuilder() - .setFromIndex(connection.getFromIndex()) - .setToIndex(connection.getToIndex()) - .setType(connection.getType()) - .build(); - } - - protected List constructNodes(List nodes) throws JsonProcessingException { - List result = new ArrayList<>(); - if (nodes != null && !nodes.isEmpty()) { - for (RuleNode node : nodes) { - result.add(constructNode(node)); - } - } - return result; - } - - private RuleNodeProto constructNode(RuleNode node) throws JsonProcessingException { - return RuleNodeProto.newBuilder() - .setIdMSB(node.getId().getId().getMostSignificantBits()) - .setIdLSB(node.getId().getId().getLeastSignificantBits()) - .setType(node.getType()) - .setName(node.getName()) - .setDebugMode(node.isDebugMode()) - .setConfiguration(JacksonUtil.OBJECT_MAPPER.writeValueAsString(node.getConfiguration())) - .setAdditionalInfo(JacksonUtil.OBJECT_MAPPER.writeValueAsString(node.getAdditionalInfo())) - .build(); - } - - protected List constructRuleChainConnections(List ruleChainConnections, - NavigableSet removedNodeIndexes) throws JsonProcessingException { - List result = new ArrayList<>(); - if (ruleChainConnections != null && !ruleChainConnections.isEmpty()) { - for (RuleChainConnectionInfo ruleChainConnectionInfo : ruleChainConnections) { - if (!removedNodeIndexes.isEmpty()) { // 3_3_0 only - int fromIndex = ruleChainConnectionInfo.getFromIndex(); - // decrease index because of removed nodes - for (Integer removedIndex : removedNodeIndexes) { - if (fromIndex > removedIndex) { - fromIndex = fromIndex - 1; - } - } - ruleChainConnectionInfo.setFromIndex(fromIndex); - ObjectNode additionalInfo = (ObjectNode) ruleChainConnectionInfo.getAdditionalInfo(); - if (additionalInfo.get("ruleChainNodeId") == null) { - additionalInfo.put("ruleChainNodeId", "rule-chain-node-UNDEFINED"); - } - } - result.add(constructRuleChainConnection(ruleChainConnectionInfo)); - } - } - return result; - } - - private RuleChainConnectionInfoProto constructRuleChainConnection(RuleChainConnectionInfo ruleChainConnectionInfo) throws JsonProcessingException { - return RuleChainConnectionInfoProto.newBuilder() - .setFromIndex(ruleChainConnectionInfo.getFromIndex()) - .setTargetRuleChainIdMSB(ruleChainConnectionInfo.getTargetRuleChainId().getId().getMostSignificantBits()) - .setTargetRuleChainIdLSB(ruleChainConnectionInfo.getTargetRuleChainId().getId().getLeastSignificantBits()) - .setType(ruleChainConnectionInfo.getType()) - .setAdditionalInfo(JacksonUtil.OBJECT_MAPPER.writeValueAsString(ruleChainConnectionInfo.getAdditionalInfo())) - .build(); - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMetadataConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMetadataConstructor.java new file mode 100644 index 0000000000..be10a714f6 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMetadataConstructor.java @@ -0,0 +1,133 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.rule; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.NodeConnectionInfo; +import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.NodeConnectionInfoProto; +import org.thingsboard.server.gen.edge.v1.RuleChainConnectionInfoProto; +import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; +import org.thingsboard.server.gen.edge.v1.RuleNodeProto; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +import java.util.ArrayList; +import java.util.List; +import java.util.NavigableSet; + +@Slf4j +@AllArgsConstructor +public abstract class BaseRuleChainMetadataConstructor implements RuleChainMetadataConstructor { + + @Override + public RuleChainMetadataUpdateMsg constructRuleChainMetadataUpdatedMsg(TenantId tenantId, + UpdateMsgType msgType, + RuleChainMetaData ruleChainMetaData, + EdgeVersion edgeVersion) { + RuleChainMetadataUpdateMsg.Builder builder = RuleChainMetadataUpdateMsg.newBuilder(); + constructRuleChainMetadataUpdatedMsg(tenantId, builder, ruleChainMetaData); + builder.setMsgType(msgType); + return builder.build(); + } + + protected abstract void constructRuleChainMetadataUpdatedMsg(TenantId tenantId, + RuleChainMetadataUpdateMsg.Builder builder, + RuleChainMetaData ruleChainMetaData); + + protected List constructConnections(List connections) { + List result = new ArrayList<>(); + if (connections != null && !connections.isEmpty()) { + for (NodeConnectionInfo connection : connections) { + result.add(constructConnection(connection)); + } + } + return result; + } + + private NodeConnectionInfoProto constructConnection(NodeConnectionInfo connection) { + return NodeConnectionInfoProto.newBuilder() + .setFromIndex(connection.getFromIndex()) + .setToIndex(connection.getToIndex()) + .setType(connection.getType()) + .build(); + } + + protected List constructNodes(List nodes) { + List result = new ArrayList<>(); + if (nodes != null && !nodes.isEmpty()) { + for (RuleNode node : nodes) { + result.add(constructNode(node)); + } + } + return result; + } + + private RuleNodeProto constructNode(RuleNode node) { + return RuleNodeProto.newBuilder() + .setIdMSB(node.getId().getId().getMostSignificantBits()) + .setIdLSB(node.getId().getId().getLeastSignificantBits()) + .setType(node.getType()) + .setName(node.getName()) + .setDebugMode(node.isDebugMode()) + .setConfiguration(JacksonUtil.toString(node.getConfiguration())) + .setAdditionalInfo(JacksonUtil.toString(node.getAdditionalInfo())) + .setSingletonMode(node.isSingletonMode()) + .setConfigurationVersion(node.getConfigurationVersion()) + .build(); + } + + protected List constructRuleChainConnections(List ruleChainConnections, + NavigableSet removedNodeIndexes) { + List result = new ArrayList<>(); + if (ruleChainConnections != null && !ruleChainConnections.isEmpty()) { + for (RuleChainConnectionInfo ruleChainConnectionInfo : ruleChainConnections) { + if (!removedNodeIndexes.isEmpty()) { // 3_3_0 only + int fromIndex = ruleChainConnectionInfo.getFromIndex(); + // decrease index because of removed nodes + for (Integer removedIndex : removedNodeIndexes) { + if (fromIndex > removedIndex) { + fromIndex = fromIndex - 1; + } + } + ruleChainConnectionInfo.setFromIndex(fromIndex); + ObjectNode additionalInfo = (ObjectNode) ruleChainConnectionInfo.getAdditionalInfo(); + if (additionalInfo.get("ruleChainNodeId") == null) { + additionalInfo.put("ruleChainNodeId", "rule-chain-node-UNDEFINED"); + } + } + result.add(constructRuleChainConnection(ruleChainConnectionInfo)); + } + } + return result; + } + + private RuleChainConnectionInfoProto constructRuleChainConnection(RuleChainConnectionInfo ruleChainConnectionInfo) { + return RuleChainConnectionInfoProto.newBuilder() + .setFromIndex(ruleChainConnectionInfo.getFromIndex()) + .setTargetRuleChainIdMSB(ruleChainConnectionInfo.getTargetRuleChainId().getId().getMostSignificantBits()) + .setTargetRuleChainIdLSB(ruleChainConnectionInfo.getTargetRuleChainId().getId().getLeastSignificantBits()) + .setType(ruleChainConnectionInfo.getType()) + .setAdditionalInfo(JacksonUtil.toString(ruleChainConnectionInfo.getAdditionalInfo())) + .build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMsgConstructor.java new file mode 100644 index 0000000000..cf5967fac1 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMsgConstructor.java @@ -0,0 +1,44 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.rule; + +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; +import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +public abstract class BaseRuleChainMsgConstructor implements RuleChainMsgConstructor { + + @Override + public RuleChainUpdateMsg constructRuleChainDeleteMsg(RuleChainId ruleChainId) { + return RuleChainUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(ruleChainId.getId().getMostSignificantBits()) + .setIdLSB(ruleChainId.getId().getLeastSignificantBits()).build(); + } + + @Override + public RuleChainMetadataUpdateMsg constructRuleChainMetadataUpdatedMsg(TenantId tenantId, + UpdateMsgType msgType, + RuleChainMetaData ruleChainMetaData, + EdgeVersion edgeVersion) { + RuleChainMetadataConstructor ruleChainMetadataConstructor = RuleChainMetadataConstructorFactory.getByEdgeVersion(edgeVersion); + return ruleChainMetadataConstructor.constructRuleChainMetadataUpdatedMsg(tenantId, msgType, ruleChainMetaData, edgeVersion); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructor.java index 51b7380e48..f80b30b3c4 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructor.java @@ -17,6 +17,7 @@ package org.thingsboard.server.service.edge.rpc.constructor.rule; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; @@ -24,5 +25,6 @@ public interface RuleChainMetadataConstructor { RuleChainMetadataUpdateMsg constructRuleChainMetadataUpdatedMsg(TenantId tenantId, UpdateMsgType msgType, - RuleChainMetaData ruleChainMetaData); + RuleChainMetaData ruleChainMetaData, + EdgeVersion edgeVersion); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorFactory.java index 1c581267c4..cdc7e128d3 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorFactory.java @@ -25,8 +25,12 @@ public final class RuleChainMetadataConstructorFactory { return new RuleChainMetadataConstructorV330(); case V_3_3_3: case V_3_4_0: - default: + case V_3_6_0: + case V_3_6_1: return new RuleChainMetadataConstructorV340(); + case V_3_6_2: + default: + return new RuleChainMetadataConstructorV362(); } } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV330.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV330.java index 5e1310a5a5..64d362342b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV330.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV330.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.edge.rpc.constructor.rule; -import com.fasterxml.jackson.core.JsonProcessingException; import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.flow.TbRuleChainInputNode; @@ -37,7 +36,7 @@ import java.util.UUID; import java.util.stream.Collectors; @Slf4j -public class RuleChainMetadataConstructorV330 extends AbstractRuleChainMetadataConstructor { +public class RuleChainMetadataConstructorV330 extends BaseRuleChainMetadataConstructor { private static final String RULE_CHAIN_INPUT_NODE = TbRuleChainInputNode.class.getName(); private static final String TB_RULE_CHAIN_OUTPUT_NODE = TbRuleChainOutputNode.class.getName(); @@ -45,7 +44,9 @@ public class RuleChainMetadataConstructorV330 extends AbstractRuleChainMetadataC @Override protected void constructRuleChainMetadataUpdatedMsg(TenantId tenantId, RuleChainMetadataUpdateMsg.Builder builder, - RuleChainMetaData ruleChainMetaData) throws JsonProcessingException { + RuleChainMetaData ruleChainMetaData) { + builder.setRuleChainIdMSB(ruleChainMetaData.getRuleChainId().getId().getMostSignificantBits()) + .setRuleChainIdLSB(ruleChainMetaData.getRuleChainId().getId().getLeastSignificantBits()); List supportedNodes = filterNodes(ruleChainMetaData.getNodes()); NavigableSet removedNodeIndexes = getRemovedNodeIndexes(ruleChainMetaData.getNodes(), ruleChainMetaData.getConnections()); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV340.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV340.java index 6fe39942ad..2133024931 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV340.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV340.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.edge.rpc.constructor.rule; -import com.fasterxml.jackson.core.JsonProcessingException; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.RuleChainMetaData; @@ -24,12 +23,14 @@ import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import java.util.TreeSet; @Slf4j -public class RuleChainMetadataConstructorV340 extends AbstractRuleChainMetadataConstructor { +public class RuleChainMetadataConstructorV340 extends BaseRuleChainMetadataConstructor { @Override protected void constructRuleChainMetadataUpdatedMsg(TenantId tenantId, RuleChainMetadataUpdateMsg.Builder builder, - RuleChainMetaData ruleChainMetaData) throws JsonProcessingException { + RuleChainMetaData ruleChainMetaData) { + builder.setRuleChainIdMSB(ruleChainMetaData.getRuleChainId().getId().getMostSignificantBits()) + .setRuleChainIdLSB(ruleChainMetaData.getRuleChainId().getId().getLeastSignificantBits()); builder.addAllNodes(constructNodes(ruleChainMetaData.getNodes())) .addAllConnections(constructConnections(ruleChainMetaData.getConnections())) .addAllRuleChainConnections(constructRuleChainConnections(ruleChainMetaData.getRuleChainConnections(), new TreeSet<>())); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV362.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV362.java new file mode 100644 index 0000000000..7a8a6d02cb --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMetadataConstructorV362.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.rule; + +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; + +public class RuleChainMetadataConstructorV362 extends BaseRuleChainMetadataConstructor { + + @Override + protected void constructRuleChainMetadataUpdatedMsg(TenantId tenantId, RuleChainMetadataUpdateMsg.Builder builder, RuleChainMetaData ruleChainMetaData) { + builder.setEntity(JacksonUtil.toString(ruleChainMetaData)); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructor.java new file mode 100644 index 0000000000..8ea2ff9a63 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructor.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.rule; + +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; +import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; + +public interface RuleChainMsgConstructor extends MsgConstructor { + + RuleChainUpdateMsg constructRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain, boolean isRoot); + + RuleChainUpdateMsg constructRuleChainDeleteMsg(RuleChainId ruleChainId); + + RuleChainMetadataUpdateMsg constructRuleChainMetadataUpdatedMsg(TenantId tenantId, + UpdateMsgType msgType, + RuleChainMetaData ruleChainMetaData, + EdgeVersion edgeVersion); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorFactory.java new file mode 100644 index 0000000000..486941b0fe --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.rule; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.BaseMsgConstructorFactory; + +@Component +@TbCoreComponent +public class RuleChainMsgConstructorFactory extends BaseMsgConstructorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV1.java new file mode 100644 index 0000000000..6c918cfc56 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV1.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.rule; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class RuleChainMsgConstructorV1 extends BaseRuleChainMsgConstructor { + + @Override + public RuleChainUpdateMsg constructRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain, boolean isRoot) { + RuleChainUpdateMsg.Builder builder = RuleChainUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(ruleChain.getId().getId().getMostSignificantBits()) + .setIdLSB(ruleChain.getId().getId().getLeastSignificantBits()) + .setName(ruleChain.getName()) + .setRoot(isRoot) + .setDebugMode(ruleChain.isDebugMode()) + .setConfiguration(JacksonUtil.toString(ruleChain.getConfiguration())); + if (ruleChain.getFirstRuleNodeId() != null) { + builder.setFirstRuleNodeIdMSB(ruleChain.getFirstRuleNodeId().getId().getMostSignificantBits()) + .setFirstRuleNodeIdLSB(ruleChain.getFirstRuleNodeId().getId().getLeastSignificantBits()); + } + return builder.build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV2.java new file mode 100644 index 0000000000..f5c1c12b39 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/RuleChainMsgConstructorV2.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.rule; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class RuleChainMsgConstructorV2 extends BaseRuleChainMsgConstructor { + + @Override + public RuleChainUpdateMsg constructRuleChainUpdatedMsg(UpdateMsgType msgType, RuleChain ruleChain, boolean isRoot) { + boolean isTemplateRoot = ruleChain.isRoot(); + ruleChain.setRoot(isRoot); + RuleChainUpdateMsg result = RuleChainUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(ruleChain)) + .setIdMSB(ruleChain.getId().getId().getMostSignificantBits()) + .setIdLSB(ruleChain.getId().getId().getLeastSignificantBits()).build(); + ruleChain.setRoot(isTemplateRoot); + return result; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructor.java new file mode 100644 index 0000000000..da4f964218 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructor.java @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.settings; + +import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; + +public interface AdminSettingsMsgConstructor extends MsgConstructor { + + AdminSettingsUpdateMsg constructAdminSettingsUpdateMsg(AdminSettings adminSettings); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorFactory.java new file mode 100644 index 0000000000..88c5a6b756 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.settings; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.BaseMsgConstructorFactory; + +@Component +@TbCoreComponent +public class AdminSettingsMsgConstructorFactory extends BaseMsgConstructorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorV1.java new file mode 100644 index 0000000000..cbbd9b5bb8 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorV1.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.settings; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class AdminSettingsMsgConstructorV1 implements AdminSettingsMsgConstructor { + + @Override + public AdminSettingsUpdateMsg constructAdminSettingsUpdateMsg(AdminSettings adminSettings) { + AdminSettingsUpdateMsg.Builder builder = AdminSettingsUpdateMsg.newBuilder() + .setKey(adminSettings.getKey()) + .setJsonValue(JacksonUtil.toString(adminSettings.getJsonValue())); + if (TenantId.SYS_TENANT_ID.equals(adminSettings.getTenantId())) { + builder.setIsSystem(true); + } + return builder.build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorV2.java new file mode 100644 index 0000000000..de92d2a1f9 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/settings/AdminSettingsMsgConstructorV2.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.settings; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class AdminSettingsMsgConstructorV2 implements AdminSettingsMsgConstructor { + + @Override + public AdminSettingsUpdateMsg constructAdminSettingsUpdateMsg(AdminSettings adminSettings) { + return AdminSettingsUpdateMsg.newBuilder().setEntity(JacksonUtil.toString(adminSettings)).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/telemetry/EntityDataMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/telemetry/EntityDataMsgConstructor.java new file mode 100644 index 0000000000..4ec6376a4d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/telemetry/EntityDataMsgConstructor.java @@ -0,0 +1,109 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.telemetry; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import com.google.gson.reflect.TypeToken; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.adaptor.JsonConverter; +import org.thingsboard.server.gen.edge.v1.AttributeDeleteMsg; +import org.thingsboard.server.gen.edge.v1.EntityDataProto; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import java.util.List; + +@Slf4j +@Component +@TbCoreComponent +public class EntityDataMsgConstructor { + + public EntityDataProto constructEntityDataMsg(TenantId tenantId, EntityId entityId, EdgeEventActionType actionType, JsonElement entityData) { + EntityDataProto.Builder builder = EntityDataProto.newBuilder() + .setEntityIdMSB(entityId.getId().getMostSignificantBits()) + .setEntityIdLSB(entityId.getId().getLeastSignificantBits()) + .setEntityType(entityId.getEntityType().name()); + switch (actionType) { + case TIMESERIES_UPDATED: + try { + JsonObject data = entityData.getAsJsonObject(); + long ts; + if (data.get("ts") != null && !data.get("ts").isJsonNull()) { + ts = data.getAsJsonPrimitive("ts").getAsLong(); + } else { + ts = System.currentTimeMillis(); + } + builder.setPostTelemetryMsg(JsonConverter.convertToTelemetryProto(data.getAsJsonObject("data"), ts)); + } catch (Exception e) { + log.warn("[{}][{}] Can't convert to telemetry proto, entityData [{}]", tenantId, entityId, entityData, e); + } + break; + case ATTRIBUTES_UPDATED: + try { + JsonObject data = entityData.getAsJsonObject(); + TransportProtos.PostAttributeMsg attributesUpdatedMsg = JsonConverter.convertToAttributesProto(data.getAsJsonObject("kv")); + builder.setAttributesUpdatedMsg(attributesUpdatedMsg); + builder.setPostAttributeScope(getScopeOfDefault(data)); + } catch (Exception e) { + log.warn("[{}][{}] Can't convert to AttributesUpdatedMsg proto, entityData [{}]", tenantId, entityId, entityData, e); + } + break; + case POST_ATTRIBUTES: + try { + JsonObject data = entityData.getAsJsonObject(); + TransportProtos.PostAttributeMsg postAttributesMsg = JsonConverter.convertToAttributesProto(data.getAsJsonObject("kv")); + builder.setPostAttributesMsg(postAttributesMsg); + builder.setPostAttributeScope(getScopeOfDefault(data)); + } catch (Exception e) { + log.warn("[{}][{}] Can't convert to PostAttributesMsg, entityData [{}]", tenantId, entityId, entityData, e); + } + break; + case ATTRIBUTES_DELETED: + try { + AttributeDeleteMsg.Builder attributeDeleteMsg = AttributeDeleteMsg.newBuilder(); + attributeDeleteMsg.setScope(entityData.getAsJsonObject().getAsJsonPrimitive("scope").getAsString()); + JsonArray jsonArray = entityData.getAsJsonObject().getAsJsonArray("keys"); + List keys = new Gson().fromJson(jsonArray.toString(), new TypeToken<>(){}.getType()); + attributeDeleteMsg.addAllAttributeNames(keys); + attributeDeleteMsg.build(); + builder.setAttributeDeleteMsg(attributeDeleteMsg); + } catch (Exception e) { + log.warn("[{}][{}] Can't convert to AttributeDeleteMsg proto, entityData [{}]", tenantId, entityId, entityData, e); + } + break; + } + return builder.build(); + } + + private String getScopeOfDefault(JsonObject data) { + JsonPrimitive scope = data.getAsJsonPrimitive("scope"); + String result = DataConstants.SERVER_SCOPE; + if (scope != null && StringUtils.isNotBlank(scope.getAsString())) { + result = scope.getAsString(); + } + return result; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructor.java new file mode 100644 index 0000000000..3de654fda6 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructor.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.tenant; + +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; + +public interface TenantMsgConstructor extends MsgConstructor { + + TenantUpdateMsg constructTenantUpdateMsg(UpdateMsgType msgType, Tenant tenant); + + TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, EdgeVersion edgeVersion); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorFactory.java new file mode 100644 index 0000000000..bf96203734 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.tenant; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.BaseMsgConstructorFactory; + +@Component +@TbCoreComponent +public class TenantMsgConstructorFactory extends BaseMsgConstructorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java new file mode 100644 index 0000000000..b454a3b971 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java @@ -0,0 +1,96 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.tenant; + +import com.google.protobuf.ByteString; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.DataDecodingEncodingService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; + +@Component +@TbCoreComponent +public class TenantMsgConstructorV1 implements TenantMsgConstructor { + + @Autowired + private DataDecodingEncodingService dataDecodingEncodingService; + + @Override + public TenantUpdateMsg constructTenantUpdateMsg(UpdateMsgType msgType, Tenant tenant) { + TenantUpdateMsg.Builder builder = TenantUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(tenant.getId().getId().getMostSignificantBits()) + .setIdLSB(tenant.getId().getId().getLeastSignificantBits()) + .setTitle(tenant.getTitle()) + .setProfileIdMSB(tenant.getTenantProfileId().getId().getMostSignificantBits()) + .setProfileIdLSB(tenant.getTenantProfileId().getId().getLeastSignificantBits()) + .setRegion(tenant.getRegion()); + if (tenant.getCountry() != null) { + builder.setCountry(tenant.getCountry()); + } + if (tenant.getState() != null) { + builder.setState(tenant.getState()); + } + if (tenant.getCity() != null) { + builder.setCity(tenant.getCity()); + } + if (tenant.getAddress() != null) { + builder.setAddress(tenant.getAddress()); + } + if (tenant.getAddress2() != null) { + builder.setAddress2(tenant.getAddress2()); + } + if (tenant.getZip() != null) { + builder.setZip(tenant.getZip()); + } + if (tenant.getPhone() != null) { + builder.setPhone(tenant.getPhone()); + } + if (tenant.getEmail() != null) { + builder.setEmail(tenant.getEmail()); + } + if (tenant.getAdditionalInfo() != null) { + builder.setAdditionalInfo(JacksonUtil.toString(tenant.getAdditionalInfo())); + } + return builder.build(); + } + + @Override + public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, EdgeVersion edgeVersion) { + ByteString profileData = EdgeVersionUtils.isEdgeVersionOlderThan(edgeVersion, EdgeVersion.V_3_6_2) ? + ByteString.empty() : ByteString.copyFrom(dataDecodingEncodingService.encode(tenantProfile.getProfileData())); + TenantProfileUpdateMsg.Builder builder = TenantProfileUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(tenantProfile.getId().getId().getMostSignificantBits()) + .setIdLSB(tenantProfile.getId().getId().getLeastSignificantBits()) + .setName(tenantProfile.getName()) + .setDefault(tenantProfile.isDefault()) + .setIsolatedRuleChain(tenantProfile.isIsolatedTbRuleEngine()) + .setProfileDataBytes(profileData); + if (tenantProfile.getDescription() != null) { + builder.setDescription(tenantProfile.getDescription()); + } + return builder.build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java new file mode 100644 index 0000000000..69531e5630 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV2.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.tenant; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class TenantMsgConstructorV2 implements TenantMsgConstructor { + + @Override + public TenantUpdateMsg constructTenantUpdateMsg(UpdateMsgType msgType, Tenant tenant) { + return TenantUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(tenant)).build(); + } + + @Override + public TenantProfileUpdateMsg constructTenantProfileUpdateMsg(UpdateMsgType msgType, TenantProfile tenantProfile, EdgeVersion edgeVersion) { + return TenantProfileUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(tenantProfile)).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/BaseUserMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/BaseUserMsgConstructor.java new file mode 100644 index 0000000000..aef35d8f50 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/BaseUserMsgConstructor.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.user; + +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; + +public abstract class BaseUserMsgConstructor implements UserMsgConstructor { + + @Override + public UserUpdateMsg constructUserDeleteMsg(UserId userId) { + return UserUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(userId.getId().getMostSignificantBits()) + .setIdLSB(userId.getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructor.java new file mode 100644 index 0000000000..3184f8c751 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructor.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.user; + +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.security.UserCredentials; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; + +public interface UserMsgConstructor extends MsgConstructor { + + UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user); + + UserUpdateMsg constructUserDeleteMsg(UserId userId); + + UserCredentialsUpdateMsg constructUserCredentialsUpdatedMsg(UserCredentials userCredentials); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorFactory.java new file mode 100644 index 0000000000..b2309286dd --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.user; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.BaseMsgConstructorFactory; + +@Component +@TbCoreComponent +public class UserMsgConstructorFactory extends BaseMsgConstructorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorV1.java new file mode 100644 index 0000000000..3fe635c54c --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorV1.java @@ -0,0 +1,63 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.user; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.security.UserCredentials; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class UserMsgConstructorV1 extends BaseUserMsgConstructor { + + @Override + public UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user) { + UserUpdateMsg.Builder builder = UserUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(user.getId().getId().getMostSignificantBits()) + .setIdLSB(user.getId().getId().getLeastSignificantBits()) + .setEmail(user.getEmail()) + .setAuthority(user.getAuthority().name()); + if (user.getCustomerId() != null) { + builder.setCustomerIdMSB(user.getCustomerId().getId().getMostSignificantBits()); + builder.setCustomerIdLSB(user.getCustomerId().getId().getLeastSignificantBits()); + } + if (user.getFirstName() != null) { + builder.setFirstName(user.getFirstName()); + } + if (user.getLastName() != null) { + builder.setLastName(user.getLastName()); + } + if (user.getAdditionalInfo() != null) { + builder.setAdditionalInfo(JacksonUtil.toString(user.getAdditionalInfo())); + } + return builder.build(); + } + + @Override + public UserCredentialsUpdateMsg constructUserCredentialsUpdatedMsg(UserCredentials userCredentials) { + return UserCredentialsUpdateMsg.newBuilder() + .setUserIdMSB(userCredentials.getUserId().getId().getMostSignificantBits()) + .setUserIdLSB(userCredentials.getUserId().getId().getLeastSignificantBits()) + .setEnabled(userCredentials.isEnabled()) + .setPassword(userCredentials.getPassword()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorV2.java new file mode 100644 index 0000000000..0b4c592712 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/user/UserMsgConstructorV2.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.user; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.security.UserCredentials; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class UserMsgConstructorV2 extends BaseUserMsgConstructor { + + @Override + public UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user) { + return UserUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(user)) + .setIdMSB(user.getId().getId().getMostSignificantBits()) + .setIdLSB(user.getId().getId().getLeastSignificantBits()).build(); + } + + @Override + public UserCredentialsUpdateMsg constructUserCredentialsUpdatedMsg(UserCredentials userCredentials) { + return UserCredentialsUpdateMsg.newBuilder().setEntity(JacksonUtil.toString(userCredentials)).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/BaseWidgetMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/BaseWidgetMsgConstructor.java new file mode 100644 index 0000000000..6b4b3ac821 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/BaseWidgetMsgConstructor.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.widget; + +import org.thingsboard.server.common.data.id.WidgetTypeId; +import org.thingsboard.server.common.data.id.WidgetsBundleId; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg; +import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg; + +public abstract class BaseWidgetMsgConstructor implements WidgetMsgConstructor { + + @Override + public WidgetsBundleUpdateMsg constructWidgetsBundleDeleteMsg(WidgetsBundleId widgetsBundleId) { + return WidgetsBundleUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(widgetsBundleId.getId().getMostSignificantBits()) + .setIdLSB(widgetsBundleId.getId().getLeastSignificantBits()) + .build(); + } + + @Override + public WidgetTypeUpdateMsg constructWidgetTypeDeleteMsg(WidgetTypeId widgetTypeId) { + return WidgetTypeUpdateMsg.newBuilder() + .setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) + .setIdMSB(widgetTypeId.getId().getMostSignificantBits()) + .setIdLSB(widgetTypeId.getId().getLeastSignificantBits()) + .build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructor.java new file mode 100644 index 0000000000..544ba39b13 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructor.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.widget; + +import org.thingsboard.server.common.data.id.WidgetTypeId; +import org.thingsboard.server.common.data.id.WidgetsBundleId; +import org.thingsboard.server.common.data.widget.WidgetTypeDetails; +import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg; +import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg; +import org.thingsboard.server.service.edge.rpc.constructor.MsgConstructor; + +import java.util.List; + +public interface WidgetMsgConstructor extends MsgConstructor { + + WidgetsBundleUpdateMsg constructWidgetsBundleUpdateMsg(UpdateMsgType msgType, WidgetsBundle widgetsBundle, List widgets); + + WidgetsBundleUpdateMsg constructWidgetsBundleDeleteMsg(WidgetsBundleId widgetsBundleId); + + WidgetTypeUpdateMsg constructWidgetTypeUpdateMsg(UpdateMsgType msgType, WidgetTypeDetails widgetTypeDetails, EdgeVersion edgeVersion); + + WidgetTypeUpdateMsg constructWidgetTypeDeleteMsg(WidgetTypeId widgetTypeId); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorFactory.java new file mode 100644 index 0000000000..051b6ac4bf --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.widget; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.BaseMsgConstructorFactory; + +@Component +@TbCoreComponent +public class WidgetMsgConstructorFactory extends BaseMsgConstructorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorV1.java new file mode 100644 index 0000000000..1bc6857863 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorV1.java @@ -0,0 +1,112 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.widget; + +import com.google.protobuf.ByteString; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.widget.WidgetTypeDetails; +import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.dao.resource.ImageService; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg; +import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.utils.EdgeVersionUtils; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; + +@Component +@TbCoreComponent +public class WidgetMsgConstructorV1 extends BaseWidgetMsgConstructor { + + @Autowired + private ImageService imageService; + + @Override + public WidgetsBundleUpdateMsg constructWidgetsBundleUpdateMsg(UpdateMsgType msgType, WidgetsBundle widgetsBundle, List widgets) { + imageService.inlineImageForEdge(widgetsBundle); + WidgetsBundleUpdateMsg.Builder builder = WidgetsBundleUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(widgetsBundle.getId().getId().getMostSignificantBits()) + .setIdLSB(widgetsBundle.getId().getId().getLeastSignificantBits()) + .setTitle(widgetsBundle.getTitle()) + .setAlias(widgetsBundle.getAlias()); + if (widgetsBundle.getImage() != null) { + builder.setImage(ByteString.copyFrom(widgetsBundle.getImage().getBytes(StandardCharsets.UTF_8))); + } + if (widgetsBundle.getDescription() != null) { + builder.setDescription(widgetsBundle.getDescription()); + } + if (widgetsBundle.getOrder() != null) { + builder.setOrder(widgetsBundle.getOrder()); + } + if (widgetsBundle.getTenantId().equals(TenantId.SYS_TENANT_ID)) { + builder.setIsSystem(true); + } + builder.setWidgets(JacksonUtil.toString(widgets)); + return builder.build(); + } + + @Override + public WidgetTypeUpdateMsg constructWidgetTypeUpdateMsg(UpdateMsgType msgType, WidgetTypeDetails widgetTypeDetails, EdgeVersion edgeVersion) { + imageService.inlineImagesForEdge(widgetTypeDetails); + WidgetTypeUpdateMsg.Builder builder = WidgetTypeUpdateMsg.newBuilder() + .setMsgType(msgType) + .setIdMSB(widgetTypeDetails.getId().getId().getMostSignificantBits()) + .setIdLSB(widgetTypeDetails.getId().getId().getLeastSignificantBits()); + if (widgetTypeDetails.getFqn() != null) { + builder.setFqn(widgetTypeDetails.getFqn()); + if (widgetTypeDetails.getFqn().contains(".")) { + String[] aliases = widgetTypeDetails.getFqn().split("\\.", 2); + if (aliases.length == 2) { + builder.setBundleAlias(aliases[0]); + builder.setAlias(aliases[1]); + } + } + } + if (widgetTypeDetails.getName() != null) { + builder.setName(widgetTypeDetails.getName()); + } + if (widgetTypeDetails.getDescriptor() != null) { + builder.setDescriptorJson(JacksonUtil.toString(widgetTypeDetails.getDescriptor())); + } + if (widgetTypeDetails.getTenantId().equals(TenantId.SYS_TENANT_ID)) { + builder.setIsSystem(true); + } + if (widgetTypeDetails.getImage() != null) { + builder.setImage(widgetTypeDetails.getImage()); + } + if (widgetTypeDetails.getDescription() != null) { + if (EdgeVersionUtils.isEdgeVersionOlderThan(edgeVersion, EdgeVersion.V_3_6_0) && + widgetTypeDetails.getDescription().length() > 255) { + builder.setDescription(widgetTypeDetails.getDescription().substring(0, 254)); + } else { + builder.setDescription(widgetTypeDetails.getDescription()); + } + } + builder.setDeprecated(widgetTypeDetails.isDeprecated()); + if (widgetTypeDetails.getTags() != null) { + builder.addAllTags(Arrays.asList(widgetTypeDetails.getTags())); + } + return builder.build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorV2.java new file mode 100644 index 0000000000..aacc3c8399 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorV2.java @@ -0,0 +1,48 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.constructor.widget; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.widget.WidgetTypeDetails; +import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg; +import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import java.util.List; + +@Component +@TbCoreComponent +public class WidgetMsgConstructorV2 extends BaseWidgetMsgConstructor { + + @Override + public WidgetsBundleUpdateMsg constructWidgetsBundleUpdateMsg(UpdateMsgType msgType, WidgetsBundle widgetsBundle, List widgets) { + return WidgetsBundleUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(widgetsBundle)) + .setWidgets(JacksonUtil.toString(widgets)) + .setIdMSB(widgetsBundle.getId().getId().getMostSignificantBits()) + .setIdLSB(widgetsBundle.getId().getId().getLeastSignificantBits()).build(); + } + + @Override + public WidgetTypeUpdateMsg constructWidgetTypeUpdateMsg(UpdateMsgType msgType, WidgetTypeDetails widgetTypeDetails, EdgeVersion edgeVersion) { + return WidgetTypeUpdateMsg.newBuilder().setMsgType(msgType).setEntity(JacksonUtil.toString(widgetTypeDetails)) + .setIdMSB(widgetTypeDetails.getId().getId().getMostSignificantBits()) + .setIdLSB(widgetTypeDetails.getId().getId().getLeastSignificantBits()).build(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java index cefeb4c46a..a292c89e9c 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java @@ -15,146 +15,51 @@ */ package org.thingsboard.server.service.edge.rpc.fetch; -import com.datastax.oss.driver.api.core.uuid.Uuids; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import freemarker.template.Configuration; -import freemarker.template.Template; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.text.WordUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.EdgeUtils; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; -import org.thingsboard.server.common.data.id.AdminSettingsId; +import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.settings.AdminSettingsService; import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; @AllArgsConstructor @Slf4j public class AdminSettingsEdgeEventFetcher implements EdgeEventFetcher { private final AdminSettingsService adminSettingsService; - private final Configuration freemarkerConfig; - - private static final Pattern startPattern = Pattern.compile("

"); - private static final Pattern endPattern = Pattern.compile("
"); - - private static final List templatesNames = Arrays.asList( - "account.activated.ftl", - "account.lockout.ftl", - "activation.ftl", - "password.was.reset.ftl", - "reset.password.ftl", - "test.ftl"); - - // TODO: @voba fix format of next templates - // "state.disabled.ftl", - // "state.enabled.ftl", - // "state.warning.ftl", @Override public PageLink getPageLink(int pageSize) { return null; } - @Override - public PageData fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) throws Exception { - List result = new ArrayList<>(); - - AdminSettings systemMailSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail"); - result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, - EdgeEventActionType.UPDATED, null, JacksonUtil.OBJECT_MAPPER.valueToTree(systemMailSettings))); - - AdminSettings tenantMailSettings = convertToTenantAdminSettings(tenantId, systemMailSettings.getKey(), (ObjectNode) systemMailSettings.getJsonValue()); - result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, - EdgeEventActionType.UPDATED, null, JacksonUtil.OBJECT_MAPPER.valueToTree(tenantMailSettings))); - - AdminSettings systemMailTemplates = loadMailTemplates(); - result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, - EdgeEventActionType.UPDATED, null, JacksonUtil.OBJECT_MAPPER.valueToTree(systemMailTemplates))); - - AdminSettings tenantMailTemplates = convertToTenantAdminSettings(tenantId, systemMailTemplates.getKey(), (ObjectNode) systemMailTemplates.getJsonValue()); - result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, - EdgeEventActionType.UPDATED, null, JacksonUtil.OBJECT_MAPPER.valueToTree(tenantMailTemplates))); + public PageData fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) { + List result = fetchAdminSettingsForKeys(tenantId, edge.getId(), List.of("general", "mail", "connectivity", "jwt")); // return PageData object to be in sync with other fetchers return new PageData<>(result, 1, result.size(), false); } - private AdminSettings loadMailTemplates() throws Exception { - Map mailTemplates = new HashMap<>(); - for (String templatesName : templatesNames) { - Template template = freemarkerConfig.getTemplate(templatesName); - if (template != null) { - String name = validateName(template.getName()); - Map mailTemplate = getMailTemplateFromFile(template.toString()); - if (mailTemplate != null) { - mailTemplates.put(name, mailTemplate); - } else { - log.error("Can't load mail template from file {}", template.getName()); - } - } - } - AdminSettings adminSettings = new AdminSettings(); - adminSettings.setId(new AdminSettingsId(Uuids.timeBased())); - adminSettings.setKey("mailTemplates"); - adminSettings.setJsonValue(JacksonUtil.OBJECT_MAPPER.convertValue(mailTemplates, JsonNode.class)); - return adminSettings; - } - - private Map getMailTemplateFromFile(String stringTemplate) { - Map mailTemplate = new HashMap<>(); - Matcher start = startPattern.matcher(stringTemplate); - Matcher end = endPattern.matcher(stringTemplate); - if (start.find() && end.find()) { - String body = StringUtils.substringBetween(stringTemplate, start.group(), end.group()).replaceAll("\t", ""); - String subject = StringUtils.substringBetween(body, "

", "

"); - mailTemplate.put("subject", subject); - mailTemplate.put("body", body); - } else { - return null; - } - return mailTemplate; - } - - private String validateName(String name) throws Exception { - StringBuilder nameBuilder = new StringBuilder(); - name = name.replace(".ftl", ""); - String[] nameParts = name.split("\\."); - if (nameParts.length >= 1) { - nameBuilder.append(nameParts[0]); - for (int i = 1; i < nameParts.length; i++) { - String word = WordUtils.capitalize(nameParts[i]); - nameBuilder.append(word); + private List fetchAdminSettingsForKeys(TenantId tenantId, EdgeId edgeId, List keys) { + List result = new ArrayList<>(); + for (String key : keys) { + AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, key); + if (adminSettings != null) { + result.add(EdgeUtils.constructEdgeEvent(tenantId, edgeId, EdgeEventType.ADMIN_SETTINGS, + EdgeEventActionType.UPDATED, null, JacksonUtil.valueToTree(adminSettings))); } - return nameBuilder.toString(); - } else { - throw new Exception("Error during filename validation"); } - } - - private AdminSettings convertToTenantAdminSettings(TenantId tenantId, String key, ObjectNode jsonValue) { - AdminSettings tenantMailSettings = new AdminSettings(); - tenantMailSettings.setTenantId(tenantId); - jsonValue.put("useSystemMailSettings", true); - tenantMailSettings.setJsonValue(jsonValue); - tenantMailSettings.setKey(key); - return tenantMailSettings; + return result; } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BaseWidgetTypesEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BaseWidgetTypesEdgeEventFetcher.java new file mode 100644 index 0000000000..05d9071ed3 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/BaseWidgetTypesEdgeEventFetcher.java @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.fetch; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.EdgeUtils; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.widget.WidgetTypeDetails; +import org.thingsboard.server.common.data.widget.WidgetTypeInfo; +import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.dao.widget.WidgetTypeService; +import org.thingsboard.server.dao.widget.WidgetsBundleService; + +@Slf4j +@AllArgsConstructor +public abstract class BaseWidgetTypesEdgeEventFetcher extends BasePageableEdgeEventFetcher { + + protected final WidgetTypeService widgetTypeService; + + @Override + PageData fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) { + return findWidgetTypes(tenantId, pageLink); + } + + @Override + EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, WidgetTypeInfo widgetTypeInfo) { + return EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.WIDGET_TYPE, + EdgeEventActionType.ADDED, widgetTypeInfo.getId(), null); + } + + protected abstract PageData findWidgetTypes(TenantId tenantId, PageLink pageLink); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/DefaultProfilesEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/DefaultProfilesEdgeEventFetcher.java new file mode 100644 index 0000000000..1b4897dfff --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/DefaultProfilesEdgeEventFetcher.java @@ -0,0 +1,62 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.fetch; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.EdgeUtils; +import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.asset.AssetProfileService; +import org.thingsboard.server.dao.device.DeviceProfileService; + +import java.util.ArrayList; +import java.util.List; + +@AllArgsConstructor +@Slf4j +public class DefaultProfilesEdgeEventFetcher implements EdgeEventFetcher { + + private final DeviceProfileService deviceProfileService; + private final AssetProfileService assetProfileService; + + @Override + public PageLink getPageLink(int pageSize) { + return null; + } + + @Override + public PageData fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) { + List result = new ArrayList<>(); + + DeviceProfile deviceProfile = deviceProfileService.findDefaultDeviceProfile(tenantId); + result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE_PROFILE, + EdgeEventActionType.ADDED, deviceProfile.getId(), null)); + + AssetProfile assetProfile = assetProfileService.findDefaultAssetProfile(tenantId); + result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ASSET_PROFILE, + EdgeEventActionType.ADDED, assetProfile.getId(), null)); + + return new PageData<>(result, 1, result.size(), false); + } +} \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/GeneralEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/GeneralEdgeEventFetcher.java index 327184e6a9..24008ece09 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/GeneralEdgeEventFetcher.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/GeneralEdgeEventFetcher.java @@ -16,19 +16,27 @@ package org.thingsboard.server.service.edge.rpc.fetch; import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.dao.edge.EdgeEventService; @AllArgsConstructor +@Slf4j public class GeneralEdgeEventFetcher implements EdgeEventFetcher { private final Long queueStartTs; + private Long seqIdStart; + @Getter + private Long seqIdEnd; + @Getter + private boolean seqIdNewCycleStarted; + private Long maxReadRecordsCount; private final EdgeEventService edgeEventService; @Override @@ -37,13 +45,32 @@ public class GeneralEdgeEventFetcher implements EdgeEventFetcher { pageSize, 0, null, - new SortOrder("createdTime", SortOrder.Direction.ASC), + null, queueStartTs, - null); + System.currentTimeMillis()); } @Override public PageData fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) { - return edgeEventService.findEdgeEvents(tenantId, edge.getId(), (TimePageLink) pageLink, true); + try { + PageData edgeEvents = edgeEventService.findEdgeEvents(tenantId, edge.getId(), seqIdStart, seqIdEnd, (TimePageLink) pageLink); + if (edgeEvents.getData().isEmpty()) { + this.seqIdEnd = Math.max(this.maxReadRecordsCount, seqIdStart - this.maxReadRecordsCount); + edgeEvents = edgeEventService.findEdgeEvents(tenantId, edge.getId(), 0L, seqIdEnd, (TimePageLink) pageLink); + if (edgeEvents.getData().stream().anyMatch(ee -> ee.getSeqId() < seqIdStart)) { + log.info("[{}] seqId column of edge_event table started new cycle [{}]", tenantId, edge.getId()); + this.seqIdNewCycleStarted = true; + this.seqIdStart = 0L; + } else { + edgeEvents = new PageData<>(); + log.warn("[{}] unexpected edge notification message received. " + + "no new events found and seqId column of edge_event table doesn't started new cycle [{}]", tenantId, edge.getId()); + } + } + return edgeEvents; + } catch (Exception e) { + log.error("[{}] failed to find edge events [{}]", tenantId, edge.getId()); + } + return new PageData<>(); } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/SystemWidgetTypesEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/SystemWidgetTypesEdgeEventFetcher.java new file mode 100644 index 0000000000..4bb4d1fb06 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/SystemWidgetTypesEdgeEventFetcher.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.fetch; + +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.widget.DeprecatedFilter; +import org.thingsboard.server.common.data.widget.WidgetTypeInfo; +import org.thingsboard.server.dao.widget.WidgetTypeService; + +@Slf4j +public class SystemWidgetTypesEdgeEventFetcher extends BaseWidgetTypesEdgeEventFetcher { + + public SystemWidgetTypesEdgeEventFetcher(WidgetTypeService widgetTypeService) { + super(widgetTypeService); + } + + @Override + protected PageData findWidgetTypes(TenantId tenantId, PageLink pageLink) { + return widgetTypeService.findSystemWidgetTypesByPageLink(tenantId, false, DeprecatedFilter.ALL, null, pageLink); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/SystemWidgetsBundlesEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/SystemWidgetsBundlesEdgeEventFetcher.java index 87cffcee9f..d1ee0723dd 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/SystemWidgetsBundlesEdgeEventFetcher.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/SystemWidgetsBundlesEdgeEventFetcher.java @@ -31,6 +31,6 @@ public class SystemWidgetsBundlesEdgeEventFetcher extends BaseWidgetsBundlesEdge @Override protected PageData findWidgetsBundles(TenantId tenantId, PageLink pageLink) { - return widgetsBundleService.findSystemWidgetsBundlesByPageLink(tenantId, pageLink); + return widgetsBundleService.findSystemWidgetsBundlesByPageLink(tenantId, false, pageLink); } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantEdgeEventFetcher.java new file mode 100644 index 0000000000..1e9f81be0f --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantEdgeEventFetcher.java @@ -0,0 +1,51 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.fetch; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.EdgeUtils; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.tenant.TenantService; + +import java.util.List; + +@AllArgsConstructor +@Slf4j +public class TenantEdgeEventFetcher extends BasePageableEdgeEventFetcher { + + private final TenantService tenantService; + + @Override + PageData fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) { + Tenant tenant = tenantService.findTenantById(tenantId); + // returns PageData object to be in sync with other fetchers + return new PageData<>(List.of(tenant), 1, 1, false); + } + + @Override + EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, Tenant entity) { + return EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.TENANT, + EdgeEventActionType.UPDATED, entity.getId(), null); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantResourcesEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantResourcesEdgeEventFetcher.java new file mode 100644 index 0000000000..61a6ce4e6e --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantResourcesEdgeEventFetcher.java @@ -0,0 +1,47 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.fetch; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.EdgeUtils; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.resource.ResourceService; + +@AllArgsConstructor +@Slf4j +public class TenantResourcesEdgeEventFetcher extends BasePageableEdgeEventFetcher { + + private final ResourceService resourceService; + + @Override + PageData fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) { + return resourceService.findAllTenantResources(tenantId, pageLink); + } + + @Override + EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, TbResource tbResource) { + return EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.TB_RESOURCE, + EdgeEventActionType.ADDED, tbResource.getId(), null); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantWidgetTypesEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantWidgetTypesEdgeEventFetcher.java new file mode 100644 index 0000000000..85e23427d3 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantWidgetTypesEdgeEventFetcher.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.fetch; + +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.widget.DeprecatedFilter; +import org.thingsboard.server.common.data.widget.WidgetTypeInfo; +import org.thingsboard.server.dao.widget.WidgetTypeService; + +@Slf4j +public class TenantWidgetTypesEdgeEventFetcher extends BaseWidgetTypesEdgeEventFetcher { + + public TenantWidgetTypesEdgeEventFetcher(WidgetTypeService widgetTypeService) { + super(widgetTypeService); + } + @Override + protected PageData findWidgetTypes(TenantId tenantId, PageLink pageLink) { + return widgetTypeService.findTenantWidgetTypesByTenantIdAndPageLink(tenantId, false, DeprecatedFilter.ALL, null, pageLink); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantWidgetsBundlesEdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantWidgetsBundlesEdgeEventFetcher.java index ffddb65315..8f69a904e8 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantWidgetsBundlesEdgeEventFetcher.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/TenantWidgetsBundlesEdgeEventFetcher.java @@ -28,6 +28,7 @@ public class TenantWidgetsBundlesEdgeEventFetcher extends BaseWidgetsBundlesEdge public TenantWidgetsBundlesEdgeEventFetcher(WidgetsBundleService widgetsBundleService) { super(widgetsBundleService); } + @Override protected PageData findWidgetsBundles(TenantId tenantId, PageLink pageLink) { return widgetsBundleService.findTenantWidgetsBundlesByTenantId(tenantId, pageLink); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java index ac7c791338..382491ca26 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java @@ -21,10 +21,18 @@ import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEventActionType; @@ -40,10 +48,17 @@ import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgDataType; +import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.asset.AssetProfileService; import org.thingsboard.server.dao.asset.AssetService; @@ -55,47 +70,57 @@ import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeService; +import org.thingsboard.server.dao.edge.EdgeSynchronizationManager; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TenantProfileService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetsBundleService; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.queue.TbQueueCallback; +import org.thingsboard.server.queue.TbQueueMsgMetadata; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; -import org.thingsboard.server.service.edge.rpc.constructor.AdminSettingsMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.AlarmMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.AssetMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.AssetProfileMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.CustomerMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.DashboardMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.DeviceMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.DeviceProfileMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.EdgeMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.EntityDataMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.EntityViewMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.OtaPackageMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.QueueMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.RelationMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.RuleChainMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.UserMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.WidgetTypeMsgConstructor; -import org.thingsboard.server.service.edge.rpc.constructor.WidgetsBundleMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.alarm.AlarmMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.asset.AssetMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.customer.CustomerMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.dashboard.DashboardMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.device.DeviceMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.edge.EdgeMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.entityview.EntityViewMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.ota.OtaPackageMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.queue.QueueMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.relation.RelationMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.resource.ResourceMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.settings.AdminSettingsMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.telemetry.EntityDataMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.tenant.TenantMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.user.UserMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.widget.WidgetMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.processor.alarm.AlarmEdgeProcessorFactory; +import org.thingsboard.server.service.edge.rpc.processor.asset.AssetEdgeProcessorFactory; +import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewProcessorFactory; import org.thingsboard.server.service.entitiy.TbNotificationEntityService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.profile.TbAssetProfileCache; import org.thingsboard.server.service.profile.TbDeviceProfileCache; +import org.thingsboard.server.service.state.DefaultDeviceStateService; import org.thingsboard.server.service.state.DeviceStateService; import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -104,6 +129,7 @@ import java.util.concurrent.locks.ReentrantLock; public abstract class BaseEdgeProcessor { protected static final Lock deviceCreationLock = new ReentrantLock(); + protected static final Lock assetCreationLock = new ReentrantLock(); protected static final int DEFAULT_PAGE_SIZE = 100; @@ -140,6 +166,9 @@ public abstract class BaseEdgeProcessor { @Autowired protected TenantService tenantService; + @Autowired + protected TenantProfileService tenantProfileService; + @Autowired protected EdgeService edgeService; @@ -188,6 +217,9 @@ public abstract class BaseEdgeProcessor { @Autowired protected PartitionService partitionService; + @Autowired + protected ResourceService resourceService; + @Autowired @Lazy protected TbQueueProducerProvider producerProvider; @@ -195,6 +227,24 @@ public abstract class BaseEdgeProcessor { @Autowired protected DataValidator deviceValidator; + @Autowired + protected DataValidator deviceProfileValidator; + + @Autowired + protected DataValidator assetValidator; + + @Autowired + protected DataValidator assetProfileValidator; + + @Autowired + protected DataValidator dashboardValidator; + + @Autowired + protected DataValidator entityViewValidator; + + @Autowired + protected DataValidator resourceValidator; + @Autowired protected EdgeMsgConstructor edgeMsgConstructor; @@ -202,62 +252,126 @@ public abstract class BaseEdgeProcessor { protected EntityDataMsgConstructor entityDataMsgConstructor; @Autowired - protected RuleChainMsgConstructor ruleChainMsgConstructor; + protected RuleChainMsgConstructorFactory ruleChainMsgConstructorFactory; + + @Autowired + protected AlarmMsgConstructorFactory alarmMsgConstructorFactory; + + @Autowired + protected DeviceMsgConstructorFactory deviceMsgConstructorFactory; + + @Autowired + protected AssetMsgConstructorFactory assetMsgConstructorFactory; @Autowired - protected AlarmMsgConstructor alarmMsgConstructor; + protected EntityViewMsgConstructorFactory entityViewMsgConstructorFactory; @Autowired - protected DeviceMsgConstructor deviceMsgConstructor; + protected DashboardMsgConstructorFactory dashboardMsgConstructorFactory; @Autowired - protected AssetMsgConstructor assetMsgConstructor; + protected RelationMsgConstructorFactory relationMsgConstructorFactory; @Autowired - protected EntityViewMsgConstructor entityViewMsgConstructor; + protected UserMsgConstructorFactory userMsgConstructorFactory; @Autowired - protected DashboardMsgConstructor dashboardMsgConstructor; + protected CustomerMsgConstructorFactory customerMsgConstructorFactory; @Autowired - protected RelationMsgConstructor relationMsgConstructor; + protected TenantMsgConstructorFactory tenantMsgConstructorFactory; @Autowired - protected UserMsgConstructor userMsgConstructor; + protected WidgetMsgConstructorFactory widgetMsgConstructorFactory; @Autowired - protected CustomerMsgConstructor customerMsgConstructor; + protected AdminSettingsMsgConstructorFactory adminSettingsMsgConstructorFactory; @Autowired - protected DeviceProfileMsgConstructor deviceProfileMsgConstructor; + protected OtaPackageMsgConstructorFactory otaPackageMsgConstructorFactory; @Autowired - protected AssetProfileMsgConstructor assetProfileMsgConstructor; + protected QueueMsgConstructorFactory queueMsgConstructorFactory; @Autowired - protected WidgetsBundleMsgConstructor widgetsBundleMsgConstructor; + protected ResourceMsgConstructorFactory resourceMsgConstructorFactory; @Autowired - protected WidgetTypeMsgConstructor widgetTypeMsgConstructor; + protected AlarmEdgeProcessorFactory alarmEdgeProcessorFactory; @Autowired - protected AdminSettingsMsgConstructor adminSettingsMsgConstructor; + protected AssetEdgeProcessorFactory assetEdgeProcessorFactory; @Autowired - protected OtaPackageMsgConstructor otaPackageMsgConstructor; + protected EntityViewProcessorFactory entityViewProcessorFactory; @Autowired - protected QueueMsgConstructor queueMsgConstructor; + protected EdgeSynchronizationManager edgeSynchronizationManager; @Autowired protected DbCallbackExecutorService dbCallbackExecutorService; protected ListenableFuture saveEdgeEvent(TenantId tenantId, - EdgeId edgeId, - EdgeEventType type, - EdgeEventActionType action, - EntityId entityId, - JsonNode body) { + EdgeId edgeId, + EdgeEventType type, + EdgeEventActionType action, + EntityId entityId, + JsonNode body) { + ListenableFuture> future = + attributesService.find(tenantId, edgeId, DataConstants.SERVER_SCOPE, DefaultDeviceStateService.ACTIVITY_STATE); + return Futures.transformAsync(future, activeOpt -> { + if (activeOpt.isEmpty()) { + log.trace("Edge is not activated. Skipping event. tenantId [{}], edgeId [{}], type[{}], " + + "action [{}], entityId [{}], body [{}]", + tenantId, edgeId, type, action, entityId, body); + return Futures.immediateFuture(null); + } + if (activeOpt.get().getBooleanValue().isPresent() && activeOpt.get().getBooleanValue().get()) { + return doSaveEdgeEvent(tenantId, edgeId, type, action, entityId, body); + } else { + if (doSaveIfEdgeIsOffline(type, action)) { + return doSaveEdgeEvent(tenantId, edgeId, type, action, entityId, body); + } else { + log.trace("Edge is not active at the moment. Skipping event. tenantId [{}], edgeId [{}], type[{}], " + + "action [{}], entityId [{}], body [{}]", + tenantId, edgeId, type, action, entityId, body); + return Futures.immediateFuture(null); + } + } + }, dbCallbackExecutorService); + } + + private boolean doSaveIfEdgeIsOffline(EdgeEventType type, + EdgeEventActionType action) { + switch (action) { + case TIMESERIES_UPDATED: + case ALARM_ACK: + case ALARM_CLEAR: + case ALARM_ASSIGNED: + case ALARM_UNASSIGNED: + case CREDENTIALS_REQUEST: + return true; + } + switch (type) { + case ALARM: + case RULE_CHAIN: + case RULE_CHAIN_METADATA: + case USER: + case CUSTOMER: + case TENANT: + case TENANT_PROFILE: + case WIDGETS_BUNDLE: + case WIDGET_TYPE: + case ADMIN_SETTINGS: + case OTA_PACKAGE: + case QUEUE: + case RELATION: + return true; + } + return false; + } + + private ListenableFuture doSaveEdgeEvent(TenantId tenantId, EdgeId edgeId, EdgeEventType type, EdgeEventActionType action, EntityId entityId, JsonNode body) { log.debug("Pushing event to edge queue. tenantId [{}], edgeId [{}], type[{}], " + "action [{}], entityId [{}], body [{}]", tenantId, edgeId, type, action, entityId, body); @@ -270,7 +384,9 @@ public abstract class BaseEdgeProcessor { }, dbCallbackExecutorService); } - protected ListenableFuture processActionForAllEdges(TenantId tenantId, EdgeEventType type, EdgeEventActionType actionType, EntityId entityId) { + protected ListenableFuture processActionForAllEdges(TenantId tenantId, EdgeEventType type, + EdgeEventActionType actionType, EntityId entityId, + EdgeId sourceEdgeId) { List> futures = new ArrayList<>(); if (TenantId.SYS_TENANT_ID.equals(tenantId)) { PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE); @@ -278,21 +394,22 @@ public abstract class BaseEdgeProcessor { do { tenantsIds = tenantService.findTenantsIds(pageLink); for (TenantId tenantId1 : tenantsIds.getData()) { - futures.addAll(processActionForAllEdgesByTenantId(tenantId1, type, actionType, entityId, null)); + futures.addAll(processActionForAllEdgesByTenantId(tenantId1, type, actionType, entityId, null, sourceEdgeId)); } pageLink = pageLink.nextPageLink(); } while (tenantsIds.hasNext()); } else { - futures = processActionForAllEdgesByTenantId(tenantId, type, actionType, entityId, null); + futures = processActionForAllEdgesByTenantId(tenantId, type, actionType, entityId, null, sourceEdgeId); } return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService); } - protected List> processActionForAllEdgesByTenantId(TenantId tenantId, - EdgeEventType type, - EdgeEventActionType actionType, - EntityId entityId, - JsonNode body) { + private List> processActionForAllEdgesByTenantId(TenantId tenantId, + EdgeEventType type, + EdgeEventActionType actionType, + EntityId entityId, + JsonNode body, + EdgeId sourceEdgeId) { PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE); PageData pageData; List> futures = new ArrayList<>(); @@ -300,7 +417,9 @@ public abstract class BaseEdgeProcessor { pageData = edgeService.findEdgesByTenantId(tenantId, pageLink); if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { for (Edge edge : pageData.getData()) { - futures.add(saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, body)); + if (!edge.getId().equals(sourceEdgeId)) { + futures.add(saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, body)); + } } if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -340,48 +459,64 @@ public abstract class BaseEdgeProcessor { } } - protected ListenableFuture processEntityNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { - EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()); + public ListenableFuture processEntityNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); - EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type, - new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); - EdgeId edgeId = safeGetEdgeId(edgeNotificationMsg); - switch (actionType) { - case ADDED: - case UPDATED: - case CREDENTIALS_UPDATED: - case ASSIGNED_TO_CUSTOMER: - case UNASSIGNED_FROM_CUSTOMER: - case DELETED: - if (edgeId != null) { - return saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null); - } else { - return pushNotificationToAllRelatedEdges(tenantId, entityId, type, actionType); - } - case ASSIGNED_TO_EDGE: - case UNASSIGNED_FROM_EDGE: - ListenableFuture future = saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null); - return Futures.transformAsync(future, unused -> { - if (type.equals(EdgeEventType.RULE_CHAIN)) { - return updateDependentRuleChains(tenantId, new RuleChainId(entityId.getId()), edgeId); + EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()); + EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); + EdgeId originatorEdgeId = safeGetEdgeId(edgeNotificationMsg.getOriginatorEdgeIdMSB(), edgeNotificationMsg.getOriginatorEdgeIdLSB()); + if (type.isAllEdgesRelated()) { + return processEntityNotificationForAllEdges(tenantId, type, actionType, entityId, originatorEdgeId); + } else { + JsonNode body = JacksonUtil.toJsonNode(edgeNotificationMsg.getBody()); + EdgeId edgeId = safeGetEdgeId(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()); + switch (actionType) { + case UPDATED: + case CREDENTIALS_UPDATED: + case ASSIGNED_TO_CUSTOMER: + case UNASSIGNED_FROM_CUSTOMER: + if (edgeId != null) { + return saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, body); + } else { + return processNotificationToRelatedEdges(tenantId, entityId, type, actionType, originatorEdgeId); + } + case DELETED: + EdgeEventActionType deleted = EdgeEventActionType.DELETED; + if (edgeId != null) { + return saveEdgeEvent(tenantId, edgeId, type, deleted, entityId, body); + } else { + return Futures.transform(Futures.allAsList(processActionForAllEdgesByTenantId(tenantId, type, deleted, entityId, body, originatorEdgeId)), + voids -> null, dbCallbackExecutorService); + } + case ASSIGNED_TO_EDGE: + case UNASSIGNED_FROM_EDGE: + if (originatorEdgeId == null) { + ListenableFuture future = saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, body); + return Futures.transformAsync(future, unused -> { + if (type.equals(EdgeEventType.RULE_CHAIN)) { + return updateDependentRuleChains(tenantId, new RuleChainId(entityId.getId()), edgeId); + } else { + return Futures.immediateFuture(null); + } + }, dbCallbackExecutorService); } else { return Futures.immediateFuture(null); } - }, dbCallbackExecutorService); - default: - return Futures.immediateFuture(null); + default: + return Futures.immediateFuture(null); + } } } - private EdgeId safeGetEdgeId(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { - if (edgeNotificationMsg.getEdgeIdMSB() != 0 && edgeNotificationMsg.getEdgeIdLSB() != 0) { - return new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB())); + protected EdgeId safeGetEdgeId(long edgeIdMSB, long edgeIdLSB) { + if (edgeIdMSB != 0 && edgeIdLSB != 0) { + return new EdgeId(new UUID(edgeIdMSB, edgeIdLSB)); } else { return null; } } - private ListenableFuture pushNotificationToAllRelatedEdges(TenantId tenantId, EntityId entityId, EdgeEventType type, EdgeEventActionType actionType) { + private ListenableFuture processNotificationToRelatedEdges(TenantId tenantId, EntityId entityId, EdgeEventType type, + EdgeEventActionType actionType, EdgeId sourceEdgeId) { PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE); PageData pageData; List> futures = new ArrayList<>(); @@ -389,7 +524,9 @@ public abstract class BaseEdgeProcessor { pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId, pageLink); if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { for (EdgeId relatedEdgeId : pageData.getData()) { - futures.add(saveEdgeEvent(tenantId, relatedEdgeId, type, actionType, entityId, null)); + if (!relatedEdgeId.equals(sourceEdgeId)) { + futures.add(saveEdgeEvent(tenantId, relatedEdgeId, type, actionType, entityId, null)); + } } if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -432,16 +569,13 @@ public abstract class BaseEdgeProcessor { return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService); } - protected ListenableFuture processEntityNotificationForAllEdges(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { - EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()); - EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); - EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); + private ListenableFuture processEntityNotificationForAllEdges(TenantId tenantId, EdgeEventType type, EdgeEventActionType actionType, EntityId entityId, EdgeId sourceEdgeId) { switch (actionType) { case ADDED: case UPDATED: case DELETED: case CREDENTIALS_UPDATED: // used by USER entity - return processActionForAllEdges(tenantId, type, actionType, entityId); + return processActionForAllEdges(tenantId, type, actionType, entityId, sourceEdgeId); default: return Futures.immediateFuture(null); } @@ -508,4 +642,92 @@ public abstract class BaseEdgeProcessor { return false; } } + + protected void createRelationFromEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId) { + EntityRelation relation = new EntityRelation(); + relation.setFrom(edgeId); + relation.setTo(entityId); + relation.setTypeGroup(RelationTypeGroup.COMMON); + relation.setType(EntityRelation.EDGE_TYPE); + relationService.saveRelation(tenantId, relation); + } + + protected TbMsgMetaData getEdgeActionTbMsgMetaData(Edge edge, CustomerId customerId) { + TbMsgMetaData metaData = new TbMsgMetaData(); + metaData.putValue("edgeId", edge.getId().toString()); + metaData.putValue("edgeName", edge.getName()); + if (customerId != null && !customerId.isNullUid()) { + metaData.putValue("customerId", customerId.toString()); + } + return metaData; + } + + protected void pushEntityEventToRuleEngine(TenantId tenantId, EntityId entityId, CustomerId customerId, + TbMsgType msgType, String msgData, TbMsgMetaData metaData) { + TbMsg tbMsg = TbMsg.newMsg(msgType, entityId, customerId, metaData, TbMsgDataType.JSON, msgData); + tbClusterService.pushMsgToRuleEngine(tenantId, entityId, tbMsg, new TbQueueCallback() { + @Override + public void onSuccess(TbQueueMsgMetadata metadata) { + log.debug("[{}] Successfully send ENTITY_CREATED EVENT to rule engine [{}]", tenantId, msgData); + } + + @Override + public void onFailure(Throwable t) { + log.warn("[{}] Failed to send ENTITY_CREATED EVENT to rule engine [{}]", tenantId, msgData, t); + } + }); + } + + protected AssetProfile checkIfAssetProfileDefaultFieldsAssignedToEdge(TenantId tenantId, EdgeId edgeId, AssetProfile assetProfile, EdgeVersion edgeVersion) { + switch (edgeVersion) { + case V_3_3_3: + case V_3_3_0: + case V_3_4_0: + if (assetProfile.getDefaultDashboardId() != null + && isEntityNotAssignedToEdge(tenantId, assetProfile.getDefaultDashboardId(), edgeId)) { + assetProfile.setDefaultDashboardId(null); + } + if (assetProfile.getDefaultEdgeRuleChainId() != null + && isEntityNotAssignedToEdge(tenantId, assetProfile.getDefaultEdgeRuleChainId(), edgeId)) { + assetProfile.setDefaultEdgeRuleChainId(null); + } + break; + } + return assetProfile; + } + + protected DeviceProfile checkIfDeviceProfileDefaultFieldsAssignedToEdge(TenantId tenantId, EdgeId edgeId, DeviceProfile deviceProfile, EdgeVersion edgeVersion) { + switch (edgeVersion) { + case V_3_3_3: + case V_3_3_0: + case V_3_4_0: + if (deviceProfile.getDefaultDashboardId() != null + && isEntityNotAssignedToEdge(tenantId, deviceProfile.getDefaultDashboardId(), edgeId)) { + deviceProfile.setDefaultDashboardId(null); + } + if (deviceProfile.getDefaultEdgeRuleChainId() != null + && isEntityNotAssignedToEdge(tenantId, deviceProfile.getDefaultEdgeRuleChainId(), edgeId)) { + deviceProfile.setDefaultEdgeRuleChainId(null); + } + break; + } + return deviceProfile; + } + + private boolean isEntityNotAssignedToEdge(TenantId tenantId, EntityId entityId, EdgeId edgeId) { + PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE); + PageData pageData; + do { + pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId, pageLink); + if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { + if (pageData.getData().contains(edgeId)) { + return false; + } + if (pageData.hasNext()) { + pageLink = pageLink.nextPageLink(); + } + } + } while (pageData != null && pageData.hasNext()); + return true; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorFactory.java new file mode 100644 index 0000000000..157bd944ff --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorFactory.java @@ -0,0 +1,46 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public abstract class BaseEdgeProcessorFactory { + + @Autowired + protected T v1Processor; + + @Autowired + protected U v2Processor; + + public EdgeProcessor getProcessorByEdgeVersion(EdgeVersion edgeVersion) { + switch (edgeVersion) { + case V_3_3_0: + case V_3_3_3: + case V_3_4_0: + case V_3_6_0: + case V_3_6_1: + return v1Processor; + case V_3_6_2: + default: + return v2Processor; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/EdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/EdgeProcessor.java new file mode 100644 index 0000000000..183f977a52 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/EdgeProcessor.java @@ -0,0 +1,19 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor; + +public interface EdgeProcessor { +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java index 189d69e68d..a16e4833a0 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessor.java @@ -15,11 +15,10 @@ */ package org.thingsboard.server.service.edge.rpc.processor.alarm; -import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.alarm.Alarm; @@ -28,26 +27,37 @@ import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.queue.util.TbCoreComponent; import java.util.ArrayList; import java.util.List; import java.util.UUID; -@Component @Slf4j -@TbCoreComponent -public class AlarmEdgeProcessor extends BaseAlarmProcessor { +public abstract class AlarmEdgeProcessor extends BaseAlarmProcessor implements AlarmProcessor { - public DownlinkMsg convertAlarmEventToDownlink(EdgeEvent edgeEvent) { - AlarmUpdateMsg alarmUpdateMsg = - convertAlarmEventToAlarmMsg(edgeEvent.getTenantId(), edgeEvent.getEntityId(), edgeEvent.getAction(), edgeEvent.getBody()); + @Override + public ListenableFuture processAlarmMsgFromEdge(TenantId tenantId, EdgeId edgeId, AlarmUpdateMsg alarmUpdateMsg) { + log.trace("[{}] processAlarmMsgFromEdge [{}]", tenantId, alarmUpdateMsg); + try { + edgeSynchronizationManager.getEdgeId().set(edgeId); + return processAlarmMsg(tenantId, alarmUpdateMsg); + } finally { + edgeSynchronizationManager.getEdgeId().remove(); + } + } + + @Override + public DownlinkMsg convertAlarmEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { + AlarmUpdateMsg alarmUpdateMsg = convertAlarmEventToAlarmMsg(edgeEvent.getTenantId(), edgeEvent.getEntityId(), + edgeEvent.getAction(), edgeEvent.getBody(), edgeVersion); if (alarmUpdateMsg != null) { return DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) @@ -57,14 +67,19 @@ public class AlarmEdgeProcessor extends BaseAlarmProcessor { return null; } - public ListenableFuture processAlarmNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) throws JsonProcessingException { + public ListenableFuture processAlarmNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()); AlarmId alarmId = new AlarmId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); + EdgeId originatorEdgeId = safeGetEdgeId(edgeNotificationMsg.getOriginatorEdgeIdMSB(), edgeNotificationMsg.getOriginatorEdgeIdLSB()); switch (actionType) { case DELETED: - EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB())); - Alarm deletedAlarm = JacksonUtil.OBJECT_MAPPER.readValue(edgeNotificationMsg.getBody(), Alarm.class); - return saveEdgeEvent(tenantId, edgeId, EdgeEventType.ALARM, actionType, alarmId, JacksonUtil.OBJECT_MAPPER.valueToTree(deletedAlarm)); + Alarm deletedAlarm = JacksonUtil.fromString(edgeNotificationMsg.getBody(), Alarm.class); + if (deletedAlarm == null) { + return Futures.immediateFuture(null); + } + List> delFutures = pushEventToAllRelatedEdges(tenantId, deletedAlarm.getOriginator(), + alarmId, actionType, JacksonUtil.valueToTree(deletedAlarm), originatorEdgeId); + return Futures.transform(Futures.allAsList(delFutures), voids -> null, dbCallbackExecutorService); default: ListenableFuture alarmFuture = alarmService.findAlarmByIdAsync(tenantId, alarmId); return Futures.transformAsync(alarmFuture, alarm -> { @@ -75,28 +90,35 @@ public class AlarmEdgeProcessor extends BaseAlarmProcessor { if (type == null) { return Futures.immediateFuture(null); } - PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE); - PageData pageData; - List> futures = new ArrayList<>(); - do { - pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator(), pageLink); - if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { - for (EdgeId relatedEdgeId : pageData.getData()) { - futures.add(saveEdgeEvent(tenantId, - relatedEdgeId, - EdgeEventType.ALARM, - EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()), - alarmId, - null)); - } - if (pageData.hasNext()) { - pageLink = pageLink.nextPageLink(); - } - } - } while (pageData != null && pageData.hasNext()); + List> futures = pushEventToAllRelatedEdges(tenantId, alarm.getOriginator(), + alarmId, actionType, null, originatorEdgeId); return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService); }, dbCallbackExecutorService); } } + private List> pushEventToAllRelatedEdges(TenantId tenantId, EntityId originatorId, AlarmId alarmId, EdgeEventActionType actionType, JsonNode body, EdgeId sourceEdgeId) { + PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE); + PageData pageData; + List> futures = new ArrayList<>(); + do { + pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, originatorId, pageLink); + if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { + for (EdgeId relatedEdgeId : pageData.getData()) { + if (!relatedEdgeId.equals(sourceEdgeId)) { + futures.add(saveEdgeEvent(tenantId, + relatedEdgeId, + EdgeEventType.ALARM, + actionType, + alarmId, + body)); + } + } + if (pageData.hasNext()) { + pageLink = pageLink.nextPageLink(); + } + } + } while (pageData != null && pageData.hasNext()); + return futures; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessorFactory.java new file mode 100644 index 0000000000..1b0b62532b --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.alarm; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessorFactory; + +@Component +@TbCoreComponent +public class AlarmEdgeProcessorFactory extends BaseEdgeProcessorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessorV1.java new file mode 100644 index 0000000000..b9968b45d5 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessorV1.java @@ -0,0 +1,72 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.alarm; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.alarm.AlarmStatus; +import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class AlarmEdgeProcessorV1 extends AlarmEdgeProcessor { + + @Override + protected Alarm constructAlarmFromUpdateMsg(TenantId tenantId, AlarmId alarmId, EntityId originatorId, AlarmUpdateMsg alarmUpdateMsg) { + Alarm alarm = new Alarm(); + alarm.setId(alarmId); + alarm.setTenantId(tenantId); + alarm.setOriginator(originatorId); + alarm.setType(alarmUpdateMsg.getName()); + alarm.setSeverity(AlarmSeverity.valueOf(alarmUpdateMsg.getSeverity())); + alarm.setStartTs(alarmUpdateMsg.getStartTs()); + AlarmStatus alarmStatus = AlarmStatus.valueOf(alarmUpdateMsg.getStatus()); + alarm.setClearTs(alarmUpdateMsg.getClearTs()); + alarm.setPropagate(alarmUpdateMsg.getPropagate()); + alarm.setCleared(alarmStatus.isCleared()); + alarm.setAcknowledged(alarmStatus.isAck()); + alarm.setAckTs(alarmUpdateMsg.getAckTs()); + alarm.setEndTs(alarmUpdateMsg.getEndTs()); + alarm.setDetails(JacksonUtil.toJsonNode(alarmUpdateMsg.getDetails())); + return alarm; + } + + @Override + protected EntityId getAlarmOriginatorFromMsg(TenantId tenantId, AlarmUpdateMsg alarmUpdateMsg) { + return getAlarmOriginator(tenantId, alarmUpdateMsg.getOriginatorName(), + EntityType.valueOf(alarmUpdateMsg.getOriginatorType())); + } + + private EntityId getAlarmOriginator(TenantId tenantId, String entityName, EntityType entityType) { + switch (entityType) { + case DEVICE: + return deviceService.findDeviceByTenantIdAndName(tenantId, entityName).getId(); + case ASSET: + return assetService.findAssetByTenantIdAndName(tenantId, entityName).getId(); + case ENTITY_VIEW: + return entityViewService.findEntityViewByTenantIdAndName(tenantId, entityName).getId(); + default: + return null; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessorV2.java new file mode 100644 index 0000000000..a60eeac175 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmEdgeProcessorV2.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.alarm; + +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Primary +@Component +@TbCoreComponent +public class AlarmEdgeProcessorV2 extends AlarmEdgeProcessor { + + @Override + protected EntityId getAlarmOriginatorFromMsg(TenantId tenantId, AlarmUpdateMsg alarmUpdateMsg) { + Alarm alarm = JacksonUtil.fromString(alarmUpdateMsg.getEntity(), Alarm.class, true); + return alarm != null ? alarm.getOriginator() : null; + } + + @Override + protected Alarm constructAlarmFromUpdateMsg(TenantId tenantId, AlarmId alarmId, EntityId originatorId, AlarmUpdateMsg alarmUpdateMsg) { + return JacksonUtil.fromString(alarmUpdateMsg.getEntity(), Alarm.class, true); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmProcessor.java new file mode 100644 index 0000000000..80a300836c --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/AlarmProcessor.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.alarm; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.service.edge.rpc.processor.EdgeProcessor; + +public interface AlarmProcessor extends EdgeProcessor { + + ListenableFuture processAlarmMsgFromEdge(TenantId tenantId, EdgeId edgeId, AlarmUpdateMsg alarmUpdateMsg); + + DownlinkMsg convertAlarmEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java index be64f475a2..1e8784bf05 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java @@ -20,18 +20,23 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; -import org.thingsboard.server.common.data.alarm.AlarmSeverity; -import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest; +import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.alarm.AlarmMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; import java.util.UUID; @@ -41,57 +46,42 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor { public ListenableFuture processAlarmMsg(TenantId tenantId, AlarmUpdateMsg alarmUpdateMsg) { log.trace("[{}] processAlarmMsg [{}]", tenantId, alarmUpdateMsg); - EntityId originatorId = getAlarmOriginator(tenantId, alarmUpdateMsg.getOriginatorName(), - EntityType.valueOf(alarmUpdateMsg.getOriginatorType())); AlarmId alarmId = new AlarmId(new UUID(alarmUpdateMsg.getIdMSB(), alarmUpdateMsg.getIdLSB())); - if (originatorId == null) { - log.warn("Originator not found for the alarm msg {}", alarmUpdateMsg); + EntityId originatorId = getAlarmOriginatorFromMsg(tenantId, alarmUpdateMsg); + Alarm alarm = constructAlarmFromUpdateMsg(tenantId, alarmId, originatorId, alarmUpdateMsg); + if (alarm == null) { + throw new RuntimeException("[{" + tenantId + "}] alarmUpdateMsg {" + alarmUpdateMsg + "} cannot be converted to alarm"); + } + if (alarm.getOriginator() == null) { + log.warn("[{}] Originator not found for the alarm msg {}", tenantId, alarmUpdateMsg); return Futures.immediateFuture(null); } try { switch (alarmUpdateMsg.getMsgType()) { case ENTITY_CREATED_RPC_MESSAGE: + alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.fromAlarm(alarm, null, alarmId)); + break; case ENTITY_UPDATED_RPC_MESSAGE: - Alarm alarm = new Alarm(); - alarm.setId(alarmId); - alarm.setTenantId(tenantId); - alarm.setType(alarmUpdateMsg.getName()); - alarm.setOriginator(originatorId); - alarm.setSeverity(AlarmSeverity.valueOf(alarmUpdateMsg.getSeverity())); - alarm.setStartTs(alarmUpdateMsg.getStartTs()); - var alarmStatus = AlarmStatus.valueOf(alarmUpdateMsg.getStatus()); - alarm.setClearTs(alarmUpdateMsg.getClearTs()); - alarm.setPropagate(alarmUpdateMsg.getPropagate()); - alarm.setCleared(alarmStatus.isCleared()); - alarm.setAcknowledged(alarmStatus.isAck()); - alarm.setAckTs(alarmUpdateMsg.getAckTs()); - alarm.setEndTs(alarmUpdateMsg.getEndTs()); - alarm.setDetails(JacksonUtil.OBJECT_MAPPER.readTree(alarmUpdateMsg.getDetails())); - if (UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE.equals(alarmUpdateMsg.getMsgType())) { - alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.fromAlarm(alarm, null, alarmId)); - } else { - alarmService.updateAlarm(AlarmUpdateRequest.fromAlarm(alarm)); - } - return Futures.immediateFuture(null); + alarmService.updateAlarm(AlarmUpdateRequest.fromAlarm(alarm)); + break; case ALARM_ACK_RPC_MESSAGE: Alarm alarmToAck = alarmService.findAlarmById(tenantId, alarmId); if (alarmToAck != null) { - alarmService.acknowledgeAlarm(tenantId, alarmId, alarmUpdateMsg.getAckTs()); + alarmService.acknowledgeAlarm(tenantId, alarmId, alarm.getAckTs()); } - return Futures.immediateFuture(null); + break; case ALARM_CLEAR_RPC_MESSAGE: Alarm alarmToClear = alarmService.findAlarmById(tenantId, alarmId); if (alarmToClear != null) { - alarmService.clearAlarm(tenantId, alarmId, alarmUpdateMsg.getClearTs(), - JacksonUtil.OBJECT_MAPPER.readTree(alarmUpdateMsg.getDetails())); + alarmService.clearAlarm(tenantId, alarmId, alarm.getClearTs(), alarm.getDetails()); } - return Futures.immediateFuture(null); + break; case ENTITY_DELETED_RPC_MESSAGE: Alarm alarmToDelete = alarmService.findAlarmById(tenantId, alarmId); if (alarmToDelete != null) { alarmService.delAlarm(tenantId, alarmId); } - return Futures.immediateFuture(null); + break; case UNRECOGNIZED: default: return handleUnsupportedMsgType(alarmUpdateMsg.getMsgType()); @@ -100,22 +90,14 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor { log.error("[{}] Failed to process alarm update msg [{}]", tenantId, alarmUpdateMsg, e); return Futures.immediateFailedFuture(e); } + return Futures.immediateFuture(null); } - private EntityId getAlarmOriginator(TenantId tenantId, String entityName, EntityType entityType) { - switch (entityType) { - case DEVICE: - return deviceService.findDeviceByTenantIdAndName(tenantId, entityName).getId(); - case ASSET: - return assetService.findAssetByTenantIdAndName(tenantId, entityName).getId(); - case ENTITY_VIEW: - return entityViewService.findEntityViewByTenantIdAndName(tenantId, entityName).getId(); - default: - return null; - } - } + protected abstract EntityId getAlarmOriginatorFromMsg(TenantId tenantId, AlarmUpdateMsg alarmUpdateMsg); + + protected abstract Alarm constructAlarmFromUpdateMsg(TenantId tenantId, AlarmId alarmId, EntityId originatorId, AlarmUpdateMsg alarmUpdateMsg); - public AlarmUpdateMsg convertAlarmEventToAlarmMsg(TenantId tenantId, UUID entityId, EdgeEventActionType actionType, JsonNode body) { + protected AlarmUpdateMsg convertAlarmEventToAlarmMsg(TenantId tenantId, UUID entityId, EdgeEventActionType actionType, JsonNode body, EdgeVersion edgeVersion) { AlarmId alarmId = new AlarmId(entityId); UpdateMsgType msgType = getUpdateMsgType(actionType); switch (actionType) { @@ -125,13 +107,42 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor { case ALARM_CLEAR: Alarm alarm = alarmService.findAlarmById(tenantId, alarmId); if (alarm != null) { - return alarmMsgConstructor.constructAlarmUpdatedMsg(tenantId, msgType, alarm); + return ((AlarmMsgConstructor) alarmMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructAlarmUpdatedMsg(msgType, alarm, findOriginatorEntityName(tenantId, alarm)); } break; case DELETED: - Alarm deletedAlarm = JacksonUtil.OBJECT_MAPPER.convertValue(body, Alarm.class); - return alarmMsgConstructor.constructAlarmUpdatedMsg(tenantId, msgType, deletedAlarm); + Alarm deletedAlarm = JacksonUtil.convertValue(body, Alarm.class); + if (deletedAlarm != null) { + return ((AlarmMsgConstructor) alarmMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructAlarmUpdatedMsg(msgType, deletedAlarm, findOriginatorEntityName(tenantId, deletedAlarm)); + } } return null; } + + private String findOriginatorEntityName(TenantId tenantId, Alarm alarm) { + String entityName = null; + switch (alarm.getOriginator().getEntityType()) { + case DEVICE: + Device deviceById = deviceService.findDeviceById(tenantId, new DeviceId(alarm.getOriginator().getId())); + if (deviceById != null) { + entityName = deviceById.getName(); + } + break; + case ASSET: + Asset assetById = assetService.findAssetById(tenantId, new AssetId(alarm.getOriginator().getId())); + if (assetById != null) { + entityName = assetById.getName(); + } + break; + case ENTITY_VIEW: + EntityView entityViewById = entityViewService.findEntityViewById(tenantId, new EntityViewId(alarm.getOriginator().getId())); + if (entityViewById != null) { + entityName = entityViewById.getName(); + } + break; + } + return entityName; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java index 2fb7320219..8600800b3a 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java @@ -15,28 +15,97 @@ */ package org.thingsboard.server.service.edge.rpc.processor.asset; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; +import org.springframework.data.util.Pair; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.dao.asset.BaseAssetService; +import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.constructor.asset.AssetMsgConstructor; + +import java.util.UUID; -@Component @Slf4j -@TbCoreComponent -public class AssetEdgeProcessor extends BaseEdgeProcessor { +public abstract class AssetEdgeProcessor extends BaseAssetProcessor implements AssetProcessor { + + @Override + public ListenableFuture processAssetMsgFromEdge(TenantId tenantId, Edge edge, AssetUpdateMsg assetUpdateMsg) { + log.trace("[{}] executing processAssetMsgFromEdge [{}] from edge [{}]", tenantId, assetUpdateMsg, edge.getId()); + AssetId assetId = new AssetId(new UUID(assetUpdateMsg.getIdMSB(), assetUpdateMsg.getIdLSB())); + try { + edgeSynchronizationManager.getEdgeId().set(edge.getId()); + + switch (assetUpdateMsg.getMsgType()) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + saveOrUpdateAsset(tenantId, assetId, assetUpdateMsg, edge); + return Futures.immediateFuture(null); + case ENTITY_DELETED_RPC_MESSAGE: + Asset assetToDelete = assetService.findAssetById(tenantId, assetId); + if (assetToDelete != null) { + assetService.unassignAssetFromEdge(tenantId, assetId, edge.getId()); + } + return Futures.immediateFuture(null); + case UNRECOGNIZED: + default: + return handleUnsupportedMsgType(assetUpdateMsg.getMsgType()); + } + } catch (DataValidationException e) { + if (e.getMessage().contains("limit reached")) { + log.warn("[{}] Number of allowed asset violated {}", tenantId, assetUpdateMsg, e); + return Futures.immediateFuture(null); + } else { + return Futures.immediateFailedFuture(e); + } + } finally { + edgeSynchronizationManager.getEdgeId().remove(); + } + } - public DownlinkMsg convertAssetEventToDownlink(EdgeEvent edgeEvent) { + private void saveOrUpdateAsset(TenantId tenantId, AssetId assetId, AssetUpdateMsg assetUpdateMsg, Edge edge) { + Pair resultPair = super.saveOrUpdateAsset(tenantId, assetId, assetUpdateMsg); + Boolean created = resultPair.getFirst(); + if (created) { + createRelationFromEdge(tenantId, edge.getId(), assetId); + pushAssetCreatedEventToRuleEngine(tenantId, edge, assetId); + assetService.assignAssetToEdge(tenantId, assetId, edge.getId()); + } + Boolean assetNameUpdated = resultPair.getSecond(); + if (assetNameUpdated) { + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ASSET, EdgeEventActionType.UPDATED, assetId, null); + } + } + + private void pushAssetCreatedEventToRuleEngine(TenantId tenantId, Edge edge, AssetId assetId) { + try { + Asset asset = assetService.findAssetById(tenantId, assetId); + String assetAsString = JacksonUtil.toString(asset); + TbMsgMetaData msgMetaData = getEdgeActionTbMsgMetaData(edge, asset.getCustomerId()); + pushEntityEventToRuleEngine(tenantId, assetId, asset.getCustomerId(), TbMsgType.ENTITY_CREATED, assetAsString, msgMetaData); + } catch (Exception e) { + log.warn("[{}][{}] Failed to push asset action to rule engine: {}", tenantId, assetId, TbMsgType.ENTITY_CREATED.name(), e); + } + } + + @Override + public DownlinkMsg convertAssetEventToDownlink(EdgeEvent edgeEvent, EdgeId edgeId, EdgeVersion edgeVersion) { AssetId assetId = new AssetId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; switch (edgeEvent.getAction()) { @@ -46,24 +115,26 @@ public class AssetEdgeProcessor extends BaseEdgeProcessor { case ASSIGNED_TO_CUSTOMER: case UNASSIGNED_FROM_CUSTOMER: Asset asset = assetService.findAssetById(edgeEvent.getTenantId(), assetId); - if (asset != null) { + if (asset != null && !BaseAssetService.TB_SERVICE_QUEUE.equals(asset.getType())) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - AssetUpdateMsg assetUpdateMsg = - assetMsgConstructor.constructAssetUpdatedMsg(msgType, asset); + AssetUpdateMsg assetUpdateMsg = ((AssetMsgConstructor) + assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructAssetUpdatedMsg(msgType, asset); DownlinkMsg.Builder builder = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addAssetUpdateMsg(assetUpdateMsg); if (UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE.equals(msgType)) { AssetProfile assetProfile = assetProfileService.findAssetProfileById(edgeEvent.getTenantId(), asset.getAssetProfileId()); - builder.addAssetProfileUpdateMsg(assetProfileMsgConstructor.constructAssetProfileUpdatedMsg(msgType, assetProfile)); + assetProfile = checkIfAssetProfileDefaultFieldsAssignedToEdge(edgeEvent.getTenantId(), edgeId, assetProfile, edgeVersion); + builder.addAssetProfileUpdateMsg(((AssetMsgConstructor) assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructAssetProfileUpdatedMsg(msgType, assetProfile)); } downlinkMsg = builder.build(); } break; case DELETED: case UNASSIGNED_FROM_EDGE: - AssetUpdateMsg assetUpdateMsg = - assetMsgConstructor.constructAssetDeleteMsg(assetId); + AssetUpdateMsg assetUpdateMsg = ((AssetMsgConstructor) + assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructAssetDeleteMsg(assetId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addAssetUpdateMsg(assetUpdateMsg) @@ -72,8 +143,4 @@ public class AssetEdgeProcessor extends BaseEdgeProcessor { } return downlinkMsg; } - - public ListenableFuture processAssetNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { - return processEntityNotification(tenantId, edgeNotificationMsg); - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessorFactory.java new file mode 100644 index 0000000000..4aa623e0d6 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.asset; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessorFactory; + +@Component +@TbCoreComponent +public class AssetEdgeProcessorFactory extends BaseEdgeProcessorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessorV1.java new file mode 100644 index 0000000000..8b2585b8fb --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessorV1.java @@ -0,0 +1,59 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.asset; + +import com.datastax.oss.driver.api.core.uuid.Uuids; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.AssetProfileId; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import java.util.UUID; + +@Component +@TbCoreComponent +public class AssetEdgeProcessorV1 extends AssetEdgeProcessor { + + @Override + protected Asset constructAssetFromUpdateMsg(TenantId tenantId, AssetId assetId, AssetUpdateMsg assetUpdateMsg) { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + asset.setName(assetUpdateMsg.getName()); + asset.setCreatedTime(Uuids.unixTimestamp(assetId.getId())); + asset.setType(assetUpdateMsg.getType()); + asset.setLabel(assetUpdateMsg.hasLabel() ? assetUpdateMsg.getLabel() : null); + asset.setAdditionalInfo(assetUpdateMsg.hasAdditionalInfo() + ? JacksonUtil.toJsonNode(assetUpdateMsg.getAdditionalInfo()) : null); + + UUID assetProfileUUID = safeGetUUID(assetUpdateMsg.getAssetProfileIdMSB(), assetUpdateMsg.getAssetProfileIdLSB()); + asset.setAssetProfileId(assetProfileUUID != null ? new AssetProfileId(assetProfileUUID) : null); + + CustomerId customerId = safeGetCustomerId(assetUpdateMsg.getCustomerIdMSB(), assetUpdateMsg.getCustomerIdLSB()); + asset.setCustomerId(customerId); + return asset; + } + + @Override + protected void setCustomerId(TenantId tenantId, CustomerId customerId, Asset asset, AssetUpdateMsg assetUpdateMsg) { + CustomerId customerUUID = safeGetCustomerId(assetUpdateMsg.getCustomerIdMSB(), assetUpdateMsg.getCustomerIdLSB()); + asset.setCustomerId(customerUUID != null ? customerUUID : customerId); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessorV2.java new file mode 100644 index 0000000000..64041d9e4d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessorV2.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.asset; + +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Primary +@Component +@TbCoreComponent +public class AssetEdgeProcessorV2 extends AssetEdgeProcessor { + + @Override + protected Asset constructAssetFromUpdateMsg(TenantId tenantId, AssetId assetId, AssetUpdateMsg assetUpdateMsg) { + return JacksonUtil.fromString(assetUpdateMsg.getEntity(), Asset.class, true); + } + + @Override + protected void setCustomerId(TenantId tenantId, CustomerId customerId, Asset asset, AssetUpdateMsg assetUpdateMsg) { + CustomerId customerUUID = asset.getCustomerId() != null ? asset.getCustomerId() : customerId; + asset.setCustomerId(customerUUID); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProcessor.java new file mode 100644 index 0000000000..b892a4a57d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProcessor.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.asset; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.service.edge.rpc.processor.EdgeProcessor; + +public interface AssetProcessor extends EdgeProcessor { + + ListenableFuture processAssetMsgFromEdge(TenantId tenantId, Edge edge, AssetUpdateMsg assetUpdateMsg); + + DownlinkMsg convertAssetEventToDownlink(EdgeEvent edgeEvent, EdgeId edgeId, EdgeVersion edgeVersion); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java deleted file mode 100644 index 51c42e5764..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.processor.asset; - -import com.google.common.util.concurrent.ListenableFuture; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.EdgeUtils; -import org.thingsboard.server.common.data.asset.AssetProfile; -import org.thingsboard.server.common.data.edge.EdgeEvent; -import org.thingsboard.server.common.data.id.AssetProfileId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; -import org.thingsboard.server.gen.edge.v1.DownlinkMsg; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; - -@Component -@Slf4j -@TbCoreComponent -public class AssetProfileEdgeProcessor extends BaseEdgeProcessor { - - public DownlinkMsg convertAssetProfileEventToDownlink(EdgeEvent edgeEvent) { - AssetProfileId assetProfileId = new AssetProfileId(edgeEvent.getEntityId()); - DownlinkMsg downlinkMsg = null; - switch (edgeEvent.getAction()) { - case ADDED: - case UPDATED: - AssetProfile assetProfile = assetProfileService.findAssetProfileById(edgeEvent.getTenantId(), assetProfileId); - if (assetProfile != null) { - UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - AssetProfileUpdateMsg assetProfileUpdateMsg = - assetProfileMsgConstructor.constructAssetProfileUpdatedMsg(msgType, assetProfile); - downlinkMsg = DownlinkMsg.newBuilder() - .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addAssetProfileUpdateMsg(assetProfileUpdateMsg) - .build(); - } - break; - case DELETED: - AssetProfileUpdateMsg assetProfileUpdateMsg = - assetProfileMsgConstructor.constructAssetProfileDeleteMsg(assetProfileId); - downlinkMsg = DownlinkMsg.newBuilder() - .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addAssetProfileUpdateMsg(assetProfileUpdateMsg) - .build(); - break; - } - return downlinkMsg; - } - - public ListenableFuture processAssetProfileNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { - return processEntityNotificationForAllEdges(tenantId, edgeNotificationMsg); - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java new file mode 100644 index 0000000000..326c48819f --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java @@ -0,0 +1,75 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.asset; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.util.Pair; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; + +@Slf4j +public abstract class BaseAssetProcessor extends BaseEdgeProcessor { + + protected Pair saveOrUpdateAsset(TenantId tenantId, AssetId assetId, AssetUpdateMsg assetUpdateMsg) { + boolean created = false; + boolean assetNameUpdated = false; + assetCreationLock.lock(); + try { + Asset asset = constructAssetFromUpdateMsg(tenantId, assetId, assetUpdateMsg); + if (asset == null) { + throw new RuntimeException("[{" + tenantId + "}] assetUpdateMsg {" + assetUpdateMsg + " } cannot be converted to asset"); + } + Asset assetById = assetService.findAssetById(tenantId, assetId); + if (assetById == null) { + created = true; + asset.setId(null); + } else { + asset.setId(assetId); + } + String assetName = asset.getName(); + Asset assetByName = assetService.findAssetByTenantIdAndName(tenantId, assetName); + if (assetByName != null && !assetByName.getId().equals(assetId)) { + assetName = assetName + "_" + StringUtils.randomAlphanumeric(15); + log.warn("[{}] Asset with name {} already exists. Renaming asset name to {}", + tenantId, asset.getName(), assetName); + assetNameUpdated = true; + } + asset.setName(assetName); + setCustomerId(tenantId, created ? null : assetById.getCustomerId(), asset, assetUpdateMsg); + + assetValidator.validate(asset, Asset::getTenantId); + if (created) { + asset.setId(assetId); + } + assetService.saveAsset(asset, false); + } catch (Exception e) { + log.error("[{}] Failed to process asset update msg [{}]", tenantId, assetUpdateMsg, e); + throw e; + } finally { + assetCreationLock.unlock(); + } + return Pair.of(created, assetNameUpdated); + } + + protected abstract Asset constructAssetFromUpdateMsg(TenantId tenantId, AssetId assetId, AssetUpdateMsg assetUpdateMsg); + + protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, Asset asset, AssetUpdateMsg assetUpdateMsg); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/profile/AssetProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/profile/AssetProfileEdgeProcessor.java new file mode 100644 index 0000000000..847690a227 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/profile/AssetProfileEdgeProcessor.java @@ -0,0 +1,125 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.asset.profile; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.util.Pair; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.EdgeUtils; +import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.id.AssetProfileId; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.asset.AssetMsgConstructor; + +import java.util.UUID; + +@Slf4j +public abstract class AssetProfileEdgeProcessor extends BaseAssetProfileProcessor implements AssetProfileProcessor { + + @Override + public ListenableFuture processAssetProfileMsgFromEdge(TenantId tenantId, Edge edge, AssetProfileUpdateMsg assetProfileUpdateMsg) { + log.trace("[{}] executing processAssetProfileMsgFromEdge [{}] from edge [{}]", tenantId, assetProfileUpdateMsg, edge.getId()); + AssetProfileId assetProfileId = new AssetProfileId(new UUID(assetProfileUpdateMsg.getIdMSB(), assetProfileUpdateMsg.getIdLSB())); + try { + edgeSynchronizationManager.getEdgeId().set(edge.getId()); + + switch (assetProfileUpdateMsg.getMsgType()) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + saveOrUpdateAssetProfile(tenantId, assetProfileId, assetProfileUpdateMsg, edge); + return Futures.immediateFuture(null); + case ENTITY_DELETED_RPC_MESSAGE: + case UNRECOGNIZED: + default: + return handleUnsupportedMsgType(assetProfileUpdateMsg.getMsgType()); + } + } catch (DataValidationException e) { + log.warn("[{}] Failed to process AssetProfileUpdateMsg from Edge [{}]", tenantId, assetProfileUpdateMsg, e); + return Futures.immediateFailedFuture(e); + } finally { + edgeSynchronizationManager.getEdgeId().remove(); + } + } + + private void saveOrUpdateAssetProfile(TenantId tenantId, AssetProfileId assetProfileId, AssetProfileUpdateMsg assetProfileUpdateMsg, Edge edge) { + Pair resultPair = super.saveOrUpdateAssetProfile(tenantId, assetProfileId, assetProfileUpdateMsg); + Boolean created = resultPair.getFirst(); + if (created) { + createRelationFromEdge(tenantId, edge.getId(), assetProfileId); + pushAssetProfileCreatedEventToRuleEngine(tenantId, edge, assetProfileId); + } + Boolean assetProfileNameUpdated = resultPair.getSecond(); + if (assetProfileNameUpdated) { + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ASSET_PROFILE, EdgeEventActionType.UPDATED, assetProfileId, null); + } + } + + private void pushAssetProfileCreatedEventToRuleEngine(TenantId tenantId, Edge edge, AssetProfileId assetProfileId) { + try { + AssetProfile assetProfile = assetProfileService.findAssetProfileById(tenantId, assetProfileId); + String assetProfileAsString = JacksonUtil.toString(assetProfile); + TbMsgMetaData msgMetaData = getEdgeActionTbMsgMetaData(edge, null); + pushEntityEventToRuleEngine(tenantId, assetProfileId, null, TbMsgType.ENTITY_CREATED, assetProfileAsString, msgMetaData); + } catch (Exception e) { + log.warn("[{}][{}] Failed to push asset profile action to rule engine: {}", tenantId, assetProfileId, TbMsgType.ENTITY_CREATED.name(), e); + } + } + + @Override + public DownlinkMsg convertAssetProfileEventToDownlink(EdgeEvent edgeEvent, EdgeId edgeId, EdgeVersion edgeVersion) { + AssetProfileId assetProfileId = new AssetProfileId(edgeEvent.getEntityId()); + DownlinkMsg downlinkMsg = null; + switch (edgeEvent.getAction()) { + case ADDED: + case UPDATED: + AssetProfile assetProfile = assetProfileService.findAssetProfileById(edgeEvent.getTenantId(), assetProfileId); + if (assetProfile != null) { + UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); + assetProfile = checkIfAssetProfileDefaultFieldsAssignedToEdge(edgeEvent.getTenantId(), edgeId, assetProfile, edgeVersion); + AssetProfileUpdateMsg assetProfileUpdateMsg = ((AssetMsgConstructor) + assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructAssetProfileUpdatedMsg(msgType, assetProfile); + downlinkMsg = DownlinkMsg.newBuilder() + .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) + .addAssetProfileUpdateMsg(assetProfileUpdateMsg) + .build(); + } + break; + case DELETED: + AssetProfileUpdateMsg assetProfileUpdateMsg = ((AssetMsgConstructor) + assetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructAssetProfileDeleteMsg(assetProfileId); + downlinkMsg = DownlinkMsg.newBuilder() + .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) + .addAssetProfileUpdateMsg(assetProfileUpdateMsg) + .build(); + break; + } + return downlinkMsg; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/profile/AssetProfileEdgeProcessorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/profile/AssetProfileEdgeProcessorFactory.java new file mode 100644 index 0000000000..5a9935d8dd --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/profile/AssetProfileEdgeProcessorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.asset.profile; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessorFactory; + +@Component +@TbCoreComponent +public class AssetProfileEdgeProcessorFactory extends BaseEdgeProcessorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/profile/AssetProfileEdgeProcessorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/profile/AssetProfileEdgeProcessorV1.java new file mode 100644 index 0000000000..f0af368187 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/profile/AssetProfileEdgeProcessorV1.java @@ -0,0 +1,65 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.asset.profile; + +import com.datastax.oss.driver.api.core.uuid.Uuids; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.common.data.id.AssetProfileId; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import java.nio.charset.StandardCharsets; +import java.util.UUID; + +@Component +@TbCoreComponent +public class AssetProfileEdgeProcessorV1 extends AssetProfileEdgeProcessor { + + @Override + protected AssetProfile constructAssetProfileFromUpdateMsg(TenantId tenantId, AssetProfileId assetProfileId, AssetProfileUpdateMsg assetProfileUpdateMsg) { + AssetProfile assetProfile = new AssetProfile(); + assetProfile.setTenantId(tenantId); + assetProfile.setName(assetProfileUpdateMsg.getName()); + assetProfile.setCreatedTime(Uuids.unixTimestamp(assetProfileId.getId())); + assetProfile.setDefault(assetProfileUpdateMsg.getDefault()); + assetProfile.setDefaultQueueName(assetProfileUpdateMsg.hasDefaultQueueName() ? assetProfileUpdateMsg.getDefaultQueueName() : null); + assetProfile.setDescription(assetProfileUpdateMsg.hasDescription() ? assetProfileUpdateMsg.getDescription() : null); + assetProfile.setImage(assetProfileUpdateMsg.hasImage() + ? new String(assetProfileUpdateMsg.getImage().toByteArray(), StandardCharsets.UTF_8) : null); + return assetProfile; + } + + @Override + protected void setDefaultRuleChainId(TenantId tenantId, AssetProfile assetProfile, RuleChainId ruleChainId) { + assetProfile.setDefaultRuleChainId(ruleChainId); + } + + @Override + protected void setDefaultEdgeRuleChainId(AssetProfile assetProfile, RuleChainId ruleChainId, AssetProfileUpdateMsg assetProfileUpdateMsg) { + UUID defaultEdgeRuleChainUUID = safeGetUUID(assetProfileUpdateMsg.getDefaultRuleChainIdMSB(), assetProfileUpdateMsg.getDefaultRuleChainIdLSB()); + assetProfile.setDefaultEdgeRuleChainId(defaultEdgeRuleChainUUID != null ? new RuleChainId(defaultEdgeRuleChainUUID) : ruleChainId); + } + + @Override + protected void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg) { + UUID defaultDashboardUUID = safeGetUUID(assetProfileUpdateMsg.getDefaultDashboardIdMSB(), assetProfileUpdateMsg.getDefaultDashboardIdLSB()); + assetProfile.setDefaultDashboardId(defaultDashboardUUID != null ? new DashboardId(defaultDashboardUUID) : dashboardId); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/profile/AssetProfileEdgeProcessorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/profile/AssetProfileEdgeProcessorV2.java new file mode 100644 index 0000000000..7bccbc0a38 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/profile/AssetProfileEdgeProcessorV2.java @@ -0,0 +1,57 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.asset.profile; + +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.common.data.id.AssetProfileId; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import java.util.UUID; + +@Primary +@Component +@TbCoreComponent +public class AssetProfileEdgeProcessorV2 extends AssetProfileEdgeProcessor { + + @Override + protected AssetProfile constructAssetProfileFromUpdateMsg(TenantId tenantId, AssetProfileId assetProfileId, AssetProfileUpdateMsg assetProfileUpdateMsg) { + return JacksonUtil.fromString(assetProfileUpdateMsg.getEntity(), AssetProfile.class, true); + } + + @Override + protected void setDefaultRuleChainId(TenantId tenantId, AssetProfile assetProfile, RuleChainId ruleChainId) { + assetProfile.setDefaultRuleChainId(ruleChainId); + } + + @Override + protected void setDefaultEdgeRuleChainId(AssetProfile assetProfile, RuleChainId ruleChainId, AssetProfileUpdateMsg assetProfileUpdateMsg) { + UUID defaultEdgeRuleChainUUID = ruleChainId != null ? ruleChainId.getId() : null; + assetProfile.setDefaultEdgeRuleChainId(defaultEdgeRuleChainUUID != null ? new RuleChainId(defaultEdgeRuleChainUUID) : null); + } + + @Override + protected void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg) { + UUID defaultDashboardUUID = assetProfile.getDefaultDashboardId() != null ? assetProfile.getDefaultDashboardId().getId() : (dashboardId != null ? dashboardId.getId() : null); + assetProfile.setDefaultDashboardId(defaultDashboardUUID != null ? new DashboardId(defaultDashboardUUID) : null); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/profile/AssetProfileProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/profile/AssetProfileProcessor.java new file mode 100644 index 0000000000..16ca8d2756 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/profile/AssetProfileProcessor.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.asset.profile; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.service.edge.rpc.processor.EdgeProcessor; + +public interface AssetProfileProcessor extends EdgeProcessor { + + ListenableFuture processAssetProfileMsgFromEdge(TenantId tenantId, Edge edge, AssetProfileUpdateMsg assetProfileUpdateMsg); + + DownlinkMsg convertAssetProfileEventToDownlink(EdgeEvent edgeEvent, EdgeId edgeId, EdgeVersion edgeVersion); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/profile/BaseAssetProfileProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/profile/BaseAssetProfileProcessor.java new file mode 100644 index 0000000000..2fe6cb36c2 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/profile/BaseAssetProfileProcessor.java @@ -0,0 +1,87 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.asset.profile; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.util.Pair; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.common.data.id.AssetProfileId; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; + +@Slf4j +public abstract class BaseAssetProfileProcessor extends BaseEdgeProcessor { + + protected Pair saveOrUpdateAssetProfile(TenantId tenantId, AssetProfileId assetProfileId, AssetProfileUpdateMsg assetProfileUpdateMsg) { + boolean created = false; + boolean assetProfileNameUpdated = false; + assetCreationLock.lock(); + try { + AssetProfile assetProfile = constructAssetProfileFromUpdateMsg(tenantId, assetProfileId, assetProfileUpdateMsg); + if (assetProfile == null) { + throw new RuntimeException("[{" + tenantId + "}] assetProfileUpdateMsg {" + assetProfileUpdateMsg + "} cannot be converted to asset profile"); + } + AssetProfile assetProfileById = assetProfileService.findAssetProfileById(tenantId, assetProfileId); + if (assetProfileById == null) { + created = true; + assetProfile.setId(null); + assetProfile.setDefault(false); + } else { + assetProfile.setId(assetProfileId); + assetProfile.setDefault(assetProfileById.isDefault()); + } + assetProfile.setDefault(false); + String assetProfileName = assetProfile.getName(); + AssetProfile assetProfileByName = assetProfileService.findAssetProfileByName(tenantId, assetProfileName); + if (assetProfileByName != null && !assetProfileByName.getId().equals(assetProfileId)) { + assetProfileName = assetProfileName + "_" + StringUtils.randomAlphabetic(15); + log.warn("[{}] Asset profile with name {} already exists. Renaming asset profile name to {}", + tenantId, assetProfile.getName(), assetProfileName); + assetProfileNameUpdated = true; + } + assetProfile.setName(assetProfileName); + + RuleChainId ruleChainId = assetProfile.getDefaultRuleChainId(); + setDefaultRuleChainId(tenantId, assetProfile, created ? null : assetProfileById.getDefaultRuleChainId()); + setDefaultEdgeRuleChainId(assetProfile, ruleChainId, assetProfileUpdateMsg); + setDefaultDashboardId(tenantId, created ? null : assetProfileById.getDefaultDashboardId(), assetProfile, assetProfileUpdateMsg); + + assetProfileValidator.validate(assetProfile, AssetProfile::getTenantId); + if (created) { + assetProfile.setId(assetProfileId); + } + assetProfileService.saveAssetProfile(assetProfile, false); + } catch (Exception e) { + log.error("[{}] Failed to process asset profile update msg [{}]", tenantId, assetProfileUpdateMsg, e); + throw e; + } finally { + assetCreationLock.unlock(); + } + return Pair.of(created, assetProfileNameUpdated); + } + + protected abstract AssetProfile constructAssetProfileFromUpdateMsg(TenantId tenantId, AssetProfileId assetProfileId, AssetProfileUpdateMsg assetProfileUpdateMsg); + + protected abstract void setDefaultRuleChainId(TenantId tenantId, AssetProfile assetProfile, RuleChainId ruleChainId); + + protected abstract void setDefaultEdgeRuleChainId(AssetProfile assetProfile, RuleChainId ruleChainId, AssetProfileUpdateMsg assetProfileUpdateMsg); + + protected abstract void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java index f553d6d5a0..9b9d99fcc3 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/customer/CustomerEdgeProcessor.java @@ -33,9 +33,11 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.customer.CustomerMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; import java.util.ArrayList; @@ -47,7 +49,7 @@ import java.util.UUID; @TbCoreComponent public class CustomerEdgeProcessor extends BaseEdgeProcessor { - public DownlinkMsg convertCustomerEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertCustomerEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { CustomerId customerId = new CustomerId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; switch (edgeEvent.getAction()) { @@ -56,8 +58,8 @@ public class CustomerEdgeProcessor extends BaseEdgeProcessor { Customer customer = customerService.findCustomerById(edgeEvent.getTenantId(), customerId); if (customer != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - CustomerUpdateMsg customerUpdateMsg = - customerMsgConstructor.constructCustomerUpdatedMsg(msgType, customer); + CustomerUpdateMsg customerUpdateMsg = ((CustomerMsgConstructor) + customerMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructCustomerUpdatedMsg(msgType, customer); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addCustomerUpdateMsg(customerUpdateMsg) @@ -65,8 +67,8 @@ public class CustomerEdgeProcessor extends BaseEdgeProcessor { } break; case DELETED: - CustomerUpdateMsg customerUpdateMsg = - customerMsgConstructor.constructCustomerDeleteMsg(customerId); + CustomerUpdateMsg customerUpdateMsg = ((CustomerMsgConstructor) + customerMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructCustomerDeleteMsg(customerId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addCustomerUpdateMsg(customerUpdateMsg) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java new file mode 100644 index 0000000000..a303386c6b --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java @@ -0,0 +1,87 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.dashboard; + +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.ShortCustomerInfo; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; + +import java.util.Set; + +@Slf4j +public abstract class BaseDashboardProcessor extends BaseEdgeProcessor { + + protected boolean saveOrUpdateDashboard(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg, CustomerId customerId) { + boolean created = false; + Dashboard dashboard = constructDashboardFromUpdateMsg(tenantId, dashboardId, dashboardUpdateMsg); + if (dashboard == null) { + throw new RuntimeException("[{" + tenantId + "}] dashboardUpdateMsg {" + dashboardUpdateMsg + "} cannot be converted to dashboard"); + } + Set assignedCustomers = null; + Dashboard dashboardById = dashboardService.findDashboardById(tenantId, dashboardId); + if (dashboardById == null) { + created = true; + dashboard.setId(null); + } else { + dashboard.setId(dashboardId); + assignedCustomers = filterNonExistingCustomers(tenantId, dashboardById.getAssignedCustomers()); + } + + dashboardValidator.validate(dashboard, Dashboard::getTenantId); + if (created) { + dashboard.setId(dashboardId); + } + Set msgAssignedCustomers = filterNonExistingCustomers(tenantId, dashboard.getAssignedCustomers()); + if (msgAssignedCustomers != null) { + if (assignedCustomers == null) { + assignedCustomers = msgAssignedCustomers; + } else { + assignedCustomers.addAll(msgAssignedCustomers); + } + } + dashboard.setAssignedCustomers(assignedCustomers); + Dashboard savedDashboard = dashboardService.saveDashboard(dashboard, false); + if (msgAssignedCustomers != null && !msgAssignedCustomers.isEmpty()) { + for (ShortCustomerInfo assignedCustomer : msgAssignedCustomers) { + if (assignedCustomer.getCustomerId().equals(customerId)) { + dashboardService.assignDashboardToCustomer(tenantId, savedDashboard.getId(), assignedCustomer.getCustomerId()); + } + } + } else { + unassignCustomersFromDashboard(tenantId, savedDashboard, customerId); + } + return created; + } + + private void unassignCustomersFromDashboard(TenantId tenantId, Dashboard dashboard, CustomerId customerId) { + if (dashboard.getAssignedCustomers() != null && !dashboard.getAssignedCustomers().isEmpty()) { + for (ShortCustomerInfo assignedCustomer : dashboard.getAssignedCustomers()) { + if (assignedCustomer.getCustomerId().equals(customerId)) { + dashboardService.unassignDashboardFromCustomer(tenantId, dashboard.getId(), assignedCustomer.getCustomerId()); + } + } + } + } + + protected abstract Dashboard constructDashboardFromUpdateMsg(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg); + + protected abstract Set filterNonExistingCustomers(TenantId tenantId, Set assignedCustomers); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java index 9651609bd3..cd2a97ee37 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java @@ -15,27 +15,89 @@ */ package org.thingsboard.server.service.edge.rpc.processor.dashboard; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.EdgeUtils; +import org.thingsboard.server.common.data.ShortCustomerInfo; +import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.constructor.dashboard.DashboardMsgConstructor; + +import java.util.Set; +import java.util.UUID; -@Component @Slf4j -@TbCoreComponent -public class DashboardEdgeProcessor extends BaseEdgeProcessor { +public abstract class DashboardEdgeProcessor extends BaseDashboardProcessor implements DashboardProcessor { + + @Override + public ListenableFuture processDashboardMsgFromEdge(TenantId tenantId, Edge edge, DashboardUpdateMsg dashboardUpdateMsg) { + log.trace("[{}] executing processDashboardMsgFromEdge [{}] from edge [{}]", tenantId, dashboardUpdateMsg, edge.getId()); + DashboardId dashboardId = new DashboardId(new UUID(dashboardUpdateMsg.getIdMSB(), dashboardUpdateMsg.getIdLSB())); + try { + edgeSynchronizationManager.getEdgeId().set(edge.getId()); + + switch (dashboardUpdateMsg.getMsgType()) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + saveOrUpdateDashboard(tenantId, dashboardId, dashboardUpdateMsg, edge); + return Futures.immediateFuture(null); + case ENTITY_DELETED_RPC_MESSAGE: + Dashboard dashboardToDelete = dashboardService.findDashboardById(tenantId, dashboardId); + if (dashboardToDelete != null) { + dashboardService.unassignDashboardFromEdge(tenantId, dashboardId, edge.getId()); + } + return Futures.immediateFuture(null); + case UNRECOGNIZED: + default: + return handleUnsupportedMsgType(dashboardUpdateMsg.getMsgType()); + } + } catch (DataValidationException e) { + if (e.getMessage().contains("limit reached")) { + log.warn("[{}] Number of allowed dashboard violated {}", tenantId, dashboardUpdateMsg, e); + return Futures.immediateFuture(null); + } else { + return Futures.immediateFailedFuture(e); + } + } finally { + edgeSynchronizationManager.getEdgeId().remove(); + } + } + + private void saveOrUpdateDashboard(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg, Edge edge) { + boolean created = super.saveOrUpdateDashboard(tenantId, dashboardId, dashboardUpdateMsg, + edge.getCustomerId()); + if (created) { + createRelationFromEdge(tenantId, edge.getId(), dashboardId); + pushDashboardCreatedEventToRuleEngine(tenantId, edge, dashboardId); + dashboardService.assignDashboardToEdge(tenantId, dashboardId, edge.getId()); + } + } + + private void pushDashboardCreatedEventToRuleEngine(TenantId tenantId, Edge edge, DashboardId dashboardId) { + try { + Dashboard dashboard = dashboardService.findDashboardById(tenantId, dashboardId); + String dashboardAsString = JacksonUtil.toString(dashboard); + TbMsgMetaData msgMetaData = getEdgeActionTbMsgMetaData(edge, null); + pushEntityEventToRuleEngine(tenantId, dashboardId, null, TbMsgType.ENTITY_CREATED, dashboardAsString, msgMetaData); + } catch (Exception e) { + log.warn("[{}][{}] Failed to push dashboard action to rule engine: {}", tenantId, dashboardId, TbMsgType.ENTITY_CREATED.name(), e); + } + } - public DownlinkMsg convertDashboardEventToDownlink(EdgeEvent edgeEvent) { + @Override + public DownlinkMsg convertDashboardEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { DashboardId dashboardId = new DashboardId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; switch (edgeEvent.getAction()) { @@ -47,8 +109,8 @@ public class DashboardEdgeProcessor extends BaseEdgeProcessor { Dashboard dashboard = dashboardService.findDashboardById(edgeEvent.getTenantId(), dashboardId); if (dashboard != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - DashboardUpdateMsg dashboardUpdateMsg = - dashboardMsgConstructor.constructDashboardUpdatedMsg(msgType, dashboard); + DashboardUpdateMsg dashboardUpdateMsg = ((DashboardMsgConstructor) + dashboardMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructDashboardUpdatedMsg(msgType, dashboard); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDashboardUpdateMsg(dashboardUpdateMsg) @@ -57,8 +119,8 @@ public class DashboardEdgeProcessor extends BaseEdgeProcessor { break; case DELETED: case UNASSIGNED_FROM_EDGE: - DashboardUpdateMsg dashboardUpdateMsg = - dashboardMsgConstructor.constructDashboardDeleteMsg(dashboardId); + DashboardUpdateMsg dashboardUpdateMsg = ((DashboardMsgConstructor) + dashboardMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructDashboardDeleteMsg(dashboardId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDashboardUpdateMsg(dashboardUpdateMsg) @@ -68,7 +130,9 @@ public class DashboardEdgeProcessor extends BaseEdgeProcessor { return downlinkMsg; } - public ListenableFuture processDashboardNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { - return processEntityNotification(tenantId, edgeNotificationMsg); + @Override + protected Set filterNonExistingCustomers(TenantId tenantId, Set assignedCustomers) { + // do nothing on cloud + return assignedCustomers; } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessorFactory.java new file mode 100644 index 0000000000..fa956f7502 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.dashboard; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessorFactory; + +@Component +@TbCoreComponent +public class DashboardEdgeProcessorFactory extends BaseEdgeProcessorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessorV1.java new file mode 100644 index 0000000000..a375e0047b --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessorV1.java @@ -0,0 +1,54 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.dashboard; + +import com.datastax.oss.driver.api.core.uuid.Uuids; +import com.fasterxml.jackson.core.type.TypeReference; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.ShortCustomerInfo; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import java.util.Set; + +@Component +@TbCoreComponent +public class DashboardEdgeProcessorV1 extends DashboardEdgeProcessor { + + @Override + protected Dashboard constructDashboardFromUpdateMsg(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg) { + Dashboard dashboard = new Dashboard(); + dashboard.setTenantId(tenantId); + dashboard.setCreatedTime(Uuids.unixTimestamp(dashboardId.getId())); + dashboard.setTitle(dashboardUpdateMsg.getTitle()); + dashboard.setImage(dashboardUpdateMsg.hasImage() ? dashboardUpdateMsg.getImage() : null); + dashboard.setConfiguration(JacksonUtil.toJsonNode(dashboardUpdateMsg.getConfiguration())); + + Set assignedCustomers; + if (dashboardUpdateMsg.hasAssignedCustomers()) { + assignedCustomers = JacksonUtil.fromString(dashboardUpdateMsg.getAssignedCustomers(), new TypeReference<>() {}); + assignedCustomers = filterNonExistingCustomers(tenantId, assignedCustomers); + dashboard.setAssignedCustomers(assignedCustomers); + } + dashboard.setMobileOrder(dashboardUpdateMsg.hasMobileOrder() ? dashboardUpdateMsg.getMobileOrder() : null); + dashboard.setMobileHide(dashboardUpdateMsg.getMobileHide()); + return dashboard; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessorV2.java new file mode 100644 index 0000000000..005aa06871 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessorV2.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.dashboard; + +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Primary +@Component +@TbCoreComponent +public class DashboardEdgeProcessorV2 extends DashboardEdgeProcessor { + + @Override + protected Dashboard constructDashboardFromUpdateMsg(TenantId tenantId, DashboardId dashboardId, DashboardUpdateMsg dashboardUpdateMsg) { + return JacksonUtil.fromString(dashboardUpdateMsg.getEntity(), Dashboard.class, true); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardProcessor.java new file mode 100644 index 0000000000..08cca99b8d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardProcessor.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.dashboard; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.service.edge.rpc.processor.EdgeProcessor; + +public interface DashboardProcessor extends EdgeProcessor { + + ListenableFuture processDashboardMsgFromEdge(TenantId tenantId, Edge edge, DashboardUpdateMsg dashboardUpdateMsg); + + DownlinkMsg convertDashboardEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java index 742acdbcda..2ec72625a3 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java @@ -15,76 +15,49 @@ */ package org.thingsboard.server.service.edge.rpc.processor.device; -import com.datastax.oss.driver.api.core.uuid.Uuids; -import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.util.Pair; -import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.StringUtils; -import org.thingsboard.server.common.data.device.data.DeviceData; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; -import org.thingsboard.server.queue.util.DataDecodingEncodingService; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; -import java.util.Optional; -import java.util.UUID; - @Slf4j public abstract class BaseDeviceProcessor extends BaseEdgeProcessor { - @Autowired - protected DataDecodingEncodingService dataDecodingEncodingService; - - protected Pair saveOrUpdateDevice(TenantId tenantId, DeviceId deviceId, DeviceUpdateMsg deviceUpdateMsg, CustomerId customerId) { + protected Pair saveOrUpdateDevice(TenantId tenantId, DeviceId deviceId, DeviceUpdateMsg deviceUpdateMsg) { boolean created = false; boolean deviceNameUpdated = false; deviceCreationLock.lock(); try { - Device device = deviceService.findDeviceById(tenantId, deviceId); - String deviceName = deviceUpdateMsg.getName(); + Device device = constructDeviceFromUpdateMsg(tenantId, deviceId, deviceUpdateMsg); if (device == null) { + throw new RuntimeException("[{" + tenantId + "}] deviceUpdateMsg {" + deviceUpdateMsg + "} cannot be converted to device"); + } + Device deviceById = deviceService.findDeviceById(tenantId, deviceId); + if (deviceById == null) { created = true; - device = new Device(); - device.setTenantId(tenantId); - device.setCreatedTime(Uuids.unixTimestamp(deviceId.getId())); - Device deviceByName = deviceService.findDeviceByTenantIdAndName(tenantId, deviceName); - if (deviceByName != null) { - deviceName = deviceName + "_" + StringUtils.randomAlphabetic(15); - log.warn("Device with name {} already exists. Renaming device name to {}", - deviceUpdateMsg.getName(), deviceName); - deviceNameUpdated = true; - } + device.setId(null); + } else { + device.setId(deviceId); + } + String deviceName = device.getName(); + Device deviceByName = deviceService.findDeviceByTenantIdAndName(tenantId, deviceName); + if (deviceByName != null && !deviceByName.getId().equals(deviceId)) { + deviceName = deviceName + "_" + StringUtils.randomAlphabetic(15); + log.warn("[{}] Device with name {} already exists. Renaming device name to {}", + tenantId, device.getName(), deviceName); + deviceNameUpdated = true; } device.setName(deviceName); - device.setType(deviceUpdateMsg.getType()); - device.setLabel(deviceUpdateMsg.hasLabel() ? deviceUpdateMsg.getLabel() : null); - device.setAdditionalInfo(deviceUpdateMsg.hasAdditionalInfo() - ? JacksonUtil.toJsonNode(deviceUpdateMsg.getAdditionalInfo()) : null); - - UUID deviceProfileUUID = safeGetUUID(deviceUpdateMsg.getDeviceProfileIdMSB(), deviceUpdateMsg.getDeviceProfileIdLSB()); - device.setDeviceProfileId(deviceProfileUUID != null ? new DeviceProfileId(deviceProfileUUID) : null); - - device.setCustomerId(customerId); - - Optional deviceDataOpt = - dataDecodingEncodingService.decode(deviceUpdateMsg.getDeviceDataBytes().toByteArray()); - device.setDeviceData(deviceDataOpt.orElse(null)); + setCustomerId(tenantId, created ? null : deviceById.getCustomerId(), device, deviceUpdateMsg); - UUID firmwareUUID = safeGetUUID(deviceUpdateMsg.getFirmwareIdMSB(), deviceUpdateMsg.getFirmwareIdLSB()); - device.setFirmwareId(firmwareUUID != null ? new OtaPackageId(firmwareUUID) : null); - - UUID softwareUUID = safeGetUUID(deviceUpdateMsg.getSoftwareIdMSB(), deviceUpdateMsg.getSoftwareIdLSB()); - device.setSoftwareId(softwareUUID != null ? new OtaPackageId(softwareUUID) : null); deviceValidator.validate(device, Device::getTenantId); if (created) { device.setId(deviceId); @@ -97,37 +70,45 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor { deviceCredentials.setCredentialsId(StringUtils.randomAlphanumeric(20)); deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials); } - tbClusterService.onDeviceUpdated(savedDevice, created ? null : device, false); + tbClusterService.onDeviceUpdated(savedDevice, created ? null : device); + } catch (Exception e) { + log.error("[{}] Failed to process device update msg [{}]", tenantId, deviceUpdateMsg, e); + throw e; } finally { deviceCreationLock.unlock(); } return Pair.of(created, deviceNameUpdated); } - public ListenableFuture processDeviceCredentialsMsg(TenantId tenantId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) { - log.debug("[{}] Executing processDeviceCredentialsMsg, deviceCredentialsUpdateMsg [{}]", tenantId, deviceCredentialsUpdateMsg); - DeviceId deviceId = new DeviceId(new UUID(deviceCredentialsUpdateMsg.getDeviceIdMSB(), deviceCredentialsUpdateMsg.getDeviceIdLSB())); - return dbCallbackExecutorService.submit(() -> { - Device device = deviceService.findDeviceById(tenantId, deviceId); - if (device != null) { - log.debug("Updating device credentials for device [{}]. New device credentials Id [{}], value [{}]", - device.getName(), deviceCredentialsUpdateMsg.getCredentialsId(), deviceCredentialsUpdateMsg.getCredentialsValue()); - try { - DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, device.getId()); - deviceCredentials.setCredentialsType(DeviceCredentialsType.valueOf(deviceCredentialsUpdateMsg.getCredentialsType())); - deviceCredentials.setCredentialsId(deviceCredentialsUpdateMsg.getCredentialsId()); - deviceCredentials.setCredentialsValue(deviceCredentialsUpdateMsg.hasCredentialsValue() - ? deviceCredentialsUpdateMsg.getCredentialsValue() : null); - deviceCredentialsService.updateDeviceCredentials(tenantId, deviceCredentials); - } catch (Exception e) { - log.error("Can't update device credentials for device [{}], deviceCredentialsUpdateMsg [{}]", - device.getName(), deviceCredentialsUpdateMsg, e); - throw new RuntimeException(e); - } - } else { - log.warn("Can't find device by id [{}], deviceCredentialsUpdateMsg [{}]", deviceId, deviceCredentialsUpdateMsg); + protected void updateDeviceCredentials(TenantId tenantId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) { + DeviceCredentials deviceCredentials = constructDeviceCredentialsFromUpdateMsg(tenantId, deviceCredentialsUpdateMsg); + if (deviceCredentials == null) { + throw new RuntimeException("[{" + tenantId + "}] deviceCredentialsUpdateMsg {" + deviceCredentialsUpdateMsg + "} cannot be converted to device credentials"); + } + Device device = deviceService.findDeviceById(tenantId, deviceCredentials.getDeviceId()); + if (device != null) { + log.debug("[{}] Updating device credentials for device [{}]. New device credentials Id [{}], value [{}]", + tenantId, device.getName(), deviceCredentials.getCredentialsId(), deviceCredentials.getCredentialsValue()); + try { + DeviceCredentials deviceCredentialsByDeviceId = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, device.getId()); + deviceCredentialsByDeviceId.setCredentialsType(deviceCredentials.getCredentialsType()); + deviceCredentialsByDeviceId.setCredentialsId(deviceCredentials.getCredentialsId()); + deviceCredentialsByDeviceId.setCredentialsValue(deviceCredentials.getCredentialsValue()); + deviceCredentialsService.updateDeviceCredentials(tenantId, deviceCredentialsByDeviceId); + + } catch (Exception e) { + log.error("[{}] Can't update device credentials for device [{}], deviceCredentialsUpdateMsg [{}]", + tenantId, device.getName(), deviceCredentialsUpdateMsg, e); + throw new RuntimeException(e); } - return null; - }); + } else { + log.warn("[{}] Can't find device by id [{}], deviceCredentialsUpdateMsg [{}]", tenantId, deviceCredentials.getDeviceId(), deviceCredentialsUpdateMsg); + } } + + protected abstract Device constructDeviceFromUpdateMsg(TenantId tenantId, DeviceId deviceId, DeviceUpdateMsg deviceUpdateMsg); + + protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, Device device, DeviceUpdateMsg deviceUpdateMsg); + + protected abstract DeviceCredentials constructDeviceCredentialsFromUpdateMsg(TenantId tenantId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java index 3e617de8c6..7eff237dfb 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java @@ -15,14 +15,12 @@ */ package org.thingsboard.server.service.edge.rpc.processor.device; -import com.fasterxml.jackson.core.JsonProcessingException; 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.SettableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.data.util.Pair; -import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; @@ -33,44 +31,41 @@ import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; -import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EdgeId; -import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.relation.EntityRelation; -import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.rpc.RpcError; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; -import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponseActorMsg; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsRequestMsg; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg; import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueMsgMetadata; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg; +import org.thingsboard.server.service.edge.rpc.constructor.device.DeviceMsgConstructor; import java.util.UUID; -@Component @Slf4j -@TbCoreComponent -public class DeviceEdgeProcessor extends BaseDeviceProcessor { +public abstract class DeviceEdgeProcessor extends BaseDeviceProcessor implements DeviceProcessor { + @Override public ListenableFuture processDeviceMsgFromEdge(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg) { - log.trace("[{}] executing processDeviceMsgFromEdge [{}] from edge [{}]", tenantId, deviceUpdateMsg, edge.getName()); + log.trace("[{}] executing processDeviceMsgFromEdge [{}] from edge [{}]", tenantId, deviceUpdateMsg, edge.getId()); DeviceId deviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB())); try { + edgeSynchronizationManager.getEdgeId().set(edge.getId()); + switch (deviceUpdateMsg.getMsgType()) { case ENTITY_CREATED_RPC_MESSAGE: case ENTITY_UPDATED_RPC_MESSAGE: @@ -93,12 +88,26 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { } else { return Futures.immediateFailedFuture(e); } + } finally { + edgeSynchronizationManager.getEdgeId().remove(); + } + } + + @Override + public ListenableFuture processDeviceCredentialsMsgFromEdge(TenantId tenantId, EdgeId edgeId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) { + log.debug("[{}] Executing processDeviceCredentialsMsgFromEdge, deviceCredentialsUpdateMsg [{}]", tenantId, deviceCredentialsUpdateMsg); + try { + edgeSynchronizationManager.getEdgeId().set(edgeId); + + updateDeviceCredentials(tenantId, deviceCredentialsUpdateMsg); + } finally { + edgeSynchronizationManager.getEdgeId().remove(); } + return Futures.immediateFuture(null); } private void saveOrUpdateDevice(TenantId tenantId, DeviceId deviceId, DeviceUpdateMsg deviceUpdateMsg, Edge edge) { - CustomerId customerId = safeGetCustomerId(deviceUpdateMsg.getCustomerIdMSB(), deviceUpdateMsg.getCustomerIdLSB()); - Pair resultPair = super.saveOrUpdateDevice(tenantId, deviceId, deviceUpdateMsg, customerId); + Pair resultPair = super.saveOrUpdateDevice(tenantId, deviceId, deviceUpdateMsg); Boolean created = resultPair.getFirst(); if (created) { createRelationFromEdge(tenantId, edge.getId(), deviceId); @@ -111,52 +120,18 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { } } - private void createRelationFromEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId) { - EntityRelation relation = new EntityRelation(); - relation.setFrom(edgeId); - relation.setTo(entityId); - relation.setTypeGroup(RelationTypeGroup.COMMON); - relation.setType(EntityRelation.EDGE_TYPE); - relationService.saveRelation(tenantId, relation); - } - private void pushDeviceCreatedEventToRuleEngine(TenantId tenantId, Edge edge, DeviceId deviceId) { try { Device device = deviceService.findDeviceById(tenantId, deviceId); - ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(device); - TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId, device.getCustomerId(), - getActionTbMsgMetaData(edge, device.getCustomerId()), TbMsgDataType.JSON, JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode)); - tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, new TbQueueCallback() { - @Override - public void onSuccess(TbQueueMsgMetadata metadata) { - log.debug("Successfully send ENTITY_CREATED EVENT to rule engine [{}]", device); - } - - @Override - public void onFailure(Throwable t) { - log.debug("Failed to send ENTITY_CREATED EVENT to rule engine [{}]", device, t); - } - }); - } catch (JsonProcessingException | IllegalArgumentException e) { - log.warn("[{}] Failed to push device action to rule engine: {}", deviceId, DataConstants.ENTITY_CREATED, e); - } - } - - private TbMsgMetaData getActionTbMsgMetaData(Edge edge, CustomerId customerId) { - TbMsgMetaData metaData = getTbMsgMetaData(edge); - if (customerId != null && !customerId.isNullUid()) { - metaData.putValue("customerId", customerId.toString()); + String deviceAsString = JacksonUtil.toString(device); + TbMsgMetaData msgMetaData = getEdgeActionTbMsgMetaData(edge, device.getCustomerId()); + pushEntityEventToRuleEngine(tenantId, deviceId, device.getCustomerId(), TbMsgType.ENTITY_CREATED, deviceAsString, msgMetaData); + } catch (Exception e) { + log.warn("[{}][{}] Failed to push device action to rule engine: {}", tenantId, deviceId, TbMsgType.ENTITY_CREATED.name(), e); } - return metaData; - } - - private TbMsgMetaData getTbMsgMetaData(Edge edge) { - TbMsgMetaData metaData = new TbMsgMetaData(); - metaData.putValue("edgeId", edge.getId().toString()); - metaData.putValue("edgeName", edge.getName()); - return metaData; } + @Override public ListenableFuture processDeviceRpcCallFromEdge(TenantId tenantId, Edge edge, DeviceRpcCallMsg deviceRpcCallMsg) { log.trace("[{}] processDeviceRpcCallFromEdge [{}]", tenantId, deviceRpcCallMsg); if (deviceRpcCallMsg.hasResponseMsg()) { @@ -186,7 +161,7 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { @Override public void onFailure(Throwable t) { - log.error("Can't process push notification to core [{}]", deviceRpcCallMsg, t); + log.error("[{}] Can't process push notification to core [{}]", tenantId, deviceRpcCallMsg, t); futureToSet.setException(t); } }; @@ -216,29 +191,30 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { ObjectNode data = JacksonUtil.newObjectNode(); data.put("method", deviceRpcCallMsg.getRequestMsg().getMethod()); data.put("params", deviceRpcCallMsg.getRequestMsg().getParams()); - TbMsg tbMsg = TbMsg.newMsg(SessionMsgType.TO_SERVER_RPC_REQUEST.name(), deviceId, null, metaData, - TbMsgDataType.JSON, JacksonUtil.OBJECT_MAPPER.writeValueAsString(data)); + TbMsg tbMsg = TbMsg.newMsg(TbMsgType.TO_SERVER_RPC_REQUEST, deviceId, null, metaData, + TbMsgDataType.JSON, JacksonUtil.toString(data)); tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, new TbQueueCallback() { @Override public void onSuccess(TbQueueMsgMetadata metadata) { - log.debug("Successfully send TO_SERVER_RPC_REQUEST to rule engine [{}], deviceRpcCallMsg {}", - device, deviceRpcCallMsg); + log.debug("[{}] Successfully send TO_SERVER_RPC_REQUEST to rule engine [{}], deviceRpcCallMsg {}", + tenantId, device, deviceRpcCallMsg); } @Override public void onFailure(Throwable t) { - log.debug("Failed to send TO_SERVER_RPC_REQUEST to rule engine [{}], deviceRpcCallMsg {}", - device, deviceRpcCallMsg, t); + log.debug("[{}] Failed to send TO_SERVER_RPC_REQUEST to rule engine [{}], deviceRpcCallMsg {}", + tenantId, device, deviceRpcCallMsg, t); } }); - } catch (JsonProcessingException | IllegalArgumentException e) { - log.warn("[{}] Failed to push TO_SERVER_RPC_REQUEST to rule engine. deviceRpcCallMsg {}", deviceId, deviceRpcCallMsg, e); + } catch (IllegalArgumentException e) { + log.warn("[{}][{}] Failed to push TO_SERVER_RPC_REQUEST to rule engine. deviceRpcCallMsg {}", tenantId, deviceId, deviceRpcCallMsg, e); } return Futures.immediateFuture(null); } - public DownlinkMsg convertDeviceEventToDownlink(EdgeEvent edgeEvent) { + @Override + public DownlinkMsg convertDeviceEventToDownlink(EdgeEvent edgeEvent, EdgeId edgeId, EdgeVersion edgeVersion) { DeviceId deviceId = new DeviceId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; switch (edgeEvent.getAction()) { @@ -250,22 +226,26 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { Device device = deviceService.findDeviceById(edgeEvent.getTenantId(), deviceId); if (device != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - DeviceUpdateMsg deviceUpdateMsg = - deviceMsgConstructor.constructDeviceUpdatedMsg(msgType, device); + DeviceUpdateMsg deviceUpdateMsg = ((DeviceMsgConstructor) + deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructDeviceUpdatedMsg(msgType, device); DownlinkMsg.Builder builder = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDeviceUpdateMsg(deviceUpdateMsg); if (UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE.equals(msgType)) { DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(edgeEvent.getTenantId(), device.getDeviceProfileId()); - builder.addDeviceProfileUpdateMsg(deviceProfileMsgConstructor.constructDeviceProfileUpdatedMsg(msgType, deviceProfile)); + deviceProfile = checkIfDeviceProfileDefaultFieldsAssignedToEdge(edgeEvent.getTenantId(), edgeId, deviceProfile, edgeVersion); + builder.addDeviceProfileUpdateMsg(((DeviceMsgConstructor) + deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructDeviceProfileUpdatedMsg(msgType, deviceProfile)); } downlinkMsg = builder.build(); } break; case DELETED: case UNASSIGNED_FROM_EDGE: - DeviceUpdateMsg deviceUpdateMsg = - deviceMsgConstructor.constructDeviceDeleteMsg(deviceId); + DeviceUpdateMsg deviceUpdateMsg = ((DeviceMsgConstructor) + deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructDeviceDeleteMsg(deviceId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDeviceUpdateMsg(deviceUpdateMsg) @@ -274,8 +254,8 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { case CREDENTIALS_UPDATED: DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(edgeEvent.getTenantId(), deviceId); if (deviceCredentials != null) { - DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = - deviceMsgConstructor.constructDeviceCredentialsUpdatedMsg(deviceCredentials); + DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = ((DeviceMsgConstructor) + deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructDeviceCredentialsUpdatedMsg(deviceCredentials); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addDeviceCredentialsUpdateMsg(deviceCredentialsUpdateMsg) @@ -283,20 +263,18 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { } break; case RPC_CALL: - return convertRpcCallEventToDownlink(edgeEvent); + return DownlinkMsg.newBuilder() + .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) + .addDeviceRpcCallMsg(((DeviceMsgConstructor) + deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructDeviceRpcCallMsg(edgeEvent.getEntityId(), edgeEvent.getBody())) + .build(); case CREDENTIALS_REQUEST: return convertCredentialsRequestEventToDownlink(edgeEvent); } return downlinkMsg; } - private DownlinkMsg convertRpcCallEventToDownlink(EdgeEvent edgeEvent) { - return DownlinkMsg.newBuilder() - .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addDeviceRpcCallMsg(deviceMsgConstructor.constructDeviceRpcCallMsg(edgeEvent.getEntityId(), edgeEvent.getBody())) - .build(); - } - private DownlinkMsg convertCredentialsRequestEventToDownlink(EdgeEvent edgeEvent) { DeviceId deviceId = new DeviceId(edgeEvent.getEntityId()); DeviceCredentialsRequestMsg deviceCredentialsRequestMsg = DeviceCredentialsRequestMsg.newBuilder() @@ -308,8 +286,4 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor { .addDeviceCredentialsRequestMsg(deviceCredentialsRequestMsg); return builder.build(); } - - public ListenableFuture processDeviceNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { - return processEntityNotification(tenantId, edgeNotificationMsg); - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessorFactory.java new file mode 100644 index 0000000000..8b90010450 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.device; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessorFactory; + +@Component +@TbCoreComponent +public class DeviceEdgeProcessorFactory extends BaseEdgeProcessorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessorV1.java new file mode 100644 index 0000000000..98405ca44b --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessorV1.java @@ -0,0 +1,87 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.device; + +import com.datastax.oss.driver.api.core.uuid.Uuids; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.device.data.DeviceData; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.OtaPackageId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.common.data.security.DeviceCredentialsType; +import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; +import org.thingsboard.server.queue.util.DataDecodingEncodingService; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import java.util.Optional; +import java.util.UUID; + +@Component +@TbCoreComponent +public class DeviceEdgeProcessorV1 extends DeviceEdgeProcessor { + + @Autowired + private DataDecodingEncodingService dataDecodingEncodingService; + + @Override + protected Device constructDeviceFromUpdateMsg(TenantId tenantId, DeviceId deviceId, DeviceUpdateMsg deviceUpdateMsg) { + Device device = new Device(); + device.setTenantId(tenantId); + device.setCreatedTime(Uuids.unixTimestamp(deviceId.getId())); + device.setName(deviceUpdateMsg.getName()); + device.setType(deviceUpdateMsg.getType()); + device.setLabel(deviceUpdateMsg.hasLabel() ? deviceUpdateMsg.getLabel() : null); + device.setAdditionalInfo(deviceUpdateMsg.hasAdditionalInfo() + ? JacksonUtil.toJsonNode(deviceUpdateMsg.getAdditionalInfo()) : null); + + UUID deviceProfileUUID = safeGetUUID(deviceUpdateMsg.getDeviceProfileIdMSB(), deviceUpdateMsg.getDeviceProfileIdLSB()); + device.setDeviceProfileId(deviceProfileUUID != null ? new DeviceProfileId(deviceProfileUUID) : null); + + Optional deviceDataOpt = dataDecodingEncodingService.decode(deviceUpdateMsg.getDeviceDataBytes().toByteArray()); + device.setDeviceData(deviceDataOpt.orElse(null)); + + UUID firmwareUUID = safeGetUUID(deviceUpdateMsg.getFirmwareIdMSB(), deviceUpdateMsg.getFirmwareIdLSB()); + device.setFirmwareId(firmwareUUID != null ? new OtaPackageId(firmwareUUID) : null); + UUID softwareUUID = safeGetUUID(deviceUpdateMsg.getSoftwareIdMSB(), deviceUpdateMsg.getSoftwareIdLSB()); + device.setSoftwareId(softwareUUID != null ? new OtaPackageId(softwareUUID) : null); + + return device; + } + + @Override + protected DeviceCredentials constructDeviceCredentialsFromUpdateMsg(TenantId tenantId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) { + DeviceCredentials deviceCredentials = new DeviceCredentials(); + deviceCredentials.setDeviceId(new DeviceId(new UUID(deviceCredentialsUpdateMsg.getDeviceIdMSB(), deviceCredentialsUpdateMsg.getDeviceIdLSB()))); + deviceCredentials.setCredentialsType(DeviceCredentialsType.valueOf(deviceCredentialsUpdateMsg.getCredentialsType())); + deviceCredentials.setCredentialsId(deviceCredentialsUpdateMsg.getCredentialsId()); + deviceCredentials.setCredentialsValue(deviceCredentialsUpdateMsg.hasCredentialsValue() + ? deviceCredentialsUpdateMsg.getCredentialsValue() : null); + return deviceCredentials; + } + + @Override + protected void setCustomerId(TenantId tenantId, CustomerId customerId, Device device, DeviceUpdateMsg deviceUpdateMsg) { + CustomerId customerUUID = safeGetCustomerId(deviceUpdateMsg.getCustomerIdMSB(), deviceUpdateMsg.getCustomerIdLSB()); + device.setCustomerId(customerUUID != null ? customerUUID : customerId); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessorV2.java new file mode 100644 index 0000000000..c972bb3d3e --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessorV2.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.device; + +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Primary +@Component +@TbCoreComponent +public class DeviceEdgeProcessorV2 extends DeviceEdgeProcessor { + + @Override + protected Device constructDeviceFromUpdateMsg(TenantId tenantId, DeviceId deviceId, DeviceUpdateMsg deviceUpdateMsg) { + return JacksonUtil.fromString(deviceUpdateMsg.getEntity(), Device.class, true); + } + + @Override + protected DeviceCredentials constructDeviceCredentialsFromUpdateMsg(TenantId tenantId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) { + return JacksonUtil.fromString(deviceCredentialsUpdateMsg.getEntity(), DeviceCredentials.class, true); + } + + @Override + protected void setCustomerId(TenantId tenantId, CustomerId customerId, Device device, DeviceUpdateMsg deviceUpdateMsg) { + CustomerId customerUUID = device.getCustomerId() != null ? device.getCustomerId() : customerId; + device.setCustomerId(customerUUID); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProcessor.java new file mode 100644 index 0000000000..81d1d7f22a --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProcessor.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.device; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg; +import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.service.edge.rpc.processor.EdgeProcessor; + +public interface DeviceProcessor extends EdgeProcessor { + + ListenableFuture processDeviceMsgFromEdge(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg); + + ListenableFuture processDeviceCredentialsMsgFromEdge(TenantId tenantId, EdgeId edgeId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg); + + DownlinkMsg convertDeviceEventToDownlink(EdgeEvent edgeEvent, EdgeId edgeId, EdgeVersion edgeVersion); + + ListenableFuture processDeviceRpcCallFromEdge(TenantId tenantId, Edge edge, DeviceRpcCallMsg deviceRpcCallMsg); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java deleted file mode 100644 index 5ddfecfdb1..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.edge.rpc.processor.device; - -import com.google.common.util.concurrent.ListenableFuture; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.EdgeUtils; -import org.thingsboard.server.common.data.edge.EdgeEvent; -import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; -import org.thingsboard.server.gen.edge.v1.DownlinkMsg; -import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; - -@Component -@Slf4j -@TbCoreComponent -public class DeviceProfileEdgeProcessor extends BaseEdgeProcessor { - - public DownlinkMsg convertDeviceProfileEventToDownlink(EdgeEvent edgeEvent) { - DeviceProfileId deviceProfileId = new DeviceProfileId(edgeEvent.getEntityId()); - DownlinkMsg downlinkMsg = null; - switch (edgeEvent.getAction()) { - case ADDED: - case UPDATED: - DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(edgeEvent.getTenantId(), deviceProfileId); - if (deviceProfile != null) { - UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - DeviceProfileUpdateMsg deviceProfileUpdateMsg = - deviceProfileMsgConstructor.constructDeviceProfileUpdatedMsg(msgType, deviceProfile); - downlinkMsg = DownlinkMsg.newBuilder() - .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addDeviceProfileUpdateMsg(deviceProfileUpdateMsg) - .build(); - } - break; - case DELETED: - DeviceProfileUpdateMsg deviceProfileUpdateMsg = - deviceProfileMsgConstructor.constructDeviceProfileDeleteMsg(deviceProfileId); - downlinkMsg = DownlinkMsg.newBuilder() - .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addDeviceProfileUpdateMsg(deviceProfileUpdateMsg) - .build(); - break; - } - return downlinkMsg; - } - - public ListenableFuture processDeviceProfileNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { - return processEntityNotificationForAllEdges(tenantId, edgeNotificationMsg); - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/profile/BaseDeviceProfileProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/profile/BaseDeviceProfileProcessor.java new file mode 100644 index 0000000000..7104764a7b --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/profile/BaseDeviceProfileProcessor.java @@ -0,0 +1,86 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.device.profile; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.util.Pair; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; + +@Slf4j +public abstract class BaseDeviceProfileProcessor extends BaseEdgeProcessor { + + protected Pair saveOrUpdateDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId, DeviceProfileUpdateMsg deviceProfileUpdateMsg) { + boolean created = false; + boolean deviceProfileNameUpdated = false; + deviceCreationLock.lock(); + try { + DeviceProfile deviceProfile = constructDeviceProfileFromUpdateMsg(tenantId, deviceProfileId, deviceProfileUpdateMsg); + if (deviceProfile == null) { + throw new RuntimeException("[{" + tenantId + "}] deviceProfileUpdateMsg {" + deviceProfileUpdateMsg + "} cannot be converted to device profile"); + } + DeviceProfile deviceProfileById = deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId); + if (deviceProfileById == null) { + created = true; + deviceProfile.setId(null); + deviceProfile.setDefault(false); + } else { + deviceProfile.setId(deviceProfileId); + deviceProfile.setDefault(deviceProfileById.isDefault()); + } + String deviceProfileName = deviceProfile.getName(); + DeviceProfile deviceProfileByName = deviceProfileService.findDeviceProfileByName(tenantId, deviceProfileName); + if (deviceProfileByName != null && !deviceProfileByName.getId().equals(deviceProfileId)) { + deviceProfileName = deviceProfileName + "_" + StringUtils.randomAlphabetic(15); + log.warn("[{}] Device profile with name {} already exists. Renaming device profile name to {}", + tenantId, deviceProfile.getName(), deviceProfileName); + deviceProfileNameUpdated = true; + } + deviceProfile.setName(deviceProfileName); + + RuleChainId ruleChainId = deviceProfile.getDefaultRuleChainId(); + setDefaultRuleChainId(tenantId, deviceProfile, created ? null : deviceProfileById.getDefaultRuleChainId()); + setDefaultEdgeRuleChainId(deviceProfile, ruleChainId, deviceProfileUpdateMsg); + setDefaultDashboardId(tenantId, created ? null : deviceProfileById.getDefaultDashboardId(), deviceProfile, deviceProfileUpdateMsg); + + deviceProfileValidator.validate(deviceProfile, DeviceProfile::getTenantId); + if (created) { + deviceProfile.setId(deviceProfileId); + } + deviceProfileService.saveDeviceProfile(deviceProfile, false); + } catch (Exception e) { + log.error("[{}] Failed to process device profile update msg [{}]", tenantId, deviceProfileUpdateMsg, e); + throw e; + } finally { + deviceCreationLock.unlock(); + } + return Pair.of(created, deviceProfileNameUpdated); + } + + protected abstract DeviceProfile constructDeviceProfileFromUpdateMsg(TenantId tenantId, DeviceProfileId deviceProfileId, DeviceProfileUpdateMsg deviceProfileUpdateMsg); + + protected abstract void setDefaultRuleChainId(TenantId tenantId, DeviceProfile deviceProfile, RuleChainId ruleChainId); + + protected abstract void setDefaultEdgeRuleChainId(DeviceProfile deviceProfile, RuleChainId ruleChainId, DeviceProfileUpdateMsg deviceProfileUpdateMsg); + + protected abstract void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/profile/DeviceProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/profile/DeviceProfileEdgeProcessor.java new file mode 100644 index 0000000000..7012dff6b5 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/profile/DeviceProfileEdgeProcessor.java @@ -0,0 +1,125 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.device.profile; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.util.Pair; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.EdgeUtils; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.device.DeviceMsgConstructor; + +import java.util.UUID; + +@Slf4j +public abstract class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor implements DeviceProfileProcessor { + + @Override + public ListenableFuture processDeviceProfileMsgFromEdge(TenantId tenantId, Edge edge, DeviceProfileUpdateMsg deviceProfileUpdateMsg) { + log.trace("[{}] executing processDeviceProfileMsgFromEdge [{}] from edge [{}]", tenantId, deviceProfileUpdateMsg, edge.getId()); + DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(deviceProfileUpdateMsg.getIdMSB(), deviceProfileUpdateMsg.getIdLSB())); + try { + edgeSynchronizationManager.getEdgeId().set(edge.getId()); + + switch (deviceProfileUpdateMsg.getMsgType()) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + saveOrUpdateDeviceProfile(tenantId, deviceProfileId, deviceProfileUpdateMsg, edge); + return Futures.immediateFuture(null); + case ENTITY_DELETED_RPC_MESSAGE: + case UNRECOGNIZED: + default: + return handleUnsupportedMsgType(deviceProfileUpdateMsg.getMsgType()); + } + } catch (DataValidationException e) { + log.warn("[{}] Failed to process DeviceProfileUpdateMsg from Edge [{}]", tenantId, deviceProfileUpdateMsg, e); + return Futures.immediateFailedFuture(e); + } finally { + edgeSynchronizationManager.getEdgeId().remove(); + } + } + + private void saveOrUpdateDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, Edge edge) { + Pair resultPair = super.saveOrUpdateDeviceProfile(tenantId, deviceProfileId, deviceProfileUpdateMsg); + Boolean created = resultPair.getFirst(); + if (created) { + createRelationFromEdge(tenantId, edge.getId(), deviceProfileId); + pushDeviceProfileCreatedEventToRuleEngine(tenantId, edge, deviceProfileId); + } + Boolean deviceProfileNameUpdated = resultPair.getSecond(); + if (deviceProfileNameUpdated) { + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE_PROFILE, EdgeEventActionType.UPDATED, deviceProfileId, null); + } + } + + private void pushDeviceProfileCreatedEventToRuleEngine(TenantId tenantId, Edge edge, DeviceProfileId deviceProfileId) { + try { + DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId); + String deviceProfileAsString = JacksonUtil.toString(deviceProfile); + TbMsgMetaData msgMetaData = getEdgeActionTbMsgMetaData(edge, null); + pushEntityEventToRuleEngine(tenantId, deviceProfileId, null, TbMsgType.ENTITY_CREATED, deviceProfileAsString, msgMetaData); + } catch (Exception e) { + log.warn("[{}][{}] Failed to push device profile action to rule engine: {}", tenantId, deviceProfileId, TbMsgType.ENTITY_CREATED.name(), e); + } + } + + @Override + public DownlinkMsg convertDeviceProfileEventToDownlink(EdgeEvent edgeEvent, EdgeId edgeId, EdgeVersion edgeVersion) { + DeviceProfileId deviceProfileId = new DeviceProfileId(edgeEvent.getEntityId()); + DownlinkMsg downlinkMsg = null; + switch (edgeEvent.getAction()) { + case ADDED: + case UPDATED: + DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(edgeEvent.getTenantId(), deviceProfileId); + if (deviceProfile != null) { + UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); + deviceProfile = checkIfDeviceProfileDefaultFieldsAssignedToEdge(edgeEvent.getTenantId(), edgeId, deviceProfile, edgeVersion); + DeviceProfileUpdateMsg deviceProfileUpdateMsg = ((DeviceMsgConstructor) + deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructDeviceProfileUpdatedMsg(msgType, deviceProfile); + downlinkMsg = DownlinkMsg.newBuilder() + .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) + .addDeviceProfileUpdateMsg(deviceProfileUpdateMsg) + .build(); + } + break; + case DELETED: + DeviceProfileUpdateMsg deviceProfileUpdateMsg = ((DeviceMsgConstructor) + deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructDeviceProfileDeleteMsg(deviceProfileId); + downlinkMsg = DownlinkMsg.newBuilder() + .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) + .addDeviceProfileUpdateMsg(deviceProfileUpdateMsg) + .build(); + break; + } + return downlinkMsg; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/profile/DeviceProfileEdgeProcessorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/profile/DeviceProfileEdgeProcessorFactory.java new file mode 100644 index 0000000000..06e579e647 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/profile/DeviceProfileEdgeProcessorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.device.profile; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessorFactory; + +@Component +@TbCoreComponent +public class DeviceProfileEdgeProcessorFactory extends BaseEdgeProcessorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/profile/DeviceProfileEdgeProcessorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/profile/DeviceProfileEdgeProcessorV1.java new file mode 100644 index 0000000000..03461356df --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/profile/DeviceProfileEdgeProcessorV1.java @@ -0,0 +1,98 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.device.profile; + +import com.datastax.oss.driver.api.core.uuid.Uuids; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.DeviceProfileProvisionType; +import org.thingsboard.server.common.data.DeviceProfileType; +import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.OtaPackageId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; +import org.thingsboard.server.queue.util.DataDecodingEncodingService; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import java.nio.charset.StandardCharsets; +import java.util.Optional; +import java.util.UUID; + +@Component +@TbCoreComponent +public class DeviceProfileEdgeProcessorV1 extends DeviceProfileEdgeProcessor { + + @Autowired + private DataDecodingEncodingService dataDecodingEncodingService; + + @Override + protected DeviceProfile constructDeviceProfileFromUpdateMsg(TenantId tenantId, DeviceProfileId deviceProfileId, DeviceProfileUpdateMsg deviceProfileUpdateMsg) { + DeviceProfile deviceProfile = new DeviceProfile(); + deviceProfile.setTenantId(tenantId); + deviceProfile.setCreatedTime(Uuids.unixTimestamp(deviceProfileId.getId())); + deviceProfile.setName(deviceProfileUpdateMsg.getName()); + deviceProfile.setDefault(deviceProfileUpdateMsg.getDefault()); + deviceProfile.setDescription(deviceProfileUpdateMsg.hasDescription() ? deviceProfileUpdateMsg.getDescription() : null); + deviceProfile.setType(DeviceProfileType.valueOf(deviceProfileUpdateMsg.getType())); + deviceProfile.setTransportType(deviceProfileUpdateMsg.hasTransportType() + ? DeviceTransportType.valueOf(deviceProfileUpdateMsg.getTransportType()) : DeviceTransportType.DEFAULT); + deviceProfile.setImage(deviceProfileUpdateMsg.hasImage() + ? new String(deviceProfileUpdateMsg.getImage().toByteArray(), StandardCharsets.UTF_8) : null); + deviceProfile.setProvisionType(deviceProfileUpdateMsg.hasProvisionType() + ? DeviceProfileProvisionType.valueOf(deviceProfileUpdateMsg.getProvisionType()) : DeviceProfileProvisionType.DISABLED); + deviceProfile.setProvisionDeviceKey(deviceProfileUpdateMsg.hasProvisionDeviceKey() + ? deviceProfileUpdateMsg.getProvisionDeviceKey() : null); + deviceProfile.setDefaultQueueName(deviceProfileUpdateMsg.getDefaultQueueName()); + + Optional profileDataOpt = + dataDecodingEncodingService.decode(deviceProfileUpdateMsg.getProfileDataBytes().toByteArray()); + deviceProfile.setProfileData(profileDataOpt.orElse(null)); + + String defaultQueueName = StringUtils.isNotBlank(deviceProfileUpdateMsg.getDefaultQueueName()) + ? deviceProfileUpdateMsg.getDefaultQueueName() : null; + deviceProfile.setDefaultQueueName(defaultQueueName); + + UUID firmwareUUID = safeGetUUID(deviceProfileUpdateMsg.getFirmwareIdMSB(), deviceProfileUpdateMsg.getFirmwareIdLSB()); + deviceProfile.setFirmwareId(firmwareUUID != null ? new OtaPackageId(firmwareUUID) : null); + + UUID softwareUUID = safeGetUUID(deviceProfileUpdateMsg.getSoftwareIdMSB(), deviceProfileUpdateMsg.getSoftwareIdLSB()); + deviceProfile.setSoftwareId(softwareUUID != null ? new OtaPackageId(softwareUUID) : null); + return deviceProfile; + } + + @Override + protected void setDefaultRuleChainId(TenantId tenantId, DeviceProfile deviceProfile, RuleChainId ruleChainId) { + deviceProfile.setDefaultRuleChainId(ruleChainId); + } + + @Override + protected void setDefaultEdgeRuleChainId(DeviceProfile deviceProfile, RuleChainId ruleChainId, DeviceProfileUpdateMsg deviceProfileUpdateMsg) { + UUID defaultEdgeRuleChainUUID = safeGetUUID(deviceProfileUpdateMsg.getDefaultRuleChainIdMSB(), deviceProfileUpdateMsg.getDefaultRuleChainIdLSB()); + deviceProfile.setDefaultEdgeRuleChainId(defaultEdgeRuleChainUUID != null ? new RuleChainId(defaultEdgeRuleChainUUID) : ruleChainId); + } + + @Override + protected void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg) { + UUID defaultDashboardUUID = safeGetUUID(deviceProfileUpdateMsg.getDefaultDashboardIdMSB(), deviceProfileUpdateMsg.getDefaultDashboardIdLSB()); + deviceProfile.setDefaultDashboardId(defaultDashboardUUID != null ? new DashboardId(defaultDashboardUUID) : dashboardId); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/profile/DeviceProfileEdgeProcessorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/profile/DeviceProfileEdgeProcessorV2.java new file mode 100644 index 0000000000..bfbd7c36ef --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/profile/DeviceProfileEdgeProcessorV2.java @@ -0,0 +1,57 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.device.profile; + +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import java.util.UUID; + +@Primary +@Component +@TbCoreComponent +public class DeviceProfileEdgeProcessorV2 extends DeviceProfileEdgeProcessor { + + @Override + protected DeviceProfile constructDeviceProfileFromUpdateMsg(TenantId tenantId, DeviceProfileId deviceProfileId, DeviceProfileUpdateMsg deviceProfileUpdateMsg) { + return JacksonUtil.fromString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class, true); + } + + @Override + protected void setDefaultRuleChainId(TenantId tenantId, DeviceProfile deviceProfile, RuleChainId ruleChainId) { + deviceProfile.setDefaultRuleChainId(ruleChainId); + } + + @Override + protected void setDefaultEdgeRuleChainId(DeviceProfile deviceProfile, RuleChainId ruleChainId, DeviceProfileUpdateMsg deviceProfileUpdateMsg) { + UUID defaultEdgeRuleChainUUID = ruleChainId != null ? ruleChainId.getId() : null; + deviceProfile.setDefaultEdgeRuleChainId(defaultEdgeRuleChainUUID != null ? new RuleChainId(defaultEdgeRuleChainUUID) : null); + } + + @Override + protected void setDefaultDashboardId(TenantId tenantId, DashboardId dashboardId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg) { + UUID defaultDashboardUUID = deviceProfile.getDefaultDashboardId() != null ? deviceProfile.getDefaultDashboardId().getId() : (dashboardId != null ? dashboardId.getId() : null); + deviceProfile.setDefaultDashboardId(defaultDashboardUUID != null ? new DashboardId(defaultDashboardUUID) : null); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/profile/DeviceProfileProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/profile/DeviceProfileProcessor.java new file mode 100644 index 0000000000..354ef5924a --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/profile/DeviceProfileProcessor.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.device.profile; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.service.edge.rpc.processor.EdgeProcessor; + +public interface DeviceProfileProcessor extends EdgeProcessor { + + ListenableFuture processDeviceProfileMsgFromEdge(TenantId tenantId, Edge edge, DeviceProfileUpdateMsg deviceProfileUpdateMsg); + + DownlinkMsg convertDeviceProfileEventToDownlink(EdgeEvent edgeEvent, EdgeId edgeId, EdgeVersion edgeVersion); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/edge/EdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/edge/EdgeProcessor.java index 123209afc1..62756cb963 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/edge/EdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/edge/EdgeProcessor.java @@ -41,8 +41,8 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; -@Component @Slf4j +@Component @TbCoreComponent public class EdgeProcessor extends BaseEdgeProcessor { @@ -72,9 +72,9 @@ public class EdgeProcessor extends BaseEdgeProcessor { EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); switch (actionType) { case ASSIGNED_TO_CUSTOMER: - CustomerId customerId = JacksonUtil.OBJECT_MAPPER.readValue(edgeNotificationMsg.getBody(), CustomerId.class); + CustomerId customerId = JacksonUtil.fromString(edgeNotificationMsg.getBody(), CustomerId.class); Edge edge = edgeService.findEdgeById(tenantId, edgeId); - if (edge == null || customerId.isNullUid()) { + if (customerId != null && (edge == null || customerId.isNullUid())) { return Futures.immediateFuture(null); } List> futures = new ArrayList<>(); @@ -85,7 +85,7 @@ public class EdgeProcessor extends BaseEdgeProcessor { do { pageData = userService.findCustomerUsers(tenantId, customerId, pageLink); if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { - log.trace("[{}] [{}] user(s) are going to be added to edge.", edge.getId(), pageData.getData().size()); + log.trace("[{}][{}][{}] user(s) are going to be added to edge.", tenantId, edge.getId(), pageData.getData().size()); for (User user : pageData.getData()) { futures.add(saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.USER, EdgeEventActionType.ADDED, user.getId(), null)); } @@ -96,9 +96,9 @@ public class EdgeProcessor extends BaseEdgeProcessor { } while (pageData != null && pageData.hasNext()); return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService); case UNASSIGNED_FROM_CUSTOMER: - CustomerId customerIdToDelete = JacksonUtil.OBJECT_MAPPER.readValue(edgeNotificationMsg.getBody(), CustomerId.class); + CustomerId customerIdToDelete = JacksonUtil.fromString(edgeNotificationMsg.getBody(), CustomerId.class); edge = edgeService.findEdgeById(tenantId, edgeId); - if (edge == null || customerIdToDelete.isNullUid()) { + if (customerIdToDelete != null && (edge == null || customerIdToDelete.isNullUid())) { return Futures.immediateFuture(null); } return Futures.transformAsync(saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.EDGE, EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER, edgeId, null), @@ -108,7 +108,7 @@ public class EdgeProcessor extends BaseEdgeProcessor { return Futures.immediateFuture(null); } } catch (Exception e) { - log.error("Exception during processing edge event", e); + log.error("[{}] Exception during processing edge event", tenantId, e); return Futures.immediateFailedFuture(e); } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java new file mode 100644 index 0000000000..7365d6bc15 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java @@ -0,0 +1,67 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.entityview; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.util.Pair; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; + +@Slf4j +public abstract class BaseEntityViewProcessor extends BaseEdgeProcessor { + + protected Pair saveOrUpdateEntityView(TenantId tenantId, EntityViewId entityViewId, EntityViewUpdateMsg entityViewUpdateMsg) { + boolean created = false; + boolean entityViewNameUpdated = false; + EntityView entityView = constructEntityViewFromUpdateMsg(tenantId, entityViewId, entityViewUpdateMsg); + if (entityView == null) { + throw new RuntimeException("[{" + tenantId + "}] entityViewUpdateMsg {" + entityViewUpdateMsg + "} cannot be converted to entity view"); + } + EntityView entityViewById = entityViewService.findEntityViewById(tenantId, entityViewId); + if (entityViewById == null) { + created = true; + entityView.setId(null); + } else { + entityView.setId(entityViewId); + } + String entityViewName = entityView.getName(); + EntityView entityViewByName = entityViewService.findEntityViewByTenantIdAndName(tenantId, entityViewName); + if (entityViewByName != null && !entityViewByName.getId().equals(entityViewId)) { + entityViewName = entityViewName + "_" + StringUtils.randomAlphanumeric(15); + log.warn("[{}] Entity view with name {} already exists. Renaming entity view name to {}", + tenantId, entityView.getName(), entityViewName); + entityViewNameUpdated = true; + } + entityView.setName(entityViewName); + setCustomerId(tenantId, created ? null : entityViewById.getCustomerId(), entityView, entityViewUpdateMsg); + + entityViewValidator.validate(entityView, EntityView::getTenantId); + if (created) { + entityView.setId(entityViewId); + } + entityViewService.saveEntityView(entityView, false); + return Pair.of(created, entityViewNameUpdated); + } + + protected abstract EntityView constructEntityViewFromUpdateMsg(TenantId tenantId, EntityViewId entityViewId, EntityViewUpdateMsg entityViewUpdateMsg); + + protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, EntityView entityView, EntityViewUpdateMsg entityViewUpdateMsg); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java index 29965fcc69..3298dd6822 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java @@ -15,27 +15,93 @@ */ package org.thingsboard.server.service.edge.rpc.processor.entityview; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; +import org.springframework.data.util.Pair; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; +import org.thingsboard.server.service.edge.rpc.constructor.entityview.EntityViewMsgConstructor; + +import java.util.UUID; -@Component @Slf4j -@TbCoreComponent -public class EntityViewEdgeProcessor extends BaseEdgeProcessor { +public abstract class EntityViewEdgeProcessor extends BaseEntityViewProcessor implements EntityViewProcessor { + + @Override + public ListenableFuture processEntityViewMsgFromEdge(TenantId tenantId, Edge edge, EntityViewUpdateMsg entityViewUpdateMsg) { + log.trace("[{}] executing processEntityViewMsgFromEdge [{}] from edge [{}]", tenantId, entityViewUpdateMsg, edge.getId()); + EntityViewId entityViewId = new EntityViewId(new UUID(entityViewUpdateMsg.getIdMSB(), entityViewUpdateMsg.getIdLSB())); + try { + edgeSynchronizationManager.getEdgeId().set(edge.getId()); + + switch (entityViewUpdateMsg.getMsgType()) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + saveOrUpdateEntityView(tenantId, entityViewId, entityViewUpdateMsg, edge); + return Futures.immediateFuture(null); + case ENTITY_DELETED_RPC_MESSAGE: + EntityView entityViewToDelete = entityViewService.findEntityViewById(tenantId, entityViewId); + if (entityViewToDelete != null) { + entityViewService.unassignEntityViewFromEdge(tenantId, entityViewId, edge.getId()); + } + return Futures.immediateFuture(null); + case UNRECOGNIZED: + default: + return handleUnsupportedMsgType(entityViewUpdateMsg.getMsgType()); + } + } catch (DataValidationException e) { + if (e.getMessage().contains("limit reached")) { + log.warn("[{}] Number of allowed entity views violated {}", tenantId, entityViewUpdateMsg, e); + return Futures.immediateFuture(null); + } else { + return Futures.immediateFailedFuture(e); + } + } finally { + edgeSynchronizationManager.getEdgeId().remove(); + } + } + + private void saveOrUpdateEntityView(TenantId tenantId, EntityViewId entityViewId, EntityViewUpdateMsg entityViewUpdateMsg, Edge edge) { + Pair resultPair = super.saveOrUpdateEntityView(tenantId, entityViewId, entityViewUpdateMsg); + Boolean created = resultPair.getFirst(); + if (created) { + createRelationFromEdge(tenantId, edge.getId(), entityViewId); + pushEntityViewCreatedEventToRuleEngine(tenantId, edge, entityViewId); + entityViewService.assignEntityViewToEdge(tenantId, entityViewId, edge.getId()); + } + Boolean assetNameUpdated = resultPair.getSecond(); + if (assetNameUpdated) { + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ENTITY_VIEW, EdgeEventActionType.UPDATED, entityViewId, null); + } + } - public DownlinkMsg convertEntityViewEventToDownlink(EdgeEvent edgeEvent) { + private void pushEntityViewCreatedEventToRuleEngine(TenantId tenantId, Edge edge, EntityViewId entityViewId) { + try { + EntityView entityView = entityViewService.findEntityViewById(tenantId, entityViewId); + String entityViewAsString = JacksonUtil.toString(entityView); + TbMsgMetaData msgMetaData = getEdgeActionTbMsgMetaData(edge, entityView.getCustomerId()); + pushEntityEventToRuleEngine(tenantId, entityViewId, entityView.getCustomerId(), TbMsgType.ENTITY_CREATED, entityViewAsString, msgMetaData); + } catch (Exception e) { + log.warn("[{}][{}] Failed to push entity view action to rule engine: {}", tenantId, entityViewId, TbMsgType.ENTITY_CREATED.name(), e); + } + } + + public DownlinkMsg convertEntityViewEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { EntityViewId entityViewId = new EntityViewId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; switch (edgeEvent.getAction()) { @@ -47,8 +113,8 @@ public class EntityViewEdgeProcessor extends BaseEdgeProcessor { EntityView entityView = entityViewService.findEntityViewById(edgeEvent.getTenantId(), entityViewId); if (entityView != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - EntityViewUpdateMsg entityViewUpdateMsg = - entityViewMsgConstructor.constructEntityViewUpdatedMsg(msgType, entityView); + EntityViewUpdateMsg entityViewUpdateMsg = ((EntityViewMsgConstructor) + entityViewMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructEntityViewUpdatedMsg(msgType, entityView); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addEntityViewUpdateMsg(entityViewUpdateMsg) @@ -57,8 +123,8 @@ public class EntityViewEdgeProcessor extends BaseEdgeProcessor { break; case DELETED: case UNASSIGNED_FROM_EDGE: - EntityViewUpdateMsg entityViewUpdateMsg = - entityViewMsgConstructor.constructEntityViewDeleteMsg(entityViewId); + EntityViewUpdateMsg entityViewUpdateMsg = ((EntityViewMsgConstructor) + entityViewMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructEntityViewDeleteMsg(entityViewId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addEntityViewUpdateMsg(entityViewUpdateMsg) @@ -67,8 +133,4 @@ public class EntityViewEdgeProcessor extends BaseEdgeProcessor { } return downlinkMsg; } - - public ListenableFuture processEntityViewNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { - return processEntityNotification(tenantId, edgeNotificationMsg); - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewProcessor.java new file mode 100644 index 0000000000..67de0487a4 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewProcessor.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.entityview; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; +import org.thingsboard.server.service.edge.rpc.processor.EdgeProcessor; + +public interface EntityViewProcessor extends EdgeProcessor { + + ListenableFuture processEntityViewMsgFromEdge(TenantId tenantId, Edge edge, EntityViewUpdateMsg entityViewUpdateMsg); + + DownlinkMsg convertEntityViewEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewProcessorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewProcessorFactory.java new file mode 100644 index 0000000000..497ca47056 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewProcessorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.entityview; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessorFactory; + +@Component +@TbCoreComponent +public class EntityViewProcessorFactory extends BaseEdgeProcessorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewProcessorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewProcessorV1.java new file mode 100644 index 0000000000..8c07604a56 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewProcessorV1.java @@ -0,0 +1,65 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.entityview; + +import com.datastax.oss.driver.api.core.uuid.Uuids; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.EdgeEntityType; +import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import java.util.UUID; + +@Component +@TbCoreComponent +public class EntityViewProcessorV1 extends EntityViewEdgeProcessor { + + @Override + protected EntityView constructEntityViewFromUpdateMsg(TenantId tenantId, EntityViewId entityViewId, EntityViewUpdateMsg entityViewUpdateMsg) { + EntityView entityView = new EntityView(); + entityView.setTenantId(tenantId); + entityView.setCreatedTime(Uuids.unixTimestamp(entityViewId.getId())); + entityView.setName(entityViewUpdateMsg.getName()); + entityView.setType(entityViewUpdateMsg.getType()); + + entityView.setAdditionalInfo(entityViewUpdateMsg.hasAdditionalInfo() ? + JacksonUtil.toJsonNode(entityViewUpdateMsg.getAdditionalInfo()) : null); + + CustomerId customerId = safeGetCustomerId(entityViewUpdateMsg.getCustomerIdMSB(), entityViewUpdateMsg.getCustomerIdLSB()); + entityView.setCustomerId(customerId); + + UUID entityIdUUID = safeGetUUID(entityViewUpdateMsg.getEntityIdMSB(), entityViewUpdateMsg.getEntityIdLSB()); + if (EdgeEntityType.DEVICE.equals(entityViewUpdateMsg.getEntityType())) { + entityView.setEntityId(entityIdUUID != null ? new DeviceId(entityIdUUID) : null); + } else if (EdgeEntityType.ASSET.equals(entityViewUpdateMsg.getEntityType())) { + entityView.setEntityId(entityIdUUID != null ? new AssetId(entityIdUUID) : null); + } + return entityView; + } + + @Override + protected void setCustomerId(TenantId tenantId, CustomerId customerId, EntityView entityView, EntityViewUpdateMsg entityViewUpdateMsg) { + CustomerId customerUUID = safeGetCustomerId(entityViewUpdateMsg.getCustomerIdMSB(), entityViewUpdateMsg.getCustomerIdLSB()); + entityView.setCustomerId(customerUUID != null ? customerUUID : customerId); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewProcessorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewProcessorV2.java new file mode 100644 index 0000000000..2ced2b2549 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewProcessorV2.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.entityview; + +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Primary +@Component +@TbCoreComponent +public class EntityViewProcessorV2 extends EntityViewEdgeProcessor { + + @Override + protected EntityView constructEntityViewFromUpdateMsg(TenantId tenantId, EntityViewId entityViewId, EntityViewUpdateMsg entityViewUpdateMsg) { + return JacksonUtil.fromString(entityViewUpdateMsg.getEntity(), EntityView.class, true); + } + + @Override + protected void setCustomerId(TenantId tenantId, CustomerId customerId, EntityView entityView, EntityViewUpdateMsg entityViewUpdateMsg) { + CustomerId customerUUID = entityView.getCustomerId() != null ? entityView.getCustomerId() : customerId; + entityView.setCustomerId(customerUUID); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ota/OtaPackageEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ota/OtaPackageEdgeProcessor.java index 8206e0f1b0..247cfe5c15 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ota/OtaPackageEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/ota/OtaPackageEdgeProcessor.java @@ -15,19 +15,18 @@ */ package org.thingsboard.server.service.edge.rpc.processor.ota; -import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.id.OtaPackageId; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.ota.OtaPackageMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @Component @@ -35,7 +34,7 @@ import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @TbCoreComponent public class OtaPackageEdgeProcessor extends BaseEdgeProcessor { - public DownlinkMsg convertOtaPackageEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertOtaPackageEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { OtaPackageId otaPackageId = new OtaPackageId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; switch (edgeEvent.getAction()) { @@ -44,8 +43,8 @@ public class OtaPackageEdgeProcessor extends BaseEdgeProcessor { OtaPackage otaPackage = otaPackageService.findOtaPackageById(edgeEvent.getTenantId(), otaPackageId); if (otaPackage != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - OtaPackageUpdateMsg otaPackageUpdateMsg = - otaPackageMsgConstructor.constructOtaPackageUpdatedMsg(msgType, otaPackage); + OtaPackageUpdateMsg otaPackageUpdateMsg = ((OtaPackageMsgConstructor) + otaPackageMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructOtaPackageUpdatedMsg(msgType, otaPackage); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addOtaPackageUpdateMsg(otaPackageUpdateMsg) @@ -53,8 +52,8 @@ public class OtaPackageEdgeProcessor extends BaseEdgeProcessor { } break; case DELETED: - OtaPackageUpdateMsg otaPackageUpdateMsg = - otaPackageMsgConstructor.constructOtaPackageDeleteMsg(otaPackageId); + OtaPackageUpdateMsg otaPackageUpdateMsg = ((OtaPackageMsgConstructor) + otaPackageMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructOtaPackageDeleteMsg(otaPackageId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addOtaPackageUpdateMsg(otaPackageUpdateMsg) @@ -63,8 +62,4 @@ public class OtaPackageEdgeProcessor extends BaseEdgeProcessor { } return downlinkMsg; } - - public ListenableFuture processOtaPackageNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { - return processEntityNotificationForAllEdges(tenantId, edgeNotificationMsg); - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java index cbacaf9276..cd595c1699 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/queue/QueueEdgeProcessor.java @@ -15,27 +15,26 @@ */ package org.thingsboard.server.service.edge.rpc.processor.queue; -import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.id.QueueId; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.queue.QueueMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; -@Component @Slf4j +@Component @TbCoreComponent public class QueueEdgeProcessor extends BaseEdgeProcessor { - public DownlinkMsg convertQueueEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertQueueEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { QueueId queueId = new QueueId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; switch (edgeEvent.getAction()) { @@ -44,8 +43,8 @@ public class QueueEdgeProcessor extends BaseEdgeProcessor { Queue queue = queueService.findQueueById(edgeEvent.getTenantId(), queueId); if (queue != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - QueueUpdateMsg queueUpdateMsg = - queueMsgConstructor.constructQueueUpdatedMsg(msgType, queue); + QueueUpdateMsg queueUpdateMsg = ((QueueMsgConstructor) + queueMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructQueueUpdatedMsg(msgType, queue); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addQueueUpdateMsg(queueUpdateMsg) @@ -53,8 +52,8 @@ public class QueueEdgeProcessor extends BaseEdgeProcessor { } break; case DELETED: - QueueUpdateMsg queueDeleteMsg = - queueMsgConstructor.constructQueueDeleteMsg(queueId); + QueueUpdateMsg queueDeleteMsg = ((QueueMsgConstructor) + queueMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructQueueDeleteMsg(queueId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addQueueUpdateMsg(queueDeleteMsg) @@ -63,8 +62,4 @@ public class QueueEdgeProcessor extends BaseEdgeProcessor { } return downlinkMsg; } - - public ListenableFuture processQueueNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { - return processEntityNotificationForAllEdges(tenantId, edgeNotificationMsg); - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java index df683bbbe2..424d737c01 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java @@ -18,52 +18,34 @@ package org.thingsboard.server.service.edge.rpc.processor.relation; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.relation.EntityRelation; -import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; -import java.util.UUID; - @Slf4j public abstract class BaseRelationProcessor extends BaseEdgeProcessor { - public ListenableFuture processRelationMsg(TenantId tenantId, RelationUpdateMsg relationUpdateMsg) { - log.trace("[{}] processRelationFromEdge [{}]", tenantId, relationUpdateMsg); + protected ListenableFuture processRelationMsg(TenantId tenantId, RelationUpdateMsg relationUpdateMsg) { + log.trace("[{}] processRelationMsg [{}]", tenantId, relationUpdateMsg); try { - EntityRelation entityRelation = new EntityRelation(); - - UUID fromUUID = new UUID(relationUpdateMsg.getFromIdMSB(), relationUpdateMsg.getFromIdLSB()); - EntityId fromId = EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(relationUpdateMsg.getFromEntityType()), fromUUID); - entityRelation.setFrom(fromId); - - UUID toUUID = new UUID(relationUpdateMsg.getToIdMSB(), relationUpdateMsg.getToIdLSB()); - EntityId toId = EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(relationUpdateMsg.getToEntityType()), toUUID); - entityRelation.setTo(toId); - - entityRelation.setType(relationUpdateMsg.getType()); - entityRelation.setTypeGroup(relationUpdateMsg.hasTypeGroup() - ? RelationTypeGroup.valueOf(relationUpdateMsg.getTypeGroup()) : RelationTypeGroup.COMMON); - entityRelation.setAdditionalInfo(JacksonUtil.toJsonNode(relationUpdateMsg.getAdditionalInfo())); + EntityRelation entityRelation = constructEntityRelationFromUpdateMsg(relationUpdateMsg); + if (entityRelation == null) { + throw new RuntimeException("[{" + tenantId + "}] relationUpdateMsg {" + relationUpdateMsg + "} cannot be converted to entity relation"); + } switch (relationUpdateMsg.getMsgType()) { case ENTITY_CREATED_RPC_MESSAGE: case ENTITY_UPDATED_RPC_MESSAGE: if (isEntityExists(tenantId, entityRelation.getTo()) && isEntityExists(tenantId, entityRelation.getFrom())) { - return Futures.transform(relationService.saveRelationAsync(tenantId, entityRelation), - (result) -> null, dbCallbackExecutorService); + relationService.saveRelation(tenantId, entityRelation); } else { - log.warn("Skipping relating update msg because from/to entity doesn't exists on edge, {}", relationUpdateMsg); - return Futures.immediateFuture(null); + log.warn("[{}] Skipping relating update msg because from/to entity doesn't exists on edge, {}", tenantId, relationUpdateMsg); } + break; case ENTITY_DELETED_RPC_MESSAGE: - return Futures.transform(relationService.deleteRelationAsync(tenantId, entityRelation), - (result) -> null, dbCallbackExecutorService); + relationService.deleteRelation(tenantId, entityRelation); + break; case UNRECOGNIZED: default: return handleUnsupportedMsgType(relationUpdateMsg.getMsgType()); @@ -72,5 +54,8 @@ public abstract class BaseRelationProcessor extends BaseEdgeProcessor { log.error("[{}] Failed to process relation update msg [{}]", tenantId, relationUpdateMsg, e); return Futures.immediateFailedFuture(e); } + return Futures.immediateFuture(null); } + + protected abstract EntityRelation constructEntityRelationFromUpdateMsg(RelationUpdateMsg relationUpdateMsg); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java index 48192a3973..c59f5e7fc0 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessor.java @@ -15,14 +15,13 @@ */ package org.thingsboard.server.service.edge.rpc.processor.relation; -import com.fasterxml.jackson.core.JsonProcessingException; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; @@ -30,41 +29,54 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.relation.RelationMsgConstructor; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; -@Component @Slf4j -@TbCoreComponent -public class RelationEdgeProcessor extends BaseRelationProcessor { +public abstract class RelationEdgeProcessor extends BaseRelationProcessor implements RelationProcessor { - public DownlinkMsg convertRelationEventToDownlink(EdgeEvent edgeEvent) { - EntityRelation entityRelation = JacksonUtil.OBJECT_MAPPER.convertValue(edgeEvent.getBody(), EntityRelation.class); + @Override + public ListenableFuture processRelationMsgFromEdge(TenantId tenantId, Edge edge, RelationUpdateMsg relationUpdateMsg) { + log.trace("[{}] executing processRelationMsgFromEdge [{}] from edge [{}]", tenantId, relationUpdateMsg, edge.getId()); + try { + edgeSynchronizationManager.getEdgeId().set(edge.getId()); + return processRelationMsg(tenantId, relationUpdateMsg); + } finally { + edgeSynchronizationManager.getEdgeId().remove(); + } + } + + @Override + public DownlinkMsg convertRelationEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { + EntityRelation entityRelation = JacksonUtil.convertValue(edgeEvent.getBody(), EntityRelation.class); UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - RelationUpdateMsg relationUpdateMsg = relationMsgConstructor.constructRelationUpdatedMsg(msgType, entityRelation); + RelationUpdateMsg relationUpdateMsg = ((RelationMsgConstructor) relationMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructRelationUpdatedMsg(msgType, entityRelation); return DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addRelationUpdateMsg(relationUpdateMsg) .build(); } - public ListenableFuture processRelationNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) throws JsonProcessingException { - EntityRelation relation = JacksonUtil.OBJECT_MAPPER.readValue(edgeNotificationMsg.getBody(), EntityRelation.class); - if (relation.getFrom().getEntityType().equals(EntityType.EDGE) || - relation.getTo().getEntityType().equals(EntityType.EDGE)) { + public ListenableFuture processRelationNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { + EntityRelation relation = JacksonUtil.fromString(edgeNotificationMsg.getBody(), EntityRelation.class); + if (relation == null || (relation.getFrom().getEntityType().equals(EntityType.EDGE) || relation.getTo().getEntityType().equals(EntityType.EDGE))) { return Futures.immediateFuture(null); } + EdgeId originatorEdgeId = safeGetEdgeId(edgeNotificationMsg.getOriginatorEdgeIdMSB(), edgeNotificationMsg.getOriginatorEdgeIdLSB()); Set uniqueEdgeIds = new HashSet<>(); uniqueEdgeIds.addAll(edgeService.findAllRelatedEdgeIds(tenantId, relation.getTo())); uniqueEdgeIds.addAll(edgeService.findAllRelatedEdgeIds(tenantId, relation.getFrom())); + uniqueEdgeIds.remove(originatorEdgeId); if (uniqueEdgeIds.isEmpty()) { return Futures.immediateFuture(null); } @@ -75,7 +87,7 @@ public class RelationEdgeProcessor extends BaseRelationProcessor { EdgeEventType.RELATION, EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()), null, - JacksonUtil.OBJECT_MAPPER.valueToTree(relation))); + JacksonUtil.valueToTree(relation))); } return Futures.transform(Futures.allAsList(futures), voids -> null, dbCallbackExecutorService); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessorFactory.java new file mode 100644 index 0000000000..534d5b3e91 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.relation; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessorFactory; + +@Component +@TbCoreComponent +public class RelationEdgeProcessorFactory extends BaseEdgeProcessorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessorV1.java new file mode 100644 index 0000000000..c5db9ef801 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessorV1.java @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.relation; + +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +import java.util.UUID; + +@Component +@TbCoreComponent +public class RelationEdgeProcessorV1 extends RelationEdgeProcessor { + + @Override + protected EntityRelation constructEntityRelationFromUpdateMsg(RelationUpdateMsg relationUpdateMsg) { + EntityRelation entityRelation = new EntityRelation(); + + UUID fromUUID = new UUID(relationUpdateMsg.getFromIdMSB(), relationUpdateMsg.getFromIdLSB()); + EntityId fromId = EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(relationUpdateMsg.getFromEntityType()), fromUUID); + entityRelation.setFrom(fromId); + + UUID toUUID = new UUID(relationUpdateMsg.getToIdMSB(), relationUpdateMsg.getToIdLSB()); + EntityId toId = EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(relationUpdateMsg.getToEntityType()), toUUID); + entityRelation.setTo(toId); + + entityRelation.setType(relationUpdateMsg.getType()); + entityRelation.setTypeGroup(relationUpdateMsg.hasTypeGroup() + ? RelationTypeGroup.valueOf(relationUpdateMsg.getTypeGroup()) : RelationTypeGroup.COMMON); + entityRelation.setAdditionalInfo(JacksonUtil.toJsonNode(relationUpdateMsg.getAdditionalInfo())); + return entityRelation; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessorV2.java new file mode 100644 index 0000000000..4347280115 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationEdgeProcessorV2.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.relation; + +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Primary +@Component +@TbCoreComponent +public class RelationEdgeProcessorV2 extends RelationEdgeProcessor { + + @Override + protected EntityRelation constructEntityRelationFromUpdateMsg(RelationUpdateMsg relationUpdateMsg) { + return JacksonUtil.fromString(relationUpdateMsg.getEntity(), EntityRelation.class, true); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationProcessor.java new file mode 100644 index 0000000000..765d9914cc --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/RelationProcessor.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.relation; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; +import org.thingsboard.server.service.edge.rpc.processor.EdgeProcessor; + +public interface RelationProcessor extends EdgeProcessor { + + ListenableFuture processRelationMsgFromEdge(TenantId tenantId, Edge edge, RelationUpdateMsg relationUpdateMsg); + + DownlinkMsg convertRelationEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java new file mode 100644 index 0000000000..a70c6eaaad --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/BaseResourceProcessor.java @@ -0,0 +1,75 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.resource; + +import com.datastax.oss.driver.api.core.uuid.Uuids; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageDataIterable; +import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; + +@Slf4j +public abstract class BaseResourceProcessor extends BaseEdgeProcessor { + + protected boolean saveOrUpdateTbResource(TenantId tenantId, TbResourceId tbResourceId, ResourceUpdateMsg resourceUpdateMsg) { + boolean resourceKeyUpdated = false; + try { + TbResource resource = constructResourceFromUpdateMsg(tenantId, tbResourceId, resourceUpdateMsg); + if (resource == null) { + throw new RuntimeException("[{" + tenantId + "}] resourceUpdateMsg {" + resourceUpdateMsg + " } cannot be converted to resource"); + } + boolean created = false; + TbResource resourceById = resourceService.findResourceById(tenantId, tbResourceId); + if (resourceById == null) { + resource.setCreatedTime(Uuids.unixTimestamp(tbResourceId.getId())); + created = true; + resource.setId(null); + } else { + resource.setId(tbResourceId); + } + String resourceKey = resource.getResourceKey(); + ResourceType resourceType = resource.getResourceType(); + PageDataIterable resourcesIterable = new PageDataIterable<>( + link -> resourceService.findTenantResourcesByResourceTypeAndPageLink(tenantId, resourceType, link), 1024); + for (TbResource tbResource : resourcesIterable) { + if (tbResource.getResourceKey().equals(resourceKey) && !tbResourceId.equals(tbResource.getId())) { + resourceKey = StringUtils.randomAlphabetic(15) + "_" + resourceKey; + log.warn("[{}] Resource with resource type {} and key {} already exists. Renaming resource key to {}", + tenantId, resourceType, resource.getResourceKey(), resourceKey); + resourceKeyUpdated = true; + } + } + resource.setResourceKey(resourceKey); + resourceValidator.validate(resource, TbResourceInfo::getTenantId); + if (created) { + resource.setId(tbResourceId); + } + resourceService.saveResource(resource, false); + } catch (Exception e) { + log.error("[{}] Failed to process resource update msg [{}]", tenantId, resourceUpdateMsg, e); + throw e; + } + return resourceKeyUpdated; + } + + protected abstract TbResource constructResourceFromUpdateMsg(TenantId tenantId, TbResourceId tbResourceId, ResourceUpdateMsg resourceUpdateMsg); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java new file mode 100644 index 0000000000..f0da087472 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessor.java @@ -0,0 +1,101 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.resource; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.EdgeUtils; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.resource.ResourceMsgConstructor; + +import java.util.UUID; + +@Slf4j +public abstract class ResourceEdgeProcessor extends BaseResourceProcessor implements ResourceProcessor { + + @Override + public ListenableFuture processResourceMsgFromEdge(TenantId tenantId, Edge edge, ResourceUpdateMsg resourceUpdateMsg) { + TbResourceId tbResourceId = new TbResourceId(new UUID(resourceUpdateMsg.getIdMSB(), resourceUpdateMsg.getIdLSB())); + try { + edgeSynchronizationManager.getEdgeId().set(edge.getId()); + + switch (resourceUpdateMsg.getMsgType()) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + boolean resourceKeyUpdated = super.saveOrUpdateTbResource(tenantId, tbResourceId, resourceUpdateMsg); + if (resourceKeyUpdated) { + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.TB_RESOURCE, EdgeEventActionType.UPDATED, tbResourceId, null); + } + break; + case ENTITY_DELETED_RPC_MESSAGE: + case UNRECOGNIZED: + return handleUnsupportedMsgType(resourceUpdateMsg.getMsgType()); + } + } catch (DataValidationException e) { + if (e.getMessage().contains("exceeds the maximum")) { + log.warn("[{}] Resource data size has been exhausted {}", tenantId, resourceUpdateMsg, e); + return Futures.immediateFuture(null); + } else { + return Futures.immediateFailedFuture(e); + } + } finally { + edgeSynchronizationManager.getEdgeId().remove(); + } + return Futures.immediateFuture(null); + } + + @Override + public DownlinkMsg convertResourceEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { + TbResourceId tbResourceId = new TbResourceId(edgeEvent.getEntityId()); + DownlinkMsg downlinkMsg = null; + switch (edgeEvent.getAction()) { + case ADDED: + case UPDATED: + TbResource tbResource = resourceService.findResourceById(edgeEvent.getTenantId(), tbResourceId); + if (tbResource != null) { + UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); + ResourceUpdateMsg resourceUpdateMsg = ((ResourceMsgConstructor) + resourceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructResourceUpdatedMsg(msgType, tbResource); + downlinkMsg = resourceUpdateMsg != null ? DownlinkMsg.newBuilder() + .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) + .addResourceUpdateMsg(resourceUpdateMsg) + .build() : null; + } + break; + case DELETED: + ResourceUpdateMsg resourceUpdateMsg = ((ResourceMsgConstructor) + resourceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructResourceDeleteMsg(tbResourceId); + downlinkMsg = DownlinkMsg.newBuilder() + .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) + .addResourceUpdateMsg(resourceUpdateMsg) + .build(); + break; + } + return downlinkMsg; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessorFactory.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessorFactory.java new file mode 100644 index 0000000000..75b80a5ba1 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessorFactory.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.resource; + +import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessorFactory; + +@Component +@TbCoreComponent +public class ResourceEdgeProcessorFactory extends BaseEdgeProcessorFactory { + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessorV1.java new file mode 100644 index 0000000000..56b1d5507c --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessorV1.java @@ -0,0 +1,48 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.resource; + +import com.datastax.oss.driver.api.core.uuid.Uuids; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Component +@TbCoreComponent +public class ResourceEdgeProcessorV1 extends ResourceEdgeProcessor { + + @Override + protected TbResource constructResourceFromUpdateMsg(TenantId tenantId, TbResourceId tbResourceId, ResourceUpdateMsg resourceUpdateMsg) { + TbResource resource = new TbResource(); + if (resourceUpdateMsg.getIsSystem()) { + resource.setTenantId(TenantId.SYS_TENANT_ID); + } else { + resource.setTenantId(tenantId); + } + resource.setCreatedTime(Uuids.unixTimestamp(tbResourceId.getId())); + resource.setTitle(resourceUpdateMsg.getTitle()); + resource.setResourceKey(resourceUpdateMsg.getResourceKey()); + resource.setResourceType(ResourceType.valueOf(resourceUpdateMsg.getResourceType())); + resource.setFileName(resourceUpdateMsg.getFileName()); + resource.setEncodedData(resourceUpdateMsg.hasData() ? resourceUpdateMsg.getData() : null); + resource.setEtag(resourceUpdateMsg.hasEtag() ? resourceUpdateMsg.getEtag() : null); + return resource; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessorV2.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessorV2.java new file mode 100644 index 0000000000..797e1e6789 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceEdgeProcessorV2.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.resource; + +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Primary +@Component +@TbCoreComponent +public class ResourceEdgeProcessorV2 extends ResourceEdgeProcessor { + + @Override + protected TbResource constructResourceFromUpdateMsg(TenantId tenantId, TbResourceId tbResourceId, ResourceUpdateMsg resourceUpdateMsg) { + return JacksonUtil.fromString(resourceUpdateMsg.getEntity(), TbResource.class, true); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceProcessor.java new file mode 100644 index 0000000000..427b966ed2 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/resource/ResourceProcessor.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.resource; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; +import org.thingsboard.server.service.edge.rpc.processor.EdgeProcessor; + +public interface ResourceProcessor extends EdgeProcessor { + + ListenableFuture processResourceMsgFromEdge(TenantId tenantId, Edge edge, ResourceUpdateMsg resourceUpdateMsg); + + DownlinkMsg convertResourceEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java index 69d4f7ef64..670ee87ec6 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java @@ -15,13 +15,11 @@ */ package org.thingsboard.server.service.edge.rpc.processor.rule; -import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.id.RuleChainId; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; @@ -29,18 +27,18 @@ import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; import static org.thingsboard.server.service.edge.DefaultEdgeNotificationService.EDGE_IS_ROOT_BODY_KEY; -@Component @Slf4j +@Component @TbCoreComponent public class RuleChainEdgeProcessor extends BaseEdgeProcessor { - public DownlinkMsg convertRuleChainEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertRuleChainEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { RuleChainId ruleChainId = new RuleChainId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; switch (edgeEvent.getAction()) { @@ -56,8 +54,9 @@ public class RuleChainEdgeProcessor extends BaseEdgeProcessor { } catch (Exception ignored) {} } UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - RuleChainUpdateMsg ruleChainUpdateMsg = - ruleChainMsgConstructor.constructRuleChainUpdatedMsg(msgType, ruleChain, isRoot); + RuleChainUpdateMsg ruleChainUpdateMsg = ((RuleChainMsgConstructor) + ruleChainMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructRuleChainUpdatedMsg(msgType, ruleChain, isRoot); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addRuleChainUpdateMsg(ruleChainUpdateMsg) @@ -68,7 +67,8 @@ public class RuleChainEdgeProcessor extends BaseEdgeProcessor { case UNASSIGNED_FROM_EDGE: downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addRuleChainUpdateMsg(ruleChainMsgConstructor.constructRuleChainDeleteMsg(ruleChainId)) + .addRuleChainUpdateMsg(((RuleChainMsgConstructor) ruleChainMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructRuleChainDeleteMsg(ruleChainId)) .build(); break; } @@ -82,8 +82,9 @@ public class RuleChainEdgeProcessor extends BaseEdgeProcessor { if (ruleChain != null) { RuleChainMetaData ruleChainMetaData = ruleChainService.loadRuleChainMetaData(edgeEvent.getTenantId(), ruleChainId); UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); - RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = - ruleChainMsgConstructor.constructRuleChainMetadataUpdatedMsg(edgeEvent.getTenantId(), msgType, ruleChainMetaData, edgeVersion); + RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = ((RuleChainMsgConstructor) + ruleChainMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructRuleChainMetadataUpdatedMsg(edgeEvent.getTenantId(), msgType, ruleChainMetaData, edgeVersion); if (ruleChainMetadataUpdateMsg != null) { downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) @@ -93,8 +94,4 @@ public class RuleChainEdgeProcessor extends BaseEdgeProcessor { } return downlinkMsg; } - - public ListenableFuture processRuleChainNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { - return processEntityNotification(tenantId, edgeNotificationMsg); - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/settings/AdminSettingsEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/settings/AdminSettingsEdgeProcessor.java index 6bd21e0796..c787c28d9f 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/settings/AdminSettingsEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/settings/AdminSettingsEdgeProcessor.java @@ -23,21 +23,26 @@ import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.settings.AdminSettingsMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; -@Component @Slf4j +@Component @TbCoreComponent public class AdminSettingsEdgeProcessor extends BaseEdgeProcessor { - public DownlinkMsg convertAdminSettingsEventToDownlink(EdgeEvent edgeEvent) { - AdminSettings adminSettings = JacksonUtil.OBJECT_MAPPER.convertValue(edgeEvent.getBody(), AdminSettings.class); - AdminSettingsUpdateMsg adminSettingsUpdateMsg = adminSettingsMsgConstructor.constructAdminSettingsUpdateMsg(adminSettings); + public DownlinkMsg convertAdminSettingsEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { + AdminSettings adminSettings = JacksonUtil.convertValue(edgeEvent.getBody(), AdminSettings.class); + if (adminSettings == null) { + return null; + } + AdminSettingsUpdateMsg adminSettingsUpdateMsg = ((AdminSettingsMsgConstructor) + adminSettingsMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructAdminSettingsUpdateMsg(adminSettings); return DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addAdminSettingsUpdateMsg(adminSettingsUpdateMsg) .build(); } - } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java index fece86b92a..9cea8050b6 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java @@ -15,21 +15,18 @@ */ package org.thingsboard.server.service.edge.rpc.processor.telemetry; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; import com.google.gson.Gson; -import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; @@ -50,12 +47,13 @@ import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.common.adaptor.JsonConverter; +import org.thingsboard.server.common.msg.rule.engine.DeviceAttributesEventNotificationMsg; import org.thingsboard.server.common.transport.util.JsonUtils; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.gen.edge.v1.AttributeDeleteMsg; @@ -130,7 +128,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { result.add(processAttributeDeleteMsg(tenantId, entityId, entityData.getAttributeDeleteMsg(), entityData.getEntityType())); } } else { - log.warn("Skipping telemetry update msg because entity doesn't exists on edge, {}", entityData); + log.warn("[{}] Skipping telemetry update msg because entity doesn't exists on edge, {}", tenantId, entityData); } return result; } @@ -172,7 +170,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { } break; default: - log.debug("Using empty metadata for entityId [{}]", entityId); + log.debug("[{}] Using empty metadata for entityId [{}]", tenantId, entityId); break; } return new ImmutablePair<>(metaData, customerId != null ? customerId : new CustomerId(ModelConstants.NULL_UUID)); @@ -184,7 +182,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList()); metaData.putValue("ts", tsKv.getTs() + ""); var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); - TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null); + TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), TbMsgType.POST_TELEMETRY_REQUEST, entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null); tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { @Override public void onSuccess(TbQueueMsgMetadata metadata) { @@ -193,7 +191,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { @Override public void onFailure(Throwable t) { - log.error("Can't process post telemetry [{}]", msg, t); + log.error("[{}] Can't process post telemetry [{}]", tenantId, msg, t); futureToSet.setException(t); } }); @@ -207,7 +205,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { if (EntityType.DEVICE.equals(entityId.getEntityType())) { DeviceProfile deviceProfile = deviceProfileCache.get(tenantId, new DeviceId(entityId.getId())); if (deviceProfile == null) { - log.warn("[{}] Device profile is null!", entityId); + log.warn("[{}][{}] Device profile is null!", tenantId, entityId); } else { ruleChainId = deviceProfile.getDefaultRuleChainId(); queueName = deviceProfile.getDefaultQueueName(); @@ -215,7 +213,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { } else if (EntityType.ASSET.equals(entityId.getEntityType())) { AssetProfile assetProfile = assetProfileCache.get(tenantId, new AssetId(entityId.getId())); if (assetProfile == null) { - log.warn("[{}] Asset profile is null!", entityId); + log.warn("[{}][{}] Asset profile is null!", tenantId, entityId); } else { ruleChainId = assetProfile.getDefaultRuleChainId(); queueName = assetProfile.getDefaultQueueName(); @@ -228,7 +226,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { SettableFuture futureToSet = SettableFuture.create(); JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); - TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null); + TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null); tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { @Override public void onSuccess(TbQueueMsgMetadata metadata) { @@ -237,7 +235,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { @Override public void onFailure(Throwable t) { - log.error("Can't process post attributes [{}]", msg, t); + log.error("[{}] Can't process post attributes [{}]", tenantId, msg, t); futureToSet.setException(t); } }); @@ -257,7 +255,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { @Override public void onSuccess(@Nullable Void tmp) { var defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); - TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), DataConstants.ATTRIBUTES_UPDATED, entityId, + TbMsg tbMsg = TbMsg.newMsg(defaultQueueAndRuleChain.getKey(), TbMsgType.ATTRIBUTES_UPDATED, entityId, customerId, metaData, gson.toJson(json), defaultQueueAndRuleChain.getValue(), null); tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { @Override @@ -267,7 +265,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { @Override public void onFailure(Throwable t) { - log.error("Can't process attributes update [{}]", msg, t); + log.error("[{}] Can't process attributes update [{}]", tenantId, msg, t); futureToSet.setException(t); } }); @@ -275,7 +273,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { @Override public void onFailure(Throwable t) { - log.error("Can't process attributes update [{}]", msg, t); + log.error("[{}] Can't process attributes update [{}]", tenantId, msg, t); futureToSet.setException(t); } }); @@ -300,7 +298,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { @Override public void onFailure(Throwable t) { - log.error("Can't process attribute delete msg [{}]", attributeDeleteMsg, t); + log.error("[{}] Can't process attribute delete msg [{}]", tenantId, attributeDeleteMsg, t); futureToSet.setException(t); } }); @@ -311,10 +309,11 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { }, dbCallbackExecutorService); } - public EntityDataProto convertTelemetryEventToEntityDataProto(EntityType entityType, + public EntityDataProto convertTelemetryEventToEntityDataProto(TenantId tenantId, + EntityType entityType, UUID entityUUID, EdgeEventActionType actionType, - JsonNode body) throws JsonProcessingException { + JsonNode body) { EntityId entityId; switch (entityType) { case DEVICE: @@ -342,11 +341,11 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor { entityId = new EdgeId(entityUUID); break; default: - log.warn("Unsupported edge event type [{}]", entityType); + log.warn("[{}] Unsupported edge event type [{}]", tenantId, entityType); return null; } - JsonElement entityData = JsonParser.parseString(JacksonUtil.OBJECT_MAPPER.writeValueAsString(body)); - return entityDataMsgConstructor.constructEntityDataMsg(entityId, actionType, entityData); + String bodyJackson = JacksonUtil.toString(body); + return bodyJackson == null ? null : + entityDataMsgConstructor.constructEntityDataMsg(tenantId, entityId, actionType, JsonParser.parseString(bodyJackson)); } - } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java index 66c5596545..f657a96bdb 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java @@ -15,19 +15,19 @@ */ package org.thingsboard.server.service.edge.rpc.processor.telemetry; -import com.fasterxml.jackson.core.JsonProcessingException; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.EntityDataProto; import org.thingsboard.server.queue.util.TbCoreComponent; -@Component @Slf4j +@Component @TbCoreComponent public class TelemetryEdgeProcessor extends BaseTelemetryProcessor { @@ -36,9 +36,19 @@ public class TelemetryEdgeProcessor extends BaseTelemetryProcessor { return DataConstants.EDGE_MSG_SOURCE; } - public DownlinkMsg convertTelemetryEventToDownlink(EdgeEvent edgeEvent) throws JsonProcessingException { + public DownlinkMsg convertTelemetryEventToDownlink(EdgeEvent edgeEvent) { + if (edgeEvent.getBody() != null) { + String bodyStr = edgeEvent.getBody().toString(); + if (bodyStr.length() > 1000) { + log.debug("[{}][{}][{}] Conversion to a DownlinkMsg telemetry event failed due to a size limit violation. " + + "Current size is {}, but the limit is 1000. {}", edgeEvent.getTenantId(), edgeEvent.getEdgeId(), + edgeEvent.getEntityId(), bodyStr.length(), StringUtils.truncate(bodyStr, 100)); + return null; + } + } EntityType entityType = EntityType.valueOf(edgeEvent.getType().name()); - EntityDataProto entityDataProto = convertTelemetryEventToEntityDataProto(entityType, edgeEvent.getEntityId(), + EntityDataProto entityDataProto = convertTelemetryEventToEntityDataProto( + edgeEvent.getTenantId(), entityType, edgeEvent.getEntityId(), edgeEvent.getAction(), edgeEvent.getBody()); return DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java new file mode 100644 index 0000000000..927e47df82 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java @@ -0,0 +1,63 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.tenant; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EdgeUtils; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.tenant.TenantMsgConstructor; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; + +@Slf4j +@Component +@TbCoreComponent +public class TenantEdgeProcessor extends BaseEdgeProcessor { + + public DownlinkMsg convertTenantEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { + TenantId tenantId = new TenantId(edgeEvent.getEntityId()); + DownlinkMsg downlinkMsg = null; + if (EdgeEventActionType.UPDATED.equals(edgeEvent.getAction())) { + Tenant tenant = tenantService.findTenantById(tenantId); + if (tenant != null) { + UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); + TenantUpdateMsg tenantUpdateMsg = ((TenantMsgConstructor) + tenantMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructTenantUpdateMsg(msgType, tenant); + TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(tenantId, tenant.getTenantProfileId()); + TenantProfileUpdateMsg tenantProfileUpdateMsg = ((TenantMsgConstructor) + tenantMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructTenantProfileUpdateMsg(msgType, tenantProfile, edgeVersion); + downlinkMsg = DownlinkMsg.newBuilder() + .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) + .addTenantUpdateMsg(tenantUpdateMsg) + .addTenantProfileUpdateMsg(tenantProfileUpdateMsg) + .build(); + } + } + return downlinkMsg; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantProfileEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantProfileEdgeProcessor.java new file mode 100644 index 0000000000..5510e6d6f3 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantProfileEdgeProcessor.java @@ -0,0 +1,56 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.tenant; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EdgeUtils; +import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.id.TenantProfileId; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.tenant.TenantMsgConstructor; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; + +@Slf4j +@Component +@TbCoreComponent +public class TenantProfileEdgeProcessor extends BaseEdgeProcessor { + + public DownlinkMsg convertTenantProfileEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { + TenantProfileId tenantProfileId = new TenantProfileId(edgeEvent.getEntityId()); + DownlinkMsg downlinkMsg = null; + if (EdgeEventActionType.UPDATED.equals(edgeEvent.getAction())) { + TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(edgeEvent.getTenantId(), tenantProfileId); + if (tenantProfile != null) { + UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); + TenantProfileUpdateMsg tenantProfileUpdateMsg = ((TenantMsgConstructor) + tenantMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)) + .constructTenantProfileUpdateMsg(msgType, tenantProfile, edgeVersion); + downlinkMsg = DownlinkMsg.newBuilder() + .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) + .addTenantProfileUpdateMsg(tenantProfileUpdateMsg) + .build(); + } + } + return downlinkMsg; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java index 9070686fd6..8c63bcfe0c 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java @@ -15,28 +15,27 @@ */ package org.thingsboard.server.service.edge.rpc.processor.user; -import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.edge.EdgeEvent; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.user.UserMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; -@Component @Slf4j +@Component @TbCoreComponent public class UserEdgeProcessor extends BaseEdgeProcessor { - public DownlinkMsg convertUserEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertUserEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { UserId userId = new UserId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; switch (edgeEvent.getAction()) { @@ -47,31 +46,28 @@ public class UserEdgeProcessor extends BaseEdgeProcessor { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addUserUpdateMsg(userMsgConstructor.constructUserUpdatedMsg(msgType, user)) + .addUserUpdateMsg(((UserMsgConstructor) userMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructUserUpdatedMsg(msgType, user)) .build(); } break; case DELETED: downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addUserUpdateMsg(userMsgConstructor.constructUserDeleteMsg(userId)) + .addUserUpdateMsg(((UserMsgConstructor) userMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructUserDeleteMsg(userId)) .build(); break; case CREDENTIALS_UPDATED: UserCredentials userCredentialsByUserId = userService.findUserCredentialsByUserId(edgeEvent.getTenantId(), userId); if (userCredentialsByUserId != null && userCredentialsByUserId.isEnabled()) { UserCredentialsUpdateMsg userCredentialsUpdateMsg = - userMsgConstructor.constructUserCredentialsUpdatedMsg(userCredentialsByUserId); + ((UserMsgConstructor) userMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructUserCredentialsUpdatedMsg(userCredentialsByUserId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addUserCredentialsUpdateMsg(userCredentialsUpdateMsg) .build(); } + break; } return downlinkMsg; } - - public ListenableFuture processUserNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { - return processEntityNotificationForAllEdges(tenantId, edgeNotificationMsg); - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java index a429816a31..28cd3167aa 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetBundleEdgeProcessor.java @@ -15,27 +15,28 @@ */ package org.thingsboard.server.service.edge.rpc.processor.widget; -import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.edge.EdgeEvent; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.widget.WidgetMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; +import java.util.List; + @Component @Slf4j @TbCoreComponent public class WidgetBundleEdgeProcessor extends BaseEdgeProcessor { - public DownlinkMsg convertWidgetsBundleEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertWidgetsBundleEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { WidgetsBundleId widgetsBundleId = new WidgetsBundleId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; switch (edgeEvent.getAction()) { @@ -43,9 +44,10 @@ public class WidgetBundleEdgeProcessor extends BaseEdgeProcessor { case UPDATED: WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleById(edgeEvent.getTenantId(), widgetsBundleId); if (widgetsBundle != null) { + List widgets = widgetTypeService.findWidgetFqnsByWidgetsBundleId(edgeEvent.getTenantId(), widgetsBundleId); UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = - widgetsBundleMsgConstructor.constructWidgetsBundleUpdateMsg(msgType, widgetsBundle); + ((WidgetMsgConstructor) widgetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructWidgetsBundleUpdateMsg(msgType, widgetsBundle, widgets); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addWidgetsBundleUpdateMsg(widgetsBundleUpdateMsg) @@ -54,7 +56,7 @@ public class WidgetBundleEdgeProcessor extends BaseEdgeProcessor { break; case DELETED: WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = - widgetsBundleMsgConstructor.constructWidgetsBundleDeleteMsg(widgetsBundleId); + ((WidgetMsgConstructor) widgetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructWidgetsBundleDeleteMsg(widgetsBundleId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addWidgetsBundleUpdateMsg(widgetsBundleUpdateMsg) @@ -63,8 +65,4 @@ public class WidgetBundleEdgeProcessor extends BaseEdgeProcessor { } return downlinkMsg; } - - public ListenableFuture processWidgetsBundleNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { - return processEntityNotificationForAllEdges(tenantId, edgeNotificationMsg); - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java index 3caad57081..40556b5215 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/widget/WidgetTypeEdgeProcessor.java @@ -15,27 +15,26 @@ */ package org.thingsboard.server.service.edge.rpc.processor.widget; -import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.edge.EdgeEvent; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.rpc.constructor.widget.WidgetMsgConstructor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; -@Component @Slf4j +@Component @TbCoreComponent public class WidgetTypeEdgeProcessor extends BaseEdgeProcessor { - public DownlinkMsg convertWidgetTypeEventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertWidgetTypeEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { WidgetTypeId widgetTypeId = new WidgetTypeId(edgeEvent.getEntityId()); DownlinkMsg downlinkMsg = null; switch (edgeEvent.getAction()) { @@ -45,7 +44,7 @@ public class WidgetTypeEdgeProcessor extends BaseEdgeProcessor { if (widgetTypeDetails != null) { UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction()); WidgetTypeUpdateMsg widgetTypeUpdateMsg = - widgetTypeMsgConstructor.constructWidgetTypeUpdateMsg(msgType, widgetTypeDetails); + ((WidgetMsgConstructor) widgetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructWidgetTypeUpdateMsg(msgType, widgetTypeDetails, edgeVersion); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addWidgetTypeUpdateMsg(widgetTypeUpdateMsg) @@ -54,7 +53,7 @@ public class WidgetTypeEdgeProcessor extends BaseEdgeProcessor { break; case DELETED: WidgetTypeUpdateMsg widgetTypeUpdateMsg = - widgetTypeMsgConstructor.constructWidgetTypeDeleteMsg(widgetTypeId); + ((WidgetMsgConstructor) widgetMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructWidgetTypeDeleteMsg(widgetTypeId); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .addWidgetTypeUpdateMsg(widgetTypeUpdateMsg) @@ -63,8 +62,4 @@ public class WidgetTypeEdgeProcessor extends BaseEdgeProcessor { } return downlinkMsg; } - - public ListenableFuture processWidgetTypeNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { - return processEntityNotificationForAllEdges(tenantId, edgeNotificationMsg); - } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java index 700e0f3861..cd23f19556 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java @@ -167,6 +167,8 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService { attributes.put(attr.getKey(), attr.getDoubleValue().get()); } else if (attr.getDataType() == DataType.LONG && attr.getLongValue().isPresent()) { attributes.put(attr.getKey(), attr.getLongValue().get()); + } else if (attr.getDataType() == DataType.JSON && attr.getJsonValue().isPresent()) { + attributes.set(attr.getKey(), JacksonUtil.toJsonNode(attr.getJsonValue().get())); } else { attributes.put(attr.getKey(), attr.getValueAsString()); } @@ -174,8 +176,8 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService { if (attributes.size() > 0) { entityData.put("kv", attributes); entityData.put("scope", scope); - JsonNode body = JacksonUtil.OBJECT_MAPPER.valueToTree(entityData); - log.debug("Sending attributes data msg, entityId [{}], attributes [{}]", entityId, body); + JsonNode body = JacksonUtil.valueToTree(entityData); + log.debug("[{}] Sending attributes data msg, entityId [{}], attributes [{}]", tenantId, entityId, body); future = saveEdgeEvent(tenantId, edge.getId(), entityType, EdgeEventActionType.ATTRIBUTES_UPDATED, entityId, body); } else { future = Futures.immediateFuture(null); @@ -183,7 +185,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService { } return Futures.transformAsync(future, v -> processLatestTimeseriesAndAddToEdgeQueue(tenantId, entityId, edge, entityType), dbCallbackExecutorService); } catch (Exception e) { - String errMsg = String.format("[%s] Failed to save attribute updates to the edge [%s]", edge.getId(), attributesRequestMsg); + String errMsg = String.format("[%s][%s] Failed to save attribute updates to the edge [%s]", tenantId, edge.getId(), attributesRequestMsg); log.error(errMsg, e); return Futures.immediateFailedFuture(new RuntimeException(errMsg, e)); } @@ -237,7 +239,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService { if (relationsList != null && !relationsList.isEmpty()) { List> futures = new ArrayList<>(); for (List entityRelations : relationsList) { - log.trace("[{}] [{}] [{}] relation(s) are going to be pushed to edge.", edge.getId(), entityId, entityRelations.size()); + log.trace("[{}][{}][{}][{}] relation(s) are going to be pushed to edge.", tenantId, edge.getId(), entityId, entityRelations.size()); for (EntityRelation relation : entityRelations) { try { if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) && @@ -247,10 +249,10 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService { EdgeEventType.RELATION, EdgeEventActionType.ADDED, null, - JacksonUtil.OBJECT_MAPPER.valueToTree(relation))); + JacksonUtil.valueToTree(relation))); } } catch (Exception e) { - String errMsg = String.format("[%s] Exception during loading relation [%s] to edge on sync!", edge.getId(), relation); + String errMsg = String.format("[%s][%s] Exception during loading relation [%s] to edge on sync!", tenantId, edge.getId(), relation); log.error(errMsg, e); futureToSet.setException(new RuntimeException(errMsg, e)); return; @@ -265,7 +267,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService { @Override public void onFailure(Throwable throwable) { - String errMsg = String.format("[%s] Exception during saving edge events [%s]!", edge.getId(), relationRequestMsg); + String errMsg = String.format("[%s][%s] Exception during saving edge events [%s]!", tenantId, edge.getId(), relationRequestMsg); log.error(errMsg, throwable); futureToSet.setException(new RuntimeException(errMsg, throwable)); } @@ -274,7 +276,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService { futureToSet.set(null); } } catch (Exception e) { - log.error("Exception during loading relation(s) to edge on sync!", e); + log.error("[{}] Exception during loading relation(s) to edge on sync!", tenantId, e); futureToSet.setException(e); } } @@ -292,7 +294,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService { private ListenableFuture> findRelationByQuery(TenantId tenantId, Edge edge, EntityId entityId, EntitySearchDirection direction) { EntityRelationsQuery query = new EntityRelationsQuery(); - query.setParameters(new RelationsSearchParameters(entityId, direction, -1, false)); + query.setParameters(new RelationsSearchParameters(entityId, direction, 1, false)); return relationService.findByQuery(tenantId, query); } @@ -328,7 +330,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService { WidgetsBundle widgetsBundleById = widgetsBundleService.findWidgetsBundleById(tenantId, widgetsBundleId); if (widgetsBundleById != null) { List widgetTypesToPush = - widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(widgetsBundleById.getTenantId(), widgetsBundleById.getAlias()); + widgetTypeService.findWidgetTypesByWidgetsBundleId(widgetsBundleById.getTenantId(), widgetsBundleId); for (WidgetType widgetType : widgetTypesToPush) { futures.add(saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.WIDGET_TYPE, EdgeEventActionType.ADDED, widgetType.getId(), null)); } @@ -372,7 +374,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService { @Override public void onFailure(Throwable t) { - log.error("Exception during loading relation to edge on sync!", t); + log.error("[{}] Exception during loading relation to edge on sync!", tenantId, t); futureToSet.setException(t); } }, dbCallbackExecutorService); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/utils/EdgeVersionUtils.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/utils/EdgeVersionUtils.java new file mode 100644 index 0000000000..c9e6987a23 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/utils/EdgeVersionUtils.java @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.utils; + +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; + +@Slf4j +public final class EdgeVersionUtils { + + public static boolean isEdgeVersionOlderThan(EdgeVersion currentVersion, EdgeVersion requiredVersion) { + return currentVersion.ordinal() < requiredVersion.ordinal(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java index 11ff6a3321..9f724261a4 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java @@ -29,7 +29,6 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmQuery; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; -import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; @@ -46,7 +45,6 @@ import org.thingsboard.server.service.telemetry.AlarmSubscriptionService; import java.util.List; import java.util.Optional; import java.util.UUID; -import java.util.stream.Collectors; @Slf4j public abstract class AbstractTbEntityService { @@ -63,26 +61,22 @@ public abstract class AbstractTbEntityService { protected EdgeService edgeService; @Autowired protected AlarmService alarmService; - @Autowired @Lazy + @Autowired + @Lazy protected AlarmSubscriptionService alarmSubscriptionService; @Autowired protected CustomerService customerService; @Autowired protected TbClusterService tbClusterService; - @Autowired(required = false) @Lazy + @Autowired(required = false) + @Lazy private EntitiesVersionControlService vcService; - protected ListenableFuture removeAlarmsByEntityId(TenantId tenantId, EntityId entityId) { - ListenableFuture> alarmsFuture = + protected void removeAlarmsByEntityId(TenantId tenantId, EntityId entityId) { + PageData alarms = alarmService.findAlarms(tenantId, new AlarmQuery(entityId, new TimePageLink(Integer.MAX_VALUE), null, null, null, false)); - ListenableFuture> alarmIdsFuture = Futures.transform(alarmsFuture, page -> - page.getData().stream().map(AlarmInfo::getId).collect(Collectors.toList()), dbExecutor); - - return Futures.transform(alarmIdsFuture, ids -> { - ids.stream().map(alarmId -> alarmService.deleteAlarm(tenantId, alarmId)).collect(Collectors.toList()); - return null; - }, dbExecutor); + alarms.getData().stream().map(AlarmInfo::getId).forEach(alarmId -> alarmService.delAlarm(tenantId, alarmId)); } protected T checkNotNull(T reference) throws ThingsboardException { diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java index 0a75404fb4..8682d18a62 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java @@ -19,31 +19,23 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceCredentialsUpdateNotificationMsg; import org.thingsboard.server.cluster.TbClusterService; -import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.alarm.Alarm; -import org.thingsboard.server.common.data.alarm.AlarmComment; -import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEventActionType; -import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.relation.EntityRelation; -import org.thingsboard.server.common.data.rule.RuleChain; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; @@ -51,8 +43,6 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.service.action.EntityActionService; import org.thingsboard.server.service.gateway_device.GatewayNotificationsService; -import java.util.List; - @Slf4j @Service @RequiredArgsConstructor @@ -98,54 +88,6 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS } } - @Override - public void notifyDeleteEntity(TenantId tenantId, I entityId, E entity, - CustomerId customerId, ActionType actionType, - List relatedEdgeIds, - User user, Object... additionalInfo) { - logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo); - sendDeleteNotificationMsg(tenantId, entityId, relatedEdgeIds, null); - } - - @Override - public void notifyDeleteAlarm(TenantId tenantId, Alarm alarm, EntityId originatorId, CustomerId customerId, - List relatedEdgeIds, User user, String body, Object... additionalInfo) { - logEntityAction(tenantId, originatorId, alarm, customerId, ActionType.DELETED, user, additionalInfo); - sendAlarmDeleteNotificationMsg(tenantId, alarm, relatedEdgeIds, body); - } - - @Override - public void notifyDeleteRuleChain(TenantId tenantId, RuleChain ruleChain, List relatedEdgeIds, User user) { - RuleChainId ruleChainId = ruleChain.getId(); - logEntityAction(tenantId, ruleChainId, ruleChain, null, ActionType.DELETED, user, null, ruleChainId.toString()); - if (RuleChainType.EDGE.equals(ruleChain.getType())) { - sendDeleteNotificationMsg(tenantId, ruleChainId, relatedEdgeIds, null); - } - } - - @Override - public void notifySendMsgToEdgeService(TenantId tenantId, I entityId, EdgeEventActionType edgeEventActionType) { - sendEntityNotificationMsg(tenantId, entityId, edgeEventActionType); - } - - @Override - public void notifyAssignOrUnassignEntityToCustomer(TenantId tenantId, I entityId, - CustomerId customerId, E entity, - ActionType actionType, - User user, Object... additionalInfo) { - logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo); - sendEntityNotificationMsg(tenantId, entityId, edgeTypeByActionType(actionType), JacksonUtil.toString(customerId)); - } - - @Override - public void notifyAssignOrUnassignEntityToEdge(TenantId tenantId, I entityId, - CustomerId customerId, EdgeId edgeId, - E entity, ActionType actionType, - User user, Object... additionalInfo) { - logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo); - sendEntityAssignToEdgeNotificationMsg(tenantId, edgeId, entityId, edgeTypeByActionType(actionType)); - } - @Override public void notifyCreateOrUpdateTenant(Tenant tenant, ComponentLifecycleEvent event) { tbClusterService.onTenantChange(tenant, null); @@ -168,38 +110,27 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS @Override public void notifyDeleteDevice(TenantId tenantId, DeviceId deviceId, CustomerId customerId, Device device, - List relatedEdgeIds, User user, Object... additionalInfo) { + User user, Object... additionalInfo) { gatewayNotificationsService.onDeviceDeleted(device); - tbClusterService.onDeviceDeleted(device, null); - - notifyDeleteEntity(tenantId, deviceId, device, customerId, ActionType.DELETED, relatedEdgeIds, user, additionalInfo); + tbClusterService.onDeviceDeleted(tenantId, device, null); + logEntityAction(tenantId, deviceId, device, customerId, ActionType.DELETED, user, additionalInfo); } @Override public void notifyUpdateDeviceCredentials(TenantId tenantId, DeviceId deviceId, CustomerId customerId, Device device, DeviceCredentials deviceCredentials, User user) { tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(tenantId, deviceCredentials.getDeviceId(), deviceCredentials), null); - sendEntityNotificationMsg(tenantId, deviceId, EdgeEventActionType.CREDENTIALS_UPDATED); logEntityAction(tenantId, deviceId, device, customerId, ActionType.CREDENTIALS_UPDATED, user, deviceCredentials); } @Override public void notifyAssignDeviceToTenant(TenantId tenantId, TenantId newTenantId, DeviceId deviceId, CustomerId customerId, Device device, Tenant tenant, User user, Object... additionalInfo) { + tbClusterService.onDeviceAssignedToTenant(tenantId, device); logEntityAction(tenantId, deviceId, device, customerId, ActionType.ASSIGNED_TO_TENANT, user, additionalInfo); pushAssignedFromNotification(tenant, newTenantId, device); } - @Override - public void notifyCreateOrUpdateEntity(TenantId tenantId, I entityId, E entity, - CustomerId customerId, ActionType actionType, - User user, Object... additionalInfo) { - logEntityAction(tenantId, entityId, entity, customerId, actionType, user, additionalInfo); - if (actionType == ActionType.UPDATED) { - sendEntityNotificationMsg(tenantId, entityId, EdgeEventActionType.UPDATED); - } - } - @Override public void notifyCreateOrUpdateOrDeleteEdge(TenantId tenantId, EdgeId edgeId, CustomerId customerId, Edge edge, ActionType actionType, User user, Object... additionalInfo) { @@ -222,71 +153,16 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS } @Override - public void notifyCreateOrUpdateAlarm(AlarmInfo alarm, ActionType actionType, User user, Object... additionalInfo) { - logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarm, alarm.getCustomerId(), actionType, user, additionalInfo); - sendEntityNotificationMsg(alarm.getTenantId(), alarm.getId(), edgeTypeByActionType(actionType)); - } - - @Override - public void notifyAlarmComment(Alarm alarm, AlarmComment alarmComment, ActionType actionType, User user) { - logEntityAction(alarm.getTenantId(), alarm.getId(), alarm, alarm.getCustomerId(), actionType, user, alarmComment); - } - - @Override - public void notifyCreateOrUpdateOrDelete(TenantId tenantId, CustomerId customerId, - I entityId, E entity, User user, - ActionType actionType, boolean sendNotifyMsgToEdge, Exception e, - Object... additionalInfo) { - logEntityAction(tenantId, entityId, entity, customerId, actionType, user, e, additionalInfo); - if (sendNotifyMsgToEdge) { - sendEntityNotificationMsg(tenantId, entityId, edgeTypeByActionType(actionType)); - } - } - - @Override - public void notifyRelation(TenantId tenantId, CustomerId customerId, EntityRelation relation, User user, - ActionType actionType, Object... additionalInfo) { - logEntityAction(tenantId, relation.getFrom(), null, customerId, actionType, user, additionalInfo); - logEntityAction(tenantId, relation.getTo(), null, customerId, actionType, user, additionalInfo); - if (!EntityType.EDGE.equals(relation.getFrom().getEntityType()) && !EntityType.EDGE.equals(relation.getTo().getEntityType())) { - sendNotificationMsgToEdge(tenantId, null, null, JacksonUtil.toString(relation), - EdgeEventType.RELATION, edgeTypeByActionType(actionType)); - } - } - - private void sendEntityNotificationMsg(TenantId tenantId, EntityId entityId, EdgeEventActionType action) { - sendEntityNotificationMsg(tenantId, entityId, action, null); - } - - private void sendEntityNotificationMsg(TenantId tenantId, EntityId entityId, EdgeEventActionType action, String body) { - sendNotificationMsgToEdge(tenantId, null, entityId, body, null, action); - } - - private void sendAlarmDeleteNotificationMsg(TenantId tenantId, Alarm alarm, List edgeIds, String body) { - sendDeleteNotificationMsg(tenantId, alarm.getId(), edgeIds, body); - } - - private void sendDeleteNotificationMsg(TenantId tenantId, EntityId entityId, List edgeIds, String body) { - if (edgeIds != null && !edgeIds.isEmpty()) { - for (EdgeId edgeId : edgeIds) { - sendNotificationMsgToEdge(tenantId, edgeId, entityId, body, null, EdgeEventActionType.DELETED); - } - } - } - - private void sendEntityAssignToEdgeNotificationMsg(TenantId tenantId, EdgeId edgeId, EntityId entityId, EdgeEventActionType action) { - sendNotificationMsgToEdge(tenantId, edgeId, entityId, null, null, action); - } - - private void sendNotificationMsgToEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, - EdgeEventType type, EdgeEventActionType action) { - tbClusterService.sendNotificationMsgToEdge(tenantId, edgeId, entityId, body, type, action); + public void logEntityRelationAction(TenantId tenantId, CustomerId customerId, EntityRelation relation, User user, + ActionType actionType, Exception e, Object... additionalInfo) { + logEntityAction(tenantId, relation.getFrom(), null, customerId, actionType, user, e, additionalInfo); + logEntityAction(tenantId, relation.getTo(), null, customerId, actionType, user, e, additionalInfo); } private void pushAssignedFromNotification(Tenant currentTenant, TenantId newTenantId, Device assignedDevice) { String data = JacksonUtil.toString(JacksonUtil.valueToTree(assignedDevice)); if (data != null) { - TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_ASSIGNED_FROM_TENANT, assignedDevice.getId(), + TbMsg tbMsg = TbMsg.newMsg(TbMsgType.ENTITY_ASSIGNED_FROM_TENANT, assignedDevice.getId(), assignedDevice.getCustomerId(), getMetaDataForAssignedFrom(currentTenant), TbMsgDataType.JSON, data); tbClusterService.pushMsgToRuleEngine(newTenantId, assignedDevice.getId(), tbMsg, null); } @@ -327,6 +203,8 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS return EdgeEventActionType.ASSIGNED_TO_EDGE; case UNASSIGNED_FROM_EDGE: return EdgeEventActionType.UNASSIGNED_FROM_EDGE; + case CREDENTIALS_UPDATED: + return EdgeEventActionType.CREDENTIALS_UPDATED; default: return null; } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java index c5f8f85831..d14b2c6293 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java @@ -19,12 +19,8 @@ import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.alarm.Alarm; -import org.thingsboard.server.common.data.alarm.AlarmComment; -import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; -import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EdgeId; @@ -32,11 +28,8 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.relation.EntityRelation; -import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.security.DeviceCredentials; -import java.util.List; - public interface TbNotificationEntityService { void logEntityAction(TenantId tenantId, I entityId, ActionType actionType, User user, @@ -55,33 +48,6 @@ public interface TbNotificationEntityService { ActionType actionType, User user, Exception e, Object... additionalInfo); - void notifyCreateOrUpdateEntity(TenantId tenantId, I entityId, E entity, - CustomerId customerId, ActionType actionType, - User user, Object... additionalInfo); - - void notifyDeleteEntity(TenantId tenantId, I entityId, E entity, - CustomerId customerId, ActionType actionType, - List relatedEdgeIds, - User user, Object... additionalInfo); - - void notifyDeleteAlarm(TenantId tenantId, Alarm alarm, EntityId originatorId, CustomerId customerId, - List relatedEdgeIds, User user, String body, Object... additionalInfo); - - void notifyDeleteRuleChain(TenantId tenantId, RuleChain ruleChain, - List relatedEdgeIds, User user); - - void notifySendMsgToEdgeService(TenantId tenantId, I entityId, EdgeEventActionType edgeEventActionType); - - void notifyAssignOrUnassignEntityToCustomer(TenantId tenantId, I entityId, - CustomerId customerId, E entity, - ActionType actionType, - User user, Object... additionalInfo); - - void notifyAssignOrUnassignEntityToEdge(TenantId tenantId, I entityId, - CustomerId customerId, EdgeId edgeId, - E entity, ActionType actionType, - User user, Object... additionalInfo); - void notifyCreateOrUpdateTenant(Tenant tenant, ComponentLifecycleEvent event); void notifyDeleteTenant(Tenant tenant); @@ -90,7 +56,7 @@ public interface TbNotificationEntityService { Device oldDevice, ActionType actionType, User user, Object... additionalInfo); void notifyDeleteDevice(TenantId tenantId, DeviceId deviceId, CustomerId customerId, Device device, - List relatedEdgeIds, User user, Object... additionalInfo); + User user, Object... additionalInfo); void notifyUpdateDeviceCredentials(TenantId tenantId, DeviceId deviceId, CustomerId customerId, Device device, DeviceCredentials deviceCredentials, User user); @@ -101,16 +67,6 @@ public interface TbNotificationEntityService { void notifyCreateOrUpdateOrDeleteEdge(TenantId tenantId, EdgeId edgeId, CustomerId customerId, Edge edge, ActionType actionType, User user, Object... additionalInfo); - void notifyCreateOrUpdateAlarm(AlarmInfo alarm, ActionType actionType, User user, Object... additionalInfo); - - void notifyAlarmComment(Alarm alarm, AlarmComment alarmComment, ActionType actionType, User user); - - - void notifyCreateOrUpdateOrDelete(TenantId tenantId, CustomerId customerId, - I entityId, E entity, User user, - ActionType actionType, boolean sendNotifyMsgToEdge, - Exception e, Object... additionalInfo); - - void notifyRelation(TenantId tenantId, CustomerId customerId, EntityRelation relation, User user, - ActionType actionType, Object... additionalInfo); + void logEntityRelationAction(TenantId tenantId, CustomerId customerId, EntityRelation relation, User user, + ActionType actionType, Exception e, Object... additionalInfo); } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java index 6dfc81c747..282d11ab2a 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java @@ -45,7 +45,8 @@ public class DefaultTbAlarmCommentService extends AbstractTbEntityService implem } try { AlarmComment savedAlarmComment = checkNotNull(alarmCommentService.createOrUpdateAlarmComment(alarm.getTenantId(), alarmComment)); - notificationEntityService.notifyAlarmComment(alarm, savedAlarmComment, actionType, user); + notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getId(), alarm, alarm.getCustomerId(), actionType, user, savedAlarmComment); + return savedAlarmComment; } catch (Exception e) { notificationEntityService.logEntityAction(alarm.getTenantId(), emptyId(EntityType.ALARM), alarm, actionType, user, e, alarmComment); @@ -62,7 +63,7 @@ public class DefaultTbAlarmCommentService extends AbstractTbEntityService implem String.format("User %s deleted his comment", (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName()))); AlarmComment savedAlarmComment = checkNotNull(alarmCommentService.saveAlarmComment(alarm.getTenantId(), alarmComment)); - notificationEntityService.notifyAlarmComment(alarm, savedAlarmComment, ActionType.DELETED_COMMENT, user); + notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getId(), alarm, alarm.getCustomerId(), ActionType.DELETED_COMMENT, user, savedAlarmComment); } else { throw new ThingsboardException("System comment could not be deleted", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java index caf65d39ec..bd09b04a02 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.entitiy.alarm; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -33,11 +34,15 @@ import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; -import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; +import java.util.ArrayList; import java.util.List; @Service @@ -62,10 +67,6 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb if (!result.isSuccessful()) { throw new ThingsboardException(ThingsboardErrorCode.ITEM_NOT_FOUND); } - actionType = result.isCreated() ? ActionType.ADDED : ActionType.UPDATED; - if (result.isModified()) { - notificationEntityService.notifyCreateOrUpdateAlarm(result.getAlarm(), actionType, user); - } AlarmInfo resultAlarm = result.getAlarm(); if (alarm.isAcknowledged() && !resultAlarm.isAcknowledged()) { resultAlarm = ack(resultAlarm, alarm.getAckTs(), user); @@ -80,6 +81,10 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } else if (newAssignee == null && curAssignee != null) { resultAlarm = unassign(alarm, alarm.getAssignTs(), user); } + if (result.isModified()) { + notificationEntityService.logEntityAction(tenantId, alarm.getOriginator(), resultAlarm, + resultAlarm.getCustomerId(), actionType, user); + } return new Alarm(resultAlarm); } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ALARM), alarm, actionType, user, e); @@ -98,25 +103,16 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb if (!result.isSuccessful()) { throw new ThingsboardException(ThingsboardErrorCode.ITEM_NOT_FOUND); } + AlarmInfo alarmInfo = result.getAlarm(); if (result.isModified()) { - AlarmComment alarmComment = AlarmComment.builder() - .alarmId(alarm.getId()) - .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm was acknowledged by user %s", - (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName())) - .put("userId", user.getId().toString()) - .put("subtype", "ACK")) - .build(); - try { - alarmCommentService.saveAlarmComment(alarm, alarmComment, user); - } catch (ThingsboardException e) { - log.error("Failed to save alarm comment", e); - } - notificationEntityService.notifyCreateOrUpdateAlarm(result.getAlarm(), ActionType.ALARM_ACK, user); + String systemComment = String.format("Alarm was acknowledged by user %s", user.getTitle()); + addSystemAlarmComment(alarmInfo, user, "ACK", systemComment); + notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, + alarmInfo.getCustomerId(), ActionType.ALARM_ACK, user); } else { throw new ThingsboardException("Alarm was already acknowledged!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } - return result.getAlarm(); + return alarmInfo; } @Override @@ -130,25 +126,16 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb if (!result.isSuccessful()) { throw new ThingsboardException(ThingsboardErrorCode.ITEM_NOT_FOUND); } + AlarmInfo alarmInfo = result.getAlarm(); if (result.isCleared()) { - AlarmComment alarmComment = AlarmComment.builder() - .alarmId(alarm.getId()) - .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm was cleared by user %s", - (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName())) - .put("userId", user.getId().toString()) - .put("subtype", "CLEAR")) - .build(); - try { - alarmCommentService.saveAlarmComment(alarm, alarmComment, user); - } catch (ThingsboardException e) { - log.error("Failed to save alarm comment", e); - } - notificationEntityService.notifyCreateOrUpdateAlarm(result.getAlarm(), ActionType.ALARM_CLEAR, user); + String systemComment = String.format("Alarm was cleared by user %s", user.getTitle()); + addSystemAlarmComment(alarmInfo, user, "CLEAR", systemComment); + notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, + alarmInfo.getCustomerId(), ActionType.ALARM_CLEAR, user); } else { throw new ThingsboardException("Alarm was already cleared!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } - return result.getAlarm(); + return alarmInfo; } @Override @@ -160,22 +147,10 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb AlarmInfo alarmInfo = result.getAlarm(); if (result.isModified()) { AlarmAssignee assignee = alarmInfo.getAssignee(); - AlarmComment alarmComment = AlarmComment.builder() - .alarmId(alarm.getId()) - .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm was assigned by user %s to user %s", - (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName(), - (assignee.getFirstName() == null || assignee.getLastName() == null) ? assignee.getEmail() : assignee.getFirstName() + " " + assignee.getLastName())) - .put("userId", user.getId().toString()) - .put("assigneeId", assignee.getId().toString()) - .put("subtype", "ASSIGN")) - .build(); - try { - alarmCommentService.saveAlarmComment(alarm, alarmComment, user); - } catch (ThingsboardException e) { - log.error("Failed to save alarm comment", e); - } - notificationEntityService.notifyCreateOrUpdateAlarm(result.getAlarm(), ActionType.ALARM_ASSIGNED, user); + String systemComment = String.format("Alarm was assigned by user %s to user %s", user.getTitle(), assignee.getTitle()); + addSystemAlarmComment(alarmInfo, user, "ASSIGN", systemComment, assignee.getId()); + notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, + alarmInfo.getCustomerId(), ActionType.ALARM_ASSIGNED, user); } else { throw new ThingsboardException("Alarm was already assigned to this user!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } @@ -190,36 +165,84 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb } AlarmInfo alarmInfo = result.getAlarm(); if (result.isModified()) { - AlarmComment alarmComment = AlarmComment.builder() - .alarmId(alarm.getId()) - .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm was unassigned by user %s", - (user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName())) - .put("userId", user.getId().toString()) - .put("subtype", "ASSIGN")) - .build(); - try { - alarmCommentService.saveAlarmComment(alarm, alarmComment, user); - } catch (ThingsboardException e) { - log.error("Failed to save alarm comment", e); - } - notificationEntityService.notifyCreateOrUpdateAlarm(result.getAlarm(), ActionType.ALARM_UNASSIGNED, user); + String systemComment = String.format("Alarm was unassigned by user %s", user.getTitle()); + addSystemAlarmComment(alarmInfo, user, "ASSIGN", systemComment); + notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, + alarmInfo.getCustomerId(), ActionType.ALARM_UNASSIGNED, user); } else { throw new ThingsboardException("Alarm was already unassigned!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } return alarmInfo; } + @Override + public List unassignDeletedUserAlarms(TenantId tenantId, User user, long unassignTs) { + List totalAlarmIds = new ArrayList<>(); + PageLink pageLink = new PageLink(100, 0, null, new SortOrder("id", SortOrder.Direction.ASC)); + while (true) { + PageData pageData = alarmService.findAlarmIdsByAssigneeId(user.getTenantId(), user.getId(), pageLink); + List alarmIds = pageData.getData(); + if (alarmIds.isEmpty()) { + break; + } + processAlarmsUnassignment(tenantId, user, alarmIds, unassignTs); + totalAlarmIds.addAll(alarmIds); + pageLink = pageLink.nextPageLink(); + } + return totalAlarmIds; + } + @Override public Boolean delete(Alarm alarm, User user) { TenantId tenantId = alarm.getTenantId(); - List relatedEdgeIds = edgeService.findAllRelatedEdgeIds(tenantId, alarm.getOriginator()); - notificationEntityService.notifyDeleteAlarm(tenantId, alarm, alarm.getOriginator(), alarm.getCustomerId(), - relatedEdgeIds, user, JacksonUtil.toString(alarm)); + notificationEntityService.logEntityAction(tenantId, alarm.getOriginator(), alarm, alarm.getCustomerId(), + ActionType.DELETED, user); return alarmSubscriptionService.deleteAlarm(tenantId, alarm.getId()); } private static long getOrDefault(long ts) { return ts > 0 ? ts : System.currentTimeMillis(); } + + private void processAlarmsUnassignment(TenantId tenantId, User user, List alarmIds, long unassignTs) { + for (AlarmId alarmId : alarmIds) { + log.trace("[{}] Unassigning alarm {} userId {}", tenantId, alarmId, user.getId()); + AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(user.getTenantId(), alarmId, unassignTs); + if (!result.isSuccessful()) { + log.error("[{}] Cannot unassign alarm {} userId {}", tenantId, alarmId, user.getId()); + continue; + } + if (result.isModified()) { + String comment = String.format("Alarm was unassigned because user %s - was deleted", user.getTitle()); + addSystemAlarmComment(result.getAlarm(), null, "ASSIGN", comment); + notificationEntityService.logEntityAction(result.getAlarm().getTenantId(), result.getAlarm().getOriginator(), result.getAlarm(), result.getAlarm().getCustomerId(), ActionType.ALARM_UNASSIGNED, null); + } + } + } + + private void addSystemAlarmComment(Alarm alarm, User user, String subType, String commentText) { + addSystemAlarmComment(alarm, user, subType, commentText, null); + } + + private void addSystemAlarmComment(Alarm alarm, User user, String subType, String commentText, UserId assigneeId) { + ObjectNode commentNode = JacksonUtil.newObjectNode(); + commentNode.put("text", commentText) + .put("subtype", subType); + if (user != null) { + commentNode.put("userId", user.getId().getId().toString()); + } + if (assigneeId != null) { + commentNode.put("assigneeId", assigneeId.getId().toString()); + } + AlarmComment alarmComment = AlarmComment.builder() + .alarmId(alarm.getId()) + .type(AlarmCommentType.SYSTEM) + .comment(commentNode) + .build(); + try { + alarmCommentService.saveAlarmComment(alarm, alarmComment, user); + } catch (ThingsboardException e) { + log.error("Failed to save alarm comment", e); + } + } } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java index a2ae9c8cc7..24af55f38b 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java @@ -19,8 +19,12 @@ import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import java.util.List; + public interface TbAlarmService { Alarm save(Alarm entity, User user) throws ThingsboardException; @@ -37,5 +41,7 @@ public interface TbAlarmService { AlarmInfo unassign(Alarm alarm, long unassignTs, User user) throws ThingsboardException; + List unassignDeletedUserAlarms(TenantId tenantId, User user, long unassignTs); + Boolean delete(Alarm alarm, User user); } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java index ab57ecd050..5e49a315c3 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java @@ -15,9 +15,9 @@ */ package org.thingsboard.server.service.entitiy.asset; -import com.google.common.util.concurrent.ListenableFuture; import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; @@ -36,8 +36,6 @@ import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import org.thingsboard.server.service.profile.TbAssetProfileCache; -import java.util.List; - import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE; @Service @@ -62,8 +60,8 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb } Asset savedAsset = checkNotNull(assetService.saveAsset(asset)); autoCommit(user, savedAsset.getId()); - notificationEntityService.notifyCreateOrUpdateEntity(tenantId, savedAsset.getId(), savedAsset, - asset.getCustomerId(), actionType, user); + notificationEntityService.logEntityAction(tenantId, savedAsset.getId(), savedAsset, asset.getCustomerId(), + actionType, user); tbClusterService.broadcastEntityStateChangeEvent(tenantId, savedAsset.getId(), asset.getId() == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); return savedAsset; @@ -74,18 +72,18 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb } @Override - public ListenableFuture delete(Asset asset, User user) { + @Transactional + public void delete(Asset asset, User user) { + ActionType actionType = ActionType.DELETED; TenantId tenantId = asset.getTenantId(); AssetId assetId = asset.getId(); try { - List relatedEdgeIds = edgeService.findAllRelatedEdgeIds(tenantId, assetId); + removeAlarmsByEntityId(tenantId, assetId); assetService.deleteAsset(tenantId, assetId); - notificationEntityService.notifyDeleteEntity(tenantId, assetId, asset, asset.getCustomerId(), - ActionType.DELETED, relatedEdgeIds, user, assetId.toString()); + notificationEntityService.logEntityAction(tenantId, assetId, asset, asset.getCustomerId(), actionType, user, assetId.toString()); tbClusterService.broadcastEntityStateChangeEvent(tenantId, assetId, ComponentLifecycleEvent.DELETED); - return removeAlarmsByEntityId(tenantId, assetId); } catch (Exception e) { - notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ASSET), ActionType.DELETED, user, e, + notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ASSET), actionType, user, e, assetId.toString()); throw e; } @@ -97,8 +95,8 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb CustomerId customerId = customer.getId(); try { Asset savedAsset = checkNotNull(assetService.assignAssetToCustomer(tenantId, assetId, customerId)); - notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, assetId, customerId, savedAsset, - actionType, user, assetId.toString(), customerId.toString(), customer.getName()); + notificationEntityService.logEntityAction(tenantId, assetId, savedAsset, customerId, actionType, user, + assetId.toString(), customerId.toString(), customer.getName()); return savedAsset; } catch (Exception e) { @@ -114,8 +112,8 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb try { Asset savedAsset = checkNotNull(assetService.unassignAssetFromCustomer(tenantId, assetId)); CustomerId customerId = customer.getId(); - notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, assetId, customerId, savedAsset, - actionType, user, assetId.toString(), customerId.toString(), customer.getName()); + notificationEntityService.logEntityAction(tenantId, assetId, savedAsset, customerId, actionType, user, + assetId.toString(), customerId.toString(), customer.getName()); return savedAsset; } catch (Exception e) { @@ -130,8 +128,9 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb try { Customer publicCustomer = customerService.findOrCreatePublicCustomer(tenantId); Asset savedAsset = checkNotNull(assetService.assignAssetToCustomer(tenantId, assetId, publicCustomer.getId())); - notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, assetId, savedAsset.getCustomerId(), savedAsset, - actionType, user, assetId.toString(), publicCustomer.getId().toString(), publicCustomer.getName()); + CustomerId customerId = publicCustomer.getId(); + notificationEntityService.logEntityAction(tenantId, assetId, savedAsset, customerId, actionType, user, + assetId.toString(), customerId.toString(), publicCustomer.getName()); return savedAsset; } catch (Exception e) { @@ -146,9 +145,8 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb EdgeId edgeId = edge.getId(); try { Asset savedAsset = checkNotNull(assetService.assignAssetToEdge(tenantId, assetId, edgeId)); - notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, assetId, savedAsset.getCustomerId(), - edgeId, savedAsset, actionType, user, assetId.toString(), edgeId.toString(), edge.getName()); - + notificationEntityService.logEntityAction(tenantId, assetId, savedAsset, savedAsset.getCustomerId(), + actionType, user, assetId.toString(), edgeId.toString(), edge.getName()); return savedAsset; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ASSET), actionType, @@ -164,9 +162,8 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb EdgeId edgeId = edge.getId(); try { Asset savedAsset = checkNotNull(assetService.unassignAssetFromEdge(tenantId, assetId, edgeId)); - - notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, assetId, asset.getCustomerId(), - edgeId, asset, actionType, user, assetId.toString(), edgeId.toString(), edge.getName()); + notificationEntityService.logEntityAction(tenantId, assetId, asset, asset.getCustomerId(), + actionType, user, assetId.toString(), edgeId.toString(), edge.getName()); return savedAsset; } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/TbAssetService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/TbAssetService.java index b966eb8c6a..8b175599cf 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/TbAssetService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/TbAssetService.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.entitiy.asset; -import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.asset.Asset; @@ -28,7 +27,7 @@ public interface TbAssetService { Asset save(Asset asset, User user) throws Exception; - ListenableFuture delete(Asset asset, User user); + void delete(Asset asset, User user); Asset assignAssetToCustomer(TenantId tenantId, AssetId assetId, Customer customer, User user) throws ThingsboardException; diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/profile/DefaultTbAssetProfileService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/profile/DefaultTbAssetProfileService.java index b662994451..00b181a52a 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/asset/profile/DefaultTbAssetProfileService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/asset/profile/DefaultTbAssetProfileService.java @@ -59,8 +59,8 @@ public class DefaultTbAssetProfileService extends AbstractTbEntityService implem tbClusterService.broadcastEntityStateChangeEvent(tenantId, savedAssetProfile.getId(), actionType.equals(ActionType.ADDED) ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedAssetProfile.getId(), - savedAssetProfile, user, actionType, true, null); + notificationEntityService.logEntityAction(tenantId, savedAssetProfile.getId(), savedAssetProfile, + null, actionType, user); return savedAssetProfile; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ASSET_PROFILE), assetProfile, actionType, user, e); @@ -70,16 +70,17 @@ public class DefaultTbAssetProfileService extends AbstractTbEntityService implem @Override public void delete(AssetProfile assetProfile, User user) { + ActionType actionType = ActionType.DELETED; AssetProfileId assetProfileId = assetProfile.getId(); TenantId tenantId = assetProfile.getTenantId(); try { assetProfileService.deleteAssetProfile(tenantId, assetProfileId); tbClusterService.broadcastEntityStateChangeEvent(tenantId, assetProfileId, ComponentLifecycleEvent.DELETED); - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, assetProfileId, assetProfile, - user, ActionType.DELETED, true, null, assetProfileId.toString()); + notificationEntityService.logEntityAction(tenantId, assetProfileId, assetProfile, null, + actionType, user, assetProfileId.toString()); } catch (Exception e) { - notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ASSET_PROFILE), ActionType.DELETED, + notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ASSET_PROFILE), actionType, user, e, assetProfileId.toString()); throw e; } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java index b71cfd222e..96611bb3b5 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java @@ -22,13 +22,10 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; -import java.util.List; - @Service @AllArgsConstructor public class DefaultTbCustomerService extends AbstractTbEntityService implements TbCustomerService { @@ -40,7 +37,7 @@ public class DefaultTbCustomerService extends AbstractTbEntityService implements try { Customer savedCustomer = checkNotNull(customerService.saveCustomer(customer)); autoCommit(user, savedCustomer.getId()); - notificationEntityService.notifyCreateOrUpdateEntity(tenantId, savedCustomer.getId(), savedCustomer, null, actionType, user); + notificationEntityService.logEntityAction(tenantId, savedCustomer.getId(), savedCustomer, null, actionType, user); return savedCustomer; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.CUSTOMER), customer, actionType, user, e); @@ -50,17 +47,17 @@ public class DefaultTbCustomerService extends AbstractTbEntityService implements @Override public void delete(Customer customer, User user) { + ActionType actionType = ActionType.DELETED; TenantId tenantId = customer.getTenantId(); CustomerId customerId = customer.getId(); try { - List relatedEdgeIds = edgeService.findAllRelatedEdgeIds(tenantId, customer.getId()); customerService.deleteCustomer(tenantId, customerId); - notificationEntityService.notifyDeleteEntity(tenantId, customer.getId(), customer, customerId, - ActionType.DELETED, relatedEdgeIds, user, customerId.toString()); + notificationEntityService.logEntityAction(tenantId, customer.getId(), customer, customerId, actionType, + user, customerId.toString()); tbClusterService.broadcastEntityStateChangeEvent(tenantId, customer.getId(), ComponentLifecycleEvent.DELETED); } catch (Exception e) { - notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.CUSTOMER), ActionType.DELETED, - user, e, customerId.toString()); + notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.CUSTOMER), actionType, user, + e, customerId.toString()); throw e; } } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java index fa5b967bd0..8d143d7607 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java @@ -30,11 +30,11 @@ import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.dashboard.DashboardService; +import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import java.util.HashSet; -import java.util.List; import java.util.Set; @Service @@ -51,8 +51,8 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement try { Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard)); autoCommit(user, savedDashboard.getId()); - notificationEntityService.notifyCreateOrUpdateEntity(tenantId, savedDashboard.getId(), savedDashboard, - null, actionType, user); + notificationEntityService.logEntityAction(tenantId, savedDashboard.getId(), savedDashboard, null, + actionType, user); return savedDashboard; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DASHBOARD), dashboard, actionType, user, e); @@ -62,15 +62,14 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement @Override public void delete(Dashboard dashboard, User user) { + ActionType actionType = ActionType.DELETED; DashboardId dashboardId = dashboard.getId(); TenantId tenantId = dashboard.getTenantId(); try { - List relatedEdgeIds = edgeService.findAllRelatedEdgeIds(tenantId, dashboardId); dashboardService.deleteDashboard(tenantId, dashboardId); - notificationEntityService.notifyDeleteEntity(tenantId, dashboardId, dashboard, null, - ActionType.DELETED, relatedEdgeIds, user, dashboardId.toString()); + notificationEntityService.logEntityAction(tenantId, dashboardId, dashboard, null, actionType, user, dashboardId.toString()); } catch (Exception e) { - notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DASHBOARD), ActionType.DELETED, user, e, dashboardId.toString()); + notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DASHBOARD), actionType, user, e, dashboardId.toString()); throw e; } } @@ -83,8 +82,8 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement DashboardId dashboardId = dashboard.getId(); try { Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(tenantId, dashboardId, customerId)); - notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboardId, customerId, savedDashboard, - actionType, user, dashboardId.toString(), customerId.toString(), customer.getName()); + notificationEntityService.logEntityAction(tenantId, dashboardId, savedDashboard, customerId, actionType, + user, dashboardId.toString(), customerId.toString(), customer.getName()); return savedDashboard; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DASHBOARD), actionType, @@ -101,9 +100,8 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement try { Customer publicCustomer = customerService.findOrCreatePublicCustomer(tenantId); Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(tenantId, dashboardId, publicCustomer.getId())); - notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboardId, publicCustomer.getId(), savedDashboard, - actionType, user, dashboardId.toString(), - publicCustomer.getId().toString(), publicCustomer.getName()); + notificationEntityService.logEntityAction(tenantId, dashboardId, savedDashboard, publicCustomer.getId(), + actionType, user, dashboardId.toString(), publicCustomer.getId().toString(), publicCustomer.getName()); return savedDashboard; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DASHBOARD), actionType, user, e, dashboardId.toString()); @@ -119,9 +117,8 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement try { Customer publicCustomer = customerService.findOrCreatePublicCustomer(tenantId); Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboardId, publicCustomer.getId())); - notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboardId, publicCustomer.getId(), dashboard, - actionType, user, dashboardId.toString(), - publicCustomer.getId().toString(), publicCustomer.getName()); + notificationEntityService.logEntityAction(tenantId, dashboardId, dashboard, publicCustomer.getId(), actionType, + user, dashboardId.toString(), publicCustomer.getId().toString(), publicCustomer.getName()); return savedDashboard; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DASHBOARD), actionType, user, e, dashboardId.toString()); @@ -159,15 +156,15 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement for (CustomerId customerId : addedCustomerIds) { savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(tenantId, dashboardId, customerId)); ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId); - notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, savedDashboard.getId(), customerId, savedDashboard, + notificationEntityService.logEntityAction(tenantId, savedDashboard.getId(), savedDashboard, customerId, actionType, user, dashboardId.toString(), customerId.toString(), customerInfo.getTitle()); } actionType = ActionType.UNASSIGNED_FROM_CUSTOMER; for (CustomerId customerId : removedCustomerIds) { ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId); savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboardId, customerId)); - notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, savedDashboard.getId(), customerId, savedDashboard, - ActionType.UNASSIGNED_FROM_CUSTOMER, user, dashboardId.toString(), customerId.toString(), customerInfo.getTitle()); + notificationEntityService.logEntityAction(tenantId, savedDashboard.getId(), savedDashboard, customerId, + actionType, user, dashboardId.toString(), customerId.toString(), customerInfo.getTitle()); } return savedDashboard; } @@ -196,7 +193,7 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement for (CustomerId customerId : addedCustomerIds) { savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(tenantId, dashboardId, customerId)); ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId); - notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboardId, customerId, savedDashboard, + notificationEntityService.logEntityAction(tenantId, dashboardId, savedDashboard, customerId, actionType, user, dashboardId.toString(), customerId.toString(), customerInfo.getTitle()); } return savedDashboard; @@ -226,7 +223,7 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement for (CustomerId customerId : removedCustomerIds) { ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId); savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboardId, customerId)); - notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboardId, customerId, savedDashboard, + notificationEntityService.logEntityAction(tenantId, dashboardId, savedDashboard, customerId, actionType, user, dashboardId.toString(), customerId.toString(), customerInfo.getTitle()); } return savedDashboard; @@ -243,9 +240,8 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement EdgeId edgeId = edge.getId(); try { Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToEdge(tenantId, dashboardId, edgeId)); - notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, dashboardId, null, - edgeId, savedDashboard, actionType, user, dashboardId.toString(), - edgeId.toString(), edge.getName()); + notificationEntityService.logEntityAction(tenantId, dashboardId, savedDashboard, null, actionType, + user, dashboardId.toString(), edgeId.toString(), edge.getName()); return savedDashboard; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DEVICE), @@ -262,10 +258,8 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement EdgeId edgeId = edge.getId(); try { Dashboard savedDevice = checkNotNull(dashboardService.unassignDashboardFromEdge(tenantId, dashboardId, edgeId)); - - notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, dashboardId, null, - edgeId, dashboard, actionType, user, dashboardId.toString(), - edgeId.toString(), edge.getName()); + notificationEntityService.logEntityAction(tenantId, dashboardId, dashboard, null, actionType, + user, dashboardId.toString(), edgeId.toString(), edge.getName()); return savedDevice; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DASHBOARD), actionType, user, e, @@ -281,7 +275,7 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement DashboardId dashboardId = dashboard.getId(); try { Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboardId, customer.getId())); - notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboardId, customer.getId(), savedDashboard, + notificationEntityService.logEntityAction(tenantId, dashboardId, savedDashboard, customer.getId(), actionType, user, dashboardId.toString(), customer.getId().toString(), customer.getName()); return savedDashboard; } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java index 2ab8de9438..ac2d656ba6 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java @@ -21,6 +21,7 @@ import com.google.common.util.concurrent.MoreExecutors; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; @@ -44,8 +45,6 @@ import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; -import java.util.List; - @AllArgsConstructor @TbCoreComponent @Service @@ -93,16 +92,15 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T } @Override - public ListenableFuture delete(Device device, User user) { + @Transactional + public void delete(Device device, User user) { TenantId tenantId = device.getTenantId(); DeviceId deviceId = device.getId(); try { - List relatedEdgeIds = edgeService.findAllRelatedEdgeIds(tenantId, deviceId); + removeAlarmsByEntityId(tenantId, deviceId); deviceService.deleteDevice(tenantId, deviceId); notificationEntityService.notifyDeleteDevice(tenantId, deviceId, device.getCustomerId(), device, - relatedEdgeIds, user, deviceId.toString()); - - return removeAlarmsByEntityId(tenantId, deviceId); + user, deviceId.toString()); } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DEVICE), ActionType.DELETED, user, e, deviceId.toString()); @@ -116,8 +114,8 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T CustomerId customerId = customer.getId(); try { Device savedDevice = checkNotNull(deviceService.assignDeviceToCustomer(tenantId, deviceId, customerId)); - notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, deviceId, customerId, savedDevice, - actionType, user, deviceId.toString(), customerId.toString(), customer.getName()); + notificationEntityService.logEntityAction(tenantId, deviceId, savedDevice, customerId, actionType, user, + deviceId.toString(), customerId.toString(), customer.getName()); return savedDevice; } catch (Exception e) { @@ -136,8 +134,8 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T Device savedDevice = checkNotNull(deviceService.unassignDeviceFromCustomer(tenantId, deviceId)); CustomerId customerId = customer.getId(); - notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, deviceId, customerId, savedDevice, - actionType, user, deviceId.toString(), customerId.toString(), customer.getName()); + notificationEntityService.logEntityAction(tenantId, deviceId, savedDevice, customerId, actionType, user, + deviceId.toString(), customerId.toString(), customer.getName()); return savedDevice; } catch (Exception e) { @@ -154,9 +152,8 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T try { Device savedDevice = checkNotNull(deviceService.assignDeviceToCustomer(tenantId, deviceId, publicCustomer.getId())); - notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, deviceId, savedDevice.getCustomerId(), savedDevice, - actionType, user, deviceId.toString(), - publicCustomer.getId().toString(), publicCustomer.getName()); + notificationEntityService.logEntityAction(tenantId, deviceId, savedDevice, savedDevice.getCustomerId(), + actionType, user, deviceId.toString(), publicCustomer.getId().toString(), publicCustomer.getName()); return savedDevice; } catch (Exception e) { @@ -252,30 +249,32 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T EdgeId edgeId = edge.getId(); try { Device savedDevice = checkNotNull(deviceService.assignDeviceToEdge(tenantId, deviceId, edgeId)); - notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, deviceId, savedDevice.getCustomerId(), - edgeId, savedDevice, actionType, user, deviceId.toString(), edgeId.toString(), edge.getName()); + notificationEntityService.logEntityAction(tenantId, deviceId, savedDevice, savedDevice.getCustomerId(), + actionType, user, deviceId.toString(), edgeId.toString(), edge.getName()); + return savedDevice; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DEVICE), - ActionType.ASSIGNED_TO_EDGE, user, e, deviceId.toString(), edgeId.toString()); + actionType, user, e, deviceId.toString(), edgeId.toString()); throw e; } } @Override public Device unassignDeviceFromEdge(Device device, Edge edge, User user) throws ThingsboardException { + ActionType actionType = ActionType.UNASSIGNED_FROM_EDGE; TenantId tenantId = device.getTenantId(); DeviceId deviceId = device.getId(); EdgeId edgeId = edge.getId(); try { Device savedDevice = checkNotNull(deviceService.unassignDeviceFromEdge(tenantId, deviceId, edgeId)); + notificationEntityService.logEntityAction(tenantId, deviceId, savedDevice, savedDevice.getCustomerId(), + actionType, user, deviceId.toString(), edgeId.toString(), edge.getName()); - notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, deviceId, device.getCustomerId(), - edgeId, device, ActionType.UNASSIGNED_FROM_EDGE, user, deviceId.toString(), edgeId.toString(), edge.getName()); return savedDevice; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DEVICE), - ActionType.UNASSIGNED_FROM_EDGE, user, e, deviceId.toString(), edgeId.toString()); + actionType, user, e, deviceId.toString(), edgeId.toString()); throw e; } } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/device/TbDeviceService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/device/TbDeviceService.java index e9665c6e3e..ef44b3679e 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/device/TbDeviceService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/device/TbDeviceService.java @@ -35,7 +35,7 @@ public interface TbDeviceService { Device saveDeviceWithCredentials(Device device, DeviceCredentials deviceCredentials, User user) throws ThingsboardException; - ListenableFuture delete(Device device, User user); + void delete(Device device, User user); Device assignDeviceToCustomer(TenantId tenantId, DeviceId deviceId, Customer customer, User user) throws ThingsboardException; diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/device/profile/DefaultTbDeviceProfileService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/device/profile/DefaultTbDeviceProfileService.java index faa6a69983..70ee722493 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/device/profile/DefaultTbDeviceProfileService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/device/profile/DefaultTbDeviceProfileService.java @@ -67,8 +67,8 @@ public class DefaultTbDeviceProfileService extends AbstractTbEntityService imple otaPackageStateService.update(savedDeviceProfile, isFirmwareChanged, isSoftwareChanged); - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedDeviceProfile.getId(), - savedDeviceProfile, user, actionType, true, null); + notificationEntityService.logEntityAction(tenantId, savedDeviceProfile.getId(), savedDeviceProfile, + null, actionType, user); return savedDeviceProfile; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DEVICE_PROFILE), deviceProfile, actionType, user, e); @@ -78,6 +78,7 @@ public class DefaultTbDeviceProfileService extends AbstractTbEntityService imple @Override public void delete(DeviceProfile deviceProfile, User user) { + ActionType actionType = ActionType.DELETED; DeviceProfileId deviceProfileId = deviceProfile.getId(); TenantId tenantId = deviceProfile.getTenantId(); try { @@ -85,10 +86,10 @@ public class DefaultTbDeviceProfileService extends AbstractTbEntityService imple tbClusterService.onDeviceProfileDelete(deviceProfile, null); tbClusterService.broadcastEntityStateChangeEvent(tenantId, deviceProfileId, ComponentLifecycleEvent.DELETED); - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, deviceProfileId, deviceProfile, - user, ActionType.DELETED, true, null, deviceProfileId.toString()); + notificationEntityService.logEntityAction(tenantId, deviceProfileId, deviceProfile, null, + actionType, user, deviceProfileId.toString()); } catch (Exception e) { - notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DEVICE_PROFILE), ActionType.DELETED, + notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.DEVICE_PROFILE), actionType, user, e, deviceProfileId.toString()); throw e; } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/edge/DefaultTbEdgeService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/edge/DefaultTbEdgeService.java index c51ae3352e..adc20c21d0 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/edge/DefaultTbEdgeService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/edge/DefaultTbEdgeService.java @@ -89,8 +89,8 @@ public class DefaultTbEdgeService extends AbstractTbEntityService implements TbE CustomerId customerId = customer.getId(); try { Edge savedEdge = checkNotNull(edgeService.assignEdgeToCustomer(tenantId, edgeId, customerId)); - notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, edgeId, customerId, savedEdge, - actionType, user, edgeId.toString(), customerId.toString(), customer.getName()); + notificationEntityService.logEntityAction(tenantId, edgeId, savedEdge, customerId, actionType, + user, edgeId.toString(), customerId.toString(), customer.getName()); return savedEdge; } catch (Exception e) { @@ -108,8 +108,8 @@ public class DefaultTbEdgeService extends AbstractTbEntityService implements TbE CustomerId customerId = customer.getId(); try { Edge savedEdge = checkNotNull(edgeService.unassignEdgeFromCustomer(tenantId, edgeId)); - notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, edgeId, customerId, savedEdge, - actionType, user, edgeId.toString(), customerId.toString(), customer.getName()); + notificationEntityService.logEntityAction(tenantId, edgeId, savedEdge, customerId, actionType, + user, edgeId.toString(), customerId.toString(), customer.getName()); return savedEdge; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.EDGE), diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/entity/relation/DefaultTbEntityRelationService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/entity/relation/DefaultTbEntityRelationService.java index b978b730fd..904d98653e 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/entity/relation/DefaultTbEntityRelationService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/entity/relation/DefaultTbEntityRelationService.java @@ -40,41 +40,38 @@ public class DefaultTbEntityRelationService extends AbstractTbEntityService impl @Override public void save(TenantId tenantId, CustomerId customerId, EntityRelation relation, User user) throws ThingsboardException { + ActionType actionType = ActionType.RELATION_ADD_OR_UPDATE; try { relationService.saveRelation(tenantId, relation); - notificationEntityService.notifyRelation(tenantId, customerId, - relation, user, ActionType.RELATION_ADD_OR_UPDATE, relation); + notificationEntityService.logEntityRelationAction(tenantId, customerId, + relation, user, actionType, null, relation); } catch (Exception e) { - notificationEntityService.logEntityAction(tenantId, relation.getFrom(), null, customerId, - ActionType.RELATION_ADD_OR_UPDATE, user, e, relation); - notificationEntityService.logEntityAction(tenantId, relation.getTo(), null, customerId, - ActionType.RELATION_ADD_OR_UPDATE, user, e, relation); + notificationEntityService.logEntityRelationAction(tenantId, customerId, + relation, user, actionType, e, relation); throw e; } } @Override public void delete(TenantId tenantId, CustomerId customerId, EntityRelation relation, User user) throws ThingsboardException { + ActionType actionType = ActionType.RELATION_DELETED; try { boolean found = relationService.deleteRelation(tenantId, relation.getFrom(), relation.getTo(), relation.getType(), relation.getTypeGroup()); if (!found) { throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND); } - notificationEntityService.notifyRelation(tenantId, customerId, - relation, user, ActionType.RELATION_DELETED, relation); + notificationEntityService.logEntityRelationAction(tenantId, customerId, relation, user, actionType, null, relation); } catch (Exception e) { - notificationEntityService.logEntityAction(tenantId, relation.getFrom(), null, customerId, - ActionType.RELATION_DELETED, user, e, relation); - notificationEntityService.logEntityAction(tenantId, relation.getTo(), null, customerId, - ActionType.RELATION_DELETED, user, e, relation); + notificationEntityService.logEntityRelationAction(tenantId, customerId, + relation, user, actionType, e, relation); throw e; } } @Override - public void deleteRelations(TenantId tenantId, CustomerId customerId, EntityId entityId, User user) throws ThingsboardException { + public void deleteCommonRelations(TenantId tenantId, CustomerId customerId, EntityId entityId, User user) throws ThingsboardException { try { - relationService.deleteEntityRelations(tenantId, entityId); + relationService.deleteEntityCommonRelations(tenantId, entityId); notificationEntityService.logEntityAction(tenantId, entityId, null, customerId, ActionType.RELATIONS_DELETED, user); } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, entityId, null, customerId, diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/entity/relation/TbEntityRelationService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/entity/relation/TbEntityRelationService.java index 2caee86d0c..8bf5018c95 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/entity/relation/TbEntityRelationService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/entity/relation/TbEntityRelationService.java @@ -28,6 +28,6 @@ public interface TbEntityRelationService { void delete(TenantId tenantId, CustomerId customerId, EntityRelation entity, User user) throws ThingsboardException; - void deleteRelations(TenantId tenantId, CustomerId customerId, EntityId entityId, User user) throws ThingsboardException; + void deleteCommonRelations(TenantId tenantId, CustomerId customerId, EntityId entityId, User user) throws ThingsboardException; } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java index 48e6c4b634..f587e19ef2 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/entityview/DefaultTbEntityViewService.java @@ -81,7 +81,7 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen EntityView savedEntityView = checkNotNull(entityViewService.saveEntityView(entityView)); this.updateEntityViewAttributes(tenantId, savedEntityView, existingEntityView, user); autoCommit(user, savedEntityView.getId()); - notificationEntityService.notifyCreateOrUpdateEntity(savedEntityView.getTenantId(), savedEntityView.getId(), savedEntityView, + notificationEntityService.logEntityAction(savedEntityView.getTenantId(), savedEntityView.getId(), savedEntityView, null, actionType, user); localCache.computeIfAbsent(savedEntityView.getTenantId(), (k) -> new ConcurrentReferenceHashMap<>()).clear(); tbClusterService.broadcastEntityStateChangeEvent(savedEntityView.getTenantId(), savedEntityView.getId(), @@ -129,10 +129,9 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen TenantId tenantId = entityView.getTenantId(); EntityViewId entityViewId = entityView.getId(); try { - List relatedEdgeIds = edgeService.findAllRelatedEdgeIds(tenantId, entityViewId); entityViewService.deleteEntityView(tenantId, entityViewId); - notificationEntityService.notifyDeleteEntity(tenantId, entityViewId, entityView, entityView.getCustomerId(), ActionType.DELETED, - relatedEdgeIds, user, entityViewId.toString()); + notificationEntityService.logEntityAction(tenantId, entityViewId, entityView, entityView.getCustomerId(), + ActionType.DELETED, user, entityViewId.toString()); localCache.computeIfAbsent(tenantId, (k) -> new ConcurrentReferenceHashMap<>()).clear(); tbClusterService.broadcastEntityStateChangeEvent(tenantId, entityViewId, ComponentLifecycleEvent.DELETED); @@ -145,15 +144,31 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen @Override public EntityView assignEntityViewToCustomer(TenantId tenantId, EntityViewId entityViewId, Customer customer, User user) throws ThingsboardException { + ActionType actionType = ActionType.ASSIGNED_TO_CUSTOMER; CustomerId customerId = customer.getId(); try { EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToCustomer(tenantId, entityViewId, customerId)); - notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, entityViewId, customerId, savedEntityView, - ActionType.ASSIGNED_TO_CUSTOMER, user, entityViewId.toString(), customerId.toString(), customer.getName()); + notificationEntityService.logEntityAction(tenantId, entityViewId, savedEntityView, savedEntityView.getCustomerId(), + actionType, user, entityViewId.toString(), customerId.toString(), customer.getName()); + return savedEntityView; + } catch (Exception e) { + notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ENTITY_VIEW), + actionType, user, e, entityViewId.toString(), customerId.toString()); + throw e; + } + } + + @Override + public EntityView unassignEntityViewFromCustomer(TenantId tenantId, EntityViewId entityViewId, Customer customer, User user) throws ThingsboardException { + ActionType actionType = ActionType.UNASSIGNED_FROM_CUSTOMER; + try { + EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromCustomer(tenantId, entityViewId)); + notificationEntityService.logEntityAction(tenantId, entityViewId, savedEntityView, customer.getId(), + actionType, user, savedEntityView.getId().toString(), customer.getId().toString(), customer.getName()); return savedEntityView; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ENTITY_VIEW), - ActionType.ASSIGNED_TO_CUSTOMER, user, e, entityViewId.toString(), customerId.toString()); + actionType, user, e, entityViewId.toString()); throw e; } } @@ -165,9 +180,8 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen try { EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToCustomer(tenantId, entityViewId, publicCustomer.getId())); - notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, entityViewId, savedEntityView.getCustomerId(), savedEntityView, - actionType, user, savedEntityView.getId().toString(), - publicCustomer.getId().toString(), publicCustomer.getName()); + notificationEntityService.logEntityAction(tenantId, entityViewId, savedEntityView, savedEntityView.getCustomerId(), + actionType, user, savedEntityView.getId().toString(), publicCustomer.getId().toString(), publicCustomer.getName()); return savedEntityView; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ENTITY_VIEW), @@ -178,16 +192,16 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen @Override public EntityView assignEntityViewToEdge(TenantId tenantId, CustomerId customerId, EntityViewId entityViewId, Edge edge, User user) throws ThingsboardException { + ActionType actionType = ActionType.ASSIGNED_TO_EDGE; EdgeId edgeId = edge.getId(); try { EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToEdge(tenantId, entityViewId, edgeId)); - notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, entityViewId, customerId, - edgeId, savedEntityView, ActionType.ASSIGNED_TO_EDGE, user, savedEntityView.getEntityId().toString(), - edgeId.toString(), edge.getName()); + notificationEntityService.logEntityAction(tenantId, entityViewId, savedEntityView, customerId, actionType, + user, savedEntityView.getEntityId().toString(), edgeId.toString(), edge.getName()); return savedEntityView; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ENTITY_VIEW), - ActionType.ASSIGNED_TO_EDGE, user, e, entityViewId.toString(), edgeId.toString()); + actionType, user, e, entityViewId.toString(), edgeId.toString()); throw e; } } @@ -195,34 +209,17 @@ public class DefaultTbEntityViewService extends AbstractTbEntityService implemen @Override public EntityView unassignEntityViewFromEdge(TenantId tenantId, CustomerId customerId, EntityView entityView, Edge edge, User user) throws ThingsboardException { + ActionType actionType = ActionType.UNASSIGNED_FROM_EDGE; EntityViewId entityViewId = entityView.getId(); EdgeId edgeId = edge.getId(); try { EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromEdge(tenantId, entityViewId, edgeId)); - notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, entityViewId, customerId, - edgeId, entityView, ActionType.UNASSIGNED_FROM_EDGE, user, entityViewId.toString(), - edgeId.toString(), edge.getName()); + notificationEntityService.logEntityAction(tenantId, entityViewId, savedEntityView, customerId, actionType, + user, entityViewId.toString(), edgeId.toString(), edge.getName()); return savedEntityView; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ENTITY_VIEW), - ActionType.UNASSIGNED_FROM_EDGE, user, e, entityViewId.toString(), edgeId.toString()); - throw e; - } - } - - @Override - public EntityView unassignEntityViewFromCustomer(TenantId tenantId, EntityViewId entityViewId, Customer customer, User user) throws ThingsboardException { - ActionType actionType = ActionType.UNASSIGNED_FROM_CUSTOMER; - try { - EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromCustomer(tenantId, entityViewId)); - - notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, entityViewId, customer.getId(), savedEntityView, - actionType, user, savedEntityView.getId().toString(), customer.getId().toString(), customer.getName()); - - return savedEntityView; - } catch (Exception e) { - notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ENTITY_VIEW), - actionType, user, e, entityViewId.toString()); + actionType, user, e, entityViewId.toString(), edgeId.toString()); throw e; } } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/ota/DefaultTbOtaPackageService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/ota/DefaultTbOtaPackageService.java index 4d3c61e615..3cc444caf5 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/ota/DefaultTbOtaPackageService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/ota/DefaultTbOtaPackageService.java @@ -50,9 +50,8 @@ public class DefaultTbOtaPackageService extends AbstractTbEntityService implemen try { OtaPackageInfo savedOtaPackageInfo = otaPackageService.saveOtaPackageInfo(new OtaPackageInfo(saveOtaPackageInfoRequest), saveOtaPackageInfoRequest.isUsesUrl()); - boolean sendMsgToEdge = savedOtaPackageInfo.hasUrl() || savedOtaPackageInfo.isHasData(); - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedOtaPackageInfo.getId(), - savedOtaPackageInfo, user, actionType, sendMsgToEdge, null); + notificationEntityService.logEntityAction(tenantId, savedOtaPackageInfo.getId(), savedOtaPackageInfo, + null, actionType, user); return savedOtaPackageInfo; } catch (Exception e) { @@ -65,6 +64,7 @@ public class DefaultTbOtaPackageService extends AbstractTbEntityService implemen @Override public OtaPackageInfo saveOtaPackageData(OtaPackageInfo otaPackageInfo, String checksum, ChecksumAlgorithm checksumAlgorithm, byte[] data, String filename, String contentType, User user) throws ThingsboardException { + ActionType actionType = ActionType.UPDATED; TenantId tenantId = otaPackageInfo.getTenantId(); OtaPackageId otaPackageId = otaPackageInfo.getId(); try { @@ -87,27 +87,26 @@ public class DefaultTbOtaPackageService extends AbstractTbEntityService implemen otaPackage.setData(ByteBuffer.wrap(data)); otaPackage.setDataSize((long) data.length); OtaPackageInfo savedOtaPackage = otaPackageService.saveOtaPackage(otaPackage); - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedOtaPackage.getId(), - savedOtaPackage, user, ActionType.UPDATED, true, null); + notificationEntityService.logEntityAction(tenantId, savedOtaPackage.getId(), savedOtaPackage, null, actionType, user); return savedOtaPackage; } catch (Exception e) { - notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.OTA_PACKAGE), ActionType.UPDATED, - user, e, otaPackageId.toString()); + notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.OTA_PACKAGE), actionType, user, e, otaPackageId.toString()); throw e; } } @Override public void delete(OtaPackageInfo otaPackageInfo, User user) throws ThingsboardException { + ActionType actionType = ActionType.DELETED; TenantId tenantId = otaPackageInfo.getTenantId(); OtaPackageId otaPackageId = otaPackageInfo.getId(); try { otaPackageService.deleteOtaPackage(tenantId, otaPackageId); - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, otaPackageId, otaPackageInfo, - user, ActionType.DELETED, true, null, otaPackageInfo.getId().toString()); + notificationEntityService.logEntityAction(tenantId, otaPackageId, otaPackageInfo, null, + actionType, user, otaPackageInfo.getId().toString()); } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.OTA_PACKAGE), - ActionType.DELETED, user, e, otaPackageId.toString()); + actionType, user, e, otaPackageId.toString()); throw e; } } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java index 63e11aeb7a..b0ef8f4cde 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/queue/DefaultTbQueueService.java @@ -15,12 +15,11 @@ */ package org.thingsboard.server.service.entitiy.queue; -import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.TenantProfile; -import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.queue.Queue; @@ -28,7 +27,6 @@ import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfi import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.queue.TbQueueAdmin; -import org.thingsboard.server.queue.scheduler.SchedulerComponent; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; @@ -36,20 +34,17 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @Slf4j @Service @TbCoreComponent -@AllArgsConstructor +@RequiredArgsConstructor public class DefaultTbQueueService extends AbstractTbEntityService implements TbQueueService { - private static final long DELETE_DELAY = 30; private final QueueService queueService; private final TbClusterService tbClusterService; private final TbQueueAdmin tbQueueAdmin; - private final SchedulerComponent scheduler; @Override public Queue saveQueue(Queue queue) { @@ -71,8 +66,6 @@ public class DefaultTbQueueService extends AbstractTbEntityService implements Tb onQueueUpdated(savedQueue, oldQueue); } - notificationEntityService.notifySendMsgToEdgeService(queue.getTenantId(), savedQueue.getId(), create ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); - return savedQueue; } @@ -93,7 +86,9 @@ public class DefaultTbQueueService extends AbstractTbEntityService implements Tb private void onQueueCreated(Queue queue) { for (int i = 0; i < queue.getPartitions(); i++) { tbQueueAdmin.createTopicIfNotExists( - new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName()); + new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName(), + queue.getCustomProperties() + ); } tbClusterService.onQueueChange(queue); @@ -108,21 +103,15 @@ public class DefaultTbQueueService extends AbstractTbEntityService implements Tb log.info("Added [{}] new partitions to [{}] queue", currentPartitions - oldPartitions, queue.getName()); for (int i = oldPartitions; i < currentPartitions; i++) { tbQueueAdmin.createTopicIfNotExists( - new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName()); + new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName(), + queue.getCustomProperties() + ); } tbClusterService.onQueueChange(queue); } else { log.info("Removed [{}] partitions from [{}] queue", oldPartitions - currentPartitions, queue.getName()); tbClusterService.onQueueChange(queue); - - scheduler.schedule(() -> { - for (int i = currentPartitions; i < oldPartitions; i++) { - String fullTopicName = new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName(); - log.info("Removed partition [{}]", fullTopicName); - tbQueueAdmin.deleteTopic( - fullTopicName); - } - }, DELETE_DELAY, TimeUnit.SECONDS); + // TODO: move all the messages left in old partitions and delete topics } } else if (!oldQueue.equals(queue)) { tbClusterService.onQueueChange(queue); @@ -131,22 +120,7 @@ public class DefaultTbQueueService extends AbstractTbEntityService implements Tb private void onQueueDeleted(Queue queue) { tbClusterService.onQueueDelete(queue); - // queueStatsService.deleteQueueStatsByQueueId(tenantId, queueId); - - scheduler.schedule(() -> { - for (int i = 0; i < queue.getPartitions(); i++) { - String fullTopicName = new TopicPartitionInfo(queue.getTopic(), queue.getTenantId(), i, false).getFullTopicName(); - log.info("Deleting queue [{}]", fullTopicName); - try { - tbQueueAdmin.deleteTopic(fullTopicName); - } catch (Exception e) { - log.error("Failed to delete queue [{}]", fullTopicName); - } - } - }, DELETE_DELAY, TimeUnit.SECONDS); - - notificationEntityService.notifySendMsgToEdgeService(queue.getTenantId(), queue.getId(), EdgeEventActionType.DELETED); } @Override @@ -198,6 +172,10 @@ public class DefaultTbQueueService extends AbstractTbEntityService implements Tb } } + if (log.isDebugEnabled()) { + log.debug("[{}] Handling profile queue config update: creating queues {}, updating {}, deleting {}. Affected tenants: {}", + newTenantProfile.getUuidId(), toCreate, toUpdate, toRemove, tenantIds); + } tenantIds.forEach(tenantId -> { toCreate.forEach(key -> saveQueue(new Queue(tenantId, newQueues.get(key)))); diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/DefaultTbTenantService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/DefaultTbTenantService.java index 19d4126989..3267ed1c17 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/DefaultTbTenantService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/DefaultTbTenantService.java @@ -54,6 +54,7 @@ public class DefaultTbTenantService extends AbstractTbEntityService implements T if (created) { installScripts.createDefaultRuleChains(savedTenant.getId()); installScripts.createDefaultEdgeRuleChains(savedTenant.getId()); + installScripts.createDefaultTenantDashboards(savedTenant.getId(), null); } tenantProfileCache.evict(savedTenant.getId()); notificationEntityService.notifyCreateOrUpdateTenant(savedTenant, created ? diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java index 1219a5b929..15bcc2e040 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/DefaultTbTenantProfileService.java @@ -44,16 +44,14 @@ public class DefaultTbTenantProfileService extends AbstractTbEntityService imple @Override public TenantProfile save(TenantId tenantId, TenantProfile tenantProfile, TenantProfile oldTenantProfile) throws ThingsboardException { TenantProfile savedTenantProfile = checkNotNull(tenantProfileService.saveTenantProfile(tenantId, tenantProfile)); - if (oldTenantProfile != null && savedTenantProfile.isIsolatedTbRuleEngine()) { - List tenantIds = tenantService.findTenantIdsByTenantProfileId(savedTenantProfile.getId()); - tbQueueService.updateQueuesByTenants(tenantIds, savedTenantProfile, oldTenantProfile); - } - tenantProfileCache.put(savedTenantProfile); tbClusterService.onTenantProfileChange(savedTenantProfile, null); tbClusterService.broadcastEntityStateChangeEvent(TenantId.SYS_TENANT_ID, savedTenantProfile.getId(), tenantProfile.getId() == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); + List tenantIds = tenantService.findTenantIdsByTenantProfileId(savedTenantProfile.getId()); + tbQueueService.updateQueuesByTenants(tenantIds, savedTenantProfile, oldTenantProfile); + return savedTenantProfile; } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/TbTenantProfileService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/TbTenantProfileService.java index c45d3314cd..2588f4a82c 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/TbTenantProfileService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/profile/TbTenantProfileService.java @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; public interface TbTenantProfileService { + TenantProfile save(TenantId tenantId, TenantProfile tenantProfile, TenantProfile oldTenantProfile) throws ThingsboardException; void delete(TenantId tenantId, TenantProfile tenantProfile) throws ThingsboardException; diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java index c83c48cc8a..2c2db7c6c7 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java @@ -52,7 +52,7 @@ public class DefaultUserService extends AbstractTbEntityService implements TbUse ActionType actionType = tbUser.getId() == null ? ActionType.ADDED : ActionType.UPDATED; try { boolean sendEmail = tbUser.getId() == null && sendActivationMail; - User savedUser = checkNotNull(userService.saveUser(tbUser)); + User savedUser = checkNotNull(userService.saveUser(tenantId, tbUser)); if (sendEmail) { UserCredentials userCredentials = userService.findUserCredentialsByUserId(tenantId, savedUser.getId()); String baseUrl = systemSecurityService.getBaseUrl(tenantId, customerId, request); @@ -62,12 +62,11 @@ public class DefaultUserService extends AbstractTbEntityService implements TbUse try { mailService.sendActivationEmail(activateUrl, email); } catch (ThingsboardException e) { - userService.deleteUser(tenantId, savedUser.getId()); + userService.deleteUser(tenantId, savedUser); throw e; } } - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, customerId, savedUser.getId(), - savedUser, user, actionType, true, null); + notificationEntityService.logEntityAction(tenantId, savedUser.getId(), savedUser, customerId, actionType, user); return savedUser; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.USER), tbUser, actionType, user, e); @@ -76,16 +75,16 @@ public class DefaultUserService extends AbstractTbEntityService implements TbUse } @Override - public void delete(TenantId tenantId, CustomerId customerId, User tbUser, User user) throws ThingsboardException { - UserId userId = tbUser.getId(); + public void delete(TenantId tenantId, CustomerId customerId, User user, User responsibleUser) throws ThingsboardException { + ActionType actionType = ActionType.DELETED; + UserId userId = user.getId(); try { - userService.deleteUser(tenantId, userId); - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, customerId, userId, tbUser, - user, ActionType.DELETED, true, null, customerId.toString()); + userService.deleteUser(tenantId, user); + notificationEntityService.logEntityAction(tenantId, userId, user, customerId, actionType, responsibleUser, customerId.toString()); } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.USER), - ActionType.DELETED, user, e, userId.toString()); + actionType, responsibleUser, e, userId.toString()); throw e; } } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java index 3ef79371d4..0764425116 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java @@ -25,5 +25,5 @@ import javax.servlet.http.HttpServletRequest; public interface TbUserService { User save(TenantId tenantId, CustomerId customerId, User tbUser, boolean sendActivationMail, HttpServletRequest request, User user) throws ThingsboardException; - void delete(TenantId tenantId, CustomerId customerId, User tbUser, User user) throws ThingsboardException; + void delete(TenantId tenantId, CustomerId customerId, User user, User responsibleUser) throws ThingsboardException; } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/bundle/DefaultWidgetsBundleService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/bundle/DefaultWidgetsBundleService.java index 7cc150b746..ffa11e8c46 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/bundle/DefaultWidgetsBundleService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/bundle/DefaultWidgetsBundleService.java @@ -21,17 +21,23 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.WidgetTypeId; +import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetsBundleService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; +import java.util.List; + @Service @TbCoreComponent @AllArgsConstructor public class DefaultWidgetsBundleService extends AbstractTbEntityService implements TbWidgetsBundleService { private final WidgetsBundleService widgetsBundleService; + private final WidgetTypeService widgetTypeService; @Override public WidgetsBundle save(WidgetsBundle widgetsBundle, User user) throws Exception { @@ -40,8 +46,8 @@ public class DefaultWidgetsBundleService extends AbstractTbEntityService impleme try { WidgetsBundle savedWidgetsBundle = checkNotNull(widgetsBundleService.saveWidgetsBundle(widgetsBundle)); autoCommit(user, savedWidgetsBundle.getId()); - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedWidgetsBundle.getId(), - savedWidgetsBundle, user, actionType, true, null); + notificationEntityService.logEntityAction(tenantId, savedWidgetsBundle.getId(), savedWidgetsBundle, + null, actionType, user); return savedWidgetsBundle; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.WIDGETS_BUNDLE), widgetsBundle, actionType, user, e); @@ -51,15 +57,26 @@ public class DefaultWidgetsBundleService extends AbstractTbEntityService impleme @Override public void delete(WidgetsBundle widgetsBundle, User user) { + ActionType actionType = ActionType.DELETED; TenantId tenantId = widgetsBundle.getTenantId(); try { widgetsBundleService.deleteWidgetsBundle(widgetsBundle.getTenantId(), widgetsBundle.getId()); - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, widgetsBundle.getId(), widgetsBundle, - user, ActionType.DELETED, true, null); + notificationEntityService.logEntityAction(tenantId, widgetsBundle.getId(), widgetsBundle, null, actionType, user); } catch (Exception e) { - notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.WIDGETS_BUNDLE), - ActionType.DELETED, user, e, widgetsBundle.getId()); + notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.WIDGETS_BUNDLE), actionType, user, e, widgetsBundle.getId()); throw e; } } + + @Override + public void updateWidgetsBundleWidgetTypes(WidgetsBundleId widgetsBundleId, List widgetTypeIds, User user) throws Exception { + widgetTypeService.updateWidgetsBundleWidgetTypes(user.getTenantId(), widgetsBundleId, widgetTypeIds); + autoCommit(user, widgetsBundleId); + } + + @Override + public void updateWidgetsBundleWidgetFqns(WidgetsBundleId widgetsBundleId, List widgetFqns, User user) throws Exception { + widgetTypeService.updateWidgetsBundleWidgetFqns(user.getTenantId(), widgetsBundleId, widgetFqns); + autoCommit(user, widgetsBundleId); + } } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/bundle/TbWidgetsBundleService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/bundle/TbWidgetsBundleService.java index d60bc66d66..7a954cdda8 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/bundle/TbWidgetsBundleService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/bundle/TbWidgetsBundleService.java @@ -15,8 +15,18 @@ */ package org.thingsboard.server.service.entitiy.widgets.bundle; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.WidgetTypeId; +import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.service.entitiy.SimpleTbEntityService; +import java.util.List; + public interface TbWidgetsBundleService extends SimpleTbEntityService { + + void updateWidgetsBundleWidgetTypes(WidgetsBundleId widgetsBundleId, List widgetTypeIds, User user) throws Exception; + + void updateWidgetsBundleWidgetFqns(WidgetsBundleId widgetsBundleId, List widgetFqns, User user) throws Exception; + } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/type/DefaultWidgetTypeService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/type/DefaultWidgetTypeService.java new file mode 100644 index 0000000000..53afc9fb4f --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/type/DefaultWidgetTypeService.java @@ -0,0 +1,80 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.entitiy.widgets.type; + +import lombok.AllArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.widget.WidgetType; +import org.thingsboard.server.common.data.widget.WidgetTypeDetails; +import org.thingsboard.server.dao.resource.ImageService; +import org.thingsboard.server.dao.widget.WidgetTypeService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.entitiy.AbstractTbEntityService; + +@Service +@TbCoreComponent +@AllArgsConstructor +public class DefaultWidgetTypeService extends AbstractTbEntityService implements TbWidgetTypeService { + + + private final WidgetTypeService widgetTypeService; + + @Override + public WidgetTypeDetails save(WidgetTypeDetails entity, User user) throws Exception { + return this.save(entity, false, user); + } + + @Override + public WidgetTypeDetails save(WidgetTypeDetails widgetTypeDetails, boolean updateExistingByFqn, User user) throws Exception { + TenantId tenantId = widgetTypeDetails.getTenantId(); + if (widgetTypeDetails.getId() == null && StringUtils.isNotEmpty(widgetTypeDetails.getFqn()) && updateExistingByFqn) { + WidgetType widgetType = widgetTypeService.findWidgetTypeByTenantIdAndFqn(tenantId, widgetTypeDetails.getFqn()); + if (widgetType != null) { + widgetTypeDetails.setId(widgetType.getId()); + } + } + ActionType actionType = widgetTypeDetails.getId() == null ? ActionType.ADDED : ActionType.UPDATED; + try { + WidgetTypeDetails savedWidgetTypeDetails = checkNotNull(widgetTypeService.saveWidgetType(widgetTypeDetails)); + autoCommit(user, savedWidgetTypeDetails.getId()); + notificationEntityService.logEntityAction(tenantId, savedWidgetTypeDetails.getId(), savedWidgetTypeDetails, + null, actionType, user); + return savedWidgetTypeDetails; + } catch (Exception e) { + notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.WIDGET_TYPE), widgetTypeDetails, actionType, user, e); + throw e; + } + } + + @Override + public void delete(WidgetTypeDetails widgetTypeDetails, User user) { + ActionType actionType = ActionType.DELETED; + TenantId tenantId = widgetTypeDetails.getTenantId(); + try { + widgetTypeService.deleteWidgetType(widgetTypeDetails.getTenantId(), widgetTypeDetails.getId()); + notificationEntityService.logEntityAction(tenantId, widgetTypeDetails.getId(), widgetTypeDetails, null, actionType, user); + } catch (Exception e) { + notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.WIDGET_TYPE), actionType, user, e, widgetTypeDetails.getId()); + throw e; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/type/TbWidgetTypeService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/type/TbWidgetTypeService.java new file mode 100644 index 0000000000..8951b49552 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/type/TbWidgetTypeService.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.entitiy.widgets.type; + +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.widget.WidgetTypeDetails; +import org.thingsboard.server.service.entitiy.SimpleTbEntityService; + +public interface TbWidgetTypeService extends SimpleTbEntityService { + + WidgetTypeDetails save(WidgetTypeDetails widgetTypeDetails, boolean updateExistingByFqn, User user) throws Exception; + +} diff --git a/application/src/main/java/org/thingsboard/server/service/executors/PubSubRuleNodeExecutorProvider.java b/application/src/main/java/org/thingsboard/server/service/executors/PubSubRuleNodeExecutorProvider.java new file mode 100644 index 0000000000..74e1222acf --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/executors/PubSubRuleNodeExecutorProvider.java @@ -0,0 +1,63 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.executors; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.ExecutorProvider; +import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.queue.util.TbRuleEngineComponent; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +@Lazy +@TbRuleEngineComponent +@Component +public class PubSubRuleNodeExecutorProvider implements ExecutorProvider { + + @Value("${service.rule_engine.pubsub.executor_thread_pool_size}") + private Integer threadPoolSize; + + /** + * Refers to com.google.cloud.pubsub.v1.Publisher default executor configuration + */ + private static final int THREADS_PER_CPU = 5; + private ScheduledExecutorService executor; + + @PostConstruct + public void init() { + if (threadPoolSize == null) { + threadPoolSize = THREADS_PER_CPU * Runtime.getRuntime().availableProcessors(); + } + executor = Executors.newScheduledThreadPool(threadPoolSize, ThingsBoardThreadFactory.forName("pubsub-rule-nodes")); + } + + @Override + public ScheduledExecutorService getExecutor() { + return executor; + } + + @PreDestroy + private void destroy() { + if (executor != null) { + executor.shutdownNow(); + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/executors/VersionControlExecutor.java b/application/src/main/java/org/thingsboard/server/service/executors/VersionControlExecutor.java new file mode 100644 index 0000000000..a3957653d9 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/executors/VersionControlExecutor.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.executors; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.AbstractListeningExecutor; + +@Component +public class VersionControlExecutor extends AbstractListeningExecutor { + + @Value("${vc.thread_pool_size:6}") + private int threadPoolSize; + + @Override + protected int getThreadPollSize() { + return threadPoolSize; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java b/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java new file mode 100644 index 0000000000..9edc81f9fd --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java @@ -0,0 +1,103 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.housekeeper; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionalEventListener; +import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; +import org.thingsboard.server.dao.housekeeper.HouseKeeperService; +import org.thingsboard.server.service.entitiy.alarm.TbAlarmService; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; + +@Component +@RequiredArgsConstructor +@Slf4j +public class InMemoryHouseKeeperServiceService implements HouseKeeperService { + + final TbAlarmService alarmService; + + ListeningExecutorService executor; + + AtomicInteger queueSize = new AtomicInteger(); + AtomicInteger totalProcessedCounter = new AtomicInteger(); + + @PostConstruct + public void init() { + log.debug("Starting HouseKeeper service"); + executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("housekeeper"))); + } + + @PreDestroy + public void destroy() { + if (executor != null) { + log.debug("Stopping HouseKeeper service"); + executor.shutdown(); + } + } + + @TransactionalEventListener(fallbackExecution = true) + public void handleEvent(DeleteEntityEvent event) { + log.trace("[{}] DeleteEntityEvent handler: {}", event.getTenantId(), event); + EntityId entityId = event.getEntityId(); + if (EntityType.USER.equals(entityId.getEntityType())) { + unassignDeletedUserAlarms(event.getTenantId(), (User) event.getEntity(), event.getTs()); + } + } + + @Override + public ListenableFuture> unassignDeletedUserAlarms(TenantId tenantId, User user, long unassignTs) { + log.debug("[{}][{}] unassignDeletedUserAlarms submitting, pending queue size: {} ", tenantId, user.getId().getId(), queueSize.get()); + queueSize.incrementAndGet(); + ListenableFuture> future = executor.submit(() -> alarmService.unassignDeletedUserAlarms(tenantId, user, unassignTs)); + Futures.addCallback(future, new FutureCallback<>() { + @Override + public void onSuccess(List alarmIds) { + queueSize.decrementAndGet(); + totalProcessedCounter.incrementAndGet(); + log.debug("[{}][{}] unassignDeletedUserAlarms finished, pending queue size: {}, total processed count: {} ", + tenantId, user.getId().getId(), queueSize.get(), totalProcessedCounter.get()); + } + + @Override + public void onFailure(Throwable throwable) { + queueSize.decrementAndGet(); + totalProcessedCounter.incrementAndGet(); + log.error("[{}][{}] unassignDeletedUserAlarms failed, pending queue size: {}, total processed count: {}", + tenantId, user.getId().getId(), queueSize.get(), totalProcessedCounter.get(), throwable); + } + }, MoreExecutors.directExecutor()); + return future; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java index 497627f629..63771f8165 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java @@ -83,7 +83,6 @@ import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; -import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.device.DeviceCredentialsService; @@ -99,6 +98,7 @@ import org.thingsboard.server.dao.tenant.TenantProfileService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.user.UserService; +import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetsBundleService; import org.thingsboard.server.service.security.auth.jwt.settings.JwtSettingsService; @@ -114,13 +114,14 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import static org.thingsboard.server.common.data.DataConstants.DEFAULT_DEVICE_TYPE; + @Service @Profile("install") @Slf4j public class DefaultSystemDataLoaderService implements SystemDataLoaderService { public static final String CUSTOMER_CRED = "customer"; - public static final String DEFAULT_DEVICE_TYPE = "default"; public static final String ACTIVITY_STATE = "active"; @Autowired @@ -135,6 +136,9 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @Autowired private AdminSettingsService adminSettingsService; + @Autowired + private WidgetTypeService widgetTypeService; + @Autowired private WidgetsBundleService widgetsBundleService; @@ -256,6 +260,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { ObjectNode node = JacksonUtil.newObjectNode(); node.put("baseUrl", "http://localhost:8080"); node.put("prohibitDifferentUrl", false); + node.set("connectivity", createDeviceConnectivityConfiguration()); generalSettings.setJsonValue(node); adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, generalSettings); @@ -276,6 +281,53 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { node.put("showChangePassword", false); mailSettings.setJsonValue(node); adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, mailSettings); + + AdminSettings connectivitySettings = new AdminSettings(); + connectivitySettings.setTenantId(TenantId.SYS_TENANT_ID); + connectivitySettings.setKey("connectivity"); + connectivitySettings.setJsonValue(createDeviceConnectivityConfiguration()); + adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, connectivitySettings); + } + + private ObjectNode createDeviceConnectivityConfiguration() { + ObjectNode config = JacksonUtil.newObjectNode(); + + ObjectNode http = JacksonUtil.newObjectNode(); + http.put("enabled", true); + http.put("host", ""); + http.put("port", 8080); + config.set("http", http); + + ObjectNode https = JacksonUtil.newObjectNode(); + https.put("enabled", false); + https.put("host", ""); + https.put("port", 443); + config.set("https", https); + + ObjectNode mqtt = JacksonUtil.newObjectNode(); + mqtt.put("enabled", true); + mqtt.put("host", ""); + mqtt.put("port", 1883); + config.set("mqtt", mqtt); + + ObjectNode mqtts = JacksonUtil.newObjectNode(); + mqtts.put("enabled", false); + mqtts.put("host", ""); + mqtts.put("port", 8883); + config.set("mqtts", mqtts); + + ObjectNode coap = JacksonUtil.newObjectNode(); + coap.put("enabled", true); + coap.put("host", ""); + coap.put("port", 5683); + config.set("coap", coap); + + ObjectNode coaps = JacksonUtil.newObjectNode(); + coaps.put("enabled", false); + coaps.put("host", ""); + coaps.put("port", 5684); + config.set("coaps", coaps); + return config; } @Override @@ -478,14 +530,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("humidityAlarmThreshold", (long) 30)))); installScripts.loadDashboards(demoTenant.getId(), null); - } - - @Override - public void deleteSystemWidgetBundle(String bundleAlias) throws Exception { - WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(TenantId.SYS_TENANT_ID, bundleAlias); - if (widgetsBundle != null) { - widgetsBundleService.deleteWidgetsBundle(TenantId.SYS_TENANT_ID, widgetsBundle.getId()); - } + installScripts.createDefaultTenantDashboards(demoTenant.getId(), null); } @Override @@ -493,27 +538,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { installScripts.loadSystemWidgets(); } - @Override - public void updateSystemWidgets() throws Exception { - this.deleteSystemWidgetBundle("charts"); - this.deleteSystemWidgetBundle("cards"); - this.deleteSystemWidgetBundle("maps"); - this.deleteSystemWidgetBundle("analogue_gauges"); - this.deleteSystemWidgetBundle("digital_gauges"); - this.deleteSystemWidgetBundle("gpio_widgets"); - this.deleteSystemWidgetBundle("alarm_widgets"); - this.deleteSystemWidgetBundle("control_widgets"); - this.deleteSystemWidgetBundle("maps_v2"); - this.deleteSystemWidgetBundle("gateway_widgets"); - this.deleteSystemWidgetBundle("input_widgets"); - this.deleteSystemWidgetBundle("date"); - this.deleteSystemWidgetBundle("entity_admin_widgets"); - this.deleteSystemWidgetBundle("navigation_widgets"); - this.deleteSystemWidgetBundle("edge_widgets"); - this.deleteSystemWidgetBundle("home_page_widgets"); - installScripts.loadSystemWidgets(); - } - private User createUser(Authority authority, TenantId tenantId, CustomerId customerId, @@ -524,7 +548,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { user.setEmail(email); user.setTenantId(tenantId); user.setCustomerId(customerId); - user = userService.saveUser(user); + user = userService.saveUser(tenantId, user); UserCredentials userCredentials = userService.findUserCredentialsByUserId(TenantId.SYS_TENANT_ID, user.getId()); userCredentials.setPassword(passwordEncoder.encode(password)); userCredentials.setEnabled(true); @@ -709,4 +733,9 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { executor.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS); } + @Override + public void updateDefaultNotificationConfigs() { + notificationSettingsService.updateDefaultNotificationConfigs(TenantId.SYS_TENANT_ID); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java index 9a810aa446..9373ac6d34 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java +++ b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java @@ -16,22 +16,29 @@ package org.thingsboard.server.service.install; import com.fasterxml.jackson.databind.JsonNode; +import lombok.Getter; +import lombok.Setter; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.ResourceType; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.widget.DeprecatedFilter; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; +import org.thingsboard.server.common.data.widget.WidgetTypeInfo; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.exception.DataValidationException; @@ -40,14 +47,20 @@ import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetsBundleService; +import org.thingsboard.server.service.install.update.ImagesUpdater; import java.io.IOException; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Base64; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; import static org.thingsboard.server.utils.LwM2mObjectModelUtils.toLwm2mResource; @@ -69,6 +82,7 @@ public class InstallScripts { public static final String DEVICE_PROFILE_DIR = "device_profile"; public static final String DEMO_DIR = "demo"; public static final String RULE_CHAINS_DIR = "rule_chains"; + public static final String WIDGET_TYPES_DIR = "widget_types"; public static final String WIDGET_BUNDLES_DIR = "widget_bundles"; public static final String OAUTH2_CONFIG_TEMPLATES_DIR = "oauth2_config_templates"; public static final String DASHBOARDS_DIR = "dashboards"; @@ -99,15 +113,20 @@ public class InstallScripts { @Autowired private ResourceService resourceService; - private Path getTenantRuleChainsDir() { + @Autowired + private ImagesUpdater imagesUpdater; + @Getter @Setter + private boolean updateImages = false; + + Path getTenantRuleChainsDir() { return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, RULE_CHAINS_DIR); } - private Path getDeviceProfileDefaultRuleChainTemplateFilePath() { + Path getDeviceProfileDefaultRuleChainTemplateFilePath() { return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, DEVICE_PROFILE_DIR, "rule_chain_template.json"); } - private Path getEdgeRuleChainsDir() { + Path getEdgeRuleChainsDir() { return Paths.get(getDataDir(), JSON_DIR, EDGE_DIR, RULE_CHAINS_DIR); } @@ -143,18 +162,22 @@ public class InstallScripts { } private void loadRuleChainsFromPath(TenantId tenantId, Path ruleChainsPath) throws IOException { + findRuleChainsFromPath(ruleChainsPath).forEach(path -> { + try { + createRuleChainFromFile(tenantId, path, null); + } catch (Exception e) { + log.error("Unable to load rule chain from json: [{}]", path.toString()); + throw new RuntimeException("Unable to load rule chain from json", e); + } + }); + } + + List findRuleChainsFromPath(Path ruleChainsPath) throws IOException { + List paths = new ArrayList<>(); try (DirectoryStream dirStream = Files.newDirectoryStream(ruleChainsPath, path -> path.toString().endsWith(InstallScripts.JSON_EXT))) { - dirStream.forEach( - path -> { - try { - createRuleChainFromFile(tenantId, path, null); - } catch (Exception e) { - log.error("Unable to load rule chain from json: [{}]", path.toString()); - throw new RuntimeException("Unable to load rule chain from json", e); - } - } - ); + dirStream.forEach(paths::add); } + return paths; } public RuleChain createDefaultRuleChain(TenantId tenantId, String ruleChainName) throws IOException { @@ -173,45 +196,150 @@ public class InstallScripts { ruleChain = ruleChainService.saveRuleChain(ruleChain); ruleChainMetaData.setRuleChainId(ruleChain.getId()); - ruleChainService.saveRuleChainMetaData(TenantId.SYS_TENANT_ID, ruleChainMetaData); + ruleChainService.saveRuleChainMetaData(TenantId.SYS_TENANT_ID, ruleChainMetaData, Function.identity()); return ruleChain; } public void loadSystemWidgets() throws Exception { + log.info("Loading system widgets"); + Map widgetsBundlesMap = new HashMap<>(); Path widgetBundlesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, WIDGET_BUNDLES_DIR); try (DirectoryStream dirStream = Files.newDirectoryStream(widgetBundlesDir, path -> path.toString().endsWith(JSON_EXT))) { dirStream.forEach( path -> { + JsonNode widgetsBundleDescriptorJson; + try { + widgetsBundleDescriptorJson = JacksonUtil.toJsonNode(path.toFile()); + } catch (Exception e) { + log.error("Unable to parse widgets bundle from json: [{}]", path); + throw new RuntimeException("Unable to parse widgets bundle from json", e); + } + if (widgetsBundleDescriptorJson == null || !widgetsBundleDescriptorJson.has("widgetsBundle")) { + log.error("Invalid widgets bundle json: [{}]", path); + throw new RuntimeException("Invalid widgets bundle json: [" + path + "]"); + } + widgetsBundlesMap.put(path, widgetsBundleDescriptorJson); + JsonNode bundleAliasNode = widgetsBundleDescriptorJson.get("widgetsBundle").get("alias"); + if (bundleAliasNode == null || !bundleAliasNode.isTextual()) { + log.error("Invalid widgets bundle json: [{}]", path); + throw new RuntimeException("Invalid widgets bundle json: [" + path + "]"); + } + String bundleAlias = bundleAliasNode.asText(); try { - JsonNode widgetsBundleDescriptorJson = JacksonUtil.toJsonNode(path.toFile()); - JsonNode widgetsBundleJson = widgetsBundleDescriptorJson.get("widgetsBundle"); - WidgetsBundle widgetsBundle = JacksonUtil.treeToValue(widgetsBundleJson, WidgetsBundle.class); - WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); - JsonNode widgetTypesArrayJson = widgetsBundleDescriptorJson.get("widgetTypes"); - widgetTypesArrayJson.forEach( - widgetTypeJson -> { - try { - WidgetTypeDetails widgetTypeDetails = JacksonUtil.treeToValue(widgetTypeJson, WidgetTypeDetails.class); - widgetTypeDetails.setBundleAlias(savedWidgetsBundle.getAlias()); - widgetTypeService.saveWidgetType(widgetTypeDetails); - } catch (Exception e) { - log.error("Unable to load widget type from json: [{}]", path.toString()); - throw new RuntimeException("Unable to load widget type from json", e); - } - } - ); + this.deleteSystemWidgetBundle(bundleAlias); } catch (Exception e) { - log.error("Unable to load widgets bundle from json: [{}]", path.toString()); - throw new RuntimeException("Unable to load widgets bundle from json", e); + log.error("Failed to delete system widgets bundle: [{}]", bundleAlias); + throw new RuntimeException("Failed to delete system widgets bundle: [" + bundleAlias + "]", e); } } ); } + Path widgetTypesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, WIDGET_TYPES_DIR); + if (Files.exists(widgetTypesDir)) { + try (DirectoryStream dirStream = Files.newDirectoryStream(widgetTypesDir, path -> path.toString().endsWith(JSON_EXT))) { + dirStream.forEach( + path -> { + try { + JsonNode widgetTypeJson = JacksonUtil.toJsonNode(path.toFile()); + WidgetTypeDetails widgetTypeDetails = JacksonUtil.treeToValue(widgetTypeJson, WidgetTypeDetails.class); + widgetTypeService.saveWidgetType(widgetTypeDetails); + } catch (Exception e) { + log.error("Unable to load widget type from json: [{}]", path.toString()); + throw new RuntimeException("Unable to load widget type from json", e); + } + } + ); + } + } + for (var widgetsBundleDescriptorEntry : widgetsBundlesMap.entrySet()) { + Path path = widgetsBundleDescriptorEntry.getKey(); + try { + JsonNode widgetsBundleDescriptorJson = widgetsBundleDescriptorEntry.getValue(); + JsonNode widgetsBundleJson = widgetsBundleDescriptorJson.get("widgetsBundle"); + WidgetsBundle widgetsBundle = JacksonUtil.treeToValue(widgetsBundleJson, WidgetsBundle.class); + WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); + List widgetTypeFqns = new ArrayList<>(); + if (widgetsBundleDescriptorJson.has("widgetTypes")) { + JsonNode widgetTypesArrayJson = widgetsBundleDescriptorJson.get("widgetTypes"); + widgetTypesArrayJson.forEach( + widgetTypeJson -> { + try { + WidgetTypeDetails widgetTypeDetails = JacksonUtil.treeToValue(widgetTypeJson, WidgetTypeDetails.class); + var savedWidgetType = widgetTypeService.saveWidgetType(widgetTypeDetails); + widgetTypeFqns.add(savedWidgetType.getFqn()); + } catch (Exception e) { + log.error("Unable to load widget type from json: [{}]", path.toString()); + throw new RuntimeException("Unable to load widget type from json", e); + } + } + ); + } + if (widgetsBundleDescriptorJson.has("widgetTypeFqns")) { + JsonNode widgetFqnsArrayJson = widgetsBundleDescriptorJson.get("widgetTypeFqns"); + widgetFqnsArrayJson.forEach(fqnJson -> { + widgetTypeFqns.add(fqnJson.asText()); + }); + } + widgetTypeService.updateWidgetsBundleWidgetFqns(TenantId.SYS_TENANT_ID, savedWidgetsBundle.getId(), widgetTypeFqns); + } catch (Exception e) { + log.error("Unable to load widgets bundle from json: [{}]", path.toString()); + throw new RuntimeException("Unable to load widgets bundle from json", e); + } + } + } + + private void deleteSystemWidgetBundle(String bundleAlias) { + WidgetsBundle widgetsBundle = widgetsBundleService.findWidgetsBundleByTenantIdAndAlias(TenantId.SYS_TENANT_ID, bundleAlias); + if (widgetsBundle != null) { + PageData widgetTypes; + var pageLink = new PageLink(1024); + do { + widgetTypes = widgetTypeService.findWidgetTypesInfosByWidgetsBundleId(TenantId.SYS_TENANT_ID, widgetsBundle.getId(), false, DeprecatedFilter.ALL, null, pageLink); + for (var widgetType : widgetTypes.getData()) { + widgetTypeService.deleteWidgetType(TenantId.SYS_TENANT_ID, widgetType.getId()); + } + pageLink.nextPageLink(); + } while (widgetTypes.hasNext()); + widgetsBundleService.deleteWidgetsBundle(TenantId.SYS_TENANT_ID, widgetsBundle.getId()); + } + } + + public void updateImages() { + imagesUpdater.updateWidgetsBundlesImages(); + imagesUpdater.updateWidgetTypesImages(); + imagesUpdater.updateDashboardsImages(); + imagesUpdater.updateDeviceProfilesImages(); + imagesUpdater.updateAssetProfilesImages(); + } + + @SneakyThrows + public void loadSystemImages() { + log.info("Loading system images..."); + Stream dashboardsFiles = Files.list(Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, DASHBOARDS_DIR)); + try (dashboardsFiles) { + dashboardsFiles.forEach(file -> { + try { + Dashboard dashboard = JacksonUtil.OBJECT_MAPPER.readValue(file.toFile(), Dashboard.class); + imagesUpdater.createSystemImages(dashboard); + } catch (Exception e) { + log.error("Failed to create system images for default dashboard {}", file.getFileName(), e); + } + }); + } } public void loadDashboards(TenantId tenantId, CustomerId customerId) throws Exception { Path dashboardsDir = Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, DASHBOARDS_DIR); + loadDashboardsFromDir(tenantId, customerId, dashboardsDir); + } + + public void createDefaultTenantDashboards(TenantId tenantId, CustomerId customerId) throws Exception { + Path dashboardsDir = Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, DASHBOARDS_DIR); + loadDashboardsFromDir(tenantId, customerId, dashboardsDir); + } + + private void loadDashboardsFromDir(TenantId tenantId, CustomerId customerId, Path dashboardsDir) throws IOException { try (DirectoryStream dirStream = Files.newDirectoryStream(dashboardsDir, path -> path.toString().endsWith(JSON_EXT))) { dirStream.forEach( path -> { @@ -267,12 +395,12 @@ public class InstallScripts { } public void loadSystemLwm2mResources() { - Path resourceLwm2mPath = Paths.get(dataDir, MODELS_LWM2M_DIR); + Path resourceLwm2mPath = Paths.get(getDataDir(), MODELS_LWM2M_DIR); try (DirectoryStream dirStream = Files.newDirectoryStream(resourceLwm2mPath, path -> path.toString().endsWith(InstallScripts.XML_EXT))) { dirStream.forEach( path -> { try { - String data = Base64.getEncoder().encodeToString(Files.readAllBytes(path)); + byte[] data = Files.readAllBytes(path); TbResource tbResource = new TbResource(); tbResource.setTenantId(TenantId.SYS_TENANT_ID); tbResource.setData(data); @@ -293,15 +421,13 @@ public class InstallScripts { private void doSaveLwm2mResource(TbResource resource) throws ThingsboardException { log.trace("Executing saveResource [{}]", resource); - if (StringUtils.isEmpty(resource.getData())) { + if (resource.getData() == null || resource.getData().length == 0) { throw new DataValidationException("Resource data should be specified!"); } toLwm2mResource(resource); - TbResource foundResource = - resourceService.getResource(TenantId.SYS_TENANT_ID, ResourceType.LWM2M_MODEL, resource.getResourceKey()); + TbResource foundResource = resourceService.findResourceByTenantIdAndKey(TenantId.SYS_TENANT_ID, ResourceType.LWM2M_MODEL, resource.getResourceKey()); if (foundResource == null) { resourceService.saveResource(resource); } } } - diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index e1a34e616b..c2444390f6 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -63,6 +63,7 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; import static org.thingsboard.server.service.install.DatabaseHelper.ADDITIONAL_INFO; import static org.thingsboard.server.service.install.DatabaseHelper.ASSIGNED_CUSTOMERS; @@ -717,46 +718,84 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService } break; case "3.5.0": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - if (isOldSchema(conn, 3005000)) { - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.5.0", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3005001;"); - } - log.info("Schema updated."); - } catch (Exception e) { - log.error("Failed updating schema!!!", e); - } + updateSchema("3.5.0", 3005000, "3.5.1", 3005001, null); break; case "3.5.1": - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { - log.info("Updating schema ..."); - if (isOldSchema(conn, 3005001)) { - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.5.1", SCHEMA_UPDATE_SQL); - loadSql(schemaUpdateFile, conn); - - String[] entityNames = new String[]{"device", "component_descriptor", "customer", "dashboard", "rule_chain", "rule_node", "ota_package", - "asset_profile", "asset", "device_profile", "tb_user", "tenant_profile", "tenant", "widgets_bundle", "entity_view", "edge"}; - for (String entityName : entityNames) { - try { - conn.createStatement().execute("ALTER TABLE " + entityName + " DROP COLUMN " + SEARCH_TEXT + " CASCADE"); - } catch (Exception e) { - } + updateSchema("3.5.1", 3005001, "3.6.0", 3006000, conn -> { + String[] entityNames = new String[]{"device", "component_descriptor", "customer", "dashboard", "rule_chain", "rule_node", "ota_package", + "asset_profile", "asset", "device_profile", "tb_user", "tenant_profile", "tenant", "widgets_bundle", "entity_view", "edge"}; + for (String entityName : entityNames) { + try { + conn.createStatement().execute("ALTER TABLE " + entityName + " DROP COLUMN " + SEARCH_TEXT + " CASCADE"); + } catch (Exception e) { } - - conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3005002;"); } - log.info("Schema updated."); - } catch (Exception e) { - log.error("Failed updating schema!!!", e); - } + try { + conn.createStatement().execute("ALTER TABLE component_descriptor ADD COLUMN IF NOT EXISTS configuration_version int DEFAULT 0;"); + } catch (Exception e) { + } + try { + conn.createStatement().execute("ALTER TABLE rule_node ADD COLUMN IF NOT EXISTS configuration_version int DEFAULT 0;"); + } catch (Exception e) { + } + try { + conn.createStatement().execute("CREATE INDEX IF NOT EXISTS idx_rule_node_type_configuration_version ON rule_node(type, configuration_version);"); + } catch (Exception e) { + } + try { + conn.createStatement().execute("UPDATE rule_node SET " + + "configuration = (configuration::jsonb || '{\"updateAttributesOnlyOnValueChange\": \"false\"}'::jsonb)::varchar, " + + "configuration_version = 1 " + + "WHERE type = 'org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode' AND configuration_version < 1;"); + } catch (Exception e) { + } + try { + conn.createStatement().execute("CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_unread ON notification(recipient_id) WHERE status <> 'READ';"); + } catch (Exception e) { + } + }); + break; + case "3.6.0": + updateSchema("3.6.0", 3006000, "3.6.1", 3006001, null); + break; + case "3.6.1": + updateSchema("3.6.1", 3006001, "3.6.2", 3006002, connection -> { + try { + Path saveAttributesNodeUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.6.1", "save_attributes_node_update.sql"); + loadSql(saveAttributesNodeUpdateFile, connection); + } catch (Exception e) { + log.warn("Failed to execute update script for save attributes rule nodes due to: ", e); + } + try { + connection.createStatement().execute("CREATE INDEX IF NOT EXISTS idx_asset_profile_id ON asset(tenant_id, asset_profile_id);"); + } catch (Exception e) { + } + }); break; default: throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); } } + private void updateSchema(String oldVersionStr, int oldVersion, String newVersionStr, int newVersion, Consumer additionalAction) { + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { + log.info("Updating schema ..."); + if (isOldSchema(conn, oldVersion)) { + Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", oldVersionStr, SCHEMA_UPDATE_SQL); + loadSql(schemaUpdateFile, conn); + if (additionalAction != null) { + additionalAction.accept(conn); + } + conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = " + newVersion + ";"); + log.info("Schema updated to version {}", newVersionStr); + } else { + log.info("Skip schema re-update to version {}. Use env flag 'SKIP_SCHEMA_VERSION_CHECK' to force the re-update.", newVersionStr); + } + } catch (Exception e) { + log.error("Failed updating schema!!!", e); + } + } + private void runSchemaUpdateScript(Connection connection, String version) throws Exception { Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", version, SCHEMA_UPDATE_SQL); loadSql(schemaUpdateFile, connection); diff --git a/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java index fb6b28592c..344870cf1b 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java @@ -31,14 +31,12 @@ public interface SystemDataLoaderService { void loadSystemWidgets() throws Exception; - void updateSystemWidgets() throws Exception; - void loadDemoData() throws Exception; - void deleteSystemWidgetBundle(String bundleAlias) throws Exception; - void createQueues(); void createDefaultNotificationConfigs(); + void updateDefaultNotificationConfigs(); + } diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DataUpdateService.java index ae573b829a..289e3b1c94 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/DataUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/DataUpdateService.java @@ -19,4 +19,5 @@ public interface DataUpdateService { void updateData(String fromVersion) throws Exception; + void upgradeRuleNodes(); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultCacheCleanupService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultCacheCleanupService.java index 278fc0adf5..350005536a 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultCacheCleanupService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultCacheCleanupService.java @@ -27,6 +27,9 @@ import org.springframework.stereotype.Service; import java.util.Objects; import java.util.Optional; +import static org.thingsboard.server.common.data.CacheConstants.RESOURCE_INFO_CACHE; +import static org.thingsboard.server.common.data.CacheConstants.SECURITY_SETTINGS_CACHE; + @RequiredArgsConstructor @Service @Profile("install") @@ -86,6 +89,11 @@ public class DefaultCacheCleanupService implements CacheCleanupService { log.info("Clearing cache to upgrade from version 3.4.4 to 3.5.0"); clearAll(); break; + case "3.6.1": + log.info("Clearing cache to upgrade from version 3.6.1 to 3.6.2"); + clearCacheByName(SECURITY_SETTINGS_CACHE); + clearCacheByName(RESOURCE_INFO_CACHE); + break; default: //Do nothing, since cache cleanup is optional. } diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java index 13943d5698..7d08fdca00 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java @@ -17,6 +17,7 @@ package org.thingsboard.server.service.install.update; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.Lists; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; @@ -30,6 +31,7 @@ import org.thingsboard.rule.engine.flow.TbRuleChainInputNode; import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration; import org.thingsboard.rule.engine.profile.TbDeviceProfileNode; import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration; +import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.Tenant; @@ -45,7 +47,9 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; import org.thingsboard.server.common.data.kv.ReadTsKvQuery; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.query.DynamicValue; @@ -65,6 +69,7 @@ import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfi import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.alarm.AlarmDao; import org.thingsboard.server.dao.audit.AuditLogDao; +import org.thingsboard.server.dao.device.DeviceConnectivityConfiguration; import org.thingsboard.server.dao.edge.EdgeEventDao; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.entityview.EntityViewService; @@ -73,19 +78,24 @@ import org.thingsboard.server.dao.model.sql.DeviceProfileEntity; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.settings.AdminSettingsService; +import org.thingsboard.server.dao.sql.JpaExecutorService; import org.thingsboard.server.dao.sql.device.DeviceProfileRepository; import org.thingsboard.server.dao.tenant.TenantProfileService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.timeseries.TimeseriesService; +import org.thingsboard.server.service.component.ComponentDiscoveryService; +import org.thingsboard.server.service.component.RuleNodeClassInfo; import org.thingsboard.server.service.install.InstallScripts; import org.thingsboard.server.service.install.SystemDataLoaderService; -import org.thingsboard.server.service.install.TbRuleEngineQueueConfigService; +import org.thingsboard.server.utils.TbNodeUpgradeUtils; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Function; import java.util.stream.Collectors; import static org.thingsboard.server.common.data.StringUtils.isBlank; @@ -95,6 +105,9 @@ import static org.thingsboard.server.common.data.StringUtils.isBlank; @Slf4j public class DefaultDataUpdateService implements DataUpdateService { + private static final int MAX_PENDING_SAVE_RULE_NODE_FUTURES = 256; + private static final int DEFAULT_PAGE_SIZE = 1024; + @Autowired private TenantService tenantService; @@ -133,7 +146,7 @@ public class DefaultDataUpdateService implements DataUpdateService { private QueueService queueService; @Autowired - private TbRuleEngineQueueConfigService queueConfig; + private ComponentDiscoveryService componentDiscoveryService; @Autowired private SystemDataLoaderService systemDataLoaderService; @@ -147,6 +160,15 @@ public class DefaultDataUpdateService implements DataUpdateService { @Autowired private EdgeEventDao edgeEventDao; + @Autowired + JpaExecutorService jpaExecutorService; + + @Autowired + AdminSettingsService adminSettingsService; + + @Autowired + DeviceConnectivityConfiguration connectivityConfiguration; + @Override public void updateData(String fromVersion) throws Exception { switch (fromVersion) { @@ -195,19 +217,109 @@ public class DefaultDataUpdateService implements DataUpdateService { } else { log.info("Skipping audit logs migration"); } - boolean skipEdgeEventsMigration = getEnv("TB_SKIP_EDGE_EVENTS_MIGRATION", false); - if (!skipEdgeEventsMigration) { - log.info("Starting edge events migration. Can be skipped with TB_SKIP_EDGE_EVENTS_MIGRATION env variable set to true"); - edgeEventDao.migrateEdgeEvents(); - } else { - log.info("Skipping edge events migration"); - } + migrateEdgeEvents("Starting edge events migration. "); + break; + case "3.5.1": + log.info("Updating data from version 3.5.1 to 3.6.0 ..."); + migrateEdgeEvents("Starting edge events migration - adding seq_id column. "); + break; + case "3.6.0": + log.info("Updating data from version 3.6.0 to 3.6.1 ..."); + migrateDeviceConnectivity(); break; default: throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion); } } + private void migrateEdgeEvents(String logPrefix) { + boolean skipEdgeEventsMigration = getEnv("TB_SKIP_EDGE_EVENTS_MIGRATION", false); + if (!skipEdgeEventsMigration) { + log.info(logPrefix + "Can be skipped with TB_SKIP_EDGE_EVENTS_MIGRATION env variable set to true"); + edgeEventDao.migrateEdgeEvents(); + } else { + log.info("Skipping edge events migration"); + } + } + + private void migrateDeviceConnectivity() { + if (adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "connectivity") == null) { + AdminSettings connectivitySettings = new AdminSettings(); + connectivitySettings.setTenantId(TenantId.SYS_TENANT_ID); + connectivitySettings.setKey("connectivity"); + connectivitySettings.setJsonValue(JacksonUtil.valueToTree(connectivityConfiguration.getConnectivity())); + adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, connectivitySettings); + } + } + + @Override + public void upgradeRuleNodes() { + try { + int totalRuleNodesUpgraded = 0; + log.info("Starting rule nodes upgrade ..."); + var nodeClassToVersionMap = componentDiscoveryService.getVersionedNodes(); + log.debug("Found {} versioned nodes to check for upgrade!", nodeClassToVersionMap.size()); + for (var ruleNodeClassInfo : nodeClassToVersionMap) { + var ruleNodeTypeForLogs = ruleNodeClassInfo.getSimpleName(); + var toVersion = ruleNodeClassInfo.getCurrentVersion(); + log.debug("Going to check for nodes with type: {} to upgrade to version: {}.", ruleNodeTypeForLogs, toVersion); + var ruleNodesIdsToUpgrade = getRuleNodesIdsWithTypeAndVersionLessThan(ruleNodeClassInfo.getClassName(), toVersion); + if (ruleNodesIdsToUpgrade.isEmpty()) { + log.debug("There are no active nodes with type {}, or all nodes with this type already set to latest version!", ruleNodeTypeForLogs); + continue; + } + var ruleNodeIdsPartitions = Lists.partition(ruleNodesIdsToUpgrade, MAX_PENDING_SAVE_RULE_NODE_FUTURES); + for (var ruleNodePack : ruleNodeIdsPartitions) { + totalRuleNodesUpgraded += processRuleNodePack(ruleNodePack, ruleNodeClassInfo); + log.info("{} upgraded rule nodes so far ...", totalRuleNodesUpgraded); + } + } + log.info("Finished rule nodes upgrade. Upgraded rule nodes count: {}", totalRuleNodesUpgraded); + } catch (Exception e) { + log.error("Unexpected error during rule nodes upgrade: ", e); + } + } + + private int processRuleNodePack(List ruleNodeIdsBatch, RuleNodeClassInfo ruleNodeClassInfo) { + var saveFutures = new ArrayList>(MAX_PENDING_SAVE_RULE_NODE_FUTURES); + String ruleNodeType = ruleNodeClassInfo.getSimpleName(); + int toVersion = ruleNodeClassInfo.getCurrentVersion(); + var ruleNodesPack = ruleChainService.findAllRuleNodesByIds(ruleNodeIdsBatch); + for (var ruleNode : ruleNodesPack) { + if (ruleNode == null) { + continue; + } + var ruleNodeId = ruleNode.getId(); + int fromVersion = ruleNode.getConfigurationVersion(); + log.debug("Going to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}", + ruleNodeId, ruleNodeType, fromVersion, toVersion); + try { + TbNodeUpgradeUtils.upgradeConfigurationAndVersion(ruleNode, ruleNodeClassInfo); + saveFutures.add(jpaExecutorService.submit(() -> { + ruleChainService.saveRuleNode(TenantId.SYS_TENANT_ID, ruleNode); + log.debug("Successfully upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}", + ruleNodeId, ruleNodeType, fromVersion, toVersion); + })); + } catch (Exception e) { + log.warn("Failed to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {} due to: ", + ruleNodeId, ruleNodeType, fromVersion, toVersion, e); + } + } + try { + return Futures.allAsList(saveFutures).get().size(); + } catch (ExecutionException | InterruptedException e) { + throw new RuntimeException("Failed to process save rule nodes requests due to: ", e); + } + } + + private List getRuleNodesIdsWithTypeAndVersionLessThan(String type, int toVersion) { + var ruleNodeIds = new ArrayList(); + new PageDataIterable<>(pageLink -> + ruleChainService.findAllRuleNodeIdsByTypeAndVersionLessThan(type, toVersion, pageLink), DEFAULT_PAGE_SIZE + ).forEach(ruleNodeIds::add); + return ruleNodeIds; + } + private final PaginatedUpdater deviceProfileEntityDynamicConditionsUpdater = new PaginatedUpdater<>() { @@ -289,12 +401,11 @@ public class DefaultDataUpdateService implements DataUpdateService { private void updateNestedRuleChains() { try { - var packSize = 1024; var updated = 0; boolean hasNext = true; while (hasNext) { - List relations = relationService.findRuleNodeToRuleChainRelations(TenantId.SYS_TENANT_ID, RuleChainType.CORE, packSize); - hasNext = relations.size() == packSize; + List relations = relationService.findRuleNodeToRuleChainRelations(TenantId.SYS_TENANT_ID, RuleChainType.CORE, DEFAULT_PAGE_SIZE); + hasNext = relations.size() == DEFAULT_PAGE_SIZE; for (EntityRelation relation : relations) { try { RuleNodeId sourceNodeId = new RuleNodeId(relation.getFrom().getId()); @@ -441,8 +552,8 @@ public class DefaultDataUpdateService implements DataUpdateService { md.getNodes().add(ruleNode); md.setFirstNodeIndex(newIdx); - md.addConnectionInfo(newIdx, oldIdx, "Success"); - ruleChainService.saveRuleChainMetaData(tenant.getId(), md); + md.addConnectionInfo(newIdx, oldIdx, TbNodeConnectionType.SUCCESS); + ruleChainService.saveRuleChainMetaData(tenant.getId(), md, Function.identity()); } } catch (Exception e) { log.error("[{}] Unable to update Tenant: {}", tenant.getId(), tenant.getName(), e); diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java b/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java new file mode 100644 index 0000000000..8ff0124481 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java @@ -0,0 +1,170 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.install.update; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.HasImage; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageDataIterable; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.asset.AssetProfileDao; +import org.thingsboard.server.dao.dashboard.DashboardDao; +import org.thingsboard.server.dao.device.DeviceProfileDao; +import org.thingsboard.server.dao.resource.ImageService; +import org.thingsboard.server.dao.tenant.TenantDao; +import org.thingsboard.server.dao.widget.WidgetTypeDao; +import org.thingsboard.server.dao.widget.WidgetsBundleDao; + +import java.util.function.BiFunction; +import java.util.function.Function; + +@Component +@RequiredArgsConstructor +@Slf4j +public class ImagesUpdater { + private final ImageService imageService; + private final WidgetsBundleDao widgetsBundleDao; + private final WidgetTypeDao widgetTypeDao; + private final TenantDao tenantDao; + private final DashboardDao dashboardDao; + private final DeviceProfileDao deviceProfileDao; + private final AssetProfileDao assetProfileDao; + + public void updateWidgetsBundlesImages() { + log.info("Updating widgets bundles images..."); + var widgetsBundles = new PageDataIterable<>(widgetsBundleDao::findAllWidgetsBundles, 128); + updateImages(widgetsBundles, "bundle", imageService::replaceBase64WithImageUrl, widgetsBundleDao); + } + + public void updateWidgetTypesImages() { + log.info("Updating widget types images..."); + var widgetTypesIds = new PageDataIterable<>(widgetTypeDao::findAllWidgetTypesIds, 1024); + updateImages(widgetTypesIds, "widget type", imageService::replaceBase64WithImageUrl, widgetTypeDao); + } + + public void updateDashboardsImages() { + log.info("Updating dashboards images..."); + updateImages("dashboard", dashboardDao::findIdsByTenantId, imageService::replaceBase64WithImageUrl, dashboardDao); + } + + public void createSystemImages(Dashboard defaultDashboard) { + defaultDashboard.setTenantId(TenantId.SYS_TENANT_ID); + boolean created = imageService.replaceBase64WithImageUrl(defaultDashboard); + if (created) { + log.debug("Created system images for default dashboard '{}'", defaultDashboard.getTitle()); + } + } + + public void updateDeviceProfilesImages() { + log.info("Updating device profiles images..."); + var deviceProfiles = new PageDataIterable<>(deviceProfileDao::findAllWithImages, 256); + updateImages(deviceProfiles, "device profile", imageService::replaceBase64WithImageUrl, deviceProfileDao); + } + + public void updateAssetProfilesImages() { + log.info("Updating asset profiles images..."); + var assetProfiles = new PageDataIterable<>(assetProfileDao::findAllWithImages, 256); + updateImages(assetProfiles, "asset profile", imageService::replaceBase64WithImageUrl, assetProfileDao); + } + + private void updateImages(Iterable entities, String type, + BiFunction updater, Dao dao) { + int updatedCount = 0; + int totalCount = 0; + for (E entity : entities) { + totalCount++; + try { + boolean updated = updater.apply(entity, type); + if (updated) { + dao.save(entity.getTenantId(), entity); + log.debug("[{}][{}] Updated {} images", entity.getTenantId(), entity.getName(), type); + updatedCount++; + } + } catch (Exception e) { + log.error("[{}][{}] Failed to update {} images", entity.getTenantId(), entity.getName(), type, e); + } + if (totalCount % 100 == 0) { + log.info("Processed {} {}s so far", totalCount, type); + } + } + log.info("Updated {} {}s out of {}", updatedCount, type, totalCount); + } + + private void updateImages(Iterable entitiesIds, String type, + Function updater, Dao dao) { + int totalCount = 0; + int updatedCount = 0; + var counts = updateImages(entitiesIds, type, updater, dao, totalCount, updatedCount); + totalCount = counts[0]; + updatedCount = counts[1]; + log.info("Updated {} {}s out of {}", updatedCount, type, totalCount); + } + + private void updateImages(String type, BiFunction> entityIdsByTenantId, + Function updater, Dao dao) { + int tenantCount = 0; + int totalCount = 0; + int updatedCount = 0; + var tenantIds = new PageDataIterable<>(tenantDao::findTenantsIds, 128); + for (var tenantId : tenantIds) { + tenantCount++; + var entitiesIds = new PageDataIterable<>(link -> entityIdsByTenantId.apply(tenantId, link), 128); + var counts = updateImages(entitiesIds, type, updater, dao, totalCount, updatedCount); + totalCount = counts[0]; + updatedCount = counts[1]; + if (tenantCount % 100 == 0) { + log.info("Update {}s images: processed {} tenants so far", type, tenantCount); + } + } + log.info("Updated {} {}s out of {}", updatedCount, type, totalCount); + } + + private int[] updateImages(Iterable entitiesIds, String type, + Function updater, Dao dao, int totalCount, int updatedCount) { + for (EntityId id : entitiesIds) { + totalCount++; + E entity; + try { + entity = dao.findById(TenantId.SYS_TENANT_ID, id.getId()); + } catch (Exception e) { + log.error("Failed to update {} images: error fetching {} by id [{}]: {}", type, type, id.getId(), StringUtils.abbreviate(e.toString(), 1000)); + continue; + } + try { + boolean updated = updater.apply(entity); + if (updated) { + dao.save(entity.getTenantId(), entity); + log.debug("[{}][{}] Updated {} images", entity.getTenantId(), entity.getName(), type); + updatedCount++; + } + } catch (Exception e) { + log.error("[{}][{}] Failed to update {} images", entity.getTenantId(), entity.getName(), type, e); + } + if (totalCount % 100 == 0) { + log.info("Processed {} {}s so far", totalCount, type); + } + } + return new int[]{totalCount, updatedCount}; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java index 1c6af41360..ba1b8cca75 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java @@ -16,7 +16,6 @@ package org.thingsboard.server.service.mail; import com.fasterxml.jackson.databind.JsonNode; - import freemarker.template.Configuration; import freemarker.template.Template; import lombok.extern.slf4j.Slf4j; @@ -363,6 +362,8 @@ public class DefaultMailService implements MailService { return valueInM + " out of " + thresholdInM + " allowed messages"; case JS_EXEC_COUNT: return valueInM + " out of " + thresholdInM + " allowed JavaScript functions"; + case TBEL_EXEC_COUNT: + return valueInM + " out of " + thresholdInM + " allowed Tbel functions"; case RE_EXEC_COUNT: return valueInM + " out of " + thresholdInM + " allowed Rule Engine messages"; case EMAIL_EXEC_COUNT: @@ -383,6 +384,8 @@ public class DefaultMailService implements MailService { return recordState.getValueAsString() + " messages"; case JS_EXEC_COUNT: return "JavaScript functions " + recordState.getValueAsString() + " times"; + case TBEL_EXEC_COUNT: + return "TBEL functions " + recordState.getValueAsString() + " times"; case RE_EXEC_COUNT: return recordState.getValueAsString() + " Rule Engine messages"; case EMAIL_EXEC_COUNT: diff --git a/application/src/main/java/org/thingsboard/server/service/mail/TbMailSender.java b/application/src/main/java/org/thingsboard/server/service/mail/TbMailSender.java index 0a9b173247..3872fc4c7a 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/TbMailSender.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/TbMailSender.java @@ -23,18 +23,18 @@ import com.google.api.client.auth.oauth2.TokenResponse; import com.google.api.client.http.GenericUrl; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.gson.GsonFactory; -import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.lang.Nullable; +import org.springframework.mail.MailException; +import org.springframework.mail.MailSendException; import org.springframework.mail.javamail.JavaMailSenderImpl; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.StringUtils; -import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; -import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.mail.MailOauth2Provider; import org.thingsboard.server.dao.exception.IncorrectParameterException; +import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; import java.time.Duration; import java.time.Instant; @@ -50,6 +50,7 @@ public class TbMailSender extends JavaMailSenderImpl { private static final String MAIL_PROP = "mail."; private final TbMailContextComponent ctx; private final Lock lock; + private final Boolean oauth2Enabled; private volatile String accessToken; private volatile long tokenExpires; @@ -70,14 +71,39 @@ public class TbMailSender extends JavaMailSenderImpl { setJavaMailProperties(createJavaMailProperties(jsonConfig)); } - @SneakyThrows + public Boolean getOauth2Enabled() { + return oauth2Enabled; + } + + public long getTokenExpires() { + return tokenExpires; + } + + @Override + protected void doSend(MimeMessage[] mimeMessages, @Nullable Object[] originalMessages) throws MailException { + updateOauth2PasswordIfExpired(); + doSendSuper(mimeMessages, originalMessages); + } + + public void doSendSuper(MimeMessage[] mimeMessages, Object[] originalMessages) { + super.doSend(mimeMessages, originalMessages); + } + @Override - public void doSend(MimeMessage[] mimeMessages, @Nullable Object[] originalMessages) { - if (oauth2Enabled && (System.currentTimeMillis() > tokenExpires)){ + public void testConnection() throws MessagingException { + updateOauth2PasswordIfExpired(); + testConnectionSuper(); + } + + public void testConnectionSuper() throws MessagingException { + super.testConnection(); + } + + public void updateOauth2PasswordIfExpired() { + if (getOauth2Enabled() && (System.currentTimeMillis() > getTokenExpires())){ refreshAccessToken(); setPassword(accessToken); } - super.doSend(mimeMessages, originalMessages); } private Properties createJavaMailProperties(JsonNode jsonConfig) { @@ -125,10 +151,10 @@ public class TbMailSender extends JavaMailSenderImpl { return javaMailProperties; } - public void refreshAccessToken() throws ThingsboardException { + public void refreshAccessToken() { lock.lock(); try { - if (System.currentTimeMillis() > tokenExpires) { + if (System.currentTimeMillis() > getTokenExpires()) { AdminSettings settings = ctx.getAdminSettingsService().findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail"); JsonNode jsonValue = settings.getJsonValue(); @@ -151,8 +177,8 @@ public class TbMailSender extends JavaMailSenderImpl { tokenExpires = System.currentTimeMillis() + (tokenResponse.getExpiresInSeconds().intValue() * 1000); } } catch (Exception e) { - log.warn("Unable to retrieve access token: {}", e.getMessage()); - throw new ThingsboardException("Error while retrieving access token: " + e.getMessage(), ThingsboardErrorCode.GENERAL); + log.error("Unable to retrieve access token: {}", e.getMessage()); + throw new RuntimeException("Error while retrieving access token: " + e.getMessage()); } finally { lock.unlock(); } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java index 7215776170..ef4fd02e8b 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.notification; +import com.google.common.util.concurrent.FutureCallback; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -22,12 +23,14 @@ import org.springframework.stereotype.Service; import org.thingsboard.rule.engine.api.NotificationCenter; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.data.notification.AlreadySentException; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; @@ -38,9 +41,12 @@ import org.thingsboard.server.common.data.notification.NotificationRequestStatus import org.thingsboard.server.common.data.notification.NotificationStatus; import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; +import org.thingsboard.server.common.data.notification.settings.UserNotificationSettings; +import org.thingsboard.server.common.data.notification.targets.MicrosoftTeamsNotificationTargetConfig; import org.thingsboard.server.common.data.notification.targets.NotificationRecipient; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter; import org.thingsboard.server.common.data.notification.targets.slack.SlackNotificationTargetConfig; import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; @@ -56,11 +62,10 @@ import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.dao.notification.NotificationSettingsService; import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; -import org.thingsboard.server.dao.util.limits.LimitedApi; import org.thingsboard.server.dao.util.limits.RateLimitService; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.service.executors.NotificationExecutorService; import org.thingsboard.server.service.notification.channels.NotificationChannel; @@ -75,7 +80,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; -import java.util.function.Consumer; import java.util.stream.Collectors; @Service @@ -90,14 +94,14 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple private final NotificationTemplateService notificationTemplateService; private final NotificationSettingsService notificationSettingsService; private final NotificationExecutorService notificationExecutor; - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final TbQueueProducerProvider producerProvider; private final RateLimitService rateLimitService; private Map channels; @Override - public NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest request, Consumer callback) { + public NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest request, FutureCallback callback) { if (request.getRuleId() == null) { if (!rateLimitService.checkRateLimit(LimitedApi.NOTIFICATION_REQUESTS, tenantId)) { throw new TbRateLimitsException(EntityType.TENANT); @@ -163,30 +167,72 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple .settings(settings) .build(); + processNotificationRequestAsync(ctx, targets, callback); + return request; + } + + @Override + public void sendGeneralWebNotification(TenantId tenantId, UsersFilter recipients, NotificationTemplate template) { + NotificationTarget target = new NotificationTarget(); + target.setTenantId(tenantId); + PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig(); + targetConfig.setUsersFilter(recipients); + target.setConfiguration(targetConfig); + + NotificationRequest notificationRequest = NotificationRequest.builder() + .tenantId(tenantId) + .template(template) + .targets(List.of(EntityId.NULL_UUID)) // this is temporary and will be removed when 'create from scratch' functionality is implemented for recipients + .status(NotificationRequestStatus.PROCESSING) + .build(); + try { + notificationRequest = notificationRequestService.saveNotificationRequest(tenantId, notificationRequest); + NotificationProcessingContext ctx = NotificationProcessingContext.builder() + .tenantId(tenantId) + .request(notificationRequest) + .deliveryMethods(Set.of(NotificationDeliveryMethod.WEB)) + .template(template) + .build(); + + processNotificationRequestAsync(ctx, List.of(target), null); + } catch (Exception e) { + log.error("Failed to process notification request for recipients {} for template '{}'", recipients, template.getName(), e); + } + } + + private void processNotificationRequestAsync(NotificationProcessingContext ctx, List targets, FutureCallback callback) { notificationExecutor.submit(() -> { + NotificationRequestId requestId = ctx.getRequest().getId(); for (NotificationTarget target : targets) { - processForTarget(target, ctx); - } + try { + processForTarget(target, ctx); + } catch (Exception e) { + log.error("[{}] Failed to process notification request for target {}", requestId, target.getId(), e); + ctx.getStats().setError(e.getMessage()); + updateRequestStats(ctx, requestId, ctx.getStats()); - NotificationRequestId requestId = ctx.getRequest().getId(); - log.debug("[{}] Notification request processing is finished", requestId); - NotificationRequestStats stats = ctx.getStats(); - try { - notificationRequestService.updateNotificationRequest(tenantId, requestId, NotificationRequestStatus.SENT, stats); - } catch (Exception e) { - log.error("[{}] Failed to update stats for notification request", requestId, e); + if (callback != null) { + callback.onFailure(e); + } + return; + } } + log.debug("[{}] Notification request processing is finished", requestId); + NotificationRequestStats stats = ctx.getStats(); + updateRequestStats(ctx, requestId, stats); if (callback != null) { - try { - callback.accept(stats); - } catch (Exception e) { - log.error("Failed to process callback for notification request {}", requestId, e); - } + callback.onSuccess(stats); } }); + } - return request; + private void updateRequestStats(NotificationProcessingContext ctx, NotificationRequestId requestId, NotificationRequestStats stats) { + try { + notificationRequestService.updateNotificationRequest(ctx.getTenantId(), requestId, NotificationRequestStatus.SENT, stats); + } catch (Exception e) { + log.error("[{}] Failed to update stats for notification request", requestId, e); + } } private void processForTarget(NotificationTarget target, NotificationProcessingContext ctx) { @@ -194,7 +240,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple switch (target.getConfiguration().getType()) { case PLATFORM_USERS: { PlatformUsersNotificationTargetConfig targetConfig = (PlatformUsersNotificationTargetConfig) target.getConfiguration(); - if (targetConfig.getUsersFilter().getType().isForRules()) { + if (targetConfig.getUsersFilter().getType().isForRules() && ctx.getRequest().getInfo() instanceof RuleOriginatedNotificationInfo) { recipients = new PageDataIterable<>(pageLink -> { return notificationTargetService.findRecipientsForRuleNotificationTargetConfig(ctx.getTenantId(), targetConfig, (RuleOriginatedNotificationInfo) ctx.getRequest().getInfo(), pageLink); }, 500); @@ -210,6 +256,11 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple recipients = List.of(targetConfig.getConversation()); break; } + case MICROSOFT_TEAMS: { + MicrosoftTeamsNotificationTargetConfig targetConfig = (MicrosoftTeamsNotificationTargetConfig) target.getConfiguration(); + recipients = List.of(targetConfig); + break; + } default: { recipients = Collections.emptyList(); } @@ -237,7 +288,17 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple private void processForRecipient(NotificationDeliveryMethod deliveryMethod, NotificationRecipient recipient, NotificationProcessingContext ctx) throws Exception { if (ctx.getStats().contains(deliveryMethod, recipient.getId())) { throw new AlreadySentException(); + } else { + ctx.getStats().reportProcessed(deliveryMethod, recipient.getId()); } + + if (recipient instanceof User) { + UserNotificationSettings settings = notificationSettingsService.getUserNotificationSettings(ctx.getTenantId(), ((User) recipient).getId(), false); + if (!settings.isEnabled(ctx.getNotificationType(), deliveryMethod)) { + throw new RuntimeException("User disabled " + deliveryMethod.getName() + " notifications of this type"); + } + } + NotificationChannel notificationChannel = channels.get(deliveryMethod); DeliveryMethodNotificationTemplate processedTemplate = ctx.getProcessedTemplate(deliveryMethod, recipient); @@ -251,7 +312,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple Notification notification = Notification.builder() .requestId(request.getId()) .recipientId(recipient.getId()) - .type(ctx.getNotificationTemplate().getNotificationType()) + .type(ctx.getNotificationType()) .subject(processedTemplate.getSubject()) .text(processedTemplate.getBody()) .additionalConfig(processedTemplate.getAdditionalConfig()) @@ -376,7 +437,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple TransportProtos.ToCoreNotificationMsg notificationRequestUpdateProto = TbSubscriptionUtils.notificationRequestUpdateToProto(tenantId, update); Set coreServices = new HashSet<>(partitionService.getAllServiceIds(ServiceType.TB_CORE)); for (String serviceId : coreServices) { - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId); + TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId); producerProvider.getTbCoreNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), notificationRequestUpdateProto), null); } }); diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSchedulerService.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSchedulerService.java index 01591003ad..eeeb1f0a7a 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSchedulerService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSchedulerService.java @@ -21,6 +21,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; +import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.rule.engine.api.NotificationCenter; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.NotificationRequestId; @@ -35,12 +36,12 @@ import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.notification.NotificationRequestService; -import org.thingsboard.server.queue.scheduler.SchedulerComponent; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.executors.NotificationExecutorService; import org.thingsboard.server.service.partition.AbstractPartitionBasedService; import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -48,6 +49,8 @@ import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -55,16 +58,16 @@ import java.util.concurrent.TimeUnit; @Service @RequiredArgsConstructor @Slf4j -@SuppressWarnings("UnstableApiUsage") public class DefaultNotificationSchedulerService extends AbstractPartitionBasedService implements NotificationSchedulerService { private final NotificationCenter notificationCenter; private final NotificationRequestService notificationRequestService; - private final SchedulerComponent scheduler; private final NotificationExecutorService notificationExecutor; + private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("notification-scheduler")); private final Map scheduledNotificationRequests = new ConcurrentHashMap<>(); + @Override @PostConstruct public void init() { super.init(); @@ -166,6 +169,13 @@ public class DefaultNotificationSchedulerService extends AbstractPartitionBasedS return "notifications-scheduler"; } + @Override + @PreDestroy + public void stop() { + super.stop(); + scheduler.shutdownNow(); + } + @Data private static class ScheduledRequestMetadata { private final TenantId tenantId; diff --git a/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingContext.java b/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingContext.java index 27a9cabe43..e91a3e80fa 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingContext.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/NotificationProcessingContext.java @@ -18,19 +18,18 @@ package org.thingsboard.server.service.notification; import com.google.common.base.Strings; import lombok.Builder; import lombok.Getter; -import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.notification.NotificationRequestStats; +import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.settings.NotificationDeliveryMethodConfig; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; import org.thingsboard.server.common.data.notification.targets.NotificationRecipient; import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate; -import org.thingsboard.server.common.data.notification.template.HasSubject; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig; -import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.util.TemplateUtils; import java.util.EnumMap; @@ -38,8 +37,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; -import static org.apache.commons.lang3.StringUtils.isNotEmpty; - @SuppressWarnings("unchecked") public class NotificationProcessingContext { @@ -52,6 +49,8 @@ public class NotificationProcessingContext { private final Set deliveryMethods; @Getter private final NotificationTemplate notificationTemplate; + @Getter + private final NotificationType notificationType; private final Map templates; @Getter @@ -65,6 +64,7 @@ public class NotificationProcessingContext { this.deliveryMethods = deliveryMethods; this.settings = settings; this.notificationTemplate = template; + this.notificationType = template.getNotificationType(); this.templates = new EnumMap<>(NotificationDeliveryMethod.class); this.stats = new NotificationRequestStats(); init(); @@ -86,12 +86,11 @@ public class NotificationProcessingContext { public T getProcessedTemplate(NotificationDeliveryMethod deliveryMethod, NotificationRecipient recipient) { T template = (T) templates.get(deliveryMethod); - Map additionalTemplateContext = null; if (recipient != null) { - additionalTemplateContext = createTemplateContextForRecipient(recipient); - } - if (MapUtils.isNotEmpty(additionalTemplateContext) && template.containsAny(additionalTemplateContext.keySet().toArray(String[]::new))) { - template = processTemplate(template, additionalTemplateContext); + Map additionalTemplateContext = createTemplateContextForRecipient(recipient); + if (template.getTemplatableValues().stream().anyMatch(value -> value.containsParams(additionalTemplateContext.keySet()))) { + template = processTemplate(template, additionalTemplateContext); + } } return template; } @@ -107,22 +106,13 @@ public class NotificationProcessingContext { if (templateContext.isEmpty()) return template; template = (T) template.copy(); - template.setBody(TemplateUtils.processTemplate(template.getBody(), templateContext)); - if (template instanceof HasSubject) { - String subject = ((HasSubject) template).getSubject(); - ((HasSubject) template).setSubject(TemplateUtils.processTemplate(subject, templateContext)); - } - if (template instanceof WebDeliveryMethodNotificationTemplate) { - WebDeliveryMethodNotificationTemplate webNotificationTemplate = (WebDeliveryMethodNotificationTemplate) template; - String buttonText = webNotificationTemplate.getButtonText(); - if (isNotEmpty(buttonText)) { - webNotificationTemplate.setButtonText(TemplateUtils.processTemplate(buttonText, templateContext)); - } - String buttonLink = webNotificationTemplate.getButtonLink(); - if (isNotEmpty(buttonLink)) { - webNotificationTemplate.setButtonLink(TemplateUtils.processTemplate(buttonLink, templateContext)); + template.getTemplatableValues().forEach(templatableValue -> { + String value = templatableValue.get(); + if (StringUtils.isNotEmpty(value)) { + value = TemplateUtils.processTemplate(value, templateContext); + templatableValue.set(value); } - } + }); return template; } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/channels/MicrosoftTeamsNotificationChannel.java b/application/src/main/java/org/thingsboard/server/service/notification/channels/MicrosoftTeamsNotificationChannel.java new file mode 100644 index 0000000000..8720505267 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/notification/channels/MicrosoftTeamsNotificationChannel.java @@ -0,0 +1,194 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.notification.channels; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.base.Strings; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; +import org.thingsboard.server.common.data.notification.info.NotificationInfo; +import org.thingsboard.server.common.data.notification.targets.MicrosoftTeamsNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.template.MicrosoftTeamsDeliveryMethodNotificationTemplate; +import org.thingsboard.server.common.data.notification.template.MicrosoftTeamsDeliveryMethodNotificationTemplate.Button.LinkType; +import org.thingsboard.server.service.notification.NotificationProcessingContext; +import org.thingsboard.server.service.security.system.SystemSecurityService; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.Optional; + +@Component +@RequiredArgsConstructor +public class MicrosoftTeamsNotificationChannel implements NotificationChannel { + + private final SystemSecurityService systemSecurityService; + + @Setter + private RestTemplate restTemplate = new RestTemplateBuilder() + .setConnectTimeout(Duration.of(15, ChronoUnit.SECONDS)) + .setReadTimeout(Duration.of(15, ChronoUnit.SECONDS)) + .build(); + + @Override + public void sendNotification(MicrosoftTeamsNotificationTargetConfig targetConfig, MicrosoftTeamsDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) throws Exception { + Message message = new Message(); + message.setThemeColor(Strings.emptyToNull(processedTemplate.getThemeColor())); + if (StringUtils.isEmpty(processedTemplate.getSubject())) { + message.setText(processedTemplate.getBody()); + } else { + message.setSummary(processedTemplate.getSubject()); + Message.Section section = new Message.Section(); + section.setActivityTitle(processedTemplate.getSubject()); + section.setActivitySubtitle(processedTemplate.getBody()); + message.setSections(List.of(section)); + } + var button = processedTemplate.getButton(); + if (button != null && button.isEnabled()) { + String uri; + if (button.getLinkType() == LinkType.DASHBOARD) { + String state = null; + if (button.isSetEntityIdInState() || StringUtils.isNotEmpty(button.getDashboardState())) { + ObjectNode stateObject = JacksonUtil.newObjectNode(); + if (button.isSetEntityIdInState()) { + stateObject.putObject("params") + .set("entityId", Optional.ofNullable(ctx.getRequest().getInfo()) + .map(NotificationInfo::getStateEntityId) + .map(JacksonUtil::valueToTree) + .orElse(null)); + } else { + stateObject.putObject("params"); + } + if (StringUtils.isNotEmpty(button.getDashboardState())) { + stateObject.put("id", button.getDashboardState()); + } + state = Base64.encodeBase64String(JacksonUtil.OBJECT_MAPPER.writeValueAsBytes(List.of(stateObject))); + } + String baseUrl = systemSecurityService.getBaseUrl(ctx.getTenantId(), null, null); + if (StringUtils.isEmpty(baseUrl)) { + throw new IllegalStateException("Failed to determine base url to construct dashboard link"); + } + uri = baseUrl + "/dashboards/" + button.getDashboardId(); + if (state != null) { + uri += "?state=" + state; + } + } else { + uri = button.getLink(); + } + if (StringUtils.isNotBlank(uri) && button.getText() != null) { + Message.ActionCard actionCard = new Message.ActionCard(); + actionCard.setType("OpenUri"); + actionCard.setName(button.getText()); + var target = new Message.ActionCard.Target("default", uri); + actionCard.setTargets(List.of(target)); + message.setPotentialAction(List.of(actionCard)); + } + } + + restTemplate.postForEntity(targetConfig.getWebhookUrl(), message, String.class); + } + + @Override + public void check(TenantId tenantId) throws Exception { + } + + @Override + public NotificationDeliveryMethod getDeliveryMethod() { + return NotificationDeliveryMethod.MICROSOFT_TEAMS; + } + + @Data + public static class Message { + @JsonProperty("@type") + private final String type = "MessageCard"; + @JsonProperty("@context") + private final String context = "http://schema.org/extensions"; + private String themeColor; + private String summary; + private String text; + private List
sections; + private List potentialAction; + + @Data + public static class Section { + private String activityTitle; + private String activitySubtitle; + private String activityImage; + private List facts; + private boolean markdown; + + @Data + public static class Fact { + private final String name; + private final String value; + } + } + + @Data + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class ActionCard { + @JsonProperty("@type") + private String type; // ActionCard, OpenUri + private String name; + private List inputs; // for ActionCard + private List actions; // for ActionCard + private List targets; + + @Data + public static class Input { + @JsonProperty("@type") + private String type; // TextInput, DateInput, MultichoiceInput + private String id; + private boolean isMultiple; + private String title; + private boolean isMultiSelect; + + @Data + public static class Choice { + private final String display; + private final String value; + } + } + + @Data + public static class Action { + @JsonProperty("@type") + private final String type; // HttpPOST + private final String name; + private final String target; // url + } + + @Data + public static class Target { + private final String os; + private final String uri; + } + } + + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java index 71a59026a2..70ba344b84 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java @@ -16,98 +16,78 @@ package org.thingsboard.server.service.notification.rule; import lombok.RequiredArgsConstructor; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; import org.springframework.context.annotation.Lazy; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; import org.thingsboard.rule.engine.api.NotificationCenter; -import org.thingsboard.server.common.data.CacheConstants; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.notification.NotificationRequestConfig; import org.thingsboard.server.common.data.notification.NotificationRequestStatus; import org.thingsboard.server.common.data.notification.info.NotificationInfo; import org.thingsboard.server.common.data.notification.rule.NotificationRule; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; -import org.thingsboard.server.common.data.notification.settings.TriggerTypeConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; -import org.thingsboard.server.common.msg.notification.trigger.NotificationRuleTrigger; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.dao.notification.NotificationRequestService; -import org.thingsboard.server.dao.util.limits.LimitedApi; import org.thingsboard.server.dao.util.limits.RateLimitService; import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.queue.notification.NotificationDeduplicationService; import org.thingsboard.server.service.executors.NotificationExecutorService; import org.thingsboard.server.service.notification.rule.cache.NotificationRulesCache; import org.thingsboard.server.service.notification.rule.trigger.NotificationRuleTriggerProcessor; -import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.Collection; import java.util.EnumMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; @Service @RequiredArgsConstructor -@ConfigurationProperties(prefix = "notification-system.rules") @Slf4j @SuppressWarnings({"rawtypes", "unchecked"}) public class DefaultNotificationRuleProcessor implements NotificationRuleProcessor { private final NotificationRulesCache notificationRulesCache; private final NotificationRequestService notificationRequestService; + private final NotificationDeduplicationService deduplicationService; private final PartitionService partitionService; private final RateLimitService rateLimitService; @Autowired @Lazy private NotificationCenter notificationCenter; private final NotificationExecutorService notificationExecutor; - private final CacheManager cacheManager; - private Cache sentNotifications; - @Setter - private Map triggerTypesConfigs; private final Map triggerProcessors = new EnumMap<>(NotificationRuleTriggerType.class); - @PostConstruct - private void init() { - sentNotifications = cacheManager.getCache(CacheConstants.SENT_NOTIFICATIONS_CACHE); - if (sentNotifications == null) { - throw new IllegalStateException("Sent notifications cache is not set up"); - } - } - @Override public void process(NotificationRuleTrigger trigger) { NotificationRuleTriggerType triggerType = trigger.getType(); TenantId tenantId = triggerType.isTenantLevel() ? trigger.getTenantId() : TenantId.SYS_TENANT_ID; - - try { - List enabledRules = notificationRulesCache.getEnabled(tenantId, triggerType); - if (enabledRules.isEmpty()) { - return; - } - if (trigger.deduplicate()) { - enabledRules = new ArrayList<>(enabledRules); - enabledRules.removeIf(rule -> alreadySent(rule, trigger)); - } - final List rules = enabledRules; - notificationExecutor.submit(() -> { + notificationExecutor.submit(() -> { + try { + List enabledRules = notificationRulesCache.getEnabled(tenantId, triggerType); + if (enabledRules.isEmpty()) { + return; + } + if (trigger.deduplicate()) { + enabledRules = new ArrayList<>(enabledRules); + enabledRules.removeIf(rule -> deduplicationService.alreadyProcessed(trigger, rule)); + } + final List rules = enabledRules; for (NotificationRule rule : rules) { try { processNotificationRule(rule, trigger); @@ -115,10 +95,10 @@ public class DefaultNotificationRuleProcessor implements NotificationRuleProcess log.error("Failed to process notification rule {} for trigger type {} with trigger object {}", rule.getId(), rule.getTriggerType(), trigger, e); } } - }); - } catch (Throwable e) { - log.error("Failed to process notification rules for trigger: {}", trigger, e); - } + } catch (Throwable e) { + log.error("Failed to process notification rules for trigger: {}", trigger, e); + } + }); } private void processNotificationRule(NotificationRule rule, NotificationRuleTrigger trigger) { @@ -199,34 +179,6 @@ public class DefaultNotificationRuleProcessor implements NotificationRuleProcess return triggerProcessors.get(triggerConfig.getTriggerType()).constructNotificationInfo(trigger); } - private boolean alreadySent(NotificationRule rule, NotificationRuleTrigger trigger) { - String deduplicationKey = getDeduplicationKey(trigger, rule); - - boolean alreadySent = false; - Long lastSentTs = sentNotifications.get(deduplicationKey, Long.class); - if (lastSentTs != null) { - long deduplicationDuration = Optional.ofNullable(triggerTypesConfigs) - .map(triggerTypes -> triggerTypes.get(trigger.getType())) - .map(TriggerTypeConfig::getDeduplicationDuration) - .orElseGet(trigger::getDefaultDeduplicationDuration); - long passed = System.currentTimeMillis() - lastSentTs; - log.trace("Deduplicating trigger {} for rule '{}' by key '{}'. Deduplication duration: {} ms, passed: {} ms", - trigger.getType(), rule.getName(), deduplicationKey, deduplicationDuration, passed); - if (deduplicationDuration == 0 || passed <= deduplicationDuration) { - alreadySent = true; - } - } - if (!alreadySent) { - lastSentTs = System.currentTimeMillis(); - } - sentNotifications.put(deduplicationKey, lastSentTs); - return alreadySent; - } - - public static String getDeduplicationKey(NotificationRuleTrigger trigger, NotificationRule rule) { - return String.join("_", trigger.getDeduplicationKey(), rule.getDeduplicationKey()); - } - @EventListener(ComponentLifecycleMsg.class) public void onNotificationRuleDeleted(ComponentLifecycleMsg componentLifecycleMsg) { if (componentLifecycleMsg.getEvent() != ComponentLifecycleEvent.DELETED || diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/cache/DefaultNotificationRulesCache.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/cache/DefaultNotificationRulesCache.java index a43ed59efa..d8ac13364b 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/cache/DefaultNotificationRulesCache.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/cache/DefaultNotificationRulesCache.java @@ -24,7 +24,7 @@ import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.rule.NotificationRule; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.dao.notification.NotificationRuleService; diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/cache/NotificationRulesCache.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/cache/NotificationRulesCache.java index 2a1054b352..bcfa7d0120 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/cache/NotificationRulesCache.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/cache/NotificationRulesCache.java @@ -17,7 +17,7 @@ package org.thingsboard.server.service.notification.rule.cache; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.rule.NotificationRule; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; import java.util.List; diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java index eca258aecc..dba5fd8de4 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java @@ -22,10 +22,10 @@ import org.thingsboard.server.common.data.alarm.AlarmStatusFilter; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.notification.info.AlarmAssignmentNotificationInfo; import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; -import org.thingsboard.server.common.data.notification.rule.trigger.AlarmAssignmentNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.AlarmAssignmentNotificationRuleTriggerConfig.Action; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; -import org.thingsboard.server.common.msg.notification.trigger.AlarmAssignmentTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmAssignmentNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmAssignmentNotificationRuleTriggerConfig.Action; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.notification.rule.trigger.AlarmAssignmentTrigger; import static org.apache.commons.collections.CollectionUtils.isEmpty; import static org.thingsboard.server.common.data.util.CollectionsUtil.emptyOrContains; diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java index 70024d6e09..ef742922b3 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java @@ -24,9 +24,9 @@ import org.thingsboard.server.common.data.alarm.AlarmStatusFilter; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.notification.info.AlarmCommentNotificationInfo; import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; -import org.thingsboard.server.common.data.notification.rule.trigger.AlarmCommentNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; -import org.thingsboard.server.common.msg.notification.trigger.AlarmCommentTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmCommentNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.notification.rule.trigger.AlarmCommentTrigger; import org.thingsboard.server.dao.entity.EntityService; import static org.apache.commons.collections.CollectionUtils.isEmpty; diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java index c32ed26854..a942dce736 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java @@ -22,11 +22,11 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmStatusFilter; import org.thingsboard.server.common.data.notification.info.AlarmNotificationInfo; import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; -import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig.AlarmAction; -import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig.ClearRule; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; -import org.thingsboard.server.common.msg.notification.trigger.AlarmTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmNotificationRuleTriggerConfig.AlarmAction; +import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmNotificationRuleTriggerConfig.ClearRule; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.notification.rule.trigger.AlarmTrigger; import static org.apache.commons.collections.CollectionUtils.isNotEmpty; import static org.thingsboard.server.common.data.util.CollectionsUtil.emptyOrContains; @@ -108,6 +108,8 @@ public class AlarmTriggerProcessor implements NotificationRuleTriggerProcessor { diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RateLimitsTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RateLimitsTriggerProcessor.java new file mode 100644 index 0000000000..bec1502427 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RateLimitsTriggerProcessor.java @@ -0,0 +1,71 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.notification.rule.trigger; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.info.RateLimitsNotificationInfo; +import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.notification.rule.trigger.config.RateLimitsNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.util.CollectionsUtil; +import org.thingsboard.server.common.data.notification.rule.trigger.RateLimitsTrigger; +import org.thingsboard.server.dao.entity.EntityService; +import org.thingsboard.server.dao.tenant.TenantService; + +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class RateLimitsTriggerProcessor implements NotificationRuleTriggerProcessor { + + private final TenantService tenantService; + private final EntityService entityService; + + @Override + public boolean matchesFilter(RateLimitsTrigger trigger, RateLimitsNotificationRuleTriggerConfig triggerConfig) { + return trigger.getLimitLevel() != null && trigger.getApi().getLabel() != null && + CollectionsUtil.emptyOrContains(triggerConfig.getApis(), trigger.getApi()); + } + + @Override + public RuleOriginatedNotificationInfo constructNotificationInfo(RateLimitsTrigger trigger) { + EntityId limitLevel = trigger.getLimitLevel(); + String tenantName = tenantService.findTenantById(trigger.getTenantId()).getName(); + String limitLevelEntityName = null; + if (limitLevel instanceof TenantId) { + limitLevelEntityName = tenantName; + } else if (limitLevel != null) { + limitLevelEntityName = Optional.ofNullable(trigger.getLimitLevelEntityName()) + .orElseGet(() -> entityService.fetchEntityName(trigger.getTenantId(), limitLevel).orElse(null)); + } + return RateLimitsNotificationInfo.builder() + .tenantId(trigger.getTenantId()) + .tenantName(tenantName) + .api(trigger.getApi()) + .limitLevel(limitLevel) + .limitLevelEntityName(limitLevelEntityName) + .build(); + } + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.RATE_LIMITS; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineComponentLifecycleEventTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineComponentLifecycleEventTriggerProcessor.java index 6e37db83ef..8aac7ffd66 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineComponentLifecycleEventTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineComponentLifecycleEventTriggerProcessor.java @@ -23,10 +23,10 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.notification.info.RuleEngineComponentLifecycleEventNotificationInfo; import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; -import org.thingsboard.server.common.data.notification.rule.trigger.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.notification.rule.trigger.config.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; -import org.thingsboard.server.common.msg.notification.trigger.RuleEngineComponentLifecycleEventTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.RuleEngineComponentLifecycleEventTrigger; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.queue.discovery.PartitionService; diff --git a/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java index 6ab61426a8..fef96c0fcb 100644 --- a/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java @@ -20,7 +20,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; -import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceAttributesEventNotificationMsg; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; diff --git a/application/src/main/java/org/thingsboard/server/service/partition/TbCoreStartupService.java b/application/src/main/java/org/thingsboard/server/service/partition/TbCoreStartupService.java new file mode 100644 index 0000000000..3139ff256e --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/partition/TbCoreStartupService.java @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.partition; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.stereotype.Service; +import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.queue.discovery.QueueKey; +import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; +import org.thingsboard.server.queue.util.AfterStartUp; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Slf4j +@TbCoreComponent +@Service +@RequiredArgsConstructor +public class TbCoreStartupService { + + private final PartitionService partitionService; + private final TbServiceInfoProvider serviceInfoProvider; + private final TbClusterService clusterService; + + @AfterStartUp(order = AfterStartUp.STARTUP_SERVICE) + public void onApplicationEvent(ApplicationReadyEvent event) { + var myPartitions = partitionService.getMyPartitions(new QueueKey(ServiceType.TB_CORE)); + if (myPartitions != null && !myPartitions.isEmpty()) { + TransportProtos.ToCoreNotificationMsg toCoreMsg = TransportProtos.ToCoreNotificationMsg.newBuilder() + .setCoreStartupMsg(TransportProtos.CoreStartupMsg.newBuilder() + .setServiceId(serviceInfoProvider.getServiceId()).addAllPartitions(myPartitions).build()).build(); + clusterService.broadcastToCore(toCoreMsg); + } + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java index 76631aaa95..14930912d1 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java @@ -23,8 +23,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; -import org.thingsboard.rule.engine.api.msg.DeviceEdgeUpdateMsg; -import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.Device; @@ -32,10 +30,10 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.HasName; +import org.thingsboard.server.common.data.HasRuleEngineProfile; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; -import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.id.AssetId; @@ -57,6 +55,8 @@ import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; +import org.thingsboard.server.common.msg.rule.engine.DeviceEdgeUpdateMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceNameOrTypeUpdateMsg; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; @@ -68,7 +68,7 @@ import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.MultipleTbQueueCallbackWrapper; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.util.DataDecodingEncodingService; @@ -81,6 +81,8 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; +import static org.thingsboard.server.common.util.ProtoUtils.toProto; + @Service @Slf4j @RequiredArgsConstructor @@ -109,7 +111,7 @@ public class DefaultTbClusterService implements TbClusterService { @Lazy private OtaPackageStateService otaPackageStateService; - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final DataDecodingEncodingService encodingService; private final TbDeviceProfileCache deviceProfileCache; private final TbAssetProfileCache assetProfileCache; @@ -132,12 +134,23 @@ public class DefaultTbClusterService implements TbClusterService { public void pushMsgToCore(ToDeviceActorNotificationMsg msg, TbQueueCallback callback) { TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, msg.getTenantId(), msg.getDeviceId()); log.trace("PUSHING msg: {} to:{}", msg, tpi); - byte[] msgBytes = encodingService.encode(msg); - ToCoreMsg toCoreMsg = ToCoreMsg.newBuilder().setToDeviceActorNotificationMsg(ByteString.copyFrom(msgBytes)).build(); + ToCoreMsg toCoreMsg = ToCoreMsg.newBuilder().setToDeviceActorNotification(toProto(msg)).build(); producerProvider.getTbCoreMsgProducer().send(tpi, new TbProtoQueueMsg<>(msg.getDeviceId().getId(), toCoreMsg), callback); toCoreMsgs.incrementAndGet(); } + @Override + public void broadcastToCore(ToCoreNotificationMsg toCoreMsg) { + UUID msgId = UUID.randomUUID(); + TbQueueProducer> toCoreNfProducer = producerProvider.getTbCoreNotificationsMsgProducer(); + Set tbCoreServices = partitionService.getAllServiceIds(ServiceType.TB_CORE); + for (String serviceId : tbCoreServices) { + TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId); + toCoreNfProducer.send(tpi, new TbProtoQueueMsg<>(msgId, toCoreMsg), null); + toCoreNfs.incrementAndGet(); + } + } + @Override public void pushMsgToVersionControl(TenantId tenantId, TransportProtos.ToVersionControlServiceMsg msg, TbQueueCallback callback) { TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_VC_EXECUTOR, tenantId, tenantId); @@ -149,7 +162,7 @@ public class DefaultTbClusterService implements TbClusterService { @Override public void pushNotificationToCore(String serviceId, FromDeviceRpcResponse response, TbQueueCallback callback) { - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId); + TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId); log.trace("PUSHING msg: {} to:{}", response, tpi); FromDeviceRPCResponseProto.Builder builder = FromDeviceRPCResponseProto.newBuilder() .setRequestIdMSB(response.getId().getMostSignificantBits()) @@ -178,15 +191,8 @@ public class DefaultTbClusterService implements TbClusterService { return; } } else { - if (entityId.getEntityType().equals(EntityType.DEVICE)) { - tbMsg = transformMsg(tbMsg, deviceProfileCache.get(tenantId, new DeviceId(entityId.getId()))); - } else if (entityId.getEntityType().equals(EntityType.DEVICE_PROFILE)) { - tbMsg = transformMsg(tbMsg, deviceProfileCache.get(tenantId, new DeviceProfileId(entityId.getId()))); - } else if (entityId.getEntityType().equals(EntityType.ASSET)) { - tbMsg = transformMsg(tbMsg, assetProfileCache.get(tenantId, new AssetId(entityId.getId()))); - } else if (entityId.getEntityType().equals(EntityType.ASSET_PROFILE)) { - tbMsg = transformMsg(tbMsg, assetProfileCache.get(tenantId, new AssetProfileId(entityId.getId()))); - } + HasRuleEngineProfile ruleEngineProfile = getRuleEngineProfileForEntityOrElseNull(tenantId, entityId); + tbMsg = transformMsg(tbMsg, ruleEngineProfile); } TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tbMsg.getQueueName(), tenantId, entityId); log.trace("PUSHING msg: {} to:{}", tbMsg, tpi); @@ -198,41 +204,41 @@ public class DefaultTbClusterService implements TbClusterService { toRuleEngineMsgs.incrementAndGet(); } - private TbMsg transformMsg(TbMsg tbMsg, DeviceProfile deviceProfile) { - if (deviceProfile != null) { - RuleChainId targetRuleChainId = deviceProfile.getDefaultRuleChainId(); - String targetQueueName = deviceProfile.getDefaultQueueName(); - tbMsg = transformMsg(tbMsg, targetRuleChainId, targetQueueName); + private HasRuleEngineProfile getRuleEngineProfileForEntityOrElseNull(TenantId tenantId, EntityId entityId) { + if (entityId.getEntityType().equals(EntityType.DEVICE)) { + return deviceProfileCache.get(tenantId, new DeviceId(entityId.getId())); + } else if (entityId.getEntityType().equals(EntityType.DEVICE_PROFILE)) { + return deviceProfileCache.get(tenantId, new DeviceProfileId(entityId.getId())); + } else if (entityId.getEntityType().equals(EntityType.ASSET)) { + return assetProfileCache.get(tenantId, new AssetId(entityId.getId())); + } else if (entityId.getEntityType().equals(EntityType.ASSET_PROFILE)) { + return assetProfileCache.get(tenantId, new AssetProfileId(entityId.getId())); } - return tbMsg; + return null; } - private TbMsg transformMsg(TbMsg tbMsg, AssetProfile assetProfile) { - if (assetProfile != null) { - RuleChainId targetRuleChainId = assetProfile.getDefaultRuleChainId(); - String targetQueueName = assetProfile.getDefaultQueueName(); - tbMsg = transformMsg(tbMsg, targetRuleChainId, targetQueueName); - } - return tbMsg; - } + private TbMsg transformMsg(TbMsg tbMsg, HasRuleEngineProfile ruleEngineProfile) { + if (ruleEngineProfile != null) { + RuleChainId targetRuleChainId = ruleEngineProfile.getDefaultRuleChainId(); + String targetQueueName = ruleEngineProfile.getDefaultQueueName(); - private TbMsg transformMsg(TbMsg tbMsg, RuleChainId targetRuleChainId, String targetQueueName) { - boolean isRuleChainTransform = targetRuleChainId != null && !targetRuleChainId.equals(tbMsg.getRuleChainId()); - boolean isQueueTransform = targetQueueName != null && !targetQueueName.equals(tbMsg.getQueueName()); + boolean isRuleChainTransform = targetRuleChainId != null && !targetRuleChainId.equals(tbMsg.getRuleChainId()); + boolean isQueueTransform = targetQueueName != null && !targetQueueName.equals(tbMsg.getQueueName()); - if (isRuleChainTransform && isQueueTransform) { - tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId, targetQueueName); - } else if (isRuleChainTransform) { - tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId); - } else if (isQueueTransform) { - tbMsg = TbMsg.transformMsg(tbMsg, targetQueueName); + if (isRuleChainTransform && isQueueTransform) { + tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId, targetQueueName); + } else if (isRuleChainTransform) { + tbMsg = TbMsg.transformMsgRuleChainId(tbMsg, targetRuleChainId); + } else if (isQueueTransform) { + tbMsg = TbMsg.transformMsgQueueName(tbMsg, targetQueueName); + } } return tbMsg; } @Override public void pushNotificationToRuleEngine(String serviceId, FromDeviceRpcResponse response, TbQueueCallback callback) { - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceId); + TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceId); log.trace("PUSHING msg: {} to:{}", response, tpi); FromDeviceRPCResponseProto.Builder builder = FromDeviceRPCResponseProto.newBuilder() .setRequestIdMSB(response.getId().getMostSignificantBits()) @@ -253,7 +259,7 @@ public class DefaultTbClusterService implements TbClusterService { } return; } - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, serviceId); + TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, serviceId); log.trace("PUSHING msg: {} to:{}", response, tpi); producerProvider.getTransportNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), response), callback); toTransportNfs.incrementAndGet(); @@ -302,10 +308,17 @@ public class DefaultTbClusterService implements TbClusterService { } @Override - public void onDeviceDeleted(Device device, TbQueueCallback callback) { - broadcastEntityDeleteToTransport(device.getTenantId(), device.getId(), device.getName(), callback); - sendDeviceStateServiceEvent(device.getTenantId(), device.getId(), false, false, true); - broadcastEntityStateChangeEvent(device.getTenantId(), device.getId(), ComponentLifecycleEvent.DELETED); + public void onDeviceDeleted(TenantId tenantId, Device device, TbQueueCallback callback) { + DeviceId deviceId = device.getId(); + broadcastEntityDeleteToTransport(tenantId, deviceId, device.getName(), callback); + sendDeviceStateServiceEvent(tenantId, deviceId, false, false, true); + broadcastEntityStateChangeEvent(tenantId, deviceId, ComponentLifecycleEvent.DELETED); + } + + @Override + public void onDeviceAssignedToTenant(TenantId oldTenantId, Device device) { + onDeviceDeleted(oldTenantId, device, null); + sendDeviceStateServiceEvent(device.getTenantId(), device.getId(), true, false, false); } @Override @@ -361,7 +374,7 @@ public class DefaultTbClusterService implements TbClusterService { Set tbTransportServices = partitionService.getAllServiceIds(ServiceType.TB_TRANSPORT); TbQueueCallback proxyCallback = callback != null ? new MultipleTbQueueCallbackWrapper(tbTransportServices.size(), callback) : null; for (String transportServiceId : tbTransportServices) { - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transportServiceId); + TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transportServiceId); toTransportNfProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), transportMsg), proxyCallback); toTransportNfs.incrementAndGet(); } @@ -371,24 +384,21 @@ public class DefaultTbClusterService implements TbClusterService { public void onEdgeEventUpdate(TenantId tenantId, EdgeId edgeId) { log.trace("[{}] Processing edge {} event update ", tenantId, edgeId); EdgeEventUpdateMsg msg = new EdgeEventUpdateMsg(tenantId, edgeId); - byte[] msgBytes = encodingService.encode(msg); - ToCoreNotificationMsg toCoreMsg = ToCoreNotificationMsg.newBuilder().setEdgeEventUpdateMsg(ByteString.copyFrom(msgBytes)).build(); + ToCoreNotificationMsg toCoreMsg = ToCoreNotificationMsg.newBuilder().setEdgeEventUpdate(toProto(msg)).build(); pushEdgeSyncMsgToCore(edgeId, toCoreMsg); } @Override public void pushEdgeSyncRequestToCore(ToEdgeSyncRequest toEdgeSyncRequest) { log.trace("[{}] Processing edge sync request {} ", toEdgeSyncRequest.getTenantId(), toEdgeSyncRequest); - byte[] msgBytes = encodingService.encode(toEdgeSyncRequest); - ToCoreNotificationMsg toCoreMsg = ToCoreNotificationMsg.newBuilder().setToEdgeSyncRequestMsg(ByteString.copyFrom(msgBytes)).build(); + ToCoreNotificationMsg toCoreMsg = ToCoreNotificationMsg.newBuilder().setToEdgeSyncRequest(toProto(toEdgeSyncRequest)).build(); pushEdgeSyncMsgToCore(toEdgeSyncRequest.getEdgeId(), toCoreMsg); } @Override public void pushEdgeSyncResponseToCore(FromEdgeSyncResponse fromEdgeSyncResponse) { log.trace("[{}] Processing edge sync response {}", fromEdgeSyncResponse.getTenantId(), fromEdgeSyncResponse); - byte[] msgBytes = encodingService.encode(fromEdgeSyncResponse); - ToCoreNotificationMsg toCoreMsg = ToCoreNotificationMsg.newBuilder().setFromEdgeSyncResponseMsg(ByteString.copyFrom(msgBytes)).build(); + ToCoreNotificationMsg toCoreMsg = ToCoreNotificationMsg.newBuilder().setFromEdgeSyncResponse(toProto(fromEdgeSyncResponse)).build(); pushEdgeSyncMsgToCore(fromEdgeSyncResponse.getEdgeId(), toCoreMsg); } @@ -396,14 +406,14 @@ public class DefaultTbClusterService implements TbClusterService { TbQueueProducer> toCoreNfProducer = producerProvider.getTbCoreNotificationsMsgProducer(); Set tbCoreServices = partitionService.getAllServiceIds(ServiceType.TB_CORE); for (String serviceId : tbCoreServices) { - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId); + TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId); toCoreNfProducer.send(tpi, new TbProtoQueueMsg<>(edgeId.getId(), toCoreMsg), null); toCoreNfs.incrementAndGet(); } } private void broadcast(ComponentLifecycleMsg msg) { - byte[] msgBytes = encodingService.encode(msg); + TransportProtos.ComponentLifecycleMsgProto componentLifecycleMsgProto = toProto(msg); TbQueueProducer> toRuleEngineProducer = producerProvider.getRuleEngineNotificationsMsgProducer(); Set tbRuleEngineServices = partitionService.getAllServiceIds(ServiceType.TB_RULE_ENGINE); EntityType entityType = msg.getEntityId().getEntityType(); @@ -420,8 +430,8 @@ public class DefaultTbClusterService implements TbClusterService { TbQueueProducer> toCoreNfProducer = producerProvider.getTbCoreNotificationsMsgProducer(); Set tbCoreServices = partitionService.getAllServiceIds(ServiceType.TB_CORE); for (String serviceId : tbCoreServices) { - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId); - ToCoreNotificationMsg toCoreMsg = ToCoreNotificationMsg.newBuilder().setComponentLifecycleMsg(ByteString.copyFrom(msgBytes)).build(); + TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId); + ToCoreNotificationMsg toCoreMsg = ToCoreNotificationMsg.newBuilder().setComponentLifecycle(componentLifecycleMsgProto).build(); toCoreNfProducer.send(tpi, new TbProtoQueueMsg<>(msg.getEntityId().getId(), toCoreMsg), null); toCoreNfs.incrementAndGet(); } @@ -429,8 +439,8 @@ public class DefaultTbClusterService implements TbClusterService { tbRuleEngineServices.removeAll(tbCoreServices); } for (String serviceId : tbRuleEngineServices) { - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceId); - ToRuleEngineNotificationMsg toRuleEngineMsg = ToRuleEngineNotificationMsg.newBuilder().setComponentLifecycleMsg(ByteString.copyFrom(msgBytes)).build(); + TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceId); + ToRuleEngineNotificationMsg toRuleEngineMsg = ToRuleEngineNotificationMsg.newBuilder().setComponentLifecycle(componentLifecycleMsgProto).build(); toRuleEngineProducer.send(tpi, new TbProtoQueueMsg<>(msg.getEntityId().getId(), toRuleEngineMsg), null); toRuleEngineNfs.incrementAndGet(); } @@ -466,11 +476,6 @@ public class DefaultTbClusterService implements TbClusterService { @Override public void onDeviceUpdated(Device device, Device old) { - onDeviceUpdated(device, old, true); - } - - @Override - public void onDeviceUpdated(Device device, Device old, boolean notifyEdge) { var created = old == null; broadcastEntityChangeToTransport(device.getTenantId(), device.getId(), device, null); if (old != null) { @@ -485,13 +490,10 @@ public class DefaultTbClusterService implements TbClusterService { broadcastEntityStateChangeEvent(device.getTenantId(), device.getId(), created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); sendDeviceStateServiceEvent(device.getTenantId(), device.getId(), created, !created, false); otaPackageStateService.update(device, old); - if (!created && notifyEdge) { - sendNotificationMsgToEdge(device.getTenantId(), null, device.getId(), null, null, EdgeEventActionType.UPDATED); - } } @Override - public void sendNotificationMsgToEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, EdgeEventType type, EdgeEventActionType action) { + public void sendNotificationMsgToEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, EdgeEventType type, EdgeEventActionType action, EdgeId originatorEdgeId) { if (!edgesEnabled) { return; } @@ -524,6 +526,10 @@ public class DefaultTbClusterService implements TbClusterService { if (body != null) { builder.setBody(body); } + if (originatorEdgeId != null) { + builder.setOriginatorEdgeIdMSB(originatorEdgeId.getId().getMostSignificantBits()); + builder.setOriginatorEdgeIdLSB(originatorEdgeId.getId().getLeastSignificantBits()); + } TransportProtos.EdgeNotificationMsgProto msg = builder.build(); log.trace("[{}] sending notification to edge service {}", tenantId.getId(), msg); pushMsgToCore(tenantId, entityId != null ? entityId : tenantId, TransportProtos.ToCoreMsg.newBuilder().setEdgeNotificationMsg(msg).build(), null); @@ -592,17 +598,17 @@ public class DefaultTbClusterService implements TbClusterService { tbCoreServices.removeAll(tbRuleEngineServices); for (String ruleEngineServiceId : tbRuleEngineServices) { - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, ruleEngineServiceId); + TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, ruleEngineServiceId); producerProvider.getRuleEngineNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), ruleEngineMsg), null); toRuleEngineNfs.incrementAndGet(); } for (String coreServiceId : tbCoreServices) { - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, coreServiceId); + TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_CORE, coreServiceId); producerProvider.getTbCoreNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), coreMsg), null); toCoreNfs.incrementAndGet(); } for (String transportServiceId : tbTransportServices) { - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transportServiceId); + TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transportServiceId); producerProvider.getTransportNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), transportMsg), null); toTransportNfs.incrementAndGet(); } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java index 182fce1c48..3df8876b60 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java @@ -24,34 +24,44 @@ import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; +import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.common.data.alarm.AlarmInfo; +import org.thingsboard.server.common.data.event.ErrorEvent; +import org.thingsboard.server.common.data.event.Event; +import org.thingsboard.server.common.data.event.LifecycleEvent; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.NotificationRequestId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTrigger; import org.thingsboard.server.common.data.rpc.RpcError; import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.TbActorMsg; -import org.thingsboard.server.common.msg.notification.trigger.NotificationRuleTrigger; +import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequestActorMsg; import org.thingsboard.server.common.stats.StatsFactory; +import org.thingsboard.server.common.util.ProtoUtils; +import org.thingsboard.server.dao.resource.ImageCacheKey; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.DeviceStateServiceMsgProto; import org.thingsboard.server.gen.transport.TransportProtos.EdgeNotificationMsgProto; +import org.thingsboard.server.gen.transport.TransportProtos.ErrorEventProto; import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto; +import org.thingsboard.server.gen.transport.TransportProtos.LifecycleEventProto; import org.thingsboard.server.gen.transport.TransportProtos.LocalSubscriptionServiceMsgProto; import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionMgrMsgProto; import org.thingsboard.server.gen.transport.TransportProtos.TbAlarmDeleteProto; import org.thingsboard.server.gen.transport.TransportProtos.TbAlarmUpdateProto; import org.thingsboard.server.gen.transport.TransportProtos.TbAttributeDeleteProto; import org.thingsboard.server.gen.transport.TransportProtos.TbAttributeUpdateProto; -import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionCloseProto; +import org.thingsboard.server.gen.transport.TransportProtos.TbEntitySubEventProto; import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesDeleteProto; import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesUpdateProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; @@ -63,7 +73,6 @@ import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; -import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.queue.provider.TbCoreQueueFactory; import org.thingsboard.server.queue.util.AfterStartUp; import org.thingsboard.server.queue.util.DataDecodingEncodingService; @@ -76,8 +85,8 @@ import org.thingsboard.server.service.profile.TbAssetProfileCache; import org.thingsboard.server.service.profile.TbDeviceProfileCache; import org.thingsboard.server.service.queue.processing.AbstractConsumerService; import org.thingsboard.server.service.queue.processing.IdMsgPair; +import org.thingsboard.server.service.resource.TbImageService; import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; -import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; import org.thingsboard.server.service.security.auth.jwt.settings.JwtSettingsService; import org.thingsboard.server.service.state.DeviceStateService; import org.thingsboard.server.service.subscription.SubscriptionManagerService; @@ -87,7 +96,6 @@ import org.thingsboard.server.service.sync.vc.GitVersionControlQueueService; import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationUpdate; -import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @@ -135,9 +143,10 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService> usageStatsConsumer; private final TbQueueConsumer> firmwareStatesConsumer; + private final TbImageService imageService; + protected volatile ExecutorService consumersExecutor; protected volatile ExecutorService usageStatsExecutor; - private volatile ExecutorService firmwareStatesExecutor; public DefaultTbCoreConsumerService(TbCoreQueueFactory tbCoreQueueFactory, @@ -160,7 +169,8 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService jwtSettingsService, NotificationSchedulerService notificationSchedulerService, - NotificationRuleProcessor notificationRuleProcessor) { + NotificationRuleProcessor notificationRuleProcessor, + TbImageService imageService) { super(actorContext, encodingService, tenantProfileCache, deviceProfileCache, assetProfileCache, apiUsageStateService, partitionService, eventPublisher, tbCoreQueueFactory.createToCoreNotificationsMsgConsumer(), jwtSettingsService); this.mainConsumer = tbCoreQueueFactory.createToCoreMsgConsumer(); this.usageStatsConsumer = tbCoreQueueFactory.createToUsageStatsServiceMsgConsumer(); @@ -176,11 +186,13 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService actorMsg = encodingService.decode(toCoreMsg.getToDeviceActorNotificationMsg().toByteArray()); if (actorMsg.isPresent()) { TbActorMsg tbActorMsg = actorMsg.get(); @@ -274,6 +301,10 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService Do NOTHING. + callback.onSuccess(); } else { throwNotHandled(msg, callback); } } + private void forwardCoreStartupMsg(TransportProtos.CoreStartupMsg coreStartupMsg, TbCallback callback) { + log.info("[{}] Processing core startup with partitions: {}", coreStartupMsg.getServiceId(), coreStartupMsg.getPartitionsList()); + localSubscriptionService.onCoreStartupMsg(coreStartupMsg); + callback.onSuccess(); + } + + private void forwardToResourceService(TransportProtos.ResourceCacheInvalidateMsg msg, TbCallback callback) { + var tenantId = new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB())); + msg.getKeysList().stream().map(cacheKeyProto -> { + if (cacheKeyProto.hasResourceKey()) { + return ImageCacheKey.forImage(tenantId, cacheKeyProto.getResourceKey()); + } else { + return ImageCacheKey.forPublicImage(cacheKeyProto.getPublicResourceKey()); + } + }).forEach(imageService::evictETags); + callback.onSuccess(); + } + private void forwardToSubMgrService(SubscriptionMgrMsgProto msg, TbCallback callback) { - if (msg.hasAttributeSub()) { - subscriptionManagerService.addSubscription(TbSubscriptionUtils.fromProto(msg.getAttributeSub()), callback); + if (msg.hasSubEvent()) { + TbEntitySubEventProto subEvent = msg.getSubEvent(); + subscriptionManagerService.onSubEvent(subEvent.getServiceId(), TbSubscriptionUtils.fromProto(subEvent), callback); } else if (msg.hasTelemetrySub()) { - subscriptionManagerService.addSubscription(TbSubscriptionUtils.fromProto(msg.getTelemetrySub()), callback); + callback.onSuccess(); + // Deprecated, for removal; Left intentionally to avoid throwNotHandled } else if (msg.hasAlarmSub()) { - subscriptionManagerService.addSubscription(TbSubscriptionUtils.fromProto(msg.getAlarmSub()), callback); + callback.onSuccess(); + // Deprecated, for removal; Left intentionally to avoid throwNotHandled } else if (msg.hasNotificationsSub()) { - subscriptionManagerService.addSubscription(TbSubscriptionUtils.fromProto(msg.getNotificationsSub()), callback); + callback.onSuccess(); + // Deprecated, for removal; Left intentionally to avoid throwNotHandled } else if (msg.hasNotificationsCountSub()) { - subscriptionManagerService.addSubscription(TbSubscriptionUtils.fromProto(msg.getNotificationsCountSub()), callback); + callback.onSuccess(); + // Deprecated, for removal; Left intentionally to avoid throwNotHandled } else if (msg.hasSubClose()) { - TbSubscriptionCloseProto closeProto = msg.getSubClose(); - subscriptionManagerService.cancelSubscription(closeProto.getSessionId(), closeProto.getSubscriptionId(), callback); + callback.onSuccess(); + // Deprecated, for removal; Left intentionally to avoid throwNotHandled } else if (msg.hasTsUpdate()) { TbTimeSeriesUpdateProto proto = msg.getTsUpdate(); + long tenantIdMSB = proto.getTenantIdMSB(); + long tenantIdLSB = proto.getTenantIdLSB(); subscriptionManagerService.onTimeSeriesUpdate( - TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), + toTenantId(tenantIdMSB, tenantIdLSB), TbSubscriptionUtils.toEntityId(proto.getEntityType(), proto.getEntityIdMSB(), proto.getEntityIdLSB()), TbSubscriptionUtils.toTsKvEntityList(proto.getDataList()), callback); } else if (msg.hasAttrUpdate()) { TbAttributeUpdateProto proto = msg.getAttrUpdate(); subscriptionManagerService.onAttributesUpdate( - TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), + toTenantId(proto.getTenantIdMSB(), proto.getTenantIdLSB()), TbSubscriptionUtils.toEntityId(proto.getEntityType(), proto.getEntityIdMSB(), proto.getEntityIdLSB()), proto.getScope(), TbSubscriptionUtils.toAttributeKvList(proto.getDataList()), callback); } else if (msg.hasAttrDelete()) { TbAttributeDeleteProto proto = msg.getAttrDelete(); subscriptionManagerService.onAttributesDelete( - TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), + toTenantId(proto.getTenantIdMSB(), proto.getTenantIdLSB()), TbSubscriptionUtils.toEntityId(proto.getEntityType(), proto.getEntityIdMSB(), proto.getEntityIdLSB()), proto.getScope(), proto.getKeysList(), proto.getNotifyDevice(), callback); } else if (msg.hasTsDelete()) { TbTimeSeriesDeleteProto proto = msg.getTsDelete(); subscriptionManagerService.onTimeSeriesDelete( - TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), + toTenantId(proto.getTenantIdMSB(), proto.getTenantIdLSB()), TbSubscriptionUtils.toEntityId(proto.getEntityType(), proto.getEntityIdMSB(), proto.getEntityIdLSB()), proto.getKeysList(), callback); } else if (msg.hasAlarmUpdate()) { TbAlarmUpdateProto proto = msg.getAlarmUpdate(); subscriptionManagerService.onAlarmUpdate( - TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), + toTenantId(proto.getTenantIdMSB(), proto.getTenantIdLSB()), TbSubscriptionUtils.toEntityId(proto.getEntityType(), proto.getEntityIdMSB(), proto.getEntityIdLSB()), JacksonUtil.fromString(proto.getAlarm(), AlarmInfo.class), callback); } else if (msg.hasAlarmDelete()) { TbAlarmDeleteProto proto = msg.getAlarmDelete(); subscriptionManagerService.onAlarmDeleted( - TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), + toTenantId(proto.getTenantIdMSB(), proto.getTenantIdLSB()), TbSubscriptionUtils.toEntityId(proto.getEntityType(), proto.getEntityIdMSB(), proto.getEntityIdLSB()), JacksonUtil.fromString(proto.getAlarm(), AlarmInfo.class), callback); } else if (msg.hasNotificationUpdate()) { TransportProtos.NotificationUpdateProto updateProto = msg.getNotificationUpdate(); - TenantId tenantId = TenantId.fromUUID(new UUID(updateProto.getTenantIdMSB(), updateProto.getTenantIdLSB())); + TenantId tenantId = toTenantId(updateProto.getTenantIdMSB(), updateProto.getTenantIdLSB()); UserId recipientId = new UserId(new UUID(updateProto.getRecipientIdMSB(), updateProto.getRecipientIdLSB())); NotificationUpdate update = JacksonUtil.fromString(updateProto.getUpdate(), NotificationUpdate.class); subscriptionManagerService.onNotificationUpdate(tenantId, recipientId, update, callback); } else if (msg.hasNotificationRequestUpdate()) { TransportProtos.NotificationRequestUpdateProto updateProto = msg.getNotificationRequestUpdate(); - TenantId tenantId = TenantId.fromUUID(new UUID(updateProto.getTenantIdMSB(), updateProto.getTenantIdLSB())); + TenantId tenantId = toTenantId(updateProto.getTenantIdMSB(), updateProto.getTenantIdLSB()); NotificationRequestUpdate update = JacksonUtil.fromString(updateProto.getUpdate(), NotificationRequestUpdate.class); - subscriptionManagerService.onNotificationRequestUpdate(tenantId, update, callback); + localSubscriptionService.onNotificationRequestUpdate(tenantId, update, callback); } else { throwNotHandled(msg, callback); } @@ -584,7 +658,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService actorMsg, TbCallback callback) { if (actorMsg.isPresent()) { - log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg.get()); - actorContext.tell(actorMsg.get()); + forwardToAppActor(id, actorMsg.get()); } callback.onSuccess(); } + private void forwardToAppActor(UUID id, TbActorMsg actorMsg) { + log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg); + actorContext.tell(actorMsg); + } + + private void forwardToEventService(ErrorEventProto eventProto, TbCallback callback) { + Event event = ErrorEvent.builder() + .tenantId(toTenantId(eventProto.getTenantIdMSB(), eventProto.getTenantIdLSB())) + .entityId(new UUID(eventProto.getEntityIdMSB(), eventProto.getEntityIdLSB())) + .serviceId(eventProto.getServiceId()) + .ts(System.currentTimeMillis()) + .method(eventProto.getMethod()) + .error(eventProto.getError()) + .build(); + forwardToEventService(event, callback); + } + + private void forwardToEventService(LifecycleEventProto eventProto, TbCallback callback) { + Event event = LifecycleEvent.builder() + .tenantId(toTenantId(eventProto.getTenantIdMSB(), eventProto.getTenantIdLSB())) + .entityId(new UUID(eventProto.getEntityIdMSB(), eventProto.getEntityIdLSB())) + .serviceId(eventProto.getServiceId()) + .ts(System.currentTimeMillis()) + .lcEventType(eventProto.getLcEventType()) + .success(eventProto.getSuccess()) + .error(StringUtils.isNotEmpty(eventProto.getError()) ? eventProto.getError() : null) + .build(); + forwardToEventService(event, callback); + } + + private void forwardToEventService(Event event, TbCallback callback) { + DonAsynchron.withCallback(actorContext.getEventService().saveAsync(event), + result -> callback.onSuccess(), + callback::onFailure, + actorContext.getDbCallbackExecutor()); + } + private void throwNotHandled(Object msg, TbCallback callback) { log.warn("Message not handled: {}", msg); callback.onFailure(new RuntimeException("Message not handled!")); } + private TenantId toTenantId(long tenantIdMSB, long tenantIdLSB) { + return TenantId.fromUUID(new UUID(tenantIdMSB, tenantIdLSB)); + } + @Override - protected void stopMainConsumers() { + protected void stopConsumers() { if (mainConsumer != null) { mainConsumer.unsubscribe(); } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java index f8f6a7d25f..273d7afc97 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java @@ -15,358 +15,120 @@ */ package org.thingsboard.server.service.queue; -import com.google.protobuf.ProtocolStringList; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; -import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.rpc.RpcError; -import org.thingsboard.server.common.msg.TbMsg; -import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; -import org.thingsboard.server.common.msg.queue.RuleEngineException; -import org.thingsboard.server.common.msg.queue.RuleNodeInfo; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TbCallback; -import org.thingsboard.server.common.msg.queue.TbMsgCallback; -import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; -import org.thingsboard.server.common.stats.StatsFactory; +import org.thingsboard.server.common.util.ProtoUtils; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; -import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.QueueKey; -import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; import org.thingsboard.server.queue.provider.TbRuleEngineQueueFactory; +import org.thingsboard.server.queue.util.AfterStartUp; import org.thingsboard.server.queue.util.DataDecodingEncodingService; import org.thingsboard.server.queue.util.TbRuleEngineComponent; import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.profile.TbAssetProfileCache; import org.thingsboard.server.service.profile.TbDeviceProfileCache; import org.thingsboard.server.service.queue.processing.AbstractConsumerService; -import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingDecision; -import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingResult; -import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingStrategy; -import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingStrategyFactory; -import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrategy; -import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrategyFactory; +import org.thingsboard.server.service.queue.ruleengine.TbRuleEngineConsumerContext; +import org.thingsboard.server.service.queue.ruleengine.TbRuleEngineQueueConsumerManager; import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService; -import org.thingsboard.server.service.stats.RuleEngineStatisticsService; import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReentrantLock; @Service @TbRuleEngineComponent @Slf4j public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService implements TbRuleEngineConsumerService { - public static final String SUCCESSFUL_STATUS = "successful"; - public static final String FAILED_STATUS = "failed"; - public static final String THREAD_TOPIC_SEPARATOR = " | "; - @Value("${queue.rule-engine.poll-interval}") - private long pollDuration; - @Value("${queue.rule-engine.pack-processing-timeout}") - private long packProcessingTimeout; - @Value("${queue.rule-engine.stats.enabled:true}") - private boolean statsEnabled; - @Value("${queue.rule-engine.prometheus-stats.enabled:false}") - boolean prometheusStatsEnabled; - - private final StatsFactory statsFactory; - private final TbRuleEngineSubmitStrategyFactory submitStrategyFactory; - private final TbRuleEngineProcessingStrategyFactory processingStrategyFactory; - private final TbRuleEngineQueueFactory tbRuleEngineQueueFactory; - private final RuleEngineStatisticsService statisticsService; - private final TbRuleEngineDeviceRpcService tbDeviceRpcService; - private final TbServiceInfoProvider serviceInfoProvider; + private final TbRuleEngineConsumerContext ctx; private final QueueService queueService; - // private final TenantId tenantId; - private final ConcurrentMap>> consumers = new ConcurrentHashMap<>(); - private final ConcurrentMap consumerConfigurations = new ConcurrentHashMap<>(); - private final ConcurrentMap consumerStats = new ConcurrentHashMap<>(); - private final ConcurrentMap topicsConsumerPerPartition = new ConcurrentHashMap<>(); - final ExecutorService submitExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("tb-rule-engine-consumer-submit")); - final ScheduledExecutorService repartitionExecutor = Executors.newScheduledThreadPool(1, ThingsBoardThreadFactory.forName("tb-rule-engine-consumer-repartition")); + private final TbRuleEngineDeviceRpcService tbDeviceRpcService; - public DefaultTbRuleEngineConsumerService(TbRuleEngineProcessingStrategyFactory processingStrategyFactory, - TbRuleEngineSubmitStrategyFactory submitStrategyFactory, + private final ConcurrentMap consumers = new ConcurrentHashMap<>(); + + public DefaultTbRuleEngineConsumerService(TbRuleEngineConsumerContext ctx, TbRuleEngineQueueFactory tbRuleEngineQueueFactory, - RuleEngineStatisticsService statisticsService, ActorSystemContext actorContext, DataDecodingEncodingService encodingService, TbRuleEngineDeviceRpcService tbDeviceRpcService, - StatsFactory statsFactory, + QueueService queueService, TbDeviceProfileCache deviceProfileCache, TbAssetProfileCache assetProfileCache, TbTenantProfileCache tenantProfileCache, TbApiUsageStateService apiUsageStateService, - PartitionService partitionService, ApplicationEventPublisher eventPublisher, - TbServiceInfoProvider serviceInfoProvider, QueueService queueService) { - super(actorContext, encodingService, tenantProfileCache, deviceProfileCache, assetProfileCache, apiUsageStateService, partitionService, eventPublisher, tbRuleEngineQueueFactory.createToRuleEngineNotificationsMsgConsumer(), Optional.empty()); - this.statisticsService = statisticsService; - this.tbRuleEngineQueueFactory = tbRuleEngineQueueFactory; - this.submitStrategyFactory = submitStrategyFactory; - this.processingStrategyFactory = processingStrategyFactory; + PartitionService partitionService, ApplicationEventPublisher eventPublisher) { + super(actorContext, encodingService, tenantProfileCache, deviceProfileCache, assetProfileCache, apiUsageStateService, partitionService, + eventPublisher, tbRuleEngineQueueFactory.createToRuleEngineNotificationsMsgConsumer(), Optional.empty()); + this.ctx = ctx; this.tbDeviceRpcService = tbDeviceRpcService; - this.statsFactory = statsFactory; - this.serviceInfoProvider = serviceInfoProvider; this.queueService = queueService; } @PostConstruct public void init() { - super.init("tb-rule-engine-consumer", "tb-rule-engine-notifications-consumer"); + super.init("tb-rule-engine-notifications-consumer"); List queues = queueService.findAllQueues(); for (Queue configuration : queues) { - initConsumer(configuration); + if (partitionService.isManagedByCurrentService(configuration.getTenantId())) { + initConsumer(configuration); + } } } private void initConsumer(Queue configuration) { - QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, configuration); - consumerConfigurations.putIfAbsent(queueKey, configuration); - consumerStats.putIfAbsent(queueKey, new TbRuleEngineConsumerStats(configuration.getName(), statsFactory)); - if (!configuration.isConsumerPerPartition()) { - consumers.computeIfAbsent(queueKey, queueName -> tbRuleEngineQueueFactory.createToRuleEngineMsgConsumer(configuration)); - } else { - topicsConsumerPerPartition.computeIfAbsent(queueKey, k -> new TbTopicWithConsumerPerPartition(k.getQueueName())); - } - } - - @PreDestroy - public void stop() { - super.destroy(); - submitExecutor.shutdownNow(); - repartitionExecutor.shutdownNow(); + getOrCreateConsumer(new QueueKey(ServiceType.TB_RULE_ENGINE, configuration)).init(configuration); } @Override protected void onTbApplicationEvent(PartitionChangeEvent event) { if (event.getServiceType().equals(getServiceType())) { - String serviceQueue = event.getQueueKey().getQueueName(); - log.info("[{}] Subscribing to partitions: {}", serviceQueue, event.getPartitions()); - if (!consumerConfigurations.get(event.getQueueKey()).isConsumerPerPartition()) { - consumers.get(event.getQueueKey()).subscribe(event.getPartitions()); - } else { - log.info("[{}] Subscribing consumer per partition: {}", serviceQueue, event.getPartitions()); - subscribeConsumerPerPartition(event.getQueueKey(), event.getPartitions()); - } - } - } - - void subscribeConsumerPerPartition(QueueKey queue, Set partitions) { - topicsConsumerPerPartition.get(queue).getSubscribeQueue().add(partitions); - scheduleTopicRepartition(queue); - } - - private void scheduleTopicRepartition(QueueKey queue) { - repartitionExecutor.schedule(() -> repartitionTopicWithConsumerPerPartition(queue), 1, TimeUnit.SECONDS); - } - - void repartitionTopicWithConsumerPerPartition(final QueueKey queueKey) { - if (stopped) { - return; - } - TbTopicWithConsumerPerPartition tbTopicWithConsumerPerPartition = topicsConsumerPerPartition.get(queueKey); - java.util.Queue> subscribeQueue = tbTopicWithConsumerPerPartition.getSubscribeQueue(); - if (subscribeQueue.isEmpty()) { - return; - } - if (tbTopicWithConsumerPerPartition.getLock().tryLock()) { - try { - Set partitions = null; - while (!subscribeQueue.isEmpty()) { - partitions = subscribeQueue.poll(); - } - if (partitions == null) { - return; + event.getPartitionsMap().forEach((queueKey, partitions) -> { + var consumer = consumers.get(queueKey); + if (consumer != null) { + consumer.update(partitions); + } else { + log.warn("Received invalid partition change event for {} that is not managed by this service", queueKey); } - - Set addedPartitions = new HashSet<>(partitions); - ConcurrentMap>> consumers = tbTopicWithConsumerPerPartition.getConsumers(); - addedPartitions.removeAll(consumers.keySet()); - log.info("calculated addedPartitions {}", addedPartitions); - - Set removedPartitions = new HashSet<>(consumers.keySet()); - removedPartitions.removeAll(partitions); - log.info("calculated removedPartitions {}", removedPartitions); - - removedPartitions.forEach((tpi) -> { - removeConsumerForTopicByTpi(queueKey.getQueueName(), consumers, tpi); - }); - - addedPartitions.forEach((tpi) -> { - log.info("[{}] Adding consumer for topic: {}", queueKey, tpi); - Queue configuration = consumerConfigurations.get(queueKey); - TbQueueConsumer> consumer = tbRuleEngineQueueFactory.createToRuleEngineMsgConsumer(configuration); - consumers.put(tpi, consumer); - launchConsumer(consumer, consumerConfigurations.get(queueKey), consumerStats.get(queueKey), "" + queueKey + "-" + tpi.getPartition().orElse(-999999)); - consumer.subscribe(Collections.singleton(tpi)); - }); - - } finally { - tbTopicWithConsumerPerPartition.getLock().unlock(); - } - } else { - scheduleTopicRepartition(queueKey); //reschedule later + }); } - } - void removeConsumerForTopicByTpi(String queue, ConcurrentMap>> consumers, TopicPartitionInfo tpi) { - log.info("[{}] Removing consumer for topic: {}", queue, tpi); - consumers.get(tpi).unsubscribe(); - consumers.remove(tpi); + @AfterStartUp(order = AfterStartUp.REGULAR_SERVICE) + public void onApplicationEvent(ApplicationReadyEvent event) { + super.onApplicationEvent(event); + ctx.setReady(true); } @Override - protected void launchMainConsumers() { - consumers.forEach((queue, consumer) -> launchConsumer(consumer, consumerConfigurations.get(queue), consumerStats.get(queue), queue.getQueueName())); - } + protected void launchMainConsumers() {} @Override - protected void stopMainConsumers() { - consumers.values().forEach(TbQueueConsumer::unsubscribe); - topicsConsumerPerPartition.values().forEach(tbTopicWithConsumerPerPartition -> tbTopicWithConsumerPerPartition.getConsumers().keySet() - .forEach((tpi) -> removeConsumerForTopicByTpi(tbTopicWithConsumerPerPartition.getTopic(), tbTopicWithConsumerPerPartition.getConsumers(), tpi))); - } - - void launchConsumer(TbQueueConsumer> consumer, Queue configuration, TbRuleEngineConsumerStats stats, String threadSuffix) { - consumersExecutor.execute(() -> consumerLoop(consumer, configuration, stats, threadSuffix)); - } - - void consumerLoop(TbQueueConsumer> consumer, org.thingsboard.server.common.data.queue.Queue configuration, TbRuleEngineConsumerStats stats, String threadSuffix) { - updateCurrentThreadName(threadSuffix); - while (!stopped && !consumer.isStopped()) { - try { - List> msgs = consumer.poll(pollDuration); - if (msgs.isEmpty()) { - continue; - } - final TbRuleEngineSubmitStrategy submitStrategy = getSubmitStrategy(configuration); - final TbRuleEngineProcessingStrategy ackStrategy = getAckStrategy(configuration); - submitStrategy.init(msgs); - while (!stopped && !consumer.isStopped()) { - TbMsgPackProcessingContext ctx = new TbMsgPackProcessingContext(configuration.getName(), submitStrategy, ackStrategy.isSkipTimeoutMsgs()); - submitStrategy.submitAttempt((id, msg) -> submitExecutor.submit(() -> submitMessage(configuration, stats, ctx, id, msg))); - - final boolean timeout = !ctx.await(configuration.getPackProcessingTimeout(), TimeUnit.MILLISECONDS); - - TbRuleEngineProcessingResult result = new TbRuleEngineProcessingResult(configuration.getName(), timeout, ctx); - if (timeout) { - printFirstOrAll(configuration, ctx, ctx.getPendingMap(), "Timeout"); - } - if (!ctx.getFailedMap().isEmpty()) { - printFirstOrAll(configuration, ctx, ctx.getFailedMap(), "Failed"); - } - ctx.printProfilerStats(); - - TbRuleEngineProcessingDecision decision = ackStrategy.analyze(result); - if (statsEnabled) { - stats.log(result, decision.isCommit()); - } - - ctx.cleanup(); - - if (decision.isCommit()) { - submitStrategy.stop(); - break; - } else { - submitStrategy.update(decision.getReprocessMap()); - } - } - consumer.commit(); - } catch (Exception e) { - if (!stopped) { - log.warn("Failed to process messages from queue.", e); - try { - Thread.sleep(pollDuration); - } catch (InterruptedException e2) { - log.trace("Failed to wait until the server has capacity to handle new requests", e2); - } - } - } - } - log.info("TB Rule Engine Consumer stopped."); - } - - void updateCurrentThreadName(String threadSuffix) { - String name = Thread.currentThread().getName(); - int spliteratorIndex = name.indexOf(THREAD_TOPIC_SEPARATOR); - if (spliteratorIndex > 0) { - name = name.substring(0, spliteratorIndex); - } - name = name + THREAD_TOPIC_SEPARATOR + threadSuffix; - Thread.currentThread().setName(name); - } - - TbRuleEngineProcessingStrategy getAckStrategy(Queue configuration) { - return processingStrategyFactory.newInstance(configuration.getName(), configuration.getProcessingStrategy()); - } - - TbRuleEngineSubmitStrategy getSubmitStrategy(Queue configuration) { - return submitStrategyFactory.newInstance(configuration.getName(), configuration.getSubmitStrategy()); - } - - void submitMessage(Queue configuration, TbRuleEngineConsumerStats stats, TbMsgPackProcessingContext ctx, UUID id, TbProtoQueueMsg msg) { - log.trace("[{}] Creating callback for topic {} message: {}", id, configuration.getName(), msg.getValue()); - ToRuleEngineMsg toRuleEngineMsg = msg.getValue(); - TenantId tenantId = TenantId.fromUUID(new UUID(toRuleEngineMsg.getTenantIdMSB(), toRuleEngineMsg.getTenantIdLSB())); - TbMsgCallback callback = prometheusStatsEnabled ? - new TbMsgPackCallback(id, tenantId, ctx, stats.getTimer(tenantId, SUCCESSFUL_STATUS), stats.getTimer(tenantId, FAILED_STATUS)) : - new TbMsgPackCallback(id, tenantId, ctx); - try { - if (toRuleEngineMsg.getTbMsg() != null && !toRuleEngineMsg.getTbMsg().isEmpty()) { - forwardToRuleEngineActor(configuration.getName(), tenantId, toRuleEngineMsg, callback); - } else { - callback.onSuccess(); - } - } catch (Exception e) { - callback.onFailure(new RuleEngineException(e.getMessage())); - } - } - - private void printFirstOrAll(Queue configuration, TbMsgPackProcessingContext ctx, Map> map, String prefix) { - boolean printAll = log.isTraceEnabled(); - log.info("{} to process [{}] messages", prefix, map.size()); - for (Map.Entry> pending : map.entrySet()) { - ToRuleEngineMsg tmp = pending.getValue().getValue(); - TbMsg tmpMsg = TbMsg.fromBytes(configuration.getName(), tmp.getTbMsg().toByteArray(), TbMsgCallback.EMPTY); - RuleNodeInfo ruleNodeInfo = ctx.getLastVisitedRuleNode(pending.getKey()); - if (printAll) { - log.trace("[{}] {} to process message: {}, Last Rule Node: {}", TenantId.fromUUID(new UUID(tmp.getTenantIdMSB(), tmp.getTenantIdLSB())), prefix, tmpMsg, ruleNodeInfo); - } else { - log.info("[{}] {} to process message: {}, Last Rule Node: {}", TenantId.fromUUID(new UUID(tmp.getTenantIdMSB(), tmp.getTenantIdLSB())), prefix, tmpMsg, ruleNodeInfo); - break; - } - } + protected void stopConsumers() { + consumers.values().forEach(TbRuleEngineQueueConsumerManager::stop); + consumers.values().forEach(TbRuleEngineQueueConsumerManager::awaitStop); + ctx.stop(); } @Override @@ -376,18 +138,22 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< @Override protected long getNotificationPollDuration() { - return pollDuration; + return ctx.getPollDuration(); } @Override protected long getNotificationPackProcessingTimeout() { - return packProcessingTimeout; + return ctx.getPackProcessingTimeout(); } @Override protected void handleNotification(UUID id, TbProtoQueueMsg msg, TbCallback callback) throws Exception { ToRuleEngineNotificationMsg nfMsg = msg.getValue(); - if (nfMsg.getComponentLifecycleMsg() != null && !nfMsg.getComponentLifecycleMsg().isEmpty()) { + if (nfMsg.hasComponentLifecycle()) { + handleComponentLifecycleMsg(id, ProtoUtils.fromProto(nfMsg.getComponentLifecycle())); + callback.onSuccess(); + } else if (!nfMsg.getComponentLifecycleMsg().isEmpty()) { + //will be removed in 3.6.1 in favour of hasComponentLifecycle() handleComponentLifecycleMsg(id, nfMsg.getComponentLifecycleMsg()); callback.onSuccess(); } else if (nfMsg.hasFromDeviceRpcResponse()) { @@ -398,10 +164,10 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< tbDeviceRpcService.processRpcResponseFromDevice(response); callback.onSuccess(); } else if (nfMsg.hasQueueUpdateMsg()) { - repartitionExecutor.execute(() -> updateQueue(nfMsg.getQueueUpdateMsg())); + ctx.getScheduler().execute(() -> updateQueue(nfMsg.getQueueUpdateMsg())); callback.onSuccess(); } else if (nfMsg.hasQueueDeleteMsg()) { - repartitionExecutor.execute(() -> deleteQueue(nfMsg.getQueueDeleteMsg())); + ctx.getScheduler().execute(() -> deleteQueue(nfMsg.getQueueDeleteMsg())); callback.onSuccess(); } else { log.trace("Received notification with missing handler"); @@ -411,86 +177,49 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< private void updateQueue(TransportProtos.QueueUpdateMsg queueUpdateMsg) { log.info("Received queue update msg: [{}]", queueUpdateMsg); - String queueName = queueUpdateMsg.getQueueName(); TenantId tenantId = new TenantId(new UUID(queueUpdateMsg.getTenantIdMSB(), queueUpdateMsg.getTenantIdLSB())); - QueueId queueId = new QueueId(new UUID(queueUpdateMsg.getQueueIdMSB(), queueUpdateMsg.getQueueIdLSB())); - QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueUpdateMsg.getQueueName(), tenantId); - Queue queue = queueService.findQueueById(tenantId, queueId); - Queue oldQueue = consumerConfigurations.remove(queueKey); - if (oldQueue != null) { - if (oldQueue.isConsumerPerPartition()) { - TbTopicWithConsumerPerPartition consumerPerPartition = topicsConsumerPerPartition.remove(queueKey); - ReentrantLock lock = consumerPerPartition.getLock(); - try { - lock.lock(); - consumerPerPartition.getConsumers().values().forEach(TbQueueConsumer::unsubscribe); - } finally { - lock.unlock(); - } - } else { - TbQueueConsumer> consumer = consumers.remove(queueKey); - consumer.unsubscribe(); + if (partitionService.isManagedByCurrentService(tenantId)) { + QueueId queueId = new QueueId(new UUID(queueUpdateMsg.getQueueIdMSB(), queueUpdateMsg.getQueueIdLSB())); + String queueName = queueUpdateMsg.getQueueName(); + QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueName, tenantId); + Queue queue = queueService.findQueueById(tenantId, queueId); + + TbRuleEngineQueueConsumerManager consumerManager = getOrCreateConsumer(queueKey); + Queue oldQueue = consumerManager.getQueue(); + consumerManager.update(queue); + + if (oldQueue != null && queue.getPartitions() == oldQueue.getPartitions()) { + return; } } - initConsumer(queue); - - if (!queue.isConsumerPerPartition()) { - launchConsumer(consumers.get(queueKey), consumerConfigurations.get(queueKey), consumerStats.get(queueKey), queueName); - } - partitionService.updateQueue(queueUpdateMsg); - partitionService.recalculatePartitions(serviceInfoProvider.getServiceInfo(), new ArrayList<>(partitionService.getOtherServices(ServiceType.TB_RULE_ENGINE))); + partitionService.recalculatePartitions(ctx.getServiceInfoProvider().getServiceInfo(), + new ArrayList<>(partitionService.getOtherServices(ServiceType.TB_RULE_ENGINE))); } private void deleteQueue(TransportProtos.QueueDeleteMsg queueDeleteMsg) { log.info("Received queue delete msg: [{}]", queueDeleteMsg); TenantId tenantId = new TenantId(new UUID(queueDeleteMsg.getTenantIdMSB(), queueDeleteMsg.getTenantIdLSB())); QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueDeleteMsg.getQueueName(), tenantId); - - Queue queue = consumerConfigurations.remove(queueKey); - if (queue != null) { - if (queue.isConsumerPerPartition()) { - TbTopicWithConsumerPerPartition tbTopicWithConsumerPerPartition = topicsConsumerPerPartition.remove(queueKey); - if (tbTopicWithConsumerPerPartition != null) { - tbTopicWithConsumerPerPartition.getConsumers().values().forEach(TbQueueConsumer::unsubscribe); - tbTopicWithConsumerPerPartition.getConsumers().clear(); - } - } else { - TbQueueConsumer> consumer = consumers.remove(queueKey); - if (consumer != null) { - consumer.unsubscribe(); - } - } + var consumerManager = consumers.remove(queueKey); + if (consumerManager != null) { + consumerManager.delete(); } + partitionService.removeQueue(queueDeleteMsg); + partitionService.recalculatePartitions(ctx.getServiceInfoProvider().getServiceInfo(), new ArrayList<>(partitionService.getOtherServices(ServiceType.TB_RULE_ENGINE))); } - private void forwardToRuleEngineActor(String queueName, TenantId tenantId, ToRuleEngineMsg toRuleEngineMsg, TbMsgCallback callback) { - TbMsg tbMsg = TbMsg.fromBytes(queueName, toRuleEngineMsg.getTbMsg().toByteArray(), callback); - QueueToRuleEngineMsg msg; - ProtocolStringList relationTypesList = toRuleEngineMsg.getRelationTypesList(); - Set relationTypes = null; - if (relationTypesList != null) { - if (relationTypesList.size() == 1) { - relationTypes = Collections.singleton(relationTypesList.get(0)); - } else { - relationTypes = new HashSet<>(relationTypesList); - } - } - msg = new QueueToRuleEngineMsg(tenantId, tbMsg, relationTypes, toRuleEngineMsg.getFailureMessage()); - actorContext.tell(msg); + private TbRuleEngineQueueConsumerManager getOrCreateConsumer(QueueKey queueKey) { + return consumers.computeIfAbsent(queueKey, key -> new TbRuleEngineQueueConsumerManager(ctx, key)); } @Scheduled(fixedDelayString = "${queue.rule-engine.stats.print-interval-ms}") public void printStats() { - if (statsEnabled) { + if (ctx.isStatsEnabled()) { long ts = System.currentTimeMillis(); - consumerStats.forEach((queue, stats) -> { - stats.printStats(); - statisticsService.reportQueueStats(ts, stats); - stats.reset(); - }); + consumers.values().forEach(manager -> manager.printStats(ts)); } } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTenantRoutingInfoService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTenantRoutingInfoService.java index 400586235e..ea90364ef7 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTenantRoutingInfoService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTenantRoutingInfoService.java @@ -22,7 +22,6 @@ import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.exception.TenantNotFoundException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; -import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.queue.discovery.TenantRoutingInfo; import org.thingsboard.server.queue.discovery.TenantRoutingInfoService; @@ -31,12 +30,9 @@ import org.thingsboard.server.queue.discovery.TenantRoutingInfoService; @ConditionalOnExpression("'${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core' || '${service.type:null}'=='tb-rule-engine'") public class DefaultTenantRoutingInfoService implements TenantRoutingInfoService { - private final TenantService tenantService; - private final TbTenantProfileCache tenantProfileCache; - public DefaultTenantRoutingInfoService(TenantService tenantService, TbTenantProfileCache tenantProfileCache) { - this.tenantService = tenantService; + public DefaultTenantRoutingInfoService(TbTenantProfileCache tenantProfileCache) { this.tenantProfileCache = tenantProfileCache; } @@ -44,7 +40,7 @@ public class DefaultTenantRoutingInfoService implements TenantRoutingInfoService public TenantRoutingInfo getRoutingInfo(TenantId tenantId) { TenantProfile tenantProfile = tenantProfileCache.get(tenantId); if (tenantProfile != null) { - return new TenantRoutingInfo(tenantId, tenantProfile.isIsolatedTbRuleEngine()); + return new TenantRoutingInfo(tenantId, tenantProfile.getId(), tenantProfile.isIsolatedTbRuleEngine()); } else { throw new TenantNotFoundException(tenantId); } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerStats.java b/application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerStats.java index 6f614957c4..5ff72e97e3 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerStats.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerStats.java @@ -36,10 +36,22 @@ public class TbCoreConsumerStats { public static final String DEVICE_CLAIMS = "claimDevice"; public static final String DEVICE_STATES = "deviceState"; public static final String SUBSCRIPTION_MSGS = "subMsgs"; - public static final String TO_CORE_NOTIFICATIONS = "coreNfs"; public static final String EDGE_NOTIFICATIONS = "edgeNfs"; public static final String DEVICE_ACTIVITIES = "deviceActivity"; + public static final String TO_CORE_NF_OTHER = "coreNfOther"; // normally, there is no messages when codebase is fine + public static final String TO_CORE_NF_COMPONENT_LIFECYCLE = "coreNfCompLfcl"; + public static final String TO_CORE_NF_DEVICE_RPC_RESPONSE = "coreNfDevRpcRsp"; + public static final String TO_CORE_NF_EDGE_EVENT_UPDATE = "coreNfEdgeUpd"; + public static final String TO_CORE_NF_EDGE_SYNC_REQUEST = "coreNfEdgeSyncReq"; + public static final String TO_CORE_NF_EDGE_SYNC_RESPONSE = "coreNfEdgeSyncResp"; + public static final String TO_CORE_NF_NOTIFICATION_RULE_PROCESSOR = "coreNfNfRlProc"; + public static final String TO_CORE_NF_QUEUE_UPDATE = "coreNfQueueUpd"; + public static final String TO_CORE_NF_QUEUE_DELETE = "coreNfQueueDel"; + public static final String TO_CORE_NF_SUBSCRIPTION_SERVICE = "coreNfSubSvc"; + public static final String TO_CORE_NF_SUBSCRIPTION_MANAGER = "coreNfSubMgr"; + public static final String TO_CORE_NF_VC_RESPONSE = "coreNfVCRsp"; + private final StatsCounter totalCounter; private final StatsCounter sessionEventCounter; private final StatsCounter getAttributesCounter; @@ -48,14 +60,25 @@ public class TbCoreConsumerStats { private final StatsCounter toDeviceRPCCallResponseCounter; private final StatsCounter subscriptionInfoCounter; private final StatsCounter claimDeviceCounter; - private final StatsCounter deviceStateCounter; private final StatsCounter subscriptionMsgCounter; - private final StatsCounter toCoreNotificationsCounter; private final StatsCounter edgeNotificationsCounter; private final StatsCounter deviceActivitiesCounter; - private final List counters = new ArrayList<>(); + private final StatsCounter toCoreNfOtherCounter; + private final StatsCounter toCoreNfComponentLifecycleCounter; + private final StatsCounter toCoreNfDeviceRpcResponseCounter; + private final StatsCounter toCoreNfEdgeEventUpdateCounter; + private final StatsCounter toCoreNfEdgeSyncRequestCounter; + private final StatsCounter toCoreNfEdgeSyncResponseCounter; + private final StatsCounter toCoreNfNotificationRuleProcessorCounter; + private final StatsCounter toCoreNfQueueUpdateCounter; + private final StatsCounter toCoreNfQueueDeleteCounter; + private final StatsCounter toCoreNfSubscriptionServiceCounter; + private final StatsCounter toCoreNfSubscriptionManagerCounter; + private final StatsCounter toCoreNfVersionControlResponseCounter; + + private final List counters = new ArrayList<>(24); public TbCoreConsumerStats(StatsFactory statsFactory) { String statsKey = StatsType.CORE.getName(); @@ -70,9 +93,23 @@ public class TbCoreConsumerStats { this.claimDeviceCounter = register(statsFactory.createStatsCounter(statsKey, DEVICE_CLAIMS)); this.deviceStateCounter = register(statsFactory.createStatsCounter(statsKey, DEVICE_STATES)); this.subscriptionMsgCounter = register(statsFactory.createStatsCounter(statsKey, SUBSCRIPTION_MSGS)); - this.toCoreNotificationsCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NOTIFICATIONS)); this.edgeNotificationsCounter = register(statsFactory.createStatsCounter(statsKey, EDGE_NOTIFICATIONS)); this.deviceActivitiesCounter = register(statsFactory.createStatsCounter(statsKey, DEVICE_ACTIVITIES)); + + // Core notification counters + this.toCoreNfOtherCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_OTHER)); + this.toCoreNfComponentLifecycleCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_COMPONENT_LIFECYCLE)); + this.toCoreNfDeviceRpcResponseCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_DEVICE_RPC_RESPONSE)); + this.toCoreNfEdgeEventUpdateCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_EDGE_EVENT_UPDATE)); + this.toCoreNfEdgeSyncRequestCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_EDGE_SYNC_REQUEST)); + this.toCoreNfEdgeSyncResponseCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_EDGE_SYNC_RESPONSE)); + this.toCoreNfNotificationRuleProcessorCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_NOTIFICATION_RULE_PROCESSOR)); + this.toCoreNfQueueUpdateCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_QUEUE_UPDATE)); + this.toCoreNfQueueDeleteCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_QUEUE_DELETE)); + this.toCoreNfSubscriptionServiceCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_SUBSCRIPTION_SERVICE)); + this.toCoreNfSubscriptionManagerCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_SUBSCRIPTION_MANAGER)); + this.toCoreNfVersionControlResponseCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NF_VC_RESPONSE)); + } private StatsCounter register(StatsCounter counter){ @@ -127,7 +164,39 @@ public class TbCoreConsumerStats { public void log(TransportProtos.ToCoreNotificationMsg msg) { totalCounter.increment(); - toCoreNotificationsCounter.increment(); + if (msg.hasToLocalSubscriptionServiceMsg()) { + toCoreNfSubscriptionServiceCounter.increment(); + } else if (msg.hasFromDeviceRpcResponse()) { + toCoreNfDeviceRpcResponseCounter.increment(); + } else if (msg.hasComponentLifecycle()) { + toCoreNfComponentLifecycleCounter.increment(); + } else if (!msg.getComponentLifecycleMsg().isEmpty()) { + toCoreNfComponentLifecycleCounter.increment(); + } else if (msg.hasEdgeEventUpdate()) { + toCoreNfEdgeEventUpdateCounter.increment(); + } else if (!msg.getEdgeEventUpdateMsg().isEmpty()) { + toCoreNfEdgeEventUpdateCounter.increment(); + } else if (msg.hasToEdgeSyncRequest()) { + toCoreNfEdgeSyncRequestCounter.increment(); + } else if (!msg.getToEdgeSyncRequestMsg().isEmpty()) { + toCoreNfEdgeSyncRequestCounter.increment(); + } else if (msg.hasFromEdgeSyncResponse()) { + toCoreNfEdgeSyncResponseCounter.increment(); + } else if (!msg.getFromEdgeSyncResponseMsg().isEmpty()) { + toCoreNfEdgeSyncResponseCounter.increment(); + } else if (msg.hasQueueUpdateMsg()) { + toCoreNfQueueUpdateCounter.increment(); + } else if (msg.hasQueueDeleteMsg()) { + toCoreNfQueueDeleteCounter.increment(); + } else if (msg.hasVcResponseMsg()) { + toCoreNfVersionControlResponseCounter.increment(); + } else if (msg.hasToSubscriptionMgrMsg()) { + toCoreNfSubscriptionManagerCounter.increment(); + } else if (msg.hasNotificationRuleProcessorMsg()) { + toCoreNfNotificationRuleProcessorCounter.increment(); + } else { + toCoreNfOtherCounter.increment(); + } } public void printStats() { diff --git a/application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackCallback.java b/application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackCallback.java index ef2ba8798d..c2171c2550 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackCallback.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackCallback.java @@ -17,11 +17,14 @@ package org.thingsboard.server.service.queue; import io.micrometer.core.instrument.Timer; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.ExceptionUtil; +import org.thingsboard.server.common.data.exception.AbstractRateLimitException; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.queue.RuleEngineException; import org.thingsboard.server.common.msg.queue.RuleNodeInfo; import org.thingsboard.server.common.msg.queue.TbMsgCallback; +import org.thingsboard.server.common.msg.tools.TbRateLimitsException; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -57,8 +60,23 @@ public class TbMsgPackCallback implements TbMsgCallback { ctx.onSuccess(id); } + @Override + public void onRateLimit(RuleEngineException e) { + log.debug("[{}] ON RATE LIMIT", id, e); + //TODO notify tenant on rate limit + if (failedMsgTimer != null) { + failedMsgTimer.record(System.currentTimeMillis() - startMsgProcessing, TimeUnit.MILLISECONDS); + } + ctx.onSuccess(id); + } + @Override public void onFailure(RuleEngineException e) { + if (ExceptionUtil.lookupExceptionInCause(e, AbstractRateLimitException.class) != null) { + onRateLimit(e); + return; + } + log.trace("[{}] ON FAILURE", id, e); if (failedMsgTimer != null) { failedMsgTimer.record(System.currentTimeMillis() - startMsgProcessing, TimeUnit.MILLISECONDS); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/TbRuleEngineConsumerStats.java b/application/src/main/java/org/thingsboard/server/service/queue/TbRuleEngineConsumerStats.java index 77e805eb62..842b627c22 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/TbRuleEngineConsumerStats.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/TbRuleEngineConsumerStats.java @@ -18,12 +18,14 @@ package org.thingsboard.server.service.queue; import io.micrometer.core.instrument.Timer; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.msg.queue.RuleEngineException; import org.thingsboard.server.common.stats.StatsCounter; import org.thingsboard.server.common.stats.StatsFactory; import org.thingsboard.server.common.stats.StatsType; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; +import org.thingsboard.server.queue.discovery.QueueKey; import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingResult; import java.util.ArrayList; @@ -63,9 +65,11 @@ public class TbRuleEngineConsumerStats { private final ConcurrentMap tenantExceptions = new ConcurrentHashMap<>(); private final String queueName; + private final TenantId tenantId; - public TbRuleEngineConsumerStats(String queueName, StatsFactory statsFactory) { - this.queueName = queueName; + public TbRuleEngineConsumerStats(QueueKey queueKey, StatsFactory statsFactory) { + this.queueName = queueKey.getQueueName(); + this.tenantId = queueKey.getTenantId(); this.statsFactory = statsFactory; String statsKey = StatsType.RULE_ENGINE.getName() + "." + queueName; @@ -156,7 +160,11 @@ public class TbRuleEngineConsumerStats { counters.forEach(counter -> { stats.append(counter.getName()).append(" = [").append(counter.get()).append("] "); }); - log.info("[{}] Stats: {}", queueName, stats); + if (tenantId.isSysTenantId()) { + log.info("[{}] Stats: {}", queueName, stats); + } else { + log.info("[{}][{}] Stats: {}", queueName, tenantId, stats); + } } } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java index 2d517a2213..091a5a4988 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java @@ -59,16 +59,14 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import java.util.function.Function; import java.util.stream.Collectors; @Slf4j public abstract class AbstractConsumerService extends TbApplicationEventListener { - protected volatile ExecutorService consumersExecutor; protected volatile ExecutorService notificationsConsumerExecutor; protected volatile boolean stopped = false; - + protected volatile boolean isReady = false; protected final ActorSystemContext actorContext; protected final DataDecodingEncodingService encodingService; protected final TbTenantProfileCache tenantProfileCache; @@ -99,8 +97,7 @@ public abstract class AbstractConsumerService> pendingMap = msgs.stream().collect( - Collectors.toConcurrentMap(s -> UUID.randomUUID(), Function.identity())); + List> orderedMsgList = msgs.stream().map(msg -> new IdMsgPair<>(UUID.randomUUID(), msg)).collect(Collectors.toList()); + ConcurrentMap> pendingMap = orderedMsgList.stream().collect( + Collectors.toConcurrentMap(IdMsgPair::getUuid, IdMsgPair::getMsg)); CountDownLatch processingTimeoutLatch = new CountDownLatch(1); TbPackProcessingContext> ctx = new TbPackProcessingContext<>( processingTimeoutLatch, pendingMap, new ConcurrentHashMap<>()); - pendingMap.forEach((id, msg) -> { + orderedMsgList.forEach(element -> { + UUID id = element.getUuid(); + TbProtoQueueMsg msg = element.getMsg(); log.trace("[{}] Creating notification callback for message: {}", id, msg.getValue()); TbCallback callback = new TbPackCallback<>(id, ctx); try { @@ -165,55 +166,57 @@ public abstract class AbstractConsumerService actorMsgOpt = encodingService.decode(nfMsg.toByteArray()); - if (actorMsgOpt.isPresent()) { - TbActorMsg actorMsg = actorMsgOpt.get(); - if (actorMsg instanceof ComponentLifecycleMsg) { - ComponentLifecycleMsg componentLifecycleMsg = (ComponentLifecycleMsg) actorMsg; - log.debug("[{}][{}][{}] Received Lifecycle event: {}", componentLifecycleMsg.getTenantId(), componentLifecycleMsg.getEntityId().getEntityType(), - componentLifecycleMsg.getEntityId(), componentLifecycleMsg.getEvent()); - if (EntityType.TENANT_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { - TenantProfileId tenantProfileId = new TenantProfileId(componentLifecycleMsg.getEntityId().getId()); - tenantProfileCache.evict(tenantProfileId); + actorMsgOpt.ifPresent(tbActorMsg -> handleComponentLifecycleMsg(id, tbActorMsg)); + } + + protected void handleComponentLifecycleMsg(UUID id, TbActorMsg actorMsg) { + if (actorMsg instanceof ComponentLifecycleMsg) { + ComponentLifecycleMsg componentLifecycleMsg = (ComponentLifecycleMsg) actorMsg; + log.debug("[{}][{}][{}] Received Lifecycle event: {}", componentLifecycleMsg.getTenantId(), componentLifecycleMsg.getEntityId().getEntityType(), + componentLifecycleMsg.getEntityId(), componentLifecycleMsg.getEvent()); + if (EntityType.TENANT_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { + TenantProfileId tenantProfileId = new TenantProfileId(componentLifecycleMsg.getEntityId().getId()); + tenantProfileCache.evict(tenantProfileId); + if (componentLifecycleMsg.getEvent().equals(ComponentLifecycleEvent.UPDATED)) { + apiUsageStateService.onTenantProfileUpdate(tenantProfileId); + } + } else if (EntityType.TENANT.equals(componentLifecycleMsg.getEntityId().getEntityType())) { + if (TenantId.SYS_TENANT_ID.equals(componentLifecycleMsg.getTenantId())) { + jwtSettingsService.ifPresent(JwtSettingsService::reloadJwtSettings); + return; + } else { + tenantProfileCache.evict(componentLifecycleMsg.getTenantId()); + partitionService.removeTenant(componentLifecycleMsg.getTenantId()); if (componentLifecycleMsg.getEvent().equals(ComponentLifecycleEvent.UPDATED)) { - apiUsageStateService.onTenantProfileUpdate(tenantProfileId); - } - } else if (EntityType.TENANT.equals(componentLifecycleMsg.getEntityId().getEntityType())) { - if (TenantId.SYS_TENANT_ID.equals(componentLifecycleMsg.getTenantId())) { - jwtSettingsService.ifPresent(JwtSettingsService::reloadJwtSettings); - return; - } else { - tenantProfileCache.evict(componentLifecycleMsg.getTenantId()); - partitionService.removeTenant(componentLifecycleMsg.getTenantId()); - if (componentLifecycleMsg.getEvent().equals(ComponentLifecycleEvent.UPDATED)) { - apiUsageStateService.onTenantUpdate(componentLifecycleMsg.getTenantId()); - } else if (componentLifecycleMsg.getEvent().equals(ComponentLifecycleEvent.DELETED)) { - apiUsageStateService.onTenantDelete((TenantId) componentLifecycleMsg.getEntityId()); - } - } - } else if (EntityType.DEVICE_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { - deviceProfileCache.evict(componentLifecycleMsg.getTenantId(), new DeviceProfileId(componentLifecycleMsg.getEntityId().getId())); - } else if (EntityType.DEVICE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { - deviceProfileCache.evict(componentLifecycleMsg.getTenantId(), new DeviceId(componentLifecycleMsg.getEntityId().getId())); - } else if (EntityType.ASSET_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { - assetProfileCache.evict(componentLifecycleMsg.getTenantId(), new AssetProfileId(componentLifecycleMsg.getEntityId().getId())); - } else if (EntityType.ASSET.equals(componentLifecycleMsg.getEntityId().getEntityType())) { - assetProfileCache.evict(componentLifecycleMsg.getTenantId(), new AssetId(componentLifecycleMsg.getEntityId().getId())); - } else if (EntityType.ENTITY_VIEW.equals(componentLifecycleMsg.getEntityId().getEntityType())) { - actorContext.getTbEntityViewService().onComponentLifecycleMsg(componentLifecycleMsg); - } else if (EntityType.API_USAGE_STATE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { - apiUsageStateService.onApiUsageStateUpdate(componentLifecycleMsg.getTenantId()); - } else if (EntityType.CUSTOMER.equals(componentLifecycleMsg.getEntityId().getEntityType())) { - if (componentLifecycleMsg.getEvent() == ComponentLifecycleEvent.DELETED) { - apiUsageStateService.onCustomerDelete((CustomerId) componentLifecycleMsg.getEntityId()); + apiUsageStateService.onTenantUpdate(componentLifecycleMsg.getTenantId()); + } else if (componentLifecycleMsg.getEvent().equals(ComponentLifecycleEvent.DELETED)) { + apiUsageStateService.onTenantDelete((TenantId) componentLifecycleMsg.getEntityId()); } } - eventPublisher.publishEvent(componentLifecycleMsg); + } else if (EntityType.DEVICE_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { + deviceProfileCache.evict(componentLifecycleMsg.getTenantId(), new DeviceProfileId(componentLifecycleMsg.getEntityId().getId())); + } else if (EntityType.DEVICE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { + deviceProfileCache.evict(componentLifecycleMsg.getTenantId(), new DeviceId(componentLifecycleMsg.getEntityId().getId())); + } else if (EntityType.ASSET_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { + assetProfileCache.evict(componentLifecycleMsg.getTenantId(), new AssetProfileId(componentLifecycleMsg.getEntityId().getId())); + } else if (EntityType.ASSET.equals(componentLifecycleMsg.getEntityId().getEntityType())) { + assetProfileCache.evict(componentLifecycleMsg.getTenantId(), new AssetId(componentLifecycleMsg.getEntityId().getId())); + } else if (EntityType.ENTITY_VIEW.equals(componentLifecycleMsg.getEntityId().getEntityType())) { + actorContext.getTbEntityViewService().onComponentLifecycleMsg(componentLifecycleMsg); + } else if (EntityType.API_USAGE_STATE.equals(componentLifecycleMsg.getEntityId().getEntityType())) { + apiUsageStateService.onApiUsageStateUpdate(componentLifecycleMsg.getTenantId()); + } else if (EntityType.CUSTOMER.equals(componentLifecycleMsg.getEntityId().getEntityType())) { + if (componentLifecycleMsg.getEvent() == ComponentLifecycleEvent.DELETED) { + apiUsageStateService.onCustomerDelete((CustomerId) componentLifecycleMsg.getEntityId()); + } } - log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg); - actorContext.tellWithHighPriority(actorMsg); + eventPublisher.publishEvent(componentLifecycleMsg); } + log.trace("[{}] Forwarding component lifecycle message to App Actor {}", id, actorMsg); + actorContext.tellWithHighPriority(actorMsg); } protected abstract void handleNotification(UUID id, TbProtoQueueMsg msg, TbCallback callback) throws Exception; @@ -221,13 +224,10 @@ public abstract class AbstractConsumerService partitions; + + public TbQueueConsumerManagerTask(QueueEvent event) { + this.event = event; + } + + public TbQueueConsumerManagerTask(QueueEvent event, Queue queue) { + this.event = event; + this.queue = queue; + } + + public TbQueueConsumerManagerTask(QueueEvent event, Set partitions) { + this.event = event; + this.partitions = partitions; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/queue/ruleengine/TbQueueConsumerTask.java b/application/src/main/java/org/thingsboard/server/service/queue/ruleengine/TbQueueConsumerTask.java new file mode 100644 index 0000000000..59d55285f0 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/queue/ruleengine/TbQueueConsumerTask.java @@ -0,0 +1,70 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.queue.ruleengine; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.queue.TbQueueConsumer; +import org.thingsboard.server.queue.common.TbProtoQueueMsg; + +import java.util.Set; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +@RequiredArgsConstructor +@Slf4j +public class TbQueueConsumerTask { + + @Getter + private final Object key; + @Getter + private final TbQueueConsumer> consumer; + + @Setter + private Future task; + + public void subscribe(Set partitions) { + log.trace("[{}] Subscribing to partitions: {}", key, partitions); + consumer.subscribe(partitions); + } + + public void initiateStop() { + log.debug("[{}] Initiating stop", key); + consumer.stop(); + } + + public void awaitCompletion() { + log.trace("[{}] Awaiting finish", key); + if (isRunning()) { + try { + task.get(30, TimeUnit.SECONDS); + log.trace("[{}] Awaited finish", key); + } catch (Exception e) { + log.warn("[{}] Failed to await for consumer to stop", key, e); + } + task = null; + } + } + + public boolean isRunning() { + return task != null; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineConsumerContext.java b/application/src/main/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineConsumerContext.java new file mode 100644 index 0000000000..da2a5d0db4 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineConsumerContext.java @@ -0,0 +1,89 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.queue.ruleengine; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.common.stats.StatsFactory; +import org.thingsboard.server.queue.TbQueueAdmin; +import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; +import org.thingsboard.server.queue.provider.TbQueueProducerProvider; +import org.thingsboard.server.queue.provider.TbRuleEngineQueueFactory; +import org.thingsboard.server.queue.util.TbRuleEngineComponent; +import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingStrategyFactory; +import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrategyFactory; +import org.thingsboard.server.service.stats.RuleEngineStatisticsService; + +import javax.annotation.PostConstruct; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +@Component +@TbRuleEngineComponent +@Slf4j +@Data +public class TbRuleEngineConsumerContext { + + @Value("${queue.rule-engine.poll-interval}") + private long pollDuration; + @Value("${queue.rule-engine.pack-processing-timeout}") + private long packProcessingTimeout; + @Value("${queue.rule-engine.stats.enabled:true}") + private boolean statsEnabled; + @Value("${queue.rule-engine.prometheus-stats.enabled:false}") + private boolean prometheusStatsEnabled; + @Value("${queue.rule-engine.topic-deletion-delay:15}") + private int topicDeletionDelayInSec; + @Value("${queue.rule-engine.management-thread-pool-size:12}") + private int mgmtThreadPoolSize; + + private final ActorSystemContext actorContext; + private final StatsFactory statsFactory; + private final TbRuleEngineSubmitStrategyFactory submitStrategyFactory; + private final TbRuleEngineProcessingStrategyFactory processingStrategyFactory; + private final TbRuleEngineQueueFactory queueFactory; + private final RuleEngineStatisticsService statisticsService; + private final TbServiceInfoProvider serviceInfoProvider; + private final PartitionService partitionService; + private final TbQueueProducerProvider producerProvider; + private final TbQueueAdmin queueAdmin; + + private ExecutorService consumersExecutor; + private ExecutorService mgmtExecutor; + private ScheduledExecutorService scheduler; + + private volatile boolean isReady = false; + + @PostConstruct + void init() { + this.consumersExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName("tb-rule-engine-consumer")); + this.mgmtExecutor = ThingsBoardExecutors.newWorkStealingPool(mgmtThreadPoolSize, "tb-rule-engine-mgmt"); + this.scheduler = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("tb-rule-engine-consumer-scheduler")); + } + + public void stop() { + scheduler.shutdownNow(); + consumersExecutor.shutdownNow(); + mgmtExecutor.shutdownNow(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManager.java b/application/src/main/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManager.java new file mode 100644 index 0000000000..5a59cb124e --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManager.java @@ -0,0 +1,486 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.queue.ruleengine; + +import com.google.protobuf.ProtocolStringList; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.queue.Queue; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.gen.MsgProtos; +import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; +import org.thingsboard.server.common.msg.queue.RuleEngineException; +import org.thingsboard.server.common.msg.queue.RuleNodeInfo; +import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.common.msg.queue.TbMsgCallback; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; +import org.thingsboard.server.queue.TbQueueConsumer; +import org.thingsboard.server.queue.common.TbProtoQueueMsg; +import org.thingsboard.server.queue.discovery.QueueKey; +import org.thingsboard.server.service.queue.TbMsgPackCallback; +import org.thingsboard.server.service.queue.TbMsgPackProcessingContext; +import org.thingsboard.server.service.queue.TbRuleEngineConsumerStats; +import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingDecision; +import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingResult; +import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingStrategy; +import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrategy; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; + +@Slf4j +public class TbRuleEngineQueueConsumerManager { + + public static final String SUCCESSFUL_STATUS = "successful"; + public static final String FAILED_STATUS = "failed"; + + private final TbRuleEngineConsumerContext ctx; + private final QueueKey queueKey; + private final TbRuleEngineConsumerStats stats; + private final ReentrantLock lock = new ReentrantLock(); //NonfairSync + + @Getter + private volatile Queue queue; + @Getter + private volatile Set partitions; + private volatile ConsumerWrapper consumerWrapper; + + private volatile boolean stopped; + + private final java.util.Queue tasks = new ConcurrentLinkedQueue<>(); + + public TbRuleEngineQueueConsumerManager(TbRuleEngineConsumerContext ctx, QueueKey queueKey) { + this.ctx = ctx; + this.queueKey = queueKey; + this.stats = new TbRuleEngineConsumerStats(queueKey, ctx.getStatsFactory()); + } + + public void init(Queue queue) { + this.queue = queue; + if (queue.isConsumerPerPartition()) { + this.consumerWrapper = new ConsumerPerPartitionWrapper(); + } else { + this.consumerWrapper = new SingleConsumerWrapper(); + } + log.debug("[{}] Initialized consumer for queue: {}", queueKey, queue); + } + + public void update(Queue queue) { + addTask(new TbQueueConsumerManagerTask(QueueEvent.CONFIG_UPDATE, queue)); + } + + public void update(Set partitions) { + addTask(new TbQueueConsumerManagerTask(QueueEvent.PARTITION_CHANGE, partitions)); + } + + public void delete() { + addTask(new TbQueueConsumerManagerTask(QueueEvent.DELETE)); + } + + private void addTask(TbQueueConsumerManagerTask todo) { + if (stopped) { + return; + } + tasks.add(todo); + log.trace("[{}] Added task: {}", queueKey, todo); + tryProcessTasks(); + } + + private void tryProcessTasks() { + if (!ctx.isReady()) { + log.debug("[{}] TbRuleEngineConsumerContext is not ready yet, will process tasks later", queueKey); + ctx.getScheduler().schedule(this::tryProcessTasks, 1, TimeUnit.SECONDS); + return; + } + ctx.getMgmtExecutor().submit(() -> { + if (lock.tryLock()) { + try { + Queue newConfiguration = null; + Set newPartitions = null; + while (!stopped) { + TbQueueConsumerManagerTask task = tasks.poll(); + if (task == null) { + break; + } + log.trace("[{}] Processing task: {}", queueKey, task); + + if (task.getEvent() == QueueEvent.PARTITION_CHANGE) { + newPartitions = task.getPartitions(); + } else if (task.getEvent() == QueueEvent.CONFIG_UPDATE) { + newConfiguration = task.getQueue(); + } else if (task.getEvent() == QueueEvent.DELETE) { + doDelete(); + return; + } + } + if (stopped) { + return; + } + if (newConfiguration != null) { + doUpdate(newConfiguration); + } + if (newPartitions != null) { + doUpdate(newPartitions); + } + } catch (Exception e) { + log.error("[{}] Failed to process tasks", queueKey, e); + } finally { + lock.unlock(); + } + } else { + log.trace("[{}] Failed to acquire lock", queueKey); + ctx.getScheduler().schedule(this::tryProcessTasks, 1, TimeUnit.SECONDS); + } + }); + } + + private void doUpdate(Queue newQueue) { + log.info("[{}] Processing queue update: {}", queueKey, newQueue); + var oldQueue = this.queue; + this.queue = newQueue; + if (log.isTraceEnabled()) { + log.trace("[{}] Old queue configuration: {}", queueKey, oldQueue); + log.trace("[{}] New queue configuration: {}", queueKey, newQueue); + } + + if (oldQueue == null) { + init(queue); + } else if (newQueue.isConsumerPerPartition() != oldQueue.isConsumerPerPartition()) { + consumerWrapper.getConsumers().forEach(TbQueueConsumerTask::initiateStop); + consumerWrapper.getConsumers().forEach(TbQueueConsumerTask::awaitCompletion); + + init(queue); + if (partitions != null) { + doUpdate(partitions); // even if partitions number was changed, there can be no partition change event + } + } else { + // do nothing, because partitions change (if they changed) will be handled on PartitionChangeEvent, + // and changes to pollInterval/packProcessingTimeout/submitStrategy/processingStrategy will be picked up by consumer on the fly, + // and queue topic and name are immutable + } + } + + private void doUpdate(Set partitions) { + this.partitions = partitions; + consumerWrapper.updatePartitions(partitions); + } + + public void stop() { + log.debug("[{}] Stopping consumers", queueKey); + consumerWrapper.getConsumers().forEach(TbQueueConsumerTask::initiateStop); + stopped = true; + } + + public void awaitStop() { + consumerWrapper.getConsumers().forEach(TbQueueConsumerTask::awaitCompletion); + log.debug("[{}] Unsubscribed and stopped consumers", queueKey); + } + + private void doDelete() { + stopped = true; + log.info("[{}] Handling queue deletion", queueKey); + consumerWrapper.getConsumers().forEach(TbQueueConsumerTask::awaitCompletion); + + List>> queueConsumers = consumerWrapper.getConsumers().stream() + .map(TbQueueConsumerTask::getConsumer).collect(Collectors.toList()); + ctx.getConsumersExecutor().submit(() -> { + drainQueue(queueConsumers); + + queueConsumers.forEach(consumer -> { + for (String topic : consumer.getFullTopicNames()) { + try { + ctx.getQueueAdmin().deleteTopic(topic); + log.info("Deleted topic {}", topic); + } catch (Exception e) { + log.error("Failed to delete topic {}", topic, e); + } + } + try { + consumer.unsubscribe(); + } catch (Exception e) { + log.error("[{}] Failed to unsubscribe consumer", queueKey, e); + } + }); + }); + } + + private void launchConsumer(TbQueueConsumerTask consumerTask) { + log.info("[{}] Launching consumer", consumerTask.getKey()); + Future consumerLoop = ctx.getConsumersExecutor().submit(() -> { + ThingsBoardThreadFactory.updateCurrentThreadName(consumerTask.getKey().toString()); + try { + consumerLoop(consumerTask.getConsumer()); + } catch (Throwable e) { + log.error("Failure in consumer loop", e); + } + }); + consumerTask.setTask(consumerLoop); + } + + private void consumerLoop(TbQueueConsumer> consumer) { + while (!stopped && !consumer.isStopped()) { + try { + List> msgs = consumer.poll(queue.getPollInterval()); + if (msgs.isEmpty()) { + continue; + } + processMsgs(msgs, consumer, queue); + } catch (Exception e) { + if (!consumer.isStopped()) { + log.warn("Failed to process messages from queue", e); + try { + Thread.sleep(ctx.getPollDuration()); + } catch (InterruptedException e2) { + log.trace("Failed to wait until the server has capacity to handle new requests", e2); + } + } + } + } + if (consumer.isStopped()) { + consumer.unsubscribe(); + } + log.info("Rule Engine consumer stopped"); + } + + private void processMsgs(List> msgs, + TbQueueConsumer> consumer, + Queue queue) throws InterruptedException { + TbRuleEngineSubmitStrategy submitStrategy = getSubmitStrategy(queue); + TbRuleEngineProcessingStrategy ackStrategy = getProcessingStrategy(queue); + submitStrategy.init(msgs); + while (!stopped && !consumer.isStopped()) { + TbMsgPackProcessingContext packCtx = new TbMsgPackProcessingContext(queue.getName(), submitStrategy, ackStrategy.isSkipTimeoutMsgs()); + submitStrategy.submitAttempt((id, msg) -> submitMessage(packCtx, id, msg)); + + final boolean timeout = !packCtx.await(queue.getPackProcessingTimeout(), TimeUnit.MILLISECONDS); + + TbRuleEngineProcessingResult result = new TbRuleEngineProcessingResult(queue.getName(), timeout, packCtx); + if (timeout) { + printFirstOrAll(packCtx, packCtx.getPendingMap(), "Timeout"); + } + if (!packCtx.getFailedMap().isEmpty()) { + printFirstOrAll(packCtx, packCtx.getFailedMap(), "Failed"); + } + packCtx.printProfilerStats(); + + TbRuleEngineProcessingDecision decision = ackStrategy.analyze(result); + if (ctx.isStatsEnabled()) { + stats.log(result, decision.isCommit()); + } + + packCtx.cleanup(); + + if (decision.isCommit()) { + submitStrategy.stop(); + consumer.commit(); + break; + } else { + submitStrategy.update(decision.getReprocessMap()); + } + } + } + + private TbRuleEngineSubmitStrategy getSubmitStrategy(Queue queue) { + return ctx.getSubmitStrategyFactory().newInstance(queue.getName(), queue.getSubmitStrategy()); + } + + private TbRuleEngineProcessingStrategy getProcessingStrategy(Queue queue) { + return ctx.getProcessingStrategyFactory().newInstance(queue.getName(), queue.getProcessingStrategy()); + } + + private void submitMessage(TbMsgPackProcessingContext packCtx, UUID id, TbProtoQueueMsg msg) { + log.trace("[{}] Creating callback for topic {} message: {}", id, queue.getName(), msg.getValue()); + ToRuleEngineMsg toRuleEngineMsg = msg.getValue(); + TenantId tenantId = TenantId.fromUUID(new UUID(toRuleEngineMsg.getTenantIdMSB(), toRuleEngineMsg.getTenantIdLSB())); + TbMsgCallback callback = ctx.isPrometheusStatsEnabled() ? + new TbMsgPackCallback(id, tenantId, packCtx, stats.getTimer(tenantId, SUCCESSFUL_STATUS), stats.getTimer(tenantId, FAILED_STATUS)) : + new TbMsgPackCallback(id, tenantId, packCtx); + try { + if (!toRuleEngineMsg.getTbMsg().isEmpty()) { + forwardToRuleEngineActor(queue.getName(), tenantId, toRuleEngineMsg, callback); + } else { + callback.onSuccess(); + } + } catch (Exception e) { + callback.onFailure(new RuleEngineException(e.getMessage(), e)); + } + } + + private void forwardToRuleEngineActor(String queueName, TenantId tenantId, ToRuleEngineMsg toRuleEngineMsg, TbMsgCallback callback) { + TbMsg tbMsg = TbMsg.fromBytes(queueName, toRuleEngineMsg.getTbMsg().toByteArray(), callback); + QueueToRuleEngineMsg msg; + ProtocolStringList relationTypesList = toRuleEngineMsg.getRelationTypesList(); + Set relationTypes; + if (relationTypesList.size() == 1) { + relationTypes = Collections.singleton(relationTypesList.get(0)); + } else { + relationTypes = new HashSet<>(relationTypesList); + } + msg = new QueueToRuleEngineMsg(tenantId, tbMsg, relationTypes, toRuleEngineMsg.getFailureMessage()); + ctx.getActorContext().tell(msg); + } + + private void printFirstOrAll(TbMsgPackProcessingContext ctx, Map> map, String prefix) { + boolean printAll = log.isTraceEnabled(); + log.info("[{}] {} to process [{}] messages", queueKey, prefix, map.size()); + for (Map.Entry> pending : map.entrySet()) { + ToRuleEngineMsg tmp = pending.getValue().getValue(); + TbMsg tmpMsg = TbMsg.fromBytes(queue.getName(), tmp.getTbMsg().toByteArray(), TbMsgCallback.EMPTY); + RuleNodeInfo ruleNodeInfo = ctx.getLastVisitedRuleNode(pending.getKey()); + if (printAll) { + log.trace("[{}][{}] {} to process message: {}, Last Rule Node: {}", queueKey, TenantId.fromUUID(new UUID(tmp.getTenantIdMSB(), tmp.getTenantIdLSB())), prefix, tmpMsg, ruleNodeInfo); + } else { + log.info("[{}] {} to process message: {}, Last Rule Node: {}", TenantId.fromUUID(new UUID(tmp.getTenantIdMSB(), tmp.getTenantIdLSB())), prefix, tmpMsg, ruleNodeInfo); + break; + } + } + } + + public void printStats(long ts) { + stats.printStats(); + ctx.getStatisticsService().reportQueueStats(ts, stats); + stats.reset(); + } + + private void drainQueue(List>> consumers) { + long finishTs = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(ctx.getTopicDeletionDelayInSec()); + try { + int n = 0; + while (System.currentTimeMillis() <= finishTs) { + for (TbQueueConsumer> consumer : consumers) { + List> msgs = consumer.poll(queue.getPollInterval()); + if (msgs.isEmpty()) { + continue; + } + for (TbProtoQueueMsg msg : msgs) { + try { + MsgProtos.TbMsgProto tbMsgProto = MsgProtos.TbMsgProto.parseFrom(msg.getValue().getTbMsg().toByteArray()); + EntityId originator = EntityIdFactory.getByTypeAndUuid(tbMsgProto.getEntityType(), new UUID(tbMsgProto.getEntityIdMSB(), tbMsgProto.getEntityIdLSB())); + + TopicPartitionInfo tpi = ctx.getPartitionService().resolve(ServiceType.TB_RULE_ENGINE, queue.getName(), TenantId.SYS_TENANT_ID, originator); + ctx.getProducerProvider().getRuleEngineMsgProducer().send(tpi, msg, null); + n++; + } catch (Throwable e) { + log.warn("Failed to move message to system {}: {}", consumer.getTopic(), msg, e); + } + } + consumer.commit(); + } + } + if (n > 0) { + log.info("Moved {} messages from {} to system {}", n, queueKey, queue.getName()); + } + } catch (Exception e) { + log.error("[{}] Failed to drain queue", queueKey, e); + } + } + + private static String partitionsToString(Collection partitions) { + return partitions.stream().map(TopicPartitionInfo::getFullTopicName).collect(Collectors.joining(", ", "[", "]")); + } + + interface ConsumerWrapper { + + void updatePartitions(Set partitions); + + Collection getConsumers(); + + } + + class ConsumerPerPartitionWrapper implements ConsumerWrapper { + private final Map consumers = new HashMap<>(); + + @Override + public void updatePartitions(Set partitions) { + Set addedPartitions = new HashSet<>(partitions); + addedPartitions.removeAll(consumers.keySet()); + + Set removedPartitions = new HashSet<>(consumers.keySet()); + removedPartitions.removeAll(partitions); + log.info("[{}] Added partitions: {}, removed partitions: {}", queueKey, partitionsToString(addedPartitions), partitionsToString(removedPartitions)); + + removedPartitions.forEach((tpi) -> { + consumers.get(tpi).initiateStop(); + }); + removedPartitions.forEach((tpi) -> { + consumers.remove(tpi).awaitCompletion(); + }); + + addedPartitions.forEach((tpi) -> { + String key = queueKey + "-" + tpi.getPartition().orElse(-999999); + TbQueueConsumerTask consumer = new TbQueueConsumerTask(key, ctx.getQueueFactory().createToRuleEngineMsgConsumer(queue)); + consumers.put(tpi, consumer); + consumer.subscribe(Set.of(tpi)); + launchConsumer(consumer); + }); + } + + @Override + public Collection getConsumers() { + return consumers.values(); + } + } + + class SingleConsumerWrapper implements ConsumerWrapper { + private TbQueueConsumerTask consumer; + + @Override + public void updatePartitions(Set partitions) { + log.info("[{}] New partitions: {}", queueKey, partitionsToString(partitions)); + if (partitions.isEmpty()) { + if (consumer != null && consumer.isRunning()) { + consumer.initiateStop(); + consumer.awaitCompletion(); + } + consumer = null; + return; + } + + if (consumer == null) { + consumer = new TbQueueConsumerTask(queueKey, ctx.getQueueFactory().createToRuleEngineMsgConsumer(queue)); + } + consumer.subscribe(partitions); + if (!consumer.isRunning()) { + launchConsumer(consumer); + } + } + + @Override + public Collection getConsumers() { + if (consumer == null) { + return Collections.emptyList(); + } + return List.of(consumer); + } + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java new file mode 100644 index 0000000000..9d8024d76f --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java @@ -0,0 +1,188 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.resource; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.ImageDescriptor; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.TbImageDeleteResult; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.resource.ImageCacheKey; +import org.thingsboard.server.dao.resource.ImageService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.entitiy.AbstractTbEntityService; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Service +@Slf4j +@TbCoreComponent +public class DefaultTbImageService extends AbstractTbEntityService implements TbImageService { + + private final TbClusterService clusterService; + private final ImageService imageService; + private final Cache etagCache; + + public DefaultTbImageService(TbClusterService clusterService, ImageService imageService, + @Value("${cache.image.etag.timeToLiveInMinutes:44640}") int cacheTtl, + @Value("${cache.image.etag.maxSize:10000}") int cacheMaxSize) { + this.clusterService = clusterService; + this.imageService = imageService; + this.etagCache = Caffeine.newBuilder() + .expireAfterAccess(cacheTtl, TimeUnit.MINUTES) + .maximumSize(cacheMaxSize) + .build(); + } + + @Override + public String getETag(ImageCacheKey imageCacheKey) { + return etagCache.getIfPresent(imageCacheKey); + } + + @Override + public void putETag(ImageCacheKey imageCacheKey, String etag) { + etagCache.put(imageCacheKey, etag); + } + + @Override + public void evictETags(ImageCacheKey imageCacheKey) { + etagCache.invalidate(imageCacheKey); + if (imageCacheKey.getPublicResourceKey() == null) { + etagCache.invalidate(imageCacheKey.withPreview(true)); + } + } + + @Override + public TbResourceInfo save(TbResource image, User user) throws Exception { + ActionType actionType = image.getId() == null ? ActionType.ADDED : ActionType.UPDATED; + TenantId tenantId = image.getTenantId(); + try { + var oldEtag = getEtag(image); + TbResourceInfo existingImage = null; + if (image.getId() == null && StringUtils.isNotEmpty(image.getResourceKey())) { + existingImage = imageService.getImageInfoByTenantIdAndKey(tenantId, image.getResourceKey()); + if (existingImage != null) { + image.setId(existingImage.getId()); + } + } + TbResourceInfo savedImage = imageService.saveImage(image); + notificationEntityService.logEntityAction(tenantId, savedImage.getId(), savedImage, actionType, user); + + List toEvict = new ArrayList<>(); + if (oldEtag.isPresent()) { + var newEtag = getEtag(savedImage); + if (newEtag.isPresent() && !oldEtag.get().equals(newEtag.get())) { + toEvict.add(ImageCacheKey.forImage(tenantId, image.getResourceKey())); + if (image.isPublic()) { + toEvict.add(ImageCacheKey.forPublicImage(savedImage.getPublicResourceKey())); + } + } + } + if (existingImage != null && image.isPublic() != existingImage.isPublic()) { + toEvict.add(ImageCacheKey.forPublicImage(image.getPublicResourceKey())); + } + if (!toEvict.isEmpty()) { + evictFromCache(tenantId, toEvict); + } + return savedImage; + } catch (Exception e) { + image.setData(null); + notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.TB_RESOURCE), image, actionType, user, e); + throw e; + } + } + + private Optional getEtag(TbResourceInfo image) throws JsonProcessingException { + var descriptor = image.getDescriptor(ImageDescriptor.class); + return Optional.ofNullable(descriptor != null ? descriptor.getEtag() : null); + } + + private Optional getPreviewEtag(TbResourceInfo image) throws JsonProcessingException { + var descriptor = image.getDescriptor(ImageDescriptor.class); + descriptor = descriptor != null ? descriptor.getPreviewDescriptor() : null; + return Optional.ofNullable(descriptor != null ? descriptor.getEtag() : null); + } + + @Override + public TbResourceInfo save(TbResourceInfo imageInfo, TbResourceInfo oldImageInfo, User user) { + TenantId tenantId = imageInfo.getTenantId(); + TbResourceId imageId = imageInfo.getId(); + try { + imageInfo = imageService.saveImageInfo(imageInfo); + notificationEntityService.logEntityAction(tenantId, imageId, imageInfo, ActionType.UPDATED, user); + + if (imageInfo.isPublic() != oldImageInfo.isPublic()) { + evictFromCache(tenantId, List.of(ImageCacheKey.forPublicImage(imageInfo.getPublicResourceKey()))); + } + return imageInfo; + } catch (Exception e) { + notificationEntityService.logEntityAction(tenantId, imageId, imageInfo, ActionType.UPDATED, user, e); + throw e; + } + } + + @Override + public TbImageDeleteResult delete(TbResourceInfo imageInfo, User user, boolean force) { + TenantId tenantId = imageInfo.getTenantId(); + TbResourceId imageId = imageInfo.getId(); + try { + TbImageDeleteResult result = imageService.deleteImage(imageInfo, force); + if (result.isSuccess()) { + notificationEntityService.logEntityAction(tenantId, imageId, imageInfo, ActionType.DELETED, user, imageId.toString()); + + List toEvict = new ArrayList<>(); + toEvict.add(ImageCacheKey.forImage(tenantId, imageInfo.getResourceKey())); + if (imageInfo.isPublic()) { + toEvict.add(ImageCacheKey.forPublicImage(imageInfo.getPublicResourceKey())); + } + evictFromCache(tenantId, toEvict); + } + return result; + } catch (Exception e) { + notificationEntityService.logEntityAction(tenantId, imageId, ActionType.DELETED, user, e, imageId.toString()); + throw e; + } + } + + private void evictFromCache(TenantId tenantId, List toEvict) { + toEvict.forEach(this::evictETags); + clusterService.broadcastToCore(TransportProtos.ToCoreNotificationMsg.newBuilder() + .setResourceCacheInvalidateMsg(TransportProtos.ResourceCacheInvalidateMsg.newBuilder() + .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) + .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) + .addAllKeys(toEvict.stream().map(ImageCacheKey::toProto).collect(Collectors.toList())) + .build()) + .build()); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java index 81a06e9344..2705092b04 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java @@ -15,16 +15,12 @@ */ package org.thingsboard.server.service.resource; -import com.google.common.hash.HashCode; -import com.google.common.hash.Hashing; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ResourceType; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TbResource; -import org.thingsboard.server.common.data.TbResourceInfo; -import org.thingsboard.server.common.data.TbResourceInfoFilter; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -33,12 +29,10 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.lwm2m.LwM2mObject; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; -import java.util.Base64; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; @@ -52,37 +46,51 @@ import static org.thingsboard.server.utils.LwM2mObjectModelUtils.toLwm2mResource @Slf4j @Service @TbCoreComponent +@RequiredArgsConstructor public class DefaultTbResourceService extends AbstractTbEntityService implements TbResourceService { private final ResourceService resourceService; - public DefaultTbResourceService(ResourceService resourceService) { - this.resourceService = resourceService; - } - - @Override - public TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceId) { - return resourceService.getResource(tenantId, resourceType, resourceId); - } - - @Override - public TbResource findResourceById(TenantId tenantId, TbResourceId resourceId) { - return resourceService.findResourceById(tenantId, resourceId); - } - - @Override - public TbResourceInfo findResourceInfoById(TenantId tenantId, TbResourceId resourceId) { - return resourceService.findResourceInfoById(tenantId, resourceId); - } - @Override - public PageData findAllTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink) { - return resourceService.findAllTenantResourcesByTenantId(filter, pageLink); + public TbResource save(TbResource resource, User user) throws ThingsboardException { + if (resource.getResourceType() == ResourceType.IMAGE) { + throw new IllegalArgumentException("Image resource type is not supported"); + } + ActionType actionType = resource.getId() == null ? ActionType.ADDED : ActionType.UPDATED; + TenantId tenantId = resource.getTenantId(); + try { + if (ResourceType.LWM2M_MODEL.equals(resource.getResourceType())) { + toLwm2mResource(resource); + } else if (resource.getResourceKey() == null) { + resource.setResourceKey(resource.getFileName()); + } + TbResource savedResource = resourceService.saveResource(resource); + tbClusterService.onResourceChange(savedResource, null); + notificationEntityService.logEntityAction(tenantId, savedResource.getId(), savedResource, actionType, user); + return savedResource; + } catch (Exception e) { + notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.TB_RESOURCE), + resource, actionType, user, e); + throw e; + } } @Override - public PageData findTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink) { - return resourceService.findTenantResourcesByTenantId(filter, pageLink); + public void delete(TbResource tbResource, User user) { + if (tbResource.getResourceType() == ResourceType.IMAGE) { + throw new IllegalArgumentException("Image resource type is not supported"); + } + TbResourceId resourceId = tbResource.getId(); + TenantId tenantId = tbResource.getTenantId(); + try { + resourceService.deleteResource(tenantId, resourceId); + tbClusterService.onResourceDeleted(tbResource, null); + notificationEntityService.logEntityAction(tenantId, resourceId, tbResource, ActionType.DELETED, user, resourceId.toString()); + } catch (Exception e) { + notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.TB_RESOURCE), + ActionType.DELETED, user, e, resourceId.toString()); + throw e; + } } @Override @@ -108,16 +116,6 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements .collect(Collectors.toList()); } - @Override - public void deleteResourcesByTenantId(TenantId tenantId) { - resourceService.deleteResourcesByTenantId(tenantId); - } - - @Override - public long sumDataSizeByTenantId(TenantId tenantId) { - return resourceService.sumDataSizeByTenantId(tenantId); - } - private Comparator getComparator(String sortProperty, String sortOrder) { Comparator comparator; if ("name".equals(sortProperty)) { @@ -128,50 +126,4 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements return "DESC".equals(sortOrder) ? comparator.reversed() : comparator; } - @Override - public TbResource save(TbResource tbResource, User user) throws ThingsboardException { - ActionType actionType = tbResource.getId() == null ? ActionType.ADDED : ActionType.UPDATED; - TenantId tenantId = tbResource.getTenantId(); - try { - TbResource savedResource = checkNotNull(doSave(tbResource)); - tbClusterService.onResourceChange(savedResource, null); - notificationEntityService.logEntityAction(tenantId, savedResource.getId(), savedResource, actionType, user); - return savedResource; - } catch (Exception e) { - notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.TB_RESOURCE), - tbResource, actionType, user, e); - throw e; - } - } - - @Override - public void delete(TbResource tbResource, User user) { - TbResourceId resourceId = tbResource.getId(); - TenantId tenantId = tbResource.getTenantId(); - try { - resourceService.deleteResource(tenantId, resourceId); - tbClusterService.onResourceDeleted(tbResource, null); - notificationEntityService.logEntityAction(tenantId, resourceId, tbResource, ActionType.DELETED, user, resourceId.toString()); - } catch (Exception e) { - notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.TB_RESOURCE), - ActionType.DELETED, user, e, resourceId.toString()); - throw e; - } - } - - private TbResource doSave(TbResource resource) throws ThingsboardException { - log.trace("Executing saveResource [{}]", resource); - if (StringUtils.isEmpty(resource.getData())) { - throw new DataValidationException("Resource data should be specified!"); - } - if (ResourceType.LWM2M_MODEL.equals(resource.getResourceType())) { - toLwm2mResource(resource); - } else { - resource.setResourceKey(resource.getFileName()); - } - HashCode hashCode = Hashing.sha256().hashBytes(Base64.getDecoder().decode(resource.getData().getBytes())); - resource.setEtag(hashCode.toString()); - return resourceService.saveResource(resource); - } } - diff --git a/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java b/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java new file mode 100644 index 0000000000..d2917ece14 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.resource; + +import org.thingsboard.server.common.data.TbImageDeleteResult; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.dao.resource.ImageCacheKey; + +public interface TbImageService { + + TbResourceInfo save(TbResource image, User user) throws Exception; + + TbResourceInfo save(TbResourceInfo imageInfo, TbResourceInfo oldImageInfo, User user); + + TbImageDeleteResult delete(TbResourceInfo imageInfo, User user, boolean force); + + String getETag(ImageCacheKey imageCacheKey); + + void putETag(ImageCacheKey imageCacheKey, String etag); + + void evictETags(ImageCacheKey imageCacheKey); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java index 40fe7dda9a..e6dbc0f071 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java @@ -15,14 +15,9 @@ */ package org.thingsboard.server.service.resource; -import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; -import org.thingsboard.server.common.data.TbResourceInfo; -import org.thingsboard.server.common.data.TbResourceInfoFilter; -import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.lwm2m.LwM2mObject; -import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.service.entitiy.SimpleTbEntityService; @@ -30,16 +25,6 @@ import java.util.List; public interface TbResourceService extends SimpleTbEntityService { - TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceKey); - - TbResource findResourceById(TenantId tenantId, TbResourceId resourceId); - - TbResourceInfo findResourceInfoById(TenantId tenantId, TbResourceId resourceId); - - PageData findAllTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink); - - PageData findTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink); - List findLwM2mObject(TenantId tenantId, String sortOrder, String sortProperty, @@ -50,7 +35,4 @@ public interface TbResourceService extends SimpleTbEntityService { String sortOrder, PageLink pageLink); - void deleteResourcesByTenantId(TenantId tenantId); - - long sumDataSizeByTenantId(TenantId tenantId); } diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java index bca7c7d81a..d421466963 100644 --- a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.rpc; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -27,10 +26,13 @@ import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.rpc.RpcError; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.common.msg.rpc.RemoveRpcActorMsg; +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequestActorMsg; import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; import org.thingsboard.server.dao.device.DeviceService; @@ -182,7 +184,7 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService { entityNode.put(DataConstants.ADDITIONAL_INFO, msg.getAdditionalInfo()); try { - TbMsg tbMsg = TbMsg.newMsg(DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE, msg.getDeviceId(), Optional.ofNullable(currentUser).map(User::getCustomerId).orElse(null), metaData, TbMsgDataType.JSON, JacksonUtil.toString(entityNode)); + TbMsg tbMsg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, msg.getDeviceId(), Optional.ofNullable(currentUser).map(User::getCustomerId).orElse(null), metaData, TbMsgDataType.JSON, JacksonUtil.toString(entityNode)); clusterService.pushMsgToRuleEngine(msg.getTenantId(), msg.getDeviceId(), tbMsg, null); } catch (IllegalArgumentException e) { throw new RuntimeException(e); diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java index 1fef6acbc0..2d854e5604 100644 --- a/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rpc.Rpc; import org.thingsboard.server.common.data.rpc.RpcError; import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequestActorMsg; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/FromDeviceRpcResponseActorMsg.java b/application/src/main/java/org/thingsboard/server/service/rpc/FromDeviceRpcResponseActorMsg.java deleted file mode 100644 index 2a3e30adcf..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/rpc/FromDeviceRpcResponseActorMsg.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.rpc; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.ToString; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.MsgType; -import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; -import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; - -@ToString -@RequiredArgsConstructor -public class FromDeviceRpcResponseActorMsg implements ToDeviceActorNotificationMsg { - - @Getter - private final Integer requestId; - @Getter - private final TenantId tenantId; - @Getter - private final DeviceId deviceId; - - @Getter - private final FromDeviceRpcResponse msg; - - @Override - public MsgType getMsgType() { - return MsgType.DEVICE_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG; - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/RemoveRpcActorMsg.java b/application/src/main/java/org/thingsboard/server/service/rpc/RemoveRpcActorMsg.java deleted file mode 100644 index c0b0c64b2d..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/rpc/RemoveRpcActorMsg.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.rpc; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.ToString; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.MsgType; -import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; - -import java.util.UUID; - -@ToString -@RequiredArgsConstructor -public class RemoveRpcActorMsg implements ToDeviceActorNotificationMsg { - - @Getter - private final TenantId tenantId; - @Getter - private final DeviceId deviceId; - - @Getter - private final UUID requestId; - - @Override - public MsgType getMsgType() { - return MsgType.REMOVE_RPC_TO_DEVICE_ACTOR_MSG; - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/RpcSubmitStrategy.java b/application/src/main/java/org/thingsboard/server/service/rpc/RpcSubmitStrategy.java new file mode 100644 index 0000000000..09da40d026 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/rpc/RpcSubmitStrategy.java @@ -0,0 +1,30 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.rpc; + +import java.util.Arrays; + +public enum RpcSubmitStrategy { + + BURST, SEQUENTIAL_ON_ACK_FROM_DEVICE, SEQUENTIAL_ON_RESPONSE_FROM_DEVICE; + + public static RpcSubmitStrategy parse(String strategyStr) { + return Arrays.stream(RpcSubmitStrategy.values()) + .filter(strategy -> strategy.name().equalsIgnoreCase(strategyStr)) + .findFirst() + .orElse(BURST); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/TbCoreDeviceRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/TbCoreDeviceRpcService.java index 9b81f4c6de..973d88d58a 100644 --- a/application/src/main/java/org/thingsboard/server/service/rpc/TbCoreDeviceRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/TbCoreDeviceRpcService.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.service.rpc; +import org.thingsboard.server.common.msg.rpc.RemoveRpcActorMsg; +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequestActorMsg; import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; import org.thingsboard.server.service.security.model.SecurityUser; diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java b/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java index ad40176cef..8c6f0d768c 100644 --- a/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java +++ b/application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java @@ -24,6 +24,7 @@ import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.RpcId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rpc.Rpc; @@ -62,7 +63,7 @@ public class TbRpcService { } private void pushRpcMsgToRuleEngine(TenantId tenantId, Rpc rpc) { - TbMsg msg = TbMsg.newMsg("RPC_" + rpc.getStatus().name(), rpc.getDeviceId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(rpc)); + TbMsg msg = TbMsg.newMsg(TbMsgType.valueOf("RPC_" + rpc.getStatus().name()), rpc.getDeviceId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(rpc)); tbClusterService.pushMsgToRuleEngine(tenantId, rpc.getDeviceId(), msg, null); } diff --git a/application/src/main/java/org/thingsboard/server/service/rpc/ToDeviceRpcRequestActorMsg.java b/application/src/main/java/org/thingsboard/server/service/rpc/ToDeviceRpcRequestActorMsg.java deleted file mode 100644 index 64c8887edb..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/rpc/ToDeviceRpcRequestActorMsg.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.rpc; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.ToString; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.MsgType; -import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; -import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; - -/** - * Created by ashvayka on 16.04.18. - */ -@ToString -@RequiredArgsConstructor -public class ToDeviceRpcRequestActorMsg implements ToDeviceActorNotificationMsg { - - private static final long serialVersionUID = -8592877558138716589L; - - @Getter - private final String serviceId; - @Getter - private final ToDeviceRpcRequest msg; - - @Override - public DeviceId getDeviceId() { - return msg.getDeviceId(); - } - - @Override - public TenantId getTenantId() { - return msg.getTenantId(); - } - - @Override - public MsgType getMsgType() { - return MsgType.DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG; - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java b/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java index c2e425e14a..22cdcdcd18 100644 --- a/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java +++ b/application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java @@ -26,7 +26,6 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; -import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.RuleChainId; @@ -45,9 +44,10 @@ import org.thingsboard.server.common.data.rule.RuleNodeUpdateResult; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import org.thingsboard.server.service.install.InstallScripts; -import org.thingsboard.server.service.sync.vc.EntitiesVersionControlService; +import org.thingsboard.server.utils.TbNodeUpgradeUtils; import java.util.ArrayList; import java.util.Collections; @@ -70,8 +70,7 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement private final RuleChainService ruleChainService; private final RelationService relationService; private final InstallScripts installScripts; - - private final EntitiesVersionControlService vcService; + private final ComponentDiscoveryService componentDiscoveryService; @Override public Set getRuleChainOutputLabels(TenantId tenantId, RuleChainId ruleChainId) { @@ -182,9 +181,7 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement tbClusterService.broadcastEntityStateChangeEvent(tenantId, savedRuleChain.getId(), actionType.equals(ActionType.ADDED) ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); } - boolean sendMsgToEdge = RuleChainType.EDGE.equals(savedRuleChain.getType()) && actionType.equals(ActionType.UPDATED); - notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedRuleChain.getId(), - savedRuleChain, user, actionType, sendMsgToEdge, null); + notificationEntityService.logEntityAction(tenantId, savedRuleChain.getId(), savedRuleChain, null, actionType, user); return savedRuleChain; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.RULE_CHAIN), ruleChain, actionType, user, e); @@ -201,11 +198,6 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement Set referencingRuleChainIds = referencingRuleNodes.stream().map(RuleNode::getRuleChainId).collect(Collectors.toSet()); - List relatedEdgeIds = null; - if (RuleChainType.EDGE.equals(ruleChain.getType())) { - relatedEdgeIds = edgeService.findAllRelatedEdgeIds(tenantId, ruleChainId); - } - ruleChainService.deleteRuleChainById(tenantId, ruleChainId); referencingRuleChainIds.remove(ruleChain.getId()); @@ -217,7 +209,7 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement tbClusterService.broadcastEntityStateChangeEvent(tenantId, ruleChain.getId(), ComponentLifecycleEvent.DELETED); } - notificationEntityService.notifyDeleteRuleChain(tenantId, ruleChain, relatedEdgeIds, user); + notificationEntityService.logEntityAction(tenantId, ruleChainId, ruleChain, null, ActionType.DELETED, user, ruleChainId.toString()); } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.RULE_CHAIN), ActionType.DELETED, user, e, ruleChainId.toString()); @@ -277,7 +269,7 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement RuleChainId ruleChainId = ruleChain.getId(); RuleChainId ruleChainMetaDataId = ruleChainMetaData.getRuleChainId(); try { - RuleChainUpdateResult result = ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData); + RuleChainUpdateResult result = ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData, this::updateRuleNodeConfiguration); checkNotNull(result.isSuccess() ? true : null); List updatedRuleChains; @@ -307,14 +299,8 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement notificationEntityService.logEntityAction(tenantId, ruleChainId, ruleChain, ActionType.UPDATED, user, ruleChainMetaData); - if (RuleChainType.EDGE.equals(ruleChain.getType())) { - notificationEntityService.notifySendMsgToEdgeService(tenantId, ruleChain.getId(), EdgeEventActionType.UPDATED); - } - for (RuleChain updatedRuleChain : updatedRuleChains) { - if (RuleChainType.EDGE.equals(ruleChain.getType())) { - notificationEntityService.notifySendMsgToEdgeService(tenantId, updatedRuleChain.getId(), EdgeEventActionType.UPDATED); - } else { + if (RuleChainType.CORE.equals(ruleChain.getType())) { RuleChainMetaData updatedRuleChainMetaData = checkNotNull(ruleChainService.loadRuleChainMetaData(tenantId, updatedRuleChain.getId())); notificationEntityService.logEntityAction(tenantId, updatedRuleChain.getId(), updatedRuleChain, ActionType.UPDATED, user, updatedRuleChainMetaData); @@ -330,34 +316,34 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement @Override public RuleChain assignRuleChainToEdge(TenantId tenantId, RuleChain ruleChain, Edge edge, User user) throws ThingsboardException { + ActionType actionType = ActionType.ASSIGNED_TO_EDGE; RuleChainId ruleChainId = ruleChain.getId(); EdgeId edgeId = edge.getId(); try { RuleChain savedRuleChain = checkNotNull(ruleChainService.assignRuleChainToEdge(tenantId, ruleChainId, edgeId)); - notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, ruleChainId, - null, edgeId, savedRuleChain, ActionType.ASSIGNED_TO_EDGE, + notificationEntityService.logEntityAction(tenantId, ruleChainId, savedRuleChain, null, actionType, user, ruleChainId.toString(), edgeId.toString(), edge.getName()); return savedRuleChain; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.RULE_CHAIN), - ActionType.ASSIGNED_TO_EDGE, user, e, ruleChainId.toString(), edgeId.toString()); + actionType, user, e, ruleChainId.toString(), edgeId.toString()); throw e; } } @Override public RuleChain unassignRuleChainFromEdge(TenantId tenantId, RuleChain ruleChain, Edge edge, User user) throws ThingsboardException { + ActionType actionType = ActionType.UNASSIGNED_FROM_EDGE; RuleChainId ruleChainId = ruleChain.getId(); EdgeId edgeId = edge.getId(); try { RuleChain savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(tenantId, ruleChainId, edgeId, false)); - notificationEntityService.notifyAssignOrUnassignEntityToEdge(tenantId, ruleChainId, - null, edgeId, savedRuleChain, ActionType.UNASSIGNED_FROM_EDGE, + notificationEntityService.logEntityAction(tenantId, ruleChainId, savedRuleChain, null, actionType, user, ruleChainId.toString(), edgeId.toString(), edge.getName()); return savedRuleChain; } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.RULE_CHAIN), - ActionType.UNASSIGNED_FROM_EDGE, user, e, ruleChainId, edgeId); + actionType, user, e, ruleChainId, edgeId); throw e; } } @@ -404,6 +390,35 @@ public class DefaultTbRuleChainService extends AbstractTbEntityService implement } } + @Override + public RuleNode updateRuleNodeConfiguration(RuleNode node) { + var ruleChainId = node.getRuleChainId(); + var ruleNodeId = node.getId(); + var ruleNodeType = node.getType(); + try { + var ruleNodeClass = componentDiscoveryService.getRuleNodeInfo(ruleNodeType) + .orElseThrow(() -> new RuntimeException("Rule node " + ruleNodeType + " is not supported!")); + if (ruleNodeClass.isVersioned()) { + int fromVersion = node.getConfigurationVersion(); + int toVersion = ruleNodeClass.getCurrentVersion(); + if (fromVersion < toVersion) { + log.debug("Going to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}", + ruleNodeId, ruleNodeType, fromVersion, toVersion); + TbNodeUpgradeUtils.upgradeConfigurationAndVersion(node, ruleNodeClass); + log.debug("Successfully upgrade rule node with id: {} type: {}, rule chain id: {} fromVersion: {} toVersion: {}", + ruleNodeId, ruleNodeType, ruleChainId, fromVersion, toVersion); + } else { + log.debug("Rule node with id: {} type: {} ruleChainId: {} already set to latest version!", + ruleNodeId, ruleChainId, ruleNodeType); + } + } + } catch (Exception e) { + log.error("Failed to upgrade rule node with id: {} type: {}, rule chain id: {}", + ruleNodeId, ruleNodeType, ruleChainId, e); + } + return node; + } + private Set updateRelatedRuleChains(TenantId tenantId, RuleChainId ruleChainId, Map labelsMap) { Set updatedRuleChains = new HashSet<>(); List usageList = getOutputLabelUsage(tenantId, ruleChainId); diff --git a/application/src/main/java/org/thingsboard/server/service/rule/TbRuleChainService.java b/application/src/main/java/org/thingsboard/server/service/rule/TbRuleChainService.java index 02b79544f4..17f1338bba 100644 --- a/application/src/main/java/org/thingsboard/server/service/rule/TbRuleChainService.java +++ b/application/src/main/java/org/thingsboard/server/service/rule/TbRuleChainService.java @@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainOutputLabelsUsage; import org.thingsboard.server.common.data.rule.RuleChainUpdateResult; +import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.service.entitiy.SimpleTbEntityService; import java.util.List; @@ -54,4 +55,6 @@ public interface TbRuleChainService extends SimpleTbEntityService { RuleChain setAutoAssignToEdgeRuleChain(TenantId tenantId, RuleChain ruleChain, User user) throws ThingsboardException; RuleChain unsetAutoAssignToEdgeRuleChain(TenantId tenantId, RuleChain ruleChain, User user) throws ThingsboardException; + + RuleNode updateRuleNodeConfiguration(RuleNode ruleNode); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/DefaultTokenOutdatingService.java b/application/src/main/java/org/thingsboard/server/service/security/auth/DefaultTokenOutdatingService.java index 9f33466793..02c712a640 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/DefaultTokenOutdatingService.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/DefaultTokenOutdatingService.java @@ -23,7 +23,6 @@ import org.thingsboard.server.cache.TbTransactionalCache; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.security.event.UserAuthDataChangedEvent; -import org.thingsboard.server.common.data.security.model.JwtToken; import org.thingsboard.server.service.security.model.token.JwtTokenFactory; import java.util.Optional; @@ -49,7 +48,7 @@ public class DefaultTokenOutdatingService implements TokenOutdatingService { } @Override - public boolean isOutdated(JwtToken token, UserId userId) { + public boolean isOutdated(String token, UserId userId) { Claims claims = tokenFactory.parseTokenClaims(token).getBody(); long issueTime = claims.getIssuedAt().getTime(); String sessionId = claims.get("sessionId", String.class); diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/TokenOutdatingService.java b/application/src/main/java/org/thingsboard/server/service/security/auth/TokenOutdatingService.java index 20df639619..f1d0c499ff 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/TokenOutdatingService.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/TokenOutdatingService.java @@ -16,10 +16,9 @@ package org.thingsboard.server.service.security.auth; import org.thingsboard.server.common.data.id.UserId; -import org.thingsboard.server.common.data.security.model.JwtToken; public interface TokenOutdatingService { - boolean isOutdated(JwtToken token, UserId userId); + boolean isOutdated(String token, UserId userId); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtAuthenticationProvider.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtAuthenticationProvider.java index e0242126fd..43389e2a09 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtAuthenticationProvider.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtAuthenticationProvider.java @@ -16,7 +16,9 @@ package org.thingsboard.server.service.security.auth.jwt; import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.stereotype.Component; @@ -37,13 +39,19 @@ public class JwtAuthenticationProvider implements AuthenticationProvider { @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { RawAccessJwtToken rawAccessToken = (RawAccessJwtToken) authentication.getCredentials(); - SecurityUser securityUser = tokenFactory.parseAccessJwtToken(rawAccessToken); + SecurityUser securityUser = authenticate(rawAccessToken.getToken()); + return new JwtAuthenticationToken(securityUser); + } - if (tokenOutdatingService.isOutdated(rawAccessToken, securityUser.getId())) { + public SecurityUser authenticate(String accessToken) throws AuthenticationException { + if (StringUtils.isEmpty(accessToken)) { + throw new BadCredentialsException("Token is invalid"); + } + SecurityUser securityUser = tokenFactory.parseAccessJwtToken(accessToken); + if (tokenOutdatingService.isOutdated(accessToken, securityUser.getId())) { throw new JwtExpiredTokenException("Token is outdated"); } - - return new JwtAuthenticationToken(securityUser); + return securityUser; } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenAuthenticationProvider.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenAuthenticationProvider.java index e75d3aa4d1..527ff97ba5 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenAuthenticationProvider.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenAuthenticationProvider.java @@ -57,7 +57,7 @@ public class RefreshTokenAuthenticationProvider implements AuthenticationProvide public Authentication authenticate(Authentication authentication) throws AuthenticationException { Assert.notNull(authentication, "No authentication data provided"); RawAccessJwtToken rawAccessToken = (RawAccessJwtToken) authentication.getCredentials(); - SecurityUser unsafeUser = tokenFactory.parseRefreshToken(rawAccessToken); + SecurityUser unsafeUser = tokenFactory.parseRefreshToken(rawAccessToken.getToken()); UserPrincipal principal = unsafeUser.getUserPrincipal(); SecurityUser securityUser; @@ -67,7 +67,7 @@ public class RefreshTokenAuthenticationProvider implements AuthenticationProvide securityUser = authenticateByPublicId(principal.getValue()); } securityUser.setSessionId(unsafeUser.getSessionId()); - if (tokenOutdatingService.isOutdated(rawAccessToken, securityUser.getId())) { + if (tokenOutdatingService.isOutdated(rawAccessToken.getToken(), securityUser.getId())) { throw new CredentialsExpiredException("Token is outdated"); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/DefaultJwtSettingsService.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/DefaultJwtSettingsService.java index d159c469c0..d350a86f91 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/DefaultJwtSettingsService.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/settings/DefaultJwtSettingsService.java @@ -22,11 +22,14 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.NotificationCenter; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.targets.platform.SystemAdministratorsFilter; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.security.model.JwtSettings; +import org.thingsboard.server.dao.notification.DefaultNotifications; import org.thingsboard.server.dao.settings.AdminSettingsService; import java.nio.charset.StandardCharsets; @@ -43,6 +46,7 @@ public class DefaultJwtSettingsService implements JwtSettingsService { private final AdminSettingsService adminSettingsService; @Lazy private final Optional tbClusterService; + private final Optional notificationCenter; private final JwtSettingsValidator jwtSettingsValidator; @Value("${security.jwt.tokenExpirationTime:9000}") @@ -124,6 +128,9 @@ public class DefaultJwtSettingsService implements JwtSettingsService { log.warn("WARNING: The platform is configured to use default JWT Signing Key. " + "This is a security issue that needs to be resolved. Please change the JWT Signing Key using the Web UI. " + "Navigate to \"System settings -> Security settings\" while logged in as a System Administrator."); + notificationCenter.ifPresent(notificationCenter -> { + notificationCenter.sendGeneralWebNotification(TenantId.SYS_TENANT_ID, new SystemAdministratorsFilter(), DefaultNotifications.jwtSigningKeyIssue.toTemplate()); + }); } this.jwtSettings = result; } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java index a58c801583..08717986bf 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java @@ -31,7 +31,7 @@ import org.thingsboard.server.common.data.security.model.mfa.account.TwoFaAccoun import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderConfig; import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType; import org.thingsboard.server.dao.user.UserService; -import org.thingsboard.server.dao.util.limits.LimitedApi; +import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.dao.util.limits.RateLimitService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.auth.mfa.config.TwoFaConfigManager; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/HttpCookieOAuth2AuthorizationRequestRepository.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/HttpCookieOAuth2AuthorizationRequestRepository.java index d63f759dd3..98ccb2c1be 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/HttpCookieOAuth2AuthorizationRequestRepository.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/HttpCookieOAuth2AuthorizationRequestRepository.java @@ -18,11 +18,13 @@ package org.thingsboard.server.service.security.auth.oauth2; import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; import org.springframework.stereotype.Component; +import org.thingsboard.server.queue.util.TbCoreComponent; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component +@TbCoreComponent public class HttpCookieOAuth2AuthorizationRequestRepository implements AuthorizationRequestRepository { public static final String OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME = "oauth2_auth_request"; public static final String PREV_URI_PARAMETER = "prevUri"; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationProvider.java b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationProvider.java index 7d612fef25..1b28457792 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationProvider.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationProvider.java @@ -36,11 +36,15 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; +import org.thingsboard.server.common.data.security.model.SecuritySettings; +import org.thingsboard.server.common.data.security.model.UserPasswordPolicy; import org.thingsboard.server.dao.customer.CustomerService; +import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.auth.MfaAuthenticationToken; import org.thingsboard.server.service.security.auth.mfa.TwoFactorAuthService; +import org.thingsboard.server.service.security.exception.UserPasswordNotValidException; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.UserPrincipal; import org.thingsboard.server.service.security.system.SystemSecurityService; @@ -83,6 +87,17 @@ public class RestAuthenticationProvider implements AuthenticationProvider { if (userPrincipal.getType() == UserPrincipal.Type.USER_NAME) { String username = userPrincipal.getValue(); String password = (String) authentication.getCredentials(); + + SecuritySettings securitySettings = systemSecurityService.getSecuritySettings(); + UserPasswordPolicy passwordPolicy = securitySettings.getPasswordPolicy(); + if (Boolean.TRUE.equals(passwordPolicy.getForceUserToResetPasswordIfNotValid())) { + try { + systemSecurityService.validatePasswordByPolicy(password, passwordPolicy); + } catch (DataValidationException e) { + throw new UserPasswordNotValidException("The entered password violates our policies. If this is your real password, please reset it."); + } + } + securityUser = authenticateByUsernameAndPassword(authentication, userPrincipal, username, password); if (twoFactorAuthService.isTwoFaEnabled(securityUser.getTenantId(), securityUser.getId())) { return new MfaAuthenticationToken(securityUser); diff --git a/application/src/main/java/org/thingsboard/server/service/security/exception/JwtExpiredTokenException.java b/application/src/main/java/org/thingsboard/server/service/security/exception/JwtExpiredTokenException.java index 87b7403a85..4e4430ceaa 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/exception/JwtExpiredTokenException.java +++ b/application/src/main/java/org/thingsboard/server/service/security/exception/JwtExpiredTokenException.java @@ -16,23 +16,22 @@ package org.thingsboard.server.service.security.exception; import org.springframework.security.core.AuthenticationException; -import org.thingsboard.server.common.data.security.model.JwtToken; public class JwtExpiredTokenException extends AuthenticationException { private static final long serialVersionUID = -5959543783324224864L; - private JwtToken token; + private String token; public JwtExpiredTokenException(String msg) { super(msg); } - public JwtExpiredTokenException(JwtToken token, String msg, Throwable t) { + public JwtExpiredTokenException(String token, String msg, Throwable t) { super(msg, t); this.token = token; } public String token() { - return this.token.getToken(); + return this.token; } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/exception/UserPasswordNotValidException.java b/application/src/main/java/org/thingsboard/server/service/security/exception/UserPasswordNotValidException.java new file mode 100644 index 0000000000..2c1488bcbf --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/security/exception/UserPasswordNotValidException.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.security.exception; + +import org.springframework.security.authentication.AccountStatusException; + +public class UserPasswordNotValidException extends AccountStatusException { + + public UserPasswordNotValidException(String msg) { + super(msg); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java b/application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java index a222a7e83a..a9d32c4d95 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java +++ b/application/src/main/java/org/thingsboard/server/service/security/model/token/JwtTokenFactory.java @@ -93,8 +93,8 @@ public class JwtTokenFactory { return new AccessJwtToken(token); } - public SecurityUser parseAccessJwtToken(RawAccessJwtToken rawAccessToken) { - Jws jwsClaims = parseTokenClaims(rawAccessToken); + public SecurityUser parseAccessJwtToken(String token) { + Jws jwsClaims = parseTokenClaims(token); Claims claims = jwsClaims.getBody(); String subject = claims.getSubject(); @SuppressWarnings("unchecked") @@ -145,8 +145,8 @@ public class JwtTokenFactory { return new AccessJwtToken(token); } - public SecurityUser parseRefreshToken(RawAccessJwtToken rawAccessToken) { - Jws jwsClaims = parseTokenClaims(rawAccessToken); + public SecurityUser parseRefreshToken(String token) { + Jws jwsClaims = parseTokenClaims(token); Claims claims = jwsClaims.getBody(); String subject = claims.getSubject(); @SuppressWarnings("unchecked") @@ -200,11 +200,11 @@ public class JwtTokenFactory { .signWith(SignatureAlgorithm.HS512, jwtSettingsService.getJwtSettings().getTokenSigningKey()); } - public Jws parseTokenClaims(JwtToken token) { + public Jws parseTokenClaims(String token) { try { return Jwts.parser() .setSigningKey(jwtSettingsService.getJwtSettings().getTokenSigningKey()) - .parseClaimsJws(token.getToken()); + .parseClaimsJws(token); } catch (UnsupportedJwtException | MalformedJwtException | IllegalArgumentException ex) { log.debug("Invalid JWT Token", ex); throw new BadCredentialsException("Invalid JWT token: ", ex); diff --git a/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java b/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java index 726a5d98f0..36e580c087 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java +++ b/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java @@ -55,9 +55,9 @@ import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.user.UserServiceImpl; -import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails; import org.thingsboard.server.service.security.exception.UserPasswordExpiredException; +import org.thingsboard.server.service.security.exception.UserPasswordNotValidException; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.utils.MiscUtils; import ua_parser.Client; @@ -73,7 +73,6 @@ import static org.thingsboard.server.common.data.CacheConstants.SECURITY_SETTING @Service @Slf4j -@TbCoreComponent public class DefaultSystemSecurityService implements SystemSecurityService { @Autowired @@ -96,9 +95,9 @@ public class DefaultSystemSecurityService implements SystemSecurityService { @Cacheable(cacheNames = SECURITY_SETTINGS_CACHE, key = "'securitySettings'") @Override - public SecuritySettings getSecuritySettings(TenantId tenantId) { + public SecuritySettings getSecuritySettings() { SecuritySettings securitySettings = null; - AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(tenantId, "securitySettings"); + AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "securitySettings"); if (adminSettings != null) { try { securitySettings = JacksonUtil.convertValue(adminSettings.getJsonValue(), SecuritySettings.class); @@ -109,21 +108,22 @@ public class DefaultSystemSecurityService implements SystemSecurityService { securitySettings = new SecuritySettings(); securitySettings.setPasswordPolicy(new UserPasswordPolicy()); securitySettings.getPasswordPolicy().setMinimumLength(6); + securitySettings.getPasswordPolicy().setMaximumLength(72); } return securitySettings; } @CacheEvict(cacheNames = SECURITY_SETTINGS_CACHE, key = "'securitySettings'") @Override - public SecuritySettings saveSecuritySettings(TenantId tenantId, SecuritySettings securitySettings) { - AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(tenantId, "securitySettings"); + public SecuritySettings saveSecuritySettings(SecuritySettings securitySettings) { + AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "securitySettings"); if (adminSettings == null) { adminSettings = new AdminSettings(); - adminSettings.setTenantId(tenantId); + adminSettings.setTenantId(TenantId.SYS_TENANT_ID); adminSettings.setKey("securitySettings"); } adminSettings.setJsonValue(JacksonUtil.valueToTree(securitySettings)); - AdminSettings savedAdminSettings = adminSettingsService.saveAdminSettings(tenantId, adminSettings); + AdminSettings savedAdminSettings = adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings); try { return JacksonUtil.convertValue(savedAdminSettings.getJsonValue(), SecuritySettings.class); } catch (Exception e) { @@ -135,7 +135,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService { public void validateUserCredentials(TenantId tenantId, UserCredentials userCredentials, String username, String password) throws AuthenticationException { if (!encoder.matches(password, userCredentials.getPassword())) { int failedLoginAttempts = userService.increaseFailedLoginAttempts(tenantId, userCredentials.getUserId()); - SecuritySettings securitySettings = self.getSecuritySettings(tenantId); + SecuritySettings securitySettings = self.getSecuritySettings(); if (securitySettings.getMaxFailedLoginAttempts() != null && securitySettings.getMaxFailedLoginAttempts() > 0) { if (failedLoginAttempts > securitySettings.getMaxFailedLoginAttempts() && userCredentials.isEnabled()) { lockAccount(userCredentials.getUserId(), username, securitySettings.getUserLockoutNotificationEmail(), securitySettings.getMaxFailedLoginAttempts()); @@ -151,7 +151,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService { userService.resetFailedLoginAttempts(tenantId, userCredentials.getUserId()); - SecuritySettings securitySettings = self.getSecuritySettings(tenantId); + SecuritySettings securitySettings = self.getSecuritySettings(); if (isPositiveInteger(securitySettings.getPasswordPolicy().getPasswordExpirationPeriodDays())) { if ((userCredentials.getCreatedTime() + TimeUnit.DAYS.toMillis(securitySettings.getPasswordPolicy().getPasswordExpirationPeriodDays())) @@ -179,7 +179,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService { if (maxVerificationFailures != null && maxVerificationFailures > 0 && failedVerificationAttempts >= maxVerificationFailures) { userService.setUserCredentialsEnabled(TenantId.SYS_TENANT_ID, userId, false); - SecuritySettings securitySettings = self.getSecuritySettings(tenantId); + SecuritySettings securitySettings = self.getSecuritySettings(); lockAccount(userId, securityUser.getEmail(), securitySettings.getUserLockoutNotificationEmail(), maxVerificationFailures); throw new LockedException("User account was locked due to exceeded 2FA verification attempts"); } @@ -197,12 +197,36 @@ public class DefaultSystemSecurityService implements SystemSecurityService { } @Override - public void validatePassword(TenantId tenantId, String password, UserCredentials userCredentials) throws DataValidationException { - SecuritySettings securitySettings = self.getSecuritySettings(tenantId); + public void validatePassword(String password, UserCredentials userCredentials) throws DataValidationException { + SecuritySettings securitySettings = self.getSecuritySettings(); UserPasswordPolicy passwordPolicy = securitySettings.getPasswordPolicy(); + validatePasswordByPolicy(password, passwordPolicy); + + if (userCredentials != null && isPositiveInteger(passwordPolicy.getPasswordReuseFrequencyDays())) { + long passwordReuseFrequencyTs = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(passwordPolicy.getPasswordReuseFrequencyDays()); + JsonNode additionalInfo = userCredentials.getAdditionalInfo(); + if (additionalInfo instanceof ObjectNode && additionalInfo.has(UserServiceImpl.USER_PASSWORD_HISTORY)) { + JsonNode userPasswordHistoryJson = additionalInfo.get(UserServiceImpl.USER_PASSWORD_HISTORY); + Map userPasswordHistoryMap = JacksonUtil.convertValue(userPasswordHistoryJson, new TypeReference<>() {}); + for (Map.Entry entry : userPasswordHistoryMap.entrySet()) { + if (encoder.matches(password, entry.getValue()) && Long.parseLong(entry.getKey()) > passwordReuseFrequencyTs) { + throw new DataValidationException("Password was already used for the last " + passwordPolicy.getPasswordReuseFrequencyDays() + " days"); + } + } + } + } + } + + @Override + public void validatePasswordByPolicy(String password, UserPasswordPolicy passwordPolicy) { List passwordRules = new ArrayList<>(); - passwordRules.add(new LengthRule(passwordPolicy.getMinimumLength(), Integer.MAX_VALUE)); + + Integer maximumLength = passwordPolicy.getMaximumLength(); + Integer minLengthBound = passwordPolicy.getMinimumLength(); + int maxLengthBound = (maximumLength != null && maximumLength > passwordPolicy.getMinimumLength()) ? maximumLength : Integer.MAX_VALUE; + + passwordRules.add(new LengthRule(minLengthBound, maxLengthBound)); if (isPositiveInteger(passwordPolicy.getMinimumUppercaseLetters())) { passwordRules.add(new CharacterRule(EnglishCharacterData.UpperCase, passwordPolicy.getMinimumUppercaseLetters())); } @@ -225,21 +249,6 @@ public class DefaultSystemSecurityService implements SystemSecurityService { String message = String.join("\n", validator.getMessages(result)); throw new DataValidationException(message); } - - if (userCredentials != null && isPositiveInteger(passwordPolicy.getPasswordReuseFrequencyDays())) { - long passwordReuseFrequencyTs = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(passwordPolicy.getPasswordReuseFrequencyDays()); - JsonNode additionalInfo = userCredentials.getAdditionalInfo(); - if (additionalInfo instanceof ObjectNode && additionalInfo.has(UserServiceImpl.USER_PASSWORD_HISTORY)) { - JsonNode userPasswordHistoryJson = additionalInfo.get(UserServiceImpl.USER_PASSWORD_HISTORY); - Map userPasswordHistoryMap = JacksonUtil.convertValue(userPasswordHistoryJson, new TypeReference<>() {}); - for (Map.Entry entry : userPasswordHistoryMap.entrySet()) { - if (encoder.matches(password, entry.getValue()) && Long.parseLong(entry.getKey()) > passwordReuseFrequencyTs) { - throw new DataValidationException("Password was already used for the last " + passwordPolicy.getPasswordReuseFrequencyDays() + " days"); - } - } - - } - } } @Override @@ -249,11 +258,11 @@ public class DefaultSystemSecurityService implements SystemSecurityService { JsonNode prohibitDifferentUrl = generalSettings.getJsonValue().get("prohibitDifferentUrl"); - if (prohibitDifferentUrl != null && prohibitDifferentUrl.asBoolean()) { + if ((prohibitDifferentUrl != null && prohibitDifferentUrl.asBoolean()) || httpServletRequest == null) { baseUrl = generalSettings.getJsonValue().get("baseUrl").asText(); } - if (StringUtils.isEmpty(baseUrl)) { + if (StringUtils.isEmpty(baseUrl) && httpServletRequest != null) { baseUrl = MiscUtils.constructBaseUrl(httpServletRequest); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/system/SystemSecurityService.java b/application/src/main/java/org/thingsboard/server/service/security/system/SystemSecurityService.java index 64550c438e..103a874bd7 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/system/SystemSecurityService.java +++ b/application/src/main/java/org/thingsboard/server/service/security/system/SystemSecurityService.java @@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.security.model.SecuritySettings; +import org.thingsboard.server.common.data.security.model.UserPasswordPolicy; import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettings; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.service.security.model.SecurityUser; @@ -30,15 +31,17 @@ import javax.servlet.http.HttpServletRequest; public interface SystemSecurityService { - SecuritySettings getSecuritySettings(TenantId tenantId); + SecuritySettings getSecuritySettings(); - SecuritySettings saveSecuritySettings(TenantId tenantId, SecuritySettings securitySettings); + SecuritySettings saveSecuritySettings(SecuritySettings securitySettings); + + void validatePasswordByPolicy(String password, UserPasswordPolicy passwordPolicy); void validateUserCredentials(TenantId tenantId, UserCredentials userCredentials, String username, String password) throws AuthenticationException; void validateTwoFaVerification(SecurityUser securityUser, boolean verificationSuccess, PlatformTwoFaSettings twoFaSettings); - void validatePassword(TenantId tenantId, String password, UserCredentials userCredentials) throws DataValidationException; + void validatePassword(String password, UserCredentials userCredentials) throws DataValidationException; String getBaseUrl(TenantId tenantId, CustomerId customerId, HttpServletRequest httpServletRequest); diff --git a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java index 337d607fac..41a41f5e82 100644 --- a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java @@ -36,7 +36,6 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.ApiUsageRecordKey; -import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceIdInfo; import org.thingsboard.server.common.data.EntityType; @@ -51,6 +50,8 @@ import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.notification.rule.trigger.DeviceActivityTrigger; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.common.data.query.EntityData; @@ -63,7 +64,6 @@ import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; -import org.thingsboard.server.common.msg.notification.trigger.DeviceActivityTrigger; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; @@ -102,10 +102,7 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; -import static org.thingsboard.server.common.data.DataConstants.ACTIVITY_EVENT; -import static org.thingsboard.server.common.data.DataConstants.CONNECT_EVENT; -import static org.thingsboard.server.common.data.DataConstants.DISCONNECT_EVENT; -import static org.thingsboard.server.common.data.DataConstants.INACTIVITY_EVENT; +import static org.thingsboard.server.common.data.DataConstants.SCOPE; import static org.thingsboard.server.common.data.DataConstants.SERVER_SCOPE; /** @@ -226,12 +223,11 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService= deviceState.getLastActivityTime()) { + deviceState.setLastInactivityAlarmTime(0L); + save(deviceId, INACTIVITY_ALARM_TIME, 0L); + } } } } @@ -424,7 +429,7 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService { log.debug("Calculating state updates. tpi {} for {} devices", tpi.getFullTopicName(), deviceIds.size()); Set idsFromRemovedTenant = new HashSet<>(); @@ -454,7 +459,7 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService> stats = new HashMap<>(); for (DeviceStateData stateData : deviceStates.values()) { Pair tenantDevicesActivity = stats.computeIfAbsent(stateData.getTenantId(), @@ -485,8 +490,8 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService> tsData = tsService.findLatest(TenantId.SYS_TENANT_ID, device.getId(), PERSISTENT_ATTRIBUTES); future = Futures.transform(tsData, extractDeviceStateData(device), deviceStateExecutor); } else { - ListenableFuture> attrData = attributesService.find(TenantId.SYS_TENANT_ID, device.getId(), DataConstants.SERVER_SCOPE, PERSISTENT_ATTRIBUTES); + ListenableFuture> attrData = attributesService.find(TenantId.SYS_TENANT_ID, device.getId(), SERVER_SCOPE, PERSISTENT_ATTRIBUTES); future = Futures.transform(attrData, extractDeviceStateData(device), deviceStateExecutor); } return transformInactivityTimeout(future); @@ -771,11 +776,11 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService(deviceId, key, value)); } else { - tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, DataConstants.SERVER_SCOPE, key, value, new TelemetrySaveCallback<>(deviceId, key, value)); + tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, SERVER_SCOPE, key, value, new TelemetrySaveCallback<>(deviceId, key, value)); } } @@ -808,13 +813,17 @@ public class DefaultDeviceStateService extends AbstractPartitionBasedService(deviceId, key, value)); } else { - tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, DataConstants.SERVER_SCOPE, key, value, new TelemetrySaveCallback<>(deviceId, key, value)); + tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, SERVER_SCOPE, key, value, new TelemetrySaveCallback<>(deviceId, key, value)); } } + long getCurrentTimeMillis() { + return System.currentTimeMillis(); + } + private static class TelemetrySaveCallback implements FutureCallback { private final DeviceId deviceId; private final String key; diff --git a/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java b/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java index 1b5bfd56d1..d8d86922cf 100644 --- a/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java +++ b/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java @@ -17,7 +17,9 @@ package org.thingsboard.server.service.stats; import com.google.common.util.concurrent.FutureCallback; import lombok.Data; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.AssetId; @@ -26,7 +28,9 @@ import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.JsonDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.usagerecord.ApiLimitService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.util.TbRuleEngineComponent; import org.thingsboard.server.service.queue.TbRuleEngineConsumerStats; @@ -37,6 +41,7 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; @@ -44,9 +49,11 @@ import java.util.stream.Collectors; @TbRuleEngineComponent @Service @Slf4j +@RequiredArgsConstructor public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsService { public static final String TB_SERVICE_QUEUE = "TbServiceQueue"; + public static final String RULE_ENGINE_EXCEPTION = "ruleEngineException"; public static final FutureCallback CALLBACK = new FutureCallback() { @Override public void onSuccess(@Nullable Integer result) { @@ -61,16 +68,13 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS private final TbServiceInfoProvider serviceInfoProvider; private final TelemetrySubscriptionService tsService; - private final Lock lock = new ReentrantLock(); private final AssetService assetService; - private final ConcurrentMap tenantQueueAssets; + private final ApiLimitService apiLimitService; + private final Lock lock = new ReentrantLock(); + private final ConcurrentMap tenantQueueAssets = new ConcurrentHashMap<>(); - public DefaultRuleEngineStatisticsService(TelemetrySubscriptionService tsService, TbServiceInfoProvider serviceInfoProvider, AssetService assetService) { - this.tsService = tsService; - this.serviceInfoProvider = serviceInfoProvider; - this.assetService = assetService; - this.tenantQueueAssets = new ConcurrentHashMap<>(); - } + @Value("${queue.rule-engine.stats.max-error-message-length:4096}") + private int maxErrorMessageLength; @Override public void reportQueueStats(long ts, TbRuleEngineConsumerStats ruleEngineStats) { @@ -84,7 +88,9 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS .map(kv -> new BasicTsKvEntry(ts, new LongDataEntry(kv.getKey(), (long) kv.getValue().get()))) .collect(Collectors.toList()); if (!tsList.isEmpty()) { - tsService.saveAndNotifyInternal(tenantId, serviceAssetId, tsList, CALLBACK); + long ttl = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getQueueStatsTtlDays); + ttl = TimeUnit.DAYS.toSeconds(ttl); + tsService.saveAndNotifyInternal(tenantId, serviceAssetId, tsList, ttl, CALLBACK); } } } catch (Exception e) { @@ -95,8 +101,10 @@ public class DefaultRuleEngineStatisticsService implements RuleEngineStatisticsS }); ruleEngineStats.getTenantExceptions().forEach((tenantId, e) -> { try { - TsKvEntry tsKv = new BasicTsKvEntry(e.getTs(), new JsonDataEntry("ruleEngineException", e.toJsonString())); - tsService.saveAndNotifyInternal(tenantId, getServiceAssetId(tenantId, queueName), Collections.singletonList(tsKv), CALLBACK); + TsKvEntry tsKv = new BasicTsKvEntry(e.getTs(), new JsonDataEntry(RULE_ENGINE_EXCEPTION, e.toJsonString(maxErrorMessageLength))); + long ttl = apiLimitService.getLimit(tenantId, DefaultTenantProfileConfiguration::getRuleEngineExceptionsTtlDays); + ttl = TimeUnit.DAYS.toSeconds(ttl); + tsService.saveAndNotifyInternal(tenantId, getServiceAssetId(tenantId, queueName), Collections.singletonList(tsKv), ttl, CALLBACK); } catch (Exception e2) { if (!"Asset is referencing to non-existent tenant!".equalsIgnoreCase(e2.getMessage())) { log.debug("[{}] Failed to store the statistics", tenantId, e2); diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java index 60fce13560..b04291a303 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java @@ -17,11 +17,9 @@ package org.thingsboard.server.service.subscription; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; -import org.thingsboard.common.util.DonAsynchron; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.common.util.ThingsBoardThreadFactory; -import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceAttributesEventNotificationMsg; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityType; @@ -30,57 +28,42 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; -import org.thingsboard.server.common.data.kv.Aggregation; import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.KvEntry; -import org.thingsboard.server.common.data.kv.ReadTsKvQuery; import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; -import org.thingsboard.server.dao.attributes.AttributesService; -import org.thingsboard.server.dao.timeseries.TimeseriesService; -import org.thingsboard.server.gen.transport.TransportProtos.LocalSubscriptionServiceMsgProto; -import org.thingsboard.server.gen.transport.TransportProtos.TbAlarmSubscriptionUpdateProto; -import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdateProto; -import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdateTsValue; -import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdateValueListProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.TbApplicationEventListener; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; +import org.thingsboard.server.queue.discovery.event.OtherServiceShutdownEvent; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.state.DefaultDeviceStateService; import org.thingsboard.server.service.state.DeviceStateService; -import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; -import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate; -import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; -import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.function.Function; -import java.util.function.Predicate; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; @Slf4j @TbCoreComponent @@ -88,135 +71,96 @@ import java.util.function.Predicate; @RequiredArgsConstructor public class DefaultSubscriptionManagerService extends TbApplicationEventListener implements SubscriptionManagerService { - private final AttributesService attrService; - private final TimeseriesService tsService; - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final PartitionService partitionService; private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueProducerProvider producerProvider; private final TbLocalSubscriptionService localSubscriptionService; private final DeviceStateService deviceStateService; private final TbClusterService clusterService; + private final SubscriptionSchedulerComponent scheduler; - private final Map> subscriptionsByEntityId = new ConcurrentHashMap<>(); - private final Map> subscriptionsByWsSessionId = new ConcurrentHashMap<>(); - private final ConcurrentMap> partitionedSubscriptions = new ConcurrentHashMap<>(); - private final Set currentPartitions = ConcurrentHashMap.newKeySet(); + private final Lock subsLock = new ReentrantLock(); + private final ConcurrentMap entitySubscriptions = new ConcurrentHashMap<>(); + + private final ConcurrentMap entityUpdates = new ConcurrentHashMap<>(); - private ExecutorService tsCallBackExecutor; private String serviceId; private TbQueueProducer> toCoreNotificationsProducer; + private long initTs; + @PostConstruct public void initExecutor() { - tsCallBackExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("ts-sub-callback")); serviceId = serviceInfoProvider.getServiceId(); + initTs = System.currentTimeMillis(); toCoreNotificationsProducer = producerProvider.getTbCoreNotificationsMsgProducer(); - } - - @PreDestroy - public void shutdownExecutor() { - if (tsCallBackExecutor != null) { - tsCallBackExecutor.shutdownNow(); - } + scheduler.scheduleWithFixedDelay(this::cleanupEntityUpdates, 1, 1, TimeUnit.HOURS); } @Override - public void addSubscription(TbSubscription subscription, TbCallback callback) { - log.trace("[{}][{}][{}] Registering subscription for entity [{}]", - subscription.getServiceId(), subscription.getSessionId(), subscription.getSubscriptionId(), subscription.getEntityId()); - TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, subscription.getTenantId(), subscription.getEntityId()); - if (currentPartitions.contains(tpi)) { - partitionedSubscriptions.computeIfAbsent(tpi, k -> ConcurrentHashMap.newKeySet()).add(subscription); + public void onSubEvent(String serviceId, TbEntitySubEvent event, TbCallback callback) { + var tenantId = event.getTenantId(); + var entityId = event.getEntityId(); + log.trace("[{}][{}][{}] Processing subscription event {}", tenantId, entityId, serviceId, event); + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); + if (tpi.isMyPartition()) { + subsLock.lock(); + try { + var entitySubs = entitySubscriptions.computeIfAbsent(entityId, id -> new TbEntityRemoteSubsInfo(tenantId, entityId)); + boolean empty = entitySubs.updateAndCheckIsEmpty(serviceId, event); + if (empty) { + entitySubscriptions.remove(entityId); + } + } finally { + subsLock.unlock(); + } callback.onSuccess(); + if (event.hasTsOrAttrSub()) { + sendSubEventCallback(serviceId, entityId, event.getSeqNumber()); + } } else { - log.warn("[{}][{}] Entity belongs to external partition. Probably rebalancing is in progress. Topic: {}" - , subscription.getTenantId(), subscription.getEntityId(), tpi.getFullTopicName()); + log.warn("[{}][{}][{}] Event belongs to external partition. Probably re-balancing is in progress. Topic: {}" + , tenantId, entityId, serviceId, tpi.getFullTopicName()); callback.onFailure(new RuntimeException("Entity belongs to external partition " + tpi.getFullTopicName() + "!")); } - boolean newSubscription = subscriptionsByEntityId - .computeIfAbsent(subscription.getEntityId(), k -> ConcurrentHashMap.newKeySet()).add(subscription); - subscriptionsByWsSessionId.computeIfAbsent(subscription.getSessionId(), k -> new ConcurrentHashMap<>()).put(subscription.getSubscriptionId(), subscription); - if (newSubscription) { - switch (subscription.getType()) { - case TIMESERIES: - handleNewTelemetrySubscription((TbTimeseriesSubscription) subscription); - break; - case ATTRIBUTES: - handleNewAttributeSubscription((TbAttributeSubscription) subscription); - break; - case ALARMS: - handleNewAlarmsSubscription((TbAlarmsSubscription) subscription); - break; - } - } } @Override - public void cancelSubscription(String sessionId, int subscriptionId, TbCallback callback) { - log.debug("[{}][{}] Going to remove subscription.", sessionId, subscriptionId); - Map sessionSubscriptions = subscriptionsByWsSessionId.get(sessionId); - if (sessionSubscriptions != null) { - TbSubscription subscription = sessionSubscriptions.remove(subscriptionId); - if (subscription != null) { - removeSubscriptionFromEntityMap(subscription); - removeSubscriptionFromPartitionMap(subscription); - if (sessionSubscriptions.isEmpty()) { - subscriptionsByWsSessionId.remove(sessionId); - } - } else { - log.debug("[{}][{}] Subscription not found!", sessionId, subscriptionId); + @EventListener(OtherServiceShutdownEvent.class) + public void onApplicationEvent(OtherServiceShutdownEvent event) { + if (event.getServiceTypes() != null && event.getServiceTypes().contains(ServiceType.TB_CORE)) { + subsLock.lock(); + try { + int sizeBeforeCleanup = entitySubscriptions.size(); + entitySubscriptions.entrySet().removeIf(kv -> kv.getValue().removeAndCheckIsEmpty(event.getServiceId())); + log.info("[{}][{}] Removed {} entity subscription records due to server shutdown.", serviceId, event.getServiceId(), entitySubscriptions.size() - sizeBeforeCleanup); + } finally { + subsLock.unlock(); } + } + } + + private void sendSubEventCallback(String targetId, EntityId entityId, int seqNumber) { + var update = getEntityUpdatesInfo(entityId); + if (serviceId.equals(targetId)) { + localSubscriptionService.onSubEventCallback(entityId, seqNumber, update, TbCallback.EMPTY); } else { - log.debug("[{}] No session subscriptions found!", sessionId); + sendCoreNotification(targetId, entityId, TbSubscriptionUtils.toProto(entityId.getId(), seqNumber, update)); } - callback.onSuccess(); } @Override protected void onTbApplicationEvent(PartitionChangeEvent partitionChangeEvent) { if (ServiceType.TB_CORE.equals(partitionChangeEvent.getServiceType())) { - Set removedPartitions = new HashSet<>(currentPartitions); - removedPartitions.removeAll(partitionChangeEvent.getPartitions()); - - currentPartitions.clear(); - currentPartitions.addAll(partitionChangeEvent.getPartitions()); - - // We no longer manage current partition of devices; - removedPartitions.forEach(partition -> { - Set subs = partitionedSubscriptions.remove(partition); - if (subs != null) { - subs.forEach(sub -> { - if (!serviceId.equals(sub.getServiceId())) { - removeSubscriptionFromEntityMap(sub); - } - }); - } - }); + entitySubscriptions.values().removeIf(sub -> + !partitionService.resolve(ServiceType.TB_CORE, sub.getTenantId(), sub.getEntityId()).isMyPartition()); } } @Override public void onTimeSeriesUpdate(TenantId tenantId, EntityId entityId, List ts, TbCallback callback) { - onLocalTelemetrySubUpdate(entityId, - s -> { - if (TbSubscriptionType.TIMESERIES.equals(s.getType())) { - return (TbTimeseriesSubscription) s; - } else { - return null; - } - }, s -> true, s -> { - List subscriptionUpdate = null; - for (TsKvEntry kv : ts) { - if ((s.isAllKeys() || s.getKeyStates().containsKey((kv.getKey())))) { - if (subscriptionUpdate == null) { - subscriptionUpdate = new ArrayList<>(); - } - subscriptionUpdate.add(kv); - } - } - return subscriptionUpdate; - }, true); + onTimeSeriesUpdate(entityId, ts); if (entityId.getEntityType() == EntityType.DEVICE) { updateDeviceInactivityTimeout(tenantId, entityId, ts); } @@ -224,168 +168,68 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene } @Override - public void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List attributes, TbCallback callback) { - onAttributesUpdate(tenantId, entityId, scope, attributes, true, callback); - } - - @Override - public void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List attributes, boolean notifyDevice, TbCallback callback) { - onLocalTelemetrySubUpdate(entityId, - s -> { - if (TbSubscriptionType.ATTRIBUTES.equals(s.getType())) { - return (TbAttributeSubscription) s; - } else { - return null; - } - }, - s -> (TbAttributeSubscriptionScope.ANY_SCOPE.equals(s.getScope()) || scope.equals(s.getScope().name())), - s -> { - List subscriptionUpdate = null; - for (AttributeKvEntry kv : attributes) { - if (s.isAllKeys() || s.getKeyStates().containsKey(kv.getKey())) { - if (subscriptionUpdate == null) { - subscriptionUpdate = new ArrayList<>(); - } - subscriptionUpdate.add(new BasicTsKvEntry(kv.getLastUpdateTs(), kv)); - } - } - return subscriptionUpdate; - }, true); + public void onTimeSeriesDelete(TenantId tenantId, EntityId entityId, List keys, TbCallback callback) { + onTimeSeriesUpdate(entityId, + keys.stream().map(key -> new BasicTsKvEntry(0, new StringDataEntry(key, ""))).collect(Collectors.toList())); if (entityId.getEntityType() == EntityType.DEVICE) { - if (TbAttributeSubscriptionScope.SERVER_SCOPE.name().equalsIgnoreCase(scope)) { - updateDeviceInactivityTimeout(tenantId, entityId, attributes); - } else if (TbAttributeSubscriptionScope.SHARED_SCOPE.name().equalsIgnoreCase(scope) && notifyDevice) { - clusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onUpdate(tenantId, - new DeviceId(entityId.getId()), DataConstants.SHARED_SCOPE, new ArrayList<>(attributes)) - , null); - } + deleteDeviceInactivityTimeout(tenantId, entityId, keys); } callback.onSuccess(); } - private void updateDeviceInactivityTimeout(TenantId tenantId, EntityId entityId, List kvEntries) { - for (KvEntry kvEntry : kvEntries) { - if (kvEntry.getKey().equals(DefaultDeviceStateService.INACTIVITY_TIMEOUT)) { - deviceStateService.onDeviceInactivityTimeoutUpdate(tenantId, new DeviceId(entityId.getId()), getLongValue(kvEntry)); - } + public void onTimeSeriesUpdate(EntityId entityId, List update) { + getEntityUpdatesInfo(entityId).timeSeriesUpdateTs = System.currentTimeMillis(); + TbEntityRemoteSubsInfo subInfo = entitySubscriptions.get(entityId); + if (subInfo != null) { + log.trace("[{}] Handling time-series update: {}", entityId, update); + subInfo.getSubs().forEach((serviceId, sub) -> { + if (sub.tsAllKeys) { + onTimeSeriesUpdate(serviceId, entityId, update); + } else if (sub.tsKeys != null) { + List tmp = getSubList(update, sub.tsKeys); + if (tmp != null) { + onTimeSeriesUpdate(serviceId, entityId, tmp); + } + } + }); + } else { + log.trace("[{}] No time-series subscriptions for entity.", entityId); } } - private void deleteDeviceInactivityTimeout(TenantId tenantId, EntityId entityId, List keys) { - for (String key : keys) { - if (key.equals(DefaultDeviceStateService.INACTIVITY_TIMEOUT)) { - deviceStateService.onDeviceInactivityTimeoutUpdate(tenantId, new DeviceId(entityId.getId()), 0); - } + private void onTimeSeriesUpdate(String targetId, EntityId entityId, List update) { + if (serviceId.equals(targetId)) { + localSubscriptionService.onTimeSeriesUpdate(entityId, update, TbCallback.EMPTY); + } else { + sendCoreNotification(targetId, entityId, TbSubscriptionUtils.toProto(entityId, update)); } } @Override - public void onAlarmUpdate(TenantId tenantId, EntityId entityId, AlarmInfo alarm, TbCallback callback) { - onLocalAlarmSubUpdate(entityId, - s -> { - if (TbSubscriptionType.ALARMS.equals(s.getType())) { - return (TbAlarmsSubscription) s; - } else { - return null; - } - }, - s -> alarm.getCreatedTime() >= s.getTs() || alarm.getAssignTs() >= s.getTs(), - alarm, false - ); - callback.onSuccess(); - } - - @Override - public void onAlarmDeleted(TenantId tenantId, EntityId entityId, AlarmInfo alarm, TbCallback callback) { - onLocalAlarmSubUpdate(entityId, - s -> { - if (TbSubscriptionType.ALARMS.equals(s.getType())) { - return (TbAlarmsSubscription) s; - } else { - return null; - } - }, - s -> alarm.getCreatedTime() >= s.getTs(), - alarm, true - ); - callback.onSuccess(); - } - - @Override - public void onNotificationUpdate(TenantId tenantId, UserId recipientId, NotificationUpdate notificationUpdate, TbCallback callback) { - Set subscriptions = subscriptionsByEntityId.get(recipientId); - if (subscriptions != null) { - NotificationsSubscriptionUpdate subscriptionUpdate = new NotificationsSubscriptionUpdate(notificationUpdate); - log.trace("Handling notificationUpdate for user {}: {}", recipientId, notificationUpdate); - subscriptions.stream() - .filter(subscription -> subscription.getType() == TbSubscriptionType.NOTIFICATIONS - || subscription.getType() == TbSubscriptionType.NOTIFICATIONS_COUNT) - .forEach(subscription -> onNotificationsSubUpdate(subscriptionUpdate, subscription)); - } - callback.onSuccess(); + public void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List attributes, TbCallback callback) { + onAttributesUpdate(tenantId, entityId, scope, attributes, true, callback); } @Override - public void onNotificationRequestUpdate(TenantId tenantId, NotificationRequestUpdate notificationRequestUpdate, TbCallback callback) { - NotificationsSubscriptionUpdate subscriptionUpdate = new NotificationsSubscriptionUpdate(notificationRequestUpdate); - subscriptionsByEntityId.forEach((entityId, subscriptions) -> { - if (entityId.getEntityType() != EntityType.USER) { - return; + public void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List attributes, boolean notifyDevice, TbCallback callback) { + getEntityUpdatesInfo(entityId).attributesUpdateTs = System.currentTimeMillis(); + processAttributesUpdate(entityId, scope, attributes); + if (entityId.getEntityType() == EntityType.DEVICE) { + if (TbAttributeSubscriptionScope.SERVER_SCOPE.name().equalsIgnoreCase(scope)) { + updateDeviceInactivityTimeout(tenantId, entityId, attributes); + } else if (TbAttributeSubscriptionScope.SHARED_SCOPE.name().equalsIgnoreCase(scope) && notifyDevice) { + clusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onUpdate(tenantId, + new DeviceId(entityId.getId()), DataConstants.SHARED_SCOPE, new ArrayList<>(attributes)) + , null); } - log.trace("Handling notificationRequestUpdate for user {}: {}", entityId, notificationRequestUpdate); - subscriptions.forEach(subscription -> { - if (subscription.getType() != TbSubscriptionType.NOTIFICATIONS && - subscription.getType() != TbSubscriptionType.NOTIFICATIONS_COUNT) { - return; - } - if (!subscription.getTenantId().equals(tenantId)) { - return; - } - onNotificationsSubUpdate(subscriptionUpdate, subscription); - }); - }); - callback.onSuccess(); - } - - private void onNotificationsSubUpdate(NotificationsSubscriptionUpdate subscriptionUpdate, TbSubscription subscription) { - if (serviceId.equals(subscription.getServiceId())) { - log.trace("[{}][{}][{}] Subscription session is managed by current service, forwarding to localSubscriptionService (update: {})", - subscription.getServiceId(), subscription.getEntityId(), subscription.getSessionId(), subscriptionUpdate); - localSubscriptionService.onSubscriptionUpdate(subscription.getSessionId(), - subscription.getSubscriptionId(), subscriptionUpdate, TbCallback.EMPTY); - } else { - log.trace("[{}][{}][{}] Subscription session is not managed by current service (update: {})", - subscription.getServiceId(), subscription.getEntityId(), subscription.getSessionId(), subscriptionUpdate); - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, subscription.getServiceId()); - ToCoreNotificationMsg updateProto = TbSubscriptionUtils.notificationsSubUpdateToProto(subscription, subscriptionUpdate); - TbProtoQueueMsg queueMsg = new TbProtoQueueMsg<>(subscription.getEntityId().getId(), updateProto); - toCoreNotificationsProducer.send(tpi, queueMsg, null); } + callback.onSuccess(); } @Override public void onAttributesDelete(TenantId tenantId, EntityId entityId, String scope, List keys, boolean notifyDevice, TbCallback callback) { - onLocalTelemetrySubUpdate(entityId, - s -> { - if (TbSubscriptionType.ATTRIBUTES.equals(s.getType())) { - return (TbAttributeSubscription) s; - } else { - return null; - } - }, - s -> (TbAttributeSubscriptionScope.ANY_SCOPE.equals(s.getScope()) || scope.equals(s.getScope().name())), - s -> { - List subscriptionUpdate = null; - for (String key : keys) { - if (s.isAllKeys() || s.getKeyStates().containsKey(key)) { - if (subscriptionUpdate == null) { - subscriptionUpdate = new ArrayList<>(); - } - subscriptionUpdate.add(new BasicTsKvEntry(0, new StringDataEntry(key, ""))); - } - } - return subscriptionUpdate; - }, false); + processAttributesUpdate(entityId, scope, + keys.stream().map(key -> new BaseAttributeKvEntry(0, new StringDataEntry(key, ""))).collect(Collectors.toList())); if (entityId.getEntityType() == EntityType.DEVICE) { if (TbAttributeSubscriptionScope.SERVER_SCOPE.name().equalsIgnoreCase(scope) || TbAttributeSubscriptionScope.ANY_SCOPE.name().equalsIgnoreCase(scope)) { @@ -398,222 +242,117 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene callback.onSuccess(); } - @Override - public void onTimeSeriesDelete(TenantId tenantId, EntityId entityId, List keys, TbCallback callback) { - onLocalTelemetrySubUpdate(entityId, - s -> { - if (TbSubscriptionType.TIMESERIES.equals(s.getType())) { - return (TbTimeseriesSubscription) s; - } else { - return null; - } - }, s -> true, s -> { - List subscriptionUpdate = null; - for (String key : keys) { - if (s.isAllKeys() || s.getKeyStates().containsKey(key)) { - if (subscriptionUpdate == null) { - subscriptionUpdate = new ArrayList<>(); - } - subscriptionUpdate.add(new BasicTsKvEntry(0, new StringDataEntry(key, ""))); - } - } - return subscriptionUpdate; - }, false); - if (entityId.getEntityType() == EntityType.DEVICE) { - deleteDeviceInactivityTimeout(tenantId, entityId, keys); - } - callback.onSuccess(); - } - - private void onLocalTelemetrySubUpdate(EntityId entityId, - Function castFunction, - Predicate filterFunction, - Function> processFunction, - boolean ignoreEmptyUpdates) { - Set entitySubscriptions = subscriptionsByEntityId.get(entityId); - if (entitySubscriptions != null) { - entitySubscriptions.stream().map(castFunction).filter(Objects::nonNull).filter(filterFunction).forEach(s -> { - List subscriptionUpdate = processFunction.apply(s); - if (subscriptionUpdate != null && !subscriptionUpdate.isEmpty()) { - if (serviceId.equals(s.getServiceId())) { - TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(s.getSubscriptionId(), subscriptionUpdate); - localSubscriptionService.onSubscriptionUpdate(s.getSessionId(), update, TbCallback.EMPTY); - } else { - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, s.getServiceId()); - toCoreNotificationsProducer.send(tpi, toProto(s, subscriptionUpdate, ignoreEmptyUpdates), null); + public void processAttributesUpdate(EntityId entityId, String scope, List update) { + TbEntityRemoteSubsInfo subInfo = entitySubscriptions.get(entityId); + if (subInfo != null) { + log.trace("[{}] Handling attributes update: {}", entityId, update); + subInfo.getSubs().forEach((serviceId, sub) -> { + if (sub.attrAllKeys) { + processAttributesUpdate(serviceId, entityId, scope, update); + } else if (sub.attrKeys != null) { + List tmp = getSubList(update, sub.attrKeys); + if (tmp != null) { + processAttributesUpdate(serviceId, entityId, scope, tmp); } } }); } else { - log.debug("[{}] No device subscriptions to process!", entityId); + log.trace("[{}] No attributes subscriptions for entity.", entityId); } } - private void onLocalAlarmSubUpdate(EntityId entityId, - Function castFunction, - Predicate filterFunction, - AlarmInfo alarm, boolean deleted) { - Set entitySubscriptions = subscriptionsByEntityId.get(entityId); - if (alarm == null) { - log.warn("[{}] empty alarm update!", entityId); - return; - } - if (entitySubscriptions != null) { - entitySubscriptions.stream().map(castFunction).filter(Objects::nonNull).filter(filterFunction).forEach(s -> { - if (serviceId.equals(s.getServiceId())) { - AlarmSubscriptionUpdate update = new AlarmSubscriptionUpdate(s.getSubscriptionId(), alarm, deleted); - localSubscriptionService.onSubscriptionUpdate(s.getSessionId(), update, TbCallback.EMPTY); - } else { - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, s.getServiceId()); - toCoreNotificationsProducer.send(tpi, toProto(s, alarm, deleted), null); - } - }); + private void processAttributesUpdate(String targetId, EntityId entityId, String scope, List update) { + List tsKvEntryList = update.stream().map(attr -> new BasicTsKvEntry(attr.getLastUpdateTs(), attr)).collect(Collectors.toList()); + if (serviceId.equals(targetId)) { + localSubscriptionService.onAttributesUpdate(entityId, scope, tsKvEntryList, TbCallback.EMPTY); } else { - log.debug("[{}] No device subscriptions to process!", entityId); + sendCoreNotification(targetId, entityId, TbSubscriptionUtils.toProto(scope, entityId, tsKvEntryList)); } } - private void removeSubscriptionFromEntityMap(TbSubscription sub) { - Set entitySubSet = subscriptionsByEntityId.get(sub.getEntityId()); - if (entitySubSet != null) { - entitySubSet.remove(sub); - if (entitySubSet.isEmpty()) { - subscriptionsByEntityId.remove(sub.getEntityId()); + private void updateDeviceInactivityTimeout(TenantId tenantId, EntityId entityId, List kvEntries) { + for (KvEntry kvEntry : kvEntries) { + if (kvEntry.getKey().equals(DefaultDeviceStateService.INACTIVITY_TIMEOUT)) { + deviceStateService.onDeviceInactivityTimeoutUpdate(tenantId, new DeviceId(entityId.getId()), getLongValue(kvEntry)); } } } - private void removeSubscriptionFromPartitionMap(TbSubscription sub) { - TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, sub.getTenantId(), sub.getEntityId()); - Set subs = partitionedSubscriptions.get(tpi); - if (subs != null) { - subs.remove(sub); + private void deleteDeviceInactivityTimeout(TenantId tenantId, EntityId entityId, List keys) { + for (String key : keys) { + if (key.equals(DefaultDeviceStateService.INACTIVITY_TIMEOUT)) { + deviceStateService.onDeviceInactivityTimeoutUpdate(tenantId, new DeviceId(entityId.getId()), 0); + } } } - private void handleNewAttributeSubscription(TbAttributeSubscription subscription) { - log.trace("[{}][{}][{}] Processing remote attribute subscription for entity [{}]", - serviceId, subscription.getSessionId(), subscription.getSubscriptionId(), subscription.getEntityId()); - - final Map keyStates = subscription.getKeyStates(); - DonAsynchron.withCallback(attrService.find(subscription.getTenantId(), subscription.getEntityId(), DataConstants.CLIENT_SCOPE, keyStates.keySet()), values -> { - List missedUpdates = new ArrayList<>(); - values.forEach(latestEntry -> { - if (latestEntry.getLastUpdateTs() > keyStates.get(latestEntry.getKey())) { - missedUpdates.add(new BasicTsKvEntry(latestEntry.getLastUpdateTs(), latestEntry)); - } - }); - if (!missedUpdates.isEmpty()) { - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, subscription.getServiceId()); - toCoreNotificationsProducer.send(tpi, toProto(subscription, missedUpdates), null); - } - }, - e -> log.error("Failed to fetch missed updates.", e), tsCallBackExecutor); + @Override + public void onAlarmUpdate(TenantId tenantId, EntityId entityId, AlarmInfo alarm, TbCallback callback) { + onAlarmSubUpdate(tenantId, entityId, alarm, false, callback); } - private void handleNewAlarmsSubscription(TbAlarmsSubscription subscription) { - log.trace("[{}][{}][{}] Processing remote alarm subscription for entity [{}]", - serviceId, subscription.getSessionId(), subscription.getSubscriptionId(), subscription.getEntityId()); - //TODO: @dlandiak search all new alarms for this entity. + @Override + public void onAlarmDeleted(TenantId tenantId, EntityId entityId, AlarmInfo alarm, TbCallback callback) { + onAlarmSubUpdate(tenantId, entityId, alarm, true, callback); } - private void handleNewTelemetrySubscription(TbTimeseriesSubscription subscription) { - log.trace("[{}][{}][{}] Processing remote telemetry subscription for entity [{}]", - serviceId, subscription.getSessionId(), subscription.getSubscriptionId(), subscription.getEntityId()); - - long curTs = System.currentTimeMillis(); - - if (subscription.isLatestValues()) { - DonAsynchron.withCallback(tsService.findLatest(subscription.getTenantId(), subscription.getEntityId(), subscription.getKeyStates().keySet()), - missedUpdates -> { - if (missedUpdates != null && !missedUpdates.isEmpty()) { - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, subscription.getServiceId()); - toCoreNotificationsProducer.send(tpi, toProto(subscription, missedUpdates), null); - } - }, - e -> log.error("Failed to fetch missed updates.", e), - tsCallBackExecutor); - } else { - List queries = new ArrayList<>(); - subscription.getKeyStates().forEach((key, value) -> { - if (curTs > value) { - long startTs = subscription.getStartTime() > 0 ? Math.max(subscription.getStartTime(), value + 1L) : (value + 1L); - long endTs = subscription.getEndTime() > 0 ? Math.min(subscription.getEndTime(), curTs) : curTs; - queries.add(new BaseReadTsKvQuery(key, startTs, endTs, 0, 1000, Aggregation.NONE)); + private void onAlarmSubUpdate(TenantId tenantId, EntityId entityId, AlarmInfo alarm, boolean deleted, TbCallback callback) { + TbEntityRemoteSubsInfo subInfo = entitySubscriptions.get(entityId); + if (subInfo != null) { + log.trace("[{}][{}] Handling alarm update {}: {}", tenantId, entityId, alarm, deleted); + for (Map.Entry entry : subInfo.getSubs().entrySet()) { + if (entry.getValue().alarms) { + onAlarmSubUpdate(entry.getKey(), entityId, alarm, deleted); } - }); - if (!queries.isEmpty()) { - DonAsynchron.withCallback(tsService.findAll(subscription.getTenantId(), subscription.getEntityId(), queries), - missedUpdates -> { - if (missedUpdates != null && !missedUpdates.isEmpty()) { - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, subscription.getServiceId()); - toCoreNotificationsProducer.send(tpi, toProto(subscription, missedUpdates), null); - } - }, - e -> log.error("Failed to fetch missed updates.", e), - tsCallBackExecutor); } } + callback.onSuccess(); } - private TbProtoQueueMsg toProto(TbSubscription subscription, List updates) { - return toProto(subscription, updates, true); + private void onAlarmSubUpdate(String targetServiceId, EntityId entityId, AlarmInfo alarm, boolean deleted) { + if (alarm == null) { + log.warn("[{}] empty alarm update!", entityId); + return; + } + if (serviceId.equals(targetServiceId)) { + log.trace("[{}] Forwarding to local service: {} deleted: {}", entityId, alarm, deleted); + localSubscriptionService.onAlarmUpdate(entityId, alarm, deleted, TbCallback.EMPTY); + } else { + sendCoreNotification(targetServiceId, entityId, + TbSubscriptionUtils.toAlarmSubUpdateToProto(entityId, alarm, deleted)); + } } - private TbProtoQueueMsg toProto(TbSubscription subscription, List updates, boolean ignoreEmptyUpdates) { - TbSubscriptionUpdateProto.Builder builder = TbSubscriptionUpdateProto.newBuilder(); - - builder.setSessionId(subscription.getSessionId()); - builder.setSubscriptionId(subscription.getSubscriptionId()); - - Map> data = new TreeMap<>(); - for (TsKvEntry tsEntry : updates) { - List values = data.computeIfAbsent(tsEntry.getKey(), k -> new ArrayList<>()); - Object[] value = new Object[2]; - value[0] = tsEntry.getTs(); - value[1] = tsEntry.getValueAsString(); - values.add(value); - } + private void sendCoreNotification(String targetServiceId, EntityId entityId, ToCoreNotificationMsg msg) { + log.trace("[{}] Forwarding to remote service [{}]: {}", entityId, targetServiceId, msg); + TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_CORE, targetServiceId); + TbProtoQueueMsg queueMsg = new TbProtoQueueMsg<>(entityId.getId(), msg); + toCoreNotificationsProducer.send(tpi, queueMsg, null); + } - data.forEach((key, value) -> { - TbSubscriptionUpdateValueListProto.Builder dataBuilder = TbSubscriptionUpdateValueListProto.newBuilder(); - dataBuilder.setKey(key); - boolean hasData = false; - for (Object v : value) { - Object[] array = (Object[]) v; - TbSubscriptionUpdateTsValue.Builder tsValueBuilder = TbSubscriptionUpdateTsValue.newBuilder(); - tsValueBuilder.setTs((long) array[0]); - String strVal = (String) array[1]; - if (strVal != null) { - hasData = true; - tsValueBuilder.setValue(strVal); + @Override + public void onNotificationUpdate(TenantId tenantId, UserId entityId, NotificationUpdate notificationUpdate, TbCallback callback) { + TbEntityRemoteSubsInfo subInfo = entitySubscriptions.get(entityId); + if (subInfo != null) { + NotificationsSubscriptionUpdate subscriptionUpdate = new NotificationsSubscriptionUpdate(notificationUpdate); + log.trace("[{}][{}] Handling notificationUpdate for user {}", tenantId, entityId, notificationUpdate); + for (Map.Entry entry : subInfo.getSubs().entrySet()) { + if (entry.getValue().notifications) { + onNotificationsSubUpdate(entry.getKey(), entityId, subscriptionUpdate); } - dataBuilder.addTsValue(tsValueBuilder.build()); - } - if (!ignoreEmptyUpdates || hasData) { - builder.addData(dataBuilder.build()); } - }); - - ToCoreNotificationMsg toCoreMsg = ToCoreNotificationMsg.newBuilder().setToLocalSubscriptionServiceMsg( - LocalSubscriptionServiceMsgProto.newBuilder().setSubUpdate(builder.build()).build()) - .build(); - return new TbProtoQueueMsg<>(subscription.getEntityId().getId(), toCoreMsg); + } + callback.onSuccess(); } - private TbProtoQueueMsg toProto(TbSubscription subscription, AlarmInfo alarm, boolean deleted) { - TbAlarmSubscriptionUpdateProto.Builder builder = TbAlarmSubscriptionUpdateProto.newBuilder(); - - builder.setSessionId(subscription.getSessionId()); - builder.setSubscriptionId(subscription.getSubscriptionId()); - builder.setAlarm(JacksonUtil.toString(alarm)); - builder.setDeleted(deleted); - - ToCoreNotificationMsg toCoreMsg = ToCoreNotificationMsg.newBuilder().setToLocalSubscriptionServiceMsg( - LocalSubscriptionServiceMsgProto.newBuilder() - .setAlarmSubUpdate(builder.build()).build()) - .build(); - return new TbProtoQueueMsg<>(subscription.getEntityId().getId(), toCoreMsg); + private void onNotificationsSubUpdate(String targetServiceId, EntityId entityId, NotificationsSubscriptionUpdate subscriptionUpdate) { + if (serviceId.equals(targetServiceId)) { + log.trace("[{}] Forwarding to local service: {}", entityId, subscriptionUpdate); + localSubscriptionService.onNotificationUpdate(entityId, subscriptionUpdate, TbCallback.EMPTY); + } else { + sendCoreNotification(targetServiceId, entityId, + TbSubscriptionUtils.notificationsSubUpdateToProto(entityId, subscriptionUpdate)); + } } private static long getLongValue(KvEntry kve) { @@ -639,4 +378,31 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene } } + private static List getSubList(List ts, Set keys) { + List update = null; + for (T entry : ts) { + if (keys.contains(entry.getKey())) { + if (update == null) { + update = new ArrayList<>(ts.size()); + } + update.add(entry); + } + } + return update; + } + + private TbEntityUpdatesInfo getEntityUpdatesInfo(EntityId entityId) { + return entityUpdates.computeIfAbsent(entityId, id -> new TbEntityUpdatesInfo(initTs)); + } + + private void cleanupEntityUpdates() { + initTs = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1); + int sizeBeforeCleanup = entityUpdates.size(); + entityUpdates.entrySet().removeIf(kv -> { + var v = kv.getValue(); + return initTs > v.attributesUpdateTs && initTs > v.timeSeriesUpdateTs; + }); + log.info("Removed {} old entity update records.", entityUpdates.size() - sizeBeforeCleanup); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java index 4b37ba26b5..b45450cec8 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java @@ -641,7 +641,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc private void handleLatestCmd(TbEntityDataSubCtx ctx, LatestValueCmd latestCmd) { log.trace("[{}][{}] Going to process latest command: {}", ctx.getSessionId(), ctx.getCmdId(), latestCmd); - //Fetch the latest values for telemetry keys (in case they are not copied from NoSQL to SQL DB in hybrid mode. + //Fetch the latest values for telemetry keys in case they are not copied from NoSQL to SQL DB in hybrid mode. if (!tsInSqlDB) { log.trace("[{}][{}] Going to fetch missing latest values: {}", ctx.getSessionId(), ctx.getCmdId(), latestCmd); List allTsKeys = latestCmd.getKeys().stream() diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java index 64f1de8662..3cf4c1c0e0 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java @@ -16,85 +16,97 @@ package org.thingsboard.server.service.subscription; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; +import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.alarm.AlarmInfo; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.Aggregation; +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.ReadTsKvQuery; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.discovery.PartitionService; -import org.thingsboard.server.queue.discovery.TbApplicationEventListener; +import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent; -import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate; import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; -import java.util.Collections; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Collectors; @Slf4j @TbCoreComponent @Service public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionService { - private final Set currentPartitions = ConcurrentHashMap.newKeySet(); - private final Map> subscriptionsBySessionId = new ConcurrentHashMap<>(); + private final ConcurrentMap>> subscriptionsBySessionId = new ConcurrentHashMap<>(); + private final ConcurrentMap subscriptionsByEntityId = new ConcurrentHashMap<>(); + private final ConcurrentMap entityUpdates = new ConcurrentHashMap<>(); - @Autowired - private PartitionService partitionService; + private final AttributesService attrService; + private final TimeseriesService tsService; + private final TbServiceInfoProvider serviceInfoProvider; + private final PartitionService partitionService; + private final TbClusterService clusterService; + private final SubscriptionManagerService subscriptionManagerService; - @Autowired - private TbClusterService clusterService; + private ExecutorService tsCallBackExecutor; - @Autowired - @Lazy - private SubscriptionManagerService subscriptionManagerService; + public DefaultTbLocalSubscriptionService(AttributesService attrService, TimeseriesService tsService, TbServiceInfoProvider serviceInfoProvider, + PartitionService partitionService, TbClusterService clusterService, + @Lazy SubscriptionManagerService subscriptionManagerService) { + this.attrService = attrService; + this.tsService = tsService; + this.serviceInfoProvider = serviceInfoProvider; + this.partitionService = partitionService; + this.clusterService = clusterService; + this.subscriptionManagerService = subscriptionManagerService; + } + private String serviceId; private ExecutorService subscriptionUpdateExecutor; - private TbApplicationEventListener partitionChangeListener = new TbApplicationEventListener<>() { - @Override - protected void onTbApplicationEvent(PartitionChangeEvent event) { - if (ServiceType.TB_CORE.equals(event.getServiceType())) { - currentPartitions.clear(); - currentPartitions.addAll(event.getPartitions()); - } - } - }; - - private TbApplicationEventListener clusterTopologyChangeListener = new TbApplicationEventListener<>() { - @Override - protected void onTbApplicationEvent(ClusterTopologyChangeEvent event) { - if (event.getQueueKeys().stream().anyMatch(key -> ServiceType.TB_CORE.equals(key.getType()))) { - /* - * If the cluster topology has changed, we need to push all current subscriptions to SubscriptionManagerService again. - * Otherwise, the SubscriptionManagerService may "forget" those subscriptions in case of restart. - * Although this is resource consuming operation, it is cheaper than sending ping/pong commands periodically - * It is also cheaper then caching the subscriptions by entity id and then lookup of those caches every time we have new telemetry in SubscriptionManagerService. - * Even if we cache locally the list of active subscriptions by entity id, it is still time consuming operation to get them from cache - * Since number of subscriptions is usually much less then number of devices that are pushing data. - */ - subscriptionsBySessionId.values().forEach(map -> map.values() - .forEach(sub -> pushSubscriptionToManagerService(sub, true))); - } - } - }; + private final Lock subsLock = new ReentrantLock(); @PostConstruct public void initExecutor() { subscriptionUpdateExecutor = ThingsBoardExecutors.newWorkStealingPool(20, getClass()); + tsCallBackExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("ts-sub-callback")); + serviceId = serviceInfoProvider.getServiceId(); } @PreDestroy @@ -102,79 +114,79 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer if (subscriptionUpdateExecutor != null) { subscriptionUpdateExecutor.shutdownNow(); } - } - - @Override - @EventListener(PartitionChangeEvent.class) - public void onApplicationEvent(PartitionChangeEvent event) { - partitionChangeListener.onApplicationEvent(event); + if (tsCallBackExecutor != null) { + tsCallBackExecutor.shutdownNow(); + } } @Override @EventListener(ClusterTopologyChangeEvent.class) public void onApplicationEvent(ClusterTopologyChangeEvent event) { - clusterTopologyChangeListener.onApplicationEvent(event); + if (event.getQueueKeys().stream().anyMatch(key -> ServiceType.TB_CORE.equals(key.getType()))) { + /* + * If the cluster topology has changed, we need to push all current subscriptions to SubscriptionManagerService again. + * Otherwise, the SubscriptionManagerService may "forget" those subscriptions in case of restart. + * Although this is resource consuming operation, it is cheaper than sending ping/pong commands periodically + * It is also cheaper than caching the subscriptions by entity id and then lookup of those caches every time we have new telemetry in SubscriptionManagerService. + * Even if we cache locally the list of active subscriptions by entity id, it is still time-consuming operation to get them from cache + * Since number of subscriptions is usually much less than number of devices that are pushing data. + */ + subscriptionsByEntityId.values().forEach(sub -> pushSubEventToManagerService(sub.getTenantId(), sub.getEntityId(), sub.toEvent(ComponentLifecycleEvent.UPDATED))); + } } - //TODO 3.1: replace null callbacks with callbacks from websocket service. @Override - public void addSubscription(TbSubscription subscription) { - pushSubscriptionToManagerService(subscription, true); - registerSubscription(subscription); + public void onCoreStartupMsg(TransportProtos.CoreStartupMsg coreStartupMsg) { + subscriptionUpdateExecutor.submit(() -> { + Set partitions = new HashSet<>(coreStartupMsg.getPartitionsList()); + AtomicInteger counter = new AtomicInteger(); + subscriptionsByEntityId.values().forEach(sub -> { + var tpi = partitionService.resolve(ServiceType.TB_CORE, sub.getTenantId(), sub.getEntityId()); + if (!tpi.isMyPartition() && partitions.contains(tpi.getPartition().orElse(Integer.MAX_VALUE))) { + pushToQueue(sub.getEntityId(), sub.toEvent(ComponentLifecycleEvent.UPDATED), tpi); + counter.incrementAndGet(); + } + }); + log.info("[{}] Pushed {} subscriptions to [{}]", serviceId, counter.get(), coreStartupMsg.getServiceId()); + }); } - private void pushSubscriptionToManagerService(TbSubscription subscription, boolean pushToLocalService) { - TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, subscription.getTenantId(), subscription.getEntityId()); - if (currentPartitions.contains(tpi)) { - // Subscription is managed on the same server; - if (pushToLocalService) { - subscriptionManagerService.addSubscription(subscription, TbCallback.EMPTY); - } - } else { - // Push to the queue; - TransportProtos.ToCoreMsg toCoreMsg = TbSubscriptionUtils.toNewSubscriptionProto(subscription); - clusterService.pushMsgToCore(tpi, subscription.getEntityId().getId(), toCoreMsg, null); - } + @Override + public void addSubscription(TbSubscription subscription) { + TenantId tenantId = subscription.getTenantId(); + EntityId entityId = subscription.getEntityId(); + log.debug("[{}][{}] Register subscription: {}", tenantId, entityId, subscription); + Map> sessionSubscriptions = subscriptionsBySessionId.computeIfAbsent(subscription.getSessionId(), k -> new ConcurrentHashMap<>()); + sessionSubscriptions.put(subscription.getSubscriptionId(), subscription); + modifySubscription(tenantId, entityId, subscription, true); } @Override - @SuppressWarnings("unchecked") - public void onSubscriptionUpdate(String sessionId, TelemetrySubscriptionUpdate update, TbCallback callback) { - TbSubscription subscription = subscriptionsBySessionId - .getOrDefault(sessionId, Collections.emptyMap()).get(update.getSubscriptionId()); - if (subscription != null) { - switch (subscription.getType()) { - case TIMESERIES: - TbTimeseriesSubscription tsSub = (TbTimeseriesSubscription) subscription; - update.getLatestValues().forEach((key, value) -> tsSub.getKeyStates().put(key, value)); - break; - case ATTRIBUTES: - TbAttributeSubscription attrSub = (TbAttributeSubscription) subscription; - update.getLatestValues().forEach((key, value) -> attrSub.getKeyStates().put(key, value)); - break; - } - subscriptionUpdateExecutor.submit(() -> subscription.getUpdateProcessor().accept(subscription, update)); - } - callback.onSuccess(); + public void onSubEventCallback(TransportProtos.TbEntitySubEventCallbackProto subEventCallback, TbCallback callback) { + UUID entityId = new UUID(subEventCallback.getEntityIdMSB(), subEventCallback.getEntityIdLSB()); + onSubEventCallback(entityId, subEventCallback.getSeqNumber(), new TbEntityUpdatesInfo(subEventCallback.getAttributesUpdateTs(), subEventCallback.getTimeSeriesUpdateTs()), callback); } @Override - @SuppressWarnings("unchecked") - public void onSubscriptionUpdate(String sessionId, AlarmSubscriptionUpdate update, TbCallback callback) { - TbSubscription subscription = subscriptionsBySessionId - .getOrDefault(sessionId, Collections.emptyMap()).get(update.getSubscriptionId()); - if (subscription != null && subscription.getType() == TbSubscriptionType.ALARMS) { - subscriptionUpdateExecutor.submit(() -> subscription.getUpdateProcessor().accept(subscription, update)); - } - callback.onSuccess(); + public void onSubEventCallback(EntityId entityId, int seqNumber, TbEntityUpdatesInfo entityUpdatesInfo, TbCallback callback) { + onSubEventCallback(entityId.getId(), seqNumber, entityUpdatesInfo, callback); } - @Override - public void onSubscriptionUpdate(String sessionId, int subscriptionId, NotificationsSubscriptionUpdate update, TbCallback callback) { - TbSubscription subscription = subscriptionsBySessionId.getOrDefault(sessionId, Collections.emptyMap()).get(subscriptionId); - if (subscription != null && (subscription.getType() == TbSubscriptionType.NOTIFICATIONS - || subscription.getType() == TbSubscriptionType.NOTIFICATIONS_COUNT)) { - subscriptionUpdateExecutor.submit(() -> subscription.getUpdateProcessor().accept(subscription, update)); + public void onSubEventCallback(UUID entityId, int seqNumber, TbEntityUpdatesInfo entityUpdatesInfo, TbCallback callback) { + log.debug("[{}][{}] Processing sub event callback: {}.", entityId, seqNumber, entityUpdatesInfo); + entityUpdates.put(entityId, entityUpdatesInfo); + Set> pendingSubs = null; + subsLock.lock(); + try { + TbEntityLocalSubsInfo entitySubs = subscriptionsByEntityId.get(entityId); + if (entitySubs != null) { + pendingSubs = entitySubs.clearPendingSubscriptions(seqNumber); + } + } finally { + subsLock.unlock(); + } + if (pendingSubs != null) { + pendingSubs.forEach(this::checkMissedUpdates); } callback.onSuccess(); } @@ -182,22 +194,14 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer @Override public void cancelSubscription(String sessionId, int subscriptionId) { log.debug("[{}][{}] Going to remove subscription.", sessionId, subscriptionId); - Map sessionSubscriptions = subscriptionsBySessionId.get(sessionId); + Map> sessionSubscriptions = subscriptionsBySessionId.get(sessionId); if (sessionSubscriptions != null) { - TbSubscription subscription = sessionSubscriptions.remove(subscriptionId); + TbSubscription subscription = sessionSubscriptions.remove(subscriptionId); if (subscription != null) { if (sessionSubscriptions.isEmpty()) { subscriptionsBySessionId.remove(sessionId); } - TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, subscription.getTenantId(), subscription.getEntityId()); - if (currentPartitions.contains(tpi)) { - // Subscription is managed on the same server; - subscriptionManagerService.cancelSubscription(sessionId, subscriptionId, TbCallback.EMPTY); - } else { - // Push to the queue; - TransportProtos.ToCoreMsg toCoreMsg = TbSubscriptionUtils.toCloseSubscriptionProto(subscription); - clusterService.pushMsgToCore(tpi, subscription.getEntityId().getId(), toCoreMsg, null); - } + modifySubscription(subscription.getTenantId(), subscription.getEntityId(), subscription, false); } else { log.debug("[{}][{}] Subscription not found!", sessionId, subscriptionId); } @@ -208,16 +212,309 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer @Override public void cancelAllSessionSubscriptions(String sessionId) { - Map subscriptions = subscriptionsBySessionId.get(sessionId); - if (subscriptions != null) { - Set toRemove = new HashSet<>(subscriptions.keySet()); - toRemove.forEach(id -> cancelSubscription(sessionId, id)); + log.debug("[{}] Going to remove session subscriptions.", sessionId); + Map> sessionSubscriptions = subscriptionsBySessionId.remove(sessionId); + if (sessionSubscriptions != null) { + for (TbSubscription subscription : sessionSubscriptions.values()) { + modifySubscription(subscription.getTenantId(), subscription.getEntityId(), subscription, false); + } + } else { + log.debug("[{}] No session subscriptions found!", sessionId); } } - private void registerSubscription(TbSubscription subscription) { - Map sessionSubscriptions = subscriptionsBySessionId.computeIfAbsent(subscription.getSessionId(), k -> new ConcurrentHashMap<>()); - sessionSubscriptions.put(subscription.getSubscriptionId(), subscription); + @Override + public void onTimeSeriesUpdate(TransportProtos.TbSubUpdateProto proto, TbCallback callback) { + //TODO: optimize to avoid re-wrapping from TsValueListProto -> List -> Map>. Low priority. + onTimeSeriesUpdate(new UUID(proto.getEntityIdMSB(), proto.getEntityIdLSB()), TbSubscriptionUtils.fromProto(proto), callback); + } + + @Override + public void onTimeSeriesUpdate(EntityId entityId, List data, TbCallback callback) { + onTimeSeriesUpdate(entityId.getId(), data, callback); + } + + private void onTimeSeriesUpdate(UUID entityId, List data, TbCallback callback) { + entityUpdates.get(entityId).timeSeriesUpdateTs = System.currentTimeMillis(); + processSubscriptionData(entityId, + sub -> TbSubscriptionType.TIMESERIES.equals(sub.getType()), + s -> { + TbTimeSeriesSubscription sub = (TbTimeSeriesSubscription) s; + List updateData = null; + if (sub.isAllKeys()) { + updateData = data; + } else { + for (TsKvEntry kv : data) { + if (sub.getKeyStates().containsKey((kv.getKey()))) { + if (updateData == null) { + updateData = new ArrayList<>(); + } + updateData.add(kv); + } + } + } + if (updateData != null) { + TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(sub.getSubscriptionId(), updateData); + update.getLatestValues().forEach((key, value) -> sub.getKeyStates().put(key, value)); + subscriptionUpdateExecutor.submit(() -> sub.getUpdateProcessor().accept(sub, update)); + } + }, callback); + } + + @Override + public void onAttributesUpdate(TransportProtos.TbSubUpdateProto proto, TbCallback callback) { + onAttributesUpdate(new UUID(proto.getEntityIdMSB(), proto.getEntityIdLSB()), proto.getScope(), TbSubscriptionUtils.fromProto(proto), callback); + } + + @Override + public void onAttributesUpdate(EntityId entityId, String scope, List data, TbCallback callback) { + onAttributesUpdate(entityId.getId(), scope, data, callback); + } + + private void onAttributesUpdate(UUID entityId, String scope, List data, TbCallback callback) { + entityUpdates.get(entityId).attributesUpdateTs = System.currentTimeMillis(); + processSubscriptionData(entityId, + sub -> TbSubscriptionType.ATTRIBUTES.equals(sub.getType()), + s -> { + TbAttributeSubscription sub = (TbAttributeSubscription) s; + if (sub.getScope() == null || TbAttributeSubscriptionScope.ANY_SCOPE.equals(sub.getScope()) || sub.getScope().name().equals(scope)) { + List updateData = null; + if (sub.isAllKeys()) { + updateData = data; + } else { + for (TsKvEntry kv : data) { + if (sub.getKeyStates().containsKey((kv.getKey()))) { + if (updateData == null) { + updateData = new ArrayList<>(); + } + updateData.add(kv); + } + } + } + if (updateData != null) { + TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(sub.getSubscriptionId(), updateData); + update.getLatestValues().forEach((key, value) -> sub.getKeyStates().put(key, value)); + subscriptionUpdateExecutor.submit(() -> sub.getUpdateProcessor().accept(sub, update)); + } + } + }, callback); + } + + @Override + public void onAlarmUpdate(TransportProtos.TbAlarmSubUpdateProto proto, TbCallback callback) { + onAlarmUpdate(new UUID(proto.getEntityIdMSB(), proto.getEntityIdLSB()), TbSubscriptionUtils.fromProto(proto), callback); + } + + @Override + public void onAlarmUpdate(EntityId entityId, AlarmInfo alarm, boolean deleted, TbCallback callback) { + onAlarmUpdate(entityId.getId(), new AlarmSubscriptionUpdate(alarm, deleted), callback); + } + + private void onAlarmUpdate(UUID entityId, AlarmSubscriptionUpdate update, TbCallback callback) { + processSubscriptionData(entityId, + sub -> TbSubscriptionType.ALARMS.equals(sub.getType()), + update, callback); + } + + @Override + public void onNotificationUpdate(TransportProtos.NotificationsSubUpdateProto proto, TbCallback callback) { + onNotificationUpdate(new UUID(proto.getEntityIdMSB(), proto.getEntityIdLSB()), TbSubscriptionUtils.fromProto(proto), callback); + } + + @Override + public void onNotificationUpdate(EntityId entityId, NotificationsSubscriptionUpdate update, TbCallback callback) { + onNotificationUpdate(entityId.getId(), update, callback); + } + + private void onNotificationUpdate(UUID entityId, NotificationsSubscriptionUpdate update, TbCallback callback) { + processSubscriptionData(entityId, + sub -> TbSubscriptionType.NOTIFICATIONS.equals(sub.getType()) || TbSubscriptionType.NOTIFICATIONS_COUNT.equals(sub.getType()), + update, callback); + } + + @Override + @SuppressWarnings("unchecked") + public void onNotificationRequestUpdate(TenantId tenantId, NotificationRequestUpdate update, TbCallback callback) { + log.trace("[{}] Received notification request update: {}", tenantId, update); + NotificationsSubscriptionUpdate theUpdate = new NotificationsSubscriptionUpdate(update); + subscriptionsByEntityId.values().forEach(subInfo -> { + if (subInfo.isNf() && tenantId.equals(subInfo.getTenantId()) && EntityType.USER.equals(subInfo.getEntityId().getEntityType())) { + subInfo.getSubs().forEach(s -> { + TbSubscription sub = (TbSubscription) s; + subscriptionUpdateExecutor.submit(() -> sub.getUpdateProcessor().accept(sub, theUpdate)); + }); + } + }); + callback.onSuccess(); + } + + @SuppressWarnings("unchecked") + private void processSubscriptionData(UUID entityId, + Predicate> filter, + T data, + TbCallback callback) { + log.trace("[{}] Received subscription data: {}", entityId, data); + var subs = subscriptionsByEntityId.get(entityId); + if (subs != null) { + subs.getSubs().forEach(s -> { + if (filter.test(s)) { + subscriptionUpdateExecutor.submit(() -> { + TbSubscription sub = (TbSubscription) s; + sub.getUpdateProcessor().accept(sub, data); + }); + } + }); + } + callback.onSuccess(); + } + + private void processSubscriptionData(UUID entityId, + Predicate> filter, + Consumer> processor, + TbCallback callback) { + var subs = subscriptionsByEntityId.get(entityId); + if (subs != null) { + subs.getSubs().forEach(s -> { + if (filter.test(s)) { + processor.accept(s); + } + }); + } + callback.onSuccess(); + } + + private void modifySubscription(TenantId tenantId, EntityId entityId, TbSubscription subscription, boolean add) { + TbSubscription missedUpdatesCandidate = null; + TbEntitySubEvent event; + subsLock.lock(); + try { + TbEntityLocalSubsInfo entitySubs = subscriptionsByEntityId.computeIfAbsent(entityId.getId(), id -> new TbEntityLocalSubsInfo(tenantId, entityId)); + event = add ? entitySubs.add(subscription) : entitySubs.remove(subscription); + if (entitySubs.isEmpty()) { + subscriptionsByEntityId.remove(entityId.getId()); + entityUpdates.remove(entityId.getId()); + } else if (add) { + missedUpdatesCandidate = entitySubs.registerPendingSubscription(subscription, event); + } + } finally { + subsLock.unlock(); + } + if (event != null) { + log.trace("[{}][{}][{}] Event: {}", tenantId, entityId, subscription.getSubscriptionId(), event); + pushSubEventToManagerService(tenantId, entityId, event); + if (missedUpdatesCandidate != null) { + checkMissedUpdates(missedUpdatesCandidate); + } + } else { + log.trace("[{}][{}][{}] No changes detected.", tenantId, entityId, subscription.getSubscriptionId()); + } + } + + private void pushSubEventToManagerService(TenantId tenantId, EntityId entityId, TbEntitySubEvent event) { + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); + if (tpi.isMyPartition()) { + // Subscription is managed on the same server; + subscriptionManagerService.onSubEvent(serviceId, event, TbCallback.EMPTY); + } else { + pushToQueue(entityId, event, tpi); + } + } + + private void pushToQueue(EntityId entityId, TbEntitySubEvent event, TopicPartitionInfo tpi) { + clusterService.pushMsgToCore(tpi, entityId.getId(), TbSubscriptionUtils.toSubEventProto(serviceId, event), null); + } + + private void checkMissedUpdates(TbSubscription subscription) { + log.trace("[{}][{}][{}] Check missed updates for subscription: {}", + subscription.getTenantId(), subscription.getEntityId(), subscription.getSubscriptionId(), subscription); + switch (subscription.getType()) { + case TIMESERIES: + handleNewTelemetrySubscription((TbTimeSeriesSubscription) subscription); + break; + case ATTRIBUTES: + handleNewAttributeSubscription((TbAttributeSubscription) subscription); + break; + } + } + + private void handleNewAttributeSubscription(TbAttributeSubscription subscription) { + log.trace("[{}][{}][{}] Processing attribute subscription for entity [{}]", + subscription.getTenantId(), subscription.getSessionId(), subscription.getSubscriptionId(), subscription.getEntityId()); + var entityUpdateInfo = entityUpdates.get(subscription.getEntityId().getId()); + if (entityUpdateInfo != null && entityUpdateInfo.attributesUpdateTs > 0 && subscription.getQueryTs() > entityUpdateInfo.attributesUpdateTs) { + log.trace("[{}][{}][{}] No need to check for missed updates [{}]", + subscription.getTenantId(), subscription.getSessionId(), subscription.getSubscriptionId(), subscription.getEntityId()); + return; + } + final Map keyStates = subscription.getKeyStates(); + String scope; + if (subscription.getScope() != null && !TbAttributeSubscriptionScope.ANY_SCOPE.equals(subscription.getScope())) { + scope = subscription.getScope().name(); + } else { + scope = DataConstants.CLIENT_SCOPE; + } + DonAsynchron.withCallback(attrService.find(subscription.getTenantId(), subscription.getEntityId(), scope, keyStates.keySet()), values -> { + List updates = new ArrayList<>(); + values.forEach(latestEntry -> { + if (latestEntry.getLastUpdateTs() > keyStates.get(latestEntry.getKey())) { + updates.add(new BasicTsKvEntry(latestEntry.getLastUpdateTs(), latestEntry)); + } + }); + var missedUpdates = updates.stream().filter(u -> u.getValue() != null).collect(Collectors.toList()); + if (!missedUpdates.isEmpty()) { + onAttributesUpdate(subscription.getEntityId(), scope, missedUpdates, TbCallback.EMPTY); + } + }, + e -> log.error("Failed to fetch missed updates.", e), tsCallBackExecutor); + } + + private void handleNewTelemetrySubscription(TbTimeSeriesSubscription subscription) { + log.trace("[{}][{}][{}] Processing telemetry subscription for entity [{}]", + subscription.getTenantId(), subscription.getSessionId(), subscription.getSubscriptionId(), subscription.getEntityId()); + var entityUpdateInfo = entityUpdates.get(subscription.getEntityId().getId()); + if (entityUpdateInfo != null && entityUpdateInfo.timeSeriesUpdateTs > 0 && subscription.getQueryTs() > entityUpdateInfo.timeSeriesUpdateTs) { + log.trace("[{}][{}][{}] No need to check for missed updates. time [{}][{}] diff: {}ms", + subscription.getTenantId(), subscription.getSessionId(), subscription.getSubscriptionId(), subscription.getQueryTs(), entityUpdateInfo.timeSeriesUpdateTs, subscription.getQueryTs() - entityUpdateInfo.timeSeriesUpdateTs); + return; + } + + long curTs = System.currentTimeMillis(); + + if (subscription.isLatestValues()) { + DonAsynchron.withCallback(tsService.findLatest(subscription.getTenantId(), subscription.getEntityId(), subscription.getKeyStates().keySet()), + missedUpdates -> { + if (missedUpdates != null && !missedUpdates.isEmpty()) { + missedUpdates = missedUpdates.stream().filter(u -> u.getValue() != null).collect(Collectors.toList()); + if (!missedUpdates.isEmpty()) { + onTimeSeriesUpdate(subscription.getEntityId(), missedUpdates, TbCallback.EMPTY); + } + } + }, + e -> log.error("Failed to fetch missed updates.", e), + tsCallBackExecutor); + } else { + List queries = new ArrayList<>(); + subscription.getKeyStates().forEach((key, value) -> { + if (curTs > value) { + long startTs = subscription.getStartTime() > 0 ? Math.max(subscription.getStartTime(), value + 1L) : (value + 1L); + long endTs = subscription.getEndTime() > 0 ? Math.min(subscription.getEndTime(), curTs) : curTs; + queries.add(new BaseReadTsKvQuery(key, startTs, endTs, 0, 1000, Aggregation.NONE)); + } + }); + if (!queries.isEmpty()) { + DonAsynchron.withCallback(tsService.findAll(subscription.getTenantId(), subscription.getEntityId(), queries), + missedUpdates -> { + if (missedUpdates != null && !missedUpdates.isEmpty()) { + missedUpdates = missedUpdates.stream().filter(u -> u.getValue() != null).collect(Collectors.toList()); + if (!missedUpdates.isEmpty()) { + onTimeSeriesUpdate(subscription.getEntityId(), missedUpdates, TbCallback.EMPTY); + } + } + }, + e -> log.error("Failed to fetch missed updates.", e), + tsCallBackExecutor); + } + } } } diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java b/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java index 736a251dff..4f6d16e6c2 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java @@ -16,6 +16,7 @@ package org.thingsboard.server.service.subscription; import org.springframework.context.ApplicationListener; +import org.springframework.context.event.EventListener; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -23,6 +24,8 @@ import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.msg.queue.TbCallback; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.queue.discovery.event.OtherServiceShutdownEvent; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationUpdate; @@ -31,9 +34,9 @@ import java.util.List; public interface SubscriptionManagerService extends ApplicationListener { - void addSubscription(TbSubscription subscription, TbCallback callback); + void onSubEvent(String serviceId, TbEntitySubEvent event, TbCallback empty); - void cancelSubscription(String sessionId, int subscriptionId, TbCallback callback); + void onApplicationEvent(OtherServiceShutdownEvent event); void onTimeSeriesUpdate(TenantId tenantId, EntityId entityId, List ts, TbCallback callback); @@ -51,6 +54,4 @@ public interface SubscriptionManagerService extends ApplicationListener scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { + return scheduler.scheduleWithFixedDelay(command, initialDelay, delay, unit); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractDataSubCtx.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractDataSubCtx.java index 3f11679849..f02a466994 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractDataSubCtx.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractDataSubCtx.java @@ -186,6 +186,7 @@ public abstract class TbAbstractDataSubCtx sendWsMsg(sub.getSessionId(), subscriptionUpdate, keysType)) + .queryTs(createdTime) .allKeys(false) .keyStates(keyStates) .scope(scope) @@ -207,19 +208,20 @@ public abstract class TbAbstractDataSubCtx keyStates) { + private TbTimeSeriesSubscription createTsSub(EntityData entityData, int subIdx, boolean latestValues, long startTs, long endTs, Map keyStates) { return createTsSub(entityData, subIdx, latestValues, startTs, endTs, keyStates, latestValues); } - private TbTimeseriesSubscription createTsSub(EntityData entityData, int subIdx, boolean latestValues, long startTs, long endTs, Map keyStates, boolean resultToLatestValues) { + private TbTimeSeriesSubscription createTsSub(EntityData entityData, int subIdx, boolean latestValues, long startTs, long endTs, Map keyStates, boolean resultToLatestValues) { log.trace("[{}][{}][{}] Creating time-series subscription for [{}] with keys: {}", serviceId, cmdId, subIdx, entityData.getEntityId(), keyStates); - return TbTimeseriesSubscription.builder() + return TbTimeSeriesSubscription.builder() .serviceId(serviceId) .sessionId(sessionRef.getSessionId()) .subscriptionId(subIdx) .tenantId(sessionRef.getSecurityCtx().getTenantId()) .entityId(entityData.getEntityId()) .updateProcessor((sub, subscriptionUpdate) -> sendWsMsg(sub.getSessionId(), subscriptionUpdate, EntityKeyType.TIME_SERIES, resultToLatestValues)) + .queryTs(createdTime) .allKeys(false) .keyStates(keyStates) .latestValues(latestValues) diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java index 7eba28bccd..b4468c0bd0 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java @@ -78,11 +78,14 @@ public abstract class TbAbstractSubCtx { @Setter protected volatile ScheduledFuture refreshTask; protected volatile boolean stopped; + @Getter + protected long createdTime; public TbAbstractSubCtx(String serviceId, WebSocketService wsService, EntityService entityService, TbLocalSubscriptionService localSubscriptionService, AttributesService attributesService, SubscriptionServiceStatistics stats, WebSocketSessionRef sessionRef, int cmdId) { + this.createdTime = System.currentTimeMillis(); this.serviceId = serviceId; this.wsService = wsService; this.entityService = entityService; @@ -142,6 +145,7 @@ public abstract class TbAbstractSubCtx { .tenantId(sessionRef.getSecurityCtx().getTenantId()) .entityId(entityId) .updateProcessor((subscription, subscriptionUpdate) -> dynamicValueSubUpdate(subscription.getSessionId(), subscriptionUpdate, dynamicValueKeySubMap)) + .queryTs(createdTime) .allKeys(false) .keyStates(keyStates) .scope(TbAttributeSubscriptionScope.SERVER_SCOPE) @@ -332,7 +336,7 @@ public abstract class TbAbstractSubCtx { public void sendWsMsg(CmdUpdate update) { wsLock.lock(); try { - wsService.sendWsMsg(sessionRef.getSessionId(), update); + wsService.sendUpdate(sessionRef.getSessionId(), update); } finally { wsLock.unlock(); } diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbAttributeSubscription.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbAttributeSubscription.java index 3746758caa..2392899dc7 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbAttributeSubscription.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbAttributeSubscription.java @@ -26,6 +26,7 @@ import java.util.function.BiConsumer; public class TbAttributeSubscription extends TbSubscription { + @Getter private final long queryTs; @Getter private final boolean allKeys; @Getter private final Map keyStates; @Getter private final TbAttributeSubscriptionScope scope; @@ -33,8 +34,9 @@ public class TbAttributeSubscription extends TbSubscription, TelemetrySubscriptionUpdate> updateProcessor, - boolean allKeys, Map keyStates, TbAttributeSubscriptionScope scope) { + long queryTs, boolean allKeys, Map keyStates, TbAttributeSubscriptionScope scope) { super(serviceId, sessionId, subscriptionId, tenantId, entityId, TbSubscriptionType.ATTRIBUTES, updateProcessor); + this.queryTs = queryTs; this.allKeys = allKeys; this.keyStates = keyStates; this.scope = scope; diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfo.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfo.java new file mode 100644 index 0000000000..96ea628de2 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfo.java @@ -0,0 +1,245 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.subscription; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Information about the local websocket subscriptions. + */ +@Slf4j +@RequiredArgsConstructor +public class TbEntityLocalSubsInfo { + + @Getter + private final TenantId tenantId; + @Getter + private final EntityId entityId; + @Getter + private final Lock lock = new ReentrantLock(); + @Getter + private final Set> subs = ConcurrentHashMap.newKeySet(); + private volatile TbSubscriptionsInfo state = new TbSubscriptionsInfo(); + + private final Map>> pendingSubs = new ConcurrentHashMap<>(); + @Getter + @Setter + private int pendingTimeSeriesEvent; + @Getter + @Setter + private long pendingTimeSeriesEventTs; + @Getter + @Setter + private int pendingAttributesEvent; + @Getter + @Setter + private long pendingAttributesEventTs; + + private int seqNumber = 0; + + public TbEntitySubEvent add(TbSubscription subscription) { + log.trace("[{}][{}][{}] Adding: {}", tenantId, entityId, subscription.getSubscriptionId(), subscription); + boolean created = subs.isEmpty(); + subs.add(subscription); + TbSubscriptionsInfo newState = created ? state : state.copy(); + boolean stateChanged = false; + switch (subscription.getType()) { + case NOTIFICATIONS: + case NOTIFICATIONS_COUNT: + if (!newState.notifications) { + newState.notifications = true; + stateChanged = true; + } + break; + case ALARMS: + if (!newState.alarms) { + newState.alarms = true; + stateChanged = true; + } + break; + case ATTRIBUTES: + var attrSub = (TbAttributeSubscription) subscription; + if (!newState.attrAllKeys) { + if (attrSub.isAllKeys()) { + newState.attrAllKeys = true; + stateChanged = true; + } else { + if (newState.attrKeys == null) { + newState.attrKeys = new HashSet<>(attrSub.getKeyStates().keySet()); + stateChanged = true; + } else if (newState.attrKeys.addAll(attrSub.getKeyStates().keySet())) { + stateChanged = true; + } + } + } + break; + case TIMESERIES: + var tsSub = (TbTimeSeriesSubscription) subscription; + if (!newState.tsAllKeys) { + if (tsSub.isAllKeys()) { + newState.tsAllKeys = true; + stateChanged = true; + } else { + if (newState.tsKeys == null) { + newState.tsKeys = new HashSet<>(tsSub.getKeyStates().keySet()); + stateChanged = true; + } else if (newState.tsKeys.addAll(tsSub.getKeyStates().keySet())) { + stateChanged = true; + } + } + } + break; + } + if (stateChanged) { + state = newState; + } + if (created) { + return toEvent(ComponentLifecycleEvent.CREATED); + } else if (stateChanged) { + return toEvent(ComponentLifecycleEvent.UPDATED); + } else { + return null; + } + } + + public TbEntitySubEvent remove(TbSubscription sub) { + log.trace("[{}][{}][{}] Removing: {}", tenantId, entityId, sub.getSubscriptionId(), sub); + if (!subs.remove(sub)) { + return null; + } + if (subs.isEmpty()) { + return toEvent(ComponentLifecycleEvent.DELETED); + } + TbSubscriptionsInfo oldState = state.copy(); + TbSubscriptionsInfo newState = new TbSubscriptionsInfo(); + for (TbSubscription subscription : subs) { + switch (subscription.getType()) { + case NOTIFICATIONS: + case NOTIFICATIONS_COUNT: + if (!newState.notifications) { + newState.notifications = true; + } + break; + case ALARMS: + if (!newState.alarms) { + newState.alarms = true; + } + break; + case ATTRIBUTES: + var attrSub = (TbAttributeSubscription) subscription; + if (!newState.attrAllKeys && attrSub.isAllKeys()) { + newState.attrAllKeys = true; + continue; + } + if (newState.attrKeys == null) { + newState.attrKeys = new HashSet<>(attrSub.getKeyStates().keySet()); + } else { + newState.attrKeys.addAll(attrSub.getKeyStates().keySet()); + } + break; + case TIMESERIES: + var tsSub = (TbTimeSeriesSubscription) subscription; + if (!newState.tsAllKeys && tsSub.isAllKeys()) { + newState.tsAllKeys = true; + continue; + } + if (newState.tsKeys == null) { + newState.tsKeys = new HashSet<>(tsSub.getKeyStates().keySet()); + } else { + newState.tsKeys.addAll(tsSub.getKeyStates().keySet()); + } + break; + } + } + if (newState.equals(oldState)) { + return null; + } else { + this.state = newState; + return toEvent(ComponentLifecycleEvent.UPDATED); + } + } + + public TbEntitySubEvent toEvent(ComponentLifecycleEvent type) { + seqNumber++; + var result = TbEntitySubEvent.builder().tenantId(tenantId).entityId(entityId).type(type).seqNumber(seqNumber); + if (!ComponentLifecycleEvent.DELETED.equals(type)) { + result.info(state.copy(seqNumber)); + } + return result.build(); + } + + public boolean isNf() { + return state.notifications; + } + + + public boolean isEmpty() { + return state.isEmpty(); + } + + public TbSubscription registerPendingSubscription(TbSubscription subscription, TbEntitySubEvent event) { + if (TbSubscriptionType.ATTRIBUTES.equals(subscription.getType())) { + if (event != null) { + log.trace("[{}][{}] Registering new pending attributes subscription event: {} for subscription: {}", tenantId, entityId, event.getSeqNumber(), subscription.getSubscriptionId()); + pendingAttributesEvent = event.getSeqNumber(); + pendingAttributesEventTs = System.currentTimeMillis(); + pendingSubs.computeIfAbsent(pendingAttributesEvent, e -> new HashSet<>()).add(subscription); + } else if (pendingAttributesEvent > 0) { + log.trace("[{}][{}] Registering pending attributes subscription {} for event: {} ", tenantId, entityId, subscription.getSubscriptionId(), pendingAttributesEvent); + pendingSubs.computeIfAbsent(pendingAttributesEvent, e -> new HashSet<>()).add(subscription); + } else { + return subscription; + } + } else if (subscription instanceof TbTimeSeriesSubscription) { + if (event != null) { + log.trace("[{}][{}] Registering new pending time-series subscription event: {} for subscription: {}", tenantId, entityId, event.getSeqNumber(), subscription.getSubscriptionId()); + pendingTimeSeriesEvent = event.getSeqNumber(); + pendingTimeSeriesEventTs = System.currentTimeMillis(); + pendingSubs.computeIfAbsent(pendingTimeSeriesEvent, e -> new HashSet<>()).add(subscription); + } else if (pendingTimeSeriesEvent > 0) { + log.trace("[{}][{}] Registering pending time-series subscription {} for event: {} ", tenantId, entityId, subscription.getSubscriptionId(), pendingTimeSeriesEvent); + pendingSubs.computeIfAbsent(pendingTimeSeriesEvent, e -> new HashSet<>()).add(subscription); + } else { + return subscription; + } + } + return null; + } + + public Set> clearPendingSubscriptions(int seqNumber) { + if (pendingTimeSeriesEvent == seqNumber) { + pendingTimeSeriesEvent = 0; + pendingTimeSeriesEventTs = 0L; + } else if (pendingAttributesEvent == seqNumber) { + pendingAttributesEvent = 0; + pendingAttributesEventTs = 0L; + } + return pendingSubs.remove(seqNumber); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityRemoteSubsInfo.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityRemoteSubsInfo.java new file mode 100644 index 0000000000..df66b060da --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityRemoteSubsInfo.java @@ -0,0 +1,79 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.subscription; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.util.Pair; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Information about the local websocket subscriptions. + */ +@RequiredArgsConstructor +@Slf4j +public class TbEntityRemoteSubsInfo { + @Getter + private final TenantId tenantId; + @Getter + private final EntityId entityId; + @Getter + private final Map subs = new ConcurrentHashMap<>(); // By service ID + + public boolean updateAndCheckIsEmpty(String serviceId, TbEntitySubEvent event) { + var current = subs.get(serviceId); + if (current != null && current.seqNumber > event.getSeqNumber()) { + log.warn("[{}][{}] Duplicate subscription event received. Current: {}, Event: {}", + tenantId, entityId, current, event.getInfo()); + return false; + } + switch (event.getType()) { + case CREATED: + subs.put(serviceId, event.getInfo()); + break; + case UPDATED: + var newSubInfo = event.getInfo(); + if (newSubInfo.isEmpty()) { + subs.remove(serviceId); + return isEmpty(); + } else { + subs.put(serviceId, newSubInfo); + } + break; + case DELETED: + subs.remove(serviceId); + return isEmpty(); + } + return false; + } + + public boolean removeAndCheckIsEmpty(String serviceId) { + if (subs.remove(serviceId) != null) { + return subs.isEmpty(); + } else { + return false; + } + } + + public boolean isEmpty() { + return subs.isEmpty(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntitySubEvent.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntitySubEvent.java new file mode 100644 index 0000000000..7e2956b5f0 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntitySubEvent.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.subscription; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; + +import java.util.Set; + +/** + * Information about the local websocket subscriptions. + */ +@Builder +@Data +public class TbEntitySubEvent { + + private final TenantId tenantId; + private final EntityId entityId; + private final ComponentLifecycleEvent type; + private final TbSubscriptionsInfo info; + private final int seqNumber; + + public boolean hasTsOrAttrSub() { + return info != null && (info.tsAllKeys || info.attrAllKeys || info.tsKeys != null || info.attrKeys != null); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityUpdatesInfo.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityUpdatesInfo.java new file mode 100644 index 0000000000..a09da220d5 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityUpdatesInfo.java @@ -0,0 +1,30 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.subscription; + +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class TbEntityUpdatesInfo { + + volatile long attributesUpdateTs; + volatile long timeSeriesUpdateTs; + + public TbEntityUpdatesInfo(long ts) { + this.attributesUpdateTs = ts; + this.timeSeriesUpdateTs = ts; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java index 7685c8fbed..d968bd515f 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java @@ -15,28 +15,50 @@ */ package org.thingsboard.server.service.subscription; +import org.thingsboard.server.common.data.alarm.AlarmInfo; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.msg.queue.TbCallback; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent; -import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; +import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; -import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate; -import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; + +import java.util.List; public interface TbLocalSubscriptionService { - void addSubscription(TbSubscription subscription); + void addSubscription(TbSubscription subscription); + + void onSubEventCallback(TransportProtos.TbEntitySubEventCallbackProto subEventCallback, TbCallback callback); + + void onSubEventCallback(EntityId entityId, int seqNumber, TbEntityUpdatesInfo entityUpdatesInfo, TbCallback empty); void cancelSubscription(String sessionId, int subscriptionId); void cancelAllSessionSubscriptions(String sessionId); - void onSubscriptionUpdate(String sessionId, TelemetrySubscriptionUpdate update, TbCallback callback); + void onTimeSeriesUpdate(TransportProtos.TbSubUpdateProto tsUpdate, TbCallback callback); + + void onTimeSeriesUpdate(EntityId entityId, List update, TbCallback callback); + + void onAttributesUpdate(TransportProtos.TbSubUpdateProto attrUpdate, TbCallback callback); - void onSubscriptionUpdate(String sessionId, AlarmSubscriptionUpdate update, TbCallback callback); + void onAttributesUpdate(EntityId entityId, String scope, List update, TbCallback callback); - void onSubscriptionUpdate(String sessionId, int subscriptionId, NotificationsSubscriptionUpdate update, TbCallback callback); + void onAlarmUpdate(EntityId entityId, AlarmInfo alarm, boolean deleted, TbCallback callback); - void onApplicationEvent(PartitionChangeEvent event); + void onAlarmUpdate(TransportProtos.TbAlarmSubUpdateProto update, TbCallback callback); + + void onNotificationUpdate(EntityId entityId, NotificationsSubscriptionUpdate subscriptionUpdate, TbCallback callback); void onApplicationEvent(ClusterTopologyChangeEvent event); + + void onCoreStartupMsg(TransportProtos.CoreStartupMsg coreStartupMsg); + + void onNotificationRequestUpdate(TenantId tenantId, NotificationRequestUpdate update, TbCallback callback); + + void onNotificationUpdate(TransportProtos.NotificationsSubUpdateProto notificationsUpdate, TbCallback callback); + } diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscription.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscription.java index dfad6a41de..04007e949c 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscription.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscription.java @@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; @Data @@ -33,7 +34,9 @@ public abstract class TbSubscription { private final TenantId tenantId; private final EntityId entityId; private final TbSubscriptionType type; - private final BiConsumer, T> updateProcessor; + private final BiConsumer, T> updateProcessor; + + protected final AtomicInteger sequence = new AtomicInteger(); @Override public boolean equals(Object o) { diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java index 8905b1b3ad..1163e13077 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.subscription; +import org.apache.commons.lang3.StringUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.id.EntityId; @@ -32,6 +33,7 @@ import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType; @@ -39,29 +41,20 @@ import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionMgrMsgPr import org.thingsboard.server.gen.transport.TransportProtos.TbAlarmDeleteProto; import org.thingsboard.server.gen.transport.TransportProtos.TbAlarmUpdateProto; import org.thingsboard.server.gen.transport.TransportProtos.TbAttributeDeleteProto; -import org.thingsboard.server.gen.transport.TransportProtos.TbAttributeSubscriptionProto; import org.thingsboard.server.gen.transport.TransportProtos.TbAttributeUpdateProto; -import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionCloseProto; -import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionKetStateProto; -import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionProto; -import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdateProto; -import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionUpdateTsValue; +import org.thingsboard.server.gen.transport.TransportProtos.TbEntitySubEventProto; import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesDeleteProto; -import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesSubscriptionProto; import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesUpdateProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationUpdate; -import org.thingsboard.server.service.ws.notification.sub.NotificationsCountSubscription; -import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscription; import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate; -import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; import java.util.ArrayList; -import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -69,172 +62,118 @@ import java.util.UUID; public class TbSubscriptionUtils { - public static ToCoreMsg toNewSubscriptionProto(TbSubscription subscription) { + public static ToCoreMsg toSubEventProto(String serviceId, TbEntitySubEvent event) { SubscriptionMgrMsgProto.Builder msgBuilder = SubscriptionMgrMsgProto.newBuilder(); - TbSubscriptionProto subscriptionProto = TbSubscriptionProto.newBuilder() - .setServiceId(subscription.getServiceId()) - .setSessionId(subscription.getSessionId()) - .setSubscriptionId(subscription.getSubscriptionId()) - .setTenantIdMSB(subscription.getTenantId().getId().getMostSignificantBits()) - .setTenantIdLSB(subscription.getTenantId().getId().getLeastSignificantBits()) - .setEntityType(subscription.getEntityId().getEntityType().name()) - .setEntityIdMSB(subscription.getEntityId().getId().getMostSignificantBits()) - .setEntityIdLSB(subscription.getEntityId().getId().getLeastSignificantBits()).build(); - - switch (subscription.getType()) { - case TIMESERIES: - TbTimeseriesSubscription tSub = (TbTimeseriesSubscription) subscription; - TbTimeSeriesSubscriptionProto.Builder tSubProto = TbTimeSeriesSubscriptionProto.newBuilder() - .setSub(subscriptionProto) - .setAllKeys(tSub.isAllKeys()); - tSub.getKeyStates().forEach((key, value) -> tSubProto.addKeyStates( - TbSubscriptionKetStateProto.newBuilder().setKey(key).setTs(value).build())); - tSubProto.setStartTime(tSub.getStartTime()); - tSubProto.setEndTime(tSub.getEndTime()); - tSubProto.setLatestValues(tSub.isLatestValues()); - msgBuilder.setTelemetrySub(tSubProto.build()); - break; - case ATTRIBUTES: - TbAttributeSubscription aSub = (TbAttributeSubscription) subscription; - TbAttributeSubscriptionProto.Builder aSubProto = TbAttributeSubscriptionProto.newBuilder() - .setSub(subscriptionProto) - .setAllKeys(aSub.isAllKeys()) - .setScope(aSub.getScope().name()); - aSub.getKeyStates().forEach((key, value) -> aSubProto.addKeyStates( - TbSubscriptionKetStateProto.newBuilder().setKey(key).setTs(value).build())); - msgBuilder.setAttributeSub(aSubProto.build()); - break; - case ALARMS: - TbAlarmsSubscription alarmSub = (TbAlarmsSubscription) subscription; - TransportProtos.TbAlarmSubscriptionProto.Builder alarmSubProto = TransportProtos.TbAlarmSubscriptionProto.newBuilder() - .setSub(subscriptionProto) - .setTs(alarmSub.getTs()); - msgBuilder.setAlarmSub(alarmSubProto.build()); - break; - case NOTIFICATIONS: - NotificationsSubscription notificationsSub = (NotificationsSubscription) subscription; - msgBuilder.setNotificationsSub(TransportProtos.NotificationsSubscriptionProto.newBuilder() - .setSub(subscriptionProto) - .setLimit(notificationsSub.getLimit())); - break; - case NOTIFICATIONS_COUNT: - NotificationsCountSubscription notificationsCountSub = (NotificationsCountSubscription) subscription; - msgBuilder.setNotificationsCountSub(TransportProtos.NotificationsCountSubscriptionProto.newBuilder() - .setSub(subscriptionProto)); - break; + var builder = TbEntitySubEventProto.newBuilder() + .setServiceId(serviceId) + .setSeqNumber(event.getSeqNumber()) + .setTenantIdMSB(event.getTenantId().getId().getMostSignificantBits()) + .setTenantIdLSB(event.getTenantId().getId().getLeastSignificantBits()) + .setEntityType(event.getEntityId().getEntityType().name()) + .setEntityIdMSB(event.getEntityId().getId().getMostSignificantBits()) + .setEntityIdLSB(event.getEntityId().getId().getLeastSignificantBits()) + .setType(event.getType().name()); + TbSubscriptionsInfo info = event.getInfo(); + if (info != null) { + builder.setNotifications(info.notifications) + .setAlarms(info.alarms) + .setTsAllKeys(info.tsAllKeys) + .setAttrAllKeys(info.attrAllKeys); + if (info.tsKeys != null) { + builder.addAllTsKeys(info.tsKeys); + } + if (info.attrKeys != null) { + builder.addAllAttrKeys(info.attrKeys); + } } - return ToCoreMsg.newBuilder().setToSubscriptionMgrMsg(msgBuilder.build()).build(); + msgBuilder.setSubEvent(builder); + return ToCoreMsg.newBuilder().setToSubscriptionMgrMsg(msgBuilder).build(); } - public static ToCoreMsg toCloseSubscriptionProto(TbSubscription subscription) { - SubscriptionMgrMsgProto.Builder msgBuilder = SubscriptionMgrMsgProto.newBuilder(); - TbSubscriptionCloseProto closeProto = TbSubscriptionCloseProto.newBuilder() - .setSessionId(subscription.getSessionId()) - .setSubscriptionId(subscription.getSubscriptionId()).build(); - msgBuilder.setSubClose(closeProto); - return ToCoreMsg.newBuilder().setToSubscriptionMgrMsg(msgBuilder.build()).build(); + public static ToCoreNotificationMsg toProto(UUID id, int seqNumber, TbEntityUpdatesInfo update) { + TransportProtos.TbEntitySubEventCallbackProto.Builder updateProto = TransportProtos.TbEntitySubEventCallbackProto.newBuilder() + .setEntityIdMSB(id.getMostSignificantBits()) + .setEntityIdLSB(id.getLeastSignificantBits()) + .setSeqNumber(seqNumber) + .setAttributesUpdateTs(update.attributesUpdateTs) + .setTimeSeriesUpdateTs(update.timeSeriesUpdateTs); + return ToCoreNotificationMsg.newBuilder() + .setToLocalSubscriptionServiceMsg( + TransportProtos.LocalSubscriptionServiceMsgProto.newBuilder() + .setSubEventCallback(updateProto) + .build()) + .build(); } - public static TbSubscription fromProto(TbAttributeSubscriptionProto attributeSub) { - TbSubscriptionProto subProto = attributeSub.getSub(); - TbAttributeSubscription.TbAttributeSubscriptionBuilder builder = TbAttributeSubscription.builder() - .serviceId(subProto.getServiceId()) - .sessionId(subProto.getSessionId()) - .subscriptionId(subProto.getSubscriptionId()) - .entityId(EntityIdFactory.getByTypeAndUuid(subProto.getEntityType(), new UUID(subProto.getEntityIdMSB(), subProto.getEntityIdLSB()))) - .tenantId(TenantId.fromUUID(new UUID(subProto.getTenantIdMSB(), subProto.getTenantIdLSB()))); - - builder.scope(TbAttributeSubscriptionScope.valueOf(attributeSub.getScope())); - builder.allKeys(attributeSub.getAllKeys()); - Map keyStates = new HashMap<>(); - attributeSub.getKeyStatesList().forEach(ksProto -> keyStates.put(ksProto.getKey(), ksProto.getTs())); - builder.keyStates(keyStates); - return builder.build(); - } - public static TbSubscription fromProto(TbTimeSeriesSubscriptionProto telemetrySub) { - TbSubscriptionProto subProto = telemetrySub.getSub(); - TbTimeseriesSubscription.TbTimeseriesSubscriptionBuilder builder = TbTimeseriesSubscription.builder() - .serviceId(subProto.getServiceId()) - .sessionId(subProto.getSessionId()) - .subscriptionId(subProto.getSubscriptionId()) - .entityId(EntityIdFactory.getByTypeAndUuid(subProto.getEntityType(), new UUID(subProto.getEntityIdMSB(), subProto.getEntityIdLSB()))) - .tenantId(TenantId.fromUUID(new UUID(subProto.getTenantIdMSB(), subProto.getTenantIdLSB()))); - - builder.allKeys(telemetrySub.getAllKeys()); - Map keyStates = new HashMap<>(); - telemetrySub.getKeyStatesList().forEach(ksProto -> keyStates.put(ksProto.getKey(), ksProto.getTs())); - builder.startTime(telemetrySub.getStartTime()); - builder.endTime(telemetrySub.getEndTime()); - builder.latestValues(telemetrySub.getLatestValues()); - builder.keyStates(keyStates); + public static TbEntitySubEvent fromProto(TbEntitySubEventProto proto) { + ComponentLifecycleEvent event = ComponentLifecycleEvent.valueOf(proto.getType()); + var builder = TbEntitySubEvent.builder() + .tenantId(TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB()))) + .seqNumber(proto.getSeqNumber()) + .entityId(EntityIdFactory.getByTypeAndUuid(proto.getEntityType(), new UUID(proto.getEntityIdMSB(), proto.getEntityIdLSB()))) + .type(event); + if (!ComponentLifecycleEvent.DELETED.equals(event)) { + builder.info(new TbSubscriptionsInfo(proto.getNotifications(), proto.getAlarms(), + proto.getTsAllKeys(), proto.getTsKeysCount() > 0 ? new HashSet<>(proto.getTsKeysList()) : null, + proto.getAttrAllKeys(), proto.getAttrKeysCount() > 0 ? new HashSet<>(proto.getAttrKeysList()) : null, + proto.getSeqNumber())); + } return builder.build(); } - public static TbSubscription fromProto(TransportProtos.TbAlarmSubscriptionProto alarmSub) { - TbSubscriptionProto subProto = alarmSub.getSub(); - TbAlarmsSubscription.TbAlarmsSubscriptionBuilder builder = TbAlarmsSubscription.builder() - .serviceId(subProto.getServiceId()) - .sessionId(subProto.getSessionId()) - .subscriptionId(subProto.getSubscriptionId()) - .entityId(EntityIdFactory.getByTypeAndUuid(subProto.getEntityType(), new UUID(subProto.getEntityIdMSB(), subProto.getEntityIdLSB()))) - .tenantId(TenantId.fromUUID(new UUID(subProto.getTenantIdMSB(), subProto.getTenantIdLSB()))); - builder.ts(alarmSub.getTs()); - return builder.build(); + public static AlarmSubscriptionUpdate fromProto(TransportProtos.TbAlarmSubUpdateProto proto) { + if (proto.getErrorCode() > 0) { + return new AlarmSubscriptionUpdate(SubscriptionErrorCode.forCode(proto.getErrorCode()), proto.getErrorMsg()); + } else { + AlarmInfo alarm = JacksonUtil.fromString(proto.getAlarm(), AlarmInfo.class); + return new AlarmSubscriptionUpdate(alarm, proto.getDeleted()); + } } - public static NotificationsSubscription fromProto(TransportProtos.NotificationsSubscriptionProto notificationsSub) { - TbSubscriptionProto sub = notificationsSub.getSub(); - return NotificationsSubscription.builder() - .serviceId(sub.getServiceId()) - .sessionId(sub.getSessionId()) - .subscriptionId(sub.getSubscriptionId()) - .tenantId(TenantId.fromUUID(new UUID(sub.getTenantIdMSB(), sub.getTenantIdLSB()))) - .entityId(EntityIdFactory.getByTypeAndUuid(sub.getEntityType(), new UUID(sub.getEntityIdMSB(), sub.getEntityIdLSB()))) - .limit(notificationsSub.getLimit()) - .build(); + public static NotificationsSubscriptionUpdate fromProto(TransportProtos.NotificationsSubUpdateProto proto) { + NotificationsSubscriptionUpdate update; + if (StringUtils.isNotEmpty(proto.getNotificationUpdate())) { + NotificationUpdate notificationUpdate = JacksonUtil.fromString(proto.getNotificationUpdate(), NotificationUpdate.class); + update = new NotificationsSubscriptionUpdate(notificationUpdate); + } else { + NotificationRequestUpdate notificationRequestUpdate = JacksonUtil.fromString(proto.getNotificationRequestUpdate(), NotificationRequestUpdate.class); + update = new NotificationsSubscriptionUpdate(notificationRequestUpdate); + } + return update; } - public static NotificationsCountSubscription fromProto(TransportProtos.NotificationsCountSubscriptionProto notificationsCountSub) { - TbSubscriptionProto sub = notificationsCountSub.getSub(); - return NotificationsCountSubscription.builder() - .serviceId(sub.getServiceId()) - .sessionId(sub.getSessionId()) - .subscriptionId(sub.getSubscriptionId()) - .tenantId(TenantId.fromUUID(new UUID(sub.getTenantIdMSB(), sub.getTenantIdLSB()))) - .entityId(EntityIdFactory.getByTypeAndUuid(sub.getEntityType(), new UUID(sub.getEntityIdMSB(), sub.getEntityIdLSB()))) + public static ToCoreNotificationMsg toAlarmSubUpdateToProto(EntityId entityId, AlarmInfo alarmInfo, boolean deleted) { + TransportProtos.TbAlarmSubUpdateProto.Builder updateProto = TransportProtos.TbAlarmSubUpdateProto.newBuilder() + .setEntityIdMSB(entityId.getId().getMostSignificantBits()) + .setEntityIdLSB(entityId.getId().getLeastSignificantBits()) + .setAlarm(JacksonUtil.toString(alarmInfo)) + .setDeleted(deleted); + return ToCoreNotificationMsg.newBuilder() + .setToLocalSubscriptionServiceMsg( + TransportProtos.LocalSubscriptionServiceMsgProto.newBuilder() + .setAlarmUpdate(updateProto) + .build()) .build(); } - public static TelemetrySubscriptionUpdate fromProto(TbSubscriptionUpdateProto proto) { - if (proto.getErrorCode() > 0) { - return new TelemetrySubscriptionUpdate(proto.getSubscriptionId(), SubscriptionErrorCode.forCode(proto.getErrorCode()), proto.getErrorMsg()); - } else { - Map> data = new TreeMap<>(); - proto.getDataList().forEach(v -> { - List values = data.computeIfAbsent(v.getKey(), k -> new ArrayList<>()); - for (int i = 0; i < v.getTsValueCount(); i++) { - Object[] value = new Object[2]; - TbSubscriptionUpdateTsValue tsValue = v.getTsValue(i); - value[0] = tsValue.getTs(); - value[1] = tsValue.hasValue() ? tsValue.getValue() : null; - values.add(value); - } - }); - return new TelemetrySubscriptionUpdate(proto.getSubscriptionId(), data); + public static ToCoreNotificationMsg notificationsSubUpdateToProto(EntityId entityId, NotificationsSubscriptionUpdate update) { + TransportProtos.NotificationsSubUpdateProto.Builder updateProto = TransportProtos.NotificationsSubUpdateProto.newBuilder() + .setEntityIdMSB(entityId.getId().getMostSignificantBits()) + .setEntityIdLSB(entityId.getId().getLeastSignificantBits()); + if (update.getNotificationUpdate() != null) { + updateProto.setNotificationUpdate(JacksonUtil.toString(update.getNotificationUpdate())); } - } - - public static AlarmSubscriptionUpdate fromProto(TransportProtos.TbAlarmSubscriptionUpdateProto proto) { - if (proto.getErrorCode() > 0) { - return new AlarmSubscriptionUpdate(proto.getSubscriptionId(), SubscriptionErrorCode.forCode(proto.getErrorCode()), proto.getErrorMsg()); - } else { - AlarmInfo alarm = JacksonUtil.fromString(proto.getAlarm(), AlarmInfo.class); - return new AlarmSubscriptionUpdate(proto.getSubscriptionId(), alarm, proto.getDeleted()); + if (update.getNotificationRequestUpdate() != null) { + updateProto.setNotificationRequestUpdate(JacksonUtil.toString(update.getNotificationRequestUpdate())); } + return ToCoreNotificationMsg.newBuilder() + .setToLocalSubscriptionServiceMsg(TransportProtos.LocalSubscriptionServiceMsgProto.newBuilder() + .setNotificationsUpdate(updateProto) + .build()) + .build(); } - public static ToCoreMsg toTimeseriesUpdateProto(TenantId tenantId, EntityId entityId, List ts) { TbTimeSeriesUpdateProto.Builder builder = TbTimeSeriesUpdateProto.newBuilder(); builder.setEntityType(entityId.getEntityType().name()); @@ -317,6 +256,31 @@ public class TbSubscriptionUtils { return TsKvProto.newBuilder().setTs(ts).setKv(dataBuilder); } + private static TransportProtos.TsValueProto toTsValueProto(long ts, KvEntry attr) { + TransportProtos.TsValueProto.Builder dataBuilder = TransportProtos.TsValueProto.newBuilder(); + dataBuilder.setTs(ts); + dataBuilder.setType(KeyValueType.forNumber(attr.getDataType().ordinal())); + switch (attr.getDataType()) { + case BOOLEAN: + attr.getBooleanValue().ifPresent(dataBuilder::setBoolV); + break; + case LONG: + attr.getLongValue().ifPresent(dataBuilder::setLongV); + break; + case DOUBLE: + attr.getDoubleValue().ifPresent(dataBuilder::setDoubleV); + break; + case JSON: + attr.getJsonValue().ifPresent(dataBuilder::setJsonV); + break; + case STRING: + attr.getStrValue().ifPresent(dataBuilder::setStringV); + break; + } + return dataBuilder.build(); + } + + public static EntityId toEntityId(String entityType, long entityIdMSB, long entityIdLSB) { return EntityIdFactory.getByTypeAndUuid(entityType, new UUID(entityIdMSB, entityIdLSB)); } @@ -356,6 +320,35 @@ public class TbSubscriptionUtils { return entry; } + public static List toTsKvEntityList(String key, List dataList) { + List result = new ArrayList<>(dataList.size()); + dataList.forEach(proto -> result.add(new BasicTsKvEntry(proto.getTs(), getKvEntry(key, proto)))); + return result; + } + + private static KvEntry getKvEntry(String key, TransportProtos.TsValueProto proto) { + KvEntry entry = null; + DataType type = DataType.values()[proto.getType().getNumber()]; + switch (type) { + case BOOLEAN: + entry = new BooleanDataEntry(key, proto.getBoolV()); + break; + case LONG: + entry = new LongDataEntry(key, proto.getLongV()); + break; + case DOUBLE: + entry = new DoubleDataEntry(key, proto.getDoubleV()); + break; + case STRING: + entry = new StringDataEntry(key, proto.getStringV()); + break; + case JSON: + entry = new JsonDataEntry(key, proto.getJsonV()); + break; + } + return entry; + } + public static ToCoreMsg toAlarmUpdateProto(TenantId tenantId, EntityId entityId, AlarmInfo alarm) { TbAlarmUpdateProto.Builder builder = TbAlarmUpdateProto.newBuilder(); builder.setEntityType(entityId.getEntityType().name()); @@ -382,23 +375,6 @@ public class TbSubscriptionUtils { return ToCoreMsg.newBuilder().setToSubscriptionMgrMsg(msgBuilder.build()).build(); } - public static ToCoreNotificationMsg notificationsSubUpdateToProto(TbSubscription subscription, NotificationsSubscriptionUpdate update) { - TransportProtos.NotificationsSubscriptionUpdateProto.Builder updateProto = TransportProtos.NotificationsSubscriptionUpdateProto.newBuilder() - .setSessionId(subscription.getSessionId()) - .setSubscriptionId(subscription.getSubscriptionId()); - if (update.getNotificationUpdate() != null) { - updateProto.setNotificationUpdate(JacksonUtil.toString(update.getNotificationUpdate())); - } - if (update.getNotificationRequestUpdate() != null) { - updateProto.setNotificationRequestUpdate(JacksonUtil.toString(update.getNotificationRequestUpdate())); - } - return ToCoreNotificationMsg.newBuilder() - .setToLocalSubscriptionServiceMsg(TransportProtos.LocalSubscriptionServiceMsgProto.newBuilder() - .setNotificationsSubUpdate(updateProto) - .build()) - .build(); - } - public static ToCoreMsg notificationUpdateToProto(TenantId tenantId, UserId recipientId, NotificationUpdate notificationUpdate) { TransportProtos.NotificationUpdateProto updateProto = TransportProtos.NotificationUpdateProto.newBuilder() .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) @@ -427,4 +403,49 @@ public class TbSubscriptionUtils { .build(); } + public static List fromProto(TransportProtos.TbSubUpdateProto proto) { + List result = new ArrayList<>(); + for (var p : proto.getDataList()) { + result.addAll(toTsKvEntityList(p.getKey(), p.getTsValueList())); + } + return result; + } + + static ToCoreNotificationMsg toProto(EntityId entityId, List updates) { + return toProto(true, null, entityId, updates); + } + + static ToCoreNotificationMsg toProto(String scope, EntityId entityId, List updates) { + return toProto(false, scope, entityId, updates); + } + + static ToCoreNotificationMsg toProto(boolean timeSeries, String scope, EntityId entityId, List updates) { + TransportProtos.TbSubUpdateProto.Builder builder = TransportProtos.TbSubUpdateProto.newBuilder(); + + builder.setEntityIdMSB(entityId.getId().getMostSignificantBits()); + builder.setEntityIdLSB(entityId.getId().getLeastSignificantBits()); + + Map> data = new TreeMap<>(); + + for (TsKvEntry tsEntry : updates) { + data.computeIfAbsent(tsEntry.getKey(), k -> new ArrayList<>()).add(toTsValueProto(tsEntry.getTs(), tsEntry)); + } + + data.forEach((key, value) -> { + TransportProtos.TsValueListProto.Builder dataBuilder = TransportProtos.TsValueListProto.newBuilder(); + dataBuilder.setKey(key); + dataBuilder.addAllTsValue(value); + builder.addData(dataBuilder.build()); + }); + + var result = TransportProtos.LocalSubscriptionServiceMsgProto.newBuilder(); + if (timeSeries) { + result.setTsUpdate(builder); + } else { + builder.setScope(scope); + result.setAttrUpdate(builder); + } + return ToCoreNotificationMsg.newBuilder().setToLocalSubscriptionServiceMsg(result).build(); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionsInfo.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionsInfo.java new file mode 100644 index 0000000000..93b1fb0857 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionsInfo.java @@ -0,0 +1,56 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.subscription; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; +import lombok.ToString; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Information about the local websocket subscriptions. + */ +@RequiredArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(exclude = {"seqNumber"}) +@ToString +public class TbSubscriptionsInfo { + + protected boolean notifications; + protected boolean alarms; + protected boolean tsAllKeys; + protected Set tsKeys; + protected boolean attrAllKeys; + protected Set attrKeys; + protected int seqNumber; + + public boolean isEmpty() { + return !notifications && !alarms && !tsAllKeys && !attrAllKeys && tsKeys == null && attrKeys == null; + } + + protected TbSubscriptionsInfo copy() { + return copy(0); + } + + protected TbSubscriptionsInfo copy(int seqNumber) { + return new TbSubscriptionsInfo(notifications, alarms, tsAllKeys, tsKeys, attrAllKeys, attrKeys, seqNumber); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbTimeSeriesSubscription.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbTimeSeriesSubscription.java new file mode 100644 index 0000000000..d3ebe4cd3f --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbTimeSeriesSubscription.java @@ -0,0 +1,64 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.subscription; + +import lombok.Builder; +import lombok.Getter; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; + +import java.util.Map; +import java.util.function.BiConsumer; + +public class TbTimeSeriesSubscription extends TbSubscription { + + @Getter + private final long queryTs; + @Getter + private final boolean allKeys; + @Getter + private final Map keyStates; + @Getter + private final long startTime; + @Getter + private final long endTime; + @Getter + private final boolean latestValues; + + @Builder + public TbTimeSeriesSubscription(String serviceId, String sessionId, int subscriptionId, TenantId tenantId, EntityId entityId, + BiConsumer, TelemetrySubscriptionUpdate> updateProcessor, + long queryTs, boolean allKeys, Map keyStates, long startTime, long endTime, boolean latestValues) { + super(serviceId, sessionId, subscriptionId, tenantId, entityId, TbSubscriptionType.TIMESERIES, updateProcessor); + this.queryTs = queryTs; + this.allKeys = allKeys; + this.keyStates = keyStates; + this.startTime = startTime; + this.endTime = endTime; + this.latestValues = latestValues; + } + + @Override + public boolean equals(Object o) { + return super.equals(o); + } + + @Override + public int hashCode() { + return super.hashCode(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbTimeseriesSubscription.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbTimeseriesSubscription.java deleted file mode 100644 index 400cf07e7e..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbTimeseriesSubscription.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.subscription; - -import lombok.Builder; -import lombok.Getter; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; - -import java.util.Map; -import java.util.function.BiConsumer; - -public class TbTimeseriesSubscription extends TbSubscription { - - @Getter - private final boolean allKeys; - @Getter - private final Map keyStates; - @Getter - private final long startTime; - @Getter - private final long endTime; - @Getter - private final boolean latestValues; - - @Builder - public TbTimeseriesSubscription(String serviceId, String sessionId, int subscriptionId, TenantId tenantId, EntityId entityId, - BiConsumer, TelemetrySubscriptionUpdate> updateProcessor, - boolean allKeys, Map keyStates, long startTime, long endTime, boolean latestValues) { - super(serviceId, sessionId, subscriptionId, tenantId, entityId, TbSubscriptionType.TIMESERIES, updateProcessor); - this.allKeys = allKeys; - this.keyStates = keyStates; - this.startTime = startTime; - this.endTime = endTime; - this.latestValues = latestValues; - } - - @Override - public boolean equals(Object o) { - return super.equals(o); - } - - @Override - public int hashCode() { - return super.hashCode(); - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java index c8f8f945f9..bc1735f30b 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java @@ -25,15 +25,15 @@ import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.sync.ie.EntityExportData; import org.thingsboard.server.common.data.sync.ie.EntityImportResult; import org.thingsboard.server.common.data.util.ThrowingRunnable; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.relation.RelationService; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.dao.util.limits.LimitedApi; import org.thingsboard.server.dao.util.limits.RateLimitService; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.TbNotificationEntityService; import org.thingsboard.server.service.sync.ie.exporting.EntityExportService; import org.thingsboard.server.service.sync.ie.exporting.impl.BaseEntityExportService; @@ -65,9 +65,10 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS private final TbNotificationEntityService entityNotificationService; protected static final List SUPPORTED_ENTITY_TYPES = List.of( - EntityType.CUSTOMER, EntityType.ASSET_PROFILE, EntityType.ASSET, EntityType.RULE_CHAIN, - EntityType.DASHBOARD, EntityType.DEVICE_PROFILE, EntityType.DEVICE, - EntityType.ENTITY_VIEW, EntityType.WIDGETS_BUNDLE, + EntityType.CUSTOMER, EntityType.RULE_CHAIN, EntityType.TB_RESOURCE, + EntityType.DASHBOARD, EntityType.ASSET_PROFILE, EntityType.ASSET, + EntityType.DEVICE_PROFILE, EntityType.DEVICE, + EntityType.ENTITY_VIEW, EntityType.WIDGET_TYPE, EntityType.WIDGETS_BUNDLE, EntityType.NOTIFICATION_TEMPLATE, EntityType.NOTIFICATION_TARGET, EntityType.NOTIFICATION_RULE ); @@ -119,8 +120,8 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS relationService.saveRelations(ctx.getTenantId(), new ArrayList<>(ctx.getRelations())); for (EntityRelation relation : ctx.getRelations()) { - entityNotificationService.notifyRelation(ctx.getTenantId(), null, - relation, ctx.getUser(), ActionType.RELATION_ADD_OR_UPDATE, relation); + entityNotificationService.logEntityRelationAction(ctx.getTenantId(), null, + relation, ctx.getUser(), ActionType.RELATION_ADD_OR_UPDATE, null, relation); } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/DefaultExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/DefaultExportableEntitiesService.java index 3358e5182f..d112075a51 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/DefaultExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/DefaultExportableEntitiesService.java @@ -118,6 +118,16 @@ public class DefaultExportableEntitiesService implements ExportableEntitiesServi } } + @Override + public PageData findEntitiesIdsByTenantId(TenantId tenantId, EntityType entityType, PageLink pageLink) { + ExportableEntityDao dao = getExportableEntityDao(entityType); + if (dao != null) { + return dao.findIdsByTenantId(tenantId.getId(), pageLink); + } else { + return new PageData<>(); + } + } + @Override public I getExternalIdByInternal(I internalId) { ExportableEntityDao dao = getExportableEntityDao(internalId.getEntityType()); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/ExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/ExportableEntitiesService.java index c5c26c4476..eee102961d 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/ExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/ExportableEntitiesService.java @@ -35,6 +35,8 @@ public interface ExportableEntitiesService { , I extends EntityId> PageData findEntitiesByTenantId(TenantId tenantId, EntityType entityType, PageLink pageLink); + PageData findEntitiesIdsByTenantId(TenantId tenantId, EntityType entityType, PageLink pageLink); + I getExternalIdByInternal(I internalId); void removeById(TenantId tenantId, I id); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/AssetProfileExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/AssetProfileExportService.java index 340298db7a..d8a3e76b7f 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/AssetProfileExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/AssetProfileExportService.java @@ -33,6 +33,8 @@ public class AssetProfileExportService extends BaseEntityExportService ctx, AssetProfile assetProfile, EntityExportData exportData) { assetProfile.setDefaultDashboardId(getExternalIdOrElseInternal(ctx, assetProfile.getDefaultDashboardId())); assetProfile.setDefaultRuleChainId(getExternalIdOrElseInternal(ctx, assetProfile.getDefaultRuleChainId())); + assetProfile.setDefaultEdgeRuleChainId(getExternalIdOrElseInternal(ctx, assetProfile.getDefaultEdgeRuleChainId())); + imageService.inlineImage(assetProfile); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/BaseEntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/BaseEntityExportService.java index 09b96d68fa..3cf05ac503 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/BaseEntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/BaseEntityExportService.java @@ -26,6 +26,7 @@ import org.thingsboard.server.service.sync.vc.data.EntitiesExportCtx; import java.util.Collection; import java.util.Set; +import java.util.regex.Pattern; import java.util.UUID; import java.util.function.Function; import java.util.stream.Stream; @@ -47,8 +48,8 @@ public abstract class BaseEntityExportService getSupportedEntityTypes(); - protected void replaceUuidsRecursively(EntitiesExportCtx ctx, JsonNode node, Set skipFieldsSet) { - JacksonUtil.replaceUuidsRecursively(node, skipFieldsSet, uuid -> getExternalIdOrElseInternalByUuid(ctx, uuid)); + protected void replaceUuidsRecursively(EntitiesExportCtx ctx, JsonNode node, Set skippedRootFields, Pattern includedFieldsPattern) { + JacksonUtil.replaceUuidsRecursively(node, skippedRootFields, includedFieldsPattern, uuid -> getExternalIdOrElseInternalByUuid(ctx, uuid), true); } protected Stream toExternalIds(Collection internalIds, Function entityIdCreator, diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DashboardExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DashboardExportService.java index b408aadd68..e2fd8e1666 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DashboardExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DashboardExportService.java @@ -29,6 +29,8 @@ import org.thingsboard.server.service.sync.vc.data.EntitiesExportCtx; import java.util.Collections; import java.util.Set; +import static org.thingsboard.server.service.sync.ie.importing.impl.DashboardImportService.WIDGET_CONFIG_PROCESSED_FIELDS_PATTERN; + @Service @TbCoreComponent public class DashboardExportService extends BaseEntityExportService> { @@ -41,11 +43,12 @@ public class DashboardExportService extends BaseEntityExportService ctx, I entityId) throws ThingsboardException { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DeviceProfileExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DeviceProfileExportService.java index 82ce17893a..8c6269831d 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DeviceProfileExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DeviceProfileExportService.java @@ -33,6 +33,8 @@ public class DeviceProfileExportService extends BaseEntityExportService ctx, DeviceProfile deviceProfile, EntityExportData exportData) { deviceProfile.setDefaultDashboardId(getExternalIdOrElseInternal(ctx, deviceProfile.getDefaultDashboardId())); deviceProfile.setDefaultRuleChainId(getExternalIdOrElseInternal(ctx, deviceProfile.getDefaultRuleChainId())); + deviceProfile.setDefaultEdgeRuleChainId(getExternalIdOrElseInternal(ctx, deviceProfile.getDefaultEdgeRuleChainId())); + imageService.inlineImage(deviceProfile); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationRuleExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationRuleExportService.java index 0906fb83ef..a2b9c54403 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationRuleExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationRuleExportService.java @@ -28,9 +28,9 @@ import org.thingsboard.server.common.data.notification.rule.DefaultNotificationR import org.thingsboard.server.common.data.notification.rule.EscalatedNotificationRuleRecipientsConfig; import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.rule.NotificationRuleRecipientsConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.DeviceActivityNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.DeviceActivityNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.sync.ie.EntityExportData; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.sync.vc.data.EntitiesExportCtx; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationTargetExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationTargetExportService.java index f0a5da3efc..a5ef322c56 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationTargetExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationTargetExportService.java @@ -43,6 +43,9 @@ public class NotificationTargetExportService extends BaseEntityExportService> { + + @Override + protected void setAdditionalExportData(EntitiesExportCtx ctx, TbResource resource, EntityExportData exportData) throws ThingsboardException { + super.setAdditionalExportData(ctx, resource, exportData); + resource.setPreview(null); // will be generated on import + } + + @Override + public Set getSupportedEntityTypes() { + return Set.of(EntityType.TB_RESOURCE); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/RuleChainExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/RuleChainExportService.java index 83a0be5be2..1fd7626ab5 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/RuleChainExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/RuleChainExportService.java @@ -30,6 +30,8 @@ import java.util.Collections; import java.util.Optional; import java.util.Set; +import static org.thingsboard.server.service.sync.ie.importing.impl.RuleChainImportService.PROCESSED_CONFIG_FIELDS_PATTERN; + @Service @TbCoreComponent @RequiredArgsConstructor @@ -47,7 +49,7 @@ public class RuleChainExportService extends BaseEntityExportService { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetTypeExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetTypeExportService.java new file mode 100644 index 0000000000..5ff5e88700 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetTypeExportService.java @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.sync.ie.exporting.impl; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.WidgetTypeId; +import org.thingsboard.server.common.data.sync.ie.WidgetTypeExportData; +import org.thingsboard.server.common.data.widget.WidgetTypeDetails; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.sync.vc.data.EntitiesExportCtx; + +import java.util.Set; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +public class WidgetTypeExportService extends BaseEntityExportService { + + @Override + protected void setRelatedEntities(EntitiesExportCtx ctx, WidgetTypeDetails widgetTypeDetails, WidgetTypeExportData exportData) { + if (widgetTypeDetails.getTenantId() == null || widgetTypeDetails.getTenantId().isNullUid()) { + throw new IllegalArgumentException("Export of system Widget Type is not allowed"); + } + imageService.inlineImages(widgetTypeDetails); + } + + @Override + protected WidgetTypeExportData newExportData() { + return new WidgetTypeExportData(); + } + + @Override + public Set getSupportedEntityTypes() { + return Set.of(EntityType.WIDGET_TYPE); + } + +} \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetsBundleExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetsBundleExportService.java index 7024f36623..46078ac293 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetsBundleExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetsBundleExportService.java @@ -41,9 +41,10 @@ public class WidgetsBundleExportService extends BaseEntityExportService widgets = widgetTypeService.findWidgetTypesDetailsByTenantIdAndBundleAlias(ctx.getTenantId(), widgetsBundle.getAlias()); - exportData.setWidgets(widgets); + List fqns = widgetTypeService.findWidgetFqnsByWidgetsBundleId(ctx.getTenantId(), widgetsBundle.getId()); + exportData.setFqns(fqns); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java index 45805c5939..30765bd959 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java @@ -22,6 +22,7 @@ import com.google.gson.JsonPrimitive; import lombok.Data; import lombok.SneakyThrows; import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.commons.lang3.tuple.Pair; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; @@ -46,7 +47,7 @@ import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportColumn import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportRequest; import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportResult; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.controller.BaseController; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.service.action.EntityActionService; @@ -57,7 +58,7 @@ import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; import org.thingsboard.server.utils.CsvUtils; -import org.thingsboard.server.utils.TypeCastUtil; +import org.thingsboard.server.common.data.util.TypeCastUtil; import javax.annotation.Nullable; import javax.annotation.PostConstruct; @@ -269,7 +270,7 @@ public abstract class AbstractBulkImportService castResult = TypeCastUtil.castValue(entry.getValue()); + Pair castResult = TypeCastUtil.castValue(entry.getValue()); entityData.getKvs().put(entry.getKey(), new ParsedValue(castResult.getValue(), castResult.getKey())); } }); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/AssetProfileImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/AssetProfileImportService.java index 7f0af64ed7..68481bad66 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/AssetProfileImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/AssetProfileImportService.java @@ -46,6 +46,7 @@ public class AssetProfileImportService extends BaseEntityImportService exportData, IdProvider idProvider) { assetProfile.setDefaultRuleChainId(idProvider.getInternalId(assetProfile.getDefaultRuleChainId())); assetProfile.setDefaultDashboardId(idProvider.getInternalId(assetProfile.getDefaultDashboardId())); + assetProfile.setDefaultEdgeRuleChainId(idProvider.getInternalId(assetProfile.getDefaultEdgeRuleChainId())); return assetProfile; } @@ -58,8 +59,8 @@ public class AssetProfileImportService extends BaseEntityImportService { - entityNotificationService.notifyRelation(tenantId, null, - existingRelation, ctx.getUser(), ActionType.RELATION_DELETED, existingRelation); + entityNotificationService.logEntityRelationAction(tenantId, null, + existingRelation, ctx.getUser(), ActionType.RELATION_DELETED, null, existingRelation); }); } else if (Objects.equal(relation.getAdditionalInfo(), existingRelation.getAdditionalInfo())) { relationsMap.remove(relation); @@ -266,8 +267,8 @@ public abstract class BaseEntityImportService skipFieldsSet, LinkedHashSet hints) { - JacksonUtil.replaceUuidsRecursively(entityAlias, skipFieldsSet, - uuid -> idProvider.getInternalIdByUuid(uuid, ctx.isFinalImportAttempt(), hints).map(EntityId::getId).orElse(uuid)); + protected void replaceIdsRecursively(EntitiesImportCtx ctx, IdProvider idProvider, JsonNode json, + Set skippedRootFields, Pattern includedFieldsPattern, + LinkedHashSet hints) { + JacksonUtil.replaceUuidsRecursively(json, skippedRootFields, includedFieldsPattern, + uuid -> idProvider.getInternalIdByUuid(uuid, ctx.isFinalImportAttempt(), hints) + .map(EntityId::getId).orElse(uuid), true); } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DashboardImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DashboardImportService.java index 250026c71c..658ecf744a 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DashboardImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DashboardImportService.java @@ -36,6 +36,7 @@ import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Optional; import java.util.Set; +import java.util.regex.Pattern; import java.util.stream.Collectors; @Service @@ -44,6 +45,7 @@ import java.util.stream.Collectors; public class DashboardImportService extends BaseEntityImportService> { private static final LinkedHashSet HINTS = new LinkedHashSet<>(Arrays.asList(EntityType.DASHBOARD, EntityType.DEVICE, EntityType.ASSET)); + public static final Pattern WIDGET_CONFIG_PROCESSED_FIELDS_PATTERN = Pattern.compile(".*Id.*"); private final DashboardService dashboardService; @@ -65,10 +67,10 @@ public class DashboardImportService extends BaseEntityImportService exportData, IdProvider idProvider) { for (JsonNode entityAlias : dashboard.getEntityAliasesConfig()) { - replaceIdsRecursively(ctx, idProvider, entityAlias, Set.of("id"), HINTS); + replaceIdsRecursively(ctx, idProvider, entityAlias, Set.of("id"), null, HINTS); } for (JsonNode widgetConfig : dashboard.getWidgetsConfig()) { - replaceIdsRecursively(ctx, idProvider, JacksonUtil.getSafely(widgetConfig, "config", "actions"), Set.of("id"), HINTS); + replaceIdsRecursively(ctx, idProvider, JacksonUtil.getSafely(widgetConfig, "config", "actions"), Collections.emptySet(), WIDGET_CONFIG_PROCESSED_FIELDS_PATTERN, HINTS); } return dashboard; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DeviceProfileImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DeviceProfileImportService.java index 3cc0310079..0684858803 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DeviceProfileImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/DeviceProfileImportService.java @@ -49,6 +49,7 @@ public class DeviceProfileImportService extends BaseEntityImportService exportData, IdProvider idProvider) { deviceProfile.setDefaultRuleChainId(idProvider.getInternalId(deviceProfile.getDefaultRuleChainId())); + deviceProfile.setDefaultEdgeRuleChainId(idProvider.getInternalId(deviceProfile.getDefaultEdgeRuleChainId())); deviceProfile.setDefaultDashboardId(idProvider.getInternalId(deviceProfile.getDefaultDashboardId())); deviceProfile.setFirmwareId(getOldEntityField(old, DeviceProfile::getFirmwareId)); deviceProfile.setSoftwareId(getOldEntityField(old, DeviceProfile::getSoftwareId)); @@ -68,8 +69,8 @@ public class DeviceProfileImportService extends BaseEntityImportService> { + + private final ResourceService resourceService; + private final ImageService imageService; + + @Override + protected void setOwner(TenantId tenantId, TbResource resource, IdProvider idProvider) { + resource.setTenantId(tenantId); + } + + @Override + protected TbResource prepare(EntitiesImportCtx ctx, TbResource resource, TbResource oldResource, EntityExportData exportData, IdProvider idProvider) { + return resource; + } + + @Override + protected TbResource findExistingEntity(EntitiesImportCtx ctx, TbResource resource, IdProvider idProvider) { + TbResource existingResource = super.findExistingEntity(ctx, resource, idProvider); + if (existingResource == null && ctx.isFindExistingByName()) { + existingResource = resourceService.findResourceByTenantIdAndKey(ctx.getTenantId(), resource.getResourceType(), resource.getResourceKey()); + } + return existingResource; + } + + @Override + protected boolean compare(EntitiesImportCtx ctx, EntityExportData exportData, TbResource prepared, TbResource existing) { + return true; + } + + @Override + protected TbResource deepCopy(TbResource resource) { + return new TbResource(resource); + } + + @Override + protected TbResource saveOrUpdate(EntitiesImportCtx ctx, TbResource resource, EntityExportData exportData, IdProvider idProvider) { + if (resource.getResourceType() == ResourceType.IMAGE) { + return new TbResource(imageService.saveImage(resource)); + } else { + resource = resourceService.saveResource(resource); + resource.setData(null); + resource.setPreview(null); + return resource; + } + } + + @Override + protected void onEntitySaved(User user, TbResource savedResource, TbResource oldResource) throws ThingsboardException { + super.onEntitySaved(user, savedResource, oldResource); + clusterService.onResourceChange(savedResource, null); + } + + @Override + public EntityType getEntityType() { + return EntityType.TB_RESOURCE; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java index eb54fce21e..d2673595ff 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java @@ -21,7 +21,6 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; -import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; @@ -35,6 +34,7 @@ import org.thingsboard.server.common.data.sync.ie.RuleChainExportData; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleNodeDao; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.rule.TbRuleChainService; import org.thingsboard.server.service.sync.vc.data.EntitiesImportCtx; import java.util.Arrays; @@ -42,6 +42,7 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; +import java.util.regex.Pattern; import java.util.stream.Collectors; @Slf4j @@ -51,7 +52,9 @@ import java.util.stream.Collectors; public class RuleChainImportService extends BaseEntityImportService { private static final LinkedHashSet HINTS = new LinkedHashSet<>(Arrays.asList(EntityType.RULE_CHAIN, EntityType.DEVICE, EntityType.ASSET)); + public static final Pattern PROCESSED_CONFIG_FIELDS_PATTERN = Pattern.compile(".*[iI]d.*"); + private final TbRuleChainService tbRuleChainService; private final RuleChainService ruleChainService; private final RuleNodeDao ruleNodeDao; @@ -90,7 +93,8 @@ public class RuleChainImportService extends BaseEntityImportService replaceIdsRecursively(ctx, idProvider, ruleNode.getConfiguration(), Collections.emptySet(), HINTS)); + ruleNodes.forEach(ruleNode -> replaceIdsRecursively(ctx, idProvider, ruleNode.getConfiguration(), + Collections.emptySet(), PROCESSED_CONFIG_FIELDS_PATTERN, HINTS)); Optional.ofNullable(metaData.getRuleChainConnections()).orElse(Collections.emptyList()) .forEach(ruleChainConnectionInfo -> { ruleChainConnectionInfo.setTargetRuleChainId(idProvider.getInternalId(ruleChainConnectionInfo.getTargetRuleChainId(), false)); @@ -106,7 +110,7 @@ public class RuleChainImportService extends BaseEntityImportService { + + private final WidgetTypeService widgetTypeService; + + @Override + protected void setOwner(TenantId tenantId, WidgetTypeDetails widgetsBundle, IdProvider idProvider) { + widgetsBundle.setTenantId(tenantId); + } + + @Override + protected WidgetTypeDetails prepare(EntitiesImportCtx ctx, WidgetTypeDetails widgetsBundle, WidgetTypeDetails old, WidgetTypeExportData exportData, IdProvider idProvider) { + return widgetsBundle; + } + + @Override + protected WidgetTypeDetails saveOrUpdate(EntitiesImportCtx ctx, WidgetTypeDetails widgetsBundle, WidgetTypeExportData exportData, IdProvider idProvider) { + return widgetTypeService.saveWidgetType(widgetsBundle); + } + + @Override + protected boolean compare(EntitiesImportCtx ctx, WidgetTypeExportData exportData, WidgetTypeDetails prepared, WidgetTypeDetails existing) { + return true; + } + + @Override + protected WidgetTypeDetails deepCopy(WidgetTypeDetails widgetsBundle) { + return new WidgetTypeDetails(widgetsBundle); + } + + @Override + public EntityType getEntityType() { + return EntityType.WIDGET_TYPE; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/WidgetsBundleImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/WidgetsBundleImportService.java index 8dc748c43f..21cf3e4a88 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/WidgetsBundleImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/WidgetsBundleImportService.java @@ -17,25 +17,19 @@ package org.thingsboard.server.service.sync.ie.importing.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.edge.EdgeEventActionType; -import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.sync.ie.WidgetsBundleExportData; -import org.thingsboard.server.common.data.widget.BaseWidgetType; +import org.thingsboard.server.common.data.util.CollectionsUtil; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; -import org.thingsboard.server.common.data.widget.WidgetTypeInfo; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetsBundleService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.sync.vc.data.EntitiesImportCtx; -import java.util.Map; -import java.util.stream.Collectors; - @Service @TbCoreComponent @RequiredArgsConstructor @@ -56,33 +50,27 @@ public class WidgetsBundleImportService extends BaseEntityImportService existingWidgets = widgetTypeService.findWidgetTypesInfosByTenantIdAndBundleAlias(ctx.getTenantId(), savedWidgetsBundle.getAlias()).stream() - .collect(Collectors.toMap(BaseWidgetType::getAlias, w -> w)); - for (WidgetTypeDetails widget : exportData.getWidgets()) { - WidgetTypeInfo existingWidget; - if ((existingWidget = existingWidgets.remove(widget.getAlias())) != null) { - widget.setId(existingWidget.getId()); - widget.setCreatedTime(existingWidget.getCreatedTime()); + if (CollectionsUtil.isNotEmpty(exportData.getWidgets())) { + exportData.getWidgets().forEach(widgetTypeNode -> { + String bundleAlias = widgetTypeNode.remove("bundleAlias").asText(); + String alias = widgetTypeNode.remove("alias").asText(); + String fqn = String.format("%s.%s", bundleAlias, alias); + exportData.addFqn(fqn); + WidgetTypeDetails widgetType = JacksonUtil.treeToValue(widgetTypeNode, WidgetTypeDetails.class); + widgetType.setTenantId(ctx.getTenantId()); + widgetType.setFqn(fqn); + var existingWidgetType = widgetTypeService.findWidgetTypeByTenantIdAndFqn(ctx.getTenantId(), fqn); + if (existingWidgetType == null) { + widgetType.setId(null); } else { - widget.setId(null); + widgetType.setId(existingWidgetType.getId()); + widgetType.setCreatedTime(existingWidgetType.getCreatedTime()); } - widget.setTenantId(ctx.getTenantId()); - widget.setBundleAlias(savedWidgetsBundle.getAlias()); - widgetTypeService.saveWidgetType(widget); - } - existingWidgets.values().stream() - .map(BaseWidgetType::getId) - .forEach(widgetTypeId -> widgetTypeService.deleteWidgetType(ctx.getTenantId(), widgetTypeId)); + widgetTypeService.saveWidgetType(widgetType); + }); } + WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); + widgetTypeService.updateWidgetsBundleWidgetFqns(ctx.getTenantId(), savedWidgetsBundle.getId(), exportData.getFqns()); return savedWidgetsBundle; } @@ -91,12 +79,6 @@ public class WidgetsBundleImportService extends BaseEntityImportService taskCache; - - private ListeningExecutorService executor; - - @Value("${vc.thread_pool_size:4}") - private int threadPoolSize; - - @PostConstruct - public void init() { - executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(threadPoolSize, DefaultEntitiesVersionControlService.class)); - } - - @PreDestroy - public void shutdown() { - if (executor != null) { - executor.shutdownNow(); - } - } + private final VersionControlExecutor executor; @SuppressWarnings("UnstableApiUsage") @Override @@ -188,7 +165,7 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont throw new ThingsboardException(ThingsboardErrorCode.ITEM_NOT_FOUND); } else { var entry = cacheEntry.get(); - log.debug("[{}] Cache get: {}", requestId, entry); + log.trace("[{}] Cache get: {}", requestId, entry); var result = getter.apply(entry); if (result == null) { throw new ThingsboardException(ThingsboardErrorCode.BAD_REQUEST_PARAMS); @@ -211,10 +188,10 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont } if (config.isAllEntities()) { - DaoUtil.processInBatches(pageLink -> exportableEntitiesService.findEntitiesByTenantId(ctx.getTenantId(), entityType, pageLink) - , 100, entity -> { + DaoUtil.processInBatches(pageLink -> exportableEntitiesService.findEntitiesIdsByTenantId(ctx.getTenantId(), entityType, pageLink), + 100, entityId -> { try { - ctx.add(saveEntityData(ctx, entity.getId())); + ctx.add(saveEntityData(ctx, entityId)); } catch (Exception e) { throw new RuntimeException(e); } @@ -290,7 +267,15 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont private VersionLoadResult doInTemplate(EntitiesImportCtx ctx, VersionLoadRequest request, Function function) { try { - VersionLoadResult result = transactionTemplate.execute(status -> function.apply(ctx)); + VersionLoadResult result = transactionTemplate.execute(status -> { + try { + return function.apply(ctx); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); // to prevent UndeclaredThrowableException + } + }); for (ThrowingRunnable throwingRunnable : ctx.getEventCallbacks()) { throwingRunnable.run(); } @@ -358,9 +343,9 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont sw.stop(); for (var task : sw.getTaskInfo()) { - log.info("[{}] Executed: {} in {}ms", ctx.getTenantId(), task.getTaskName(), task.getTimeMillis()); + log.debug("[{}] Executed: {} in {}ms", ctx.getTenantId(), task.getTaskName(), task.getTimeMillis()); } - log.info("[{}] Total time: {}ms", ctx.getTenantId(), sw.getTotalTimeMillis()); + log.debug("[{}] Total time: {}ms", ctx.getTenantId(), sw.getTotalTimeMillis()); return VersionLoadResult.success(new ArrayList<>(ctx.getResults().values())); } @@ -383,8 +368,8 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont do { try { entityDataList = gitServiceQueue.getEntities(ctx.getTenantId(), ctx.getVersionId(), entityType, offset, limit).get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); + } catch (ExecutionException e) { + throw e.getCause(); } log.debug("[{}] Loading {} entities pack ({})", ctx.getTenantId(), entityType, entityDataList.size()); for (EntityExportData entityData : entityDataList) { @@ -432,12 +417,11 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont return exportableEntitiesService.findEntitiesByTenantId(ctx.getTenantId(), entityType, pageLink); }, 100, entity -> { if (ctx.getImportedEntities().get(entityType) == null || !ctx.getImportedEntities().get(entityType).contains(entity.getId())) { - List relatedEdgeIds = edgeService.findAllRelatedEdgeIds(ctx.getTenantId(), entity.getId()); exportableEntitiesService.removeById(ctx.getTenantId(), entity.getId()); ctx.addEventCallback(() -> { - entityNotificationService.notifyDeleteEntity(ctx.getTenantId(), entity.getId(), - entity, null, ActionType.DELETED, relatedEdgeIds, ctx.getUser()); + entityNotificationService.logEntityAction(ctx.getTenantId(), entity.getId(), entity, null, + ActionType.DELETED, ctx.getUser()); }); ctx.registerDeleted(entityType); } @@ -627,7 +611,7 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont } private VersionLoadResult cachePut(UUID requestId, VersionLoadResult result) { - log.debug("[{}] Cache put: {}", requestId, result); + log.trace("[{}] Cache put: {}", requestId, result); taskCache.put(requestId, VersionControlTaskCacheEntry.newForImport(result)); return result; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java index feff708983..7a1c6bec6e 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java @@ -18,7 +18,6 @@ package org.thingsboard.server.service.sync.vc; import com.google.common.collect.Iterables; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.SettableFuture; import com.google.protobuf.ByteString; import lombok.SneakyThrows; @@ -62,6 +61,7 @@ import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.scheduler.SchedulerComponent; import org.thingsboard.server.queue.util.DataDecodingEncodingService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.executors.VersionControlExecutor; import org.thingsboard.server.service.sync.vc.data.ClearRepositoryGitRequest; import org.thingsboard.server.service.sync.vc.data.CommitGitRequest; import org.thingsboard.server.service.sync.vc.data.EntitiesContentGitRequest; @@ -92,6 +92,7 @@ import java.util.stream.Collectors; @TbCoreComponent @Service @Slf4j +@SuppressWarnings("UnstableApiUsage") public class DefaultGitVersionControlQueueService implements GitVersionControlQueueService { private final TbServiceInfoProvider serviceInfoProvider; @@ -99,40 +100,42 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu private final DataDecodingEncodingService encodingService; private final DefaultEntitiesVersionControlService entitiesVersionControlService; private final SchedulerComponent scheduler; + private final VersionControlExecutor executor; - private final Map> pendingRequestMap = new HashMap<>(); + private final Map> pendingRequestMap = new ConcurrentHashMap<>(); private final Map> chunkedMsgs = new ConcurrentHashMap<>(); - @Value("${queue.vc.request-timeout:60000}") + @Value("${queue.vc.request-timeout:180000}") private int requestTimeout; - @Value("${queue.vc.msg-chunk-size:500000}") + @Value("${queue.vc.msg-chunk-size:250000}") private int msgChunkSize; public DefaultGitVersionControlQueueService(TbServiceInfoProvider serviceInfoProvider, TbClusterService clusterService, DataDecodingEncodingService encodingService, @Lazy DefaultEntitiesVersionControlService entitiesVersionControlService, - SchedulerComponent scheduler) { + SchedulerComponent scheduler, VersionControlExecutor executor) { this.serviceInfoProvider = serviceInfoProvider; this.clusterService = clusterService; this.encodingService = encodingService; this.entitiesVersionControlService = entitiesVersionControlService; this.scheduler = scheduler; + this.executor = executor; } @Override public ListenableFuture prepareCommit(User user, VersionCreateRequest request) { - SettableFuture future = SettableFuture.create(); - + log.debug("Executing prepareCommit [{}][{}]", request.getBranch(), request.getVersionName()); CommitGitRequest commit = new CommitGitRequest(user.getTenantId(), request); - registerAndSend(commit, builder -> builder.setCommitRequest( + ListenableFuture future = registerAndSend(commit, builder -> builder.setCommitRequest( buildCommitRequest(commit).setPrepareMsg(getCommitPrepareMsg(user, request)).build() - ).build(), wrap(future, commit)); - return future; + ).build()); + return Futures.transform(future, f -> commit, executor); } - @SuppressWarnings("UnstableApiUsage") + @SneakyThrows @Override public ListenableFuture addToCommit(CommitGitRequest commit, EntityExportData> entityData) { + log.debug("Executing addToCommit [{}][{}][{}]", entityData.getEntityType(), entityData.getEntity().getId(), commit.getRequestId()); String path = getRelativePath(entityData.getEntityType(), entityData.getExternalId()); String entityDataJson = JacksonUtil.toPrettyString(entityData.sort()); @@ -143,53 +146,42 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu AtomicInteger chunkIndex = new AtomicInteger(); List> futures = new ArrayList<>(); entityDataChunks.forEach(chunk -> { - SettableFuture chunkFuture = SettableFuture.create(); log.trace("[{}] sending chunk {} for 'addToCommit'", chunkedMsgId, chunkIndex.get()); - registerAndSend(commit, builder -> builder.setCommitRequest( - buildCommitRequest(commit).setAddMsg( - TransportProtos.AddMsg.newBuilder() - .setRelativePath(path).setEntityDataJsonChunk(chunk) - .setChunkedMsgId(chunkedMsgId).setChunkIndex(chunkIndex.getAndIncrement()) - .setChunksCount(chunksCount).build() + ListenableFuture chunkFuture = registerAndSend(commit, builder -> builder.setCommitRequest( + buildCommitRequest(commit).setAddMsg(TransportProtos.AddMsg.newBuilder() + .setRelativePath(path).setEntityDataJsonChunk(chunk) + .setChunkedMsgId(chunkedMsgId).setChunkIndex(chunkIndex.getAndIncrement()) + .setChunksCount(chunksCount) ).build() - ).build(), wrap(chunkFuture, null)); + ).build()); futures.add(chunkFuture); }); return Futures.transform(Futures.allAsList(futures), r -> { log.trace("[{}] sent all chunks for 'addToCommit'", chunkedMsgId); return null; - }, MoreExecutors.directExecutor()); + }, executor); } @Override public ListenableFuture deleteAll(CommitGitRequest commit, EntityType entityType) { - SettableFuture future = SettableFuture.create(); - + log.debug("Executing deleteAll [{}][{}][{}]", commit.getTenantId(), entityType, commit.getRequestId()); String path = getRelativePath(entityType, null); - - registerAndSend(commit, builder -> builder.setCommitRequest( + return registerAndSend(commit, builder -> builder.setCommitRequest( buildCommitRequest(commit).setDeleteMsg( - TransportProtos.DeleteMsg.newBuilder().setRelativePath(path).build() - ).build() - ).build(), wrap(future, null)); - - return future; + TransportProtos.DeleteMsg.newBuilder().setRelativePath(path) + )).build()); } @Override public ListenableFuture push(CommitGitRequest commit) { - registerAndSend(commit, builder -> builder.setCommitRequest( - buildCommitRequest(commit).setPushMsg( - TransportProtos.PushMsg.newBuilder().build() - ).build() - ).build(), wrap(commit.getFuture())); - - return commit.getFuture(); + log.debug("Executing push [{}][{}]", commit.getTenantId(), commit.getRequestId()); + return sendRequest(commit, builder -> builder.setCommitRequest( + buildCommitRequest(commit).setPushMsg(TransportProtos.PushMsg.getDefaultInstance()) + )); } @Override public ListenableFuture> listVersions(TenantId tenantId, String branch, PageLink pageLink) { - return listVersions(tenantId, applyPageLinkParameters( ListVersionsRequestMsg.newBuilder() @@ -284,90 +276,95 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu @Override @SuppressWarnings("rawtypes") public ListenableFuture getEntity(TenantId tenantId, String versionId, EntityId entityId) { + log.debug("Executing getEntity [{}][{}][{}]", tenantId, versionId, entityId); EntityContentGitRequest request = new EntityContentGitRequest(tenantId, versionId, entityId); chunkedMsgs.put(request.getRequestId(), new HashMap<>()); - registerAndSend(request, builder -> builder.setEntityContentRequest(EntityContentRequestMsg.newBuilder() - .setVersionId(versionId) - .setEntityType(entityId.getEntityType().name()) - .setEntityIdMSB(entityId.getId().getMostSignificantBits()) - .setEntityIdLSB(entityId.getId().getLeastSignificantBits())).build() - , wrap(request.getFuture())); - return request.getFuture(); + return sendRequest(request, builder -> builder.setEntityContentRequest(EntityContentRequestMsg.newBuilder() + .setVersionId(versionId) + .setEntityType(entityId.getEntityType().name()) + .setEntityIdMSB(entityId.getId().getMostSignificantBits()) + .setEntityIdLSB(entityId.getId().getLeastSignificantBits())).build()); } - private void registerAndSend(PendingGitRequest request, - Function enrichFunction, TbQueueCallback callback) { - registerAndSend(request, enrichFunction, null, callback); + private ListenableFuture registerAndSend(PendingGitRequest request, + Function enrichFunction) { + return registerAndSend(request, enrichFunction, null); } - private void registerAndSend(PendingGitRequest request, - Function enrichFunction, RepositorySettings settings, TbQueueCallback callback) { + private ListenableFuture registerAndSend(PendingGitRequest request, + Function enrichFunction, + RepositorySettings settings) { if (!request.getFuture().isDone()) { pendingRequestMap.putIfAbsent(request.getRequestId(), request); var requestBody = enrichFunction.apply(newRequestProto(request, settings)); log.trace("[{}][{}] PUSHING request: {}", request.getTenantId(), request.getRequestId(), requestBody); - clusterService.pushMsgToVersionControl(request.getTenantId(), requestBody, callback); + SettableFuture submitFuture = SettableFuture.create(); + clusterService.pushMsgToVersionControl(request.getTenantId(), requestBody, new TbQueueCallback() { + @Override + public void onSuccess(TbQueueMsgMetadata metadata) { + submitFuture.set(null); + } + + @Override + public void onFailure(Throwable t) { + submitFuture.setException(t); + } + }); if (request.getTimeoutTask() == null) { request.setTimeoutTask(scheduler.schedule(() -> processTimeout(request.getRequestId()), requestTimeout, TimeUnit.MILLISECONDS)); } + return submitFuture; } else { throw new RuntimeException("Future is already done!"); } } private ListenableFuture sendRequest(PendingGitRequest request, Consumer enrichFunction) { - registerAndSend(request, builder -> { + return sendRequest(request, enrichFunction, null); + } + + private ListenableFuture sendRequest(PendingGitRequest request, Consumer enrichFunction, RepositorySettings settings) { + ListenableFuture submitFuture = registerAndSend(request, builder -> { enrichFunction.accept(builder); return builder.build(); - }, wrap(request.getFuture())); - return request.getFuture(); + }, settings); + return Futures.transformAsync(submitFuture, input -> request.getFuture(), executor); } @Override @SuppressWarnings("rawtypes") public ListenableFuture> getEntities(TenantId tenantId, String versionId, EntityType entityType, int offset, int limit) { + log.debug("Executing getEntities [{}][{}][{}]", tenantId, versionId, entityType); EntitiesContentGitRequest request = new EntitiesContentGitRequest(tenantId, versionId, entityType); chunkedMsgs.put(request.getRequestId(), new HashMap<>()); - registerAndSend(request, builder -> builder.setEntitiesContentRequest(EntitiesContentRequestMsg.newBuilder() + return sendRequest(request, builder -> builder.setEntitiesContentRequest( + EntitiesContentRequestMsg.newBuilder() .setVersionId(versionId) .setEntityType(entityType.name()) .setOffset(offset) .setLimit(limit) - ).build() - , wrap(request.getFuture())); - - return request.getFuture(); + ).build()); } @Override public ListenableFuture initRepository(TenantId tenantId, RepositorySettings settings) { + log.debug("Executing initRepository [{}]", tenantId); VoidGitRequest request = new VoidGitRequest(tenantId); - - registerAndSend(request, builder -> builder.setInitRepositoryRequest(GenericRepositoryRequestMsg.newBuilder().build()).build() - , settings, wrap(request.getFuture())); - - return request.getFuture(); + return sendRequest(request, builder -> builder.setInitRepositoryRequest(GenericRepositoryRequestMsg.getDefaultInstance()), settings); } @Override public ListenableFuture testRepository(TenantId tenantId, RepositorySettings settings) { + log.debug("Executing testRepository [{}]", tenantId); VoidGitRequest request = new VoidGitRequest(tenantId); - - registerAndSend(request, builder -> builder - .setTestRepositoryRequest(GenericRepositoryRequestMsg.newBuilder().build()).build() - , settings, wrap(request.getFuture())); - - return request.getFuture(); + return sendRequest(request, builder -> builder.setTestRepositoryRequest(GenericRepositoryRequestMsg.getDefaultInstance()), settings); } @Override public ListenableFuture clearRepository(TenantId tenantId) { + log.debug("Executing clearRepository [{}]", tenantId); ClearRepositoryGitRequest request = new ClearRepositoryGitRequest(tenantId); - - registerAndSend(request, builder -> builder.setClearRepositoryRequest(GenericRepositoryRequestMsg.newBuilder().build()).build() - , wrap(request.getFuture())); - - return request.getFuture(); + return sendRequest(request, builder -> builder.setClearRepositoryRequest(GenericRepositoryRequestMsg.getDefaultInstance())); } @Override @@ -518,35 +515,6 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu return JacksonUtil.fromString(data, EntityExportData.class); } - //The future will be completed when the corresponding result arrives from kafka - private static TbQueueCallback wrap(SettableFuture future) { - return new TbQueueCallback() { - @Override - public void onSuccess(TbQueueMsgMetadata metadata) { - } - - @Override - public void onFailure(Throwable t) { - future.setException(t); - } - }; - } - - //The future will be completed when the request is successfully sent to kafka - private TbQueueCallback wrap(SettableFuture future, T value) { - return new TbQueueCallback() { - @Override - public void onSuccess(TbQueueMsgMetadata metadata) { - future.set(value); - } - - @Override - public void onFailure(Throwable t) { - future.setException(t); - } - }; - } - private static String getRelativePath(EntityType entityType, EntityId entityId) { String path = entityType.name().toLowerCase(); if (entityId != null) { diff --git a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java index 80d4cede03..4b2a3faa2b 100644 --- a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java +++ b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.protobuf.ProtocolStringList; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardThreadFactory; @@ -93,6 +94,11 @@ public class DefaultSystemInfoService extends TbApplicationEventListener telemetry) { ApiUsageState apiUsageState = apiUsageStateClient.getApiUsageState(TenantId.SYS_TENANT_ID); - telemetryService.saveAndNotifyInternal(TenantId.SYS_TENANT_ID, apiUsageState.getId(), telemetry, CALLBACK); + telemetryService.saveAndNotifyInternal(TenantId.SYS_TENANT_ID, apiUsageState.getId(), telemetry, systemInfoTtlSeconds, CALLBACK); } private List getSystemData(ServiceInfo serviceInfo) { diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java index e7f2a5a66f..cc2c8966e5 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java @@ -25,6 +25,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.ApiUsageRecordKey; +import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; import org.thingsboard.server.common.data.alarm.AlarmComment; @@ -44,15 +45,15 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.rule.trigger.AlarmTrigger; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.query.AlarmData; import org.thingsboard.server.common.data.query.AlarmDataQuery; -import org.thingsboard.server.common.msg.notification.trigger.AlarmTrigger; +import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.stats.TbApiUsageReportClient; -import org.thingsboard.server.dao.alarm.AlarmOperationResult; import org.thingsboard.server.dao.alarm.AlarmService; -import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.entitiy.alarm.TbAlarmCommentService; import org.thingsboard.server.service.subscription.TbSubscriptionUtils; @@ -113,31 +114,6 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService return withWsCallback(alarmService.unassignAlarm(tenantId, alarmId, assignTs)); } - @Override - public Alarm createOrUpdateAlarm(Alarm alarm) { - AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm, apiUsageStateService.getApiUsageState(alarm.getTenantId()).isAlarmCreationEnabled()); - if (result.isSuccessful()) { - onAlarmUpdated(result); - AlarmSeverity oldSeverity = result.getOldSeverity(); - if (oldSeverity != null && !oldSeverity.equals(result.getAlarm().getSeverity())) { - AlarmComment alarmComment = AlarmComment.builder() - .alarmId(alarm.getId()) - .type(AlarmCommentType.SYSTEM) - .comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm severity was updated from %s to %s", oldSeverity, result.getAlarm().getSeverity()))) - .build(); - try { - alarmCommentService.saveAlarmComment(alarm, alarmComment, null); - } catch (ThingsboardException e) { - log.error("Failed to save alarm comment", e); - } - } - } - if (result.isCreated()) { - apiUsageClient.report(alarm.getTenantId(), null, ApiUsageRecordKey.CREATED_ALARMS_COUNT); - } - return result.getAlarm(); - } - @Override public Boolean deleteAlarm(TenantId tenantId, AlarmId alarmId) { AlarmApiCallResult result = alarmService.delAlarm(tenantId, alarmId); @@ -145,26 +121,6 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService return result.isSuccessful(); } - @Override - public ListenableFuture ackAlarm(TenantId tenantId, AlarmId alarmId, long ackTs) { - ListenableFuture result = Futures.immediateFuture(alarmService.acknowledgeAlarm(tenantId, alarmId, ackTs)); - Futures.addCallback(result, new AlarmUpdateCallback(), wsCallBackExecutor); - return Futures.transform(result, AlarmApiCallResult::isSuccessful, wsCallBackExecutor); - } - - @Override - public ListenableFuture clearAlarm(TenantId tenantId, AlarmId alarmId, JsonNode details, long clearTs) { - AlarmApiCallResult result = alarmService.clearAlarm(tenantId, alarmId, clearTs, details); - return Futures.transform(Futures.immediateFuture(result), AlarmApiCallResult::isSuccessful, wsCallBackExecutor); - } - - @Override - public ListenableFuture clearAlarmForResult(TenantId tenantId, AlarmId alarmId, JsonNode details, long clearTs) { - AlarmApiCallResult result = alarmService.clearAlarm(tenantId, alarmId, clearTs, details); - Futures.addCallback(Futures.immediateFuture(result), new AlarmUpdateCallback(), wsCallBackExecutor); - return Futures.immediateFuture(new AlarmOperationResult(result)); - } - @Override public ListenableFuture findAlarmByIdAsync(TenantId tenantId, AlarmId alarmId) { return alarmService.findAlarmByIdAsync(tenantId, alarmId); @@ -181,22 +137,22 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService } @Override - public ListenableFuture> findAlarms(TenantId tenantId, AlarmQuery query) { + public PageData findAlarms(TenantId tenantId, AlarmQuery query) { return alarmService.findAlarms(tenantId, query); } @Override - public ListenableFuture> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query) { + public PageData findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query) { return alarmService.findCustomerAlarms(tenantId, customerId, query); } @Override - public ListenableFuture> findAlarmsV2(TenantId tenantId, AlarmQueryV2 query) { + public PageData findAlarmsV2(TenantId tenantId, AlarmQueryV2 query) { return alarmService.findAlarmsV2(tenantId, query); } @Override - public ListenableFuture> findCustomerAlarmsV2(TenantId tenantId, CustomerId customerId, AlarmQueryV2 query) { + public PageData findCustomerAlarmsV2(TenantId tenantId, CustomerId customerId, AlarmQueryV2 query) { return alarmService.findCustomerAlarmsV2(tenantId, customerId, query); } @@ -216,23 +172,13 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService } @Override - public ListenableFuture findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type) { - return alarmService.findLatestByOriginatorAndType(tenantId, originator, type); + public Alarm findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type) { + return alarmService.findLatestActiveByOriginatorAndType(tenantId, originator, type); } - @Deprecated - private void onAlarmUpdated(AlarmOperationResult result) { - wsCallBackExecutor.submit(() -> { - AlarmInfo alarm = new AlarmInfo(result.getAlarm()); - TenantId tenantId = alarm.getTenantId(); - for (EntityId entityId : result.getPropagatedEntitiesList()) { - forwardToSubscriptionManagerService(tenantId, entityId, subscriptionManagerService -> { - subscriptionManagerService.onAlarmUpdate(tenantId, entityId, alarm, TbCallback.EMPTY); - }, () -> { - return TbSubscriptionUtils.toAlarmUpdateProto(tenantId, entityId, alarm); - }); - } - }); + @Override + public PageData findAlarmTypesByTenantId(TenantId tenantId, PageLink pageLink) { + return alarmService.findAlarmTypesByTenantId(tenantId, pageLink); } private void onAlarmUpdated(AlarmApiCallResult result) { @@ -241,10 +187,9 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService TenantId tenantId = alarm.getTenantId(); for (EntityId entityId : result.getPropagatedEntitiesList()) { forwardToSubscriptionManagerService(tenantId, entityId, subscriptionManagerService -> { - subscriptionManagerService.onAlarmUpdate(tenantId, entityId, alarm, TbCallback.EMPTY); - }, () -> { - return TbSubscriptionUtils.toAlarmUpdateProto(tenantId, entityId, alarm); - }); + subscriptionManagerService.onAlarmUpdate(tenantId, entityId, alarm, TbCallback.EMPTY); + }, () -> TbSubscriptionUtils.toAlarmUpdateProto(tenantId, entityId, alarm) + ); } notificationRuleProcessor.process(AlarmTrigger.builder() .tenantId(tenantId) diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java index 3f5e52796a..e66029a8a1 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java @@ -21,6 +21,7 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.SettableFuture; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.ThingsBoardThreadFactory; @@ -78,6 +79,9 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer private ExecutorService tsCallBackExecutor; + @Value("${sql.ts.value_no_xss_validation:false}") + private boolean valueNoXssValidation; + public DefaultTelemetrySubscriptionService(AttributesService attrService, TimeseriesService tsService, @Lazy TbEntityViewService tbEntityViewService, @@ -135,7 +139,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer checkInternalEntity(entityId); boolean sysTenant = TenantId.SYS_TENANT_ID.equals(tenantId) || tenantId == null; if (sysTenant || apiUsageStateService.getApiUsageState(tenantId).isDbStorageEnabled()) { - KvUtils.validate(ts); + KvUtils.validate(ts, valueNoXssValidation); if (saveLatest) { saveAndNotifyInternal(tenantId, entityId, ts, ttl, getCallback(tenantId, customerId, sysTenant, callback)); } else { @@ -171,20 +175,21 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer @Override public void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, List ts, long ttl, FutureCallback callback) { ListenableFuture saveFuture = tsService.save(tenantId, entityId, ts, ttl); - addCallbacks(tenantId, entityId, ts, callback, saveFuture); + addMainCallback(saveFuture, callback); + addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, ts)); + addEntityViewCallback(tenantId, entityId, ts); } private void saveWithoutLatestAndNotifyInternal(TenantId tenantId, EntityId entityId, List ts, long ttl, FutureCallback callback) { ListenableFuture saveFuture = tsService.saveWithoutLatest(tenantId, entityId, ts, ttl); - addCallbacks(tenantId, entityId, ts, callback, saveFuture); - } - - private void addCallbacks(TenantId tenantId, EntityId entityId, List ts, FutureCallback callback, ListenableFuture saveFuture) { addMainCallback(saveFuture, callback); addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, ts)); + } + + private void addEntityViewCallback(TenantId tenantId, EntityId entityId, List ts) { if (EntityType.DEVICE.equals(entityId.getEntityType()) || EntityType.ASSET.equals(entityId.getEntityType())) { Futures.addCallback(this.tbEntityViewService.findEntityViewsByTenantIdAndEntityIdAsync(tenantId, entityId), - new FutureCallback>() { + new FutureCallback<>() { @Override public void onSuccess(@Nullable List result) { if (result != null && !result.isEmpty()) { @@ -208,7 +213,7 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer } } if (!entityViewLatest.isEmpty()) { - saveLatestAndNotify(tenantId, entityView.getId(), entityViewLatest, new FutureCallback() { + saveLatestAndNotify(tenantId, entityView.getId(), entityViewLatest, new FutureCallback<>() { @Override public void onSuccess(@Nullable Void tmp) { } diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTbCoreToTransportService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTbCoreToTransportService.java index b87758ace5..380ba34f91 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTbCoreToTransportService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTbCoreToTransportService.java @@ -24,7 +24,7 @@ import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueMsgMetadata; import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -38,11 +38,11 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; @TbCoreComponent public class DefaultTbCoreToTransportService implements TbCoreToTransportService { - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final TbQueueProducer> tbTransportProducer; - public DefaultTbCoreToTransportService(NotificationsTopicService notificationsTopicService, TbQueueProducerProvider tbQueueProducerProvider) { - this.notificationsTopicService = notificationsTopicService; + public DefaultTbCoreToTransportService(TopicService topicService, TbQueueProducerProvider tbQueueProducerProvider) { + this.topicService = topicService; this.tbTransportProducer = tbQueueProducerProvider.getTransportNotificationsMsgProducer(); } @@ -60,7 +60,7 @@ public class DefaultTbCoreToTransportService implements TbCoreToTransportService } return; } - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, nodeId); + TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, nodeId); UUID sessionId = new UUID(msg.getSessionIdMSB(), msg.getSessionIdLSB()); log.trace("[{}][{}] Pushing session data to topic: {}", tpi.getFullTopicName(), sessionId, msg); TbProtoQueueMsg queueMsg = new TbProtoQueueMsg<>(NULL_UUID, msg); diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index d0d996fe1f..f622b1fee0 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -20,12 +20,16 @@ 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.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.ByteString; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.springframework.util.ConcurrentReferenceHashMap; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.ApiUsageState; @@ -53,6 +57,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageUtil; import org.thingsboard.server.common.data.page.PageData; @@ -73,9 +78,11 @@ import org.thingsboard.server.dao.device.provision.ProvisionFailedException; import org.thingsboard.server.dao.device.provision.ProvisionRequest; import org.thingsboard.server.dao.device.provision.ProvisionResponse; import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; +import org.thingsboard.server.dao.exception.EntitiesLimitException; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; @@ -103,10 +110,11 @@ import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.profile.TbDeviceProfileCache; import org.thingsboard.server.service.resource.TbResourceService; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; import java.util.List; import java.util.Optional; import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -138,17 +146,34 @@ public class DefaultTransportApiService implements TransportApiService { private final TbClusterService tbClusterService; private final DataDecodingEncodingService dataDecodingEncodingService; private final DeviceProvisionService deviceProvisionService; - private final TbResourceService resourceService; + private final ResourceService resourceService; private final OtaPackageService otaPackageService; private final OtaPackageDataCache otaPackageDataCache; private final QueueService queueService; - private final ConcurrentMap deviceCreationLocks = new ConcurrentHashMap<>(); + private final ConcurrentMap deviceCreationLocks = new ConcurrentReferenceHashMap<>(16, ConcurrentReferenceHashMap.ReferenceType.WEAK); + + @Value("${queue.transport_api.max_core_handler_threads:16}") + private int maxCoreHandlerThreads; + + ListeningExecutorService handlerExecutor; private static boolean checkIsMqttCredentials(DeviceCredentials credentials) { return credentials != null && DeviceCredentialsType.MQTT_BASIC.equals(credentials.getCredentialsType()); } + @PostConstruct + public void init() { + handlerExecutor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(maxCoreHandlerThreads, "transport-api-service-core-handler")); + } + + @PreDestroy + public void destroy() { + if (handlerExecutor != null) { + handlerExecutor.shutdownNow(); + } + } + @Override public ListenableFuture> handle(TbProtoQueueMsg tbProtoQueueMsg) { TransportApiRequestMsg transportApiRequestMsg = tbProtoQueueMsg.getValue(); @@ -156,25 +181,29 @@ public class DefaultTransportApiService implements TransportApiService { if (transportApiRequestMsg.hasValidateTokenRequestMsg()) { ValidateDeviceTokenRequestMsg msg = transportApiRequestMsg.getValidateTokenRequestMsg(); - result = validateCredentials(msg.getToken(), DeviceCredentialsType.ACCESS_TOKEN); + final String token = msg.getToken(); + result = handlerExecutor.submit(() -> validateCredentials(token, DeviceCredentialsType.ACCESS_TOKEN)); } else if (transportApiRequestMsg.hasValidateBasicMqttCredRequestMsg()) { TransportProtos.ValidateBasicMqttCredRequestMsg msg = transportApiRequestMsg.getValidateBasicMqttCredRequestMsg(); - result = validateCredentials(msg); + result = handlerExecutor.submit(() -> validateCredentials(msg)); } else if (transportApiRequestMsg.hasValidateX509CertRequestMsg()) { ValidateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getValidateX509CertRequestMsg(); - result = validateCredentials(msg.getHash(), DeviceCredentialsType.X509_CERTIFICATE); + final String hash = msg.getHash(); + result = handlerExecutor.submit(() -> validateCredentials(hash, DeviceCredentialsType.X509_CERTIFICATE)); } else if (transportApiRequestMsg.hasValidateOrCreateX509CertRequestMsg()) { TransportProtos.ValidateOrCreateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getValidateOrCreateX509CertRequestMsg(); - result = validateOrCreateDeviceX509Certificate(msg.getCertificateChain()); + final String certChain = msg.getCertificateChain(); + result = handlerExecutor.submit(() -> validateOrCreateDeviceX509Certificate(certChain)); } else if (transportApiRequestMsg.hasGetOrCreateDeviceRequestMsg()) { - result = handle(transportApiRequestMsg.getGetOrCreateDeviceRequestMsg()); + result = handlerExecutor.submit(() -> handle(transportApiRequestMsg.getGetOrCreateDeviceRequestMsg())); } else if (transportApiRequestMsg.hasEntityProfileRequestMsg()) { result = handle(transportApiRequestMsg.getEntityProfileRequestMsg()); } else if (transportApiRequestMsg.hasLwM2MRequestMsg()) { result = handle(transportApiRequestMsg.getLwM2MRequestMsg()); } else if (transportApiRequestMsg.hasValidateDeviceLwM2MCredentialsRequestMsg()) { ValidateDeviceLwM2MCredentialsRequestMsg msg = transportApiRequestMsg.getValidateDeviceLwM2MCredentialsRequestMsg(); - result = validateCredentials(msg.getCredentialsId(), DeviceCredentialsType.LWM2M_CREDENTIALS); + final String credentialsId = msg.getCredentialsId(); + result = handlerExecutor.submit(() -> validateCredentials(credentialsId, DeviceCredentialsType.LWM2M_CREDENTIALS)); } else if (transportApiRequestMsg.hasProvisionDeviceRequestMsg()) { result = handle(transportApiRequestMsg.getProvisionDeviceRequestMsg()); } else if (transportApiRequestMsg.hasResourceRequestMsg()) { @@ -196,24 +225,23 @@ public class DefaultTransportApiService implements TransportApiService { MoreExecutors.directExecutor()); } - private ListenableFuture validateCredentials(String credentialsId, DeviceCredentialsType credentialsType) { - //TODO: Make async and enable caching + private TransportApiResponseMsg validateCredentials(String credentialsId, DeviceCredentialsType credentialsType) { DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(credentialsId); if (credentials != null && credentials.getCredentialsType() == credentialsType) { return getDeviceInfo(credentials); } else { - return getEmptyTransportApiResponseFuture(); + return getEmptyTransportApiResponse(); } } - private ListenableFuture validateCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg mqtt) { + private TransportApiResponseMsg validateCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg mqtt) { DeviceCredentials credentials; if (StringUtils.isEmpty(mqtt.getUserName())) { credentials = checkMqttCredentials(mqtt, EncryptionUtil.getSha3Hash(mqtt.getClientId())); if (credentials != null) { return getDeviceInfo(credentials); } else { - return getEmptyTransportApiResponseFuture(); + return getEmptyTransportApiResponse(); } } else { credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId( @@ -223,7 +251,7 @@ public class DefaultTransportApiService implements TransportApiService { if (VALID.equals(validationResult)) { return getDeviceInfo(credentials); } else if (PASSWORD_MISMATCH.equals(validationResult)) { - return getEmptyTransportApiResponseFuture(); + return getEmptyTransportApiResponse(); } else { return validateUserNameCredentials(mqtt); } @@ -233,7 +261,7 @@ public class DefaultTransportApiService implements TransportApiService { } } - protected ListenableFuture validateOrCreateDeviceX509Certificate(String certificateChain) { + protected TransportApiResponseMsg validateOrCreateDeviceX509Certificate(String certificateChain) { List chain = X509_CERTIFICATE_TRIM_CHAIN_PATTERN.matcher(certificateChain).results().map(match -> EncryptionUtil.certTrimNewLines(match.group())).collect(Collectors.toList()); for (String certificateValue : chain) { @@ -253,16 +281,16 @@ public class DefaultTransportApiService implements TransportApiService { } } catch (ProvisionFailedException e) { log.debug("[{}][{}] Failed to provision device with cert chain: {}", deviceProfile.getTenantId(), deviceProfile.getId(), provisionRequest, e); - return getEmptyTransportApiResponseFuture(); + return getEmptyTransportApiResponse(); } } else if (deviceProfile != null) { log.warn("[{}][{}] Device Profile provision configuration mismatched: expected {}, actual {}", deviceProfile.getTenantId(), deviceProfile.getId(), DeviceProfileProvisionType.X509_CERTIFICATE_CHAIN, deviceProfile.getProvisionType()); } } - return getEmptyTransportApiResponseFuture(); + return getEmptyTransportApiResponse(); } - private ListenableFuture validateUserNameCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg mqtt) { + private TransportApiResponseMsg validateUserNameCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg mqtt) { DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(mqtt.getUserName()); if (credentials != null) { switch (credentials.getCredentialsType()) { @@ -272,11 +300,11 @@ public class DefaultTransportApiService implements TransportApiService { if (VALID.equals(validateMqttCredentials(mqtt, credentials))) { return getDeviceInfo(credentials); } else { - return getEmptyTransportApiResponseFuture(); + return getEmptyTransportApiResponse(); } } } - return getEmptyTransportApiResponseFuture(); + return getEmptyTransportApiResponse(); } private DeviceCredentials checkMqttCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg clientCred, String credId) { @@ -310,76 +338,81 @@ public class DefaultTransportApiService implements TransportApiService { return VALID; } - private ListenableFuture handle(GetOrCreateDeviceFromGatewayRequestMsg requestMsg) { + private TransportApiResponseMsg handle(GetOrCreateDeviceFromGatewayRequestMsg requestMsg) { DeviceId gatewayId = new DeviceId(new UUID(requestMsg.getGatewayIdMSB(), requestMsg.getGatewayIdLSB())); - ListenableFuture gatewayFuture = deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, gatewayId); - return Futures.transform(gatewayFuture, gateway -> { - Lock deviceCreationLock = deviceCreationLocks.computeIfAbsent(requestMsg.getDeviceName(), id -> new ReentrantLock()); - deviceCreationLock.lock(); - try { - Device device = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), requestMsg.getDeviceName()); - if (device == null) { - TenantId tenantId = gateway.getTenantId(); - device = new Device(); - device.setTenantId(tenantId); - device.setName(requestMsg.getDeviceName()); - device.setType(requestMsg.getDeviceType()); - device.setCustomerId(gateway.getCustomerId()); - DeviceProfile deviceProfile = deviceProfileCache.findOrCreateDeviceProfile(gateway.getTenantId(), requestMsg.getDeviceType()); - - device.setDeviceProfileId(deviceProfile.getId()); - ObjectNode additionalInfo = JacksonUtil.newObjectNode(); - additionalInfo.put(DataConstants.LAST_CONNECTED_GATEWAY, gatewayId.toString()); - device.setAdditionalInfo(additionalInfo); - Device savedDevice = deviceService.saveDevice(device); - tbClusterService.onDeviceUpdated(savedDevice, null); - device = savedDevice; - - relationService.saveRelation(TenantId.SYS_TENANT_ID, new EntityRelation(gateway.getId(), device.getId(), "Created")); - - TbMsgMetaData metaData = new TbMsgMetaData(); - CustomerId customerId = gateway.getCustomerId(); - if (customerId != null && !customerId.isNullUid()) { - metaData.putValue("customerId", customerId.toString()); - } - metaData.putValue("gatewayId", gatewayId.toString()); + Device gateway = deviceService.findDeviceById(TenantId.SYS_TENANT_ID, gatewayId); + Lock deviceCreationLock = deviceCreationLocks.computeIfAbsent(requestMsg.getDeviceName(), id -> new ReentrantLock()); + deviceCreationLock.lock(); + try { + Device device = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), requestMsg.getDeviceName()); + if (device == null) { + TenantId tenantId = gateway.getTenantId(); + device = new Device(); + device.setTenantId(tenantId); + device.setName(requestMsg.getDeviceName()); + device.setType(requestMsg.getDeviceType()); + device.setCustomerId(gateway.getCustomerId()); + DeviceProfile deviceProfile = deviceProfileCache.findOrCreateDeviceProfile(gateway.getTenantId(), requestMsg.getDeviceType()); + + device.setDeviceProfileId(deviceProfile.getId()); + ObjectNode additionalInfo = JacksonUtil.newObjectNode(); + additionalInfo.put(DataConstants.LAST_CONNECTED_GATEWAY, gatewayId.toString()); + device.setAdditionalInfo(additionalInfo); + Device savedDevice = deviceService.saveDevice(device); + tbClusterService.onDeviceUpdated(savedDevice, null); + device = savedDevice; + + relationService.saveRelation(TenantId.SYS_TENANT_ID, new EntityRelation(gateway.getId(), device.getId(), "Created")); + + TbMsgMetaData metaData = new TbMsgMetaData(); + CustomerId customerId = gateway.getCustomerId(); + if (customerId != null && !customerId.isNullUid()) { + metaData.putValue("customerId", customerId.toString()); + } + metaData.putValue("gatewayId", gatewayId.toString()); - DeviceId deviceId = device.getId(); - JsonNode entityNode = JacksonUtil.valueToTree(device); - TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId, customerId, metaData, TbMsgDataType.JSON, JacksonUtil.toString(entityNode)); - tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, null); - } else { - JsonNode deviceAdditionalInfo = device.getAdditionalInfo(); - if (deviceAdditionalInfo == null) { - deviceAdditionalInfo = JacksonUtil.newObjectNode(); - } - if (deviceAdditionalInfo.isObject() && - (!deviceAdditionalInfo.has(DataConstants.LAST_CONNECTED_GATEWAY) - || !gatewayId.toString().equals(deviceAdditionalInfo.get(DataConstants.LAST_CONNECTED_GATEWAY).asText()))) { - ObjectNode newDeviceAdditionalInfo = (ObjectNode) deviceAdditionalInfo; - newDeviceAdditionalInfo.put(DataConstants.LAST_CONNECTED_GATEWAY, gatewayId.toString()); - Device savedDevice = deviceService.saveDevice(device); - tbClusterService.onDeviceUpdated(savedDevice, device); - } + DeviceId deviceId = device.getId(); + JsonNode entityNode = JacksonUtil.valueToTree(device); + TbMsg tbMsg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, deviceId, customerId, metaData, TbMsgDataType.JSON, JacksonUtil.toString(entityNode)); + tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, null); + } else { + JsonNode deviceAdditionalInfo = device.getAdditionalInfo(); + if (deviceAdditionalInfo == null) { + deviceAdditionalInfo = JacksonUtil.newObjectNode(); } - GetOrCreateDeviceFromGatewayResponseMsg.Builder builder = GetOrCreateDeviceFromGatewayResponseMsg.newBuilder() - .setDeviceInfo(getDeviceInfoProto(device)); - DeviceProfile deviceProfile = deviceProfileCache.get(device.getTenantId(), device.getDeviceProfileId()); - if (deviceProfile != null) { - builder.setProfileBody(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfile))); - } else { - log.warn("[{}] Failed to find device profile [{}] for device. ", device.getId(), device.getDeviceProfileId()); + if (deviceAdditionalInfo.isObject() && + (!deviceAdditionalInfo.has(DataConstants.LAST_CONNECTED_GATEWAY) + || !gatewayId.toString().equals(deviceAdditionalInfo.get(DataConstants.LAST_CONNECTED_GATEWAY).asText()))) { + ObjectNode newDeviceAdditionalInfo = (ObjectNode) deviceAdditionalInfo; + newDeviceAdditionalInfo.put(DataConstants.LAST_CONNECTED_GATEWAY, gatewayId.toString()); + Device savedDevice = deviceService.saveDevice(device); + tbClusterService.onDeviceUpdated(savedDevice, device); } - return TransportApiResponseMsg.newBuilder() - .setGetOrCreateDeviceResponseMsg(builder.build()) - .build(); - } catch (JsonProcessingException e) { - log.warn("[{}] Failed to lookup device by gateway id and name: [{}]", gatewayId, requestMsg.getDeviceName(), e); - throw new RuntimeException(e); - } finally { - deviceCreationLock.unlock(); } - }, dbCallbackExecutorService); + GetOrCreateDeviceFromGatewayResponseMsg.Builder builder = GetOrCreateDeviceFromGatewayResponseMsg.newBuilder() + .setDeviceInfo(getDeviceInfoProto(device)); + DeviceProfile deviceProfile = deviceProfileCache.get(device.getTenantId(), device.getDeviceProfileId()); + if (deviceProfile != null) { + builder.setProfileBody(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfile))); + } else { + log.warn("[{}] Failed to find device profile [{}] for device. ", device.getId(), device.getDeviceProfileId()); + } + return TransportApiResponseMsg.newBuilder() + .setGetOrCreateDeviceResponseMsg(builder.build()) + .build(); + } catch (JsonProcessingException e) { + log.warn("[{}] Failed to lookup device by gateway id and name: [{}]", gatewayId, requestMsg.getDeviceName(), e); + throw new RuntimeException(e); + } catch (EntitiesLimitException e) { + log.warn("[{}][{}] API limit exception: [{}]", e.getTenantId(), gatewayId, e.getMessage()); + return TransportApiResponseMsg.newBuilder() + .setGetOrCreateDeviceResponseMsg( + GetOrCreateDeviceFromGatewayResponseMsg.newBuilder() + .setError(TransportProtos.TransportApiRequestErrorCode.ENTITY_LIMIT)) + .build(); + } finally { + deviceCreationLock.unlock(); + } } private ListenableFuture handle(ProvisionDeviceRequestMsg requestMsg) { @@ -488,10 +521,10 @@ public class DefaultTransportApiService implements TransportApiService { ResourceType resourceType = ResourceType.valueOf(requestMsg.getResourceType()); String resourceKey = requestMsg.getResourceKey(); TransportProtos.GetResourceResponseMsg.Builder builder = TransportProtos.GetResourceResponseMsg.newBuilder(); - TbResource resource = resourceService.getResource(tenantId, resourceType, resourceKey); + TbResource resource = resourceService.findResourceByTenantIdAndKey(tenantId, resourceType, resourceKey); if (resource == null && !tenantId.equals(TenantId.SYS_TENANT_ID)) { - resource = resourceService.getResource(TenantId.SYS_TENANT_ID, resourceType, resourceKey); + resource = resourceService.findResourceByTenantIdAndKey(TenantId.SYS_TENANT_ID, resourceType, resourceKey); } if (resource != null) { @@ -517,31 +550,30 @@ public class DefaultTransportApiService implements TransportApiService { .build()); } - private ListenableFuture getDeviceInfo(DeviceCredentials credentials) { - return Futures.transform(deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, credentials.getDeviceId()), device -> { - if (device == null) { - log.trace("[{}] Failed to lookup device by id", credentials.getDeviceId()); - return getEmptyTransportApiResponse(); + TransportApiResponseMsg getDeviceInfo(DeviceCredentials credentials) { + Device device = deviceService.findDeviceById(TenantId.SYS_TENANT_ID, credentials.getDeviceId()); + if (device == null) { + log.trace("[{}] Failed to lookup device by id", credentials.getDeviceId()); + return getEmptyTransportApiResponse(); + } + try { + ValidateDeviceCredentialsResponseMsg.Builder builder = ValidateDeviceCredentialsResponseMsg.newBuilder(); + builder.setDeviceInfo(getDeviceInfoProto(device)); + DeviceProfile deviceProfile = deviceProfileCache.get(device.getTenantId(), device.getDeviceProfileId()); + if (deviceProfile != null) { + builder.setProfileBody(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfile))); + } else { + log.warn("[{}] Failed to find device profile [{}] for device. ", device.getId(), device.getDeviceProfileId()); } - try { - ValidateDeviceCredentialsResponseMsg.Builder builder = ValidateDeviceCredentialsResponseMsg.newBuilder(); - builder.setDeviceInfo(getDeviceInfoProto(device)); - DeviceProfile deviceProfile = deviceProfileCache.get(device.getTenantId(), device.getDeviceProfileId()); - if (deviceProfile != null) { - builder.setProfileBody(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfile))); - } else { - log.warn("[{}] Failed to find device profile [{}] for device. ", device.getId(), device.getDeviceProfileId()); - } - if (!StringUtils.isEmpty(credentials.getCredentialsValue())) { - builder.setCredentialsBody(credentials.getCredentialsValue()); - } - return TransportApiResponseMsg.newBuilder() - .setValidateCredResponseMsg(builder.build()).build(); - } catch (JsonProcessingException e) { - log.warn("[{}] Failed to lookup device by id", credentials.getDeviceId(), e); - return getEmptyTransportApiResponse(); + if (!StringUtils.isEmpty(credentials.getCredentialsValue())) { + builder.setCredentialsBody(credentials.getCredentialsValue()); } - }, MoreExecutors.directExecutor()); + return TransportApiResponseMsg.newBuilder() + .setValidateCredResponseMsg(builder.build()).build(); + } catch (JsonProcessingException e) { + log.warn("[{}] Failed to lookup device by id", credentials.getDeviceId(), e); + return getEmptyTransportApiResponse(); + } } private DeviceInfoProto getDeviceInfoProto(Device device) throws JsonProcessingException { diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java index 5154829ea6..416f95cf96 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java @@ -39,7 +39,9 @@ import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.action.EntityActionService; import java.util.Date; +import java.util.HashSet; import java.util.Optional; +import java.util.Set; import java.util.concurrent.TimeUnit; @TbCoreComponent @@ -86,20 +88,24 @@ public class AlarmsCleanUpService { PageLink removalBatchRequest = new PageLink(removalBatchSize, 0); long totalRemoved = 0; + Set typesToRemove = new HashSet<>(); while (true) { PageData toRemove = alarmDao.findAlarmsIdsByEndTsBeforeAndTenantId(expirationTime, tenantId, removalBatchRequest); for (AlarmId alarmId : toRemove.getData()) { - relationService.deleteEntityRelations(tenantId, alarmId); - Alarm alarm = alarmService.delAlarm(tenantId, alarmId).getAlarm(); + Alarm alarm = alarmService.delAlarm(tenantId, alarmId, false).getAlarm(); if (alarm != null) { entityActionService.pushEntityActionToRuleEngine(alarm.getOriginator(), alarm, tenantId, null, ActionType.ALARM_DELETE, null); totalRemoved++; + typesToRemove.add(alarm.getType()); } } if (!toRemove.hasNext()) { break; } } + + alarmService.delAlarmTypes(tenantId, typesToRemove); + if (totalRemoved > 0) { log.info("Removed {} outdated alarm(s) for tenant {} older than {}", totalRemoved, tenantId, new Date(expirationTime)); } diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/NotificationsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/NotificationsCleanUpService.java index 65b04ef0ab..b7e6e57e04 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/NotificationsCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/NotificationsCleanUpService.java @@ -63,8 +63,8 @@ public class NotificationsCleanUpService extends AbstractCleanUpService { if (lastRemovedNotificationTs > 0) { long gap = TimeUnit.MINUTES.toMillis(10); long requestExpTime = lastRemovedNotificationTs - TimeUnit.SECONDS.toMillis(NotificationRequestConfig.MAX_SENDING_DELAY) - gap; - // TODO: double-check this - notificationRequestDao.removeAllByCreatedTimeBefore(requestExpTime); + int removed = notificationRequestDao.removeAllByCreatedTimeBefore(requestExpTime); + log.info("Removed {} outdated notification requests older than {}", removed, requestExpTime); } } diff --git a/application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java b/application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java index 587d86af32..ebc253ca9e 100644 --- a/application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java @@ -27,11 +27,14 @@ import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.common.data.EdgeUpgradeMessage; import org.thingsboard.server.common.data.UpdateMessage; -import org.thingsboard.server.common.msg.notification.trigger.NewPlatformVersionTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.NewPlatformVersionTrigger; import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.queue.util.AfterStartUp; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.edge.instructions.EdgeInstallInstructionsService; +import org.thingsboard.server.service.edge.instructions.EdgeUpgradeInstructionsService; import javax.annotation.PreDestroy; import java.io.IOException; @@ -65,6 +68,12 @@ public class DefaultUpdateService implements UpdateService { @Autowired private NotificationRuleProcessor notificationRuleProcessor; + @Autowired(required = false) + private EdgeInstallInstructionsService edgeInstallInstructionsService; + + @Autowired(required = false) + private EdgeUpgradeInstructionsService edgeUpgradeInstructionsService; + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, ThingsBoardThreadFactory.forName("tb-update-service")); private ScheduledFuture checkUpdatesFuture = null; @@ -141,6 +150,16 @@ public class DefaultUpdateService implements UpdateService { .updateInfo(updateMessage) .build()); } + ObjectNode edgeRequest = JacksonUtil.newObjectNode().put(VERSION_PARAM, version); + String edgeInstallVersion = restClient.postForObject(UPDATE_SERVER_BASE_URL + "/api/v1/edge/installMapping", new HttpEntity<>(edgeRequest.toString(), headers), String.class); + if (edgeInstallVersion != null) { + edgeInstallInstructionsService.setAppVersion(edgeInstallVersion); + edgeUpgradeInstructionsService.setAppVersion(edgeInstallVersion); + } + EdgeUpgradeMessage edgeUpgradeMessage = restClient.postForObject(UPDATE_SERVER_BASE_URL + "/api/v1/edge/upgradeMapping", new HttpEntity<>(edgeRequest.toString(), headers), EdgeUpgradeMessage.class); + if (edgeUpgradeMessage != null) { + edgeUpgradeInstructionsService.updateInstructionMap(edgeUpgradeMessage.getEdgeVersions()); + } } catch (Exception e) { log.trace(e.getMessage()); } @@ -150,5 +169,4 @@ public class DefaultUpdateService implements UpdateService { public UpdateMessage checkUpdates() { return updateMessage; } - } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/AuthCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/AuthCmd.java new file mode 100644 index 0000000000..776cdd40d2 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ws/AuthCmd.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ws; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class AuthCmd implements WsCmd { + private int cmdId; + private String token; + + @Override + public WsCmdType getType() { + return WsCmdType.AUTH; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java index 21d69d6b42..3e9ee69cfc 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java @@ -21,8 +21,10 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; +import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.socket.CloseStatus; @@ -62,11 +64,8 @@ import org.thingsboard.server.service.subscription.TbAttributeSubscription; import org.thingsboard.server.service.subscription.TbAttributeSubscriptionScope; import org.thingsboard.server.service.subscription.TbEntityDataSubscriptionService; import org.thingsboard.server.service.subscription.TbLocalSubscriptionService; -import org.thingsboard.server.service.subscription.TbTimeseriesSubscription; +import org.thingsboard.server.service.subscription.TbTimeSeriesSubscription; import org.thingsboard.server.service.ws.notification.NotificationCommandsHandler; -import org.thingsboard.server.service.ws.notification.cmd.NotificationCmdsWrapper; -import org.thingsboard.server.service.ws.notification.cmd.WsCmd; -import org.thingsboard.server.service.ws.telemetry.cmd.TelemetryPluginCmdsWrapper; import org.thingsboard.server.service.ws.telemetry.cmd.v1.AttributesSubscriptionCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v1.GetHistoryCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v1.SubscriptionCmd; @@ -87,6 +86,7 @@ import javax.annotation.PreDestroy; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -123,7 +123,6 @@ public class DefaultWebSocketService implements WebSocketService { private static final String FAILED_TO_FETCH_DATA = "Failed to fetch data!"; private static final String FAILED_TO_FETCH_ATTRIBUTES = "Failed to fetch attributes!"; private static final String SESSION_META_DATA_NOT_FOUND = "Session meta-data not found!"; - private static final String FAILED_TO_PARSE_WS_COMMAND = "Failed to parse websocket command!"; private final ConcurrentMap wsSessionsMap = new ConcurrentHashMap<>(); @@ -149,8 +148,7 @@ public class DefaultWebSocketService implements WebSocketService { private ScheduledExecutorService pingExecutor; private String serviceId; - private List> telemetryCmdsHandlers; - private List> notificationCmdsHandlers; + private Map> cmdsHandlers; @PostConstruct public void init() { @@ -160,26 +158,23 @@ public class DefaultWebSocketService implements WebSocketService { pingExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("telemetry-web-socket-ping")); pingExecutor.scheduleWithFixedDelay(this::sendPing, pingTimeout / NUMBER_OF_PING_ATTEMPTS, pingTimeout / NUMBER_OF_PING_ATTEMPTS, TimeUnit.MILLISECONDS); - telemetryCmdsHandlers = List.of( - newCmdsHandler(TelemetryPluginCmdsWrapper::getAttrSubCmds, this::handleWsAttributesSubscriptionCmd), - newCmdsHandler(TelemetryPluginCmdsWrapper::getTsSubCmds, this::handleWsTimeseriesSubscriptionCmd), - newCmdsHandler(TelemetryPluginCmdsWrapper::getHistoryCmds, this::handleWsHistoryCmd), - newCmdsHandler(TelemetryPluginCmdsWrapper::getEntityDataCmds, this::handleWsEntityDataCmd), - newCmdsHandler(TelemetryPluginCmdsWrapper::getAlarmDataCmds, this::handleWsAlarmDataCmd), - newCmdsHandler(TelemetryPluginCmdsWrapper::getEntityCountCmds, this::handleWsEntityCountCmd), - newCmdsHandler(TelemetryPluginCmdsWrapper::getAlarmCountCmds, this::handleWsAlarmCountCmd), - newCmdsHandler(TelemetryPluginCmdsWrapper::getEntityDataUnsubscribeCmds, this::handleWsDataUnsubscribeCmd), - newCmdsHandler(TelemetryPluginCmdsWrapper::getAlarmDataUnsubscribeCmds, this::handleWsDataUnsubscribeCmd), - newCmdsHandler(TelemetryPluginCmdsWrapper::getEntityCountUnsubscribeCmds, this::handleWsDataUnsubscribeCmd), - newCmdsHandler(TelemetryPluginCmdsWrapper::getAlarmCountUnsubscribeCmds, this::handleWsDataUnsubscribeCmd) - ); - notificationCmdsHandlers = List.of( - newCmdHandler(NotificationCmdsWrapper::getUnreadSubCmd, notificationCmdsHandler::handleUnreadNotificationsSubCmd), - newCmdHandler(NotificationCmdsWrapper::getUnreadCountSubCmd, notificationCmdsHandler::handleUnreadNotificationsCountSubCmd), - newCmdHandler(NotificationCmdsWrapper::getMarkAsReadCmd, notificationCmdsHandler::handleMarkAsReadCmd), - newCmdHandler(NotificationCmdsWrapper::getMarkAllAsReadCmd, notificationCmdsHandler::handleMarkAllAsReadCmd), - newCmdHandler(NotificationCmdsWrapper::getUnsubCmd, notificationCmdsHandler::handleUnsubCmd) - ); + cmdsHandlers = new EnumMap<>(WsCmdType.class); + cmdsHandlers.put(WsCmdType.ATTRIBUTES, newCmdHandler(this::handleWsAttributesSubscriptionCmd)); + cmdsHandlers.put(WsCmdType.TIMESERIES, newCmdHandler(this::handleWsTimeseriesSubscriptionCmd)); + cmdsHandlers.put(WsCmdType.TIMESERIES_HISTORY, newCmdHandler(this::handleWsHistoryCmd)); + cmdsHandlers.put(WsCmdType.ENTITY_DATA, newCmdHandler(this::handleWsEntityDataCmd)); + cmdsHandlers.put(WsCmdType.ALARM_DATA, newCmdHandler(this::handleWsAlarmDataCmd)); + cmdsHandlers.put(WsCmdType.ENTITY_COUNT, newCmdHandler(this::handleWsEntityCountCmd)); + cmdsHandlers.put(WsCmdType.ALARM_COUNT, newCmdHandler(this::handleWsAlarmCountCmd)); + cmdsHandlers.put(WsCmdType.ENTITY_DATA_UNSUBSCRIBE, newCmdHandler(this::handleWsDataUnsubscribeCmd)); + cmdsHandlers.put(WsCmdType.ALARM_DATA_UNSUBSCRIBE, newCmdHandler(this::handleWsDataUnsubscribeCmd)); + cmdsHandlers.put(WsCmdType.ENTITY_COUNT_UNSUBSCRIBE, newCmdHandler(this::handleWsDataUnsubscribeCmd)); + cmdsHandlers.put(WsCmdType.ALARM_COUNT_UNSUBSCRIBE, newCmdHandler(this::handleWsDataUnsubscribeCmd)); + cmdsHandlers.put(WsCmdType.NOTIFICATIONS, newCmdHandler(notificationCmdsHandler::handleUnreadNotificationsSubCmd)); + cmdsHandlers.put(WsCmdType.NOTIFICATIONS_COUNT, newCmdHandler(notificationCmdsHandler::handleUnreadNotificationsCountSubCmd)); + cmdsHandlers.put(WsCmdType.MARK_NOTIFICATIONS_AS_READ, newCmdHandler(notificationCmdsHandler::handleMarkAsReadCmd)); + cmdsHandlers.put(WsCmdType.MARK_ALL_NOTIFICATIONS_AS_READ, newCmdHandler(notificationCmdsHandler::handleMarkAllAsReadCmd)); + cmdsHandlers.put(WsCmdType.NOTIFICATIONS_UNSUBSCRIBE, newCmdHandler(notificationCmdsHandler::handleUnsubCmd)); } @PreDestroy @@ -194,7 +189,7 @@ public class DefaultWebSocketService implements WebSocketService { } @Override - public void handleWebSocketSessionEvent(WebSocketSessionRef sessionRef, SessionEvent event) { + public void handleSessionEvent(WebSocketSessionRef sessionRef, SessionEvent event) { String sessionId = sessionRef.getSessionId(); log.debug(PROCESSING_MSG, sessionId, event); switch (event.getEventType()) { @@ -214,120 +209,75 @@ public class DefaultWebSocketService implements WebSocketService { } @Override - public void handleWebSocketMsg(WebSocketSessionRef sessionRef, String msg) { - if (log.isTraceEnabled()) { - log.trace("[{}] Processing: {}", sessionRef.getSessionId(), msg); - } - - try { - switch (sessionRef.getSessionType()) { - case TELEMETRY: - processTelemetryCmds(sessionRef, msg); - break; - case NOTIFICATIONS: - processNotificationCmds(sessionRef, msg); - break; - } - } catch (IOException e) { - log.warn("Failed to decode subscription cmd: {}", e.getMessage(), e); - sendWsMsg(sessionRef, new TelemetrySubscriptionUpdate(UNKNOWN_SUBSCRIPTION_ID, SubscriptionErrorCode.BAD_REQUEST, FAILED_TO_PARSE_WS_COMMAND)); - } - } - - private void processTelemetryCmds(WebSocketSessionRef sessionRef, String msg) throws JsonProcessingException { - TelemetryPluginCmdsWrapper cmdsWrapper = JacksonUtil.fromString(msg, TelemetryPluginCmdsWrapper.class); - if (cmdsWrapper == null) { + public void handleCommands(WebSocketSessionRef sessionRef, WsCommandsWrapper commandsWrapper) { + if (commandsWrapper == null || CollectionUtils.isEmpty(commandsWrapper.getCmds())) { return; } - for (WsCmdListHandler cmdHandler : telemetryCmdsHandlers) { - List cmds = cmdHandler.extractCmds(cmdsWrapper); - if (cmds != null) { - cmdHandler.handle(sessionRef, cmds); - } + String sessionId = sessionRef.getSessionId(); + if (!validateSessionMetadata(sessionRef, UNKNOWN_SUBSCRIPTION_ID, sessionId)) { + return; } - } - private void processNotificationCmds(WebSocketSessionRef sessionRef, String msg) throws IOException { - NotificationCmdsWrapper cmdsWrapper = JacksonUtil.fromString(msg, NotificationCmdsWrapper.class); - for (WsCmdHandler cmdHandler : notificationCmdsHandlers) { - WsCmd cmd = cmdHandler.extractCmd(cmdsWrapper); - if (cmd != null) { - String sessionId = sessionRef.getSessionId(); - if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId)) { - try { - cmdHandler.handle(sessionRef, cmd); - } catch (Exception e) { - log.error("[sessionId: {}, tenantId: {}, userId: {}] Failed to handle WS cmd: {}", sessionId, - sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), cmd, e); - } - } + for (WsCmd cmd : commandsWrapper.getCmds()) { + log.debug("[{}][{}][{}] Processing cmd: {}", sessionId, cmd.getType(), cmd.getCmdId(), cmd); + try { + Optional.ofNullable(cmdsHandlers.get(cmd.getType())) + .ifPresent(cmdHandler -> cmdHandler.handle(sessionRef, cmd)); + } catch (Exception e) { + log.error("[sessionId: {}, tenantId: {}, userId: {}] Failed to handle WS cmd: {}", sessionId, + sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), cmd, e); } } } private void handleWsEntityDataCmd(WebSocketSessionRef sessionRef, EntityDataCmd cmd) { - String sessionId = sessionRef.getSessionId(); - log.debug("[{}] Processing: {}", sessionId, cmd); - - if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId) - && validateSubscriptionCmd(sessionRef, cmd)) { + if (validateSubscriptionCmd(sessionRef, cmd)) { entityDataSubService.handleCmd(sessionRef, cmd); } } private void handleWsEntityCountCmd(WebSocketSessionRef sessionRef, EntityCountCmd cmd) { - String sessionId = sessionRef.getSessionId(); - log.debug("[{}] Processing: {}", sessionId, cmd); - - if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId) - && validateSubscriptionCmd(sessionRef, cmd)) { + if (validateSubscriptionCmd(sessionRef, cmd)) { entityDataSubService.handleCmd(sessionRef, cmd); } } private void handleWsAlarmDataCmd(WebSocketSessionRef sessionRef, AlarmDataCmd cmd) { - String sessionId = sessionRef.getSessionId(); - log.debug("[{}] Processing: {}", sessionId, cmd); - - if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId) - && validateSubscriptionCmd(sessionRef, cmd)) { + if (validateSubscriptionCmd(sessionRef, cmd)) { entityDataSubService.handleCmd(sessionRef, cmd); } } private void handleWsDataUnsubscribeCmd(WebSocketSessionRef sessionRef, UnsubscribeCmd cmd) { - String sessionId = sessionRef.getSessionId(); - log.debug("[{}] Processing: {}", sessionId, cmd); - - if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId)) { - entityDataSubService.cancelSubscription(sessionRef.getSessionId(), cmd); - } + entityDataSubService.cancelSubscription(sessionRef.getSessionId(), cmd); } private void handleWsAlarmCountCmd(WebSocketSessionRef sessionRef, AlarmCountCmd cmd) { - String sessionId = sessionRef.getSessionId(); - log.debug("[{}] Processing: {}", sessionId, cmd); - - if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId) - && validateSubscriptionCmd(sessionRef, cmd)) { + if (validateCmd(sessionRef, cmd)) { entityDataSubService.handleCmd(sessionRef, cmd); } } @Override - public void sendWsMsg(String sessionId, TelemetrySubscriptionUpdate update) { - sendWsMsg(sessionId, update.getSubscriptionId(), update); + public void sendUpdate(String sessionId, TelemetrySubscriptionUpdate update) { + sendUpdate(sessionId, update.getSubscriptionId(), update); + } + + @Override + public void sendUpdate(String sessionId, CmdUpdate update) { + sendUpdate(sessionId, update.getCmdId(), update); } @Override - public void sendWsMsg(String sessionId, CmdUpdate update) { - sendWsMsg(sessionId, update.getCmdId(), update); + public void sendError(WebSocketSessionRef sessionRef, int subId, SubscriptionErrorCode errorCode, String errorMsg) { + TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(subId, errorCode, errorMsg); + sendUpdate(sessionRef, update); } - private void sendWsMsg(String sessionId, int cmdId, T update) { + private void sendUpdate(String sessionId, int cmdId, T update) { WsSessionMetaData md = wsSessionsMap.get(sessionId); if (md != null) { - sendWsMsg(md.getSessionRef(), cmdId, update); + sendUpdate(md.getSessionRef(), cmdId, update); } } @@ -443,7 +393,7 @@ public class DefaultWebSocketService implements WebSocketService { } } } catch (IOException e) { - log.warn("[{}] Failed to send session close: {}", sessionRef.getSessionId(), e); + log.warn("[{}] Failed to send session close:", sessionRef.getSessionId(), e); return false; } return true; @@ -455,21 +405,17 @@ public class DefaultWebSocketService implements WebSocketService { } String sessionId = sessionRef.getSessionId(); - log.debug("[{}] Processing: {}", sessionId, cmd); - - if (validateSessionMetadata(sessionRef, cmd, sessionId)) { - if (cmd.isUnsubscribe()) { - unsubscribe(sessionRef, cmd, sessionId); - } else if (validateSubscriptionCmd(sessionRef, cmd)) { - EntityId entityId = EntityIdFactory.getByTypeAndId(cmd.getEntityType(), cmd.getEntityId()); - log.debug("[{}] fetching latest attributes ({}) values for device: {}", sessionId, cmd.getKeys(), entityId); - Optional> keysOptional = getKeys(cmd); - if (keysOptional.isPresent()) { - List keys = new ArrayList<>(keysOptional.get()); - handleWsAttributesSubscriptionByKeys(sessionRef, cmd, sessionId, entityId, keys); - } else { - handleWsAttributesSubscription(sessionRef, cmd, sessionId, entityId); - } + if (cmd.isUnsubscribe()) { + unsubscribe(sessionRef, cmd, sessionId); + } else if (validateSubscriptionCmd(sessionRef, cmd)) { + EntityId entityId = EntityIdFactory.getByTypeAndId(cmd.getEntityType(), cmd.getEntityId()); + log.debug("[{}] fetching latest attributes ({}) values for device: {}", sessionId, cmd.getKeys(), entityId); + Optional> keysOptional = getKeys(cmd); + if (keysOptional.isPresent()) { + List keys = new ArrayList<>(keysOptional.get()); + handleWsAttributesSubscriptionByKeys(sessionRef, cmd, sessionId, entityId, keys); + } else { + handleWsAttributesSubscription(sessionRef, cmd, sessionId, entityId); } } } @@ -477,6 +423,7 @@ public class DefaultWebSocketService implements WebSocketService { private void handleWsAttributesSubscriptionByKeys(WebSocketSessionRef sessionRef, AttributesSubscriptionCmd cmd, String sessionId, EntityId entityId, List keys) { + long queryTs = System.currentTimeMillis(); FutureCallback> callback = new FutureCallback<>() { @Override public void onSuccess(List data) { @@ -495,13 +442,14 @@ public class DefaultWebSocketService implements WebSocketService { .subscriptionId(cmd.getCmdId()) .tenantId(sessionRef.getSecurityCtx().getTenantId()) .entityId(entityId) + .queryTs(queryTs) .allKeys(false) .keyStates(subState) .scope(scope) .updateProcessor((subscription, update) -> { subLock.lock(); try { - sendWsMsg(subscription.getSessionId(), update); + sendUpdate(subscription.getSessionId(), update); } finally { subLock.unlock(); } @@ -509,9 +457,9 @@ public class DefaultWebSocketService implements WebSocketService { .build(); subLock.lock(); - try{ + try { oldSubService.addSubscription(sub); - sendWsMsg(sessionRef, new TelemetrySubscriptionUpdate(cmd.getCmdId(), attributesData)); + sendUpdate(sessionRef, new TelemetrySubscriptionUpdate(cmd.getCmdId(), attributesData)); } finally { subLock.unlock(); } @@ -529,7 +477,7 @@ public class DefaultWebSocketService implements WebSocketService { update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR, FAILED_TO_FETCH_ATTRIBUTES); } - sendWsMsg(sessionRef, update); + sendUpdate(sessionRef, update); } }; @@ -541,27 +489,15 @@ public class DefaultWebSocketService implements WebSocketService { } private void handleWsHistoryCmd(WebSocketSessionRef sessionRef, GetHistoryCmd cmd) { - String sessionId = sessionRef.getSessionId(); - WsSessionMetaData sessionMD = wsSessionsMap.get(sessionId); - if (sessionMD == null) { - log.warn("[{}] Session meta data not found. ", sessionId); - TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR, - SESSION_META_DATA_NOT_FOUND); - sendWsMsg(sessionRef, update); - return; - } - if (cmd.getEntityId() == null || cmd.getEntityId().isEmpty() || cmd.getEntityType() == null || cmd.getEntityType().isEmpty()) { - TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, - "Device id is empty!"); - sendWsMsg(sessionRef, update); - return; - } - if (cmd.getKeys() == null || cmd.getKeys().isEmpty()) { - TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, - "Keys are empty!"); - sendWsMsg(sessionRef, update); - return; - } + if (!validateCmd(sessionRef, cmd, () -> { + if (cmd.getEntityId() == null || cmd.getEntityId().isEmpty() || cmd.getEntityType() == null || cmd.getEntityType().isEmpty()) { + throw new IllegalArgumentException("Device id is empty!"); + } + if (cmd.getKeys() == null || cmd.getKeys().isEmpty()) { + throw new IllegalArgumentException("Keys are empty!"); + } + })) return; + EntityId entityId = EntityIdFactory.getByTypeAndId(cmd.getEntityType(), cmd.getEntityId()); List keys = new ArrayList<>(getKeys(cmd).orElse(Collections.emptySet())); List queries = keys.stream().map(key -> new BaseReadTsKvQuery(key, cmd.getStartTs(), cmd.getEndTs(), cmd.getInterval(), getLimit(cmd.getLimit()), getAggregation(cmd.getAgg()))) @@ -570,7 +506,7 @@ public class DefaultWebSocketService implements WebSocketService { FutureCallback> callback = new FutureCallback>() { @Override public void onSuccess(List data) { - sendWsMsg(sessionRef, new TelemetrySubscriptionUpdate(cmd.getCmdId(), data)); + sendUpdate(sessionRef, new TelemetrySubscriptionUpdate(cmd.getCmdId(), data)); } @Override @@ -583,7 +519,7 @@ public class DefaultWebSocketService implements WebSocketService { update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR, FAILED_TO_FETCH_DATA); } - sendWsMsg(sessionRef, update); + sendUpdate(sessionRef, update); } }; accessValidator.validate(sessionRef.getSecurityCtx(), Operation.READ_TELEMETRY, entityId, @@ -591,7 +527,10 @@ public class DefaultWebSocketService implements WebSocketService { } private void handleWsAttributesSubscription(WebSocketSessionRef sessionRef, - AttributesSubscriptionCmd cmd, String sessionId, EntityId entityId) { + AttributesSubscriptionCmd cmd, + String sessionId, + EntityId entityId) { + long queryTs = System.currentTimeMillis(); FutureCallback> callback = new FutureCallback<>() { @Override public void onSuccess(List data) { @@ -609,12 +548,13 @@ public class DefaultWebSocketService implements WebSocketService { .subscriptionId(cmd.getCmdId()) .tenantId(sessionRef.getSecurityCtx().getTenantId()) .entityId(entityId) + .queryTs(queryTs) .allKeys(true) .keyStates(subState) .updateProcessor((subscription, update) -> { subLock.lock(); try { - sendWsMsg(subscription.getSessionId(), update); + sendUpdate(subscription.getSessionId(), update); } finally { subLock.unlock(); } @@ -625,7 +565,7 @@ public class DefaultWebSocketService implements WebSocketService { subLock.lock(); try { oldSubService.addSubscription(sub); - sendWsMsg(sessionRef, new TelemetrySubscriptionUpdate(cmd.getCmdId(), attributesData)); + sendUpdate(sessionRef, new TelemetrySubscriptionUpdate(cmd.getCmdId(), attributesData)); } finally { subLock.unlock(); } @@ -634,9 +574,7 @@ public class DefaultWebSocketService implements WebSocketService { @Override public void onFailure(Throwable e) { log.error(FAILED_TO_FETCH_ATTRIBUTES, e); - TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR, - FAILED_TO_FETCH_ATTRIBUTES); - sendWsMsg(sessionRef, update); + sendError(sessionRef, cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR, FAILED_TO_FETCH_ATTRIBUTES); } }; @@ -654,27 +592,24 @@ public class DefaultWebSocketService implements WebSocketService { } String sessionId = sessionRef.getSessionId(); - log.debug("[{}] Processing: {}", sessionId, cmd); - - if (validateSessionMetadata(sessionRef, cmd, sessionId)) { - if (cmd.isUnsubscribe()) { - unsubscribe(sessionRef, cmd, sessionId); - } else if (validateSubscriptionCmd(sessionRef, cmd)) { - EntityId entityId = EntityIdFactory.getByTypeAndId(cmd.getEntityType(), cmd.getEntityId()); - Optional> keysOptional = getKeys(cmd); - - if (keysOptional.isPresent()) { - handleWsTimeseriesSubscriptionByKeys(sessionRef, cmd, sessionId, entityId); - } else { - handleWsTimeseriesSubscription(sessionRef, cmd, sessionId, entityId); - } + if (cmd.isUnsubscribe()) { + unsubscribe(sessionRef, cmd, sessionId); + } else if (validateSubscriptionCmd(sessionRef, cmd)) { + EntityId entityId = EntityIdFactory.getByTypeAndId(cmd.getEntityType(), cmd.getEntityId()); + Optional> keysOptional = getKeys(cmd); + + if (keysOptional.isPresent()) { + handleWsTimeSeriesSubscriptionByKeys(sessionRef, cmd, sessionId, entityId); + } else { + handleWsTimeSeriesSubscription(sessionRef, cmd, sessionId, entityId); } } } - private void handleWsTimeseriesSubscriptionByKeys(WebSocketSessionRef sessionRef, + private void handleWsTimeSeriesSubscriptionByKeys(WebSocketSessionRef sessionRef, TimeseriesSubscriptionCmd cmd, String sessionId, EntityId entityId) { long startTs; + long queryTs = System.currentTimeMillis(); if (cmd.getTimeWindow() > 0) { List keys = new ArrayList<>(getKeys(cmd).orElse(Collections.emptySet())); log.debug("[{}] fetching timeseries data for last {} ms for keys: ({}) for device : {}", sessionId, cmd.getTimeWindow(), cmd.getKeys(), entityId); @@ -682,22 +617,22 @@ public class DefaultWebSocketService implements WebSocketService { long endTs = cmd.getStartTs() + cmd.getTimeWindow(); List queries = keys.stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, cmd.getInterval(), getLimit(cmd.getLimit()), getAggregation(cmd.getAgg()))).collect(Collectors.toList()); - - final FutureCallback> callback = getSubscriptionCallback(sessionRef, cmd, sessionId, entityId, startTs, keys); + final FutureCallback> callback = getSubscriptionCallback(sessionRef, cmd, sessionId, entityId, queryTs, startTs, keys); accessValidator.validate(sessionRef.getSecurityCtx(), Operation.READ_TELEMETRY, entityId, on(r -> Futures.addCallback(tsService.findAll(sessionRef.getSecurityCtx().getTenantId(), entityId, queries), callback, executor), callback::onFailure)); } else { List keys = new ArrayList<>(getKeys(cmd).orElse(Collections.emptySet())); startTs = System.currentTimeMillis(); log.debug("[{}] fetching latest timeseries data for keys: ({}) for device : {}", sessionId, cmd.getKeys(), entityId); - final FutureCallback> callback = getSubscriptionCallback(sessionRef, cmd, sessionId, entityId, startTs, keys); + final FutureCallback> callback = getSubscriptionCallback(sessionRef, cmd, sessionId, entityId, queryTs, startTs, keys); accessValidator.validate(sessionRef.getSecurityCtx(), Operation.READ_TELEMETRY, entityId, on(r -> Futures.addCallback(tsService.findLatest(sessionRef.getSecurityCtx().getTenantId(), entityId, keys), callback, executor), callback::onFailure)); } } - private void handleWsTimeseriesSubscription(WebSocketSessionRef sessionRef, + private void handleWsTimeSeriesSubscription(WebSocketSessionRef sessionRef, TimeseriesSubscriptionCmd cmd, String sessionId, EntityId entityId) { + long queryTs = System.currentTimeMillis(); FutureCallback> callback = new FutureCallback>() { @Override public void onSuccess(List data) { @@ -705,7 +640,7 @@ public class DefaultWebSocketService implements WebSocketService { data.forEach(v -> subState.put(v.getKey(), v.getTs())); Lock subLock = new ReentrantLock(); - TbTimeseriesSubscription sub = TbTimeseriesSubscription.builder() + TbTimeSeriesSubscription sub = TbTimeSeriesSubscription.builder() .serviceId(serviceId) .sessionId(sessionId) .subscriptionId(cmd.getCmdId()) @@ -714,11 +649,12 @@ public class DefaultWebSocketService implements WebSocketService { .updateProcessor((subscription, update) -> { subLock.lock(); try { - sendWsMsg(subscription.getSessionId(), update); + sendUpdate(subscription.getSessionId(), update); } finally { subLock.unlock(); } }) + .queryTs(queryTs) .allKeys(true) .keyStates(subState) .build(); @@ -726,7 +662,7 @@ public class DefaultWebSocketService implements WebSocketService { subLock.lock(); try { oldSubService.addSubscription(sub); - sendWsMsg(sessionRef, new TelemetrySubscriptionUpdate(cmd.getCmdId(), data)); + sendUpdate(sessionRef, new TelemetrySubscriptionUpdate(cmd.getCmdId(), data)); } finally { subLock.unlock(); } @@ -742,14 +678,15 @@ public class DefaultWebSocketService implements WebSocketService { update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR, FAILED_TO_FETCH_DATA); } - sendWsMsg(sessionRef, update); + sendUpdate(sessionRef, update); } }; accessValidator.validate(sessionRef.getSecurityCtx(), Operation.READ_TELEMETRY, entityId, on(r -> Futures.addCallback(tsService.findAllLatest(sessionRef.getSecurityCtx().getTenantId(), entityId), callback, executor), callback::onFailure)); } - private FutureCallback> getSubscriptionCallback(final WebSocketSessionRef sessionRef, final TimeseriesSubscriptionCmd cmd, final String sessionId, final EntityId entityId, final long startTs, final List keys) { + private FutureCallback> getSubscriptionCallback(final WebSocketSessionRef sessionRef, final TimeseriesSubscriptionCmd cmd, + final String sessionId, final EntityId entityId, final long queryTs, final long startTs, final List keys) { return new FutureCallback<>() { @Override public void onSuccess(List data) { @@ -758,7 +695,7 @@ public class DefaultWebSocketService implements WebSocketService { data.forEach(v -> subState.put(v.getKey(), v.getTs())); Lock subLock = new ReentrantLock(); - TbTimeseriesSubscription sub = TbTimeseriesSubscription.builder() + TbTimeSeriesSubscription sub = TbTimeSeriesSubscription.builder() .serviceId(serviceId) .sessionId(sessionId) .subscriptionId(cmd.getCmdId()) @@ -767,19 +704,20 @@ public class DefaultWebSocketService implements WebSocketService { .updateProcessor((subscription, update) -> { subLock.lock(); try { - sendWsMsg(subscription.getSessionId(), update); + sendUpdate(subscription.getSessionId(), update); } finally { subLock.unlock(); } }) + .queryTs(queryTs) .allKeys(false) .keyStates(subState) .build(); subLock.lock(); - try{ + try { oldSubService.addSubscription(sub); - sendWsMsg(sessionRef, new TelemetrySubscriptionUpdate(cmd.getCmdId(), data)); + sendUpdate(sessionRef, new TelemetrySubscriptionUpdate(cmd.getCmdId(), data)); } finally { subLock.unlock(); } @@ -792,9 +730,7 @@ public class DefaultWebSocketService implements WebSocketService { } else { log.info(FAILED_TO_FETCH_DATA, e); } - TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR, - FAILED_TO_FETCH_DATA); - sendWsMsg(sessionRef, update); + sendError(sessionRef, cmd.getCmdId(), SubscriptionErrorCode.INTERNAL_ERROR, FAILED_TO_FETCH_DATA); } }; } @@ -808,95 +744,77 @@ public class DefaultWebSocketService implements WebSocketService { } private boolean validateSubscriptionCmd(WebSocketSessionRef sessionRef, EntityDataCmd cmd) { - if (cmd.getCmdId() < 0) { - TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, - "Cmd id is negative value!"); - sendWsMsg(sessionRef, update); - return false; - } else if (cmd.getQuery() == null && !cmd.hasAnyCmd()) { - TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, - "Query is empty!"); - sendWsMsg(sessionRef, update); - return false; - } - return true; + return validateCmd(sessionRef, cmd, () -> { + if (cmd.getQuery() == null && !cmd.hasAnyCmd()) { + throw new IllegalArgumentException("Query is empty!"); + } + }); } private boolean validateSubscriptionCmd(WebSocketSessionRef sessionRef, EntityCountCmd cmd) { - if (cmd.getCmdId() < 0) { - TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, - "Cmd id is negative value!"); - sendWsMsg(sessionRef, update); - return false; - } else if (cmd.getQuery() == null) { - TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, "Query is empty!"); - sendWsMsg(sessionRef, update); - return false; - } - return true; + return validateCmd(sessionRef, cmd, () -> { + if (cmd.getQuery() == null) { + throw new IllegalArgumentException("Query is empty!"); + } + }); } private boolean validateSubscriptionCmd(WebSocketSessionRef sessionRef, AlarmDataCmd cmd) { - if (cmd.getCmdId() < 0) { - TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, - "Cmd id is negative value!"); - sendWsMsg(sessionRef, update); - return false; - } else if (cmd.getQuery() == null) { - TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, - "Query is empty!"); - sendWsMsg(sessionRef, update); - return false; - } - return true; + return validateCmd(sessionRef, cmd, () -> { + if (cmd.getQuery() == null) { + throw new IllegalArgumentException("Query is empty!"); + } + }); } private boolean validateSubscriptionCmd(WebSocketSessionRef sessionRef, SubscriptionCmd cmd) { - if (cmd.getEntityId() == null || cmd.getEntityId().isEmpty()) { - TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, - "Device id is empty!"); - sendWsMsg(sessionRef, update); - return false; - } - return true; - } - - private boolean validateSessionMetadata(WebSocketSessionRef sessionRef, SubscriptionCmd cmd, String sessionId) { - return validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId); + return validateCmd(sessionRef, cmd, () -> { + if (cmd.getEntityId() == null || cmd.getEntityId().isEmpty()) { + throw new IllegalArgumentException("Device id is empty!"); + } + }); } private boolean validateSessionMetadata(WebSocketSessionRef sessionRef, int cmdId, String sessionId) { WsSessionMetaData sessionMD = wsSessionsMap.get(sessionId); if (sessionMD == null) { log.warn("[{}] Session meta data not found. ", sessionId); - TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmdId, SubscriptionErrorCode.INTERNAL_ERROR, - SESSION_META_DATA_NOT_FOUND); - sendWsMsg(sessionRef, update); + sendError(sessionRef, cmdId, SubscriptionErrorCode.INTERNAL_ERROR, SESSION_META_DATA_NOT_FOUND); return false; } else { return true; } } - private boolean validateSubscriptionCmd(WebSocketSessionRef sessionRef, AlarmCountCmd cmd) { + private boolean validateCmd(WebSocketSessionRef sessionRef, WsCmd cmd) { + return validateCmd(sessionRef, cmd, null); + } + + private boolean validateCmd(WebSocketSessionRef sessionRef, C cmd, Runnable validator) { if (cmd.getCmdId() < 0) { - TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, - "Cmd id is negative value!"); - sendWsMsg(sessionRef, update); + sendError(sessionRef, cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, "Cmd id is negative value!"); + return false; + } + try { + if (validator != null) { + validator.run(); + } + } catch (Exception e) { + sendError(sessionRef, cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, e.getMessage()); return false; } return true; } - private void sendWsMsg(WebSocketSessionRef sessionRef, EntityDataUpdate update) { - sendWsMsg(sessionRef, update.getCmdId(), update); + private void sendUpdate(WebSocketSessionRef sessionRef, EntityDataUpdate update) { + sendUpdate(sessionRef, update.getCmdId(), update); } - private void sendWsMsg(WebSocketSessionRef sessionRef, TelemetrySubscriptionUpdate update) { - sendWsMsg(sessionRef, update.getSubscriptionId(), update); + private void sendUpdate(WebSocketSessionRef sessionRef, TelemetrySubscriptionUpdate update) { + sendUpdate(sessionRef, update.getSubscriptionId(), update); } - private void sendWsMsg(WebSocketSessionRef sessionRef, int cmdId, Object update) { + private void sendUpdate(WebSocketSessionRef sessionRef, int cmdId, Object update) { try { String msg = JacksonUtil.OBJECT_MAPPER.writeValueAsString(update); executor.submit(() -> { @@ -1045,47 +963,19 @@ public class DefaultWebSocketService implements WebSocketService { .map(TenantProfile::getDefaultProfileConfiguration).orElse(null); } - - public static WsCmdHandler newCmdHandler(java.util.function.Function cmdExtractor, - BiConsumer handler) { - return new WsCmdHandler<>(cmdExtractor, handler); - } - - public static WsCmdListHandler newCmdsHandler(java.util.function.Function> cmdsExtractor, - BiConsumer handler) { - return new WsCmdListHandler<>(cmdsExtractor, handler); + public static WsCmdHandler newCmdHandler(BiConsumer handler) { + return new WsCmdHandler<>(handler); } @RequiredArgsConstructor - public static class WsCmdHandler { - private final java.util.function.Function cmdExtractor; - private final BiConsumer handler; - - public C extractCmd(W cmdsWrapper) { - return cmdExtractor.apply(cmdsWrapper); - } + @Getter + @SuppressWarnings("unchecked") + public static class WsCmdHandler { + protected final BiConsumer handler; - @SuppressWarnings("unchecked") - public void handle(WebSocketSessionRef sessionRef, Object cmd) { + public void handle(WebSocketSessionRef sessionRef, WsCmd cmd) { handler.accept(sessionRef, (C) cmd); } } - @RequiredArgsConstructor - public static class WsCmdListHandler { - private final java.util.function.Function> cmdsExtractor; - private final BiConsumer handler; - - public List extractCmds(W cmdsWrapper) { - return cmdsExtractor.apply(cmdsWrapper); - } - - @SuppressWarnings("unchecked") - public void handle(WebSocketSessionRef sessionRef, List cmds) { - cmds.forEach(cmd -> { - handler.accept(sessionRef, (C) cmd); - }); - } - } - } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/WebSocketService.java b/application/src/main/java/org/thingsboard/server/service/ws/WebSocketService.java index 1ebd6101f0..19753b91ca 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/WebSocketService.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/WebSocketService.java @@ -16,6 +16,7 @@ package org.thingsboard.server.service.ws; import org.springframework.web.socket.CloseStatus; +import org.thingsboard.server.service.subscription.SubscriptionErrorCode; import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdate; import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; @@ -24,13 +25,15 @@ import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpda */ public interface WebSocketService { - void handleWebSocketSessionEvent(WebSocketSessionRef sessionRef, SessionEvent sessionEvent); + void handleSessionEvent(WebSocketSessionRef sessionRef, SessionEvent sessionEvent); - void handleWebSocketMsg(WebSocketSessionRef sessionRef, String msg); + void handleCommands(WebSocketSessionRef sessionRef, WsCommandsWrapper commandsWrapper); - void sendWsMsg(String sessionId, TelemetrySubscriptionUpdate update); + void sendUpdate(String sessionId, TelemetrySubscriptionUpdate update); - void sendWsMsg(String sessionId, CmdUpdate update); + void sendUpdate(String sessionId, CmdUpdate update); + + void sendError(WebSocketSessionRef sessionRef, int subId, SubscriptionErrorCode errorCode, String errorMsg); void close(String sessionId, CloseStatus status); } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionRef.java b/application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionRef.java index e799c8faa0..ba9d27b44c 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionRef.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionRef.java @@ -16,8 +16,7 @@ package org.thingsboard.server.service.ws; import lombok.Builder; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.Data; import org.thingsboard.server.service.security.model.SecurityUser; import java.net.InetSocketAddress; @@ -27,15 +26,14 @@ import java.util.concurrent.atomic.AtomicInteger; /** * Created by ashvayka on 27.03.18. */ -@RequiredArgsConstructor @Builder -@Getter +@Data public class WebSocketSessionRef { private static final long serialVersionUID = 1L; private final String sessionId; - private final SecurityUser securityCtx; + private SecurityUser securityCtx; private final InetSocketAddress localAddress; private final InetSocketAddress remoteAddress; private final WebSocketSessionType sessionType; @@ -56,11 +54,13 @@ public class WebSocketSessionRef { @Override public String toString() { - return "WebSocketSessionRef{" + - "sessionId='" + sessionId + '\'' + - ", localAddress=" + localAddress + - ", remoteAddress=" + remoteAddress + - ", sessionType=" + sessionType + - '}'; + String info = ""; + if (securityCtx != null) { + info += "[" + securityCtx.getTenantId() + "]"; + info += "[" + securityCtx.getId() + "]"; + } + info += "[" + sessionId + "]"; + return info; } + } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionType.java b/application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionType.java index 5c1a155c4b..af2206686f 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionType.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/WebSocketSessionType.java @@ -15,23 +15,25 @@ */ package org.thingsboard.server.service.ws; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.StringUtils; import java.util.Arrays; import java.util.Optional; -@RequiredArgsConstructor -@Getter +@NoArgsConstructor +@AllArgsConstructor public enum WebSocketSessionType { - TELEMETRY("telemetry"), - NOTIFICATIONS("notifications"); + GENERAL(), + TELEMETRY("telemetry"), // deprecated + NOTIFICATIONS("notifications"); // deprecated - private final String name; + private String name; public static Optional forName(String name) { return Arrays.stream(values()) - .filter(sessionType -> sessionType.getName().equals(name)) + .filter(sessionType -> StringUtils.equals(sessionType.name, name)) .findFirst(); } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/WsCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/WsCmd.java new file mode 100644 index 0000000000..0f4678cf76 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ws/WsCmd.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ws; + +import com.fasterxml.jackson.annotation.JsonIgnore; + + +public interface WsCmd { + + int getCmdId(); + + @JsonIgnore + WsCmdType getType(); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/ws/WsCmdType.java b/application/src/main/java/org/thingsboard/server/service/ws/WsCmdType.java new file mode 100644 index 0000000000..285dd88efe --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ws/WsCmdType.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ws; + +public enum WsCmdType { + AUTH, + + ATTRIBUTES, + TIMESERIES, + TIMESERIES_HISTORY, + ENTITY_DATA, + ENTITY_COUNT, + ALARM_DATA, + ALARM_COUNT, + + NOTIFICATIONS, + NOTIFICATIONS_COUNT, + MARK_NOTIFICATIONS_AS_READ, + MARK_ALL_NOTIFICATIONS_AS_READ, + + ALARM_DATA_UNSUBSCRIBE, + ALARM_COUNT_UNSUBSCRIBE, + ENTITY_DATA_UNSUBSCRIBE, + ENTITY_COUNT_UNSUBSCRIBE, + NOTIFICATIONS_UNSUBSCRIBE +} diff --git a/application/src/main/java/org/thingsboard/server/service/ws/WsCommandsWrapper.java b/application/src/main/java/org/thingsboard/server/service/ws/WsCommandsWrapper.java new file mode 100644 index 0000000000..5704f3b2dc --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ws/WsCommandsWrapper.java @@ -0,0 +1,71 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ws; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.service.ws.notification.cmd.MarkAllNotificationsAsReadCmd; +import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationsAsReadCmd; +import org.thingsboard.server.service.ws.notification.cmd.NotificationsCountSubCmd; +import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd; +import org.thingsboard.server.service.ws.notification.cmd.NotificationsUnsubCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v1.AttributesSubscriptionCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v1.GetHistoryCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v1.TimeseriesSubscriptionCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmCountCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmCountUnsubscribeCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataUnsubscribeCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountUnsubscribeCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd; +import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUnsubscribeCmd; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class WsCommandsWrapper { + + private AuthCmd authCmd; + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") + @JsonSubTypes({ + @Type(name = "ATTRIBUTES", value = AttributesSubscriptionCmd.class), + @Type(name = "TIMESERIES", value = TimeseriesSubscriptionCmd.class), + @Type(name = "TIMESERIES_HISTORY", value = GetHistoryCmd.class), + @Type(name = "ENTITY_DATA", value = EntityDataCmd.class), + @Type(name = "ENTITY_COUNT", value = EntityCountCmd.class), + @Type(name = "ALARM_DATA", value = AlarmDataCmd.class), + @Type(name = "ALARM_COUNT", value = AlarmCountCmd.class), + @Type(name = "NOTIFICATIONS", value = NotificationsSubCmd.class), + @Type(name = "NOTIFICATIONS_COUNT", value = NotificationsCountSubCmd.class), + @Type(name = "MARK_NOTIFICATIONS_AS_READ", value = MarkNotificationsAsReadCmd.class), + @Type(name = "MARK_ALL_NOTIFICATIONS_AS_READ", value = MarkAllNotificationsAsReadCmd.class), + @Type(name = "ALARM_DATA_UNSUBSCRIBE", value = AlarmDataUnsubscribeCmd.class), + @Type(name = "ALARM_COUNT_UNSUBSCRIBE", value = AlarmCountUnsubscribeCmd.class), + @Type(name = "ENTITY_DATA_UNSUBSCRIBE", value = EntityDataUnsubscribeCmd.class), + @Type(name = "ENTITY_COUNT_UNSUBSCRIBE", value = EntityCountUnsubscribeCmd.class), + @Type(name = "NOTIFICATIONS_UNSUBSCRIBE", value = NotificationsUnsubCmd.class), + }) + private List cmds; + +} diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java index 012808b253..5a02814870 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java @@ -32,6 +32,7 @@ import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.subscription.TbLocalSubscriptionService; +import org.thingsboard.server.service.subscription.TbSubscription; import org.thingsboard.server.service.ws.WebSocketService; import org.thingsboard.server.service.ws.WebSocketSessionRef; import org.thingsboard.server.service.ws.notification.cmd.MarkAllNotificationsAsReadCmd; @@ -119,7 +120,8 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH /* Notifications subscription update handling */ - private void handleNotificationsSubscriptionUpdate(NotificationsSubscription subscription, NotificationsSubscriptionUpdate subscriptionUpdate) { + private void handleNotificationsSubscriptionUpdate(TbSubscription sub, NotificationsSubscriptionUpdate subscriptionUpdate) { + NotificationsSubscription subscription = (NotificationsSubscription) sub; try { if (subscriptionUpdate.getNotificationUpdate() != null) { handleNotificationUpdate(subscription, subscriptionUpdate.getNotificationUpdate()); @@ -178,7 +180,8 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH /* Notifications count subscription update handling */ - private void handleNotificationsCountSubscriptionUpdate(NotificationsCountSubscription subscription, NotificationsSubscriptionUpdate subscriptionUpdate) { + private void handleNotificationsCountSubscriptionUpdate(TbSubscription sub, NotificationsSubscriptionUpdate subscriptionUpdate) { + NotificationsCountSubscription subscription = (NotificationsCountSubscription) sub; try { if (subscriptionUpdate.getNotificationUpdate() != null) { handleNotificationUpdate(subscription, subscriptionUpdate.getNotificationUpdate()); @@ -242,7 +245,7 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH private void sendUpdate(String sessionId, CmdUpdate update) { log.trace("[{}, cmdId: {}] Sending WS update: {}", sessionId, update.getCmdId(), update); - wsService.sendWsMsg(sessionId, update); + wsService.sendUpdate(sessionId, update); } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkAllNotificationsAsReadCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkAllNotificationsAsReadCmd.java index f096106135..d11992ba4f 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkAllNotificationsAsReadCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkAllNotificationsAsReadCmd.java @@ -18,10 +18,17 @@ package org.thingsboard.server.service.ws.notification.cmd; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import org.thingsboard.server.service.ws.WsCmd; +import org.thingsboard.server.service.ws.WsCmdType; @Data @NoArgsConstructor @AllArgsConstructor public class MarkAllNotificationsAsReadCmd implements WsCmd { private int cmdId; + + @Override + public WsCmdType getType() { + return WsCmdType.MARK_ALL_NOTIFICATIONS_AS_READ; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkNotificationsAsReadCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkNotificationsAsReadCmd.java index 55de75387f..2452163fd0 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkNotificationsAsReadCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/MarkNotificationsAsReadCmd.java @@ -18,6 +18,8 @@ package org.thingsboard.server.service.ws.notification.cmd; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import org.thingsboard.server.service.ws.WsCmd; +import org.thingsboard.server.service.ws.WsCmdType; import java.util.List; import java.util.UUID; @@ -28,4 +30,9 @@ import java.util.UUID; public class MarkNotificationsAsReadCmd implements WsCmd { private int cmdId; private List notifications; + + @Override + public WsCmdType getType() { + return WsCmdType.MARK_NOTIFICATIONS_AS_READ; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationCmdsWrapper.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationCmdsWrapper.java index 88613eb966..8ba52a9b3c 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationCmdsWrapper.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationCmdsWrapper.java @@ -15,9 +15,19 @@ */ package org.thingsboard.server.service.ws.notification.cmd; +import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; +import org.thingsboard.server.service.ws.WsCommandsWrapper; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @deprecated Use {@link WsCommandsWrapper}. This class is left for backward compatibility + * */ @Data +@Deprecated public class NotificationCmdsWrapper { private NotificationsCountSubCmd unreadCountSubCmd; @@ -30,4 +40,13 @@ public class NotificationCmdsWrapper { private NotificationsUnsubCmd unsubCmd; + @JsonIgnore + public WsCommandsWrapper toCommonCmdsWrapper() { + return new WsCommandsWrapper(null, Stream.of( + unreadCountSubCmd, unreadSubCmd, markAsReadCmd, markAllAsReadCmd, unsubCmd + ) + .filter(Objects::nonNull) + .collect(Collectors.toList())); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsCountSubCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsCountSubCmd.java index 0d6757a6e7..7fa0ac9a6a 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsCountSubCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsCountSubCmd.java @@ -18,10 +18,17 @@ package org.thingsboard.server.service.ws.notification.cmd; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import org.thingsboard.server.service.ws.WsCmd; +import org.thingsboard.server.service.ws.WsCmdType; @Data @NoArgsConstructor @AllArgsConstructor public class NotificationsCountSubCmd implements WsCmd { private int cmdId; + + @Override + public WsCmdType getType() { + return WsCmdType.NOTIFICATIONS_COUNT; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsSubCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsSubCmd.java index e022d2c49f..44d6c47ff6 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsSubCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsSubCmd.java @@ -18,6 +18,8 @@ package org.thingsboard.server.service.ws.notification.cmd; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import org.thingsboard.server.service.ws.WsCmd; +import org.thingsboard.server.service.ws.WsCmdType; @Data @NoArgsConstructor @@ -25,4 +27,9 @@ import lombok.NoArgsConstructor; public class NotificationsSubCmd implements WsCmd { private int cmdId; private int limit; + + @Override + public WsCmdType getType() { + return WsCmdType.NOTIFICATIONS; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsUnsubCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsUnsubCmd.java index c9f0897eeb..a63351d9b9 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsUnsubCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/NotificationsUnsubCmd.java @@ -18,6 +18,8 @@ package org.thingsboard.server.service.ws.notification.cmd; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import org.thingsboard.server.service.ws.WsCmd; +import org.thingsboard.server.service.ws.WsCmdType; import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd; @Data @@ -25,4 +27,9 @@ import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd; @AllArgsConstructor public class NotificationsUnsubCmd implements UnsubscribeCmd, WsCmd { private int cmdId; + + @Override + public WsCmdType getType() { + return WsCmdType.NOTIFICATIONS_UNSUBSCRIBE; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsCountUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsCountUpdate.java index 93f51a965b..42dc670a21 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsCountUpdate.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsCountUpdate.java @@ -28,14 +28,17 @@ import org.thingsboard.server.service.ws.telemetry.cmd.v2.CmdUpdateType; public class UnreadNotificationsCountUpdate extends CmdUpdate { private final int totalUnreadCount; + private final int sequenceNumber; @Builder @JsonCreator public UnreadNotificationsCountUpdate(@JsonProperty("cmdId") int cmdId, @JsonProperty("errorCode") int errorCode, @JsonProperty("errorMsg") String errorMsg, - @JsonProperty("totalUnreadCount") int totalUnreadCount) { + @JsonProperty("totalUnreadCount") int totalUnreadCount, + @JsonProperty("sequenceNumber") int sequenceNumber) { super(cmdId, errorCode, errorMsg); this.totalUnreadCount = totalUnreadCount; + this.sequenceNumber = sequenceNumber; } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsUpdate.java index e64624d16e..65492f9579 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsUpdate.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/UnreadNotificationsUpdate.java @@ -33,6 +33,7 @@ public class UnreadNotificationsUpdate extends CmdUpdate { private final Collection notifications; private final Notification update; private final int totalUnreadCount; + private final int sequenceNumber; @Builder @JsonCreator @@ -40,11 +41,13 @@ public class UnreadNotificationsUpdate extends CmdUpdate { @JsonProperty("errorMsg") String errorMsg, @JsonProperty("notifications") Collection notifications, @JsonProperty("update") Notification update, - @JsonProperty("totalUnreadCount") int totalUnreadCount) { + @JsonProperty("totalUnreadCount") int totalUnreadCount, + @JsonProperty("sequenceNumber") int sequenceNumber) { super(cmdId, errorCode, errorMsg); this.notifications = notifications; this.update = update; this.totalUnreadCount = totalUnreadCount; + this.sequenceNumber = sequenceNumber; } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/WsCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/WsCmd.java deleted file mode 100644 index 97bfeab70c..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/cmd/WsCmd.java +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.ws.notification.cmd; - -public interface WsCmd { - int getCmdId(); -} diff --git a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsCountSubscription.java b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsCountSubscription.java index cb5d140d21..94939682e9 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsCountSubscription.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/notification/sub/NotificationsCountSubscription.java @@ -33,7 +33,7 @@ public class NotificationsCountSubscription extends TbSubscription updateProcessor) { + BiConsumer, NotificationsSubscriptionUpdate> updateProcessor) { super(serviceId, sessionId, subscriptionId, tenantId, entityId, TbSubscriptionType.NOTIFICATIONS_COUNT, updateProcessor); } @@ -41,6 +41,7 @@ public class NotificationsCountSubscription extends TbSubscription updateProcessor, + BiConsumer, NotificationsSubscriptionUpdate> updateProcessor, int limit) { super(serviceId, sessionId, subscriptionId, tenantId, entityId, TbSubscriptionType.NOTIFICATIONS, updateProcessor); this.limit = limit; @@ -54,6 +54,7 @@ public class NotificationsSubscription extends TbSubscription attrSubCmds; + + private List tsSubCmds; + + private List historyCmds; + + private List entityDataCmds; + + private List entityDataUnsubscribeCmds; + + private List alarmDataCmds; + + private List alarmDataUnsubscribeCmds; + + private List entityCountCmds; + + private List entityCountUnsubscribeCmds; + + private List alarmCountCmds; + + private List alarmCountUnsubscribeCmds; + + @JsonIgnore + public WsCommandsWrapper toCommonCmdsWrapper() { + return new WsCommandsWrapper(null, Stream.of( + attrSubCmds, tsSubCmds, historyCmds, entityDataCmds, + entityDataUnsubscribeCmds, alarmDataCmds, alarmDataUnsubscribeCmds, + entityCountCmds, entityCountUnsubscribeCmds, + alarmCountCmds, alarmCountUnsubscribeCmds + ) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .collect(Collectors.toList())); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/TelemetryPluginCmdsWrapper.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/TelemetryPluginCmdsWrapper.java deleted file mode 100644 index 5b2c12afcd..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/TelemetryPluginCmdsWrapper.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.ws.telemetry.cmd; - -import lombok.Data; -import org.thingsboard.server.service.ws.telemetry.cmd.v1.AttributesSubscriptionCmd; -import org.thingsboard.server.service.ws.telemetry.cmd.v1.GetHistoryCmd; -import org.thingsboard.server.service.ws.telemetry.cmd.v1.TimeseriesSubscriptionCmd; -import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmCountCmd; -import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmCountUnsubscribeCmd; -import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataCmd; -import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmDataUnsubscribeCmd; -import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd; -import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountUnsubscribeCmd; -import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd; -import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUnsubscribeCmd; - -import java.util.List; - -/** - * @author Andrew Shvayka - */ -@Data -public class TelemetryPluginCmdsWrapper { - - private List attrSubCmds; - - private List tsSubCmds; - - private List historyCmds; - - private List entityDataCmds; - - private List entityDataUnsubscribeCmds; - - private List alarmDataCmds; - - private List alarmDataUnsubscribeCmds; - - private List entityCountCmds; - - private List entityCountUnsubscribeCmds; - - private List alarmCountCmds; - - private List alarmCountUnsubscribeCmds; - -} diff --git a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/AttributesSubscriptionCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/AttributesSubscriptionCmd.java index 1ccec261b0..d1153b8bdd 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/AttributesSubscriptionCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/AttributesSubscriptionCmd.java @@ -16,7 +16,7 @@ package org.thingsboard.server.service.ws.telemetry.cmd.v1; import lombok.NoArgsConstructor; -import org.thingsboard.server.service.ws.telemetry.TelemetryFeature; +import org.thingsboard.server.service.ws.WsCmdType; /** * @author Andrew Shvayka @@ -25,8 +25,8 @@ import org.thingsboard.server.service.ws.telemetry.TelemetryFeature; public class AttributesSubscriptionCmd extends SubscriptionCmd { @Override - public TelemetryFeature getType() { - return TelemetryFeature.ATTRIBUTES; + public WsCmdType getType() { + return WsCmdType.ATTRIBUTES; } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/GetHistoryCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/GetHistoryCmd.java index 6791066641..0da067f6da 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/GetHistoryCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/GetHistoryCmd.java @@ -18,6 +18,7 @@ package org.thingsboard.server.service.ws.telemetry.cmd.v1; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import org.thingsboard.server.service.ws.WsCmdType; /** * @author Andrew Shvayka @@ -37,4 +38,8 @@ public class GetHistoryCmd implements TelemetryPluginCmd { private int limit; private String agg; + @Override + public WsCmdType getType() { + return WsCmdType.TIMESERIES_HISTORY; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/SubscriptionCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/SubscriptionCmd.java index 6a5f9d820d..3b742c3a2f 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/SubscriptionCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/SubscriptionCmd.java @@ -32,8 +32,6 @@ public abstract class SubscriptionCmd implements TelemetryPluginCmd { private String scope; private boolean unsubscribe; - public abstract TelemetryFeature getType(); - @Override public String toString() { return "SubscriptionCmd [entityType=" + entityType + ", entityId=" + entityId + ", tags=" + keys + ", unsubscribe=" + unsubscribe + "]"; diff --git a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/TelemetryPluginCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/TelemetryPluginCmd.java index dc9d3a48c2..b57b907184 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/TelemetryPluginCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/TelemetryPluginCmd.java @@ -15,10 +15,12 @@ */ package org.thingsboard.server.service.ws.telemetry.cmd.v1; +import org.thingsboard.server.service.ws.WsCmd; + /** * @author Andrew Shvayka */ -public interface TelemetryPluginCmd { +public interface TelemetryPluginCmd extends WsCmd { int getCmdId(); diff --git a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/TimeseriesSubscriptionCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/TimeseriesSubscriptionCmd.java index f0a7b9d3af..aea228eebc 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/TimeseriesSubscriptionCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/TimeseriesSubscriptionCmd.java @@ -17,8 +17,9 @@ package org.thingsboard.server.service.ws.telemetry.cmd.v1; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import org.thingsboard.server.service.ws.telemetry.TelemetryFeature; +import org.thingsboard.server.service.ws.WsCmdType; /** * @author Andrew Shvayka @@ -26,6 +27,7 @@ import org.thingsboard.server.service.ws.telemetry.TelemetryFeature; @NoArgsConstructor @AllArgsConstructor @Data +@EqualsAndHashCode(callSuper = true) public class TimeseriesSubscriptionCmd extends SubscriptionCmd { private long startTs; @@ -35,7 +37,7 @@ public class TimeseriesSubscriptionCmd extends SubscriptionCmd { private String agg; @Override - public TelemetryFeature getType() { - return TelemetryFeature.TIMESERIES; + public WsCmdType getType() { + return WsCmdType.TIMESERIES; } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmCountCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmCountCmd.java index 80fd998eb1..b1fa9cf785 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmCountCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmCountCmd.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import org.thingsboard.server.common.data.query.AlarmCountQuery; +import org.thingsboard.server.service.ws.WsCmdType; public class AlarmCountCmd extends DataCmd { @@ -31,4 +32,9 @@ public class AlarmCountCmd extends DataCmd { super(cmdId); this.query = query; } + + @Override + public WsCmdType getType() { + return WsCmdType.ALARM_COUNT; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmCountUnsubscribeCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmCountUnsubscribeCmd.java index 79c860f3f6..02cf5d5572 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmCountUnsubscribeCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmCountUnsubscribeCmd.java @@ -16,10 +16,15 @@ package org.thingsboard.server.service.ws.telemetry.cmd.v2; import lombok.Data; +import org.thingsboard.server.service.ws.WsCmdType; @Data public class AlarmCountUnsubscribeCmd implements UnsubscribeCmd { private final int cmdId; + @Override + public WsCmdType getType() { + return WsCmdType.ALARM_COUNT_UNSUBSCRIBE; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataCmd.java index 0f83d00d6d..0efa6ae608 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataCmd.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import org.thingsboard.server.common.data.query.AlarmDataQuery; +import org.thingsboard.server.service.ws.WsCmdType; public class AlarmDataCmd extends DataCmd { @@ -30,4 +31,9 @@ public class AlarmDataCmd extends DataCmd { super(cmdId); this.query = query; } + + @Override + public WsCmdType getType() { + return WsCmdType.ALARM_DATA; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataUnsubscribeCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataUnsubscribeCmd.java index 97db558cf9..7dfb1159c5 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataUnsubscribeCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/AlarmDataUnsubscribeCmd.java @@ -16,10 +16,15 @@ package org.thingsboard.server.service.ws.telemetry.cmd.v2; import lombok.Data; +import org.thingsboard.server.service.ws.WsCmdType; @Data public class AlarmDataUnsubscribeCmd implements UnsubscribeCmd { private final int cmdId; + @Override + public WsCmdType getType() { + return WsCmdType.ALARM_DATA_UNSUBSCRIBE; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/DataCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/DataCmd.java index c09cbc03d1..74133230cb 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/DataCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/DataCmd.java @@ -17,9 +17,10 @@ package org.thingsboard.server.service.ws.telemetry.cmd.v2; import lombok.Data; import lombok.Getter; +import org.thingsboard.server.service.ws.WsCmd; @Data -public class DataCmd { +public abstract class DataCmd implements WsCmd { @Getter private final int cmdId; diff --git a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountCmd.java index c39f81f1b7..3431afaf1a 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountCmd.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import org.thingsboard.server.common.data.query.EntityCountQuery; +import org.thingsboard.server.service.ws.WsCmdType; public class EntityCountCmd extends DataCmd { @@ -31,4 +32,9 @@ public class EntityCountCmd extends DataCmd { super(cmdId); this.query = query; } + + @Override + public WsCmdType getType() { + return WsCmdType.ENTITY_COUNT; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountUnsubscribeCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountUnsubscribeCmd.java index b82fecd8fd..4e0a50c7e1 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountUnsubscribeCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityCountUnsubscribeCmd.java @@ -16,10 +16,15 @@ package org.thingsboard.server.service.ws.telemetry.cmd.v2; import lombok.Data; +import org.thingsboard.server.service.ws.WsCmdType; @Data public class EntityCountUnsubscribeCmd implements UnsubscribeCmd { private final int cmdId; + @Override + public WsCmdType getType() { + return WsCmdType.ENTITY_COUNT_UNSUBSCRIBE; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataCmd.java index 5fe9179651..ace96f6673 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataCmd.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import org.thingsboard.server.common.data.query.EntityDataQuery; +import org.thingsboard.server.service.ws.WsCmdType; public class EntityDataCmd extends DataCmd { @@ -62,4 +63,8 @@ public class EntityDataCmd extends DataCmd { return historyCmd != null || latestCmd != null || tsCmd != null || aggHistoryCmd != null || aggTsCmd != null; } + @Override + public WsCmdType getType() { + return WsCmdType.ENTITY_DATA; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataUnsubscribeCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataUnsubscribeCmd.java index 80577edda2..56727faf54 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataUnsubscribeCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/EntityDataUnsubscribeCmd.java @@ -16,10 +16,15 @@ package org.thingsboard.server.service.ws.telemetry.cmd.v2; import lombok.Data; +import org.thingsboard.server.service.ws.WsCmdType; @Data public class EntityDataUnsubscribeCmd implements UnsubscribeCmd { private final int cmdId; + @Override + public WsCmdType getType() { + return WsCmdType.ENTITY_DATA_UNSUBSCRIBE; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/UnsubscribeCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/UnsubscribeCmd.java index 81288c76da..4efdb279be 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/UnsubscribeCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v2/UnsubscribeCmd.java @@ -15,7 +15,9 @@ */ package org.thingsboard.server.service.ws.telemetry.cmd.v2; -public interface UnsubscribeCmd { +import org.thingsboard.server.service.ws.WsCmd; + +public interface UnsubscribeCmd extends WsCmd { int getCmdId(); diff --git a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/AlarmSubscriptionUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/AlarmSubscriptionUpdate.java index 90041ffa13..66d18b24b2 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/AlarmSubscriptionUpdate.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/AlarmSubscriptionUpdate.java @@ -16,13 +16,13 @@ package org.thingsboard.server.service.ws.telemetry.sub; import lombok.Getter; +import lombok.ToString; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.service.subscription.SubscriptionErrorCode; +@ToString public class AlarmSubscriptionUpdate { - @Getter - private int subscriptionId; @Getter private int errorCode; @Getter @@ -32,31 +32,24 @@ public class AlarmSubscriptionUpdate { @Getter private boolean alarmDeleted; - public AlarmSubscriptionUpdate(int subscriptionId, AlarmInfo alarm) { - this(subscriptionId, alarm, false); + public AlarmSubscriptionUpdate(AlarmInfo alarm) { + this(alarm, false); } - public AlarmSubscriptionUpdate(int subscriptionId, AlarmInfo alarm, boolean alarmDeleted) { + public AlarmSubscriptionUpdate(AlarmInfo alarm, boolean alarmDeleted) { super(); - this.subscriptionId = subscriptionId; this.alarm = alarm; this.alarmDeleted = alarmDeleted; } - public AlarmSubscriptionUpdate(int subscriptionId, SubscriptionErrorCode errorCode) { - this(subscriptionId, errorCode, null); + public AlarmSubscriptionUpdate(SubscriptionErrorCode errorCode) { + this(errorCode, null); } - public AlarmSubscriptionUpdate(int subscriptionId, SubscriptionErrorCode errorCode, String errorMsg) { + public AlarmSubscriptionUpdate(SubscriptionErrorCode errorCode, String errorMsg) { super(); - this.subscriptionId = subscriptionId; this.errorCode = errorCode.getCode(); this.errorMsg = errorMsg != null ? errorMsg : errorCode.getDefaultMsg(); } - @Override - public String toString() { - return "AlarmUpdate [subscriptionId=" + subscriptionId + ", errorCode=" + errorCode + ", errorMsg=" + errorMsg + - ", alarm=" + alarm + "]"; - } } \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/TelemetrySubscriptionUpdate.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/TelemetrySubscriptionUpdate.java index 108c5cf1d4..cea1731a31 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/TelemetrySubscriptionUpdate.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/sub/TelemetrySubscriptionUpdate.java @@ -19,6 +19,7 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.service.subscription.SubscriptionErrorCode; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -27,7 +28,7 @@ import java.util.stream.Collectors; public class TelemetrySubscriptionUpdate { - private int subscriptionId; + private final int subscriptionId; private int errorCode; private String errorMsg; private Map> data; @@ -94,7 +95,14 @@ public class TelemetrySubscriptionUpdate { @Override public String toString() { - return "TsSubscriptionUpdate [subscriptionId=" + subscriptionId + ", errorCode=" + errorCode + ", errorMsg=" + errorMsg + ", data=" - + data + "]"; + StringBuilder result = new StringBuilder("TelemetrySubscriptionUpdate [subscriptionId=" + subscriptionId + ", errorCode=" + errorCode + ", errorMsg=" + errorMsg + ", data="); + data.forEach((k, v) -> { + result.append(k).append("=["); + for(Object a : v){ + result.append(Arrays.toString((Object[])a)).append("|"); + } + result.append("]"); + }); + return result.toString(); } } diff --git a/application/src/main/java/org/thingsboard/server/utils/LwM2mObjectModelUtils.java b/application/src/main/java/org/thingsboard/server/utils/LwM2mObjectModelUtils.java index 4a07444bbb..b73ba09541 100644 --- a/application/src/main/java/org/thingsboard/server/utils/LwM2mObjectModelUtils.java +++ b/application/src/main/java/org/thingsboard/server/utils/LwM2mObjectModelUtils.java @@ -16,8 +16,6 @@ package org.thingsboard.server.utils; import lombok.extern.slf4j.Slf4j; -import org.eclipse.leshan.core.model.DDFFileParser; -import org.eclipse.leshan.core.model.DefaultDDFFileValidator; import org.eclipse.leshan.core.model.InvalidDDFFileException; import org.eclipse.leshan.core.model.ObjectModel; import org.thingsboard.server.common.data.ResourceType; @@ -41,13 +39,13 @@ import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPA @Slf4j public class LwM2mObjectModelUtils { - + private static final TbDDFFileParser ddfFileParser = new TbDDFFileParser(); - - public static void toLwm2mResource (TbResource resource) throws ThingsboardException { + + public static void toLwm2mResource(TbResource resource) throws ThingsboardException { try { List objectModels = - ddfFileParser.parse(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText()); + ddfFileParser.parse(new ByteArrayInputStream(resource.getData()), resource.getSearchText()); if (!objectModels.isEmpty()) { ObjectModel objectModel = objectModels.get(0); @@ -75,7 +73,7 @@ public class LwM2mObjectModelUtils { public static LwM2mObject toLwM2mObject(TbResource resource, boolean isSave) { try { List objectModels = - ddfFileParser.parse(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText()); + ddfFileParser.parse(new ByteArrayInputStream(resource.getData()), resource.getSearchText()); if (objectModels.size() == 0) { return null; } else { diff --git a/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java b/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java new file mode 100644 index 0000000000..530679683d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/utils/TbNodeUpgradeUtils.java @@ -0,0 +1,75 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.utils; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.NodeConfiguration; +import org.thingsboard.rule.engine.api.TbNode; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.data.util.TbPair; +import org.thingsboard.server.service.component.RuleNodeClassInfo; + +@Slf4j +public class TbNodeUpgradeUtils { + + public static void upgradeConfigurationAndVersion(RuleNode node, RuleNodeClassInfo nodeInfo) { + JsonNode oldConfiguration = node.getConfiguration(); + int configurationVersion = node.getConfigurationVersion(); + + int currentVersion = nodeInfo.getCurrentVersion(); + var configClass = nodeInfo.getAnnotation().configClazz(); + + if (oldConfiguration == null || !oldConfiguration.isObject()) { + log.warn("Failed to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}. " + + "Current configuration is null or not a json object. " + + "Going to set default configuration ... ", + node.getId(), node.getType(), configurationVersion, currentVersion); + node.setConfiguration(getDefaultConfig(configClass)); + } else { + var tbVersionedNode = getTbVersionedNode(nodeInfo); + try { + TbPair upgradeResult = tbVersionedNode.upgrade(configurationVersion, oldConfiguration); + if (upgradeResult.getFirst()) { + node.setConfiguration(upgradeResult.getSecond()); + } + } catch (Exception e) { + try { + JacksonUtil.treeToValue(oldConfiguration, configClass); + } catch (Exception ex) { + log.warn("Failed to upgrade rule node with id: {} type: {} fromVersion: {} toVersion: {}. " + + "Going to set default configuration ... ", + node.getId(), node.getType(), configurationVersion, currentVersion, e); + node.setConfiguration(getDefaultConfig(configClass)); + } + } + } + node.setConfigurationVersion(currentVersion); + } + + @SneakyThrows + private static TbNode getTbVersionedNode(RuleNodeClassInfo nodeInfo) { + return (TbNode) nodeInfo.getClazz().getDeclaredConstructor().newInstance(); + } + + @SneakyThrows + private static JsonNode getDefaultConfig(Class configClass) { + return JacksonUtil.valueToTree(configClass.getDeclaredConstructor().newInstance().defaultConfiguration()); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/utils/TypeCastUtil.java b/application/src/main/java/org/thingsboard/server/utils/TypeCastUtil.java deleted file mode 100644 index 3bf1efc8f4..0000000000 --- a/application/src/main/java/org/thingsboard/server/utils/TypeCastUtil.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.utils; - -import org.apache.commons.lang3.math.NumberUtils; -import org.thingsboard.server.common.data.kv.DataType; - -import java.math.BigDecimal; -import java.util.Map; - -public class TypeCastUtil { - - private TypeCastUtil() {} - - public static Map.Entry castValue(String value) { - if (isNumber(value)) { - String formattedValue = value.replace(',', '.'); - try { - BigDecimal bd = new BigDecimal(formattedValue); - if (bd.stripTrailingZeros().scale() > 0 || isSimpleDouble(formattedValue)) { - if (bd.scale() <= 16) { - return Map.entry(DataType.DOUBLE, bd.doubleValue()); - } - } else { - return Map.entry(DataType.LONG, bd.longValueExact()); - } - } catch (RuntimeException ignored) {} - } else if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) { - return Map.entry(DataType.BOOLEAN, Boolean.parseBoolean(value)); - } - return Map.entry(DataType.STRING, value); - } - - private static boolean isNumber(String value) { - return NumberUtils.isNumber(value.replace(',', '.')); - } - - private static boolean isSimpleDouble(String valueAsString) { - return valueAsString.contains(".") && !valueAsString.contains("E") && !valueAsString.contains("e"); - } - -} diff --git a/application/src/main/resources/logback.xml b/application/src/main/resources/logback.xml index eaf6ace29e..b0a4174012 100644 --- a/application/src/main/resources/logback.xml +++ b/application/src/main/resources/logback.xml @@ -30,6 +30,8 @@ + + diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 8e9c6147f0..c650d53b8f 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -14,8 +14,9 @@ # limitations under the License. # +# Server common parameters server: - # Server bind address + # Server bind-address address: "${HTTP_BIND_ADDRESS:0.0.0.0}" # Server bind port port: "${HTTP_BIND_PORT:8080}" @@ -33,7 +34,7 @@ server: pem: # Path to the server certificate file (holds server certificate or certificate chain, may include server private key) cert_file: "${SSL_PEM_CERT:server.pem}" - # Path to the server certificate private key file. Optional by default. Required if the private key is not present in server certificate file; + # Path to the server certificate private key file (optional). Required if the private key is not present in the server certificate file key_file: "${SSL_PEM_KEY:server_key.pem}" # Server certificate private key password (optional) key_password: "${SSL_PEM_KEY_PASSWORD:server_key_password}" @@ -53,36 +54,48 @@ server: http2: # Enable/disable HTTP/2 support enabled: "${HTTP2_ENABLED:true}" + # Log errors with stacktrace when REST API throws an exception with the message "Please contact sysadmin" log_controller_error_stack_trace: "${HTTP_LOG_CONTROLLER_ERROR_STACK_TRACE:false}" ws: + # Timeout for sending data to client WebSocket session in milliseconds send_timeout: "${TB_SERVER_WS_SEND_TIMEOUT:5000}" - # recommended timeout >= 30 seconds. Platform will attempt to send 'ping' request 3 times within the timeout + # recommended timeout >= 30 seconds. The platform will attempt to send a 'ping' request 3 times within the timeout ping_timeout: "${TB_SERVER_WS_PING_TIMEOUT:30000}" dynamic_page_link: + # Refresh rate of the dynamic alarm end entity data queries refresh_interval: "${TB_SERVER_WS_DYNAMIC_PAGE_LINK_REFRESH_INTERVAL_SEC:60}" + # Thread pool size to execute dynamic queries refresh_pool_size: "${TB_SERVER_WS_DYNAMIC_PAGE_LINK_REFRESH_POOL_SIZE:1}" + # Maximum number of dynamic queries per refresh interval. For example, no more than 10 alarm queries are executed by the user simultaneously in all browsers. max_alarm_queries_per_refresh_interval: "${TB_SERVER_WS_MAX_ALARM_QUERIES_PER_REFRESH_INTERVAL:10}" + # Maximum number of dynamic queries per user. For example, no more than 10 alarm widgets opened by the user simultaneously in all browsers max_per_user: "${TB_SERVER_WS_DYNAMIC_PAGE_LINK_MAX_PER_USER:10}" + # Maximum number of entities returned for single entity subscription. For example, no more than 10,000 entities on the map widget max_entities_per_data_subscription: "${TB_SERVER_WS_MAX_ENTITIES_PER_DATA_SUBSCRIPTION:10000}" + # Maximum number of alarms returned for single alarm subscription. For example, no more than 10,000 alarms on the alarm widget max_entities_per_alarm_subscription: "${TB_SERVER_WS_MAX_ENTITIES_PER_ALARM_SUBSCRIPTION:10000}" + # Maximum queue size of the websocket updates per session. This restriction prevents infinite updates of WS max_queue_messages_per_session: "${TB_SERVER_WS_DEFAULT_QUEUE_MESSAGES_PER_SESSION:1000}" + # Maximum time between WS session opening and sending auth command + auth_timeout_ms: "${TB_SERVER_WS_AUTH_TIMEOUT_MS:10000}" rest: server_side_rpc: - # Minimum value of the server side RPC timeout. May override value provided in the REST API call. - # Since 2.5 migration to queues, the RPC delay depends on the size of the pending messages in the queue, - # so default UI parameter of 500ms may not be sufficient for loaded environments. + # Minimum value of the server-side RPC timeout. May override value provided in the REST API call. + # Since 2.5 migration to queues, the RPC delay depends on the size of the pending messages in the queue. + # So default UI parameter of 500ms may not be sufficient for loaded environments. min_timeout: "${MIN_SERVER_SIDE_RPC_TIMEOUT:5000}" - # Default value of the server side RPC timeout. + # Default value of the server-side RPC timeout. default_timeout: "${DEFAULT_SERVER_SIDE_RPC_TIMEOUT:10000}" rate_limits: + # Limit that prohibits resetting the password for the user too often. The value of the rate limit. By default, no more than 5 requests per hour reset_password_per_user: "${RESET_PASSWORD_PER_USER_RATE_LIMIT_CONFIGURATION:5:3600}" -# Application info +# Application info parameters app: # Application version version: "@project.version@" -# Zookeeper connection parameters. Used for service discovery. +# Zookeeper connection parameters zk: # Enable/disable zookeeper discovery service. enabled: "${ZOOKEEPER_ENABLED:false}" @@ -96,65 +109,82 @@ zk: session_timeout_ms: "${ZOOKEEPER_SESSION_TIMEOUT_MS:3000}" # Name of the directory in zookeeper 'filesystem' zk_dir: "${ZOOKEEPER_NODES_DIR:/thingsboard}" + # The recalculate_delay property is recommended in a microservices architecture setup for rule-engine services. + # This property provides a pause to ensure that when a rule-engine service is restarted, other nodes don't immediately attempt to recalculate their partitions. + # The delay is recommended because the initialization of rule chain actors is time-consuming. Avoiding unnecessary recalculations during a restart can enhance system performance and stability. + recalculate_delay: "${ZOOKEEPER_RECALCULATE_DELAY_MS:0}" +# Cluster parameters cluster: stats: + # Enable/Disable the cluster statistics. Calculates the number of messages sent between cluster nodes based on each type enabled: "${TB_CLUSTER_STATS_ENABLED:false}" + # Interval of printing the cluster stats to the log file print_interval_ms: "${TB_CLUSTER_STATS_PRINT_INTERVAL_MS:10000}" # Plugins configuration parameters plugins: - # Comma separated package list used during classpath scanning for plugins + # Comma-separated package list used during classpath scanning for plugins scan_packages: "${PLUGINS_SCAN_PACKAGES:org.thingsboard.server.extensions,org.thingsboard.rule.engine}" # Security parameters security: # JWT Token parameters - jwt: # Since 3.4.2 values are persisted to the database during install or upgrade. On Install, the key will be generated randomly if no custom value set. You can change it later from Web UI under SYS_ADMIN + jwt: # Since 3.4.2 values are persisted in the database during installation or upgrade. On Install, the key will be generated randomly if no custom value set. You can change it later from Web UI under SYS_ADMIN tokenExpirationTime: "${JWT_TOKEN_EXPIRATION_TIME:9000}" # Number of seconds (2.5 hours) refreshTokenExpTime: "${JWT_REFRESH_TOKEN_EXPIRATION_TIME:604800}" # Number of seconds (1 week). - tokenIssuer: "${JWT_TOKEN_ISSUER:thingsboard.io}" + tokenIssuer: "${JWT_TOKEN_ISSUER:thingsboard.io}" # User JWT Token issuer tokenSigningKey: "${JWT_TOKEN_SIGNING_KEY:thingsboardDefaultSigningKey}" # Base64 encoded # Enable/disable access to Tenant Administrators JWT token by System Administrator or Customer Users JWT token by Tenant Administrator user_token_access_enabled: "${SECURITY_USER_TOKEN_ACCESS_ENABLED:true}" # Enable/disable case-sensitive username login user_login_case_sensitive: "${SECURITY_USER_LOGIN_CASE_SENSITIVE:true}" claim: - # Enable/disable claiming devices, if false -> the device's [claimingAllowed] SERVER_SCOPE attribute must be set to [true] to allow claiming specific device + # Enable/disable claiming devices; if false -> the device's [claimingAllowed] SERVER_SCOPE attribute must be set to [true] to allow claiming the specific device allowClaimingByDefault: "${SECURITY_CLAIM_ALLOW_CLAIMING_BY_DEFAULT:true}" # Time allowed to claim the device in milliseconds duration: "${SECURITY_CLAIM_DURATION:86400000}" # 1 minute, note this value must equal claimDevices.timeToLiveInMinutes value basic: + # Enable/Disable basic security options enabled: "${SECURITY_BASIC_ENABLED:false}" oauth2: # Redirect URL where access code from external user management system will be processed loginProcessingUrl: "${SECURITY_OAUTH2_LOGIN_PROCESSING_URL:/login/oauth2/code/}" githubMapper: + # The email addresses that will be mapped from the URL emailUrl: "${SECURITY_OAUTH2_GITHUB_MAPPER_EMAIL_URL_KEY:https://api.github.com/user/emails}" java_cacerts: + # CA certificates keystore default path. Typically this keystore is at JAVA_HOME/lib/security/cacerts path: "${SECURITY_JAVA_CACERTS_PATH:${java.home}/lib/security/cacerts}" + # The password of the cacerts keystore file password: "${SECURITY_JAVA_CACERTS_PASSWORD:changeit}" +# Mail settings parameters mail: oauth2: - refreshTokenCheckingInterval: "${REFRESH_TOKEN_EXPIRATION_CHECKING_INTERVAL:86400}" # Number of seconds (1 day). + # Interval for checking refresh token expiration in seconds(by default, 1 day). + refreshTokenCheckingInterval: "${REFRESH_TOKEN_EXPIRATION_CHECKING_INTERVAL:86400}" # Usage statistics parameters usage: stats: report: + # Enable/Disable the collection of API usage statistics. Collected on a system and tenant level by default enabled: "${USAGE_STATS_REPORT_ENABLED:true}" + # Enable/Disable the collection of API usage statistics on a customer level enabled_per_customer: "${USAGE_STATS_REPORT_PER_CUSTOMER_ENABLED:false}" + # Statistics reporting interval, set to send summarized data every 10 seconds by default interval: "${USAGE_STATS_REPORT_INTERVAL:10}" check: + # Interval of checking the start of the next cycle and re-enabling the blocked tenants/customers cycle: "${USAGE_STATS_CHECK_CYCLE:60000}" - # In milliseconds. Default value is 3 minutes + # In milliseconds. The default value is 3 minutes gauge_report_interval: "${USAGE_STATS_GAUGE_REPORT_INTERVAL:180000}" devices: - # In seconds, default value is 1 minutes. When changing, in cluster mode, make sure usage.stats.gauge_report_interval is set to x2-x3 of this value + # In seconds, the default value is 1 minute. When changing, in cluster mode, make sure usage.stats.gauge_report_interval is set to x2-x3 of this value report_interval: "${DEVICES_STATS_REPORT_INTERVAL:60}" -# UI parameters +# UI settings parameters ui: # Dashboard parameters dashboard: @@ -162,11 +192,12 @@ ui: max_datapoints_limit: "${DASHBOARD_MAX_DATAPOINTS_LIMIT:50000}" # Help parameters help: - # Base url for UI help assets - base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-3.5.1}" + # Base URL for UI help assets + base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-3.6.2}" +# Database telemetry parameters database: - ts_max_intervals: "${DATABASE_TS_MAX_INTERVALS:700}" # Max number of DB queries generated by single API call to fetch telemetry records + ts_max_intervals: "${DATABASE_TS_MAX_INTERVALS:700}" # Max number of DB queries generated by a single API call to fetch telemetry records ts: type: "${DATABASE_TS_TYPE:sql}" # cassandra, sql, or timescale (for hybrid mode, DATABASE_TS_TYPE value should be cassandra, or timescale) ts_latest: @@ -180,22 +211,24 @@ cassandra: keyspace_name: "${CASSANDRA_KEYSPACE_NAME:thingsboard}" # Specify node list url: "${CASSANDRA_URL:127.0.0.1:9042}" - # Specify local datacenter name + # Specify the local data center name local_datacenter: "${CASSANDRA_LOCAL_DATACENTER:datacenter1}" ssl: # Enable/disable secure connection enabled: "${CASSANDRA_USE_SSL:false}" # Enable/disable validation of Cassandra server hostname - # If enabled, hostname of Cassandra server must match CN of server certificate + # If enabled, the hostname of the Cassandra server must match the CN of the server certificate hostname_validation: "${CASSANDRA_SSL_HOSTNAME_VALIDATION:true}" - # Set trust store for client authentication of server (optional, uses trust store from default SSLContext if not set) + # Set trust store for client authentication of the server (optional, uses trust store from default SSLContext if not set) trust_store: "${CASSANDRA_SSL_TRUST_STORE:}" + # The password for Cassandra trust store key trust_store_password: "${CASSANDRA_SSL_TRUST_STORE_PASSWORD:}" - # Set key store for server authentication of client (optional, uses key store from default SSLContext if not set) + # Set key store for server authentication of the client (optional, uses key store from default SSLContext if not set) # A key store is only needed if the Cassandra server requires client authentication key_store: "${CASSANDRA_SSL_KEY_STORE:}" + # The password for the Cassandra key store key_store_password: "${CASSANDRA_SSL_KEY_STORE_PASSWORD:}" - # Comma separated list of cipher suites (optional, uses Java default cipher suites if not set) + # Comma-separated list of cipher suites (optional, uses Java default cipher suites if not set) cipher_suites: "${CASSANDRA_SSL_CIPHER_SUITES:}" # Enable/disable JMX jmx: "${CASSANDRA_USE_JMX:false}" @@ -203,11 +236,13 @@ cassandra: metrics: "${CASSANDRA_USE_METRICS:false}" # NONE SNAPPY LZ4 compression: "${CASSANDRA_COMPRESSION:none}" - # Specify cassandra cluster initialization timeout in milliseconds (if no hosts available during startup) + # Specify cassandra cluster initialization timeout in milliseconds (if no hosts are available during startup) init_timeout_ms: "${CASSANDRA_CLUSTER_INIT_TIMEOUT_MS:300000}" - # Specify cassandra claster initialization retry interval (if no hosts available during startup) + # Specify cassandra cluster initialization retry interval (if no hosts available during startup) init_retry_interval_ms: "${CASSANDRA_CLUSTER_INIT_RETRY_INTERVAL_MS:3000}" + # Cassandra max local requests per connection max_requests_per_connection_local: "${CASSANDRA_MAX_REQUESTS_PER_CONNECTION_LOCAL:32768}" + # Cassandra max remote requests per connection max_requests_per_connection_remote: "${CASSANDRA_MAX_REQUESTS_PER_CONNECTION_REMOTE:32768}" # Credential parameters credentials: "${CASSANDRA_USE_CREDENTIALS:false}" @@ -226,69 +261,93 @@ cassandra: # Cassandra cluster connection socket parameters # socket: + # Sets the timeout, in milliseconds, of a native connection from ThingsBoard to Cassandra. The default value is 5000 connect_timeout: "${CASSANDRA_SOCKET_TIMEOUT:5000}" + # Timeout before closing the connection. Value set in milliseconds read_timeout: "${CASSANDRA_SOCKET_READ_TIMEOUT:20000}" + # Gets if TCP keep-alive must be used keep_alive: "${CASSANDRA_SOCKET_KEEP_ALIVE:true}" + # Enable/Disable reuse-address. The socket option allows for the reuse of local addresses and ports reuse_address: "${CASSANDRA_SOCKET_REUSE_ADDRESS:true}" + # Sets the linger-on-close timeout. By default, this option is not set by the driver. The actual value will be the default from the underlying Netty transport so_linger: "${CASSANDRA_SOCKET_SO_LINGER:}" + # Enable/Disable Nagle's algorithm tcp_no_delay: "${CASSANDRA_SOCKET_TCP_NO_DELAY:false}" + # Sets a hint to the size of the underlying buffers for incoming network I/O. By default, this option is not set by the driver. The actual value will be the default from the underlying Netty transport receive_buffer_size: "${CASSANDRA_SOCKET_RECEIVE_BUFFER_SIZE:}" + # Returns the hint to the size of the underlying buffers for outgoing network I/O. By default, this option is not set by the driver. The actual value will be the default from the underlying Netty transport send_buffer_size: "${CASSANDRA_SOCKET_SEND_BUFFER_SIZE:}" - # Cassandra cluster connection query parameters # + # Cassandra cluster connection query parameters query: + # Consistency levels in Cassandra can be configured to manage availability versus data accuracy. The consistency level defaults to ONE for all write and read operations read_consistency_level: "${CASSANDRA_READ_CONSISTENCY_LEVEL:ONE}" + # Consistency levels in Cassandra can be configured to manage availability versus data accuracy. The consistency level defaults to ONE for all write and read operations write_consistency_level: "${CASSANDRA_WRITE_CONSISTENCY_LEVEL:ONE}" + # The fetch size specifies how many rows will be returned at once by Cassandra (in other words, it’s the size of each page) default_fetch_size: "${CASSANDRA_DEFAULT_FETCH_SIZE:2000}" # Specify partitioning size for timestamp key-value storage. Example: MINUTES, HOURS, DAYS, MONTHS, INDEFINITE ts_key_value_partitioning: "${TS_KV_PARTITIONING:MONTHS}" + # Enable/Disable timestamp key-value partioning on read queries use_ts_key_value_partitioning_on_read: "${USE_TS_KV_PARTITIONING_ON_READ:true}" + # The number of partitions that are cached in memory of each service. It is useful to decrease the load of re-inserting the same partitions again ts_key_value_partitions_max_cache_size: "${TS_KV_PARTITIONS_MAX_CACHE_SIZE:100000}" + # Timeseries Time To Live (in seconds) for Cassandra Record. 0 - record has never expired ts_key_value_ttl: "${TS_KV_TTL:0}" + # Maximum number of Cassandra queries that are waiting for execution buffer_size: "${CASSANDRA_QUERY_BUFFER_SIZE:200000}" + # Maximum number of concurrent Cassandra queries concurrent_limit: "${CASSANDRA_QUERY_CONCURRENT_LIMIT:1000}" + # Max time in milliseconds query waits for execution permit_max_wait_time: "${PERMIT_MAX_WAIT_TIME:120000}" + # Amount of threads to dispatch cassandra queries dispatcher_threads: "${CASSANDRA_QUERY_DISPATCHER_THREADS:2}" - callback_threads: "${CASSANDRA_QUERY_CALLBACK_THREADS:4}" + callback_threads: "${CASSANDRA_QUERY_CALLBACK_THREADS:4}" # Buffered rate executor (read, write) for managing I/O rate. See "nosql-*-callback" threads in JMX + result_processing_threads: "${CASSANDRA_QUERY_RESULT_PROCESSING_THREADS:50}" # Result set transformer and processing. See "cassandra-callback" threads in JMX + # Cassandra query queue polling interval in milliseconds poll_ms: "${CASSANDRA_QUERY_POLL_MS:50}" + # Interval in milliseconds for printing Cassandra query queue statistic rate_limit_print_interval_ms: "${CASSANDRA_QUERY_RATE_LIMIT_PRINT_MS:10000}" - # set all data types values except target to null for the same ts on save + # set all data type values except target to null for the same ts on save set_null_values_enabled: "${CASSANDRA_QUERY_SET_NULL_VALUES_ENABLED:false}" # log one of cassandra queries with specified frequency (0 - logging is disabled) print_queries_freq: "${CASSANDRA_QUERY_PRINT_FREQ:0}" tenant_rate_limits: + # Whether to print rate-limited tenant names when printing Cassandra query queue statistic print_tenant_names: "${CASSANDRA_QUERY_TENANT_RATE_LIMITS_PRINT_TENANT_NAMES:false}" # SQL configuration parameters sql: # Specify batch size for persisting attribute updates attributes: - batch_size: "${SQL_ATTRIBUTES_BATCH_SIZE:10000}" - batch_max_delay: "${SQL_ATTRIBUTES_BATCH_MAX_DELAY_MS:100}" - stats_print_interval_ms: "${SQL_ATTRIBUTES_BATCH_STATS_PRINT_MS:10000}" - batch_threads: "${SQL_ATTRIBUTES_BATCH_THREADS:3}" # batch thread count have to be a prime number like 3 or 5 to gain perfect hash distribution + batch_size: "${SQL_ATTRIBUTES_BATCH_SIZE:1000}" # Batch size for persisting attribute updates + batch_max_delay: "${SQL_ATTRIBUTES_BATCH_MAX_DELAY_MS:50}" # Max timeout for attributes entries queue polling. The value is set in milliseconds + stats_print_interval_ms: "${SQL_ATTRIBUTES_BATCH_STATS_PRINT_MS:10000}" # Interval in milliseconds for printing attributes updates statistic + batch_threads: "${SQL_ATTRIBUTES_BATCH_THREADS:3}" # batch thread count has to be a prime number like 3 or 5 to gain perfect hash distribution + value_no_xss_validation: "${SQL_ATTRIBUTES_VALUE_NO_XSS_VALIDATION:false}" # If true attribute values will be checked for XSS vulnerability ts: - batch_size: "${SQL_TS_BATCH_SIZE:10000}" - batch_max_delay: "${SQL_TS_BATCH_MAX_DELAY_MS:100}" - stats_print_interval_ms: "${SQL_TS_BATCH_STATS_PRINT_MS:10000}" - batch_threads: "${SQL_TS_BATCH_THREADS:3}" # batch thread count have to be a prime number like 3 or 5 to gain perfect hash distribution + batch_size: "${SQL_TS_BATCH_SIZE:10000}" # Batch size for persisting timeseries inserts + batch_max_delay: "${SQL_TS_BATCH_MAX_DELAY_MS:100}" # Max timeout for time-series entries queue polling. The value set in milliseconds + stats_print_interval_ms: "${SQL_TS_BATCH_STATS_PRINT_MS:10000}" # Interval in milliseconds for printing timeseries insert statistic + batch_threads: "${SQL_TS_BATCH_THREADS:3}" # batch thread count has to be a prime number like 3 or 5 to gain perfect hash distribution + value_no_xss_validation: "${SQL_TS_VALUE_NO_XSS_VALIDATION:false}" # If true telemetry values will be checked for XSS vulnerability ts_latest: - batch_size: "${SQL_TS_LATEST_BATCH_SIZE:10000}" - batch_max_delay: "${SQL_TS_LATEST_BATCH_MAX_DELAY_MS:100}" - stats_print_interval_ms: "${SQL_TS_LATEST_BATCH_STATS_PRINT_MS:10000}" - batch_threads: "${SQL_TS_LATEST_BATCH_THREADS:3}" # batch thread count have to be a prime number like 3 or 5 to gain perfect hash distribution - update_by_latest_ts: "${SQL_TS_UPDATE_BY_LATEST_TIMESTAMP:true}" + batch_size: "${SQL_TS_LATEST_BATCH_SIZE:1000}" # Batch size for persisting latest telemetry updates + batch_max_delay: "${SQL_TS_LATEST_BATCH_MAX_DELAY_MS:50}" # Maximum timeout for latest telemetry entries queue polling. The value set in milliseconds + stats_print_interval_ms: "${SQL_TS_LATEST_BATCH_STATS_PRINT_MS:10000}" # Interval in milliseconds for printing latest telemetry updates statistic + batch_threads: "${SQL_TS_LATEST_BATCH_THREADS:3}" # batch thread count has to be a prime number like 3 or 5 to gain perfect hash distribution + update_by_latest_ts: "${SQL_TS_UPDATE_BY_LATEST_TIMESTAMP:true}" # Update latest values only if the timestamp of the new record is greater or equals the timestamp of the previously saved latest value. The latest values are stored separately from historical values for fast lookup from DB. Insert of historical value happens in any case events: - batch_size: "${SQL_EVENTS_BATCH_SIZE:10000}" - batch_max_delay: "${SQL_EVENTS_BATCH_MAX_DELAY_MS:100}" - stats_print_interval_ms: "${SQL_EVENTS_BATCH_STATS_PRINT_MS:10000}" - batch_threads: "${SQL_EVENTS_BATCH_THREADS:3}" # batch thread count have to be a prime number like 3 or 5 to gain perfect hash distribution + batch_size: "${SQL_EVENTS_BATCH_SIZE:10000}" # Batch size for persisting latest telemetry updates + batch_max_delay: "${SQL_EVENTS_BATCH_MAX_DELAY_MS:100}" # Max timeout for latest telemetry entries queue polling. The value set in milliseconds + stats_print_interval_ms: "${SQL_EVENTS_BATCH_STATS_PRINT_MS:10000}" # Interval in milliseconds for printing latest telemetry updates statistic + batch_threads: "${SQL_EVENTS_BATCH_THREADS:3}" # batch thread count has to be a prime number like 3 or 5 to gain perfect hash distribution partition_size: "${SQL_EVENTS_REGULAR_PARTITION_SIZE_HOURS:168}" # Number of hours to partition the events. The current value corresponds to one week. debug_partition_size: "${SQL_EVENTS_DEBUG_PARTITION_SIZE_HOURS:1}" # Number of hours to partition the debug events. The current value corresponds to one hour. edge_events: - batch_size: "${SQL_EDGE_EVENTS_BATCH_SIZE:1000}" - batch_max_delay: "${SQL_EDGE_EVENTS_BATCH_MAX_DELAY_MS:100}" - stats_print_interval_ms: "${SQL_EDGE_EVENTS_BATCH_STATS_PRINT_MS:10000}" + batch_size: "${SQL_EDGE_EVENTS_BATCH_SIZE:1000}" # Batch size for persisting latest telemetry updates + batch_max_delay: "${SQL_EDGE_EVENTS_BATCH_MAX_DELAY_MS:100}" # Max timeout for latest telemetry entries queue polling. The value set in milliseconds + stats_print_interval_ms: "${SQL_EDGE_EVENTS_BATCH_STATS_PRINT_MS:10000}" # Interval in milliseconds for printing latest telemetry updates statistic partition_size: "${SQL_EDGE_EVENTS_PARTITION_SIZE_HOURS:168}" # Number of hours to partition the events. The current value corresponds to one week. audit_logs: partition_size: "${SQL_AUDIT_LOGS_PARTITION_SIZE_HOURS:168}" # Default value - 1 week @@ -300,10 +359,13 @@ sql: batch_sort: "${SQL_BATCH_SORT:true}" # Specify whether to remove null characters from strValue of attributes and timeseries before insert remove_null_chars: "${SQL_REMOVE_NULL_CHARS:true}" - # Specify whether to log database queries and their parameters generated by entity query repository + # Specify whether to log database queries and their parameters generated by the entity query repository log_queries: "${SQL_LOG_QUERIES:false}" + # Threshold of slow SQL queries to log. The value set in milliseconds log_queries_threshold: "${SQL_LOG_QUERIES_THRESHOLD:5000}" + # Enable/Disable logging statistic information about tenants log_tenant_stats: "${SQL_LOG_TENANT_STATS:true}" + # Interval in milliseconds for printing the latest statistic information about the tenant log_tenant_stats_interval_ms: "${SQL_LOG_TENANT_STATS_INTERVAL_MS:60000}" postgres: # Specify partitioning size for timestamp key-value storage. Example: DAYS, MONTHS, YEARS, INDEFINITE. @@ -311,58 +373,61 @@ sql: timescale: # Specify Interval size for new data chunks storage. chunk_time_interval: "${SQL_TIMESCALE_CHUNK_TIME_INTERVAL:604800000}" - batch_threads: "${SQL_TIMESCALE_BATCH_THREADS:3}" # batch thread count have to be a prime number like 3 or 5 to gain perfect hash distribution + batch_threads: "${SQL_TIMESCALE_BATCH_THREADS:3}" # batch thread count has to be a prime number like 3 or 5 to gain perfect hash distribution ttl: ts: + # Enable/disable TTL (Time To Live) for timeseries records enabled: "${SQL_TTL_TS_ENABLED:true}" execution_interval_ms: "${SQL_TTL_TS_EXECUTION_INTERVAL:86400000}" # Number of milliseconds. The current value corresponds to one day ts_key_value_ttl: "${SQL_TTL_TS_TS_KEY_VALUE_TTL:0}" # Number of seconds events: + # Enable/disable TTL (Time To Live) for event records enabled: "${SQL_TTL_EVENTS_ENABLED:true}" execution_interval_ms: "${SQL_TTL_EVENTS_EXECUTION_INTERVAL:3600000}" # Number of milliseconds (max random initial delay and fixed period). - # Number of seconds. TTL is disabled by default. Accuracy of the cleanup depends on the sql.events.partition_size parameter. + # Number of seconds. TTL is disabled by default. The accuracy of the cleanup depends on the sql.events.partition_size parameter. events_ttl: "${SQL_TTL_EVENTS_EVENTS_TTL:0}" - # Number of seconds. The current value corresponds to one week. Accuracy of the cleanup depends on the sql.events.debug_partition_size parameter. + # Number of seconds. The current value corresponds to one week. The accuracy of the cleanup depends on the sql.events.debug_partition_size parameter. debug_events_ttl: "${SQL_TTL_EVENTS_DEBUG_EVENTS_TTL:604800}" edge_events: - enabled: "${SQL_TTL_EDGE_EVENTS_ENABLED:true}" + enabled: "${SQL_TTL_EDGE_EVENTS_ENABLED:true}" # Enable/disable TTL (Time To Live) for edge event records execution_interval_ms: "${SQL_TTL_EDGE_EVENTS_EXECUTION_INTERVAL:86400000}" # Number of milliseconds. The current value corresponds to one day edge_events_ttl: "${SQL_TTL_EDGE_EVENTS_TTL:2628000}" # Number of seconds. The current value corresponds to one month alarms: checking_interval: "${SQL_ALARMS_TTL_CHECKING_INTERVAL:7200000}" # Number of milliseconds. The current value corresponds to two hours removal_batch_size: "${SQL_ALARMS_TTL_REMOVAL_BATCH_SIZE:3000}" # To delete outdated alarms not all at once but in batches rpc: - enabled: "${SQL_TTL_RPC_ENABLED:true}" + enabled: "${SQL_TTL_RPC_ENABLED:true}" # Enable/disable TTL (Time To Live) for rpc call records checking_interval: "${SQL_RPC_TTL_CHECKING_INTERVAL:7200000}" # Number of milliseconds. The current value corresponds to two hours audit_logs: - enabled: "${SQL_TTL_AUDIT_LOGS_ENABLED:true}" - ttl: "${SQL_TTL_AUDIT_LOGS_SECS:0}" # Disabled by default. Accuracy of the cleanup depends on the sql.audit_logs.partition_size + enabled: "${SQL_TTL_AUDIT_LOGS_ENABLED:true}" # Enable/disable TTL (Time To Live) for audit log records + ttl: "${SQL_TTL_AUDIT_LOGS_SECS:0}" # Disabled by default. The accuracy of the cleanup depends on the sql.audit_logs.partition_size checking_interval_ms: "${SQL_TTL_AUDIT_LOGS_CHECKING_INTERVAL_MS:86400000}" # Default value - 1 day notifications: - enabled: "${SQL_TTL_NOTIFICATIONS_ENABLED:true}" + enabled: "${SQL_TTL_NOTIFICATIONS_ENABLED:true}" # Enable/disable TTL (Time To Live) for notification center records ttl: "${SQL_TTL_NOTIFICATIONS_SECS:2592000}" # Default value - 30 days checking_interval_ms: "${SQL_TTL_NOTIFICATIONS_CHECKING_INTERVAL_MS:86400000}" # Default value - 1 day relations: - max_level: "${SQL_RELATIONS_MAX_LEVEL:50}" # This value has to be reasonable small to prevent infinite recursion as early as possible - pool_size: "${SQL_RELATIONS_POOL_SIZE:4}" # This value has to be reasonable small to prevent relation query blocking all other DB calls - query_timeout: "${SQL_RELATIONS_QUERY_TIMEOUT_SEC:20}" # This value has to be reasonable small to prevent relation query blocking all other DB calls + max_level: "${SQL_RELATIONS_MAX_LEVEL:50}" # This value has to be reasonably small to prevent infinite recursion as early as possible + pool_size: "${SQL_RELATIONS_POOL_SIZE:4}" # This value has to be reasonably small to prevent the relation query from blocking all other DB calls + query_timeout: "${SQL_RELATIONS_QUERY_TIMEOUT_SEC:20}" # This value has to be reasonably small to prevent the relation query from blocking all other DB calls # Actor system parameters actors: system: - throughput: "${ACTORS_SYSTEM_THROUGHPUT:5}" - scheduler_pool_size: "${ACTORS_SYSTEM_SCHEDULER_POOL_SIZE:1}" - max_actor_init_attempts: "${ACTORS_SYSTEM_MAX_ACTOR_INIT_ATTEMPTS:10}" - app_dispatcher_pool_size: "${ACTORS_SYSTEM_APP_DISPATCHER_POOL_SIZE:1}" - tenant_dispatcher_pool_size: "${ACTORS_SYSTEM_TENANT_DISPATCHER_POOL_SIZE:2}" - device_dispatcher_pool_size: "${ACTORS_SYSTEM_DEVICE_DISPATCHER_POOL_SIZE:4}" - rule_dispatcher_pool_size: "${ACTORS_SYSTEM_RULE_DISPATCHER_POOL_SIZE:4}" + throughput: "${ACTORS_SYSTEM_THROUGHPUT:5}" # Number of messages the actor system will process per actor before switching to processing of messages for the next actor + scheduler_pool_size: "${ACTORS_SYSTEM_SCHEDULER_POOL_SIZE:1}" # Thread pool size for actor system scheduler + max_actor_init_attempts: "${ACTORS_SYSTEM_MAX_ACTOR_INIT_ATTEMPTS:10}" # Maximum number of attempts to init the actor before disabling the actor + app_dispatcher_pool_size: "${ACTORS_SYSTEM_APP_DISPATCHER_POOL_SIZE:1}" # Thread pool size for main actor system dispatcher + tenant_dispatcher_pool_size: "${ACTORS_SYSTEM_TENANT_DISPATCHER_POOL_SIZE:2}" # Thread pool size for actor system dispatcher that process messages for tenant actors + device_dispatcher_pool_size: "${ACTORS_SYSTEM_DEVICE_DISPATCHER_POOL_SIZE:4}" # Thread pool size for actor system dispatcher that process messages for device actors + rule_dispatcher_pool_size: "${ACTORS_SYSTEM_RULE_DISPATCHER_POOL_SIZE:8}" # Thread pool size for actor system dispatcher that process messages for rule engine (chain/node) actors + edge_dispatcher_pool_size: "${ACTORS_SYSTEM_EDGE_DISPATCHER_POOL_SIZE:4}" # Thread pool size for actor system dispatcher that process messages for edge actors tenant: - create_components_on_init: "${ACTORS_TENANT_CREATE_COMPONENTS_ON_INIT:true}" + create_components_on_init: "${ACTORS_TENANT_CREATE_COMPONENTS_ON_INIT:true}" # Create components in initialization session: - max_concurrent_sessions_per_device: "${ACTORS_MAX_CONCURRENT_SESSION_PER_DEVICE:1}" + max_concurrent_sessions_per_device: "${ACTORS_MAX_CONCURRENT_SESSION_PER_DEVICE:1}" # Max number of concurrent sessions per device sync: - # Default timeout for processing request using synchronous session (HTTP, CoAP) in milliseconds + # Default timeout for processing requests using synchronous session (HTTP, CoAP) in milliseconds timeout: "${ACTORS_SESSION_SYNC_TIMEOUT:10000}" rule: # Specify thread pool size for database request callbacks executor service @@ -380,146 +445,179 @@ actors: # Specify thread pool size for external call service external_call_thread_pool_size: "${ACTORS_RULE_EXTERNAL_CALL_THREAD_POOL_SIZE:50}" chain: - # Errors for particular actor are persisted once per specified amount of milliseconds + # Errors for particular actors are persisted once per specified amount of milliseconds error_persist_frequency: "${ACTORS_RULE_CHAIN_ERROR_FREQUENCY:3000}" debug_mode_rate_limits_per_tenant: + # Enable/Disable the rate limit of persisted debug events for all rule nodes per tenant enabled: "${ACTORS_RULE_CHAIN_DEBUG_MODE_RATE_LIMITS_PER_TENANT_ENABLED:true}" + # The value of DEBUG mode rate limit. By default, no more then 50 thousand events per hour configuration: "${ACTORS_RULE_CHAIN_DEBUG_MODE_RATE_LIMITS_PER_TENANT_CONFIGURATION:50000:3600}" node: # Errors for particular actor are persisted once per specified amount of milliseconds error_persist_frequency: "${ACTORS_RULE_NODE_ERROR_FREQUENCY:3000}" transaction: - # Size of queues which store messages for transaction rule nodes + # Size of queues that store messages for transaction rule nodes queue_size: "${ACTORS_RULE_TRANSACTION_QUEUE_SIZE:15000}" # Time in milliseconds for transaction to complete duration: "${ACTORS_RULE_TRANSACTION_DURATION:60000}" + external: + # Force acknowledgment of the incoming message for external rule nodes to decrease processing latency. + # Enqueue the result of external node processing as a separate message to the rule engine. + force_ack: "${ACTORS_RULE_EXTERNAL_NODE_FORCE_ACK:false}" rpc: + # Maximum number of persistent RPC call retries in case of failed request delivery. max_retries: "${ACTORS_RPC_MAX_RETRIES:5}" - sequential: "${ACTORS_RPC_SEQUENTIAL:false}" + # RPC submit strategies. Allowed values: BURST, SEQUENTIAL_ON_ACK_FROM_DEVICE, SEQUENTIAL_ON_RESPONSE_FROM_DEVICE. + submit_strategy: "${ACTORS_RPC_SUBMIT_STRATEGY_TYPE:BURST}" + # Time in milliseconds for RPC to receive a response after delivery. Used only for SEQUENTIAL_ON_RESPONSE_FROM_DEVICE submit strategy. + response_timeout_ms: "${ACTORS_RPC_RESPONSE_TIMEOUT_MS:30000}" statistics: # Enable/disable actor statistics enabled: "${ACTORS_STATISTICS_ENABLED:true}" + # Frequency of printing the JS executor statistics js_print_interval_ms: "${ACTORS_JS_STATISTICS_PRINT_INTERVAL_MS:10000}" + # Actors statistic persistence frequency in milliseconds persist_frequency: "${ACTORS_STATISTICS_PERSIST_FREQUENCY:3600000}" +# Cache settings parameters cache: # caffeine or redis type: "${CACHE_TYPE:caffeine}" - maximumPoolSize: "${CACHE_MAXIMUM_POOL_SIZE:16}" # max pool size to process futures that calls the external cache + maximumPoolSize: "${CACHE_MAXIMUM_POOL_SIZE:16}" # max pool size to process futures that call the external cache attributes: - # make sure that if cache.type is 'redis' and cache.attributes.enabled is 'true' that you change 'maxmemory-policy' Redis config property to 'allkeys-lru', 'allkeys-lfu' or 'allkeys-random' + # make sure that if cache.type is 'redis' and cache.attributes.enabled is 'true' if you change 'maxmemory-policy' Redis config property to 'allkeys-lru', 'allkeys-lfu' or 'allkeys-random' enabled: "${CACHE_ATTRIBUTES_ENABLED:true}" specs: relations: - timeToLiveInMinutes: "${CACHE_SPECS_RELATIONS_TTL:1440}" - maxSize: "${CACHE_SPECS_RELATIONS_MAX_SIZE:10000}" # maxSize: 0 means the cache is disabled + timeToLiveInMinutes: "${CACHE_SPECS_RELATIONS_TTL:1440}" # Relations cache TTL + maxSize: "${CACHE_SPECS_RELATIONS_MAX_SIZE:10000}" # 0 means the cache is disabled deviceCredentials: - timeToLiveInMinutes: "${CACHE_SPECS_DEVICE_CREDENTIALS_TTL:1440}" - maxSize: "${CACHE_SPECS_DEVICE_CREDENTIALS_MAX_SIZE:10000}" + timeToLiveInMinutes: "${CACHE_SPECS_DEVICE_CREDENTIALS_TTL:1440}" # Device credentials cache TTL + maxSize: "${CACHE_SPECS_DEVICE_CREDENTIALS_MAX_SIZE:10000}" # 0 means the cache is disabled devices: - timeToLiveInMinutes: "${CACHE_SPECS_DEVICES_TTL:1440}" - maxSize: "${CACHE_SPECS_DEVICES_MAX_SIZE:10000}" + timeToLiveInMinutes: "${CACHE_SPECS_DEVICES_TTL:1440}" # Device cache TTL + maxSize: "${CACHE_SPECS_DEVICES_MAX_SIZE:10000}" # 0 means the cache is disabled sessions: - timeToLiveInMinutes: "${CACHE_SPECS_SESSIONS_TTL:1440}" - maxSize: "${CACHE_SPECS_SESSIONS_MAX_SIZE:10000}" + timeToLiveInMinutes: "${CACHE_SPECS_SESSIONS_TTL:1440}" # Sessions cache TTL + maxSize: "${CACHE_SPECS_SESSIONS_MAX_SIZE:10000}" # 0 means the cache is disabled assets: - timeToLiveInMinutes: "${CACHE_SPECS_ASSETS_TTL:1440}" - maxSize: "${CACHE_SPECS_ASSETS_MAX_SIZE:10000}" + timeToLiveInMinutes: "${CACHE_SPECS_ASSETS_TTL:1440}" # Asset cache TTL + maxSize: "${CACHE_SPECS_ASSETS_MAX_SIZE:10000}" # 0 means the cache is disabled entityViews: - timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_VIEWS_TTL:1440}" - maxSize: "${CACHE_SPECS_ENTITY_VIEWS_MAX_SIZE:10000}" + timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_VIEWS_TTL:1440}" # Entity view cache TTL + maxSize: "${CACHE_SPECS_ENTITY_VIEWS_MAX_SIZE:10000}" # 0 means the cache is disabled claimDevices: - timeToLiveInMinutes: "${CACHE_SPECS_CLAIM_DEVICES_TTL:1440}" - maxSize: "${CACHE_SPECS_CLAIM_DEVICES_MAX_SIZE:1000}" + timeToLiveInMinutes: "${CACHE_SPECS_CLAIM_DEVICES_TTL:1440}" # Claim devices cache TTL + maxSize: "${CACHE_SPECS_CLAIM_DEVICES_MAX_SIZE:1000}" # 0 means the cache is disabled securitySettings: - timeToLiveInMinutes: "${CACHE_SPECS_SECURITY_SETTINGS_TTL:1440}" - maxSize: "${CACHE_SPECS_SECURITY_SETTINGS_MAX_SIZE:10000}" + timeToLiveInMinutes: "${CACHE_SPECS_SECURITY_SETTINGS_TTL:1440}" # Security settings cache TTL + maxSize: "${CACHE_SPECS_SECURITY_SETTINGS_MAX_SIZE:10000}" # 0 means the cache is disabled tenantProfiles: - timeToLiveInMinutes: "${CACHE_SPECS_TENANT_PROFILES_TTL:1440}" - maxSize: "${CACHE_SPECS_TENANT_PROFILES_MAX_SIZE:10000}" + timeToLiveInMinutes: "${CACHE_SPECS_TENANT_PROFILES_TTL:1440}" # Tenant profiles cache TTL + maxSize: "${CACHE_SPECS_TENANT_PROFILES_MAX_SIZE:10000}" # 0 means the cache is disabled tenants: - timeToLiveInMinutes: "${CACHE_SPECS_TENANTS_TTL:1440}" - maxSize: "${CACHE_SPECS_TENANTS_MAX_SIZE:10000}" + timeToLiveInMinutes: "${CACHE_SPECS_TENANTS_TTL:1440}" # Tenant cache TTL + maxSize: "${CACHE_SPECS_TENANTS_MAX_SIZE:10000}" # 0 means the cache is disabled tenantsExist: - # environment variables are intentionally the same as in 'tenants' cache to be equal. + # Environment variables are intentionally the same as in 'tenants' cache to be equal. timeToLiveInMinutes: "${CACHE_SPECS_TENANTS_TTL:1440}" - maxSize: "${CACHE_SPECS_TENANTS_MAX_SIZE:10000}" + maxSize: "${CACHE_SPECS_TENANTS_MAX_SIZE:10000}" # 0 means the cache is disabled deviceProfiles: - timeToLiveInMinutes: "${CACHE_SPECS_DEVICE_PROFILES_TTL:1440}" - maxSize: "${CACHE_SPECS_DEVICE_PROFILES_MAX_SIZE:10000}" + timeToLiveInMinutes: "${CACHE_SPECS_DEVICE_PROFILES_TTL:1440}" # Device profile cache TTL + maxSize: "${CACHE_SPECS_DEVICE_PROFILES_MAX_SIZE:10000}" # 0 means the cache is disabled assetProfiles: - timeToLiveInMinutes: "${CACHE_SPECS_ASSET_PROFILES_TTL:1440}" - maxSize: "${CACHE_SPECS_ASSET_PROFILES_MAX_SIZE:10000}" + timeToLiveInMinutes: "${CACHE_SPECS_ASSET_PROFILES_TTL:1440}" # Asset profile cache TTL + maxSize: "${CACHE_SPECS_ASSET_PROFILES_MAX_SIZE:10000}" # 0 means the cache is disabled notificationSettings: - timeToLiveInMinutes: "${CACHE_SPECS_NOTIFICATION_SETTINGS_TTL:10}" - maxSize: "${CACHE_SPECS_NOTIFICATION_SETTINGS_MAX_SIZE:1000}" + timeToLiveInMinutes: "${CACHE_SPECS_NOTIFICATION_SETTINGS_TTL:10}" # Notification settings cache TTL + maxSize: "${CACHE_SPECS_NOTIFICATION_SETTINGS_MAX_SIZE:1000}" # 0 means the cache is disabled sentNotifications: - timeToLiveInMinutes: "${CACHE_SPECS_SENT_NOTIFICATIONS_TTL:1440}" - maxSize: "${CACHE_SPECS_SENT_NOTIFICATIONS_MAX_SIZE:10000}" + timeToLiveInMinutes: "${CACHE_SPECS_SENT_NOTIFICATIONS_TTL:1440}" # Sent notifications cache TTL + maxSize: "${CACHE_SPECS_SENT_NOTIFICATIONS_MAX_SIZE:10000}" # 0 means the cache is disabled attributes: - timeToLiveInMinutes: "${CACHE_SPECS_ATTRIBUTES_TTL:1440}" - maxSize: "${CACHE_SPECS_ATTRIBUTES_MAX_SIZE:100000}" + timeToLiveInMinutes: "${CACHE_SPECS_ATTRIBUTES_TTL:1440}" # Attributes cache TTL + maxSize: "${CACHE_SPECS_ATTRIBUTES_MAX_SIZE:100000}" # 0 means the cache is disabled userSessionsInvalidation: - # The value of this TTL is ignored and replaced by JWT refresh token expiration time + # The value of this TTL is ignored and replaced by the JWT refresh token expiration time timeToLiveInMinutes: "0" - maxSize: "${CACHE_SPECS_USERS_UPDATE_TIME_MAX_SIZE:10000}" + maxSize: "${CACHE_SPECS_USERS_UPDATE_TIME_MAX_SIZE:10000}" # 0 means the cache is disabled otaPackages: - timeToLiveInMinutes: "${CACHE_SPECS_OTA_PACKAGES_TTL:60}" - maxSize: "${CACHE_SPECS_OTA_PACKAGES_MAX_SIZE:10}" + timeToLiveInMinutes: "${CACHE_SPECS_OTA_PACKAGES_TTL:60}" # Ota packages cache TTL + maxSize: "${CACHE_SPECS_OTA_PACKAGES_MAX_SIZE:10}" # 0 means the cache is disabled otaPackagesData: - timeToLiveInMinutes: "${CACHE_SPECS_OTA_PACKAGES_DATA_TTL:60}" - maxSize: "${CACHE_SPECS_OTA_PACKAGES_DATA_MAX_SIZE:10}" + timeToLiveInMinutes: "${CACHE_SPECS_OTA_PACKAGES_DATA_TTL:60}" # Ota packages data cache TTL + maxSize: "${CACHE_SPECS_OTA_PACKAGES_DATA_MAX_SIZE:10}" # 0 means the cache is disabled edges: - timeToLiveInMinutes: "${CACHE_SPECS_EDGES_TTL:1440}" - maxSize: "${CACHE_SPECS_EDGES_MAX_SIZE:10000}" + timeToLiveInMinutes: "${CACHE_SPECS_EDGES_TTL:1440}" # Edges cache TTL + maxSize: "${CACHE_SPECS_EDGES_MAX_SIZE:10000}" # 0 means the cache is disabled repositorySettings: - timeToLiveInMinutes: "${CACHE_SPECS_REPOSITORY_SETTINGS_TTL:1440}" - maxSize: "${CACHE_SPECS_REPOSITORY_SETTINGS_MAX_SIZE:10000}" + timeToLiveInMinutes: "${CACHE_SPECS_REPOSITORY_SETTINGS_TTL:1440}" # Repository settings cache TTL + maxSize: "${CACHE_SPECS_REPOSITORY_SETTINGS_MAX_SIZE:10000}" # 0 means the cache is disabled autoCommitSettings: - timeToLiveInMinutes: "${CACHE_SPECS_AUTO_COMMIT_SETTINGS_TTL:1440}" - maxSize: "${CACHE_SPECS_AUTO_COMMIT_SETTINGS_MAX_SIZE:10000}" + timeToLiveInMinutes: "${CACHE_SPECS_AUTO_COMMIT_SETTINGS_TTL:1440}" # Autocommit settings cache TTL + maxSize: "${CACHE_SPECS_AUTO_COMMIT_SETTINGS_MAX_SIZE:10000}" # 0 means the cache is disabled twoFaVerificationCodes: - timeToLiveInMinutes: "${CACHE_SPECS_TWO_FA_VERIFICATION_CODES_TTL:60}" - maxSize: "${CACHE_SPECS_TWO_FA_VERIFICATION_CODES_MAX_SIZE:100000}" + timeToLiveInMinutes: "${CACHE_SPECS_TWO_FA_VERIFICATION_CODES_TTL:60}" # Two factor verification codes cache TTL + maxSize: "${CACHE_SPECS_TWO_FA_VERIFICATION_CODES_MAX_SIZE:100000}" # 0 means the cache is disabled versionControlTask: - timeToLiveInMinutes: "${CACHE_SPECS_VERSION_CONTROL_TASK_TTL:5}" - maxSize: "${CACHE_SPECS_VERSION_CONTROL_TASK_MAX_SIZE:100000}" + timeToLiveInMinutes: "${CACHE_SPECS_VERSION_CONTROL_TASK_TTL:20}" # Version control task cache TTL + maxSize: "${CACHE_SPECS_VERSION_CONTROL_TASK_MAX_SIZE:100000}" # 0 means the cache is disabled userSettings: - timeToLiveInMinutes: "${CACHE_SPECS_USER_SETTINGS_TTL:1440}" - maxSize: "${CACHE_SPECS_USER_SETTINGS_MAX_SIZE:100000}" + timeToLiveInMinutes: "${CACHE_SPECS_USER_SETTINGS_TTL:1440}" # User settings cache TTL + maxSize: "${CACHE_SPECS_USER_SETTINGS_MAX_SIZE:100000}" # 0 means the cache is disabled dashboardTitles: - timeToLiveInMinutes: "${CACHE_SPECS_DASHBOARD_TITLES_TTL:1440}" - maxSize: "${CACHE_SPECS_DASHBOARD_TITLES_MAX_SIZE:100000}" + timeToLiveInMinutes: "${CACHE_SPECS_DASHBOARD_TITLES_TTL:1440}" # Dashboard titles cache TTL + maxSize: "${CACHE_SPECS_DASHBOARD_TITLES_MAX_SIZE:100000}" # 0 means the cache is disabled entityCount: - timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_COUNT_TTL:1440}" - maxSize: "${CACHE_SPECS_ENTITY_COUNT_MAX_SIZE:100000}" + timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_COUNT_TTL:1440}" # Entity count cache TTL + maxSize: "${CACHE_SPECS_ENTITY_COUNT_MAX_SIZE:100000}" # 0 means the cache is disabled + resourceInfo: + timeToLiveInMinutes: "${CACHE_SPECS_RESOURCE_INFO_TTL:1440}" # Resource info cache TTL + maxSize: "${CACHE_SPECS_RESOURCE_INFO_MAX_SIZE:100000}" # 0 means the cache is disabled + alarmTypes: + timeToLiveInMinutes: "${CACHE_SPECS_ALARM_TYPES_TTL:60}" # Alarm types cache TTL + maxSize: "${CACHE_SPECS_ALARM_TYPES_MAX_SIZE:10000}" # 0 means the cache is disabled - # deliberately placed outside 'specs' group above + # Deliberately placed outside the 'specs' group above notificationRules: - timeToLiveInMinutes: "${CACHE_SPECS_NOTIFICATION_RULES_TTL:30}" - maxSize: "${CACHE_SPECS_NOTIFICATION_RULES_MAX_SIZE:1000}" + timeToLiveInMinutes: "${CACHE_SPECS_NOTIFICATION_RULES_TTL:30}" # Notification rules cache TTL + maxSize: "${CACHE_SPECS_NOTIFICATION_RULES_MAX_SIZE:1000}" # 0 means the cache is disabled rateLimits: - timeToLiveInMinutes: "${CACHE_SPECS_RATE_LIMITS_TTL:120}" - maxSize: "${CACHE_SPECS_RATE_LIMITS_MAX_SIZE:200000}" + timeToLiveInMinutes: "${CACHE_SPECS_RATE_LIMITS_TTL:120}" # Rate limits cache TTL + maxSize: "${CACHE_SPECS_RATE_LIMITS_MAX_SIZE:200000}" # 0 means the cache is disabled + entityLimits: + timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_LIMITS_TTL:5}" # Entity limits cache TTL + maxSize: "${CACHE_SPECS_ENTITY_LIMITS_MAX_SIZE:100000}" # 0 means the cache is disabled + image: + etag: + timeToLiveInMinutes: "${CACHE_SPECS_IMAGE_ETAGS_TTL:44640}" # Image ETags cache TTL + maxSize: "${CACHE_SPECS_IMAGE_ETAGS_MAX_SIZE:10000}" # 0 means the cache is disabled + systemImagesBrowserTtlInMinutes: "${CACHE_SPECS_IMAGE_SYSTEM_BROWSER_TTL:0}" # Browser cache TTL for system images in minutes. 0 means the cache is disabled + tenantImagesBrowserTtlInMinutes: "${CACHE_SPECS_IMAGE_TENANT_BROWSER_TTL:0}" # Browser cache TTL for tenant images in minutes. 0 means the cache is disabled -#Disable this because it is not required. -spring.data.redis.repositories.enabled: false +# Spring data parameters +spring.data.redis.repositories.enabled: false # Disable this because it is not required. +# Redis configuration parameters redis: # standalone or cluster or sentinel connection: + # Redis deployment type: Standalone (single Redis node deployment) OR Cluster type: "${REDIS_CONNECTION_TYPE:standalone}" standalone: + # Redis connection host host: "${REDIS_HOST:localhost}" + # Redis connection port port: "${REDIS_PORT:6379}" + # Use the default Redis configuration file useDefaultClientConfig: "${REDIS_USE_DEFAULT_CLIENT_CONFIG:true}" - # this value may be used only if you used not default ClientConfig + # This value may be used only if you used not default ClientConfig clientName: "${REDIS_CLIENT_NAME:standalone}" - # this value may be used only if you used not default ClientConfig + # This value may be used only if you used not default ClientConfig connectTimeout: "${REDIS_CLIENT_CONNECT_TIMEOUT:30000}" - # this value may be used only if you used not default ClientConfig + # This value may be used only if you used not default ClientConfig readTimeout: "${REDIS_CLIENT_READ_TIMEOUT:60000}" - # this value may be used only if you used not default ClientConfig + # This value may be used only if you used not default ClientConfig usePoolConfig: "${REDIS_CLIENT_USE_POOL_CONFIG:false}" cluster: # Comma-separated list of "host:port" pairs to bootstrap from. @@ -529,7 +627,7 @@ redis: # if set false will be used pool config build from values of the pool config section useDefaultPoolConfig: "${REDIS_USE_DEFAULT_POOL_CONFIG:true}" sentinel: - # name of master node + # name of the master node master: "${REDIS_MASTER:}" # comma-separated list of "host:port" pairs of sentinels sentinels: "${REDIS_SENTINELS:}" @@ -543,36 +641,43 @@ redis: password: "${REDIS_PASSWORD:}" # pool config pool_config: + # Maximum number of connections that can be allocated by the connection pool maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:128}" + # Maximum number of idle connections that can be maintained in the pool without being closed maxIdle: "${REDIS_POOL_CONFIG_MAX_IDLE:128}" + # Minumum number of idle connections that can be maintained in the pool without being closed minIdle: "${REDIS_POOL_CONFIG_MIN_IDLE:16}" + # Enable/Disable PING command sent when a connection is borrowed testOnBorrow: "${REDIS_POOL_CONFIG_TEST_ON_BORROW:true}" + # The property is used to specify whether to test the connection before returning it to the connection pool. testOnReturn: "${REDIS_POOL_CONFIG_TEST_ON_RETURN:true}" + # The property is used in the context of connection pooling in Redis testWhileIdle: "${REDIS_POOL_CONFIG_TEST_WHILE_IDLE:true}" + # Minimum time that an idle connection should be idle before it can be evicted from the connection pool. The value is set in milliseconds minEvictableMs: "${REDIS_POOL_CONFIG_MIN_EVICTABLE_MS:60000}" + # Specifies the time interval in milliseconds between two consecutive eviction runs evictionRunsMs: "${REDIS_POOL_CONFIG_EVICTION_RUNS_MS:30000}" + # Maximum time in milliseconds where a client is willing to wait for a connection from the pool when all connections are exhausted maxWaitMills: "${REDIS_POOL_CONFIG_MAX_WAIT_MS:60000}" + # Specifies the number of connections to test for eviction during each eviction run numberTestsPerEvictionRun: "${REDIS_POOL_CONFIG_NUMBER_TESTS_PER_EVICTION_RUN:3}" + # Determines the behavior when a thread requests a connection from the pool, but there are no available connections, and the pool cannot create more due to the maxTotal configuration blockWhenExhausted: "${REDIS_POOL_CONFIG_BLOCK_WHEN_EXHAUSTED:true}" - # TTL for short-living SET commands that are used to replace DEL in order to enable transaction support + # TTL for short-living SET commands that are used to replace DEL to enable transaction support evictTtlInMs: "${REDIS_EVICT_TTL_MS:60000}" -# Check new version updates parameters + +# Update version parameters updates: - # Enable/disable updates checking. + # Enable/disable checks for the new version enabled: "${UPDATES_ENABLED:true}" -spring.main.allow-circular-references: "true" - -# spring freemarker configuration -spring.freemarker.checkTemplateLocation: "false" - -# spring CORS configuration +# Spring CORS configuration parameters spring.mvc.cors: mappings: # Intercept path "[/api/**]": - #Comma-separated list of origins to allow. '*' allows all origins. When not set,CORS support is disabled. + #Comma-separated list of origins to allow. '*' allows all origins. When not set, CORS support is disabled. allowed-origin-patterns: "*" #Comma-separated list of methods to allow. '*' allows all methods. allowed-methods: "*" @@ -583,45 +688,50 @@ spring.mvc.cors: #Set whether credentials are supported. When not set, credentials are not supported. allow-credentials: "true" -# The default timeout for asynchronous requests in milliseconds -spring.mvc.async.request-timeout: "${SPRING_MVC_ASYNC_REQUEST_TIMEOUT:30000}" - -# For endpoints matching in Swagger -spring.mvc.pathmatch.matching-strategy: "ANT_PATH_MATCHER" - -# spring serve gzip compressed static resources +# General spring parameters +spring.main.allow-circular-references: "true" # Spring Boot configuration property that controls whether circular dependencies between beans are allowed. +spring.freemarker.checkTemplateLocation: "false" # spring freemarker configuration +spring.mvc.async.request-timeout: "${SPRING_MVC_ASYNC_REQUEST_TIMEOUT:30000}" # The default timeout for asynchronous requests in milliseconds +spring.mvc.pathmatch.matching-strategy: "ANT_PATH_MATCHER" # For endpoints matching in Swagger spring.resources.chain: - compressed: "true" + compressed: "true" # This property enables or disables support for serving pre-compressed resources (for example, a .gzip or .br file) strategy: content: - enabled: "true" + enabled: "true" # This property enables or disables the content Version Strategy. This strategy allows Spring to generate a unique version for static resources, which is based on the content of the resource -spring.servlet.multipart.max-file-size: "50MB" -spring.servlet.multipart.max-request-size: "50MB" +spring.servlet.multipart.max-file-size: "${SPRING_SERVLET_MULTIPART_MAX_FILE_SIZE:50MB}" # Total file size cannot exceed 50MB when configuring file uploads +spring.servlet.multipart.max-request-size: "${SPRING_SERVLET_MULTIPART_MAX_REQUEST_SIZE:50MB}" # Total request size for a multipart/form-data cannot exceed 50MB -spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation: "true" -# Note: as for current Spring JPA version, custom NullHandling for the Sort.Order is ignored and this parameter is used -spring.jpa.properties.hibernate.order_by.default_null_ordering: "${SPRING_JPA_PROPERTIES_HIBERNATE_ORDER_BY_DEFAULT_NULL_ORDERING:last}" +spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation: "true" #Fix Postgres JPA Error (Method org.postgresql.jdbc.PgConnection.createClob() is not yet implemented) +spring.jpa.properties.hibernate.order_by.default_null_ordering: "${SPRING_JPA_PROPERTIES_HIBERNATE_ORDER_BY_DEFAULT_NULL_ORDERING:last}" # Note: as for current Spring JPA version, custom NullHandling for the Sort.Order is ignored and this parameter is used +spring.jpa.properties.hibernate.dialect: "${SPRING_JPA_DIALECT:org.thingsboard.server.dao.ThingsboardPostgreSQLDialect}" # we use custom dialect that contains ilike(arg1, arg2) function (is interpreted to postgres ILIKE operator) -# SQL DAO Configuration +# SQL DAO Configuration parameters spring: data: jpa: repositories: - enabled: "true" + enabled: "true" # Enable/Disable the Spring Data JPA repositories support jpa: properties: - javax.persistence.query.timeout: "${JAVAX_PERSISTENCE_QUERY_TIMEOUT:30000}" - open-in-view: "false" + javax.persistence.query.timeout: "${JAVAX_PERSISTENCE_QUERY_TIMEOUT:30000}" # General timeout for JDBC queries + open-in-view: "false" # Enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning hibernate: + # You can set a Hibernate feature that controls the DDL behavior in a more fine-grained way. The standard Hibernate property values are none, validate, update, create-drop. Spring Boot chooses a default value for you based on whether it thinks your database is embedded (default create-drop) or not (default none) ddl-auto: "none" datasource: + # Database driver for Spring JPA - org.postgresql.Driver driverClassName: "${SPRING_DRIVER_CLASS_NAME:org.postgresql.Driver}" + # Database connection URL url: "${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/thingsboard}" + # Database user name username: "${SPRING_DATASOURCE_USERNAME:postgres}" + # Database user password password: "${SPRING_DATASOURCE_PASSWORD:postgres}" hikari: + # This property controls the amount of time that a connection can be out of the pool before a message is logged indicating a possible connection leak. A value of 0 means leak detection is disabled leakDetectionThreshold: "${SPRING_DATASOURCE_HIKARI_LEAK_DETECTION_THRESHOLD:0}" + # This property increases the number of connections in the pool as demand increases. At the same time, the property ensures that the pool doesn't grow to the point of exhausting a system's resources, which ultimately affects an application's performance and availability maximumPoolSize: "${SPRING_DATASOURCE_MAXIMUM_POOL_SIZE:16}" registerMbeans: "${SPRING_DATASOURCE_HIKARI_REGISTER_MBEANS:false}" # true - enable MBean to diagnose pools state via JMX @@ -633,51 +743,58 @@ audit-log: # Allowed values: OFF (disable), W (log write operations), RW (log read and write operations) logging-level: mask: - "device": "${AUDIT_LOG_MASK_DEVICE:W}" - "asset": "${AUDIT_LOG_MASK_ASSET:W}" - "dashboard": "${AUDIT_LOG_MASK_DASHBOARD:W}" - "widgets_bundle": "${AUDIT_LOG_MASK_WIDGETS_BUNDLE:W}" - "customer": "${AUDIT_LOG_MASK_CUSTOMER:W}" - "user": "${AUDIT_LOG_MASK_USER:W}" - "rule_chain": "${AUDIT_LOG_MASK_RULE_CHAIN:W}" - "alarm": "${AUDIT_LOG_MASK_ALARM:W}" - "entity_view": "${AUDIT_LOG_MASK_ENTITY_VIEW:W}" - "device_profile": "${AUDIT_LOG_MASK_DEVICE_PROFILE:W}" - "asset_profile": "${AUDIT_LOG_MASK_ASSET_PROFILE:W}" - "edge": "${AUDIT_LOG_MASK_EDGE:W}" - "tb_resource": "${AUDIT_LOG_MASK_RESOURCE:W}" - "ota_package": "${AUDIT_LOG_MASK_OTA_PACKAGE:W}" + "device": "${AUDIT_LOG_MASK_DEVICE:W}" # Device logging levels. Allowed values: OFF (disable), W (log write operations), RW (log read and write operation + "asset": "${AUDIT_LOG_MASK_ASSET:W}" # Asset logging levels. Allowed values: OFF (disable), W (log write operations), RW (log read and write operation + "dashboard": "${AUDIT_LOG_MASK_DASHBOARD:W}" # Dashboard logging levels. Allowed values: OFF (disable), W (log write operations), RW (log read and write operation + "widget_type": "${AUDIT_LOG_MASK_WIDGET_TYPE:W}" # Widget type logging levels. Allowed values: OFF (disable), W (log write operations), RW (log read and write operation + "widgets_bundle": "${AUDIT_LOG_MASK_WIDGETS_BUNDLE:W}" # Widget bundles logging levels. Allowed values: OFF (disable), W (log write operations), RW (log read and write operation + "customer": "${AUDIT_LOG_MASK_CUSTOMER:W}" # Customer logging levels. Allowed values: OFF (disable), W (log write operations), RW (log read and write operation + "user": "${AUDIT_LOG_MASK_USER:W}" # User logging levels. Allowed values: OFF (disable), W (log write operations), RW (log read and write operation + "rule_chain": "${AUDIT_LOG_MASK_RULE_CHAIN:W}" # Rule chain logging levels. Allowed values: OFF (disable), W (log write operations), RW (log read and write operation + "alarm": "${AUDIT_LOG_MASK_ALARM:W}" # Alarm logging levels. Allowed values: OFF (disable), W (log write operations), RW (log read and write operation + "entity_view": "${AUDIT_LOG_MASK_ENTITY_VIEW:W}" # Entity view logging levels. Allowed values: OFF (disable), W (log write operations), RW (log read and write operation + "device_profile": "${AUDIT_LOG_MASK_DEVICE_PROFILE:W}" # Device profile logging levels. Allowed values: OFF (disable), W (log write operations), RW (log read and write operation + "asset_profile": "${AUDIT_LOG_MASK_ASSET_PROFILE:W}" # Asset profile logging levels. Allowed values: OFF (disable), W (log write operations), RW (log read and write operation + "edge": "${AUDIT_LOG_MASK_EDGE:W}" # Edge logging levels. Allowed values: OFF (disable), W (log write operations), RW (log read and write operation + "tb_resource": "${AUDIT_LOG_MASK_RESOURCE:W}" # TB resource logging levels. Allowed values: OFF (disable), W (log write operations), RW (log read and write operation + "ota_package": "${AUDIT_LOG_MASK_OTA_PACKAGE:W}" # Ota package logging levels. Allowed values: OFF (disable), W (log write operations), RW (log read and write operation sink: # Type of external sink. possible options: none, elasticsearch type: "${AUDIT_LOG_SINK_TYPE:none}" - # Name of the index where audit logs stored + # Name of the index where audit logs are stored # Index name could contain next placeholders (not mandatory): # @{TENANT} - substituted by tenant ID # @{DATE} - substituted by current date in format provided in audit_log.sink.date_format index_pattern: "${AUDIT_LOG_SINK_INDEX_PATTERN:@{TENANT}_AUDIT_LOG_@{DATE}}" - # Date format. Details of the pattern could be found here: + # Date format. Details of the pattern can be found here: # https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html date_format: "${AUDIT_LOG_SINK_DATE_FORMAT:YYYY.MM.dd}" scheme_name: "${AUDIT_LOG_SINK_SCHEME_NAME:http}" # http or https - host: "${AUDIT_LOG_SINK_HOST:localhost}" - port: "${AUDIT_LOG_SINK_PORT:9200}" - user_name: "${AUDIT_LOG_SINK_USER_NAME:}" - password: "${AUDIT_LOG_SINK_PASSWORD:}" + host: "${AUDIT_LOG_SINK_HOST:localhost}" # Host of external sink system + port: "${AUDIT_LOG_SINK_PORT:9200}" # Port of external sink system + user_name: "${AUDIT_LOG_SINK_USER_NAME:}" # Username used to access external sink system + password: "${AUDIT_LOG_SINK_PASSWORD:}" # Password used to access external sink system +# Device state parameters state: - # Should be greater then transport.sessions.report_timeout + # Should be greater than transport.sessions.report_timeout defaultInactivityTimeoutInSec: "${DEFAULT_INACTIVITY_TIMEOUT:600}" - defaultStateCheckIntervalInSec: "${DEFAULT_STATE_CHECK_INTERVAL:60}" - # Controls whether we store device 'active' flag in attributes (default) or telemetry. + defaultStateCheckIntervalInSec: "${DEFAULT_STATE_CHECK_INTERVAL:60}" # Interval for checking the device state after a specified period. Time in seconds + # Controls whether we store the device 'active' flag in attributes (default) or telemetry. # If you device to change this parameter, you should re-create the device info view as one of the following: # If 'persistToTelemetry' is changed from 'false' to 'true': 'CREATE OR REPLACE VIEW device_info_view AS SELECT * FROM device_info_active_ts_view;' # If 'persistToTelemetry' is changed from 'true' to 'false': 'CREATE OR REPLACE VIEW device_info_view AS SELECT * FROM device_info_active_attribute_view;' persistToTelemetry: "${PERSIST_STATE_TO_TELEMETRY:false}" +# Tbel parameters tbel: + # Enable/Disable TBEL feature. enabled: "${TBEL_ENABLED:true}" + # Limit the number of arguments that are passed to the function to execute the script max_total_args_size: "${TBEL_MAX_TOTAL_ARGS_SIZE:100000}" + # Maximum allowed symbols in a result after processing a script max_result_size: "${TBEL_MAX_RESULT_SIZE:300000}" + # Maximum allowed symbols in the script body max_script_body_size: "${TBEL_MAX_SCRIPT_BODY_SIZE:50000}" # Maximum allowed TBEL script execution memory max_memory_limit_mb: "${TBEL_MAX_MEMORY_LIMIT_MB: 8}" @@ -689,15 +806,23 @@ tbel: max_black_list_duration_sec: "${TBEL_MAX_BLACKLIST_DURATION_SEC:60}" # Specify thread pool size for javascript executor service thread_pool_size: "${TBEL_THREAD_POOL_SIZE:50}" + # Maximum cache size of TBEL compiled scripts compiled_scripts_cache_size: "${TBEL_COMPILED_SCRIPTS_CACHE_SIZE:1000}" stats: + # Enable/Disable stats collection for TBEL engine enabled: "${TB_TBEL_STATS_ENABLED:false}" + # Interval of logging for TBEL stats print_interval_ms: "${TB_TBEL_STATS_PRINT_INTERVAL_MS:10000}" +# JS parameters js: - evaluator: "${JS_EVALUATOR:local}" # local/remote + # local (Nashorn Engine, deprecated) OR remote JS-Executors (NodeJS) + evaluator: "${JS_EVALUATOR:local}" + # Limit the number of arguments that are passed to the function to execute the script max_total_args_size: "${JS_MAX_TOTAL_ARGS_SIZE:100000}" + # Maximum allowed symbols in a result after processing a script max_result_size: "${JS_MAX_RESULT_SIZE:300000}" + # Maximum allowed symbols in script body max_script_body_size: "${JS_MAX_SCRIPT_BODY_SIZE:50000}" # Built-in JVM JavaScript environment properties local: @@ -716,7 +841,9 @@ js: # Maximum time in seconds for black listed function to stay in the list. max_black_list_duration_sec: "${LOCAL_JS_SANDBOX_MAX_BLACKLIST_DURATION_SEC:60}" stats: + # Enable/Disable stats collection for local JS executor enabled: "${TB_JS_LOCAL_STATS_ENABLED:false}" + # Interval of logging for local JS executor stats print_interval_ms: "${TB_JS_LOCAL_STATS_PRINT_INTERVAL_MS:10000}" # Remote JavaScript environment properties remote: @@ -727,12 +854,17 @@ js: # Maximum time in seconds for black listed function to stay in the list. max_black_list_duration_sec: "${REMOTE_JS_SANDBOX_MAX_BLACKLIST_DURATION_SEC:60}" stats: + # Enable/Disable stats collection for remote JS executor enabled: "${TB_JS_REMOTE_STATS_ENABLED:false}" + # Interval of logging for remote JS executor stats print_interval_ms: "${TB_JS_REMOTE_STATS_PRINT_INTERVAL_MS:10000}" +# Transport configuration parameters transport: sessions: + # Inactivity timeout for device session in transport service. The last activity time of the device session is updated if the device sends any message, including keepalive messages inactivity_timeout: "${TB_TRANSPORT_SESSIONS_INACTIVITY_TIMEOUT:300000}" + # Interval of periodic check for expired sessions and report of the changes to session last activity time report_timeout: "${TB_TRANSPORT_SESSIONS_REPORT_TIMEOUT:3000}" json: # Cast String data types to Numeric if possible when processing Telemetry/Attributes JSON @@ -740,46 +872,60 @@ transport: # Maximum allowed string value length when processing Telemetry/Attributes JSON (0 value disables string value length check) max_string_value_length: "${JSON_MAX_STRING_VALUE_LENGTH:0}" client_side_rpc: + # Processing timeout interval of the RPC command on the CLIENT SIDE. Time in milliseconds timeout: "${CLIENT_SIDE_RPC_TIMEOUT:60000}" # Enable/disable http/mqtt/coap transport protocols (has higher priority than certain protocol's 'enabled' property) api_enabled: "${TB_TRANSPORT_API_ENABLED:true}" log: + # Enable/Disable log of transport messages to telemetry. For example, logging of LwM2M registration update enabled: "${TB_TRANSPORT_LOG_ENABLED:true}" + # Maximum length of the log message. The content will be truncated to the specified value if needed max_length: "${TB_TRANSPORT_LOG_MAX_LENGTH:1024}" rate_limits: - # Enable or disable generic rate limits. Device and Tenant specific rate limits are controlled in Tenant Profile. + # Enable or disable generic rate limits. Device and Tenant-specific rate limits are controlled in Tenant Profile. ip_limits_enabled: "${TB_TRANSPORT_IP_RATE_LIMITS_ENABLED:false}" # Maximum number of connect attempts with invalid credentials max_wrong_credentials_per_ip: "${TB_TRANSPORT_MAX_WRONG_CREDENTIALS_PER_IP:10}" - # Timeout to expire block IP addresses + # Timeout (in milliseconds) to expire block IP addresses ip_block_timeout: "${TB_TRANSPORT_IP_BLOCK_TIMEOUT:60000}" # Local HTTP transport parameters http: + # Enable/Disable local HTTP transport protocol enabled: "${HTTP_ENABLED:true}" + # HTTP request processing timeout in milliseconds request_timeout: "${HTTP_REQUEST_TIMEOUT:60000}" + # HTTP maximum request processing timeout in milliseconds max_request_timeout: "${HTTP_MAX_REQUEST_TIMEOUT:300000}" # Local MQTT transport parameters mqtt: # Enable/disable mqtt transport protocol. enabled: "${MQTT_ENABLED:true}" + # MQTT bind-address bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}" + # MQTT bind port bind_port: "${MQTT_BIND_PORT:1883}" # Enable proxy protocol support. Disabled by default. If enabled, supports both v1 and v2. # Useful to get the real IP address of the client in the logs and for rate limits. proxy_enabled: "${MQTT_PROXY_PROTOCOL_ENABLED:false}" + # MQTT processing timeout in milliseconds timeout: "${MQTT_TIMEOUT:10000}" - msg_queue_size_per_device_limit: "${MQTT_MSG_QUEUE_SIZE_PER_DEVICE_LIMIT:100}" # messages await in the queue before device connected state. This limit works on low level before TenantProfileLimits mechanism + msg_queue_size_per_device_limit: "${MQTT_MSG_QUEUE_SIZE_PER_DEVICE_LIMIT:100}" # messages await in the queue before the device connected state. This limit works on the low level before TenantProfileLimits mechanism netty: + # Netty leak detector level leak_detector_level: "${NETTY_LEAK_DETECTOR_LVL:DISABLED}" + # Netty BOSS threads count boss_group_thread_count: "${NETTY_BOSS_GROUP_THREADS:1}" + # Netty worker threads count worker_group_thread_count: "${NETTY_WORKER_GROUP_THREADS:12}" + # Max payload size in bytes max_payload_size: "${NETTY_MAX_PAYLOAD_SIZE:65536}" + # Enables TCP keepalive. This means that TCP starts sending keepalive probes when a connection is idle for some time so_keep_alive: "${NETTY_SO_KEEPALIVE:false}" # MQTT SSL configuration ssl: # Enable/disable SSL support enabled: "${MQTT_SSL_ENABLED:false}" - # MQTT SSL bind address + # MQTT SSL bind-address bind_address: "${MQTT_SSL_BIND_ADDRESS:0.0.0.0}" # MQTT SSL bind port bind_port: "${MQTT_SSL_BIND_PORT:8883}" @@ -805,7 +951,7 @@ transport: store_file: "${MQTT_SSL_KEY_STORE:mqttserver.jks}" # Password used to access the key store store_password: "${MQTT_SSL_KEY_STORE_PASSWORD:server_ks_password}" - # Optional alias of the private key; If not set, the platform will load the first private key from the keystore; + # Optional alias of the private key. If not set, the platform will load the first private key from the keystore key_alias: "${MQTT_SSL_KEY_ALIAS:}" # Optional password to access the private key. If not set, the platform will attempt to load the private keys that are not protected with the password; key_password: "${MQTT_SSL_KEY_PASSWORD:server_key_password}" @@ -813,20 +959,26 @@ transport: skip_validity_check_for_client_cert: "${MQTT_SSL_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}" # Local CoAP transport parameters coap: - # Enable/disable coap transport protocol. + # Enable/disable CoAP transport protocol. enabled: "${COAP_ENABLED:true}" + # CoAP bind-address bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}" + # CoAP bind port bind_port: "${COAP_BIND_PORT:5683}" + # CoaP processing timeout in milliseconds timeout: "${COAP_TIMEOUT:10000}" + # CoaP piggyback response timeout in milliseconds piggyback_timeout: "${COAP_PIGGYBACK_TIMEOUT:500}" + # Default PSM Activity Timer if not specified in device profile psm_activity_timer: "${COAP_PSM_ACTIVITY_TIMER:10000}" + # Default PSM Activity Timer if not specified in device profile paging_transmission_window: "${COAP_PAGING_TRANSMISSION_WINDOW:10000}" dtls: # Enable/disable DTLS 1.2 support enabled: "${COAP_DTLS_ENABLED:false}" # RFC7925_RETRANSMISSION_TIMEOUT_IN_MILLISECONDS = 9000 retransmission_timeout: "${COAP_DTLS_RETRANSMISSION_TIMEOUT_MS:9000}" - # CoAP DTLS bind address + # CoAP DTLS bind-address bind_address: "${COAP_DTLS_BIND_ADDRESS:0.0.0.0}" # CoAP DTLS bind port bind_port: "${COAP_DTLS_BIND_PORT:5684}" @@ -838,7 +990,7 @@ transport: pem: # Path to the server certificate file (holds server certificate or certificate chain, may include server private key) cert_file: "${COAP_DTLS_PEM_CERT:coapserver.pem}" - # Path to the server certificate private key file. Optional by default. Required if the private key is not present in server certificate file; + # Path to the server certificate private key file. Optional by default. Required if the private key is not present in the server certificate file; key_file: "${COAP_DTLS_PEM_KEY:coapserver_key.pem}" # Server certificate private key password (optional) key_password: "${COAP_DTLS_PEM_KEY_PASSWORD:server_key_password}" @@ -857,21 +1009,28 @@ transport: x509: # Skip certificate validity check for client certificates. skip_validity_check_for_client_cert: "${TB_COAP_X509_DTLS_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}" + # Inactivity timeout of DTLS session. Used to clean cache dtls_session_inactivity_timeout: "${TB_COAP_X509_DTLS_SESSION_INACTIVITY_TIMEOUT:86400000}" + # Interval of periodic eviction of the timed-out DTLS sessions dtls_session_report_timeout: "${TB_COAP_X509_DTLS_SESSION_REPORT_TIMEOUT:1800000}" # Local LwM2M transport parameters lwm2m: - # Enable/disable lvm2m transport protocol. + # Enable/disable LwM2M transport protocol. enabled: "${LWM2M_ENABLED:true}" dtls: # RFC7925_RETRANSMISSION_TIMEOUT_IN_MILLISECONDS = 9000 retransmission_timeout: "${LWM2M_DTLS_RETRANSMISSION_TIMEOUT_MS:9000}" server: + # LwM2M Server ID id: "${LWM2M_SERVER_ID:123}" + # LwM2M server bind address. Bind to all interfaces by default bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}" + # LwM2M server bind port bind_port: "${LWM2M_BIND_PORT:5685}" security: + # LwM2M server bind address for DTLS. Bind to all interfaces by default bind_address: "${LWM2M_SECURITY_BIND_ADDRESS:0.0.0.0}" + # LwM2M server bind port for DTLS bind_port: "${LWM2M_SECURITY_BIND_PORT:5686}" # Server X509 Certificates support credentials: @@ -883,7 +1042,7 @@ transport: pem: # Path to the server certificate file (holds server certificate or certificate chain, may include server private key) cert_file: "${LWM2M_SERVER_PEM_CERT:lwm2mserver.pem}" - # Path to the server certificate private key file. Optional by default. Required if the private key is not present in server certificate file; + # Path to the server certificate private key file. Optional by default. Required if the private key is not present in the server certificate file; key_file: "${LWM2M_SERVER_PEM_KEY:lwm2mserver_key.pem}" # Server certificate private key password (optional) key_password: "${LWM2M_SERVER_PEM_KEY_PASSWORD:server_key_password}" @@ -902,12 +1061,18 @@ transport: # Only Certificate_x509: skip_validity_check_for_client_cert: "${TB_LWM2M_SERVER_SECURITY_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}" bootstrap: + # Enable/disable Bootstrap Server enabled: "${LWM2M_ENABLED_BS:true}" + # Default value in LwM2M client after start in mode Bootstrap for the object : name "LWM2M Security" field: "Short Server ID" (deviceProfile: Bootstrap.BOOTSTRAP SERVER.Short ID) id: "${LWM2M_SERVER_ID_BS:111}" + # LwM2M bootstrap server bind address. Bind to all interfaces by default bind_address: "${LWM2M_BS_BIND_ADDRESS:0.0.0.0}" + # LwM2M bootstrap server bind port bind_port: "${LWM2M_BS_BIND_PORT:5687}" security: + # LwM2M bootstrap server bind address for DTLS. Bind to all interfaces by default bind_address: "${LWM2M_BS_SECURITY_BIND_ADDRESS:0.0.0.0}" + # LwM2M bootstrap server bind address for DTLS. Bind to all interfaces by default bind_port: "${LWM2M_BS_SECURITY_BIND_PORT:5688}" # Bootstrap server X509 Certificates support credentials: @@ -919,7 +1084,7 @@ transport: pem: # Path to the server certificate file (holds server certificate or certificate chain, may include server private key) cert_file: "${LWM2M_BS_PEM_CERT:lwm2mserver.pem}" - # Path to the server certificate private key file. Optional by default. Required if the private key is not present in server certificate file; + # Path to the server certificate private key file. Optional by default. Required if the private key is not present in server certificate file key_file: "${LWM2M_BS_PEM_KEY:lwm2mserver_key.pem}" # Server certificate private key password (optional) key_password: "${LWM2M_BS_PEM_KEY_PASSWORD:server_key_password}" @@ -954,221 +1119,432 @@ transport: store_file: "${LWM2M_TRUST_KEY_STORE:lwm2mtruststorechain.jks}" # Password used to access the key store store_password: "${LWM2M_TRUST_KEY_STORE_PASSWORD:server_ks_password}" + # Set usage of recommended cipher suites; true - allow only recommended cipher suites; false - allow not recommended cipher suites recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}" + # Set usage of recommended supported groups (curves); true - allow only recommended supported groups, false - allow not recommended supported groups recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:true}" + # Timeout of LwM2M operation timeout: "${LWM2M_TIMEOUT:120000}" + # Thread pool size for processing of the LwM2M uplinks uplink_pool_size: "${LWM2M_UPLINK_POOL_SIZE:10}" + # Thread pool size for processing of the LwM2M downlinks downlink_pool_size: "${LWM2M_DOWNLINK_POOL_SIZE:10}" + # Thread pool size for processing of the OTA updates ota_pool_size: "${LWM2M_OTA_POOL_SIZE:10}" + # Period of cleanup for the registrations in store clean_period_in_sec: "${LWM2M_CLEAN_PERIOD_IN_SEC:2}" + # Maximum log size log_max_length: "${LWM2M_LOG_MAX_LENGTH:1024}" + # PSM Activity Timer if not specified in the device profile psm_activity_timer: "${LWM2M_PSM_ACTIVITY_TIMER:10000}" + # Paging Transmission Window for eDRX support if not specified in the device profile paging_transmission_window: "${LWM2M_PAGING_TRANSMISSION_WINDOW:10000}" network_config: # In this section you can specify custom parameters for LwM2M network configuration and expose the env variables to configure outside # - key: "PROTOCOL_STAGE_THREAD_COUNT" # value: "${LWM2M_PROTOCOL_STAGE_THREAD_COUNT:4}" snmp: + # Enable/disable SNMP transport protocol enabled: "${SNMP_ENABLED:true}" + # Snmp bind port + bind_port: "${SNMP_BIND_PORT:1620}" response_processing: # parallelism level for executor (workStealingPool) that is responsible for handling responses from SNMP devices parallelism_level: "${SNMP_RESPONSE_PROCESSING_PARALLELISM_LEVEL:20}" # to configure SNMP to work over UDP or TCP underlying_protocol: "${SNMP_UNDERLYING_PROTOCOL:udp}" + # Batch size to request OID mappings from the device (useful when the device profile has multiple hundreds of OID mappings) + max_request_oids: "${SNMP_MAX_REQUEST_OIDS:100}" + response: + # To ignore SNMP response values that do not match the data type of the configured OID mapping (by default false - will throw an error if any value of the response not match configured data types) + ignore_type_cast_errors: "${SNMP_RESPONSE_IGNORE_TYPE_CAST_ERRORS:false}" stats: + # Enable/Disable the collection of transport statistics enabled: "${TB_TRANSPORT_STATS_ENABLED:true}" + # Interval of transport statistics logging print-interval-ms: "${TB_TRANSPORT_STATS_PRINT_INTERVAL_MS:60000}" +# Device connectivity parameters +device: + connectivity: + http: + # If true check-connectivity service will include curl command to the list of all test commands using DEVICE_CONNECTIVITY_HTTP_HOST and DEVICE_CONNECTIVITY_HTTP_PORT variables + enabled: "${DEVICE_CONNECTIVITY_HTTP_ENABLED:true}" + # Host of http transport service. If empty, the base URL will be used. + host: "${DEVICE_CONNECTIVITY_HTTP_HOST:}" + # Port of http transport service. If empty, default http port will be used. + port: "${DEVICE_CONNECTIVITY_HTTP_PORT:8080}" + https: + # If true check-connectivity service will include curl command to the list of all test commands using DEVICE_CONNECTIVITY_HTTPS_HOST and DEVICE_CONNECTIVITY_HTTPS_PORT variables + enabled: "${DEVICE_CONNECTIVITY_HTTPS_ENABLED:false}" + # Host of http transport service. If empty, the base URL will be used. + host: "${DEVICE_CONNECTIVITY_HTTPS_HOST:}" + # Port of http transport service. If empty, the default https port will be used. + port: "${DEVICE_CONNECTIVITY_HTTPS_PORT:443}" + mqtt: + # If true mosquito command will be included to the list of all test commands using DEVICE_CONNECTIVITY_MQTT_HOST and DEVICE_CONNECTIVITY_MQTT_PORT + enabled: "${DEVICE_CONNECTIVITY_MQTT_ENABLED:true}" + # Host of mqtt transport service. If empty, the base url host will be used. + host: "${DEVICE_CONNECTIVITY_MQTT_HOST:}" + # Port of mqtt transport service + port: "${DEVICE_CONNECTIVITY_MQTT_PORT:1883}" + mqtts: + # If true mosquito command will be included in the list of all test commands using DEVICE_CONNECTIVITY_MQTTS_HOST and DEVICE_CONNECTIVITY_MQTTS_PORT< + enabled: "${DEVICE_CONNECTIVITY_MQTTS_ENABLED:false}" + # Host of mqtt transport service. If empty, the base URL host will be used. + host: "${DEVICE_CONNECTIVITY_MQTTS_HOST:}" + # Port of mqtt transport service. If empty, the default port for mqtts will be used. + port: "${DEVICE_CONNECTIVITY_MQTTS_PORT:8883}" + # Path to the MQTT CA root certificate file + pem_cert_file: "${DEVICE_CONNECTIVITY_MQTTS_CA_ROOT_CERT:cafile.pem}" + coap: + # If true coap command will be included in the list of all test commands using DEVICE_CONNECTIVITY_COAP_HOST and DEVICE_CONNECTIVITY_COAP_PORT. + enabled: "${DEVICE_CONNECTIVITY_COAP_ENABLED:true}" + # Host of coap transport service. If empty, the base URL host will be used. + host: "${DEVICE_CONNECTIVITY_COAP_HOST:}" + # Port of coap transport service. If empty, the default port for coap will be used. + port: "${DEVICE_CONNECTIVITY_COAP_PORT:5683}" + coaps: + # If true coap command will be included in the list of all test commands using DEVICE_CONNECTIVITY_COAPS_HOST and DEVICE_CONNECTIVITY_COAPS_PORT. + enabled: "${DEVICE_CONNECTIVITY_COAPS_ENABLED:false}" + # Host of coap transport service. If empty, the base URL host will be used. + host: "${DEVICE_CONNECTIVITY_COAPS_HOST:}" + # Port of coap transport service. If empty, the default port for coaps will be used. + port: "${DEVICE_CONNECTIVITY_COAPS_PORT:5684}" + # Edges parameters edges: + # Enable/disable Edge instance enabled: "${EDGES_ENABLED:true}" rpc: + # RPC port bind port: "${EDGES_RPC_PORT:7070}" + # Maximum time of keeping alive client RPC connection. Time in seconds client_max_keep_alive_time_sec: "${EDGES_RPC_CLIENT_MAX_KEEP_ALIVE_TIME_SEC:1}" + # Maximum time of keep-alive connection. Time in seconds keep_alive_time_sec: "${EDGES_RPC_KEEP_ALIVE_TIME_SEC:10}" + # Timeout of keep alive RPC connection. Time in seconds keep_alive_timeout_sec: "${EDGES_RPC_KEEP_ALIVE_TIMEOUT_SEC:5}" ssl: # Enable/disable SSL support enabled: "${EDGES_RPC_SSL_ENABLED:false}" + # Cert file to be used during TLS connectivity to the cloud cert: "${EDGES_RPC_SSL_CERT:certChainFile.pem}" + # Private key file associated with the Cert certificate. This key is used in the encryption process during a secure connection private_key: "${EDGES_RPC_SSL_PRIVATE_KEY:privateKeyFile.pem}" + # Maximum size (in bytes) of inbound messages the cloud can handle from the edge. By default, it can handle messages up to 4 Megabytes max_inbound_message_size: "${EDGES_RPC_MAX_INBOUND_MESSAGE_SIZE:4194304}" storage: + # Max records of edge event to read from DB and sent to the edge max_read_records_count: "${EDGES_STORAGE_MAX_READ_RECORDS_COUNT:50}" + # Number of milliseconds to wait before the next check of edge events in DB no_read_records_sleep: "${EDGES_NO_READ_RECORDS_SLEEP:1000}" - sleep_between_batches: "${EDGES_SLEEP_BETWEEN_BATCHES:10000}" + # Number of milliseconds to wait before resending failed batch of edge events to edge + sleep_between_batches: "${EDGES_SLEEP_BETWEEN_BATCHES:60000}" + # Number of threads that are used to check DB for edge events scheduler_pool_size: "${EDGES_SCHEDULER_POOL_SIZE:1}" + # Number of threads that are used to send downlink messages to edge over gRPC send_scheduler_pool_size: "${EDGES_SEND_SCHEDULER_POOL_SIZE:1}" + # Number of threads that are used to convert edge events from DB into downlink messages and send them for delivery grpc_callback_thread_pool_size: "${EDGES_GRPC_CALLBACK_POOL_SIZE:1}" state: + # Persist state of edge (active, last connect, last disconnect) into timeseries or attributes tables. 'false' means to store edge state into attributes table persistToTelemetry: "${EDGES_PERSIST_STATE_TO_TELEMETRY:false}" +# Swagger common parameters swagger: + # If false swagger API docs will be unavailable enabled: "${SWAGGER_ENABLED:true}" + # General swagger match pattern of swagger UI links api_path_regex: "${SWAGGER_API_PATH_REGEX:/api/.*}" + # General swagger match pattern path of swagger UI links security_path_regex: "${SWAGGER_SECURITY_PATH_REGEX:/api/.*}" + # Nonsecurity API path match pattern of swagger UI links non_security_path_regex: "${SWAGGER_NON_SECURITY_PATH_REGEX:/api/(?:noauth|v1)/.*}" + # The title on the API doc UI page title: "${SWAGGER_TITLE:ThingsBoard REST API}" + # The description on the API doc UI page description: "${SWAGGER_DESCRIPTION: ThingsBoard open-source IoT platform REST API documentation.}" contact: + # The contact name on the API doc UI page name: "${SWAGGER_CONTACT_NAME:ThingsBoard team}" + # The contact URL on the API doc UI page url: "${SWAGGER_CONTACT_URL:https://thingsboard.io}" + # The contact email on the API doc UI page email: "${SWAGGER_CONTACT_EMAIL:info@thingsboard.io}" license: + # The license title on the API doc UI page title: "${SWAGGER_LICENSE_TITLE:Apache License Version 2.0}" + # Link to the license body on the API doc UI page url: "${SWAGGER_LICENSE_URL:https://github.com/thingsboard/thingsboard/blob/master/LICENSE}" + # The version of the API doc to display. Default to the package version. version: "${SWAGGER_VERSION:}" +# Queue configuration parameters queue: type: "${TB_QUEUE_TYPE:in-memory}" # in-memory or kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) + prefix: "${TB_QUEUE_PREFIX:}" # Global queue prefix. If specified, prefix is added before default topic name: 'prefix.default_topic_name'. Prefix is applied to all topics (and consumer groups for kafka). in_memory: stats: - # For debug lvl + # For debug level print-interval-ms: "${TB_QUEUE_IN_MEMORY_STATS_PRINT_INTERVAL_MS:60000}" kafka: + # Kafka Bootstrap nodes in "host:port" format bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}" ssl: + # Enable/Disable SSL Kafka communication enabled: "${TB_KAFKA_SSL_ENABLED:false}" + # The location of the trust store file truststore.location: "${TB_KAFKA_SSL_TRUSTSTORE_LOCATION:}" + # The password of trust store file if specified truststore.password: "${TB_KAFKA_SSL_TRUSTSTORE_PASSWORD:}" + # The location of the key store file. This is optional for the client and can be used for two-way authentication for the client keystore.location: "${TB_KAFKA_SSL_KEYSTORE_LOCATION:}" + # The store password for the key store file. This is optional for the client and only needed if ‘ssl.keystore.location’ is configured. Key store password is not supported for PEM format keystore.password: "${TB_KAFKA_SSL_KEYSTORE_PASSWORD:}" + # The password of the private key in the key store file or the PEM key specified in ‘keystore.key’ key.password: "${TB_KAFKA_SSL_KEY_PASSWORD:}" + # The number of acknowledgments the producer requires the leader to have received before considering a request complete. This controls the durability of records that are sent. The following settings are allowed:0, 1 and all acks: "${TB_KAFKA_ACKS:all}" + # Number of retries. Resend any record whose send fails with a potentially transient error retries: "${TB_KAFKA_RETRIES:1}" + # The compression type for all data generated by the producer. The default is none (i.e. no compression). Valid values none or gzip compression.type: "${TB_KAFKA_COMPRESSION_TYPE:none}" # none or gzip + # Default batch size. This setting gives the upper bound of the batch size to be sent batch.size: "${TB_KAFKA_BATCH_SIZE:16384}" + # This variable creates a small amount of artificial delay—that is, rather than immediately sending out a record linger.ms: "${TB_KAFKA_LINGER_MS:1}" + # The maximum size of a request in bytes. This setting will limit the number of record batches the producer will send in a single request to avoid sending huge requests max.request.size: "${TB_KAFKA_MAX_REQUEST_SIZE:1048576}" + # The maximum number of unacknowledged requests the client will send on a single connection before blocking max.in.flight.requests.per.connection: "${TB_KAFKA_MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION:5}" + # The total bytes of memory the producer can use to buffer records waiting to be sent to the server buffer.memory: "${TB_BUFFER_MEMORY:33554432}" + # The multiple copies of data over the multiple brokers of Kafka replication_factor: "${TB_QUEUE_KAFKA_REPLICATION_FACTOR:1}" + # The maximum delay between invocations of poll() method when using consumer group management. This places an upper bound on the amount of time that the consumer can be idle before fetching more records max_poll_interval_ms: "${TB_QUEUE_KAFKA_MAX_POLL_INTERVAL_MS:300000}" + # The maximum number of records returned in a single call of poll() method max_poll_records: "${TB_QUEUE_KAFKA_MAX_POLL_RECORDS:8192}" + # The maximum amount of data per-partition the server will return. Records are fetched in batches by the consumer max_partition_fetch_bytes: "${TB_QUEUE_KAFKA_MAX_PARTITION_FETCH_BYTES:16777216}" + # The maximum amount of data the server will return. Records are fetched in batches by the consumer fetch_max_bytes: "${TB_QUEUE_KAFKA_FETCH_MAX_BYTES:134217728}" request.timeout.ms: "${TB_QUEUE_KAFKA_REQUEST_TIMEOUT_MS:30000}" # (30 seconds) # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms session.timeout.ms: "${TB_QUEUE_KAFKA_SESSION_TIMEOUT_MS:10000}" # (10 seconds) # refer to https://docs.confluent.io/platform/current/installation/configuration/consumer-configs.html#consumerconfigs_session.timeout.ms + auto_offset_reset: "${TB_QUEUE_KAFKA_AUTO_OFFSET_RESET:earliest}" # earliest, latest or none + # Enable/Disable using of Confluent Cloud use_confluent_cloud: "${TB_QUEUE_KAFKA_USE_CONFLUENT_CLOUD:false}" confluent: + # The endpoint identification algorithm used by clients to validate server hostname. The default value is https ssl.algorithm: "${TB_QUEUE_KAFKA_CONFLUENT_SSL_ALGORITHM:https}" + # The mechanism used to authenticate Schema Registry requests. SASL/PLAIN should only be used with TLS/SSL as a transport layer to ensure that clear passwords are not transmitted on the wire without encryption sasl.mechanism: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_MECHANISM:PLAIN}" + # Using JAAS Configuration for specifying multiple SASL mechanisms on a broker sasl.config: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_JAAS_CONFIG:org.apache.kafka.common.security.plain.PlainLoginModule required username=\"CLUSTER_API_KEY\" password=\"CLUSTER_API_SECRET\";}" + # Protocol used to communicate with brokers. Valid values are: PLAINTEXT, SSL, SASL_PLAINTEXT, SASL_SSL security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}" # Key-value properties for Kafka consumer per specific topic, e.g. tb_ota_package is a topic name for ota, tb_rule_engine.sq is a topic name for default SequentialByOriginator queue. # Check TB_QUEUE_CORE_OTA_TOPIC and TB_QUEUE_RE_SQ_TOPIC params consumer-properties-per-topic: tb_ota_package: + # Key-value properties for Kafka consumer per specific topic, e.g. tb_ota_package is a topic name for ota, tb_rule_engine.sq is a topic name for default SequentialByOriginator queue. Check TB_QUEUE_CORE_OTA_TOPIC and TB_QUEUE_RE_SQ_TOPIC params - key: max.poll.records + # Example of specific consumer properties value per topic value: "${TB_QUEUE_KAFKA_OTA_MAX_POLL_RECORDS:10}" tb_version_control: + # Example of specific consumer properties value per topic for VC - key: max.poll.interval.ms + # Example of specific consumer properties value per topic for VC value: "${TB_QUEUE_KAFKA_VC_MAX_POLL_INTERVAL_MS:600000}" # tb_rule_engine.sq: # - key: max.poll.records # value: "${TB_QUEUE_KAFKA_SQ_MAX_POLL_RECORDS:1024}" other-inline: "${TB_QUEUE_KAFKA_OTHER_PROPERTIES:}" # In this section you can specify custom parameters (semicolon separated) for Kafka consumer/producer/admin # Example "metrics.recording.level:INFO;metrics.sample.window.ms:30000" - other: # DEPRECATED. In this section you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside + other: # DEPRECATED. In this section, you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside # - key: "request.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms # value: "${TB_QUEUE_KAFKA_REQUEST_TIMEOUT_MS:30000}" # (30 seconds) # - key: "session.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/consumer-configs.html#consumerconfigs_session.timeout.ms # value: "${TB_QUEUE_KAFKA_SESSION_TIMEOUT_MS:10000}" # (10 seconds) topic-properties: + # Kafka properties for Rule Engine rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" + # Kafka properties for Core topics core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" + # Kafka properties for Transport Api topics transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}" + # Kafka properties for Notifications topics notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" + # Kafka properties for JS Executor topics js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1}" + # Kafka properties for OTA updates topic ota-updates: "${TB_QUEUE_KAFKA_OTA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}" - version-control: "${TB_QUEUE_KAFKA_VC_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}" + # Kafka properties for Version Control topic + version-control: "${TB_QUEUE_KAFKA_VC_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" consumer-stats: + # Prints lag between consumer group offset and last messages offset in Kafka topics enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}" + # Statistics printing interval for Kafka's consumer-groups stats print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}" + # Time to wait for the stats-loading requests to Kafka to finish kafka-response-timeout-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_RESPONSE_TIMEOUT_MS:1000}" aws_sqs: + # Use the default credentials provider for AWS SQS use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" + # Access key ID from AWS IAM user access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" + # Secret access key from AWS IAM user secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}" + # Region from AWS account region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}" + # Number of threads per each AWS SQS queue in consumer threads_per_topic: "${TB_QUEUE_AWS_SQS_THREADS_PER_TOPIC:1}" queue-properties: + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds rule-engine: "${TB_QUEUE_AWS_SQS_RE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800 js-executor: "${TB_QUEUE_AWS_SQS_JE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds ota-updates: "${TB_QUEUE_AWS_SQS_OTA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds version-control: "${TB_QUEUE_AWS_SQS_VC_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds pubsub: + # Project ID from Google Cloud project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" + # API Credentials in JSON format service_account: "${TB_QUEUE_PUBSUB_SERVICE_ACCOUNT:YOUR_SERVICE_ACCOUNT}" - max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" #in bytes + # Message size for PubSub queue.Value in bytes + max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" + # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" + # Thread pool size for pubsub queue executor provider. If set to 0 - default pubsub executor provider value will be used (5 * number of available processors) + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" queue-properties: + # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # Pub/Sub properties for Core subscribers, messages which will commit after ackDeadlineInSec period can be consumed again core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # Pub/Sub properties for Transport API subscribers, messages which will commit after ackDeadlineInSec period can be consumed again transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # Pub/Sub properties for Version Control subscribers, messages which will commit after ackDeadlineInSec period can be consumed again notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # PubSub queue properties js-executor: "${TB_QUEUE_PUBSUB_JE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # Pub/Sub properties for Transport Api subscribers, messages which will commit after ackDeadlineInSec period can be consumed again version-control: "${TB_QUEUE_PUBSUB_VC_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" service_bus: + # Azure namespace namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" + # Azure Service Bus Shared Access Signatures key name sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" + # Azure Service Bus Shared Access Signatures key sas_key: "${TB_QUEUE_SERVICE_BUS_SAS_KEY:YOUR_SAS_KEY}" + # Number of messages per a consumer max_messages: "${TB_QUEUE_SERVICE_BUS_MAX_MESSAGES:1000}" queue-properties: + # Azure Service Bus properties for Rule Engine queues rule-engine: "${TB_QUEUE_SERVICE_BUS_RE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus properties for Core queues core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus properties for Transport Api queues transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus properties for Notification queues notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus queue properties js-executor: "${TB_QUEUE_SERVICE_BUS_JE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus properties for Version Control queues version-control: "${TB_QUEUE_SERVICE_BUS_VC_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" rabbitmq: + # By default empty exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" + # RabbitMQ host used to establish connection host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" + # RabbitMQ host used to establish a connection port: "${TB_QUEUE_RABBIT_MQ_PORT:5672}" + # Virtual hosts provide logical grouping and separation of resources virtual_host: "${TB_QUEUE_RABBIT_MQ_VIRTUAL_HOST:/}" + # Username for RabbitMQ user account username: "${TB_QUEUE_RABBIT_MQ_USERNAME:YOUR_USERNAME}" + # User password for RabbitMQ user account password: "${TB_QUEUE_RABBIT_MQ_PASSWORD:YOUR_PASSWORD}" + # Network connection between clients and RabbitMQ nodes can fail. RabbitMQ Java client supports automatic recovery of connections and topology (queues, exchanges, bindings, and consumers) automatic_recovery_enabled: "${TB_QUEUE_RABBIT_MQ_AUTOMATIC_RECOVERY_ENABLED:false}" + # The connection timeout for the RabbitMQ connection factory connection_timeout: "${TB_QUEUE_RABBIT_MQ_CONNECTION_TIMEOUT:60000}" + # RabbitMQ has a timeout for connection handshake. When clients run in heavily constrained environments, it may be necessary to increase the timeout handshake_timeout: "${TB_QUEUE_RABBIT_MQ_HANDSHAKE_TIMEOUT:10000}" queue-properties: + # RabbitMQ properties for Rule Engine queues rule-engine: "${TB_QUEUE_RABBIT_MQ_RE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ properties for Core queues core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ properties for Transport API queues transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ properties for Notification queues notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ queue properties js-executor: "${TB_QUEUE_RABBIT_MQ_JE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ properties for Version Control queues version-control: "${TB_QUEUE_RABBIT_MQ_VC_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" partitions: hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 transport_api: + # Topic used to consume api requests from transport microservices requests_topic: "${TB_QUEUE_TRANSPORT_API_REQUEST_TOPIC:tb_transport.api.requests}" + # Topic used to produce api responses to transport microservices responses_topic: "${TB_QUEUE_TRANSPORT_API_RESPONSE_TOPIC:tb_transport.api.responses}" + # Maximum pending api requests from transport microservices to be handled by server max_pending_requests: "${TB_QUEUE_TRANSPORT_MAX_PENDING_REQUESTS:10000}" + # Maximum timeout in milliseconds to handle api request from transport microservice by server max_requests_timeout: "${TB_QUEUE_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}" + # Amount of threads used to invoke callbacks max_callback_threads: "${TB_QUEUE_TRANSPORT_MAX_CALLBACK_THREADS:100}" + # Amount of threads used for transport API requests + max_core_handler_threads: "${TB_QUEUE_TRANSPORT_MAX_CORE_HANDLER_THREADS:16}" + # Interval in milliseconds to poll api requests from transport microservices request_poll_interval: "${TB_QUEUE_TRANSPORT_REQUEST_POLL_INTERVAL_MS:25}" + # Interval in milliseconds to poll api response from transport microservices response_poll_interval: "${TB_QUEUE_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}" core: + # Default topic name of Kafka, RabbitMQ, etc. queue topic: "${TB_QUEUE_CORE_TOPIC:tb_core}" + # Interval in milliseconds to poll messages by Core microservices poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" + # Amount of partitions used by Core microservices partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" + # Timeout for processing a message pack by Core microservices pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:2000}" ota: + # Default topic name for OTA updates topic: "${TB_QUEUE_CORE_OTA_TOPIC:tb_ota_package}" + # The interval of processing the OTA updates for devices. Used to avoid any harm to the network due to many parallel OTA updates pack-interval-ms: "${TB_QUEUE_CORE_OTA_PACK_INTERVAL_MS:60000}" + # The size of OTA updates notifications fetched from the queue. The queue stores pairs of firmware and device ids pack-size: "${TB_QUEUE_CORE_OTA_PACK_SIZE:100}" + # Stats topic name for queue Kafka, RabbitMQ, etc. usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}" stats: + # Enable/disable statistics for Core microservices enabled: "${TB_QUEUE_CORE_STATS_ENABLED:true}" + # Statistics printing interval for Core microservices print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:60000}" vc: + # Default topic name for Kafka, RabbitMQ, etc. topic: "${TB_QUEUE_VC_TOPIC:tb_version_control}" + # Number of partitions to associate with this queue. Used for scaling the number of messages that can be processed in parallel partitions: "${TB_QUEUE_VC_PARTITIONS:10}" + # Interval in milliseconds between polling of the messages if no new messages arrive poll-interval: "${TB_QUEUE_VC_INTERVAL_MS:25}" - pack-processing-timeout: "${TB_QUEUE_VC_PACK_PROCESSING_TIMEOUT_MS:60000}" - request-timeout: "${TB_QUEUE_VC_REQUEST_TIMEOUT:60000}" + # Timeout before retrying all failed and timed-out messages from the processing pack + pack-processing-timeout: "${TB_QUEUE_VC_PACK_PROCESSING_TIMEOUT_MS:180000}" + # Timeout for a request to VC-executor (for a request for the version of the entity, for a commit charge, etc.) + request-timeout: "${TB_QUEUE_VC_REQUEST_TIMEOUT:180000}" + # Queue settings for Kafka, RabbitMQ, etc. Limit for single message size msg-chunk-size: "${TB_QUEUE_VC_MSG_CHUNK_SIZE:250000}" js: # JS Eval request topic @@ -1186,19 +1562,26 @@ queue: # JS response poll interval response_poll_interval: "${REMOTE_JS_RESPONSE_POLL_INTERVAL_MS:25}" rule-engine: + # Deprecated. It will be removed in the nearest releases topic: "${TB_QUEUE_RULE_ENGINE_TOPIC:tb_rule_engine}" + # Interval in milliseconds to poll messages by Rule Engine poll-interval: "${TB_QUEUE_RULE_ENGINE_POLL_INTERVAL_MS:25}" + # Timeout for processing a message pack of Rule Engine pack-processing-timeout: "${TB_QUEUE_RULE_ENGINE_PACK_PROCESSING_TIMEOUT_MS:2000}" stats: + # Enable/disable statistics for Rule Engine enabled: "${TB_QUEUE_RULE_ENGINE_STATS_ENABLED:true}" + # Statistics printing interval for Rule Engine print-interval-ms: "${TB_QUEUE_RULE_ENGINE_STATS_PRINT_INTERVAL_MS:60000}" + # Max length of the error message that is printed by statistics + max-error-message-length: "${TB_QUEUE_RULE_ENGINE_MAX_ERROR_MESSAGE_LENGTH:4096}" queues: - - name: "${TB_QUEUE_RE_MAIN_QUEUE_NAME:Main}" - topic: "${TB_QUEUE_RE_MAIN_TOPIC:tb_rule_engine.main}" - poll-interval: "${TB_QUEUE_RE_MAIN_POLL_INTERVAL_MS:25}" - partitions: "${TB_QUEUE_RE_MAIN_PARTITIONS:10}" - consumer-per-partition: "${TB_QUEUE_RE_MAIN_CONSUMER_PER_PARTITION:true}" - pack-processing-timeout: "${TB_QUEUE_RE_MAIN_PACK_PROCESSING_TIMEOUT_MS:2000}" + - name: "${TB_QUEUE_RE_MAIN_QUEUE_NAME:Main}" # queue name + topic: "${TB_QUEUE_RE_MAIN_TOPIC:tb_rule_engine.main}" # queue topic + poll-interval: "${TB_QUEUE_RE_MAIN_POLL_INTERVAL_MS:25}" # poll interval + partitions: "${TB_QUEUE_RE_MAIN_PARTITIONS:10}" # number queue partitions + consumer-per-partition: "${TB_QUEUE_RE_MAIN_CONSUMER_PER_PARTITION:true}" # if true - use for each customer different partition + pack-processing-timeout: "${TB_QUEUE_RE_MAIN_PACK_PROCESSING_TIMEOUT_MS:2000}" # Timeout for processing a message pack submit-strategy: type: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_TYPE:BURST}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL # For BATCH only @@ -1207,15 +1590,15 @@ queue: type: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_TYPE:SKIP_ALL_FAILURES}" # SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT # For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited - failure-percentage: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages; + failure-percentage: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less than X percentage of messages; pause-between-retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_RETRY_PAUSE:3}" # Time in seconds to wait in consumer thread before retries; max-pause-between-retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_MAX_RETRY_PAUSE:3}" # Max allowed time in seconds for pause between retries. - - name: "${TB_QUEUE_RE_HP_QUEUE_NAME:HighPriority}" - topic: "${TB_QUEUE_RE_HP_TOPIC:tb_rule_engine.hp}" - poll-interval: "${TB_QUEUE_RE_HP_POLL_INTERVAL_MS:25}" - partitions: "${TB_QUEUE_RE_HP_PARTITIONS:10}" - consumer-per-partition: "${TB_QUEUE_RE_HP_CONSUMER_PER_PARTITION:true}" - pack-processing-timeout: "${TB_QUEUE_RE_HP_PACK_PROCESSING_TIMEOUT_MS:2000}" + - name: "${TB_QUEUE_RE_HP_QUEUE_NAME:HighPriority}" # queue name + topic: "${TB_QUEUE_RE_HP_TOPIC:tb_rule_engine.hp}" # queue topic + poll-interval: "${TB_QUEUE_RE_HP_POLL_INTERVAL_MS:25}" # poll interval + partitions: "${TB_QUEUE_RE_HP_PARTITIONS:10}" # number queue partitions + consumer-per-partition: "${TB_QUEUE_RE_HP_CONSUMER_PER_PARTITION:true}" # if true - use for each customer different partition + pack-processing-timeout: "${TB_QUEUE_RE_HP_PACK_PROCESSING_TIMEOUT_MS:2000}" # Timeout for processing a message pack submit-strategy: type: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_TYPE:BURST}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL # For BATCH only @@ -1224,15 +1607,15 @@ queue: type: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT # For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRIES:0}" # Number of retries, 0 is unlimited - failure-percentage: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages; + failure-percentage: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less than X percentage of messages; pause-between-retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRY_PAUSE:5}" # Time in seconds to wait in consumer thread before retries; max-pause-between-retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_MAX_RETRY_PAUSE:5}" # Max allowed time in seconds for pause between retries. - - name: "${TB_QUEUE_RE_SQ_QUEUE_NAME:SequentialByOriginator}" - topic: "${TB_QUEUE_RE_SQ_TOPIC:tb_rule_engine.sq}" - poll-interval: "${TB_QUEUE_RE_SQ_POLL_INTERVAL_MS:25}" - partitions: "${TB_QUEUE_RE_SQ_PARTITIONS:10}" - consumer-per-partition: "${TB_QUEUE_RE_SQ_CONSUMER_PER_PARTITION:true}" - pack-processing-timeout: "${TB_QUEUE_RE_SQ_PACK_PROCESSING_TIMEOUT_MS:2000}" + - name: "${TB_QUEUE_RE_SQ_QUEUE_NAME:SequentialByOriginator}" # queue name + topic: "${TB_QUEUE_RE_SQ_TOPIC:tb_rule_engine.sq}" # queue topic + poll-interval: "${TB_QUEUE_RE_SQ_POLL_INTERVAL_MS:25}" # poll interval + partitions: "${TB_QUEUE_RE_SQ_PARTITIONS:10}" # number queue partitions + consumer-per-partition: "${TB_QUEUE_RE_SQ_CONSUMER_PER_PARTITION:true}" # if true - use for each customer different partition + pack-processing-timeout: "${TB_QUEUE_RE_SQ_PACK_PROCESSING_TIMEOUT_MS:2000}" # Timeout for processing a message pack submit-strategy: type: "${TB_QUEUE_RE_SQ_SUBMIT_STRATEGY_TYPE:SEQUENTIAL_BY_ORIGINATOR}" # BURST, BATCH, SEQUENTIAL_BY_ORIGINATOR, SEQUENTIAL_BY_TENANT, SEQUENTIAL # For BATCH only @@ -1241,50 +1624,73 @@ queue: type: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT # For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited - failure-percentage: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages; + failure-percentage: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less than X percentage of messages; pause-between-retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRY_PAUSE:5}" # Time in seconds to wait in consumer thread before retries; max-pause-between-retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_MAX_RETRY_PAUSE:5}" # Max allowed time in seconds for pause between retries. + # After a queue is deleted (or the profile's isolation option was disabled), Rule Engine will continue reading related topics during this period before deleting the actual topics + topic-deletion-delay: "${TB_QUEUE_RULE_ENGINE_TOPIC_DELETION_DELAY_SEC:15}" + # Size of the thread pool that handles such operations as partition changes, config updates, queue deletion + management-thread-pool-size: "${TB_QUEUE_RULE_ENGINE_MGMT_THREAD_POOL_SIZE:12}" transport: - # For high priority notifications that require minimum latency and processing time + # For high-priority notifications that require minimum latency and processing time notifications_topic: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_TOPIC:tb_transport.notifications}" + # Interval in milliseconds to poll messages poll_interval: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_POLL_INTERVAL_MS:25}" +# Event configuration parameters event: debug: + # Maximum number of symbols per debug event. The event content will be truncated if needed max-symbols: "${TB_MAX_DEBUG_EVENT_SYMBOLS:4096}" +# General service parameters service: type: "${TB_SERVICE_TYPE:monolith}" # monolith or tb-core or tb-rule-engine # Unique id for this service (autogenerated if empty) id: "${TB_SERVICE_ID:}" + rule_engine: + # Comma-separated list of tenant profile ids assigned to this Rule Engine. + # This Rule Engine will only be responsible for tenants with these profiles (in case 'isolation' option is enabled in the profile). + assigned_tenant_profiles: "${TB_RULE_ENGINE_ASSIGNED_TENANT_PROFILES:}" + pubsub: + # Thread pool size for pubsub rule node executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) + executor_thread_pool_size: "${TB_RULE_ENGINE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" +# Metrics parameters metrics: # Enable/disable actuator metrics. enabled: "${METRICS_ENABLED:false}" timer: # Metrics percentiles returned by actuator for timer metrics. List of double values (divided by ,). percentiles: "${METRICS_TIMER_PERCENTILES:0.5}" + system_info: + # Persist frequency of system info (CPU, memory usage, etc.) in seconds + persist_frequency: "${METRICS_SYSTEM_INFO_PERSIST_FREQUENCY_SECONDS:60}" + # TTL in days for system info timeseries + ttl: "${METRICS_SYSTEM_INFO_TTL_DAYS:7}" +# Version control parameters vc: # Pool size for handling export tasks - thread_pool_size: "${TB_VC_POOL_SIZE:2}" + thread_pool_size: "${TB_VC_POOL_SIZE:6}" git: # Pool size for handling the git IO operations io_pool_size: "${TB_VC_GIT_POOL_SIZE:3}" + # Default storing repository path repositories-folder: "${TB_VC_GIT_REPOSITORIES_FOLDER:${java.io.tmpdir}/repositories}" +# Notification system parameters notification_system: + # Specify thread pool size for Notification System processing notification rules and notification sending. Recommend value <= 10 thread_pool_size: "${TB_NOTIFICATION_SYSTEM_THREAD_POOL_SIZE:10}" rules: - trigger_types_configs: - NEW_PLATFORM_VERSION: - # In milliseconds, infinitely by default - deduplication_duration: "${NEW_PLATFORM_VERSION_NOTIFICATION_RULE_DEDUPLICATION_DURATION:0}" + # Semicolon-separated deduplication durations (in millis) for trigger types. Format: 'NotificationRuleTriggerType1:123;NotificationRuleTriggerType2:456' + deduplication_durations: "${TB_NOTIFICATION_RULES_DEDUPLICATION_DURATIONS:NEW_PLATFORM_VERSION:0;RATE_LIMITS:14400000;}" +# General management parameters management: endpoints: web: exposure: # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). - include: '${METRICS_ENDPOINTS_EXPOSE:info}' - + include: '${METRICS_ENDPOINTS_EXPOSE:info}' \ No newline at end of file diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java index 5809ab5e42..1729285034 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java @@ -52,8 +52,8 @@ public abstract class AbstractControllerTest extends AbstractNotifyEntityTest { @LocalServerPort protected int wsPort; - private volatile TbTestWebSocketClient wsClient; // lazy - private volatile TbTestWebSocketClient anotherWsClient; // lazy + protected volatile TbTestWebSocketClient wsClient; // lazy + protected volatile TbTestWebSocketClient anotherWsClient; // lazy public TbTestWebSocketClient getWsClient() { if (wsClient == null) { @@ -101,8 +101,15 @@ public abstract class AbstractControllerTest extends AbstractNotifyEntityTest { } protected TbTestWebSocketClient buildAndConnectWebSocketClient() throws URISyntaxException, InterruptedException { - TbTestWebSocketClient wsClient = new TbTestWebSocketClient(new URI(WS_URL + wsPort + "/api/ws/plugins/telemetry?token=" + token)); + return buildAndConnectWebSocketClient("/api/ws"); + } + + protected TbTestWebSocketClient buildAndConnectWebSocketClient(String path) throws URISyntaxException, InterruptedException { + TbTestWebSocketClient wsClient = new TbTestWebSocketClient(new URI(WS_URL + wsPort + path)); assertThat(wsClient.connectBlocking(TIMEOUT, TimeUnit.SECONDS)).isTrue(); + if (!path.contains("token=")) { + wsClient.authenticate(token); + } return wsClient; } diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java index d5eabdff2a..d9bc42db9a 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java @@ -74,13 +74,24 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.reset(tbClusterService, auditLogService); } + protected void testNotifyAssignUnassignEntityAllOneTime(HasName entity, EntityId entityId, EntityId originatorId, + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, ActionType actionTypeEdge, Object... additionalInfo) { + int cntTime = 1; + testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionTypeEdge, cntTime); + testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); + ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime); + Mockito.reset(tbClusterService, auditLogService); + } + protected void testNotifyEntityAllOneTimeRelation(EntityRelation relation, TenantId tenantId, CustomerId customerId, UserId userId, String userName, ActionType actionType, Object... additionalInfo) { int cntTime = 1; Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId), Mockito.isNull(), Mockito.isNull(), Mockito.any(), Mockito.eq(EdgeEventType.RELATION), - Mockito.eq(edgeTypeByActionType(actionType))); + Mockito.eq(edgeTypeByActionType(actionType)), Mockito.any()); ArgumentMatcher matcherOriginatorId = argument -> argument.equals(relation.getTo()); ArgumentMatcher matcherEntityClassEquals = Objects::isNull; ArgumentMatcher matcherCustomerId = customerId == null ? @@ -100,7 +111,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { ActionType actionType, int cntTime) { Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId), Mockito.isNull(), Mockito.isNull(), Mockito.any(), Mockito.eq(EdgeEventType.RELATION), - Mockito.eq(edgeTypeByActionType(actionType))); + Mockito.eq(edgeTypeByActionType(actionType)), Mockito.any()); ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(relation.getFrom().getClass()); ArgumentMatcher matcherEntityClassEquals = Objects::isNull; ArgumentMatcher matcherCustomerId = customerId == null ? @@ -115,9 +126,9 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { protected void testNotifyEntityAllOneTimeLogEntityActionEntityEqClass(HasName entity, EntityId entityId, EntityId originatorId, TenantId tenantId, CustomerId customerId, UserId userId, String userName, - ActionType actionType, Object... additionalInfo) { + ActionType actionType, ActionType actionTypeEdge, Object... additionalInfo) { int cntTime = 1; - testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); + testNotificationMsgToEdgeServiceTime(entityId, tenantId, actionTypeEdge, cntTime); testLogEntityActionEntityEqClass(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, entity, cntTime); @@ -163,10 +174,10 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { protected void testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(HasName entity, HasName originator, TenantId tenantId, CustomerId customerId, UserId userId, String userName, - ActionType actionType, ActionType actionTypeEdge, + ActionType actionType, int cntTime, int cntTimeEdge, int cntTimeRuleEngine, Object... additionalInfo) { EntityId originatorId = createEntityId_NULL_UUID(originator); - testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionTypeEdge, cntTimeEdge); + testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionType, cntTimeEdge); ArgumentMatcher matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); ArgumentMatcher matcherCustomerId = customerId == null ? @@ -307,13 +318,13 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { private void testNotificationMsgToEdgeServiceNeverWithActionType(EntityId entityId, ActionType actionType) { EdgeEventActionType edgeEventActionType = ActionType.CREDENTIALS_UPDATED.equals(actionType) ? EdgeEventActionType.CREDENTIALS_UPDATED : edgeTypeByActionType(actionType); - Mockito.verify(tbClusterService, never()).sendNotificationMsgToEdge(Mockito.any(), - Mockito.any(), Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any(), Mockito.eq(edgeEventActionType)); + Mockito.verify(tbClusterService, never()).sendNotificationMsgToEdge(Mockito.any(), Mockito.any(), + Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any(), Mockito.eq(edgeEventActionType), Mockito.any()); } private void testNotificationMsgToEdgeServiceNever(EntityId entityId) { - Mockito.verify(tbClusterService, never()).sendNotificationMsgToEdge(Mockito.any(), - Mockito.any(), Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any(), Mockito.any()); + Mockito.verify(tbClusterService, never()).sendNotificationMsgToEdge(Mockito.any(), Mockito.any(), + Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); } private void testLogEntityActionNever(EntityId entityId, HasName entity) { @@ -347,13 +358,13 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { argument -> argument.getClass().equals(entityId.getClass()); Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId), Mockito.any(), Mockito.argThat(matcherEntityId), Mockito.any(), Mockito.isNull(), - Mockito.eq(edgeEventActionType)); + Mockito.eq(edgeEventActionType), Mockito.any()); } private void testSendNotificationMsgToEdgeServiceTimeEntityEqAny(TenantId tenantId, ActionType actionType, int cntTime) { Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId), Mockito.any(), Mockito.any(EntityId.class), Mockito.any(), Mockito.isNull(), - Mockito.eq(edgeTypeByActionType(actionType))); + Mockito.eq(edgeTypeByActionType(actionType)), Mockito.any()); } protected void testBroadcastEntityStateChangeEventTime(EntityId entityId, TenantId tenantId, int cntTime) { diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java index 49e3550a1e..656936b175 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java @@ -25,13 +25,13 @@ import org.thingsboard.server.common.data.event.EventType; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.dao.rule.RuleChainService; -import java.io.IOException; import java.util.function.Predicate; /** @@ -82,8 +82,8 @@ public abstract class AbstractRuleEngineControllerTest extends AbstractControlle } } - protected Predicate filterByCustomEvent() { - return event -> event.getBody().get("msgType").textValue().equals("CUSTOM"); + protected Predicate filterByPostTelemetryEventType() { + return event -> event.getBody().get("msgType").textValue().equals(TbMsgType.POST_TELEMETRY_REQUEST.name()); } } diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index 7f57fbbb3f..76186e6566 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -25,6 +25,7 @@ import io.jsonwebtoken.Header; import io.jsonwebtoken.Jwt; import io.jsonwebtoken.Jwts; import lombok.extern.slf4j.Slf4j; +import org.apache.http.HttpStatus; import org.awaitility.Awaitility; import org.hamcrest.Matcher; import org.hibernate.exception.ConstraintViolationException; @@ -65,6 +66,7 @@ import org.thingsboard.server.actors.TbEntityActorId; import org.thingsboard.server.actors.device.DeviceActor; import org.thingsboard.server.actors.device.DeviceActorMessageProcessor; import org.thingsboard.server.actors.device.SessionInfo; +import org.thingsboard.server.actors.device.ToDeviceRpcRequestMetadata; import org.thingsboard.server.actors.service.DefaultActorService; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; @@ -95,6 +97,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.PageData; @@ -175,6 +178,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { * and {@link org.springframework.mock.web.MockAsyncContext#getTimeout()} */ private static final long DEFAULT_TIMEOUT = -1L; + private static final int CLEANUP_TENANT_RETRIES_COUNT = 3; protected MediaType contentType = MediaType.APPLICATION_JSON; @@ -188,7 +192,9 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { protected String username; protected TenantId tenantId; + protected TenantProfileId tenantProfileId; protected UserId tenantAdminUserId; + protected User tenantAdminUser; protected CustomerId tenantAdminCustomerId; protected CustomerId customerId; protected TenantId differentTenantId; @@ -269,15 +275,16 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class); Assert.assertNotNull(savedTenant); tenantId = savedTenant.getId(); + tenantProfileId = savedTenant.getTenantProfileId(); - User tenantAdmin = new User(); - tenantAdmin.setAuthority(Authority.TENANT_ADMIN); - tenantAdmin.setTenantId(tenantId); - tenantAdmin.setEmail(TENANT_ADMIN_EMAIL); + tenantAdminUser = new User(); + tenantAdminUser.setAuthority(Authority.TENANT_ADMIN); + tenantAdminUser.setTenantId(tenantId); + tenantAdminUser.setEmail(TENANT_ADMIN_EMAIL); - tenantAdmin = createUserAndLogin(tenantAdmin, TENANT_ADMIN_PASSWORD); - tenantAdminUserId = tenantAdmin.getId(); - tenantAdminCustomerId = tenantAdmin.getCustomerId(); + tenantAdminUser = createUserAndLogin(tenantAdminUser, TENANT_ADMIN_PASSWORD); + tenantAdminUserId = tenantAdminUser.getId(); + tenantAdminCustomerId = tenantAdminUser.getCustomerId(); Customer customer = new Customer(); customer.setTitle("Customer"); @@ -325,10 +332,8 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { log.debug("Executing web test teardown"); loginSysAdmin(); - doDelete("/api/tenant/" + tenantId.getId().toString()) - .andExpect(status().isOk()); + deleteTenant(tenantId); deleteDifferentTenant(); - verifyNoTenantsLeft(); tenantProfileService.deleteTenantProfiles(TenantId.SYS_TENANT_ID); @@ -336,7 +341,34 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { log.info("Executed web test teardown"); } - void verifyNoTenantsLeft() throws Exception { + private void verifyNoTenantsLeft() throws Exception { + List loadedTenants = getAllTenants(); + if (!loadedTenants.isEmpty()) { + loadedTenants.forEach(tenant -> deleteTenant(tenant.getId())); + loadedTenants = getAllTenants(); + } + assertThat(loadedTenants).as("All tenants expected to be deleted, but some tenants left in the database").isEmpty(); + } + + private void deleteTenant(TenantId tenantId) { + int status = 0; + int retries = 0; + while (status != HttpStatus.SC_OK && retries < CLEANUP_TENANT_RETRIES_COUNT) { + retries++; + try { + status = doDelete("/api/tenant/" + tenantId.getId().toString()) + .andReturn().getResponse().getStatus(); + if (status != HttpStatus.SC_OK) { + log.warn("Tenant deletion failed, tenantId: {}", tenantId.getId().toString()); + Thread.sleep(1000L); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + private List getAllTenants() throws Exception { List loadedTenants = new ArrayList<>(); PageLink pageLink = new PageLink(10); PageData pageData; @@ -348,8 +380,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { pageLink = pageLink.nextPageLink(); } } while (pageData.hasNext()); - - assertThat(loadedTenants).as("All tenants expected to be deleted, but some tenants left in the database").isEmpty(); + return loadedTenants; } protected void loginSysAdmin() throws Exception { @@ -460,8 +491,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { protected void deleteDifferentTenant() throws Exception { if (savedDifferentTenant != null) { loginSysAdmin(); - doDelete("/api/tenant/" + savedDifferentTenant.getId().getId().toString()) - .andExpect(status().isOk()); + deleteTenant(savedDifferentTenant.getId()); savedDifferentTenant = null; } } @@ -772,7 +802,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { return readResponse(doPostAsync(urlTemplate, content, DEFAULT_TIMEOUT, params).andExpect(resultMatcher), responseClass); } - protected T doPut(String urlTemplate, T content, Class responseClass, String... params) { + protected T doPut(String urlTemplate, Object content, Class responseClass, String... params) { try { return readResponse(doPut(urlTemplate, content, params).andExpect(status().isOk()), responseClass); } catch (Exception e) { @@ -792,6 +822,10 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { return readResponse(doDelete(urlTemplate, params).andExpect(status().isOk()), responseClass); } + protected T doDeleteAsync(String urlTemplate, Class responseClass, String... params) throws Exception { + return readResponse(doDeleteAsync(urlTemplate, DEFAULT_TIMEOUT, params).andExpect(status().isOk()), responseClass); + } + protected ResultActions doPost(String urlTemplate, String... params) throws Exception { MockHttpServletRequestBuilder postRequest = post(urlTemplate); setJwtToken(postRequest); @@ -824,6 +858,15 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { return mockMvc.perform(deleteRequest); } + protected ResultActions doDeleteAsync(String urlTemplate, Long timeout, String... params) throws Exception { + MockHttpServletRequestBuilder deleteRequest = delete(urlTemplate, params); + setJwtToken(deleteRequest); +// populateParams(deleteRequest, params); + MvcResult result = mockMvc.perform(deleteRequest).andReturn(); + result.getAsyncResult(timeout); + return mockMvc.perform(asyncDispatch(result)); + } + protected void populateParams(MockHttpServletRequestBuilder request, String... params) { if (params != null && params.length > 0) { Assert.assertEquals(0, params.length % 2); @@ -991,6 +1034,15 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { }); } + protected void awaitForDeviceActorToProcessAllRpcResponses(DeviceId deviceId) { + DeviceActorMessageProcessor processor = getDeviceActorProcessor(deviceId); + Map toDeviceRpcPendingMap = (Map) ReflectionTestUtils.getField(processor, "toDeviceRpcPendingMap"); + Awaitility.await("Device actor pending map is empty").atMost(5, TimeUnit.SECONDS).until(() -> { + log.warn("device {}, toDeviceRpcPendingMap.size() == {}", deviceId, toDeviceRpcPendingMap.size()); + return toDeviceRpcPendingMap.isEmpty(); + }); + } + protected static String getMapName(FeatureType featureType) { switch (featureType) { case ATTRIBUTES: @@ -1012,13 +1064,20 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { return (DeviceActorMessageProcessor) ReflectionTestUtils.getField(actor, "processor"); } - protected void updateDefaultTenantProfile(Consumer updater) throws ThingsboardException { - TenantProfile tenantProfile = tenantProfileService.findDefaultTenantProfile(TenantId.SYS_TENANT_ID); - TenantProfileData profileData = tenantProfile.getProfileData(); - DefaultTenantProfileConfiguration profileConfiguration = (DefaultTenantProfileConfiguration) profileData.getConfiguration(); - updater.accept(profileConfiguration); - tenantProfile.setProfileData(profileData); - tbTenantProfileService.save(TenantId.SYS_TENANT_ID, tenantProfile, null); + protected void updateDefaultTenantProfileConfig(Consumer updater) throws ThingsboardException { + updateDefaultTenantProfile(tenantProfile -> { + TenantProfileData profileData = tenantProfile.getProfileData(); + DefaultTenantProfileConfiguration profileConfiguration = (DefaultTenantProfileConfiguration) profileData.getConfiguration(); + updater.accept(profileConfiguration); + tenantProfile.setProfileData(profileData); + }); + } + + protected void updateDefaultTenantProfile(Consumer updater) throws ThingsboardException { + TenantProfile oldTenantProfile = tenantProfileService.findDefaultTenantProfile(TenantId.SYS_TENANT_ID); + TenantProfile tenantProfile = JacksonUtil.clone(oldTenantProfile); + updater.accept(tenantProfile); + tbTenantProfileService.save(TenantId.SYS_TENANT_ID, tenantProfile, oldTenantProfile); } } diff --git a/application/src/test/java/org/thingsboard/server/controller/AlarmCommentControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AlarmCommentControllerTest.java index bf3e7528c9..837ad86962 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AlarmCommentControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AlarmCommentControllerTest.java @@ -136,7 +136,7 @@ public class AlarmCommentControllerTest extends AbstractControllerTest { Assert.assertEquals("true", updatedAlarmComment.getComment().get("edited").asText()); Assert.assertNotNull(updatedAlarmComment.getComment().get("editedOn")); - testLogEntityActionEntityEqClass(alarm, alarm.getId(), tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.UPDATED_COMMENT, 1, savedComment); + testLogEntityActionEntityEqClass(alarm, alarm.getId(), tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.UPDATED_COMMENT, 1, updatedAlarmComment); } @Test diff --git a/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java index 05030f0091..087c2a2106 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java @@ -18,6 +18,7 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; +import org.awaitility.Awaitility; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -31,19 +32,30 @@ import org.springframework.context.annotation.Primary; import org.springframework.test.context.ContextConfiguration; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.alarm.AlarmDao; import org.thingsboard.server.dao.service.DaoSqlTest; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.LinkedList; import java.util.List; +import java.util.stream.Collectors; +import java.util.Objects; +import java.util.concurrent.TimeUnit; import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -127,7 +139,7 @@ public class AlarmControllerTest extends AbstractControllerTest { Assert.assertEquals(AlarmSeverity.MAJOR, updatedAlarm.getSeverity()); AlarmInfo foundAlarm = doGet("/api/alarm/info/" + updatedAlarm.getId(), AlarmInfo.class); - testNotifyEntityAllOneTime(foundAlarm, updatedAlarm.getId(), updatedAlarm.getOriginator(), + testNotifyEntityOneTimeMsgToEdgeServiceNever(foundAlarm, updatedAlarm.getId(), updatedAlarm.getOriginator(), tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.UPDATED); } @@ -144,7 +156,7 @@ public class AlarmControllerTest extends AbstractControllerTest { Assert.assertEquals(AlarmSeverity.MAJOR, updatedAlarm.getSeverity()); AlarmInfo foundAlarm = doGet("/api/alarm/info/" + updatedAlarm.getId(), AlarmInfo.class); - testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(), + testNotifyEntityOneTimeMsgToEdgeServiceNever(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(), tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.UPDATED); alarm = updatedAlarm; @@ -156,8 +168,10 @@ public class AlarmControllerTest extends AbstractControllerTest { Assert.assertEquals(alarm.getAckTs(), updatedAlarm.getAckTs()); foundAlarm = doGet("/api/alarm/info/" + updatedAlarm.getId(), AlarmInfo.class); - testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(), - tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_ACK); + + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(foundAlarm, customerDevice, tenantId, + customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_ACK, 1, 1, 1); + Mockito.reset(tbClusterService, auditLogService); alarm = updatedAlarm; alarm.setCleared(true); @@ -168,8 +182,10 @@ public class AlarmControllerTest extends AbstractControllerTest { Assert.assertEquals(alarm.getClearTs(), updatedAlarm.getClearTs()); foundAlarm = doGet("/api/alarm/info/" + updatedAlarm.getId(), AlarmInfo.class); - testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(), - tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_CLEAR); + + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(foundAlarm, customerDevice, tenantId, + customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_CLEAR, 1, 1, 1); + Mockito.reset(tbClusterService, auditLogService); alarm = updatedAlarm; alarm.setAssigneeId(tenantAdminUserId); @@ -180,8 +196,10 @@ public class AlarmControllerTest extends AbstractControllerTest { Assert.assertEquals(alarm.getAssignTs(), updatedAlarm.getAssignTs()); foundAlarm = doGet("/api/alarm/info/" + updatedAlarm.getId(), AlarmInfo.class); - testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(), - tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_ASSIGNED); + + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(foundAlarm, customerDevice, tenantId, + customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_ASSIGNED, 1, 1, 1); + Mockito.reset(tbClusterService, auditLogService); alarm = updatedAlarm; alarm.setAssigneeId(null); @@ -192,8 +210,11 @@ public class AlarmControllerTest extends AbstractControllerTest { Assert.assertEquals(alarm.getAssignTs(), updatedAlarm.getAssignTs()); foundAlarm = doGet("/api/alarm/info/" + updatedAlarm.getId(), AlarmInfo.class); - testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(), - tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_UNASSIGNED); + + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(foundAlarm, customerDevice, tenantId, + customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_UNASSIGNED, 1, 1, 1); + Mockito.reset(tbClusterService, auditLogService); + } @Test @@ -239,7 +260,7 @@ public class AlarmControllerTest extends AbstractControllerTest { doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isOk()); - testNotifyEntityOneTimeMsgToEdgeServiceNever(new Alarm(alarm), alarm.getId(), alarm.getOriginator(), + testNotifyEntityAllOneTime(new Alarm(alarm), alarm.getId(), alarm.getOriginator(), tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.DELETED); } @@ -252,7 +273,7 @@ public class AlarmControllerTest extends AbstractControllerTest { doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isOk()); - testNotifyEntityOneTimeMsgToEdgeServiceNever(new Alarm(alarm), alarm.getId(), alarm.getOriginator(), + testNotifyEntityAllOneTime(new Alarm(alarm), alarm.getId(), alarm.getOriginator(), tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.DELETED); } @@ -529,6 +550,154 @@ public class AlarmControllerTest extends AbstractControllerTest { tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ALARM_UNASSIGNED); } + @Test + public void testUnassignTenantUserAlarmOnUserRemoving() throws Exception { + loginDifferentTenant(); + + User user = new User(); + user.setAuthority(Authority.TENANT_ADMIN); + user.setTenantId(tenantId); + user.setEmail("tenantForAssign@thingsboard.org"); + User savedUser = createUser(user, "password"); + + Device device = createDevice("Different tenant device", "default", "differentTenantTest"); + + Alarm alarm = Alarm.builder() + .type(TEST_ALARM_TYPE) + .tenantId(savedDifferentTenant.getId()) + .originator(device.getId()) + .severity(AlarmSeverity.MAJOR) + .build(); + alarm = doPost("/api/alarm", alarm, Alarm.class); + Assert.assertNotNull(alarm); + + alarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); + Assert.assertNotNull(alarm); + AlarmId alarmId = alarm.getId(); + + long beforeAssignmentTs = System.currentTimeMillis(); + + doPost("/api/alarm/" + alarmId.getId() + "/assign/" + savedUser.getId().getId()).andExpect(status().isOk()); + Alarm foundAlarm = doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class); + + Assert.assertNotNull(foundAlarm); + Assert.assertEquals(savedUser.getId(), foundAlarm.getAssigneeId()); + Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); + + beforeAssignmentTs = System.currentTimeMillis(); + + loginSysAdmin(); + + doDelete("/api/user/" + savedUser.getId().getId()).andExpect(status().isOk()); + + loginDifferentTenant(); + + foundAlarm = Awaitility.await().atMost(TIMEOUT, TimeUnit.SECONDS) + .until(() -> doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class), Objects::nonNull); + + Assert.assertNotNull(foundAlarm); + Assert.assertNull(foundAlarm.getAssigneeId()); + Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); + } + + @Test + public void testUnassignAlarmOnUserRemoving() throws Exception { + loginDifferentTenant(); + + User user = new User(); + user.setAuthority(Authority.TENANT_ADMIN); + user.setTenantId(tenantId); + user.setEmail("tenantForAssign@thingsboard.org"); + User savedUser = createUser(user, "password"); + + Device device = createDevice("Different tenant device", "default", "differentTenantTest"); + + Alarm alarm = Alarm.builder() + .type(TEST_ALARM_TYPE) + .tenantId(savedDifferentTenant.getId()) + .originator(device.getId()) + .severity(AlarmSeverity.MAJOR) + .build(); + alarm = doPost("/api/alarm", alarm, Alarm.class); + Assert.assertNotNull(alarm); + AlarmId alarmId = alarm.getId(); + alarm = doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class); + Assert.assertNotNull(alarm); + long beforeAssignmentTs = System.currentTimeMillis(); + + doPost("/api/alarm/" + alarmId.getId() + "/assign/" + savedUser.getId().getId()).andExpect(status().isOk()); + AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class); + Assert.assertNotNull(foundAlarm); + Assert.assertEquals(savedUser.getId(), foundAlarm.getAssigneeId()); + Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); + + beforeAssignmentTs = System.currentTimeMillis(); + + doDelete("/api/user/" + savedUser.getId().getId()).andExpect(status().isOk()); + + foundAlarm = Awaitility.await().atMost(TIMEOUT, TimeUnit.SECONDS) + .until(() -> doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class), Objects::nonNull); + + Assert.assertNotNull(foundAlarm); + Assert.assertNull(foundAlarm.getAssigneeId()); + Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); + } + + @Test + public void testUnassignAlarmOnCustomerRemoving() throws Exception { + createDifferentTenantCustomer(); + loginDifferentTenant(); + + User user = new User(); + user.setAuthority(Authority.CUSTOMER_USER); + user.setTenantId(tenantId); + user.setCustomerId(differentTenantCustomerId); + user.setEmail("customerForAssign@thingsboard.org"); + User savedUser = createUser(user, "password"); + + Device device = createDevice("Different customer device", "default", "differentTenantTest"); + + Device assignedDevice = doPost("/api/customer/" + differentTenantCustomerId.getId() + + "/device/" + device.getId().getId(), Device.class); + Assert.assertEquals(differentTenantCustomerId, assignedDevice.getCustomerId()); + + Alarm alarm = Alarm.builder() + .type(TEST_ALARM_TYPE) + .tenantId(savedDifferentTenant.getId()) + .customerId(differentTenantCustomerId) + .originator(device.getId()) + .severity(AlarmSeverity.MAJOR) + .build(); + alarm = doPost("/api/alarm", alarm, Alarm.class); + Assert.assertNotNull(alarm); + AlarmId alarmId = alarm.getId(); + + alarm = doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class); + Assert.assertNotNull(alarm); + + Mockito.reset(tbClusterService, auditLogService); + long beforeAssignmentTs = System.currentTimeMillis(); + + doPost("/api/alarm/" + alarmId.getId() + "/assign/" + savedUser.getId().getId()).andExpect(status().isOk()); + AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class); + Assert.assertNotNull(foundAlarm); + Assert.assertEquals(savedUser.getId(), foundAlarm.getAssigneeId()); + Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); + + beforeAssignmentTs = System.currentTimeMillis(); + + Mockito.reset(tbClusterService, auditLogService); + + doDelete("/api/customer/" + differentTenantCustomerId.getId()).andExpect(status().isOk()); + + foundAlarm = Awaitility.await().atMost(TIMEOUT, TimeUnit.SECONDS) + .until(() -> doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class), Objects::nonNull); + + Assert.assertNotNull(foundAlarm); + Assert.assertNull(foundAlarm.getAssigneeId()); + Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); + } + @Test public void testFindAlarmsViaCustomerUser() throws Exception { loginCustomerUser(); @@ -640,10 +809,14 @@ public class AlarmControllerTest extends AbstractControllerTest { } private AlarmInfo createAlarm(String type) throws Exception { + return createAlarm(type, customerDevice.getId(), customerId); + } + + private AlarmInfo createAlarm(String type, EntityId originatorId, CustomerId customerId) throws Exception { Alarm alarm = Alarm.builder() .tenantId(tenantId) .customerId(customerId) - .originator(customerDevice.getId()) + .originator(originatorId) .severity(AlarmSeverity.CRITICAL) .type(type) .build(); @@ -691,4 +864,166 @@ public class AlarmControllerTest extends AbstractControllerTest { doPost("/api/alarm", alarm).andExpect(status().isForbidden()); } + @Test + public void testSaveAlarmTypes() throws Exception { + loginTenantAdmin(); + + List types = new ArrayList<>(); + + for (int i = 1; i < 13; i++) { + types.add(createAlarm(TEST_ALARM_TYPE + i).getType()); + } + + Device device = new Device(); + device.setName("Test device 2"); + device.setCustomerId(customerId); + customerDevice = doPost("/api/device", device, Device.class); + + for (int i = 1; i < 10; i++) { + createAlarm(TEST_ALARM_TYPE + i); + } + + List foundTypes = doGetTyped("/api/alarm/types?pageSize=1024&page=0", new TypeReference>() { + }) + .getData() + .stream() + .map(EntitySubtype::getType) + .collect(Collectors.toList()); + + Collections.sort(types); + Collections.sort(foundTypes); + + Assert.assertEquals(types, foundTypes); + } + + @Test + public void testDeleteAlarmTypes() throws Exception { + loginTenantAdmin(); + + List alarms = new ArrayList<>(); + + for (int i = 1; i < 13; i++) { + alarms.add(createAlarm(TEST_ALARM_TYPE + i)); + } + + Device device = new Device(); + device.setName("Test device 2"); + device.setCustomerId(customerId); + customerDevice = doPost("/api/device", device, Device.class); + + for (int i = 1; i < 14; i++) { + alarms.add(createAlarm(TEST_ALARM_TYPE + i)); + } + + List expectedTypes = alarms.stream().map(AlarmInfo::getType).distinct().sorted().collect(Collectors.toList()); + + List foundTypes = doGetTyped("/api/alarm/types?pageSize=1024&page=0", new TypeReference>() { + }) + .getData() + .stream() + .map(EntitySubtype::getType) + .sorted() + .collect(Collectors.toList()); + + Assert.assertEquals(13, foundTypes.size()); + Assert.assertEquals(expectedTypes, foundTypes); + + for (int i = 0; i < 12; i++) { + doDelete("/api/alarm/" + alarms.get(i).getId()).andExpect(status().isOk()); + } + + foundTypes = doGetTyped("/api/alarm/types?pageSize=1024&page=0", new TypeReference>() { + }) + .getData() + .stream() + .map(EntitySubtype::getType) + .sorted() + .collect(Collectors.toList()); + + Assert.assertEquals(13, foundTypes.size()); + Assert.assertEquals(expectedTypes, foundTypes); + + doDelete("/api/alarm/" + alarms.get(12).getId()).andExpect(status().isOk()); + + foundTypes = doGetTyped("/api/alarm/types?pageSize=1024&page=0", new TypeReference>() { + }) + .getData() + .stream() + .map(EntitySubtype::getType) + .sorted() + .collect(Collectors.toList()); + + Assert.assertEquals(12, foundTypes.size()); + + for (int i = 13; i < alarms.size(); i++) { + doDelete("/api/alarm/" + alarms.get(i).getId()).andExpect(status().isOk()); + } + + foundTypes = doGetTyped("/api/alarm/types?pageSize=1024&page=0", new TypeReference>() { + }) + .getData() + .stream() + .map(EntitySubtype::getType) + .sorted() + .collect(Collectors.toList()); + + Assert.assertTrue(foundTypes.isEmpty()); + } + + @Test + public void testDeleteAlarmTypesAfterDeletingAlarmOriginator() throws Exception { + loginTenantAdmin(); + + List devices = new ArrayList<>(); + List types = new ArrayList<>(); + + for (int i = 0; i < 13; i++) { + Device device = new Device(); + device.setName("Test_device_" + i); + devices.add(device = doPost("/api/device", device, Device.class)); + types.add(createAlarm(TEST_ALARM_TYPE + i, device.getId(), null).getType()); + } + + devices.sort(Comparator.comparing(Device::getName)); + Collections.sort(types); + + List foundTypes = doGetTyped("/api/alarm/types?pageSize=1024&page=0", new TypeReference>() { + }) + .getData() + .stream() + .map(EntitySubtype::getType) + .sorted() + .collect(Collectors.toList()); + + Assert.assertEquals(13, foundTypes.size()); + Assert.assertEquals(types, foundTypes); + + for (int i = 0; i < 12; i++) { + doDelete("/api/device/" + devices.remove(0).getId()).andExpect(status().isOk()); + types.remove(0); + } + + foundTypes = doGetTyped("/api/alarm/types?pageSize=1024&page=0", new TypeReference>() { + }) + .getData() + .stream() + .map(EntitySubtype::getType) + .collect(Collectors.toList()); + + Assert.assertEquals(types.size(), foundTypes.size()); + Assert.assertEquals(types, foundTypes); + + doDelete("/api/device/" + devices.get(0).getId()).andExpect(status().isOk()); + + foundTypes = doGetTyped("/api/alarm/types?pageSize=1024&page=0", new TypeReference>() { + }) + .getData() + .stream() + .map(EntitySubtype::getType) + .sorted() + .collect(Collectors.toList()); + + Assert.assertTrue(foundTypes.isEmpty()); + } + } diff --git a/application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java index 40b1707271..ae60bfee3b 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java @@ -34,6 +34,9 @@ import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmInfo; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.audit.ActionType; @@ -50,7 +53,6 @@ import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.service.stats.DefaultRuleEngineStatisticsService; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import static org.hamcrest.Matchers.containsString; @@ -114,8 +116,8 @@ public class AssetControllerTest extends AbstractControllerTest { Asset savedAsset = doPost("/api/asset", asset, Asset.class); - testNotifyEntityOneTimeMsgToEdgeServiceNever(savedAsset, savedAsset.getId(), savedAsset.getId(), - savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED); + testNotifyEntityAllOneTime(savedAsset, savedAsset.getId(), savedAsset.getId(), savedTenant.getId(), + tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED); Assert.assertNotNull(savedAsset); Assert.assertNotNull(savedAsset.getId()); @@ -130,8 +132,8 @@ public class AssetControllerTest extends AbstractControllerTest { savedAsset.setName("My new asset"); doPost("/api/asset", savedAsset, Asset.class); - testNotifyEntityAllOneTime(savedAsset, savedAsset.getId(), savedAsset.getId(), - savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UPDATED); + testNotifyEntityAllOneTime(savedAsset, savedAsset.getId(), savedAsset.getId(), savedTenant.getId(), + tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UPDATED); Asset foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class); Assert.assertEquals(foundAsset.getName(), savedAsset.getName()); @@ -229,7 +231,8 @@ public class AssetControllerTest extends AbstractControllerTest { @Test public void testFindAssetTypesByTenantId() throws Exception { - List assets = new ArrayList<>(); + AssetProfile assetProfile = createAssetProfile("typeB"); + assetProfile = doPost("/api/assetProfile", assetProfile, AssetProfile.class); Mockito.reset(tbClusterService, auditLogService); @@ -238,24 +241,25 @@ public class AssetControllerTest extends AbstractControllerTest { Asset asset = new Asset(); asset.setName("My asset B" + i); asset.setType("typeB"); - assets.add(doPost("/api/asset", asset, Asset.class)); + asset.setAssetProfileId(assetProfile.getId()); + doPost("/api/asset", asset, Asset.class); } - testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new Asset(), new Asset(), + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Asset(), new Asset(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ADDED, cntTime); + ActionType.ADDED, cntTime, cntTime, cntTime); for (int i = 0; i < 7; i++) { Asset asset = new Asset(); asset.setName("My asset C" + i); asset.setType("typeC"); - assets.add(doPost("/api/asset", asset, Asset.class)); + doPost("/api/asset", asset, Asset.class); } for (int i = 0; i < 9; i++) { Asset asset = new Asset(); asset.setName("My asset A" + i); asset.setType("typeA"); - assets.add(doPost("/api/asset", asset, Asset.class)); + doPost("/api/asset", asset, Asset.class); } List assetTypes = doGetTyped("/api/asset/types", new TypeReference>() { @@ -280,7 +284,7 @@ public class AssetControllerTest extends AbstractControllerTest { doDelete("/api/asset/" + savedAsset.getId().getId().toString()) .andExpect(status().isOk()); - testNotifyEntityOneTimeMsgToEdgeServiceNever(savedAsset, savedAsset.getId(), savedAsset.getId(), + testNotifyEntityAllOneTime(savedAsset, savedAsset.getId(), savedAsset.getId(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.DELETED, savedAsset.getId().getId().toString()); @@ -290,6 +294,39 @@ public class AssetControllerTest extends AbstractControllerTest { .andExpect(statusReason(containsString(msgErrorNoFound("Asset", assetIdStr)))); } + @Test + public void testDeleteAssetWithAlarmsAndAlarmTypes() throws Exception { + Asset asset = new Asset(); + asset.setName("My asset"); + asset.setType("default"); + Asset savedAsset = doPost("/api/asset", asset, Asset.class); + + Alarm alarm = Alarm.builder() + .tenantId(tenantId) + .originator(savedAsset.getId()) + .severity(AlarmSeverity.CRITICAL) + .type("test_type") + .build(); + + alarm = doPost("/api/alarm", alarm, Alarm.class); + Assert.assertNotNull(alarm); + + AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); + Assert.assertNotNull(foundAlarm); + + doDelete("/api/asset/" + savedAsset.getId().getId().toString()) + .andExpect(status().isOk()); + + String assetIdStr = savedAsset.getId().getId().toString(); + doGet("/api/asset/" + assetIdStr) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Asset", assetIdStr)))); + + doGet("/api/alarm/info/" + alarm.getId()) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Alarm", alarm.getId().getId().toString())))); + } + @Test public void testDeleteAssetAssignedToEntityView() throws Exception { Asset asset1 = new Asset(); @@ -342,7 +379,7 @@ public class AssetControllerTest extends AbstractControllerTest { Asset savedAsset = doPost("/api/asset", asset, Asset.class); Assert.assertEquals("default", savedAsset.getType()); - testNotifyEntityOneTimeMsgToEdgeServiceNever(savedAsset, savedAsset.getId(), savedAsset.getId(), + testNotifyEntityAllOneTime(savedAsset, savedAsset.getId(), savedAsset.getId(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED); } @@ -380,9 +417,9 @@ public class AssetControllerTest extends AbstractControllerTest { + "/asset/" + savedAsset.getId().getId().toString(), Asset.class); Assert.assertEquals(savedCustomer.getId(), assignedAsset.getCustomerId()); - testNotifyEntityAllOneTime(assignedAsset, assignedAsset.getId(), assignedAsset.getId(), + testNotifyAssignUnassignEntityAllOneTime(assignedAsset, assignedAsset.getId(), assignedAsset.getId(), savedTenant.getId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ASSIGNED_TO_CUSTOMER, assignedAsset.getId().toString(), savedCustomer.getId().toString(), savedCustomer.getTitle()); + ActionType.ASSIGNED_TO_CUSTOMER, ActionType.UPDATED, assignedAsset.getId().toString(), savedCustomer.getId().toString(), savedCustomer.getTitle()); Asset foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class); Assert.assertEquals(savedCustomer.getId(), foundAsset.getCustomerId()); @@ -393,9 +430,9 @@ public class AssetControllerTest extends AbstractControllerTest { doDelete("/api/customer/asset/" + savedAsset.getId().getId().toString(), Asset.class); Assert.assertEquals(ModelConstants.NULL_UUID, unassignedAsset.getCustomerId().getId()); - testNotifyEntityAllOneTime(savedAsset, savedAsset.getId(), savedAsset.getId(), + testNotifyAssignUnassignEntityAllOneTime(savedAsset, savedAsset.getId(), savedAsset.getId(), savedTenant.getId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.UNASSIGNED_FROM_CUSTOMER, savedAsset.getId().toString(), savedCustomer.getId().toString(), savedCustomer.getTitle()); + ActionType.UNASSIGNED_FROM_CUSTOMER, ActionType.UPDATED, savedAsset.getId().toString(), savedCustomer.getId().toString(), savedCustomer.getTitle()); foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class); Assert.assertEquals(ModelConstants.NULL_UUID, foundAsset.getCustomerId().getId()); @@ -415,9 +452,10 @@ public class AssetControllerTest extends AbstractControllerTest { Customer publicCustomer = doGet("/api/customer/" + assignedAsset.getCustomerId(), Customer.class); Assert.assertTrue(publicCustomer.isPublic()); - testNotifyEntityAllOneTime(assignedAsset, assignedAsset.getId(), assignedAsset.getId(), + testNotifyAssignUnassignEntityAllOneTime(assignedAsset, assignedAsset.getId(), assignedAsset.getId(), savedTenant.getId(), publicCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ASSIGNED_TO_CUSTOMER, assignedAsset.getId().toString(), publicCustomer.getId().toString(), publicCustomer.getTitle()); + ActionType.ASSIGNED_TO_CUSTOMER, ActionType.UPDATED, assignedAsset.getId().toString(), + publicCustomer.getId().toString(), publicCustomer.getTitle()); Asset foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class); Assert.assertEquals(publicCustomer.getId(), foundAsset.getCustomerId()); @@ -428,9 +466,10 @@ public class AssetControllerTest extends AbstractControllerTest { doDelete("/api/customer/asset/" + savedAsset.getId().getId().toString(), Asset.class); Assert.assertEquals(ModelConstants.NULL_UUID, unassignedAsset.getCustomerId().getId()); - testNotifyEntityAllOneTime(savedAsset, savedAsset.getId(), savedAsset.getId(), + testNotifyAssignUnassignEntityAllOneTime(savedAsset, savedAsset.getId(), savedAsset.getId(), savedTenant.getId(), publicCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.UNASSIGNED_FROM_CUSTOMER, savedAsset.getId().toString(), publicCustomer.getId().toString(), publicCustomer.getTitle()); + ActionType.UNASSIGNED_FROM_CUSTOMER, ActionType.UPDATED, savedAsset.getId().toString(), + publicCustomer.getId().toString(), publicCustomer.getTitle()); foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class); Assert.assertEquals(ModelConstants.NULL_UUID, foundAsset.getCustomerId().getId()); @@ -513,7 +552,7 @@ public class AssetControllerTest extends AbstractControllerTest { } List loadedAssets = new ArrayList<>(); PageLink pageLink = new PageLink(23); - PageData pageData = null; + PageData pageData; do { pageData = doGetTypedWithPageLink("/api/tenant/assets?", new TypeReference>() { @@ -524,14 +563,14 @@ public class AssetControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new Asset(), new Asset(), + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Asset(), new Asset(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ADDED, cntEntity); + ActionType.ADDED, cntEntity, cntEntity, cntEntity); loadedAssets.removeIf(asset -> asset.getType().equals(DefaultRuleEngineStatisticsService.TB_SERVICE_QUEUE)); - Collections.sort(assets, idComparator); - Collections.sort(loadedAssets, idComparator); + assets.sort(idComparator); + loadedAssets.sort(idComparator); Assert.assertEquals(assets, loadedAssets); } @@ -574,8 +613,8 @@ public class AssetControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - Collections.sort(assetsTitle1, idComparator); - Collections.sort(loadedAssetsTitle1, idComparator); + assetsTitle1.sort(idComparator); + loadedAssetsTitle1.sort(idComparator); Assert.assertEquals(assetsTitle1, loadedAssetsTitle1); @@ -591,8 +630,8 @@ public class AssetControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - Collections.sort(assetsTitle2, idComparator); - Collections.sort(loadedAssetsTitle2, idComparator); + assetsTitle2.sort(idComparator); + loadedAssetsTitle2.sort(idComparator); Assert.assertEquals(assetsTitle2, loadedAssetsTitle2); @@ -661,8 +700,8 @@ public class AssetControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - Collections.sort(assetsType1, idComparator); - Collections.sort(loadedAssetsType1, idComparator); + assetsType1.sort(idComparator); + loadedAssetsType1.sort(idComparator); Assert.assertEquals(assetsType1, loadedAssetsType1); @@ -678,8 +717,8 @@ public class AssetControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - Collections.sort(assetsType2, idComparator); - Collections.sort(loadedAssetsType2, idComparator); + assetsType2.sort(idComparator); + loadedAssetsType2.sort(idComparator); Assert.assertEquals(assetsType2, loadedAssetsType2); @@ -738,8 +777,8 @@ public class AssetControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - Collections.sort(assets, idComparator); - Collections.sort(loadedAssets, idComparator); + assets.sort(idComparator); + loadedAssets.sort(idComparator); Assert.assertEquals(assets, loadedAssets); } @@ -791,8 +830,8 @@ public class AssetControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - Collections.sort(assetsTitle1, idComparator); - Collections.sort(loadedAssetsTitle1, idComparator); + assetsTitle1.sort(idComparator); + loadedAssetsTitle1.sort(idComparator); Assert.assertEquals(assetsTitle1, loadedAssetsTitle1); @@ -808,8 +847,8 @@ public class AssetControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - Collections.sort(assetsTitle2, idComparator); - Collections.sort(loadedAssetsTitle2, idComparator); + assetsTitle2.sort(idComparator); + loadedAssetsTitle2.sort(idComparator); Assert.assertEquals(assetsTitle2, loadedAssetsTitle2); @@ -887,8 +926,8 @@ public class AssetControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - Collections.sort(assetsType1, idComparator); - Collections.sort(loadedAssetsType1, idComparator); + assetsType1.sort(idComparator); + loadedAssetsType1.sort(idComparator); Assert.assertEquals(assetsType1, loadedAssetsType1); @@ -904,8 +943,8 @@ public class AssetControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - Collections.sort(assetsType2, idComparator); - Collections.sort(loadedAssetsType2, idComparator); + assetsType2.sort(idComparator); + loadedAssetsType2.sort(idComparator); Assert.assertEquals(assetsType2, loadedAssetsType2); diff --git a/application/src/test/java/org/thingsboard/server/controller/AssetProfileControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AssetProfileControllerTest.java index c84b058cc7..34b1006ffb 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AssetProfileControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AssetProfileControllerTest.java @@ -28,6 +28,7 @@ import org.springframework.context.annotation.Primary; import org.springframework.test.context.ContextConfiguration; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; @@ -46,11 +47,13 @@ import org.thingsboard.server.dao.service.DaoSqlTest; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.common.data.DataConstants.DEFAULT_DEVICE_TYPE; @ContextConfiguration(classes = {AssetProfileControllerTest.Config.class}) @DaoSqlTest @@ -355,7 +358,7 @@ public class AssetProfileControllerTest extends AbstractControllerTest { testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new AssetProfile(), new AssetProfile(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ADDED, ActionType.ADDED, cntEntity, cntEntity, cntEntity); + ActionType.ADDED, cntEntity, cntEntity, cntEntity); Mockito.reset(tbClusterService, auditLogService); List loadedAssetProfiles = new ArrayList<>(); @@ -384,7 +387,7 @@ public class AssetProfileControllerTest extends AbstractControllerTest { testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(loadedAssetProfiles.get(0), loadedAssetProfiles.get(0), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.DELETED, ActionType.DELETED, cntEntity, cntEntity, cntEntity, loadedAssetProfiles.get(0).getId().getId().toString()); + ActionType.DELETED, cntEntity, cntEntity, cntEntity, loadedAssetProfiles.get(0).getId().getId().toString()); pageLink = new PageLink(17); pageData = doGetTypedWithPageLink("/api/assetProfiles?", @@ -459,6 +462,62 @@ public class AssetProfileControllerTest extends AbstractControllerTest { testEntityDaoWithRelationsTransactionalException(assetProfileDao, savedTenant.getId(), assetProfileId, "/api/assetProfile/" + assetProfileId); } + @Test + public void testGetAssetProfileNames() throws Exception { + var pageLink = new PageLink(Integer.MAX_VALUE); + var assetProfileInfos = doGetTypedWithPageLink("/api/assetProfileInfos?", + new TypeReference>() { + }, pageLink); + Assert.assertNotNull("Asset Profile Infos page data is null!", assetProfileInfos); + Assert.assertEquals("Asset Profile Infos Page data is empty! Expected to have default profile created!", 1, assetProfileInfos.getTotalElements()); + List expectedAssetProfileNames = assetProfileInfos.getData().stream() + .map(info -> new EntityInfo(info.getId(), info.getName())) + .sorted(Comparator.comparing(EntityInfo::getName)) + .collect(Collectors.toList()); + var assetProfileNames = doGetTyped("/api/assetProfile/names", new TypeReference>() { + }); + Assert.assertNotNull("Asset Profile Names list is null!", assetProfileNames); + Assert.assertFalse("Asset Profile Names list is empty!", assetProfileNames.isEmpty()); + Assert.assertEquals(expectedAssetProfileNames, assetProfileNames); + Assert.assertEquals(1, assetProfileNames.size()); + Assert.assertEquals(DEFAULT_DEVICE_TYPE, assetProfileNames.get(0).getName()); + + int count = 3; + for (int i = 0; i < count; i++) { + Asset asset = new Asset(); + asset.setName("AssetName" + i); + asset.setType("AssetProfileName" + i); + Asset savedAsset = doPost("/api/asset", asset, Asset.class); + Assert.assertNotNull(savedAsset); + } + assetProfileInfos = doGetTypedWithPageLink("/api/assetProfileInfos?", + new TypeReference<>() { + }, pageLink); + Assert.assertNotNull("Asset Profile Infos page data is null!", assetProfileInfos); + Assert.assertEquals("Asset Profile Infos Page data is empty! Expected to have default profile created + count value!", 1 + count, assetProfileInfos.getTotalElements()); + expectedAssetProfileNames = assetProfileInfos.getData().stream() + .map(info -> new EntityInfo(info.getId(), info.getName())) + .sorted(Comparator.comparing(EntityInfo::getName)) + .collect(Collectors.toList()); + + assetProfileNames = doGetTyped("/api/assetProfile/names", new TypeReference<>() { + }); + Assert.assertNotNull("Asset Profile Names list is null!", assetProfileNames); + Assert.assertFalse("Asset Profile Names list is empty!", assetProfileNames.isEmpty()); + Assert.assertEquals(expectedAssetProfileNames, assetProfileNames); + Assert.assertEquals(1 + count, assetProfileNames.size()); + + assetProfileNames = doGetTyped("/api/assetProfile/names?activeOnly=true", new TypeReference<>() { + }); + Assert.assertNotNull("Asset Profile Names list is null!", assetProfileNames); + Assert.assertFalse("Asset Profile Names list is empty!", assetProfileNames.isEmpty()); + var expectedAssetProfileNamesWithoutDefault = expectedAssetProfileNames.stream() + .filter(entityInfo -> !entityInfo.getName().equals(DEFAULT_DEVICE_TYPE)) + .collect(Collectors.toList()); + Assert.assertEquals(expectedAssetProfileNamesWithoutDefault, assetProfileNames); + Assert.assertEquals(count, assetProfileNames.size()); + } + private AssetProfile savedAssetProfile(String name) { AssetProfile assetProfile = createAssetProfile(name); return doPost("/api/assetProfile", assetProfile, AssetProfile.class); diff --git a/application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java index b833e45d34..bab102aeca 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java @@ -15,19 +15,41 @@ */ package org.thingsboard.server.controller; +import com.fasterxml.jackson.databind.JsonNode; +import org.junit.After; import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.http.HttpHeaders; +import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.security.model.SecuritySettings; import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.service.security.auth.rest.LoginRequest; +import org.thingsboard.server.service.security.model.ChangePasswordRequest; import java.util.concurrent.TimeUnit; import static org.hamcrest.Matchers.is; +import static org.mockito.ArgumentMatchers.anyString; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @DaoSqlTest public class AuthControllerTest extends AbstractControllerTest { + @After + public void tearDown() throws Exception { + loginSysAdmin(); + SecuritySettings securitySettings = doGet("/api/admin/securitySettings", SecuritySettings.class); + + securitySettings.getPasswordPolicy().setMaximumLength(72); + securitySettings.getPasswordPolicy().setForceUserToResetPasswordIfNotValid(false); + + doPost("/api/admin/securitySettings", securitySettings).andExpect(status().isOk()); + } + @Test public void testGetUser() throws Exception { @@ -84,4 +106,65 @@ public class AuthControllerTest extends AbstractControllerTest { .andExpect(jsonPath("$.authority",is(Authority.SYS_ADMIN.name()))) .andExpect(jsonPath("$.email",is(SYS_ADMIN_EMAIL))); } + + @Test + public void testShouldNotUpdatePasswordWithValueLongerThanDefaultLimit() throws Exception { + loginTenantAdmin(); + ChangePasswordRequest changePasswordRequest = new ChangePasswordRequest(); + changePasswordRequest.setCurrentPassword("tenant"); + changePasswordRequest.setNewPassword(RandomStringUtils.randomAlphanumeric(73)); + doPost("/api/auth/changePassword", changePasswordRequest) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message", is("Password must be no more than 72 characters in length."))); + } + + @Test + public void testShouldNotAuthorizeUserIfHisPasswordBecameTooLong() throws Exception { + loginTenantAdmin(); + + ChangePasswordRequest changePasswordRequest = new ChangePasswordRequest(); + changePasswordRequest.setCurrentPassword("tenant"); + String newPassword = RandomStringUtils.randomAlphanumeric(16); + changePasswordRequest.setNewPassword(newPassword); + doPost("/api/auth/changePassword", changePasswordRequest) + .andExpect(status().isOk()); + loginUser(TENANT_ADMIN_EMAIL, newPassword); + + loginSysAdmin(); + SecuritySettings securitySettings = doGet("/api/admin/securitySettings", SecuritySettings.class); + securitySettings.getPasswordPolicy().setMaximumLength(15); + securitySettings.getPasswordPolicy().setForceUserToResetPasswordIfNotValid(true); + doPost("/api/admin/securitySettings", securitySettings).andExpect(status().isOk()); + + //try to login with user password that is not valid after security settings was updated + doPost("/api/auth/login", new LoginRequest(TENANT_ADMIN_EMAIL, newPassword)) + .andExpect(status().isUnauthorized()) + .andExpect(jsonPath("$.message", is("The entered password violates our policies. If this is your real password, please reset it."))); + } + + @Test + public void testShouldNotResetPasswordToTooLongValue() throws Exception { + loginTenantAdmin(); + + JsonNode resetPasswordByEmailRequest = JacksonUtil.newObjectNode() + .put("email", TENANT_ADMIN_EMAIL); + + doPost("/api/noauth/resetPasswordByEmail", resetPasswordByEmailRequest) + .andExpect(status().isOk()); + Thread.sleep(1000); + doGet("/api/noauth/resetPassword?resetToken={resetToken}", this.currentResetPasswordToken) + .andExpect(status().isSeeOther()) + .andExpect(header().string(HttpHeaders.LOCATION, "/login/resetPassword?resetToken=" + this.currentResetPasswordToken)); + + String newPassword = RandomStringUtils.randomAlphanumeric(73); + JsonNode resetPasswordRequest = JacksonUtil.newObjectNode() + .put("resetToken", this.currentResetPasswordToken) + .put("password", newPassword); + + Mockito.doNothing().when(mailService).sendPasswordWasResetEmail(anyString(), anyString()); + doPost("/api/noauth/resetPassword", resetPasswordRequest) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.message", + is("Password must be no more than 72 characters in length."))); + } } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java index 465ec37bb9..8418ba67d9 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseQueueControllerTest.java @@ -16,9 +16,19 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.core.type.TypeReference; +import org.apache.commons.lang3.RandomStringUtils; import org.junit.Assert; import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.test.context.TestPropertySource; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.queue.ProcessingStrategy; @@ -26,13 +36,53 @@ import org.thingsboard.server.common.data.queue.ProcessingStrategyType; import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.queue.SubmitStrategy; import org.thingsboard.server.common.data.queue.SubmitStrategyType; +import org.thingsboard.server.common.msg.queue.RuleEngineException; +import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.common.stats.StatsFactory; +import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.dao.timeseries.TimeseriesDao; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.queue.common.TbProtoQueueMsg; +import org.thingsboard.server.queue.discovery.QueueKey; +import org.thingsboard.server.service.queue.TbRuleEngineConsumerStats; +import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingResult; +import org.thingsboard.server.service.stats.DefaultRuleEngineStatisticsService; +import org.thingsboard.server.service.stats.RuleEngineStatisticsService; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE; @DaoSqlTest +@TestPropertySource(properties = { + "queue.rule-engine.stats.max-error-message-length=100" +}) public class BaseQueueControllerTest extends AbstractControllerTest { + @Autowired + private RuleEngineStatisticsService ruleEngineStatisticsService; + @Autowired + private StatsFactory statsFactory; + @SpyBean + private TimeseriesDao timeseriesDao; + @Autowired + private AssetService assetService; + @Test public void testQueueWithServiceTypeRE() throws Exception { loginSysAdmin(); @@ -93,4 +143,96 @@ public class BaseQueueControllerTest extends AbstractControllerTest { .andExpect(status().isOk()); } + @Test + public void testQueueStatsTtl() throws ThingsboardException { + Queue queue = new Queue(); + queue.setName("Test-1"); + queue.setTenantId(TenantId.SYS_TENANT_ID); + + TbRuleEngineProcessingResult testProcessingResult = Mockito.mock(TbRuleEngineProcessingResult.class); + TbProtoQueueMsg msg = new TbProtoQueueMsg<>(UUID.randomUUID(), + TransportProtos.ToRuleEngineMsg.newBuilder() + .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) + .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) + .build()); + when(testProcessingResult.getSuccessMap()).thenReturn(Stream.generate(() -> msg) + .limit(5).collect(Collectors.toConcurrentMap(m -> UUID.randomUUID(), m -> m))); + when(testProcessingResult.getFailedMap()).thenReturn(Stream.generate(() -> msg) + .limit(5).collect(Collectors.toConcurrentMap(m -> UUID.randomUUID(), m -> m))); + when(testProcessingResult.getPendingMap()).thenReturn(new ConcurrentHashMap<>()); + RuleEngineException ruleEngineException = new RuleEngineException("Test Exception"); + when(testProcessingResult.getExceptionsMap()).thenReturn(new ConcurrentHashMap<>(Map.of( + tenantId, ruleEngineException + ))); + + TbRuleEngineConsumerStats testStats = new TbRuleEngineConsumerStats(new QueueKey(ServiceType.TB_RULE_ENGINE, queue), statsFactory); + testStats.log(testProcessingResult, true); + + int queueStatsTtlDays = 14; + int ruleEngineExceptionsTtlDays = 7; + updateDefaultTenantProfileConfig(profileConfiguration -> { + profileConfiguration.setQueueStatsTtlDays(queueStatsTtlDays); + profileConfiguration.setRuleEngineExceptionsTtlDays(ruleEngineExceptionsTtlDays); + }); + ruleEngineStatisticsService.reportQueueStats(System.currentTimeMillis(), testStats); + + Asset serviceAsset = assetService.findAssetsByTenantIdAndType(tenantId, TB_SERVICE_QUEUE, new PageLink(100)).getData() + .stream().filter(asset -> asset.getName().startsWith(queue.getName())) + .findFirst().get(); + + ArgumentCaptor ttlCaptor = ArgumentCaptor.forClass(Long.class); + verify(timeseriesDao).save(eq(tenantId), eq(serviceAsset.getId()), argThat(tsKvEntry -> { + return tsKvEntry.getKey().equals(TbRuleEngineConsumerStats.SUCCESSFUL_MSGS) && + tsKvEntry.getLongValue().get().equals(5L); + }), ttlCaptor.capture()); + verify(timeseriesDao).save(eq(tenantId), eq(serviceAsset.getId()), argThat(tsKvEntry -> { + return tsKvEntry.getKey().equals(TbRuleEngineConsumerStats.FAILED_MSGS) && + tsKvEntry.getLongValue().get().equals(5L); + }), ttlCaptor.capture()); + assertThat(ttlCaptor.getAllValues()).allSatisfy(usedTtl -> { + assertThat(usedTtl).isEqualTo(TimeUnit.DAYS.toSeconds(queueStatsTtlDays)); + }); + + verify(timeseriesDao).save(eq(tenantId), eq(serviceAsset.getId()), argThat(tsKvEntry -> { + return tsKvEntry.getKey().equals(DefaultRuleEngineStatisticsService.RULE_ENGINE_EXCEPTION) && + tsKvEntry.getJsonValue().get().equals(ruleEngineException.toJsonString(0)); + }), ttlCaptor.capture()); + assertThat(ttlCaptor.getValue()).isEqualTo(TimeUnit.DAYS.toSeconds(ruleEngineExceptionsTtlDays)); + } + + @Test + public void testRuleEngineExceptionTruncation() { + Queue queue = new Queue(); + queue.setName("Test-2"); + queue.setTenantId(TenantId.SYS_TENANT_ID); + + TbRuleEngineProcessingResult testProcessingResult = Mockito.mock(TbRuleEngineProcessingResult.class); + when(testProcessingResult.getSuccessMap()).thenReturn(new ConcurrentHashMap<>()); + when(testProcessingResult.getFailedMap()).thenReturn(new ConcurrentHashMap<>()); + when(testProcessingResult.getPendingMap()).thenReturn(new ConcurrentHashMap<>()); + + String largeExceptionMessage = RandomStringUtils.randomAlphabetic(150); + RuleEngineException ruleEngineException = new RuleEngineException(largeExceptionMessage); + when(testProcessingResult.getExceptionsMap()).thenReturn(new ConcurrentHashMap<>(Map.of( + tenantId, ruleEngineException + ))); + + TbRuleEngineConsumerStats testStats = new TbRuleEngineConsumerStats(new QueueKey(ServiceType.TB_RULE_ENGINE, queue), statsFactory); + testStats.log(testProcessingResult, true); + ruleEngineStatisticsService.reportQueueStats(System.currentTimeMillis(), testStats); + + AtomicReference reExceptionTsKvEntryCaptor = new AtomicReference<>(); + verify(timeseriesDao).save(eq(tenantId), any(), argThat(tsKvEntry -> { + if (tsKvEntry.getKey().equals(DefaultRuleEngineStatisticsService.RULE_ENGINE_EXCEPTION)) { + reExceptionTsKvEntryCaptor.set(tsKvEntry); + return true; + } + return false; + }), anyLong()); + TsKvEntry reExceptionTsKvEntry = reExceptionTsKvEntryCaptor.get(); + + String finalErrorMessage = JacksonUtil.toJsonNode(reExceptionTsKvEntry.getJsonValue().get()).get("message").asText(); + assertThat(finalErrorMessage).isEqualTo(largeExceptionMessage.substring(0, 100) + "...[truncated 50 symbols]"); + } + } diff --git a/application/src/test/java/org/thingsboard/server/controller/CustomerControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/CustomerControllerTest.java index 125e533471..f5c33debb2 100644 --- a/application/src/test/java/org/thingsboard/server/controller/CustomerControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/CustomerControllerTest.java @@ -117,8 +117,8 @@ public class CustomerControllerTest extends AbstractControllerTest { Customer savedCustomer = doPost("/api/customer", customer, Customer.class); - testNotifyEntityOneTimeMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), - savedCustomer.getTenantId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + testNotifyEntityAllOneTime(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), savedCustomer.getTenantId(), + new CustomerId(CustomerId.NULL_UUID), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED); Assert.assertNotNull(savedCustomer); @@ -242,7 +242,7 @@ public class CustomerControllerTest extends AbstractControllerTest { doDelete("/api/customer/" + savedCustomer.getId().getId().toString()) .andExpect(status().isOk()); - testNotifyEntityBroadcastEntityStateChangeEventOneTimeMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), + testNotifyEntityBroadcastEntityStateChangeEventOneTime(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.DELETED, savedCustomer.getId().getId().toString()); } @@ -272,7 +272,7 @@ public class CustomerControllerTest extends AbstractControllerTest { doDelete("/api/customer/" + savedCustomer.getId().getId().toString()) .andExpect(status().isOk()); - testNotifyEntityBroadcastEntityStateChangeEventOneTimeMsgToEdgeServiceNever(savedCustomer, savedCustomer.getId(), + testNotifyEntityBroadcastEntityStateChangeEventOneTime(savedCustomer, savedCustomer.getId(), savedCustomer.getId(), savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.DELETED, savedCustomer.getId().getId().toString()); @@ -332,9 +332,9 @@ public class CustomerControllerTest extends AbstractControllerTest { } List customers = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); - testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new Customer(), new Customer(), + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Customer(), new Customer(), tenantId, tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ADDED, cntEntity); + ActionType.ADDED, cntEntity, cntEntity, cntEntity); List loadedCustomers = new ArrayList<>(135); PageLink pageLink = new PageLink(23); diff --git a/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java index 9f8e63e3b0..fb8064d6d5 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java @@ -46,7 +46,6 @@ import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DaoSqlTest; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import static org.hamcrest.Matchers.containsString; @@ -154,7 +153,7 @@ public class DashboardControllerTest extends AbstractControllerTest { doDelete("/api/dashboard/" + savedDashboard.getId().getId().toString()).andExpect(status().isOk()); - testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDashboard, savedDashboard.getId(), savedDashboard.getId(), + testNotifyEntityAllOneTime(savedDashboard, savedDashboard.getId(), savedDashboard.getId(), savedDashboard.getTenantId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.DELETED, savedDashboard.getId().getId().toString()); @@ -198,7 +197,7 @@ public class DashboardControllerTest extends AbstractControllerTest { testNotifyEntityAllOneTimeLogEntityActionEntityEqClass(assignedDashboard, assignedDashboard.getId(), assignedDashboard.getId(), savedTenant.getId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ASSIGNED_TO_CUSTOMER, - assignedDashboard .getId().getId().toString(), savedCustomer.getId().getId().toString(), savedCustomer.getTitle()); + ActionType.UPDATED, assignedDashboard.getId().getId().toString(), savedCustomer.getId().getId().toString(), savedCustomer.getTitle()); Dashboard foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); Assert.assertTrue(foundDashboard.getAssignedCustomers().contains(savedCustomer.toShortCustomerInfo())); @@ -210,7 +209,7 @@ public class DashboardControllerTest extends AbstractControllerTest { testNotifyEntityAllOneTimeLogEntityActionEntityEqClass(assignedDashboard, assignedDashboard.getId(), assignedDashboard.getId(), savedTenant.getId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UNASSIGNED_FROM_CUSTOMER, - unassignedDashboard.getId().getId().toString(), savedCustomer.getId().getId().toString(), savedCustomer.getTitle()); + ActionType.UPDATED, unassignedDashboard.getId().getId().toString(), savedCustomer.getId().getId().toString(), savedCustomer.getTitle()); Assert.assertTrue(unassignedDashboard.getAssignedCustomers() == null || unassignedDashboard.getAssignedCustomers().isEmpty()); @@ -241,7 +240,7 @@ public class DashboardControllerTest extends AbstractControllerTest { testNotifyEntityAllOneTimeLogEntityActionEntityEqClass(assignedDashboard, assignedDashboard.getId(), assignedDashboard.getId(), savedTenant.getId(), publicCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ASSIGNED_TO_CUSTOMER, - assignedDashboard .getId().getId().toString(), publicCustomer.getId().getId().toString(), publicCustomer.getTitle()); + ActionType.UPDATED, assignedDashboard .getId().getId().toString(), publicCustomer.getId().getId().toString(), publicCustomer.getTitle()); Dashboard foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); Assert.assertTrue(foundDashboard.getAssignedCustomers().contains(publicCustomer.toShortCustomerInfo())); @@ -253,7 +252,7 @@ public class DashboardControllerTest extends AbstractControllerTest { testNotifyEntityAllOneTimeLogEntityActionEntityEqClass(assignedDashboard, assignedDashboard.getId(), assignedDashboard.getId(), savedTenant.getId(), publicCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UNASSIGNED_FROM_CUSTOMER, - unassignedDashboard.getId().getId().toString(), publicCustomer.getId().getId().toString(), publicCustomer.getTitle()); + ActionType.UPDATED, unassignedDashboard.getId().getId().toString(), publicCustomer.getId().getId().toString(), publicCustomer.getTitle()); Assert.assertTrue(unassignedDashboard.getAssignedCustomers() == null || unassignedDashboard.getAssignedCustomers().isEmpty()); @@ -328,7 +327,18 @@ public class DashboardControllerTest extends AbstractControllerTest { @Test public void testFindTenantDashboards() throws Exception { - List dashboards = new ArrayList<>(); + List expectedDashboards = new ArrayList<>(); + PageLink pageLink = new PageLink(24); + PageData pageData = null; + do { + pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", + new TypeReference>() { + }, pageLink); + expectedDashboards.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageLink.nextPageLink(); + } + } while (pageData.hasNext()); Mockito.reset(tbClusterService, auditLogService); @@ -336,16 +346,14 @@ public class DashboardControllerTest extends AbstractControllerTest { for (int i = 0; i < cntEntity; i++) { Dashboard dashboard = new Dashboard(); dashboard.setTitle("Dashboard" + i); - dashboards.add(new DashboardInfo(doPost("/api/dashboard", dashboard, Dashboard.class))); + expectedDashboards.add(new DashboardInfo(doPost("/api/dashboard", dashboard, Dashboard.class))); } - testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new Dashboard(), new Dashboard(), + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Dashboard(), new Dashboard(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ADDED, cntEntity); + ActionType.ADDED, cntEntity, cntEntity, cntEntity); List loadedDashboards = new ArrayList<>(); - PageLink pageLink = new PageLink(24); - PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", new TypeReference>() { @@ -356,10 +364,10 @@ public class DashboardControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - Collections.sort(dashboards, idComparator); - Collections.sort(loadedDashboards, idComparator); + expectedDashboards.sort(idComparator); + loadedDashboards.sort(idComparator); - Assert.assertEquals(dashboards, loadedDashboards); + Assert.assertEquals(expectedDashboards, loadedDashboards); } @Test @@ -400,8 +408,8 @@ public class DashboardControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - Collections.sort(dashboardsTitle1, idComparator); - Collections.sort(loadedDashboardsTitle1, idComparator); + dashboardsTitle1.sort(idComparator); + loadedDashboardsTitle1.sort(idComparator); Assert.assertEquals(dashboardsTitle1, loadedDashboardsTitle1); @@ -417,8 +425,8 @@ public class DashboardControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - Collections.sort(dashboardsTitle2, idComparator); - Collections.sort(loadedDashboardsTitle2, idComparator); + dashboardsTitle2.sort(idComparator); + loadedDashboardsTitle2.sort(idComparator); Assert.assertEquals(dashboardsTitle2, loadedDashboardsTitle2); @@ -429,9 +437,9 @@ public class DashboardControllerTest extends AbstractControllerTest { .andExpect(status().isOk()); } - testNotifyManyEntityManyTimeMsgToEdgeServiceNeverAdditionalInfoAny(new Dashboard(), new Dashboard(), + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(new Dashboard(), new Dashboard(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.DELETED, cntEntity, 1); + ActionType.DELETED, ActionType.DELETED, cntEntity, cntEntity, 1); pageLink = new PageLink(4, 0, title1); pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", @@ -474,7 +482,7 @@ public class DashboardControllerTest extends AbstractControllerTest { testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Dashboard(), new Dashboard(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ADDED, ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity, cntEntity*2); + ActionType.ADDED, cntEntity, cntEntity, cntEntity*2); List loadedDashboards = new ArrayList<>(); PageLink pageLink = new PageLink(21); @@ -489,8 +497,8 @@ public class DashboardControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - Collections.sort(dashboards, idComparator); - Collections.sort(loadedDashboards, idComparator); + dashboards.sort(idComparator); + loadedDashboards.sort(idComparator); Assert.assertEquals(dashboards, loadedDashboards); } diff --git a/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java new file mode 100644 index 0000000000..fbeb1fd0d9 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/DeviceConnectivityControllerTest.java @@ -0,0 +1,947 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.controller; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.NullNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.TestPropertySource; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.DeviceProfileType; +import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; +import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.common.data.security.DeviceCredentialsType; +import org.thingsboard.server.dao.service.DaoSqlTest; + +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.CA_ROOT_CERT_PEM; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.COAP; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.COAPS; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.DOCKER; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTP; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTPS; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.MQTT; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.MQTTS; + +@TestPropertySource(properties = { + "device.connectivity.mqtts.pem_cert_file=/tmp/" + CA_ROOT_CERT_PEM +}) +@DaoSqlTest +public class DeviceConnectivityControllerTest extends AbstractControllerTest { + + private static final String DEVICE_TELEMETRY_TOPIC = "v1/devices/customTopic"; + private static final String CHECK_DOCUMENTATION = "Check documentation"; + + private static final String CERT = "-----BEGIN CERTIFICATE-----\n" + + "MIIBfzCCASmgAwIBAgIUC1dtaskm/SJLmFE2Ae+YojArg+swDQYJKoZIhvcNAQEL\n" + + "BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIzMDgyODEzMTAzM1oXDTI0MDgy\n" + + "NzEzMTAzM1owFDESMBAGA1UEAwwJbG9jYWxob3N0MFwwDQYJKoZIhvcNAQEBBQAD\n" + + "SwAwSAJBANpcs46MavFdv7onsxH178YgK5XbpMqzx8AKaLMP2X6UEXN0nlt5mpX5\n" + + "uCJmSwVaFn6lwTm8ThXFYOBydOQImIsCAwEAAaNTMFEwHQYDVR0OBBYEFDvN49bI\n" + + "LaWMmUZ+cMboWAaozfXTMB8GA1UdIwQYMBaAFDvN49bILaWMmUZ+cMboWAaozfXT\n" + + "MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADQQAhIQL8zPvIhQvHJocU\n" + + "tnSmDAE0iR2rJVkousA+LiORE9BnuBtBUEv5SvFUv3VYUWA0eYFoyatpDHByIm6e\n" + + "/+1c\n" + + "-----END CERTIFICATE-----\n"; + private static final String P_KEY = "-----BEGIN PRIVATE KEY-----\n" + + "MIIBVgIBADANBgkqhkiG9w0BAQEFAASCAUAwggE8AgEAAkEA2lyzjoxq8V2/uiez\n" + + "EfXvxiArldukyrPHwAposw/ZfpQRc3SeW3malfm4ImZLBVoWfqXBObxOFcVg4HJ0\n" + + "5AiYiwIDAQABAkEA1DYhPljSmc2dRcHNMphLtMWQ9iumpGRBrS2wgMzXdz2NF2+0\n" + + "4cicaaL06/Cw6XXx43s8cn7e1xZAkGtNRQuqMQIhAPbrqrcYsropURpI5HSemeha\n" + + "MJA3i67ZFaom39VSrNKJAiEA4mQ0qFKxFSh2xAOqDWDRkiCgdOS00J6hgrYJRPcI\n" + + "nXMCIQDBHGjkT72gGKYkT3PUvSGTdc3bTIXDFmZ6L3MJTGJ7OQIhAKO+6r9coCy3\n" + + "ib+ZDuSCRNK2upgR3B6Qvi020VmKfDa1AiBhCgpBlClv5OjnmC42EGxxFOaZtNQQ\n" + + "C3swkUdrR3pezg==\n" + + "-----END PRIVATE KEY-----\n"; + + private Tenant savedTenant; + private User tenantAdmin; + private DeviceProfileId mqttDeviceProfileId; + private DeviceProfileId coapDeviceProfileId; + + @Before + public void beforeTest() throws Exception { + loginSysAdmin(); + + ObjectNode config = JacksonUtil.newObjectNode(); + + ObjectNode http = JacksonUtil.newObjectNode(); + http.put("enabled", true); + http.put("host", ""); + http.put("port", 8080); + config.set("http", http); + + ObjectNode https = JacksonUtil.newObjectNode(); + https.put("enabled", true); + https.put("host", ""); + https.put("port", 444); + config.set("https", https); + + ObjectNode mqtt = JacksonUtil.newObjectNode(); + mqtt.put("enabled", true); + mqtt.put("host", ""); + mqtt.put("port", 1883); + config.set("mqtt", mqtt); + + ObjectNode mqtts = JacksonUtil.newObjectNode(); + mqtts.put("enabled", true); + mqtts.put("host", ""); + mqtts.put("port", 8883); + config.set("mqtts", mqtts); + + ObjectNode coap = JacksonUtil.newObjectNode(); + coap.put("enabled", true); + coap.put("host", ""); + coap.put("port", 5683); + config.set("coap", coap); + + ObjectNode coaps = JacksonUtil.newObjectNode(); + coaps.put("enabled", true); + coaps.put("host", ""); + coaps.put("port", 5684); + config.set("coaps", coaps); + + AdminSettings adminSettings = doGet("/api/admin/settings/connectivity", AdminSettings.class); + adminSettings.setJsonValue(config); + doPost("/api/admin/settings", adminSettings).andExpect(status().isOk()); + + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant"); + savedTenant = doPost("/api/tenant", tenant, Tenant.class); + Assert.assertNotNull(savedTenant); + + tenantAdmin = new User(); + tenantAdmin.setAuthority(Authority.TENANT_ADMIN); + tenantAdmin.setTenantId(savedTenant.getId()); + tenantAdmin.setEmail("tenant2@thingsboard.org"); + tenantAdmin.setFirstName("Joe"); + tenantAdmin.setLastName("Downs"); + + tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); + + DeviceProfile mqttProfile = new DeviceProfile(); + mqttProfile.setName("Mqtt device profile"); + mqttProfile.setType(DeviceProfileType.DEFAULT); + mqttProfile.setTransportType(DeviceTransportType.MQTT); + DeviceProfileData deviceProfileData = new DeviceProfileData(); + deviceProfileData.setConfiguration(new DefaultDeviceProfileConfiguration()); + MqttDeviceProfileTransportConfiguration transportConfiguration = new MqttDeviceProfileTransportConfiguration(); + transportConfiguration.setDeviceTelemetryTopic(DEVICE_TELEMETRY_TOPIC); + deviceProfileData.setTransportConfiguration(transportConfiguration); + mqttProfile.setProfileData(deviceProfileData); + mqttProfile.setDefault(false); + mqttProfile.setDefaultRuleChainId(null); + + mqttDeviceProfileId = doPost("/api/deviceProfile", mqttProfile, DeviceProfile.class).getId(); + + DeviceProfile coapProfile = new DeviceProfile(); + coapProfile.setName("Coap device profile"); + coapProfile.setType(DeviceProfileType.DEFAULT); + coapProfile.setTransportType(DeviceTransportType.COAP); + DeviceProfileData deviceProfileData2 = new DeviceProfileData(); + deviceProfileData2.setConfiguration(new DefaultDeviceProfileConfiguration()); + deviceProfileData2.setTransportConfiguration(new CoapDeviceProfileTransportConfiguration()); + coapProfile.setProfileData(deviceProfileData); + coapProfile.setDefault(false); + coapProfile.setDefaultRuleChainId(null); + + coapDeviceProfileId = doPost("/api/deviceProfile", coapProfile, DeviceProfile.class).getId(); + } + + @After + public void afterTest() throws Exception { + loginSysAdmin(); + + doDelete("/api/tenant/" + savedTenant.getId().getId()) + .andExpect(status().isOk()); + } + + @Test + public void testFetchPublishTelemetryCommandsForDefaultDevice() throws Exception { + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + Device savedDevice = doPost("/api/device", device, Device.class); + JsonNode commands = + doGetTyped("/api/device-connectivity/" + savedDevice.getId().getId(), new TypeReference<>() { + }); + + DeviceCredentials credentials = + doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); + + assertThat(commands).hasSize(3); + JsonNode httpCommands = commands.get(HTTP); + assertThat(httpCommands.get(HTTP).asText()).isEqualTo(String.format("curl -v -X POST http://localhost:8080/api/v1/%s/telemetry " + + "--header Content-Type:application/json --data \"{temperature:25}\"", + credentials.getCredentialsId())); + assertThat(httpCommands.get(HTTPS).asText()).isEqualTo(String.format("curl -v -X POST https://localhost:444/api/v1/%s/telemetry " + + "--header Content-Type:application/json --data \"{temperature:25}\"", + credentials.getCredentialsId())); + + + JsonNode mqttCommands = commands.get(MQTT); + assertThat(mqttCommands.get(MQTT).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 -h localhost -p 1883 -t v1/devices/me/telemetry " + + "-u \"%s\" -m \"{temperature:25}\"", + credentials.getCredentialsId())); + assertThat(mqttCommands.get(MQTTS).get(0).asText()).isEqualTo("curl -f -S -o " + CA_ROOT_CERT_PEM + " http://localhost:80/api/device-connectivity/mqtts/certificate/download"); + assertThat(mqttCommands.get(MQTTS).get(1).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 --cafile " + CA_ROOT_CERT_PEM + " -h localhost -p 8883 " + + "-t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); + + JsonNode dockerMqttCommands = commands.get(MQTT).get(DOCKER); + assertThat(dockerMqttCommands.get(MQTT).asText()).isEqualTo(String.format("docker run --rm -it --add-host=host.docker.internal:host-gateway thingsboard/mosquitto-clients mosquitto_pub -d -q 1 -h host.docker.internal" + + " -p 1883 -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"", + credentials.getCredentialsId())); + assertThat(dockerMqttCommands.get(MQTTS).asText()).isEqualTo(String.format("docker run --rm -it --add-host=host.docker.internal:host-gateway thingsboard/mosquitto-clients " + + "/bin/sh -c \"curl -f -S -o " + CA_ROOT_CERT_PEM + " http://localhost:80/api/device-connectivity/mqtts/certificate/download && " + + "mosquitto_pub -d -q 1 --cafile " + CA_ROOT_CERT_PEM + " -h host.docker.internal -p 8883 -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"\"", + credentials.getCredentialsId())); + + JsonNode linuxCoapCommands = commands.get(COAP); + assertThat(linuxCoapCommands.get(COAP).asText()).isEqualTo(String.format("coap-client -v 6 -m POST coap://localhost:5683/api/v1/%s/telemetry " + + "-t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(linuxCoapCommands.get(COAPS).asText()).isEqualTo(String.format("coap-client-openssl -v 6 -m POST coaps://localhost:5684/api/v1/%s/telemetry" + + " -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + + JsonNode dockerCoapCommands = commands.get(COAP).get(DOCKER); + assertThat(dockerCoapCommands.get(COAP).asText()).isEqualTo(String.format("docker run --rm -it --add-host=host.docker.internal:host-gateway" + + " thingsboard/coap-clients coap-client -v 6 -m POST coap://host.docker.internal:5683/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(dockerCoapCommands.get(COAPS).asText()).isEqualTo(String.format("docker run --rm -it --add-host=host.docker.internal:host-gateway" + + " thingsboard/coap-clients coap-client-openssl -v 6 -m POST coaps://host.docker.internal:5684/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + } + + @Test + public void testFetchPublishTelemetryCommandsForMqttDeviceWithAccessToken() throws Exception { + Device device = new Device(); + device.setName("My device"); + device.setDeviceProfileId(mqttDeviceProfileId); + + Device savedDevice = doPost("/api/device", device, Device.class); + DeviceCredentials credentials = + doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); + + JsonNode commands = + doGetTyped("/api/device-connectivity/" + savedDevice.getId().getId(), new TypeReference<>() { + }); + assertThat(commands).hasSize(1); + + JsonNode mqttCommands = commands.get(MQTT); + assertThat(mqttCommands.get(MQTT).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 -h localhost -p 1883 -t %s " + + "-u \"%s\" -m \"{temperature:25}\"", DEVICE_TELEMETRY_TOPIC, credentials.getCredentialsId())); + assertThat(mqttCommands.get(MQTTS).get(0).asText()).isEqualTo("curl -f -S -o " + CA_ROOT_CERT_PEM + " http://localhost:80/api/device-connectivity/mqtts/certificate/download"); + assertThat(mqttCommands.get(MQTTS).get(1).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 --cafile " + CA_ROOT_CERT_PEM + " -h localhost -p 8883 " + + "-t %s -u \"%s\" -m \"{temperature:25}\"", DEVICE_TELEMETRY_TOPIC, credentials.getCredentialsId())); + + JsonNode dockerMqttCommands = commands.get(MQTT).get(DOCKER); + assertThat(dockerMqttCommands.get(MQTT).asText()).isEqualTo(String.format("docker run --rm -it --add-host=host.docker.internal:host-gateway thingsboard/mosquitto-clients mosquitto_pub -d -q 1 -h host.docker.internal" + + " -p 1883 -t %s -u \"%s\" -m \"{temperature:25}\"", + DEVICE_TELEMETRY_TOPIC, credentials.getCredentialsId())); + assertThat(dockerMqttCommands.get(MQTTS).asText()).isEqualTo(String.format("docker run --rm -it --add-host=host.docker.internal:host-gateway thingsboard/mosquitto-clients " + + "/bin/sh -c \"curl -f -S -o " + CA_ROOT_CERT_PEM + " http://localhost:80/api/device-connectivity/mqtts/certificate/download && " + + "mosquitto_pub -d -q 1 --cafile " + CA_ROOT_CERT_PEM + " -h host.docker.internal -p 8883 -t %s -u \"%s\" -m \"{temperature:25}\"\"", + DEVICE_TELEMETRY_TOPIC, credentials.getCredentialsId())); + } + + @Test + public void testFetchGatewayDockerComposeFile() throws Exception { + String deviceName = "My device"; + Device device = new Device(); + device.setName(deviceName); + device.setType("default"); + ObjectNode additionalInfo = JacksonUtil.newObjectNode(); + additionalInfo.put("gateway", true); + device.setAdditionalInfo(additionalInfo); + Device savedDevice = doPost("/api/device", device, Device.class); + DeviceCredentials credentials = + doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); + + String commands = + doGet("/api/device-connectivity/gateway-launch/" + savedDevice.getId().getId() + "/docker-compose/download", String.class); + + assertThat(commands).isEqualTo(String.format("version: '3.4'\n" + + "services:\n" + + " # ThingsBoard IoT Gateway Service Configuration\n" + + " tb-gateway:\n" + + " image: thingsboard/tb-gateway\n" + + " container_name: tb-gateway\n" + + " restart: always\n" + + "\n" + + " # Ports bindings - required by some connectors\n" + + " ports:\n" + + " - \"5000:5000\" # Comment if you don't use REST connector and change if you use another port\n" + + " # Uncomment and modify the following ports based on connector usage:\n" + + "# - \"1052:1052\" # BACnet connector\n" + + "# - \"5026:5026\" # Modbus TCP connector (Modbus Slave)\n" + + "# - \"50000:50000/tcp\" # Socket connector with type TCP\n" + + "# - \"50000:50000/udp\" # Socket connector with type UDP\n" + + "\n" + + " # Necessary mapping for Linux\n" + + " extra_hosts:\n" + + " - \"host.docker.internal:host-gateway\"\n" + + "\n" + + " # Environment variables\n" + + " environment:\n" + + " - host=host.docker.internal\n" + + " - port=1883\n" + + " - accessToken=" + credentials.getCredentialsId() + "\n" + + "\n" + + " # Volumes bind\n" + + " volumes:\n" + + " - tb-gw-config:/thingsboard_gateway/config\n" + + " - tb-gw-logs:/thingsboard_gateway/logs\n" + + " - tb-gw-extensions:/thingsboard_gateway/extensions\n" + + "\n" + + "# Volumes declaration for configurations, extensions and configuration\n" + + "volumes:\n" + + " tb-gw-config:\n" + + " name: tb-gw-config\n" + + " tb-gw-logs:\n" + + " name: tb-gw-logs\n" + + " tb-gw-extensions:\n" + + " name: tb-gw-extensions\n")); + } + + @Test + public void testFetchPublishTelemetryCommandsForDeviceWithIpV6LocalhostAddress() throws Exception { + loginSysAdmin(); + setConnectivityHost("::1"); + loginTenantAdmin(); + + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + Device savedDevice = doPost("/api/device", device, Device.class); + DeviceCredentials credentials = + doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); + + JsonNode commands = + doGetTyped("/api/device-connectivity/" + savedDevice.getId().getId(), new TypeReference<>() { + }); + + assertThat(commands).hasSize(3); + JsonNode httpCommands = commands.get(HTTP); + assertThat(httpCommands.get(HTTP).asText()).isEqualTo(String.format("curl -v -X POST http://[::1]:8080/api/v1/%s/telemetry " + + "--header Content-Type:application/json --data \"{temperature:25}\"", + credentials.getCredentialsId())); + assertThat(httpCommands.get(HTTPS).asText()).isEqualTo(String.format("curl -v -X POST https://[::1]/api/v1/%s/telemetry " + + "--header Content-Type:application/json --data \"{temperature:25}\"", + credentials.getCredentialsId())); + + JsonNode mqttCommands = commands.get(MQTT); + assertThat(mqttCommands.get(MQTT).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 -h ::1 -p 1883 -t v1/devices/me/telemetry " + + "-u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(mqttCommands.get(MQTTS).get(0).asText()).isEqualTo("curl -f -S -o " + CA_ROOT_CERT_PEM + " http://localhost:80/api/device-connectivity/mqtts/certificate/download"); + assertThat(mqttCommands.get(MQTTS).get(1).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 --cafile " + CA_ROOT_CERT_PEM + " -h ::1 -p 8883 " + + "-t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); + + JsonNode dockerMqttCommands = commands.get(MQTT).get(DOCKER); + assertThat(dockerMqttCommands.get(MQTT).asText()).isEqualTo(String.format("docker run --rm -it --add-host=host.docker.internal:host-gateway thingsboard/mosquitto-clients mosquitto_pub -d -q 1 -h host.docker.internal" + + " -p 1883 -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(dockerMqttCommands.get(MQTTS).asText()).isEqualTo(String.format("docker run --rm -it --add-host=host.docker.internal:host-gateway thingsboard/mosquitto-clients " + + "/bin/sh -c \"curl -f -S -o " + CA_ROOT_CERT_PEM + " http://localhost:80/api/device-connectivity/mqtts/certificate/download && " + + "mosquitto_pub -d -q 1 --cafile " + CA_ROOT_CERT_PEM + " -h host.docker.internal -p 8883 -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"\"", + credentials.getCredentialsId())); + + JsonNode linuxCoapCommands = commands.get(COAP); + assertThat(linuxCoapCommands.get(COAP).asText()).isEqualTo(String.format("coap-client -v 6 -m POST coap://[::1]:5683/api/v1/%s/telemetry " + + "-t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(linuxCoapCommands.get(COAPS).asText()).isEqualTo(String.format("coap-client-openssl -v 6 -m POST coaps://[::1]:5684/api/v1/%s/telemetry" + + " -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + + JsonNode dockerCoapCommands = commands.get(COAP).get(DOCKER); + assertThat(dockerCoapCommands.get(COAP).asText()).isEqualTo(String.format("docker run --rm -it --add-host=host.docker.internal:host-gateway" + + " thingsboard/coap-clients coap-client -v 6 -m POST coap://host.docker.internal:5683/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(dockerCoapCommands.get(COAPS).asText()).isEqualTo(String.format("docker run --rm -it --add-host=host.docker.internal:host-gateway" + + " thingsboard/coap-clients coap-client-openssl -v 6 -m POST coaps://host.docker.internal:5684/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + } + + @Test + public void testFetchPublishTelemetryCommandsForDeviceWithIpV6Address() throws Exception { + + loginSysAdmin(); + setConnectivityHost("1:1:1:1:1:1:1:1"); + loginTenantAdmin(); + + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + Device savedDevice = doPost("/api/device", device, Device.class); + DeviceCredentials credentials = + doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); + + JsonNode commands = + doGetTyped("/api/device-connectivity/" + savedDevice.getId().getId(), new TypeReference<>() { + }); + assertThat(commands).hasSize(3); + JsonNode httpCommands = commands.get(HTTP); + assertThat(httpCommands.get(HTTP).asText()).isEqualTo(String.format("curl -v -X POST http://[1:1:1:1:1:1:1:1]:8080/api/v1/%s/telemetry " + + "--header Content-Type:application/json --data \"{temperature:25}\"", + credentials.getCredentialsId())); + assertThat(httpCommands.get(HTTPS).asText()).isEqualTo(String.format("curl -v -X POST https://[1:1:1:1:1:1:1:1]/api/v1/%s/telemetry " + + "--header Content-Type:application/json --data \"{temperature:25}\"", + credentials.getCredentialsId())); + + JsonNode mqttCommands = commands.get(MQTT); + assertThat(mqttCommands.get(MQTT).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 -h 1:1:1:1:1:1:1:1 -p 1883 -t v1/devices/me/telemetry " + + "-u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(mqttCommands.get(MQTTS).get(0).asText()).isEqualTo("curl -f -S -o " + CA_ROOT_CERT_PEM + " http://localhost:80/api/device-connectivity/mqtts/certificate/download"); + assertThat(mqttCommands.get(MQTTS).get(1).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 --cafile " + CA_ROOT_CERT_PEM + " -h 1:1:1:1:1:1:1:1 -p 8883 " + + "-t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); + + JsonNode dockerMqttCommands = commands.get(MQTT).get(DOCKER); + assertThat(dockerMqttCommands.get(MQTT).asText()).isEqualTo(String.format("docker run --rm -it thingsboard/mosquitto-clients mosquitto_pub -d -q 1 -h 1:1:1:1:1:1:1:1" + + " -p 1883 -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(dockerMqttCommands.get(MQTTS).asText()).isEqualTo(String.format("docker run --rm -it thingsboard/mosquitto-clients " + + "/bin/sh -c \"curl -f -S -o " + CA_ROOT_CERT_PEM + " http://localhost:80/api/device-connectivity/mqtts/certificate/download && " + + "mosquitto_pub -d -q 1 --cafile " + CA_ROOT_CERT_PEM + " -h 1:1:1:1:1:1:1:1 -p 8883 -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"\"", + credentials.getCredentialsId())); + + JsonNode linuxCoapCommands = commands.get(COAP); + assertThat(linuxCoapCommands.get(COAP).asText()).isEqualTo(String.format("coap-client -v 6 -m POST coap://[1:1:1:1:1:1:1:1]:5683/api/v1/%s/telemetry " + + "-t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(linuxCoapCommands.get(COAPS).asText()).isEqualTo(String.format("coap-client-openssl -v 6 -m POST coaps://[1:1:1:1:1:1:1:1]:5684/api/v1/%s/telemetry" + + " -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + + JsonNode dockerCoapCommands = commands.get(COAP).get(DOCKER); + assertThat(dockerCoapCommands.get(COAP).asText()).isEqualTo(String.format("docker run --rm -it" + + " thingsboard/coap-clients coap-client -v 6 -m POST coap://[1:1:1:1:1:1:1:1]:5683/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(dockerCoapCommands.get(COAPS).asText()).isEqualTo(String.format("docker run --rm -it" + + " thingsboard/coap-clients coap-client-openssl -v 6 -m POST coaps://[1:1:1:1:1:1:1:1]:5684/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + + } + + @Test + public void testFetchPublishTelemetryCommandsForDeviceWithMqttBasicCreds() throws Exception { + Device device = new Device(); + device.setName("My device"); + device.setDeviceProfileId(mqttDeviceProfileId); + + Device savedDevice = doPost("/api/device", device, Device.class); + DeviceCredentials credentials = + doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); + credentials.setCredentialsId(null); + credentials.setCredentialsType(DeviceCredentialsType.MQTT_BASIC); + BasicMqttCredentials basicMqttCredentials = new BasicMqttCredentials(); + String clientId = "testClientId"; + String userName = "testUsername"; + String password = "testPassword"; + basicMqttCredentials.setClientId(clientId); + basicMqttCredentials.setUserName(userName); + basicMqttCredentials.setPassword(password); + credentials.setCredentialsValue(JacksonUtil.toString(basicMqttCredentials)); + doPost("/api/device/credentials", credentials) + .andExpect(status().isOk()); + + JsonNode commands = + doGetTyped("/api/device-connectivity/" + savedDevice.getId().getId(), new TypeReference<>() { + }); + assertThat(commands).hasSize(1); + + JsonNode mqttCommands = commands.get(MQTT); + assertThat(mqttCommands.get(MQTT).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 -h localhost -p 1883 -t %s " + + "-i \"%s\" -u \"%s\" -P \"%s\" -m \"{temperature:25}\"", DEVICE_TELEMETRY_TOPIC, clientId, userName, password)); + assertThat(mqttCommands.get(MQTTS).get(0).asText()).isEqualTo("curl -f -S -o " + CA_ROOT_CERT_PEM + " http://localhost:80/api/device-connectivity/mqtts/certificate/download"); + assertThat(mqttCommands.get(MQTTS).get(1).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 --cafile " + CA_ROOT_CERT_PEM + " -h localhost -p 8883 " + + "-t %s -i \"%s\" -u \"%s\" -P \"%s\" -m \"{temperature:25}\"", DEVICE_TELEMETRY_TOPIC, clientId, userName, password)); + + JsonNode dockerMqttCommands = commands.get(MQTT).get(DOCKER); + assertThat(dockerMqttCommands.get(MQTT).asText()).isEqualTo(String.format("docker run --rm -it --add-host=host.docker.internal:host-gateway thingsboard/mosquitto-clients mosquitto_pub -d -q 1 -h host.docker.internal" + + " -p 1883 -t %s -i \"%s\" -u \"%s\" -P \"%s\" -m \"{temperature:25}\"", + DEVICE_TELEMETRY_TOPIC, clientId, userName, password)); + assertThat(dockerMqttCommands.get(MQTTS).asText()).isEqualTo(String.format("docker run --rm -it --add-host=host.docker.internal:host-gateway thingsboard/mosquitto-clients " + + "/bin/sh -c \"curl -f -S -o " + CA_ROOT_CERT_PEM + " http://localhost:80/api/device-connectivity/mqtts/certificate/download && " + + "mosquitto_pub -d -q 1 --cafile " + CA_ROOT_CERT_PEM + " -h host.docker.internal -p 8883 -t %s -i \"%s\" -u \"%s\" -P \"%s\" -m \"{temperature:25}\"\"", + DEVICE_TELEMETRY_TOPIC, clientId, userName, password)); + } + + @Test + public void testFetchPublishTelemetryCommandsForDeviceWithX509Creds() throws Exception { + Device device = new Device(); + device.setName("My device"); + device.setDeviceProfileId(mqttDeviceProfileId); + + Device savedDevice = doPost("/api/device", device, Device.class); + DeviceCredentials credentials = + doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); + credentials.setCredentialsId(null); + credentials.setCredentialsType(DeviceCredentialsType.X509_CERTIFICATE); + credentials.setCredentialsValue("testValue"); + doPost("/api/device/credentials", credentials) + .andExpect(status().isOk()); + + JsonNode commands = + doGetTyped("/api/device-connectivity/" + savedDevice.getId().getId(), new TypeReference<>() { + }); + assertThat(commands).hasSize(1); + assertThat(commands.get(MQTT).get(MQTTS).asText()).isEqualTo(CHECK_DOCUMENTATION); + assertThat(commands.get(MQTT).get(DOCKER)).isNull(); + } + + @Test + public void testFetchPublishTelemetryCommandsForCoapDevice() throws Exception { + Device device = new Device(); + device.setName("My device"); + device.setDeviceProfileId(coapDeviceProfileId); + + Device savedDevice = doPost("/api/device", device, Device.class); + DeviceCredentials credentials = + doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); + + JsonNode commands = + doGetTyped("/api/device-connectivity/" + savedDevice.getId().getId(), new TypeReference<>() { + }); + assertThat(commands).hasSize(1); + + JsonNode linuxCommands = commands.get(COAP); + assertThat(linuxCommands.get(COAP).asText()).isEqualTo(String.format("coap-client -v 6 -m POST coap://localhost:5683/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", + credentials.getCredentialsId())); + assertThat(linuxCommands.get(COAPS).asText()).isEqualTo(String.format("coap-client-openssl -v 6 -m POST coaps://localhost:5684/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", + credentials.getCredentialsId())); + } + + @Test + public void testFetchPublishTelemetryCommandsForCoapDeviceWithX509Creds() throws Exception { + Device device = new Device(); + device.setName("My device"); + device.setDeviceProfileId(coapDeviceProfileId); + + Device savedDevice = doPost("/api/device", device, Device.class); + DeviceCredentials credentials = + doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); + credentials.setCredentialsId(null); + credentials.setCredentialsType(DeviceCredentialsType.X509_CERTIFICATE); + credentials.setCredentialsValue("testValue"); + doPost("/api/device/credentials", credentials) + .andExpect(status().isOk()); + + JsonNode commands = + doGetTyped("/api/device-connectivity/" + savedDevice.getId().getId(), new TypeReference<>() { + }); + assertThat(commands).hasSize(1); + assertThat(commands.get(COAP).get(COAPS).asText()).isEqualTo(CHECK_DOCUMENTATION); + } + + @Test + @DirtiesContext + public void testDownloadMqttCert() throws Exception { + Path path = Files.createFile(Path.of("/tmp/" + CA_ROOT_CERT_PEM)); + Files.writeString(path, CERT); + + try { + String downloadedCert = doGet("/api/device-connectivity/mqtts/certificate/download", String.class); + Assert.assertEquals(CERT, downloadedCert); + } finally { + Files.deleteIfExists(path); + } + } + + @Test + @DirtiesContext + public void testDownloadMqttCertFromFileWithPrivateKey() throws Exception { + Path path = Files.createFile(Path.of("/tmp/" + CA_ROOT_CERT_PEM)); + Files.writeString(path, CERT + P_KEY); + + try { + String downloadedCert = doGet("/api/device-connectivity/mqtts/certificate/download", String.class); + Assert.assertEquals(CERT, downloadedCert); + } finally { + Files.deleteIfExists(path); + } + } + + @Test + @DirtiesContext + public void testDownloadMqttCertWithoutCertFile() throws Exception { + doGet("/api/device-connectivity/mqtts/certificate/download").andExpect(status().isNotFound()); + } + + @Test + @DirtiesContext + public void testDownloadCertWithUnknownProtocol() throws Exception { + doGet("/api/device-connectivity/unknownProtocol/certificate/download").andExpect(status().isNotFound()); + } + + @Test + public void testFetchPublishTelemetryCommandsForDefaultDeviceIfPortsSetToDefault() throws Exception { + loginSysAdmin(); + + ObjectNode config = JacksonUtil.newObjectNode(); + + ObjectNode http = JacksonUtil.newObjectNode(); + http.put("enabled", true); + http.put("host", ""); + http.put("port", 80); + config.set("http", http); + + ObjectNode https = JacksonUtil.newObjectNode(); + https.put("enabled", true); + https.put("host", ""); + https.put("port", 443); + config.set("https", https); + + ObjectNode mqtt = JacksonUtil.newObjectNode(); + mqtt.put("enabled", false); + mqtt.put("host", ""); + mqtt.put("port", 1883); + config.set("mqtt", mqtt); + + ObjectNode mqtts = JacksonUtil.newObjectNode(); + mqtts.put("enabled", false); + mqtts.put("host", ""); + mqtts.put("port", 8883); + config.set("mqtts", mqtts); + + ObjectNode coap = JacksonUtil.newObjectNode(); + coap.put("enabled", false); + coap.put("host", ""); + coap.put("port", 5683); + config.set("coap", coap); + + ObjectNode coaps = JacksonUtil.newObjectNode(); + coaps.put("enabled", false); + coaps.put("host", ""); + coaps.put("port", 5684); + config.set("coaps", coaps); + + AdminSettings adminSettings = doGet("/api/admin/settings/connectivity", AdminSettings.class); + adminSettings.setJsonValue(config); + doPost("/api/admin/settings", adminSettings).andExpect(status().isOk()); + + login("tenant2@thingsboard.org", "testPassword1"); + + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + Device savedDevice = doPost("/api/device", device, Device.class); + JsonNode commands = + doGetTyped("/api/device-connectivity/" + savedDevice.getId().getId(), new TypeReference<>() { + }); + + DeviceCredentials credentials = + doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); + + assertThat(commands).hasSize(1); + JsonNode httpCommands = commands.get(HTTP); + assertThat(httpCommands.get(HTTP).asText()).isEqualTo(String.format("curl -v -X POST http://localhost/api/v1/%s/telemetry " + + "--header Content-Type:application/json --data \"{temperature:25}\"", + credentials.getCredentialsId())); + assertThat(httpCommands.get(HTTPS).asText()).isEqualTo(String.format("curl -v -X POST https://localhost/api/v1/%s/telemetry " + + "--header Content-Type:application/json --data \"{temperature:25}\"", + credentials.getCredentialsId())); + } + + @Test + public void testFetchPublishTelemetryCommandsForDefaultDeviceIfPortsAndHostsNull() throws Exception { + loginSysAdmin(); + + ObjectNode config = JacksonUtil.newObjectNode(); + + ObjectNode http = JacksonUtil.newObjectNode(); + http.put("enabled", true); + http.set("host", null); + http.set("port", null); + config.set("http", http); + + ObjectNode https = JacksonUtil.newObjectNode(); + https.put("enabled", true); + https.set("host", null); + https.set("port", null); + config.set("https", https); + + ObjectNode mqtt = JacksonUtil.newObjectNode(); + mqtt.put("enabled", true); + mqtt.set("host", null); + mqtt.set("port", null); + config.set("mqtt", mqtt); + + ObjectNode mqtts = JacksonUtil.newObjectNode(); + mqtts.put("enabled", true); + mqtts.set("host", null); + mqtts.set("port", null); + config.set("mqtts", mqtts); + + ObjectNode coap = JacksonUtil.newObjectNode(); + coap.put("enabled", true); + coap.set("host", null); + coap.set("port", null); + config.set("coap", coap); + + ObjectNode coaps = JacksonUtil.newObjectNode(); + coaps.put("enabled", true); + coaps.set("host", null); + coaps.set("port", null); + config.set("coaps", coaps); + + AdminSettings adminSettings = doGet("/api/admin/settings/connectivity", AdminSettings.class); + adminSettings.setJsonValue(config); + doPost("/api/admin/settings", adminSettings).andExpect(status().isOk()); + + login("tenant2@thingsboard.org", "testPassword1"); + + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + Device savedDevice = doPost("/api/device", device, Device.class); + JsonNode commands = + doGetTyped("/api/device-connectivity/" + savedDevice.getId().getId(), new TypeReference<>() { + }); + + DeviceCredentials credentials = + doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); + + assertThat(commands).hasSize(3); + JsonNode httpCommands = commands.get(HTTP); + assertThat(httpCommands.get(HTTP).asText()).isEqualTo(String.format("curl -v -X POST http://localhost/api/v1/%s/telemetry " + + "--header Content-Type:application/json --data \"{temperature:25}\"", + credentials.getCredentialsId())); + assertThat(httpCommands.get(HTTPS).asText()).isEqualTo(String.format("curl -v -X POST https://localhost/api/v1/%s/telemetry " + + "--header Content-Type:application/json --data \"{temperature:25}\"", + credentials.getCredentialsId())); + + JsonNode mqttCommands = commands.get(MQTT); + assertThat(mqttCommands.get(MQTT).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 -h localhost -t v1/devices/me/telemetry " + + "-u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(mqttCommands.get(MQTTS).get(0).asText()).isEqualTo("curl -f -S -o " + CA_ROOT_CERT_PEM + " http://localhost:80/api/device-connectivity/mqtts/certificate/download"); + assertThat(mqttCommands.get(MQTTS).get(1).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 --cafile " + CA_ROOT_CERT_PEM + " -h localhost " + + "-t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); + + JsonNode dockerMqttCommands = commands.get(MQTT).get(DOCKER); + assertThat(dockerMqttCommands.get(MQTT).asText()).isEqualTo(String.format("docker run --rm -it --add-host=host.docker.internal:host-gateway thingsboard/mosquitto-clients mosquitto_pub -d -q 1 -h host.docker.internal" + + " -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(dockerMqttCommands.get(MQTTS).asText()).isEqualTo(String.format("docker run --rm -it --add-host=host.docker.internal:host-gateway thingsboard/mosquitto-clients " + + "/bin/sh -c \"curl -f -S -o " + CA_ROOT_CERT_PEM + " http://localhost:80/api/device-connectivity/mqtts/certificate/download && " + + "mosquitto_pub -d -q 1 --cafile " + CA_ROOT_CERT_PEM + " -h host.docker.internal -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"\"", credentials.getCredentialsId())); + + JsonNode linuxCoapCommands = commands.get(COAP); + assertThat(linuxCoapCommands.get(COAP).asText()).isEqualTo(String.format("coap-client -v 6 -m POST coap://localhost/api/v1/%s/telemetry " + + "-t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(linuxCoapCommands.get(COAPS).asText()).isEqualTo(String.format("coap-client-openssl -v 6 -m POST coaps://localhost/api/v1/%s/telemetry" + + " -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + + JsonNode dockerCoapCommands = commands.get(COAP).get(DOCKER); + assertThat(dockerCoapCommands.get(COAP).asText()).isEqualTo(String.format("docker run --rm -it --add-host=host.docker.internal:host-gateway" + + " thingsboard/coap-clients coap-client -v 6 -m POST coap://host.docker.internal/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(dockerCoapCommands.get(COAPS).asText()).isEqualTo(String.format("docker run --rm -it --add-host=host.docker.internal:host-gateway" + + " thingsboard/coap-clients coap-client-openssl -v 6 -m POST coaps://host.docker.internal/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + } + + @Test + public void testFetchPublishTelemetryCommandsForDefaultDeviceIfHostIsNotLocalhost() throws Exception { + loginSysAdmin(); + + setConnectivityHost("test.domain"); + + login("tenant2@thingsboard.org", "testPassword1"); + + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + Device savedDevice = doPost("/api/device", device, Device.class); + JsonNode commands = + doGetTyped("/api/device-connectivity/" + savedDevice.getId().getId(), new TypeReference<>() { + }); + + DeviceCredentials credentials = + doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); + + assertThat(commands).hasSize(3); + JsonNode httpCommands = commands.get(HTTP); + assertThat(httpCommands.get(HTTP).asText()).isEqualTo(String.format("curl -v -X POST http://test.domain:8080/api/v1/%s/telemetry " + + "--header Content-Type:application/json --data \"{temperature:25}\"", + credentials.getCredentialsId())); + assertThat(httpCommands.get(HTTPS).asText()).isEqualTo(String.format("curl -v -X POST https://test.domain/api/v1/%s/telemetry " + + "--header Content-Type:application/json --data \"{temperature:25}\"", + credentials.getCredentialsId())); + + + JsonNode mqttCommands = commands.get(MQTT); + assertThat(mqttCommands.get(MQTT).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 -h test.domain -p 1883 -t v1/devices/me/telemetry " + + "-u \"%s\" -m \"{temperature:25}\"", + credentials.getCredentialsId())); + assertThat(mqttCommands.get(MQTTS).get(0).asText()).isEqualTo("curl -f -S -o " + CA_ROOT_CERT_PEM + " http://localhost:80/api/device-connectivity/mqtts/certificate/download"); + assertThat(mqttCommands.get(MQTTS).get(1).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 --cafile " + CA_ROOT_CERT_PEM + " -h test.domain -p 8883 " + + "-t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); + + JsonNode dockerMqttCommands = commands.get(MQTT).get(DOCKER); + assertThat(dockerMqttCommands.get(MQTT).asText()).isEqualTo(String.format("docker run --rm -it thingsboard/mosquitto-clients mosquitto_pub -d -q 1 -h test.domain" + + " -p 1883 -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"", + credentials.getCredentialsId())); + assertThat(dockerMqttCommands.get(MQTTS).asText()).isEqualTo(String.format("docker run --rm -it thingsboard/mosquitto-clients " + + "/bin/sh -c \"curl -f -S -o " + CA_ROOT_CERT_PEM + " http://localhost:80/api/device-connectivity/mqtts/certificate/download && " + + "mosquitto_pub -d -q 1 --cafile " + CA_ROOT_CERT_PEM + " -h test.domain -p 8883 -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"\"", + credentials.getCredentialsId())); + + JsonNode linuxCoapCommands = commands.get(COAP); + assertThat(linuxCoapCommands.get(COAP).asText()).isEqualTo(String.format("coap-client -v 6 -m POST coap://test.domain:5683/api/v1/%s/telemetry " + + "-t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(linuxCoapCommands.get(COAPS).asText()).isEqualTo(String.format("coap-client-openssl -v 6 -m POST coaps://test.domain:5684/api/v1/%s/telemetry" + + " -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + + JsonNode dockerCoapCommands = commands.get(COAP).get(DOCKER); + assertThat(dockerCoapCommands.get(COAP).asText()).isEqualTo(String.format("docker run --rm -it " + + "thingsboard/coap-clients coap-client -v 6 -m POST coap://test.domain:5683/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(dockerCoapCommands.get(COAPS).asText()).isEqualTo(String.format("docker run --rm -it " + + "thingsboard/coap-clients coap-client-openssl -v 6 -m POST coaps://test.domain:5684/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + } + + @Test + public void testFetchPublishTelemetryCommandsForDeviceWhenHostSetToNullInSettings() throws Exception { + loginSysAdmin(); + ObjectNode config = JacksonUtil.newObjectNode(); + + ObjectNode http = JacksonUtil.newObjectNode(); + http.put("enabled", true); + http.put("host", " "); + http.put("port", 8080); + config.set("http", http); + + ObjectNode https = JacksonUtil.newObjectNode(); + https.put("enabled", true); + https.put("host", ""); + https.put("port", 443); + config.set("https", https); + + ObjectNode mqtt = JacksonUtil.newObjectNode(); + mqtt.put("enabled", true); + mqtt.set("host", NullNode.getInstance()); + mqtt.set("port", NullNode.getInstance()); + config.set("mqtt", mqtt); + + ObjectNode mqtts = JacksonUtil.newObjectNode(); + mqtts.put("enabled", true); + mqtts.put("host", ""); + mqtts.set("port", NullNode.getInstance()); + config.set("mqtts", mqtts); + + ObjectNode coap = JacksonUtil.newObjectNode(); + coap.put("enabled", true); + coap.set("host", NullNode.getInstance()); + coap.put("port", ""); + config.set("coap", coap); + + ObjectNode coaps = JacksonUtil.newObjectNode(); + coaps.put("enabled", true); + coaps.set("host", NullNode.getInstance()); + coaps.set("port", NullNode.getInstance()); + config.set("coaps", coaps); + + AdminSettings adminSettings = doGet("/api/admin/settings/connectivity", AdminSettings.class); + adminSettings.setJsonValue(config); + doPost("/api/admin/settings", adminSettings).andExpect(status().isOk()); + + + login("tenant2@thingsboard.org", "testPassword1"); + + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + Device savedDevice = doPost("/api/device", device, Device.class); + JsonNode commands = + doGetTyped("/api/device-connectivity/" + savedDevice.getId().getId(), new TypeReference<>() { + }); + + DeviceCredentials credentials = + doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); + + assertThat(commands).hasSize(3); + JsonNode httpCommands = commands.get(HTTP); + assertThat(httpCommands.get(HTTP).asText()).isEqualTo(String.format("curl -v -X POST http://localhost:8080/api/v1/%s/telemetry --header Content-Type:application/json --data \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(httpCommands.get(HTTPS).asText()).isEqualTo(String.format("curl -v -X POST https://localhost/api/v1/%s/telemetry --header Content-Type:application/json --data \"{temperature:25}\"", credentials.getCredentialsId())); + + JsonNode mqttCommands = commands.get(MQTT); + assertThat(mqttCommands.get(MQTT).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 -h localhost -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(mqttCommands.get(MQTTS).get(0).asText()).isEqualTo("curl -f -S -o " + CA_ROOT_CERT_PEM + " http://localhost:80/api/device-connectivity/mqtts/certificate/download"); + assertThat(mqttCommands.get(MQTTS).get(1).asText()).isEqualTo(String.format("mosquitto_pub -d -q 1 --cafile " + CA_ROOT_CERT_PEM + " -h localhost -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); + + JsonNode dockerMqttCommands = mqttCommands.get(DOCKER); + assertThat(dockerMqttCommands.get(MQTT).asText()).isEqualTo(String.format("docker run --rm -it --add-host=host.docker.internal:host-gateway thingsboard/mosquitto-clients mosquitto_pub -d -q 1 -h host.docker.internal -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(dockerMqttCommands.get(MQTTS).asText()).isEqualTo(String.format("docker run --rm -it --add-host=host.docker.internal:host-gateway thingsboard/mosquitto-clients /bin/sh -c \"curl -f -S -o " + CA_ROOT_CERT_PEM + " http://localhost:80/api/device-connectivity/mqtts/certificate/download && mosquitto_pub -d -q 1 --cafile " + CA_ROOT_CERT_PEM + " -h host.docker.internal -t v1/devices/me/telemetry -u \"%s\" -m \"{temperature:25}\"\"", credentials.getCredentialsId())); + + JsonNode coapCommands = commands.get(COAP); + assertThat(coapCommands.get(COAP).asText()).isEqualTo(String.format("coap-client -v 6 -m POST coap://localhost/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(coapCommands.get(COAPS).asText()).isEqualTo(String.format("coap-client-openssl -v 6 -m POST coaps://localhost/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + + JsonNode dockerCoapCommands = coapCommands.get(DOCKER); + assertThat(dockerCoapCommands.get(COAP).asText()).isEqualTo(String.format("docker run --rm -it --add-host=host.docker.internal:host-gateway thingsboard/coap-clients coap-client -v 6 -m POST coap://host.docker.internal/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + assertThat(dockerCoapCommands.get(COAPS).asText()).isEqualTo(String.format("docker run --rm -it --add-host=host.docker.internal:host-gateway thingsboard/coap-clients coap-client-openssl -v 6 -m POST coaps://host.docker.internal/api/v1/%s/telemetry -t json -e \"{temperature:25}\"", credentials.getCredentialsId())); + } + + + private void setConnectivityHost(String host) throws Exception { + ObjectNode config = JacksonUtil.newObjectNode(); + + ObjectNode http = JacksonUtil.newObjectNode(); + http.put("enabled", true); + http.put("host", host); + http.put("port", 8080); + config.set("http", http); + + ObjectNode https = JacksonUtil.newObjectNode(); + https.put("enabled", true); + https.put("host", host); + https.put("port", 443); + config.set("https", https); + + ObjectNode mqtt = JacksonUtil.newObjectNode(); + mqtt.put("enabled", true); + mqtt.put("host", host); + mqtt.put("port", 1883); + config.set("mqtt", mqtt); + + ObjectNode mqtts = JacksonUtil.newObjectNode(); + mqtts.put("enabled", true); + mqtts.put("host", host); + mqtts.put("port", 8883); + config.set("mqtts", mqtts); + + ObjectNode coap = JacksonUtil.newObjectNode(); + coap.put("enabled", true); + coap.put("host", host); + coap.put("port", 5683); + config.set("coap", coap); + + ObjectNode coaps = JacksonUtil.newObjectNode(); + coaps.put("enabled", true); + coaps.put("host", host); + coaps.put("port", 5684); + config.set("coaps", coaps); + + AdminSettings adminSettings = doGet("/api/admin/settings/connectivity", AdminSettings.class); + adminSettings.setJsonValue(config); + doPost("/api/admin/settings", adminSettings).andExpect(status().isOk()); + } +} diff --git a/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java index 11d11eccdd..7cafe2bbcb 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java @@ -28,12 +28,14 @@ import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.mockito.AdditionalAnswers; +import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.test.context.ContextConfiguration; +import org.testcontainers.shaded.org.awaitility.Awaitility; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.common.data.Customer; @@ -48,6 +50,9 @@ import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmInfo; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.CustomerId; @@ -69,7 +74,9 @@ import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.service.gateway_device.GatewayNotificationsService; +import org.thingsboard.server.service.state.DeviceStateService; import java.util.ArrayList; import java.util.List; @@ -78,6 +85,7 @@ import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -102,6 +110,9 @@ public class DeviceControllerTest extends AbstractControllerTest { @SpyBean private GatewayNotificationsService gatewayNotificationsService; + @SpyBean + private DeviceStateService deviceStateService; + @Autowired private DeviceDao deviceDao; @@ -156,9 +167,8 @@ public class DeviceControllerTest extends AbstractControllerTest { Device oldDevice = new Device(savedDevice); - testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDevice, savedDevice.getId(), savedDevice.getId(), - savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ADDED); + testNotifyEntityAllOneTime(savedDevice, savedDevice.getId(), savedDevice.getId(), savedTenant.getId(), + tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED); testNotificationUpdateGatewayNever(); Assert.assertNotNull(savedDevice); @@ -212,7 +222,7 @@ public class DeviceControllerTest extends AbstractControllerTest { Device oldDevice = new Device(savedDevice); - testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDevice, savedDevice.getId(), savedDevice.getId(), + testNotifyEntityAllOneTime(savedDevice, savedDevice.getId(), savedDevice.getId(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED); testNotificationUpdateGatewayNever(); @@ -244,6 +254,53 @@ public class DeviceControllerTest extends AbstractControllerTest { testNotificationUpdateGatewayOneTime(savedDevice, oldDevice); } + @Test + public void testSaveDeviceWithCredentials_CredentialsIsNull() throws Exception { + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + + SaveDeviceWithCredentialsRequest saveRequest = new SaveDeviceWithCredentialsRequest(device, null); + doPost("/api/device-with-credentials", saveRequest).andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Validation error: credentials must not be null"))); + } + + @Test + public void testSaveDeviceWithCredentials_DeviceIsNull() throws Exception { + String testToken = "TEST_TOKEN"; + + DeviceCredentials deviceCredentials = new DeviceCredentials(); + deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); + deviceCredentials.setCredentialsId(testToken); + + SaveDeviceWithCredentialsRequest saveRequest = new SaveDeviceWithCredentialsRequest(null, deviceCredentials); + doPost("/api/device-with-credentials", saveRequest).andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Validation error: device must not be null"))); + } + + @Test + public void testSaveDeviceWithCredentials_WithExistingName() throws Exception { + String testToken = "TEST_TOKEN"; + + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + + DeviceCredentials deviceCredentials = new DeviceCredentials(); + deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); + deviceCredentials.setCredentialsId(testToken); + + SaveDeviceWithCredentialsRequest saveRequest = new SaveDeviceWithCredentialsRequest(device, deviceCredentials); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + Device savedDevice = readResponse(doPost("/api/device-with-credentials", saveRequest).andExpect(status().isOk()), Device.class); + Assert.assertNotNull(savedDevice); + + doPost("/api/device-with-credentials", saveRequest).andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Device with such name already exists!"))); + } + @Test public void saveDeviceWithViolationOfValidation() throws Exception { Device device = new Device(); @@ -389,6 +446,9 @@ public class DeviceControllerTest extends AbstractControllerTest { @Test public void testFindDeviceTypesByTenantId() throws Exception { + DeviceProfile deviceProfile = createDeviceProfile("typeB"); + deviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); + List devices = new ArrayList<>(); int cntEntity = 3; @@ -399,12 +459,13 @@ public class DeviceControllerTest extends AbstractControllerTest { Device device = new Device(); device.setName("My device B" + i); device.setType("typeB"); + device.setDeviceProfileId(deviceProfile.getId()); devices.add(doPost("/api/device", device, Device.class)); } - testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new Device(), new Device(), + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Device(), new Device(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ADDED, cntEntity); + ActionType.ADDED, cntEntity, cntEntity, cntEntity); testNotificationUpdateGatewayNever(); for (int i = 0; i < 7; i++) { @@ -444,7 +505,7 @@ public class DeviceControllerTest extends AbstractControllerTest { doDelete("/api/device/" + savedDevice.getId().getId()) .andExpect(status().isOk()); - testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDevice, savedDevice.getId(), savedDevice.getId(), savedTenant.getId(), + testNotifyEntityAllOneTime(savedDevice, savedDevice.getId(), savedDevice.getId(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.DELETED, savedDevice.getId().getId().toString()); testNotificationDeleteGatewayOneTime(savedDevice); @@ -454,6 +515,39 @@ public class DeviceControllerTest extends AbstractControllerTest { .andExpect(statusReason(containsString(msgErrorNoFound("Device", savedDeviceId.getId().toString())))); } + @Test + public void testDeleteDeviceWithAlarmsAndAlarmTypes() throws Exception { + Device device = new Device(); + device.setName("My device"); + device.setType("default"); + Device savedDevice = doPost("/api/device", device, Device.class); + + Alarm alarm = Alarm.builder() + .tenantId(tenantId) + .originator(savedDevice.getId()) + .severity(AlarmSeverity.CRITICAL) + .type("test_type") + .build(); + + alarm = doPost("/api/alarm", alarm, Alarm.class); + Assert.assertNotNull(alarm); + + AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); + Assert.assertNotNull(foundAlarm); + + doDelete("/api/device/" + savedDevice.getId().getId().toString()) + .andExpect(status().isOk()); + + String DeviceIdStr = savedDevice.getId().getId().toString(); + doGet("/api/device/" + DeviceIdStr) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Device", DeviceIdStr)))); + + doGet("/api/device/info/" + alarm.getId()) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Device", alarm.getId().getId().toString())))); + } + @Test public void testSaveDeviceWithEmptyType() throws Exception { Device device = new Device(); @@ -464,7 +558,7 @@ public class DeviceControllerTest extends AbstractControllerTest { Device savedDevice = doPost("/api/device", device, Device.class); Assert.assertEquals("default", savedDevice.getType()); - testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDevice, savedDevice.getId(), savedDevice.getId(), + testNotifyEntityAllOneTime(savedDevice, savedDevice.getId(), savedDevice.getId(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED); testNotificationUpdateGatewayNever(); @@ -504,9 +598,9 @@ public class DeviceControllerTest extends AbstractControllerTest { + "/device/" + savedDevice.getId().getId(), Device.class); Assert.assertEquals(savedCustomer.getId(), assignedDevice.getCustomerId()); - testNotifyEntityAllOneTime(assignedDevice, assignedDevice.getId(), assignedDevice.getId(), savedTenant.getId(), + testNotifyAssignUnassignEntityAllOneTime(assignedDevice, assignedDevice.getId(), assignedDevice.getId(), savedTenant.getId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ASSIGNED_TO_CUSTOMER, - assignedDevice.getId().getId().toString(), savedCustomer.getId().getId().toString(), + ActionType.UPDATED, assignedDevice.getId().getId().toString(), savedCustomer.getId().getId().toString(), savedCustomer.getTitle()); testNotificationUpdateGatewayNever(); @@ -519,9 +613,9 @@ public class DeviceControllerTest extends AbstractControllerTest { doDelete("/api/customer/device/" + savedDevice.getId().getId(), Device.class); Assert.assertEquals(ModelConstants.NULL_UUID, unassignedDevice.getCustomerId().getId()); - testNotifyEntityAllOneTime(unassignedDevice, unassignedDevice.getId(), unassignedDevice.getId(), savedTenant.getId(), + testNotifyAssignUnassignEntityAllOneTime(unassignedDevice, unassignedDevice.getId(), unassignedDevice.getId(), savedTenant.getId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UNASSIGNED_FROM_CUSTOMER, - unassignedDevice.getId().getId().toString(), savedCustomer.getId().getId().toString(), + ActionType.UPDATED, unassignedDevice.getId().getId().toString(), savedCustomer.getId().getId().toString(), savedCustomer.getTitle()); testNotificationDeleteGatewayNever(); @@ -543,9 +637,9 @@ public class DeviceControllerTest extends AbstractControllerTest { Customer publicCustomer = doGet("/api/customer/" + assignedDevice.getCustomerId(), Customer.class); Assert.assertTrue(publicCustomer.isPublic()); - testNotifyEntityAllOneTime(assignedDevice, assignedDevice.getId(), assignedDevice.getId(), savedTenant.getId(), + testNotifyAssignUnassignEntityAllOneTime(assignedDevice, assignedDevice.getId(), assignedDevice.getId(), savedTenant.getId(), publicCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ASSIGNED_TO_CUSTOMER, - assignedDevice.getId().getId().toString(), publicCustomer.getId().getId().toString(), + ActionType.UPDATED, assignedDevice.getId().getId().toString(), publicCustomer.getId().getId().toString(), publicCustomer.getTitle()); testNotificationUpdateGatewayNever(); @@ -558,9 +652,9 @@ public class DeviceControllerTest extends AbstractControllerTest { doDelete("/api/customer/device/" + savedDevice.getId().getId(), Device.class); Assert.assertEquals(ModelConstants.NULL_UUID, unassignedDevice.getCustomerId().getId()); - testNotifyEntityAllOneTime(unassignedDevice, unassignedDevice.getId(), unassignedDevice.getId(), savedTenant.getId(), + testNotifyAssignUnassignEntityAllOneTime(unassignedDevice, unassignedDevice.getId(), unassignedDevice.getId(), savedTenant.getId(), publicCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UNASSIGNED_FROM_CUSTOMER, - unassignedDevice.getId().getId().toString(), publicCustomer.getId().getId().toString(), + ActionType.UPDATED, unassignedDevice.getId().getId().toString(), publicCustomer.getId().getId().toString(), publicCustomer.getTitle()); testNotificationDeleteGatewayNever(); @@ -795,9 +889,9 @@ public class DeviceControllerTest extends AbstractControllerTest { List devices = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); - testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new Device(), new Device(), + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Device(), new Device(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ADDED, cntEntity); + ActionType.ADDED, cntEntity, cntEntity, cntEntity); testNotificationUpdateGatewayNever(); List loadedDevices = new ArrayList<>(cntEntity); @@ -818,9 +912,9 @@ public class DeviceControllerTest extends AbstractControllerTest { deleteEntitiesAsync("/api/device/", loadedDevices, executor).get(TIMEOUT, TimeUnit.SECONDS); - testNotifyManyEntityManyTimeMsgToEdgeServiceNeverAdditionalInfoAny(new Device(), new Device(), + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(new Device(), new Device(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.DELETED, cntEntity, 1); + ActionType.DELETED, ActionType.DELETED, cntEntity, cntEntity,1); testNotificationUpdateGatewayNever(); } @@ -1005,7 +1099,7 @@ public class DeviceControllerTest extends AbstractControllerTest { testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Device(), new Device(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ADDED, ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity, cntEntity * 2); + ActionType.ADDED, cntEntity, cntEntity, cntEntity * 2); Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); testNotificationUpdateGatewayNever(); Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); @@ -1027,7 +1121,7 @@ public class DeviceControllerTest extends AbstractControllerTest { testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(new Device(), new Device(), savedTenant.getId(), customerId, tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.UNASSIGNED_FROM_CUSTOMER, ActionType.UNASSIGNED_FROM_CUSTOMER, cntEntity, cntEntity, 3); + ActionType.UNASSIGNED_FROM_CUSTOMER, ActionType.UPDATED, cntEntity, cntEntity, 3); testNotificationUpdateGatewayNever(); } @@ -1253,6 +1347,28 @@ public class DeviceControllerTest extends AbstractControllerTest { ActionType.ASSIGNED_TO_TENANT, savedDifferentTenant.getId().getId().toString(), savedDifferentTenant.getTitle()); testNotificationUpdateGatewayNever(); + ArgumentCaptor protoCaptor = ArgumentCaptor.forClass(TransportProtos.DeviceStateServiceMsgProto.class); + + Awaitility.await().atMost(5, TimeUnit.SECONDS).until(() -> { + Mockito.verify(deviceStateService, Mockito.atLeastOnce()).onQueueMsg(protoCaptor.capture(), any()); + return protoCaptor.getAllValues().stream().anyMatch(proto -> + proto.getTenantIdMSB() == savedTenant.getUuidId().getMostSignificantBits() && + proto.getTenantIdLSB() == savedTenant.getUuidId().getLeastSignificantBits() && + proto.getDeviceIdMSB() == savedDevice.getUuidId().getMostSignificantBits() && + proto.getDeviceIdLSB() == savedDevice.getUuidId().getLeastSignificantBits() && + proto.getDeleted()); + }); + + Awaitility.await().atMost(5, TimeUnit.SECONDS).until(() -> { + Mockito.verify(deviceStateService, Mockito.atLeastOnce()).onQueueMsg(protoCaptor.capture(), any()); + return protoCaptor.getAllValues().stream().anyMatch(proto -> + proto.getTenantIdMSB() == savedDifferentTenant.getUuidId().getMostSignificantBits() && + proto.getTenantIdLSB() == savedDifferentTenant.getUuidId().getLeastSignificantBits() && + proto.getDeviceIdMSB() == savedDevice.getUuidId().getMostSignificantBits() && + proto.getDeviceIdLSB() == savedDevice.getUuidId().getLeastSignificantBits() && + proto.getAdded()); + }); + login("tenant9@thingsboard.org", "testPassword1"); Device foundDevice1 = doGet("/api/device/" + assignedDevice.getId().getId(), Device.class); diff --git a/application/src/test/java/org/thingsboard/server/controller/DeviceProfileControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/DeviceProfileControllerTest.java index d2bcfe2b88..7cdfa10ea0 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DeviceProfileControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DeviceProfileControllerTest.java @@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceProfileProvisionType; import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest; import org.thingsboard.server.common.data.StringUtils; @@ -55,11 +56,13 @@ import org.thingsboard.server.dao.service.DaoSqlTest; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.common.data.DataConstants.DEFAULT_DEVICE_TYPE; import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; @@ -498,7 +501,7 @@ public class DeviceProfileControllerTest extends AbstractControllerTest { testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new DeviceProfile(), new DeviceProfile(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ADDED, ActionType.ADDED, cntEntity, cntEntity, cntEntity); + ActionType.ADDED, cntEntity, cntEntity, cntEntity); Mockito.reset(tbClusterService, auditLogService); List loadedDeviceProfiles = new ArrayList<>(); @@ -527,7 +530,7 @@ public class DeviceProfileControllerTest extends AbstractControllerTest { testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(loadedDeviceProfiles.get(0), loadedDeviceProfiles.get(0), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.DELETED, ActionType.DELETED, cntEntity, cntEntity, cntEntity, loadedDeviceProfiles.get(0).getId().getId().toString()); + ActionType.DELETED, cntEntity, cntEntity, cntEntity, loadedDeviceProfiles.get(0).getId().getId().toString()); pageLink = new PageLink(17); pageData = doGetTypedWithPageLink("/api/deviceProfiles?", @@ -1045,6 +1048,62 @@ public class DeviceProfileControllerTest extends AbstractControllerTest { testEntityDaoWithRelationsTransactionalException(deviceProfileDao, savedTenant.getId(), deviceProfileId, "/api/deviceProfile/" + deviceProfileId); } + @Test + public void testGetDeviceProfileNames() throws Exception { + var pageLink = new PageLink(Integer.MAX_VALUE); + var deviceProfileInfos = doGetTypedWithPageLink("/api/deviceProfileInfos?", + new TypeReference>() { + }, pageLink); + Assert.assertNotNull("Device Profile Infos page data is null!", deviceProfileInfos); + Assert.assertEquals("Device Profile Infos Page data is empty! Expected to have default profile created!", 1, deviceProfileInfos.getTotalElements()); + List expectedDeviceProfileNames = deviceProfileInfos.getData().stream() + .map(info -> new EntityInfo(info.getId(), info.getName())) + .sorted(Comparator.comparing(EntityInfo::getName)) + .collect(Collectors.toList()); + var deviceProfileNames = doGetTyped("/api/deviceProfile/names", new TypeReference>() { + }); + Assert.assertNotNull("Device Profile Names list is null!", deviceProfileNames); + Assert.assertFalse("Device Profile Names list is empty!", deviceProfileNames.isEmpty()); + Assert.assertEquals(expectedDeviceProfileNames, deviceProfileNames); + Assert.assertEquals(1, deviceProfileNames.size()); + Assert.assertEquals(DEFAULT_DEVICE_TYPE, deviceProfileNames.get(0).getName()); + + int count = 3; + for (int i = 0; i < count; i++) { + Device device = new Device(); + device.setName("DeviceName" + i); + device.setType("DeviceProfileName" + i); + Device savedDevice = doPost("/api/device", device, Device.class); + Assert.assertNotNull(savedDevice); + } + deviceProfileInfos = doGetTypedWithPageLink("/api/deviceProfileInfos?", + new TypeReference<>() { + }, pageLink); + Assert.assertNotNull("Device Profile Infos page data is null!", deviceProfileInfos); + Assert.assertEquals("Device Profile Infos Page data is empty! Expected to have default profile created + count value!", 1 + count, deviceProfileInfos.getTotalElements()); + expectedDeviceProfileNames = deviceProfileInfos.getData().stream() + .map(info -> new EntityInfo(info.getId(), info.getName())) + .sorted(Comparator.comparing(EntityInfo::getName)) + .collect(Collectors.toList()); + + deviceProfileNames = doGetTyped("/api/deviceProfile/names", new TypeReference<>() { + }); + Assert.assertNotNull("Device Profile Names list is null!", deviceProfileNames); + Assert.assertFalse("Device Profile Names list is empty!", deviceProfileNames.isEmpty()); + Assert.assertEquals(expectedDeviceProfileNames, deviceProfileNames); + Assert.assertEquals(1 + count, deviceProfileNames.size()); + + deviceProfileNames = doGetTyped("/api/deviceProfile/names?activeOnly=true", new TypeReference<>() { + }); + Assert.assertNotNull("Device Profile Names list is null!", deviceProfileNames); + Assert.assertFalse("Device Profile Names list is empty!", deviceProfileNames.isEmpty()); + var expectedDeviceProfileNamesWithoutDefault = expectedDeviceProfileNames.stream() + .filter(entityInfo -> !entityInfo.getName().equals(DEFAULT_DEVICE_TYPE)) + .collect(Collectors.toList()); + Assert.assertEquals(expectedDeviceProfileNamesWithoutDefault, deviceProfileNames); + Assert.assertEquals(count, deviceProfileNames.size()); + } + private DeviceProfile savedDeviceProfile(String name) { DeviceProfile deviceProfile = createDeviceProfile(name); return doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); diff --git a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java index c8a20a38f5..a0b0455b9e 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java @@ -17,10 +17,14 @@ package org.thingsboard.server.controller; import com.datastax.oss.driver.api.core.uuid.Uuids; import com.fasterxml.jackson.core.type.TypeReference; +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.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; +import com.google.protobuf.AbstractMessage; +import lombok.extern.slf4j.Slf4j; +import org.awaitility.Awaitility; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -33,22 +37,33 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.EdgeUpgradeInfo; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.queue.Queue; +import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.security.model.JwtSettings; import org.thingsboard.server.dao.edge.EdgeDao; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.model.ModelConstants; @@ -60,26 +75,36 @@ import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; +import org.thingsboard.server.gen.edge.v1.SyncCompletedMsg; +import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg; import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; +import org.thingsboard.server.service.edge.instructions.EdgeUpgradeInstructionsService; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.concurrent.TimeUnit; -import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; @TestPropertySource(properties = { "edges.enabled=true", + "queue.rule-engine.stats.enabled=false" }) @ContextConfiguration(classes = {EdgeControllerTest.Config.class}) @DaoSqlTest +@Slf4j public class EdgeControllerTest extends AbstractControllerTest { public static final String EDGE_HOST = "localhost"; @@ -87,10 +112,6 @@ public class EdgeControllerTest extends AbstractControllerTest { private IdComparator idComparator = new IdComparator<>(); - private Tenant savedTenant; - private TenantId tenantId; - private User tenantAdmin; - ListeningExecutorService executor; List> futures; @@ -98,6 +119,9 @@ public class EdgeControllerTest extends AbstractControllerTest { @Autowired private EdgeDao edgeDao; + @Autowired + private EdgeUpgradeInstructionsService edgeUpgradeInstructionsService; + static class Config { @Bean @Primary @@ -107,35 +131,14 @@ public class EdgeControllerTest extends AbstractControllerTest { } @Before - public void beforeTest() throws Exception { + public void setupEdgeTest() throws Exception { executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass())); - - loginSysAdmin(); - - Tenant tenant = new Tenant(); - tenant.setTitle("My tenant for Edge"); - savedTenant = doPost("/api/tenant", tenant, Tenant.class); - tenantId = savedTenant.getId(); - Assert.assertNotNull(savedTenant); - - tenantAdmin = new User(); - tenantAdmin.setAuthority(Authority.TENANT_ADMIN); - tenantAdmin.setTenantId(savedTenant.getId()); - tenantAdmin.setEmail("tenant2@thingsboard.org"); - tenantAdmin.setFirstName("Joe"); - tenantAdmin.setLastName("Downs"); - - tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); + loginTenantAdmin(); } @After - public void afterTest() throws Exception { + public void teardownEdgeTest() { executor.shutdownNow(); - - loginSysAdmin(); - - doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) - .andExpect(status().isOk()); } @Test @@ -149,13 +152,13 @@ public class EdgeControllerTest extends AbstractControllerTest { Assert.assertNotNull(savedEdge); Assert.assertNotNull(savedEdge.getId()); Assert.assertTrue(savedEdge.getCreatedTime() > 0); - Assert.assertEquals(savedTenant.getId(), savedEdge.getTenantId()); + Assert.assertEquals(tenantId, savedEdge.getTenantId()); Assert.assertNotNull(savedEdge.getCustomerId()); Assert.assertEquals(NULL_UUID, savedEdge.getCustomerId().getId()); Assert.assertEquals(edge.getName(), savedEdge.getName()); testNotifyEntityBroadcastEntityStateChangeEventOneTimeMsgToEdgeServiceNever(savedEdge, savedEdge.getId(), savedEdge.getId(), - savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + tenantId, tenantAdminUser.getCustomerId(), tenantAdminUser.getId(), tenantAdminUser.getEmail(), ActionType.ADDED); savedEdge.setName("My new edge"); @@ -165,7 +168,7 @@ public class EdgeControllerTest extends AbstractControllerTest { Assert.assertEquals(foundEdge.getName(), savedEdge.getName()); testNotifyEntityBroadcastEntityStateChangeEventOneTimeMsgToEdgeServiceNever(foundEdge, foundEdge.getId(), foundEdge.getId(), - savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + tenantId, tenantAdminUser.getCustomerId(), tenantAdminUser.getId(), tenantAdminUser.getEmail(), ActionType.UPDATED); } @@ -180,8 +183,8 @@ public class EdgeControllerTest extends AbstractControllerTest { .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(msgError))); - testNotifyEntityEqualsOneTimeServiceNeverError(edge, savedTenant.getId(), - tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); + testNotifyEntityEqualsOneTimeServiceNeverError(edge, tenantId, + tenantAdminUser.getId(), tenantAdminUser.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); Mockito.reset(tbClusterService, auditLogService); msgError = msgErrorFieldLength("type"); @@ -191,8 +194,8 @@ public class EdgeControllerTest extends AbstractControllerTest { .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(msgError))); - testNotifyEntityEqualsOneTimeServiceNeverError(edge, savedTenant.getId(), - tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); + testNotifyEntityEqualsOneTimeServiceNeverError(edge, tenantId, + tenantAdminUser.getId(), tenantAdminUser.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); Mockito.reset(tbClusterService, auditLogService); msgError = msgErrorFieldLength("label"); @@ -202,8 +205,8 @@ public class EdgeControllerTest extends AbstractControllerTest { .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(msgError))); - testNotifyEntityEqualsOneTimeServiceNeverError(edge, savedTenant.getId(), - tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); + testNotifyEntityEqualsOneTimeServiceNeverError(edge, tenantId, + tenantAdminUser.getId(), tenantAdminUser.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); } @Test @@ -217,28 +220,26 @@ public class EdgeControllerTest extends AbstractControllerTest { @Test public void testFindEdgeTypesByTenantId() throws Exception { - List edges = new ArrayList<>(); - int cntEntity = 3; Mockito.reset(tbClusterService, auditLogService); for (int i = 0; i < cntEntity; i++) { Edge edge = constructEdge("My edge B" + i, "typeB"); - edges.add(doPost("/api/edge", edge, Edge.class)); + doPost("/api/edge", edge, Edge.class); } testNotifyManyEntityManyTimeMsgToEdgeServiceNeverAdditionalInfoAny(new Edge(), new Edge(), - savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + tenantId, tenantAdminUser.getCustomerId(), tenantAdminUser.getId(), tenantAdminUser.getEmail(), ActionType.ADDED, cntEntity, 0); for (int i = 0; i < 7; i++) { Edge edge = constructEdge("My edge C" + i, "typeC"); - edges.add(doPost("/api/edge", edge, Edge.class)); + doPost("/api/edge", edge, Edge.class); } for (int i = 0; i < 9; i++) { Edge edge = constructEdge("My edge A" + i, "typeA"); - edges.add(doPost("/api/edge", edge, Edge.class)); + doPost("/api/edge", edge, Edge.class); } List edgeTypes = doGetTyped("/api/edge/types", new TypeReference<>() { @@ -262,7 +263,7 @@ public class EdgeControllerTest extends AbstractControllerTest { .andExpect(status().isOk()); testNotifyEntityBroadcastEntityStateChangeEventOneTimeMsgToEdgeServiceNever(savedEdge, savedEdge.getId(), savedEdge.getId(), - savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + tenantId, tenantAdminUser.getCustomerId(), tenantAdminUser.getId(), tenantAdminUser.getEmail(), ActionType.DELETED, savedEdge.getId().getId().toString()); doGet("/api/edge/" + savedEdge.getId().getId().toString()) @@ -281,8 +282,8 @@ public class EdgeControllerTest extends AbstractControllerTest { .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(msgError))); - testNotifyEntityEqualsOneTimeServiceNeverError(edge, savedTenant.getId(), - tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); + testNotifyEntityEqualsOneTimeServiceNeverError(edge, tenantId, + tenantAdminUser.getId(), tenantAdminUser.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); } @Test @@ -296,8 +297,8 @@ public class EdgeControllerTest extends AbstractControllerTest { .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(msgError))); - testNotifyEntityEqualsOneTimeServiceNeverError(edge, savedTenant.getId(), - tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); + testNotifyEntityEqualsOneTimeServiceNeverError(edge, tenantId, + tenantAdminUser.getId(), tenantAdminUser.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); } @Test @@ -316,8 +317,8 @@ public class EdgeControllerTest extends AbstractControllerTest { Assert.assertEquals(savedCustomer.getId(), assignedEdge.getCustomerId()); testNotifyEntityAllOneTimeLogEntityActionEntityEqClass(assignedEdge, assignedEdge.getId(), assignedEdge.getId(), - savedTenant.getId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ASSIGNED_TO_CUSTOMER, - assignedEdge.getId().getId().toString(), savedCustomer.getId().getId().toString(), savedCustomer.getTitle()); + tenantId, savedCustomer.getId(), tenantAdminUser.getId(), tenantAdminUser.getEmail(), ActionType.ASSIGNED_TO_CUSTOMER, + ActionType.ASSIGNED_TO_CUSTOMER, assignedEdge.getId().getId().toString(), savedCustomer.getId().getId().toString(), savedCustomer.getTitle()); Edge foundEdge = doGet("/api/edge/" + savedEdge.getId().getId().toString(), Edge.class); Assert.assertEquals(savedCustomer.getId(), foundEdge.getCustomerId()); @@ -327,8 +328,8 @@ public class EdgeControllerTest extends AbstractControllerTest { Assert.assertEquals(ModelConstants.NULL_UUID, unassignedEdge.getCustomerId().getId()); testNotifyEntityAllOneTimeLogEntityActionEntityEqClass(unassignedEdge, unassignedEdge.getId(), unassignedEdge.getId(), - savedTenant.getId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UNASSIGNED_FROM_CUSTOMER, - unassignedEdge.getId().getId().toString(), savedCustomer.getId().getId().toString(), savedCustomer.getTitle()); + tenantId, savedCustomer.getId(), tenantAdminUser.getId(), tenantAdminUser.getEmail(), ActionType.UNASSIGNED_FROM_CUSTOMER, + ActionType.UNASSIGNED_FROM_CUSTOMER, unassignedEdge.getId().getId().toString(), savedCustomer.getId().getId().toString(), savedCustomer.getTitle()); foundEdge = doGet("/api/edge/" + savedEdge.getId().getId().toString(), Edge.class); Assert.assertEquals(ModelConstants.NULL_UUID, foundEdge.getCustomerId().getId()); @@ -375,7 +376,7 @@ public class EdgeControllerTest extends AbstractControllerTest { customer.setTitle("Different customer"); Customer savedCustomer = doPost("/api/customer", customer, Customer.class); - login(tenantAdmin.getEmail(), "testPassword1"); + loginTenantAdmin(); Edge edge = constructEdge("My edge", "default"); Edge savedEdge = doPost("/api/edge", edge, Edge.class); @@ -408,7 +409,7 @@ public class EdgeControllerTest extends AbstractControllerTest { List edges = new ArrayList<>(Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS)); List loadedEdges = new ArrayList<>(); PageLink pageLink = new PageLink(23); - PageData pageData = null; + PageData pageData; do { pageData = doGetTypedWithPageLink("/api/tenant/edges?", new TypeReference<>() { @@ -455,10 +456,10 @@ public class EdgeControllerTest extends AbstractControllerTest { List loadedEdgesTitle1 = new ArrayList<>(); PageLink pageLink = new PageLink(15, 0, title1); - PageData pageData = null; + PageData pageData; do { pageData = doGetTypedWithPageLink("/api/tenant/edges?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); loadedEdgesTitle1.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -475,7 +476,7 @@ public class EdgeControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title2); do { pageData = doGetTypedWithPageLink("/api/tenant/edges?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); loadedEdgesTitle2.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -545,10 +546,10 @@ public class EdgeControllerTest extends AbstractControllerTest { List loadedEdgesType1 = new ArrayList<>(); PageLink pageLink = new PageLink(15); - PageData pageData = null; + PageData pageData; do { pageData = doGetTypedWithPageLink("/api/tenant/edges?type={type}&", - new TypeReference>() { + new TypeReference<>() { }, pageLink, type1); loadedEdgesType1.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -565,7 +566,7 @@ public class EdgeControllerTest extends AbstractControllerTest { pageLink = new PageLink(4); do { pageData = doGetTypedWithPageLink("/api/tenant/edges?type={type}&", - new TypeReference>() { + new TypeReference<>() { }, pageLink, type2); loadedEdgesType2.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -585,7 +586,7 @@ public class EdgeControllerTest extends AbstractControllerTest { pageLink = new PageLink(4); pageData = doGetTypedWithPageLink("/api/tenant/edges?type={type}&", - new TypeReference>() { + new TypeReference<>() { }, pageLink, type1); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -597,7 +598,7 @@ public class EdgeControllerTest extends AbstractControllerTest { pageLink = new PageLink(4); pageData = doGetTypedWithPageLink("/api/tenant/edges?type={type}&", - new TypeReference>() { + new TypeReference<>() { }, pageLink, type2); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -625,16 +626,15 @@ public class EdgeControllerTest extends AbstractControllerTest { List edges = new ArrayList<>(Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS)); testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Edge(), new Edge(), - savedTenant.getId(), customerId, tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ASSIGNED_TO_CUSTOMER, ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity, cntEntity * 2, - new String(), new String(), new String()); + tenantId, customerId, tenantAdminUser.getId(), tenantAdminUser.getEmail(), + ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity, cntEntity * 2, "", "", ""); List loadedEdges = new ArrayList<>(); PageLink pageLink = new PageLink(23); PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); loadedEdges.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -692,7 +692,7 @@ public class EdgeControllerTest extends AbstractControllerTest { PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); loadedEdgesTitle1.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -709,7 +709,7 @@ public class EdgeControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title2); do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); loadedEdgesTitle2.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -731,12 +731,12 @@ public class EdgeControllerTest extends AbstractControllerTest { cntEntity = loadedEdgesTitle1.size(); testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(new Edge(), new Edge(), - savedTenant.getId(), customerId, tenantAdmin.getId(), tenantAdmin.getEmail(), + tenantId, customerId, tenantAdminUser.getId(), tenantAdminUser.getEmail(), ActionType.UNASSIGNED_FROM_CUSTOMER, ActionType.UNASSIGNED_FROM_CUSTOMER, cntEntity, cntEntity, 3); pageLink = new PageLink(4, 0, title1); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -748,7 +748,7 @@ public class EdgeControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title2); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?", - new TypeReference>() { + new TypeReference<>() { }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -797,10 +797,10 @@ public class EdgeControllerTest extends AbstractControllerTest { List loadedEdgesType1 = new ArrayList<>(); PageLink pageLink = new PageLink(15, 0, title1); - PageData pageData = null; + PageData pageData; do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?type={type}&", - new TypeReference>() { + new TypeReference<>() { }, pageLink, type1); loadedEdgesType1.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -817,7 +817,7 @@ public class EdgeControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title2); do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?type={type}&", - new TypeReference>() { + new TypeReference<>() { }, pageLink, type2); loadedEdgesType2.addAll(pageData.getData()); if (pageData.hasNext()) { @@ -837,7 +837,7 @@ public class EdgeControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title1); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?type={type}&", - new TypeReference>() { + new TypeReference<>() { }, pageLink, type1); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -849,7 +849,7 @@ public class EdgeControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title2); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/edges?type={type}&", - new TypeReference>() { + new TypeReference<>() { }, pageLink, type2); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -857,56 +857,53 @@ public class EdgeControllerTest extends AbstractControllerTest { @Test public void testSyncEdge() throws Exception { - Edge edge = doPost("/api/edge", constructEdge("Test Sync Edge", "test"), Edge.class); + loginSysAdmin(); + // get jwt settings from yaml config + JwtSettings settings = doGet("/api/admin/jwtSettings", JwtSettings.class); + // save jwt settings into db + doPost("/api/admin/jwtSettings", settings).andExpect(status().isOk()); + loginTenantAdmin(); + + Asset asset = new Asset(); + asset.setName("Test Sync Edge Asset 1"); + asset.setType("test"); + Asset savedAsset = doPost("/api/asset", asset, Asset.class); Device device = new Device(); device.setName("Test Sync Edge Device 1"); device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); + + Edge edge = doPost("/api/edge", constructEdge("Test Sync Edge", "test"), Edge.class); + + simulateEdgeActivation(edge); + doPost("/api/edge/" + edge.getId().getId().toString() + "/device/" + savedDevice.getId().getId().toString(), Device.class); - - Asset asset = new Asset(); - asset.setName("Test Sync Edge Asset 1"); - asset.setType("test"); - Asset savedAsset = doPost("/api/asset", asset, Asset.class); doPost("/api/edge/" + edge.getId().getId().toString() + "/asset/" + savedAsset.getId().getId().toString(), Asset.class); EdgeImitator edgeImitator = new EdgeImitator(EDGE_HOST, EDGE_PORT, edge.getRoutingKey(), edge.getSecret()); edgeImitator.ignoreType(UserCredentialsUpdateMsg.class); - edgeImitator.expectMessageAmount(20); + edgeImitator.expectMessageAmount(24); edgeImitator.connect(); - assertThat(edgeImitator.waitForMessages()).as("await for messages on first connect").isTrue(); - - assertThat(edgeImitator.findAllMessagesByType(QueueUpdateMsg.class)).as("one msg during sync process").hasSize(1); - List ruleChainUpdateMsgs = edgeImitator.findAllMessagesByType(RuleChainUpdateMsg.class); - assertThat(ruleChainUpdateMsgs).as("one msg during sync process, another from edge creation").hasSize(2); - assertThat(edgeImitator.findAllMessagesByType(DeviceProfileUpdateMsg.class)).as("one msg during sync process for 'default' device profile").hasSize(3); - assertThat(edgeImitator.findAllMessagesByType(DeviceUpdateMsg.class)).as("one msg once device assigned to edge").hasSize(2); - assertThat(edgeImitator.findAllMessagesByType(AssetProfileUpdateMsg.class)).as("two msgs during sync process for 'default' and 'test' asset profiles").hasSize(4); - assertThat(edgeImitator.findAllMessagesByType(AssetUpdateMsg.class)).as("two msgs - one during sync process, and one more once asset assigned to edge").hasSize(2); - assertThat(edgeImitator.findAllMessagesByType(UserUpdateMsg.class)).as("one msg during sync process for tenant admin user").hasSize(1); - assertThat(edgeImitator.findAllMessagesByType(AdminSettingsUpdateMsg.class)).as("admin setting update").hasSize(4); - assertThat(edgeImitator.findAllMessagesByType(CustomerUpdateMsg.class)).as("one msg during sync process for 'Public' customer").hasSize(1); - verifyRuleChainMsgsAreRoot(ruleChainUpdateMsgs); - - edgeImitator.expectMessageAmount(15); + waitForMessages(edgeImitator); + + verifyFetchersMsgs(edgeImitator); + // verify queue msgs + Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); + Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1")); + Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "test")); + Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1")); + Assert.assertTrue(edgeImitator.getDownlinkMsgs().isEmpty()); + + edgeImitator.expectMessageAmount(20); doPost("/api/edge/sync/" + edge.getId()); - assertThat(edgeImitator.waitForMessages()).as("await for messages after edge sync rest api call").isTrue(); - - assertThat(edgeImitator.findAllMessagesByType(QueueUpdateMsg.class)).as("queue msg").hasSize(1); - ruleChainUpdateMsgs = edgeImitator.findAllMessagesByType(RuleChainUpdateMsg.class); - assertThat(ruleChainUpdateMsgs).as("rule chain msg").hasSize(1); - assertThat(edgeImitator.findAllMessagesByType(DeviceProfileUpdateMsg.class)).as("device profile msg").hasSize(2); - assertThat(edgeImitator.findAllMessagesByType(AssetProfileUpdateMsg.class)).as("asset profile msg").hasSize(3); - assertThat(edgeImitator.findAllMessagesByType(AssetUpdateMsg.class)).as("asset update msg").hasSize(1); - assertThat(edgeImitator.findAllMessagesByType(UserUpdateMsg.class)).as("user update msg").hasSize(1); - assertThat(edgeImitator.findAllMessagesByType(AdminSettingsUpdateMsg.class)).as("admin setting update msg").hasSize(4); - assertThat(edgeImitator.findAllMessagesByType(DeviceUpdateMsg.class)).as("asset update msg").hasSize(1); - assertThat(edgeImitator.findAllMessagesByType(CustomerUpdateMsg.class)).as("one msg during sync process for 'Public' customer").hasSize(1); - verifyRuleChainMsgsAreRoot(ruleChainUpdateMsgs); + waitForMessages(edgeImitator); + + verifyFetchersMsgs(edgeImitator); + Assert.assertTrue(edgeImitator.getDownlinkMsgs().isEmpty()); edgeImitator.allowIgnoredTypes(); try { @@ -922,23 +919,255 @@ public class EdgeControllerTest extends AbstractControllerTest { .andExpect(status().isOk()); } - private void verifyRuleChainMsgsAreRoot(List ruleChainUpdateMsgs) { - for (RuleChainUpdateMsg ruleChainUpdateMsg : ruleChainUpdateMsgs) { - Assert.assertTrue(ruleChainUpdateMsg.getRoot()); + private void simulateEdgeActivation(Edge edge) throws Exception { + ObjectNode attributes = JacksonUtil.newObjectNode(); + attributes.put("active", true); + doPost("/api/plugins/telemetry/EDGE/" + edge.getId() + "/attributes/" + DataConstants.SERVER_SCOPE, attributes); + Awaitility.await() + .atMost(30, TimeUnit.SECONDS) + .until(() -> { + List> values = doGetAsyncTyped("/api/plugins/telemetry/EDGE/" + edge.getId() + + "/values/attributes/SERVER_SCOPE", new TypeReference<>() {}); + Optional> activeAttrOpt = values.stream().filter(att -> att.get("key").equals("active")).findFirst(); + if (activeAttrOpt.isEmpty()) { + return false; + } + Map activeAttr = activeAttrOpt.get(); + return "true".equals(activeAttr.get("value").toString()); + }); + } + + private void waitForMessages(EdgeImitator edgeImitator) throws Exception { + boolean success = edgeImitator.waitForMessages(); + if (!success) { + List downlinkMsgs = edgeImitator.getDownlinkMsgs(); + for (AbstractMessage downlinkMsg : downlinkMsgs) { + log.error("{}\n{}", downlinkMsg.getClass(), downlinkMsg); + } + Assert.fail("Await for messages was not successful!"); + } + } + + private void verifyFetchersMsgs(EdgeImitator edgeImitator) { + Assert.assertTrue(popQueueMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Main")); + Assert.assertTrue(popRuleChainMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Edge Root Rule Chain")); + Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "general")); + Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "mail")); + Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "connectivity")); + Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "jwt")); + Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); + Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); + Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); + Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); + Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "test")); + Assert.assertTrue(popUserMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, TENANT_ADMIN_EMAIL, Authority.TENANT_ADMIN)); + Assert.assertTrue(popCustomerMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Public")); + Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); + Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1")); + Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "test")); + Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1")); + Assert.assertTrue(popTenantMsg(edgeImitator.getDownlinkMsgs(), tenantId)); + Assert.assertTrue(popTenantProfileMsg(edgeImitator.getDownlinkMsgs(), tenantProfileId)); + Assert.assertTrue(popSyncCompletedMsg(edgeImitator.getDownlinkMsgs())); + } + + private boolean popQueueMsg(List messages, UpdateMsgType msgType, String name) { + for (AbstractMessage message : messages) { + if (message instanceof QueueUpdateMsg) { + QueueUpdateMsg queueUpdateMsg = (QueueUpdateMsg) message; + Queue queue = JacksonUtil.fromString(queueUpdateMsg.getEntity(), Queue.class, true); + Assert.assertNotNull(queue); + if (msgType.equals(queueUpdateMsg.getMsgType()) && name.equals(queue.getName())) { + messages.remove(message); + return true; + } + } + } + return false; + } + + private boolean popRuleChainMsg(List messages, UpdateMsgType msgType, String name) { + for (AbstractMessage message : messages) { + if (message instanceof RuleChainUpdateMsg) { + RuleChainUpdateMsg ruleChainUpdateMsg = (RuleChainUpdateMsg) message; + RuleChain ruleChain = JacksonUtil.fromString(ruleChainUpdateMsg.getEntity(), RuleChain.class, true); + Assert.assertNotNull(ruleChain); + if (msgType.equals(ruleChainUpdateMsg.getMsgType()) + && name.equals(ruleChain.getName()) + && ruleChain.isRoot()) { + messages.remove(message); + return true; + } + } + } + return false; + } + + private boolean popAdminSettingsMsg(List messages, String key) { + for (AbstractMessage message : messages) { + if (message instanceof AdminSettingsUpdateMsg) { + AdminSettingsUpdateMsg adminSettingsUpdateMsg = (AdminSettingsUpdateMsg) message; + AdminSettings adminSettings = JacksonUtil.fromString(adminSettingsUpdateMsg.getEntity(), AdminSettings.class, true); + Assert.assertNotNull(adminSettings); + if (key.equals(adminSettings.getKey())) { + messages.remove(message); + return true; + } + } + } + return false; + } + + private boolean popDeviceProfileMsg(List messages, UpdateMsgType msgType, String name) { + for (AbstractMessage message : messages) { + if (message instanceof DeviceProfileUpdateMsg) { + DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) message; + DeviceProfile deviceProfile = JacksonUtil.fromString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class, true); + Assert.assertNotNull(deviceProfile); + if (msgType.equals(deviceProfileUpdateMsg.getMsgType()) + && name.equals(deviceProfile.getName())) { + messages.remove(message); + return true; + } + } + } + return false; + } + + private boolean popDeviceMsg(List messages, UpdateMsgType msgType, String name) { + for (AbstractMessage message : messages) { + if (message instanceof DeviceUpdateMsg) { + DeviceUpdateMsg deviceUpdateMsg = (DeviceUpdateMsg) message; + Device device = JacksonUtil.fromString(deviceUpdateMsg.getEntity(), Device.class, true); + Assert.assertNotNull(device); + if (msgType.equals(deviceUpdateMsg.getMsgType()) + && name.equals(device.getName())) { + messages.remove(message); + return true; + } + } + } + return false; + } + + private boolean popAssetProfileMsg(List messages, UpdateMsgType msgType, String name) { + for (AbstractMessage message : messages) { + if (message instanceof AssetProfileUpdateMsg) { + AssetProfileUpdateMsg assetProfileUpdateMsg = (AssetProfileUpdateMsg) message; + AssetProfile assetProfile = JacksonUtil.fromString(assetProfileUpdateMsg.getEntity(), AssetProfile.class, true); + Assert.assertNotNull(assetProfile); + if (msgType.equals(assetProfileUpdateMsg.getMsgType()) + && name.equals(assetProfile.getName())) { + messages.remove(message); + return true; + } + } + } + return false; + } + + private boolean popAssetMsg(List messages, UpdateMsgType msgType, String name) { + for (AbstractMessage message : messages) { + if (message instanceof AssetUpdateMsg) { + AssetUpdateMsg assetUpdateMsg = (AssetUpdateMsg) message; + Asset asset = JacksonUtil.fromString(assetUpdateMsg.getEntity(), Asset.class, true); + Assert.assertNotNull(asset); + if (msgType.equals(assetUpdateMsg.getMsgType()) + && name.equals(asset.getName())) { + messages.remove(message); + return true; + } + } + } + return false; + } + + private boolean popUserMsg(List messages, UpdateMsgType msgType, String email, Authority authority) { + for (AbstractMessage message : messages) { + if (message instanceof UserUpdateMsg) { + UserUpdateMsg userUpdateMsg = (UserUpdateMsg) message; + User user = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true); + Assert.assertNotNull(user); + if (msgType.equals(userUpdateMsg.getMsgType()) + && email.equals(user.getEmail()) + && authority.equals(user.getAuthority())) { + messages.remove(message); + return true; + } + } + } + return false; + } + + private boolean popCustomerMsg(List messages, UpdateMsgType msgType, String title) { + for (AbstractMessage message : messages) { + if (message instanceof CustomerUpdateMsg) { + CustomerUpdateMsg customerUpdateMsg = (CustomerUpdateMsg) message; + Customer customer = JacksonUtil.fromString(customerUpdateMsg.getEntity(), Customer.class, true); + Assert.assertNotNull(customer); + if (msgType.equals(customerUpdateMsg.getMsgType()) + && title.equals(customer.getTitle())) { + messages.remove(message); + return true; + } + } } + return false; + } + + private boolean popTenantMsg(List messages, TenantId tenantId1) { + for (AbstractMessage message : messages) { + if (message instanceof TenantUpdateMsg) { + TenantUpdateMsg tenantUpdateMsg = (TenantUpdateMsg) message; + Tenant tenant = JacksonUtil.fromString(tenantUpdateMsg.getEntity(), Tenant.class, true); + Assert.assertNotNull(tenant); + if (UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE.equals(tenantUpdateMsg.getMsgType()) + && tenantId1.equals(tenant.getId())) { + messages.remove(message); + return true; + } + } + } + return false; + } + + private boolean popTenantProfileMsg(List messages, TenantProfileId tenantProfileId) { + for (AbstractMessage message : messages) { + if (message instanceof TenantProfileUpdateMsg) { + TenantProfileUpdateMsg tenantProfileUpdateMsg = (TenantProfileUpdateMsg) message; + TenantProfile tenantProfile = JacksonUtil.fromString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class, true); + Assert.assertNotNull(tenantProfile); + if (UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE.equals(tenantProfileUpdateMsg.getMsgType()) + && tenantProfileId.equals(tenantProfile.getId())) { + messages.remove(message); + return true; + } + } + } + return false; + } + + private boolean popSyncCompletedMsg(List messages) { + for (AbstractMessage message : messages) { + if (message instanceof SyncCompletedMsg) { + messages.remove(message); + return true; + } + } + return false; } @Test public void testDeleteEdgeWithDeleteRelationsOk() throws Exception { EdgeId edgeId = savedEdge("Edge for Test WithRelationsOk").getId(); - testEntityDaoWithRelationsOk(savedTenant.getId(), edgeId, "/api/edge/" + edgeId); + testEntityDaoWithRelationsOk(tenantId, edgeId, "/api/edge/" + edgeId); } @Ignore @Test public void testDeleteEdgeExceptionWithRelationsTransactional() throws Exception { EdgeId edgeId = savedEdge("Edge for Test WithRelations Transactional Exception").getId(); - testEntityDaoWithRelationsTransactionalException(edgeDao, savedTenant.getId(), edgeId, "/api/edge/" + edgeId); + testEntityDaoWithRelationsTransactionalException(edgeDao, tenantId, edgeId, "/api/edge/" + edgeId); } private Edge savedEdge(String name) { @@ -950,8 +1179,61 @@ public class EdgeControllerTest extends AbstractControllerTest { public void testGetEdgeInstallInstructions() throws Exception { Edge edge = constructEdge(tenantId, "Edge for Test Docker Install Instructions", "default", "7390c3a6-69b0-9910-d155-b90aca4b772e", "l7q4zsjplzwhk16geqxy"); Edge savedEdge = doPost("/api/edge", edge, Edge.class); - String installInstructions = doGet("/api/edge/instructions/" + savedEdge.getId().getId().toString(), String.class); + String installInstructions = doGet("/api/edge/instructions/install/" + savedEdge.getId().getId().toString() + "/docker", String.class); Assert.assertTrue(installInstructions.contains("l7q4zsjplzwhk16geqxy")); Assert.assertTrue(installInstructions.contains("7390c3a6-69b0-9910-d155-b90aca4b772e")); } + + @Test + public void testGetEdgeUpgradeInstructions() throws Exception { + // UpdateInfo config is updating from Thingsboard Update server + HashMap upgradeInfoHashMap = new HashMap<>(); + upgradeInfoHashMap.put("3.6.0", new EdgeUpgradeInfo(true, "3.6.1")); + upgradeInfoHashMap.put("3.6.1", new EdgeUpgradeInfo(true, "3.6.2")); + upgradeInfoHashMap.put("3.6.2", new EdgeUpgradeInfo(true, null)); + edgeUpgradeInstructionsService.updateInstructionMap(upgradeInfoHashMap); + Edge edge = constructEdge("Edge for Test Docker Upgrade Instructions", "default"); + Edge savedEdge = doPost("/api/edge", edge, Edge.class); + String body = "{\"edgeVersion\": \"V_3_6_0\"}"; + doPostAsync("/api/plugins/telemetry/EDGE/" + savedEdge.getId().getId() + "/attributes/SERVER_SCOPE", body, String.class, status().isOk()); + String upgradeInstructions = doGet("/api/edge/instructions/upgrade/" + EdgeVersion.V_3_6_0.name() + "/docker", String.class); + Assert.assertTrue(upgradeInstructions.contains("Upgrading to 3.6.1EDGE")); + Assert.assertTrue(upgradeInstructions.contains("Upgrading to 3.6.2EDGE")); + } + + @Test + public void testIsEdgeUpgradeAvailable() throws Exception { + Edge edge = constructEdge("Edge Upgrade Available", "default"); + Edge savedEdge = doPost("/api/edge", edge, Edge.class); + + // Test 3.5.0 Edge - upgrade not available + String body = "{\"edgeVersion\": \"V_3_5_0\"}"; + doPostAsync("/api/plugins/telemetry/EDGE/" + savedEdge.getId().getId() + "/attributes/SERVER_SCOPE", body, String.class, status().isOk()); + edgeUpgradeInstructionsService.setAppVersion("3.6.0"); + Assert.assertFalse(edgeUpgradeInstructionsService.isUpgradeAvailable(savedEdge.getTenantId(), savedEdge.getId())); + edgeUpgradeInstructionsService.setAppVersion("3.6.2"); + Assert.assertFalse(edgeUpgradeInstructionsService.isUpgradeAvailable(savedEdge.getTenantId(), savedEdge.getId())); + edgeUpgradeInstructionsService.setAppVersion("3.6.2.7"); + Assert.assertFalse(edgeUpgradeInstructionsService.isUpgradeAvailable(savedEdge.getTenantId(), savedEdge.getId())); + + // Test 3.6.0 Edge - upgrade available + body = "{\"edgeVersion\": \"V_3_6_0\"}"; + doPostAsync("/api/plugins/telemetry/EDGE/" + savedEdge.getId().getId() + "/attributes/SERVER_SCOPE", body, String.class, status().isOk()); + edgeUpgradeInstructionsService.setAppVersion("3.6.0"); + Assert.assertFalse(edgeUpgradeInstructionsService.isUpgradeAvailable(savedEdge.getTenantId(), savedEdge.getId())); + edgeUpgradeInstructionsService.setAppVersion("3.6.1.5"); + Assert.assertTrue(edgeUpgradeInstructionsService.isUpgradeAvailable(savedEdge.getTenantId(), savedEdge.getId())); + edgeUpgradeInstructionsService.setAppVersion("3.6.2"); + Assert.assertTrue(edgeUpgradeInstructionsService.isUpgradeAvailable(savedEdge.getTenantId(), savedEdge.getId())); + + // Test 3.6.1 Edge - upgrade available + body = "{\"edgeVersion\": \"V_3_6_1\"}"; + doPostAsync("/api/plugins/telemetry/EDGE/" + savedEdge.getId().getId() + "/attributes/SERVER_SCOPE", body, String.class, status().isOk()); + edgeUpgradeInstructionsService.setAppVersion("3.6.1"); + Assert.assertFalse(edgeUpgradeInstructionsService.isUpgradeAvailable(savedEdge.getTenantId(), savedEdge.getId())); + edgeUpgradeInstructionsService.setAppVersion("3.6.2"); + Assert.assertTrue(edgeUpgradeInstructionsService.isUpgradeAvailable(savedEdge.getTenantId(), savedEdge.getId())); + edgeUpgradeInstructionsService.setAppVersion("3.6.2.6"); + Assert.assertTrue(edgeUpgradeInstructionsService.isUpgradeAvailable(savedEdge.getTenantId(), savedEdge.getId())); + } } diff --git a/application/src/test/java/org/thingsboard/server/controller/EdgeEventControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EdgeEventControllerTest.java index 255aa2bdf1..3303c64c27 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EdgeEventControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EdgeEventControllerTest.java @@ -16,6 +16,7 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.awaitility.Awaitility; import org.junit.After; @@ -26,9 +27,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.context.TestPropertySource; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; @@ -38,7 +39,6 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.relation.EntityRelation; -import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.edge.EdgeEventDao; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.sqlts.insert.sql.SqlPartitioningRepository; @@ -54,18 +54,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @TestPropertySource(properties = { "edges.enabled=true", + "queue.rule-engine.stats.enabled=false" }) @Slf4j @DaoSqlTest public class EdgeEventControllerTest extends AbstractControllerTest { - private Tenant savedTenant; - private User tenantAdmin; - @Autowired private EdgeEventDao edgeEventDao; @SpyBean @@ -80,33 +77,11 @@ public class EdgeEventControllerTest extends AbstractControllerTest { @Before public void beforeTest() throws Exception { - loginSysAdmin(); - - Tenant tenant = new Tenant(); - tenant.setTitle("My tenant"); - savedTenant = doPost("/api/tenant", tenant, Tenant.class); - Assert.assertNotNull(savedTenant); - - tenantAdmin = new User(); - tenantAdmin.setAuthority(Authority.TENANT_ADMIN); - tenantAdmin.setTenantId(savedTenant.getId()); - tenantAdmin.setEmail("tenant2@thingsboard.org"); - tenantAdmin.setFirstName("Joe"); - tenantAdmin.setLastName("Downs"); - - tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); - // sleep 1 seconds to avoid CREDENTIALS updated message for the user - // user credentials is going to be stored and updated event pushed to edge notification service - // while service will be processing this event edge could be already added and additional message will be pushed - Thread.sleep(1000); + loginTenantAdmin(); } @After public void afterTest() throws Exception { - loginSysAdmin(); - - doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) - .andExpect(status().isOk()); } @Test @@ -114,6 +89,11 @@ public class EdgeEventControllerTest extends AbstractControllerTest { Edge edge = constructEdge("TestEdge", "default"); edge = doPost("/api/edge", edge, Edge.class); + // simulate edge activation + ObjectNode attributes = JacksonUtil.newObjectNode(); + attributes.put("active", true); + doPost("/api/plugins/telemetry/EDGE/" + edge.getId() + "/attributes/" + DataConstants.SERVER_SCOPE, attributes); + Device device = constructDevice("TestDevice", "default"); Device savedDevice = doPost("/api/device", device, Device.class); @@ -127,19 +107,36 @@ public class EdgeEventControllerTest extends AbstractControllerTest { EntityRelation relation = new EntityRelation(savedAsset.getId(), savedDevice.getId(), EntityRelation.CONTAINS_TYPE); + awaitForNumberOfEdgeEvents(edgeId, 2); + doPost("/api/relation", relation); + awaitForNumberOfEdgeEvents(edgeId, 3); + + List edgeEvents = findEdgeEvents(edgeId); + Assert.assertTrue(popEdgeEvent(edgeEvents, EdgeEventType.DEVICE)); // TestDevice + Assert.assertTrue(popEdgeEvent(edgeEvents, EdgeEventType.ASSET)); // TestAsset + Assert.assertTrue(popEdgeEvent(edgeEvents, EdgeEventType.RELATION)); + Assert.assertTrue(edgeEvents.isEmpty()); + } + + private boolean popEdgeEvent(List edgeEvents, EdgeEventType edgeEventType) { + for (EdgeEvent edgeEvent : edgeEvents) { + if (edgeEventType.equals(edgeEvent.getType())) { + edgeEvents.remove(edgeEvent); + return true; + } + } + return false; + } + + private void awaitForNumberOfEdgeEvents(EdgeId edgeId, int expectedNumber) { Awaitility.await() .atMost(30, TimeUnit.SECONDS) .until(() -> { List edgeEvents = findEdgeEvents(edgeId); - return edgeEvents.size() == 4; + return edgeEvents.size() == expectedNumber; }); - List edgeEvents = findEdgeEvents(edgeId); - Assert.assertTrue(edgeEvents.stream().anyMatch(ee -> EdgeEventType.RULE_CHAIN.equals(ee.getType()))); - Assert.assertTrue(edgeEvents.stream().anyMatch(ee -> EdgeEventType.DEVICE.equals(ee.getType()))); - Assert.assertTrue(edgeEvents.stream().anyMatch(ee -> EdgeEventType.ASSET.equals(ee.getType()))); - Assert.assertTrue(edgeEvents.stream().anyMatch(ee -> EdgeEventType.RELATION.equals(ee.getType()))); } @Test @@ -195,7 +192,7 @@ public class EdgeEventControllerTest extends AbstractControllerTest { edgeEvent.setCreatedTime(System.currentTimeMillis()); edgeEvent.setTenantId(tenantId); edgeEvent.setAction(EdgeEventActionType.ADDED); - edgeEvent.setEntityId(tenantAdmin.getUuidId()); + edgeEvent.setEntityId(tenantAdminUser.getUuidId()); edgeEvent.setType(EdgeEventType.ALARM); try { edgeEventDao.saveAsync(edgeEvent).get(); diff --git a/application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java index a65769f3f1..beabcf77d1 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java @@ -43,6 +43,7 @@ import org.springframework.test.web.servlet.ResultActions; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityViewInfo; import org.thingsboard.server.common.data.StringUtils; @@ -170,7 +171,7 @@ public class EntityViewControllerTest extends AbstractControllerTest { testBroadcastEntityStateChangeEventTime(foundEntityView.getId(), tenantId, 1); testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(foundEntityView, foundEntityView, tenantId, tenantAdminCustomerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, - ActionType.ADDED, ActionType.ADDED, 1, 0, 1); + ActionType.ADDED, 1, 1, 1); Mockito.reset(tbClusterService, auditLogService); savedView.setName("New test entity view"); @@ -183,7 +184,7 @@ public class EntityViewControllerTest extends AbstractControllerTest { testBroadcastEntityStateChangeEventTime(foundEntityView.getId(), tenantId, 1); testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(foundEntityView, foundEntityView, tenantId, tenantAdminCustomerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, - ActionType.UPDATED, ActionType.UPDATED, 1, 1, 5); + ActionType.UPDATED, 1, 1, 5); doGet("/api/tenant/entityViews?entityViewName=" + name) .andExpect(status().isNotFound()) @@ -247,13 +248,13 @@ public class EntityViewControllerTest extends AbstractControllerTest { doDelete("/api/entityView/" + entityIdStr) .andExpect(status().isOk()); - testNotifyEntityBroadcastEntityStateChangeEventOneTimeMsgToEdgeServiceNever(savedView, savedView.getId(), savedView.getId(), + testNotifyEntityBroadcastEntityStateChangeEventOneTime(savedView, savedView.getId(), savedView.getId(), tenantId, view.getCustomerId(), tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.DELETED, entityIdStr); doGet("/api/entityView/" + entityIdStr) .andExpect(status().isNotFound()) - .andExpect(statusReason(containsString(msgErrorNoFound("Entity view",entityIdStr)))); + .andExpect(statusReason(containsString(msgErrorNoFound(EntityType.ENTITY_VIEW.getNormalName(), entityIdStr)))); } @Test @@ -286,7 +287,7 @@ public class EntityViewControllerTest extends AbstractControllerTest { testBroadcastEntityStateChangeEventTime(savedView.getId(), tenantId, 1); testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(savedView, savedView, tenantId, tenantAdminCustomerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, - ActionType.UPDATED, ActionType.UPDATED, 1, 1, 5); + ActionType.UPDATED, 1, 1, 5); Mockito.reset(tbClusterService, auditLogService); EntityView assignedView = doPost( @@ -298,9 +299,9 @@ public class EntityViewControllerTest extends AbstractControllerTest { assertEquals(savedCustomer.getId(), foundView.getCustomerId()); testBroadcastEntityStateChangeEventNever(foundView.getId()); - testNotifyEntityAllOneTime(foundView, foundView.getId(), foundView.getId(), + testNotifyAssignUnassignEntityAllOneTime(foundView, foundView.getId(), foundView.getId(), tenantId, foundView.getCustomerId(), tenantAdminUserId, TENANT_ADMIN_EMAIL, - ActionType.ASSIGNED_TO_CUSTOMER, + ActionType.ASSIGNED_TO_CUSTOMER, ActionType.UPDATED, foundView.getId().getId().toString(), foundView.getCustomerId().getId().toString(), savedCustomer.getTitle()); EntityView unAssignedView = doDelete("/api/customer/entityView/" + savedView.getId().getId().toString(), EntityView.class); @@ -310,9 +311,9 @@ public class EntityViewControllerTest extends AbstractControllerTest { assertEquals(ModelConstants.NULL_UUID, foundView.getCustomerId().getId()); testBroadcastEntityStateChangeEventNever(foundView.getId()); - testNotifyEntityAllOneTime(unAssignedView, savedView.getId(), savedView.getId(), + testNotifyAssignUnassignEntityAllOneTime(unAssignedView, savedView.getId(), savedView.getId(), tenantId, savedView.getCustomerId(), tenantAdminUserId, TENANT_ADMIN_EMAIL, - ActionType.UNASSIGNED_FROM_CUSTOMER, + ActionType.UNASSIGNED_FROM_CUSTOMER, ActionType.UPDATED, assignedView.getId().getId().toString(), savedView.getCustomerId().getId().toString(), savedCustomer.getTitle()); } @@ -328,9 +329,9 @@ public class EntityViewControllerTest extends AbstractControllerTest { Assert.assertTrue(publicCustomer.isPublic()); testBroadcastEntityStateChangeEventNever(assignedView.getId()); - testNotifyEntityAllOneTime(assignedView, assignedView.getId(), assignedView.getId(), + testNotifyAssignUnassignEntityAllOneTime(assignedView, assignedView.getId(), assignedView.getId(), tenantId, assignedView.getCustomerId(), tenantAdminUserId, TENANT_ADMIN_EMAIL, - ActionType.ASSIGNED_TO_CUSTOMER, + ActionType.ASSIGNED_TO_CUSTOMER, ActionType.UPDATED, assignedView.getId().getId().toString(), assignedView.getCustomerId().getId().toString(), publicCustomer.getTitle()); EntityView foundView = doGet("/api/entityView/" + savedView.getId().getId().toString(), EntityView.class); @@ -343,9 +344,9 @@ public class EntityViewControllerTest extends AbstractControllerTest { assertEquals(ModelConstants.NULL_UUID, foundView.getCustomerId().getId()); testBroadcastEntityStateChangeEventNever(foundView.getId()); - testNotifyEntityAllOneTime(unAssignedView, unAssignedView.getId(), unAssignedView.getId(), + testNotifyAssignUnassignEntityAllOneTime(unAssignedView, unAssignedView.getId(), unAssignedView.getId(), tenantId, publicCustomer.getId(), tenantAdminUserId, TENANT_ADMIN_EMAIL, - ActionType.UNASSIGNED_FROM_CUSTOMER, + ActionType.UNASSIGNED_FROM_CUSTOMER, ActionType.UPDATED, unAssignedView.getId().getId().toString(), publicCustomer.getId().getId().toString(), publicCustomer.getTitle()); } @@ -425,11 +426,11 @@ public class EntityViewControllerTest extends AbstractControllerTest { testNotifyEntityBroadcastEntityStateChangeEventMany(new EntityView(), new EntityView(), tenantId, tenantAdminCustomerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, - ActionType.ADDED, ActionType.ADDED, cntEntity, 0, cntEntity*2, 0); + ActionType.ADDED, ActionType.ADDED, cntEntity, cntEntity, cntEntity * 2, 0); testNotifyEntityBroadcastEntityStateChangeEventMany(new EntityView(), new EntityView(), tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, - ActionType.ASSIGNED_TO_CUSTOMER, ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity, + ActionType.ASSIGNED_TO_CUSTOMER, ActionType.UPDATED, cntEntity, cntEntity, cntEntity*2, 3); } @@ -464,7 +465,7 @@ public class EntityViewControllerTest extends AbstractControllerTest { testBroadcastEntityStateChangeEventNever(loadedNamesOfView1.get(0).getId()); testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(new EntityView(), new EntityView(), tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, - ActionType.UNASSIGNED_FROM_CUSTOMER, ActionType.UNASSIGNED_FROM_CUSTOMER, cntEntity, cntEntity, 3); + ActionType.UNASSIGNED_FROM_CUSTOMER, ActionType.UPDATED, cntEntity, cntEntity, 3); PageData pageData = doGetTypedWithPageLink(urlTemplate, PAGE_DATA_ENTITY_VIEW_TYPE_REF, new PageLink(4, 0, name1)); diff --git a/application/src/test/java/org/thingsboard/server/controller/HomePageApiTest.java b/application/src/test/java/org/thingsboard/server/controller/HomePageApiTest.java index c601be7272..148c4afea9 100644 --- a/application/src/test/java/org/thingsboard/server/controller/HomePageApiTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/HomePageApiTest.java @@ -92,6 +92,8 @@ public class HomePageApiTest extends AbstractControllerTest { @MockBean private SmsService smsService; + private static final int DEFAULT_DASHBOARDS_COUNT = 1; + //For system administrator @Test public void testTenantsCountWsCmd() throws Exception { @@ -408,7 +410,7 @@ public class HomePageApiTest extends AbstractControllerTest { Assert.assertEquals(2, usageInfo.getUsers()); Assert.assertEquals(configuration.getMaxUsers(), usageInfo.getMaxUsers()); - Assert.assertEquals(0, usageInfo.getDashboards()); + Assert.assertEquals(DEFAULT_DASHBOARDS_COUNT, usageInfo.getDashboards()); Assert.assertEquals(configuration.getMaxDashboards(), usageInfo.getMaxDashboards()); Assert.assertEquals(0, usageInfo.getTransportMessages()); @@ -417,6 +419,9 @@ public class HomePageApiTest extends AbstractControllerTest { Assert.assertEquals(0, usageInfo.getJsExecutions()); Assert.assertEquals(configuration.getMaxJSExecutions(), usageInfo.getMaxJsExecutions()); + Assert.assertEquals(0, usageInfo.getTbelExecutions()); + Assert.assertEquals(configuration.getMaxTbelExecutions(), usageInfo.getMaxTbelExecutions()); + Assert.assertEquals(0, usageInfo.getEmails()); Assert.assertEquals(configuration.getMaxEmails(), usageInfo.getMaxEmails()); @@ -475,7 +480,8 @@ public class HomePageApiTest extends AbstractControllerTest { } usageInfo = doGet("/api/usage", UsageInfo.class); - Assert.assertEquals(dashboards.size(), usageInfo.getDashboards()); + int expectedDashboardsCount = dashboards.size() + DEFAULT_DASHBOARDS_COUNT; + Assert.assertEquals(expectedDashboardsCount, usageInfo.getDashboards()); } private Long getInitialEntityCount(EntityType entityType) throws Exception { diff --git a/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java new file mode 100644 index 0000000000..246eb71ace --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java @@ -0,0 +1,339 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.controller; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.google.common.hash.Hashing; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.mock.web.MockPart; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.ImageDescriptor; +import org.thingsboard.server.common.data.ImageExportData; +import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.SystemParams; +import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.dao.sql.resource.TbResourceRepository; + +import java.util.Base64; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@DaoSqlTest +public class ImageControllerTest extends AbstractControllerTest { + + private static final byte[] PNG_IMAGE = Base64.getDecoder().decode("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"); + private static final byte[] JPEG_IMAGE = Base64.getDecoder().decode("/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHCBYVFRUSEhUYGBgaGBocGRwYGhgYHBgSGBwZGRoYGhghIS4lHB4tHxgZJjorKy8xNTU1GiRIQDszPy40NTEBDAwMEA8QHhISHjsrJSxAOjY/PTE/ND86NzY3NDc0NDQ0NjQ0PTQ2Nj00NDQ0NDQ0Nj42MTQ0NDQ0NjQ0NDQ0NP/AABEIAOEA4QMBIgACEQEDEQH/xAAcAAEAAgMBAQEAAAAAAAAAAAAABgcCBAUDCAH/xAA9EAACAQMABwUFBwQBBAMAAAABAgADBBEFBhIhMUFRByJhcYETMlKRoRRCYnKCsdEjksHw8TOi0uFzssL/xAAaAQEAAwEBAQAAAAAAAAAAAAAAAQMEBQIG/8QAKhEBAAICAgAEBQQDAAAAAAAAAAECAxEEIQUSMUETMlFhoSKBsfBCcZH/2gAMAwEAAhEDEQA/ALmiIgIiICIiAiIgIiICIiAkE072h06dRraypNdVlyG2CBTRhuwz8OIImr2i6ysGGj7Zyjsu1Xdfep0jwRTyZt+/kMyM6KCUkFOmoVR05nqTzMDuLpPS1bJNe2thyCUzUYDoSxxnymxQfSyDK3lvXPw1KRTP6kO4+k1Le78Z07e7gbOjdeQtRbfSNE2tRjhHJDUah/DU4A8Nx68pNZBdIW1O5pPQrKGRhz4qeTDoRI92ca1vQuH0NeMW2HK0HY53D3UJ6Fd6+eOkC3IiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAmnpO+WhRq1392mjO35UUsQOp3TckA7Z7409Gug41alNPHZDbZ/wDoB6wKlt9JtVd7iocvVdnb14AeAAA9J1Le78ZC7G4OyB0nVt7uEplb3fjOlb3chtvdzp293AmVvd+MqXXW7zpCtVpkgq1PBG4h0RBkeIZZLNIadWhTLk5bgo6ty9JWVaqWZmY5LEknqScmEPqzVDTP2yzoXO7adBt45VV7rjHIbQOPAidyVf2E3ZayrUjwp1jjydQf8S0ICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAkC7StEi7azoMSED1HcDcWVVVQM8slh6Zk9kN18vfZKzqcOtvUZeeMNTXax0BYH0gVPrlqaLan9ptshFwHUkthScBgTvxnjIYl0Oe4y4dXNIJcmrYu7Vg1JmVn2SxTcjoxUAH31I3cz0lL31sadSpSPFHZfPZJGfpA6ltdbTBVyzHgFBYnyA3zo3r3FBdpreoo+J1IUeP8AziWR2d6t07W2W5qACpUQM7t9ynxCD4d289T5Cd+lpO2rsaIcMWBwrqQHXns7Qw3pBt85XV01Rtpzk/QDoBPCSvtB1dFlc4pjFKou3T/DvwyZ54OD5MJFIF0dgFTu3a+KH6ES5JUPYCn9K7b8aDx90mW9AREQEREBERAREQEREBERAREQEREBERAREQMTK07SbhRd2dFzspXpV6DnGSqVgFD/AKXFNv0yyyZC7ista5BrKCAxUDooJA38eOCZm5HJjBEdbmel2HDOTf27Vn2faOrUdJqKylBSWqKhIIUnZ2RhuDAsVImjrNqddVLm4rUqWabOzL3hkjw85e32GmpyqAHrvP7zzdJiy87LX0iI/KyuKk/VCe0i6alo63NPcGq0lf8AKEdtk9O+iytquslQqvxo6shHEOpHDzGQfAmXPpvRSV6L0KmdhwMgbsEHIYdCDvkL0J2eUaVwtavWZ0RgyLs4yw3jaPQHfu44lmDxPFf9N+p/Dzbj2juvcNbtpxsWZONomofHGE2vTOz9JU8nnapc161zttSdKKDYp5G4jOS5I3AseXQCQMzo1tFo3WdwotWazqYXt2CJi1uG61h9FAlqyp+wOpm3ulzwqqcdAV/9S2JKCIiAiIgIiICIiAiIgIiICIiAiIgIiICIkT1z13t9HriodusRlKSnvHozfCvifSBs66ayJYWz12wXIK0lP36pG4eQ4nwErPUXWBrhdm4bL7eyWPHbbLKx8zkHyleazaxV76sa9w2TwVR7qJ8Kj9zzlj6gaAAsyXXv1O/jnsDco8N2/wA5l5mH4mKdRuY7aONk8l43OonpY1LSP3agII3E9fGe4rKeBkXoXVwgA7tZRwFUYZR0DD/M2l0pU5WgB/8Ak7v8z53Ja0x1aP39Wy2LU9R/x2axB4b5yLyuinDOoPQkZ+XGFoV62522V+GllR+p+J9MTfttAoowNlPyjefM854xcLJl+WJn7+kJi9cfzSj9YK4KkBlIwQQcEHwMpPWSxFG5q0l90Nu8FIDAegOPSW3rbfvbUGrU1DMrAd7OACcZwJS11Xao7O5yzEknqTvnc8P42XDNvP6KeXnx5IiK+qyOw3S607upbOce2TuZ4e0TJwPErn+2X7Pjm2rtTZaiMVZCGVhuKspyCD1zPoXs57QFvlFCvhLlV8hVA4so5HmR/idNgWDERAREQEREBERAREQEREBERAREQERODrZp37JRBQbVao2xRT4qh5n8KjLHwEDk6765LaI9Kky+2CgsdxFFW4Fhzc/dXnx4T5x0hdtWqPVdizOxJLEsxJ6nmZ2NatKF3aktQuAxao541rk++5/COCjgAN0jwGdwgdrVXQrXdwlMZ2QdqoelMHePM8B5+Evq3QJshRgAAADgFG7Hykf1G1d+yW4DjFV8M/geSen75kqSkTwBPlJQzNJH3kDz5zJLRBy+s9Es35K3yxMzTdeKH5GU24+K0+aaxM/6WRlvEaiZeqHG4T9qV9lSx/0zWNbwmtWYtxlsRp4R7WCy9tb105sjfPGRKCIxuM+lWSUBrTY+wu7ilyDkj8j4df8AtYSZHInta3D03WpTYq6EMrDcVYHIIPXM8ZkjEEEcQcjzEgfTXZ3reukbfabAr08Cqo68nA+E4+eZMJ8wau6XeyrUtI0B3NrYroOGGwWXwDDvL0I8MT6VsL1K1NK1IhkdQykc1IzA2oiICIiAiIgIiICIiAiIgIiIGDMACScAcSekofXjWU1Gq3QO5tqhaj4aCnFSrjkXYHf0Cyyu0jSppWwoUzipct7JSOKoRmo48lz6kShtYn9rWdE3U6CbK44AIADgZxktu8gIEdk+7MNXfbVftVRcpSPcBHvVeIPjj98dJBqFJnZUUZZiFAHNicAfMz6X1P0Itrb0qQwSi4JG7ac++3qc+kDq2mjhxqfL+Z00UDcAB5TzUz0WBmDMgZgJlA861ure8o8+BnKvNHld671+o852cxmBFHSVT2taM2Xo3IG5l9m35lyy+e4n+0S6dJWuydteB4+B/iQ/XLRX2i1q0wMsBtJ+Zd4kofP8QRMtg4zjd18ZCXW1dulWp7Or/wBKsPZ1PAE91x0Ktg585b3ZPpd6FSroi5bvIS9An7ycWUeneA/N0lEyxq94xt7LS1L/AKtuyq+PvKDsnPhy/VA+hompoy+WvSp16ZytRFZfJhmbcBERAREQEREBERAREQERPKtVCqzHgoJPkBmBTmvGlfa6RrtnuWlHYXp7ZxtufPgv6BK4akVsmrH3q1xs+aIu23/cy/IzrVLs1KF7cMcmrVqNnqDjH7mY6fttnRWi3HAtc7XHexqHB/tXEDPsv0V7a7FRhlaS7X6z3V/yfQT6EpDAA6Spex+kBQqvjeagGfBQP5lroYGyhnss10M90MD0E/cT8WZmBjMcz9MwJgKqBlKnmJG6y4JHSSMmcK/Hfbz/AHgfO2uWjvs97cUwMLt7S/kfvgDyzj0mvoqh7WncU+a0zVXnvpkbQHmpJ/TJV2uW+LijU+Ons/2Mf/Kcfs6oh7+hTb3XFVGHVWpVARAjEsPs7YVbe6tG3qf2dSu71XMr+qhVmU8QSD5g4MmXZm5FaqORQfMN/wCzAs3sX0qWtqtnU9+2qFev9NicfJlceQEsqUZ2aXnstNXFEHu1VqDHV1xUB88B/mZecBERAREQEREBERAREQE4muN2aVjdVBxWjUI89kgCdksBvO6QztbrFdF3JHP2a+jVFB+hgUno0bVk6DiQ/wA8yTXtmK+rlvUXe1vVfaA6GpUB9Nl1M4epuj3rUmVRu2zx5ggZA/3nJr2fWpprd6Juh3KoZ06EEBHAHxDuH0niMtJt5N9/R7mlvL5tdMOypQLLPWq+fkssig+QDK47PqLW4ubKpuejWO7qrAYYeBAB9ZOrapjyljw66NPZGmmjz2R5A3Fafu1NdXmW3A9S0wLTzLzEvAzLThXj5Zj4zpXVfZXxPCcZ2gVZ2wnv235an7pNXsbsPaaRR+VNHbPiRsAfJj8pj2s3O1c0qfwU8+rsf/ESSdntP7Boq80k256iMKfkoKp83OfSBVGkHDVajKcguxB6gkkSU9nad+s/RVHzJP8AgSGye6sKKFs1Rt2Qzn8oG76CBjqXdE6douOdaovoUdD9J9Iz5i7MVNTS1rniXdj+mm7n9p9OwEREBERAREQERED8n4xwMyN6Q0lUas9KnUWkqYySu0WJ3+gnhcaQuSpp5pnO7bXPA9F6zFk52KkzEz6L64LTqfq8KINy5qVRtLkhF2mVVUHGdx4mautOrjVbd7anUYUn2SysdrZKsHBRjwGRwnZ0Vb7CgDhgAfzOmDOTj5F53bzdz+F99ROojpFdTNWxQpCmd4X5k7ycnxJJ+U4faQGtilzQ9+myup8AcEHqCpYeUsld0jOu+j/a0ifDB8t/8/SescxW9ck9zvuXmLTaZj2mNI9baRp3Hs9J0DjKCncJzVc5BYfgbO/4WJ5SRo0oyy0jWsKzPS3oSQ6H3Tg4Knp4GWjqzrBRuFApNggDuH3kHwkcwOAPhPoY7Y5hLaVbE3Eq5nIR57K8lDqipM9uctax6zL7QYS6W3PGrcgeJmg9YnnPJngZ1qpJyZrO0O84ust+1K3c0wS7DYRRxNR9wx88+kIVnWsm0npSoiE7BchmG8LRp4TaHmF3eLTs9qWsCYp6MtsCnR2dsLwDKMLT/SOPj6zVbSqaMt2t6DBrtx/VdcEU2+EHmR0675BUps7YGSxO/PXmSZCWxoqyNWoE5cWPRf5PCSfWW7CUPZru28KAOSLvP7Aesw0ZbrSXZHHix6n+JHtM3vtahIPdG5fLmYE67C9Hl7565HdpUW39KlQhVHqu38p9AyvuxzQP2exFVhh67bZ6imNyD5ZP6jLBgIiICIiAiIgIiIEZ1g0a4f7TSG1uAdRxIHBl6kTQtqocBlOR/u6TORXTll7BvtCDuMcVAPuseD+XIzi+JcGLROWnr7tvHz/4W/ZsW1TkfSb6tOOjzap1+s4OPNNf0ytvTfcOjtTzrIGUqeBnkKk/dubqZImNSp8swpjX3Q5o1TUA7pOG/wDyflu9JChSKtt0mZGHAqSvyI3iX1rfo5a1FsjkQfLr6f4lC1QVZkPFWKn8ykg/UTucDN56eWfWP4V5661aPf8AlPNW9f8A3aN93W4CqB3W8XA4eY3eUsJKoIBBBBGQRvBB5gz58feMGWJ2Y6TZ6D0WOfZsNnwRuA+YM3s6wxUj2k1Q8bcIbLVMcTMFba93f6jGOpPACatjYtcs3wqcb+A8fE/xIjrRWVbxrIt/QpqjVFGQKlRt+H371A5dZlx8n4mSaxHUe662KK1iZnv6JhTuabsUp16DuPuJWpM2emyGzK67RNYKlOt9mRWQou9iCGDMN+xnh3TjaHUzt0rayddhrenjyxj1mppuzFKkSpNe2Hv0KpLNTU/eo1D3kx04TSq0rGhalt5OB14ztWdNUGF9TzM5ekKaU6h9g5ZCAVJ3MAfuuOG0OB5HGRuMwa+bGBuPM/xA39KaR3Gmp3ncx6DpPXUnQBvrylbgHZztVD8NFcFj67lHiwnB3k9SfrPo3sq1T+xW3tKq4r1gGfPFKf3afhxyfE+ECcUqYUKqjAAAA6KBgCesRAREQEREBERAREQE8q9EOrIwyCCCPAz1iBB0RqLtbt93eh+KmeHqJsh52NO6M9soKbqi70P7qfAyN0q5JKsCrruZTxB/ifLeI8KcV5vWOpdTBkjJX7t0VCOEyF11moXmLPOfWbV9Gj4cS2bmurKV375QWtqFLurjnsn1KjP1BMu6rUwCTylF613Ae6qkHIBA9VAB+uZ3PCZta9pn6MnLrFaRH3ctq5IxLP7NLApbvVPGo278i7h9SZWlvZVH3pTdx+FWb9hN3R2lrm0b+mzJzKsDsnzUzuucvGY1XwpPhK1o9o9UDDUEY9QzLn03yZao3tW+AepTVE2sgAk5UcyT4/tKuRljFSZ/u3vFTzW17J7qzR2aC54sSTKP7S3alpSu2/DBG81Kjf8AMGfQNNQoCjgBiQLtH1SF1isoO0BgsBkqRwJHNf4nO42WMdtWWX7mZVPbaWI3hh85tVdMNVQ0lO4++fw9BPOnqTWL7JqJjPFdpj/bgb/WWVqvqfTt0DVEBPEB8Ek/E3TymzNy8eOu97eaY7WnSs0oIOCj5ZmvpC1DqdkDaG8cB6SV9paCiVemqqS2DgDBGCeHpIlq/ZVr65pWy5w7DaKjctMHvMfADP0luHLGWkWiNbeclPJbSw+y3s7baS+vkwBhqNNhvLcRUYeHED1l0xEteCIiAiIgIiICIiAiIgIiIH5ORpfQq1u8DsVBwYfsw5idcT8ni9K3jy2jcJraazuEFubatS3VKZI+JO8p9OImobteAyT0wc/KWLMPZjoPlOZfwnHM7rMw2051ojuNoZY6Eeuc1AyUvkzeXw+c3rHUDR1I5W1RjnOam1UOeOcuTJTE34MFcFdVZsuW2Sdy8qNBFGEUKPwgD9prX2iaFdSleilRTxDKp/xN+JeqUV2h9mQt1N1ZEmltDbpnLGmCcbatxZd+/O8dSOE91LslpURgAYAUeQH/ABJheU9qm69VI+YIkX1fq/0gOYOD5zm8+Z3X6dtWCN1l3NqfheapqTBqs4+S6yKDogOQqg9cDM1bipP2rcTQq1MzNa8tOPG4Ot+rZvKWVzkHOQMkEdRzGJ1NSrex0dT2E2/aMB7SpUQhmPQBc7K9Bk+JPGZrcshypI/3pMKtyze8R8gJ0OPzrYqRWPy9X4fntuye21ytRQyEMp5ie0jGpiHZqt90sNnoSBvIkmnew3m9ItMa25WWkUvNYn0ZRES1WREQEREBERAREQEREBERAREQEREBERAxMhOkqJt67bO5KhLKeW195fn+8m01L+xSspSoMjl1B5EHkZm5WCM1Ne/stw5Ph23PoigvW8Ji1wTxMwvdE1qJ901E5MoywH4lmityp5/PdPms3Hy0nVol2cc47xust5qk8Hea7XI4ZyeQG8nyE6ej9BVa2C+aaePvMPAcvWTh4t8k6iHq+THjjdpc5dpm2EUu3Rd/z6Ts2WrLvg122V+Fd5Pm3KSSw0fTors01A6nmT1J5zcnbweHUp3buXNzc69+q9Q8regqKERQqgYAHIT1ifs6MRphIiJIREQEREBERAREQEREBERAREQEREBERA/IiIH4ZBdcPfiJj5nyNXF+d6al+83+9JNhERw/kRyvnfsT9ibGYiIgIiICIiAiIgIiIH//2Q=="); + private static final byte[] SVG_IMAGE = Base64.getDecoder().decode("PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiMyNzg2MjJ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+Cg=="); + + @Autowired + private TbResourceRepository resourceRepository; + + @Before + public void beforeEach() throws Exception { + loginTenantAdmin(); + } + + @After + public void afterEach() { + resourceRepository.deleteAll(); + } + + @Test + public void testUploadPngImage() throws Exception { + String filename = "my_png_image.png"; + TbResourceInfo imageInfo = uploadImage(HttpMethod.POST, "/api/image", filename, "image/png", PNG_IMAGE, false); + + assertThat(imageInfo.getTitle()).isEqualTo(filename); + assertThat(imageInfo.getResourceType()).isEqualTo(ResourceType.IMAGE); + assertThat(imageInfo.getResourceKey()).isEqualTo(filename); + assertThat(imageInfo.getFileName()).isEqualTo(filename); + assertThat(imageInfo.getEtag()).isEqualTo(Hashing.sha256().hashBytes(PNG_IMAGE).toString()); + + checkPngImageDescriptor(imageInfo.getDescriptor(ImageDescriptor.class)); + + assertThat(downloadImage("tenant", filename)).containsExactly(PNG_IMAGE); + assertThat(downloadImagePreview("tenant", filename)).containsExactly(PNG_IMAGE); + + assertThat(getImages(null, false, 10)).contains(imageInfo); + } + + @Test + public void testUploadJpegImage() throws Exception { + String filename = "my_jpeg_image.jpg"; + TbResourceInfo imageInfo = uploadImage(HttpMethod.POST, "/api/image", filename, "image/jpeg", JPEG_IMAGE, false); + + ImageDescriptor imageDescriptor = imageInfo.getDescriptor(ImageDescriptor.class); + checkJpegImageDescriptor(imageDescriptor); + + assertThat(downloadImage("tenant", filename)).containsExactly(JPEG_IMAGE); + assertThat(downloadImagePreview("tenant", filename)).hasSize((int) imageDescriptor.getPreviewDescriptor().getSize()); + } + + @Test + public void testUploadSvgImage() throws Exception { + String filename = "my_svg_image.svg"; + TbResourceInfo imageInfo = uploadImage(HttpMethod.POST, "/api/image", filename, "image/svg+xml", SVG_IMAGE, false); + + ImageDescriptor imageDescriptor = imageInfo.getDescriptor(ImageDescriptor.class); + checkSvgImageDescriptor(imageDescriptor); + + assertThat(downloadImage("tenant", filename)).containsExactly(SVG_IMAGE); + assertThat(downloadImagePreview("tenant", filename)).hasSize((int) imageDescriptor.getPreviewDescriptor().getSize()); + } + + @Test + public void testUploadImageWithSameFilename() throws Exception { + String filename = "my_jpeg_image.jpg"; + TbResourceInfo imageInfo1 = uploadImage(HttpMethod.POST, "/api/image", filename, "image/jpeg", JPEG_IMAGE, false); + assertThat(imageInfo1.getTitle()).isEqualTo(filename); + assertThat(imageInfo1.getFileName()).isEqualTo(filename); + assertThat(imageInfo1.getResourceKey()).isEqualTo(filename); + + TbResourceInfo imageInfo2 = uploadImage(HttpMethod.POST, "/api/image", filename, "image/jpeg", JPEG_IMAGE, false); + assertThat(imageInfo2.getTitle()).isEqualTo(filename); + assertThat(imageInfo2.getFileName()).isEqualTo(filename); + assertThat(imageInfo2.getResourceKey()).isEqualTo("my_jpeg_image_(1).jpg"); + + TbResourceInfo imageInfo3 = uploadImage(HttpMethod.POST, "/api/image", filename, "image/jpeg", JPEG_IMAGE, false); + assertThat(imageInfo3.getTitle()).isEqualTo(filename); + assertThat(imageInfo3.getFileName()).isEqualTo(filename); + assertThat(imageInfo3.getResourceKey()).isEqualTo("my_jpeg_image_(2).jpg"); + } + + @Test + public void testUpdateImage() throws Exception { + String filename = "my_png_image.png"; + TbResourceInfo imageInfo = uploadImage(HttpMethod.POST, "/api/image", filename, "image/png", PNG_IMAGE, false); + checkPngImageDescriptor(imageInfo.getDescriptor(ImageDescriptor.class)); + + String newFilename = "my_jpeg_image.png"; + imageInfo = uploadImage(HttpMethod.PUT, "/api/images/tenant/" + filename, newFilename, "image/jpeg", JPEG_IMAGE, false); + + assertThat(imageInfo.getTitle()).isEqualTo(filename); + assertThat(imageInfo.getResourceKey()).isEqualTo(filename); + assertThat(imageInfo.getFileName()).isEqualTo(newFilename); + + ImageDescriptor imageDescriptor = imageInfo.getDescriptor(ImageDescriptor.class); + checkJpegImageDescriptor(imageDescriptor); + + assertThat(downloadImage("tenant", filename)).containsExactly(JPEG_IMAGE); + assertThat(downloadImagePreview("tenant", filename)).hasSize((int) imageDescriptor.getPreviewDescriptor().getSize()); + } + + @Test + public void testUpdateImageInfo() throws Exception { + String filename = "my_png_image.png"; + TbResourceInfo imageInfo = uploadImage(HttpMethod.POST, "/api/image", filename, "image/png", PNG_IMAGE, false); + ImageDescriptor imageDescriptor = imageInfo.getDescriptor(ImageDescriptor.class); + + assertThat(imageInfo.getTitle()).isEqualTo(filename); + assertThat(imageInfo.getResourceKey()).isEqualTo(filename); + assertThat(imageInfo.getFileName()).isEqualTo(filename); + + String newTitle = "My PNG image"; + imageInfo.setTitle(newTitle); + imageInfo.setDescriptor(JacksonUtil.newObjectNode()); + imageInfo = doPut("/api/images/tenant/" + filename + "/info", imageInfo, TbResourceInfo.class); + + assertThat(imageInfo.getTitle()).isEqualTo(newTitle); + assertThat(imageInfo.getDescriptor(ImageDescriptor.class)).isEqualTo(imageDescriptor); + } + + @Test + public void testExportImportImage() throws Exception { + String filename = "my_png_image.png"; + uploadImage(HttpMethod.POST, "/api/image", filename, "image/png", PNG_IMAGE, false); + + ImageExportData exportData = doGet("/api/images/tenant/" + filename + "/export", ImageExportData.class); + assertThat(exportData.getMediaType()).isEqualTo("image/png"); + assertThat(exportData.getFileName()).isEqualTo(filename); + assertThat(exportData.getTitle()).isEqualTo(filename); + assertThat(exportData.getResourceKey()).isEqualTo(filename); + assertThat(exportData.getData()).isEqualTo(Base64.getEncoder().encodeToString(PNG_IMAGE)); + + doDelete("/api/images/tenant/" + filename).andExpect(status().isOk()); + + TbResourceInfo importedImageInfo = doPut("/api/image/import", exportData, TbResourceInfo.class); + assertThat(importedImageInfo.getTitle()).isEqualTo(filename); + assertThat(importedImageInfo.getResourceKey()).isEqualTo(filename); + assertThat(importedImageInfo.getFileName()).isEqualTo(filename); + checkPngImageDescriptor(importedImageInfo.getDescriptor(ImageDescriptor.class)); + assertThat(downloadImage("tenant", filename)).containsExactly(PNG_IMAGE); + } + + @Test + public void testGetImages() throws Exception { + loginSysAdmin(); + String systemImageName = "my_system_png_image.png"; + TbResourceInfo systemImage = uploadImage(HttpMethod.POST, "/api/image", systemImageName, "image/png", PNG_IMAGE, false); + + loginTenantAdmin(); + String tenantImageName = "my_jpeg_image.jpg"; + TbResourceInfo tenantImage = uploadImage(HttpMethod.POST, "/api/image", tenantImageName, "image/jpeg", JPEG_IMAGE, false); + + List tenantImages = getImages(null, false, 10); + assertThat(tenantImages).containsOnly(tenantImage); + + List allImages = getImages(null, true, 10); + assertThat(allImages).containsOnly(tenantImage, systemImage); + + assertThat(getImages("png", true, 10)) + .containsOnly(systemImage); + assertThat(getImages("jpg", true, 10)) + .containsOnly(tenantImage); + } + + @Test + public void testUploadPublicImage() throws Exception { + String filename = "my_public_image.png"; + TbResourceInfo imageInfo = uploadImage(HttpMethod.POST, "/api/image", filename, "image/png", PNG_IMAGE, true); + + assertThat(imageInfo.isPublic()).isTrue(); + assertThat(imageInfo.getPublicResourceKey()).hasSize(32); + assertThat(imageInfo.getPublicLink()).isEqualTo("/api/images/public/" + imageInfo.getPublicResourceKey()); + + assertThat(downloadImage("tenant", filename)).containsExactly(PNG_IMAGE); + resetTokens(); + assertThat(downloadPublicImage(imageInfo.getPublicResourceKey())).containsExactly(PNG_IMAGE); + } + + @Test + public void testMakeImagePublic() throws Exception { + String filename = "my_public_image.png"; + TbResourceInfo imageInfo = uploadImage(HttpMethod.POST, "/api/image", filename, "image/png", PNG_IMAGE, false); + String publicKey = imageInfo.getPublicResourceKey(); + assertThat(publicKey).hasSize(32); + + updateImagePublicStatus(filename, false); + doGet("/api/images/public/" + publicKey).andExpect(status().isNotFound()); + + updateImagePublicStatus(filename, true); + resetTokens(); + assertThat(downloadPublicImage(publicKey)).containsExactly(PNG_IMAGE); + + loginTenantAdmin(); + updateImagePublicStatus(filename, false); + doGet("/api/images/public/" + publicKey).andExpect(status().isNotFound()); + } + + @Test + public void testGetImageUploadSpecs() throws Exception { + SystemParams systemParams = doGet("/api/system/params", SystemParams.class); + assertThat(systemParams.getMaxResourceSize()).isZero(); + + loginSysAdmin(); + updateDefaultTenantProfileConfig(tenantProfileConfig -> { + tenantProfileConfig.setMaxResourceSize(100); + }); + loginTenantAdmin(); + systemParams = doGet("/api/system/params", SystemParams.class); + assertThat(systemParams.getMaxResourceSize()).isEqualTo(100); + + loginSysAdmin(); + updateDefaultTenantProfileConfig(tenantProfileConfig -> { + tenantProfileConfig.setMaxResourceSize(0); + }); + loginTenantAdmin(); + systemParams = doGet("/api/system/params", SystemParams.class); + assertThat(systemParams.getMaxResourceSize()).isEqualTo(0); + } + + private TbResourceInfo updateImagePublicStatus(String filename, boolean isPublic) throws Exception { + return doPut("/api/images/tenant/" + filename + "/public/" + isPublic, "", TbResourceInfo.class); + } + + private void checkPngImageDescriptor(ImageDescriptor imageDescriptor) { + assertThat(imageDescriptor.getMediaType()).isEqualTo("image/png"); + assertThat(imageDescriptor.getWidth()).isEqualTo(200); + assertThat(imageDescriptor.getHeight()).isEqualTo(160); + assertThat(imageDescriptor.getSize()).isEqualTo(PNG_IMAGE.length); + assertThat(imageDescriptor.getEtag()).isEqualTo(Hashing.sha256().hashBytes(PNG_IMAGE).toString()); + + ImageDescriptor previewDescriptor = imageDescriptor.getPreviewDescriptor(); + assertThat(previewDescriptor.getMediaType()).isEqualTo("image/png"); + assertThat(previewDescriptor.getWidth()).isEqualTo(200); + assertThat(previewDescriptor.getHeight()).isEqualTo(160); + assertThat(previewDescriptor.getSize()).isEqualTo(PNG_IMAGE.length); + assertThat(previewDescriptor.getEtag()).isEqualTo(imageDescriptor.getEtag()); + } + + private void checkJpegImageDescriptor(ImageDescriptor imageDescriptor) { + assertThat(imageDescriptor.getMediaType()).isEqualTo("image/jpeg"); + assertThat(imageDescriptor.getWidth()).isEqualTo(225); + assertThat(imageDescriptor.getHeight()).isEqualTo(225); + assertThat(imageDescriptor.getSize()).isEqualTo(JPEG_IMAGE.length); + assertThat(imageDescriptor.getEtag()).isEqualTo(Hashing.sha256().hashBytes(JPEG_IMAGE).toString()); + + ImageDescriptor previewDescriptor = imageDescriptor.getPreviewDescriptor(); + assertThat(previewDescriptor.getMediaType()).isEqualTo("image/png"); + assertThat(previewDescriptor.getWidth()).isEqualTo(225); + assertThat(previewDescriptor.getHeight()).isEqualTo(225); + assertThat(previewDescriptor.getSize()).isEqualTo(53498); + assertThat(previewDescriptor.getEtag()).isEqualTo("c909e20ba942f95f4ed1ddfcf4307ce846b4a689195c629cd85f2517f46e84f9"); + } + + private void checkSvgImageDescriptor(ImageDescriptor imageDescriptor) { + assertThat(imageDescriptor.getMediaType()).isEqualTo("image/svg+xml"); + assertThat(imageDescriptor.getWidth()).isEqualTo(150); + assertThat(imageDescriptor.getHeight()).isEqualTo(150); + assertThat(imageDescriptor.getSize()).isEqualTo(SVG_IMAGE.length); + assertThat(imageDescriptor.getEtag()).isEqualTo(Hashing.sha256().hashBytes(SVG_IMAGE).toString()); + } + + private List getImages(String searchText, boolean includeSystemImages, int limit) throws Exception { + PageData images = doGetTypedWithPageLink("/api/images?includeSystemImages=" + includeSystemImages + "&", new TypeReference<>() {}, new PageLink(limit, 0, searchText)); + return images.getData(); + } + + private byte[] downloadImage(String type, String key) throws Exception { + return doGet("/api/images/" + type + "/" + key).andExpect(status().isOk()) + .andReturn().getResponse().getContentAsByteArray(); + } + + private byte[] downloadImagePreview(String type, String key) throws Exception { + return doGet("/api/images/" + type + "/" + key + "/preview").andExpect(status().isOk()) + .andReturn().getResponse().getContentAsByteArray(); + } + + private byte[] downloadPublicImage(String publicKey) throws Exception { + return doGet("/api/images/public/" + publicKey).andExpect(status().isOk()) + .andReturn().getResponse().getContentAsByteArray(); + } + + private TbResourceInfo uploadImage(HttpMethod httpMethod, String url, String filename, String mediaType, byte[] content, boolean isPublic) throws Exception { + MockMultipartFile file = new MockMultipartFile("file", filename, mediaType, content); + MockPart publicPart = new MockPart("isPublic", String.valueOf(isPublic).getBytes()); + publicPart.getHeaders().setContentType(MediaType.APPLICATION_JSON); + var request = MockMvcRequestBuilders.multipart(httpMethod, url) + .file(file) + .part(publicPart); + setJwtToken(request); + return readResponse(mockMvc.perform(request).andExpect(status().isOk()), TbResourceInfo.class); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/controller/OtaPackageControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/OtaPackageControllerTest.java index f4472e83be..3ebbb9ebbc 100644 --- a/application/src/test/java/org/thingsboard/server/controller/OtaPackageControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/OtaPackageControllerTest.java @@ -339,7 +339,7 @@ public class OtaPackageControllerTest extends AbstractControllerTest { testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new OtaPackageInfo(), new OtaPackageInfo(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ADDED, ActionType.ADDED, cntEntity, 0, (cntEntity*2 - startIndexSaveData)); + ActionType.ADDED, cntEntity, 0, (cntEntity*2 - startIndexSaveData)); List loadedFirmwares = new ArrayList<>(); PageLink pageLink = new PageLink(24); diff --git a/application/src/test/java/org/thingsboard/server/controller/RuleChainControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/RuleChainControllerTest.java index c9eba1dbce..cafeabcf16 100644 --- a/application/src/test/java/org/thingsboard/server/controller/RuleChainControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/RuleChainControllerTest.java @@ -30,6 +30,8 @@ import org.springframework.test.context.ContextConfiguration; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.action.TbCreateAlarmNode; import org.thingsboard.rule.engine.action.TbCreateAlarmNodeConfiguration; +import org.thingsboard.rule.engine.metadata.TbGetRelatedAttributeNode; +import org.thingsboard.rule.engine.metadata.TbGetRelatedDataNodeConfiguration; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; @@ -48,7 +50,6 @@ import org.thingsboard.server.dao.rule.RuleChainDao; import org.thingsboard.server.dao.service.DaoSqlTest; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -59,7 +60,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @DaoSqlTest public class RuleChainControllerTest extends AbstractControllerTest { - private IdComparator idComparator = new IdComparator<>(); + private final IdComparator idComparator = new IdComparator<>(); private Tenant savedTenant; private User tenantAdmin; @@ -129,6 +130,78 @@ public class RuleChainControllerTest extends AbstractControllerTest { ActionType.UPDATED); } + @Test + public void testSaveRuleChainMetadataWithVersionedNodes() throws Exception { + RuleChain ruleChain = new RuleChain(); + ruleChain.setName("RuleChain"); + + RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class); + Assert.assertNotNull(savedRuleChain); + RuleChainId ruleChainId = savedRuleChain.getId(); + Assert.assertNotNull(ruleChainId); + Assert.assertTrue(savedRuleChain.getCreatedTime() > 0); + Assert.assertEquals(ruleChain.getName(), savedRuleChain.getName()); + + var annotation = TbGetRelatedAttributeNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class); + String ruleNodeType = TbGetRelatedAttributeNode.class.getName(); + int currentVersion = annotation.version(); + + String oldConfig = "{\"attrMapping\":{\"serialNumber\":\"sn\"}," + + "\"relationsQuery\":{\"direction\":\"FROM\",\"maxLevel\":1," + + "\"filters\":[{\"relationType\":\"Contains\",\"entityTypes\":[]}]," + + "\"fetchLastLevelOnly\":false},\"telemetry\":false}"; + + TbGetRelatedDataNodeConfiguration defaultConfiguration = new TbGetRelatedDataNodeConfiguration().defaultConfiguration(); + String newConfig = JacksonUtil.toString(defaultConfiguration); + + var ruleChainMetaData = createRuleChainMetadataWithTbVersionedNodes( + ruleChainId, + ruleNodeType, + currentVersion, + oldConfig, + newConfig + ); + var savedRuleChainMetaData = doPost("/api/ruleChain/metadata", ruleChainMetaData, RuleChainMetaData.class); + + Assert.assertEquals(ruleChainId, savedRuleChainMetaData.getRuleChainId()); + Assert.assertEquals(2, savedRuleChainMetaData.getNodes().size()); + + for (RuleNode ruleNode : savedRuleChainMetaData.getNodes()) { + Assert.assertNotNull(ruleNode.getId()); + Assert.assertEquals(currentVersion, ruleNode.getConfigurationVersion()); + Assert.assertEquals(defaultConfiguration, JacksonUtil.treeToValue(ruleNode.getConfiguration(), defaultConfiguration.getClass())); + } + } + + private RuleChainMetaData createRuleChainMetadataWithTbVersionedNodes( + RuleChainId ruleChainId, + String ruleNodeType, + int currentVersion, + String oldConfig, + String newConfig + ) { + RuleChainMetaData ruleChainMetaData = new RuleChainMetaData(); + ruleChainMetaData.setRuleChainId(ruleChainId); + + var ruleNodeWithOldConfig = new RuleNode(); + ruleNodeWithOldConfig.setName("Old Rule Node"); + ruleNodeWithOldConfig.setType(ruleNodeType); + ruleNodeWithOldConfig.setConfiguration(JacksonUtil.toJsonNode(oldConfig)); + + var ruleNodeWithNewConfig = new RuleNode(); + ruleNodeWithNewConfig.setName("New Rule Node"); + ruleNodeWithNewConfig.setType(ruleNodeType); + ruleNodeWithNewConfig.setConfigurationVersion(currentVersion); + ruleNodeWithNewConfig.setConfiguration(JacksonUtil.toJsonNode(newConfig)); + + List ruleNodes = new ArrayList<>(); + ruleNodes.add(ruleNodeWithOldConfig); + ruleNodes.add(ruleNodeWithNewConfig); + ruleChainMetaData.setFirstNodeIndex(0); + ruleChainMetaData.setNodes(ruleNodes); + return ruleChainMetaData; + } + @Test public void testSaveRuleChainWithViolationOfLengthValidation() throws Exception { @@ -169,7 +242,7 @@ public class RuleChainControllerTest extends AbstractControllerTest { doDelete("/api/ruleChain/" + savedRuleChain.getId().getId().toString()) .andExpect(status().isOk()); - testNotifyEntityBroadcastEntityStateChangeEventOneTimeMsgToEdgeServiceNever(savedRuleChain, savedRuleChain.getId(), savedRuleChain.getId(), + testNotifyEntityBroadcastEntityStateChangeEventOneTime(savedRuleChain, savedRuleChain.getId(), savedRuleChain.getId(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.DELETED, savedRuleChain.getId().getId().toString()); @@ -184,14 +257,13 @@ public class RuleChainControllerTest extends AbstractControllerTest { Edge savedEdge = doPost("/api/edge", edge, Edge.class); - List edgeRuleChains = new ArrayList<>(); PageLink pageLink = new PageLink(17); PageData pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/ruleChains?", new TypeReference<>() { }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(1, pageData.getTotalElements()); - edgeRuleChains.addAll(pageData.getData()); + List edgeRuleChains = new ArrayList<>(pageData.getData()); Mockito.reset(tbClusterService, auditLogService); @@ -208,11 +280,7 @@ public class RuleChainControllerTest extends AbstractControllerTest { testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new RuleChain(), new RuleChain(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ADDED, ActionType.ADDED, cntEntity, 0, cntEntity * 2); - testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new RuleChain(), new RuleChain(), - savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ASSIGNED_TO_EDGE, ActionType.ASSIGNED_TO_EDGE, cntEntity, cntEntity, cntEntity * 2, - new String(), new String(), new String()); + ActionType.ADDED, cntEntity, cntEntity, cntEntity * 2); Mockito.reset(tbClusterService, auditLogService); List loadedEdgeRuleChains = new ArrayList<>(); @@ -227,8 +295,8 @@ public class RuleChainControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - Collections.sort(edgeRuleChains, idComparator); - Collections.sort(loadedEdgeRuleChains, idComparator); + edgeRuleChains.sort(idComparator); + loadedEdgeRuleChains.sort(idComparator); Assert.assertEquals(edgeRuleChains, loadedEdgeRuleChains); diff --git a/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java index b4735519a5..20a9e7ced9 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TbResourceControllerTest.java @@ -17,10 +17,10 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; +import org.apache.commons.io.IOUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.mockito.Mockito; import org.springframework.http.HttpHeaders; @@ -35,9 +35,12 @@ import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.common.data.lwm2m.LwM2mObject; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DaoSqlTest; @@ -46,6 +49,7 @@ import java.util.Base64; import java.util.Collections; import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -99,13 +103,13 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My first resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResource savedResource = save(resource); - testNotifyEntityOneTimeMsgToEdgeServiceNever(savedResource, savedResource.getId(), savedResource.getId(), + testNotifyEntityAllOneTimeLogEntityActionEntityEqClass(savedResource, savedResource.getId(), savedResource.getId(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ADDED); + ActionType.ADDED, ActionType.ADDED); Assert.assertNotNull(savedResource); Assert.assertNotNull(savedResource.getId()); @@ -114,18 +118,19 @@ public class TbResourceControllerTest extends AbstractControllerTest { Assert.assertEquals(resource.getTitle(), savedResource.getTitle()); Assert.assertEquals(DEFAULT_FILE_NAME, savedResource.getFileName()); Assert.assertEquals(DEFAULT_FILE_NAME, savedResource.getResourceKey()); - Assert.assertEquals(resource.getData(), savedResource.getData()); + Assert.assertArrayEquals(resource.getData(), download(savedResource.getId())); savedResource.setTitle("My new resource"); + savedResource.setData(null); save(savedResource); TbResource foundResource = doGet("/api/resource/" + savedResource.getId().getId().toString(), TbResource.class); Assert.assertEquals(foundResource.getTitle(), savedResource.getTitle()); - testNotifyEntityOneTimeMsgToEdgeServiceNever(foundResource, foundResource.getId(), foundResource.getId(), + testNotifyEntityAllOneTimeLogEntityActionEntityEqClass(foundResource, foundResource.getId(), foundResource.getId(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.UPDATED); + ActionType.UPDATED, ActionType.UPDATED); } @Test @@ -134,7 +139,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle(StringUtils.randomAlphabetic(300)); resource.setFileName(DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setEncodedData(TEST_DATA); Mockito.reset(tbClusterService, auditLogService); @@ -153,9 +158,9 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My first resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setEncodedData(TEST_DATA); - TbResource savedResource = save(resource); + TbResource savedResource = save(resource); loginDifferentTenant(); @@ -182,13 +187,14 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My first resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResource savedResource = save(resource); - TbResource foundResource = doGet("/api/resource/" + savedResource.getId().getId().toString(), TbResource.class); + TbResource foundResource = doGet("/api/resource/" + savedResource.getUuidId(), TbResource.class); Assert.assertNotNull(foundResource); - Assert.assertEquals(savedResource, foundResource); + Assert.assertEquals(savedResource.getId(), foundResource.getId()); + Assert.assertEquals(savedResource.getFileName(), foundResource.getFileName()); } @Test @@ -197,7 +203,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setTitle("My first resource"); resource.setFileName(DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResource savedResource = save(resource); @@ -207,15 +213,40 @@ public class TbResourceControllerTest extends AbstractControllerTest { .andExpect(status().isOk()); - testNotifyEntityOneTimeMsgToEdgeServiceNever(savedResource, savedResource.getId(), savedResource.getId(), + testNotifyEntityAllOneTimeLogEntityActionEntityEqClass(savedResource, savedResource.getId(), savedResource.getId(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.DELETED, resourceIdStr); + ActionType.DELETED, ActionType.DELETED, resourceIdStr); - doGet("/api/resource/" + savedResource.getId().getId().toString()) + doGet("/api/resource/" + savedResource.getUuidId()) .andExpect(status().isNotFound()) .andExpect(statusReason(containsString(msgErrorNoFound("Resource", resourceIdStr)))); } + @Test + public void testShoudNotDeleteTbResourceIfAssignedToWidgetType() throws Exception { + TbResource resource = new TbResource(); + resource.setResourceType(ResourceType.JKS); + resource.setTitle("My first resource"); + resource.setFileName(DEFAULT_FILE_NAME); + resource.setEncodedData(TEST_DATA); + + TbResource savedResource = save(resource); + + Mockito.reset(tbClusterService, auditLogService); + String resourceIdStr = savedResource.getId().getId().toString(); + + //create widget type + WidgetTypeDetails widgetType = new WidgetTypeDetails(); + widgetType.setName("Widget Type"); + widgetType.setDescriptor(JacksonUtil.fromString(String.format("{ \"resources\": [{\"url\":{\"entityType\":\"TB_RESOURCE\",\"id\":\"%s\"},\"isModule\":true}]}", savedResource.getId()), JsonNode.class)); + doPost("/api/widgetType", widgetType, WidgetTypeDetails.class); + + doDelete("/api/resource/" + resourceIdStr) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Following widget types uses current resource: [" + + widgetType.getName() + "]"))); + } + @Test public void testFindTenantTbResources() throws Exception { @@ -228,7 +259,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setEncodedData(TEST_DATA); resources.add(new TbResourceInfo(save(resource))); } List loadedResources = new ArrayList<>(); @@ -244,9 +275,9 @@ public class TbResourceControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new TbResource(), new TbResource(), + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new TbResource(), new TbResource(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ADDED, cntEntity); + ActionType.ADDED, cntEntity, cntEntity, cntEntity); Collections.sort(resources, idComparator); Collections.sort(loadedResources, idComparator); @@ -265,7 +296,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("JKS Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setEncodedData(TEST_DATA); resources.add(new TbResourceInfo(save(resource))); } @@ -275,7 +306,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("LWM2M Resource" + i); resource.setResourceType(ResourceType.PKCS_12); resource.setFileName(i + DEFAULT_FILE_NAME_2); - resource.setData(TEST_DATA); + resource.setEncodedData(TEST_DATA); save(resource); } @@ -292,9 +323,9 @@ public class TbResourceControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new TbResource(), new TbResource(), - savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ADDED, jksCntEntity + lwm2mCntEntity); + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new TbResource(), new TbResource(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, + jksCntEntity + lwm2mCntEntity, jksCntEntity + lwm2mCntEntity, jksCntEntity + lwm2mCntEntity); Collections.sort(resources, idComparator); Collections.sort(loadedResources, idComparator); @@ -312,7 +343,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setEncodedData(TEST_DATA); resources.add(new TbResourceInfo(save(resource))); } List loadedResources = new ArrayList<>(); @@ -341,9 +372,9 @@ public class TbResourceControllerTest extends AbstractControllerTest { .andExpect(status().isOk()); } - testNotifyManyEntityManyTimeMsgToEdgeServiceNeverAdditionalInfoAny(new TbResource(), new TbResource(), + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(new TbResource(), new TbResource(), resources.get(0).getTenantId(), null, null, SYS_ADMIN_EMAIL, - ActionType.DELETED, cntEntity, 1); + ActionType.DELETED, ActionType.DELETED, cntEntity, cntEntity, 1); pageLink = new PageLink(27); loadedResources.clear(); @@ -372,7 +403,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("JKS Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResourceInfo saved = new TbResourceInfo(save(resource)); jksResources.add(saved); } @@ -383,7 +414,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("LWM2M Resource" + i); resource.setResourceType(ResourceType.PKCS_12); resource.setFileName(i + DEFAULT_FILE_NAME_2); - resource.setData(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResource saved = save(resource); lwm2mesources.add(saved); } @@ -414,9 +445,9 @@ public class TbResourceControllerTest extends AbstractControllerTest { .andExpect(status().isOk()); } - testNotifyManyEntityManyTimeMsgToEdgeServiceNeverAdditionalInfoAny(new TbResource(), new TbResource(), + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(new TbResource(), new TbResource(), jksResources.get(0).getTenantId(), null, null, SYS_ADMIN_EMAIL, - ActionType.DELETED, cntEntity, 1); + ActionType.DELETED, ActionType.DELETED, cntEntity, cntEntity, 1); pageLink = new PageLink(27); loadedResources.clear(); @@ -449,7 +480,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setEncodedData(TEST_DATA); expectedResources.add(new TbResourceInfo(save(resource))); } @@ -460,7 +491,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setTitle("Resource" + i); resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); - resource.setData(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResourceInfo savedResource = new TbResourceInfo(save(resource)); systemResources.add(savedResource); if (i >= 73) { @@ -504,13 +535,13 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JS_MODULE); resource.setTitle("Js resource"); resource.setFileName(JS_TEST_FILE_NAME); - resource.setData(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResource savedResource = save(resource); - testNotifyEntityOneTimeMsgToEdgeServiceNever(savedResource, savedResource.getId(), savedResource.getId(), + testNotifyEntityAllOneTimeLogEntityActionEntityEqClass(savedResource, savedResource.getId(), savedResource.getId(), savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), - ActionType.ADDED); + ActionType.ADDED, ActionType.ADDED); ResultActions resultActions = doGet("/api/resource/js/" + savedResource.getId().getId().toString() + "/download") .andExpect(status().isOk()); @@ -535,7 +566,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JS_MODULE); resource.setTitle("Js resource"); resource.setFileName(JS_TEST_FILE_NAME); - resource.setData(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResource savedResource = save(resource); @@ -579,7 +610,7 @@ public class TbResourceControllerTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JS_MODULE); resource.setTitle("Js resource"); resource.setFileName(JS_TEST_FILE_NAME); - resource.setData(TEST_DATA); + resource.setEncodedData(TEST_DATA); TbResource savedResource = save(resource); @@ -589,7 +620,120 @@ public class TbResourceControllerTest extends AbstractControllerTest { .andExpect(status().isForbidden()); } + @Test + public void testUpdateResourceData_nonUpdatableResourceType() throws Exception { + TbResource resource = new TbResource(); + resource.setResourceType(ResourceType.PKCS_12); + resource.setTitle("My resource"); + resource.setFileName("3.pks"); + resource.setEncodedData(TEST_DATA); + TbResource savedResource = save(resource); + resource.setEtag(savedResource.getEtag()); + + savedResource.setEncodedData(TEST_DATA); + doPost("/api/resource", savedResource) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("can't be updated"))); + + savedResource.setData(null); + savedResource.setTitle("Updated resource"); + savedResource = doPost("/api/resource", savedResource, TbResource.class); + assertThat(savedResource.getTitle()).isEqualTo("Updated resource"); + assertThat(savedResource.getFileName()).isEqualTo(resource.getFileName()); + assertThat(savedResource.getEtag()).isEqualTo(resource.getEtag()); + assertThat(download(savedResource.getId())).asBase64Encoded().isEqualTo(TEST_DATA); + } + + @Test + public void testUpdateResourceData_updatableResourceType() throws Exception { + TbResource resource = new TbResource(); + resource.setResourceType(ResourceType.JS_MODULE); + resource.setTitle("My resource"); + resource.setFileName("module.js"); + resource.setEncodedData(TEST_DATA); + TbResource savedResource = save(resource); + resource.setEtag(savedResource.getEtag()); + + String newData = Base64.getEncoder().encodeToString(new byte[]{1, 2, 3}); + savedResource.setEncodedData(newData); + savedResource.setFileName("new-module.js"); + savedResource.setTitle("Updated title"); + savedResource = save(savedResource); + + assertThat(savedResource.getTitle()).isEqualTo("Updated title"); + assertThat(savedResource.getFileName()).isEqualTo("new-module.js"); + assertThat(savedResource.getEtag()).isNotEqualTo(resource.getEtag()); + assertThat(download(savedResource.getId())).asBase64Encoded().isEqualTo(newData); + } + + @Test + public void testGetLwm2mListObjectsPage() throws Exception { + loginTenantAdmin(); + + List resources = loadLwm2mResources(); + + List objects = + doGetTyped("/api/resource/lwm2m/page?pageSize=100&page=0", new TypeReference<>() {}); + Assert.assertNotNull(objects); + Assert.assertEquals(resources.size(), objects.size()); + + removeLoadResources(resources); + } + + @Test + public void testGetLwm2mListObjects() throws Exception { + loginTenantAdmin(); + + List resources = loadLwm2mResources(); + + List objects = + doGetTyped("/api/resource/lwm2m?sortProperty=id&sortOrder=ASC&objectIds=3_1.0,5_1.0,19_1.1", new TypeReference<>() {}); + Assert.assertNotNull(objects); + Assert.assertEquals(3, objects.size()); + + removeLoadResources(resources); + } + private TbResource save(TbResource tbResource) throws Exception { - return doPostWithTypedResponse("/api/resource", tbResource, new TypeReference<>(){}); + return doPostWithTypedResponse("/api/resource", tbResource, new TypeReference<>() { + }); + } + + private byte[] download(TbResourceId resourceId) throws Exception { + return doGet("/api/resource/" + resourceId + "/download") + .andExpect(status().isOk()) + .andReturn().getResponse().getContentAsByteArray(); + } + + private String getImageLink(TbResourceInfo resourceInfo) { + return "/api/images/" + (resourceInfo.getTenantId().isSysTenantId() ? "system/" : "") + resourceInfo.getResourceKey(); } + + + private List loadLwm2mResources() throws Exception { + var models = List.of("1", "2", "3", "5", "6", "9", "19", "3303"); + + List resources = new ArrayList<>(models.size()); + + for (String model : models) { + String fileName = model + ".xml"; + byte[] bytes = IOUtils.toByteArray(getClass().getClassLoader().getResourceAsStream("lwm2m/" + fileName)); + + TbResource resource = new TbResource(); + resource.setResourceType(ResourceType.LWM2M_MODEL); + resource.setFileName(fileName); + resource.setData(bytes); + + resources.add(save(resource)); + } + return resources; + } + + private void removeLoadResources(List resources) throws Exception { + for (TbResourceInfo resource : resources) { + doDelete("/api/resource/" + resource.getId().getId().toString()) + .andExpect(status().isOk()); + } + } + } diff --git a/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java b/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java index ede6c69fd4..f7082782a3 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java +++ b/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java @@ -16,7 +16,6 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.java_websocket.client.WebSocketClient; @@ -28,11 +27,11 @@ import org.thingsboard.server.common.data.query.EntityDataPageLink; import org.thingsboard.server.common.data.query.EntityDataQuery; import org.thingsboard.server.common.data.query.EntityFilter; import org.thingsboard.server.common.data.query.EntityKey; -import org.thingsboard.server.service.ws.telemetry.cmd.TelemetryPluginCmdsWrapper; +import org.thingsboard.server.service.ws.AuthCmd; +import org.thingsboard.server.service.ws.WsCmd; +import org.thingsboard.server.service.ws.WsCommandsWrapper; import org.thingsboard.server.service.ws.telemetry.cmd.v1.AttributesSubscriptionCmd; -import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmCountCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmCountUpdate; -import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate; @@ -66,6 +65,12 @@ public class TbTestWebSocketClient extends WebSocketClient { } + public void authenticate(String token) { + WsCommandsWrapper cmdsWrapper = new WsCommandsWrapper(); + cmdsWrapper.setAuthCmd(new AuthCmd(1, token)); + send(JacksonUtil.toString(cmdsWrapper)); + } + @Override public void onMessage(String s) { log.info("RECEIVED: {}", s); @@ -105,24 +110,6 @@ public class TbTestWebSocketClient extends WebSocketClient { super.send(text); } - public void send(EntityDataCmd cmd) throws NotYetConnectedException { - TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); - wrapper.setEntityDataCmds(Collections.singletonList(cmd)); - this.send(JacksonUtil.toString(wrapper)); - } - - public void send(EntityCountCmd cmd) throws NotYetConnectedException { - TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); - wrapper.setEntityCountCmds(Collections.singletonList(cmd)); - this.send(JacksonUtil.toString(wrapper)); - } - - public void send(AlarmCountCmd cmd) throws NotYetConnectedException { - TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); - wrapper.setAlarmCountCmds(Collections.singletonList(cmd)); - this.send(JacksonUtil.toString(wrapper)); - } - public String waitForUpdate() { return waitForUpdate(false); } @@ -240,11 +227,7 @@ public class TbTestWebSocketClient extends WebSocketClient { cmd.setEntityId(entityId.getId().toString()); cmd.setScope(scope); cmd.setKeys(String.join(",", keys)); - TelemetryPluginCmdsWrapper cmdsWrapper = new TelemetryPluginCmdsWrapper(); - cmdsWrapper.setAttrSubCmds(List.of(cmd)); - JsonNode msg = JacksonUtil.valueToTree(cmdsWrapper); - ((ObjectNode) msg.get("attrSubCmds").get(0)).remove("type"); - send(msg.toString()); + send(cmd); return JacksonUtil.toJsonNode(waitForReply()); } @@ -288,4 +271,10 @@ public class TbTestWebSocketClient extends WebSocketClient { return sendEntityDataQuery(edq); } + public void send(WsCmd... cmds) { + WsCommandsWrapper cmdsWrapper = new WsCommandsWrapper(); + cmdsWrapper.setCmds(List.of(cmds)); + send(JacksonUtil.toString(cmdsWrapper)); + } + } diff --git a/application/src/test/java/org/thingsboard/server/controller/TelemetryControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/TelemetryControllerTest.java index 47cac1b549..7b7554c559 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TelemetryControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TelemetryControllerTest.java @@ -15,16 +15,28 @@ */ package org.thingsboard.server.controller; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.Assert; import org.junit.Test; +import org.springframework.test.context.TestPropertySource; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.SaveDeviceWithCredentialsRequest; +import org.thingsboard.server.common.data.query.EntityKey; +import org.thingsboard.server.common.data.query.SingleEntityFilter; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.dao.service.DaoSqlTest; +import java.util.List; + import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.common.data.query.EntityKeyType.TIME_SERIES; @DaoSqlTest +@TestPropertySource(properties = { + "sql.attributes.value_no_xss_validation=true", + "sql.ts.value_no_xss_validation=true" +}) public class TelemetryControllerTest extends AbstractControllerTest { @Test @@ -39,6 +51,117 @@ public class TelemetryControllerTest extends AbstractControllerTest { doPostAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/smth", invalidRequestBody, String.class, status().isBadRequest()); } + @Test + public void testDeleteAllTelemetryWithLatest() throws Exception { + loginTenantAdmin(); + Device device = createDevice(); + + SingleEntityFilter filter = new SingleEntityFilter(); + filter.setSingleEntity(device.getId()); + + getWsClient().subscribeLatestUpdate(List.of(new EntityKey(TIME_SERIES, "data")), filter); + + getWsClient().registerWaitForUpdate(1); + + long startTs = System.currentTimeMillis(); + + String testBody = "{\"data\": \"value\"}"; + doPostAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/smth", testBody, String.class, status().isOk()); + + long endTs = System.currentTimeMillis(); + + ObjectNode latest = doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/timeseries?keys=data", ObjectNode.class); + + Assert.assertNotNull(latest); + var data = latest.get("data"); + Assert.assertNotNull(data); + + Assert.assertEquals("value", data.get(0).get("value").asText()); + + ObjectNode timeseries = doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/timeseries?keys=data&startTs={startTs}&endTs={endTs}", ObjectNode.class, startTs, endTs); + + Assert.assertNotNull(timeseries); + + Assert.assertEquals("value", timeseries.get("data").get(0).get("value").asText()); + + doDeleteAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/delete?keys=data&deleteAllDataForKeys=true", String.class); + + latest = doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/timeseries?keys=data", ObjectNode.class); + + Assert.assertTrue(latest.get("data").get(0).get("value").isNull()); + + timeseries = doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/timeseries?keys=data&startTs={startTs}&endTs={endTs}", ObjectNode.class, startTs, endTs); + + Assert.assertTrue(timeseries.isEmpty()); + } + + @Test + public void testDeleteAllTelemetryWithoutLatest() throws Exception { + loginTenantAdmin(); + Device device = createDevice(); + + SingleEntityFilter filter = new SingleEntityFilter(); + filter.setSingleEntity(device.getId()); + + getWsClient().subscribeLatestUpdate(List.of(new EntityKey(TIME_SERIES, "data")), filter); + + getWsClient().registerWaitForUpdate(1); + + long startTs = System.currentTimeMillis(); + + String testBody = "{\"data\": \"value\"}"; + doPostAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/smth", testBody, String.class, status().isOk()); + + long endTs = System.currentTimeMillis(); + + ObjectNode latest = doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/timeseries?keys=data", ObjectNode.class); + + Assert.assertNotNull(latest); + + Assert.assertEquals("value", latest.get("data").get(0).get("value").asText()); + + ObjectNode timeseries = doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/timeseries?keys=data&startTs={startTs}&endTs={endTs}", ObjectNode.class, startTs, endTs); + + Assert.assertNotNull(timeseries); + + Assert.assertEquals("value", timeseries.get("data").get(0).get("value").asText()); + + doDeleteAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/delete?keys=data&deleteAllDataForKeys=true&deleteLatest=false", String.class); + + latest = doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/timeseries?keys=data", ObjectNode.class); + + Assert.assertEquals("value", latest.get("data").get(0).get("value").asText()); + + timeseries = doGetAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/timeseries?keys=data&startTs={startTs}&endTs={endTs}", ObjectNode.class, startTs, endTs); + + Assert.assertTrue(timeseries.isEmpty()); + } + + @Test + public void testValueConstraintValidator() throws Exception { + loginTenantAdmin(); + Device device = createDevice(); + String correctRequestBody = "{\"data\": \"value\"}"; + doPostAsync("/api/plugins/telemetry/" + device.getId() + "/SHARED_SCOPE", correctRequestBody, String.class, status().isOk()); + doPostAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/smth", correctRequestBody, String.class, status().isOk()); + String invalidRequestBody = "{\"data\": \"alert(document)\\\">\"}"; + doPostAsync("/api/plugins/telemetry/" + device.getId() + "/SHARED_SCOPE", invalidRequestBody, String.class, status().isBadRequest()); + doPostAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/smth", invalidRequestBody, String.class, status().isBadRequest()); + } + + @Test + public void testEmptyKeyIsProhibited() throws Exception { + loginTenantAdmin(); + Device device = createDevice(); + String invalidRequestBody = "{\"\": \"value\"}"; + doPostAsync("/api/plugins/telemetry/" + device.getId() + "/SHARED_SCOPE", invalidRequestBody, String.class, status().isBadRequest()); + doPostAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/smth", invalidRequestBody, String.class, status().isBadRequest()); + + String invalidRequestBody2 = "{\" \": \"value\"}"; + doPostAsync("/api/plugins/telemetry/" + device.getId() + "/SHARED_SCOPE", invalidRequestBody2, String.class, status().isBadRequest()); + doPostAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/smth", invalidRequestBody2, String.class, status().isBadRequest()); + } + private Device createDevice() throws Exception { String testToken = "TEST_TOKEN"; diff --git a/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java index a5b86fff85..27a9872ba5 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java @@ -27,16 +27,22 @@ import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentMatcher; import org.mockito.Mockito; +import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.ResultActions; import org.thingsboard.common.util.ThingsBoardExecutors; +import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantInfo; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; @@ -49,25 +55,46 @@ import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; +import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.queue.TbQueueAdmin; +import org.thingsboard.server.queue.discovery.PartitionService; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.UUID; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.function.Predicate; import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; import static org.hamcrest.Matchers.containsString; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.common.data.DataConstants.MAIN_QUEUE_NAME; +import static org.thingsboard.server.common.data.DataConstants.MAIN_QUEUE_TOPIC; @TestPropertySource(properties = { "js.evaluator=mock", + "queue.rule-engine.topic-deletion-delay=10" }) @Slf4j @DaoSqlTest @@ -80,6 +107,13 @@ public class TenantControllerTest extends AbstractControllerTest { ListeningExecutorService executor; + @SpyBean + private PartitionService partitionService; + @SpyBean + private ActorSystemContext actorContext; + @SpyBean + private TbQueueAdmin queueAdmin; + @Before public void setUp() throws Exception { executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass())); @@ -87,6 +121,12 @@ public class TenantControllerTest extends AbstractControllerTest { @After public void tearDown() throws Exception { + loginSysAdmin(); + for (Queue queue : doGetTypedWithPageLink("/api/queues?serviceType=TB_RULE_ENGINE&", new TypeReference>() {}, new PageLink(100)).getData()) { + if (!queue.getName().equals(MAIN_QUEUE_NAME)) { + doDelete("/api/queues/" + queue.getId()).andExpect(status().isOk()); + } + } executor.shutdownNow(); } @@ -430,7 +470,7 @@ public class TenantControllerTest extends AbstractControllerTest { tenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration()); tenantProfile.setProfileData(tenantProfileData); tenantProfile.setIsolatedTbRuleEngine(true); - addQueueConfig(tenantProfile, DataConstants.MAIN_QUEUE_NAME); + addQueueConfig(tenantProfile, MAIN_QUEUE_NAME); addQueueConfig(tenantProfile, "Test"); tenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); @@ -459,7 +499,7 @@ public class TenantControllerTest extends AbstractControllerTest { tenantProfileData2.setConfiguration(new DefaultTenantProfileConfiguration()); tenantProfile2.setProfileData(tenantProfileData2); tenantProfile2.setIsolatedTbRuleEngine(true); - addQueueConfig(tenantProfile2, DataConstants.MAIN_QUEUE_NAME); + addQueueConfig(tenantProfile2, MAIN_QUEUE_NAME); addQueueConfig(tenantProfile2, "Test"); addQueueConfig(tenantProfile2, "Test2"); tenantProfile2 = doPost("/api/tenantProfile", tenantProfile2, TenantProfile.class); @@ -520,10 +560,204 @@ public class TenantControllerTest extends AbstractControllerTest { doDelete("/api/tenant/" + tenant.getId().getId().toString()).andExpect(status().isOk()); } + @Test + public void testUpdateTenantProfileToIsolated() throws Exception { + loginSysAdmin(); + doPost("/api/queues?serviceType=TB_RULE_ENGINE", new Queue(TenantId.SYS_TENANT_ID, getQueueConfig(DataConstants.HP_QUEUE_NAME, DataConstants.HP_QUEUE_TOPIC))).andExpect(status().isOk()); + TenantProfile tenantProfile = new TenantProfile(); + tenantProfile.setName("Test profile"); + TenantProfileData tenantProfileData = new TenantProfileData(); + tenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration()); + tenantProfile.setProfileData(tenantProfileData); + tenantProfile.setIsolatedTbRuleEngine(false); + tenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); + createDifferentTenant(); + loginSysAdmin(); + savedDifferentTenant.setTenantProfileId(tenantProfile.getId()); + savedDifferentTenant = doPost("/api/tenant", savedDifferentTenant, Tenant.class); + TenantId tenantId = differentTenantId; + + loginDifferentTenant(); + DeviceProfile hpQueueProfile = createDeviceProfile("HighPriority profile"); + hpQueueProfile.setDefaultQueueName(DataConstants.HP_QUEUE_NAME); + hpQueueProfile = doPost("/api/deviceProfile", hpQueueProfile, DeviceProfile.class); + Device hpQueueDevice = createDevice("HP", hpQueueProfile.getName(), "HP"); + + DeviceProfile mainQueueProfile = createDeviceProfile("Main profile"); + mainQueueProfile.setDefaultQueueName(MAIN_QUEUE_NAME); + mainQueueProfile = doPost("/api/deviceProfile", mainQueueProfile, DeviceProfile.class); + Device mainQueueDevice = createDevice("Main", mainQueueProfile.getName(), "Main"); + + verifyUsedQueueAndMessage(DataConstants.HP_QUEUE_NAME, tenantId, hpQueueDevice.getId(), DataConstants.ATTRIBUTES_UPDATED, () -> { + doPost("/api/plugins/telemetry/DEVICE/" + hpQueueDevice.getId() + "/attributes/SERVER_SCOPE", "{\"test\":123}", String.class); + }, usedTpi -> { + assertThat(usedTpi.getTopic()).isEqualTo(DataConstants.HP_QUEUE_TOPIC); + assertThat(usedTpi.getTenantId()).get().isEqualTo(TenantId.SYS_TENANT_ID); + }); + verifyUsedQueueAndMessage(MAIN_QUEUE_NAME, tenantId, mainQueueDevice.getId(), DataConstants.ATTRIBUTES_UPDATED, () -> { + doPost("/api/plugins/telemetry/DEVICE/" + mainQueueDevice.getId() + "/attributes/SERVER_SCOPE", "{\"test\":123}", String.class); + }, usedTpi -> { + assertThat(usedTpi.getTopic()).isEqualTo(MAIN_QUEUE_TOPIC); + assertThat(usedTpi.getTenantId()).get().isEqualTo(TenantId.SYS_TENANT_ID); + }); + + loginSysAdmin(); + tenantProfile.setIsolatedTbRuleEngine(true); + tenantProfile.getProfileData().setQueueConfiguration(List.of( + getQueueConfig(MAIN_QUEUE_NAME, MAIN_QUEUE_TOPIC) + )); + tenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); + + loginDifferentTenant(); + verifyUsedQueueAndMessage(MAIN_QUEUE_NAME, tenantId, mainQueueDevice.getId(), DataConstants.ATTRIBUTES_UPDATED, () -> { + doPost("/api/plugins/telemetry/DEVICE/" + mainQueueDevice.getId() + "/attributes/SERVER_SCOPE", "{\"test\":123}", String.class); + }, usedTpi -> { + assertThat(usedTpi.getTopic()).isEqualTo(MAIN_QUEUE_TOPIC); + assertThat(usedTpi.getTenantId()).get().isEqualTo(tenantId); + }); + verifyUsedQueueAndMessage(DataConstants.HP_QUEUE_NAME, tenantId, hpQueueDevice.getId(), DataConstants.ATTRIBUTES_UPDATED, () -> { + doPost("/api/plugins/telemetry/DEVICE/" + hpQueueDevice.getId() + "/attributes/SERVER_SCOPE", "{\"test\":123}", String.class); + }, usedTpi -> { + assertThat(usedTpi.getTopic()).isEqualTo(DataConstants.HP_QUEUE_TOPIC); + assertThat(usedTpi.getTenantId()).get().isEqualTo(TenantId.SYS_TENANT_ID); + }); + + loginSysAdmin(); + tenantProfile.setIsolatedTbRuleEngine(true); + tenantProfile.getProfileData().setQueueConfiguration(List.of( + getQueueConfig(MAIN_QUEUE_NAME, MAIN_QUEUE_TOPIC), + getQueueConfig(DataConstants.HP_QUEUE_NAME, DataConstants.HP_QUEUE_TOPIC) + )); + tenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); + + loginDifferentTenant(); + verifyUsedQueueAndMessage(DataConstants.HP_QUEUE_NAME, tenantId, hpQueueDevice.getId(), DataConstants.ATTRIBUTES_UPDATED, () -> { + doPost("/api/plugins/telemetry/DEVICE/" + hpQueueDevice.getId() + "/attributes/SERVER_SCOPE", "{\"test\":123}", String.class); + }, usedTpi -> { + assertThat(usedTpi.getTopic()).isEqualTo(DataConstants.HP_QUEUE_TOPIC); + assertThat(usedTpi.getTenantId()).get().isEqualTo(tenantId); + }); + verifyUsedQueueAndMessage(MAIN_QUEUE_NAME, tenantId, mainQueueDevice.getId(), DataConstants.ATTRIBUTES_UPDATED, () -> { + doPost("/api/plugins/telemetry/DEVICE/" + mainQueueDevice.getId() + "/attributes/SERVER_SCOPE", "{\"test\":123}", String.class); + }, usedTpi -> { + assertThat(usedTpi.getTopic()).isEqualTo(MAIN_QUEUE_TOPIC); + assertThat(usedTpi.getTenantId()).get().isEqualTo(tenantId); + }); + } + + @Test + public void testIsolatedQueueDeletion() throws Exception { + loginSysAdmin(); + TenantProfile tenantProfile = new TenantProfile(); + tenantProfile.setName("Test profile"); + TenantProfileData tenantProfileData = new TenantProfileData(); + tenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration()); + tenantProfile.setProfileData(tenantProfileData); + tenantProfile.setIsolatedTbRuleEngine(true); + addQueueConfig(tenantProfile, MAIN_QUEUE_NAME); + tenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); + createDifferentTenant(); + loginSysAdmin(); + savedDifferentTenant.setTenantProfileId(tenantProfile.getId()); + savedDifferentTenant = doPost("/api/tenant", savedDifferentTenant, Tenant.class); + TenantId tenantId = differentTenantId; + await().atMost(10, TimeUnit.SECONDS) + .until(() -> { + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, MAIN_QUEUE_NAME, tenantId, tenantId); + return !tpi.getTenantId().get().isSysTenantId(); + }); + TopicPartitionInfo tpi = new TopicPartitionInfo(MAIN_QUEUE_TOPIC, tenantId, 0, false); + String isolatedTopic = tpi.getFullTopicName(); + TbMsg expectedMsg = publishTbMsg(tenantId, tpi); + awaitTbMsg(tbMsg -> tbMsg.getId().equals(expectedMsg.getId()), 10000); // to wait for consumer start + + loginSysAdmin(); + tenantProfile.setIsolatedTbRuleEngine(false); + tenantProfile.getProfileData().setQueueConfiguration(Collections.emptyList()); + tenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); + await().atMost(10, TimeUnit.SECONDS) + .until(() -> partitionService.resolve(ServiceType.TB_RULE_ENGINE, MAIN_QUEUE_NAME, tenantId, tenantId) + .getTenantId().get().isSysTenantId()); + + List submittedMsgs = new ArrayList<>(); + long timeLeft = TimeUnit.SECONDS.toMillis(7); // based on topic-deletion-delay + int msgs = 100; + for (int i = 1; i <= msgs; i++) { + TbMsg tbMsg = publishTbMsg(tenantId, tpi); + submittedMsgs.add(tbMsg.getId()); + Thread.sleep(timeLeft / msgs); + } + await().atMost(15, TimeUnit.SECONDS).untilAsserted(() -> { + verify(queueAdmin, times(1)).deleteTopic(eq(isolatedTopic)); + }); + + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + for (UUID msgId : submittedMsgs) { + verify(actorContext).tell(argThat(msg -> { + return msg instanceof QueueToRuleEngineMsg && ((QueueToRuleEngineMsg) msg).getMsg().getId().equals(msgId); + })); + } + }); + } + + private TbMsg publishTbMsg(TenantId tenantId, TopicPartitionInfo tpi) { + TbMsg tbMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, tenantId, TbMsgMetaData.EMPTY, "{\"test\":1}"); + TransportProtos.ToRuleEngineMsg msg = TransportProtos.ToRuleEngineMsg.newBuilder() + .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) + .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) + .setTbMsg(TbMsg.toByteString(tbMsg)).build(); + tbClusterService.pushMsgToRuleEngine(tpi, tbMsg.getId(), msg, null); + return tbMsg; + } + + private void verifyUsedQueueAndMessage(String queue, TenantId tenantId, EntityId entityId, String msgType, Runnable action, Consumer tpiAssert) { + await().atMost(15, TimeUnit.SECONDS) + .untilAsserted(() -> { + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, queue, tenantId, entityId); + tpiAssert.accept(tpi); + }); + action.run(); + TbMsg tbMsg = awaitTbMsg(msg -> msg.getOriginator().equals(entityId) + && msg.getType().equals(msgType), 10000); + assertThat(tbMsg.getQueueName()).isEqualTo(queue); + + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, queue, tenantId, entityId); + tpiAssert.accept(tpi); + } + + protected TbMsg awaitTbMsg(Predicate predicate, int timeoutMillis) { + AtomicReference tbMsgCaptor = new AtomicReference<>(); + verify(actorContext, timeout(timeoutMillis).atLeastOnce()).tell(argThat(actorMsg -> { + if (!(actorMsg instanceof QueueToRuleEngineMsg)) { + return false; + } + TbMsg tbMsg = ((QueueToRuleEngineMsg) actorMsg).getMsg(); + if (predicate.test(tbMsg)) { + tbMsgCaptor.set(tbMsg); + return true; + } + return false; + })); + return tbMsgCaptor.get(); + } + private void addQueueConfig(TenantProfile tenantProfile, String queueName) { + TenantProfileQueueConfiguration queueConfiguration = getQueueConfig(queueName, "tb_rule_engine." + queueName.toLowerCase()); + TenantProfileData profileData = tenantProfile.getProfileData(); + + List configs = profileData.getQueueConfiguration(); + if (configs == null) { + configs = new ArrayList<>(); + } + configs.add(queueConfiguration); + profileData.setQueueConfiguration(configs); + tenantProfile.setProfileData(profileData); + } + + private TenantProfileQueueConfiguration getQueueConfig(String queueName, String topic) { TenantProfileQueueConfiguration queueConfiguration = new TenantProfileQueueConfiguration(); queueConfiguration.setName(queueName); - queueConfiguration.setTopic("tb_rule_engine." + queueName.toLowerCase()); + queueConfiguration.setTopic(topic); queueConfiguration.setPollInterval(25); queueConfiguration.setPartitions(1 + new Random().nextInt(99)); queueConfiguration.setConsumerPerPartition(true); @@ -539,15 +773,7 @@ public class TenantControllerTest extends AbstractControllerTest { processingStrategy.setPauseBetweenRetries(3); processingStrategy.setMaxPauseBetweenRetries(3); queueConfiguration.setProcessingStrategy(processingStrategy); - TenantProfileData profileData = tenantProfile.getProfileData(); - - List configs = profileData.getQueueConfiguration(); - if (configs == null) { - configs = new ArrayList<>(); - } - configs.add(queueConfiguration); - profileData.setQueueConfiguration(configs); - tenantProfile.setProfileData(profileData); + return queueConfiguration; } private List getQueuesFromConfig(List queueConfiguration, List queues) { diff --git a/application/src/test/java/org/thingsboard/server/controller/TenantProfileControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/TenantProfileControllerTest.java index e6b37143ad..eba9fb8b12 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TenantProfileControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TenantProfileControllerTest.java @@ -167,23 +167,6 @@ public class TenantProfileControllerTest extends AbstractControllerTest { testBroadcastEntityStateChangeEventNeverTenantProfile(); } - @Test - public void testSaveSameTenantProfileWithDifferentIsolatedTbRuleEngine() throws Exception { - loginSysAdmin(); - TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); - TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); - savedTenantProfile.setIsolatedTbRuleEngine(true); - addMainQueueConfig(savedTenantProfile); - - Mockito.reset(tbClusterService); - - doPost("/api/tenantProfile", savedTenantProfile) - .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Can't update isolatedTbRuleEngine property"))); - - testBroadcastEntityStateChangeEventNeverTenantProfile(); - } - @Test public void testDeleteTenantProfileWithExistingTenant() throws Exception { loginSysAdmin(); diff --git a/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java index 67564ec898..d37b4ebe5b 100644 --- a/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java @@ -49,6 +49,7 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.settings.StarredDashboardInfo; import org.thingsboard.server.common.data.settings.UserDashboardsInfo; +import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.user.UserDao; @@ -81,6 +82,9 @@ public class UserControllerTest extends AbstractControllerTest { @Autowired private UserDao userDao; + @Autowired + private DeviceService deviceService; + static class Config { @Bean @Primary @@ -113,7 +117,7 @@ public class UserControllerTest extends AbstractControllerTest { testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(foundUser, foundUser, SYSTEM_TENANT, customerNUULId, null, SYS_ADMIN_EMAIL, - ActionType.ADDED, ActionType.ADDED, 1, 1, 1); + ActionType.ADDED, 1, 1, 1); Mockito.reset(tbClusterService, auditLogService); resetTokens(); @@ -152,7 +156,7 @@ public class UserControllerTest extends AbstractControllerTest { testNotifyEntityAllOneTimeLogEntityActionEntityEqClass(foundUser, foundUser.getId(), foundUser.getId(), SYSTEM_TENANT, customerNUULId, null, SYS_ADMIN_EMAIL, - ActionType.DELETED, SYSTEM_TENANT.getId().toString()); + ActionType.DELETED, ActionType.DELETED, SYSTEM_TENANT.getId().toString()); } @Test @@ -397,7 +401,7 @@ public class UserControllerTest extends AbstractControllerTest { testManyUser.setTenantId(tenantId); testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(testManyUser, testManyUser, SYSTEM_TENANT, customerNUULId, null, SYS_ADMIN_EMAIL, - ActionType.ADDED, ActionType.ADDED, cntEntity, cntEntity, cntEntity); + ActionType.ADDED, cntEntity, cntEntity, cntEntity); List loadedTenantAdmins = new ArrayList<>(); PageLink pageLink = new PageLink(33); @@ -510,7 +514,7 @@ public class UserControllerTest extends AbstractControllerTest { testManyUser.setTenantId(tenantId); testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(testManyUser, testManyUser, SYSTEM_TENANT, customerNUULId, null, SYS_ADMIN_EMAIL, - ActionType.DELETED, ActionType.DELETED, cntEntity, NUMBER_OF_USERS, cntEntity, new String()); + ActionType.DELETED, cntEntity, NUMBER_OF_USERS, cntEntity, ""); pageLink = new PageLink(4, 0, email1); pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?", @@ -740,6 +744,43 @@ public class UserControllerTest extends AbstractControllerTest { Assert.assertEquals(expectedCustomerUserIds, loadedUserIds); } + @Test + public void testGetUsersForDeletedAlarmOriginator() throws Exception { + loginTenantAdmin(); + + String email = "testEmail1"; + for (int i = 0; i < 45; i++) { + User customerUser = createCustomerUser( customerId); + customerUser.setEmail(email + StringUtils.randomAlphanumeric((int) (5 + Math.random() * 10)) + "@thingsboard.org"); + doPost("/api/user", customerUser, User.class); + } + + Device device = new Device(); + device.setName("testDevice"); + device.setCustomerId(customerId); + Device savedDevice = doPost("/api/device", device, Device.class); + + Alarm alarm = createTestAlarm(savedDevice); + + deviceService.deleteDevice(tenantId, savedDevice.getId()); + + List loadedUserIds = new ArrayList<>(); + PageLink pageLink = new PageLink(33, 0); + PageData pageData; + do { + pageData = doGetTypedWithPageLink("/api/users/assign/" + alarm.getId().getId().toString() + "?", + new TypeReference<>() {}, pageLink); + loadedUserIds.addAll(pageData.getData().stream().map(UserEmailInfo::getId) + .collect(Collectors.toList())); + if (pageData.hasNext()) { + pageLink = pageLink.nextPageLink(); + } + } while (pageData.hasNext()); + + Assert.assertEquals(1, loadedUserIds.size()); + Assert.assertEquals(tenantAdminUserId, loadedUserIds.get(0)); + } + @Test public void testDeleteUserWithDeleteRelationsOk() throws Exception { loginSysAdmin(); diff --git a/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java b/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java index a8334fc959..47ce021dae 100644 --- a/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/WebsocketApiTest.java @@ -454,6 +454,12 @@ public class WebsocketApiTest extends AbstractControllerTest { getWsClient().registerWaitForUpdate(); + // Pushing update with wrong scope and make sure it will not arrive. + AttributeKvEntry invalidDataPoint = new BaseAttributeKvEntry(now - TimeUnit.MINUTES.toMillis(1), new LongDataEntry("serverAttributeKey", 55L)); + sendAttributes(device, TbAttributeSubscriptionScope.CLIENT_SCOPE, Arrays.asList(invalidDataPoint)); + + Assert.assertNull(getWsClient().waitForUpdate(3000)); + AttributeKvEntry dataPoint1 = new BaseAttributeKvEntry(now - TimeUnit.MINUTES.toMillis(1), new LongDataEntry("serverAttributeKey", 42L)); List tsData = Arrays.asList(dataPoint1); sendAttributes(device, TbAttributeSubscriptionScope.SERVER_SCOPE, tsData); @@ -642,7 +648,7 @@ public class WebsocketApiTest extends AbstractControllerTest { } @Test - public void testEntityCountCmd_filterTypeSingularCompatibilityTest() { + public void testEntityCountCmd_filterTypeSingularCompatibilityTest() throws Exception { ObjectNode oldFormatDeviceTypeFilterSingular = JacksonUtil.newObjectNode(); oldFormatDeviceTypeFilterSingular.put("type", "deviceType"); oldFormatDeviceTypeFilterSingular.put("deviceType", "default"); @@ -661,9 +667,10 @@ public class WebsocketApiTest extends AbstractControllerTest { ObjectNode wrapperNode = JacksonUtil.newObjectNode(); wrapperNode.set("entityCountCmds", entityCountCmds); - getWsClient().send(JacksonUtil.toString(wrapperNode)); + wsClient = buildAndConnectWebSocketClient("/api/ws/plugins/telemetry?token=" + token); + wsClient.send(JacksonUtil.toString(wrapperNode)); - EntityCountUpdate update = getWsClient().parseCountReply(getWsClient().waitForReply()); + EntityCountUpdate update = wsClient.parseCountReply(wsClient.waitForReply()); Assert.assertEquals(1, update.getCmdId()); Assert.assertEquals(1, update.getCount()); diff --git a/application/src/test/java/org/thingsboard/server/controller/WidgetTypeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/WidgetTypeControllerTest.java index f3a6048c8f..e515250321 100644 --- a/application/src/test/java/org/thingsboard/server/controller/WidgetTypeControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/WidgetTypeControllerTest.java @@ -24,6 +24,7 @@ import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; @@ -33,6 +34,7 @@ import org.thingsboard.server.dao.service.DaoSqlTest; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -43,7 +45,6 @@ public class WidgetTypeControllerTest extends AbstractControllerTest { private IdComparator idComparator = new IdComparator<>(); private Tenant savedTenant; - private WidgetsBundle savedWidgetsBundle; private User tenantAdmin; @Before @@ -63,10 +64,6 @@ public class WidgetTypeControllerTest extends AbstractControllerTest { tenantAdmin.setLastName("Downs"); tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); - - WidgetsBundle widgetsBundle = new WidgetsBundle(); - widgetsBundle.setTitle("My widgets bundle"); - savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class); } @After @@ -80,19 +77,17 @@ public class WidgetTypeControllerTest extends AbstractControllerTest { @Test public void testSaveWidgetType() throws Exception { WidgetTypeDetails widgetType = new WidgetTypeDetails(); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); widgetType.setName("Widget Type"); widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); WidgetTypeDetails savedWidgetType = doPost("/api/widgetType", widgetType, WidgetTypeDetails.class); Assert.assertNotNull(savedWidgetType); Assert.assertNotNull(savedWidgetType.getId()); - Assert.assertNotNull(savedWidgetType.getAlias()); + Assert.assertNotNull(savedWidgetType.getFqn()); Assert.assertTrue(savedWidgetType.getCreatedTime() > 0); Assert.assertEquals(savedTenant.getId(), savedWidgetType.getTenantId()); Assert.assertEquals(widgetType.getName(), savedWidgetType.getName()); Assert.assertEquals(widgetType.getDescriptor(), savedWidgetType.getDescriptor()); - Assert.assertEquals(savedWidgetsBundle.getAlias(), savedWidgetType.getBundleAlias()); savedWidgetType.setName("New Widget Type"); @@ -105,7 +100,6 @@ public class WidgetTypeControllerTest extends AbstractControllerTest { @Test public void testUpdateWidgetTypeFromDifferentTenant() throws Exception { WidgetTypeDetails widgetType = new WidgetTypeDetails(); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); widgetType.setName("Widget Type"); widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); WidgetTypeDetails savedWidgetType = doPost("/api/widgetType", widgetType, WidgetTypeDetails.class); @@ -118,7 +112,6 @@ public class WidgetTypeControllerTest extends AbstractControllerTest { @Test public void testFindWidgetTypeById() throws Exception { WidgetTypeDetails widgetType = new WidgetTypeDetails(); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); widgetType.setName("Widget Type"); widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); WidgetTypeDetails savedWidgetType = doPost("/api/widgetType", widgetType, WidgetTypeDetails.class); @@ -130,7 +123,6 @@ public class WidgetTypeControllerTest extends AbstractControllerTest { @Test public void testDeleteWidgetType() throws Exception { WidgetTypeDetails widgetType = new WidgetTypeDetails(); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); widgetType.setName("Widget Type"); widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); WidgetTypeDetails savedWidgetType = doPost("/api/widgetType", widgetType, WidgetTypeDetails.class); @@ -145,27 +137,15 @@ public class WidgetTypeControllerTest extends AbstractControllerTest { @Test public void testSaveWidgetTypeWithEmptyName() throws Exception { WidgetTypeDetails widgetType = new WidgetTypeDetails(); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); doPost("/api/widgetType", widgetType) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString("Widgets type name should be specified"))); } - @Test - public void testSaveWidgetTypeWithEmptyBundleAlias() throws Exception { - WidgetTypeDetails widgetType = new WidgetTypeDetails(); - widgetType.setName("Widget Type"); - widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); - doPost("/api/widgetType", widgetType) - .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Widgets type bundle alias should be specified"))); - } - @Test public void testSaveWidgetTypeWithEmptyDescriptor() throws Exception { WidgetTypeDetails widgetType = new WidgetTypeDetails(); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); widgetType.setName("Widget Type"); widgetType.setDescriptor(JacksonUtil.fromString("{}", JsonNode.class)); doPost("/api/widgetType", widgetType) @@ -174,73 +154,78 @@ public class WidgetTypeControllerTest extends AbstractControllerTest { } @Test - public void testSaveWidgetTypeWithInvalidBundleAlias() throws Exception { - WidgetTypeDetails widgetType = new WidgetTypeDetails(); - widgetType.setBundleAlias("some_alias"); - widgetType.setName("Widget Type"); - widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); - doPost("/api/widgetType", widgetType) - .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Widget type is referencing to non-existent widgets bundle"))); - } - - @Test - public void testUpdateWidgetTypeBundleAlias() throws Exception { - WidgetTypeDetails widgetType = new WidgetTypeDetails(); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); - widgetType.setName("Widget Type"); - widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); - WidgetTypeDetails savedWidgetType = doPost("/api/widgetType", widgetType, WidgetTypeDetails.class); - savedWidgetType.setBundleAlias("some_alias"); - doPost("/api/widgetType", savedWidgetType) - .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Update of widget type bundle alias is prohibited"))); - - } - - @Test - public void testUpdateWidgetTypeAlias() throws Exception { + public void testUpdateWidgetTypeFqn() throws Exception { WidgetTypeDetails widgetType = new WidgetTypeDetails(); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); widgetType.setName("Widget Type"); widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); WidgetTypeDetails savedWidgetType = doPost("/api/widgetType", widgetType, WidgetTypeDetails.class); - savedWidgetType.setAlias("some_alias"); + savedWidgetType.setFqn("some_fqn"); doPost("/api/widgetType", savedWidgetType) .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Update of widget type alias is prohibited"))); + .andExpect(statusReason(containsString("Update of widget type fqn is prohibited"))); } @Test public void testGetBundleWidgetTypes() throws Exception { + WidgetsBundle widgetsBundle = new WidgetsBundle(); + widgetsBundle.setTitle("My widgets bundle"); + widgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class); + List widgetTypes = new ArrayList<>(); for (int i=0;i<89;i++) { WidgetTypeDetails widgetType = new WidgetTypeDetails(); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); widgetType.setName("Widget Type " + i); widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); widgetTypes.add(new WidgetType(doPost("/api/widgetType", widgetType, WidgetTypeDetails.class))); } - List loadedWidgetTypes = doGetTyped("/api/widgetTypes?isSystem={isSystem}&bundleAlias={bundleAlias}", - new TypeReference<>(){}, false, savedWidgetsBundle.getAlias()); + List widgetTypeIds = widgetTypes.stream().map(type -> type.getId().getId().toString()).collect(Collectors.toList()); + doPost("/api/widgetsBundle/" + widgetsBundle.getId().getId().toString() + "/widgetTypes", widgetTypeIds); + + List loadedWidgetTypes = doGetTyped("/api/widgetTypes?widgetsBundleId={widgetsBundleId}", + new TypeReference<>(){}, widgetsBundle.getId().getId().toString()); Collections.sort(widgetTypes, idComparator); Collections.sort(loadedWidgetTypes, idComparator); Assert.assertEquals(widgetTypes, loadedWidgetTypes); + + loginCustomerUser(); + + List loadedWidgetTypesCustomer = doGetTyped("/api/widgetTypes?widgetsBundleId={widgetsBundleId}", + new TypeReference<>(){}, widgetsBundle.getId().getId().toString()); + Collections.sort(loadedWidgetTypesCustomer, idComparator); + Assert.assertEquals(widgetTypes, loadedWidgetTypesCustomer); + + List customerLoadedWidgetTypesDetails = doGetTyped("/api/widgetTypesDetails?widgetsBundleId={widgetsBundleId}", + new TypeReference<>(){}, widgetsBundle.getId().getId().toString()); + List widgetTypesFromDetailsListCustomer = customerLoadedWidgetTypesDetails.stream().map(WidgetType::new).collect(Collectors.toList()); + Collections.sort(widgetTypesFromDetailsListCustomer, idComparator); + Assert.assertEquals(widgetTypesFromDetailsListCustomer, loadedWidgetTypes); + + loginSysAdmin(); + + List sysAdminLoadedWidgetTypes = doGetTyped("/api/widgetTypes?widgetsBundleId={widgetsBundleId}", + new TypeReference<>(){}, widgetsBundle.getId().getId().toString()); + Collections.sort(sysAdminLoadedWidgetTypes, idComparator); + Assert.assertEquals(widgetTypes, sysAdminLoadedWidgetTypes); + + List sysAdminLoadedWidgetTypesDetails = doGetTyped("/api/widgetTypesDetails?widgetsBundleId={widgetsBundleId}", + new TypeReference<>(){}, widgetsBundle.getId().getId().toString()); + List widgetTypesFromDetailsListSysAdmin = sysAdminLoadedWidgetTypesDetails.stream().map(WidgetType::new).collect(Collectors.toList()); + Collections.sort(widgetTypesFromDetailsListSysAdmin, idComparator); + Assert.assertEquals(widgetTypesFromDetailsListSysAdmin, loadedWidgetTypes); } @Test public void testGetWidgetType() throws Exception { WidgetTypeDetails widgetType = new WidgetTypeDetails(); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); widgetType.setName("Widget Type"); widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); WidgetTypeDetails savedWidgetType = doPost("/api/widgetType", widgetType, WidgetTypeDetails.class); - WidgetType foundWidgetType = doGet("/api/widgetType?isSystem={isSystem}&bundleAlias={bundleAlias}&alias={alias}", - WidgetType.class, false, savedWidgetsBundle.getAlias(), savedWidgetType.getAlias()); + WidgetType foundWidgetType = doGet("/api/widgetType?fqn={fqn}", + WidgetType.class, "tenant."+savedWidgetType.getFqn()); Assert.assertNotNull(foundWidgetType); Assert.assertEquals(new WidgetType(savedWidgetType), foundWidgetType); } diff --git a/application/src/test/java/org/thingsboard/server/controller/WidgetsBundleControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/WidgetsBundleControllerTest.java index ee341fe284..82dfa82d5a 100644 --- a/application/src/test/java/org/thingsboard/server/controller/WidgetsBundleControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/WidgetsBundleControllerTest.java @@ -206,22 +206,32 @@ public class WidgetsBundleControllerTest extends AbstractControllerTest { @Test public void testFindTenantWidgetsBundlesByPageLink() throws Exception { + loginSysAdmin(); - login(tenantAdmin.getEmail(), "testPassword1"); + //upload some system bundles + int sysCntEntity = 10; + for (int i = 0; i < sysCntEntity; i++) { + WidgetsBundle widgetsBundle = new WidgetsBundle(); + widgetsBundle.setTitle("Widgets bundle" + i); + doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class); + } List sysWidgetsBundles = doGetTyped("/api/widgetsBundles?", new TypeReference<>() { }); + login(tenantAdmin.getEmail(), "testPassword1"); + int cntEntity = 73; - List widgetsBundles = new ArrayList<>(); + List tenantWidgetsBundles = new ArrayList<>(); for (int i = 0; i < cntEntity; i++) { WidgetsBundle widgetsBundle = new WidgetsBundle(); widgetsBundle.setTitle("Widgets bundle" + i); - widgetsBundles.add(doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class)); + tenantWidgetsBundles.add(doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class)); } - widgetsBundles.addAll(sysWidgetsBundles); + List allWidgetsBundles = new ArrayList<>(tenantWidgetsBundles); + allWidgetsBundles.addAll(sysWidgetsBundles); List loadedWidgetsBundles = new ArrayList<>(); PageLink pageLink = new PageLink(14); @@ -236,10 +246,35 @@ public class WidgetsBundleControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - Collections.sort(widgetsBundles, idComparator); + Collections.sort(allWidgetsBundles, idComparator); Collections.sort(loadedWidgetsBundles, idComparator); - Assert.assertEquals(widgetsBundles, loadedWidgetsBundles); + Assert.assertEquals(allWidgetsBundles, loadedWidgetsBundles); + + //retrieve tenant only bundles + List loadedWidgetsBundles2 = new ArrayList<>(); + PageLink pageLink2 = new PageLink(14); + do { + pageData = doGetTypedWithPageLink("/api/widgetsBundles?tenantOnly=true&", + new TypeReference<>() { + }, pageLink2); + loadedWidgetsBundles2.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink2 = pageLink2.nextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(tenantWidgetsBundles, idComparator); + Collections.sort(loadedWidgetsBundles2, idComparator); + + Assert.assertEquals(tenantWidgetsBundles, loadedWidgetsBundles2); + + // cleanup + loginSysAdmin(); + for (WidgetsBundle sysWidgetsBundle : sysWidgetsBundles) { + doDelete("/api/widgetsBundle/" + sysWidgetsBundle.getId().getId().toString()) + .andExpect(status().isOk()); + } } @Test diff --git a/application/src/test/java/org/thingsboard/server/controller/plugin/TbWebSocketHandlerTest.java b/application/src/test/java/org/thingsboard/server/controller/plugin/TbWebSocketHandlerTest.java new file mode 100644 index 0000000000..7d3d68f2d3 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/plugin/TbWebSocketHandlerTest.java @@ -0,0 +1,187 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.controller.plugin; + +import lombok.extern.slf4j.Slf4j; +import org.awaitility.Awaitility; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.web.socket.CloseStatus; +import org.springframework.web.socket.adapter.NativeWebSocketSession; +import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.service.ws.WebSocketSessionRef; + +import javax.websocket.RemoteEndpoint; +import javax.websocket.SendHandler; +import javax.websocket.SendResult; +import javax.websocket.Session; +import java.io.IOException; +import java.util.Collection; +import java.util.Deque; +import java.util.List; +import java.util.Random; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.willAnswer; +import static org.mockito.BDDMockito.willDoNothing; +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +@Slf4j +class TbWebSocketHandlerTest { + + TbWebSocketHandler wsHandler; + NativeWebSocketSession session; + Session nativeSession; + RemoteEndpoint.Async asyncRemote; + WebSocketSessionRef sessionRef; + int maxMsgQueuePerSession; + TbWebSocketHandler.SessionMetaData sendHandler; + ExecutorService executor; + + @BeforeEach + void setUp() throws IOException { + maxMsgQueuePerSession = 100; + executor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName(getClass().getSimpleName())); + wsHandler = spy(new TbWebSocketHandler()); + willDoNothing().given(wsHandler).close(any(), any()); + session = mock(NativeWebSocketSession.class); + nativeSession = mock(Session.class); + willReturn(nativeSession).given(session).getNativeSession(Session.class); + asyncRemote = mock(RemoteEndpoint.Async.class); + willReturn(asyncRemote).given(nativeSession).getAsyncRemote(); + sessionRef = mock(WebSocketSessionRef.class, Mockito.RETURNS_DEEP_STUBS); //prevent NPE on logs + TbWebSocketHandler.SessionMetaData sessionMd = wsHandler.new SessionMetaData(session, sessionRef); + sessionMd.setMaxMsgQueueSize(maxMsgQueuePerSession); + sendHandler = spy(sessionMd); + } + + @AfterEach + void tearDown() { + if (executor != null) { + executor.shutdownNow(); + } + } + + @Test + void sendHandler_sendMsg_parallel_no_race() throws InterruptedException { + CountDownLatch finishLatch = new CountDownLatch(maxMsgQueuePerSession * 2); + AtomicInteger sendersCount = new AtomicInteger(); + willAnswer(invocation -> { + assertThat(sendersCount.incrementAndGet()).as("no race").isEqualTo(1); + String text = invocation.getArgument(0); + SendHandler onResultHandler = invocation.getArgument(1); + SendResult sendResult = new SendResult(); + executor.submit(() -> { + sendersCount.decrementAndGet(); + onResultHandler.onResult(sendResult); + finishLatch.countDown(); + }); + return null; + }).given(asyncRemote).sendText(anyString(), any()); + + assertThat(sendHandler.isSending.get()).as("sendHandler not is in sending state").isFalse(); + //first batch + IntStream.range(0, maxMsgQueuePerSession).parallel().forEach(i -> sendHandler.sendMsg("hello " + i)); + Awaitility.await("first batch processed").atMost(30, TimeUnit.SECONDS).until(() -> finishLatch.getCount() == maxMsgQueuePerSession); + assertThat(sendHandler.isSending.get()).as("sendHandler not is in sending state").isFalse(); + //second batch - to test pause between big msg batches + IntStream.range(100, 100 + maxMsgQueuePerSession).parallel().forEach(i -> sendHandler.sendMsg("hello " + i)); + assertThat(finishLatch.await(30, TimeUnit.SECONDS)).as("all callbacks fired").isTrue(); + + verify(sendHandler, never()).closeSession(any()); + verify(sendHandler, times(maxMsgQueuePerSession * 2)).onResult(any()); + assertThat(sendHandler.isSending.get()).as("sendHandler not is in sending state").isFalse(); + } + + @Test + void sendHandler_sendMsg_message_order() throws InterruptedException { + CountDownLatch finishLatch = new CountDownLatch(maxMsgQueuePerSession); + Collection outputs = new ConcurrentLinkedQueue<>(); + willAnswer(invocation -> { + String text = invocation.getArgument(0); + outputs.add(text); + SendHandler onResultHandler = invocation.getArgument(1); + SendResult sendResult = new SendResult(); + executor.submit(() -> { + onResultHandler.onResult(sendResult); + finishLatch.countDown(); + }); + return null; + }).given(asyncRemote).sendText(anyString(), any()); + + List inputs = IntStream.range(0, maxMsgQueuePerSession).mapToObj(i -> "msg " + i).collect(Collectors.toList()); + inputs.forEach(s -> sendHandler.sendMsg(s)); + + assertThat(finishLatch.await(30, TimeUnit.SECONDS)).as("all callbacks fired").isTrue(); + assertThat(outputs).as("inputs exactly the same as outputs").containsExactlyElementsOf(inputs); + + verify(sendHandler, never()).closeSession(any()); + verify(sendHandler, times(maxMsgQueuePerSession)).onResult(any()); + } + + @Test + void sendHandler_sendMsg_queue_size_exceed() { + willDoNothing().given(asyncRemote).sendText(anyString(), any()); // send text will never call back, so queue will grow each sendMsg + sendHandler.sendMsg("first message to stay in-flight all the time during this test"); + IntStream.range(0, maxMsgQueuePerSession).parallel().forEach(i -> sendHandler.sendMsg("hello " + i)); + verify(sendHandler, never()).closeSession(any()); + sendHandler.sendMsg("excessive message"); + verify(sendHandler, times(1)).closeSession(eq(new CloseStatus(1008, "Max pending updates limit reached!"))); + verify(asyncRemote, times(1)).sendText(anyString(), any()); + } + + @Test + void sendHandler_onMsg_allProcessed() throws Exception { + Deque msgs = new ConcurrentLinkedDeque<>(); + doAnswer(inv -> msgs.add(inv.getArgument(1))).when(wsHandler).processMsg(any(), any()); + for (int i = 0; i < 100; i++) { + String msg = String.valueOf(i); + executor.submit(() -> { + try { + Thread.sleep(new Random().nextInt(50)); + sendHandler.onMsg(msg); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } + executor.shutdown(); + executor.awaitTermination(5, TimeUnit.SECONDS); + + assertThat(msgs).map(Integer::parseInt).doesNotHaveDuplicates().hasSize(100); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java index 135b6d87c8..7dd2547597 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java @@ -15,11 +15,12 @@ */ package org.thingsboard.server.edge; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; +import com.google.protobuf.AbstractMessage; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.MessageLite; +import lombok.extern.slf4j.Slf4j; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -27,6 +28,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.TestPropertySource; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.DataConstants; @@ -37,6 +39,7 @@ import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.asset.Asset; @@ -69,8 +72,9 @@ import org.thingsboard.server.common.data.query.FilterPredicateValue; import org.thingsboard.server.common.data.query.NumericFilterPredicate; import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainType; -import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.security.model.JwtSettings; import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.edge.imitator.EdgeImitator; @@ -85,6 +89,9 @@ import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataRequestMsg; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; +import org.thingsboard.server.gen.edge.v1.SyncCompletedMsg; +import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UplinkMsg; import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; @@ -95,20 +102,20 @@ import java.util.List; import java.util.Optional; import java.util.TreeMap; import java.util.UUID; +import java.util.concurrent.TimeUnit; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @TestPropertySource(properties = { "edges.enabled=true", + "queue.rule-engine.stats.enabled=false", + "edges.storage.sleep_between_batches=1000" }) +@Slf4j abstract public class AbstractEdgeTest extends AbstractControllerTest { private static final String THERMOSTAT_DEVICE_PROFILE_NAME = "Thermostat"; - protected Tenant savedTenant; - protected TenantId tenantId; - protected User tenantAdmin; - protected DeviceProfile thermostatDeviceProfile; protected EdgeImitator edgeImitator; @@ -124,32 +131,20 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { protected TbClusterService clusterService; @Before - public void beforeTest() throws Exception { + public void setupEdgeTest() throws Exception { loginSysAdmin(); - Tenant tenant = new Tenant(); - tenant.setTitle("My tenant"); - savedTenant = doPost("/api/tenant", tenant, Tenant.class); - tenantId = savedTenant.getId(); - Assert.assertNotNull(savedTenant); - - tenantAdmin = new User(); - tenantAdmin.setAuthority(Authority.TENANT_ADMIN); - tenantAdmin.setTenantId(savedTenant.getId()); - tenantAdmin.setEmail("tenant2@thingsboard.org"); - tenantAdmin.setFirstName("Joe"); - tenantAdmin.setLastName("Downs"); - - tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); - // sleep 0.5 second to avoid CREDENTIALS updated message for the user - // user credentials is going to be stored and updated event pushed to edge notification service - // while service will be processing this event edge could be already added and additional message will be pushed - Thread.sleep(500); + // get jwt settings from yaml config + JwtSettings settings = doGet("/api/admin/jwtSettings", JwtSettings.class); + // save jwt settings into db + doPost("/api/admin/jwtSettings", settings).andExpect(status().isOk()); + + loginTenantAdmin(); installation(); edgeImitator = new EdgeImitator("localhost", 7070, edge.getRoutingKey(), edge.getSecret()); - edgeImitator.expectMessageAmount(22); + edgeImitator.expectMessageAmount(21); edgeImitator.connect(); requestEdgeRuleChainMetadata(); @@ -180,33 +175,35 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { } @After - public void afterTest() throws Exception { - loginSysAdmin(); - - doDelete("/api/tenant/" + savedTenant.getUuidId()) - .andExpect(status().isOk()); - + public void teardownEdgeTest() { try { + loginTenantAdmin(); + + doDelete("/api/edge/" + edge.getId().toString()) + .andExpect(status().isOk()); edgeImitator.disconnect(); } catch (Exception ignored) {} } - private void installation() { - edge = doPost("/api/edge", constructEdge("Test Edge", "test"), Edge.class); - + private void installation() throws Exception { thermostatDeviceProfile = this.createDeviceProfile(THERMOSTAT_DEVICE_PROFILE_NAME, createMqttDeviceProfileTransportConfiguration(new JsonTransportPayloadConfiguration(), false)); - extendDeviceProfileData(thermostatDeviceProfile); thermostatDeviceProfile = doPost("/api/deviceProfile", thermostatDeviceProfile, DeviceProfile.class); Device savedDevice = saveDevice("Edge Device 1", THERMOSTAT_DEVICE_PROFILE_NAME); - doPost("/api/edge/" + edge.getUuidId() - + "/device/" + savedDevice.getUuidId(), Device.class); Asset savedAsset = saveAsset("Edge Asset 1"); + + edge = doPost("/api/edge", constructEdge("Test Edge", "test"), Edge.class); + + doPost("/api/edge/" + edge.getUuidId() + + "/device/" + savedDevice.getUuidId(), Device.class); doPost("/api/edge/" + edge.getUuidId() + "/asset/" + savedAsset.getUuidId(), Asset.class); + + // wait until assign device and asset events are fully processed by edge notification service + TimeUnit.MILLISECONDS.sleep(500); } protected void extendDeviceProfileData(DeviceProfile deviceProfile) { @@ -243,42 +240,74 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { validateEdgeConfiguration(); - // 5 messages - // - 2 from device profile fetcher (default and thermostat) - // - 1 from device fetcher - // - 1 from device profile controller (thermostat) - // - 1 from device controller (thermostat) - validateDeviceProfiles(); - - // 2 messages - 1 from device fetcher and 1 from device controller - validateDevices(); - - // 2 messages - 1 from asset fetcher and 1 from asset controller - validateAssets(); + // 1 message from queue fetcher + validateMsgsCnt(QueueUpdateMsg.class, 1); + validateQueues(); - // 2 messages - 1 from rule chain fetcher and 1 from rule chain controller + // 1 from rule chain fetcher + validateMsgsCnt(RuleChainUpdateMsg.class, 1); UUID ruleChainUUID = validateRuleChains(); // 1 from request message + validateMsgsCnt(RuleChainMetadataUpdateMsg.class, 1); validateRuleChainMetadataUpdates(ruleChainUUID); - // 4 messages - 4 messages from fetcher - 2 from system level ('mail', 'mailTemplates') and 2 from admin level ('mail', 'mailTemplates') - validateAdminSettings(); + // 4 messages ('general', 'mail', 'connectivity', 'jwt') + validateMsgsCnt(AdminSettingsUpdateMsg.class, 4); + validateAdminSettings(4); + + // 4 messages + // - 1 from default profile fetcher + // - 2 from device profile fetcher (default and thermostat) + // - 1 from device fetcher + validateMsgsCnt(DeviceProfileUpdateMsg.class, 4); + validateDeviceProfiles(4); // 3 messages + // - 1 from default profile fetcher // - 1 message from asset profile fetcher // - 1 message from asset fetcher - // - 1 message from asset controller - validateAssetProfiles(); + validateMsgsCnt(AssetProfileUpdateMsg.class, 3); + validateAssetProfiles(3); - // 1 message from queue fetcher - validateQueues(); + // 1 from device fetcher + validateMsgsCnt(DeviceUpdateMsg.class, 1); + validateDevices(); - // 1 message from user fetcher - validateUsers(); + // 1 from asset fetcher + validateMsgsCnt(AssetUpdateMsg.class, 1); + validateAssets(); // 1 message from public customer fetcher + validateMsgsCnt(CustomerUpdateMsg.class, 1); validatePublicCustomer(); + + // 1 message from user fetcher + validateMsgsCnt(UserUpdateMsg.class, 1); + validateUsers(); + + // 1 from tenant fetcher + validateMsgsCnt(TenantUpdateMsg.class, 1); + validateTenant(); + + // 1 from tenant profile fetcher + validateMsgsCnt(TenantProfileUpdateMsg.class, 1); + validateTenantProfile(); + + // 1 message sync completed + validateMsgsCnt(SyncCompletedMsg.class, 1); + validateSyncCompleted(); + } + + private void validateMsgsCnt(Class clazz, int expectedMsgCnt) { + List downlinkMsgsByType = edgeImitator.findAllMessagesByType(clazz); + if (downlinkMsgsByType.size() != expectedMsgCnt) { + List downlinkMsgs = edgeImitator.getDownlinkMsgs(); + for (AbstractMessage downlinkMsg : downlinkMsgs) { + log.error("{}\n{}", downlinkMsg.getClass(), downlinkMsg); + } + Assert.fail("Unexpected message count for " + clazz + "! Expected: " + expectedMsgCnt + ", but found: " + downlinkMsgsByType.size()); + } } private void validateEdgeConfiguration() throws Exception { @@ -287,16 +316,44 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { testAutoGeneratedCodeByProtobuf(configuration); } - private void validateDeviceProfiles() throws Exception { + private void validateTenant() throws Exception { + Optional tenantUpdateMsgOpt = edgeImitator.findMessageByType(TenantUpdateMsg.class); + Assert.assertTrue(tenantUpdateMsgOpt.isPresent()); + TenantUpdateMsg tenantUpdateMsg = tenantUpdateMsgOpt.get(); + Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantUpdateMsg.getMsgType()); + Tenant tenantMsg = JacksonUtil.fromString(tenantUpdateMsg.getEntity(), Tenant.class, true); + Assert.assertNotNull(tenantMsg); + Tenant tenant = doGet("/api/tenant/" + tenantMsg.getUuidId(), Tenant.class); + Assert.assertNotNull(tenant); + testAutoGeneratedCodeByProtobuf(tenantUpdateMsg); + } + + private void validateTenantProfile() throws Exception { + Optional tenantProfileUpdateMsgOpt = edgeImitator.findMessageByType(TenantProfileUpdateMsg.class); + Assert.assertTrue(tenantProfileUpdateMsgOpt.isPresent()); + TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantProfileUpdateMsgOpt.get(); + Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantProfileUpdateMsg.getMsgType()); + TenantProfile tenantProfile = JacksonUtil.fromString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class, true); + Assert.assertNotNull(tenantProfile); + Tenant tenant = doGet("/api/tenant/" + tenantId.getId(), Tenant.class); + Assert.assertNotNull(tenant); + Assert.assertEquals(tenantProfile.getId(), tenant.getTenantProfileId()); + testAutoGeneratedCodeByProtobuf(tenantProfileUpdateMsg); + } + + private void validateDeviceProfiles(int expectedMsgCnt) throws Exception { List deviceProfileUpdateMsgList = edgeImitator.findAllMessagesByType(DeviceProfileUpdateMsg.class); - // default msg - // thermostat msg from fetcher + // default msg default device profile from fetcher + // default msg device profile from fetcher + // thermostat msg from device profile fetcher // thermostat msg from device fetcher - // thermostat msg from controller - // thermostat msg from creation of device - Assert.assertEquals(5, deviceProfileUpdateMsgList.size()); + Assert.assertEquals(expectedMsgCnt, deviceProfileUpdateMsgList.size()); Optional thermostatProfileUpdateMsgOpt = - deviceProfileUpdateMsgList.stream().filter(dfum -> THERMOSTAT_DEVICE_PROFILE_NAME.equals(dfum.getName())).findAny(); + deviceProfileUpdateMsgList.stream().filter(dfum -> { + DeviceProfile deviceProfile = JacksonUtil.fromString(dfum.getEntity(), DeviceProfile.class, true); + Assert.assertNotNull(deviceProfile); + return THERMOSTAT_DEVICE_PROFILE_NAME.equals(deviceProfile.getName()); + }).findAny(); Assert.assertTrue(thermostatProfileUpdateMsgOpt.isPresent()); DeviceProfileUpdateMsg thermostatProfileUpdateMsg = thermostatProfileUpdateMsgOpt.get(); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, thermostatProfileUpdateMsg.getMsgType()); @@ -311,10 +368,9 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { } private void validateDevices() throws Exception { - List deviceUpdateMsgs = edgeImitator.findAllMessagesByType(DeviceUpdateMsg.class); - Assert.assertEquals(2, deviceUpdateMsgs.size()); - validateDevice(deviceUpdateMsgs.get(0)); - validateDevice(deviceUpdateMsgs.get(1)); + Optional deviceUpdateMsgOpt = edgeImitator.findMessageByType(DeviceUpdateMsg.class); + Assert.assertTrue(deviceUpdateMsgOpt.isPresent()); + validateDevice(deviceUpdateMsgOpt.get()); } private void validateDevice(DeviceUpdateMsg deviceUpdateMsg) throws Exception { @@ -330,10 +386,9 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { } private void validateAssets() throws Exception { - List assetUpdateMsgs = edgeImitator.findAllMessagesByType(AssetUpdateMsg.class); - Assert.assertEquals(2, assetUpdateMsgs.size()); - validateAsset(assetUpdateMsgs.get(0)); - validateAsset(assetUpdateMsgs.get(1)); + Optional assetUpdateMsgOpt = edgeImitator.findMessageByType(AssetUpdateMsg.class); + Assert.assertTrue(assetUpdateMsgOpt.isPresent()); + validateAsset(assetUpdateMsgOpt.get()); } private void validateAsset(AssetUpdateMsg assetUpdateMsg) throws Exception { @@ -350,12 +405,10 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { } private UUID validateRuleChains() throws Exception { - List ruleChainUpdateMsgs = edgeImitator.findAllMessagesByType(RuleChainUpdateMsg.class); - Assert.assertEquals(2, ruleChainUpdateMsgs.size()); - RuleChainUpdateMsg ruleChainCreateMsg = ruleChainUpdateMsgs.get(0); - RuleChainUpdateMsg ruleChainUpdateMsg = ruleChainUpdateMsgs.get(1); - validateRuleChain(ruleChainCreateMsg, UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); - validateRuleChain(ruleChainUpdateMsg, UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE); + Optional ruleChainUpdateMsgOpt = edgeImitator.findMessageByType(RuleChainUpdateMsg.class); + Assert.assertTrue(ruleChainUpdateMsgOpt.isPresent()); + RuleChainUpdateMsg ruleChainUpdateMsg = ruleChainUpdateMsgOpt.get(); + validateRuleChain(ruleChainUpdateMsg, UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); return new UUID(ruleChainUpdateMsg.getIdMSB(), ruleChainUpdateMsg.getIdLSB()); } @@ -375,26 +428,39 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { Assert.assertTrue(ruleChainMetadataUpdateOpt.isPresent()); RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = ruleChainMetadataUpdateOpt.get(); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, ruleChainMetadataUpdateMsg.getMsgType()); - UUID ruleChainUUID = new UUID(ruleChainMetadataUpdateMsg.getRuleChainIdMSB(), ruleChainMetadataUpdateMsg.getRuleChainIdLSB()); - Assert.assertEquals(expectedRuleChainUUID, ruleChainUUID); + RuleChainMetaData ruleChainMetaData = JacksonUtil.fromString(ruleChainMetadataUpdateMsg.getEntity(), RuleChainMetaData.class, true); + Assert.assertNotNull(ruleChainMetaData); + Assert.assertEquals(expectedRuleChainUUID, ruleChainMetaData.getRuleChainId().getId()); } - private void validateAdminSettings() throws JsonProcessingException { + private void validateAdminSettings(int expectedMsgCnt) { List adminSettingsUpdateMsgs = edgeImitator.findAllMessagesByType(AdminSettingsUpdateMsg.class); - Assert.assertEquals(4, adminSettingsUpdateMsgs.size()); + Assert.assertEquals(expectedMsgCnt, adminSettingsUpdateMsgs.size()); for (AdminSettingsUpdateMsg adminSettingsUpdateMsg : adminSettingsUpdateMsgs) { - if (adminSettingsUpdateMsg.getKey().equals("mail")) { - validateMailAdminSettings(adminSettingsUpdateMsg); + AdminSettings adminSettings = JacksonUtil.fromString(adminSettingsUpdateMsg.getEntity(), AdminSettings.class, true); + Assert.assertNotNull(adminSettings); + if (adminSettings.getKey().equals("general")) { + validateGeneralAdminSettings(adminSettings); + } + if (adminSettings.getKey().equals("mail")) { + validateMailAdminSettings(adminSettings); + } + if (adminSettings.getKey().equals("connectivity")) { + validateConnectivityAdminSettings(adminSettings); } - if (adminSettingsUpdateMsg.getKey().equals("mailTemplates")) { - validateMailTemplatesAdminSettings(adminSettingsUpdateMsg); + if (adminSettings.getKey().equals("jwt")) { + validateJwtAdminSettings(adminSettings); } } } - private void validateMailAdminSettings(AdminSettingsUpdateMsg adminSettingsUpdateMsg) throws JsonProcessingException { - JsonNode jsonNode = JacksonUtil.toJsonNode(adminSettingsUpdateMsg.getJsonValue()); + private void validateGeneralAdminSettings(AdminSettings adminSettings) { + Assert.assertNotNull(adminSettings.getJsonValue().get("baseUrl")); + } + + private void validateMailAdminSettings(AdminSettings adminSettings) { + JsonNode jsonNode = adminSettings.getJsonValue(); Assert.assertNotNull(jsonNode.get("mailFrom")); Assert.assertNotNull(jsonNode.get("smtpProtocol")); Assert.assertNotNull(jsonNode.get("smtpHost")); @@ -402,19 +468,27 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { Assert.assertNotNull(jsonNode.get("timeout")); } - private void validateMailTemplatesAdminSettings(AdminSettingsUpdateMsg adminSettingsUpdateMsg) throws JsonProcessingException { - JsonNode jsonNode = JacksonUtil.toJsonNode(adminSettingsUpdateMsg.getJsonValue()); - Assert.assertNotNull(jsonNode.get("accountActivated")); - Assert.assertNotNull(jsonNode.get("accountLockout")); - Assert.assertNotNull(jsonNode.get("activation")); - Assert.assertNotNull(jsonNode.get("passwordWasReset")); - Assert.assertNotNull(jsonNode.get("resetPassword")); - Assert.assertNotNull(jsonNode.get("test")); + private void validateConnectivityAdminSettings(AdminSettings adminSettings) { + JsonNode jsonNode = adminSettings.getJsonValue(); + Assert.assertNotNull(jsonNode.get("http")); + Assert.assertNotNull(jsonNode.get("https")); + Assert.assertNotNull(jsonNode.get("mqtt")); + Assert.assertNotNull(jsonNode.get("mqtts")); + Assert.assertNotNull(jsonNode.get("coap")); + Assert.assertNotNull(jsonNode.get("coaps")); + } + + private void validateJwtAdminSettings(AdminSettings adminSettings) { + JsonNode jsonNode = adminSettings.getJsonValue(); + Assert.assertNotNull(jsonNode.get("tokenExpirationTime")); + Assert.assertNotNull(jsonNode.get("refreshTokenExpTime")); + Assert.assertNotNull(jsonNode.get("tokenIssuer")); + Assert.assertNotNull(jsonNode.get("tokenSigningKey")); } - private void validateAssetProfiles() throws Exception { + private void validateAssetProfiles(int expectedMsgCnt) throws Exception { List assetProfileUpdateMsgs = edgeImitator.findAllMessagesByType(AssetProfileUpdateMsg.class); - Assert.assertEquals(3, assetProfileUpdateMsgs.size()); + Assert.assertEquals(expectedMsgCnt, assetProfileUpdateMsgs.size()); AssetProfileUpdateMsg assetProfileUpdateMsg = assetProfileUpdateMsgs.get(0); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, assetProfileUpdateMsg.getMsgType()); UUID assetProfileUUID = new UUID(assetProfileUpdateMsg.getIdMSB(), assetProfileUpdateMsg.getIdLSB()); @@ -433,10 +507,10 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { UUID queueUUID = new UUID(queueUpdateMsg.getIdMSB(), queueUpdateMsg.getIdLSB()); Queue queue = doGet("/api/queues/" + queueUUID, Queue.class); Assert.assertNotNull(queue); - Assert.assertEquals(DataConstants.MAIN_QUEUE_NAME, queueUpdateMsg.getName()); - Assert.assertEquals(DataConstants.MAIN_QUEUE_TOPIC, queueUpdateMsg.getTopic()); - Assert.assertEquals(10, queueUpdateMsg.getPartitions()); - Assert.assertEquals(25, queueUpdateMsg.getPollInterval()); + Assert.assertEquals(DataConstants.MAIN_QUEUE_NAME, queue.getName()); + Assert.assertEquals(DataConstants.MAIN_QUEUE_TOPIC, queue.getTopic()); + Assert.assertEquals(10, queue.getPartitions()); + Assert.assertEquals(25, queue.getPollInterval()); testAutoGeneratedCodeByProtobuf(queueUpdateMsg); } @@ -448,7 +522,7 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { UUID userUUID = new UUID(userUpdateMsg.getIdMSB(), userUpdateMsg.getIdLSB()); User user = doGet("/api/user/" + userUUID, User.class); Assert.assertNotNull(user); - Assert.assertEquals("tenant2@thingsboard.org", userUpdateMsg.getEmail()); + Assert.assertEquals("testtenant@thingsboard.org", user.getEmail()); testAutoGeneratedCodeByProtobuf(userUpdateMsg); } @@ -463,6 +537,11 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { Assert.assertTrue(customer.isPublic()); } + private void validateSyncCompleted() { + Optional syncCompletedMsgOpt = edgeImitator.findMessageByType(SyncCompletedMsg.class); + Assert.assertTrue(syncCompletedMsgOpt.isPresent()); + } + protected Device saveDeviceOnCloudAndVerifyDeliveryToEdge() throws Exception { // create device and assign to edge Device savedDevice = saveDevice(StringUtils.randomAlphanumeric(15), thermostatDeviceProfile.getName()); diff --git a/application/src/test/java/org/thingsboard/server/edge/AlarmEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AlarmEdgeTest.java index afccc634ed..9b5056be2a 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AlarmEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AlarmEdgeTest.java @@ -21,12 +21,12 @@ import org.junit.Assert; import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.service.DaoSqlTest; @@ -47,16 +47,13 @@ public class AlarmEdgeTest extends AbstractEdgeTest { public void testSendAlarmToCloud() throws Exception { Device device = saveDeviceOnCloudAndVerifyDeliveryToEdge(); - UUID alarmUUID = UUID.randomUUID(); + Alarm edgeAlarm = buildAlarmForUplinkMsg(device.getId()); + UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); AlarmUpdateMsg.Builder alarmUpdateMgBuilder = AlarmUpdateMsg.newBuilder(); - alarmUpdateMgBuilder.setIdMSB(alarmUUID.getMostSignificantBits()); - alarmUpdateMgBuilder.setIdLSB(alarmUUID.getLeastSignificantBits()); - alarmUpdateMgBuilder.setName("alarm from edge"); - alarmUpdateMgBuilder.setStatus(AlarmStatus.ACTIVE_UNACK.name()); - alarmUpdateMgBuilder.setSeverity(AlarmSeverity.CRITICAL.name()); - alarmUpdateMgBuilder.setOriginatorName(device.getName()); - alarmUpdateMgBuilder.setOriginatorType(EntityType.DEVICE.name()); + alarmUpdateMgBuilder.setIdMSB(edgeAlarm.getUuidId().getMostSignificantBits()); + alarmUpdateMgBuilder.setIdLSB(edgeAlarm.getUuidId().getLeastSignificantBits()); + alarmUpdateMgBuilder.setEntity(JacksonUtil.toString(edgeAlarm)); testAutoGeneratedCodeByProtobuf(alarmUpdateMgBuilder); uplinkMsgBuilder.addAlarmUpdateMsg(alarmUpdateMgBuilder.build()); @@ -73,7 +70,7 @@ public class AlarmEdgeTest extends AbstractEdgeTest { Optional foundAlarm = alarms.stream().filter(alarm -> alarm.getType().equals("alarm from edge")).findAny(); Assert.assertTrue(foundAlarm.isPresent()); AlarmInfo alarmInfo = foundAlarm.get(); - Assert.assertEquals(new AlarmId(alarmUUID), alarmInfo.getId()); + Assert.assertEquals(edgeAlarm.getId(), alarmInfo.getId()); Assert.assertEquals(device.getId(), alarmInfo.getOriginator()); Assert.assertEquals(AlarmStatus.ACTIVE_UNACK, alarmInfo.getStatus()); Assert.assertEquals(AlarmSeverity.CRITICAL, alarmInfo.getSeverity()); @@ -93,28 +90,10 @@ public class AlarmEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg); AlarmUpdateMsg alarmUpdateMsg = (AlarmUpdateMsg) latestMessage; + Alarm alarmMsg = JacksonUtil.fromString(alarmUpdateMsg.getEntity(), Alarm.class, true); + Assert.assertNotNull(alarmMsg); + Assert.assertEquals(savedAlarm, alarmMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, alarmUpdateMsg.getMsgType()); - Assert.assertEquals(savedAlarm.getUuidId().getMostSignificantBits(), alarmUpdateMsg.getIdMSB()); - Assert.assertEquals(savedAlarm.getUuidId().getLeastSignificantBits(), alarmUpdateMsg.getIdLSB()); - Assert.assertEquals(savedAlarm.getType(), alarmUpdateMsg.getType()); - Assert.assertEquals(savedAlarm.getName(), alarmUpdateMsg.getName()); - Assert.assertEquals(device.getName(), alarmUpdateMsg.getOriginatorName()); - Assert.assertEquals(savedAlarm.getStatus().name(), alarmUpdateMsg.getStatus()); - Assert.assertEquals(savedAlarm.getSeverity().name(), alarmUpdateMsg.getSeverity()); - - // update alarm - String updatedDetails = "{\"testKey\":\"testValue\"}"; - savedAlarm.setDetails(JacksonUtil.OBJECT_MAPPER.readTree(updatedDetails)); - edgeImitator.expectMessageAmount(1); - savedAlarm = doPost("/api/alarm", savedAlarm, Alarm.class); - Assert.assertTrue(edgeImitator.waitForMessages()); - latestMessage = edgeImitator.getLatestMessage(); - Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg); - alarmUpdateMsg = (AlarmUpdateMsg) latestMessage; - Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, alarmUpdateMsg.getMsgType()); - Assert.assertEquals(savedAlarm.getUuidId().getMostSignificantBits(), alarmUpdateMsg.getIdMSB()); - Assert.assertEquals(savedAlarm.getUuidId().getLeastSignificantBits(), alarmUpdateMsg.getIdLSB()); - Assert.assertEquals(updatedDetails, alarmUpdateMsg.getDetails()); // ack alarm edgeImitator.expectMessageAmount(1); @@ -124,10 +103,11 @@ public class AlarmEdgeTest extends AbstractEdgeTest { Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg); alarmUpdateMsg = (AlarmUpdateMsg) latestMessage; Assert.assertEquals(UpdateMsgType.ALARM_ACK_RPC_MESSAGE, alarmUpdateMsg.getMsgType()); - Assert.assertEquals(savedAlarm.getType(), alarmUpdateMsg.getType()); - Assert.assertEquals(savedAlarm.getName(), alarmUpdateMsg.getName()); - Assert.assertEquals(device.getName(), alarmUpdateMsg.getOriginatorName()); - Assert.assertEquals(AlarmStatus.ACTIVE_ACK.name(), alarmUpdateMsg.getStatus()); + alarmMsg = JacksonUtil.fromString(alarmUpdateMsg.getEntity(), Alarm.class, true); + Assert.assertNotNull(alarmMsg); + Assert.assertEquals(savedAlarm.getType(), alarmMsg.getType()); + Assert.assertEquals(savedAlarm.getName(), alarmMsg.getName()); + Assert.assertEquals(AlarmStatus.ACTIVE_ACK, alarmMsg.getStatus()); // clear alarm edgeImitator.expectMessageAmount(1); @@ -137,10 +117,11 @@ public class AlarmEdgeTest extends AbstractEdgeTest { Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg); alarmUpdateMsg = (AlarmUpdateMsg) latestMessage; Assert.assertEquals(UpdateMsgType.ALARM_CLEAR_RPC_MESSAGE, alarmUpdateMsg.getMsgType()); - Assert.assertEquals(savedAlarm.getType(), alarmUpdateMsg.getType()); - Assert.assertEquals(savedAlarm.getName(), alarmUpdateMsg.getName()); - Assert.assertEquals(device.getName(), alarmUpdateMsg.getOriginatorName()); - Assert.assertEquals(AlarmStatus.CLEARED_ACK.name(), alarmUpdateMsg.getStatus()); + alarmMsg = JacksonUtil.fromString(alarmUpdateMsg.getEntity(), Alarm.class, true); + Assert.assertNotNull(alarmMsg); + Assert.assertEquals(savedAlarm.getType(), alarmMsg.getType()); + Assert.assertEquals(savedAlarm.getName(), alarmMsg.getName()); + Assert.assertEquals(AlarmStatus.CLEARED_ACK, alarmMsg.getStatus()); // delete alarm edgeImitator.expectMessageAmount(1); @@ -151,10 +132,20 @@ public class AlarmEdgeTest extends AbstractEdgeTest { Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg); alarmUpdateMsg = (AlarmUpdateMsg) latestMessage; Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, alarmUpdateMsg.getMsgType()); - Assert.assertEquals(savedAlarm.getType(), alarmUpdateMsg.getType()); - Assert.assertEquals(savedAlarm.getName(), alarmUpdateMsg.getName()); - Assert.assertEquals(device.getName(), alarmUpdateMsg.getOriginatorName()); - Assert.assertEquals(AlarmStatus.CLEARED_ACK.name(), alarmUpdateMsg.getStatus()); + alarmMsg = JacksonUtil.fromString(alarmUpdateMsg.getEntity(), Alarm.class, true); + Assert.assertNotNull(alarmMsg); + Assert.assertEquals(savedAlarm.getType(), alarmMsg.getType()); + Assert.assertEquals(savedAlarm.getName(), alarmMsg.getName()); + Assert.assertEquals(AlarmStatus.CLEARED_ACK, alarmMsg.getStatus()); } + private Alarm buildAlarmForUplinkMsg(DeviceId deviceId) { + Alarm alarm = new Alarm(); + alarm.setId(new AlarmId(UUID.randomUUID())); + alarm.setTenantId(tenantId); + alarm.setType("alarm from edge"); + alarm.setOriginator(deviceId); + alarm.setSeverity(AlarmSeverity.CRITICAL); + return alarm; + } } diff --git a/application/src/test/java/org/thingsboard/server/edge/AssetEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AssetEdgeTest.java index 2f5848ece6..fe75a35be8 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AssetEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AssetEdgeTest.java @@ -15,19 +15,29 @@ */ package org.thingsboard.server.edge; +import com.fasterxml.jackson.core.type.TypeReference; import com.google.protobuf.AbstractMessage; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.AssetInfo; import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.gen.edge.v1.UplinkMsg; +import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -47,11 +57,10 @@ public class AssetEdgeTest extends AbstractEdgeTest { Optional assetUpdateMsgOpt = edgeImitator.findMessageByType(AssetUpdateMsg.class); Assert.assertTrue(assetUpdateMsgOpt.isPresent()); AssetUpdateMsg assetUpdateMsg = assetUpdateMsgOpt.get(); + Asset assetMsg = JacksonUtil.fromString(assetUpdateMsg.getEntity(), Asset.class, true); + Assert.assertNotNull(assetMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); - Assert.assertEquals(savedAsset.getUuidId().getMostSignificantBits(), assetUpdateMsg.getIdMSB()); - Assert.assertEquals(savedAsset.getUuidId().getLeastSignificantBits(), assetUpdateMsg.getIdLSB()); - Assert.assertEquals(savedAsset.getName(), assetUpdateMsg.getName()); - Assert.assertEquals(savedAsset.getType(), assetUpdateMsg.getType()); + Assert.assertEquals(savedAsset, assetMsg); Optional assetProfileUpdateMsgOpt = edgeImitator.findMessageByType(AssetProfileUpdateMsg.class); Assert.assertTrue(assetProfileUpdateMsgOpt.isPresent()); AssetProfileUpdateMsg assetProfileUpdateMsg = assetProfileUpdateMsgOpt.get(); @@ -67,8 +76,10 @@ public class AssetEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof AssetUpdateMsg); assetUpdateMsg = (AssetUpdateMsg) latestMessage; + assetMsg = JacksonUtil.fromString(assetUpdateMsg.getEntity(), Asset.class, true); + Assert.assertNotNull(assetMsg); + Assert.assertEquals(savedAsset.getName(), assetMsg.getName()); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); - Assert.assertEquals(savedAsset.getName(), assetUpdateMsg.getName()); // unassign asset from edge edgeImitator.expectMessageAmount(1); @@ -82,11 +93,11 @@ public class AssetEdgeTest extends AbstractEdgeTest { Assert.assertEquals(savedAsset.getUuidId().getMostSignificantBits(), assetUpdateMsg.getIdMSB()); Assert.assertEquals(savedAsset.getUuidId().getLeastSignificantBits(), assetUpdateMsg.getIdLSB()); - // delete asset - no messages expected + // delete asset - message expected, it was sent to all edges edgeImitator.expectMessageAmount(1); doDelete("/api/asset/" + savedAsset.getUuidId()) .andExpect(status().isOk()); - Assert.assertFalse(edgeImitator.waitForMessages(1)); + Assert.assertTrue(edgeImitator.waitForMessages(5)); // create asset #2 and assign to edge edgeImitator.expectMessageAmount(2); @@ -94,15 +105,13 @@ public class AssetEdgeTest extends AbstractEdgeTest { doPost("/api/edge/" + edge.getUuidId() + "/asset/" + savedAsset.getUuidId(), Asset.class); Assert.assertTrue(edgeImitator.waitForMessages()); - latestMessage = edgeImitator.getLatestMessage(); assetUpdateMsgOpt = edgeImitator.findMessageByType(AssetUpdateMsg.class); Assert.assertTrue(assetUpdateMsgOpt.isPresent()); assetUpdateMsg = assetUpdateMsgOpt.get(); + assetMsg = JacksonUtil.fromString(assetUpdateMsg.getEntity(), Asset.class, true); + Assert.assertNotNull(assetMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); - Assert.assertEquals(savedAsset.getUuidId().getMostSignificantBits(), assetUpdateMsg.getIdMSB()); - Assert.assertEquals(savedAsset.getUuidId().getLeastSignificantBits(), assetUpdateMsg.getIdLSB()); - Assert.assertEquals(savedAsset.getName(), assetUpdateMsg.getName()); - Assert.assertEquals(savedAsset.getType(), assetUpdateMsg.getType()); + Assert.assertEquals(savedAsset, assetMsg); assetProfileUpdateMsgOpt = edgeImitator.findMessageByType(AssetProfileUpdateMsg.class); Assert.assertTrue(assetProfileUpdateMsgOpt.isPresent()); assetProfileUpdateMsg = assetProfileUpdateMsgOpt.get(); @@ -126,9 +135,10 @@ public class AssetEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof AssetUpdateMsg); assetUpdateMsg = (AssetUpdateMsg) latestMessage; + assetMsg = JacksonUtil.fromString(assetUpdateMsg.getEntity(), Asset.class, true); + Assert.assertNotNull(assetMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); - Assert.assertEquals(savedCustomer.getUuidId().getMostSignificantBits(), assetUpdateMsg.getCustomerIdMSB()); - Assert.assertEquals(savedCustomer.getUuidId().getLeastSignificantBits(), assetUpdateMsg.getCustomerIdLSB()); + Assert.assertEquals(savedCustomer.getId(), assetMsg.getCustomerId()); // unassign asset #2 from customer edgeImitator.expectMessageAmount(1); @@ -137,10 +147,10 @@ public class AssetEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof AssetUpdateMsg); assetUpdateMsg = (AssetUpdateMsg) latestMessage; + assetMsg = JacksonUtil.fromString(assetUpdateMsg.getEntity(), Asset.class, true); + Assert.assertNotNull(assetMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); - Assert.assertEquals( - new CustomerId(EntityId.NULL_UUID), - new CustomerId(new UUID(assetUpdateMsg.getCustomerIdMSB(), assetUpdateMsg.getCustomerIdLSB()))); + Assert.assertEquals(new CustomerId(EntityId.NULL_UUID), assetMsg.getCustomerId()); // delete asset #2 - messages expected edgeImitator.expectMessageAmount(1); @@ -155,5 +165,121 @@ public class AssetEdgeTest extends AbstractEdgeTest { Assert.assertEquals(savedAsset.getUuidId().getLeastSignificantBits(), assetUpdateMsg.getIdLSB()); } + @Test + public void testSendAssetToCloud() throws Exception { + Asset asset = buildAssetForUplinkMsg("Asset Edge 2"); + + UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); + AssetUpdateMsg.Builder assetUpdateMsgBuilder = AssetUpdateMsg.newBuilder(); + assetUpdateMsgBuilder.setIdMSB(asset.getUuidId().getMostSignificantBits()); + assetUpdateMsgBuilder.setIdLSB(asset.getUuidId().getLeastSignificantBits()); + assetUpdateMsgBuilder.setEntity(JacksonUtil.toString(asset)); + assetUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); + testAutoGeneratedCodeByProtobuf(assetUpdateMsgBuilder); + uplinkMsgBuilder.addAssetUpdateMsg(assetUpdateMsgBuilder.build()); + + testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); + + edgeImitator.expectResponsesAmount(1); + edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); + + Assert.assertTrue(edgeImitator.waitForResponses()); + + UplinkResponseMsg latestResponseMsg = edgeImitator.getLatestResponseMsg(); + Assert.assertTrue(latestResponseMsg.getSuccess()); + + Asset foundAsset = doGet("/api/asset/" + asset.getUuidId(), Asset.class); + Assert.assertNotNull(foundAsset); + Assert.assertEquals("Asset Edge 2", foundAsset.getName()); + } + @Test + public void testSendAssetToCloudWithNameThatAlreadyExistsOnCloud() throws Exception { + String assetOnCloudName = StringUtils.randomAlphanumeric(15); + Asset assetOnCloud = saveAsset(assetOnCloudName); + + Asset assetOnEdge = buildAssetForUplinkMsg(assetOnCloudName); + + UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); + AssetUpdateMsg.Builder assetUpdateMsgBuilder = AssetUpdateMsg.newBuilder(); + assetUpdateMsgBuilder.setEntity(JacksonUtil.toString(assetOnEdge)); + assetUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); + testAutoGeneratedCodeByProtobuf(assetUpdateMsgBuilder); + uplinkMsgBuilder.addAssetUpdateMsg(assetUpdateMsgBuilder.build()); + + testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); + + edgeImitator.expectResponsesAmount(1); + edgeImitator.expectMessageAmount(1); + + edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); + + Assert.assertTrue(edgeImitator.waitForResponses()); + Assert.assertTrue(edgeImitator.waitForMessages()); + + Optional assetUpdateMsgOpt = edgeImitator.findMessageByType(AssetUpdateMsg.class); + Assert.assertTrue(assetUpdateMsgOpt.isPresent()); + AssetUpdateMsg latestAssetUpdateMsg = assetUpdateMsgOpt.get(); + Asset assetMsg = JacksonUtil.fromString(latestAssetUpdateMsg.getEntity(), Asset.class, true); + Assert.assertNotNull(assetMsg); + Assert.assertNotEquals(assetOnCloudName, assetMsg.getName()); + + UUID newAssetId = new UUID(latestAssetUpdateMsg.getIdMSB(), latestAssetUpdateMsg.getIdLSB()); + + Assert.assertNotEquals(assetOnCloud.getUuidId(), newAssetId); + + Asset asset = doGet("/api/asset/" + newAssetId, Asset.class); + Assert.assertNotNull(asset); + Assert.assertNotEquals(assetOnCloudName, asset.getName()); + } + + @Test + public void testSendDeleteAssetOnEdgeToCloud() throws Exception { + Asset savedAsset = saveAssetOnCloudAndVerifyDeliveryToEdge(); + UplinkMsg.Builder upLinkMsgBuilder = UplinkMsg.newBuilder(); + AssetUpdateMsg.Builder assetDeleteMsgBuilder = AssetUpdateMsg.newBuilder(); + assetDeleteMsgBuilder.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE); + assetDeleteMsgBuilder.setIdMSB(savedAsset.getUuidId().getMostSignificantBits()); + assetDeleteMsgBuilder.setIdLSB(savedAsset.getUuidId().getLeastSignificantBits()); + testAutoGeneratedCodeByProtobuf(assetDeleteMsgBuilder); + + upLinkMsgBuilder.addAssetUpdateMsg(assetDeleteMsgBuilder.build()); + + testAutoGeneratedCodeByProtobuf(upLinkMsgBuilder); + + edgeImitator.expectResponsesAmount(1); + edgeImitator.sendUplinkMsg(upLinkMsgBuilder.build()); + Assert.assertTrue(edgeImitator.waitForResponses()); + AssetInfo assetInfo = doGet("/api/asset/info/" + savedAsset.getUuidId(), AssetInfo.class); + Assert.assertNotNull(assetInfo); + List edgeAssets = doGetTypedWithPageLink("/api/edge/" + edge.getUuidId() + "/assets?", + new TypeReference>() { + }, new PageLink(100)).getData(); + Assert.assertFalse(edgeAssets.contains(assetInfo)); + } + + private Asset saveAssetOnCloudAndVerifyDeliveryToEdge() throws Exception { + // create asset and assign to edge + Asset savedAsset = saveAsset(StringUtils.randomAlphanumeric(15)); + edgeImitator.expectMessageAmount(2); // asset and asset profile messages + doPost("/api/edge/" + edge.getUuidId() + + "/asset/" + savedAsset.getUuidId(), Asset.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + Optional assetUpdateMsgOpt = edgeImitator.findMessageByType(AssetUpdateMsg.class); + Assert.assertTrue(assetUpdateMsgOpt.isPresent()); + AssetUpdateMsg assetUpdateMsg = assetUpdateMsgOpt.get(); + Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, assetUpdateMsg.getMsgType()); + Assert.assertEquals(savedAsset.getUuidId().getMostSignificantBits(), assetUpdateMsg.getIdMSB()); + Assert.assertEquals(savedAsset.getUuidId().getLeastSignificantBits(), assetUpdateMsg.getIdLSB()); + return savedAsset; + } + + private Asset buildAssetForUplinkMsg(String name) { + Asset asset = new Asset(); + asset.setId(new AssetId(UUID.randomUUID())); + asset.setTenantId(tenantId); + asset.setName(name); + asset.setType("test"); + return asset; + } } diff --git a/application/src/test/java/org/thingsboard/server/edge/AssetProfileEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AssetProfileEdgeTest.java index fd36c11db8..efa36af9ce 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AssetProfileEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AssetProfileEdgeTest.java @@ -16,16 +16,22 @@ package org.thingsboard.server.edge; import com.google.protobuf.AbstractMessage; -import com.google.protobuf.ByteString; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.common.data.id.AssetProfileId; +import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.gen.edge.v1.UplinkMsg; +import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg; -import java.nio.charset.StandardCharsets; +import java.util.Optional; +import java.util.UUID; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -45,12 +51,12 @@ public class AssetProfileEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof AssetProfileUpdateMsg); AssetProfileUpdateMsg assetProfileUpdateMsg = (AssetProfileUpdateMsg) latestMessage; + AssetProfile assetProfileMsg = JacksonUtil.fromString(assetProfileUpdateMsg.getEntity(), AssetProfile.class, true); + Assert.assertNotNull(assetProfileMsg); + Assert.assertEquals(assetProfile, assetProfileMsg); + Assert.assertEquals("Building", assetProfileMsg.getName()); + Assert.assertEquals(buildingsRuleChainId, assetProfileMsg.getDefaultEdgeRuleChainId()); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, assetProfileUpdateMsg.getMsgType()); - Assert.assertEquals(assetProfile.getUuidId().getMostSignificantBits(), assetProfileUpdateMsg.getIdMSB()); - Assert.assertEquals(assetProfile.getUuidId().getLeastSignificantBits(), assetProfileUpdateMsg.getIdLSB()); - Assert.assertEquals("Building", assetProfileUpdateMsg.getName()); - Assert.assertEquals(buildingsRuleChainId.getId().getMostSignificantBits(), assetProfileUpdateMsg.getDefaultRuleChainIdMSB()); - Assert.assertEquals(buildingsRuleChainId.getId().getLeastSignificantBits(), assetProfileUpdateMsg.getDefaultRuleChainIdLSB()); // update asset profile assetProfile.setImage("IMAGE"); @@ -60,8 +66,10 @@ public class AssetProfileEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof AssetProfileUpdateMsg); assetProfileUpdateMsg = (AssetProfileUpdateMsg) latestMessage; + assetProfileMsg = JacksonUtil.fromString(assetProfileUpdateMsg.getEntity(), AssetProfile.class, true); + Assert.assertNotNull(assetProfileMsg); + Assert.assertEquals("IMAGE", assetProfileMsg.getImage()); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, assetProfileUpdateMsg.getMsgType()); - Assert.assertEquals(ByteString.copyFrom("IMAGE".getBytes(StandardCharsets.UTF_8)), assetProfileUpdateMsg.getImage()); // delete profile edgeImitator.expectMessageAmount(1); @@ -77,4 +85,106 @@ public class AssetProfileEdgeTest extends AbstractEdgeTest { unAssignFromEdgeAndDeleteRuleChain(buildingsRuleChainId); } + + @Test + public void testSendAssetProfileToCloud() throws Exception { + RuleChainId edgeRuleChainId = createEdgeRuleChainAndAssignToEdge("Asset Profile Rule Chain"); + DashboardId dashboardId = createDashboardAndAssignToEdge("Asset Profile Dashboard"); + + AssetProfile assetProfileOnEdge = buildAssetProfileForUplinkMsg("Asset Profile On Edge"); + assetProfileOnEdge.setDefaultRuleChainId(edgeRuleChainId); + assetProfileOnEdge.setDefaultDashboardId(dashboardId); + + UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); + AssetProfileUpdateMsg.Builder assetProfileUpdateMsgBuilder = AssetProfileUpdateMsg.newBuilder(); + assetProfileUpdateMsgBuilder.setIdMSB(assetProfileOnEdge.getUuidId().getMostSignificantBits()); + assetProfileUpdateMsgBuilder.setIdLSB(assetProfileOnEdge.getUuidId().getLeastSignificantBits()); + assetProfileUpdateMsgBuilder.setEntity(JacksonUtil.toString(assetProfileOnEdge)); + assetProfileUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); + testAutoGeneratedCodeByProtobuf(assetProfileUpdateMsgBuilder); + uplinkMsgBuilder.addAssetProfileUpdateMsg(assetProfileUpdateMsgBuilder.build()); + + testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); + + edgeImitator.expectResponsesAmount(1); + edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); + + Assert.assertTrue(edgeImitator.waitForResponses()); + + UplinkResponseMsg latestResponseMsg = edgeImitator.getLatestResponseMsg(); + Assert.assertTrue(latestResponseMsg.getSuccess()); + + AssetProfile assetProfile = doGet("/api/assetProfile/" + assetProfileOnEdge.getUuidId(), AssetProfile.class); + Assert.assertNotNull(assetProfile); + Assert.assertEquals("Asset Profile On Edge", assetProfile.getName()); + Assert.assertEquals(dashboardId, assetProfile.getDefaultDashboardId()); + Assert.assertNull(assetProfile.getDefaultRuleChainId()); + Assert.assertEquals(edgeRuleChainId, assetProfile.getDefaultEdgeRuleChainId()); + + // delete profile + edgeImitator.expectMessageAmount(1); + doDelete("/api/assetProfile/" + assetProfile.getUuidId()) + .andExpect(status().isOk()); + Assert.assertTrue(edgeImitator.waitForMessages()); + AbstractMessage latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof AssetProfileUpdateMsg); + AssetProfileUpdateMsg assetProfileUpdateMsg = (AssetProfileUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, assetProfileUpdateMsg.getMsgType()); + Assert.assertEquals(assetProfile.getUuidId().getMostSignificantBits(), assetProfileUpdateMsg.getIdMSB()); + Assert.assertEquals(assetProfile.getUuidId().getLeastSignificantBits(), assetProfileUpdateMsg.getIdLSB()); + + // cleanup + unAssignFromEdgeAndDeleteDashboard(dashboardId); + unAssignFromEdgeAndDeleteRuleChain(edgeRuleChainId); + } + + @Test + public void testSendAssetProfileToCloudWithNameThatAlreadyExistsOnCloud() throws Exception { + String assetProfileOnCloudName = StringUtils.randomAlphanumeric(15); + + edgeImitator.expectMessageAmount(1); + AssetProfile assetProfileOnCloud = this.createAssetProfile(assetProfileOnCloudName); + assetProfileOnCloud = doPost("/api/assetProfile", assetProfileOnCloud, AssetProfile.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + + AssetProfile assetProfileOnEdge = buildAssetProfileForUplinkMsg(assetProfileOnCloudName); + + UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); + AssetProfileUpdateMsg.Builder assetProfileUpdateMsgBuilder = AssetProfileUpdateMsg.newBuilder(); + assetProfileUpdateMsgBuilder.setEntity(JacksonUtil.toString(assetProfileOnEdge)); + assetProfileUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); + uplinkMsgBuilder.addAssetProfileUpdateMsg(assetProfileUpdateMsgBuilder.build()); + + testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); + + edgeImitator.expectResponsesAmount(1); + edgeImitator.expectMessageAmount(1); + + edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); + + Assert.assertTrue(edgeImitator.waitForResponses()); + Assert.assertTrue(edgeImitator.waitForMessages()); + + Optional assetProfileUpdateMsgOpt = edgeImitator.findMessageByType(AssetProfileUpdateMsg.class); + Assert.assertTrue(assetProfileUpdateMsgOpt.isPresent()); + AssetProfileUpdateMsg latestAssetProfileUpdateMsg = assetProfileUpdateMsgOpt.get(); + AssetProfile assetProfileMsg = JacksonUtil.fromString(latestAssetProfileUpdateMsg.getEntity(), AssetProfile.class, true); + Assert.assertNotNull(assetProfileMsg); + Assert.assertNotEquals(assetProfileOnCloudName, assetProfileMsg.getName()); + + Assert.assertNotEquals(assetProfileOnCloud.getUuidId(), assetProfileOnEdge.getUuidId()); + + AssetProfile assetProfile = doGet("/api/assetProfile/" + assetProfileMsg.getUuidId(), AssetProfile.class); + Assert.assertNotNull(assetProfile); + Assert.assertNotEquals(assetProfileOnCloudName, assetProfile.getName()); + } + + private AssetProfile buildAssetProfileForUplinkMsg(String name) { + AssetProfile assetProfile = new AssetProfile(); + assetProfile.setId(new AssetProfileId(UUID.randomUUID())); + assetProfile.setTenantId(tenantId); + assetProfile.setName(name); + assetProfile.setDefault(false); + return assetProfile; + } } diff --git a/application/src/test/java/org/thingsboard/server/edge/CustomerEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/CustomerEdgeTest.java index b9bf41fde6..f8584a9f0d 100644 --- a/application/src/test/java/org/thingsboard/server/edge/CustomerEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/CustomerEdgeTest.java @@ -18,14 +18,18 @@ package org.thingsboard.server.edge; import com.google.protobuf.AbstractMessage; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg; import org.thingsboard.server.gen.edge.v1.EdgeConfiguration; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import java.util.Optional; +import java.util.UUID; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -39,7 +43,7 @@ public class CustomerEdgeTest extends AbstractEdgeTest { Customer customer = new Customer(); customer.setTitle("Edge Customer"); Customer savedCustomer = doPost("/api/customer", customer, Customer.class); - Assert.assertFalse(edgeImitator.waitForMessages(1)); + Assert.assertFalse(edgeImitator.waitForMessages(5)); // assign edge to customer edgeImitator.expectMessageAmount(2); @@ -54,10 +58,9 @@ public class CustomerEdgeTest extends AbstractEdgeTest { Optional customerUpdateOpt = edgeImitator.findMessageByType(CustomerUpdateMsg.class); Assert.assertTrue(customerUpdateOpt.isPresent()); CustomerUpdateMsg customerUpdateMsg = customerUpdateOpt.get(); + Customer customerMsg = JacksonUtil.fromString(customerUpdateMsg.getEntity(), Customer.class, true); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, customerUpdateMsg.getMsgType()); - Assert.assertEquals(savedCustomer.getUuidId().getMostSignificantBits(), customerUpdateMsg.getIdMSB()); - Assert.assertEquals(savedCustomer.getUuidId().getLeastSignificantBits(), customerUpdateMsg.getIdLSB()); - Assert.assertEquals(savedCustomer.getTitle(), customerUpdateMsg.getTitle()); + Assert.assertEquals(savedCustomer, customerMsg); testAutoGeneratedCodeByProtobuf(customerUpdateMsg); // update customer @@ -68,19 +71,24 @@ public class CustomerEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof CustomerUpdateMsg); customerUpdateMsg = (CustomerUpdateMsg) latestMessage; + customerMsg = JacksonUtil.fromString(customerUpdateMsg.getEntity(), Customer.class, true); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, customerUpdateMsg.getMsgType()); - Assert.assertEquals(savedCustomer.getUuidId().getMostSignificantBits(), customerUpdateMsg.getIdMSB()); - Assert.assertEquals(savedCustomer.getUuidId().getLeastSignificantBits(), customerUpdateMsg.getIdLSB()); - Assert.assertEquals(savedCustomer.getTitle(), customerUpdateMsg.getTitle()); + Assert.assertEquals(savedCustomer, customerMsg); // delete customer - edgeImitator.expectMessageAmount(1); + edgeImitator.expectMessageAmount(2); doDelete("/api/customer/" + savedCustomer.getUuidId()) .andExpect(status().isOk()); Assert.assertTrue(edgeImitator.waitForMessages()); - latestMessage = edgeImitator.getLatestMessage(); - Assert.assertTrue(latestMessage instanceof CustomerUpdateMsg); - customerUpdateMsg = (CustomerUpdateMsg) latestMessage; + edgeConfigurationOpt = edgeImitator.findMessageByType(EdgeConfiguration.class); + Assert.assertTrue(edgeConfigurationOpt.isPresent()); + edgeConfiguration = edgeConfigurationOpt.get(); + Assert.assertEquals( + new CustomerId(EntityId.NULL_UUID), + new CustomerId(new UUID(edgeConfiguration.getCustomerIdMSB(), edgeConfiguration.getCustomerIdLSB()))); + customerUpdateOpt = edgeImitator.findMessageByType(CustomerUpdateMsg.class); + Assert.assertTrue(customerUpdateOpt.isPresent()); + customerUpdateMsg = customerUpdateOpt.get(); Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, customerUpdateMsg.getMsgType()); Assert.assertEquals(customerUpdateMsg.getIdMSB(), savedCustomer.getUuidId().getMostSignificantBits()); Assert.assertEquals(customerUpdateMsg.getIdLSB(), savedCustomer.getUuidId().getLeastSignificantBits()); diff --git a/application/src/test/java/org/thingsboard/server/edge/DashboardEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/DashboardEdgeTest.java index 988d6fdc98..9260621359 100644 --- a/application/src/test/java/org/thingsboard/server/edge/DashboardEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/DashboardEdgeTest.java @@ -22,25 +22,39 @@ import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.ShortCustomerInfo; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.gen.edge.v1.UplinkMsg; -import java.util.Set; +import java.util.List; +import java.util.Optional; +import java.util.UUID; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @DaoSqlTest public class DashboardEdgeTest extends AbstractEdgeTest { + private static final int MOBILE_ORDER = 5; + private static final String IMAGE = "data:image/png;base64,iVBORw0KGgoA"; + @Test public void testDashboards() throws Exception { // create dashboard and assign to edge edgeImitator.expectMessageAmount(1); Dashboard dashboard = new Dashboard(); dashboard.setTitle("Edge Test Dashboard"); + dashboard.setMobileHide(true); + dashboard.setImage(IMAGE); + dashboard.setMobileOrder(MOBILE_ORDER); Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); doPost("/api/edge/" + edge.getUuidId() + "/dashboard/" + savedDashboard.getUuidId(), Dashboard.class); @@ -48,10 +62,12 @@ public class DashboardEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); DashboardUpdateMsg dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; + Dashboard dashboardMsg = JacksonUtil.fromString(dashboardUpdateMsg.getEntity(), Dashboard.class, true); + Assert.assertNotNull(dashboardMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); - Assert.assertEquals(savedDashboard.getUuidId().getMostSignificantBits(), dashboardUpdateMsg.getIdMSB()); - Assert.assertEquals(savedDashboard.getUuidId().getLeastSignificantBits(), dashboardUpdateMsg.getIdLSB()); - Assert.assertEquals(savedDashboard.getTitle(), dashboardUpdateMsg.getTitle()); + Assert.assertEquals(savedDashboard, dashboardMsg); + Assert.assertEquals(IMAGE, dashboardMsg.getImage()); + Assert.assertEquals(MOBILE_ORDER, dashboardMsg.getMobileOrder().intValue()); testAutoGeneratedCodeByProtobuf(dashboardUpdateMsg); // update dashboard @@ -62,8 +78,10 @@ public class DashboardEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; + dashboardMsg = JacksonUtil.fromString(dashboardUpdateMsg.getEntity(), Dashboard.class, true); + Assert.assertNotNull(dashboardMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); - Assert.assertEquals(savedDashboard.getTitle(), dashboardUpdateMsg.getTitle()); + Assert.assertEquals(savedDashboard.getTitle(), dashboardMsg.getTitle()); // unassign dashboard from edge edgeImitator.expectMessageAmount(1); @@ -77,11 +95,11 @@ public class DashboardEdgeTest extends AbstractEdgeTest { Assert.assertEquals(savedDashboard.getUuidId().getMostSignificantBits(), dashboardUpdateMsg.getIdMSB()); Assert.assertEquals(savedDashboard.getUuidId().getLeastSignificantBits(), dashboardUpdateMsg.getIdLSB()); - // delete dashboard - no messages expected + // delete dashboard - message expected, it was sent to all edges edgeImitator.expectMessageAmount(1); doDelete("/api/dashboard/" + savedDashboard.getUuidId()) .andExpect(status().isOk()); - Assert.assertFalse(edgeImitator.waitForMessages(1)); + Assert.assertTrue(edgeImitator.waitForMessages(5)); // create dashboard #2 and assign to edge edgeImitator.expectMessageAmount(1); @@ -94,10 +112,12 @@ public class DashboardEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; + dashboardMsg = JacksonUtil.fromString(dashboardUpdateMsg.getEntity(), Dashboard.class, true); + Assert.assertNotNull(dashboardMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); Assert.assertEquals(savedDashboard.getUuidId().getMostSignificantBits(), dashboardUpdateMsg.getIdMSB()); Assert.assertEquals(savedDashboard.getUuidId().getLeastSignificantBits(), dashboardUpdateMsg.getIdLSB()); - Assert.assertEquals(savedDashboard.getTitle(), dashboardUpdateMsg.getTitle()); + Assert.assertEquals(savedDashboard.getTitle(), dashboardMsg.getTitle()); // assign dashboard #2 to customer Customer customer = new Customer(); @@ -115,12 +135,12 @@ public class DashboardEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; + dashboardMsg = JacksonUtil.fromString(dashboardUpdateMsg.getEntity(), Dashboard.class, true); + Assert.assertNotNull(dashboardMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); - Set assignedCustomers = - JacksonUtil.fromString(dashboardUpdateMsg.getAssignedCustomers(), new TypeReference<>() {}); - Assert.assertNotNull(assignedCustomers); - Assert.assertFalse(assignedCustomers.isEmpty()); - Assert.assertTrue(assignedCustomers.contains(new ShortCustomerInfo(savedCustomer.getId(), customer.getTitle(), customer.isPublic()))); + Assert.assertNotNull(dashboardMsg.getAssignedCustomers()); + Assert.assertFalse(dashboardMsg.getAssignedCustomers().isEmpty()); + Assert.assertTrue(dashboardMsg.getAssignedCustomers().contains(new ShortCustomerInfo(savedCustomer.getId(), customer.getTitle(), customer.isPublic()))); // unassign dashboard #2 from customer edgeImitator.expectMessageAmount(1); @@ -130,11 +150,11 @@ public class DashboardEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DashboardUpdateMsg); dashboardUpdateMsg = (DashboardUpdateMsg) latestMessage; + dashboardMsg = JacksonUtil.fromString(dashboardUpdateMsg.getEntity(), Dashboard.class, true); + Assert.assertNotNull(dashboardMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, dashboardUpdateMsg.getMsgType()); - assignedCustomers = - JacksonUtil.fromString(dashboardUpdateMsg.getAssignedCustomers(), new TypeReference<>() {}); - Assert.assertNotNull(assignedCustomers); - Assert.assertTrue(assignedCustomers.isEmpty()); + Assert.assertNotNull(dashboardMsg.getAssignedCustomers()); + Assert.assertTrue(dashboardMsg.getAssignedCustomers().isEmpty()); // delete dashboard #2 - messages expected edgeImitator.expectMessageAmount(1); @@ -149,4 +169,81 @@ public class DashboardEdgeTest extends AbstractEdgeTest { Assert.assertEquals(savedDashboard.getUuidId().getLeastSignificantBits(), dashboardUpdateMsg.getIdLSB()); } + @Test + public void testSendDashboardToCloud() throws Exception { + Dashboard dashboard = buildDashboardForUplinkMsg(); + + UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); + DashboardUpdateMsg.Builder dashboardUpdateMsgBuilder = DashboardUpdateMsg.newBuilder(); + dashboardUpdateMsgBuilder.setIdMSB(dashboard.getUuidId().getMostSignificantBits()); + dashboardUpdateMsgBuilder.setIdLSB(dashboard.getUuidId().getLeastSignificantBits()); + dashboardUpdateMsgBuilder.setEntity(JacksonUtil.toString(dashboard)); + dashboardUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); + testAutoGeneratedCodeByProtobuf(dashboardUpdateMsgBuilder); + uplinkMsgBuilder.addDashboardUpdateMsg(dashboardUpdateMsgBuilder.build()); + + testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); + + edgeImitator.expectResponsesAmount(1); + edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); + + Assert.assertTrue(edgeImitator.waitForResponses()); + + Dashboard foundDashboard = doGet("/api/dashboard/" + dashboard.getUuidId(), Dashboard.class); + Assert.assertNotNull(foundDashboard); + Assert.assertEquals("Edge Test Dashboard", foundDashboard.getName()); + } + + @Test + public void testSendDeleteEntityViewOnEdgeToCloud() throws Exception { + Dashboard savedDashboard = saveDashboardOnCloudAndVerifyDeliveryToEdge(); + + UplinkMsg.Builder upLinkMsgBuilder = UplinkMsg.newBuilder(); + DashboardUpdateMsg.Builder dashboardDeleteMsgBuilder = DashboardUpdateMsg.newBuilder(); + dashboardDeleteMsgBuilder.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE); + dashboardDeleteMsgBuilder.setIdMSB(savedDashboard.getUuidId().getMostSignificantBits()); + dashboardDeleteMsgBuilder.setIdLSB(savedDashboard.getUuidId().getLeastSignificantBits()); + testAutoGeneratedCodeByProtobuf(dashboardDeleteMsgBuilder); + + upLinkMsgBuilder.addDashboardUpdateMsg(dashboardDeleteMsgBuilder.build()); + + testAutoGeneratedCodeByProtobuf(upLinkMsgBuilder); + + edgeImitator.expectResponsesAmount(1); + edgeImitator.sendUplinkMsg(upLinkMsgBuilder.build()); + Assert.assertTrue(edgeImitator.waitForResponses()); + DashboardInfo dashboardInfo = doGet("/api/dashboard/info/" + savedDashboard.getUuidId(), DashboardInfo.class); + Assert.assertNotNull(dashboardInfo); + List edgeAssets = doGetTypedWithPageLink("/api/edge/" + edge.getUuidId() + "/dashboards?", + new TypeReference>() { + }, new PageLink(100)).getData(); + Assert.assertFalse(edgeAssets.contains(dashboardInfo)); + } + + private Dashboard saveDashboardOnCloudAndVerifyDeliveryToEdge() throws Exception { + // create dashboard and assign to edge + Dashboard dashboard = new Dashboard(); + dashboard.setTitle(StringUtils.randomAlphanumeric(15)); + Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); + edgeImitator.expectMessageAmount(1); // dashboard message + doPost("/api/edge/" + edge.getUuidId() + + "/dashboard/" + savedDashboard.getUuidId(), Dashboard.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + Optional dashboardUpdateMsgOpt = edgeImitator.findMessageByType(DashboardUpdateMsg.class); + Assert.assertTrue(dashboardUpdateMsgOpt.isPresent()); + DashboardUpdateMsg entityViewUpdateMsg = dashboardUpdateMsgOpt.get(); + Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, entityViewUpdateMsg.getMsgType()); + Assert.assertEquals(savedDashboard.getUuidId().getMostSignificantBits(), entityViewUpdateMsg.getIdMSB()); + Assert.assertEquals(savedDashboard.getUuidId().getLeastSignificantBits(), entityViewUpdateMsg.getIdLSB()); + return savedDashboard; + } + + private Dashboard buildDashboardForUplinkMsg() { + Dashboard dashboard = new Dashboard(); + dashboard.setId(new DashboardId(UUID.randomUUID())); + dashboard.setTenantId(tenantId); + dashboard.setTitle("Edge Test Dashboard"); + return dashboard; + } + } diff --git a/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java index 15d5b5c809..7bb05cb4d5 100644 --- a/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java @@ -43,6 +43,7 @@ import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; @@ -51,7 +52,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.msg.session.FeatureType; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.gen.edge.v1.AttributesRequestMsg; import org.thingsboard.server.gen.edge.v1.DeviceCredentialsRequestMsg; @@ -83,6 +84,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @DaoSqlTest public class DeviceEdgeTest extends AbstractEdgeTest { + private static final String DEFAULT_DEVICE_TYPE = "default"; + @Test public void testDevices() throws Exception { // create device and assign to edge; update device @@ -100,26 +103,28 @@ public class DeviceEdgeTest extends AbstractEdgeTest { Assert.assertEquals(savedDevice.getUuidId().getMostSignificantBits(), deviceUpdateMsg.getIdMSB()); Assert.assertEquals(savedDevice.getUuidId().getLeastSignificantBits(), deviceUpdateMsg.getIdLSB()); - // delete device - no messages expected + // delete device - message expected, message send to all edges edgeImitator.expectMessageAmount(1); doDelete("/api/device/" + savedDevice.getUuidId()) .andExpect(status().isOk()); - Assert.assertFalse(edgeImitator.waitForMessages(1)); + Assert.assertTrue(edgeImitator.waitForMessages(5)); // create device #2 and assign to edge edgeImitator.expectMessageAmount(2); - savedDevice = saveDevice("Edge Device 3", "Default"); + savedDevice = saveDevice("Edge Device 3", DEFAULT_DEVICE_TYPE); doPost("/api/edge/" + edge.getUuidId() + "/device/" + savedDevice.getUuidId(), Device.class); Assert.assertTrue(edgeImitator.waitForMessages()); Optional deviceUpdateMsgOpt = edgeImitator.findMessageByType(DeviceUpdateMsg.class); Assert.assertTrue(deviceUpdateMsgOpt.isPresent()); deviceUpdateMsg = deviceUpdateMsgOpt.get(); + Device deviceFromMsg = JacksonUtil.fromString(deviceUpdateMsg.getEntity(), Device.class, true); + Assert.assertNotNull(deviceFromMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceUpdateMsg.getMsgType()); - Assert.assertEquals(savedDevice.getUuidId().getMostSignificantBits(), deviceUpdateMsg.getIdMSB()); - Assert.assertEquals(savedDevice.getUuidId().getLeastSignificantBits(), deviceUpdateMsg.getIdLSB()); - Assert.assertEquals(savedDevice.getName(), deviceUpdateMsg.getName()); - Assert.assertEquals(savedDevice.getType(), deviceUpdateMsg.getType()); + Assert.assertEquals(savedDevice, deviceFromMsg); + Assert.assertEquals(savedDevice.getId(), deviceFromMsg.getId()); + Assert.assertEquals(savedDevice.getName(), deviceFromMsg.getName()); + Assert.assertEquals(savedDevice.getType(), deviceFromMsg.getType()); Optional deviceProfileUpdateMsgOpt = edgeImitator.findMessageByType(DeviceProfileUpdateMsg.class); Assert.assertTrue(deviceProfileUpdateMsgOpt.isPresent()); @@ -144,9 +149,10 @@ public class DeviceEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg); deviceUpdateMsg = (DeviceUpdateMsg) latestMessage; + deviceFromMsg = JacksonUtil.fromString(deviceUpdateMsg.getEntity(), Device.class, true); + Assert.assertNotNull(deviceFromMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, deviceUpdateMsg.getMsgType()); - Assert.assertEquals(savedCustomer.getUuidId().getMostSignificantBits(), deviceUpdateMsg.getCustomerIdMSB()); - Assert.assertEquals(savedCustomer.getUuidId().getLeastSignificantBits(), deviceUpdateMsg.getCustomerIdLSB()); + Assert.assertEquals(savedCustomer.getId(), deviceFromMsg.getCustomerId()); // unassign device #2 from customer edgeImitator.expectMessageAmount(1); @@ -155,10 +161,12 @@ public class DeviceEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg); deviceUpdateMsg = (DeviceUpdateMsg) latestMessage; + deviceFromMsg = JacksonUtil.fromString(deviceUpdateMsg.getEntity(), Device.class, true); + Assert.assertNotNull(deviceFromMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, deviceUpdateMsg.getMsgType()); Assert.assertEquals( new CustomerId(EntityId.NULL_UUID), - new CustomerId(new UUID(deviceUpdateMsg.getCustomerIdMSB(), deviceUpdateMsg.getCustomerIdLSB()))); + new CustomerId(new UUID(deviceFromMsg.getCustomerId().getId().getMostSignificantBits(), deviceFromMsg.getCustomerId().getId().getLeastSignificantBits()))); // delete device #2 - messages expected edgeImitator.expectMessageAmount(1); @@ -194,9 +202,8 @@ public class DeviceEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceCredentialsUpdateMsg); DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = (DeviceCredentialsUpdateMsg) latestMessage; - Assert.assertEquals(deviceCredentials.getCredentialsType().name(), deviceCredentialsUpdateMsg.getCredentialsType()); - Assert.assertEquals(deviceCredentials.getCredentialsId(), deviceCredentialsUpdateMsg.getCredentialsId()); - Assert.assertFalse(deviceCredentialsUpdateMsg.hasCredentialsValue()); + DeviceCredentials deviceCredentialsMsg = JacksonUtil.fromString(deviceCredentialsUpdateMsg.getEntity(), DeviceCredentials.class, true); + Assert.assertEquals(deviceCredentials, deviceCredentialsMsg); // update device credentials - X509_CERTIFICATE edgeImitator.expectMessageAmount(1); @@ -209,10 +216,12 @@ public class DeviceEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceCredentialsUpdateMsg); deviceCredentialsUpdateMsg = (DeviceCredentialsUpdateMsg) latestMessage; - Assert.assertEquals(deviceCredentials.getCredentialsType().name(), deviceCredentialsUpdateMsg.getCredentialsType()); - Assert.assertFalse(deviceCredentialsUpdateMsg.getCredentialsId().isEmpty()); - Assert.assertTrue(deviceCredentialsUpdateMsg.hasCredentialsValue()); - Assert.assertEquals(deviceCredentials.getCredentialsValue(), deviceCredentialsUpdateMsg.getCredentialsValue()); + deviceCredentialsMsg = JacksonUtil.fromString(deviceCredentialsUpdateMsg.getEntity(), DeviceCredentials.class, true); + Assert.assertNotNull(deviceCredentialsMsg); + Assert.assertEquals(deviceCredentials.getCredentialsType(), deviceCredentialsMsg.getCredentialsType()); + Assert.assertFalse(deviceCredentialsMsg.getCredentialsId().isEmpty()); + Assert.assertNotNull(deviceCredentialsMsg.getCredentialsValue()); + Assert.assertEquals(deviceCredentials.getCredentialsValue(), deviceCredentialsMsg.getCredentialsValue()); } private void verifyUpdateFirmwareIdSoftwareIdAndDeviceData(Device savedDevice) throws InterruptedException { @@ -242,19 +251,13 @@ public class DeviceEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg); DeviceUpdateMsg deviceUpdateMsg = (DeviceUpdateMsg) latestMessage; + Device deviceMsg = JacksonUtil.fromString(deviceUpdateMsg.getEntity(), Device.class, true); + Assert.assertNotNull(deviceMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, deviceUpdateMsg.getMsgType()); - Assert.assertEquals(savedDevice.getUuidId().getMostSignificantBits(), deviceUpdateMsg.getIdMSB()); - Assert.assertEquals(savedDevice.getUuidId().getLeastSignificantBits(), deviceUpdateMsg.getIdLSB()); - Assert.assertEquals(savedDevice.getName(), deviceUpdateMsg.getName()); - Assert.assertEquals(savedDevice.getType(), deviceUpdateMsg.getType()); - Assert.assertEquals(firmwareOtaPackageInfo.getUuidId().getMostSignificantBits(), deviceUpdateMsg.getFirmwareIdMSB()); - Assert.assertEquals(firmwareOtaPackageInfo.getUuidId().getLeastSignificantBits(), deviceUpdateMsg.getFirmwareIdLSB()); - Assert.assertEquals(softwareOtaPackageInfo.getUuidId().getMostSignificantBits(), deviceUpdateMsg.getSoftwareIdMSB()); - Assert.assertEquals(softwareOtaPackageInfo.getUuidId().getLeastSignificantBits(), deviceUpdateMsg.getSoftwareIdLSB()); - Optional deviceDataOpt = - dataDecodingEncodingService.decode(deviceUpdateMsg.getDeviceDataBytes().toByteArray()); - Assert.assertTrue(deviceDataOpt.isPresent()); - deviceData = deviceDataOpt.get(); + Assert.assertEquals(savedDevice, deviceMsg); + Assert.assertEquals(firmwareOtaPackageInfo.getId(), deviceMsg.getFirmwareId()); + Assert.assertEquals(softwareOtaPackageInfo.getId(), deviceMsg.getSoftwareId()); + deviceData = deviceMsg.getDeviceData(); Assert.assertTrue(deviceData.getTransportConfiguration() instanceof MqttDeviceTransportConfiguration); MqttDeviceTransportConfiguration mqttDeviceTransportConfiguration = (MqttDeviceTransportConfiguration) deviceData.getTransportConfiguration(); @@ -265,7 +268,7 @@ public class DeviceEdgeTest extends AbstractEdgeTest { public void testDeviceReachedMaximumAllowedOnCloud() throws Exception { // update tenant profile configuration loginSysAdmin(); - TenantProfile tenantProfile = doGet("/api/tenantProfile/" + savedTenant.getTenantProfileId().getId(), TenantProfile.class); + TenantProfile tenantProfile = doGet("/api/tenantProfile/" + tenantProfileId.getId(), TenantProfile.class); DefaultTenantProfileConfiguration profileConfiguration = (DefaultTenantProfileConfiguration) tenantProfile.getProfileData().getConfiguration(); profileConfiguration.setMaxDevices(1); @@ -274,14 +277,13 @@ public class DeviceEdgeTest extends AbstractEdgeTest { loginTenantAdmin(); - UUID uuid = Uuids.timeBased(); + Device device = buildDeviceForUplinkMsg("Edge Device", DEFAULT_DEVICE_TYPE); UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); DeviceUpdateMsg.Builder deviceUpdateMsgBuilder = DeviceUpdateMsg.newBuilder(); - deviceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); - deviceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); - deviceUpdateMsgBuilder.setName("Edge Device"); - deviceUpdateMsgBuilder.setType("default"); + deviceUpdateMsgBuilder.setIdMSB(device.getUuidId().getMostSignificantBits()); + deviceUpdateMsgBuilder.setIdLSB(device.getUuidId().getLeastSignificantBits()); + deviceUpdateMsgBuilder.setEntity(JacksonUtil.toString(device)); deviceUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); uplinkMsgBuilder.addDeviceUpdateMsg(deviceUpdateMsgBuilder.build()); @@ -325,12 +327,11 @@ public class DeviceEdgeTest extends AbstractEdgeTest { public void testSendDeviceCredentialsUpdateToCloud() throws Exception { Device device = findDeviceByName("Edge Device 1"); + DeviceCredentials deviceCredentials = buildDeviceCredentialsForUplinkMsg(device.getId()); + UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); DeviceCredentialsUpdateMsg.Builder deviceCredentialsUpdateMsgBuilder = DeviceCredentialsUpdateMsg.newBuilder(); - deviceCredentialsUpdateMsgBuilder.setDeviceIdMSB(device.getUuidId().getMostSignificantBits()); - deviceCredentialsUpdateMsgBuilder.setDeviceIdLSB(device.getUuidId().getLeastSignificantBits()); - deviceCredentialsUpdateMsgBuilder.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN.name()); - deviceCredentialsUpdateMsgBuilder.setCredentialsId("NEW_TOKEN"); + deviceCredentialsUpdateMsgBuilder.setEntity(JacksonUtil.toString(deviceCredentials)); testAutoGeneratedCodeByProtobuf(deviceCredentialsUpdateMsgBuilder); uplinkMsgBuilder.addDeviceCredentialsUpdateMsg(deviceCredentialsUpdateMsgBuilder.build()); @@ -365,10 +366,10 @@ public class DeviceEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceCredentialsUpdateMsg); DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = (DeviceCredentialsUpdateMsg) latestMessage; - Assert.assertEquals(deviceCredentialsUpdateMsg.getDeviceIdMSB(), device.getUuidId().getMostSignificantBits()); - Assert.assertEquals(deviceCredentialsUpdateMsg.getDeviceIdLSB(), device.getUuidId().getLeastSignificantBits()); - Assert.assertEquals(deviceCredentialsUpdateMsg.getCredentialsType(), deviceCredentials.getCredentialsType().name()); - Assert.assertEquals(deviceCredentialsUpdateMsg.getCredentialsId(), deviceCredentials.getCredentialsId()); + DeviceCredentials deviceCredentialsMsg = JacksonUtil.fromString(deviceCredentialsUpdateMsg.getEntity(), DeviceCredentials.class, true); + Assert.assertNotNull(deviceCredentialsMsg); + Assert.assertEquals(device.getId(), deviceCredentialsMsg.getDeviceId()); + Assert.assertEquals(deviceCredentials, deviceCredentialsMsg); } @Test @@ -380,8 +381,10 @@ public class DeviceEdgeTest extends AbstractEdgeTest { "inactivityTimeout", "3600000"); sendAttributesRequestAndVerify(device, DataConstants.SHARED_SCOPE, "{\"key2\":\"value2\"}", "key2", "value2"); + sendAttributesRequestAndVerify(device, DataConstants.SERVER_SCOPE, "{\"jsonKey\":{\"nestedJsonKey\":\"nestedJsonValue\"}}", + "jsonKey", "{\"nestedJsonKey\":\"nestedJsonValue\"}"); - doDelete("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/" + DataConstants.SERVER_SCOPE, "keys", "key1, inactivityTimeout"); + doDelete("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/" + DataConstants.SERVER_SCOPE, "keys", "key1, inactivityTimeout, jsonKey"); doDelete("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/" + DataConstants.SHARED_SCOPE, "keys", "key2"); } @@ -479,16 +482,15 @@ public class DeviceEdgeTest extends AbstractEdgeTest { @Test public void testSendDeviceToCloudWithNameThatAlreadyExistsOnCloud() throws Exception { String deviceOnCloudName = StringUtils.randomAlphanumeric(15); - Device deviceOnCloud = saveDevice(deviceOnCloudName, "Default"); + Device deviceOnCloud = saveDevice(deviceOnCloudName, DEFAULT_DEVICE_TYPE); - UUID uuid = Uuids.timeBased(); + Device deviceMsg = buildDeviceForUplinkMsg(deviceOnCloudName, "test"); UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); DeviceUpdateMsg.Builder deviceUpdateMsgBuilder = DeviceUpdateMsg.newBuilder(); - deviceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); - deviceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); - deviceUpdateMsgBuilder.setName(deviceOnCloudName); - deviceUpdateMsgBuilder.setType("test"); + deviceUpdateMsgBuilder.setIdMSB(deviceMsg.getUuidId().getMostSignificantBits()); + deviceUpdateMsgBuilder.setIdLSB(deviceMsg.getUuidId().getLeastSignificantBits()); + deviceUpdateMsgBuilder.setEntity(JacksonUtil.toString(deviceMsg)); deviceUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); testAutoGeneratedCodeByProtobuf(deviceUpdateMsgBuilder); uplinkMsgBuilder.addDeviceUpdateMsg(deviceUpdateMsgBuilder.build()); @@ -505,7 +507,9 @@ public class DeviceEdgeTest extends AbstractEdgeTest { Optional deviceUpdateMsgOpt = edgeImitator.findMessageByType(DeviceUpdateMsg.class); Assert.assertTrue(deviceUpdateMsgOpt.isPresent()); DeviceUpdateMsg latestDeviceUpdateMsg = deviceUpdateMsgOpt.get(); - Assert.assertNotEquals(deviceOnCloudName, latestDeviceUpdateMsg.getName()); + Device deviceLatestMsg = JacksonUtil.fromString(latestDeviceUpdateMsg.getEntity(), Device.class, true); + Assert.assertNotNull(deviceLatestMsg); + Assert.assertNotEquals(deviceOnCloudName, deviceLatestMsg.getName()); UUID newDeviceId = new UUID(latestDeviceUpdateMsg.getIdMSB(), latestDeviceUpdateMsg.getIdLSB()); @@ -518,8 +522,8 @@ public class DeviceEdgeTest extends AbstractEdgeTest { Optional deviceCredentialsUpdateMsgOpt = edgeImitator.findMessageByType(DeviceCredentialsRequestMsg.class); Assert.assertTrue(deviceCredentialsUpdateMsgOpt.isPresent()); DeviceCredentialsRequestMsg latestDeviceCredentialsRequestMsg = deviceCredentialsUpdateMsgOpt.get(); - Assert.assertEquals(uuid.getMostSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdMSB()); - Assert.assertEquals(uuid.getLeastSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB()); + Assert.assertEquals(deviceMsg.getUuidId().getMostSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdMSB()); + Assert.assertEquals(device.getUuidId().getLeastSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB()); newDeviceId = new UUID(latestDeviceCredentialsRequestMsg.getDeviceIdMSB(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB()); @@ -530,14 +534,13 @@ public class DeviceEdgeTest extends AbstractEdgeTest { @Test public void testSendDeviceToCloud() throws Exception { - UUID uuid = Uuids.timeBased(); + Device deviceMsg = buildDeviceForUplinkMsg("Edge Device 2", "test"); UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); DeviceUpdateMsg.Builder deviceUpdateMsgBuilder = DeviceUpdateMsg.newBuilder(); - deviceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); - deviceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); - deviceUpdateMsgBuilder.setName("Edge Device 2"); - deviceUpdateMsgBuilder.setType("test"); + deviceUpdateMsgBuilder.setIdMSB(deviceMsg.getUuidId().getMostSignificantBits()); + deviceUpdateMsgBuilder.setIdLSB(deviceMsg.getUuidId().getLeastSignificantBits()); + deviceUpdateMsgBuilder.setEntity(JacksonUtil.toString(deviceMsg)); deviceUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); uplinkMsgBuilder.addDeviceUpdateMsg(deviceUpdateMsgBuilder.build()); @@ -552,8 +555,8 @@ public class DeviceEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceCredentialsRequestMsg); DeviceCredentialsRequestMsg latestDeviceCredentialsRequestMsg = (DeviceCredentialsRequestMsg) latestMessage; - Assert.assertEquals(uuid.getMostSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdMSB()); - Assert.assertEquals(uuid.getLeastSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB()); + Assert.assertEquals(deviceMsg.getUuidId().getMostSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdMSB()); + Assert.assertEquals(deviceMsg.getUuidId().getLeastSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB()); UUID newDeviceId = new UUID(latestDeviceCredentialsRequestMsg.getDeviceIdMSB(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB()); @@ -646,6 +649,9 @@ public class DeviceEdgeTest extends AbstractEdgeTest { case LONG_V: Assert.assertEquals(Long.parseLong(expectedValue), keyValueProto.getLongV()); break; + case JSON_V: + Assert.assertEquals(JacksonUtil.toJsonNode(expectedValue), JacksonUtil.toJsonNode(keyValueProto.getJsonV())); + break; default: Assert.fail("Unexpected data type: " + keyValueProto.getType()); } @@ -762,4 +768,22 @@ public class DeviceEdgeTest extends AbstractEdgeTest { } } } + + private Device buildDeviceForUplinkMsg(String name, String type) { + Device device = new Device(); + device.setId(new DeviceId(UUID.randomUUID())); + device.setTenantId(tenantId); + device.setType(type); + device.setName(name); + return device; + } + + + private DeviceCredentials buildDeviceCredentialsForUplinkMsg(DeviceId deviceId) { + DeviceCredentials deviceCredentials = new DeviceCredentials(); + deviceCredentials.setDeviceId(deviceId); + deviceCredentials.setCredentialsValue("NEW_TOKEN"); + deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); + return deviceCredentials; + } } diff --git a/application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java index 9d4cc0ca15..97c4b0b333 100644 --- a/application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java @@ -20,14 +20,19 @@ import org.junit.Assert; import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.device.data.PowerMode; import org.thingsboard.server.common.data.device.data.PowerSavingConfiguration; import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.DefaultCoapDeviceTypeConfiguration; +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration; @@ -37,6 +42,7 @@ import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.Abstrac import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MBootstrapServerCredential; import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.NoSecLwM2MBootstrapServerCredential; import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.kv.DataType; import org.thingsboard.server.common.data.ota.OtaPackageType; @@ -46,12 +52,15 @@ import org.thingsboard.server.common.data.transport.snmp.config.impl.TelemetryQu import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.gen.edge.v1.UplinkMsg; +import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg; import org.thingsboard.server.transport.AbstractTransportIntegrationTest; import org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.UUID; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -72,11 +81,10 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; + DeviceProfile deviceProfileMsg = JacksonUtil.fromString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class, true); + Assert.assertNotNull(deviceProfileMsg); + Assert.assertEquals(deviceProfile, deviceProfileMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); - Assert.assertEquals(deviceProfile.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getIdMSB()); - Assert.assertEquals(deviceProfile.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getIdLSB()); - Assert.assertEquals(thermostatsRuleChainId.getId().getMostSignificantBits(), deviceProfileUpdateMsg.getDefaultRuleChainIdMSB()); - Assert.assertEquals(thermostatsRuleChainId.getId().getLeastSignificantBits(), deviceProfileUpdateMsg.getDefaultRuleChainIdLSB()); // update device profile edgeImitator.expectMessageAmount(1); @@ -98,12 +106,9 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; - Assert.assertEquals(firmwareOtaPackageInfo.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getFirmwareIdMSB()); - Assert.assertEquals(firmwareOtaPackageInfo.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getFirmwareIdLSB()); - Assert.assertEquals(softwareOtaPackageInfo.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getSoftwareIdMSB()); - Assert.assertEquals(softwareOtaPackageInfo.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getSoftwareIdLSB()); - Assert.assertEquals(thermostatsDashboardId.getId().getMostSignificantBits(), deviceProfileUpdateMsg.getDefaultDashboardIdMSB()); - Assert.assertEquals(thermostatsDashboardId.getId().getLeastSignificantBits(), deviceProfileUpdateMsg.getDefaultDashboardIdLSB()); + deviceProfileMsg = JacksonUtil.fromString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class, true); + Assert.assertNotNull(deviceProfileMsg); + Assert.assertEquals(deviceProfile, deviceProfileMsg); // delete profile edgeImitator.expectMessageAmount(1); @@ -128,16 +133,13 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; + DeviceProfile deviceProfileMsg = JacksonUtil.fromString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class, true); + Assert.assertNotNull(deviceProfileMsg); + Assert.assertEquals(deviceProfile, deviceProfileMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); - Assert.assertEquals(deviceProfile.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getIdMSB()); - Assert.assertEquals(deviceProfile.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getIdLSB()); - Assert.assertEquals(DeviceTransportType.SNMP.name(), deviceProfileUpdateMsg.getTransportType()); + Assert.assertEquals(DeviceTransportType.SNMP, deviceProfileMsg.getTransportType()); - Optional deviceProfileDataOpt = - dataDecodingEncodingService.decode(deviceProfileUpdateMsg.getProfileDataBytes().toByteArray()); - - Assert.assertTrue(deviceProfileDataOpt.isPresent()); - DeviceProfileData deviceProfileData = deviceProfileDataOpt.get(); + DeviceProfileData deviceProfileData = deviceProfileMsg.getProfileData(); Assert.assertTrue(deviceProfileData.getTransportConfiguration() instanceof SnmpDeviceProfileTransportConfiguration); SnmpDeviceProfileTransportConfiguration transportConfiguration = @@ -169,16 +171,13 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; + DeviceProfile deviceProfileMsg = JacksonUtil.fromString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class, true); + Assert.assertNotNull(deviceProfileMsg); + Assert.assertEquals(deviceProfile, deviceProfileMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); - Assert.assertEquals(deviceProfile.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getIdMSB()); - Assert.assertEquals(deviceProfile.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getIdLSB()); - Assert.assertEquals(DeviceTransportType.LWM2M.name(), deviceProfileUpdateMsg.getTransportType()); + Assert.assertEquals(DeviceTransportType.LWM2M, deviceProfileMsg.getTransportType()); - Optional deviceProfileDataOpt = - dataDecodingEncodingService.decode(deviceProfileUpdateMsg.getProfileDataBytes().toByteArray()); - - Assert.assertTrue(deviceProfileDataOpt.isPresent()); - DeviceProfileData deviceProfileData = deviceProfileDataOpt.get(); + DeviceProfileData deviceProfileData = deviceProfileMsg.getProfileData(); Assert.assertTrue(deviceProfileData.getTransportConfiguration() instanceof Lwm2mDeviceProfileTransportConfiguration); Lwm2mDeviceProfileTransportConfiguration transportConfiguration = @@ -221,16 +220,13 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; + DeviceProfile deviceProfileMsg = JacksonUtil.fromString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class, true); + Assert.assertNotNull(deviceProfileMsg); + Assert.assertEquals(deviceProfile, deviceProfileMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); - Assert.assertEquals(deviceProfile.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getIdMSB()); - Assert.assertEquals(deviceProfile.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getIdLSB()); - Assert.assertEquals(DeviceTransportType.COAP.name(), deviceProfileUpdateMsg.getTransportType()); - - Optional deviceProfileDataOpt = - dataDecodingEncodingService.decode(deviceProfileUpdateMsg.getProfileDataBytes().toByteArray()); + Assert.assertEquals(DeviceTransportType.COAP, deviceProfileMsg.getTransportType()); - Assert.assertTrue(deviceProfileDataOpt.isPresent()); - DeviceProfileData deviceProfileData = deviceProfileDataOpt.get(); + DeviceProfileData deviceProfileData = deviceProfileMsg.getProfileData(); Assert.assertTrue(deviceProfileData.getTransportConfiguration() instanceof CoapDeviceProfileTransportConfiguration); CoapDeviceProfileTransportConfiguration transportConfiguration = @@ -260,6 +256,62 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { removeDeviceProfileAndDoBasicAssert(deviceProfile); } + @Test + public void testSendDeviceProfileToCloud() throws Exception { + RuleChainId ruleChainId = createEdgeRuleChainAndAssignToEdge("Device Profile Rule Chain"); + DashboardId dashboardId = createDashboardAndAssignToEdge("Device Profile Dashboard"); + + DeviceProfile deviceProfileMsg = buildDeviceProfileForUplinkMsg("Device Profile On Edge"); + deviceProfileMsg.setDefaultRuleChainId(ruleChainId); + deviceProfileMsg.setDefaultDashboardId(dashboardId); + + UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); + DeviceProfileUpdateMsg.Builder deviceProfileUpdateMsgBuilder = DeviceProfileUpdateMsg.newBuilder(); + deviceProfileUpdateMsgBuilder.setIdMSB(deviceProfileMsg.getUuidId().getMostSignificantBits()); + deviceProfileUpdateMsgBuilder.setIdLSB(deviceProfileMsg.getUuidId().getLeastSignificantBits()); + deviceProfileUpdateMsgBuilder.setEntity(JacksonUtil.toString(deviceProfileMsg)); + deviceProfileUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); + testAutoGeneratedCodeByProtobuf(deviceProfileUpdateMsgBuilder); + uplinkMsgBuilder.addDeviceProfileUpdateMsg(deviceProfileUpdateMsgBuilder.build()); + + testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); + + edgeImitator.expectResponsesAmount(1); + edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); + + Assert.assertTrue(edgeImitator.waitForResponses()); + + UplinkResponseMsg latestResponseMsg = edgeImitator.getLatestResponseMsg(); + Assert.assertTrue(latestResponseMsg.getSuccess()); + + DeviceProfile deviceProfile = doGet("/api/deviceProfile/" + deviceProfileMsg.getUuidId(), DeviceProfile.class); + Assert.assertNotNull(deviceProfile); + Assert.assertEquals("Device Profile On Edge", deviceProfile.getName()); + + // delete profile + edgeImitator.expectMessageAmount(1); + doDelete("/api/deviceProfile/" + deviceProfile.getUuidId()) + .andExpect(status().isOk()); + Assert.assertTrue(edgeImitator.waitForMessages()); + AbstractMessage latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); + DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); + Assert.assertEquals(deviceProfile.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getIdMSB()); + Assert.assertEquals(deviceProfile.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getIdLSB()); + + // cleanup + unAssignFromEdgeAndDeleteDashboard(dashboardId); + unAssignFromEdgeAndDeleteRuleChain(ruleChainId); + } + + private DeviceProfileData createProfileData() { + DeviceProfileData deviceProfileData = new DeviceProfileData(); + deviceProfileData.setConfiguration(new DefaultDeviceProfileConfiguration()); + deviceProfileData.setTransportConfiguration(new DefaultDeviceProfileTransportConfiguration()); + deviceProfileData.setProvisionConfiguration(new DisabledDeviceProfileProvisionConfiguration("Device Secret")); + return deviceProfileData; + } private DeviceProfile createDeviceProfileAndDoBasicAssert(String deviceProfileName, DeviceProfileTransportConfiguration deviceProfileTransportConfiguration) throws Exception { DeviceProfile deviceProfile = this.createDeviceProfile(deviceProfileName, deviceProfileTransportConfiguration); @@ -341,4 +393,60 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest { transportConfiguration.setCoapDeviceTypeConfiguration(coapDeviceTypeConfiguration); return transportConfiguration; } + + @Test + public void testSendDeviceProfileToCloudWithNameThatAlreadyExistsOnCloud() throws Exception { + String deviceProfileOnCloudName = StringUtils.randomAlphanumeric(15); + + edgeImitator.expectMessageAmount(1); + DeviceProfile deviceProfileOnCloud = this.createDeviceProfile(deviceProfileOnCloudName); + deviceProfileOnCloud = doPost("/api/deviceProfile", deviceProfileOnCloud, DeviceProfile.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + + DeviceProfile deviceProfileMsg = buildDeviceProfileForUplinkMsg(deviceProfileOnCloudName); + deviceProfileMsg.setProfileData(deviceProfileOnCloud.getProfileData()); + + UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); + DeviceProfileUpdateMsg.Builder deviceProfileUpdateMsgBuilder = DeviceProfileUpdateMsg.newBuilder(); + deviceProfileUpdateMsgBuilder.setIdMSB(deviceProfileMsg.getUuidId().getMostSignificantBits()); + deviceProfileUpdateMsgBuilder.setIdLSB(deviceProfileMsg.getUuidId().getLeastSignificantBits()); + deviceProfileUpdateMsgBuilder.setEntity(JacksonUtil.toString(deviceProfileMsg)); + deviceProfileUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); + uplinkMsgBuilder.addDeviceProfileUpdateMsg(deviceProfileUpdateMsgBuilder.build()); + + testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); + + edgeImitator.expectResponsesAmount(1); + edgeImitator.expectMessageAmount(1); + + edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); + + Assert.assertTrue(edgeImitator.waitForResponses()); + Assert.assertTrue(edgeImitator.waitForMessages()); + + Optional deviceProfileUpdateMsgOpt = edgeImitator.findMessageByType(DeviceProfileUpdateMsg.class); + Assert.assertTrue(deviceProfileUpdateMsgOpt.isPresent()); + DeviceProfileUpdateMsg latestDeviceProfileUpdateMsg = deviceProfileUpdateMsgOpt.get(); + deviceProfileMsg = JacksonUtil.fromString(latestDeviceProfileUpdateMsg.getEntity(), DeviceProfile.class, true); + Assert.assertNotNull(deviceProfileMsg); + Assert.assertNotEquals(deviceProfileOnCloudName, deviceProfileMsg.getName()); + + Assert.assertNotEquals(deviceProfileOnCloud.getId(), deviceProfileMsg.getId()); + + DeviceProfile deviceProfile = doGet("/api/deviceProfile/" + deviceProfileMsg.getUuidId(), DeviceProfile.class); + Assert.assertNotNull(deviceProfile); + Assert.assertNotEquals(deviceProfileOnCloudName, deviceProfile.getName()); + } + + private DeviceProfile buildDeviceProfileForUplinkMsg(String name) { + DeviceProfile deviceProfile = new DeviceProfile(); + deviceProfile.setId(new DeviceProfileId(UUID.randomUUID())); + deviceProfile.setTenantId(tenantId); + deviceProfile.setName(name); + deviceProfile.setDefault(false); + deviceProfile.setType(DeviceProfileType.DEFAULT); + deviceProfile.setTransportType(DeviceTransportType.DEFAULT); + deviceProfile.setProfileData(createProfileData()); + return deviceProfile; + } } diff --git a/application/src/test/java/org/thingsboard/server/edge/EdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/EdgeTest.java index 1f205af53a..212d14bf8a 100644 --- a/application/src/test/java/org/thingsboard/server/edge/EdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/EdgeTest.java @@ -17,6 +17,7 @@ package org.thingsboard.server.edge; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.CustomerId; @@ -39,7 +40,7 @@ public class EdgeTest extends AbstractEdgeTest { Customer customer = new Customer(); customer.setTitle("Edge Customer"); Customer savedCustomer = doPost("/api/customer", customer, Customer.class); - Assert.assertFalse(edgeImitator.waitForMessages(1)); + Assert.assertFalse(edgeImitator.waitForMessages(5)); // assign edge to customer edgeImitator.expectMessageAmount(2); @@ -54,10 +55,9 @@ public class EdgeTest extends AbstractEdgeTest { Optional customerUpdateOpt = edgeImitator.findMessageByType(CustomerUpdateMsg.class); Assert.assertTrue(customerUpdateOpt.isPresent()); CustomerUpdateMsg customerUpdateMsg = customerUpdateOpt.get(); + Customer customerMsg = JacksonUtil.fromString(customerUpdateMsg.getEntity(), Customer.class, true); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, customerUpdateMsg.getMsgType()); - Assert.assertEquals(savedCustomer.getUuidId().getMostSignificantBits(), customerUpdateMsg.getIdMSB()); - Assert.assertEquals(savedCustomer.getUuidId().getLeastSignificantBits(), customerUpdateMsg.getIdLSB()); - Assert.assertEquals(savedCustomer.getTitle(), customerUpdateMsg.getTitle()); + Assert.assertEquals(savedCustomer, customerMsg); // unassign edge from customer edgeImitator.expectMessageAmount(2); diff --git a/application/src/test/java/org/thingsboard/server/edge/EntityViewEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/EntityViewEdgeTest.java index 234a9473fa..9afb091905 100644 --- a/application/src/test/java/org/thingsboard/server/edge/EntityViewEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/EntityViewEdgeTest.java @@ -15,22 +15,33 @@ */ package org.thingsboard.server.edge; +import com.fasterxml.jackson.core.type.TypeReference; import com.google.protobuf.AbstractMessage; import com.google.protobuf.InvalidProtocolBufferException; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.EntityViewInfo; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; import org.thingsboard.server.gen.edge.v1.EntityViewsRequestMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UplinkMsg; +import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg; +import java.util.List; +import java.util.Optional; import java.util.UUID; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -43,11 +54,7 @@ public class EntityViewEdgeTest extends AbstractEdgeTest { // create entity view and assign to edge edgeImitator.expectMessageAmount(1); Device device = findDeviceByName("Edge Device 1"); - EntityView entityView = new EntityView(); - entityView.setName("Edge EntityView 1"); - entityView.setType("test"); - entityView.setEntityId(device.getId()); - EntityView savedEntityView = doPost("/api/entityView", entityView, EntityView.class); + EntityView savedEntityView = saveEntityView("Edge EntityView 1", device.getId()); doPost("/api/edge/" + edge.getUuidId() + "/entityView/" + savedEntityView.getUuidId(), EntityView.class); Assert.assertTrue(edgeImitator.waitForMessages()); @@ -61,8 +68,10 @@ public class EntityViewEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof EntityViewUpdateMsg); EntityViewUpdateMsg entityViewUpdateMsg = (EntityViewUpdateMsg) latestMessage; + EntityView entityView = JacksonUtil.fromString(entityViewUpdateMsg.getEntity(), EntityView.class, true); + Assert.assertNotNull(entityView); + Assert.assertEquals(savedEntityView, entityView); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, entityViewUpdateMsg.getMsgType()); - Assert.assertEquals(savedEntityView.getName(), entityViewUpdateMsg.getName()); // request entity view(s) for device UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); @@ -94,19 +103,15 @@ public class EntityViewEdgeTest extends AbstractEdgeTest { Assert.assertEquals(entityViewUpdateMsg.getIdMSB(), savedEntityView.getUuidId().getMostSignificantBits()); Assert.assertEquals(entityViewUpdateMsg.getIdLSB(), savedEntityView.getUuidId().getLeastSignificantBits()); - // delete entity view - no messages expected + // delete entity view - message expected, it was sent to all edges edgeImitator.expectMessageAmount(1); doDelete("/api/entityView/" + savedEntityView.getUuidId()) .andExpect(status().isOk()); - Assert.assertFalse(edgeImitator.waitForMessages(1)); + Assert.assertTrue(edgeImitator.waitForMessages(5)); // create entity view #2 and assign to edge edgeImitator.expectMessageAmount(1); - entityView = new EntityView(); - entityView.setName("Edge EntityView 2"); - entityView.setType("test"); - entityView.setEntityId(device.getId()); - savedEntityView = doPost("/api/entityView", entityView, EntityView.class); + savedEntityView = saveEntityView("Edge EntityView 2", device.getId()); doPost("/api/edge/" + edge.getUuidId() + "/entityView/" + savedEntityView.getUuidId(), EntityView.class); Assert.assertTrue(edgeImitator.waitForMessages()); @@ -128,9 +133,10 @@ public class EntityViewEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof EntityViewUpdateMsg); entityViewUpdateMsg = (EntityViewUpdateMsg) latestMessage; + EntityView entityViewMsg = JacksonUtil.fromString(entityViewUpdateMsg.getEntity(), EntityView.class, true); + Assert.assertNotNull(entityViewMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, entityViewUpdateMsg.getMsgType()); - Assert.assertEquals(savedCustomer.getUuidId().getMostSignificantBits(), entityViewUpdateMsg.getCustomerIdMSB()); - Assert.assertEquals(savedCustomer.getUuidId().getLeastSignificantBits(), entityViewUpdateMsg.getCustomerIdLSB()); + Assert.assertEquals(savedCustomer.getId(), entityViewMsg.getCustomerId()); // unassign entity view #2 from customer edgeImitator.expectMessageAmount(1); @@ -139,10 +145,10 @@ public class EntityViewEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof EntityViewUpdateMsg); entityViewUpdateMsg = (EntityViewUpdateMsg) latestMessage; + entityViewMsg = JacksonUtil.fromString(entityViewUpdateMsg.getEntity(), EntityView.class, true); + Assert.assertNotNull(entityViewMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, entityViewUpdateMsg.getMsgType()); - Assert.assertEquals( - new CustomerId(EntityId.NULL_UUID), - new CustomerId(new UUID(entityViewUpdateMsg.getCustomerIdMSB(), entityViewUpdateMsg.getCustomerIdLSB()))); + Assert.assertEquals(new CustomerId(EntityId.NULL_UUID), entityViewMsg.getCustomerId()); // delete entity view #2 - messages expected edgeImitator.expectMessageAmount(1); @@ -158,21 +164,151 @@ public class EntityViewEdgeTest extends AbstractEdgeTest { } + @Test + public void testSendEntityViewToCloud() throws Exception { + Device device = findDeviceByName("Edge Device 1"); + + EntityView entityViewMsg = buildEntityViewForUplinkMsg(device, "Edge EntityView 2"); + + UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); + EntityViewUpdateMsg.Builder entityViewUpdateMsgBuilder = EntityViewUpdateMsg.newBuilder(); + entityViewUpdateMsgBuilder.setIdMSB(entityViewMsg.getUuidId().getMostSignificantBits()); + entityViewUpdateMsgBuilder.setIdLSB(entityViewMsg.getUuidId().getLeastSignificantBits()); + entityViewUpdateMsgBuilder.setEntity(JacksonUtil.toString(entityViewMsg)); + entityViewUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); + testAutoGeneratedCodeByProtobuf(entityViewUpdateMsgBuilder); + uplinkMsgBuilder.addEntityViewUpdateMsg(entityViewUpdateMsgBuilder.build()); + + testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); + + edgeImitator.expectResponsesAmount(1); + edgeImitator.expectMessageAmount(1); + + edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); + + Assert.assertTrue(edgeImitator.waitForResponses()); + + UplinkResponseMsg latestResponseMsg = edgeImitator.getLatestResponseMsg(); + Assert.assertTrue(latestResponseMsg.getSuccess()); + + EntityView entityView = doGet("/api/entityView/" + entityViewMsg.getUuidId(), EntityView.class); + Assert.assertNotNull(entityView); + Assert.assertEquals("Edge EntityView 2", entityView.getName()); + } + + @Test + public void testSendEntityViewToCloudWithNameThatAlreadyExistsOnCloud() throws Exception { + Device device = findDeviceByName("Edge Device 1"); + + String entityViewOnCloudName = StringUtils.randomAlphanumeric(15); + EntityView entityViewOnCloud = saveEntityView(entityViewOnCloudName, device.getId()); + + EntityView entityViewMsg = buildEntityViewForUplinkMsg(device, entityViewOnCloudName); + + UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); + EntityViewUpdateMsg.Builder entityViewUpdateMsgBuilder = EntityViewUpdateMsg.newBuilder(); + entityViewUpdateMsgBuilder.setIdMSB(entityViewMsg.getUuidId().getMostSignificantBits()); + entityViewUpdateMsgBuilder.setIdLSB(entityViewMsg.getUuidId().getLeastSignificantBits()); + entityViewUpdateMsgBuilder.setEntity(JacksonUtil.toString(entityViewMsg)); + entityViewUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); + testAutoGeneratedCodeByProtobuf(entityViewUpdateMsgBuilder); + uplinkMsgBuilder.addEntityViewUpdateMsg(entityViewUpdateMsgBuilder.build()); + + testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); + + edgeImitator.expectResponsesAmount(1); + edgeImitator.expectMessageAmount(1); + + edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); + + Assert.assertTrue(edgeImitator.waitForResponses()); + Assert.assertTrue(edgeImitator.waitForMessages()); + + Optional entityViewUpdateMsgOpt = edgeImitator.findMessageByType(EntityViewUpdateMsg.class); + Assert.assertTrue(entityViewUpdateMsgOpt.isPresent()); + EntityViewUpdateMsg latestEntityViewUpdateMsg = entityViewUpdateMsgOpt.get(); + entityViewMsg = JacksonUtil.fromString(latestEntityViewUpdateMsg.getEntity(), EntityView.class, true); + Assert.assertNotNull(entityViewMsg); + Assert.assertNotEquals(entityViewOnCloudName, entityViewMsg.getName()); + Assert.assertNotEquals(entityViewOnCloud.getId(), entityViewMsg.getId()); + + EntityView entityView = doGet("/api/entityView/" + entityViewMsg.getUuidId(), EntityView.class); + Assert.assertNotNull(entityView); + Assert.assertNotEquals(entityViewOnCloudName, entityView.getName()); + } + + @Test + public void testSendDeleteEntityViewOnEdgeToCloud() throws Exception { + Device device = findDeviceByName("Edge Device 1"); + EntityView savedEntityView = saveEntityViewOnCloudAndVerifyDeliveryToEdge(device); + + UplinkMsg.Builder upLinkMsgBuilder = UplinkMsg.newBuilder(); + EntityViewUpdateMsg.Builder entityViewDeleteMsgBuilder = EntityViewUpdateMsg.newBuilder(); + entityViewDeleteMsgBuilder.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE); + entityViewDeleteMsgBuilder.setIdMSB(savedEntityView.getUuidId().getMostSignificantBits()); + entityViewDeleteMsgBuilder.setIdLSB(savedEntityView.getUuidId().getLeastSignificantBits()); + testAutoGeneratedCodeByProtobuf(entityViewDeleteMsgBuilder); + + upLinkMsgBuilder.addEntityViewUpdateMsg(entityViewDeleteMsgBuilder.build()); + + testAutoGeneratedCodeByProtobuf(upLinkMsgBuilder); + + edgeImitator.expectResponsesAmount(1); + edgeImitator.sendUplinkMsg(upLinkMsgBuilder.build()); + Assert.assertTrue(edgeImitator.waitForResponses()); + EntityViewInfo entityViewInfo = doGet("/api/entityView/info/" + savedEntityView.getUuidId(), EntityViewInfo.class); + Assert.assertNotNull(entityViewInfo); + List edgeAssets = doGetTypedWithPageLink("/api/edge/" + edge.getUuidId() + "/entityViews?", + new TypeReference>() { + }, new PageLink(100)).getData(); + Assert.assertFalse(edgeAssets.contains(entityViewInfo)); + } + private void verifyEntityViewUpdateMsg(EntityView entityView, Device device) throws InvalidProtocolBufferException { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof EntityViewUpdateMsg); EntityViewUpdateMsg entityViewUpdateMsg = (EntityViewUpdateMsg) latestMessage; + EntityView entityViewMsg = JacksonUtil.fromString(entityViewUpdateMsg.getEntity(), EntityView.class, true); + Assert.assertNotNull(entityViewMsg); + Assert.assertEquals(entityView, entityViewMsg); + Assert.assertEquals(device.getId(), entityViewMsg.getEntityId()); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, entityViewUpdateMsg.getMsgType()); - Assert.assertEquals(entityView.getType(), entityViewUpdateMsg.getType()); - Assert.assertEquals(entityView.getName(), entityViewUpdateMsg.getName()); - Assert.assertEquals(entityView.getUuidId().getMostSignificantBits(), entityViewUpdateMsg.getIdMSB()); - Assert.assertEquals(entityView.getUuidId().getLeastSignificantBits(), entityViewUpdateMsg.getIdLSB()); - Assert.assertEquals(device.getUuidId().getMostSignificantBits(), entityViewUpdateMsg.getEntityIdMSB()); - Assert.assertEquals(device.getUuidId().getLeastSignificantBits(), entityViewUpdateMsg.getEntityIdLSB()); - Assert.assertEquals(device.getId().getEntityType().name(), entityViewUpdateMsg.getEntityType().name()); testAutoGeneratedCodeByProtobuf(entityViewUpdateMsg); } + private EntityView saveEntityViewOnCloudAndVerifyDeliveryToEdge(Device device) throws Exception { + // create entity view and assign to edge + EntityView savedEntityView = saveEntityView(StringUtils.randomAlphanumeric(15), device.getId()); + edgeImitator.expectMessageAmount(1); // entity view message + doPost("/api/edge/" + edge.getUuidId() + + "/entityView/" + savedEntityView.getUuidId(), EntityView.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + Optional entityViewUpdateMsgOpt = edgeImitator.findMessageByType(EntityViewUpdateMsg.class); + Assert.assertTrue(entityViewUpdateMsgOpt.isPresent()); + EntityViewUpdateMsg entityViewUpdateMsg = entityViewUpdateMsgOpt.get(); + Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, entityViewUpdateMsg.getMsgType()); + Assert.assertEquals(savedEntityView.getUuidId().getMostSignificantBits(), entityViewUpdateMsg.getIdMSB()); + Assert.assertEquals(savedEntityView.getUuidId().getLeastSignificantBits(), entityViewUpdateMsg.getIdLSB()); + return savedEntityView; + } + private EntityView saveEntityView(String name, DeviceId deviceId) { + EntityView entityView = new EntityView(); + entityView.setName(name); + entityView.setType("test"); + entityView.setEntityId(deviceId); + return doPost("/api/entityView", entityView, EntityView.class); + } + + + private EntityView buildEntityViewForUplinkMsg(Device device, String name) { + EntityView entityView = new EntityView(); + entityView.setId(new EntityViewId(UUID.randomUUID())); + entityView.setTenantId(tenantId); + entityView.setName(name); + entityView.setType("test"); + entityView.setEntityId(device.getId()); + return entityView; + } } diff --git a/application/src/test/java/org/thingsboard/server/edge/OtaPackageEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/OtaPackageEdgeTest.java index 5525d63f41..7b1d7d9208 100644 --- a/application/src/test/java/org/thingsboard/server/edge/OtaPackageEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/OtaPackageEdgeTest.java @@ -16,17 +16,16 @@ package org.thingsboard.server.edge; import com.google.protobuf.AbstractMessage; -import com.google.protobuf.ByteString; import org.junit.Assert; import org.junit.Test; import org.springframework.mock.web.MockMultipartFile; import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest; import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; -import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; @@ -59,22 +58,22 @@ public class OtaPackageEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof OtaPackageUpdateMsg); OtaPackageUpdateMsg otaPackageUpdateMsg = (OtaPackageUpdateMsg) latestMessage; + OtaPackage otaPackage = JacksonUtil.fromString(otaPackageUpdateMsg.getEntity(), OtaPackage.class, true); + Assert.assertNotNull(otaPackage); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, otaPackageUpdateMsg.getMsgType()); - Assert.assertEquals(savedFirmwareInfo.getUuidId().getMostSignificantBits(), otaPackageUpdateMsg.getIdMSB()); - Assert.assertEquals(savedFirmwareInfo.getUuidId().getLeastSignificantBits(), otaPackageUpdateMsg.getIdLSB()); - Assert.assertEquals(thermostatDeviceProfile.getUuidId().getMostSignificantBits(), otaPackageUpdateMsg.getDeviceProfileIdMSB()); - Assert.assertEquals(thermostatDeviceProfile.getUuidId().getLeastSignificantBits(), otaPackageUpdateMsg.getDeviceProfileIdLSB()); - Assert.assertEquals(FIRMWARE, OtaPackageType.valueOf(otaPackageUpdateMsg.getType())); - Assert.assertEquals("My firmware #1", otaPackageUpdateMsg.getTitle()); - Assert.assertEquals("v1.0", otaPackageUpdateMsg.getVersion()); - Assert.assertEquals("My firmware #1 v1.0", otaPackageUpdateMsg.getTag()); - Assert.assertEquals("http://localhost:8080/v1/package", otaPackageUpdateMsg.getUrl()); - Assert.assertFalse(otaPackageUpdateMsg.hasData()); - Assert.assertFalse(otaPackageUpdateMsg.hasFileName()); - Assert.assertFalse(otaPackageUpdateMsg.hasContentType()); - Assert.assertFalse(otaPackageUpdateMsg.hasChecksumAlgorithm()); - Assert.assertFalse(otaPackageUpdateMsg.hasChecksum()); - Assert.assertFalse(otaPackageUpdateMsg.hasDataSize()); + Assert.assertEquals(savedFirmwareInfo.getId(), otaPackage.getId()); + Assert.assertEquals(thermostatDeviceProfile.getId(), otaPackage.getDeviceProfileId()); + Assert.assertEquals(FIRMWARE, otaPackage.getType()); + Assert.assertEquals("My firmware #1", otaPackage.getTitle()); + Assert.assertEquals("v1.0", otaPackage.getVersion()); + Assert.assertEquals("My firmware #1 v1.0", otaPackage.getTag()); + Assert.assertEquals("http://localhost:8080/v1/package", otaPackage.getUrl()); + Assert.assertNull(otaPackage.getData()); + Assert.assertNull(otaPackage.getFileName()); + Assert.assertNull(otaPackage.getContentType()); + Assert.assertNull(otaPackage.getChecksumAlgorithm()); + Assert.assertNull(otaPackage.getChecksum()); + Assert.assertNull(otaPackage.getDataSize()); // delete ota package edgeImitator.expectMessageAmount(1); @@ -113,22 +112,22 @@ public class OtaPackageEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof OtaPackageUpdateMsg); OtaPackageUpdateMsg otaPackageUpdateMsg = (OtaPackageUpdateMsg) latestMessage; + OtaPackage otaPackage = JacksonUtil.fromString(otaPackageUpdateMsg.getEntity(), OtaPackage.class, true); + Assert.assertNotNull(otaPackage); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, otaPackageUpdateMsg.getMsgType()); - Assert.assertEquals(savedFirmwareInfo.getUuidId().getMostSignificantBits(), otaPackageUpdateMsg.getIdMSB()); - Assert.assertEquals(savedFirmwareInfo.getUuidId().getLeastSignificantBits(), otaPackageUpdateMsg.getIdLSB()); - Assert.assertEquals(thermostatDeviceProfile.getUuidId().getMostSignificantBits(), otaPackageUpdateMsg.getDeviceProfileIdMSB()); - Assert.assertEquals(thermostatDeviceProfile.getUuidId().getLeastSignificantBits(), otaPackageUpdateMsg.getDeviceProfileIdLSB()); - Assert.assertEquals(FIRMWARE, OtaPackageType.valueOf(otaPackageUpdateMsg.getType())); - Assert.assertEquals("My firmware #2", otaPackageUpdateMsg.getTitle()); - Assert.assertEquals("v2.0", otaPackageUpdateMsg.getVersion()); - Assert.assertEquals("My firmware #2 v2.0", otaPackageUpdateMsg.getTag()); - Assert.assertFalse(otaPackageUpdateMsg.hasUrl()); - Assert.assertEquals("firmware.bin", otaPackageUpdateMsg.getFileName()); - Assert.assertEquals("image/png", otaPackageUpdateMsg.getContentType()); - Assert.assertEquals(ChecksumAlgorithm.SHA256.name(), otaPackageUpdateMsg.getChecksumAlgorithm()); - Assert.assertEquals("62467691cf583d4fa78b18fafaf9801f505e0ef03baf0603fd4b0cd004cd1e75", otaPackageUpdateMsg.getChecksum()); - Assert.assertEquals(3L, otaPackageUpdateMsg.getDataSize()); - Assert.assertEquals(ByteString.copyFrom(new byte[]{1, 3, 5}), otaPackageUpdateMsg.getData()); + Assert.assertEquals(savedFirmwareInfo.getId(), otaPackage.getId()); + Assert.assertEquals(thermostatDeviceProfile.getId(), otaPackage.getDeviceProfileId()); + Assert.assertEquals(FIRMWARE, otaPackage.getType()); + Assert.assertEquals("My firmware #2", otaPackage.getTitle()); + Assert.assertEquals("v2.0", otaPackage.getVersion()); + Assert.assertEquals("My firmware #2 v2.0", otaPackage.getTag()); + Assert.assertFalse(otaPackage.hasUrl()); + Assert.assertEquals("firmware.bin", otaPackage.getFileName()); + Assert.assertEquals("image/png", otaPackage.getContentType()); + Assert.assertEquals(ChecksumAlgorithm.SHA256, otaPackage.getChecksumAlgorithm()); + Assert.assertEquals("62467691cf583d4fa78b18fafaf9801f505e0ef03baf0603fd4b0cd004cd1e75", otaPackage.getChecksum()); + Assert.assertEquals(3L, otaPackage.getDataSize().longValue()); + Assert.assertEquals(ByteBuffer.wrap(new byte[]{1, 3, 5}), otaPackage.getData()); // delete ota package edgeImitator.expectMessageAmount(1); diff --git a/application/src/test/java/org/thingsboard/server/edge/QueueEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/QueueEdgeTest.java index 32e8e7f222..28d48b2b80 100644 --- a/application/src/test/java/org/thingsboard/server/edge/QueueEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/QueueEdgeTest.java @@ -18,6 +18,7 @@ package org.thingsboard.server.edge; import com.google.protobuf.AbstractMessage; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.queue.ProcessingStrategy; import org.thingsboard.server.common.data.queue.ProcessingStrategyType; import org.thingsboard.server.common.data.queue.Queue; @@ -63,24 +64,22 @@ public class QueueEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof QueueUpdateMsg); QueueUpdateMsg queueUpdateMsg = (QueueUpdateMsg) latestMessage; + Queue queueMsg = JacksonUtil.fromString(queueUpdateMsg.getEntity(), Queue.class, true); + Assert.assertNotNull(queueMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, queueUpdateMsg.getMsgType()); - Assert.assertEquals(savedQueue.getUuidId().getMostSignificantBits(), queueUpdateMsg.getIdMSB()); - Assert.assertEquals(savedQueue.getUuidId().getLeastSignificantBits(), queueUpdateMsg.getIdLSB()); - Assert.assertEquals(savedQueue.getTenantId().getId().getMostSignificantBits(), queueUpdateMsg.getTenantIdMSB()); - Assert.assertEquals(savedQueue.getTenantId().getId().getLeastSignificantBits(), queueUpdateMsg.getTenantIdLSB()); - Assert.assertEquals("EdgeMain", queueUpdateMsg.getName()); - Assert.assertEquals("tb_rule_engine.EdgeMain", queueUpdateMsg.getTopic()); - Assert.assertEquals(25, queueUpdateMsg.getPollInterval()); - Assert.assertEquals(10, queueUpdateMsg.getPartitions()); - Assert.assertFalse(queueUpdateMsg.getConsumerPerPartition()); - Assert.assertEquals(2000, queueUpdateMsg.getPackProcessingTimeout()); - Assert.assertEquals(SubmitStrategyType.SEQUENTIAL_BY_ORIGINATOR.name(), queueUpdateMsg.getSubmitStrategy().getType()); - Assert.assertEquals(0, queueUpdateMsg.getSubmitStrategy().getBatchSize()); - Assert.assertEquals(ProcessingStrategyType.RETRY_ALL.name(), queueUpdateMsg.getProcessingStrategy().getType()); - Assert.assertEquals(3, queueUpdateMsg.getProcessingStrategy().getRetries()); - Assert.assertEquals(0.7, queueUpdateMsg.getProcessingStrategy().getFailurePercentage(), 1); - Assert.assertEquals(3, queueUpdateMsg.getProcessingStrategy().getPauseBetweenRetries()); - Assert.assertEquals(5, queueUpdateMsg.getProcessingStrategy().getMaxPauseBetweenRetries()); + Assert.assertEquals(savedQueue, queueMsg); + Assert.assertEquals("EdgeMain", queueMsg.getName()); + Assert.assertEquals("tb_rule_engine.EdgeMain", queueMsg.getTopic()); + Assert.assertEquals(25, queueMsg.getPollInterval()); + Assert.assertEquals(10, queueMsg.getPartitions()); + Assert.assertEquals(2000, queueMsg.getPackProcessingTimeout()); + Assert.assertEquals(SubmitStrategyType.SEQUENTIAL_BY_ORIGINATOR, queueMsg.getSubmitStrategy().getType()); + Assert.assertEquals(0, queueMsg.getSubmitStrategy().getBatchSize()); + Assert.assertEquals(ProcessingStrategyType.RETRY_ALL, queueMsg.getProcessingStrategy().getType()); + Assert.assertEquals(3, queueMsg.getProcessingStrategy().getRetries()); + Assert.assertEquals(0.7, queueMsg.getProcessingStrategy().getFailurePercentage(), 1); + Assert.assertEquals(3, queueMsg.getProcessingStrategy().getPauseBetweenRetries()); + Assert.assertEquals(5, queueMsg.getProcessingStrategy().getMaxPauseBetweenRetries()); // update queue edgeImitator.expectMessageAmount(1); @@ -90,8 +89,10 @@ public class QueueEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof QueueUpdateMsg); queueUpdateMsg = (QueueUpdateMsg) latestMessage; + queueMsg = JacksonUtil.fromString(queueUpdateMsg.getEntity(), Queue.class, true); + Assert.assertNotNull(queueMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, queueUpdateMsg.getMsgType()); - Assert.assertEquals(50, queueUpdateMsg.getPollInterval()); + Assert.assertEquals(50, queueMsg.getPollInterval()); // delete queue edgeImitator.expectMessageAmount(1); diff --git a/application/src/test/java/org/thingsboard/server/edge/RelationEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/RelationEdgeTest.java index 04e6f3a3fb..5d004fe7c5 100644 --- a/application/src/test/java/org/thingsboard/server/edge/RelationEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/RelationEdgeTest.java @@ -15,13 +15,14 @@ */ package org.thingsboard.server.edge; +import com.fasterxml.jackson.databind.node.TextNode; import com.google.protobuf.AbstractMessage; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.asset.Asset; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.service.DaoSqlTest; @@ -30,8 +31,6 @@ import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UplinkMsg; -import java.util.UUID; - import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @DaoSqlTest @@ -54,15 +53,10 @@ public class RelationEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof RelationUpdateMsg); RelationUpdateMsg relationUpdateMsg = (RelationUpdateMsg) latestMessage; + EntityRelation entityRelation = JacksonUtil.fromString(relationUpdateMsg.getEntity(), EntityRelation.class, true); + Assert.assertNotNull(entityRelation); + Assert.assertEquals(relation, entityRelation); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, relationUpdateMsg.getMsgType()); - Assert.assertEquals(relationUpdateMsg.getType(), relation.getType()); - Assert.assertEquals(relationUpdateMsg.getFromIdMSB(), relation.getFrom().getId().getMostSignificantBits()); - Assert.assertEquals(relationUpdateMsg.getFromIdLSB(), relation.getFrom().getId().getLeastSignificantBits()); - Assert.assertEquals(relationUpdateMsg.getToEntityType(), relation.getTo().getEntityType().name()); - Assert.assertEquals(relationUpdateMsg.getFromIdMSB(), relation.getFrom().getId().getMostSignificantBits()); - Assert.assertEquals(relationUpdateMsg.getToIdLSB(), relation.getTo().getId().getLeastSignificantBits()); - Assert.assertEquals(relationUpdateMsg.getToEntityType(), relation.getTo().getEntityType().name()); - Assert.assertEquals(relationUpdateMsg.getTypeGroup(), relation.getTypeGroup().name()); // delete relation edgeImitator.expectMessageAmount(1); @@ -78,15 +72,10 @@ public class RelationEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof RelationUpdateMsg); relationUpdateMsg = (RelationUpdateMsg) latestMessage; + entityRelation = JacksonUtil.fromString(relationUpdateMsg.getEntity(), EntityRelation.class, true); + Assert.assertNotNull(entityRelation); + Assert.assertEquals(relation, entityRelation); Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, relationUpdateMsg.getMsgType()); - Assert.assertEquals(relationUpdateMsg.getType(), relation.getType()); - Assert.assertEquals(relationUpdateMsg.getFromIdMSB(), relation.getFrom().getId().getMostSignificantBits()); - Assert.assertEquals(relationUpdateMsg.getFromIdLSB(), relation.getFrom().getId().getLeastSignificantBits()); - Assert.assertEquals(relationUpdateMsg.getToEntityType(), relation.getTo().getEntityType().name()); - Assert.assertEquals(relationUpdateMsg.getFromIdMSB(), relation.getFrom().getId().getMostSignificantBits()); - Assert.assertEquals(relationUpdateMsg.getToIdLSB(), relation.getTo().getId().getLeastSignificantBits()); - Assert.assertEquals(relationUpdateMsg.getToEntityType(), relation.getTo().getEntityType().name()); - Assert.assertEquals(relationUpdateMsg.getTypeGroup(), relation.getTypeGroup().name()); } @Test @@ -94,17 +83,11 @@ public class RelationEdgeTest extends AbstractEdgeTest { Device device1 = saveDeviceOnCloudAndVerifyDeliveryToEdge(); Device device2 = saveDeviceOnCloudAndVerifyDeliveryToEdge(); + EntityRelation entityRelation = buildEntityRelationForUplinkMsg(device1.getId(), device2.getId()); + UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); RelationUpdateMsg.Builder relationUpdateMsgBuilder = RelationUpdateMsg.newBuilder(); - relationUpdateMsgBuilder.setType("test"); - relationUpdateMsgBuilder.setTypeGroup(RelationTypeGroup.COMMON.name()); - relationUpdateMsgBuilder.setToIdMSB(device1.getId().getId().getMostSignificantBits()); - relationUpdateMsgBuilder.setToIdLSB(device1.getId().getId().getLeastSignificantBits()); - relationUpdateMsgBuilder.setToEntityType(device1.getId().getEntityType().name()); - relationUpdateMsgBuilder.setFromIdMSB(device2.getId().getId().getMostSignificantBits()); - relationUpdateMsgBuilder.setFromIdLSB(device2.getId().getId().getLeastSignificantBits()); - relationUpdateMsgBuilder.setFromEntityType(device2.getId().getEntityType().name()); - relationUpdateMsgBuilder.setAdditionalInfo("{}"); + relationUpdateMsgBuilder.setEntity(JacksonUtil.toString(entityRelation)); testAutoGeneratedCodeByProtobuf(relationUpdateMsgBuilder); uplinkMsgBuilder.addRelationUpdateMsg(relationUpdateMsgBuilder.build()); @@ -129,14 +112,24 @@ public class RelationEdgeTest extends AbstractEdgeTest { Device device = findDeviceByName("Edge Device 1"); Asset asset = findAssetByName("Edge Asset 1"); - EntityRelation relation = new EntityRelation(); - relation.setType("test"); - relation.setFrom(device.getId()); - relation.setTo(asset.getId()); - relation.setTypeGroup(RelationTypeGroup.COMMON); + EntityRelation deviceToAssetRelation = new EntityRelation(); + deviceToAssetRelation.setType("test"); + deviceToAssetRelation.setFrom(device.getId()); + deviceToAssetRelation.setTo(asset.getId()); + deviceToAssetRelation.setTypeGroup(RelationTypeGroup.COMMON); edgeImitator.expectMessageAmount(1); - doPost("/api/relation", relation); + doPost("/api/relation", deviceToAssetRelation); + Assert.assertTrue(edgeImitator.waitForMessages()); + + EntityRelation assetToTenantRelation = new EntityRelation(); + assetToTenantRelation.setType("test"); + assetToTenantRelation.setFrom(asset.getId()); + assetToTenantRelation.setTo(tenantId); + assetToTenantRelation.setTypeGroup(RelationTypeGroup.COMMON); + + edgeImitator.expectMessageAmount(1); + doPost("/api/relation", assetToTenantRelation); Assert.assertTrue(edgeImitator.waitForMessages()); UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); @@ -158,17 +151,19 @@ public class RelationEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof RelationUpdateMsg); RelationUpdateMsg relationUpdateMsg = (RelationUpdateMsg) latestMessage; + EntityRelation entityRelation = JacksonUtil.fromString(relationUpdateMsg.getEntity(), EntityRelation.class, true); + Assert.assertNotNull(entityRelation); + Assert.assertEquals(deviceToAssetRelation, entityRelation); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, relationUpdateMsg.getMsgType()); - Assert.assertEquals(relation.getType(), relationUpdateMsg.getType()); - - UUID fromUUID = new UUID(relationUpdateMsg.getFromIdMSB(), relationUpdateMsg.getFromIdLSB()); - EntityId fromEntityId = EntityIdFactory.getByTypeAndUuid(relationUpdateMsg.getFromEntityType(), fromUUID); - Assert.assertEquals(relation.getFrom(), fromEntityId); - - UUID toUUID = new UUID(relationUpdateMsg.getToIdMSB(), relationUpdateMsg.getToIdLSB()); - EntityId toEntityId = EntityIdFactory.getByTypeAndUuid(relationUpdateMsg.getToEntityType(), toUUID); - Assert.assertEquals(relation.getTo(), toEntityId); + } - Assert.assertEquals(relation.getTypeGroup().name(), relationUpdateMsg.getTypeGroup()); + private EntityRelation buildEntityRelationForUplinkMsg(DeviceId deviceId1, DeviceId deviceId2) { + EntityRelation relation = new EntityRelation(); + relation.setType("test"); + relation.setTypeGroup(RelationTypeGroup.COMMON); + relation.setTo(deviceId1); + relation.setFrom(deviceId2); + relation.setAdditionalInfo(TextNode.valueOf("{}")); + return relation; } } diff --git a/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java new file mode 100644 index 0000000000..b94d0cdefa --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/edge/ResourceEdgeTest.java @@ -0,0 +1,180 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.edge; + +import com.datastax.oss.driver.api.core.uuid.Uuids; +import com.google.protobuf.AbstractMessage; +import org.junit.Assert; +import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.gen.edge.v1.UplinkMsg; +import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg; + +import java.util.Optional; +import java.util.UUID; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@DaoSqlTest +public class ResourceEdgeTest extends AbstractEdgeTest { + + private static final String TEST_DATA = "77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCEtLQpGSUxFIElORk9STUFUSU9OCgpPTUEgUGVybWFuZW50IERvY3VtZW50CiAgIEZpbGU6IE9NQS1TVVAtTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci1WMV8wXzEtMjAxOTAyMjEtQQogICBUeXBlOiB4bWwKClB1YmxpYyBSZWFjaGFibGUgSW5mb3JtYXRpb24KICAgUGF0aDogaHR0cDovL3d3dy5vcGVubW9iaWxlYWxsaWFuY2Uub3JnL3RlY2gvcHJvZmlsZXMKICAgTmFtZTogTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci12MV8wXzEueG1sCgpOT1JNQVRJVkUgSU5GT1JNQVRJT04KCiAgSW5mb3JtYXRpb24gYWJvdXQgdGhpcyBmaWxlIGNhbiBiZSBmb3VuZCBpbiB0aGUgbGF0ZXN0IHJldmlzaW9uIG9mCgogIE9NQS1UUy1MV00yTV9CaW5hcnlBcHBEYXRhQ29udGFpbmVyLVYxXzBfMQoKICBUaGlzIGlzIGF2YWlsYWJsZSBhdCBodHRwOi8vd3d3Lm9wZW5tb2JpbGVhbGxpYW5jZS5vcmcvCgogIFNlbmQgY29tbWVudHMgdG8gaHR0cHM6Ly9naXRodWIuY29tL09wZW5Nb2JpbGVBbGxpYW5jZS9PTUFfTHdNMk1fZm9yX0RldmVsb3BlcnMvaXNzdWVzCgpDSEFOR0UgSElTVE9SWQoKMTUwNjIwMTggU3RhdHVzIGNoYW5nZWQgdG8gQXBwcm92ZWQgYnkgRE0sIERvYyBSZWYgIyBPTUEtRE0mU0UtMjAxOC0wMDYxLUlOUF9MV00yTV9BUFBEQVRBX1YxXzBfRVJQX2Zvcl9maW5hbF9BcHByb3ZhbAoyMTAyMjAxOSBTdGF0dXMgY2hhbmdlZCB0byBBcHByb3ZlZCBieSBJUFNPLCBEb2MgUmVmICMgT01BLUlQU08tMjAxOS0wMDI1LUlOUF9Md00yTV9PYmplY3RfQXBwX0RhdGFfQ29udGFpbmVyXzFfMF8xX2Zvcl9GaW5hbF9BcHByb3ZhbAoKTEVHQUwgRElTQ0xBSU1FUgoKQ29weXJpZ2h0IDIwMTkgT3BlbiBNb2JpbGUgQWxsaWFuY2UuCgpSZWRpc3RyaWJ1dGlvbiBhbmQgdXNlIGluIHNvdXJjZSBhbmQgYmluYXJ5IGZvcm1zLCB3aXRoIG9yIHdpdGhvdXQKbW9kaWZpY2F0aW9uLCBhcmUgcGVybWl0dGVkIHByb3ZpZGVkIHRoYXQgdGhlIGZvbGxvd2luZyBjb25kaXRpb25zCmFyZSBtZXQ6CgoxLiBSZWRpc3RyaWJ1dGlvbnMgb2Ygc291cmNlIGNvZGUgbXVzdCByZXRhaW4gdGhlIGFib3ZlIGNvcHlyaWdodApub3RpY2UsIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIuCjIuIFJlZGlzdHJpYnV0aW9ucyBpbiBiaW5hcnkgZm9ybSBtdXN0IHJlcHJvZHVjZSB0aGUgYWJvdmUgY29weXJpZ2h0Cm5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lciBpbiB0aGUKZG9jdW1lbnRhdGlvbiBhbmQvb3Igb3RoZXIgbWF0ZXJpYWxzIHByb3ZpZGVkIHdpdGggdGhlIGRpc3RyaWJ1dGlvbi4KMy4gTmVpdGhlciB0aGUgbmFtZSBvZiB0aGUgY29weXJpZ2h0IGhvbGRlciBub3IgdGhlIG5hbWVzIG9mIGl0cwpjb250cmlidXRvcnMgbWF5IGJlIHVzZWQgdG8gZW5kb3JzZSBvciBwcm9tb3RlIHByb2R1Y3RzIGRlcml2ZWQKZnJvbSB0aGlzIHNvZnR3YXJlIHdpdGhvdXQgc3BlY2lmaWMgcHJpb3Igd3JpdHRlbiBwZXJtaXNzaW9uLgoKVEhJUyBTT0ZUV0FSRSBJUyBQUk9WSURFRCBCWSBUSEUgQ09QWVJJR0hUIEhPTERFUlMgQU5EIENPTlRSSUJVVE9SUwoiQVMgSVMiIEFORCBBTlkgRVhQUkVTUyBPUiBJTVBMSUVEIFdBUlJBTlRJRVMsIElOQ0xVRElORywgQlVUIE5PVApMSU1JVEVEIFRPLCBUSEUgSU1QTElFRCBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSBBTkQgRklUTkVTUwpGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQVJFIERJU0NMQUlNRUQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRQpDT1BZUklHSFQgSE9MREVSIE9SIENPTlRSSUJVVE9SUyBCRSBMSUFCTEUgRk9SIEFOWSBESVJFQ1QsIElORElSRUNULApJTkNJREVOVEFMLCBTUEVDSUFMLCBFWEVNUExBUlksIE9SIENPTlNFUVVFTlRJQUwgREFNQUdFUyAoSU5DTFVESU5HLApCVVQgTk9UIExJTUlURUQgVE8sIFBST0NVUkVNRU5UIE9GIFNVQlNUSVRVVEUgR09PRFMgT1IgU0VSVklDRVM7CkxPU1MgT0YgVVNFLCBEQVRBLCBPUiBQUk9GSVRTOyBPUiBCVVNJTkVTUyBJTlRFUlJVUFRJT04pIEhPV0VWRVIKQ0FVU0VEIEFORCBPTiBBTlkgVEhFT1JZIE9GIExJQUJJTElUWSwgV0hFVEhFUiBJTiBDT05UUkFDVCwgU1RSSUNUCkxJQUJJTElUWSwgT1IgVE9SVCAoSU5DTFVESU5HIE5FR0xJR0VOQ0UgT1IgT1RIRVJXSVNFKSBBUklTSU5HIElOCkFOWSBXQVkgT1VUIE9GIFRIRSBVU0UgT0YgVEhJUyBTT0ZUV0FSRSwgRVZFTiBJRiBBRFZJU0VEIE9GIFRIRQpQT1NTSUJJTElUWSBPRiBTVUNIIERBTUFHRS4KClRoZSBhYm92ZSBsaWNlbnNlIGlzIHVzZWQgYXMgYSBsaWNlbnNlIHVuZGVyIGNvcHlyaWdodCBvbmx5LiBQbGVhc2UKcmVmZXJlbmNlIHRoZSBPTUEgSVBSIFBvbGljeSBmb3IgcGF0ZW50IGxpY2Vuc2luZyB0ZXJtczoKaHR0cHM6Ly93d3cub21hc3BlY3dvcmtzLm9yZy9hYm91dC9pbnRlbGxlY3R1YWwtcHJvcGVydHktcmlnaHRzLwoKLS0+CjxMV00yTSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6bm9OYW1lc3BhY2VTY2hlbWFMb2NhdGlvbj0iaHR0cDovL29wZW5tb2JpbGVhbGxpYW5jZS5vcmcvdGVjaC9wcm9maWxlcy9MV00yTS54c2QiPgoJPE9iamVjdCBPYmplY3RUeXBlPSJNT0RlZmluaXRpb24iPgoJCTxOYW1lPkJpbmFyeUFwcERhdGFDb250YWluZXI8L05hbWU+CgkJPERlc2NyaXB0aW9uMT48IVtDREFUQVtUaGlzIEx3TTJNIE9iamVjdHMgcHJvdmlkZXMgdGhlIGFwcGxpY2F0aW9uIHNlcnZpY2UgZGF0YSByZWxhdGVkIHRvIGEgTHdNMk0gU2VydmVyLCBlZy4gV2F0ZXIgbWV0ZXIgZGF0YS4gClRoZXJlIGFyZSBzZXZlcmFsIG1ldGhvZHMgdG8gY3JlYXRlIGluc3RhbmNlIHRvIGluZGljYXRlIHRoZSBtZXNzYWdlIGRpcmVjdGlvbiBiYXNlZCBvbiB0aGUgbmVnb3RpYXRpb24gYmV0d2VlbiBBcHBsaWNhdGlvbiBhbmQgTHdNMk0uIFRoZSBDbGllbnQgYW5kIFNlcnZlciBzaG91bGQgbmVnb3RpYXRlIHRoZSBpbnN0YW5jZShzKSB1c2VkIHRvIGV4Y2hhbmdlIHRoZSBkYXRhLiBGb3IgZXhhbXBsZToKIC0gVXNpbmcgYSBzaW5nbGUgaW5zdGFuY2UgZm9yIGJvdGggZGlyZWN0aW9ucyBjb21tdW5pY2F0aW9uLCBmcm9tIENsaWVudCB0byBTZXJ2ZXIgYW5kIGZyb20gU2VydmVyIHRvIENsaWVudC4KIC0gVXNpbmcgYW4gaW5zdGFuY2UgZm9yIGNvbW11bmljYXRpb24gZnJvbSBDbGllbnQgdG8gU2VydmVyIGFuZCBhbm90aGVyIG9uZSBmb3IgY29tbXVuaWNhdGlvbiBmcm9tIFNlcnZlciB0byBDbGllbnQKIC0gVXNpbmcgc2V2ZXJhbCBpbnN0YW5jZXMKXV0+PC9EZXNjcmlwdGlvbjE+CgkJPE9iamVjdElEPjE5PC9PYmplY3RJRD4KCQk8T2JqZWN0VVJOPnVybjpvbWE6bHdtMm06b21hOjE5PC9PYmplY3RVUk4+CgkJPExXTTJNVmVyc2lvbj4xLjA8L0xXTTJNVmVyc2lvbj4KCQk8T2JqZWN0VmVyc2lvbj4xLjA8L09iamVjdFZlcnNpb24+CgkJPE11bHRpcGxlSW5zdGFuY2VzPk11bHRpcGxlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJPFJlc291cmNlcz4KCQkJPEl0ZW0gSUQ9IjAiPjxOYW1lPkRhdGE8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5NdWx0aXBsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk1hbmRhdG9yeTwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+T3BhcXVlPC9UeXBlPgoJCQkJPFJhbmdlRW51bWVyYXRpb24gLz4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgYXBwbGljYXRpb24gZGF0YSBjb250ZW50Ll1dPjwvRGVzY3JpcHRpb24+CgkJCTwvSXRlbT4KCQkJPEl0ZW0gSUQ9IjEiPjxOYW1lPkRhdGEgUHJpb3JpdHk8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5TaW5nbGU8L011bHRpcGxlSW5zdGFuY2VzPgoJCQkJPE1hbmRhdG9yeT5PcHRpb25hbDwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+SW50ZWdlcjwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjEgYnl0ZXM8L1JhbmdlRW51bWVyYXRpb24+CgkJCQk8VW5pdHMgLz4KCQkJCTxEZXNjcmlwdGlvbj48IVtDREFUQVtJbmRpY2F0ZXMgdGhlIEFwcGxpY2F0aW9uIGRhdGEgcHJpb3JpdHk6CjA6SW1tZWRpYXRlCjE6QmVzdEVmZm9ydAoyOkxhdGVzdAozLTEwMDogUmVzZXJ2ZWQgZm9yIGZ1dHVyZSB1c2UuCjEwMS0yNTQ6IFByb3ByaWV0YXJ5IG1vZGUuXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iMiI+PE5hbWU+RGF0YSBDcmVhdGlvbiBUaW1lPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlRpbWU8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbiAvPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBEYXRhIGluc3RhbmNlIGNyZWF0aW9uIHRpbWVzdGFtcC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSIzIj48TmFtZT5EYXRhIERlc2NyaXB0aW9uPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlN0cmluZzwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjMyIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkYXRhIGRlc2NyaXB0aW9uLgplLmcuICJtZXRlciByZWFkaW5nIi5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSI0Ij48TmFtZT5EYXRhIEZvcm1hdDwvTmFtZT4KCQkJCTxPcGVyYXRpb25zPlJXPC9PcGVyYXRpb25zPgoJCQkJPE11bHRpcGxlSW5zdGFuY2VzPlNpbmdsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJCQk8VHlwZT5TdHJpbmc8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4zMiBieXRlczwvUmFuZ2VFbnVtZXJhdGlvbj4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgZm9ybWF0IG9mIHRoZSBBcHBsaWNhdGlvbiBEYXRhLgplLmcuIFlHLU1ldGVyLVdhdGVyLVJlYWRpbmcKVVRGOC1zdHJpbmcKXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iNSI+PE5hbWU+QXBwIElEPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPkludGVnZXI8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4yIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkZXN0aW5hdGlvbiBBcHBsaWNhdGlvbiBJRC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+PC9SZXNvdXJjZXM+CgkJPERlc2NyaXB0aW9uMj48IVtDREFUQVtdXT48L0Rlc2NyaXB0aW9uMj4KCTwvT2JqZWN0Pgo8L0xXTTJNPgo="; + private static final String FILE_NAME = "test.jks"; + + @Test + public void testResources_create_update_delete() throws Exception { + // create resource + TbResource resource = new TbResource(); + resource.setResourceType(ResourceType.JKS); + resource.setTitle("Edge Test Resource"); + resource.setFileName(FILE_NAME); + resource.setEncodedData(TEST_DATA); + + edgeImitator.expectMessageAmount(1); + TbResource savedResource = doPost("/api/resource", resource, TbResource.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + + AbstractMessage latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof ResourceUpdateMsg); + ResourceUpdateMsg resourceUpdateMsg = (ResourceUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, resourceUpdateMsg.getMsgType()); + Assert.assertEquals(savedResource.getUuidId().getMostSignificantBits(), resourceUpdateMsg.getIdMSB()); + Assert.assertEquals(savedResource.getUuidId().getLeastSignificantBits(), resourceUpdateMsg.getIdLSB()); + TbResource tbResource = JacksonUtil.fromString(resourceUpdateMsg.getEntity(), TbResource.class, true); + Assert.assertNotNull(tbResource); + Assert.assertEquals("Edge Test Resource", tbResource.getTitle()); + Assert.assertEquals(ResourceType.JKS, tbResource.getResourceType()); + Assert.assertEquals(FILE_NAME, tbResource.getResourceKey()); + Assert.assertEquals(FILE_NAME, tbResource.getFileName()); + Assert.assertEquals(TEST_DATA, tbResource.getEncodedData()); + Assert.assertTrue(StringUtils.isNotBlank(tbResource.getEtag())); + + // update resource + edgeImitator.expectMessageAmount(1); + savedResource.setTitle("Updated Edge Test Resource"); + savedResource = doPost("/api/resource", savedResource, TbResource.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof ResourceUpdateMsg); + resourceUpdateMsg = (ResourceUpdateMsg) latestMessage; + tbResource = JacksonUtil.fromString(resourceUpdateMsg.getEntity(), TbResource.class, true); + Assert.assertNotNull(tbResource); + Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, resourceUpdateMsg.getMsgType()); + Assert.assertEquals("Updated Edge Test Resource", tbResource.getTitle()); + + // delete resource + edgeImitator.expectMessageAmount(1); + doDelete("/api/resource/" + savedResource.getUuidId()) + .andExpect(status().isOk()); + Assert.assertTrue(edgeImitator.waitForMessages()); + latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof ResourceUpdateMsg); + resourceUpdateMsg = (ResourceUpdateMsg) latestMessage; + Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, resourceUpdateMsg.getMsgType()); + Assert.assertEquals(savedResource.getUuidId().getMostSignificantBits(), resourceUpdateMsg.getIdMSB()); + Assert.assertEquals(savedResource.getUuidId().getLeastSignificantBits(), resourceUpdateMsg.getIdLSB()); + } + + @Test + public void testSendResourceToCloud() throws Exception { + TbResource tbResource = createTbResource(); + UUID uuid = Uuids.timeBased(); + + UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); + ResourceUpdateMsg.Builder resourceUpdateMsgBuilder = ResourceUpdateMsg.newBuilder(); + resourceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); + resourceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); + resourceUpdateMsgBuilder.setEntity(JacksonUtil.toString(tbResource)); + resourceUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); + testAutoGeneratedCodeByProtobuf(resourceUpdateMsgBuilder); + uplinkMsgBuilder.addResourceUpdateMsg(resourceUpdateMsgBuilder.build()); + + testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); + + edgeImitator.expectResponsesAmount(1); + edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); + + Assert.assertTrue(edgeImitator.waitForResponses()); + + UplinkResponseMsg latestResponseMsg = edgeImitator.getLatestResponseMsg(); + Assert.assertTrue(latestResponseMsg.getSuccess()); + + TbResource tb = doGet("/api/resource/" + uuid, TbResource.class); + Assert.assertNotNull(tb); + Assert.assertEquals("Edge Test Resource", tb.getName()); + Assert.assertEquals(TEST_DATA, tb.getEncodedData()); + } + + @Test + public void testResourceToCloudWithNameThatAlreadyExistsOnCloud() throws Exception { + TbResource resource = createTbResource(); + + edgeImitator.expectMessageAmount(1); + TbResource savedResource = doPost("/api/resource", resource, TbResource.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + + UUID uuid = Uuids.timeBased(); + + UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); + ResourceUpdateMsg.Builder resourceUpdateMsgBuilder = ResourceUpdateMsg.newBuilder(); + resourceUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits()); + resourceUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); + resourceUpdateMsgBuilder.setEntity(JacksonUtil.toString(resource)); + resourceUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); + testAutoGeneratedCodeByProtobuf(resourceUpdateMsgBuilder); + uplinkMsgBuilder.addResourceUpdateMsg(resourceUpdateMsgBuilder.build()); + + testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder); + + edgeImitator.expectResponsesAmount(1); + edgeImitator.expectMessageAmount(1); + + edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build()); + + Assert.assertTrue(edgeImitator.waitForResponses()); + Assert.assertTrue(edgeImitator.waitForMessages()); + + Optional resourceUpdateMsgOpt = edgeImitator.findMessageByType(ResourceUpdateMsg.class); + Assert.assertTrue(resourceUpdateMsgOpt.isPresent()); + ResourceUpdateMsg latestResourceUpdateMsg = resourceUpdateMsgOpt.get(); + TbResource resourceMsg = JacksonUtil.fromString(latestResourceUpdateMsg.getEntity(), TbResource.class, true); + Assert.assertNotNull(resourceMsg); + Assert.assertNotEquals(FILE_NAME, resourceMsg.getResourceKey()); + + Assert.assertNotEquals(savedResource.getUuidId(), uuid); + + TbResource tbResource = doGet("/api/resource/" + uuid, TbResource.class); + Assert.assertNotNull(tbResource); + Assert.assertNotEquals(FILE_NAME, tbResource.getName()); + } + + private TbResource createTbResource() { + TbResource tbResource = new TbResource(); + tbResource.setTenantId(tenantId); + tbResource.setTitle("Edge Test Resource"); + tbResource.setResourceType(ResourceType.JKS); + tbResource.setResourceKey(FILE_NAME); + tbResource.setFileName(FILE_NAME); + tbResource.setEncodedData(TEST_DATA); + return tbResource; + } +} diff --git a/application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java index b810810225..9782e3c41d 100644 --- a/application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java @@ -19,6 +19,8 @@ import com.google.protobuf.AbstractMessage; import org.junit.Assert; import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; +import org.thingsboard.rule.engine.util.TbMsgSource; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.rule.RuleChain; @@ -33,15 +35,17 @@ import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UplinkMsg; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.UUID; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @DaoSqlTest public class RuleChainEdgeTest extends AbstractEdgeTest { + private static final int CONFIGURATION_VERSION = 5; + @Test public void testRuleChains() throws Exception { // create rule chain @@ -57,11 +61,12 @@ public class RuleChainEdgeTest extends AbstractEdgeTest { Optional ruleChainUpdateMsgOpt = edgeImitator.findMessageByType(RuleChainUpdateMsg.class); Assert.assertTrue(ruleChainUpdateMsgOpt.isPresent()); RuleChainUpdateMsg ruleChainUpdateMsg = ruleChainUpdateMsgOpt.get(); + RuleChain ruleChainMsg = JacksonUtil.fromString(ruleChainUpdateMsg.getEntity(), RuleChain.class, true); + Assert.assertNotNull(ruleChainMsg); Assert.assertTrue(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE.equals(ruleChainUpdateMsg.getMsgType()) || UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE.equals(ruleChainUpdateMsg.getMsgType())); - Assert.assertEquals(ruleChainUpdateMsg.getIdMSB(), savedRuleChain.getUuidId().getMostSignificantBits()); - Assert.assertEquals(ruleChainUpdateMsg.getIdLSB(), savedRuleChain.getUuidId().getLeastSignificantBits()); - Assert.assertEquals(ruleChainUpdateMsg.getName(), savedRuleChain.getName()); + Assert.assertEquals(savedRuleChain.getId(), ruleChainMsg.getId()); + Assert.assertEquals(savedRuleChain.getName(), ruleChainMsg.getName()); testRuleChainMetadataRequestMsg(savedRuleChain.getId()); @@ -81,7 +86,7 @@ public class RuleChainEdgeTest extends AbstractEdgeTest { edgeImitator.expectMessageAmount(1); doDelete("/api/ruleChain/" + savedRuleChain.getUuidId()) .andExpect(status().isOk()); - Assert.assertFalse(edgeImitator.waitForMessages(1)); + Assert.assertTrue(edgeImitator.waitForMessages(5)); } @Test @@ -106,8 +111,9 @@ public class RuleChainEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof RuleChainMetadataUpdateMsg); RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = (RuleChainMetadataUpdateMsg) latestMessage; - Assert.assertEquals(ruleChainMetadataUpdateMsg.getRuleChainIdMSB(), edgeRootRuleChainId.getId().getMostSignificantBits()); - Assert.assertEquals(ruleChainMetadataUpdateMsg.getRuleChainIdLSB(), edgeRootRuleChainId.getId().getLeastSignificantBits()); + RuleChainMetaData ruleChainMetadataMsg = JacksonUtil.fromString(ruleChainMetadataUpdateMsg.getEntity(), RuleChainMetaData.class, true); + Assert.assertNotNull(ruleChainMetadataMsg); + Assert.assertEquals(edgeRootRuleChainId, ruleChainMetadataMsg.getRuleChainId()); testAutoGeneratedCodeByProtobuf(ruleChainMetadataUpdateMsg); } @@ -131,29 +137,39 @@ public class RuleChainEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof RuleChainMetadataUpdateMsg); RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = (RuleChainMetadataUpdateMsg) latestMessage; - RuleChainId receivedRuleChainId = - new RuleChainId(new UUID(ruleChainMetadataUpdateMsg.getRuleChainIdMSB(), ruleChainMetadataUpdateMsg.getRuleChainIdLSB())); - Assert.assertEquals(ruleChainId, receivedRuleChainId); + RuleChainMetaData ruleChainMetadataMsg = JacksonUtil.fromString(ruleChainMetadataUpdateMsg.getEntity(), RuleChainMetaData.class, true); + Assert.assertNotNull(ruleChainMetadataMsg); + Assert.assertEquals(ruleChainId, ruleChainMetadataMsg.getRuleChainId()); + + for (RuleNode ruleNode : ruleChainMetadataMsg.getNodes()) { + Assert.assertEquals(CONFIGURATION_VERSION, ruleNode.getConfigurationVersion()); + } } - private void createRuleChainMetadata(RuleChain ruleChain) throws Exception { + private void createRuleChainMetadata(RuleChain ruleChain) { RuleChainMetaData ruleChainMetaData = new RuleChainMetaData(); ruleChainMetaData.setRuleChainId(ruleChain.getId()); RuleNode ruleNode1 = new RuleNode(); ruleNode1.setName("name1"); - ruleNode1.setType("type1"); - ruleNode1.setConfiguration(JacksonUtil.toJsonNode("\"key1\": \"val1\"")); + ruleNode1.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode1.setConfigurationVersion(CONFIGURATION_VERSION); + TbGetAttributesNodeConfiguration configuration = new TbGetAttributesNodeConfiguration(); + configuration.setFetchTo(TbMsgSource.METADATA); + configuration.setServerAttributeNames(Collections.singletonList("serverAttributeKey2")); + ruleNode1.setConfiguration(JacksonUtil.valueToTree(configuration)); RuleNode ruleNode2 = new RuleNode(); ruleNode2.setName("name2"); - ruleNode2.setType("type2"); - ruleNode2.setConfiguration(JacksonUtil.toJsonNode("\"key2\": \"val2\"")); + ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode2.setConfigurationVersion(CONFIGURATION_VERSION); + ruleNode2.setConfiguration(JacksonUtil.valueToTree(configuration)); RuleNode ruleNode3 = new RuleNode(); ruleNode3.setName("name3"); - ruleNode3.setType("type3"); - ruleNode3.setConfiguration(JacksonUtil.toJsonNode("\"key3\": \"val3\"")); + ruleNode3.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode3.setConfigurationVersion(CONFIGURATION_VERSION); + ruleNode3.setConfiguration(JacksonUtil.valueToTree(configuration)); List ruleNodes = new ArrayList<>(); ruleNodes.add(ruleNode1); @@ -172,11 +188,12 @@ public class RuleChainEdgeTest extends AbstractEdgeTest { @Test public void testSetRootRuleChain() throws Exception { // create rule chain - edgeImitator.expectMessageAmount(1); RuleChain ruleChain = new RuleChain(); ruleChain.setName("Edge New Root Rule Chain"); ruleChain.setType(RuleChainType.EDGE); RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class); + + edgeImitator.expectMessageAmount(1); doPost("/api/edge/" + edge.getUuidId() + "/ruleChain/" + savedRuleChain.getUuidId(), RuleChain.class); Assert.assertTrue(edgeImitator.waitForMessages()); @@ -190,10 +207,11 @@ public class RuleChainEdgeTest extends AbstractEdgeTest { Optional ruleChainUpdateMsgOpt = edgeImitator.findMessageByType(RuleChainUpdateMsg.class); Assert.assertTrue(ruleChainUpdateMsgOpt.isPresent()); RuleChainUpdateMsg ruleChainUpdateMsg = ruleChainUpdateMsgOpt.get(); + RuleChain ruleChainMsg = JacksonUtil.fromString(ruleChainUpdateMsg.getEntity(), RuleChain.class, true); + Assert.assertNotNull(ruleChainMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, ruleChainUpdateMsg.getMsgType()); - Assert.assertEquals(ruleChainUpdateMsg.getIdMSB(), savedRuleChain.getUuidId().getMostSignificantBits()); - Assert.assertEquals(ruleChainUpdateMsg.getIdLSB(), savedRuleChain.getUuidId().getLeastSignificantBits()); - Assert.assertTrue(ruleChainUpdateMsg.getRoot()); + Assert.assertTrue(ruleChainMsg.isRoot()); + Assert.assertEquals(savedRuleChain.getId(), ruleChainMsg.getId()); // revert root rule chain edgeImitator.expectMessageAmount(1); @@ -211,6 +229,6 @@ public class RuleChainEdgeTest extends AbstractEdgeTest { edgeImitator.expectMessageAmount(1); doDelete("/api/ruleChain/" + savedRuleChain.getUuidId()) .andExpect(status().isOk()); - Assert.assertFalse(edgeImitator.waitForMessages(1)); + Assert.assertTrue(edgeImitator.waitForMessages(5)); } } diff --git a/application/src/test/java/org/thingsboard/server/edge/TelemetryEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/TelemetryEdgeTest.java index 0cf1c2d149..2ee267b88c 100644 --- a/application/src/test/java/org/thingsboard/server/edge/TelemetryEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/TelemetryEdgeTest.java @@ -215,7 +215,7 @@ public class TelemetryEdgeTest extends AbstractEdgeTest { @Test public void testAttributesUpdatedMsg_userEntity() throws Exception { - testAttributesUpdatedMsg(tenantAdmin.getId()); + testAttributesUpdatedMsg(tenantAdminUserId); } private void testAttributesUpdatedMsg(EntityId entityId) throws Exception { diff --git a/application/src/test/java/org/thingsboard/server/edge/TenantEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/TenantEdgeTest.java new file mode 100644 index 0000000000..1889e49e11 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/edge/TenantEdgeTest.java @@ -0,0 +1,87 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.edge; + +import org.junit.Assert; +import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +import java.util.Optional; + +@DaoSqlTest +public class TenantEdgeTest extends AbstractEdgeTest { + + @Test + public void testUpdateTenant() throws Exception { + loginSysAdmin(); + + // save current value into tmp to revert after test + Tenant savedTenant = doGet("/api/tenant/" + tenantId, Tenant.class); + + // updated edge tenant + savedTenant.setTitle("Updated Title for Tenant Edge Test"); + edgeImitator.expectMessageAmount(2); // expect tenant and tenant profile update msg + savedTenant = doPost("/api/tenant", savedTenant, Tenant.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + Optional tenantUpdateMsgOpt = edgeImitator.findMessageByType(TenantUpdateMsg.class); + Assert.assertTrue(tenantUpdateMsgOpt.isPresent()); + TenantUpdateMsg tenantUpdateMsg = tenantUpdateMsgOpt.get(); + Optional tenantProfileUpdateMsgOpt = edgeImitator.findMessageByType(TenantProfileUpdateMsg.class); + Assert.assertTrue(tenantProfileUpdateMsgOpt.isPresent()); + TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantProfileUpdateMsgOpt.get(); + Tenant tenantMsg = JacksonUtil.fromString(tenantUpdateMsg.getEntity(), Tenant.class, true); + Assert.assertNotNull(tenantMsg); + Assert.assertEquals(savedTenant, tenantMsg); + TenantProfile tenantProfileMsg = JacksonUtil.fromString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class, true); + Assert.assertNotNull(tenantProfileMsg); + Assert.assertEquals(tenantMsg.getTenantProfileId(), tenantProfileMsg.getId()); + Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantUpdateMsg.getMsgType()); + Assert.assertEquals("Updated Title for Tenant Edge Test", tenantMsg.getTitle()); + + //change tenant profile for tenant + TenantProfile tenantProfile = createTenantProfile(); + savedTenant.setTenantProfileId(tenantProfile.getId()); + edgeImitator.expectMessageAmount(2); // expect tenant and tenant profile update msg + savedTenant = doPost("/api/tenant", savedTenant, Tenant.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + tenantUpdateMsgOpt = edgeImitator.findMessageByType(TenantUpdateMsg.class); + Assert.assertTrue(tenantUpdateMsgOpt.isPresent()); + tenantUpdateMsg = tenantUpdateMsgOpt.get(); + tenantMsg = JacksonUtil.fromString(tenantUpdateMsg.getEntity(), Tenant.class, true); + Assert.assertNotNull(tenantMsg); + tenantProfileUpdateMsgOpt = edgeImitator.findMessageByType(TenantProfileUpdateMsg.class); + Assert.assertTrue(tenantProfileUpdateMsgOpt.isPresent()); + tenantProfileUpdateMsg = tenantProfileUpdateMsgOpt.get(); + tenantProfileMsg = JacksonUtil.fromString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class, true); + Assert.assertNotNull(tenantProfileMsg); + // tenant update + Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantUpdateMsg.getMsgType()); + Assert.assertEquals(savedTenant, tenantMsg); + Assert.assertEquals(savedTenant.getTenantProfileId(), tenantProfileMsg.getId()); + } + + private TenantProfile createTenantProfile() { + TenantProfile tenantProfile = new TenantProfile(); + tenantProfile.setName("TestEdge tenant profile"); + return doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); + } +} diff --git a/application/src/test/java/org/thingsboard/server/edge/TenantProfileEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/TenantProfileEdgeTest.java new file mode 100644 index 0000000000..1f6b7203b3 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/edge/TenantProfileEdgeTest.java @@ -0,0 +1,136 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.edge; + +import com.google.protobuf.AbstractMessage; +import org.junit.Assert; +import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.queue.ProcessingStrategy; +import org.thingsboard.server.common.data.queue.ProcessingStrategyType; +import org.thingsboard.server.common.data.queue.Queue; +import org.thingsboard.server.common.data.queue.SubmitStrategy; +import org.thingsboard.server.common.data.queue.SubmitStrategyType; +import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; +import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; +import org.thingsboard.server.gen.edge.v1.UpdateMsgType; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +@DaoSqlTest +public class TenantProfileEdgeTest extends AbstractEdgeTest { + + @Test + public void testTenantProfiles() throws Exception { + loginSysAdmin(); + + // save current values into tmp to revert after test + TenantProfile edgeTenantProfile = doGet("/api/tenantProfile/" + tenantProfileId.getId(), TenantProfile.class); + + // updated edge tenant profile + edgeTenantProfile.setName("Tenant Profile Edge Test"); + edgeTenantProfile.setDescription("Updated tenant profile Edge Test"); + edgeImitator.expectMessageAmount(1); + edgeTenantProfile = doPost("/api/tenantProfile", edgeTenantProfile, TenantProfile.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + AbstractMessage latestMessage = edgeImitator.getLatestMessage(); + Assert.assertTrue(latestMessage instanceof TenantProfileUpdateMsg); + TenantProfileUpdateMsg tenantProfileUpdateMsg = (TenantProfileUpdateMsg) latestMessage; + TenantProfile tenantProfileMsg = JacksonUtil.fromString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class, true); + Assert.assertNotNull(tenantProfileMsg); + Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantProfileUpdateMsg.getMsgType()); + Assert.assertEquals(edgeTenantProfile, tenantProfileMsg); + Assert.assertEquals("Updated tenant profile Edge Test", tenantProfileMsg.getDescription()); + Assert.assertEquals("Tenant Profile Edge Test", tenantProfileMsg.getName()); + + loginTenantAdmin(); + } + + @Test + public void testIsolatedTenantProfile() throws Exception { + loginSysAdmin(); + + TenantProfile edgeTenantProfile = doGet("/api/tenantProfile/" + tenantProfileId.getId(), TenantProfile.class); + + // set tenant profile isolated and add 2 queues - main and isolated + edgeTenantProfile.setIsolatedTbRuleEngine(true); + TenantProfileQueueConfiguration mainQueueConfiguration = createQueueConfig(DataConstants.MAIN_QUEUE_NAME, DataConstants.MAIN_QUEUE_TOPIC); + TenantProfileQueueConfiguration isolatedQueueConfiguration = createQueueConfig("IsolatedHighPriority", "tb_rule_engine.isolated_hp"); + edgeTenantProfile.getProfileData().setQueueConfiguration(List.of(mainQueueConfiguration, isolatedQueueConfiguration)); + edgeImitator.expectMessageAmount(3); + edgeTenantProfile = doPost("/api/tenantProfile", edgeTenantProfile, TenantProfile.class); + Assert.assertTrue(edgeImitator.waitForMessages()); + + Optional tenantProfileUpdateMsgOpt = edgeImitator.findMessageByType(TenantProfileUpdateMsg.class); + Assert.assertTrue(tenantProfileUpdateMsgOpt.isPresent()); + TenantProfileUpdateMsg tenantProfileUpdateMsg = tenantProfileUpdateMsgOpt.get(); + TenantProfile tenantProfile = JacksonUtil.fromString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class, true); + Assert.assertNotNull(tenantProfile); + Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, tenantProfileUpdateMsg.getMsgType()); + Assert.assertEquals(edgeTenantProfile.getId(), tenantProfile.getId()); + Assert.assertEquals(edgeTenantProfile.getDescription(), tenantProfile.getDescription()); + + List queueUpdateMsgs = edgeImitator.findAllMessagesByType(QueueUpdateMsg.class); + Assert.assertEquals(2, queueUpdateMsgs.size()); + + loginTenantAdmin(); + + edgeImitator.expectMessageAmount(21); + doPost("/api/edge/sync/" + edge.getId()); + assertThat(edgeImitator.waitForMessages()).as("await for messages after edge sync rest api call").isTrue(); + + Assert.assertTrue(edgeImitator.getDownlinkMsgs().get(0) instanceof TenantUpdateMsg); + Assert.assertTrue(edgeImitator.getDownlinkMsgs().get(1) instanceof TenantProfileUpdateMsg); + + queueUpdateMsgs = edgeImitator.findAllMessagesByType(QueueUpdateMsg.class); + Assert.assertEquals(2, queueUpdateMsgs.size()); + for (QueueUpdateMsg queueUpdateMsg : queueUpdateMsgs) { + Queue queue = JacksonUtil.fromString(queueUpdateMsg.getEntity(), Queue.class, true); + Assert.assertNotNull(queue); + Assert.assertEquals(tenantId, queue.getTenantId()); + } + } + + private TenantProfileQueueConfiguration createQueueConfig(String queueName, String queueTopic) { + TenantProfileQueueConfiguration queueConfiguration = new TenantProfileQueueConfiguration(); + queueConfiguration.setName(queueName); + queueConfiguration.setTopic(queueTopic); + queueConfiguration.setPollInterval(25); + queueConfiguration.setPartitions(10); + queueConfiguration.setConsumerPerPartition(true); + queueConfiguration.setPackProcessingTimeout(2000); + SubmitStrategy mainQueueSubmitStrategy = new SubmitStrategy(); + mainQueueSubmitStrategy.setType(SubmitStrategyType.BURST); + mainQueueSubmitStrategy.setBatchSize(1000); + queueConfiguration.setSubmitStrategy(mainQueueSubmitStrategy); + ProcessingStrategy mainQueueProcessingStrategy = new ProcessingStrategy(); + mainQueueProcessingStrategy.setType(ProcessingStrategyType.SKIP_ALL_FAILURES); + mainQueueProcessingStrategy.setRetries(3); + mainQueueProcessingStrategy.setFailurePercentage(0); + mainQueueProcessingStrategy.setPauseBetweenRetries(3); + mainQueueProcessingStrategy.setMaxPauseBetweenRetries(3); + queueConfiguration.setProcessingStrategy(mainQueueProcessingStrategy); + return queueConfiguration; + } +} diff --git a/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java index f376f50ac5..031731ebcb 100644 --- a/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java @@ -20,10 +20,12 @@ import org.junit.Assert; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UplinkMsg; @@ -45,25 +47,26 @@ public class UserEdgeTest extends AbstractEdgeTest { @Test public void testCreateUpdateDeleteTenantUser() throws Exception { // create user - edgeImitator.expectMessageAmount(2); + edgeImitator.expectMessageAmount(3); User newTenantAdmin = new User(); newTenantAdmin.setAuthority(Authority.TENANT_ADMIN); - newTenantAdmin.setTenantId(savedTenant.getId()); + newTenantAdmin.setTenantId(tenantId); newTenantAdmin.setEmail("tenantAdmin@thingsboard.org"); newTenantAdmin.setFirstName("Boris"); newTenantAdmin.setLastName("Johnson"); User savedTenantAdmin = createUser(newTenantAdmin, "tenant"); - Assert.assertTrue(edgeImitator.waitForMessages()); // wait 2 messages - user update msg and user credentials update msg - Optional latestMessageOpt = edgeImitator.findMessageByType(UserUpdateMsg.class); - Assert.assertTrue(latestMessageOpt.isPresent()); - UserUpdateMsg userUpdateMsg = latestMessageOpt.get(); + Assert.assertTrue(edgeImitator.waitForMessages()); // wait 3 messages - user update msg and x2 user credentials update msgs + Optional userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class); + Assert.assertTrue(userUpdateMsgOpt.isPresent()); + UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get(); + User userMsg = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true); + Assert.assertNotNull(userMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, userUpdateMsg.getMsgType()); - Assert.assertEquals(savedTenantAdmin.getUuidId().getMostSignificantBits(), userUpdateMsg.getIdMSB()); - Assert.assertEquals(savedTenantAdmin.getUuidId().getLeastSignificantBits(), userUpdateMsg.getIdLSB()); - Assert.assertEquals(savedTenantAdmin.getAuthority().name(), userUpdateMsg.getAuthority()); - Assert.assertEquals(savedTenantAdmin.getEmail(), userUpdateMsg.getEmail()); - Assert.assertEquals(savedTenantAdmin.getFirstName(), userUpdateMsg.getFirstName()); - Assert.assertEquals(savedTenantAdmin.getLastName(), userUpdateMsg.getLastName()); + Assert.assertEquals(savedTenantAdmin.getId(), userMsg.getId()); + Assert.assertEquals(savedTenantAdmin.getAuthority(), userMsg.getAuthority()); + Assert.assertEquals(savedTenantAdmin.getEmail(), userMsg.getEmail()); + Assert.assertEquals(savedTenantAdmin.getFirstName(), userMsg.getFirstName()); + Assert.assertEquals(savedTenantAdmin.getLastName(), userMsg.getLastName()); Optional userCredentialsUpdateMsgOpt = edgeImitator.findMessageByType(UserCredentialsUpdateMsg.class); Assert.assertTrue(userCredentialsUpdateMsgOpt.isPresent()); @@ -75,12 +78,15 @@ public class UserEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof UserUpdateMsg); userUpdateMsg = (UserUpdateMsg) latestMessage; + userMsg = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true); + Assert.assertNotNull(userMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, userUpdateMsg.getMsgType()); - Assert.assertEquals(savedTenantAdmin.getLastName(), userUpdateMsg.getLastName()); + Assert.assertEquals(savedTenantAdmin.getLastName(), userMsg.getLastName()); // update user credentials - edgeImitator.expectMessageAmount(1); login(savedTenantAdmin.getEmail(), "tenant"); + + edgeImitator.expectMessageAmount(1); ChangePasswordRequest changePasswordRequest = new ChangePasswordRequest(); changePasswordRequest.setCurrentPassword("tenant"); changePasswordRequest.setNewPassword("newTenant"); @@ -89,13 +95,15 @@ public class UserEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg); UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage; - Assert.assertEquals(savedTenantAdmin.getUuidId().getMostSignificantBits(), userCredentialsUpdateMsg.getUserIdMSB()); - Assert.assertEquals(savedTenantAdmin.getUuidId().getLeastSignificantBits(), userCredentialsUpdateMsg.getUserIdLSB()); - Assert.assertTrue(passwordEncoder.matches(changePasswordRequest.getNewPassword(), userCredentialsUpdateMsg.getPassword())); + UserCredentials userCredentialsMsg = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true); + Assert.assertNotNull(userCredentialsMsg); + Assert.assertEquals(savedTenantAdmin.getId(), userCredentialsMsg.getUserId()); + Assert.assertTrue(passwordEncoder.matches(changePasswordRequest.getNewPassword(), userCredentialsMsg.getPassword())); + + loginTenantAdmin(); // delete user edgeImitator.expectMessageAmount(1); - login(tenantAdmin.getEmail(), "testPassword1"); doDelete("/api/user/" + savedTenantAdmin.getUuidId()) .andExpect(status().isOk()); Assert.assertTrue(edgeImitator.waitForMessages()); @@ -114,7 +122,7 @@ public class UserEdgeTest extends AbstractEdgeTest { Customer customer = new Customer(); customer.setTitle("Edge Customer"); Customer savedCustomer = doPost("/api/customer", customer, Customer.class); - Assert.assertFalse(edgeImitator.waitForMessages(1)); + Assert.assertFalse(edgeImitator.waitForMessages(5)); // assign edge to customer edgeImitator.expectMessageAmount(2); @@ -123,28 +131,28 @@ public class UserEdgeTest extends AbstractEdgeTest { Assert.assertTrue(edgeImitator.waitForMessages()); // create user - edgeImitator.expectMessageAmount(2); + edgeImitator.expectMessageAmount(3); User customerUser = new User(); customerUser.setAuthority(Authority.CUSTOMER_USER); - customerUser.setTenantId(savedTenant.getId()); + customerUser.setTenantId(tenantId); customerUser.setCustomerId(savedCustomer.getId()); customerUser.setEmail("customerUser@thingsboard.org"); customerUser.setFirstName("John"); customerUser.setLastName("Edwards"); User savedCustomerUser = createUser(customerUser, "customer"); - Assert.assertTrue(edgeImitator.waitForMessages()); // wait 2 messages - user update msg and user credentials update msg - Optional latestMessageOpt = edgeImitator.findMessageByType(UserUpdateMsg.class); - Assert.assertTrue(latestMessageOpt.isPresent()); - UserUpdateMsg userUpdateMsg = latestMessageOpt.get(); + Assert.assertTrue(edgeImitator.waitForMessages()); // wait 3 messages - user update msg and x2 user credentials update msgs + Optional userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class); + Assert.assertTrue(userUpdateMsgOpt.isPresent()); + UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get(); + User userMsg = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true); + Assert.assertNotNull(userMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, userUpdateMsg.getMsgType()); - Assert.assertEquals(savedCustomerUser.getUuidId().getMostSignificantBits(), userUpdateMsg.getIdMSB()); - Assert.assertEquals(savedCustomerUser.getUuidId().getLeastSignificantBits(), userUpdateMsg.getIdLSB()); - Assert.assertEquals(savedCustomerUser.getCustomerId().getId().getMostSignificantBits(), userUpdateMsg.getCustomerIdMSB()); - Assert.assertEquals(savedCustomerUser.getCustomerId().getId().getLeastSignificantBits(), userUpdateMsg.getCustomerIdLSB()); - Assert.assertEquals(savedCustomerUser.getAuthority().name(), userUpdateMsg.getAuthority()); - Assert.assertEquals(savedCustomerUser.getEmail(), userUpdateMsg.getEmail()); - Assert.assertEquals(savedCustomerUser.getFirstName(), userUpdateMsg.getFirstName()); - Assert.assertEquals(savedCustomerUser.getLastName(), userUpdateMsg.getLastName()); + Assert.assertEquals(savedCustomerUser.getId(), userMsg.getId()); + Assert.assertEquals(savedCustomerUser.getCustomerId(), userMsg.getCustomerId()); + Assert.assertEquals(savedCustomerUser.getAuthority(), userMsg.getAuthority()); + Assert.assertEquals(savedCustomerUser.getEmail(), userMsg.getEmail()); + Assert.assertEquals(savedCustomerUser.getFirstName(), userMsg.getFirstName()); + Assert.assertEquals(savedCustomerUser.getLastName(), userMsg.getLastName()); // update user edgeImitator.expectMessageAmount(1); @@ -154,12 +162,15 @@ public class UserEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof UserUpdateMsg); userUpdateMsg = (UserUpdateMsg) latestMessage; + userMsg = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true); + Assert.assertNotNull(userMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, userUpdateMsg.getMsgType()); - Assert.assertEquals(savedCustomerUser.getLastName(), userUpdateMsg.getLastName()); + Assert.assertEquals(savedCustomerUser.getLastName(), userMsg.getLastName()); // update user credentials - edgeImitator.expectMessageAmount(1); login(savedCustomerUser.getEmail(), "customer"); + + edgeImitator.expectMessageAmount(1); ChangePasswordRequest changePasswordRequest = new ChangePasswordRequest(); changePasswordRequest.setCurrentPassword("customer"); changePasswordRequest.setNewPassword("newCustomer"); @@ -168,13 +179,15 @@ public class UserEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg); UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage; - Assert.assertEquals(savedCustomerUser.getUuidId().getMostSignificantBits(), userCredentialsUpdateMsg.getUserIdMSB()); - Assert.assertEquals(savedCustomerUser.getUuidId().getLeastSignificantBits(), userCredentialsUpdateMsg.getUserIdLSB()); - Assert.assertTrue(passwordEncoder.matches(changePasswordRequest.getNewPassword(), userCredentialsUpdateMsg.getPassword())); + UserCredentials userCredentialsMsg = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true); + Assert.assertNotNull(userCredentialsMsg); + Assert.assertEquals(savedCustomerUser.getId(), userCredentialsMsg.getUserId()); + Assert.assertTrue(passwordEncoder.matches(changePasswordRequest.getNewPassword(), userCredentialsMsg.getPassword())); + + loginTenantAdmin(); // delete user edgeImitator.expectMessageAmount(1); - login(tenantAdmin.getEmail(), "testPassword1"); doDelete("/api/user/" + savedCustomerUser.getUuidId()) .andExpect(status().isOk()); Assert.assertTrue(edgeImitator.waitForMessages()); @@ -191,8 +204,8 @@ public class UserEdgeTest extends AbstractEdgeTest { public void testSendUserCredentialsRequestToCloud() throws Exception { UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); UserCredentialsRequestMsg.Builder userCredentialsRequestMsgBuilder = UserCredentialsRequestMsg.newBuilder(); - userCredentialsRequestMsgBuilder.setUserIdMSB(tenantAdmin.getId().getId().getMostSignificantBits()); - userCredentialsRequestMsgBuilder.setUserIdLSB(tenantAdmin.getId().getId().getLeastSignificantBits()); + userCredentialsRequestMsgBuilder.setUserIdMSB(tenantAdminUserId.getId().getMostSignificantBits()); + userCredentialsRequestMsgBuilder.setUserIdLSB(tenantAdminUserId.getId().getLeastSignificantBits()); testAutoGeneratedCodeByProtobuf(userCredentialsRequestMsgBuilder); uplinkMsgBuilder.addUserCredentialsRequestMsg(userCredentialsRequestMsgBuilder.build()); @@ -207,16 +220,17 @@ public class UserEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg); UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage; - Assert.assertEquals(tenantAdmin.getId().getId().getMostSignificantBits(), userCredentialsUpdateMsg.getUserIdMSB()); - Assert.assertEquals(tenantAdmin.getId().getId().getLeastSignificantBits(), userCredentialsUpdateMsg.getUserIdLSB()); + UserCredentials userCredentialsMsg = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true); + Assert.assertNotNull(userCredentialsMsg); + Assert.assertEquals(tenantAdminUserId, userCredentialsMsg.getUserId()); } @Test public void sendUserCredentialsRequest() throws Exception { UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder(); UserCredentialsRequestMsg.Builder userCredentialsRequestMsgBuilder = UserCredentialsRequestMsg.newBuilder(); - userCredentialsRequestMsgBuilder.setUserIdMSB(tenantAdmin.getId().getId().getMostSignificantBits()); - userCredentialsRequestMsgBuilder.setUserIdLSB(tenantAdmin.getId().getId().getLeastSignificantBits()); + userCredentialsRequestMsgBuilder.setUserIdMSB(tenantAdminUserId.getId().getMostSignificantBits()); + userCredentialsRequestMsgBuilder.setUserIdLSB(tenantAdminUserId.getId().getLeastSignificantBits()); testAutoGeneratedCodeByProtobuf(userCredentialsRequestMsgBuilder); uplinkMsgBuilder.addUserCredentialsRequestMsg(userCredentialsRequestMsgBuilder.build()); @@ -231,8 +245,9 @@ public class UserEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg); UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage; - Assert.assertEquals(userCredentialsUpdateMsg.getUserIdMSB(), tenantAdmin.getId().getId().getMostSignificantBits()); - Assert.assertEquals(userCredentialsUpdateMsg.getUserIdLSB(), tenantAdmin.getId().getId().getLeastSignificantBits()); + UserCredentials userCredentialsMsg = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true); + Assert.assertNotNull(userCredentialsMsg); + Assert.assertEquals(tenantAdminUserId, userCredentialsMsg.getUserId()); testAutoGeneratedCodeByProtobuf(userCredentialsUpdateMsg); } diff --git a/application/src/test/java/org/thingsboard/server/edge/WidgetEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/WidgetEdgeTest.java index bfa87b194b..7195cbc2b5 100644 --- a/application/src/test/java/org/thingsboard/server/edge/WidgetEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/WidgetEdgeTest.java @@ -43,32 +43,32 @@ public class WidgetEdgeTest extends AbstractEdgeTest { AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof WidgetsBundleUpdateMsg); WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = (WidgetsBundleUpdateMsg) latestMessage; + WidgetsBundle widgetsBundleMsg = JacksonUtil.fromString(widgetsBundleUpdateMsg.getEntity(), WidgetsBundle.class, true); + Assert.assertNotNull(widgetsBundleMsg); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, widgetsBundleUpdateMsg.getMsgType()); - Assert.assertEquals(savedWidgetsBundle.getUuidId().getMostSignificantBits(), widgetsBundleUpdateMsg.getIdMSB()); - Assert.assertEquals(savedWidgetsBundle.getUuidId().getLeastSignificantBits(), widgetsBundleUpdateMsg.getIdLSB()); - Assert.assertEquals(savedWidgetsBundle.getAlias(), widgetsBundleUpdateMsg.getAlias()); - Assert.assertEquals(savedWidgetsBundle.getTitle(), widgetsBundleUpdateMsg.getTitle()); + Assert.assertEquals(savedWidgetsBundle.getId(), widgetsBundleMsg.getId()); + Assert.assertEquals(savedWidgetsBundle.getAlias(), widgetsBundleMsg.getAlias()); + Assert.assertEquals(savedWidgetsBundle.getTitle(), widgetsBundleMsg.getTitle()); testAutoGeneratedCodeByProtobuf(widgetsBundleUpdateMsg); // create widget type edgeImitator.expectMessageAmount(1); WidgetType widgetType = new WidgetType(); widgetType.setName("Test Widget Type"); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); ObjectNode descriptor = JacksonUtil.newObjectNode(); descriptor.put("key", "value"); widgetType.setDescriptor(descriptor); + widgetType.setDeprecated(true); + widgetType.setFqn("bundle_alias.type_alias"); WidgetType savedWidgetType = doPost("/api/widgetType", widgetType, WidgetType.class); Assert.assertTrue(edgeImitator.waitForMessages()); latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof WidgetTypeUpdateMsg); WidgetTypeUpdateMsg widgetTypeUpdateMsg = (WidgetTypeUpdateMsg) latestMessage; + WidgetType widgetsType = JacksonUtil.fromString(widgetTypeUpdateMsg.getEntity(), WidgetType.class, true); + Assert.assertNotNull(widgetsType); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, widgetTypeUpdateMsg.getMsgType()); - Assert.assertEquals(savedWidgetType.getUuidId().getMostSignificantBits(), widgetTypeUpdateMsg.getIdMSB()); - Assert.assertEquals(savedWidgetType.getUuidId().getLeastSignificantBits(), widgetTypeUpdateMsg.getIdLSB()); - Assert.assertEquals(savedWidgetType.getAlias(), widgetTypeUpdateMsg.getAlias()); - Assert.assertEquals(savedWidgetType.getName(), widgetTypeUpdateMsg.getName()); - Assert.assertEquals(JacksonUtil.toJsonNode(widgetTypeUpdateMsg.getDescriptorJson()), savedWidgetType.getDescriptor()); + Assert.assertEquals(savedWidgetType, widgetsType); // update widget bundle edgeImitator.expectMessageAmount(1); @@ -78,8 +78,10 @@ public class WidgetEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof WidgetsBundleUpdateMsg); widgetsBundleUpdateMsg = (WidgetsBundleUpdateMsg) latestMessage; + widgetsBundleMsg = JacksonUtil.fromString(widgetsBundleUpdateMsg.getEntity(), WidgetsBundle.class, true); + Assert.assertNotNull(widgetsBundleMsg); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, widgetsBundleUpdateMsg.getMsgType()); - Assert.assertEquals(savedWidgetsBundle.getTitle(), widgetsBundleUpdateMsg.getTitle()); + Assert.assertEquals(savedWidgetsBundle.getTitle(), widgetsBundleMsg.getTitle()); // update widget type edgeImitator.expectMessageAmount(1); @@ -89,8 +91,10 @@ public class WidgetEdgeTest extends AbstractEdgeTest { latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof WidgetTypeUpdateMsg); widgetTypeUpdateMsg = (WidgetTypeUpdateMsg) latestMessage; + widgetsType = JacksonUtil.fromString(widgetTypeUpdateMsg.getEntity(), WidgetType.class, true); + Assert.assertNotNull(widgetsType); Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, widgetTypeUpdateMsg.getMsgType()); - Assert.assertEquals(savedWidgetType.getName(), widgetTypeUpdateMsg.getName()); + Assert.assertEquals(savedWidgetType.getName(), widgetsType.getName()); // delete widget type edgeImitator.expectMessageAmount(1); diff --git a/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java b/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java index 8f05e6810f..9be0627414 100644 --- a/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java +++ b/application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java @@ -45,8 +45,11 @@ import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg; import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg; import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg; +import org.thingsboard.server.gen.edge.v1.ResourceUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg; +import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg; import org.thingsboard.server.gen.edge.v1.UplinkMsg; import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg; import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg; @@ -94,8 +97,6 @@ public class EdgeImitator { @Getter private UplinkResponseMsg latestResponseMsg; - private boolean connected = false; - public EdgeImitator(String host, int port, String routingKey, String routingSecret) throws NoSuchFieldException, IllegalAccessException { edgeRpcClient = new EdgeGrpcClient(); messagesLatch = new CountDownLatch(0); @@ -120,7 +121,6 @@ public class EdgeImitator { } public void connect() { - connected = true; edgeRpcClient.connect(routingKey, routingSecret, this::onUplinkResponse, this::onEdgeUpdate, @@ -131,7 +131,6 @@ public class EdgeImitator { } public void disconnect() throws InterruptedException { - connected = false; edgeRpcClient.disconnect(false); } @@ -175,22 +174,23 @@ public class EdgeImitator { } private ListenableFuture> processDownlinkMsg(DownlinkMsg downlinkMsg) { + log.trace("processDownlinkMsg: {}", downlinkMsg); List> result = new ArrayList<>(); if (downlinkMsg.getAdminSettingsUpdateMsgCount() > 0) { for (AdminSettingsUpdateMsg adminSettingsUpdateMsg : downlinkMsg.getAdminSettingsUpdateMsgList()) { result.add(saveDownlinkMsg(adminSettingsUpdateMsg)); } } - if (downlinkMsg.getDeviceUpdateMsgCount() > 0) { - for (DeviceUpdateMsg deviceUpdateMsg : downlinkMsg.getDeviceUpdateMsgList()) { - result.add(saveDownlinkMsg(deviceUpdateMsg)); - } - } if (downlinkMsg.getDeviceProfileUpdateMsgCount() > 0) { for (DeviceProfileUpdateMsg deviceProfileUpdateMsg : downlinkMsg.getDeviceProfileUpdateMsgList()) { result.add(saveDownlinkMsg(deviceProfileUpdateMsg)); } } + if (downlinkMsg.getDeviceUpdateMsgCount() > 0) { + for (DeviceUpdateMsg deviceUpdateMsg : downlinkMsg.getDeviceUpdateMsgList()) { + result.add(saveDownlinkMsg(deviceUpdateMsg)); + } + } if (downlinkMsg.getDeviceCredentialsUpdateMsgCount() > 0) { for (DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg : downlinkMsg.getDeviceCredentialsUpdateMsgList()) { result.add(saveDownlinkMsg(deviceCredentialsUpdateMsg)); @@ -294,9 +294,27 @@ public class EdgeImitator { result.add(saveDownlinkMsg(queueUpdateMsg)); } } + if (downlinkMsg.getTenantUpdateMsgCount() > 0) { + for (TenantUpdateMsg tenantUpdateMsg : downlinkMsg.getTenantUpdateMsgList()) { + result.add(saveDownlinkMsg(tenantUpdateMsg)); + } + } + if (downlinkMsg.getTenantProfileUpdateMsgCount() > 0) { + for (TenantProfileUpdateMsg tenantProfileUpdateMsg : downlinkMsg.getTenantProfileUpdateMsgList()) { + result.add(saveDownlinkMsg(tenantProfileUpdateMsg)); + } + } + if (downlinkMsg.getResourceUpdateMsgCount() > 0) { + for (ResourceUpdateMsg resourceUpdateMsg : downlinkMsg.getResourceUpdateMsgList()) { + result.add(saveDownlinkMsg(resourceUpdateMsg)); + } + } if (downlinkMsg.hasEdgeConfiguration()) { result.add(saveDownlinkMsg(downlinkMsg.getEdgeConfiguration())); } + if (downlinkMsg.hasSyncCompletedMsg()) { + result.add(saveDownlinkMsg(downlinkMsg.getSyncCompletedMsg())); + } return Futures.allAsList(result); } diff --git a/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java b/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java index 25b06e7840..4ea648119b 100644 --- a/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java @@ -18,31 +18,54 @@ package org.thingsboard.server.queue.discovery; import com.datastax.oss.driver.api.core.uuid.Uuids; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.ListUtils; +import org.apache.commons.lang3.RandomStringUtils; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.context.ApplicationEventPublisher; import org.springframework.test.util.ReflectionTestUtils; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.TenantProfileId; +import org.thingsboard.server.common.data.id.UUIDBased; +import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo; +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; import java.util.stream.Collectors; +import java.util.stream.Stream; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; @Slf4j @RunWith(MockitoJUnitRunner.class) @@ -56,8 +79,9 @@ public class HashPartitionServiceTest { private TenantRoutingInfoService routingInfoService; private ApplicationEventPublisher applicationEventPublisher; private QueueRoutingInfoService queueRoutingInfoService; + private TopicService topicService; - private String hashFunctionName = "sha256"; + private String hashFunctionName = "murmur3_128"; @Before public void setup() throws Exception { @@ -65,31 +89,23 @@ public class HashPartitionServiceTest { applicationEventPublisher = mock(ApplicationEventPublisher.class); routingInfoService = mock(TenantRoutingInfoService.class); queueRoutingInfoService = mock(QueueRoutingInfoService.class); - clusterRoutingService = new HashPartitionService(discoveryService, - routingInfoService, - applicationEventPublisher, - queueRoutingInfoService); - ReflectionTestUtils.setField(clusterRoutingService, "coreTopic", "tb.core"); - ReflectionTestUtils.setField(clusterRoutingService, "corePartitions", 10); - ReflectionTestUtils.setField(clusterRoutingService, "vcTopic", "tb.vc"); - ReflectionTestUtils.setField(clusterRoutingService, "vcPartitions", 10); - ReflectionTestUtils.setField(clusterRoutingService, "hashFunctionName", hashFunctionName); - TransportProtos.ServiceInfo currentServer = TransportProtos.ServiceInfo.newBuilder() + topicService = mock(TopicService.class); + when(topicService.buildTopicName(Mockito.any())).thenAnswer(i -> i.getArguments()[0]); + clusterRoutingService = createPartitionService(); + ServiceInfo currentServer = ServiceInfo.newBuilder() .setServiceId("tb-core-0") .addAllServiceTypes(Collections.singletonList(ServiceType.TB_CORE.name())) .build(); // when(queueService.resolve(Mockito.any(), Mockito.anyString())).thenAnswer(i -> i.getArguments()[1]); // when(discoveryService.getServiceInfo()).thenReturn(currentServer); - List otherServers = new ArrayList<>(); + List otherServers = new ArrayList<>(); for (int i = 1; i < SERVER_COUNT; i++) { - otherServers.add(TransportProtos.ServiceInfo.newBuilder() + otherServers.add(ServiceInfo.newBuilder() .setServiceId("tb-rule-" + i) .addAllServiceTypes(Collections.singletonList(ServiceType.TB_CORE.name())) .build()); } - clusterRoutingService.init(); - clusterRoutingService.partitionsInit(); clusterRoutingService.recalculatePartitions(currentServer, otherServers); } @@ -122,10 +138,10 @@ public class HashPartitionServiceTest { int queueCount = 3; int partitionCount = 3; - List services = new ArrayList<>(); + List services = new ArrayList<>(); for (int i = 0; i < serverCount; i++) { - services.add(TransportProtos.ServiceInfo.newBuilder().setServiceId("RE-" + i).build()); + services.add(ServiceInfo.newBuilder().setServiceId("RE-" + i).build()); } long start = System.currentTimeMillis(); @@ -140,7 +156,7 @@ public class HashPartitionServiceTest { for (int queueIndex = 0; queueIndex < queueCount; queueIndex++) { QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, "queue" + queueIndex, tenantId); for (int partition = 0; partition < partitionCount; partition++) { - TransportProtos.ServiceInfo serviceInfo = clusterRoutingService.resolveByPartitionIdx(services, queueKey, partition); + ServiceInfo serviceInfo = clusterRoutingService.resolveByPartitionIdx(services, queueKey, partition); String serviceId = serviceInfo.getServiceId(); map.put(serviceId, map.get(serviceId) + 1); } @@ -163,4 +179,264 @@ public class HashPartitionServiceTest { Assert.assertTrue(diffPercent < maxDiffPercent); } + @Test + public void testPartitionsAssignmentWithDedicatedServers() { + int isolatedProfilesCount = 5; + int tenantsCountPerProfile = 100; + int dedicatedServerSetsCount = 3; + int serversCountPerSet = 3; + int profilesPerSet = (int) Math.ceil((double) isolatedProfilesCount / dedicatedServerSetsCount); + + List isolatedTenantProfiles = Stream.generate(() -> new TenantProfileId(UUID.randomUUID())) + .limit(isolatedProfilesCount).collect(Collectors.toList()); + Map tenants = new HashMap<>(); + for (TenantProfileId tenantProfileId : isolatedTenantProfiles) { + for (int i = 0; i < tenantsCountPerProfile; i++) { + tenants.put(new TenantId(UUID.randomUUID()), tenantProfileId); + } + } + + List queues = new ArrayList<>(); + queues.add(createQueue(TenantId.SYS_TENANT_ID, 10)); + tenants.forEach((tenantId, profileId) -> { + queues.add(createQueue(tenantId, 2)); + mockRoutingInfo(tenantId, profileId, true); + }); + mockQueues(queues); + + List ruleEngines = new ArrayList<>(); + Map> dedicatedServers = new HashMap<>(); + int serviceId = 0; + for (int i = 0; i < serversCountPerSet; i++) { + ServiceInfo commonServer = ServiceInfo.newBuilder() + .setServiceId("tb-rule-engine-" + serviceId) + .addAllServiceTypes(List.of(ServiceType.TB_RULE_ENGINE.name())) + .build(); + ruleEngines.add(commonServer); + serviceId++; + } + for (int i = 0; i < dedicatedServerSetsCount; i++) { + List assignedProfiles = ListUtils.partition(isolatedTenantProfiles, profilesPerSet).get(i); + for (int j = 0; j < serversCountPerSet; j++) { + ServiceInfo dedicatedServer = ServiceInfo.newBuilder() + .setServiceId("tb-rule-engine-" + serviceId) + .addAllServiceTypes(List.of(ServiceType.TB_RULE_ENGINE.name())) + .addAllAssignedTenantProfiles(assignedProfiles.stream().map(UUIDBased::toString).collect(Collectors.toList())) + .build(); + ruleEngines.add(dedicatedServer); + serviceId++; + + for (TenantProfileId assignedProfileId : assignedProfiles) { + dedicatedServers.computeIfAbsent(assignedProfileId, p -> new ArrayList<>()).add(dedicatedServer); + } + } + } + + Map>> serversPartitions = new HashMap<>(); + clusterRoutingService.init(); + for (ServiceInfo ruleEngine : ruleEngines) { + List other = new ArrayList<>(ruleEngines); + other.removeIf(serviceInfo -> serviceInfo.getServiceId().equals(ruleEngine.getServiceId())); + + clusterRoutingService.recalculatePartitions(ruleEngine, other); + clusterRoutingService.myPartitions.forEach((queueKey, partitions) -> { + serversPartitions.computeIfAbsent(queueKey, k -> new HashMap<>()).put(ruleEngine, partitions); + }); + } + assertThat(serversPartitions.keySet()).containsAll(queues.stream().map(queue -> new QueueKey(ServiceType.TB_RULE_ENGINE, queue)).collect(Collectors.toList())); + + serversPartitions.forEach((queueKey, partitionsPerServer) -> { + if (queueKey.getTenantId().isSysTenantId()) { + partitionsPerServer.forEach((server, partitions) -> { + assertThat(server.getAssignedTenantProfilesCount()).as("system queues are not assigned to dedicated servers").isZero(); + }); + } else { + List responsibleServers = dedicatedServers.get(tenants.get(queueKey.getTenantId())); + partitionsPerServer.forEach((server, partitions) -> { + assertThat(server.getAssignedTenantProfilesCount()).as("isolated queues are only assigned to dedicated servers").isPositive(); + assertThat(responsibleServers).contains(server); + }); + } + + List allPartitions = partitionsPerServer.values().stream() + .flatMap(Collection::stream) + .collect(Collectors.toList()); + assertThat(allPartitions).doesNotHaveDuplicates(); + }); + } + + @Test + public void testPartitionChangeEvents_isolatedProfile_oneCommonServer_oneDedicated() { + ServiceInfo commonRuleEngine = ServiceInfo.newBuilder() + .setServiceId("tb-rule-engine-1") + .addAllServiceTypes(List.of(ServiceType.TB_RULE_ENGINE.name())) + .build(); + TenantProfileId tenantProfileId = new TenantProfileId(UUID.randomUUID()); + ServiceInfo dedicatedRuleEngine = ServiceInfo.newBuilder() + .setServiceId("tb-rule-engine-isolated-1") + .addAllServiceTypes(List.of(ServiceType.TB_RULE_ENGINE.name())) + .addAssignedTenantProfiles(tenantProfileId.toString()) + .build(); + + List queues = new ArrayList<>(); + Queue systemQueue = createQueue(TenantId.SYS_TENANT_ID, 10); + queues.add(systemQueue); + + TenantId tenantId = new TenantId(UUID.randomUUID()); + mockRoutingInfo(tenantId, tenantProfileId, false); // not isolated yet + mockQueues(queues); + + when(discoveryService.isService(eq(ServiceType.TB_RULE_ENGINE))).thenReturn(true); + Mockito.reset(applicationEventPublisher); + HashPartitionService partitionService_common = createPartitionService(); + partitionService_common.recalculatePartitions(commonRuleEngine, List.of(dedicatedRuleEngine)); + verifyPartitionChangeEvent(event -> { + QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, DataConstants.MAIN_QUEUE_NAME, TenantId.SYS_TENANT_ID); + return event.getPartitionsMap().get(queueKey).size() == systemQueue.getPartitions(); + }); + + Mockito.reset(applicationEventPublisher); + HashPartitionService partitionService_dedicated = createPartitionService(); + partitionService_dedicated.recalculatePartitions(dedicatedRuleEngine, List.of(commonRuleEngine)); + verify(applicationEventPublisher, never()).publishEvent(any(PartitionChangeEvent.class)); + + + Queue isolatedQueue = createQueue(tenantId, 3); + queues.add(isolatedQueue); + mockQueues(queues); + mockRoutingInfo(tenantId, tenantProfileId, true); // making isolated + TransportProtos.QueueUpdateMsg queueUpdateMsg = TransportProtos.QueueUpdateMsg.newBuilder() + .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) + .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) + .setQueueIdMSB(isolatedQueue.getUuidId().getMostSignificantBits()) + .setQueueIdLSB(isolatedQueue.getUuidId().getLeastSignificantBits()) + .setQueueName(isolatedQueue.getName()) + .setQueueTopic(isolatedQueue.getTopic()) + .setPartitions(isolatedQueue.getPartitions()) + .build(); + + partitionService_common.updateQueue(queueUpdateMsg); + partitionService_common.recalculatePartitions(commonRuleEngine, List.of(dedicatedRuleEngine)); + // expecting event about no partitions for isolated queue key + verifyPartitionChangeEvent(event -> { + QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, DataConstants.MAIN_QUEUE_NAME, tenantId); + return event.getPartitionsMap().get(queueKey).isEmpty(); + }); + + partitionService_dedicated.updateQueue(queueUpdateMsg); + partitionService_dedicated.recalculatePartitions(dedicatedRuleEngine, List.of(commonRuleEngine)); + verifyPartitionChangeEvent(event -> { + QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, DataConstants.MAIN_QUEUE_NAME, tenantId); + return event.getPartitionsMap().get(queueKey).size() == isolatedQueue.getPartitions(); + }); + + + queues = List.of(systemQueue); + mockQueues(queues); + mockRoutingInfo(tenantId, tenantProfileId, false); // turning off isolation + Mockito.reset(applicationEventPublisher); + TransportProtos.QueueDeleteMsg queueDeleteMsg = TransportProtos.QueueDeleteMsg.newBuilder() + .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) + .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) + .setQueueIdMSB(isolatedQueue.getUuidId().getMostSignificantBits()) + .setQueueIdLSB(isolatedQueue.getUuidId().getLeastSignificantBits()) + .setQueueName(isolatedQueue.getName()) + .build(); + partitionService_dedicated.removeQueue(queueDeleteMsg); + verifyPartitionChangeEvent(event -> { + QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, DataConstants.MAIN_QUEUE_NAME, tenantId); + return event.getPartitionsMap().get(queueKey).isEmpty(); + }); + } + + @Test + public void testIsManagedByCurrentServiceCheck() { + TenantProfileId isolatedProfileId = new TenantProfileId(UUID.randomUUID()); + when(discoveryService.getAssignedTenantProfiles()).thenReturn(Set.of(isolatedProfileId.getId())); // dedicated server + TenantProfileId regularProfileId = new TenantProfileId(UUID.randomUUID()); + + TenantId isolatedTenantId = new TenantId(UUID.randomUUID()); + mockRoutingInfo(isolatedTenantId, isolatedProfileId, true); + TenantId regularTenantId = new TenantId(UUID.randomUUID()); + mockRoutingInfo(regularTenantId, regularProfileId, false); + + assertThat(clusterRoutingService.isManagedByCurrentService(isolatedTenantId)).isTrue(); + assertThat(clusterRoutingService.isManagedByCurrentService(regularTenantId)).isFalse(); + + + when(discoveryService.getAssignedTenantProfiles()).thenReturn(Collections.emptySet()); // common server + + assertThat(clusterRoutingService.isManagedByCurrentService(isolatedTenantId)).isTrue(); + assertThat(clusterRoutingService.isManagedByCurrentService(regularTenantId)).isTrue(); + } + + @Test + public void testPartitionsDistribution_sameTenantDifferentQueues() { + List ruleEngines = new ArrayList<>(); + int serviceId = 0; + for (int i = 0; i < 5; i++) { + ServiceInfo commonServer = ServiceInfo.newBuilder() + .setServiceId("tb-rule-engine-" + serviceId) + .addAllServiceTypes(List.of(ServiceType.TB_RULE_ENGINE.name())) + .build(); + ruleEngines.add(commonServer); + serviceId++; + } + + Stream.concat(Stream.of(TenantId.SYS_TENANT_ID), Stream.generate(UUID::randomUUID).map(TenantId::new).limit(10)).forEach(tenantId -> { + List queues = Stream.generate(() -> RandomStringUtils.randomAlphabetic(10)) + .map(queueName -> new QueueKey(ServiceType.TB_RULE_ENGINE, queueName, tenantId)) + .limit(100).collect(Collectors.toList()); + + for (int partition = 0; partition < 10; partition++) { + ServiceInfo expectedAssignedRuleEngine = clusterRoutingService.resolveByPartitionIdx(ruleEngines, new QueueKey(ServiceType.TB_RULE_ENGINE, tenantId), partition); + for (QueueKey queueKey : queues) { + ServiceInfo assignedRuleEngine = clusterRoutingService.resolveByPartitionIdx(ruleEngines, queueKey, partition); + assertThat(assignedRuleEngine).as(queueKey + "[" + partition + "] should be assigned to " + expectedAssignedRuleEngine.getServiceId()) + .isEqualTo(expectedAssignedRuleEngine); + } + } + }); + } + + private void verifyPartitionChangeEvent(Predicate predicate) { + verify(applicationEventPublisher).publishEvent(argThat(event -> event instanceof PartitionChangeEvent && predicate.test((PartitionChangeEvent) event))); + } + + private void mockRoutingInfo(TenantId tenantId, TenantProfileId tenantProfileId, boolean isolatedTbRuleEngine) { + when(routingInfoService.getRoutingInfo(eq(tenantId))) + .thenReturn(new TenantRoutingInfo(tenantId, tenantProfileId, isolatedTbRuleEngine)); + } + + private void mockQueues(List queues) { + when(queueRoutingInfoService.getAllQueuesRoutingInfo()).thenReturn(queues.stream() + .map(QueueRoutingInfo::new).collect(Collectors.toList())); + } + + private Queue createQueue(TenantId tenantId, int partitions) { + Queue systemQueue = new Queue(); + systemQueue.setTenantId(tenantId); + systemQueue.setName("Main"); + systemQueue.setTopic(DataConstants.MAIN_QUEUE_TOPIC); + systemQueue.setPartitions(partitions); + systemQueue.setId(new QueueId(UUID.randomUUID())); + return systemQueue; + } + + private HashPartitionService createPartitionService() { + HashPartitionService partitionService = new HashPartitionService(discoveryService, + routingInfoService, + applicationEventPublisher, + queueRoutingInfoService, + topicService); + ReflectionTestUtils.setField(partitionService, "coreTopic", "tb.core"); + ReflectionTestUtils.setField(partitionService, "corePartitions", 10); + ReflectionTestUtils.setField(partitionService, "vcTopic", "tb.vc"); + ReflectionTestUtils.setField(partitionService, "vcPartitions", 10); + ReflectionTestUtils.setField(partitionService, "hashFunctionName", hashFunctionName); + partitionService.init(); + partitionService.partitionsInit(); + return partitionService; + } + } diff --git a/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java b/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java index ef94069960..b86287c5d7 100644 --- a/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java @@ -27,6 +27,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.util.ReflectionTestUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration; +import org.thingsboard.rule.engine.util.TbMsgSource; +import org.thingsboard.rule.engine.metadata.TbGetAttributesNode; import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.common.data.DataConstants; @@ -37,6 +39,8 @@ import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.event.Event; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.rule.NodeConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChain; @@ -137,23 +141,27 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule RuleNode ruleNode1 = new RuleNode(); ruleNode1.setName("Simple Rule Node 1"); ruleNode1.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode1.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); ruleNode1.setDebugMode(true); TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration(); + configuration1.setFetchTo(TbMsgSource.METADATA); configuration1.setServerAttributeNames(Collections.singletonList("serverAttributeKey1")); ruleNode1.setConfiguration(JacksonUtil.valueToTree(configuration1)); RuleNode ruleNode2 = new RuleNode(); ruleNode2.setName("Simple Rule Node 2"); ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode2.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); ruleNode2.setDebugMode(true); TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration(); + configuration2.setFetchTo(TbMsgSource.METADATA); configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2")); ruleNode2.setConfiguration(JacksonUtil.valueToTree(configuration2)); metaData.setNodes(Arrays.asList(ruleNode1, ruleNode2)); metaData.setFirstNodeIndex(0); - metaData.addConnectionInfo(0, 1, "Success"); + metaData.addConnectionInfo(0, 1, TbNodeConnectionType.SUCCESS); metaData = saveRuleChainMetaData(metaData); Assert.assertNotNull(metaData); @@ -173,14 +181,14 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class); Mockito.when(tbMsgCallback.isMsgValid()).thenReturn(true); - TbMsg tbMsg = TbMsg.newMsg("CUSTOM", device.getId(), new TbMsgMetaData(), "{}", tbMsgCallback); + TbMsg tbMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, device.getId(), TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT, tbMsgCallback); QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg, null, null); // Pushing Message to the system actorSystem.tell(qMsg); Mockito.verify(tbMsgCallback, Mockito.timeout(10000)).onSuccess(); PageData eventsPage = getDebugEvents(savedTenant.getId(), ruleChain.getFirstRuleNodeId(), 1000); - List events = eventsPage.getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList()); + List events = eventsPage.getData().stream().filter(filterByPostTelemetryEventType()).collect(Collectors.toList()); Assert.assertEquals(2, events.size()); EventInfo inEvent = events.stream().filter(e -> e.getBody().get("type").asText().equals(DataConstants.IN)).findFirst().get(); @@ -197,7 +205,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule RuleNode lastRuleNode = metaData.getNodes().stream().filter(node -> !node.getId().equals(finalRuleChain.getFirstRuleNodeId())).findFirst().get(); eventsPage = getDebugEvents(savedTenant.getId(), lastRuleNode.getId(), 1000); - events = eventsPage.getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList()); + events = eventsPage.getData().stream().filter(filterByPostTelemetryEventType()).collect(Collectors.toList()); Assert.assertEquals(2, events.size()); @@ -239,8 +247,10 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule RuleNode ruleNode1 = new RuleNode(); ruleNode1.setName("Simple Rule Node 1"); ruleNode1.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode1.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); ruleNode1.setDebugMode(true); TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration(); + configuration1.setFetchTo(TbMsgSource.METADATA); configuration1.setServerAttributeNames(Collections.singletonList("serverAttributeKey1")); ruleNode1.setConfiguration(JacksonUtil.valueToTree(configuration1)); @@ -257,7 +267,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule NodeConnectionInfo connection = new NodeConnectionInfo(); connection.setFromIndex(0); connection.setToIndex(1); - connection.setType("Success"); + connection.setType(TbNodeConnectionType.SUCCESS); rootMetaData.setConnections(Collections.singletonList(connection)); rootMetaData = saveRuleChainMetaData(rootMetaData); Assert.assertNotNull(rootMetaData); @@ -271,8 +281,10 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule RuleNode ruleNode2 = new RuleNode(); ruleNode2.setName("Simple Rule Node 2"); ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode2.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); ruleNode2.setDebugMode(true); TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration(); + configuration2.setFetchTo(TbMsgSource.METADATA); configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2")); ruleNode2.setConfiguration(JacksonUtil.valueToTree(configuration2)); @@ -294,7 +306,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class); Mockito.when(tbMsgCallback.isMsgValid()).thenReturn(true); - TbMsg tbMsg = TbMsg.newMsg("CUSTOM", device.getId(), new TbMsgMetaData(), "{}", tbMsgCallback); + TbMsg tbMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, device.getId(), TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT, tbMsgCallback); QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg, null, null); // Pushing Message to the system actorSystem.tell(qMsg); @@ -302,7 +314,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule Mockito.verify(tbMsgCallback, Mockito.timeout(10000)).onSuccess(); PageData eventsPage = getDebugEvents(savedTenant.getId(), rootRuleChain.getFirstRuleNodeId(), 1000); - List events = eventsPage.getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList()); + List events = eventsPage.getData().stream().filter(filterByPostTelemetryEventType()).collect(Collectors.toList()); Assert.assertEquals(2, events.size()); @@ -320,7 +332,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule RuleNode lastRuleNode = secondaryMetaData.getNodes().stream().filter(node -> !node.getId().equals(finalRuleChain.getFirstRuleNodeId())).findFirst().get(); eventsPage = getDebugEvents(savedTenant.getId(), lastRuleNode.getId(), 1000); - events = eventsPage.getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList()); + events = eventsPage.getData().stream().filter(filterByPostTelemetryEventType()).collect(Collectors.toList()); Assert.assertEquals(2, events.size()); diff --git a/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java b/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java index 96a5e32d95..9b05be4a05 100644 --- a/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java @@ -24,6 +24,8 @@ import org.junit.Test; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.util.TbMsgSource; +import org.thingsboard.rule.engine.metadata.TbGetAttributesNode; import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.common.data.DataConstants; @@ -32,6 +34,7 @@ import org.thingsboard.server.common.data.EventInfo; import org.thingsboard.server.common.data.event.EventType; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleNode; @@ -92,8 +95,10 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac RuleNode ruleNode = new RuleNode(); ruleNode.setName("Simple Rule Node"); ruleNode.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); ruleNode.setDebugMode(true); TbGetAttributesNodeConfiguration configuration = new TbGetAttributesNodeConfiguration(); + configuration.setFetchTo(TbMsgSource.METADATA); configuration.setServerAttributeNames(Collections.singletonList("serverAttributeKey")); ruleNode.setConfiguration(JacksonUtil.valueToTree(configuration)); @@ -135,7 +140,7 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac log.warn("attr updated"); TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class); Mockito.when(tbMsgCallback.isMsgValid()).thenReturn(true); - TbMsg tbMsg = TbMsg.newMsg("CUSTOM", device.getId(), new TbMsgMetaData(), "{}", tbMsgCallback); + TbMsg tbMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, device.getId(), TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT, tbMsgCallback); QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(tenantId, tbMsg, null, null); // Pushing Message to the system log.warn("before tell tbMsgCallback"); @@ -143,12 +148,12 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac log.warn("awaiting tbMsgCallback"); Mockito.verify(tbMsgCallback, Mockito.timeout(TimeUnit.SECONDS.toMillis(TIMEOUT))).onSuccess(); log.warn("awaiting events"); - List events = Awaitility.await("get debug by custom event") + List events = Awaitility.await("get debug by post telemetry event") .pollInterval(10, MILLISECONDS) .atMost(TIMEOUT, TimeUnit.SECONDS) .until(() -> { List debugEvents = getDebugEvents(tenantId, ruleChainFinal.getFirstRuleNodeId(), 1000) - .getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList()); + .getData().stream().filter(filterByPostTelemetryEventType()).collect(Collectors.toList()); log.warn("filtered debug events [{}]", debugEvents.size()); debugEvents.forEach((e) -> log.warn("event: {}", e)); return debugEvents; diff --git a/application/src/test/java/org/thingsboard/server/service/device/provision/DeviceProvisionServiceTest.java b/application/src/test/java/org/thingsboard/server/service/device/provision/DeviceProvisionServiceTest.java index f01f4fae04..a88f25d5bf 100644 --- a/application/src/test/java/org/thingsboard/server/service/device/provision/DeviceProvisionServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/device/provision/DeviceProvisionServiceTest.java @@ -71,8 +71,6 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -; - @Slf4j @RunWith(SpringRunner.class) @ContextConfiguration(classes = DeviceProvisionServiceImpl.class) diff --git a/application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java b/application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java index dc3cc84c72..c7a3e7a24c 100644 --- a/application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java +++ b/application/src/test/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructorTest.java @@ -27,6 +27,8 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.rule.NodeConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleNode; @@ -35,6 +37,7 @@ import org.thingsboard.server.gen.edge.v1.RuleChainConnectionInfoProto; import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.v1.RuleNodeProto; import org.thingsboard.server.gen.edge.v1.UpdateMsgType; +import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMsgConstructorV1; import java.util.ArrayList; import java.util.List; @@ -45,13 +48,15 @@ import java.util.UUID; @RunWith(MockitoJUnitRunner.class) public class RuleChainMsgConstructorTest { - private RuleChainMsgConstructor constructor; + private static final String RPC_CONNECTION_TYPE = "RPC"; + + private RuleChainMsgConstructorV1 ruleChainMsgConstructorV1; private TenantId tenantId; @Before public void setup() { - constructor = new RuleChainMsgConstructor(); + ruleChainMsgConstructorV1 = new RuleChainMsgConstructorV1(); tenantId = new TenantId(UUID.randomUUID()); } @@ -61,7 +66,7 @@ public class RuleChainMsgConstructorTest { RuleChainMetaData ruleChainMetaData = createRuleChainMetaData( ruleChainId, 3, createRuleNodes(ruleChainId), createConnections()); RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = - constructor.constructRuleChainMetadataUpdatedMsg( + ruleChainMsgConstructorV1.constructRuleChainMetadataUpdatedMsg( tenantId, UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, ruleChainMetaData, @@ -80,7 +85,7 @@ public class RuleChainMsgConstructorTest { RuleChainMetaData ruleChainMetaData = createRuleChainMetaData( ruleChainId, 3, createRuleNodes(ruleChainId), createConnections()); RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = - constructor.constructRuleChainMetadataUpdatedMsg( + ruleChainMsgConstructorV1.constructRuleChainMetadataUpdatedMsg( tenantId, UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, ruleChainMetaData, @@ -99,19 +104,19 @@ public class RuleChainMsgConstructorTest { Assert.assertEquals("Connections count incorrect!", 13, ruleChainMetadataUpdateMsg.getConnectionsCount()); Assert.assertEquals("Rule chain connections count incorrect!", 0, ruleChainMetadataUpdateMsg.getRuleChainConnectionsCount()); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(3, 6, "Success"), ruleChainMetadataUpdateMsg.getConnections(0)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(3, 10, "Success"), ruleChainMetadataUpdateMsg.getConnections(1)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(3, 0, "Success"), ruleChainMetadataUpdateMsg.getConnections(2)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 11, "Success"), ruleChainMetadataUpdateMsg.getConnections(3)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 11, "Success"), ruleChainMetadataUpdateMsg.getConnections(4)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 11, "Attributes Updated"), ruleChainMetadataUpdateMsg.getConnections(5)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 7, "RPC Request from Device"), ruleChainMetadataUpdateMsg.getConnections(6)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 4, "Post telemetry"), ruleChainMetadataUpdateMsg.getConnections(7)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 5, "Post attributes"), ruleChainMetadataUpdateMsg.getConnections(8)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 8, "Other"), ruleChainMetadataUpdateMsg.getConnections(9)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 9, "RPC Request to Device"), ruleChainMetadataUpdateMsg.getConnections(10)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(7, 11, "Success"), ruleChainMetadataUpdateMsg.getConnections(11)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(10, 9, "RPC"), ruleChainMetadataUpdateMsg.getConnections(12)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(3, 6, TbNodeConnectionType.SUCCESS), ruleChainMetadataUpdateMsg.getConnections(0)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(3, 10, TbNodeConnectionType.SUCCESS), ruleChainMetadataUpdateMsg.getConnections(1)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(3, 0, TbNodeConnectionType.SUCCESS), ruleChainMetadataUpdateMsg.getConnections(2)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 11, TbNodeConnectionType.SUCCESS), ruleChainMetadataUpdateMsg.getConnections(3)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 11, TbNodeConnectionType.SUCCESS), ruleChainMetadataUpdateMsg.getConnections(4)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 11, TbMsgType.ATTRIBUTES_UPDATED.getRuleNodeConnection()), ruleChainMetadataUpdateMsg.getConnections(5)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 7, TbMsgType.TO_SERVER_RPC_REQUEST.getRuleNodeConnection()), ruleChainMetadataUpdateMsg.getConnections(6)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 4, TbMsgType.POST_TELEMETRY_REQUEST.getRuleNodeConnection()), ruleChainMetadataUpdateMsg.getConnections(7)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 5, TbMsgType.POST_ATTRIBUTES_REQUEST.getRuleNodeConnection()), ruleChainMetadataUpdateMsg.getConnections(8)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 8, TbNodeConnectionType.OTHER), ruleChainMetadataUpdateMsg.getConnections(9)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 9, TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE.getRuleNodeConnection()), ruleChainMetadataUpdateMsg.getConnections(10)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(7, 11, TbNodeConnectionType.SUCCESS), ruleChainMetadataUpdateMsg.getConnections(11)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(10, 9, RPC_CONNECTION_TYPE), ruleChainMetadataUpdateMsg.getConnections(12)); } @Test @@ -119,7 +124,7 @@ public class RuleChainMsgConstructorTest { RuleChainId ruleChainId = new RuleChainId(UUID.randomUUID()); RuleChainMetaData ruleChainMetaData = createRuleChainMetaData(ruleChainId, 3, createRuleNodes(ruleChainId), createConnections()); RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = - constructor.constructRuleChainMetadataUpdatedMsg( + ruleChainMsgConstructorV1.constructRuleChainMetadataUpdatedMsg( tenantId, UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, ruleChainMetaData, @@ -130,20 +135,20 @@ public class RuleChainMsgConstructorTest { Assert.assertEquals("Connections count incorrect!", 10, ruleChainMetadataUpdateMsg.getConnectionsCount()); Assert.assertEquals("Rule chain connections count incorrect!", 1, ruleChainMetadataUpdateMsg.getRuleChainConnectionsCount()); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(2, 5, "Success"), ruleChainMetadataUpdateMsg.getConnections(0)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(3, 9, "Success"), ruleChainMetadataUpdateMsg.getConnections(1)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 9, "Success"), ruleChainMetadataUpdateMsg.getConnections(2)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 9, "Attributes Updated"), ruleChainMetadataUpdateMsg.getConnections(3)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 6, "RPC Request from Device"), ruleChainMetadataUpdateMsg.getConnections(4)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 3, "Post telemetry"), ruleChainMetadataUpdateMsg.getConnections(5)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 4, "Post attributes"), ruleChainMetadataUpdateMsg.getConnections(6)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 7, "Other"), ruleChainMetadataUpdateMsg.getConnections(7)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 8, "RPC Request to Device"), ruleChainMetadataUpdateMsg.getConnections(8)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 9, "Success"), ruleChainMetadataUpdateMsg.getConnections(9)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(2, 5, TbNodeConnectionType.SUCCESS), ruleChainMetadataUpdateMsg.getConnections(0)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(3, 9, TbNodeConnectionType.SUCCESS), ruleChainMetadataUpdateMsg.getConnections(1)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 9, TbNodeConnectionType.SUCCESS), ruleChainMetadataUpdateMsg.getConnections(2)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 9, TbMsgType.ATTRIBUTES_UPDATED.getRuleNodeConnection()), ruleChainMetadataUpdateMsg.getConnections(3)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 6, TbMsgType.TO_SERVER_RPC_REQUEST.getRuleNodeConnection()), ruleChainMetadataUpdateMsg.getConnections(4)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 3, TbMsgType.POST_TELEMETRY_REQUEST.getRuleNodeConnection()), ruleChainMetadataUpdateMsg.getConnections(5)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 4, TbMsgType.POST_ATTRIBUTES_REQUEST.getRuleNodeConnection()), ruleChainMetadataUpdateMsg.getConnections(6)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 7, TbNodeConnectionType.OTHER), ruleChainMetadataUpdateMsg.getConnections(7)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 8, TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE.getRuleNodeConnection()), ruleChainMetadataUpdateMsg.getConnections(8)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 9, TbNodeConnectionType.SUCCESS), ruleChainMetadataUpdateMsg.getConnections(9)); RuleChainConnectionInfoProto ruleChainConnection = ruleChainMetadataUpdateMsg.getRuleChainConnections(0); Assert.assertEquals("From index incorrect!", 2, ruleChainConnection.getFromIndex()); - Assert.assertEquals("Type index incorrect!", "Success", ruleChainConnection.getType()); + Assert.assertEquals("Type index incorrect!", TbNodeConnectionType.SUCCESS, ruleChainConnection.getType()); Assert.assertEquals("Additional info incorrect!", "{\"description\":\"\",\"layoutX\":477,\"layoutY\":560,\"ruleChainNodeId\":\"rule-chain-node-UNDEFINED\"}", ruleChainConnection.getAdditionalInfo()); @@ -161,7 +166,7 @@ public class RuleChainMsgConstructorTest { RuleChainId ruleChainId = new RuleChainId(UUID.randomUUID()); RuleChainMetaData ruleChainMetaData1 = createRuleChainMetaData(ruleChainId, 8, createRuleNodesInDifferentOrder(ruleChainId), createConnectionsInDifferentOrder()); RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = - constructor.constructRuleChainMetadataUpdatedMsg( + ruleChainMsgConstructorV1.constructRuleChainMetadataUpdatedMsg( tenantId, UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, ruleChainMetaData1, @@ -172,20 +177,20 @@ public class RuleChainMsgConstructorTest { Assert.assertEquals("Connections count incorrect!", 10, ruleChainMetadataUpdateMsg.getConnectionsCount()); Assert.assertEquals("Rule chain connections count incorrect!", 1, ruleChainMetadataUpdateMsg.getRuleChainConnectionsCount()); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(3, 0, "Success"), ruleChainMetadataUpdateMsg.getConnections(0)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 0, "Attributes Updated"), ruleChainMetadataUpdateMsg.getConnections(1)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 3, "RPC Request from Device"), ruleChainMetadataUpdateMsg.getConnections(2)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 6, "Post telemetry"), ruleChainMetadataUpdateMsg.getConnections(3)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 5, "Post attributes"), ruleChainMetadataUpdateMsg.getConnections(4)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 2, "Other"), ruleChainMetadataUpdateMsg.getConnections(5)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 1, "RPC Request to Device"), ruleChainMetadataUpdateMsg.getConnections(6)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 0, "Success"), ruleChainMetadataUpdateMsg.getConnections(7)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 0, "Success"), ruleChainMetadataUpdateMsg.getConnections(8)); - compareNodeConnectionInfoAndProto(createNodeConnectionInfo(7, 4, "Success"), ruleChainMetadataUpdateMsg.getConnections(9)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(3, 0, TbNodeConnectionType.SUCCESS), ruleChainMetadataUpdateMsg.getConnections(0)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 0, TbMsgType.ATTRIBUTES_UPDATED.getRuleNodeConnection()), ruleChainMetadataUpdateMsg.getConnections(1)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 3, TbMsgType.TO_SERVER_RPC_REQUEST.getRuleNodeConnection()), ruleChainMetadataUpdateMsg.getConnections(2)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 6, TbMsgType.POST_TELEMETRY_REQUEST.getRuleNodeConnection()), ruleChainMetadataUpdateMsg.getConnections(3)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 5, TbMsgType.POST_ATTRIBUTES_REQUEST.getRuleNodeConnection()), ruleChainMetadataUpdateMsg.getConnections(4)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 2, TbNodeConnectionType.OTHER), ruleChainMetadataUpdateMsg.getConnections(5)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(4, 1, TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE.getRuleNodeConnection()), ruleChainMetadataUpdateMsg.getConnections(6)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(5, 0, TbNodeConnectionType.SUCCESS), ruleChainMetadataUpdateMsg.getConnections(7)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(6, 0, TbNodeConnectionType.SUCCESS), ruleChainMetadataUpdateMsg.getConnections(8)); + compareNodeConnectionInfoAndProto(createNodeConnectionInfo(7, 4, TbNodeConnectionType.SUCCESS), ruleChainMetadataUpdateMsg.getConnections(9)); RuleChainConnectionInfoProto ruleChainConnection = ruleChainMetadataUpdateMsg.getRuleChainConnections(0); Assert.assertEquals("From index incorrect!", 7, ruleChainConnection.getFromIndex()); - Assert.assertEquals("Type index incorrect!", "Success", ruleChainConnection.getType()); + Assert.assertEquals("Type index incorrect!", TbNodeConnectionType.SUCCESS, ruleChainConnection.getType()); Assert.assertEquals("Additional info incorrect!", "{\"description\":\"\",\"layoutX\":477,\"layoutY\":560,\"ruleChainNodeId\":\"rule-chain-node-UNDEFINED\"}", ruleChainConnection.getAdditionalInfo()); @@ -225,19 +230,19 @@ public class RuleChainMsgConstructorTest { private List createConnections() { List result = new ArrayList<>(); - result.add(createNodeConnectionInfo(3, 6, "Success")); - result.add(createNodeConnectionInfo(3, 10, "Success")); - result.add(createNodeConnectionInfo(3, 0, "Success")); - result.add(createNodeConnectionInfo(4, 11, "Success")); - result.add(createNodeConnectionInfo(5, 11, "Success")); - result.add(createNodeConnectionInfo(6, 11, "Attributes Updated")); - result.add(createNodeConnectionInfo(6, 7, "RPC Request from Device")); - result.add(createNodeConnectionInfo(6, 4, "Post telemetry")); - result.add(createNodeConnectionInfo(6, 5, "Post attributes")); - result.add(createNodeConnectionInfo(6, 8, "Other")); - result.add(createNodeConnectionInfo(6, 9, "RPC Request to Device")); - result.add(createNodeConnectionInfo(7, 11, "Success")); - result.add(createNodeConnectionInfo(10, 9, "RPC")); + result.add(createNodeConnectionInfo(3, 6, TbNodeConnectionType.SUCCESS)); + result.add(createNodeConnectionInfo(3, 10, TbNodeConnectionType.SUCCESS)); + result.add(createNodeConnectionInfo(3, 0, TbNodeConnectionType.SUCCESS)); + result.add(createNodeConnectionInfo(4, 11, TbNodeConnectionType.SUCCESS)); + result.add(createNodeConnectionInfo(5, 11, TbNodeConnectionType.SUCCESS)); + result.add(createNodeConnectionInfo(6, 11, TbMsgType.ATTRIBUTES_UPDATED.getRuleNodeConnection())); + result.add(createNodeConnectionInfo(6, 7, TbMsgType.TO_SERVER_RPC_REQUEST.getRuleNodeConnection())); + result.add(createNodeConnectionInfo(6, 4, TbMsgType.POST_TELEMETRY_REQUEST.getRuleNodeConnection())); + result.add(createNodeConnectionInfo(6, 5, TbMsgType.POST_ATTRIBUTES_REQUEST.getRuleNodeConnection())); + result.add(createNodeConnectionInfo(6, 8, TbNodeConnectionType.OTHER)); + result.add(createNodeConnectionInfo(6, 9, TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE.getRuleNodeConnection())); + result.add(createNodeConnectionInfo(7, 11, TbNodeConnectionType.SUCCESS)); + result.add(createNodeConnectionInfo(10, 9, RPC_CONNECTION_TYPE)); return result; } @@ -280,19 +285,19 @@ public class RuleChainMsgConstructorTest { private List createConnectionsInDifferentOrder() { List result = new ArrayList<>(); - result.add(createNodeConnectionInfo(0, 2, "RPC")); - result.add(createNodeConnectionInfo(4, 1, "Success")); - result.add(createNodeConnectionInfo(5, 1, "Attributes Updated")); - result.add(createNodeConnectionInfo(5, 4, "RPC Request from Device")); - result.add(createNodeConnectionInfo(5, 7, "Post telemetry")); - result.add(createNodeConnectionInfo(5, 6, "Post attributes")); - result.add(createNodeConnectionInfo(5, 3, "Other")); - result.add(createNodeConnectionInfo(5, 2, "RPC Request to Device")); - result.add(createNodeConnectionInfo(6, 1, "Success")); - result.add(createNodeConnectionInfo(7, 1, "Success")); - result.add(createNodeConnectionInfo(8, 11, "Success")); - result.add(createNodeConnectionInfo(8, 5, "Success")); - result.add(createNodeConnectionInfo(8, 0, "Success")); + result.add(createNodeConnectionInfo(0, 2, RPC_CONNECTION_TYPE)); + result.add(createNodeConnectionInfo(4, 1, TbNodeConnectionType.SUCCESS)); + result.add(createNodeConnectionInfo(5, 1, TbMsgType.ATTRIBUTES_UPDATED.getRuleNodeConnection())); + result.add(createNodeConnectionInfo(5, 4, TbMsgType.TO_SERVER_RPC_REQUEST.getRuleNodeConnection())); + result.add(createNodeConnectionInfo(5, 7, TbMsgType.POST_TELEMETRY_REQUEST.getRuleNodeConnection())); + result.add(createNodeConnectionInfo(5, 6, TbMsgType.POST_ATTRIBUTES_REQUEST.getRuleNodeConnection())); + result.add(createNodeConnectionInfo(5, 3, TbNodeConnectionType.OTHER)); + result.add(createNodeConnectionInfo(5, 2, TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE.getRuleNodeConnection())); + result.add(createNodeConnectionInfo(6, 1, TbNodeConnectionType.SUCCESS)); + result.add(createNodeConnectionInfo(7, 1, TbNodeConnectionType.SUCCESS)); + result.add(createNodeConnectionInfo(8, 11, TbNodeConnectionType.SUCCESS)); + result.add(createNodeConnectionInfo(8, 5, TbNodeConnectionType.SUCCESS)); + result.add(createNodeConnectionInfo(8, 0, TbNodeConnectionType.SUCCESS)); return result; } diff --git a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java new file mode 100644 index 0000000000..b0b9aaea7f --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java @@ -0,0 +1,526 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor; + +import org.junit.jupiter.params.provider.Arguments; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.context.annotation.Lazy; +import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.alarm.AlarmService; +import org.thingsboard.server.dao.asset.AssetProfileService; +import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.customer.CustomerService; +import org.thingsboard.server.dao.dashboard.DashboardService; +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.edge.EdgeEventService; +import org.thingsboard.server.dao.edge.EdgeService; +import org.thingsboard.server.dao.edge.EdgeSynchronizationManager; +import org.thingsboard.server.dao.entityview.EntityViewService; +import org.thingsboard.server.dao.ota.OtaPackageService; +import org.thingsboard.server.dao.queue.QueueService; +import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.resource.ImageService; +import org.thingsboard.server.dao.resource.ResourceService; +import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TenantProfileService; +import org.thingsboard.server.dao.tenant.TenantService; +import org.thingsboard.server.dao.user.UserService; +import org.thingsboard.server.dao.widget.WidgetTypeService; +import org.thingsboard.server.dao.widget.WidgetsBundleService; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.queue.provider.TbQueueProducerProvider; +import org.thingsboard.server.queue.util.DataDecodingEncodingService; +import org.thingsboard.server.service.edge.rpc.constructor.alarm.AlarmMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.alarm.AlarmMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.alarm.AlarmMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.asset.AssetMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.asset.AssetMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.asset.AssetMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.customer.CustomerMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.customer.CustomerMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.customer.CustomerMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.dashboard.DashboardMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.dashboard.DashboardMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.dashboard.DashboardMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.device.DeviceMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.device.DeviceMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.device.DeviceMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.edge.EdgeMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.entityview.EntityViewMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.entityview.EntityViewMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.entityview.EntityViewMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.ota.OtaPackageMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.ota.OtaPackageMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.ota.OtaPackageMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.queue.QueueMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.queue.QueueMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.queue.QueueMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.relation.RelationMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.relation.RelationMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.relation.RelationMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.resource.ResourceMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.resource.ResourceMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.resource.ResourceMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.rule.RuleChainMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.settings.AdminSettingsMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.settings.AdminSettingsMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.settings.AdminSettingsMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.telemetry.EntityDataMsgConstructor; +import org.thingsboard.server.service.edge.rpc.constructor.tenant.TenantMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.tenant.TenantMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.tenant.TenantMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.user.UserMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.user.UserMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.user.UserMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.constructor.widget.WidgetMsgConstructorFactory; +import org.thingsboard.server.service.edge.rpc.constructor.widget.WidgetMsgConstructorV1; +import org.thingsboard.server.service.edge.rpc.constructor.widget.WidgetMsgConstructorV2; +import org.thingsboard.server.service.edge.rpc.processor.alarm.AlarmEdgeProcessorFactory; +import org.thingsboard.server.service.edge.rpc.processor.alarm.AlarmEdgeProcessorV1; +import org.thingsboard.server.service.edge.rpc.processor.alarm.AlarmEdgeProcessorV2; +import org.thingsboard.server.service.edge.rpc.processor.asset.AssetEdgeProcessorFactory; +import org.thingsboard.server.service.edge.rpc.processor.asset.AssetEdgeProcessorV1; +import org.thingsboard.server.service.edge.rpc.processor.asset.AssetEdgeProcessorV2; +import org.thingsboard.server.service.edge.rpc.processor.asset.profile.AssetProfileEdgeProcessorV1; +import org.thingsboard.server.service.edge.rpc.processor.asset.profile.AssetProfileEdgeProcessorV2; +import org.thingsboard.server.service.edge.rpc.processor.dashboard.DashboardEdgeProcessorFactory; +import org.thingsboard.server.service.edge.rpc.processor.dashboard.DashboardEdgeProcessorV1; +import org.thingsboard.server.service.edge.rpc.processor.dashboard.DashboardEdgeProcessorV2; +import org.thingsboard.server.service.edge.rpc.processor.device.DeviceEdgeProcessorFactory; +import org.thingsboard.server.service.edge.rpc.processor.device.DeviceEdgeProcessorV1; +import org.thingsboard.server.service.edge.rpc.processor.device.DeviceEdgeProcessorV2; +import org.thingsboard.server.service.edge.rpc.processor.device.profile.DeviceProfileEdgeProcessorV1; +import org.thingsboard.server.service.edge.rpc.processor.device.profile.DeviceProfileEdgeProcessorV2; +import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewProcessorFactory; +import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewProcessorV1; +import org.thingsboard.server.service.edge.rpc.processor.entityview.EntityViewProcessorV2; +import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgeProcessorFactory; +import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgeProcessorV1; +import org.thingsboard.server.service.edge.rpc.processor.relation.RelationEdgeProcessorV2; +import org.thingsboard.server.service.edge.rpc.processor.resource.ResourceEdgeProcessorFactory; +import org.thingsboard.server.service.edge.rpc.processor.resource.ResourceEdgeProcessorV1; +import org.thingsboard.server.service.edge.rpc.processor.resource.ResourceEdgeProcessorV2; +import org.thingsboard.server.service.entitiy.TbNotificationEntityService; +import org.thingsboard.server.service.executors.DbCallbackExecutorService; +import org.thingsboard.server.service.profile.TbAssetProfileCache; +import org.thingsboard.server.service.profile.TbDeviceProfileCache; +import org.thingsboard.server.service.state.DeviceStateService; +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; + +import java.util.UUID; +import java.util.stream.Stream; + +public abstract class BaseEdgeProcessorTest { + + @MockBean + protected TelemetrySubscriptionService tsSubService; + + @MockBean + protected TbNotificationEntityService notificationEntityService; + + @MockBean + protected RuleChainService ruleChainService; + + @MockBean + protected AlarmService alarmService; + + @MockBean + protected DeviceService deviceService; + + @MockBean + protected TbDeviceProfileCache deviceProfileCache; + + @MockBean + protected TbAssetProfileCache assetProfileCache; + + @MockBean + protected DashboardService dashboardService; + + @MockBean + protected AssetService assetService; + + @MockBean + protected EntityViewService entityViewService; + + @MockBean + protected TenantService tenantService; + + @MockBean + protected TenantProfileService tenantProfileService; + + @MockBean + protected EdgeService edgeService; + + @MockBean + protected CustomerService customerService; + + @MockBean + protected UserService userService; + + @MockBean + protected DeviceProfileService deviceProfileService; + + @MockBean + protected AssetProfileService assetProfileService; + + @MockBean + protected RelationService relationService; + + @MockBean + protected DeviceCredentialsService deviceCredentialsService; + + @MockBean + protected AttributesService attributesService; + + @MockBean + protected TbClusterService tbClusterService; + + @MockBean + protected DeviceStateService deviceStateService; + + @MockBean + protected EdgeEventService edgeEventService; + + @MockBean + protected WidgetsBundleService widgetsBundleService; + + @MockBean + protected WidgetTypeService widgetTypeService; + + @MockBean + protected OtaPackageService otaPackageService; + + @MockBean + protected QueueService queueService; + + @MockBean + protected PartitionService partitionService; + + @MockBean + protected ResourceService resourceService; + + @MockBean + @Lazy + protected TbQueueProducerProvider producerProvider; + + @MockBean + protected DataValidator deviceValidator; + + @MockBean + protected DataValidator deviceProfileValidator; + + @MockBean + protected DataValidator assetValidator; + + @MockBean + protected DataValidator assetProfileValidator; + + @MockBean + protected DataValidator dashboardValidator; + + @MockBean + protected DataValidator entityViewValidator; + + @MockBean + protected DataValidator resourceValidator; + + @MockBean + protected EdgeMsgConstructor edgeMsgConstructor; + + @MockBean + protected EntityDataMsgConstructor entityDataMsgConstructor; + + @MockBean + protected AdminSettingsMsgConstructorV1 adminSettingsMsgConstructorV1; + + @MockBean + protected AdminSettingsMsgConstructorV2 adminSettingsMsgConstructorV2; + + @MockBean + protected AlarmMsgConstructorV1 alarmMsgConstructorV1; + + @MockBean + protected AlarmMsgConstructorV2 alarmMsgConstructorV2; + + @SpyBean + protected AssetMsgConstructorV1 assetMsgConstructorV1; + + @SpyBean + protected AssetMsgConstructorV2 assetMsgConstructorV2; + + @MockBean + protected CustomerMsgConstructorV1 customerMsgConstructorV1; + + @MockBean + protected CustomerMsgConstructorV2 customerMsgConstructorV2; + + @MockBean + protected DashboardMsgConstructorV1 dashboardMsgConstructorV1; + + @MockBean + protected DashboardMsgConstructorV2 dashboardMsgConstructorV2; + + @SpyBean + protected DeviceMsgConstructorV1 deviceMsgConstructorV1; + + @SpyBean + protected DeviceMsgConstructorV2 deviceMsgConstructorV2; + + @MockBean + protected EntityViewMsgConstructorV1 entityViewMsgConstructorV1; + + @MockBean + protected EntityViewMsgConstructorV2 entityViewMsgConstructorV2; + + @MockBean + protected OtaPackageMsgConstructorV1 otaPackageMsgConstructorV1; + + @MockBean + protected OtaPackageMsgConstructorV2 otaPackageMsgConstructorV2; + + @MockBean + protected QueueMsgConstructorV1 queueMsgConstructorV1; + + @MockBean + protected QueueMsgConstructorV2 queueMsgConstructorV2; + + @MockBean + protected RelationMsgConstructorV1 relationMsgConstructorV1; + + @MockBean + protected RelationMsgConstructorV2 relationMsgConstructorV2; + + @MockBean + protected ResourceMsgConstructorV1 resourceMsgConstructorV1; + + @MockBean + protected ResourceMsgConstructorV2 resourceMsgConstructorV2; + + @SpyBean + protected RuleChainMsgConstructorV1 ruleChainMsgConstructorV1; + + @SpyBean + protected RuleChainMsgConstructorV2 ruleChainMsgConstructorV2; + + @MockBean + protected TenantMsgConstructorV1 tenantMsgConstructorV1; + + @MockBean + protected TenantMsgConstructorV2 tenantMsgConstructorV2; + + @MockBean + protected UserMsgConstructorV1 userMsgConstructorV1; + + @MockBean + protected UserMsgConstructorV2 userMsgConstructorV2; + + @MockBean + protected WidgetMsgConstructorV1 widgetMsgConstructorV1; + + @MockBean + protected WidgetMsgConstructorV2 widgetMsgConstructorV2; + + @MockBean + protected AlarmEdgeProcessorV1 alarmProcessorV1; + + @MockBean + protected AlarmEdgeProcessorV2 alarmProcessorV2; + + @SpyBean + protected AssetEdgeProcessorV1 assetProcessorV1; + + @SpyBean + protected AssetEdgeProcessorV2 assetProcessorV2; + + @SpyBean + protected AssetProfileEdgeProcessorV1 assetProfileProcessorV1; + + @SpyBean + protected AssetProfileEdgeProcessorV2 assetProfileProcessorV2; + + @MockBean + protected DashboardEdgeProcessorV1 dashboardProcessorV1; + + @MockBean + protected DashboardEdgeProcessorV2 dashboardProcessorV2; + + @MockBean + protected ImageService imageService; + + @SpyBean + protected DeviceEdgeProcessorV1 deviceEdgeProcessorV1; + + @SpyBean + protected DeviceEdgeProcessorV2 deviceEdgeProcessorV2; + + @SpyBean + protected DeviceProfileEdgeProcessorV1 deviceProfileProcessorV1; + + @SpyBean + protected DeviceProfileEdgeProcessorV2 deviceProfileProcessorV2; + + @MockBean + protected EntityViewProcessorV1 entityViewProcessorV1; + + @MockBean + protected EntityViewProcessorV2 entityViewProcessorV2; + + @MockBean + protected ResourceEdgeProcessorV1 resourceEdgeProcessorV1; + + @MockBean + protected ResourceEdgeProcessorV2 resourceEdgeProcessorV2; + + @MockBean + protected RelationEdgeProcessorV1 relationEdgeProcessorV1; + + @MockBean + protected RelationEdgeProcessorV2 relationEdgeProcessorV2; + + @SpyBean + protected RuleChainMsgConstructorFactory ruleChainMsgConstructorFactory; + + @MockBean + protected AlarmMsgConstructorFactory alarmMsgConstructorFactory; + + @SpyBean + protected DeviceMsgConstructorFactory deviceMsgConstructorFactory; + + @SpyBean + protected AssetMsgConstructorFactory assetMsgConstructorFactory; + + @MockBean + protected DashboardMsgConstructorFactory dashboardMsgConstructorFactory; + + @MockBean + protected EntityViewMsgConstructorFactory entityViewMsgConstructorFactory; + + @MockBean + protected RelationMsgConstructorFactory relationMsgConstructorFactory; + + @MockBean + protected UserMsgConstructorFactory userMsgConstructorFactory; + + @MockBean + protected CustomerMsgConstructorFactory customerMsgConstructorFactory; + + @MockBean + protected TenantMsgConstructorFactory tenantMsgConstructorFactory; + + @MockBean + protected WidgetMsgConstructorFactory widgetBundleMsgConstructorFactory; + + @MockBean + protected AdminSettingsMsgConstructorFactory adminSettingsMsgConstructorFactory; + + @MockBean + protected OtaPackageMsgConstructorFactory otaPackageMsgConstructorFactory; + + @MockBean + protected QueueMsgConstructorFactory queueMsgConstructorFactory; + + @MockBean + protected ResourceMsgConstructorFactory resourceMsgConstructorFactory; + + @MockBean + protected AlarmEdgeProcessorFactory alarmEdgeProcessorFactory; + + @SpyBean + protected AssetEdgeProcessorFactory assetEdgeProcessorFactory; + + @MockBean + protected DashboardEdgeProcessorFactory dashboardEdgeProcessorFactory; + + @SpyBean + protected DeviceEdgeProcessorFactory deviceEdgeProcessorFactory; + + @MockBean + protected EntityViewProcessorFactory entityViewProcessorFactory; + + @MockBean + protected RelationEdgeProcessorFactory relationEdgeProcessorFactory; + + @MockBean + protected ResourceEdgeProcessorFactory resourceEdgeProcessorFactory; + + @MockBean + protected EdgeSynchronizationManager edgeSynchronizationManager; + + @MockBean + protected DbCallbackExecutorService dbCallbackExecutorService; + + @MockBean + protected DataDecodingEncodingService dataDecodingEncodingService; + + protected EdgeId edgeId; + protected TenantId tenantId; + protected EdgeEvent edgeEvent; + + protected DashboardId getDashboardId(long expectedDashboardIdMSB, long expectedDashboardIdLSB) { + DashboardId dashboardId; + if (expectedDashboardIdMSB != 0 && expectedDashboardIdLSB != 0) { + dashboardId = new DashboardId(new UUID(expectedDashboardIdMSB, expectedDashboardIdLSB)); + } else { + dashboardId = new DashboardId(UUID.randomUUID()); + } + return dashboardId; + } + + protected RuleChainId getRuleChainId(long expectedRuleChainIdMSB, long expectedRuleChainIdLSB) { + RuleChainId ruleChainId; + if (expectedRuleChainIdMSB != 0 && expectedRuleChainIdLSB != 0) { + ruleChainId = new RuleChainId(new UUID(expectedRuleChainIdMSB, expectedRuleChainIdLSB)); + } else { + ruleChainId = new RuleChainId(UUID.randomUUID()); + } + return ruleChainId; + } + + protected static Stream provideParameters() { + UUID dashoboardUUID = UUID.randomUUID(); + UUID ruleChaindUUID = UUID.randomUUID(); + return Stream.of( + Arguments.of(EdgeVersion.V_3_3_0, 0, 0, 0, 0), + Arguments.of(EdgeVersion.V_3_3_3, 0, 0, 0, 0), + Arguments.of(EdgeVersion.V_3_4_0, 0, 0, 0, 0), + Arguments.of(EdgeVersion.V_3_6_0, + dashoboardUUID.getMostSignificantBits(), + dashoboardUUID.getLeastSignificantBits(), + ruleChaindUUID.getMostSignificantBits(), + ruleChaindUUID.getLeastSignificantBits()) + ); + } +} diff --git a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/asset/AbstractAssetProcessorTest.java b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/asset/AbstractAssetProcessorTest.java new file mode 100644 index 0000000000..6216c71440 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/asset/AbstractAssetProcessorTest.java @@ -0,0 +1,92 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.asset; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.AssetProfileId; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessorTest; + +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.BDDMockito.willReturn; + +public abstract class AbstractAssetProcessorTest extends BaseEdgeProcessorTest { + + + protected AssetId assetId; + protected AssetProfileId assetProfileId; + protected AssetProfile assetProfile; + + @BeforeEach + public void setUp() { + edgeId = new EdgeId(UUID.randomUUID()); + tenantId = new TenantId(UUID.randomUUID()); + assetId = new AssetId(UUID.randomUUID()); + assetProfileId = new AssetProfileId(UUID.randomUUID()); + + assetProfile = new AssetProfile(); + assetProfile.setId(assetProfileId); + assetProfile.setName("AssetProfile"); + assetProfile.setDefault(true); + + Asset asset = new Asset(); + asset.setAssetProfileId(assetProfileId); + asset.setId(assetId); + asset.setName("Asset"); + asset.setType(assetProfile.getName()); + + edgeEvent = new EdgeEvent(); + edgeEvent.setTenantId(tenantId); + edgeEvent.setAction(EdgeEventActionType.ADDED); + + + willReturn(asset).given(assetService).findAssetById(tenantId, assetId); + willReturn(assetProfile).given(assetProfileService).findAssetProfileById(tenantId, assetProfileId); + } + + protected void updateAssetProfileDefaultFields(long expectedDashboardIdMSB, long expectedDashboardIdLSB, + long expectedRuleChainIdMSB, long expectedRuleChainIdLSB) { + DashboardId dashboardId = getDashboardId(expectedDashboardIdMSB, expectedDashboardIdLSB); + RuleChainId ruleChainId = getRuleChainId(expectedRuleChainIdMSB, expectedRuleChainIdLSB); + + assetProfile.setDefaultDashboardId(dashboardId); + assetProfile.setDefaultEdgeRuleChainId(ruleChainId); + + } + + protected void verify(DownlinkMsg downlinkMsg, long expectedDashboardIdMSB, long expectedDashboardIdLSB, + long expectedRuleChainIdMSB, long expectedRuleChainIdLSB) { + AssetProfileUpdateMsg assetProfileUpdateMsg = downlinkMsg.getAssetProfileUpdateMsgList().get(0); + assertNotNull(assetProfileUpdateMsg); + Assertions.assertThat(assetProfileUpdateMsg.getDefaultDashboardIdMSB()).isEqualTo(expectedDashboardIdMSB); + Assertions.assertThat(assetProfileUpdateMsg.getDefaultDashboardIdLSB()).isEqualTo(expectedDashboardIdLSB); + Assertions.assertThat(assetProfileUpdateMsg.getDefaultRuleChainIdMSB()).isEqualTo(expectedRuleChainIdMSB); + Assertions.assertThat(assetProfileUpdateMsg.getDefaultRuleChainIdLSB()).isEqualTo(expectedRuleChainIdLSB); + } +} diff --git a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessorTest.java b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessorTest.java new file mode 100644 index 0000000000..128597cbc0 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessorTest.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.asset; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.boot.test.context.SpringBootTest; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; + +@SpringBootTest(classes = {AssetEdgeProcessorV1.class}) +class AssetEdgeProcessorTest extends AbstractAssetProcessorTest { + + @ParameterizedTest + @MethodSource("provideParameters") + public void testAssetProfileDefaultFields_notSendToEdgeOlder3_6_0IfNotAssigned(EdgeVersion edgeVersion, long expectedDashboardIdMSB, long expectedDashboardIdLSB, + long expectedRuleChainIdMSB, long expectedRuleChainIdLSB) { + updateAssetProfileDefaultFields(expectedDashboardIdMSB, expectedDashboardIdLSB, expectedRuleChainIdMSB, expectedRuleChainIdLSB); + + edgeEvent.setEntityId(assetId.getId()); + + DownlinkMsg downlinkMsg = assetProcessorV1.convertAssetEventToDownlink(edgeEvent, edgeId, edgeVersion); + + verify(downlinkMsg, expectedDashboardIdMSB, expectedDashboardIdLSB, expectedRuleChainIdMSB, expectedRuleChainIdLSB); + } +} diff --git a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessorTest.java b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessorTest.java new file mode 100644 index 0000000000..c44505e454 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessorTest.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.asset; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.boot.test.context.SpringBootTest; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; + +@SpringBootTest(classes = {AssetEdgeProcessorV1.class}) +class AssetProfileEdgeProcessorTest extends AbstractAssetProcessorTest{ + + @ParameterizedTest + @MethodSource("provideParameters") + public void testAssetProfileDefaultFields_notSendToEdgeOlder3_6_0IfNotAssigned(EdgeVersion edgeVersion, long expectedDashboardIdMSB, long expectedDashboardIdLSB, + long expectedRuleChainIdMSB, long expectedRuleChainIdLSB) { + + updateAssetProfileDefaultFields(expectedDashboardIdMSB, expectedDashboardIdLSB, expectedRuleChainIdMSB, expectedRuleChainIdLSB); + + edgeEvent.setEntityId(assetProfileId.getId()); + + DownlinkMsg downlinkMsg = assetProfileProcessorV1.convertAssetProfileEventToDownlink(edgeEvent, edgeId, edgeVersion); + + verify(downlinkMsg, expectedDashboardIdMSB, expectedDashboardIdLSB, expectedRuleChainIdMSB, expectedRuleChainIdLSB); + } +} diff --git a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/device/AbstractDeviceProcessorTest.java b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/device/AbstractDeviceProcessorTest.java new file mode 100644 index 0000000000..6f2249b173 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/device/AbstractDeviceProcessorTest.java @@ -0,0 +1,100 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.device; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.DeviceProfileType; +import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessorTest; + +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.BDDMockito.willReturn; + +public abstract class AbstractDeviceProcessorTest extends BaseEdgeProcessorTest { + + protected DeviceId deviceId; + protected DeviceProfileId deviceProfileId; + protected DeviceProfile deviceProfile; + + @BeforeEach + public void setUp() { + edgeId = new EdgeId(UUID.randomUUID()); + tenantId = new TenantId(UUID.randomUUID()); + deviceId = new DeviceId(UUID.randomUUID()); + deviceProfileId = new DeviceProfileId(UUID.randomUUID()); + + deviceProfile = new DeviceProfile(); + deviceProfile.setId(deviceProfileId); + deviceProfile.setName("DeviceProfile"); + deviceProfile.setDefault(true); + deviceProfile.setType(DeviceProfileType.DEFAULT); + DeviceProfileData deviceProfileData = new DeviceProfileData(); + deviceProfile.setProfileData(deviceProfileData); + deviceProfile.setTransportType(DeviceTransportType.DEFAULT); + + Device device = new Device(); + device.setDeviceProfileId(deviceProfileId); + device.setId(deviceId); + device.setName("Device"); + device.setType(deviceProfile.getName()); + + edgeEvent = new EdgeEvent(); + edgeEvent.setTenantId(tenantId); + edgeEvent.setAction(EdgeEventActionType.ADDED); + + + willReturn(device).given(deviceService).findDeviceById(tenantId, deviceId); + willReturn(deviceProfile).given(deviceProfileService).findDeviceProfileById(tenantId, deviceProfileId); + willReturn(new byte[]{0x00}).given(dataDecodingEncodingService).encode(deviceProfileData); + + } + + protected void updateDeviceProfileDefaultFields(long expectedDashboardIdMSB, long expectedDashboardIdLSB, + long expectedRuleChainIdMSB, long expectedRuleChainIdLSB) { + DashboardId dashboardId = getDashboardId(expectedDashboardIdMSB, expectedDashboardIdLSB); + RuleChainId ruleChainId = getRuleChainId(expectedRuleChainIdMSB, expectedRuleChainIdLSB); + + deviceProfile.setDefaultDashboardId(dashboardId); + deviceProfile.setDefaultEdgeRuleChainId(ruleChainId); + + } + + protected void verify(DownlinkMsg downlinkMsg, long expectedDashboardIdMSB, long expectedDashboardIdLSB, + long expectedRuleChainIdMSB, long expectedRuleChainIdLSB) { + DeviceProfileUpdateMsg deviceProfileUpdateMsg = downlinkMsg.getDeviceProfileUpdateMsgList().get(0); + assertNotNull(deviceProfileUpdateMsg); + Assertions.assertThat(deviceProfileUpdateMsg.getDefaultDashboardIdMSB()).isEqualTo(expectedDashboardIdMSB); + Assertions.assertThat(deviceProfileUpdateMsg.getDefaultDashboardIdLSB()).isEqualTo(expectedDashboardIdLSB); + Assertions.assertThat(deviceProfileUpdateMsg.getDefaultRuleChainIdMSB()).isEqualTo(expectedRuleChainIdMSB); + Assertions.assertThat(deviceProfileUpdateMsg.getDefaultRuleChainIdLSB()).isEqualTo(expectedRuleChainIdLSB); + } +} diff --git a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessorTest.java b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessorTest.java new file mode 100644 index 0000000000..aa70aef2da --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessorTest.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.device; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.boot.test.context.SpringBootTest; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; + +@SpringBootTest(classes = {DeviceEdgeProcessorV1.class}) +class DeviceEdgeProcessorTest extends AbstractDeviceProcessorTest { + + @ParameterizedTest + @MethodSource("provideParameters") + public void testDeviceProfileDefaultFields_notSendToEdgeOlder3_6_0IfNotAssigned(EdgeVersion edgeVersion, long expectedDashboardIdMSB, long expectedDashboardIdLSB, + long expectedRuleChainIdMSB, long expectedRuleChainIdLSB) { + updateDeviceProfileDefaultFields(expectedDashboardIdMSB, expectedDashboardIdLSB, expectedRuleChainIdMSB, expectedRuleChainIdLSB); + edgeEvent.setEntityId(deviceId.getId()); + + DownlinkMsg downlinkMsg = deviceEdgeProcessorV1.convertDeviceEventToDownlink(edgeEvent, edgeId, edgeVersion); + + verify(downlinkMsg, expectedDashboardIdMSB, expectedDashboardIdLSB, expectedRuleChainIdMSB, expectedRuleChainIdLSB); + } +} diff --git a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessorTest.java b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessorTest.java new file mode 100644 index 0000000000..02267c966c --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessorTest.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.device; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.boot.test.context.SpringBootTest; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; +import org.thingsboard.server.gen.edge.v1.EdgeVersion; +import org.thingsboard.server.service.edge.rpc.processor.device.profile.DeviceProfileEdgeProcessorV1; + + +@SpringBootTest(classes = {DeviceProfileEdgeProcessorV1.class}) +class DeviceProfileEdgeProcessorTest extends AbstractDeviceProcessorTest { + + @ParameterizedTest + @MethodSource("provideParameters") + public void testDeviceProfileDefaultFields_notSendToEdgeOlder3_6_0IfNotAssigned(EdgeVersion edgeVersion, long expectedDashboardIdMSB, long expectedDashboardIdLSB, + long expectedRuleChainIdMSB, long expectedRuleChainIdLSB) { + updateDeviceProfileDefaultFields(expectedDashboardIdMSB, expectedDashboardIdLSB, expectedRuleChainIdMSB, expectedRuleChainIdLSB); + + edgeEvent.setEntityId(deviceProfileId.getId()); + + DownlinkMsg downlinkMsg = deviceProfileProcessorV1.convertDeviceProfileEventToDownlink(edgeEvent, edgeId, edgeVersion); + + verify(downlinkMsg, expectedDashboardIdMSB, expectedDashboardIdLSB, expectedRuleChainIdMSB, expectedRuleChainIdLSB); + } +} diff --git a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessorTest.java b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessorTest.java new file mode 100644 index 0000000000..9dbfa6ae45 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessorTest.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.edge.rpc.processor.telemetry; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.gen.edge.v1.DownlinkMsg; + +@Slf4j +@RunWith(MockitoJUnitRunner.class) +public class TelemetryEdgeProcessorTest { + + @Test + public void testConvert_maxSizeLimit() throws Exception { + EdgeEvent edgeEvent = new EdgeEvent(); + ObjectNode body = JacksonUtil.newObjectNode(); + body.put("value", StringUtils.randomAlphanumeric(10000)); + edgeEvent.setBody(body); + DownlinkMsg downlinkMsg = new TelemetryEdgeProcessor().convertTelemetryEventToDownlink(edgeEvent); + Assert.assertNull(downlinkMsg); + } +} diff --git a/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java b/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java index 487783b454..b3e8941364 100644 --- a/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.entitiy.alarm; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; @@ -23,13 +24,20 @@ import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; +import org.thingsboard.server.common.data.alarm.AlarmComment; +import org.thingsboard.server.common.data.alarm.AlarmCommentType; import org.thingsboard.server.common.data.alarm.AlarmInfo; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.edge.EdgeService; @@ -38,11 +46,13 @@ import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.sync.vc.EntitiesVersionControlService; import org.thingsboard.server.service.telemetry.AlarmSubscriptionService; +import java.util.Collections; +import java.util.List; import java.util.UUID; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -73,7 +83,6 @@ public class DefaultTbAlarmServiceTest { protected TbClusterService tbClusterService; @MockBean private EntitiesVersionControlService vcService; - @SpyBean DefaultTbAlarmService service; @@ -87,7 +96,7 @@ public class DefaultTbAlarmServiceTest { .build()); service.save(alarm, new User()); - verify(notificationEntityService, times(1)).notifyCreateOrUpdateAlarm(any(), any(), any()); + verify(notificationEntityService, times(1)).logEntityAction(any(), any(), any(), any(), eq(ActionType.ADDED), any()); verify(alarmSubscriptionService, times(1)).createAlarm(any()); } @@ -95,11 +104,11 @@ public class DefaultTbAlarmServiceTest { public void testAck() throws ThingsboardException { var alarm = new Alarm(); when(alarmSubscriptionService.acknowledgeAlarm(any(), any(), anyLong())) - .thenReturn(AlarmApiCallResult.builder().successful(true).modified(true).build()); + .thenReturn(AlarmApiCallResult.builder().successful(true).modified(true).alarm(new AlarmInfo()).build()); service.ack(alarm, new User(new UserId(UUID.randomUUID()))); verify(alarmCommentService, times(1)).saveAlarmComment(any(), any(), any()); - verify(notificationEntityService, times(1)).notifyCreateOrUpdateAlarm(any(), any(), any()); + verify(notificationEntityService, times(1)).logEntityAction(any(), any(), any(), any(), eq(ActionType.ALARM_ACK), any()); verify(alarmSubscriptionService, times(1)).acknowledgeAlarm(any(), any(), anyLong()); } @@ -108,11 +117,11 @@ public class DefaultTbAlarmServiceTest { var alarm = new Alarm(); alarm.setAcknowledged(true); when(alarmSubscriptionService.clearAlarm(any(), any(), anyLong(), any())) - .thenReturn(AlarmApiCallResult.builder().successful(true).cleared(true).build()); + .thenReturn(AlarmApiCallResult.builder().successful(true).cleared(true).alarm(new AlarmInfo()).build()); service.clear(alarm, new User(new UserId(UUID.randomUUID()))); verify(alarmCommentService, times(1)).saveAlarmComment(any(), any(), any()); - verify(notificationEntityService, times(1)).notifyCreateOrUpdateAlarm(any(), any(), any()); + verify(notificationEntityService, times(1)).logEntityAction(any(), any(), any(), any(), eq(ActionType.ALARM_CLEAR), any()); verify(alarmSubscriptionService, times(1)).clearAlarm(any(), any(), anyLong(), any()); } @@ -120,7 +129,64 @@ public class DefaultTbAlarmServiceTest { public void testDelete() { service.delete(new Alarm(), new User()); - verify(notificationEntityService, times(1)).notifyDeleteAlarm(any(), any(), any(), any(), any(), any(), anyString()); + verify(notificationEntityService, times(1)).logEntityAction(any(), any(), any(), any(), eq(ActionType.DELETED), any()); verify(alarmSubscriptionService, times(1)).deleteAlarm(any(), any()); } -} \ No newline at end of file + + @Test + public void testUnassignAlarm() throws ThingsboardException { + AlarmInfo alarm = new AlarmInfo(); + alarm.setId(new AlarmId(UUID.randomUUID())); + when(alarmSubscriptionService.unassignAlarm(any(), any(), anyLong())) + .thenReturn(AlarmApiCallResult.builder().successful(true).modified(true).alarm(alarm).build()); + + User user = new User(); + user.setEmail("testEmail@gmail.com"); + user.setId(new UserId(UUID.randomUUID())); + service.unassign(new Alarm(), 0L, user); + + ObjectNode commentNode = JacksonUtil.newObjectNode(); + commentNode.put("subtype", "ASSIGN"); + commentNode.put("text", "Alarm was unassigned by user " + user.getTitle()); + commentNode.put("userId", user.getId().getId().toString()); + AlarmComment expectedAlarmComment = AlarmComment.builder() + .alarmId(alarm.getId()) + .type(AlarmCommentType.SYSTEM) + .comment(commentNode) + .build(); + + verify(alarmCommentService, times(1)) + .saveAlarmComment(eq(alarm), eq(expectedAlarmComment), eq(user)); + } + + @Test + public void testUnassignDeletedUserAlarms() throws ThingsboardException { + AlarmInfo alarm = new AlarmInfo(); + alarm.setId(new AlarmId(UUID.randomUUID())); + + when(alarmService.findAlarmIdsByAssigneeId(any(), any(), any())) + .thenReturn(new PageData<>(List.of(alarm.getId()), 0, 1, false)) + .thenReturn(new PageData<>(Collections.EMPTY_LIST, 0, 0, false)); + when(alarmSubscriptionService.unassignAlarm(any(), any(), anyLong())) + .thenReturn(AlarmApiCallResult.builder().successful(true).modified(true).alarm(alarm).build()); + + User user = new User(); + user.setEmail("testEmail@gmail.com"); + user.setId(new UserId(UUID.randomUUID())); + service.unassignDeletedUserAlarms(new TenantId(UUID.randomUUID()), user, System.currentTimeMillis()); + + ObjectNode commentNode = JacksonUtil.newObjectNode(); + commentNode.put("subtype", "ASSIGN"); + commentNode.put("text", String.format("Alarm was unassigned because user %s - was deleted", user.getTitle())); + AlarmComment expectedAlarmComment = AlarmComment.builder() + .alarmId(alarm.getId()) + .type(AlarmCommentType.SYSTEM) + .comment(commentNode) + .build(); + + verify(alarmCommentService, times(1)) + .saveAlarmComment(eq(alarm), eq(expectedAlarmComment), eq(null)); + } + + +} diff --git a/application/src/test/java/org/thingsboard/server/service/entitiy/alarmComment/DefaultTbAlarmCommentServiceTest.java b/application/src/test/java/org/thingsboard/server/service/entitiy/alarmComment/DefaultTbAlarmCommentServiceTest.java index 29d9a6ebb8..50055b8d1a 100644 --- a/application/src/test/java/org/thingsboard/server/service/entitiy/alarmComment/DefaultTbAlarmCommentServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/entitiy/alarmComment/DefaultTbAlarmCommentServiceTest.java @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmComment; import org.thingsboard.server.common.data.alarm.AlarmCommentType; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.UserId; @@ -81,7 +82,7 @@ public class DefaultTbAlarmCommentServiceTest { when(alarmCommentService.createOrUpdateAlarmComment(Mockito.any(), eq(alarmComment))).thenReturn(alarmComment); service.saveAlarmComment(alarm, alarmComment, new User()); - verify(notificationEntityService, times(1)).notifyAlarmComment(any(), any(), any(), any()); + verify(notificationEntityService, times(1)).logEntityAction(any(), any(), any(), any(), eq(ActionType.ADDED_COMMENT), any(), any()); } @Test @@ -95,7 +96,7 @@ public class DefaultTbAlarmCommentServiceTest { when(alarmCommentService.saveAlarmComment(Mockito.any(), eq(alarmComment))).thenReturn(alarmComment); service.deleteAlarmComment(new Alarm(alarmId), alarmComment, new User()); - verify(notificationEntityService, times(1)).notifyAlarmComment(any(), any(), any(), any()); + verify(notificationEntityService, times(1)).logEntityAction(any(), any(), any(), any(), eq(ActionType.DELETED_COMMENT), any(), any()); } @Test diff --git a/application/src/test/java/org/thingsboard/server/service/install/InstallScriptsTest.java b/application/src/test/java/org/thingsboard/server/service/install/InstallScriptsTest.java new file mode 100644 index 0000000000..de8fc45381 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/install/InstallScriptsTest.java @@ -0,0 +1,122 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.install; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.dao.dashboard.DashboardService; +import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; +import org.thingsboard.server.dao.resource.ResourceService; +import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.service.validator.RuleChainDataValidator; +import org.thingsboard.server.dao.tenant.TenantService; +import org.thingsboard.server.dao.usagerecord.ApiLimitService; +import org.thingsboard.server.dao.widget.WidgetTypeService; +import org.thingsboard.server.dao.widget.WidgetsBundleService; +import org.thingsboard.server.service.install.update.ImagesUpdater; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.willReturn; + +@Slf4j +@SpringBootTest(classes = {InstallScripts.class, RuleChainDataValidator.class}) +class InstallScriptsTest { + + @MockBean + RuleChainService ruleChainService; + @MockBean + DashboardService dashboardService; + @MockBean + WidgetTypeService widgetTypeService; + @MockBean + WidgetsBundleService widgetsBundleService; + @MockBean + OAuth2ConfigTemplateService oAuth2TemplateService; + @MockBean + ResourceService resourceService; + @MockBean + ImagesUpdater imagesUpdater; + @SpyBean + InstallScripts installScripts; + + @MockBean + TenantService tenantService; + @MockBean + ApiLimitService apiLimitService; + @SpyBean + RuleChainDataValidator ruleChainValidator; + TenantId tenantId = TenantId.fromUUID(UUID.fromString("9ef79cdf-37a8-4119-b682-2e7ed4e018da")); + + @BeforeEach + void setUp() { + willReturn(true).given(tenantService).tenantExists(tenantId); + willReturn(true).given(apiLimitService).checkEntitiesLimit(any(), any()); + } + + @Test + void testDefaultRuleChainsTemplates() throws IOException { + Path dir = installScripts.getTenantRuleChainsDir(); + installScripts.findRuleChainsFromPath(dir) + .forEach(this::validateRuleChainTemplate); + } + + @Test + void testDefaultEdgeRuleChainsTemplates() throws IOException { + Path dir = installScripts.getEdgeRuleChainsDir(); + installScripts.findRuleChainsFromPath(dir) + .forEach(this::validateRuleChainTemplate); + } + + @Test + void testDeviceProfileDefaultRuleChainTemplate() { + validateRuleChainTemplate(installScripts.getDeviceProfileDefaultRuleChainTemplateFilePath()); + } + + private void validateRuleChainTemplate(Path templateFilePath) { + log.warn("validateRuleChainTemplate {}", templateFilePath); + JsonNode ruleChainJson = JacksonUtil.toJsonNode(templateFilePath.toFile()); + + RuleChain ruleChain = JacksonUtil.treeToValue(ruleChainJson.get("ruleChain"), RuleChain.class); + ruleChain.setTenantId(tenantId); + ruleChainValidator.validate(ruleChain, RuleChain::getTenantId); + ruleChain.setId(new RuleChainId(UUID.randomUUID())); + + RuleChainMetaData ruleChainMetaData = JacksonUtil.treeToValue(ruleChainJson.get("metadata"), RuleChainMetaData.class); + ruleChainMetaData.setRuleChainId(ruleChain.getId()); + List throwables = RuleChainDataValidator.validateMetaData(ruleChainMetaData); + + assertThat(throwables).as("templateFilePath " + templateFilePath) + .containsExactlyInAnyOrderElementsOf(Collections.emptyList()); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java b/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java index 05ee66f327..c56adb5fd5 100644 --- a/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java @@ -24,11 +24,12 @@ import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; +import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.util.limits.DefaultRateLimitService; -import org.thingsboard.server.dao.util.limits.LimitedApi; import org.thingsboard.server.dao.util.limits.RateLimitService; import java.util.List; @@ -37,6 +38,7 @@ import java.util.UUID; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.when; @@ -50,7 +52,7 @@ public class RateLimitServiceTest { @Before public void beforeEach() { tenantProfileCache = Mockito.mock(TbTenantProfileCache.class); - rateLimitService = new DefaultRateLimitService(tenantProfileCache, 60, 100); + rateLimitService = new DefaultRateLimitService(tenantProfileCache, mock(NotificationRuleProcessor.class), 60, 100); tenantId = new TenantId(UUID.randomUUID()); } @@ -73,14 +75,14 @@ public class RateLimitServiceTest { LimitedApi.ENTITY_EXPORT, LimitedApi.ENTITY_IMPORT, LimitedApi.NOTIFICATION_REQUESTS, - LimitedApi.REST_REQUESTS, + LimitedApi.REST_REQUESTS_PER_CUSTOMER, LimitedApi.CASSANDRA_QUERIES )) { testRateLimits(limitedApi, max, tenantId); } CustomerId customerId = new CustomerId(UUID.randomUUID()); - testRateLimits(LimitedApi.REST_REQUESTS, max, customerId); + testRateLimits(LimitedApi.REST_REQUESTS_PER_CUSTOMER, max, customerId); NotificationRuleId notificationRuleId = new NotificationRuleId(UUID.randomUUID()); testRateLimits(LimitedApi.NOTIFICATION_REQUESTS_PER_RULE, max, notificationRuleId); diff --git a/application/src/test/java/org/thingsboard/server/service/mail/TbMailSenderTest.java b/application/src/test/java/org/thingsboard/server/service/mail/TbMailSenderTest.java new file mode 100644 index 0000000000..5b95adf526 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/mail/TbMailSenderTest.java @@ -0,0 +1,95 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.mail; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mockito; +import javax.mail.MessagingException; +import javax.mail.Session; +import javax.mail.internet.MimeMessage; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.stream.Stream; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.willCallRealMethod; +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; + +public class TbMailSenderTest { + + private TbMailSender tbMailSender; + + @BeforeEach + void setUp() { + tbMailSender = mock(TbMailSender.class); + } + + @Test + public void testDoSendSendMail() { + MimeMessage mimeMsg = new MimeMessage(Session.getInstance(new Properties())); + List mimeMessages = new ArrayList<>(1); + mimeMessages.add(mimeMsg); + + willCallRealMethod().given(tbMailSender).doSend(any(), any()); + tbMailSender.doSend(mimeMessages.toArray(new MimeMessage[0]), null); + + Mockito.verify(tbMailSender, times(1)).updateOauth2PasswordIfExpired(); + Mockito.verify(tbMailSender, times(1)).doSendSuper(any(), any()); + } + + @Test + public void testTestConnection() throws MessagingException { + willCallRealMethod().given(tbMailSender).testConnection(); + tbMailSender.testConnection(); + + Mockito.verify(tbMailSender, times(1)).updateOauth2PasswordIfExpired(); + Mockito.verify(tbMailSender, times(1)).testConnectionSuper(); + } + + @ParameterizedTest + @MethodSource("provideSenderConfiguration") + public void testUpdateOauth2PasswordIfExpiredIfOauth2Enabled(boolean oauth2, long expiresIn, boolean passwordUpdateNeeded) { + willReturn(oauth2).given(tbMailSender).getOauth2Enabled(); + willReturn(expiresIn).given(tbMailSender).getTokenExpires(); + + willCallRealMethod().given(tbMailSender).updateOauth2PasswordIfExpired(); + tbMailSender.updateOauth2PasswordIfExpired(); + + if (passwordUpdateNeeded) { + Mockito.verify(tbMailSender, times(1)).refreshAccessToken(); + Mockito.verify(tbMailSender, times(1)).setPassword(any()); + } else { + Mockito.verify(tbMailSender, Mockito.never()).refreshAccessToken(); + Mockito.verify(tbMailSender, Mockito.never()).setPassword(any()); + } + } + + private static Stream provideSenderConfiguration() { + return Stream.of( + Arguments.of(true, 0L, true), + Arguments.of(true, System.currentTimeMillis() + 5000, false), + Arguments.of(false, 0L, false), + Arguments.of(false, System.currentTimeMillis() + 5000, false) + ); + } +} diff --git a/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java index eb46503c41..6c28d4f880 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java @@ -40,7 +40,7 @@ import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.rule.DefaultNotificationRuleRecipientsConfig; import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig; @@ -61,6 +61,7 @@ import org.thingsboard.server.dao.notification.NotificationRequestService; import org.thingsboard.server.dao.notification.NotificationRuleService; import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; +import org.thingsboard.server.dao.sqlts.insert.sql.SqlPartitioningRepository; import java.net.URISyntaxException; import java.util.Arrays; @@ -90,6 +91,8 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest protected NotificationTargetService notificationTargetService; @Autowired protected NotificationRequestService notificationRequestService; + @Autowired + protected SqlPartitioningRepository partitioningRepository; public static final String DEFAULT_NOTIFICATION_SUBJECT = "Just a test"; public static final NotificationType DEFAULT_NOTIFICATION_TYPE = NotificationType.GENERAL; @@ -100,6 +103,7 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest notificationRuleService.deleteNotificationRulesByTenantId(TenantId.SYS_TENANT_ID); notificationTemplateService.deleteNotificationTemplatesByTenantId(TenantId.SYS_TENANT_ID); notificationTargetService.deleteNotificationTargetsByTenantId(TenantId.SYS_TENANT_ID); + partitioningRepository.dropPartitionsBefore("notification", Long.MAX_VALUE, 1); } protected NotificationTarget createNotificationTarget(UserId... usersIds) { @@ -255,8 +259,9 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest @Override protected NotificationApiWsClient buildAndConnectWebSocketClient() throws URISyntaxException, InterruptedException { - NotificationApiWsClient wsClient = new NotificationApiWsClient(WS_URL + wsPort, token); + NotificationApiWsClient wsClient = new NotificationApiWsClient(WS_URL + wsPort); assertThat(wsClient.connectBlocking(TIMEOUT, TimeUnit.SECONDS)).isTrue(); + wsClient.authenticate(token); return wsClient; } @@ -265,4 +270,8 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest return (NotificationApiWsClient) super.getWsClient(); } + @Override + public NotificationApiWsClient getAnotherWsClient() { + return (NotificationApiWsClient) super.getAnotherWsClient(); + } } diff --git a/application/src/test/java/org/thingsboard/server/service/notification/MockNotificationSettingsService.java b/application/src/test/java/org/thingsboard/server/service/notification/MockNotificationSettingsService.java deleted file mode 100644 index a1cc82fd5a..0000000000 --- a/application/src/test/java/org/thingsboard/server/service/notification/MockNotificationSettingsService.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.service.notification; - -import org.springframework.context.annotation.Primary; -import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.notification.DefaultNotificationSettingsService; -import org.thingsboard.server.dao.settings.AdminSettingsService; - -@Service -@Primary -public class MockNotificationSettingsService extends DefaultNotificationSettingsService { - - public MockNotificationSettingsService(AdminSettingsService adminSettingsService) { - super(adminSettingsService, null, null); - } - - @Override - public void createDefaultNotificationConfigs(TenantId tenantId) { - // do nothing - } - -} diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java index 851550eee7..379f7648ad 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java @@ -15,15 +15,25 @@ */ package org.thingsboard.server.service.notification; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.SettableFuture; import lombok.extern.slf4j.Slf4j; import org.assertj.core.data.Offset; import org.java_websocket.client.WebSocketClient; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.client.RestTemplate; import org.thingsboard.rule.engine.api.NotificationCenter; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.NotificationRequestId; +import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.NotificationTargetId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.NotificationRequest; @@ -33,32 +43,43 @@ import org.thingsboard.server.common.data.notification.NotificationRequestPrevie import org.thingsboard.server.common.data.notification.NotificationRequestStats; import org.thingsboard.server.common.data.notification.NotificationRequestStatus; import org.thingsboard.server.common.data.notification.NotificationType; +import org.thingsboard.server.common.data.notification.info.EntityActionNotificationInfo; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; import org.thingsboard.server.common.data.notification.settings.SlackNotificationDeliveryMethodConfig; +import org.thingsboard.server.common.data.notification.settings.UserNotificationSettings; +import org.thingsboard.server.common.data.notification.targets.MicrosoftTeamsNotificationTargetConfig; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter; import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.platform.SystemAdministratorsFilter; import org.thingsboard.server.common.data.notification.targets.platform.UserListFilter; import org.thingsboard.server.common.data.notification.targets.slack.SlackConversation; import org.thingsboard.server.common.data.notification.targets.slack.SlackConversationType; import org.thingsboard.server.common.data.notification.targets.slack.SlackNotificationTargetConfig; import org.thingsboard.server.common.data.notification.template.DeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.EmailDeliveryMethodNotificationTemplate; +import org.thingsboard.server.common.data.notification.template.MicrosoftTeamsDeliveryMethodNotificationTemplate; +import org.thingsboard.server.common.data.notification.template.MicrosoftTeamsDeliveryMethodNotificationTemplate.Button.LinkType; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig; import org.thingsboard.server.common.data.notification.template.SlackDeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.SmsDeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.dao.notification.DefaultNotifications; import org.thingsboard.server.dao.notification.NotificationDao; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.service.executors.DbCallbackExecutorService; +import org.thingsboard.server.service.notification.channels.MicrosoftTeamsNotificationChannel; import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsUpdate; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.UUID; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; @@ -67,6 +88,8 @@ import static org.awaitility.Awaitility.await; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; @DaoSqlTest @@ -79,6 +102,8 @@ public class NotificationApiTest extends AbstractNotificationApiTest { private NotificationDao notificationDao; @Autowired private DbCallbackExecutorService executor; + @Autowired + private MicrosoftTeamsNotificationChannel microsoftTeamsNotificationChannel; @Before public void beforeEach() throws Exception { @@ -89,15 +114,13 @@ public class NotificationApiTest extends AbstractNotificationApiTest { @Test public void testSubscribingToUnreadNotificationsCount() { + wsClient.subscribeForUnreadNotificationsCount().waitForReply(true); NotificationTarget notificationTarget = createNotificationTarget(customerUserId); String notificationText1 = "Notification 1"; submitNotificationRequest(notificationTarget.getId(), notificationText1); String notificationText2 = "Notification 2"; submitNotificationRequest(notificationTarget.getId(), notificationText2); - wsClient.subscribeForUnreadNotificationsCount(); - wsClient.waitForReply(true); - await().atMost(2, TimeUnit.SECONDS) .until(() -> wsClient.getLastCountUpdate().getTotalUnreadCount() == 2); } @@ -259,6 +282,25 @@ public class NotificationApiTest extends AbstractNotificationApiTest { assertThat(getMyNotifications(false, 10)).size().isZero(); } + @Test + public void whenTenantIsDeleted_thenDeleteNotificationRequests() throws Exception { + createDifferentTenant(); + TenantId tenantId = differentTenantId; + NotificationTarget target = createNotificationTarget(savedDifferentTenantUser.getId()); + int notificationsCount = 20; + for (int i = 0; i < notificationsCount; i++) { + NotificationRequest request = submitNotificationRequest(target.getId(), "Test " + i, NotificationDeliveryMethod.WEB); + awaitNotificationRequest(request.getId()); + } + List requests = notificationRequestService.findNotificationRequestsByTenantIdAndOriginatorType(tenantId, EntityType.USER, new PageLink(100)).getData(); + assertThat(requests).size().isEqualTo(notificationsCount); + + deleteDifferentTenant(); + + assertThat(notificationRequestService.findNotificationRequestsByTenantIdAndOriginatorType(tenantId, EntityType.USER, new PageLink(1)).getTotalElements()) + .isZero(); + } + @Test public void testNotificationUpdatesForSeveralUsers() throws Exception { int usersCount = 150; @@ -470,6 +512,66 @@ public class NotificationApiTest extends AbstractNotificationApiTest { assertThat(stats.getSent().get(NotificationDeliveryMethod.WEB)).hasValue(1); } + @Test + public void testUserNotificationSettings() throws Exception { + var entityActionNotificationPref = new UserNotificationSettings.NotificationPref(); + entityActionNotificationPref.setEnabled(true); + entityActionNotificationPref.setEnabledDeliveryMethods(Map.of( + NotificationDeliveryMethod.WEB, true, + NotificationDeliveryMethod.SMS, false, + NotificationDeliveryMethod.EMAIL, false + )); + + var entitiesLimitNotificationPref = new UserNotificationSettings.NotificationPref(); + entitiesLimitNotificationPref.setEnabled(true); + entitiesLimitNotificationPref.setEnabledDeliveryMethods(Map.of( + NotificationDeliveryMethod.SMS, true, + NotificationDeliveryMethod.WEB, false, + NotificationDeliveryMethod.EMAIL, false + )); + + var apiUsageLimitNotificationPref = new UserNotificationSettings.NotificationPref(); + apiUsageLimitNotificationPref.setEnabled(false); + apiUsageLimitNotificationPref.setEnabledDeliveryMethods(Map.of( + NotificationDeliveryMethod.WEB, true, + NotificationDeliveryMethod.SMS, false, + NotificationDeliveryMethod.EMAIL, false + )); + + UserNotificationSettings settings = new UserNotificationSettings(Map.of( + NotificationType.ENTITY_ACTION, entityActionNotificationPref, + NotificationType.ENTITIES_LIMIT, entitiesLimitNotificationPref, + NotificationType.API_USAGE_LIMIT, apiUsageLimitNotificationPref + )); + doPost("/api/notification/settings/user", settings, UserNotificationSettings.class); + + var entityActionNotificationTemplate = createNotificationTemplate(NotificationType.ENTITY_ACTION, "Entity action", "Entity action", NotificationDeliveryMethod.WEB); + var entitiesLimitNotificationTemplate = createNotificationTemplate(NotificationType.ENTITIES_LIMIT, "Entities limit", "Entities limit", NotificationDeliveryMethod.WEB); + var apiUsageLimitNotificationTemplate = createNotificationTemplate(NotificationType.API_USAGE_LIMIT, "API usage limit", "API usage limit", NotificationDeliveryMethod.WEB); + NotificationTarget target = createNotificationTarget(tenantAdminUserId); + + NotificationRequest notificationRequest = NotificationRequest.builder() + .tenantId(tenantId) + .templateId(entityActionNotificationTemplate.getId()) + .originatorEntityId(tenantAdminUserId) + .targets(List.of(target.getUuidId())) + .ruleId(new NotificationRuleId(UUID.randomUUID())) // to trigger user settings check + .build(); + NotificationRequestStats stats = submitNotificationRequestAndWait(notificationRequest); + assertThat(stats.getErrors()).isEmpty(); + assertThat(stats.getSent().get(NotificationDeliveryMethod.WEB).get()).isOne(); + + notificationRequest.setTemplateId(entitiesLimitNotificationTemplate.getId()); + stats = submitNotificationRequestAndWait(notificationRequest); + assertThat(stats.getSent().get(NotificationDeliveryMethod.WEB)).matches(n -> n == null || n.get() == 0); + assertThat(stats.getErrors().get(NotificationDeliveryMethod.WEB).values()).first().asString().contains("disabled"); + + notificationRequest.setTemplateId(apiUsageLimitNotificationTemplate.getId()); + stats = submitNotificationRequestAndWait(notificationRequest); + assertThat(stats.getSent().get(NotificationDeliveryMethod.WEB)).matches(n -> n == null || n.get() == 0); + assertThat(stats.getErrors().get(NotificationDeliveryMethod.WEB).values()).first().asString().contains("disabled"); + } + @Test public void testSlackNotifications() throws Exception { NotificationSettings settings = new NotificationSettings(); @@ -524,6 +626,109 @@ public class NotificationApiTest extends AbstractNotificationApiTest { assertThat(stats.getErrors().get(NotificationDeliveryMethod.SLACK).values()).containsExactly(errorMessage); } + @Test + public void testInternalGeneralWebNotifications() throws Exception { + loginSysAdmin(); + getAnotherWsClient().subscribeForUnreadNotifications(10).waitForReply(true); + + getAnotherWsClient().registerWaitForUpdate(); + + DefaultNotifications.DefaultNotification expectedNotification = DefaultNotifications.maintenanceWork; + notificationCenter.sendGeneralWebNotification(TenantId.SYS_TENANT_ID, new SystemAdministratorsFilter(), + expectedNotification.toTemplate()); + + getAnotherWsClient().waitForUpdate(true); + Notification notification = getAnotherWsClient().getLastDataUpdate().getUpdate(); + assertThat(notification.getSubject()).isEqualTo(expectedNotification.getSubject()); + assertThat(notification.getText()).isEqualTo(expectedNotification.getText()); + } + + @Test + public void testMicrosoftTeamsNotifications() throws Exception { + RestTemplate restTemplate = mock(RestTemplate.class); + microsoftTeamsNotificationChannel.setRestTemplate(restTemplate); + + String webhookUrl = "https://webhook.com/webhookb2/9628fa60-d873-11ed-913c-a196b1f9b445"; + var targetConfig = new MicrosoftTeamsNotificationTargetConfig(); + targetConfig.setWebhookUrl(webhookUrl); + targetConfig.setChannelName("My channel"); + NotificationTarget target = new NotificationTarget(); + target.setName("Microsoft Teams channel"); + target.setConfiguration(targetConfig); + target = saveNotificationTarget(target); + + var template = new MicrosoftTeamsDeliveryMethodNotificationTemplate(); + template.setEnabled(true); + String templateParams = "${recipientTitle} - ${entityType}"; + template.setSubject("Subject: " + templateParams); + template.setBody("Body: " + templateParams); + template.setThemeColor("ff0000"); + var button = new MicrosoftTeamsDeliveryMethodNotificationTemplate.Button(); + button.setEnabled(true); + button.setText("Button: " + templateParams); + button.setLinkType(LinkType.LINK); + button.setLink("https://" + templateParams); + template.setButton(button); + NotificationTemplate notificationTemplate = new NotificationTemplate(); + notificationTemplate.setName("Notification to Teams"); + notificationTemplate.setNotificationType(NotificationType.GENERAL); + NotificationTemplateConfig templateConfig = new NotificationTemplateConfig(); + templateConfig.setDeliveryMethodsTemplates(Map.of( + NotificationDeliveryMethod.MICROSOFT_TEAMS, template + )); + notificationTemplate.setConfiguration(templateConfig); + notificationTemplate = saveNotificationTemplate(notificationTemplate); + + NotificationRequest notificationRequest = NotificationRequest.builder() + .tenantId(tenantId) + .originatorEntityId(tenantAdminUserId) + .templateId(notificationTemplate.getId()) + .targets(List.of(target.getUuidId())) + .info(EntityActionNotificationInfo.builder() + .entityId(new DeviceId(UUID.randomUUID())) + .actionType(ActionType.ADDED) + .userId(tenantAdminUserId.getId()) + .build()) + .build(); + + NotificationRequestPreview preview = doPost("/api/notification/request/preview", notificationRequest, NotificationRequestPreview.class); + assertThat(preview.getRecipientsCountByTarget().get(target.getName())).isEqualTo(1); + assertThat(preview.getRecipientsPreview()).containsOnly(targetConfig.getChannelName()); + + var messageCaptor = ArgumentCaptor.forClass(MicrosoftTeamsNotificationChannel.Message.class); + notificationCenter.processNotificationRequest(tenantId, notificationRequest, null); + verify(restTemplate, timeout(20000)).postForEntity(eq(webhookUrl), messageCaptor.capture(), any()); + + var message = messageCaptor.getValue(); + String expectedParams = "My channel - Device"; + assertThat(message.getThemeColor()).isEqualTo(template.getThemeColor()); + assertThat(message.getSections().get(0).getActivityTitle()).isEqualTo("Subject: " + expectedParams); + assertThat(message.getSections().get(0).getActivitySubtitle()).isEqualTo("Body: " + expectedParams); + assertThat(message.getPotentialAction().get(0).getName()).isEqualTo("Button: " + expectedParams); + assertThat(message.getPotentialAction().get(0).getTargets().get(0).getUri()).isEqualTo("https://" + expectedParams); + } + + private NotificationRequestStats submitNotificationRequestAndWait(NotificationRequest notificationRequest) throws Exception { + SettableFuture future = SettableFuture.create(); + notificationCenter.processNotificationRequest(notificationRequest.getTenantId(), notificationRequest, new FutureCallback<>() { + @Override + public void onSuccess(NotificationRequestStats result) { + future.set(result); + } + + @Override + public void onFailure(Throwable t) { + future.setException(t); + } + }); + return future.get(30, TimeUnit.SECONDS); + } + + private NotificationRequestStats awaitNotificationRequest(NotificationRequestId requestId) { + return await().atMost(30, TimeUnit.SECONDS) + .until(() -> getStats(requestId), Objects::nonNull); + } + private void checkFullNotificationsUpdate(UnreadNotificationsUpdate notificationsUpdate, String... expectedNotifications) { assertThat(notificationsUpdate.getNotifications()).extracting(Notification::getText).containsOnly(expectedNotifications); assertThat(notificationsUpdate.getNotifications()).extracting(Notification::getType).containsOnly(DEFAULT_NOTIFICATION_TYPE); @@ -540,7 +745,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest { protected void connectOtherWsClient() throws Exception { loginCustomerUser(); - otherWsClient = (NotificationApiWsClient) super.getAnotherWsClient(); + otherWsClient = super.getAnotherWsClient(); loginTenantAdmin(); } diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiWsClient.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiWsClient.java index 664ff7bd54..d96d04b31a 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiWsClient.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiWsClient.java @@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.controller.TbTestWebSocketClient; import org.thingsboard.server.service.ws.notification.cmd.MarkAllNotificationsAsReadCmd; import org.thingsboard.server.service.ws.notification.cmd.MarkNotificationsAsReadCmd; -import org.thingsboard.server.service.ws.notification.cmd.NotificationCmdsWrapper; import org.thingsboard.server.service.ws.notification.cmd.NotificationsCountSubCmd; import org.thingsboard.server.service.ws.notification.cmd.NotificationsSubCmd; import org.thingsboard.server.service.ws.notification.cmd.UnreadNotificationsCountUpdate; @@ -49,40 +48,27 @@ public class NotificationApiWsClient extends TbTestWebSocketClient { private int unreadCount; private List notifications; - public NotificationApiWsClient(String wsUrl, String token) throws URISyntaxException { - super(new URI(wsUrl + "/api/ws/plugins/notifications?token=" + token)); + public NotificationApiWsClient(String wsUrl) throws URISyntaxException { + super(new URI(wsUrl + "/api/ws")); } public NotificationApiWsClient subscribeForUnreadNotifications(int limit) { - NotificationCmdsWrapper cmdsWrapper = new NotificationCmdsWrapper(); - cmdsWrapper.setUnreadSubCmd(new NotificationsSubCmd(1, limit)); - sendCmd(cmdsWrapper); + send(new NotificationsSubCmd(1, limit)); this.limit = limit; return this; } public NotificationApiWsClient subscribeForUnreadNotificationsCount() { - NotificationCmdsWrapper cmdsWrapper = new NotificationCmdsWrapper(); - cmdsWrapper.setUnreadCountSubCmd(new NotificationsCountSubCmd(2)); - sendCmd(cmdsWrapper); + send(new NotificationsCountSubCmd(2)); return this; } public void markNotificationAsRead(UUID... notifications) { - NotificationCmdsWrapper cmdsWrapper = new NotificationCmdsWrapper(); - cmdsWrapper.setMarkAsReadCmd(new MarkNotificationsAsReadCmd(newCmdId(), Arrays.asList(notifications))); - sendCmd(cmdsWrapper); + send(new MarkNotificationsAsReadCmd(newCmdId(), Arrays.asList(notifications))); } public void markAllNotificationsAsRead() { - NotificationCmdsWrapper cmdsWrapper = new NotificationCmdsWrapper(); - cmdsWrapper.setMarkAllAsReadCmd(new MarkAllNotificationsAsReadCmd(newCmdId())); - sendCmd(cmdsWrapper); - } - - public void sendCmd(NotificationCmdsWrapper cmdsWrapper) { - String cmd = JacksonUtil.toString(cmdsWrapper); - send(cmd); + send(new MarkAllNotificationsAsReadCmd(newCmdId())); } @Override diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java index 6c4413d1e7..0909510243 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java @@ -51,26 +51,34 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; import org.thingsboard.server.common.data.device.profile.SimpleAlarmConditionSpec; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.data.notification.Notification; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.notification.NotificationRequestInfo; import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.info.AlarmNotificationInfo; +import org.thingsboard.server.common.data.notification.info.RateLimitsNotificationInfo; import org.thingsboard.server.common.data.notification.rule.DefaultNotificationRuleRecipientsConfig; import org.thingsboard.server.common.data.notification.rule.EscalatedNotificationRuleRecipientsConfig; import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo; -import org.thingsboard.server.common.data.notification.rule.trigger.AlarmAssignmentNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.AlarmCommentNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig.AlarmAction; -import org.thingsboard.server.common.data.notification.rule.trigger.DeviceActivityNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.EntitiesLimitNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.EntityActionNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.NewPlatformVersionNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.notification.rule.trigger.NewPlatformVersionTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.RateLimitsTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmAssignmentNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmCommentNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmNotificationRuleTriggerConfig.AlarmAction; +import org.thingsboard.server.common.data.notification.rule.trigger.config.DeviceActivityNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EntitiesLimitNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EntityActionNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NewPlatformVersionNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.notification.rule.trigger.config.RateLimitsNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; +import org.thingsboard.server.common.data.notification.targets.platform.AffectedTenantAdministratorsFilter; +import org.thingsboard.server.common.data.notification.targets.platform.SystemAdministratorsFilter; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -81,13 +89,12 @@ import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; -import org.thingsboard.server.common.msg.notification.trigger.NewPlatformVersionTrigger; import org.thingsboard.server.dao.notification.DefaultNotifications; import org.thingsboard.server.dao.notification.NotificationRequestService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.dao.util.limits.LimitedApi; import org.thingsboard.server.dao.util.limits.RateLimitService; +import org.thingsboard.server.queue.notification.DefaultNotificationDeduplicationService; import org.thingsboard.server.service.notification.rule.cache.DefaultNotificationRulesCache; import org.thingsboard.server.service.state.DeviceStateService; import org.thingsboard.server.service.telemetry.AlarmSubscriptionService; @@ -103,20 +110,22 @@ import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.offset; import static org.assertj.core.api.InstanceOfAssertFactories.type; import static org.awaitility.Awaitility.await; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.thingsboard.server.common.data.notification.rule.trigger.AlarmAssignmentNotificationRuleTriggerConfig.Action.ASSIGNED; -import static org.thingsboard.server.common.data.notification.rule.trigger.AlarmAssignmentNotificationRuleTriggerConfig.Action.UNASSIGNED; -import static org.thingsboard.server.common.data.notification.rule.trigger.DeviceActivityNotificationRuleTriggerConfig.DeviceEvent.ACTIVE; -import static org.thingsboard.server.common.data.notification.rule.trigger.DeviceActivityNotificationRuleTriggerConfig.DeviceEvent.INACTIVE; +import static org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmAssignmentNotificationRuleTriggerConfig.Action.ASSIGNED; +import static org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmAssignmentNotificationRuleTriggerConfig.Action.UNASSIGNED; +import static org.thingsboard.server.common.data.notification.rule.trigger.config.DeviceActivityNotificationRuleTriggerConfig.DeviceEvent.ACTIVE; +import static org.thingsboard.server.common.data.notification.rule.trigger.config.DeviceActivityNotificationRuleTriggerConfig.DeviceEvent.INACTIVE; @DaoSqlTest @TestPropertySource(properties = { - "transport.http.enabled=true" + "transport.http.enabled=true", + "notification_system.rules.deduplication_durations=RATE_LIMITS:10000" }) public class NotificationRuleApiTest extends AbstractNotificationApiTest { @@ -221,8 +230,8 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { doPost("/api/plugins/telemetry/" + device.getId() + "/" + DataConstants.SHARED_SCOPE, attr); await().atMost(10, TimeUnit.SECONDS) - .until(() -> alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType).get() != null); - Alarm alarm = alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType).get(); + .until(() -> alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType) != null); + Alarm alarm = alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType); long ts = System.currentTimeMillis(); await().atMost(15, TimeUnit.SECONDS) @@ -260,6 +269,31 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { }); } + @Test + public void testNotificationRuleProcessing_alarmTrigger_createViaRestApi() throws Exception { + Device device = createDevice("Device with alarm", "233"); + NotificationTarget target = createNotificationTarget(tenantAdminUserId); + defaultNotifications.create(tenantId, DefaultNotifications.newAlarm, target.getId()); + defaultNotifications.create(tenantId, DefaultNotifications.entityAction, target.getId()); + notificationRulesCache.evict(tenantId); + + Alarm alarm = new Alarm(); + alarm.setSeverity(AlarmSeverity.CRITICAL); + alarm.setType("testAlarm"); + alarm.setOriginator(device.getId()); + alarm = doPost("/api/alarm", alarm, Alarm.class); + + await().atMost(15, TimeUnit.SECONDS) + .pollDelay(2, TimeUnit.SECONDS) + .untilAsserted(() -> { + List notifications = getMyNotifications(false, 10); + assertThat(notifications).hasSize(1).first().matches(notification -> { + return notification.getType() == NotificationType.ALARM && + notification.getSubject().equals("New alarm 'testAlarm'"); + }); + }); + } + @Test public void testNotificationRuleProcessing_alarmTrigger_clearRule() throws Exception { String notificationSubject = "${alarmSeverity} alarm '${alarmType}' is ${alarmStatus}"; @@ -304,8 +338,8 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { doPost("/api/plugins/telemetry/" + device.getId() + "/" + DataConstants.SHARED_SCOPE, attr); await().atMost(10, TimeUnit.SECONDS) - .until(() -> alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType).get() != null); - Alarm alarm = alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType).get(); + .until(() -> alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType) != null); + Alarm alarm = alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType); getWsClient().waitForUpdate(true); Notification notification = getWsClient().getLastDataUpdate().getUpdate(); @@ -333,7 +367,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { @Test public void testNotificationRuleProcessing_entitiesLimit() throws Exception { int limit = 5; - updateDefaultTenantProfile(profileConfiguration -> { + updateDefaultTenantProfileConfig(profileConfiguration -> { profileConfiguration.setMaxDevices(limit); profileConfiguration.setMaxAssets(limit); profileConfiguration.setMaxCustomers(limit); @@ -400,6 +434,65 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { }); } + @Test + public void testNotificationRuleProcessing_exceededRateLimits() throws Exception { + loginSysAdmin(); + NotificationTarget sysadmins = createNotificationTarget(new SystemAdministratorsFilter()); + NotificationTarget affectedTenantAdmins = createNotificationTarget(new AffectedTenantAdministratorsFilter()); + defaultNotifications.create(TenantId.SYS_TENANT_ID, DefaultNotifications.exceededRateLimitsForSysadmin, sysadmins.getId()); + defaultNotifications.create(TenantId.SYS_TENANT_ID, DefaultNotifications.exceededRateLimits, affectedTenantAdmins.getId()); + defaultNotifications.create(TenantId.SYS_TENANT_ID, DefaultNotifications.exceededPerEntityRateLimits, affectedTenantAdmins.getId()); + notificationRulesCache.evict(TenantId.SYS_TENANT_ID); + + int n = 10; + updateDefaultTenantProfileConfig(profileConfiguration -> { + profileConfiguration.setTenantEntityExportRateLimit(n + ":600"); + profileConfiguration.setCustomerServerRestLimitsConfiguration(n + ":600"); + profileConfiguration.setTenantNotificationRequestsPerRuleRateLimit(n + ":600"); + profileConfiguration.setTransportDeviceTelemetryMsgRateLimit(n + ":600"); + }); + loginTenantAdmin(); + NotificationRule rule = createNotificationRule(AlarmCommentNotificationRuleTriggerConfig.builder() + .alarmTypes(Set.of("weklfjkwefa")) + .build(), "Test", "Test", createNotificationTarget(tenantAdminUserId).getId()); + for (int i = 1; i <= n * 2; i++) { + rateLimitService.checkRateLimit(LimitedApi.ENTITY_EXPORT, tenantId); + rateLimitService.checkRateLimit(LimitedApi.REST_REQUESTS_PER_CUSTOMER, tenantId, customerId); + rateLimitService.checkRateLimit(LimitedApi.NOTIFICATION_REQUESTS_PER_RULE, tenantId, rule.getId()); + Thread.sleep(100); + } + + loginTenantAdmin(); + List notifications = await().atMost(15, TimeUnit.SECONDS) + .until(() -> getMyNotifications(true, 10).stream() + .filter(notification -> notification.getType() == NotificationType.RATE_LIMITS) + .collect(Collectors.toList()), list -> list.size() == 3); + assertThat(notifications).allSatisfy(notification -> { + assertThat(notification.getSubject()).isEqualTo("Rate limits exceeded"); + }); + assertThat(notifications).anySatisfy(notification -> { + assertThat(notification.getText()).isEqualTo("Rate limits for entity version creation exceeded"); + }); + assertThat(notifications).anySatisfy(notification -> { + assertThat(notification.getText()).isEqualTo("Rate limits for REST API requests per customer " + + "exceeded for 'Customer'"); + }); + assertThat(notifications).anySatisfy(notification -> { + assertThat(notification.getText()).isEqualTo("Rate limits for notification requests " + + "per rule exceeded for '" + rule.getName() + "'"); + }); + + loginSysAdmin(); + notifications = await().atMost(15, TimeUnit.SECONDS) + .until(() -> getMyNotifications(true, 10).stream() + .filter(notification -> notification.getType() == NotificationType.RATE_LIMITS) + .collect(Collectors.toList()), list -> list.size() == 1); + assertThat(notifications).singleElement().satisfies(notification -> { + assertThat(notification.getSubject()).isEqualTo("Rate limits exceeded for tenant " + TEST_TENANT_NAME); + assertThat(notification.getText()).isEqualTo("Rate limits for entity version creation exceeded"); + }); + } + @Test public void testNotificationRuleProcessing_alarmAssignment() throws Exception { AlarmAssignmentNotificationRuleTriggerConfig triggerConfig = AlarmAssignmentNotificationRuleTriggerConfig.builder() @@ -545,7 +638,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { @Test public void testNotificationRequestsPerRuleRateLimits() throws Exception { int notificationRequestsLimit = 10; - updateDefaultTenantProfile(profileConfiguration -> { + updateDefaultTenantProfileConfig(profileConfiguration -> { profileConfiguration.setTenantNotificationRequestsPerRuleRateLimit(notificationRequestsLimit + ":300"); }); @@ -617,6 +710,68 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { }); } + @Test + public void testNotificationsDeduplication_exceededRateLimits() throws Exception { + RateLimitsNotificationRuleTriggerConfig triggerConfig = new RateLimitsNotificationRuleTriggerConfig(); + triggerConfig.setApis(Set.of(LimitedApi.ENTITY_EXPORT, LimitedApi.TRANSPORT_MESSAGES_PER_DEVICE)); + + loginSysAdmin(); + NotificationTarget target = createNotificationTarget(tenantAdminUserId); + NotificationRule rule = createNotificationRule(triggerConfig, "Test 1", "Test", target.getId()); + + int n = 5; + updateDefaultTenantProfile(profileConfiguration -> { + profileConfiguration.getProfileConfiguration().get().setTenantEntityExportRateLimit(n + ":600"); + profileConfiguration.getProfileConfiguration().get().setTransportDeviceTelemetryMsgRateLimit(n + ":800"); + }); + + RateLimitsTrigger expectedTrigger = RateLimitsTrigger.builder() + .tenantId(tenantId) + .api(LimitedApi.ENTITY_EXPORT) + .limitLevel(tenantId) + .build(); + assertThat(DefaultNotificationDeduplicationService.getDeduplicationKey(expectedTrigger, rule)) + .isEqualTo("RATE_LIMITS:TENANT:" + tenantId + ":ENTITY_EXPORT_" + + target.getId() + ":ENTITY_EXPORT,TRANSPORT_MESSAGES_PER_DEVICE"); + + loginTenantAdmin(); + getWsClient().subscribeForUnreadNotifications(10).waitForReply(); + getWsClient().registerWaitForUpdate(2); + Device device = createDevice("Test", "Test"); + for (int i = 1; i <= n + 1; i++) { + rateLimitService.checkRateLimit(LimitedApi.ENTITY_EXPORT, tenantId); + doPost("/api/v1/" + device.getName() + "/telemetry", "{\"dp1\":123}", String.class); + } + int expectedNotificationsCount1 = 2; + getWsClient().waitForUpdate(true); + List notifications1 = getMyNotifications(true, 10); + assertThat(notifications1).size().isEqualTo(expectedNotificationsCount1); + assertThat(notifications1) + .anyMatch(notification -> ((RateLimitsNotificationInfo) notification.getInfo()).getApi() == LimitedApi.ENTITY_EXPORT) + .anyMatch(notification -> ((RateLimitsNotificationInfo) notification.getInfo()).getApi() == LimitedApi.TRANSPORT_MESSAGES_PER_DEVICE); + + getWsClient().registerWaitForUpdate(2); + for (int i = 0; i < 10; i++) { + rateLimitService.checkRateLimit(LimitedApi.ENTITY_EXPORT, tenantId); + doPost("/api/v1/" + device.getName() + "/telemetry", "{\"dp1\":123}", String.class); + } + assertThat(getWsClient().waitForUpdate(5000)).isNull(); + + int deduplicationDuration = 10000; // configured in TestPropertySource above + await().atLeast(2, TimeUnit.SECONDS) + .atMost(deduplicationDuration, TimeUnit.MILLISECONDS) + .untilAsserted(() -> { + rateLimitService.checkRateLimit(LimitedApi.ENTITY_EXPORT, tenantId); + doPost("/api/v1/" + device.getName() + "/telemetry", "{\"dp1\":123}", String.class); + + Map notifications2 = getMyNotifications(true, 10).stream() + .map(notification -> (RateLimitsNotificationInfo) notification.getInfo()) + .collect(Collectors.groupingBy(RateLimitsNotificationInfo::getApi, Collectors.counting())); + assertThat(notifications2.get(LimitedApi.ENTITY_EXPORT)).isEqualTo(2); + assertThat(notifications2.get(LimitedApi.TRANSPORT_MESSAGES_PER_DEVICE)).isEqualTo(2); + }); + } + @Test public void testNotificationRuleDisabling() throws Exception { EntityActionNotificationRuleTriggerConfig triggerConfig = new EntityActionNotificationRuleTriggerConfig(); diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationTargetApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationTargetApiTest.java index 480bbf939b..00dea12534 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationTargetApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationTargetApiTest.java @@ -24,7 +24,8 @@ import org.springframework.test.web.servlet.ResultMatcher; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; -import org.thingsboard.server.common.data.notification.rule.trigger.EntityActionNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.NotificationType; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EntityActionNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.platform.AllUsersFilter; import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter; @@ -144,6 +145,7 @@ public class NotificationTargetApiTest extends AbstractNotificationApiTest { notificationTarget.setConfiguration(targetConfig); save(notificationTarget, status().isOk()); assertThat(notificationTargetDao.findByTenantIdAndPageLink(differentTenantId, new PageLink(10)).getData()).isNotEmpty(); + assertThat(notificationTargetDao.findByTenantIdAndSupportedNotificationTypeAndPageLink(differentTenantId, NotificationType.GENERAL, new PageLink(10)).getData()).isNotEmpty(); deleteDifferentTenant(); assertThat(notificationTargetDao.findByTenantIdAndPageLink(differentTenantId, new PageLink(10)).getData()).isEmpty(); diff --git a/application/src/test/java/org/thingsboard/server/service/notification/TestNotificationSettingsService.java b/application/src/test/java/org/thingsboard/server/service/notification/TestNotificationSettingsService.java new file mode 100644 index 0000000000..9b4ced8567 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/notification/TestNotificationSettingsService.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.notification; + +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.notification.DefaultNotificationSettingsService; +import org.thingsboard.server.dao.notification.NotificationTargetService; +import org.thingsboard.server.dao.notification.NotificationTemplateService; +import org.thingsboard.server.dao.settings.AdminSettingsService; +import org.thingsboard.server.dao.user.UserSettingsService; + +@Service +@Primary +public class TestNotificationSettingsService extends DefaultNotificationSettingsService { + + public TestNotificationSettingsService(AdminSettingsService adminSettingsService, + NotificationTargetService notificationTargetService, + NotificationTemplateService notificationTemplateService, + UserSettingsService userSettingsService) { + super(adminSettingsService, notificationTargetService, notificationTemplateService, null, userSettingsService); + } + + @Override + public void createDefaultNotificationConfigs(TenantId tenantId) { + // do nothing + } + +} diff --git a/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java b/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java index 0bc1800896..233839bb5f 100644 --- a/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java @@ -32,7 +32,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.util.DataDecodingEncodingService; @@ -78,7 +78,7 @@ public class DefaultTbClusterServiceTest { protected TbQueueProducerProvider producerProvider; @SpyBean - protected NotificationsTopicService notificationsTopicService; + protected TopicService topicService; @SpyBean protected TbClusterService clusterService; @@ -94,12 +94,12 @@ public class DefaultTbClusterServiceTest { clusterService.onQueueChange(createTestQueue()); - verify(notificationsTopicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, MONOLITH); - verify(notificationsTopicService, never()).getNotificationsTopic(eq(ServiceType.TB_CORE), any()); - verify(notificationsTopicService, never()).getNotificationsTopic(eq(ServiceType.TB_TRANSPORT), any()); + verify(topicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, MONOLITH); + verify(topicService, never()).getNotificationsTopic(eq(ServiceType.TB_CORE), any()); + verify(topicService, never()).getNotificationsTopic(eq(ServiceType.TB_TRANSPORT), any()); verify(tbQueueProducer, times(1)) - .send(eq(notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, MONOLITH)), any(TbProtoQueueMsg.class), isNull()); + .send(eq(topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, MONOLITH)), any(TbProtoQueueMsg.class), isNull()); verify(producerProvider, never()).getTbCoreNotificationsMsgProducer(); verify(producerProvider, never()).getTransportNotificationsMsgProducer(); @@ -119,15 +119,15 @@ public class DefaultTbClusterServiceTest { clusterService.onQueueChange(createTestQueue()); - verify(notificationsTopicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, monolith1); - verify(notificationsTopicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, monolith2); - verify(notificationsTopicService, never()).getNotificationsTopic(eq(ServiceType.TB_CORE), any()); - verify(notificationsTopicService, never()).getNotificationsTopic(eq(ServiceType.TB_TRANSPORT), any()); + verify(topicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, monolith1); + verify(topicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, monolith2); + verify(topicService, never()).getNotificationsTopic(eq(ServiceType.TB_CORE), any()); + verify(topicService, never()).getNotificationsTopic(eq(ServiceType.TB_TRANSPORT), any()); verify(tbQueueProducer, times(1)) - .send(eq(notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, monolith1)), any(TbProtoQueueMsg.class), isNull()); + .send(eq(topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, monolith1)), any(TbProtoQueueMsg.class), isNull()); verify(tbQueueProducer, times(1)) - .send(eq(notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, monolith2)), any(TbProtoQueueMsg.class), isNull()); + .send(eq(topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, monolith2)), any(TbProtoQueueMsg.class), isNull()); verify(producerProvider, never()).getTbCoreNotificationsMsgProducer(); verify(producerProvider, never()).getTransportNotificationsMsgProducer(); @@ -147,20 +147,20 @@ public class DefaultTbClusterServiceTest { clusterService.onQueueChange(createTestQueue()); - verify(notificationsTopicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, MONOLITH); - verify(notificationsTopicService, times(1)).getNotificationsTopic(ServiceType.TB_TRANSPORT, TRANSPORT); - verify(notificationsTopicService, never()).getNotificationsTopic(eq(ServiceType.TB_CORE), any()); + verify(topicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, MONOLITH); + verify(topicService, times(1)).getNotificationsTopic(ServiceType.TB_TRANSPORT, TRANSPORT); + verify(topicService, never()).getNotificationsTopic(eq(ServiceType.TB_CORE), any()); verify(tbREQueueProducer, times(1)) - .send(eq(notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, MONOLITH)), any(TbProtoQueueMsg.class), isNull()); + .send(eq(topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, MONOLITH)), any(TbProtoQueueMsg.class), isNull()); verify(tbTransportQueueProducer, times(1)) - .send(eq(notificationsTopicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, TRANSPORT)), any(TbProtoQueueMsg.class), isNull()); + .send(eq(topicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, TRANSPORT)), any(TbProtoQueueMsg.class), isNull()); verify(tbTransportQueueProducer, never()) - .send(eq(notificationsTopicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, MONOLITH)), any(TbProtoQueueMsg.class), isNull()); + .send(eq(topicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, MONOLITH)), any(TbProtoQueueMsg.class), isNull()); verify(tbTransportQueueProducer, never()) - .send(eq(notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, MONOLITH)), any(TbProtoQueueMsg.class), isNull()); + .send(eq(topicService.getNotificationsTopic(ServiceType.TB_CORE, MONOLITH)), any(TbProtoQueueMsg.class), isNull()); verify(producerProvider, never()).getTbCoreNotificationsMsgProducer(); } @@ -193,47 +193,47 @@ public class DefaultTbClusterServiceTest { clusterService.onQueueChange(createTestQueue()); - verify(notificationsTopicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, monolith1); - verify(notificationsTopicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, monolith2); - verify(notificationsTopicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, ruleEngine1); - verify(notificationsTopicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, ruleEngine2); + verify(topicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, monolith1); + verify(topicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, monolith2); + verify(topicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, ruleEngine1); + verify(topicService, times(1)).getNotificationsTopic(ServiceType.TB_RULE_ENGINE, ruleEngine2); - verify(notificationsTopicService, times(1)).getNotificationsTopic(ServiceType.TB_CORE, core1); - verify(notificationsTopicService, times(1)).getNotificationsTopic(ServiceType.TB_CORE, core2); - verify(notificationsTopicService, never()).getNotificationsTopic(ServiceType.TB_CORE, monolith1); - verify(notificationsTopicService, never()).getNotificationsTopic(ServiceType.TB_CORE, monolith2); + verify(topicService, times(1)).getNotificationsTopic(ServiceType.TB_CORE, core1); + verify(topicService, times(1)).getNotificationsTopic(ServiceType.TB_CORE, core2); + verify(topicService, never()).getNotificationsTopic(ServiceType.TB_CORE, monolith1); + verify(topicService, never()).getNotificationsTopic(ServiceType.TB_CORE, monolith2); - verify(notificationsTopicService, times(1)).getNotificationsTopic(ServiceType.TB_TRANSPORT, transport1); - verify(notificationsTopicService, times(1)).getNotificationsTopic(ServiceType.TB_TRANSPORT, transport2); - verify(notificationsTopicService, never()).getNotificationsTopic(ServiceType.TB_TRANSPORT, monolith1); - verify(notificationsTopicService, never()).getNotificationsTopic(ServiceType.TB_TRANSPORT, monolith2); + verify(topicService, times(1)).getNotificationsTopic(ServiceType.TB_TRANSPORT, transport1); + verify(topicService, times(1)).getNotificationsTopic(ServiceType.TB_TRANSPORT, transport2); + verify(topicService, never()).getNotificationsTopic(ServiceType.TB_TRANSPORT, monolith1); + verify(topicService, never()).getNotificationsTopic(ServiceType.TB_TRANSPORT, monolith2); verify(tbREQueueProducer, times(1)) - .send(eq(notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, monolith1)), any(TbProtoQueueMsg.class), isNull()); + .send(eq(topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, monolith1)), any(TbProtoQueueMsg.class), isNull()); verify(tbREQueueProducer, times(1)) - .send(eq(notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, monolith2)), any(TbProtoQueueMsg.class), isNull()); + .send(eq(topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, monolith2)), any(TbProtoQueueMsg.class), isNull()); verify(tbREQueueProducer, times(1)) - .send(eq(notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, ruleEngine1)), any(TbProtoQueueMsg.class), isNull()); + .send(eq(topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, ruleEngine1)), any(TbProtoQueueMsg.class), isNull()); verify(tbREQueueProducer, times(1)) - .send(eq(notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, ruleEngine2)), any(TbProtoQueueMsg.class), isNull()); + .send(eq(topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, ruleEngine2)), any(TbProtoQueueMsg.class), isNull()); verify(tbCoreQueueProducer, times(1)) - .send(eq(notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, core1)), any(TbProtoQueueMsg.class), isNull()); + .send(eq(topicService.getNotificationsTopic(ServiceType.TB_CORE, core1)), any(TbProtoQueueMsg.class), isNull()); verify(tbCoreQueueProducer, times(1)) - .send(eq(notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, core2)), any(TbProtoQueueMsg.class), isNull()); + .send(eq(topicService.getNotificationsTopic(ServiceType.TB_CORE, core2)), any(TbProtoQueueMsg.class), isNull()); verify(tbCoreQueueProducer, never()) - .send(eq(notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, monolith1)), any(TbProtoQueueMsg.class), isNull()); + .send(eq(topicService.getNotificationsTopic(ServiceType.TB_CORE, monolith1)), any(TbProtoQueueMsg.class), isNull()); verify(tbCoreQueueProducer, never()) - .send(eq(notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, monolith2)), any(TbProtoQueueMsg.class), isNull()); + .send(eq(topicService.getNotificationsTopic(ServiceType.TB_CORE, monolith2)), any(TbProtoQueueMsg.class), isNull()); verify(tbTransportQueueProducer, times(1)) - .send(eq(notificationsTopicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transport1)), any(TbProtoQueueMsg.class), isNull()); + .send(eq(topicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transport1)), any(TbProtoQueueMsg.class), isNull()); verify(tbTransportQueueProducer, times(1)) - .send(eq(notificationsTopicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transport2)), any(TbProtoQueueMsg.class), isNull()); + .send(eq(topicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transport2)), any(TbProtoQueueMsg.class), isNull()); verify(tbTransportQueueProducer, never()) - .send(eq(notificationsTopicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, monolith1)), any(TbProtoQueueMsg.class), isNull()); + .send(eq(topicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, monolith1)), any(TbProtoQueueMsg.class), isNull()); verify(tbTransportQueueProducer, never()) - .send(eq(notificationsTopicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, monolith2)), any(TbProtoQueueMsg.class), isNull()); + .send(eq(topicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, monolith2)), any(TbProtoQueueMsg.class), isNull()); } protected Queue createTestQueue() { diff --git a/application/src/test/java/org/thingsboard/server/service/queue/TbMsgPackCallbackTest.java b/application/src/test/java/org/thingsboard/server/service/queue/TbMsgPackCallbackTest.java new file mode 100644 index 0000000000..80b9535af3 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/queue/TbMsgPackCallbackTest.java @@ -0,0 +1,98 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.queue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.msg.queue.RuleEngineException; +import org.thingsboard.server.common.msg.queue.RuleNodeException; +import org.thingsboard.server.common.msg.tools.TbRateLimitsException; + +import java.util.UUID; +import java.util.stream.Stream; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +class TbMsgPackCallbackTest { + + TenantId tenantId; + UUID msgId; + TbMsgPackProcessingContext ctx; + TbMsgPackCallback callback; + + @BeforeEach + void setUp() { + tenantId = TenantId.fromUUID(UUID.randomUUID()); + msgId = UUID.randomUUID(); + ctx = mock(TbMsgPackProcessingContext.class); + callback = spy(new TbMsgPackCallback(msgId, tenantId, ctx)); + } + + private static Stream testOnFailure_NotRateLimitException() { + return Stream.of( + Arguments.of(new RuleEngineException("rule engine no cause")), + Arguments.of(new RuleEngineException("rule engine caused 1 lvl", new RuntimeException())), + Arguments.of(new RuleEngineException("rule engine caused 2 lvl", new RuntimeException(new Exception()))), + Arguments.of(new RuleEngineException("rule engine caused 2 lvl Throwable", new RuntimeException(new Throwable()))), + Arguments.of(new RuleNodeException("rule node no cause", "RuleChain", new RuleNode())) + ); + } + + @ParameterizedTest + @MethodSource + void testOnFailure_NotRateLimitException(RuleEngineException ree) { + callback.onFailure(ree); + + verify(callback, never()).onRateLimit(any()); + verify(callback, never()).onSuccess(); + verify(ctx, never()).onSuccess(any()); + } + + private static Stream testOnFailure_RateLimitException() { + return Stream.of( + Arguments.of(new RuleEngineException("caused lvl 1", new TbRateLimitsException(EntityType.ASSET))), + Arguments.of(new RuleEngineException("caused lvl 2", new RuntimeException(new TbRateLimitsException(EntityType.ASSET)))), + Arguments.of( + new RuleEngineException("caused lvl 3", + new RuntimeException( + new Exception( + new TbRateLimitsException(EntityType.ASSET))))) + ); + } + + @ParameterizedTest + @MethodSource + void testOnFailure_RateLimitException(RuleEngineException ree) { + callback.onFailure(ree); + + verify(callback).onRateLimit(any()); + verify(callback).onFailure(any()); + verify(callback, never()).onSuccess(); + verify(ctx).onSuccess(msgId); + verify(ctx).onSuccess(any()); + verify(ctx, never()).onFailure(any(), any(), any()); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManagerTest.java b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManagerTest.java new file mode 100644 index 0000000000..6acf71e2bb --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineQueueConsumerManagerTest.java @@ -0,0 +1,773 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.queue.ruleengine; + +import lombok.Getter; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.testcontainers.shaded.org.apache.commons.lang3.RandomUtils; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.QueueId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.queue.ProcessingStrategy; +import org.thingsboard.server.common.data.queue.ProcessingStrategyType; +import org.thingsboard.server.common.data.queue.Queue; +import org.thingsboard.server.common.data.queue.SubmitStrategy; +import org.thingsboard.server.common.data.queue.SubmitStrategyType; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; +import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.common.stats.StatsFactory; +import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; +import org.thingsboard.server.queue.TbQueueAdmin; +import org.thingsboard.server.queue.TbQueueProducer; +import org.thingsboard.server.queue.common.AbstractTbQueueConsumerTemplate; +import org.thingsboard.server.queue.common.TbProtoQueueMsg; +import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.queue.discovery.QueueKey; +import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; +import org.thingsboard.server.queue.provider.TbQueueProducerProvider; +import org.thingsboard.server.queue.provider.TbRuleEngineQueueFactory; +import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingStrategyFactory; +import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrategyFactory; +import org.thingsboard.server.service.stats.RuleEngineStatisticsService; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.after; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +@Slf4j +@RunWith(MockitoJUnitRunner.class) +public class TbRuleEngineQueueConsumerManagerTest { + + @Mock + private ActorSystemContext actorContext; + @Mock + private StatsFactory statsFactory; + @Mock + private TbRuleEngineQueueFactory queueFactory; + @Mock + private RuleEngineStatisticsService statisticsService; + @Mock + private TbServiceInfoProvider serviceInfoProvider; + @Mock + private PartitionService partitionService; + @Mock + private TbQueueProducerProvider producerProvider; + private TbQueueProducer> ruleEngineMsgProducer; + @Mock + private TbQueueAdmin queueAdmin; + private TbRuleEngineConsumerContext ruleEngineConsumerContext; + + private TbRuleEngineQueueConsumerManager consumerManager; + private Queue queue; + + private Set consumers; + private boolean generateQueueMsgs; + private AtomicInteger totalConsumedMsgs; + private AtomicInteger totalProcessedMsgs; + + @Before + public void beforeEach() { + ruleEngineConsumerContext = new TbRuleEngineConsumerContext( + actorContext, statsFactory, spy(new TbRuleEngineSubmitStrategyFactory()), + spy(new TbRuleEngineProcessingStrategyFactory()), queueFactory, statisticsService, + serviceInfoProvider, partitionService, producerProvider, queueAdmin + ); + consumers = ConcurrentHashMap.newKeySet(); + generateQueueMsgs = true; + totalConsumedMsgs = new AtomicInteger(); + totalProcessedMsgs = new AtomicInteger(); + doAnswer(inv -> { + QueueToRuleEngineMsg msg = inv.getArgument(0); + msg.getMsg().getCallback().onSuccess(); + totalProcessedMsgs.incrementAndGet(); + log.trace("totalProcessedMsgs = {}", totalProcessedMsgs); + return null; + }).when(actorContext).tell(any()); + ruleEngineMsgProducer = mock(TbQueueProducer.class); + when(producerProvider.getRuleEngineMsgProducer()).thenReturn(ruleEngineMsgProducer); + ruleEngineConsumerContext.setMgmtThreadPoolSize(2); + ruleEngineConsumerContext.setTopicDeletionDelayInSec(5); + ruleEngineConsumerContext.init(); + ruleEngineConsumerContext.setReady(false); + + queue = new Queue(); + queue.setName("Test"); + queue.setTenantId(TenantId.SYS_TENANT_ID); + queue.setId(new QueueId(UUID.randomUUID())); + queue.setTopic("tb_test"); + queue.setPartitions(10); + queue.setConsumerPerPartition(true); + queue.setPollInterval(250); + queue.setPackProcessingTimeout(2000); + SubmitStrategy submitStrategy = new SubmitStrategy(); + submitStrategy.setType(SubmitStrategyType.BURST); + submitStrategy.setBatchSize(200); + queue.setSubmitStrategy(submitStrategy); + ProcessingStrategy processingStrategy = new ProcessingStrategy(); + processingStrategy.setType(ProcessingStrategyType.SKIP_ALL_FAILURES_AND_TIMED_OUT); + processingStrategy.setRetries(0); + queue.setProcessingStrategy(processingStrategy); + + doAnswer(i -> { + TestConsumer consumer = spy(new TestConsumer(queue.getTopic())); + if (generateQueueMsgs) { + consumer.setUpTestMsg(); + } + consumers.add(consumer); + return consumer; + }).when(queueFactory).createToRuleEngineMsgConsumer(any()); + + QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queue); + consumerManager = new TbRuleEngineQueueConsumerManager(ruleEngineConsumerContext, queueKey); + } + + @After + public void afterEach() { + consumerManager.stop(); + consumerManager.awaitStop(); + ruleEngineConsumerContext.stop(); + + if (generateQueueMsgs) { + await().atMost(10, TimeUnit.SECONDS) + .untilAsserted(() -> { + log.debug("totalConsumedMsgs = {}, totalProcessedMsgs = {}", totalConsumedMsgs.get(), totalProcessedMsgs.get()); + assertThat(totalProcessedMsgs.get()).isEqualTo(totalConsumedMsgs.get()); + }); + } + } + + @Test + public void testInit_consumerPerPartition() { + queue.setConsumerPerPartition(true); + consumerManager.init(queue); + + Set partitions = createTpis(2, 3, 4); + consumerManager.update(partitions); + partitions = createTpis(3, 4, 5); + consumerManager.update(partitions); + partitions = createTpis(1, 2, 3); + consumerManager.update(partitions); + // simulated multiple partition change events before consumer is ready; only latest partitions should be processed + verifyNoInteractions(queueFactory); + + ruleEngineConsumerContext.setReady(true); + await().atMost(2, TimeUnit.SECONDS) + .until(() -> consumers.size() == 3); + for (TopicPartitionInfo partition : partitions) { + TestConsumer consumer = getConsumer(partition); + verifySubscribedAndLaunched(consumer, Set.of(partition)); + } + } + + @Test + public void testInit_singleConsumer() { + queue.setConsumerPerPartition(false); + consumerManager.init(queue); + + Set partitions = createTpis(2, 3, 4); + consumerManager.update(partitions); + partitions = createTpis(3, 4, 5); + consumerManager.update(partitions); + partitions = createTpis(1, 2, 3); + consumerManager.update(partitions); + + verifyNoInteractions(queueFactory); + + ruleEngineConsumerContext.setReady(true); + await().atMost(2, TimeUnit.SECONDS) + .until(() -> consumers.size() == 1); + TestConsumer consumer = getConsumer(); + verifySubscribedAndLaunched(consumer, partitions); + } + + @Test + public void testPartitionsUpdate_singleConsumer() { + queue.setConsumerPerPartition(false); + consumerManager.init(queue); + ruleEngineConsumerContext.setReady(true); + + Set partitions = Collections.emptySet(); + consumerManager.update(partitions); + verify(queueFactory, after(1000).never()).createToRuleEngineMsgConsumer(any()); + + partitions = createTpis(1); + consumerManager.update(partitions); + TestConsumer consumer = getConsumer(); + verifySubscribedAndLaunched(consumer, partitions); + + partitions = createTpis(1, 2, 3); + consumerManager.update(partitions); + verifySubscribedAndLaunched(consumer, partitions); + + partitions = createTpis(4, 5, 6); + consumerManager.update(partitions); + verifySubscribedAndLaunched(consumer, partitions); + + partitions = Collections.emptySet(); + consumerManager.update(partitions); + verifyUnsubscribedAndStopped(consumer); + + partitions = createTpis(1, 2, 3); + consumerManager.update(partitions); + consumer = getConsumer(); + verifySubscribedAndLaunched(consumer, partitions); + } + + @Test + public void testPartitionsUpdate_consumerPerPartition() { + queue.setConsumerPerPartition(true); + consumerManager.init(queue); + ruleEngineConsumerContext.setReady(true); + + consumerManager.update(Collections.emptySet()); + verify(queueFactory, after(1000).never()).createToRuleEngineMsgConsumer(any()); + + consumerManager.update(createTpis(1)); + TestConsumer consumer1 = getConsumer(1); + verifySubscribedAndLaunched(consumer1, 1); + + consumerManager.update(createTpis(1, 2, 3)); + TestConsumer consumer2 = getConsumer(2); + TestConsumer consumer3 = getConsumer(3); + verifySubscribedAndLaunched(consumer2, 2); + verifySubscribedAndLaunched(consumer3, 3); + verifyNotTouched(consumer1); + + consumerManager.update(createTpis(3, 4, 5)); + TestConsumer consumer4 = getConsumer(4); + TestConsumer consumer5 = getConsumer(5); + verifySubscribedAndLaunched(consumer4, 4); + verifySubscribedAndLaunched(consumer5, 5); + verifyUnsubscribedAndStopped(consumer1); + verifyUnsubscribedAndStopped(consumer2); + verifyNotTouched(consumer3); + + consumerManager.update(Collections.emptySet()); + verifyUnsubscribedAndStopped(consumer3); + verifyUnsubscribedAndStopped(consumer4); + verifyUnsubscribedAndStopped(consumer5); + + consumerManager.update(createTpis(1, 2, 3)); + consumer1 = getConsumer(1); + consumer2 = getConsumer(2); + consumer3 = getConsumer(3); + verifySubscribedAndLaunched(consumer1, 1); + verifySubscribedAndLaunched(consumer2, 2); + verifySubscribedAndLaunched(consumer3, 3); + } + + @Test + public void testConfigUpdate_singleConsumer() { + queue.setConsumerPerPartition(false); + consumerManager.init(queue); + ruleEngineConsumerContext.setReady(true); + Set partitions = createTpis(1, 2, 3); + consumerManager.update(partitions); + TestConsumer consumer = getConsumer(); + verifySubscribedAndLaunched(consumer, partitions); + + Queue newConfig = JacksonUtil.clone(queue); + newConfig.setPollInterval(queue.getPollInterval() / 2); + newConfig.setPartitions(queue.getPartitions() / 2); + newConfig.setPackProcessingTimeout(queue.getPackProcessingTimeout() * 2); + newConfig.getSubmitStrategy().setType(SubmitStrategyType.SEQUENTIAL_BY_ORIGINATOR); + newConfig.getProcessingStrategy().setType(ProcessingStrategyType.RETRY_ALL); + consumerManager.update(newConfig); + + await().atMost(2, TimeUnit.SECONDS) + .untilAsserted(() -> { + verify(consumer, atLeastOnce()).poll(eq((long) newConfig.getPollInterval())); + verify(ruleEngineConsumerContext.getSubmitStrategyFactory(), atLeastOnce()).newInstance(any(), eq(newConfig.getSubmitStrategy())); + verify(ruleEngineConsumerContext.getProcessingStrategyFactory(), atLeastOnce()).newInstance(any(), eq(newConfig.getProcessingStrategy())); + }); + } + + @Test + public void testConfigUpdate_consumerPerPartition() { + queue.setConsumerPerPartition(true); + consumerManager.init(queue); + ruleEngineConsumerContext.setReady(true); + Set partitions = createTpis(1, 2, 3); + consumerManager.update(partitions); + TestConsumer consumer1 = getConsumer(1); + TestConsumer consumer2 = getConsumer(2); + TestConsumer consumer3 = getConsumer(3); + verifySubscribedAndLaunched(consumer1, 1); + verifySubscribedAndLaunched(consumer2, 2); + verifySubscribedAndLaunched(consumer3, 3); + + Queue newConfig = JacksonUtil.clone(queue); + newConfig.setPollInterval(queue.getPollInterval() / 2); + newConfig.setPartitions(queue.getPartitions() / 2); + newConfig.setPackProcessingTimeout(queue.getPackProcessingTimeout() * 2); + newConfig.getSubmitStrategy().setType(SubmitStrategyType.SEQUENTIAL_BY_ORIGINATOR); + newConfig.getProcessingStrategy().setType(ProcessingStrategyType.RETRY_ALL); + consumerManager.update(newConfig); + + await().atMost(2, TimeUnit.SECONDS) + .untilAsserted(() -> { + verify(consumer1, atLeastOnce()).poll(eq((long) newConfig.getPollInterval())); + verify(consumer2, atLeastOnce()).poll(eq((long) newConfig.getPollInterval())); + verify(consumer3, atLeastOnce()).poll(eq((long) newConfig.getPollInterval())); + }); + verifyNotTouched(consumer1); + verifyNotTouched(consumer2); + verifyNotTouched(consumer3); + } + + @Test + public void testConfigUpdate_fromSingleToConsumerPerPartition() { + queue.setConsumerPerPartition(false); + consumerManager.init(queue); + ruleEngineConsumerContext.setReady(true); + Set partitions = createTpis(1, 2, 3); + consumerManager.update(partitions); + TestConsumer consumer = getConsumer(); + verifySubscribedAndLaunched(consumer, partitions); + + Queue newConfig = JacksonUtil.clone(queue); + newConfig.setConsumerPerPartition(true); + consumerManager.update(newConfig); + + verifyUnsubscribedAndStopped(consumer); + verifySubscribedAndLaunched(getConsumer(1), 1); + verifySubscribedAndLaunched(getConsumer(2), 2); + verifySubscribedAndLaunched(getConsumer(3), 3); + } + + @Test + public void testConfigUpdate_fromConsumerPerPartitionToSingle() { + queue.setConsumerPerPartition(true); + consumerManager.init(queue); + ruleEngineConsumerContext.setReady(true); + Set partitions = createTpis(1, 2, 3); + consumerManager.update(partitions); + TestConsumer consumer1 = getConsumer(1); + TestConsumer consumer2 = getConsumer(2); + TestConsumer consumer3 = getConsumer(3); + verifySubscribedAndLaunched(consumer1, 1); + verifySubscribedAndLaunched(consumer2, 2); + verifySubscribedAndLaunched(consumer3, 3); + + Queue newConfig = JacksonUtil.clone(queue); + newConfig.setConsumerPerPartition(false); + consumerManager.update(newConfig); + + verifyUnsubscribedAndStopped(consumer1); + verifyUnsubscribedAndStopped(consumer2); + verifyUnsubscribedAndStopped(consumer3); + verifySubscribedAndLaunched(getConsumer(), partitions); + } + + @Test + public void testStop() { + queue.setConsumerPerPartition(true); + consumerManager.init(queue); + ruleEngineConsumerContext.setReady(true); + consumerManager.update(createTpis(1)); + TestConsumer consumer = getConsumer(1); + verifySubscribedAndLaunched(consumer, 1); + verify(queueFactory, times(1)).createToRuleEngineMsgConsumer(any()); + + consumerManager.stop(); + consumerManager.update(createTpis(1, 2, 3, 4)); // to check that no new tasks after stop are processed + consumerManager.update(createTpis(5, 6, 7)); + + verifyUnsubscribedAndStopped(consumer); + verifyNoMoreInteractions(queueFactory); + } + + @Test + public void testDelete_consumerPerPartition() { + queue.setConsumerPerPartition(true); + consumerManager.init(queue); + ruleEngineConsumerContext.setReady(true); + Set partitions = createTpis(1, 2); + consumerManager.update(partitions); + TestConsumer consumer1 = getConsumer(1); + TestConsumer consumer2 = getConsumer(2); + verifySubscribedAndLaunched(consumer1, 1); + verifySubscribedAndLaunched(consumer2, 2); + verifyMsgProcessed(consumer1.testMsg); + verifyMsgProcessed(consumer2.testMsg); + + consumerManager.delete(); + + await().atMost(2, TimeUnit.SECONDS) + .untilAsserted(() -> { + verify(ruleEngineMsgProducer).send(any(), any(), any()); + }); + clearInvocations(actorContext); + verify(consumer1, never()).unsubscribe(); + verify(consumer2, never()).unsubscribe(); + int msgCount = totalConsumedMsgs.get(); + + await().atLeast(2, TimeUnit.SECONDS) // based on topicDeletionDelayInSec(5) = 5 - ( 3 seconds the code may execute starting consumerManager.delete() call) + .atMost(7, TimeUnit.SECONDS) + .untilAsserted(() -> { + partitions.stream() + .map(TopicPartitionInfo::getFullTopicName) + .forEach(topic -> { + verify(queueAdmin).deleteTopic(eq(topic)); + }); + }); + verify(consumer1).unsubscribe(); + verify(consumer2).unsubscribe(); + + int totalMovedMsgs = totalConsumedMsgs.get() - msgCount; + assertThat(totalMovedMsgs).isNotZero(); + verify(ruleEngineMsgProducer, atLeast(totalMovedMsgs)).send(any(), any(), any()); + verify(actorContext, never()).tell(any()); + generateQueueMsgs = false; + } + + @Test + public void testDelete_singleConsumer() { + queue.setConsumerPerPartition(false); + consumerManager.init(queue); + ruleEngineConsumerContext.setReady(true); + Set partitions = createTpis(1, 2); + consumerManager.update(partitions); + TestConsumer consumer = getConsumer(); + verifySubscribedAndLaunched(consumer, partitions); + verifyMsgProcessed(consumer.testMsg); + + consumerManager.delete(); + + await().atMost(2, TimeUnit.SECONDS) + .untilAsserted(() -> { + verify(ruleEngineMsgProducer).send(any(), any(), any()); + }); + clearInvocations(actorContext); + verify(consumer, never()).unsubscribe(); + int msgCount = totalConsumedMsgs.get(); + + await().atLeast(2, TimeUnit.SECONDS) // based on topicDeletionDelayInSec(5) = 5 - ( 3 seconds the code may execute starting consumerManager.delete() call) + .atMost(7, TimeUnit.SECONDS) + .untilAsserted(() -> { + partitions.stream() + .map(TopicPartitionInfo::getFullTopicName) + .forEach(topic -> { + verify(queueAdmin).deleteTopic(eq(topic)); + }); + }); + verify(consumer).unsubscribe(); + + int movedMsgs = totalConsumedMsgs.get() - msgCount; + assertThat(movedMsgs).isNotZero(); + verify(ruleEngineMsgProducer, atLeast(movedMsgs)).send(any(), any(), any()); + verify(actorContext, never()).tell(any()); + generateQueueMsgs = false; + } + + @Test + public void testManyDifferentUpdates() throws Exception { + queue.setConsumerPerPartition(RandomUtils.nextBoolean()); + consumerManager.init(queue); + ruleEngineConsumerContext.setReady(true); + + Supplier queueConfigUpdater = () -> { + Queue oldConfig = consumerManager.getQueue(); + Queue newConfig = JacksonUtil.clone(oldConfig); + newConfig.setConsumerPerPartition(RandomUtils.nextBoolean()); + newConfig.setPollInterval(RandomUtils.nextInt(100, 501)); + newConfig.setPartitions(RandomUtils.nextInt(1, 10)); + newConfig.setPackProcessingTimeout(RandomUtils.nextLong(100, 5001)); + newConfig.getSubmitStrategy().setType(SubmitStrategyType.values()[RandomUtils.nextInt(0, SubmitStrategyType.values().length)]); + newConfig.getProcessingStrategy().setType(ProcessingStrategyType.values()[RandomUtils.nextInt(0, ProcessingStrategyType.values().length)]); + log.info("Generated new config: consumerPerPartition={}, pollInterval={}, processingStrategy={}", + newConfig.isConsumerPerPartition(), newConfig.getPollInterval(), newConfig.getProcessingStrategy().getType()); + return newConfig; + }; + Supplier> partitionsUpdater = () -> { + int partitionsCount = RandomUtils.nextInt(0, 20); + int[] partitions = IntStream.generate(() -> RandomUtils.nextInt(0, 20)) + .distinct().limit(partitionsCount) + .sorted().toArray(); + log.info("Generated new partitions: {}", Arrays.toString(partitions)); + return createTpis(partitions); + }; + + int iterations = 100; + Queue latestConfig = queue; + Set latestPartitions = Collections.emptySet(); + for (int i = 1; i <= iterations; i++) { + boolean updateQueueConfig = RandomUtils.nextBoolean(); + boolean updatePartitions = !updateQueueConfig; + if (updateQueueConfig) { + latestConfig = queueConfigUpdater.get(); + consumerManager.update(latestConfig); + } + if (updatePartitions) { + latestPartitions = partitionsUpdater.get(); + consumerManager.update(latestPartitions); + } + Thread.sleep(RandomUtils.nextLong(0, 200)); + } + if (latestPartitions.isEmpty()) { + do { + latestPartitions = partitionsUpdater.get(); + } while (latestPartitions.isEmpty()); + consumerManager.update(latestPartitions); + } + + Queue expectedConfig = latestConfig; + Set expectedPartitions = latestPartitions; + await().atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> { + assertThat(consumerManager.getQueue()).isEqualTo(expectedConfig); + assertThat(consumerManager.getPartitions()).isEqualTo(expectedPartitions); + }); + + if (expectedConfig.isConsumerPerPartition()) { + await().atMost(5, TimeUnit.SECONDS).until(() -> { + for (TopicPartitionInfo partition : expectedPartitions) { + if (consumers.stream().noneMatch(consumer -> consumer.subscribed && + consumer.pollingStarted && Set.of(partition).equals(consumer.getPartitions()))) { + return false; + } + } + return consumers.size() == expectedPartitions.size(); + }); + } else { + await().atMost(5, TimeUnit.SECONDS).until(() -> { + return consumers.size() == 1 && consumers.stream() + .anyMatch(consumer -> consumer.subscribed && consumer.pollingStarted && + expectedPartitions.equals(consumer.getPartitions())); + }); + } + Mockito.reset(ruleEngineConsumerContext.getSubmitStrategyFactory()); + Mockito.reset(ruleEngineConsumerContext.getProcessingStrategyFactory()); + consumers.forEach(Mockito::clearInvocations); + + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + for (TestConsumer consumer : consumers) { + verify(consumer, atLeastOnce().description("consumer " + consumer.topics)).poll(expectedConfig.getPollInterval()); + } + verify(ruleEngineConsumerContext.getSubmitStrategyFactory(), atLeastOnce()).newInstance(any(), eq(expectedConfig.getSubmitStrategy())); + verify(ruleEngineConsumerContext.getProcessingStrategyFactory(), atLeastOnce()).newInstance(any(), eq(expectedConfig.getProcessingStrategy())); + }); + } + + private void verifySubscribedAndLaunched(TestConsumer consumer, Set expectedPartitions) { + await().atMost(2, TimeUnit.SECONDS) + .until(() -> consumer.subscribed && consumer.getPartitions().equals(expectedPartitions) && consumer.pollingStarted); + verify(consumer, times(1)).subscribe(any()); + verify(consumer).subscribe(eq(expectedPartitions)); + verify(consumer).doSubscribe(argThat(topics -> topics.containsAll(expectedPartitions.stream() + .map(TopicPartitionInfo::getFullTopicName).collect(Collectors.toList())))); + verify(consumer, atLeastOnce()).poll(eq((long) queue.getPollInterval())); + verify(consumer, atLeastOnce()).doPoll(eq((long) queue.getPollInterval())); + verify(consumer, never()).unsubscribe(); + Mockito.reset(consumer); + } + + private void verifySubscribedAndLaunched(TestConsumer consumer, int... expectedPartitions) { + verifySubscribedAndLaunched(consumer, createTpis(expectedPartitions)); + } + + private void verifyUnsubscribedAndStopped(TestConsumer consumer) { + await().atMost(2, TimeUnit.SECONDS) + .until(() -> !consumer.subscribed && !consumer.topics.isEmpty()); + verify(consumer, never()).subscribe(any()); + verify(consumer, never()).doSubscribe(any()); + assertThat(consumers).doesNotContain(consumer); + Mockito.reset(consumer); + } + + private void verifyNotTouched(TestConsumer consumer) { + verify(consumer, never()).subscribe(any()); + verify(consumer, never()).subscribe(); + verify(consumer, never()).doSubscribe(any()); + verify(consumer, never()).unsubscribe(); + verify(consumer, never()).doUnsubscribe(); + } + + private void verifyMsgProcessed(TbMsg tbMsg) { + await().atMost(2, TimeUnit.SECONDS).untilAsserted(() -> { + verify(actorContext, atLeastOnce()).tell(argThat(msg -> { + return ((QueueToRuleEngineMsg) msg).getMsg().getId().equals(tbMsg.getId()); + })); + }); + } + + // for consumer-per-partition + private TestConsumer getConsumer(TopicPartitionInfo tpi) { + return await().atMost(5, TimeUnit.SECONDS) + .until(() -> consumers.stream() + .filter(consumer -> consumer.getPartitions() != null && + consumer.getPartitions().size() == 1 && + consumer.getPartitions().contains(tpi)) + .findFirst().orElse(null), Objects::nonNull); + } + + private TestConsumer getConsumer(int partition) { + return await().atMost(5, TimeUnit.SECONDS) + .until(() -> consumers.stream() + .filter(consumer -> consumer.getPartitions() != null && + consumer.getPartitions().size() == 1 && + consumer.getPartitions().stream() + .anyMatch(tpi -> tpi.getPartition().get().equals(partition))) + .findFirst().orElse(null), Objects::nonNull); + } + + // for single consumer + private TestConsumer getConsumer() { + return await().atMost(5, TimeUnit.SECONDS) + .until(() -> consumers.size() == 1 ? consumers.iterator().next() : null, Objects::nonNull); + } + + private Set createTpis(int... partitions) { + return Arrays.stream(partitions) + .mapToObj(n -> TopicPartitionInfo.builder() + .tenantId(queue.getTenantId()) + .topic(queue.getTopic()) + .partition(n) + .myPartition(true) + .build()) + .collect(Collectors.toSet()); + } + + + class TestConsumer extends AbstractTbQueueConsumerTemplate> { + + @Getter + private List topics; + + private boolean subscribed; + private boolean pollingStarted; + + private TbMsg testMsg; + + public TestConsumer(String topic) { + super(topic); + } + + @SneakyThrows + @Override + protected List doPoll(long durationInMillis) { + log.debug("doPoll({} ms)", durationInMillis); + if (!subscribed) { + throw new IllegalStateException("Cannot poll because not subscribed"); + } + pollingStarted = true; + if (testMsg != null && RandomUtils.nextBoolean()) { + Thread.sleep(100); + return List.of(testMsg); + } + return Collections.emptyList(); + } + + @Override + protected TbProtoQueueMsg decode(TbMsg tbMsg) throws IOException { + log.debug("decode()"); + UUID tenantId = UUID.randomUUID(); + return new TbProtoQueueMsg<>(UUID.randomUUID(), ToRuleEngineMsg.newBuilder() + .setTenantIdMSB(tenantId.getMostSignificantBits()) + .setTenantIdLSB(tenantId.getLeastSignificantBits()) + .addRelationTypes("Success") + .setTbMsg(TbMsg.toByteString(tbMsg)) + .build()); + } + + @Override + protected void doSubscribe(List topicNames) { + log.debug("doSubscribe({})", topicNames); + this.topics = topicNames; + subscribed = true; + } + + @Override + protected void doCommit() { + if (!subscribed) { + throw new IllegalStateException("Cannot commit because not subscribed"); + } + log.debug("doCommit() totalConsumedMsgs = {}", totalConsumedMsgs.incrementAndGet()); + } + + @Override + public void unsubscribe() { + super.unsubscribe(); + consumers.remove(this); + } + + @Override + protected void doUnsubscribe() { + log.debug("doUnsubscribe()"); + if (!subscribed) { + throw new IllegalStateException("Already unsubscribed!"); + } + subscribed = false; + } + + @Override + protected boolean isLongPollingSupported() { + return false; + } + + public Set getPartitions() { + return partitions; + } + + public void setUpTestMsg() { + testMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, new DeviceId(UUID.randomUUID()), new TbMsgMetaData(), "{}"); + } + } + +} diff --git a/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java b/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java index 6f08e47412..278420589f 100644 --- a/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/resource/sql/BaseTbResourceServiceTest.java @@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.TbResourceInfoFilter; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -37,6 +38,7 @@ import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.service.resource.TbResourceService; @@ -46,6 +48,7 @@ import java.util.Collections; import java.util.List; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -105,14 +108,17 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { ""; private static final String DEFAULT_FILE_NAME = "test.jks"; - private static final String TEST_DATA = "77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCEtLQpGSUxFIElORk9STUFUSU9OCgpPTUEgUGVybWFuZW50IERvY3VtZW50CiAgIEZpbGU6IE9NQS1TVVAtTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci1WMV8wXzEtMjAxOTAyMjEtQQogICBUeXBlOiB4bWwKClB1YmxpYyBSZWFjaGFibGUgSW5mb3JtYXRpb24KICAgUGF0aDogaHR0cDovL3d3dy5vcGVubW9iaWxlYWxsaWFuY2Uub3JnL3RlY2gvcHJvZmlsZXMKICAgTmFtZTogTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci12MV8wXzEueG1sCgpOT1JNQVRJVkUgSU5GT1JNQVRJT04KCiAgSW5mb3JtYXRpb24gYWJvdXQgdGhpcyBmaWxlIGNhbiBiZSBmb3VuZCBpbiB0aGUgbGF0ZXN0IHJldmlzaW9uIG9mCgogIE9NQS1UUy1MV00yTV9CaW5hcnlBcHBEYXRhQ29udGFpbmVyLVYxXzBfMQoKICBUaGlzIGlzIGF2YWlsYWJsZSBhdCBodHRwOi8vd3d3Lm9wZW5tb2JpbGVhbGxpYW5jZS5vcmcvCgogIFNlbmQgY29tbWVudHMgdG8gaHR0cHM6Ly9naXRodWIuY29tL09wZW5Nb2JpbGVBbGxpYW5jZS9PTUFfTHdNMk1fZm9yX0RldmVsb3BlcnMvaXNzdWVzCgpDSEFOR0UgSElTVE9SWQoKMTUwNjIwMTggU3RhdHVzIGNoYW5nZWQgdG8gQXBwcm92ZWQgYnkgRE0sIERvYyBSZWYgIyBPTUEtRE0mU0UtMjAxOC0wMDYxLUlOUF9MV00yTV9BUFBEQVRBX1YxXzBfRVJQX2Zvcl9maW5hbF9BcHByb3ZhbAoyMTAyMjAxOSBTdGF0dXMgY2hhbmdlZCB0byBBcHByb3ZlZCBieSBJUFNPLCBEb2MgUmVmICMgT01BLUlQU08tMjAxOS0wMDI1LUlOUF9Md00yTV9PYmplY3RfQXBwX0RhdGFfQ29udGFpbmVyXzFfMF8xX2Zvcl9GaW5hbF9BcHByb3ZhbAoKTEVHQUwgRElTQ0xBSU1FUgoKQ29weXJpZ2h0IDIwMTkgT3BlbiBNb2JpbGUgQWxsaWFuY2UuCgpSZWRpc3RyaWJ1dGlvbiBhbmQgdXNlIGluIHNvdXJjZSBhbmQgYmluYXJ5IGZvcm1zLCB3aXRoIG9yIHdpdGhvdXQKbW9kaWZpY2F0aW9uLCBhcmUgcGVybWl0dGVkIHByb3ZpZGVkIHRoYXQgdGhlIGZvbGxvd2luZyBjb25kaXRpb25zCmFyZSBtZXQ6CgoxLiBSZWRpc3RyaWJ1dGlvbnMgb2Ygc291cmNlIGNvZGUgbXVzdCByZXRhaW4gdGhlIGFib3ZlIGNvcHlyaWdodApub3RpY2UsIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIuCjIuIFJlZGlzdHJpYnV0aW9ucyBpbiBiaW5hcnkgZm9ybSBtdXN0IHJlcHJvZHVjZSB0aGUgYWJvdmUgY29weXJpZ2h0Cm5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lciBpbiB0aGUKZG9jdW1lbnRhdGlvbiBhbmQvb3Igb3RoZXIgbWF0ZXJpYWxzIHByb3ZpZGVkIHdpdGggdGhlIGRpc3RyaWJ1dGlvbi4KMy4gTmVpdGhlciB0aGUgbmFtZSBvZiB0aGUgY29weXJpZ2h0IGhvbGRlciBub3IgdGhlIG5hbWVzIG9mIGl0cwpjb250cmlidXRvcnMgbWF5IGJlIHVzZWQgdG8gZW5kb3JzZSBvciBwcm9tb3RlIHByb2R1Y3RzIGRlcml2ZWQKZnJvbSB0aGlzIHNvZnR3YXJlIHdpdGhvdXQgc3BlY2lmaWMgcHJpb3Igd3JpdHRlbiBwZXJtaXNzaW9uLgoKVEhJUyBTT0ZUV0FSRSBJUyBQUk9WSURFRCBCWSBUSEUgQ09QWVJJR0hUIEhPTERFUlMgQU5EIENPTlRSSUJVVE9SUwoiQVMgSVMiIEFORCBBTlkgRVhQUkVTUyBPUiBJTVBMSUVEIFdBUlJBTlRJRVMsIElOQ0xVRElORywgQlVUIE5PVApMSU1JVEVEIFRPLCBUSEUgSU1QTElFRCBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSBBTkQgRklUTkVTUwpGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQVJFIERJU0NMQUlNRUQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRQpDT1BZUklHSFQgSE9MREVSIE9SIENPTlRSSUJVVE9SUyBCRSBMSUFCTEUgRk9SIEFOWSBESVJFQ1QsIElORElSRUNULApJTkNJREVOVEFMLCBTUEVDSUFMLCBFWEVNUExBUlksIE9SIENPTlNFUVVFTlRJQUwgREFNQUdFUyAoSU5DTFVESU5HLApCVVQgTk9UIExJTUlURUQgVE8sIFBST0NVUkVNRU5UIE9GIFNVQlNUSVRVVEUgR09PRFMgT1IgU0VSVklDRVM7CkxPU1MgT0YgVVNFLCBEQVRBLCBPUiBQUk9GSVRTOyBPUiBCVVNJTkVTUyBJTlRFUlJVUFRJT04pIEhPV0VWRVIKQ0FVU0VEIEFORCBPTiBBTlkgVEhFT1JZIE9GIExJQUJJTElUWSwgV0hFVEhFUiBJTiBDT05UUkFDVCwgU1RSSUNUCkxJQUJJTElUWSwgT1IgVE9SVCAoSU5DTFVESU5HIE5FR0xJR0VOQ0UgT1IgT1RIRVJXSVNFKSBBUklTSU5HIElOCkFOWSBXQVkgT1VUIE9GIFRIRSBVU0UgT0YgVEhJUyBTT0ZUV0FSRSwgRVZFTiBJRiBBRFZJU0VEIE9GIFRIRQpQT1NTSUJJTElUWSBPRiBTVUNIIERBTUFHRS4KClRoZSBhYm92ZSBsaWNlbnNlIGlzIHVzZWQgYXMgYSBsaWNlbnNlIHVuZGVyIGNvcHlyaWdodCBvbmx5LiBQbGVhc2UKcmVmZXJlbmNlIHRoZSBPTUEgSVBSIFBvbGljeSBmb3IgcGF0ZW50IGxpY2Vuc2luZyB0ZXJtczoKaHR0cHM6Ly93d3cub21hc3BlY3dvcmtzLm9yZy9hYm91dC9pbnRlbGxlY3R1YWwtcHJvcGVydHktcmlnaHRzLwoKLS0+CjxMV00yTSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6bm9OYW1lc3BhY2VTY2hlbWFMb2NhdGlvbj0iaHR0cDovL29wZW5tb2JpbGVhbGxpYW5jZS5vcmcvdGVjaC9wcm9maWxlcy9MV00yTS54c2QiPgoJPE9iamVjdCBPYmplY3RUeXBlPSJNT0RlZmluaXRpb24iPgoJCTxOYW1lPkJpbmFyeUFwcERhdGFDb250YWluZXI8L05hbWU+CgkJPERlc2NyaXB0aW9uMT48IVtDREFUQVtUaGlzIEx3TTJNIE9iamVjdHMgcHJvdmlkZXMgdGhlIGFwcGxpY2F0aW9uIHNlcnZpY2UgZGF0YSByZWxhdGVkIHRvIGEgTHdNMk0gU2VydmVyLCBlZy4gV2F0ZXIgbWV0ZXIgZGF0YS4gClRoZXJlIGFyZSBzZXZlcmFsIG1ldGhvZHMgdG8gY3JlYXRlIGluc3RhbmNlIHRvIGluZGljYXRlIHRoZSBtZXNzYWdlIGRpcmVjdGlvbiBiYXNlZCBvbiB0aGUgbmVnb3RpYXRpb24gYmV0d2VlbiBBcHBsaWNhdGlvbiBhbmQgTHdNMk0uIFRoZSBDbGllbnQgYW5kIFNlcnZlciBzaG91bGQgbmVnb3RpYXRlIHRoZSBpbnN0YW5jZShzKSB1c2VkIHRvIGV4Y2hhbmdlIHRoZSBkYXRhLiBGb3IgZXhhbXBsZToKIC0gVXNpbmcgYSBzaW5nbGUgaW5zdGFuY2UgZm9yIGJvdGggZGlyZWN0aW9ucyBjb21tdW5pY2F0aW9uLCBmcm9tIENsaWVudCB0byBTZXJ2ZXIgYW5kIGZyb20gU2VydmVyIHRvIENsaWVudC4KIC0gVXNpbmcgYW4gaW5zdGFuY2UgZm9yIGNvbW11bmljYXRpb24gZnJvbSBDbGllbnQgdG8gU2VydmVyIGFuZCBhbm90aGVyIG9uZSBmb3IgY29tbXVuaWNhdGlvbiBmcm9tIFNlcnZlciB0byBDbGllbnQKIC0gVXNpbmcgc2V2ZXJhbCBpbnN0YW5jZXMKXV0+PC9EZXNjcmlwdGlvbjE+CgkJPE9iamVjdElEPjE5PC9PYmplY3RJRD4KCQk8T2JqZWN0VVJOPnVybjpvbWE6bHdtMm06b21hOjE5PC9PYmplY3RVUk4+CgkJPExXTTJNVmVyc2lvbj4xLjA8L0xXTTJNVmVyc2lvbj4KCQk8T2JqZWN0VmVyc2lvbj4xLjA8L09iamVjdFZlcnNpb24+CgkJPE11bHRpcGxlSW5zdGFuY2VzPk11bHRpcGxlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJPFJlc291cmNlcz4KCQkJPEl0ZW0gSUQ9IjAiPjxOYW1lPkRhdGE8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5NdWx0aXBsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk1hbmRhdG9yeTwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+T3BhcXVlPC9UeXBlPgoJCQkJPFJhbmdlRW51bWVyYXRpb24gLz4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgYXBwbGljYXRpb24gZGF0YSBjb250ZW50Ll1dPjwvRGVzY3JpcHRpb24+CgkJCTwvSXRlbT4KCQkJPEl0ZW0gSUQ9IjEiPjxOYW1lPkRhdGEgUHJpb3JpdHk8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5TaW5nbGU8L011bHRpcGxlSW5zdGFuY2VzPgoJCQkJPE1hbmRhdG9yeT5PcHRpb25hbDwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+SW50ZWdlcjwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjEgYnl0ZXM8L1JhbmdlRW51bWVyYXRpb24+CgkJCQk8VW5pdHMgLz4KCQkJCTxEZXNjcmlwdGlvbj48IVtDREFUQVtJbmRpY2F0ZXMgdGhlIEFwcGxpY2F0aW9uIGRhdGEgcHJpb3JpdHk6CjA6SW1tZWRpYXRlCjE6QmVzdEVmZm9ydAoyOkxhdGVzdAozLTEwMDogUmVzZXJ2ZWQgZm9yIGZ1dHVyZSB1c2UuCjEwMS0yNTQ6IFByb3ByaWV0YXJ5IG1vZGUuXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iMiI+PE5hbWU+RGF0YSBDcmVhdGlvbiBUaW1lPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlRpbWU8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbiAvPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBEYXRhIGluc3RhbmNlIGNyZWF0aW9uIHRpbWVzdGFtcC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSIzIj48TmFtZT5EYXRhIERlc2NyaXB0aW9uPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlN0cmluZzwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjMyIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkYXRhIGRlc2NyaXB0aW9uLgplLmcuICJtZXRlciByZWFkaW5nIi5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSI0Ij48TmFtZT5EYXRhIEZvcm1hdDwvTmFtZT4KCQkJCTxPcGVyYXRpb25zPlJXPC9PcGVyYXRpb25zPgoJCQkJPE11bHRpcGxlSW5zdGFuY2VzPlNpbmdsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJCQk8VHlwZT5TdHJpbmc8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4zMiBieXRlczwvUmFuZ2VFbnVtZXJhdGlvbj4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgZm9ybWF0IG9mIHRoZSBBcHBsaWNhdGlvbiBEYXRhLgplLmcuIFlHLU1ldGVyLVdhdGVyLVJlYWRpbmcKVVRGOC1zdHJpbmcKXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iNSI+PE5hbWU+QXBwIElEPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPkludGVnZXI8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4yIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkZXN0aW5hdGlvbiBBcHBsaWNhdGlvbiBJRC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+PC9SZXNvdXJjZXM+CgkJPERlc2NyaXB0aW9uMj48IVtDREFUQVtdXT48L0Rlc2NyaXB0aW9uMj4KCTwvT2JqZWN0Pgo8L0xXTTJNPgo="; + private static final String TEST_BASE64_DATA = "77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCEtLQpGSUxFIElORk9STUFUSU9OCgpPTUEgUGVybWFuZW50IERvY3VtZW50CiAgIEZpbGU6IE9NQS1TVVAtTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci1WMV8wXzEtMjAxOTAyMjEtQQogICBUeXBlOiB4bWwKClB1YmxpYyBSZWFjaGFibGUgSW5mb3JtYXRpb24KICAgUGF0aDogaHR0cDovL3d3dy5vcGVubW9iaWxlYWxsaWFuY2Uub3JnL3RlY2gvcHJvZmlsZXMKICAgTmFtZTogTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci12MV8wXzEueG1sCgpOT1JNQVRJVkUgSU5GT1JNQVRJT04KCiAgSW5mb3JtYXRpb24gYWJvdXQgdGhpcyBmaWxlIGNhbiBiZSBmb3VuZCBpbiB0aGUgbGF0ZXN0IHJldmlzaW9uIG9mCgogIE9NQS1UUy1MV00yTV9CaW5hcnlBcHBEYXRhQ29udGFpbmVyLVYxXzBfMQoKICBUaGlzIGlzIGF2YWlsYWJsZSBhdCBodHRwOi8vd3d3Lm9wZW5tb2JpbGVhbGxpYW5jZS5vcmcvCgogIFNlbmQgY29tbWVudHMgdG8gaHR0cHM6Ly9naXRodWIuY29tL09wZW5Nb2JpbGVBbGxpYW5jZS9PTUFfTHdNMk1fZm9yX0RldmVsb3BlcnMvaXNzdWVzCgpDSEFOR0UgSElTVE9SWQoKMTUwNjIwMTggU3RhdHVzIGNoYW5nZWQgdG8gQXBwcm92ZWQgYnkgRE0sIERvYyBSZWYgIyBPTUEtRE0mU0UtMjAxOC0wMDYxLUlOUF9MV00yTV9BUFBEQVRBX1YxXzBfRVJQX2Zvcl9maW5hbF9BcHByb3ZhbAoyMTAyMjAxOSBTdGF0dXMgY2hhbmdlZCB0byBBcHByb3ZlZCBieSBJUFNPLCBEb2MgUmVmICMgT01BLUlQU08tMjAxOS0wMDI1LUlOUF9Md00yTV9PYmplY3RfQXBwX0RhdGFfQ29udGFpbmVyXzFfMF8xX2Zvcl9GaW5hbF9BcHByb3ZhbAoKTEVHQUwgRElTQ0xBSU1FUgoKQ29weXJpZ2h0IDIwMTkgT3BlbiBNb2JpbGUgQWxsaWFuY2UuCgpSZWRpc3RyaWJ1dGlvbiBhbmQgdXNlIGluIHNvdXJjZSBhbmQgYmluYXJ5IGZvcm1zLCB3aXRoIG9yIHdpdGhvdXQKbW9kaWZpY2F0aW9uLCBhcmUgcGVybWl0dGVkIHByb3ZpZGVkIHRoYXQgdGhlIGZvbGxvd2luZyBjb25kaXRpb25zCmFyZSBtZXQ6CgoxLiBSZWRpc3RyaWJ1dGlvbnMgb2Ygc291cmNlIGNvZGUgbXVzdCByZXRhaW4gdGhlIGFib3ZlIGNvcHlyaWdodApub3RpY2UsIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIuCjIuIFJlZGlzdHJpYnV0aW9ucyBpbiBiaW5hcnkgZm9ybSBtdXN0IHJlcHJvZHVjZSB0aGUgYWJvdmUgY29weXJpZ2h0Cm5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lciBpbiB0aGUKZG9jdW1lbnRhdGlvbiBhbmQvb3Igb3RoZXIgbWF0ZXJpYWxzIHByb3ZpZGVkIHdpdGggdGhlIGRpc3RyaWJ1dGlvbi4KMy4gTmVpdGhlciB0aGUgbmFtZSBvZiB0aGUgY29weXJpZ2h0IGhvbGRlciBub3IgdGhlIG5hbWVzIG9mIGl0cwpjb250cmlidXRvcnMgbWF5IGJlIHVzZWQgdG8gZW5kb3JzZSBvciBwcm9tb3RlIHByb2R1Y3RzIGRlcml2ZWQKZnJvbSB0aGlzIHNvZnR3YXJlIHdpdGhvdXQgc3BlY2lmaWMgcHJpb3Igd3JpdHRlbiBwZXJtaXNzaW9uLgoKVEhJUyBTT0ZUV0FSRSBJUyBQUk9WSURFRCBCWSBUSEUgQ09QWVJJR0hUIEhPTERFUlMgQU5EIENPTlRSSUJVVE9SUwoiQVMgSVMiIEFORCBBTlkgRVhQUkVTUyBPUiBJTVBMSUVEIFdBUlJBTlRJRVMsIElOQ0xVRElORywgQlVUIE5PVApMSU1JVEVEIFRPLCBUSEUgSU1QTElFRCBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSBBTkQgRklUTkVTUwpGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQVJFIERJU0NMQUlNRUQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRQpDT1BZUklHSFQgSE9MREVSIE9SIENPTlRSSUJVVE9SUyBCRSBMSUFCTEUgRk9SIEFOWSBESVJFQ1QsIElORElSRUNULApJTkNJREVOVEFMLCBTUEVDSUFMLCBFWEVNUExBUlksIE9SIENPTlNFUVVFTlRJQUwgREFNQUdFUyAoSU5DTFVESU5HLApCVVQgTk9UIExJTUlURUQgVE8sIFBST0NVUkVNRU5UIE9GIFNVQlNUSVRVVEUgR09PRFMgT1IgU0VSVklDRVM7CkxPU1MgT0YgVVNFLCBEQVRBLCBPUiBQUk9GSVRTOyBPUiBCVVNJTkVTUyBJTlRFUlJVUFRJT04pIEhPV0VWRVIKQ0FVU0VEIEFORCBPTiBBTlkgVEhFT1JZIE9GIExJQUJJTElUWSwgV0hFVEhFUiBJTiBDT05UUkFDVCwgU1RSSUNUCkxJQUJJTElUWSwgT1IgVE9SVCAoSU5DTFVESU5HIE5FR0xJR0VOQ0UgT1IgT1RIRVJXSVNFKSBBUklTSU5HIElOCkFOWSBXQVkgT1VUIE9GIFRIRSBVU0UgT0YgVEhJUyBTT0ZUV0FSRSwgRVZFTiBJRiBBRFZJU0VEIE9GIFRIRQpQT1NTSUJJTElUWSBPRiBTVUNIIERBTUFHRS4KClRoZSBhYm92ZSBsaWNlbnNlIGlzIHVzZWQgYXMgYSBsaWNlbnNlIHVuZGVyIGNvcHlyaWdodCBvbmx5LiBQbGVhc2UKcmVmZXJlbmNlIHRoZSBPTUEgSVBSIFBvbGljeSBmb3IgcGF0ZW50IGxpY2Vuc2luZyB0ZXJtczoKaHR0cHM6Ly93d3cub21hc3BlY3dvcmtzLm9yZy9hYm91dC9pbnRlbGxlY3R1YWwtcHJvcGVydHktcmlnaHRzLwoKLS0+CjxMV00yTSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6bm9OYW1lc3BhY2VTY2hlbWFMb2NhdGlvbj0iaHR0cDovL29wZW5tb2JpbGVhbGxpYW5jZS5vcmcvdGVjaC9wcm9maWxlcy9MV00yTS54c2QiPgoJPE9iamVjdCBPYmplY3RUeXBlPSJNT0RlZmluaXRpb24iPgoJCTxOYW1lPkJpbmFyeUFwcERhdGFDb250YWluZXI8L05hbWU+CgkJPERlc2NyaXB0aW9uMT48IVtDREFUQVtUaGlzIEx3TTJNIE9iamVjdHMgcHJvdmlkZXMgdGhlIGFwcGxpY2F0aW9uIHNlcnZpY2UgZGF0YSByZWxhdGVkIHRvIGEgTHdNMk0gU2VydmVyLCBlZy4gV2F0ZXIgbWV0ZXIgZGF0YS4gClRoZXJlIGFyZSBzZXZlcmFsIG1ldGhvZHMgdG8gY3JlYXRlIGluc3RhbmNlIHRvIGluZGljYXRlIHRoZSBtZXNzYWdlIGRpcmVjdGlvbiBiYXNlZCBvbiB0aGUgbmVnb3RpYXRpb24gYmV0d2VlbiBBcHBsaWNhdGlvbiBhbmQgTHdNMk0uIFRoZSBDbGllbnQgYW5kIFNlcnZlciBzaG91bGQgbmVnb3RpYXRlIHRoZSBpbnN0YW5jZShzKSB1c2VkIHRvIGV4Y2hhbmdlIHRoZSBkYXRhLiBGb3IgZXhhbXBsZToKIC0gVXNpbmcgYSBzaW5nbGUgaW5zdGFuY2UgZm9yIGJvdGggZGlyZWN0aW9ucyBjb21tdW5pY2F0aW9uLCBmcm9tIENsaWVudCB0byBTZXJ2ZXIgYW5kIGZyb20gU2VydmVyIHRvIENsaWVudC4KIC0gVXNpbmcgYW4gaW5zdGFuY2UgZm9yIGNvbW11bmljYXRpb24gZnJvbSBDbGllbnQgdG8gU2VydmVyIGFuZCBhbm90aGVyIG9uZSBmb3IgY29tbXVuaWNhdGlvbiBmcm9tIFNlcnZlciB0byBDbGllbnQKIC0gVXNpbmcgc2V2ZXJhbCBpbnN0YW5jZXMKXV0+PC9EZXNjcmlwdGlvbjE+CgkJPE9iamVjdElEPjE5PC9PYmplY3RJRD4KCQk8T2JqZWN0VVJOPnVybjpvbWE6bHdtMm06b21hOjE5PC9PYmplY3RVUk4+CgkJPExXTTJNVmVyc2lvbj4xLjA8L0xXTTJNVmVyc2lvbj4KCQk8T2JqZWN0VmVyc2lvbj4xLjA8L09iamVjdFZlcnNpb24+CgkJPE11bHRpcGxlSW5zdGFuY2VzPk11bHRpcGxlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJPFJlc291cmNlcz4KCQkJPEl0ZW0gSUQ9IjAiPjxOYW1lPkRhdGE8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5NdWx0aXBsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk1hbmRhdG9yeTwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+T3BhcXVlPC9UeXBlPgoJCQkJPFJhbmdlRW51bWVyYXRpb24gLz4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgYXBwbGljYXRpb24gZGF0YSBjb250ZW50Ll1dPjwvRGVzY3JpcHRpb24+CgkJCTwvSXRlbT4KCQkJPEl0ZW0gSUQ9IjEiPjxOYW1lPkRhdGEgUHJpb3JpdHk8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5TaW5nbGU8L011bHRpcGxlSW5zdGFuY2VzPgoJCQkJPE1hbmRhdG9yeT5PcHRpb25hbDwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+SW50ZWdlcjwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjEgYnl0ZXM8L1JhbmdlRW51bWVyYXRpb24+CgkJCQk8VW5pdHMgLz4KCQkJCTxEZXNjcmlwdGlvbj48IVtDREFUQVtJbmRpY2F0ZXMgdGhlIEFwcGxpY2F0aW9uIGRhdGEgcHJpb3JpdHk6CjA6SW1tZWRpYXRlCjE6QmVzdEVmZm9ydAoyOkxhdGVzdAozLTEwMDogUmVzZXJ2ZWQgZm9yIGZ1dHVyZSB1c2UuCjEwMS0yNTQ6IFByb3ByaWV0YXJ5IG1vZGUuXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iMiI+PE5hbWU+RGF0YSBDcmVhdGlvbiBUaW1lPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlRpbWU8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbiAvPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBEYXRhIGluc3RhbmNlIGNyZWF0aW9uIHRpbWVzdGFtcC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSIzIj48TmFtZT5EYXRhIERlc2NyaXB0aW9uPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlN0cmluZzwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjMyIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkYXRhIGRlc2NyaXB0aW9uLgplLmcuICJtZXRlciByZWFkaW5nIi5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSI0Ij48TmFtZT5EYXRhIEZvcm1hdDwvTmFtZT4KCQkJCTxPcGVyYXRpb25zPlJXPC9PcGVyYXRpb25zPgoJCQkJPE11bHRpcGxlSW5zdGFuY2VzPlNpbmdsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJCQk8VHlwZT5TdHJpbmc8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4zMiBieXRlczwvUmFuZ2VFbnVtZXJhdGlvbj4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgZm9ybWF0IG9mIHRoZSBBcHBsaWNhdGlvbiBEYXRhLgplLmcuIFlHLU1ldGVyLVdhdGVyLVJlYWRpbmcKVVRGOC1zdHJpbmcKXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iNSI+PE5hbWU+QXBwIElEPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPkludGVnZXI8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4yIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkZXN0aW5hdGlvbiBBcHBsaWNhdGlvbiBJRC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+PC9SZXNvdXJjZXM+CgkJPERlc2NyaXB0aW9uMj48IVtDREFUQVtdXT48L0Rlc2NyaXB0aW9uMj4KCTwvT2JqZWN0Pgo8L0xXTTJNPgo="; + private static final byte[] TEST_DATA = Base64.getDecoder().decode(TEST_BASE64_DATA); private IdComparator idComparator = new IdComparator<>(); private TenantId tenantId; @Autowired - private TbResourceService resourceService; + private ResourceService resourceService; + @Autowired + private TbResourceService tbResourceService; private Tenant savedTenant; private User tenantAdmin; @@ -151,7 +157,8 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { long limit = 4; EntityInfo defaultTenantProfileInfo = doGet("/api/tenantProfileInfo/default", EntityInfo.class); TenantProfile defaultTenantProfile = doGet("/api/tenantProfile/" + defaultTenantProfileInfo.getId().getId().toString(), TenantProfile.class); - defaultTenantProfile.getProfileData().setConfiguration(DefaultTenantProfileConfiguration.builder().maxResourcesInBytes(limit).build()); + defaultTenantProfile.getProfileData().setConfiguration(DefaultTenantProfileConfiguration.builder() + .maxResourcesInBytes(limit).build()); doPost("/api/tenantProfile", defaultTenantProfile, TenantProfile.class); loginTenantAdmin(); @@ -165,7 +172,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { try { assertThatThrownBy(() -> createResource("test1", 1 + DEFAULT_FILE_NAME)) .isInstanceOf(DataValidationException.class) - .hasMessageContaining("Failed to create the tb resource, files size limit is exhausted %d bytes!", limit); + .hasMessageContaining("Resources total size exceeds the maximum of %s bytes", limit); } finally { defaultTenantProfile.getProfileData().setConfiguration(DefaultTenantProfileConfiguration.builder().maxResourcesInBytes(0).build()); loginSysAdmin(); @@ -173,6 +180,15 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { } } + @Test + public void testMaxResourceSizeValidation() throws ThingsboardException { + updateDefaultTenantProfileConfig(profileConfig -> { + profileConfig.setMaxResourceSize(2); + }); + assertThatThrownBy(() -> createResource("Test", DEFAULT_FILE_NAME)) + .hasMessageContaining("Resource exceeds the maximum size of 2 bytes"); + } + @Test public void sumDataSizeByTenantId() throws Exception { assertEquals(0, resourceService.sumDataSizeByTenantId(tenantId)); @@ -182,7 +198,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { for (int i = 2; i < 4; i++) { createResource("test" + i, i + DEFAULT_FILE_NAME); - assertEquals(i*4, resourceService.sumDataSizeByTenantId(tenantId)); + assertEquals(i * 4, resourceService.sumDataSizeByTenantId(tenantId)); } } @@ -192,9 +208,9 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTitle(title); resource.setResourceType(ResourceType.JKS); resource.setFileName(filename); - byte[] b = new byte[1]; - resource.setData(Base64.getEncoder().encodeToString(b)); - return resourceService.save(resource); + byte[] b = new byte[]{1, 2, 3, 4}; + resource.setData(b); + return tbResourceService.save(resource); } @Test @@ -206,7 +222,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setFileName(DEFAULT_FILE_NAME); resource.setData(TEST_DATA); - TbResource savedResource = resourceService.save(resource); + TbResource savedResource = tbResourceService.save(resource); Assert.assertNotNull(savedResource); Assert.assertNotNull(savedResource.getId()); @@ -214,15 +230,17 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { assertEquals(resource.getTenantId(), savedResource.getTenantId()); assertEquals(resource.getTitle(), savedResource.getTitle()); assertEquals(resource.getResourceKey(), savedResource.getResourceKey()); - assertEquals(resource.getData(), savedResource.getData()); + assertArrayEquals(TEST_DATA, savedResource.getData()); savedResource.setTitle("My new resource"); + savedResource.setData(null); - resourceService.save(savedResource); + tbResourceService.save(savedResource); TbResource foundResource = resourceService.findResourceById(tenantId, savedResource.getId()); assertEquals(foundResource.getTitle(), savedResource.getTitle()); + assertArrayEquals(foundResource.getData(), TEST_DATA); - resourceService.delete(savedResource, null); + tbResourceService.delete(savedResource, null); } @Test @@ -231,9 +249,9 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTenantId(tenantId); resource.setResourceType(ResourceType.LWM2M_MODEL); resource.setFileName("test_model.xml"); - resource.setData(Base64.getEncoder().encodeToString(LWM2M_TEST_MODEL.getBytes())); + resource.setEncodedData(Base64.getEncoder().encodeToString(LWM2M_TEST_MODEL.getBytes())); - TbResource savedResource = resourceService.save(resource); + TbResource savedResource = tbResourceService.save(resource); Assert.assertNotNull(savedResource); Assert.assertNotNull(savedResource.getId()); @@ -241,9 +259,9 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { assertEquals(resource.getTenantId(), savedResource.getTenantId()); assertEquals("My first resource id=0 v1.0", savedResource.getTitle()); assertEquals("0_1.0", savedResource.getResourceKey()); - assertEquals(resource.getData(), savedResource.getData()); + assertArrayEquals(savedResource.getData(), LWM2M_TEST_MODEL.getBytes()); - resourceService.delete(savedResource, null); + tbResourceService.delete(savedResource, null); } @Test @@ -253,11 +271,11 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); resource.setData(TEST_DATA); - TbResource savedResource = resourceService.save(resource); + TbResource savedResource = tbResourceService.save(resource); assertEquals(TenantId.SYS_TENANT_ID, savedResource.getTenantId()); - resourceService.delete(savedResource, null); + tbResourceService.delete(savedResource, null); } @Test @@ -269,7 +287,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setFileName(DEFAULT_FILE_NAME); resource.setData(TEST_DATA); - TbResource savedResource = resourceService.save(resource); + TbResource savedResource = tbResourceService.save(resource); TbResource resource2 = new TbResource(); resource.setTenantId(tenantId); @@ -280,10 +298,10 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { try { Assertions.assertThrows(DataValidationException.class, () -> { - resourceService.save(resource2); + tbResourceService.save(resource2); }); } finally { - resourceService.delete(savedResource, null); + tbResourceService.delete(savedResource, null); } } @@ -295,7 +313,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setFileName(DEFAULT_FILE_NAME); resource.setData(TEST_DATA); Assertions.assertThrows(DataValidationException.class, () -> { - resourceService.save(resource); + tbResourceService.save(resource); }); } @@ -308,7 +326,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setFileName(DEFAULT_FILE_NAME); resource.setData(TEST_DATA); Assertions.assertThrows(DataValidationException.class, () -> { - resourceService.save(resource); + tbResourceService.save(resource); }); } @@ -318,10 +336,10 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTenantId(tenantId); resource.setResourceType(ResourceType.LWM2M_MODEL); resource.setFileName("xxe_test_model.xml"); - resource.setData(Base64.getEncoder().encodeToString(LWM2M_TEST_MODEL_WITH_XXE.getBytes())); + resource.setData(LWM2M_TEST_MODEL_WITH_XXE.getBytes()); DataValidationException thrown = assertThrows(DataValidationException.class, () -> { - resourceService.save(resource); + tbResourceService.save(resource); }); assertEquals("Failed to parse file xxe_test_model.xml", thrown.getMessage()); } @@ -334,12 +352,12 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); resource.setData(TEST_DATA); - TbResource savedResource = resourceService.save(resource); + TbResource savedResource = tbResourceService.save(resource); TbResource foundResource = resourceService.findResourceById(tenantId, savedResource.getId()); Assert.assertNotNull(foundResource); assertEquals(savedResource, foundResource); - resourceService.delete(savedResource, null); + tbResourceService.delete(savedResource, null); } @Test @@ -350,12 +368,12 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); resource.setData(TEST_DATA); - TbResource savedResource = resourceService.save(resource); + TbResource savedResource = tbResourceService.save(resource); - TbResource foundResource = resourceService.getResource(tenantId, savedResource.getResourceType(), savedResource.getResourceKey()); + TbResource foundResource = resourceService.findResourceByTenantIdAndKey(tenantId, savedResource.getResourceType(), savedResource.getResourceKey()); Assert.assertNotNull(foundResource); assertEquals(savedResource, foundResource); - resourceService.delete(savedResource, null); + tbResourceService.delete(savedResource, null); } @Test @@ -365,11 +383,11 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setTitle("My resource"); resource.setFileName(DEFAULT_FILE_NAME); resource.setData(TEST_DATA); - TbResource savedResource = resourceService.save(resource); + TbResource savedResource = tbResourceService.save(resource); TbResource foundResource = resourceService.findResourceById(tenantId, savedResource.getId()); Assert.assertNotNull(foundResource); - resourceService.delete(savedResource, null); + tbResourceService.delete(savedResource, null); foundResource = resourceService.findResourceById(tenantId, savedResource.getId()); Assert.assertNull(foundResource); } @@ -391,7 +409,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); resource.setData(TEST_DATA); - resources.add(new TbResourceInfo(resourceService.save(resource))); + resources.add(new TbResourceInfo(tbResourceService.save(resource))); } List loadedResources = new ArrayList<>(); @@ -445,7 +463,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); resource.setData(TEST_DATA); - TbResourceInfo tbResourceInfo = new TbResourceInfo(resourceService.save(resource)); + TbResourceInfo tbResourceInfo = new TbResourceInfo(tbResourceService.save(resource)); if (i >= 50) { resources.add(tbResourceInfo); } @@ -458,7 +476,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { resource.setResourceType(ResourceType.JKS); resource.setFileName(i + DEFAULT_FILE_NAME); resource.setData(TEST_DATA); - resources.add(new TbResourceInfo(resourceService.save(resource))); + resources.add(new TbResourceInfo(tbResourceService.save(resource))); } List loadedResources = new ArrayList<>(); @@ -468,7 +486,7 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { TbResourceInfoFilter filter = TbResourceInfoFilter.builder() .tenantId(tenantId) .build(); - pageData = resourceService.findAllTenantResourcesByTenantId(filter, pageLink); + pageData = resourceService.findAllTenantResourcesByTenantId(filter, pageLink); loadedResources.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); diff --git a/application/src/test/java/org/thingsboard/server/service/rpc/RpcSubmitStrategyTest.java b/application/src/test/java/org/thingsboard/server/service/rpc/RpcSubmitStrategyTest.java new file mode 100644 index 0000000000..5754181381 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/rpc/RpcSubmitStrategyTest.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.rpc; + +import org.junit.jupiter.api.Test; +import org.thingsboard.server.common.data.StringUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +class RpcSubmitStrategyTest { + + @Test + void givenRandomString_whenParse_thenReturnBurstStrategy() { + String randomString = StringUtils.randomAlphanumeric(10); + RpcSubmitStrategy parsed = RpcSubmitStrategy.parse(randomString); + assertThat(parsed).isEqualTo(RpcSubmitStrategy.BURST); + } + + @Test + void givenNull_whenParse_thenReturnBurstStrategy() { + RpcSubmitStrategy parsed = RpcSubmitStrategy.parse(null); + assertThat(parsed).isEqualTo(RpcSubmitStrategy.BURST); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java b/application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java index 4373a4c108..5cd54a24eb 100644 --- a/application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java @@ -33,6 +33,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_REQUEST; @DaoSqlTest @TestPropertySource(properties = { @@ -121,7 +122,7 @@ class NashornJsInvokeServiceTest extends AbstractControllerTest { } private String invokeScript(UUID scriptId, String msg) throws ExecutionException, InterruptedException { - return invokeService.invokeScript(TenantId.SYS_TENANT_ID, null, scriptId, msg, "{}", "POST_TELEMETRY_REQUEST").get().toString(); + return invokeService.invokeScript(TenantId.SYS_TENANT_ID, null, scriptId, msg, "{}", POST_TELEMETRY_REQUEST.name()).get().toString(); } } diff --git a/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeServiceTest.java b/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeServiceTest.java index ca5dbef5f1..62af9cb16e 100644 --- a/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/script/TbelInvokeServiceTest.java @@ -42,6 +42,7 @@ import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_REQUEST; @DaoSqlTest @TestPropertySource(properties = { @@ -216,7 +217,7 @@ class TbelInvokeServiceTest extends AbstractControllerTest { private String invokeScript(UUID scriptId, String str) throws ExecutionException, InterruptedException { var msg = JacksonUtil.fromString(str, Map.class); - return invokeService.invokeScript(TenantId.SYS_TENANT_ID, null, scriptId, msg, "{}", "POST_TELEMETRY_REQUEST").get().toString(); + return invokeService.invokeScript(TenantId.SYS_TENANT_ID, null, scriptId, msg, "{}", POST_TELEMETRY_REQUEST.name()).get().toString(); } } diff --git a/application/src/test/java/org/thingsboard/server/service/security/auth/JwtTokenFactoryTest.java b/application/src/test/java/org/thingsboard/server/service/security/auth/JwtTokenFactoryTest.java index b7dc40ec6d..48070678f6 100644 --- a/application/src/test/java/org/thingsboard/server/service/security/auth/JwtTokenFactoryTest.java +++ b/application/src/test/java/org/thingsboard/server/service/security/auth/JwtTokenFactoryTest.java @@ -16,14 +16,22 @@ package org.thingsboard.server.service.security.auth; import io.jsonwebtoken.Claims; -import org.junit.BeforeClass; +import org.junit.Before; import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.NotificationCenter; +import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; +import org.thingsboard.server.common.data.notification.targets.platform.SystemAdministratorsFilter; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.model.JwtSettings; import org.thingsboard.server.common.data.security.model.JwtToken; +import org.thingsboard.server.dao.settings.AdminSettingsService; +import org.thingsboard.server.service.security.auth.jwt.settings.DefaultJwtSettingsService; +import org.thingsboard.server.service.security.auth.jwt.settings.DefaultJwtSettingsValidator; import org.thingsboard.server.service.security.auth.jwt.settings.JwtSettingsService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.UserPrincipal; @@ -33,28 +41,40 @@ import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; import java.util.Calendar; import java.util.Date; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.willReturn; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class JwtTokenFactoryTest { - private static JwtTokenFactory tokenFactory; - private static JwtSettings jwtSettings; + private JwtTokenFactory tokenFactory; + private AdminSettingsService adminSettingsService; + private NotificationCenter notificationCenter; + private JwtSettingsService jwtSettingsService; - @BeforeClass - public static void beforeAll() { + private JwtSettings jwtSettings; + + @Before + public void beforeEach() { jwtSettings = new JwtSettings(); jwtSettings.setTokenIssuer("tb"); jwtSettings.setTokenSigningKey("abewafaf"); jwtSettings.setTokenExpirationTime((int) TimeUnit.HOURS.toSeconds(2)); jwtSettings.setRefreshTokenExpTime((int) TimeUnit.DAYS.toSeconds(7)); - JwtSettingsService jwtSettingsService = mock(JwtSettingsService.class); - willReturn(jwtSettings).given(jwtSettingsService).getJwtSettings(); + adminSettingsService = mock(AdminSettingsService.class); + notificationCenter = mock(NotificationCenter.class); + jwtSettingsService = mockJwtSettingsService(); + mockJwtSettings(jwtSettings); tokenFactory = new JwtTokenFactory(jwtSettingsService); } @@ -86,7 +106,7 @@ public class JwtTokenFactoryTest { AccessJwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); checkExpirationTime(accessToken, jwtSettings.getTokenExpirationTime()); - SecurityUser parsedSecurityUser = tokenFactory.parseAccessJwtToken(new RawAccessJwtToken(accessToken.getToken())); + SecurityUser parsedSecurityUser = tokenFactory.parseAccessJwtToken(accessToken.getToken()); assertThat(parsedSecurityUser.getId()).isEqualTo(securityUser.getId()); assertThat(parsedSecurityUser.getEmail()).isEqualTo(securityUser.getEmail()); assertThat(parsedSecurityUser.getUserPrincipal()).matches(userPrincipal -> { @@ -115,7 +135,7 @@ public class JwtTokenFactoryTest { JwtToken refreshToken = tokenFactory.createRefreshToken(securityUser); checkExpirationTime(refreshToken, jwtSettings.getRefreshTokenExpTime()); - SecurityUser parsedSecurityUser = tokenFactory.parseRefreshToken(new RawAccessJwtToken(refreshToken.getToken())); + SecurityUser parsedSecurityUser = tokenFactory.parseRefreshToken(refreshToken.getToken()); assertThat(parsedSecurityUser.getId()).isEqualTo(securityUser.getId()); assertThat(parsedSecurityUser.getUserPrincipal()).matches(userPrincipal -> { return userPrincipal.getType().equals(securityUser.getUserPrincipal().getType()) @@ -139,7 +159,7 @@ public class JwtTokenFactoryTest { JwtToken preVerificationToken = tokenFactory.createPreVerificationToken(securityUser, tokenLifetime); checkExpirationTime(preVerificationToken, tokenLifetime); - SecurityUser parsedSecurityUser = tokenFactory.parseAccessJwtToken(new RawAccessJwtToken(preVerificationToken.getToken())); + SecurityUser parsedSecurityUser = tokenFactory.parseAccessJwtToken(preVerificationToken.getToken()); assertThat(parsedSecurityUser.getId()).isEqualTo(securityUser.getId()); assertThat(parsedSecurityUser.getAuthority()).isEqualTo(Authority.PRE_VERIFICATION_TOKEN); assertThat(parsedSecurityUser.getTenantId()).isEqualTo(securityUser.getTenantId()); @@ -150,8 +170,35 @@ public class JwtTokenFactoryTest { }); } + @Test + public void testJwtSigningKeyIssueNotification() { + JwtSettings badJwtSettings = jwtSettings; + badJwtSettings.setTokenSigningKey(JwtSettingsService.TOKEN_SIGNING_KEY_DEFAULT); + mockJwtSettings(badJwtSettings); + jwtSettingsService = mockJwtSettingsService(); + + for (int i = 0; i < 5; i++) { // to check if notification is not sent twice + jwtSettingsService.getJwtSettings(); + } + verify(notificationCenter, times(1)).sendGeneralWebNotification(eq(TenantId.SYS_TENANT_ID), + isA(SystemAdministratorsFilter.class), argThat(template -> template.getConfiguration().getDeliveryMethodsTemplates().get(NotificationDeliveryMethod.WEB) + .getBody().contains("The platform is configured to use default JWT Signing Key"))); + } + + private void mockJwtSettings(JwtSettings settings) { + AdminSettings adminJwtSettings = new AdminSettings(); + adminJwtSettings.setJsonValue(JacksonUtil.valueToTree(settings)); + when(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, JwtSettingsService.ADMIN_SETTINGS_JWT_KEY)) + .thenReturn(adminJwtSettings); + } + + private DefaultJwtSettingsService mockJwtSettingsService() { + return new DefaultJwtSettingsService(adminSettingsService, Optional.empty(), + Optional.of(notificationCenter), new DefaultJwtSettingsValidator()); + } + private void checkExpirationTime(JwtToken jwtToken, int tokenLifetime) { - Claims claims = tokenFactory.parseTokenClaims(jwtToken).getBody(); + Claims claims = tokenFactory.parseTokenClaims(jwtToken.getToken()).getBody(); assertThat(claims.getExpiration()).matches(actualExpirationTime -> { Calendar expirationTime = Calendar.getInstance(); expirationTime.setTime(new Date()); diff --git a/application/src/test/java/org/thingsboard/server/service/security/auth/TokenOutdatingTest.java b/application/src/test/java/org/thingsboard/server/service/security/auth/TokenOutdatingTest.java index eeb9fe64a2..90d911833c 100644 --- a/application/src/test/java/org/thingsboard/server/service/security/auth/TokenOutdatingTest.java +++ b/application/src/test/java/org/thingsboard/server/service/security/auth/TokenOutdatingTest.java @@ -114,12 +114,12 @@ public class TokenOutdatingTest { // Token outdatage time is rounded to 1 sec. Need to wait before outdating so that outdatage time is strictly after token issue time SECONDS.sleep(1); eventPublisher.publishEvent(new UserCredentialsInvalidationEvent(securityUser.getId())); - assertTrue(tokenOutdatingService.isOutdated(jwtToken, securityUser.getId())); + assertTrue(tokenOutdatingService.isOutdated(jwtToken.getToken(), securityUser.getId())); SECONDS.sleep(1); JwtToken newJwtToken = tokenFactory.createAccessJwtToken(securityUser); - assertFalse(tokenOutdatingService.isOutdated(newJwtToken, securityUser.getId())); + assertFalse(tokenOutdatingService.isOutdated(newJwtToken.getToken(), securityUser.getId())); } @Test diff --git a/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java b/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java index e70a888194..654f934ed2 100644 --- a/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java @@ -33,11 +33,11 @@ import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.JsonDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; -import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.timeseries.TimeseriesService; @@ -133,7 +133,7 @@ public class SequentialTimeseriesPersistenceTest extends AbstractControllerTest void saveLatestTsForAssetAndDevice(List devices, Asset asset, int idx) throws ExecutionException, InterruptedException, TimeoutException { for (Device device : devices) { - TbMsg tbMsg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), + TbMsg tbMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, device.getId(), getTbMsgMetadata(device.getName(), ts.get(idx)), TbMsgDataType.JSON, diff --git a/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java b/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java index 52f2ec5c9d..05624b8741 100644 --- a/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java @@ -15,39 +15,67 @@ */ package org.thingsboard.server.service.state; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.DeviceIdInfo; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.notification.rule.trigger.DeviceActivityTrigger; +import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.query.EntityData; import org.thingsboard.server.common.data.query.EntityKeyType; import org.thingsboard.server.common.data.query.TsValue; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; +import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.sql.query.EntityQueryRepository; import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.queue.discovery.QueueKey; +import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; +import org.thingsboard.server.queue.usagestats.DefaultTbApiUsageReportClient; +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; +import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Stream; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; import static org.mockito.BDDMockito.willReturn; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.thingsboard.server.common.data.DataConstants.SERVER_SCOPE; +import static org.thingsboard.server.service.state.DefaultDeviceStateService.ACTIVITY_STATE; +import static org.thingsboard.server.service.state.DefaultDeviceStateService.INACTIVITY_ALARM_TIME; import static org.thingsboard.server.service.state.DefaultDeviceStateService.INACTIVITY_TIMEOUT; +import static org.thingsboard.server.service.state.DefaultDeviceStateService.LAST_ACTIVITY_TIME; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class DefaultDeviceStateServiceTest { @Mock @@ -62,22 +90,38 @@ public class DefaultDeviceStateServiceTest { PartitionService partitionService; @Mock DeviceStateData deviceStateDataMock; + @Mock + EntityQueryRepository entityQueryRepository; + @Mock + TelemetrySubscriptionService telemetrySubscriptionService; + @Mock + NotificationRuleProcessor notificationRuleProcessor; + @Mock + DefaultTbApiUsageReportClient defaultTbApiUsageReportClient; + TenantId tenantId = new TenantId(UUID.fromString("00797a3b-7aeb-4b5b-b57a-c2a810d0f112")); DeviceId deviceId = DeviceId.fromString("00797a3b-7aeb-4b5b-b57a-c2a810d0f112"); + TopicPartitionInfo tpi; DefaultDeviceStateService service; - @Before + @BeforeEach public void setUp() { - service = spy(new DefaultDeviceStateService(deviceService, attributesService, tsService, clusterService, partitionService, null, null, null, mock(NotificationRuleProcessor.class))); + service = spy(new DefaultDeviceStateService(deviceService, attributesService, tsService, clusterService, partitionService, entityQueryRepository, null, defaultTbApiUsageReportClient, notificationRuleProcessor)); + ReflectionTestUtils.setField(service, "tsSubService", telemetrySubscriptionService); + ReflectionTestUtils.setField(service, "defaultStateCheckIntervalInSec", 60); + ReflectionTestUtils.setField(service, "defaultActivityStatsIntervalInSec", 60); + ReflectionTestUtils.setField(service, "initFetchPackSize", 10); + + tpi = TopicPartitionInfo.builder().myPartition(true).build(); } @Test public void givenDeviceIdFromDeviceStatesMap_whenGetOrFetchDeviceStateData_thenNoStackOverflow() { service.deviceStates.put(deviceId, deviceStateDataMock); DeviceStateData deviceStateData = service.getOrFetchDeviceStateData(deviceId); - assertThat(deviceStateData, is(deviceStateDataMock)); - Mockito.verify(service, never()).fetchDeviceStateDataUsingEntityDataQuery(deviceId); + assertThat(deviceStateData).isEqualTo(deviceStateDataMock); + verify(service, never()).fetchDeviceStateDataUsingEntityDataQuery(deviceId); } @Test @@ -85,8 +129,8 @@ public class DefaultDeviceStateServiceTest { service.deviceStates.clear(); willReturn(deviceStateDataMock).given(service).fetchDeviceStateDataUsingEntityDataQuery(deviceId); DeviceStateData deviceStateData = service.getOrFetchDeviceStateData(deviceId); - assertThat(deviceStateData, is(deviceStateDataMock)); - Mockito.verify(service, times(1)).fetchDeviceStateDataUsingEntityDataQuery(deviceId); + assertThat(deviceStateData).isEqualTo(deviceStateDataMock); + verify(service, times(1)).fetchDeviceStateDataUsingEntityDataQuery(deviceId); } @Test @@ -122,7 +166,517 @@ public class DefaultDeviceStateServiceTest { DeviceStateData deviceStateData = service.toDeviceStateData(new EntityData(deviceId, latest, Map.of()), new DeviceIdInfo(TenantId.SYS_TENANT_ID.getId(), UUID.randomUUID(), deviceUuid)); - Assert.assertEquals(5000L, deviceStateData.getState().getInactivityTimeout()); + assertThat(deviceStateData.getState().getInactivityTimeout()).isEqualTo(5000L); + } + + private void initStateService(long timeout) throws InterruptedException { + service.stop(); + reset(service, telemetrySubscriptionService); + service.setDefaultInactivityTimeoutMs(timeout); + service.init(); + when(partitionService.resolve(ServiceType.TB_CORE, tenantId, deviceId)).thenReturn(tpi); + when(entityQueryRepository.findEntityDataByQueryInternal(any())).thenReturn(new PageData<>()); + var deviceIdInfo = new DeviceIdInfo(tenantId.getId(), null, deviceId.getId()); + when(deviceService.findDeviceIdInfos(any())) + .thenReturn(new PageData<>(List.of(deviceIdInfo), 0, 1, false)); + PartitionChangeEvent event = new PartitionChangeEvent(this, ServiceType.TB_CORE, Map.of( + new QueueKey(ServiceType.TB_CORE), Collections.singleton(tpi) + )); + service.onApplicationEvent(event); + Thread.sleep(100); + } + + @Test + public void increaseInactivityForInactiveDeviceTest() throws Exception { + final long defaultTimeout = 1; + initStateService(defaultTimeout); + DeviceState deviceState = DeviceState.builder().build(); + DeviceStateData deviceStateData = DeviceStateData.builder() + .tenantId(tenantId) + .deviceId(deviceId) + .state(deviceState) + .metaData(new TbMsgMetaData()) + .build(); + + service.deviceStates.put(deviceId, deviceStateData); + service.getPartitionedEntities(tpi).add(deviceId); + + service.onDeviceActivity(tenantId, deviceId, System.currentTimeMillis()); + activityVerify(true); + Thread.sleep(defaultTimeout); + service.checkStates(); + activityVerify(false); + + reset(telemetrySubscriptionService); + + long increase = 100; + long newTimeout = System.currentTimeMillis() - deviceState.getLastActivityTime() + increase; + + service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout); + activityVerify(true); + Thread.sleep(increase); + service.checkStates(); + activityVerify(false); + + reset(telemetrySubscriptionService); + + service.onDeviceActivity(tenantId, deviceId, System.currentTimeMillis()); + activityVerify(true); + Thread.sleep(newTimeout + 5); + service.checkStates(); + activityVerify(false); + } + + @Test + public void increaseInactivityForActiveDeviceTest() throws Exception { + final long defaultTimeout = 1000; + initStateService(defaultTimeout); + DeviceState deviceState = DeviceState.builder().build(); + DeviceStateData deviceStateData = DeviceStateData.builder() + .tenantId(tenantId) + .deviceId(deviceId) + .state(deviceState) + .metaData(new TbMsgMetaData()) + .build(); + + service.deviceStates.put(deviceId, deviceStateData); + service.getPartitionedEntities(tpi).add(deviceId); + + service.onDeviceActivity(tenantId, deviceId, System.currentTimeMillis()); + activityVerify(true); + + reset(telemetrySubscriptionService); + + long increase = 100; + long newTimeout = System.currentTimeMillis() - deviceState.getLastActivityTime() + increase; + + service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout); + verify(telemetrySubscriptionService, never()).saveAttrAndNotify(any(), eq(deviceId), any(), eq(ACTIVITY_STATE), any(), any()); + Thread.sleep(defaultTimeout + increase); + service.checkStates(); + activityVerify(false); + + reset(telemetrySubscriptionService); + + service.onDeviceActivity(tenantId, deviceId, System.currentTimeMillis()); + activityVerify(true); + Thread.sleep(newTimeout); + service.checkStates(); + activityVerify(false); + } + + @Test + public void increaseSmallInactivityForInactiveDeviceTest() throws Exception { + final long defaultTimeout = 1; + initStateService(defaultTimeout); + DeviceState deviceState = DeviceState.builder().build(); + DeviceStateData deviceStateData = DeviceStateData.builder() + .tenantId(tenantId) + .deviceId(deviceId) + .state(deviceState) + .metaData(new TbMsgMetaData()) + .build(); + + service.deviceStates.put(deviceId, deviceStateData); + service.getPartitionedEntities(tpi).add(deviceId); + + service.onDeviceActivity(tenantId, deviceId, System.currentTimeMillis()); + activityVerify(true); + Thread.sleep(defaultTimeout); + service.checkStates(); + activityVerify(false); + + reset(telemetrySubscriptionService); + + long newTimeout = 1; + Thread.sleep(newTimeout); + verify(telemetrySubscriptionService, never()).saveAttrAndNotify(any(), eq(deviceId), any(), eq(ACTIVITY_STATE), any(), any()); + } + + @Test + public void decreaseInactivityForActiveDeviceTest() throws Exception { + final long defaultTimeout = 1000; + initStateService(defaultTimeout); + DeviceState deviceState = DeviceState.builder().build(); + DeviceStateData deviceStateData = DeviceStateData.builder() + .tenantId(tenantId) + .deviceId(deviceId) + .state(deviceState) + .metaData(new TbMsgMetaData()) + .build(); + + service.deviceStates.put(deviceId, deviceStateData); + service.getPartitionedEntities(tpi).add(deviceId); + + service.onDeviceActivity(tenantId, deviceId, System.currentTimeMillis()); + activityVerify(true); + + verify(telemetrySubscriptionService, never()).saveAttrAndNotify(any(), eq(deviceId), any(), eq(ACTIVITY_STATE), any(), any()); + + long newTimeout = 1; + Thread.sleep(newTimeout); + + service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout); + activityVerify(false); + reset(telemetrySubscriptionService); + + service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, defaultTimeout); + activityVerify(true); + Thread.sleep(defaultTimeout); + service.checkStates(); + activityVerify(false); + } + + @Test + public void decreaseInactivityForInactiveDeviceTest() throws Exception { + final long defaultTimeout = 1000; + initStateService(defaultTimeout); + DeviceState deviceState = DeviceState.builder().build(); + DeviceStateData deviceStateData = DeviceStateData.builder() + .tenantId(tenantId) + .deviceId(deviceId) + .state(deviceState) + .metaData(new TbMsgMetaData()) + .build(); + + service.deviceStates.put(deviceId, deviceStateData); + service.getPartitionedEntities(tpi).add(deviceId); + + service.onDeviceActivity(tenantId, deviceId, System.currentTimeMillis()); + activityVerify(true); + Thread.sleep(defaultTimeout); + service.checkStates(); + activityVerify(false); + reset(telemetrySubscriptionService); + + long newTimeout = 1; + + service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout); + verify(telemetrySubscriptionService, never()).saveAttrAndNotify(any(), eq(deviceId), any(), eq(ACTIVITY_STATE), any(), any()); + } + + private void activityVerify(boolean isActive) { + verify(telemetrySubscriptionService, times(1)).saveAttrAndNotify(any(), eq(deviceId), any(), eq(ACTIVITY_STATE), eq(isActive), any()); + } + + @Test + public void givenStateDataIsNull_whenUpdateActivityState_thenShouldCleanupDevice() { + // GIVEN + service.deviceStates.put(deviceId, deviceStateDataMock); + + // WHEN + service.updateActivityState(deviceId, null, System.currentTimeMillis()); + + // THEN + assertThat(service.deviceStates.get(deviceId)).isNull(); + assertThat(service.deviceStates.size()).isEqualTo(0); + assertThat(service.deviceStates.isEmpty()).isTrue(); + } + + + @ParameterizedTest + @MethodSource("provideParametersForUpdateActivityState") + public void givenTestParameters_whenUpdateActivityState_thenShouldBeInTheExpectedStateAndPerformExpectedActions( + boolean activityState, long previousActivityTime, long lastReportedActivity, long inactivityAlarmTime, + long expectedInactivityAlarmTime, boolean shouldSetInactivityAlarmTimeToZero, + boolean shouldUpdateActivityStateToActive + ) { + // GIVEN + DeviceState deviceState = DeviceState.builder() + .active(activityState) + .lastActivityTime(previousActivityTime) + .lastInactivityAlarmTime(inactivityAlarmTime) + .inactivityTimeout(10000) + .build(); + + DeviceStateData deviceStateData = DeviceStateData.builder() + .tenantId(tenantId) + .deviceId(deviceId) + .state(deviceState) + .metaData(new TbMsgMetaData()) + .build(); + + // WHEN + service.updateActivityState(deviceId, deviceStateData, lastReportedActivity); + + // THEN + assertThat(deviceState.isActive()).isEqualTo(true); + assertThat(deviceState.getLastActivityTime()).isEqualTo(lastReportedActivity); + then(telemetrySubscriptionService).should().saveAttrAndNotify( + any(), eq(deviceId), any(), eq(LAST_ACTIVITY_TIME), eq(lastReportedActivity), any() + ); + + assertThat(deviceState.getLastInactivityAlarmTime()).isEqualTo(expectedInactivityAlarmTime); + if (shouldSetInactivityAlarmTimeToZero) { + then(telemetrySubscriptionService).should().saveAttrAndNotify( + any(), eq(deviceId), any(), eq(INACTIVITY_ALARM_TIME), eq(0L), any() + ); + } + + if (shouldUpdateActivityStateToActive) { + then(telemetrySubscriptionService).should().saveAttrAndNotify( + eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(SERVER_SCOPE), eq(ACTIVITY_STATE), eq(true), any() + ); + + var msgCaptor = ArgumentCaptor.forClass(TbMsg.class); + then(clusterService).should().pushMsgToRuleEngine(eq(tenantId), eq(deviceId), msgCaptor.capture(), any()); + var actualMsg = msgCaptor.getValue(); + assertThat(actualMsg.getType()).isEqualTo(TbMsgType.ACTIVITY_EVENT.name()); + assertThat(actualMsg.getOriginator()).isEqualTo(deviceId); + + var notificationCaptor = ArgumentCaptor.forClass(DeviceActivityTrigger.class); + then(notificationRuleProcessor).should().process(notificationCaptor.capture()); + var actualNotification = notificationCaptor.getValue(); + assertThat(actualNotification.getTenantId()).isEqualTo(tenantId); + assertThat(actualNotification.getDeviceId()).isEqualTo(deviceId); + assertThat(actualNotification.isActive()).isTrue(); + } + } + + private static Stream provideParametersForUpdateActivityState() { + return Stream.of( + Arguments.of(true, 100, 120, 80, 80, false, false), + + Arguments.of(true, 100, 120, 100, 100, false, false), + + Arguments.of(false, 100, 120, 110, 110, false, true), + + + Arguments.of(true, 100, 100, 80, 80, false, false), + + Arguments.of(true, 100, 100, 100, 100, false, false), + + Arguments.of(false, 100, 100, 110, 0, true, true), + + + Arguments.of(false, 100, 110, 110, 0, true, true), + + Arguments.of(false, 100, 110, 120, 0, true, true), + + + Arguments.of(true, 0, 0, 0, 0, false, false), + + Arguments.of(false, 0, 0, 0, 0, true, true) + ); + } + + @ParameterizedTest + @MethodSource("provideParametersForDecreaseInactivityTimeout") + public void givenTestParameters_whenOnDeviceInactivityTimeout_thenShouldBeInTheExpectedStateAndPerformExpectedActions( + boolean activityState, long newInactivityTimeout, long timeIncrement, boolean expectedActivityState + ) throws Exception { + // GIVEN + long defaultInactivityTimeout = 10000; + initStateService(defaultInactivityTimeout); + + var currentTime = new AtomicLong(System.currentTimeMillis()); + + DeviceState deviceState = DeviceState.builder() + .active(activityState) + .lastActivityTime(currentTime.get()) + .inactivityTimeout(defaultInactivityTimeout) + .build(); + + DeviceStateData deviceStateData = DeviceStateData.builder() + .tenantId(tenantId) + .deviceId(deviceId) + .state(deviceState) + .metaData(new TbMsgMetaData()) + .build(); + + service.deviceStates.put(deviceId, deviceStateData); + service.getPartitionedEntities(tpi).add(deviceId); + + given(service.getCurrentTimeMillis()).willReturn(currentTime.addAndGet(timeIncrement)); + + // WHEN + service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newInactivityTimeout); + + // THEN + assertThat(deviceState.getInactivityTimeout()).isEqualTo(newInactivityTimeout); + assertThat(deviceState.isActive()).isEqualTo(expectedActivityState); + if (activityState && !expectedActivityState) { + then(telemetrySubscriptionService).should().saveAttrAndNotify( + any(), eq(deviceId), any(), eq(ACTIVITY_STATE), eq(false), any() + ); + } + } + + private static Stream provideParametersForDecreaseInactivityTimeout() { + return Stream.of( + Arguments.of(true, 1, 0, true), + + Arguments.of(true, 1, 1, false) + ); + } + + @Test + public void givenStateDataIsNull_whenUpdateInactivityTimeoutIfExpired_thenShouldCleanupDevice() { + // GIVEN + service.deviceStates.put(deviceId, deviceStateDataMock); + + // WHEN + service.updateInactivityStateIfExpired(System.currentTimeMillis(), deviceId, null); + + // THEN + assertThat(service.deviceStates.get(deviceId)).isNull(); + assertThat(service.deviceStates.size()).isEqualTo(0); + assertThat(service.deviceStates.isEmpty()).isTrue(); + } + + @Test + public void givenNotMyPartition_whenUpdateInactivityTimeoutIfExpired_thenShouldCleanupDevice() { + // GIVEN + long currentTime = System.currentTimeMillis(); + + DeviceState deviceState = DeviceState.builder() + .active(true) + .lastConnectTime(currentTime - 8000) + .lastActivityTime(currentTime - 4000) + .lastDisconnectTime(0) + .lastInactivityAlarmTime(0) + .inactivityTimeout(3000) + .build(); + + DeviceStateData stateData = DeviceStateData.builder() + .tenantId(tenantId) + .deviceId(deviceId) + .deviceCreationTime(currentTime - 10000) + .state(deviceState) + .build(); + + service.deviceStates.put(deviceId, stateData); + + var notMyTpi = TopicPartitionInfo.builder().myPartition(false).build(); + given(partitionService.resolve(ServiceType.TB_CORE, tenantId, deviceId)).willReturn(notMyTpi); + + // WHEN + service.updateInactivityStateIfExpired(System.currentTimeMillis(), deviceId, stateData); + + // THEN + assertThat(service.deviceStates.get(deviceId)).isNull(); + assertThat(service.deviceStates.size()).isEqualTo(0); + assertThat(service.deviceStates.isEmpty()).isTrue(); + } + + @ParameterizedTest + @MethodSource("provideParametersForUpdateInactivityStateIfExpired") + public void givenTestParameters_whenUpdateInactivityStateIfExpired_thenShouldBeInTheExpectedStateAndPerformExpectedActions( + boolean activityState, long ts, long lastActivityTime, long lastInactivityAlarmTime, long inactivityTimeout, long deviceCreationTime, + boolean expectedActivityState, long expectedLastInactivityAlarmTime, boolean shouldUpdateActivityStateToInactive + ) { + // GIVEN + var state = DeviceState.builder() + .active(activityState) + .lastActivityTime(lastActivityTime) + .lastInactivityAlarmTime(lastInactivityAlarmTime) + .inactivityTimeout(inactivityTimeout) + .build(); + + var deviceStateData = DeviceStateData.builder() + .tenantId(tenantId) + .deviceId(deviceId) + .deviceCreationTime(deviceCreationTime) + .metaData(new TbMsgMetaData()) + .state(state) + .build(); + + if (shouldUpdateActivityStateToInactive) { + given(partitionService.resolve(ServiceType.TB_CORE, tenantId, deviceId)).willReturn(tpi); + } + + // WHEN + service.updateInactivityStateIfExpired(ts, deviceId, deviceStateData); + + // THEN + assertThat(state.isActive()).isEqualTo(expectedActivityState); + assertThat(state.getLastInactivityAlarmTime()).isEqualTo(expectedLastInactivityAlarmTime); + + if (shouldUpdateActivityStateToInactive) { + then(telemetrySubscriptionService).should().saveAttrAndNotify( + eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(SERVER_SCOPE), eq(ACTIVITY_STATE), eq(false), any() + ); + + var msgCaptor = ArgumentCaptor.forClass(TbMsg.class); + then(clusterService).should().pushMsgToRuleEngine(eq(tenantId), eq(deviceId), msgCaptor.capture(), any()); + var actualMsg = msgCaptor.getValue(); + assertThat(actualMsg.getType()).isEqualTo(TbMsgType.INACTIVITY_EVENT.name()); + assertThat(actualMsg.getOriginator()).isEqualTo(deviceId); + + var notificationCaptor = ArgumentCaptor.forClass(DeviceActivityTrigger.class); + then(notificationRuleProcessor).should().process(notificationCaptor.capture()); + var actualNotification = notificationCaptor.getValue(); + assertThat(actualNotification.getTenantId()).isEqualTo(tenantId); + assertThat(actualNotification.getDeviceId()).isEqualTo(deviceId); + assertThat(actualNotification.isActive()).isFalse(); + + then(telemetrySubscriptionService).should().saveAttrAndNotify( + eq(TenantId.SYS_TENANT_ID), eq(deviceId), eq(SERVER_SCOPE), + eq(INACTIVITY_ALARM_TIME), eq(expectedLastInactivityAlarmTime), any() + ); + } + } + + private static Stream provideParametersForUpdateInactivityStateIfExpired() { + return Stream.of( + Arguments.of(false, 100, 70, 90, 70, 60, false, 90, false), + + Arguments.of(false, 100, 40, 50, 70, 10, false, 50, false), + + Arguments.of(false, 100, 25, 60, 75, 25, false, 60, false), + + Arguments.of(false, 100, 60, 70, 10, 50, false, 70, false), + + Arguments.of(false, 100, 10, 15, 90, 10, false, 15, false), + + Arguments.of(false, 100, 0, 40, 75, 0, false, 40, false), + + Arguments.of(true, 100, 90, 80, 80, 50, true, 80, false), + + Arguments.of(true, 100, 95, 90, 10, 50, true, 90, false), + + Arguments.of(true, 100, 10, 10, 90, 10, false, 100, true), + + Arguments.of(true, 100, 10, 10, 90, 11, true, 10, false), + + Arguments.of(true, 100, 15, 10, 85, 5, false, 100, true), + + Arguments.of(true, 100, 15, 10, 75, 5, false, 100, true), + + Arguments.of(true, 100, 95, 90, 5, 50, false, 100, true), + + Arguments.of(true, 100, 0, 0, 101, 0, true, 0, false), + + Arguments.of(true, 100, 0, 0, 100, 0, false, 100, true), + + Arguments.of(true, 100, 0, 0, 99, 0, false, 100, true), + + Arguments.of(true, 100, 0, 0, 120, 10, true, 0, false), + + Arguments.of(true, 100, 50, 0, 100, 0, true, 0, false), + + Arguments.of(true, 100, 10, 0, 91, 0, true, 0, false), + + Arguments.of(true, 100, 90, 0, 10, 0, false, 100, true), + + Arguments.of(true, 100, 100, 100, 1, 0, true, 100, false), + + Arguments.of(true, 100, 100, 100, 100, 100, true, 100, false), + + Arguments.of(false, 100, 59, 60, 30, 10, false, 60, false), + + Arguments.of(true, 100, 60, 60, 30, 10, false, 100, true), + + Arguments.of(true, 100, 61, 60, 30, 10, false, 100, true), + + Arguments.of(true, 0, 0, 0, 1, 0, true, 0, false), + + Arguments.of(true, 0, 0, 0, 0, 0, false, 0, true), + + Arguments.of(true, 100, 90, 80, 20, 70, true, 80, false), + + Arguments.of(true, 100, 80, 90, 30, 70, true, 90, false) + ); } -} \ No newline at end of file +} diff --git a/application/src/test/java/org/thingsboard/server/service/sync/ie/BaseExportImportServiceTest.java b/application/src/test/java/org/thingsboard/server/service/sync/ie/BaseExportImportServiceTest.java index aa15b1da4c..b9b023e9e9 100644 --- a/application/src/test/java/org/thingsboard/server/service/sync/ie/BaseExportImportServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/sync/ie/BaseExportImportServiceTest.java @@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.debug.TbMsgGeneratorNode; import org.thingsboard.rule.engine.debug.TbMsgGeneratorNodeConfiguration; +import org.thingsboard.rule.engine.metadata.TbGetAttributesNode; import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; @@ -51,6 +52,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.relation.EntityRelation; @@ -86,6 +88,7 @@ import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Collections; import java.util.UUID; +import java.util.function.Function; import static org.assertj.core.api.Assertions.assertThat; @@ -333,6 +336,7 @@ public abstract class BaseExportImportServiceTest extends AbstractControllerTest RuleNode ruleNode2 = new RuleNode(); ruleNode2.setName("Simple Rule Node 2"); ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode2.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); ruleNode2.setDebugMode(true); TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration(); configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2")); @@ -340,8 +344,8 @@ public abstract class BaseExportImportServiceTest extends AbstractControllerTest metaData.setNodes(Arrays.asList(ruleNode1, ruleNode2)); metaData.setFirstNodeIndex(0); - metaData.addConnectionInfo(0, 1, "Success"); - ruleChainService.saveRuleChainMetaData(tenantId, metaData); + metaData.addConnectionInfo(0, 1, TbNodeConnectionType.SUCCESS); + ruleChainService.saveRuleChainMetaData(tenantId, metaData, Function.identity()); return ruleChainService.findRuleChainById(tenantId, ruleChain.getId()); } @@ -361,6 +365,7 @@ public abstract class BaseExportImportServiceTest extends AbstractControllerTest RuleNode ruleNode1 = new RuleNode(); ruleNode1.setName("Simple Rule Node 1"); ruleNode1.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode1.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); ruleNode1.setDebugMode(true); TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration(); configuration1.setServerAttributeNames(Collections.singletonList("serverAttributeKey1")); @@ -369,6 +374,7 @@ public abstract class BaseExportImportServiceTest extends AbstractControllerTest RuleNode ruleNode2 = new RuleNode(); ruleNode2.setName("Simple Rule Node 2"); ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode2.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); ruleNode2.setDebugMode(true); TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration(); configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2")); @@ -376,8 +382,8 @@ public abstract class BaseExportImportServiceTest extends AbstractControllerTest metaData.setNodes(Arrays.asList(ruleNode1, ruleNode2)); metaData.setFirstNodeIndex(0); - metaData.addConnectionInfo(0, 1, "Success"); - ruleChainService.saveRuleChainMetaData(tenantId, metaData); + metaData.addConnectionInfo(0, 1, TbNodeConnectionType.SUCCESS); + ruleChainService.saveRuleChainMetaData(tenantId, metaData, Function.identity()); return ruleChainService.findRuleChainById(tenantId, ruleChain.getId()); } diff --git a/application/src/test/java/org/thingsboard/server/service/sync/ie/ExportImportServiceSqlTest.java b/application/src/test/java/org/thingsboard/server/service/sync/ie/ExportImportServiceSqlTest.java index e493916b8a..093275f734 100644 --- a/application/src/test/java/org/thingsboard/server/service/sync/ie/ExportImportServiceSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/service/sync/ie/ExportImportServiceSqlTest.java @@ -53,6 +53,7 @@ import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.data.script.ScriptLanguage; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.sync.ie.DeviceExportData; import org.thingsboard.server.common.data.sync.ie.EntityExportData; @@ -65,9 +66,12 @@ import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.service.action.EntityActionService; import org.thingsboard.server.service.ota.OtaPackageStateService; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -253,6 +257,7 @@ public class ExportImportServiceSqlTest extends BaseExportImportServiceTest { Asset asset1 = createAsset(tenantId1, null, assetProfile.getId(), "Asset 1"); Asset asset2 = createAsset(tenantId1, null, assetProfile.getId(), "Asset 2"); Dashboard dashboard = createDashboard(tenantId1, null, "Dashboard 1"); + Dashboard otherDashboard = createDashboard(tenantId1, null, "Dashboard 2"); DeviceProfile existingDeviceProfile = createDeviceProfile(tenantId2, null, null, "Existing"); String aliasId = "23c4185d-1497-9457-30b2-6d91e69a5b2c"; @@ -261,20 +266,43 @@ public class ExportImportServiceSqlTest extends BaseExportImportServiceTest { "\"" + aliasId + "\": {\n" + "\"alias\": \"assets\",\n" + "\"filter\": {\n" + - "\"entityList\": [\n" + - "\"" + asset1.getId().toString() + "\",\n" + - "\"" + asset2.getId().toString() + "\",\n" + - "\"" + tenantId1.getId().toString() + "\",\n" + - "\"" + existingDeviceProfile.getId().toString() + "\",\n" + - "\"" + unknownUuid + "\"\n" + - "],\n" + - "\"resolveMultiple\": true\n" + + " \"entityList\": [\n" + + " \"" + asset1.getId() + "\",\n" + + " \"" + asset2.getId() + "\",\n" + + " \"" + tenantId1.getId() + "\",\n" + + " \"" + existingDeviceProfile.getId() + "\",\n" + + " \"" + unknownUuid + "\"\n" + + " ],\n" + + " \"id\":\"" + asset1.getId() + "\",\n" + + " \"resolveMultiple\": true\n" + "},\n" + "\"id\": \"" + aliasId + "\"\n" + "}\n" + "}"; + String widgetId = "ea8f34a0-264a-f11f-cde3-05201bb4ff4b"; + String actionId = "4a8e6efa-3e68-fa59-7feb-d83366130cae"; + String widgets = "{\n" + + " \"" + widgetId + "\": {\n" + + " \"config\": {\n" + + " \"actions\": {\n" + + " \"rowClick\": [\n" + + " {\n" + + " \"name\": \"go to dashboard\",\n" + + " \"targetDashboardId\": \"" + otherDashboard.getId() + "\",\n" + + " \"id\": \"" + actionId + "\"\n" + + " }\n" + + " ]\n" + + " }\n" + + " },\n" + + " \"row\": 0,\n" + + " \"col\": 0,\n" + + " \"id\": \"" + widgetId + "\"\n" + + " }\n" + + "}"; + ObjectNode dashboardConfiguration = JacksonUtil.newObjectNode(); dashboardConfiguration.set("entityAliases", JacksonUtil.toJsonNode(entityAliases)); + dashboardConfiguration.set("widgets", JacksonUtil.toJsonNode(widgets)); dashboardConfiguration.set("description", new TextNode("hallo")); dashboard.setConfiguration(dashboardConfiguration); dashboard = dashboardService.saveDashboard(dashboard); @@ -284,10 +312,12 @@ public class ExportImportServiceSqlTest extends BaseExportImportServiceTest { EntityExportData asset1ExportData = exportEntity(tenantAdmin1, asset1.getId()); EntityExportData asset2ExportData = exportEntity(tenantAdmin1, asset2.getId()); EntityExportData dashboardExportData = exportEntity(tenantAdmin1, dashboard.getId()); + EntityExportData otherDashboardExportData = exportEntity(tenantAdmin1, otherDashboard.getId()); AssetProfile importedProfile = importEntity(tenantAdmin2, profileExportData).getSavedEntity(); Asset importedAsset1 = importEntity(tenantAdmin2, asset1ExportData).getSavedEntity(); Asset importedAsset2 = importEntity(tenantAdmin2, asset2ExportData).getSavedEntity(); + Dashboard importedOtherDashboard = importEntity(tenantAdmin2, otherDashboardExportData).getSavedEntity(); Dashboard importedDashboard = importEntity(tenantAdmin2, dashboardExportData).getSavedEntity(); Map.Entry entityAlias = importedDashboard.getConfiguration().get("entityAliases").fields().next(); @@ -307,6 +337,17 @@ public class ExportImportServiceSqlTest extends BaseExportImportServiceTest { .isEqualTo(existingDeviceProfile.getId().toString()); assertThat(aliasEntitiesIds).element(4).as("unresolved uuid was replaced with tenant id") .isEqualTo(tenantId2.toString()); + assertThat(entityAlias.getValue().get("filter").get("id").asText()).as("external asset 1 was replaced with imported one") + .isEqualTo(importedAsset1.getId().toString()); + + ObjectNode widgetConfig = importedDashboard.getWidgetsConfig().get(0); + assertThat(widgetConfig.get("id").asText()).as("widget id is not replaced") + .isEqualTo(widgetId); + JsonNode actionConfig = widgetConfig.get("config").get("actions").get("rowClick").get(0); + assertThat(actionConfig.get("id").asText()).as("action id is not replaced") + .isEqualTo(actionId); + assertThat(actionConfig.get("targetDashboardId").asText()).as("dashboard id is replaced with imported one") + .isEqualTo(importedOtherDashboard.getId().toString()); } @@ -338,6 +379,47 @@ public class ExportImportServiceSqlTest extends BaseExportImportServiceTest { checkImportedRuleChainData(ruleChain, metaData, importedRuleChain, importedMetaData); } + @Test + public void testImportRuleChain_ruleNodesConfigs() throws Exception { + Customer customer = createCustomer(tenantId1, "Customer 1"); + RuleChain ruleChain = createRuleChain(tenantId1, "Rule chain 1"); + RuleChainMetaData metaData = ruleChainService.loadRuleChainMetaData(tenantId1, ruleChain.getId()); + + List nodes = new ArrayList<>(metaData.getNodes()); + RuleNode generatorNode = new RuleNode(); + generatorNode.setName("Generator"); + generatorNode.setType(TbMsgGeneratorNode.class.getName()); + TbMsgGeneratorNodeConfiguration generatorNodeConfig = new TbMsgGeneratorNodeConfiguration(); + generatorNodeConfig.setOriginatorType(EntityType.ASSET_PROFILE); + generatorNodeConfig.setOriginatorId(customer.getId().toString()); + generatorNodeConfig.setPeriodInSeconds(5); + generatorNodeConfig.setMsgCount(1); + generatorNodeConfig.setScriptLang(ScriptLanguage.JS); + UUID someUuid = UUID.randomUUID(); + generatorNodeConfig.setJsScript("var msg = { temp: 42, humidity: 77 };\n" + + "var metadata = { data: 40 };\n" + + "var msgType = \"POST_TELEMETRY_REQUEST\";\n" + + "var someUuid = \"" + someUuid + "\";\n" + + "return { msg: msg, metadata: metadata, msgType: msgType };"); + generatorNode.setConfiguration(JacksonUtil.valueToTree(generatorNodeConfig)); + nodes.add(generatorNode); + metaData.setNodes(nodes); + ruleChainService.saveRuleChainMetaData(tenantId1, metaData, Function.identity()); + + EntityExportData ruleChainExportData = exportEntity(tenantAdmin1, ruleChain.getId()); + EntityExportData customerExportData = exportEntity(tenantAdmin1, customer.getId()); + + Customer importedCustomer = importEntity(tenantAdmin2, customerExportData).getSavedEntity(); + RuleChain importedRuleChain = importEntity(tenantAdmin2, ruleChainExportData).getSavedEntity(); + RuleChainMetaData importedMetaData = ruleChainService.loadRuleChainMetaData(tenantId2, importedRuleChain.getId()); + + TbMsgGeneratorNodeConfiguration importedGeneratorNodeConfig = JacksonUtil.treeToValue(importedMetaData.getNodes().stream() + .filter(node -> node.getName().equals(generatorNode.getName())) + .findFirst().get().getConfiguration(), TbMsgGeneratorNodeConfiguration.class); + assertThat(importedGeneratorNodeConfig.getOriginatorId()).isEqualTo(importedCustomer.getId().toString()); + assertThat(importedGeneratorNodeConfig.getJsScript()).contains("var someUuid = \"" + someUuid + "\";"); + } + @Test public void testExportImportWithInboundRelations_betweenTenants() throws Exception { @@ -510,7 +592,7 @@ public class ExportImportServiceSqlTest extends BaseExportImportServiceTest { Customer updatedCustomer = importEntity(tenantAdmin2, updatedCustomerEntity).getSavedEntity(); verify(entityActionService).logEntityAction(any(), eq(importedCustomer.getId()), eq(updatedCustomer), any(), eq(ActionType.UPDATED), isNull()); - verify(tbClusterService).sendNotificationMsgToEdge(any(), any(), eq(importedCustomer.getId()), any(), any(), eq(EdgeEventActionType.UPDATED)); + verify(tbClusterService).sendNotificationMsgToEdge(any(), any(), eq(importedCustomer.getId()), any(), any(), eq(EdgeEventActionType.UPDATED), any()); Mockito.reset(entityActionService); @@ -527,7 +609,7 @@ public class ExportImportServiceSqlTest extends BaseExportImportServiceTest { verify(entityActionService).logEntityAction(any(), eq(importedAssetProfile.getId()), eq(importedAssetProfile), any(), eq(ActionType.ADDED), isNull()); verify(tbClusterService).broadcastEntityStateChangeEvent(any(), eq(importedAssetProfile.getId()), eq(ComponentLifecycleEvent.CREATED)); - verify(tbClusterService).sendNotificationMsgToEdge(any(), any(), eq(importedAssetProfile.getId()), any(), any(), eq(EdgeEventActionType.ADDED)); + verify(tbClusterService).sendNotificationMsgToEdge(any(), any(), eq(importedAssetProfile.getId()), any(), any(), eq(EdgeEventActionType.ADDED), any()); Asset importedAsset = (Asset) importEntity(tenantAdmin2, getAndClone(entitiesExportData, EntityType.ASSET)).getSavedEntity(); verify(entityActionService).logEntityAction(any(), eq(importedAsset.getId()), eq(importedAsset), @@ -543,14 +625,14 @@ public class ExportImportServiceSqlTest extends BaseExportImportServiceTest { verify(entityActionService).logEntityAction(any(), eq(importedAsset.getId()), eq(updatedAsset), any(), eq(ActionType.UPDATED), isNull()); - verify(tbClusterService).sendNotificationMsgToEdge(any(), any(), eq(importedAsset.getId()), any(), any(), eq(EdgeEventActionType.UPDATED)); + verify(tbClusterService).sendNotificationMsgToEdge(any(), any(), eq(importedAsset.getId()), any(), any(), eq(EdgeEventActionType.UPDATED), any()); DeviceProfile importedDeviceProfile = (DeviceProfile) importEntity(tenantAdmin2, getAndClone(entitiesExportData, EntityType.DEVICE_PROFILE)).getSavedEntity(); verify(entityActionService).logEntityAction(any(), eq(importedDeviceProfile.getId()), eq(importedDeviceProfile), any(), eq(ActionType.ADDED), isNull()); verify(tbClusterService).onDeviceProfileChange(eq(importedDeviceProfile), any()); verify(tbClusterService).broadcastEntityStateChangeEvent(any(), eq(importedDeviceProfile.getId()), eq(ComponentLifecycleEvent.CREATED)); - verify(tbClusterService).sendNotificationMsgToEdge(any(), any(), eq(importedDeviceProfile.getId()), any(), any(), eq(EdgeEventActionType.ADDED)); + verify(tbClusterService).sendNotificationMsgToEdge(any(), any(), eq(importedDeviceProfile.getId()), any(), any(), eq(EdgeEventActionType.ADDED), any()); verify(otaPackageStateService).update(eq(importedDeviceProfile), eq(false), eq(false)); Device importedDevice = (Device) importEntity(tenantAdmin2, getAndClone(entitiesExportData, EntityType.DEVICE)).getSavedEntity(); diff --git a/application/src/test/java/org/thingsboard/server/service/transport/DefaultTransportApiServiceTest.java b/application/src/test/java/org/thingsboard/server/service/transport/DefaultTransportApiServiceTest.java index 9687225bd1..43d8960f3b 100644 --- a/application/src/test/java/org/thingsboard/server/service/transport/DefaultTransportApiServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/transport/DefaultTransportApiServiceTest.java @@ -16,7 +16,6 @@ package org.thingsboard.server.service.transport; -import com.google.common.util.concurrent.Futures; import lombok.extern.slf4j.Slf4j; import org.junit.Before; import org.junit.Test; @@ -45,12 +44,13 @@ import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.DataDecodingEncodingService; import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.profile.TbDeviceProfileCache; -import org.thingsboard.server.service.resource.TbResourceService; import java.io.IOException; import java.nio.file.Files; @@ -62,6 +62,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -94,7 +96,7 @@ public class DefaultTransportApiServiceTest { @MockBean protected DeviceProvisionService deviceProvisionService; @MockBean - protected TbResourceService resourceService; + protected ResourceService resourceService; @MockBean protected OtaPackageService otaPackageService; @MockBean @@ -123,11 +125,13 @@ public class DefaultTransportApiServiceTest { @Test public void validateExistingDeviceByX509CertificateStrategy() { var device = createDevice(); - when(deviceService.findDeviceByIdAsync(any(), any())).thenReturn(Futures.immediateFuture(device)); var deviceCredentials = createDeviceCredentials(chain[0], device.getId()); when(deviceCredentialsService.findDeviceCredentialsByCredentialsId(any())).thenReturn(deviceCredentials); + TransportProtos.TransportApiResponseMsg response = mock(TransportProtos.TransportApiResponseMsg.class); + willReturn(response).given(service).getDeviceInfo(deviceCredentials); + service.validateOrCreateDeviceX509Certificate(certificateChain); verify(deviceCredentialsService, times(1)).findDeviceCredentialsByCredentialsId(any()); } @@ -139,7 +143,6 @@ public class DefaultTransportApiServiceTest { var device = createDevice(); when(deviceService.findDeviceByTenantIdAndName(any(), any())).thenReturn(device); - when(deviceService.findDeviceByIdAsync(any(), any())).thenReturn(Futures.immediateFuture(device)); var deviceCredentials = createDeviceCredentials(chain[0], device.getId()); when(deviceCredentialsService.findDeviceCredentialsByCredentialsId(any())).thenReturn(null); @@ -148,9 +151,12 @@ public class DefaultTransportApiServiceTest { var provisionResponse = createProvisionResponse(deviceCredentials); when(deviceProvisionService.provisionDeviceViaX509Chain(any(), any())).thenReturn(provisionResponse); + TransportProtos.TransportApiResponseMsg response = mock(TransportProtos.TransportApiResponseMsg.class); + willReturn(response).given(service).getDeviceInfo(deviceCredentials); + service.validateOrCreateDeviceX509Certificate(certificateChain); verify(deviceProfileService, times(1)).findDeviceProfileByProvisionDeviceKey(any()); - verify(deviceService, times(1)).findDeviceByIdAsync(any(), any()); + verify(service, times(1)).getDeviceInfo(any()); verify(deviceCredentialsService, times(1)).findDeviceCredentialsByCredentialsId(any()); verify(deviceProvisionService, times(1)).provisionDeviceViaX509Chain(any(), any()); } diff --git a/application/src/test/java/org/thingsboard/server/service/ttl/AlarmsCleanUpServiceTest.java b/application/src/test/java/org/thingsboard/server/service/ttl/AlarmsCleanUpServiceTest.java index 6cabff788e..b2c06b419b 100644 --- a/application/src/test/java/org/thingsboard/server/service/ttl/AlarmsCleanUpServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/ttl/AlarmsCleanUpServiceTest.java @@ -67,7 +67,7 @@ public class AlarmsCleanUpServiceTest extends AbstractControllerTest { @Test public void testAlarmsCleanUp() throws Exception { int ttlDays = 1; - updateDefaultTenantProfile(profileConfiguration -> { + updateDefaultTenantProfileConfig(profileConfiguration -> { profileConfiguration.setAlarmsTtlDays(ttlDays); }); @@ -104,10 +104,10 @@ public class AlarmsCleanUpServiceTest extends AbstractControllerTest { alarmsCleanUpService.cleanUp(); for (AlarmId outdatedAlarm : outdatedAlarms) { - verify(alarmService).delAlarm(eq(tenantId), eq(outdatedAlarm)); + verify(alarmService).delAlarm(eq(tenantId), eq(outdatedAlarm), eq(false)); } for (AlarmId freshAlarm : freshAlarms) { - verify(alarmService, never()).delAlarm(eq(tenantId), eq(freshAlarm)); + verify(alarmService, never()).delAlarm(eq(tenantId), eq(freshAlarm), eq(false)); } verify(cleanUpServiceLogger).info(startsWith("Removed {} outdated alarm"), eq((long) count), eq(tenantId), any()); diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestCallback.java b/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestCallback.java index 07eadd731d..527846bc36 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestCallback.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestCallback.java @@ -25,9 +25,9 @@ import org.eclipse.californium.core.coap.CoAP; @Data public class CoapTestCallback implements CoapHandler { - protected Integer observe; - protected byte[] payloadBytes; - protected CoAP.ResponseCode responseCode; + protected volatile Integer observe; + protected volatile byte[] payloadBytes; + protected volatile CoAP.ResponseCode responseCode; public Integer getObserve() { return observe; diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java b/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java index 6c5f682a4f..3124571dc8 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.transport.coap; +import lombok.Getter; import org.eclipse.californium.core.CoapClient; import org.eclipse.californium.core.CoapHandler; import org.eclipse.californium.core.CoapObserveRelation; @@ -34,7 +35,10 @@ public class CoapTestClient { private final CoapClient client; - public CoapTestClient(){ + @Getter + private CoAP.Type type = CoAP.Type.CON; + + public CoapTestClient() { this.client = createClient(); } @@ -80,9 +84,13 @@ public class CoapTestClient { return client.setTimeout(CLIENT_REQUEST_TIMEOUT).get(); } - public CoapObserveRelation getObserveRelation(CoapTestCallback callback){ + public CoapObserveRelation getObserveRelation(CoapTestCallback callback) { + return getObserveRelation(callback, true); + } + + public CoapObserveRelation getObserveRelation(CoapTestCallback callback, boolean confirmable) { Request request = Request.newGet().setObserve(); - request.setType(CoAP.Type.CON); + request.setType(confirmable ? CoAP.Type.CON : CoAP.Type.NON); return client.observe(request, callback); } @@ -94,12 +102,28 @@ public class CoapTestClient { } public void setURI(String accessToken, FeatureType featureType) { - if (featureType == null){ + if (featureType == null) { featureType = FeatureType.ATTRIBUTES; } setURI(getFeatureTokenUrl(accessToken, featureType)); } + public void useCONs() { + if (client == null) { + throw new RuntimeException("Failed to connect! CoapClient is not initialized!"); + } + type = CoAP.Type.CON; + client.useCONs(); + } + + public void useNONs() { + if (client == null) { + throw new RuntimeException("Failed to connect! CoapClient is not initialized!"); + } + type = CoAP.Type.NON; + client.useNONs(); + } + private CoapClient createClient() { return new CoapClient(); } diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/client/CoapClientIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/client/CoapClientIntegrationTest.java new file mode 100644 index 0000000000..e50812403b --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/coap/client/CoapClientIntegrationTest.java @@ -0,0 +1,309 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap.client; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.CoapHandler; +import org.eclipse.californium.core.CoapObserveRelation; +import org.eclipse.californium.core.CoapResponse; +import org.eclipse.californium.core.coap.CoAP; +import org.eclipse.californium.core.coap.MediaTypeRegistry; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.query.EntityKey; +import org.thingsboard.server.common.data.query.EntityKeyType; +import org.thingsboard.server.common.data.query.SingleEntityFilter; +import org.thingsboard.server.common.msg.session.FeatureType; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest; +import org.thingsboard.server.transport.coap.CoapTestCallback; +import org.thingsboard.server.transport.coap.CoapTestClient; +import org.thingsboard.server.transport.coap.CoapTestConfigProperties; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.common.data.query.EntityKeyType.CLIENT_ATTRIBUTE; +import static org.thingsboard.server.common.data.query.EntityKeyType.SHARED_ATTRIBUTE; + +@Slf4j +@DaoSqlTest +public class CoapClientIntegrationTest extends AbstractCoapIntegrationTest { + + private static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," + + " \"key5\": {\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}}"; + private static final List EXPECTED_KEYS = Arrays.asList("key1", "key2", "key3", "key4", "key5"); + private static final String DEVICE_RESPONSE = "{\"value1\":\"A\",\"value2\":\"B\"}"; + + + @Before + public void beforeTest() throws Exception { + CoapTestConfigProperties configProperties = CoapTestConfigProperties.builder() + .deviceName("Test Post Attributes device") + .build(); + processBeforeTest(configProperties); + } + + @After + public void afterTest() throws Exception { + processAfterTest(); + } + + @Test + public void testConfirmableRequests() throws Exception { + boolean confirmable = true; + processAttributesTest(confirmable); + processTwoWayRpcTest(confirmable); + processTestRequestAttributesValuesFromTheServer(confirmable); + } + + @Test + public void testNonConfirmableRequests() throws Exception { + boolean confirmable = false; + processAttributesTest(confirmable); + processTwoWayRpcTest(confirmable); + processTestRequestAttributesValuesFromTheServer(confirmable); + } + + protected void processAttributesTest(boolean confirmable) throws Exception { + client = createClientForFeatureWithConfirmableParameter(FeatureType.ATTRIBUTES, confirmable); + CoapResponse coapResponse = client.postMethod(PAYLOAD_VALUES_STR.getBytes()); + assertEquals(CoAP.ResponseCode.CREATED, coapResponse.getCode()); + assertEquals("CoAP response type is wrong!", client.getType(), coapResponse.advanced().getType()); + + DeviceId deviceId = savedDevice.getId(); + List actualKeys = getActualKeysList(deviceId); + assertNotNull(actualKeys); + + Set actualKeySet = new HashSet<>(actualKeys); + Set expectedKeySet = new HashSet<>(EXPECTED_KEYS); + assertEquals(expectedKeySet, actualKeySet); + + String attributesValuesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/attributes/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet); + ; + List> values = doGetAsyncTyped(attributesValuesUrl, new TypeReference<>() { + }); + assertAttributesValues(values, actualKeySet); + String deleteAttributesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/CLIENT_SCOPE?keys=" + String.join(",", actualKeySet); + doDelete(deleteAttributesUrl); + } + + protected void processTwoWayRpcTest(boolean confirmable) throws Exception { + client = createClientForFeatureWithConfirmableParameter(FeatureType.RPC, confirmable); + CoapTestCallback callbackCoap = new TestCoapCallbackForRPC(client); + + CoapObserveRelation observeRelation = client.getObserveRelation(callbackCoap, confirmable); + String awaitAlias = "await Two Way Rpc (client.getObserveRelation)"; + await(awaitAlias) + .atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .until(() -> CoAP.ResponseCode.VALID.equals(callbackCoap.getResponseCode()) && + callbackCoap.getObserve() != null && + 0 == callbackCoap.getObserve()); + validateCurrentStateNotification(callbackCoap); + + String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}"; + String deviceId = savedDevice.getId().getId().toString(); + int expectedObserveCountAfterGpioRequest1 = callbackCoap.getObserve() + 1; + String actualResult = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); + awaitAlias = "await Two Way Rpc (setGpio(method, params, value) first"; + await(awaitAlias) + .atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) && + callbackCoap.getObserve() != null && + expectedObserveCountAfterGpioRequest1 == callbackCoap.getObserve()); + validateTwoWayStateChangedNotification(callbackCoap, actualResult); + + int expectedObserveCountAfterGpioRequest2 = callbackCoap.getObserve() + 1; + actualResult = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); + awaitAlias = "await Two Way Rpc (setGpio(method, params, value) second"; + await(awaitAlias) + .atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS) + .until(() -> CoAP.ResponseCode.CONTENT.equals(callbackCoap.getResponseCode()) && + callbackCoap.getObserve() != null && + expectedObserveCountAfterGpioRequest2 == callbackCoap.getObserve()); + + validateTwoWayStateChangedNotification(callbackCoap, actualResult); + + observeRelation.proactiveCancel(); + assertTrue(observeRelation.isCanceled()); + } + + protected void processTestRequestAttributesValuesFromTheServer(boolean confirmable) throws Exception { + client = createClientForFeatureWithConfirmableParameter(FeatureType.ATTRIBUTES, confirmable); + SingleEntityFilter dtf = new SingleEntityFilter(); + dtf.setSingleEntity(savedDevice.getId()); + List csKeys = getEntityKeys(CLIENT_ATTRIBUTE); + List shKeys = getEntityKeys(SHARED_ATTRIBUTE); + List keys = new ArrayList<>(); + keys.addAll(csKeys); + keys.addAll(shKeys); + getWsClient().subscribeLatestUpdate(keys, dtf); + getWsClient().registerWaitForUpdate(2); + + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", + PAYLOAD_VALUES_STR, String.class, status().isOk()); + + CoapResponse coapResponse = client.postMethod(PAYLOAD_VALUES_STR); + assertEquals(CoAP.ResponseCode.CREATED, coapResponse.getCode()); + + String update = getWsClient().waitForUpdate(); + assertThat(update).as("ws update received").isNotBlank(); + + String keysParam = String.join(",", EXPECTED_KEYS); + String featureTokenUrl = CoapTestClient.getFeatureTokenUrl(accessToken, FeatureType.ATTRIBUTES) + "?clientKeys=" + keysParam + "&sharedKeys=" + keysParam; + client.setURI(featureTokenUrl); + CoapResponse response = client.getMethod(); + assertEquals("CoAP response type is wrong!", client.getType(), response.advanced().getType()); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + protected void assertAttributesValues(List> deviceValues, Set keySet) { + for (Map map : deviceValues) { + String key = (String) map.get("key"); + Object value = map.get("value"); + assertTrue(keySet.contains(key)); + switch (key) { + case "key1": + assertEquals("value1", value); + break; + case "key2": + assertEquals(true, value); + break; + case "key3": + assertEquals(3.0, value); + break; + case "key4": + assertEquals(4, value); + break; + case "key5": + assertNotNull(value); + assertEquals(3, ((LinkedHashMap) value).size()); + assertEquals(42, ((LinkedHashMap) value).get("someNumber")); + assertEquals(Arrays.asList(1, 2, 3), ((LinkedHashMap) value).get("someArray")); + LinkedHashMap someNestedObject = (LinkedHashMap) ((LinkedHashMap) value).get("someNestedObject"); + assertEquals("value", someNestedObject.get("key")); + break; + } + } + } + + private List getActualKeysList(DeviceId deviceId) throws Exception { + long start = System.currentTimeMillis(); + long end = System.currentTimeMillis() + 5000; + + List actualKeys = null; + while (start <= end) { + actualKeys = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/attributes/CLIENT_SCOPE", new TypeReference<>() { + }); + if (actualKeys.size() == EXPECTED_KEYS.size()) { + break; + } + Thread.sleep(100); + start += 100; + } + return actualKeys; + } + + private void validateCurrentStateNotification(CoapTestCallback callback) { + assertArrayEquals(EMPTY_PAYLOAD, callback.getPayloadBytes()); + } + + private void validateTwoWayStateChangedNotification(CoapTestCallback callback, String actualResult) { + assertEquals(DEVICE_RESPONSE, actualResult); + assertNotNull(callback.getPayloadBytes()); + } + + protected class TestCoapCallbackForRPC extends CoapTestCallback { + + private final CoapTestClient client; + + @Getter + private boolean wasSuccessful = false; + + TestCoapCallbackForRPC(CoapTestClient client) { + this.client = client; + } + + @Override + public void onLoad(CoapResponse response) { + payloadBytes = response.getPayload(); + responseCode = response.getCode(); + observe = response.getOptions().getObserve(); + wasSuccessful = client.getType().equals(response.advanced().getType()); + if (observe != null) { + if (observe > 0) { + processOnLoadResponse(response, client); + } + } + } + + @Override + public void onError() { + log.warn("Command Response Ack Error, No connect"); + } + } + + protected void processOnLoadResponse(CoapResponse response, CoapTestClient client) { + JsonNode responseJson = JacksonUtil.fromBytes(response.getPayload()); + int requestId = responseJson.get("id").asInt(); + client.setURI(CoapTestClient.getFeatureTokenUrl(accessToken, FeatureType.RPC, requestId)); + client.postMethod(new CoapHandler() { + @Override + public void onLoad(CoapResponse response) { + log.warn("RPC {} command response ack: {}", requestId, response.getCode()); + } + + @Override + public void onError() { + log.warn("RPC {} command response ack error, no connect", requestId); + } + }, DEVICE_RESPONSE, MediaTypeRegistry.APPLICATION_JSON); + } + + private CoapTestClient createClientForFeatureWithConfirmableParameter(FeatureType featureType, boolean confirmable) { + CoapTestClient coapTestClient = new CoapTestClient(accessToken, featureType); + if (confirmable) { + coapTestClient.useCONs(); + } else { + coapTestClient.useNONs(); + } + return coapTestClient; + } + + private List getEntityKeys(EntityKeyType scope) { + return CoapClientIntegrationTest.EXPECTED_KEYS.stream().map(key -> new EntityKey(scope, key)).collect(Collectors.toList()); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index 64382d5d51..8094718383 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -63,7 +63,7 @@ import org.thingsboard.server.common.data.query.SingleEntityFilter; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.service.ws.telemetry.cmd.TelemetryPluginCmdsWrapper; +import org.thingsboard.server.service.ws.telemetry.cmd.TelemetryCmdsWrapper; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.LatestValueCmd; @@ -206,7 +206,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte lwModel.setFileName(resourceName); lwModel.setTenantId(tenantId); byte[] bytes = IOUtils.toByteArray(AbstractLwM2MIntegrationTest.class.getClassLoader().getResourceAsStream("lwm2m/" + resourceName)); - lwModel.setData(Base64.getEncoder().encodeToString(bytes)); + lwModel.setData(bytes); lwModel = doPostWithTypedResponse("/api/resource", lwModel, new TypeReference<>() { }); Assert.assertNotNull(lwModel); @@ -229,10 +229,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null); - TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); - wrapper.setEntityDataCmds(Collections.singletonList(cmd)); - - getWsClient().send(JacksonUtil.toString(wrapper)); + getWsClient().send(cmd); getWsClient().waitForReply(); getWsClient().registerWaitForUpdate(); diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java index 55508d1b04..c23d1c615b 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java @@ -186,8 +186,12 @@ public abstract class AbstractMqttIntegrationTest extends AbstractTransportInteg } protected void subscribeAndWait(MqttTestClient client, String attrSubTopic, DeviceId deviceId, FeatureType featureType) throws MqttException { + subscribeAndWait(client, attrSubTopic, deviceId, featureType, MqttQoS.AT_MOST_ONCE); + } + + protected void subscribeAndWait(MqttTestClient client, String attrSubTopic, DeviceId deviceId, FeatureType featureType, MqttQoS mqttQoS) throws MqttException { int subscriptionCount = getDeviceActorSubscriptionCount(deviceId, featureType); - client.subscribeAndWait(attrSubTopic, MqttQoS.AT_MOST_ONCE); + client.subscribeAndWait(attrSubTopic, mqttQoS); // TODO: This test awaits for the device actor to receive the subscription. Ideally it should not happen. See details below: // The transport layer acknowledge subscription request once the message about subscription is in the queue. // Test sends data immediately after acknowledgement. diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/MqttTestCallback.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/MqttTestCallback.java index 208189ac4e..4b8646f8f5 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/MqttTestCallback.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/MqttTestCallback.java @@ -30,7 +30,7 @@ public class MqttTestCallback implements MqttCallback { protected CountDownLatch subscribeLatch; protected final CountDownLatch deliveryLatch; - protected int qoS; + protected int messageArrivedQoS; protected byte[] payloadBytes; protected boolean pubAckReceived; @@ -53,7 +53,7 @@ public class MqttTestCallback implements MqttCallback { @Override public void messageArrived(String requestTopic, MqttMessage mqttMessage) { log.warn("messageArrived on topic: {}", requestTopic); - qoS = mqttMessage.getQos(); + messageArrivedQoS = mqttMessage.getQos(); payloadBytes = mqttMessage.getPayload(); subscribeLatch.countDown(); } @@ -63,6 +63,5 @@ public class MqttTestCallback implements MqttCallback { log.warn("delivery complete: {}", iMqttDeliveryToken.getResponse()); pubAckReceived = iMqttDeliveryToken.getResponse().getType() == MqttWireMessage.MESSAGE_TYPE_PUBACK; deliveryLatch.countDown(); - } } diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/MqttTestSubscribeOnTopicCallback.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/MqttTestSubscribeOnTopicCallback.java index 0f3cc14629..8227a3e596 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/MqttTestSubscribeOnTopicCallback.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/MqttTestSubscribeOnTopicCallback.java @@ -36,7 +36,7 @@ public class MqttTestSubscribeOnTopicCallback extends MqttTestCallback { public void messageArrived(String requestTopic, MqttMessage mqttMessage) { log.warn("messageArrived on topic: {}, awaitSubTopic: {}", requestTopic, awaitSubTopic); if (awaitSubTopic.equals(requestTopic)) { - qoS = mqttMessage.getQos(); + messageArrivedQoS = mqttMessage.getQos(); payloadBytes = mqttMessage.getPayload(); subscribeLatch.countDown(); } diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/attributes/AbstractMqttAttributesIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/attributes/AbstractMqttAttributesIntegrationTest.java index 71d8809e38..c184ccef7d 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/attributes/AbstractMqttAttributesIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/attributes/AbstractMqttAttributesIntegrationTest.java @@ -575,14 +575,14 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt protected void validateJsonResponse(MqttTestCallback callback, String expectedResponse) throws InterruptedException { assertThat(callback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS)) .as("await callback").isTrue(); - assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getMessageArrivedQoS()); assertEquals(JacksonUtil.toJsonNode(expectedResponse), JacksonUtil.fromBytes(callback.getPayloadBytes())); } protected void validateProtoResponse(MqttTestCallback callback, TransportProtos.GetAttributeResponseMsg expectedResponse) throws InterruptedException, InvalidProtocolBufferException { assertThat(callback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS)) .as("await callback").isTrue(); - assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getMessageArrivedQoS()); TransportProtos.GetAttributeResponseMsg actualAttributesResponse = TransportProtos.GetAttributeResponseMsg.parseFrom(callback.getPayloadBytes()); assertEquals(expectedResponse.getRequestId(), actualAttributesResponse.getRequestId()); List expectedClientKeyValueProtos = expectedResponse.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); @@ -606,7 +606,7 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt protected void validateJsonResponseGateway(MqttTestCallback callback, String deviceName, String expectedValues) throws InterruptedException { assertThat(callback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS)) .as("await callback").isTrue(); - assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS()); + assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getMessageArrivedQoS()); String expectedRequestPayload = "{\"id\":1,\"device\":\"" + deviceName + "\",\"values\":" + expectedValues + "}"; assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.fromBytes(callback.getPayloadBytes())); } @@ -614,7 +614,7 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt protected void validateProtoClientResponseGateway(MqttTestCallback callback, String deviceName) throws InterruptedException, InvalidProtocolBufferException { assertThat(callback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS)) .as("await callback").isTrue(); - assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS()); + assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getMessageArrivedQoS()); TransportApiProtos.GatewayAttributeResponseMsg expectedGatewayAttributeResponseMsg = getExpectedGatewayAttributeResponseMsg(deviceName, true); TransportApiProtos.GatewayAttributeResponseMsg actualGatewayAttributeResponseMsg = TransportApiProtos.GatewayAttributeResponseMsg.parseFrom(callback.getPayloadBytes()); assertEquals(expectedGatewayAttributeResponseMsg.getDeviceName(), actualGatewayAttributeResponseMsg.getDeviceName()); @@ -631,7 +631,7 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt protected void validateProtoSharedResponseGateway(MqttTestCallback callback, String deviceName) throws InterruptedException, InvalidProtocolBufferException { assertThat(callback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS)) .as("await callback").isTrue(); - assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS()); + assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getMessageArrivedQoS()); TransportApiProtos.GatewayAttributeResponseMsg expectedGatewayAttributeResponseMsg = getExpectedGatewayAttributeResponseMsg(deviceName, false); TransportApiProtos.GatewayAttributeResponseMsg actualGatewayAttributeResponseMsg = TransportApiProtos.GatewayAttributeResponseMsg.parseFrom(callback.getPayloadBytes()); assertEquals(expectedGatewayAttributeResponseMsg.getDeviceName(), actualGatewayAttributeResponseMsg.getDeviceName()); diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/rpc/AbstractMqttServerSideRpcIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/rpc/AbstractMqttServerSideRpcIntegrationTest.java index e1f20a3d95..c128ef2d26 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/rpc/AbstractMqttServerSideRpcIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/rpc/AbstractMqttServerSideRpcIntegrationTest.java @@ -37,12 +37,13 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; +import org.thingsboard.server.common.data.rpc.Rpc; import org.thingsboard.server.common.msg.session.FeatureType; import org.thingsboard.server.gen.transport.TransportApiProtos; import org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest; import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestCallback; -import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestSubscribeOnTopicCallback; import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestClient; +import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestSubscribeOnTopicCallback; import java.util.ArrayList; import java.util.List; @@ -61,7 +62,7 @@ import static org.thingsboard.server.common.data.device.profile.MqttTopics.GATEW @Slf4j public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractMqttIntegrationTest { - protected static final String RPC_REQUEST_PROTO_SCHEMA = "syntax =\"proto3\";\n" + + protected static final String RPC_REQUEST_PROTO_SCHEMA = "syntax =\"proto3\";\n" + "package rpc;\n" + "\n" + "message RpcRequestMsg {\n" + @@ -105,7 +106,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM } else { assertEquals(JacksonUtil.toJsonNode(setGpioRequest), JacksonUtil.fromBytes(callback.getPayloadBytes())); } - assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getMessageArrivedQoS()); client.disconnect(); } @@ -176,9 +177,44 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM return builder.build(); } - protected void processSequenceTwoWayRpcTest() throws Exception { - List expected = new ArrayList<>(); - List result = new ArrayList<>(); + protected void processSequenceOneWayRpcTest(MqttQoS mqttQoS) throws Exception { + List expectedRequest = new ArrayList<>(); + List actualRequests = new ArrayList<>(); + + String deviceId = savedDevice.getId().getId().toString(); + + for (int i = 0; i < 10; i++) { + ObjectNode request = JacksonUtil.newObjectNode(); + request.put("method", "test"); + request.put("params", i); + expectedRequest.add(JacksonUtil.toString(request)); + request.put("persistent", true); + doPostAsync("/api/rpc/oneway/" + deviceId, JacksonUtil.toString(request), String.class, status().isOk()); + } + + MqttTestClient client = new MqttTestClient(); + client.connectAndWait(accessToken); + MqttTestOneWaySequenceCallback callback = new MqttTestOneWaySequenceCallback(client, 10, actualRequests); + client.setCallback(callback); + subscribeAndWait(client, DEVICE_RPC_REQUESTS_SUB_TOPIC, savedDevice.getId(), FeatureType.RPC, mqttQoS); + + callback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS); + assertEquals(expectedRequest, actualRequests); + client.disconnect(); + } + + protected void processSequenceTwoWayRpcTest(MqttQoS mqttQoS) throws Exception { + processSequenceTwoWayRpcTest(mqttQoS, false); + } + + protected void processSequenceTwoWayRpcTest(MqttQoS mqttQoS, boolean manualAcksEnabled) throws Exception { + List expectedRequest = new ArrayList<>(); + List actualRequests = new ArrayList<>(); + + List rpcIds = new ArrayList<>(); + + List expectedResponses = new ArrayList<>(); + List actualResponses = new ArrayList<>(); String deviceId = savedDevice.getId().getId().toString(); @@ -186,20 +222,32 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM ObjectNode request = JacksonUtil.newObjectNode(); request.put("method", "test"); request.put("params", i); - expected.add(JacksonUtil.toString(request)); + expectedRequest.add(JacksonUtil.toString(request)); request.put("persistent", true); - doPostAsync("/api/rpc/twoway/" + deviceId, JacksonUtil.toString(request), String.class, status().isOk()); + String response = doPostAsync("/api/rpc/twoway/" + deviceId, JacksonUtil.toString(request), String.class, status().isOk()); + var responseNode = JacksonUtil.toJsonNode(response); + rpcIds.add(responseNode.get("rpcId").asText()); } MqttTestClient client = new MqttTestClient(); client.connectAndWait(accessToken); - client.enableManualAcks(); - MqttTestSequenceCallback callback = new MqttTestSequenceCallback(client, 10, result); + if (manualAcksEnabled) { + client.enableManualAcks(); + } + MqttTestTwoWaySequenceCallback callback = new MqttTestTwoWaySequenceCallback( + client, 10, actualRequests, expectedResponses, manualAcksEnabled); client.setCallback(callback); - subscribeAndWait(client, DEVICE_RPC_REQUESTS_SUB_TOPIC, savedDevice.getId(), FeatureType.RPC); + subscribeAndWait(client, DEVICE_RPC_REQUESTS_SUB_TOPIC, savedDevice.getId(), FeatureType.RPC, mqttQoS); callback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS); - assertEquals(expected, result); + assertEquals(expectedRequest, actualRequests); + awaitForDeviceActorToProcessAllRpcResponses(savedDevice.getId()); + for (String rpcId : rpcIds) { + Rpc rpc = doGet("/api/rpc/persistent/" + rpcId, Rpc.class); + actualResponses.add(JacksonUtil.toString(rpc.getResponse())); + } + assertEquals(expectedResponses, actualResponses); + client.disconnect(); } protected void processJsonTwoWayRpcTestGateway(String deviceName) throws Exception { @@ -222,7 +270,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM ); assertNotNull(savedDevice); - MqttTestCallback callback = new MqttTestSubscribeOnTopicCallback(GATEWAY_RPC_TOPIC); + MqttTestCallback callback = new MqttTestSubscribeOnTopicCallback(GATEWAY_RPC_TOPIC); client.setCallback(callback); subscribeAndCheckSubscription(client, GATEWAY_RPC_TOPIC, savedDevice.getId(), FeatureType.RPC); @@ -248,7 +296,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM JsonNode expectedJsonRequestData = getExpectedGatewayJsonRequestData(deviceName, setGpioRequest); assertEquals(expectedJsonRequestData, JacksonUtil.fromBytes(callback.getPayloadBytes())); } - assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getMessageArrivedQoS()); } private JsonNode getExpectedGatewayJsonRequestData(String deviceName, String requestStr) { @@ -280,7 +328,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM callback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS); log.warn("request payload: {}", JacksonUtil.fromBytes(callback.getPayloadBytes())); assertEquals("{\"success\":true}", actualRpcResponse); - assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getMessageArrivedQoS()); } protected void validateProtoTwoWayRpcGatewayResponse(String deviceName, MqttTestClient client, byte[] connectPayloadBytes) throws Exception { @@ -302,7 +350,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM String actualRpcResponse = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk()); callback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS); assertEquals("{\"success\":true}", actualRpcResponse); - assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getMessageArrivedQoS()); } private Device getDeviceByName(String deviceName) throws Exception { @@ -334,7 +382,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM public void messageArrived(String requestTopic, MqttMessage mqttMessage) { log.warn("messageArrived on topic: {}, awaitSubTopic: {}", requestTopic, awaitSubTopic); if (awaitSubTopic.equals(requestTopic)) { - qoS = mqttMessage.getQos(); + messageArrivedQoS = mqttMessage.getQos(); payloadBytes = mqttMessage.getPayload(); String responseTopic; if (requestTopic.startsWith(BASE_DEVICE_API_TOPIC_V2)) { @@ -366,7 +414,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM public void messageArrived(String requestTopic, MqttMessage mqttMessage) { log.warn("messageArrived on topic: {}, awaitSubTopic: {}", requestTopic, awaitSubTopic); if (awaitSubTopic.equals(requestTopic)) { - qoS = mqttMessage.getQos(); + messageArrivedQoS = mqttMessage.getQos(); payloadBytes = mqttMessage.getPayload(); String responseTopic; if (requestTopic.startsWith(BASE_DEVICE_API_TOPIC_V2)) { @@ -398,7 +446,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM try { DynamicMessage dynamicMessage = DynamicMessage.parseFrom(rpcRequestMsgDescriptor, requestPayload); List fields = rpcRequestMsgDescriptor.getFields(); - for (Descriptors.FieldDescriptor fieldDescriptor: fields) { + for (Descriptors.FieldDescriptor fieldDescriptor : fields) { assertTrue(dynamicMessage.hasField(fieldDescriptor)); } ProtoFileElement rpcResponseProtoFileElement = DynamicProtoUtils.getProtoFileElement(protoTransportPayloadConfiguration.getDeviceRpcResponseProtoSchema()); @@ -436,30 +484,69 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM return (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; } - protected class MqttTestSequenceCallback extends MqttTestCallback { + protected static class MqttTestOneWaySequenceCallback extends MqttTestCallback { + + private final List requests; + + MqttTestOneWaySequenceCallback(MqttTestClient client, int subscribeCount, List requests) { + super(subscribeCount); + this.requests = requests; + } + + @Override + public void messageArrived(String requestTopic, MqttMessage mqttMessage) { + log.warn("messageArrived on topic: {}", requestTopic); + requests.add(new String(mqttMessage.getPayload())); + messageArrivedQoS = mqttMessage.getQos(); + subscribeLatch.countDown(); + } + } + + protected class MqttTestTwoWaySequenceCallback extends MqttTestCallback { private final MqttTestClient client; - private final List expected; + private final List requests; + private final List responses; + private final boolean manualAcksEnabled; - MqttTestSequenceCallback(MqttTestClient client, int subscribeCount, List expected) { + MqttTestTwoWaySequenceCallback(MqttTestClient client, int subscribeCount, List requests, List responses, boolean manualAcksEnabled) { super(subscribeCount); this.client = client; - this.expected = expected; + this.requests = requests; + this.responses = responses; + this.manualAcksEnabled = manualAcksEnabled; } @Override public void messageArrived(String requestTopic, MqttMessage mqttMessage) { log.warn("messageArrived on topic: {}", requestTopic); - expected.add(new String(mqttMessage.getPayload())); + requests.add(new String(mqttMessage.getPayload())); + messageArrivedQoS = mqttMessage.getQos(); + if (manualAcksEnabled) { + try { + client.messageArrivedComplete(mqttMessage); + } catch (MqttException e) { + log.warn("Failed to ack message delivery on topic: {} due to: ", requestTopic, e); + } finally { + subscribeLatch.countDown(); + processResponse(requestTopic, mqttMessage); + } + return; + } + subscribeLatch.countDown(); + processResponse(requestTopic, mqttMessage); + } + + private void processResponse(String requestTopic, MqttMessage mqttMessage) { String responseTopic = requestTopic.replace("request", "response"); - qoS = mqttMessage.getQos(); + byte[] responsePayload = processJsonMessageArrived(requestTopic, mqttMessage); + responses.add(new String(responsePayload)); try { - client.messageArrivedComplete(mqttMessage); - client.publish(responseTopic, processJsonMessageArrived(requestTopic, mqttMessage)); + client.publish(responseTopic, responsePayload); } catch (MqttException e) { log.warn("Failed to publish response on topic: {} due to: ", responseTopic, e); } - subscribeLatch.countDown(); } + } } diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/rpc/MqttServerSideRpcDefaultIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/rpc/MqttServerSideRpcDefaultIntegrationTest.java index 10a7a9619f..46f9cc7cff 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/rpc/MqttServerSideRpcDefaultIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/rpc/MqttServerSideRpcDefaultIntegrationTest.java @@ -110,11 +110,6 @@ public class MqttServerSideRpcDefaultIntegrationTest extends AbstractMqttServerS processJsonTwoWayRpcTest(DEVICE_RPC_REQUESTS_SUB_SHORT_JSON_TOPIC); } - @Test - public void testSequenceServerMqttTwoWayRpc() throws Exception { - processSequenceTwoWayRpcTest(); - } - @Test public void testGatewayServerMqttOneWayRpc() throws Exception { processJsonOneWayRpcTestGateway("Gateway Device OneWay RPC"); diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/rpc/MqttServerSideRpcSequenceOnAckIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/rpc/MqttServerSideRpcSequenceOnAckIntegrationTest.java new file mode 100644 index 0000000000..4476c6babb --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/rpc/MqttServerSideRpcSequenceOnAckIntegrationTest.java @@ -0,0 +1,72 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.mqtt.mqttv3.rpc; + +import io.netty.handler.codec.mqtt.MqttQoS; +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.Test; +import org.springframework.test.context.TestPropertySource; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties; + +@Slf4j +@DaoSqlTest +@TestPropertySource(properties = { + "actors.rpc.submit_strategy=SEQUENTIAL_ON_ACK_FROM_DEVICE", +}) +public class MqttServerSideRpcSequenceOnAckIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { + + @Before + public void beforeTest() throws Exception { + MqttTestConfigProperties configProperties = MqttTestConfigProperties.builder() + .deviceName("RPC test device") + .gatewayName("RPC test gateway") + .build(); + processBeforeTest(configProperties); + } + + @Test + public void testSequenceServerMqttOneWayRpcQoSAtMostOnce() throws Exception { + processSequenceOneWayRpcTest(MqttQoS.AT_MOST_ONCE); + } + + @Test + public void testSequenceServerMqttOneWayRpcQoSAtLeastOnce() throws Exception { + processSequenceOneWayRpcTest(MqttQoS.AT_LEAST_ONCE); + } + + @Test + public void testSequenceServerMqttTwoWayRpcQoSAtMostOnce() throws Exception { + processSequenceTwoWayRpcTest(MqttQoS.AT_MOST_ONCE); + } + + @Test + public void testSequenceServerMqttTwoWayRpcQoSAtLeastOnce() throws Exception { + processSequenceTwoWayRpcTest(MqttQoS.AT_LEAST_ONCE); + } + + @Test + public void testSequenceServerMqttTwoWayRpcQoSAtMostOnceWithManualAcksEnabled() throws Exception { + processSequenceTwoWayRpcTest(MqttQoS.AT_MOST_ONCE, true); + } + + @Test + public void testSequenceServerMqttTwoWayRpcQoSAtLeastOnceWithoutManualAcksEnabled() throws Exception { + processSequenceTwoWayRpcTest(MqttQoS.AT_LEAST_ONCE, true); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/rpc/MqttServerSideRpcSequenceOnResponseIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/rpc/MqttServerSideRpcSequenceOnResponseIntegrationTest.java new file mode 100644 index 0000000000..7a081ed023 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/rpc/MqttServerSideRpcSequenceOnResponseIntegrationTest.java @@ -0,0 +1,73 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.mqtt.mqttv3.rpc; + +import io.netty.handler.codec.mqtt.MqttQoS; +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.Test; +import org.springframework.test.context.TestPropertySource; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties; + +@Slf4j +@DaoSqlTest +@TestPropertySource(properties = { + "actors.rpc.submit_strategy=SEQUENTIAL_ON_RESPONSE_FROM_DEVICE", +}) +public class MqttServerSideRpcSequenceOnResponseIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { + + @Before + public void beforeTest() throws Exception { + MqttTestConfigProperties configProperties = MqttTestConfigProperties.builder() + .deviceName("RPC test device") + .gatewayName("RPC test gateway") + .build(); + processBeforeTest(configProperties); + } + + @Test + public void testSequenceServerMqttOneWayRpcQoSAtMostOnce() throws Exception { + processSequenceOneWayRpcTest(MqttQoS.AT_MOST_ONCE); + } + + @Test + public void testSequenceServerMqttOneWayRpcQoSAtLeastOnce() throws Exception { + processSequenceOneWayRpcTest(MqttQoS.AT_LEAST_ONCE); + } + + @Test + public void testSequenceServerMqttTwoWayRpcQoSAtMostOnce() throws Exception { + processSequenceTwoWayRpcTest(MqttQoS.AT_MOST_ONCE); + } + + @Test + public void testSequenceServerMqttTwoWayRpcQoSAtLeastOnce() throws Exception { + processSequenceTwoWayRpcTest(MqttQoS.AT_LEAST_ONCE); + } + + @Test + public void testSequenceServerMqttTwoWayRpcQoSAtMostOnceWithManualAcksEnabled() throws Exception { + processSequenceTwoWayRpcTest(MqttQoS.AT_MOST_ONCE, true); + } + + @Test + public void testSequenceServerMqttTwoWayRpcQoSAtLeastOnceWithoutManualAcksEnabled() throws Exception { + processSequenceTwoWayRpcTest(MqttQoS.AT_LEAST_ONCE, true); + } + + +} diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/telemetry/timeseries/AbstractMqttTimeseriesIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/telemetry/timeseries/AbstractMqttTimeseriesIntegrationTest.java index b29c6844a7..40b2b90856 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/telemetry/timeseries/AbstractMqttTimeseriesIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/telemetry/timeseries/AbstractMqttTimeseriesIntegrationTest.java @@ -332,7 +332,7 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt doPostAsync("/api/plugins/telemetry/" + savedDevice.getId() + "/SHARED_SCOPE", payload, String.class, status().isOk()); callback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS); assertEquals(payload.getBytes(), callback.getPayloadBytes()); - assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getMessageArrivedQoS()); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/sparkplug/attributes/MqttV5ClientSparkplugBAttributesInProfileTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/sparkplug/attributes/MqttV5ClientSparkplugBAttributesInProfileTest.java index 1d5a2c127f..faa29ad35d 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/sparkplug/attributes/MqttV5ClientSparkplugBAttributesInProfileTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/sparkplug/attributes/MqttV5ClientSparkplugBAttributesInProfileTest.java @@ -52,4 +52,4 @@ public class MqttV5ClientSparkplugBAttributesInProfileTest extends AbstractMqttV processClientDeviceWithCorrectAccessTokenPublish_AttributesInProfileContainsKeyAttributes(); } -} \ No newline at end of file +} diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/sparkplug/attributes/MqttV5ClientSparkplugBAttributesTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/sparkplug/attributes/MqttV5ClientSparkplugBAttributesTest.java index f5d42046b7..abe1810887 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/sparkplug/attributes/MqttV5ClientSparkplugBAttributesTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/sparkplug/attributes/MqttV5ClientSparkplugBAttributesTest.java @@ -78,4 +78,4 @@ public class MqttV5ClientSparkplugBAttributesTest extends AbstractMqttV5ClientSp processClientDeviceWithCorrectAccessTokenPublishWithBirth_SharedAttributes_LongType_IfMetricFailedTypeCheck_SendValueOk(); } -} \ No newline at end of file +} diff --git a/application/src/test/java/org/thingsboard/server/utils/TbNodeUpgradeUtilsTest.java b/application/src/test/java/org/thingsboard/server/utils/TbNodeUpgradeUtilsTest.java new file mode 100644 index 0000000000..5acf00f0c0 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/utils/TbNodeUpgradeUtilsTest.java @@ -0,0 +1,158 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.utils; + +import com.fasterxml.jackson.databind.node.NullNode; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.metadata.TbGetAttributesNode; +import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; +import org.thingsboard.rule.engine.metadata.TbGetCustomerAttributeNode; +import org.thingsboard.rule.engine.metadata.TbGetEntityDataNodeConfiguration; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.service.component.RuleNodeClassInfo; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class TbNodeUpgradeUtilsTest { + + @Test + public void testUpgradeRuleNodeConfigurationWithNullConfig() throws Exception { + // GIVEN + var node = new RuleNode(); + var nodeInfo = mock(RuleNodeClassInfo.class); + var nodeConfigClazz = TbGetAttributesNodeConfiguration.class; + var annotation = mock(org.thingsboard.rule.engine.api.RuleNode.class); + var defaultConfig = JacksonUtil.valueToTree(nodeConfigClazz.getDeclaredConstructor().newInstance().defaultConfiguration()); + + when(nodeInfo.getClazz()).thenReturn((Class) TbGetAttributesNode.class); + when(nodeInfo.getCurrentVersion()).thenReturn(1); + when(nodeInfo.getAnnotation()).thenReturn(annotation); + when(annotation.configClazz()).thenReturn((Class) nodeConfigClazz); + + // WHEN + TbNodeUpgradeUtils.upgradeConfigurationAndVersion(node, nodeInfo); + // THEN + Assertions.assertThat(node.getConfiguration()).isEqualTo(defaultConfig); + Assertions.assertThat(node.getConfigurationVersion()).isEqualTo(1); + } + + @Test + public void testUpgradeRuleNodeConfigurationWithNullNodeConfig() throws Exception { + // GIVEN + var node = new RuleNode(); + node.setConfiguration(NullNode.instance); + var nodeInfo = mock(RuleNodeClassInfo.class); + var nodeConfigClazz = TbGetAttributesNodeConfiguration.class; + var annotation = mock(org.thingsboard.rule.engine.api.RuleNode.class); + var defaultConfig = JacksonUtil.valueToTree(nodeConfigClazz.getDeclaredConstructor().newInstance().defaultConfiguration()); + + when(nodeInfo.getClazz()).thenReturn((Class) TbGetAttributesNode.class); + when(nodeInfo.getCurrentVersion()).thenReturn(1); + when(nodeInfo.getAnnotation()).thenReturn(annotation); + when(annotation.configClazz()).thenReturn((Class) nodeConfigClazz); + + // WHEN + TbNodeUpgradeUtils.upgradeConfigurationAndVersion(node, nodeInfo); + // THEN + Assertions.assertThat(node.getConfiguration()).isEqualTo(defaultConfig); + Assertions.assertThat(node.getConfigurationVersion()).isEqualTo(1); + } + + @Test + public void testUpgradeRuleNodeConfigurationWithNonNullConfig() throws Exception { + // GIVEN + var node = new RuleNode(); + var nodeInfo = mock(RuleNodeClassInfo.class); + var nodeConfigClazz = TbGetAttributesNodeConfiguration.class; + var annotation = mock(org.thingsboard.rule.engine.api.RuleNode.class); + var defaultConfig = JacksonUtil.valueToTree(nodeConfigClazz.getDeclaredConstructor().newInstance().defaultConfiguration()); + + when(nodeInfo.getClazz()).thenReturn((Class) TbGetAttributesNode.class); + when(nodeInfo.getCurrentVersion()).thenReturn(1); + when(nodeInfo.getAnnotation()).thenReturn(annotation); + when(annotation.configClazz()).thenReturn((Class) nodeConfigClazz); + + String versionZeroDefaultConfigStr = "{\"fetchToData\":false," + + "\"clientAttributeNames\":[]," + + "\"sharedAttributeNames\":[]," + + "\"serverAttributeNames\":[]," + + "\"latestTsKeyNames\":[]," + + "\"tellFailureIfAbsent\":true," + + "\"getLatestValueWithTs\":false}"; + node.setConfiguration(JacksonUtil.toJsonNode(versionZeroDefaultConfigStr)); + // WHEN + TbNodeUpgradeUtils.upgradeConfigurationAndVersion(node, nodeInfo); + // THEN + Assertions.assertThat(node.getConfiguration()).isEqualTo(defaultConfig); + Assertions.assertThat(node.getConfigurationVersion()).isEqualTo(1); + + } + + @Test + public void testUpgradeRuleNodeConfigurationWithNewConfigAndOldConfigVersion() throws Exception { + // GIVEN + var node = new RuleNode(); + var nodeInfo = mock(RuleNodeClassInfo.class); + var nodeConfigClazz = TbGetEntityDataNodeConfiguration.class; + var annotation = mock(org.thingsboard.rule.engine.api.RuleNode.class); + var defaultConfig = JacksonUtil.valueToTree(nodeConfigClazz.getDeclaredConstructor().newInstance().defaultConfiguration()); + + when(nodeInfo.getClazz()).thenReturn((Class) TbGetCustomerAttributeNode.class); + when(nodeInfo.getCurrentVersion()).thenReturn(1); + when(nodeInfo.getAnnotation()).thenReturn(annotation); + when(annotation.configClazz()).thenReturn((Class) nodeConfigClazz); + + String versionOneDefaultConfig = "{\"fetchTo\":\"METADATA\"," + + "\"dataMapping\":{\"alarmThreshold\":\"threshold\"}," + + "\"dataToFetch\":\"ATTRIBUTES\"}"; + node.setConfiguration(JacksonUtil.toJsonNode(versionOneDefaultConfig)); + // WHEN + TbNodeUpgradeUtils.upgradeConfigurationAndVersion(node, nodeInfo); + // THEN + Assertions.assertThat(node.getConfiguration()).isEqualTo(defaultConfig); + Assertions.assertThat(node.getConfigurationVersion()).isEqualTo(1); + + } + + @Test + public void testUpgradeRuleNodeConfigurationWithInvalidConfigAndOldConfigVersion() throws Exception { + // GIVEN + var node = new RuleNode(); + var nodeInfo = mock(RuleNodeClassInfo.class); + var nodeConfigClazz = TbGetEntityDataNodeConfiguration.class; + var annotation = mock(org.thingsboard.rule.engine.api.RuleNode.class); + var defaultConfig = JacksonUtil.valueToTree(nodeConfigClazz.getDeclaredConstructor().newInstance().defaultConfiguration()); + + when(nodeInfo.getClazz()).thenReturn((Class) TbGetCustomerAttributeNode.class); + when(nodeInfo.getCurrentVersion()).thenReturn(1); + when(nodeInfo.getAnnotation()).thenReturn(annotation); + when(annotation.configClazz()).thenReturn((Class) nodeConfigClazz); + + // missing telemetry field + String oldConfig = "{\"attrMapping\":{\"alarmThreshold\":\"threshold\"}}";; + node.setConfiguration(JacksonUtil.toJsonNode(oldConfig)); + // WHEN + TbNodeUpgradeUtils.upgradeConfigurationAndVersion(node, nodeInfo); + // THEN + Assertions.assertThat(node.getConfiguration()).isEqualTo(defaultConfig); + Assertions.assertThat(node.getConfigurationVersion()).isEqualTo(1); + + } + +} diff --git a/application/src/test/resources/application-test.properties b/application/src/test/resources/application-test.properties index ad86ff736b..b01ffe0d72 100644 --- a/application/src/test/resources/application-test.properties +++ b/application/src/test/resources/application-test.properties @@ -11,9 +11,7 @@ transport.lwm2m.security.trust-credentials.keystore.store_file=lwm2m/credentials # Edge disabled to speed up the context init. Will be enabled by @TestPropertySource in respective tests edges.enabled=false -edges.storage.no_read_records_sleep=500 -edges.storage.sleep_between_batches=500 -actors.rpc.sequential=true +actors.rpc.submit_strategy=BURST # Transports disabled to speed up the context init. Particular transport will be enabled with @TestPropertySource in respective tests transport.http.enabled=false @@ -60,10 +58,14 @@ queue.rule-engine.queues[2].processing-strategy.retries=1 queue.rule-engine.queues[2].processing-strategy.pause-between-retries=0 queue.rule-engine.queues[2].processing-strategy.max-pause-between-retries=0 +queue.rule-engine.stats.enabled=true usage.stats.report.enabled=false +queue.core.stats.enabled=true sql.audit_logs.partition_size=24 sql.ttl.audit_logs.ttl=2592000 sql.edge_events.partition_size=168 sql.ttl.edge_events.edge_event_ttl=2592000 + +server.log_controller_error_stack_trace=false diff --git a/application/src/test/resources/logback-test.xml b/application/src/test/resources/logback-test.xml index 14db2eea7b..3d0ce3f933 100644 --- a/application/src/test/resources/logback-test.xml +++ b/application/src/test/resources/logback-test.xml @@ -28,12 +28,18 @@ + + + + + + diff --git a/build_proto.sh b/build_proto.sh new file mode 100755 index 0000000000..55fdea0880 --- /dev/null +++ b/build_proto.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# +# Copyright © 2016-2023 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +echo "Building ThingsBoard protobuf-containing packages..." +MAVEN_OPTS="-Xmx1024m" NODE_OPTIONS="--max_old_space_size=3072" \ +mvn clean compile -T4 --also-make --projects=' +common/cluster-api, +common/edge-api, +common/transport/coap, +common/transport/mqtt, +common/transport/transport-api' diff --git a/common/actor/pom.xml b/common/actor/pom.xml index b113788a11..149d88ba2c 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorCtx.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorCtx.java index 2a8f641c4e..bbe19fb3dd 100644 --- a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorCtx.java +++ b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorCtx.java @@ -32,7 +32,7 @@ public interface TbActorCtx extends TbActorRef { void stop(TbActorId target); - TbActorRef getOrCreateChildActor(TbActorId actorId, Supplier dispatcher, Supplier creator); + TbActorRef getOrCreateChildActor(TbActorId actorId, Supplier dispatcher, Supplier creator, Supplier createCondition); void broadcastToChildren(TbActorMsg msg); diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java index 587da8f3b1..34537c143c 100644 --- a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java +++ b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java @@ -15,10 +15,12 @@ */ package org.thingsboard.server.actors; -import lombok.Data; +import lombok.Getter; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.TbActorError; import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbActorStopReason; @@ -30,7 +32,8 @@ import java.util.function.Predicate; import java.util.function.Supplier; @Slf4j -@Data +@Getter +@RequiredArgsConstructor public final class TbActorMailbox implements TbActorCtx { private static final boolean HIGH_PRIORITY = true; private static final boolean NORMAL_PRIORITY = false; @@ -69,9 +72,14 @@ public final class TbActorMailbox implements TbActorCtx { } } } catch (Throwable t) { - log.debug("[{}] Failed to init actor, attempt: {}", selfId, attempt, t); + InitFailureStrategy strategy; int attemptIdx = attempt + 1; - InitFailureStrategy strategy = actor.onInitFailure(attempt, t); + if (isUnrecoverable(t)) { + strategy = InitFailureStrategy.stop(); + } else { + log.debug("[{}] Failed to init actor, attempt: {}", selfId, attempt, t); + strategy = actor.onInitFailure(attempt, t); + } if (strategy.isStop() || (settings.getMaxActorInitAttempts() > 0 && attemptIdx > settings.getMaxActorInitAttempts())) { log.info("[{}] Failed to init actor, attempt {}, going to stop attempts.", selfId, attempt, t); stopReason = TbActorStopReason.INIT_FAILED; @@ -88,6 +96,13 @@ public final class TbActorMailbox implements TbActorCtx { } } + private static boolean isUnrecoverable(Throwable t) { + if (t instanceof TbActorException && t.getCause() != null) { + t = t.getCause(); + } + return t instanceof TbActorError && ((TbActorError) t).isUnrecoverable(); + } + private void enqueue(TbActorMsg msg, boolean highPriority) { if (!destroyInProgress.get()) { if (highPriority) { @@ -199,9 +214,9 @@ public final class TbActorMailbox implements TbActorCtx { } @Override - public TbActorRef getOrCreateChildActor(TbActorId actorId, Supplier dispatcher, Supplier creator) { + public TbActorRef getOrCreateChildActor(TbActorId actorId, Supplier dispatcher, Supplier creator, Supplier createCondition) { TbActorRef actorRef = system.getActor(actorId); - if (actorRef == null) { + if (actorRef == null && createCondition.get()) { return system.createChildActor(dispatcher.get(), creator.get(), selfId); } else { return actorRef; diff --git a/common/cache/pom.xml b/common/cache/pom.xml index 0a9141846f..22987bad50 100644 --- a/common/cache/pom.xml +++ b/common/cache/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/RedisTbTransactionalCache.java b/common/cache/src/main/java/org/thingsboard/server/cache/RedisTbTransactionalCache.java index c5af954392..01a18ad7ad 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/RedisTbTransactionalCache.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/RedisTbTransactionalCache.java @@ -17,6 +17,7 @@ package org.thingsboard.server.cache; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.support.NullValue; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; @@ -27,6 +28,7 @@ import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.types.Expiration; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; +import org.thingsboard.server.common.data.FstStatsService; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.util.JedisClusterCRC16; @@ -44,6 +46,9 @@ public abstract class RedisTbTransactionalCache newTransactionForKeys(List keys); + default V getOrFetchFromDB(K key, Supplier dbCall, boolean cacheNullValue, boolean putToCache) { + if (putToCache) { + return getAndPutInTransaction(key, dbCall, cacheNullValue); + } else { + TbCacheValueWrapper cacheValueWrapper = get(key); + if (cacheValueWrapper != null) { + return cacheValueWrapper.get(); + } + return dbCall.get(); + } + } + default V getAndPutInTransaction(K key, Supplier dbCall, boolean cacheNullValue) { TbCacheValueWrapper cacheValueWrapper = get(key); if (cacheValueWrapper != null) { @@ -69,6 +81,19 @@ public interface TbTransactionalCache R getOrFetchFromDB(K key, Supplier dbCall, Function cacheValueToResult, Function dbValueToCacheValue, boolean cacheNullValue, boolean putToCache) { + if (putToCache) { + return getAndPutInTransaction(key, dbCall, cacheValueToResult, dbValueToCacheValue, cacheNullValue); + } else { + TbCacheValueWrapper cacheValueWrapper = get(key); + if (cacheValueWrapper != null) { + var cacheValue = cacheValueWrapper.get(); + return cacheValue == null ? null : cacheValueToResult.apply(cacheValue); + } + return dbCall.get(); + } + } + default R getAndPutInTransaction(K key, Supplier dbCall, Function cacheValueToResult, Function dbValueToCacheValue, boolean cacheNullValue) { TbCacheValueWrapper cacheValueWrapper = get(key); if (cacheValueWrapper != null) { diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceCacheKey.java b/common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceCacheKey.java index 9e6ba806f1..475b268f20 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceCacheKey.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceCacheKey.java @@ -34,6 +34,10 @@ public class DeviceCacheKey implements Serializable { private final DeviceId deviceId; private final String deviceName; + public DeviceCacheKey(DeviceId deviceId) { + this(null, deviceId, null); + } + public DeviceCacheKey(TenantId tenantId, DeviceId deviceId) { this(tenantId, deviceId, null); } @@ -44,11 +48,12 @@ public class DeviceCacheKey implements Serializable { @Override public String toString() { - if (deviceId != null) { - return tenantId + "_" + deviceId; - } else { + if (deviceId == null) { return tenantId + "_n_" + deviceName; + } else if (tenantId == null) { + return deviceId.toString(); + } else { + return tenantId + "_" + deviceId; } } - } diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/resourceInfo/ResourceInfoCacheKey.java b/common/cache/src/main/java/org/thingsboard/server/cache/resourceInfo/ResourceInfoCacheKey.java new file mode 100644 index 0000000000..9db53f86c6 --- /dev/null +++ b/common/cache/src/main/java/org/thingsboard/server/cache/resourceInfo/ResourceInfoCacheKey.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.cache.resourceInfo; + +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.common.data.id.TenantId; + +import java.io.Serializable; + +@Getter +@EqualsAndHashCode +@RequiredArgsConstructor +@Builder +public class ResourceInfoCacheKey implements Serializable { + + private final TenantId tenantId; + private final TbResourceId tbResourceId; + + @Override + public String toString() { + return tenantId + "_" + tbResourceId; + } +} diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/resourceInfo/ResourceInfoCaffeineCache.java b/common/cache/src/main/java/org/thingsboard/server/cache/resourceInfo/ResourceInfoCaffeineCache.java new file mode 100644 index 0000000000..95754d891a --- /dev/null +++ b/common/cache/src/main/java/org/thingsboard/server/cache/resourceInfo/ResourceInfoCaffeineCache.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.cache.resourceInfo; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cache.CacheManager; +import org.springframework.stereotype.Service; +import org.thingsboard.server.cache.CaffeineTbTransactionalCache; +import org.thingsboard.server.common.data.CacheConstants; +import org.thingsboard.server.common.data.TbResourceInfo; + + +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) +@Service("ResourceInfoCache") +public class ResourceInfoCaffeineCache extends CaffeineTbTransactionalCache { + + public ResourceInfoCaffeineCache(CacheManager cacheManager) { + super(cacheManager, CacheConstants.RESOURCE_INFO_CACHE); + } + +} diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/resourceInfo/ResourceInfoEvictEvent.java b/common/cache/src/main/java/org/thingsboard/server/cache/resourceInfo/ResourceInfoEvictEvent.java new file mode 100644 index 0000000000..11272a5e24 --- /dev/null +++ b/common/cache/src/main/java/org/thingsboard/server/cache/resourceInfo/ResourceInfoEvictEvent.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.cache.resourceInfo; + +import lombok.Data; +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.common.data.id.TenantId; + +@Data +public class ResourceInfoEvictEvent { + private final TenantId tenantId; + private final TbResourceId resourceId; +} diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/resourceInfo/ResourceInfoRedisCache.java b/common/cache/src/main/java/org/thingsboard/server/cache/resourceInfo/ResourceInfoRedisCache.java new file mode 100644 index 0000000000..fee14e1ca1 --- /dev/null +++ b/common/cache/src/main/java/org/thingsboard/server/cache/resourceInfo/ResourceInfoRedisCache.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.cache.resourceInfo; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.stereotype.Service; +import org.thingsboard.server.cache.CacheSpecsMap; +import org.thingsboard.server.cache.RedisTbTransactionalCache; +import org.thingsboard.server.cache.TBRedisCacheConfiguration; +import org.thingsboard.server.cache.TbFSTRedisSerializer; +import org.thingsboard.server.common.data.CacheConstants; +import org.thingsboard.server.common.data.TbResourceInfo; + +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") +@Service("ResourceInfoCache") +public class ResourceInfoRedisCache extends RedisTbTransactionalCache { + + public ResourceInfoRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) { + super(CacheConstants.RESOURCE_INFO_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbFSTRedisSerializer<>()); + } +} diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml index d6954a40f9..cd8f551aa7 100644 --- a/common/cluster-api/pom.xml +++ b/common/cluster-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common @@ -40,6 +40,10 @@ org.thingsboard.common data + + org.thingsboard.common + proto + org.thingsboard.common message diff --git a/common/cluster-api/src/main/java/org/thingsboard/server/cluster/TbClusterService.java b/common/cluster-api/src/main/java/org/thingsboard/server/cluster/TbClusterService.java index 8962a35187..5c3b9076ff 100644 --- a/common/cluster-api/src/main/java/org/thingsboard/server/cluster/TbClusterService.java +++ b/common/cluster-api/src/main/java/org/thingsboard/server/cluster/TbClusterService.java @@ -33,6 +33,7 @@ import org.thingsboard.server.common.msg.edge.FromEdgeSyncResponse; import org.thingsboard.server.common.msg.edge.ToEdgeSyncRequest; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; @@ -50,6 +51,8 @@ public interface TbClusterService extends TbQueueClusterService { void pushMsgToCore(ToDeviceActorNotificationMsg msg, TbQueueCallback callback); + void broadcastToCore(TransportProtos.ToCoreNotificationMsg msg); + void pushMsgToVersionControl(TenantId tenantId, ToVersionControlServiceMsg msg, TbQueueCallback callback); void pushNotificationToCore(String targetServiceId, FromDeviceRpcResponse response, TbQueueCallback callback); @@ -80,9 +83,9 @@ public interface TbClusterService extends TbQueueClusterService { void onDeviceUpdated(Device device, Device old); - void onDeviceUpdated(Device device, Device old, boolean notifyEdge); + void onDeviceDeleted(TenantId tenantId, Device device, TbQueueCallback callback); - void onDeviceDeleted(Device device, TbQueueCallback callback); + void onDeviceAssignedToTenant(TenantId oldTenantId, Device device); void onResourceChange(TbResource resource, TbQueueCallback callback); @@ -94,6 +97,6 @@ public interface TbClusterService extends TbQueueClusterService { void pushEdgeSyncResponseToCore(FromEdgeSyncResponse fromEdgeSyncResponse); - void sendNotificationMsgToEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, EdgeEventType type, EdgeEventActionType action); + void sendNotificationMsgToEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, EdgeEventType type, EdgeEventActionType action, EdgeId sourceEdgeId); } diff --git a/common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueAdmin.java b/common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueAdmin.java index 4b2bde733e..19aa0284ea 100644 --- a/common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueAdmin.java +++ b/common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueAdmin.java @@ -17,7 +17,11 @@ package org.thingsboard.server.queue; public interface TbQueueAdmin { - void createTopicIfNotExists(String topic); + default void createTopicIfNotExists(String topic) { + createTopicIfNotExists(topic, null); + } + + void createTopicIfNotExists(String topic, String properties); void destroy(); diff --git a/common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueConsumer.java b/common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueConsumer.java index 04439fc85d..21216e164e 100644 --- a/common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueConsumer.java +++ b/common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueConsumer.java @@ -28,6 +28,8 @@ public interface TbQueueConsumer { void subscribe(Set partitions); + void stop(); + void unsubscribe(); List poll(long durationInMillis); @@ -36,4 +38,6 @@ public interface TbQueueConsumer { boolean isStopped(); + List getFullTopicNames(); + } diff --git a/common/cluster-api/src/main/proto/queue.proto b/common/cluster-api/src/main/proto/queue.proto deleted file mode 100644 index 80f44e59be..0000000000 --- a/common/cluster-api/src/main/proto/queue.proto +++ /dev/null @@ -1,1067 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -syntax = "proto3"; - -package transport; - -option java_package = "org.thingsboard.server.gen.transport"; -option java_outer_classname = "TransportProtos"; - -/** - * Service Discovery Data Structures; - */ -message ServiceInfo { - string serviceId = 1; - repeated string serviceTypes = 2; - repeated string transports = 6; - SystemInfoProto systemInfo = 10; -} - -message SystemInfoProto { - int64 cpuUsage = 1; - int64 cpuCount = 2; - int64 memoryUsage = 3; - int64 totalMemory = 4; - int64 diskUsage = 5; - int64 totalDiscSpace = 6; -} - -/** - * Transport Service Data Structures; - */ -message SessionInfoProto { - string nodeId = 1; - int64 sessionIdMSB = 2; - int64 sessionIdLSB = 3; - int64 tenantIdMSB = 4; - int64 tenantIdLSB = 5; - int64 deviceIdMSB = 6; - int64 deviceIdLSB = 7; - string deviceName = 8; - string deviceType = 9; - int64 gwSessionIdMSB = 10; - int64 gwSessionIdLSB = 11; - int64 deviceProfileIdMSB = 12; - int64 deviceProfileIdLSB = 13; - int64 customerIdMSB = 14; - int64 customerIdLSB = 15; -} - -enum SessionEvent { - OPEN = 0; - CLOSED = 1; -} - -enum SessionType { - SYNC = 0; - ASYNC = 1; -} - -enum KeyValueType { - BOOLEAN_V = 0; - LONG_V = 1; - DOUBLE_V = 2; - STRING_V = 3; - JSON_V = 4; -} - -enum CredentialsType { - ACCESS_TOKEN = 0; - X509_CERTIFICATE = 1; - MQTT_BASIC = 2; - LWM2M_CREDENTIALS = 3; -} - -message KeyValueProto { - string key = 1; - KeyValueType type = 2; - bool bool_v = 3; - int64 long_v = 4; - double double_v = 5; - string string_v = 6; - string json_v = 7; -} - -message AttributeValueProto { - int64 lastUpdateTs = 1; - KeyValueType type = 2; - bool has_v = 3; - bool bool_v = 4; - int64 long_v = 5; - double double_v = 6; - string string_v = 7; - string json_v = 8; -} - -message TsKvProto { - int64 ts = 1; - KeyValueProto kv = 2; -} - -message TsKvListProto { - int64 ts = 1; - repeated KeyValueProto kv = 2; -} - -message DeviceInfoProto { - int64 tenantIdMSB = 1; - int64 tenantIdLSB = 2; - int64 deviceIdMSB = 3; - int64 deviceIdLSB = 4; - string deviceName = 5; - string deviceType = 6; - string additionalInfo = 7; - int64 deviceProfileIdMSB = 8; - int64 deviceProfileIdLSB = 9; - int64 customerIdMSB = 10; - int64 customerIdLSB = 11; - string powerMode = 12; - int64 edrxCycle = 13; - int64 psmActivityTimer = 14; - int64 pagingTransmissionWindow = 15; -} - -/** - * Transport Service Messages; - */ -message SessionEventMsg { - SessionType sessionType = 1; - SessionEvent event = 2; -} - -message PostTelemetryMsg { - repeated TsKvListProto tsKvList = 1; -} - -message PostAttributeMsg { - repeated KeyValueProto kv = 1; - bool shared = 2; -} - -message GetAttributeRequestMsg { - int32 requestId = 1; - repeated string clientAttributeNames = 2; - repeated string sharedAttributeNames = 3; - bool onlyShared = 4; -} - -message GetAttributeResponseMsg { - int32 requestId = 1; - repeated TsKvProto clientAttributeList = 2; - repeated TsKvProto sharedAttributeList = 3; - bool isMultipleAttributesRequest = 4; - string error = 5; - bool sharedStateMsg = 6; -} - -message AttributeUpdateNotificationMsg { - repeated TsKvProto sharedUpdated = 1; - repeated string sharedDeleted = 2; -} - -message ValidateDeviceTokenRequestMsg { - string token = 1; -} - -message ValidateDeviceX509CertRequestMsg { - string hash = 1; -} - -message ValidateOrCreateDeviceX509CertRequestMsg { - string certificateChain = 1; -} - -message ValidateBasicMqttCredRequestMsg { - string clientId = 1; - string userName = 2; - string password = 3; -} - -message ValidateDeviceCredentialsResponseMsg { - DeviceInfoProto deviceInfo = 1; - string credentialsBody = 2; - bytes profileBody = 3; -} - -message GetOrCreateDeviceFromGatewayRequestMsg { - int64 gatewayIdMSB = 1; - int64 gatewayIdLSB = 2; - string deviceName = 3; - string deviceType = 4; -} - -message GetOrCreateDeviceFromGatewayResponseMsg { - DeviceInfoProto deviceInfo = 1; - bytes profileBody = 2; -} - -message GetEntityProfileRequestMsg { - string entityType = 1; - int64 entityIdMSB = 2; - int64 entityIdLSB = 3; -} - -message GetAllQueueRoutingInfoRequestMsg { -} - -message GetQueueRoutingInfoResponseMsg { - int64 tenantIdMSB = 1; - int64 tenantIdLSB = 2; - int64 queueIdMSB = 3; - int64 queueIdLSB = 4; - string queueName = 5; - string queueTopic = 6; - int32 partitions = 7; -} - -message QueueUpdateMsg { - int64 tenantIdMSB = 1; - int64 tenantIdLSB = 2; - int64 queueIdMSB = 3; - int64 queueIdLSB = 4; - string queueName = 5; - string queueTopic = 6; - int32 partitions = 7; -} - -message QueueDeleteMsg { - int64 tenantIdMSB = 1; - int64 tenantIdLSB = 2; - int64 queueIdMSB = 3; - int64 queueIdLSB = 4; - string queueName = 5; -} - -message LwM2MRegistrationRequestMsg { - string tenantId = 1; - string endpoint = 2; -} - -message LwM2MRegistrationResponseMsg { - DeviceInfoProto deviceInfo = 1; -} - -message LwM2MRequestMsg { - LwM2MRegistrationRequestMsg registrationMsg = 1; -} - -message LwM2MResponseMsg { - LwM2MRegistrationResponseMsg registrationMsg = 1; -} - -message GetResourceRequestMsg { - int64 tenantIdMSB = 1; - int64 tenantIdLSB = 2; - string resourceType = 3; - string resourceKey = 4; -} - -message GetResourceResponseMsg { - bytes resource = 1; -} - -message ValidateDeviceLwM2MCredentialsRequestMsg { - string credentialsId = 1; -} - -message ToTransportUpdateCredentialsProto { - repeated string credentialsId = 1; - repeated string credentialsValue = 2; -} - -message GetTenantRoutingInfoRequestMsg { - int64 tenantIdMSB = 1; - int64 tenantIdLSB = 2; -} - -message GetTenantRoutingInfoResponseMsg { - bool isolatedTbRuleEngine = 2; -} - -message GetDeviceProfileRequestMsg { - int64 profileIdMSB = 1; - int64 profileIdLSB = 2; -} - -message GetEntityProfileResponseMsg { - string entityType = 1; - bytes data = 2; - bytes apiState = 3; -} - -message GetDeviceRequestMsg { - int64 deviceIdMSB = 1; - int64 deviceIdLSB = 2; -} - -message GetDeviceResponseMsg { - int64 deviceProfileIdMSB = 1; - int64 deviceProfileIdLSB = 2; - bytes deviceTransportConfiguration = 3; -} - -message GetDeviceCredentialsRequestMsg { - int64 deviceIdMSB = 1; - int64 deviceIdLSB = 2; -} - -message GetDeviceCredentialsResponseMsg { - bytes deviceCredentialsData = 1; -} - -message GetSnmpDevicesRequestMsg { - int32 page = 1; - int32 pageSize = 2; -} - -message GetSnmpDevicesResponseMsg { - repeated string ids = 1; - bool hasNextPage = 2; -} - -message EntityUpdateMsg { - string entityType = 1; - bytes data = 2; -} - -message EntityDeleteMsg { - string entityType = 1; - int64 entityIdMSB = 2; - int64 entityIdLSB = 3; -} - -message ResourceUpdateMsg { - int64 tenantIdMSB = 1; - int64 tenantIdLSB = 2; - string resourceType = 3; - string resourceKey = 4; -} - -message ResourceDeleteMsg { - int64 tenantIdMSB = 1; - int64 tenantIdLSB = 2; - string resourceType = 3; - string resourceKey = 4; -} - -message SessionCloseNotificationProto { - string message = 1; -} - -message SubscribeToAttributeUpdatesMsg { - bool unsubscribe = 1; - SessionType sessionType = 2; -} - -message SubscribeToRPCMsg { - bool unsubscribe = 1; - SessionType sessionType = 2; -} - -message SendPendingRPCMsg { -} - -message ToDeviceRpcRequestMsg { - int32 requestId = 1; - string methodName = 2; - string params = 3; - int64 expirationTime = 4; - int64 requestIdMSB = 5; - int64 requestIdLSB = 6; - bool oneway = 7; - bool persisted = 8; -} - -message ToDeviceRpcResponseMsg { - int32 requestId = 1; - string payload = 2; - string error = 3; -} - -message UplinkNotificationMsg { - int64 uplinkTs = 1; -} - -message ToDeviceRpcResponseStatusMsg { - int32 requestId = 1; - int64 requestIdMSB = 2; - int64 requestIdLSB = 3; - string status = 4; -} - -message ToServerRpcRequestMsg { - int32 requestId = 1; - string methodName = 2; - string params = 3; -} - -message ToServerRpcResponseMsg { - int32 requestId = 1; - string payload = 2; - string error = 3; -} - -message ClaimDeviceMsg { - int64 deviceIdMSB = 1; - int64 deviceIdLSB = 2; - string secretKey = 3; - int64 durationMs = 4; -} - -message DeviceCredentialsProto { - int64 deviceIdMSB = 1; - int64 deviceIdLSB = 2; - CredentialsType credentialsType = 3; - string credentialsId = 4; - string credentialsValue = 5; -} - -message CredentialsDataProto { - ValidateDeviceTokenRequestMsg validateDeviceTokenRequestMsg = 1; - ValidateDeviceX509CertRequestMsg validateDeviceX509CertRequestMsg = 2; - ValidateBasicMqttCredRequestMsg validateBasicMqttCredRequestMsg = 3; -} - -message ProvisionDeviceRequestMsg { - string deviceName = 1; - CredentialsType credentialsType = 2; - ProvisionDeviceCredentialsMsg provisionDeviceCredentialsMsg = 3; - CredentialsDataProto credentialsDataProto = 4; -} - -message ProvisionDeviceCredentialsMsg { - string provisionDeviceKey = 1; - string provisionDeviceSecret = 2; -} - -message ProvisionDeviceResponseMsg { - ResponseStatus status = 1; - CredentialsType credentialsType = 2; - string credentialsValue = 3; -} - -enum ResponseStatus { - UNKNOWN = 0; - SUCCESS = 1; - NOT_FOUND = 2; - FAILURE = 3; -} - -message GetOtaPackageRequestMsg { - int64 deviceIdMSB = 1; - int64 deviceIdLSB = 2; - int64 tenantIdMSB = 3; - int64 tenantIdLSB = 4; - string type = 5; -} - -message GetOtaPackageResponseMsg { - ResponseStatus responseStatus = 1; - int64 otaPackageIdMSB = 2; - int64 otaPackageIdLSB = 3; - string type = 4; - string title = 5; - string version = 6; - string contentType = 7; - string fileName = 8; -} - -message DeviceActivityProto { - int64 tenantIdMSB = 1; - int64 tenantIdLSB = 2; - int64 deviceIdMSB = 3; - int64 deviceIdLSB = 4; - int64 lastActivityTime = 5; -} - -//Used to report session state to tb-Service and persist this state in the cache on the tb-Service level. -message SubscriptionInfoProto { - int64 lastActivityTime = 1; - bool attributeSubscription = 2; - bool rpcSubscription = 3; -} - -message SessionSubscriptionInfoProto { - SessionInfoProto sessionInfo = 1; - SubscriptionInfoProto subscriptionInfo = 2; -} - -message DeviceSessionsCacheEntry { - repeated SessionSubscriptionInfoProto sessions = 1; -} - -message TransportToDeviceActorMsg { - SessionInfoProto sessionInfo = 1; - SessionEventMsg sessionEvent = 2; - GetAttributeRequestMsg getAttributes = 3; - SubscribeToAttributeUpdatesMsg subscribeToAttributes = 4; - SubscribeToRPCMsg subscribeToRPC = 5; - ToDeviceRpcResponseMsg toDeviceRPCCallResponse = 6; - SubscriptionInfoProto subscriptionInfo = 7; - ClaimDeviceMsg claimDevice = 8; - ProvisionDeviceRequestMsg provisionDevice = 9; - ToDeviceRpcResponseStatusMsg rpcResponseStatusMsg = 10; - SendPendingRPCMsg sendPendingRPC = 11; - UplinkNotificationMsg uplinkNotificationMsg = 12; -} - -message TransportToRuleEngineMsg { - SessionInfoProto sessionInfo = 1; - PostTelemetryMsg postTelemetry = 2; - PostAttributeMsg postAttributes = 3; - ToDeviceRpcResponseMsg toDeviceRPCCallResponse = 4; - ToServerRpcRequestMsg toServerRPCCallRequest = 5; -} - -/** - * TB Core Data Structures - */ - -message TbSubscriptionProto { - string serviceId = 1; - string sessionId = 2; - int32 subscriptionId = 3; - string entityType = 4; - int64 tenantIdMSB = 5; - int64 tenantIdLSB = 6; - int64 entityIdMSB = 7; - int64 entityIdLSB = 8; -} - -message TbTimeSeriesSubscriptionProto { - TbSubscriptionProto sub = 1; - bool allKeys = 2; - repeated TbSubscriptionKetStateProto keyStates = 3; - int64 startTime = 4; - int64 endTime = 5; - bool latestValues = 6; -} - -message TbAttributeSubscriptionProto { - TbSubscriptionProto sub = 1; - bool allKeys = 2; - repeated TbSubscriptionKetStateProto keyStates = 3; - string scope = 4; -} - -message TbAlarmSubscriptionProto { - TbSubscriptionProto sub = 1; - int64 ts = 2; -} - -message NotificationsSubscriptionProto { - TbSubscriptionProto sub = 1; - int32 limit = 2; -} - -message NotificationsCountSubscriptionProto { - TbSubscriptionProto sub = 1; -} - -message TbSubscriptionUpdateProto { - string sessionId = 1; - int32 subscriptionId = 2; - int32 errorCode = 3; - string errorMsg = 4; - repeated TbSubscriptionUpdateValueListProto data = 5; -} - -message TbAlarmSubscriptionUpdateProto { - string sessionId = 1; - int32 subscriptionId = 2; - int32 errorCode = 3; - string errorMsg = 4; - string alarm = 5; - bool deleted = 6; -} - -message NotificationsSubscriptionUpdateProto { - string sessionId = 1; - int32 subscriptionId = 2; - string notificationUpdate = 3; - string notificationRequestUpdate = 4; -} - -message NotificationUpdateProto { - int64 tenantIdMSB = 1; - int64 tenantIdLSB = 2; - int64 recipientIdMSB = 3; - int64 recipientIdLSB = 4; - string update = 5; -} - -message NotificationRequestUpdateProto { - int64 tenantIdMSB = 1; - int64 tenantIdLSB = 2; - string update = 6; -} - -message TbAttributeUpdateProto { - string entityType = 1; - int64 entityIdMSB = 2; - int64 entityIdLSB = 3; - int64 tenantIdMSB = 4; - int64 tenantIdLSB = 5; - string scope = 6; - repeated TsKvProto data = 7; -} - -message TbAlarmUpdateProto { - string entityType = 1; - int64 entityIdMSB = 2; - int64 entityIdLSB = 3; - int64 tenantIdMSB = 4; - int64 tenantIdLSB = 5; - string alarm = 6; -} - -message TbAlarmDeleteProto { - string entityType = 1; - int64 entityIdMSB = 2; - int64 entityIdLSB = 3; - int64 tenantIdMSB = 4; - int64 tenantIdLSB = 5; - string alarm = 6; -} - -message TbAttributeDeleteProto { - string entityType = 1; - int64 entityIdMSB = 2; - int64 entityIdLSB = 3; - int64 tenantIdMSB = 4; - int64 tenantIdLSB = 5; - string scope = 6; - repeated string keys = 7; - bool notifyDevice = 8; -} - -message TbTimeSeriesDeleteProto { - string entityType = 1; - int64 entityIdMSB = 2; - int64 entityIdLSB = 3; - int64 tenantIdMSB = 4; - int64 tenantIdLSB = 5; - repeated string keys = 6; -} - -message TbTimeSeriesUpdateProto { - string entityType = 1; - int64 entityIdMSB = 2; - int64 entityIdLSB = 3; - int64 tenantIdMSB = 4; - int64 tenantIdLSB = 5; - repeated TsKvProto data = 6; -} - -message TbSubscriptionCloseProto { - string sessionId = 1; - int32 subscriptionId = 2; -} - -message TbSubscriptionKetStateProto { - string key = 1; - int64 ts = 2; -} - -message TbSubscriptionUpdateValueListProto { - string key = 1; - repeated TbSubscriptionUpdateTsValue tsValue = 2; -} - -message TbSubscriptionUpdateTsValue { - int64 ts = 1; - optional string value = 2; -} - -/** - * TB Core to TB Core messages - */ - -message DeviceStateServiceMsgProto { - int64 tenantIdMSB = 1; - int64 tenantIdLSB = 2; - int64 deviceIdMSB = 3; - int64 deviceIdLSB = 4; - bool added = 5; - bool updated = 6; - bool deleted = 7; -} - -message SubscriptionMgrMsgProto { - TbTimeSeriesSubscriptionProto telemetrySub = 1; - TbAttributeSubscriptionProto attributeSub = 2; - TbSubscriptionCloseProto subClose = 3; - TbTimeSeriesUpdateProto tsUpdate = 4; - TbAttributeUpdateProto attrUpdate = 5; - TbAttributeDeleteProto attrDelete = 6; - TbAlarmSubscriptionProto alarmSub = 7; - TbAlarmUpdateProto alarmUpdate = 8; - TbAlarmDeleteProto alarmDelete = 9; - TbTimeSeriesDeleteProto tsDelete = 10; - NotificationsSubscriptionProto notificationsSub = 11; - NotificationsCountSubscriptionProto notificationsCountSub = 12; - NotificationUpdateProto notificationUpdate = 13; - NotificationRequestUpdateProto notificationRequestUpdate = 14; -} - -message LocalSubscriptionServiceMsgProto { - TbSubscriptionUpdateProto subUpdate = 1; - TbAlarmSubscriptionUpdateProto alarmSubUpdate = 2; - NotificationsSubscriptionUpdateProto notificationsSubUpdate = 3; -} - -message FromDeviceRPCResponseProto { - int64 requestIdMSB = 1; - int64 requestIdLSB = 2; - string response = 3; - int32 error = 4; -} - -message EdgeNotificationMsgProto { - int64 tenantIdMSB = 1; - int64 tenantIdLSB = 2; - int64 edgeIdMSB = 3; - int64 edgeIdLSB = 4; - string type = 5; - string action = 6; - int64 entityIdMSB = 7; - int64 entityIdLSB = 8; - string entityType = 9; - string body = 10; - PostTelemetryMsg postTelemetryMsg = 11; - PostAttributeMsg postAttributesMsg = 12; -} - -/** - TB Core to Version Control Service - */ -message CommitRequestMsg { - string txId = 1; // To correlate prepare, add, delete and push messages - PrepareMsg prepareMsg = 2; - AddMsg addMsg = 3; - DeleteMsg deleteMsg = 4; - PushMsg pushMsg = 5; - AbortMsg abortMsg = 6; -} - -message CommitResponseMsg { - int64 ts = 1; - string commitId = 2; - string name = 3; - string author = 4; - int32 added = 5; - int32 modified = 6; - int32 removed = 7; -} - -message PrepareMsg { - string commitMsg = 1; - string branchName = 2; - string authorName = 3; - string authorEmail = 4; -} - -message AddMsg { - string relativePath = 1; - string entityDataJsonChunk = 2; - string chunkedMsgId = 3; - int32 chunkIndex = 4; - int32 chunksCount = 5; -} - -message DeleteMsg { - string relativePath = 1; -} - -message PushMsg { -} - -message AbortMsg { -} - -message ListVersionsRequestMsg { - string branchName = 1; - string entityType = 2; - int64 entityIdMSB = 3; - int64 entityIdLSB = 4; - int32 pageSize = 5; - int32 page = 6; - string textSearch = 7; - string sortProperty = 8; - string sortDirection = 9; -} - -message EntityVersionProto { - int64 ts = 1; - string id = 2; - string name = 3; - string author = 4; -} - -message ListVersionsResponseMsg { - repeated EntityVersionProto versions = 1; - int32 totalPages = 2; - int64 totalElements = 3; - bool hasNext = 4; -} - -message ListEntitiesRequestMsg { - string versionId = 1; - string entityType = 2; -} - -message VersionedEntityInfoProto { - string entityType = 1; - int64 entityIdMSB = 2; - int64 entityIdLSB = 3; -} - -message ListEntitiesResponseMsg { - repeated VersionedEntityInfoProto entities = 1; -} - -message ListBranchesRequestMsg { -} - -message BranchInfoProto { - string name = 1; - bool isDefault = 2; -} - -message ListBranchesResponseMsg { - repeated BranchInfoProto branches = 1; -} - -message EntityContentRequestMsg { - string versionId = 1; - string entityType = 2; - int64 entityIdMSB = 3; - int64 entityIdLSB = 4; -} - -message EntityContentResponseMsg { - string data = 1; - int32 chunkIndex = 2; - int32 chunksCount = 3; -} - -message EntitiesContentRequestMsg { - string versionId = 1; - string entityType = 2; - int32 offset = 3; - int32 limit = 4; -} - -message EntitiesContentResponseMsg { - EntityContentResponseMsg item = 1; - int32 itemIdx = 2; - int32 itemsCount = 3; -} - -message VersionsDiffRequestMsg { - string path = 1; - string versionId1 = 2; - string versionId2 = 3; -} - -message VersionsDiffResponseMsg { - repeated EntityVersionsDiff diff = 1; -} - -message EntityVersionsDiff { - string entityType = 1; - int64 entityIdMSB = 2; - int64 entityIdLSB = 3; - string entityDataAtVersion1 = 4; - string entityDataAtVersion2 = 5; - string rawDiff = 6; -} - -message GenericRepositoryRequestMsg {} - -message GenericRepositoryResponseMsg {} - -message ToVersionControlServiceMsg { - string nodeId = 1; - int64 tenantIdMSB = 2; - int64 tenantIdLSB = 3; - int64 requestIdMSB = 4; - int64 requestIdLSB = 5; - bytes vcSettings = 6; - GenericRepositoryRequestMsg initRepositoryRequest = 7; - GenericRepositoryRequestMsg testRepositoryRequest = 8; - GenericRepositoryRequestMsg clearRepositoryRequest = 9; - CommitRequestMsg commitRequest = 10; - ListVersionsRequestMsg listVersionRequest = 11; - ListEntitiesRequestMsg listEntitiesRequest = 12; - ListBranchesRequestMsg listBranchesRequest = 13; - EntityContentRequestMsg entityContentRequest = 14; - EntitiesContentRequestMsg entitiesContentRequest = 15; - VersionsDiffRequestMsg versionsDiffRequest = 16; -} - -message VersionControlResponseMsg { - int64 requestIdMSB = 1; - int64 requestIdLSB = 2; - string error = 3; - GenericRepositoryResponseMsg genericResponse = 4; - CommitResponseMsg commitResponse = 5; - ListBranchesResponseMsg listBranchesResponse = 6; - ListEntitiesResponseMsg listEntitiesResponse = 7; - ListVersionsResponseMsg listVersionsResponse = 8; - EntityContentResponseMsg entityContentResponse = 9; - EntitiesContentResponseMsg entitiesContentResponse = 10; - VersionsDiffResponseMsg versionsDiffResponse = 11; -} - -/** - * Main messages; - */ - -/* Request from Transport Service to ThingsBoard Core Service */ -message TransportApiRequestMsg { - ValidateDeviceTokenRequestMsg validateTokenRequestMsg = 1; - ValidateDeviceX509CertRequestMsg validateX509CertRequestMsg = 2; - GetOrCreateDeviceFromGatewayRequestMsg getOrCreateDeviceRequestMsg = 3; - GetEntityProfileRequestMsg entityProfileRequestMsg = 4; - LwM2MRequestMsg lwM2MRequestMsg = 5; - ValidateBasicMqttCredRequestMsg validateBasicMqttCredRequestMsg = 6; - ProvisionDeviceRequestMsg provisionDeviceRequestMsg = 7; - ValidateDeviceLwM2MCredentialsRequestMsg validateDeviceLwM2MCredentialsRequestMsg = 8; - GetResourceRequestMsg resourceRequestMsg = 9; - GetOtaPackageRequestMsg otaPackageRequestMsg = 10; - GetSnmpDevicesRequestMsg snmpDevicesRequestMsg = 11; - GetDeviceRequestMsg deviceRequestMsg = 12; - GetDeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 13; - GetAllQueueRoutingInfoRequestMsg getAllQueueRoutingInfoRequestMsg = 14; - ValidateOrCreateDeviceX509CertRequestMsg validateOrCreateX509CertRequestMsg = 15; -} - -/* Response from ThingsBoard Core Service to Transport Service */ -message TransportApiResponseMsg { - ValidateDeviceCredentialsResponseMsg validateCredResponseMsg = 1; - GetOrCreateDeviceFromGatewayResponseMsg getOrCreateDeviceResponseMsg = 2; - GetEntityProfileResponseMsg entityProfileResponseMsg = 3; - ProvisionDeviceResponseMsg provisionDeviceResponseMsg = 4; - GetSnmpDevicesResponseMsg snmpDevicesResponseMsg = 5; - LwM2MResponseMsg lwM2MResponseMsg = 6; - GetResourceResponseMsg resourceResponseMsg = 7; - GetOtaPackageResponseMsg otaPackageResponseMsg = 8; - GetDeviceResponseMsg deviceResponseMsg = 9; - GetDeviceCredentialsResponseMsg deviceCredentialsResponseMsg = 10; - repeated GetQueueRoutingInfoResponseMsg getQueueRoutingInfoResponseMsgs = 11; -} - -/* Messages that are handled by ThingsBoard Core Service */ -message ToCoreMsg { - TransportToDeviceActorMsg toDeviceActorMsg = 1; - DeviceStateServiceMsgProto deviceStateServiceMsg = 2; - SubscriptionMgrMsgProto toSubscriptionMgrMsg = 3; - bytes toDeviceActorNotificationMsg = 4; - EdgeNotificationMsgProto edgeNotificationMsg = 5; - DeviceActivityProto deviceActivityMsg = 6; - NotificationSchedulerServiceMsg notificationSchedulerServiceMsg = 7; -} - -/* High priority messages with low latency are handled by ThingsBoard Core Service separately */ -message ToCoreNotificationMsg { - LocalSubscriptionServiceMsgProto toLocalSubscriptionServiceMsg = 1; - FromDeviceRPCResponseProto fromDeviceRpcResponse = 2; - bytes componentLifecycleMsg = 3; - bytes edgeEventUpdateMsg = 4; - QueueUpdateMsg queueUpdateMsg = 5; - QueueDeleteMsg queueDeleteMsg = 6; - VersionControlResponseMsg vcResponseMsg = 7; - bytes toEdgeSyncRequestMsg = 8; - bytes fromEdgeSyncResponseMsg = 9; - SubscriptionMgrMsgProto toSubscriptionMgrMsg = 10; - NotificationRuleProcessorMsg notificationRuleProcessorMsg = 11; -} - -/* Messages that are handled by ThingsBoard RuleEngine Service */ -message ToRuleEngineMsg { - int64 tenantIdMSB = 1; - int64 tenantIdLSB = 2; - bytes tbMsg = 3; - repeated string relationTypes = 4; - string failureMessage = 5; -} - -message ToRuleEngineNotificationMsg { - bytes componentLifecycleMsg = 1; - FromDeviceRPCResponseProto fromDeviceRpcResponse = 2; - QueueUpdateMsg queueUpdateMsg = 3; - QueueDeleteMsg queueDeleteMsg = 4; -} - -/* Messages that are handled by ThingsBoard Transport Service */ -message ToTransportMsg { - int64 sessionIdMSB = 1; - int64 sessionIdLSB = 2; - SessionCloseNotificationProto sessionCloseNotification = 3; - GetAttributeResponseMsg getAttributesResponse = 4; - AttributeUpdateNotificationMsg attributeUpdateNotification = 5; - ToDeviceRpcRequestMsg toDeviceRequest = 6; - ToServerRpcResponseMsg toServerResponse = 7; - EntityUpdateMsg entityUpdateMsg = 8; - EntityDeleteMsg entityDeleteMsg = 9; - ProvisionDeviceResponseMsg provisionResponse = 10; - ToTransportUpdateCredentialsProto toTransportUpdateCredentialsNotification = 11; - ResourceUpdateMsg resourceUpdateMsg = 12; - ResourceDeleteMsg resourceDeleteMsg = 13; - UplinkNotificationMsg uplinkNotificationMsg = 14; - QueueUpdateMsg queueUpdateMsg = 15; - QueueDeleteMsg queueDeleteMsg = 16; -} - -message UsageStatsKVProto{ - string key = 1; - int64 value = 2; -} - -message ToUsageStatsServiceMsg { - int64 tenantIdMSB = 1; - int64 tenantIdLSB = 2; - int64 entityIdMSB = 3; - int64 entityIdLSB = 4; - repeated UsageStatsKVProto values = 5; - int64 customerIdMSB = 6; - int64 customerIdLSB = 7; - string serviceId = 8; -} - -message ToOtaPackageStateServiceMsg { - int64 ts = 1; - int64 tenantIdMSB = 2; - int64 tenantIdLSB = 3; - int64 deviceIdMSB = 4; - int64 deviceIdLSB = 5; - int64 otaPackageIdMSB = 6; - int64 otaPackageIdLSB = 7; - string type = 8; -} - -message NotificationSchedulerServiceMsg { - int64 tenantIdMSB = 1; - int64 tenantIdLSB = 2; - int64 requestIdMSB = 3; - int64 requestIdLSB = 4; - int64 ts = 5; -} - -message NotificationRuleProcessorMsg { - bytes trigger = 1; -} diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml index 116a94791d..8429c4452b 100644 --- a/common/coap-server/pom.xml +++ b/common/coap-server/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index cf825ea637..fa2dca9dca 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmOperationResult.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmOperationResult.java deleted file mode 100644 index bae7c52ee7..0000000000 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmOperationResult.java +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.dao.alarm; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import org.thingsboard.server.common.data.alarm.Alarm; -import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; -import org.thingsboard.server.common.data.alarm.AlarmSeverity; -import org.thingsboard.server.common.data.id.EntityId; - -import java.util.Collections; -import java.util.List; - -@Builder -@Data -@AllArgsConstructor -@Deprecated -public class AlarmOperationResult { - private final Alarm alarm; - private final boolean successful; - private final boolean created; - private final AlarmSeverity oldSeverity; - private final List propagatedEntitiesList; - - public AlarmOperationResult(Alarm alarm, boolean successful) { - this(alarm, successful, Collections.emptyList()); - } - - public AlarmOperationResult(Alarm alarm, boolean successful, List propagatedEntitiesList) { - this(alarm, successful, false, null, propagatedEntitiesList); - } - - public AlarmOperationResult(Alarm alarm, boolean successful, boolean created, List propagatedEntitiesList) { - this.alarm = alarm; - this.successful = successful; - this.created = created; - this.propagatedEntitiesList = propagatedEntitiesList; - this.oldSeverity = null; - } - - //Temporary while we have not removed the AlarmOperationResult. - public AlarmOperationResult(AlarmApiCallResult result) { - this.alarm = result.getAlarm() != null ? new Alarm(result.getAlarm()) : null; - this.successful = result.isSuccessful() && (result.isCreated() || result.isModified()); - this.created = result.isCreated(); - this.oldSeverity = result.getOldSeverity(); - this.propagatedEntitiesList = result.getPropagatedEntitiesList(); - } -} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java index fbf8892bd5..ee2f849847 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.alarm; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; @@ -33,12 +34,14 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.query.AlarmCountQuery; import org.thingsboard.server.common.data.query.AlarmData; import org.thingsboard.server.common.data.query.AlarmDataQuery; import org.thingsboard.server.dao.entity.EntityDaoService; import java.util.Collection; +import java.util.Set; public interface AlarmService extends EntityDaoService { @@ -74,26 +77,9 @@ public interface AlarmService extends EntityDaoService { AlarmApiCallResult delAlarm(TenantId tenantId, AlarmId alarmId); - /* - * Legacy API, before 3.5. - */ - @Deprecated(since = "3.5.0", forRemoval = true) - AlarmOperationResult createOrUpdateAlarm(Alarm alarm); - - @Deprecated(since = "3.5.0", forRemoval = true) - AlarmOperationResult createOrUpdateAlarm(Alarm alarm, boolean alarmCreationEnabled); - - @Deprecated(since = "3.5.0", forRemoval = true) - ListenableFuture ackAlarm(TenantId tenantId, AlarmId alarmId, long ackTs); - - @Deprecated(since = "3.5.0", forRemoval = true) - ListenableFuture clearAlarm(TenantId tenantId, AlarmId alarmId, JsonNode details, long clearTs); + AlarmApiCallResult delAlarm(TenantId tenantId, AlarmId alarmId, boolean checkAndDeleteAlarmType); - @Deprecated(since = "3.5.0", forRemoval = true) - AlarmOperationResult deleteAlarm(TenantId tenantId, AlarmId alarmId); - - @Deprecated(since = "3.5.0", forRemoval = true) - ListenableFuture findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type); + void delAlarmTypes(TenantId tenantId, Set types); // Other API Alarm findAlarmById(TenantId tenantId, AlarmId alarmId); @@ -102,13 +88,13 @@ public interface AlarmService extends EntityDaoService { AlarmInfo findAlarmInfoById(TenantId tenantId, AlarmId alarmId); - ListenableFuture> findAlarms(TenantId tenantId, AlarmQuery query); + PageData findAlarms(TenantId tenantId, AlarmQuery query); - ListenableFuture> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query); + PageData findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query); - ListenableFuture> findAlarmsV2(TenantId tenantId, AlarmQueryV2 query); + PageData findAlarmsV2(TenantId tenantId, AlarmQueryV2 query); - ListenableFuture> findCustomerAlarmsV2(TenantId tenantId, CustomerId customerId, AlarmQueryV2 query); + PageData findCustomerAlarmsV2(TenantId tenantId, CustomerId customerId, AlarmQueryV2 query); AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus, AlarmStatus alarmStatus, String assigneeId); @@ -118,7 +104,13 @@ public interface AlarmService extends EntityDaoService { PageData findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection orderedEntityIds); + PageData findAlarmIdsByAssigneeId(TenantId tenantId, UserId userId, PageLink pageLink); + void deleteEntityAlarmRelations(TenantId tenantId, EntityId entityId); + void deleteEntityAlarmRecordsByTenantId(TenantId tenantId); + long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query); + + PageData findAlarmTypesByTenantId(TenantId tenantId, PageLink pageLink); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetProfileService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetProfileService.java index f89153feee..7d7769411e 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetProfileService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetProfileService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.asset; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.asset.AssetProfileInfo; import org.thingsboard.server.common.data.id.AssetProfileId; @@ -23,14 +24,22 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.EntityDaoService; +import java.util.List; + public interface AssetProfileService extends EntityDaoService { AssetProfile findAssetProfileById(TenantId tenantId, AssetProfileId assetProfileId); + AssetProfile findAssetProfileById(TenantId tenantId, AssetProfileId assetProfileId, boolean putInCache); + AssetProfile findAssetProfileByName(TenantId tenantId, String profileName); + AssetProfile findAssetProfileByName(TenantId tenantId, String profileName, boolean putInCache); + AssetProfileInfo findAssetProfileInfoById(TenantId tenantId, AssetProfileId assetProfileId); + AssetProfile saveAssetProfile(AssetProfile assetProfile, boolean doValidate); + AssetProfile saveAssetProfile(AssetProfile assetProfile); void deleteAssetProfile(TenantId tenantId, AssetProfileId assetProfileId); @@ -51,4 +60,6 @@ public interface AssetProfileService extends EntityDaoService { void deleteAssetProfilesByTenantId(TenantId tenantId); + List findAssetProfileNamesByTenantId(TenantId tenantId, boolean activeOnly); + } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetService.java index 8b825e0837..cec8dd9007 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetService.java @@ -41,6 +41,8 @@ public interface AssetService extends EntityDaoService { Asset findAssetByTenantIdAndName(TenantId tenantId, String name); + Asset saveAsset(Asset asset, boolean doValidate); + Asset saveAsset(Asset asset); Asset assignAssetToCustomer(TenantId tenantId, AssetId assetId, CustomerId customerId); @@ -79,6 +81,7 @@ public interface AssetService extends EntityDaoService { ListenableFuture> findAssetsByQuery(TenantId tenantId, AssetSearchQuery query); + @Deprecated(since = "3.6.2", forRemoval = true) ListenableFuture> findAssetTypesByTenantId(TenantId tenantId); Asset assignAssetToEdge(TenantId tenantId, AssetId assetId, EdgeId edgeId); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java index c9974bba69..d94256bb99 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java @@ -29,7 +29,7 @@ import org.thingsboard.server.dao.entity.EntityDaoService; import java.util.List; public interface DashboardService extends EntityDaoService { - + Dashboard findDashboardById(TenantId tenantId, DashboardId dashboardId); ListenableFuture findDashboardByIdAsync(TenantId tenantId, DashboardId dashboardId); @@ -40,6 +40,8 @@ public interface DashboardService extends EntityDaoService { ListenableFuture findDashboardInfoByIdAsync(TenantId tenantId, DashboardId dashboardId); + Dashboard saveDashboard(Dashboard dashboard, boolean doValidate); + Dashboard saveDashboard(Dashboard dashboard); Dashboard assignDashboardToCustomer(TenantId tenantId, DashboardId dashboardId, CustomerId customerId); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityService.java new file mode 100644 index 0000000000..97e69c5ec7 --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityService.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.device; + +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.core.io.Resource; +import org.thingsboard.server.common.data.Device; + +import java.net.URISyntaxException; + +public interface DeviceConnectivityService { + + JsonNode findDevicePublishTelemetryCommands(String baseUrl, Device device) throws URISyntaxException; + + Resource getPemCertFile(String protocol); + + Resource createGatewayDockerComposeFile(String baseUrl, Device device) throws URISyntaxException; +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsService.java index c57b316a44..c2c2f23a43 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsService.java @@ -36,4 +36,6 @@ public interface DeviceCredentialsService { void deleteDeviceCredentials(TenantId tenantId, DeviceCredentials deviceCredentials); + void deleteDeviceCredentialsByDeviceId(TenantId tenantId, DeviceId deviceId); + } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceProfileService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceProfileService.java index e765cc5030..066254d28d 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceProfileService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceProfileService.java @@ -17,20 +17,29 @@ package org.thingsboard.server.dao.device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileInfo; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.EntityDaoService; +import java.util.List; + public interface DeviceProfileService extends EntityDaoService { DeviceProfile findDeviceProfileById(TenantId tenantId, DeviceProfileId deviceProfileId); + DeviceProfile findDeviceProfileById(TenantId tenantId, DeviceProfileId deviceProfileId, boolean putInCache); + DeviceProfile findDeviceProfileByName(TenantId tenantId, String profileName); + DeviceProfile findDeviceProfileByName(TenantId tenantId, String profileName, boolean putInCache); + DeviceProfileInfo findDeviceProfileInfoById(TenantId tenantId, DeviceProfileId deviceProfileId); + DeviceProfile saveDeviceProfile(DeviceProfile deviceProfile, boolean doValidate); + DeviceProfile saveDeviceProfile(DeviceProfile deviceProfile); void deleteDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId); @@ -53,4 +62,6 @@ public interface DeviceProfileService extends EntityDaoService { void deleteDeviceProfilesByTenantId(TenantId tenantId); + List findDeviceProfileNamesByTenantId(TenantId tenantId, boolean activeOnly); + } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java index a90ea9a572..0bb0dbf50e 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java @@ -95,6 +95,7 @@ public interface DeviceService extends EntityDaoService { ListenableFuture> findDevicesByQuery(TenantId tenantId, DeviceSearchQuery query); + @Deprecated(since = "3.6.2", forRemoval = true) ListenableFuture> findDeviceTypesByTenantId(TenantId tenantId); Device assignDeviceToTenant(TenantId tenantId, Device device); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeEventService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeEventService.java index dcb3a5232a..9055202f4f 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeEventService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeEventService.java @@ -26,7 +26,7 @@ public interface EdgeEventService { ListenableFuture saveAsync(EdgeEvent edgeEvent); - PageData findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink, boolean withTsUpdate); + PageData findEdgeEvents(TenantId tenantId, EdgeId edgeId, Long seqIdStart, Long seqIdEnd, TimePageLink pageLink); /** * Executes stored procedure to cleanup old edge events. diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java index 064aa39a80..064782df06 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.EntityDaoService; @@ -83,6 +84,8 @@ public interface EdgeService extends EntityDaoService { PageData findEdgesByTenantIdAndEntityId(TenantId tenantId, EntityId ruleChainId, PageLink pageLink); + PageData findEdgesByTenantProfileId(TenantProfileId tenantProfileId, PageLink pageLink); + List findAllRelatedEdgeIds(TenantId tenantId, EntityId entityId); PageData findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId, PageLink pageLink); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeSynchronizationManager.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeSynchronizationManager.java new file mode 100644 index 0000000000..812b30dc9d --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeSynchronizationManager.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.edge; + +import org.thingsboard.server.common.data.id.EdgeId; + +public interface EdgeSynchronizationManager { + + ThreadLocal getEdgeId(); +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java index 31b2004a38..7a020767f5 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java @@ -38,6 +38,8 @@ public interface EntityViewService extends EntityDaoService { EntityView saveEntityView(EntityView entityView); + EntityView saveEntityView(EntityView entityView, boolean doValidate); + EntityView assignEntityViewToCustomer(TenantId tenantId, EntityViewId entityViewId, CustomerId customerId); EntityView unassignEntityViewFromCustomer(TenantId tenantId, EntityViewId entityViewId); @@ -48,6 +50,8 @@ public interface EntityViewService extends EntityDaoService { EntityView findEntityViewById(TenantId tenantId, EntityViewId entityViewId); + EntityView findEntityViewById(TenantId tenantId, EntityViewId entityViewId, boolean putInCache); + EntityView findEntityViewByTenantIdAndName(TenantId tenantId, String name); PageData findEntityViewByTenantId(TenantId tenantId, PageLink pageLink); @@ -74,6 +78,8 @@ public interface EntityViewService extends EntityDaoService { List findEntityViewsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId); + boolean existsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId); + void deleteEntityView(TenantId tenantId, EntityViewId entityViewId); void deleteEntityViewsByTenantId(TenantId tenantId); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/housekeeper/HouseKeeperService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/housekeeper/HouseKeeperService.java new file mode 100644 index 0000000000..62f53e0f41 --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/housekeeper/HouseKeeperService.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.housekeeper; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.TenantId; + +import java.util.List; + +public interface HouseKeeperService { + + ListenableFuture> unassignDeletedUserAlarms(TenantId tenantId, User user, long unassignTs); + +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleService.java index bd23f1c48d..d1b7513031 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleService.java @@ -19,7 +19,7 @@ import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationSettingsService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationSettingsService.java index 46eb5ca54e..9fca174b3a 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationSettingsService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationSettingsService.java @@ -16,7 +16,9 @@ package org.thingsboard.server.dao.notification; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; +import org.thingsboard.server.common.data.notification.settings.UserNotificationSettings; public interface NotificationSettingsService { @@ -24,6 +26,12 @@ public interface NotificationSettingsService { NotificationSettings findNotificationSettings(TenantId tenantId); + UserNotificationSettings saveUserNotificationSettings(TenantId tenantId, UserId userId, UserNotificationSettings settings); + + UserNotificationSettings getUserNotificationSettings(TenantId tenantId, UserId userId, boolean format); + void createDefaultNotificationConfigs(TenantId tenantId); + void updateDefaultNotificationConfigs(TenantId tenantId); + } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java index 05299c1675..bd442f6bb6 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetService.java @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.platform.UsersFilterType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -40,6 +41,8 @@ public interface NotificationTargetService { List findNotificationTargetsByTenantIdAndIds(TenantId tenantId, List ids); + List findNotificationTargetsByTenantIdAndUsersFilterType(TenantId tenantId, UsersFilterType filterType); + PageData findRecipientsForNotificationTarget(TenantId tenantId, CustomerId customerId, NotificationTargetId targetId, PageLink pageLink); PageData findRecipientsForNotificationTargetConfig(TenantId tenantId, PlatformUsersNotificationTargetConfig targetConfig, PageLink pageLink); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/relation/RelationService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/relation/RelationService.java index 3ddb6187ea..39e94b3a6b 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/relation/RelationService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/relation/RelationService.java @@ -53,6 +53,8 @@ public interface RelationService { void deleteEntityRelations(TenantId tenantId, EntityId entity); + void deleteEntityCommonRelations(TenantId tenantId, EntityId entity); + List findByFrom(TenantId tenantId, EntityId from, RelationTypeGroup typeGroup); ListenableFuture> findByFromAsync(TenantId tenantId, EntityId from, RelationTypeGroup typeGroup); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java new file mode 100644 index 0000000000..bb4f23a31e --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java @@ -0,0 +1,68 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.resource; + +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.HasImage; +import org.thingsboard.server.common.data.TbImageDeleteResult; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.widget.WidgetTypeDetails; + +public interface ImageService { + + TbResourceInfo saveImage(TbResource image); + + TbResourceInfo saveImageInfo(TbResourceInfo imageInfo); + + TbResourceInfo getImageInfoByTenantIdAndKey(TenantId tenantId, String key); + + TbResourceInfo getPublicImageInfoByKey(String publicResourceKey); + + PageData getImagesByTenantId(TenantId tenantId, PageLink pageLink); + + PageData getAllImagesByTenantId(TenantId tenantId, PageLink pageLink); + + byte[] getImageData(TenantId tenantId, TbResourceId imageId); + + byte[] getImagePreview(TenantId tenantId, TbResourceId imageId); + + TbImageDeleteResult deleteImage(TbResourceInfo imageInfo, boolean force); + + TbResourceInfo findSystemOrTenantImageByEtag(TenantId tenantId, String etag); + + boolean replaceBase64WithImageUrl(HasImage entity, String type); + + boolean replaceBase64WithImageUrl(Dashboard dashboard); + + boolean replaceBase64WithImageUrl(WidgetTypeDetails widgetType); + + void inlineImage(HasImage entity); + + void inlineImages(Dashboard dashboard); + + void inlineImages(WidgetTypeDetails widgetTypeDetails); + + void inlineImageForEdge(HasImage entity); + + void inlineImagesForEdge(Dashboard dashboard); + + void inlineImagesForEdge(WidgetTypeDetails widgetTypeDetails); +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java index 6f0e362209..b4cda2d733 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java @@ -32,12 +32,18 @@ public interface ResourceService extends EntityDaoService { TbResource saveResource(TbResource resource); - TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceId); + TbResource saveResource(TbResource resource, boolean doValidate); + + TbResource findResourceByTenantIdAndKey(TenantId tenantId, ResourceType resourceType, String resourceKey); TbResource findResourceById(TenantId tenantId, TbResourceId resourceId); TbResourceInfo findResourceInfoById(TenantId tenantId, TbResourceId resourceId); + TbResourceInfo findResourceInfoByTenantIdAndKey(TenantId tenantId, ResourceType resourceType, String resourceKey); + + PageData findAllTenantResources(TenantId tenantId, PageLink pageLink); + ListenableFuture findResourceInfoByIdAsync(TenantId tenantId, TbResourceId resourceId); PageData findAllTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink); @@ -50,7 +56,10 @@ public interface ResourceService extends EntityDaoService { void deleteResource(TenantId tenantId, TbResourceId resourceId); + void deleteResource(TenantId tenantId, TbResourceId resourceId, boolean force); + void deleteResourcesByTenantId(TenantId tenantId); long sumDataSizeByTenantId(TenantId tenantId); + } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java index 331fa2e5db..532da4ac00 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java @@ -35,6 +35,7 @@ import org.thingsboard.server.dao.entity.EntityDaoService; import java.util.Collection; import java.util.List; +import java.util.function.Function; /** * Created by igor on 3/12/18. @@ -45,7 +46,7 @@ public interface RuleChainService extends EntityDaoService { boolean setRootRuleChain(TenantId tenantId, RuleChainId ruleChainId); - RuleChainUpdateResult saveRuleChainMetaData(TenantId tenantId, RuleChainMetaData ruleChainMetaData); + RuleChainUpdateResult saveRuleChainMetaData(TenantId tenantId, RuleChainMetaData ruleChainMetaData, Function ruleNodeUpdater); RuleChainMetaData loadRuleChainMetaData(TenantId tenantId, RuleChainId ruleChainId); @@ -75,7 +76,7 @@ public interface RuleChainService extends EntityDaoService { RuleChainData exportTenantRuleChains(TenantId tenantId, PageLink pageLink) throws ThingsboardException; - List importTenantRuleChains(TenantId tenantId, RuleChainData ruleChainData, boolean overwrite); + List importTenantRuleChains(TenantId tenantId, RuleChainData ruleChainData, boolean overwrite, Function ruleNodeUpdater); RuleChain assignRuleChainToEdge(TenantId tenantId, RuleChainId ruleChainId, EdgeId edgeId); @@ -99,6 +100,12 @@ public interface RuleChainService extends EntityDaoService { PageData findAllRuleNodesByType(String type, PageLink pageLink); + PageData findAllRuleNodesByTypeAndVersionLessThan(String type, int version, PageLink pageLink); + + PageData findAllRuleNodeIdsByTypeAndVersionLessThan(String type, int version, PageLink pageLink); + + List findAllRuleNodesByIds(List ruleNodeIds); + RuleNode saveRuleNode(TenantId tenantId, RuleNode ruleNode); void deleteRuleNodes(TenantId tenantId, RuleChainId ruleChainId); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java index 186813f476..c2bc997235 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java @@ -42,6 +42,8 @@ public interface TimeseriesService { ListenableFuture> findLatest(TenantId tenantId, EntityId entityId, Collection keys); + List findLatestSync(TenantId tenantId, EntityId entityId, Collection keys); + ListenableFuture> findAllLatest(TenantId tenantId, EntityId entityId); ListenableFuture save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/usagerecord/ApiLimitService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/usagerecord/ApiLimitService.java index cd9524e1b1..69e366327d 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/usagerecord/ApiLimitService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/usagerecord/ApiLimitService.java @@ -17,9 +17,14 @@ package org.thingsboard.server.dao.usagerecord; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; + +import java.util.function.Function; public interface ApiLimitService { boolean checkEntitiesLimit(TenantId tenantId, EntityType entityType); + long getLimit(TenantId tenantId, Function extractor); + } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java index e1f16be1ed..b874e038d2 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java @@ -30,34 +30,34 @@ import org.thingsboard.server.dao.entity.EntityDaoService; import java.util.List; public interface UserService extends EntityDaoService { - - User findUserById(TenantId tenantId, UserId userId); - ListenableFuture findUserByIdAsync(TenantId tenantId, UserId userId); + User findUserById(TenantId tenantId, UserId userId); - User findUserByEmail(TenantId tenantId, String email); + ListenableFuture findUserByIdAsync(TenantId tenantId, UserId userId); + + User findUserByEmail(TenantId tenantId, String email); User findUserByTenantIdAndEmail(TenantId tenantId, String email); - User saveUser(User user); + User saveUser(TenantId tenantId, User user); + + UserCredentials findUserCredentialsByUserId(TenantId tenantId, UserId userId); + + UserCredentials findUserCredentialsByActivateToken(TenantId tenantId, String activateToken); + + UserCredentials findUserCredentialsByResetToken(TenantId tenantId, String resetToken); - UserCredentials findUserCredentialsByUserId(TenantId tenantId, UserId userId); - - UserCredentials findUserCredentialsByActivateToken(TenantId tenantId, String activateToken); + UserCredentials saveUserCredentials(TenantId tenantId, UserCredentials userCredentials); - UserCredentials findUserCredentialsByResetToken(TenantId tenantId, String resetToken); + UserCredentials activateUserCredentials(TenantId tenantId, String activateToken, String password); - UserCredentials saveUserCredentials(TenantId tenantId, UserCredentials userCredentials); - - UserCredentials activateUserCredentials(TenantId tenantId, String activateToken, String password); - - UserCredentials requestPasswordReset(TenantId tenantId, String email); + UserCredentials requestPasswordReset(TenantId tenantId, String email); UserCredentials requestExpiredPasswordReset(TenantId tenantId, UserCredentialsId userCredentialsId); UserCredentials replaceUserCredentials(TenantId tenantId, UserCredentials userCredentials); - void deleteUser(TenantId tenantId, UserId userId); + void deleteUser(TenantId tenantId, User user); PageData findUsersByTenantId(TenantId tenantId, PageLink pageLink); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeService.java index cb02aa1180..d8df5d61a3 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeService.java @@ -17,6 +17,10 @@ package org.thingsboard.server.dao.widget; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetTypeId; +import org.thingsboard.server.common.data.id.WidgetsBundleId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.widget.DeprecatedFilter; import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetTypeInfo; @@ -30,18 +34,32 @@ public interface WidgetTypeService extends EntityDaoService { WidgetTypeDetails findWidgetTypeDetailsById(TenantId tenantId, WidgetTypeId widgetTypeId); + boolean widgetTypeExistsByTenantIdAndWidgetTypeId(TenantId tenantId, WidgetTypeId widgetTypeId); + WidgetTypeDetails saveWidgetType(WidgetTypeDetails widgetType); void deleteWidgetType(TenantId tenantId, WidgetTypeId widgetTypeId); - List findWidgetTypesByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias); + PageData findSystemWidgetTypesByPageLink(TenantId tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink); + + PageData findAllTenantWidgetTypesByTenantIdAndPageLink(TenantId tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink); + + PageData findTenantWidgetTypesByTenantIdAndPageLink(TenantId tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink); + + List findWidgetTypesByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId); + + List findWidgetTypesDetailsByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId); + + PageData findWidgetTypesInfosByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink); + + List findWidgetFqnsByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId); - List findWidgetTypesDetailsByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias); + WidgetType findWidgetTypeByTenantIdAndFqn(TenantId tenantId, String fqn); - List findWidgetTypesInfosByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias); + void updateWidgetsBundleWidgetTypes(TenantId tenantId, WidgetsBundleId widgetsBundleId, List widgetTypeIds); - WidgetType findWidgetTypeByTenantIdBundleAliasAndAlias(TenantId tenantId, String bundleAlias, String alias); + void updateWidgetsBundleWidgetFqns(TenantId tenantId, WidgetsBundleId widgetsBundleId, List widgetFqns); - void deleteWidgetTypesByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias); + void deleteWidgetTypesByTenantId(TenantId tenantId); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleService.java index e350846881..a836fb893d 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleService.java @@ -34,13 +34,15 @@ public interface WidgetsBundleService extends EntityDaoService { WidgetsBundle findWidgetsBundleByTenantIdAndAlias(TenantId tenantId, String alias); - PageData findSystemWidgetsBundlesByPageLink(TenantId tenantId, PageLink pageLink); + PageData findSystemWidgetsBundlesByPageLink(TenantId tenantId, boolean fullSearch, PageLink pageLink); List findSystemWidgetsBundles(TenantId tenantId); PageData findTenantWidgetsBundlesByTenantId(TenantId tenantId, PageLink pageLink); - PageData findAllTenantWidgetsBundlesByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); + PageData findAllTenantWidgetsBundlesByTenantIdAndPageLink(TenantId tenantId, boolean fullSearch, PageLink pageLink); + + PageData findTenantWidgetsBundlesByTenantIdAndPageLink(TenantId tenantId, boolean fullSearch, PageLink pageLink); List findAllTenantWidgetsBundlesByTenantId(TenantId tenantId); diff --git a/common/data/pom.xml b/common/data/pom.xml index 35e9d1dc55..3eb0da67c2 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ApiFeature.java b/common/data/src/main/java/org/thingsboard/server/common/data/ApiFeature.java index 31d8b7aeea..84fe049ee9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ApiFeature.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ApiFeature.java @@ -22,6 +22,7 @@ public enum ApiFeature { DB("dbApiState", "Telemetry persistence"), RE("ruleEngineApiState", "Rule Engine execution"), JS("jsExecutionApiState", "JavaScript functions execution"), + TBEL("tbelExecutionApiState", "Tbel functions execution"), EMAIL("emailApiState", "Email messages"), SMS("smsApiState", "SMS messages"), ALARM("alarmApiState", "Alarms"); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageRecordKey.java b/common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageRecordKey.java index 64eabfdd16..4554ec8f5f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageRecordKey.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageRecordKey.java @@ -24,6 +24,7 @@ public enum ApiUsageRecordKey { STORAGE_DP_COUNT(ApiFeature.DB, "storageDataPointsCount", "storageDataPointsLimit", "data point"), RE_EXEC_COUNT(ApiFeature.RE, "ruleEngineExecutionCount", "ruleEngineExecutionLimit", "Rule Engine execution"), JS_EXEC_COUNT(ApiFeature.JS, "jsExecutionCount", "jsExecutionLimit", "JavaScript execution"), + TBEL_EXEC_COUNT(ApiFeature.TBEL, "tbelExecutionCount", "tbelExecutionLimit", "Tbel execution"), EMAIL_EXEC_COUNT(ApiFeature.EMAIL, "emailCount", "emailLimit", "email message"), SMS_EXEC_COUNT(ApiFeature.SMS, "smsCount", "smsLimit", "SMS message"), CREATED_ALARMS_COUNT(ApiFeature.ALARM, "createdAlarmsCount", "createdAlarmsLimit", "alarm"), @@ -31,6 +32,7 @@ public enum ApiUsageRecordKey { INACTIVE_DEVICES("inactiveDevicesCount"); private static final ApiUsageRecordKey[] JS_RECORD_KEYS = {JS_EXEC_COUNT}; + private static final ApiUsageRecordKey[] TBEL_RECORD_KEYS = {TBEL_EXEC_COUNT}; private static final ApiUsageRecordKey[] RE_RECORD_KEYS = {RE_EXEC_COUNT}; private static final ApiUsageRecordKey[] DB_RECORD_KEYS = {STORAGE_DP_COUNT}; private static final ApiUsageRecordKey[] TRANSPORT_RECORD_KEYS = {TRANSPORT_MSG_COUNT, TRANSPORT_DP_COUNT}; @@ -75,6 +77,8 @@ public enum ApiUsageRecordKey { return RE_RECORD_KEYS; case JS: return JS_RECORD_KEYS; + case TBEL: + return TBEL_RECORD_KEYS; case EMAIL: return EMAIL_RECORD_KEYS; case SMS: diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageState.java b/common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageState.java index d28a8b0908..e6f3192194 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageState.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageState.java @@ -37,6 +37,7 @@ public class ApiUsageState extends BaseData implements HasTenan private ApiUsageStateValue dbStorageState; private ApiUsageStateValue reExecState; private ApiUsageStateValue jsExecState; + private ApiUsageStateValue tbelExecState; private ApiUsageStateValue emailExecState; private ApiUsageStateValue smsExecState; private ApiUsageStateValue alarmExecState; @@ -57,6 +58,7 @@ public class ApiUsageState extends BaseData implements HasTenan this.dbStorageState = ur.getDbStorageState(); this.reExecState = ur.getReExecState(); this.jsExecState = ur.getJsExecState(); + this.tbelExecState = ur.getTbelExecState(); this.emailExecState = ur.getEmailExecState(); this.smsExecState = ur.getSmsExecState(); this.alarmExecState = ur.getAlarmExecState(); @@ -78,6 +80,10 @@ public class ApiUsageState extends BaseData implements HasTenan return !ApiUsageStateValue.DISABLED.equals(jsExecState); } + public boolean isTbelExecEnabled() { + return !ApiUsageStateValue.DISABLED.equals(tbelExecState); + } + public boolean isEmailSendEnabled(){ return !ApiUsageStateValue.DISABLED.equals(emailExecState); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java index ff0032f435..e06409d177 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java @@ -44,4 +44,6 @@ public class CacheConstants { public static final String USER_SETTINGS_CACHE = "userSettings"; public static final String DASHBOARD_TITLES_CACHE = "dashboardTitles"; public static final String ENTITY_COUNT_CACHE = "entityCount"; + public static final String RESOURCE_INFO_CACHE = "resourceInfo"; + public static final String ALARM_TYPES_CACHE = "alarmTypes"; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java index 636ab0de38..4b4b9e519d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java @@ -35,9 +35,9 @@ public class Customer extends ContactBased implements HasTenantId, E @NoXss @Length(fieldName = "title") - @ApiModelProperty(position = 3, value = "Title of the customer", example = "Company A") + @ApiModelProperty(position = 3, required = true, value = "Title of the customer", example = "Company A") private String title; - @ApiModelProperty(position = 5, required = true, value = "JSON object with Tenant Id") + @ApiModelProperty(position = 5, value = "JSON object with Tenant Id") private TenantId tenantId; @Getter @Setter @@ -89,43 +89,43 @@ public class Customer extends ContactBased implements HasTenantId, E return super.getCreatedTime(); } - @ApiModelProperty(position = 6, required = true, value = "Country", example = "US") + @ApiModelProperty(position = 6, value = "Country", example = "US") @Override public String getCountry() { return super.getCountry(); } - @ApiModelProperty(position = 7, required = true, value = "State", example = "NY") + @ApiModelProperty(position = 7, value = "State", example = "NY") @Override public String getState() { return super.getState(); } - @ApiModelProperty(position = 8, required = true, value = "City", example = "New York") + @ApiModelProperty(position = 8, value = "City", example = "New York") @Override public String getCity() { return super.getCity(); } - @ApiModelProperty(position = 9, required = true, value = "Address Line 1", example = "42 Broadway Suite 12-400") + @ApiModelProperty(position = 9, value = "Address Line 1", example = "42 Broadway Suite 12-400") @Override public String getAddress() { return super.getAddress(); } - @ApiModelProperty(position = 10, required = true, value = "Address Line 2", example = "") + @ApiModelProperty(position = 10, value = "Address Line 2", example = "") @Override public String getAddress2() { return super.getAddress2(); } - @ApiModelProperty(position = 11, required = true, value = "Zip code", example = "10004") + @ApiModelProperty(position = 11, value = "Zip code", example = "10004") @Override public String getZip() { return super.getZip(); } - @ApiModelProperty(position = 12, required = true, value = "Phone number", example = "+1(415)777-7777") + @ApiModelProperty(position = 12, value = "Phone number", example = "+1(415)777-7777") @Override public String getPhone() { return super.getPhone(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java index a32c16d88a..c3a06a0e6a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java @@ -30,13 +30,14 @@ import java.util.Objects; import java.util.Set; @ApiModel -public class DashboardInfo extends BaseData implements HasName, HasTenantId, HasTitle { +public class DashboardInfo extends BaseData implements HasName, HasTenantId, HasTitle, HasImage { + + private static final long serialVersionUID = -9080404114760433799L; private TenantId tenantId; @NoXss @Length(fieldName = "title") private String title; - @Length(fieldName = "image", max = 1000000) private String image; @Valid private Set assignedCustomers; @@ -85,7 +86,7 @@ public class DashboardInfo extends BaseData implements HasName, Has this.tenantId = tenantId; } - @ApiModelProperty(position = 4, value = "Title of the dashboard.") + @ApiModelProperty(position = 4, required = true, value = "Title of the dashboard.") public String getTitle() { return title; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java index 0f0cf0467f..912049cb8b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java @@ -43,11 +43,14 @@ public class DataConstants { public static final String RETRIES = "retries"; public static final String EDGE_ID = "edgeId"; public static final String DEVICE_ID = "deviceId"; + public static final String GATEWAY_PARAMETER = "gateway"; public static final String COAP_TRANSPORT_NAME = "COAP"; public static final String LWM2M_TRANSPORT_NAME = "LWM2M"; public static final String MQTT_TRANSPORT_NAME = "MQTT"; public static final String HTTP_TRANSPORT_NAME = "HTTP"; public static final String SNMP_TRANSPORT_NAME = "SNMP"; + public static final String MAXIMUM_NUMBER_OF_DEVICES_REACHED = "Maximum number of devices reached!"; + public static final String TB_IMAGE_PREFIX = "tb-image;"; public static final String[] allScopes() { @@ -109,6 +112,7 @@ public class DataConstants { public static final String PROVISION_KEY = "provisionDeviceKey"; public static final String PROVISION_SECRET = "provisionDeviceSecret"; + public static final String DEFAULT_DEVICE_TYPE = "default"; public static final String DEVICE_NAME = "deviceName"; public static final String DEVICE_TYPE = "deviceType"; public static final String CERT_PUB_KEY = "x509CertPubKey"; @@ -120,9 +124,12 @@ public class DataConstants { public static final String PASSWORD = "password"; public static final String EDGE_MSG_SOURCE = "edge"; public static final String MSG_SOURCE_KEY = "source"; + public static final String EDGE_VERSION_ATTR_KEY = "edgeVersion"; public static final String LAST_CONNECTED_GATEWAY = "lastConnectedGateway"; + public static final String MQTT_TOPIC = "mqttTopic"; + public static final String MAIN_QUEUE_NAME = "Main"; public static final String MAIN_QUEUE_TOPIC = "tb_rule_engine.main"; public static final String HP_QUEUE_NAME = "HighPriority"; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java index 45e4244bcb..f16a124ef4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java @@ -146,7 +146,7 @@ public class Device extends BaseDataWithAdditionalInfo implements HasL this.name = name; } - @ApiModelProperty(position = 6, required = true, value = "Device Profile Name", example = "Temperature Sensor") + @ApiModelProperty(position = 6, value = "Device Profile Name", example = "Temperature Sensor") public String getType() { return type; } @@ -155,7 +155,7 @@ public class Device extends BaseDataWithAdditionalInfo implements HasL this.type = type; } - @ApiModelProperty(position = 7, required = true, value = "Label that may be used in widgets", example = "Room 234 Sensor") + @ApiModelProperty(position = 7, value = "Label that may be used in widgets", example = "Room 234 Sensor") public String getLabel() { return label; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java index 196edadf9c..be4426a1d4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java @@ -41,7 +41,7 @@ import java.io.IOException; @ToString(exclude = {"image", "profileDataBytes"}) @EqualsAndHashCode(callSuper = true) @Slf4j -public class DeviceProfile extends BaseData implements HasName, HasTenantId, HasOtaPackage, HasRuleEngineProfile, ExportableEntity { +public class DeviceProfile extends BaseData implements HasName, HasTenantId, HasOtaPackage, HasRuleEngineProfile, ExportableEntity, HasImage { private static final long serialVersionUID = 6998485460273302018L; @@ -54,7 +54,6 @@ public class DeviceProfile extends BaseData implements HasName, @NoXss @ApiModelProperty(position = 11, value = "Device Profile description. ") private String description; - @Length(fieldName = "image", max = 1000000) @ApiModelProperty(position = 12, value = "Either URL or Base64 data of the icon. Used in the mobile application to visualize set of device profiles in the grid view. ") private String image; private boolean isDefault; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUpgradeInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUpgradeInfo.java new file mode 100644 index 0000000000..cadd589b49 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUpgradeInfo.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class EdgeUpgradeInfo { + private boolean requiresUpdateDb; + private String nextEdgeVersion; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUpgradeMessage.java b/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUpgradeMessage.java new file mode 100644 index 0000000000..bb17479607 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUpgradeMessage.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Map; + +@Data +@ApiModel +public class EdgeUpgradeMessage implements Serializable { + + private static final long serialVersionUID = 2872965507642822989L; + + @ApiModelProperty(position = 1, value = "Mapping for upgrade versions and upgrade strategy (next ver).") + private final Map edgeVersions; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java b/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java index b1c5f39611..f74ea6fd07 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java @@ -25,60 +25,35 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import java.util.EnumMap; import java.util.concurrent.ThreadLocalRandom; @Slf4j public final class EdgeUtils { - private static final int STACK_TRACE_LIMIT = 10; - - private EdgeUtils() { - } + private static final EnumMap entityTypeEdgeEventTypeEnumMap; - public static EdgeEventType getEdgeEventTypeByEntityType(EntityType entityType) { - switch (entityType) { - case EDGE: - return EdgeEventType.EDGE; - case DEVICE: - return EdgeEventType.DEVICE; - case DEVICE_PROFILE: - return EdgeEventType.DEVICE_PROFILE; - case ASSET: - return EdgeEventType.ASSET; - case ASSET_PROFILE: - return EdgeEventType.ASSET_PROFILE; - case ENTITY_VIEW: - return EdgeEventType.ENTITY_VIEW; - case DASHBOARD: - return EdgeEventType.DASHBOARD; - case USER: - return EdgeEventType.USER; - case RULE_CHAIN: - return EdgeEventType.RULE_CHAIN; - case ALARM: - return EdgeEventType.ALARM; - case TENANT: - return EdgeEventType.TENANT; - case CUSTOMER: - return EdgeEventType.CUSTOMER; - case WIDGETS_BUNDLE: - return EdgeEventType.WIDGETS_BUNDLE; - case WIDGET_TYPE: - return EdgeEventType.WIDGET_TYPE; - case OTA_PACKAGE: - return EdgeEventType.OTA_PACKAGE; - case QUEUE: - return EdgeEventType.QUEUE; - default: - log.warn("Unsupported entity type [{}]", entityType); - return null; + static { + entityTypeEdgeEventTypeEnumMap = new EnumMap<>(EntityType.class); + for (EdgeEventType edgeEventType : EdgeEventType.values()) { + if (edgeEventType.getEntityType() != null) { + entityTypeEdgeEventTypeEnumMap.put(edgeEventType.getEntityType(), edgeEventType); + } } } + private static final int STACK_TRACE_LIMIT = 10; + + private EdgeUtils() {} + public static int nextPositiveInt() { return ThreadLocalRandom.current().nextInt(0, Integer.MAX_VALUE); } + public static EdgeEventType getEdgeEventTypeByEntityType(EntityType entityType) { + return entityTypeEdgeEventTypeEnumMap.get(entityType); + } + public static EdgeEvent constructEdgeEvent(TenantId tenantId, EdgeId edgeId, EdgeEventType type, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityFieldsData.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityFieldsData.java index c88a6b970f..db069d8d61 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityFieldsData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityFieldsData.java @@ -68,20 +68,22 @@ public class EntityFieldsData { break; } } - if (current != null) { - if(current.isNull() && ignoreNullStrings){ + if (current == null) { + return null; + } + if (current.isNull() && ignoreNullStrings) { + return null; + } + if (current.isValueNode()) { + String textValue = current.asText(); + if (StringUtils.isEmpty(textValue) && ignoreNullStrings) { return null; } - if (current.isValueNode()) { - return current.asText(); - } else { - try { - return mapper.writeValueAsString(current); - } catch (JsonProcessingException e) { - return null; - } - } - } else { + return textValue; + } + try { + return mapper.writeValueAsString(current); + } catch (JsonProcessingException e) { return null; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntitySubtype.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntitySubtype.java index a776635c61..93454f6656 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntitySubtype.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntitySubtype.java @@ -15,71 +15,19 @@ */ package org.thingsboard.server.common.data; +import lombok.Data; import org.thingsboard.server.common.data.id.TenantId; -public class EntitySubtype { +import java.io.Serializable; - private static final long serialVersionUID = 8057240243059922101L; - - private TenantId tenantId; - private EntityType entityType; - private String type; - - public EntitySubtype() { - super(); - } - - public EntitySubtype(TenantId tenantId, EntityType entityType, String type) { - this.tenantId = tenantId; - this.entityType = entityType; - this.type = type; - } - - public TenantId getTenantId() { - return tenantId; - } - - public void setTenantId(TenantId tenantId) { - this.tenantId = tenantId; - } - - public EntityType getEntityType() { - return entityType; - } - - public void setEntityType(EntityType entityType) { - this.entityType = entityType; - } +@Data +public class EntitySubtype implements Serializable { - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - EntitySubtype that = (EntitySubtype) o; - - if (tenantId != null ? !tenantId.equals(that.tenantId) : that.tenantId != null) return false; - if (entityType != that.entityType) return false; - return type != null ? type.equals(that.type) : that.type == null; + private static final long serialVersionUID = 8057240243059922101L; - } - - @Override - public int hashCode() { - int result = tenantId != null ? tenantId.hashCode() : 0; - result = 31 * result + (entityType != null ? entityType.hashCode() : 0); - result = 31 * result + (type != null ? type.hashCode() : 0); - return result; - } + private final TenantId tenantId; + private final EntityType entityType; + private final String type; @Override public String toString() { @@ -90,5 +38,4 @@ public class EntitySubtype { sb.append('}'); return sb.toString(); } - } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java index 572dc16c0b..ce87edf43a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java @@ -18,36 +18,57 @@ package org.thingsboard.server.common.data; import lombok.Getter; import org.apache.commons.lang3.StringUtils; +import java.util.EnumSet; +import java.util.List; +import java.util.stream.Collectors; + /** * @author Andrew Shvayka */ public enum EntityType { - TENANT, - CUSTOMER, - USER, - DASHBOARD, - ASSET, - DEVICE, - ALARM, - RULE_CHAIN, - RULE_NODE, - ENTITY_VIEW, - WIDGETS_BUNDLE, - WIDGET_TYPE, - TENANT_PROFILE, - DEVICE_PROFILE, - ASSET_PROFILE, - API_USAGE_STATE, - TB_RESOURCE, - OTA_PACKAGE, - EDGE, - RPC, - QUEUE, - NOTIFICATION_TARGET, - NOTIFICATION_TEMPLATE, - NOTIFICATION_REQUEST, - NOTIFICATION, - NOTIFICATION_RULE; + TENANT(1), + CUSTOMER(2), + USER(3), + DASHBOARD(4), + ASSET(5), + DEVICE(6), + ALARM (7), + RULE_CHAIN (11), + RULE_NODE (12), + + ENTITY_VIEW (15) { + // backward compatibility for TbOriginatorTypeSwitchNode to return correct rule node connection. + @Override + public String getNormalName () { + return "Entity View"; + } + }, + WIDGETS_BUNDLE (16), + WIDGET_TYPE (17), + TENANT_PROFILE (20), + DEVICE_PROFILE (21), + ASSET_PROFILE (22), + API_USAGE_STATE (23), + TB_RESOURCE (24), + OTA_PACKAGE (25), + EDGE (26), + RPC (27), + QUEUE (28), + NOTIFICATION_TARGET (29), + NOTIFICATION_TEMPLATE (30), + NOTIFICATION_REQUEST (31), + NOTIFICATION (32), + NOTIFICATION_RULE (33); + + @Getter + private final int protoNumber; // Corresponds to EntityTypeProto + + private EntityType(int protoNumber) { + this.protoNumber = protoNumber; + } + + public static final List NORMAL_NAMES = EnumSet.allOf(EntityType.class).stream() + .map(EntityType::getNormalName).collect(Collectors.toUnmodifiableList()); @Getter private final String normalName = StringUtils.capitalize(StringUtils.removeStart(name(), "TB_") diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java index c0e44baeb3..99fedb9c93 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java @@ -40,7 +40,7 @@ public class EntityView extends BaseDataWithAdditionalInfo private static final long serialVersionUID = 5582010124562018986L; - @ApiModelProperty(position = 7, required = true, value = "JSON object with the referenced Entity Id (Device or Asset).") + @ApiModelProperty(position = 7, value = "JSON object with the referenced Entity Id (Device or Asset).") private EntityId entityId; private TenantId tenantId; private CustomerId customerId; @@ -52,7 +52,7 @@ public class EntityView extends BaseDataWithAdditionalInfo @Length(fieldName = "type") @ApiModelProperty(position = 6, required = true, value = "Device Profile Name", example = "Temperature Sensor") private String type; - @ApiModelProperty(position = 8, required = true, value = "Set of telemetry and attribute keys to expose via Entity View.") + @ApiModelProperty(position = 8, value = "Set of telemetry and attribute keys to expose via Entity View.") private TelemetryEntityView keys; @ApiModelProperty(position = 9, value = "Represents the start time of the interval that is used to limit access to target device telemetry. Customer will not be able to see entity telemetry that is outside the specified interval;") private long startTimeMs; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/FstStatsService.java b/common/data/src/main/java/org/thingsboard/server/common/data/FstStatsService.java new file mode 100644 index 0000000000..42be4b3b68 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/FstStatsService.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data; + +public interface FstStatsService { + + void incrementEncode(Class clazz); + + void incrementDecode(Class clazz); + + void recordEncodeTime(Class clazz, long startTime); + + void recordDecodeTime(Class clazz, long startTime); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/HasImage.java b/common/data/src/main/java/org/thingsboard/server/common/data/HasImage.java new file mode 100644 index 0000000000..38b01e1005 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/HasImage.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data; + +public interface HasImage extends HasTenantId, HasName { + + String getImage(); + + void setImage(String image); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ImageDescriptor.java b/common/data/src/main/java/org/thingsboard/server/common/data/ImageDescriptor.java new file mode 100644 index 0000000000..367531565f --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ImageDescriptor.java @@ -0,0 +1,30 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ImageDescriptor { + private String mediaType; + private int width; + private int height; + private long size; + private String etag; + private ImageDescriptor previewDescriptor; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ImageExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/ImageExportData.java new file mode 100644 index 0000000000..2e6d387dff --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ImageExportData.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data; + +import io.swagger.annotations.ApiModel; +import lombok.Builder; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +@ApiModel +@Slf4j +@Data +@Builder +public class ImageExportData { + + private final String mediaType; + private final String fileName; + private final String title; + private final String resourceKey; + private final boolean isPublic; + private final String publicResourceKey; + private final String data; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ResourceType.java b/common/data/src/main/java/org/thingsboard/server/common/data/ResourceType.java index 3c2b9a4bc0..22af597627 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ResourceType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ResourceType.java @@ -16,20 +16,21 @@ package org.thingsboard.server.common.data; import lombok.Getter; +import lombok.RequiredArgsConstructor; +@RequiredArgsConstructor public enum ResourceType { - LWM2M_MODEL("application/xml", false), - JKS("application/x-java-keystore", false), - PKCS_12("application/x-pkcs12", false), - JS_MODULE("application/javascript", true); + LWM2M_MODEL("application/xml", false, false), + JKS("application/x-java-keystore", false, false), + PKCS_12("application/x-pkcs12", false, false), + JS_MODULE("application/javascript", true, true), + IMAGE(null, true, true); @Getter private final String mediaType; @Getter private final boolean customerAccess; + @Getter + private final boolean updatable; - ResourceType(String mediaType, boolean customerAccess) { - this.mediaType = mediaType; - this.customerAccess = customerAccess; - } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/SaveDeviceWithCredentialsRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/SaveDeviceWithCredentialsRequest.java index f8c74a5155..836a5bff93 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/SaveDeviceWithCredentialsRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/SaveDeviceWithCredentialsRequest.java @@ -20,13 +20,17 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.thingsboard.server.common.data.security.DeviceCredentials; +import javax.validation.constraints.NotNull; + @ApiModel @Data public class SaveDeviceWithCredentialsRequest { @ApiModelProperty(position = 1, value = "The JSON with device entity.", required = true) + @NotNull private final Device device; @ApiModelProperty(position = 2, value = "The JSON with credentials entity.", required = true) + @NotNull private final DeviceCredentials credentials; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java b/common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java index a7671f4327..5a787f24a8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/StringUtils.java @@ -20,6 +20,7 @@ import org.apache.commons.lang3.RandomStringUtils; import java.security.SecureRandom; import java.util.Base64; +import java.util.function.Function; import static org.apache.commons.lang3.StringUtils.repeat; @@ -163,6 +164,19 @@ public class StringUtils { return false; } + public static boolean equalsAnyIgnoreCase(String string, String... otherStrings) { + for (String otherString : otherStrings) { + if (equalsIgnoreCase(string, otherString)) { + return true; + } + } + return false; + } + + public static String substringBeforeLast(String str, String separator) { + return org.apache.commons.lang3.StringUtils.substringBeforeLast(str, separator); + } + public static String substringAfterLast(String str, String sep) { return org.apache.commons.lang3.StringUtils.substringAfterLast(str, sep); } @@ -181,6 +195,13 @@ public class StringUtils { return org.apache.commons.lang3.StringUtils.contains(seq, searchSeq); } + /** + * Use this to prevent org.postgresql.util.PSQLException: ERROR: invalid byte sequence for encoding "UTF8": 0x00 + **/ + public static boolean contains0x00(final String s) { + return s != null && s.contains("\u0000"); + } + public static String randomNumeric(int length) { return RandomStringUtils.randomNumeric(length); } @@ -212,4 +233,16 @@ public class StringUtils { return generateSafeToken(DEFAULT_TOKEN_LENGTH); } + public static String truncate(String string, int maxLength) { + return truncate(string, maxLength, n -> "...[truncated " + n + " symbols]"); + } + + public static String truncate(String string, int maxLength, Function truncationMarkerFunc) { + if (string == null || maxLength <= 0 || string.length() <= maxLength) { + return string; + } + int truncatedSymbols = string.length() - maxLength; + return string.substring(0, maxLength) + truncationMarkerFunc.apply(truncatedSymbols); + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/SystemParams.java b/common/data/src/main/java/org/thingsboard/server/common/data/SystemParams.java index 1e7c0281a4..da092c6526 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/SystemParams.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/SystemParams.java @@ -30,4 +30,5 @@ public class SystemParams { boolean persistDeviceStateToTelemetry; JsonNode userSettings; long maxDatapointsLimit; + long maxResourceSize; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbImageDeleteResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbImageDeleteResult.java new file mode 100644 index 0000000000..9782b2b58f --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbImageDeleteResult.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.id.HasId; + +import java.util.List; +import java.util.Map; + +@Data +@Builder +public class TbImageDeleteResult { + + private boolean success; + private Map>> references; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java index 8b82db196a..2324eae1a7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java @@ -15,13 +15,16 @@ */ package org.thingsboard.server.common.data; +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonSetter; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.id.TbResourceId; -import org.thingsboard.server.common.data.validation.Length; -import org.thingsboard.server.common.data.validation.NoXss; + +import java.util.Base64; +import java.util.Optional; @Slf4j @Data @@ -30,13 +33,9 @@ public class TbResource extends TbResourceInfo { private static final long serialVersionUID = 7379609705527272306L; - @NoXss - @Length(fieldName = "file name") - @ApiModelProperty(position = 8, value = "Resource file name.", example = "19.xml", accessMode = ApiModelProperty.AccessMode.READ_ONLY) - private String fileName; + private byte[] data; - @ApiModelProperty(position = 9, value = "Resource data.", example = "77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCEtLQpGSUxFIElORk9STUFUSU9OCgpPTUEgUGVybWFuZW50IERvY3VtZW50CiAgIEZpbGU6IE9NQS1TVVAtTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci1WMV8wXzEtMjAxOTAyMjEtQQogICBUeXBlOiB4bWwKClB1YmxpYyBSZWFjaGFibGUgSW5mb3JtYXRpb24KICAgUGF0aDogaHR0cDovL3d3dy5vcGVubW9iaWxlYWxsaWFuY2Uub3JnL3RlY2gvcHJvZmlsZXMKICAgTmFtZTogTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci12MV8wXzEueG1sCgpOT1JNQVRJVkUgSU5GT1JNQVRJT04KCiAgSW5mb3JtYXRpb24gYWJvdXQgdGhpcyBmaWxlIGNhbiBiZSBmb3VuZCBpbiB0aGUgbGF0ZXN0IHJldmlzaW9uIG9mCgogIE9NQS1UUy1MV00yTV9CaW5hcnlBcHBEYXRhQ29udGFpbmVyLVYxXzBfMQoKICBUaGlzIGlzIGF2YWlsYWJsZSBhdCBodHRwOi8vd3d3Lm9wZW5tb2JpbGVhbGxpYW5jZS5vcmcvCgogIFNlbmQgY29tbWVudHMgdG8gaHR0cHM6Ly9naXRodWIuY29tL09wZW5Nb2JpbGVBbGxpYW5jZS9PTUFfTHdNMk1fZm9yX0RldmVsb3BlcnMvaXNzdWVzCgpDSEFOR0UgSElTVE9SWQoKMTUwNjIwMTggU3RhdHVzIGNoYW5nZWQgdG8gQXBwcm92ZWQgYnkgRE0sIERvYyBSZWYgIyBPTUEtRE0mU0UtMjAxOC0wMDYxLUlOUF9MV00yTV9BUFBEQVRBX1YxXzBfRVJQX2Zvcl9maW5hbF9BcHByb3ZhbAoyMTAyMjAxOSBTdGF0dXMgY2hhbmdlZCB0byBBcHByb3ZlZCBieSBJUFNPLCBEb2MgUmVmICMgT01BLUlQU08tMjAxOS0wMDI1LUlOUF9Md00yTV9PYmplY3RfQXBwX0RhdGFfQ29udGFpbmVyXzFfMF8xX2Zvcl9GaW5hbF9BcHByb3ZhbAoKTEVHQUwgRElTQ0xBSU1FUgoKQ29weXJpZ2h0IDIwMTkgT3BlbiBNb2JpbGUgQWxsaWFuY2UuCgpSZWRpc3RyaWJ1dGlvbiBhbmQgdXNlIGluIHNvdXJjZSBhbmQgYmluYXJ5IGZvcm1zLCB3aXRoIG9yIHdpdGhvdXQKbW9kaWZpY2F0aW9uLCBhcmUgcGVybWl0dGVkIHByb3ZpZGVkIHRoYXQgdGhlIGZvbGxvd2luZyBjb25kaXRpb25zCmFyZSBtZXQ6CgoxLiBSZWRpc3RyaWJ1dGlvbnMgb2Ygc291cmNlIGNvZGUgbXVzdCByZXRhaW4gdGhlIGFib3ZlIGNvcHlyaWdodApub3RpY2UsIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIuCjIuIFJlZGlzdHJpYnV0aW9ucyBpbiBiaW5hcnkgZm9ybSBtdXN0IHJlcHJvZHVjZSB0aGUgYWJvdmUgY29weXJpZ2h0Cm5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lciBpbiB0aGUKZG9jdW1lbnRhdGlvbiBhbmQvb3Igb3RoZXIgbWF0ZXJpYWxzIHByb3ZpZGVkIHdpdGggdGhlIGRpc3RyaWJ1dGlvbi4KMy4gTmVpdGhlciB0aGUgbmFtZSBvZiB0aGUgY29weXJpZ2h0IGhvbGRlciBub3IgdGhlIG5hbWVzIG9mIGl0cwpjb250cmlidXRvcnMgbWF5IGJlIHVzZWQgdG8gZW5kb3JzZSBvciBwcm9tb3RlIHByb2R1Y3RzIGRlcml2ZWQKZnJvbSB0aGlzIHNvZnR3YXJlIHdpdGhvdXQgc3BlY2lmaWMgcHJpb3Igd3JpdHRlbiBwZXJtaXNzaW9uLgoKVEhJUyBTT0ZUV0FSRSBJUyBQUk9WSURFRCBCWSBUSEUgQ09QWVJJR0hUIEhPTERFUlMgQU5EIENPTlRSSUJVVE9SUwoiQVMgSVMiIEFORCBBTlkgRVhQUkVTUyBPUiBJTVBMSUVEIFdBUlJBTlRJRVMsIElOQ0xVRElORywgQlVUIE5PVApMSU1JVEVEIFRPLCBUSEUgSU1QTElFRCBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSBBTkQgRklUTkVTUwpGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQVJFIERJU0NMQUlNRUQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRQpDT1BZUklHSFQgSE9MREVSIE9SIENPTlRSSUJVVE9SUyBCRSBMSUFCTEUgRk9SIEFOWSBESVJFQ1QsIElORElSRUNULApJTkNJREVOVEFMLCBTUEVDSUFMLCBFWEVNUExBUlksIE9SIENPTlNFUVVFTlRJQUwgREFNQUdFUyAoSU5DTFVESU5HLApCVVQgTk9UIExJTUlURUQgVE8sIFBST0NVUkVNRU5UIE9GIFNVQlNUSVRVVEUgR09PRFMgT1IgU0VSVklDRVM7CkxPU1MgT0YgVVNFLCBEQVRBLCBPUiBQUk9GSVRTOyBPUiBCVVNJTkVTUyBJTlRFUlJVUFRJT04pIEhPV0VWRVIKQ0FVU0VEIEFORCBPTiBBTlkgVEhFT1JZIE9GIExJQUJJTElUWSwgV0hFVEhFUiBJTiBDT05UUkFDVCwgU1RSSUNUCkxJQUJJTElUWSwgT1IgVE9SVCAoSU5DTFVESU5HIE5FR0xJR0VOQ0UgT1IgT1RIRVJXSVNFKSBBUklTSU5HIElOCkFOWSBXQVkgT1VUIE9GIFRIRSBVU0UgT0YgVEhJUyBTT0ZUV0FSRSwgRVZFTiBJRiBBRFZJU0VEIE9GIFRIRQpQT1NTSUJJTElUWSBPRiBTVUNIIERBTUFHRS4KClRoZSBhYm92ZSBsaWNlbnNlIGlzIHVzZWQgYXMgYSBsaWNlbnNlIHVuZGVyIGNvcHlyaWdodCBvbmx5LiBQbGVhc2UKcmVmZXJlbmNlIHRoZSBPTUEgSVBSIFBvbGljeSBmb3IgcGF0ZW50IGxpY2Vuc2luZyB0ZXJtczoKaHR0cHM6Ly93d3cub21hc3BlY3dvcmtzLm9yZy9hYm91dC9pbnRlbGxlY3R1YWwtcHJvcGVydHktcmlnaHRzLwoKLS0+CjxMV00yTSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6bm9OYW1lc3BhY2VTY2hlbWFMb2NhdGlvbj0iaHR0cDovL29wZW5tb2JpbGVhbGxpYW5jZS5vcmcvdGVjaC9wcm9maWxlcy9MV00yTS54c2QiPgoJPE9iamVjdCBPYmplY3RUeXBlPSJNT0RlZmluaXRpb24iPgoJCTxOYW1lPkJpbmFyeUFwcERhdGFDb250YWluZXI8L05hbWU+CgkJPERlc2NyaXB0aW9uMT48IVtDREFUQVtUaGlzIEx3TTJNIE9iamVjdHMgcHJvdmlkZXMgdGhlIGFwcGxpY2F0aW9uIHNlcnZpY2UgZGF0YSByZWxhdGVkIHRvIGEgTHdNMk0gU2VydmVyLCBlZy4gV2F0ZXIgbWV0ZXIgZGF0YS4gClRoZXJlIGFyZSBzZXZlcmFsIG1ldGhvZHMgdG8gY3JlYXRlIGluc3RhbmNlIHRvIGluZGljYXRlIHRoZSBtZXNzYWdlIGRpcmVjdGlvbiBiYXNlZCBvbiB0aGUgbmVnb3RpYXRpb24gYmV0d2VlbiBBcHBsaWNhdGlvbiBhbmQgTHdNMk0uIFRoZSBDbGllbnQgYW5kIFNlcnZlciBzaG91bGQgbmVnb3RpYXRlIHRoZSBpbnN0YW5jZShzKSB1c2VkIHRvIGV4Y2hhbmdlIHRoZSBkYXRhLiBGb3IgZXhhbXBsZToKIC0gVXNpbmcgYSBzaW5nbGUgaW5zdGFuY2UgZm9yIGJvdGggZGlyZWN0aW9ucyBjb21tdW5pY2F0aW9uLCBmcm9tIENsaWVudCB0byBTZXJ2ZXIgYW5kIGZyb20gU2VydmVyIHRvIENsaWVudC4KIC0gVXNpbmcgYW4gaW5zdGFuY2UgZm9yIGNvbW11bmljYXRpb24gZnJvbSBDbGllbnQgdG8gU2VydmVyIGFuZCBhbm90aGVyIG9uZSBmb3IgY29tbXVuaWNhdGlvbiBmcm9tIFNlcnZlciB0byBDbGllbnQKIC0gVXNpbmcgc2V2ZXJhbCBpbnN0YW5jZXMKXV0+PC9EZXNjcmlwdGlvbjE+CgkJPE9iamVjdElEPjE5PC9PYmplY3RJRD4KCQk8T2JqZWN0VVJOPnVybjpvbWE6bHdtMm06b21hOjE5PC9PYmplY3RVUk4+CgkJPExXTTJNVmVyc2lvbj4xLjA8L0xXTTJNVmVyc2lvbj4KCQk8T2JqZWN0VmVyc2lvbj4xLjA8L09iamVjdFZlcnNpb24+CgkJPE11bHRpcGxlSW5zdGFuY2VzPk11bHRpcGxlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJPFJlc291cmNlcz4KCQkJPEl0ZW0gSUQ9IjAiPjxOYW1lPkRhdGE8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5NdWx0aXBsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk1hbmRhdG9yeTwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+T3BhcXVlPC9UeXBlPgoJCQkJPFJhbmdlRW51bWVyYXRpb24gLz4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgYXBwbGljYXRpb24gZGF0YSBjb250ZW50Ll1dPjwvRGVzY3JpcHRpb24+CgkJCTwvSXRlbT4KCQkJPEl0ZW0gSUQ9IjEiPjxOYW1lPkRhdGEgUHJpb3JpdHk8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5TaW5nbGU8L011bHRpcGxlSW5zdGFuY2VzPgoJCQkJPE1hbmRhdG9yeT5PcHRpb25hbDwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+SW50ZWdlcjwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjEgYnl0ZXM8L1JhbmdlRW51bWVyYXRpb24+CgkJCQk8VW5pdHMgLz4KCQkJCTxEZXNjcmlwdGlvbj48IVtDREFUQVtJbmRpY2F0ZXMgdGhlIEFwcGxpY2F0aW9uIGRhdGEgcHJpb3JpdHk6CjA6SW1tZWRpYXRlCjE6QmVzdEVmZm9ydAoyOkxhdGVzdAozLTEwMDogUmVzZXJ2ZWQgZm9yIGZ1dHVyZSB1c2UuCjEwMS0yNTQ6IFByb3ByaWV0YXJ5IG1vZGUuXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iMiI+PE5hbWU+RGF0YSBDcmVhdGlvbiBUaW1lPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlRpbWU8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbiAvPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBEYXRhIGluc3RhbmNlIGNyZWF0aW9uIHRpbWVzdGFtcC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSIzIj48TmFtZT5EYXRhIERlc2NyaXB0aW9uPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlN0cmluZzwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjMyIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkYXRhIGRlc2NyaXB0aW9uLgplLmcuICJtZXRlciByZWFkaW5nIi5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSI0Ij48TmFtZT5EYXRhIEZvcm1hdDwvTmFtZT4KCQkJCTxPcGVyYXRpb25zPlJXPC9PcGVyYXRpb25zPgoJCQkJPE11bHRpcGxlSW5zdGFuY2VzPlNpbmdsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJCQk8VHlwZT5TdHJpbmc8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4zMiBieXRlczwvUmFuZ2VFbnVtZXJhdGlvbj4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgZm9ybWF0IG9mIHRoZSBBcHBsaWNhdGlvbiBEYXRhLgplLmcuIFlHLU1ldGVyLVdhdGVyLVJlYWRpbmcKVVRGOC1zdHJpbmcKXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iNSI+PE5hbWU+QXBwIElEPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPkludGVnZXI8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4yIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkZXN0aW5hdGlvbiBBcHBsaWNhdGlvbiBJRC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+PC9SZXNvdXJjZXM+CgkJPERlc2NyaXB0aW9uMj48IVtDREFUQVtdXT48L0Rlc2NyaXB0aW9uMj4KCTwvT2JqZWN0Pgo8L0xXTTJNPgo=", accessMode = ApiModelProperty.AccessMode.READ_ONLY) - private String data; + private byte[] preview; public TbResource() { super(); @@ -52,31 +51,41 @@ public class TbResource extends TbResourceInfo { public TbResource(TbResource resource) { super(resource); - this.data = resource.getData(); + this.data = resource.data; + this.preview = resource.preview; + } + + @ApiModelProperty(position = 12, value = "Resource data.", example = "77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCEtLQpGSUxFIElORk9STUFUSU9OCgpPTUEgUGVybWFuZW50IERvY3VtZW50CiAgIEZpbGU6IE9NQS1TVVAtTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci1WMV8wXzEtMjAxOTAyMjEtQQogICBUeXBlOiB4bWwKClB1YmxpYyBSZWFjaGFibGUgSW5mb3JtYXRpb24KICAgUGF0aDogaHR0cDovL3d3dy5vcGVubW9iaWxlYWxsaWFuY2Uub3JnL3RlY2gvcHJvZmlsZXMKICAgTmFtZTogTHdNMk1fQmluYXJ5QXBwRGF0YUNvbnRhaW5lci12MV8wXzEueG1sCgpOT1JNQVRJVkUgSU5GT1JNQVRJT04KCiAgSW5mb3JtYXRpb24gYWJvdXQgdGhpcyBmaWxlIGNhbiBiZSBmb3VuZCBpbiB0aGUgbGF0ZXN0IHJldmlzaW9uIG9mCgogIE9NQS1UUy1MV00yTV9CaW5hcnlBcHBEYXRhQ29udGFpbmVyLVYxXzBfMQoKICBUaGlzIGlzIGF2YWlsYWJsZSBhdCBodHRwOi8vd3d3Lm9wZW5tb2JpbGVhbGxpYW5jZS5vcmcvCgogIFNlbmQgY29tbWVudHMgdG8gaHR0cHM6Ly9naXRodWIuY29tL09wZW5Nb2JpbGVBbGxpYW5jZS9PTUFfTHdNMk1fZm9yX0RldmVsb3BlcnMvaXNzdWVzCgpDSEFOR0UgSElTVE9SWQoKMTUwNjIwMTggU3RhdHVzIGNoYW5nZWQgdG8gQXBwcm92ZWQgYnkgRE0sIERvYyBSZWYgIyBPTUEtRE0mU0UtMjAxOC0wMDYxLUlOUF9MV00yTV9BUFBEQVRBX1YxXzBfRVJQX2Zvcl9maW5hbF9BcHByb3ZhbAoyMTAyMjAxOSBTdGF0dXMgY2hhbmdlZCB0byBBcHByb3ZlZCBieSBJUFNPLCBEb2MgUmVmICMgT01BLUlQU08tMjAxOS0wMDI1LUlOUF9Md00yTV9PYmplY3RfQXBwX0RhdGFfQ29udGFpbmVyXzFfMF8xX2Zvcl9GaW5hbF9BcHByb3ZhbAoKTEVHQUwgRElTQ0xBSU1FUgoKQ29weXJpZ2h0IDIwMTkgT3BlbiBNb2JpbGUgQWxsaWFuY2UuCgpSZWRpc3RyaWJ1dGlvbiBhbmQgdXNlIGluIHNvdXJjZSBhbmQgYmluYXJ5IGZvcm1zLCB3aXRoIG9yIHdpdGhvdXQKbW9kaWZpY2F0aW9uLCBhcmUgcGVybWl0dGVkIHByb3ZpZGVkIHRoYXQgdGhlIGZvbGxvd2luZyBjb25kaXRpb25zCmFyZSBtZXQ6CgoxLiBSZWRpc3RyaWJ1dGlvbnMgb2Ygc291cmNlIGNvZGUgbXVzdCByZXRhaW4gdGhlIGFib3ZlIGNvcHlyaWdodApub3RpY2UsIHRoaXMgbGlzdCBvZiBjb25kaXRpb25zIGFuZCB0aGUgZm9sbG93aW5nIGRpc2NsYWltZXIuCjIuIFJlZGlzdHJpYnV0aW9ucyBpbiBiaW5hcnkgZm9ybSBtdXN0IHJlcHJvZHVjZSB0aGUgYWJvdmUgY29weXJpZ2h0Cm5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lciBpbiB0aGUKZG9jdW1lbnRhdGlvbiBhbmQvb3Igb3RoZXIgbWF0ZXJpYWxzIHByb3ZpZGVkIHdpdGggdGhlIGRpc3RyaWJ1dGlvbi4KMy4gTmVpdGhlciB0aGUgbmFtZSBvZiB0aGUgY29weXJpZ2h0IGhvbGRlciBub3IgdGhlIG5hbWVzIG9mIGl0cwpjb250cmlidXRvcnMgbWF5IGJlIHVzZWQgdG8gZW5kb3JzZSBvciBwcm9tb3RlIHByb2R1Y3RzIGRlcml2ZWQKZnJvbSB0aGlzIHNvZnR3YXJlIHdpdGhvdXQgc3BlY2lmaWMgcHJpb3Igd3JpdHRlbiBwZXJtaXNzaW9uLgoKVEhJUyBTT0ZUV0FSRSBJUyBQUk9WSURFRCBCWSBUSEUgQ09QWVJJR0hUIEhPTERFUlMgQU5EIENPTlRSSUJVVE9SUwoiQVMgSVMiIEFORCBBTlkgRVhQUkVTUyBPUiBJTVBMSUVEIFdBUlJBTlRJRVMsIElOQ0xVRElORywgQlVUIE5PVApMSU1JVEVEIFRPLCBUSEUgSU1QTElFRCBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSBBTkQgRklUTkVTUwpGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQVJFIERJU0NMQUlNRUQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRQpDT1BZUklHSFQgSE9MREVSIE9SIENPTlRSSUJVVE9SUyBCRSBMSUFCTEUgRk9SIEFOWSBESVJFQ1QsIElORElSRUNULApJTkNJREVOVEFMLCBTUEVDSUFMLCBFWEVNUExBUlksIE9SIENPTlNFUVVFTlRJQUwgREFNQUdFUyAoSU5DTFVESU5HLApCVVQgTk9UIExJTUlURUQgVE8sIFBST0NVUkVNRU5UIE9GIFNVQlNUSVRVVEUgR09PRFMgT1IgU0VSVklDRVM7CkxPU1MgT0YgVVNFLCBEQVRBLCBPUiBQUk9GSVRTOyBPUiBCVVNJTkVTUyBJTlRFUlJVUFRJT04pIEhPV0VWRVIKQ0FVU0VEIEFORCBPTiBBTlkgVEhFT1JZIE9GIExJQUJJTElUWSwgV0hFVEhFUiBJTiBDT05UUkFDVCwgU1RSSUNUCkxJQUJJTElUWSwgT1IgVE9SVCAoSU5DTFVESU5HIE5FR0xJR0VOQ0UgT1IgT1RIRVJXSVNFKSBBUklTSU5HIElOCkFOWSBXQVkgT1VUIE9GIFRIRSBVU0UgT0YgVEhJUyBTT0ZUV0FSRSwgRVZFTiBJRiBBRFZJU0VEIE9GIFRIRQpQT1NTSUJJTElUWSBPRiBTVUNIIERBTUFHRS4KClRoZSBhYm92ZSBsaWNlbnNlIGlzIHVzZWQgYXMgYSBsaWNlbnNlIHVuZGVyIGNvcHlyaWdodCBvbmx5LiBQbGVhc2UKcmVmZXJlbmNlIHRoZSBPTUEgSVBSIFBvbGljeSBmb3IgcGF0ZW50IGxpY2Vuc2luZyB0ZXJtczoKaHR0cHM6Ly93d3cub21hc3BlY3dvcmtzLm9yZy9hYm91dC9pbnRlbGxlY3R1YWwtcHJvcGVydHktcmlnaHRzLwoKLS0+CjxMV00yTSB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4c2k6bm9OYW1lc3BhY2VTY2hlbWFMb2NhdGlvbj0iaHR0cDovL29wZW5tb2JpbGVhbGxpYW5jZS5vcmcvdGVjaC9wcm9maWxlcy9MV00yTS54c2QiPgoJPE9iamVjdCBPYmplY3RUeXBlPSJNT0RlZmluaXRpb24iPgoJCTxOYW1lPkJpbmFyeUFwcERhdGFDb250YWluZXI8L05hbWU+CgkJPERlc2NyaXB0aW9uMT48IVtDREFUQVtUaGlzIEx3TTJNIE9iamVjdHMgcHJvdmlkZXMgdGhlIGFwcGxpY2F0aW9uIHNlcnZpY2UgZGF0YSByZWxhdGVkIHRvIGEgTHdNMk0gU2VydmVyLCBlZy4gV2F0ZXIgbWV0ZXIgZGF0YS4gClRoZXJlIGFyZSBzZXZlcmFsIG1ldGhvZHMgdG8gY3JlYXRlIGluc3RhbmNlIHRvIGluZGljYXRlIHRoZSBtZXNzYWdlIGRpcmVjdGlvbiBiYXNlZCBvbiB0aGUgbmVnb3RpYXRpb24gYmV0d2VlbiBBcHBsaWNhdGlvbiBhbmQgTHdNMk0uIFRoZSBDbGllbnQgYW5kIFNlcnZlciBzaG91bGQgbmVnb3RpYXRlIHRoZSBpbnN0YW5jZShzKSB1c2VkIHRvIGV4Y2hhbmdlIHRoZSBkYXRhLiBGb3IgZXhhbXBsZToKIC0gVXNpbmcgYSBzaW5nbGUgaW5zdGFuY2UgZm9yIGJvdGggZGlyZWN0aW9ucyBjb21tdW5pY2F0aW9uLCBmcm9tIENsaWVudCB0byBTZXJ2ZXIgYW5kIGZyb20gU2VydmVyIHRvIENsaWVudC4KIC0gVXNpbmcgYW4gaW5zdGFuY2UgZm9yIGNvbW11bmljYXRpb24gZnJvbSBDbGllbnQgdG8gU2VydmVyIGFuZCBhbm90aGVyIG9uZSBmb3IgY29tbXVuaWNhdGlvbiBmcm9tIFNlcnZlciB0byBDbGllbnQKIC0gVXNpbmcgc2V2ZXJhbCBpbnN0YW5jZXMKXV0+PC9EZXNjcmlwdGlvbjE+CgkJPE9iamVjdElEPjE5PC9PYmplY3RJRD4KCQk8T2JqZWN0VVJOPnVybjpvbWE6bHdtMm06b21hOjE5PC9PYmplY3RVUk4+CgkJPExXTTJNVmVyc2lvbj4xLjA8L0xXTTJNVmVyc2lvbj4KCQk8T2JqZWN0VmVyc2lvbj4xLjA8L09iamVjdFZlcnNpb24+CgkJPE11bHRpcGxlSW5zdGFuY2VzPk11bHRpcGxlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJPFJlc291cmNlcz4KCQkJPEl0ZW0gSUQ9IjAiPjxOYW1lPkRhdGE8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5NdWx0aXBsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk1hbmRhdG9yeTwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+T3BhcXVlPC9UeXBlPgoJCQkJPFJhbmdlRW51bWVyYXRpb24gLz4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgYXBwbGljYXRpb24gZGF0YSBjb250ZW50Ll1dPjwvRGVzY3JpcHRpb24+CgkJCTwvSXRlbT4KCQkJPEl0ZW0gSUQ9IjEiPjxOYW1lPkRhdGEgUHJpb3JpdHk8L05hbWU+CgkJCQk8T3BlcmF0aW9ucz5SVzwvT3BlcmF0aW9ucz4KCQkJCTxNdWx0aXBsZUluc3RhbmNlcz5TaW5nbGU8L011bHRpcGxlSW5zdGFuY2VzPgoJCQkJPE1hbmRhdG9yeT5PcHRpb25hbDwvTWFuZGF0b3J5PgoJCQkJPFR5cGU+SW50ZWdlcjwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjEgYnl0ZXM8L1JhbmdlRW51bWVyYXRpb24+CgkJCQk8VW5pdHMgLz4KCQkJCTxEZXNjcmlwdGlvbj48IVtDREFUQVtJbmRpY2F0ZXMgdGhlIEFwcGxpY2F0aW9uIGRhdGEgcHJpb3JpdHk6CjA6SW1tZWRpYXRlCjE6QmVzdEVmZm9ydAoyOkxhdGVzdAozLTEwMDogUmVzZXJ2ZWQgZm9yIGZ1dHVyZSB1c2UuCjEwMS0yNTQ6IFByb3ByaWV0YXJ5IG1vZGUuXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iMiI+PE5hbWU+RGF0YSBDcmVhdGlvbiBUaW1lPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlRpbWU8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbiAvPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBEYXRhIGluc3RhbmNlIGNyZWF0aW9uIHRpbWVzdGFtcC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSIzIj48TmFtZT5EYXRhIERlc2NyaXB0aW9uPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPlN0cmluZzwvVHlwZT4KCQkJCTxSYW5nZUVudW1lcmF0aW9uPjMyIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkYXRhIGRlc2NyaXB0aW9uLgplLmcuICJtZXRlciByZWFkaW5nIi5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+CgkJCTxJdGVtIElEPSI0Ij48TmFtZT5EYXRhIEZvcm1hdDwvTmFtZT4KCQkJCTxPcGVyYXRpb25zPlJXPC9PcGVyYXRpb25zPgoJCQkJPE11bHRpcGxlSW5zdGFuY2VzPlNpbmdsZTwvTXVsdGlwbGVJbnN0YW5jZXM+CgkJCQk8TWFuZGF0b3J5Pk9wdGlvbmFsPC9NYW5kYXRvcnk+CgkJCQk8VHlwZT5TdHJpbmc8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4zMiBieXRlczwvUmFuZ2VFbnVtZXJhdGlvbj4KCQkJCTxVbml0cyAvPgoJCQkJPERlc2NyaXB0aW9uPjwhW0NEQVRBW0luZGljYXRlcyB0aGUgZm9ybWF0IG9mIHRoZSBBcHBsaWNhdGlvbiBEYXRhLgplLmcuIFlHLU1ldGVyLVdhdGVyLVJlYWRpbmcKVVRGOC1zdHJpbmcKXV0+PC9EZXNjcmlwdGlvbj4KCQkJPC9JdGVtPgoJCQk8SXRlbSBJRD0iNSI+PE5hbWU+QXBwIElEPC9OYW1lPgoJCQkJPE9wZXJhdGlvbnM+Ulc8L09wZXJhdGlvbnM+CgkJCQk8TXVsdGlwbGVJbnN0YW5jZXM+U2luZ2xlPC9NdWx0aXBsZUluc3RhbmNlcz4KCQkJCTxNYW5kYXRvcnk+T3B0aW9uYWw8L01hbmRhdG9yeT4KCQkJCTxUeXBlPkludGVnZXI8L1R5cGU+CgkJCQk8UmFuZ2VFbnVtZXJhdGlvbj4yIGJ5dGVzPC9SYW5nZUVudW1lcmF0aW9uPgoJCQkJPFVuaXRzIC8+CgkJCQk8RGVzY3JpcHRpb24+PCFbQ0RBVEFbSW5kaWNhdGVzIHRoZSBkZXN0aW5hdGlvbiBBcHBsaWNhdGlvbiBJRC5dXT48L0Rlc2NyaXB0aW9uPgoJCQk8L0l0ZW0+PC9SZXNvdXJjZXM+CgkJPERlc2NyaXB0aW9uMj48IVtDREFUQVtdXT48L0Rlc2NyaXB0aW9uMj4KCTwvT2JqZWN0Pgo8L0xXTTJNPgo=", accessMode = ApiModelProperty.AccessMode.READ_WRITE) + @JsonGetter("data") + public String getEncodedData() { + return Optional.ofNullable(data) + .map(Base64.getEncoder()::encodeToString) + .orElse(null); + } + + @JsonSetter("data") + public void setEncodedData(String data) { + this.data = Optional.ofNullable(data) + .map(Base64.getDecoder()::decode) + .orElse(null); + } + + @JsonGetter("preview") + public String getEncodedPreview() { + return Optional.ofNullable(preview) + .map(Base64.getEncoder()::encodeToString) + .orElse(null); + } + + @JsonSetter("preview") + public void setEncodedPreview(String preview) { + this.preview = Optional.ofNullable(preview) + .map(Base64.getDecoder()::decode) + .orElse(null); } @Override public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("Resource [tenantId="); - builder.append(getTenantId()); - builder.append(", id="); - builder.append(getUuidId()); - builder.append(", createdTime="); - builder.append(createdTime); - builder.append(", title="); - builder.append(getTitle()); - builder.append(", resourceType="); - builder.append(getResourceType()); - builder.append(", resourceKey="); - builder.append(getResourceKey()); - builder.append(", fileName="); - builder.append(fileName); - builder.append(", data="); - builder.append(data); - builder.append(", hashCode="); - builder.append(getEtag()); - builder.append("]"); - return builder.toString(); + return super.toString(); } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java index af3c71f226..6b485488b0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java @@ -16,6 +16,9 @@ package org.thingsboard.server.common.data; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -26,11 +29,13 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; +import java.util.function.UnaryOperator; + @ApiModel @Slf4j @Data @EqualsAndHashCode(callSuper = true) -public class TbResourceInfo extends BaseData implements HasName, HasTenantId { +public class TbResourceInfo extends BaseData implements HasName, HasTenantId, ExportableEntity { private static final long serialVersionUID = 7282664529021651736L; @@ -46,10 +51,20 @@ public class TbResourceInfo extends BaseData implements HasName, H @Length(fieldName = "resourceKey") @ApiModelProperty(position = 6, value = "Resource key.", example = "19_1.0", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String resourceKey; + private boolean isPublic; + private String publicResourceKey; @ApiModelProperty(position = 7, value = "Resource search text.", example = "19_1.0:binaryappdatacontainer", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String searchText; + @ApiModelProperty(position = 8, value = "Resource etag.", example = "33a64df551425fcc55e4d42a148795d9f25f89d4", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String etag; + @NoXss + @Length(fieldName = "file name") + @ApiModelProperty(position = 9, value = "Resource file name.", example = "19.xml", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + private String fileName; + private JsonNode descriptor; + + private TbResourceId externalId; public TbResourceInfo() { super(); @@ -61,18 +76,23 @@ public class TbResourceInfo extends BaseData implements HasName, H public TbResourceInfo(TbResourceInfo resourceInfo) { super(resourceInfo); - this.tenantId = resourceInfo.getTenantId(); - this.title = resourceInfo.getTitle(); - this.resourceType = resourceInfo.getResourceType(); - this.resourceKey = resourceInfo.getResourceKey(); - this.searchText = resourceInfo.getSearchText(); - this.etag = resourceInfo.getEtag(); + this.tenantId = resourceInfo.tenantId; + this.title = resourceInfo.title; + this.resourceType = resourceInfo.resourceType; + this.resourceKey = resourceInfo.resourceKey; + this.searchText = resourceInfo.searchText; + this.isPublic = resourceInfo.isPublic; + this.publicResourceKey = resourceInfo.publicResourceKey; + this.etag = resourceInfo.etag; + this.fileName = resourceInfo.fileName; + this.descriptor = resourceInfo.descriptor != null ? resourceInfo.descriptor.deepCopy() : null; + this.externalId = resourceInfo.externalId; } @ApiModelProperty(position = 1, value = "JSON object with the Resource Id. " + "Specify this field to update the Resource. " + "Referencing non-existing Resource Id will cause error. " + - "Omit this field to create new Resource." ) + "Omit this field to create new Resource.") @Override public TbResourceId getId() { return super.getId(); @@ -85,34 +105,46 @@ public class TbResourceInfo extends BaseData implements HasName, H } @Override - @JsonIgnore + @JsonProperty(access = JsonProperty.Access.READ_ONLY) public String getName() { return title; } + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public String getLink() { + if (resourceType == ResourceType.IMAGE) { + String type = (tenantId != null && tenantId.isSysTenantId()) ? "system" : "tenant"; // tenantId is null in case of export to git + return "/api/images/" + type + "/" + resourceKey; + } + return null; + } + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public String getPublicLink() { + if (resourceType == ResourceType.IMAGE && isPublic) { + return "/api/images/public/" + getPublicResourceKey(); + } + return null; + } + @JsonIgnore public String getSearchText() { - return searchText != null ? searchText : title; + return title; } - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("ResourceInfo [tenantId="); - builder.append(tenantId); - builder.append(", id="); - builder.append(getUuidId()); - builder.append(", createdTime="); - builder.append(createdTime); - builder.append(", title="); - builder.append(title); - builder.append(", resourceType="); - builder.append(resourceType); - builder.append(", resourceKey="); - builder.append(resourceKey); - builder.append(", hashCode="); - builder.append(etag); - builder.append("]"); - return builder.toString(); + public T getDescriptor(Class type) throws JsonProcessingException { + return descriptor != null ? mapper.treeToValue(descriptor, type) : null; + } + + public void updateDescriptor(Class type, UnaryOperator updater) throws JsonProcessingException { + T descriptor = getDescriptor(type); + descriptor = updater.apply(descriptor); + setDescriptorValue(descriptor); } + + @JsonIgnore + public void setDescriptorValue(Object value) { + this.descriptor = value != null ? mapper.valueToTree(value) : null; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfoFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfoFilter.java index 9057fc144b..5c0747f100 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfoFilter.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfoFilter.java @@ -19,11 +19,13 @@ import lombok.Builder; import lombok.Data; import org.thingsboard.server.common.data.id.TenantId; +import java.util.Set; + @Data @Builder public class TbResourceInfoFilter { private TenantId tenantId; - private ResourceType resourceType; + private Set resourceTypes; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java b/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java index 89cd9ec467..dd6aadbe0b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java @@ -34,14 +34,14 @@ public class Tenant extends ContactBased implements HasTenantId, HasTi @Length(fieldName = "title") @NoXss - @ApiModelProperty(position = 3, value = "Title of the tenant", example = "Company A") + @ApiModelProperty(position = 3, required = true, value = "Title of the tenant", example = "Company A") private String title; @NoXss @Length(fieldName = "region") @ApiModelProperty(position = 5, value = "Geo region of the tenant", example = "North America") private String region; - @ApiModelProperty(position = 6, required = true, value = "JSON object with Tenant Profile Id") + @ApiModelProperty(position = 6, value = "JSON object with Tenant Profile Id") private TenantProfileId tenantProfileId; public Tenant() { @@ -111,43 +111,43 @@ public class Tenant extends ContactBased implements HasTenantId, HasTi return super.getCreatedTime(); } - @ApiModelProperty(position = 7, required = true, value = "Country", example = "US") + @ApiModelProperty(position = 7, value = "Country", example = "US") @Override public String getCountry() { return super.getCountry(); } - @ApiModelProperty(position = 8, required = true, value = "State", example = "NY") + @ApiModelProperty(position = 8, value = "State", example = "NY") @Override public String getState() { return super.getState(); } - @ApiModelProperty(position = 9, required = true, value = "City", example = "New York") + @ApiModelProperty(position = 9, value = "City", example = "New York") @Override public String getCity() { return super.getCity(); } - @ApiModelProperty(position = 10, required = true, value = "Address Line 1", example = "42 Broadway Suite 12-400") + @ApiModelProperty(position = 10, value = "Address Line 1", example = "42 Broadway Suite 12-400") @Override public String getAddress() { return super.getAddress(); } - @ApiModelProperty(position = 11, required = true, value = "Address Line 2", example = "") + @ApiModelProperty(position = 11, value = "Address Line 2", example = "") @Override public String getAddress2() { return super.getAddress2(); } - @ApiModelProperty(position = 12, required = true, value = "Zip code", example = "10004") + @ApiModelProperty(position = 12, value = "Zip code", example = "10004") @Override public String getZip() { return super.getZip(); } - @ApiModelProperty(position = 13, required = true, value = "Phone number", example = "+1(415)777-7777") + @ApiModelProperty(position = 13, value = "Phone number", example = "+1(415)777-7777") @Override public String getPhone() { return super.getPhone(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/UsageInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/UsageInfo.java index 9b03118bad..bdd9801b0c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/UsageInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/UsageInfo.java @@ -33,7 +33,9 @@ public class UsageInfo { private long transportMessages; private long maxTransportMessages; private long jsExecutions; + private long tbelExecutions; private long maxJsExecutions; + private long maxTbelExecutions; private long emails; private long maxEmails; private long sms; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/User.java b/common/data/src/main/java/org/thingsboard/server/common/data/User.java index b3d9268c6b..2db8c83a89 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/User.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/User.java @@ -129,7 +129,7 @@ public class User extends BaseDataWithAdditionalInfo implements HasName, this.authority = authority; } - @ApiModelProperty(position = 8, required = true, value = "First name of the user", example = "John") + @ApiModelProperty(position = 8, required = false, value = "First name of the user", example = "John") public String getFirstName() { return firstName; } @@ -138,7 +138,7 @@ public class User extends BaseDataWithAdditionalInfo implements HasName, this.firstName = firstName; } - @ApiModelProperty(position = 9, required = true, value = "Last name of the user", example = "Doe") + @ApiModelProperty(position = 9, required = false, value = "Last name of the user", example = "Doe") public String getLastName() { return lastName; } @@ -147,7 +147,7 @@ public class User extends BaseDataWithAdditionalInfo implements HasName, this.lastName = lastName; } - @ApiModelProperty(position = 10, required = true, value = "Phone number of the user", example = "38012345123") + @ApiModelProperty(position = 10, required = false, value = "Phone number of the user", example = "38012345123") public String getPhone() { return phone; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java index 34fec2cd82..c96b55b61d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java @@ -153,7 +153,6 @@ public class Alarm extends BaseData implements HasName, HasTenantId, Ha } public static AlarmStatus toStatus(boolean cleared, boolean acknowledged) { - if (cleared) { return acknowledged ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK; } else { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmAssignee.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmAssignee.java index 20ed7b549c..43680242e5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmAssignee.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmAssignee.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.alarm; +import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -22,6 +23,8 @@ import org.thingsboard.server.common.data.id.UserId; import java.io.Serializable; +import static org.apache.commons.lang3.StringUtils.isNotEmpty; + @Builder @AllArgsConstructor @Data @@ -34,4 +37,22 @@ public class AlarmAssignee implements Serializable { private final String lastName; private final String email; + @JsonIgnore + public String getTitle() { + String title = ""; + if (isNotEmpty(firstName)) { + title += firstName; + } + if (isNotEmpty(lastName)) { + if (!title.isEmpty()) { + title += " "; + } + title += lastName; + } + if (title.isEmpty()) { + title = email; + } + return title; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java index 982e0f94d1..76ea06d27a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java @@ -22,6 +22,7 @@ import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.id.AlarmCommentId; @@ -44,6 +45,7 @@ public class AlarmComment extends BaseData implements HasName { @ApiModelProperty(position = 6, value = "JSON object with text of comment.", dataType = "com.fasterxml.jackson.databind.JsonNode") @NoXss @Length(fieldName = "comment", max = 10000) + @EqualsAndHashCode.Include private transient JsonNode comment; @ApiModelProperty(position = 1, value = "JSON object with the alarm comment Id. " + diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmModificationRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmModificationRequest.java index e40cb15d22..9270c2d696 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmModificationRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmModificationRequest.java @@ -22,6 +22,8 @@ public interface AlarmModificationRequest { TenantId getTenantId(); + AlarmSeverity getSeverity(); + long getStartTs(); long getEndTs(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java index bbb56305db..a8fb0b01b1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java @@ -131,7 +131,7 @@ public class Asset extends BaseDataWithAdditionalInfo implements HasLab this.name = name; } - @ApiModelProperty(position = 6, required = true, value = "Asset type", example = "Building") + @ApiModelProperty(position = 6, value = "Asset type", example = "Building") public String getType() { return type; } @@ -140,7 +140,7 @@ public class Asset extends BaseDataWithAdditionalInfo implements HasLab this.type = type; } - @ApiModelProperty(position = 7, required = true, value = "Label that may be used in widgets", example = "NY Building") + @ApiModelProperty(position = 7, value = "Label that may be used in widgets", example = "NY Building") public String getLabel() { return label; } @@ -149,7 +149,7 @@ public class Asset extends BaseDataWithAdditionalInfo implements HasLab this.label = label; } - @ApiModelProperty(position = 8, required = true, value = "JSON object with Asset Profile Id.") + @ApiModelProperty(position = 8, value = "JSON object with Asset Profile Id.") public AssetProfileId getAssetProfileId() { return assetProfileId; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetProfile.java index 5456ba36ce..2daa64061d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetProfile.java @@ -22,8 +22,8 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.BaseData; -import org.thingsboard.server.common.data.BaseDataWithAdditionalInfo; import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.HasImage; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasRuleEngineProfile; import org.thingsboard.server.common.data.HasTenantId; @@ -39,7 +39,7 @@ import org.thingsboard.server.common.data.validation.NoXss; @ToString(exclude = {"image"}) @EqualsAndHashCode(callSuper = true) @Slf4j -public class AssetProfile extends BaseData implements HasName, HasTenantId, HasRuleEngineProfile, ExportableEntity { +public class AssetProfile extends BaseData implements HasName, HasTenantId, HasRuleEngineProfile, ExportableEntity, HasImage { private static final long serialVersionUID = 6998485460273302018L; @@ -52,7 +52,6 @@ public class AssetProfile extends BaseData implements HasName, H @NoXss @ApiModelProperty(position = 11, value = "Asset Profile description. ") private String description; - @Length(fieldName = "image", max = 1000000) @ApiModelProperty(position = 12, value = "Either URL or Base64 data of the icon. Used in the mobile application to visualize set of asset profiles in the grid view. ") private String image; private boolean isDefault; @@ -114,7 +113,7 @@ public class AssetProfile extends BaseData implements HasName, H } @ApiModelProperty(position = 5, value = "Used to mark the default profile. Default profile is used when the asset profile is not specified during asset creation.") - public boolean isDefault(){ + public boolean isDefault() { return isDefault; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java b/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java index 01009751d4..056be5a958 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java @@ -16,49 +16,61 @@ package org.thingsboard.server.common.data.audit; import lombok.Getter; +import org.thingsboard.server.common.data.msg.TbMsgType; + +import java.util.Optional; -@Getter public enum ActionType { - ADDED(false), // log entity - DELETED(false), // log string id - UPDATED(false), // log entity - ATTRIBUTES_UPDATED(false), // log attributes/values - ATTRIBUTES_DELETED(false), // log attributes - TIMESERIES_UPDATED(false), // log timeseries update - TIMESERIES_DELETED(false), // log timeseries - RPC_CALL(false), // log method and params - CREDENTIALS_UPDATED(false), // log new credentials - ASSIGNED_TO_CUSTOMER(false), // log customer name - UNASSIGNED_FROM_CUSTOMER(false), // log customer name - ACTIVATED(false), // log string id - SUSPENDED(false), // log string id - CREDENTIALS_READ(true), // log device id - ATTRIBUTES_READ(true), // log attributes - RELATION_ADD_OR_UPDATE(false), - RELATION_DELETED(false), - RELATIONS_DELETED(false), - ALARM_ACK(false), - ALARM_CLEAR(false), - ALARM_DELETE(false), - ALARM_ASSIGNED(false), - ALARM_UNASSIGNED(false), - LOGIN(false), - LOGOUT(false), - LOCKOUT(false), - ASSIGNED_FROM_TENANT(false), - ASSIGNED_TO_TENANT(false), - PROVISION_SUCCESS(false), - PROVISION_FAILURE(false), - ASSIGNED_TO_EDGE(false), // log edge name - UNASSIGNED_FROM_EDGE(false), - ADDED_COMMENT(false), - UPDATED_COMMENT(false), - DELETED_COMMENT(false), - SMS_SENT(false); + ADDED(false, TbMsgType.ENTITY_CREATED), // log entity + DELETED(false, TbMsgType.ENTITY_DELETED), // log string id + UPDATED(false, TbMsgType.ENTITY_UPDATED), // log entity + ATTRIBUTES_UPDATED(false, TbMsgType.ATTRIBUTES_UPDATED), // log attributes/values + ATTRIBUTES_DELETED(false, TbMsgType.ATTRIBUTES_DELETED), // log attributes + TIMESERIES_UPDATED(false, TbMsgType.TIMESERIES_UPDATED), // log timeseries update + TIMESERIES_DELETED(false, TbMsgType.TIMESERIES_DELETED), // log timeseries + RPC_CALL(false, null), // log method and params + CREDENTIALS_UPDATED(false, null), // log new credentials + ASSIGNED_TO_CUSTOMER(false, TbMsgType.ENTITY_ASSIGNED), // log customer name + UNASSIGNED_FROM_CUSTOMER(false, TbMsgType.ENTITY_UNASSIGNED), // log customer name + ACTIVATED(false, null), // log string id + SUSPENDED(false, null), // log string id + CREDENTIALS_READ(true, null), // log device id + ATTRIBUTES_READ(true, null), // log attributes + RELATION_ADD_OR_UPDATE(false, TbMsgType.RELATION_ADD_OR_UPDATE), + RELATION_DELETED(false, TbMsgType.RELATION_DELETED), + RELATIONS_DELETED(false, TbMsgType.RELATIONS_DELETED), + ALARM_ACK(false, TbMsgType.ALARM_ACK), + ALARM_CLEAR(false, TbMsgType.ALARM_CLEAR), + ALARM_DELETE(false, TbMsgType.ALARM_DELETE), + ALARM_ASSIGNED(false, TbMsgType.ALARM_ASSIGNED), + ALARM_UNASSIGNED(false, TbMsgType.ALARM_UNASSIGNED), + LOGIN(false, null), + LOGOUT(false, null), + LOCKOUT(false, null), + ASSIGNED_FROM_TENANT(false, TbMsgType.ENTITY_ASSIGNED_FROM_TENANT), + ASSIGNED_TO_TENANT(false, TbMsgType.ENTITY_ASSIGNED_TO_TENANT), + PROVISION_SUCCESS(false, TbMsgType.PROVISION_SUCCESS), + PROVISION_FAILURE(false, TbMsgType.PROVISION_FAILURE), + ASSIGNED_TO_EDGE(false, TbMsgType.ENTITY_ASSIGNED_TO_EDGE), // log edge name + UNASSIGNED_FROM_EDGE(false, TbMsgType.ENTITY_UNASSIGNED_FROM_EDGE), + ADDED_COMMENT(false, TbMsgType.COMMENT_CREATED), + UPDATED_COMMENT(false, TbMsgType.COMMENT_UPDATED), + DELETED_COMMENT(false, null), + SMS_SENT(false, null); + + @Getter private final boolean isRead; - ActionType(boolean isRead) { + private final TbMsgType ruleEngineMsgType; + + ActionType(boolean isRead, TbMsgType ruleEngineMsgType) { this.isRead = isRead; + this.ruleEngineMsgType = ruleEngineMsgType; } + + public Optional getRuleEngineMsgType() { + return Optional.ofNullable(ruleEngineMsgType); + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java index 389b87f813..e51a0b5dea 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java @@ -27,6 +27,8 @@ import java.util.List; @Data public class DeviceProfileData implements Serializable { + private static final long serialVersionUID = -3864805547939495272L; + @ApiModelProperty(position = 1, value = "JSON object of device profile configuration") private DeviceProfileConfiguration configuration; @Valid diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java index 14b62fe02e..6c89c91006 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileTransportConfiguration.java @@ -18,7 +18,6 @@ package org.thingsboard.server.common.data.device.profile; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; import org.thingsboard.server.common.data.DeviceTransportType; -import org.thingsboard.server.common.data.transport.snmp.SnmpMapping; import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig; import java.util.List; @@ -45,8 +44,7 @@ public class SnmpDeviceProfileTransportConfiguration implements DeviceProfileTra private boolean isValid() { return timeoutMs != null && timeoutMs >= 0 && retries != null && retries >= 0 && communicationConfigs != null - && communicationConfigs.stream().allMatch(config -> config != null && config.isValid()) - && communicationConfigs.stream().flatMap(config -> config.getAllMappings().stream()).map(SnmpMapping::getOid) - .distinct().count() == communicationConfigs.stream().mapToInt(config -> config.getAllMappings().size()).sum(); + && communicationConfigs.stream().allMatch(config -> config != null && config.isValid()); } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEvent.java b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEvent.java index 71c35f4bd8..1baf707040 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEvent.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEvent.java @@ -31,6 +31,9 @@ import java.util.UUID; @ToString(callSuper = true) public class EdgeEvent extends BaseData { + private static final long serialVersionUID = 5548866356798094088L; + + private long seqId; private TenantId tenantId; private EdgeId edgeId; private EdgeEventActionType action; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java index 756c30690f..5ecd8eab27 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java @@ -15,24 +15,40 @@ */ package org.thingsboard.server.common.data.edge; +import lombok.Getter; +import org.thingsboard.server.common.data.EntityType; + +@Getter public enum EdgeEventType { - DASHBOARD, - ASSET, - DEVICE, - DEVICE_PROFILE, - ASSET_PROFILE, - ENTITY_VIEW, - ALARM, - RULE_CHAIN, - RULE_CHAIN_METADATA, - EDGE, - USER, - CUSTOMER, - RELATION, - TENANT, - WIDGETS_BUNDLE, - WIDGET_TYPE, - ADMIN_SETTINGS, - OTA_PACKAGE, - QUEUE + DASHBOARD(false, EntityType.DASHBOARD), + ASSET(false, EntityType.ASSET), + DEVICE(false, EntityType.DEVICE), + DEVICE_PROFILE(true, EntityType.DEVICE_PROFILE), + ASSET_PROFILE(true, EntityType.ASSET_PROFILE), + ENTITY_VIEW(false, EntityType.ENTITY_VIEW), + ALARM(false, EntityType.ALARM), + RULE_CHAIN(false, EntityType.RULE_CHAIN), + RULE_CHAIN_METADATA(false, null), + EDGE(false, EntityType.EDGE), + USER(true, EntityType.USER), + CUSTOMER(true, EntityType.CUSTOMER), + RELATION(true, null), + TENANT(true, EntityType.TENANT), + TENANT_PROFILE(true, EntityType.TENANT_PROFILE), + WIDGETS_BUNDLE(true, EntityType.WIDGETS_BUNDLE), + WIDGET_TYPE(true, EntityType.WIDGET_TYPE), + ADMIN_SETTINGS(true, null), + OTA_PACKAGE(true, EntityType.OTA_PACKAGE), + QUEUE(true, EntityType.QUEUE), + TB_RESOURCE(true, EntityType.TB_RESOURCE); + + private final boolean allEdgesRelated; + + private final EntityType entityType; + + + EdgeEventType(boolean allEdgesRelated, EntityType entityType) { + this.allEdgesRelated = allEdgesRelated; + this.entityType = entityType; + } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeInstallInstructions.java b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeInstallInstructions.java deleted file mode 100644 index acfc458906..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeInstallInstructions.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.data.edge; - -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@ApiModel -@Data -@AllArgsConstructor -@NoArgsConstructor -public class EdgeInstallInstructions { - - @ApiModelProperty(position = 1, value = "Markdown with docker install instructions") - private String dockerInstallInstructions; -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeInstructions.java b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeInstructions.java new file mode 100644 index 0000000000..2c3a66c002 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeInstructions.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.edge; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@ApiModel +@Data +@AllArgsConstructor +@NoArgsConstructor +public class EdgeInstructions { + + @ApiModelProperty(position = 1, value = "Markdown with install/upgrade instructions") + private String instructions; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/exception/AbstractRateLimitException.java b/common/data/src/main/java/org/thingsboard/server/common/data/exception/AbstractRateLimitException.java new file mode 100644 index 0000000000..1d1da75da3 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/exception/AbstractRateLimitException.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.exception; + +public abstract class AbstractRateLimitException extends RuntimeException { + + public AbstractRateLimitException() { + super(); + } + + public AbstractRateLimitException(String message) { + super(message); + } + + public AbstractRateLimitException(String message, Throwable cause) { + super(message, cause); + } + + public AbstractRateLimitException(Throwable cause) { + super(cause); + } + + protected AbstractRateLimitException(String message, Throwable cause, + boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/exception/ApiUsageLimitsExceededException.java b/common/data/src/main/java/org/thingsboard/server/common/data/exception/ApiUsageLimitsExceededException.java index 2d24184a3f..aa9441c776 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/exception/ApiUsageLimitsExceededException.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/exception/ApiUsageLimitsExceededException.java @@ -15,7 +15,7 @@ */ package org.thingsboard.server.common.data.exception; -public class ApiUsageLimitsExceededException extends RuntimeException { +public class ApiUsageLimitsExceededException extends AbstractRateLimitException { public ApiUsageLimitsExceededException(String message) { super(message); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardErrorCode.java b/common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardErrorCode.java index 00ffd655dc..1ab0e3e634 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardErrorCode.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardErrorCode.java @@ -29,7 +29,8 @@ public enum ThingsboardErrorCode { ITEM_NOT_FOUND(32), TOO_MANY_REQUESTS(33), TOO_MANY_UPDATES(34), - SUBSCRIPTION_VIOLATION(40); + SUBSCRIPTION_VIOLATION(40), + PASSWORD_VIOLATION(45); private int errorCode; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java index 3aaba1cf61..75e6abe273 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java @@ -29,6 +29,10 @@ public class EntityIdFactory { return getByTypeAndUuid(EntityType.values()[type], UUID.fromString(uuid)); } + public static EntityId getByTypeAndUuid(int type, UUID uuid) { + return getByTypeAndUuid(EntityType.values()[type], uuid); + } + public static EntityId getByTypeAndUuid(String type, String uuid) { return getByTypeAndUuid(EntityType.valueOf(type), UUID.fromString(uuid)); } @@ -115,10 +119,6 @@ public class EntityIdFactory { return new DashboardId(uuid); case DEVICE: return new DeviceId(uuid); - case DEVICE_PROFILE: - return new DeviceProfileId(uuid); - case ASSET_PROFILE: - return new AssetProfileId(uuid); case ASSET: return new AssetId(uuid); case ALARM: @@ -131,12 +131,20 @@ public class EntityIdFactory { return new WidgetsBundleId(uuid); case WIDGET_TYPE: return new WidgetTypeId(uuid); + case DEVICE_PROFILE: + return new DeviceProfileId(uuid); + case ASSET_PROFILE: + return new AssetProfileId(uuid); + case TENANT_PROFILE: + return new TenantProfileId(uuid); case OTA_PACKAGE: return new OtaPackageId(uuid); - case QUEUE: - return new QueueId(uuid); case EDGE: return new EdgeId(uuid); + case QUEUE: + return new QueueId(uuid); + case TB_RESOURCE: + return new TbResourceId(uuid); } throw new IllegalArgumentException("EdgeEventType " + edgeEventType + " is not supported!"); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/UserCredentialsId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/UserCredentialsId.java index 543ac1ba6f..d0a29695c0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/UserCredentialsId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/UserCredentialsId.java @@ -15,11 +15,15 @@ */ package org.thingsboard.server.common.data.id; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.util.UUID; public class UserCredentialsId extends UUIDBased { - public UserCredentialsId(UUID id){ + @JsonCreator + public UserCredentialsId(@JsonProperty("id")UUID id){ super(id); } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseDeleteTsKvQuery.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseDeleteTsKvQuery.java index dee0e7aa9b..a97cc8f834 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseDeleteTsKvQuery.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseDeleteTsKvQuery.java @@ -21,14 +21,20 @@ import lombok.Data; public class BaseDeleteTsKvQuery extends BaseTsKvQuery implements DeleteTsKvQuery { private final Boolean rewriteLatestIfDeleted; + private final Boolean deleteLatest; - public BaseDeleteTsKvQuery(String key, long startTs, long endTs, boolean rewriteLatestIfDeleted) { + public BaseDeleteTsKvQuery(String key, long startTs, long endTs, boolean rewriteLatestIfDeleted, boolean deleteLatest) { super(key, startTs, endTs); this.rewriteLatestIfDeleted = rewriteLatestIfDeleted; + this.deleteLatest = deleteLatest; + } + + public BaseDeleteTsKvQuery(String key, long startTs, long endTs, boolean rewriteLatestIfDeleted) { + this(key, startTs, endTs, rewriteLatestIfDeleted, true); } public BaseDeleteTsKvQuery(String key, long startTs, long endTs) { - this(key, startTs, endTs, false); + this(key, startTs, endTs, false, true); } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/DeleteTsKvQuery.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/DeleteTsKvQuery.java index 7b9b4ad16f..b2f41fffb4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/DeleteTsKvQuery.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/DeleteTsKvQuery.java @@ -19,4 +19,6 @@ public interface DeleteTsKvQuery extends TsKvQuery { Boolean getRewriteLatestIfDeleted(); + Boolean getDeleteLatest(); + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java new file mode 100644 index 0000000000..e709ac596e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java @@ -0,0 +1,70 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.limit; + +import lombok.Getter; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; + +import java.util.Optional; +import java.util.function.Function; + +public enum LimitedApi { + + ENTITY_EXPORT(DefaultTenantProfileConfiguration::getTenantEntityExportRateLimit, "entity version creation", true), + ENTITY_IMPORT(DefaultTenantProfileConfiguration::getTenantEntityImportRateLimit, "entity version load", true), + NOTIFICATION_REQUESTS(DefaultTenantProfileConfiguration::getTenantNotificationRequestsRateLimit, "notification requests", true), + NOTIFICATION_REQUESTS_PER_RULE(DefaultTenantProfileConfiguration::getTenantNotificationRequestsPerRuleRateLimit, "notification requests per rule", false), + REST_REQUESTS_PER_TENANT(DefaultTenantProfileConfiguration::getTenantServerRestLimitsConfiguration, "REST API requests", true), + REST_REQUESTS_PER_CUSTOMER(DefaultTenantProfileConfiguration::getCustomerServerRestLimitsConfiguration, "REST API requests per customer", false), + WS_UPDATES_PER_SESSION(DefaultTenantProfileConfiguration::getWsUpdatesPerSessionRateLimit, "WS updates per session", true), + CASSANDRA_QUERIES(DefaultTenantProfileConfiguration::getCassandraQueryTenantRateLimitsConfiguration, "Cassandra queries", true), + PASSWORD_RESET(false, true), + TWO_FA_VERIFICATION_CODE_SEND(false, true), + TWO_FA_VERIFICATION_CODE_CHECK(false, true), + TRANSPORT_MESSAGES_PER_TENANT("transport messages", true), + TRANSPORT_MESSAGES_PER_DEVICE("transport messages per device", false); + + private Function configExtractor; + @Getter + private final boolean perTenant; + @Getter + private boolean refillRateLimitIntervally; + @Getter + private String label; + + LimitedApi(Function configExtractor, String label, boolean perTenant) { + this.configExtractor = configExtractor; + this.label = label; + this.perTenant = perTenant; + } + + LimitedApi(boolean perTenant, boolean refillRateLimitIntervally) { + this.perTenant = perTenant; + this.refillRateLimitIntervally = refillRateLimitIntervally; + } + + LimitedApi(String label, boolean perTenant) { + this.label = label; + this.perTenant = perTenant; + } + + public String getLimitConfig(DefaultTenantProfileConfiguration profileConfiguration) { + return Optional.ofNullable(configExtractor) + .map(extractor -> extractor.apply(profileConfiguration)) + .orElse(null); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/msg/TbMsgType.java b/common/data/src/main/java/org/thingsboard/server/common/data/msg/TbMsgType.java new file mode 100644 index 0000000000..23149c79c1 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/msg/TbMsgType.java @@ -0,0 +1,108 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.msg; + +import lombok.Getter; +import org.thingsboard.server.common.data.StringUtils; + +import java.util.EnumSet; +import java.util.List; +import java.util.stream.Collectors; + +public enum TbMsgType { + + POST_ATTRIBUTES_REQUEST("Post attributes"), + POST_TELEMETRY_REQUEST("Post telemetry"), + TO_SERVER_RPC_REQUEST("RPC Request from Device"), + ACTIVITY_EVENT("Activity Event"), + INACTIVITY_EVENT("Inactivity Event"), + CONNECT_EVENT("Connect Event"), + DISCONNECT_EVENT("Disconnect Event"), + ENTITY_CREATED("Entity Created"), + ENTITY_UPDATED("Entity Updated"), + ENTITY_DELETED("Entity Deleted"), + ENTITY_ASSIGNED("Entity Assigned"), + ENTITY_UNASSIGNED("Entity Unassigned"), + ATTRIBUTES_UPDATED("Attributes Updated"), + ATTRIBUTES_DELETED("Attributes Deleted"), + ALARM, + ALARM_ACK("Alarm Acknowledged"), + ALARM_CLEAR("Alarm Cleared"), + ALARM_DELETE, + ALARM_ASSIGNED("Alarm Assigned"), + ALARM_UNASSIGNED("Alarm Unassigned"), + COMMENT_CREATED("Comment Created"), + COMMENT_UPDATED("Comment Updated"), + RPC_CALL_FROM_SERVER_TO_DEVICE("RPC Request to Device"), + ENTITY_ASSIGNED_FROM_TENANT("Entity Assigned From Tenant"), + ENTITY_ASSIGNED_TO_TENANT("Entity Assigned To Tenant"), + ENTITY_ASSIGNED_TO_EDGE, + ENTITY_UNASSIGNED_FROM_EDGE, + TIMESERIES_UPDATED("Timeseries Updated"), + TIMESERIES_DELETED("Timeseries Deleted"), + RPC_QUEUED("RPC Queued"), + RPC_SENT("RPC Sent"), + RPC_DELIVERED("RPC Delivered"), + RPC_SUCCESSFUL("RPC Successful"), + RPC_TIMEOUT("RPC Timeout"), + RPC_EXPIRED("RPC Expired"), + RPC_FAILED("RPC Failed"), + RPC_DELETED("RPC Deleted"), + RELATION_ADD_OR_UPDATE("Relation Added or Updated"), + RELATION_DELETED("Relation Deleted"), + RELATIONS_DELETED("All Relations Deleted"), + PROVISION_SUCCESS, + PROVISION_FAILURE, + SEND_EMAIL, + + // tellSelfOnly types + GENERATOR_NODE_SELF_MSG(null, true), + DEVICE_PROFILE_PERIODIC_SELF_MSG(null, true), + DEVICE_PROFILE_UPDATE_SELF_MSG(null, true), + DEVICE_UPDATE_SELF_MSG(null, true), + DEDUPLICATION_TIMEOUT_SELF_MSG(null, true), + DELAY_TIMEOUT_SELF_MSG(null, true), + MSG_COUNT_SELF_MSG(null, true), + + // Custom or N/A type: + NA; + + public static final List NODE_CONNECTIONS = EnumSet.allOf(TbMsgType.class).stream() + .filter(tbMsgType -> !tbMsgType.isTellSelfOnly()) + .map(TbMsgType::getRuleNodeConnection) + .filter(connection -> !TbNodeConnectionType.OTHER.equals(connection)) + .collect(Collectors.toUnmodifiableList()); + + @Getter + private final String ruleNodeConnection; + + @Getter + private final boolean tellSelfOnly; + + TbMsgType(String ruleNodeConnection, boolean tellSelfOnly) { + this.ruleNodeConnection = StringUtils.isNotEmpty(ruleNodeConnection) ? ruleNodeConnection : TbNodeConnectionType.OTHER; + this.tellSelfOnly = tellSelfOnly; + } + + TbMsgType(String ruleNodeConnection) { + this(ruleNodeConnection, false); + } + + TbMsgType() { + this(null, false); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/msg/TbNodeConnectionType.java b/common/data/src/main/java/org/thingsboard/server/common/data/msg/TbNodeConnectionType.java new file mode 100644 index 0000000000..584393bf53 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/msg/TbNodeConnectionType.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.msg; + +/** + * Created by ashvayka on 19.01.18. + */ +public final class TbNodeConnectionType { + + public static final String SUCCESS = "Success"; + public static final String FAILURE = "Failure"; + + public static final String TRUE = "True"; + public static final String FALSE = "False"; + + public static final String OTHER = "Other"; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java index 5b9e84d37d..5f8ac6bdc2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationDeliveryMethod.java @@ -25,6 +25,7 @@ public enum NotificationDeliveryMethod { EMAIL("email"), SMS("SMS"), SLACK("Slack"), + MICROSOFT_TEAMS("Microsoft Teams"), MOBILE("mobile"); @Getter diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStats.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStats.java index 619e1ad38f..691ecf8bc1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStats.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStats.java @@ -54,7 +54,6 @@ public class NotificationRequestStats { public void reportSent(NotificationDeliveryMethod deliveryMethod, NotificationRecipient recipient) { sent.computeIfAbsent(deliveryMethod, k -> new AtomicInteger()).incrementAndGet(); - processedRecipients.computeIfAbsent(deliveryMethod, k -> ConcurrentHashMap.newKeySet()).add(recipient.getId()); } public void reportError(NotificationDeliveryMethod deliveryMethod, Throwable error, NotificationRecipient recipient) { @@ -68,6 +67,10 @@ public class NotificationRequestStats { errors.computeIfAbsent(deliveryMethod, k -> new ConcurrentHashMap<>()).put(recipient.getTitle(), errorMessage); } + public void reportProcessed(NotificationDeliveryMethod deliveryMethod, Object recipientId) { + processedRecipients.computeIfAbsent(deliveryMethod, k -> ConcurrentHashMap.newKeySet()).add(recipientId); + } + public boolean contains(NotificationDeliveryMethod deliveryMethod, Object recipientId) { Set processedRecipients = this.processedRecipients.get(deliveryMethod); return processedRecipients != null && processedRecipients.contains(recipientId); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java index de07d03c67..251fae2ac0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java @@ -27,6 +27,7 @@ public enum NotificationType { NEW_PLATFORM_VERSION, ENTITIES_LIMIT, API_USAGE_LIMIT, - RULE_NODE + RULE_NODE, + RATE_LIMITS } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmNotificationInfo.java index d3af30907d..fd1b4ee0f8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmNotificationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmNotificationInfo.java @@ -42,6 +42,8 @@ public class AlarmNotificationInfo implements RuleOriginatedNotificationInfo { private String alarmOriginatorName; private AlarmSeverity alarmSeverity; private AlarmStatus alarmStatus; + private boolean acknowledged; + private boolean cleared; private CustomerId alarmCustomerId; @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RateLimitsNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RateLimitsNotificationInfo.java new file mode 100644 index 0000000000..57fc278cff --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RateLimitsNotificationInfo.java @@ -0,0 +1,59 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.info; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.limit.LimitedApi; + +import java.util.Map; + +import static org.thingsboard.server.common.data.util.CollectionsUtil.mapOf; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class RateLimitsNotificationInfo implements RuleOriginatedNotificationInfo { + + private TenantId tenantId; + private String tenantName; + private LimitedApi api; + private EntityId limitLevel; + private String limitLevelEntityName; + + @Override + public Map getTemplateData() { + return mapOf( + "api", api.getLabel(), + "limitLevelEntityType", limitLevel != null ? limitLevel.getEntityType().getNormalName() : null, + "limitLevelEntityId", limitLevel != null ? limitLevel.getId().toString() : null, + "limitLevelEntityName", limitLevelEntityName, + "tenantName", tenantName, + "tenantId", tenantId.toString() + ); + } + + @Override + public TenantId getAffectedTenantId() { + return tenantId; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineOriginatedNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineOriginatedNotificationInfo.java index adf1e3a0f6..f37c145f2c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineOriginatedNotificationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/RuleEngineOriginatedNotificationInfo.java @@ -19,6 +19,7 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import java.util.HashMap; @@ -28,9 +29,10 @@ import java.util.Map; @AllArgsConstructor @NoArgsConstructor @Builder -public class RuleEngineOriginatedNotificationInfo implements NotificationInfo { +public class RuleEngineOriginatedNotificationInfo implements RuleOriginatedNotificationInfo { private EntityId msgOriginator; + private CustomerId msgCustomerId; private String msgType; private Map msgMetadata; private Map msgData; @@ -43,6 +45,7 @@ public class RuleEngineOriginatedNotificationInfo implements NotificationInfo { templateData.put("originatorType", msgOriginator.getEntityType().getNormalName()); templateData.put("originatorId", msgOriginator.getId().toString()); templateData.put("msgType", msgType); + templateData.put("customerId", msgCustomerId != null ? msgCustomerId.getId().toString() : ""); return templateData; } @@ -51,4 +54,9 @@ public class RuleEngineOriginatedNotificationInfo implements NotificationInfo { return msgOriginator; } + @Override + public CustomerId getAffectedCustomerId() { + return msgCustomerId; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java index c88ade5bb1..e7cb4f8c4f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java @@ -26,8 +26,8 @@ import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.NotificationTemplateId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleRecipientsConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleRecipientsConfig.java index d31ec098ca..c5bcf98302 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleRecipientsConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleRecipientsConfig.java @@ -21,7 +21,7 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.Data; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; import javax.validation.constraints.NotNull; import java.io.Serializable; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmAssignmentNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmAssignmentNotificationRuleTriggerConfig.java deleted file mode 100644 index 59ad9e5316..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmAssignmentNotificationRuleTriggerConfig.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.data.notification.rule.trigger; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; -import org.thingsboard.server.common.data.alarm.AlarmSeverity; - -import javax.validation.constraints.NotEmpty; -import java.util.Set; - -@Data -@AllArgsConstructor -@NoArgsConstructor -@Builder -public class AlarmAssignmentNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { - - private Set alarmTypes; - private Set alarmSeverities; - private Set alarmStatuses; - @NotEmpty - private Set notifyOn; - - @Override - public NotificationRuleTriggerType getTriggerType() { - return NotificationRuleTriggerType.ALARM_ASSIGNMENT; - } - - public enum Action { - ASSIGNED, UNASSIGNED - } - -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmAssignmentTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmAssignmentTrigger.java new file mode 100644 index 0000000000..11d24386bc --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmAssignmentTrigger.java @@ -0,0 +1,46 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.alarm.AlarmInfo; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; + +@Data +@Builder +public class AlarmAssignmentTrigger implements NotificationRuleTrigger { + + private final TenantId tenantId; + private final AlarmInfo alarmInfo; + private final ActionType actionType; + private final User user; + + @Override + public EntityId getOriginatorEntityId() { + return alarmInfo.getOriginator(); + } + + @Override + public NotificationRuleTriggerType getType() { + return NotificationRuleTriggerType.ALARM_ASSIGNMENT; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmCommentNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmCommentNotificationRuleTriggerConfig.java deleted file mode 100644 index c34127b611..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmCommentNotificationRuleTriggerConfig.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.data.notification.rule.trigger; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; -import org.thingsboard.server.common.data.alarm.AlarmSeverity; - -import java.util.Set; - -@Data -@AllArgsConstructor -@NoArgsConstructor -@Builder -public class AlarmCommentNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { - - private Set alarmTypes; - private Set alarmSeverities; - private Set alarmStatuses; - private boolean onlyUserComments; - private boolean notifyOnCommentUpdate; - - @Override - public NotificationRuleTriggerType getTriggerType() { - return NotificationRuleTriggerType.ALARM_COMMENT; - } - -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmCommentTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmCommentTrigger.java new file mode 100644 index 0000000000..65ad9c9619 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmCommentTrigger.java @@ -0,0 +1,48 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmComment; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; + +@Data +@Builder +public class AlarmCommentTrigger implements NotificationRuleTrigger { + + private final TenantId tenantId; + private final AlarmComment comment; + private final Alarm alarm; + private final ActionType actionType; + private final User user; + + @Override + public NotificationRuleTriggerType getType() { + return NotificationRuleTriggerType.ALARM_COMMENT; + } + + @Override + public EntityId getOriginatorEntityId() { + return alarm.getId(); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmNotificationRuleTriggerConfig.java deleted file mode 100644 index 6118e9da00..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmNotificationRuleTriggerConfig.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.data.notification.rule.trigger; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; -import org.thingsboard.server.common.data.alarm.AlarmSeverity; - -import javax.validation.constraints.NotEmpty; -import java.io.Serializable; -import java.util.Set; - -@Data -@AllArgsConstructor -@NoArgsConstructor -@Builder -public class AlarmNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { - - private Set alarmTypes; - private Set alarmSeverities; - @NotEmpty - private Set notifyOn; - - private ClearRule clearRule; - - @Override - public NotificationRuleTriggerType getTriggerType() { - return NotificationRuleTriggerType.ALARM; - } - - @Data - public static class ClearRule implements Serializable { - private Set alarmStatuses; - } - - public enum AlarmAction { - CREATED, SEVERITY_CHANGED, ACKNOWLEDGED, CLEARED - } - -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmTrigger.java new file mode 100644 index 0000000000..674355150f --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/AlarmTrigger.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; + +@Data +@Builder +public class AlarmTrigger implements NotificationRuleTrigger { + + private final TenantId tenantId; + private final AlarmApiCallResult alarmUpdate; + + @Override + public NotificationRuleTriggerType getType() { + return NotificationRuleTriggerType.ALARM; + } + + @Override + public EntityId getOriginatorEntityId() { + return alarmUpdate.getAlarm().getId(); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ApiUsageLimitNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ApiUsageLimitNotificationRuleTriggerConfig.java deleted file mode 100644 index d5ce9dff68..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ApiUsageLimitNotificationRuleTriggerConfig.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.data.notification.rule.trigger; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.thingsboard.server.common.data.ApiFeature; -import org.thingsboard.server.common.data.ApiUsageStateValue; - -import java.util.Set; - -@Data -@AllArgsConstructor -@NoArgsConstructor -@Builder -public class ApiUsageLimitNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { - - private Set apiFeatures; - private Set notifyOn; - - @Override - public NotificationRuleTriggerType getTriggerType() { - return NotificationRuleTriggerType.API_USAGE_LIMIT; - } - -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ApiUsageLimitTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ApiUsageLimitTrigger.java new file mode 100644 index 0000000000..323f2b275f --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/ApiUsageLimitTrigger.java @@ -0,0 +1,44 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.ApiUsageRecordState; +import org.thingsboard.server.common.data.ApiUsageStateValue; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; + +@Data +@Builder +public class ApiUsageLimitTrigger implements NotificationRuleTrigger { + + private final TenantId tenantId; + private final ApiUsageRecordState state; + private final ApiUsageStateValue status; + + @Override + public NotificationRuleTriggerType getType() { + return NotificationRuleTriggerType.API_USAGE_LIMIT; + } + + @Override + public EntityId getOriginatorEntityId() { + return tenantId; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/DeviceActivityNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/DeviceActivityNotificationRuleTriggerConfig.java deleted file mode 100644 index 8deeada7c0..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/DeviceActivityNotificationRuleTriggerConfig.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.data.notification.rule.trigger; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import javax.validation.constraints.NotEmpty; -import java.util.Set; -import java.util.UUID; - -@Data -@AllArgsConstructor -@NoArgsConstructor -@Builder -public class DeviceActivityNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { - - private Set devices; - private Set deviceProfiles; // set either devices or profiles - @NotEmpty - private Set notifyOn; - - @Override - public NotificationRuleTriggerType getTriggerType() { - return NotificationRuleTriggerType.DEVICE_ACTIVITY; - } - - public enum DeviceEvent { - ACTIVE, INACTIVE - } - -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/DeviceActivityTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/DeviceActivityTrigger.java new file mode 100644 index 0000000000..0c0139410e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/DeviceActivityTrigger.java @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; + +@Data +@Builder +public class DeviceActivityTrigger implements NotificationRuleTrigger { + + private final TenantId tenantId; + private final CustomerId customerId; + private final DeviceId deviceId; + private final boolean active; + + private final String deviceName; + private final String deviceType; + private final String deviceLabel; + + @Override + public EntityId getOriginatorEntityId() { + return deviceId; + } + + @Override + public NotificationRuleTriggerType getType() { + return NotificationRuleTriggerType.DEVICE_ACTIVITY; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EntitiesLimitNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EntitiesLimitNotificationRuleTriggerConfig.java deleted file mode 100644 index a40ddfb081..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EntitiesLimitNotificationRuleTriggerConfig.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.data.notification.rule.trigger; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.thingsboard.server.common.data.EntityType; - -import javax.validation.constraints.Max; -import java.util.Set; - -@Data -@AllArgsConstructor -@NoArgsConstructor -@Builder -public class EntitiesLimitNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { - - private Set entityTypes; - @Max(1) - private float threshold; // in percents, - - @Override - public NotificationRuleTriggerType getTriggerType() { - return NotificationRuleTriggerType.ENTITIES_LIMIT; - } - -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EntitiesLimitTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EntitiesLimitTrigger.java new file mode 100644 index 0000000000..3222e3fc6d --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EntitiesLimitTrigger.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; + +@Data +@Builder +public class EntitiesLimitTrigger implements NotificationRuleTrigger { + + private final TenantId tenantId; + private final EntityType entityType; + + private long limit; + private long currentCount; + + @Override + public NotificationRuleTriggerType getType() { + return NotificationRuleTriggerType.ENTITIES_LIMIT; + } + + @Override + public EntityId getOriginatorEntityId() { + return tenantId; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EntityActionNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EntityActionNotificationRuleTriggerConfig.java deleted file mode 100644 index 3a627a9e56..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EntityActionNotificationRuleTriggerConfig.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.data.notification.rule.trigger; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.thingsboard.server.common.data.EntityType; - -import java.util.Set; - -@Data -@AllArgsConstructor -@NoArgsConstructor -@Builder -public class EntityActionNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { - - private Set entityTypes; // maybe add name filter ? - private boolean created; - private boolean updated; - private boolean deleted; - - @Override - public NotificationRuleTriggerType getTriggerType() { - return NotificationRuleTriggerType.ENTITY_ACTION; - } - -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EntityActionTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EntityActionTrigger.java new file mode 100644 index 0000000000..aaf61ab0bc --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/EntityActionTrigger.java @@ -0,0 +1,47 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.HasName; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; + +@Data +@Builder +public class EntityActionTrigger implements NotificationRuleTrigger { + + private final TenantId tenantId; + private final EntityId entityId; + private final HasName entity; + private final ActionType actionType; + private final User user; + + @Override + public NotificationRuleTriggerType getType() { + return NotificationRuleTriggerType.ENTITY_ACTION; + } + + @Override + public EntityId getOriginatorEntityId() { + return entityId; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionNotificationRuleTriggerConfig.java deleted file mode 100644 index 230be3b9d5..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionNotificationRuleTriggerConfig.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.data.notification.rule.trigger; - -import lombok.Data; - -@Data -public class NewPlatformVersionNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { - - @Override - public NotificationRuleTriggerType getTriggerType() { - return NotificationRuleTriggerType.NEW_PLATFORM_VERSION; - } - -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionTrigger.java new file mode 100644 index 0000000000..9776bfb575 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NewPlatformVersionTrigger.java @@ -0,0 +1,63 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.UpdateMessage; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; + +@Data +@Builder +public class NewPlatformVersionTrigger implements NotificationRuleTrigger { + + private final UpdateMessage updateInfo; + + @Override + public NotificationRuleTriggerType getType() { + return NotificationRuleTriggerType.NEW_PLATFORM_VERSION; + } + + @Override + public TenantId getTenantId() { + return TenantId.SYS_TENANT_ID; + } + + @Override + public EntityId getOriginatorEntityId() { + return TenantId.SYS_TENANT_ID; + } + + + @Override + public boolean deduplicate() { + return true; + } + + @Override + public String getDeduplicationKey() { + return String.join(":", NotificationRuleTrigger.super.getDeduplicationKey(), + updateInfo.getCurrentVersion(), updateInfo.getLatestVersion()); + } + + @Override + public long getDefaultDeduplicationDuration() { + return 0; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTrigger.java new file mode 100644 index 0000000000..2aac6fe75e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTrigger.java @@ -0,0 +1,46 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger; + +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; + +import java.io.Serializable; + +public interface NotificationRuleTrigger extends Serializable { + + NotificationRuleTriggerType getType(); + + TenantId getTenantId(); + + EntityId getOriginatorEntityId(); + + + default boolean deduplicate() { + return false; + } + + default String getDeduplicationKey() { + EntityId originatorEntityId = getOriginatorEntityId(); + return String.join(":", getType().toString(), originatorEntityId.getEntityType().toString(), originatorEntityId.getId().toString()); + } + + default long getDefaultDeduplicationDuration() { + return 0; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerConfig.java deleted file mode 100644 index b0eec28858..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerConfig.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.data.notification.rule.trigger; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonSubTypes.Type; -import com.fasterxml.jackson.annotation.JsonTypeInfo; - -import java.io.Serializable; - -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "triggerType") -@JsonSubTypes({ - @Type(value = AlarmNotificationRuleTriggerConfig.class, name = "ALARM"), - @Type(value = DeviceActivityNotificationRuleTriggerConfig.class, name = "DEVICE_ACTIVITY"), - @Type(value = EntityActionNotificationRuleTriggerConfig.class, name = "ENTITY_ACTION"), - @Type(value = AlarmCommentNotificationRuleTriggerConfig.class, name = "ALARM_COMMENT"), - @Type(value = RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.class, name = "RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT"), - @Type(value = AlarmAssignmentNotificationRuleTriggerConfig.class, name = "ALARM_ASSIGNMENT"), - @Type(value = NewPlatformVersionNotificationRuleTriggerConfig.class, name = "NEW_PLATFORM_VERSION"), - @Type(value = EntitiesLimitNotificationRuleTriggerConfig.class, name = "ENTITIES_LIMIT"), - @Type(value = ApiUsageLimitNotificationRuleTriggerConfig.class, name = "API_USAGE_LIMIT"), -}) -public interface NotificationRuleTriggerConfig extends Serializable { - - NotificationRuleTriggerType getTriggerType(); - - @JsonIgnore - default String getDeduplicationKey() { - return "#"; - } - -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerType.java deleted file mode 100644 index dff86f4ba1..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/NotificationRuleTriggerType.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.data.notification.rule.trigger; - -import lombok.Getter; - -@Getter -public enum NotificationRuleTriggerType { - - ENTITY_ACTION, - ALARM, - ALARM_COMMENT, - ALARM_ASSIGNMENT, - DEVICE_ACTIVITY, - RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, - NEW_PLATFORM_VERSION(false), - ENTITIES_LIMIT(false), - API_USAGE_LIMIT(false); - - private final boolean tenantLevel; - - NotificationRuleTriggerType() { - this(true); - } - - NotificationRuleTriggerType(boolean tenantLevel) { - this.tenantLevel = tenantLevel; - } - -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/RateLimitsTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/RateLimitsTrigger.java new file mode 100644 index 0000000000..3941ade824 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/RateLimitsTrigger.java @@ -0,0 +1,62 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.limit.LimitedApi; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; + +import java.util.concurrent.TimeUnit; + +@Data +@Builder +public class RateLimitsTrigger implements NotificationRuleTrigger { + + private final TenantId tenantId; + private final LimitedApi api; + private final EntityId limitLevel; + private final String limitLevelEntityName; + + @Override + public NotificationRuleTriggerType getType() { + return NotificationRuleTriggerType.RATE_LIMITS; + } + + @Override + public EntityId getOriginatorEntityId() { + return limitLevel != null ? limitLevel : tenantId; + } + + + @Override + public boolean deduplicate() { + return true; + } + + @Override + public String getDeduplicationKey() { + return String.join(":", NotificationRuleTrigger.super.getDeduplicationKey(), api.toString()); + } + + @Override + public long getDefaultDeduplicationDuration() { + return TimeUnit.HOURS.toMillis(4); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.java deleted file mode 100644 index c28e190cc9..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.data.notification.rule.trigger; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; - -import java.util.Set; -import java.util.UUID; - -@Data -@AllArgsConstructor -@NoArgsConstructor -@Builder -public class RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { - - private Set ruleChains; // if empty - all rule chains - - private Set ruleChainEvents; // available options: STARTED, UPDATED, STOPPED. if empty - all events - private boolean onlyRuleChainLifecycleFailures; - - private boolean trackRuleNodeEvents; - private Set ruleNodeEvents; // available options: STARTED, UPDATED, STOPPED. if empty - all events - private boolean onlyRuleNodeLifecycleFailures; - - @Override - public NotificationRuleTriggerType getTriggerType() { - return NotificationRuleTriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT; - } - -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/RuleEngineComponentLifecycleEventTrigger.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/RuleEngineComponentLifecycleEventTrigger.java new file mode 100644 index 0000000000..f1509eb2dc --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/RuleEngineComponentLifecycleEventTrigger.java @@ -0,0 +1,48 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; + +@Data +@Builder +public class RuleEngineComponentLifecycleEventTrigger implements NotificationRuleTrigger { + + private final TenantId tenantId; + private final RuleChainId ruleChainId; + private final String ruleChainName; + private final EntityId componentId; + private final String componentName; + private final ComponentLifecycleEvent eventType; + private final Throwable error; + + @Override + public NotificationRuleTriggerType getType() { + return NotificationRuleTriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT; + } + + @Override + public EntityId getOriginatorEntityId() { + return componentId; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmAssignmentNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmAssignmentNotificationRuleTriggerConfig.java new file mode 100644 index 0000000000..ce9ade2e6f --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmAssignmentNotificationRuleTriggerConfig.java @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger.config; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; + +import javax.validation.constraints.NotEmpty; +import java.util.Set; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class AlarmAssignmentNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + + private Set alarmTypes; + private Set alarmSeverities; + private Set alarmStatuses; + @NotEmpty + private Set notifyOn; + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.ALARM_ASSIGNMENT; + } + + public enum Action { + ASSIGNED, UNASSIGNED + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmCommentNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmCommentNotificationRuleTriggerConfig.java new file mode 100644 index 0000000000..f0d8a7ef15 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmCommentNotificationRuleTriggerConfig.java @@ -0,0 +1,44 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger.config; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; + +import java.util.Set; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class AlarmCommentNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + + private Set alarmTypes; + private Set alarmSeverities; + private Set alarmStatuses; + private boolean onlyUserComments; + private boolean notifyOnCommentUpdate; + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.ALARM_COMMENT; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmNotificationRuleTriggerConfig.java new file mode 100644 index 0000000000..318a99ffdd --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmNotificationRuleTriggerConfig.java @@ -0,0 +1,56 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger.config; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; + +import javax.validation.constraints.NotEmpty; +import java.io.Serializable; +import java.util.Set; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class AlarmNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + + private Set alarmTypes; + private Set alarmSeverities; + @NotEmpty + private Set notifyOn; + + private ClearRule clearRule; + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.ALARM; + } + + @Data + public static class ClearRule implements Serializable { + private Set alarmStatuses; + } + + public enum AlarmAction { + CREATED, SEVERITY_CHANGED, ACKNOWLEDGED, CLEARED + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ApiUsageLimitNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ApiUsageLimitNotificationRuleTriggerConfig.java new file mode 100644 index 0000000000..ec49e3828a --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/ApiUsageLimitNotificationRuleTriggerConfig.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger.config; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.ApiFeature; +import org.thingsboard.server.common.data.ApiUsageStateValue; + +import java.util.Set; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ApiUsageLimitNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + + private Set apiFeatures; + private Set notifyOn; + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.API_USAGE_LIMIT; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/DeviceActivityNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/DeviceActivityNotificationRuleTriggerConfig.java new file mode 100644 index 0000000000..7777955912 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/DeviceActivityNotificationRuleTriggerConfig.java @@ -0,0 +1,47 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger.config; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotEmpty; +import java.util.Set; +import java.util.UUID; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class DeviceActivityNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + + private Set devices; + private Set deviceProfiles; // set either devices or profiles + @NotEmpty + private Set notifyOn; + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.DEVICE_ACTIVITY; + } + + public enum DeviceEvent { + ACTIVE, INACTIVE + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EntitiesLimitNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EntitiesLimitNotificationRuleTriggerConfig.java new file mode 100644 index 0000000000..0dfd8d0cc4 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EntitiesLimitNotificationRuleTriggerConfig.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger.config; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.EntityType; + +import javax.validation.constraints.Max; +import java.util.Set; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class EntitiesLimitNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + + private Set entityTypes; + @Max(1) + private float threshold; // in percents, + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.ENTITIES_LIMIT; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EntityActionNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EntityActionNotificationRuleTriggerConfig.java new file mode 100644 index 0000000000..ed61d5eb94 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EntityActionNotificationRuleTriggerConfig.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger.config; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.EntityType; + +import java.util.Set; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class EntityActionNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + + private Set entityTypes; // maybe add name filter ? + private boolean created; + private boolean updated; + private boolean deleted; + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.ENTITY_ACTION; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NewPlatformVersionNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NewPlatformVersionNotificationRuleTriggerConfig.java new file mode 100644 index 0000000000..ebec3967d0 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NewPlatformVersionNotificationRuleTriggerConfig.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger.config; + +import lombok.Data; + +@Data +public class NewPlatformVersionNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.NEW_PLATFORM_VERSION; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerConfig.java new file mode 100644 index 0000000000..db75e395b1 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerConfig.java @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger.config; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +import java.io.Serializable; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "triggerType") +@JsonSubTypes({ + @Type(value = AlarmNotificationRuleTriggerConfig.class, name = "ALARM"), + @Type(value = DeviceActivityNotificationRuleTriggerConfig.class, name = "DEVICE_ACTIVITY"), + @Type(value = EntityActionNotificationRuleTriggerConfig.class, name = "ENTITY_ACTION"), + @Type(value = AlarmCommentNotificationRuleTriggerConfig.class, name = "ALARM_COMMENT"), + @Type(value = RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.class, name = "RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT"), + @Type(value = AlarmAssignmentNotificationRuleTriggerConfig.class, name = "ALARM_ASSIGNMENT"), + @Type(value = NewPlatformVersionNotificationRuleTriggerConfig.class, name = "NEW_PLATFORM_VERSION"), + @Type(value = EntitiesLimitNotificationRuleTriggerConfig.class, name = "ENTITIES_LIMIT"), + @Type(value = ApiUsageLimitNotificationRuleTriggerConfig.class, name = "API_USAGE_LIMIT"), + @Type(value = RateLimitsNotificationRuleTriggerConfig.class, name = "RATE_LIMITS"), +}) +public interface NotificationRuleTriggerConfig extends Serializable { + + NotificationRuleTriggerType getTriggerType(); + + @JsonIgnore + default String getDeduplicationKey() { + return "#"; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerType.java new file mode 100644 index 0000000000..1d73de0d2b --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerType.java @@ -0,0 +1,44 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger.config; + +import lombok.Getter; + +@Getter +public enum NotificationRuleTriggerType { + + ENTITY_ACTION, + ALARM, + ALARM_COMMENT, + ALARM_ASSIGNMENT, + DEVICE_ACTIVITY, + RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, + NEW_PLATFORM_VERSION(false), + ENTITIES_LIMIT(false), + API_USAGE_LIMIT(false), + RATE_LIMITS(false); + + private final boolean tenantLevel; + + NotificationRuleTriggerType() { + this(true); + } + + NotificationRuleTriggerType(boolean tenantLevel) { + this.tenantLevel = tenantLevel; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/RateLimitsNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/RateLimitsNotificationRuleTriggerConfig.java new file mode 100644 index 0000000000..6cb447bc8f --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/RateLimitsNotificationRuleTriggerConfig.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger.config; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.limit.LimitedApi; + +import java.util.Set; +import java.util.stream.Collectors; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class RateLimitsNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + + private Set apis; + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.RATE_LIMITS; + } + + @Override + public String getDeduplicationKey() { + return apis == null ? "#" : apis.stream().sorted().map(Enum::name).collect(Collectors.joining(",")); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.java new file mode 100644 index 0000000000..005d86ac6f --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig.java @@ -0,0 +1,47 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.rule.trigger.config; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; + +import java.util.Set; +import java.util.UUID; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig { + + private Set ruleChains; // if empty - all rule chains + + private Set ruleChainEvents; // available options: STARTED, UPDATED, STOPPED. if empty - all events + private boolean onlyRuleChainLifecycleFailures; + + private boolean trackRuleNodeEvents; + private Set ruleNodeEvents; // available options: STARTED, UPDATED, STOPPED. if empty - all events + private boolean onlyRuleNodeLifecycleFailures; + + @Override + public NotificationRuleTriggerType getTriggerType() { + return NotificationRuleTriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/TriggerTypeConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/TriggerTypeConfig.java deleted file mode 100644 index 6991bb1277..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/TriggerTypeConfig.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.data.notification.settings; - -import lombok.Data; - -@Data -public class TriggerTypeConfig { - private long deduplicationDuration; -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/UserNotificationSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/UserNotificationSettings.java new file mode 100644 index 0000000000..0ad7cc6d78 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/UserNotificationSettings.java @@ -0,0 +1,82 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.settings; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; +import org.thingsboard.server.common.data.notification.NotificationType; +import org.thingsboard.server.common.data.notification.targets.NotificationTargetType; + +import javax.validation.Valid; +import javax.validation.constraints.AssertTrue; +import javax.validation.constraints.NotNull; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +@Data +public class UserNotificationSettings { + + @NotNull + @Valid + private final Map prefs; + + public static final UserNotificationSettings DEFAULT = new UserNotificationSettings(Collections.emptyMap()); + + public static final Set deliveryMethods = NotificationTargetType.PLATFORM_USERS.getSupportedDeliveryMethods(); + + @JsonCreator + public UserNotificationSettings(@JsonProperty("prefs") Map prefs) { + this.prefs = prefs; + } + + public boolean isEnabled(NotificationType notificationType, NotificationDeliveryMethod deliveryMethod) { + NotificationPref pref = prefs.get(notificationType); + if (pref == null) { + return true; + } + if (!pref.isEnabled()) { + return false; + } + return pref.getEnabledDeliveryMethods().getOrDefault(deliveryMethod, true); + } + + @Data + public static class NotificationPref { + private boolean enabled; + @NotNull + private Map enabledDeliveryMethods; + + public static NotificationPref createDefault() { + NotificationPref pref = new NotificationPref(); + pref.setEnabled(true); + pref.setEnabledDeliveryMethods(deliveryMethods.stream().collect(Collectors.toMap(v -> v, v -> true))); + return pref; + } + + @JsonIgnore + @AssertTrue(message = "Only email, Web and SMS delivery methods are allowed") + public boolean isValid() { + return enabledDeliveryMethods.entrySet().stream() + .allMatch(entry -> deliveryMethods.contains(entry.getKey()) && entry.getValue() != null); + } + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/MicrosoftTeamsNotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/MicrosoftTeamsNotificationTargetConfig.java new file mode 100644 index 0000000000..826f64bbd4 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/MicrosoftTeamsNotificationTargetConfig.java @@ -0,0 +1,48 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.targets; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; + +@Data +@EqualsAndHashCode(callSuper = true) +public class MicrosoftTeamsNotificationTargetConfig extends NotificationTargetConfig implements NotificationRecipient { + + @NotBlank + private String webhookUrl; + @NotEmpty + private String channelName; + + @Override + public NotificationTargetType getType() { + return NotificationTargetType.MICROSOFT_TEAMS; + } + + @Override + public Object getId() { + return webhookUrl; + } + + @Override + public String getTitle() { + return channelName; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationRecipient.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationRecipient.java index 940d596332..3fbe7173fd 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationRecipient.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationRecipient.java @@ -21,10 +21,16 @@ public interface NotificationRecipient { String getTitle(); - String getFirstName(); + default String getFirstName() { + return null; + } - String getLastName(); + default String getLastName() { + return null; + } - String getEmail(); + default String getEmail() { + return null; + } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java index 231e419fb0..529167c637 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetConfig.java @@ -30,7 +30,8 @@ import org.thingsboard.server.common.data.validation.NoXss; @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @JsonSubTypes({ @Type(value = PlatformUsersNotificationTargetConfig.class, name = "PLATFORM_USERS"), - @Type(value = SlackNotificationTargetConfig.class, name = "SLACK") + @Type(value = SlackNotificationTargetConfig.class, name = "SLACK"), + @Type(value = MicrosoftTeamsNotificationTargetConfig.class, name = "MICROSOFT_TEAMS") }) @Data public abstract class NotificationTargetConfig { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java index 2180679cb2..2d0257d4a7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTargetType.java @@ -26,7 +26,8 @@ import java.util.Set; public enum NotificationTargetType { PLATFORM_USERS(Set.of(NotificationDeliveryMethod.WEB, NotificationDeliveryMethod.EMAIL, NotificationDeliveryMethod.SMS, NotificationDeliveryMethod.MOBILE)), - SLACK(Set.of(NotificationDeliveryMethod.SLACK)); + SLACK(Set.of(NotificationDeliveryMethod.SLACK)), + MICROSOFT_TEAMS(Set.of(NotificationDeliveryMethod.MICROSOFT_TEAMS)); @Getter private final Set supportedDeliveryMethods; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java index f033fe5092..6e5b6d2b0c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java @@ -22,10 +22,10 @@ import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.Data; import lombok.NoArgsConstructor; -import org.apache.commons.lang3.StringUtils; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import javax.validation.constraints.NotEmpty; +import java.util.List; @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "method") @@ -34,6 +34,7 @@ import javax.validation.constraints.NotEmpty; @Type(name = "EMAIL", value = EmailDeliveryMethodNotificationTemplate.class), @Type(name = "SMS", value = SmsDeliveryMethodNotificationTemplate.class), @Type(name = "SLACK", value = SlackDeliveryMethodNotificationTemplate.class), + @Type(name = "MICROSOFT_TEAMS", value = MicrosoftTeamsDeliveryMethodNotificationTemplate.class), @Type(name = "MOBILE", value = MobileDeliveryMethodNotificationTemplate.class) }) @Data @@ -42,7 +43,7 @@ public abstract class DeliveryMethodNotificationTemplate { private boolean enabled; @NotEmpty - private String body; + protected String body; public DeliveryMethodNotificationTemplate(DeliveryMethodNotificationTemplate other) { this.enabled = other.enabled; @@ -55,8 +56,7 @@ public abstract class DeliveryMethodNotificationTemplate { @JsonIgnore public abstract DeliveryMethodNotificationTemplate copy(); - public boolean containsAny(String... params) { - return StringUtils.containsAny(body, params); - } + @JsonIgnore + public abstract List getTemplatableValues(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java index fe909a104e..4ac2e1fb17 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java @@ -19,12 +19,12 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; -import org.apache.commons.lang3.StringUtils; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; import javax.validation.constraints.NotEmpty; +import java.util.List; @Data @NoArgsConstructor @@ -37,6 +37,11 @@ public class EmailDeliveryMethodNotificationTemplate extends DeliveryMethodNotif @NotEmpty private String subject; + private final List templatableValues = List.of( + TemplatableValue.of(this::getBody, this::setBody), + TemplatableValue.of(this::getSubject, this::setSubject) + ); + public EmailDeliveryMethodNotificationTemplate(EmailDeliveryMethodNotificationTemplate other) { super(other); this.subject = other.subject; @@ -52,9 +57,4 @@ public class EmailDeliveryMethodNotificationTemplate extends DeliveryMethodNotif return new EmailDeliveryMethodNotificationTemplate(this); } - @Override - public boolean containsAny(String... params) { - return super.containsAny(params) || StringUtils.containsAny(subject, params); - } - } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/MicrosoftTeamsDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/MicrosoftTeamsDeliveryMethodNotificationTemplate.java new file mode 100644 index 0000000000..4a3df6287e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/MicrosoftTeamsDeliveryMethodNotificationTemplate.java @@ -0,0 +1,90 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.template; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; + +import java.util.List; +import java.util.UUID; + +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@ToString(callSuper = true) +public class MicrosoftTeamsDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate implements HasSubject { + + private String subject; + private String themeColor; + private Button button; + + private final List templatableValues = List.of( + TemplatableValue.of(this::getBody, this::setBody), + TemplatableValue.of(this::getSubject, this::setSubject), + TemplatableValue.of(() -> button != null ? button.getText() : null, + processed -> { if (button != null) button.setText(processed); }), + TemplatableValue.of(() -> button != null ? button.getLink() : null, + processed -> { if (button != null) button.setLink(processed); }) + ); + + public MicrosoftTeamsDeliveryMethodNotificationTemplate(MicrosoftTeamsDeliveryMethodNotificationTemplate other) { + super(other); + this.subject = other.subject; + this.themeColor = other.themeColor; + this.button = other.button != null ? new Button(other.button) : null; + } + + @Override + public NotificationDeliveryMethod getMethod() { + return NotificationDeliveryMethod.MICROSOFT_TEAMS; + } + + @Override + public MicrosoftTeamsDeliveryMethodNotificationTemplate copy() { + return new MicrosoftTeamsDeliveryMethodNotificationTemplate(this); + } + + @Data + @NoArgsConstructor + public static class Button { + private boolean enabled; + private String text; + private LinkType linkType; + private String link; + + private UUID dashboardId; + private String dashboardState; + private boolean setEntityIdInState; + + public Button(Button other) { + this.enabled = other.enabled; + this.text = other.text; + this.linkType = other.linkType; + this.link = other.link; + this.dashboardId = other.dashboardId; + this.dashboardState = other.dashboardState; + this.setEntityIdInState = other.setEntityIdInState; + } + + public enum LinkType { + LINK, DASHBOARD + } + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java index 8457677308..6a3790feb6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SlackDeliveryMethodNotificationTemplate.java @@ -22,12 +22,18 @@ import lombok.ToString; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.validation.NoXss; +import java.util.List; + @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) public class SlackDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate { + private final List templatableValues = List.of( + TemplatableValue.of(this::getBody, this::setBody) + ); + public SlackDeliveryMethodNotificationTemplate(DeliveryMethodNotificationTemplate other) { super(other); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SmsDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SmsDeliveryMethodNotificationTemplate.java index 7dc2e494f3..f4aab10e1e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SmsDeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/SmsDeliveryMethodNotificationTemplate.java @@ -23,12 +23,18 @@ import org.thingsboard.server.common.data.notification.NotificationDeliveryMetho import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; +import java.util.List; + @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) public class SmsDeliveryMethodNotificationTemplate extends DeliveryMethodNotificationTemplate { + private final List templatableValues = List.of( + TemplatableValue.of(this::getBody, this::setBody) + ); + public SmsDeliveryMethodNotificationTemplate(SmsDeliveryMethodNotificationTemplate other) { super(other); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/TemplatableValue.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/TemplatableValue.java new file mode 100644 index 0000000000..25dfeb643a --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/TemplatableValue.java @@ -0,0 +1,46 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.notification.template; + +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; + +import java.util.Collection; +import java.util.function.Consumer; +import java.util.function.Supplier; + +@RequiredArgsConstructor +public class TemplatableValue { + private final Supplier getter; + private final Consumer setter; + + public static TemplatableValue of(Supplier getter, Consumer setter) { + return new TemplatableValue(getter, setter); + } + + public String get() { + return getter.get(); + } + + public void set(String processed) { + setter.accept(processed); + } + + public boolean containsParams(Collection params) { + return StringUtils.containsAny(get(), params.toArray(String[]::new)); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java index f26ed00568..e3a8ab4d8c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java @@ -23,12 +23,12 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; -import org.apache.commons.lang3.StringUtils; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; import javax.validation.constraints.NotEmpty; +import java.util.List; import java.util.Optional; @Data @@ -43,13 +43,19 @@ public class WebDeliveryMethodNotificationTemplate extends DeliveryMethodNotific private String subject; private JsonNode additionalConfig; + private final List templatableValues = List.of( + TemplatableValue.of(this::getBody, this::setBody), + TemplatableValue.of(this::getSubject, this::setSubject), + TemplatableValue.of(this::getButtonText, this::setButtonText), + TemplatableValue.of(this::getButtonLink, this::setButtonLink) + ); + public WebDeliveryMethodNotificationTemplate(WebDeliveryMethodNotificationTemplate other) { super(other); this.subject = other.subject; this.additionalConfig = other.additionalConfig != null ? other.additionalConfig.deepCopy() : null; } - @NoXss(fieldName = "web notification message") @Length(fieldName = "web notification message", max = 250, message = "cannot be longer than 250 chars") @Override public String getBody() { @@ -107,10 +113,4 @@ public class WebDeliveryMethodNotificationTemplate extends DeliveryMethodNotific return new WebDeliveryMethodNotificationTemplate(this); } - @Override - public boolean containsAny(String... params) { - return super.containsAny(params) || StringUtils.containsAny(subject, params) - || StringUtils.containsAny(getButtonText(), params) || StringUtils.containsAny(getButtonLink(), params); - } - } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/page/PageData.java b/common/data/src/main/java/org/thingsboard/server/common/data/page/PageData.java index 2ae94a0064..dd49aa7d97 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/page/PageData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/page/PageData.java @@ -19,14 +19,19 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +import lombok.EqualsAndHashCode; +import java.io.Serializable; import java.util.Collections; import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; @ApiModel -public class PageData { +@EqualsAndHashCode +public class PageData implements Serializable { + + public static final PageData EMPTY_PAGE_DATA = new PageData<>(); private final List data; private final int totalPages; @@ -48,6 +53,11 @@ public class PageData { this.hasNext = hasNext; } + @SuppressWarnings("unchecked") + public static PageData emptyPageData() { + return (PageData) EMPTY_PAGE_DATA; + } + @ApiModelProperty(position = 1, value = "Array of the entities", accessMode = ApiModelProperty.AccessMode.READ_ONLY) public List getData() { return data; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/page/SortOrder.java b/common/data/src/main/java/org/thingsboard/server/common/data/page/SortOrder.java index 5c540995aa..62a6377519 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/page/SortOrder.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/page/SortOrder.java @@ -36,4 +36,6 @@ public class SortOrder { ASC, DESC } + public static final SortOrder BY_CREATED_TIME_DESC = new SortOrder("createdTime", Direction.DESC); + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentDescriptor.java b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentDescriptor.java index 1c186f2697..aab0a8f858 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentDescriptor.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentDescriptor.java @@ -26,6 +26,8 @@ import org.thingsboard.server.common.data.BaseDataWithAdditionalInfo; import org.thingsboard.server.common.data.id.ComponentDescriptorId; import org.thingsboard.server.common.data.validation.Length; +import java.util.Objects; + /** * @author Andrew Shvayka */ @@ -48,8 +50,10 @@ public class ComponentDescriptor extends BaseData { @Getter @Setter private String clazz; @ApiModelProperty(position = 8, value = "Complex JSON object that represents the Rule Node configuration.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Getter @Setter private transient JsonNode configurationDescriptor; + @ApiModelProperty(position = 9, value = "Rule node configuration version. By default, this value is 0. If the rule node is a versioned node, this value might be greater than 0.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + @Getter @Setter private int configurationVersion; @Length(fieldName = "actions") - @ApiModelProperty(position = 9, value = "Rule Node Actions. Deprecated. Always null.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + @ApiModelProperty(position = 10, value = "Rule Node Actions. Deprecated. Always null.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @Getter @Setter private String actions; public ComponentDescriptor() { @@ -64,9 +68,11 @@ public class ComponentDescriptor extends BaseData { super(plugin); this.type = plugin.getType(); this.scope = plugin.getScope(); + this.clusteringMode = plugin.getClusteringMode(); this.name = plugin.getName(); this.clazz = plugin.getClazz(); this.configurationDescriptor = plugin.getConfigurationDescriptor(); + this.configurationVersion = plugin.getConfigurationVersion(); this.actions = plugin.getActions(); } @@ -94,10 +100,11 @@ public class ComponentDescriptor extends BaseData { if (type != that.type) return false; if (scope != that.scope) return false; - if (name != null ? !name.equals(that.name) : that.name != null) return false; - if (actions != null ? !actions.equals(that.actions) : that.actions != null) return false; - if (configurationDescriptor != null ? !configurationDescriptor.equals(that.configurationDescriptor) : that.configurationDescriptor != null) return false; - return clazz != null ? clazz.equals(that.clazz) : that.clazz == null; + if (!Objects.equals(name, that.name)) return false; + if (!Objects.equals(actions, that.actions)) return false; + if (!Objects.equals(configurationDescriptor, that.configurationDescriptor)) return false; + if (configurationVersion != that.configurationVersion) return false; + return Objects.equals(clazz, that.clazz); } @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleEvent.java b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleEvent.java index a7ae9599ea..cbcf94445b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleEvent.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/plugin/ComponentLifecycleEvent.java @@ -21,5 +21,6 @@ import java.io.Serializable; * @author Andrew Shvayka */ public enum ComponentLifecycleEvent implements Serializable { - CREATED, STARTED, ACTIVATED, SUSPENDED, UPDATED, STOPPED, DELETED -} \ No newline at end of file + // In sync with ComponentLifecycleEvent proto + CREATED, STARTED, ACTIVATED, SUSPENDED, UPDATED, STOPPED, DELETED, FAILED, DEACTIVATED +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/queue/ProcessingStrategy.java b/common/data/src/main/java/org/thingsboard/server/common/data/queue/ProcessingStrategy.java index e1889ed759..4ad8265c22 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/queue/ProcessingStrategy.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/queue/ProcessingStrategy.java @@ -17,8 +17,13 @@ package org.thingsboard.server.common.data.queue; import lombok.Data; +import java.io.Serializable; + @Data -public class ProcessingStrategy { +public class ProcessingStrategy implements Serializable { + + private static final long serialVersionUID = 293598327194813L; + private ProcessingStrategyType type; private int retries; private double failurePercentage; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/queue/Queue.java b/common/data/src/main/java/org/thingsboard/server/common/data/queue/Queue.java index 6839c60bcd..5b613a7c05 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/queue/Queue.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/queue/Queue.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data.queue; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.JsonNode; import lombok.Data; import org.thingsboard.server.common.data.BaseDataWithAdditionalInfo; import org.thingsboard.server.common.data.HasName; @@ -25,6 +27,8 @@ import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfi import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; +import java.util.Optional; + @Data public class Queue extends BaseDataWithAdditionalInfo implements HasName, HasTenantId { private TenantId tenantId; @@ -60,4 +64,13 @@ public class Queue extends BaseDataWithAdditionalInfo implements HasNam this.processingStrategy = queueConfiguration.getProcessingStrategy(); setAdditionalInfo(queueConfiguration.getAdditionalInfo()); } -} \ No newline at end of file + + + @JsonIgnore + public String getCustomProperties() { + return Optional.ofNullable(getAdditionalInfo()) + .map(info -> info.get("customProperties")) + .filter(JsonNode::isTextual).map(JsonNode::asText).orElse(null); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/queue/SubmitStrategy.java b/common/data/src/main/java/org/thingsboard/server/common/data/queue/SubmitStrategy.java index 18198ee6d3..63461951c4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/queue/SubmitStrategy.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/queue/SubmitStrategy.java @@ -17,8 +17,13 @@ package org.thingsboard.server.common.data.queue; import lombok.Data; +import java.io.Serializable; + @Data -public class SubmitStrategy { +public class SubmitStrategy implements Serializable { + + private static final long serialVersionUID = 2938457983298400L; + private SubmitStrategyType type; private int batchSize; } \ No newline at end of file diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rpc/RpcStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/rpc/RpcStatus.java index 58a7b3c707..be6d4767ec 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rpc/RpcStatus.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rpc/RpcStatus.java @@ -15,6 +15,24 @@ */ package org.thingsboard.server.common.data.rpc; +import lombok.Getter; + public enum RpcStatus { - QUEUED, SENT, DELIVERED, SUCCESSFUL, TIMEOUT, EXPIRED, FAILED, DELETED + + QUEUED(true), + SENT(true), + DELIVERED(true), + SUCCESSFUL(false), + TIMEOUT(false), + EXPIRED(false), + FAILED(false), + DELETED(false); + + @Getter + private final boolean pushDeleteNotificationToCore; + + RpcStatus(boolean pushDeleteNotificationToCore) { + this.pushDeleteNotificationToCore = pushDeleteNotificationToCore; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java index 16a127e48a..3fc1ceb628 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java @@ -50,7 +50,9 @@ public class RuleNode extends BaseDataWithAdditionalInfo implements private boolean debugMode; @ApiModelProperty(position = 7, value = "Enable/disable singleton mode. ", example = "false") private boolean singletonMode; - @ApiModelProperty(position = 8, value = "JSON with the rule node configuration. Structure depends on the rule node implementation.", dataType = "com.fasterxml.jackson.databind.JsonNode") + @ApiModelProperty(position = 8, value = "Version of rule node configuration. ", example = "0") + private int configurationVersion; + @ApiModelProperty(position = 9, value = "JSON with the rule node configuration. Structure depends on the rule node implementation.", dataType = "com.fasterxml.jackson.databind.JsonNode") private transient JsonNode configuration; @JsonIgnore private byte[] configurationBytes; @@ -104,7 +106,7 @@ public class RuleNode extends BaseDataWithAdditionalInfo implements return super.getCreatedTime(); } - @ApiModelProperty(position = 8, value = "Additional parameters of the rule node. Contains 'layoutX' and 'layoutY' properties for visualization.", dataType = "com.fasterxml.jackson.databind.JsonNode") + @ApiModelProperty(position = 10, value = "Additional parameters of the rule node. Contains 'layoutX' and 'layoutY' properties for visualization.", dataType = "com.fasterxml.jackson.databind.JsonNode") @Override public JsonNode getAdditionalInfo() { return super.getAdditionalInfo(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/UserPasswordPolicy.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/UserPasswordPolicy.java index 881af89e27..fd067287b5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/UserPasswordPolicy.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/UserPasswordPolicy.java @@ -27,6 +27,8 @@ public class UserPasswordPolicy implements Serializable { @ApiModelProperty(position = 1, value = "Minimum number of symbols in the password." ) private Integer minimumLength; + @ApiModelProperty(position = 1, value = "Maximum number of symbols in the password." ) + private Integer maximumLength; @ApiModelProperty(position = 1, value = "Minimum number of uppercase letters in the password." ) private Integer minimumUppercaseLetters; @ApiModelProperty(position = 1, value = "Minimum number of lowercase letters in the password." ) @@ -37,6 +39,8 @@ public class UserPasswordPolicy implements Serializable { private Integer minimumSpecialCharacters; @ApiModelProperty(position = 1, value = "Allow whitespaces") private Boolean allowWhitespaces = true; + @ApiModelProperty(position = 1, value = "Force user to update password if existing one does not pass validation") + private Boolean forceUserToResetPasswordIfNotValid = false; @ApiModelProperty(position = 1, value = "Password expiration period (days). Force expiration of the password." ) private Integer passwordExpirationPeriodDays; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/settings/UserSettingsType.java b/common/data/src/main/java/org/thingsboard/server/common/data/settings/UserSettingsType.java index b19dbbbaee..cd627821ac 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/settings/UserSettingsType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/settings/UserSettingsType.java @@ -19,7 +19,7 @@ import lombok.Getter; public enum UserSettingsType { - GENERAL, VISITED_DASHBOARDS(true), QUICK_LINKS, DOC_LINKS, DASHBOARDS, GETTING_STARTED; + GENERAL, VISITED_DASHBOARDS(true), QUICK_LINKS, DOC_LINKS, DASHBOARDS, GETTING_STARTED, NOTIFICATIONS; @Getter private final boolean reserved; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sync/JsonTbEntity.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/JsonTbEntity.java index 23a98b395b..10f20cc973 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sync/JsonTbEntity.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/JsonTbEntity.java @@ -25,12 +25,14 @@ import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetsBundle; import java.lang.annotation.ElementType; @@ -52,9 +54,11 @@ import java.lang.annotation.Target; @Type(name = "CUSTOMER", value = Customer.class), @Type(name = "ENTITY_VIEW", value = EntityView.class), @Type(name = "WIDGETS_BUNDLE", value = WidgetsBundle.class), + @Type(name = "WIDGET_TYPE", value = WidgetTypeDetails.class), @Type(name = "NOTIFICATION_TEMPLATE", value = NotificationTemplate.class), @Type(name = "NOTIFICATION_TARGET", value = NotificationTarget.class), - @Type(name = "NOTIFICATION_RULE", value = NotificationRule.class) + @Type(name = "NOTIFICATION_RULE", value = NotificationRule.class), + @Type(name = "TB_RESOURCE", value = TbResource.class) }) @JsonIgnoreProperties(value = {"tenantId", "createdTime"}, ignoreUnknown = true) public @interface JsonTbEntity { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/EntityExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/EntityExportData.java index 6518a485be..7445ba4da5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/EntityExportData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/EntityExportData.java @@ -39,6 +39,7 @@ import java.util.Map; @JsonSubTypes({ @Type(name = "DEVICE", value = DeviceExportData.class), @Type(name = "RULE_CHAIN", value = RuleChainExportData.class), + @Type(name = "WIDGET_TYPE", value = WidgetTypeExportData.class), @Type(name = "WIDGETS_BUNDLE", value = WidgetsBundleExportData.class) }) @JsonInclude(JsonInclude.Include.NON_NULL) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/WidgetTypeExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/WidgetTypeExportData.java new file mode 100644 index 0000000000..37714e0712 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/WidgetTypeExportData.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.sync.ie; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.widget.WidgetTypeDetails; + +@Data +@EqualsAndHashCode(callSuper = true) +public class WidgetTypeExportData extends EntityExportData { + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/WidgetsBundleExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/WidgetsBundleExportData.java index 55a684df04..89d509159d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/WidgetsBundleExportData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/WidgetsBundleExportData.java @@ -16,13 +16,12 @@ package org.thingsboard.server.common.data.sync.ie; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Data; import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.widget.BaseWidgetType; -import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetsBundle; -import java.util.Comparator; +import java.util.ArrayList; import java.util.List; @Data @@ -30,13 +29,16 @@ import java.util.List; public class WidgetsBundleExportData extends EntityExportData { @JsonProperty(index = 3) - private List widgets; + private List widgets; - @Override - public EntityExportData sort() { - super.sort(); - widgets.sort(Comparator.comparing(BaseWidgetType::getAlias)); - return this; + @JsonProperty(index = 4) + private List fqns; + + public void addFqn(String fqn) { + if (fqns == null) { + fqns = new ArrayList<>(); + } + fqns.add(fqn); } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportColumnType.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportColumnType.java index c004187876..87582b4d6d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportColumnType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportColumnType.java @@ -43,6 +43,10 @@ public enum BulkImportColumnType { LWM2M_SERVER_SECURITY_MODE("securityMode", LwM2MSecurityMode.NO_SEC.name()), LWM2M_SERVER_CLIENT_PUBLIC_KEY_OR_ID("clientPublicKeyOrId"), LWM2M_SERVER_CLIENT_SECRET_KEY("clientSecretKey"), + SNMP_HOST, + SNMP_PORT, + SNMP_VERSION, + SNMP_COMMUNITY_STRING, IS_GATEWAY, DESCRIPTION, ROUTING_KEY, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java index d84c87ac51..dfc6f5206b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java @@ -29,6 +29,8 @@ import org.thingsboard.server.common.data.TenantProfileType; @Data public class DefaultTenantProfileConfiguration implements TenantProfileConfiguration { + private static final long serialVersionUID = -7134932690332578595L; + private long maxDevices; private long maxAssets; private long maxCustomers; @@ -37,6 +39,7 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura private long maxRuleChains; private long maxResourcesInBytes; private long maxOtaPackagesInBytes; + private long maxResourceSize; private String transportTenantMsgRateLimit; private String transportTenantTelemetryMsgRateLimit; @@ -54,6 +57,7 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura private long maxTransportDataPoints; private long maxREExecutions; private long maxJSExecutions; + private long maxTbelExecutions; private long maxDPStorageDays; private int maxRuleNodeExecutionsPerMessage; private long maxEmails; @@ -80,6 +84,8 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura private int defaultStorageTtlDays; private int alarmsTtlDays; private int rpcTtlDays; + private int queueStatsTtlDays; + private int ruleEngineExceptionsTtlDays; private double warnThreshold; @@ -92,6 +98,8 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura return maxTransportDataPoints; case JS_EXEC_COUNT: return maxJSExecutions; + case TBEL_EXEC_COUNT: + return maxTbelExecutions; case RE_EXEC_COUNT: return maxREExecutions; case STORAGE_DP_COUNT: diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileConfiguration.java index dacff693b7..a30be83b76 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileConfiguration.java @@ -22,6 +22,8 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.TenantProfileType; +import java.io.Serializable; + @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, @@ -29,7 +31,7 @@ import org.thingsboard.server.common.data.TenantProfileType; property = "type") @JsonSubTypes({ @JsonSubTypes.Type(value = DefaultTenantProfileConfiguration.class, name = "DEFAULT")}) -public interface TenantProfileConfiguration { +public interface TenantProfileConfiguration extends Serializable { @JsonIgnore TenantProfileType getType(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileData.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileData.java index 1def815cff..a863b34dfa 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileData.java @@ -19,11 +19,14 @@ import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import java.io.Serializable; import java.util.List; @ApiModel @Data -public class TenantProfileData { +public class TenantProfileData implements Serializable { + + private static final long serialVersionUID = -3642550257035920976L; @ApiModelProperty(position = 1, value = "Complex JSON object that contains profile settings: max devices, max assets, rate limits, etc.") private TenantProfileConfiguration configuration; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileQueueConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileQueueConfiguration.java index d73e7fd486..46eaa82743 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileQueueConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileQueueConfiguration.java @@ -20,8 +20,13 @@ import lombok.Data; import org.thingsboard.server.common.data.queue.ProcessingStrategy; import org.thingsboard.server.common.data.queue.SubmitStrategy; +import java.io.Serializable; + @Data -public class TenantProfileQueueConfiguration { +public class TenantProfileQueueConfiguration implements Serializable { + + private static final long serialVersionUID = -546600745123197362L; + private String name; private String topic; private int pollInterval; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java index a779d9a23d..1e0e5a745b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpCommunicationSpec.java @@ -15,11 +15,21 @@ */ package org.thingsboard.server.common.data.transport.snmp; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter public enum SnmpCommunicationSpec { - TELEMETRY_QUERYING, - CLIENT_ATTRIBUTES_QUERYING, - SHARED_ATTRIBUTES_SETTING, + TELEMETRY_QUERYING("telemetryQuerying"), + CLIENT_ATTRIBUTES_QUERYING("clientAttributesQuerying"), + + SHARED_ATTRIBUTES_SETTING("sharedAttributesSetting"), + TO_DEVICE_RPC_REQUEST("toDeviceRpcRequest"), + + TO_SERVER_RPC_REQUEST("toServerRpcRequest"); + + private final String label; - TO_DEVICE_RPC_REQUEST, } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMethod.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMethod.java index 257dd28217..e366cdb8b4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMethod.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/SnmpMethod.java @@ -17,7 +17,8 @@ package org.thingsboard.server.common.data.transport.snmp; public enum SnmpMethod { GET(-96), - SET(-93); + SET(-93), + TRAP(-89); // codes taken from org.snmp4j.PDU class private final int code; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/SnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/SnmpCommunicationConfig.java index 36a7f64d3a..d09c9c147c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/SnmpCommunicationConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/SnmpCommunicationConfig.java @@ -37,7 +37,8 @@ import java.util.List; @Type(value = TelemetryQueryingSnmpCommunicationConfig.class, name = "TELEMETRY_QUERYING"), @Type(value = ClientAttributesQueryingSnmpCommunicationConfig.class, name = "CLIENT_ATTRIBUTES_QUERYING"), @Type(value = SharedAttributesSettingSnmpCommunicationConfig.class, name = "SHARED_ATTRIBUTES_SETTING"), - @Type(value = ToDeviceRpcRequestSnmpCommunicationConfig.class, name = "TO_DEVICE_RPC_REQUEST") + @Type(value = ToDeviceRpcRequestSnmpCommunicationConfig.class, name = "TO_DEVICE_RPC_REQUEST"), + @Type(value = ToServerRpcRequestSnmpCommunicationConfig.class, name = "TO_SERVER_RPC_REQUEST") }) public interface SnmpCommunicationConfig extends Serializable { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/ToServerRpcRequestSnmpCommunicationConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/ToServerRpcRequestSnmpCommunicationConfig.java new file mode 100644 index 0000000000..f8a7968b82 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/transport/snmp/config/ToServerRpcRequestSnmpCommunicationConfig.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.transport.snmp.config; + +import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; + +public class ToServerRpcRequestSnmpCommunicationConfig extends MultipleMappingsSnmpCommunicationConfig { + + private static final long serialVersionUID = 4851028734093214L; + + @Override + public SnmpCommunicationSpec getSpec() { + return SnmpCommunicationSpec.TO_SERVER_RPC_REQUEST; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/util/ThrowingSupplier.java b/common/data/src/main/java/org/thingsboard/server/common/data/util/ThrowingSupplier.java new file mode 100644 index 0000000000..0e9a74dd15 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/util/ThrowingSupplier.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.util; + +@FunctionalInterface +public interface ThrowingSupplier { + + T get() throws Exception; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/util/TypeCastUtil.java b/common/data/src/main/java/org/thingsboard/server/common/data/util/TypeCastUtil.java new file mode 100644 index 0000000000..cc40c0d13f --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/util/TypeCastUtil.java @@ -0,0 +1,73 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.util; + +import org.apache.commons.lang3.math.NumberUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.thingsboard.server.common.data.kv.DataType; + +import java.math.BigDecimal; + +public class TypeCastUtil { + + private TypeCastUtil() {} + + public static Pair castValue(String value) { + if (isNumber(value)) { + String formattedValue = value.replace(',', '.'); + try { + BigDecimal bd = new BigDecimal(formattedValue); + if (bd.stripTrailingZeros().scale() > 0 || isSimpleDouble(formattedValue)) { + if (bd.scale() <= 16) { + return Pair.of(DataType.DOUBLE, bd.doubleValue()); + } + } else { + return Pair.of(DataType.LONG, bd.longValueExact()); + } + } catch (RuntimeException ignored) {} + } else if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) { + return Pair.of(DataType.BOOLEAN, Boolean.parseBoolean(value)); + } + return Pair.of(DataType.STRING, value); + } + + public static Pair castToNumber(String value) { + if (isNumber(value)) { + String formattedValue = value.replace(',', '.'); + BigDecimal bd = new BigDecimal(formattedValue); + if (bd.stripTrailingZeros().scale() > 0 || isSimpleDouble(formattedValue)) { + if (bd.scale() <= 16) { + return Pair.of(DataType.DOUBLE, bd.doubleValue()); + } else { + return Pair.of(DataType.DOUBLE, bd); + } + } else { + return Pair.of(DataType.LONG, bd.longValueExact()); + } + } else { + throw new IllegalArgumentException("'" + value + "' can't be parsed as number"); + } + } + + private static boolean isNumber(String value) { + return NumberUtils.isNumber(value.replace(',', '.')); + } + + private static boolean isSimpleDouble(String valueAsString) { + return valueAsString.contains(".") && !valueAsString.contains("E") && !valueAsString.contains("e"); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/BaseWidgetType.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/BaseWidgetType.java index a7e9fee82a..e26f0ca6e5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/widget/BaseWidgetType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/widget/BaseWidgetType.java @@ -33,18 +33,17 @@ public class BaseWidgetType extends BaseData implements HasName, H @ApiModelProperty(position = 3, value = "JSON object with Tenant Id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private TenantId tenantId; @NoXss - @Length(fieldName = "bundleAlias") - @ApiModelProperty(position = 4, value = "Reference to widget bundle", accessMode = ApiModelProperty.AccessMode.READ_ONLY) - private String bundleAlias; - @NoXss - @Length(fieldName = "alias") - @ApiModelProperty(position = 5, value = "Unique alias that is used in dashboards as a reference widget type", accessMode = ApiModelProperty.AccessMode.READ_ONLY) - private String alias; + @Length(fieldName = "fqn") + @ApiModelProperty(position = 5, value = "Unique FQN that is used in dashboards as a reference widget type", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + private String fqn; @NoXss @Length(fieldName = "name") - @ApiModelProperty(position = 6, value = "Widget name used in search and UI", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + @ApiModelProperty(position = 6, value = "Widget name used in search and UI") private String name; + @ApiModelProperty(position = 7, value = "Whether widget type is deprecated.", example = "true") + private boolean deprecated; + public BaseWidgetType() { super(); } @@ -56,9 +55,9 @@ public class BaseWidgetType extends BaseData implements HasName, H public BaseWidgetType(BaseWidgetType widgetType) { super(widgetType); this.tenantId = widgetType.getTenantId(); - this.bundleAlias = widgetType.getBundleAlias(); - this.alias = widgetType.getAlias(); + this.fqn = widgetType.getFqn(); this.name = widgetType.getName(); + this.deprecated = widgetType.isDeprecated(); } @ApiModelProperty(position = 1, value = "JSON object with the Widget Type Id. " + diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/DeprecatedFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/DeprecatedFilter.java new file mode 100644 index 0000000000..8ceb0d48a9 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/widget/DeprecatedFilter.java @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.widget; + +public enum DeprecatedFilter { + ALL, + ACTUAL, + DEPRECATED +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java index f726cfb71a..ddf1b6b03f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.WidgetTypeId; @Data public class WidgetType extends BaseWidgetType { - @ApiModelProperty(position = 7, value = "Complex JSON object that describes the widget type", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + @ApiModelProperty(position = 8, value = "Complex JSON object that describes the widget type") private transient JsonNode descriptor; public WidgetType() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeDetails.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeDetails.java index e6faece85d..02992421eb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeDetails.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeDetails.java @@ -18,21 +18,33 @@ package org.thingsboard.server.common.data.widget; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.HasImage; +import org.thingsboard.server.common.data.HasName; +import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; @Data -@JsonPropertyOrder({ "alias", "name", "image", "description", "descriptor" }) -public class WidgetTypeDetails extends WidgetType { +@JsonPropertyOrder({ "fqn", "name", "deprecated", "image", "description", "descriptor", "externalId" }) +public class WidgetTypeDetails extends WidgetType implements HasName, HasTenantId, HasImage, ExportableEntity { - @Length(fieldName = "image", max = 1000000) - @ApiModelProperty(position = 8, value = "Base64 encoded thumbnail", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + @ApiModelProperty(position = 9, value = "Relative or external image URL. Replaced with image data URL (Base64) in case of relative URL and 'inlineImages' option enabled.") private String image; @NoXss - @Length(fieldName = "description") - @ApiModelProperty(position = 9, value = "Description of the widget", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + @Length(fieldName = "description", max = 1024) + @ApiModelProperty(position = 10, value = "Description of the widget") private String description; + @NoXss + @ApiModelProperty(position = 11, value = "Tags of the widget type") + private String[] tags; + + @Getter + @Setter + private WidgetTypeId externalId; public WidgetTypeDetails() { super(); @@ -50,5 +62,7 @@ public class WidgetTypeDetails extends WidgetType { super(widgetTypeDetails); this.image = widgetTypeDetails.getImage(); this.description = widgetTypeDetails.getDescription(); + this.tags = widgetTypeDetails.getTags(); + this.externalId = widgetTypeDetails.getExternalId(); } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeInfo.java index f5c4abd66d..3387fb13b6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeInfo.java @@ -23,13 +23,16 @@ import org.thingsboard.server.common.data.validation.NoXss; @Data public class WidgetTypeInfo extends BaseWidgetType { - @ApiModelProperty(position = 7, value = "Base64 encoded widget thumbnail", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + @ApiModelProperty(position = 8, value = "Base64 encoded widget thumbnail", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String image; @NoXss - @ApiModelProperty(position = 7, value = "Description of the widget type", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + @ApiModelProperty(position = 9, value = "Description of the widget type", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String description; @NoXss - @ApiModelProperty(position = 8, value = "Type of the widget (timeseries, latest, control, alarm or static)", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + @ApiModelProperty(position = 10, value = "Tags of the widget type", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + private String[] tags; + @NoXss + @ApiModelProperty(position = 11, value = "Type of the widget (timeseries, latest, control, alarm or static)", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String widgetType; public WidgetTypeInfo() { @@ -48,6 +51,19 @@ public class WidgetTypeInfo extends BaseWidgetType { super(widgetTypeInfo); this.image = widgetTypeInfo.getImage(); this.description = widgetTypeInfo.getDescription(); + this.tags = widgetTypeInfo.getTags(); this.widgetType = widgetTypeInfo.getWidgetType(); } + + public WidgetTypeInfo(WidgetTypeDetails widgetTypeDetails) { + super(widgetTypeDetails); + this.image = widgetTypeDetails.getImage(); + this.description = widgetTypeDetails.getDescription(); + this.tags = widgetTypeDetails.getTags(); + if (widgetTypeDetails.getDescriptor() != null && widgetTypeDetails.getDescriptor().has("type")) { + this.widgetType = widgetTypeDetails.getDescriptor().get("type").asText(); + } else { + this.widgetType = ""; + } + } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java index 0ceef991ad..5d38c10550 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java @@ -22,8 +22,8 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; import org.thingsboard.server.common.data.BaseData; -import org.thingsboard.server.common.data.BaseDataWithAdditionalInfo; import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.HasImage; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.HasTitle; @@ -34,7 +34,7 @@ import org.thingsboard.server.common.data.validation.NoXss; @ApiModel @EqualsAndHashCode(callSuper = true) -public class WidgetsBundle extends BaseData implements HasName, HasTenantId, ExportableEntity, HasTitle { +public class WidgetsBundle extends BaseData implements HasName, HasTenantId, ExportableEntity, HasTitle, HasImage { private static final long serialVersionUID = -7627368878362410489L; @@ -57,19 +57,23 @@ public class WidgetsBundle extends BaseData implements HasName, @ApiModelProperty(position = 5, value = "Title used in search and UI", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String title; - @Length(fieldName = "image", max = 1000000) @Getter @Setter - @ApiModelProperty(position = 6, value = "Base64 encoded thumbnail", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + @ApiModelProperty(position = 6, value = "Relative or external image URL. Replaced with image data URL (Base64) in case of relative URL and 'inlineImages' option enabled.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String image; @NoXss - @Length(fieldName = "description") + @Length(fieldName = "description", max = 1024) @Getter @Setter @ApiModelProperty(position = 7, value = "Description", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String description; + @Getter + @Setter + @ApiModelProperty(position = 8, value = "Order", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + private Integer order; + @Getter @Setter private WidgetsBundleId externalId; @@ -89,6 +93,7 @@ public class WidgetsBundle extends BaseData implements HasName, this.title = widgetsBundle.getTitle(); this.image = widgetsBundle.getImage(); this.description = widgetsBundle.getDescription(); + this.order = widgetsBundle.getOrder(); this.externalId = widgetsBundle.getExternalId(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundleWidget.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundleWidget.java new file mode 100644 index 0000000000..1ac3c19132 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundleWidget.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.widget; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.id.WidgetTypeId; +import org.thingsboard.server.common.data.id.WidgetsBundleId; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class WidgetsBundleWidget { + + private WidgetsBundleId widgetsBundleId; + private WidgetTypeId widgetTypeId; + private int widgetTypeOrder; + +} diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/EntityTypeTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/EntityTypeTest.java new file mode 100644 index 0000000000..9eee9ec23d --- /dev/null +++ b/common/data/src/test/java/org/thingsboard/server/common/data/EntityTypeTest.java @@ -0,0 +1,30 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class EntityTypeTest { + + // backward-compatibility test + @Test + void getNormalNameTest() { + assertThat(EntityType.ENTITY_VIEW.getNormalName()).isEqualTo("Entity View"); + } + +} diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/StringUtilsTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/StringUtilsTest.java new file mode 100644 index 0000000000..b28d64cd2d --- /dev/null +++ b/common/data/src/test/java/org/thingsboard/server/common/data/StringUtilsTest.java @@ -0,0 +1,51 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThat; + +class StringUtilsTest { + + @ParameterizedTest + @ValueSource(strings = { + "\000", "\u0000", " \000", " \000 ", "\000 ", "\000\000", "\000 \000", + "世\000界", "F0929906\000\000\000\000\000\000\000\000\000", + }) + void testContains0x00_thenTrue(String sample) { + assertThat(StringUtils.contains0x00(sample)).isTrue(); + } + + @ParameterizedTest + @ValueSource(strings = {"", " ", "abc", "世界", "\001", "\uD83D\uDC0C"}) + void testContains0x00_thenFalse(String sample) { + assertThat(StringUtils.contains0x00(sample)).isFalse(); + } + + @Test + void testTruncate() { + int maxLength = 5; + assertThat(StringUtils.truncate(null, maxLength)).isNull(); + assertThat(StringUtils.truncate("", maxLength)).isEmpty(); + assertThat(StringUtils.truncate("123", maxLength)).isEqualTo("123"); + assertThat(StringUtils.truncate("1234567", maxLength)).isEqualTo("12345...[truncated 2 symbols]"); + assertThat(StringUtils.truncate("1234567", 0)).isEqualTo("1234567"); + } + +} diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/audit/ActionTypeTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/audit/ActionTypeTest.java new file mode 100644 index 0000000000..8c602c3cff --- /dev/null +++ b/common/data/src/test/java/org/thingsboard/server/common/data/audit/ActionTypeTest.java @@ -0,0 +1,65 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.audit; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.thingsboard.server.common.data.audit.ActionType.ACTIVATED; +import static org.thingsboard.server.common.data.audit.ActionType.ATTRIBUTES_READ; +import static org.thingsboard.server.common.data.audit.ActionType.CREDENTIALS_READ; +import static org.thingsboard.server.common.data.audit.ActionType.CREDENTIALS_UPDATED; +import static org.thingsboard.server.common.data.audit.ActionType.DELETED_COMMENT; +import static org.thingsboard.server.common.data.audit.ActionType.LOCKOUT; +import static org.thingsboard.server.common.data.audit.ActionType.LOGIN; +import static org.thingsboard.server.common.data.audit.ActionType.LOGOUT; +import static org.thingsboard.server.common.data.audit.ActionType.RPC_CALL; +import static org.thingsboard.server.common.data.audit.ActionType.SMS_SENT; +import static org.thingsboard.server.common.data.audit.ActionType.SUSPENDED; + +class ActionTypeTest { + + private static final List typesWithNullRuleEngineMsgType = List.of( + RPC_CALL, + CREDENTIALS_UPDATED, + ACTIVATED, + SUSPENDED, + CREDENTIALS_READ, + ATTRIBUTES_READ, + LOGIN, + LOGOUT, + LOCKOUT, + DELETED_COMMENT, + SMS_SENT + ); + + // backward-compatibility tests + + @Test + void getRuleEngineMsgTypeTest() { + var types = ActionType.values(); + for (var type : types) { + if (typesWithNullRuleEngineMsgType.contains(type)) { + assertThat(type.getRuleEngineMsgType()).isEmpty(); + } else { + assertThat(type.getRuleEngineMsgType()).isPresent(); + } + } + } + +} diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/msg/TbMsgTypeTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/msg/TbMsgTypeTest.java new file mode 100644 index 0000000000..870d5a2804 --- /dev/null +++ b/common/data/src/test/java/org/thingsboard/server/common/data/msg/TbMsgTypeTest.java @@ -0,0 +1,87 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.msg; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.thingsboard.server.common.data.msg.TbMsgType.ALARM; +import static org.thingsboard.server.common.data.msg.TbMsgType.ALARM_DELETE; +import static org.thingsboard.server.common.data.msg.TbMsgType.NA; +import static org.thingsboard.server.common.data.msg.TbMsgType.DEDUPLICATION_TIMEOUT_SELF_MSG; +import static org.thingsboard.server.common.data.msg.TbMsgType.DELAY_TIMEOUT_SELF_MSG; +import static org.thingsboard.server.common.data.msg.TbMsgType.ENTITY_ASSIGNED_TO_EDGE; +import static org.thingsboard.server.common.data.msg.TbMsgType.ENTITY_UNASSIGNED_FROM_EDGE; +import static org.thingsboard.server.common.data.msg.TbMsgType.MSG_COUNT_SELF_MSG; +import static org.thingsboard.server.common.data.msg.TbMsgType.PROVISION_FAILURE; +import static org.thingsboard.server.common.data.msg.TbMsgType.PROVISION_SUCCESS; +import static org.thingsboard.server.common.data.msg.TbMsgType.DEVICE_PROFILE_PERIODIC_SELF_MSG; +import static org.thingsboard.server.common.data.msg.TbMsgType.DEVICE_PROFILE_UPDATE_SELF_MSG; +import static org.thingsboard.server.common.data.msg.TbMsgType.DEVICE_UPDATE_SELF_MSG; +import static org.thingsboard.server.common.data.msg.TbMsgType.GENERATOR_NODE_SELF_MSG; +import static org.thingsboard.server.common.data.msg.TbMsgType.SEND_EMAIL; + +class TbMsgTypeTest { + + private static final List typesWithNullRuleNodeConnection = List.of( + ALARM, + ALARM_DELETE, + ENTITY_ASSIGNED_TO_EDGE, + ENTITY_UNASSIGNED_FROM_EDGE, + PROVISION_FAILURE, + PROVISION_SUCCESS, + SEND_EMAIL, + GENERATOR_NODE_SELF_MSG, + DEVICE_PROFILE_PERIODIC_SELF_MSG, + DEVICE_PROFILE_UPDATE_SELF_MSG, + DEVICE_UPDATE_SELF_MSG, + DEDUPLICATION_TIMEOUT_SELF_MSG, + DELAY_TIMEOUT_SELF_MSG, + MSG_COUNT_SELF_MSG, + NA + ); + + // backward-compatibility tests + + @Test + void getRuleNodeConnectionsTest() { + var tbMsgTypes = TbMsgType.values(); + for (var type : tbMsgTypes) { + if (typesWithNullRuleNodeConnection.contains(type)) { + assertThat(type.getRuleNodeConnection()).isEqualTo(TbNodeConnectionType.OTHER); + } else { + assertThat(type.getRuleNodeConnection()).isNotEqualTo(TbNodeConnectionType.OTHER); + } + } + } + + @Test + void getRuleNodeConnectionOrElseOtherTest() { + var tbMsgTypes = TbMsgType.values(); + for (var type : tbMsgTypes) { + if (typesWithNullRuleNodeConnection.contains(type)) { + assertThat(type.getRuleNodeConnection()) + .isEqualTo(TbNodeConnectionType.OTHER); + } else { + assertThat(type.getRuleNodeConnection()).isNotNull() + .isNotEqualTo(TbNodeConnectionType.OTHER); + } + } + } + +} diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/rpc/RpcStatusTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/rpc/RpcStatusTest.java new file mode 100644 index 0000000000..dc3c30d28b --- /dev/null +++ b/common/data/src/test/java/org/thingsboard/server/common/data/rpc/RpcStatusTest.java @@ -0,0 +1,47 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.rpc; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.thingsboard.server.common.data.rpc.RpcStatus.DELIVERED; +import static org.thingsboard.server.common.data.rpc.RpcStatus.QUEUED; +import static org.thingsboard.server.common.data.rpc.RpcStatus.SENT; + +class RpcStatusTest { + + private static final List pushDeleteNotificationToCoreStatuses = List.of( + QUEUED, + SENT, + DELIVERED + ); + + @Test + void isPushDeleteNotificationToCoreStatusTest() { + var rpcStatuses = RpcStatus.values(); + for (var status : rpcStatuses) { + if (pushDeleteNotificationToCoreStatuses.contains(status)) { + assertThat(status.isPushDeleteNotificationToCore()).isTrue(); + } else { + assertThat(status.isPushDeleteNotificationToCore()).isFalse(); + } + } + } + +} diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index c99a475236..8e24615a88 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java index 4e8548aa33..973c50c1d2 100644 --- a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java +++ b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java @@ -111,7 +111,7 @@ public class EdgeGrpcClient implements EdgeRpcClient { .setConnectRequestMsg(ConnectRequestMsg.newBuilder() .setEdgeRoutingKey(edgeKey) .setEdgeSecret(edgeSecret) - .setEdgeVersion(EdgeVersion.V_3_4_0) + .setEdgeVersion(EdgeVersion.V_3_6_2) .setMaxInboundMessageSize(maxInboundMessageSize) .build()) .build()); @@ -221,17 +221,11 @@ public class EdgeGrpcClient implements EdgeRpcClient { } @Override - public void sendSyncRequestMsg(boolean syncRequired) { - sendSyncRequestMsg(syncRequired, true); - } - - @Override - public void sendSyncRequestMsg(boolean syncRequired, boolean fullSync) { + public void sendSyncRequestMsg(boolean fullSyncRequired) { uplinkMsgLock.lock(); try { SyncRequestMsg syncRequestMsg = SyncRequestMsg.newBuilder() - .setSyncRequired(syncRequired) - .setFullSync(fullSync) + .setFullSync(fullSyncRequired) .build(); this.inputStream.onNext(RequestMsg.newBuilder() .setMsgType(RequestMsgType.SYNC_REQUEST_RPC_MESSAGE) diff --git a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeRpcClient.java b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeRpcClient.java index 44d00e22a8..9f2a303f5b 100644 --- a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeRpcClient.java +++ b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeRpcClient.java @@ -34,9 +34,7 @@ public interface EdgeRpcClient { void disconnect(boolean onError) throws InterruptedException; - void sendSyncRequestMsg(boolean syncRequired); - - void sendSyncRequestMsg(boolean syncRequired, boolean fullSync); + void sendSyncRequestMsg(boolean fullSyncRequired); void sendUplinkMsg(UplinkMsg uplinkMsg); diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index afcf4056c4..18b30e439f 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -34,6 +34,9 @@ enum EdgeVersion { V_3_3_0 = 0; V_3_3_3 = 1; V_3_4_0 = 2; + V_3_6_0 = 3; + V_3_6_1 = 4; + V_3_6_2 = 5; } /** @@ -85,7 +88,7 @@ message ConnectResponseMsg { } message SyncRequestMsg { - bool syncRequired = 1; + bool syncRequired = 1 [deprecated = true]; optional bool fullSync = 2; } @@ -113,7 +116,7 @@ enum UpdateMsgType { ENTITY_DELETED_RPC_MESSAGE = 2; ALARM_ACK_RPC_MESSAGE = 3; ALARM_CLEAR_RPC_MESSAGE = 4; - ENTITY_MERGE_RPC_MESSAGE = 5; // deprecated + ENTITY_MERGE_RPC_MESSAGE = 5 [deprecated = true]; } message EntityDataProto { @@ -136,25 +139,28 @@ message RuleChainUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - string name = 4; - optional int64 firstRuleNodeIdMSB = 5; - optional int64 firstRuleNodeIdLSB = 6; - bool root = 7; - bool debugMode = 8; - string configuration = 9; + string name = 4 [deprecated = true]; + optional int64 firstRuleNodeIdMSB = 5 [deprecated = true]; + optional int64 firstRuleNodeIdLSB = 6 [deprecated = true]; + bool root = 7 [deprecated = true]; + bool debugMode = 8 [deprecated = true]; + string configuration = 9 [deprecated = true]; + string entity = 10; } message RuleChainMetadataUpdateMsg { UpdateMsgType msgType = 1; - int64 ruleChainIdMSB = 2; - int64 ruleChainIdLSB = 3; - int32 firstNodeIndex = 4; - repeated RuleNodeProto nodes = 5; - repeated NodeConnectionInfoProto connections = 6; - repeated RuleChainConnectionInfoProto ruleChainConnections = 7; + int64 ruleChainIdMSB = 2 [deprecated = true]; + int64 ruleChainIdLSB = 3 [deprecated = true]; + int32 firstNodeIndex = 4 [deprecated = true]; + repeated RuleNodeProto nodes = 5 [deprecated = true]; + repeated NodeConnectionInfoProto connections = 6 [deprecated = true]; + repeated RuleChainConnectionInfoProto ruleChainConnections = 7 [deprecated = true]; + string entity = 8; } message RuleNodeProto { + option deprecated = true; int64 idMSB = 1; int64 idLSB = 2; string type = 3; @@ -162,15 +168,19 @@ message RuleNodeProto { bool debugMode = 5; string configuration = 6; string additionalInfo = 7; + bool singletonMode = 8; + int32 configurationVersion = 9; } message NodeConnectionInfoProto { + option deprecated = true; int32 fromIndex = 1; int32 toIndex = 2; string type = 3; } message RuleChainConnectionInfoProto { + option deprecated = true; int32 fromIndex = 1; int64 targetRuleChainIdMSB = 2; int64 targetRuleChainIdLSB = 3; @@ -182,205 +192,274 @@ message DashboardUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - optional int64 customerIdMSB = 4; - optional int64 customerIdLSB = 5; - string title = 6; - string configuration = 7; - optional string assignedCustomers = 8; + optional int64 customerIdMSB = 4 [deprecated = true]; + optional int64 customerIdLSB = 5 [deprecated = true]; + string title = 6 [deprecated = true]; + string configuration = 7 [deprecated = true]; + optional string assignedCustomers = 8 [deprecated = true]; + optional string image = 9 [deprecated = true]; + optional int32 mobileOrder = 10 [deprecated = true]; + bool mobileHide = 11 [deprecated = true]; + string entity = 12; } message DeviceUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - optional int64 customerIdMSB = 4; - optional int64 customerIdLSB = 5; - optional int64 deviceProfileIdMSB = 6; - optional int64 deviceProfileIdLSB = 7; - string name = 8; - string type = 9; - optional string label = 10; - optional string additionalInfo = 11; - optional string conflictName = 12; // deprecated - optional int64 firmwareIdMSB = 13; - optional int64 firmwareIdLSB = 14; - optional bytes deviceDataBytes = 15; - optional int64 softwareIdMSB = 16; - optional int64 softwareIdLSB = 17; + optional int64 customerIdMSB = 4 [deprecated = true]; + optional int64 customerIdLSB = 5 [deprecated = true]; + optional int64 deviceProfileIdMSB = 6 [deprecated = true]; + optional int64 deviceProfileIdLSB = 7 [deprecated = true]; + string name = 8 [deprecated = true]; + string type = 9 [deprecated = true]; + optional string label = 10 [deprecated = true]; + optional string additionalInfo = 11 [deprecated = true]; + optional string conflictName = 12 [deprecated = true]; + optional int64 firmwareIdMSB = 13 [deprecated = true]; + optional int64 firmwareIdLSB = 14 [deprecated = true]; + optional bytes deviceDataBytes = 15 [deprecated = true]; + optional int64 softwareIdMSB = 16 [deprecated = true]; + optional int64 softwareIdLSB = 17 [deprecated = true]; + string entity = 18; } message DeviceProfileUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - string name = 4; - optional string description = 5; - bool default = 6; - string type = 7; - optional string transportType = 8; - optional string provisionType = 9; - int64 defaultRuleChainIdMSB = 10; - int64 defaultRuleChainIdLSB = 11; - string defaultQueueName = 12; - bytes profileDataBytes = 13; - optional string provisionDeviceKey = 14; - optional bytes image = 15; - optional int64 firmwareIdMSB = 16; - optional int64 firmwareIdLSB = 17; - optional int64 softwareIdMSB = 18; - optional int64 softwareIdLSB = 19; - optional int64 defaultDashboardIdMSB = 20; - optional int64 defaultDashboardIdLSB = 21; + string name = 4 [deprecated = true]; + optional string description = 5 [deprecated = true]; + bool default = 6 [deprecated = true]; + string type = 7 [deprecated = true]; + optional string transportType = 8 [deprecated = true]; + optional string provisionType = 9 [deprecated = true]; + int64 defaultRuleChainIdMSB = 10 [deprecated = true]; + int64 defaultRuleChainIdLSB = 11 [deprecated = true]; + string defaultQueueName = 12 [deprecated = true]; + bytes profileDataBytes = 13 [deprecated = true]; + optional string provisionDeviceKey = 14 [deprecated = true]; + optional bytes image = 15 [deprecated = true]; + optional int64 firmwareIdMSB = 16 [deprecated = true]; + optional int64 firmwareIdLSB = 17 [deprecated = true]; + optional int64 softwareIdMSB = 18 [deprecated = true]; + optional int64 softwareIdLSB = 19 [deprecated = true]; + optional int64 defaultDashboardIdMSB = 20 [deprecated = true]; + optional int64 defaultDashboardIdLSB = 21 [deprecated = true]; + string entity = 22; } message AssetProfileUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - string name = 4; - optional string description = 5; - bool default = 6; - int64 defaultRuleChainIdMSB = 7; - int64 defaultRuleChainIdLSB = 8; - int64 defaultDashboardIdMSB = 9; - int64 defaultDashboardIdLSB = 10; - optional string defaultQueueName = 11; - optional bytes image = 12; + string name = 4 [deprecated = true]; + optional string description = 5 [deprecated = true]; + bool default = 6 [deprecated = true]; + int64 defaultRuleChainIdMSB = 7 [deprecated = true]; + int64 defaultRuleChainIdLSB = 8 [deprecated = true]; + int64 defaultDashboardIdMSB = 9 [deprecated = true]; + int64 defaultDashboardIdLSB = 10 [deprecated = true]; + optional string defaultQueueName = 11 [deprecated = true]; + optional bytes image = 12 [deprecated = true]; + string entity = 13; } message DeviceCredentialsUpdateMsg { - int64 deviceIdMSB = 1; - int64 deviceIdLSB = 2; - string credentialsType = 3; - string credentialsId = 4; - optional string credentialsValue = 5; + int64 deviceIdMSB = 1 [deprecated = true]; + int64 deviceIdLSB = 2 [deprecated = true]; + string credentialsType = 3 [deprecated = true]; + string credentialsId = 4 [deprecated = true]; + optional string credentialsValue = 5 [deprecated = true]; + string entity = 6; } message AssetUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - optional int64 customerIdMSB = 4; - optional int64 customerIdLSB = 5; - string name = 6; - string type = 7; - optional string label = 8; - optional string additionalInfo = 9; - optional int64 assetProfileIdMSB = 10; - optional int64 assetProfileIdLSB = 11; + optional int64 customerIdMSB = 4 [deprecated = true]; + optional int64 customerIdLSB = 5 [deprecated = true]; + string name = 6 [deprecated = true]; + string type = 7 [deprecated = true]; + optional string label = 8 [deprecated = true]; + optional string additionalInfo = 9 [deprecated = true]; + optional int64 assetProfileIdMSB = 10 [deprecated = true]; + optional int64 assetProfileIdLSB = 11 [deprecated = true]; + string entity = 12; } message EntityViewUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - optional int64 customerIdMSB = 4; - optional int64 customerIdLSB = 5; - string name = 6; - string type = 7; - int64 entityIdMSB = 8; - int64 entityIdLSB = 9; - EdgeEntityType entityType = 10; - optional string additionalInfo = 11; + optional int64 customerIdMSB = 4 [deprecated = true]; + optional int64 customerIdLSB = 5 [deprecated = true]; + string name = 6 [deprecated = true]; + string type = 7 [deprecated = true]; + int64 entityIdMSB = 8 [deprecated = true]; + int64 entityIdLSB = 9 [deprecated = true]; + EdgeEntityType entityType = 10 [deprecated = true]; + optional string additionalInfo = 11 [deprecated = true]; + string entity = 12; } message AlarmUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - string name = 4; - string type = 5; - string originatorType = 6; - string originatorName = 7; - string severity = 8; - string status = 9; - int64 startTs = 10; - int64 endTs = 11; - int64 ackTs = 12; - int64 clearTs = 13; - string details = 14; - bool propagate = 15; - bool propagateToOwner = 16; - bool propagateToTenant = 17; + string name = 4 [deprecated = true]; + string type = 5 [deprecated = true]; + string originatorType = 6 [deprecated = true]; + string originatorName = 7 [deprecated = true]; + string severity = 8 [deprecated = true]; + string status = 9 [deprecated = true]; + int64 startTs = 10 [deprecated = true]; + int64 endTs = 11 [deprecated = true]; + int64 ackTs = 12 [deprecated = true]; + int64 clearTs = 13 [deprecated = true]; + string details = 14 [deprecated = true]; + bool propagate = 15 [deprecated = true]; + bool propagateToOwner = 16 [deprecated = true]; + bool propagateToTenant = 17 [deprecated = true]; + string entity = 18; } message CustomerUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - string title = 4; - optional string country = 5; - optional string state = 6; - optional string city = 7; - optional string address = 8; - optional string address2 = 9; - optional string zip = 10; - optional string phone = 11; - optional string email = 12; - optional string additionalInfo = 13; + string title = 4 [deprecated = true]; + optional string country = 5 [deprecated = true]; + optional string state = 6 [deprecated = true]; + optional string city = 7 [deprecated = true]; + optional string address = 8 [deprecated = true]; + optional string address2 = 9 [deprecated = true]; + optional string zip = 10 [deprecated = true]; + optional string phone = 11 [deprecated = true]; + optional string email = 12 [deprecated = true]; + optional string additionalInfo = 13 [deprecated = true]; + string entity = 14; } message RelationUpdateMsg { UpdateMsgType msgType = 1; - int64 fromIdMSB = 2; - int64 fromIdLSB = 3; - string fromEntityType = 4; - int64 toIdMSB = 5; - int64 toIdLSB = 6; - string toEntityType = 7; - string type = 8; - optional string typeGroup = 9; - string additionalInfo = 10; + int64 fromIdMSB = 2 [deprecated = true]; + int64 fromIdLSB = 3 [deprecated = true]; + string fromEntityType = 4 [deprecated = true]; + int64 toIdMSB = 5 [deprecated = true]; + int64 toIdLSB = 6 [deprecated = true]; + string toEntityType = 7 [deprecated = true]; + string type = 8 [deprecated = true]; + optional string typeGroup = 9 [deprecated = true]; + string additionalInfo = 10 [deprecated = true]; + string entity = 11; } message UserUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - optional int64 customerIdMSB = 4; - optional int64 customerIdLSB = 5; - string email = 6; - string authority = 7; - optional string firstName = 8; - optional string lastName = 9; - optional string additionalInfo = 10; + optional int64 customerIdMSB = 4 [deprecated = true]; + optional int64 customerIdLSB = 5 [deprecated = true]; + string email = 6 [deprecated = true]; + string authority = 7 [deprecated = true]; + optional string firstName = 8 [deprecated = true]; + optional string lastName = 9 [deprecated = true]; + optional string additionalInfo = 10 [deprecated = true]; + string entity = 11; } message WidgetsBundleUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - string title = 4; - string alias = 5; - optional bytes image = 6; - bool isSystem = 7; - optional string description = 8; + string title = 4 [deprecated = true]; + string alias = 5 [deprecated = true]; + optional bytes image = 6 [deprecated = true]; + bool isSystem = 7 [deprecated = true]; + optional string description = 8 [deprecated = true]; + optional string widgets = 9; + optional int32 order = 10 [deprecated = true]; + string entity = 11; } message WidgetTypeUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - optional string bundleAlias = 4; - optional string alias = 5; - optional string name = 6; - optional string descriptorJson = 7; - bool isSystem = 8; - optional string image = 9; - optional string description = 10; + optional string bundleAlias = 4 [deprecated = true]; + optional string alias = 5 [deprecated = true]; + optional string name = 6 [deprecated = true]; + optional string descriptorJson = 7 [deprecated = true]; + bool isSystem = 8 [deprecated = true]; + optional string image = 9 [deprecated = true]; + optional string description = 10 [deprecated = true]; + optional string fqn = 11 [deprecated = true]; + bool deprecated = 12 [deprecated = true]; + repeated string tags = 13 [deprecated = true]; + string entity = 14; } message AdminSettingsUpdateMsg { - bool isSystem = 1; - string key = 2; - string jsonValue = 3; + bool isSystem = 1 [deprecated = true]; + string key = 2 [deprecated = true]; + string jsonValue = 3 [deprecated = true]; + string entity = 4; } message UserCredentialsUpdateMsg { - int64 userIdMSB = 1; - int64 userIdLSB = 2; - bool enabled = 3; - string password = 4; + int64 userIdMSB = 1 [deprecated = true]; + int64 userIdLSB = 2 [deprecated = true]; + bool enabled = 3 [deprecated = true]; + string password = 4 [deprecated = true]; + string entity = 5; +} + +message TenantUpdateMsg { + UpdateMsgType msgType = 1; + int64 idMSB = 2 [deprecated = true]; + int64 idLSB = 3 [deprecated = true]; + string title = 4 [deprecated = true]; + int64 profileIdMSB = 5 [deprecated = true]; + int64 profileIdLSB = 6 [deprecated = true]; + string region = 7 [deprecated = true]; + optional string country = 8 [deprecated = true]; + optional string state = 9 [deprecated = true]; + optional string city = 10 [deprecated = true]; + optional string address = 11 [deprecated = true]; + optional string address2 = 12 [deprecated = true]; + optional string zip = 13 [deprecated = true]; + optional string phone = 14 [deprecated = true]; + optional string email = 15 [deprecated = true]; + optional string additionalInfo = 16 [deprecated = true]; + string entity = 17; +} + +message TenantProfileUpdateMsg { + UpdateMsgType msgType = 1; + int64 idMSB = 2 [deprecated = true]; + int64 idLSB = 3 [deprecated = true]; + string name = 4 [deprecated = true]; + optional string description = 5 [deprecated = true]; + bool default = 6 [deprecated = true]; + bool isolatedRuleChain = 7 [deprecated = true]; + bytes profileDataBytes = 8 [deprecated = true]; + string entity = 9; +} + +message ResourceUpdateMsg { + UpdateMsgType msgType = 1; + int64 idMSB = 2; + int64 idLSB = 3; + string title = 4 [deprecated = true]; + string resourceType = 5 [deprecated = true]; + string resourceKey = 6 [deprecated = true]; + string fileName = 7 [deprecated = true]; + optional string data = 8 [deprecated = true]; + optional string etag = 9 [deprecated = true]; + bool isSystem = 10 [deprecated = true]; + string entity = 11; } message RuleChainMetadataRequestMsg { @@ -411,8 +490,8 @@ message DeviceCredentialsRequestMsg { int64 deviceIdLSB = 2; } -// deprecated message DeviceProfileDevicesRequestMsg { + option deprecated = true; int64 deviceProfileIdMSB = 1; int64 deviceProfileIdLSB = 2; } @@ -464,44 +543,48 @@ message OtaPackageUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - int64 deviceProfileIdMSB = 4; - int64 deviceProfileIdLSB = 5; - string type = 6; - string title = 7; - string version = 8; - string tag = 9; - optional string url = 10; - optional string fileName = 11; - optional string contentType = 12; - optional string checksumAlgorithm = 13; - optional string checksum = 14; - optional int64 dataSize = 15; - optional bytes data = 16; - optional string additionalInfo = 17; + int64 deviceProfileIdMSB = 4 [deprecated = true]; + int64 deviceProfileIdLSB = 5 [deprecated = true]; + string type = 6 [deprecated = true]; + string title = 7 [deprecated = true]; + string version = 8 [deprecated = true]; + string tag = 9 [deprecated = true]; + optional string url = 10 [deprecated = true]; + optional string fileName = 11 [deprecated = true]; + optional string contentType = 12 [deprecated = true]; + optional string checksumAlgorithm = 13 [deprecated = true]; + optional string checksum = 14 [deprecated = true]; + optional int64 dataSize = 15 [deprecated = true]; + optional bytes data = 16 [deprecated = true]; + optional string additionalInfo = 17 [deprecated = true]; + string entity = 18; } message QueueUpdateMsg { UpdateMsgType msgType = 1; int64 idMSB = 2; int64 idLSB = 3; - int64 tenantIdMSB = 4; - int64 tenantIdLSB = 5; - string name = 6; - string topic = 7; - int32 pollInterval = 8; - int32 partitions = 9; - bool consumerPerPartition = 10; - int64 packProcessingTimeout = 11; - SubmitStrategyProto submitStrategy = 12; - ProcessingStrategyProto processingStrategy = 13; + int64 tenantIdMSB = 4 [deprecated = true]; + int64 tenantIdLSB = 5 [deprecated = true]; + string name = 6 [deprecated = true]; + string topic = 7 [deprecated = true]; + int32 pollInterval = 8 [deprecated = true]; + int32 partitions = 9 [deprecated = true]; + bool consumerPerPartition = 10 [deprecated = true]; + int64 packProcessingTimeout = 11 [deprecated = true]; + SubmitStrategyProto submitStrategy = 12 [deprecated = true]; + ProcessingStrategyProto processingStrategy = 13 [deprecated = true]; + string entity = 14; } message SubmitStrategyProto { + option deprecated = true; string type = 1; int32 batchSize = 2; } message ProcessingStrategyProto { + option deprecated = true; string type = 1; int32 retries = 2; double failurePercentage = 3; @@ -526,9 +609,15 @@ message UplinkMsg { repeated UserCredentialsRequestMsg userCredentialsRequestMsg = 10; repeated DeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 11; repeated DeviceRpcCallMsg deviceRpcCallMsg = 12; - repeated DeviceProfileDevicesRequestMsg deviceProfileDevicesRequestMsg = 13; // deprecated + repeated DeviceProfileDevicesRequestMsg deviceProfileDevicesRequestMsg = 13 [deprecated = true]; repeated WidgetBundleTypesRequestMsg widgetBundleTypesRequestMsg = 14; repeated EntityViewsRequestMsg entityViewsRequestMsg = 15; + repeated AssetUpdateMsg assetUpdateMsg = 16; + repeated DashboardUpdateMsg dashboardUpdateMsg = 17; + repeated EntityViewUpdateMsg entityViewUpdateMsg = 18; + repeated AssetProfileUpdateMsg assetProfileUpdateMsg = 19; + repeated DeviceProfileUpdateMsg deviceProfileUpdateMsg = 20; + repeated ResourceUpdateMsg resourceUpdateMsg = 21; } message UplinkResponseMsg { @@ -569,5 +658,8 @@ message DownlinkMsg { repeated QueueUpdateMsg queueUpdateMsg = 23; repeated AssetProfileUpdateMsg assetProfileUpdateMsg = 24; EdgeConfiguration edgeConfiguration = 25; + repeated TenantUpdateMsg tenantUpdateMsg = 26; + repeated TenantProfileUpdateMsg tenantProfileUpdateMsg = 27; + repeated ResourceUpdateMsg resourceUpdateMsg = 28; } diff --git a/common/message/pom.xml b/common/message/pom.xml index 62e176e675..64c9140569 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java b/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java index 04b5d3fb35..c3d3623d8e 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.msg; +import lombok.Getter; import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; @@ -28,7 +29,7 @@ public enum MsgType { * * See {@link PartitionChangeMsg} */ - PARTITION_CHANGE_MSG, + PARTITION_CHANGE_MSG(true), APP_INIT_MSG, @@ -92,6 +93,8 @@ public enum MsgType { DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG, + DEVICE_DELETE_TO_DEVICE_ACTOR_MSG, + DEVICE_EDGE_UPDATE_TO_DEVICE_ACTOR_MSG, DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG, @@ -108,7 +111,7 @@ public enum MsgType { * Message that is sent from the Device Actor to Rule Engine. Requires acknowledgement */ - SESSION_TIMEOUT_MSG, + SESSION_TIMEOUT_MSG(true), STATS_PERSIST_TICK_MSG, @@ -130,4 +133,14 @@ public enum MsgType { EDGE_SYNC_REQUEST_TO_EDGE_SESSION_MSG, EDGE_SYNC_RESPONSE_FROM_EDGE_SESSION_MSG; + @Getter + private final boolean ignoreOnStart; + + MsgType() { + this.ignoreOnStart = false; + } + + MsgType(boolean ignoreOnStart) { + this.ignoreOnStart = ignoreOnStart; + } } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/TbActorError.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbActorError.java new file mode 100644 index 0000000000..fc27feb096 --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbActorError.java @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.msg; + +public interface TbActorError { + + boolean isUnrecoverable(); + +} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java index f04104bc51..b107d3d06e 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbMsg.java @@ -29,10 +29,12 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.gen.MsgProtos; import org.thingsboard.server.common.msg.queue.TbMsgCallback; import java.io.Serializable; +import java.util.Objects; import java.util.UUID; /** @@ -42,10 +44,15 @@ import java.util.UUID; @Slf4j public final class TbMsg implements Serializable { + public static final String EMPTY_JSON_OBJECT = "{}"; + public static final String EMPTY_JSON_ARRAY = "[]"; + public static final String EMPTY_STRING = ""; + private final String queueName; private final UUID id; private final long ts; private final String type; + private final TbMsgType internalType; private final EntityId originator; private final CustomerId customerId; private final TbMsgMetaData metaData; @@ -66,98 +73,255 @@ public final class TbMsg implements Serializable { return ctx.getAndIncrementRuleNodeCounter(); } + @Deprecated(since = "3.6.0", forRemoval = true) public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { return newMsg(queueName, type, originator, null, metaData, data, ruleChainId, ruleNodeId); } + /** + * Creates a new TbMsg instance with the specified parameters. + * + *

Deprecated: This method is deprecated since version 3.6.0 and should only be used when you need to + * specify a custom message type that doesn't exist in the {@link TbMsgType} enum. For standard message types, + * it is recommended to use the {@link #newMsg(String, TbMsgType, EntityId, CustomerId, TbMsgMetaData, String, RuleChainId, RuleNodeId)} + * method instead.

+ * + * @param queueName the name of the queue where the message will be sent + * @param type the type of the message + * @param originator the originator of the message + * @param customerId the ID of the customer associated with the message + * @param metaData the metadata of the message + * @param data the data of the message + * @param ruleChainId the ID of the rule chain associated with the message + * @param ruleNodeId the ID of the rule node associated with the message + * @return new TbMsg instance + */ + @Deprecated(since = "3.6.0") public static TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, + return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), null, type, originator, customerId, metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, null, TbMsgCallback.EMPTY); } + @Deprecated(since = "3.6.0", forRemoval = true) public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) { return newMsg(type, originator, null, metaData, data); } + @Deprecated(since = "3.6.0", forRemoval = true) public static TbMsg newMsg(String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { + return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), null, type, originator, customerId, + metaData.copy(), TbMsgDataType.JSON, data, null, null, null, TbMsgCallback.EMPTY); + } + + public static TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { + return newMsg(queueName, type, originator, null, metaData, data, ruleChainId, ruleNodeId); + } + + public static TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { + return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, + metaData.copy(), TbMsgDataType.JSON, data, ruleChainId, ruleNodeId, null, TbMsgCallback.EMPTY); + } + + public static TbMsg newMsg(TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data) { + return newMsg(type, originator, null, metaData, data); + } + + public static TbMsg newMsg(TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, metaData.copy(), TbMsgDataType.JSON, data, null, null, null, TbMsgCallback.EMPTY); } // REALLY NEW MSG + /** + * Creates a new TbMsg instance with the specified parameters. + * + *

Deprecated: This method is deprecated since version 3.6.0 and should only be used when you need to + * specify a custom message type that doesn't exist in the {@link TbMsgType} enum. For standard message types, + * it is recommended to use the {@link #newMsg(String, TbMsgType, EntityId, TbMsgMetaData, String)} + * method instead.

+ * + * @param queueName the name of the queue where the message will be sent + * @param type the type of the message + * @param originator the originator of the message + * @param metaData the metadata of the message + * @param data the data of the message + * @return new TbMsg instance + */ + @Deprecated(since = "3.6.0") public static TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data) { return newMsg(queueName, type, originator, null, metaData, data); } + /** + * Creates a new TbMsg instance with the specified parameters. + * + *

Deprecated: This method is deprecated since version 3.6.0 and should only be used when you need to + * specify a custom message type that doesn't exist in the {@link TbMsgType} enum. For standard message types, + * it is recommended to use the {@link #newMsg(String, TbMsgType, EntityId, CustomerId, TbMsgMetaData, String)} + * method instead.

+ * + * @param queueName the name of the queue where the message will be sent + * @param type the type of the message + * @param originator the originator of the message + * @param customerId the ID of the customer associated with the message + * @param metaData the metadata of the message + * @param data the data of the message + * @return new TbMsg instance + */ + @Deprecated(since = "3.6.0") public static TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { - return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, + return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), null, type, originator, customerId, metaData.copy(), TbMsgDataType.JSON, data, null, null, null, TbMsgCallback.EMPTY); } + @Deprecated(since = "3.6.0", forRemoval = true) public static TbMsg newMsg(String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data) { - return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, + return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), null, type, originator, customerId, metaData.copy(), dataType, data, null, null, null, TbMsgCallback.EMPTY); } + /** + * Creates a new TbMsg instance with the specified parameters. + * + *

Deprecated: This method is deprecated since version 3.6.0 and should only be used when you need to + * specify a custom message type that doesn't exist in the {@link TbMsgType} enum. For standard message types, + * it is recommended to use the {@link #newMsg(TbMsgType, EntityId, TbMsgMetaData, TbMsgDataType, String)} + * method instead.

+ * + * @param type the type of the message + * @param originator the originator of the message + * @param metaData the metadata of the message + * @param dataType the dataType of the message + * @param data the data of the message + * @return new TbMsg instance + */ + @Deprecated(since = "3.6.0") public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data) { return newMsg(type, originator, null, metaData, dataType, data); } + public static TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data) { + return newMsg(queueName, type, originator, null, metaData, data); + } + + public static TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data) { + return new TbMsg(queueName, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, + metaData.copy(), TbMsgDataType.JSON, data, null, null, null, TbMsgCallback.EMPTY); + } + + public static TbMsg newMsg(TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data) { + return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), type, originator, customerId, + metaData.copy(), dataType, data, null, null, null, TbMsgCallback.EMPTY); + } + + public static TbMsg newMsg(TbMsgType type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data) { + return newMsg(type, originator, null, metaData, dataType, data); + } + // For Tests only + @Deprecated(since = "3.6.0", forRemoval = true) public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), type, originator, null, + return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), null, type, originator, null, metaData.copy(), dataType, data, ruleChainId, ruleNodeId, null, TbMsgCallback.EMPTY); } + @Deprecated(since = "3.6.0", forRemoval = true) public static TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data, TbMsgCallback callback) { - return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), type, originator, null, + return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), null, type, originator, null, metaData.copy(), TbMsgDataType.JSON, data, null, null, null, callback); } + /** + * Transforms an existing TbMsg instance by changing its message type, originator, metadata, and data. + * + *

Deprecated: This method is deprecated since version 3.6.0 and should only be used when you need to + * specify a custom message type that doesn't exist in the {@link TbMsgType} enum. For standard message types, + * it is recommended to use the {@link #transformMsg(TbMsg, TbMsgType, EntityId, TbMsgMetaData, String)} + * method instead.

+ * + * + * @param tbMsg the TbMsg instance to transform + * @param type the new message type + * @param originator the new originator + * @param metaData the new metadata + * @param data the new data + * @return the transformed TbMsg instance + */ + @Deprecated(since = "3.6.0") public static TbMsg transformMsg(TbMsg tbMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) { + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, null, type, originator, tbMsg.customerId, metaData.copy(), tbMsg.dataType, + data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.ctx.copy(), tbMsg.callback); + } + + public static TbMsg newMsg(TbMsgType type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { + return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), type, originator, null, + metaData.copy(), dataType, data, ruleChainId, ruleNodeId, null, TbMsgCallback.EMPTY); + } + + public static TbMsg newMsg(TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data, TbMsgCallback callback) { + return new TbMsg(null, UUID.randomUUID(), System.currentTimeMillis(), type, originator, null, + metaData.copy(), TbMsgDataType.JSON, data, null, null, null, callback); + } + + public static TbMsg transformMsg(TbMsg tbMsg, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data) { return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, type, originator, tbMsg.customerId, metaData.copy(), tbMsg.dataType, data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.ctx.copy(), tbMsg.callback); } + public static TbMsg transformMsgOriginator(TbMsg tbMsg, EntityId originatorId) { + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, originatorId, tbMsg.getCustomerId(), tbMsg.metaData, tbMsg.dataType, + tbMsg.data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.ctx.copy(), tbMsg.getCallback()); + } + public static TbMsg transformMsgData(TbMsg tbMsg, String data) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.ctx.copy(), tbMsg.getCallback()); } - public static TbMsg transformMsg(TbMsg tbMsg, TbMsgMetaData metadata) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, metadata.copy(), tbMsg.dataType, + public static TbMsg transformMsgMetadata(TbMsg tbMsg, TbMsgMetaData metadata) { + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, metadata.copy(), tbMsg.dataType, tbMsg.data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.ctx.copy(), tbMsg.getCallback()); } - public static TbMsg transformMsg(TbMsg tbMsg, CustomerId customerId) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, customerId, tbMsg.metaData, tbMsg.dataType, + public static TbMsg transformMsg(TbMsg tbMsg, TbMsgMetaData metadata, String data) { + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, metadata, tbMsg.dataType, + data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.ctx.copy(), tbMsg.getCallback()); + } + + public static TbMsg transformMsgCustomerId(TbMsg tbMsg, CustomerId customerId) { + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, customerId, tbMsg.metaData, tbMsg.dataType, tbMsg.data, tbMsg.ruleChainId, tbMsg.ruleNodeId, tbMsg.ctx.copy(), tbMsg.getCallback()); } - public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId) { - return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, + public static TbMsg transformMsgRuleChainId(TbMsg tbMsg, RuleChainId ruleChainId) { + return new TbMsg(tbMsg.queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, tbMsg.data, ruleChainId, null, tbMsg.ctx.copy(), tbMsg.getCallback()); } - public static TbMsg transformMsg(TbMsg tbMsg, String queueName) { - return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, + public static TbMsg transformMsgQueueName(TbMsg tbMsg, String queueName) { + return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, tbMsg.data, tbMsg.getRuleChainId(), null, tbMsg.ctx.copy(), tbMsg.getCallback()); } public static TbMsg transformMsg(TbMsg tbMsg, RuleChainId ruleChainId, String queueName) { - return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, + return new TbMsg(queueName, tbMsg.id, tbMsg.ts, tbMsg.internalType, tbMsg.type, tbMsg.originator, tbMsg.customerId, tbMsg.metaData, tbMsg.dataType, tbMsg.data, ruleChainId, null, tbMsg.ctx.copy(), tbMsg.getCallback()); } //used for enqueueForTellNext public static TbMsg newMsg(TbMsg tbMsg, String queueName, RuleChainId ruleChainId, RuleNodeId ruleNodeId) { - return new TbMsg(queueName, UUID.randomUUID(), tbMsg.getTs(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.customerId, tbMsg.getMetaData().copy(), + return new TbMsg(queueName, UUID.randomUUID(), tbMsg.getTs(), tbMsg.getInternalType(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.customerId, tbMsg.getMetaData().copy(), tbMsg.getDataType(), tbMsg.getData(), ruleChainId, ruleNodeId, tbMsg.ctx.copy(), TbMsgCallback.EMPTY); } - private TbMsg(String queueName, UUID id, long ts, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data, + private TbMsg(String queueName, UUID id, long ts, TbMsgType internalType, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data, + RuleChainId ruleChainId, RuleNodeId ruleNodeId, TbMsgProcessingCtx ctx, TbMsgCallback callback) { + this(queueName, id, ts, internalType, internalType.name(), originator, customerId, metaData, dataType, data, ruleChainId, ruleNodeId, ctx, callback); + } + + private TbMsg(String queueName, UUID id, long ts, TbMsgType internalType, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, TbMsgDataType dataType, String data, RuleChainId ruleChainId, RuleNodeId ruleNodeId, TbMsgProcessingCtx ctx, TbMsgCallback callback) { this.id = id; this.queueName = queueName; @@ -167,6 +331,7 @@ public final class TbMsg implements Serializable { this.ts = System.currentTimeMillis(); } this.type = type; + this.internalType = internalType != null ? internalType : getInternalType(type); this.originator = originator; if (customerId == null || customerId.isNullUid()) { if (originator != null && originator.getEntityType() == EntityType.CUSTOMER) { @@ -183,11 +348,7 @@ public final class TbMsg implements Serializable { this.ruleChainId = ruleChainId; this.ruleNodeId = ruleNodeId; this.ctx = ctx != null ? ctx : new TbMsgProcessingCtx(); - if (callback != null) { - this.callback = callback; - } else { - this.callback = TbMsgCallback.EMPTY; - } + this.callback = Objects.requireNonNullElse(callback, TbMsgCallback.EMPTY); } public static ByteString toByteString(TbMsg msg) { @@ -256,7 +417,7 @@ public final class TbMsg implements Serializable { } TbMsgDataType dataType = TbMsgDataType.values()[proto.getDataType()]; - return new TbMsg(queueName, UUID.fromString(proto.getId()), proto.getTs(), proto.getType(), entityId, customerId, + return new TbMsg(queueName, UUID.fromString(proto.getId()), proto.getTs(), null, proto.getType(), entityId, customerId, metaData, dataType, proto.getData(), ruleChainId, ruleNodeId, ctx, callback); } catch (InvalidProtocolBufferException e) { throw new IllegalStateException("Could not parse protobuf for TbMsg", e); @@ -268,22 +429,23 @@ public final class TbMsg implements Serializable { } public TbMsg copyWithRuleChainId(RuleChainId ruleChainId, UUID msgId) { - return new TbMsg(this.queueName, msgId, this.ts, this.type, this.originator, this.customerId, + return new TbMsg(this.queueName, msgId, this.ts, this.internalType, this.type, this.originator, this.customerId, this.metaData, this.dataType, this.data, ruleChainId, null, this.ctx, callback); } public TbMsg copyWithRuleNodeId(RuleChainId ruleChainId, RuleNodeId ruleNodeId, UUID msgId) { - return new TbMsg(this.queueName, msgId, this.ts, this.type, this.originator, this.customerId, + return new TbMsg(this.queueName, msgId, this.ts, this.internalType, this.type, this.originator, this.customerId, this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, this.ctx, callback); } + public TbMsg copyWithNewCtx() { + return new TbMsg(this.queueName, this.id, this.ts, this.internalType, this.type, this.originator, this.customerId, + this.metaData, this.dataType, this.data, ruleChainId, ruleNodeId, this.ctx.copy(), TbMsgCallback.EMPTY); + } + public TbMsgCallback getCallback() { // May be null in case of deserialization; - if (callback != null) { - return callback; - } else { - return TbMsgCallback.EMPTY; - } + return Objects.requireNonNullElse(callback, TbMsgCallback.EMPTY); } public void pushToStack(RuleChainId ruleChainId, RuleNodeId ruleNodeId) { @@ -312,4 +474,26 @@ public final class TbMsg implements Serializable { } return ts; } + + private TbMsgType getInternalType(String type) { + try { + return TbMsgType.valueOf(type); + } catch (IllegalArgumentException e) { + return TbMsgType.NA; + } + } + + public boolean isTypeOf(TbMsgType tbMsgType) { + return internalType.equals(tbMsgType); + } + + public boolean isTypeOneOf(TbMsgType... types) { + for (TbMsgType type : types) { + if (isTypeOf(type)) { + return true; + } + } + return false; + } + } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/aware/RuleChainAwareMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/aware/RuleChainAwareMsg.java index d0e90ae421..5fbc857e0c 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/aware/RuleChainAwareMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/aware/RuleChainAwareMsg.java @@ -17,9 +17,12 @@ package org.thingsboard.server.common.msg.aware; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.msg.TbActorMsg; +import org.thingsboard.server.common.msg.TbMsg; public interface RuleChainAwareMsg extends TbActorMsg { RuleChainId getRuleChainId(); + + TbMsg getMsg(); } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/edge/EdgeEventUpdateMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/edge/EdgeEventUpdateMsg.java index 30d95ac28c..f8c7bd437f 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/edge/EdgeEventUpdateMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/edge/EdgeEventUpdateMsg.java @@ -15,24 +15,19 @@ */ package org.thingsboard.server.common.msg.edge; -import lombok.Getter; -import lombok.ToString; +import lombok.Data; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.MsgType; -@ToString +@Data public class EdgeEventUpdateMsg implements EdgeSessionMsg { - @Getter + + private static final long serialVersionUID = -8050114506822836537L; + private final TenantId tenantId; - @Getter private final EdgeId edgeId; - public EdgeEventUpdateMsg(TenantId tenantId, EdgeId edgeId) { - this.tenantId = tenantId; - this.edgeId = edgeId; - } - @Override public MsgType getMsgType() { return MsgType.EDGE_EVENT_UPDATE_TO_EDGE_SESSION_MSG; diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/edge/FromEdgeSyncResponse.java b/common/message/src/main/java/org/thingsboard/server/common/msg/edge/FromEdgeSyncResponse.java index 74c2d96b40..f9302fcb3f 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/edge/FromEdgeSyncResponse.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/edge/FromEdgeSyncResponse.java @@ -15,18 +15,18 @@ */ package org.thingsboard.server.common.msg.edge; -import lombok.AllArgsConstructor; -import lombok.Getter; +import lombok.Data; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.MsgType; import java.util.UUID; -@AllArgsConstructor -@Getter +@Data public class FromEdgeSyncResponse implements EdgeSessionMsg { + private static final long serialVersionUID = -6360890886315347486L; + private final UUID id; private final TenantId tenantId; private final EdgeId edgeId; diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/edge/ToEdgeSyncRequest.java b/common/message/src/main/java/org/thingsboard/server/common/msg/edge/ToEdgeSyncRequest.java index 06778089e2..f6324ba5d0 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/edge/ToEdgeSyncRequest.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/edge/ToEdgeSyncRequest.java @@ -15,17 +15,18 @@ */ package org.thingsboard.server.common.msg.edge; -import lombok.AllArgsConstructor; -import lombok.Getter; +import lombok.Data; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.MsgType; import java.util.UUID; -@AllArgsConstructor -@Getter +@Data public class ToEdgeSyncRequest implements EdgeSessionMsg { + + private static final long serialVersionUID = -7624597032448212259L; + private final UUID id; private final TenantId tenantId; private final EdgeId edgeId; diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/NotificationRuleProcessor.java b/common/message/src/main/java/org/thingsboard/server/common/msg/notification/NotificationRuleProcessor.java index 380773c1b0..8516a50b2f 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/NotificationRuleProcessor.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/notification/NotificationRuleProcessor.java @@ -15,7 +15,7 @@ */ package org.thingsboard.server.common.msg.notification; -import org.thingsboard.server.common.msg.notification.trigger.NotificationRuleTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTrigger; public interface NotificationRuleProcessor { diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/AlarmAssignmentTrigger.java b/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/AlarmAssignmentTrigger.java deleted file mode 100644 index 98ed43f886..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/AlarmAssignmentTrigger.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.msg.notification.trigger; - -import lombok.Builder; -import lombok.Data; -import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.alarm.AlarmInfo; -import org.thingsboard.server.common.data.audit.ActionType; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; - -@Data -@Builder -public class AlarmAssignmentTrigger implements NotificationRuleTrigger { - - private final TenantId tenantId; - private final AlarmInfo alarmInfo; - private final ActionType actionType; - private final User user; - - @Override - public EntityId getOriginatorEntityId() { - return alarmInfo.getOriginator(); - } - - @Override - public NotificationRuleTriggerType getType() { - return NotificationRuleTriggerType.ALARM_ASSIGNMENT; - } - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/AlarmCommentTrigger.java b/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/AlarmCommentTrigger.java deleted file mode 100644 index d0b3bdd5de..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/AlarmCommentTrigger.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.msg.notification.trigger; - -import lombok.Builder; -import lombok.Data; -import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.alarm.Alarm; -import org.thingsboard.server.common.data.alarm.AlarmComment; -import org.thingsboard.server.common.data.audit.ActionType; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; - -@Data -@Builder -public class AlarmCommentTrigger implements NotificationRuleTrigger { - - private final TenantId tenantId; - private final AlarmComment comment; - private final Alarm alarm; - private final ActionType actionType; - private final User user; - - @Override - public NotificationRuleTriggerType getType() { - return NotificationRuleTriggerType.ALARM_COMMENT; - } - - @Override - public EntityId getOriginatorEntityId() { - return alarm.getId(); - } - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/AlarmTrigger.java b/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/AlarmTrigger.java deleted file mode 100644 index b395ba9ebe..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/AlarmTrigger.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.msg.notification.trigger; - -import lombok.Builder; -import lombok.Data; -import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; - -@Data -@Builder -public class AlarmTrigger implements NotificationRuleTrigger { - - private final TenantId tenantId; - private final AlarmApiCallResult alarmUpdate; - - @Override - public NotificationRuleTriggerType getType() { - return NotificationRuleTriggerType.ALARM; - } - - @Override - public EntityId getOriginatorEntityId() { - return alarmUpdate.getAlarm().getId(); - } - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/ApiUsageLimitTrigger.java b/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/ApiUsageLimitTrigger.java deleted file mode 100644 index f21d3077ca..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/ApiUsageLimitTrigger.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.msg.notification.trigger; - -import lombok.Builder; -import lombok.Data; -import org.thingsboard.server.common.data.ApiUsageRecordState; -import org.thingsboard.server.common.data.ApiUsageStateValue; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; - -@Data -@Builder -public class ApiUsageLimitTrigger implements NotificationRuleTrigger { - - private final TenantId tenantId; - private final ApiUsageRecordState state; - private final ApiUsageStateValue status; - - @Override - public NotificationRuleTriggerType getType() { - return NotificationRuleTriggerType.API_USAGE_LIMIT; - } - - @Override - public EntityId getOriginatorEntityId() { - return tenantId; - } - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/DeviceActivityTrigger.java b/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/DeviceActivityTrigger.java deleted file mode 100644 index b426b6674b..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/DeviceActivityTrigger.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.msg.notification.trigger; - -import lombok.Builder; -import lombok.Data; -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; - -@Data -@Builder -public class DeviceActivityTrigger implements NotificationRuleTrigger { - - private final TenantId tenantId; - private final CustomerId customerId; - private final DeviceId deviceId; - private final boolean active; - - private final String deviceName; - private final String deviceType; - private final String deviceLabel; - - @Override - public EntityId getOriginatorEntityId() { - return deviceId; - } - - @Override - public NotificationRuleTriggerType getType() { - return NotificationRuleTriggerType.DEVICE_ACTIVITY; - } - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/EntitiesLimitTrigger.java b/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/EntitiesLimitTrigger.java deleted file mode 100644 index e6ce143fd0..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/EntitiesLimitTrigger.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.msg.notification.trigger; - -import lombok.Builder; -import lombok.Data; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; - -@Data -@Builder -public class EntitiesLimitTrigger implements NotificationRuleTrigger { - - private final TenantId tenantId; - private final EntityType entityType; - - private long limit; - private long currentCount; - - @Override - public NotificationRuleTriggerType getType() { - return NotificationRuleTriggerType.ENTITIES_LIMIT; - } - - @Override - public EntityId getOriginatorEntityId() { - return tenantId; - } - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/EntityActionTrigger.java b/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/EntityActionTrigger.java deleted file mode 100644 index 47d1789fec..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/EntityActionTrigger.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.msg.notification.trigger; - -import lombok.Builder; -import lombok.Data; -import org.thingsboard.server.common.data.HasName; -import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.audit.ActionType; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; - -@Data -@Builder -public class EntityActionTrigger implements NotificationRuleTrigger { - - private final TenantId tenantId; - private final EntityId entityId; - private final HasName entity; - private final ActionType actionType; - private final User user; - - @Override - public NotificationRuleTriggerType getType() { - return NotificationRuleTriggerType.ENTITY_ACTION; - } - - @Override - public EntityId getOriginatorEntityId() { - return entityId; - } - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/NewPlatformVersionTrigger.java b/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/NewPlatformVersionTrigger.java deleted file mode 100644 index 2bb88a708b..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/NewPlatformVersionTrigger.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.msg.notification.trigger; - -import lombok.Builder; -import lombok.Data; -import org.thingsboard.server.common.data.UpdateMessage; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; - -@Data -@Builder -public class NewPlatformVersionTrigger implements NotificationRuleTrigger { - - private final UpdateMessage updateInfo; - - @Override - public NotificationRuleTriggerType getType() { - return NotificationRuleTriggerType.NEW_PLATFORM_VERSION; - } - - @Override - public TenantId getTenantId() { - return TenantId.SYS_TENANT_ID; - } - - @Override - public EntityId getOriginatorEntityId() { - return TenantId.SYS_TENANT_ID; - } - - - @Override - public boolean deduplicate() { - return true; - } - - @Override - public String getDeduplicationKey() { - return String.join(":", NotificationRuleTrigger.super.getDeduplicationKey(), - updateInfo.getCurrentVersion(), updateInfo.getLatestVersion()); - } - - @Override - public long getDefaultDeduplicationDuration() { - return 0; - } - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/NotificationRuleTrigger.java b/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/NotificationRuleTrigger.java deleted file mode 100644 index 0cfc87a4df..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/NotificationRuleTrigger.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.msg.notification.trigger; - -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; - -import java.io.Serializable; - -public interface NotificationRuleTrigger extends Serializable { - - NotificationRuleTriggerType getType(); - - TenantId getTenantId(); - - EntityId getOriginatorEntityId(); - - - default boolean deduplicate() { - return false; - } - - default String getDeduplicationKey() { - EntityId originatorEntityId = getOriginatorEntityId(); - return String.join(":", getType().toString(), originatorEntityId.getEntityType().toString(), originatorEntityId.getId().toString()); - } - - default long getDefaultDeduplicationDuration() { - return 0; - } - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/RuleEngineComponentLifecycleEventTrigger.java b/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/RuleEngineComponentLifecycleEventTrigger.java deleted file mode 100644 index 56f4298b29..0000000000 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/notification/trigger/RuleEngineComponentLifecycleEventTrigger.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.msg.notification.trigger; - -import lombok.Builder; -import lombok.Data; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.RuleChainId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; - -@Data -@Builder -public class RuleEngineComponentLifecycleEventTrigger implements NotificationRuleTrigger { - - private final TenantId tenantId; - private final RuleChainId ruleChainId; - private final String ruleChainName; - private final EntityId componentId; - private final String componentName; - private final ComponentLifecycleEvent eventType; - private final Throwable error; - - @Override - public NotificationRuleTriggerType getType() { - return NotificationRuleTriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT; - } - - @Override - public EntityId getOriginatorEntityId() { - return componentId; - } - -} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java index b25a8cd3ea..f293b5dc24 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java @@ -15,8 +15,7 @@ */ package org.thingsboard.server.common.msg.plugin; -import lombok.Getter; -import lombok.ToString; +import lombok.Data; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; @@ -31,21 +30,14 @@ import java.util.Optional; /** * @author Andrew Shvayka */ -@ToString +@Data public class ComponentLifecycleMsg implements TenantAwareMsg, ToAllNodesMsg { - @Getter + private static final long serialVersionUID = -5303421482781273062L; + private final TenantId tenantId; - @Getter private final EntityId entityId; - @Getter private final ComponentLifecycleEvent event; - public ComponentLifecycleMsg(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent event) { - this.tenantId = tenantId; - this.entityId = entityId; - this.event = event; - } - public Optional getRuleChainId() { return entityId.getEntityType() == EntityType.RULE_CHAIN ? Optional.of((RuleChainId) entityId) : Optional.empty(); } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/RuleNodeUpdatedMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/RuleNodeUpdatedMsg.java index 0a5dfa484f..d912aaa29c 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/RuleNodeUpdatedMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/RuleNodeUpdatedMsg.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.msg.plugin; +import lombok.EqualsAndHashCode; import lombok.ToString; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -23,8 +24,11 @@ import org.thingsboard.server.common.msg.MsgType; /** * @author Andrew Shvayka + * This class used only to tell local rule-node actor like 'existing.getSelfActor().tellWithHighPriority(new RuleNodeUpdatedMs( ...' + * Never serialized to/from proto, otherwise you need to change proto mappers in ProtoUtils class */ -@ToString +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) public class RuleNodeUpdatedMsg extends ComponentLifecycleMsg { public RuleNodeUpdatedMsg(TenantId tenantId, EntityId entityId) { diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/queue/PartitionChangeMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/queue/PartitionChangeMsg.java index 39d7dad2db..fd48b36e67 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/queue/PartitionChangeMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/queue/PartitionChangeMsg.java @@ -20,8 +20,6 @@ import lombok.Getter; import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.TbActorMsg; -import java.util.Set; - /** * @author Andrew Shvayka */ @@ -30,8 +28,6 @@ public final class PartitionChangeMsg implements TbActorMsg { @Getter private final ServiceType serviceType; - @Getter - private final Set partitions; @Override public MsgType getMsgType() { diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/queue/RuleEngineException.java b/common/message/src/main/java/org/thingsboard/server/common/msg/queue/RuleEngineException.java index 1724a9050f..9003579c20 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/queue/RuleEngineException.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/queue/RuleEngineException.java @@ -19,25 +19,36 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.StringUtils; @Slf4j public class RuleEngineException extends Exception { protected static final ObjectMapper mapper = new ObjectMapper(); @Getter - private long ts; + private final long ts; public RuleEngineException(String message) { - super(message != null ? message : "Unknown"); + this(message, null); + } + + public RuleEngineException(String message, Throwable t) { + super(message != null ? message : "Unknown", t); ts = System.currentTimeMillis(); } - public String toJsonString() { + public String toJsonString(int maxMessageLength) { try { - return mapper.writeValueAsString(mapper.createObjectNode().put("message", getMessage())); + return mapper.writeValueAsString(mapper.createObjectNode() + .put("message", truncateIfNecessary(getMessage(), maxMessageLength))); } catch (JsonProcessingException e) { log.warn("Failed to serialize exception ", e); throw new RuntimeException(e); } } + + protected String truncateIfNecessary(String message, int maxMessageLength) { + return StringUtils.truncate(message, maxMessageLength); + } + } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/queue/RuleNodeException.java b/common/message/src/main/java/org/thingsboard/server/common/msg/queue/RuleNodeException.java index 160e2e6f95..4be1875bc1 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/queue/RuleNodeException.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/queue/RuleNodeException.java @@ -52,14 +52,14 @@ public class RuleNodeException extends RuleEngineException { } } - public String toJsonString() { + public String toJsonString(int maxMessageLength) { try { return mapper.writeValueAsString(mapper.createObjectNode() .put("ruleNodeId", ruleNodeId.toString()) .put("ruleChainId", ruleChainId.toString()) .put("ruleNodeName", ruleNodeName) .put("ruleChainName", ruleChainName) - .put("message", getMessage())); + .put("message", truncateIfNecessary(getMessage(), maxMessageLength))); } catch (JsonProcessingException e) { log.warn("Failed to serialize exception ", e); throw new RuntimeException(e); diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/queue/TbMsgCallback.java b/common/message/src/main/java/org/thingsboard/server/common/msg/queue/TbMsgCallback.java index 3312c98b64..6cf298adb2 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/queue/TbMsgCallback.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/queue/TbMsgCallback.java @@ -39,6 +39,10 @@ public interface TbMsgCallback { void onFailure(RuleEngineException e); + default void onRateLimit(RuleEngineException e) { + onFailure(e); + }; + /** * Returns 'true' if rule engine is expecting the message to be processed, 'false' otherwise. * message may no longer be valid, if the message pack is already expired/canceled/failed. diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/queue/TopicPartitionInfo.java b/common/message/src/main/java/org/thingsboard/server/common/msg/queue/TopicPartitionInfo.java index 7122d5ef2a..d2144e8111 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/queue/TopicPartitionInfo.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/queue/TopicPartitionInfo.java @@ -42,7 +42,7 @@ public class TopicPartitionInfo { this.myPartition = myPartition; String tmp = topic; if (tenantId != null && !tenantId.isNullUid()) { - tmp += "." + tenantId.getId().toString(); + tmp += ".isolated." + tenantId.getId().toString(); } if (partition != null) { tmp += "." + partition; diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/FromDeviceRpcResponse.java b/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/FromDeviceRpcResponse.java index 75c2f35a5e..0ef89e460e 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/FromDeviceRpcResponse.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/FromDeviceRpcResponse.java @@ -15,9 +15,7 @@ */ package org.thingsboard.server.common.msg.rpc; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.ToString; +import lombok.Data; import org.thingsboard.server.common.data.rpc.RpcError; import java.io.Serializable; @@ -27,10 +25,11 @@ import java.util.UUID; /** * @author Andrew Shvayka */ -@RequiredArgsConstructor -@ToString +@Data public class FromDeviceRpcResponse implements Serializable { - @Getter + + private static final long serialVersionUID = -3799452502112373491L; + private final UUID id; private final String response; private final RpcError error; diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/FromDeviceRpcResponseActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/FromDeviceRpcResponseActorMsg.java new file mode 100644 index 0000000000..28eb1b6a06 --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/FromDeviceRpcResponseActorMsg.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.msg.rpc; + +import lombok.Data; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; + +@Data +public class FromDeviceRpcResponseActorMsg implements ToDeviceActorNotificationMsg { + + private static final long serialVersionUID = -6648120137236354987L; + + private final Integer requestId; + private final TenantId tenantId; + private final DeviceId deviceId; + private final FromDeviceRpcResponse msg; + + @Override + public MsgType getMsgType() { + return MsgType.DEVICE_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG; + } +} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/RemoveRpcActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/RemoveRpcActorMsg.java new file mode 100644 index 0000000000..dc7db3e7fa --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/RemoveRpcActorMsg.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.msg.rpc; + +import lombok.Data; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; + +import java.util.UUID; + +@Data +public class RemoveRpcActorMsg implements ToDeviceActorNotificationMsg { + + private static final long serialVersionUID = -6112720854949677477L; + + private final TenantId tenantId; + private final DeviceId deviceId; + private final UUID requestId; + + @Override + public MsgType getMsgType() { + return MsgType.REMOVE_RPC_TO_DEVICE_ACTOR_MSG; + } +} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/ToDeviceRpcRequest.java b/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/ToDeviceRpcRequest.java index 08785e4c31..846022bdc8 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/ToDeviceRpcRequest.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/ToDeviceRpcRequest.java @@ -29,6 +29,9 @@ import java.util.UUID; */ @Data public class ToDeviceRpcRequest implements Serializable { + + private static final long serialVersionUID = -7089247105087346214L; + private final UUID id; private final TenantId tenantId; private final DeviceId deviceId; @@ -40,4 +43,3 @@ public class ToDeviceRpcRequest implements Serializable { @JsonIgnore private final String additionalInfo; } - diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/ToDeviceRpcRequestActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/ToDeviceRpcRequestActorMsg.java new file mode 100644 index 0000000000..d5f8c6d797 --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/ToDeviceRpcRequestActorMsg.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.msg.rpc; + +import lombok.Data; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; + +/** + * Created by ashvayka on 16.04.18. + */ +@Data +public class ToDeviceRpcRequestActorMsg implements ToDeviceActorNotificationMsg { + + private static final long serialVersionUID = -8592877558138716589L; + + private final String serviceId; + private final ToDeviceRpcRequest msg; + + @Override + public DeviceId getDeviceId() { + return msg.getDeviceId(); + } + + @Override + public TenantId getTenantId() { + return msg.getTenantId(); + } + + @Override + public MsgType getMsgType() { + return MsgType.DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG; + } +} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceAttributes.java b/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceAttributes.java new file mode 100644 index 0000000000..00079558e6 --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceAttributes.java @@ -0,0 +1,107 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.msg.rule.engine; + +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.kv.AttributeKey; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * @author Andrew Shvayka + */ +public class DeviceAttributes { + + private final Map clientSideAttributesMap; + private final Map serverPrivateAttributesMap; + private final Map serverPublicAttributesMap; + + public DeviceAttributes(List clientSideAttributes, List serverPrivateAttributes, List serverPublicAttributes) { + this.clientSideAttributesMap = mapAttributes(clientSideAttributes); + this.serverPrivateAttributesMap = mapAttributes(serverPrivateAttributes); + this.serverPublicAttributesMap = mapAttributes(serverPublicAttributes); + } + + private static Map mapAttributes(List attributes) { + Map result = new HashMap<>(); + for (AttributeKvEntry attribute : attributes) { + result.put(attribute.getKey(), attribute); + } + return result; + } + + public Collection getClientSideAttributes() { + return clientSideAttributesMap.values(); + } + + public Collection getServerSideAttributes() { + return serverPrivateAttributesMap.values(); + } + + public Collection getServerSidePublicAttributes() { + return serverPublicAttributesMap.values(); + } + + public Optional getClientSideAttribute(String attribute) { + return Optional.ofNullable(clientSideAttributesMap.get(attribute)); + } + + public Optional getServerPrivateAttribute(String attribute) { + return Optional.ofNullable(serverPrivateAttributesMap.get(attribute)); + } + + public Optional getServerPublicAttribute(String attribute) { + return Optional.ofNullable(serverPublicAttributesMap.get(attribute)); + } + + public void remove(AttributeKey key) { + Map map = getMapByScope(key.getScope()); + if (map != null) { + map.remove(key.getAttributeKey()); + } + } + + public void update(String scope, List values) { + Map map = getMapByScope(scope); + values.forEach(v -> map.put(v.getKey(), v)); + } + + private Map getMapByScope(String scope) { + Map map = null; + if (scope.equalsIgnoreCase(DataConstants.CLIENT_SCOPE)) { + map = clientSideAttributesMap; + } else if (scope.equalsIgnoreCase(DataConstants.SHARED_SCOPE)) { + map = serverPublicAttributesMap; + } else if (scope.equalsIgnoreCase(DataConstants.SERVER_SCOPE)) { + map = serverPrivateAttributesMap; + } + return map; + } + + @Override + public String toString() { + return "DeviceAttributes{" + + "clientSideAttributesMap=" + clientSideAttributesMap + + ", serverPrivateAttributesMap=" + serverPrivateAttributesMap + + ", serverPublicAttributesMap=" + serverPublicAttributesMap + + '}'; + } +} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceAttributesEventNotificationMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceAttributesEventNotificationMsg.java new file mode 100644 index 0000000000..d0124fbb01 --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceAttributesEventNotificationMsg.java @@ -0,0 +1,59 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.msg.rule.engine; + +import lombok.Data; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.AttributeKey; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Andrew Shvayka + */ +@Data +public class DeviceAttributesEventNotificationMsg implements ToDeviceActorNotificationMsg { + + private static final long serialVersionUID = 2422071590415277039L; + + private final TenantId tenantId; + private final DeviceId deviceId; + private final Set deletedKeys; + private final String scope; + private final List values; + private final boolean deleted; + + public static DeviceAttributesEventNotificationMsg onUpdate(TenantId tenantId, DeviceId deviceId, String scope, List values) { + return new DeviceAttributesEventNotificationMsg(tenantId, deviceId, null, scope, values, false); + } + + public static DeviceAttributesEventNotificationMsg onDelete(TenantId tenantId, DeviceId deviceId, String scope, List keys) { + Set keysToNotify = new HashSet<>(); + keys.forEach(key -> keysToNotify.add(new AttributeKey(scope, key))); + return new DeviceAttributesEventNotificationMsg(tenantId, deviceId, keysToNotify, null, null, true); + } + + @Override + public MsgType getMsgType() { + return MsgType.DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG; + } +} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceCredentialsUpdateNotificationMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceCredentialsUpdateNotificationMsg.java new file mode 100644 index 0000000000..b13026ba38 --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceCredentialsUpdateNotificationMsg.java @@ -0,0 +1,46 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.msg.rule.engine; + +import lombok.Data; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; + +/** + * @author Andrew Shvayka + */ +@Data +public class DeviceCredentialsUpdateNotificationMsg implements ToDeviceActorNotificationMsg { + + private static final long serialVersionUID = -3956907402411126990L; + + private final TenantId tenantId; + private final DeviceId deviceId; + + /** + * LwM2M + * @return + */ + private final DeviceCredentials deviceCredentials; + + @Override + public MsgType getMsgType() { + return MsgType.DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG; + } +} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceDeleteMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceDeleteMsg.java new file mode 100644 index 0000000000..1bdab7409b --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceDeleteMsg.java @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.msg.rule.engine; + +import lombok.Data; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; + +@Data +public class DeviceDeleteMsg implements ToDeviceActorNotificationMsg { + + private static final long serialVersionUID = 4679029228395462172L; + + private final TenantId tenantId; + private final DeviceId deviceId; + + @Override + public MsgType getMsgType() { + return MsgType.DEVICE_DELETE_TO_DEVICE_ACTOR_MSG; + } +} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceEdgeUpdateMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceEdgeUpdateMsg.java new file mode 100644 index 0000000000..99e52e31b0 --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceEdgeUpdateMsg.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.msg.rule.engine; + +import lombok.Data; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; + +@Data +public class DeviceEdgeUpdateMsg implements ToDeviceActorNotificationMsg { + + private static final long serialVersionUID = 4679029228395462172L; + + private final TenantId tenantId; + private final DeviceId deviceId; + private final EdgeId edgeId; + + @Override + public MsgType getMsgType() { + return MsgType.DEVICE_EDGE_UPDATE_TO_DEVICE_ACTOR_MSG; + } +} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceMetaData.java b/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceMetaData.java new file mode 100644 index 0000000000..f7fbb7b1ef --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceMetaData.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.msg.rule.engine; + +import lombok.Data; +import org.thingsboard.server.common.data.id.DeviceId; + +/** + * Contains basic device metadata; + * + * @author ashvayka + */ +@Data +public final class DeviceMetaData { + + final DeviceId deviceId; + final String deviceName; + final String deviceType; + final DeviceAttributes deviceAttributes; + +} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceNameOrTypeUpdateMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceNameOrTypeUpdateMsg.java new file mode 100644 index 0000000000..13b6a8558f --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/rule/engine/DeviceNameOrTypeUpdateMsg.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.msg.rule.engine; + +import lombok.Data; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.MsgType; +import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; + +@Data +public class DeviceNameOrTypeUpdateMsg implements ToDeviceActorNotificationMsg { + + private static final long serialVersionUID = -5738949227650536685L; + + private final TenantId tenantId; + private final DeviceId deviceId; + private final String deviceName; + private final String deviceType; + + @Override + public MsgType getMsgType() { + return MsgType.DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG; + } +} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java index dfcf178473..c1ca525cc9 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java @@ -15,6 +15,12 @@ */ package org.thingsboard.server.common.msg.session; +/** + * @deprecated This enum is deprecated and will be removed in a future version. + * Note: This enum was originally part of the public API but is now specific to CoAP transport only. + * Please use {@link org.thingsboard.server.transport.coap.CoapSessionMsgType} instead. + */ +@Deprecated(since="3.6.0", forRemoval = true) public enum SessionMsgType { GET_ATTRIBUTES_REQUEST(true), POST_ATTRIBUTES_REQUEST(true), GET_ATTRIBUTES_RESPONSE, SUBSCRIBE_ATTRIBUTES_REQUEST, UNSUBSCRIBE_ATTRIBUTES_REQUEST, ATTRIBUTES_UPDATE_NOTIFICATION, diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/tools/TbRateLimitsException.java b/common/message/src/main/java/org/thingsboard/server/common/msg/tools/TbRateLimitsException.java index dd5fda4dd5..5e63cb037e 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/tools/TbRateLimitsException.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/tools/TbRateLimitsException.java @@ -17,11 +17,12 @@ package org.thingsboard.server.common.msg.tools; import lombok.Getter; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.exception.AbstractRateLimitException; /** * Created by ashvayka on 22.10.18. */ -public class TbRateLimitsException extends RuntimeException { +public class TbRateLimitsException extends AbstractRateLimitException { @Getter private final EntityType entityType; diff --git a/common/pom.xml b/common/pom.xml index 51ca9546b1..76387a1773 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard common @@ -48,6 +48,7 @@ edge-api version-control script + proto diff --git a/common/proto/pom.xml b/common/proto/pom.xml new file mode 100644 index 0000000000..4ccf154ead --- /dev/null +++ b/common/proto/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + org.thingsboard + 3.6.2-SNAPSHOT + common + + org.thingsboard.common + proto + jar + + Thingsboard Server Common Protobuf and gRPC structures + https://thingsboard.io + + + UTF-8 + ${basedir}/../.. + + + + + org.thingsboard.common + data + + + org.thingsboard.common + message + + + com.google.protobuf + protobuf-java + + + com.google.protobuf + protobuf-java-util + + + org.springframework.boot + spring-boot-starter-web + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + test + + + org.awaitility + awaitility + test + + + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + + + + + + + thingsboard-repo-deploy + ThingsBoard Repo Deployment + https://repo.thingsboard.io/artifactory/libs-release-public + + + + \ No newline at end of file diff --git a/common/proto/src/main/java/org/thingsboard/server/common/adaptor/AdaptorException.java b/common/proto/src/main/java/org/thingsboard/server/common/adaptor/AdaptorException.java new file mode 100644 index 0000000000..1c22163c1f --- /dev/null +++ b/common/proto/src/main/java/org/thingsboard/server/common/adaptor/AdaptorException.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.adaptor; + +public class AdaptorException extends Exception { + + private static final long serialVersionUID = 1L; + + public AdaptorException() { + super(); + } + + public AdaptorException(String cause) { + super(cause); + } + + public AdaptorException(Exception cause) { + super(cause); + } + + public AdaptorException(String message, Exception cause) { + super(message, cause); + } + +} diff --git a/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverter.java b/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverter.java new file mode 100644 index 0000000000..c51bc784a6 --- /dev/null +++ b/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverter.java @@ -0,0 +1,653 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.adaptor; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSyntaxException; +import org.apache.commons.lang3.math.NumberUtils; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +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.JsonDataEntry; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.CredentialsType; +import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; +import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; +import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType; +import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; +import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ResponseStatus; +import org.thingsboard.server.gen.transport.TransportProtos.TsKvListProto; +import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; +import org.thingsboard.server.gen.transport.TransportProtos.ValidateBasicMqttCredRequestMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +public class JsonConverter { + + private static final Gson GSON = new Gson(); + private static final JsonParser JSON_PARSER = new JsonParser(); + private static final String CAN_T_PARSE_VALUE = "Can't parse value: "; + private static final String DEVICE_PROPERTY = "device"; + + private static boolean isTypeCastEnabled = true; + + private static int maxStringValueLength = 0; + + public static PostTelemetryMsg convertToTelemetryProto(JsonElement jsonElement, long ts) throws JsonSyntaxException { + PostTelemetryMsg.Builder builder = PostTelemetryMsg.newBuilder(); + convertToTelemetry(jsonElement, ts, null, builder); + return builder.build(); + } + + public static PostTelemetryMsg convertToTelemetryProto(JsonElement jsonElement) throws JsonSyntaxException { + return convertToTelemetryProto(jsonElement, System.currentTimeMillis()); + } + + private static void convertToTelemetry(JsonElement jsonElement, long systemTs, Map> result, PostTelemetryMsg.Builder builder) { + if (jsonElement.isJsonObject()) { + parseObject(systemTs, result, builder, jsonElement.getAsJsonObject()); + } else if (jsonElement.isJsonArray()) { + jsonElement.getAsJsonArray().forEach(je -> { + if (je.isJsonObject()) { + parseObject(systemTs, result, builder, je.getAsJsonObject()); + } else { + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + je); + } + }); + } else { + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonElement); + } + } + + private static void parseObject(long systemTs, Map> result, PostTelemetryMsg.Builder builder, JsonObject jo) { + if (result != null) { + parseObject(result, systemTs, jo); + } else { + parseObject(builder, systemTs, jo); + } + } + + public static ClaimDeviceMsg convertToClaimDeviceProto(DeviceId deviceId, String json) { + long durationMs = 0L; + if (json != null && !json.isEmpty()) { + return convertToClaimDeviceProto(deviceId, JSON_PARSER.parse(json)); + } + return buildClaimDeviceMsg(deviceId, DataConstants.DEFAULT_SECRET_KEY, durationMs); + } + + public static ClaimDeviceMsg convertToClaimDeviceProto(DeviceId deviceId, JsonElement jsonElement) { + String secretKey = DataConstants.DEFAULT_SECRET_KEY; + long durationMs = 0L; + if (jsonElement.isJsonObject()) { + JsonObject jo = jsonElement.getAsJsonObject(); + if (jo.has(DataConstants.SECRET_KEY_FIELD_NAME)) { + secretKey = jo.get(DataConstants.SECRET_KEY_FIELD_NAME).getAsString(); + } + if (jo.has(DataConstants.DURATION_MS_FIELD_NAME)) { + durationMs = jo.get(DataConstants.DURATION_MS_FIELD_NAME).getAsLong(); + } + } else { + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonElement); + } + return buildClaimDeviceMsg(deviceId, secretKey, durationMs); + } + + private static ClaimDeviceMsg buildClaimDeviceMsg(DeviceId deviceId, String secretKey, long durationMs) { + ClaimDeviceMsg.Builder result = ClaimDeviceMsg.newBuilder(); + return result + .setDeviceIdMSB(deviceId.getId().getMostSignificantBits()) + .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits()) + .setSecretKey(secretKey) + .setDurationMs(durationMs) + .build(); + } + + public static PostAttributeMsg convertToAttributesProto(JsonElement jsonObject) throws JsonSyntaxException { + if (jsonObject.isJsonObject()) { + PostAttributeMsg.Builder result = PostAttributeMsg.newBuilder(); + List keyValueList = parseProtoValues(jsonObject.getAsJsonObject()); + result.addAllKv(keyValueList); + return result.build(); + } else { + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonObject); + } + } + + public static JsonElement toJson(TransportProtos.ToDeviceRpcRequestMsg msg, boolean includeRequestId) { + JsonObject result = new JsonObject(); + if (includeRequestId) { + result.addProperty("id", msg.getRequestId()); + } + result.addProperty("method", msg.getMethodName()); + result.add("params", JSON_PARSER.parse(msg.getParams())); + return result; + } + + private static void parseObject(PostTelemetryMsg.Builder builder, long systemTs, JsonObject jo) { + if (jo.has("ts") && jo.has("values")) { + parseWithTs(builder, jo); + } else { + parseWithoutTs(builder, systemTs, jo); + } + } + + private static void parseWithoutTs(PostTelemetryMsg.Builder request, long systemTs, JsonObject jo) { + TsKvListProto.Builder builder = TsKvListProto.newBuilder(); + builder.setTs(systemTs); + builder.addAllKv(parseProtoValues(jo)); + request.addTsKvList(builder.build()); + } + + private static void parseWithTs(PostTelemetryMsg.Builder request, JsonObject jo) { + TsKvListProto.Builder builder = TsKvListProto.newBuilder(); + builder.setTs(jo.get("ts").getAsLong()); + builder.addAllKv(parseProtoValues(jo.get("values").getAsJsonObject())); + request.addTsKvList(builder.build()); + } + + private static List parseProtoValues(JsonObject valuesObject) { + List result = new ArrayList<>(); + for (Entry valueEntry : valuesObject.entrySet()) { + JsonElement element = valueEntry.getValue(); + if (element.isJsonPrimitive()) { + JsonPrimitive value = element.getAsJsonPrimitive(); + if (value.isString()) { + if (maxStringValueLength > 0 && value.getAsString().length() > maxStringValueLength) { + String message = String.format("String value length [%d] for key [%s] is greater than maximum allowed [%d]", value.getAsString().length(), valueEntry.getKey(), maxStringValueLength); + throw new JsonSyntaxException(message); + } + if (isTypeCastEnabled && NumberUtils.isParsable(value.getAsString())) { + try { + result.add(buildNumericKeyValueProto(value, valueEntry.getKey())); + } catch (RuntimeException th) { + result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.STRING_V) + .setStringV(value.getAsString()).build()); + } + } else { + result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.STRING_V) + .setStringV(value.getAsString()).build()); + } + } else if (value.isBoolean()) { + result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.BOOLEAN_V) + .setBoolV(value.getAsBoolean()).build()); + } else if (value.isNumber()) { + result.add(buildNumericKeyValueProto(value, valueEntry.getKey())); + } else if (!value.isJsonNull()) { + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + value); + } + } else if (element.isJsonObject() || element.isJsonArray()) { + result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.JSON_V).setJsonV(element.toString()).build()); + } else if (!element.isJsonNull()) { + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + element); + } + } + return result; + } + + private static KeyValueProto buildNumericKeyValueProto(JsonPrimitive value, String key) { + String valueAsString = value.getAsString(); + KeyValueProto.Builder builder = KeyValueProto.newBuilder().setKey(key); + var bd = new BigDecimal(valueAsString); + if (bd.stripTrailingZeros().scale() <= 0 && !isSimpleDouble(valueAsString)) { + try { + return builder.setType(KeyValueType.LONG_V).setLongV(bd.longValueExact()).build(); + } catch (ArithmeticException e) { + if (isTypeCastEnabled) { + return builder.setType(KeyValueType.STRING_V).setStringV(bd.toPlainString()).build(); + } else { + throw new JsonSyntaxException("Big integer values are not supported!"); + } + } + } else { + if (bd.scale() <= 16) { + return builder.setType(KeyValueType.DOUBLE_V).setDoubleV(bd.doubleValue()).build(); + } else if (isTypeCastEnabled) { + return builder.setType(KeyValueType.STRING_V).setStringV(bd.toPlainString()).build(); + } else { + throw new JsonSyntaxException("Big integer values are not supported!"); + } + } + + } + + private static boolean isSimpleDouble(String valueAsString) { + return valueAsString.contains(".") && !valueAsString.contains("E") && !valueAsString.contains("e"); + } + + public static TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(JsonElement json, int requestId) throws JsonSyntaxException { + JsonObject object = json.getAsJsonObject(); + return TransportProtos.ToServerRpcRequestMsg.newBuilder().setRequestId(requestId).setMethodName(object.get("method").getAsString()).setParams(GSON.toJson(object.get("params"))).build(); + } + + private static void parseNumericValue(List result, Entry valueEntry, JsonPrimitive value) { + String valueAsString = value.getAsString(); + String key = valueEntry.getKey(); + var bd = new BigDecimal(valueAsString); + if (bd.stripTrailingZeros().scale() <= 0 && !isSimpleDouble(valueAsString)) { + try { + result.add(new LongDataEntry(key, bd.longValueExact())); + } catch (ArithmeticException e) { + if (isTypeCastEnabled) { + result.add(new StringDataEntry(key, bd.toPlainString())); + } else { + throw new JsonSyntaxException("Big integer values are not supported!"); + } + } + } else { + if (bd.scale() <= 16) { + result.add(new DoubleDataEntry(key, bd.doubleValue())); + } else if (isTypeCastEnabled) { + result.add(new StringDataEntry(key, bd.toPlainString())); + } else { + throw new JsonSyntaxException("Big integer values are not supported!"); + } + } + } + + public static JsonObject toJson(GetAttributeResponseMsg payload) { + JsonObject result = new JsonObject(); + if (payload.getClientAttributeListCount() > 0) { + JsonObject attrObject = new JsonObject(); + payload.getClientAttributeListList().forEach(addToObjectFromProto(attrObject)); + result.add("client", attrObject); + } + if (payload.getSharedAttributeListCount() > 0) { + JsonObject attrObject = new JsonObject(); + payload.getSharedAttributeListList().forEach(addToObjectFromProto(attrObject)); + result.add("shared", attrObject); + } + return result; + } + + public static JsonObject toJson(AttributeUpdateNotificationMsg payload) { + JsonObject result = new JsonObject(); + if (payload.getSharedUpdatedCount() > 0) { + payload.getSharedUpdatedList().forEach(addToObjectFromProto(result)); + } + if (payload.getSharedDeletedCount() > 0) { + JsonArray attrObject = new JsonArray(); + payload.getSharedDeletedList().forEach(attrObject::add); + result.add("deleted", attrObject); + } + return result; + } + + public static JsonObject getJsonObjectForGateway( + String deviceName, + TransportProtos.GetAttributeResponseMsg responseMsg + ) { + JsonObject result = new JsonObject(); + result.addProperty("id", responseMsg.getRequestId()); + result.addProperty(DEVICE_PROPERTY, deviceName); + if (responseMsg.getClientAttributeListCount() > 0) { + addValues(result, responseMsg.getClientAttributeListList(), responseMsg.getIsMultipleAttributesRequest()); + } + if (responseMsg.getSharedAttributeListCount() > 0) { + addValues(result, responseMsg.getSharedAttributeListList(), responseMsg.getIsMultipleAttributesRequest()); + } + return result; + } + + public static JsonObject getJsonObjectForGateway(String deviceName, AttributeUpdateNotificationMsg + notificationMsg) { + JsonObject result = new JsonObject(); + result.addProperty(DEVICE_PROPERTY, deviceName); + result.add("data", toJson(notificationMsg)); + return result; + } + + private static void addValues(JsonObject result, List kvList, boolean multipleAttrKeysRequested) { + if (kvList.size() == 1 && !multipleAttrKeysRequested) { + addValueToJson(result, "value", kvList.get(0).getKv()); + } else { + JsonObject values; + if (result.has("values")) { + values = result.get("values").getAsJsonObject(); + } else { + values = new JsonObject(); + result.add("values", values); + } + kvList.forEach(value -> addValueToJson(values, value.getKv().getKey(), value.getKv())); + } + } + + private static void addValueToJson(JsonObject json, String name, TransportProtos.KeyValueProto entry) { + switch (entry.getType()) { + case BOOLEAN_V: + json.addProperty(name, entry.getBoolV()); + break; + case STRING_V: + json.addProperty(name, entry.getStringV()); + break; + case DOUBLE_V: + json.addProperty(name, entry.getDoubleV()); + break; + case LONG_V: + json.addProperty(name, entry.getLongV()); + break; + case JSON_V: + json.add(name, JSON_PARSER.parse(entry.getJsonV())); + break; + } + } + + private static Consumer addToObjectFromProto(JsonObject result) { + return de -> { + switch (de.getKv().getType()) { + case BOOLEAN_V: + result.add(de.getKv().getKey(), new JsonPrimitive(de.getKv().getBoolV())); + break; + case DOUBLE_V: + result.add(de.getKv().getKey(), new JsonPrimitive(de.getKv().getDoubleV())); + break; + case LONG_V: + result.add(de.getKv().getKey(), new JsonPrimitive(de.getKv().getLongV())); + break; + case STRING_V: + result.add(de.getKv().getKey(), new JsonPrimitive(de.getKv().getStringV())); + break; + case JSON_V: + result.add(de.getKv().getKey(), JSON_PARSER.parse(de.getKv().getJsonV())); + break; + default: + throw new IllegalArgumentException("Unsupported data type: " + de.getKv().getType()); + } + }; + } + + private static Consumer addToObject(JsonObject result) { + return de -> { + switch (de.getDataType()) { + case BOOLEAN: + result.add(de.getKey(), new JsonPrimitive(de.getBooleanValue().get())); + break; + case DOUBLE: + result.add(de.getKey(), new JsonPrimitive(de.getDoubleValue().get())); + break; + case LONG: + result.add(de.getKey(), new JsonPrimitive(de.getLongValue().get())); + break; + case STRING: + result.add(de.getKey(), new JsonPrimitive(de.getStrValue().get())); + break; + case JSON: + result.add(de.getKey(), JSON_PARSER.parse(de.getJsonValue().get())); + break; + default: + throw new IllegalArgumentException("Unsupported data type: " + de.getDataType()); + } + }; + } + + public static JsonElement toJson(TransportProtos.ToServerRpcResponseMsg msg) { + if (StringUtils.isEmpty(msg.getError())) { + return JSON_PARSER.parse(msg.getPayload()); + } else { + JsonObject errorMsg = new JsonObject(); + errorMsg.addProperty("error", msg.getError()); + return errorMsg; + } + } + + public static JsonObject toJson(ProvisionDeviceResponseMsg payload) { + return toJson(payload, false, 0); + } + + public static JsonObject toJson(ProvisionDeviceResponseMsg payload, int requestId) { + return toJson(payload, true, requestId); + } + + private static JsonObject toJson(ProvisionDeviceResponseMsg payload, boolean toGateway, int requestId) { + JsonObject result = new JsonObject(); + if (payload.getStatus() == ResponseStatus.NOT_FOUND) { + result.addProperty("errorMsg", "Provision data was not found!"); + result.addProperty("status", ResponseStatus.NOT_FOUND.name()); + } else if (payload.getStatus() == TransportProtos.ResponseStatus.FAILURE) { + result.addProperty("errorMsg", "Failed to provision device!"); + result.addProperty("status", ResponseStatus.FAILURE.name()); + } else { + if (toGateway) { + result.addProperty("id", requestId); + } + switch (payload.getCredentialsType()) { + case ACCESS_TOKEN: + case X509_CERTIFICATE: + result.addProperty("credentialsValue", payload.getCredentialsValue()); + break; + case MQTT_BASIC: + result.add("credentialsValue", JSON_PARSER.parse(payload.getCredentialsValue()).getAsJsonObject()); + break; + case LWM2M_CREDENTIALS: + break; + } + result.addProperty("credentialsType", payload.getCredentialsType().name()); + result.addProperty("status", ResponseStatus.SUCCESS.name()); + } + return result; + } + + public static JsonElement toErrorJson(String errorMsg) { + JsonObject error = new JsonObject(); + error.addProperty("error", errorMsg); + return error; + } + + public static JsonElement toGatewayJson(String deviceName, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) { + JsonObject result = new JsonObject(); + result.addProperty(DEVICE_PROPERTY, deviceName); + result.add("data", JsonConverter.toJson(rpcRequest, true)); + return result; + } + + public static JsonElement toGatewayJson(String deviceName, TransportProtos.ProvisionDeviceResponseMsg + responseRequest) { + JsonObject result = new JsonObject(); + result.addProperty(DEVICE_PROPERTY, deviceName); + result.add("data", JsonConverter.toJson(responseRequest)); + return result; + } + + public static Set convertToAttributes(JsonElement element) { + Set result = new HashSet<>(); + long ts = System.currentTimeMillis(); + result.addAll(parseValues(element.getAsJsonObject()).stream().map(kv -> new BaseAttributeKvEntry(kv, ts)).collect(Collectors.toList())); + return result; + } + + private static List parseValues(JsonObject valuesObject) { + List result = new ArrayList<>(); + for (Entry valueEntry : valuesObject.entrySet()) { + JsonElement element = valueEntry.getValue(); + if (element.isJsonPrimitive()) { + JsonPrimitive value = element.getAsJsonPrimitive(); + if (value.isString()) { + if (maxStringValueLength > 0 && value.getAsString().length() > maxStringValueLength) { + String message = String.format("String value length [%d] for key [%s] is greater than maximum allowed [%d]", value.getAsString().length(), valueEntry.getKey(), maxStringValueLength); + throw new JsonSyntaxException(message); + } + if (isTypeCastEnabled && NumberUtils.isParsable(value.getAsString())) { + try { + parseNumericValue(result, valueEntry, value); + } catch (RuntimeException th) { + result.add(new StringDataEntry(valueEntry.getKey(), value.getAsString())); + } + } else { + result.add(new StringDataEntry(valueEntry.getKey(), value.getAsString())); + } + } else if (value.isBoolean()) { + result.add(new BooleanDataEntry(valueEntry.getKey(), value.getAsBoolean())); + } else if (value.isNumber()) { + parseNumericValue(result, valueEntry, value); + } else { + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + value); + } + } else if (element.isJsonObject() || element.isJsonArray()) { + result.add(new JsonDataEntry(valueEntry.getKey(), element.toString())); + } else { + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + element); + } + } + return result; + } + + public static Map> convertToTelemetry(JsonElement jsonElement, long systemTs) throws + JsonSyntaxException { + return convertToTelemetry(jsonElement, systemTs, false); + } + + public static Map> convertToSortedTelemetry(JsonElement jsonElement, long systemTs) throws + JsonSyntaxException { + return convertToTelemetry(jsonElement, systemTs, true); + } + + public static Map> convertToTelemetry(JsonElement jsonElement, long systemTs, boolean sorted) throws + JsonSyntaxException { + Map> result = sorted ? new TreeMap<>() : new HashMap<>(); + convertToTelemetry(jsonElement, systemTs, result, null); + return result; + } + + + private static void parseObject(Map> result, long systemTs, JsonObject jo) { + if (jo.has("ts") && jo.has("values")) { + parseWithTs(result, jo); + } else { + parseWithoutTs(result, systemTs, jo); + } + } + + private static void parseWithoutTs(Map> result, long systemTs, JsonObject jo) { + for (KvEntry entry : parseValues(jo)) { + result.computeIfAbsent(systemTs, tmp -> new ArrayList<>()).add(entry); + } + } + + public static void parseWithTs(Map> result, JsonObject jo) { + long ts = jo.get("ts").getAsLong(); + JsonObject valuesObject = jo.get("values").getAsJsonObject(); + for (KvEntry entry : parseValues(valuesObject)) { + result.computeIfAbsent(ts, tmp -> new ArrayList<>()).add(entry); + } + } + + public static JsonElement parse(String json) { + return JSON_PARSER.parse(json); + } + + public static T parse(String json, Class clazz) { + return fromJson(parse(json), clazz); + } + + public static String toJson(JsonElement element) { + return GSON.toJson(element); + } + + public static JsonObject toJsonObject(Object o) { + return (JsonObject) GSON.toJsonTree(o); + } + + public static T fromJson(JsonElement element, Class type) { + return GSON.fromJson(element, type); + } + + public static void setTypeCastEnabled(boolean enabled) { + isTypeCastEnabled = enabled; + } + + public static void setMaxStringValueLength(int length) { + maxStringValueLength = length; + } + + public static TransportProtos.ProvisionDeviceRequestMsg convertToProvisionRequestMsg(String json) { + JsonElement jsonElement = JSON_PARSER.parse(json); + if (jsonElement.isJsonObject()) { + return buildProvisionRequestMsg(jsonElement.getAsJsonObject()); + } else { + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonElement); + } + } + + public static TransportProtos.ProvisionDeviceRequestMsg convertToProvisionRequestMsg(JsonObject jo) { + return buildProvisionRequestMsg(jo); + } + + private static TransportProtos.ProvisionDeviceRequestMsg buildProvisionRequestMsg(JsonObject jo) { + return TransportProtos.ProvisionDeviceRequestMsg.newBuilder() + .setDeviceName(getStrValue(jo, DataConstants.DEVICE_NAME, false)) + .setCredentialsType(jo.get(DataConstants.CREDENTIALS_TYPE) != null ? TransportProtos.CredentialsType.valueOf(getStrValue(jo, DataConstants.CREDENTIALS_TYPE, false)) : CredentialsType.ACCESS_TOKEN) + .setCredentialsDataProto(TransportProtos.CredentialsDataProto.newBuilder() + .setValidateDeviceTokenRequestMsg(ValidateDeviceTokenRequestMsg.newBuilder().setToken(getStrValue(jo, DataConstants.TOKEN, false)).build()) + .setValidateBasicMqttCredRequestMsg(ValidateBasicMqttCredRequestMsg.newBuilder() + .setClientId(getStrValue(jo, DataConstants.CLIENT_ID, false)) + .setUserName(getStrValue(jo, DataConstants.USERNAME, false)) + .setPassword(getStrValue(jo, DataConstants.PASSWORD, false)) + .build()) + .setValidateDeviceX509CertRequestMsg(ValidateDeviceX509CertRequestMsg.newBuilder() + .setHash(getStrValue(jo, DataConstants.HASH, false)).build()) + .build()) + .setProvisionDeviceCredentialsMsg(buildProvisionDeviceCredentialsMsg( + getStrValue(jo, DataConstants.PROVISION_KEY, true), + getStrValue(jo, DataConstants.PROVISION_SECRET, true))) + .build(); + } + + private static TransportProtos.ProvisionDeviceCredentialsMsg buildProvisionDeviceCredentialsMsg(String provisionKey, String provisionSecret) { + return TransportProtos.ProvisionDeviceCredentialsMsg.newBuilder() + .setProvisionDeviceKey(provisionKey) + .setProvisionDeviceSecret(provisionSecret) + .build(); + } + + + private static String getStrValue(JsonObject jo, String field, boolean requiredField) { + if (jo.has(field)) { + return jo.get(field).getAsString(); + } else { + if (requiredField) { + throw new RuntimeException("Failed to find the field " + field + " in JSON body " + jo + "!"); + } + return ""; + } + } +} diff --git a/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverterConfig.java b/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverterConfig.java new file mode 100644 index 0000000000..e160c051f5 --- /dev/null +++ b/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverterConfig.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.adaptor; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +@Configuration +@Slf4j +public class JsonConverterConfig { + + @Value("${transport.json.type_cast_enabled:true}") + public void setIsJsonTypeCastEnabled(boolean jsonTypeCastEnabled) { + JsonConverter.setTypeCastEnabled(jsonTypeCastEnabled); + log.info("JSON type cast enabled = {}", jsonTypeCastEnabled); + } + + @Value("${transport.json.max_string_value_length:0}") + public void setMaxStringValueLength(int maxStringValueLength) { + JsonConverter.setMaxStringValueLength(maxStringValueLength); + log.info("JSON max string value length = {}", maxStringValueLength); + } +} diff --git a/common/proto/src/main/java/org/thingsboard/server/common/adaptor/ProtoConverter.java b/common/proto/src/main/java/org/thingsboard/server/common/adaptor/ProtoConverter.java new file mode 100644 index 0000000000..098108c864 --- /dev/null +++ b/common/proto/src/main/java/org/thingsboard/server/common/adaptor/ProtoConverter.java @@ -0,0 +1,207 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.adaptor; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.protobuf.Descriptors; +import com.google.protobuf.DynamicMessage; +import com.google.protobuf.InvalidProtocolBufferException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.CollectionUtils; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.DynamicProtoUtils; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.gen.transport.TransportApiProtos; +import org.thingsboard.server.gen.transport.TransportProtos; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@Slf4j +public class ProtoConverter { + + public static final Gson GSON = new Gson(); + public static final JsonParser JSON_PARSER = new JsonParser(); + + public static TransportProtos.PostTelemetryMsg convertToTelemetryProto(byte[] payload) throws InvalidProtocolBufferException, IllegalArgumentException { + TransportProtos.TsKvListProto protoPayload = TransportProtos.TsKvListProto.parseFrom(payload); + TransportProtos.PostTelemetryMsg.Builder postTelemetryMsgBuilder = TransportProtos.PostTelemetryMsg.newBuilder(); + TransportProtos.TsKvListProto tsKvListProto = validateTsKvListProto(protoPayload); + postTelemetryMsgBuilder.addTsKvList(tsKvListProto); + return postTelemetryMsgBuilder.build(); + } + + public static TransportProtos.PostTelemetryMsg validatePostTelemetryMsg(byte[] payload) throws InvalidProtocolBufferException, IllegalArgumentException { + TransportProtos.PostTelemetryMsg msg = TransportProtos.PostTelemetryMsg.parseFrom(payload); + TransportProtos.PostTelemetryMsg.Builder postTelemetryMsgBuilder = TransportProtos.PostTelemetryMsg.newBuilder(); + List tsKvListProtoList = msg.getTsKvListList(); + if (!CollectionUtils.isEmpty(tsKvListProtoList)) { + List tsKvListProtos = new ArrayList<>(); + tsKvListProtoList.forEach(tsKvListProto -> { + TransportProtos.TsKvListProto transportTsKvListProto = validateTsKvListProto(tsKvListProto); + tsKvListProtos.add(transportTsKvListProto); + }); + postTelemetryMsgBuilder.addAllTsKvList(tsKvListProtos); + return postTelemetryMsgBuilder.build(); + } else { + throw new IllegalArgumentException("TsKv list is empty!"); + } + } + + public static TransportProtos.PostAttributeMsg validatePostAttributeMsg(TransportProtos.PostAttributeMsg msg) throws IllegalArgumentException, InvalidProtocolBufferException { + if (!CollectionUtils.isEmpty(msg.getKvList())) { + byte[] bytes = msg.toByteArray(); + TransportProtos.PostAttributeMsg proto = TransportProtos.PostAttributeMsg.parseFrom(bytes); + List kvList = proto.getKvList(); + List keyValueProtos = validateKeyValueProtos(kvList); + TransportProtos.PostAttributeMsg.Builder result = TransportProtos.PostAttributeMsg.newBuilder(); + result.addAllKv(keyValueProtos); + result.setShared(msg.getShared()); + return result.build(); + } else { + throw new IllegalArgumentException("KeyValue list is empty!"); + } + } + + public static TransportProtos.ClaimDeviceMsg convertToClaimDeviceProto(DeviceId deviceId, byte[] bytes) throws InvalidProtocolBufferException { + if (bytes == null) { + return buildClaimDeviceMsg(deviceId, DataConstants.DEFAULT_SECRET_KEY, 0); + } + TransportApiProtos.ClaimDevice proto = TransportApiProtos.ClaimDevice.parseFrom(bytes); + String secretKey = proto.getSecretKey() != null ? proto.getSecretKey() : DataConstants.DEFAULT_SECRET_KEY; + long durationMs = proto.getDurationMs(); + return buildClaimDeviceMsg(deviceId, secretKey, durationMs); + } + + public static TransportProtos.GetAttributeRequestMsg convertToGetAttributeRequestMessage(byte[] bytes, int requestId) throws InvalidProtocolBufferException, RuntimeException { + TransportApiProtos.AttributesRequest proto = TransportApiProtos.AttributesRequest.parseFrom(bytes); + TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder(); + result.setRequestId(requestId); + String clientKeys = proto.getClientKeys(); + String sharedKeys = proto.getSharedKeys(); + if (!StringUtils.isEmpty(clientKeys)) { + List clientKeysList = Arrays.asList(clientKeys.split(",")); + result.addAllClientAttributeNames(clientKeysList); + } + if (!StringUtils.isEmpty(sharedKeys)) { + List sharedKeysList = Arrays.asList(sharedKeys.split(",")); + result.addAllSharedAttributeNames(sharedKeysList); + } + return result.build(); + } + + public static TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(byte[] bytes, int requestId) throws InvalidProtocolBufferException { + TransportApiProtos.RpcRequest proto = TransportApiProtos.RpcRequest.parseFrom(bytes); + String method = proto.getMethod(); + String params = proto.getParams(); + return TransportProtos.ToServerRpcRequestMsg.newBuilder().setRequestId(requestId).setMethodName(method).setParams(params).build(); + } + + private static TransportProtos.ClaimDeviceMsg buildClaimDeviceMsg(DeviceId deviceId, String secretKey, long durationMs) { + TransportProtos.ClaimDeviceMsg.Builder result = TransportProtos.ClaimDeviceMsg.newBuilder(); + return result + .setDeviceIdMSB(deviceId.getId().getMostSignificantBits()) + .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits()) + .setSecretKey(secretKey) + .setDurationMs(durationMs) + .build(); + } + + private static TransportProtos.TsKvListProto validateTsKvListProto(TransportProtos.TsKvListProto tsKvListProto) { + TransportProtos.TsKvListProto.Builder tsKvListBuilder = TransportProtos.TsKvListProto.newBuilder(); + long ts = tsKvListProto.getTs(); + if (ts == 0) { + ts = System.currentTimeMillis(); + } + tsKvListBuilder.setTs(ts); + List kvList = tsKvListProto.getKvList(); + if (!CollectionUtils.isEmpty(kvList)) { + List keyValueListProtos = validateKeyValueProtos(kvList); + tsKvListBuilder.addAllKv(keyValueListProtos); + return tsKvListBuilder.build(); + } else { + throw new IllegalArgumentException("KeyValue list is empty!"); + } + } + + public static TransportProtos.ProvisionDeviceRequestMsg convertToProvisionRequestMsg(byte[] bytes) throws InvalidProtocolBufferException { + return TransportProtos.ProvisionDeviceRequestMsg.parseFrom(bytes); + } + + private static List validateKeyValueProtos(List kvList) { + kvList.forEach(keyValueProto -> { + String key = keyValueProto.getKey(); + if (StringUtils.isEmpty(key)) { + throw new IllegalArgumentException("Invalid key value: " + key + "!"); + } + TransportProtos.KeyValueType type = keyValueProto.getType(); + switch (type) { + case BOOLEAN_V: + case LONG_V: + case DOUBLE_V: + break; + case STRING_V: + if (StringUtils.isEmpty(keyValueProto.getStringV())) { + throw new IllegalArgumentException("Value is empty for key: " + key + "!"); + } + break; + case JSON_V: + try { + JSON_PARSER.parse(keyValueProto.getJsonV()); + } catch (Exception e) { + throw new IllegalArgumentException("Can't parse value: " + keyValueProto.getJsonV() + " for key: " + key + "!"); + } + break; + case UNRECOGNIZED: + throw new IllegalArgumentException("Unsupported keyValueType: " + type + "!"); + } + }); + return kvList; + } + + public static byte[] convertToRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg, DynamicMessage.Builder rpcRequestDynamicMessageBuilder) throws AdaptorException { + rpcRequestDynamicMessageBuilder = rpcRequestDynamicMessageBuilder.getDefaultInstanceForType().newBuilderForType(); + JsonObject rpcRequestJson = new JsonObject(); + rpcRequestJson.addProperty("method", toDeviceRpcRequestMsg.getMethodName()); + rpcRequestJson.addProperty("requestId", toDeviceRpcRequestMsg.getRequestId()); + String params = toDeviceRpcRequestMsg.getParams(); + try { + JsonElement paramsElement = JSON_PARSER.parse(params); + rpcRequestJson.add("params", paramsElement); + DynamicMessage dynamicRpcRequest = DynamicProtoUtils.jsonToDynamicMessage(rpcRequestDynamicMessageBuilder, GSON.toJson(rpcRequestJson)); + return dynamicRpcRequest.toByteArray(); + } catch (Exception e) { + throw new AdaptorException("Failed to convert ToDeviceRpcRequestMsg to Dynamic Rpc request message due to: ", e); + } + } + + public static Descriptors.Descriptor validateDescriptor(Descriptors.Descriptor descriptor) throws AdaptorException { + if (descriptor == null) { + throw new AdaptorException("Failed to get dynamic message descriptor!"); + } + return descriptor; + } + + public static String dynamicMsgToJson(byte[] bytes, Descriptors.Descriptor descriptor) throws InvalidProtocolBufferException { + return DynamicProtoUtils.dynamicMsgToJson(descriptor, bytes); + } + +} diff --git a/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java b/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java new file mode 100644 index 0000000000..fd9e35b860 --- /dev/null +++ b/common/proto/src/main/java/org/thingsboard/server/common/util/ProtoUtils.java @@ -0,0 +1,499 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.util; + +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.AttributeKey; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +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.JsonDataEntry; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; +import org.thingsboard.server.common.data.rpc.RpcError; +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.common.data.security.DeviceCredentialsType; +import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; +import org.thingsboard.server.common.msg.edge.EdgeEventUpdateMsg; +import org.thingsboard.server.common.msg.edge.FromEdgeSyncResponse; +import org.thingsboard.server.common.msg.edge.ToEdgeSyncRequest; +import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponseActorMsg; +import org.thingsboard.server.common.msg.rpc.RemoveRpcActorMsg; +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequestActorMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceAttributesEventNotificationMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceCredentialsUpdateNotificationMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceDeleteMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceEdgeUpdateMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceNameOrTypeUpdateMsg; +import org.thingsboard.server.gen.transport.TransportProtos; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +public class ProtoUtils { + + private static final EntityType[] entityTypeByProtoNumber; + + static { + int arraySize = Arrays.stream(EntityType.values()).mapToInt(EntityType::getProtoNumber).max().orElse(0); + entityTypeByProtoNumber = new EntityType[arraySize + 1]; + Arrays.stream(EntityType.values()).forEach(entityType -> entityTypeByProtoNumber[entityType.getProtoNumber()] = entityType); + } + + public static TransportProtos.ComponentLifecycleMsgProto toProto(ComponentLifecycleMsg msg) { + return TransportProtos.ComponentLifecycleMsgProto.newBuilder() + .setTenantIdMSB(msg.getTenantId().getId().getMostSignificantBits()) + .setTenantIdLSB(msg.getTenantId().getId().getLeastSignificantBits()) + .setEntityType(toProto(msg.getEntityId().getEntityType())) + .setEntityIdMSB(msg.getEntityId().getId().getMostSignificantBits()) + .setEntityIdLSB(msg.getEntityId().getId().getLeastSignificantBits()) + .setEvent(TransportProtos.ComponentLifecycleEvent.forNumber(msg.getEvent().ordinal())) + .build(); + } + + public static TransportProtos.EntityTypeProto toProto(EntityType entityType) { + return TransportProtos.EntityTypeProto.forNumber(entityType.getProtoNumber()); + } + + public static ComponentLifecycleMsg fromProto(TransportProtos.ComponentLifecycleMsgProto proto) { + return new ComponentLifecycleMsg( + TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), + EntityIdFactory.getByTypeAndUuid(fromProto(proto.getEntityType()), new UUID(proto.getEntityIdMSB(), proto.getEntityIdLSB())), + ComponentLifecycleEvent.values()[proto.getEventValue()] + ); + } + + public static EntityType fromProto(TransportProtos.EntityTypeProto entityType) { + return entityTypeByProtoNumber[entityType.getNumber()]; + } + + + public static TransportProtos.ToEdgeSyncRequestMsgProto toProto(ToEdgeSyncRequest request) { + return TransportProtos.ToEdgeSyncRequestMsgProto.newBuilder() + .setTenantIdMSB(request.getTenantId().getId().getMostSignificantBits()) + .setTenantIdLSB(request.getTenantId().getId().getLeastSignificantBits()) + .setRequestIdMSB(request.getId().getMostSignificantBits()) + .setRequestIdLSB(request.getId().getLeastSignificantBits()) + .setEdgeIdMSB(request.getEdgeId().getId().getMostSignificantBits()) + .setEdgeIdLSB(request.getEdgeId().getId().getLeastSignificantBits()) + .build(); + } + + public static ToEdgeSyncRequest fromProto(TransportProtos.ToEdgeSyncRequestMsgProto proto) { + return new ToEdgeSyncRequest( + new UUID(proto.getRequestIdMSB(), proto.getRequestIdLSB()), + TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), + new EdgeId(new UUID(proto.getEdgeIdMSB(), proto.getEdgeIdLSB())) + ); + } + + public static TransportProtos.FromEdgeSyncResponseMsgProto toProto(FromEdgeSyncResponse response) { + return TransportProtos.FromEdgeSyncResponseMsgProto.newBuilder() + .setTenantIdMSB(response.getTenantId().getId().getMostSignificantBits()) + .setTenantIdLSB(response.getTenantId().getId().getLeastSignificantBits()) + .setResponseIdMSB(response.getId().getMostSignificantBits()) + .setResponseIdLSB(response.getId().getLeastSignificantBits()) + .setEdgeIdMSB(response.getEdgeId().getId().getMostSignificantBits()) + .setEdgeIdLSB(response.getEdgeId().getId().getLeastSignificantBits()) + .setSuccess(response.isSuccess()) + .build(); + } + + public static FromEdgeSyncResponse fromProto(TransportProtos.FromEdgeSyncResponseMsgProto proto) { + return new FromEdgeSyncResponse( + new UUID(proto.getResponseIdMSB(), proto.getResponseIdLSB()), + TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), + new EdgeId(new UUID(proto.getEdgeIdMSB(), proto.getEdgeIdLSB())), + proto.getSuccess() + ); + } + + public static TransportProtos.EdgeEventUpdateMsgProto toProto(EdgeEventUpdateMsg msg) { + return TransportProtos.EdgeEventUpdateMsgProto.newBuilder() + .setTenantIdMSB(msg.getTenantId().getId().getMostSignificantBits()) + .setTenantIdLSB(msg.getTenantId().getId().getLeastSignificantBits()) + .setEdgeIdMSB(msg.getEdgeId().getId().getMostSignificantBits()) + .setEdgeIdLSB(msg.getEdgeId().getId().getLeastSignificantBits()) + .build(); + } + + public static EdgeEventUpdateMsg fromProto(TransportProtos.EdgeEventUpdateMsgProto proto) { + return new EdgeEventUpdateMsg( + TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), + new EdgeId(new UUID(proto.getEdgeIdMSB(), proto.getEdgeIdLSB())) + ); + } + + private static TransportProtos.DeviceEdgeUpdateMsgProto toProto(DeviceEdgeUpdateMsg msg) { + TransportProtos.DeviceEdgeUpdateMsgProto.Builder builder = TransportProtos.DeviceEdgeUpdateMsgProto.newBuilder() + .setTenantIdMSB(msg.getTenantId().getId().getMostSignificantBits()) + .setTenantIdLSB(msg.getTenantId().getId().getLeastSignificantBits()) + .setDeviceIdMSB(msg.getDeviceId().getId().getMostSignificantBits()) + .setDeviceIdLSB(msg.getDeviceId().getId().getLeastSignificantBits()); + + if (msg.getEdgeId() != null) { + builder.setEdgeIdMSB(msg.getEdgeId().getId().getMostSignificantBits()) + .setEdgeIdLSB(msg.getEdgeId().getId().getLeastSignificantBits()); + } + + return builder.build(); + } + + private static DeviceEdgeUpdateMsg fromProto(TransportProtos.DeviceEdgeUpdateMsgProto proto) { + EdgeId edgeId = null; + if (proto.hasEdgeIdMSB() && proto.hasEdgeIdLSB()) { + edgeId = new EdgeId(new UUID(proto.getEdgeIdMSB(), proto.getEdgeIdLSB())); + } + return new DeviceEdgeUpdateMsg( + TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), + new DeviceId(new UUID(proto.getDeviceIdMSB(), proto.getDeviceIdLSB())), + edgeId); + } + + private static TransportProtos.DeviceNameOrTypeUpdateMsgProto toProto(DeviceNameOrTypeUpdateMsg msg) { + return TransportProtos.DeviceNameOrTypeUpdateMsgProto.newBuilder() + .setTenantIdMSB(msg.getTenantId().getId().getMostSignificantBits()) + .setTenantIdLSB(msg.getTenantId().getId().getLeastSignificantBits()) + .setDeviceIdMSB(msg.getDeviceId().getId().getMostSignificantBits()) + .setDeviceIdLSB(msg.getDeviceId().getId().getLeastSignificantBits()) + .setDeviceName(msg.getDeviceName()) + .setDeviceType(msg.getDeviceType()) + .build(); + } + + private static DeviceNameOrTypeUpdateMsg fromProto(TransportProtos.DeviceNameOrTypeUpdateMsgProto proto) { + return new DeviceNameOrTypeUpdateMsg( + TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), + new DeviceId(new UUID(proto.getDeviceIdMSB(), proto.getDeviceIdLSB())), + proto.getDeviceName(), + proto.getDeviceType() + ); + } + + private static TransportProtos.DeviceAttributesEventMsgProto toProto(DeviceAttributesEventNotificationMsg msg) { + TransportProtos.DeviceAttributesEventMsgProto.Builder builder = TransportProtos.DeviceAttributesEventMsgProto.newBuilder(); + builder.setTenantIdMSB(msg.getTenantId().getId().getMostSignificantBits()) + .setTenantIdLSB(msg.getTenantId().getId().getLeastSignificantBits()) + .setDeviceIdMSB(msg.getDeviceId().getId().getMostSignificantBits()) + .setDeviceIdLSB(msg.getDeviceId().getId().getLeastSignificantBits()) + .setDeleted(msg.isDeleted()); + + if (msg.getScope() != null) { + builder.setScope(TransportProtos.AttributeScopeProto.valueOf(msg.getScope())); + } + + if (msg.getDeletedKeys() != null) { + for (AttributeKey key : msg.getDeletedKeys()) { + builder.addDeletedKeys(TransportProtos.AttributeKey.newBuilder() + .setScope(TransportProtos.AttributeScopeProto.valueOf(key.getScope())) + .setAttributeKey(key.getAttributeKey()) + .build()); + } + } + + if (msg.getValues() != null) { + for (AttributeKvEntry attributeKvEntry : msg.getValues()) { + TransportProtos.AttributeValueProto.Builder attributeValueBuilder = TransportProtos.AttributeValueProto.newBuilder() + .setLastUpdateTs(attributeKvEntry.getLastUpdateTs()) + .setKey(attributeKvEntry.getKey()); + switch (attributeKvEntry.getDataType()) { + case BOOLEAN: + attributeKvEntry.getBooleanValue().ifPresent(attributeValueBuilder::setBoolV); + attributeValueBuilder.setHasV(attributeKvEntry.getBooleanValue().isPresent()); + attributeValueBuilder.setType(TransportProtos.KeyValueType.BOOLEAN_V); + break; + case STRING: + attributeKvEntry.getStrValue().ifPresent(attributeValueBuilder::setStringV); + attributeValueBuilder.setHasV(attributeKvEntry.getStrValue().isPresent()); + attributeValueBuilder.setType(TransportProtos.KeyValueType.STRING_V); + break; + case DOUBLE: + attributeKvEntry.getDoubleValue().ifPresent(attributeValueBuilder::setDoubleV); + attributeValueBuilder.setHasV(attributeKvEntry.getDoubleValue().isPresent()); + attributeValueBuilder.setType(TransportProtos.KeyValueType.DOUBLE_V); + break; + case LONG: + attributeKvEntry.getLongValue().ifPresent(attributeValueBuilder::setLongV); + attributeValueBuilder.setHasV(attributeKvEntry.getLongValue().isPresent()); + attributeValueBuilder.setType(TransportProtos.KeyValueType.LONG_V); + break; + case JSON: + attributeKvEntry.getJsonValue().ifPresent(attributeValueBuilder::setJsonV); + attributeValueBuilder.setHasV(attributeKvEntry.getJsonValue().isPresent()); + attributeValueBuilder.setType(TransportProtos.KeyValueType.JSON_V); + break; + } + builder.addValues(attributeValueBuilder.build()); + } + } + return builder.build(); + } + + private static ToDeviceActorNotificationMsg fromProto(TransportProtos.DeviceAttributesEventMsgProto proto) { + return new DeviceAttributesEventNotificationMsg( + TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), + new DeviceId(new UUID(proto.getDeviceIdMSB(), proto.getDeviceIdLSB())), + getAttributeKeySetFromProto(proto.getDeletedKeysList()), + proto.hasScope() ? proto.getScope().name() : null, + getAttributesKvEntryFromProto(proto.getValuesList()), + proto.getDeleted() + ); + } + + private static TransportProtos.DeviceCredentialsUpdateMsgProto toProto(DeviceCredentialsUpdateNotificationMsg msg) { + TransportProtos.DeviceCredentialsProto.Builder protoBuilder = TransportProtos.DeviceCredentialsProto.newBuilder() + .setDeviceIdMSB(msg.getDeviceCredentials().getDeviceId().getId().getMostSignificantBits()) + .setDeviceIdLSB(msg.getDeviceCredentials().getDeviceId().getId().getLeastSignificantBits()) + .setCredentialsId(msg.getDeviceCredentials().getCredentialsId()) + .setCredentialsType(TransportProtos.CredentialsType.valueOf(msg.getDeviceCredentials().getCredentialsType().name())); + + if (msg.getDeviceCredentials().getCredentialsValue() != null) { + protoBuilder.setCredentialsValue(msg.getDeviceCredentials().getCredentialsValue()); + } + + return TransportProtos.DeviceCredentialsUpdateMsgProto.newBuilder() + .setTenantIdMSB(msg.getTenantId().getId().getMostSignificantBits()) + .setTenantIdLSB(msg.getTenantId().getId().getLeastSignificantBits()) + .setDeviceIdMSB(msg.getDeviceId().getId().getMostSignificantBits()) + .setDeviceIdLSB(msg.getDeviceId().getId().getLeastSignificantBits()) + .setDeviceCredentials(protoBuilder.build()) + .build(); + } + + private static ToDeviceActorNotificationMsg fromProto(TransportProtos.DeviceCredentialsUpdateMsgProto proto) { + DeviceCredentials deviceCredentials = new DeviceCredentials(); + deviceCredentials.setDeviceId(new DeviceId(new UUID(proto.getDeviceCredentials().getDeviceIdMSB(), proto.getDeviceCredentials().getDeviceIdLSB()))); + deviceCredentials.setCredentialsId(proto.getDeviceCredentials().getCredentialsId()); + deviceCredentials.setCredentialsValue(proto.getDeviceCredentials().hasCredentialsValue() ? proto.getDeviceCredentials().getCredentialsValue() : null); + deviceCredentials.setCredentialsType(DeviceCredentialsType.valueOf(proto.getDeviceCredentials().getCredentialsType().name())); + return new DeviceCredentialsUpdateNotificationMsg( + TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), + new DeviceId(new UUID(proto.getDeviceIdMSB(), proto.getDeviceIdLSB())), + deviceCredentials + ); + } + + private static TransportProtos.ToDeviceRpcRequestActorMsgProto toProto(ToDeviceRpcRequestActorMsg msg) { + TransportProtos.ToDeviceRpcRequestMsg proto = TransportProtos.ToDeviceRpcRequestMsg.newBuilder() + .setMethodName(msg.getMsg().getBody().getMethod()) + .setParams(msg.getMsg().getBody().getParams()) + .setExpirationTime(msg.getMsg().getExpirationTime()) + .setRequestIdMSB(msg.getMsg().getId().getMostSignificantBits()) + .setRequestIdLSB(msg.getMsg().getId().getLeastSignificantBits()) + .setOneway(msg.getMsg().isOneway()) + .build(); + + return TransportProtos.ToDeviceRpcRequestActorMsgProto.newBuilder() + .setTenantIdMSB(msg.getTenantId().getId().getMostSignificantBits()) + .setTenantIdLSB(msg.getTenantId().getId().getLeastSignificantBits()) + .setDeviceIdMSB(msg.getDeviceId().getId().getMostSignificantBits()) + .setDeviceIdLSB(msg.getDeviceId().getId().getLeastSignificantBits()) + .setServiceId(msg.getServiceId()) + .setToDeviceRpcRequestMsg(proto) + .build(); + } + + private static ToDeviceActorNotificationMsg fromProto(TransportProtos.ToDeviceRpcRequestActorMsgProto proto) { + TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg = proto.getToDeviceRpcRequestMsg(); + ToDeviceRpcRequest toDeviceRpcRequest = new ToDeviceRpcRequest( + new UUID(toDeviceRpcRequestMsg.getRequestIdMSB(), toDeviceRpcRequestMsg.getRequestIdLSB()), + TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), + new DeviceId(new UUID(proto.getDeviceIdMSB(), proto.getDeviceIdLSB())), + toDeviceRpcRequestMsg.getOneway(), + toDeviceRpcRequestMsg.getExpirationTime(), + new ToDeviceRpcRequestBody(toDeviceRpcRequestMsg.getMethodName(), toDeviceRpcRequestMsg.getParams()), + toDeviceRpcRequestMsg.getPersisted(), 0, ""); + return new ToDeviceRpcRequestActorMsg(proto.getServiceId(), toDeviceRpcRequest); + } + + private static TransportProtos.FromDeviceRpcResponseActorMsgProto toProto(FromDeviceRpcResponseActorMsg msg) { + TransportProtos.FromDeviceRPCResponseProto.Builder builder = TransportProtos.FromDeviceRPCResponseProto.newBuilder() + .setRequestIdMSB(msg.getMsg().getId().getMostSignificantBits()) + .setRequestIdLSB(msg.getMsg().getId().getLeastSignificantBits()) + .setError(msg.getMsg().getError().isPresent() ? msg.getMsg().getError().get().ordinal() : -1); + if (msg.getMsg().getResponse().isPresent()) { + builder.setResponse(msg.getMsg().getResponse().get()); + } + + return TransportProtos.FromDeviceRpcResponseActorMsgProto.newBuilder() + .setRequestId(msg.getRequestId()) + .setTenantIdMSB(msg.getTenantId().getId().getMostSignificantBits()) + .setTenantIdLSB(msg.getTenantId().getId().getLeastSignificantBits()) + .setDeviceIdMSB(msg.getDeviceId().getId().getMostSignificantBits()) + .setDeviceIdLSB(msg.getDeviceId().getId().getLeastSignificantBits()) + .setRpcResponse(builder.build()) + .build(); + } + + private static ToDeviceActorNotificationMsg fromProto(TransportProtos.FromDeviceRpcResponseActorMsgProto proto) { + FromDeviceRpcResponse fromDeviceRpcResponse = new FromDeviceRpcResponse( + new UUID(proto.getRpcResponse().getRequestIdMSB(), proto.getRpcResponse().getRequestIdLSB()), + proto.getRpcResponse().getResponse(), + proto.getRpcResponse().getError() >= 0 ? RpcError.values()[proto.getRpcResponse().getError()] : null); + return new FromDeviceRpcResponseActorMsg( + proto.getRequestId(), + TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), + new DeviceId(new UUID(proto.getDeviceIdMSB(), proto.getDeviceIdLSB())), + fromDeviceRpcResponse + ); + } + + private static TransportProtos.RemoveRpcActorMsgProto toProto(RemoveRpcActorMsg msg) { + return TransportProtos.RemoveRpcActorMsgProto.newBuilder() + .setTenantIdMSB(msg.getTenantId().getId().getMostSignificantBits()) + .setTenantIdLSB(msg.getTenantId().getId().getLeastSignificantBits()) + .setDeviceIdMSB(msg.getDeviceId().getId().getMostSignificantBits()) + .setDeviceIdLSB(msg.getDeviceId().getId().getLeastSignificantBits()) + .setRequestIdMSB(msg.getRequestId().getMostSignificantBits()) + .setRequestIdLSB(msg.getRequestId().getLeastSignificantBits()) + .build(); + } + + private static ToDeviceActorNotificationMsg fromProto(TransportProtos.RemoveRpcActorMsgProto proto) { + return new RemoveRpcActorMsg( + TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), + new DeviceId(new UUID(proto.getDeviceIdMSB(), proto.getDeviceIdLSB())), + new UUID(proto.getRequestIdMSB(), proto.getRequestIdLSB()) + ); + } + + private static TransportProtos.DeviceDeleteMsgProto toProto(DeviceDeleteMsg msg) { + return TransportProtos.DeviceDeleteMsgProto.newBuilder() + .setTenantIdMSB(msg.getTenantId().getId().getMostSignificantBits()) + .setTenantIdLSB(msg.getTenantId().getId().getLeastSignificantBits()) + .setDeviceIdMSB(msg.getDeviceId().getId().getMostSignificantBits()) + .setDeviceIdLSB(msg.getDeviceId().getId().getLeastSignificantBits()) + .build(); + } + + private static DeviceDeleteMsg fromProto(TransportProtos.DeviceDeleteMsgProto proto) { + return new DeviceDeleteMsg( + TenantId.fromUUID(new UUID(proto.getTenantIdMSB(), proto.getTenantIdLSB())), + new DeviceId(new UUID(proto.getDeviceIdMSB(), proto.getDeviceIdLSB()))); + } + + public static TransportProtos.ToDeviceActorNotificationMsgProto toProto(ToDeviceActorNotificationMsg msg) { + if (msg instanceof DeviceEdgeUpdateMsg) { + DeviceEdgeUpdateMsg updateMsg = (DeviceEdgeUpdateMsg) msg; + TransportProtos.DeviceEdgeUpdateMsgProto proto = toProto(updateMsg); + return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceEdgeUpdateMsg(proto).build(); + } else if (msg instanceof DeviceNameOrTypeUpdateMsg) { + DeviceNameOrTypeUpdateMsg updateMsg = (DeviceNameOrTypeUpdateMsg) msg; + TransportProtos.DeviceNameOrTypeUpdateMsgProto proto = toProto(updateMsg); + return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceNameOrTypeMsg(proto).build(); + } else if (msg instanceof DeviceAttributesEventNotificationMsg) { + DeviceAttributesEventNotificationMsg updateMsg = (DeviceAttributesEventNotificationMsg) msg; + TransportProtos.DeviceAttributesEventMsgProto proto = toProto(updateMsg); + return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceAttributesEventMsg(proto).build(); + } else if (msg instanceof DeviceCredentialsUpdateNotificationMsg) { + DeviceCredentialsUpdateNotificationMsg updateMsg = (DeviceCredentialsUpdateNotificationMsg) msg; + TransportProtos.DeviceCredentialsUpdateMsgProto proto = toProto(updateMsg); + return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceCredentialsUpdateMsg(proto).build(); + } else if (msg instanceof ToDeviceRpcRequestActorMsg) { + ToDeviceRpcRequestActorMsg updateMsg = (ToDeviceRpcRequestActorMsg) msg; + TransportProtos.ToDeviceRpcRequestActorMsgProto proto = toProto(updateMsg); + return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setToDeviceRpcRequestMsg(proto).build(); + } else if (msg instanceof FromDeviceRpcResponseActorMsg) { + FromDeviceRpcResponseActorMsg updateMsg = (FromDeviceRpcResponseActorMsg) msg; + TransportProtos.FromDeviceRpcResponseActorMsgProto proto = toProto(updateMsg); + return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setFromDeviceRpcResponseMsg(proto).build(); + } else if (msg instanceof RemoveRpcActorMsg) { + RemoveRpcActorMsg updateMsg = (RemoveRpcActorMsg) msg; + TransportProtos.RemoveRpcActorMsgProto proto = toProto(updateMsg); + return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setRemoveRpcActorMsg(proto).build(); + } else if (msg instanceof DeviceDeleteMsg) { + DeviceDeleteMsg updateMsg = (DeviceDeleteMsg) msg; + TransportProtos.DeviceDeleteMsgProto proto = toProto(updateMsg); + return TransportProtos.ToDeviceActorNotificationMsgProto.newBuilder().setDeviceDeleteMsg(proto).build(); + } + return null; + } + + public static ToDeviceActorNotificationMsg fromProto(TransportProtos.ToDeviceActorNotificationMsgProto proto) { + if (proto.hasDeviceEdgeUpdateMsg()) { + return fromProto(proto.getDeviceEdgeUpdateMsg()); + } else if (proto.hasDeviceNameOrTypeMsg()) { + return fromProto(proto.getDeviceNameOrTypeMsg()); + } else if (proto.hasDeviceAttributesEventMsg()) { + return fromProto(proto.getDeviceAttributesEventMsg()); + } else if (proto.hasDeviceCredentialsUpdateMsg()) { + return fromProto(proto.getDeviceCredentialsUpdateMsg()); + } else if (proto.hasToDeviceRpcRequestMsg()) { + return fromProto(proto.getToDeviceRpcRequestMsg()); + } else if (proto.hasFromDeviceRpcResponseMsg()) { + return fromProto(proto.getFromDeviceRpcResponseMsg()); + } else if (proto.hasRemoveRpcActorMsg()) { + return fromProto(proto.getRemoveRpcActorMsg()); + } else if (proto.hasDeviceDeleteMsg()) { + return fromProto(proto.getDeviceDeleteMsg()); + } + return null; + } + + private static Set getAttributeKeySetFromProto(List deletedKeysList) { + if (deletedKeysList.isEmpty()) { + return null; + } + return deletedKeysList.stream() + .map(attributeKey -> new AttributeKey(attributeKey.getScope().name(), attributeKey.getAttributeKey())) + .collect(Collectors.toSet()); + } + + private static List getAttributesKvEntryFromProto(List valuesList) { + if (valuesList.isEmpty()) { + return null; + } + List result = new ArrayList<>(); + for (TransportProtos.AttributeValueProto kvEntry : valuesList) { + boolean hasValue = kvEntry.getHasV(); + KvEntry entry = null; + switch (kvEntry.getType()) { + case BOOLEAN_V: + entry = new BooleanDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getBoolV() : null); + break; + case LONG_V: + entry = new LongDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getLongV() : null); + break; + case DOUBLE_V: + entry = new DoubleDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getDoubleV() : null); + break; + case STRING_V: + entry = new StringDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getStringV() : null); + break; + case JSON_V: + entry = new JsonDataEntry(kvEntry.getKey(), hasValue ? kvEntry.getJsonV() : null); + break; + } + result.add(new BaseAttributeKvEntry(kvEntry.getLastUpdateTs(), entry)); + } + return result; + } +} diff --git a/common/cluster-api/src/main/proto/jsinvoke.proto b/common/proto/src/main/proto/jsinvoke.proto similarity index 100% rename from common/cluster-api/src/main/proto/jsinvoke.proto rename to common/proto/src/main/proto/jsinvoke.proto diff --git a/common/proto/src/main/proto/queue.proto b/common/proto/src/main/proto/queue.proto new file mode 100644 index 0000000000..fea4848654 --- /dev/null +++ b/common/proto/src/main/proto/queue.proto @@ -0,0 +1,1393 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +syntax = "proto3"; + +package transport; + +option java_package = "org.thingsboard.server.gen.transport"; +option java_outer_classname = "TransportProtos"; + +/** + * Common data structures + */ +enum EntityTypeProto { + UNSPECIFIED = 0; + TENANT = 1; + CUSTOMER = 2; + USER = 3; + DASHBOARD = 4; + ASSET = 5; + DEVICE = 6; + ALARM = 7; + // next 3 reserved for PE; + RULE_CHAIN = 11; + RULE_NODE = 12; + // next 2 reserved for PE; + ENTITY_VIEW = 15; + WIDGETS_BUNDLE = 16; + WIDGET_TYPE = 17; + // next 2 reserved for PE; + TENANT_PROFILE = 20; + DEVICE_PROFILE = 21; + ASSET_PROFILE = 22; + API_USAGE_STATE = 23; + TB_RESOURCE = 24; + OTA_PACKAGE = 25; + EDGE = 26; + RPC = 27; + QUEUE = 28; + NOTIFICATION_TARGET = 29; + NOTIFICATION_TEMPLATE = 30; + NOTIFICATION_REQUEST = 31; + NOTIFICATION = 32; + NOTIFICATION_RULE = 33; +} + +/** + * Service Discovery Data Structures; + */ +message ServiceInfo { + string serviceId = 1; + repeated string serviceTypes = 2; + repeated string transports = 6; + SystemInfoProto systemInfo = 10; + repeated string assignedTenantProfiles = 11; +} + +message SystemInfoProto { + int64 cpuUsage = 1; + int64 cpuCount = 2; + int64 memoryUsage = 3; + int64 totalMemory = 4; + int64 diskUsage = 5; + int64 totalDiscSpace = 6; +} + +/** + * Transport Service Data Structures; + */ +message SessionInfoProto { + string nodeId = 1; + int64 sessionIdMSB = 2; + int64 sessionIdLSB = 3; + int64 tenantIdMSB = 4; + int64 tenantIdLSB = 5; + int64 deviceIdMSB = 6; + int64 deviceIdLSB = 7; + string deviceName = 8; + string deviceType = 9; + int64 gwSessionIdMSB = 10; + int64 gwSessionIdLSB = 11; + int64 deviceProfileIdMSB = 12; + int64 deviceProfileIdLSB = 13; + int64 customerIdMSB = 14; + int64 customerIdLSB = 15; +} + +enum SessionEvent { + OPEN = 0; + CLOSED = 1; +} + +enum SessionType { + SYNC = 0; + ASYNC = 1; +} + +enum KeyValueType { + BOOLEAN_V = 0; + LONG_V = 1; + DOUBLE_V = 2; + STRING_V = 3; + JSON_V = 4; +} + +enum CredentialsType { + ACCESS_TOKEN = 0; + X509_CERTIFICATE = 1; + MQTT_BASIC = 2; + LWM2M_CREDENTIALS = 3; +} + +message KeyValueProto { + string key = 1; + KeyValueType type = 2; + bool bool_v = 3; + int64 long_v = 4; + double double_v = 5; + string string_v = 6; + string json_v = 7; +} + +enum AttributeScopeProto { + CLIENT_SCOPE = 0; + SERVER_SCOPE = 1; + SHARED_SCOPE = 2; +} + +message AttributeKey { + AttributeScopeProto scope = 1; + string attributeKey = 2; +} + +message AttributeValueProto { + int64 lastUpdateTs = 1; + KeyValueType type = 2; + bool has_v = 3; + bool bool_v = 4; + int64 long_v = 5; + double double_v = 6; + string string_v = 7; + string json_v = 8; + optional string key = 9; +} + +message TsKvProto { + int64 ts = 1; + KeyValueProto kv = 2; +} + +message TsKvListProto { + int64 ts = 1; + repeated KeyValueProto kv = 2; +} + +message DeviceInfoProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 deviceIdMSB = 3; + int64 deviceIdLSB = 4; + string deviceName = 5; + string deviceType = 6; + string additionalInfo = 7; + int64 deviceProfileIdMSB = 8; + int64 deviceProfileIdLSB = 9; + int64 customerIdMSB = 10; + int64 customerIdLSB = 11; + string powerMode = 12; + int64 edrxCycle = 13; + int64 psmActivityTimer = 14; + int64 pagingTransmissionWindow = 15; +} + +/** + * Transport Service Messages; + */ +message SessionEventMsg { + SessionType sessionType = 1; + SessionEvent event = 2; +} + +message PostTelemetryMsg { + repeated TsKvListProto tsKvList = 1; +} + +message PostAttributeMsg { + repeated KeyValueProto kv = 1; + bool shared = 2; +} + +message GetAttributeRequestMsg { + int32 requestId = 1; + repeated string clientAttributeNames = 2; + repeated string sharedAttributeNames = 3; + bool onlyShared = 4; +} + +message GetAttributeResponseMsg { + int32 requestId = 1; + repeated TsKvProto clientAttributeList = 2; + repeated TsKvProto sharedAttributeList = 3; + bool isMultipleAttributesRequest = 4; + string error = 5; + bool sharedStateMsg = 6; +} + +message AttributeUpdateNotificationMsg { + repeated TsKvProto sharedUpdated = 1; + repeated string sharedDeleted = 2; +} + +message ValidateDeviceTokenRequestMsg { + string token = 1; +} + +message ValidateDeviceX509CertRequestMsg { + string hash = 1; +} + +message ValidateOrCreateDeviceX509CertRequestMsg { + string certificateChain = 1; +} + +message ValidateBasicMqttCredRequestMsg { + string clientId = 1; + string userName = 2; + string password = 3; +} + +message ValidateDeviceCredentialsResponseMsg { + DeviceInfoProto deviceInfo = 1; + string credentialsBody = 2; + bytes profileBody = 3; +} + +message GetOrCreateDeviceFromGatewayRequestMsg { + int64 gatewayIdMSB = 1; + int64 gatewayIdLSB = 2; + string deviceName = 3; + string deviceType = 4; +} + +message GetOrCreateDeviceFromGatewayResponseMsg { + DeviceInfoProto deviceInfo = 1; + bytes profileBody = 2; + TransportApiRequestErrorCode error = 3; +} + +enum TransportApiRequestErrorCode { + UNKNOWN_TRANSPORT_API_ERROR = 0; + ENTITY_LIMIT = 1; +} + +message GetEntityProfileRequestMsg { + string entityType = 1; + int64 entityIdMSB = 2; + int64 entityIdLSB = 3; +} + +message GetAllQueueRoutingInfoRequestMsg { +} + +message GetQueueRoutingInfoResponseMsg { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 queueIdMSB = 3; + int64 queueIdLSB = 4; + string queueName = 5; + string queueTopic = 6; + int32 partitions = 7; +} + +message QueueUpdateMsg { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 queueIdMSB = 3; + int64 queueIdLSB = 4; + string queueName = 5; + string queueTopic = 6; + int32 partitions = 7; +} + +message QueueDeleteMsg { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 queueIdMSB = 3; + int64 queueIdLSB = 4; + string queueName = 5; +} + +message CoreStartupMsg { + string serviceId = 1; + repeated int32 partitions = 2; + int64 ts = 3; +} + +message ResourceCacheInvalidateMsg { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + repeated ImageCacheKeyProto keys = 3; +} + +message ImageCacheKeyProto { + optional string resourceKey = 1; + optional string publicResourceKey = 2; +} + +message LwM2MRegistrationRequestMsg { + string tenantId = 1; + string endpoint = 2; +} + +message LwM2MRegistrationResponseMsg { + DeviceInfoProto deviceInfo = 1; +} + +message LwM2MRequestMsg { + LwM2MRegistrationRequestMsg registrationMsg = 1; +} + +message LwM2MResponseMsg { + LwM2MRegistrationResponseMsg registrationMsg = 1; +} + +message GetResourceRequestMsg { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + string resourceType = 3; + string resourceKey = 4; +} + +message GetResourceResponseMsg { + bytes resource = 1; +} + +message ValidateDeviceLwM2MCredentialsRequestMsg { + string credentialsId = 1; +} + +message ToTransportUpdateCredentialsProto { + repeated string credentialsId = 1; + repeated string credentialsValue = 2; +} + +message GetTenantRoutingInfoRequestMsg { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; +} + +message GetTenantRoutingInfoResponseMsg { + bool isolatedTbRuleEngine = 2; +} + +message GetDeviceProfileRequestMsg { + int64 profileIdMSB = 1; + int64 profileIdLSB = 2; +} + +message GetEntityProfileResponseMsg { + string entityType = 1; + bytes data = 2; + bytes apiState = 3; +} + +message GetDeviceRequestMsg { + int64 deviceIdMSB = 1; + int64 deviceIdLSB = 2; +} + +message GetDeviceResponseMsg { + int64 deviceProfileIdMSB = 1; + int64 deviceProfileIdLSB = 2; + bytes deviceTransportConfiguration = 3; +} + +message GetDeviceCredentialsRequestMsg { + int64 deviceIdMSB = 1; + int64 deviceIdLSB = 2; +} + +message GetDeviceCredentialsResponseMsg { + bytes deviceCredentialsData = 1; +} + +message GetSnmpDevicesRequestMsg { + int32 page = 1; + int32 pageSize = 2; +} + +message GetSnmpDevicesResponseMsg { + repeated string ids = 1; + bool hasNextPage = 2; +} + +message EntityUpdateMsg { + string entityType = 1; + bytes data = 2; +} + +message EntityDeleteMsg { + string entityType = 1; + int64 entityIdMSB = 2; + int64 entityIdLSB = 3; +} + +message ResourceUpdateMsg { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + string resourceType = 3; + string resourceKey = 4; +} + +message ResourceDeleteMsg { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + string resourceType = 3; + string resourceKey = 4; +} + +message SessionCloseNotificationProto { + string message = 1; +} + +message SubscribeToAttributeUpdatesMsg { + bool unsubscribe = 1; + SessionType sessionType = 2; +} + +message SubscribeToRPCMsg { + bool unsubscribe = 1; + SessionType sessionType = 2; +} + +message SendPendingRPCMsg { +} + +message ToDeviceRpcRequestMsg { + int32 requestId = 1; + string methodName = 2; + string params = 3; + int64 expirationTime = 4; + int64 requestIdMSB = 5; + int64 requestIdLSB = 6; + bool oneway = 7; + bool persisted = 8; +} + +message ToDeviceRpcResponseMsg { + int32 requestId = 1; + string payload = 2; + string error = 3; +} + +message UplinkNotificationMsg { + int64 uplinkTs = 1; +} + +message ToDeviceRpcResponseStatusMsg { + int32 requestId = 1; + int64 requestIdMSB = 2; + int64 requestIdLSB = 3; + string status = 4; +} + +message ToServerRpcRequestMsg { + int32 requestId = 1; + string methodName = 2; + string params = 3; +} + +message ToServerRpcResponseMsg { + int32 requestId = 1; + string payload = 2; + string error = 3; +} + +message ClaimDeviceMsg { + int64 deviceIdMSB = 1; + int64 deviceIdLSB = 2; + string secretKey = 3; + int64 durationMs = 4; +} + +message DeviceCredentialsProto { + int64 deviceIdMSB = 1; + int64 deviceIdLSB = 2; + CredentialsType credentialsType = 3; + string credentialsId = 4; + optional string credentialsValue = 5; +} + +message CredentialsDataProto { + ValidateDeviceTokenRequestMsg validateDeviceTokenRequestMsg = 1; + ValidateDeviceX509CertRequestMsg validateDeviceX509CertRequestMsg = 2; + ValidateBasicMqttCredRequestMsg validateBasicMqttCredRequestMsg = 3; +} + +message ProvisionDeviceRequestMsg { + string deviceName = 1; + CredentialsType credentialsType = 2; + ProvisionDeviceCredentialsMsg provisionDeviceCredentialsMsg = 3; + CredentialsDataProto credentialsDataProto = 4; +} + +message ProvisionDeviceCredentialsMsg { + string provisionDeviceKey = 1; + string provisionDeviceSecret = 2; +} + +message ProvisionDeviceResponseMsg { + ResponseStatus status = 1; + CredentialsType credentialsType = 2; + string credentialsValue = 3; +} + +enum ResponseStatus { + UNKNOWN = 0; + SUCCESS = 1; + NOT_FOUND = 2; + FAILURE = 3; +} + +message GetOtaPackageRequestMsg { + int64 deviceIdMSB = 1; + int64 deviceIdLSB = 2; + int64 tenantIdMSB = 3; + int64 tenantIdLSB = 4; + string type = 5; +} + +message GetOtaPackageResponseMsg { + ResponseStatus responseStatus = 1; + int64 otaPackageIdMSB = 2; + int64 otaPackageIdLSB = 3; + string type = 4; + string title = 5; + string version = 6; + string contentType = 7; + string fileName = 8; +} + +message DeviceActivityProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 deviceIdMSB = 3; + int64 deviceIdLSB = 4; + int64 lastActivityTime = 5; +} + +//Used to report session state to tb-Service and persist this state in the cache on the tb-Service level. +message SubscriptionInfoProto { + int64 lastActivityTime = 1; + bool attributeSubscription = 2; + bool rpcSubscription = 3; +} + +message SessionSubscriptionInfoProto { + SessionInfoProto sessionInfo = 1; + SubscriptionInfoProto subscriptionInfo = 2; +} + +message DeviceSessionsCacheEntry { + repeated SessionSubscriptionInfoProto sessions = 1; +} + +message TransportToDeviceActorMsg { + SessionInfoProto sessionInfo = 1; + SessionEventMsg sessionEvent = 2; + GetAttributeRequestMsg getAttributes = 3; + SubscribeToAttributeUpdatesMsg subscribeToAttributes = 4; + SubscribeToRPCMsg subscribeToRPC = 5; + ToDeviceRpcResponseMsg toDeviceRPCCallResponse = 6; + SubscriptionInfoProto subscriptionInfo = 7; + ClaimDeviceMsg claimDevice = 8; + ProvisionDeviceRequestMsg provisionDevice = 9; + ToDeviceRpcResponseStatusMsg rpcResponseStatusMsg = 10; + SendPendingRPCMsg sendPendingRPC = 11; + UplinkNotificationMsg uplinkNotificationMsg = 12; +} + +message TransportToRuleEngineMsg { + SessionInfoProto sessionInfo = 1; + PostTelemetryMsg postTelemetry = 2; + PostAttributeMsg postAttributes = 3; + ToDeviceRpcResponseMsg toDeviceRPCCallResponse = 4; + ToServerRpcRequestMsg toServerRPCCallRequest = 5; +} + +/** + * TB Core Data Structures + */ + +message TbEntitySubEventProto { + string serviceId = 1; + int32 seqNumber = 2; + int64 tenantIdMSB = 3; + int64 tenantIdLSB = 4; + string entityType = 5; + int64 entityIdMSB = 6; + int64 entityIdLSB = 7; + string type = 8; + bool notifications = 9; + bool alarms = 10; + bool tsAllKeys = 11; + repeated string tsKeys = 12; + bool attrAllKeys = 13; + repeated string attrKeys = 14; +} + +message TbEntitySubEventCallbackProto { + int64 entityIdMSB = 1; + int64 entityIdLSB = 2; + int32 seqNumber = 3; + int64 attributesUpdateTs = 4; + int64 timeSeriesUpdateTs = 5; +} + +message TsValueProto { + int64 ts = 1; + KeyValueType type = 2; + bool bool_v = 3; + int64 long_v = 4; + double double_v = 5; + string string_v = 6; + string json_v = 7; +} + +message TsValueListProto { + string key = 1; + repeated TsValueProto tsValue = 2; +} + +message TbSubUpdateProto { + int64 entityIdMSB = 1; + int64 entityIdLSB = 2; + int32 errorCode = 3; + string errorMsg = 4; + repeated TsValueListProto data = 5; + string scope = 6; +} + +message TbAlarmSubUpdateProto { + int64 entityIdMSB = 1; + int64 entityIdLSB = 2; + int32 errorCode = 3; + string errorMsg = 4; + string alarm = 5; + bool deleted = 6; +} + +message NotificationsSubUpdateProto { + int64 entityIdMSB = 1; + int64 entityIdLSB = 2; + string notificationUpdate = 3; + string notificationRequestUpdate = 4; +} + +// DEPRECATED. FOR REMOVAL +message TbSubscriptionProto { + option deprecated = true; + string serviceId = 1; + string sessionId = 2; + int32 subscriptionId = 3; + string entityType = 4; + int64 tenantIdMSB = 5; + int64 tenantIdLSB = 6; + int64 entityIdMSB = 7; + int64 entityIdLSB = 8; +} + +// DEPRECATED. FOR REMOVAL +message TbTimeSeriesSubscriptionProto { + option deprecated = true; + TbSubscriptionProto sub = 1; + bool allKeys = 2; + repeated TbSubscriptionKetStateProto keyStates = 3; + int64 startTime = 4; + int64 endTime = 5; + bool latestValues = 6; +} + +// DEPRECATED. FOR REMOVAL +message TbAttributeSubscriptionProto { + option deprecated = true; + TbSubscriptionProto sub = 1; + bool allKeys = 2; + repeated TbSubscriptionKetStateProto keyStates = 3; + string scope = 4; +} + +// DEPRECATED. FOR REMOVAL +message TbAlarmSubscriptionProto { + option deprecated = true; + TbSubscriptionProto sub = 1; + int64 ts = 2; +} + +// DEPRECATED. FOR REMOVAL +message NotificationsSubscriptionProto { + option deprecated = true; + TbSubscriptionProto sub = 1; + int32 limit = 2; +} + +// DEPRECATED. FOR REMOVAL +message NotificationsCountSubscriptionProto { + option deprecated = true; + TbSubscriptionProto sub = 1; +} + +// DEPRECATED. FOR REMOVAL +message TbSubscriptionUpdateProto { + option deprecated = true; + string sessionId = 1; + int32 subscriptionId = 2; + int32 errorCode = 3; + string errorMsg = 4; + repeated TbSubscriptionUpdateValueListProto data = 5; +} + +// DEPRECATED. FOR REMOVAL +message TbAlarmSubscriptionUpdateProto { + option deprecated = true; + string sessionId = 1; + int32 subscriptionId = 2; + int32 errorCode = 3; + string errorMsg = 4; + string alarm = 5; + bool deleted = 6; +} + +// DEPRECATED. FOR REMOVAL +message NotificationsSubscriptionUpdateProto { + option deprecated = true; + string sessionId = 1; + int32 subscriptionId = 2; + string notificationUpdate = 3; + string notificationRequestUpdate = 4; +} + +message NotificationUpdateProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 recipientIdMSB = 3; + int64 recipientIdLSB = 4; + string update = 5; +} + +message NotificationRequestUpdateProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + string update = 6; +} + +message TbAttributeUpdateProto { + string entityType = 1; + int64 entityIdMSB = 2; + int64 entityIdLSB = 3; + int64 tenantIdMSB = 4; + int64 tenantIdLSB = 5; + string scope = 6; + repeated TsKvProto data = 7; +} + +message TbAlarmUpdateProto { + string entityType = 1; + int64 entityIdMSB = 2; + int64 entityIdLSB = 3; + int64 tenantIdMSB = 4; + int64 tenantIdLSB = 5; + string alarm = 6; +} + +message TbAlarmDeleteProto { + string entityType = 1; + int64 entityIdMSB = 2; + int64 entityIdLSB = 3; + int64 tenantIdMSB = 4; + int64 tenantIdLSB = 5; + string alarm = 6; +} + +message TbAttributeDeleteProto { + string entityType = 1; + int64 entityIdMSB = 2; + int64 entityIdLSB = 3; + int64 tenantIdMSB = 4; + int64 tenantIdLSB = 5; + string scope = 6; + repeated string keys = 7; + bool notifyDevice = 8; +} + +message TbTimeSeriesDeleteProto { + string entityType = 1; + int64 entityIdMSB = 2; + int64 entityIdLSB = 3; + int64 tenantIdMSB = 4; + int64 tenantIdLSB = 5; + repeated string keys = 6; +} + +message TbTimeSeriesUpdateProto { + string entityType = 1; + int64 entityIdMSB = 2; + int64 entityIdLSB = 3; + int64 tenantIdMSB = 4; + int64 tenantIdLSB = 5; + repeated TsKvProto data = 6; +} + +// DEPRECATED. FOR REMOVAL +message TbSubscriptionCloseProto { + option deprecated = true; + string sessionId = 1; + int32 subscriptionId = 2; +} + +message TbSubscriptionKetStateProto { + string key = 1; + int64 ts = 2; +} + +message TbSubscriptionUpdateValueListProto { + string key = 1; + repeated TbSubscriptionUpdateTsValue tsValue = 2; +} + +message TbSubscriptionUpdateTsValue { + int64 ts = 1; + optional string value = 2; +} + +/** + * TB Core to TB Core messages + */ + +message DeviceStateServiceMsgProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 deviceIdMSB = 3; + int64 deviceIdLSB = 4; + bool added = 5; + bool updated = 6; + bool deleted = 7; +} + +message SubscriptionMgrMsgProto { + TbTimeSeriesSubscriptionProto telemetrySub = 1 [deprecated = true]; // DEPRECATED. FOR REMOVAL + TbAttributeSubscriptionProto attributeSub = 2 [deprecated = true]; // DEPRECATED. FOR REMOVAL + TbSubscriptionCloseProto subClose = 3 [deprecated = true]; // DEPRECATED. FOR REMOVAL + TbTimeSeriesUpdateProto tsUpdate = 4; + TbAttributeUpdateProto attrUpdate = 5; + TbAttributeDeleteProto attrDelete = 6; + TbAlarmSubscriptionProto alarmSub = 7 [deprecated = true]; // DEPRECATED. FOR REMOVAL + TbAlarmUpdateProto alarmUpdate = 8; + TbAlarmDeleteProto alarmDelete = 9; + TbTimeSeriesDeleteProto tsDelete = 10; + NotificationsSubscriptionProto notificationsSub = 11 [deprecated = true]; // DEPRECATED. FOR REMOVAL + NotificationsCountSubscriptionProto notificationsCountSub = 12 [deprecated = true]; // DEPRECATED. FOR REMOVAL + NotificationUpdateProto notificationUpdate = 13; + NotificationRequestUpdateProto notificationRequestUpdate = 14; + TbEntitySubEventProto subEvent = 15; +} + +message LocalSubscriptionServiceMsgProto { + TbSubscriptionUpdateProto subUpdate = 1 [deprecated = true]; // DEPRECATED. FOR REMOVAL + TbAlarmSubscriptionUpdateProto alarmSubUpdate = 2 [deprecated = true]; // DEPRECATED. FOR REMOVAL + NotificationsSubscriptionUpdateProto notificationsSubUpdate = 3 [deprecated = true]; // DEPRECATED. FOR REMOVAL + TbEntitySubEventCallbackProto subEventCallback = 4; + TbSubUpdateProto tsUpdate = 5; + TbSubUpdateProto attrUpdate = 6; + TbAlarmSubUpdateProto alarmUpdate = 7; + NotificationsSubUpdateProto notificationsUpdate = 8; +} + +message FromDeviceRPCResponseProto { + int64 requestIdMSB = 1; + int64 requestIdLSB = 2; + string response = 3; + int32 error = 4; +} + +enum ComponentLifecycleEvent { + CREATED = 0; + STARTED = 1; + ACTIVATED = 2; + SUSPENDED = 3; + UPDATED = 4; + STOPPED = 5; + DELETED = 6; + FAILED = 7; + DEACTIVATED = 8; +} + +message ComponentLifecycleMsgProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + EntityTypeProto entityType = 3; + int64 entityIdMSB = 4; + int64 entityIdLSB = 5; + ComponentLifecycleEvent event = 6; +} + +message EdgeNotificationMsgProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 edgeIdMSB = 3; + int64 edgeIdLSB = 4; + string type = 5; + string action = 6; + int64 entityIdMSB = 7; + int64 entityIdLSB = 8; + string entityType = 9; + string body = 10; + PostTelemetryMsg postTelemetryMsg = 11; + PostAttributeMsg postAttributesMsg = 12; + int64 originatorEdgeIdMSB = 13; + int64 originatorEdgeIdLSB = 14; +} + +message EdgeEventUpdateMsgProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 edgeIdMSB = 3; + int64 edgeIdLSB = 4; +} + +message ToEdgeSyncRequestMsgProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 requestIdMSB = 3; + int64 requestIdLSB = 4; + int64 edgeIdMSB = 5; + int64 edgeIdLSB = 6; +} + +message FromEdgeSyncResponseMsgProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 responseIdMSB = 3; + int64 responseIdLSB = 4; + int64 edgeIdMSB = 5; + int64 edgeIdLSB = 6; + bool success = 7; +} + +message DeviceEdgeUpdateMsgProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 deviceIdMSB = 3; + int64 deviceIdLSB = 4; + optional int64 edgeIdMSB = 5; + optional int64 edgeIdLSB = 6; +} + +message DeviceNameOrTypeUpdateMsgProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 deviceIdMSB = 3; + int64 deviceIdLSB = 4; + string deviceName = 5; + string deviceType = 6; +} + +message DeviceAttributesEventMsgProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 deviceIdMSB = 3; + int64 deviceIdLSB = 4; + repeated AttributeKey deletedKeys = 5; + optional AttributeScopeProto scope = 6; + repeated AttributeValueProto values = 7; + bool deleted = 8; +} + +message DeviceCredentialsUpdateMsgProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 deviceIdMSB = 3; + int64 deviceIdLSB = 4; + DeviceCredentialsProto deviceCredentials = 5; +} + +message ToDeviceRpcRequestActorMsgProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 deviceIdMSB = 3; + int64 deviceIdLSB = 4; + string serviceId = 5; + ToDeviceRpcRequestMsg toDeviceRpcRequestMsg = 6; +} + +message FromDeviceRpcResponseActorMsgProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 deviceIdMSB = 3; + int64 deviceIdLSB = 4; + int32 requestId = 5; + FromDeviceRPCResponseProto rpcResponse = 6; +} + +message RemoveRpcActorMsgProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 requestIdMSB = 3; + int64 requestIdLSB = 4; + int64 deviceIdMSB = 5; + int64 deviceIdLSB = 6; +} + +message DeviceDeleteMsgProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 deviceIdMSB = 3; + int64 deviceIdLSB = 4; +} + +message ToDeviceActorNotificationMsgProto { + DeviceEdgeUpdateMsgProto deviceEdgeUpdateMsg = 1; + DeviceNameOrTypeUpdateMsgProto deviceNameOrTypeMsg = 2; + DeviceAttributesEventMsgProto deviceAttributesEventMsg = 3; + DeviceCredentialsUpdateMsgProto deviceCredentialsUpdateMsg = 4; + ToDeviceRpcRequestActorMsgProto toDeviceRpcRequestMsg = 5; + FromDeviceRpcResponseActorMsgProto fromDeviceRpcResponseMsg = 6; + RemoveRpcActorMsgProto removeRpcActorMsg = 7; + DeviceDeleteMsgProto deviceDeleteMsg = 8; +} + +/** + TB Core to Version Control Service + */ +message CommitRequestMsg { + string txId = 1; // To correlate prepare, add, delete and push messages + PrepareMsg prepareMsg = 2; + AddMsg addMsg = 3; + DeleteMsg deleteMsg = 4; + PushMsg pushMsg = 5; + AbortMsg abortMsg = 6; +} + +message CommitResponseMsg { + int64 ts = 1; + string commitId = 2; + string name = 3; + string author = 4; + int32 added = 5; + int32 modified = 6; + int32 removed = 7; +} + +message PrepareMsg { + string commitMsg = 1; + string branchName = 2; + string authorName = 3; + string authorEmail = 4; +} + +message AddMsg { + string relativePath = 1; + string entityDataJsonChunk = 2; + string chunkedMsgId = 3; + int32 chunkIndex = 4; + int32 chunksCount = 5; +} + +message DeleteMsg { + string relativePath = 1; +} + +message PushMsg { +} + +message AbortMsg { +} + +message ListVersionsRequestMsg { + string branchName = 1; + string entityType = 2; + int64 entityIdMSB = 3; + int64 entityIdLSB = 4; + int32 pageSize = 5; + int32 page = 6; + string textSearch = 7; + string sortProperty = 8; + string sortDirection = 9; +} + +message EntityVersionProto { + int64 ts = 1; + string id = 2; + string name = 3; + string author = 4; +} + +message ListVersionsResponseMsg { + repeated EntityVersionProto versions = 1; + int32 totalPages = 2; + int64 totalElements = 3; + bool hasNext = 4; +} + +message ListEntitiesRequestMsg { + string versionId = 1; + string entityType = 2; +} + +message VersionedEntityInfoProto { + string entityType = 1; + int64 entityIdMSB = 2; + int64 entityIdLSB = 3; +} + +message ListEntitiesResponseMsg { + repeated VersionedEntityInfoProto entities = 1; +} + +message ListBranchesRequestMsg { +} + +message BranchInfoProto { + string name = 1; + bool isDefault = 2; +} + +message ListBranchesResponseMsg { + repeated BranchInfoProto branches = 1; +} + +message EntityContentRequestMsg { + string versionId = 1; + string entityType = 2; + int64 entityIdMSB = 3; + int64 entityIdLSB = 4; +} + +message EntityContentResponseMsg { + string data = 1; + int32 chunkIndex = 2; + int32 chunksCount = 3; +} + +message EntitiesContentRequestMsg { + string versionId = 1; + string entityType = 2; + int32 offset = 3; + int32 limit = 4; +} + +message EntitiesContentResponseMsg { + EntityContentResponseMsg item = 1; + int32 itemIdx = 2; + int32 itemsCount = 3; +} + +message VersionsDiffRequestMsg { + string path = 1; + string versionId1 = 2; + string versionId2 = 3; +} + +message VersionsDiffResponseMsg { + repeated EntityVersionsDiff diff = 1; +} + +message EntityVersionsDiff { + string entityType = 1; + int64 entityIdMSB = 2; + int64 entityIdLSB = 3; + string entityDataAtVersion1 = 4; + string entityDataAtVersion2 = 5; + string rawDiff = 6; +} + +message GenericRepositoryRequestMsg {} + +message GenericRepositoryResponseMsg {} + +message ToVersionControlServiceMsg { + string nodeId = 1; + int64 tenantIdMSB = 2; + int64 tenantIdLSB = 3; + int64 requestIdMSB = 4; + int64 requestIdLSB = 5; + bytes vcSettings = 6; + GenericRepositoryRequestMsg initRepositoryRequest = 7; + GenericRepositoryRequestMsg testRepositoryRequest = 8; + GenericRepositoryRequestMsg clearRepositoryRequest = 9; + CommitRequestMsg commitRequest = 10; + ListVersionsRequestMsg listVersionRequest = 11; + ListEntitiesRequestMsg listEntitiesRequest = 12; + ListBranchesRequestMsg listBranchesRequest = 13; + EntityContentRequestMsg entityContentRequest = 14; + EntitiesContentRequestMsg entitiesContentRequest = 15; + VersionsDiffRequestMsg versionsDiffRequest = 16; +} + +message VersionControlResponseMsg { + int64 requestIdMSB = 1; + int64 requestIdLSB = 2; + string error = 3; + GenericRepositoryResponseMsg genericResponse = 4; + CommitResponseMsg commitResponse = 5; + ListBranchesResponseMsg listBranchesResponse = 6; + ListEntitiesResponseMsg listEntitiesResponse = 7; + ListVersionsResponseMsg listVersionsResponse = 8; + EntityContentResponseMsg entityContentResponse = 9; + EntitiesContentResponseMsg entitiesContentResponse = 10; + VersionsDiffResponseMsg versionsDiffResponse = 11; +} + +/** + * Main messages; + */ + +/* Request from Transport Service to ThingsBoard Core Service */ +message TransportApiRequestMsg { + ValidateDeviceTokenRequestMsg validateTokenRequestMsg = 1; + ValidateDeviceX509CertRequestMsg validateX509CertRequestMsg = 2; + GetOrCreateDeviceFromGatewayRequestMsg getOrCreateDeviceRequestMsg = 3; + GetEntityProfileRequestMsg entityProfileRequestMsg = 4; + LwM2MRequestMsg lwM2MRequestMsg = 5; + ValidateBasicMqttCredRequestMsg validateBasicMqttCredRequestMsg = 6; + ProvisionDeviceRequestMsg provisionDeviceRequestMsg = 7; + ValidateDeviceLwM2MCredentialsRequestMsg validateDeviceLwM2MCredentialsRequestMsg = 8; + GetResourceRequestMsg resourceRequestMsg = 9; + GetOtaPackageRequestMsg otaPackageRequestMsg = 10; + GetSnmpDevicesRequestMsg snmpDevicesRequestMsg = 11; + GetDeviceRequestMsg deviceRequestMsg = 12; + GetDeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 13; + GetAllQueueRoutingInfoRequestMsg getAllQueueRoutingInfoRequestMsg = 14; + ValidateOrCreateDeviceX509CertRequestMsg validateOrCreateX509CertRequestMsg = 15; +} + +/* Response from ThingsBoard Core Service to Transport Service */ +message TransportApiResponseMsg { + ValidateDeviceCredentialsResponseMsg validateCredResponseMsg = 1; + GetOrCreateDeviceFromGatewayResponseMsg getOrCreateDeviceResponseMsg = 2; + GetEntityProfileResponseMsg entityProfileResponseMsg = 3; + ProvisionDeviceResponseMsg provisionDeviceResponseMsg = 4; + GetSnmpDevicesResponseMsg snmpDevicesResponseMsg = 5; + LwM2MResponseMsg lwM2MResponseMsg = 6; + GetResourceResponseMsg resourceResponseMsg = 7; + GetOtaPackageResponseMsg otaPackageResponseMsg = 8; + GetDeviceResponseMsg deviceResponseMsg = 9; + GetDeviceCredentialsResponseMsg deviceCredentialsResponseMsg = 10; + repeated GetQueueRoutingInfoResponseMsg getQueueRoutingInfoResponseMsgs = 11; +} + +/* Messages that are handled by ThingsBoard Core Service */ +message ToCoreMsg { + TransportToDeviceActorMsg toDeviceActorMsg = 1; + DeviceStateServiceMsgProto deviceStateServiceMsg = 2; + SubscriptionMgrMsgProto toSubscriptionMgrMsg = 3; + bytes toDeviceActorNotificationMsg = 4 [deprecated = true]; + EdgeNotificationMsgProto edgeNotificationMsg = 5; + DeviceActivityProto deviceActivityMsg = 6; + NotificationSchedulerServiceMsg notificationSchedulerServiceMsg = 7; + LifecycleEventProto lifecycleEventMsg = 8; + ErrorEventProto errorEventMsg = 9; + ToDeviceActorNotificationMsgProto toDeviceActorNotification = 10; +} + +/* High priority messages with low latency are handled by ThingsBoard Core Service separately */ +/* Please, adjust the TbCoreConsumerStats when modifying the ToCoreNotificationMsg */ +message ToCoreNotificationMsg { + LocalSubscriptionServiceMsgProto toLocalSubscriptionServiceMsg = 1; + FromDeviceRPCResponseProto fromDeviceRpcResponse = 2; + bytes componentLifecycleMsg = 3 [deprecated = true]; + bytes edgeEventUpdateMsg = 4 [deprecated = true]; + QueueUpdateMsg queueUpdateMsg = 5; + QueueDeleteMsg queueDeleteMsg = 6; + VersionControlResponseMsg vcResponseMsg = 7; + bytes toEdgeSyncRequestMsg = 8 [deprecated = true]; + bytes fromEdgeSyncResponseMsg = 9 [deprecated = true]; + SubscriptionMgrMsgProto toSubscriptionMgrMsg = 10; + NotificationRuleProcessorMsg notificationRuleProcessorMsg = 11; + ComponentLifecycleMsgProto componentLifecycle = 12; + CoreStartupMsg coreStartupMsg = 13; + EdgeEventUpdateMsgProto edgeEventUpdate = 14; + ToEdgeSyncRequestMsgProto toEdgeSyncRequest = 15; + FromEdgeSyncResponseMsgProto fromEdgeSyncResponse = 16; + ResourceCacheInvalidateMsg resourceCacheInvalidateMsg = 17; +} + +/* Messages that are handled by ThingsBoard RuleEngine Service */ +message ToRuleEngineMsg { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + bytes tbMsg = 3; + repeated string relationTypes = 4; + string failureMessage = 5; +} + +message ToRuleEngineNotificationMsg { + bytes componentLifecycleMsg = 1 [deprecated = true]; + FromDeviceRPCResponseProto fromDeviceRpcResponse = 2; + QueueUpdateMsg queueUpdateMsg = 3; + QueueDeleteMsg queueDeleteMsg = 4; + ComponentLifecycleMsgProto componentLifecycle = 5; +} + +/* Messages that are handled by ThingsBoard Transport Service */ +message ToTransportMsg { + int64 sessionIdMSB = 1; + int64 sessionIdLSB = 2; + SessionCloseNotificationProto sessionCloseNotification = 3; + GetAttributeResponseMsg getAttributesResponse = 4; + AttributeUpdateNotificationMsg attributeUpdateNotification = 5; + ToDeviceRpcRequestMsg toDeviceRequest = 6; + ToServerRpcResponseMsg toServerResponse = 7; + EntityUpdateMsg entityUpdateMsg = 8; + EntityDeleteMsg entityDeleteMsg = 9; + ProvisionDeviceResponseMsg provisionResponse = 10; + ToTransportUpdateCredentialsProto toTransportUpdateCredentialsNotification = 11; + ResourceUpdateMsg resourceUpdateMsg = 12; + ResourceDeleteMsg resourceDeleteMsg = 13; + UplinkNotificationMsg uplinkNotificationMsg = 14; + QueueUpdateMsg queueUpdateMsg = 15; + QueueDeleteMsg queueDeleteMsg = 16; +} + +message UsageStatsKVProto{ + string key = 1; + int64 value = 2; +} + +message ToUsageStatsServiceMsg { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 entityIdMSB = 3; + int64 entityIdLSB = 4; + repeated UsageStatsKVProto values = 5; + int64 customerIdMSB = 6; + int64 customerIdLSB = 7; + string serviceId = 8; +} + +message ToOtaPackageStateServiceMsg { + int64 ts = 1; + int64 tenantIdMSB = 2; + int64 tenantIdLSB = 3; + int64 deviceIdMSB = 4; + int64 deviceIdLSB = 5; + int64 otaPackageIdMSB = 6; + int64 otaPackageIdLSB = 7; + string type = 8; +} + +message NotificationSchedulerServiceMsg { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 requestIdMSB = 3; + int64 requestIdLSB = 4; + int64 ts = 5; +} + +message NotificationRuleProcessorMsg { + bytes trigger = 1; +} + +message ErrorEventProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 entityIdMSB = 3; + int64 entityIdLSB = 4; + string serviceId = 5; + string method = 6; + string error = 7; +} + +message LifecycleEventProto { + int64 tenantIdMSB = 1; + int64 tenantIdLSB = 2; + int64 entityIdMSB = 3; + int64 entityIdLSB = 4; + string serviceId = 5; + string lcEventType = 6; + bool success = 7; + string error = 8; +} diff --git a/common/transport/transport-api/src/main/proto/transport.proto b/common/proto/src/main/proto/transport.proto similarity index 100% rename from common/transport/transport-api/src/main/proto/transport.proto rename to common/proto/src/main/proto/transport.proto diff --git a/common/proto/src/test/java/org/thingsboard/server/common/adaptor/JsonConverterTest.java b/common/proto/src/test/java/org/thingsboard/server/common/adaptor/JsonConverterTest.java new file mode 100644 index 0000000000..8c32677afb --- /dev/null +++ b/common/proto/src/test/java/org/thingsboard/server/common/adaptor/JsonConverterTest.java @@ -0,0 +1,105 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.adaptor; + +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; +import org.junit.Assert; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; + +public class JsonConverterTest { + + private final JsonParser JSON_PARSER = new JsonParser(); + + @BeforeEach + public void before() { + JsonConverter.setTypeCastEnabled(true); + } + + @Test + public void testParseBigDecimalAsLong() { + var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 1E+1}"), 0L); + Assert.assertEquals(10L, result.get(0L).get(0).getLongValue().get().longValue()); + } + + @Test + public void testParseBigDecimalAsDouble() { + var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 101E-1}"), 0L); + Assert.assertEquals(10.1, result.get(0L).get(0).getDoubleValue().get(), 0.0); + } + + @Test + public void testParseAttributesBigDecimalAsLong() { + var result = new ArrayList<>(JsonConverter.convertToAttributes(JSON_PARSER.parse("{\"meterReadingDelta\": 1E1}"))); + Assert.assertEquals(10L, result.get(0).getLongValue().get().longValue()); + } + + @Test + public void testParseAsDoubleWithZero() { + var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 42.0}"), 0L); + Assert.assertEquals(42.0, result.get(0L).get(0).getDoubleValue().get(), 0.0); + } + + @Test + public void testParseAsDouble() { + var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 1.1}"), 0L); + Assert.assertEquals(1.1, result.get(0L).get(0).getDoubleValue().get(), 0.0); + } + + @Test + public void testParseAsLong() { + var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 11}"), 0L); + Assert.assertEquals(11L, result.get(0L).get(0).getLongValue().get().longValue()); + } + + @Test + public void testParseBigDecimalAsStringOutOfLongRange() { + var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 9.9701010061400066E19}"), 0L); + Assert.assertEquals("99701010061400066000", result.get(0L).get(0).getStrValue().get()); + } + + @Test + public void testParseBigDecimalAsStringOutOfLongRange2() { + var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 99701010061400066001}"), 0L); + Assert.assertEquals("99701010061400066001", result.get(0L).get(0).getStrValue().get()); + } + + @Test + public void testParseBigDecimalAsStringOutOfLongRange3() { + var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 1E19}"), 0L); + Assert.assertEquals("10000000000000000000", result.get(0L).get(0).getStrValue().get()); + } + + @Test + public void testParseBigDecimalOutOfLongRangeWithoutParsing() { + JsonConverter.setTypeCastEnabled(false); + Assertions.assertThrows(JsonSyntaxException.class, () -> { + JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 89701010051400054084}"), 0L); + }); + } + + @Test + public void testParseBigDecimalOutOfLongRangeWithoutParsing2() { + JsonConverter.setTypeCastEnabled(false); + Assertions.assertThrows(JsonSyntaxException.class, () -> { + JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 9.9701010061400066E19}"), 0L); + }); + } +} diff --git a/common/proto/src/test/java/org/thingsboard/server/common/util/ProtoUtilsTest.java b/common/proto/src/test/java/org/thingsboard/server/common/util/ProtoUtilsTest.java new file mode 100644 index 0000000000..a41b2be208 --- /dev/null +++ b/common/proto/src/test/java/org/thingsboard/server/common/util/ProtoUtilsTest.java @@ -0,0 +1,182 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.util; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.AttributeKey; +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.JsonDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; +import org.thingsboard.server.common.data.rpc.RpcError; +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.common.data.security.DeviceCredentialsType; +import org.thingsboard.server.common.msg.edge.EdgeEventUpdateMsg; +import org.thingsboard.server.common.msg.edge.FromEdgeSyncResponse; +import org.thingsboard.server.common.msg.edge.ToEdgeSyncRequest; +import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponseActorMsg; +import org.thingsboard.server.common.msg.rpc.RemoveRpcActorMsg; +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequestActorMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceAttributesEventNotificationMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceCredentialsUpdateNotificationMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceEdgeUpdateMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceNameOrTypeUpdateMsg; +import org.thingsboard.server.gen.transport.TransportProtos; + +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + +class ProtoUtilsTest { + + TenantId tenantId = TenantId.fromUUID(UUID.fromString("35e10f77-16e7-424d-ae46-ee780f87ac4f")); + EntityId entityId = new RuleChainId(UUID.fromString("c640b635-4f0f-41e6-b10b-25a86003094e")); + DeviceId deviceId = new DeviceId(UUID.fromString("ceebb9e5-4239-437c-a507-dc5f71f1232d")); + EdgeId edgeId = new EdgeId(UUID.fromString("364be452-2183-459b-af93-1ddb325feac1")); + UUID id = UUID.fromString("31a07d85-6ed5-46f8-83c0-6715cb0a8782"); + + @Test + void protoComponentLifecycleSerialization() { + ComponentLifecycleMsg msg = new ComponentLifecycleMsg(tenantId, entityId, ComponentLifecycleEvent.UPDATED); + assertThat(ProtoUtils.fromProto(ProtoUtils.toProto(msg))).as("deserialized").isEqualTo(msg); + msg = new ComponentLifecycleMsg(tenantId, entityId, ComponentLifecycleEvent.STARTED); + assertThat(ProtoUtils.fromProto(ProtoUtils.toProto(msg))).as("deserialized").isEqualTo(msg); + } + + @Test + void protoEntityTypeSerialization() { + for (EntityType entityType : EntityType.values()) { + assertThat(ProtoUtils.fromProto(ProtoUtils.toProto(entityType))).as(entityType.getNormalName()).isEqualTo(entityType); + } + } + + @Test + void protoEdgeEventUpdateSerialization() { + EdgeEventUpdateMsg msg = new EdgeEventUpdateMsg(tenantId, edgeId); + assertThat(ProtoUtils.fromProto(ProtoUtils.toProto(msg))).as("deserialized").isEqualTo(msg); + } + + @Test + void protoToEdgeSyncRequestSerialization() { + ToEdgeSyncRequest msg = new ToEdgeSyncRequest(id, tenantId, edgeId); + assertThat(ProtoUtils.fromProto(ProtoUtils.toProto(msg))).as("deserialized").isEqualTo(msg); + } + + @Test + void protoFromEdgeSyncResponseSerialization() { + FromEdgeSyncResponse msg = new FromEdgeSyncResponse(id, tenantId, edgeId, true); + assertThat(ProtoUtils.fromProto(ProtoUtils.toProto(msg))).as("deserialized").isEqualTo(msg); + } + + @Test + void protoDeviceEdgeUpdateSerialization() { + DeviceEdgeUpdateMsg msg = new DeviceEdgeUpdateMsg(tenantId, deviceId, edgeId); + TransportProtos.ToDeviceActorNotificationMsgProto serializedMsg = ProtoUtils.toProto(msg); + Assertions.assertNotNull(serializedMsg); + assertThat(ProtoUtils.fromProto(serializedMsg)).as("deserialized").isEqualTo(msg); + } + + @Test + void protoDeviceNameOrTypeSerialization() { + String deviceName = "test", deviceType = "test"; + DeviceNameOrTypeUpdateMsg msg = new DeviceNameOrTypeUpdateMsg(tenantId, deviceId, deviceName, deviceType); + TransportProtos.ToDeviceActorNotificationMsgProto serializedMsg = ProtoUtils.toProto(msg); + Assertions.assertNotNull(serializedMsg); + assertThat(ProtoUtils.fromProto(serializedMsg)).as("deserialized").isEqualTo(msg); + } + + @Test + void protoDeviceAttributesEventSerialization() { + DeviceAttributesEventNotificationMsg msg = new DeviceAttributesEventNotificationMsg(tenantId, deviceId, null, "CLIENT_SCOPE", + List.of(new BaseAttributeKvEntry(System.currentTimeMillis(), new StringDataEntry("key", "value"))), false); + TransportProtos.ToDeviceActorNotificationMsgProto serializedMsg = ProtoUtils.toProto(msg); + Assertions.assertNotNull(serializedMsg); + assertThat(ProtoUtils.fromProto(serializedMsg)).as("deserialized").isEqualTo(msg); + + msg = new DeviceAttributesEventNotificationMsg(tenantId, deviceId, null, "SERVER_SCOPE", + List.of(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("doubleEntry", 231.5)), + new BaseAttributeKvEntry(System.currentTimeMillis(), new JsonDataEntry("jsonEntry", "jsonValue"))), false); + serializedMsg = ProtoUtils.toProto(msg); + Assertions.assertNotNull(serializedMsg); + assertThat(ProtoUtils.fromProto(serializedMsg)).as("deserialized").isEqualTo(msg); + + msg = new DeviceAttributesEventNotificationMsg(tenantId, deviceId, null, "SERVER_SCOPE", + List.of(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("entry", 11.3)), + new BaseAttributeKvEntry(System.currentTimeMillis(), new BooleanDataEntry("jsonEntry", true))), false); + serializedMsg = ProtoUtils.toProto(msg); + Assertions.assertNotNull(serializedMsg); + assertThat(ProtoUtils.fromProto(serializedMsg)).as("deserialized").isEqualTo(msg); + + msg = new DeviceAttributesEventNotificationMsg(tenantId, deviceId, Set.of(new AttributeKey("SHARED_SCOPE", "attributeKey")), null, null, true); + serializedMsg = ProtoUtils.toProto(msg); + Assertions.assertNotNull(serializedMsg); + assertThat(ProtoUtils.fromProto(serializedMsg)).as("deserialized").isEqualTo(msg); + } + + @Test + void protoDeviceCredentialsUpdateSerialization() { + DeviceCredentials deviceCredentials = new DeviceCredentials(); + deviceCredentials.setDeviceId(deviceId); + deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); + deviceCredentials.setCredentialsValue("test"); + deviceCredentials.setCredentialsId("test"); + DeviceCredentialsUpdateNotificationMsg msg = new DeviceCredentialsUpdateNotificationMsg(tenantId, deviceId, deviceCredentials); + TransportProtos.ToDeviceActorNotificationMsgProto serializedMsg = ProtoUtils.toProto(msg); + Assertions.assertNotNull(serializedMsg); + assertThat(ProtoUtils.fromProto(serializedMsg)).as("deserialized").isEqualTo(msg); + } + + @Test + void protoToDeviceRpcRequestSerialization() { + String serviceId = "cadcaac6-85c3-4211-9756-f074dcd1e7f7"; + ToDeviceRpcRequest request = new ToDeviceRpcRequest(id, tenantId, deviceId, true, 0, new ToDeviceRpcRequestBody("method", "params"), false, 0, ""); + ToDeviceRpcRequestActorMsg msg = new ToDeviceRpcRequestActorMsg(serviceId, request); + TransportProtos.ToDeviceActorNotificationMsgProto serializedMsg = ProtoUtils.toProto(msg); + Assertions.assertNotNull(serializedMsg); + assertThat(ProtoUtils.fromProto(serializedMsg)).as("deserialized").isEqualTo(msg); + } + + @Test + void protoFromDeviceRpcResponseSerialization() { + FromDeviceRpcResponseActorMsg msg = new FromDeviceRpcResponseActorMsg(23, tenantId, deviceId, new FromDeviceRpcResponse(id, "response", RpcError.NOT_FOUND)); + TransportProtos.ToDeviceActorNotificationMsgProto serializedMsg = ProtoUtils.toProto(msg); + Assertions.assertNotNull(serializedMsg); + assertThat(ProtoUtils.fromProto(serializedMsg)).as("deserialized").isEqualTo(msg); + } + + @Test + void protoRemoveRpcActorSerialization() { + RemoveRpcActorMsg msg = new RemoveRpcActorMsg(tenantId, deviceId, id); + TransportProtos.ToDeviceActorNotificationMsgProto serializedMsg = ProtoUtils.toProto(msg); + Assertions.assertNotNull(serializedMsg); + assertThat(ProtoUtils.fromProto(serializedMsg)).as("deserialized").isEqualTo(msg); + } +} diff --git a/common/queue/pom.xml b/common/queue/pom.xml index a995d9b4a3..32d740812d 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/RuleEngineTbQueueAdminFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/RuleEngineTbQueueAdminFactory.java index fe29c3a04f..7a8764325f 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/RuleEngineTbQueueAdminFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/RuleEngineTbQueueAdminFactory.java @@ -99,7 +99,7 @@ public class RuleEngineTbQueueAdminFactory { return new TbQueueAdmin() { @Override - public void createTopicIfNotExists(String topic) { + public void createTopicIfNotExists(String topic, String properties) { } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusAdmin.java b/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusAdmin.java index e171bb7a31..d95d2064a4 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusAdmin.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusAdmin.java @@ -22,6 +22,7 @@ import com.microsoft.azure.servicebus.primitives.MessagingEntityAlreadyExistsExc import com.microsoft.azure.servicebus.primitives.ServiceBusException; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.queue.TbQueueAdmin; +import org.thingsboard.server.queue.util.PropertyUtils; import java.io.IOException; import java.time.Duration; @@ -60,7 +61,7 @@ public class TbServiceBusAdmin implements TbQueueAdmin { } @Override - public void createTopicIfNotExists(String topic) { + public void createTopicIfNotExists(String topic, String properties) { if (queues.contains(topic)) { return; } @@ -68,7 +69,7 @@ public class TbServiceBusAdmin implements TbQueueAdmin { try { QueueDescription queueDescription = new QueueDescription(topic); queueDescription.setRequiresDuplicateDetection(false); - setQueueConfigs(queueDescription); + setQueueConfigs(queueDescription, PropertyUtils.getProps(queueConfigs, properties)); client.createQueue(queueDescription); queues.add(topic); @@ -107,7 +108,7 @@ public class TbServiceBusAdmin implements TbQueueAdmin { } } - private void setQueueConfigs(QueueDescription queueDescription) { + private void setQueueConfigs(QueueDescription queueDescription, Map queueConfigs) { queueConfigs.forEach((confKey, confValue) -> { switch (confKey) { case MAX_SIZE: diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/AbstractTbQueueConsumerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/AbstractTbQueueConsumerTemplate.java index 86146dabd1..073371ebb5 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/AbstractTbQueueConsumerTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/AbstractTbQueueConsumerTemplate.java @@ -54,7 +54,7 @@ public abstract class AbstractTbQueueConsumerTemplate i @Override public void subscribe() { - log.info("enqueue topic subscribe {} ", topic); + log.debug("enqueue topic subscribe {} ", topic); if (stopped) { log.error("trying subscribe, but consumer stopped for topic {}", topic); return; @@ -64,7 +64,7 @@ public abstract class AbstractTbQueueConsumerTemplate i @Override public void subscribe(Set partitions) { - log.info("enqueue topics subscribe {} ", partitions); + log.debug("enqueue topics subscribe {} ", partitions); if (stopped) { log.error("trying subscribe, but consumer stopped for topic {}", topic); return; @@ -77,7 +77,8 @@ public abstract class AbstractTbQueueConsumerTemplate i List records; long startNanos = System.nanoTime(); if (stopped) { - return errorAndReturnEmpty(); + log.error("poll invoked but consumer stopped for topic " + topic, new RuntimeException("stacktrace")); + return emptyList(); } if (!subscribed && partitions == null && subscribeQueue.isEmpty()) { return sleepAndReturnEmpty(startNanos, durationInMillis); @@ -94,7 +95,8 @@ public abstract class AbstractTbQueueConsumerTemplate i partitions = subscribeQueue.poll(); } if (!subscribed) { - List topicNames = partitions.stream().map(TopicPartitionInfo::getFullTopicName).collect(Collectors.toList()); + List topicNames = getFullTopicNames(); + log.info("Subscribing to topics {}", topicNames); doSubscribe(topicNames); subscribed = true; } @@ -103,7 +105,9 @@ public abstract class AbstractTbQueueConsumerTemplate i consumerLock.unlock(); } - if (records.isEmpty()) { return sleepAndReturnEmpty(startNanos, durationInMillis); } + if (records.isEmpty() && !isLongPollingSupported()) { + return sleepAndReturnEmpty(startNanos, durationInMillis); + } return decodeRecords(records); } @@ -124,11 +128,6 @@ public abstract class AbstractTbQueueConsumerTemplate i return result; } - List errorAndReturnEmpty() { - log.error("poll invoked but consumer stopped for topic" + topic, new RuntimeException("stacktrace")); - return emptyList(); - } - List sleepAndReturnEmpty(final long startNanos, final long durationInMillis) { long durationNanos = TimeUnit.MILLISECONDS.toNanos(durationInMillis); long spentNanos = System.nanoTime() - startNanos; @@ -160,13 +159,20 @@ public abstract class AbstractTbQueueConsumerTemplate i } } + @Override + public void stop() { + stopped = true; + } + @Override public void unsubscribe() { - log.info("unsubscribe topic and stop consumer {}", getTopic()); + log.info("Unsubscribing and stopping consumer for topics {}", getFullTopicNames()); stopped = true; consumerLock.lock(); try { - doUnsubscribe(); + if (subscribed) { + doUnsubscribe(); + } } finally { consumerLock.unlock(); } @@ -187,4 +193,16 @@ public abstract class AbstractTbQueueConsumerTemplate i abstract protected void doUnsubscribe(); + @Override + public List getFullTopicNames() { + if (partitions == null) { + return Collections.emptyList(); + } + return partitions.stream().map(TopicPartitionInfo::getFullTopicName).collect(Collectors.toList()); + } + + protected boolean isLongPollingSupported() { + return false; + } + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/MultipleTbQueueCallbackWrapper.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/MultipleTbQueueCallbackWrapper.java index f14c9a047b..f102e34666 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/MultipleTbQueueCallbackWrapper.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/MultipleTbQueueCallbackWrapper.java @@ -40,6 +40,6 @@ public class MultipleTbQueueCallbackWrapper implements TbQueueCallback { @Override public void onFailure(Throwable t) { - callback.onFailure(new RuleEngineException(t.getMessage())); + callback.onFailure(new RuleEngineException(t.getMessage(), t)); } } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/MultipleTbQueueTbMsgCallbackWrapper.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/MultipleTbQueueTbMsgCallbackWrapper.java index e812628f16..b3bd05ca5e 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/MultipleTbQueueTbMsgCallbackWrapper.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/MultipleTbQueueTbMsgCallbackWrapper.java @@ -41,6 +41,6 @@ public class MultipleTbQueueTbMsgCallbackWrapper implements TbQueueCallback { @Override public void onFailure(Throwable t) { - tbMsgCallback.onFailure(new RuleEngineException(t.getMessage())); + tbMsgCallback.onFailure(new RuleEngineException(t.getMessage(), t)); } } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/TbQueueTbMsgCallbackWrapper.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/TbQueueTbMsgCallbackWrapper.java index 413ef11e30..1107115085 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/TbQueueTbMsgCallbackWrapper.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/TbQueueTbMsgCallbackWrapper.java @@ -35,6 +35,6 @@ public class TbQueueTbMsgCallbackWrapper implements TbQueueCallback { @Override public void onFailure(Throwable t) { - tbMsgCallback.onFailure(new RuleEngineException(t.getMessage())); + tbMsgCallback.onFailure(new RuleEngineException(t.getMessage(), t)); } } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java index 3c85aef350..64a8b70a1f 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java @@ -23,6 +23,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TbTransportService; +import org.thingsboard.server.common.data.util.CollectionsUtil; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo; @@ -35,6 +36,8 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Set; +import java.util.UUID; import java.util.stream.Collectors; import static org.thingsboard.common.util.SystemUtil.getCpuCount; @@ -57,6 +60,10 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider { @Value("${service.type:monolith}") private String serviceType; + @Getter + @Value("${service.rule_engine.assigned_tenant_profiles:}") + private Set assignedTenantProfiles; + @Autowired private ApplicationContext applicationContext; @@ -78,6 +85,9 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider { } else { serviceTypes = Collections.singletonList(ServiceType.of(serviceType)); } + if (!serviceTypes.contains(ServiceType.TB_RULE_ENGINE) || assignedTenantProfiles == null) { + assignedTenantProfiles = Collections.emptySet(); + } generateNewServiceInfoWithCurrentSystemInfo(); } @@ -111,7 +121,9 @@ public class DefaultTbServiceInfoProvider implements TbServiceInfoProvider { .setServiceId(serviceId) .addAllServiceTypes(serviceTypes.stream().map(ServiceType::name).collect(Collectors.toList())) .setSystemInfo(getCurrentSystemInfoProto()); - + if (CollectionsUtil.isNotEmpty(assignedTenantProfiles)) { + builder.addAllAssignedTenantProfiles(assignedTenantProfiles.stream().map(UUID::toString).collect(Collectors.toList())); + } return serviceInfo = builder.build(); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java index 14fb0369ee..a02d4054ac 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/HashPartitionService.java @@ -24,6 +24,7 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.exception.TenantNotFoundException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.gen.transport.TransportProtos; @@ -36,6 +37,7 @@ import org.thingsboard.server.queue.util.AfterStartUp; import javax.annotation.PostConstruct; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -48,6 +50,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.stream.Collectors; +import static org.thingsboard.server.common.data.DataConstants.MAIN_QUEUE_NAME; + @Service @Slf4j public class HashPartitionService implements PartitionService { @@ -67,27 +71,31 @@ public class HashPartitionService implements PartitionService { private final TbServiceInfoProvider serviceInfoProvider; private final TenantRoutingInfoService tenantRoutingInfoService; private final QueueRoutingInfoService queueRoutingInfoService; + private final TopicService topicService; - private volatile ConcurrentMap> myPartitions = new ConcurrentHashMap<>(); + protected volatile ConcurrentMap> myPartitions = new ConcurrentHashMap<>(); private final ConcurrentMap partitionTopicsMap = new ConcurrentHashMap<>(); private final ConcurrentMap partitionSizesMap = new ConcurrentHashMap<>(); private final ConcurrentMap tenantRoutingInfoMap = new ConcurrentHashMap<>(); - private Map> tbTransportServicesByType = new HashMap<>(); private List currentOtherServices; + private final Map> tbTransportServicesByType = new HashMap<>(); + private final Map> responsibleServices = new HashMap<>(); private HashFunction hashFunction; public HashPartitionService(TbServiceInfoProvider serviceInfoProvider, TenantRoutingInfoService tenantRoutingInfoService, ApplicationEventPublisher applicationEventPublisher, - QueueRoutingInfoService queueRoutingInfoService) { + QueueRoutingInfoService queueRoutingInfoService, + TopicService topicService) { this.serviceInfoProvider = serviceInfoProvider; this.tenantRoutingInfoService = tenantRoutingInfoService; this.applicationEventPublisher = applicationEventPublisher; this.queueRoutingInfoService = queueRoutingInfoService; + this.topicService = topicService; } @PostConstruct @@ -113,6 +121,11 @@ public class HashPartitionService implements PartitionService { } } + @Override + public List getMyPartitions(QueueKey queueKey) { + return myPartitions.get(queueKey); + } + private void doInitRuleEnginePartitions() { List queueRoutingInfoList = getQueueRoutingInfos(); queueRoutingInfoList.forEach(queue -> { @@ -165,6 +178,9 @@ public class HashPartitionService implements PartitionService { partitionTopicsMap.put(queueKey, queueUpdateMsg.getQueueTopic()); partitionSizesMap.put(queueKey, queueUpdateMsg.getPartitions()); myPartitions.remove(queueKey); + if (!tenantId.isSysTenantId()) { + tenantRoutingInfoMap.remove(tenantId); + } } @Override @@ -176,14 +192,44 @@ public class HashPartitionService implements PartitionService { partitionSizesMap.remove(queueKey); //TODO: remove after merging tb entity services removeTenant(tenantId); + + if (serviceInfoProvider.isService(ServiceType.TB_RULE_ENGINE)) { + publishPartitionChangeEvent(ServiceType.TB_RULE_ENGINE, Map.of(queueKey, Collections.emptySet())); + } + } + + @Override + public boolean isManagedByCurrentService(TenantId tenantId) { + Set assignedTenantProfiles = serviceInfoProvider.getAssignedTenantProfiles(); + if (assignedTenantProfiles.isEmpty()) { + // TODO: refactor this for common servers + return true; + } else { + if (tenantId.isSysTenantId()) { + return false; + } + TenantProfileId profileId = tenantRoutingInfoService.getRoutingInfo(tenantId).getProfileId(); + return assignedTenantProfiles.contains(profileId.getId()); + } } @Override public TopicPartitionInfo resolve(ServiceType serviceType, String queueName, TenantId tenantId, EntityId entityId) { TenantId isolatedOrSystemTenantId = getIsolatedOrSystemTenantId(serviceType, tenantId); + if (queueName == null) { + queueName = MAIN_QUEUE_NAME; + } QueueKey queueKey = new QueueKey(serviceType, queueName, isolatedOrSystemTenantId); if (!partitionSizesMap.containsKey(queueKey)) { - queueKey = new QueueKey(serviceType, isolatedOrSystemTenantId); + if (isolatedOrSystemTenantId.isSysTenantId()) { + queueKey = new QueueKey(serviceType, TenantId.SYS_TENANT_ID); + } else { + queueKey = new QueueKey(serviceType, queueName, TenantId.SYS_TENANT_ID); + if (!MAIN_QUEUE_NAME.equals(queueName) && !partitionSizesMap.containsKey(queueKey)) { + queueKey = new QueueKey(serviceType, TenantId.SYS_TENANT_ID); + } + log.warn("Using queue {} instead of isolated {} for tenant {}", queueKey, queueName, isolatedOrSystemTenantId); + } } return resolve(queueKey, entityId); } @@ -199,11 +245,12 @@ public class HashPartitionService implements PartitionService { } private TopicPartitionInfo resolve(QueueKey queueKey, EntityId entityId) { - int hash = hashFunction.newHasher() - .putLong(entityId.getId().getMostSignificantBits()) - .putLong(entityId.getId().getLeastSignificantBits()).hash().asInt(); - Integer partitionSize = partitionSizesMap.get(queueKey); + if (partitionSize == null) { + throw new IllegalStateException("Partitions info for queue " + queueKey + " is missing"); + } + + int hash = hash(entityId.getId()); int partition = Math.abs(hash % partitionSize); return buildTopicPartitionInfo(queueKey, partition); @@ -212,6 +259,7 @@ public class HashPartitionService implements PartitionService { @Override public synchronized void recalculatePartitions(ServiceInfo currentService, List otherServices) { tbTransportServicesByType.clear(); + responsibleServices.clear(); logServiceInfo(currentService); otherServices.forEach(this::logServiceInfo); @@ -221,6 +269,7 @@ public class HashPartitionService implements PartitionService { addNode(queueServicesMap, other); } queueServicesMap.values().forEach(list -> list.sort(Comparator.comparing(ServiceInfo::getServiceId))); + responsibleServices.values().forEach(list -> list.sort(Comparator.comparing(ServiceInfo::getServiceId))); final ConcurrentMap> newPartitions = new ConcurrentHashMap<>(); partitionSizesMap.forEach((queueKey, size) -> { @@ -235,12 +284,25 @@ public class HashPartitionService implements PartitionService { final ConcurrentMap> oldPartitions = myPartitions; myPartitions = newPartitions; + Map> changedPartitionsMap = new HashMap<>(); + + Set removed = new HashSet<>(); oldPartitions.forEach((queueKey, partitions) -> { - if (!myPartitions.containsKey(queueKey)) { - log.info("[{}] NO MORE PARTITIONS FOR CURRENT KEY", queueKey); - applicationEventPublisher.publishEvent(new PartitionChangeEvent(this, queueKey, Collections.emptySet())); + if (!newPartitions.containsKey(queueKey)) { + removed.add(queueKey); } }); + if (serviceInfoProvider.isService(ServiceType.TB_RULE_ENGINE)) { + partitionSizesMap.keySet().stream() + .filter(queueKey -> queueKey.getType() == ServiceType.TB_RULE_ENGINE && + !queueKey.getTenantId().isSysTenantId() && + !newPartitions.containsKey(queueKey)) + .forEach(removed::add); + } + removed.forEach(queueKey -> { + log.info("[{}] NO MORE PARTITIONS FOR CURRENT KEY", queueKey); + changedPartitionsMap.put(queueKey, Collections.emptySet()); + }); myPartitions.forEach((queueKey, partitions) -> { if (!partitions.equals(oldPartitions.get(queueKey))) { @@ -248,9 +310,17 @@ public class HashPartitionService implements PartitionService { Set tpiList = partitions.stream() .map(partition -> buildTopicPartitionInfo(queueKey, partition)) .collect(Collectors.toSet()); - applicationEventPublisher.publishEvent(new PartitionChangeEvent(this, queueKey, tpiList)); + changedPartitionsMap.put(queueKey, tpiList); } }); + if (!changedPartitionsMap.isEmpty()) { + Map>> partitionsByServiceType = new HashMap<>(); + changedPartitionsMap.forEach((queueKey, partitions) -> { + partitionsByServiceType.computeIfAbsent(queueKey.getType(), serviceType -> new HashMap<>()) + .put(queueKey, partitions); + }); + partitionsByServiceType.forEach(this::publishPartitionChangeEvent); + } if (currentOtherServices == null) { currentOtherServices = new ArrayList<>(otherServices); @@ -268,12 +338,31 @@ public class HashPartitionService implements PartitionService { changes.addAll(newMap.keySet()); if (!changes.isEmpty()) { applicationEventPublisher.publishEvent(new ClusterTopologyChangeEvent(this, changes)); + responsibleServices.forEach((profileId, serviceInfos) -> { + if (profileId != null) { + log.info("Servers responsible for tenant profile {}: {}", profileId, toServiceIds(serviceInfos)); + } else { + log.info("Servers responsible for system queues: {}", toServiceIds(serviceInfos)); + } + }); } } applicationEventPublisher.publishEvent(new ServiceListChangedEvent(otherServices, currentService)); } + private void publishPartitionChangeEvent(ServiceType serviceType, Map> partitionsMap) { + if (log.isDebugEnabled()) { + log.debug("Publishing partition change event for service type " + serviceType + ":" + System.lineSeparator() + + partitionsMap.entrySet().stream() + .map(entry -> entry.getKey() + " - " + entry.getValue().stream() + .map(TopicPartitionInfo::getFullTopicName).sorted() + .collect(Collectors.toList())) + .collect(Collectors.joining(System.lineSeparator()))); + } + applicationEventPublisher.publishEvent(new PartitionChangeEvent(this, serviceType, partitionsMap)); + } + @Override public Set getAllServiceIds(ServiceType serviceType) { return getAllServices(serviceType).stream().map(ServiceInfo::getServiceId).collect(Collectors.toSet()); @@ -305,9 +394,7 @@ public class HashPartitionService implements PartitionService { @Override public int resolvePartitionIndex(UUID entityId, int partitions) { - int hash = hashFunction.newHasher() - .putLong(entityId.getMostSignificantBits()) - .putLong(entityId.getLeastSignificantBits()).hash().asInt(); + int hash = hash(entityId); return Math.abs(hash % partitions); } @@ -341,7 +428,7 @@ public class HashPartitionService implements PartitionService { private TopicPartitionInfo buildTopicPartitionInfo(QueueKey queueKey, int partition) { TopicPartitionInfo.TopicPartitionInfoBuilder tpi = TopicPartitionInfo.builder(); - tpi.topic(partitionTopicsMap.get(queueKey)); + tpi.topic(topicService.buildTopicName(partitionTopicsMap.get(queueKey))); tpi.partition(partition); tpi.tenantId(queueKey.getTenantId()); @@ -358,16 +445,9 @@ public class HashPartitionService implements PartitionService { if (TenantId.SYS_TENANT_ID.equals(tenantId)) { return false; } - TenantRoutingInfo routingInfo = tenantRoutingInfoMap.get(tenantId); - if (routingInfo == null) { - synchronized (tenantRoutingInfoMap) { - routingInfo = tenantRoutingInfoMap.get(tenantId); - if (routingInfo == null) { - routingInfo = tenantRoutingInfoService.getRoutingInfo(tenantId); - tenantRoutingInfoMap.put(tenantId, routingInfo); - } - } - } + TenantRoutingInfo routingInfo = tenantRoutingInfoMap.computeIfAbsent(tenantId, k -> { + return tenantRoutingInfoService.getRoutingInfo(tenantId); + }); if (routingInfo == null) { throw new TenantNotFoundException(tenantId); } @@ -396,6 +476,19 @@ public class HashPartitionService implements PartitionService { queueServiceList.computeIfAbsent(key, k -> new ArrayList<>()).add(instance); } }); + + if (instance.getAssignedTenantProfilesCount() > 0) { + for (String profileIdStr : instance.getAssignedTenantProfilesList()) { + TenantProfileId profileId; + try { + profileId = new TenantProfileId(UUID.fromString(profileIdStr)); + } catch (IllegalArgumentException e) { + log.warn("Failed to parse '{}' as tenant profile id", profileIdStr); + continue; + } + responsibleServices.computeIfAbsent(profileId, k -> new ArrayList<>()).add(instance); + } + } } else if (ServiceType.TB_CORE.equals(serviceType) || ServiceType.TB_VC_EXECUTOR.equals(serviceType)) { queueServiceList.computeIfAbsent(new QueueKey(serviceType), key -> new ArrayList<>()).add(instance); } @@ -411,18 +504,50 @@ public class HashPartitionService implements PartitionService { return null; } - if (!ServiceType.TB_RULE_ENGINE.equals(queueKey.getType()) || TenantId.SYS_TENANT_ID.equals(queueKey.getTenantId())) { - return servers.get(partition % servers.size()); - } else { - int hash = hashFunction.newHasher().putLong(queueKey.getTenantId().getId().getMostSignificantBits()) - .putLong(queueKey.getTenantId().getId().getLeastSignificantBits()) - .putString(queueKey.getQueueName(), StandardCharsets.UTF_8) - .hash().asInt(); + TenantId tenantId = queueKey.getTenantId(); + if (queueKey.getType() == ServiceType.TB_RULE_ENGINE) { + if (!responsibleServices.isEmpty()) { // if there are any dedicated servers + TenantProfileId profileId; + if (tenantId != null && !tenantId.isSysTenantId()) { + TenantRoutingInfo routingInfo = tenantRoutingInfoService.getRoutingInfo(tenantId); + profileId = routingInfo.getProfileId(); + } else { + profileId = null; + } + + List responsible = responsibleServices.get(profileId); + if (responsible == null) { + // if there are no dedicated servers for this tenant profile, or for system queues, + // using the servers that are not responsible for any profile + responsible = servers.stream() + .filter(serviceInfo -> serviceInfo.getAssignedTenantProfilesCount() == 0) + .sorted(Comparator.comparing(ServiceInfo::getServiceId)) + .collect(Collectors.toList()); + if (profileId != null) { + log.debug("Using servers {} for profile {}", toServiceIds(responsible), profileId); + } + responsibleServices.put(profileId, responsible); + } + if (responsible.isEmpty()) { + return null; + } + servers = responsible; + } + int hash = hash(tenantId.getId()); return servers.get(Math.abs((hash + partition) % servers.size())); + } else { + return servers.get(partition % servers.size()); } } + private int hash(UUID key) { + return hashFunction.newHasher() + .putLong(key.getMostSignificantBits()) + .putLong(key.getLeastSignificantBits()) + .hash().asInt(); + } + public static HashFunction forName(String name) { switch (name) { case "murmur3_32": @@ -436,4 +561,8 @@ public class HashPartitionService implements PartitionService { } } + private List toServiceIds(Collection serviceInfos) { + return serviceInfos.stream().map(ServiceInfo::getServiceId).collect(Collectors.toList()); + } + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/NotificationsTopicService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/NotificationsTopicService.java deleted file mode 100644 index c209bd1aee..0000000000 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/NotificationsTopicService.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.queue.discovery; - -import org.springframework.stereotype.Service; -import org.thingsboard.server.common.msg.queue.ServiceType; -import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; - -import java.util.HashMap; -import java.util.Map; - -@Service -public class NotificationsTopicService { - - private Map tbCoreNotificationTopics = new HashMap<>(); - private Map tbRuleEngineNotificationTopics = new HashMap<>(); - - /** - * Each Service should start a consumer for messages that target individual service instance based on serviceId. - * This topic is likely to have single partition, and is always assigned to the service. - * @param serviceType - * @param serviceId - * @return - */ - public TopicPartitionInfo getNotificationsTopic(ServiceType serviceType, String serviceId) { - switch (serviceType) { - case TB_CORE: - return tbCoreNotificationTopics.computeIfAbsent(serviceId, - id -> buildNotificationsTopicPartitionInfo(serviceType, serviceId)); - case TB_RULE_ENGINE: - return tbRuleEngineNotificationTopics.computeIfAbsent(serviceId, - id -> buildNotificationsTopicPartitionInfo(serviceType, serviceId)); - default: - return buildNotificationsTopicPartitionInfo(serviceType, serviceId); - } - } - - private TopicPartitionInfo buildNotificationsTopicPartitionInfo(ServiceType serviceType, String serviceId) { - return new TopicPartitionInfo(serviceType.name().toLowerCase() + ".notifications." + serviceId, null, null, false); - } -} \ No newline at end of file diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java index faa4d956a8..fe06db789b 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/PartitionService.java @@ -37,6 +37,8 @@ public interface PartitionService { boolean isMyPartition(ServiceType serviceType, TenantId tenantId, EntityId entityId); + List getMyPartitions(QueueKey queueKey); + /** * Received from the Discovery service when network topology is changed. * @param currentService - current service information {@link org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo} @@ -64,4 +66,7 @@ public interface PartitionService { void updateQueue(TransportProtos.QueueUpdateMsg queueUpdateMsg); void removeQueue(TransportProtos.QueueDeleteMsg queueDeleteMsg); + + boolean isManagedByCurrentService(TenantId tenantId); + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbServiceInfoProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbServiceInfoProvider.java index e49cbbcfd9..9c7d1630ec 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbServiceInfoProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TbServiceInfoProvider.java @@ -18,6 +18,9 @@ package org.thingsboard.server.queue.discovery; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo; +import java.util.Set; +import java.util.UUID; + public interface TbServiceInfoProvider { String getServiceId(); @@ -30,4 +33,6 @@ public interface TbServiceInfoProvider { ServiceInfo generateNewServiceInfoWithCurrentSystemInfo(); + Set getAssignedTenantProfiles(); + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TenantRoutingInfo.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TenantRoutingInfo.java index c1c0b49dab..8dee68da49 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TenantRoutingInfo.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TenantRoutingInfo.java @@ -17,9 +17,11 @@ package org.thingsboard.server.queue.discovery; import lombok.Data; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.TenantProfileId; @Data public class TenantRoutingInfo { private final TenantId tenantId; + private final TenantProfileId profileId; private final boolean isolatedTbRuleEngine; } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TenantRoutingInfoService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TenantRoutingInfoService.java index 8dd3ff95e7..e4c0ac8250 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TenantRoutingInfoService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TenantRoutingInfoService.java @@ -20,4 +20,5 @@ import org.thingsboard.server.common.data.id.TenantId; public interface TenantRoutingInfoService { TenantRoutingInfo getRoutingInfo(TenantId tenantId); + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TopicService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TopicService.java new file mode 100644 index 0000000000..40e19519b0 --- /dev/null +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TopicService.java @@ -0,0 +1,67 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.queue.discovery; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; + +import java.util.HashMap; +import java.util.Map; + +@Service +public class TopicService { + + @Value("${queue.prefix:}") + private String prefix; + + private Map tbCoreNotificationTopics = new HashMap<>(); + private Map tbRuleEngineNotificationTopics = new HashMap<>(); + + /** + * Each Service should start a consumer for messages that target individual service instance based on serviceId. + * This topic is likely to have single partition, and is always assigned to the service. + * @param serviceType + * @param serviceId + * @return + */ + public TopicPartitionInfo getNotificationsTopic(ServiceType serviceType, String serviceId) { + switch (serviceType) { + case TB_CORE: + return tbCoreNotificationTopics.computeIfAbsent(serviceId, + id -> buildNotificationsTopicPartitionInfo(serviceType, serviceId)); + case TB_RULE_ENGINE: + return tbRuleEngineNotificationTopics.computeIfAbsent(serviceId, + id -> buildNotificationsTopicPartitionInfo(serviceType, serviceId)); + default: + return buildNotificationsTopicPartitionInfo(serviceType, serviceId); + } + } + + private TopicPartitionInfo buildNotificationsTopicPartitionInfo(ServiceType serviceType, String serviceId) { + return buildTopicPartitionInfo(serviceType.name().toLowerCase() + ".notifications." + serviceId, null, null, false); + } + + public TopicPartitionInfo buildTopicPartitionInfo(String topic, TenantId tenantId, Integer partition, boolean myPartition) { + return new TopicPartitionInfo(buildTopicName(topic), tenantId, partition, myPartition); + } + + public String buildTopicName(String topic) { + return prefix.isBlank() ? topic : prefix + "." + topic; + } +} \ No newline at end of file diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/ZkDiscoveryService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/ZkDiscoveryService.java index fcf80bcf3d..d94ff082d1 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/ZkDiscoveryService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/ZkDiscoveryService.java @@ -16,6 +16,7 @@ package org.thingsboard.server.queue.discovery; import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.ProtocolStringList; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.curator.framework.CuratorFramework; @@ -34,18 +35,22 @@ import org.apache.zookeeper.KeeperException; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.util.Assert; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.queue.discovery.event.OtherServiceShutdownEvent; import org.thingsboard.server.queue.util.AfterStartUp; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.util.List; import java.util.NoSuchElementException; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -66,7 +71,12 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi private Integer zkSessionTimeout; @Value("${zk.zk_dir}") private String zkDir; + @Value("${zk.recalculate_delay:0}") + private Long recalculateDelay; + protected final ConcurrentHashMap> delayedTasks; + + private final ApplicationEventPublisher applicationEventPublisher; private final TbServiceInfoProvider serviceInfoProvider; private final PartitionService partitionService; @@ -78,10 +88,13 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi private volatile boolean stopped = true; - public ZkDiscoveryService(TbServiceInfoProvider serviceInfoProvider, + public ZkDiscoveryService(ApplicationEventPublisher applicationEventPublisher, + TbServiceInfoProvider serviceInfoProvider, PartitionService partitionService) { + this.applicationEventPublisher = applicationEventPublisher; this.serviceInfoProvider = serviceInfoProvider; this.partitionService = partitionService; + delayedTasks = new ConcurrentHashMap<>(); } @PostConstruct @@ -133,9 +146,11 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi return; } log.info("Going to publish current server..."); - zkExecutorService.scheduleAtFixedRate(this::publishCurrentServer, 0, 1, TimeUnit.MINUTES); + publishCurrentServer(); log.info("Going to recalculate partitions..."); recalculatePartitions(); + + zkExecutorService.scheduleAtFixedRate(this::publishCurrentServer, 1, 1, TimeUnit.MINUTES); } @SneakyThrows @@ -287,11 +302,40 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi log.error("Failed to decode server instance for node {}", data.getPath(), e); throw e; } - log.debug("Processing [{}] event for [{}]", pathChildrenCacheEvent.getType(), instance.getServiceId()); + + String serviceId = instance.getServiceId(); + ProtocolStringList serviceTypesList = instance.getServiceTypesList(); + + log.trace("Processing [{}] event for [{}]", pathChildrenCacheEvent.getType(), serviceId); switch (pathChildrenCacheEvent.getType()) { case CHILD_ADDED: + ScheduledFuture task = delayedTasks.remove(serviceId); + if (task != null) { + if (task.cancel(false)) { + log.debug("[{}] Recalculate partitions ignored. Service was restarted in time [{}].", + serviceId, serviceTypesList); + } else { + log.debug("[{}] Going to recalculate partitions. Service was not restarted in time [{}]!", + serviceId, serviceTypesList); + recalculatePartitions(); + } + } else { + log.trace("[{}] Going to recalculate partitions due to adding new node [{}].", + serviceId, serviceTypesList); + recalculatePartitions(); + } + break; case CHILD_REMOVED: - recalculatePartitions(); + zkExecutorService.submit(() -> applicationEventPublisher.publishEvent(new OtherServiceShutdownEvent(this, serviceId, serviceTypesList))); + ScheduledFuture future = zkExecutorService.schedule(() -> { + log.debug("[{}] Going to recalculate partitions due to removed node [{}]", + serviceId, serviceTypesList); + ScheduledFuture removedTask = delayedTasks.remove(serviceId); + if (removedTask != null) { + recalculatePartitions(); + } + }, recalculateDelay, TimeUnit.MILLISECONDS); + delayedTasks.put(serviceId, future); break; default: break; @@ -303,6 +347,8 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi * Synchronized to ensure that other servers info is up to date * */ synchronized void recalculatePartitions() { + delayedTasks.values().forEach(future -> future.cancel(false)); + delayedTasks.clear(); partitionService.recalculatePartitions(serviceInfoProvider.getServiceInfo(), getOtherServers()); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/OtherServiceShutdownEvent.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/OtherServiceShutdownEvent.java new file mode 100644 index 0000000000..5de09696c6 --- /dev/null +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/OtherServiceShutdownEvent.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.queue.discovery.event; + +import com.google.protobuf.ProtocolStringList; +import lombok.Getter; +import org.thingsboard.server.common.msg.queue.ServiceType; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class OtherServiceShutdownEvent extends TbApplicationEvent { + + private static final long serialVersionUID = -2441739930040282254L; + + @Getter + private final String serviceId; + @Getter + private final Set serviceTypes; + + public OtherServiceShutdownEvent(Object source, String serviceId, List serviceTypes) { + super(source); + this.serviceId = serviceId; + this.serviceTypes = serviceTypes.stream().map(ServiceType::valueOf).collect(Collectors.toSet()); + } +} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/PartitionChangeEvent.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/PartitionChangeEvent.java index c7a00daa1c..0d74cdfaee 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/PartitionChangeEvent.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/PartitionChangeEvent.java @@ -21,6 +21,8 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.queue.discovery.QueueKey; +import java.util.Collections; +import java.util.Map; import java.util.Set; @ToString(callSuper = true) @@ -29,17 +31,19 @@ public class PartitionChangeEvent extends TbApplicationEvent { private static final long serialVersionUID = -8731788167026510559L; @Getter - private final QueueKey queueKey; + private final ServiceType serviceType; @Getter - private final Set partitions; + private final Map> partitionsMap; - public PartitionChangeEvent(Object source, QueueKey queueKey, Set partitions) { + public PartitionChangeEvent(Object source, ServiceType serviceType, Map> partitionsMap) { super(source); - this.queueKey = queueKey; - this.partitions = partitions; + this.serviceType = serviceType; + this.partitionsMap = partitionsMap; } - public ServiceType getServiceType() { - return queueKey.getType(); + // only for service types that have single QueueKey + public Set getPartitions() { + return partitionsMap.values().stream().findAny().orElse(Collections.emptySet()); } + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaAdmin.java b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaAdmin.java index f15b9258e8..d486d04783 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaAdmin.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaAdmin.java @@ -21,6 +21,7 @@ import org.apache.kafka.clients.admin.CreateTopicsResult; import org.apache.kafka.clients.admin.NewTopic; import org.apache.kafka.common.errors.TopicExistsException; import org.thingsboard.server.queue.TbQueueAdmin; +import org.thingsboard.server.queue.util.PropertyUtils; import java.util.Collections; import java.util.Map; @@ -62,12 +63,12 @@ public class TbKafkaAdmin implements TbQueueAdmin { } @Override - public void createTopicIfNotExists(String topic) { + public void createTopicIfNotExists(String topic, String properties) { if (topics.contains(topic)) { return; } try { - NewTopic newTopic = new NewTopic(topic, numPartitions, replicationFactor).configs(topicConfigs); + NewTopic newTopic = new NewTopic(topic, numPartitions, replicationFactor).configs(PropertyUtils.getProps(topicConfigs, properties)); createTopic(newTopic).values().get(topic).get(); topics.add(topic); } catch (ExecutionException ee) { @@ -81,7 +82,6 @@ public class TbKafkaAdmin implements TbQueueAdmin { log.warn("[{}] Failed to create topic", topic, e); throw new RuntimeException(e); } - } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerTemplate.java index c17a563d46..ff3f0cc9b3 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaConsumerTemplate.java @@ -73,7 +73,6 @@ public class TbKafkaConsumerTemplate extends AbstractTbQue protected void doSubscribe(List topicNames) { if (!topicNames.isEmpty()) { topicNames.forEach(admin::createTopicIfNotExists); - log.info("subscribe topics {}", topicNames); consumer.subscribe(topicNames); } else { log.info("unsubscribe due to empty topic list"); @@ -114,7 +113,6 @@ public class TbKafkaConsumerTemplate extends AbstractTbQue @Override protected void doUnsubscribe() { - log.info("unsubscribe topic and close consumer for topic {}", getTopic()); if (consumer != null) { consumer.unsubscribe(); consumer.close(); @@ -123,4 +121,10 @@ public class TbKafkaConsumerTemplate extends AbstractTbQue statsService.unregisterClientGroup(groupId); } } + + @Override + public boolean isLongPollingSupported() { + return true; + } + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaProducerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaProducerTemplate.java index 15c2f04d17..d67d2c4399 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaProducerTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaProducerTemplate.java @@ -30,6 +30,9 @@ import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueMsg; import org.thingsboard.server.queue.TbQueueProducer; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Objects; import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -53,10 +56,14 @@ public class TbKafkaProducerTemplate implements TbQueuePro private final Set topics; + @Getter + private final String clientId; + @Builder private TbKafkaProducerTemplate(TbKafkaSettings settings, String defaultTopic, String clientId, TbQueueAdmin admin) { Properties props = settings.toProducerProps(); + this.clientId = Objects.requireNonNull(clientId, "Kafka producer client.id is null"); if (!StringUtils.isEmpty(clientId)) { props.put(ProducerConfig.CLIENT_ID_CONFIG, clientId); } @@ -72,6 +79,22 @@ public class TbKafkaProducerTemplate implements TbQueuePro public void init() { } + void addAnalyticHeaders(List
headers) { + headers.add(new RecordHeader("_producerId", getClientId().getBytes(StandardCharsets.UTF_8))); + headers.add(new RecordHeader("_threadName", Thread.currentThread().getName().getBytes(StandardCharsets.UTF_8))); + if (log.isTraceEnabled()) { + try { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + int maxLevel = Math.min(stackTrace.length, 20); + for (int i = 2; i < maxLevel; i++) { // ignore two levels: getStackTrace and addAnalyticHeaders + headers.add(new RecordHeader("_stackTrace" + i, stackTrace[i].toString().getBytes(StandardCharsets.UTF_8))); + } + } catch (Throwable t) { + log.trace("Failed to add stacktrace headers in Kafka producer {}", getClientId(), t); + } + } + } + @Override public void send(TopicPartitionInfo tpi, T msg, TbQueueCallback callback) { try { @@ -79,7 +102,10 @@ public class TbKafkaProducerTemplate implements TbQueuePro String key = msg.getKey().toString(); byte[] data = msg.getData(); ProducerRecord record; - Iterable
headers = msg.getHeaders().getData().entrySet().stream().map(e -> new RecordHeader(e.getKey(), e.getValue())).collect(Collectors.toList()); + List
headers = msg.getHeaders().getData().entrySet().stream().map(e -> new RecordHeader(e.getKey(), e.getValue())).collect(Collectors.toList()); + if (log.isDebugEnabled()) { + addAnalyticHeaders(headers); + } record = new ProducerRecord<>(tpi.getFullTopicName(), null, key, data, headers); producer.send(record, (metadata, exception) -> { if (exception == null) { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaSettings.java b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaSettings.java index 41ae0d5ea3..55f1721c46 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaSettings.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaSettings.java @@ -115,6 +115,9 @@ public class TbKafkaSettings { @Value("${queue.kafka.session.timeout.ms:10000}") private int sessionTimeoutMs; + @Value("${queue.kafka.auto_offset_reset:earliest}") + private String autoOffsetReset; + @Value("${queue.kafka.use_confluent_cloud:false}") private boolean useConfluent; @@ -155,6 +158,8 @@ public class TbKafkaSettings { props.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, maxPartitionFetchBytes); props.put(ConsumerConfig.FETCH_MAX_BYTES_CONFIG, fetchMaxBytes); props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, maxPollIntervalMs); + props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, autoOffsetReset); + props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false); props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryTbQueueConsumer.java b/common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryTbQueueConsumer.java index 081202315e..a7f8cadd0d 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryTbQueueConsumer.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryTbQueueConsumer.java @@ -57,9 +57,15 @@ public class InMemoryTbQueueConsumer implements TbQueueCon subscribed = true; } + @Override + public void stop() { + stopped = true; + } + @Override public void unsubscribe() { stopped = true; + subscribed = false; } @Override @@ -103,4 +109,9 @@ public class InMemoryTbQueueConsumer implements TbQueueCon return stopped; } + @Override + public List getFullTopicNames() { + return partitions.stream().map(TopicPartitionInfo::getFullTopicName).collect(Collectors.toList()); + } + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/notification/DefaultNotificationDeduplicationService.java b/common/queue/src/main/java/org/thingsboard/server/queue/notification/DefaultNotificationDeduplicationService.java new file mode 100644 index 0000000000..871408ac40 --- /dev/null +++ b/common/queue/src/main/java/org/thingsboard/server/queue/notification/DefaultNotificationDeduplicationService.java @@ -0,0 +1,125 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.queue.notification; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.stereotype.Service; +import org.springframework.util.ConcurrentReferenceHashMap; +import org.thingsboard.server.common.data.CacheConstants; +import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTrigger; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; +import org.thingsboard.server.queue.util.PropertyUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentMap; + +import static org.springframework.util.ConcurrentReferenceHashMap.ReferenceType.SOFT; + +@Service +@RequiredArgsConstructor +@Slf4j +public class DefaultNotificationDeduplicationService implements NotificationDeduplicationService { + + private Map deduplicationDurations; + + @Autowired(required = false) + private CacheManager cacheManager; + private final ConcurrentMap localCache = new ConcurrentReferenceHashMap<>(16, SOFT); + + @Override + public boolean alreadyProcessed(NotificationRuleTrigger trigger) { + String deduplicationKey = trigger.getDeduplicationKey(); + return alreadyProcessed(trigger, deduplicationKey, true); + } + + @Override + public boolean alreadyProcessed(NotificationRuleTrigger trigger, NotificationRule rule) { + String deduplicationKey = getDeduplicationKey(trigger, rule); + return alreadyProcessed(trigger, deduplicationKey, false); + } + + private boolean alreadyProcessed(NotificationRuleTrigger trigger, String deduplicationKey, boolean onlyLocalCache) { + Long lastProcessedTs = localCache.get(deduplicationKey); + if (lastProcessedTs == null && !onlyLocalCache) { + Cache externalCache = getExternalCache(); + if (externalCache != null) { + lastProcessedTs = externalCache.get(deduplicationKey, Long.class); + } else { + log.warn("Sent notifications cache is not set up"); + } + } + + boolean alreadyProcessed = false; + long deduplicationDuration = getDeduplicationDuration(trigger); + if (lastProcessedTs != null) { + long passed = System.currentTimeMillis() - lastProcessedTs; + log.trace("Deduplicating trigger {} by key '{}'. Deduplication duration: {} ms, passed: {} ms", + trigger.getType(), deduplicationKey, deduplicationDuration, passed); + if (deduplicationDuration == 0 || passed <= deduplicationDuration) { + alreadyProcessed = true; + } + } + + if (!alreadyProcessed) { + lastProcessedTs = System.currentTimeMillis(); + } + localCache.put(deduplicationKey, lastProcessedTs); + if (!onlyLocalCache) { + if (!alreadyProcessed || deduplicationDuration == 0) { + // if lastProcessedTs is changed or if deduplicating infinitely (so that cache value not removed by ttl) + Cache externalCache = getExternalCache(); + if (externalCache != null) { + externalCache.put(deduplicationKey, lastProcessedTs); + } + } + } + return alreadyProcessed; + } + + public static String getDeduplicationKey(NotificationRuleTrigger trigger, NotificationRule rule) { + return String.join("_", trigger.getDeduplicationKey(), rule.getDeduplicationKey()); + } + + private long getDeduplicationDuration(NotificationRuleTrigger trigger) { + return deduplicationDurations.computeIfAbsent(trigger.getType(), triggerType -> { + return trigger.getDefaultDeduplicationDuration(); + }); + } + + private Cache getExternalCache() { + return Optional.ofNullable(cacheManager) + .map(cacheManager -> cacheManager.getCache(CacheConstants.SENT_NOTIFICATIONS_CACHE)) + .orElse(null); + } + + @Autowired + public void setDeduplicationDurations(@Value("${notification_system.rules.deduplication_durations:}") + String deduplicationDurationsStr) { + this.deduplicationDurations = new HashMap<>(); + PropertyUtils.getProps(deduplicationDurationsStr).forEach((triggerType, duration) -> { + this.deduplicationDurations.put(NotificationRuleTriggerType.valueOf(triggerType), Long.parseLong(duration)); + }); + } + +} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/notification/NotificationDeduplicationService.java b/common/queue/src/main/java/org/thingsboard/server/queue/notification/NotificationDeduplicationService.java new file mode 100644 index 0000000000..3ccc98ecb6 --- /dev/null +++ b/common/queue/src/main/java/org/thingsboard/server/queue/notification/NotificationDeduplicationService.java @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.queue.notification; + +import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTrigger; + +public interface NotificationDeduplicationService { + + boolean alreadyProcessed(NotificationRuleTrigger trigger); + + boolean alreadyProcessed(NotificationRuleTrigger trigger, NotificationRule rule); + +} diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/notification/RemoteNotificationRuleProcessor.java b/common/queue/src/main/java/org/thingsboard/server/queue/notification/RemoteNotificationRuleProcessor.java index 617fdbb50e..8ad5816b77 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/notification/RemoteNotificationRuleProcessor.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/notification/RemoteNotificationRuleProcessor.java @@ -19,57 +19,45 @@ import com.google.protobuf.ByteString; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Service; -import org.springframework.util.ConcurrentReferenceHashMap; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; -import org.thingsboard.server.common.data.notification.settings.TriggerTypeConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTrigger; import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; -import org.thingsboard.server.common.msg.notification.trigger.NotificationRuleTrigger; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.util.DataDecodingEncodingService; -import java.util.EnumMap; -import java.util.Map; import java.util.UUID; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicBoolean; - -import static org.springframework.util.ConcurrentReferenceHashMap.ReferenceType.SOFT; @Service @ConditionalOnMissingBean(value = NotificationRuleProcessor.class, ignored = RemoteNotificationRuleProcessor.class) -@ConfigurationProperties(prefix = "notification-system.rules") @RequiredArgsConstructor @Slf4j public class RemoteNotificationRuleProcessor implements NotificationRuleProcessor { + private final NotificationDeduplicationService deduplicationService; private final TbQueueProducerProvider producerProvider; - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final PartitionService partitionService; private final DataDecodingEncodingService encodingService; - private Map triggerTypesConfigs; - private final ConcurrentMap submittedTriggers = new ConcurrentReferenceHashMap<>(16, SOFT); - @Override public void process(NotificationRuleTrigger trigger) { - if (trigger.deduplicate() && alreadySubmitted(trigger)) { - return; - } try { + if (trigger.deduplicate() && deduplicationService.alreadyProcessed(trigger)) { + return; + } + log.debug("Submitting notification rule trigger: {}", trigger); TransportProtos.NotificationRuleProcessorMsg.Builder msg = TransportProtos.NotificationRuleProcessorMsg.newBuilder() .setTrigger(ByteString.copyFrom(encodingService.encode(trigger))); partitionService.getAllServiceIds(ServiceType.TB_CORE).stream().findAny().ifPresent(serviceId -> { - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId); + TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceId); producerProvider.getTbCoreNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), TransportProtos.ToCoreNotificationMsg.newBuilder() .setNotificationRuleProcessorMsg(msg) @@ -80,47 +68,4 @@ public class RemoteNotificationRuleProcessor implements NotificationRuleProcesso } } - private boolean alreadySubmitted(NotificationRuleTrigger trigger) { - String deduplicationKey = trigger.getDeduplicationKey(); - - AtomicBoolean alreadySubmitted = new AtomicBoolean(false); - submittedTriggers.compute(deduplicationKey, (key, lastSubmittedTs) -> { - long currentTs = System.currentTimeMillis(); - if (lastSubmittedTs == null) { - return currentTs; - } else { - long deduplicationDuration = getDeduplicationDuration(trigger); - long passed = currentTs - lastSubmittedTs; - if (deduplicationDuration == 0 || passed <= deduplicationDuration) { - log.trace("Notification rule trigger {} was already submitted {} ms ago, deduplication duration is {} ms. Key: '{}'", - trigger.getType(), passed, deduplicationDuration, deduplicationKey); - alreadySubmitted.set(true); - return lastSubmittedTs; - } else { - return currentTs; - } - } - }); - return alreadySubmitted.get(); - } - - private long getDeduplicationDuration(NotificationRuleTrigger trigger) { - if (triggerTypesConfigs == null) { - triggerTypesConfigs = new EnumMap<>(NotificationRuleTriggerType.class); - } - TriggerTypeConfig triggerTypeConfig = triggerTypesConfigs.computeIfAbsent(trigger.getType(), triggerType -> { - TriggerTypeConfig config = new TriggerTypeConfig(); - config.setDeduplicationDuration(trigger.getDefaultDeduplicationDuration()); - return config; - }); - return triggerTypeConfig.getDeduplicationDuration(); - } - - // set from ConfigurationProperties - public void setTriggerTypesConfigs(Map triggerTypesConfigs) { - if (triggerTypesConfigs != null) { - this.triggerTypesConfigs = new EnumMap<>(triggerTypesConfigs); - } - } - } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java index 08609b13d0..5774e59d32 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java @@ -40,7 +40,7 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.settings.TbQueueCoreSettings; import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; @@ -61,7 +61,7 @@ import java.nio.charset.StandardCharsets; @ConditionalOnExpression("'${queue.type:null}'=='aws-sqs' && '${service.type:null}'=='monolith'") public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngineQueueFactory, TbVersionControlQueueFactory { - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final TbQueueCoreSettings coreSettings; private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueRuleEngineSettings ruleEngineSettings; @@ -79,7 +79,7 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng private final TbQueueAdmin otaAdmin; private final TbQueueAdmin vcAdmin; - public AwsSqsMonolithQueueFactory(NotificationsTopicService notificationsTopicService, TbQueueCoreSettings coreSettings, + public AwsSqsMonolithQueueFactory(TopicService topicService, TbQueueCoreSettings coreSettings, TbQueueRuleEngineSettings ruleEngineSettings, TbServiceInfoProvider serviceInfoProvider, TbQueueTransportApiSettings transportApiSettings, @@ -88,7 +88,7 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng TbQueueVersionControlSettings vcSettings, TbAwsSqsQueueAttributes sqsQueueAttributes, TbQueueRemoteJsInvokeSettings jsInvokeSettings) { - this.notificationsTopicService = notificationsTopicService; + this.topicService = topicService; this.coreSettings = coreSettings; this.serviceInfoProvider = serviceInfoProvider; this.ruleEngineSettings = ruleEngineSettings; @@ -109,71 +109,71 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng @Override public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, transportNotificationSettings.getNotificationsTopic()); + return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); } @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbAwsSqsProducerTemplate<>(ruleEngineAdmin, sqsSettings, ruleEngineSettings.getTopic()); + return new TbAwsSqsProducerTemplate<>(ruleEngineAdmin, sqsSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, ruleEngineSettings.getTopic()); + return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getTopic()); + return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, coreSettings.getTopic()); + return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToVersionControlMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(vcAdmin, sqsSettings, vcSettings.getTopic(), + return new TbAwsSqsConsumerTemplate<>(vcAdmin, sqsSettings, topicService.buildTopicName(vcSettings.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()) ); } @Override public TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration) { - return new TbAwsSqsConsumerTemplate<>(ruleEngineAdmin, sqsSettings, configuration.getTopic(), + return new TbAwsSqsConsumerTemplate<>(ruleEngineAdmin, sqsSettings, topicService.buildTopicName(configuration.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { return new TbAwsSqsConsumerTemplate<>(notificationAdmin, sqsSettings, - notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), + topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToCoreMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(coreAdmin, sqsSettings, coreSettings.getTopic(), + return new TbAwsSqsConsumerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { return new TbAwsSqsConsumerTemplate<>(notificationAdmin, sqsSettings, - notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), + topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createTransportApiRequestConsumer() { - return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, transportApiSettings.getRequestsTopic(), + return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createTransportApiResponseProducer() { - return new TbAwsSqsProducerTemplate<>(transportApiAdmin, sqsSettings, transportApiSettings.getResponsesTopic()); + return new TbAwsSqsProducerTemplate<>(transportApiAdmin, sqsSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic())); } @Override @@ -201,29 +201,29 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getUsageStatsTopic()); + return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @Override public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(coreAdmin, sqsSettings, coreSettings.getUsageStatsTopic(), + return new TbAwsSqsConsumerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(otaAdmin, sqsSettings, coreSettings.getOtaPackageTopic(), + return new TbAwsSqsConsumerTemplate<>(otaAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(otaAdmin, sqsSettings, coreSettings.getOtaPackageTopic()); + return new TbAwsSqsProducerTemplate<>(otaAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); } @Override public TbQueueProducer> createVersionControlMsgProducer() { - return new TbAwsSqsProducerTemplate<>(vcAdmin, sqsSettings, vcSettings.getTopic()); + return new TbAwsSqsProducerTemplate<>(vcAdmin, sqsSettings, topicService.buildTopicName(vcSettings.getTopic())); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java index 257ac37408..d7f7a0412f 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java @@ -38,7 +38,7 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.settings.TbQueueCoreSettings; import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; @@ -63,7 +63,7 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory { private final TbQueueRuleEngineSettings ruleEngineSettings; private final TbQueueCoreSettings coreSettings; private final TbQueueTransportApiSettings transportApiSettings; - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; private final TbQueueTransportNotificationSettings transportNotificationSettings; @@ -81,7 +81,7 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory { TbQueueCoreSettings coreSettings, TbQueueTransportApiSettings transportApiSettings, TbQueueRuleEngineSettings ruleEngineSettings, - NotificationsTopicService notificationsTopicService, + TopicService topicService, TbQueueVersionControlSettings vcSettings, TbServiceInfoProvider serviceInfoProvider, TbQueueRemoteJsInvokeSettings jsInvokeSettings, @@ -91,7 +91,7 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory { this.coreSettings = coreSettings; this.transportApiSettings = transportApiSettings; this.ruleEngineSettings = ruleEngineSettings; - this.notificationsTopicService = notificationsTopicService; + this.topicService = topicService; this.serviceInfoProvider = serviceInfoProvider; this.jsInvokeSettings = jsInvokeSettings; this.transportNotificationSettings = transportNotificationSettings; @@ -108,51 +108,51 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory { @Override public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, transportNotificationSettings.getNotificationsTopic()); + return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); } @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getTopic()); + return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, ruleEngineSettings.getTopic()); + return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getTopic()); + return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, coreSettings.getTopic()); + return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToCoreMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(coreAdmin, sqsSettings, coreSettings.getTopic(), + return new TbAwsSqsConsumerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { return new TbAwsSqsConsumerTemplate<>(notificationAdmin, sqsSettings, - notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), + topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createTransportApiRequestConsumer() { - return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, transportApiSettings.getRequestsTopic(), + return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createTransportApiResponseProducer() { - return new TbAwsSqsProducerTemplate<>(transportApiAdmin, sqsSettings, transportApiSettings.getResponsesTopic()); + return new TbAwsSqsProducerTemplate<>(transportApiAdmin, sqsSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic())); } @Override @@ -180,29 +180,29 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory { @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getUsageStatsTopic()); + return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @Override public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(coreAdmin, sqsSettings, coreSettings.getUsageStatsTopic(), + return new TbAwsSqsConsumerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(otaAdmin, sqsSettings, coreSettings.getOtaPackageTopic(), + return new TbAwsSqsConsumerTemplate<>(otaAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(otaAdmin, sqsSettings, coreSettings.getOtaPackageTopic()); + return new TbAwsSqsProducerTemplate<>(otaAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); } @Override public TbQueueProducer> createVersionControlMsgProducer() { - return new TbAwsSqsProducerTemplate<>(vcAdmin, sqsSettings, vcSettings.getTopic()); + return new TbAwsSqsProducerTemplate<>(vcAdmin, sqsSettings, topicService.buildTopicName(vcSettings.getTopic())); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbRuleEngineQueueFactory.java index 9b1f9dba0d..9df4cc5c4a 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbRuleEngineQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbRuleEngineQueueFactory.java @@ -33,7 +33,7 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.settings.TbQueueCoreSettings; import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; @@ -52,7 +52,7 @@ import java.nio.charset.StandardCharsets; @ConditionalOnExpression("'${queue.type:null}'=='aws-sqs' && '${service.type:null}'=='tb-rule-engine'") public class AwsSqsTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final TbQueueCoreSettings coreSettings; private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueRuleEngineSettings ruleEngineSettings; @@ -66,14 +66,14 @@ public class AwsSqsTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory private final TbQueueAdmin notificationAdmin; private final TbQueueAdmin otaAdmin; - public AwsSqsTbRuleEngineQueueFactory(NotificationsTopicService notificationsTopicService, TbQueueCoreSettings coreSettings, + public AwsSqsTbRuleEngineQueueFactory(TopicService topicService, TbQueueCoreSettings coreSettings, TbQueueRuleEngineSettings ruleEngineSettings, TbServiceInfoProvider serviceInfoProvider, TbAwsSqsSettings sqsSettings, TbAwsSqsQueueAttributes sqsQueueAttributes, TbQueueRemoteJsInvokeSettings jsInvokeSettings, TbQueueTransportNotificationSettings transportNotificationSettings) { - this.notificationsTopicService = notificationsTopicService; + this.topicService = topicService; this.coreSettings = coreSettings; this.serviceInfoProvider = serviceInfoProvider; this.ruleEngineSettings = ruleEngineSettings; @@ -90,39 +90,39 @@ public class AwsSqsTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory @Override public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, transportNotificationSettings.getNotificationsTopic()); + return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); } @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbAwsSqsProducerTemplate<>(ruleEngineAdmin, sqsSettings, ruleEngineSettings.getTopic()); + return new TbAwsSqsProducerTemplate<>(ruleEngineAdmin, sqsSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, ruleEngineSettings.getTopic()); + return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getTopic()); + return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, coreSettings.getTopic()); + return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration) { - return new TbAwsSqsConsumerTemplate<>(ruleEngineAdmin, sqsSettings, configuration.getTopic(), + return new TbAwsSqsConsumerTemplate<>(ruleEngineAdmin, sqsSettings, topicService.buildTopicName(configuration.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { return new TbAwsSqsConsumerTemplate<>(notificationAdmin, sqsSettings, - notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), + topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); } @@ -151,12 +151,12 @@ public class AwsSqsTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getUsageStatsTopic()); + return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(otaAdmin, sqsSettings, coreSettings.getOtaPackageTopic()); + return new TbAwsSqsProducerTemplate<>(otaAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbVersionControlQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbVersionControlQueueFactory.java index 24453d3c5c..a8f69e6eab 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbVersionControlQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbVersionControlQueueFactory.java @@ -22,6 +22,7 @@ import org.thingsboard.server.queue.TbQueueAdmin; import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.settings.TbQueueCoreSettings; import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings; import org.thingsboard.server.queue.sqs.TbAwsSqsAdmin; @@ -39,6 +40,7 @@ public class AwsSqsTbVersionControlQueueFactory implements TbVersionControlQueue private final TbAwsSqsSettings sqsSettings; private final TbQueueCoreSettings coreSettings; private final TbQueueVersionControlSettings vcSettings; + private final TopicService topicService; private final TbQueueAdmin coreAdmin; @@ -48,11 +50,13 @@ public class AwsSqsTbVersionControlQueueFactory implements TbVersionControlQueue public AwsSqsTbVersionControlQueueFactory(TbAwsSqsSettings sqsSettings, TbQueueCoreSettings coreSettings, TbQueueVersionControlSettings vcSettings, - TbAwsSqsQueueAttributes sqsQueueAttributes + TbAwsSqsQueueAttributes sqsQueueAttributes, + TopicService topicService ) { this.sqsSettings = sqsSettings; this.coreSettings = coreSettings; this.vcSettings = vcSettings; + this.topicService = topicService; this.coreAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getCoreAttributes()); this.notificationAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getNotificationsAttributes()); @@ -61,17 +65,17 @@ public class AwsSqsTbVersionControlQueueFactory implements TbVersionControlQueue @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getUsageStatsTopic()); + return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, coreSettings.getTopic()); + return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToVersionControlMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(vcAdmin, sqsSettings, vcSettings.getTopic(), + return new TbAwsSqsConsumerTemplate<>(vcAdmin, sqsSettings, topicService.buildTopicName(vcSettings.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()) ); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTransportQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTransportQueueFactory.java index c1ae9fe855..4818affc93 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTransportQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTransportQueueFactory.java @@ -20,6 +20,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; @@ -31,6 +32,7 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.settings.TbQueueCoreSettings; import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; @@ -53,6 +55,7 @@ public class AwsSqsTransportQueueFactory implements TbTransportQueueFactory { private final TbQueueCoreSettings coreSettings; private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueRuleEngineSettings ruleEngineSettings; + private final TopicService topicService; private final TbQueueAdmin coreAdmin; private final TbQueueAdmin transportApiAdmin; @@ -65,13 +68,15 @@ public class AwsSqsTransportQueueFactory implements TbTransportQueueFactory { TbServiceInfoProvider serviceInfoProvider, TbQueueCoreSettings coreSettings, TbAwsSqsQueueAttributes sqsQueueAttributes, - TbQueueRuleEngineSettings ruleEngineSettings) { + TbQueueRuleEngineSettings ruleEngineSettings, + TopicService topicService) { this.transportApiSettings = transportApiSettings; this.transportNotificationSettings = transportNotificationSettings; this.sqsSettings = sqsSettings; this.serviceInfoProvider = serviceInfoProvider; this.coreSettings = coreSettings; this.ruleEngineSettings = ruleEngineSettings; + this.topicService = topicService; this.coreAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getCoreAttributes()); this.transportApiAdmin = new TbAwsSqsAdmin(sqsSettings, sqsQueueAttributes.getTransportApiAttributes()); @@ -82,11 +87,11 @@ public class AwsSqsTransportQueueFactory implements TbTransportQueueFactory { @Override public TbQueueRequestTemplate, TbProtoQueueMsg> createTransportApiRequestTemplate() { TbQueueProducer> producerTemplate = - new TbAwsSqsProducerTemplate<>(transportApiAdmin, sqsSettings, transportApiSettings.getRequestsTopic()); + new TbAwsSqsProducerTemplate<>(transportApiAdmin, sqsSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic())); TbQueueConsumer> consumerTemplate = new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, - transportApiSettings.getResponsesTopic() + "_" + serviceInfoProvider.getServiceId(), + topicService.buildTopicName(transportApiSettings.getResponsesTopic() + "_" + serviceInfoProvider.getServiceId()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiResponseMsg.parseFrom(msg.getData()), msg.getHeaders())); DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder @@ -102,23 +107,28 @@ public class AwsSqsTransportQueueFactory implements TbTransportQueueFactory { @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbAwsSqsProducerTemplate<>(ruleEngineAdmin, sqsSettings, ruleEngineSettings.getTopic()); + return new TbAwsSqsProducerTemplate<>(ruleEngineAdmin, sqsSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getTopic()); + return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic())); + } + + @Override + public TbQueueProducer> createTbCoreNotificationsMsgProducer() { + return new TbAwsSqsProducerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createTransportNotificationsConsumer() { - return new TbAwsSqsConsumerTemplate<>(notificationAdmin, sqsSettings, transportNotificationSettings.getNotificationsTopic() + "_" + serviceInfoProvider.getServiceId(), + return new TbAwsSqsConsumerTemplate<>(notificationAdmin, sqsSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic() + "_" + serviceInfoProvider.getServiceId()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToTransportMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getUsageStatsTopic()); + return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java index 87fc8aa3db..933daac0df 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java @@ -28,7 +28,7 @@ import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.memory.InMemoryStorage; import org.thingsboard.server.queue.memory.InMemoryTbQueueConsumer; @@ -44,7 +44,7 @@ import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings; @ConditionalOnExpression("'${queue.type:null}'=='in-memory' && '${service.type:null}'=='monolith'") public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngineQueueFactory, TbVersionControlQueueFactory { - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final TbQueueCoreSettings coreSettings; private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueRuleEngineSettings ruleEngineSettings; @@ -53,14 +53,14 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE private final TbQueueTransportNotificationSettings transportNotificationSettings; private final InMemoryStorage storage; - public InMemoryMonolithQueueFactory(NotificationsTopicService notificationsTopicService, TbQueueCoreSettings coreSettings, + public InMemoryMonolithQueueFactory(TopicService topicService, TbQueueCoreSettings coreSettings, TbQueueRuleEngineSettings ruleEngineSettings, TbQueueVersionControlSettings vcSettings, TbServiceInfoProvider serviceInfoProvider, TbQueueTransportApiSettings transportApiSettings, TbQueueTransportNotificationSettings transportNotificationSettings, InMemoryStorage storage) { - this.notificationsTopicService = notificationsTopicService; + this.topicService = topicService; this.coreSettings = coreSettings; this.vcSettings = vcSettings; this.serviceInfoProvider = serviceInfoProvider; @@ -72,62 +72,62 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE @Override public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new InMemoryTbQueueProducer<>(storage, transportNotificationSettings.getNotificationsTopic()); + return new InMemoryTbQueueProducer<>(storage, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); } @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new InMemoryTbQueueProducer<>(storage, ruleEngineSettings.getTopic()); + return new InMemoryTbQueueProducer<>(storage, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new InMemoryTbQueueProducer<>(storage, ruleEngineSettings.getTopic()); + return new InMemoryTbQueueProducer<>(storage, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new InMemoryTbQueueProducer<>(storage, coreSettings.getTopic()); + return new InMemoryTbQueueProducer<>(storage, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new InMemoryTbQueueProducer<>(storage, coreSettings.getTopic()); + return new InMemoryTbQueueProducer<>(storage, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToVersionControlMsgConsumer() { - return new InMemoryTbQueueConsumer<>(storage, vcSettings.getTopic()); + return new InMemoryTbQueueConsumer<>(storage, topicService.buildTopicName(vcSettings.getTopic())); } @Override public TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration) { - return new InMemoryTbQueueConsumer<>(storage, configuration.getTopic()); + return new InMemoryTbQueueConsumer<>(storage, topicService.buildTopicName(configuration.getTopic())); } @Override public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { - return new InMemoryTbQueueConsumer<>(storage, notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName()); + return new InMemoryTbQueueConsumer<>(storage, topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName()); } @Override public TbQueueConsumer> createToCoreMsgConsumer() { - return new InMemoryTbQueueConsumer<>(storage, coreSettings.getTopic()); + return new InMemoryTbQueueConsumer<>(storage, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { - return new InMemoryTbQueueConsumer<>(storage, notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName()); + return new InMemoryTbQueueConsumer<>(storage, topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName()); } @Override public TbQueueConsumer> createTransportApiRequestConsumer() { - return new InMemoryTbQueueConsumer<>(storage, transportApiSettings.getRequestsTopic()); + return new InMemoryTbQueueConsumer<>(storage, topicService.buildTopicName(transportApiSettings.getRequestsTopic())); } @Override public TbQueueProducer> createTransportApiResponseProducer() { - return new InMemoryTbQueueProducer<>(storage, transportApiSettings.getResponsesTopic()); + return new InMemoryTbQueueProducer<>(storage, topicService.buildTopicName(transportApiSettings.getResponsesTopic())); } @Override @@ -137,27 +137,27 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE @Override public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { - return new InMemoryTbQueueConsumer<>(storage, coreSettings.getUsageStatsTopic()); + return new InMemoryTbQueueConsumer<>(storage, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @Override public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { - return new InMemoryTbQueueConsumer<>(storage, coreSettings.getOtaPackageTopic()); + return new InMemoryTbQueueConsumer<>(storage, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new InMemoryTbQueueProducer<>(storage, coreSettings.getOtaPackageTopic()); + return new InMemoryTbQueueProducer<>(storage, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); } @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new InMemoryTbQueueProducer<>(storage, coreSettings.getUsageStatsTopic()); + return new InMemoryTbQueueProducer<>(storage, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @Override public TbQueueProducer> createVersionControlMsgProducer() { - return new InMemoryTbQueueProducer<>(storage, vcSettings.getTopic()); + return new InMemoryTbQueueProducer<>(storage, topicService.buildTopicName(vcSettings.getTopic())); } @Scheduled(fixedRateString = "${queue.in_memory.stats.print-interval-ms:60000}") diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryTbTransportQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryTbTransportQueueFactory.java index 60c464ecab..f578079a4f 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryTbTransportQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryTbTransportQueueFactory.java @@ -20,6 +20,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; @@ -31,6 +32,7 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.memory.InMemoryStorage; import org.thingsboard.server.queue.memory.InMemoryTbQueueConsumer; import org.thingsboard.server.queue.memory.InMemoryTbQueueProducer; @@ -47,33 +49,36 @@ public class InMemoryTbTransportQueueFactory implements TbTransportQueueFactory private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueCoreSettings coreSettings; private final InMemoryStorage storage; + private final TopicService topicService; public InMemoryTbTransportQueueFactory(TbQueueTransportApiSettings transportApiSettings, TbQueueTransportNotificationSettings transportNotificationSettings, TbServiceInfoProvider serviceInfoProvider, TbQueueCoreSettings coreSettings, - InMemoryStorage storage) { + InMemoryStorage storage, + TopicService topicService) { this.transportApiSettings = transportApiSettings; this.transportNotificationSettings = transportNotificationSettings; this.serviceInfoProvider = serviceInfoProvider; this.coreSettings = coreSettings; this.storage = storage; + this.topicService = topicService; } @Override public TbQueueRequestTemplate, TbProtoQueueMsg> createTransportApiRequestTemplate() { InMemoryTbQueueProducer> producerTemplate = - new InMemoryTbQueueProducer<>(storage, transportApiSettings.getRequestsTopic()); + new InMemoryTbQueueProducer<>(storage, topicService.buildTopicName(transportApiSettings.getRequestsTopic())); InMemoryTbQueueConsumer> consumerTemplate = - new InMemoryTbQueueConsumer<>(storage, transportApiSettings.getResponsesTopic() + "." + serviceInfoProvider.getServiceId()); + new InMemoryTbQueueConsumer<>(storage, topicService.buildTopicName(transportApiSettings.getResponsesTopic() + "." + serviceInfoProvider.getServiceId())); DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder , TbProtoQueueMsg> templateBuilder = DefaultTbQueueRequestTemplate.builder(); templateBuilder.queueAdmin(new TbQueueAdmin() { @Override - public void createTopicIfNotExists(String topic) {} + public void createTopicIfNotExists(String topic, String properties) {} @Override public void destroy() {} @@ -92,22 +97,27 @@ public class InMemoryTbTransportQueueFactory implements TbTransportQueueFactory @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new InMemoryTbQueueProducer<>(storage, transportApiSettings.getRequestsTopic()); + return new InMemoryTbQueueProducer<>(storage, topicService.buildTopicName(transportApiSettings.getRequestsTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new InMemoryTbQueueProducer<>(storage, coreSettings.getTopic()); + return new InMemoryTbQueueProducer<>(storage, topicService.buildTopicName(coreSettings.getTopic())); + } + + @Override + public TbQueueProducer> createTbCoreNotificationsMsgProducer() { + return new InMemoryTbQueueProducer<>(storage, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createTransportNotificationsConsumer() { - return new InMemoryTbQueueConsumer<>(storage, transportNotificationSettings.getNotificationsTopic() + "." + serviceInfoProvider.getServiceId()); + return new InMemoryTbQueueConsumer<>(storage, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic() + "." + serviceInfoProvider.getServiceId())); } @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new InMemoryTbQueueProducer<>(storage, coreSettings.getUsageStatsTopic()); + return new InMemoryTbQueueProducer<>(storage, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java index 364abcff4c..0e3721d215 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java @@ -39,7 +39,7 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.kafka.TbKafkaAdmin; import org.thingsboard.server.queue.kafka.TbKafkaConsumerStatsService; @@ -62,7 +62,7 @@ import java.util.concurrent.atomic.AtomicLong; @ConditionalOnExpression("'${queue.type:null}'=='kafka' && '${service.type:null}'=='monolith'") public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngineQueueFactory, TbVersionControlQueueFactory { - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final TbKafkaSettings kafkaSettings; private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueCoreSettings coreSettings; @@ -85,7 +85,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi private final AtomicLong consumerCount = new AtomicLong(); - public KafkaMonolithQueueFactory(NotificationsTopicService notificationsTopicService, TbKafkaSettings kafkaSettings, + public KafkaMonolithQueueFactory(TopicService topicService, TbKafkaSettings kafkaSettings, TbServiceInfoProvider serviceInfoProvider, TbQueueCoreSettings coreSettings, TbQueueRuleEngineSettings ruleEngineSettings, @@ -95,7 +95,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi TbQueueVersionControlSettings vcSettings, TbKafkaConsumerStatsService consumerStatsService, TbKafkaTopicConfigs kafkaTopicConfigs) { - this.notificationsTopicService = notificationsTopicService; + this.topicService = topicService; this.kafkaSettings = kafkaSettings; this.serviceInfoProvider = serviceInfoProvider; this.coreSettings = coreSettings; @@ -122,7 +122,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("monolith-transport-notifications-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(transportNotificationSettings.getNotificationsTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); requestBuilder.admin(notificationAdmin); return requestBuilder.build(); } @@ -132,7 +132,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("monolith-rule-engine-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(ruleEngineSettings.getTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(ruleEngineSettings.getTopic())); requestBuilder.admin(ruleEngineAdmin); return requestBuilder.build(); } @@ -142,7 +142,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("monolith-rule-engine-notifications-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(ruleEngineSettings.getTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(ruleEngineSettings.getTopic())); requestBuilder.admin(notificationAdmin); return requestBuilder.build(); } @@ -152,7 +152,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("monolith-core-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(coreSettings.getTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(coreSettings.getTopic())); requestBuilder.admin(coreAdmin); return requestBuilder.build(); } @@ -162,7 +162,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("monolith-core-notifications-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(coreSettings.getTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(coreSettings.getTopic())); requestBuilder.admin(notificationAdmin); return requestBuilder.build(); } @@ -171,9 +171,9 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi public TbQueueConsumer> createToVersionControlMsgConsumer() { TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(vcSettings.getTopic()); + consumerBuilder.topic(topicService.buildTopicName(vcSettings.getTopic())); consumerBuilder.clientId("monolith-vc-consumer-" + serviceInfoProvider.getServiceId()); - consumerBuilder.groupId("monolith-vc-node"); + consumerBuilder.groupId(topicService.buildTopicName("monolith-vc-node")); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(vcAdmin); consumerBuilder.statsService(consumerStatsService); @@ -185,9 +185,9 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi String queueName = configuration.getName(); TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(configuration.getTopic()); + consumerBuilder.topic(topicService.buildTopicName(configuration.getTopic())); consumerBuilder.clientId("re-" + queueName + "-consumer-" + serviceInfoProvider.getServiceId() + "-" + consumerCount.incrementAndGet()); - consumerBuilder.groupId("re-" + queueName + "-consumer"); + consumerBuilder.groupId(topicService.buildTopicName("re-" + queueName + (configuration.getTenantId().isSysTenantId() ? "" : ("-isolated-" + configuration.getTenantId())) + "-consumer")); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(ruleEngineAdmin); consumerBuilder.statsService(consumerStatsService); @@ -198,9 +198,9 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName()); + consumerBuilder.topic(topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName()); consumerBuilder.clientId("monolith-rule-engine-notifications-consumer-" + serviceInfoProvider.getServiceId()); - consumerBuilder.groupId("monolith-rule-engine-notifications-consumer-" + serviceInfoProvider.getServiceId()); + consumerBuilder.groupId(topicService.buildTopicName("monolith-rule-engine-notifications-consumer-" + serviceInfoProvider.getServiceId())); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(notificationAdmin); consumerBuilder.statsService(consumerStatsService); @@ -211,9 +211,9 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi public TbQueueConsumer> createToCoreMsgConsumer() { TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(coreSettings.getTopic()); + consumerBuilder.topic(topicService.buildTopicName(coreSettings.getTopic())); consumerBuilder.clientId("monolith-core-consumer-" + serviceInfoProvider.getServiceId()); - consumerBuilder.groupId("monolith-core-consumer"); + consumerBuilder.groupId(topicService.buildTopicName("monolith-core-consumer")); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(coreAdmin); consumerBuilder.statsService(consumerStatsService); @@ -224,9 +224,9 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName()); + consumerBuilder.topic(topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName()); consumerBuilder.clientId("monolith-core-notifications-consumer-" + serviceInfoProvider.getServiceId()); - consumerBuilder.groupId("monolith-core-notifications-consumer-" + serviceInfoProvider.getServiceId()); + consumerBuilder.groupId(topicService.buildTopicName("monolith-core-notifications-consumer-" + serviceInfoProvider.getServiceId())); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(notificationAdmin); consumerBuilder.statsService(consumerStatsService); @@ -237,9 +237,9 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi public TbQueueConsumer> createTransportApiRequestConsumer() { TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(transportApiSettings.getRequestsTopic()); + consumerBuilder.topic(topicService.buildTopicName(transportApiSettings.getRequestsTopic())); consumerBuilder.clientId("monolith-transport-api-consumer-" + serviceInfoProvider.getServiceId()); - consumerBuilder.groupId("monolith-transport-api-consumer"); + consumerBuilder.groupId(topicService.buildTopicName("monolith-transport-api-consumer")); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(transportApiRequestAdmin); consumerBuilder.statsService(consumerStatsService); @@ -251,7 +251,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("monolith-transport-api-producer-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(transportApiSettings.getResponsesTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(transportApiSettings.getResponsesTopic())); requestBuilder.admin(transportApiResponseAdmin); return requestBuilder.build(); } @@ -294,9 +294,9 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(coreSettings.getUsageStatsTopic()); + consumerBuilder.topic(topicService.buildTopicName(coreSettings.getUsageStatsTopic())); consumerBuilder.clientId("monolith-us-consumer-" + serviceInfoProvider.getServiceId()); - consumerBuilder.groupId("monolith-us-consumer"); + consumerBuilder.groupId(topicService.buildTopicName("monolith-us-consumer")); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(coreAdmin); consumerBuilder.statsService(consumerStatsService); @@ -307,9 +307,9 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(coreSettings.getOtaPackageTopic()); + consumerBuilder.topic(topicService.buildTopicName(coreSettings.getOtaPackageTopic())); consumerBuilder.clientId("monolith-ota-consumer-" + serviceInfoProvider.getServiceId()); - consumerBuilder.groupId("monolith-ota-consumer"); + consumerBuilder.groupId(topicService.buildTopicName("monolith-ota-consumer")); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(fwUpdatesAdmin); consumerBuilder.statsService(consumerStatsService); @@ -321,7 +321,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("monolith-ota-producer-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(coreSettings.getOtaPackageTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(coreSettings.getOtaPackageTopic())); requestBuilder.admin(fwUpdatesAdmin); return requestBuilder.build(); } @@ -331,7 +331,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("monolith-us-producer-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(coreSettings.getUsageStatsTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(coreSettings.getUsageStatsTopic())); requestBuilder.admin(coreAdmin); return requestBuilder.build(); } @@ -341,7 +341,7 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("monolith-vc-producer-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(vcSettings.getTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(vcSettings.getTopic())); requestBuilder.admin(vcAdmin); return requestBuilder.build(); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java index 9c25a5d420..09dd7d8820 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java @@ -38,7 +38,7 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.kafka.TbKafkaAdmin; import org.thingsboard.server.queue.kafka.TbKafkaConsumerStatsService; @@ -60,7 +60,7 @@ import java.nio.charset.StandardCharsets; @ConditionalOnExpression("'${queue.type:null}'=='kafka' && '${service.type:null}'=='tb-core'") public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final TbKafkaSettings kafkaSettings; private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueCoreSettings coreSettings; @@ -81,7 +81,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { private final TbQueueAdmin fwUpdatesAdmin; private final TbQueueAdmin vcAdmin; - public KafkaTbCoreQueueFactory(NotificationsTopicService notificationsTopicService, + public KafkaTbCoreQueueFactory(TopicService topicService, TbKafkaSettings kafkaSettings, TbServiceInfoProvider serviceInfoProvider, TbQueueCoreSettings coreSettings, @@ -92,7 +92,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { TbKafkaConsumerStatsService consumerStatsService, TbQueueTransportNotificationSettings transportNotificationSettings, TbKafkaTopicConfigs kafkaTopicConfigs) { - this.notificationsTopicService = notificationsTopicService; + this.topicService = topicService; this.kafkaSettings = kafkaSettings; this.serviceInfoProvider = serviceInfoProvider; this.coreSettings = coreSettings; @@ -119,7 +119,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("tb-core-transport-notifications-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(transportNotificationSettings.getNotificationsTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); requestBuilder.admin(notificationAdmin); return requestBuilder.build(); } @@ -129,7 +129,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("tb-core-rule-engine-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(coreSettings.getTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(coreSettings.getTopic())); requestBuilder.admin(coreAdmin); return requestBuilder.build(); } @@ -139,7 +139,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("tb-core-rule-engine-notifications-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(ruleEngineSettings.getTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(ruleEngineSettings.getTopic())); requestBuilder.admin(notificationAdmin); return requestBuilder.build(); } @@ -149,7 +149,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("tb-core-to-core-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(coreSettings.getTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(coreSettings.getTopic())); requestBuilder.admin(coreAdmin); return requestBuilder.build(); } @@ -159,7 +159,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("tb-core-to-core-notifications-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(coreSettings.getTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(coreSettings.getTopic())); requestBuilder.admin(notificationAdmin); return requestBuilder.build(); } @@ -168,9 +168,9 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { public TbQueueConsumer> createToCoreMsgConsumer() { TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(coreSettings.getTopic()); + consumerBuilder.topic(topicService.buildTopicName(coreSettings.getTopic())); consumerBuilder.clientId("tb-core-consumer-" + serviceInfoProvider.getServiceId()); - consumerBuilder.groupId("tb-core-node"); + consumerBuilder.groupId(topicService.buildTopicName("tb-core-node")); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(coreAdmin); consumerBuilder.statsService(consumerStatsService); @@ -181,9 +181,9 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName()); + consumerBuilder.topic(topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName()); consumerBuilder.clientId("tb-core-notifications-consumer-" + serviceInfoProvider.getServiceId()); - consumerBuilder.groupId("tb-core-notifications-node-" + serviceInfoProvider.getServiceId()); + consumerBuilder.groupId(topicService.buildTopicName("tb-core-notifications-node-" + serviceInfoProvider.getServiceId())); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(notificationAdmin); consumerBuilder.statsService(consumerStatsService); @@ -194,9 +194,9 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { public TbQueueConsumer> createTransportApiRequestConsumer() { TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(transportApiSettings.getRequestsTopic()); + consumerBuilder.topic(topicService.buildTopicName(transportApiSettings.getRequestsTopic())); consumerBuilder.clientId("tb-core-transport-api-consumer-" + serviceInfoProvider.getServiceId()); - consumerBuilder.groupId("tb-core-transport-api-consumer"); + consumerBuilder.groupId(topicService.buildTopicName("tb-core-transport-api-consumer")); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(transportApiRequestAdmin); consumerBuilder.statsService(consumerStatsService); @@ -208,7 +208,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("tb-core-transport-api-producer-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(transportApiSettings.getResponsesTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(transportApiSettings.getResponsesTopic())); requestBuilder.admin(transportApiResponseAdmin); return requestBuilder.build(); } @@ -251,9 +251,9 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(coreSettings.getUsageStatsTopic()); + consumerBuilder.topic(topicService.buildTopicName(coreSettings.getUsageStatsTopic())); consumerBuilder.clientId("tb-core-us-consumer-" + serviceInfoProvider.getServiceId()); - consumerBuilder.groupId("tb-core-us-consumer"); + consumerBuilder.groupId(topicService.buildTopicName("tb-core-us-consumer")); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(coreAdmin); consumerBuilder.statsService(consumerStatsService); @@ -264,9 +264,9 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(coreSettings.getOtaPackageTopic()); + consumerBuilder.topic(topicService.buildTopicName(coreSettings.getOtaPackageTopic())); consumerBuilder.clientId("tb-core-ota-consumer-" + serviceInfoProvider.getServiceId()); - consumerBuilder.groupId("tb-core-ota-consumer"); + consumerBuilder.groupId(topicService.buildTopicName("tb-core-ota-consumer")); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(fwUpdatesAdmin); consumerBuilder.statsService(consumerStatsService); @@ -278,7 +278,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("tb-core-ota-producer-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(coreSettings.getOtaPackageTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(coreSettings.getOtaPackageTopic())); requestBuilder.admin(fwUpdatesAdmin); return requestBuilder.build(); } @@ -288,7 +288,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("tb-core-us-producer-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(coreSettings.getUsageStatsTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(coreSettings.getUsageStatsTopic())); requestBuilder.admin(coreAdmin); return requestBuilder.build(); } @@ -298,7 +298,7 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("tb-core-vc-producer-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(vcSettings.getTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(vcSettings.getTopic())); requestBuilder.admin(vcAdmin); return requestBuilder.build(); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java index b8e07a45f7..2baaf08640 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java @@ -36,7 +36,7 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.kafka.TbKafkaAdmin; import org.thingsboard.server.queue.kafka.TbKafkaConsumerStatsService; @@ -57,7 +57,7 @@ import java.util.concurrent.atomic.AtomicLong; @ConditionalOnExpression("'${queue.type:null}'=='kafka' && '${service.type:null}'=='tb-rule-engine'") public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final TbKafkaSettings kafkaSettings; private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueCoreSettings coreSettings; @@ -74,7 +74,7 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { private final TbQueueAdmin fwUpdatesAdmin; private final AtomicLong consumerCount = new AtomicLong(); - public KafkaTbRuleEngineQueueFactory(NotificationsTopicService notificationsTopicService, TbKafkaSettings kafkaSettings, + public KafkaTbRuleEngineQueueFactory(TopicService topicService, TbKafkaSettings kafkaSettings, TbServiceInfoProvider serviceInfoProvider, TbQueueCoreSettings coreSettings, TbQueueRuleEngineSettings ruleEngineSettings, @@ -82,7 +82,7 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { TbKafkaConsumerStatsService consumerStatsService, TbQueueTransportNotificationSettings transportNotificationSettings, TbKafkaTopicConfigs kafkaTopicConfigs) { - this.notificationsTopicService = notificationsTopicService; + this.topicService = topicService; this.kafkaSettings = kafkaSettings; this.serviceInfoProvider = serviceInfoProvider; this.coreSettings = coreSettings; @@ -104,7 +104,7 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("tb-rule-engine-transport-notifications-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(transportNotificationSettings.getNotificationsTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); requestBuilder.admin(notificationAdmin); return requestBuilder.build(); } @@ -114,7 +114,7 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("tb-rule-engine-to-rule-engine-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(ruleEngineSettings.getTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(ruleEngineSettings.getTopic())); requestBuilder.admin(ruleEngineAdmin); return requestBuilder.build(); } @@ -124,7 +124,7 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("tb-rule-engine-to-rule-engine-notifications-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(ruleEngineSettings.getTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(ruleEngineSettings.getTopic())); requestBuilder.admin(notificationAdmin); return requestBuilder.build(); } @@ -134,7 +134,7 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("tb-rule-engine-to-core-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(coreSettings.getTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(coreSettings.getTopic())); requestBuilder.admin(coreAdmin); return requestBuilder.build(); } @@ -144,7 +144,7 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("tb-rule-engine-ota-producer-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(coreSettings.getOtaPackageTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(coreSettings.getOtaPackageTopic())); requestBuilder.admin(fwUpdatesAdmin); return requestBuilder.build(); } @@ -154,7 +154,7 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("tb-rule-engine-to-core-notifications-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(coreSettings.getTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(coreSettings.getTopic())); requestBuilder.admin(notificationAdmin); return requestBuilder.build(); } @@ -164,9 +164,9 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { String queueName = configuration.getName(); TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(configuration.getTopic()); + consumerBuilder.topic(topicService.buildTopicName(configuration.getTopic())); consumerBuilder.clientId("re-" + queueName + "-consumer-" + serviceInfoProvider.getServiceId() + "-" + consumerCount.incrementAndGet()); - consumerBuilder.groupId("re-" + queueName + "-consumer"); + consumerBuilder.groupId(topicService.buildTopicName("re-" + queueName + (configuration.getTenantId().isSysTenantId() ? "" : ("-isolated-" + configuration.getTenantId())) + "-consumer")); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(ruleEngineAdmin); consumerBuilder.statsService(consumerStatsService); @@ -177,7 +177,7 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName()); + consumerBuilder.topic(topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName()); consumerBuilder.clientId("tb-rule-engine-notifications-consumer-" + serviceInfoProvider.getServiceId()); consumerBuilder.groupId("tb-rule-engine-notifications-node-" + serviceInfoProvider.getServiceId()); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); @@ -225,7 +225,7 @@ public class KafkaTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("tb-rule-engine-us-producer-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(coreSettings.getUsageStatsTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(coreSettings.getUsageStatsTopic())); requestBuilder.admin(coreAdmin); return requestBuilder.build(); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbTransportQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbTransportQueueFactory.java index 51e3dc99c7..11c038a2bc 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbTransportQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbTransportQueueFactory.java @@ -18,7 +18,9 @@ package org.thingsboard.server.queue.provider; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; @@ -31,6 +33,7 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.kafka.TbKafkaAdmin; import org.thingsboard.server.queue.kafka.TbKafkaConsumerStatsService; import org.thingsboard.server.queue.kafka.TbKafkaConsumerTemplate; @@ -56,6 +59,7 @@ public class KafkaTbTransportQueueFactory implements TbTransportQueueFactory { private final TbQueueTransportApiSettings transportApiSettings; private final TbQueueTransportNotificationSettings transportNotificationSettings; private final TbKafkaConsumerStatsService consumerStatsService; + private final TopicService topicService; private final TbQueueAdmin coreAdmin; private final TbQueueAdmin ruleEngineAdmin; @@ -70,7 +74,8 @@ public class KafkaTbTransportQueueFactory implements TbTransportQueueFactory { TbQueueTransportApiSettings transportApiSettings, TbQueueTransportNotificationSettings transportNotificationSettings, TbKafkaConsumerStatsService consumerStatsService, - TbKafkaTopicConfigs kafkaTopicConfigs) { + TbKafkaTopicConfigs kafkaTopicConfigs, + TopicService topicService) { this.kafkaSettings = kafkaSettings; this.serviceInfoProvider = serviceInfoProvider; this.coreSettings = coreSettings; @@ -78,6 +83,7 @@ public class KafkaTbTransportQueueFactory implements TbTransportQueueFactory { this.transportApiSettings = transportApiSettings; this.transportNotificationSettings = transportNotificationSettings; this.consumerStatsService = consumerStatsService; + this.topicService = topicService; this.coreAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getCoreConfigs()); this.ruleEngineAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getRuleEngineConfigs()); @@ -91,14 +97,14 @@ public class KafkaTbTransportQueueFactory implements TbTransportQueueFactory { TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("transport-api-request-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(transportApiSettings.getRequestsTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(transportApiSettings.getRequestsTopic())); requestBuilder.admin(transportApiRequestAdmin); TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> responseBuilder = TbKafkaConsumerTemplate.builder(); responseBuilder.settings(kafkaSettings); - responseBuilder.topic(transportApiSettings.getResponsesTopic() + "." + serviceInfoProvider.getServiceId()); + responseBuilder.topic(topicService.buildTopicName(transportApiSettings.getResponsesTopic() + "." + serviceInfoProvider.getServiceId())); responseBuilder.clientId("transport-api-response-" + serviceInfoProvider.getServiceId()); - responseBuilder.groupId("transport-node-" + serviceInfoProvider.getServiceId()); + responseBuilder.groupId(topicService.buildTopicName("transport-node-" + serviceInfoProvider.getServiceId())); responseBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiResponseMsg.parseFrom(msg.getData()), msg.getHeaders())); responseBuilder.admin(transportApiResponseAdmin); responseBuilder.statsService(consumerStatsService); @@ -119,7 +125,7 @@ public class KafkaTbTransportQueueFactory implements TbTransportQueueFactory { TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("transport-node-rule-engine-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(ruleEngineSettings.getTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(ruleEngineSettings.getTopic())); requestBuilder.admin(ruleEngineAdmin); return requestBuilder.build(); } @@ -129,18 +135,28 @@ public class KafkaTbTransportQueueFactory implements TbTransportQueueFactory { TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("transport-node-core-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(coreSettings.getTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(coreSettings.getTopic())); requestBuilder.admin(coreAdmin); return requestBuilder.build(); } + @Override + public TbQueueProducer> createTbCoreNotificationsMsgProducer() { + TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); + requestBuilder.settings(kafkaSettings); + requestBuilder.clientId("transport-node-to-core-notifications-" + serviceInfoProvider.getServiceId()); + requestBuilder.defaultTopic(topicService.buildTopicName(coreSettings.getTopic())); + requestBuilder.admin(notificationAdmin); + return requestBuilder.build(); + } + @Override public TbQueueConsumer> createTransportNotificationsConsumer() { TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> responseBuilder = TbKafkaConsumerTemplate.builder(); responseBuilder.settings(kafkaSettings); - responseBuilder.topic(transportNotificationSettings.getNotificationsTopic() + "." + serviceInfoProvider.getServiceId()); + responseBuilder.topic(topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic() + "." + serviceInfoProvider.getServiceId())); responseBuilder.clientId("transport-api-notifications-" + serviceInfoProvider.getServiceId()); - responseBuilder.groupId("transport-node-" + serviceInfoProvider.getServiceId()); + responseBuilder.groupId(topicService.buildTopicName("transport-node-" + serviceInfoProvider.getServiceId())); responseBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToTransportMsg.parseFrom(msg.getData()), msg.getHeaders())); responseBuilder.admin(notificationAdmin); responseBuilder.statsService(consumerStatsService); @@ -152,7 +168,7 @@ public class KafkaTbTransportQueueFactory implements TbTransportQueueFactory { TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("transport-node-us-producer-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(coreSettings.getUsageStatsTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(coreSettings.getUsageStatsTopic())); requestBuilder.admin(coreAdmin); return requestBuilder.build(); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbVersionControlQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbVersionControlQueueFactory.java index d851956420..952c9817a3 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbVersionControlQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbVersionControlQueueFactory.java @@ -25,6 +25,7 @@ import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.kafka.TbKafkaAdmin; import org.thingsboard.server.queue.kafka.TbKafkaConsumerStatsService; import org.thingsboard.server.queue.kafka.TbKafkaConsumerTemplate; @@ -45,6 +46,7 @@ public class KafkaTbVersionControlQueueFactory implements TbVersionControlQueueF private final TbQueueCoreSettings coreSettings; private final TbQueueVersionControlSettings vcSettings; private final TbKafkaConsumerStatsService consumerStatsService; + private final TopicService topicService; private final TbQueueAdmin coreAdmin; private final TbQueueAdmin vcAdmin; @@ -55,12 +57,14 @@ public class KafkaTbVersionControlQueueFactory implements TbVersionControlQueueF TbQueueCoreSettings coreSettings, TbQueueVersionControlSettings vcSettings, TbKafkaConsumerStatsService consumerStatsService, - TbKafkaTopicConfigs kafkaTopicConfigs) { + TbKafkaTopicConfigs kafkaTopicConfigs, + TopicService topicService) { this.kafkaSettings = kafkaSettings; this.serviceInfoProvider = serviceInfoProvider; this.coreSettings = coreSettings; this.vcSettings = vcSettings; this.consumerStatsService = consumerStatsService; + this.topicService = topicService; this.coreAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getCoreConfigs()); this.vcAdmin = new TbKafkaAdmin(kafkaSettings, kafkaTopicConfigs.getVcConfigs()); @@ -73,7 +77,7 @@ public class KafkaTbVersionControlQueueFactory implements TbVersionControlQueueF TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("tb-vc-to-core-notifications-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(coreSettings.getTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(coreSettings.getTopic())); requestBuilder.admin(notificationAdmin); return requestBuilder.build(); } @@ -82,9 +86,9 @@ public class KafkaTbVersionControlQueueFactory implements TbVersionControlQueueF public TbQueueConsumer> createToVersionControlMsgConsumer() { TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(vcSettings.getTopic()); + consumerBuilder.topic(topicService.buildTopicName(vcSettings.getTopic())); consumerBuilder.clientId("tb-vc-consumer-" + serviceInfoProvider.getServiceId()); - consumerBuilder.groupId("tb-vc-node"); + consumerBuilder.groupId(topicService.buildTopicName("tb-vc-node")); consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(vcAdmin); consumerBuilder.statsService(consumerStatsService); @@ -96,7 +100,7 @@ public class KafkaTbVersionControlQueueFactory implements TbVersionControlQueueF TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); requestBuilder.clientId("tb-vc-us-producer-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(coreSettings.getUsageStatsTopic()); + requestBuilder.defaultTopic(topicService.buildTopicName(coreSettings.getUsageStatsTopic())); requestBuilder.admin(coreAdmin); return requestBuilder.build(); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java index 857641098c..554d452556 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java @@ -40,7 +40,7 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.pubsub.TbPubSubAdmin; import org.thingsboard.server.queue.pubsub.TbPubSubConsumerTemplate; @@ -66,7 +66,7 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng private final TbQueueRuleEngineSettings ruleEngineSettings; private final TbQueueTransportApiSettings transportApiSettings; private final TbQueueTransportNotificationSettings transportNotificationSettings; - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; private final TbQueueVersionControlSettings vcSettings; @@ -83,7 +83,7 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng TbQueueRuleEngineSettings ruleEngineSettings, TbQueueTransportApiSettings transportApiSettings, TbQueueTransportNotificationSettings transportNotificationSettings, - NotificationsTopicService notificationsTopicService, + TopicService topicService, TbServiceInfoProvider serviceInfoProvider, TbPubSubSubscriptionSettings pubSubSubscriptionSettings, TbQueueRemoteJsInvokeSettings jsInvokeSettings, @@ -93,7 +93,7 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng this.ruleEngineSettings = ruleEngineSettings; this.transportApiSettings = transportApiSettings; this.transportNotificationSettings = transportNotificationSettings; - this.notificationsTopicService = notificationsTopicService; + this.topicService = topicService; this.serviceInfoProvider = serviceInfoProvider; this.vcSettings = vcSettings; @@ -149,7 +149,7 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng @Override public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, - notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), + topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); } @@ -162,7 +162,7 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng @Override public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, - notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), + topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java index f197082829..3b45992e25 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java @@ -38,7 +38,7 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.pubsub.TbPubSubAdmin; import org.thingsboard.server.queue.pubsub.TbPubSubConsumerTemplate; @@ -61,7 +61,7 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { private final TbPubSubSettings pubSubSettings; private final TbQueueCoreSettings coreSettings; private final TbQueueTransportApiSettings transportApiSettings; - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; private final TbQueueTransportNotificationSettings transportNotificationSettings; @@ -76,7 +76,7 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { public PubSubTbCoreQueueFactory(TbPubSubSettings pubSubSettings, TbQueueCoreSettings coreSettings, TbQueueTransportApiSettings transportApiSettings, - NotificationsTopicService notificationsTopicService, + TopicService topicService, TbServiceInfoProvider serviceInfoProvider, TbQueueRemoteJsInvokeSettings jsInvokeSettings, TbQueueTransportNotificationSettings transportNotificationSettings, @@ -85,7 +85,7 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { this.pubSubSettings = pubSubSettings; this.coreSettings = coreSettings; this.transportApiSettings = transportApiSettings; - this.notificationsTopicService = notificationsTopicService; + this.topicService = topicService; this.serviceInfoProvider = serviceInfoProvider; this.jsInvokeSettings = jsInvokeSettings; this.transportNotificationSettings = transportNotificationSettings; @@ -132,7 +132,7 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { @Override public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, - notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), + topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbRuleEngineQueueFactory.java index 9766bf9915..a60f77852f 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbRuleEngineQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbRuleEngineQueueFactory.java @@ -36,7 +36,7 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.pubsub.TbPubSubAdmin; import org.thingsboard.server.queue.pubsub.TbPubSubConsumerTemplate; @@ -58,7 +58,7 @@ public class PubSubTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory private final TbPubSubSettings pubSubSettings; private final TbQueueCoreSettings coreSettings; private final TbQueueRuleEngineSettings ruleEngineSettings; - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; private final TbQueueTransportNotificationSettings transportNotificationSettings; @@ -71,7 +71,7 @@ public class PubSubTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory public PubSubTbRuleEngineQueueFactory(TbPubSubSettings pubSubSettings, TbQueueCoreSettings coreSettings, TbQueueRuleEngineSettings ruleEngineSettings, - NotificationsTopicService notificationsTopicService, + TopicService topicService, TbServiceInfoProvider serviceInfoProvider, TbQueueRemoteJsInvokeSettings jsInvokeSettings, TbQueueTransportNotificationSettings transportNotificationSettings, @@ -79,7 +79,7 @@ public class PubSubTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory this.pubSubSettings = pubSubSettings; this.coreSettings = coreSettings; this.ruleEngineSettings = ruleEngineSettings; - this.notificationsTopicService = notificationsTopicService; + this.topicService = topicService; this.serviceInfoProvider = serviceInfoProvider; this.jsInvokeSettings = jsInvokeSettings; this.transportNotificationSettings = transportNotificationSettings; @@ -92,39 +92,39 @@ public class PubSubTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory @Override public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, transportNotificationSettings.getNotificationsTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); } @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, ruleEngineSettings.getTopic()); + return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, ruleEngineSettings.getTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, coreSettings.getTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration) { - return new TbPubSubConsumerTemplate<>(ruleEngineAdmin, pubSubSettings, configuration.getTopic(), + return new TbPubSubConsumerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(configuration.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, - notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), + topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); } @@ -153,12 +153,12 @@ public class PubSubTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbVersionControlQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbVersionControlQueueFactory.java index 57db7b884c..8154d04d6d 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbVersionControlQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbVersionControlQueueFactory.java @@ -22,6 +22,7 @@ import org.thingsboard.server.queue.TbQueueAdmin; import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.pubsub.TbPubSubAdmin; import org.thingsboard.server.queue.pubsub.TbPubSubConsumerTemplate; import org.thingsboard.server.queue.pubsub.TbPubSubProducerTemplate; @@ -39,6 +40,7 @@ public class PubSubTbVersionControlQueueFactory implements TbVersionControlQueue private final TbPubSubSettings pubSubSettings; private final TbQueueCoreSettings coreSettings; private final TbQueueVersionControlSettings vcSettings; + private final TopicService topicService; private final TbQueueAdmin coreAdmin; private final TbQueueAdmin notificationAdmin; @@ -47,11 +49,13 @@ public class PubSubTbVersionControlQueueFactory implements TbVersionControlQueue public PubSubTbVersionControlQueueFactory(TbPubSubSettings pubSubSettings, TbQueueCoreSettings coreSettings, TbQueueVersionControlSettings vcSettings, - TbPubSubSubscriptionSettings pubSubSubscriptionSettings + TbPubSubSubscriptionSettings pubSubSubscriptionSettings, + TopicService topicService ) { this.pubSubSettings = pubSubSettings; this.coreSettings = coreSettings; this.vcSettings = vcSettings; + this.topicService = topicService; this.coreAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getCoreSettings()); this.notificationAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getNotificationsSettings()); @@ -60,17 +64,17 @@ public class PubSubTbVersionControlQueueFactory implements TbVersionControlQueue @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, coreSettings.getTopic()); + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToVersionControlMsgConsumer() { - return new TbPubSubConsumerTemplate<>(vcAdmin, pubSubSettings, vcSettings.getTopic(), + return new TbPubSubConsumerTemplate<>(vcAdmin, pubSubSettings, topicService.buildTopicName(vcSettings.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()) ); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTransportQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTransportQueueFactory.java index d5665e2fbe..edcad873c4 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTransportQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTransportQueueFactory.java @@ -18,7 +18,9 @@ package org.thingsboard.server.queue.provider; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; @@ -31,6 +33,7 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.pubsub.TbPubSubAdmin; import org.thingsboard.server.queue.pubsub.TbPubSubConsumerTemplate; import org.thingsboard.server.queue.pubsub.TbPubSubProducerTemplate; @@ -54,6 +57,7 @@ public class PubSubTransportQueueFactory implements TbTransportQueueFactory { private final TbQueueRuleEngineSettings ruleEngineSettings; private final TbQueueTransportApiSettings transportApiSettings; private final TbQueueTransportNotificationSettings transportNotificationSettings; + private final TopicService topicService; private final TbQueueAdmin coreAdmin; private final TbQueueAdmin ruleEngineAdmin; @@ -66,13 +70,15 @@ public class PubSubTransportQueueFactory implements TbTransportQueueFactory { TbQueueRuleEngineSettings ruleEngineSettings, TbQueueTransportApiSettings transportApiSettings, TbQueueTransportNotificationSettings transportNotificationSettings, - TbPubSubSubscriptionSettings pubSubSubscriptionSettings) { + TbPubSubSubscriptionSettings pubSubSubscriptionSettings, + TopicService topicService) { this.pubSubSettings = pubSubSettings; this.serviceInfoProvider = serviceInfoProvider; this.coreSettings = coreSettings; this.ruleEngineSettings = ruleEngineSettings; this.transportApiSettings = transportApiSettings; this.transportNotificationSettings = transportNotificationSettings; + this.topicService = topicService; this.coreAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getCoreSettings()); this.ruleEngineAdmin = new TbPubSubAdmin(pubSubSettings, pubSubSubscriptionSettings.getRuleEngineSettings()); @@ -82,9 +88,9 @@ public class PubSubTransportQueueFactory implements TbTransportQueueFactory { @Override public TbQueueRequestTemplate, TbProtoQueueMsg> createTransportApiRequestTemplate() { - TbQueueProducer> producer = new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, transportApiSettings.getRequestsTopic()); + TbQueueProducer> producer = new TbPubSubProducerTemplate<>(transportApiAdmin, pubSubSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic())); TbQueueConsumer> consumer = new TbPubSubConsumerTemplate<>(transportApiAdmin, pubSubSettings, - transportApiSettings.getResponsesTopic() + "." + serviceInfoProvider.getServiceId(), + topicService.buildTopicName(transportApiSettings.getResponsesTopic() + "." + serviceInfoProvider.getServiceId()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiResponseMsg.parseFrom(msg.getData()), msg.getHeaders())); DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder @@ -100,24 +106,29 @@ public class PubSubTransportQueueFactory implements TbTransportQueueFactory { @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, ruleEngineSettings.getTopic()); + return new TbPubSubProducerTemplate<>(ruleEngineAdmin, pubSubSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); + } + + @Override + public TbQueueProducer> createTbCoreNotificationsMsgProducer() { + return new TbPubSubProducerTemplate<>(notificationAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createTransportNotificationsConsumer() { return new TbPubSubConsumerTemplate<>(notificationAdmin, pubSubSettings, - transportNotificationSettings.getNotificationsTopic() + "." + serviceInfoProvider.getServiceId(), + topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic() + "." + serviceInfoProvider.getServiceId()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToTransportMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getUsageStatsTopic()); + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java index 0318b5d06f..7e9833f56b 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java @@ -40,7 +40,7 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.rabbitmq.TbRabbitMqAdmin; import org.thingsboard.server.queue.rabbitmq.TbRabbitMqConsumerTemplate; @@ -61,7 +61,7 @@ import java.nio.charset.StandardCharsets; @ConditionalOnExpression("'${queue.type:null}'=='rabbitmq' && '${service.type:null}'=='monolith'") public class RabbitMqMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngineQueueFactory, TbVersionControlQueueFactory { - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final TbQueueCoreSettings coreSettings; private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueRuleEngineSettings ruleEngineSettings; @@ -78,7 +78,7 @@ public class RabbitMqMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE private final TbQueueAdmin notificationAdmin; private final TbQueueAdmin vcAdmin; - public RabbitMqMonolithQueueFactory(NotificationsTopicService notificationsTopicService, TbQueueCoreSettings coreSettings, + public RabbitMqMonolithQueueFactory(TopicService topicService, TbQueueCoreSettings coreSettings, TbQueueRuleEngineSettings ruleEngineSettings, TbServiceInfoProvider serviceInfoProvider, TbQueueTransportApiSettings transportApiSettings, @@ -87,7 +87,7 @@ public class RabbitMqMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE TbRabbitMqQueueArguments queueArguments, TbQueueRemoteJsInvokeSettings jsInvokeSettings, TbQueueVersionControlSettings vcSettings) { - this.notificationsTopicService = notificationsTopicService; + this.topicService = topicService; this.coreSettings = coreSettings; this.serviceInfoProvider = serviceInfoProvider; this.ruleEngineSettings = ruleEngineSettings; @@ -107,71 +107,71 @@ public class RabbitMqMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE @Override public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, transportNotificationSettings.getNotificationsTopic()); + return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); } @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbRabbitMqProducerTemplate<>(ruleEngineAdmin, rabbitMqSettings, ruleEngineSettings.getTopic()); + return new TbRabbitMqProducerTemplate<>(ruleEngineAdmin, rabbitMqSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, ruleEngineSettings.getTopic()); + return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getTopic()); + return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, coreSettings.getTopic()); + return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToVersionControlMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(vcAdmin, rabbitMqSettings, vcSettings.getTopic(), + return new TbRabbitMqConsumerTemplate<>(vcAdmin, rabbitMqSettings, topicService.buildTopicName(vcSettings.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()) ); } @Override public TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration) { - return new TbRabbitMqConsumerTemplate<>(ruleEngineAdmin, rabbitMqSettings, configuration.getTopic(), + return new TbRabbitMqConsumerTemplate<>(ruleEngineAdmin, rabbitMqSettings, topicService.buildTopicName(configuration.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { return new TbRabbitMqConsumerTemplate<>(notificationAdmin, rabbitMqSettings, - notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), + topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToCoreMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getTopic(), + return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { return new TbRabbitMqConsumerTemplate<>(notificationAdmin, rabbitMqSettings, - notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), + topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createTransportApiRequestConsumer() { - return new TbRabbitMqConsumerTemplate<>(transportApiAdmin, rabbitMqSettings, transportApiSettings.getRequestsTopic(), + return new TbRabbitMqConsumerTemplate<>(transportApiAdmin, rabbitMqSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createTransportApiResponseProducer() { - return new TbRabbitMqProducerTemplate<>(transportApiAdmin, rabbitMqSettings, transportApiSettings.getResponsesTopic()); + return new TbRabbitMqProducerTemplate<>(transportApiAdmin, rabbitMqSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic())); } @Override @@ -199,29 +199,29 @@ public class RabbitMqMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE @Override public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getUsageStatsTopic(), + return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getOtaPackageTopic(), + return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getOtaPackageTopic()); + return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); } @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getUsageStatsTopic()); + return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @Override public TbQueueProducer> createVersionControlMsgProducer() { - return new TbRabbitMqProducerTemplate<>(vcAdmin, rabbitMqSettings, vcSettings.getTopic()); + return new TbRabbitMqProducerTemplate<>(vcAdmin, rabbitMqSettings, topicService.buildTopicName(vcSettings.getTopic())); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java index 45f5a92980..8311560207 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java @@ -38,7 +38,7 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.rabbitmq.TbRabbitMqAdmin; import org.thingsboard.server.queue.rabbitmq.TbRabbitMqConsumerTemplate; @@ -62,7 +62,7 @@ public class RabbitMqTbCoreQueueFactory implements TbCoreQueueFactory { private final TbQueueRuleEngineSettings ruleEngineSettings; private final TbQueueCoreSettings coreSettings; private final TbQueueTransportApiSettings transportApiSettings; - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; private final TbQueueTransportNotificationSettings transportNotificationSettings; @@ -77,7 +77,7 @@ public class RabbitMqTbCoreQueueFactory implements TbCoreQueueFactory { TbQueueCoreSettings coreSettings, TbQueueTransportApiSettings transportApiSettings, TbQueueRuleEngineSettings ruleEngineSettings, - NotificationsTopicService notificationsTopicService, + TopicService topicService, TbServiceInfoProvider serviceInfoProvider, TbQueueRemoteJsInvokeSettings jsInvokeSettings, TbQueueTransportNotificationSettings transportNotificationSettings, @@ -86,7 +86,7 @@ public class RabbitMqTbCoreQueueFactory implements TbCoreQueueFactory { this.coreSettings = coreSettings; this.transportApiSettings = transportApiSettings; this.ruleEngineSettings = ruleEngineSettings; - this.notificationsTopicService = notificationsTopicService; + this.topicService = topicService; this.serviceInfoProvider = serviceInfoProvider; this.jsInvokeSettings = jsInvokeSettings; this.transportNotificationSettings = transportNotificationSettings; @@ -100,51 +100,51 @@ public class RabbitMqTbCoreQueueFactory implements TbCoreQueueFactory { @Override public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, transportNotificationSettings.getNotificationsTopic()); + return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); } @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getTopic()); + return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, ruleEngineSettings.getTopic()); + return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getTopic()); + return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, coreSettings.getTopic()); + return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToCoreMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getTopic(), + return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { return new TbRabbitMqConsumerTemplate<>(notificationAdmin, rabbitMqSettings, - notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), + topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createTransportApiRequestConsumer() { - return new TbRabbitMqConsumerTemplate<>(transportApiAdmin, rabbitMqSettings, transportApiSettings.getRequestsTopic(), + return new TbRabbitMqConsumerTemplate<>(transportApiAdmin, rabbitMqSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createTransportApiResponseProducer() { - return new TbRabbitMqProducerTemplate<>(transportApiAdmin, rabbitMqSettings, transportApiSettings.getResponsesTopic()); + return new TbRabbitMqProducerTemplate<>(transportApiAdmin, rabbitMqSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic())); } @Override @@ -178,24 +178,24 @@ public class RabbitMqTbCoreQueueFactory implements TbCoreQueueFactory { @Override public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getUsageStatsTopic(), + return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getOtaPackageTopic(), + return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getOtaPackageTopic()); + return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); } @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getUsageStatsTopic()); + return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbRuleEngineQueueFactory.java index 37633ced95..ae8a879782 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbRuleEngineQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbRuleEngineQueueFactory.java @@ -36,7 +36,7 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.rabbitmq.TbRabbitMqAdmin; import org.thingsboard.server.queue.rabbitmq.TbRabbitMqConsumerTemplate; @@ -55,7 +55,7 @@ import java.nio.charset.StandardCharsets; @ConditionalOnExpression("'${queue.type:null}'=='rabbitmq' && '${service.type:null}'=='tb-rule-engine'") public class RabbitMqTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final TbQueueCoreSettings coreSettings; private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueRuleEngineSettings ruleEngineSettings; @@ -68,14 +68,14 @@ public class RabbitMqTbRuleEngineQueueFactory implements TbRuleEngineQueueFactor private final TbQueueAdmin jsExecutorAdmin; private final TbQueueAdmin notificationAdmin; - public RabbitMqTbRuleEngineQueueFactory(NotificationsTopicService notificationsTopicService, TbQueueCoreSettings coreSettings, + public RabbitMqTbRuleEngineQueueFactory(TopicService topicService, TbQueueCoreSettings coreSettings, TbQueueRuleEngineSettings ruleEngineSettings, TbServiceInfoProvider serviceInfoProvider, TbRabbitMqSettings rabbitMqSettings, TbQueueRemoteJsInvokeSettings jsInvokeSettings, TbQueueTransportNotificationSettings transportNotificationSettings, TbRabbitMqQueueArguments queueArguments) { - this.notificationsTopicService = notificationsTopicService; + this.topicService = topicService; this.coreSettings = coreSettings; this.serviceInfoProvider = serviceInfoProvider; this.ruleEngineSettings = ruleEngineSettings; @@ -91,39 +91,39 @@ public class RabbitMqTbRuleEngineQueueFactory implements TbRuleEngineQueueFactor @Override public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, transportNotificationSettings.getNotificationsTopic()); + return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); } @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbRabbitMqProducerTemplate<>(ruleEngineAdmin, rabbitMqSettings, ruleEngineSettings.getTopic()); + return new TbRabbitMqProducerTemplate<>(ruleEngineAdmin, rabbitMqSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, ruleEngineSettings.getTopic()); + return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getTopic()); + return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, coreSettings.getTopic()); + return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration) { - return new TbRabbitMqConsumerTemplate<>(ruleEngineAdmin, rabbitMqSettings, configuration.getTopic(), + return new TbRabbitMqConsumerTemplate<>(ruleEngineAdmin, rabbitMqSettings, topicService.buildTopicName(configuration.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { return new TbRabbitMqConsumerTemplate<>(notificationAdmin, rabbitMqSettings, - notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), + topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); } @@ -152,12 +152,12 @@ public class RabbitMqTbRuleEngineQueueFactory implements TbRuleEngineQueueFactor @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getUsageStatsTopic()); + return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getOtaPackageTopic()); + return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbVersionControlQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbVersionControlQueueFactory.java index ed1da6a302..d32865a05c 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbVersionControlQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbVersionControlQueueFactory.java @@ -22,6 +22,7 @@ import org.thingsboard.server.queue.TbQueueAdmin; import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.rabbitmq.TbRabbitMqAdmin; import org.thingsboard.server.queue.rabbitmq.TbRabbitMqConsumerTemplate; import org.thingsboard.server.queue.rabbitmq.TbRabbitMqProducerTemplate; @@ -39,6 +40,7 @@ public class RabbitMqTbVersionControlQueueFactory implements TbVersionControlQue private final TbRabbitMqSettings rabbitMqSettings; private final TbQueueCoreSettings coreSettings; private final TbQueueVersionControlSettings vcSettings; + private final TopicService topicService; private final TbQueueAdmin coreAdmin; private final TbQueueAdmin notificationAdmin; @@ -47,11 +49,13 @@ public class RabbitMqTbVersionControlQueueFactory implements TbVersionControlQue public RabbitMqTbVersionControlQueueFactory(TbRabbitMqSettings rabbitMqSettings, TbQueueCoreSettings coreSettings, TbQueueVersionControlSettings vcSettings, - TbRabbitMqQueueArguments queueArguments + TbRabbitMqQueueArguments queueArguments, + TopicService topicService ) { this.rabbitMqSettings = rabbitMqSettings; this.coreSettings = coreSettings; this.vcSettings = vcSettings; + this.topicService = topicService; this.coreAdmin = new TbRabbitMqAdmin(this.rabbitMqSettings, queueArguments.getCoreArgs()); this.notificationAdmin = new TbRabbitMqAdmin(this.rabbitMqSettings, queueArguments.getNotificationsArgs()); @@ -60,17 +64,17 @@ public class RabbitMqTbVersionControlQueueFactory implements TbVersionControlQue @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getUsageStatsTopic()); + return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, coreSettings.getTopic()); + return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToVersionControlMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(vcAdmin, rabbitMqSettings, vcSettings.getTopic(), + return new TbRabbitMqConsumerTemplate<>(vcAdmin, rabbitMqSettings, topicService.buildTopicName(vcSettings.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()) ); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTransportQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTransportQueueFactory.java index f11a877fc5..1af28b629b 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTransportQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTransportQueueFactory.java @@ -18,7 +18,9 @@ package org.thingsboard.server.queue.provider; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; @@ -31,6 +33,7 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.rabbitmq.TbRabbitMqAdmin; import org.thingsboard.server.queue.rabbitmq.TbRabbitMqConsumerTemplate; import org.thingsboard.server.queue.rabbitmq.TbRabbitMqProducerTemplate; @@ -53,6 +56,7 @@ public class RabbitMqTransportQueueFactory implements TbTransportQueueFactory { private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueCoreSettings coreSettings; private final TbQueueRuleEngineSettings ruleEngineSettings; + private final TopicService topicService; private final TbQueueAdmin coreAdmin; private final TbQueueAdmin ruleEngineAdmin; @@ -65,13 +69,15 @@ public class RabbitMqTransportQueueFactory implements TbTransportQueueFactory { TbServiceInfoProvider serviceInfoProvider, TbQueueCoreSettings coreSettings, TbQueueRuleEngineSettings ruleEngineSettings, - TbRabbitMqQueueArguments queueArguments) { + TbRabbitMqQueueArguments queueArguments, + TopicService topicService) { this.transportApiSettings = transportApiSettings; this.transportNotificationSettings = transportNotificationSettings; this.rabbitMqSettings = rabbitMqSettings; this.serviceInfoProvider = serviceInfoProvider; this.coreSettings = coreSettings; this.ruleEngineSettings = ruleEngineSettings; + this.topicService = topicService; this.coreAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getCoreArgs()); this.ruleEngineAdmin = new TbRabbitMqAdmin(rabbitMqSettings, queueArguments.getRuleEngineArgs()); @@ -82,11 +88,11 @@ public class RabbitMqTransportQueueFactory implements TbTransportQueueFactory { @Override public TbQueueRequestTemplate, TbProtoQueueMsg> createTransportApiRequestTemplate() { TbQueueProducer> producerTemplate = - new TbRabbitMqProducerTemplate<>(transportApiAdmin, rabbitMqSettings, transportApiSettings.getRequestsTopic()); + new TbRabbitMqProducerTemplate<>(transportApiAdmin, rabbitMqSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic())); TbQueueConsumer> consumerTemplate = new TbRabbitMqConsumerTemplate<>(transportApiAdmin, rabbitMqSettings, - transportApiSettings.getResponsesTopic() + "." + serviceInfoProvider.getServiceId(), + topicService.buildTopicName(transportApiSettings.getResponsesTopic() + "." + serviceInfoProvider.getServiceId()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiResponseMsg.parseFrom(msg.getData()), msg.getHeaders())); DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder @@ -102,23 +108,28 @@ public class RabbitMqTransportQueueFactory implements TbTransportQueueFactory { @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbRabbitMqProducerTemplate<>(ruleEngineAdmin, rabbitMqSettings, ruleEngineSettings.getTopic()); + return new TbRabbitMqProducerTemplate<>(ruleEngineAdmin, rabbitMqSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getTopic()); + return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic())); + } + + @Override + public TbQueueProducer> createTbCoreNotificationsMsgProducer() { + return new TbRabbitMqProducerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createTransportNotificationsConsumer() { - return new TbRabbitMqConsumerTemplate<>(notificationAdmin, rabbitMqSettings, transportNotificationSettings.getNotificationsTopic() + "." + serviceInfoProvider.getServiceId(), + return new TbRabbitMqConsumerTemplate<>(notificationAdmin, rabbitMqSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic() + "." + serviceInfoProvider.getServiceId()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToTransportMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getUsageStatsTopic()); + return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java index 950e49dc93..6f2dbdc72f 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java @@ -44,7 +44,7 @@ import org.thingsboard.server.queue.azure.servicebus.TbServiceBusSettings; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.settings.TbQueueCoreSettings; import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; @@ -60,7 +60,7 @@ import java.nio.charset.StandardCharsets; @ConditionalOnExpression("'${queue.type:null}'=='service-bus' && '${service.type:null}'=='monolith'") public class ServiceBusMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngineQueueFactory, TbVersionControlQueueFactory { - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final TbQueueCoreSettings coreSettings; private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueRuleEngineSettings ruleEngineSettings; @@ -77,7 +77,7 @@ public class ServiceBusMonolithQueueFactory implements TbCoreQueueFactory, TbRul private final TbQueueAdmin notificationAdmin; private final TbQueueAdmin vcAdmin; - public ServiceBusMonolithQueueFactory(NotificationsTopicService notificationsTopicService, TbQueueCoreSettings coreSettings, + public ServiceBusMonolithQueueFactory(TopicService topicService, TbQueueCoreSettings coreSettings, TbQueueRuleEngineSettings ruleEngineSettings, TbServiceInfoProvider serviceInfoProvider, TbQueueTransportApiSettings transportApiSettings, @@ -86,7 +86,7 @@ public class ServiceBusMonolithQueueFactory implements TbCoreQueueFactory, TbRul TbQueueRemoteJsInvokeSettings jsInvokeSettings, TbQueueVersionControlSettings vcSettings, TbServiceBusQueueConfigs serviceBusQueueConfigs) { - this.notificationsTopicService = notificationsTopicService; + this.topicService = topicService; this.coreSettings = coreSettings; this.serviceInfoProvider = serviceInfoProvider; this.ruleEngineSettings = ruleEngineSettings; @@ -106,32 +106,32 @@ public class ServiceBusMonolithQueueFactory implements TbCoreQueueFactory, TbRul @Override public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, transportNotificationSettings.getNotificationsTopic()); + return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); } @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbServiceBusProducerTemplate<>(ruleEngineAdmin, serviceBusSettings, ruleEngineSettings.getTopic()); + return new TbServiceBusProducerTemplate<>(ruleEngineAdmin, serviceBusSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, ruleEngineSettings.getTopic()); + return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getTopic()); + return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, coreSettings.getTopic()); + return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToVersionControlMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(vcAdmin, serviceBusSettings, vcSettings.getTopic(), + return new TbServiceBusConsumerTemplate<>(vcAdmin, serviceBusSettings, topicService.buildTopicName(vcSettings.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()) ); } @@ -145,32 +145,32 @@ public class ServiceBusMonolithQueueFactory implements TbCoreQueueFactory, TbRul @Override public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { return new TbServiceBusConsumerTemplate<>(notificationAdmin, serviceBusSettings, - notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), + topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToCoreMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getTopic(), + return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { return new TbServiceBusConsumerTemplate<>(notificationAdmin, serviceBusSettings, - notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), + topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createTransportApiRequestConsumer() { - return new TbServiceBusConsumerTemplate<>(transportApiAdmin, serviceBusSettings, transportApiSettings.getRequestsTopic(), + return new TbServiceBusConsumerTemplate<>(transportApiAdmin, serviceBusSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createTransportApiResponseProducer() { - return new TbServiceBusProducerTemplate<>(transportApiAdmin, serviceBusSettings, transportApiSettings.getResponsesTopic()); + return new TbServiceBusProducerTemplate<>(transportApiAdmin, serviceBusSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic())); } @Override @@ -198,29 +198,29 @@ public class ServiceBusMonolithQueueFactory implements TbCoreQueueFactory, TbRul @Override public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getUsageStatsTopic(), + return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getOtaPackageTopic(), + return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getOtaPackageTopic()); + return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); } @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getUsageStatsTopic()); + return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @Override public TbQueueProducer> createVersionControlMsgProducer() { - return new TbServiceBusProducerTemplate<>(vcAdmin, serviceBusSettings, vcSettings.getTopic()); + return new TbServiceBusProducerTemplate<>(vcAdmin, serviceBusSettings, topicService.buildTopicName(vcSettings.getTopic())); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java index b92629633d..c86efee599 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java @@ -43,7 +43,7 @@ import org.thingsboard.server.queue.azure.servicebus.TbServiceBusSettings; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.settings.TbQueueCoreSettings; import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; @@ -62,7 +62,7 @@ public class ServiceBusTbCoreQueueFactory implements TbCoreQueueFactory { private final TbQueueRuleEngineSettings ruleEngineSettings; private final TbQueueCoreSettings coreSettings; private final TbQueueTransportApiSettings transportApiSettings; - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueRemoteJsInvokeSettings jsInvokeSettings; private final TbQueueTransportNotificationSettings transportNotificationSettings; @@ -77,7 +77,7 @@ public class ServiceBusTbCoreQueueFactory implements TbCoreQueueFactory { TbQueueCoreSettings coreSettings, TbQueueTransportApiSettings transportApiSettings, TbQueueRuleEngineSettings ruleEngineSettings, - NotificationsTopicService notificationsTopicService, + TopicService topicService, TbServiceInfoProvider serviceInfoProvider, TbQueueRemoteJsInvokeSettings jsInvokeSettings, TbQueueTransportNotificationSettings transportNotificationSettings, @@ -86,7 +86,7 @@ public class ServiceBusTbCoreQueueFactory implements TbCoreQueueFactory { this.coreSettings = coreSettings; this.transportApiSettings = transportApiSettings; this.ruleEngineSettings = ruleEngineSettings; - this.notificationsTopicService = notificationsTopicService; + this.topicService = topicService; this.serviceInfoProvider = serviceInfoProvider; this.jsInvokeSettings = jsInvokeSettings; this.transportNotificationSettings = transportNotificationSettings; @@ -100,51 +100,51 @@ public class ServiceBusTbCoreQueueFactory implements TbCoreQueueFactory { @Override public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, transportNotificationSettings.getNotificationsTopic()); + return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); } @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getTopic()); + return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, ruleEngineSettings.getTopic()); + return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getTopic()); + return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, coreSettings.getTopic()); + return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToCoreMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getTopic(), + return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToCoreNotificationsMsgConsumer() { return new TbServiceBusConsumerTemplate<>(notificationAdmin, serviceBusSettings, - notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), + topicService.getNotificationsTopic(ServiceType.TB_CORE, serviceInfoProvider.getServiceId()).getFullTopicName(), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToCoreNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createTransportApiRequestConsumer() { - return new TbServiceBusConsumerTemplate<>(transportApiAdmin, serviceBusSettings, transportApiSettings.getRequestsTopic(), + return new TbServiceBusConsumerTemplate<>(transportApiAdmin, serviceBusSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiRequestMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createTransportApiResponseProducer() { - return new TbServiceBusProducerTemplate<>(transportApiAdmin, serviceBusSettings, transportApiSettings.getResponsesTopic()); + return new TbServiceBusProducerTemplate<>(transportApiAdmin, serviceBusSettings, topicService.buildTopicName(transportApiSettings.getResponsesTopic())); } @Override @@ -172,24 +172,24 @@ public class ServiceBusTbCoreQueueFactory implements TbCoreQueueFactory { @Override public TbQueueConsumer> createToUsageStatsServiceMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getUsageStatsTopic(), + return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToUsageStatsServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getOtaPackageTopic(), + return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getOtaPackageTopic()); + return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); } @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getUsageStatsTopic()); + return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbRuleEngineQueueFactory.java index a1571becc7..28e26ac080 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbRuleEngineQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbRuleEngineQueueFactory.java @@ -41,7 +41,7 @@ import org.thingsboard.server.queue.azure.servicebus.TbServiceBusSettings; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.settings.TbQueueCoreSettings; import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; @@ -55,7 +55,7 @@ import java.nio.charset.StandardCharsets; @ConditionalOnExpression("'${queue.type:null}'=='service-bus' && '${service.type:null}'=='tb-rule-engine'") public class ServiceBusTbRuleEngineQueueFactory implements TbRuleEngineQueueFactory { - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final TbQueueCoreSettings coreSettings; private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueRuleEngineSettings ruleEngineSettings; @@ -68,14 +68,14 @@ public class ServiceBusTbRuleEngineQueueFactory implements TbRuleEngineQueueFact private final TbQueueAdmin jsExecutorAdmin; private final TbQueueAdmin notificationAdmin; - public ServiceBusTbRuleEngineQueueFactory(NotificationsTopicService notificationsTopicService, TbQueueCoreSettings coreSettings, + public ServiceBusTbRuleEngineQueueFactory(TopicService topicService, TbQueueCoreSettings coreSettings, TbQueueRuleEngineSettings ruleEngineSettings, TbServiceInfoProvider serviceInfoProvider, TbServiceBusSettings serviceBusSettings, TbQueueRemoteJsInvokeSettings jsInvokeSettings, TbQueueTransportNotificationSettings transportNotificationSettings, TbServiceBusQueueConfigs serviceBusQueueConfigs) { - this.notificationsTopicService = notificationsTopicService; + this.topicService = topicService; this.coreSettings = coreSettings; this.serviceInfoProvider = serviceInfoProvider; this.ruleEngineSettings = ruleEngineSettings; @@ -91,39 +91,39 @@ public class ServiceBusTbRuleEngineQueueFactory implements TbRuleEngineQueueFact @Override public TbQueueProducer> createTransportNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, transportNotificationSettings.getNotificationsTopic()); + return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic())); } @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbServiceBusProducerTemplate<>(ruleEngineAdmin, serviceBusSettings, ruleEngineSettings.getTopic()); + return new TbServiceBusProducerTemplate<>(ruleEngineAdmin, serviceBusSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createRuleEngineNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, ruleEngineSettings.getTopic()); + return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getTopic()); + return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, coreSettings.getTopic()); + return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration) { - return new TbServiceBusConsumerTemplate<>(ruleEngineAdmin, serviceBusSettings, configuration.getTopic(), + return new TbServiceBusConsumerTemplate<>(ruleEngineAdmin, serviceBusSettings, topicService.buildTopicName(configuration.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueConsumer> createToRuleEngineNotificationsMsgConsumer() { return new TbServiceBusConsumerTemplate<>(notificationAdmin, serviceBusSettings, - notificationsTopicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), + topicService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceInfoProvider.getServiceId()).getFullTopicName(), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToRuleEngineNotificationMsg.parseFrom(msg.getData()), msg.getHeaders())); } @@ -152,12 +152,12 @@ public class ServiceBusTbRuleEngineQueueFactory implements TbRuleEngineQueueFact @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getUsageStatsTopic()); + return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @Override public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getOtaPackageTopic()); + return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getOtaPackageTopic())); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbVersionControlQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbVersionControlQueueFactory.java index 24cb4fe8bc..76149713ce 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbVersionControlQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbVersionControlQueueFactory.java @@ -27,6 +27,7 @@ import org.thingsboard.server.queue.azure.servicebus.TbServiceBusProducerTemplat import org.thingsboard.server.queue.azure.servicebus.TbServiceBusQueueConfigs; import org.thingsboard.server.queue.azure.servicebus.TbServiceBusSettings; import org.thingsboard.server.queue.common.TbProtoQueueMsg; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.settings.TbQueueCoreSettings; import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings; @@ -39,6 +40,7 @@ public class ServiceBusTbVersionControlQueueFactory implements TbVersionControlQ private final TbServiceBusSettings serviceBusSettings; private final TbQueueCoreSettings coreSettings; private final TbQueueVersionControlSettings vcSettings; + private final TopicService topicService; private final TbQueueAdmin coreAdmin; private final TbQueueAdmin notificationAdmin; @@ -47,11 +49,13 @@ public class ServiceBusTbVersionControlQueueFactory implements TbVersionControlQ public ServiceBusTbVersionControlQueueFactory(TbServiceBusSettings serviceBusSettings, TbQueueCoreSettings coreSettings, TbQueueVersionControlSettings vcSettings, - TbServiceBusQueueConfigs serviceBusQueueConfigs + TbServiceBusQueueConfigs serviceBusQueueConfigs, + TopicService topicService ) { this.serviceBusSettings = serviceBusSettings; this.coreSettings = coreSettings; this.vcSettings = vcSettings; + this.topicService = topicService; this.coreAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getCoreConfigs()); this.notificationAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getNotificationsConfigs()); @@ -60,17 +64,17 @@ public class ServiceBusTbVersionControlQueueFactory implements TbVersionControlQ @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getUsageStatsTopic()); + return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @Override public TbQueueProducer> createTbCoreNotificationsMsgProducer() { - return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, coreSettings.getTopic()); + return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createToVersionControlMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(vcAdmin, serviceBusSettings, vcSettings.getTopic(), + return new TbServiceBusConsumerTemplate<>(vcAdmin, serviceBusSettings, topicService.buildTopicName(vcSettings.getTopic()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportProtos.ToVersionControlServiceMsg.parseFrom(msg.getData()), msg.getHeaders()) ); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTransportQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTransportQueueFactory.java index 15e661f5b0..f6d85577e1 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTransportQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTransportQueueFactory.java @@ -18,7 +18,9 @@ package org.thingsboard.server.queue.provider; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; @@ -36,6 +38,7 @@ import org.thingsboard.server.queue.azure.servicebus.TbServiceBusSettings; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.settings.TbQueueCoreSettings; import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; @@ -53,6 +56,7 @@ public class ServiceBusTransportQueueFactory implements TbTransportQueueFactory private final TbServiceInfoProvider serviceInfoProvider; private final TbQueueCoreSettings coreSettings; private final TbQueueRuleEngineSettings ruleEngineSettings; + private final TopicService topicService; private final TbQueueAdmin coreAdmin; private final TbQueueAdmin transportApiAdmin; @@ -65,13 +69,15 @@ public class ServiceBusTransportQueueFactory implements TbTransportQueueFactory TbQueueRuleEngineSettings ruleEngineSettings, TbServiceInfoProvider serviceInfoProvider, TbQueueCoreSettings coreSettings, - TbServiceBusQueueConfigs serviceBusQueueConfigs) { + TbServiceBusQueueConfigs serviceBusQueueConfigs, + TopicService topicService) { this.transportApiSettings = transportApiSettings; this.transportNotificationSettings = transportNotificationSettings; this.serviceBusSettings = serviceBusSettings; this.serviceInfoProvider = serviceInfoProvider; this.coreSettings = coreSettings; this.ruleEngineSettings = ruleEngineSettings; + this.topicService = topicService; this.coreAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getCoreConfigs()); this.transportApiAdmin = new TbServiceBusAdmin(serviceBusSettings, serviceBusQueueConfigs.getTransportApiConfigs()); @@ -82,11 +88,11 @@ public class ServiceBusTransportQueueFactory implements TbTransportQueueFactory @Override public TbQueueRequestTemplate, TbProtoQueueMsg> createTransportApiRequestTemplate() { TbQueueProducer> producerTemplate = - new TbServiceBusProducerTemplate<>(transportApiAdmin, serviceBusSettings, transportApiSettings.getRequestsTopic()); + new TbServiceBusProducerTemplate<>(transportApiAdmin, serviceBusSettings, topicService.buildTopicName(transportApiSettings.getRequestsTopic())); TbQueueConsumer> consumerTemplate = new TbServiceBusConsumerTemplate<>(transportApiAdmin, serviceBusSettings, - transportApiSettings.getResponsesTopic() + "." + serviceInfoProvider.getServiceId(), + topicService.buildTopicName(transportApiSettings.getResponsesTopic() + "." + serviceInfoProvider.getServiceId()), msg -> new TbProtoQueueMsg<>(msg.getKey(), TransportApiResponseMsg.parseFrom(msg.getData()), msg.getHeaders())); DefaultTbQueueRequestTemplate.DefaultTbQueueRequestTemplateBuilder @@ -102,24 +108,29 @@ public class ServiceBusTransportQueueFactory implements TbTransportQueueFactory @Override public TbQueueProducer> createRuleEngineMsgProducer() { - return new TbServiceBusProducerTemplate<>(ruleEngineAdmin, serviceBusSettings, ruleEngineSettings.getTopic()); + return new TbServiceBusProducerTemplate<>(ruleEngineAdmin, serviceBusSettings, topicService.buildTopicName(ruleEngineSettings.getTopic())); } @Override public TbQueueProducer> createTbCoreMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getTopic()); + return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic())); + } + + @Override + public TbQueueProducer> createTbCoreNotificationsMsgProducer() { + return new TbServiceBusProducerTemplate<>(notificationAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getTopic())); } @Override public TbQueueConsumer> createTransportNotificationsConsumer() { return new TbServiceBusConsumerTemplate<>(notificationAdmin, serviceBusSettings, - transportNotificationSettings.getNotificationsTopic() + "." + serviceInfoProvider.getServiceId(), + topicService.buildTopicName(transportNotificationSettings.getNotificationsTopic() + "." + serviceInfoProvider.getServiceId()), msg -> new TbProtoQueueMsg<>(msg.getKey(), ToTransportMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override public TbQueueProducer> createToUsageStatsServiceMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getUsageStatsTopic()); + return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, topicService.buildTopicName(coreSettings.getUsageStatsTopic())); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineQueueFactory.java index 386447045d..bb1943a7d5 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineQueueFactory.java @@ -82,7 +82,6 @@ public interface TbRuleEngineQueueFactory extends TbUsageStatsClientQueueFactory * @return * @param configuration */ - //TODO 2.5 ybondarenko: make sure you use queueName to distinct consumers where necessary TbQueueConsumer> createToRuleEngineMsgConsumer(Queue configuration); /** diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbTransportQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbTransportQueueFactory.java index feff3830fc..c7d2f78838 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbTransportQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbTransportQueueFactory.java @@ -16,6 +16,7 @@ package org.thingsboard.server.queue.provider; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; @@ -33,6 +34,8 @@ public interface TbTransportQueueFactory extends TbUsageStatsClientQueueFactory TbQueueProducer> createTbCoreMsgProducer(); + TbQueueProducer> createTbCoreNotificationsMsgProducer(); + TbQueueConsumer> createTransportNotificationsConsumer(); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbTransportQueueProducerProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbTransportQueueProducerProvider.java index e9177d8b1c..afbcd718a5 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbTransportQueueProducerProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbTransportQueueProducerProvider.java @@ -36,6 +36,7 @@ public class TbTransportQueueProducerProvider implements TbQueueProducerProvider private final TbTransportQueueFactory tbQueueProvider; private TbQueueProducer> toRuleEngine; private TbQueueProducer> toTbCore; + private TbQueueProducer> toTbCoreNotifications; private TbQueueProducer> toUsageStats; public TbTransportQueueProducerProvider(TbTransportQueueFactory tbQueueProvider) { @@ -47,6 +48,7 @@ public class TbTransportQueueProducerProvider implements TbQueueProducerProvider this.toTbCore = tbQueueProvider.createTbCoreMsgProducer(); this.toRuleEngine = tbQueueProvider.createRuleEngineMsgProducer(); this.toUsageStats = tbQueueProvider.createToUsageStatsServiceMsgProducer(); + this.toTbCoreNotifications = tbQueueProvider.createTbCoreNotificationsMsgProducer(); } @Override @@ -71,7 +73,7 @@ public class TbTransportQueueProducerProvider implements TbQueueProducerProvider @Override public TbQueueProducer> getTbCoreNotificationsMsgProducer() { - throw new RuntimeException("Not Implemented! Should not be used by Transport!"); + return toTbCoreNotifications; } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubAdmin.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubAdmin.java index d1a4942ad3..f9f20c2448 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubAdmin.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubAdmin.java @@ -103,7 +103,7 @@ public class TbPubSubAdmin implements TbQueueAdmin { } @Override - public void createTopicIfNotExists(String partition) { + public void createTopicIfNotExists(String partition, String properties) { TopicName topicName = TopicName.newBuilder() .setTopic(partition) .setProject(pubSubSettings.getProjectId()) diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java index 97b7b47843..1a84b80cd2 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java @@ -17,6 +17,7 @@ package org.thingsboard.server.queue.pubsub; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; +import com.google.api.gax.core.FixedExecutorProvider; import com.google.cloud.pubsub.v1.stub.GrpcSubscriberStub; import com.google.cloud.pubsub.v1.stub.SubscriberStub; import com.google.cloud.pubsub.v1.stub.SubscriberStubSettings; @@ -76,6 +77,7 @@ public class TbPubSubConsumerTemplate extends AbstractPara SubscriberStubSettings.defaultGrpcTransportProviderBuilder() .setMaxInboundMessageSize(pubSubSettings.getMaxMsgSize()) .build()) + .setExecutorProvider(pubSubSettings.getExecutorProvider()) .build(); this.subscriber = GrpcSubscriberStub.create(subscriberStubSettings); } catch (IOException e) { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java index 0d84a8f839..3107855ab1 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java @@ -18,6 +18,7 @@ package org.thingsboard.server.queue.pubsub; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutureCallback; import com.google.api.core.ApiFutures; +import com.google.api.gax.core.FixedExecutorProvider; import com.google.cloud.pubsub.v1.Publisher; import com.google.gson.Gson; import com.google.protobuf.ByteString; @@ -120,7 +121,10 @@ public class TbPubSubProducerTemplate implements TbQueuePr try { admin.createTopicIfNotExists(topic); ProjectTopicName topicName = ProjectTopicName.of(pubSubSettings.getProjectId(), topic); - Publisher publisher = Publisher.newBuilder(topicName).setCredentialsProvider(pubSubSettings.getCredentialsProvider()).build(); + Publisher publisher = Publisher.newBuilder(topicName) + .setCredentialsProvider(pubSubSettings.getCredentialsProvider()) + .setExecutorProvider(pubSubSettings.getExecutorProvider()) + .build(); publisherMap.put(topic, publisher); return publisher; } catch (IOException e) { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java index fe9d85c306..145661fed1 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java @@ -17,16 +17,20 @@ package org.thingsboard.server.queue.pubsub; import com.google.api.gax.core.CredentialsProvider; import com.google.api.gax.core.FixedCredentialsProvider; +import com.google.api.gax.core.FixedExecutorProvider; import com.google.auth.oauth2.ServiceAccountCredentials; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; +import org.thingsboard.common.util.ThingsBoardThreadFactory; import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.util.concurrent.Executors; @Slf4j @ConditionalOnExpression("'${queue.type:null}'=='pubsub'") @@ -46,6 +50,16 @@ public class TbPubSubSettings { @Value("${queue.pubsub.max_messages}") private int maxMessages; + @Value("${queue.pubsub.executor_thread_pool_size:0}") + private int threadPoolSize; + + /** + * Refers to com.google.cloud.pubsub.v1.Publisher default executor configuration + */ + private static final int THREADS_PER_CPU = 5; + + private FixedExecutorProvider executorProvider; + private CredentialsProvider credentialsProvider; @PostConstruct @@ -53,6 +67,17 @@ public class TbPubSubSettings { ServiceAccountCredentials credentials = ServiceAccountCredentials.fromStream( new ByteArrayInputStream(serviceAccount.getBytes())); credentialsProvider = FixedCredentialsProvider.create(credentials); + if (threadPoolSize == 0) { + threadPoolSize = THREADS_PER_CPU * Runtime.getRuntime().availableProcessors(); + } + executorProvider = FixedExecutorProvider + .create(Executors.newScheduledThreadPool(threadPoolSize, ThingsBoardThreadFactory.forName("pubsub-queue-executor"))); } + @PreDestroy + private void destroy() { + if (executorProvider != null) { + executorProvider.getExecutor().shutdownNow(); + } + } } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqAdmin.java b/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqAdmin.java index 00a2ee4c6c..fb646f383a 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqAdmin.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqAdmin.java @@ -18,9 +18,11 @@ package org.thingsboard.server.queue.rabbitmq; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.thingsboard.server.queue.TbQueueAdmin; import java.io.IOException; +import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeoutException; @@ -50,7 +52,12 @@ public class TbRabbitMqAdmin implements TbQueueAdmin { } @Override - public void createTopicIfNotExists(String topic) { + public void createTopicIfNotExists(String topic, String properties) { + Map arguments = this.arguments; + if (StringUtils.isNotBlank(properties)) { + arguments = new HashMap<>(arguments); + arguments.putAll(TbRabbitMqQueueArguments.getArgs(properties)); + } try { channel.queueDeclare(topic, false, false, false, arguments); } catch (IOException e) { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqQueueArguments.java b/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqQueueArguments.java index cb96abdf3c..8fa8c537e6 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqQueueArguments.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqQueueArguments.java @@ -65,7 +65,7 @@ public class TbRabbitMqQueueArguments { vcArgs = getArgs(vcProperties); } - private Map getArgs(String properties) { + public static Map getArgs(String properties) { Map configs = new HashMap<>(); if (StringUtils.isNotEmpty(properties)) { for (String property : properties.split(";")) { @@ -78,7 +78,7 @@ public class TbRabbitMqQueueArguments { return configs; } - private Object getObjectValue(String str) { + private static Object getObjectValue(String str) { if (str.equalsIgnoreCase("true") || str.equalsIgnoreCase("false")) { return Boolean.valueOf(str); } else if (isNumeric(str)) { @@ -87,7 +87,7 @@ public class TbRabbitMqQueueArguments { return str; } - private Object getNumericValue(String str) { + private static Object getNumericValue(String str) { if (str.contains(".")) { return Double.valueOf(str); } else { @@ -97,7 +97,7 @@ public class TbRabbitMqQueueArguments { private static final Pattern PATTERN = Pattern.compile("-?\\d+(\\.\\d+)?"); - public boolean isNumeric(String strNum) { + private static boolean isNumeric(String strNum) { if (strNum == null) { return false; } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/scheduler/DefaultSchedulerComponent.java b/common/queue/src/main/java/org/thingsboard/server/queue/scheduler/DefaultSchedulerComponent.java index 808e7c2c83..ffacea8076 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/scheduler/DefaultSchedulerComponent.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/scheduler/DefaultSchedulerComponent.java @@ -27,12 +27,12 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @Component -public class DefaultSchedulerComponent implements SchedulerComponent{ +public class DefaultSchedulerComponent implements SchedulerComponent { protected ScheduledExecutorService schedulerExecutor; @PostConstruct - public void init(){ + public void init() { this.schedulerExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("queue-scheduler")); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbQueueRemoteJsInvokeSettings.java b/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbQueueRemoteJsInvokeSettings.java index 25f3244079..15ff55972d 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbQueueRemoteJsInvokeSettings.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbQueueRemoteJsInvokeSettings.java @@ -24,6 +24,9 @@ import org.springframework.stereotype.Component; @Data @Component public class TbQueueRemoteJsInvokeSettings { + + @Value("${queue.prefix:}") + private String prefix; @Value("${queue.js.request_topic}") private String requestTopic; @@ -38,4 +41,12 @@ public class TbQueueRemoteJsInvokeSettings { @Value("${queue.js.max_requests_timeout}") private long maxRequestsTimeout; + + public String getRequestTopic(){ + return prefix.isBlank() ? requestTopic : prefix + "." + requestTopic; + } + + public String getResponseTopic(){ + return prefix.isBlank() ? responseTopic : prefix + "." + responseTopic; + } } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsAdmin.java b/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsAdmin.java index f88a34941a..ba4eeb6ca4 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsAdmin.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsAdmin.java @@ -26,6 +26,7 @@ import com.amazonaws.services.sqs.model.CreateQueueRequest; import com.amazonaws.services.sqs.model.GetQueueUrlResult; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.queue.TbQueueAdmin; +import org.thingsboard.server.queue.util.PropertyUtils; import java.util.Map; import java.util.function.Function; @@ -63,11 +64,12 @@ public class TbAwsSqsAdmin implements TbQueueAdmin { } @Override - public void createTopicIfNotExists(String topic) { + public void createTopicIfNotExists(String topic, String properties) { String queueName = convertTopicToQueueName(topic); if (queues.containsKey(queueName)) { return; } + Map attributes = PropertyUtils.getProps(this.attributes, properties, TbAwsSqsQueueAttributes::toConfigs); final CreateQueueRequest createQueueRequest = new CreateQueueRequest(queueName).withAttributes(attributes); String queueUrl = sqsClient.createQueue(createQueueRequest).getQueueUrl(); queues.put(getQueueNameFromUrl(queueUrl), queueUrl); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsQueueAttributes.java b/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsQueueAttributes.java index 66110ade74..faa8eccc90 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsQueueAttributes.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsQueueAttributes.java @@ -76,6 +76,12 @@ public class TbAwsSqsQueueAttributes { private Map getConfigs(String properties) { Map configs = new HashMap<>(defaultAttributes); + configs.putAll(toConfigs(properties)); + return configs; + } + + public static Map toConfigs(String properties) { + Map configs = new HashMap<>(); if (StringUtils.isNotEmpty(properties)) { for (String property : properties.split(";")) { int delimiterPosition = property.indexOf(":"); @@ -88,7 +94,7 @@ public class TbAwsSqsQueueAttributes { return configs; } - private void validateAttributeName(String key) { + private static void validateAttributeName(String key) { QueueAttributeName.fromValue(key); } } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/util/AfterStartUp.java b/common/queue/src/main/java/org/thingsboard/server/queue/util/AfterStartUp.java index e000735fba..d235acb4fa 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/util/AfterStartUp.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/util/AfterStartUp.java @@ -34,6 +34,7 @@ public @interface AfterStartUp { int QUEUE_INFO_INITIALIZATION = 1; int DISCOVERY_SERVICE = 2; + int STARTUP_SERVICE = 8; int ACTOR_SYSTEM = 9; int REGULAR_SERVICE = 10; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/util/PropertyUtils.java b/common/queue/src/main/java/org/thingsboard/server/queue/util/PropertyUtils.java index 089d7f2219..afee64f382 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/util/PropertyUtils.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/util/PropertyUtils.java @@ -19,6 +19,7 @@ import org.thingsboard.server.common.data.StringUtils; import java.util.HashMap; import java.util.Map; +import java.util.function.Function; public class PropertyUtils { @@ -37,4 +38,17 @@ public class PropertyUtils { return configs; } + public static Map getProps(Map defaultProperties, String propertiesStr) { + return getProps(defaultProperties, propertiesStr, PropertyUtils::getProps); + } + + public static Map getProps(Map defaultProperties, String propertiesStr, Function> parser) { + Map properties = defaultProperties; + if (StringUtils.isNotBlank(propertiesStr)) { + properties = new HashMap<>(properties); + properties.putAll(parser.apply(propertiesStr)); + } + return properties; + } + } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/util/ProtoWithFSTService.java b/common/queue/src/main/java/org/thingsboard/server/queue/util/ProtoWithFSTService.java index b0d0b182e9..204502068c 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/util/ProtoWithFSTService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/util/ProtoWithFSTService.java @@ -17,8 +17,10 @@ package org.thingsboard.server.queue.util; import lombok.extern.slf4j.Slf4j; import org.nustaq.serialization.FSTConfiguration; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.FSTUtils; +import org.thingsboard.server.common.data.FstStatsService; import java.util.Optional; @@ -26,12 +28,21 @@ import java.util.Optional; @Service public class ProtoWithFSTService implements DataDecodingEncodingService { + @Autowired + private FstStatsService fstStatsService; + public static final FSTConfiguration CONFIG = FSTConfiguration.createDefaultConfiguration(); @Override public Optional decode(byte[] byteArray) { try { - return Optional.ofNullable(FSTUtils.decode(byteArray)); + long startTime = System.nanoTime(); + Optional optional = Optional.ofNullable(FSTUtils.decode(byteArray)); + optional.ifPresent(obj -> { + fstStatsService.recordDecodeTime(obj.getClass(), startTime); + fstStatsService.incrementDecode(obj.getClass()); + }); + return optional; } catch (IllegalArgumentException e) { log.error("Error during deserialization message, [{}]", e.getMessage()); return Optional.empty(); @@ -41,7 +52,11 @@ public class ProtoWithFSTService implements DataDecodingEncodingService { @Override public byte[] encode(T msq) { - return FSTUtils.encode(msq); + long startTime = System.nanoTime(); + var bytes = FSTUtils.encode(msq); + fstStatsService.recordEncodeTime(msq.getClass(), startTime); + fstStatsService.incrementEncode(msq.getClass()); + return bytes; } diff --git a/common/queue/src/test/java/org/thingsboard/server/queue/discovery/ZkDiscoveryServiceTest.java b/common/queue/src/test/java/org/thingsboard/server/queue/discovery/ZkDiscoveryServiceTest.java new file mode 100644 index 0000000000..3af4683ab3 --- /dev/null +++ b/common/queue/src/test/java/org/thingsboard/server/queue/discovery/ZkDiscoveryServiceTest.java @@ -0,0 +1,192 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.queue.discovery; + +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.imps.CuratorFrameworkState; +import org.apache.curator.framework.recipes.cache.ChildData; +import org.apache.curator.framework.recipes.cache.PathChildrenCache; +import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.test.util.ReflectionTestUtils; +import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.gen.transport.TransportProtos; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +import static org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent.Type.CHILD_ADDED; +import static org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent.Type.CHILD_REMOVED; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class ZkDiscoveryServiceTest { + + @Mock + private ApplicationEventPublisher applicationEventPublisher; + @Mock + private TbServiceInfoProvider serviceInfoProvider; + + @Mock + private PartitionService partitionService; + + @Mock + private CuratorFramework client; + + @Mock + private PathChildrenCache cache; + + @Mock + private CuratorFramework curatorFramework; + + private ZkDiscoveryService zkDiscoveryService; + + private static final long RECALCULATE_DELAY = 100L; + + final TransportProtos.ServiceInfo currentInfo = TransportProtos.ServiceInfo.newBuilder().setServiceId("tb-rule-engine-0").build(); + final ChildData currentData = new ChildData("/thingsboard/nodes/0000000010", null, currentInfo.toByteArray()); + final TransportProtos.ServiceInfo childInfo = TransportProtos.ServiceInfo.newBuilder().setServiceId("tb-rule-engine-1").build(); + final ChildData childData = new ChildData("/thingsboard/nodes/0000000020", null, childInfo.toByteArray()); + + @Before + public void setup() { + zkDiscoveryService = Mockito.spy(new ZkDiscoveryService(applicationEventPublisher, serviceInfoProvider, partitionService)); + ScheduledExecutorService zkExecutorService = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("zk-discovery")); + when(client.getState()).thenReturn(CuratorFrameworkState.STARTED); + ReflectionTestUtils.setField(zkDiscoveryService, "stopped", false); + ReflectionTestUtils.setField(zkDiscoveryService, "client", client); + ReflectionTestUtils.setField(zkDiscoveryService, "cache", cache); + ReflectionTestUtils.setField(zkDiscoveryService, "nodePath", "/thingsboard/nodes/0000000010"); + ReflectionTestUtils.setField(zkDiscoveryService, "zkExecutorService", zkExecutorService); + ReflectionTestUtils.setField(zkDiscoveryService, "recalculateDelay", RECALCULATE_DELAY); + ReflectionTestUtils.setField(zkDiscoveryService, "zkDir", "/thingsboard"); + + when(serviceInfoProvider.getServiceInfo()).thenReturn(currentInfo); + + List dataList = new ArrayList<>(); + dataList.add(currentData); + when(cache.getCurrentData()).thenReturn(dataList); + } + + @Test + public void restartNodeInTimeTest() throws Exception { + startNode(childData); + + verify(partitionService, times(1)).recalculatePartitions(eq(currentInfo), eq(List.of(childInfo))); + + reset(partitionService); + + stopNode(childData); + + assertEquals(1, zkDiscoveryService.delayedTasks.size()); + + verify(partitionService, never()).recalculatePartitions(any(), any()); + + startNode(childData); + + verify(partitionService, never()).recalculatePartitions(any(), any()); + + Thread.sleep(RECALCULATE_DELAY * 2); + + verify(partitionService, never()).recalculatePartitions(any(), any()); + + assertTrue(zkDiscoveryService.delayedTasks.isEmpty()); + } + + @Test + public void restartNodeNotInTimeTest() throws Exception { + startNode(childData); + + verify(partitionService, times(1)).recalculatePartitions(eq(currentInfo), eq(List.of(childInfo))); + + reset(partitionService); + + stopNode(childData); + + assertEquals(1, zkDiscoveryService.delayedTasks.size()); + + Thread.sleep(RECALCULATE_DELAY * 2); + + assertTrue(zkDiscoveryService.delayedTasks.isEmpty()); + + startNode(childData); + + verify(partitionService, times(1)).recalculatePartitions(eq(currentInfo), eq(Collections.emptyList())); + + verify(partitionService, times(1)).recalculatePartitions(eq(currentInfo), eq(List.of(childInfo))); + + reset(partitionService); + } + + @Test + public void startAnotherNodeDuringRestartTest() throws Exception { + var anotherInfo = TransportProtos.ServiceInfo.newBuilder().setServiceId("tb-transport").build(); + var anotherData = new ChildData("/thingsboard/nodes/0000000030", null, anotherInfo.toByteArray()); + + startNode(childData); + + verify(partitionService, times(1)).recalculatePartitions(eq(currentInfo), eq(List.of(childInfo))); + + reset(partitionService); + + stopNode(childData); + + assertEquals(1, zkDiscoveryService.delayedTasks.size()); + + startNode(anotherData); + + assertTrue(zkDiscoveryService.delayedTasks.isEmpty()); + + verify(partitionService, times(1)).recalculatePartitions(eq(currentInfo), eq(List.of(anotherInfo))); + reset(partitionService); + + Thread.sleep(RECALCULATE_DELAY * 2); + + verify(partitionService, never()).recalculatePartitions(any(), any()); + + startNode(childData); + + verify(partitionService, times(1)).recalculatePartitions(eq(currentInfo), eq(List.of(anotherInfo, childInfo))); + } + + private void startNode(ChildData data) throws Exception { + cache.getCurrentData().add(data); + zkDiscoveryService.childEvent(curatorFramework, new PathChildrenCacheEvent(CHILD_ADDED, data)); + } + + private void stopNode(ChildData data) throws Exception { + cache.getCurrentData().remove(data); + zkDiscoveryService.childEvent(curatorFramework, new PathChildrenCacheEvent(CHILD_REMOVED, data)); + } + +} diff --git a/common/queue/src/test/java/org/thingsboard/server/queue/kafka/TbKafkaProducerTemplateTest.java b/common/queue/src/test/java/org/thingsboard/server/queue/kafka/TbKafkaProducerTemplateTest.java new file mode 100644 index 0000000000..bfd3c4a6dc --- /dev/null +++ b/common/queue/src/test/java/org/thingsboard/server/queue/kafka/TbKafkaProducerTemplateTest.java @@ -0,0 +1,54 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.queue.kafka; + +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.common.header.Header; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.thingsboard.server.queue.TbQueueMsg; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.willCallRealMethod; +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.Mockito.mock; + +@Slf4j +class TbKafkaProducerTemplateTest { + + TbKafkaProducerTemplate producerTemplate; + + @BeforeEach + void setUp() { + producerTemplate = mock(TbKafkaProducerTemplate.class); + willCallRealMethod().given(producerTemplate).addAnalyticHeaders(any()); + willReturn("tb-core-to-core-notifications-tb-core-3").given(producerTemplate).getClientId(); + } + + @Test + void testAddAnalyticHeaders() { + List
headers = new ArrayList<>(); + producerTemplate.addAnalyticHeaders(headers); + assertThat(headers).isNotEmpty(); + headers.forEach(r -> log.info("RecordHeader key [{}] value [{}]", r.key(), new String(r.value(), StandardCharsets.UTF_8))); + } + +} diff --git a/common/queue/src/test/resources/logback-test.xml b/common/queue/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..f7053313d4 --- /dev/null +++ b/common/queue/src/test/resources/logback-test.xml @@ -0,0 +1,20 @@ + + + + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + diff --git a/common/script/pom.xml b/common/script/pom.xml index 7a4304b77a..58630b78db 100644 --- a/common/script/pom.xml +++ b/common/script/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/script/remote-js-client/pom.xml b/common/script/remote-js-client/pom.xml index 53e222ad76..874c0d4d0c 100644 --- a/common/script/remote-js-client/pom.xml +++ b/common/script/remote-js-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT script org.thingsboard.common.script diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml index ae5f38f42f..d12eccd184 100644 --- a/common/script/script-api/pom.xml +++ b/common/script/script-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT script org.thingsboard.common.script diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java index ac196a8edd..ad45891cda 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/AbstractScriptInvokeService.java @@ -25,11 +25,8 @@ import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.stats.TbApiUsageReportClient; -import org.thingsboard.server.common.stats.TbApiUsageStateClient; import java.util.Map; -import java.util.Optional; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; @@ -45,9 +42,6 @@ import static java.lang.String.format; public abstract class AbstractScriptInvokeService implements ScriptInvokeService { protected final Map disabledScripts = new ConcurrentHashMap<>(); - - private final Optional apiUsageStateClient; - private final Optional apiUsageReportClient; private final AtomicInteger pushedMsgs = new AtomicInteger(0); private final AtomicInteger invokeMsgs = new AtomicInteger(0); private final AtomicInteger evalMsgs = new AtomicInteger(0); @@ -59,11 +53,6 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService protected ScheduledExecutorService timeoutExecutorService; - protected AbstractScriptInvokeService(Optional apiUsageStateClient, Optional apiUsageReportClient) { - this.apiUsageStateClient = apiUsageStateClient; - this.apiUsageReportClient = apiUsageReportClient; - } - protected long getMaxEvalRequestsTimeout() { return getMaxInvokeRequestsTimeout(); } @@ -88,6 +77,9 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService protected abstract boolean isScriptPresent(UUID scriptId); + protected abstract boolean isExecEnabled(TenantId tenantId); + protected abstract void reportExecution(TenantId tenantId, CustomerId customerId); + protected abstract ListenableFuture doEvalScript(TenantId tenantId, ScriptType scriptType, String scriptBody, UUID scriptId, String[] argNames); protected abstract TbScriptExecutionTask doInvokeFunction(UUID scriptId, Object[] args); @@ -122,7 +114,7 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService @Override public ListenableFuture eval(TenantId tenantId, ScriptType scriptType, String scriptBody, String... argNames) { - if (!apiUsageStateClient.isPresent() || apiUsageStateClient.get().getApiUsageState(tenantId).isJsExecEnabled()) { + if (isExecEnabled(tenantId)) { if (scriptBodySizeExceeded(scriptBody)) { return error(format("Script body exceeds maximum allowed size of %s symbols", getMaxScriptBodySize())); } @@ -137,7 +129,7 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService @Override public ListenableFuture invokeScript(TenantId tenantId, CustomerId customerId, UUID scriptId, Object... args) { - if (!apiUsageStateClient.isPresent() || apiUsageStateClient.get().getApiUsageState(tenantId).isJsExecEnabled()) { + if (isExecEnabled(tenantId)) { if (!isScriptPresent(scriptId)) { return error("No compiled script found for scriptId: [" + scriptId + "]!"); } @@ -148,9 +140,9 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService )); return Futures.immediateFailedFuture(handleScriptException(scriptId, null, t)); } - apiUsageReportClient.ifPresent(client -> client.report(tenantId, customerId, ApiUsageRecordKey.JS_EXEC_COUNT, 1)); + reportExecution(tenantId, customerId); pushedMsgs.incrementAndGet(); - log.trace("InvokeScript uuid {} with timeout {}ms", scriptId, getMaxInvokeRequestsTimeout()); + log.trace("[{}] InvokeScript uuid {} with timeout {}ms", tenantId, scriptId, getMaxInvokeRequestsTimeout()); var task = doInvokeFunction(scriptId, args); var resultFuture = Futures.transformAsync(task.getResultFuture(), output -> { @@ -167,7 +159,7 @@ public abstract class AbstractScriptInvokeService implements ScriptInvokeService } else { String message = "Script invocation is blocked due to maximum error count " + getMaxErrors() + ", scriptId " + scriptId + "!"; - log.warn(message); + log.warn("[{}] " + message, tenantId); return error(message); } } else { diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/js/AbstractJsInvokeService.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/js/AbstractJsInvokeService.java index df84876869..192997f6a7 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/js/AbstractJsInvokeService.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/js/AbstractJsInvokeService.java @@ -23,6 +23,8 @@ import org.springframework.beans.factory.annotation.Value; import org.thingsboard.script.api.AbstractScriptInvokeService; import org.thingsboard.script.api.RuleNodeScriptFactory; import org.thingsboard.script.api.ScriptType; +import org.thingsboard.server.common.data.ApiUsageRecordKey; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.stats.TbApiUsageReportClient; import org.thingsboard.server.common.stats.TbApiUsageStateClient; @@ -39,6 +41,8 @@ import java.util.concurrent.ConcurrentHashMap; public abstract class AbstractJsInvokeService extends AbstractScriptInvokeService implements JsInvokeService { protected final Map scriptInfoMap = new ConcurrentHashMap<>(); + private final Optional apiUsageStateClient; + private final Optional apiUsageReportClient; @Getter @Value("${js.max_total_args_size:100000}") @@ -51,7 +55,8 @@ public abstract class AbstractJsInvokeService extends AbstractScriptInvokeServic private long maxScriptBodySize; protected AbstractJsInvokeService(Optional apiUsageStateClient, Optional apiUsageReportClient) { - super(apiUsageStateClient, apiUsageReportClient); + this.apiUsageStateClient = apiUsageStateClient; + this.apiUsageReportClient = apiUsageReportClient; } @Override @@ -59,6 +64,16 @@ public abstract class AbstractJsInvokeService extends AbstractScriptInvokeServic return scriptInfoMap.containsKey(scriptId); } + @Override + protected boolean isExecEnabled(TenantId tenantId) { + return !apiUsageStateClient.isPresent() || apiUsageStateClient.get().getApiUsageState(tenantId).isJsExecEnabled(); + } + + @Override + protected void reportExecution(TenantId tenantId, CustomerId customerId) { + apiUsageReportClient.ifPresent(client -> client.report(tenantId, customerId, ApiUsageRecordKey.JS_EXEC_COUNT, 1)); + } + @Override protected JsScriptExecutionTask doInvokeFunction(UUID scriptId, Object[] args) { return new JsScriptExecutionTask(doInvokeFunction(scriptId, scriptInfoMap.get(scriptId), args)); diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/DateTimeFormatOptions.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/DateTimeFormatOptions.java new file mode 100644 index 0000000000..93d35c55b4 --- /dev/null +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/DateTimeFormatOptions.java @@ -0,0 +1,65 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.script.api.tbel; + +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.StringUtils; + +import java.time.format.FormatStyle; +import java.util.TimeZone; + +@NoArgsConstructor +@Data +class DateTimeFormatOptions { + private static final TimeZone DEFAULT_TZ = TimeZone.getDefault(); + + private String timeZone; + private String dateStyle; + private String timeStyle; + @Getter + private String pattern; + + public DateTimeFormatOptions(String timeZone) { + this.timeZone = timeZone; + } + + TimeZone getTimeZone() { + return StringUtils.isNotEmpty(timeZone) ? TimeZone.getTimeZone(timeZone) : TimeZone.getDefault(); + } + + FormatStyle getDateStyle() { + return getFormatStyle(dateStyle, FormatStyle.SHORT); + } + + FormatStyle getTimeStyle() { + return getFormatStyle(timeStyle, FormatStyle.MEDIUM); + } + + private static FormatStyle getFormatStyle(String style, FormatStyle defaultStyle) { + if (StringUtils.isNotEmpty(style)) { + try { + return FormatStyle.valueOf(style.toUpperCase()); + } catch (IllegalArgumentException e) { + return defaultStyle; + } + } else { + return defaultStyle; + } + } + +} diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/DefaultTbelInvokeService.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/DefaultTbelInvokeService.java index f98e33b615..221d68c33b 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/DefaultTbelInvokeService.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/DefaultTbelInvokeService.java @@ -25,6 +25,7 @@ import com.google.common.util.concurrent.MoreExecutors; import lombok.Getter; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import org.mvel2.CompileException; import org.mvel2.ExecutionContext; import org.mvel2.MVEL; import org.mvel2.ParserContext; @@ -39,6 +40,8 @@ import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.script.api.AbstractScriptInvokeService; import org.thingsboard.script.api.ScriptType; import org.thingsboard.script.api.TbScriptException; +import org.thingsboard.server.common.data.ApiUsageRecordKey; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.stats.TbApiUsageReportClient; import org.thingsboard.server.common.stats.TbApiUsageStateClient; @@ -68,6 +71,8 @@ public class DefaultTbelInvokeService extends AbstractScriptInvokeService implem protected Cache compiledScriptsCache; private SandboxedParserConfiguration parserConfig; + private final Optional apiUsageStateClient; + private final Optional apiUsageReportClient; @Getter @Value("${tbel.max_total_args_size:100000}") @@ -109,7 +114,8 @@ public class DefaultTbelInvokeService extends AbstractScriptInvokeService implem private final Lock lock = new ReentrantLock(); protected DefaultTbelInvokeService(Optional apiUsageStateClient, Optional apiUsageReportClient) { - super(apiUsageStateClient, apiUsageReportClient); + this.apiUsageStateClient = apiUsageStateClient; + this.apiUsageReportClient = apiUsageReportClient; } @Scheduled(fixedDelayString = "${tbel.stats.print_interval_ms:10000}") @@ -166,6 +172,16 @@ public class DefaultTbelInvokeService extends AbstractScriptInvokeService implem return scriptIdToHash.containsKey(scriptId); } + @Override + protected boolean isExecEnabled(TenantId tenantId) { + return !apiUsageStateClient.isPresent() || apiUsageStateClient.get().getApiUsageState(tenantId).isTbelExecEnabled(); + } + + @Override + protected void reportExecution(TenantId tenantId, CustomerId customerId) { + apiUsageReportClient.ifPresent(client -> client.report(tenantId, customerId, ApiUsageRecordKey.TBEL_EXEC_COUNT, 1)); + } + @Override protected ListenableFuture doEvalScript(TenantId tenantId, ScriptType scriptType, String scriptBody, UUID scriptId, String[] argNames) { return executor.submit(() -> { diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbDate.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbDate.java index 9c360e5c7e..9bab4060ab 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbDate.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbDate.java @@ -15,80 +15,455 @@ */ package org.thingsboard.script.api.tbel; +import com.fasterxml.jackson.annotation.JsonValue; +import org.mvel2.ConversionException; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.StringUtils; + +import java.io.Serializable; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeParseException; +import java.time.format.FormatStyle; import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalAccessor; -import java.util.Date; -import java.util.GregorianCalendar; +import java.util.Arrays; import java.util.Locale; -import java.util.TimeZone; +import java.util.function.BiFunction; + +public class TbDate implements Serializable, Cloneable { + + private Instant instant; -public class TbDate extends Date { + private static final ZoneId zoneIdUTC = ZoneId.of("UTC"); + private static final Locale localeUTC = Locale.forLanguageTag("UTC"); private static final DateTimeFormatter isoDateFormatter = DateTimeFormatter.ofPattern( "yyyy-MM-dd[[ ]['T']HH:mm[:ss[.SSS]][ ][XXX][Z][z][VV][O]]").withZone(ZoneId.systemDefault()); - private static final DateFormat isoDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); - public TbDate() { - super(); + this.instant = Instant.now(); } public TbDate(String s) { - super(parse(s)); + this.instant = parseInstant(s); } - public TbDate(long date) { - super(date); + public TbDate(String s, String pattern) { + this.instant = parseInstant(s, Locale.getDefault().toLanguageTag(), pattern); } - public TbDate(int year, int month, int date) { - this(year, month, date, 0, 0, 0); + public TbDate(String s, String pattern, String locale) { + this.instant = parseInstant(s, locale, pattern); } - public TbDate(int year, int month, int date, int hrs, int min) { - this(year, month, date, hrs, min, 0); + public TbDate(String s, String pattern, String locale, String zoneId) { + this.instant = parseInstant(s, pattern, locale, zoneId); } - public TbDate(int year, int month, int date, - int hrs, int min, int second) { - super(new GregorianCalendar(year, month, date, hrs, min, second).getTimeInMillis()); + public TbDate(long dateMilliSecond) { + instant = Instant.ofEpochMilli(dateMilliSecond); } - public String toDateString() { - DateFormat formatter = DateFormat.getDateInstance(); - return formatter.format(this); + public TbDate(int year, int month, int date, String... tz) { + this(year, month, date, 0, 0, 0, 0, tz); + } + + public TbDate(int year, int month, int date, int hrs, int min, String... tz) { + this(year, month, date, hrs, min, 0, 0, tz); + } + + public TbDate(int year, int month, int date, int hrs, int min, int second, String... tz) { + this(year, month, date, hrs, min, second, 0, tz); + } + + public TbDate(int year, int month, int date, int hrs, int min, int second, int secondMilli, String... tz) { + ZoneId zoneId = tz.length > 0 ? ZoneId.of(Arrays.stream(tz).findFirst().get()) : ZoneId.systemDefault(); + instant = parseInstant(year, month, date, hrs, min, second, secondMilli, zoneId); } + public Instant getInstant() { + return instant; + } + + public ZonedDateTime getZonedDateTime() { + return getZonedDateTime(zoneIdUTC); + } + public ZonedDateTime getZonedDateTime(ZoneId zoneId) { + return instant.atZone(zoneId); + } + + public LocalDateTime getLocalDateTime() { + return LocalDateTime.ofInstant(this.instant, ZoneId.systemDefault()); + } + + public LocalDateTime getUTCDateTime() { + return LocalDateTime.ofInstant(this.instant, zoneIdUTC); + } + + public String toDateString() { + return toDateString(localeUTC.getLanguage()); + } + public String toDateString(String locale) { + return toDateString(locale, ZoneId.systemDefault().toString()); + } + public String toDateString(String localeStr, String optionsStr) { + return toLocaleString(localeStr, optionsStr, (locale, options) -> DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).withLocale(locale)); + } public String toTimeString() { - DateFormat formatter = DateFormat.getTimeInstance(DateFormat.LONG); - return formatter.format(this); + return toTimeString(Locale.getDefault().getLanguage()); + } + public String toTimeString(String locale) { + return toTimeString(locale, ZoneId.systemDefault().toString()); + } + public String toTimeString(String localeStr, String optionsStr) { + return toLocaleString(localeStr, optionsStr, (locale, options) -> DateTimeFormatter.ofLocalizedTime(FormatStyle.FULL).withLocale(locale)); } public String toISOString() { - return isoDateFormat.format(this); + return instant.toString(); + } + public String toJSON() { + return toISOString(); + } + public String toUTCString() { + return toUTCString(localeUTC.getLanguage()); + } + + public String toUTCString(String localeStr) { + return toLocaleString(localeStr, zoneIdUTC.getId(), (locale, options) -> DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.MEDIUM).withLocale(locale)); + } + + @JsonValue + public String toString() { + return toString(Locale.getDefault().getLanguage()); + } + + public String toString(String locale) { + return toString(locale, ZoneId.systemDefault().toString()); + } + + public String toString(String localeStr, String optionsStr) { + return toLocaleString(localeStr, optionsStr, (locale, options) -> DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.FULL).withLocale(locale)); + } + + public String toLocaleDateString() { + return toLocaleDateString(localeUTC.getLanguage()); + } + + public String toLocaleDateString(String localeStr) { + return toLocaleString(localeStr, ZoneId.systemDefault().toString(), (locale, options) -> DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).withLocale(locale)); + } + + public String toLocaleDateString(String localeStr, String optionsStr) { + return toLocaleString(localeStr, optionsStr, (locale, options) -> DateTimeFormatter.ofLocalizedDate(options.getDateStyle()).withLocale(locale)); + } + + public String toLocaleTimeString() { + return toLocaleTimeString(Locale.getDefault().getLanguage()); + } + + public String toLocaleTimeString(String localeStr) { + return toLocaleTimeString(localeStr, ZoneId.systemDefault().toString()); + } + + public String toLocaleTimeString(String localeStr, String optionsStr) { + return toLocaleString(localeStr, optionsStr, (locale, options) -> DateTimeFormatter.ofLocalizedTime(options.getTimeStyle()).withLocale(locale)); + } + + public String toLocaleString() { + return toLocaleString(localeUTC.getLanguage(), ZoneId.systemDefault().toString()); } public String toLocaleString(String locale) { - DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, Locale.forLanguageTag(locale)); - return formatter.format(this); + return toLocaleString(locale, ZoneId.systemDefault().toString()); + } + + public String toLocaleString(String localeStr, String optionsStr) { + return toLocaleString(localeStr, optionsStr, (locale, options) -> + DateTimeFormatter.ofLocalizedDateTime(options.getDateStyle(), options.getTimeStyle()).withLocale(locale)); } - public String toLocaleString(String locale, String tz) { - DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, Locale.forLanguageTag(locale)); - formatter.setTimeZone(TimeZone.getTimeZone(tz)); - return formatter.format(this); + public String toLocaleString(String localeStr, String optionsStr, BiFunction formatterBuilder) { + Locale locale = StringUtils.isNotEmpty(localeStr) ? Locale.forLanguageTag(localeStr) : Locale.getDefault(); + DateTimeFormatOptions options = getDateFormattingOptions(optionsStr); + ZonedDateTime zdt = this.getInstant().atZone(options.getTimeZone().toZoneId()); + DateTimeFormatter formatter; + if (StringUtils.isNotEmpty(options.getPattern())) { + formatter = new DateTimeFormatterBuilder().appendPattern(options.getPattern()).toFormatter(locale); + } else { + formatter = formatterBuilder.apply(locale, options); + } + return formatter.format(zdt); + } + + private DateTimeFormatOptions getDateFormattingOptions(String optionsStr) { + DateTimeFormatOptions opt = null; + if (StringUtils.isNotEmpty(optionsStr)) { + try { + opt = JacksonUtil.fromString(optionsStr, DateTimeFormatOptions.class); + } catch (IllegalArgumentException iae) { + opt = new DateTimeFormatOptions(optionsStr); + } + } + if (opt == null) { + opt = new DateTimeFormatOptions(); + } + return opt; } public static long now() { - return System.currentTimeMillis(); + return Instant.now().toEpochMilli(); + } + public long parseSecond() { + return instant.getEpochSecond(); + } + + public long parseSecondMilli() { + return instant.toEpochMilli(); + } + + public static long UTC(int year) { + return UTC(year, 0, 0, 0, 0, 0, 0); + } + public static long UTC(int year, int month) { + return UTC(year, month, 0, 0, 0, 0, 0); + } + public static long UTC(int year, int month, int date) { + return UTC(year, month, date, 0, 0, 0, 0); + } + public static long UTC(int year, int month, int date, int hrs) { + return UTC(year, month, date, hrs, 0, 0, 0); + } + public static long UTC(int year, int month, int date, int hrs, int min) { + return UTC(year, month, date, hrs, min, 0, 0); + } + public static long UTC(int year, int month, int date, int hrs, int min, int sec) { + return UTC(year, month, date, hrs, min, sec, 0); + } + public static long UTC(int year, int month, int date, int hrs, int min, int sec, int ms) { + year = year == 0 ? year = 1899 : year; + month = month == 0 ? month = 12 : month; + date = date == 0 ? date = 31 : date; + return parseInstant(year, month, date, hrs, min, sec, ms, zoneIdUTC).toEpochMilli(); + } + public int getUTCFullYear() { + return getUTCDateTime().getYear(); + } + + public int getUTCMonth() { + return getUTCDateTime().getMonthValue(); + } + // day in month + public int getUTCDate() { + return getUTCDateTime().getDayOfMonth(); + } + // day in week + public int getUTCDay() { + return getUTCDateTime().getDayOfWeek().getValue(); + } + + public int getUTCHours() { + return getUTCDateTime().getHour(); + } + + public int getUTCMinutes() { + return getZonedDateTime().getMinute(); + } + + public int getUTCSeconds() { + return getUTCDateTime().getSecond(); + } + public int getUTCMilliseconds() { + return getUTCDateTime().getNano()/1000000; + } + + public void setUTCFullYear(int year) { + if (getUTCDate() > 28) { + long time = getZonedDateTime().withYear(year).withDayOfMonth(1).toInstant().toEpochMilli() + (getUTCDate() - 1) * 24 * 60 * 60 * 1000L; + this.instant = Instant.ofEpochMilli(time); + } else { + this.instant = getZonedDateTime().withYear(year).toInstant(); + } + } + public void setUTCFullYear(int year, int month) { + if (getUTCDate() > 28) { + long time = getZonedDateTime().withYear(year).withMonth(month).withDayOfMonth(1).toInstant().toEpochMilli() + (getUTCDate() - 1) * 24 * 60 * 60 * 1000L; + this.instant = Instant.ofEpochMilli(time); + } else { + this.instant = getZonedDateTime().withYear(year).withMonth(month).toInstant(); + } + } + public void setUTCFullYear(int year, int month, int date) { + this.instant = getZonedDateTime().withYear(year).withMonth(month).withDayOfMonth(date).toInstant(); + } + public void setUTCMonth(int month) { + if (getUTCDate() > 28) { + long time = getZonedDateTime().withMonth(month).withDayOfMonth(1).toInstant().toEpochMilli() + (getUTCDate() - 1) * 24 * 60 * 60 * 1000L; + this.instant = Instant.ofEpochMilli(time); + } else { + this.instant = getZonedDateTime().withMonth(month).toInstant(); + } + } + public void setUTCMonth(int month, int date) { + this.instant = getZonedDateTime().withMonth(month).withDayOfMonth(date).toInstant(); + } + public void setUTCDate(int date) { + this.instant = getZonedDateTime().withDayOfMonth(date).toInstant(); + } + public void setUTCHours(int hrs) { + this.instant = getZonedDateTime().withHour(hrs).toInstant(); + } + public void setUTCHours(int hrs, int minutes) { + this.instant = getZonedDateTime().withHour(hrs).withMinute(minutes).toInstant(); + } + public void setUTCHours(int hrs, int minutes, int seconds) { + this.instant = getZonedDateTime().withHour(hrs).withMinute(minutes).withSecond(seconds).toInstant(); + } + public void setUTCHours(int hrs, int minutes, int seconds, int ms) { + this.instant = getZonedDateTime().withHour(hrs).withMinute(minutes).withSecond(seconds).withNano(ms*1000000).toInstant(); + } + public void setUTCMinutes(int minutes) { + this.instant = getZonedDateTime().withMinute(minutes).toInstant(); + } + public void setUTCMinutes(int minutes, int seconds) { + this.instant = getZonedDateTime().withMinute(minutes).withSecond(seconds).toInstant(); } + public void setUTCMinutes(int minutes, int seconds, int ms) { + this.instant = parseInstant(getUTCFullYear(), getUTCMonth(), getUTCDate(), getUTCHours(), minutes, seconds, ms, zoneIdUTC); + } + public void setUTCSeconds(int seconds) { + this.instant = getZonedDateTime().withSecond(seconds).toInstant(); + } + public void setUTCSeconds(int seconds, int ms) { + this.instant = getZonedDateTime().withSecond(seconds).withNano(ms*1000000).toInstant(); + } + public void setUTCMilliseconds(int ms) { + this.instant = getZonedDateTime().withNano(ms*1000000).toInstant(); + } + public int getFullYear() { + return getLocalDateTime().getYear(); + } + + public int getMonth() { + return getLocalDateTime().getMonthValue(); + } + // day in month + public int getDate() { + return getLocalDateTime().getDayOfMonth(); + } + // day in week + public int getDay() { + return getLocalDateTime().getDayOfWeek().getValue(); + } + + public int getHours() { + return getLocalDateTime().getHour(); + } + + public int getMinutes() { + return getLocalDateTime().getMinute(); + } + + public int getSeconds() { + return getLocalDateTime().getSecond(); + } + public int getMilliseconds() { + return getLocalDateTime().getNano()/1000000; + } + // Milliseconds since Jan 1, 1970, 00:00:00.000 GMT + public long getTime() { + return instant.toEpochMilli(); + } + public long valueOf(){ + return getTime() ; + } + public void setFullYear(int year) { + Instant instantEpochWithYear = getZonedDateTime().withYear(year).toInstant(); + if (getDate() > 28) { + long time = getLocalDateTime().withYear(year).withDayOfMonth(1).toInstant(getLocaleZoneOffset(instantEpochWithYear)).toEpochMilli() + (getDate() - 1) * 24 * 60 * 60 * 1000L; + this.instant = Instant.ofEpochMilli(time); + } else { + this.instant = getLocalDateTime().withYear(year).toInstant(getLocaleZoneOffset(instantEpochWithYear)); + } + } + public void setFullYear(int year, int month) { + Instant instantEpochWithYear = getZonedDateTime().withYear(year).withMonth(month).toInstant(); + if (getDate() > 28) { + long time = getLocalDateTime().withYear(year).withMonth(month).withDayOfMonth(1).toInstant(getLocaleZoneOffset(instantEpochWithYear)).toEpochMilli() + (getDate() - 1) * 24 * 60 * 60 * 1000L; + this.instant = Instant.ofEpochMilli(time); + } else { + this.instant = getLocalDateTime().withYear(year).withMonth(month).toInstant(getLocaleZoneOffset(instantEpochWithYear)); + } + } + public void setFullYear(int year, int month, int date) { + Instant instantEpochWithYearMonthDate = getZonedDateTime().withYear(year).withMonth(month).withDayOfMonth(date).toInstant(); + this.instant = getLocalDateTime().withYear(year).withMonth(month).withDayOfMonth(date).toInstant(getLocaleZoneOffset(instantEpochWithYearMonthDate)); + } + public void setMonth(int month) { + Instant instantEpochWithYear = getZonedDateTime().withMonth(month).toInstant(); + if (getDate() > 28) { + long time = getLocalDateTime().withMonth(month).withDayOfMonth(1).toInstant(getLocaleZoneOffset(instantEpochWithYear)).toEpochMilli() + (getDate() - 1) * 24 * 60 * 60 * 1000L; + this.instant = Instant.ofEpochMilli(time); + } else { + this.instant = getLocalDateTime().withMonth(month).toInstant(getLocaleZoneOffset(instantEpochWithYear)); + } + + } + public void setMonth(int month, int date) { + Instant instantEpochWithMonthDate = getZonedDateTime().withMonth(month).withDayOfMonth(date).toInstant(); + this.instant = getLocalDateTime().withMonth(month).withDayOfMonth(date).toInstant(getLocaleZoneOffset(instantEpochWithMonthDate)); + } + public void setDate(int date) { + Instant instantEpochWithDate = getZonedDateTime().withDayOfMonth(date).toInstant(); + this.instant = getLocalDateTime().withDayOfMonth(date).toInstant(getLocaleZoneOffset(instantEpochWithDate)); + } + public void setHours(int hrs) { + this.instant = getLocalDateTime().withHour(hrs).toInstant(getLocaleZoneOffset(this.instant)); + } + public void setHours(int hrs, int minutes) { + this.instant = getLocalDateTime().withHour(hrs).withMinute(minutes).toInstant(getLocaleZoneOffset(this.instant)); + } + public void setHours(int hrs, int minutes, int seconds) { + this.instant = getLocalDateTime().withHour(hrs).withMinute(minutes).withSecond(seconds).toInstant(getLocaleZoneOffset(this.instant)); + } + public void setHours(int hrs, int minutes, int seconds, int ms) { + this.instant = getLocalDateTime().withHour(hrs).withMinute(minutes).withSecond(seconds).withNano(ms*1000000).toInstant(getLocaleZoneOffset(this.instant)); + } + public void setMinutes(int minutes) { + this.instant = getLocalDateTime().withMinute(minutes).toInstant(getLocaleZoneOffset(this.instant)); + } + public void setMinutes(int minutes, int seconds) { + this.instant = getLocalDateTime().withMinute(minutes).withSecond(seconds).toInstant(getLocaleZoneOffset(this.instant)); + + } + public void setMinutes(int minutes, int seconds, int ms) { + this.instant = getLocalDateTime().withMinute(minutes).withSecond(seconds).withNano(ms*1000000).toInstant(getLocaleZoneOffset(this.instant)); + } + public void setSeconds(int seconds) { + this.instant = getLocalDateTime().withSecond(seconds).toInstant(getLocaleZoneOffset(this.instant)); + } + public void setSeconds(int seconds, int ms) { + this.instant = getLocalDateTime().withSecond(seconds).withNano(ms*1000000).toInstant(getLocaleZoneOffset(this.instant)); + } + public void setMilliseconds(int ms) { + this.instant = getLocalDateTime().withNano(ms*1000000).toInstant(getLocaleZoneOffset(this.instant)); + } + + // Milliseconds since Jan 1, 1970, 00:00:00.000 GMT + public void setTime(long dateMilliSecond) { + instant = Instant.ofEpochMilli(dateMilliSecond); + } + + public ZoneOffset getLocaleZoneOffset(Instant... instants){ + return ZoneId.systemDefault().getRules().getOffset(instants.length > 0 ? instants[0] : this.instant); } public static long parse(String value, String format) { @@ -99,7 +474,6 @@ public class TbDate extends Date { return -1; } } - public static long parse(String value) { try { TemporalAccessor accessor = isoDateFormatter.parseBest(value, @@ -109,17 +483,87 @@ public class TbDate extends Date { Instant instant = Instant.from(accessor); return Instant.EPOCH.until(instant, ChronoUnit.MILLIS); } catch (Exception e) { + return -1; + } + } + + private static Instant parseInstant(String s) { + boolean isIsoFormat = s.length() > 0 && Character.isDigit(s.charAt(0)); + if (isIsoFormat) { + return getInstant_ISO_OFFSET_DATE_TIME(s); + } else { + return getInstant_RFC_1123(s); + } + } + + private static Instant parseInstant(String s, String localeStr, String pattern) { + try { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern, Locale.forLanguageTag(localeStr)); + return Instant.from(formatter.parse(s)); + } catch (Exception ex) { try { - return Date.parse(value); - } catch (IllegalArgumentException e2) { - return -1; + return parseInstant(s, pattern, localeStr, ZoneId.systemDefault().getId()); + } catch (final DateTimeParseException e) { + final ConversionException exception = new ConversionException("Cannot parse value [" + s + "] as instant", ex); + throw exception; } } } - public static long UTC(int year, int month, int date, - int hrs, int min, int sec) { - return Date.UTC(year - 1900, month, date, hrs, min, sec); + private static Instant parseInstant(int year, int month, int date, int hrs, int min, int second, int secondMilli, ZoneId zoneId) { + year = year < 70 ? 2000 + year : year <= 99 ? 1900 + year : year; + ZonedDateTime zonedDateTime = ZonedDateTime.of(year, month, date, hrs, min, second, secondMilli*1000000, zoneId); + return zonedDateTime.toInstant(); + } + private static Instant parseInstant(String s, String pattern, String localeStr, String zoneIdStr) { + DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern, Locale.forLanguageTag(localeStr)); + LocalDateTime localDateTime = LocalDateTime.parse(s, dateTimeFormatter); + ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of(zoneIdStr)); + return zonedDateTime.toInstant(); } + private static Instant getInstant_ISO_OFFSET_DATE_TIME(String s) { + // assuming "2007-12-03T10:15:30.00Z" UTC instant + // assuming "2007-12-03T10:15:30.00" ZoneId.systemDefault() instant + // assuming "2007-12-03T10:15:30.00-04:00" TZ instant + // assuming "2007-12-03T10:15:30.00+04:00" TZ instant + DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME; + try { + return Instant.from(formatter.parse(s)); + } catch (DateTimeParseException ex) { + try { + long timeMS = parse(s); + if (timeMS != -1) { + return Instant.ofEpochMilli(timeMS); + } else { + throw new ConversionException("Cannot parse value [" + s + "] as instant"); + } + } catch (final DateTimeParseException e) { + throw new ConversionException("Cannot parse value [" + s + "] as instant"); + } + } + } + private static Instant getInstant_RFC_1123(String s) { + // assuming RFC-1123 value "Tue, 3 Jun 2008 11:05:30 GMT" + // assuming RFC-1123 value "Tue, 3 Jun 2008 11:05:30 GMT-02:00" + // assuming RFC-1123 value "Tue, 3 Jun 2008 11:05:30 -0200" + DateTimeFormatter formatter = DateTimeFormatter.RFC_1123_DATE_TIME; + try { + return Instant.from(formatter.parse(s)); + } catch (DateTimeParseException ex) { + try { + return getInstantWithLocalZoneOffsetId_RFC_1123(s); + } catch (final DateTimeParseException e) { + throw new ConversionException("Cannot parse value [" + s + "] as instant"); + } + } + } + private static Instant getInstantWithLocalZoneOffsetId_RFC_1123(String value) { + String s = value.trim() + " GMT"; + Instant instant = Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(s)); + ZoneId systemZone = ZoneId.systemDefault(); // my timezone + String id = systemZone.getRules().getOffset(instant).getId(); + value = value.trim() + " " + id.replaceAll(":", ""); + return Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(value)); + } } diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java index 6c50561363..4a3f2d306b 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java @@ -15,6 +15,8 @@ */ package org.thingsboard.script.api.tbel; +import com.google.common.primitives.Bytes; +import org.apache.commons.lang3.ArrayUtils; import org.mvel2.ExecutionContext; import org.mvel2.ParserConfiguration; import org.mvel2.execution.ExecutionArrayList; @@ -25,13 +27,16 @@ import org.thingsboard.server.common.data.StringUtils; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; +import java.math.BigInteger; import java.math.RoundingMode; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.Base64; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -53,14 +58,21 @@ public class TbUtils { List.class))); parserConfig.addImport("decodeToJson", new MethodStub(TbUtils.class.getMethod("decodeToJson", ExecutionContext.class, List.class))); - parserConfig.addImport("stringToBytes", new MethodStub(TbUtils.class.getMethod("stringToBytes", + parserConfig.addImport("decodeToJson", new MethodStub(TbUtils.class.getMethod("decodeToJson", ExecutionContext.class, String.class))); parserConfig.addImport("stringToBytes", new MethodStub(TbUtils.class.getMethod("stringToBytes", - ExecutionContext.class, String.class, String.class))); + ExecutionContext.class, Object.class))); + parserConfig.addImport("stringToBytes", new MethodStub(TbUtils.class.getMethod("stringToBytes", + ExecutionContext.class, Object.class, String.class))); + parserConfig.registerNonConvertableMethods(TbUtils.class, Collections.singleton("stringToBytes")); parserConfig.addImport("parseInt", new MethodStub(TbUtils.class.getMethod("parseInt", String.class))); parserConfig.addImport("parseInt", new MethodStub(TbUtils.class.getMethod("parseInt", String.class, int.class))); + parserConfig.addImport("parseLong", new MethodStub(TbUtils.class.getMethod("parseLong", + String.class))); + parserConfig.addImport("parseLong", new MethodStub(TbUtils.class.getMethod("parseLong", + String.class, int.class))); parserConfig.addImport("parseFloat", new MethodStub(TbUtils.class.getMethod("parseFloat", String.class))); parserConfig.addImport("parseDouble", new MethodStub(TbUtils.class.getMethod("parseDouble", @@ -81,8 +93,58 @@ public class TbUtils { byte[].class, int.class, int.class))); parserConfig.addImport("parseBytesToInt", new MethodStub(TbUtils.class.getMethod("parseBytesToInt", byte[].class, int.class, int.class, boolean.class))); + parserConfig.addImport("parseLittleEndianHexToLong", new MethodStub(TbUtils.class.getMethod("parseLittleEndianHexToLong", + String.class))); + parserConfig.addImport("parseBigEndianHexToLong", new MethodStub(TbUtils.class.getMethod("parseBigEndianHexToLong", + String.class))); + parserConfig.addImport("parseHexToLong", new MethodStub(TbUtils.class.getMethod("parseHexToLong", + String.class))); + parserConfig.addImport("parseHexToLong", new MethodStub(TbUtils.class.getMethod("parseHexToLong", + String.class, boolean.class))); + parserConfig.addImport("parseBytesToLong", new MethodStub(TbUtils.class.getMethod("parseBytesToLong", + List.class, int.class, int.class))); + parserConfig.addImport("parseBytesToLong", new MethodStub(TbUtils.class.getMethod("parseBytesToLong", + List.class, int.class, int.class, boolean.class))); + parserConfig.addImport("parseBytesToLong", new MethodStub(TbUtils.class.getMethod("parseBytesToLong", + byte[].class, int.class, int.class))); + parserConfig.addImport("parseBytesToLong", new MethodStub(TbUtils.class.getMethod("parseBytesToLong", + byte[].class, int.class, int.class, boolean.class))); + parserConfig.addImport("parseLittleEndianHexToFloat", new MethodStub(TbUtils.class.getMethod("parseLittleEndianHexToFloat", + String.class))); + parserConfig.addImport("parseBigEndianHexToFloat", new MethodStub(TbUtils.class.getMethod("parseBigEndianHexToFloat", + String.class))); + parserConfig.addImport("parseHexToFloat", new MethodStub(TbUtils.class.getMethod("parseHexToFloat", + String.class))); + parserConfig.addImport("parseHexToFloat", new MethodStub(TbUtils.class.getMethod("parseHexToFloat", + String.class, boolean.class))); + parserConfig.addImport("parseBytesToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesToFloat", + byte[].class, int.class, boolean.class))); + parserConfig.addImport("parseBytesToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesToFloat", + byte[].class, int.class))); + parserConfig.addImport("parseBytesToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesToFloat", + List.class, int.class, boolean.class))); + parserConfig.addImport("parseBytesToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesToFloat", + List.class, int.class))); + parserConfig.addImport("parseLittleEndianHexToDouble", new MethodStub(TbUtils.class.getMethod("parseLittleEndianHexToDouble", + String.class))); + parserConfig.addImport("parseBigEndianHexToDouble", new MethodStub(TbUtils.class.getMethod("parseBigEndianHexToDouble", + String.class))); + parserConfig.addImport("parseHexToDouble", new MethodStub(TbUtils.class.getMethod("parseHexToDouble", + String.class))); + parserConfig.addImport("parseHexToDouble", new MethodStub(TbUtils.class.getMethod("parseHexToDouble", + String.class, boolean.class))); + parserConfig.addImport("parseBytesToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesToDouble", + byte[].class, int.class))); + parserConfig.addImport("parseBytesToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesToDouble", + byte[].class, int.class, boolean.class))); + parserConfig.addImport("parseBytesToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesToDouble", + List.class, int.class))); + parserConfig.addImport("parseBytesToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesToDouble", + List.class, int.class, boolean.class))); parserConfig.addImport("toFixed", new MethodStub(TbUtils.class.getMethod("toFixed", double.class, int.class))); + parserConfig.addImport("toFixed", new MethodStub(TbUtils.class.getMethod("toFixed", + float.class, int.class))); parserConfig.addImport("hexToBytes", new MethodStub(TbUtils.class.getMethod("hexToBytes", ExecutionContext.class, String.class))); parserConfig.addImport("base64ToHex", new MethodStub(TbUtils.class.getMethod("base64ToHex", @@ -116,31 +178,52 @@ public class TbUtils { public static Object decodeToJson(ExecutionContext ctx, List bytesList) throws IOException { return TbJson.parse(ctx, bytesToString(bytesList)); } + public static Object decodeToJson(ExecutionContext ctx, String jsonStr) throws IOException { + return TbJson.parse(ctx, jsonStr); + } - public static String bytesToString(List bytesList) { + public static String bytesToString(List bytesList) { byte[] bytes = bytesFromList(bytesList); return new String(bytes); } - public static String bytesToString(List bytesList, String charsetName) throws UnsupportedEncodingException { + public static String bytesToString(List bytesList, String charsetName) throws UnsupportedEncodingException { byte[] bytes = bytesFromList(bytesList); return new String(bytes, charsetName); } - public static List stringToBytes(ExecutionContext ctx, String str) { - byte[] bytes = str.getBytes(); - return bytesToList(ctx, bytes); + public static List stringToBytes(ExecutionContext ctx, Object str) throws IllegalAccessException { + if (str instanceof String) { + byte[] bytes = str.toString().getBytes(); + return bytesToList(ctx, bytes); + } else { + throw new IllegalAccessException("Invalid type parameter [" + str.getClass().getSimpleName() + "]. Expected 'String'"); + } } - public static List stringToBytes(ExecutionContext ctx, String str, String charsetName) throws UnsupportedEncodingException { - byte[] bytes = str.getBytes(charsetName); - return bytesToList(ctx, bytes); + public static List stringToBytes(ExecutionContext ctx, Object str, String charsetName) throws UnsupportedEncodingException, IllegalAccessException { + if (str instanceof String) { + byte[] bytes = str.toString().getBytes(charsetName); + return bytesToList(ctx, bytes); + } else { + throw new IllegalAccessException("Invalid type parameter [" + str.getClass().getSimpleName() + "]. Expected 'String'"); + } } - private static byte[] bytesFromList(List bytesList) { + private static byte[] bytesFromList(List bytesList) { byte[] bytes = new byte[bytesList.size()]; for (int i = 0; i < bytesList.size(); i++) { - bytes[i] = bytesList.get(i); + Object objectVal = bytesList.get(i); + if (objectVal instanceof Integer) { + bytes[i] = isValidIntegerToByte((Integer) objectVal); + } else if (objectVal instanceof String) { + bytes[i] = isValidIntegerToByte(parseInt((String) objectVal)); + } else if (objectVal instanceof Byte) { + bytes[i] = (byte) objectVal; + } else { + throw new NumberFormatException("The value '" + objectVal + "' could not be correctly converted to a byte. " + + "Must be a HexDecimal/String/Integer/Byte format !"); + } } return bytes; } @@ -154,37 +237,73 @@ public class TbUtils { } public static Integer parseInt(String value) { - if (value != null) { + int radix = getRadix(value); + return parseInt(value, radix); + } + + public static Integer parseInt(String value, int radix) { + if (StringUtils.isNotBlank(value)) { try { - int radix = 10; - if (isHexadecimal(value)) { - radix = 16; + String valueP = prepareNumberString(value); + isValidRadix(valueP, radix); + try { + return Integer.parseInt(valueP, radix); + } catch (NumberFormatException e) { + BigInteger bi = new BigInteger(valueP, radix); + if (bi.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) + throw new NumberFormatException("Value \"" + value + "\" is greater than the maximum Integer value " + Integer.MAX_VALUE + " !"); + if (bi.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0) + throw new NumberFormatException("Value \"" + value + "\" is less than the minimum Integer value " + Integer.MIN_VALUE + " !"); + Float f = parseFloat(valueP); + if (f != null) { + return f.intValue(); + } else { + throw new NumberFormatException(e.getMessage()); + } } - return Integer.parseInt(prepareNumberString(value), radix); } catch (NumberFormatException e) { - Float f = parseFloat(value); - if (f != null) { - return f.intValue(); - } + throw new NumberFormatException(e.getMessage()); } } return null; } - public static Integer parseInt(String value, int radix) { - if (value != null) { + public static Long parseLong(String value) { + int radix = getRadix(value); + return parseLong(value, radix); + } + + public static Long parseLong(String value, int radix) { + if (StringUtils.isNotBlank(value)) { try { - return Integer.parseInt(prepareNumberString(value), radix); - } catch (NumberFormatException e) { - Float f = parseFloat(value); - if (f != null) { - return f.intValue(); + String valueP = prepareNumberString(value); + isValidRadix(valueP, radix); + try { + return Long.parseLong(valueP, radix); + } catch (NumberFormatException e) { + BigInteger bi = new BigInteger(valueP, radix); + if (bi.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) + throw new NumberFormatException("Value \"" + value + "\"is greater than the maximum Long value " + Long.MAX_VALUE + " !"); + if (bi.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0) + throw new NumberFormatException("Value \"" + value + "\" is less than the minimum Long value " + Long.MIN_VALUE + " !"); + Double dd = parseDouble(valueP); + if (dd != null) { + return dd.longValue(); + } else { + throw new NumberFormatException(e.getMessage()); + } } + } catch (NumberFormatException e) { + throw new NumberFormatException(e.getMessage()); } } return null; } + private static int getRadix(String value, int... radixS) { + return radixS.length > 0 ? radixS[0] : isHexadecimal(value) ? 16 : 10; + } + public static Float parseFloat(String value) { if (value != null) { try { @@ -218,8 +337,64 @@ public class TbUtils { } public static int parseHexToInt(String hex, boolean bigEndian) { + byte[] data = prepareHexToBytesNumber(hex, 8); + return parseBytesToInt(data, 0, data.length, bigEndian); + } + + public static long parseLittleEndianHexToLong(String hex) { + return parseHexToLong(hex, false); + } + + public static long parseBigEndianHexToLong(String hex) { + return parseHexToLong(hex, true); + } + + public static long parseHexToLong(String hex) { + return parseHexToLong(hex, true); + } + + public static long parseHexToLong(String hex, boolean bigEndian) { + byte[] data = prepareHexToBytesNumber(hex, 16); + return parseBytesToLong(data, 0, data.length, bigEndian); + } + + public static float parseLittleEndianHexToFloat(String hex) { + return parseHexToFloat(hex, false); + } + + public static float parseBigEndianHexToFloat(String hex) { + return parseHexToFloat(hex, true); + } + + public static float parseHexToFloat(String hex) { + return parseHexToFloat(hex, true); + } + + public static float parseHexToFloat(String hex, boolean bigEndian) { + byte[] data = prepareHexToBytesNumber(hex, 8); + return parseBytesToFloat(data, 0, bigEndian); + } + + public static double parseLittleEndianHexToDouble(String hex) { + return parseHexToDouble(hex, false); + } + + public static double parseBigEndianHexToDouble(String hex) { + return parseHexToDouble(hex, true); + } + + public static double parseHexToDouble(String hex) { + return parseHexToDouble(hex, true); + } + + public static double parseHexToDouble(String hex, boolean bigEndian) { + byte[] data = prepareHexToBytesNumber(hex, 16); + return parseBytesToDouble(data, 0, bigEndian); + } + + private static byte[] prepareHexToBytesNumber(String hex, int len) { int length = hex.length(); - if (length > 8) { + if (length > len) { throw new IllegalArgumentException("Hex string is too large. Maximum 8 symbols allowed."); } if (length % 2 > 0) { @@ -229,7 +404,7 @@ public class TbUtils { for (int i = 0; i < length; i += 2) { data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i + 1), 16)); } - return parseBytesToInt(data, 0, data.length, bigEndian); + return data; } public static ExecutionArrayList hexToBytes(ExecutionContext ctx, String hex) { @@ -293,6 +468,91 @@ public class TbUtils { return bb.getInt(); } + public static long parseBytesToLong(List data, int offset, int length) { + return parseBytesToLong(data, offset, length, true); + } + + public static long parseBytesToLong(List data, int offset, int length, boolean bigEndian) { + final byte[] bytes = new byte[data.size()]; + for (int i = 0; i < bytes.length; i++) { + bytes[i] = data.get(i); + } + return parseBytesToLong(bytes, offset, length, bigEndian); + } + + public static long parseBytesToLong(byte[] data, int offset, int length) { + return parseBytesToLong(data, offset, length, true); + } + + public static long parseBytesToLong(byte[] data, int offset, int length, boolean bigEndian) { + if (offset > data.length) { + throw new IllegalArgumentException("Offset: " + offset + " is out of bounds for array with length: " + data.length + "!"); + } + if (length > 8) { + throw new IllegalArgumentException("Length: " + length + " is too large. Maximum 4 bytes is allowed!"); + } + if (offset + length > data.length) { + throw new IllegalArgumentException("Offset: " + offset + " and Length: " + length + " is out of bounds for array with length: " + data.length + "!"); + } + var bb = ByteBuffer.allocate(8); + if (!bigEndian) { + bb.order(ByteOrder.LITTLE_ENDIAN); + } + bb.position(bigEndian ? 8 - length : 0); + bb.put(data, offset, length); + bb.position(0); + return bb.getLong(); + } + + public static float parseBytesToFloat(byte[] data, int offset) { + return parseBytesToFloat(data, offset, true); + } + + public static float parseBytesToFloat(List data, int offset) { + return parseBytesToFloat(data, offset, true); + } + + public static float parseBytesToFloat(List data, int offset, boolean bigEndian) { + return parseBytesToFloat(Bytes.toArray(data), offset, bigEndian); + } + + public static float parseBytesToFloat(byte[] data, int offset, boolean bigEndian) { + byte[] bytesToNumber = prepareBytesToNumber(data, offset, 4, bigEndian); + return ByteBuffer.wrap(bytesToNumber).getFloat(); + } + + + public static double parseBytesToDouble(byte[] data, int offset) { + return parseBytesToDouble(data, offset, true); + } + + public static double parseBytesToDouble(List data, int offset) { + return parseBytesToDouble(data, offset, true); + } + + public static double parseBytesToDouble(List data, int offset, boolean bigEndian) { + return parseBytesToDouble(Bytes.toArray(data), offset, bigEndian); + } + + public static double parseBytesToDouble(byte[] data, int offset, boolean bigEndian) { + byte[] bytesToNumber = prepareBytesToNumber(data, offset, 8, bigEndian); + return ByteBuffer.wrap(bytesToNumber).getDouble(); + } + + private static byte[] prepareBytesToNumber(byte[] data, int offset, int length, boolean bigEndian) { + if (offset > data.length) { + throw new IllegalArgumentException("Offset: " + offset + " is out of bounds for array with length: " + data.length + "!"); + } + if ((offset + length) > data.length) { + throw new IllegalArgumentException("Default length is always " + length + " bytes. Offset: " + offset + " and Length: " + length + " is out of bounds for array with length: " + data.length + "!"); + } + byte[] dataBytesArray = Arrays.copyOfRange(data, offset, (offset + length)); + if (!bigEndian) { + ArrayUtils.reverse(dataBytesArray); + } + return dataBytesArray; + } + public static String bytesToHex(ExecutionArrayList bytesList) { byte[] bytes = new byte[bytesList.size()]; for (int i = 0; i < bytesList.size(); i++) { @@ -315,6 +575,10 @@ public class TbUtils { return BigDecimal.valueOf(value).setScale(precision, RoundingMode.HALF_UP).doubleValue(); } + public static float toFixed(float value, int precision) { + return BigDecimal.valueOf(value).setScale(precision, RoundingMode.HALF_UP).floatValue(); + } + private static boolean isHexadecimal(String value) { return value != null && (value.contains("0x") || value.contains("0X")); } @@ -388,4 +652,27 @@ public class TbUtils { } } } + + private static boolean isValidRadix(String value, int radix) { + for (int i = 0; i < value.length(); i++) { + if (i == 0 && value.charAt(i) == '-') { + if (value.length() == 1) + throw new NumberFormatException("Failed radix [" + radix + "] for value: \"" + value + "\"!"); + else + continue; + } + if (Character.digit(value.charAt(i), radix) < 0) + throw new NumberFormatException("Failed radix: [" + radix + "] for value: \"" + value + "\"!"); + } + return true; + } + + private static byte isValidIntegerToByte (Integer val) { + if (val > 255 || val.intValue() < -128) { + throw new NumberFormatException("The value '" + val + "' could not be correctly converted to a byte. " + + "Integer to byte conversion requires the use of only 8 bits (with a range of min/max = -128/255)!"); + } else { + return val.byteValue(); + } + } } diff --git a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbDateConstructorTest.java b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbDateConstructorTest.java new file mode 100644 index 0000000000..8a9d84b980 --- /dev/null +++ b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbDateConstructorTest.java @@ -0,0 +1,102 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.script.api.tbel; + +import org.junit.Assert; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.mvel2.CompileException; +import org.mvel2.ExecutionContext; +import org.mvel2.ParserContext; +import org.mvel2.SandboxedParserConfiguration; + +import java.io.Serializable; +import java.util.HashMap; + +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mvel2.MVEL.compileExpression; +import static org.mvel2.MVEL.executeTbExpression; + +public class TbDateConstructorTest { + + private static ExecutionContext executionContext; + + @BeforeAll + public static void setup() { + SandboxedParserConfiguration parserConfig = ParserContext.enableSandboxedMode(); + parserConfig.addImport("JSON", TbJson.class); + parserConfig.registerDataType("Date", TbDate.class, date -> 8L); + executionContext = new ExecutionContext(parserConfig, 5 * 1024 * 1024); + } + + @AfterAll + public static void tearDown() { + ParserContext.disableSandboxedMode(); + } + + + @Test + void TestTbDateConstructorWithStringParameters () { + // one: date in String + String body = "var d = new Date(\"2023-08-06T04:04:05.123Z\"); \n" + + "d.toISOString()"; + Object res = executeScript(body); + Assert.assertNotEquals("2023-08-06T04:04:05.123Z".length(), res); + + // two: date in String + pattern + body = "var pattern = \"yyyy-MM-dd HH:mm:ss.SSSXXX\";\n" + + "var d = new Date(\"2023-08-06 04:04:05.000Z\", pattern);\n" + + "d.toISOString()"; + res = executeScript(body); + Assert.assertNotEquals("2023-08-06T04:04:05Z".length(), res); + + + // three: date in String + pattern + locale + body = "var pattern = \"hh:mm:ss a, EEE M/d/uuuu\";\n" + + "var d = new Date(\"02:15:30 PM, Sun 10/09/2022\", pattern, \"en-US\");" + + "d.toISOString()"; + res = executeScript(body); + Assert.assertNotEquals("2023-08-06T04:04:05Z".length(), res); + + // four: date in String + pattern + locale + TimeZone + body = "var pattern = \"hh:mm:ss a, EEE M/d/uuuu\";\n" + + "var d = new Date(\"02:15:30 PM, Sun 10/09/2022\", pattern, \"en-US\", \"America/New_York\");" + + "d.toISOString()"; + res = executeScript(body); + Assert.assertNotEquals("22022-10-09T18:15:30Z".length(), res); + } + + @Test + void TbDateConstructorWithStringParameters_PatternNotMatchLocale_Error () { + String expectedMessage = "could not create constructor: null"; + + String body = "var pattern = \"hh:mm:ss a, EEE M/d/uuuu\";\n" + + "var d = new Date(\"02:15:30 PM, Sun 10/09/2022\", pattern, \"de\");" + + "d.toISOString()"; + Exception actual = assertThrows(CompileException.class, () -> { + executeScript(body); + }); + assertTrue(actual.getMessage().contains(expectedMessage)); + + } + + private Object executeScript(String ex) { + Serializable compiled = compileExpression(ex, new ParserContext()); + return executeTbExpression(compiled, executionContext, new HashMap()); + } +} diff --git a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbDateTest.java b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbDateTest.java new file mode 100644 index 0000000000..120a776c1a --- /dev/null +++ b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbDateTest.java @@ -0,0 +1,882 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.script.api.tbel; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import lombok.extern.slf4j.Slf4j; +import org.assertj.core.api.Assertions; +import org.assertj.core.data.Offset; +import org.junit.Assert; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.mvel2.ConversionException; +import org.thingsboard.common.util.JacksonUtil; + +import java.time.DateTimeException; +import java.time.Instant; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +@Slf4j +class TbDateTest { + + ListeningExecutorService executor; + + @AfterEach + void tearDown() { + if (executor != null) { + executor.shutdownNow(); + } + } + + /** + * Note: This test simulated the high concurrency calls. + * But it not always fails before as the concurrency issue happens far away from the test subject, inside the SimpleDateFormat class. + * Few calls later after latch open, the concurrency issue is not well reproduced. + * Depends on environment some failure may happen each 2 or 100 runs. + * To be highly confident run this test in a ForkMode=method, repeat 100 times. This will provide about 99 failures per 100 runs. + * The value of this test is *never* to fail when isoDateFormat.format(this) is properly synchronized + * If this test fails time-to-time -- it is a sign that isoDateFormat.format() called concurrently and have to be fixed (synchronized) + * The expected exception example: + * Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 14 out of bounds for length 13 + * at java.base/sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate(BaseCalendar.java:457) + * at java.base/java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2394) + * at java.base/java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2309) + * at java.base/java.util.Calendar.setTimeInMillis(Calendar.java:1834) + * at java.base/java.util.Calendar.setTime(Calendar.java:1800) + * at java.base/java.text.SimpleDateFormat.format(SimpleDateFormat.java:974) + * Tests were checked with: + * -timezone Etc/GMT-14 + * -timezone Etc/GMT+12 + * -timezone America/New_York + * -timezone Europe/Kyiv + */ + @Test + void testToISOStringConcurrently() throws ExecutionException, InterruptedException, TimeoutException { + int threads = 5; + executor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(threads)); + for (int j = 0; j < 1000; j++) { + final int iteration = j; + CountDownLatch readyLatch = new CountDownLatch(threads); + CountDownLatch latch = new CountDownLatch(1); + long now = 1709217342000L; + List> futures = new ArrayList<>(threads); + for (int i = 0; i < threads; i++) { + long ts = now + TimeUnit.DAYS.toMillis(i * 366) + TimeUnit.MINUTES.toMillis(iteration) + TimeUnit.SECONDS.toMillis(iteration) + iteration; + TbDate tbDate = new TbDate(ts); + futures.add(executor.submit(() -> { + readyLatch.countDown(); + if (!latch.await(30, TimeUnit.SECONDS)) { + throw new RuntimeException("await timeout"); + } + return tbDate.toISOString(); + })); + } + ListenableFuture> future = Futures.allAsList(futures); + Futures.addCallback(future, new FutureCallback>() { + @Override + public void onSuccess(List result) { + + } + + @Override + public void onFailure(Throwable t) { + log.error("Failure happens on iteration {}", iteration); + } + }, MoreExecutors.directExecutor()); + readyLatch.await(30, TimeUnit.SECONDS); + latch.countDown(); + future.get(30, TimeUnit.SECONDS); + } + } + + @Test + void testToISOStringThreadLocalStaticFormatter() throws ExecutionException, InterruptedException, TimeoutException { + executor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(1)); + int hrs = 14; + int date = 29; + int month = 2; + String pattern = "2024-%s-%sT%s:35:42.987Z"; + String tsStr = String.format(pattern, String.format("%02d", month), date, hrs); //Thu Feb 29 2024 14:35:42.987 GMT+0000 + TbDate tbDate = new TbDate(tsStr); + long ts = 1709217342987L; //Thu Feb 29 2024 14:35:42.987 GMT+0000 + Assert.assertEquals(ts, tbDate.parseSecondMilli()); + int offsetLocalSecond = tbDate.getLocaleZoneOffset().getTotalSeconds(); // for example 7200 for GMT + 2 + + String datePrefix = tsStr; //without time zone + assertThat(tbDate.toISOString()) + .as("format in main thread") + .startsWith(datePrefix); + assertThat(executor.submit(tbDate::toISOString).get(30, TimeUnit.SECONDS)) + .as("format in executor thread") + .startsWith(datePrefix); + TbDateTestEntity tbDateTest = new TbDateTestEntity(tbDate.getFullYear(), month, date,hrs + offsetLocalSecond/60/60); + String datePrefixLocal = String.format(pattern, tbDateTest.geMonthStr(), tbDateTest.geDateStr(), tbDateTest.geHoursStr()); + long offsetLocalMilli = offsetLocalSecond*1000; + assertThat(new TbDate(ts + offsetLocalMilli).toISOString()) + .as("new instance format in main thread") + .startsWith(datePrefixLocal); + assertThat(executor.submit(() -> new TbDate(ts + offsetLocalMilli).toISOString()).get(30, TimeUnit.SECONDS)) + .as("new instance format in executor thread") + .startsWith(datePrefixLocal); + } + + @Test + void testToLocaleDateString() { + String s = "02:15:30 PM, Sun 10/09/2022"; + String pattern = "hh:mm:ss a, EEE M/d/uuuu"; + TbDate d = new TbDate(s, pattern, "en-US"); + // tz = local + int localOffsetHrs = ZoneId.systemDefault().getRules().getOffset(d.getInstant()).getTotalSeconds()/60/60; + int hrs = 14 - localOffsetHrs; + String expected = "2022-10-09T" + hrs + ":15:30Z"; + Assert.assertEquals(expected, d.toISOString()); + + // tz = "-04:00" + s = "2023-08-06T04:04:05.00-04:00"; + d = new TbDate(s); + Assert.assertEquals("2023-08-06T08:04:05Z", d.toISOString()); + + s = "02:15:30 PM, Sun 10/09/2022"; + d = new TbDate(s, pattern, "en-US", "-04:00"); + Assert.assertEquals("2022-10-09T18:15:30Z", d.toISOString()); + d = new TbDate(s, pattern,"en-US", "America/New_York"); + Assert.assertEquals("2022-10-09T18:15:30Z", d.toISOString()); + // tz = "+02:00" + /** + * For Java 11: + * String[2] { "vorm.", "nachm." } + * For Java 17: + * `{ "AM", "PM" }` + */ + s = "09:15:30 nachm., So. 10/09/2022"; + d = new TbDate(s, pattern, "de","Europe/Berlin"); + Assert.assertEquals("2022-10-09T19:15:30Z", d.toISOString()); + + s = "02:15:30 пп, середа, 4 жовтня 2023 р."; + pattern = "hh:mm:ss a, EEEE, d MMMM y 'р.'"; + d = new TbDate(s, pattern, "uk-UA"); + localOffsetHrs = ZoneId.systemDefault().getRules().getOffset(d.getInstant()).getTotalSeconds()/60/60; + hrs = 14 - localOffsetHrs; + expected = "2023-10-04T" + hrs + ":15:30Z"; + Assert.assertEquals(expected, d.toISOString()); + + d = new TbDate(1693962245000L); + Assert.assertEquals("2023-09-06T01:04:05Z", d.toISOString()); + + // Depends on time zone, so we just check it works; + Assert.assertNotNull(d.toLocaleDateString()); + Assert.assertNotNull(d.toLocaleDateString("en-US")); + + Assert.assertEquals("9/5/23", d.toLocaleDateString("en-US", "America/New_York")); + Assert.assertEquals("23. 9. 5.", d.toLocaleDateString("ko-KR", "America/New_York")); + Assert.assertEquals("06.09.23", d.toLocaleDateString( "uk-UA", "Europe/Kiev")); + Assert.assertEquals("5\u200F/9\u200F/2023", d.toLocaleDateString( "ar-EG", "America/New_York")); + + Assert.assertEquals("Tuesday, September 5, 2023", d.toLocaleDateString("en-US", JacksonUtil.newObjectNode() + .put("timeZone", "America/New_York") + .put("dateStyle", "full") + .toString())); + Assert.assertEquals("2023년 9월 5일 화요일", d.toLocaleDateString("ko-KR", JacksonUtil.newObjectNode() + .put("timeZone", "America/New_York") + .put("dateStyle", "full") + .toString())); + Assert.assertEquals("середа, 6 вересня 2023 р.", d.toLocaleDateString("uk-UA", JacksonUtil.newObjectNode() + .put("timeZone", "Europe/Kiev") + .put("dateStyle", "full") + .toString())); + Assert.assertEquals("الثلاثاء، 5 سبتمبر 2023", d.toLocaleDateString("ar-EG", JacksonUtil.newObjectNode() + .put("timeZone", "America/New_York") + .put("dateStyle", "full") + .toString())); + + Assert.assertEquals("Tuesday 9/5/2023", d.toLocaleDateString("en-US", JacksonUtil.newObjectNode() + .put("timeZone", "America/New_York") + .put("pattern", "EEEE M/d/yyyy") + .toString())); + Assert.assertEquals("화요일 9/5/2023", d.toLocaleDateString("ko-KR", JacksonUtil.newObjectNode() + .put("timeZone", "America/New_York") + .put("pattern", "EEEE M/d/yyyy") + .toString())); + Assert.assertEquals("середа 9/6/2023", d.toLocaleDateString("uk-UA", JacksonUtil.newObjectNode() + .put("timeZone", "Europe/Kiev") + .put("pattern", "EEEE M/d/yyyy") + .toString())); + Assert.assertEquals("الثلاثاء 9/5/2023", d.toLocaleDateString("ar-EG", JacksonUtil.newObjectNode() + .put("timeZone", "America/New_York") + .put("pattern", "EEEE M/d/yyyy") + .toString())); + } + + @Test + void testToLocaleTimeString() { + TbDate d = new TbDate(1693962245000L); + + // Depends on time zone, so we just check it works; + Assert.assertNotNull(d.toLocaleTimeString()); + Assert.assertNotNull(d.toLocaleTimeString("en-US")); + + Assert.assertEquals("9:04:05 PM", d.toLocaleTimeString("en-US", "America/New_York")); + Assert.assertEquals("오후 9:04:05", d.toLocaleTimeString("ko-KR", "America/New_York")); + Assert.assertEquals("04:04:05", d.toLocaleTimeString( "uk-UA", "Europe/Kiev")); + Assert.assertEquals("9:04:05 م", d.toLocaleTimeString( "ar-EG", "America/New_York")); + + Assert.assertEquals("9:04:05 PM Eastern Daylight Time", d.toLocaleTimeString("en-US", JacksonUtil.newObjectNode() + .put("timeZone", "America/New_York") + .put("timeStyle", "full") + .toString())); + Assert.assertEquals("오후 9시 4분 5초 미 동부 하계 표준시", d.toLocaleTimeString("ko-KR", JacksonUtil.newObjectNode() + .put("timeZone", "America/New_York") + .put("timeStyle", "full") + .toString())); + Assert.assertEquals("04:04:05 за східноєвропейським літнім часом", d.toLocaleTimeString("uk-UA", JacksonUtil.newObjectNode() + .put("timeZone", "Europe/Kiev") + .put("timeStyle", "full") + .toString())); + Assert.assertEquals("9:04:05 م التوقيت الصيفي الشرقي لأمريكا الشمالية", d.toLocaleTimeString("ar-EG", JacksonUtil.newObjectNode() + .put("timeZone", "America/New_York") + .put("timeStyle", "full") + .toString())); + + Assert.assertEquals("9:04:05 PM", d.toLocaleTimeString("en-US", JacksonUtil.newObjectNode() + .put("timeZone", "America/New_York") + .put("pattern", "h:mm:ss a") + .toString())); + Assert.assertEquals("9:04:05 오후", d.toLocaleTimeString("ko-KR", JacksonUtil.newObjectNode() + .put("timeZone", "America/New_York") + .put("pattern", "h:mm:ss a") + .toString())); + Assert.assertEquals("4:04:05 дп", d.toLocaleTimeString("uk-UA", JacksonUtil.newObjectNode() + .put("timeZone", "Europe/Kiev") + .put("pattern", "h:mm:ss a") + .toString())); + Assert.assertEquals("9:04:05 م", d.toLocaleTimeString("ar-EG", JacksonUtil.newObjectNode() + .put("timeZone", "America/New_York") + .put("pattern", "h:mm:ss a") + .toString())); + } + + @Test + void testToLocaleString() { + TbDate d = new TbDate(1693962245000L); + + // Depends on time zone, so we just check it works; + Assert.assertNotNull(d.toLocaleString()); + Assert.assertNotNull(d.toLocaleString("en-US")); + + Assert.assertEquals("9/5/23, 9:04:05 PM", d.toLocaleString("en-US", "America/New_York")); + Assert.assertEquals("23. 9. 5. 오후 9:04:05", d.toLocaleString("ko-KR", "America/New_York")); + Assert.assertEquals("06.09.23, 04:04:05", d.toLocaleString( "uk-UA", "Europe/Kiev")); + Assert.assertEquals("5\u200F/9\u200F/2023 9:04:05 م", d.toLocaleString( "ar-EG", "America/New_York")); + + Assert.assertEquals("Tuesday, September 5, 2023 at 9:04:05 PM Eastern Daylight Time", d.toLocaleString("en-US", JacksonUtil.newObjectNode() + .put("timeZone", "America/New_York") + .put("dateStyle", "full") + .put("timeStyle", "full") + .toString())); + Assert.assertEquals("2023년 9월 5일 화요일 오후 9시 4분 5초 미 동부 하계 표준시", d.toLocaleString("ko-KR", JacksonUtil.newObjectNode() + .put("timeZone", "America/New_York") + .put("dateStyle", "full") + .put("timeStyle", "full") + .toString())); + Assert.assertEquals("середа, 6 вересня 2023 р. о 04:04:05 за східноєвропейським літнім часом", d.toLocaleString("uk-UA", JacksonUtil.newObjectNode() + .put("timeZone", "Europe/Kiev") + .put("dateStyle", "full") + .put("timeStyle", "full") + .toString())); + Assert.assertEquals("الثلاثاء، 5 سبتمبر 2023 9:04:05 م التوقيت الصيفي الشرقي لأمريكا الشمالية", d.toLocaleString("ar-EG", JacksonUtil.newObjectNode() + .put("timeZone", "America/New_York") + .put("dateStyle", "full") + .put("timeStyle", "full") + .toString())); + + Assert.assertEquals("9/5/2023, 9:04:05 PM", d.toLocaleString("en-US", JacksonUtil.newObjectNode() + .put("timeZone", "America/New_York") + .put("pattern", "M/d/yyyy, h:mm:ss a") + .toString())); + Assert.assertEquals("9/5/2023, 9:04:05 오후", d.toLocaleString("ko-KR", JacksonUtil.newObjectNode() + .put("timeZone", "America/New_York") + .put("pattern", "M/d/yyyy, h:mm:ss a") + .toString())); + Assert.assertEquals("9/6/2023, 4:04:05 дп", d.toLocaleString("uk-UA", JacksonUtil.newObjectNode() + .put("timeZone", "Europe/Kiev") + .put("pattern", "M/d/yyyy, h:mm:ss a") + .toString())); + Assert.assertEquals("9/5/2023, 9:04:05 م", d.toLocaleString("ar-EG", JacksonUtil.newObjectNode() + .put("timeZone", "America/New_York") + .put("pattern", "M/d/yyyy, h:mm:ss a") + .toString())); + } + + @Test + void TestFromString () { + String stringDateUTC = "2023-09-06T01:04:05.00Z"; + TbDate d = new TbDate(stringDateUTC); + Assert.assertEquals("2023-09-06T01:04:05Z", d.toISOString()); + String stringDateTZ = "2023-09-06T01:04:05.00+04:00:00"; + d = new TbDate(stringDateTZ); + Assert.assertEquals("2023-09-05T21:04:05Z", d.toISOString()); + stringDateTZ = "2023-09-06T01:04:05.00+04"; + d = new TbDate(stringDateTZ); + Assert.assertEquals("2023-09-05T21:04:05Z", d.toISOString()); + stringDateTZ = "2023-09-06T01:04:05.00-04:00"; + d = new TbDate(stringDateTZ); + Assert.assertEquals("2023-09-06T05:04:05Z", d.toISOString()); + stringDateTZ = "2023-09-06T01:04:05.00+04:30:56"; + d = new TbDate(stringDateTZ); + Assert.assertEquals("2023-09-05T20:33:09Z", d.toISOString()); + stringDateTZ = "2023-09-06T01:04:05.00-02:00"; + d = new TbDate(stringDateTZ); + Assert.assertEquals("2023-09-06T03:04:05Z", d.toISOString()); + // Without_TZ + stringDateTZ = "2023-08-06T04:04:05.123"; + d = new TbDate(stringDateTZ); + Assert.assertEquals("2023-08-06 04:04:05", d.toLocaleString()); + + // With TZ RFC_1123 + String stringDateRFC_1123 = "Sat, 3 Jun 2023 11:05:30 GMT"; + d = new TbDate(stringDateRFC_1123); + Assert.assertEquals("2023-06-03T11:05:30Z", d.toISOString()); + stringDateRFC_1123 = "Sat, 3 Jun 2023 01:04:05 +043056"; + d = new TbDate(stringDateRFC_1123); + Assert.assertEquals("2023-06-02T20:33:09Z", d.toISOString()); + stringDateRFC_1123 = "Sat, 3 Jun 2023 11:05:30 +0400"; + d = new TbDate(stringDateRFC_1123); + Assert.assertEquals("2023-06-03T07:05:30Z", d.toISOString()); + stringDateRFC_1123 = "Thu, 29 Feb 2024 11:05:30 -03"; + d = new TbDate(stringDateRFC_1123); + Assert.assertEquals("2024-02-29T14:05:30Z", d.toISOString()); + // Without TZ RFC_1123 + stringDateRFC_1123 = "Sat, 3 Jun 2023 11:05:30"; + d = new TbDate(stringDateRFC_1123); + Assert.assertEquals("2023-06-03 11:05:30", d.toLocaleString()); + + // With pattern + locale - ok + String pattern = "hh:mm:ss a, EEE M/d/uuuu"; + stringDateRFC_1123 = "09:15:30 nachm., So. 10/09/2022"; + d = new TbDate(stringDateRFC_1123 , pattern, "de"); + Assert.assertEquals("2022-10-09 21:15:30", d.toLocaleString()); + + // failed TZ + String expectedMessage = "Cannot parse value"; + String finalStringDateZ_error0 = "2023-09-06T01:04:05.00+045"; + Exception actual = assertThrows(ConversionException.class, () -> { + new TbDate(finalStringDateZ_error0); + }); + assertTrue(actual.getMessage().contains(expectedMessage)); + // failed TZ + String finalStringDateZ_error1 = "2023-08-06T04:04:05.123+04:00:00:00"; + actual = assertThrows(ConversionException.class, () -> { + new TbDate(finalStringDateZ_error1); + }); + assertTrue(actual.getMessage().contains(expectedMessage)); + // failed TZ + String finalStringDateZ_error2 ="2023-08-06T04:04:05.123+4"; + actual = assertThrows(ConversionException.class, () -> { + new TbDate(finalStringDateZ_error2); + }); + assertTrue(actual.getMessage().contains(expectedMessage)); + // The locale does not match the pattern RFC_1123 + String finalStringDateZ_error3= "02:15:30 PM, Sun 10/09/2022"; + pattern = "hh:mm:ss a, EEE M/d/uuuu"; + String finalPattern = pattern; + actual = assertThrows(ConversionException.class, () -> { + new TbDate(finalStringDateZ_error3, finalPattern, "de"); + }); + assertTrue(actual.getMessage().contains(expectedMessage)); + + // failed DayOfWeek RFC_1123 + String stringDateRFC_1123_error = "Tue, 3 Jun 2023 11:05:30 GMT"; + actual = assertThrows(ConversionException.class, () -> { + new TbDate(stringDateRFC_1123_error); + }); + assertTrue(actual.getMessage().contains(expectedMessage)); + } + + @Test + void TestParse () { + String stringDateStart = "1970-01-01T00:00:00Z"; + TbDate d = new TbDate(stringDateStart); + long actualMillis = TbDate.parse("1970-01-01 T00:00:00"); + Assert.assertEquals(-d.getLocaleZoneOffset().getTotalSeconds() * 1000, actualMillis); + String pattern = "yyyy-MM-dd HH:mm:ss.SSS"; + String stringDate = "1995-12-04 00:12:00.000"; + Assert.assertNotEquals(-1L, TbDate.parse(stringDate, pattern)); + } + + @Test + void TestMethodGetAsDateTimeLocal() { + TbDate d = new TbDate(1975, 12, 31, 23,15,30, 560); + TbDate d0 = new TbDate(1975, 12, 31, 23,15,30, 560,"UTC"); + TbDate d1 = new TbDate(1975, 12, 31, 23,15,30, 561,"+03:00"); + TbDate d2 = new TbDate(1975, 12, 31, 23,15,30, 562,"-02:00"); + TbDate dLocal = new TbDate(d.parseSecondMilli() + d.getLocaleZoneOffset().getTotalSeconds()*1000); + TbDate dLocal0 = new TbDate(d0.parseSecondMilli() + d0.getLocaleZoneOffset().getTotalSeconds()*1000); + TbDate dLocal1 = new TbDate(d1.parseSecondMilli() + d1.getLocaleZoneOffset().getTotalSeconds()*1000); + TbDate dLocal2 = new TbDate(d2.parseSecondMilli() + d2.getLocaleZoneOffset().getTotalSeconds()*1000); + + Assert.assertEquals(dLocal.getUTCFullYear(), d.getFullYear()); + Assert.assertEquals(dLocal0.getUTCFullYear(), d0.getFullYear()); + Assert.assertEquals(dLocal1.getUTCFullYear(), d1.getFullYear()); + Assert.assertEquals(dLocal2.getUTCFullYear(), d2.getFullYear()); + + Assert.assertEquals(dLocal.getUTCMonth(), d.getMonth()); + Assert.assertEquals(dLocal0.getUTCMonth(), d0.getMonth()); + Assert.assertEquals(dLocal1.getUTCMonth(), d1.getMonth()); + Assert.assertEquals(dLocal2.getUTCMonth(), d2.getMonth()); + + Assert.assertEquals(dLocal.getUTCDate(), d.getDate()); + Assert.assertEquals(dLocal0.getUTCDate(), d0.getDate()); + Assert.assertEquals(dLocal1.getUTCDate(), d1.getDate()); + Assert.assertEquals(dLocal2.getUTCDate(), d2.getDate()); + + Assert.assertEquals(dLocal.getUTCDay(), d.getDay()); + Assert.assertEquals(dLocal0.getUTCDay(), d0.getDay()); + Assert.assertEquals(dLocal1.getUTCDay(), d1.getDay()); + Assert.assertEquals(dLocal2.getUTCDay(), d2.getDay()); + + Assert.assertEquals(dLocal.getUTCHours(), d.getHours()); + Assert.assertEquals(dLocal0.getUTCHours(), d0.getHours()); + Assert.assertEquals(dLocal1.getUTCHours(), d1.getHours()); + Assert.assertEquals(dLocal2.getUTCHours(), d2.getHours()); + + Assert.assertEquals(dLocal.getUTCMinutes(), d.getMinutes()); + Assert.assertEquals(dLocal0.getUTCMinutes(), d0.getMinutes()); + Assert.assertEquals(dLocal1.getUTCMinutes(), d1.getMinutes()); + Assert.assertEquals(dLocal2.getUTCMinutes(), d2.getMinutes()); + + Assert.assertEquals(dLocal.getUTCSeconds(), d.getSeconds()); + Assert.assertEquals(dLocal0.getUTCSeconds(), d0.getSeconds()); + Assert.assertEquals(dLocal1.getUTCSeconds(), d1.getSeconds()); + Assert.assertEquals(dLocal2.getUTCSeconds(), d2.getSeconds()); + + Assert.assertEquals(dLocal.getUTCMilliseconds(), d.getMilliseconds()); + Assert.assertEquals(dLocal0.getUTCMilliseconds(), d0.getMilliseconds()); + Assert.assertEquals(dLocal1.getUTCMilliseconds(), d1.getMilliseconds()); + Assert.assertEquals(dLocal2.getUTCMilliseconds(), d2.getMilliseconds()); + } + + @Test + void Test_Year_Moth_Date_Hours_Min_Sec_Without_TZ() { + + TbDate d = new TbDate(2023, 8, 18); + Assert.assertEquals("2023-08-18 00:00:00", d.toLocaleString()); + d = new TbDate(2023, 9, 17, 17, 34); + Assert.assertEquals("2023-09-17 17:34:00", d.toLocaleString()); + d = new TbDate(23, 9, 7, 8, 4); + Assert.assertEquals("2023-09-07 08:04:00", d.toLocaleString()); + d = new TbDate(23, 9, 7, 8, 4, 5); + Assert.assertEquals("2023-09-07 08:04:05", d.toLocaleString()); + d = new TbDate(23, 9, 7, 8, 4, 5, 567); + Assert.assertEquals("2023-09-07 08:04:05", d.toLocaleString()); + } + + @Test + void Test_DateString_With_Pattern() { + String pattern = "yyyy-MM-dd HH:mm:ss.SSSXXX"; + TbDate d = new TbDate("2023-08-06 04:04:05.000-04:00", pattern); + Assert.assertEquals("2023-08-06T08:04:05Z", d.toISOString()); + } + @Test + void Test_DateString_With_TZ() { + int date = 7; + int tz = -4; + int hrs = 8; + String pattern = "2023-09-%s %s:04:05"; + String tzStr = "-04:00:00"; + String dateStr = "2023-09-0" + date + "T0" + hrs + ":04:05.123" + tzStr; + TbDate d = new TbDate(dateStr); + int localOffsetHrs = ZoneId.systemDefault().getRules().getOffset(d.getInstant()).getTotalSeconds()/60/60; + TbDateTestEntity tbDateTest = new TbDateTestEntity(23, 9, date, hrs + localOffsetHrs - tz); + String expected = String.format(pattern, tbDateTest.geDateStr(), tbDateTest.geHoursStr()); + Assert.assertEquals(expected, d.toLocaleString()); + + tz = +5; + tzStr = "+05:00"; + dateStr = "2023-09-0" + date + "T0" + hrs + ":04:05.123" + tzStr; + d = new TbDate(dateStr); + localOffsetHrs = ZoneId.systemDefault().getRules().getOffset(d.getInstant()).getTotalSeconds()/60/60; + tbDateTest = new TbDateTestEntity(23, 9, date, hrs + localOffsetHrs - tz); + expected = String.format(pattern, tbDateTest.geDateStr(), tbDateTest.geHoursStr()); + Assert.assertEquals(expected, d.toLocaleString()); + + tz = -2; + tzStr = "-02"; + dateStr = "2023-09-0" + date + "T0" + hrs + ":04:05.123" + tzStr; + d = new TbDate(dateStr); + localOffsetHrs = ZoneId.systemDefault().getRules().getOffset(d.getInstant()).getTotalSeconds()/60/60; + tbDateTest = new TbDateTestEntity(23, 9, date, hrs + localOffsetHrs - tz); + expected = String.format(pattern, tbDateTest.geDateStr(), tbDateTest.geHoursStr()); + Assert.assertEquals(expected, d.toLocaleString()); + } + + @Test + void Test_Get_LocalDateTime_With_TZ() { + int hrs = 8; + int date = 7; + int tz = 0; + String pattern = "2023-09-%s %s:04:05"; + String tzStr = "UTC"; + TbDate d = new TbDate(23, 9, date, hrs, 4, 5); + int localOffsetHrs = 0; + TbDateTestEntity tbDateTest = new TbDateTestEntity(23, 9, date, hrs + localOffsetHrs - tz); + String expected = String.format(pattern, tbDateTest.geDateStr(), tbDateTest.geHoursStr()); + Assert.assertEquals(expected, d.toLocaleString()); + + d = new TbDate(23, 9, date, hrs, 4, 5, tzStr); + localOffsetHrs = ZoneId.systemDefault().getRules().getOffset(d.getInstant()).getTotalSeconds()/60/60; + tbDateTest = new TbDateTestEntity(23, 9, date, hrs + localOffsetHrs - tz); + expected = String.format(pattern, tbDateTest.geDateStr(), tbDateTest.geHoursStr()); + Assert.assertEquals(expected, d.toLocaleString()); + + tz = 3; + tzStr = "+03:00"; + d = new TbDate(23, 9, date, hrs, 4, 5, tzStr); + localOffsetHrs = ZoneId.systemDefault().getRules().getOffset(d.getInstant()).getTotalSeconds()/60/60; + tbDateTest = new TbDateTestEntity(23, 9, date, hrs + localOffsetHrs - tz); + expected = String.format(pattern, tbDateTest.geDateStr(), tbDateTest.geHoursStr()); + Assert.assertEquals(expected, d.toLocaleString()); + + tz = -4; + tzStr = "-04:00"; + d = new TbDate(23, 9, date, hrs, 4, 5, tzStr); + localOffsetHrs = ZoneId.systemDefault().getRules().getOffset(d.getInstant()).getTotalSeconds()/60/60; + tbDateTest = new TbDateTestEntity(23, 9, date, hrs + localOffsetHrs - tz); + expected = String.format(pattern, tbDateTest.geDateStr(), tbDateTest.geHoursStr()); + Assert.assertEquals(expected, d.toLocaleString()); + } + + @Test + public void TestToUTC() { + Assert.assertEquals(-2209075200000L, TbDate.UTC(0)); + Assert.assertEquals("1899-12-31T00:00:00Z", new TbDate(TbDate.UTC(0)).toJSON()); + Assert.assertEquals("1996-02-02T03:04:05Z", new TbDate(TbDate.UTC(96, 2, 2, 3, 4, 5)).toJSON()); + Assert.assertEquals("2022-12-31T03:04:05.678Z", new TbDate(TbDate.UTC(22, 0, 0, 3, 4, 5, 678)).toJSON()); + Assert.assertEquals("0903-12-31T03:04:05.678Z", new TbDate(TbDate.UTC(903, 0, 0, 3, 4, 5, 678)).toJSON()); + Assert.assertEquals("1958-12-31T03:04:05.678Z", new TbDate(TbDate.UTC(1958, 0, 0, 3, 4, 5, 678)).toJSON()); + Assert.assertEquals("2032-04-05T03:04:05.678Z", new TbDate(TbDate.UTC(2032, 4, 5, 3, 4, 5, 678)).toJSON()); + Assert.assertEquals("2024-02-29T03:04:05.678Z", new TbDate(TbDate.UTC(2024, 2, 29, 3, 4, 5, 678)).toJSON()); + Exception actual = assertThrows(DateTimeException.class, () -> { + TbDate.UTC(2023, 2, 29, 3, 4, 5, 678); + }); + String expectedMessage = "Invalid date 'February 29' as '2023' is not a leap year"; + assertTrue(actual.getMessage().contains(expectedMessage)); + } + + @Test + void TestMethodGetTimeUTC() { + TbDate ddUTC = new TbDate(TbDate.UTC(1996, 1, 2, 3, 4, 5)); + Assert.assertEquals(820551845000L, ddUTC.valueOf()); + TbDate dd = new TbDate(1996, 1, 2, 3, 4, 5); + int localOffsetMilli = ZoneId.systemDefault().getRules().getOffset(dd.getInstant()).getTotalSeconds()*1000; + Assert.assertEquals((dd.valueOf() + localOffsetMilli), ddUTC.valueOf()); + + ddUTC = new TbDate(TbDate.UTC(1969, 7, 20, 20, 17, 40)); + Assert.assertEquals(-14182940000L, ddUTC.valueOf()); + TbDate beforeStartUTC = new TbDate(1969, 7, 20, 20, 17, 40); + localOffsetMilli = ZoneId.systemDefault().getRules().getOffset(beforeStartUTC.getInstant()).getTotalSeconds()*1000; + Assert.assertEquals((beforeStartUTC.valueOf() + localOffsetMilli), ddUTC.valueOf()); + } + + @Test + void TestMethodSetUTCFullYearMonthDate() { + TbDate d1 = new TbDate(1975, 12, 31, 23,15,30, 567,"-04:00"); + TbDate d2 = new TbDate(1975, 12, 31, 23,15,30, 567,"+04:00"); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + + d1.setUTCFullYear(1969); + d2.setUTCFullYear(1969); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + + d1.setUTCFullYear(1975, 5); + d2.setUTCFullYear(2023, 11); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + + d1.setUTCFullYear(2023, 2, 28); + d2.setUTCFullYear(2023, 12, 31); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + + d1.setUTCMonth(11); + d2.setUTCMonth(2); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + + d1.setUTCMonth(5, 20); + d2.setUTCMonth(2, 8); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + + d1.setUTCDate(6); + d2.setUTCDate(15); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + } + + @Test + void TestMethodSetUTCHoursMinutesSecondsMilliSec() { + TbDate d1 = new TbDate(1975, 12, 31, 23, 15, 30, 567, "+02:00"); + TbDate d2 = new TbDate(1975, 12, 31, 23, 15, 30, 567, "-02:00"); + + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + + d1.setUTCHours(5); + d2.setUTCHours(23); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + + d1.setUTCHours(23, 45); + d2.setUTCHours(1, 5); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + + d1.setUTCHours(0, 12, 59); + d2.setUTCHours(4, 45, 01); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + + d1.setUTCHours(2, 32, 49, 123); + d2.setUTCHours(8, 45, 12, 234); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + + d1.setUTCMinutes(5); + d2.setUTCMinutes(15); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + + d1.setUTCMinutes(5, 34); + d2.setUTCMinutes(25, 43); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + + d1.setUTCMinutes(25, 14, 567); + d2.setUTCMinutes(45, 3, 876); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + + d1.setUTCSeconds(5); + d2.setUTCSeconds(23); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + + d1.setUTCSeconds(45, 987); + d2.setUTCSeconds(54, 842); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + + d1.setUTCMilliseconds(675); + d1.setUTCMilliseconds(923); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + } + @Test + void TestMethodSetTime() { + TbDate d1 = new TbDate(1975, 12, 31, 23, 15, 30, 567, "-03:00"); + long dateMilliSecond = d1.getTime(); + int fiveMinutesInMillis = 5 * 60 * 1000; + Assert.assertEquals(15, d1.getUTCMinutes()); + Assert.assertEquals(4, d1.getUTCDay()); + d1.setTime(dateMilliSecond + fiveMinutesInMillis); + Assert.assertEquals(20, d1.getUTCMinutes()); + Assert.assertEquals(4, d1.getUTCDay()); + d1.setTime(-378682769433L); + Assert.assertEquals(1958, d1.getUTCFullYear()); + Assert.assertEquals(1, d1.getUTCMonth()); + Assert.assertEquals(1, d1.getUTCDate()); + Assert.assertEquals(2, d1.getUTCHours()); + Assert.assertEquals(20, d1.getUTCMinutes()); + Assert.assertEquals(30, d1.getUTCSeconds()); + Assert.assertEquals(567, d1.getUTCMilliseconds()); + Assert.assertEquals(3, d1.getUTCDay()); + } + @Test + void TestMethodSeFullYearMonthDate() { + TbDate d = new TbDate(2024, 1, 1, 1, 15, 30, 567); + testResultChangeDateTime(d); + + d = new TbDate(2023, 12, 31, 22, 15, 30, 567); + testResultChangeDateTime(d); + + d = new TbDate(1975, 12, 31, 1, 15, 30, 567); + testResultChangeDateTime(d); + + d.setFullYear(1969); + testResultChangeDateTime(d); + + d.setFullYear(1975, 2); + testResultChangeDateTime(d); + + d.setFullYear(2023, 6, 30); + testResultChangeDateTime(d); + + d.setMonth(2); + testResultChangeDateTime(d); + + d.setMonth(1, 24); + testResultChangeDateTime(d); + + d.setDate(6); + testResultChangeDateTime(d); + } + @Test + void TestMethodSeHoursMinutesSecondsMilliSec() { + TbDate d1 = new TbDate(1975, 12, 31, 23, 15, 30, 567, "+02:00"); + TbDate d2 = new TbDate(1975, 12, 31, 23, 15, 30, 567, "-02:00"); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + + d1.setHours(5); + d2.setHours(23); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + + d1.setHours(23, 45); + d2.setHours(1, 5); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + + d1.setHours(0, 12, 59); + d2.setHours(4, 45, 1); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + + d1.setHours(2, 32, 49, 123); + d2.setHours(8, 45, 12, 234); + testResultChangeDateTime(d1); + testResultChangeDateTime(d2); + + d1.setMinutes(5); + d2.setMinutes(15); + Assert.assertEquals(5, d1.getMinutes()); + Assert.assertEquals(15, d2.getMinutes()); + + d1.setMinutes(5, 34); + d2.setMinutes(25, 43); + Assert.assertEquals(5, d1.getMinutes()); + Assert.assertEquals(34, d1.getSeconds()); + Assert.assertEquals(25, d2.getMinutes()); + Assert.assertEquals(43, d2.getSeconds()); + + d1.setMinutes(25, 14, 567); + d2.setMinutes(45, 3, 876); + Assert.assertEquals(25, d1.getMinutes()); + Assert.assertEquals(14, d1.getSeconds()); + Assert.assertEquals(567, d1.getMilliseconds()); + Assert.assertEquals(45, d2.getMinutes()); + Assert.assertEquals(3, d2.getSeconds()); + Assert.assertEquals(876, d2.getMilliseconds()); + + d1.setSeconds(5); + d2.setSeconds(23); + Assert.assertEquals(5, d1.getSeconds()); + Assert.assertEquals(23, d2.getSeconds()); + + d1.setSeconds(45, 987); + d2.setSeconds(54, 842); + Assert.assertEquals(45, d1.getSeconds()); + Assert.assertEquals(987, d1.getMilliseconds()); + Assert.assertEquals(54, d2.getSeconds()); + Assert.assertEquals(842, d2.getMilliseconds()); + + d1.setMilliseconds(675); + d2.setMilliseconds(923); + Assert.assertEquals(675, d1.getMilliseconds()); + Assert.assertEquals(923, d2.getMilliseconds()); + } + @Test + public void toStringAsJs() { + TbDate d1 = new TbDate(1975, 12, 31, 23,15,30, 567,"-04:00"); + Assert.assertEquals("четвер, 1 січня 1976 р. о 06:15:30 за східноєвропейським стандартним часом", d1.toString("uk-UA", "Europe/Kyiv")); + Assert.assertEquals("Thursday, January 1, 1976 at 6:15:30 AM Eastern European Standard Time", d1.toString("en-US", "Europe/Kyiv")); + Assert.assertEquals("1976 Jan 1, Thu 06:15:30 Eastern European Time", d1.toString("UTC", "Europe/Kyiv")); + Assert.assertEquals("Wednesday, December 31, 1975 at 10:15:30 PM Eastern Standard Time", d1.toString("en-US", "America/New_York")); + Assert.assertEquals("1975 Dec 31, Wed 22:15:30 Eastern Standard Time", d1.toString("GMT", "America/New_York")); + Assert.assertEquals("1975 Dec 31, Wed 22:15:30 Eastern Standard Time", d1.toString("UTC", "America/New_York")); + + Assert.assertEquals(d1.toUTCString("UTC"), d1.toUTCString()); + Assert.assertEquals("четвер, 1 січня 1976 р., 03:15:30", d1.toUTCString("uk-UA")); + Assert.assertEquals("Thursday, January 1, 1976, 3:15:30 AM", d1.toUTCString("en-US")); + + Assert.assertEquals("1976-01-01T03:15:30.567Z", d1.toJSON()); + Assert.assertEquals("1976-01-01T03:15:30.567Z", d1.toISOString()); + + Assert.assertEquals("1976-01-01 06:15:30", d1.toLocaleString("UTC", "Europe/Kyiv")); + Assert.assertEquals("01.01.76, 06:15:30", d1.toLocaleString("uk-UA", "Europe/Kyiv")); + Assert.assertEquals("1975-12-31 22:15:30", d1.toLocaleString("UTC", "America/New_York")); + Assert.assertEquals("12/31/75, 10:15:30 PM", d1.toLocaleString("en-US", "America/New_York")); + + Assert.assertEquals("1976 Jan 1, Thu", d1.toDateString("UTC", "Europe/Kyiv")); + Assert.assertEquals("четвер, 1 січня 1976 р.", d1.toDateString("uk-UA", "Europe/Kyiv")); + Assert.assertEquals("1975 Dec 31, Wed", d1.toDateString("UTC", "America/New_York")); + Assert.assertEquals("Wednesday, December 31, 1975", d1.toDateString("en-US", "America/New_York")); + + Assert.assertEquals("06:15:30", d1.toLocaleTimeString("uk-UA", "Europe/Kyiv")); + Assert.assertEquals("06:15:30", d1.toLocaleTimeString("UTC", "Europe/Kyiv")); + Assert.assertEquals("10:15:30 PM", d1.toLocaleTimeString("en-US", "America/New_York")); + Assert.assertEquals("22:15:30", d1.toLocaleTimeString("UTC", "America/New_York")); + + Assert.assertEquals("06:15:30 за східноєвропейським стандартним часом", d1.toTimeString("uk-UA", "Europe/Kyiv")); + Assert.assertEquals("06:15:30 Eastern European Time", d1.toTimeString("UTC", "Europe/Kyiv")); + Assert.assertEquals("10:15:30 PM Eastern Standard Time", d1.toTimeString("en-US", "America/New_York")); + Assert.assertEquals("22:15:30 Eastern Standard Time", d1.toTimeString("UTC", "America/New_York")); + } + + @Test + public void testNow() { + Assertions.assertThat(TbDate.now()).isCloseTo(Instant.now().toEpochMilli(), Offset.offset(1000L)); + } + + private void testResultChangeDateTime(TbDate d) { + int localOffset = ZoneId.systemDefault().getRules().getOffset(d.getInstant()).getTotalSeconds(); + TbDateTestEntity tbDateTestEntity = new TbDateTestEntity(d.getFullYear(), d.getMonth(), d.getDate(), (d.getHours() - (localOffset/60/60))); + Assert.assertEquals(tbDateTestEntity.getYear(), d.getUTCFullYear()); + Assert.assertEquals(tbDateTestEntity.getMonth(), d.getUTCMonth()); + Assert.assertEquals(tbDateTestEntity.getDate(), d.getUTCDate()); + Assert.assertEquals(tbDateTestEntity.getHours(), d.getUTCHours()); + Assert.assertEquals(d.getMinutes(), d.getUTCMinutes()); + Assert.assertEquals(d.getSeconds(), d.getUTCSeconds()); + Assert.assertEquals(d.getMilliseconds(), d.getUTCMilliseconds()); + } + + @Test + public void tbDateSerializedMapperTest() { + String stringDateUTC = "2023-09-06T01:04:05.345Z"; + TbDate expectedDate = new TbDate(stringDateUTC); + String serializedTbDate = JacksonUtil.toJsonNode(JacksonUtil.toString(Map.of("date", expectedDate))).get("date").asText(); + Assert.assertNotNull(serializedTbDate); + Assert.assertEquals(expectedDate.toString(), serializedTbDate); + } +} diff --git a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbDateTestEntity.java b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbDateTestEntity.java new file mode 100644 index 0000000000..3c6c5f56ff --- /dev/null +++ b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbDateTestEntity.java @@ -0,0 +1,92 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.script.api.tbel; + +import lombok.Data; + +import java.time.chrono.IsoChronology; + +@Data +public class TbDateTestEntity { + private int year; + private int month; + private int date; + private int hours; + public TbDateTestEntity(int year, int month, int date, int hours) { + this.year = year; + this.month = month; + this.date = date; + this.hours = hours; + if (hours > 23) { + if (date == 31) { + this.year++; + this.month = 1; + this.date = 1; + } else { + this.date++; + } + this.hours = hours - 24; + } else if (hours < 0) { + if (month== 1 && date == 1) { + this.year--; + this.month = 12; + this.date = 31; + } else { + this.date--; + } + this.hours = hours + 24; + } + + if (this.date > 28) { + int dom = 31; + switch (month) { + case 2: + dom = IsoChronology.INSTANCE.isLeapYear((long) year) ? 29 : 28; + case 3: + case 5: + case 7: + case 8: + case 10: + default: + break; + case 4: + case 6: + case 9: + case 11: + dom = 30; + } + if (this.date > dom) { + this.date = this.date - dom; + this.month++; + } + } + } + public int getYear(){ + return year < 70 ? 2000 + year : year <= 99 ? 1900 + year : year; + } + + public String geMonthStr(){ + return String.format("%02d", month); + } + + public String geDateStr(){ + return String.format("%02d", date); + } + + public String geHoursStr(){ + return String.format("%02d", hours); + } +} diff --git a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java index 82cd74ca30..7d5ae00dcf 100644 --- a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java +++ b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java @@ -15,6 +15,7 @@ */ package org.thingsboard.script.api.tbel; +import com.google.common.primitives.Bytes; import lombok.extern.slf4j.Slf4j; import org.junit.After; import org.junit.Assert; @@ -26,8 +27,11 @@ import org.mvel2.SandboxedParserConfiguration; import org.mvel2.execution.ExecutionArrayList; import org.mvel2.execution.ExecutionHashMap; +import java.io.IOException; +import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; import java.util.List; import java.util.Random; @@ -38,6 +42,23 @@ public class TbUtilsTest { private ExecutionContext ctx; + private final String intValHex = "41EA62CC"; + private final float floatVal = 29.29824f; + private final String floatValStr = "29.29824"; + + + private final String floatValHexRev = "CC62EA41"; + private final float floatValRev = -5.948442E7f; + + private final long longVal = 0x409B04B10CB295EAL; + private final String longValHex = "409B04B10CB295EA"; + private final long longValRev = 0xEA95B20CB1049B40L; + private final String longValHexRev = "EA95B20CB1049B40"; + private final String doubleValStr = "1729.1729"; + private final double doubleVal = 1729.1729; + private final double doubleValRev = -2.7208640774822924E205; + + @Before public void before() { SandboxedParserConfiguration parserConfig = ParserContext.enableSandboxedMode(); @@ -62,6 +83,7 @@ public class TbUtilsTest { @Test public void parseHexToInt() { Assert.assertEquals(0xAB, TbUtils.parseHexToInt("AB")); + Assert.assertEquals(0xABBA, TbUtils.parseHexToInt("ABBA", true)); Assert.assertEquals(0xBAAB, TbUtils.parseHexToInt("ABBA", false)); Assert.assertEquals(0xAABBCC, TbUtils.parseHexToInt("AABBCC", true)); @@ -182,11 +204,277 @@ public class TbUtilsTest { Assert.assertEquals(expectedMapWithoutPaths, actualMapWithoutPaths); } + @Test + public void parseInt() { + Assert.assertNull(TbUtils.parseInt(null)); + Assert.assertNull(TbUtils.parseInt("")); + Assert.assertNull(TbUtils.parseInt(" ")); + + Assert.assertEquals(java.util.Optional.of(0).get(), TbUtils.parseInt("0")); + Assert.assertEquals(java.util.Optional.of(0).get(), TbUtils.parseInt("-0")); + Assert.assertEquals(java.util.Optional.of(473).get(), TbUtils.parseInt("473")); + Assert.assertEquals(java.util.Optional.of(-255).get(), TbUtils.parseInt("-0xFF")); + Assert.assertThrows(NumberFormatException.class, () -> TbUtils.parseInt("FF")); + Assert.assertThrows(NumberFormatException.class, () -> TbUtils.parseInt("0xFG")); + + Assert.assertEquals(java.util.Optional.of(102).get(), TbUtils.parseInt("1100110", 2)); + Assert.assertThrows(NumberFormatException.class, () -> TbUtils.parseInt("1100210", 2)); + + Assert.assertEquals(java.util.Optional.of(63).get(), TbUtils.parseInt("77", 8)); + Assert.assertThrows(NumberFormatException.class, () -> TbUtils.parseInt("18", 8)); + + Assert.assertEquals(java.util.Optional.of(-255).get(), TbUtils.parseInt("-FF", 16)); + Assert.assertThrows(NumberFormatException.class, () -> TbUtils.parseInt("FG", 16)); + + + Assert.assertEquals(java.util.Optional.of(Integer.MAX_VALUE).get(), TbUtils.parseInt(Integer.toString(Integer.MAX_VALUE), 10)); + Assert.assertThrows(NumberFormatException.class, () -> TbUtils.parseInt(BigInteger.valueOf(Integer.MAX_VALUE).add(BigInteger.valueOf(1)).toString(10), 10)); + Assert.assertEquals(java.util.Optional.of(Integer.MIN_VALUE).get(), TbUtils.parseInt(Integer.toString(Integer.MIN_VALUE), 10)); + Assert.assertThrows(NumberFormatException.class, () -> TbUtils.parseInt(BigInteger.valueOf(Integer.MIN_VALUE).subtract(BigInteger.valueOf(1)).toString(10), 10)); - private static String keyToValue(String key, String extraSymbol) { - return key + "Value" + (extraSymbol == null ? "" : extraSymbol); + Assert.assertEquals(java.util.Optional.of(506070563).get(), TbUtils.parseInt("KonaIn", 30)); + Assert.assertThrows(NumberFormatException.class, () -> TbUtils.parseInt("KonaIn", 10)); + } + + @Test + public void parseFloat() { + Assert.assertEquals(java.util.Optional.of(floatVal).get(), TbUtils.parseFloat(floatValStr)); + } + + @Test + public void toFixedFloat() { + float actualF = TbUtils.toFixed(floatVal, 3); + Assert.assertEquals(1, Float.compare(floatVal, actualF)); + Assert.assertEquals(0, Float.compare(29.298f, actualF)); + } + + @Test + public void parseHexToFloat() { + Assert.assertEquals(0, Float.compare(floatVal, TbUtils.parseHexToFloat(intValHex))); + Assert.assertEquals(0, Float.compare(floatValRev, TbUtils.parseHexToFloat(intValHex, false))); + Assert.assertEquals(0, Float.compare(floatVal, TbUtils.parseBigEndianHexToFloat(intValHex))); + Assert.assertEquals(0, Float.compare(floatVal, TbUtils.parseLittleEndianHexToFloat(floatValHexRev))); + } + + @Test + public void arseBytesToFloat() { + byte[] floatValByte = {65, -22, 98, -52}; + Assert.assertEquals(0, Float.compare(floatVal, TbUtils.parseBytesToFloat(floatValByte, 0))); + Assert.assertEquals(0, Float.compare(floatValRev, TbUtils.parseBytesToFloat(floatValByte, 0, false))); + + List floatVaList = Bytes.asList(floatValByte); + Assert.assertEquals(0, Float.compare(floatVal, TbUtils.parseBytesToFloat(floatVaList, 0))); + Assert.assertEquals(0, Float.compare(floatValRev, TbUtils.parseBytesToFloat(floatVaList, 0, false))); + } + + @Test + public void parseLong() { + Assert.assertNull(TbUtils.parseLong(null)); + Assert.assertNull(TbUtils.parseLong("")); + Assert.assertNull(TbUtils.parseLong(" ")); + + Assert.assertEquals(java.util.Optional.of(0L).get(), TbUtils.parseLong("0")); + Assert.assertEquals(java.util.Optional.of(0L).get(), TbUtils.parseLong("-0")); + Assert.assertEquals(java.util.Optional.of(473L).get(), TbUtils.parseLong("473")); + Assert.assertEquals(java.util.Optional.of(-65535L).get(), TbUtils.parseLong("-0xFFFF")); + Assert.assertThrows(NumberFormatException.class, () -> TbUtils.parseLong("FFFFFFFF")); + Assert.assertThrows(NumberFormatException.class, () -> TbUtils.parseLong("0xFGFFFFFF")); + + Assert.assertEquals(java.util.Optional.of(13158L).get(), TbUtils.parseLong("11001101100110", 2)); + Assert.assertThrows(NumberFormatException.class, () -> TbUtils.parseLong("11001101100210", 2)); + + Assert.assertEquals(java.util.Optional.of(9223372036854775807L).get(), TbUtils.parseLong("777777777777777777777", 8)); + Assert.assertThrows(NumberFormatException.class, () -> TbUtils.parseLong("1787", 8)); + + Assert.assertEquals(java.util.Optional.of(-255L).get(), TbUtils.parseLong("-FF", 16)); + Assert.assertThrows(NumberFormatException.class, () -> TbUtils.parseLong("FG", 16)); + + + Assert.assertEquals(java.util.Optional.of(Long.MAX_VALUE).get(), TbUtils.parseLong(Long.toString(Long.MAX_VALUE), 10)); + Assert.assertThrows(NumberFormatException.class, () -> TbUtils.parseLong(BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.valueOf(1)).toString(10), 10)); + Assert.assertEquals(java.util.Optional.of(Long.MIN_VALUE).get(), TbUtils.parseLong(Long.toString(Long.MIN_VALUE), 10)); + Assert.assertThrows(NumberFormatException.class, () -> TbUtils.parseLong(BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.valueOf(1)).toString(10), 10)); + + Assert.assertEquals(java.util.Optional.of(218840926543L).get(), TbUtils.parseLong("KonaLong", 27)); + Assert.assertThrows(NumberFormatException.class, () -> TbUtils.parseLong("KonaLong", 10)); + } + + @Test + public void parseHexToLong() { + Assert.assertEquals(longVal, TbUtils.parseHexToLong(longValHex)); + Assert.assertEquals(longVal, TbUtils.parseHexToLong(longValHexRev, false)); + Assert.assertEquals(longVal, TbUtils.parseBigEndianHexToLong(longValHex)); + Assert.assertEquals(longVal, TbUtils.parseLittleEndianHexToLong(longValHexRev)); + } + + @Test + public void parseBytesToLong() { + byte[] longValByte = {64, -101, 4, -79, 12, -78, -107, -22}; + Assert.assertEquals(longVal, TbUtils.parseBytesToLong(longValByte, 0, 8)); + Bytes.reverse(longValByte); + Assert.assertEquals(longVal, TbUtils.parseBytesToLong(longValByte, 0, 8, false)); + + List longVaList = Bytes.asList(longValByte); + Assert.assertEquals(longVal, TbUtils.parseBytesToLong(longVaList, 0, 8, false)); + Assert.assertEquals(longValRev, TbUtils.parseBytesToLong(longVaList, 0, 8)); + } + + @Test + public void parsDouble() { + Assert.assertEquals(java.util.Optional.of(doubleVal).get(), TbUtils.parseDouble(doubleValStr)); + } + + @Test + public void toFixedDouble() { + double actualD = TbUtils.toFixed(doubleVal, 3); + Assert.assertEquals(-1, Double.compare(doubleVal, actualD)); + Assert.assertEquals(0, Double.compare(1729.173, actualD)); } + @Test + public void parseHexToDouble() { + Assert.assertEquals(0, Double.compare(doubleVal, TbUtils.parseHexToDouble(longValHex))); + Assert.assertEquals(0, Double.compare(doubleValRev, TbUtils.parseHexToDouble(longValHex, false))); + Assert.assertEquals(0, Double.compare(doubleVal, TbUtils.parseBigEndianHexToDouble(longValHex))); + Assert.assertEquals(0, Double.compare(doubleVal, TbUtils.parseLittleEndianHexToDouble(longValHexRev))); + } + + @Test + public void parseBytesToDouble() { + byte[] doubleValByte = {64, -101, 4, -79, 12, -78, -107, -22}; + Assert.assertEquals(0, Double.compare(doubleVal, TbUtils.parseBytesToDouble(doubleValByte, 0))); + Assert.assertEquals(0, Double.compare(doubleValRev, TbUtils.parseBytesToDouble(doubleValByte, 0, false))); + + List doubleVaList = Bytes.asList(doubleValByte); + Assert.assertEquals(0, Double.compare(doubleVal, TbUtils.parseBytesToDouble(doubleVaList, 0))); + Assert.assertEquals(0, Double.compare(doubleValRev, TbUtils.parseBytesToDouble(doubleVaList, 0, false))); + } + + @Test + public void parseBytesDecodeToJson() throws IOException, IllegalAccessException { + String expectedStr = "{\"hello\": \"world\"}"; + ExecutionHashMap expectedJson = new ExecutionHashMap<>(1, ctx); + expectedJson.put("hello", "world"); + List expectedBytes = TbUtils.stringToBytes(ctx, expectedStr); + Object actualJson = TbUtils.decodeToJson(ctx, expectedBytes); + Assert.assertEquals(expectedJson,actualJson); + } + @Test + public void parseStringDecodeToJson() throws IOException { + String expectedStr = "{\"hello\": \"world\"}"; + ExecutionHashMap expectedJson = new ExecutionHashMap<>(1, ctx); + expectedJson.put("hello", "world"); + Object actualJson = TbUtils.decodeToJson(ctx, expectedStr); + Assert.assertEquals(expectedJson,actualJson); + } + + @Test + public void stringToBytesInputObjectTest() throws IOException, IllegalAccessException { + String expectedStr = "{\"hello\": \"world\"}"; + Object inputJson = TbUtils.decodeToJson(ctx, expectedStr); + byte[] arrayBytes = {119, 111, 114, 108, 100}; + List expectedBytes = Bytes.asList(arrayBytes); + List actualBytes = TbUtils.stringToBytes(ctx, ((ExecutionHashMap) inputJson).get("hello")); + Assert.assertEquals(expectedBytes, actualBytes); + actualBytes = TbUtils.stringToBytes(ctx, ((ExecutionHashMap) inputJson).get("hello"), "UTF-8"); + Assert.assertEquals(expectedBytes, actualBytes); + + expectedStr = "{\"hello\": 1234}"; + inputJson = TbUtils.decodeToJson(ctx, expectedStr); + Object finalInputJson = inputJson; + Assert.assertThrows(IllegalAccessException.class, () -> TbUtils.stringToBytes(ctx, ((ExecutionHashMap) finalInputJson).get("hello"))); + Assert.assertThrows(IllegalAccessException.class, () -> TbUtils.stringToBytes(ctx, ((ExecutionHashMap) finalInputJson).get("hello"), "UTF-8")); + } + + @Test + public void bytesFromList() { + byte[] arrayBytes = {(byte)0x00, (byte)0x08, (byte)0x10, (byte)0x1C, (byte)0xFF, (byte)0xFC, (byte)0xAD, (byte)0x88, (byte)0x75, (byte)0x74, (byte)0x8A, (byte)0x82}; + Object[] arrayMix = { "0x00", 8, "16", "0x1C", 255, (byte)0xFC, 173, 136, 117, 116, -118, "-126"}; + + String expected = new String(arrayBytes); + ArrayList listBytes = new ArrayList<>(arrayBytes.length); + for (Byte element : arrayBytes) { + listBytes.add(element); + } + Assert.assertEquals(expected, TbUtils.bytesToString(listBytes)); + + ArrayList listMix = new ArrayList<>(arrayMix.length); + for (Object element : arrayMix) { + listMix.add(element); + } + Assert.assertEquals(expected, TbUtils.bytesToString(listMix)); + } + + @Test + public void bytesFromList_Error() { + List listHex = new ArrayList<>(); + listHex.add("0xFG"); + try { + TbUtils.bytesToString(listHex); + Assert.fail("Should throw NumberFormatException"); + } catch (NumberFormatException e) { + Assert.assertTrue(e.getMessage().contains("Failed radix: [16] for value: \"FG\"!")); + } + listHex.add(0, "1F"); + try { + TbUtils.bytesToString(listHex); + Assert.fail("Should throw NumberFormatException"); + } catch (NumberFormatException e) { + Assert.assertTrue(e.getMessage().contains("Failed radix: [10] for value: \"1F\"!")); + } + + List listIntString = new ArrayList<>(); + listIntString.add("-129"); + try { + TbUtils.bytesToString(listIntString); + Assert.fail("Should throw NumberFormatException"); + } catch (NumberFormatException e) { + Assert.assertTrue(e.getMessage().contains("The value '-129' could not be correctly converted to a byte. " + + "Integer to byte conversion requires the use of only 8 bits (with a range of min/max = -128/255)!")); + } + + listIntString.add(0, "256"); + try { + TbUtils.bytesToString(listIntString); + Assert.fail("Should throw NumberFormatException"); + } catch (NumberFormatException e) { + Assert.assertTrue(e.getMessage().contains("The value '256' could not be correctly converted to a byte. " + + "Integer to byte conversion requires the use of only 8 bits (with a range of min/max = -128/255)!")); + } + + ArrayList listIntBytes = new ArrayList<>(); + listIntBytes.add(-129); + try { + TbUtils.bytesToString(listIntBytes); + Assert.fail("Should throw NumberFormatException"); + } catch (NumberFormatException e) { + Assert.assertTrue(e.getMessage().contains("The value '-129' could not be correctly converted to a byte. " + + "Integer to byte conversion requires the use of only 8 bits (with a range of min/max = -128/255)!")); + } + + listIntBytes.add(0, 256); + try { + TbUtils.bytesToString(listIntBytes); + Assert.fail("Should throw NumberFormatException"); + } catch (NumberFormatException e) { + Assert.assertTrue(e.getMessage().contains("The value '256' could not be correctly converted to a byte. " + + "Integer to byte conversion requires the use of only 8 bits (with a range of min/max = -128/255)!")); + } + + ArrayList listObjects = new ArrayList<>(); + ArrayList listStringObjects = new ArrayList<>(); + listStringObjects.add("0xFD"); + listObjects.add(listStringObjects); + try { + TbUtils.bytesToString(listObjects); + Assert.fail("Should throw NumberFormatException"); + } catch (NumberFormatException e) { + Assert.assertTrue(e.getMessage().contains("The value '[0xFD]' could not be correctly converted to a byte. " + + "Must be a HexDecimal/String/Integer/Byte format !")); + } + } + + private static List toList(byte[] data) { List result = new ArrayList<>(data.length); for (Byte b : data) { @@ -194,5 +482,5 @@ public class TbUtilsTest { } return result; } - } + diff --git a/common/stats/pom.xml b/common/stats/pom.xml index 10d4ea3b95..2dce8dc465 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/stats/src/main/java/org/thingsboard/server/common/stats/FstStatsServiceImpl.java b/common/stats/src/main/java/org/thingsboard/server/common/stats/FstStatsServiceImpl.java new file mode 100644 index 0000000000..b5cb25f2dc --- /dev/null +++ b/common/stats/src/main/java/org/thingsboard/server/common/stats/FstStatsServiceImpl.java @@ -0,0 +1,58 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.stats; + +import io.micrometer.core.instrument.Timer; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.FstStatsService; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +@Service +public class FstStatsServiceImpl implements FstStatsService { + private final ConcurrentHashMap encodeCounters = new ConcurrentHashMap<>(); + private final ConcurrentHashMap decodeCounters = new ConcurrentHashMap<>(); + private final ConcurrentHashMap encodeTimers = new ConcurrentHashMap<>(); + private final ConcurrentHashMap decodeTimer = new ConcurrentHashMap<>(); + + @Autowired + private StatsFactory statsFactory; + + @Override + public void incrementEncode(Class clazz) { + encodeCounters.computeIfAbsent(clazz.getSimpleName(), key -> statsFactory.createStatsCounter("fst_encode", key)).increment(); + } + + @Override + public void incrementDecode(Class clazz) { + decodeCounters.computeIfAbsent(clazz.getSimpleName(), key -> statsFactory.createStatsCounter("fst_decode", key)).increment(); + } + + @Override + public void recordEncodeTime(Class clazz, long startTime) { + encodeTimers.computeIfAbsent(clazz.getSimpleName(), + key -> statsFactory.createTimer("fst_encode_time", "statsName", key)).record(System.nanoTime() - startTime, TimeUnit.NANOSECONDS); + } + + @Override + public void recordDecodeTime(Class clazz, long startTime) { + decodeTimer.computeIfAbsent(clazz.getSimpleName(), + key -> statsFactory.createTimer("fst_decode_time", "statsName", key)).record(System.nanoTime() - startTime, TimeUnit.NANOSECONDS); + } + +} diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index 3e1174362c..ee8c5dba86 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapSessionMsgType.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapSessionMsgType.java new file mode 100644 index 0000000000..fcf33ea037 --- /dev/null +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapSessionMsgType.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap; + +public enum CoapSessionMsgType { + + GET_ATTRIBUTES_REQUEST, + POST_ATTRIBUTES_REQUEST, + SUBSCRIBE_ATTRIBUTES_REQUEST, + UNSUBSCRIBE_ATTRIBUTES_REQUEST, + POST_TELEMETRY_REQUEST, + SUBSCRIBE_RPC_COMMANDS_REQUEST, + UNSUBSCRIBE_RPC_COMMANDS_REQUEST, + TO_DEVICE_RPC_RESPONSE, + TO_SERVER_RPC_REQUEST, + CLAIM_REQUEST; + +} diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java index 7dde25bfd0..63979ce500 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java @@ -30,13 +30,13 @@ import org.thingsboard.server.coapserver.TbCoapDtlsSessionInfo; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TransportPayloadType; import org.thingsboard.server.common.data.security.DeviceTokenCredentials; import org.thingsboard.server.common.msg.session.FeatureType; -import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.common.transport.TransportServiceCallback; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.common.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.coap.callback.CoapDeviceAuthCallback; @@ -120,7 +120,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { } else if (exchange.getRequestOptions().hasObserve()) { processExchangeGetRequest(exchange, featureType.get()); } else if (featureType.get() == FeatureType.ATTRIBUTES) { - processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST); + processRequest(exchange, CoapSessionMsgType.GET_ATTRIBUTES_REQUEST); } else { log.trace("Invalid feature type parameter"); exchange.respond(CoAP.ResponseCode.BAD_REQUEST); @@ -129,13 +129,13 @@ public class CoapTransportResource extends AbstractCoapTransportResource { private void processExchangeGetRequest(CoapExchange exchange, FeatureType featureType) { boolean unsubscribe = exchange.getRequestOptions().getObserve() == 1; - SessionMsgType sessionMsgType; + CoapSessionMsgType coapSessionMsgType; if (featureType == FeatureType.RPC) { - sessionMsgType = unsubscribe ? SessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST : SessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST; + coapSessionMsgType = unsubscribe ? CoapSessionMsgType.UNSUBSCRIBE_RPC_COMMANDS_REQUEST : CoapSessionMsgType.SUBSCRIBE_RPC_COMMANDS_REQUEST; } else { - sessionMsgType = unsubscribe ? SessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST : SessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST; + coapSessionMsgType = unsubscribe ? CoapSessionMsgType.UNSUBSCRIBE_ATTRIBUTES_REQUEST : CoapSessionMsgType.SUBSCRIBE_ATTRIBUTES_REQUEST; } - processRequest(exchange, sessionMsgType); + processRequest(exchange, coapSessionMsgType); } @Override @@ -147,21 +147,21 @@ public class CoapTransportResource extends AbstractCoapTransportResource { } else { switch (featureType.get()) { case ATTRIBUTES: - processRequest(exchange, SessionMsgType.POST_ATTRIBUTES_REQUEST); + processRequest(exchange, CoapSessionMsgType.POST_ATTRIBUTES_REQUEST); break; case TELEMETRY: - processRequest(exchange, SessionMsgType.POST_TELEMETRY_REQUEST); + processRequest(exchange, CoapSessionMsgType.POST_TELEMETRY_REQUEST); break; case RPC: Optional requestId = getRequestId(exchange.advanced().getRequest()); if (requestId.isPresent()) { - processRequest(exchange, SessionMsgType.TO_DEVICE_RPC_RESPONSE); + processRequest(exchange, CoapSessionMsgType.TO_DEVICE_RPC_RESPONSE); } else { - processRequest(exchange, SessionMsgType.TO_SERVER_RPC_REQUEST); + processRequest(exchange, CoapSessionMsgType.TO_SERVER_RPC_REQUEST); } break; case CLAIM: - processRequest(exchange, SessionMsgType.CLAIM_REQUEST); + processRequest(exchange, CoapSessionMsgType.CLAIM_REQUEST); break; case PROVISION: processProvision(exchange); @@ -195,7 +195,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { } } - private void processRequest(CoapExchange exchange, SessionMsgType type) { + private void processRequest(CoapExchange exchange, CoapSessionMsgType type) { log.trace("Processing {}", exchange.advanced().getRequest()); deferAccept(exchange); Exchange advanced = exchange.advanced(); @@ -218,7 +218,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { } } - private void processAccessTokenRequest(CoapExchange exchange, SessionMsgType type, Request request) { + private void processAccessTokenRequest(CoapExchange exchange, CoapSessionMsgType type, Request request) { Optional credentials = decodeCredentials(request); if (credentials.isEmpty()) { exchange.respond(CoAP.ResponseCode.UNAUTHORIZED); @@ -228,7 +228,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { new CoapDeviceAuthCallback(exchange, (deviceCredentials, deviceProfile) -> processRequest(exchange, type, request, deviceCredentials, deviceProfile))); } - private void processRequest(CoapExchange exchange, SessionMsgType type, Request request, ValidateDeviceCredentialsResponse deviceCredentials, DeviceProfile deviceProfile) { + private void processRequest(CoapExchange exchange, CoapSessionMsgType type, Request request, ValidateDeviceCredentialsResponse deviceCredentials, DeviceProfile deviceProfile) { TbCoapClientState clientState = null; try { clientState = clients.getOrCreateClient(type, deviceCredentials, deviceProfile); @@ -380,12 +380,16 @@ public class CoapTransportResource extends AbstractCoapTransportResource { } } - private Optional getFeatureType(Request request) { + protected Optional getFeatureType(Request request) { List uriPath = request.getOptions().getUriPath(); try { - if (uriPath.size() >= FEATURE_TYPE_POSITION) { + int size = uriPath.size(); + if (size >= FEATURE_TYPE_POSITION) { + if (size == FEATURE_TYPE_POSITION && StringUtils.isNumeric(uriPath.get(size - 1))) { + return Optional.of(FeatureType.valueOf(uriPath.get(FEATURE_TYPE_POSITION - 2).toUpperCase())); + } return Optional.of(FeatureType.valueOf(uriPath.get(FEATURE_TYPE_POSITION - 1).toUpperCase())); - } else if (uriPath.size() >= FEATURE_TYPE_POSITION_CERTIFICATE_REQUEST) { + } else if (size == FEATURE_TYPE_POSITION_CERTIFICATE_REQUEST) { if (uriPath.contains(DataConstants.PROVISION)) { return Optional.of(FeatureType.valueOf(DataConstants.PROVISION.toUpperCase())); } diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapAdaptorUtils.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapAdaptorUtils.java index 710f18a8b7..629e0380e0 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapAdaptorUtils.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapAdaptorUtils.java @@ -17,7 +17,7 @@ package org.thingsboard.server.transport.coap.adaptors; import org.eclipse.californium.core.coap.Request; import org.thingsboard.server.common.data.StringUtils; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.AdaptorException; import org.thingsboard.server.gen.transport.TransportProtos; import java.util.Arrays; diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java index 05a38e1ad1..e88251c93c 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java @@ -19,7 +19,7 @@ import com.google.protobuf.Descriptors; import com.google.protobuf.DynamicMessage; import org.eclipse.californium.core.coap.Request; import org.eclipse.californium.core.coap.Response; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.AdaptorException; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg; diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java index d81ff32edd..fffe1a7e1a 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java @@ -29,8 +29,8 @@ import org.eclipse.californium.core.coap.Response; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.common.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.coap.CoapTransportResource; diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/ProtoCoapAdaptor.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/ProtoCoapAdaptor.java index 4d3b0015ef..5287645591 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/ProtoCoapAdaptor.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/ProtoCoapAdaptor.java @@ -28,9 +28,9 @@ import org.eclipse.californium.core.coap.Response; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; -import org.thingsboard.server.common.transport.adaptor.ProtoConverter; +import org.thingsboard.server.common.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.JsonConverter; +import org.thingsboard.server.common.adaptor.ProtoConverter; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.coap.CoapTransportResource; diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/AbstractSyncSessionCallback.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/AbstractSyncSessionCallback.java index 6dc5cccf38..ca46d989e4 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/AbstractSyncSessionCallback.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/AbstractSyncSessionCallback.java @@ -81,6 +81,7 @@ public abstract class AbstractSyncSessionCallback implements SessionMsgListener protected void respond(Response response) { response.getOptions().setContentFormat(TbCoapContentFormatUtil.getContentFormat(exchange.getRequestOptions().getContentFormat(), state.getContentFormat())); + response.setConfirmable(exchange.advanced().getRequest().isConfirmable()); exchange.respond(response); } diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/CoapOkCallback.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/CoapOkCallback.java index db843a0ea3..04ce043331 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/CoapOkCallback.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/CoapOkCallback.java @@ -34,7 +34,9 @@ public class CoapOkCallback implements TransportServiceCallback { @Override public void onSuccess(Void msg) { - exchange.respond(new Response(onSuccessResponse)); + Response response = new Response(onSuccessResponse); + response.setConfirmable(isConRequest()); + exchange.respond(response); } @Override diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/GetAttributesSyncSessionCallback.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/GetAttributesSyncSessionCallback.java index 907e49f600..caacd31012 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/GetAttributesSyncSessionCallback.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/GetAttributesSyncSessionCallback.java @@ -20,7 +20,7 @@ import org.eclipse.californium.core.coap.CoAP; import org.eclipse.californium.core.coap.Request; import org.eclipse.californium.core.coap.Response; import org.eclipse.californium.core.server.resources.CoapExchange; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.AdaptorException; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.coap.client.TbCoapClientState; diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/ToServerRpcSyncSessionCallback.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/ToServerRpcSyncSessionCallback.java index 2209ea6613..fc35da0bd6 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/ToServerRpcSyncSessionCallback.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/callback/ToServerRpcSyncSessionCallback.java @@ -19,7 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.core.coap.CoAP; import org.eclipse.californium.core.coap.Request; import org.eclipse.californium.core.server.resources.CoapExchange; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.AdaptorException; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.coap.client.TbCoapClientState; diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/CoapClientContext.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/CoapClientContext.java index 36ef8472de..a98a04ce28 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/CoapClientContext.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/CoapClientContext.java @@ -18,8 +18,8 @@ package org.thingsboard.server.transport.coap.client; import org.eclipse.californium.core.observe.ObserveRelation; import org.eclipse.californium.core.server.resources.CoapExchange; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.transport.coap.CoapSessionMsgType; +import org.thingsboard.server.common.adaptor.AdaptorException; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos; @@ -33,7 +33,7 @@ public interface CoapClientContext { AtomicInteger getNotificationCounterByToken(String token); - TbCoapClientState getOrCreateClient(SessionMsgType type, ValidateDeviceCredentialsResponse deviceCredentials, DeviceProfile deviceProfile) throws AdaptorException; + TbCoapClientState getOrCreateClient(CoapSessionMsgType type, ValidateDeviceCredentialsResponse deviceCredentials, DeviceProfile deviceProfile) throws AdaptorException; TransportProtos.SessionInfoProto getNewSyncSession(TbCoapClientState clientState); diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DefaultCoapClientContext.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DefaultCoapClientContext.java index f70967b72d..f4346946a7 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DefaultCoapClientContext.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/DefaultCoapClientContext.java @@ -45,7 +45,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.rpc.RpcStatus; import org.thingsboard.server.common.msg.session.FeatureType; -import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.transport.coap.CoapSessionMsgType; import org.thingsboard.server.common.transport.DeviceDeletedEvent; import org.thingsboard.server.common.transport.DeviceProfileUpdatedEvent; import org.thingsboard.server.common.transport.DeviceUpdatedEvent; @@ -53,7 +53,7 @@ import org.thingsboard.server.common.transport.SessionMsgListener; import org.thingsboard.server.common.transport.TransportDeviceProfileCache; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportServiceCallback; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.AdaptorException; import org.thingsboard.server.common.transport.auth.SessionInfoCreator; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos; @@ -388,7 +388,7 @@ public class DefaultCoapClientContext implements CoapClientContext { } @Override - public TbCoapClientState getOrCreateClient(SessionMsgType type, ValidateDeviceCredentialsResponse deviceCredentials, DeviceProfile deviceProfile) throws AdaptorException { + public TbCoapClientState getOrCreateClient(CoapSessionMsgType type, ValidateDeviceCredentialsResponse deviceCredentials, DeviceProfile deviceProfile) throws AdaptorException { DeviceId deviceId = deviceCredentials.getDeviceInfo().getDeviceId(); TbCoapClientState state = getClientState(deviceId); state.lock(); diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/CoapEfentoTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/CoapEfentoTransportResource.java index 93061d522a..b3304c6a97 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/CoapEfentoTransportResource.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/CoapEfentoTransportResource.java @@ -15,13 +15,15 @@ */ package org.thingsboard.server.transport.coap.efento; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.protobuf.InvalidProtocolBufferException; import lombok.AllArgsConstructor; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import org.apache.logging.log4j.util.TriConsumer; import org.eclipse.californium.core.coap.CoAP; import org.eclipse.californium.core.coap.Request; -import org.eclipse.californium.core.coap.Response; import org.eclipse.californium.core.network.Exchange; import org.eclipse.californium.core.server.resources.CoapExchange; import org.eclipse.californium.core.server.resources.Resource; @@ -31,9 +33,12 @@ import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.EfentoCoapDeviceTypeConfiguration; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.ProtoConverter; import org.thingsboard.server.common.transport.auth.SessionInfoCreator; import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.gen.transport.coap.ConfigProtos; +import org.thingsboard.server.gen.transport.coap.DeviceInfoProtos; import org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos; import org.thingsboard.server.gen.transport.coap.MeasurementsProtos; import org.thingsboard.server.transport.coap.AbstractCoapTransportResource; @@ -43,13 +48,16 @@ import org.thingsboard.server.transport.coap.callback.CoapEfentoCallback; import org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils; import java.nio.ByteBuffer; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.UUID; import java.util.concurrent.TimeUnit; +import static com.google.gson.JsonParser.parseString; import static org.thingsboard.server.transport.coap.CoapTransportService.CONFIGURATION; import static org.thingsboard.server.transport.coap.CoapTransportService.CURRENT_TIMESTAMP; import static org.thingsboard.server.transport.coap.CoapTransportService.DEVICE_INFO; @@ -94,14 +102,13 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource { String requestType = uriPath.get(1); switch (requestType) { case MEASUREMENTS: - processMeasurementsRequest(exchange, request); + processMeasurementsRequest(exchange); break; case DEVICE_INFO: + processDeviceInfoRequest(exchange); + break; case CONFIGURATION: - //We respond only to confirmed requests in order to reduce battery consumption for Efento devices. - if (exchange.advanced().getRequest().isConfirmable()) { - exchange.respond(new Response(CoAP.ResponseCode.CREATED)); - } + processConfigurationRequest(exchange); break; default: exchange.respond(CoAP.ResponseCode.BAD_REQUEST); @@ -109,34 +116,92 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource { } } - private void processMeasurementsRequest(CoapExchange exchange, Request request) { - byte[] bytes = request.getPayload(); + private void processMeasurementsRequest(CoapExchange exchange) { + byte[] bytes = exchange.advanced().getRequest().getPayload(); try { MeasurementsProtos.ProtoMeasurements protoMeasurements = MeasurementsProtos.ProtoMeasurements.parseFrom(bytes); log.trace("Successfully parsed Efento ProtoMeasurements: [{}]", protoMeasurements.getCloudToken()); - String token = protoMeasurements.getCloudToken(); - transportService.process(DeviceTransportType.COAP, TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(token).build(), - new CoapDeviceAuthCallback(exchange, (msg, deviceProfile) -> { - TransportProtos.SessionInfoProto sessionInfo = SessionInfoCreator.create(msg, transportContext, UUID.randomUUID()); - UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); - try { - validateEfentoTransportConfiguration(deviceProfile); - List efentoMeasurements = getEfentoMeasurements(protoMeasurements, sessionId); - transportService.process(sessionInfo, - transportContext.getEfentoCoapAdaptor().convertToPostTelemetry(sessionId, efentoMeasurements), - new CoapEfentoCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)); - reportSubscriptionInfo(sessionInfo, false, false); - } catch (AdaptorException e) { - log.error("[{}] Failed to decode Efento ProtoMeasurements: ", sessionId, e); - exchange.respond(CoAP.ResponseCode.BAD_REQUEST); - } - })); + validateAndProcessEffentoMessage(protoMeasurements.getCloudToken(), exchange, (deviceProfile, sessionInfo, sessionId) -> { + try { + List measurements = getEfentoMeasurements(protoMeasurements, sessionId); + transportService.process(sessionInfo, + transportContext.getEfentoCoapAdaptor().convertToPostTelemetry(sessionId, measurements), + new CoapEfentoCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)); + } catch (AdaptorException e) { + log.error("[{}] Failed to decode Efento ProtoMeasurements: ", sessionId, e); + exchange.respond(CoAP.ResponseCode.BAD_REQUEST); + } + }); } catch (Exception e) { log.error("Failed to decode Efento ProtoMeasurements: ", e); exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); } } + private void processDeviceInfoRequest(CoapExchange exchange) { + byte[] bytes = exchange.advanced().getRequest().getPayload(); + try { + DeviceInfoProtos.ProtoDeviceInfo protoDeviceInfo = DeviceInfoProtos.ProtoDeviceInfo.parseFrom(bytes); + String token = protoDeviceInfo.getCloudToken(); + log.trace("Successfully parsed Efento ProtoDeviceInfo: [{}]", token); + validateAndProcessEffentoMessage(token, exchange, (deviceProfile, sessionInfo, sessionId) -> { + try { + EfentoTelemetry deviceInfo = getEfentoDeviceInfo(protoDeviceInfo); + transportService.process(sessionInfo, + transportContext.getEfentoCoapAdaptor().convertToPostTelemetry(sessionId, List.of(deviceInfo)), + new CoapEfentoCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)); + } catch (AdaptorException e) { + log.error("[{}] Failed to decode Efento ProtoDeviceInfo: ", sessionId, e); + exchange.respond(CoAP.ResponseCode.BAD_REQUEST); + } + }); + } catch (Exception e) { + log.error("Failed to decode Efento ProtoDeviceInfo: ", e); + exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); + } + } + + private void processConfigurationRequest(CoapExchange exchange) { + byte[] bytes = exchange.advanced().getRequest().getPayload(); + try { + ConfigProtos.ProtoConfig protoConfig = ConfigProtos.ProtoConfig.parseFrom(bytes); + String token = protoConfig.getCloudToken(); + log.trace("Successfully parsed Efento ProtoConfig: [{}]", token); + validateAndProcessEffentoMessage(token, exchange, (deviceProfile, sessionInfo, sessionId) -> { + try { + JsonElement configuration = getEfentoConfiguration(bytes); + transportService.process(sessionInfo, + transportContext.getEfentoCoapAdaptor().convertToPostAttributes(sessionId, configuration), + new CoapEfentoCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)); + } catch (AdaptorException e) { + log.error("[{}] Failed to decode Efento ProtoConfig: ", sessionId, e); + exchange.respond(CoAP.ResponseCode.BAD_REQUEST); + } catch (InvalidProtocolBufferException e) { + throw new RuntimeException(e); + } + }); + } catch (Exception e) { + log.error("Failed to decode Efento ProtoConfig: ", e); + exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); + } + } + + private void validateAndProcessEffentoMessage(String token, CoapExchange exchange, TriConsumer requestProcessor) { + transportService.process(DeviceTransportType.COAP, TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(token).build(), + new CoapDeviceAuthCallback(exchange, (msg, deviceProfile) -> { + TransportProtos.SessionInfoProto sessionInfo = SessionInfoCreator.create(msg, transportContext, UUID.randomUUID()); + UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); + try { + validateEfentoTransportConfiguration(deviceProfile); + requestProcessor.accept(deviceProfile, sessionInfo, sessionId); + reportSubscriptionInfo(sessionInfo, false, false); + } catch (AdaptorException e) { + log.error("[{}] Failed to decode Efento request: ", sessionId, e); + exchange.respond(CoAP.ResponseCode.BAD_REQUEST); + } + })); + } + @Override public Resource getChild(String name) { return this; @@ -155,7 +220,7 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource { } } - private List getEfentoMeasurements(MeasurementsProtos.ProtoMeasurements protoMeasurements, UUID sessionId) { + private List getEfentoMeasurements(MeasurementsProtos.ProtoMeasurements protoMeasurements, UUID sessionId) { String serialNumber = CoapEfentoUtils.convertByteArrayToString(protoMeasurements.getSerialNum().toByteArray()); boolean batteryStatus = protoMeasurements.getBatteryStatus(); int measurementPeriodBase = protoMeasurements.getMeasurementPeriodBase(); @@ -243,6 +308,12 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource { CoapEfentoUtils.setDefaultMeasurements(serialNumber, batteryStatus, measurementPeriod, nextTransmissionAtMillis, signal, k)); values.addProperty("ok_alarm_" + channel, data); break; + case PULSE_CNT: + values = valuesMap.computeIfAbsent(startTimestampMillis, k -> + CoapEfentoUtils.setDefaultMeasurements(serialNumber, batteryStatus, measurementPeriod, nextTransmissionAtMillis, signal, k)); + values.addProperty("pulse_cnt_" + channel, (double) (startPoint + sampleOffset)); + startTimestampMillis = startTimestampMillis + measurementPeriodMillis; + break; case NO_SENSOR: case UNRECOGNIZED: log.trace("[{}][{}] Sensor error value! Ignoring.", sessionId, measurementTypeName); @@ -261,9 +332,9 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource { throw new IllegalStateException("[" + sessionId + "]: Failed to get Efento measurements, reason: channels list is empty!"); } if (!CollectionUtils.isEmpty(valuesMap)) { - List efentoMeasurements = new ArrayList<>(); + List efentoMeasurements = new ArrayList<>(); for (Long ts : valuesMap.keySet()) { - EfentoMeasurements measurement = new EfentoMeasurements(ts, valuesMap.get(ts)); + EfentoTelemetry measurement = new EfentoTelemetry(ts, valuesMap.get(ts)); efentoMeasurements.add(measurement); } return efentoMeasurements; @@ -272,12 +343,107 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource { } } + private EfentoTelemetry getEfentoDeviceInfo(DeviceInfoProtos.ProtoDeviceInfo protoDeviceInfo) { + JsonObject values = new JsonObject(); + values.addProperty("sw_version", protoDeviceInfo.getSwVersion()); + + //memory statistics + values.addProperty("nv_storage_status", protoDeviceInfo.getMemoryStatistics(0)); + values.addProperty("timestamp_of_the_end_of_collecting_statistics", getDate(protoDeviceInfo.getMemoryStatistics(1))); + values.addProperty("capacity_of_memory_in_bytes", protoDeviceInfo.getMemoryStatistics(2)); + values.addProperty("used_space_in_bytes", protoDeviceInfo.getMemoryStatistics(3)); + values.addProperty("size_of_invalid_packets_in_bytes", protoDeviceInfo.getMemoryStatistics(4)); + values.addProperty("size_of_corrupted_packets_in_bytes", protoDeviceInfo.getMemoryStatistics(5)); + values.addProperty("number_of_valid_packets", protoDeviceInfo.getMemoryStatistics(6)); + values.addProperty("number_of_invalid_packets", protoDeviceInfo.getMemoryStatistics(7)); + values.addProperty("number_of_corrupted_packets", protoDeviceInfo.getMemoryStatistics(8)); + values.addProperty("number_of_all_samples_for_channel_1", protoDeviceInfo.getMemoryStatistics(9)); + values.addProperty("number_of_all_samples_for_channel_2", protoDeviceInfo.getMemoryStatistics(10)); + values.addProperty("number_of_all_samples_for_channel_3", protoDeviceInfo.getMemoryStatistics(11)); + values.addProperty("number_of_all_samples_for_channel_4", protoDeviceInfo.getMemoryStatistics(12)); + values.addProperty("number_of_all_samples_for_channel_5", protoDeviceInfo.getMemoryStatistics(13)); + values.addProperty("number_of_all_samples_for_channel_6", protoDeviceInfo.getMemoryStatistics(14)); + values.addProperty("timestamp_of_the_first_binary_measurement", getDate(protoDeviceInfo.getMemoryStatistics(15))); + values.addProperty("timestamp_of_the_last_binary_measurement", getDate(protoDeviceInfo.getMemoryStatistics(16))); + values.addProperty("timestamp_of_the_first_binary_measurement_sent", getDate(protoDeviceInfo.getMemoryStatistics(17))); + values.addProperty("timestamp_of_the_first_continuous_measurement", getDate(protoDeviceInfo.getMemoryStatistics(18))); + values.addProperty("timestamp_of_the_last_continuous_measurement", getDate(protoDeviceInfo.getMemoryStatistics(19))); + values.addProperty("timestamp_of_the_last_continuous_measurement_sent", getDate(protoDeviceInfo.getMemoryStatistics(20))); + values.addProperty("nvm_write_counter", protoDeviceInfo.getMemoryStatistics(21)); + + //modem info + DeviceInfoProtos.ProtoModem modem = protoDeviceInfo.getModem(); + values.addProperty("modem_types", modem.getType().toString()); + values.addProperty("sc_EARNFCN_offset", modem.getParameters(0)); + values.addProperty("sc_EARFCN", modem.getParameters(1)); + values.addProperty("sc_PCI", modem.getParameters(2)); + values.addProperty("sc_Cell_id", modem.getParameters(3)); + values.addProperty("sc_RSRP", modem.getParameters(4)); + values.addProperty("sc_RSRQ", modem.getParameters(5)); + values.addProperty("sc_RSSI", modem.getParameters(6)); + values.addProperty("sc_SINR", modem.getParameters(7)); + values.addProperty("sc_Band", modem.getParameters(8)); + values.addProperty("sc_TAC", modem.getParameters(9)); + values.addProperty("sc_ECL", modem.getParameters(10)); + values.addProperty("sc_TX_PWR", modem.getParameters(11)); + values.addProperty("op_mode", modem.getParameters(12)); + values.addProperty("nc_EARFCN", modem.getParameters(13)); + values.addProperty("nc_EARNFCN_offset", modem.getParameters(14)); + values.addProperty("nc_PCI", modem.getParameters(15)); + values.addProperty("nc_RSRP", modem.getParameters(16)); + values.addProperty("RLC_UL_BLER", modem.getParameters(17)); + values.addProperty("RLC_DL_BLER", modem.getParameters(18)); + values.addProperty("MAC_UL_BLER", modem.getParameters(19)); + values.addProperty("MAC_DL_BLER", modem.getParameters(20)); + values.addProperty("MAC_UL_TOTAL_BYTES", modem.getParameters(21)); + values.addProperty("MAC_DL_TOTAL_BYTES", modem.getParameters(22)); + values.addProperty("MAC_UL_total_HARQ_Tx", modem.getParameters(23)); + values.addProperty("MAC_DL_total_HARQ_Tx", modem.getParameters(24)); + values.addProperty("MAC_UL_HARQ_re_Tx", modem.getParameters(25)); + values.addProperty("MAC_DL_HARQ_re_Tx", modem.getParameters(26)); + values.addProperty("RLC_UL_tput", modem.getParameters(27)); + values.addProperty("RLC_DL_tput", modem.getParameters(28)); + values.addProperty("MAC_UL_tput", modem.getParameters(29)); + values.addProperty("MAC_DL_tput", modem.getParameters(30)); + values.addProperty("sleep_duration", modem.getParameters(31)); + values.addProperty("rx_time", modem.getParameters(32)); + values.addProperty("tx_time", modem.getParameters(33)); + + //Runtime info + DeviceInfoProtos.ProtoRuntime runtimeInfo = protoDeviceInfo.getRuntimeInfo(); + values.addProperty("battery_reset_timestamp", getDate(runtimeInfo.getBatteryResetTimestamp())); + values.addProperty("max_mcu_temp", runtimeInfo.getMaxMcuTemperature()); + values.addProperty("mcu_temp", runtimeInfo.getMcuTemperature()); + values.addProperty("counter_of_confirmable_messages_attempts", runtimeInfo.getMessageCounters(0)); + values.addProperty("counter_of_non_confirmable_messages_attempts", runtimeInfo.getMessageCounters(1)); + values.addProperty("counter_of_succeeded_messages", runtimeInfo.getMessageCounters(2)); + values.addProperty("min_battery_mcu_temp", runtimeInfo.getMinBatteryMcuTemperature()); + values.addProperty("min_battery_voltage", runtimeInfo.getMinBatteryVoltage()); + values.addProperty("min_mcu_temp", runtimeInfo.getMinMcuTemperature()); + values.addProperty("runtime_errors", runtimeInfo.getRuntimeErrorsCount()); + values.addProperty("up_time", runtimeInfo.getUpTime()); + + return new EfentoTelemetry(System.currentTimeMillis(), values); + } + + private JsonElement getEfentoConfiguration(byte[] bytes) throws InvalidProtocolBufferException { + return parseString(ProtoConverter.dynamicMsgToJson(bytes, ConfigProtos.getDescriptor().getMessageTypes().get(2))); + } + + private static String getDate(long seconds) { + if (seconds == -1L || seconds == 4294967295L) { + return "Undefined"; + } + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd MMM yyyy HH:mm:ss Z"); + return simpleDateFormat.format(new Date(TimeUnit.SECONDS.toMillis(seconds))); + } + @Data @AllArgsConstructor - public static class EfentoMeasurements { + public static class EfentoTelemetry { private long ts; - private JsonObject values; + private JsonElement values; } } diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/adaptor/EfentoCoapAdaptor.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/adaptor/EfentoCoapAdaptor.java index a6b588139a..fa6394d41b 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/adaptor/EfentoCoapAdaptor.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/adaptor/EfentoCoapAdaptor.java @@ -16,10 +16,11 @@ package org.thingsboard.server.transport.coap.efento.adaptor; import com.google.gson.Gson; +import com.google.gson.JsonElement; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.common.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.coap.efento.CoapEfentoTransportResource; @@ -32,12 +33,23 @@ public class EfentoCoapAdaptor { private static final Gson gson = new Gson(); - public TransportProtos.PostTelemetryMsg convertToPostTelemetry(UUID sessionId, List measurements) throws AdaptorException { + public TransportProtos.PostTelemetryMsg convertToPostTelemetry(UUID sessionId, List telemetryList) throws AdaptorException { try { - return JsonConverter.convertToTelemetryProto(gson.toJsonTree(measurements)); + return JsonConverter.convertToTelemetryProto(gson.toJsonTree(telemetryList)); } catch (Exception ex) { log.warn("[{}] Failed to convert EfentoMeasurements to PostTelemetry request!", sessionId); throw new AdaptorException(ex); } } + + public TransportProtos.PostAttributeMsg convertToPostAttributes(UUID sessionId, JsonElement deviceInfo) throws AdaptorException { + try { + return JsonConverter.convertToAttributesProto(deviceInfo); + } catch (Exception ex) { + log.warn("[{}] Failed to convert JsonObject to PostTelemetry request!", sessionId); + throw new AdaptorException(ex); + } + } + + } diff --git a/common/transport/coap/src/main/proto/efento/proto_config.proto b/common/transport/coap/src/main/proto/efento/proto_config.proto new file mode 100644 index 0000000000..0903494245 --- /dev/null +++ b/common/transport/coap/src/main/proto/efento/proto_config.proto @@ -0,0 +1,312 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +syntax = "proto3"; + +import "efento/proto_measurement_types.proto"; +import "efento/proto_rule.proto"; + +option java_package = "org.thingsboard.server.gen.transport.coap"; +option java_outer_classname = "ConfigProtos"; + +/* Message containing optional channels control parameters */ +message ProtoChannelControl { + + /* Channel index */ + uint32 channel_index = 1; + + /* Control parameters. Maximal number equals 4. This field is channel specific: */ + /* IO_control channel: */ + /* - control_params[0]: */ + /* - Byte 0: On state configuration */ + /* 0x01 - Low */ + /* 0x02 - High */ + /* 0x03 - High-Z (disconnected) */ + /* - Byte 1: Off state configuration */ + /* 0x01 - Low */ + /* 0x02 - High */ + /* 0x03 - High-Z (disconnected) */ + /* - Byte 2: Power on channel state */ + /* 0x01 - On */ + /* 0x02 - Off */ + repeated uint32 control_params = 2; +} + +/* Message containing request data for accesing calibration parameters */ +message ProtoCalibrationParameters { + + /* Request details. Bitmask: */ + /* - calibration_request[0:2] - requested channel number. */ + uint32 calibration_request = 1; + + /* Assignment of a channel. */ + uint32 channel_assignment = 2; + + /* Table of calibration parameters. Max size = 8. */ + repeated int32 parameters = 3; +} + +/* Main message sent in the payload. Each field in this message is independent of the others - only parameters that should be */ +/* changed need to be sent in the payload. */ +/* If the value of a selected parameter shall not be changed, do not include it in the payload */ +message ProtoConfig { + + /* DEPRECATED - Used for backward compatibility with fw versions 5.x */ + /* repeated Threshold thresholds = 1; */ + + /* 'Measurement_period_base' and 'measurement_period_factor' define how often the measurements are taken. */ + /* Sensors of 'Continuous' type take measurement each Measurement_period_base * measurement_period_factor. */ + /* Sensors of 'Binary' type take measurement each Measurement_period_base. */ + /* For backward compatibility with versions 5.x in case of binary/mixed sensors, if the 'measurement_period_factor' is */ + /* not sent (equal to 0), then the default value '14' shall be used for period calculation. */ + /* For backward compatibility with versions 5.x in case of continues sensors, if the measurement_period_factor is */ + /* not sent (equal to 0), then the default value '1' shall be used for period calculation. */ + /* measurement period base in seconds */ + /* Range [1:65535] - minimum value can vary depends on installed sensors */ + uint32 measurement_period_base = 2; + + /* Measurement period factor */ + /* Range [1:65535] - minimum value can vary depends on installed sensors */ + uint32 measurement_period_factor = 26; + + /* Transmission interval in seconds. Range: [60:604800] */ + uint32 transmission_interval = 3; + + /* BLE turnoff time in seconds. Once receiving this setting, BLE will be switched off after the set number of seconds. */ + /* If BLE is already switched off, it will switch on for the set number of seconds and switch off afterwards. */ + /* Range [60:604800] and 0xFFFFFFFF */ + /* 0xFFFFFFFF - always on */ + uint32 ble_turnoff_time = 4; + + /* ACK interval in seconds */ + /* Range [180:2592000] and 0xFFFFFFFF */ + /* 0xFFFFFFFF - always request ACK */ + uint32 ack_interval = 5; + + /* Specifies, if the additional device info is requested. If true, sensor will send a message to endpoint '/i' with the */ + /* device info. This field is only sent by server */ + bool request_device_info = 6; + + /* Specifies, if software update is available. This field is only sent by server */ + bool request_fw_update = 7; + + /* Current time in seconds sine 1st of January 1970 (epoch time). */ + uint32 current_time = 8; + + /* NB-IoT transfer limit */ + /* Range: [1:65535] */ + /* 65535 - disable transfer limit function */ + uint32 transfer_limit = 9; + + /* NB-IoT transfer limit timer in seconds */ + /* Range: [1:65535] */ + /* 65535 - disable transfer limit function */ + uint32 transfer_limit_timer = 10; + + /* Data (measurements) server IP address */ + /* IP of the data server as string in form x.x.x.x. For example: 18.184.24.239 */ + string data_server_ip = 11; + + /* Data (measurements) server port */ + /* Range: [1:65535] */ + uint32 data_server_port = 12; + + /* Update server IP address */ + /* IP of data server as string in form x.x.x.x. For example: 18.184.24.239 */ + string update_server_ip = 13; + + /* Update server port for UDP transmission */ + /* Range: [1:65535] */ + uint32 update_server_port_udp = 14; + + /* Update server port for CoAP transmission */ + /* Range: [1:65535] */ + uint32 update_server_port_coap = 15; + + /* APN as string. Max length 49 */ + /* String with special character 0x7F (DEL) only indicates that automatic apn is turn on */ + string apn = 16; + + /* PLMN selection */ + /* Range: [100:999999] */ + /* 0xFFFFFFFF or 1000000 - automatic selection */ + uint32 plmn_selection = 17; + + /* Device will power off its cellular modem for requested number of seconds. Maximum number of seconds 604800 (7 days) */ + /* This field is only sent by server */ + uint32 disable_modem_request = 18; + + /* If set, the device will send its configuration to the endpoint '/c' as a confirmable message */ + /* This field is only sent by server */ + bool request_configuration = 19; + + /* Device's error codes. */ + /* This field is only sent by device */ + repeated uint32 errors = 20; + + /* Identifier of current configuration - Every change of the configuration results in change of the value of this field */ + /* This field is only sent by device */ + uint32 hash = 21; + + /* If true, the device will accept the configuration without functional testing (eg. network connection) */ + bool accept_without_testing = 22; + + /* Cloud token configuration: */ + /* - 1: cloud token set to the value of cloud_token field */ + /* - 2: cloud token set to IMEI of the cellular module */ + /* - 255: do not send cloud_token field */ + uint32 cloud_token_config = 23; + + /* Cloud token that should be sent with each measurement frame */ + string cloud_token = 24; + + /* Serial number of the device */ + /* This field is only sent by device */ + bytes serial_number = 25; + + /* Type of channel */ + /* This field is only sent by device */ + repeated MeasurementType channel_types = 27; + + /* Edge logic rules set on the device. Up to 12 rules are supported */ + repeated ProtoRule rules = 28; + + /* Supervision period */ + /* Range: [180:604800] */ + /* 0xFFFFFFFF - Functionality disabled */ + uint32 supervision_period = 29; + + /* If true, sensor's measurement memory will be erased */ + bool memory_reset_request = 30; + + /* Bytes 0-4 - Band selection mask. Mask = 1 << position */ + /* Band | 1 | 2 | 3 | 4 | 5 | 8 | 12 | 13 | 17 | 18 | 19 | 20 | 25 | 26 | 28 | 66 | 71 | 85 | */ + /* Position: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | */ + /* example: To enable band 3, 8 and 20 set to (1 << 2) + (1 << 5) + (1 << 11) = 2084 */ + uint32 modem_bands_mask = 31; + + /* Data endpoint (string - max length 16) */ + string data_endpoint = 32; + + /* Configuration endpoint (string - max length 16) */ + string configuration_endpoint = 33; + + /* Device info endpoint (string - max length 16) */ + string device_info_endpoint = 34; + + /* Time endpoint (string - max length 16) */ + string time_endpoint = 35; + + /* Bluetooth TX power level. Value is the index of the absolute value of TX power, that depends on the BLE module */ + /* Range: [1:4] */ + uint32 ble_tx_power_level = 36; + + /* Deprecated field */ + /* If true, the sensor's runtime errors will be cleared */ + bool request_runtime_errors_clear = 37; + + /* Timestamp when a new error code was reported */ + uint32 error_timestamp = 38; + + /* Timestamp when the new configuration was set */ + uint32 hash_timestamp = 39; + + /* Cloud token CoAP option ID: */ + /* - [1:64999] - CoAP option ID containing cloud token */ + /* - 65000 - cloud token sent in the payload */ + uint32 cloud_token_coap_option = 40; + + /* ECDSA payload signature CoAP option ID: */ + /* - [1:64999] - CoAP option ID containing ECDSA payload signature */ + /* - 65000 - no payload signature in CoAP option */ + uint32 payload_signature_coap_option = 41; + + /* DNS server IP address grouped in the array as four octets. Set 255.255.255.255 to use a network DNS server */ + /* Note: when setting less than four octets the remaining will be filled with zeros. */ + repeated uint32 dns_server_ip = 42; + + /* DNS TTL configuration: */ + /* - [1:864000] - custom TTL in seconds (additionally, the DNS request when communication has failed) */ + /* - 864001 - accept TTL from the DNS server (additionally, the DNS request when communication has failed) */ + /* - 864002 - DNS request is only after communication failed */ + uint32 dns_ttl_config = 43; + + /* Configuration payload split information. Information about dividing the payload into parts */ + /* values < 0 - payload has been split, expect another part of the payload in the next message. */ + /* The absolute value indicates an index of the current message. */ + /* value = 0 - payload has not been splitted */ + /* values > 0 - last part of the split payload, the value indicates the total number of the messages sent */ + sint32 payload_split_info = 44; + + /* Modem update request (string - max length 48) */ + /* This field is only sent by server */ + /* For BC66 module, this field is a DFOTA URL */ + string modem_update_request = 45; + + /* Cellular configuration parameters. */ + /* 1st item - Number of used cellular parameters */ + /* 2nd - 12th items - Cellular parameters */ + repeated uint32 cellular_config_params = 46; + + /* Calendar configuration. Up to 6 calendars are supported */ + repeated ProtoCalendar calendars = 47; + + /* Control parameters for channels. Maximal number of requests equals 6 */ + /* This field is only sent by server */ + repeated ProtoChannelControl channels_control_request = 48; + + /* Set/get calibration parameters for single channel. */ + ProtoCalibrationParameters calibration_parameters_request = 49; + + /* LED behaviour configuration: */ + /* Period of LEDs flashing (5-600 seconds in 5 seconds resolution): */ + /* - led_config[0] - green LED */ + /* - led_config[1] - red LED */ + /* Time from entering the normal state, after which the LED indication is turned off */ + /* (0-240 minutes in 1 minute resolution, or 255 for always turned on): */ + /* - led_config[2] - flashing red led on communication problem */ + /* - led_config[3] - flashing red led on a sensor problem */ + /* - led_config[4] - flashing red led on a low power */ + /* - led_config[5] - flashing green led on measurement */ + /* - led_config[6] - flashing green led on transmission */ + /* - led_config[7] - flashing green led to indicate sensor's proper operation */ + /* - led_config[8] - Blink duration (20-1000ms in 5 ms resolution) */ + repeated uint32 led_config = 50; + + /* Network troubleshooting configuration, if bluetooth is turned off and communication with the server is faulty, */ + /* bluetooth will be automatically turned on until the connection is stabilized */ + /* - 1: network troubleshooting disabled */ + /* - 2: network troubleshooting enabled */ + uint32 network_troubleshooting = 51; + + /* Reserved by gateway client */ + reserved 52, 53; + + /* Encryption key configuration. Sensor sends in this field two last bytes of SHA256 hash calculated from its current */ + /* encryption_key configuration. */ + /* Max length: 16 bytes. */ + /* 0x7F - encryption key disabled. */ + bytes encryption_key = 54; + + /* User name as string. Max length 31 */ + /* String with special character 0x7F (DEL) only indicates that automatic user name is turn on */ + /* User name can only be set to custom value if apn has been configured (is not automatic) */ + string apn_user_name = 55; + + /* Password as string. Max length 31 */ + /* String with special character 0x7F (DEL) only indicates that automatic password is turn on */ + /* Password can only be set to custom value if apn_user_name has been configured (is not automatic) */ + string apn_password = 56; +} \ No newline at end of file diff --git a/common/transport/coap/src/main/proto/efento/proto_device_info.proto b/common/transport/coap/src/main/proto/efento/proto_device_info.proto new file mode 100644 index 0000000000..27dc73c25b --- /dev/null +++ b/common/transport/coap/src/main/proto/efento/proto_device_info.proto @@ -0,0 +1,197 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +syntax = "proto3"; + +option java_package = "org.thingsboard.server.gen.transport.coap"; +option java_outer_classname = "DeviceInfoProtos"; + +enum ModemType +{ + + /* Invalid value */ + MODEM_TYPE_UNSPECIFIED = 0; + + /* Quectel BC66 modem */ + MODEM_TYPE_BC66 = 1; + + /* Quectel BC66-NA modem */ + MODEM_TYPE_BC66NA = 2; +} + +message ProtoRuntime +{ + + /* Up-time in seconds (since reset) */ + uint32 up_time = 1; + + /* Message counters (since reset). There are 3 counters: */ + /* message_counters[0] - Counter of confirmable messages attempts */ + /* message_counters[1] - Counter of non-confirmable messages attempts */ + /* message_counters[2] - Counter of succeeded messages */ + repeated uint32 message_counters = 2; + + /* MCU temperature in Celsius */ + sint32 mcu_temperature = 3; + + /* Minimum battery voltage in mV */ + uint32 min_battery_voltage = 4; + + /* MCU temperature in Celsius, while the minimum battery voltage was reached */ + sint32 min_battery_mcu_temperature = 5; + + /* Battery reset timestamp (Unix timestamp) */ + uint32 battery_reset_timestamp = 6; + + /* Max MCU temperature in Celsius */ + sint32 max_mcu_temperature = 7; + + /* Min MCU temperature in Celsius */ + sint32 min_mcu_temperature = 8; + + /* Table of runtime errors. Max length: 20 */ + repeated uint32 runtime_errors = 9; +} + +message ProtoModem +{ + + ModemType type = 1; + + /* Parameters for BC66 modem: */ + /* parameters[0] - sc_EARFCN - Range: [0:262143]. Unknown value: -1 */ + /* parameters[1] - sc_EARNFCN_offset - Range: [0:4] mapped to [-2, -1, -0.5, 0, 1]. Unknown value: -1 */ + /* parameters[2] - sc_PCI - Range: [0:502]. Unknown value: -1 */ + /* parameters[3] - sc_Cell id - Range: [1:268435456]. Unknown value: 0 */ + /* parameters[4] - sc_RSRP - [dBm] - Range: [-140:-44]. Unknown value: 0 */ + /* parameters[5] - sc_RSRQ - [dB] - Range: [-20:-3]. Unknown value: 0 */ + /* parameters[6] - sc_RSSI - [dBm] - Range: [-110:-3] Unknown value: 0 */ + /* parameters[7] - sc_SINR - [dB] - Range: [-10:30]. Unknown value: 31 */ + /* parameters[8] - sc_Band - Range: [see module supported bands]. The current serving cell band. Unknown value: -1 */ + /* parameters[9] - sc_TAC - Range: [0:65536]. Unknown value: -1 */ + /* parameters[10] - sc_ECL - Range: [0:2]. Unknown value: -1 */ + /* parameters[11] - sc_TX_PWR - [0.1cBm] - Range [-440:230]. Unknown value: -1000 */ + /* parameters[12] - OP_MODE - Range: [0:3]. Unknown value: -1 */ + /* parameters[13] - nc_EARFCN - Range: [0:262143]. Unknown value: -1 */ + /* parameters[14] - nc_EARNFCN_offset - Range: [0:4] mapped to [-2, -1, -0.5, 0, 1]. Unknown value: -1 */ + /* parameters[15] - nc_PCI - Range: [0:502]. Unknown value: -1 */ + /* parameters[16] - nc_RSRP - [dBm] - Range: [-140:-44]. Unknown value: 0 */ + /* parameters[17] - RLC_UL_BLER - Range: [0:100]. Unknown value: -1 */ + /* parameters[18] - RLC_DL_BLER - Range: [0:100]. Unknown value: -1 */ + /* parameters[19] - MAC_UL_BLER - Range: [0:100]. Unknown value: -1 */ + /* parameters[20] - MAC_DL_BLER - Range: [0:100]. Unknown value: -1 */ + /* parameters[21] - MAC_UL_TOTAL_BYTES - Range: [0:2147483647]. Unknown value: -1 */ + /* parameters[22] - MAC_DL_TOTAL_BYTES - Range: [0:2147483647]. Unknown value: -1 */ + /* parameters[23] - MAC_UL_total_HARQ_Tx - Range: [0:2147483647]. Unknown value: -1 */ + /* parameters[24] - MAC_DL_total_HARQ_Tx - Range: [0:2147483647]. Unknown value: -1 */ + /* parameters[25] - MAC_UL_HARQ_re_Tx - Range: [0:2147483647]. Unknown value: -1 */ + /* parameters[26] - MAC_DL_HARQ_re_Tx - Range: [0:2147483647]. Unknown value: -1 */ + /* parameters[27] - RLC_UL_tput - Range: [0:2147483647]. Unknown value: -1 */ + /* parameters[28] - RLC_DL_tput - Range: [0:2147483647]. Unknown value: -1 */ + /* parameters[29] - MAC_UL_tput - Range: [0:2147483647]. Unknown value: -1 */ + /* parameters[30] - MAC_DL_tput - Range: [0:2147483647]. Unknown value: -1 */ + /* parameters[31] - sleep_duration - [0.1s] - Range: [0:2147483647]. Unknown value: -1 */ + /* parameters[32] - Rx_time - [0.1s] - Range: [0:2147483647]. Unknown value: -1 */ + /* parameters[33] - Tx_time - [0.1s] - Range: [0:2147483647]. Unknown value: -1 */ + repeated sint32 parameters = 2; +} + +message ProtoUpdateInfo +{ + + /* Timestamp of update (Unix timestamp) */ + uint32 timestamp = 1; + + /* Update status, possible values: */ + /* - 0x1 - No update yet */ + /* - 0x2 - No error */ + /* - 0x3 - UDP socekt error */ + /* - 0x4 - Hash error */ + /* - 0x5 - Missing packet error */ + /* - 0x6 - Invalid data error */ + /* - 0x7 - Sending timeout error */ + /* - 0x8 - No SW to update error */ + /* - 0x9 - Sending unexpected error */ + /* - 0x10 - Unexpected error */ + uint32 status = 2; +} + +message ProtoDeviceInfo +{ + + /* Serial number of device */ + bytes serial_num = 1; + + /* Deprecated field */ + reserved 2; + + /* Software version e.g ver 06.10 -> 0x060A -> 1546 */ + uint32 sw_version = 3; + + /* Deprecated fields */ + reserved 4,5,6,7,8,9,10,11,12; + + /* Structure with battery and temperature information */ + ProtoRuntime runtime_info = 13; + + /* Structure with modem specific runtime information */ + ProtoModem modem = 14; + + /* String up to 7 bytes long. Software commit id e.g. "e0e8556" */ + /* From version 06.07 the first two characters indicate the LTS version. */ + /* For example: the value "0bdd23f" means LTS version 11 and the beginning of the commit ID "dd23f" */ + string commit_id = 15; + + /* Optional string up to 36 bytes long. Can be set to any user define value or hold device's IMEI */ + string cloud_token = 16; + + /* Memory statistics: */ + /* memory_statistics[0] - Status of Nv storage: */ + /* - 0 - Nv storage hasn't errors */ + /* - 1 - Nv storage has some corrupted packet. Memory is read-only */ + /* - 2 - Nv storage is corrupted. Memory is unavailable */ + /* memory_statistics[1] - Timestamp of the end of collecting statistics. */ + /* Value in seconds since UNIX EPOCH 01-01-1970. Undefined value: 4294967295 */ + /* memory_statistics[2] - Capacity of memory in bytes */ + /* memory_statistics[3] - Used space in bytes */ + /* memory_statistics[4] - Size of invalid (outdated) packets in bytes */ + /* memory_statistics[5] - Size of corrupted packets in bytes */ + /* memory_statistics[6] - Number of valid packets */ + /* memory_statistics[7] - Number of invalid (outdated) packets */ + /* memory_statistics[8] - Number of corrupted packets */ + /* memory_statistics[9] - Number of all samples for channel 1 (valid packets) */ + /* memory_statistics[10] - Number of all samples for channel 2 (valid packets) */ + /* memory_statistics[11] - Number of all samples for channel 3 (valid packets) */ + /* memory_statistics[12] - Number of all samples for channel 4 (valid packets) */ + /* memory_statistics[13] - Number of all samples for channel 5 (valid packets) */ + /* memory_statistics[14] - Number of all samples for channel 6 (valid packets) */ + /* memory_statistics[15] - Timestamp of the first binary measurement. */ + /* Value in seconds since UNIX EPOCH 01-01-1970. Undefined value: 4294967295 */ + /* memory_statistics[16] - Timestamp of the last binary measurement. */ + /* Value in seconds since UNIX EPOCH 01-01-1970. Undefined value: 4294967295 */ + /* memory_statistics[17] - Timestamp of the last binary measurement, that marked as sent. */ + /* Value in seconds since UNIX EPOCH 01-01-1970. Undefined value: 4294967295 */ + /* memory_statistics[18] - Timestamp of the first continuous measurement. */ + /* Value in seconds since UNIX EPOCH 01-01-1970. Undefined value: 4294967295 */ + /* memory_statistics[19] - Timestamp of the last continuous measurement. */ + /* Value in seconds since UNIX EPOCH 01-01-1970. Undefined value: 4294967295 */ + /* memory_statistics[20] - Timestamp of the last continuous measurement, that marked as sent. */ + /* Value in seconds since UNIX EPOCH 01-01-1970. Undefined value: 4294967295 */ + /* memory_statistics[21] - NVM write counter */ + repeated uint32 memory_statistics = 17; + + /* Information about last sensor SW update */ + ProtoUpdateInfo last_update_info = 18; +} \ No newline at end of file diff --git a/common/transport/coap/src/main/proto/proto_measurement_types.proto b/common/transport/coap/src/main/proto/efento/proto_measurement_types.proto similarity index 100% rename from common/transport/coap/src/main/proto/proto_measurement_types.proto rename to common/transport/coap/src/main/proto/efento/proto_measurement_types.proto diff --git a/common/transport/coap/src/main/proto/efento/proto_measurements.proto b/common/transport/coap/src/main/proto/efento/proto_measurements.proto new file mode 100644 index 0000000000..fd93b38b82 --- /dev/null +++ b/common/transport/coap/src/main/proto/efento/proto_measurements.proto @@ -0,0 +1,121 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +syntax = "proto3"; +import "efento/proto_measurement_types.proto"; + +option java_package = "org.thingsboard.server.gen.transport.coap"; +option java_outer_classname = "MeasurementsProtos"; + +message ProtoChannel{ + /* Type of channel */ + MeasurementType type = 1; + + /* Timestamp of the first sample (the oldest one) in seconds since UNIX EPOCH 01-01-1970 */ + int32 timestamp = 2; + + /* Only used for 'Continuous' sensor types. Value used as the starting point for calculating the values of all */ + /* measurements in the package. */ + /* Format defined by 'MeasurementType' field */ + sint32 start_point = 4; + + /* 'Continuous' sensor types */ + /* Value of the offset from the 'start_point' for each measurement in the package. The oldest sample first ([0]). */ + /* 'sample_offsets' format defined by 'MeasurementType' field. */ + /* Example: MeasurementType = 1 (temperature), start_point = 100, sample_offsets[0] = 15, sample_offsets[1] = 20 */ + /* 1st sample in the package temperature value = 11.5 °C, 2nd sample in the package temperature value = 12 °C */ + /* Calculating timestamps of the measurements: timestamp = 1606391700, measurement_period_base = 60, */ + /* measurement_period_factor = 1. Timestamp of the 1st sample = 1606391700, timestamp of the 2nd sample = 1606391760 */ + /* 'Binary' sensor types: */ + /* Absolute value of the 'sample_offsets' field indicates the offset in seconds from 'timestamp' field. */ + /* Sign (- or +) indicates the state of measurements depend of sensor type. */ + /* Value of this field equals to '1' or '-1' indicates the state at the 'timestamp'. Other values */ + /* indicate the state of the relay at the time (in seconds) equal to 'timestamp' + value. */ + /* Values of this field are incremented starting from 1 (1->0: state at the time */ + /* of 'timestamp', 2->1: state at the time equal to 'timestamp' + 1 s, 3->2 : */ + /* state at the time equal to 'timestamp' + 2 s, etc.). The first and the last sample define the time range of the */ + /* measurements. Only state changes in the time range are included in the 'sample_offsets' field */ + /* Examples: if 'timestamp' value is 1553518060 and 'sample_offsets' equals '1', it means that at 1553518060 the state */ + /* was high, if 'timestamp' value is 1553518060 and 'sample_offsets' equals '-9', it means at 1553518068 the state was low */ + repeated sint32 sample_offsets = 5 [packed=true]; + + /* Deprecated - configuration is sent to endpoint 'c' */ + //int32 lo_threshold = 6; + + /* Deprecated - configuration is sent to endpoint 'c' */ + //int32 hi_threshold = 7; + + /* Deprecated - configurationis sent to endpoint 'c' */ + //int32 diff_threshold = 8; + } + +message ProtoMeasurements { + + /* serial number of the device */ + bytes serial_num = 1; + + /* true - battery ok, false - battery low */ + bool battery_status = 2; + + /* 'Measurement_period_base' and 'measurement_period_factor' define how often the measurements are taken. */ + /* Sensors of 'Continuous' type take measurement each Measurement_period_base * measurement_period_factor. */ + /* Sensors of 'Binary' type take measurement each Measurement_period_base. */ + /* For backward compatibility with versions 5.x in case of binary/mixed sensors, if the 'measurement_period_factor' is */ + /* not sent (equal to 0), then the default value '14' shall be used for period calculation. */ + /* For backward compatibility with versions 5.x in case of continues sensors, if the measurement_period_factor is */ + /* not sent (equal to 0), then the default value '1' shall be used for period calculation. */ + /* measurement period base in seconds */ + uint32 measurement_period_base = 3; + + /* Measurement period factor */ + uint32 measurement_period_factor = 8; + + repeated ProtoChannel channels = 4; + + /* Timestamp of the next scheduled transmission. If the device will not send data until this time, */ + /* it should be considered as 'lost' */ + uint32 next_transmission_at = 5; + + /* reason of transmission - unsigned integer where each bit indicates different */ + /* possible communication reason. Can be more than one */ + /* - bit 0: first message after sensor reset */ + /* - bit 1: user button triggered */ + /* - bit 2: user BLE triggered */ + /* - bit 3-7: number of retries -> incremented after each unsuccessful transmission. Max value 4. */ + /* Set to 0 after a successful transmission. */ + /* - bit 8: channel 1 lower threshold exceeded */ + /* - bit 9: channel 1 lower threshold returned */ + /* - bit 10: channel 1 higher threshold exceeded */ + /* - bit 11: channel 1 higher threshold returned */ + /* - bit 12: channel 1 differential threshold crossed */ + /* - bits 13-17: channel 2 thresholds (same as for channel 1) */ + /* - bits 18-22: channel 3 thresholds (same as for channel 1) */ + /* - bits 23-27: channel 4 or 5 or 6 thresholds (same as for channel 1) */ + uint32 transfer_reason = 6; + + /* Signal strength level mapped from RSSI */ + /* - 0 : 113 dBm or less */ + /* - 1 : 111 dBm */ + /* - 2...30 : 109...-53 dBm */ + /* - 31 : -51 dBm or greater */ + /* - 99 : Not known or not detectable */ + uint32 signal = 7; + + /* Hash of the current configuration. Hash value changes each time a device receives a new configuration */ + uint32 hash = 9; + + /* Optional string up to 36 bytes long. Can be set to any user define value or hold device's IMEI */ + string cloud_token = 16; +} \ No newline at end of file diff --git a/common/transport/coap/src/main/proto/efento/proto_rule.proto b/common/transport/coap/src/main/proto/efento/proto_rule.proto new file mode 100644 index 0000000000..9a4a3cd0d1 --- /dev/null +++ b/common/transport/coap/src/main/proto/efento/proto_rule.proto @@ -0,0 +1,267 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +syntax = "proto3"; + +option java_package = "org.thingsboard.server.gen.transport.coap"; +option java_outer_classname = "ProtoRuleProtos"; + +/* Encoding A: used to set absolute values in the Rules (e.g. upper and lower threshold values) */ +/* - TEMPERATURE - [°C] - Celsius degree. Resolution 0.1°C. Range [-273.2:4000.0]. */ +/* - HUMIDITY - [% RH] - Relative humidity. Resolution 1%. Range [0:100]. */ +/* - ATMOSPHERIC_PRESSURE - [hPa] - Hectopascal (1hPa = 100Pa). Resolution 0.1hPa. Range: [1.0:2000.0]. */ +/* - DIFERENTIAL_PRESSURE - [Pa] - Pascal. Resolution 1Pa. Range [-10000:10000] */ +/* - OK/ALARM - Not applicable */ +/* - IAQ - [IAQ] - IAQ index. Resolution 1IAQ. Range [0:500]. */ +/* - FLOODING - Not applicable */ +/* - PULSE_CNT - [NB] Number of pulses. Resolution 1 pulse. Range [0:8000000]. */ +/* - ELECTRICITY_METER - [W] - Watt; Resolution 1W. Range [0:8000000]. Average power consumption in period */ +/* - WATER_METER [l/min] - Liter per minute. Resolution 1l/min. Range [0:8000000]. Average water flow in period. */ +/* - SOIL_MOISTURE - [kPa] - Kilopascal (1kPa = 1000Pa); Resolution 1kPa. Range [-1000:0]. Soil moisture (tension). */ +/* - CO_GAS - [ppm] - Parts per million. Resolution 1ppm. Range [0:1000000]. Carbon monoxide concentration. */ +/* - NO2_GAS - [ppm] - Parts per million. Resolution 1ppm. Range [0:1000000]. Nitrogen dioxide concentration. */ +/* - H2S_GAS - [ppm] - Parts per million. Resolution 0.01ppm. Range [0.00:80000.00]. Hydrogen sulfide concentration. */ +/* - AMBIENT_LIGHT -[lx] - Lux. Resolution 0.1lx. Range [0.0:100000.0]. Illuminance. */ +/* - PM_1_0 - [µg/m^3] - Micro gram per cubic meter. Resolution 1µg/m^3 Range [0:1000]. */ +/* - PM_2_5 - [µg/m^3] - Micro gram per cubic meter. Resolution 1µg/m^3 Range [0:1000]. */ +/* - PM_10_0 - [µg/m^3] - Micro gram per cubic meter. Resolution 1µg/m^3 Range [0:1000]. */ +/* - NOISE_LEVEL - [dB] - Decibels. Resolution 0.1 dB. Range: [0.0:200.0]. Noise level. */ +/* - NH3_GAS - [ppm] - Parts per million. Resolution 1ppm. Range [0:1000000]. Ammonia concentration. */ +/* - CH4_GAS - [ppm] - Parts per million. Resolution 1ppm. Range [0:1000000]. Methane concentration. */ +/* - HIGH_PRESSURE - [kPa] - Kilopascal (1kPa = 1000Pa, 100kPa = 1bar). Resolution 1kPa. Range [0:200000]. Pressure. */ +/* - DISTANCE_MM - [mm] - Millimeter. Resolution 1mm. Range [0:100000]. Distance. */ +/* - WATER_METER_ACC_MINOR - [l] - Liter. Resolution 1l. Range [0:1000000]. Accumulative water meter (minor). */ +/* - WATER_METER_ACC_MAJOR - [hl] - Hectoliter. Resolution 1hl. Range [0:1000000]. Accumulative water meter (major). */ +/* - CO2_GAS - [ppm] - Parts per million. Resolution 1ppm. Range [0:1000000]. Carbon dioxide concentration. */ +/* - HUMIDITY ACCURATE - [% RH] - Relative humidity. Resolution 0.1%. Range [0.0:100.0]. */ +/* - STATIC_IAQ - [sIAQ] - Static IAQ index. Resolution 1sIAQ. Range [0:10000]. */ +/* - CO2_EQUIVALENT - [ppm] - Parts per million. Resolution 1ppm. Range [0:1000000]. Carbon dioxide equivalent. */ +/* - BREATH_VOC - [ppm] - Parts per million. Resolution 1ppm. Range [0:100000]. Breath VOC estimate. */ +/* - PERCENTAGE - [%] - Percentage. Resolution 0.01%. Range [0.00:100.00]. */ +/* - PULSE_CNT_ACC_MINOR - [NB] - Number of pulses. Resolution 1 pulse. Range [0:1000000]. Accumulative pulse counter (minor). */ +/* - PULSE_CNT_ACC_MAJOR - [kNB] - Number of kilopulses. Resolution 1 kilopulse. Range [0:1000000]. */ +/* Accumulative pulse counter (major). */ +/* - ELEC_METER_ACC_MINOR - [Wh] - Watt-hour. Resolution 1Wh. Range [0:1000000]. Accumulative electricity meter (minor). */ +/* - ELEC_METER_ACC_MAJOR - [kWh] - Kilowatt-hour. Resolution 1kWh. Range [0:1000000]. Accumulative electricity meter (major). */ + +/* Encoding R: used to set relative values in the Rules (e.g. differential threshold and hysteresis) */ +/* - TEMPERATURE - [°C] - Celsius degree. Resolution 0.1°C. Range [0.1:4273.2]. */ +/* - HUMIDITY - [% RH] - Relative humidity. Resolution 1%. Range [1:100]. */ +/* - ATMOSPHERIC_PRESSURE - [hPa] - Hectopascal (1hPa = 100Pa). Resolution 0.1hPa. Range: [0.1:1999.0]. */ +/* - DIFERENTIAL_PRESSURE - [Pa] - Pascal. Resolution 1Pa. Range [1:20000] */ +/* - OK/ALARM - Not applicable */ +/* - VOC - [IAQ] - Iaq index. Resolution 1IAQ. Range [1:500]. */ +/* - FLOODING - Not applicable */ +/* - PULSE_CNT - [NB] Number of pulses. Resolution 1 pulse. Range [1:8000000]. */ +/* - ELECTRICITY_METER - [W] - Watt; Resolution 1W. Range [1:8000000]. Average power consumption in period */ +/* - WATER_METER [l/min] - Liter per minute. Resolution 1l/min. Range [1:8000000]. Average water flow in period. */ +/* - SOIL_MOISTURE - [kPa] - Kilopascal (1kPa = 1000Pa); Resolution 1kPa. Range [1:1000]. Soil moisture (tension). */ +/* - CO_GAS - [ppm] - Parts per million. Resolution 1ppm. Range [1:1000000]. Carbon monoxide concentration. */ +/* - NO2_GAS - [ppm] - Parts per million. Resolution 1ppm. Range [1:1000000]. Nitrogen dioxide concentration. */ +/* - H2S_GAS - [ppm] - Parts per million. Resolution 0.01ppm. Range [0.01:80000.00]. Hydrogen sulfide concentration. */ +/* - AMBIENT_LIGHT -[lx] - Lux. Resolution 0.1lx. Range [0.1:100000.0]. Illuminance. */ +/* - PM_1_0 - [µg/m^3] - Micro gram per cubic meter. Resolution 1µg/m^3 Range [1:1000]. */ +/* - PM_2_5 - [µg/m^3] - Micro gram per cubic meter. Resolution 1µg/m^3 Range [1:1000]. */ +/* - PM_10_0 - [µg/m^3] - Micro gram per cubic meter. Resolution 1µg/m^3 Range [1:1000]. */ +/* - NOISE_LEVEL - [dB] - Decibels. Resolution 0.1 dB. Range: [0.1:200.0]. Noise level. */ +/* - NH3_GAS - [ppm] - Parts per million. Resolution 1ppm. Range [1:1000000]. Ammonia concentration. */ +/* - CH4_GAS - [ppm] - Parts per million. Resolution 1ppm. Range [1:1000000]. Methane concentration. */ +/* - HIGH_PRESSURE - [kPa] - Kilopascal (1kPa = 1000Pa, 100kPa = 1bar). Resolution 1kPa. Range [1:200000]. Pressure. */ +/* - DISTANCE_MM - [mm] - Millimeter. Resolution 1mm. Range [1:100000]. Distance. */ +/* - WATER_METER_ACC_MINOR - [l] - Liter. Resolution 1l. Range [1:1000000]. Accumulative water meter (minor). */ +/* - WATER_METER_ACC_MAJOR - [hl] - Hectoliter. Resolution 1hl. Range [1:1000000]. Accumulative water meter (major). */ +/* - CO2_GAS - [ppm] - Parts per million. Resolution 1ppm. Range [1:1000000]. Carbon dioxide concentration. */ +/* - HUMIDITY ACCURATE - [% RH] - Relative humidity. Resolution 0.1%. Range [0.1:100.0]. */ +/* - STATIC_IAQ - [sIAQ] - Static IAQ index. Resolution 1sIAQ. Range [1:10000]. */ +/* - CO2_EQUIVALENT - [ppm] - Parts per million. Resolution 1ppm. Range [1:1000000]. Carbon dioxide equivalent. */ +/* - BREATH_VOC - [ppm] - Parts per million. Resolution 1ppm. Range [1:100000]. Breath VOC estimate. */ +/* - PERCENTAGE - [%] - Percentage. Resolution 0.01%. Range [0.01:100.00]. */ +/* - PULSE_CNT_ACC_MINOR - [NB] - Number of pulses. Resolution 1 pulse. Range [1:1000000]. Accumulative pulse counter (minor). */ +/* - PULSE_CNT_ACC_MAJOR - [kNB] - Number of kilopulses. Resolution 1 kilopulse. Range [1:1000000]. */ +/* Accumulative pulse counter (major). */ +/* - ELEC_METER_ACC_MINOR - [Wh] - Watt-hour. Resolution 1Wh. Range [1:1000000]. Accumulative electricity meter (minor). */ +/* - ELEC_METER_ACC_MAJOR - [kWh] - Kilowatt-hour. Resolution 1kWh. Range [1:1000000]. Accumulative electricity meter (major). */ + +/* Condition to be checked by the device. If the condition is true, an action is triggered */ +enum Condition { + + /* Invalid value */ + CONDITION_UNSPECIFIED = 0; + + /* Threshold function for given rule_id is disabled */ + CONDITION_DISABLED = 1; + + /* Upper threshold. Continuous sensors only. If the measurement (or average from a few measurements) is over the threshold, */ + /* an action is triggered. */ + /* parameter[0] - Threshold value in "Encoding A" format. Must match channel type */ + /* parameter[1] - Hysteresis value in "Encoding R" format. Must much channel type. Set to "0" to disable */ + /* parameter[2] - Triggering mode: */ + /* - 1 - moving average (a1=(n1+n2+n3)/3, a2=(n2+n3+n4)/3, etc.) */ + /* - 2 - window average (a1=(n1+n2+n3)/3, a2=(n4+n5+n6)/3, etc.) */ + /* - 3 - consecutive samples (number of consecutive samples above threshold) */ + /* parameter[3] - Number of measurements for trigger determination. E.g parameter[3] equals 3, average value from three */ + /* samples will be calculated and compared to the threshold value in average mode or the third consecutive */ + /* sample above threshold will trigger action in consecutive mode. Range: [1:10]. */ + /* parameter[4] - Type of measurement (as described in MeasurementType). */ + CONDITION_HIGH_THRESHOLD = 2; + + /* Lower threshold. Continuous sensors only. If the measurement (or average from a few measurements) is below the threshold, */ + /* an action is triggered. */ + /* parameter[0] - Threshold value in "Encoding A" format. Must match channel type */ + /* parameter[1] - Hysteresis value in "Encoding R" format. Must much channel type. Set to "0" to disable */ + /* parameter[2] - Triggering mode: */ + /* - 1 - moving average (a1=(n1+n2+n3)/3, a2=(n2+n3+n4)/3, etc.) */ + /* - 2 - window average (a1=(n1+n2+n3)/3, a2=(n4+n5+n6)/3, etc.) */ + /* - 3 - consecutive samples (number of consecutive samples above threshold) */ + /* parameter[3] - Number of measurements for trigger determination. E.g parameter[3] equals 3, average value from three */ + /* samples will be calculated and compared to the threshold value in average mode or the third consecutive */ + /* sample below threshold will trigger action in consecutive mode. Range: [1:10]. */ + /* parameter[4] - Type of measurement (as described in MeasurementType). */ + CONDITION_LOW_THRESHOLD = 3; + + /* Differential threshold. Continuous sensors only. If the absolute value of the difference between the last value sent to */ + /* the server and the measurement value (or average from a few measurements) is greater or equal to the value of */ + /* the threshold set, an action is triggered. */ + /* parameter[0] - Threshold value in "Encoding R" format. Must match channel type */ + /* parameter[1] - Triggering mode: */ + /* - 1 - moving average (a1=(n1+n2+n3)/3, a2=(n2+n3+n4)/3, etc.) */ + /* - 2 - window average (a1=(n1+n2+n3)/3, a2=(n4+n5+n6)/3, etc.) */ + /* - 3 - consecutive samples (number of consecutive samples above threshold) */ + /* parameter[2] - Number of measurements for trigger determination. E.g parameter[3] equals 3, average value from three */ + /* samples will be calculated and compared to the threshold value in average mode or the third consecutive */ + /* sample exceeding threshold will trigger action in consecutive mode. Range: [1:10]. */ + /* parameter[3] - Type of measurement (as described in MeasurementType). */ + CONDITION_DIFF_THRESHOLD = 4; + + /* Change of binary sensor's state. Binary sensors only. Each change of the binary's sensor state will trigger an action. */ + CONDITION_BINARY_CHANGE_STATE = 5; + + /* Logic operator. Used for combining multiple rules into more complex conditions. If the logic condition specified by */ + /* parameters (logic operator and selected rules) is met, an action is triggered. */ + /* parameter[0] - Logic operator (as described in LogicOperation). */ + /* parameter[1] - Rule selector (bit mask). Specifies which rules should be taken into account while determining */ + /* rules outcome. */ + /* parameter[2] - Rule negation (bit mask). Specifies which of chosen in parameter[1] rules should be negated */ + /* before determining rules outcome. */ + /* parameter[3] - Rule action delay [s]. Specifies time delay between the rule activation and rule action being triggered. */ + /* Range: [0:864000]. */ + /* parameter[4] - Rule return delay [s]. Specifies time delay between the rule deactivation and rule action being triggered. */ + /* Range: [0:864001]. Max parameter value disables action triggering on rule deactivation. */ + CONDITION_LOGIC_OPERATOR = 6; + + /* On measurement. Continous sensors only. The basic function is to trigger communication after measurement if at least 60s */ + /* have passed since the last one. Transmission may occur every x measurement. Optionally dependency on the other rule can */ + /* be configured, then, when all conditions are met, transmission is triggered. */ + /* parameter[0] - Send every n measurement. This parameter specifies every which measurement transmission will be triggered */ + /* if all other conditions are met. Range: [1:500]. If parameter[0] equals 1, transmission will occur after */ + /* every measurement. */ + /* parameter[1] - Optional. Rule selector (bit mask). Specifies which rule should be taken into account while determining */ + /* the measurement rule outcome. */ + /* parameter[2] - Optional. Rule negation (bit mask). Specifies which of chosen in parameter[1] rule should be negated */ + /* before determining the measurement rule outcome. */ + CONDITION_ON_MEASUREMENT = 7; +} + +/* Logic operators to be used for determining the outcome of rules with logic operator condition. */ +enum LogicOperator { + + /* Invalid use */ + LOGIC_OPERATOR_UNSPECIFIED = 0; + + /* Logic AND */ + LOGIC_OPERATOR_AND = 1; + + /* Logic OR */ + LOGIC_OPERATOR_OR = 2; +} + +/* Action to be triggered. Currently the only possible action is to trigger the transmission. */ +/* Other actions will be available in next SW releases. */ +enum Action { + + /* Invalid value */ + ACTION_UNSPECIFIED = 0; + + /* To trigger the transmission */ + ACTION_TRIGGER_TRANSMISSION = 1; + + /* To take no action. Possible for logic operator components */ + ACTION_NO_ACTION = 2; + + /* To trigger the transmission with ACK */ + ACTION_TRIGGER_TRANSMISSION_WITH_ACK = 3; +} + +/* Type of a rule calendars. */ +enum CalendarType { + + /* Invalid value */ + CALENDAR_TYPE_UNSPECIFIED = 0; + + /* Type for inactive calendars */ + CALENDAR_TYPE_DISABLED = 1; + + /* Week type. Enables selcted rules on specified days of the week in specified time periods. */ + /* parameter[0] - Week day mask. Bitmask of days when selected rules are enabled */ + /* - Bit 0 - Sunday */ + /* - Bit 1 - Monday */ + /* ... */ + /* - Bit 6 - Saturday */ + /* parameter[1] - 'From time' - point in time from which selected rules will be enabled (in minutes from midnight). */ + /* parameter[2] - 'To time' - point in time from which selected rules will be disabled (in minutes from midnight). */ + /* Note: if 'From time' is bigger than 'To time' there are two periods when rules are enabled - from 00:00 to 'To time' */ + /* and from 'From time' to 23:59. */ + /* parameter[3] - Timezone - desired timezone for date comparison. Encoded as number (N) of 15 minutes offsets */ + /* - example - if N = 4, then offset = 4 * 15min = 1h. I.e. timezone is UTC+1. */ + CALENDAR_TYPE_WEEK = 2; +} + +/* Rules calendars. Used for enabling/disabling rules based on date/time. */ +/* It is possible to configure up to 6 calendars. Each of them can affect any number of rules. */ +message ProtoCalendar { + + /* Bit mask of selected rules. Mask on bits [0:11] */ + /* - Bit 0 - Rule ID 0 */ + /* - Bit 1 - Rule ID 1 */ + /* ... */ + /* - Bit 11 - Rule ID 11 */ + uint32 rule_mask = 1; + + /* Calendars's parameters. Described in Type. */ + repeated sint32 parameters = 2; + + /* Calendar's type. Described in Type. */ + CalendarType type = 3; +} + +/* Rules used to define edge logic on the device. Rules are defined by conditions and actions: */ +/* If Condition is true, trigger Action. It is possible to configure up to 12 rules and assign them to different channels. */ +/* One rule can be assigned to any number of channels. For instance rule "If temperature is over 10 C, trigger the transmission"*/ +/* can be assigned to channels 1 and 2. No matter to how many channels a rule is assigned, it's still counted as one rule. */ +message ProtoRule { + + /* Channels to which the rule is assigned. One rule can be assigned to multiple channels as long as those are of the same type*/ + /* Bit mask on bits [0:5]. E.g. To assign the rule for channel 1: "000001", to assign rule to channels 2 and 4: "001010" */ + uint32 channel_mask = 1; + + /* Rule's condition (as described in Condition). */ + Condition condition = 2; + + /* Condition's parameters (as described in Condition). For binary sensors there are no parameters */ + repeated sint32 parameters = 3; + + /* Action to be triggered. */ + Action action = 4; +} \ No newline at end of file diff --git a/common/transport/coap/src/main/proto/proto_measurements.proto b/common/transport/coap/src/main/proto/proto_measurements.proto deleted file mode 100644 index 27bd6e3313..0000000000 --- a/common/transport/coap/src/main/proto/proto_measurements.proto +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -syntax = "proto3"; -import "proto_measurement_types.proto"; - -option java_package = "org.thingsboard.server.gen.transport.coap"; -option java_outer_classname = "MeasurementsProtos"; - -message ProtoChannel{ - /* Type of channel */ - MeasurementType type = 1; - - /* Timestamp of the first sample (the oldest one) in seconds since UNIX EPOCH 01-01-1970 */ - int32 timestamp = 2; - - /* Only used for 'Continuous' sensor types. Value used as the starting point for calculating the values of all */ - /* measurements in the package. */ - /* Format defined by 'MeasurementType' field */ - sint32 start_point = 4; - - /* 'Continuous' sensor types */ - /* Value of the offset from the 'start_point' for each measurement in the package. The oldest sample first ([0]). */ - /* 'sample_offsets' format defined by 'MeasurementType' field. */ - /* Example: MeasurementType = 1 (temperature), start_point = 100, sample_offsets[0] = 15, sample_offsets[1] = 20 */ - /* 1st sample in the package temperature value = 11.5 °C, 2nd sample in the package temperature value = 12 °C */ - /* Calculating timestamps of the measurements: timestamp = 1606391700, measurement_period_base = 60, */ - /* measurement_period_factor = 1. Timestamp of the 1st sample = 1606391700, timestamp of the 2nd sample = 1606391760 */ - /* 'Binary' sensor types: */ - /* Absolute value of the 'sample_offsets' field indicates the offset in seconds from 'timestamp' field. */ - /* Sign (- or +) indicates the state of measurements depend of sensor type. */ - /* Value of this field equals to '1' or '-1' indicates the state at the 'timestamp'. Other values */ - /* indicate the state of the relay at the time (in seconds) equal to 'timestamp' + value. */ - /* Values of this field are incremented starting from 1 (1->0: state at the time */ - /* of 'timestamp', 2->1: state at the time equal to 'timestamp' + 1 s, 3->2 : */ - /* state at the time equal to 'timestamp' + 2 s, etc.). The first and the last sample define the time range of the */ - /* measurements. Only state changes in the time range are included in the 'sample_offsets' field */ - /* Examples: if 'timestamp' value is 1553518060 and 'sample_offsets' equals '1', it means that at 1553518060 the state */ - /* was high, if 'timestamp' value is 1553518060 and 'sample_offsets' equals '-9', it means at 1553518068 the state was low */ - repeated sint32 sample_offsets = 5 [packed=true]; - - /* Deprecated - configuration is sent to endpoint 'c' */ - //int32 lo_threshold = 6; - - /* Deprecated - configuration is sent to endpoint 'c' */ - //int32 hi_threshold = 7; - - /* Deprecated - configurationis sent to endpoint 'c' */ - //int32 diff_threshold = 8; - } - -message ProtoMeasurements { - - /* serial number of the device */ - bytes serial_num = 1; - - /* true - battery ok, false - battery low */ - bool battery_status = 2; - - /* 'Measurement_period_base' and 'measurement_period_factor' define how often the measurements are taken. */ - /* Sensors of 'Continuous' type take measurement each Measurement_period_base * measurement_period_factor. */ - /* Sensors of 'Binary' type take measurement each Measurement_period_base. */ - /* For backward compatibility with versions 5.x in case of binary/mixed sensors, if the 'measurement_period_factor' is */ - /* not sent (equal to 0), then the default value '14' shall be used for period calculation. */ - /* For backward compatibility with versions 5.x in case of continues sensors, if the measurement_period_factor is */ - /* not sent (equal to 0), then the default value '1' shall be used for period calculation. */ - /* measurement period base in seconds */ - uint32 measurement_period_base = 3; - - /* Measurement period factor */ - uint32 measurement_period_factor = 8; - - repeated ProtoChannel channels = 4; - - /* Timestamp of the next scheduled transmission. If the device will not send data until this time, */ - /* it should be considered as 'lost' */ - uint32 next_transmission_at = 5; - - /* reason of transmission - unsigned integer where each bit indicates different */ - /* possible communication reason. Can be more than one */ - /* - bit 0: first message after sensor reset */ - /* - bit 1: user button triggered */ - /* - bit 2: user BLE triggered */ - /* - bit 3-7: number of retries -> incremented after each unsuccessful transmission. Max value 4. */ - /* Set to 0 after a successful transmission. */ - /* - bit 8: channel 1 lower threshold exceeded */ - /* - bit 9: channel 1 lower threshold returned */ - /* - bit 10: channel 1 higher threshold exceeded */ - /* - bit 11: channel 1 higher threshold returned */ - /* - bit 12: channel 1 differential threshold crossed */ - /* - bits 13-17: channel 2 thresholds (same as for channel 1) */ - /* - bits 18-22: channel 3 thresholds (same as for channel 1) */ - /* - bits 23-27: channel 4 or 5 or 6 thresholds (same as for channel 1) */ - uint32 transfer_reason = 6; - - /* Signal strength level mapped from RSSI */ - /* - 0 : 113 dBm or less */ - /* - 1 : 111 dBm */ - /* - 2...30 : 109...-53 dBm */ - /* - 31 : -51 dBm or greater */ - /* - 99 : Not known or not detectable */ - uint32 signal = 7; - - /* Hash of the current configuration. Hash value changes each time a device receives a new configuration */ - uint32 hash = 9; - - /* Optional string up to 36 bytes long. Can be set to any user define value or hold device's IMEI */ - string cloud_token = 16; -} \ No newline at end of file diff --git a/common/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapTransportResourceTest.java b/common/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapTransportResourceTest.java new file mode 100644 index 0000000000..c7f33e3694 --- /dev/null +++ b/common/transport/coap/src/test/java/org/thingsboard/server/transport/coap/CoapTransportResourceTest.java @@ -0,0 +1,161 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.coap; + +import org.eclipse.californium.core.coap.CoAP; +import org.eclipse.californium.core.coap.OptionSet; +import org.eclipse.californium.core.coap.Request; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.thingsboard.server.coapserver.CoapServerService; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.msg.session.FeatureType; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.queue.scheduler.SchedulerComponent; +import org.thingsboard.server.transport.coap.client.CoapClientContext; + +import java.util.Random; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class CoapTransportResourceTest { + + private static final String V1 = "v1"; + private static final String API = "api"; + private static final String TELEMETRY = "telemetry"; + private static final String ATTRIBUTES = "attributes"; + private static final String RPC = "rpc"; + private static final String CLAIM = "claim"; + private static final String PROVISION = "provision"; + private static final String GET_ATTRIBUTES_URI_QUERY = "clientKeys=attribute1,attribute2&sharedKeys=shared1,shared2"; + + private static final Random RANDOM = new Random(); + + private static CoapTransportResource coapTransportResource; + + @BeforeAll + static void setUp() { + + var ctxMock = mock(CoapTransportContext.class); + var coapServerServiceMock = mock(CoapServerService.class); + var transportServiceMock = mock(TransportService.class); + var clientContextMock = mock(CoapClientContext.class); + var schedulerComponentMock = mock(SchedulerComponent.class); + + when(ctxMock.getTransportService()).thenReturn(transportServiceMock); + when(ctxMock.getClientContext()).thenReturn(clientContextMock); + when(ctxMock.getSessionReportTimeout()).thenReturn(1L); + when(ctxMock.getScheduler()).thenReturn(schedulerComponentMock); + + coapTransportResource = new CoapTransportResource(ctxMock, coapServerServiceMock, V1); + } + + @ParameterizedTest + @MethodSource("provideRequestAndFeatureType") + void givenRequest_whenGetFeatureType_thenReturnedExpectedFeatureType(Request request, FeatureType expectedFeatureType) { + var featureTypeOptional = coapTransportResource.getFeatureType(request); + + assertTrue(featureTypeOptional.isPresent(), "Optional is empty"); + assertEquals(expectedFeatureType, featureTypeOptional.get(), "Feature type is invalid"); + } + + static Stream provideRequestAndFeatureType() { + return Stream.of( + // accessToken based tests + Arguments.of(toAccessTokenRequest(CoAP.Code.POST, TELEMETRY), FeatureType.TELEMETRY), + Arguments.of(toAccessTokenRequest(CoAP.Code.POST, ATTRIBUTES), FeatureType.ATTRIBUTES), + Arguments.of(toGetAttributesAccessTokenRequest(), FeatureType.ATTRIBUTES), + Arguments.of(toAccessTokenRequest(CoAP.Code.GET, ATTRIBUTES), FeatureType.ATTRIBUTES), + Arguments.of(toAccessTokenRequest(CoAP.Code.GET, RPC), FeatureType.RPC), + Arguments.of(toRpcResponseAccessTokenRequest(), FeatureType.RPC), + Arguments.of(toAccessTokenRequest(CoAP.Code.POST, RPC), FeatureType.RPC), + Arguments.of(toAccessTokenRequest(CoAP.Code.POST, CLAIM), FeatureType.CLAIM), + // certificate based tests + Arguments.of(toCertificateRequest(CoAP.Code.POST, TELEMETRY), FeatureType.TELEMETRY), + Arguments.of(toCertificateRequest(CoAP.Code.POST, ATTRIBUTES), FeatureType.ATTRIBUTES), + Arguments.of(toGetAttributesCertificateRequest(), FeatureType.ATTRIBUTES), + Arguments.of(toCertificateRequest(CoAP.Code.GET, ATTRIBUTES), FeatureType.ATTRIBUTES), + Arguments.of(toCertificateRequest(CoAP.Code.GET, RPC), FeatureType.RPC), + Arguments.of(toRpcResponseCertificateRequest(), FeatureType.RPC), + Arguments.of(toCertificateRequest(CoAP.Code.POST, RPC), FeatureType.RPC), + Arguments.of(toCertificateRequest(CoAP.Code.POST, CLAIM), FeatureType.CLAIM), + // provision request + Arguments.of(toProvisionRequest(), FeatureType.PROVISION) + ); + } + + private static Request toAccessTokenRequest(CoAP.Code method, String featureType) { + return getAccessTokenRequest(method, featureType, null, null); + } + + private static Request toGetAttributesAccessTokenRequest() { + return getAccessTokenRequest(CoAP.Code.GET, CoapTransportResourceTest.ATTRIBUTES, null, CoapTransportResourceTest.GET_ATTRIBUTES_URI_QUERY); + } + + private static Request toRpcResponseAccessTokenRequest() { + return getAccessTokenRequest(CoAP.Code.POST, CoapTransportResourceTest.RPC, RANDOM.nextInt(100), null); + } + + private static Request toCertificateRequest(CoAP.Code method, String featureType) { + return getCertificateRequest(method, featureType, null, null); + } + + private static Request toGetAttributesCertificateRequest() { + return getCertificateRequest(CoAP.Code.GET, CoapTransportResourceTest.ATTRIBUTES, null, CoapTransportResourceTest.GET_ATTRIBUTES_URI_QUERY); + } + + private static Request toRpcResponseCertificateRequest() { + return getCertificateRequest(CoAP.Code.POST, CoapTransportResourceTest.RPC, RANDOM.nextInt(100), null); + } + + private static Request getAccessTokenRequest(CoAP.Code method, String featureType, Integer requestId, String uriQuery) { + return getRequest(method, featureType, false, requestId, uriQuery); + } + + private static Request getCertificateRequest(CoAP.Code method, String featureType, Integer requestId, String uriQuery) { + return getRequest(method, featureType, true, requestId, uriQuery); + } + + private static Request toProvisionRequest() { + return getRequest(CoAP.Code.POST, PROVISION, true, null, null); + } + + private static Request getRequest(CoAP.Code method, String featureType, boolean dtls, Integer requestId, String uriQuery) { + var request = new Request(method); + var options = new OptionSet(); + options.addUriPath(API); + options.addUriPath(V1); + if (!dtls) { + options.addUriPath(StringUtils.randomAlphanumeric(20)); + } + options.addUriPath(featureType); + if (requestId != null) { + options.addUriPath(String.valueOf(requestId)); + } + if (uriQuery != null) { + options.setUriQuery(uriQuery); + } + request.setOptions(options); + return request; + } + +} diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index 09f822959f..fdbb45fd6c 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java index 041ae0510d..589e93d803 100644 --- a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java +++ b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java @@ -46,7 +46,7 @@ import org.thingsboard.server.common.transport.SessionMsgListener; import org.thingsboard.server.common.transport.TransportContext; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportServiceCallback; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.transport.auth.SessionInfoCreator; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos; diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index f5ee1eea52..5c37bbcf89 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java index 3dd13cd171..7ba78193b7 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mVersionedModelProvider.java @@ -152,8 +152,7 @@ public class LwM2mVersionedModelProvider implements LwM2mModelProvider { private ObjectModel getObjectModel(String key) { Optional tbResource = context.getTransportResourceCache().get(this.tenantId, LWM2M_MODEL, key); - return tbResource.map(resource -> helper.parseFromXmlToObjectModel( - Base64.getDecoder().decode(resource.getData()), + return tbResource.map(resource -> helper.parseFromXmlToObjectModel(resource.getData(), key + ".xml")).orElse(null); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MJsonAdaptor.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MJsonAdaptor.java index feb74b47f9..afe1d160ce 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MJsonAdaptor.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MJsonAdaptor.java @@ -19,8 +19,8 @@ import com.google.gson.JsonElement; import com.google.gson.JsonSyntaxException; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.common.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MTransportAdaptor.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MTransportAdaptor.java index 96e6008c63..fe24b21053 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MTransportAdaptor.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/adaptors/LwM2MTransportAdaptor.java @@ -16,7 +16,7 @@ package org.thingsboard.server.transport.lwm2m.server.adaptors; import com.google.gson.JsonElement; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.AdaptorException; import org.thingsboard.server.gen.transport.TransportProtos; import java.util.Collection; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/model/LwM2MModelConfigServiceImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/model/LwM2MModelConfigServiceImpl.java index 302bd20c8b..eef9a53024 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/model/LwM2MModelConfigServiceImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/model/LwM2MModelConfigServiceImpl.java @@ -52,7 +52,7 @@ import java.util.stream.Collectors; public class LwM2MModelConfigServiceImpl implements LwM2MModelConfigService { @Autowired - private TbLwM2MModelConfigStore modelStore; + TbLwM2MModelConfigStore modelStore; @Autowired @Lazy @@ -67,14 +67,14 @@ public class LwM2MModelConfigServiceImpl implements LwM2MModelConfigService { @Autowired private LwM2MTelemetryLogService logService; - private ConcurrentMap currentModelConfigs; + ConcurrentMap currentModelConfigs; @AfterStartUp(order = AfterStartUp.BEFORE_TRANSPORT_SERVICE) - private void init() { + public void init() { List models = modelStore.getAll(); log.debug("Fetched model configs: {}", models); currentModelConfigs = models.stream() - .collect(Collectors.toConcurrentMap(LwM2MModelConfig::getEndpoint, m -> m)); + .collect(Collectors.toConcurrentMap(LwM2MModelConfig::getEndpoint, m -> m, (existing, replacement) -> existing)); } @Override diff --git a/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/model/LwM2MModelConfigServiceImplTest.java b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/model/LwM2MModelConfigServiceImplTest.java new file mode 100644 index 0000000000..fc54ca9e0b --- /dev/null +++ b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/model/LwM2MModelConfigServiceImplTest.java @@ -0,0 +1,73 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.model; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MModelConfigStore; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.Mockito.mock; + +class LwM2MModelConfigServiceImplTest { + + LwM2MModelConfigServiceImpl service; + TbLwM2MModelConfigStore modelStore; + + @BeforeEach + void setUp() { + service = new LwM2MModelConfigServiceImpl(); + modelStore = mock(TbLwM2MModelConfigStore.class); + service.modelStore = modelStore; + } + + @Test + void testInitWithDuplicatedModels() { + LwM2MModelConfig config = new LwM2MModelConfig("urn:imei:951358811362976"); + List models = List.of(config, config); + willReturn(models).given(modelStore).getAll(); + service.init(); + assertThat(service.currentModelConfigs).containsExactlyEntriesOf(Map.of(config.getEndpoint(), config)); + } + + @Test + void testInitWithNonUniqueEndpoints() { + LwM2MModelConfig configAlfa = new LwM2MModelConfig("urn:imei:951358811362976"); + LwM2MModelConfig configBravo = new LwM2MModelConfig("urn:imei:151358811362976"); + LwM2MModelConfig configDelta = new LwM2MModelConfig("urn:imei:151358811362976"); + assertThat(configBravo.getEndpoint()).as("non-unique endpoints provided").isEqualTo(configDelta.getEndpoint()); + List models = List.of(configAlfa, configBravo, configDelta); + willReturn(models).given(modelStore).getAll(); + service.init(); + assertThat(service.currentModelConfigs).containsExactlyInAnyOrderEntriesOf(Map.of( + configAlfa.getEndpoint(), configAlfa, + configBravo.getEndpoint(), configBravo + )); + } + + @Test + void testInitWithEmptyModels() { + willReturn(Collections.emptyList()).given(modelStore).getAll(); + service.init(); + assertThat(service.currentModelConfigs).isEmpty(); + } + +} diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index e11f923a0b..5c0aed7121 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index 6267ae9424..87f6a47150 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -59,11 +59,12 @@ import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.rpc.RpcStatus; import org.thingsboard.server.common.msg.EncryptionUtil; +import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.tools.TbRateLimitsException; import org.thingsboard.server.common.transport.SessionMsgListener; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportServiceCallback; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.AdaptorException; import org.thingsboard.server.common.transport.auth.SessionInfoCreator; import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; @@ -136,6 +137,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement private static final MqttQoS MAX_SUPPORTED_QOS_LVL = AT_LEAST_ONCE; private final UUID sessionId; + protected final MqttTransportContext context; private final TransportService transportService; private final SchedulerComponent scheduler; @@ -442,10 +444,12 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement MqttTransportAdaptor payloadAdaptor = deviceSessionCtx.getPayloadAdaptor(); if (deviceSessionCtx.isDeviceAttributesTopic(topicName)) { TransportProtos.PostAttributeMsg postAttributeMsg = payloadAdaptor.convertToPostAttributes(deviceSessionCtx, mqttMsg); - transportService.process(deviceSessionCtx.getSessionInfo(), postAttributeMsg, getPubAckCallback(ctx, msgId, postAttributeMsg)); + transportService.process(deviceSessionCtx.getSessionInfo(), postAttributeMsg, getMetadata(deviceSessionCtx, topicName), + getPubAckCallback(ctx, msgId, postAttributeMsg)); } else if (deviceSessionCtx.isDeviceTelemetryTopic(topicName)) { TransportProtos.PostTelemetryMsg postTelemetryMsg = payloadAdaptor.convertToPostTelemetry(deviceSessionCtx, mqttMsg); - transportService.process(deviceSessionCtx.getSessionInfo(), postTelemetryMsg, getPubAckCallback(ctx, msgId, postTelemetryMsg)); + transportService.process(deviceSessionCtx.getSessionInfo(), postTelemetryMsg, getMetadata(deviceSessionCtx, topicName), + getPubAckCallback(ctx, msgId, postTelemetryMsg)); } else if (topicName.startsWith(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX)) { TransportProtos.GetAttributeRequestMsg getAttributeMsg = payloadAdaptor.convertToGetAttributes(deviceSessionCtx, mqttMsg, MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX); transportService.process(deviceSessionCtx.getSessionInfo(), getAttributeMsg, getPubAckCallback(ctx, msgId, getAttributeMsg)); @@ -466,22 +470,28 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement getOtaPackageCallback(ctx, mqttMsg, msgId, fwMatcher, OtaPackageType.SOFTWARE); } else if (topicName.equals(MqttTopics.DEVICE_TELEMETRY_SHORT_TOPIC)) { TransportProtos.PostTelemetryMsg postTelemetryMsg = payloadAdaptor.convertToPostTelemetry(deviceSessionCtx, mqttMsg); - transportService.process(deviceSessionCtx.getSessionInfo(), postTelemetryMsg, getPubAckCallback(ctx, msgId, postTelemetryMsg)); + transportService.process(deviceSessionCtx.getSessionInfo(), postTelemetryMsg, getMetadata(deviceSessionCtx, topicName), + getPubAckCallback(ctx, msgId, postTelemetryMsg)); } else if (topicName.equals(MqttTopics.DEVICE_TELEMETRY_SHORT_JSON_TOPIC)) { TransportProtos.PostTelemetryMsg postTelemetryMsg = context.getJsonMqttAdaptor().convertToPostTelemetry(deviceSessionCtx, mqttMsg); - transportService.process(deviceSessionCtx.getSessionInfo(), postTelemetryMsg, getPubAckCallback(ctx, msgId, postTelemetryMsg)); + transportService.process(deviceSessionCtx.getSessionInfo(), postTelemetryMsg, getMetadata(deviceSessionCtx, topicName), + getPubAckCallback(ctx, msgId, postTelemetryMsg)); } else if (topicName.equals(MqttTopics.DEVICE_TELEMETRY_SHORT_PROTO_TOPIC)) { TransportProtos.PostTelemetryMsg postTelemetryMsg = context.getProtoMqttAdaptor().convertToPostTelemetry(deviceSessionCtx, mqttMsg); - transportService.process(deviceSessionCtx.getSessionInfo(), postTelemetryMsg, getPubAckCallback(ctx, msgId, postTelemetryMsg)); + transportService.process(deviceSessionCtx.getSessionInfo(), postTelemetryMsg, getMetadata(deviceSessionCtx, topicName), + getPubAckCallback(ctx, msgId, postTelemetryMsg)); } else if (topicName.equals(MqttTopics.DEVICE_ATTRIBUTES_SHORT_TOPIC)) { TransportProtos.PostAttributeMsg postAttributeMsg = payloadAdaptor.convertToPostAttributes(deviceSessionCtx, mqttMsg); - transportService.process(deviceSessionCtx.getSessionInfo(), postAttributeMsg, getPubAckCallback(ctx, msgId, postAttributeMsg)); + transportService.process(deviceSessionCtx.getSessionInfo(), postAttributeMsg, getMetadata(deviceSessionCtx, topicName), + getPubAckCallback(ctx, msgId, postAttributeMsg)); } else if (topicName.equals(MqttTopics.DEVICE_ATTRIBUTES_SHORT_JSON_TOPIC)) { TransportProtos.PostAttributeMsg postAttributeMsg = context.getJsonMqttAdaptor().convertToPostAttributes(deviceSessionCtx, mqttMsg); - transportService.process(deviceSessionCtx.getSessionInfo(), postAttributeMsg, getPubAckCallback(ctx, msgId, postAttributeMsg)); + transportService.process(deviceSessionCtx.getSessionInfo(), postAttributeMsg, getMetadata(deviceSessionCtx, topicName), + getPubAckCallback(ctx, msgId, postAttributeMsg)); } else if (topicName.equals(MqttTopics.DEVICE_ATTRIBUTES_SHORT_PROTO_TOPIC)) { TransportProtos.PostAttributeMsg postAttributeMsg = context.getProtoMqttAdaptor().convertToPostAttributes(deviceSessionCtx, mqttMsg); - transportService.process(deviceSessionCtx.getSessionInfo(), postAttributeMsg, getPubAckCallback(ctx, msgId, postAttributeMsg)); + transportService.process(deviceSessionCtx.getSessionInfo(), postAttributeMsg, getMetadata(deviceSessionCtx, topicName), + getPubAckCallback(ctx, msgId, postAttributeMsg)); } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_RESPONSE_SHORT_JSON_TOPIC)) { TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = context.getJsonMqttAdaptor().convertToDeviceRpcResponse(deviceSessionCtx, mqttMsg, MqttTopics.DEVICE_RPC_RESPONSE_SHORT_JSON_TOPIC); transportService.process(deviceSessionCtx.getSessionInfo(), rpcResponseMsg, getPubAckCallback(ctx, msgId, rpcResponseMsg)); @@ -525,6 +535,16 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } } + private TbMsgMetaData getMetadata(DeviceSessionCtx ctx, String topicName) { + if (ctx.isDeviceProfileMqttTransportType()) { + TbMsgMetaData md = new TbMsgMetaData(); + md.putValue(DataConstants.MQTT_TOPIC, topicName); + return md; + } else { + return null; + } + } + private void sendAckOrCloseSession(ChannelHandlerContext ctx, String topicName, int msgId) { if ((deviceSessionCtx.isSendAckOnValidationException() || MqttVersion.MQTT_5.equals(deviceSessionCtx.getMqttVersion())) && msgId > 0) { log.debug("[{}] Send pub ack on invalid publish msg [{}][{}]", sessionId, topicName, msgId); diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/BackwardCompatibilityAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/BackwardCompatibilityAdaptor.java index 40be642a1a..73bba60cff 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/BackwardCompatibilityAdaptor.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/BackwardCompatibilityAdaptor.java @@ -21,7 +21,7 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.ota.OtaPackageType; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.AdaptorException; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext; diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java index 513fd97cf6..a30749b17d 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java @@ -30,8 +30,8 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.device.profile.MqttTopics; import org.thingsboard.server.common.data.ota.OtaPackageType; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.common.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext; diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java index 3764c2a9a9..10599d0be7 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java @@ -24,7 +24,7 @@ import io.netty.handler.codec.mqtt.MqttMessageType; import io.netty.handler.codec.mqtt.MqttPublishMessage; import io.netty.handler.codec.mqtt.MqttPublishVariableHeader; import org.thingsboard.server.common.data.ota.OtaPackageType; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.AdaptorException; import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/ProtoMqttAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/ProtoMqttAdaptor.java index 39176fa212..3ff4faf8e8 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/ProtoMqttAdaptor.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/ProtoMqttAdaptor.java @@ -25,12 +25,12 @@ import io.netty.handler.codec.mqtt.MqttMessage; import io.netty.handler.codec.mqtt.MqttPublishMessage; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.JsonConverter; +import org.thingsboard.server.common.adaptor.ProtoConverter; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.device.profile.MqttTopics; import org.thingsboard.server.common.data.ota.OtaPackageType; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; -import org.thingsboard.server.common.transport.adaptor.ProtoConverter; import org.thingsboard.server.gen.transport.TransportApiProtos; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx; diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/AbstractGatewaySessionHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/AbstractGatewaySessionHandler.java index 95603e751c..64aa448931 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/AbstractGatewaySessionHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/AbstractGatewaySessionHandler.java @@ -35,13 +35,14 @@ import io.netty.handler.codec.mqtt.MqttPublishMessage; import lombok.extern.slf4j.Slf4j; import org.springframework.util.CollectionUtils; import org.springframework.util.ConcurrentReferenceHashMap; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportServiceCallback; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; -import org.thingsboard.server.common.transport.adaptor.ProtoConverter; +import org.thingsboard.server.common.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.JsonConverter; +import org.thingsboard.server.common.adaptor.ProtoConverter; import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; import org.thingsboard.server.gen.transport.TransportApiProtos; @@ -71,6 +72,7 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import static org.springframework.util.ConcurrentReferenceHashMap.ReferenceType; +import static org.thingsboard.server.common.data.DataConstants.DEFAULT_DEVICE_TYPE; import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_CLOSED; import static org.thingsboard.server.common.transport.service.DefaultTransportService.SESSION_EVENT_MSG_OPEN; import static org.thingsboard.server.common.transport.service.DefaultTransportService.SUBSCRIBE_TO_ATTRIBUTE_UPDATES_ASYNC_MSG; @@ -85,7 +87,6 @@ import static org.thingsboard.server.transport.mqtt.util.sparkplug.SparkplugMess @Slf4j public abstract class AbstractGatewaySessionHandler { - protected static final String DEFAULT_DEVICE_TYPE = "default"; private static final String CAN_T_PARSE_VALUE = "Can't parse value: "; private static final String DEVICE_PROPERTY = "device"; @@ -188,7 +189,7 @@ public abstract class AbstractGatewaySessionHandler() { @Override public void onSuccess(@Nullable T result) { ack(msg, ReturnCode.SUCCESS); - log.trace("[{}] onDeviceConnectOk: {}", sessionId, deviceName); + log.trace("[{}][{}][{}] onDeviceConnectOk: [{}]", gateway.getTenantId(), gateway.getDeviceId(), sessionId, deviceName); } @Override public void onFailure(Throwable t) { - log.warn("[{}] Failed to process device connect command: {}", sessionId, deviceName, t); - + logDeviceCreationError(t, deviceName); } }, context.getExecutor()); } @@ -248,7 +248,8 @@ public abstract class AbstractGatewaySessionHandler checkDeviceConnected(String deviceName) { T ctx = devices.get(deviceName); if (ctx == null) { - log.debug("[{}] Missing device [{}] for the gateway session", sessionId, deviceName); + log.debug("[{}][{}][{}] Missing device [{}] for the gateway session", gateway.getTenantId(), gateway.getDeviceId(), sessionId, deviceName); return onDeviceConnect(deviceName, DEFAULT_DEVICE_TYPE); } else { return Futures.immediateFuture(ctx); @@ -733,7 +743,7 @@ public abstract class AbstractGatewaySessionHandler() { @Override public void onSuccess(Void dummy) { - log.trace("[{}][{}] Published msg: {}", sessionId, deviceName, msg); + log.trace("[{}][{}][{}][{}] Published msg: [{}]", gateway.getTenantId(), gateway.getDeviceId(), sessionId, deviceName, msg); if (msgId > 0) { ctx.writeAndFlush(MqttTransportHandler.createMqttPubAckMsg(deviceSessionCtx, msgId, ReturnCode.SUCCESS)); } @@ -757,7 +767,7 @@ public abstract class AbstractGatewaySessionHandler mqttQoSMap, MqttTransportContext context) { super(sessionId, mqttQoSMap); this.context = context; @@ -165,6 +169,7 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { MqttDeviceProfileTransportConfiguration mqttConfig = (MqttDeviceProfileTransportConfiguration) transportConfiguration; TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = mqttConfig.getTransportPayloadTypeConfiguration(); payloadType = transportPayloadTypeConfiguration.getTransportPayloadType(); + deviceProfileMqttTransportType = true; telemetryTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceTelemetryTopic()); attributesPublishTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceAttributesTopic()); attributesSubscribeTopicFilter = MqttTopicFilterFactory.toFilter(mqttConfig.getDeviceAttributesSubscribeTopic()); @@ -179,6 +184,7 @@ public class DeviceSessionCtx extends MqttDeviceAwareSessionContext { telemetryTopicFilter = MqttTopicFilterFactory.getDefaultTelemetryFilter(); attributesPublishTopicFilter = MqttTopicFilterFactory.getDefaultAttributesFilter(); payloadType = TransportPayloadType.JSON; + deviceProfileMqttTransportType = false; sendAckOnValidationException = false; } updateAdaptor(); diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java index de0f2a502b..6ff505cb36 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java @@ -17,7 +17,7 @@ package org.thingsboard.server.transport.mqtt.session; import io.netty.buffer.ByteBuf; import io.netty.handler.codec.mqtt.MqttPublishMessage; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.AdaptorException; import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; import java.util.UUID; diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/SparkplugNodeSessionHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/SparkplugNodeSessionHandler.java index 48998d58cd..f3aaa2708f 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/SparkplugNodeSessionHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/SparkplugNodeSessionHandler.java @@ -32,9 +32,9 @@ import org.springframework.util.CollectionUtils; import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; -import org.thingsboard.server.common.transport.adaptor.ProtoConverter; +import org.thingsboard.server.common.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.JsonConverter; +import org.thingsboard.server.common.adaptor.ProtoConverter; import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; import org.thingsboard.server.gen.transport.TransportApiProtos; import org.thingsboard.server.gen.transport.TransportProtos; diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/sparkplug/MetricDataType.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/sparkplug/MetricDataType.java index d5e781a402..c49286a4b1 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/sparkplug/MetricDataType.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/util/sparkplug/MetricDataType.java @@ -16,7 +16,7 @@ package org.thingsboard.server.transport.mqtt.util.sparkplug; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.transport.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.AdaptorException; import org.thingsboard.server.gen.transport.mqtt.SparkplugBProto; import java.math.BigInteger; diff --git a/common/transport/mqtt/src/test/java/org/thingsboard/server/transport/mqtt/MqttTransportHandlerTest.java b/common/transport/mqtt/src/test/java/org/thingsboard/server/transport/mqtt/MqttTransportHandlerTest.java index 4892acb161..f2ee512760 100644 --- a/common/transport/mqtt/src/test/java/org/thingsboard/server/transport/mqtt/MqttTransportHandlerTest.java +++ b/common/transport/mqtt/src/test/java/org/thingsboard/server/transport/mqtt/MqttTransportHandlerTest.java @@ -16,8 +16,7 @@ package org.thingsboard.server.transport.mqtt; import io.netty.buffer.ByteBuf; -import io.netty.buffer.EmptyByteBuf; -import io.netty.buffer.PooledByteBufAllocator; +import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.mqtt.MqttConnectMessage; import io.netty.handler.codec.mqtt.MqttConnectPayload; @@ -34,8 +33,19 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; +import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration; +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; @@ -55,12 +65,14 @@ import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.willDoNothing; import static org.mockito.BDDMockito.willReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; @Slf4j @RunWith(MockitoJUnitRunner.class) @@ -81,9 +93,14 @@ public class MqttTransportHandlerTest { ExecutorService executor; MqttTransportHandler handler; + @Spy + TransportService transportService; + @Before public void setUp() throws Exception { + willReturn(MSG_QUEUE_LIMIT).given(context).getMessageQueueSizePerDeviceLimit(); + willReturn(transportService).given(context).getTransportService(); handler = spy(new MqttTransportHandler(context, sslHandler)); willReturn(IP_ADDR).given(handler).getAddress(any()); @@ -104,9 +121,17 @@ public class MqttTransportHandlerTest { } MqttPublishMessage getMqttPublishMessage() { + return getMqttPublishMessage("v1/gateway/telemetry"); + } + + MqttPublishMessage getDeviceMqttPublishMessage() { + return getMqttPublishMessage("v1/devices/me/telemetry"); + } + + MqttPublishMessage getMqttPublishMessage(String topicName) { MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH, true, MqttQoS.AT_LEAST_ONCE, false, 123); - MqttPublishVariableHeader variableHeader = new MqttPublishVariableHeader("v1/gateway/telemetry", packedId.incrementAndGet()); - ByteBuf payload = new EmptyByteBuf(new PooledByteBufAllocator()); + MqttPublishVariableHeader variableHeader = new MqttPublishVariableHeader(topicName, packedId.incrementAndGet()); + ByteBuf payload = Unpooled.wrappedBuffer("{\"testKey\":\"testValue\"}".getBytes()); return new MqttPublishMessage(mqttFixedHeader, variableHeader, payload); } @@ -205,4 +230,26 @@ public class MqttTransportHandlerTest { messages.forEach((msg) -> verify(handler, times(1)).processRegularSessionMsg(ctx, msg)); } + @Test + public void givenMqttMessage_whenDeviceProfileMqttTransport_thenTopicAddedToMetadata() { + MqttPublishMessage message = getDeviceMqttPublishMessage(); + when(context.getJsonMqttAdaptor()).thenReturn(new JsonMqttAdaptor()); + handler.deviceSessionCtx.setConnected(true); + DeviceProfile deviceProfile = new DeviceProfile(); + DeviceProfileData deviceProfileData = new DeviceProfileData(); + MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = new MqttDeviceProfileTransportConfiguration(); + mqttDeviceProfileTransportConfiguration.setTransportPayloadTypeConfiguration(new JsonTransportPayloadConfiguration()); + deviceProfileData.setTransportConfiguration(mqttDeviceProfileTransportConfiguration); + deviceProfile.setProfileData(deviceProfileData); + deviceProfile.setTransportType(DeviceTransportType.MQTT); + handler.deviceSessionCtx.setDeviceProfile(deviceProfile); + + handler.processRegularSessionMsg(ctx, message); + + TbMsgMetaData expectedMd = new TbMsgMetaData(); + expectedMd.putValue(DataConstants.MQTT_TOPIC, message.variableHeader().topicName()); + + verify(transportService, times(1)).process(any(), (TransportProtos.PostTelemetryMsg) any(), eq(expectedMd), any()); + } + } \ No newline at end of file diff --git a/common/transport/mqtt/src/test/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandlerTest.java b/common/transport/mqtt/src/test/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandlerTest.java index d3b65445c3..ae38645fba 100644 --- a/common/transport/mqtt/src/test/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandlerTest.java +++ b/common/transport/mqtt/src/test/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandlerTest.java @@ -15,47 +15,21 @@ */ package org.thingsboard.server.transport.mqtt.session; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.springframework.util.ConcurrentReferenceHashMap; -import java.util.WeakHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -import static org.awaitility.Awaitility.await; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.willCallRealMethod; import static org.mockito.Mockito.mock; public class GatewaySessionHandlerTest { @Test - public void givenWeakHashMap_WhenGC_thenMapIsEmpty() { - WeakHashMap map = new WeakHashMap<>(); - - String deviceName = new String("device"); //constants are static and doesn't affected by GC, so use new instead - map.put(deviceName, new ReentrantLock()); - assertTrue(map.containsKey(deviceName)); - - deviceName = null; - System.gc(); - - await().atMost(10, TimeUnit.SECONDS).until(() -> !map.containsKey("device")); - } - - @Test - public void givenConcurrentReferenceHashMap_WhenGC_thenMapIsEmpty() { + public void givenGatewaySessionHandler_WhenCreateWeakMap_thenConcurrentReferenceHashMapClass() { GatewaySessionHandler gsh = mock(GatewaySessionHandler.class); willCallRealMethod().given(gsh).createWeakMap(); - ConcurrentMap map = gsh.createWeakMap(); - map.put("device", new ReentrantLock()); - assertTrue(map.containsKey("device")); - - System.gc(); - - await().atMost(10, TimeUnit.SECONDS).until(() -> !map.containsKey("device")); + assertThat(gsh.createWeakMap()).isInstanceOf(ConcurrentReferenceHashMap.class); } -} \ No newline at end of file +} diff --git a/common/transport/pom.xml b/common/transport/pom.xml index 4fc43aa3ea..14509a8648 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml index 0a615cfd4f..2a3874510d 100644 --- a/common/transport/snmp/pom.xml +++ b/common/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.common - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT transport diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java index 5dcffbf62f..f3c6d8bc83 100644 --- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java +++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfigu import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.transport.DeviceUpdatedEvent; @@ -113,19 +114,25 @@ public class SnmpTransportContext extends TransportContext { SnmpDeviceProfileTransportConfiguration profileTransportConfiguration = (SnmpDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration(); SnmpDeviceTransportConfiguration deviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration(); - DeviceSessionContext deviceSessionContext; + DeviceSessionContext sessionContext; try { - deviceSessionContext = new DeviceSessionContext( - device, deviceProfile, credentials.getCredentialsId(), - profileTransportConfiguration, deviceTransportConfiguration, this - ); - registerSessionMsgListener(deviceSessionContext); + sessionContext = DeviceSessionContext.builder() + .tenantId(deviceProfile.getTenantId()) + .device(device) + .deviceProfile(deviceProfile) + .token(credentials.getCredentialsId()) + .profileTransportConfiguration(profileTransportConfiguration) + .deviceTransportConfiguration(deviceTransportConfiguration) + .snmpTransportContext(this) + .build(); + registerSessionMsgListener(sessionContext); } catch (Exception e) { log.error("Failed to establish session for SNMP device {}: {}", device.getId(), e.toString()); + transportService.errorEvent(device.getTenantId(), device.getId(), "sessionEstablishing", e); return; } - sessions.put(device.getId(), deviceSessionContext); - snmpTransportService.createQueryingTasks(deviceSessionContext); + sessions.put(device.getId(), sessionContext); + snmpTransportService.createQueryingTasks(sessionContext); log.info("Established SNMP device session for device {}", device.getId()); } @@ -148,14 +155,17 @@ public class SnmpTransportContext extends TransportContext { sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration); snmpTransportService.cancelQueryingTasks(sessionContext); snmpTransportService.createQueryingTasks(sessionContext); + transportService.lifecycleEvent(sessionContext.getTenantId(), sessionContext.getDeviceId(), ComponentLifecycleEvent.UPDATED, true, null); } else if (!newDeviceTransportConfiguration.equals(sessionContext.getDeviceTransportConfiguration())) { sessionContext.setDeviceTransportConfiguration(newDeviceTransportConfiguration); sessionContext.initializeTarget(newProfileTransportConfiguration, newDeviceTransportConfiguration); + transportService.lifecycleEvent(sessionContext.getTenantId(), sessionContext.getDeviceId(), ComponentLifecycleEvent.UPDATED, true, null); } else { log.trace("Configuration of the device {} was not updated", device); } } catch (Exception e) { log.error("Failed to update session for SNMP device {}: {}", sessionContext.getDeviceId(), e.getMessage()); + transportService.lifecycleEvent(sessionContext.getTenantId(), sessionContext.getDeviceId(), ComponentLifecycleEvent.UPDATED, false, e); destroyDeviceSession(sessionContext); } } @@ -168,43 +178,58 @@ public class SnmpTransportContext extends TransportContext { transportService.deregisterSession(sessionContext.getSessionInfo()); snmpTransportService.cancelQueryingTasks(sessionContext); sessions.remove(sessionContext.getDeviceId()); + transportService.lifecycleEvent(sessionContext.getTenantId(), sessionContext.getDeviceId(), ComponentLifecycleEvent.STOPPED, true, null); log.trace("Unregistered and removed session"); } - private void registerSessionMsgListener(DeviceSessionContext deviceSessionContext) { + private void registerSessionMsgListener(DeviceSessionContext sessionContext) { transportService.process(DeviceTransportType.SNMP, - TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceSessionContext.getToken()).build(), + TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(sessionContext.getToken()).build(), new TransportServiceCallback<>() { @Override public void onSuccess(ValidateDeviceCredentialsResponse msg) { if (msg.hasDeviceInfo()) { - SessionInfoProto sessionInfo = SessionInfoCreator.create( - msg, SnmpTransportContext.this, UUID.randomUUID() - ); - - transportService.registerAsyncSession(sessionInfo, deviceSessionContext); - transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), TransportServiceCallback.EMPTY); - transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), TransportServiceCallback.EMPTY); - - deviceSessionContext.setSessionInfo(sessionInfo); - deviceSessionContext.setDeviceInfo(msg.getDeviceInfo()); - deviceSessionContext.setConnected(true); + registerTransportSession(sessionContext, msg); + sessionContext.setSessionTimeoutHandler(() -> { + registerTransportSession(sessionContext, msg); + }); + transportService.lifecycleEvent(sessionContext.getTenantId(), sessionContext.getDeviceId(), ComponentLifecycleEvent.STARTED, true, null); } else { - log.warn("[{}] Failed to process device auth", deviceSessionContext.getDeviceId()); + log.warn("[{}] Failed to process device auth", sessionContext.getDeviceId()); } } @Override public void onError(Throwable e) { - log.warn("[{}] Failed to process device auth: {}", deviceSessionContext.getDeviceId(), e); + log.warn("[{}] Failed to process device auth: {}", sessionContext.getDeviceId(), e); + transportService.lifecycleEvent(sessionContext.getTenantId(), sessionContext.getDeviceId(), ComponentLifecycleEvent.STARTED, false, e); } }); } + private void registerTransportSession(DeviceSessionContext deviceSessionContext, ValidateDeviceCredentialsResponse msg) { + SessionInfoProto sessionInfo = SessionInfoCreator.create( + msg, SnmpTransportContext.this, UUID.randomUUID() + ); + log.debug("Registering transport session: {}", sessionInfo); + + transportService.registerAsyncSession(sessionInfo, deviceSessionContext); + transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder() + .setSessionType(TransportProtos.SessionType.ASYNC) + .build(), TransportServiceCallback.EMPTY); + transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder() + .setSessionType(TransportProtos.SessionType.ASYNC) + .build(), TransportServiceCallback.EMPTY); + + deviceSessionContext.setSessionInfo(sessionInfo); + deviceSessionContext.setDeviceInfo(msg.getDeviceInfo()); + deviceSessionContext.setConnected(true); + } + @EventListener(DeviceUpdatedEvent.class) public void onDeviceUpdatedOrCreated(DeviceUpdatedEvent deviceUpdatedEvent) { Device device = deviceUpdatedEvent.getDevice(); - log.trace("Got creating or updating device event for device {}", device); + log.debug("Got creating or updating device event for device {}", device); DeviceTransportType transportType = Optional.ofNullable(device.getDeviceData().getTransportConfiguration()) .map(DeviceTransportConfiguration::getType) .orElse(null); @@ -238,6 +263,7 @@ public class SnmpTransportContext extends TransportContext { } public void onDeviceProfileUpdated(DeviceProfile deviceProfile, DeviceSessionContext sessionContext) { + log.debug("Handling device profile {} update event for device {}", deviceProfile.getId(), sessionContext.getDeviceId()); updateDeviceSession(sessionContext, sessionContext.getDevice(), deviceProfile); } diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/PduService.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/PduService.java index b395672c00..1cfcfd2c85 100644 --- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/PduService.java +++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/PduService.java @@ -15,7 +15,9 @@ */ package org.thingsboard.server.transport.snmp.service; +import com.google.common.collect.Lists; import com.google.gson.JsonObject; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.snmp4j.PDU; import org.snmp4j.ScopedPDU; @@ -25,43 +27,61 @@ import org.snmp4j.smi.OID; import org.snmp4j.smi.OctetString; import org.snmp4j.smi.Variable; import org.snmp4j.smi.VariableBinding; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration; import org.thingsboard.server.common.data.kv.DataType; import org.thingsboard.server.common.data.transport.snmp.SnmpMapping; import org.thingsboard.server.common.data.transport.snmp.SnmpMethod; import org.thingsboard.server.common.data.transport.snmp.SnmpProtocolVersion; import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig; +import org.thingsboard.server.common.data.util.TypeCastUtil; import org.thingsboard.server.queue.util.TbSnmpTransportComponent; import org.thingsboard.server.transport.snmp.session.DeviceSessionContext; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; -import java.util.stream.IntStream; @TbSnmpTransportComponent @Service @Slf4j +@RequiredArgsConstructor public class PduService { - public PDU createPdu(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map values) { - PDU pdu = setUpPdu(sessionContext); - pdu.setType(communicationConfig.getMethod().getCode()); - pdu.addAll(communicationConfig.getAllMappings().stream() - .filter(mapping -> values.isEmpty() || values.containsKey(mapping.getKey())) - .map(mapping -> Optional.ofNullable(values.get(mapping.getKey())) - .map(value -> { - Variable variable = toSnmpVariable(value, mapping.getDataType()); - return new VariableBinding(new OID(mapping.getOid()), variable); - }) - .orElseGet(() -> new VariableBinding(new OID(mapping.getOid())))) - .collect(Collectors.toList())); + @Value("${transport.snmp.max_request_oids:100}") + private int maxRequestOids; + + @Value("${transport.snmp.response.ignore_type_cast_errors:false}") + private boolean ignoreTypeCastErrors; + + public List createPdus(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map values) { + List pdus = new ArrayList<>(); + List allMappings = communicationConfig.getAllMappings(); + + for (List mappings : Lists.partition(allMappings, maxRequestOids)) { + PDU pdu = setUpPdu(sessionContext); + pdu.setType(communicationConfig.getMethod().getCode()); + pdu.addAll(mappings.stream() + .filter(mapping -> values.isEmpty() || values.containsKey(mapping.getKey())) + .map(mapping -> Optional.ofNullable(values.get(mapping.getKey())) + .map(value -> { + Variable variable = toSnmpVariable(value, mapping.getDataType()); + return new VariableBinding(new OID(mapping.getOid()), variable); + }) + .orElseGet(() -> new VariableBinding(new OID(mapping.getOid())))) + .collect(Collectors.toList())); + if (pdu.size() > 0) { + pdus.add(pdu); + } + } - return pdu; + return pdus; } public PDU createSingleVariablePdu(DeviceSessionContext sessionContext, SnmpMethod snmpMethod, String oid, String value, DataType dataType) { @@ -116,8 +136,8 @@ public class PduService { } - public JsonObject processPdu(PDU pdu, List responseMappings) { - Map values = processPdu(pdu); + public JsonObject processPdus(List pdus, List responseMappings) { + Map values = processPdus(pdus); Map mappings = new HashMap<>(); if (responseMappings != null) { @@ -143,29 +163,40 @@ public class PduService { return data; } - public Map processPdu(PDU pdu) { - return IntStream.range(0, pdu.size()) - .mapToObj(pdu::get) + public Map processPdus(List pdus) { + return pdus.stream() + .flatMap(pdu -> pdu.getVariableBindings().stream()) .filter(Objects::nonNull) .filter(variableBinding -> !(variableBinding.getVariable() instanceof Null)) .collect(Collectors.toMap(VariableBinding::getOid, VariableBinding::toValueString)); } public void processValue(String key, DataType dataType, String value, JsonObject result) { - switch (dataType) { - case LONG: - result.addProperty(key, Long.parseLong(value)); - break; - case BOOLEAN: - result.addProperty(key, Boolean.parseBoolean(value)); - break; - case DOUBLE: - result.addProperty(key, Double.parseDouble(value)); - break; - case STRING: - case JSON: - default: - result.addProperty(key, value); + try { + switch (dataType) { + case STRING: + case JSON: + result.addProperty(key, value); + break; + case LONG: + case DOUBLE: + result.addProperty(key, TypeCastUtil.castToNumber(value).getValue()); + break; + case BOOLEAN: + if (StringUtils.equalsAnyIgnoreCase(value, "true", "false")) { + result.addProperty(key, Boolean.parseBoolean(value)); + } else { + throw new IllegalArgumentException("Can't parse '" + value + "' as boolean"); + } + break; + } + } catch (IllegalArgumentException e) { + if (ignoreTypeCastErrors) { + log.debug("Ignoring value '{}' for key '{}' because of data type mismatch ({} required)", value, key, dataType); + } else { + throw e; + } } } + } diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java index 6e68a0bd44..014fcf8d67 100644 --- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java +++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java @@ -17,10 +17,13 @@ package org.thingsboard.server.transport.snmp.service; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import lombok.Builder; import lombok.Data; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.snmp4j.CommandResponder; +import org.snmp4j.CommandResponderEvent; import org.snmp4j.PDU; import org.snmp4j.Snmp; import org.snmp4j.TransportMapping; @@ -29,10 +32,15 @@ import org.snmp4j.mp.MPv3; import org.snmp4j.security.SecurityModels; import org.snmp4j.security.SecurityProtocols; import org.snmp4j.security.USM; +import org.snmp4j.smi.Address; import org.snmp4j.smi.OctetString; +import org.snmp4j.smi.TcpAddress; +import org.snmp4j.smi.UdpAddress; import org.snmp4j.transport.DefaultTcpTransportMapping; import org.snmp4j.transport.DefaultUdpTransportMapping; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardThreadFactory; @@ -45,15 +53,16 @@ import org.thingsboard.server.common.data.transport.snmp.SnmpMethod; import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig; import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig; import org.thingsboard.server.common.transport.TransportService; -import org.thingsboard.server.common.transport.TransportServiceCallback; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.util.TbSnmpTransportComponent; +import org.thingsboard.server.transport.snmp.SnmpTransportContext; import org.thingsboard.server.transport.snmp.session.DeviceSessionContext; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumMap; @@ -71,9 +80,11 @@ import java.util.stream.Collectors; @Service @Slf4j @RequiredArgsConstructor -public class SnmpTransportService implements TbTransportService { +public class SnmpTransportService implements TbTransportService, CommandResponder { private final TransportService transportService; private final PduService pduService; + @Autowired @Lazy + private SnmpTransportContext transportContext; @Getter private Snmp snmp; @@ -83,6 +94,8 @@ public class SnmpTransportService implements TbTransportService { private final Map responseDataMappers = new EnumMap<>(SnmpCommunicationSpec.class); private final Map responseProcessors = new EnumMap<>(SnmpCommunicationSpec.class); + @Value("${transport.snmp.bind_port:1620}") + private Integer snmpBindPort; @Value("${transport.snmp.response_processing.parallelism_level}") private Integer responseProcessingParallelismLevel; @Value("${transport.snmp.underlying_protocol}") @@ -114,15 +127,16 @@ public class SnmpTransportService implements TbTransportService { TransportMapping transportMapping; switch (snmpUnderlyingProtocol) { case "udp": - transportMapping = new DefaultUdpTransportMapping(); + transportMapping = new DefaultUdpTransportMapping(new UdpAddress(snmpBindPort)); break; case "tcp": - transportMapping = new DefaultTcpTransportMapping(); + transportMapping = new DefaultTcpTransportMapping(new TcpAddress(snmpBindPort)); break; default: throw new IllegalArgumentException("Underlying protocol " + snmpUnderlyingProtocol + " for SNMP is not supported"); } snmp = new Snmp(transportMapping); + snmp.addNotificationListener(transportMapping, transportMapping.getListenAddress(), this); snmp.listen(); USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0); @@ -143,6 +157,7 @@ public class SnmpTransportService implements TbTransportService { } } catch (Exception e) { log.error("Failed to send SNMP request for device {}: {}", sessionContext.getDeviceId(), e.toString()); + transportService.errorEvent(sessionContext.getTenantId(), sessionContext.getDeviceId(), config.getSpec().getLabel(), e); } }, queryingFrequency, queryingFrequency, TimeUnit.MILLISECONDS); }) @@ -161,18 +176,24 @@ public class SnmpTransportService implements TbTransportService { } private void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map values) { - PDU request = pduService.createPdu(sessionContext, communicationConfig, values); - RequestInfo requestInfo = new RequestInfo(communicationConfig.getSpec(), communicationConfig.getAllMappings()); - sendRequest(sessionContext, request, requestInfo); + List request = pduService.createPdus(sessionContext, communicationConfig, values); + RequestContext requestContext = RequestContext.builder() + .communicationSpec(communicationConfig.getSpec()) + .method(communicationConfig.getMethod()) + .responseMappings(communicationConfig.getAllMappings()) + .requestSize(request.size()) + .build(); + sendRequest(sessionContext, request, requestContext); } - private void sendRequest(DeviceSessionContext sessionContext, PDU request, RequestInfo requestInfo) { - if (request.size() > 0) { - log.trace("Executing SNMP request for device {}. Variables bindings: {}", sessionContext.getDeviceId(), request.getVariableBindings()); + private void sendRequest(DeviceSessionContext sessionContext, List request, RequestContext requestContext) { + for (PDU pdu : request) { + log.debug("Executing SNMP request for device {} with {} variable bindings", sessionContext.getDeviceId(), pdu.size()); try { - snmp.send(request, sessionContext.getTarget(), requestInfo, sessionContext); + snmp.send(pdu, sessionContext.getTarget(), requestContext, sessionContext); } catch (IOException e) { log.error("Failed to send SNMP request to device {}: {}", sessionContext.getDeviceId(), e.toString()); + transportService.errorEvent(sessionContext.getTenantId(), sessionContext.getDeviceId(), requestContext.getCommunicationSpec().getLabel(), e); } } } @@ -215,51 +236,135 @@ public class SnmpTransportService implements TbTransportService { DataType dataType = snmpMapping.getDataType(); PDU request = pduService.createSingleVariablePdu(sessionContext, snmpMethod, oid, value, dataType); - RequestInfo requestInfo = new RequestInfo(toDeviceRpcRequestMsg.getRequestId(), communicationConfig.getSpec(), communicationConfig.getAllMappings()); - sendRequest(sessionContext, request, requestInfo); + RequestContext requestContext = RequestContext.builder() + .requestId(toDeviceRpcRequestMsg.getRequestId()) + .communicationSpec(communicationConfig.getSpec()) + .method(snmpMethod) + .responseMappings(communicationConfig.getAllMappings()) + .requestSize(1) + .build(); + sendRequest(sessionContext, List.of(request), requestContext); } public void processResponseEvent(DeviceSessionContext sessionContext, ResponseEvent event) { ((Snmp) event.getSource()).cancel(event.getRequest(), sessionContext); - + RequestContext requestContext = (RequestContext) event.getUserObject(); if (event.getError() != null) { log.warn("SNMP response error: {}", event.getError().toString()); + transportService.errorEvent(sessionContext.getTenantId(), sessionContext.getDeviceId(), requestContext.getCommunicationSpec().getLabel(), new RuntimeException(event.getError())); return; } - PDU response = event.getResponse(); - if (response == null) { - log.debug("No response from SNMP device {}, requestId: {}", sessionContext.getDeviceId(), event.getRequest().getRequestID()); + PDU responsePdu = event.getResponse(); + if (log.isTraceEnabled()) { + log.trace("Received PDU for device {}: {}", sessionContext.getDeviceId(), responsePdu); + } + + List response; + if (requestContext.getRequestSize() == 1) { + if (responsePdu == null) { + log.debug("No response from SNMP device {}, requestId: {}", sessionContext.getDeviceId(), event.getRequest().getRequestID()); + if (requestContext.getMethod() == SnmpMethod.GET) { + transportService.errorEvent(sessionContext.getTenantId(), sessionContext.getDeviceId(), requestContext.getCommunicationSpec().getLabel(), new RuntimeException("No response from device")); + } + return; + } + response = List.of(responsePdu); + } else { + List responseParts = requestContext.getResponseParts(); + responseParts.add(responsePdu); + if (responseParts.size() == requestContext.getRequestSize()) { + response = new ArrayList<>(); + for (PDU responsePart : responseParts) { + if (responsePart != null) { + response.add(responsePart); + } + } + log.debug("All response parts are collected for request to device {}", sessionContext.getDeviceId()); + } else { + log.trace("Awaiting other response parts for request to device {}", sessionContext.getDeviceId()); + return; + } + } + + responseProcessingExecutor.execute(() -> { + try { + processResponse(sessionContext, response, requestContext); + } catch (Exception e) { + transportService.errorEvent(sessionContext.getTenantId(), sessionContext.getDeviceId(), requestContext.getCommunicationSpec().getLabel(), e); + } + }); + } + + /* + * SNMP notifications handler + * + * TODO: add check for host uniqueness when saving device (for backward compatibility - only for the ones using from-device RPC requests) + * + * NOTE: SNMP TRAPs support won't work properly when there is more than one SNMP transport, + * due to load-balancing of requests from devices: session might not be on this instance + * */ + @Override + public void processPdu(CommandResponderEvent event) { + Address sourceAddress = event.getPeerAddress(); + DeviceSessionContext sessionContext = transportContext.getSessions().stream() + .filter(session -> session.getTarget().getAddress().equals(sourceAddress)) + .findFirst().orElse(null); + if (sessionContext == null) { + log.warn("SNMP TRAP processing failed: couldn't find device session for address {}", sourceAddress); return; } - RequestInfo requestInfo = (RequestInfo) event.getUserObject(); + try { + processIncomingTrap(sessionContext, event); + } catch (Throwable e) { + transportService.errorEvent(sessionContext.getTenantId(), sessionContext.getDeviceId(), + SnmpCommunicationSpec.TO_SERVER_RPC_REQUEST.getLabel(), e); + } + } + + private void processIncomingTrap(DeviceSessionContext sessionContext, CommandResponderEvent event) { + PDU pdu = event.getPDU(); + if (pdu == null) { + log.warn("Got empty trap from device {}", sessionContext.getDeviceId()); + throw new IllegalArgumentException("Received TRAP with no data"); + } + + log.debug("Processing SNMP trap from device {} (PDU: {}}", sessionContext.getDeviceId(), pdu); + SnmpCommunicationConfig communicationConfig = sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream() + .filter(config -> config.getSpec() == SnmpCommunicationSpec.TO_SERVER_RPC_REQUEST).findFirst() + .orElseThrow(() -> new IllegalArgumentException("No config found for to-server RPC requests")); + RequestContext requestContext = RequestContext.builder() + .communicationSpec(communicationConfig.getSpec()) + .responseMappings(communicationConfig.getAllMappings()) + .method(SnmpMethod.TRAP) + .build(); + responseProcessingExecutor.execute(() -> { - processResponse(sessionContext, response, requestInfo); + processResponse(sessionContext, List.of(pdu), requestContext); }); } - private void processResponse(DeviceSessionContext sessionContext, PDU response, RequestInfo requestInfo) { - ResponseProcessor responseProcessor = responseProcessors.get(requestInfo.getCommunicationSpec()); + private void processResponse(DeviceSessionContext sessionContext, List response, RequestContext requestContext) { + ResponseProcessor responseProcessor = responseProcessors.get(requestContext.getCommunicationSpec()); if (responseProcessor == null) return; - JsonObject responseData = responseDataMappers.get(requestInfo.getCommunicationSpec()).map(response, requestInfo); - - if (responseData.entrySet().isEmpty()) { - log.debug("No values is the SNMP response for device {}. Request id: {}", sessionContext.getDeviceId(), response.getRequestID()); - return; + JsonObject responseData = responseDataMappers.get(requestContext.getCommunicationSpec()).map(response, requestContext); + if (responseData.size() == 0) { + log.warn("No values in the SNMP response for device {}", sessionContext.getDeviceId()); + throw new IllegalArgumentException("No values in the response"); } - responseProcessor.process(responseData, requestInfo, sessionContext); + responseProcessor.process(responseData, requestContext, sessionContext); reportActivity(sessionContext.getSessionInfo()); } private void configureResponseDataMappers() { - responseDataMappers.put(SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST, (pdu, requestInfo) -> { + responseDataMappers.put(SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST, (pdus, requestContext) -> { JsonObject responseData = new JsonObject(); - pduService.processPdu(pdu).forEach((oid, value) -> { - requestInfo.getResponseMappings().stream() + pduService.processPdus(pdus).forEach((oid, value) -> { + requestContext.getResponseMappings().stream() .filter(snmpMapping -> snmpMapping.getOid().equals(oid.toDottedString())) .findFirst() .ifPresent(snmpMapping -> { @@ -269,8 +374,8 @@ public class SnmpTransportService implements TbTransportService { return responseData; }); - ResponseDataMapper defaultResponseDataMapper = (pdu, requestInfo) -> { - return pduService.processPdu(pdu, requestInfo.getResponseMappings()); + ResponseDataMapper defaultResponseDataMapper = (pdus, requestContext) -> { + return pduService.processPdus(pdus, requestContext.getResponseMappings()); }; Arrays.stream(SnmpCommunicationSpec.values()) .forEach(communicationSpec -> { @@ -279,34 +384,39 @@ public class SnmpTransportService implements TbTransportService { } private void configureResponseProcessors() { - responseProcessors.put(SnmpCommunicationSpec.TELEMETRY_QUERYING, (responseData, requestInfo, sessionContext) -> { + responseProcessors.put(SnmpCommunicationSpec.TELEMETRY_QUERYING, (responseData, requestContext, sessionContext) -> { TransportProtos.PostTelemetryMsg postTelemetryMsg = JsonConverter.convertToTelemetryProto(responseData); transportService.process(sessionContext.getSessionInfo(), postTelemetryMsg, null); log.debug("Posted telemetry for SNMP device {}: {}", sessionContext.getDeviceId(), responseData); }); - responseProcessors.put(SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING, (responseData, requestInfo, sessionContext) -> { + responseProcessors.put(SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING, (responseData, requestContext, sessionContext) -> { TransportProtos.PostAttributeMsg postAttributesMsg = JsonConverter.convertToAttributesProto(responseData); transportService.process(sessionContext.getSessionInfo(), postAttributesMsg, null); log.debug("Posted attributes for SNMP device {}: {}", sessionContext.getDeviceId(), responseData); }); - responseProcessors.put(SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST, (responseData, requestInfo, sessionContext) -> { + responseProcessors.put(SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST, (responseData, requestContext, sessionContext) -> { TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder() - .setRequestId(requestInfo.getRequestId()) + .setRequestId(requestContext.getRequestId()) .setPayload(JsonConverter.toJson(responseData)) .build(); transportService.process(sessionContext.getSessionInfo(), rpcResponseMsg, null); log.debug("Posted RPC response {} for device {}", responseData, sessionContext.getDeviceId()); }); + + responseProcessors.put(SnmpCommunicationSpec.TO_SERVER_RPC_REQUEST, (responseData, requestContext, sessionContext) -> { + TransportProtos.ToServerRpcRequestMsg toServerRpcRequestMsg = TransportProtos.ToServerRpcRequestMsg.newBuilder() + .setRequestId(0) + .setMethodName(requestContext.getMethod().name()) + .setParams(JsonConverter.toJson(responseData)) + .build(); + transportService.process(sessionContext.getSessionInfo(), toServerRpcRequestMsg, null); + }); } private void reportActivity(TransportProtos.SessionInfoProto sessionInfo) { - transportService.process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder() - .setAttributeSubscription(true) - .setRpcSubscription(true) - .setLastActivityTime(System.currentTimeMillis()) - .build(), TransportServiceCallback.EMPTY); + transportService.reportActivity(sessionInfo); } @@ -335,29 +445,34 @@ public class SnmpTransportService implements TbTransportService { } @Data - private static class RequestInfo { - private Integer requestId; - private SnmpCommunicationSpec communicationSpec; - private List responseMappings; + private static class RequestContext { + private final Integer requestId; + private final SnmpCommunicationSpec communicationSpec; + private final SnmpMethod method; + private final List responseMappings; - public RequestInfo(Integer requestId, SnmpCommunicationSpec communicationSpec, List responseMappings) { - this.requestId = requestId; - this.communicationSpec = communicationSpec; - this.responseMappings = responseMappings; - } + private final int requestSize; + private List responseParts; - public RequestInfo(SnmpCommunicationSpec communicationSpec, List responseMappings) { + @Builder + public RequestContext(Integer requestId, SnmpCommunicationSpec communicationSpec, SnmpMethod method, List responseMappings, int requestSize) { + this.requestId = requestId; this.communicationSpec = communicationSpec; + this.method = method; this.responseMappings = responseMappings; + this.requestSize = requestSize; + if (requestSize > 1) { + this.responseParts = Collections.synchronizedList(new ArrayList<>()); + } } } private interface ResponseDataMapper { - JsonObject map(PDU pdu, RequestInfo requestInfo); + JsonObject map(List pdus, RequestContext requestContext); } private interface ResponseProcessor { - void process(JsonObject responseData, RequestInfo requestInfo, DeviceSessionContext sessionContext); + void process(JsonObject responseData, RequestContext requestContext, DeviceSessionContext sessionContext); } } diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java index 2b7c452ca7..b95a11e8c2 100644 --- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java +++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionContext.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.transport.snmp.session; +import lombok.Builder; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -26,9 +27,12 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rpc.RpcStatus; +import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; import org.thingsboard.server.common.transport.SessionMsgListener; import org.thingsboard.server.common.transport.TransportServiceCallback; +import org.thingsboard.server.common.transport.service.DefaultTransportService; import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; @@ -57,17 +61,22 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S private SnmpDeviceTransportConfiguration deviceTransportConfiguration; @Getter private final Device device; + @Getter + private final TenantId tenantId; private final SnmpTransportContext snmpTransportContext; private final AtomicInteger msgIdSeq = new AtomicInteger(0); @Getter private boolean isActive = true; + @Setter + private Runnable sessionTimeoutHandler; @Getter private final List> queryingTasks = new LinkedList<>(); - public DeviceSessionContext(Device device, DeviceProfile deviceProfile, String token, + @Builder + public DeviceSessionContext(TenantId tenantId, Device device, DeviceProfile deviceProfile, String token, SnmpDeviceProfileTransportConfiguration profileTransportConfiguration, SnmpDeviceTransportConfiguration deviceTransportConfiguration, SnmpTransportContext snmpTransportContext) throws Exception { @@ -75,6 +84,7 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S super.setDeviceId(device.getId()); super.setDeviceProfile(deviceProfile); this.device = device; + this.tenantId = tenantId; this.token = token; this.snmpTransportContext = snmpTransportContext; @@ -131,19 +141,32 @@ public class DeviceSessionContext extends DeviceAwareSessionContext implements S @Override public void onAttributeUpdate(UUID sessionId, AttributeUpdateNotificationMsg attributeUpdateNotification) { log.trace("[{}] Received attributes update notification to device", sessionId); - snmpTransportContext.getSnmpTransportService().onAttributeUpdate(this, attributeUpdateNotification); + try { + snmpTransportContext.getSnmpTransportService().onAttributeUpdate(this, attributeUpdateNotification); + } catch (Exception e) { + snmpTransportContext.getTransportService().errorEvent(getTenantId(), getDeviceId(), SnmpCommunicationSpec.SHARED_ATTRIBUTES_SETTING.getLabel(), e); + } } @Override public void onRemoteSessionCloseCommand(UUID sessionId, SessionCloseNotificationProto sessionCloseNotification) { log.trace("[{}] Received the remote command to close the session: {}", sessionId, sessionCloseNotification.getMessage()); + if (sessionCloseNotification.getMessage().equals(DefaultTransportService.SESSION_EXPIRED_MESSAGE)) { + if (sessionTimeoutHandler != null) { + sessionTimeoutHandler.run(); + } + } } @Override public void onToDeviceRpcRequest(UUID sessionId, ToDeviceRpcRequestMsg toDeviceRequest) { log.trace("[{}] Received RPC command to device", sessionId); - snmpTransportContext.getSnmpTransportService().onToDeviceRpcRequest(this, toDeviceRequest); - snmpTransportContext.getTransportService().process(getSessionInfo(), toDeviceRequest, RpcStatus.DELIVERED, TransportServiceCallback.EMPTY); + try { + snmpTransportContext.getSnmpTransportService().onToDeviceRpcRequest(this, toDeviceRequest); + snmpTransportContext.getTransportService().process(getSessionInfo(), toDeviceRequest, RpcStatus.DELIVERED, TransportServiceCallback.EMPTY); + } catch (Exception e) { + snmpTransportContext.getTransportService().errorEvent(getTenantId(), getDeviceId(), SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST.getLabel(), e); + } } @Override diff --git a/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV2.java b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV2.java index 5fd7f3bed0..fa49b17f13 100644 --- a/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV2.java +++ b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpDeviceSimulatorV2.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.transport.snmp; -import org.snmp4j.CommandResponderEvent; import org.snmp4j.CommunityTarget; import org.snmp4j.PDU; import org.snmp4j.Snmp; @@ -35,7 +34,6 @@ import org.snmp4j.agent.mo.snmp.SnmpTargetMIB; import org.snmp4j.agent.mo.snmp.StorageType; import org.snmp4j.agent.mo.snmp.VacmMIB; import org.snmp4j.agent.security.MutableVACM; -import org.snmp4j.mp.MPv3; import org.snmp4j.mp.SnmpConstants; import org.snmp4j.security.SecurityLevel; import org.snmp4j.security.SecurityModel; @@ -53,27 +51,11 @@ import org.snmp4j.transport.TransportMappings; import java.io.File; import java.io.IOException; import java.util.Map; -import java.util.function.Consumer; import java.util.stream.Collectors; @SuppressWarnings("deprecation") public class SnmpDeviceSimulatorV2 extends BaseAgent { - public static class RequestProcessor extends CommandProcessor { - private final Consumer processor; - - public RequestProcessor(Consumer processor) { - super(new OctetString(MPv3.createLocalEngineID())); - this.processor = processor; - } - - @Override - public void processPdu(CommandResponderEvent event) { - processor.accept(event); - } - } - - private final Target target; private final Address address; private Snmp snmp; @@ -81,10 +63,7 @@ public class SnmpDeviceSimulatorV2 extends BaseAgent { private final String password; public SnmpDeviceSimulatorV2(int port, String password) throws IOException { - super(new File("conf.agent"), new File("bootCounter.agent"), new RequestProcessor(event -> { - System.out.println("aboba"); - ((Snmp) event.getSource()).cancel(event.getPDU(), event1 -> System.out.println("canceled")); - })); + super(new File("conf.agent"), new File("bootCounter.agent"), new CommandProcessor(new OctetString("12312"))); CommunityTarget target = new CommunityTarget(); target.setCommunity(new OctetString(password)); this.address = GenericAddress.parse("udp:0.0.0.0/" + port); diff --git a/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV2.java b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV2.java index bf371058aa..5367ef93cf 100644 --- a/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV2.java +++ b/common/transport/snmp/src/test/java/org/thingsboard/server/transport/snmp/SnmpTestV2.java @@ -16,34 +16,41 @@ package org.thingsboard.server.transport.snmp; import java.io.IOException; +import java.util.HashMap; import java.util.Map; import java.util.Scanner; public class SnmpTestV2 { - public static void main(String[] args) throws IOException { - SnmpDeviceSimulatorV2 device = new SnmpDeviceSimulatorV2(1610, "public"); - device.start(); - device.setUpMappings(Map.of( - ".1.3.6.1.2.1.1.1.50", "12", - ".1.3.6.1.2.1.2.1.52", "56", - ".1.3.6.1.2.1.3.1.54", "yes", - ".1.3.6.1.2.1.7.1.58", "" - )); + private static final Scanner scanner = new Scanner(System.in); + public static void main(String[] args) throws IOException { + SnmpDeviceSimulatorV2 client = new SnmpDeviceSimulatorV2(1610, "public"); -// while (true) { -// new Scanner(System.in).nextLine(); -// device.sendTrap("127.0.0.1", 1062, Map.of(".1.3.6.1.2.87.1.56", "12")); -// System.out.println("sent"); + client.start(); + Map mappings = new HashMap<>(); +// for (int i = 1; i <= 500; i++) { +// String oid = String.format(".1.3.6.1.2.1.%s.1.52", i); +// mappings.put(oid, "value_" + i); // } + mappings.put("1.3.6.1.2.1.266.1.52", "****"); -// Snmp snmp = new Snmp(device.transportMappings[0]); -// device.snmp.addCommandResponder(event -> { -// System.out.println(event); -// }); + client.setUpMappings(mappings); + inputTraps(client); + + scanner.nextLine(); + } - new Scanner(System.in).nextLine(); + private static void inputTraps(SnmpDeviceSimulatorV2 client) throws IOException { + while (true) { + String data = scanner.nextLine(); + if (!data.isEmpty()) { + client.sendTrap("127.0.0.1", 1620, Map.of( + "1.3.6.1.2.1.266.1.52", data + " (266)", + "1.3.6.1.2.1.267.1.52", data + " (267)" + )); + } + } } } diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index 6840a24ab2..42dc93c2e9 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java index cd61d42016..bc9710c0ac 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java @@ -17,7 +17,11 @@ package org.thingsboard.server.common.transport; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.rpc.RpcStatus; +import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.common.transport.service.SessionMetaData; @@ -94,7 +98,7 @@ public interface TransportService { void process(ValidateDeviceLwM2MCredentialsRequestMsg msg, TransportServiceCallback callback); - void process(GetOrCreateDeviceFromGatewayRequestMsg msg, + void process(TenantId tenantId, GetOrCreateDeviceFromGatewayRequestMsg msg, TransportServiceCallback callback); void process(ProvisionDeviceRequestMsg msg, @@ -109,8 +113,12 @@ public interface TransportService { void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TransportServiceCallback callback); + void process(SessionInfoProto sessionInfo, PostTelemetryMsg msg, TbMsgMetaData md, TransportServiceCallback callback); + void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TransportServiceCallback callback); + void process(SessionInfoProto sessionInfo, PostAttributeMsg msg, TbMsgMetaData md, TransportServiceCallback callback); + void process(SessionInfoProto sessionInfo, GetAttributeRequestMsg msg, TransportServiceCallback callback); void process(SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg msg, TransportServiceCallback callback); @@ -139,6 +147,10 @@ public interface TransportService { void reportActivity(SessionInfoProto sessionInfo); + void lifecycleEvent(TenantId tenantId, DeviceId deviceId, ComponentLifecycleEvent eventType, boolean success, Throwable error); + + void errorEvent(TenantId tenantId, DeviceId deviceId, String method, Throwable error); + void deregisterSession(SessionInfoProto sessionInfo); void log(SessionInfoProto sessionInfo, String msg); diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/AdaptorException.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/AdaptorException.java deleted file mode 100644 index 8b2908a4b5..0000000000 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/AdaptorException.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.transport.adaptor; - -public class AdaptorException extends Exception { - - private static final long serialVersionUID = 1L; - - public AdaptorException() { - super(); - } - - public AdaptorException(String cause) { - super(cause); - } - - public AdaptorException(Exception cause) { - super(cause); - } - - public AdaptorException(String message, Exception cause) { - super(message, cause); - } - -} diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java deleted file mode 100644 index 51e74d5097..0000000000 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java +++ /dev/null @@ -1,653 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.transport.adaptor; - -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSyntaxException; -import org.apache.commons.lang3.math.NumberUtils; -import org.thingsboard.server.common.data.DataConstants; -import org.thingsboard.server.common.data.StringUtils; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.kv.AttributeKvEntry; -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.JsonDataEntry; -import org.thingsboard.server.common.data.kv.KvEntry; -import org.thingsboard.server.common.data.kv.LongDataEntry; -import org.thingsboard.server.common.data.kv.StringDataEntry; -import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; -import org.thingsboard.server.gen.transport.TransportProtos.CredentialsType; -import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; -import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; -import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType; -import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; -import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ResponseStatus; -import org.thingsboard.server.gen.transport.TransportProtos.TsKvListProto; -import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; -import org.thingsboard.server.gen.transport.TransportProtos.ValidateBasicMqttCredRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.TreeMap; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -public class JsonConverter { - - private static final Gson GSON = new Gson(); - private static final JsonParser JSON_PARSER = new JsonParser(); - private static final String CAN_T_PARSE_VALUE = "Can't parse value: "; - private static final String DEVICE_PROPERTY = "device"; - - private static boolean isTypeCastEnabled = true; - - private static int maxStringValueLength = 0; - - public static PostTelemetryMsg convertToTelemetryProto(JsonElement jsonElement, long ts) throws JsonSyntaxException { - PostTelemetryMsg.Builder builder = PostTelemetryMsg.newBuilder(); - convertToTelemetry(jsonElement, ts, null, builder); - return builder.build(); - } - - public static PostTelemetryMsg convertToTelemetryProto(JsonElement jsonElement) throws JsonSyntaxException { - return convertToTelemetryProto(jsonElement, System.currentTimeMillis()); - } - - private static void convertToTelemetry(JsonElement jsonElement, long systemTs, Map> result, PostTelemetryMsg.Builder builder) { - if (jsonElement.isJsonObject()) { - parseObject(systemTs, result, builder, jsonElement.getAsJsonObject()); - } else if (jsonElement.isJsonArray()) { - jsonElement.getAsJsonArray().forEach(je -> { - if (je.isJsonObject()) { - parseObject(systemTs, result, builder, je.getAsJsonObject()); - } else { - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + je); - } - }); - } else { - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonElement); - } - } - - private static void parseObject(long systemTs, Map> result, PostTelemetryMsg.Builder builder, JsonObject jo) { - if (result != null) { - parseObject(result, systemTs, jo); - } else { - parseObject(builder, systemTs, jo); - } - } - - public static ClaimDeviceMsg convertToClaimDeviceProto(DeviceId deviceId, String json) { - long durationMs = 0L; - if (json != null && !json.isEmpty()) { - return convertToClaimDeviceProto(deviceId, JSON_PARSER.parse(json)); - } - return buildClaimDeviceMsg(deviceId, DataConstants.DEFAULT_SECRET_KEY, durationMs); - } - - public static ClaimDeviceMsg convertToClaimDeviceProto(DeviceId deviceId, JsonElement jsonElement) { - String secretKey = DataConstants.DEFAULT_SECRET_KEY; - long durationMs = 0L; - if (jsonElement.isJsonObject()) { - JsonObject jo = jsonElement.getAsJsonObject(); - if (jo.has(DataConstants.SECRET_KEY_FIELD_NAME)) { - secretKey = jo.get(DataConstants.SECRET_KEY_FIELD_NAME).getAsString(); - } - if (jo.has(DataConstants.DURATION_MS_FIELD_NAME)) { - durationMs = jo.get(DataConstants.DURATION_MS_FIELD_NAME).getAsLong(); - } - } else { - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonElement); - } - return buildClaimDeviceMsg(deviceId, secretKey, durationMs); - } - - private static ClaimDeviceMsg buildClaimDeviceMsg(DeviceId deviceId, String secretKey, long durationMs) { - ClaimDeviceMsg.Builder result = ClaimDeviceMsg.newBuilder(); - return result - .setDeviceIdMSB(deviceId.getId().getMostSignificantBits()) - .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits()) - .setSecretKey(secretKey) - .setDurationMs(durationMs) - .build(); - } - - public static PostAttributeMsg convertToAttributesProto(JsonElement jsonObject) throws JsonSyntaxException { - if (jsonObject.isJsonObject()) { - PostAttributeMsg.Builder result = PostAttributeMsg.newBuilder(); - List keyValueList = parseProtoValues(jsonObject.getAsJsonObject()); - result.addAllKv(keyValueList); - return result.build(); - } else { - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonObject); - } - } - - public static JsonElement toJson(TransportProtos.ToDeviceRpcRequestMsg msg, boolean includeRequestId) { - JsonObject result = new JsonObject(); - if (includeRequestId) { - result.addProperty("id", msg.getRequestId()); - } - result.addProperty("method", msg.getMethodName()); - result.add("params", JSON_PARSER.parse(msg.getParams())); - return result; - } - - private static void parseObject(PostTelemetryMsg.Builder builder, long systemTs, JsonObject jo) { - if (jo.has("ts") && jo.has("values")) { - parseWithTs(builder, jo); - } else { - parseWithoutTs(builder, systemTs, jo); - } - } - - private static void parseWithoutTs(PostTelemetryMsg.Builder request, long systemTs, JsonObject jo) { - TsKvListProto.Builder builder = TsKvListProto.newBuilder(); - builder.setTs(systemTs); - builder.addAllKv(parseProtoValues(jo)); - request.addTsKvList(builder.build()); - } - - private static void parseWithTs(PostTelemetryMsg.Builder request, JsonObject jo) { - TsKvListProto.Builder builder = TsKvListProto.newBuilder(); - builder.setTs(jo.get("ts").getAsLong()); - builder.addAllKv(parseProtoValues(jo.get("values").getAsJsonObject())); - request.addTsKvList(builder.build()); - } - - private static List parseProtoValues(JsonObject valuesObject) { - List result = new ArrayList<>(); - for (Entry valueEntry : valuesObject.entrySet()) { - JsonElement element = valueEntry.getValue(); - if (element.isJsonPrimitive()) { - JsonPrimitive value = element.getAsJsonPrimitive(); - if (value.isString()) { - if (maxStringValueLength > 0 && value.getAsString().length() > maxStringValueLength) { - String message = String.format("String value length [%d] for key [%s] is greater than maximum allowed [%d]", value.getAsString().length(), valueEntry.getKey(), maxStringValueLength); - throw new JsonSyntaxException(message); - } - if (isTypeCastEnabled && NumberUtils.isParsable(value.getAsString())) { - try { - result.add(buildNumericKeyValueProto(value, valueEntry.getKey())); - } catch (RuntimeException th) { - result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.STRING_V) - .setStringV(value.getAsString()).build()); - } - } else { - result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.STRING_V) - .setStringV(value.getAsString()).build()); - } - } else if (value.isBoolean()) { - result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.BOOLEAN_V) - .setBoolV(value.getAsBoolean()).build()); - } else if (value.isNumber()) { - result.add(buildNumericKeyValueProto(value, valueEntry.getKey())); - } else if (!value.isJsonNull()) { - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + value); - } - } else if (element.isJsonObject() || element.isJsonArray()) { - result.add(KeyValueProto.newBuilder().setKey(valueEntry.getKey()).setType(KeyValueType.JSON_V).setJsonV(element.toString()).build()); - } else if (!element.isJsonNull()) { - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + element); - } - } - return result; - } - - private static KeyValueProto buildNumericKeyValueProto(JsonPrimitive value, String key) { - String valueAsString = value.getAsString(); - KeyValueProto.Builder builder = KeyValueProto.newBuilder().setKey(key); - var bd = new BigDecimal(valueAsString); - if (bd.stripTrailingZeros().scale() <= 0 && !isSimpleDouble(valueAsString)) { - try { - return builder.setType(KeyValueType.LONG_V).setLongV(bd.longValueExact()).build(); - } catch (ArithmeticException e) { - if (isTypeCastEnabled) { - return builder.setType(KeyValueType.STRING_V).setStringV(bd.toPlainString()).build(); - } else { - throw new JsonSyntaxException("Big integer values are not supported!"); - } - } - } else { - if (bd.scale() <= 16) { - return builder.setType(KeyValueType.DOUBLE_V).setDoubleV(bd.doubleValue()).build(); - } else if (isTypeCastEnabled) { - return builder.setType(KeyValueType.STRING_V).setStringV(bd.toPlainString()).build(); - } else { - throw new JsonSyntaxException("Big integer values are not supported!"); - } - } - - } - - private static boolean isSimpleDouble(String valueAsString) { - return valueAsString.contains(".") && !valueAsString.contains("E") && !valueAsString.contains("e"); - } - - public static TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(JsonElement json, int requestId) throws JsonSyntaxException { - JsonObject object = json.getAsJsonObject(); - return TransportProtos.ToServerRpcRequestMsg.newBuilder().setRequestId(requestId).setMethodName(object.get("method").getAsString()).setParams(GSON.toJson(object.get("params"))).build(); - } - - private static void parseNumericValue(List result, Entry valueEntry, JsonPrimitive value) { - String valueAsString = value.getAsString(); - String key = valueEntry.getKey(); - var bd = new BigDecimal(valueAsString); - if (bd.stripTrailingZeros().scale() <= 0 && !isSimpleDouble(valueAsString)) { - try { - result.add(new LongDataEntry(key, bd.longValueExact())); - } catch (ArithmeticException e) { - if (isTypeCastEnabled) { - result.add(new StringDataEntry(key, bd.toPlainString())); - } else { - throw new JsonSyntaxException("Big integer values are not supported!"); - } - } - } else { - if (bd.scale() <= 16) { - result.add(new DoubleDataEntry(key, bd.doubleValue())); - } else if (isTypeCastEnabled) { - result.add(new StringDataEntry(key, bd.toPlainString())); - } else { - throw new JsonSyntaxException("Big integer values are not supported!"); - } - } - } - - public static JsonObject toJson(GetAttributeResponseMsg payload) { - JsonObject result = new JsonObject(); - if (payload.getClientAttributeListCount() > 0) { - JsonObject attrObject = new JsonObject(); - payload.getClientAttributeListList().forEach(addToObjectFromProto(attrObject)); - result.add("client", attrObject); - } - if (payload.getSharedAttributeListCount() > 0) { - JsonObject attrObject = new JsonObject(); - payload.getSharedAttributeListList().forEach(addToObjectFromProto(attrObject)); - result.add("shared", attrObject); - } - return result; - } - - public static JsonObject toJson(AttributeUpdateNotificationMsg payload) { - JsonObject result = new JsonObject(); - if (payload.getSharedUpdatedCount() > 0) { - payload.getSharedUpdatedList().forEach(addToObjectFromProto(result)); - } - if (payload.getSharedDeletedCount() > 0) { - JsonArray attrObject = new JsonArray(); - payload.getSharedDeletedList().forEach(attrObject::add); - result.add("deleted", attrObject); - } - return result; - } - - public static JsonObject getJsonObjectForGateway( - String deviceName, - TransportProtos.GetAttributeResponseMsg responseMsg - ) { - JsonObject result = new JsonObject(); - result.addProperty("id", responseMsg.getRequestId()); - result.addProperty(DEVICE_PROPERTY, deviceName); - if (responseMsg.getClientAttributeListCount() > 0) { - addValues(result, responseMsg.getClientAttributeListList(), responseMsg.getIsMultipleAttributesRequest()); - } - if (responseMsg.getSharedAttributeListCount() > 0) { - addValues(result, responseMsg.getSharedAttributeListList(), responseMsg.getIsMultipleAttributesRequest()); - } - return result; - } - - public static JsonObject getJsonObjectForGateway(String deviceName, AttributeUpdateNotificationMsg - notificationMsg) { - JsonObject result = new JsonObject(); - result.addProperty(DEVICE_PROPERTY, deviceName); - result.add("data", toJson(notificationMsg)); - return result; - } - - private static void addValues(JsonObject result, List kvList, boolean multipleAttrKeysRequested) { - if (kvList.size() == 1 && !multipleAttrKeysRequested) { - addValueToJson(result, "value", kvList.get(0).getKv()); - } else { - JsonObject values; - if (result.has("values")) { - values = result.get("values").getAsJsonObject(); - } else { - values = new JsonObject(); - result.add("values", values); - } - kvList.forEach(value -> addValueToJson(values, value.getKv().getKey(), value.getKv())); - } - } - - private static void addValueToJson(JsonObject json, String name, TransportProtos.KeyValueProto entry) { - switch (entry.getType()) { - case BOOLEAN_V: - json.addProperty(name, entry.getBoolV()); - break; - case STRING_V: - json.addProperty(name, entry.getStringV()); - break; - case DOUBLE_V: - json.addProperty(name, entry.getDoubleV()); - break; - case LONG_V: - json.addProperty(name, entry.getLongV()); - break; - case JSON_V: - json.add(name, JSON_PARSER.parse(entry.getJsonV())); - break; - } - } - - private static Consumer addToObjectFromProto(JsonObject result) { - return de -> { - switch (de.getKv().getType()) { - case BOOLEAN_V: - result.add(de.getKv().getKey(), new JsonPrimitive(de.getKv().getBoolV())); - break; - case DOUBLE_V: - result.add(de.getKv().getKey(), new JsonPrimitive(de.getKv().getDoubleV())); - break; - case LONG_V: - result.add(de.getKv().getKey(), new JsonPrimitive(de.getKv().getLongV())); - break; - case STRING_V: - result.add(de.getKv().getKey(), new JsonPrimitive(de.getKv().getStringV())); - break; - case JSON_V: - result.add(de.getKv().getKey(), JSON_PARSER.parse(de.getKv().getJsonV())); - break; - default: - throw new IllegalArgumentException("Unsupported data type: " + de.getKv().getType()); - } - }; - } - - private static Consumer addToObject(JsonObject result) { - return de -> { - switch (de.getDataType()) { - case BOOLEAN: - result.add(de.getKey(), new JsonPrimitive(de.getBooleanValue().get())); - break; - case DOUBLE: - result.add(de.getKey(), new JsonPrimitive(de.getDoubleValue().get())); - break; - case LONG: - result.add(de.getKey(), new JsonPrimitive(de.getLongValue().get())); - break; - case STRING: - result.add(de.getKey(), new JsonPrimitive(de.getStrValue().get())); - break; - case JSON: - result.add(de.getKey(), JSON_PARSER.parse(de.getJsonValue().get())); - break; - default: - throw new IllegalArgumentException("Unsupported data type: " + de.getDataType()); - } - }; - } - - public static JsonElement toJson(TransportProtos.ToServerRpcResponseMsg msg) { - if (StringUtils.isEmpty(msg.getError())) { - return JSON_PARSER.parse(msg.getPayload()); - } else { - JsonObject errorMsg = new JsonObject(); - errorMsg.addProperty("error", msg.getError()); - return errorMsg; - } - } - - public static JsonObject toJson(ProvisionDeviceResponseMsg payload) { - return toJson(payload, false, 0); - } - - public static JsonObject toJson(ProvisionDeviceResponseMsg payload, int requestId) { - return toJson(payload, true, requestId); - } - - private static JsonObject toJson(ProvisionDeviceResponseMsg payload, boolean toGateway, int requestId) { - JsonObject result = new JsonObject(); - if (payload.getStatus() == ResponseStatus.NOT_FOUND) { - result.addProperty("errorMsg", "Provision data was not found!"); - result.addProperty("status", ResponseStatus.NOT_FOUND.name()); - } else if (payload.getStatus() == TransportProtos.ResponseStatus.FAILURE) { - result.addProperty("errorMsg", "Failed to provision device!"); - result.addProperty("status", ResponseStatus.FAILURE.name()); - } else { - if (toGateway) { - result.addProperty("id", requestId); - } - switch (payload.getCredentialsType()) { - case ACCESS_TOKEN: - case X509_CERTIFICATE: - result.addProperty("credentialsValue", payload.getCredentialsValue()); - break; - case MQTT_BASIC: - result.add("credentialsValue", JSON_PARSER.parse(payload.getCredentialsValue()).getAsJsonObject()); - break; - case LWM2M_CREDENTIALS: - break; - } - result.addProperty("credentialsType", payload.getCredentialsType().name()); - result.addProperty("status", ResponseStatus.SUCCESS.name()); - } - return result; - } - - public static JsonElement toErrorJson(String errorMsg) { - JsonObject error = new JsonObject(); - error.addProperty("error", errorMsg); - return error; - } - - public static JsonElement toGatewayJson(String deviceName, TransportProtos.ToDeviceRpcRequestMsg rpcRequest) { - JsonObject result = new JsonObject(); - result.addProperty(DEVICE_PROPERTY, deviceName); - result.add("data", JsonConverter.toJson(rpcRequest, true)); - return result; - } - - public static JsonElement toGatewayJson(String deviceName, TransportProtos.ProvisionDeviceResponseMsg - responseRequest) { - JsonObject result = new JsonObject(); - result.addProperty(DEVICE_PROPERTY, deviceName); - result.add("data", JsonConverter.toJson(responseRequest)); - return result; - } - - public static Set convertToAttributes(JsonElement element) { - Set result = new HashSet<>(); - long ts = System.currentTimeMillis(); - result.addAll(parseValues(element.getAsJsonObject()).stream().map(kv -> new BaseAttributeKvEntry(kv, ts)).collect(Collectors.toList())); - return result; - } - - private static List parseValues(JsonObject valuesObject) { - List result = new ArrayList<>(); - for (Entry valueEntry : valuesObject.entrySet()) { - JsonElement element = valueEntry.getValue(); - if (element.isJsonPrimitive()) { - JsonPrimitive value = element.getAsJsonPrimitive(); - if (value.isString()) { - if (maxStringValueLength > 0 && value.getAsString().length() > maxStringValueLength) { - String message = String.format("String value length [%d] for key [%s] is greater than maximum allowed [%d]", value.getAsString().length(), valueEntry.getKey(), maxStringValueLength); - throw new JsonSyntaxException(message); - } - if (isTypeCastEnabled && NumberUtils.isParsable(value.getAsString())) { - try { - parseNumericValue(result, valueEntry, value); - } catch (RuntimeException th) { - result.add(new StringDataEntry(valueEntry.getKey(), value.getAsString())); - } - } else { - result.add(new StringDataEntry(valueEntry.getKey(), value.getAsString())); - } - } else if (value.isBoolean()) { - result.add(new BooleanDataEntry(valueEntry.getKey(), value.getAsBoolean())); - } else if (value.isNumber()) { - parseNumericValue(result, valueEntry, value); - } else { - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + value); - } - } else if (element.isJsonObject() || element.isJsonArray()) { - result.add(new JsonDataEntry(valueEntry.getKey(), element.toString())); - } else { - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + element); - } - } - return result; - } - - public static Map> convertToTelemetry(JsonElement jsonElement, long systemTs) throws - JsonSyntaxException { - return convertToTelemetry(jsonElement, systemTs, false); - } - - public static Map> convertToSortedTelemetry(JsonElement jsonElement, long systemTs) throws - JsonSyntaxException { - return convertToTelemetry(jsonElement, systemTs, true); - } - - public static Map> convertToTelemetry(JsonElement jsonElement, long systemTs, boolean sorted) throws - JsonSyntaxException { - Map> result = sorted ? new TreeMap<>() : new HashMap<>(); - convertToTelemetry(jsonElement, systemTs, result, null); - return result; - } - - - private static void parseObject(Map> result, long systemTs, JsonObject jo) { - if (jo.has("ts") && jo.has("values")) { - parseWithTs(result, jo); - } else { - parseWithoutTs(result, systemTs, jo); - } - } - - private static void parseWithoutTs(Map> result, long systemTs, JsonObject jo) { - for (KvEntry entry : parseValues(jo)) { - result.computeIfAbsent(systemTs, tmp -> new ArrayList<>()).add(entry); - } - } - - public static void parseWithTs(Map> result, JsonObject jo) { - long ts = jo.get("ts").getAsLong(); - JsonObject valuesObject = jo.get("values").getAsJsonObject(); - for (KvEntry entry : parseValues(valuesObject)) { - result.computeIfAbsent(ts, tmp -> new ArrayList<>()).add(entry); - } - } - - public static JsonElement parse(String json) { - return JSON_PARSER.parse(json); - } - - public static T parse(String json, Class clazz) { - return fromJson(parse(json), clazz); - } - - public static String toJson(JsonElement element) { - return GSON.toJson(element); - } - - public static JsonObject toJsonObject(Object o) { - return (JsonObject) GSON.toJsonTree(o); - } - - public static T fromJson(JsonElement element, Class type) { - return GSON.fromJson(element, type); - } - - public static void setTypeCastEnabled(boolean enabled) { - isTypeCastEnabled = enabled; - } - - public static void setMaxStringValueLength(int length) { - maxStringValueLength = length; - } - - public static TransportProtos.ProvisionDeviceRequestMsg convertToProvisionRequestMsg(String json) { - JsonElement jsonElement = JSON_PARSER.parse(json); - if (jsonElement.isJsonObject()) { - return buildProvisionRequestMsg(jsonElement.getAsJsonObject()); - } else { - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonElement); - } - } - - public static TransportProtos.ProvisionDeviceRequestMsg convertToProvisionRequestMsg(JsonObject jo) { - return buildProvisionRequestMsg(jo); - } - - private static TransportProtos.ProvisionDeviceRequestMsg buildProvisionRequestMsg(JsonObject jo) { - return TransportProtos.ProvisionDeviceRequestMsg.newBuilder() - .setDeviceName(getStrValue(jo, DataConstants.DEVICE_NAME, false)) - .setCredentialsType(jo.get(DataConstants.CREDENTIALS_TYPE) != null ? TransportProtos.CredentialsType.valueOf(getStrValue(jo, DataConstants.CREDENTIALS_TYPE, false)) : CredentialsType.ACCESS_TOKEN) - .setCredentialsDataProto(TransportProtos.CredentialsDataProto.newBuilder() - .setValidateDeviceTokenRequestMsg(ValidateDeviceTokenRequestMsg.newBuilder().setToken(getStrValue(jo, DataConstants.TOKEN, false)).build()) - .setValidateBasicMqttCredRequestMsg(ValidateBasicMqttCredRequestMsg.newBuilder() - .setClientId(getStrValue(jo, DataConstants.CLIENT_ID, false)) - .setUserName(getStrValue(jo, DataConstants.USERNAME, false)) - .setPassword(getStrValue(jo, DataConstants.PASSWORD, false)) - .build()) - .setValidateDeviceX509CertRequestMsg(ValidateDeviceX509CertRequestMsg.newBuilder() - .setHash(getStrValue(jo, DataConstants.HASH, false)).build()) - .build()) - .setProvisionDeviceCredentialsMsg(buildProvisionDeviceCredentialsMsg( - getStrValue(jo, DataConstants.PROVISION_KEY, true), - getStrValue(jo, DataConstants.PROVISION_SECRET, true))) - .build(); - } - - private static TransportProtos.ProvisionDeviceCredentialsMsg buildProvisionDeviceCredentialsMsg(String provisionKey, String provisionSecret) { - return TransportProtos.ProvisionDeviceCredentialsMsg.newBuilder() - .setProvisionDeviceKey(provisionKey) - .setProvisionDeviceSecret(provisionSecret) - .build(); - } - - - private static String getStrValue(JsonObject jo, String field, boolean requiredField) { - if (jo.has(field)) { - return jo.get(field).getAsString(); - } else { - if (requiredField) { - throw new RuntimeException("Failed to find the field " + field + " in JSON body " + jo + "!"); - } - return ""; - } - } -} diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverterConfig.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverterConfig.java deleted file mode 100644 index 0a9df150e5..0000000000 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverterConfig.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.transport.adaptor; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Configuration; - -@Configuration -@Slf4j -public class JsonConverterConfig { - - @Value("${transport.json.type_cast_enabled:true}") - public void setIsJsonTypeCastEnabled(boolean jsonTypeCastEnabled) { - JsonConverter.setTypeCastEnabled(jsonTypeCastEnabled); - log.info("JSON type cast enabled = {}", jsonTypeCastEnabled); - } - - @Value("${transport.json.max_string_value_length:0}") - public void setMaxStringValueLength(int maxStringValueLength) { - JsonConverter.setMaxStringValueLength(maxStringValueLength); - log.info("JSON max string value length = {}", maxStringValueLength); - } -} diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/ProtoConverter.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/ProtoConverter.java deleted file mode 100644 index 54358709f0..0000000000 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/ProtoConverter.java +++ /dev/null @@ -1,207 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.transport.adaptor; - -import com.google.gson.Gson; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.protobuf.Descriptors; -import com.google.protobuf.DynamicMessage; -import com.google.protobuf.InvalidProtocolBufferException; -import lombok.extern.slf4j.Slf4j; -import org.springframework.util.CollectionUtils; -import org.thingsboard.server.common.data.DataConstants; -import org.thingsboard.server.common.data.DynamicProtoUtils; -import org.thingsboard.server.common.data.StringUtils; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.gen.transport.TransportApiProtos; -import org.thingsboard.server.gen.transport.TransportProtos; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -@Slf4j -public class ProtoConverter { - - public static final Gson GSON = new Gson(); - public static final JsonParser JSON_PARSER = new JsonParser(); - - public static TransportProtos.PostTelemetryMsg convertToTelemetryProto(byte[] payload) throws InvalidProtocolBufferException, IllegalArgumentException { - TransportProtos.TsKvListProto protoPayload = TransportProtos.TsKvListProto.parseFrom(payload); - TransportProtos.PostTelemetryMsg.Builder postTelemetryMsgBuilder = TransportProtos.PostTelemetryMsg.newBuilder(); - TransportProtos.TsKvListProto tsKvListProto = validateTsKvListProto(protoPayload); - postTelemetryMsgBuilder.addTsKvList(tsKvListProto); - return postTelemetryMsgBuilder.build(); - } - - public static TransportProtos.PostTelemetryMsg validatePostTelemetryMsg(byte[] payload) throws InvalidProtocolBufferException, IllegalArgumentException { - TransportProtos.PostTelemetryMsg msg = TransportProtos.PostTelemetryMsg.parseFrom(payload); - TransportProtos.PostTelemetryMsg.Builder postTelemetryMsgBuilder = TransportProtos.PostTelemetryMsg.newBuilder(); - List tsKvListProtoList = msg.getTsKvListList(); - if (!CollectionUtils.isEmpty(tsKvListProtoList)) { - List tsKvListProtos = new ArrayList<>(); - tsKvListProtoList.forEach(tsKvListProto -> { - TransportProtos.TsKvListProto transportTsKvListProto = validateTsKvListProto(tsKvListProto); - tsKvListProtos.add(transportTsKvListProto); - }); - postTelemetryMsgBuilder.addAllTsKvList(tsKvListProtos); - return postTelemetryMsgBuilder.build(); - } else { - throw new IllegalArgumentException("TsKv list is empty!"); - } - } - - public static TransportProtos.PostAttributeMsg validatePostAttributeMsg(TransportProtos.PostAttributeMsg msg) throws IllegalArgumentException, InvalidProtocolBufferException { - if (!CollectionUtils.isEmpty(msg.getKvList())) { - byte[] bytes = msg.toByteArray(); - TransportProtos.PostAttributeMsg proto = TransportProtos.PostAttributeMsg.parseFrom(bytes); - List kvList = proto.getKvList(); - List keyValueProtos = validateKeyValueProtos(kvList); - TransportProtos.PostAttributeMsg.Builder result = TransportProtos.PostAttributeMsg.newBuilder(); - result.addAllKv(keyValueProtos); - result.setShared(msg.getShared()); - return result.build(); - } else { - throw new IllegalArgumentException("KeyValue list is empty!"); - } - } - - public static TransportProtos.ClaimDeviceMsg convertToClaimDeviceProto(DeviceId deviceId, byte[] bytes) throws InvalidProtocolBufferException { - if (bytes == null) { - return buildClaimDeviceMsg(deviceId, DataConstants.DEFAULT_SECRET_KEY, 0); - } - TransportApiProtos.ClaimDevice proto = TransportApiProtos.ClaimDevice.parseFrom(bytes); - String secretKey = proto.getSecretKey() != null ? proto.getSecretKey() : DataConstants.DEFAULT_SECRET_KEY; - long durationMs = proto.getDurationMs(); - return buildClaimDeviceMsg(deviceId, secretKey, durationMs); - } - - public static TransportProtos.GetAttributeRequestMsg convertToGetAttributeRequestMessage(byte[] bytes, int requestId) throws InvalidProtocolBufferException, RuntimeException { - TransportApiProtos.AttributesRequest proto = TransportApiProtos.AttributesRequest.parseFrom(bytes); - TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder(); - result.setRequestId(requestId); - String clientKeys = proto.getClientKeys(); - String sharedKeys = proto.getSharedKeys(); - if (!StringUtils.isEmpty(clientKeys)) { - List clientKeysList = Arrays.asList(clientKeys.split(",")); - result.addAllClientAttributeNames(clientKeysList); - } - if (!StringUtils.isEmpty(sharedKeys)) { - List sharedKeysList = Arrays.asList(sharedKeys.split(",")); - result.addAllSharedAttributeNames(sharedKeysList); - } - return result.build(); - } - - public static TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(byte[] bytes, int requestId) throws InvalidProtocolBufferException { - TransportApiProtos.RpcRequest proto = TransportApiProtos.RpcRequest.parseFrom(bytes); - String method = proto.getMethod(); - String params = proto.getParams(); - return TransportProtos.ToServerRpcRequestMsg.newBuilder().setRequestId(requestId).setMethodName(method).setParams(params).build(); - } - - private static TransportProtos.ClaimDeviceMsg buildClaimDeviceMsg(DeviceId deviceId, String secretKey, long durationMs) { - TransportProtos.ClaimDeviceMsg.Builder result = TransportProtos.ClaimDeviceMsg.newBuilder(); - return result - .setDeviceIdMSB(deviceId.getId().getMostSignificantBits()) - .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits()) - .setSecretKey(secretKey) - .setDurationMs(durationMs) - .build(); - } - - private static TransportProtos.TsKvListProto validateTsKvListProto(TransportProtos.TsKvListProto tsKvListProto) { - TransportProtos.TsKvListProto.Builder tsKvListBuilder = TransportProtos.TsKvListProto.newBuilder(); - long ts = tsKvListProto.getTs(); - if (ts == 0) { - ts = System.currentTimeMillis(); - } - tsKvListBuilder.setTs(ts); - List kvList = tsKvListProto.getKvList(); - if (!CollectionUtils.isEmpty(kvList)) { - List keyValueListProtos = validateKeyValueProtos(kvList); - tsKvListBuilder.addAllKv(keyValueListProtos); - return tsKvListBuilder.build(); - } else { - throw new IllegalArgumentException("KeyValue list is empty!"); - } - } - - public static TransportProtos.ProvisionDeviceRequestMsg convertToProvisionRequestMsg(byte[] bytes) throws InvalidProtocolBufferException { - return TransportProtos.ProvisionDeviceRequestMsg.parseFrom(bytes); - } - - private static List validateKeyValueProtos(List kvList) { - kvList.forEach(keyValueProto -> { - String key = keyValueProto.getKey(); - if (StringUtils.isEmpty(key)) { - throw new IllegalArgumentException("Invalid key value: " + key + "!"); - } - TransportProtos.KeyValueType type = keyValueProto.getType(); - switch (type) { - case BOOLEAN_V: - case LONG_V: - case DOUBLE_V: - break; - case STRING_V: - if (StringUtils.isEmpty(keyValueProto.getStringV())) { - throw new IllegalArgumentException("Value is empty for key: " + key + "!"); - } - break; - case JSON_V: - try { - JSON_PARSER.parse(keyValueProto.getJsonV()); - } catch (Exception e) { - throw new IllegalArgumentException("Can't parse value: " + keyValueProto.getJsonV() + " for key: " + key + "!"); - } - break; - case UNRECOGNIZED: - throw new IllegalArgumentException("Unsupported keyValueType: " + type + "!"); - } - }); - return kvList; - } - - public static byte[] convertToRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg, DynamicMessage.Builder rpcRequestDynamicMessageBuilder) throws AdaptorException { - rpcRequestDynamicMessageBuilder = rpcRequestDynamicMessageBuilder.getDefaultInstanceForType().newBuilderForType(); - JsonObject rpcRequestJson = new JsonObject(); - rpcRequestJson.addProperty("method", toDeviceRpcRequestMsg.getMethodName()); - rpcRequestJson.addProperty("requestId", toDeviceRpcRequestMsg.getRequestId()); - String params = toDeviceRpcRequestMsg.getParams(); - try { - JsonElement paramsElement = JSON_PARSER.parse(params); - rpcRequestJson.add("params", paramsElement); - DynamicMessage dynamicRpcRequest = DynamicProtoUtils.jsonToDynamicMessage(rpcRequestDynamicMessageBuilder, GSON.toJson(rpcRequestJson)); - return dynamicRpcRequest.toByteArray(); - } catch (Exception e) { - throw new AdaptorException("Failed to convert ToDeviceRpcRequestMsg to Dynamic Rpc request message due to: ", e); - } - } - - public static Descriptors.Descriptor validateDescriptor(Descriptors.Descriptor descriptor) throws AdaptorException { - if (descriptor == null) { - throw new AdaptorException("Failed to get dynamic message descriptor!"); - } - return descriptor; - } - - public static String dynamicMsgToJson(byte[] bytes, Descriptors.Descriptor descriptor) throws InvalidProtocolBufferException { - return DynamicProtoUtils.dynamicMsgToJson(descriptor, bytes); - } - -} diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/DefaultEntityLimitsCache.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/DefaultEntityLimitsCache.java new file mode 100644 index 0000000000..3bfab1e3f0 --- /dev/null +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/DefaultEntityLimitsCache.java @@ -0,0 +1,74 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.transport.limits; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.Expiry; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.thingsboard.server.queue.util.TbTransportComponent; + +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +@Service +@TbTransportComponent +@Slf4j +public class DefaultEntityLimitsCache implements EntityLimitsCache { + + private static final int DEVIATION = 10; + private final Cache cache; + + public DefaultEntityLimitsCache(@Value("${cache.entityLimits.timeToLiveInMinutes:5}") int ttl, + @Value("${cache.entityLimits.maxSize:100000}") int maxSize) { + // We use the 'random' expiration time to avoid peak loads. + long mainPart = (TimeUnit.MINUTES.toNanos(ttl) / 100) * (100 - DEVIATION); + long randomPart = (TimeUnit.MINUTES.toNanos(ttl) / 100) * DEVIATION; + cache = Caffeine.newBuilder() + .expireAfter(new Expiry() { + @Override + public long expireAfterCreate(@NotNull EntityLimitKey key, @NotNull Boolean value, long currentTime) { + return mainPart + (long) (randomPart * ThreadLocalRandom.current().nextDouble()); + } + + @Override + public long expireAfterUpdate(@NotNull EntityLimitKey key, @NotNull Boolean value, long currentTime, long currentDuration) { + return currentDuration; + } + + @Override + public long expireAfterRead(@NotNull EntityLimitKey key, @NotNull Boolean value, long currentTime, long currentDuration) { + return currentDuration; + } + }) + .maximumSize(maxSize) + .build(); + } + + @Override + public boolean get(EntityLimitKey key) { + var result = cache.getIfPresent(key); + return result != null ? result : false; + } + + @Override + public void put(EntityLimitKey key, boolean value) { + cache.put(key, value); + } +} diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/DefaultTransportRateLimitService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/DefaultTransportRateLimitService.java index 6539f2d22c..47f7717212 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/DefaultTransportRateLimitService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/DefaultTransportRateLimitService.java @@ -78,11 +78,11 @@ public class DefaultTransportRateLimitService implements TransportRateLimitServi return null; } - private boolean checkEntityRateLimit(int dataPoints, EntityTransportRateLimits tenantLimits) { + private boolean checkEntityRateLimit(int dataPoints, EntityTransportRateLimits limits) { if (dataPoints > 0) { - return tenantLimits.getTelemetryMsgRateLimit().tryConsume() && tenantLimits.getTelemetryDataPointsRateLimit().tryConsume(dataPoints); + return limits.getTelemetryMsgRateLimit().tryConsume() && limits.getTelemetryDataPointsRateLimit().tryConsume(dataPoints); } else { - return tenantLimits.getRegularMsgRateLimit().tryConsume(); + return limits.getRegularMsgRateLimit().tryConsume(); } } @@ -241,7 +241,7 @@ public class DefaultTransportRateLimitService implements TransportRateLimitServi } else { TransportRateLimit regularMsgRateLimit = newLimit(tenant ? profile.getTransportTenantMsgRateLimit() : profile.getTransportDeviceMsgRateLimit()); TransportRateLimit telemetryMsgRateLimit = newLimit(tenant ? profile.getTransportTenantTelemetryMsgRateLimit() : profile.getTransportDeviceTelemetryMsgRateLimit()); - TransportRateLimit telemetryDpRateLimit = newLimit(tenant ? profile.getTransportTenantTelemetryDataPointsRateLimit() : profile.getTransportTenantTelemetryDataPointsRateLimit()); + TransportRateLimit telemetryDpRateLimit = newLimit(tenant ? profile.getTransportTenantTelemetryDataPointsRateLimit() : profile.getTransportDeviceTelemetryDataPointsRateLimit()); return new EntityTransportRateLimits(regularMsgRateLimit, telemetryMsgRateLimit, telemetryDpRateLimit); } } @@ -251,21 +251,16 @@ public class DefaultTransportRateLimitService implements TransportRateLimitServi } private EntityTransportRateLimits getTenantRateLimits(TenantId tenantId) { - EntityTransportRateLimits limits = perTenantLimits.get(tenantId); - if (limits == null) { - limits = createRateLimits(tenantProfileCache.get(tenantId), true); - perTenantLimits.put(tenantId, limits); - } - return limits; + return perTenantLimits.computeIfAbsent(tenantId, k -> { + return createRateLimits(tenantProfileCache.get(tenantId), true); + }); } private EntityTransportRateLimits getDeviceRateLimits(TenantId tenantId, DeviceId deviceId) { - EntityTransportRateLimits limits = perDeviceLimits.get(deviceId); - if (limits == null) { - limits = createRateLimits(tenantProfileCache.get(tenantId), false); - perDeviceLimits.put(deviceId, limits); + return perDeviceLimits.computeIfAbsent(deviceId, k -> { + EntityTransportRateLimits limits = createRateLimits(tenantProfileCache.get(tenantId), false); tenantDevices.computeIfAbsent(tenantId, id -> ConcurrentHashMap.newKeySet()).add(deviceId); - } - return limits; + return limits; + }); } } diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/EntityLimitKey.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/EntityLimitKey.java new file mode 100644 index 0000000000..f8957f780d --- /dev/null +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/EntityLimitKey.java @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.transport.limits; + +import lombok.Data; +import org.thingsboard.server.common.data.id.TenantId; + +@Data +public class EntityLimitKey { + + private final TenantId tenantId; + private final String deviceName; + +} diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/EntityLimitsCache.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/EntityLimitsCache.java new file mode 100644 index 0000000000..b350a2eb27 --- /dev/null +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/limits/EntityLimitsCache.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.transport.limits; + +public interface EntityLimitsCache { + + boolean get(EntityLimitKey key); + + void put(EntityLimitKey key, boolean value); + +} diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java index b495448d90..c4840b2402 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java @@ -22,6 +22,7 @@ import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.protobuf.ByteString; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationEventPublisher; @@ -48,12 +49,16 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantProfileId; +import org.thingsboard.server.common.data.limit.LimitedApi; +import org.thingsboard.server.common.data.notification.rule.trigger.RateLimitsTrigger; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.rpc.RpcStatus; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; -import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.common.msg.tools.TbRateLimitsException; import org.thingsboard.server.common.stats.MessagesStats; import org.thingsboard.server.common.stats.StatsFactory; @@ -71,6 +76,8 @@ import org.thingsboard.server.common.transport.TransportTenantProfileCache; import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; +import org.thingsboard.server.common.transport.limits.EntityLimitKey; +import org.thingsboard.server.common.transport.limits.EntityLimitsCache; import org.thingsboard.server.common.transport.limits.TransportRateLimitService; import org.thingsboard.server.common.transport.util.JsonUtils; import org.thingsboard.server.gen.transport.TransportProtos; @@ -89,7 +96,7 @@ import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.AsyncCallbackTemplate; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; @@ -156,6 +163,7 @@ public class DefaultTransportService implements TransportService { @Value("${transport.stats.enabled:false}") private boolean statsEnabled; + @Autowired @Lazy private TbApiUsageReportClient apiUsageClient; @@ -166,7 +174,7 @@ public class DefaultTransportService implements TransportService { private final TbTransportQueueFactory queueProvider; private final TbQueueProducerProvider producerProvider; - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final TbServiceInfoProvider serviceInfoProvider; private final StatsFactory statsFactory; private final TransportDeviceProfileCache deviceProfileCache; @@ -177,6 +185,9 @@ public class DefaultTransportService implements TransportService { private final SchedulerComponent scheduler; private final ApplicationEventPublisher eventPublisher; private final TransportResourceCache transportResourceCache; + private final NotificationRuleProcessor notificationRuleProcessor; + + private final EntityLimitsCache entityLimitsCache; protected TbQueueRequestTemplate, TbProtoQueueMsg> transportApiRequestTemplate; protected TbQueueProducer> ruleEngineMsgProducer; @@ -200,18 +211,19 @@ public class DefaultTransportService implements TransportService { TbServiceInfoProvider serviceInfoProvider, TbTransportQueueFactory queueProvider, TbQueueProducerProvider producerProvider, - NotificationsTopicService notificationsTopicService, + TopicService topicService, StatsFactory statsFactory, TransportDeviceProfileCache deviceProfileCache, TransportTenantProfileCache tenantProfileCache, TransportRateLimitService rateLimitService, DataDecodingEncodingService dataDecodingEncodingService, SchedulerComponent scheduler, TransportResourceCache transportResourceCache, - ApplicationEventPublisher eventPublisher) { + ApplicationEventPublisher eventPublisher, NotificationRuleProcessor notificationRuleProcessor, + EntityLimitsCache entityLimitsCache) { this.partitionService = partitionService; this.serviceInfoProvider = serviceInfoProvider; this.queueProvider = queueProvider; this.producerProvider = producerProvider; - this.notificationsTopicService = notificationsTopicService; + this.topicService = topicService; this.statsFactory = statsFactory; this.deviceProfileCache = deviceProfileCache; this.tenantProfileCache = tenantProfileCache; @@ -220,6 +232,8 @@ public class DefaultTransportService implements TransportService { this.scheduler = scheduler; this.transportResourceCache = transportResourceCache; this.eventPublisher = eventPublisher; + this.notificationRuleProcessor = notificationRuleProcessor; + this.entityLimitsCache = entityLimitsCache; } @PostConstruct @@ -235,14 +249,14 @@ public class DefaultTransportService implements TransportService { ruleEngineMsgProducer = producerProvider.getRuleEngineMsgProducer(); tbCoreMsgProducer = producerProvider.getTbCoreMsgProducer(); transportNotificationsConsumer = queueProvider.createTransportNotificationsConsumer(); - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, serviceInfoProvider.getServiceId()); + TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, serviceInfoProvider.getServiceId()); transportNotificationsConsumer.subscribe(Collections.singleton(tpi)); transportApiRequestTemplate.init(); mainConsumerExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("transport-consumer")); } @AfterStartUp(order = AfterStartUp.TRANSPORT_SERVICE) - private void start() { + public void start() { mainConsumerExecutor.execute(() -> { while (!stopped) { try { @@ -466,24 +480,33 @@ public class DefaultTransportService implements TransportService { AsyncCallbackTemplate.withCallback(response, callback::onSuccess, callback::onError, transportCallbackExecutor); } + @Override - public void process(TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg requestMsg, TransportServiceCallback callback) { + public void process(TenantId tenantId, TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg requestMsg, TransportServiceCallback callback) { TbProtoQueueMsg protoMsg = new TbProtoQueueMsg<>(UUID.randomUUID(), TransportApiRequestMsg.newBuilder().setGetOrCreateDeviceRequestMsg(requestMsg).build()); log.trace("Processing msg: {}", requestMsg); - ListenableFuture response = Futures.transform(transportApiRequestTemplate.send(protoMsg), tmp -> { - TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg msg = tmp.getValue().getGetOrCreateDeviceResponseMsg(); - GetOrCreateDeviceFromGatewayResponse.GetOrCreateDeviceFromGatewayResponseBuilder result = GetOrCreateDeviceFromGatewayResponse.builder(); - if (msg.hasDeviceInfo()) { - TransportDeviceInfo tdi = getTransportDeviceInfo(msg.getDeviceInfo()); - result.deviceInfo(tdi); - ByteString profileBody = msg.getProfileBody(); - if (profileBody != null && !profileBody.isEmpty()) { - result.deviceProfile(deviceProfileCache.getOrCreate(tdi.getDeviceProfileId(), profileBody)); + var key = new EntityLimitKey(tenantId, StringUtils.truncate(requestMsg.getDeviceName(), 256)); + if (entityLimitsCache.get(key)) { + transportCallbackExecutor.submit(() -> callback.onError(new RuntimeException(DataConstants.MAXIMUM_NUMBER_OF_DEVICES_REACHED))); + } else { + ListenableFuture response = Futures.transform(transportApiRequestTemplate.send(protoMsg), tmp -> { + TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg msg = tmp.getValue().getGetOrCreateDeviceResponseMsg(); + GetOrCreateDeviceFromGatewayResponse.GetOrCreateDeviceFromGatewayResponseBuilder result = GetOrCreateDeviceFromGatewayResponse.builder(); + if (msg.hasDeviceInfo()) { + TransportDeviceInfo tdi = getTransportDeviceInfo(msg.getDeviceInfo()); + result.deviceInfo(tdi); + ByteString profileBody = msg.getProfileBody(); + if (!profileBody.isEmpty()) { + result.deviceProfile(deviceProfileCache.getOrCreate(tdi.getDeviceProfileId(), profileBody)); + } + } else if (TransportProtos.TransportApiRequestErrorCode.ENTITY_LIMIT.equals(msg.getError())) { + entityLimitsCache.put(key, true); + throw new RuntimeException(DataConstants.MAXIMUM_NUMBER_OF_DEVICES_REACHED); } - } - return result.build(); - }, MoreExecutors.directExecutor()); - AsyncCallbackTemplate.withCallback(response, callback::onSuccess, callback::onError, transportCallbackExecutor); + return result.build(); + }, MoreExecutors.directExecutor()); + AsyncCallbackTemplate.withCallback(response, callback::onSuccess, callback::onError, transportCallbackExecutor); + } } @Override @@ -562,6 +585,11 @@ public class DefaultTransportService implements TransportService { @Override public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.PostTelemetryMsg msg, TransportServiceCallback callback) { + process(sessionInfo, msg, null, callback); + } + + @Override + public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.PostTelemetryMsg msg, TbMsgMetaData md, TransportServiceCallback callback) { int dataPoints = 0; for (TransportProtos.TsKvListProto tsKv : msg.getTsKvListList()) { dataPoints += tsKv.getKvCount(); @@ -573,24 +601,29 @@ public class DefaultTransportService implements TransportService { CustomerId customerId = getCustomerId(sessionInfo); MsgPackCallback packCallback = new MsgPackCallback(msg.getTsKvListCount(), new ApiStatsProxyCallback<>(tenantId, customerId, dataPoints, callback)); for (TransportProtos.TsKvListProto tsKv : msg.getTsKvListList()) { - TbMsgMetaData metaData = new TbMsgMetaData(); + TbMsgMetaData metaData = md != null ? md.copy() : new TbMsgMetaData(); metaData.putValue("deviceName", sessionInfo.getDeviceName()); metaData.putValue("deviceType", sessionInfo.getDeviceType()); metaData.putValue("ts", tsKv.getTs() + ""); JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList()); - sendToRuleEngine(tenantId, deviceId, customerId, sessionInfo, json, metaData, SessionMsgType.POST_TELEMETRY_REQUEST, packCallback); + sendToRuleEngine(tenantId, deviceId, customerId, sessionInfo, json, metaData, TbMsgType.POST_TELEMETRY_REQUEST, packCallback); } } } @Override public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.PostAttributeMsg msg, TransportServiceCallback callback) { + process(sessionInfo, msg, null, callback); + } + + @Override + public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.PostAttributeMsg msg, TbMsgMetaData md, TransportServiceCallback callback) { if (checkLimits(sessionInfo, msg, callback, msg.getKvCount())) { reportActivityInternal(sessionInfo); TenantId tenantId = getTenantId(sessionInfo); DeviceId deviceId = new DeviceId(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); - TbMsgMetaData metaData = new TbMsgMetaData(); + TbMsgMetaData metaData = md != null ? md.copy() : new TbMsgMetaData(); metaData.putValue("deviceName", sessionInfo.getDeviceName()); metaData.putValue("deviceType", sessionInfo.getDeviceType()); if (msg.getShared()) { @@ -598,7 +631,7 @@ public class DefaultTransportService implements TransportService { } metaData.putValue(DataConstants.NOTIFY_DEVICE_METADATA_KEY, "false"); CustomerId customerId = getCustomerId(sessionInfo); - sendToRuleEngine(tenantId, deviceId, customerId, sessionInfo, json, metaData, SessionMsgType.POST_ATTRIBUTES_REQUEST, + sendToRuleEngine(tenantId, deviceId, customerId, sessionInfo, json, metaData, TbMsgType.POST_ATTRIBUTES_REQUEST, new TransportTbQueueCallback(new ApiStatsProxyCallback<>(tenantId, customerId, msg.getKvList().size(), callback))); } } @@ -718,7 +751,7 @@ public class DefaultTransportService implements TransportService { metaData.putValue("serviceId", serviceInfoProvider.getServiceId()); metaData.putValue("sessionId", sessionId.toString()); sendToRuleEngine(tenantId, deviceId, getCustomerId(sessionInfo), sessionInfo, json, metaData, - SessionMsgType.TO_SERVER_RPC_REQUEST, new TransportTbQueueCallback(callback)); + TbMsgType.TO_SERVER_RPC_REQUEST, new TransportTbQueueCallback(callback)); String requestId = sessionId + "-" + msg.getRequestId(); toServerRpcPendingMap.put(requestId, new RpcRequestMetadata(sessionId, msg.getRequestId())); scheduler.schedule(() -> processTimeout(requestId), clientSideRpcTimeout, TimeUnit.MILLISECONDS); @@ -812,6 +845,37 @@ public class DefaultTransportService implements TransportService { sessionsToRemove.forEach(sessionsActivity::remove); } + @Override + public void lifecycleEvent(TenantId tenantId, DeviceId deviceId, ComponentLifecycleEvent eventType, boolean success, Throwable error) { + ToCoreMsg msg = ToCoreMsg.newBuilder() + .setLifecycleEventMsg(TransportProtos.LifecycleEventProto.newBuilder() + .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) + .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) + .setEntityIdMSB(deviceId.getId().getMostSignificantBits()) + .setEntityIdLSB(deviceId.getId().getLeastSignificantBits()) + .setServiceId(serviceInfoProvider.getServiceId()) + .setLcEventType(eventType.name()) + .setSuccess(success) + .setError(error != null ? ExceptionUtils.getStackTrace(error) : "")) + .build(); + sendToCore(tenantId, deviceId, msg, deviceId.getId(), TransportServiceCallback.EMPTY); + } + + @Override + public void errorEvent(TenantId tenantId, DeviceId deviceId, String method, Throwable error) { + ToCoreMsg msg = ToCoreMsg.newBuilder() + .setErrorEventMsg(TransportProtos.ErrorEventProto.newBuilder() + .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) + .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) + .setEntityIdMSB(deviceId.getId().getMostSignificantBits()) + .setEntityIdLSB(deviceId.getId().getLeastSignificantBits()) + .setServiceId(serviceInfoProvider.getServiceId()) + .setMethod(method) + .setError(ExceptionUtils.getStackTrace(error))) + .build(); + sendToCore(tenantId, deviceId, msg, deviceId.getId(), TransportServiceCallback.EMPTY); + } + @Override public SessionMetaData registerSyncSession(TransportProtos.SessionInfoProto sessionInfo, SessionMsgListener listener, long timeout) { SessionMetaData currentSession = new SessionMetaData(sessionInfo, TransportProtos.SessionType.SYNC, listener); @@ -877,6 +941,14 @@ public class DefaultTransportService implements TransportService { if (callback != null) { callback.onError(new TbRateLimitsException(rateLimitedEntityType)); } + if (rateLimitedEntityType == EntityType.DEVICE || rateLimitedEntityType == EntityType.TENANT) { + notificationRuleProcessor.process(RateLimitsTrigger.builder() + .tenantId(tenantId) + .api(rateLimitedEntityType == EntityType.DEVICE ? LimitedApi.TRANSPORT_MESSAGES_PER_DEVICE : LimitedApi.TRANSPORT_MESSAGES_PER_TENANT) + .limitLevel(rateLimitedEntityType == EntityType.DEVICE ? deviceId : tenantId) + .limitLevelEntityName(rateLimitedEntityType == EntityType.DEVICE ? sessionInfo.getDeviceName() : null) + .build()); + } return false; } } @@ -1095,18 +1167,21 @@ public class DefaultTransportService implements TransportService { } protected void sendToDeviceActor(TransportProtos.SessionInfoProto sessionInfo, TransportToDeviceActorMsg toDeviceActorMsg, TransportServiceCallback callback) { - TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, getTenantId(sessionInfo), getDeviceId(sessionInfo)); + ToCoreMsg toCoreMsg = ToCoreMsg.newBuilder().setToDeviceActorMsg(toDeviceActorMsg).build(); + sendToCore(getTenantId(sessionInfo), getDeviceId(sessionInfo), toCoreMsg, getRoutingKey(sessionInfo), callback); + } + + private void sendToCore(TenantId tenantId, EntityId entityId, ToCoreMsg msg, UUID routingKey, TransportServiceCallback callback) { + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); if (log.isTraceEnabled()) { - log.trace("[{}][{}] Pushing to topic {} message {}", getTenantId(sessionInfo), getDeviceId(sessionInfo), tpi.getFullTopicName(), toDeviceActorMsg); + log.trace("[{}][{}] Pushing to topic {} message {}", tenantId, entityId, tpi.getFullTopicName(), msg); } + TransportTbQueueCallback transportTbQueueCallback = callback != null ? new TransportTbQueueCallback(callback) : null; tbCoreProducerStats.incrementTotal(); StatsCallback wrappedCallback = new StatsCallback(transportTbQueueCallback, tbCoreProducerStats); - tbCoreMsgProducer.send(tpi, - new TbProtoQueueMsg<>(getRoutingKey(sessionInfo), - ToCoreMsg.newBuilder().setToDeviceActorMsg(toDeviceActorMsg).build()), - wrappedCallback); + tbCoreMsgProducer.send(tpi, new TbProtoQueueMsg<>(routingKey, msg), wrappedCallback); } private void sendToRuleEngine(TenantId tenantId, TbMsg tbMsg, TbQueueCallback callback) { @@ -1123,7 +1198,7 @@ public class DefaultTransportService implements TransportService { } private void sendToRuleEngine(TenantId tenantId, DeviceId deviceId, CustomerId customerId, TransportProtos.SessionInfoProto sessionInfo, JsonObject json, - TbMsgMetaData metaData, SessionMsgType sessionMsgType, TbQueueCallback callback) { + TbMsgMetaData metaData, TbMsgType tbMsgType, TbQueueCallback callback) { DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); DeviceProfile deviceProfile = deviceProfileCache.get(deviceProfileId); RuleChainId ruleChainId; @@ -1138,7 +1213,7 @@ public class DefaultTransportService implements TransportService { queueName = deviceProfile.getDefaultQueueName(); } - TbMsg tbMsg = TbMsg.newMsg(queueName, sessionMsgType.name(), deviceId, customerId, metaData, gson.toJson(json), ruleChainId, null); + TbMsg tbMsg = TbMsg.newMsg(queueName, tbMsgType, deviceId, customerId, metaData, gson.toJson(json), ruleChainId, null); sendToRuleEngine(tenantId, tbMsg, callback); } diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportTenantProfileCache.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportTenantProfileCache.java index d73e445516..12f89f29e6 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportTenantProfileCache.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportTenantProfileCache.java @@ -82,6 +82,7 @@ public class DefaultTransportTenantProfileCache implements TransportTenantProfil if (profileOpt.isPresent()) { TenantProfile newProfile = profileOpt.get(); log.trace("[{}] put: {}", newProfile.getId(), newProfile); + profiles.put(newProfile.getId(), newProfile); Set affectedTenants = tenantProfileIds.get(newProfile.getId()); return new TenantProfileUpdateResult(newProfile, affectedTenants != null ? affectedTenants : Collections.emptySet()); } else { diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportTenantRoutingInfoService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportTenantRoutingInfoService.java index c9f126b808..e1192391d5 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportTenantRoutingInfoService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/TransportTenantRoutingInfoService.java @@ -29,7 +29,7 @@ import org.thingsboard.server.queue.discovery.TenantRoutingInfoService; @ConditionalOnExpression("'${service.type:null}'=='tb-transport'") public class TransportTenantRoutingInfoService implements TenantRoutingInfoService { - private TransportTenantProfileCache tenantProfileCache; + private final TransportTenantProfileCache tenantProfileCache; public TransportTenantRoutingInfoService(TransportTenantProfileCache tenantProfileCache) { this.tenantProfileCache = tenantProfileCache; @@ -38,7 +38,7 @@ public class TransportTenantRoutingInfoService implements TenantRoutingInfoServi @Override public TenantRoutingInfo getRoutingInfo(TenantId tenantId) { TenantProfile profile = tenantProfileCache.get(tenantId); - return new TenantRoutingInfo(tenantId, profile.isIsolatedTbRuleEngine()); + return new TenantRoutingInfo(tenantId, profile.getId(), profile.isIsolatedTbRuleEngine()); } } diff --git a/common/transport/transport-api/src/test/java/JsonConverterTest.java b/common/transport/transport-api/src/test/java/JsonConverterTest.java deleted file mode 100644 index 39ed04a29a..0000000000 --- a/common/transport/transport-api/src/test/java/JsonConverterTest.java +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import com.google.gson.JsonParser; -import com.google.gson.JsonSyntaxException; -import org.junit.Assert; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; - -import java.util.ArrayList; - -public class JsonConverterTest { - - private final JsonParser JSON_PARSER = new JsonParser(); - - @BeforeEach - public void before() { - JsonConverter.setTypeCastEnabled(true); - } - - @Test - public void testParseBigDecimalAsLong() { - var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 1E+1}"), 0L); - Assert.assertEquals(10L, result.get(0L).get(0).getLongValue().get().longValue()); - } - - @Test - public void testParseBigDecimalAsDouble() { - var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 101E-1}"), 0L); - Assert.assertEquals(10.1, result.get(0L).get(0).getDoubleValue().get(), 0.0); - } - - @Test - public void testParseAttributesBigDecimalAsLong() { - var result = new ArrayList<>(JsonConverter.convertToAttributes(JSON_PARSER.parse("{\"meterReadingDelta\": 1E1}"))); - Assert.assertEquals(10L, result.get(0).getLongValue().get().longValue()); - } - - @Test - public void testParseAsDoubleWithZero() { - var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 42.0}"), 0L); - Assert.assertEquals(42.0, result.get(0L).get(0).getDoubleValue().get(), 0.0); - } - - @Test - public void testParseAsDouble() { - var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 1.1}"), 0L); - Assert.assertEquals(1.1, result.get(0L).get(0).getDoubleValue().get(), 0.0); - } - - @Test - public void testParseAsLong() { - var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 11}"), 0L); - Assert.assertEquals(11L, result.get(0L).get(0).getLongValue().get().longValue()); - } - - @Test - public void testParseBigDecimalAsStringOutOfLongRange() { - var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 9.9701010061400066E19}"), 0L); - Assert.assertEquals("99701010061400066000", result.get(0L).get(0).getStrValue().get()); - } - - @Test - public void testParseBigDecimalAsStringOutOfLongRange2() { - var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 99701010061400066001}"), 0L); - Assert.assertEquals("99701010061400066001", result.get(0L).get(0).getStrValue().get()); - } - - @Test - public void testParseBigDecimalAsStringOutOfLongRange3() { - var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 1E19}"), 0L); - Assert.assertEquals("10000000000000000000", result.get(0L).get(0).getStrValue().get()); - } - - @Test - public void testParseBigDecimalOutOfLongRangeWithoutParsing() { - JsonConverter.setTypeCastEnabled(false); - Assertions.assertThrows(JsonSyntaxException.class, () -> { - JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 89701010051400054084}"), 0L); - }); - } - - @Test - public void testParseBigDecimalOutOfLongRangeWithoutParsing2() { - JsonConverter.setTypeCastEnabled(false); - Assertions.assertThrows(JsonSyntaxException.class, () -> { - JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 9.9701010061400066E19}"), 0L); - }); - } -} diff --git a/common/util/pom.xml b/common/util/pom.xml index 8d7cad921e..eab8ae982b 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common @@ -36,6 +36,14 @@ + + org.bouncycastle + bcprov-jdk15on + + + org.bouncycastle + bcpkix-jdk15on + org.springframework spring-core diff --git a/common/util/src/main/java/org/thingsboard/common/util/AzureIotHubUtil.java b/common/util/src/main/java/org/thingsboard/common/util/AzureIotHubUtil.java index ca9cc2660f..b80427d0de 100644 --- a/common/util/src/main/java/org/thingsboard/common/util/AzureIotHubUtil.java +++ b/common/util/src/main/java/org/thingsboard/common/util/AzureIotHubUtil.java @@ -22,10 +22,12 @@ import javax.crypto.spec.SecretKeySpec; import java.io.IOException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Base64; +import java.util.Iterator; @Slf4j public final class AzureIotHubUtil { @@ -36,7 +38,7 @@ public final class AzureIotHubUtil { private static final String DATA_DIR = "data"; private static final String CERTS_DIR = "certs"; private static final String AZURE_DIR = "azure"; - private static final String FILE_NAME = "BaltimoreCyberTrustRoot.crt.pem"; + private static final String FILE_NAME = "DigiCertGlobalRootG2.crt.pem"; private static final Path FULL_FILE_PATH; @@ -88,12 +90,31 @@ public final class AzureIotHubUtil { } public static String getDefaultCaCert() { - try { - return new String(Files.readAllBytes(FULL_FILE_PATH)); - } catch (IOException e) { - log.error("Failed to load Default CaCert file!!! [{}]", FULL_FILE_PATH.toString()); - throw new RuntimeException("Failed to load Default CaCert file!!!"); + byte[] fileBytes; + if (Files.exists(FULL_FILE_PATH)) { + try { + fileBytes = Files.readAllBytes(FULL_FILE_PATH); + } catch (IOException e) { + log.error("Failed to load Default CaCert file!!! [{}]", FULL_FILE_PATH, e); + throw new RuntimeException("Failed to load Default CaCert file!!!"); + } + } else { + Path azureDirectory = FULL_FILE_PATH.getParent(); + try (DirectoryStream stream = Files.newDirectoryStream(azureDirectory)) { + Iterator iterator = stream.iterator(); + if (iterator.hasNext()) { + Path firstFile = iterator.next(); + fileBytes = Files.readAllBytes(firstFile); + } else { + log.error("Default CaCert file not found in the directory [{}]!!!", azureDirectory); + throw new RuntimeException("Default CaCert file not found in the directory!!!"); + } + } catch (IOException e) { + log.error("Failed to load Default CaCert file from the directory [{}]!!!", azureDirectory, e); + throw new RuntimeException("Failed to load Default CaCert file from the directory!!!"); + } } + return new String(fileBytes); } } diff --git a/common/util/src/main/java/org/thingsboard/common/util/ExceptionUtil.java b/common/util/src/main/java/org/thingsboard/common/util/ExceptionUtil.java new file mode 100644 index 0000000000..2ec15f3724 --- /dev/null +++ b/common/util/src/main/java/org/thingsboard/common/util/ExceptionUtil.java @@ -0,0 +1,67 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.common.util; + +import com.google.gson.JsonParseException; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.id.EntityId; + +import javax.script.ScriptException; +import java.io.PrintWriter; +import java.io.StringWriter; + +@Slf4j +public class ExceptionUtil { + + @SuppressWarnings("unchecked") + public static T lookupException(Throwable source, Class clazz) { + Exception e = lookupExceptionInCause(source, clazz); + if (e != null) { + return (T) e; + } else { + return null; + } + } + + public static Exception lookupExceptionInCause(Throwable source, Class... clazzes) { + while (source != null) { + for (Class clazz : clazzes) { + if (clazz.isAssignableFrom(source.getClass())) { + return (Exception) source; + } + } + source = source.getCause(); + } + return null; + } + + public static String toString(Exception e, EntityId componentId, boolean stackTraceEnabled) { + Exception exception = lookupExceptionInCause(e, ScriptException.class, JsonParseException.class); + if (exception != null && StringUtils.isNotEmpty(exception.getMessage())) { + return exception.getMessage(); + } else { + if (stackTraceEnabled) { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + return sw.toString(); + } else { + log.debug("[{}] Unknown error during message processing", componentId, e); + return "Please contact system administrator"; + } + } + } +} diff --git a/common/util/src/main/java/org/thingsboard/common/util/ExecutorProvider.java b/common/util/src/main/java/org/thingsboard/common/util/ExecutorProvider.java new file mode 100644 index 0000000000..61379c8c89 --- /dev/null +++ b/common/util/src/main/java/org/thingsboard/common/util/ExecutorProvider.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.common.util; + +import java.util.concurrent.ScheduledExecutorService; + +public interface ExecutorProvider { + + ScheduledExecutorService getExecutor(); +} diff --git a/common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java b/common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java index 0e4204fd95..1121085ac1 100644 --- a/common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java +++ b/common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.json.JsonWriteFeature; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.MapperFeature; @@ -28,6 +29,7 @@ import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.google.common.collect.Lists; import org.thingsboard.server.common.data.kv.DataType; import org.thingsboard.server.common.data.kv.KvEntry; @@ -35,7 +37,6 @@ import java.io.File; import java.io.IOException; import java.io.Reader; import java.io.Writer; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; @@ -44,6 +45,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.function.UnaryOperator; +import java.util.regex.Pattern; /** * Created by Valerii Sosliuk on 5/12/2017. @@ -60,6 +62,9 @@ public class JacksonUtil { .configure(JsonWriteFeature.QUOTE_FIELD_NAMES.mappedFeature(), false) .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true) .build(); + public static final ObjectMapper IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER = JsonMapper.builder() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .build(); public static ObjectMapper getObjectMapperWithJavaTimeModule() { return new ObjectMapper().registerModule(new JavaTimeModule()); @@ -69,8 +74,7 @@ public class JacksonUtil { try { return fromValue != null ? OBJECT_MAPPER.convertValue(fromValue, toValueType) : null; } catch (IllegalArgumentException e) { - throw new IllegalArgumentException("The given object value: " - + fromValue + " cannot be converted to " + toValueType, e); + throw new IllegalArgumentException("The given object value cannot be converted to " + toValueType + ": " + fromValue, e); } } @@ -78,8 +82,7 @@ public class JacksonUtil { try { return fromValue != null ? OBJECT_MAPPER.convertValue(fromValue, toValueTypeRef) : null; } catch (IllegalArgumentException e) { - throw new IllegalArgumentException("The given object value: " - + fromValue + " cannot be converted to " + toValueTypeRef, e); + throw new IllegalArgumentException("The given object value cannot be converted to " + toValueTypeRef + ": " + fromValue, e); } } @@ -87,8 +90,7 @@ public class JacksonUtil { try { return string != null ? OBJECT_MAPPER.readValue(string, clazz) : null; } catch (IOException e) { - throw new IllegalArgumentException("The given string value: " - + string + " cannot be transformed to Json object", e); + throw new IllegalArgumentException("The given string value cannot be transformed to Json object: " + string, e); } } @@ -96,8 +98,7 @@ public class JacksonUtil { try { return string != null ? OBJECT_MAPPER.readValue(string, valueTypeRef) : null; } catch (IOException e) { - throw new IllegalArgumentException("The given string value: " - + string + " cannot be transformed to Json object", e); + throw new IllegalArgumentException("The given string value cannot be transformed to Json object: " + string, e); } } @@ -105,8 +106,15 @@ public class JacksonUtil { try { return string != null ? OBJECT_MAPPER.readValue(string, javaType) : null; } catch (IOException e) { - throw new IllegalArgumentException("The given String value: " - + string + " cannot be transformed to Json object", e); + throw new IllegalArgumentException("The given String value cannot be transformed to Json object: " + string, e); + } + } + + public static T fromString(String string, Class clazz, boolean ignoreUnknownFields) { + try { + return string != null ? IGNORE_UNKNOWN_PROPERTIES_JSON_MAPPER.readValue(string, clazz) : null; + } catch (IOException e) { + throw new IllegalArgumentException("The given string value cannot be transformed to Json object: " + string, e); } } @@ -114,8 +122,7 @@ public class JacksonUtil { try { return bytes != null ? OBJECT_MAPPER.readValue(bytes, clazz) : null; } catch (IOException e) { - throw new IllegalArgumentException("The given string value: " - + Arrays.toString(bytes) + " cannot be transformed to Json object", e); + throw new IllegalArgumentException("The given string value cannot be transformed to Json object: " + Arrays.toString(bytes), e); } } @@ -123,8 +130,7 @@ public class JacksonUtil { try { return OBJECT_MAPPER.readTree(bytes); } catch (IOException e) { - throw new IllegalArgumentException("The given byte[] value: " - + Arrays.toString(bytes) + " cannot be transformed to Json object", e); + throw new IllegalArgumentException("The given byte[] value cannot be transformed to Json object: " + Arrays.toString(bytes), e); } } @@ -132,8 +138,7 @@ public class JacksonUtil { try { return value != null ? OBJECT_MAPPER.writeValueAsString(value) : null; } catch (JsonProcessingException e) { - throw new IllegalArgumentException("The given Json object value: " - + value + " cannot be transformed to a String", e); + throw new IllegalArgumentException("The given Json object value cannot be transformed to a String: " + value, e); } } @@ -207,8 +212,7 @@ public class JacksonUtil { try { return OBJECT_MAPPER.writeValueAsBytes(value); } catch (JsonProcessingException e) { - throw new IllegalArgumentException("The given Json object value: " - + value + " cannot be transformed to a String", e); + throw new IllegalArgumentException("The given Json object value cannot be transformed to a String: " + value, e); } } @@ -227,22 +231,24 @@ public class JacksonUtil { return node; } - public static void replaceUuidsRecursively(JsonNode node, Set skipFieldsSet, UnaryOperator replacer) { + public static void replaceUuidsRecursively(JsonNode node, Set skippedRootFields, Pattern includedFieldsPattern, UnaryOperator replacer, boolean root) { if (node == null) { return; } if (node.isObject()) { ObjectNode objectNode = (ObjectNode) node; - List fieldNames = new ArrayList<>(objectNode.size()); - objectNode.fieldNames().forEachRemaining(fieldNames::add); + List fieldNames = Lists.newArrayList(objectNode.fieldNames()); for (String fieldName : fieldNames) { - if (skipFieldsSet.contains(fieldName)) { + if (root && skippedRootFields.contains(fieldName)) { continue; } var child = objectNode.get(fieldName); if (child.isObject() || child.isArray()) { - replaceUuidsRecursively(child, skipFieldsSet, replacer); + replaceUuidsRecursively(child, skippedRootFields, includedFieldsPattern, replacer, false); } else if (child.isTextual()) { + if (includedFieldsPattern != null && !RegexUtils.matches(fieldName, includedFieldsPattern)) { + continue; + } String text = child.asText(); String newText = RegexUtils.replace(text, RegexUtils.UUID_PATTERN, uuid -> replacer.apply(UUID.fromString(uuid)).toString()); if (!text.equals(newText)) { @@ -255,7 +261,7 @@ public class JacksonUtil { for (int i = 0; i < array.size(); i++) { JsonNode arrayElement = array.get(i); if (arrayElement.isObject() || arrayElement.isArray()) { - replaceUuidsRecursively(arrayElement, skipFieldsSet, replacer); + replaceUuidsRecursively(arrayElement, skippedRootFields, includedFieldsPattern, replacer, false); } else if (arrayElement.isTextual()) { String text = arrayElement.asText(); String newText = RegexUtils.replace(text, RegexUtils.UUID_PATTERN, uuid -> replacer.apply(UUID.fromString(uuid)).toString()); diff --git a/common/util/src/main/java/org/thingsboard/common/util/RegexUtils.java b/common/util/src/main/java/org/thingsboard/common/util/RegexUtils.java index 87d1e11bab..bbeb86efca 100644 --- a/common/util/src/main/java/org/thingsboard/common/util/RegexUtils.java +++ b/common/util/src/main/java/org/thingsboard/common/util/RegexUtils.java @@ -19,6 +19,7 @@ import lombok.AccessLevel; import lombok.NoArgsConstructor; import java.util.function.UnaryOperator; +import java.util.regex.Matcher; import java.util.regex.Pattern; @NoArgsConstructor(access = AccessLevel.PRIVATE) @@ -37,4 +38,14 @@ public class RegexUtils { return pattern.matcher(input).matches(); } + public static String getMatch(String input, Pattern pattern, int group) { + Matcher matcher = pattern.matcher(input); + if (matcher.find()) { + try { + return matcher.group(group); + } catch (Exception ignored) {} + } + return null; + } + } diff --git a/common/util/src/main/java/org/thingsboard/common/util/SslUtil.java b/common/util/src/main/java/org/thingsboard/common/util/SslUtil.java new file mode 100644 index 0000000000..889436671a --- /dev/null +++ b/common/util/src/main/java/org/thingsboard/common/util/SslUtil.java @@ -0,0 +1,105 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.common.util; + +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMDecryptorProvider; +import org.bouncycastle.openssl.PEMEncryptedKeyPair; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; +import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder; +import org.bouncycastle.operator.InputDecryptorProvider; +import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo; +import org.bouncycastle.pkcs.jcajce.JcePKCSPBEInputDecryptorProviderBuilder; +import org.thingsboard.server.common.data.StringUtils; + +import java.io.StringReader; +import java.security.PrivateKey; +import java.security.Security; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.List; + +@Slf4j +public class SslUtil { + + public static final char[] EMPTY_PASS = {}; + + public static final BouncyCastleProvider DEFAULT_PROVIDER = new BouncyCastleProvider(); + + static { + if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { + Security.addProvider(DEFAULT_PROVIDER); + } + } + + private SslUtil() { + } + + @SneakyThrows + public static List readCertFile(String fileContent) { + List certificates = new ArrayList<>(); + JcaX509CertificateConverter certConverter = new JcaX509CertificateConverter(); + try (PEMParser pemParser = new PEMParser(new StringReader(fileContent))) { + Object object; + while ((object = pemParser.readObject()) != null) { + if (object instanceof X509CertificateHolder) { + X509Certificate x509Cert = certConverter.getCertificate((X509CertificateHolder) object); + certificates.add(x509Cert); + } + } + } + return certificates; + } + + @SneakyThrows + public static PrivateKey readPrivateKey(String fileContent, String passStr) { + char[] password = StringUtils.isEmpty(passStr) ? EMPTY_PASS : passStr.toCharArray(); + + PrivateKey privateKey = null; + JcaPEMKeyConverter keyConverter = new JcaPEMKeyConverter(); + if (StringUtils.isNotEmpty(fileContent)) { + try (PEMParser pemParser = new PEMParser(new StringReader(fileContent))) { + Object object; + while ((object = pemParser.readObject()) != null) { + if (object instanceof PEMEncryptedKeyPair) { + PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(password); + privateKey = keyConverter.getKeyPair(((PEMEncryptedKeyPair) object).decryptKeyPair(decProv)).getPrivate(); + break; + } else if (object instanceof PKCS8EncryptedPrivateKeyInfo) { + InputDecryptorProvider decProv = + new JcePKCSPBEInputDecryptorProviderBuilder().setProvider(DEFAULT_PROVIDER).build(password); + privateKey = keyConverter.getPrivateKey(((PKCS8EncryptedPrivateKeyInfo) object).decryptPrivateKeyInfo(decProv)); + break; + } else if (object instanceof PEMKeyPair) { + privateKey = keyConverter.getKeyPair((PEMKeyPair) object).getPrivate(); + break; + } else if (object instanceof PrivateKeyInfo) { + privateKey = keyConverter.getPrivateKey((PrivateKeyInfo) object); + } + } + } + } + return privateKey; + } + +} diff --git a/common/util/src/main/java/org/thingsboard/common/util/ThingsBoardThreadFactory.java b/common/util/src/main/java/org/thingsboard/common/util/ThingsBoardThreadFactory.java index 1f41c48f43..9eaca3ca47 100644 --- a/common/util/src/main/java/org/thingsboard/common/util/ThingsBoardThreadFactory.java +++ b/common/util/src/main/java/org/thingsboard/common/util/ThingsBoardThreadFactory.java @@ -22,6 +22,7 @@ import java.util.concurrent.atomic.AtomicInteger; * Copy of Executors.DefaultThreadFactory but with ability to set name of the pool */ public class ThingsBoardThreadFactory implements ThreadFactory { + public static final String THREAD_TOPIC_SEPARATOR = " | "; private static final AtomicInteger poolNumber = new AtomicInteger(1); private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); @@ -40,6 +41,17 @@ public class ThingsBoardThreadFactory implements ThreadFactory { "-thread-"; } + public static void updateCurrentThreadName(String threadSuffix) { + String name = Thread.currentThread().getName(); + int spliteratorIndex = name.indexOf(THREAD_TOPIC_SEPARATOR); + if (spliteratorIndex > 0) { + name = name.substring(0, spliteratorIndex); + } + name = name + THREAD_TOPIC_SEPARATOR + threadSuffix; + Thread.currentThread().setName(name); + } + + @Override public Thread newThread(Runnable r) { Thread t = new Thread(group, r, diff --git a/common/util/src/test/java/org/thingsboard/common/util/ExceptionUtilTest.java b/common/util/src/test/java/org/thingsboard/common/util/ExceptionUtilTest.java new file mode 100644 index 0000000000..e589ae8e30 --- /dev/null +++ b/common/util/src/test/java/org/thingsboard/common/util/ExceptionUtilTest.java @@ -0,0 +1,74 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.common.util; + +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThat; + +class ExceptionUtilTest { + + final Exception cause = new RuntimeException(); + + @Test + void givenRootCause_whenLookupExceptionInCause_thenReturnRootCauseAndNoStackOverflow() { + Exception e = cause; + for (int i = 0; i <= 16384; i++) { + e = new Exception(e); + } + assertThat(ExceptionUtil.lookupExceptionInCause(e, RuntimeException.class)).isSameAs(cause); + } + + @Test + void givenCause_whenLookupExceptionInCause_thenReturnCause() { + assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(cause), RuntimeException.class)).isSameAs(cause); + } + + @Test + void givenNoCauseAndExceptionIsWantedCauseClass_whenLookupExceptionInCause_thenReturnSelf() { + assertThat(ExceptionUtil.lookupExceptionInCause(cause, RuntimeException.class)).isSameAs(cause); + } + + @Test + void givenNoCause_whenLookupExceptionInCause_thenReturnNull() { + assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(), RuntimeException.class)).isNull(); + } + + @Test + void givenNotWantedCause_whenLookupExceptionInCause_thenReturnNull() { + final Exception cause = new IOException(); + assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(cause), RuntimeException.class)).isNull(); + } + + @Test + void givenCause_whenLookupExceptionInCauseByMany_thenReturnFirstCause() { + final Exception causeIAE = new IllegalAccessException(); + assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(causeIAE))).isNull(); + assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(causeIAE), IOException.class, NoSuchFieldException.class)).isNull(); + assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(causeIAE), IllegalAccessException.class, IOException.class, NoSuchFieldException.class)).isSameAs(causeIAE); + assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(causeIAE), IOException.class, NoSuchFieldException.class, IllegalAccessException.class)).isSameAs(causeIAE); + + final Exception causeIOE = new IOException(causeIAE); + assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(causeIOE))).isNull(); + assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(causeIAE), ClassNotFoundException.class, NoSuchFieldException.class)).isNull(); + assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(causeIOE), IOException.class, NoSuchFieldException.class)).isSameAs(causeIOE); + assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(causeIOE), IllegalAccessException.class, IOException.class, NoSuchFieldException.class)).isSameAs(causeIOE); + assertThat(ExceptionUtil.lookupExceptionInCause(new Exception(causeIOE), IOException.class, NoSuchFieldException.class, IllegalAccessException.class)).isSameAs(causeIOE); + } + +} diff --git a/common/util/src/test/java/org/thingsboard/common/util/JacksonUtilTest.java b/common/util/src/test/java/org/thingsboard/common/util/JacksonUtilTest.java index 2ba2384acd..94c3d2d340 100644 --- a/common/util/src/test/java/org/thingsboard/common/util/JacksonUtilTest.java +++ b/common/util/src/test/java/org/thingsboard/common/util/JacksonUtilTest.java @@ -19,11 +19,15 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.id.AssetId; + +import java.util.UUID; public class JacksonUtilTest { @Test - public void allow_unquoted_field_mapper_test() { + public void allowUnquotedFieldMapperTest() { String data = "{data: 123}"; JsonNode actualResult = JacksonUtil.toJsonNode(data, JacksonUtil.ALLOW_UNQUOTED_FIELD_NAMES_MAPPER); // should be: {"data": 123} ObjectNode expectedResult = JacksonUtil.newObjectNode(); @@ -32,4 +36,23 @@ public class JacksonUtilTest { Assert.assertThrows(IllegalArgumentException.class, () -> JacksonUtil.toJsonNode(data)); // syntax exception due to missing quotes in the field name! } -} \ No newline at end of file + @Test + public void failOnUnknownPropertiesMapperTest() { + Asset asset = new Asset(); + asset.setId(new AssetId(UUID.randomUUID())); + asset.setName("Test"); + asset.setType("type"); + String serializedAsset = JacksonUtil.toString(asset); + JsonNode jsonNode = JacksonUtil.toJsonNode(serializedAsset); + // case: add new field to serialized Asset string and check for backward compatibility with original Asset object + Assert.assertNotNull(jsonNode); + ((ObjectNode) jsonNode).put("test", (String) null); + serializedAsset = JacksonUtil.toString(jsonNode); + // deserialize with FAIL_ON_UNKNOWN_PROPERTIES = false + Asset result = JacksonUtil.fromString(serializedAsset, Asset.class, true); + Assert.assertNotNull(result); + Assert.assertEquals(asset.getId(), result.getId()); + Assert.assertEquals(asset.getName(), result.getName()); + Assert.assertEquals(asset.getType(), result.getType()); + } +} diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml index ae12a413c0..c6595ca824 100644 --- a/common/version-control/pom.xml +++ b/common/version-control/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT common org.thingsboard.common diff --git a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultClusterVersionControlService.java b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultClusterVersionControlService.java index 618eb11d98..fa45ddf98c 100644 --- a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultClusterVersionControlService.java +++ b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultClusterVersionControlService.java @@ -24,6 +24,7 @@ import com.google.common.util.concurrent.MoreExecutors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.checkerframework.checker.nullness.qual.Nullable; +import org.eclipse.jgit.errors.LargeObjectException; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.EventListener; @@ -67,7 +68,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.VersionedEntityInfoP import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.NotificationsTopicService; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.TbApplicationEventListener; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; @@ -110,7 +111,7 @@ public class DefaultClusterVersionControlService extends TbApplicationEventListe private final TbVersionControlQueueFactory queueFactory; private final DataDecodingEncodingService encodingService; private final GitRepositoryService vcService; - private final NotificationsTopicService notificationsTopicService; + private final TopicService topicService; private final ConcurrentMap tenantRepoLocks = new ConcurrentHashMap<>(); private final Map pendingCommitMap = new HashMap<>(); @@ -122,11 +123,11 @@ public class DefaultClusterVersionControlService extends TbApplicationEventListe @Value("${queue.vc.poll-interval:25}") private long pollDuration; - @Value("${queue.vc.pack-processing-timeout:60000}") + @Value("${queue.vc.pack-processing-timeout:180000}") private long packProcessingTimeout; @Value("${vc.git.io_pool_size:3}") private int ioPoolSize; - @Value("${queue.vc.msg-chunk-size:500000}") + @Value("${queue.vc.msg-chunk-size:250000}") private int msgChunkSize; //We need to manually manage the threads since tasks for particular tenant need to be processed sequentially. @@ -261,7 +262,7 @@ public class DefaultClusterVersionControlService extends TbApplicationEventListe } } } catch (Exception e) { - reply(ctx, Optional.of(e)); + reply(ctx, Optional.of(handleError(e))); } finally { lock.unlock(); } @@ -303,6 +304,7 @@ public class DefaultClusterVersionControlService extends TbApplicationEventListe private void handleEntityContentRequest(VersionControlRequestCtx ctx, EntityContentRequestMsg request) throws IOException { String path = getRelativePath(EntityType.valueOf(request.getEntityType()), new UUID(request.getEntityIdMSB(), request.getEntityIdLSB()).toString()); + log.debug("Executing handleEntityContentRequest [{}][{}]", ctx.getTenantId(), path); String data = vcService.getFileContentAtCommit(ctx.getTenantId(), path, request.getVersionId()); Iterable dataChunks = StringUtils.split(data, msgChunkSize); @@ -393,6 +395,7 @@ public class DefaultClusterVersionControlService extends TbApplicationEventListe } private void handleCommitRequest(VersionControlRequestCtx ctx, CommitRequestMsg request) throws Exception { + log.debug("Executing handleCommitRequest [{}][{}]", ctx.getTenantId(), ctx.getRequestId()); var tenantId = ctx.getTenantId(); UUID txId = UUID.fromString(request.getTxId()); if (request.hasPrepareMsg()) { @@ -443,6 +446,7 @@ public class DefaultClusterVersionControlService extends TbApplicationEventListe } private void addToCommit(VersionControlRequestCtx ctx, PendingCommit commit, AddMsg addMsg) throws IOException { + log.debug("Executing addToCommit [{}][{}]", ctx.getTenantId(), ctx.getRequestId()); log.trace("[{}] received chunk {} for 'addToCommit'", addMsg.getChunkedMsgId(), addMsg.getChunkIndex()); Map chunkedMsgs = commit.getChunkedMsgs(); String[] msgChunks = chunkedMsgs.computeIfAbsent(addMsg.getChunkedMsgId(), id -> new String[addMsg.getChunksCount()]); @@ -496,6 +500,13 @@ public class DefaultClusterVersionControlService extends TbApplicationEventListe } } + private Exception handleError(Exception e) { + if (e instanceof LargeObjectException) { + return new RuntimeException("Version is too big"); + } + return e; + } + private void reply(VersionControlRequestCtx ctx, VersionCreationResult result) { var responseBuilder = CommitResponseMsg.newBuilder().setAdded(result.getAdded()) .setModified(result.getModified()) @@ -520,7 +531,7 @@ public class DefaultClusterVersionControlService extends TbApplicationEventListe } private void reply(VersionControlRequestCtx ctx, Optional e, Function enrichFunction) { - TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, ctx.getNodeId()); + TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_CORE, ctx.getNodeId()); VersionControlResponseMsg.Builder builder = VersionControlResponseMsg.newBuilder() .setRequestIdMSB(ctx.getRequestId().getMostSignificantBits()) .setRequestIdLSB(ctx.getRequestId().getLeastSignificantBits()); diff --git a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java index 08fc75363c..911233a2f8 100644 --- a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java +++ b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java @@ -39,6 +39,7 @@ import org.eclipse.jgit.diff.EditList; import org.eclipse.jgit.diff.HistogramDiff; import org.eclipse.jgit.diff.RawText; import org.eclipse.jgit.diff.RawTextComparator; +import org.eclipse.jgit.errors.LargeObjectException; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectLoader; @@ -236,8 +237,12 @@ public class GitRepository { ObjectId blobId = treeWalk.getObjectId(0); try (ObjectReader objectReader = git.getRepository().newObjectReader()) { ObjectLoader objectLoader = objectReader.open(blobId); - byte[] bytes = objectLoader.getBytes(); - return new String(bytes, StandardCharsets.UTF_8); + try { + byte[] bytes = objectLoader.getBytes(); + return new String(bytes, StandardCharsets.UTF_8); + } catch (LargeObjectException e) { + throw new RuntimeException("File " + file + " is too big to load"); + } } } } diff --git a/dao/pom.xml b/dao/pom.xml index 45f49790b0..d3145fe892 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard dao @@ -79,6 +79,10 @@ org.postgresql postgresql + + org.bouncycastle + bcpkix-jdk15on + org.springframework.boot spring-boot-starter-test @@ -222,6 +226,26 @@ com.jayway.jsonpath json-path + + org.thingsboard.rule-engine + rule-engine-api + + + io.hypersistence + hypersistence-utils-hibernate-55 + + + org.apache.xmlgraphics + batik-transcoder + + + org.apache.xmlgraphics + batik-codec + + + com.drewnoakes + metadata-extractor + diff --git a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java index 63b13bca66..1c4aed5393 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java @@ -18,6 +18,11 @@ package org.thingsboard.server.dao; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.util.CollectionUtils; +import org.thingsboard.server.common.data.EntityInfo; +import org.thingsboard.server.common.data.EntitySubtype; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -27,12 +32,14 @@ import org.thingsboard.server.dao.model.ToData; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; import java.util.function.Consumer; import java.util.function.Function; +import java.util.stream.Collectors; public abstract class DaoUtil { @@ -109,6 +116,10 @@ public abstract class DaoUtil { return ids; } + public static List fromUUIDs(List uuids, Function mapper) { + return uuids.stream().map(mapper).collect(Collectors.toList()); + } + public static I toEntityId(UUID uuid, Function creator) { if (uuid != null) { return creator.apply(uuid); @@ -142,4 +153,27 @@ public abstract class DaoUtil { return null; } } + + public static List convertTenantEntityTypesToDto(UUID tenantUUID, EntityType entityType, List types) { + if (CollectionUtils.isEmpty(types)) { + return Collections.emptyList(); + } + TenantId tenantId = TenantId.fromUUID(tenantUUID); + return types.stream() + .map(type -> new EntitySubtype(tenantId, entityType, type)) + .collect(Collectors.toList()); + } + + @Deprecated // used only in deprecated DAO api + public static List convertTenantEntityInfosToDto(UUID tenantUUID, EntityType entityType, List entityInfos) { + if (CollectionUtils.isEmpty(entityInfos)) { + return Collections.emptyList(); + } + var tenantId = TenantId.fromUUID(tenantUUID); + return entityInfos.stream() + .map(info -> new EntitySubtype(tenantId, entityType, info.getName())) + .sorted(Comparator.comparing(EntitySubtype::getType)) + .collect(Collectors.toList()); + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java index 169d7c3b88..5ac75f94f7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java @@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.page.PageLink; import java.util.UUID; -public interface ExportableEntityDao> extends Dao { +public interface ExportableEntityDao> extends Dao { T findByTenantIdAndExternalId(UUID tenantId, UUID externalId); @@ -30,6 +30,10 @@ public interface ExportableEntityDao findByTenantId(UUID tenantId, PageLink pageLink); + default PageData findIdsByTenantId(UUID tenantId, PageLink pageLink) { + return findByTenantId(tenantId, pageLink).mapData(ExportableEntity::getId); + } + I getExternalIdByInternal(I internalId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/ImageContainerDao.java b/dao/src/main/java/org/thingsboard/server/dao/ImageContainerDao.java new file mode 100644 index 0000000000..162ce75dc0 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/ImageContainerDao.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao; + +import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.TenantId; + +import java.util.List; + +public interface ImageContainerDao> { + + List findByTenantAndImageLink(TenantId tenantId, String imageUrl, int limit); + + List findByImageLink(String imageUrl, int limit); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/ThingsboardPostgreSQLDialect.java b/dao/src/main/java/org/thingsboard/server/dao/ThingsboardPostgreSQLDialect.java new file mode 100644 index 0000000000..1c70f7387c --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/ThingsboardPostgreSQLDialect.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao; + +import org.hibernate.dialect.function.SQLFunctionTemplate; +import org.hibernate.type.BooleanType; + +public class ThingsboardPostgreSQLDialect extends org.hibernate.dialect.PostgreSQL10Dialect { + public ThingsboardPostgreSQLDialect() { + super(); + registerFunction("ilike", new SQLFunctionTemplate(BooleanType.INSTANCE, "(?1 ILIKE ?2)")); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java index 745861c6c8..9028bf33d4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.alarm; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; @@ -77,12 +78,16 @@ public interface AlarmDao extends Dao { PageData findAlarmsIdsByEndTsBeforeAndTenantId(Long time, TenantId tenantId, PageLink pageLink); + PageData findAlarmIdsByAssigneeId(TenantId tenantId, UUID userId, PageLink pageLink); + void createEntityAlarmRecord(EntityAlarm entityAlarm); List findEntityAlarmRecords(TenantId tenantId, AlarmId id); void deleteEntityAlarmRecords(TenantId tenantId, EntityId entityId); + void deleteEntityAlarmRecordsByTenantId(TenantId tenantId); + AlarmApiCallResult createOrUpdateActiveAlarm(AlarmCreateOrUpdateActiveRequest request, boolean alarmCreationEnabled); AlarmApiCallResult updateAlarm(AlarmUpdateRequest request); @@ -97,4 +102,7 @@ public interface AlarmDao extends Dao { long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query); + PageData findTenantAlarmTypes(UUID tenantId, PageLink pageLink); + + boolean removeAlarmTypesIfNoAlarmsPresent(UUID tenantId, Set types); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCacheEvictEvent.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCacheEvictEvent.java new file mode 100644 index 0000000000..985d0c8ad1 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCacheEvictEvent.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.alarm; + +import lombok.Data; +import lombok.RequiredArgsConstructor; +import org.thingsboard.server.common.data.id.TenantId; + +@Data +@RequiredArgsConstructor +class AlarmTypesCacheEvictEvent { + private final TenantId tenantId; +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCaffeineCache.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCaffeineCache.java new file mode 100644 index 0000000000..4ad08338c4 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCaffeineCache.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.alarm; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cache.CacheManager; +import org.springframework.stereotype.Service; +import org.thingsboard.server.cache.CaffeineTbTransactionalCache; +import org.thingsboard.server.common.data.CacheConstants; +import org.thingsboard.server.common.data.EntitySubtype; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; + +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) +@Service("AlarmTypesCache") +public class AlarmTypesCaffeineCache extends CaffeineTbTransactionalCache> { + + public AlarmTypesCaffeineCache(CacheManager cacheManager) { + super(cacheManager, CacheConstants.ALARM_TYPES_CACHE); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesRedisCache.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesRedisCache.java new file mode 100644 index 0000000000..18eb81f690 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesRedisCache.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.alarm; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.stereotype.Service; +import org.thingsboard.server.cache.CacheSpecsMap; +import org.thingsboard.server.cache.RedisTbTransactionalCache; +import org.thingsboard.server.cache.TBRedisCacheConfiguration; +import org.thingsboard.server.cache.TbFSTRedisSerializer; +import org.thingsboard.server.common.data.CacheConstants; +import org.thingsboard.server.common.data.EntitySubtype; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; + +@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") +@Service("AlarmTypesCache") +public class AlarmTypesRedisCache extends RedisTbTransactionalCache> { + + public AlarmTypesRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) { + super(CacheConstants.ALARM_TYPES_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbFSTRedisSerializer<>()); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java index 96342aca2f..24d9c6366b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java @@ -18,13 +18,14 @@ package org.thingsboard.server.dao.alarm; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.base.Function; -import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.event.TransactionalEventListener; import org.springframework.util.CollectionUtils; +import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; @@ -39,6 +40,7 @@ import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.alarm.AlarmStatusFilter; import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest; import org.thingsboard.server.common.data.alarm.EntityAlarm; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ApiUsageLimitsExceededException; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.CustomerId; @@ -47,6 +49,8 @@ import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.query.AlarmCountQuery; import org.thingsboard.server.common.data.query.AlarmData; import org.thingsboard.server.common.data.query.AlarmDataQuery; @@ -54,8 +58,11 @@ import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelationsQuery; import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.RelationsSearchParameters; -import org.thingsboard.server.dao.entity.AbstractEntityService; +import org.thingsboard.server.dao.entity.AbstractCachedEntityService; import org.thingsboard.server.dao.entity.EntityService; +import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent; +import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; +import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.ConstraintValidator; import org.thingsboard.server.dao.service.DataValidator; @@ -78,19 +85,33 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @Service("AlarmDaoService") @Slf4j @RequiredArgsConstructor -public class BaseAlarmService extends AbstractEntityService implements AlarmService { +public class BaseAlarmService extends AbstractCachedEntityService, AlarmTypesCacheEvictEvent> implements AlarmService { public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; + private static final PageLink DEFAULT_ALARM_TYPES_PAGE_LINK = new PageLink(25, 0, null, new SortOrder("type")); + private final TenantService tenantService; private final AlarmDao alarmDao; private final EntityService entityService; private final DataValidator alarmDataValidator; + @TransactionalEventListener(classes = AlarmTypesCacheEvictEvent.class) + @Override + public void handleEvictEvent(AlarmTypesCacheEvictEvent event) { + TenantId tenantId = event.getTenantId(); + cache.evict(tenantId); + } + @Override public AlarmApiCallResult updateAlarm(AlarmUpdateRequest request) { validateAlarmRequest(request); - return withPropagated(alarmDao.updateAlarm(request)); + AlarmApiCallResult result = withPropagated(alarmDao.updateAlarm(request)); + if (result.getAlarm() != null) { + eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(result.getAlarm().getTenantId()).entity(result) + .entityId(result.getAlarm().getId()).build()); + } + return result; } @Override @@ -112,52 +133,32 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ if (!result.isSuccessful() && !alarmCreationEnabled) { throw new ApiUsageLimitsExceededException("Alarms creation is disabled"); } + if (result.getAlarm() != null) { + eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(result.getAlarm().getTenantId()) + .entityId(result.getAlarm().getId()).added(true).build()); + publishEvictEvent(new AlarmTypesCacheEvictEvent(request.getTenantId())); + } return withPropagated(result); } @Override public AlarmApiCallResult acknowledgeAlarm(TenantId tenantId, AlarmId alarmId, long ackTs) { - return withPropagated(alarmDao.acknowledgeAlarm(tenantId, alarmId, ackTs)); + var result = withPropagated(alarmDao.acknowledgeAlarm(tenantId, alarmId, ackTs)); + if (result.getAlarm() != null) { + eventPublisher.publishEvent(ActionEntityEvent.builder().tenantId(tenantId).entityId(result.getAlarm().getId()) + .actionType(ActionType.ALARM_ACK).build()); + } + return result; } @Override public AlarmApiCallResult clearAlarm(TenantId tenantId, AlarmId alarmId, long clearTs, JsonNode details) { - return withPropagated(alarmDao.clearAlarm(tenantId, alarmId, clearTs, details)); - } - - @Override - public AlarmOperationResult createOrUpdateAlarm(Alarm alarm) { - return createOrUpdateAlarm(alarm, true); - } - - @Override - public AlarmOperationResult createOrUpdateAlarm(Alarm alarm, boolean alarmCreationEnabled) { - alarmDataValidator.validate(alarm, Alarm::getTenantId); - try { - if (alarm.getStartTs() == 0L) { - alarm.setStartTs(System.currentTimeMillis()); - } - if (alarm.getEndTs() == 0L) { - alarm.setEndTs(alarm.getStartTs()); - } - alarm.setCustomerId(entityService.fetchEntityCustomerId(alarm.getTenantId(), alarm.getOriginator()).orElse(null)); - if (alarm.getId() == null) { - // Atomic update and return alarm + assignee. - Alarm existing = alarmDao.findLatestByOriginatorAndType(alarm.getTenantId(), alarm.getOriginator(), alarm.getType()); - if (existing == null || existing.getStatus().isCleared()) { - if (!alarmCreationEnabled) { - throw new ApiUsageLimitsExceededException("Alarms creation is disabled"); - } - return createAlarm(alarm); - } else { - return updateAlarm(existing, alarm); - } - } else { - return updateAlarm(alarm); - } - } catch (ExecutionException | InterruptedException e) { - throw new RuntimeException(e); + var result = withPropagated(alarmDao.clearAlarm(tenantId, alarmId, clearTs, details)); + if (result.getAlarm() != null) { + eventPublisher.publishEvent(ActionEntityEvent.builder().tenantId(tenantId).entityId(result.getAlarm().getId()) + .actionType(ActionType.ALARM_CLEAR).build()); } + return result; } @Override @@ -165,11 +166,6 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ return alarmDao.findLatestActiveByOriginatorAndType(tenantId, originator, type); } - @Override - public ListenableFuture findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type) { - return alarmDao.findLatestByOriginatorAndTypeAsync(tenantId, originator, type); - } - @Override public PageData findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection orderedEntityIds) { @@ -181,36 +177,35 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ @Override @Transactional public AlarmApiCallResult delAlarm(TenantId tenantId, AlarmId alarmId) { + return delAlarm(tenantId, alarmId, true); + } + + @Override + @Transactional + public AlarmApiCallResult delAlarm(TenantId tenantId, AlarmId alarmId, boolean checkAndDeleteAlarmType) { log.debug("Deleting Alarm Id: {}", alarmId); AlarmInfo alarm = alarmDao.findAlarmInfoById(tenantId, alarmId.getId()); if (alarm == null) { return AlarmApiCallResult.builder().successful(false).build(); } else { + var propagationIds = getPropagationEntityIdsList(alarm); deleteEntityRelations(tenantId, alarm.getId()); alarmDao.removeById(tenantId, alarm.getUuidId()); - return AlarmApiCallResult.builder().alarm(alarm).deleted(true).successful(true).build(); + eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId) + .entityId(alarmId).entity(alarm).build()); + if (checkAndDeleteAlarmType) { + delAlarmTypes(tenantId, Collections.singleton(alarm.getType())); + } + return AlarmApiCallResult.builder().alarm(alarm).deleted(true).successful(true).propagatedEntitiesList(propagationIds).build(); } } @Override @Transactional - public AlarmOperationResult deleteAlarm(TenantId tenantId, AlarmId alarmId) { - log.debug("Deleting Alarm Id: {}", alarmId); - Alarm alarm = alarmDao.findAlarmById(tenantId, alarmId.getId()); - if (alarm == null) { - return new AlarmOperationResult(alarm, false); + public void delAlarmTypes(TenantId tenantId, Set types) { + if (!types.isEmpty() && alarmDao.removeAlarmTypesIfNoAlarmsPresent(tenantId.getId(), types)) { + publishEvictEvent(new AlarmTypesCacheEvictEvent(tenantId)); } - AlarmOperationResult result = new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm))); - deleteEntityRelations(tenantId, alarm.getId()); - alarmDao.removeById(tenantId, alarm.getUuidId()); - return result; - } - - private AlarmOperationResult createAlarm(Alarm alarm) throws InterruptedException, ExecutionException { - log.debug("New Alarm : {}", alarm); - Alarm saved = alarmDao.save(alarm.getTenantId(), alarm); - List propagatedEntitiesList = createEntityAlarmRecords(saved); - return new AlarmOperationResult(saved, true, true, propagatedEntitiesList); } private List createEntityAlarmRecords(Alarm alarm) throws ExecutionException, InterruptedException { @@ -243,69 +238,24 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ return relations.map(EntityRelation::getFrom).collect(Collectors.toCollection(LinkedHashSet::new)); } - private AlarmOperationResult updateAlarm(Alarm update) { - alarmDataValidator.validate(update, Alarm::getTenantId); - return getAndUpdate(update.getTenantId(), update.getId(), - (alarm) -> alarm == null ? null : updateAlarm(alarm, update)); - } - - private AlarmOperationResult updateAlarm(Alarm oldAlarm, Alarm newAlarm) { - boolean propagationEnabled = !oldAlarm.isPropagate() && newAlarm.isPropagate(); - boolean propagationToOwnerEnabled = !oldAlarm.isPropagateToOwner() && newAlarm.isPropagateToOwner(); - boolean propagationToTenantEnabled = !oldAlarm.isPropagateToTenant() && newAlarm.isPropagateToTenant(); - AlarmSeverity oldAlarmSeverity = oldAlarm.getSeverity(); - Alarm result = alarmDao.save(newAlarm.getTenantId(), merge(oldAlarm, newAlarm)); - List propagatedEntitiesList; - if (propagationEnabled || propagationToOwnerEnabled || propagationToTenantEnabled) { - try { - propagatedEntitiesList = createEntityAlarmRecords(result); - } catch (InterruptedException | ExecutionException e) { - log.warn("Failed to update alarm relations [{}]", result, e); - throw new RuntimeException(e); - } - } else { - propagatedEntitiesList = new ArrayList<>(getPropagationEntityIds(result)); - } - return new AlarmOperationResult(result, true, false, oldAlarmSeverity, propagatedEntitiesList); - } - - @Override - public ListenableFuture ackAlarm(TenantId tenantId, AlarmId alarmId, long ackTime) { - Alarm alarm = alarmDao.findAlarmById(tenantId, alarmId.getId()); - if (alarm == null || alarm.getStatus().isAck()) { - return Futures.immediateFuture(new AlarmOperationResult(alarm, false)); - } else { - alarm.setAcknowledged(true); - alarm.setAckTs(ackTime); - alarm = alarmDao.save(alarm.getTenantId(), alarm); - return Futures.immediateFuture(new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm)))); - } - } - - @Override - public ListenableFuture clearAlarm(TenantId tenantId, AlarmId alarmId, JsonNode details, long clearTime) { - Alarm alarm = alarmDao.findAlarmById(tenantId, alarmId.getId()); - if (alarm == null || alarm.getStatus().isCleared()) { - return Futures.immediateFuture(new AlarmOperationResult(alarm, false)); - } else { - alarm.setCleared(true); - alarm.setClearTs(clearTime); - if (details != null) { - alarm.setDetails(details); - } - alarm = alarmDao.save(alarm.getTenantId(), alarm); - return Futures.immediateFuture(new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm)))); - } - } - @Override public AlarmApiCallResult assignAlarm(TenantId tenantId, AlarmId alarmId, UserId assigneeId, long assignTime) { - return withPropagated(alarmDao.assignAlarm(tenantId, alarmId, assigneeId, assignTime)); + var result = withPropagated(alarmDao.assignAlarm(tenantId, alarmId, assigneeId, assignTime)); + if (result.getAlarm() != null) { + eventPublisher.publishEvent(ActionEntityEvent.builder().tenantId(tenantId).entityId(result.getAlarm().getId()) + .actionType(ActionType.ALARM_ASSIGNED).build()); + } + return result; } @Override public AlarmApiCallResult unassignAlarm(TenantId tenantId, AlarmId alarmId, long unassignTime) { - return withPropagated(alarmDao.unassignAlarm(tenantId, alarmId, unassignTime)); + var result = withPropagated(alarmDao.unassignAlarm(tenantId, alarmId, unassignTime)); + if (result.getAlarm() != null) { + eventPublisher.publishEvent(ActionEntityEvent.builder().tenantId(tenantId).entityId(result.getAlarm().getId()) + .actionType(ActionType.ALARM_UNASSIGNED).build()); + } + return result; } @Override @@ -330,23 +280,30 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ } @Override - public ListenableFuture> findAlarms(TenantId tenantId, AlarmQuery query) { - return Futures.immediateFuture(alarmDao.findAlarms(tenantId, query)); + public PageData findAlarms(TenantId tenantId, AlarmQuery query) { + return alarmDao.findAlarms(tenantId, query); } @Override - public ListenableFuture> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query) { - return Futures.immediateFuture(alarmDao.findCustomerAlarms(tenantId, customerId, query)); + public PageData findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query) { + return alarmDao.findCustomerAlarms(tenantId, customerId, query); } @Override - public ListenableFuture> findAlarmsV2(TenantId tenantId, AlarmQueryV2 query) { - return Futures.immediateFuture(alarmDao.findAlarmsV2(tenantId, query)); + public PageData findAlarmsV2(TenantId tenantId, AlarmQueryV2 query) { + return alarmDao.findAlarmsV2(tenantId, query); } @Override - public ListenableFuture> findCustomerAlarmsV2(TenantId tenantId, CustomerId customerId, AlarmQueryV2 query) { - return Futures.immediateFuture(alarmDao.findCustomerAlarmsV2(tenantId, customerId, query)); + public PageData findCustomerAlarmsV2(TenantId tenantId, CustomerId customerId, AlarmQueryV2 query) { + return alarmDao.findCustomerAlarmsV2(tenantId, customerId, query); + } + + @Override + public PageData findAlarmIdsByAssigneeId(TenantId tenantId, UserId userId, PageLink pageLink) { + log.trace("[{}] Executing findAlarmIdsByAssigneeId [{}]", tenantId, userId); + validateId(userId, "Incorrect userId " + userId); + return alarmDao.findAlarmIdsByAssigneeId(tenantId, userId.getId(), pageLink); } @Override @@ -367,15 +324,33 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ @Override public void deleteEntityAlarmRelations(TenantId tenantId, EntityId entityId) { + log.trace("Executing deleteEntityAlarms [{}]", entityId); alarmDao.deleteEntityAlarmRecords(tenantId, entityId); } + @Override + public void deleteEntityAlarmRecordsByTenantId(TenantId tenantId) { + log.trace("Executing deleteEntityAlarmRecordsByTenantId [{}]", tenantId); + alarmDao.deleteEntityAlarmRecordsByTenantId(tenantId); + } + @Override public long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query) { validateId(tenantId, INCORRECT_TENANT_ID + tenantId); return alarmDao.countAlarmsByQuery(tenantId, customerId, query); } + @Override + public PageData findAlarmTypesByTenantId(TenantId tenantId, PageLink pageLink) { + log.trace("Executing findAlarmTypesByTenantId, tenantId [{}]", tenantId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + if (DEFAULT_ALARM_TYPES_PAGE_LINK.equals(pageLink)) { + return cache.getAndPutInTransaction(tenantId, () -> + alarmDao.findTenantAlarmTypes(tenantId.getId(), pageLink), false); + } + return alarmDao.findTenantAlarmTypes(tenantId.getId(), pageLink); + } + private Alarm merge(Alarm existing, Alarm alarm) { if (alarm.getStartTs() > existing.getEndTs()) { existing.setEndTs(alarm.getStartTs()); @@ -487,5 +462,4 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ request.setEndTs(request.getStartTs()); } } - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java index 5e7c3ff455..4cdb64348b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java @@ -189,6 +189,7 @@ public interface AssetDao extends Dao, TenantEntityDao, ExportableEntityD * * @return the list of tenant asset type objects */ + @Deprecated(since = "3.6.2", forRemoval = true) ListenableFuture> findTenantAssetTypesAsync(UUID tenantId); Long countAssetsByAssetProfileId(TenantId tenantId, UUID assetProfileId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileDao.java index b7fc5fedc6..7dc188000c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileDao.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.asset; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.asset.AssetProfileInfo; import org.thingsboard.server.common.data.id.AssetProfileId; @@ -23,10 +24,12 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ExportableEntityDao; +import org.thingsboard.server.dao.ImageContainerDao; +import java.util.List; import java.util.UUID; -public interface AssetProfileDao extends Dao, ExportableEntityDao { +public interface AssetProfileDao extends Dao, ExportableEntityDao, ImageContainerDao { AssetProfileInfo findAssetProfileInfoById(TenantId tenantId, UUID assetProfileId); @@ -43,4 +46,9 @@ public interface AssetProfileDao extends Dao, ExportableEntityDao< AssetProfileInfo findDefaultAssetProfileInfo(TenantId tenantId); AssetProfile findByName(TenantId tenantId, String profileName); + + PageData findAllWithImages(PageLink pageLink); + + List findTenantAssetProfileNames(UUID tenantId, boolean activeOnly); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java index eb297e0049..0b9e949c7c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java @@ -21,6 +21,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.event.TransactionalEventListener; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.asset.Asset; @@ -33,15 +34,20 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.AbstractCachedEntityService; +import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; +import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; import static org.thingsboard.server.dao.service.Validator.validateId; @@ -69,6 +75,9 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService assetProfileValidator; + @Autowired + private ImageService imageService; + @TransactionalEventListener(classes = AssetProfileEvictEvent.class) @Override public void handleEvictEvent(AssetProfileEvictEvent event) { @@ -88,18 +97,28 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService assetProfileDao.findById(tenantId, assetProfileId.getId()), true); + return cache.getOrFetchFromDB(AssetProfileCacheKey.fromId(assetProfileId), + () -> assetProfileDao.findById(tenantId, assetProfileId.getId()), true, putInCache); } @Override public AssetProfile findAssetProfileByName(TenantId tenantId, String profileName) { + return findAssetProfileByName(tenantId, profileName, true); + } + + @Override + public AssetProfile findAssetProfileByName(TenantId tenantId, String profileName, boolean putInCache) { log.trace("Executing findAssetProfileByName [{}][{}]", tenantId, profileName); Validator.validateString(profileName, INCORRECT_ASSET_PROFILE_NAME + profileName); - return cache.getAndPutInTransaction(AssetProfileCacheKey.fromName(tenantId, profileName), - () -> assetProfileDao.findByName(tenantId, profileName), false); + return cache.getOrFetchFromDB(AssetProfileCacheKey.fromName(tenantId, profileName), + () -> assetProfileDao.findByName(tenantId, profileName), false, putInCache); } @Override @@ -109,15 +128,32 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService tenantAssetProfilesRemover = + @Override + public List findAssetProfileNamesByTenantId(TenantId tenantId, boolean activeOnly) { + log.trace("Executing findAssetProfileNamesByTenantId, tenantId [{}]", tenantId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + return assetProfileDao.findTenantAssetProfileNames(tenantId.getId(), activeOnly) + .stream().sorted(Comparator.comparing(EntityInfo::getName)) + .collect(Collectors.toList()); + } + + private final PaginatedRemover tenantAssetProfilesRemover = new PaginatedRemover<>() { @Override @@ -302,5 +348,4 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService entityViews = entityViewService.findEntityViewsByTenantIdAndEntityId(asset.getTenantId(), assetId); - if (entityViews != null && !entityViews.isEmpty()) { + if (entityViewService.existsByTenantIdAndEntityId(tenantId, assetId)) { throw new DataValidationException("Can't delete asset that has entity views!"); } + Asset asset = assetDao.findById(tenantId, assetId.getId()); + alarmService.deleteEntityAlarmRelations(tenantId, assetId); + deleteAsset(tenantId, asset); + } + + private void deleteAsset(TenantId tenantId, Asset asset) { + log.trace("Executing deleteAsset [{}]", asset.getId()); + relationService.deleteEntityRelations(tenantId, asset.getAssetProfileId()); + + assetDao.removeById(tenantId, asset.getUuidId()); + publishEvictEvent(new AssetCacheEvictEvent(asset.getTenantId(), asset.getName(), null)); countService.publishCountEntityEvictEvent(tenantId, EntityType.ASSET); - - assetDao.removeById(tenantId, assetId.getId()); + eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(asset.getId()).build()); } @Override @@ -339,7 +362,12 @@ public class BaseAssetService extends AbstractCachedEntityService - assetList == null ? Collections.emptyList() : assetList.stream().filter(asset -> query.getAssetTypes().contains(asset.getType())).collect(Collectors.toList()), MoreExecutors.directExecutor() + assetList == null ? + Collections.emptyList() : + assetList.stream() + .filter(asset -> query.getAssetTypes().contains(asset.getType())) + .collect(Collectors.toList()), + MoreExecutors.directExecutor() ); return assets; } @@ -348,12 +376,7 @@ public class BaseAssetService extends AbstractCachedEntityService> findAssetTypesByTenantId(TenantId tenantId) { log.trace("Executing findAssetTypesByTenantId, tenantId [{}]", tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - ListenableFuture> tenantAssetTypes = assetDao.findTenantAssetTypesAsync(tenantId.getId()); - return Futures.transform(tenantAssetTypes, - assetTypes -> { - assetTypes.sort(Comparator.comparing(EntitySubtype::getType)); - return assetTypes; - }, MoreExecutors.directExecutor()); + return assetDao.findTenantAssetTypesAsync(tenantId.getId()); } @Override @@ -372,6 +395,8 @@ public class BaseAssetService extends AbstractCachedEntityService tenantAssetsRemover = - new PaginatedRemover() { + private final PaginatedRemover tenantAssetsRemover = new PaginatedRemover<>() { - @Override - protected PageData findEntities(TenantId tenantId, TenantId id, PageLink pageLink) { - return assetDao.findAssetsByTenantId(id.getId(), pageLink); - } + @Override + protected PageData findEntities(TenantId tenantId, TenantId id, PageLink pageLink) { + return assetDao.findAssetsByTenantId(id.getId(), pageLink); + } - @Override - protected void removeEntity(TenantId tenantId, Asset entity) { - deleteAsset(tenantId, new AssetId(entity.getId().getId())); - } - }; + @Override + protected void removeEntity(TenantId tenantId, Asset asset) { + deleteAsset(tenantId, asset); + } + }; - private PaginatedRemover customerAssetsUnasigner = new PaginatedRemover() { + private final PaginatedRemover customerAssetsUnasigner = new PaginatedRemover() { @Override protected PageData findEntities(TenantId tenantId, CustomerId id, PageLink pageLink) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeUtils.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeUtils.java index d1abeda5b6..192d56334d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeUtils.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeUtils.java @@ -30,12 +30,12 @@ public class AttributeUtils { Validator.validateString(scope, "Incorrect scope " + scope); } - public static void validate(List kvEntries) { - kvEntries.forEach(AttributeUtils::validate); + public static void validate(List kvEntries, boolean valueNoXssValidation) { + kvEntries.forEach(tsKvEntry -> validate(tsKvEntry, valueNoXssValidation)); } - public static void validate(AttributeKvEntry kvEntry) { - KvUtils.validate(kvEntry); + public static void validate(AttributeKvEntry kvEntry, boolean valueNoXssValidation) { + KvUtils.validate(kvEntry, valueNoXssValidation); if (kvEntry.getDataType() == null) { throw new IncorrectParameterException("Incorrect kvEntry. Data type can't be null"); } else { diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java index 09414ac750..f855c116e2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.attributes; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Service; @@ -45,6 +46,9 @@ import static org.thingsboard.server.dao.attributes.AttributeUtils.validate; public class BaseAttributesService implements AttributesService { private final AttributesDao attributesDao; + @Value("${sql.attributes.value_no_xss_validation:false}") + private boolean valueNoXssValidation; + public BaseAttributesService(AttributesDao attributesDao) { this.attributesDao = attributesDao; } @@ -82,14 +86,14 @@ public class BaseAttributesService implements AttributesService { @Override public ListenableFuture save(TenantId tenantId, EntityId entityId, String scope, AttributeKvEntry attribute) { validate(entityId, scope); - AttributeUtils.validate(attribute); + AttributeUtils.validate(attribute, valueNoXssValidation); return attributesDao.save(tenantId, entityId, scope, attribute); } @Override public ListenableFuture> save(TenantId tenantId, EntityId entityId, String scope, List attributes) { validate(entityId, scope); - AttributeUtils.validate(attributes); + AttributeUtils.validate(attributes, valueNoXssValidation); List> saveFutures = attributes.stream().map(attribute -> attributesDao.save(tenantId, entityId, scope, attribute)).collect(Collectors.toList()); return Futures.allAsList(saveFutures); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java index b95ce39d9a..9a1a1b7ee3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java @@ -36,6 +36,7 @@ import org.thingsboard.server.common.stats.DefaultCounter; import org.thingsboard.server.common.stats.StatsFactory; import org.thingsboard.server.dao.cache.CacheExecutorService; import org.thingsboard.server.dao.service.Validator; +import org.thingsboard.server.dao.sql.JpaExecutorService; import javax.annotation.PostConstruct; import java.util.ArrayList; @@ -61,6 +62,7 @@ public class CachedAttributesService implements AttributesService { public static final String LOCAL_CACHE_TYPE = "caffeine"; private final AttributesDao attributesDao; + private final JpaExecutorService jpaExecutorService; private final CacheExecutorService cacheExecutorService; private final DefaultCounter hitCounter; private final DefaultCounter missCounter; @@ -69,12 +71,16 @@ public class CachedAttributesService implements AttributesService { @Value("${cache.type:caffeine}") private String cacheType; + @Value("${sql.attributes.value_no_xss_validation:false}") + private boolean valueNoXssValidation; public CachedAttributesService(AttributesDao attributesDao, + JpaExecutorService jpaExecutorService, StatsFactory statsFactory, CacheExecutorService cacheExecutorService, TbTransactionalCache cache) { this.attributesDao = attributesDao; + this.jpaExecutorService = jpaExecutorService; this.cacheExecutorService = cacheExecutorService; this.cache = cache; @@ -132,12 +138,14 @@ public class CachedAttributesService implements AttributesService { } @Override - public ListenableFuture> find(TenantId tenantId, EntityId entityId, String scope, Collection attributeKeys) { + public ListenableFuture> find(TenantId tenantId, EntityId entityId, String scope, final Collection attributeKeysNonUnique) { validate(entityId, scope); - attributeKeys = new LinkedHashSet<>(attributeKeys); // deduplicate the attributes + final var attributeKeys = new LinkedHashSet<>(attributeKeysNonUnique); // deduplicate the attributes attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey)); - Map> wrappedCachedAttributes = findCachedAttributes(entityId, scope, attributeKeys); + //CacheExecutor for Redis or DirectExecutor for local Caffeine + return Futures.transformAsync(cacheExecutor.submit(() -> findCachedAttributes(entityId, scope, attributeKeys)), + wrappedCachedAttributes -> { List cachedAttributes = wrappedCachedAttributes.values().stream() .map(TbCacheValueWrapper::get) @@ -153,7 +161,8 @@ public class CachedAttributesService implements AttributesService { List notFoundKeys = notFoundAttributeKeys.stream().map(k -> new AttributeCacheKey(scope, entityId, k)).collect(Collectors.toList()); - return cacheExecutor.submit(() -> { + // DB call should run in DB executor, not in cache-related executor + return jpaExecutorService.submit(() -> { var cacheTransaction = cache.newTransactionForKeys(notFoundKeys); try { log.trace("[{}][{}] Lookup attributes from db: {}", entityId, scope, notFoundAttributeKeys); @@ -177,6 +186,8 @@ public class CachedAttributesService implements AttributesService { throw e; } }); + + }, MoreExecutors.directExecutor()); // cacheExecutor analyse and returns results or submit to DB executor } private Map> findCachedAttributes(EntityId entityId, String scope, Collection attributeKeys) { @@ -212,7 +223,7 @@ public class CachedAttributesService implements AttributesService { @Override public ListenableFuture save(TenantId tenantId, EntityId entityId, String scope, AttributeKvEntry attribute) { validate(entityId, scope); - AttributeUtils.validate(attribute); + AttributeUtils.validate(attribute, valueNoXssValidation); ListenableFuture future = attributesDao.save(tenantId, entityId, scope, attribute); return Futures.transform(future, key -> evict(entityId, scope, attribute, key), cacheExecutor); } @@ -220,7 +231,7 @@ public class CachedAttributesService implements AttributesService { @Override public ListenableFuture> save(TenantId tenantId, EntityId entityId, String scope, List attributes) { validate(entityId, scope); - AttributeUtils.validate(attributes); + AttributeUtils.validate(attributes, valueNoXssValidation); List> futures = new ArrayList<>(attributes.size()); for (var attribute : attributes) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java index 1d8704f0e6..e69ec5865c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.dao.customer; -import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -36,6 +35,8 @@ import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entity.EntityCountService; +import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; +import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -43,7 +44,6 @@ import org.thingsboard.server.dao.service.Validator; import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; import org.thingsboard.server.dao.user.UserService; -import java.io.IOException; import java.util.Optional; import static org.thingsboard.server.dao.service.Validator.validateId; @@ -112,6 +112,8 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom if (customer.getId() == null) { countService.publishCountEntityEvictEvent(savedCustomer.getTenantId(), EntityType.CUSTOMER); } + eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(savedCustomer.getTenantId()) + .entityId(savedCustomer.getId()).added(customer.getId() == null).build()); return savedCustomer; } catch (Exception e) { checkConstraintViolation(e, "customer_external_id_unq_key", "Customer with such external id already exists!"); @@ -139,6 +141,7 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom apiUsageStateService.deleteApiUsageStateByEntityId(customerId); customerDao.removeById(tenantId, customerId.getId()); countService.publishCountEntityEvictEvent(tenantId, EntityType.CUSTOMER); + eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(customerId).build()); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java index 387dc96554..54b767446c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java @@ -18,6 +18,8 @@ package org.thingsboard.server.dao.dashboard; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ExportableEntityDao; import org.thingsboard.server.dao.TenantEntityDao; @@ -40,4 +42,8 @@ public interface DashboardDao extends Dao, TenantEntityDao, Exportabl List findByTenantIdAndTitle(UUID tenantId, String title); + PageData findIdsByTenantId(TenantId tenantId, PageLink pageLink); + + PageData findAllIds(PageLink pageLink); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java index 887c39109e..1d1cf36fff 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java @@ -16,16 +16,20 @@ package org.thingsboard.server.dao.dashboard; import org.thingsboard.server.common.data.DashboardInfo; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.widget.WidgetTypeInfo; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.ImageContainerDao; +import java.util.List; import java.util.UUID; /** * The Interface DashboardInfoDao. */ -public interface DashboardInfoDao extends Dao { +public interface DashboardInfoDao extends Dao, ImageContainerDao { /** * Find dashboards by tenantId and page link. @@ -78,4 +82,5 @@ public interface DashboardInfoDao extends Dao { DashboardInfo findFirstByTenantIdAndName(UUID tenantId, String name); String findTitleById(UUID tenantId, UUID dashboardId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java index 31fc29dd69..f050e924c3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java @@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; @@ -45,7 +46,11 @@ import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.edge.EdgeDao; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entity.EntityCountService; +import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent; +import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; +import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; @@ -74,6 +79,9 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Autowired private EdgeDao edgeDao; + @Autowired + private ImageService imageService; + @Autowired private DataValidator dashboardValidator; @@ -133,13 +141,27 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb return dashboardInfoDao.findByIdAsync(tenantId, dashboardId.getId()); } + @Override + public Dashboard saveDashboard(Dashboard dashboard, boolean doValidate) { + return doSaveDashboard(dashboard, doValidate); + } + @Override public Dashboard saveDashboard(Dashboard dashboard) { + return doSaveDashboard(dashboard, true); + } + + private Dashboard doSaveDashboard(Dashboard dashboard, boolean doValidate) { log.trace("Executing saveDashboard [{}]", dashboard); - dashboardValidator.validate(dashboard, DashboardInfo::getTenantId); + if (doValidate) { + dashboardValidator.validate(dashboard, DashboardInfo::getTenantId); + } try { + imageService.replaceBase64WithImageUrl(dashboard); var saved = dashboardDao.save(dashboard.getTenantId(), dashboard); publishEvictEvent(new DashboardTitleEvictEvent(saved.getId())); + eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(saved.getTenantId()) + .entityId(saved.getId()).added(dashboard.getId() == null).build()); if (dashboard.getId() == null) { countService.publishCountEntityEvictEvent(saved.getTenantId(), EntityType.DASHBOARD); } @@ -153,6 +175,8 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb } } + + @Override public Dashboard assignDashboardToCustomer(TenantId tenantId, DashboardId dashboardId, CustomerId customerId) { Dashboard dashboard = findDashboardById(tenantId, dashboardId); @@ -215,6 +239,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb dashboardDao.removeById(tenantId, dashboardId.getId()); publishEvictEvent(new DashboardTitleEvictEvent(dashboardId)); countService.publishCountEntityEvictEvent(tenantId, EntityType.DASHBOARD); + eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(dashboardId).build()); } catch (Exception t) { ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_default_dashboard_device_profile")) { @@ -304,6 +329,8 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb log.warn("[{}] Failed to create dashboard relation. Edge Id: [{}]", dashboardId, edgeId); throw new RuntimeException(e); } + eventPublisher.publishEvent(ActionEntityEvent.builder().tenantId(tenantId).edgeId(edgeId).entityId(dashboardId) + .actionType(ActionType.ASSIGNED_TO_EDGE).build()); return dashboard; } @@ -320,6 +347,8 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb log.warn("[{}] Failed to delete dashboard relation. Edge Id: [{}]", dashboardId, edgeId); throw new RuntimeException(e); } + eventPublisher.publishEvent(ActionEntityEvent.builder().tenantId(tenantId).edgeId(edgeId).entityId(dashboardId) + .actionType(ActionType.UNASSIGNED_FROM_EDGE).build()); return dashboard; } @@ -342,19 +371,18 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb return dashboardDao.findByTenantIdAndTitle(tenantId.getId(), title); } - private PaginatedRemover tenantDashboardsRemover = - new PaginatedRemover() { + private final PaginatedRemover tenantDashboardsRemover = new PaginatedRemover<>() { - @Override - protected PageData findEntities(TenantId tenantId, TenantId id, PageLink pageLink) { - return dashboardInfoDao.findDashboardsByTenantId(id.getId(), pageLink); - } + @Override + protected PageData findEntities(TenantId tenantId, TenantId id, PageLink pageLink) { + return dashboardDao.findIdsByTenantId(id, pageLink); + } - @Override - protected void removeEntity(TenantId tenantId, DashboardInfo entity) { - deleteDashboard(tenantId, new DashboardId(entity.getUuidId())); - } - }; + @Override + protected void removeEntity(TenantId tenantId, DashboardId dashboardId) { + deleteDashboard(tenantId, dashboardId); + } + }; @Override public Optional> findEntity(TenantId tenantId, EntityId entityId) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityConfiguration.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityConfiguration.java new file mode 100644 index 0000000000..df55eee2e4 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityConfiguration.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.device; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +@ConfigurationProperties(prefix = "device") +@Data +@Deprecated(since = "3.6.1") +public class DeviceConnectivityConfiguration { + private Map connectivity = new HashMap<>(); + + public DeviceConnectivityInfo getConnectivity(String protocol) { + return connectivity.get(protocol); + } + + public boolean isEnabled(String protocol) { + var info = connectivity.get(protocol); + return info != null && info.isEnabled(); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityInfo.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityInfo.java new file mode 100644 index 0000000000..0f8723fcfb --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityInfo.java @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.device; + +import lombok.Data; + +@Data +public class DeviceConnectivityInfo { + private boolean enabled; + private String host; + private String port; +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java new file mode 100644 index 0000000000..1cce6fd51c --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceConnectivityServiceImpl.java @@ -0,0 +1,342 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.device; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.openssl.PEMParser; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.Resource; +import org.springframework.stereotype.Service; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.ResourceUtils; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.common.data.security.DeviceCredentialsType; +import org.thingsboard.server.dao.settings.AdminSettingsService; +import org.thingsboard.server.dao.util.DeviceConnectivityUtil; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +import static org.thingsboard.server.dao.service.Validator.validateId; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.CHECK_DOCUMENTATION; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.COAP; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.COAPS; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.DOCKER; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTP; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.HTTPS; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.MQTT; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.MQTTS; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.getHost; +import static org.thingsboard.server.dao.util.DeviceConnectivityUtil.getPort; + +@Service("DeviceConnectivityDaoService") +@Slf4j +@RequiredArgsConstructor +public class DeviceConnectivityServiceImpl implements DeviceConnectivityService { + + public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; + public static final String INCORRECT_DEVICE_ID = "Incorrect deviceId "; + public static final String DEFAULT_DEVICE_TELEMETRY_TOPIC = "v1/devices/me/telemetry"; + public static final String HTTP_DEFAULT_PORT = "80"; + public static final String HTTPS_DEFAULT_PORT = "443"; + + private final Map certs = new ConcurrentHashMap<>(); + + private final DeviceCredentialsService deviceCredentialsService; + private final DeviceProfileService deviceProfileService; + private final AdminSettingsService adminSettingsService; + + @Value("${device.connectivity.mqtts.pem_cert_file:}") + private String mqttsPemCertFile; + + @Override + public JsonNode findDevicePublishTelemetryCommands(String baseUrl, Device device) throws URISyntaxException { + DeviceId deviceId = device.getId(); + log.trace("Executing findDevicePublishTelemetryCommands [{}]", deviceId); + validateId(deviceId, INCORRECT_DEVICE_ID + deviceId); + + DeviceCredentials creds = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), deviceId); + DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId()); + DeviceTransportType transportType = deviceProfile.getTransportType(); + + ObjectNode commands = JacksonUtil.newObjectNode(); + switch (transportType) { + case DEFAULT: + Optional.ofNullable(getHttpTransportPublishCommands(baseUrl, creds)) + .ifPresent(v -> commands.set(HTTP, v)); + Optional.ofNullable(getMqttTransportPublishCommands(baseUrl, creds)) + .ifPresent(v -> commands.set(MQTT, v)); + Optional.ofNullable(getCoapTransportPublishCommands(baseUrl, creds)) + .ifPresent(v -> commands.set(COAP, v)); + break; + case MQTT: + MqttDeviceProfileTransportConfiguration transportConfiguration = + (MqttDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration(); + //TODO: add sparkplug command with emulator (check SSL) + if (transportConfiguration.isSparkplug()) { + ObjectNode sparkplug = JacksonUtil.newObjectNode(); + sparkplug.put("sparkplug", CHECK_DOCUMENTATION); + commands.set(MQTT, sparkplug); + } else { + String topicName = transportConfiguration.getDeviceTelemetryTopic(); + + Optional.ofNullable(getMqttTransportPublishCommands(baseUrl, topicName, creds)) + .ifPresent(v -> commands.set(MQTT, v)); + } + break; + case COAP: + Optional.ofNullable(getCoapTransportPublishCommands(baseUrl, creds)) + .ifPresent(v -> commands.set(COAP, v)); + break; + default: + commands.put(transportType.name(), CHECK_DOCUMENTATION); + } + return commands; + } + + @Override + public Resource getPemCertFile(String protocol) { + return certs.computeIfAbsent(protocol, key -> { + DeviceConnectivityInfo connectivity = getConnectivity(protocol); + if (!MQTTS.equals(protocol) || connectivity == null) { + log.warn("Unknown connectivity protocol: {}", protocol); + return null; + } + + if (StringUtils.isNotBlank(mqttsPemCertFile) && ResourceUtils.resourceExists(this, mqttsPemCertFile)) { + try { + return getCert(mqttsPemCertFile); + } catch (Exception e) { + String msg = String.format("Failed to read %s server certificate!", protocol); + log.warn(msg); + throw new RuntimeException(msg, e); + } + } else { + return null; + } + }); + } + + @Override + public Resource createGatewayDockerComposeFile(String baseUrl, Device device) throws URISyntaxException { + String mqttType = isEnabled(MQTTS) ? MQTTS : MQTT; + DeviceConnectivityInfo properties = getConnectivity(mqttType); + DeviceCredentials creds = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId()); + return DeviceConnectivityUtil.getGatewayDockerComposeFile(baseUrl, properties, creds, mqttType); + } + + private DeviceConnectivityInfo getConnectivity(String protocol) { + AdminSettings connectivitySettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "connectivity"); + JsonNode connectivity; + if (connectivitySettings != null && (connectivity = connectivitySettings.getJsonValue()) != null) { + return JacksonUtil.convertValue(connectivity.get(protocol), DeviceConnectivityInfo.class); + } + return null; + } + + public boolean isEnabled(String protocol) { + var info = getConnectivity(protocol); + return info != null && info.isEnabled(); + } + + private Resource getCert(String path) throws Exception { + StringBuilder pemContentBuilder = new StringBuilder(); + + try (InputStream inStream = ResourceUtils.getInputStream(this, path); + PEMParser pemParser = new PEMParser(new InputStreamReader(inStream))) { + + Object object; + + while ((object = pemParser.readObject()) != null) { + if (object instanceof X509CertificateHolder) { + var certHolder = (X509CertificateHolder) object; + String certBase64 = Base64.getEncoder().encodeToString(certHolder.getEncoded()); + + pemContentBuilder.append("-----BEGIN CERTIFICATE-----\n"); + int index = 0; + while (index < certBase64.length()) { + pemContentBuilder.append(certBase64, index, Math.min(index + 64, certBase64.length())); + pemContentBuilder.append("\n"); + index += 64; + } + pemContentBuilder.append("-----END CERTIFICATE-----\n"); + } + } + } + + return new ByteArrayResource(pemContentBuilder.toString().getBytes(StandardCharsets.UTF_8)); + } + + private JsonNode getHttpTransportPublishCommands(String defaultHostname, DeviceCredentials deviceCredentials) throws URISyntaxException { + ObjectNode httpCommands = JacksonUtil.newObjectNode(); + Optional.ofNullable(getHttpPublishCommand(HTTP, defaultHostname, deviceCredentials)) + .ifPresent(v -> httpCommands.put(HTTP, v)); + Optional.ofNullable(getHttpPublishCommand(HTTPS, defaultHostname, deviceCredentials)) + .ifPresent(v -> httpCommands.put(HTTPS, v)); + return httpCommands.isEmpty() ? null : httpCommands; + } + + private String getHttpPublishCommand(String protocol, String baseUrl, DeviceCredentials deviceCredentials) throws URISyntaxException { + DeviceConnectivityInfo properties = getConnectivity(protocol); + if (properties == null || !properties.isEnabled() || + deviceCredentials.getCredentialsType() != DeviceCredentialsType.ACCESS_TOKEN) { + return null; + } + String hostName = getHost(baseUrl, properties, protocol); + String propertiesPort = getPort(properties); + String port = (propertiesPort.isEmpty() || HTTP_DEFAULT_PORT.equals(propertiesPort) || HTTPS_DEFAULT_PORT.equals(propertiesPort)) + ? "" : ":" + propertiesPort; + return DeviceConnectivityUtil.getHttpPublishCommand(protocol, hostName, port, deviceCredentials); + } + + private JsonNode getMqttTransportPublishCommands(String baseUrl, DeviceCredentials deviceCredentials) throws URISyntaxException { + return getMqttTransportPublishCommands(baseUrl, DEFAULT_DEVICE_TELEMETRY_TOPIC, deviceCredentials); + } + + private JsonNode getMqttTransportPublishCommands(String baseUrl, String topic, DeviceCredentials deviceCredentials) throws URISyntaxException { + ObjectNode mqttCommands = JacksonUtil.newObjectNode(); + + if (deviceCredentials.getCredentialsType() == DeviceCredentialsType.X509_CERTIFICATE) { + mqttCommands.put(MQTTS, CHECK_DOCUMENTATION); + return mqttCommands; + } + + ObjectNode dockerMqttCommands = JacksonUtil.newObjectNode(); + + if (isEnabled(MQTT)) { + Optional.ofNullable(getMqttPublishCommand(baseUrl, topic, deviceCredentials)). + ifPresent(v -> mqttCommands.put(MQTT, v)); + + Optional.ofNullable(getDockerMqttPublishCommand(MQTT, baseUrl, topic, deviceCredentials)) + .ifPresent(v -> dockerMqttCommands.put(MQTT, v)); + } + + if (isEnabled(MQTTS)) { + List mqttsPublishCommand = getMqttsPublishCommand(baseUrl, topic, deviceCredentials); + if (mqttsPublishCommand != null) { + ArrayNode arrayNode = mqttCommands.putArray(MQTTS); + mqttsPublishCommand.forEach(arrayNode::add); + } + + Optional.ofNullable(getDockerMqttPublishCommand(MQTTS, baseUrl, topic, deviceCredentials)) + .ifPresent(v -> dockerMqttCommands.put(MQTTS, v)); + } + + if (!dockerMqttCommands.isEmpty()) { + mqttCommands.set(DOCKER, dockerMqttCommands); + } + return mqttCommands.isEmpty() ? null : mqttCommands; + } + + private String getMqttPublishCommand(String baseUrl, String deviceTelemetryTopic, DeviceCredentials deviceCredentials) throws URISyntaxException { + DeviceConnectivityInfo properties = getConnectivity(MQTT); + String mqttHost = getHost(baseUrl, properties, MQTT); + String mqttPort = getPort(properties); + return DeviceConnectivityUtil.getMqttPublishCommand(MQTT, mqttHost, mqttPort, deviceTelemetryTopic, deviceCredentials); + } + + private List getMqttsPublishCommand(String baseUrl, String deviceTelemetryTopic, DeviceCredentials deviceCredentials) throws URISyntaxException { + DeviceConnectivityInfo properties = getConnectivity(MQTTS); + String mqttHost = getHost(baseUrl, properties, MQTTS); + String mqttPort = getPort(properties); + String pubCommand = DeviceConnectivityUtil.getMqttPublishCommand(MQTTS, mqttHost, mqttPort, deviceTelemetryTopic, deviceCredentials); + + ArrayList commands = new ArrayList<>(); + if (pubCommand != null) { + commands.add(DeviceConnectivityUtil.getCurlPemCertCommand(baseUrl, MQTTS)); + commands.add(pubCommand); + return commands; + } + return null; + } + + private String getDockerMqttPublishCommand(String protocol, String baseUrl, String deviceTelemetryTopic, DeviceCredentials deviceCredentials) throws URISyntaxException { + DeviceConnectivityInfo properties = getConnectivity(protocol); + String mqttHost = getHost(baseUrl, properties, protocol); + String mqttPort = getPort(properties); + return DeviceConnectivityUtil.getDockerMqttPublishCommand(protocol, baseUrl, mqttHost, mqttPort, deviceTelemetryTopic, deviceCredentials); + } + + private JsonNode getCoapTransportPublishCommands(String baseUrl, DeviceCredentials deviceCredentials) throws URISyntaxException { + ObjectNode coapCommands = JacksonUtil.newObjectNode(); + + if (deviceCredentials.getCredentialsType() == DeviceCredentialsType.X509_CERTIFICATE) { + coapCommands.put(COAPS, CHECK_DOCUMENTATION); + return coapCommands; + } + + ObjectNode dockerCoapCommands = JacksonUtil.newObjectNode(); + + if (isEnabled(COAP)) { + Optional.ofNullable(getCoapPublishCommand(COAP, baseUrl, deviceCredentials)) + .ifPresent(v -> coapCommands.put(COAP, v)); + + Optional.ofNullable(getDockerCoapPublishCommand(COAP, baseUrl, deviceCredentials)) + .ifPresent(v -> dockerCoapCommands.put(COAP, v)); + } + + if (isEnabled(COAPS)) { + Optional.ofNullable(getCoapPublishCommand(COAPS, baseUrl, deviceCredentials)) + .ifPresent(v -> coapCommands.put(COAPS, v)); + + Optional.ofNullable(getDockerCoapPublishCommand(COAPS, baseUrl, deviceCredentials)) + .ifPresent(v -> dockerCoapCommands.put(COAPS, v)); + } + + if (!dockerCoapCommands.isEmpty()) { + coapCommands.set(DOCKER, dockerCoapCommands); + } + + return coapCommands.isEmpty() ? null : coapCommands; + } + + private String getCoapPublishCommand(String protocol, String baseUrl, DeviceCredentials deviceCredentials) throws URISyntaxException { + DeviceConnectivityInfo properties = getConnectivity(protocol); + String hostName = getHost(baseUrl, properties, protocol); + String port = StringUtils.isBlank(properties.getPort()) ? "" : ":" + properties.getPort(); + return DeviceConnectivityUtil.getCoapPublishCommand(protocol, hostName, port, deviceCredentials); + } + + private String getDockerCoapPublishCommand(String protocol, String baseUrl, DeviceCredentials deviceCredentials) throws URISyntaxException { + DeviceConnectivityInfo properties = getConnectivity(protocol); + String host = getHost(baseUrl, properties, protocol); + String port = StringUtils.isBlank(properties.getPort()) ? "" : ":" + properties.getPort(); + return DeviceConnectivityUtil.getDockerCoapPublishCommand(protocol, host, port, deviceCredentials); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsDao.java index 30796934fb..36fe0cc70f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsDao.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.device; +import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.dao.Dao; @@ -53,4 +54,6 @@ public interface DeviceCredentialsDao extends Dao { */ DeviceCredentials findByCredentialsId(TenantId tenantId, String credentialsId); + DeviceCredentials removeByDeviceId(TenantId tenantId, DeviceId deviceId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java index 6e79b953ff..1d8935da44 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java @@ -25,6 +25,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.event.TransactionalEventListener; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MBootstrapClientCredential; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MBootstrapClientCredentials; @@ -41,6 +42,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.msg.EncryptionUtil; import org.thingsboard.server.dao.entity.AbstractCachedEntityService; +import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; import org.thingsboard.server.dao.service.DataValidator; @@ -80,7 +82,7 @@ public class DeviceCredentialsServiceImpl extends AbstractCachedEntityService deviceCredentialsDao.findByCredentialsId(TenantId.SYS_TENANT_ID, credentialsId), - false); + true); // caching null values is essential for permanently invalid requests } @Override @@ -107,6 +109,9 @@ public class DeviceCredentialsServiceImpl extends AbstractCachedEntityService, TenantEntityDao, ExportableEntit * * @return the list of tenant device type objects */ + @Deprecated(since = "3.6.2", forRemoval = true) ListenableFuture> findTenantDeviceTypesAsync(UUID tenantId); /** diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileDao.java index c221002413..95336e8f4a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileDao.java @@ -17,16 +17,19 @@ package org.thingsboard.server.dao.device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileInfo; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ExportableEntityDao; +import org.thingsboard.server.dao.ImageContainerDao; +import java.util.List; import java.util.UUID; -public interface DeviceProfileDao extends Dao, ExportableEntityDao { +public interface DeviceProfileDao extends Dao, ExportableEntityDao, ImageContainerDao { DeviceProfileInfo findDeviceProfileInfoById(TenantId tenantId, UUID deviceProfileId); @@ -45,4 +48,9 @@ public interface DeviceProfileDao extends Dao, ExportableEntityDa DeviceProfile findByProvisionDeviceKey(String provisionDeviceKey); DeviceProfile findByName(TenantId tenantId, String profileName); + + PageData findAllWithImages(PageLink pageLink); + + List findTenantDeviceProfileNames(UUID tenantId, boolean activeOnly); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java index 14d6098975..7e3567c888 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceProfileProvisionType; import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; @@ -43,8 +44,11 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.msg.EncryptionUtil; import org.thingsboard.server.dao.entity.AbstractCachedEntityService; +import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; +import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.queue.QueueService; +import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; @@ -54,11 +58,13 @@ import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validateString; @@ -89,6 +95,9 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService deviceProfileDao.findById(tenantId, deviceProfileId.getId()), true); + return cache.getOrFetchFromDB(DeviceProfileCacheKey.fromId(deviceProfileId), + () -> deviceProfileDao.findById(tenantId, deviceProfileId.getId()), true, putInCache); } @Override public DeviceProfile findDeviceProfileByName(TenantId tenantId, String profileName) { + return findDeviceProfileByName(tenantId, profileName, true); + } + + @Override + public DeviceProfile findDeviceProfileByName(TenantId tenantId, String profileName, boolean putInCache) { log.trace("Executing findDeviceProfileByName [{}][{}]", tenantId, profileName); validateString(profileName, INCORRECT_DEVICE_PROFILE_NAME + profileName); - return cache.getAndPutInTransaction(DeviceProfileCacheKey.fromName(tenantId, profileName), - () -> deviceProfileDao.findByName(tenantId, profileName), true); + return cache.getOrFetchFromDB(DeviceProfileCacheKey.fromName(tenantId, profileName), + () -> deviceProfileDao.findByName(tenantId, profileName), true, putInCache); } @Override @@ -140,8 +159,17 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService tenantDeviceProfilesRemover = + @Override + public List findDeviceProfileNamesByTenantId(TenantId tenantId, boolean activeOnly) { + log.trace("Executing findDeviceProfileNamesByTenantId, tenantId [{}]", tenantId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + return deviceProfileDao.findTenantDeviceProfileNames(tenantId.getId(), activeOnly) + .stream().sorted(Comparator.comparing(EntityInfo::getName)) + .collect(Collectors.toList()); + } + + private final PaginatedRemover tenantDeviceProfilesRemover = new PaginatedRemover<>() { @Override @@ -388,7 +434,8 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService 1) { return EncryptionUtil.certTrimNewLinesForChainInDeviceProfile(certificateValue); } - } catch (CertificateException ignored) {} + } catch (CertificateException ignored) { + } return EncryptionUtil.certTrimNewLines(certificateValue); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index 46830f8f76..fcedc94c9c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.device.DeviceSearchQuery; import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; import org.thingsboard.server.common.data.device.data.CoapDeviceTransportConfiguration; @@ -69,6 +70,9 @@ import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; import org.thingsboard.server.dao.entity.AbstractCachedEntityService; import org.thingsboard.server.dao.entity.EntityCountService; import org.thingsboard.server.dao.event.EventService; +import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent; +import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; +import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.service.DataValidator; @@ -126,15 +130,13 @@ public class DeviceServiceImpl extends AbstractCachedEntityService { - //TODO: possible bug source since sometimes we need to clear cache by tenant id and sometimes by sys tenant id? - if (TenantId.SYS_TENANT_ID.equals(tenantId)) { - return deviceDao.findById(tenantId, deviceId.getId()); - } else { - return deviceDao.findDeviceByTenantIdAndId(tenantId, deviceId.getId()); - } - }, true); + if (TenantId.SYS_TENANT_ID.equals(tenantId)) { + return cache.getAndPutInTransaction(new DeviceCacheKey(deviceId), + () -> deviceDao.findById(tenantId, deviceId.getId()), true); + } else { + return cache.getAndPutInTransaction(new DeviceCacheKey(tenantId, deviceId), + () -> deviceDao.findDeviceByTenantIdAndId(tenantId, deviceId.getId()), true); + } } @Override @@ -174,10 +176,6 @@ public class DeviceServiceImpl extends AbstractCachedEntityService keys = new ArrayList<>(3); keys.add(new DeviceCacheKey(event.getTenantId(), event.getNewName())); if (event.getDeviceId() != null) { + keys.add(new DeviceCacheKey(event.getDeviceId())); keys.add(new DeviceCacheKey(event.getTenantId(), event.getDeviceId())); } if (StringUtils.isNotEmpty(event.getOldName()) && !event.getOldName().equals(event.getNewName())) { @@ -314,26 +315,27 @@ public class DeviceServiceImpl extends AbstractCachedEntityService entityViews = entityViewService.findEntityViewsByTenantIdAndEntityId(device.getTenantId(), deviceId); - if (entityViews != null && !entityViews.isEmpty()) { + if (entityViewService.existsByTenantIdAndEntityId(tenantId, deviceId)) { throw new DataValidationException("Can't delete device that has entity views!"); } - DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, deviceId); - if (deviceCredentials != null) { - deviceCredentialsService.deleteDeviceCredentials(tenantId, deviceCredentials); - } - deleteEntityRelations(tenantId, deviceId); + Device device = deviceDao.findById(tenantId, deviceId.getId()); + alarmService.deleteEntityAlarmRelations(tenantId, deviceId); + deleteDevice(tenantId, device); + } + + private void deleteDevice(TenantId tenantId, Device device) { + log.trace("Executing deleteDevice [{}]", device.getId()); + deviceCredentialsService.deleteDeviceCredentialsByDeviceId(tenantId, device.getId()); + relationService.deleteEntityRelations(tenantId, device.getId()); - deviceDao.removeById(tenantId, deviceId.getId()); + deviceDao.removeById(tenantId, device.getUuidId()); + DeviceCacheEvictEvent deviceCacheEvictEvent = new DeviceCacheEvictEvent(device.getTenantId(), device.getId(), device.getName(), null); publishEvictEvent(deviceCacheEvictEvent); countService.publishCountEntityEvictEvent(tenantId, EntityType.DEVICE); + eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(device.getId()).build()); } @Override @@ -484,12 +486,7 @@ public class DeviceServiceImpl extends AbstractCachedEntityService> findDeviceTypesByTenantId(TenantId tenantId) { log.trace("Executing findDeviceTypesByTenantId, tenantId [{}]", tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - ListenableFuture> tenantDeviceTypes = deviceDao.findTenantDeviceTypesAsync(tenantId.getId()); - return Futures.transform(tenantDeviceTypes, - deviceTypes -> { - deviceTypes.sort(Comparator.comparing(EntitySubtype::getType)); - return deviceTypes; - }, MoreExecutors.directExecutor()); + return deviceDao.findTenantDeviceTypesAsync(tenantId.getId()); } @Transactional @@ -588,6 +585,8 @@ public class DeviceServiceImpl extends AbstractCachedEntityService tenantDevicesRemover = - new PaginatedRemover<>() { + private final PaginatedRemover tenantDevicesRemover = new PaginatedRemover<>() { - @Override - protected PageData findEntities(TenantId tenantId, TenantId id, PageLink pageLink) { - return deviceDao.findDevicesByTenantId(id.getId(), pageLink); - } + @Override + protected PageData findEntities(TenantId tenantId, TenantId id, PageLink pageLink) { + return deviceDao.findDevicesByTenantId(id.getId(), pageLink); + } - @Override - protected void removeEntity(TenantId tenantId, Device entity) { - deleteDevice(tenantId, new DeviceId(entity.getUuidId())); - } - }; + @Override + protected void removeEntity(TenantId tenantId, Device device) { + deleteDevice(tenantId, device); + } + }; - private PaginatedRemover customerDeviceUnasigner = new PaginatedRemover() { + private final PaginatedRemover customerDeviceUnasigner = new PaginatedRemover() { @Override protected PageData findEntities(TenantId tenantId, CustomerId id, PageLink pageLink) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java b/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java index 0fc451cf18..82058761f8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java @@ -42,8 +42,8 @@ public class BaseEdgeEventService implements EdgeEventService { } @Override - public PageData findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink, boolean withTsUpdate) { - return edgeEventDao.findEdgeEvents(tenantId.getId(), edgeId, pageLink, withTsUpdate); + public PageData findEdgeEvents(TenantId tenantId, EdgeId edgeId, Long seqIdStart, Long seqIdEnd, TimePageLink pageLink) { + return edgeEventDao.findEdgeEvents(tenantId.getId(), edgeId, seqIdStart, seqIdEnd, pageLink); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/DefaultEdgeSynchronizationManager.java b/dao/src/main/java/org/thingsboard/server/dao/edge/DefaultEdgeSynchronizationManager.java new file mode 100644 index 0000000000..3d7c48efbd --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/DefaultEdgeSynchronizationManager.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.edge; + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.EdgeId; + +@Component +@Slf4j +public class DefaultEdgeSynchronizationManager implements EdgeSynchronizationManager { + + @Getter + private final ThreadLocal edgeId = new ThreadLocal<>(); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java index 17d512a786..58d96fe33b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java @@ -170,4 +170,12 @@ public interface EdgeDao extends Dao { */ PageData findEdgesByTenantIdAndEntityId(UUID tenantId, UUID entityId, EntityType entityType, PageLink pageLink); -} \ No newline at end of file + /** + * Find edges by tenantProfileId. + * + * @param tenantProfileId the tenantProfileId + * @return the list of edge objects + */ + PageData findEdgesByTenantProfileId(UUID tenantProfileId, PageLink pageLink); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeEventDao.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeEventDao.java index 84bf8c40d2..942a536674 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeEventDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeEventDao.java @@ -43,10 +43,12 @@ public interface EdgeEventDao extends Dao { * * @param tenantId the tenantId * @param edgeId the edgeId + * @param seqIdStart the seq id start + * @param seqIdEnd the seq id end * @param pageLink the pageLink * @return the event list */ - PageData findEdgeEvents(UUID tenantId, EdgeId edgeId, TimePageLink pageLink, boolean withTsUpdate); + PageData findEdgeEvents(UUID tenantId, EdgeId edgeId, Long seqIdStart, Long seqIdEnd, TimePageLink pageLink); /** * Executes stored procedure to cleanup old edge events. diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java index 31b446fbd5..4e7e3f0892 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java @@ -33,7 +33,9 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeInfo; import org.thingsboard.server.common.data.edge.EdgeSearchQuery; @@ -44,8 +46,10 @@ import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.IdBased; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.common.data.page.PageDataIterableByTenantIdEntityId; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; @@ -53,12 +57,14 @@ import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.dao.entity.AbstractCachedEntityService; +import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; +import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.user.UserService; import javax.annotation.Nullable; @@ -98,6 +104,9 @@ public class EdgeServiceImpl extends AbstractCachedEntityService edgeValidator; @@ -179,6 +188,8 @@ public class EdgeServiceImpl extends AbstractCachedEntityService findEdgesByTenantProfileId(TenantProfileId tenantProfileId, PageLink pageLink) { + log.trace("Executing findEdgesByTenantProfileId, tenantProfileId [{}], pageLink [{}]", tenantProfileId, pageLink); + Validator.validateId(tenantProfileId, "Incorrect tenantProfileId " + tenantProfileId); + validatePageLink(pageLink); + return edgeDao.findEdgesByTenantProfileId(tenantProfileId.getId(), pageLink); + } + private PaginatedRemover tenantEdgesRemover = new PaginatedRemover() { @@ -451,6 +474,8 @@ public class EdgeServiceImpl extends AbstractCachedEntityService cache; - @Autowired - private ApplicationEventPublisher eventPublisher; - protected void publishEvictEvent(E event) { if (TransactionSynchronizationManager.isActualTransactionActive()) { eventPublisher.publishEvent(event); diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java index 6d4ac9f97b..7c77f48710 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.entity; import lombok.extern.slf4j.Slf4j; import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Lazy; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.StringUtils; @@ -43,6 +44,9 @@ public abstract class AbstractEntityService { public static final String INCORRECT_EDGE_ID = "Incorrect edgeId "; public static final String INCORRECT_PAGE_LINK = "Incorrect page link "; + @Autowired + protected ApplicationEventPublisher eventPublisher; + @Lazy @Autowired protected RelationService relationService; @@ -70,9 +74,7 @@ public abstract class AbstractEntityService { } protected void deleteEntityRelations(TenantId tenantId, EntityId entityId) { - log.trace("Executing deleteEntityRelations [{}]", entityId); relationService.deleteEntityRelations(tenantId, entityId); - log.trace("Executing deleteEntityAlarms [{}]", entityId); alarmService.deleteEntityAlarmRelations(tenantId, entityId); } @@ -113,7 +115,7 @@ public abstract class AbstractEntityService { List entityViews = entityViewService.findEntityViewsByTenantIdAndEntityId(tenantId, entityId); if (entityViews != null && !entityViews.isEmpty()) { EntityView entityView = entityViews.get(0); - Boolean relationExists = relationService.checkRelation( + boolean relationExists = relationService.checkRelation( tenantId, edgeId, entityView.getId(), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE ); @@ -122,5 +124,4 @@ public abstract class AbstractEntityService { } } } - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java index 132d26a406..7ce2c6fabf 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java @@ -148,6 +148,8 @@ public interface EntityViewDao extends Dao, ExportableEntityDao findEntityViewsByTenantIdAndEntityId(UUID tenantId, UUID entityId); + boolean existsByTenantIdAndEntityId(UUID tenantId, UUID entityId); + /** * Find tenants entity view types. * diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java index 6433db5ae8..d934bb766e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityViewInfo; import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; import org.thingsboard.server.common.data.id.CustomerId; @@ -43,6 +44,9 @@ import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.entity.AbstractCachedEntityService; +import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent; +import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; +import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -97,13 +101,29 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService entityViewDao.findById(tenantId, entityViewId.getId()) - , EntityViewCacheValue::getEntityView, v -> new EntityViewCacheValue(v, null), true); + , EntityViewCacheValue::getEntityView, v -> new EntityViewCacheValue(v, null), true, putInCache); } @Override @@ -296,6 +321,11 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService new EntityViewCacheValue(null, v), true); } + @Override + public boolean existsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId) { + return entityViewDao.existsByTenantIdAndEntityId(tenantId.getId(), entityId.getId()); + } + @Override @Transactional public void deleteEntityView(TenantId tenantId, EntityViewId entityViewId) { @@ -305,6 +335,7 @@ public class EntityViewServiceImpl extends AbstractCachedEntityService void truncateField(T event, Function getter, BiConsumer setter) { var str = getter.apply(event); - if (StringUtils.isNotEmpty(str)) { - var length = str.length(); - if (length > maxDebugEventSymbols) { - setter.accept(event, str.substring(0, maxDebugEventSymbols) + "...[truncated " + (length - maxDebugEventSymbols) + " symbols]"); - } - } + str = StringUtils.truncate(str, maxDebugEventSymbols); + setter.accept(event, str); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/eventsourcing/ActionEntityEvent.java b/dao/src/main/java/org/thingsboard/server/dao/eventsourcing/ActionEntityEvent.java new file mode 100644 index 0000000000..732064999a --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/eventsourcing/ActionEntityEvent.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.eventsourcing; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; + +@Data +@Builder +public class ActionEntityEvent { + private final TenantId tenantId; + private final EdgeId edgeId; + private final EntityId entityId; + private final String body; + private final ActionType actionType; +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/eventsourcing/DeleteEntityEvent.java b/dao/src/main/java/org/thingsboard/server/dao/eventsourcing/DeleteEntityEvent.java new file mode 100644 index 0000000000..7c4caa9618 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/eventsourcing/DeleteEntityEvent.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.eventsourcing; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; + +@Builder +@Data +public class DeleteEntityEvent { + private final TenantId tenantId; + private final EntityId entityId; + private final T entity; + + @Builder.Default + private final long ts = System.currentTimeMillis(); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/eventsourcing/RelationActionEvent.java b/dao/src/main/java/org/thingsboard/server/dao/eventsourcing/RelationActionEvent.java new file mode 100644 index 0000000000..81437cd781 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/eventsourcing/RelationActionEvent.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.eventsourcing; + +import lombok.Data; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.relation.EntityRelation; + +@Data +public class RelationActionEvent { + private final TenantId tenantId; + private final EntityRelation relation; + private final ActionType actionType; +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/eventsourcing/SaveEntityEvent.java b/dao/src/main/java/org/thingsboard/server/dao/eventsourcing/SaveEntityEvent.java new file mode 100644 index 0000000000..cc2e854f59 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/eventsourcing/SaveEntityEvent.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.eventsourcing; + +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; + +@Builder +@Data +public class SaveEntityEvent { + private final TenantId tenantId; + private final T entity; + private final T oldEntity; + private final EntityId entityId; + private final Boolean added; +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/exception/EntitiesLimitException.java b/dao/src/main/java/org/thingsboard/server/dao/exception/EntitiesLimitException.java new file mode 100644 index 0000000000..2f618e94d5 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/exception/EntitiesLimitException.java @@ -0,0 +1,35 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.exception; + +import lombok.Getter; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.TenantId; + +public class EntitiesLimitException extends DataValidationException { + private static final long serialVersionUID = -9211462514373279196L; + + @Getter + private final TenantId tenantId; + @Getter + private final EntityType entityType; + + public EntitiesLimitException(TenantId tenantId, EntityType entityType) { + super(entityType.getNormalName() + "s limit reached"); + this.tenantId = tenantId; + this.entityType = entityType; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index ccbd029595..dbbacb5358 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -302,19 +302,34 @@ public class ModelConstants { public static final String WIDGETS_BUNDLE_TITLE_PROPERTY = TITLE_PROPERTY; public static final String WIDGETS_BUNDLE_IMAGE_PROPERTY = "image"; public static final String WIDGETS_BUNDLE_DESCRIPTION = "description"; + public static final String WIDGETS_BUNDLE_ORDER = "widgets_bundle_order"; /** * Widget_type constants. */ public static final String WIDGET_TYPE_TABLE_NAME = "widget_type"; public static final String WIDGET_TYPE_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; - public static final String WIDGET_TYPE_BUNDLE_ALIAS_PROPERTY = "bundle_alias"; - public static final String WIDGET_TYPE_ALIAS_PROPERTY = ALIAS_PROPERTY; + + public static final String WIDGET_TYPE_FQN_PROPERTY = "fqn"; public static final String WIDGET_TYPE_NAME_PROPERTY = "name"; public static final String WIDGET_TYPE_IMAGE_PROPERTY = "image"; public static final String WIDGET_TYPE_DESCRIPTION_PROPERTY = "description"; + public static final String WIDGET_TYPE_TAGS_PROPERTY = "tags"; public static final String WIDGET_TYPE_DESCRIPTOR_PROPERTY = "descriptor"; + public static final String WIDGET_TYPE_DEPRECATED_PROPERTY = "deprecated"; + + public static final String WIDGET_TYPE_WIDGET_TYPE_PROPERTY = "widget_type"; + + public static final String WIDGET_TYPE_INFO_VIEW_TABLE_NAME = "widget_type_info_view"; + + /** + * Widgets bundle widget constants. + */ + public static final String WIDGETS_BUNDLE_WIDGET_TABLE_NAME = "widgets_bundle_widget"; + + public static final String WIDGET_TYPE_ORDER_PROPERTY = "widget_type_order"; + /** * Dashboard constants. */ @@ -337,6 +352,7 @@ public class ModelConstants { public static final String COMPONENT_DESCRIPTOR_NAME_PROPERTY = "name"; public static final String COMPONENT_DESCRIPTOR_CLASS_PROPERTY = "clazz"; public static final String COMPONENT_DESCRIPTOR_CONFIGURATION_DESCRIPTOR_PROPERTY = "configuration_descriptor"; + public static final String COMPONENT_DESCRIPTOR_CONFIGURATION_VERSION_PROPERTY = "configuration_version"; public static final String COMPONENT_DESCRIPTOR_ACTIONS_PROPERTY = "actions"; /** @@ -392,6 +408,7 @@ public class ModelConstants { public static final String RULE_NODE_CHAIN_ID_PROPERTY = "rule_chain_id"; public static final String RULE_NODE_TYPE_PROPERTY = "type"; public static final String RULE_NODE_NAME_PROPERTY = "name"; + public static final String RULE_NODE_VERSION_PROPERTY = "configuration_version"; public static final String RULE_NODE_CONFIGURATION_PROPERTY = "configuration"; /** @@ -466,6 +483,7 @@ public class ModelConstants { public static final String API_USAGE_STATE_DB_STORAGE_COLUMN = "db_storage"; public static final String API_USAGE_STATE_RE_EXEC_COLUMN = "re_exec"; public static final String API_USAGE_STATE_JS_EXEC_COLUMN = "js_exec"; + public static final String API_USAGE_STATE_TBEL_EXEC_COLUMN = "tbel_exec"; public static final String API_USAGE_STATE_EMAIL_EXEC_COLUMN = "email_exec"; public static final String API_USAGE_STATE_SMS_EXEC_COLUMN = "sms_exec"; public static final String API_USAGE_STATE_ALARM_EXEC_COLUMN = "alarm_exec"; @@ -481,6 +499,10 @@ public class ModelConstants { public static final String RESOURCE_FILE_NAME_COLUMN = "file_name"; public static final String RESOURCE_DATA_COLUMN = "data"; public static final String RESOURCE_ETAG_COLUMN = "etag"; + public static final String RESOURCE_DESCRIPTOR_COLUMN = "descriptor"; + public static final String RESOURCE_PREVIEW_COLUMN = "preview"; + public static final String RESOURCE_IS_PUBLIC_COLUMN = "is_public"; + public static final String PUBLIC_RESOURCE_KEY_COLUMN = "public_resource_key"; /** * Ota Package constants. @@ -533,6 +555,7 @@ public class ModelConstants { */ public static final String EDGE_EVENT_TABLE_NAME = "edge_event"; public static final String EDGE_EVENT_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; + public static final String EDGE_EVENT_SEQUENTIAL_ID_PROPERTY = "seq_id"; public static final String EDGE_EVENT_EDGE_ID_PROPERTY = "edge_id"; public static final String EDGE_EVENT_TYPE_PROPERTY = "edge_event_type"; public static final String EDGE_EVENT_ACTION_PROPERTY = "edge_event_action"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractWidgetTypeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractWidgetTypeEntity.java index 127e8509b7..fa700315d0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractWidgetTypeEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractWidgetTypeEntity.java @@ -31,20 +31,20 @@ import java.util.UUID; @Data @EqualsAndHashCode(callSuper = true) @MappedSuperclass -public abstract class AbstractWidgetTypeEntity extends BaseSqlEntity implements BaseEntity { +public abstract class AbstractWidgetTypeEntity extends BaseSqlEntity { @Column(name = ModelConstants.WIDGET_TYPE_TENANT_ID_PROPERTY) private UUID tenantId; - @Column(name = ModelConstants.WIDGET_TYPE_BUNDLE_ALIAS_PROPERTY) - private String bundleAlias; - - @Column(name = ModelConstants.WIDGET_TYPE_ALIAS_PROPERTY) - private String alias; + @Column(name = ModelConstants.WIDGET_TYPE_FQN_PROPERTY) + private String fqn; @Column(name = ModelConstants.WIDGET_TYPE_NAME_PROPERTY) private String name; + @Column(name = ModelConstants.WIDGET_TYPE_DEPRECATED_PROPERTY) + private boolean deprecated; + public AbstractWidgetTypeEntity() { super(); } @@ -57,18 +57,18 @@ public abstract class AbstractWidgetTypeEntity extends if (widgetType.getTenantId() != null) { this.tenantId = widgetType.getTenantId().getId(); } - this.bundleAlias = widgetType.getBundleAlias(); - this.alias = widgetType.getAlias(); + this.fqn = widgetType.getFqn(); this.name = widgetType.getName(); + this.deprecated = widgetType.isDeprecated(); } public AbstractWidgetTypeEntity(AbstractWidgetTypeEntity widgetTypeEntity) { this.setId(widgetTypeEntity.getId()); this.setCreatedTime(widgetTypeEntity.getCreatedTime()); this.tenantId = widgetTypeEntity.getTenantId(); - this.bundleAlias = widgetTypeEntity.getBundleAlias(); - this.alias = widgetTypeEntity.getAlias(); + this.fqn = widgetTypeEntity.getFqn(); this.name = widgetTypeEntity.getName(); + this.deprecated = widgetTypeEntity.isDeprecated(); } protected BaseWidgetType toBaseWidgetType() { @@ -77,9 +77,9 @@ public abstract class AbstractWidgetTypeEntity extends if (tenantId != null) { widgetType.setTenantId(TenantId.fromUUID(tenantId)); } - widgetType.setBundleAlias(bundleAlias); - widgetType.setAlias(alias); + widgetType.setFqn(fqn); widgetType.setName(name); + widgetType.setDeprecated(deprecated); return widgetType; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/ApiUsageStateEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/ApiUsageStateEntity.java index b18f414c63..8b92002db5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/ApiUsageStateEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/ApiUsageStateEntity.java @@ -64,6 +64,9 @@ public class ApiUsageStateEntity extends BaseSqlEntity implements @Column(name = ModelConstants.API_USAGE_STATE_JS_EXEC_COLUMN) private ApiUsageStateValue jsExecState = ApiUsageStateValue.ENABLED; @Enumerated(EnumType.STRING) + @Column(name = ModelConstants.API_USAGE_STATE_TBEL_EXEC_COLUMN) + private ApiUsageStateValue tbelExecState = ApiUsageStateValue.ENABLED; + @Enumerated(EnumType.STRING) @Column(name = ModelConstants.API_USAGE_STATE_EMAIL_EXEC_COLUMN) private ApiUsageStateValue emailExecState = ApiUsageStateValue.ENABLED; @Enumerated(EnumType.STRING) @@ -92,6 +95,7 @@ public class ApiUsageStateEntity extends BaseSqlEntity implements this.dbStorageState = ur.getDbStorageState(); this.reExecState = ur.getReExecState(); this.jsExecState = ur.getJsExecState(); + this.tbelExecState = ur.getTbelExecState(); this.emailExecState = ur.getEmailExecState(); this.smsExecState = ur.getSmsExecState(); this.alarmExecState = ur.getAlarmExecState(); @@ -111,6 +115,7 @@ public class ApiUsageStateEntity extends BaseSqlEntity implements ur.setDbStorageState(dbStorageState); ur.setReExecState(reExecState); ur.setJsExecState(jsExecState); + ur.setTbelExecState(tbelExecState); ur.setEmailExecState(emailExecState); ur.setSmsExecState(smsExecState); ur.setAlarmExecState(alarmExecState); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/ComponentDescriptorEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/ComponentDescriptorEntity.java index 47e8e258fc..a476d69c97 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/ComponentDescriptorEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/ComponentDescriptorEntity.java @@ -64,6 +64,9 @@ public class ComponentDescriptorEntity extends BaseSqlEntity implements BaseEntity { + @Column(name = EDGE_EVENT_SEQUENTIAL_ID_PROPERTY) + protected long seqId; + @Column(name = EDGE_EVENT_TENANT_ID_PROPERTY) private UUID tenantId; @@ -120,6 +124,7 @@ public class EdgeEventEntity extends BaseSqlEntity implements BaseEnt edgeEvent.setAction(edgeEventAction); edgeEvent.setBody(entityBody); edgeEvent.setUid(edgeEventUid); + edgeEvent.setSeqId(seqId); return edgeEvent; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRuleEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRuleEntity.java index 15283c9fc1..e7a4b6a571 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRuleEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRuleEntity.java @@ -25,8 +25,8 @@ import org.thingsboard.server.common.data.id.NotificationTemplateId; import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.rule.NotificationRuleConfig; import org.thingsboard.server.common.data.notification.rule.NotificationRuleRecipientsConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.util.mapping.JsonStringType; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.java index 5c92394245..c984f7b1d4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.java @@ -49,6 +49,9 @@ public class RuleNodeEntity extends BaseSqlEntity { @Column(name = ModelConstants.RULE_NODE_NAME_PROPERTY) private String name; + @Column(name = ModelConstants.RULE_NODE_VERSION_PROPERTY) + private int configurationVersion; + @Type(type = "json") @Column(name = ModelConstants.RULE_NODE_CONFIGURATION_PROPERTY) private JsonNode configuration; @@ -81,6 +84,7 @@ public class RuleNodeEntity extends BaseSqlEntity { this.name = ruleNode.getName(); this.debugMode = ruleNode.isDebugMode(); this.singletonMode = ruleNode.isSingletonMode(); + this.configurationVersion = ruleNode.getConfigurationVersion(); this.configuration = ruleNode.getConfiguration(); this.additionalInfo = ruleNode.getAdditionalInfo(); if (ruleNode.getExternalId() != null) { @@ -99,6 +103,7 @@ public class RuleNodeEntity extends BaseSqlEntity { ruleNode.setName(name); ruleNode.setDebugMode(debugMode); ruleNode.setSingletonMode(singletonMode); + ruleNode.setConfigurationVersion(configurationVersion); ruleNode.setConfiguration(configuration); ruleNode.setAdditionalInfo(additionalInfo); if (externalId != null) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java index 4fbc50fd69..eec109278c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java @@ -15,24 +15,32 @@ */ package org.thingsboard.server.dao.model.sql; +import com.fasterxml.jackson.databind.JsonNode; import lombok.Data; import lombok.EqualsAndHashCode; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.model.BaseEntity; import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; import java.util.UUID; +import static org.thingsboard.server.dao.model.ModelConstants.EXTERNAL_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_DATA_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_FILE_NAME_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_DESCRIPTOR_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_ETAG_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_FILE_NAME_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_IS_PUBLIC_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_KEY_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_PREVIEW_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.PUBLIC_RESOURCE_KEY_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAME; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TITLE_COLUMN; @@ -42,8 +50,9 @@ import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPER @Data @EqualsAndHashCode(callSuper = true) @Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) @Table(name = RESOURCE_TABLE_NAME) -public class TbResourceEntity extends BaseSqlEntity implements BaseEntity { +public class TbResourceEntity extends BaseSqlEntity { @Column(name = RESOURCE_TENANT_ID_COLUMN, columnDefinition = "uuid") private UUID tenantId; @@ -64,11 +73,27 @@ public class TbResourceEntity extends BaseSqlEntity implements BaseE private String fileName; @Column(name = RESOURCE_DATA_COLUMN) - private String data; + private byte[] data; @Column(name = RESOURCE_ETAG_COLUMN) private String etag; + @Type(type = "json") + @Column(name = RESOURCE_DESCRIPTOR_COLUMN) + private JsonNode descriptor; + + @Column(name = RESOURCE_PREVIEW_COLUMN) + private byte[] preview; + + @Column(name = RESOURCE_IS_PUBLIC_COLUMN) + private Boolean isPublic; + + @Column(name = PUBLIC_RESOURCE_KEY_COLUMN, unique = true, updatable = false) + private String publicResourceKey; + + @Column(name = EXTERNAL_ID_PROPERTY) + private UUID externalId; + public TbResourceEntity() { } @@ -87,6 +112,11 @@ public class TbResourceEntity extends BaseSqlEntity implements BaseE this.fileName = resource.getFileName(); this.data = resource.getData(); this.etag = resource.getEtag(); + this.descriptor = resource.getDescriptor(); + this.preview = resource.getPreview(); + this.isPublic = resource.isPublic(); + this.publicResourceKey = resource.getPublicResourceKey(); + this.externalId = getUuid(resource.getExternalId()); } @Override @@ -101,6 +131,11 @@ public class TbResourceEntity extends BaseSqlEntity implements BaseE resource.setFileName(fileName); resource.setData(data); resource.setEtag(etag); + resource.setDescriptor(descriptor); + resource.setPreview(preview); + resource.setPublic(isPublic == null || isPublic); + resource.setPublicResourceKey(publicResourceKey); + resource.setExternalId(getEntityId(externalId, TbResourceId::new)); return resource; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java index e8504bc388..c7f05d93b4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java @@ -15,22 +15,31 @@ */ package org.thingsboard.server.dao.model.sql; +import com.fasterxml.jackson.databind.JsonNode; import lombok.Data; import lombok.EqualsAndHashCode; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseEntity; import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; import java.util.UUID; +import static org.thingsboard.server.dao.model.ModelConstants.EXTERNAL_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_DESCRIPTOR_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_ETAG_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_FILE_NAME_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_IS_PUBLIC_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_KEY_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.PUBLIC_RESOURCE_KEY_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAME; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TITLE_COLUMN; @@ -40,6 +49,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPER @Data @EqualsAndHashCode(callSuper = true) @Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) @Table(name = RESOURCE_TABLE_NAME) public class TbResourceInfoEntity extends BaseSqlEntity implements BaseEntity { @@ -59,7 +69,23 @@ public class TbResourceInfoEntity extends BaseSqlEntity implemen private String searchText; @Column(name = RESOURCE_ETAG_COLUMN) - private String hashCode; + private String etag; + + @Column(name = RESOURCE_FILE_NAME_COLUMN) + private String fileName; + + @Type(type = "json") + @Column(name = RESOURCE_DESCRIPTOR_COLUMN) + private JsonNode descriptor; + + @Column(name = RESOURCE_IS_PUBLIC_COLUMN) + private Boolean isPublic; + + @Column(name = PUBLIC_RESOURCE_KEY_COLUMN, unique = true, updatable = false) + private String publicResourceKey; + + @Column(name = EXTERNAL_ID_PROPERTY) + private UUID externalId; public TbResourceInfoEntity() { } @@ -74,7 +100,12 @@ public class TbResourceInfoEntity extends BaseSqlEntity implemen this.resourceType = resource.getResourceType().name(); this.resourceKey = resource.getResourceKey(); this.searchText = resource.getSearchText(); - this.hashCode = resource.getEtag(); + this.etag = resource.getEtag(); + this.fileName = resource.getFileName(); + this.descriptor = resource.getDescriptor(); + this.isPublic = resource.isPublic(); + this.publicResourceKey = resource.getPublicResourceKey(); + this.externalId = getUuid(resource.getExternalId()); } @Override @@ -86,7 +117,12 @@ public class TbResourceInfoEntity extends BaseSqlEntity implemen resource.setResourceType(ResourceType.valueOf(resourceType)); resource.setResourceKey(resourceKey); resource.setSearchText(searchText); - resource.setEtag(hashCode); + resource.setEtag(etag); + resource.setFileName(fileName); + resource.setDescriptor(descriptor); + resource.setPublic(isPublic == null || isPublic); + resource.setPublicResourceKey(publicResourceKey); + resource.setExternalId(getEntityId(externalId, TbResourceId::new)); return resource; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetTypeDetailsEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetTypeDetailsEntity.java index c936952b40..eec70be387 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetTypeDetailsEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetTypeDetailsEntity.java @@ -16,10 +16,12 @@ package org.thingsboard.server.dao.model.sql; import com.fasterxml.jackson.databind.JsonNode; +import io.hypersistence.utils.hibernate.type.array.StringArrayType; import lombok.Data; import lombok.EqualsAndHashCode; import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; +import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.widget.BaseWidgetType; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.dao.model.ModelConstants; @@ -28,11 +30,13 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import java.util.UUID; @Data @EqualsAndHashCode(callSuper = true) @Entity @TypeDef(name = "json", typeClass = JsonStringType.class) +@TypeDef(name = "string-array", typeClass = StringArrayType.class) @Table(name = ModelConstants.WIDGET_TYPE_TABLE_NAME) public class WidgetTypeDetailsEntity extends AbstractWidgetTypeEntity { @@ -42,10 +46,17 @@ public class WidgetTypeDetailsEntity extends AbstractWidgetTypeEntity { +@Entity +@TypeDef(name = "string-array", typeClass = StringArrayType.class) +@Table(name = ModelConstants.WIDGET_TYPE_INFO_VIEW_TABLE_NAME) +public class WidgetTypeInfoEntity extends AbstractWidgetTypeEntity { + + public static final Map SEARCH_COLUMNS_MAP = new HashMap<>(); + static { + SEARCH_COLUMNS_MAP.put("createdTime", "created_time"); + SEARCH_COLUMNS_MAP.put("tenantId", "tenant_id"); + SEARCH_COLUMNS_MAP.put("widgetType", "widget_type"); + } + + @Column(name = ModelConstants.WIDGET_TYPE_IMAGE_PROPERTY) private String image; + + @Column(name = ModelConstants.WIDGET_TYPE_DESCRIPTION_PROPERTY) private String description; + + @Type(type = "string-array") + @Column(name = ModelConstants.WIDGET_TYPE_TAGS_PROPERTY, columnDefinition = "text[]") + private String[] tags; + + @Column(name = ModelConstants.WIDGET_TYPE_WIDGET_TYPE_PROPERTY) private String widgetType; public WidgetTypeInfoEntity() { super(); } - public WidgetTypeInfoEntity(WidgetTypeDetailsEntity widgetTypeDetailsEntity) { - super(widgetTypeDetailsEntity); - this.image = widgetTypeDetailsEntity.getImage(); - this.description = widgetTypeDetailsEntity.getDescription(); - if (widgetTypeDetailsEntity.getDescriptor() != null && widgetTypeDetailsEntity.getDescriptor().has("type")) { - this.widgetType = widgetTypeDetailsEntity.getDescriptor().get("type").asText(); - } else { - this.widgetType = ""; - } - } - @Override public WidgetTypeInfo toData() { BaseWidgetType baseWidgetType = super.toBaseWidgetType(); WidgetTypeInfo widgetTypeInfo = new WidgetTypeInfo(baseWidgetType); widgetTypeInfo.setImage(image); widgetTypeInfo.setDescription(description); + widgetTypeInfo.setTags(tags); widgetTypeInfo.setWidgetType(widgetType); return widgetTypeInfo; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleEntity.java index fedfa1471d..baa1140803 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleEntity.java @@ -50,6 +50,9 @@ public final class WidgetsBundleEntity extends BaseSqlEntity { @Column(name = ModelConstants.WIDGETS_BUNDLE_DESCRIPTION) private String description; + @Column(name = ModelConstants.WIDGETS_BUNDLE_ORDER) + private Integer order; + @Column(name = ModelConstants.EXTERNAL_ID_PROPERTY) private UUID externalId; @@ -69,6 +72,7 @@ public final class WidgetsBundleEntity extends BaseSqlEntity { this.title = widgetsBundle.getTitle(); this.image = widgetsBundle.getImage(); this.description = widgetsBundle.getDescription(); + this.order = widgetsBundle.getOrder(); if (widgetsBundle.getExternalId() != null) { this.externalId = widgetsBundle.getExternalId().getId(); } @@ -85,6 +89,7 @@ public final class WidgetsBundleEntity extends BaseSqlEntity { widgetsBundle.setTitle(title); widgetsBundle.setImage(image); widgetsBundle.setDescription(description); + widgetsBundle.setOrder(order); if (externalId != null) { widgetsBundle.setExternalId(new WidgetsBundleId(externalId)); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleWidgetCompositeKey.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleWidgetCompositeKey.java new file mode 100644 index 0000000000..faab5b9446 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleWidgetCompositeKey.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.model.sql; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Transient; +import java.io.Serializable; +import java.util.UUID; + +@NoArgsConstructor +@AllArgsConstructor +@Data +public class WidgetsBundleWidgetCompositeKey implements Serializable { + + @Transient + private static final long serialVersionUID = -245388185894468455L; + + private UUID widgetsBundleId; + private UUID widgetTypeId; + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleWidgetEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleWidgetEntity.java new file mode 100644 index 0000000000..d873312858 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleWidgetEntity.java @@ -0,0 +1,75 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.model.sql; + +import lombok.Data; +import org.thingsboard.server.common.data.id.WidgetTypeId; +import org.thingsboard.server.common.data.id.WidgetsBundleId; +import org.thingsboard.server.common.data.widget.WidgetsBundleWidget; +import org.thingsboard.server.dao.model.ToData; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.Table; +import java.util.UUID; + +import static org.thingsboard.server.dao.model.ModelConstants.WIDGETS_BUNDLE_WIDGET_TABLE_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.WIDGET_TYPE_ORDER_PROPERTY; + +@Data +@Entity +@Table(name = WIDGETS_BUNDLE_WIDGET_TABLE_NAME) +@IdClass(WidgetsBundleWidgetCompositeKey.class) +public final class WidgetsBundleWidgetEntity implements ToData { + + @Id + @Column(name = "widgets_bundle_id", columnDefinition = "uuid") + private UUID widgetsBundleId; + + @Id + @Column(name = "widget_type_id", columnDefinition = "uuid") + private UUID widgetTypeId; + + @Column(name = WIDGET_TYPE_ORDER_PROPERTY) + private int widgetTypeOrder; + + public WidgetsBundleWidgetEntity() { + super(); + } + + public WidgetsBundleWidgetEntity(WidgetsBundleWidget widgetsBundleWidget) { + widgetsBundleId = widgetsBundleWidget.getWidgetsBundleId().getId(); + widgetTypeId = widgetsBundleWidget.getWidgetTypeId().getId(); + widgetTypeOrder = widgetsBundleWidget.getWidgetTypeOrder(); + } + + public WidgetsBundleWidgetEntity(UUID widgetsBundleId, UUID widgetTypeId, int widgetTypeOrder) { + this.widgetsBundleId = widgetsBundleId; + this.widgetTypeId = widgetTypeId; + this.widgetTypeOrder = widgetTypeOrder; + } + + @Override + public WidgetsBundleWidget toData() { + WidgetsBundleWidget result = new WidgetsBundleWidget(); + result.setWidgetsBundleId(new WidgetsBundleId(widgetsBundleId)); + result.setWidgetTypeId(new WidgetTypeId(widgetTypeId)); + result.setWidgetTypeOrder(widgetTypeOrder); + return result; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractAsyncDao.java b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractAsyncDao.java index 425724bf2c..92289b7178 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractAsyncDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractAsyncDao.java @@ -19,13 +19,13 @@ import com.google.common.base.Function; import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.springframework.beans.factory.annotation.Value; +import org.thingsboard.common.util.ThingsBoardExecutors; import javax.annotation.Nullable; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; /** * Created by ashvayka on 21.02.17. @@ -34,9 +34,12 @@ public abstract class CassandraAbstractAsyncDao extends CassandraAbstractDao { protected ExecutorService readResultsProcessingExecutor; + @Value("${cassandra.query.result_processing_threads:50}") + private int threadPoolSize; + @PostConstruct public void startExecutor() { - readResultsProcessingExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName("cassandra-callback")); + readResultsProcessingExecutor = ThingsBoardExecutors.newWorkStealingPool(threadPoolSize, "cassandra-callback"); } @PreDestroy diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java index e951a75c7c..0a11c058e8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRequestService.java @@ -42,6 +42,7 @@ import java.util.Optional; public class DefaultNotificationRequestService implements NotificationRequestService, EntityDaoService { private final NotificationRequestDao notificationRequestDao; + private final NotificationDao notificationDao; private final NotificationRequestValidator notificationRequestValidator = new NotificationRequestValidator(); @@ -81,10 +82,10 @@ public class DefaultNotificationRequestService implements NotificationRequestSer return notificationRequestDao.findByRuleIdAndOriginatorEntityId(tenantId, ruleId, originatorEntityId); } - // ON DELETE CASCADE is used: notifications for request are deleted as well @Override public void deleteNotificationRequest(TenantId tenantId, NotificationRequestId requestId) { notificationRequestDao.removeById(tenantId, requestId.getId()); + notificationDao.deleteByRequestId(tenantId, requestId); } @Override @@ -97,6 +98,7 @@ public class DefaultNotificationRequestService implements NotificationRequestSer notificationRequestDao.updateById(tenantId, requestId, requestStatus, stats); } + // notifications themselves are left in the database until removed by ttl @Override public void deleteNotificationRequestsByTenantId(TenantId tenantId) { notificationRequestDao.removeByTenantId(tenantId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java index 3cb92bd6ff..41cea42875 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationRuleService.java @@ -24,7 +24,7 @@ import org.thingsboard.server.common.data.id.NotificationRuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.AbstractEntityService; diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java index 0e25a0cab3..4faed9cb20 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao.notification; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @@ -25,7 +26,11 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.CacheConstants; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; +import org.thingsboard.server.common.data.notification.settings.UserNotificationSettings; +import org.thingsboard.server.common.data.notification.settings.UserNotificationSettings.NotificationPref; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.platform.AffectedTenantAdministratorsFilter; import org.thingsboard.server.common.data.notification.targets.platform.AffectedUserFilter; @@ -35,18 +40,30 @@ import org.thingsboard.server.common.data.notification.targets.platform.Platform import org.thingsboard.server.common.data.notification.targets.platform.SystemAdministratorsFilter; import org.thingsboard.server.common.data.notification.targets.platform.TenantAdministratorsFilter; import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter; +import org.thingsboard.server.common.data.notification.targets.platform.UsersFilterType; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.settings.UserSettings; +import org.thingsboard.server.common.data.settings.UserSettingsType; import org.thingsboard.server.dao.settings.AdminSettingsService; +import org.thingsboard.server.dao.user.UserSettingsService; import java.util.Collections; +import java.util.EnumMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; import java.util.Optional; @Service @RequiredArgsConstructor +@Slf4j public class DefaultNotificationSettingsService implements NotificationSettingsService { private final AdminSettingsService adminSettingsService; private final NotificationTargetService notificationTargetService; + private final NotificationTemplateService notificationTemplateService; private final DefaultNotifications defaultNotifications; + private final UserSettingsService userSettingsService; private static final String SETTINGS_KEY = "notifications"; @@ -76,6 +93,58 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS }); } + @Override + public UserNotificationSettings saveUserNotificationSettings(TenantId tenantId, UserId userId, UserNotificationSettings settings) { + UserSettings userSettings = new UserSettings(); + userSettings.setUserId(userId); + userSettings.setType(UserSettingsType.NOTIFICATIONS); + userSettings.setSettings(JacksonUtil.valueToTree(settings)); + userSettingsService.saveUserSettings(tenantId, userSettings); + return formatUserNotificationSettings(settings); + } + + @Override + public UserNotificationSettings getUserNotificationSettings(TenantId tenantId, UserId userId, boolean format) { + UserSettings userSettings = userSettingsService.findUserSettings(tenantId, userId, UserSettingsType.NOTIFICATIONS); + UserNotificationSettings settings = null; + if (userSettings != null) { + try { + settings = JacksonUtil.treeToValue(userSettings.getSettings(), UserNotificationSettings.class); + } catch (Exception e) { + log.warn("Failed to parse notification settings for user {}", userId, e); + } + } + if (settings == null) { + settings = UserNotificationSettings.DEFAULT; + } + if (format) { + settings = formatUserNotificationSettings(settings); + } + return settings; + } + + private UserNotificationSettings formatUserNotificationSettings(UserNotificationSettings settings) { + Map prefs = new EnumMap<>(NotificationType.class); + if (settings != null) { + prefs.putAll(settings.getPrefs()); + } + NotificationPref defaultPref = NotificationPref.createDefault(); + for (NotificationType notificationType : NotificationType.values()) { + NotificationPref pref = prefs.get(notificationType); + if (pref == null) { + prefs.put(notificationType, defaultPref); + } else { + var enabledDeliveryMethods = new LinkedHashMap<>(pref.getEnabledDeliveryMethods()); + // in case a new delivery method was added to the platform + UserNotificationSettings.deliveryMethods.forEach(deliveryMethod -> { + enabledDeliveryMethods.putIfAbsent(deliveryMethod, true); + }); + pref.setEnabledDeliveryMethods(enabledDeliveryMethods); + } + } + return new UserNotificationSettings(prefs); + } + @Transactional(propagation = Propagation.NOT_SUPPORTED) // so that parent transaction is not aborted on method failure @Override public void createDefaultNotificationConfigs(TenantId tenantId) { @@ -98,6 +167,10 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS defaultNotifications.create(tenantId, DefaultNotifications.apiFeatureDisabledForSysadmin, sysAdmins.getId()); defaultNotifications.create(tenantId, DefaultNotifications.apiFeatureDisabledForTenant, affectedTenantAdmins.getId()); + defaultNotifications.create(tenantId, DefaultNotifications.exceededRateLimits, affectedTenantAdmins.getId()); + defaultNotifications.create(tenantId, DefaultNotifications.exceededPerEntityRateLimits, affectedTenantAdmins.getId()); + defaultNotifications.create(tenantId, DefaultNotifications.exceededRateLimitsForSysadmin, sysAdmins.getId()); + defaultNotifications.create(tenantId, DefaultNotifications.newPlatformVersion, sysAdmins.getId()); return; } @@ -116,6 +189,29 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS defaultNotifications.create(tenantId, DefaultNotifications.ruleEngineComponentLifecycleFailure, tenantAdmins.getId()); } + @Override + public void updateDefaultNotificationConfigs(TenantId tenantId) { + if (tenantId.isSysTenantId()) { + if (notificationTemplateService.findNotificationTemplatesByTenantIdAndNotificationTypes(tenantId, + List.of(NotificationType.RATE_LIMITS), new PageLink(1)).getTotalElements() > 0) { + return; + } + + NotificationTarget sysAdmins = notificationTargetService.findNotificationTargetsByTenantIdAndUsersFilterType(tenantId, UsersFilterType.SYSTEM_ADMINISTRATORS).stream() + .findFirst().orElseGet(() -> { + return createTarget(tenantId, "System administrators", new SystemAdministratorsFilter(), "All system administrators"); + }); + NotificationTarget affectedTenantAdmins = notificationTargetService.findNotificationTargetsByTenantIdAndUsersFilterType(tenantId, UsersFilterType.AFFECTED_TENANT_ADMINISTRATORS).stream() + .findFirst().orElseGet(() -> { + return createTarget(tenantId, "Affected tenant's administrators", new AffectedTenantAdministratorsFilter(), ""); + }); + + defaultNotifications.create(tenantId, DefaultNotifications.exceededRateLimits, affectedTenantAdmins.getId()); + defaultNotifications.create(tenantId, DefaultNotifications.exceededPerEntityRateLimits, affectedTenantAdmins.getId()); + defaultNotifications.create(tenantId, DefaultNotifications.exceededRateLimitsForSysadmin, sysAdmins.getId()); + } + } + private NotificationTarget createTarget(TenantId tenantId, String name, UsersFilter filter, String description) { NotificationTarget target = new NotificationTarget(); target.setTenantId(tenantId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java index 78d5a4b454..f313fb6e9f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTargetService.java @@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.notification.targets.platform.Platform import org.thingsboard.server.common.data.notification.targets.platform.TenantAdministratorsFilter; import org.thingsboard.server.common.data.notification.targets.platform.UserListFilter; import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter; +import org.thingsboard.server.common.data.notification.targets.platform.UsersFilterType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.AbstractEntityService; @@ -93,6 +94,11 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl return notificationTargetDao.findByTenantIdAndIds(tenantId, ids); } + @Override + public List findNotificationTargetsByTenantIdAndUsersFilterType(TenantId tenantId, UsersFilterType filterType) { + return notificationTargetDao.findByTenantIdAndUsersFilterType(tenantId, filterType); + } + @Override public PageData findRecipientsForNotificationTarget(TenantId tenantId, CustomerId customerId, NotificationTargetId targetId, PageLink pageLink) { NotificationTarget notificationTarget = findNotificationTargetById(tenantId, targetId); @@ -144,8 +150,9 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl return userService.findAllUsers(pageLink); } } + default: + throw new IllegalArgumentException("Recipient type not supported"); } - return new PageData<>(); } @Override @@ -172,6 +179,8 @@ public class DefaultNotificationTargetService extends AbstractEntityService impl return userService.findTenantAdmins(affectedTenantId, pageLink); } break; + default: + throw new IllegalArgumentException("Recipient type not supported"); } return new PageData<>(); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java index 01f9f68ae4..29c7928a24 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java @@ -26,34 +26,39 @@ import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.NotificationTemplateId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.rule.DefaultNotificationRuleRecipientsConfig; import org.thingsboard.server.common.data.notification.rule.EscalatedNotificationRuleRecipientsConfig; import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.rule.NotificationRuleConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.AlarmAssignmentNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.AlarmCommentNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.AlarmNotificationRuleTriggerConfig.AlarmAction; -import org.thingsboard.server.common.data.notification.rule.trigger.ApiUsageLimitNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.DeviceActivityNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.DeviceActivityNotificationRuleTriggerConfig.DeviceEvent; -import org.thingsboard.server.common.data.notification.rule.trigger.EntitiesLimitNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.EntityActionNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.NewPlatformVersionNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; -import org.thingsboard.server.common.data.notification.rule.trigger.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmAssignmentNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmCommentNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmNotificationRuleTriggerConfig.AlarmAction; +import org.thingsboard.server.common.data.notification.rule.trigger.config.ApiUsageLimitNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.DeviceActivityNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.DeviceActivityNotificationRuleTriggerConfig.DeviceEvent; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EntitiesLimitNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EntityActionNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NewPlatformVersionNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.notification.rule.trigger.config.RateLimitsNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig; import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; +import static java.util.function.Predicate.not; import static org.thingsboard.common.util.JacksonUtil.newObjectNode; import static org.thingsboard.server.dao.DaoUtil.toUUIDs; @@ -61,17 +66,21 @@ import static org.thingsboard.server.dao.DaoUtil.toUUIDs; @RequiredArgsConstructor public class DefaultNotifications { + private static final String YELLOW_COLOR = "#F9D916"; + private static final String RED_COLOR = "#e91a1a"; + public static final DefaultNotification maintenanceWork = DefaultNotification.builder() .name("Maintenance work notification") .subject("Infrastructure maintenance") .text("Maintenance work is scheduled for tomorrow (7:00 a.m. - 9:00 a.m. UTC)") .build(); + public static final DefaultNotification entitiesLimitForSysadmin = DefaultNotification.builder() .name("Entities count limit notification for sysadmin") .type(NotificationType.ENTITIES_LIMIT) .subject("${entityType}s limit will be reached soon for tenant ${tenantName}") .text("${entityType}s usage: ${currentCount}/${limit} (${percents}%)") - .icon("warning").color("#F9D916") + .icon("warning").color(YELLOW_COLOR) .rule(DefaultRule.builder() .name("Entities count limit (sysadmin)") .triggerConfig(EntitiesLimitNotificationRuleTriggerConfig.builder() @@ -88,12 +97,13 @@ public class DefaultNotifications { .description("Send notification to tenant admins when count of entities of some type reached 80% threshold of the limit") .build()) .build(); + public static final DefaultNotification apiFeatureWarningForSysadmin = DefaultNotification.builder() .name("API feature warning notification for sysadmin") .type(NotificationType.API_USAGE_LIMIT) .subject("${feature} feature will be disabled soon for tenant ${tenantName}") .text("Usage: ${currentValue} out of ${limit} ${unitLabel}s") - .icon("warning").color("#F9D916") + .icon("warning").color(YELLOW_COLOR) .rule(DefaultRule.builder() .name("API feature warning (sysadmin)") .triggerConfig(ApiUsageLimitNotificationRuleTriggerConfig.builder() @@ -116,7 +126,7 @@ public class DefaultNotifications { .type(NotificationType.API_USAGE_LIMIT) .subject("${feature} feature was disabled for tenant ${tenantName}") .text("Used ${currentValue} out of ${limit} ${unitLabel}s") - .icon("block").color("#e91a1a") + .icon("block").color(RED_COLOR) .rule(DefaultRule.builder() .name("API feature disabled (sysadmin)") .triggerConfig(ApiUsageLimitNotificationRuleTriggerConfig.builder() @@ -134,6 +144,51 @@ public class DefaultNotifications { .description("Send notification to tenant admins when API feature is disabled") .build()) .build(); + + public static final DefaultNotification exceededRateLimits = DefaultNotification.builder() + .name("Exceeded per-tenant rate limits notification for tenant") + .type(NotificationType.RATE_LIMITS) + .subject("Rate limits exceeded") + .text("Rate limits for ${api} exceeded") + .icon("block").color(RED_COLOR) + .rule(DefaultRule.builder() + .name("Per-tenant rate limits exceeded") + .triggerConfig(RateLimitsNotificationRuleTriggerConfig.builder() + .apis(Arrays.stream(LimitedApi.values()) + .filter(LimitedApi::isPerTenant) + .filter(api -> api.getLabel() != null) + .collect(Collectors.toSet())) + .build()) + .description("Send notification to tenant admins when some per-tenant rate limit is exceeded") + .build()) + .build(); + public static final DefaultNotification exceededPerEntityRateLimits = DefaultNotification.builder() + .name("Exceeded per-entity rate limits notification for tenant") + .type(NotificationType.RATE_LIMITS) + .subject("Rate limits exceeded") + .text("Rate limits for ${api} exceeded for '${limitLevelEntityName}'") + .icon("block").color(RED_COLOR) + .rule(DefaultRule.builder() + .name("Per-entity rate limits exceeded") + .triggerConfig(RateLimitsNotificationRuleTriggerConfig.builder() + .apis(Arrays.stream(LimitedApi.values()) + .filter(not(LimitedApi::isPerTenant)) + .filter(api -> api.getLabel() != null) + .collect(Collectors.toSet())) + .build()) + .description("Send notification to tenant admins when some per-entity rate limit is exceeded for an entity") + .build()) + .build(); + public static final DefaultNotification exceededRateLimitsForSysadmin = exceededRateLimits.toBuilder() + .name("Exceeded per-tenant rate limits notification for sysadmin") + .subject("Rate limits exceeded for tenant ${tenantName}") + .button("Go to tenant").link("/tenants/${tenantId}") + .rule(exceededRateLimits.getRule().toBuilder() + .name("Per-tenant rate limits exceeded (sysadmin)") + .description("Send notification to system admins when a tenant exceeds some per-tenant rate limit") + .build()) + .build(); + public static final DefaultNotification newPlatformVersion = DefaultNotification.builder() .name("New platform version notification") .type(NotificationType.NEW_PLATFORM_VERSION) @@ -271,6 +326,15 @@ public class DefaultNotifications { .build()) .build(); + public static final DefaultNotification jwtSigningKeyIssue = DefaultNotification.builder() + .name("JWT Signing Key issue notification") + .type(NotificationType.GENERAL) + .subject("WARNING: security issue") + .text("The platform is configured to use default JWT Signing Key. Please change it on the security settings page") + .icon("warning").color(YELLOW_COLOR) + .button("Go to settings").link("/security-settings/general") + .build(); + private final NotificationTemplateService templateService; private final NotificationRuleService ruleService; diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java index 1ced714bce..fb3890fcbb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationDao.java @@ -41,4 +41,8 @@ public interface NotificationDao extends Dao { int updateStatusByRecipientId(TenantId tenantId, UserId recipientId, NotificationStatus status); + void deleteByRequestId(TenantId tenantId, NotificationRequestId requestId); + + void deleteByRecipientId(TenantId tenantId, UserId recipientId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java index 90eee4ba63..17d09c4404 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationRuleDao.java @@ -20,7 +20,7 @@ import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java index 912e873bcc..3e542cc50f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java @@ -19,6 +19,7 @@ import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; +import org.thingsboard.server.common.data.notification.targets.platform.UsersFilterType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; @@ -35,6 +36,8 @@ public interface NotificationTargetDao extends Dao, TenantEn List findByTenantIdAndIds(TenantId tenantId, List ids); + List findByTenantIdAndUsersFilterType(TenantId tenantId, UsersFilterType filterType); + void removeByTenantId(TenantId tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java index 2a784d7985..13f84161c8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java @@ -38,6 +38,8 @@ import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.entity.AbstractCachedEntityService; +import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; +import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -77,10 +79,12 @@ public class BaseOtaPackageService extends AbstractCachedEntityService handleEvictEvent(EntityRelationEvent.from(relation)), MoreExecutors.directExecutor()); + future.addListener(() -> { + handleEvictEvent(EntityRelationEvent.from(relation)); + eventPublisher.publishEvent(new RelationActionEvent(tenantId, relation, ActionType.RELATION_ADD_OR_UPDATE)); + }, MoreExecutors.directExecutor()); return future; } @@ -188,6 +195,7 @@ public class BaseRelationService implements RelationService { var result = relationDao.deleteRelation(tenantId, relation); //TODO: evict cache only if the relation was deleted. Note: relationDao.deleteRelation requires improvement. publishEvictEvent(EntityRelationEvent.from(relation)); + eventPublisher.publishEvent(new RelationActionEvent(tenantId, relation, ActionType.RELATION_DELETED)); return result; } @@ -196,7 +204,10 @@ public class BaseRelationService implements RelationService { log.trace("Executing deleteRelationAsync [{}]", relation); validate(relation); var future = relationDao.deleteRelationAsync(tenantId, relation); - future.addListener(() -> handleEvictEvent(EntityRelationEvent.from(relation)), MoreExecutors.directExecutor()); + future.addListener(() -> { + handleEvictEvent(EntityRelationEvent.from(relation)); + eventPublisher.publishEvent(new RelationActionEvent(tenantId, relation, ActionType.RELATION_DELETED)); + }, MoreExecutors.directExecutor()); return future; } @@ -206,7 +217,9 @@ public class BaseRelationService implements RelationService { validate(from, to, relationType, typeGroup); var result = relationDao.deleteRelation(tenantId, from, to, relationType, typeGroup); //TODO: evict cache only if the relation was deleted. Note: relationDao.deleteRelation requires improvement. - publishEvictEvent(new EntityRelationEvent(from, to, relationType, typeGroup)); + EntityRelation entityRelation = new EntityRelation(from, to, relationType, typeGroup); + publishEvictEvent(EntityRelationEvent.from(entityRelation)); + eventPublisher.publishEvent(new RelationActionEvent(tenantId, entityRelation, ActionType.RELATION_DELETED)); return result; } @@ -220,17 +233,36 @@ public class BaseRelationService implements RelationService { return future; } + @Transactional + @Override + public void deleteEntityCommonRelations(TenantId tenantId, EntityId entityId) { + deleteEntityRelations(tenantId, entityId, RelationTypeGroup.COMMON); + } + @Transactional @Override public void deleteEntityRelations(TenantId tenantId, EntityId entityId) { + deleteEntityRelations(tenantId, entityId, null); + } + + @Transactional + public void deleteEntityRelations(TenantId tenantId, EntityId entityId, RelationTypeGroup relationTypeGroup) { log.trace("Executing deleteEntityRelations [{}]", entityId); validate(entityId); - List inboundRelations = new ArrayList<>(relationDao.findAllByTo(tenantId, entityId)); - List outboundRelations = new ArrayList<>(relationDao.findAllByFrom(tenantId, entityId)); + List inboundRelations = relationTypeGroup == null + ? relationDao.findAllByTo(tenantId, entityId) + : relationDao.findAllByTo(tenantId, entityId, relationTypeGroup); + List outboundRelations = relationTypeGroup == null + ? relationDao.findAllByFrom(tenantId, entityId) + : relationDao.findAllByFrom(tenantId, entityId, relationTypeGroup); if (!inboundRelations.isEmpty()) { try { - relationDao.deleteInboundRelations(tenantId, entityId); + if (relationTypeGroup == null) { + relationDao.deleteInboundRelations(tenantId, entityId); + } else { + relationDao.deleteInboundRelations(tenantId, entityId, relationTypeGroup); + } } catch (ConcurrencyFailureException e) { log.debug("Concurrency exception while deleting relations [{}]", inboundRelations, e); } @@ -241,7 +273,11 @@ public class BaseRelationService implements RelationService { } if (!outboundRelations.isEmpty()) { - relationDao.deleteOutboundRelations(tenantId, entityId); + if (relationTypeGroup == null) { + relationDao.deleteOutboundRelations(tenantId, entityId); + } else { + relationDao.deleteOutboundRelations(tenantId, entityId, relationTypeGroup); + } for (EntityRelation relation : outboundRelations) { eventPublisher.publishEvent(EntityRelationEvent.from(relation)); @@ -634,5 +670,4 @@ public class BaseRelationService implements RelationService { handleEvictEvent(event); } } - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/RelationDao.java b/dao/src/main/java/org/thingsboard/server/dao/relation/RelationDao.java index 7fee4a31ff..250a0c6105 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/relation/RelationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/relation/RelationDao.java @@ -64,8 +64,12 @@ public interface RelationDao { void deleteOutboundRelations(TenantId tenantId, EntityId entity); + void deleteOutboundRelations(TenantId tenantId, EntityId entity, RelationTypeGroup relationTypeGroup); + void deleteInboundRelations(TenantId tenantId, EntityId entity); + void deleteInboundRelations(TenantId tenantId, EntityId entity, RelationTypeGroup relationTypeGroup); + ListenableFuture deleteOutboundRelationsAsync(TenantId tenantId, EntityId entity); List findRuleNodeToRuleChainRelations(RuleChainType ruleChainType, int limit); diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java new file mode 100644 index 0000000000..50ca981c1a --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java @@ -0,0 +1,672 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.resource; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.base.Strings; +import lombok.Data; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Base64Utils; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.HasImage; +import org.thingsboard.server.common.data.ImageDescriptor; +import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.TbImageDeleteResult; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.TbResourceInfoFilter; +import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.TbResourceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.widget.WidgetTypeDetails; +import org.thingsboard.server.dao.ImageContainerDao; +import org.thingsboard.server.dao.asset.AssetProfileDao; +import org.thingsboard.server.dao.dashboard.DashboardInfoDao; +import org.thingsboard.server.dao.device.DeviceProfileDao; +import org.thingsboard.server.dao.service.Validator; +import org.thingsboard.server.dao.service.validator.ResourceDataValidator; +import org.thingsboard.server.dao.util.ImageUtils; +import org.thingsboard.server.dao.util.ImageUtils.ProcessedImage; +import org.thingsboard.server.dao.util.JsonNodeProcessingTask; +import org.thingsboard.server.dao.util.JsonPathProcessingTask; +import org.thingsboard.server.dao.widget.WidgetTypeDao; +import org.thingsboard.server.dao.widget.WidgetsBundleDao; + +import javax.annotation.PostConstruct; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Queue; +import java.util.Set; +import java.util.regex.Pattern; + +@Service +@Slf4j +public class BaseImageService extends BaseResourceService implements ImageService { + + private static final int MAX_ENTITIES_TO_FIND = 10; + private static final String DEFAULT_CONFIG_TAG = "defaultConfig"; + + public static Map DASHBOARD_BASE64_MAPPING = new HashMap<>(); + public static Map WIDGET_TYPE_BASE64_MAPPING = new HashMap<>(); + + static { + DASHBOARD_BASE64_MAPPING.put("settings.dashboardLogoUrl", "$prefix logo"); + DASHBOARD_BASE64_MAPPING.put("states.default.layouts.main.gridSettings.backgroundImageUrl", "$prefix background"); + DASHBOARD_BASE64_MAPPING.put("states.default.layouts.right.gridSettings.backgroundImageUrl", "$prefix right background"); + DASHBOARD_BASE64_MAPPING.put("states.$stateId.layouts.main.gridSettings.backgroundImageUrl", "$prefix $stateId background"); + DASHBOARD_BASE64_MAPPING.put("states.$stateId.layouts.right.gridSettings.backgroundImageUrl", "$prefix $stateId right background"); + DASHBOARD_BASE64_MAPPING.put("widgets.*.config[$title].settings.backgroundImageUrl", "$prefix widget \"$title\" background"); + DASHBOARD_BASE64_MAPPING.put("widgets.*.config[$title].settings.mapImageUrl", "$prefix widget \"$title\" map image"); + DASHBOARD_BASE64_MAPPING.put("widgets.*.config[$title].settings.markerImage", "$prefix widget \"$title\" marker image"); + DASHBOARD_BASE64_MAPPING.put("widgets.*.config[$title].settings.markerImages", "$prefix widget \"$title\" marker image $index"); + DASHBOARD_BASE64_MAPPING.put("widgets.*.config[$title].settings.background.imageUrl", "$prefix widget \"$title\" background"); + DASHBOARD_BASE64_MAPPING.put("widgets.*.config[$title].settings.background.imageBase64", "$prefix widget \"$title\" background"); + DASHBOARD_BASE64_MAPPING.put("widgets.*.config[$title].datasources.*.dataKeys.*.settings.customIcon", "$prefix widget \"$title\" custom icon"); + + WIDGET_TYPE_BASE64_MAPPING.put("settings.backgroundImageUrl", "$prefix background"); + WIDGET_TYPE_BASE64_MAPPING.put("settings.mapImageUrl", "$prefix map image"); + WIDGET_TYPE_BASE64_MAPPING.put("settings.markerImage", "Map marker image"); + WIDGET_TYPE_BASE64_MAPPING.put("settings.markerImages", "Map marker image $index"); + WIDGET_TYPE_BASE64_MAPPING.put("settings.background.imageUrl", "$prefix background"); + WIDGET_TYPE_BASE64_MAPPING.put("settings.background.imageBase64", "$prefix background"); + WIDGET_TYPE_BASE64_MAPPING.put("datasources.*.dataKeys.*.settings.customIcon", "$prefix custom icon"); + } + + private final AssetProfileDao assetProfileDao; + private final DeviceProfileDao deviceProfileDao; + private final WidgetsBundleDao widgetsBundleDao; + private final WidgetTypeDao widgetTypeDao; + private final DashboardInfoDao dashboardInfoDao; + private final Map> imageContainerDaoMap = new HashMap<>(); + + public BaseImageService(TbResourceDao resourceDao, TbResourceInfoDao resourceInfoDao, ResourceDataValidator resourceValidator, + AssetProfileDao assetProfileDao, DeviceProfileDao deviceProfileDao, WidgetsBundleDao widgetsBundleDao, + WidgetTypeDao widgetTypeDao, DashboardInfoDao dashboardInfoDao) { + super(resourceDao, resourceInfoDao, resourceValidator); + this.assetProfileDao = assetProfileDao; + this.deviceProfileDao = deviceProfileDao; + this.widgetsBundleDao = widgetsBundleDao; + this.widgetTypeDao = widgetTypeDao; + this.dashboardInfoDao = dashboardInfoDao; + } + + @PostConstruct + public void init() { + imageContainerDaoMap.put(EntityType.WIDGET_TYPE, widgetTypeDao); + imageContainerDaoMap.put(EntityType.WIDGETS_BUNDLE, widgetsBundleDao); + imageContainerDaoMap.put(EntityType.DEVICE_PROFILE, deviceProfileDao); + imageContainerDaoMap.put(EntityType.ASSET_PROFILE, assetProfileDao); + imageContainerDaoMap.put(EntityType.DASHBOARD, dashboardInfoDao); + } + + + @Override + @SneakyThrows + public TbResourceInfo saveImage(TbResource image) { + if (image.getId() == null) { + image.setResourceKey(getUniqueKey(image.getTenantId(), StringUtils.defaultIfEmpty(image.getResourceKey(), image.getFileName()))); + } + resourceValidator.validate(image, TbResourceInfo::getTenantId); + + ImageDescriptor descriptor = image.getDescriptor(ImageDescriptor.class); + Pair result = processImage(image.getData(), descriptor); + descriptor = result.getLeft(); + image.setEtag(descriptor.getEtag()); + image.setDescriptorValue(descriptor); + image.setPreview(result.getRight()); + + if (StringUtils.isEmpty(image.getPublicResourceKey())) { + image.setPublicResourceKey(generatePublicResourceKey()); + } else { + if (resourceInfoDao.existsByPublicResourceKey(ResourceType.IMAGE, image.getPublicResourceKey())) { + image.setPublicResourceKey(generatePublicResourceKey()); + } + } + log.debug("[{}] Creating image {} ('{}')", image.getTenantId(), image.getResourceKey(), image.getName()); + return new TbResourceInfo(doSaveResource(image)); + } + + private Pair processImage(byte[] data, ImageDescriptor descriptor) throws Exception { + ProcessedImage image = ImageUtils.processImage(data, descriptor.getMediaType(), 250); + ProcessedImage preview = image.getPreview(); + + descriptor.setWidth(image.getWidth()); + descriptor.setHeight(image.getHeight()); + descriptor.setSize(image.getSize()); + descriptor.setEtag(calculateEtag(data)); + + ImageDescriptor previewDescriptor = new ImageDescriptor(); + previewDescriptor.setWidth(preview.getWidth()); + previewDescriptor.setHeight(preview.getHeight()); + previewDescriptor.setMediaType(preview.getMediaType()); + previewDescriptor.setSize(preview.getSize()); + previewDescriptor.setEtag(preview.getData() != null ? calculateEtag(preview.getData()) : descriptor.getEtag()); + descriptor.setPreviewDescriptor(previewDescriptor); + + return Pair.of(descriptor, preview.getData()); + } + + private String getUniqueKey(TenantId tenantId, String filename) { + if (!resourceInfoDao.existsByTenantIdAndResourceTypeAndResourceKey(tenantId, ResourceType.IMAGE, filename)) { + return filename; + } + + String basename = StringUtils.substringBeforeLast(filename, "."); + String extension = StringUtils.substringAfterLast(filename, "."); + + Set existing = resourceInfoDao.findKeysByTenantIdAndResourceTypeAndResourceKeyPrefix( + tenantId, ResourceType.IMAGE, basename + ); + String resourceKey = filename; + int idx = 1; + while (existing.contains(resourceKey)) { + resourceKey = basename + "_(" + idx + ")." + extension; + idx++; + } + log.debug("[{}] Generated unique key {} for image {}", tenantId, resourceKey, filename); + return resourceKey; + } + + private String generatePublicResourceKey() { + return RandomStringUtils.randomAlphanumeric(32); + } + + @Override + public TbResourceInfo saveImageInfo(TbResourceInfo imageInfo) { + log.trace("Executing saveImageInfo [{}] [{}]", imageInfo.getTenantId(), imageInfo.getId()); + return saveResource(new TbResource(imageInfo)); + } + + @Override + public TbResourceInfo getImageInfoByTenantIdAndKey(TenantId tenantId, String key) { + log.trace("Executing getImageInfoByTenantIdAndKey [{}] [{}]", tenantId, key); + return findResourceInfoByTenantIdAndKey(tenantId, ResourceType.IMAGE, key); + } + + @Override + public TbResourceInfo getPublicImageInfoByKey(String publicResourceKey) { + return resourceInfoDao.findPublicResourceByKey(ResourceType.IMAGE, publicResourceKey); + } + + @Override + public PageData getImagesByTenantId(TenantId tenantId, PageLink pageLink) { + log.trace("Executing getImagesByTenantId [{}]", tenantId); + TbResourceInfoFilter filter = TbResourceInfoFilter.builder() + .tenantId(tenantId) + .resourceTypes(Set.of(ResourceType.IMAGE)) + .build(); + return findTenantResourcesByTenantId(filter, pageLink); + } + + @Override + public PageData getAllImagesByTenantId(TenantId tenantId, PageLink pageLink) { + log.trace("Executing getAllImagesByTenantId [{}]", tenantId); + TbResourceInfoFilter filter = TbResourceInfoFilter.builder() + .tenantId(tenantId) + .resourceTypes(Set.of(ResourceType.IMAGE)) + .build(); + return findAllTenantResourcesByTenantId(filter, pageLink); + } + + @Override + public byte[] getImageData(TenantId tenantId, TbResourceId imageId) { + log.trace("Executing getImageData [{}] [{}]", tenantId, imageId); + return resourceDao.getResourceData(tenantId, imageId); + } + + @Override + public byte[] getImagePreview(TenantId tenantId, TbResourceId imageId) { + log.trace("Executing getImagePreview [{}] [{}]", tenantId, imageId); + return resourceDao.getResourcePreview(tenantId, imageId); + } + + @Override + public TbImageDeleteResult deleteImage(TbResourceInfo imageInfo, boolean force) { + var tenantId = imageInfo.getTenantId(); + var imageId = imageInfo.getId(); + log.trace("Executing deleteImage [{}] [{}]", tenantId, imageId); + Validator.validateId(imageId, INCORRECT_RESOURCE_ID + imageId); + TbImageDeleteResult.TbImageDeleteResultBuilder result = TbImageDeleteResult.builder(); + boolean success = true; + if (!force) { + var link = DataConstants.TB_IMAGE_PREFIX + imageInfo.getLink(); + Map>> affectedEntities = new HashMap<>(); + imageContainerDaoMap.forEach((entityType, imageContainerDao) -> { + var entities = tenantId.isSysTenantId() ? imageContainerDao.findByImageLink(link, MAX_ENTITIES_TO_FIND) : + imageContainerDao.findByTenantAndImageLink(tenantId, link, MAX_ENTITIES_TO_FIND); + if (!entities.isEmpty()) { + affectedEntities.put(entityType.name(), entities); + } + }); + if (!affectedEntities.isEmpty()) { + success = false; + result.references(affectedEntities); + } + } + if (success) { + deleteResource(tenantId, imageId, force); + } + return result.success(success).build(); + } + + @Override + public TbResourceInfo findSystemOrTenantImageByEtag(TenantId tenantId, String etag) { + log.trace("Executing findSystemOrTenantImageByEtag [{}] [{}]", tenantId, etag); + return resourceInfoDao.findSystemOrTenantImageByEtag(tenantId, ResourceType.IMAGE, etag); + } + + @Transactional(noRollbackFor = Exception.class) // we don't want transaction to rollback in case of an image processing failure + @Override + public boolean replaceBase64WithImageUrl(HasImage entity, String type) { + log.trace("Executing replaceBase64WithImageUrl [{}] [{}] [{}]", entity.getTenantId(), type, entity.getName()); + String imageName = "\"" + entity.getName() + "\" "; + if (entity.getTenantId() == null || entity.getTenantId().isSysTenantId()) { + imageName += "system "; + } + imageName = imageName + type + " image"; + + UpdateResult result = base64ToImageUrl(entity.getTenantId(), imageName, entity.getImage()); + entity.setImage(result.getValue()); + return result.isUpdated(); + } + + @Transactional(noRollbackFor = Exception.class) // we don't want transaction to rollback in case of an image processing failure + @Override + public boolean replaceBase64WithImageUrl(WidgetTypeDetails entity) { + log.trace("Executing replaceBase64WithImageUrl [{}] [WidgetTypeDetails] [{}]", entity.getTenantId(), entity.getId()); + String prefix = "\"" + entity.getName() + "\" "; + if (entity.getTenantId() == null || entity.getTenantId().isSysTenantId()) { + prefix += "system "; + } + prefix += "widget"; + UpdateResult result = base64ToImageUrl(entity.getTenantId(), prefix + " image", entity.getImage()); + entity.setImage(result.getValue()); + boolean updated = result.isUpdated(); + if (entity.getDescriptor().isObject()) { + ObjectNode descriptor = (ObjectNode) entity.getDescriptor(); + JsonNode defaultConfig = Optional.ofNullable(descriptor.get(DEFAULT_CONFIG_TAG)) + .filter(JsonNode::isTextual).map(JsonNode::asText) + .map(JacksonUtil::toJsonNode).orElse(null); + if (defaultConfig != null) { + updated |= base64ToImageUrlUsingMapping(entity.getTenantId(), WIDGET_TYPE_BASE64_MAPPING, Collections.singletonMap("prefix", prefix), defaultConfig); + descriptor.put(DEFAULT_CONFIG_TAG, defaultConfig.toString()); + } + } + updated |= base64ToImageUrlRecursively(entity.getTenantId(), prefix, entity.getDescriptor()); + return updated; + } + + @Transactional(noRollbackFor = Exception.class) // we don't want transaction to rollback in case of an image processing failure + @Override + public boolean replaceBase64WithImageUrl(Dashboard entity) { + log.trace("Executing replaceBase64WithImageUrl [{}] [Dashboard] [{}]", entity.getTenantId(), entity.getId()); + String prefix = "\"" + entity.getTitle() + "\" dashboard"; + var result = base64ToImageUrl(entity.getTenantId(), prefix + " image", entity.getImage()); + boolean updated = result.isUpdated(); + entity.setImage(result.getValue()); + updated |= base64ToImageUrlUsingMapping(entity.getTenantId(), DASHBOARD_BASE64_MAPPING, Collections.singletonMap("prefix", prefix), entity.getConfiguration()); + updated |= base64ToImageUrlRecursively(entity.getTenantId(), prefix, entity.getConfiguration()); + return updated; + } + + private boolean base64ToImageUrlUsingMapping(TenantId tenantId, Map mapping, Map templateParams, JsonNode configuration) { + boolean updated = false; + for (var entry : mapping.entrySet()) { + String expression = entry.getValue(); + Queue tasks = new LinkedList<>(); + tasks.add(new JsonPathProcessingTask(entry.getKey().split("\\."), templateParams, configuration)); + while (!tasks.isEmpty()) { + JsonPathProcessingTask task = tasks.poll(); + String token = task.currentToken(); + JsonNode node = task.getNode(); + if (node == null) { + continue; + } + if (token.equals("*") || token.startsWith("$")) { + String variableName = token.startsWith("$") ? token.substring(1) : null; + if (node.isArray()) { + ArrayNode childArray = (ArrayNode) node; + for (JsonNode element : childArray) { + tasks.add(task.next(element)); + } + } else if (node.isObject()) { + ObjectNode on = (ObjectNode) node; + for (Iterator> it = on.fields(); it.hasNext(); ) { + var kv = it.next(); + if (variableName != null) { + tasks.add(task.next(kv.getValue(), variableName, kv.getKey())); + } else { + tasks.add(task.next(kv.getValue())); + } + } + } + } else { + String variableName = null; + String variableValue = null; + if (token.contains("[$")) { + variableName = StringUtils.substringBetween(token, "[$", "]"); + token = StringUtils.substringBefore(token, "[$"); + } + if (node.has(token)) { + JsonNode value = node.get(token); + if (variableName != null && value.has(variableName) && value.get(variableName).isTextual()) { + variableValue = value.get(variableName).asText(); + } + if (task.isLast()) { + String name = expression; + for (var replacement : task.getVariables().entrySet()) { + name = name.replace("$" + replacement.getKey(), Strings.nullToEmpty(replacement.getValue())); + } + if (node.isObject() && value.isTextual()) { + var result = base64ToImageUrl(tenantId, name, value.asText()); + ((ObjectNode) node).put(token, result.getValue()); + updated |= result.isUpdated(); + } else if (value.isArray()) { + ArrayNode array = (ArrayNode) value; + for (int i = 0; i < array.size(); i++) { + String arrayElementName = name.replace("$index", Integer.toString(i)); + UpdateResult result = base64ToImageUrl(tenantId, arrayElementName, array.get(i).asText()); + array.set(i, result.getValue()); + updated |= result.isUpdated(); + } + } + } else { + if (StringUtils.isNotEmpty(variableName)) { + tasks.add(task.next(value, variableName, variableValue)); + } else { + tasks.add(task.next(value)); + } + } + } + } + } + } + return updated; + } + + private UpdateResult base64ToImageUrl(TenantId tenantId, String name, String data) { + return base64ToImageUrl(tenantId, name, data, false); + } + + private static final Pattern TB_IMAGE_METADATA_PATTERN = Pattern.compile("^tb-image:(.*):(.*);data:(.*);.*"); + + private UpdateResult base64ToImageUrl(TenantId tenantId, String name, String data, boolean strict) { + if (StringUtils.isBlank(data)) { + return UpdateResult.of(false, data); + } + var matcher = TB_IMAGE_METADATA_PATTERN.matcher(data); + boolean matches = matcher.matches(); + String mdResourceKey = null; + String mdResourceName = null; + String mdMediaType; + if (matches) { + mdResourceKey = new String(Base64Utils.decodeFromString(matcher.group(1)), StandardCharsets.UTF_8); + mdResourceName = new String(Base64Utils.decodeFromString(matcher.group(2)), StandardCharsets.UTF_8); + mdMediaType = matcher.group(3); + } else if (data.startsWith(DataConstants.TB_IMAGE_PREFIX + "data:image/") || (!strict && data.startsWith("data:image/"))) { + mdMediaType = StringUtils.substringBetween(data, "data:", ";base64"); + } else { + return UpdateResult.of(false, data); + } + String base64Data = StringUtils.substringAfter(data, "base64,"); + String extension = ImageUtils.mediaTypeToFileExtension(mdMediaType); + byte[] imageData = Base64.getDecoder().decode(base64Data); + String etag = calculateEtag(imageData); + var imageInfo = findSystemOrTenantImageByEtag(tenantId, etag); + if (imageInfo == null) { + TbResource image = new TbResource(); + image.setTenantId(tenantId); + image.setResourceType(ResourceType.IMAGE); + if (StringUtils.isBlank(mdResourceName)) { + mdResourceName = name; + } + image.setTitle(mdResourceName); + + String fileName; + if (StringUtils.isBlank(mdResourceKey)) { + fileName = StringUtils.strip(mdResourceName.toLowerCase() + .replaceAll("['\"]", "") + .replaceAll("[^\\pL\\d]+", "_"), "_") // leaving only letters and numbers + + "." + extension; + } else { + fileName = mdResourceKey; + } + image.setFileName(fileName); + image.setDescriptor(JacksonUtil.newObjectNode().put("mediaType", mdMediaType)); + image.setData(imageData); + image.setPublic(true); + try { + imageInfo = saveImage(image); + } catch (Exception e) { + if (log.isDebugEnabled()) { // printing stacktrace + log.warn("[{}][{}] Failed to replace Base64 with image url for {}", tenantId, name, StringUtils.abbreviate(data, 50), e); + } else { + log.warn("[{}][{}] Failed to replace Base64 with image url for {}: {}", tenantId, name, StringUtils.abbreviate(data, 50), ExceptionUtils.getMessage(e)); + } + return UpdateResult.of(false, data); + } + } else { + log.debug("[{}] Using existing image {} ({} - '{}') for '{}'", tenantId, imageInfo.getResourceKey(), imageInfo.getTenantId(), imageInfo.getName(), name); + } + return UpdateResult.of(true, DataConstants.TB_IMAGE_PREFIX + imageInfo.getLink()); + } + + private boolean base64ToImageUrlRecursively(TenantId tenantId, String title, JsonNode root) { + boolean updated = false; + Queue tasks = new LinkedList<>(); + tasks.add(new JsonNodeProcessingTask(title, root)); + while (!tasks.isEmpty()) { + JsonNodeProcessingTask task = tasks.poll(); + JsonNode node = task.getNode(); + if (node == null) { + continue; + } + String currentPath = StringUtils.isBlank(task.getPath()) ? "" : (task.getPath() + " "); + if (node.isObject()) { + ObjectNode on = (ObjectNode) node; + for (Iterator it = on.fieldNames(); it.hasNext(); ) { + String childName = it.next(); + JsonNode childValue = on.get(childName); + if (childValue.isTextual()) { + UpdateResult result = base64ToImageUrl(tenantId, currentPath + childName, childValue.asText(), true); + on.put(childName, result.getValue()); + updated |= result.isUpdated(); + } else if (childValue.isObject() || childValue.isArray()) { + tasks.add(new JsonNodeProcessingTask(currentPath + childName, childValue)); + } + } + } else if (node.isArray()) { + ArrayNode childArray = (ArrayNode) node; + for (int i = 0; i < childArray.size(); i++) { + JsonNode element = childArray.get(i); + if (element.isObject()) { + tasks.add(new JsonNodeProcessingTask(currentPath + " " + i, element)); + } else if (element.isTextual()) { + UpdateResult result = base64ToImageUrl(tenantId, currentPath + "." + i, element.asText(), true); + childArray.set(i, result.getValue()); + updated |= result.isUpdated(); + } + } + } + } + return updated; + } + + @Override + public void inlineImage(HasImage entity) { + log.trace("Executing inlineImage [{}] [{}] [{}]", entity.getTenantId(), entity.getClass().getSimpleName(), entity.getName()); + entity.setImage(inlineImage(entity.getTenantId(), "image", entity.getImage(), true)); + } + + @Override + public void inlineImages(Dashboard dashboard) { + log.trace("Executing inlineImage [{}] [Dashboard] [{}]", dashboard.getTenantId(), dashboard.getId()); + inlineImage(dashboard); + inlineIntoJson(dashboard.getTenantId(), dashboard.getConfiguration()); + } + + @Override + public void inlineImages(WidgetTypeDetails widgetTypeDetails) { + log.trace("Executing inlineImage [{}] [WidgetTypeDetails] [{}]", widgetTypeDetails.getTenantId(), widgetTypeDetails.getId()); + inlineImage(widgetTypeDetails); + ObjectNode descriptor = (ObjectNode) widgetTypeDetails.getDescriptor(); + inlineIntoJson(widgetTypeDetails.getTenantId(), descriptor); + if (descriptor.has(DEFAULT_CONFIG_TAG) && descriptor.get(DEFAULT_CONFIG_TAG).isTextual()) { + try { + var defaultConfig = JacksonUtil.toJsonNode(descriptor.get(DEFAULT_CONFIG_TAG).asText()); + inlineIntoJson(widgetTypeDetails.getTenantId(), defaultConfig); + descriptor.put(DEFAULT_CONFIG_TAG, JacksonUtil.toString(defaultConfig)); + } catch (Exception e) { + log.debug("[{}][{}] Failed to process default config: ", widgetTypeDetails.getTenantId(), widgetTypeDetails.getId(), e); + } + } + } + + @Override + public void inlineImageForEdge(HasImage entity) { + log.trace("Executing inlineImageForEdge [{}] [{}] [{}]", entity.getTenantId(), entity.getClass().getSimpleName(), entity.getName()); + entity.setImage(inlineImage(entity.getTenantId(), "image", entity.getImage(), false)); + } + + @Override + public void inlineImagesForEdge(Dashboard dashboard) { + log.trace("Executing inlineImagesForEdge [{}] [Dashboard] [{}]", dashboard.getTenantId(), dashboard.getId()); + inlineImageForEdge(dashboard); + inlineIntoJson(dashboard.getTenantId(), dashboard.getConfiguration(), false); + } + + @Override + public void inlineImagesForEdge(WidgetTypeDetails widgetTypeDetails) { + log.trace("Executing inlineImage [{}] [WidgetTypeDetails] [{}]", widgetTypeDetails.getTenantId(), widgetTypeDetails.getId()); + inlineImageForEdge(widgetTypeDetails); + inlineIntoJson(widgetTypeDetails.getTenantId(), widgetTypeDetails.getDescriptor(), false); + } + + private void inlineIntoJson(TenantId tenantId, JsonNode root) { + inlineIntoJson(tenantId, root, true); + } + + private void inlineIntoJson(TenantId tenantId, JsonNode root, boolean addTbImagePrefix) { + Queue tasks = new LinkedList<>(); + tasks.add(new JsonNodeProcessingTask("", root)); + while (!tasks.isEmpty()) { + JsonNodeProcessingTask task = tasks.poll(); + JsonNode node = task.getNode(); + if (node == null) { + continue; + } + String currentPath = StringUtils.isBlank(task.getPath()) ? "" : (task.getPath() + "."); + if (node.isObject()) { + ObjectNode on = (ObjectNode) node; + for (Iterator it = on.fieldNames(); it.hasNext(); ) { + String childName = it.next(); + JsonNode childValue = on.get(childName); + if (childValue.isTextual()) { + on.put(childName, inlineImage(tenantId, currentPath + childName, childValue.asText(), addTbImagePrefix)); + } else if (childValue.isObject() || childValue.isArray()) { + tasks.add(new JsonNodeProcessingTask(currentPath + childName, childValue)); + } + } + } else if (node.isArray()) { + ArrayNode childArray = (ArrayNode) node; + for (int i = 0; i < childArray.size(); i++) { + JsonNode element = childArray.get(i); + if (element.isObject()) { + tasks.add(new JsonNodeProcessingTask(currentPath + "." + i, element)); + } else if (element.isTextual()) { + childArray.set(i, inlineImage(tenantId, currentPath + "." + i, element.asText(), addTbImagePrefix)); + } + } + } + } + } + + private String inlineImage(TenantId tenantId, String path, String url, boolean addTbImagePrefix) { + try { + ImageCacheKey key = getKeyFromUrl(tenantId, url); + if (key != null) { + var imageInfo = getImageInfoByTenantIdAndKey(key.getTenantId(), key.getResourceKey()); + if (imageInfo != null) { + byte[] data = key.isPreview() ? getImagePreview(tenantId, imageInfo.getId()) : getImageData(tenantId, imageInfo.getId()); + ImageDescriptor descriptor = getImageDescriptor(imageInfo, key.isPreview()); + String tbImagePrefix = ""; + if (addTbImagePrefix) { + tbImagePrefix = "tb-image:" + Base64Utils.encodeToString(imageInfo.getResourceKey().getBytes(StandardCharsets.UTF_8)) + ":" + + Base64Utils.encodeToString(imageInfo.getName().getBytes(StandardCharsets.UTF_8)) + ";"; + } + return tbImagePrefix + "data:" + descriptor.getMediaType() + ";base64," + Base64Utils.encodeToString(data); + } + } + } catch (Exception e) { + log.warn("[{}][{}][{}] Failed to inline image.", tenantId, path, url, e); + } + return url; + } + + private ImageDescriptor getImageDescriptor(TbResourceInfo imageInfo, boolean preview) throws JsonProcessingException { + ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class); + return preview ? descriptor.getPreviewDescriptor() : descriptor; + } + + private ImageCacheKey getKeyFromUrl(TenantId tenantId, String url) { + if (StringUtils.isBlank(url)) { + return null; + } + TenantId imageTenantId = null; + if (url.startsWith(DataConstants.TB_IMAGE_PREFIX + "/api/images/tenant/")) { + imageTenantId = tenantId; + } else if (url.startsWith(DataConstants.TB_IMAGE_PREFIX + "/api/images/system/")) { + imageTenantId = TenantId.SYS_TENANT_ID; + } + if (imageTenantId != null) { + var parts = url.split("/"); + if (parts.length == 5) { + return ImageCacheKey.forImage(imageTenantId, parts[4], false); + } else if (parts.length == 6 && "preview".equals(parts[5])) { + return ImageCacheKey.forImage(imageTenantId, parts[4], true); + } + } + return null; + } + + @Data(staticConstructor = "of") + private static class UpdateResult { + private final boolean updated; + private final String value; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index 974e3665f1..eb34f434b1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -15,11 +15,16 @@ */ package org.thingsboard.server.dao.resource; +import com.google.common.hash.Hashing; import com.google.common.util.concurrent.ListenableFuture; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.hibernate.exception.ConstraintViolationException; +import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Service; +import org.springframework.transaction.event.TransactionalEventListener; +import org.thingsboard.server.cache.resourceInfo.ResourceInfoCacheKey; +import org.thingsboard.server.cache.resourceInfo.ResourceInfoEvictEvent; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; @@ -31,10 +36,13 @@ import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.entity.AbstractCachedEntityService; +import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; +import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; +import org.thingsboard.server.dao.service.validator.ResourceDataValidator; import java.util.List; import java.util.Optional; @@ -45,34 +53,64 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @Service("TbResourceDaoService") @Slf4j @AllArgsConstructor -public class BaseResourceService implements ResourceService { +@Primary +public class BaseResourceService extends AbstractCachedEntityService implements ResourceService { public static final String INCORRECT_RESOURCE_ID = "Incorrect resourceId "; - private final TbResourceDao resourceDao; - private final TbResourceInfoDao resourceInfoDao; - private final DataValidator resourceValidator; + protected final TbResourceDao resourceDao; + protected final TbResourceInfoDao resourceInfoDao; + protected final ResourceDataValidator resourceValidator; + + @Override + public TbResource saveResource(TbResource resource, boolean doValidate) { + log.trace("Executing saveResource [{}]", resource); + if (doValidate) { + resourceValidator.validate(resource, TbResourceInfo::getTenantId); + } + if (resource.getData() != null) { + resource.setEtag(calculateEtag(resource.getData())); + } + return doSaveResource(resource); + } @Override public TbResource saveResource(TbResource resource) { - resourceValidator.validate(resource, TbResourceInfo::getTenantId); + return saveResource(resource, true); + } + protected TbResource doSaveResource(TbResource resource) { + TenantId tenantId = resource.getTenantId(); try { - return resourceDao.save(resource.getTenantId(), resource); + TbResource saved; + if (resource.getData() != null) { + saved = resourceDao.save(tenantId, resource); + } else { + TbResourceInfo resourceInfo = saveResourceInfo(resource); + saved = new TbResource(resourceInfo); + } + publishEvictEvent(new ResourceInfoEvictEvent(tenantId, resource.getId())); + eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(saved.getTenantId()) + .entityId(saved.getId()).added(resource.getId() == null).build()); + return saved; } catch (Exception t) { + publishEvictEvent(new ResourceInfoEvictEvent(tenantId, resource.getId())); ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("resource_unq_key")) { - String field = ResourceType.LWM2M_MODEL.equals(resource.getResourceType()) ? "resourceKey" : "fileName"; - throw new DataValidationException("Resource with such " + field + " already exists!"); + throw new DataValidationException("Resource with such key already exists!"); } else { throw t; } } } + private TbResourceInfo saveResourceInfo(TbResource resource) { + return resourceInfoDao.save(resource.getTenantId(), new TbResourceInfo(resource)); + } + @Override - public TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceKey) { - log.trace("Executing getResource [{}] [{}] [{}]", tenantId, resourceType, resourceKey); - return resourceDao.getResource(tenantId, resourceType, resourceKey); + public TbResource findResourceByTenantIdAndKey(TenantId tenantId, ResourceType resourceType, String resourceKey) { + log.trace("Executing findResourceByTenantIdAndKey [{}] [{}] [{}]", tenantId, resourceType, resourceKey); + return resourceDao.findResourceByTenantIdAndKey(tenantId, resourceType, resourceKey); } @Override @@ -86,7 +124,15 @@ public class BaseResourceService implements ResourceService { public TbResourceInfo findResourceInfoById(TenantId tenantId, TbResourceId resourceId) { log.trace("Executing findResourceInfoById [{}] [{}]", tenantId, resourceId); Validator.validateId(resourceId, INCORRECT_RESOURCE_ID + resourceId); - return resourceInfoDao.findById(tenantId, resourceId.getId()); + + return cache.getAndPutInTransaction(new ResourceInfoCacheKey(tenantId, resourceId), + () -> resourceInfoDao.findById(tenantId, resourceId.getId()), true); + } + + @Override + public TbResourceInfo findResourceInfoByTenantIdAndKey(TenantId tenantId, ResourceType resourceType, String resourceKey) { + log.trace("Executing findResourceInfoByTenantIdAndKey [{}] [{}] [{}]", tenantId, resourceType, resourceKey); + return resourceInfoDao.findByTenantIdAndKey(tenantId, resourceType, resourceKey); } @Override @@ -98,11 +144,21 @@ public class BaseResourceService implements ResourceService { @Override public void deleteResource(TenantId tenantId, TbResourceId resourceId) { + deleteResource(tenantId, resourceId, false); + } + + @Override + public void deleteResource(TenantId tenantId, TbResourceId resourceId, boolean force) { log.trace("Executing deleteResource [{}] [{}]", tenantId, resourceId); Validator.validateId(resourceId, INCORRECT_RESOURCE_ID + resourceId); + if (!force) { + resourceValidator.validateDelete(tenantId, resourceId); + } resourceDao.removeById(tenantId, resourceId.getId()); + eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(resourceId).build()); } + @Override public PageData findAllTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink) { TenantId tenantId = filter.getTenantId(); @@ -126,6 +182,13 @@ public class BaseResourceService implements ResourceService { return resourceDao.findResourcesByTenantIdAndResourceType(tenantId, resourceType, objectIds, null); } + @Override + public PageData findAllTenantResources(TenantId tenantId, PageLink pageLink) { + log.trace("Executing findAllTenantResources [{}][{}]", tenantId, pageLink); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + return resourceDao.findAllByTenantId(tenantId, pageLink); + } + @Override public PageData findTenantResourcesByResourceTypeAndPageLink(TenantId tenantId, ResourceType resourceType, PageLink pageLink) { log.trace("Executing findTenantResourcesByResourceTypeAndPageLink [{}][{}][{}]", tenantId, resourceType, pageLink); @@ -145,6 +208,11 @@ public class BaseResourceService implements ResourceService { return Optional.ofNullable(findResourceInfoById(tenantId, new TbResourceId(entityId.getId()))); } + @Override + public void deleteEntity(TenantId tenantId, EntityId id) { + deleteResource(tenantId, (TbResourceId) id); + } + @Override public EntityType getEntityType() { return EntityType.TB_RESOURCE; @@ -155,6 +223,10 @@ public class BaseResourceService implements ResourceService { return resourceDao.sumDataSizeByTenantId(tenantId); } + protected String calculateEtag(byte[] data) { + return Hashing.sha256().hashBytes(data).toString(); + } + private final PaginatedRemover tenantResourcesRemover = new PaginatedRemover<>() { @@ -169,13 +241,11 @@ public class BaseResourceService implements ResourceService { } }; - protected Optional extractConstraintViolationException(Exception t) { - if (t instanceof ConstraintViolationException) { - return Optional.of((ConstraintViolationException) t); - } else if (t.getCause() instanceof ConstraintViolationException) { - return Optional.of((ConstraintViolationException) (t.getCause())); - } else { - return Optional.empty(); + @TransactionalEventListener(classes = ResourceInfoEvictEvent.class) + @Override + public void handleEvictEvent(ResourceInfoEvictEvent event) { + if (event.getResourceId() != null) { + cache.evict(new ResourceInfoCacheKey(event.getTenantId(), event.getResourceId())); } } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/ImageCacheKey.java b/dao/src/main/java/org/thingsboard/server/dao/resource/ImageCacheKey.java new file mode 100644 index 0000000000..82efa3c434 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/ImageCacheKey.java @@ -0,0 +1,62 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.resource; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.With; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.gen.transport.TransportProtos.ImageCacheKeyProto; + +@Data +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public class ImageCacheKey { + + private final TenantId tenantId; + private final String resourceKey; + @With + private final boolean preview; + + private final String publicResourceKey; + + public static ImageCacheKey forImage(TenantId tenantId, String key, boolean preview) { + return new ImageCacheKey(tenantId, key, preview, null); + } + + public static ImageCacheKey forImage(TenantId tenantId, String key) { + return forImage(tenantId, key, false); + } + + public static ImageCacheKey forPublicImage(String publicKey) { + return new ImageCacheKey(null, null, false, publicKey); + } + + public ImageCacheKeyProto toProto() { + var msg = ImageCacheKeyProto.newBuilder(); + if (resourceKey != null) { + msg.setResourceKey(resourceKey); + } else { + msg.setPublicResourceKey(publicResourceKey); + } + return msg.build(); + } + + public boolean isPublic() { + return this.publicResourceKey != null; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java b/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java index 4117563805..0a8e215618 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java @@ -17,17 +17,19 @@ package org.thingsboard.server.dao.resource; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.ExportableEntityDao; import org.thingsboard.server.dao.TenantEntityWithDataDao; import java.util.List; -public interface TbResourceDao extends Dao, TenantEntityWithDataDao { +public interface TbResourceDao extends Dao, TenantEntityWithDataDao, ExportableEntityDao { - TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceId); + TbResource findResourceByTenantIdAndKey(TenantId tenantId, ResourceType resourceType, String resourceId); PageData findAllByTenantId(TenantId tenantId, PageLink pageLink); @@ -39,4 +41,11 @@ public interface TbResourceDao extends Dao, TenantEntityWithDataDao ResourceType resourceType, String[] objectIds, String searchText); + + byte[] getResourceData(TenantId tenantId, TbResourceId resourceId); + + byte[] getResourcePreview(TenantId tenantId, TbResourceId resourceId); + + long getResourceSize(TenantId tenantId, TbResourceId resourceId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceInfoDao.java index f0f0b6fdad..cea4661ee7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceInfoDao.java @@ -15,13 +15,16 @@ */ package org.thingsboard.server.dao.resource; +import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.TbResourceInfoFilter; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; -import java.util.UUID; +import java.util.List; +import java.util.Set; public interface TbResourceInfoDao extends Dao { @@ -29,4 +32,18 @@ public interface TbResourceInfoDao extends Dao { PageData findTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink); + TbResourceInfo findByTenantIdAndKey(TenantId tenantId, ResourceType resourceType, String resourceKey); + + boolean existsByTenantIdAndResourceTypeAndResourceKey(TenantId tenantId, ResourceType resourceType, String resourceKey); + + Set findKeysByTenantIdAndResourceTypeAndResourceKeyPrefix(TenantId tenantId, ResourceType resourceType, String prefix); + + List findByTenantIdAndEtagAndKeyStartingWith(TenantId tenantId, String etag, String query); + + TbResourceInfo findSystemOrTenantImageByEtag(TenantId tenantId, ResourceType resourceType, String etag); + + boolean existsByPublicResourceKey(ResourceType resourceType, String publicResourceKey); + + TbResourceInfo findPublicResourceByKey(ResourceType resourceType, String publicResourceKey); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index ba4afa7bd0..6257bb0420 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -29,6 +29,7 @@ import org.springframework.transaction.annotation.Transactional; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; @@ -54,6 +55,9 @@ import org.thingsboard.server.common.data.rule.RuleNodeUpdateResult; import org.thingsboard.server.common.data.util.ReflectionUtils; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entity.EntityCountService; +import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent; +import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; +import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -69,11 +73,14 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.Function; import java.util.stream.Collectors; import static org.thingsboard.server.common.data.DataConstants.TENANT; import static org.thingsboard.server.dao.service.Validator.validateId; +import static org.thingsboard.server.dao.service.Validator.validateIds; import static org.thingsboard.server.dao.service.Validator.validatePageLink; +import static org.thingsboard.server.dao.service.Validator.validatePositiveNumber; import static org.thingsboard.server.dao.service.Validator.validateString; /** @@ -108,6 +115,8 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC if (ruleChain.getId() == null) { entityCountService.publishCountEntityEvictEvent(ruleChain.getTenantId(), EntityType.RULE_CHAIN); } + eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(savedRuleChain.getTenantId()) + .entity(savedRuleChain).entityId(savedRuleChain.getId()).added(ruleChain.getId() == null).build()); return savedRuleChain; } catch (Exception e) { checkConstraintViolation(e, "rule_chain_external_id_unq_key", "Rule Chain with such external id already exists!"); @@ -140,13 +149,13 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC } @Override - public RuleChainUpdateResult saveRuleChainMetaData(TenantId tenantId, RuleChainMetaData ruleChainMetaData) { + public RuleChainUpdateResult saveRuleChainMetaData(TenantId tenantId, RuleChainMetaData ruleChainMetaData, Function ruleNodeUpdater) { Validator.validateId(ruleChainMetaData.getRuleChainId(), "Incorrect rule chain id."); RuleChain ruleChain = findRuleChainById(tenantId, ruleChainMetaData.getRuleChainId()); if (ruleChain == null) { return RuleChainUpdateResult.failed(); } - RuleChainDataValidator.validateMetaData(ruleChainMetaData); + RuleChainDataValidator.validateMetaDataFieldsAndConnections(ruleChainMetaData); List nodes = ruleChainMetaData.getNodes(); List toAddOrUpdate = new ArrayList<>(); @@ -180,9 +189,12 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC } updatedRuleNodes.add(new RuleNodeUpdateResult(existingNode, newRuleNode)); } + RuleChainId ruleChainId = ruleChain.getId(); if (nodes != null) { for (RuleNode node : toAddOrUpdate) { - node.setRuleChainId(ruleChain.getId()); + node.setRuleChainId(ruleChainId); + node = ruleNodeUpdater.apply(node); + RuleChainDataValidator.validateRuleNode(node); RuleNode savedNode = ruleNodeDao.save(tenantId, node); relations.add(new EntityRelation(ruleChainMetaData.getRuleChainId(), savedNode.getId(), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN)); @@ -218,7 +230,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC RuleChain targetRuleChain = findRuleChainById(TenantId.SYS_TENANT_ID, targetRuleChainId); RuleNode targetNode = new RuleNode(); targetNode.setName(targetRuleChain != null ? targetRuleChain.getName() : "Rule Chain Input"); - targetNode.setRuleChainId(ruleChain.getId()); + targetNode.setRuleChainId(ruleChainId); targetNode.setType("org.thingsboard.rule.engine.flow.TbRuleChainInputNode"); var configuration = JacksonUtil.newObjectNode(); configuration.put("ruleChainId", targetRuleChainId.getId().toString()); @@ -231,7 +243,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC targetNode = ruleNodeDao.save(tenantId, targetNode); EntityRelation sourceRuleChainToRuleNode = new EntityRelation(); - sourceRuleChainToRuleNode.setFrom(ruleChain.getId()); + sourceRuleChainToRuleNode.setFrom(ruleChainId); sourceRuleChainToRuleNode.setTo(targetNode.getId()); sourceRuleChainToRuleNode.setType(EntityRelation.CONTAINS_TYPE); sourceRuleChainToRuleNode.setTypeGroup(RelationTypeGroup.RULE_CHAIN); @@ -251,6 +263,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC if (!relations.isEmpty()) { relationService.saveRelations(tenantId, relations); } + eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId).entity(ruleChain).entityId(ruleChain.getId()).build()); return RuleChainUpdateResult.successful(updatedRuleNodes); } @@ -446,7 +459,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC } @Override - public List importTenantRuleChains(TenantId tenantId, RuleChainData ruleChainData, boolean overwrite) { + public List importTenantRuleChains(TenantId tenantId, RuleChainData ruleChainData, boolean overwrite, Function ruleNodeUpdater) { List importResults = new ArrayList<>(); setRandomRuleChainIds(ruleChainData); @@ -483,7 +496,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC } if (CollectionUtils.isNotEmpty(ruleChainData.getMetadata())) { - ruleChainData.getMetadata().forEach(md -> saveRuleChainMetaData(tenantId, md)); + ruleChainData.getMetadata().forEach(md -> saveRuleChainMetaData(tenantId, md, ruleNodeUpdater)); } return importResults; @@ -586,6 +599,10 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC log.warn("[{}] Failed to create ruleChain relation. Edge Id: [{}]", ruleChainId, edgeId); throw new RuntimeException(e); } + if (!ruleChainId.equals(edge.getRootRuleChainId())) { + eventPublisher.publishEvent(ActionEntityEvent.builder().tenantId(tenantId).edgeId(edgeId).entityId(ruleChainId) + .actionType(ActionType.ASSIGNED_TO_EDGE).build()); + } return ruleChain; } @@ -605,6 +622,8 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC log.warn("[{}] Failed to delete rule chain relation. Edge Id: [{}]", ruleChainId, edgeId); throw new RuntimeException(e); } + eventPublisher.publishEvent(ActionEntityEvent.builder().tenantId(tenantId).edgeId(edgeId).entityId(ruleChainId) + .actionType(ActionType.UNASSIGNED_FROM_EDGE).build()); return ruleChain; } @@ -700,6 +719,32 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC return ruleNodeDao.findAllRuleNodesByType(type, pageLink); } + @Override + public PageData findAllRuleNodesByTypeAndVersionLessThan(String type, int version, PageLink pageLink) { + log.trace("Executing findAllRuleNodesByTypeAndVersionLessThan, type {}, pageLink {}, version {}", type, pageLink, version); + validateString(type, "Incorrect type of the rule node"); + validatePositiveNumber(version, "Incorrect version to compare with. Version should be greater than 0!"); + validatePageLink(pageLink); + return ruleNodeDao.findAllRuleNodesByTypeAndVersionLessThan(type, version, pageLink); + } + + @Override + public PageData findAllRuleNodeIdsByTypeAndVersionLessThan(String type, int version, PageLink pageLink) { + log.trace("Executing findAllRuleNodeIdsByTypeAndVersionLessThan, type {}, pageLink {}, version {}", type, pageLink, version); + validateString(type, "Incorrect type of the rule node"); + validatePositiveNumber(version, "Incorrect version to compare with. Version should be greater than 0!"); + validatePageLink(pageLink); + return ruleNodeDao.findAllRuleNodeIdsByTypeAndVersionLessThan(type, version, pageLink); + } + + @Override + public List findAllRuleNodesByIds(List ruleNodeIds) { + log.trace("Executing findAllRuleNodesByIds, ruleNodeIds {}", ruleNodeIds); + validateIds(ruleNodeIds, "Incorrect ruleNodeIds " + ruleNodeIds); + assert ruleNodeIds.size() <= 1024; + return ruleNodeDao.findAllRuleNodeByIds(ruleNodeIds); + } + @Override public RuleNode saveRuleNode(TenantId tenantId, RuleNode ruleNode) { return ruleNodeDao.save(tenantId, ruleNode); @@ -709,6 +754,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC try { entityCountService.publishCountEntityEvictEvent(tenantId, EntityType.RULE_CHAIN); ruleChainDao.removeById(tenantId, ruleChainId.getId()); + eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(ruleChainId).build()); } catch (Exception t) { ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_default_rule_chain_device_profile")) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleNodeDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleNodeDao.java index 880797a981..b6f2dd097e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleNodeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleNodeDao.java @@ -30,11 +30,18 @@ import java.util.List; */ public interface RuleNodeDao extends Dao { - List findRuleNodesByTenantIdAndType(TenantId tenantId, String type, String search); + List findRuleNodesByTenantIdAndType(TenantId tenantId, String type, String configurationSearch); PageData findAllRuleNodesByType(String type, PageLink pageLink); + PageData findAllRuleNodesByTypeAndVersionLessThan(String type, int version, PageLink pageLink); + + PageData findAllRuleNodeIdsByTypeAndVersionLessThan(String type, int version, PageLink pageLink); + + List findAllRuleNodeByIds(List ruleNodeIds); + List findByExternalIds(RuleChainId ruleChainId, List externalIds); void deleteByIdIn(List ruleNodeIds); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java index 65ab189081..80c9376f1c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java @@ -17,14 +17,17 @@ package org.thingsboard.server.dao.service; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.TenantEntityWithDataDao; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.exception.EntitiesLimitException; import org.thingsboard.server.dao.usagerecord.ApiLimitService; import java.util.HashSet; @@ -82,6 +85,18 @@ public abstract class DataValidator> { return null; } + public void validateDelete(TenantId tenantId, EntityId entityId) { + } + + public void validateString(String exceptionPrefix, String name) { + if (StringUtils.isEmpty(name) || name.trim().length() == 0) { + throw new DataValidationException(exceptionPrefix + " should be specified!"); + } + if (StringUtils.contains0x00(name)) { + throw new DataValidationException(exceptionPrefix + " should not contain 0x00 symbol!"); + } + } + protected boolean isSameData(D existentData, D actualData) { return actualData.getId() != null && existentData.getId().equals(actualData.getId()); } @@ -104,7 +119,7 @@ public abstract class DataValidator> { protected void validateNumberOfEntitiesPerTenant(TenantId tenantId, EntityType entityType) { if (!apiLimitService.checkEntitiesLimit(tenantId, entityType)) { - throw new DataValidationException(entityType.getNormalName() + "s limit reached"); + throw new EntitiesLimitException(tenantId, entityType); } } @@ -115,8 +130,7 @@ public abstract class DataValidator> { EntityType entityType) { if (maxSumDataSize > 0) { if (dataDao.sumDataSizeByTenantId(tenantId) + currentDataSize > maxSumDataSize) { - throw new DataValidationException(String.format("Failed to create the %s, files size limit is exhausted %d bytes!", - entityType.name().toLowerCase().replaceAll("_", " "), maxSumDataSize)); + throw new DataValidationException(String.format("%ss total size exceeds the maximum of " + FileUtils.byteCountToDisplaySize(maxSumDataSize), entityType.getNormalName())); } } } @@ -147,8 +161,8 @@ public abstract class DataValidator> { validateQueueNameOrTopic(topic, TOPIC); } - private static void validateQueueNameOrTopic(String value, String fieldName) { - if (StringUtils.isEmpty(value)) { + static void validateQueueNameOrTopic(String value, String fieldName) { + if (StringUtils.isEmpty(value) || value.trim().length() == 0) { throw new DataValidationException(String.format("Queue %s should be specified!", fieldName)); } if (!QUEUE_PATTERN.matcher(value).matches()) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/PaginatedRemover.java b/dao/src/main/java/org/thingsboard/server/dao/service/PaginatedRemover.java index fb98a01178..c7b1173c58 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/PaginatedRemover.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/PaginatedRemover.java @@ -15,12 +15,11 @@ */ package org.thingsboard.server.dao.service; -import org.thingsboard.server.common.data.id.IdBased; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -public abstract class PaginatedRemover> { +public abstract class PaginatedRemover { private static final int DEFAULT_LIMIT = 100; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java index 0685194d26..d8c4ffddc9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java @@ -61,7 +61,7 @@ public class Validator { /** - * This method validate String string. If string is invalid than throw + * This method validate long value. If value isn't possitive than throw * IncorrectParameterException exception * * @param val the val @@ -157,4 +157,10 @@ public class Validator { return StringUtils.isEmpty(key) || RegexUtils.matches(key, PROPERTY_PATTERN); } + public static void checkNotNull(Object reference, String errorMessage) { + if (reference == null) { + throw new IncorrectParameterException(errorMessage); + } + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AdminSettingsDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AdminSettingsDataValidator.java index 692690f328..9d0d53732c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AdminSettingsDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AdminSettingsDataValidator.java @@ -18,7 +18,6 @@ package org.thingsboard.server.dao.service.validator; import lombok.AllArgsConstructor; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.AdminSettings; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; @@ -49,12 +48,9 @@ public class AdminSettingsDataValidator extends DataValidator { return existentAdminSettings; } - @Override protected void validateDataImpl(TenantId tenantId, AdminSettings adminSettings) { - if (StringUtils.isEmpty(adminSettings.getKey())) { - throw new DataValidationException("Key should be specified!"); - } + validateString("Key", adminSettings.getKey()); if (adminSettings.getJsonValue() == null) { throw new DataValidationException("Json value should be specified!"); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AlarmDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AlarmDataValidator.java index 2c76fd2bff..6ef2eb5052 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AlarmDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AlarmDataValidator.java @@ -17,7 +17,6 @@ package org.thingsboard.server.dao.service.validator; import lombok.AllArgsConstructor; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.exception.DataValidationException; @@ -32,9 +31,7 @@ public class AlarmDataValidator extends DataValidator { @Override protected void validateDataImpl(TenantId tenantId, Alarm alarm) { - if (StringUtils.isEmpty(alarm.getType())) { - throw new DataValidationException("Alarm type should be specified!"); - } + validateString("Alarm type", alarm.getType()); if (alarm.getOriginator() == null) { throw new DataValidationException("Alarm originator should be specified!"); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java index 57ae7d3c5e..6c0d862c0d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java @@ -20,7 +20,6 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; @@ -64,9 +63,7 @@ public class AssetDataValidator extends DataValidator { @Override protected void validateDataImpl(TenantId tenantId, Asset asset) { - if (StringUtils.isEmpty(asset.getName())) { - throw new DataValidationException("Asset name should be specified!"); - } + validateString("Asset name", asset.getName()); if (asset.getTenantId() == null) { throw new DataValidationException("Asset should be assigned to tenant!"); } else { diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetProfileDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetProfileDataValidator.java index d206dfefba..f0816621e0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetProfileDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetProfileDataValidator.java @@ -53,9 +53,7 @@ public class AssetProfileDataValidator extends DataValidator { @Override protected void validateDataImpl(TenantId tenantId, AssetProfile assetProfile) { - if (StringUtils.isEmpty(assetProfile.getName())) { - throw new DataValidationException("Asset profile name should be specified!"); - } + validateString("Asset profile name", assetProfile.getName()); if (assetProfile.getTenantId() == null) { throw new DataValidationException("Asset profile should be assigned to tenant!"); } else { diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/BaseOtaPackageDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/BaseOtaPackageDataValidator.java index df37676688..5befacf8ea 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/BaseOtaPackageDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/BaseOtaPackageDataValidator.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.service.validator; +import lombok.Getter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.thingsboard.server.common.data.BaseData; @@ -31,23 +32,27 @@ import java.util.Objects; public abstract class BaseOtaPackageDataValidator> extends DataValidator { @Autowired + @Getter @Lazy private TenantService tenantService; @Autowired + @Getter private DeviceProfileDao deviceProfileDao; protected void validateImpl(OtaPackageInfo otaPackageInfo) { + validateString("OtaPackage title", otaPackageInfo.getTitle()); + if (otaPackageInfo.getTenantId() == null) { throw new DataValidationException("OtaPackage should be assigned to tenant!"); } else { - if (!tenantService.tenantExists(otaPackageInfo.getTenantId())) { + if (!getTenantService().tenantExists(otaPackageInfo.getTenantId())) { throw new DataValidationException("OtaPackage is referencing to non-existent tenant!"); } } if (otaPackageInfo.getDeviceProfileId() != null) { - DeviceProfile deviceProfile = deviceProfileDao.findById(otaPackageInfo.getTenantId(), otaPackageInfo.getDeviceProfileId().getId()); + DeviceProfile deviceProfile = getDeviceProfileDao().findById(otaPackageInfo.getTenantId(), otaPackageInfo.getDeviceProfileId().getId()); if (deviceProfile == null) { throw new DataValidationException("OtaPackage is referencing to non-existent device profile!"); } @@ -57,10 +62,6 @@ public abstract class BaseOtaPackageDataValidator> extends throw new DataValidationException("Type should be specified!"); } - if (StringUtils.isEmpty(otaPackageInfo.getTitle())) { - throw new DataValidationException("OtaPackage title should be specified!"); - } - if (StringUtils.isEmpty(otaPackageInfo.getVersion())) { throw new DataValidationException("OtaPackage version should be specified!"); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ComponentDescriptorDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ComponentDescriptorDataValidator.java index 831b81a6fc..446091a698 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ComponentDescriptorDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ComponentDescriptorDataValidator.java @@ -27,15 +27,13 @@ public class ComponentDescriptorDataValidator extends DataValidator { @Override protected void validateDataImpl(TenantId tenantId, Customer customer) { - if (StringUtils.isEmpty(customer.getTitle())) { - throw new DataValidationException("Customer title should be specified!"); - } + validateString("Customer title", customer.getTitle()); if (customer.getTitle().equals(CustomerServiceImpl.PUBLIC_CUSTOMER_TITLE)) { throw new DataValidationException("'Public' title for customer is system reserved!"); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DashboardDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DashboardDataValidator.java index 246944199a..60e1045f07 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DashboardDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DashboardDataValidator.java @@ -19,9 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.dashboard.DashboardDao; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantService; @@ -29,9 +27,6 @@ import org.thingsboard.server.dao.tenant.TenantService; @Component public class DashboardDataValidator extends DataValidator { - @Autowired - private DashboardDao dashboardDao; - @Autowired private TenantService tenantService; @@ -42,9 +37,7 @@ public class DashboardDataValidator extends DataValidator { @Override protected void validateDataImpl(TenantId tenantId, Dashboard dashboard) { - if (StringUtils.isEmpty(dashboard.getTitle())) { - throw new DataValidationException("Dashboard title should be specified!"); - } + validateString("Dashboard title", dashboard.getTitle()); if (dashboard.getTenantId() == null) { throw new DataValidationException("Dashboard should be assigned to tenant!"); } else { diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceDataValidator.java index 51517a4dc2..41cdcb0b2e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceDataValidator.java @@ -20,7 +20,6 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.device.data.DeviceTransportConfiguration; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; @@ -61,9 +60,7 @@ public class DeviceDataValidator extends AbstractHasOtaPackageValidator @Override protected void validateDataImpl(TenantId tenantId, Device device) { - if (StringUtils.isEmpty(device.getName()) || device.getName().trim().length() == 0) { - throw new DataValidationException("Device name should be specified!"); - } + validateString("Device name", device.getName()); if (device.getTenantId() == null) { throw new DataValidationException("Device should be assigned to tenant!"); } else { diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java index bd4bf04c45..89510c6076 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java @@ -103,9 +103,7 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator { @Override protected void validateDataImpl(TenantId tenantId, Edge edge) { - if (StringUtils.isEmpty(edge.getType())) { - throw new DataValidationException("Edge type should be specified!"); - } - if (StringUtils.isEmpty(edge.getName())) { - throw new DataValidationException("Edge name should be specified!"); - } + validateString("Edge name", edge.getName()); + validateString("Edge type", edge.getType()); if (StringUtils.isEmpty(edge.getSecret())) { throw new DataValidationException("Edge secret should be specified!"); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/EntityViewDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/EntityViewDataValidator.java index dddae3195d..7cc72a67cf 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/EntityViewDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/EntityViewDataValidator.java @@ -19,7 +19,6 @@ import lombok.AllArgsConstructor; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityView; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.customer.CustomerDao; @@ -59,12 +58,8 @@ public class EntityViewDataValidator extends DataValidator { @Override protected void validateDataImpl(TenantId tenantId, EntityView entityView) { - if (StringUtils.isEmpty(entityView.getType())) { - throw new DataValidationException("Entity View type should be specified!"); - } - if (StringUtils.isEmpty(entityView.getName())) { - throw new DataValidationException("Entity view name should be specified!"); - } + validateString("Entity view name", entityView.getName()); + validateString("Entity view type", entityView.getType()); if (entityView.getTenantId() == null) { throw new DataValidationException("Entity view should be assigned to tenant!"); } else { diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java index c547f3c416..352966d561 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java @@ -15,19 +15,27 @@ */ package org.thingsboard.server.dao.service.validator; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.common.data.widget.BaseWidgetType; +import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.resource.TbResourceDao; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantService; +import org.thingsboard.server.dao.widget.WidgetTypeDao; + +import java.util.List; +import java.util.stream.Collectors; import static org.thingsboard.server.common.data.EntityType.TB_RESOURCE; @@ -37,6 +45,9 @@ public class ResourceDataValidator extends DataValidator { @Autowired private TbResourceDao resourceDao; + @Autowired + private WidgetTypeDao widgetTypeDao; + @Autowired private TenantService tenantService; @@ -46,35 +57,72 @@ public class ResourceDataValidator extends DataValidator { @Override protected void validateCreate(TenantId tenantId, TbResource resource) { - if (tenantId != null && !TenantId.SYS_TENANT_ID.equals(tenantId)) { - DefaultTenantProfileConfiguration profileConfiguration = - (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); - long maxSumResourcesDataInBytes = profileConfiguration.getMaxResourcesInBytes(); - validateMaxSumDataSizePerTenant(tenantId, resourceDao, maxSumResourcesDataInBytes, resource.getData().length(), TB_RESOURCE); + if (resource.getData() == null || resource.getData().length == 0) { + throw new DataValidationException("Resource data should be specified"); + } + } + + @Override + protected TbResource validateUpdate(TenantId tenantId, TbResource resource) { + if (resource.getData() != null && !resource.getResourceType().isUpdatable() && + tenantId != null && !tenantId.isSysTenantId()) { + throw new DataValidationException("This type of resource can't be updated"); } + return resource; } @Override protected void validateDataImpl(TenantId tenantId, TbResource resource) { - if (StringUtils.isEmpty(resource.getTitle())) { - throw new DataValidationException("Resource title should be specified!"); + validateString("Resource title", resource.getTitle()); + if (resource.getTenantId() == null) { + resource.setTenantId(TenantId.SYS_TENANT_ID); + } + if (!resource.getTenantId().isSysTenantId()) { + if (!tenantService.tenantExists(resource.getTenantId())) { + throw new DataValidationException("Resource is referencing to non-existent tenant!"); + } } if (resource.getResourceType() == null) { throw new DataValidationException("Resource type should be specified!"); } + if (resource.getData() != null) { + validateResourceSize(resource.getTenantId(), resource.getId(), resource.getData().length); + } if (StringUtils.isEmpty(resource.getFileName())) { throw new DataValidationException("Resource file name should be specified!"); } + if (StringUtils.containsAny(resource.getFileName(), "/", "\\")) { + throw new DataValidationException("File name contains forbidden symbols"); + } if (StringUtils.isEmpty(resource.getResourceKey())) { throw new DataValidationException("Resource key should be specified!"); } - if (resource.getTenantId() == null) { - resource.setTenantId(TenantId.fromUUID(ModelConstants.NULL_UUID)); - } - if (!resource.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { - if (!tenantService.tenantExists(resource.getTenantId())) { - throw new DataValidationException("Resource is referencing to non-existent tenant!"); + } + + public void validateResourceSize(TenantId tenantId, TbResourceId resourceId, long dataSize) { + if (!tenantId.isSysTenantId()) { + DefaultTenantProfileConfiguration profileConfiguration = tenantProfileCache.get(tenantId).getDefaultProfileConfiguration(); + long maxResourceSize = profileConfiguration.getMaxResourceSize(); + if (maxResourceSize > 0 && dataSize > maxResourceSize) { + throw new IllegalArgumentException("Resource exceeds the maximum size of " + FileUtils.byteCountToDisplaySize(maxResourceSize)); } + long maxSumResourcesDataInBytes = profileConfiguration.getMaxResourcesInBytes(); + if (resourceId != null) { + long prevSize = resourceDao.getResourceSize(tenantId, resourceId); + dataSize -= prevSize; + } + validateMaxSumDataSizePerTenant(tenantId, resourceDao, maxSumResourcesDataInBytes, dataSize, TB_RESOURCE); } } + + @Override + public void validateDelete(TenantId tenantId, EntityId resourceId) { + List widgets = widgetTypeDao.findWidgetTypesInfosByTenantIdAndResourceId(tenantId.getId(), + resourceId.getId()); + if (!widgets.isEmpty()) { + List widgetNames = widgets.stream().map(BaseWidgetType::getName).collect(Collectors.toList()); + throw new DataValidationException(String.format("Following widget types uses current resource: %s", widgetNames)); + } + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/RuleChainDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/RuleChainDataValidator.java index 5450e29706..76dad19bcd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/RuleChainDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/RuleChainDataValidator.java @@ -23,7 +23,6 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.NodeConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChain; @@ -32,7 +31,6 @@ import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.util.ReflectionUtils; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.rule.RuleChainDao; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.ConstraintValidator; import org.thingsboard.server.dao.service.DataValidator; @@ -42,15 +40,14 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; @Component @Slf4j public class RuleChainDataValidator extends DataValidator { - @Autowired - private RuleChainDao ruleChainDao; - @Autowired @Lazy private RuleChainService ruleChainService; @@ -65,9 +62,7 @@ public class RuleChainDataValidator extends DataValidator { @Override protected void validateDataImpl(TenantId tenantId, RuleChain ruleChain) { - if (StringUtils.isEmpty(ruleChain.getName())) { - throw new DataValidationException("Rule chain name should be specified!"); - } + validateString("Rule chain name", ruleChain.getName()); if (ruleChain.getType() == null) { ruleChain.setType(RuleChainType.CORE); } @@ -91,15 +86,22 @@ public class RuleChainDataValidator extends DataValidator { } } - public static void validateMetaData(RuleChainMetaData ruleChainMetaData) { + public static List validateMetaData(RuleChainMetaData ruleChainMetaData) { + validateMetaDataFieldsAndConnections(ruleChainMetaData); + return ruleChainMetaData.getNodes().stream() + .map(RuleChainDataValidator::validateRuleNode) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + public static void validateMetaDataFieldsAndConnections(RuleChainMetaData ruleChainMetaData) { ConstraintValidator.validateFields(ruleChainMetaData); - ruleChainMetaData.getNodes().forEach(RuleChainDataValidator::validateRuleNode); if (CollectionUtils.isNotEmpty(ruleChainMetaData.getConnections())) { validateCircles(ruleChainMetaData.getConnections()); } } - public static void validateRuleNode(RuleNode ruleNode) { + public static Throwable validateRuleNode(RuleNode ruleNode) { String errorPrefix = "'" + ruleNode.getName() + "' node configuration is invalid: "; ConstraintValidator.validateFields(ruleNode, errorPrefix); Object nodeConfig; @@ -107,11 +109,12 @@ public class RuleChainDataValidator extends DataValidator { Class nodeConfigType = ReflectionUtils.getAnnotationProperty(ruleNode.getType(), "org.thingsboard.rule.engine.api.RuleNode", "configClazz"); nodeConfig = JacksonUtil.treeToValue(ruleNode.getConfiguration(), nodeConfigType); - } catch (Exception e) { - log.warn("Failed to validate node configuration: {}", ExceptionUtils.getRootCauseMessage(e)); - return; + } catch (Throwable t) { + log.warn("Failed to validate node configuration: {}", ExceptionUtils.getRootCauseMessage(t)); + return t; } ConstraintValidator.validateFields(nodeConfig, errorPrefix); + return null; } private static void validateCircles(List connectionInfos) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantDataValidator.java index c6e8e055a5..64dff6495a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantDataValidator.java @@ -32,9 +32,7 @@ public class TenantDataValidator extends DataValidator { @Override protected void validateDataImpl(TenantId tenantId, Tenant tenant) { - if (StringUtils.isEmpty(tenant.getTitle())) { - throw new DataValidationException("Tenant title should be specified!"); - } + validateString("Tenant title", tenant.getTitle()); if (!StringUtils.isEmpty(tenant.getEmail())) { validateEmail(tenant.getEmail()); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java index 0477ccf32f..a46371de39 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java @@ -19,7 +19,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.DataConstants; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.queue.ProcessingStrategy; @@ -48,9 +47,7 @@ public class TenantProfileDataValidator extends DataValidator { @Override protected void validateDataImpl(TenantId tenantId, TenantProfile tenantProfile) { - if (StringUtils.isEmpty(tenantProfile.getName())) { - throw new DataValidationException("Tenant profile name should be specified!"); - } + validateString("Tenant profile name", tenantProfile.getName()); if (tenantProfile.getProfileData() == null) { throw new DataValidationException("Tenant profile data should be specified!"); } @@ -99,8 +96,6 @@ public class TenantProfileDataValidator extends DataValidator { TenantProfile old = tenantProfileDao.findById(TenantId.SYS_TENANT_ID, tenantProfile.getId().getId()); if (old == null) { throw new DataValidationException("Can't update non existing tenant profile!"); - } else if (old.isIsolatedTbRuleEngine() != tenantProfile.isIsolatedTbRuleEngine()) { - throw new DataValidationException("Can't update isolatedTbRuleEngine property!"); } return old; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetTypeDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetTypeDataValidator.java index aa44ff6934..71bc17a8fa 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetTypeDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetTypeDataValidator.java @@ -17,11 +17,9 @@ package org.thingsboard.server.dao.service.validator; import lombok.AllArgsConstructor; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; -import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DataValidator; @@ -39,12 +37,7 @@ public class WidgetTypeDataValidator extends DataValidator { @Override protected void validateDataImpl(TenantId tenantId, WidgetTypeDetails widgetTypeDetails) { - if (StringUtils.isEmpty(widgetTypeDetails.getName())) { - throw new DataValidationException("Widgets type name should be specified!"); - } - if (StringUtils.isEmpty(widgetTypeDetails.getBundleAlias())) { - throw new DataValidationException("Widgets type bundle alias should be specified!"); - } + validateString("Widgets type name", widgetTypeDetails.getName()); if (widgetTypeDetails.getDescriptor() == null || widgetTypeDetails.getDescriptor().size() == 0) { throw new DataValidationException("Widgets type descriptor can't be empty!"); } @@ -60,24 +53,20 @@ public class WidgetTypeDataValidator extends DataValidator { @Override protected void validateCreate(TenantId tenantId, WidgetTypeDetails widgetTypeDetails) { - WidgetsBundle widgetsBundle = widgetsBundleDao.findWidgetsBundleByTenantIdAndAlias(widgetTypeDetails.getTenantId().getId(), widgetTypeDetails.getBundleAlias()); - if (widgetsBundle == null) { - throw new DataValidationException("Widget type is referencing to non-existent widgets bundle!"); + String fqn = widgetTypeDetails.getFqn(); + if (fqn == null || fqn.trim().isEmpty()) { + fqn = widgetTypeDetails.getName().toLowerCase().replaceAll("\\W+", "_"); } - String alias = widgetTypeDetails.getAlias(); - if (alias == null || alias.trim().isEmpty()) { - alias = widgetTypeDetails.getName().toLowerCase().replaceAll("\\W+", "_"); - } - String originalAlias = alias; + String originalFqn = fqn; int c = 1; - WidgetType withSameAlias; + WidgetType withSameFqn; do { - withSameAlias = widgetTypeDao.findByTenantIdBundleAliasAndAlias(widgetTypeDetails.getTenantId().getId(), widgetTypeDetails.getBundleAlias(), alias); - if (withSameAlias != null) { - alias = originalAlias + (++c); + withSameFqn = widgetTypeDao.findByTenantIdAndFqn(widgetTypeDetails.getTenantId().getId(), fqn); + if (withSameFqn != null) { + fqn = originalFqn + (++c); } - } while (withSameAlias != null); - widgetTypeDetails.setAlias(alias); + } while (withSameFqn != null); + widgetTypeDetails.setFqn(fqn); } @Override @@ -86,11 +75,8 @@ public class WidgetTypeDataValidator extends DataValidator { if (!storedWidgetType.getTenantId().getId().equals(widgetTypeDetails.getTenantId().getId())) { throw new DataValidationException("Can't move existing widget type to different tenant!"); } - if (!storedWidgetType.getBundleAlias().equals(widgetTypeDetails.getBundleAlias())) { - throw new DataValidationException("Update of widget type bundle alias is prohibited!"); - } - if (!storedWidgetType.getAlias().equals(widgetTypeDetails.getAlias())) { - throw new DataValidationException("Update of widget type alias is prohibited!"); + if (!storedWidgetType.getFqn().equals(widgetTypeDetails.getFqn())) { + throw new DataValidationException("Update of widget type fqn is prohibited!"); } return new WidgetTypeDetails(storedWidgetType); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetsBundleDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetsBundleDataValidator.java index 8c730398ed..42b60588cb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetsBundleDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/WidgetsBundleDataValidator.java @@ -17,7 +17,6 @@ package org.thingsboard.server.dao.service.validator; import lombok.AllArgsConstructor; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.exception.DataValidationException; @@ -35,9 +34,7 @@ public class WidgetsBundleDataValidator extends DataValidator { @Override protected void validateDataImpl(TenantId tenantId, WidgetsBundle widgetsBundle) { - if (StringUtils.isEmpty(widgetsBundle.getTitle())) { - throw new DataValidationException("Widgets bundle title should be specified!"); - } + validateString("Widgets bundle title", widgetsBundle.getTitle()); if (widgetsBundle.getTenantId() == null) { widgetsBundle.setTenantId(TenantId.fromUUID(ModelConstants.NULL_UUID)); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlBlockingQueue.java b/dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlBlockingQueue.java index fd3822bb5f..2f3d287c93 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlBlockingQueue.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlBlockingQueue.java @@ -54,7 +54,7 @@ public class TbSqlBlockingQueue implements TbSqlQueue { String logName = params.getLogName(); int batchSize = params.getBatchSize(); long maxDelay = params.getMaxDelay(); - List> entities = new ArrayList<>(batchSize); + final List> entities = new ArrayList<>(batchSize); while (!Thread.interrupted()) { try { long currentTs = System.currentTimeMillis(); @@ -83,19 +83,24 @@ public class TbSqlBlockingQueue implements TbSqlQueue { Thread.sleep(remainingDelay); } } - } catch (Exception e) { - stats.incrementFailed(entities.size()); - entities.forEach(entityFutureWrapper -> entityFutureWrapper.getFuture().setException(e)); - if (e instanceof InterruptedException) { + } catch (Throwable t) { + if (t instanceof InterruptedException) { log.info("[{}] Queue polling was interrupted", logName); break; } else { - log.error("[{}] Failed to save {} entities", logName, entities.size(), e); + log.error("[{}] Failed to save {} entities", logName, entities.size(), t); + try { + stats.incrementFailed(entities.size()); + entities.forEach(entityFutureWrapper -> entityFutureWrapper.getFuture().setException(t)); + } catch (Throwable th) { + log.error("[{}] Failed to set future exception", logName, th); + } } } finally { entities.clear(); } } + log.info("[{}] Queue polling completed", logName); }); logExecutor.scheduleAtFixedRate(() -> { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java index 92537dc850..dc1f9114dd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java @@ -18,9 +18,12 @@ package org.thingsboard.server.dao.sql.alarm; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.sql.AlarmEntity; import org.thingsboard.server.dao.model.sql.AlarmInfoEntity; @@ -55,9 +58,9 @@ public interface AlarmRepository extends JpaRepository { "AND ((:clearFilterEnabled) IS FALSE OR a.cleared = :clearFilter) " + "AND ((:ackFilterEnabled) IS FALSE OR a.acknowledged = :ackFilter) " + "AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " + - "AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.status) LIKE LOWER(CONCAT('%', :searchText, '%'))) " + "AND (:searchText IS NULL OR (ilike(a.type, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.severity, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.status, CONCAT('%', :searchText, '%')) = true)) " , countQuery = "" + "SELECT count(a) " + //alarms with relations only @@ -72,9 +75,9 @@ public interface AlarmRepository extends JpaRepository { "AND ((:clearFilterEnabled) IS FALSE OR a.cleared = :clearFilter) " + "AND ((:ackFilterEnabled) IS FALSE OR a.acknowledged = :ackFilter) " + "AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " + - "AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.status) LIKE LOWER(CONCAT('%', :searchText, '%'))) ") + "AND (:searchText IS NULL OR (ilike(a.type, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.severity, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.status, CONCAT('%', :searchText, '%')) = true))") Page findAlarms(@Param("tenantId") UUID tenantId, @Param("affectedEntityId") UUID affectedEntityId, @Param("affectedEntityType") String affectedEntityType, @@ -102,9 +105,9 @@ public interface AlarmRepository extends JpaRepository { "AND ((:clearFilterEnabled) IS FALSE OR a.cleared = :clearFilter) " + "AND ((:ackFilterEnabled) IS FALSE OR a.acknowledged = :ackFilter) " + "AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " + - "AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.status) LIKE LOWER(CONCAT('%', :searchText, '%'))) " + "AND (:searchText IS NULL OR (ilike(a.type, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.severity, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.status, CONCAT('%', :searchText, '%')) = true)) " , countQuery = "" + "SELECT count(a) " + //alarms with relations only @@ -121,9 +124,9 @@ public interface AlarmRepository extends JpaRepository { "AND ((:clearFilterEnabled) IS FALSE OR a.cleared = :clearFilter) " + "AND ((:ackFilterEnabled) IS FALSE OR a.acknowledged = :ackFilter) " + "AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " + - "AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.status) LIKE LOWER(CONCAT('%', :searchText, '%'))) ") + "AND (:searchText IS NULL OR (ilike(a.type, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.severity, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.status, CONCAT('%', :searchText, '%')) = true))") Page findAlarmsV2(@Param("tenantId") UUID tenantId, @Param("affectedEntityId") UUID affectedEntityId, @Param("affectedEntityType") String affectedEntityType, @@ -147,9 +150,9 @@ public interface AlarmRepository extends JpaRepository { "AND ((:clearFilterEnabled) IS FALSE OR a.cleared = :clearFilter) " + "AND ((:ackFilterEnabled) IS FALSE OR a.acknowledged = :ackFilter) " + "AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " + - "AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.status) LIKE LOWER(CONCAT('%', :searchText, '%'))) ", + "AND (:searchText IS NULL OR (ilike(a.type, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.severity, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.status, CONCAT('%', :searchText, '%')) = true)) ", countQuery = "" + "SELECT count(a) " + "FROM AlarmInfoEntity a " + @@ -159,9 +162,9 @@ public interface AlarmRepository extends JpaRepository { "AND ((:clearFilterEnabled) IS FALSE OR a.cleared = :clearFilter) " + "AND ((:ackFilterEnabled) IS FALSE OR a.acknowledged = :ackFilter) " + "AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " + - "AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.status) LIKE LOWER(CONCAT('%', :searchText, '%'))) ") + "AND (:searchText IS NULL OR (ilike(a.type, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.severity, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.status, CONCAT('%', :searchText, '%')) = true))") Page findAllAlarms(@Param("tenantId") UUID tenantId, @Param("startTime") Long startTime, @Param("endTime") Long endTime, @@ -183,9 +186,9 @@ public interface AlarmRepository extends JpaRepository { "AND ((:clearFilterEnabled) IS FALSE OR a.cleared = :clearFilter) " + "AND ((:ackFilterEnabled) IS FALSE OR a.acknowledged = :ackFilter) " + "AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " + - "AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.status) LIKE LOWER(CONCAT('%', :searchText, '%'))) ", + "AND (:searchText IS NULL OR (ilike(a.type, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.severity, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.status, CONCAT('%', :searchText, '%')) = true)) ", countQuery = "" + "SELECT count(a) " + "FROM AlarmInfoEntity a " + @@ -197,9 +200,9 @@ public interface AlarmRepository extends JpaRepository { "AND ((:clearFilterEnabled) IS FALSE OR a.cleared = :clearFilter) " + "AND ((:ackFilterEnabled) IS FALSE OR a.acknowledged = :ackFilter) " + "AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " + - "AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.status) LIKE LOWER(CONCAT('%', :searchText, '%'))) ") + "AND (:searchText IS NULL OR (ilike(a.type, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.severity, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.status, CONCAT('%', :searchText, '%')) = true))") Page findAllAlarmsV2(@Param("tenantId") UUID tenantId, @Param("startTime") Long startTime, @Param("endTime") Long endTime, @@ -221,9 +224,9 @@ public interface AlarmRepository extends JpaRepository { "AND ((:clearFilterEnabled) IS FALSE OR a.cleared = :clearFilter) " + "AND ((:ackFilterEnabled) IS FALSE OR a.acknowledged = :ackFilter) " + "AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " + - "AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.status) LIKE LOWER(CONCAT('%', :searchText, '%'))) " + "AND (:searchText IS NULL OR (ilike(a.type, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.severity, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.status, CONCAT('%', :searchText, '%')) = true)) " , countQuery = "" + "SELECT count(a) " + @@ -234,9 +237,9 @@ public interface AlarmRepository extends JpaRepository { "AND ((:clearFilterEnabled) IS FALSE OR a.cleared = :clearFilter) " + "AND ((:ackFilterEnabled) IS FALSE OR a.acknowledged = :ackFilter) " + "AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " + - "AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.status) LIKE LOWER(CONCAT('%', :searchText, '%'))) ") + "AND (:searchText IS NULL OR (ilike(a.type, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.severity, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.status, CONCAT('%', :searchText, '%')) = true))") Page findCustomerAlarms(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, @Param("startTime") Long startTime, @@ -259,9 +262,9 @@ public interface AlarmRepository extends JpaRepository { "AND ((:clearFilterEnabled) IS FALSE OR a.cleared = :clearFilter) " + "AND ((:ackFilterEnabled) IS FALSE OR a.acknowledged = :ackFilter) " + "AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " + - "AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.status) LIKE LOWER(CONCAT('%', :searchText, '%'))) " + "AND (:searchText IS NULL OR (ilike(a.type, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.severity, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.status, CONCAT('%', :searchText, '%')) = true)) " , countQuery = "" + "SELECT count(a) " + @@ -274,9 +277,9 @@ public interface AlarmRepository extends JpaRepository { "AND ((:clearFilterEnabled) IS FALSE OR a.cleared = :clearFilter) " + "AND ((:ackFilterEnabled) IS FALSE OR a.acknowledged = :ackFilter) " + "AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " + - "AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " + - " OR LOWER(a.status) LIKE LOWER(CONCAT('%', :searchText, '%'))) ") + "AND (:searchText IS NULL OR (ilike(a.type, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.severity, CONCAT('%', :searchText, '%')) = true " + + " OR ilike(a.status, CONCAT('%', :searchText, '%')) = true))") Page findCustomerAlarmsV2(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, @Param("startTime") Long startTime, @@ -315,6 +318,9 @@ public interface AlarmRepository extends JpaRepository { @Query(value = "SELECT a FROM AlarmInfoEntity a WHERE a.tenantId = :tenantId AND a.id = :alarmId") AlarmInfoEntity findAlarmInfoById(@Param("tenantId") UUID tenantId, @Param("alarmId") UUID alarmId); + @Query("SELECT a.id FROM AlarmEntity a WHERE a.tenantId = :tenantId AND a.assigneeId = :assigneeId") + Page findAlarmIdsByAssigneeId(@Param("tenantId") UUID tenantId, @Param("assigneeId") UUID assigneeId, Pageable pageable); + @Query(value = "SELECT create_or_update_active_alarm(:t_id, :c_id, :a_id, :a_created_ts, :a_o_id, :a_o_type, :a_type, :a_severity, " + ":a_start_ts, :a_end_ts, :a_details, :a_propagate, :a_propagate_to_owner, " + ":a_propagate_to_tenant, :a_propagation_types, :a_creation_enabled)", nativeQuery = true) @@ -345,4 +351,13 @@ public interface AlarmRepository extends JpaRepository { @Query(value = "SELECT unassign_alarm(:t_id, :a_id, :a_ts)", nativeQuery = true) String unassignAlarm(@Param("t_id") UUID tenantId, @Param("a_id") UUID alarmId, @Param("a_ts") long unassignTime); + + @Query(value = "SELECT at.type FROM alarm_types AS at WHERE at.tenant_id = :tenantId AND at.type ILIKE CONCAT('%', :searchText, '%')", nativeQuery = true) + Page findTenantAlarmTypes(@Param("tenantId") UUID tenantId, @Param("searchText") String searchText, Pageable pageable); + + @Transactional + @Modifying + @Query(value = "DELETE FROM alarm_types AS at WHERE NOT EXISTS (SELECT 1 FROM alarm AS a WHERE a.tenant_id = at.tenant_id AND a.type = at.type) AND at.tenant_id = :tenantId AND at.type IN (:types)", nativeQuery = true) + int deleteTypeIfNoAlarmsExist(@Param("tenantId") UUID tenantId, @Param("types") Set types); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/EntityAlarmRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/EntityAlarmRepository.java index ac27435df1..2ed2364621 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/EntityAlarmRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/EntityAlarmRepository.java @@ -34,4 +34,10 @@ public interface EntityAlarmRepository extends JpaRepository implements A asf.hasAckFilter(), asf.hasAckFilter() && asf.getAckFilter(), DaoUtil.getStringId(query.getAssigneeId()), - Objects.toString(query.getPageLink().getTextSearch(), ""), + query.getPageLink().getTextSearch(), DaoUtil.toPageable(query.getPageLink()) ) ); @@ -165,7 +170,7 @@ public class JpaAlarmDao extends JpaAbstractDao implements A asf.hasAckFilter(), asf.hasAckFilter() && asf.getAckFilter(), DaoUtil.getStringId(query.getAssigneeId()), - Objects.toString(query.getPageLink().getTextSearch(), ""), + query.getPageLink().getTextSearch(), DaoUtil.toPageable(query.getPageLink()) ) ); @@ -187,7 +192,7 @@ public class JpaAlarmDao extends JpaAbstractDao implements A asf.hasAckFilter(), asf.hasAckFilter() && asf.getAckFilter(), DaoUtil.getStringId(query.getAssigneeId()), - Objects.toString(query.getPageLink().getTextSearch(), ""), + query.getPageLink().getTextSearch(), DaoUtil.toPageable(query.getPageLink()) ) ); @@ -215,7 +220,7 @@ public class JpaAlarmDao extends JpaAbstractDao implements A asf.hasAckFilter(), asf.hasAckFilter() && asf.getAckFilter(), DaoUtil.getStringId(query.getAssigneeId()), - Objects.toString(query.getPageLink().getTextSearch(), ""), + query.getPageLink().getTextSearch(), DaoUtil.toPageable(query.getPageLink()) ) ); @@ -232,7 +237,7 @@ public class JpaAlarmDao extends JpaAbstractDao implements A asf.hasAckFilter(), asf.hasAckFilter() && asf.getAckFilter(), DaoUtil.getStringId(query.getAssigneeId()), - Objects.toString(query.getPageLink().getTextSearch(), ""), + query.getPageLink().getTextSearch(), DaoUtil.toPageable(query.getPageLink()) ) ); @@ -258,7 +263,7 @@ public class JpaAlarmDao extends JpaAbstractDao implements A asf.hasAckFilter(), asf.hasAckFilter() && asf.getAckFilter(), DaoUtil.getStringId(query.getAssigneeId()), - Objects.toString(query.getPageLink().getTextSearch(), ""), + query.getPageLink().getTextSearch(), DaoUtil.toPageable(query.getPageLink()) ) ); @@ -285,6 +290,12 @@ public class JpaAlarmDao extends JpaAbstractDao implements A .mapData(AlarmId::new); } + @Override + public PageData findAlarmIdsByAssigneeId(TenantId tenantId, UUID userId, PageLink pageLink) { + return DaoUtil.pageToPageData(alarmRepository.findAlarmIdsByAssigneeId(tenantId.getId(), userId, DaoUtil.toPageable(pageLink))) + .mapData(AlarmId::new); + } + @Override public void createEntityAlarmRecord(EntityAlarm entityAlarm) { log.debug("Saving entity {}", entityAlarm); @@ -299,10 +310,14 @@ public class JpaAlarmDao extends JpaAbstractDao implements A @Override public void deleteEntityAlarmRecords(TenantId tenantId, EntityId entityId) { - log.trace("[{}] Try to delete entity alarm records using [{}]", tenantId, entityId); entityAlarmRepository.deleteByEntityId(entityId.getId()); } + @Override + public void deleteEntityAlarmRecordsByTenantId(TenantId tenantId) { + entityAlarmRepository.deleteByTenantId(tenantId.getId()); + } + @Override public AlarmApiCallResult createOrUpdateActiveAlarm(AlarmCreateOrUpdateActiveRequest request, boolean alarmCreationEnabled) { AlarmPropagationInfo ap = getSafePropagationInfo(request.getPropagation()); @@ -366,6 +381,22 @@ public class JpaAlarmDao extends JpaAbstractDao implements A return alarmQueryRepository.countAlarmsByQuery(tenantId, customerId, query); } + @Override + public PageData findTenantAlarmTypes(UUID tenantId, PageLink pageLink) { + Page page = alarmRepository.findTenantAlarmTypes(tenantId, Objects.toString(pageLink.getTextSearch(), ""), toPageable(pageLink)); + if (page.isEmpty()) { + return PageData.emptyPageData(); + } + + List data = convertTenantEntityTypesToDto(tenantId, EntityType.ALARM, page.getContent()); + return new PageData<>(data, page.getTotalPages(), page.getTotalElements(), page.hasNext()); + } + + @Override + public boolean removeAlarmTypesIfNoAlarmsPresent(UUID tenantId, Set types) { + return alarmRepository.deleteTypeIfNoAlarmsExist(tenantId, types) > 0; + } + private static String getPropagationTypes(AlarmPropagationInfo ap) { String propagateRelationTypes; if (!CollectionUtils.isEmpty(ap.getPropagateRelationTypes())) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetProfileRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetProfileRepository.java index b18f80bcd8..27dde2fc51 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetProfileRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetProfileRepository.java @@ -20,10 +20,12 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.asset.AssetProfileInfo; import org.thingsboard.server.dao.ExportableEntityRepository; import org.thingsboard.server.dao.model.sql.AssetProfileEntity; +import java.util.List; import java.util.UUID; public interface AssetProfileRepository extends JpaRepository, ExportableEntityRepository { @@ -34,14 +36,14 @@ public interface AssetProfileRepository extends JpaRepository findAssetProfiles(@Param("tenantId") UUID tenantId, @Param("textSearch") String textSearch, Pageable pageable); @Query("SELECT new org.thingsboard.server.common.data.asset.AssetProfileInfo(a.id, a.tenantId, a.name, a.image, a.defaultDashboardId) " + "FROM AssetProfileEntity a WHERE " + - "a.tenantId = :tenantId AND LOWER(a.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + "a.tenantId = :tenantId AND (:textSearch IS NULL OR ilike(a.name, CONCAT('%', :textSearch, '%')) = true)") Page findAssetProfileInfos(@Param("tenantId") UUID tenantId, @Param("textSearch") String textSearch, Pageable pageable); @@ -60,4 +62,23 @@ public interface AssetProfileRepository extends JpaRepository findByTenantAndImageLink(@Param("tenantId") UUID tenantId, @Param("imageLink") String imageLink, Pageable page); + + @Query("SELECT new org.thingsboard.server.common.data.asset.AssetProfileInfo(a.id, a.tenantId, a.name, a.image, a.defaultDashboardId) " + + "FROM AssetProfileEntity a WHERE a.image = :imageLink") + List findByImageLink(@Param("imageLink") String imageLink, Pageable page); + + Page findAllByImageNotNull(Pageable pageable); + + @Query("SELECT new org.thingsboard.server.common.data.EntityInfo(ap.id, 'ASSET_PROFILE', ap.name) " + + "FROM AssetProfileEntity ap WHERE ap.tenantId = :tenantId AND EXISTS " + + "(SELECT 1 FROM AssetEntity a WHERE a.tenantId = :tenantId AND a.assetProfileId = ap.id)") + List findActiveTenantAssetProfileNames(@Param("tenantId") UUID tenantId); + + @Query("SELECT new org.thingsboard.server.common.data.EntityInfo(a.id, 'ASSET_PROFILE', a.name) " + + "FROM AssetProfileEntity a WHERE a.tenantId = :tenantId") + List findAllTenantAssetProfileNames(@Param("tenantId") UUID tenantId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java index b14f346cd4..0c2db8ec66 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java @@ -41,7 +41,7 @@ public interface AssetRepository extends JpaRepository, Expor AssetInfoEntity findAssetInfoById(@Param("assetId") UUID assetId); @Query("SELECT a FROM AssetEntity a WHERE a.tenantId = :tenantId " + - "AND LOWER(a.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + "AND (:textSearch IS NULL OR ilike(a.name, CONCAT('%', :textSearch, '%')) = true)") Page findByTenantId(@Param("tenantId") UUID tenantId, @Param("textSearch") String textSearch, Pageable pageable); @@ -51,17 +51,17 @@ public interface AssetRepository extends JpaRepository, Expor "LEFT JOIN CustomerEntity c on c.id = a.customerId " + "LEFT JOIN AssetProfileEntity p on p.id = a.assetProfileId " + "WHERE a.tenantId = :tenantId " + - "AND (LOWER(a.name) LIKE LOWER(CONCAT('%', :textSearch, '%')) " + - "OR LOWER(a.label) LIKE LOWER(CONCAT('%', :textSearch, '%')) " + - "OR LOWER(p.name) LIKE LOWER(CONCAT('%', :textSearch, '%')) " + - "OR LOWER(c.title) LIKE LOWER(CONCAT('%', :textSearch, '%')))") + "AND (:textSearch IS NULL OR ilike(a.name, CONCAT('%', :textSearch, '%')) = true " + + " OR ilike(a.label, CONCAT('%', :textSearch, '%')) = true " + + " OR ilike(p.name, CONCAT('%', :textSearch, '%')) = true " + + " OR ilike(c.title, CONCAT('%', :textSearch, '%')) = true) ") Page findAssetInfosByTenantId(@Param("tenantId") UUID tenantId, @Param("textSearch") String textSearch, Pageable pageable); @Query("SELECT a FROM AssetEntity a WHERE a.tenantId = :tenantId " + "AND a.customerId = :customerId " + - "AND LOWER(a.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + "AND (:textSearch IS NULL OR ilike(a.name, CONCAT('%', :textSearch, '%')) = true)") Page findByTenantIdAndCustomerId(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, @Param("textSearch") String textSearch, @@ -69,7 +69,7 @@ public interface AssetRepository extends JpaRepository, Expor @Query("SELECT a FROM AssetEntity a WHERE a.tenantId = :tenantId " + "AND a.assetProfileId = :profileId " + - "AND LOWER(a.name) LIKE LOWER(CONCAT('%', :searchText, '%'))") + "AND (:searchText IS NULL OR ilike(a.name, CONCAT('%', :searchText, '%')) = true)") Page findByTenantIdAndProfileId(@Param("tenantId") UUID tenantId, @Param("profileId") UUID profileId, @Param("searchText") String searchText, @@ -81,7 +81,7 @@ public interface AssetRepository extends JpaRepository, Expor "LEFT JOIN AssetProfileEntity p on p.id = a.assetProfileId " + "WHERE a.tenantId = :tenantId " + "AND a.customerId = :customerId " + - "AND LOWER(a.name) LIKE LOWER(CONCAT('%', :searchText, '%'))") + "AND (:searchText IS NULL OR ilike(a.name, CONCAT('%', :searchText, '%')) = true)") Page findAssetInfosByTenantIdAndCustomerId(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, @Param("searchText") String searchText, @@ -95,7 +95,7 @@ public interface AssetRepository extends JpaRepository, Expor @Query("SELECT a FROM AssetEntity a WHERE a.tenantId = :tenantId " + "AND a.type = :type " + - "AND LOWER(a.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + "AND (:textSearch IS NULL OR ilike(a.name, CONCAT('%', :textSearch, '%')) = true)") Page findByTenantIdAndType(@Param("tenantId") UUID tenantId, @Param("type") String type, @Param("textSearch") String textSearch, @@ -107,9 +107,9 @@ public interface AssetRepository extends JpaRepository, Expor "LEFT JOIN AssetProfileEntity p on p.id = a.assetProfileId " + "WHERE a.tenantId = :tenantId " + "AND a.type = :type " + - "AND (LOWER(a.name) LIKE LOWER(CONCAT('%', :textSearch, '%')) " + - "OR LOWER(a.label) LIKE LOWER(CONCAT('%', :textSearch, '%')) " + - "OR LOWER(c.title) LIKE LOWER(CONCAT('%', :textSearch, '%')))") + "AND (:textSearch IS NULL OR ilike(a.name, CONCAT('%', :textSearch, '%')) = true " + + " OR ilike(a.label, CONCAT('%', :textSearch, '%')) = true " + + " OR ilike(c.title, CONCAT('%', :textSearch, '%')) = true) ") Page findAssetInfosByTenantIdAndType(@Param("tenantId") UUID tenantId, @Param("type") String type, @Param("textSearch") String textSearch, @@ -121,9 +121,9 @@ public interface AssetRepository extends JpaRepository, Expor "LEFT JOIN AssetProfileEntity p on p.id = a.assetProfileId " + "WHERE a.tenantId = :tenantId " + "AND a.assetProfileId = :assetProfileId " + - "AND (LOWER(a.name) LIKE LOWER(CONCAT('%', :textSearch, '%')) " + - "OR LOWER(a.label) LIKE LOWER(CONCAT('%', :textSearch, '%')) " + - "OR LOWER(c.title) LIKE LOWER(CONCAT('%', :textSearch, '%')))") + "AND (:textSearch IS NULL OR ilike(a.name, CONCAT('%', :textSearch, '%')) = true " + + " OR ilike(a.label, CONCAT('%', :textSearch, '%')) = true " + + " OR ilike(c.title, CONCAT('%', :textSearch, '%')) = true) ") Page findAssetInfosByTenantIdAndAssetProfileId(@Param("tenantId") UUID tenantId, @Param("assetProfileId") UUID assetProfileId, @Param("textSearch") String textSearch, @@ -132,7 +132,7 @@ public interface AssetRepository extends JpaRepository, Expor @Query("SELECT a FROM AssetEntity a WHERE a.tenantId = :tenantId " + "AND a.customerId = :customerId AND a.type = :type " + - "AND LOWER(a.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + "AND (:textSearch IS NULL OR ilike(a.name, CONCAT('%', :textSearch, '%')) = true)") Page findByTenantIdAndCustomerIdAndType(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, @Param("type") String type, @@ -146,7 +146,7 @@ public interface AssetRepository extends JpaRepository, Expor "WHERE a.tenantId = :tenantId " + "AND a.customerId = :customerId " + "AND a.type = :type " + - "AND LOWER(a.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + "AND (:textSearch IS NULL OR ilike(a.name, CONCAT('%', :textSearch, '%')) = true)") Page findAssetInfosByTenantIdAndCustomerIdAndType(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, @Param("type") String type, @@ -160,22 +160,19 @@ public interface AssetRepository extends JpaRepository, Expor "WHERE a.tenantId = :tenantId " + "AND a.customerId = :customerId " + "AND a.assetProfileId = :assetProfileId " + - "AND LOWER(a.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + "AND (:textSearch IS NULL OR ilike(a.name, CONCAT('%', :textSearch, '%')) = true)") Page findAssetInfosByTenantIdAndCustomerIdAndAssetProfileId(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, @Param("assetProfileId") UUID assetProfileId, @Param("textSearch") String textSearch, Pageable pageable); - @Query("SELECT DISTINCT a.type FROM AssetEntity a WHERE a.tenantId = :tenantId") - List findTenantAssetTypes(@Param("tenantId") UUID tenantId); - Long countByAssetProfileId(UUID assetProfileId); @Query("SELECT a FROM AssetEntity a, RelationEntity re WHERE a.tenantId = :tenantId " + "AND a.id = re.toId AND re.toType = 'ASSET' AND re.relationTypeGroup = 'EDGE' " + "AND re.relationType = 'Contains' AND re.fromId = :edgeId AND re.fromType = 'EDGE' " + - "AND LOWER(a.name) LIKE LOWER(CONCAT('%', :searchText, '%'))") + "AND (:searchText IS NULL OR ilike(a.name, CONCAT('%', :searchText, '%')) = true)") Page findByTenantIdAndEdgeId(@Param("tenantId") UUID tenantId, @Param("edgeId") UUID edgeId, @Param("searchText") String searchText, @@ -185,7 +182,7 @@ public interface AssetRepository extends JpaRepository, Expor "AND a.id = re.toId AND re.toType = 'ASSET' AND re.relationTypeGroup = 'EDGE' " + "AND re.relationType = 'Contains' AND re.fromId = :edgeId AND re.fromType = 'EDGE' " + "AND a.type = :type " + - "AND LOWER(a.name) LIKE LOWER(CONCAT('%', :searchText, '%'))") + "AND (:searchText IS NULL OR ilike(a.name, CONCAT('%', :searchText, '%')) = true)") Page findByTenantIdAndEdgeIdAndType(@Param("tenantId") UUID tenantId, @Param("edgeId") UUID edgeId, @Param("type") String type, diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java index ec5a8318f8..48dd0996de 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java @@ -37,14 +37,12 @@ import org.thingsboard.server.dao.model.sql.AssetInfoEntity; import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.UUID; +import static org.thingsboard.server.dao.DaoUtil.convertTenantEntityInfosToDto; import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE; /** @@ -58,6 +56,9 @@ public class JpaAssetDao extends JpaAbstractDao implements A @Autowired private AssetRepository assetRepository; + @Autowired + private AssetProfileRepository assetProfileRepository; + @Override protected Class getEntityClass() { return AssetEntity.class; @@ -78,7 +79,7 @@ public class JpaAssetDao extends JpaAbstractDao implements A return DaoUtil.toPageData(assetRepository .findByTenantId( tenantId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @@ -87,7 +88,7 @@ public class JpaAssetDao extends JpaAbstractDao implements A return DaoUtil.toPageData( assetRepository.findAssetInfosByTenantId( tenantId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink, AssetInfoEntity.assetInfoColumnMap))); } @@ -103,7 +104,7 @@ public class JpaAssetDao extends JpaAbstractDao implements A .findByTenantIdAndCustomerId( tenantId, customerId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @@ -113,7 +114,7 @@ public class JpaAssetDao extends JpaAbstractDao implements A assetRepository.findAssetInfosByTenantIdAndCustomerId( tenantId, customerId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink, AssetInfoEntity.assetInfoColumnMap))); } @@ -135,7 +136,7 @@ public class JpaAssetDao extends JpaAbstractDao implements A .findByTenantIdAndType( tenantId, type, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @@ -145,7 +146,7 @@ public class JpaAssetDao extends JpaAbstractDao implements A assetRepository.findAssetInfosByTenantIdAndType( tenantId, type, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink, AssetInfoEntity.assetInfoColumnMap))); } @@ -155,7 +156,7 @@ public class JpaAssetDao extends JpaAbstractDao implements A assetRepository.findAssetInfosByTenantIdAndAssetProfileId( tenantId, assetProfileId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink, AssetInfoEntity.assetInfoColumnMap))); } @@ -166,7 +167,7 @@ public class JpaAssetDao extends JpaAbstractDao implements A tenantId, customerId, type, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @@ -177,7 +178,7 @@ public class JpaAssetDao extends JpaAbstractDao implements A tenantId, customerId, type, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink, AssetInfoEntity.assetInfoColumnMap))); } @@ -188,13 +189,13 @@ public class JpaAssetDao extends JpaAbstractDao implements A tenantId, customerId, assetProfileId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink, AssetInfoEntity.assetInfoColumnMap))); } @Override public ListenableFuture> findTenantAssetTypesAsync(UUID tenantId) { - return service.submit(() -> convertTenantAssetTypesToDto(tenantId, assetRepository.findTenantAssetTypes(tenantId))); + return service.submit(() -> convertTenantEntityInfosToDto(tenantId, EntityType.ASSET, assetProfileRepository.findActiveTenantAssetProfileNames(tenantId))); } @Override @@ -208,21 +209,10 @@ public class JpaAssetDao extends JpaAbstractDao implements A assetRepository.findByTenantIdAndProfileId( tenantId, profileId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } - private List convertTenantAssetTypesToDto(UUID tenantId, List types) { - List list = Collections.emptyList(); - if (types != null && !types.isEmpty()) { - list = new ArrayList<>(); - for (String type : types) { - list.add(new EntitySubtype(TenantId.fromUUID(tenantId), EntityType.ASSET, type)); - } - } - return list; - } - @Override public PageData findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, PageLink pageLink) { log.debug("Try to find assets by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); @@ -230,7 +220,7 @@ public class JpaAssetDao extends JpaAbstractDao implements A .findByTenantIdAndEdgeId( tenantId, edgeId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @@ -242,7 +232,7 @@ public class JpaAssetDao extends JpaAbstractDao implements A tenantId, edgeId, type, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetProfileDao.java index fb709b56dc..d409b2c4d2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetProfileDao.java @@ -16,9 +16,11 @@ package org.thingsboard.server.dao.sql.asset; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.asset.AssetProfileInfo; @@ -31,7 +33,7 @@ import org.thingsboard.server.dao.asset.AssetProfileDao; import org.thingsboard.server.dao.model.sql.AssetProfileEntity; import org.thingsboard.server.dao.sql.JpaAbstractDao; -import java.util.Objects; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -69,7 +71,7 @@ public class JpaAssetProfileDao extends JpaAbstractDao findAllWithImages(PageLink pageLink) { + return DaoUtil.toPageData(assetProfileRepository.findAllByImageNotNull(DaoUtil.toPageable(pageLink))); + } + + @Override + public List findTenantAssetProfileNames(UUID tenantId, boolean activeOnly) { + return activeOnly ? + assetProfileRepository.findActiveTenantAssetProfileNames(tenantId) : + assetProfileRepository.findAllTenantAssetProfileNames(tenantId); + } + @Override public AssetProfile findByTenantIdAndExternalId(UUID tenantId, UUID externalId) { return DaoUtil.getData(assetProfileRepository.findByTenantIdAndExternalId(tenantId, externalId)); @@ -118,6 +132,16 @@ public class JpaAssetProfileDao extends JpaAbstractDao findByTenantAndImageLink(TenantId tenantId, String imageLink, int limit) { + return assetProfileRepository.findByTenantAndImageLink(tenantId.getId(), imageLink, PageRequest.of(0, limit)); + } + + @Override + public List findByImageLink(String imageLink, int limit) { + return assetProfileRepository.findByImageLink(imageLink, PageRequest.of(0, limit)); + } + @Override public EntityType getEntityType() { return EntityType.ASSET_PROFILE; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/audit/AuditLogRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/audit/AuditLogRepository.java index 3d35383eb0..cd3afa4530 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/audit/AuditLogRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/audit/AuditLogRepository.java @@ -34,11 +34,11 @@ public interface AuditLogRepository extends JpaRepository "AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " + "AND ((:actionTypes) IS NULL OR a.actionType in (:actionTypes)) " + - "AND (LOWER(a.entityType) LIKE LOWER(CONCAT('%', :textSearch, '%'))" + - "OR LOWER(a.entityName) LIKE LOWER(CONCAT('%', :textSearch, '%'))" + - "OR LOWER(a.userName) LIKE LOWER(CONCAT('%', :textSearch, '%'))" + - "OR LOWER(a.actionType) LIKE LOWER(CONCAT('%', :textSearch, '%'))" + - "OR LOWER(a.actionStatus) LIKE LOWER(CONCAT('%', :textSearch, '%')))" + "AND (:textSearch IS NULL OR ilike(a.entityType, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(a.entityName, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(a.userName, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(a.actionType, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(a.actionStatus, CONCAT('%', :textSearch, '%')) = true)" ) Page findByTenantId( @Param("tenantId") UUID tenantId, @@ -54,10 +54,10 @@ public interface AuditLogRepository extends JpaRepository "AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " + "AND ((:actionTypes) IS NULL OR a.actionType in (:actionTypes)) " + - "AND (LOWER(a.entityName) LIKE LOWER(CONCAT('%', :textSearch, '%'))" + - "OR LOWER(a.userName) LIKE LOWER(CONCAT('%', :textSearch, '%'))" + - "OR LOWER(a.actionType) LIKE LOWER(CONCAT('%', :textSearch, '%'))" + - "OR LOWER(a.actionStatus) LIKE LOWER(CONCAT('%', :textSearch, '%')))" + "AND (:textSearch IS NULL OR ilike(a.entityName, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(a.userName, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(a.actionType, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(a.actionStatus, CONCAT('%', :textSearch, '%')) = true)" ) Page findAuditLogsByTenantIdAndEntityId(@Param("tenantId") UUID tenantId, @Param("entityType") EntityType entityType, @@ -74,11 +74,11 @@ public interface AuditLogRepository extends JpaRepository "AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " + "AND ((:actionTypes) IS NULL OR a.actionType in (:actionTypes)) " + - "AND (LOWER(a.entityType) LIKE LOWER(CONCAT('%', :textSearch, '%'))" + - "OR LOWER(a.entityName) LIKE LOWER(CONCAT('%', :textSearch, '%'))" + - "OR LOWER(a.userName) LIKE LOWER(CONCAT('%', :textSearch, '%'))" + - "OR LOWER(a.actionType) LIKE LOWER(CONCAT('%', :textSearch, '%'))" + - "OR LOWER(a.actionStatus) LIKE LOWER(CONCAT('%', :textSearch, '%')))" + "AND (:textSearch IS NULL OR ilike(a.entityType, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(a.entityName, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(a.userName, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(a.actionType, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(a.actionStatus, CONCAT('%', :textSearch, '%')) = true)" ) Page findAuditLogsByTenantIdAndCustomerId(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, @@ -94,10 +94,10 @@ public interface AuditLogRepository extends JpaRepository "AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " + "AND ((:actionTypes) IS NULL OR a.actionType in (:actionTypes)) " + - "AND (LOWER(a.entityType) LIKE LOWER(CONCAT('%', :textSearch, '%'))" + - "OR LOWER(a.entityName) LIKE LOWER(CONCAT('%', :textSearch, '%'))" + - "OR LOWER(a.actionType) LIKE LOWER(CONCAT('%', :textSearch, '%'))" + - "OR LOWER(a.actionStatus) LIKE LOWER(CONCAT('%', :textSearch, '%')))" + "AND (:textSearch IS NULL OR ilike(a.entityType, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(a.entityName, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(a.actionType, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(a.actionStatus, CONCAT('%', :textSearch, '%')) = true)" ) Page findAuditLogsByTenantIdAndUserId(@Param("tenantId") UUID tenantId, @Param("userId") UUID userId, diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/audit/JpaAuditLogDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/audit/JpaAuditLogDao.java index a605f515d0..f9cbefbca1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/audit/JpaAuditLogDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/audit/JpaAuditLogDao.java @@ -99,7 +99,7 @@ public class JpaAuditLogDao extends JpaAbstractDao imp tenantId, entityId.getEntityType(), entityId.getId(), - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), pageLink.getStartTime(), pageLink.getEndTime(), actionTypes, @@ -113,7 +113,7 @@ public class JpaAuditLogDao extends JpaAbstractDao imp .findAuditLogsByTenantIdAndCustomerId( tenantId, customerId.getId(), - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), pageLink.getStartTime(), pageLink.getEndTime(), actionTypes, @@ -127,7 +127,7 @@ public class JpaAuditLogDao extends JpaAbstractDao imp .findAuditLogsByTenantIdAndUserId( tenantId, userId.getId(), - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), pageLink.getStartTime(), pageLink.getEndTime(), actionTypes, @@ -139,7 +139,7 @@ public class JpaAuditLogDao extends JpaAbstractDao imp return DaoUtil.toPageData( auditLogRepository.findByTenantId( tenantId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), pageLink.getStartTime(), pageLink.getEndTime(), actionTypes, diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/component/AbstractComponentDescriptorInsertRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/component/AbstractComponentDescriptorInsertRepository.java index b385bc2344..ad8a07be08 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/component/AbstractComponentDescriptorInsertRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/component/AbstractComponentDescriptorInsertRepository.java @@ -73,6 +73,7 @@ public abstract class AbstractComponentDescriptorInsertRepository implements Com .setParameter("actions", entity.getActions()) .setParameter("clazz", entity.getClazz()) .setParameter("configuration_descriptor", entity.getConfigurationDescriptor().toString()) + .setParameter("configuration_version", entity.getConfigurationVersion()) .setParameter("name", entity.getName()) .setParameter("scope", entity.getScope().name()) .setParameter("type", entity.getType().name()) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/component/ComponentDescriptorRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/component/ComponentDescriptorRepository.java index aef31602fb..e5b65f5473 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/component/ComponentDescriptorRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/component/ComponentDescriptorRepository.java @@ -36,13 +36,13 @@ public interface ComponentDescriptorRepository extends JpaRepository findByType(@Param("type") ComponentType type, @Param("textSearch") String textSearch, Pageable pageable); - @Query("SELECT cd FROM ComponentDescriptorEntity cd WHERE cd.type = :type " + - "AND cd.scope = :scope AND LOWER(cd.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + @Query("SELECT cd FROM ComponentDescriptorEntity cd WHERE cd.type = :type AND cd.scope = :scope " + + "AND (:textSearch IS NULL OR ilike(cd.name, CONCAT('%', :textSearch, '%')) = true)") Page findByScopeAndType(@Param("type") ComponentType type, @Param("scope") ComponentScope scope, @Param("textSearch") String textSearch, diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java index 58a6c784cb..24ed28383c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java @@ -89,7 +89,7 @@ public class JpaBaseComponentDescriptorDao extends JpaAbstractDao, ExportableEntityRepository { @Query("SELECT c FROM CustomerEntity c WHERE c.tenantId = :tenantId " + - "AND LOWER(c.title) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + "AND (:textSearch IS NULL OR ilike(c.title, CONCAT('%', :textSearch, '%')) = true)") Page findByTenantId(@Param("tenantId") UUID tenantId, @Param("textSearch") String textSearch, Pageable pageable); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java index a10ea6765c..6dd03625b9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java @@ -58,7 +58,7 @@ public class JpaCustomerDao extends JpaAbstractDao imp public PageData findCustomersByTenantId(UUID tenantId, PageLink pageLink) { return DaoUtil.toPageData(customerRepository.findByTenantId( tenantId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java index d8b07e9364..35c765dccf 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java @@ -22,6 +22,7 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.thingsboard.server.dao.model.sql.DashboardInfoEntity; +import java.util.List; import java.util.UUID; /** @@ -32,14 +33,14 @@ public interface DashboardInfoRepository extends JpaRepository findByTenantId(@Param("tenantId") UUID tenantId, @Param("searchText") String searchText, Pageable pageable); @Query("SELECT di FROM DashboardInfoEntity di WHERE di.tenantId = :tenantId " + "AND di.mobileHide = false " + - "AND LOWER(di.title) LIKE LOWER(CONCAT('%', :searchText, '%'))") + "AND (:searchText IS NULL OR ilike(di.title, CONCAT('%', :searchText, '%')) = true)") Page findMobileByTenantId(@Param("tenantId") UUID tenantId, @Param("searchText") String searchText, Pageable pageable); @@ -47,7 +48,7 @@ public interface DashboardInfoRepository extends JpaRepository findByTenantIdAndCustomerId(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, @Param("searchText") String searchText, @@ -57,7 +58,7 @@ public interface DashboardInfoRepository extends JpaRepository findMobileByTenantIdAndCustomerId(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, @Param("searchText") String searchText, @@ -66,7 +67,7 @@ public interface DashboardInfoRepository extends JpaRepository findByTenantIdAndEdgeId(@Param("tenantId") UUID tenantId, @Param("edgeId") UUID edgeId, @Param("searchText") String searchText, @@ -74,4 +75,16 @@ public interface DashboardInfoRepository extends JpaRepository findByTenantAndImageLink(@Param("tenantId") UUID tenantId, @Param("imageLink") String imageLink, @Param("lmt") int lmt); + + @Query(nativeQuery = true, + value = "SELECT * FROM dashboard d WHERE d.image = :imageLink or d.configuration ILIKE CONCAT('%\"', :imageLink, '\"%') limit :lmt" + ) + List findByImageLink(@Param("imageLink") String imageLink, @Param("lmt") int lmt); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardRepository.java index 1ee316bb7d..3adc50d2aa 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardRepository.java @@ -40,4 +40,10 @@ public interface DashboardRepository extends JpaRepository findIdsByTenantId(@Param("tenantId") UUID tenantId, Pageable pageable); + + @Query("SELECT d.id FROM DashboardEntity d") + Page findAllIds(Pageable pageable); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java index 35e746bc05..2a85692459 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java @@ -80,6 +80,16 @@ public class JpaDashboardDao extends JpaAbstractDao return DaoUtil.convertDataList(dashboardRepository.findByTenantIdAndTitle(tenantId, title)); } + @Override + public PageData findIdsByTenantId(TenantId tenantId, PageLink pageLink) { + return DaoUtil.pageToPageData(dashboardRepository.findIdsByTenantId(tenantId.getId(), DaoUtil.toPageable(pageLink)).map(DashboardId::new)); + } + + @Override + public PageData findAllIds(PageLink pageLink) { + return DaoUtil.pageToPageData(dashboardRepository.findAllIds(DaoUtil.toPageable(pageLink)).map(DashboardId::new)); + } + @Override public EntityType getEntityType() { return EntityType.DASHBOARD; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java index 2e2148f089..c9318db61e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java @@ -20,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.DashboardInfo; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.SortOrder; @@ -31,7 +32,6 @@ import org.thingsboard.server.dao.util.SqlDao; import java.util.ArrayList; import java.util.List; -import java.util.Objects; import java.util.UUID; /** @@ -60,7 +60,7 @@ public class JpaDashboardInfoDao extends JpaAbstractDao findByTenantAndImageLink(TenantId tenantId, String imageLink, int limit) { + return DaoUtil.convertDataList(dashboardInfoRepository.findByTenantAndImageLink(tenantId.getId(), imageLink, limit)); + } + + @Override + public List findByImageLink(String imageLink, int limit) { + return DaoUtil.convertDataList(dashboardInfoRepository.findByImageLink(imageLink, limit)); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceCredentialsRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceCredentialsRepository.java index 16bde4178c..5f66b17458 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceCredentialsRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceCredentialsRepository.java @@ -16,6 +16,9 @@ package org.thingsboard.server.dao.sql.device; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.dao.model.sql.DeviceCredentialsEntity; import java.util.UUID; @@ -28,4 +31,9 @@ public interface DeviceCredentialsRepository extends JpaRepository, ExportableEntityRepository { @@ -34,22 +36,22 @@ public interface DeviceProfileRepository extends JpaRepository findDeviceProfiles(@Param("tenantId") UUID tenantId, @Param("textSearch") String textSearch, Pageable pageable); @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.tenantId, d.name, d.image, d.defaultDashboardId, d.type, d.transportType) " + - "FROM DeviceProfileEntity d WHERE " + - "d.tenantId = :tenantId AND LOWER(d.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + "FROM DeviceProfileEntity d WHERE d.tenantId = :tenantId " + + "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true)") Page findDeviceProfileInfos(@Param("tenantId") UUID tenantId, @Param("textSearch") String textSearch, Pageable pageable); @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.tenantId, d.name, d.image, d.defaultDashboardId, d.type, d.transportType) " + - "FROM DeviceProfileEntity d WHERE " + - "d.tenantId = :tenantId AND d.transportType = :transportType AND LOWER(d.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + "FROM DeviceProfileEntity d WHERE d.tenantId = :tenantId AND d.transportType = :transportType " + + "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true)") Page findDeviceProfileInfos(@Param("tenantId") UUID tenantId, @Param("textSearch") String textSearch, @Param("transportType") DeviceTransportType transportType, @@ -68,7 +70,26 @@ public interface DeviceProfileRepository extends JpaRepository findByTenantAndImageLink(@Param("tenantId") UUID tenantId, @Param("imageLink") String imageLink, Pageable page); + + @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.tenantId, d.name, d.image, d.defaultDashboardId, d.type, d.transportType) " + + "FROM DeviceProfileEntity d WHERE d.image = :imageLink") + List findByImageLink(@Param("imageLink") String imageLink, Pageable page); + @Query("SELECT externalId FROM DeviceProfileEntity WHERE id = :id") UUID getExternalIdById(@Param("id") UUID id); + Page findAllByImageNotNull(Pageable pageable); + + @Query("SELECT new org.thingsboard.server.common.data.EntityInfo(dp.id, 'DEVICE_PROFILE', dp.name) " + + "FROM DeviceProfileEntity dp WHERE dp.tenantId = :tenantId AND EXISTS " + + "(SELECT 1 FROM DeviceEntity dv WHERE dv.tenantId = :tenantId AND dv.deviceProfileId = dp.id)") + List findActiveTenantDeviceProfileNames(@Param("tenantId") UUID tenantId); + + @Query("SELECT new org.thingsboard.server.common.data.EntityInfo(d.id, 'DEVICE_PROFILE', d.name) " + + "FROM DeviceProfileEntity d WHERE d.tenantId = :tenantId") + List findAllTenantDeviceProfileNames(@Param("tenantId") UUID tenantId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java index 56f6a7c647..97ddb94b88 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java @@ -35,8 +35,8 @@ public interface DeviceRepository extends JpaRepository, Exp @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " + "AND d.customerId = :customerId " + - "AND (LOWER(d.name) LIKE LOWER(CONCAT('%', :textSearch, '%')) " + - "OR LOWER(d.label) LIKE LOWER(CONCAT('%', :textSearch, '%')))") + "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(d.label, CONCAT('%', :textSearch, '%')) = true)") Page findByTenantIdAndCustomerId(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, @Param("textSearch") String textSearch, @@ -44,8 +44,8 @@ public interface DeviceRepository extends JpaRepository, Exp @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " + "AND d.deviceProfileId = :profileId " + - "AND (LOWER(d.name) LIKE LOWER(CONCAT('%', :textSearch, '%')) " + - "OR LOWER(d.label) LIKE LOWER(CONCAT('%', :textSearch, '%')))") + "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(d.label, CONCAT('%', :textSearch, '%')) = true)") Page findByTenantIdAndProfileId(@Param("tenantId") UUID tenantId, @Param("profileId") UUID profileId, @Param("textSearch") String textSearch, @@ -54,8 +54,8 @@ public interface DeviceRepository extends JpaRepository, Exp @Query("SELECT d FROM DeviceInfoEntity d " + "WHERE d.tenantId = :tenantId " + "AND d.customerId = :customerId " + - "AND (LOWER(d.name) LIKE LOWER(CONCAT('%', :textSearch, '%')) " + - "OR LOWER(d.label) LIKE LOWER(CONCAT('%', :textSearch, '%')))") + "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(d.label, CONCAT('%', :textSearch, '%')) = true)") Page findDeviceInfosByTenantIdAndCustomerId(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, @Param("textSearch") String textSearch, @@ -66,16 +66,16 @@ public interface DeviceRepository extends JpaRepository, Exp Pageable pageable); @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " + - "AND (LOWER(d.name) LIKE LOWER(CONCAT('%', :textSearch, '%')) " + - "OR LOWER(d.label) LIKE LOWER(CONCAT('%', :textSearch, '%')))") + "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(d.label, CONCAT('%', :textSearch, '%')) = true)") Page findByTenantId(@Param("tenantId") UUID tenantId, @Param("textSearch") String textSearch, Pageable pageable); @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " + "AND d.type = :type " + - "AND (LOWER(d.name) LIKE LOWER(CONCAT('%', :textSearch, '%')) " + - "OR LOWER(d.label) LIKE LOWER(CONCAT('%', :textSearch, '%')))") + "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(d.label, CONCAT('%', :textSearch, '%')) = true)") Page findByTenantIdAndType(@Param("tenantId") UUID tenantId, @Param("type") String type, @Param("textSearch") String textSearch, @@ -84,8 +84,8 @@ public interface DeviceRepository extends JpaRepository, Exp @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " + "AND d.deviceProfileId = :deviceProfileId " + "AND d.firmwareId = null " + - "AND (LOWER(d.name) LIKE LOWER(CONCAT('%', :textSearch, '%')) " + - "OR LOWER(d.label) LIKE LOWER(CONCAT('%', :textSearch, '%')))") + "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(d.label, CONCAT('%', :textSearch, '%')) = true)") Page findByTenantIdAndTypeAndFirmwareIdIsNull(@Param("tenantId") UUID tenantId, @Param("deviceProfileId") UUID deviceProfileId, @Param("textSearch") String textSearch, @@ -94,8 +94,8 @@ public interface DeviceRepository extends JpaRepository, Exp @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " + "AND d.deviceProfileId = :deviceProfileId " + "AND d.softwareId = null " + - "AND (LOWER(d.name) LIKE LOWER(CONCAT('%', :textSearch, '%')) " + - "OR LOWER(d.label) LIKE LOWER(CONCAT('%', :textSearch, '%')))") + "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(d.label, CONCAT('%', :textSearch, '%')) = true)") Page findByTenantIdAndTypeAndSoftwareIdIsNull(@Param("tenantId") UUID tenantId, @Param("deviceProfileId") UUID deviceProfileId, @Param("textSearch") String textSearch, @@ -116,8 +116,8 @@ public interface DeviceRepository extends JpaRepository, Exp @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " + "AND d.customerId = :customerId " + "AND d.type = :type " + - "AND (LOWER(d.name) LIKE LOWER(CONCAT('%', :textSearch, '%')) " + - "OR LOWER(d.label) LIKE LOWER(CONCAT('%', :textSearch, '%')))") + "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(d.label, CONCAT('%', :textSearch, '%')) = true)") Page findByTenantIdAndCustomerIdAndType(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, @Param("type") String type, @@ -131,10 +131,10 @@ public interface DeviceRepository extends JpaRepository, Exp "AND ((:deviceType) IS NULL OR d.type = :deviceType) " + "AND (:deviceProfileId IS NULL OR d.deviceProfileId = uuid(:deviceProfileId)) " + "AND ((:filterByActive) IS FALSE OR d.active = :deviceActive) " + - "AND (LOWER(d.name) LIKE LOWER(CONCAT('%', :textSearch, '%')) " + - "OR LOWER(d.label) LIKE LOWER(CONCAT('%', :textSearch, '%')) " + - "OR LOWER(d.type) LIKE LOWER(CONCAT('%', :textSearch, '%')) " + - "OR LOWER(d.customerTitle) LIKE LOWER(CONCAT('%', :textSearch, '%')))") + "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(d.label, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(d.type, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(d.customerTitle, CONCAT('%', :textSearch, '%')) = true)") Page findDeviceInfosByFilter(@Param("tenantId") UUID tenantId, @Param("customerId") String customerId, @Param("edgeId") String edgeId, @@ -145,9 +145,6 @@ public interface DeviceRepository extends JpaRepository, Exp @Param("textSearch") String textSearch, Pageable pageable); - @Query("SELECT DISTINCT d.type FROM DeviceEntity d WHERE d.tenantId = :tenantId") - List findTenantDeviceTypes(@Param("tenantId") UUID tenantId); - DeviceEntity findByTenantIdAndName(UUID tenantId, String name); List findDevicesByTenantIdAndCustomerIdAndIdIn(UUID tenantId, UUID customerId, List deviceIds); @@ -163,8 +160,8 @@ public interface DeviceRepository extends JpaRepository, Exp @Query("SELECT d FROM DeviceEntity d, RelationEntity re WHERE d.tenantId = :tenantId " + "AND d.id = re.toId AND re.toType = 'DEVICE' AND re.relationTypeGroup = 'EDGE' " + "AND re.relationType = 'Contains' AND re.fromId = :edgeId AND re.fromType = 'EDGE' " + - "AND (LOWER(d.name) LIKE LOWER(CONCAT('%', :textSearch, '%')) " + - "OR LOWER(d.label) LIKE LOWER(CONCAT('%', :textSearch, '%')))") + "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(d.label, CONCAT('%', :textSearch, '%')) = true)") Page findByTenantIdAndEdgeId(@Param("tenantId") UUID tenantId, @Param("edgeId") UUID edgeId, @Param("textSearch") String textSearch, @@ -174,8 +171,8 @@ public interface DeviceRepository extends JpaRepository, Exp "AND d.id = re.toId AND re.toType = 'DEVICE' AND re.relationTypeGroup = 'EDGE' " + "AND re.relationType = 'Contains' AND re.fromId = :edgeId AND re.fromType = 'EDGE' " + "AND d.type = :type " + - "AND (LOWER(d.name) LIKE LOWER(CONCAT('%', :textSearch, '%')) " + - "OR LOWER(d.label) LIKE LOWER(CONCAT('%', :textSearch, '%')))") + "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true " + + "OR ilike(d.label, CONCAT('%', :textSearch, '%')) = true)") Page findByTenantIdAndEdgeIdAndType(@Param("tenantId") UUID tenantId, @Param("edgeId") UUID edgeId, @Param("type") String type, diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceCredentialsDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceCredentialsDao.java index f9450461ab..80ea66d5bd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceCredentialsDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceCredentialsDao.java @@ -15,10 +15,12 @@ */ package org.thingsboard.server.dao.sql.device; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; +import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.dao.DaoUtil; @@ -32,6 +34,7 @@ import java.util.UUID; /** * Created by Valerii Sosliuk on 5/6/2017. */ +@Slf4j @Component @SqlDao public class JpaDeviceCredentialsDao extends JpaAbstractDao implements DeviceCredentialsDao { @@ -64,6 +67,13 @@ public class JpaDeviceCredentialsDao extends JpaAbstractDao implement @Autowired private NativeDeviceRepository nativeDeviceRepository; + @Autowired + private DeviceProfileRepository deviceProfileRepository; + @Override protected Class getEntityClass() { return DeviceEntity.class; @@ -98,7 +100,7 @@ public class JpaDeviceDao extends JpaAbstractDao implement return DaoUtil.toPageData( deviceRepository.findByTenantId( tenantId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } } @@ -114,7 +116,7 @@ public class JpaDeviceDao extends JpaAbstractDao implement DaoUtil.getStringId(filter.getDeviceProfileId()), filter.getActive() != null, Boolean.TRUE.equals(filter.getActive()), - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @@ -139,7 +141,7 @@ public class JpaDeviceDao extends JpaAbstractDao implement deviceRepository.findByTenantIdAndCustomerId( tenantId, customerId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @@ -149,7 +151,7 @@ public class JpaDeviceDao extends JpaAbstractDao implement deviceRepository.findByTenantIdAndProfileId( tenantId, profileId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @@ -176,7 +178,7 @@ public class JpaDeviceDao extends JpaAbstractDao implement deviceRepository.findByTenantIdAndType( tenantId, type, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @@ -186,7 +188,7 @@ public class JpaDeviceDao extends JpaAbstractDao implement OtaPackageType type, PageLink pageLink) { Pageable pageable = DaoUtil.toPageable(pageLink); - String searchText = Objects.toString(pageLink.getTextSearch(), ""); + String searchText = pageLink.getTextSearch(); Page page = OtaPackageUtil.getByOtaPackageType( () -> deviceRepository.findByTenantIdAndTypeAndFirmwareIdIsNull(tenantId, deviceProfileId, searchText, pageable), () -> deviceRepository.findByTenantIdAndTypeAndSoftwareIdIsNull(tenantId, deviceProfileId, searchText, pageable), @@ -211,13 +213,13 @@ public class JpaDeviceDao extends JpaAbstractDao implement tenantId, customerId, type, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @Override public ListenableFuture> findTenantDeviceTypesAsync(UUID tenantId) { - return service.submit(() -> convertTenantDeviceTypesToDto(tenantId, deviceRepository.findTenantDeviceTypes(tenantId))); + return service.submit(() -> convertTenantEntityInfosToDto(tenantId, EntityType.DEVICE, deviceProfileRepository.findActiveTenantDeviceProfileNames(tenantId))); } @Override @@ -240,17 +242,6 @@ public class JpaDeviceDao extends JpaAbstractDao implement return deviceRepository.countByTenantId(tenantId.getId()); } - private List convertTenantDeviceTypesToDto(UUID tenantId, List types) { - List list = Collections.emptyList(); - if (types != null && !types.isEmpty()) { - list = new ArrayList<>(); - for (String type : types) { - list.add(new EntitySubtype(TenantId.fromUUID(tenantId), EntityType.DEVICE, type)); - } - } - return list; - } - @Override public PageData findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, PageLink pageLink) { log.debug("Try to find devices by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); @@ -258,7 +249,7 @@ public class JpaDeviceDao extends JpaAbstractDao implement .findByTenantIdAndEdgeId( tenantId, edgeId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @@ -270,7 +261,7 @@ public class JpaDeviceDao extends JpaAbstractDao implement tenantId, edgeId, type, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java index f500600722..9109f223a9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java @@ -16,12 +16,14 @@ package org.thingsboard.server.dao.sql.device; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.DeviceProfileId; @@ -34,7 +36,7 @@ import org.thingsboard.server.dao.model.sql.DeviceProfileEntity; import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; -import java.util.Objects; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -73,7 +75,7 @@ public class JpaDeviceProfileDao extends JpaAbstractDao findAllWithImages(PageLink pageLink) { + return DaoUtil.toPageData(deviceProfileRepository.findAllByImageNotNull(DaoUtil.toPageable(pageLink))); + } + + @Override + public List findTenantDeviceProfileNames(UUID tenantId, boolean activeOnly) { + return activeOnly ? + deviceProfileRepository.findActiveTenantDeviceProfileNames(tenantId) : + deviceProfileRepository.findAllTenantDeviceProfileNames(tenantId); + } + @Override public DeviceProfile findByTenantIdAndExternalId(UUID tenantId, UUID externalId) { return DaoUtil.getData(deviceProfileRepository.findByTenantIdAndExternalId(tenantId, externalId)); @@ -136,6 +150,16 @@ public class JpaDeviceProfileDao extends JpaAbstractDao findByTenantAndImageLink(TenantId tenantId, String imageLink, int limit) { + return deviceProfileRepository.findByTenantAndImageLink(tenantId.getId(), imageLink, PageRequest.of(0, limit)); + } + + @Override + public List findByImageLink(String imageLink, int limit) { + return deviceProfileRepository.findByImageLink(imageLink, PageRequest.of(0, limit)); + } + @Override public EntityType getEntityType() { return EntityType.DEVICE_PROFILE; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeEventRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeEventRepository.java index c4827f1ffe..e6daad26da 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeEventRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeEventRepository.java @@ -30,29 +30,18 @@ public interface EdgeEventRepository extends JpaRepository :startTime) " + + "AND (:startTime IS NULL OR e.createdTime >= :startTime) " + "AND (:endTime IS NULL OR e.createdTime <= :endTime) " + - "AND LOWER(e.edgeEventType) LIKE LOWER(CONCAT('%', :textSearch, '%'))" + "AND (:seqIdStart IS NULL OR e.seqId > :seqIdStart) " + + "AND (:seqIdEnd IS NULL OR e.seqId < :seqIdEnd) " + + "AND (:textSearch IS NULL OR ilike(e.edgeEventType, CONCAT('%', :textSearch, '%')) = true)" ) Page findEdgeEventsByTenantIdAndEdgeId(@Param("tenantId") UUID tenantId, @Param("edgeId") UUID edgeId, @Param("textSearch") String textSearch, @Param("startTime") Long startTime, @Param("endTime") Long endTime, + @Param("seqIdStart") Long seqIdStart, + @Param("seqIdEnd") Long seqIdEnd, Pageable pageable); - - @Query("SELECT e FROM EdgeEventEntity e WHERE " + - "e.tenantId = :tenantId " + - "AND e.edgeId = :edgeId " + - "AND (:startTime IS NULL OR e.createdTime > :startTime) " + - "AND (:endTime IS NULL OR e.createdTime <= :endTime) " + - "AND e.edgeEventAction <> 'TIMESERIES_UPDATED' " + - "AND LOWER(e.edgeEventType) LIKE LOWER(CONCAT('%', :textSearch, '%'))" - ) - Page findEdgeEventsByTenantIdAndEdgeIdWithoutTimeseriesUpdated(@Param("tenantId") UUID tenantId, - @Param("edgeId") UUID edgeId, - @Param("textSearch") String textSearch, - @Param("startTime") Long startTime, - @Param("endTime") Long endTime, - Pageable pageable); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java index 83816fec3a..69795edf11 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeRepository.java @@ -30,7 +30,7 @@ public interface EdgeRepository extends JpaRepository { @Query("SELECT d FROM EdgeEntity d WHERE d.tenantId = :tenantId " + "AND d.customerId = :customerId " + - "AND LOWER(d.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true)") Page findByTenantIdAndCustomerId(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, @Param("textSearch") String textSearch, @@ -43,7 +43,7 @@ public interface EdgeRepository extends JpaRepository { EdgeInfoEntity findEdgeInfoById(@Param("edgeId") UUID edgeId); @Query("SELECT d FROM EdgeEntity d WHERE d.tenantId = :tenantId " + - "AND LOWER(d.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true)") Page findByTenantId(@Param("tenantId") UUID tenantId, @Param("textSearch") String textSearch, Pageable pageable); @@ -52,14 +52,14 @@ public interface EdgeRepository extends JpaRepository { "FROM EdgeEntity d " + "LEFT JOIN CustomerEntity c on c.id = d.customerId " + "WHERE d.tenantId = :tenantId " + - "AND LOWER(d.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true)") Page findEdgeInfosByTenantId(@Param("tenantId") UUID tenantId, @Param("textSearch") String textSearch, Pageable pageable); @Query("SELECT d FROM EdgeEntity d WHERE d.tenantId = :tenantId " + "AND d.type = :type " + - "AND LOWER(d.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true)") Page findByTenantIdAndType(@Param("tenantId") UUID tenantId, @Param("type") String type, @Param("textSearch") String textSearch, @@ -70,7 +70,7 @@ public interface EdgeRepository extends JpaRepository { "LEFT JOIN CustomerEntity c on c.id = d.customerId " + "WHERE d.tenantId = :tenantId " + "AND d.type = :type " + - "AND LOWER(d.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true)") Page findEdgeInfosByTenantIdAndType(@Param("tenantId") UUID tenantId, @Param("type") String type, @Param("textSearch") String textSearch, @@ -79,7 +79,7 @@ public interface EdgeRepository extends JpaRepository { @Query("SELECT d FROM EdgeEntity d WHERE d.tenantId = :tenantId " + "AND d.customerId = :customerId " + "AND d.type = :type " + - "AND LOWER(d.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + "AND (:textSearch IS NULL OR ilike(d.name, CONCAT('%', :textSearch, '%')) = true)") Page findByTenantIdAndCustomerIdAndType(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, @Param("type") String type, @@ -91,10 +91,10 @@ public interface EdgeRepository extends JpaRepository { "LEFT JOIN CustomerEntity c on c.id = a.customerId " + "WHERE a.tenantId = :tenantId " + "AND a.customerId = :customerId " + - "AND LOWER(a.name) LIKE LOWER(CONCAT('%', :searchText, '%'))") + "AND (:textSearch IS NULL OR ilike(a.name, CONCAT('%', :textSearch, '%')) = true)") Page findEdgeInfosByTenantIdAndCustomerId(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, - @Param("searchText") String searchText, + @Param("textSearch") String textSearch, Pageable pageable); @Query("SELECT new org.thingsboard.server.dao.model.sql.EdgeInfoEntity(a, c.title, c.additionalInfo) " + @@ -103,7 +103,7 @@ public interface EdgeRepository extends JpaRepository { "WHERE a.tenantId = :tenantId " + "AND a.customerId = :customerId " + "AND a.type = :type " + - "AND LOWER(a.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + "AND (:textSearch IS NULL OR ilike(a.name, CONCAT('%', :textSearch, '%')) = true)") Page findEdgeInfosByTenantIdAndCustomerIdAndType(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, @Param("type") String type, @@ -113,13 +113,17 @@ public interface EdgeRepository extends JpaRepository { @Query("SELECT ee FROM EdgeEntity ee, RelationEntity re WHERE ee.tenantId = :tenantId " + "AND ee.id = re.fromId AND re.fromType = 'EDGE' AND re.relationTypeGroup = 'EDGE' " + "AND re.relationType = 'Contains' AND re.toId = :entityId AND re.toType = :entityType " + - "AND LOWER(ee.name) LIKE LOWER(CONCAT('%', :searchText, '%'))") + "AND (:textSearch IS NULL OR ilike(ee.name, CONCAT('%', :textSearch, '%')) = true)") Page findByTenantIdAndEntityId(@Param("tenantId") UUID tenantId, @Param("entityId") UUID entityId, @Param("entityType") String entityType, - @Param("searchText") String searchText, + @Param("textSearch") String textSearch, Pageable pageable); + @Query("SELECT ee FROM EdgeEntity ee, TenantEntity te WHERE ee.tenantId = te.id AND te.tenantProfileId = :tenantProfileId ") + Page findByTenantProfileId(@Param("tenantProfileId") UUID tenantProfileId, + Pageable pageable); + @Query("SELECT DISTINCT d.type FROM EdgeEntity d WHERE d.tenantId = :tenantId") List findTenantEdgeTypes(@Param("tenantId") UUID tenantId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaBaseEdgeEventDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaBaseEdgeEventDao.java index bb825f504d..91aa61baf4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaBaseEdgeEventDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaBaseEdgeEventDao.java @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.id.EdgeEventId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.stats.StatsFactory; import org.thingsboard.server.dao.DaoUtil; @@ -43,7 +44,9 @@ import org.thingsboard.server.dao.util.SqlDao; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; +import java.util.ArrayList; import java.util.Comparator; +import java.util.List; import java.util.Objects; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -118,7 +121,7 @@ public class JpaBaseEdgeEventDao extends JpaAbstractDao(params, hashcodeFunction, 1, statsFactory); - queue.init(logExecutor, v -> edgeEventInsertRepository.save(v), + queue.init(logExecutor, edgeEventInsertRepository::save, Comparator.comparing(EdgeEventEntity::getTs) ); } @@ -171,29 +174,23 @@ public class JpaBaseEdgeEventDao extends JpaAbstractDao findEdgeEvents(UUID tenantId, EdgeId edgeId, TimePageLink pageLink, boolean withTsUpdate) { - if (withTsUpdate) { - return DaoUtil.toPageData( - edgeEventRepository - .findEdgeEventsByTenantIdAndEdgeId( - tenantId, - edgeId.getId(), - Objects.toString(pageLink.getTextSearch(), ""), - pageLink.getStartTime(), - pageLink.getEndTime(), - DaoUtil.toPageable(pageLink))); - } else { - return DaoUtil.toPageData( - edgeEventRepository - .findEdgeEventsByTenantIdAndEdgeIdWithoutTimeseriesUpdated( - tenantId, - edgeId.getId(), - Objects.toString(pageLink.getTextSearch(), ""), - pageLink.getStartTime(), - pageLink.getEndTime(), - DaoUtil.toPageable(pageLink))); - + public PageData findEdgeEvents(UUID tenantId, EdgeId edgeId, Long seqIdStart, Long seqIdEnd, TimePageLink pageLink) { + List sortOrders = new ArrayList<>(); + if (pageLink.getSortOrder() != null) { + sortOrders.add(pageLink.getSortOrder()); } + sortOrders.add(new SortOrder("seqId")); + return DaoUtil.toPageData( + edgeEventRepository + .findEdgeEventsByTenantIdAndEdgeId( + tenantId, + edgeId.getId(), + pageLink.getTextSearch(), + pageLink.getStartTime(), + pageLink.getEndTime(), + seqIdStart, + seqIdEnd, + DaoUtil.toPageable(pageLink, sortOrders))); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java index c40ca2ca6d..6790ed0224 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java @@ -34,13 +34,13 @@ import org.thingsboard.server.dao.model.sql.EdgeInfoEntity; import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.UUID; +import static org.thingsboard.server.dao.DaoUtil.convertTenantEntityTypesToDto; + @Component @Slf4j @SqlDao @@ -69,7 +69,7 @@ public class JpaEdgeDao extends JpaAbstractDao implements Edge return DaoUtil.toPageData( edgeRepository.findByTenantId( tenantId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @@ -84,7 +84,7 @@ public class JpaEdgeDao extends JpaAbstractDao implements Edge edgeRepository.findByTenantIdAndCustomerId( tenantId, customerId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @@ -106,7 +106,7 @@ public class JpaEdgeDao extends JpaAbstractDao implements Edge edgeRepository.findByTenantIdAndType( tenantId, type, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @@ -117,7 +117,7 @@ public class JpaEdgeDao extends JpaAbstractDao implements Edge tenantId, customerId, type, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @@ -127,7 +127,7 @@ public class JpaEdgeDao extends JpaAbstractDao implements Edge edgeRepository.findEdgeInfosByTenantIdAndCustomerId( tenantId, customerId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink, EdgeInfoEntity.edgeInfoColumnMap))); } @@ -138,13 +138,13 @@ public class JpaEdgeDao extends JpaAbstractDao implements Edge tenantId, customerId, type, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink, EdgeInfoEntity.edgeInfoColumnMap))); } @Override public ListenableFuture> findTenantEdgeTypesAsync(UUID tenantId) { - return service.submit(() -> convertTenantEdgeTypesToDto(tenantId, edgeRepository.findTenantEdgeTypes(tenantId))); + return service.submit(() -> convertTenantEntityTypesToDto(tenantId, EntityType.EDGE, edgeRepository.findTenantEdgeTypes(tenantId))); } @Override @@ -153,7 +153,7 @@ public class JpaEdgeDao extends JpaAbstractDao implements Edge edgeRepository.findEdgeInfosByTenantIdAndType( tenantId, type, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink, EdgeInfoEntity.edgeInfoColumnMap))); } @@ -162,7 +162,7 @@ public class JpaEdgeDao extends JpaAbstractDao implements Edge return DaoUtil.toPageData( edgeRepository.findEdgeInfosByTenantId( tenantId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink, EdgeInfoEntity.edgeInfoColumnMap))); } @@ -180,19 +180,17 @@ public class JpaEdgeDao extends JpaAbstractDao implements Edge tenantId, entityId, entityType.name(), - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } - private List convertTenantEdgeTypesToDto(UUID tenantId, List types) { - List list = Collections.emptyList(); - if (types != null && !types.isEmpty()) { - list = new ArrayList<>(); - for (String type : types) { - list.add(new EntitySubtype(TenantId.fromUUID(tenantId), EntityType.EDGE, type)); - } - } - return list; + @Override + public PageData findEdgesByTenantProfileId(UUID tenantProfileId, PageLink pageLink) { + log.debug("Try to find edges by tenantProfileId [{}], pageLink [{}]", tenantProfileId, pageLink); + return DaoUtil.toPageData( + edgeRepository.findByTenantProfileId( + tenantProfileId, + DaoUtil.toPageable(pageLink))); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java index 1478e23a54..f039082ec9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java @@ -39,7 +39,7 @@ public interface EntityViewRepository extends JpaRepository findByTenantId(@Param("tenantId") UUID tenantId, @Param("textSearch") String textSearch, Pageable pageable); @@ -48,14 +48,14 @@ public interface EntityViewRepository extends JpaRepository findEntityViewInfosByTenantId(@Param("tenantId") UUID tenantId, @Param("textSearch") String textSearch, Pageable pageable); @Query("SELECT e FROM EntityViewEntity e WHERE e.tenantId = :tenantId " + "AND e.type = :type " + - "AND LOWER(e.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + "AND (:textSearch IS NULL OR ilike(e.name, CONCAT('%', :textSearch, '%')) = true)") Page findByTenantIdAndType(@Param("tenantId") UUID tenantId, @Param("type") String type, @Param("textSearch") String textSearch, @@ -66,7 +66,7 @@ public interface EntityViewRepository extends JpaRepository findEntityViewInfosByTenantIdAndType(@Param("tenantId") UUID tenantId, @Param("type") String type, @Param("textSearch") String textSearch, @@ -74,7 +74,7 @@ public interface EntityViewRepository extends JpaRepository findByTenantIdAndCustomerId(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, @Param("searchText") String searchText, @@ -85,7 +85,7 @@ public interface EntityViewRepository extends JpaRepository findEntityViewInfosByTenantIdAndCustomerId(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, @Param("searchText") String searchText, @@ -94,7 +94,7 @@ public interface EntityViewRepository extends JpaRepository findByTenantIdAndCustomerIdAndType(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, @Param("type") String type, @@ -107,7 +107,7 @@ public interface EntityViewRepository extends JpaRepository findEntityViewInfosByTenantIdAndCustomerIdAndType(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, @Param("type") String type, @@ -118,13 +118,15 @@ public interface EntityViewRepository extends JpaRepository findAllByTenantIdAndEntityId(UUID tenantId, UUID entityId); + boolean existsByTenantIdAndEntityId(UUID tenantId, UUID entityId); + @Query("SELECT DISTINCT ev.type FROM EntityViewEntity ev WHERE ev.tenantId = :tenantId") List findTenantEntityViewTypes(@Param("tenantId") UUID tenantId); @Query("SELECT ev FROM EntityViewEntity ev, RelationEntity re WHERE ev.tenantId = :tenantId " + "AND ev.id = re.toId AND re.toType = 'ENTITY_VIEW' AND re.relationTypeGroup = 'EDGE' " + "AND re.relationType = 'Contains' AND re.fromId = :edgeId AND re.fromType = 'EDGE' " + - "AND LOWER(ev.name) LIKE LOWER(CONCAT('%', :searchText, '%'))") + "AND (:searchText IS NULL OR ilike(ev.name, CONCAT('%', :searchText, '%')) = true)") Page findByTenantIdAndEdgeId(@Param("tenantId") UUID tenantId, @Param("edgeId") UUID edgeId, @Param("searchText") String searchText, @@ -134,7 +136,7 @@ public interface EntityViewRepository extends JpaRepository findByTenantIdAndEdgeIdAndType(@Param("tenantId") UUID tenantId, @Param("edgeId") UUID edgeId, @Param("type") String type, diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java index f3f82d34da..11b8ccfaea 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java @@ -35,13 +35,13 @@ import org.thingsboard.server.dao.model.sql.EntityViewInfoEntity; import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.UUID; +import static org.thingsboard.server.dao.DaoUtil.convertTenantEntityTypesToDto; + /** * Created by Victor Basanets on 8/31/2017. */ @@ -74,7 +74,7 @@ public class JpaEntityViewDao extends JpaAbstractDao> findTenantEntityViewTypesAsync(UUID tenantId) { - return service.submit(() -> convertTenantEntityViewTypesToDto(tenantId, entityViewRepository.findTenantEntityViewTypes(tenantId))); + public boolean existsByTenantIdAndEntityId(UUID tenantId, UUID entityId) { + return entityViewRepository.existsByTenantIdAndEntityId(tenantId, entityId); } - private List convertTenantEntityViewTypesToDto(UUID tenantId, List types) { - List list = Collections.emptyList(); - if (types != null && !types.isEmpty()) { - list = new ArrayList<>(); - for (String type : types) { - list.add(new EntitySubtype(TenantId.fromUUID(tenantId), EntityType.ENTITY_VIEW, type)); - } - } - return list; + @Override + public ListenableFuture> findTenantEntityViewTypesAsync(UUID tenantId) { + return service.submit(() -> convertTenantEntityTypesToDto(tenantId, EntityType.ENTITY_VIEW, entityViewRepository.findTenantEntityViewTypes(tenantId))); } @Override @@ -188,7 +182,7 @@ public class JpaEntityViewDao extends JpaAbstractDao findEventByFilter(UUID tenantId, UUID entityId, LifeCycleEventFilter eventFilter, TimePageLink pageLink) { boolean statusFilterEnabled = !StringUtils.isEmpty(eventFilter.getStatus()); - boolean statusFilter = statusFilterEnabled && eventFilter.getStatus().equalsIgnoreCase("Success"); + boolean statusFilter = statusFilterEnabled && eventFilter.getStatus().equalsIgnoreCase(TbNodeConnectionType.SUCCESS); return DaoUtil.toPageData( lcEventRepository.findEvents( tenantId, @@ -359,7 +360,7 @@ public class JpaBaseEventDao implements EventDao { private void removeEventsByFilter(UUID tenantId, UUID entityId, LifeCycleEventFilter eventFilter, Long startTime, Long endTime) { boolean statusFilterEnabled = !StringUtils.isEmpty(eventFilter.getStatus()); - boolean statusFilter = statusFilterEnabled && eventFilter.getStatus().equalsIgnoreCase("Success"); + boolean statusFilter = statusFilterEnabled && eventFilter.getStatus().equalsIgnoreCase(TbNodeConnectionType.SUCCESS); lcEventRepository.removeEvents( tenantId, entityId, diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java index 5fa156725d..57a8306fc1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationDao.java @@ -67,13 +67,13 @@ public class JpaNotificationDao extends JpaAbstractDao findUnreadByRecipientIdAndPageLink(TenantId tenantId, UserId recipientId, PageLink pageLink) { return DaoUtil.toPageData(notificationRepository.findByRecipientIdAndStatusNot(recipientId.getId(), NotificationStatus.READ, - Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))); + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @Override public PageData findByRecipientIdAndPageLink(TenantId tenantId, UserId recipientId, PageLink pageLink) { return DaoUtil.toPageData(notificationRepository.findByRecipientId(recipientId.getId(), - Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))); + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @Override @@ -81,6 +81,9 @@ public class JpaNotificationDao extends JpaAbstractDao getEntityClass() { return NotificationEntity.class; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java index 53b8318d3f..3464133fdd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java @@ -61,7 +61,7 @@ public class JpaNotificationRequestDao extends JpaAbstractDao findInfosByTenantIdAndOriginatorTypeAndPageLink(TenantId tenantId, EntityType originatorType, PageLink pageLink) { return DaoUtil.pageToPageData(notificationRequestRepository.findInfosByTenantIdAndOriginatorEntityTypeAndSearchText(tenantId.getId(), - originatorType, Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink, Map.of( + originatorType, pageLink.getTextSearch(), DaoUtil.toPageable(pageLink, Map.of( "templateName", "t.name" )))) .mapData(NotificationRequestInfoEntity::toData); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java index aa3258287e..a5eb7ce527 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java @@ -25,7 +25,7 @@ import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.DaoUtil; @@ -49,13 +49,13 @@ public class JpaNotificationRuleDao extends JpaAbstractDao findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { return DaoUtil.toPageData(notificationRuleRepository.findByTenantIdAndSearchText(tenantId.getId(), - Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))); + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @Override public PageData findInfosByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { return DaoUtil.pageToPageData(notificationRuleRepository.findInfosByTenantIdAndSearchText(tenantId.getId(), - Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink, Map.of( + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink, Map.of( "templateName", "t.name" )))) .mapData(NotificationRuleInfoEntity::toData); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java index d976802fd2..bfa1d1b979 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java @@ -48,13 +48,13 @@ public class JpaNotificationTargetDao extends JpaAbstractDao findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { return DaoUtil.toPageData(notificationTargetRepository.findByTenantIdAndSearchText(tenantId.getId(), - Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))); + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @Override public PageData findByTenantIdAndSupportedNotificationTypeAndPageLink(TenantId tenantId, NotificationType notificationType, PageLink pageLink) { return DaoUtil.toPageData(notificationTargetRepository.findByTenantIdAndSearchTextAndUsersFilterTypeIfPresent(tenantId.getId(), - Strings.nullToEmpty(pageLink.getTextSearch()), + pageLink.getTextSearch(), Arrays.stream(UsersFilterType.values()) .filter(type -> notificationType != NotificationType.GENERAL || !type.isForRules()) .map(Enum::name).collect(Collectors.toList()), @@ -66,6 +66,12 @@ public class JpaNotificationTargetDao extends JpaAbstractDao findByTenantIdAndUsersFilterType(TenantId tenantId, UsersFilterType filterType) { + return DaoUtil.convertDataList(notificationTargetRepository.findByTenantIdAndSearchTextAndUsersFilterTypeIfPresent(tenantId.getId(), null, + List.of(filterType.name()), DaoUtil.toPageable(new PageLink(Integer.MAX_VALUE))).getContent()); + } + @Override public void removeByTenantId(TenantId tenantId) { notificationTargetRepository.deleteByTenantId(tenantId.getId()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java index 8a7cecb3d1..6e3e353b3d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java @@ -50,7 +50,7 @@ public class JpaNotificationTemplateDao extends JpaAbstractDao findByTenantIdAndNotificationTypesAndPageLink(TenantId tenantId, List notificationTypes, PageLink pageLink) { return DaoUtil.toPageData(notificationTemplateRepository.findByTenantIdAndNotificationTypesAndSearchText(tenantId.getId(), - notificationTypes, Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink))); + notificationTypes, pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java index c08a3c290e..6b986716bb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRepository.java @@ -32,16 +32,16 @@ import java.util.UUID; public interface NotificationRepository extends JpaRepository { @Query("SELECT n FROM NotificationEntity n WHERE n.recipientId = :recipientId AND n.status <> :status " + - "AND (:searchText = '' OR lower(n.subject) LIKE lower(concat('%', :searchText, '%')) " + - "OR lower(n.text) LIKE lower(concat('%', :searchText, '%')))") + "AND (:searchText is NULL OR ilike(n.subject, concat('%', :searchText, '%')) = true " + + "OR ilike(n.text, concat('%', :searchText, '%')) = true)") Page findByRecipientIdAndStatusNot(@Param("recipientId") UUID recipientId, @Param("status") NotificationStatus status, @Param("searchText") String searchText, Pageable pageable); @Query("SELECT n FROM NotificationEntity n WHERE n.recipientId = :recipientId " + - "AND (:searchText = '' OR lower(n.subject) LIKE lower(concat('%', :searchText, '%')) " + - "OR lower(n.text) LIKE lower(concat('%', :searchText, '%')))") + "AND (:searchText is NULL OR ilike(n.subject, concat('%', :searchText, '%')) = true " + + "OR ilike(n.text, concat('%', :searchText, '%')) = true)") Page findByRecipientId(@Param("recipientId") UUID recipientId, @Param("searchText") String searchText, Pageable pageable); @@ -59,7 +59,19 @@ public interface NotificationRepository extends JpaRepository findByRequestId(UUID requestId, Pageable pageable); @Transactional - int deleteByIdAndRecipientId(UUID id, UUID recipientId); + @Modifying + @Query("DELETE FROM NotificationEntity n WHERE n.id = :id AND n.recipientId = :recipientId") + int deleteByIdAndRecipientId(@Param("id") UUID id, @Param("recipientId") UUID recipientId); + + @Transactional + @Modifying + @Query("DELETE FROM NotificationEntity n WHERE n.requestId = :requestId") + void deleteByRequestId(@Param("requestId") UUID requestId); + + @Transactional + @Modifying + @Query("DELETE FROM NotificationEntity n WHERE n.recipientId = :recipientId") + void deleteByRecipientId(@Param("recipientId") UUID recipientId); @Modifying @Transactional diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java index 126295616f..06e16e0882 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRequestRepository.java @@ -41,7 +41,7 @@ public interface NotificationRequestRepository extends JpaRepository findByTenantIdAndOriginatorEntityType(UUID tenantId, EntityType originatorType, Pageable pageable); @Query(REQUEST_INFO_QUERY + " WHERE r.tenantId = :tenantId AND r.originatorEntityType = :originatorType " + - "AND (:searchText = '' OR t.name IS NOT NULL AND lower(t.name) LIKE lower(concat('%', :searchText, '%')))") + "AND (:searchText is NULL OR (t.name IS NOT NULL AND ilike(t.name, concat('%', :searchText, '%')) = true))") Page findInfosByTenantIdAndOriginatorEntityTypeAndSearchText(@Param("tenantId") UUID tenantId, @Param("originatorType") EntityType originatorType, @Param("searchText") String searchText, @@ -70,9 +70,13 @@ public interface NotificationRequestRepository extends JpaRepository findByTenantIdAndSearchText(@Param("tenantId") UUID tenantId, @Param("searchText") String searchText, Pageable pageable); @@ -52,13 +53,15 @@ public interface NotificationRuleRepository extends JpaRepository findInfosByTenantIdAndSearchText(@Param("tenantId") UUID tenantId, @Param("searchText") String searchText, Pageable pageable); @Transactional - void deleteByTenantId(UUID tenantId); + @Modifying + @Query("DELETE FROM NotificationRuleEntity r WHERE r.tenantId = :tenantId") + void deleteByTenantId(@Param("tenantId") UUID tenantId); NotificationRuleEntity findByTenantIdAndName(UUID tenantId, String name); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java index 278fea696e..432a97493c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.sql.notification; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @@ -32,13 +33,13 @@ import java.util.UUID; public interface NotificationTargetRepository extends JpaRepository, ExportableEntityRepository { @Query("SELECT t FROM NotificationTargetEntity t WHERE t.tenantId = :tenantId " + - "AND (:searchText = '' OR lower(t.name) LIKE lower(concat('%', :searchText, '%')))") + "AND (:searchText is NULL OR ilike(t.name, concat('%', :searchText, '%')) = true)") Page findByTenantIdAndSearchText(@Param("tenantId") UUID tenantId, @Param("searchText") String searchText, Pageable pageable); @Query(value = "SELECT * FROM notification_target t WHERE t.tenant_id = :tenantId " + - "AND (:searchText = '' OR lower(t.name) LIKE lower(concat('%', :searchText, '%'))) " + + "AND (:searchText IS NULL OR t.name ILIKE concat('%', :searchText, '%')) " + "AND (cast(t.configuration as json) ->> 'type' <> 'PLATFORM_USERS' OR " + "cast(t.configuration as json) -> 'usersFilter' ->> 'type' IN :usersFilterTypes)", nativeQuery = true) Page findByTenantIdAndSearchTextAndUsersFilterTypeIfPresent(@Param("tenantId") UUID tenantId, @@ -49,7 +50,9 @@ public interface NotificationTargetRepository extends JpaRepository findByTenantIdAndIdIn(UUID tenantId, List ids); @Transactional - void deleteByTenantId(UUID tenantId); + @Modifying + @Query("DELETE FROM NotificationTargetEntity t WHERE t.tenantId = :tenantId") + void deleteByTenantId(@Param("tenantId") UUID tenantId); long countByTenantId(UUID tenantId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java index 73a25b98c3..6e008a7fa7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.sql.notification; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @@ -34,15 +35,17 @@ public interface NotificationTemplateRepository extends JpaRepository findByTenantIdAndNotificationTypesAndSearchText(@Param("tenantId") UUID tenantId, @Param("notificationTypes") List notificationTypes, @Param("searchText") String searchText, Pageable pageable); @Transactional - void deleteByTenantId(UUID tenantId); + @Modifying + @Query("DELETE FROM NotificationTemplateEntity t WHERE t.tenantId = :tenantId") + void deleteByTenantId(@Param("tenantId") UUID tenantId); NotificationTemplateEntity findByTenantIdAndName(UUID tenantId, String name); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageInfoDao.java index f50dd6ed0f..8a65bee2e1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageInfoDao.java @@ -73,7 +73,7 @@ public class JpaOtaPackageInfoDao extends JpaAbstractDao { @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.tag, f.url, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, CASE WHEN (f.data IS NOT NULL OR f.url IS NOT NULL) THEN true ELSE false END) FROM OtaPackageEntity f WHERE " + "f.tenantId = :tenantId " + - "AND LOWER(f.title) LIKE LOWER(CONCAT('%', :searchText, '%'))") + "AND (:searchText IS NULL OR ilike(f.title, CONCAT('%', :searchText, '%')) = true)") Page findAllByTenantId(@Param("tenantId") UUID tenantId, @Param("searchText") String searchText, Pageable pageable); @@ -38,7 +38,7 @@ public interface OtaPackageInfoRepository extends JpaRepository findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData(@Param("tenantId") UUID tenantId, @Param("deviceProfileId") UUID deviceProfileId, @Param("type") OtaPackageType type, diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java index 04e978c7d4..a707b9c196 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java @@ -406,7 +406,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { .map(mapping -> { String paramName = mapping + "_lowerSearchText"; ctx.addStringParameter(paramName, lowerSearchText); - return String.format("LOWER(cast(%s as varchar)) LIKE concat('%%', :%s, '%%')", mapping, paramName); + return String.format("cast(%s as varchar) ILIKE concat('%%', :%s, '%%')", mapping, paramName); } ).collect(Collectors.toList()); return String.format("%s", String.join(" or ", searchPredicates)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java index 2135e98eed..fd9ea53b98 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java @@ -17,7 +17,6 @@ package org.thingsboard.server.dao.sql.query; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Value; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Repository; @@ -69,7 +68,8 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { private static final Map entityTableMap = new HashMap<>(); private static final Map entityNameColumns = new HashMap<>(); private static final String SELECT_PHONE = " CASE WHEN entity.entity_type = 'TENANT' THEN (select phone from tenant where id = entity_id)" + - " WHEN entity.entity_type = 'CUSTOMER' THEN (select phone from customer where id = entity_id) END as phone"; + " WHEN entity.entity_type = 'CUSTOMER' THEN (select phone from customer where id = entity_id)" + + " WHEN entity.entity_type = 'USER' THEN (select phone from tb_user where id = entity_id) END as phone"; private static final String SELECT_ZIP = " CASE WHEN entity.entity_type = 'TENANT' THEN (select zip from tenant where id = entity_id)" + " WHEN entity.entity_type = 'CUSTOMER' THEN (select zip from customer where id = entity_id) END as zip"; private static final String SELECT_ADDRESS_2 = " CASE WHEN entity.entity_type = 'TENANT'" + @@ -800,8 +800,8 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { private String buildTextSearchQuery(QueryContext ctx, List selectionMapping, String searchText) { if (!StringUtils.isEmpty(searchText) && !selectionMapping.isEmpty()) { - String lowerSearchText = "%" + searchText.toLowerCase() + "%"; - ctx.addStringParameter("lowerSearchTextParam", lowerSearchText); + String sqlSearchText = "%" + searchText + "%"; + ctx.addStringParameter("lowerSearchTextParam", sqlSearchText); List searchAliases = selectionMapping.stream().filter(EntityKeyMapping::isSearchable).map(EntityKeyMapping::getValueAlias).collect(Collectors.toList()); String searchAliasesExpression; if (searchAliases.size() > 1) { @@ -809,7 +809,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { } else { searchAliasesExpression = searchAliases.get(0); } - return String.format(" WHERE LOWER(%s) LIKE :%s", searchAliasesExpression, "lowerSearchTextParam"); + return String.format(" WHERE %s ILIKE :%s", searchAliasesExpression, "lowerSearchTextParam"); } else { return ""; } @@ -829,10 +829,10 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { ctx.addStringParameter("entity_filter_name_filter", filter.getEntityNameFilter()); String nameColumn = getNameColumn(filter.getEntityType()); if (filter.getEntityNameFilter().startsWith("%") || filter.getEntityNameFilter().endsWith("%")) { - return String.format("lower(e.%s) like lower(:entity_filter_name_filter)", nameColumn); + return String.format("e.%s ilike :entity_filter_name_filter", nameColumn); } - return String.format("lower(e.%s) like lower(concat(:entity_filter_name_filter, '%%'))", nameColumn); + return String.format("e.%s ilike concat(:entity_filter_name_filter, '%%')", nameColumn); } private String typeQuery(QueryContext ctx, EntityFilter filter) { @@ -868,9 +868,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { if (!StringUtils.isEmpty(name)) { ctx.addStringParameter("entity_filter_type_query_name", name); if (name.startsWith("%") || name.endsWith("%")) { - return typesFilter + " and lower(e." + nameColumn + ") like lower(:entity_filter_type_query_name)"; + return typesFilter + " and e." + nameColumn + " ilike :entity_filter_type_query_name"; } - return typesFilter + " and lower(e." + nameColumn + ") like lower(concat(:entity_filter_type_query_name, '%%'))"; + return typesFilter + " and e." + nameColumn + " ilike concat(:entity_filter_type_query_name, '%%')"; } else { return typesFilter; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/queue/JpaQueueDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/queue/JpaQueueDao.java index ed51f6cab0..7cd3314d5f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/queue/JpaQueueDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/queue/JpaQueueDao.java @@ -84,6 +84,6 @@ public class JpaQueueDao extends JpaAbstractDao implements Q @Override public PageData findQueuesByTenantId(TenantId tenantId, PageLink pageLink) { return DaoUtil.toPageData(queueRepository - .findByTenantId(tenantId.getId(), Objects.toString(pageLink.getTextSearch(), ""), DaoUtil.toPageable(pageLink))); + .findByTenantId(tenantId.getId(), pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } } \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/queue/QueueRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/queue/QueueRepository.java index 8b11bbcef5..11805a3109 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/queue/QueueRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/queue/QueueRepository.java @@ -33,7 +33,7 @@ public interface QueueRepository extends JpaRepository { List findByTenantId(UUID tenantId); @Query("SELECT q FROM QueueEntity q WHERE q.tenantId = :tenantId " + - "AND LOWER(q.name) LIKE LOWER(CONCAT(:textSearch, '%'))") + "AND (:textSearch IS NULL OR ilike(q.name, CONCAT(:textSearch, '%')) = true)") Page findByTenantId(@Param("tenantId") UUID tenantId, @Param("textSearch") String textSearch, Pageable pageable); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java index c1b17f160e..31125a0791 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java @@ -37,6 +37,7 @@ import org.thingsboard.server.dao.util.SqlDao; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -205,6 +206,15 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple } } + @Override + public void deleteOutboundRelations(TenantId tenantId, EntityId entity, RelationTypeGroup relationTypeGroup) { + try { + relationRepository.deleteByFromIdAndFromTypeAndRelationTypeGroupIn(entity.getId(), entity.getEntityType().name(), Collections.singletonList(relationTypeGroup.name())); + } catch (ConcurrencyFailureException e) { + log.debug("Concurrency exception while deleting relations [{}]", entity, e); + } + } + @Override public void deleteInboundRelations(TenantId tenantId, EntityId entity) { try { @@ -214,6 +224,15 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple } } + @Override + public void deleteInboundRelations(TenantId tenantId, EntityId entity, RelationTypeGroup relationTypeGroup) { + try { + relationRepository.deleteByToIdAndToTypeAndRelationTypeGroupIn(entity.getId(), entity.getEntityType().name(), Collections.singletonList(relationTypeGroup.name())); + } catch (ConcurrencyFailureException e) { + log.debug("Concurrency exception while deleting relations [{}]", entity, e); + } + } + @Override public ListenableFuture deleteOutboundRelationsAsync(TenantId tenantId, EntityId entity) { return service.submit( diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java index a3d6d8570d..10c8c826eb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/relation/RelationRepository.java @@ -82,4 +82,9 @@ public interface RelationRepository @Query("DELETE FROM RelationEntity r where r.toId = :toId and r.toType = :toType and r.relationTypeGroup in :relationTypeGroups") void deleteByToIdAndToTypeAndRelationTypeGroupIn(@Param("toId") UUID toId, @Param("toType") String toType, @Param("relationTypeGroups") List relationTypeGroups); + @Transactional + @Modifying + @Query("DELETE FROM RelationEntity r where r.fromId = :fromId and r.fromType = :fromType and r.relationTypeGroup in :relationTypeGroups") + void deleteByFromIdAndFromTypeAndRelationTypeGroupIn(@Param("fromId") UUID fromId, @Param("fromType") String fromType, @Param("relationTypeGroups") List relationTypeGroups); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java index 9b6ef62501..2326d2ffe4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java @@ -21,6 +21,7 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -31,7 +32,6 @@ import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; import java.util.List; -import java.util.Objects; import java.util.UUID; @Slf4j @@ -56,8 +56,7 @@ public class JpaTbResourceDao extends JpaAbstractDao findByTenantId(UUID tenantId, PageLink pageLink) { + return findAllByTenantId(TenantId.fromUUID(tenantId), pageLink); + } + + @Override + public PageData findIdsByTenantId(UUID tenantId, PageLink pageLink) { + return DaoUtil.pageToPageData(resourceRepository.findIdsByTenantId(tenantId, DaoUtil.toPageable(pageLink)) + .map(TbResourceId::new)); + } + + @Override + public TbResourceId getExternalIdByInternal(TbResourceId internalId) { + return DaoUtil.toEntityId(resourceRepository.getExternalIdByInternal(internalId.getId()), TbResourceId::new); + } + @Override public EntityType getEntityType() { return EntityType.TB_RESOURCE; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java index 99b30f0971..a6b0fdba75 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java @@ -25,14 +25,19 @@ import org.thingsboard.server.common.data.TbResourceInfoFilter; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.util.CollectionsUtil; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.TbResourceInfoEntity; import org.thingsboard.server.dao.resource.TbResourceInfoDao; import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; +import java.util.EnumSet; +import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; @Slf4j @Component @@ -54,25 +59,64 @@ public class JpaTbResourceInfoDao extends JpaAbstractDao findAllTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink) { - ResourceType resourceType = filter.getResourceType(); + Set resourceTypes = filter.getResourceTypes(); + if (CollectionsUtil.isEmpty(resourceTypes)) { + resourceTypes = EnumSet.allOf(ResourceType.class); + } return DaoUtil.toPageData(resourceInfoRepository .findAllTenantResourcesByTenantId( - filter.getTenantId().getId(), - TenantId.NULL_UUID, - resourceType == null ? null : resourceType.name(), + filter.getTenantId().getId(), TenantId.NULL_UUID, + resourceTypes.stream().map(Enum::name).collect(Collectors.toList()), Objects.toString(pageLink.getTextSearch(), ""), DaoUtil.toPageable(pageLink))); } @Override public PageData findTenantResourcesByTenantId(TbResourceInfoFilter filter, PageLink pageLink) { - ResourceType resourceType = filter.getResourceType(); + Set resourceTypes = filter.getResourceTypes(); + if (CollectionsUtil.isEmpty(resourceTypes)) { + resourceTypes = EnumSet.allOf(ResourceType.class); + } return DaoUtil.toPageData(resourceInfoRepository .findTenantResourcesByTenantId( filter.getTenantId().getId(), - resourceType == null ? null : resourceType.name(), - Objects.toString(pageLink.getTextSearch(), ""), + resourceTypes.stream().map(Enum::name).collect(Collectors.toList()), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } + @Override + public TbResourceInfo findByTenantIdAndKey(TenantId tenantId, ResourceType resourceType, String resourceKey) { + return DaoUtil.getData(resourceInfoRepository.findByTenantIdAndResourceTypeAndResourceKey(tenantId.getId(), resourceType.name(), resourceKey)); + } + + @Override + public boolean existsByTenantIdAndResourceTypeAndResourceKey(TenantId tenantId, ResourceType resourceType, String resourceKey) { + return resourceInfoRepository.existsByTenantIdAndResourceTypeAndResourceKey(tenantId.getId(), resourceType.name(), resourceKey); + } + + @Override + public Set findKeysByTenantIdAndResourceTypeAndResourceKeyPrefix(TenantId tenantId, ResourceType resourceType, String prefix) { + return resourceInfoRepository.findKeysByTenantIdAndResourceTypeAndResourceKeyStartingWith(tenantId.getId(), resourceType.name(), prefix); + } + + @Override + public List findByTenantIdAndEtagAndKeyStartingWith(TenantId tenantId, String etag, String query) { + return DaoUtil.convertDataList(resourceInfoRepository.findByTenantIdAndEtagAndResourceKeyStartingWith(tenantId.getId(), etag, query)); + } + + @Override + public TbResourceInfo findSystemOrTenantImageByEtag(TenantId tenantId, ResourceType resourceType, String etag) { + return DaoUtil.getData(resourceInfoRepository.findSystemOrTenantImageByEtag(tenantId.getId(), resourceType.name(), etag)); + } + + @Override + public boolean existsByPublicResourceKey(ResourceType resourceType, String publicResourceKey) { + return resourceInfoRepository.existsByResourceTypeAndPublicResourceKey(resourceType.name(), publicResourceKey); + } + + @Override + public TbResourceInfo findPublicResourceByKey(ResourceType resourceType, String publicResourceKey) { + return DaoUtil.getData(resourceInfoRepository.findByResourceTypeAndPublicResourceKeyAndIsPublicTrue(resourceType.name(), publicResourceKey)); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java index 720b78a5f5..6af6600f43 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java @@ -22,33 +22,57 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.thingsboard.server.dao.model.sql.TbResourceInfoEntity; +import java.util.List; +import java.util.Set; import java.util.UUID; public interface TbResourceInfoRepository extends JpaRepository { @Query("SELECT tr FROM TbResourceInfoEntity tr WHERE " + - "LOWER(tr.title) LIKE LOWER(CONCAT('%', :searchText, '%'))" + + "(:searchText IS NULL OR ilike(tr.title, CONCAT('%', :searchText, '%')) = true) " + "AND (tr.tenantId = :tenantId " + - "OR (tr.tenantId = :systemAdminId " + + "OR (tr.tenantId = :systemTenantId " + "AND NOT EXISTS " + "(SELECT sr FROM TbResourceEntity sr " + "WHERE sr.tenantId = :tenantId " + "AND tr.resourceType = sr.resourceType " + "AND tr.resourceKey = sr.resourceKey)))" + - "AND (:resourceType IS NULL OR tr.resourceType = :resourceType)") + "AND tr.resourceType IN :resourceTypes") Page findAllTenantResourcesByTenantId(@Param("tenantId") UUID tenantId, - @Param("systemAdminId") UUID sysadminId, - @Param("resourceType") String resourceType, + @Param("systemTenantId") UUID systemTenantId, + @Param("resourceTypes") List resourceTypes, @Param("searchText") String searchText, Pageable pageable); @Query("SELECT ri FROM TbResourceInfoEntity ri WHERE " + "ri.tenantId = :tenantId " + - "AND (:resourceType IS NULL OR ri.resourceType = :resourceType)" + - "AND LOWER(ri.title) LIKE LOWER(CONCAT('%', :searchText, '%'))") + "AND ri.resourceType IN :resourceTypes " + + "AND (:searchText IS NULL OR ilike(ri.title, CONCAT('%', :searchText, '%')) = true)") Page findTenantResourcesByTenantId(@Param("tenantId") UUID tenantId, - @Param("resourceType") String resourceType, + @Param("resourceTypes") List resourceTypes, @Param("searchText") String searchText, Pageable pageable); + TbResourceInfoEntity findByTenantIdAndResourceTypeAndResourceKey(UUID tenantId, String resourceType, String resourceKey); + + boolean existsByTenantIdAndResourceTypeAndResourceKey(UUID tenantId, String resourceType, String resourceKey); + + @Query(value = "SELECT r.resource_key FROM resource r WHERE r.tenant_id = :tenantId AND r.resource_type = :resourceType " + + "AND starts_with(r.resource_key, :prefix)", nativeQuery = true) + Set findKeysByTenantIdAndResourceTypeAndResourceKeyStartingWith(@Param("tenantId") UUID tenantId, + @Param("resourceType") String resourceType, + @Param("prefix") String prefix); + + List findByTenantIdAndEtagAndResourceKeyStartingWith(UUID tenantId, String etag, String query); + + @Query(value = "SELECT * FROM resource r WHERE (r.tenant_id = '13814000-1dd2-11b2-8080-808080808080' OR r.tenant_id = :tenantId) " + + "AND r.resource_type = :resourceType AND r.etag = :etag ORDER BY created_time, id LIMIT 1", nativeQuery = true) + TbResourceInfoEntity findSystemOrTenantImageByEtag(@Param("tenantId") UUID tenantId, + @Param("resourceType") String resourceType, + @Param("etag") String etag); + + boolean existsByResourceTypeAndPublicResourceKey(String resourceType, String publicResourceKey); + + TbResourceInfoEntity findByResourceTypeAndPublicResourceKeyAndIsPublicTrue(String resourceType, String publicResourceKey); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceRepository.java index 54494673a1..9fd69a37ef 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceRepository.java @@ -20,12 +20,13 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import org.thingsboard.server.dao.ExportableEntityRepository; import org.thingsboard.server.dao.model.sql.TbResourceEntity; import java.util.List; import java.util.UUID; -public interface TbResourceRepository extends JpaRepository { +public interface TbResourceRepository extends JpaRepository, ExportableEntityRepository { TbResourceEntity findByTenantIdAndResourceTypeAndResourceKey(UUID tenantId, String resourceType, String resourceKey); @@ -33,7 +34,7 @@ public interface TbResourceRepository extends JpaRepository findResources(@Param("tenantId") UUID tenantId, @Param("systemAdminId") UUID sysAdminId, @Param("resourceType") String resourceType, - @Param("searchText") String search); + @Param("searchText") String searchText); @Query("SELECT tr FROM TbResourceEntity tr " + "WHERE tr.resourceType = :resourceType " + @@ -80,4 +81,20 @@ public interface TbResourceRepository extends JpaRepository findIdsByTenantId(@Param("tenantId") UUID tenantId, Pageable pageable); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java index 7b5e6b8d79..4a16b401c2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java @@ -61,7 +61,7 @@ public class JpaRuleChainDao extends JpaAbstractDao return DaoUtil.toPageData(ruleChainRepository .findByTenantId( tenantId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @@ -72,7 +72,7 @@ public class JpaRuleChainDao extends JpaAbstractDao .findByTenantIdAndType( tenantId, type, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @@ -89,7 +89,7 @@ public class JpaRuleChainDao extends JpaAbstractDao .findByTenantIdAndEdgeId( tenantId, edgeId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @@ -99,7 +99,7 @@ public class JpaRuleChainDao extends JpaAbstractDao return DaoUtil.toPageData(ruleChainRepository .findAutoAssignByTenantId( tenantId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java index 7843dece21..de5ff109d3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java @@ -56,8 +56,8 @@ public class JpaRuleNodeDao extends JpaAbstractDao imp } @Override - public List findRuleNodesByTenantIdAndType(TenantId tenantId, String type, String search) { - return DaoUtil.convertDataList(ruleNodeRepository.findRuleNodesByTenantIdAndType(tenantId.getId(), type, search)); + public List findRuleNodesByTenantIdAndType(TenantId tenantId, String type, String configurationSearch) { + return DaoUtil.convertDataList(ruleNodeRepository.findRuleNodesByTenantIdAndType(tenantId.getId(), type, configurationSearch)); } @Override @@ -69,6 +69,32 @@ public class JpaRuleNodeDao extends JpaAbstractDao imp DaoUtil.toPageable(pageLink))); } + @Override + public PageData findAllRuleNodesByTypeAndVersionLessThan(String type, int version, PageLink pageLink) { + return DaoUtil.toPageData(ruleNodeRepository + .findAllRuleNodesByTypeAndVersionLessThan( + type, + version, + Objects.toString(pageLink.getTextSearch(), ""), + DaoUtil.toPageable(pageLink))); + } + + @Override + public PageData findAllRuleNodeIdsByTypeAndVersionLessThan(String type, int version, PageLink pageLink) { + return DaoUtil.pageToPageData(ruleNodeRepository + .findAllRuleNodeIdsByTypeAndVersionLessThan( + type, + version, + DaoUtil.toPageable(pageLink))) + .mapData(RuleNodeId::new); + } + + @Override + public List findAllRuleNodeByIds(List ruleNodeIds) { + return DaoUtil.convertDataList(ruleNodeRepository.findAllById( + ruleNodeIds.stream().map(RuleNodeId::getId).collect(Collectors.toList()))); + } + @Override public List findByExternalIds(RuleChainId ruleChainId, List externalIds) { return DaoUtil.convertDataList(ruleNodeRepository.findRuleNodesByRuleChainIdAndExternalIdIn(ruleChainId.getId(), diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java index 8cc57c62ce..4b6b5a0db7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java @@ -30,14 +30,14 @@ import java.util.UUID; public interface RuleChainRepository extends JpaRepository, ExportableEntityRepository { @Query("SELECT rc FROM RuleChainEntity rc WHERE rc.tenantId = :tenantId " + - "AND LOWER(rc.name) LIKE LOWER(CONCAT('%', :searchText, '%'))") + "AND (:searchText IS NULL OR ilike(rc.name, CONCAT('%', :searchText, '%')) = true)") Page findByTenantId(@Param("tenantId") UUID tenantId, @Param("searchText") String searchText, Pageable pageable); @Query("SELECT rc FROM RuleChainEntity rc WHERE rc.tenantId = :tenantId " + "AND rc.type = :type " + - "AND LOWER(rc.name) LIKE LOWER(CONCAT('%', :searchText, '%'))") + "AND (:searchText IS NULL OR ilike(rc.name, CONCAT('%', :searchText, '%')) = true)") Page findByTenantIdAndType(@Param("tenantId") UUID tenantId, @Param("type") RuleChainType type, @Param("searchText") String searchText, @@ -46,7 +46,7 @@ public interface RuleChainRepository extends JpaRepository findByTenantIdAndEdgeId(@Param("tenantId") UUID tenantId, @Param("edgeId") UUID edgeId, @Param("searchText") String searchText, @@ -55,7 +55,7 @@ public interface RuleChainRepository extends JpaRepository findAutoAssignByTenantId(@Param("tenantId") UUID tenantId, @Param("searchText") String searchText, Pageable pageable); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleNodeRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleNodeRepository.java index 1e157d371f..f02072b356 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleNodeRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleNodeRepository.java @@ -29,18 +29,32 @@ import java.util.UUID; public interface RuleNodeRepository extends JpaRepository { - @Query("SELECT r FROM RuleNodeEntity r WHERE r.ruleChainId in " + - "(select id from RuleChainEntity rc WHERE rc.tenantId = :tenantId) " + - "AND r.type = :ruleType AND LOWER(r.configuration) LIKE LOWER(CONCAT('%', :searchText, '%')) ") + @Query(nativeQuery = true, value = "SELECT * FROM rule_node r WHERE r.rule_chain_id in " + + "(select id from rule_chain rc WHERE rc.tenant_id = :tenantId) AND r.type = :ruleType " + + " AND (:searchText IS NULL OR r.configuration ILIKE CONCAT('%', :searchText, '%'))") List findRuleNodesByTenantIdAndType(@Param("tenantId") UUID tenantId, @Param("ruleType") String ruleType, @Param("searchText") String searchText); - @Query("SELECT r FROM RuleNodeEntity r WHERE r.type = :ruleType AND LOWER(r.configuration) LIKE LOWER(CONCAT('%', :searchText, '%')) ") + @Query(nativeQuery = true, value = "SELECT * FROM rule_node r WHERE r.type = :ruleType " + + " AND (:searchText IS NULL OR r.configuration ILIKE CONCAT('%', :searchText, '%'))") Page findAllRuleNodesByType(@Param("ruleType") String ruleType, @Param("searchText") String searchText, Pageable pageable); + @Query(nativeQuery = true, value = "SELECT * FROM rule_node r WHERE r.type = :ruleType " + + " AND configuration_version < :version " + + " AND (:searchText IS NULL OR r.configuration ILIKE CONCAT('%', :searchText, '%'))") + Page findAllRuleNodesByTypeAndVersionLessThan(@Param("ruleType") String ruleType, + @Param("version") int version, + @Param("searchText") String searchText, + Pageable pageable); + + @Query("SELECT r.id FROM RuleNodeEntity r WHERE r.type = :ruleType AND r.configurationVersion < :version") + Page findAllRuleNodeIdsByTypeAndVersionLessThan(@Param("ruleType") String ruleType, + @Param("version") int version, + Pageable pageable); + List findRuleNodesByRuleChainIdAndExternalIdIn(UUID ruleChainId, List externalIds); @Transactional diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java index 9f01494e5c..6f0b810817 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java @@ -67,7 +67,7 @@ public class JpaTenantDao extends JpaAbstractDao implement public PageData findTenants(TenantId tenantId, PageLink pageLink) { return DaoUtil.toPageData(tenantRepository .findTenantsNextPage( - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @@ -75,7 +75,7 @@ public class JpaTenantDao extends JpaAbstractDao implement public PageData findTenantInfos(TenantId tenantId, PageLink pageLink) { return DaoUtil.toPageData(tenantRepository .findTenantInfosNextPage( - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink, TenantInfoEntity.tenantInfoColumnMap))); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantProfileDao.java index 2d65326b50..e74d2d555d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantProfileDao.java @@ -60,7 +60,7 @@ public class JpaTenantProfileDao extends JpaAbstractDao findTenantProfiles(TenantId tenantId, PageLink pageLink) { return DaoUtil.toPageData( tenantProfileRepository.findTenantProfiles( - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @@ -68,7 +68,7 @@ public class JpaTenantProfileDao extends JpaAbstractDao findTenantProfileInfos(TenantId tenantId, PageLink pageLink) { return DaoUtil.pageToPageData( tenantProfileRepository.findTenantProfileInfos( - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantProfileRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantProfileRepository.java index a42dc1c797..57cd8fc06f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantProfileRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantProfileRepository.java @@ -34,13 +34,13 @@ public interface TenantProfileRepository extends JpaRepository findTenantProfiles(@Param("textSearch") String textSearch, Pageable pageable); @Query("SELECT new org.thingsboard.server.common.data.EntityInfo(t.id, 'TENANT_PROFILE', t.name) " + "FROM TenantProfileEntity t " + - "WHERE LOWER(t.name) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + "WHERE (:textSearch IS NULL OR ilike(t.name, CONCAT('%', :textSearch, '%')) = true)") Page findTenantProfileInfos(@Param("textSearch") String textSearch, Pageable pageable); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantRepository.java index 5174e3af83..c5340de615 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantRepository.java @@ -37,14 +37,14 @@ public interface TenantRepository extends JpaRepository { "WHERE t.id = :tenantId") TenantInfoEntity findTenantInfoById(@Param("tenantId") UUID tenantId); - @Query("SELECT t FROM TenantEntity t WHERE LOWER(t.title) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + @Query("SELECT t FROM TenantEntity t WHERE (:textSearch IS NULL OR ilike(t.title, CONCAT('%', :textSearch, '%')) = true)") Page findTenantsNextPage(@Param("textSearch") String textSearch, Pageable pageable); @Query("SELECT new org.thingsboard.server.dao.model.sql.TenantInfoEntity(t, p.name) " + "FROM TenantEntity t " + "LEFT JOIN TenantProfileEntity p on p.id = t.tenantProfileId " + - "WHERE LOWER(t.title) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + "WHERE (:textSearch IS NULL OR ilike(t.title, CONCAT('%', :textSearch, '%')) = true)") Page findTenantInfosNextPage(@Param("textSearch") String textSearch, Pageable pageable); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserCredentialsDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserCredentialsDao.java index e5fcd5bbf8..8ac3c1cf55 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserCredentialsDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserCredentialsDao.java @@ -19,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.UserCredentialsEntity; @@ -62,4 +63,10 @@ public class JpaUserCredentialsDao extends JpaAbstractDao implements User userRepository .findByTenantId( tenantId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } @@ -85,7 +85,7 @@ public class JpaUserDao extends JpaAbstractDao implements User .findUsersByAuthority( tenantId, NULL_UUID, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), Authority.TENANT_ADMIN, DaoUtil.toPageable(pageLink))); } @@ -97,7 +97,7 @@ public class JpaUserDao extends JpaAbstractDao implements User .findUsersByAuthority( tenantId, customerId, - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), Authority.CUSTOMER_USER, DaoUtil.toPageable(pageLink))); @@ -110,7 +110,7 @@ public class JpaUserDao extends JpaAbstractDao implements User .findTenantAndCustomerUsers( tenantId, DaoUtil.toUUIDs(customerIds), - Objects.toString(pageLink.getTextSearch(), ""), + pageLink.getTextSearch(), DaoUtil.toPageable(pageLink))); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserCredentialsRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserCredentialsRepository.java index 1cfc8b350c..46c2054016 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserCredentialsRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserCredentialsRepository.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao.sql.user; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.dao.model.sql.UserCredentialsEntity; import java.util.UUID; @@ -30,4 +31,8 @@ public interface UserCredentialsRepository extends JpaRepository { @Query("SELECT u FROM UserEntity u WHERE u.tenantId = :tenantId " + "AND u.customerId = :customerId AND u.authority = :authority " + - "AND LOWER(u.email) LIKE LOWER(CONCAT('%', :searchText, '%'))") + "AND (:searchText IS NULL OR ilike(u.email, CONCAT('%', :searchText, '%')) = true)") Page findUsersByAuthority(@Param("tenantId") UUID tenantId, @Param("customerId") UUID customerId, @Param("searchText") String searchText, @@ -46,14 +46,14 @@ public interface UserRepository extends JpaRepository { @Query("SELECT u FROM UserEntity u WHERE u.tenantId = :tenantId " + "AND u.customerId IN (:customerIds) " + - "AND LOWER(u.email) LIKE LOWER(CONCAT('%', :searchText, '%'))") + "AND (:searchText IS NULL OR ilike(u.email, CONCAT('%', :searchText, '%')) = true)") Page findTenantAndCustomerUsers(@Param("tenantId") UUID tenantId, @Param("customerIds") Collection customerIds, @Param("searchText") String searchText, Pageable pageable); @Query("SELECT u FROM UserEntity u WHERE u.tenantId = :tenantId " + - "AND LOWER(u.email) LIKE LOWER(CONCAT('%', :searchText, '%'))") + "AND (:searchText IS NULL OR ilike(u.email, CONCAT('%', :searchText, '%')) = true)") Page findByTenantId(@Param("tenantId") UUID tenantId, @Param("searchText") String searchText, Pageable pageable); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java index b54dd371fe..d105943a80 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java @@ -20,17 +20,32 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.WidgetTypeId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.widget.DeprecatedFilter; import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetTypeInfo; +import org.thingsboard.server.common.data.widget.WidgetsBundleWidget; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.WidgetTypeDetailsEntity; +import org.thingsboard.server.dao.model.sql.WidgetTypeInfoEntity; +import org.thingsboard.server.dao.model.sql.WidgetsBundleWidgetCompositeKey; +import org.thingsboard.server.dao.model.sql.WidgetsBundleWidgetEntity; import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; import org.thingsboard.server.dao.widget.WidgetTypeDao; +import java.util.Collections; +import java.util.Comparator; import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.UUID; +import java.util.stream.Collectors; + +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; /** * Created by Valerii Sosliuk on 4/29/2017. @@ -42,6 +57,12 @@ public class JpaWidgetTypeDao extends JpaAbstractDao getEntityClass() { return WidgetTypeDetailsEntity.class; @@ -58,23 +79,171 @@ public class JpaWidgetTypeDao extends JpaAbstractDao findWidgetTypesByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias) { - return DaoUtil.convertDataList(widgetTypeRepository.findWidgetTypesByTenantIdAndBundleAlias(tenantId, bundleAlias)); + public boolean existsByTenantIdAndId(TenantId tenantId, UUID widgetTypeId) { + return widgetTypeRepository.existsByTenantIdAndId(tenantId.getId(), widgetTypeId); + } + + @Override + public PageData findSystemWidgetTypes(TenantId tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink) { + boolean deprecatedFilterEnabled = !DeprecatedFilter.ALL.equals(deprecatedFilter); + boolean deprecatedFilterBool = DeprecatedFilter.DEPRECATED.equals(deprecatedFilter); + boolean widgetTypesEmpty = widgetTypes == null || widgetTypes.isEmpty(); + return DaoUtil.toPageData( + widgetTypeInfoRepository + .findSystemWidgetTypes( + NULL_UUID, + pageLink.getTextSearch(), + fullSearch, + deprecatedFilterEnabled, + deprecatedFilterBool, + widgetTypesEmpty, + widgetTypes == null ? Collections.emptyList() : widgetTypes, + DaoUtil.toPageable(pageLink, WidgetTypeInfoEntity.SEARCH_COLUMNS_MAP))); + } + + @Override + public PageData findAllTenantWidgetTypesByTenantId(UUID tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink) { + boolean deprecatedFilterEnabled = !DeprecatedFilter.ALL.equals(deprecatedFilter); + boolean deprecatedFilterBool = DeprecatedFilter.DEPRECATED.equals(deprecatedFilter); + boolean widgetTypesEmpty = widgetTypes == null || widgetTypes.isEmpty(); + return DaoUtil.toPageData( + widgetTypeInfoRepository + .findAllTenantWidgetTypesByTenantId( + tenantId, + NULL_UUID, + pageLink.getTextSearch(), + fullSearch, + deprecatedFilterEnabled, + deprecatedFilterBool, + widgetTypesEmpty, + widgetTypes == null ? Collections.emptyList() : widgetTypes, + DaoUtil.toPageable(pageLink, WidgetTypeInfoEntity.SEARCH_COLUMNS_MAP))); + } + + @Override + public PageData findTenantWidgetTypesByTenantId(UUID tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink) { + boolean deprecatedFilterEnabled = !DeprecatedFilter.ALL.equals(deprecatedFilter); + boolean deprecatedFilterBool = DeprecatedFilter.DEPRECATED.equals(deprecatedFilter); + boolean widgetTypesEmpty = widgetTypes == null || widgetTypes.isEmpty(); + return DaoUtil.toPageData( + widgetTypeInfoRepository + .findTenantWidgetTypesByTenantId( + tenantId, + pageLink.getTextSearch(), + fullSearch, + deprecatedFilterEnabled, + deprecatedFilterBool, + widgetTypesEmpty, + widgetTypes == null ? Collections.emptyList() : widgetTypes, + DaoUtil.toPageable(pageLink, WidgetTypeInfoEntity.SEARCH_COLUMNS_MAP))); + } + + @Override + public List findWidgetTypesByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId) { + return DaoUtil.convertDataList(widgetTypeRepository.findWidgetTypesByWidgetsBundleId(widgetsBundleId)); + } + + @Override + public List findWidgetTypesDetailsByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId) { + return DaoUtil.convertDataList(widgetTypeRepository.findWidgetTypesDetailsByWidgetsBundleId(widgetsBundleId)); + } + + @Override + public PageData findWidgetTypesInfosByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink) { + boolean deprecatedFilterEnabled = !DeprecatedFilter.ALL.equals(deprecatedFilter); + boolean deprecatedFilterBool = DeprecatedFilter.DEPRECATED.equals(deprecatedFilter); + boolean widgetTypesEmpty = widgetTypes == null || widgetTypes.isEmpty(); + return DaoUtil.toPageData( + widgetTypeInfoRepository + .findWidgetTypesInfosByWidgetsBundleId( + widgetsBundleId, + Objects.toString(pageLink.getTextSearch(), ""), + fullSearch, + deprecatedFilterEnabled, + deprecatedFilterBool, + widgetTypesEmpty, + widgetTypes == null ? Collections.emptyList() : widgetTypes, + DaoUtil.toPageable(pageLink, WidgetTypeInfoEntity.SEARCH_COLUMNS_MAP))); + } + + @Override + public List findWidgetFqnsByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId) { + return widgetTypeRepository.findWidgetFqnsByWidgetsBundleId(widgetsBundleId); + } + + @Override + public WidgetType findByTenantIdAndFqn(UUID tenantId, String fqn) { + return DaoUtil.getData(widgetTypeRepository.findWidgetTypeByTenantIdAndFqn(tenantId, fqn)); } @Override - public List findWidgetTypesDetailsByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias) { - return DaoUtil.convertDataList(widgetTypeRepository.findByTenantIdAndBundleAlias(tenantId, bundleAlias)); + public List findWidgetTypesInfosByTenantIdAndResourceId(UUID tenantId, UUID tbResourceId) { + return DaoUtil.convertDataList(widgetTypeRepository.findWidgetTypesInfosByTenantIdAndResourceId(tenantId, tbResourceId)); } @Override - public List findWidgetTypesInfosByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias) { - return DaoUtil.convertDataList(widgetTypeRepository.findWidgetTypesInfosByTenantIdAndBundleAlias(tenantId, bundleAlias)); + public List findWidgetTypeIdsByTenantIdAndFqns(UUID tenantId, List widgetFqns) { + var idFqnPairs = widgetTypeRepository.findWidgetTypeIdsByTenantIdAndFqns(tenantId, widgetFqns); + idFqnPairs.sort(Comparator.comparingInt(o -> widgetFqns.indexOf(o.getFqn()))); + return idFqnPairs.stream() + .map(id -> new WidgetTypeId(id.getId())).collect(Collectors.toList()); } @Override - public WidgetType findByTenantIdBundleAliasAndAlias(UUID tenantId, String bundleAlias, String alias) { - return DaoUtil.getData(widgetTypeRepository.findWidgetTypeByTenantIdAndBundleAliasAndAlias(tenantId, bundleAlias, alias)); + public WidgetTypeDetails findByTenantIdAndExternalId(UUID tenantId, UUID externalId) { + return DaoUtil.getData(widgetTypeRepository.findByTenantIdAndExternalId(tenantId, externalId)); + } + + @Override + public PageData findByTenantId(UUID tenantId, PageLink pageLink) { + return DaoUtil.toPageData( + widgetTypeRepository + .findTenantWidgetTypeDetailsByTenantId( + tenantId, + pageLink.getTextSearch(), + DaoUtil.toPageable(pageLink))); + } + + @Override + public PageData findIdsByTenantId(UUID tenantId, PageLink pageLink) { + return DaoUtil.pageToPageData(widgetTypeRepository.findIdsByTenantId(tenantId, DaoUtil.toPageable(pageLink)) + .map(WidgetTypeId::new)); + } + + @Override + public WidgetTypeId getExternalIdByInternal(WidgetTypeId internalId) { + return Optional.ofNullable(widgetTypeRepository.getExternalIdById(internalId.getId())) + .map(WidgetTypeId::new).orElse(null); + } + + @Override + public List findWidgetsBundleWidgetsByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId) { + return DaoUtil.convertDataList(widgetsBundleWidgetRepository.findAllByWidgetsBundleId(widgetsBundleId)); + } + + @Override + public void saveWidgetsBundleWidget(WidgetsBundleWidget widgetsBundleWidget) { + widgetsBundleWidgetRepository.save(new WidgetsBundleWidgetEntity(widgetsBundleWidget)); + } + + @Override + public void removeWidgetTypeFromWidgetsBundle(UUID widgetsBundleId, UUID widgetTypeId) { + widgetsBundleWidgetRepository.deleteById(new WidgetsBundleWidgetCompositeKey(widgetsBundleId, widgetTypeId)); + } + + @Override + public PageData findAllWidgetTypesIds(PageLink pageLink) { + return DaoUtil.pageToPageData(widgetTypeRepository.findAllIds(DaoUtil.toPageable(pageLink)).map(WidgetTypeId::new)); + } + + @Override + public List findByTenantAndImageLink(TenantId tenantId, String imageUrl, int limit) { + return DaoUtil.convertDataList(widgetTypeInfoRepository.findByTenantAndImageUrl(tenantId.getId(), imageUrl, limit)); + } + + @Override + public List findByImageLink(String imageUrl, int limit) { + return DaoUtil.convertDataList(widgetTypeInfoRepository.findByImageUrl(imageUrl, limit)); } @Override @@ -82,4 +251,5 @@ public class JpaWidgetTypeDao extends JpaAbstractDao findSystemWidgetsBundles(TenantId tenantId, PageLink pageLink) { - return DaoUtil.toPageData( - widgetsBundleRepository - .findSystemWidgetsBundles( - NULL_UUID, - Objects.toString(pageLink.getTextSearch(), ""), - DaoUtil.toPageable(pageLink))); + public PageData findSystemWidgetsBundles(TenantId tenantId, boolean fullSearch, PageLink pageLink) { + if (fullSearch) { + return DaoUtil.toPageData( + widgetsBundleRepository + .findSystemWidgetsBundlesFullSearch( + NULL_UUID, + pageLink.getTextSearch(), + DaoUtil.toPageable(pageLink))); + } else { + return DaoUtil.toPageData( + widgetsBundleRepository + .findSystemWidgetsBundles( + NULL_UUID, + pageLink.getTextSearch(), + DaoUtil.toPageable(pageLink))); + } } @Override @@ -77,19 +88,41 @@ public class JpaWidgetsBundleDao extends JpaAbstractDao findAllTenantWidgetsBundlesByTenantId(UUID tenantId, PageLink pageLink) { - return DaoUtil.toPageData( - widgetsBundleRepository - .findAllTenantWidgetsBundlesByTenantId( - tenantId, - NULL_UUID, - Objects.toString(pageLink.getTextSearch(), ""), - DaoUtil.toPageable(pageLink))); + public PageData findAllTenantWidgetsBundlesByTenantId(UUID tenantId, boolean fullSearch, PageLink pageLink) { + return findTenantWidgetsBundlesByTenantIds(Arrays.asList(tenantId, NULL_UUID), fullSearch, pageLink); + } + + @Override + public PageData findTenantWidgetsBundlesByTenantId(UUID tenantId, boolean fullSearch, PageLink pageLink) { + return findTenantWidgetsBundlesByTenantIds(Collections.singletonList(tenantId), fullSearch, pageLink); + } + + @Override + public PageData findAllWidgetsBundles(PageLink pageLink) { + return DaoUtil.toPageData(widgetsBundleRepository.findAll(DaoUtil.toPageable(pageLink))); + } + + private PageData findTenantWidgetsBundlesByTenantIds(List tenantIds, boolean fullSearch, PageLink pageLink) { + if (fullSearch) { + return DaoUtil.toPageData( + widgetsBundleRepository + .findAllTenantWidgetsBundlesByTenantIdsFullSearch( + tenantIds, + pageLink.getTextSearch(), + DaoUtil.toPageable(pageLink))); + } else { + return DaoUtil.toPageData( + widgetsBundleRepository + .findAllTenantWidgetsBundlesByTenantIds( + tenantIds, + pageLink.getTextSearch(), + DaoUtil.toPageable(pageLink))); + } } @Override @@ -113,6 +146,16 @@ public class JpaWidgetsBundleDao extends JpaAbstractDao findByTenantAndImageLink(TenantId tenantId, String imageUrl, int limit) { + return DaoUtil.convertDataList(widgetsBundleRepository.findByTenantAndImageUrl(tenantId.getId(), imageUrl, limit)); + } + + @Override + public List findByImageLink(String imageUrl, int limit) { + return DaoUtil.convertDataList(widgetsBundleRepository.findByImageUrl(imageUrl, limit)); + } + @Override public EntityType getEntityType() { return EntityType.WIDGETS_BUNDLE; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeInfoRepository.java new file mode 100644 index 0000000000..221642699e --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeInfoRepository.java @@ -0,0 +1,210 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.widget; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.thingsboard.server.dao.model.sql.WidgetTypeInfoEntity; + +import java.util.List; +import java.util.UUID; + +public interface WidgetTypeInfoRepository extends JpaRepository { + + @Query(nativeQuery = true, + value = "SELECT * FROM widget_type_info_view wti WHERE wti.tenant_id = :systemTenantId " + + "AND ((:deprecatedFilterEnabled) IS FALSE OR wti.deprecated = :deprecatedFilter) " + + "AND ((:widgetTypesEmpty) IS TRUE OR wti.widget_type IN (:widgetTypes)) " + + "AND (wti.name ILIKE CONCAT('%', :searchText, '%') " + + "OR ((:fullSearch) IS TRUE AND (wti.description ILIKE CONCAT('%', :searchText, '%') " + + "OR EXISTS (" + + "SELECT 1 " + + "FROM unnest(wti.tags) AS currentTag " + + "WHERE :searchText ILIKE '%' || currentTag || '%' " + + "AND (length(:searchText) = length(currentTag) " + + "OR :searchText ILIKE currentTag || ' %' " + + "OR :searchText ILIKE '% ' || currentTag " + + "OR :searchText ILIKE '% ' || currentTag || ' %')" + + "))))", + countQuery = "SELECT count(*) FROM widget_type_info_view wti WHERE wti.tenant_id = :systemTenantId " + + "AND ((:deprecatedFilterEnabled) IS FALSE OR wti.deprecated = :deprecatedFilter) " + + "AND ((:widgetTypesEmpty) IS TRUE OR wti.widget_type IN (:widgetTypes)) " + + "AND (wti.name ILIKE CONCAT('%', :searchText, '%') " + + "OR ((:fullSearch) IS TRUE AND (wti.description ILIKE CONCAT('%', :searchText, '%') " + + "OR EXISTS (" + + "SELECT 1 " + + "FROM unnest(wti.tags) AS currentTag " + + "WHERE :searchText ILIKE '%' || currentTag || '%' " + + "AND (length(:searchText) = length(currentTag) " + + "OR :searchText ILIKE currentTag || ' %' " + + "OR :searchText ILIKE '% ' || currentTag " + + "OR :searchText ILIKE '% ' || currentTag || ' %')" + + "))))" + ) + Page findSystemWidgetTypes(@Param("systemTenantId") UUID systemTenantId, + @Param("searchText") String searchText, + @Param("fullSearch") boolean fullSearch, + @Param("deprecatedFilterEnabled") boolean deprecatedFilterEnabled, + @Param("deprecatedFilter") boolean deprecatedFilter, + @Param("widgetTypesEmpty") boolean widgetTypesEmpty, + @Param("widgetTypes") List widgetTypes, + Pageable pageable); + + @Query(nativeQuery = true, + value = "SELECT * FROM widget_type_info_view wti WHERE wti.tenant_id IN (:tenantId, :nullTenantId) " + + "AND ((:deprecatedFilterEnabled) IS FALSE OR wti.deprecated = :deprecatedFilter) " + + "AND ((:widgetTypesEmpty) IS TRUE OR wti.widget_type IN (:widgetTypes)) " + + "AND (wti.name ILIKE CONCAT('%', :searchText, '%') " + + "OR ((:fullSearch) IS TRUE AND (wti.description ILIKE CONCAT('%', :searchText, '%') " + + "OR EXISTS (" + + "SELECT 1 " + + "FROM unnest(wti.tags) AS currentTag " + + "WHERE :searchText ILIKE '%' || currentTag || '%' " + + "AND (length(:searchText) = length(currentTag) " + + "OR :searchText ILIKE currentTag || ' %' " + + "OR :searchText ILIKE '% ' || currentTag " + + "OR :searchText ILIKE '% ' || currentTag || ' %')" + + "))))", + countQuery = "SELECT count(*) FROM widget_type_info_view wti WHERE wti.tenant_id IN (:tenantId, :nullTenantId) " + + "AND ((:deprecatedFilterEnabled) IS FALSE OR wti.deprecated = :deprecatedFilter) " + + "AND ((:widgetTypesEmpty) IS TRUE OR wti.widget_type IN (:widgetTypes)) " + + "AND (wti.name ILIKE CONCAT('%', :searchText, '%') " + + "OR ((:fullSearch) IS TRUE AND (wti.description ILIKE CONCAT('%', :searchText, '%') " + + "OR EXISTS (" + + "SELECT 1 " + + "FROM unnest(wti.tags) AS currentTag " + + "WHERE :searchText ILIKE '%' || currentTag || '%' " + + "AND (length(:searchText) = length(currentTag) " + + "OR :searchText ILIKE currentTag || ' %' " + + "OR :searchText ILIKE '% ' || currentTag " + + "OR :searchText ILIKE '% ' || currentTag || ' %')" + + "))))" + ) + Page findAllTenantWidgetTypesByTenantId(@Param("tenantId") UUID tenantId, + @Param("nullTenantId") UUID nullTenantId, + @Param("searchText") String searchText, + @Param("fullSearch") boolean fullSearch, + @Param("deprecatedFilterEnabled") boolean deprecatedFilterEnabled, + @Param("deprecatedFilter") boolean deprecatedFilter, + @Param("widgetTypesEmpty") boolean widgetTypesEmpty, + @Param("widgetTypes") List widgetTypes, + Pageable pageable); + + @Query(nativeQuery = true, + value = "SELECT * FROM widget_type_info_view wti WHERE wti.tenant_id = :tenantId " + + "AND ((:deprecatedFilterEnabled) IS FALSE OR wti.deprecated = :deprecatedFilter) " + + "AND ((:widgetTypesEmpty) IS TRUE OR wti.widget_type IN (:widgetTypes)) " + + "AND (wti.name ILIKE CONCAT('%', :searchText, '%') " + + "OR ((:fullSearch) IS TRUE AND (wti.description ILIKE CONCAT('%', :searchText, '%') " + + "OR EXISTS (" + + "SELECT 1 " + + "FROM unnest(wti.tags) AS currentTag " + + "WHERE :searchText ILIKE '%' || currentTag || '%' " + + "AND (length(:searchText) = length(currentTag) " + + "OR :searchText ILIKE currentTag || ' %' " + + "OR :searchText ILIKE '% ' || currentTag " + + "OR :searchText ILIKE '% ' || currentTag || ' %')" + + "))))", + countQuery = "SELECT count(*) FROM widget_type_info_view wti WHERE wti.tenant_id = :tenantId " + + "AND ((:deprecatedFilterEnabled) IS FALSE OR wti.deprecated = :deprecatedFilter) " + + "AND ((:widgetTypesEmpty) IS TRUE OR wti.widget_type IN (:widgetTypes)) " + + "AND (wti.name ILIKE CONCAT('%', :searchText, '%') " + + "OR ((:fullSearch) IS TRUE AND (wti.description ILIKE CONCAT('%', :searchText, '%') " + + "OR EXISTS (" + + "SELECT 1 " + + "FROM unnest(wti.tags) AS currentTag " + + "WHERE :searchText ILIKE '%' || currentTag || '%' " + + "AND (length(:searchText) = length(currentTag) " + + "OR :searchText ILIKE currentTag || ' %' " + + "OR :searchText ILIKE '% ' || currentTag " + + "OR :searchText ILIKE '% ' || currentTag || ' %')" + + "))))" + ) + Page findTenantWidgetTypesByTenantId(@Param("tenantId") UUID tenantId, + @Param("searchText") String searchText, + @Param("fullSearch") boolean fullSearch, + @Param("deprecatedFilterEnabled") boolean deprecatedFilterEnabled, + @Param("deprecatedFilter") boolean deprecatedFilter, + @Param("widgetTypesEmpty") boolean widgetTypesEmpty, + @Param("widgetTypes") List widgetTypes, + Pageable pageable); + + @Query("SELECT wti FROM WidgetTypeInfoEntity wti, WidgetsBundleWidgetEntity wbw " + + "WHERE wbw.widgetsBundleId = :widgetsBundleId " + + "AND wbw.widgetTypeId = wti.id ORDER BY wbw.widgetTypeOrder") + List findWidgetTypesInfosByWidgetsBundleId(@Param("widgetsBundleId") UUID widgetsBundleId); + + @Query(nativeQuery = true, + value = "SELECT * FROM widget_type_info_view wti, widgets_bundle_widget wbw " + + "WHERE wbw.widgets_bundle_id = :widgetsBundleId " + + "AND wbw.widget_type_id = wti.id " + + "AND ((:deprecatedFilterEnabled) IS FALSE OR wti.deprecated = :deprecatedFilter) " + + "AND ((:widgetTypesEmpty) IS TRUE OR wti.widget_type IN (:widgetTypes)) " + + "AND (wti.name ILIKE CONCAT('%', :searchText, '%') " + + "OR ((:fullSearch) IS TRUE AND (wti.description ILIKE CONCAT('%', :searchText, '%') " + + "OR EXISTS (" + + "SELECT 1 " + + "FROM unnest(wti.tags) AS currentTag " + + "WHERE :searchText ILIKE '%' || currentTag || '%' " + + "AND (length(:searchText) = length(currentTag) " + + "OR :searchText ILIKE currentTag || ' %' " + + "OR :searchText ILIKE '% ' || currentTag " + + "OR :searchText ILIKE '% ' || currentTag || ' %')" + + ")))) " + + "ORDER BY wbw.widget_type_order", + countQuery = "SELECT count(*) FROM widget_type_info_view wti, widgets_bundle_widget wbw " + + "WHERE wbw.widgets_bundle_id = :widgetsBundleId " + + "AND wbw.widget_type_id = wti.id " + + "AND ((:deprecatedFilterEnabled) IS FALSE OR wti.deprecated = :deprecatedFilter) " + + "AND ((:widgetTypesEmpty) IS TRUE OR wti.widget_type IN (:widgetTypes)) " + + "AND (wti.name ILIKE CONCAT('%', :searchText, '%') " + + "OR ((:fullSearch) IS TRUE AND (wti.description ILIKE CONCAT('%', :searchText, '%') " + + "OR EXISTS (" + + "SELECT 1 " + + "FROM unnest(wti.tags) AS currentTag " + + "WHERE :searchText ILIKE '%' || currentTag || '%' " + + "AND (length(:searchText) = length(currentTag) " + + "OR :searchText ILIKE currentTag || ' %' " + + "OR :searchText ILIKE '% ' || currentTag " + + "OR :searchText ILIKE '% ' || currentTag || ' %')" + + "))))" + ) + Page findWidgetTypesInfosByWidgetsBundleId(@Param("widgetsBundleId") UUID widgetsBundleId, + @Param("searchText") String searchText, + @Param("fullSearch") boolean fullSearch, + @Param("deprecatedFilterEnabled") boolean deprecatedFilterEnabled, + @Param("deprecatedFilter") boolean deprecatedFilter, + @Param("widgetTypesEmpty") boolean widgetTypesEmpty, + @Param("widgetTypes") List widgetTypes, + Pageable pageable); + + + @Query(nativeQuery = true, + value = "SELECT * FROM widget_type_info_view wti WHERE wti.id IN " + + "(select id from widget_type where tenant_id = :tenantId " + + "and (image = :imageLink or descriptor ILIKE CONCAT('%\"', :imageLink, '\"%')) limit :lmt)" + ) + List findByTenantAndImageUrl(@Param("tenantId") UUID tenantId, @Param("imageLink") String imageLink, @Param("lmt") int lmt); + + @Query(nativeQuery = true, + value = "SELECT * FROM widget_type_info_view wti WHERE wti.id IN " + + "(select id from widget_type where image = :imageLink or descriptor ILIKE CONCAT('%', :imageLink, '%') limit :lmt)" + ) + List findByImageUrl(@Param("imageLink") String imageLink, @Param("lmt") int lmt); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeRepository.java index a3cba644b5..0583d0b47d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetTypeRepository.java @@ -15,35 +15,71 @@ */ package org.thingsboard.server.dao.sql.widget; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import org.thingsboard.server.dao.ExportableEntityRepository; import org.thingsboard.server.dao.model.sql.WidgetTypeDetailsEntity; import org.thingsboard.server.dao.model.sql.WidgetTypeEntity; -import org.thingsboard.server.dao.model.sql.WidgetTypeInfoEntity; +import org.thingsboard.server.dao.model.sql.WidgetTypeIdFqnEntity; import java.util.List; import java.util.UUID; -public interface WidgetTypeRepository extends JpaRepository { +public interface WidgetTypeRepository extends JpaRepository, ExportableEntityRepository { @Query("SELECT wt FROM WidgetTypeEntity wt WHERE wt.id = :widgetTypeId") WidgetTypeEntity findWidgetTypeById(@Param("widgetTypeId") UUID widgetTypeId); - @Query("SELECT wt FROM WidgetTypeEntity wt WHERE wt.tenantId = :tenantId AND wt.bundleAlias = :bundleAlias") - List findWidgetTypesByTenantIdAndBundleAlias(@Param("tenantId") UUID tenantId, - @Param("bundleAlias") String bundleAlias); + boolean existsByTenantIdAndId(UUID tenantId, UUID id); - @Query("SELECT new org.thingsboard.server.dao.model.sql.WidgetTypeInfoEntity(wtd) FROM WidgetTypeDetailsEntity wtd " + - "WHERE wtd.tenantId = :tenantId AND wtd.bundleAlias = :bundleAlias") - List findWidgetTypesInfosByTenantIdAndBundleAlias(@Param("tenantId") UUID tenantId, - @Param("bundleAlias") String bundleAlias); + @Query("SELECT wtd FROM WidgetTypeDetailsEntity wtd WHERE wtd.tenantId = :tenantId " + + "AND (:textSearch IS NULL OR ilike(wtd.name, CONCAT('%', :textSearch, '%')) = true)") + Page findTenantWidgetTypeDetailsByTenantId(@Param("tenantId") UUID tenantId, + @Param("textSearch") String textSearch, + Pageable pageable); - List findByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias); + @Query("SELECT wt FROM WidgetTypeEntity wt, WidgetsBundleWidgetEntity wbw " + + "WHERE wbw.widgetsBundleId = :widgetsBundleId " + + "AND wbw.widgetTypeId = wt.id ORDER BY wbw.widgetTypeOrder") + List findWidgetTypesByWidgetsBundleId(@Param("widgetsBundleId") UUID widgetsBundleId); + + @Query("SELECT wtd FROM WidgetTypeDetailsEntity wtd, WidgetsBundleWidgetEntity wbw " + + "WHERE wbw.widgetsBundleId = :widgetsBundleId " + + "AND wbw.widgetTypeId = wtd.id ORDER BY wbw.widgetTypeOrder") + List findWidgetTypesDetailsByWidgetsBundleId(@Param("widgetsBundleId") UUID widgetsBundleId); + + + @Query("SELECT wtd.fqn FROM WidgetTypeDetailsEntity wtd, WidgetsBundleWidgetEntity wbw " + + "WHERE wbw.widgetsBundleId = :widgetsBundleId " + + "AND wbw.widgetTypeId = wtd.id ORDER BY wbw.widgetTypeOrder") + List findWidgetFqnsByWidgetsBundleId(@Param("widgetsBundleId") UUID widgetsBundleId); + + @Query("SELECT new org.thingsboard.server.dao.model.sql.WidgetTypeIdFqnEntity(wtd.id, wtd.fqn) FROM WidgetTypeDetailsEntity wtd " + + "WHERE wtd.tenantId = :tenantId " + + "AND wtd.fqn IN (:widgetFqns)") + List findWidgetTypeIdsByTenantIdAndFqns(@Param("tenantId") UUID tenantId, @Param("widgetFqns") List widgetFqns); @Query("SELECT wt FROM WidgetTypeEntity wt " + - "WHERE wt.tenantId = :tenantId AND wt.bundleAlias = :bundleAlias AND wt.alias = :alias") - WidgetTypeEntity findWidgetTypeByTenantIdAndBundleAliasAndAlias(@Param("tenantId") UUID tenantId, - @Param("bundleAlias") String bundleAlias, - @Param("alias") String alias); + "WHERE wt.tenantId = :tenantId AND wt.fqn = :fqn") + WidgetTypeEntity findWidgetTypeByTenantIdAndFqn(@Param("tenantId") UUID tenantId, + @Param("fqn") String fqn); + + @Query(value = "SELECT * FROM widget_type wt " + + "WHERE wt.tenant_id = :tenantId AND cast(wt.descriptor as json) ->> 'resources' LIKE LOWER(CONCAT('%', :resourceId, '%'))", + nativeQuery = true) + List findWidgetTypesInfosByTenantIdAndResourceId(@Param("tenantId") UUID tenantId, + @Param("resourceId") UUID resourceId); + + @Query("SELECT externalId FROM WidgetTypeDetailsEntity WHERE id = :id") + UUID getExternalIdById(@Param("id") UUID id); + + @Query("SELECT w.id FROM WidgetTypeDetailsEntity w") + Page findAllIds(Pageable pageable); + + @Query("SELECT w.id FROM WidgetTypeDetailsEntity w WHERE w.tenantId = :tenantId") + Page findIdsByTenantId(@Param("tenantId") UUID tenantId, Pageable pageable); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleRepository.java index 7a6ba68a36..64440fb1f4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleRepository.java @@ -21,8 +21,10 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.thingsboard.server.dao.ExportableEntityRepository; +import org.thingsboard.server.dao.model.sql.WidgetTypeInfoEntity; import org.thingsboard.server.dao.model.sql.WidgetsBundleEntity; +import java.util.List; import java.util.UUID; /** @@ -33,27 +35,108 @@ public interface WidgetsBundleRepository extends JpaRepository findSystemWidgetsBundles(@Param("systemTenantId") UUID systemTenantId, - @Param("searchText") String searchText, + @Param("textSearch") String textSearch, Pageable pageable); + @Query(nativeQuery = true, + value = "SELECT * FROM widgets_bundle wb WHERE wb.tenant_id = :systemTenantId " + + "AND (:textSearch IS NULL OR wb.title ILIKE CONCAT('%', :textSearch, '%') " + + "OR wb.description ILIKE CONCAT('%', :textSearch, '%') " + + "OR wb.id in (SELECT wbw.widgets_bundle_id FROM widgets_bundle_widget wbw, widget_type wtd " + + "WHERE wtd.id = wbw.widget_type_id " + + "AND (:textSearch IS NULL OR wtd.name ILIKE CONCAT('%', :textSearch, '%') " + + "OR wtd.description ILIKE CONCAT('%', :textSearch, '%') " + + "OR EXISTS (" + + "SELECT 1 " + + "FROM unnest(wtd.tags) AS currentTag " + + "WHERE :textSearch ILIKE '%' || currentTag || '%' " + + "AND (length(:textSearch) = length(currentTag) " + + "OR :textSearch ILIKE currentTag || ' %' " + + "OR :textSearch ILIKE '% ' || currentTag " + + "OR :textSearch ILIKE '% ' || currentTag || ' %')" + + "))))", + countQuery = "SELECT count(*) FROM widgets_bundle wb WHERE wb.tenant_id = :systemTenantId " + + "AND (:textSearch IS NULL OR wb.title ILIKE CONCAT('%', :textSearch, '%') " + + "OR wb.description ILIKE CONCAT('%', :textSearch, '%') " + + "OR wb.id in (SELECT wbw.widgets_bundle_id FROM widgets_bundle_widget wbw, widget_type wtd " + + "WHERE wtd.id = wbw.widget_type_id " + + "AND (:textSearch IS NULL OR wtd.name ILIKE CONCAT('%', :textSearch, '%') " + + "OR wtd.description ILIKE CONCAT('%', :textSearch, '%') " + + "OR EXISTS (" + + "SELECT 1 " + + "FROM unnest(wtd.tags) AS currentTag " + + "WHERE :textSearch ILIKE '%' || currentTag || '%' " + + "AND (length(:textSearch) = length(currentTag) " + + "OR :textSearch ILIKE currentTag || ' %' " + + "OR :textSearch ILIKE '% ' || currentTag " + + "OR :textSearch ILIKE '% ' || currentTag || ' %')" + + "))))" + ) + Page findSystemWidgetsBundlesFullSearch(@Param("systemTenantId") UUID systemTenantId, + @Param("textSearch") String textSearch, + Pageable pageable); + @Query("SELECT wb FROM WidgetsBundleEntity wb WHERE wb.tenantId = :tenantId " + - "AND LOWER(wb.title) LIKE LOWER(CONCAT('%', :textSearch, '%'))") + "AND (:textSearch IS NULL OR ilike(wb.title, CONCAT('%', :textSearch, '%')) = true)") Page findTenantWidgetsBundlesByTenantId(@Param("tenantId") UUID tenantId, @Param("textSearch") String textSearch, Pageable pageable); - @Query("SELECT wb FROM WidgetsBundleEntity wb WHERE wb.tenantId IN (:tenantId, :nullTenantId) " + - "AND LOWER(wb.title) LIKE LOWER(CONCAT('%', :textSearch, '%'))") - Page findAllTenantWidgetsBundlesByTenantId(@Param("tenantId") UUID tenantId, - @Param("nullTenantId") UUID nullTenantId, + @Query("SELECT wb FROM WidgetsBundleEntity wb WHERE wb.tenantId IN (:tenantIds) " + + "AND (:textSearch IS NULL OR ilike(wb.title, CONCAT('%', :textSearch, '%')) = true)") + Page findAllTenantWidgetsBundlesByTenantIds(@Param("tenantIds") List tenantIds, @Param("textSearch") String textSearch, Pageable pageable); + @Query(nativeQuery = true, + value = "SELECT * FROM widgets_bundle wb WHERE wb.tenant_id IN (:tenantIds) " + + "AND (:textSearch IS NULL OR wb.title ILIKE CONCAT('%', :textSearch, '%') " + + "OR wb.description ILIKE CONCAT('%', :textSearch, '%') " + + "OR wb.id in (SELECT wbw.widgets_bundle_id FROM widgets_bundle_widget wbw, widget_type wtd " + + "WHERE wtd.id = wbw.widget_type_id " + + "AND (:textSearch IS NULL OR wtd.name ILIKE CONCAT('%', :textSearch, '%') " + + "OR wtd.description ILIKE CONCAT('%', :textSearch, '%') " + + "OR EXISTS (" + + "SELECT 1 " + + "FROM unnest(wtd.tags) AS currentTag " + + "WHERE :textSearch ILIKE '%' || currentTag || '%' " + + "AND (length(:textSearch) = length(currentTag) " + + "OR :textSearch ILIKE currentTag || ' %' " + + "OR :textSearch ILIKE '% ' || currentTag " + + "OR :textSearch ILIKE '% ' || currentTag || ' %')" + + ")))) " + + "ORDER BY wb.widgets_bundle_order ASC NULLS LAST", + countQuery = "SELECT count(*) FROM widgets_bundle wb WHERE wb.tenant_id IN (:tenantIds) " + + "AND (:textSearch IS NULL OR wb.title ILIKE CONCAT('%', :textSearch, '%') " + + "OR wb.description ILIKE CONCAT('%', :textSearch, '%') " + + "OR wb.id in (SELECT wbw.widgets_bundle_id FROM widgets_bundle_widget wbw, widget_type wtd " + + "WHERE wtd.id = wbw.widget_type_id " + + "AND (:textSearch IS NULL OR wtd.name ILIKE CONCAT('%', :textSearch, '%') " + + "OR wtd.description ILIKE CONCAT('%', :textSearch, '%') " + + "OR EXISTS (" + + "SELECT 1 " + + "FROM unnest(wtd.tags) AS currentTag " + + "WHERE :textSearch ILIKE '%' || currentTag || '%' " + + "AND (length(:textSearch) = length(currentTag) " + + "OR :textSearch ILIKE currentTag || ' %' " + + "OR :textSearch ILIKE '% ' || currentTag " + + "OR :textSearch ILIKE '% ' || currentTag || ' %')" + + "))))" + ) + Page findAllTenantWidgetsBundlesByTenantIdsFullSearch(@Param("tenantIds") List tenantIds, + @Param("textSearch") String textSearch, + Pageable pageable); + WidgetsBundleEntity findFirstByTenantIdAndTitle(UUID tenantId, String title); @Query("SELECT externalId FROM WidgetsBundleEntity WHERE id = :id") UUID getExternalIdById(@Param("id") UUID id); + @Query(nativeQuery = true, value = "SELECT * FROM widgets_bundle wb WHERE wb.tenant_id = :tenantId and wb.image = :imageLink limit :lmt") + List findByTenantAndImageUrl(@Param("tenantId") UUID tenantId, @Param("imageLink") String imageLink, @Param("lmt") int lmt); + + @Query(nativeQuery = true, value = "SELECT * FROM widgets_bundle wb WHERE wb.image = :imageLink limit :lmt") + List findByImageUrl(@Param("imageLink") String imageLink, @Param("lmt") int lmt); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleWidgetRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleWidgetRepository.java new file mode 100644 index 0000000000..d064fca88e --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleWidgetRepository.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.widget; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.thingsboard.server.dao.model.sql.WidgetsBundleWidgetCompositeKey; +import org.thingsboard.server.dao.model.sql.WidgetsBundleWidgetEntity; + +import java.util.List; +import java.util.UUID; + +public interface WidgetsBundleWidgetRepository extends JpaRepository { + + List findAllByWidgetsBundleId(UUID widgetsBundleId); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/BaseAbstractSqlTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/BaseAbstractSqlTimeseriesDao.java index c4c3e021c8..43c1a3feea 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/BaseAbstractSqlTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/BaseAbstractSqlTimeseriesDao.java @@ -53,11 +53,15 @@ public abstract class BaseAbstractSqlTimeseriesDao extends JpaAbstractDaoListeni if (keyId == null) { Optional tsKvDictionaryOptional; tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey)); - if (!tsKvDictionaryOptional.isPresent()) { + if (tsKvDictionaryOptional.isEmpty()) { tsCreationLock.lock(); try { + keyId = tsKvDictionaryMap.get(strKey); + if (keyId != null) { + return keyId; + } tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey)); - if (!tsKvDictionaryOptional.isPresent()) { + if (tsKvDictionaryOptional.isEmpty()) { TsKvDictionary tsKvDictionary = new TsKvDictionary(); tsKvDictionary.setKey(strKey); try { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/SqlTimeseriesLatestDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/SqlTimeseriesLatestDao.java index b58f0dc7c7..48b5b7b19a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/SqlTimeseriesLatestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/SqlTimeseriesLatestDao.java @@ -156,11 +156,12 @@ public class SqlTimeseriesLatestDao extends BaseAbstractSqlTimeseriesDao impleme @Override public ListenableFuture findLatest(TenantId tenantId, EntityId entityId, String key) { - TsKvEntry latest = doFindLatest(entityId, key); - if (latest == null) { - latest = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(key, null)); - } - return Futures.immediateFuture(latest); + return Futures.immediateFuture(getLatestTsKvEntry(entityId, key)); + } + + @Override + public TsKvEntry findLatestSync(TenantId tenantId, EntityId entityId, String key) { + return getLatestTsKvEntry(entityId, key); } @Override @@ -268,4 +269,12 @@ public class SqlTimeseriesLatestDao extends BaseAbstractSqlTimeseriesDao impleme return tsLatestQueue.add(latestEntity); } + private TsKvEntry getLatestTsKvEntry(EntityId entityId, String key) { + TsKvEntry latest = doFindLatest(entityId, key); + if (latest == null) { + latest = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(key, null)); + } + return latest; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/sql/SqlPartitioningRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/sql/SqlPartitioningRepository.java index 60614e8c90..e317e6d7c0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/sql/SqlPartitioningRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/sql/SqlPartitioningRepository.java @@ -140,7 +140,7 @@ public class SqlPartitioningRepository { try { partitions.add(Long.parseLong(partitionTsStr)); } catch (NumberFormatException nfe) { - log.warn("Failed to parse table name: {}", partitionTableName); + log.debug("Failed to parse table name: {}", partitionTableName); } } return partitions; diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java index 4262aca2e4..3ff25972d2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantProfileServiceImpl.java @@ -32,6 +32,8 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.dao.entity.AbstractCachedEntityService; +import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; +import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -92,6 +94,8 @@ public class TenantProfileServiceImpl extends AbstractCachedEntityService findLatestSync(TenantId tenantId, EntityId entityId, Collection keys) { + validate(entityId); + List latestEntries = Lists.newArrayListWithExpectedSize(keys.size()); + keys.forEach(key -> Validator.validateString(key, "Incorrect key " + key)); + keys.forEach(key -> latestEntries.add(timeseriesLatestDao.findLatestSync(tenantId, entityId, key))); + return latestEntries; + } + @Override public ListenableFuture> findAllLatest(TenantId tenantId, EntityId entityId) { validate(entityId); @@ -266,7 +275,9 @@ public class BaseTimeseriesService implements TimeseriesService { private void deleteAndRegisterFutures(TenantId tenantId, List> futures, EntityId entityId, DeleteTsKvQuery query) { futures.add(Futures.transform(timeseriesDao.remove(tenantId, entityId, query), v -> null, MoreExecutors.directExecutor())); - futures.add(timeseriesLatestDao.removeLatest(tenantId, entityId, query)); + if (query.getDeleteLatest()) { + futures.add(timeseriesLatestDao.removeLatest(tenantId, entityId, query)); + } } private static void validate(EntityId entityId) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesLatestDao.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesLatestDao.java index bab6ea8736..605e0bb428 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesLatestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesLatestDao.java @@ -44,6 +44,7 @@ import org.thingsboard.server.dao.util.NoSqlTsLatestDao; import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.concurrent.ExecutionException; import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.literal; @@ -69,6 +70,16 @@ public class CassandraBaseTimeseriesLatestDao extends AbstractCassandraBaseTimes return findLatest(tenantId, entityId, key, rs -> convertResultToTsKvEntry(key, rs.one())); } + @Override + public TsKvEntry findLatestSync(TenantId tenantId, EntityId entityId, String key) { + try { + return findLatest(tenantId, entityId, key, rs -> convertResultToTsKvEntry(key, rs.one())).get(); + } catch (InterruptedException | ExecutionException e) { + log.error("[{}][{}] Failed to get latest entry for key: {} due to: ", tenantId, entityId, key, e); + throw new RuntimeException(e); + } + } + private ListenableFuture findLatest(TenantId tenantId, EntityId entityId, String key, java.util.function.Function function) { BoundStatementBuilder stmtBuilder = new BoundStatementBuilder(getFindLatestStmt().bind()); stmtBuilder.setString(0, entityId.getEntityType().name()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesLatestDao.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesLatestDao.java index c55abeed7e..4c46d67a46 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesLatestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesLatestDao.java @@ -40,6 +40,8 @@ public interface TimeseriesLatestDao { */ ListenableFuture findLatest(TenantId tenantId, EntityId entityId, String key); + TsKvEntry findLatestSync(TenantId tenantId, EntityId entityId, String key); + ListenableFuture> findAllLatest(TenantId tenantId, EntityId entityId); ListenableFuture saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry); diff --git a/dao/src/main/java/org/thingsboard/server/dao/usage/BasicUsageInfoService.java b/dao/src/main/java/org/thingsboard/server/dao/usage/BasicUsageInfoService.java index fcd75b25f7..7f4e065bd9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/usage/BasicUsageInfoService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/usage/BasicUsageInfoService.java @@ -67,6 +67,7 @@ public class BasicUsageInfoService implements UsageInfoService { usageInfo.setMaxAlarms(profileConfiguration.getMaxCreatedAlarms()); usageInfo.setMaxTransportMessages(profileConfiguration.getMaxTransportMessages()); usageInfo.setMaxJsExecutions(profileConfiguration.getMaxJSExecutions()); + usageInfo.setMaxTbelExecutions(profileConfiguration.getMaxTbelExecutions()); usageInfo.setMaxEmails(profileConfiguration.getMaxEmails()); usageInfo.setMaxSms(profileConfiguration.getMaxSms()); usageInfo.setSmsEnabled(profileConfiguration.getSmsEnabled()); @@ -75,6 +76,7 @@ public class BasicUsageInfoService implements UsageInfoService { Collection keys = Arrays.asList( ApiUsageRecordKey.TRANSPORT_MSG_COUNT.getApiCountKey(), ApiUsageRecordKey.JS_EXEC_COUNT.getApiCountKey(), + ApiUsageRecordKey.TBEL_EXEC_COUNT.getApiCountKey(), ApiUsageRecordKey.EMAIL_EXEC_COUNT.getApiCountKey(), ApiUsageRecordKey.SMS_EXEC_COUNT.getApiCountKey(), ApiUsageRecordKey.CREATED_ALARMS_COUNT.getApiCountKey()); @@ -82,6 +84,7 @@ public class BasicUsageInfoService implements UsageInfoService { List entries = tsService.findLatest(tenantId, apiUsageState.getId(), keys).get(); usageInfo.setTransportMessages(getLongValueFromTsEntries(entries, ApiUsageRecordKey.TRANSPORT_MSG_COUNT.getApiCountKey())); usageInfo.setJsExecutions(getLongValueFromTsEntries(entries, ApiUsageRecordKey.JS_EXEC_COUNT.getApiCountKey())); + usageInfo.setTbelExecutions(getLongValueFromTsEntries(entries, ApiUsageRecordKey.TBEL_EXEC_COUNT.getApiCountKey())); usageInfo.setEmails(getLongValueFromTsEntries(entries, ApiUsageRecordKey.EMAIL_EXEC_COUNT.getApiCountKey())); usageInfo.setSms(getLongValueFromTsEntries(entries, ApiUsageRecordKey.SMS_EXEC_COUNT.getApiCountKey())); usageInfo.setAlarms(getLongValueFromTsEntries(entries, ApiUsageRecordKey.CREATED_ALARMS_COUNT.getApiCountKey())); diff --git a/dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java index ba49cc964c..25d3c97c42 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/usagerecord/ApiUsageStateServiceImpl.java @@ -93,6 +93,7 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A apiUsageState.setTransportState(ApiUsageStateValue.ENABLED); apiUsageState.setReExecState(ApiUsageStateValue.ENABLED); apiUsageState.setJsExecState(ApiUsageStateValue.ENABLED); + apiUsageState.setTbelExecState(ApiUsageStateValue.ENABLED); apiUsageState.setDbStorageState(ApiUsageStateValue.ENABLED); apiUsageState.setSmsExecState(ApiUsageStateValue.ENABLED); apiUsageState.setEmailExecState(ApiUsageStateValue.ENABLED); @@ -110,6 +111,8 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A new StringDataEntry(ApiFeature.RE.getApiStateKey(), ApiUsageStateValue.ENABLED.name()))); apiUsageStates.add(new BasicTsKvEntry(saved.getCreatedTime(), new StringDataEntry(ApiFeature.JS.getApiStateKey(), ApiUsageStateValue.ENABLED.name()))); + apiUsageStates.add(new BasicTsKvEntry(saved.getCreatedTime(), + new StringDataEntry(ApiFeature.TBEL.getApiStateKey(), ApiUsageStateValue.ENABLED.name()))); apiUsageStates.add(new BasicTsKvEntry(saved.getCreatedTime(), new StringDataEntry(ApiFeature.EMAIL.getApiStateKey(), ApiUsageStateValue.ENABLED.name()))); apiUsageStates.add(new BasicTsKvEntry(saved.getCreatedTime(), diff --git a/dao/src/main/java/org/thingsboard/server/dao/usagerecord/DefaultApiLimitService.java b/dao/src/main/java/org/thingsboard/server/dao/usagerecord/DefaultApiLimitService.java index d5d19aa453..56bb096b5c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/usagerecord/DefaultApiLimitService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/usagerecord/DefaultApiLimitService.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.usagerecord; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -27,6 +28,8 @@ import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileCon import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import java.util.function.Function; + @Service @RequiredArgsConstructor public class DefaultApiLimitService implements ApiLimitService { @@ -36,16 +39,31 @@ public class DefaultApiLimitService implements ApiLimitService { @Override public boolean checkEntitiesLimit(TenantId tenantId, EntityType entityType) { - DefaultTenantProfileConfiguration profileConfiguration = tenantProfileCache.get(tenantId).getDefaultProfileConfiguration(); - long limit = profileConfiguration.getEntitiesLimit(entityType); - if (limit > 0) { - EntityTypeFilter filter = new EntityTypeFilter(); - filter.setEntityType(entityType); - long currentCount = entityService.countEntitiesByQuery(tenantId, new CustomerId(EntityId.NULL_UUID), new EntityCountQuery(filter)); - return currentCount < limit; - } else { + long limit = getLimit(tenantId, profileConfiguration -> profileConfiguration.getEntitiesLimit(entityType)); + if (limit <= 0) { return true; } + + EntityTypeFilter filter = new EntityTypeFilter(); + filter.setEntityType(entityType); + long currentCount = entityService.countEntitiesByQuery(tenantId, new CustomerId(EntityId.NULL_UUID), new EntityCountQuery(filter)); + return currentCount < limit; + } + + @Override + public long getLimit(TenantId tenantId, Function extractor) { + if (tenantId == null || tenantId.isSysTenantId()) { + return 0L; + } + TenantProfile tenantProfile = tenantProfileCache.get(tenantId); + if (tenantProfile == null) { + throw new IllegalArgumentException("Tenant profile not found for tenant " + tenantId); + } + Number value = extractor.apply(tenantProfile.getDefaultProfileConfiguration()); + if (value == null) { + return 0L; + } + return Math.max(0, value.longValue()); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserCredentialsDao.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserCredentialsDao.java index c3f19f2590..b40121a718 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserCredentialsDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserCredentialsDao.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao.user; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.dao.Dao; @@ -58,4 +59,6 @@ public interface UserCredentialsDao extends Dao { */ UserCredentials findByResetToken(TenantId tenantId, String resetToken); + void removeByUserId(TenantId tenantId, UserId userId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index 5dc32b8eed..a584fef86c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -30,6 +30,7 @@ import org.springframework.transaction.annotation.Transactional; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; @@ -44,6 +45,9 @@ import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.security.event.UserCredentialsInvalidationEvent; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entity.EntityCountService; +import org.thingsboard.server.dao.eventsourcing.ActionEntityEvent; +import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; +import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -51,6 +55,7 @@ import org.thingsboard.server.dao.service.PaginatedRemover; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import static org.thingsboard.server.common.data.StringUtils.generateSafeToken; @@ -65,8 +70,8 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic public static final String USER_PASSWORD_HISTORY = "userPasswordHistory"; - private static final String LAST_LOGIN_TS = "lastLoginTs"; - private static final String FAILED_LOGIN_ATTEMPTS = "failedLoginAttempts"; + public static final String LAST_LOGIN_TS = "lastLoginTs"; + public static final String FAILED_LOGIN_ATTEMPTS = "failedLoginAttempts"; private static final int DEFAULT_TOKEN_LENGTH = 30; public static final String INCORRECT_USER_ID = "Incorrect userId "; @@ -119,9 +124,9 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic } @Override - public User saveUser(User user) { + public User saveUser(TenantId tenantId, User user) { log.trace("Executing saveUser [{}]", user); - userValidator.validate(user, User::getTenantId); + User oldUser = userValidator.validate(user, User::getTenantId); if (!userLoginCaseSensitive) { user.setEmail(user.getEmail().toLowerCase()); } @@ -135,6 +140,12 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic userCredentials.setAdditionalInfo(JacksonUtil.newObjectNode()); userCredentialsDao.save(user.getTenantId(), userCredentials); } + eventPublisher.publishEvent(SaveEntityEvent.builder() + .tenantId(tenantId == null ? TenantId.SYS_TENANT_ID : tenantId) + .entity(savedUser) + .oldEntity(oldUser) + .entityId(savedUser.getId()) + .added(user.getId() == null).build()); return savedUser; } @@ -163,7 +174,12 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic public UserCredentials saveUserCredentials(TenantId tenantId, UserCredentials userCredentials) { log.trace("Executing saveUserCredentials [{}]", userCredentials); userCredentialsValidator.validate(userCredentials, data -> tenantId); - return userCredentialsDao.save(tenantId, userCredentials); + UserCredentials result = userCredentialsDao.save(tenantId, userCredentials); + eventPublisher.publishEvent(ActionEntityEvent.builder() + .tenantId(tenantId) + .entityId(userCredentials.getUserId()) + .actionType(ActionType.CREDENTIALS_UPDATED).build()); + return result; } @Override @@ -222,21 +238,32 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic if (userCredentials.getPassword() != null) { updatePasswordHistory(userCredentials); } - return userCredentialsDao.save(tenantId, userCredentials); + UserCredentials result = userCredentialsDao.save(tenantId, userCredentials); + eventPublisher.publishEvent(ActionEntityEvent.builder() + .tenantId(tenantId) + .entityId(userCredentials.getUserId()) + .actionType(ActionType.CREDENTIALS_UPDATED).build()); + return result; } @Override @Transactional - public void deleteUser(TenantId tenantId, UserId userId) { - log.trace("Executing deleteUser [{}]", userId); + public void deleteUser(TenantId tenantId, User user) { + Objects.requireNonNull(user, "User is null"); + UserId userId = user.getId(); + log.trace("[{}] Executing deleteUser [{}]", tenantId, userId); validateId(userId, INCORRECT_USER_ID + userId); - UserCredentials userCredentials = userCredentialsDao.findByUserId(tenantId, userId.getId()); - userCredentialsDao.removeById(tenantId, userCredentials.getUuidId()); + userCredentialsDao.removeByUserId(tenantId, userId); userAuthSettingsDao.removeByUserId(userId); deleteEntityRelations(tenantId, userId); + userDao.removeById(tenantId, userId.getId()); eventPublisher.publishEvent(new UserCredentialsInvalidationEvent(userId)); countService.publishCountEntityEvictEvent(tenantId, EntityType.USER); + eventPublisher.publishEvent(DeleteEntityEvent.builder() + .tenantId(tenantId) + .entityId(userId) + .entity(user).build()); } @Override @@ -340,7 +367,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic log.trace("Executing onUserLoginSuccessful [{}]", userId); User user = findUserById(tenantId, userId); resetFailedLoginAttempts(user); - saveUser(user); + saveUser(tenantId, user); } private void resetFailedLoginAttempts(User user) { @@ -361,7 +388,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic } ((ObjectNode) additionalInfo).put(LAST_LOGIN_TS, System.currentTimeMillis()); user.setAdditionalInfo(additionalInfo); - saveUser(user); + saveUser(tenantId, user); } @Override @@ -369,7 +396,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic log.trace("Executing onUserLoginIncorrectCredentials [{}]", userId); User user = findUserById(tenantId, userId); int failedLoginAttempts = increaseFailedLoginAttempts(user); - saveUser(user); + saveUser(tenantId, user); return failedLoginAttempts; } @@ -420,8 +447,8 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic } @Override - protected void removeEntity(TenantId tenantId, User entity) { - deleteUser(tenantId, new UserId(entity.getUuidId())); + protected void removeEntity(TenantId tenantId, User user) { + deleteUser(tenantId, user); } }; @@ -434,7 +461,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Override protected void removeEntity(TenantId tenantId, User entity) { - deleteUser(tenantId, new UserId(entity.getUuidId())); + deleteUser(tenantId, entity); } }; diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java b/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java index 64b4eb3666..d6c3a8efdd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java @@ -37,7 +37,7 @@ import org.thingsboard.server.common.stats.StatsFactory; import org.thingsboard.server.common.stats.StatsType; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.nosql.CassandraStatementTask; -import org.thingsboard.server.dao.util.limits.LimitedApi; +import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.dao.util.limits.RateLimitService; import javax.annotation.Nullable; @@ -121,7 +121,7 @@ public abstract class AbstractBufferedRateExecutor taskCtx = null; @@ -169,7 +169,7 @@ public abstract class AbstractBufferedRateExecutor= printQueriesFreq) { printQueriesIdx.set(0); String query = queryToString(finalTaskCtx); - log.info("[{}] Cassandra query: {}", taskCtx.getId(), query); + log.info("[{}][{}] Cassandra query: {}", getBufferName(), taskCtx.getId(), query); } } logTask("Processing", finalTaskCtx); @@ -222,7 +222,7 @@ public abstract class AbstractBufferedRateExecutor taskCtx) { @@ -298,7 +298,7 @@ public abstract class AbstractBufferedRateExecutor mediaTypeMappings = Map.of( + "jpeg", "jpg", + "svg+xml", "svg", + "x-icon", "ico" + ); + + public static String mediaTypeToFileExtension(String mimeType) { + String subtype = MimeTypeUtils.parseMimeType(mimeType).getSubtype(); + return mediaTypeMappings.getOrDefault(subtype, subtype); + } + + public static String fileExtensionToMediaType(String extension) { + String subtype = mediaTypeMappings.entrySet().stream() + .filter(mapping -> mapping.getValue().equals(extension)) + .map(Map.Entry::getKey).findFirst().orElse(extension); + return new MimeType("image", subtype).toString(); + } + + public static ProcessedImage processImage(byte[] data, String mediaType, int thumbnailMaxDimension) throws Exception { + if (mediaTypeToFileExtension(mediaType).equals("svg")) { + try { + return processSvgImage(data, mediaType, thumbnailMaxDimension); + } catch (Exception e) { + if (log.isDebugEnabled()) { // printing stacktrace + log.warn("Couldn't process SVG image, leaving preview as original image", e); + } else { + log.warn("Couldn't process SVG image, leaving preview as original image: {}", ExceptionUtils.getMessage(e)); + } + return previewAsOriginalImage(data, mediaType); + } + } + BufferedImage bufferedImage = null; + try { + bufferedImage = ImageIO.read(new ByteArrayInputStream(data)); + } catch (Exception ignored) { + } + if (bufferedImage == null) { // means that media type is not supported by ImageIO; extracting width and height from metadata and leaving preview as original image + Metadata metadata = ImageMetadataReader.readMetadata(new ByteArrayInputStream(data)); + ProcessedImage image = previewAsOriginalImage(data, mediaType); + String dirName = "Unknown"; + for (Directory dir : metadata.getDirectories()) { + Tag widthTag = dir.getTags().stream() + .filter(tag -> tag.getTagName().toLowerCase().contains("width")) + .findFirst().orElse(null); + Tag heightTag = dir.getTags().stream() + .filter(tag -> tag.getTagName().toLowerCase().contains("height")) + .findFirst().orElse(null); + if (widthTag == null || heightTag == null) { + continue; + } + int width = Integer.parseInt(dir.getObject(widthTag.getTagType()).toString()); + int height = Integer.parseInt(dir.getObject(heightTag.getTagType()).toString()); + image.setWidth(width); + image.setHeight(height); + image.getPreview().setWidth(width); + image.getPreview().setHeight(height); + dirName = dir.getName(); + break; + } + log.warn("Couldn't process {} ({}) with ImageIO, leaving preview as original image", mediaType, dirName); + return image; + } + + ProcessedImage image = new ProcessedImage(); + image.setMediaType(mediaType); + image.setData(data); + image.setSize(data.length); + image.setWidth(bufferedImage.getWidth()); + image.setHeight(bufferedImage.getHeight()); + + ProcessedImage preview = new ProcessedImage(); + int[] thumbnailDimensions = getThumbnailDimensions(image.getWidth(), image.getHeight(), thumbnailMaxDimension, true); + preview.setWidth(thumbnailDimensions[0]); + preview.setHeight(thumbnailDimensions[1]); + + if (preview.getWidth() == image.getWidth() && preview.getHeight() == image.getHeight()) { + if (mediaType.equals("image/png")) { + preview.setMediaType(mediaType); + preview.setData(null); + preview.setSize(data.length); + image.setPreview(preview); + return image; + } + } + + BufferedImage thumbnail = new BufferedImage(preview.getWidth(), preview.getHeight(), BufferedImage.TYPE_INT_ARGB); + thumbnail.getGraphics().drawImage(bufferedImage, 0, 0, preview.getWidth(), preview.getHeight(), null); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ImageIO.write(thumbnail, "png", out); + + preview.setMediaType("image/png"); + preview.setData(out.toByteArray()); + preview.setSize(preview.getData().length); + image.setPreview(preview); + return image; + } + + public static ProcessedImage processSvgImage(byte[] data, String mediaType, int thumbnailMaxDimension) throws Exception { + SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName()); + Document document = factory.createDocument(null, new ByteArrayInputStream(data)); + Integer width = null; + Integer height = null; + String strWidth = document.getDocumentElement().getAttribute("width"); + String strHeight = document.getDocumentElement().getAttribute("height"); + if (StringUtils.isNotEmpty(strWidth) && StringUtils.isNotEmpty(strHeight)) { + try { + width = (int) Double.parseDouble(strWidth); + height = (int) Double.parseDouble(strHeight); + } catch (NumberFormatException ignored) {} // in case width and height are in %, mm, etc. + } + if (width == null || height == null) { + String viewBox = document.getDocumentElement().getAttribute("viewBox"); + if (StringUtils.isNotEmpty(viewBox)) { + String[] viewBoxValues = viewBox.split(" "); + if (viewBoxValues.length > 3) { + width = (int) Double.parseDouble(viewBoxValues[2]); + height = (int) Double.parseDouble(viewBoxValues[3]); + } + } + } + if (width == null) { + UserAgent agent = new UserAgentAdapter(); + DocumentLoader loader = new DocumentLoader(agent); + BridgeContext context = new BridgeContext(agent, loader); + context.setDynamic(true); + GVTBuilder builder = new GVTBuilder(); + GraphicsNode root = builder.build(context, document); + var bounds = root.getPrimitiveBounds(); + if (bounds != null) { + width = (int) bounds.getWidth(); + height = (int) bounds.getHeight(); + } + } + ProcessedImage image = new ProcessedImage(); + image.setMediaType(mediaType); + image.setWidth(width == null ? 0 : width); + image.setHeight(height == null ? 0 : height); + image.setData(data); + image.setSize(data.length); + + PNGTranscoder transcoder = new PNGTranscoder(); + if (image.getSize() < 10240) { // if SVG is smaller than 10kB (average 250x250 PNG preview size) + return withPreviewAsOriginalImage(image); + } else if (image.getSize() > 102400 && image.getWidth() != 0) { // considering SVG image detailed after 100kB + // increasing preview dimensions + thumbnailMaxDimension = 512; + int[] thumbnailDimensions = getThumbnailDimensions(image.getWidth(), image.getHeight(), thumbnailMaxDimension, false); + transcoder.addTranscodingHint(PNGTranscoder.KEY_WIDTH, (float) thumbnailDimensions[0]); + transcoder.addTranscodingHint(PNGTranscoder.KEY_HEIGHT, (float) thumbnailDimensions[1]); + } else { + transcoder.addTranscodingHint(PNGTranscoder.KEY_MAX_WIDTH, (float) thumbnailMaxDimension); + transcoder.addTranscodingHint(PNGTranscoder.KEY_MAX_HEIGHT, (float) thumbnailMaxDimension); + } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + transcoder.transcode(new TranscoderInput(new ByteArrayInputStream(data)), new TranscoderOutput(out)); + byte[] pngThumbnail = out.toByteArray(); + + ProcessedImage preview = new ProcessedImage(); + preview.setWidth(thumbnailMaxDimension); + preview.setHeight(thumbnailMaxDimension); + preview.setMediaType("image/png"); + preview.setData(pngThumbnail); + preview.setSize(pngThumbnail.length); + image.setPreview(preview); + return image; + } + + private static ProcessedImage previewAsOriginalImage(byte[] data, String mediaType) { + ProcessedImage image = new ProcessedImage(); + image.setMediaType(mediaType); + image.setData(data); + image.setSize(data.length); + image.setWidth(0); + image.setHeight(0); + return withPreviewAsOriginalImage(image); + } + + public static ProcessedImage withPreviewAsOriginalImage(ProcessedImage originalImage) { + originalImage.setPreview(originalImage.withData(null)); + return originalImage; + } + + private static int[] getThumbnailDimensions(int originalWidth, int originalHeight, int maxDimension, boolean originalIfSmaller) { + if (originalWidth <= maxDimension && originalHeight <= maxDimension && originalIfSmaller) { + return new int[]{originalWidth, originalHeight}; + } + int thumbnailWidth; + int thumbnailHeight; + double aspectRatio = (double) originalWidth / originalHeight; + if (originalWidth > originalHeight) { + thumbnailWidth = maxDimension; + thumbnailHeight = (int) (maxDimension / aspectRatio); + } else { + thumbnailWidth = (int) (maxDimension * aspectRatio); + thumbnailHeight = maxDimension; + } + return new int[]{thumbnailWidth, thumbnailHeight}; + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class ProcessedImage { + private String mediaType; + private int width; + private int height; + @With + private byte[] data; + private long size; + private ProcessedImage preview; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/JsonNodeProcessingTask.java b/dao/src/main/java/org/thingsboard/server/dao/util/JsonNodeProcessingTask.java new file mode 100644 index 0000000000..17454b4757 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/util/JsonNodeProcessingTask.java @@ -0,0 +1,30 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.util; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; + +@Data +public class JsonNodeProcessingTask { + private final String path; + private final JsonNode node; + + public JsonNodeProcessingTask(String path, JsonNode node) { + this.path = path; + this.node = node; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/JsonPathProcessingTask.java b/dao/src/main/java/org/thingsboard/server/dao/util/JsonPathProcessingTask.java new file mode 100644 index 0000000000..c8703b6155 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/util/JsonPathProcessingTask.java @@ -0,0 +1,70 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.util; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +import org.thingsboard.server.dao.dashboard.DashboardServiceImpl; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +@Data +public class JsonPathProcessingTask { + private final String[] tokens; + private final Map variables; + private final JsonNode node; + + public JsonPathProcessingTask(String[] tokens, Map variables, JsonNode node) { + this.tokens = tokens; + this.variables = variables; + this.node = node; + } + + public boolean isLast() { + return tokens.length == 1; + } + + public String currentToken() { + return tokens[0]; + } + + public JsonPathProcessingTask next(JsonNode next) { + return new JsonPathProcessingTask( + Arrays.copyOfRange(tokens, 1, tokens.length), + variables, + next); + } + + public JsonPathProcessingTask next(JsonNode next, String key, String value) { + Map variables = new HashMap<>(this.variables); + variables.put(key, value); + return new JsonPathProcessingTask( + Arrays.copyOfRange(tokens, 1, tokens.length), + variables, + next); + } + + @Override + public String toString() { + return "JsonPathProcessingTask{" + + "tokens=" + Arrays.toString(tokens) + + ", variables=" + variables + + ", node=" + node.toString().substring(0, 20) + + '}'; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/KvUtils.java b/dao/src/main/java/org/thingsboard/server/dao/util/KvUtils.java index 788a19228b..31bb00ebd9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/KvUtils.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/KvUtils.java @@ -15,8 +15,10 @@ */ package org.thingsboard.server.dao.util; +import com.fasterxml.jackson.databind.JsonNode; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; @@ -36,33 +38,39 @@ public class KvUtils { .maximumSize(100000).build(); } - public static void validate(List tsKvEntries) { - tsKvEntries.forEach(KvUtils::validate); + public static void validate(List tsKvEntries, boolean valueNoXssValidation) { + tsKvEntries.forEach(tsKvEntry -> validate(tsKvEntry, valueNoXssValidation)); } - public static void validate(KvEntry tsKvEntry) { + public static void validate(KvEntry tsKvEntry, boolean valueNoXssValidation) { if (tsKvEntry == null) { throw new IncorrectParameterException("Key value entry can't be null"); } String key = tsKvEntry.getKey(); - if (key == null) { - throw new DataValidationException("Key can't be null"); + if (StringUtils.isBlank(key)) { + throw new DataValidationException("Key can't be null or empty"); } if (key.length() > 255) { throw new DataValidationException("Validation error: key length must be equal or less than 255"); } - if (validatedKeys.getIfPresent(key) != null) { - return; + if (validatedKeys.getIfPresent(key) == null) { + if (!NoXssValidator.isValid(key)) { + throw new DataValidationException("Validation error: key is malformed"); + } + validatedKeys.put(key, Boolean.TRUE); } - if (!NoXssValidator.isValid(key)) { - throw new DataValidationException("Validation error: key is malformed"); + if (valueNoXssValidation) { + Object value = tsKvEntry.getValue(); + if (value instanceof CharSequence || value instanceof JsonNode) { + if (!NoXssValidator.isValid(value.toString())) { + throw new DataValidationException("Validation error: value is malformed"); + } + } } - - validatedKeys.put(key, Boolean.TRUE); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/limits/DefaultRateLimitService.java b/dao/src/main/java/org/thingsboard/server/dao/util/limits/DefaultRateLimitService.java index 918f16ef19..12201712c1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/limits/DefaultRateLimitService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/limits/DefaultRateLimitService.java @@ -20,11 +20,16 @@ import com.github.benmanes.caffeine.cache.Caffeine; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.exception.TenantProfileNotFoundException; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.limit.LimitedApi; +import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; +import org.thingsboard.server.common.data.notification.rule.trigger.RateLimitsTrigger; import org.thingsboard.server.common.msg.tools.TbRateLimits; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; @@ -35,11 +40,14 @@ import java.util.concurrent.TimeUnit; public class DefaultRateLimitService implements RateLimitService { private final TbTenantProfileCache tenantProfileCache; + private final NotificationRuleProcessor notificationRuleProcessor; public DefaultRateLimitService(TbTenantProfileCache tenantProfileCache, + @Lazy NotificationRuleProcessor notificationRuleProcessor, @Value("${cache.rateLimits.timeToLiveInMinutes:120}") int rateLimitsTtl, @Value("${cache.rateLimits.maxSize:200000}") int rateLimitsCacheMaxSize) { this.tenantProfileCache = tenantProfileCache; + this.notificationRuleProcessor = notificationRuleProcessor; this.rateLimits = Caffeine.newBuilder() .expireAfterAccess(rateLimitsTtl, TimeUnit.MINUTES) .maximumSize(rateLimitsCacheMaxSize) @@ -64,9 +72,17 @@ public class DefaultRateLimitService implements RateLimitService { } String rateLimitConfig = tenantProfile.getProfileConfiguration() - .map(profileConfiguration -> api.getLimitConfig(profileConfiguration, level)) - .orElse(null); - return checkRateLimit(api, level, rateLimitConfig); + .map(api::getLimitConfig).orElse(null); + boolean success = checkRateLimit(api, level, rateLimitConfig); + if (!success) { + notificationRuleProcessor.process(RateLimitsTrigger.builder() + .tenantId(tenantId) + .api(api) + .limitLevel(level instanceof EntityId ? (EntityId) level : tenantId) + .limitLevelEntityName(null) + .build()); + } + return success; } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/limits/LimitedApi.java b/dao/src/main/java/org/thingsboard/server/dao/util/limits/LimitedApi.java deleted file mode 100644 index ee79230d8a..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/util/limits/LimitedApi.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.dao.util.limits; - -import lombok.Getter; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; - -import java.util.function.BiFunction; -import java.util.function.Function; - -public enum LimitedApi { - - ENTITY_EXPORT(DefaultTenantProfileConfiguration::getTenantEntityExportRateLimit), - ENTITY_IMPORT(DefaultTenantProfileConfiguration::getTenantEntityImportRateLimit), - NOTIFICATION_REQUESTS(DefaultTenantProfileConfiguration::getTenantNotificationRequestsRateLimit), - NOTIFICATION_REQUESTS_PER_RULE(DefaultTenantProfileConfiguration::getTenantNotificationRequestsPerRuleRateLimit), - REST_REQUESTS((profileConfiguration, level) -> ((EntityId) level).getEntityType() == EntityType.TENANT ? - profileConfiguration.getTenantServerRestLimitsConfiguration() : - profileConfiguration.getCustomerServerRestLimitsConfiguration()), - WS_UPDATES_PER_SESSION(DefaultTenantProfileConfiguration::getWsUpdatesPerSessionRateLimit), - CASSANDRA_QUERIES(DefaultTenantProfileConfiguration::getCassandraQueryTenantRateLimitsConfiguration), - PASSWORD_RESET(true), - TWO_FA_VERIFICATION_CODE_SEND(true), - TWO_FA_VERIFICATION_CODE_CHECK(true); - - private final BiFunction configExtractor; - @Getter - private final boolean refillRateLimitIntervally; - - LimitedApi(Function configExtractor) { - this((profileConfiguration, level) -> configExtractor.apply(profileConfiguration)); - } - - LimitedApi(BiFunction configExtractor) { - this.configExtractor = configExtractor; - this.refillRateLimitIntervally = false; - } - - LimitedApi(boolean refillRateLimitIntervally) { - this.configExtractor = null; - this.refillRateLimitIntervally = refillRateLimitIntervally; - } - - public String getLimitConfig(DefaultTenantProfileConfiguration profileConfiguration, Object level) { - if (configExtractor != null) { - return configExtractor.apply(profileConfiguration, level); - } else { - throw new IllegalArgumentException("No tenant profile config for " + name() + " rate limits"); - } - } - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/limits/RateLimitService.java b/dao/src/main/java/org/thingsboard/server/dao/util/limits/RateLimitService.java index c3f2fd179f..1f6e87111c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/limits/RateLimitService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/limits/RateLimitService.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao.util.limits; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.limit.LimitedApi; public interface RateLimitService { diff --git a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeDao.java b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeDao.java index c0fb8dd3f4..23e25d12ec 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeDao.java @@ -16,10 +16,17 @@ package org.thingsboard.server.dao.widget; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.WidgetTypeId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.widget.DeprecatedFilter; import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetTypeInfo; +import org.thingsboard.server.common.data.widget.WidgetsBundleWidget; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.ExportableEntityDao; +import org.thingsboard.server.dao.ImageContainerDao; import java.util.List; import java.util.UUID; @@ -27,7 +34,7 @@ import java.util.UUID; /** * The Interface WidgetTypeDao. */ -public interface WidgetTypeDao extends Dao { +public interface WidgetTypeDao extends Dao, ExportableEntityDao, ImageContainerDao { /** * Save or update widget type object @@ -46,41 +53,62 @@ public interface WidgetTypeDao extends Dao { */ WidgetType findWidgetTypeById(TenantId tenantId, UUID widgetTypeId); + boolean existsByTenantIdAndId(TenantId tenantId, UUID widgetTypeId); + + PageData findSystemWidgetTypes(TenantId tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink); + + PageData findAllTenantWidgetTypesByTenantId(UUID tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink); + + PageData findTenantWidgetTypesByTenantId(UUID tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink); + /** - * Find widget types by tenantId and bundleAlias. + * Find widget types by widgetsBundleId. * * @param tenantId the tenantId - * @param bundleAlias the bundle alias + * @param widgetsBundleId the widgets bundle id * @return the list of widget types objects */ - List findWidgetTypesByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias); + List findWidgetTypesByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId); /** - * Find widget types details by tenantId and bundleAlias. + * Find widget types details by widgetsBundleId. * * @param tenantId the tenantId - * @param bundleAlias the bundle alias + * @param widgetsBundleId the widgets bundle id * @return the list of widget types details objects */ - List findWidgetTypesDetailsByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias); + List findWidgetTypesDetailsByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId); + + PageData findWidgetTypesInfosByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink); + + List findWidgetFqnsByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId); /** - * Find widget types infos by tenantId and bundleAlias. + * Find widget type by tenantId and FQN. * * @param tenantId the tenantId - * @param bundleAlias the bundle alias - * @return the list of widget types infos objects + * @param fqn the FQN + * @return the widget type object */ - List findWidgetTypesInfosByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias); + WidgetType findByTenantIdAndFqn(UUID tenantId, String fqn); /** - * Find widget type by tenantId, bundleAlias and alias. + * Find widget types infos by tenantId and resourceId in descriptor. * * @param tenantId the tenantId - * @param bundleAlias the bundle alias - * @param alias the alias - * @return the widget type object + * @param tbResourceId the resourceId + * @return the list of widget types infos objects */ - WidgetType findByTenantIdBundleAliasAndAlias(UUID tenantId, String bundleAlias, String alias); + List findWidgetTypesInfosByTenantIdAndResourceId(UUID tenantId, UUID tbResourceId); + + List findWidgetTypeIdsByTenantIdAndFqns(UUID tenantId, List widgetFqns); + + List findWidgetsBundleWidgetsByWidgetsBundleId(UUID tenantId, UUID widgetsBundleId); + + void saveWidgetsBundleWidget(WidgetsBundleWidget widgetsBundleWidget); + + void removeWidgetTypeFromWidgetsBundle(UUID widgetsBundleId, UUID widgetTypeId); + + PageData findAllWidgetTypesIds(PageLink pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java index 0337c190a4..4d9510d0b1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java @@ -17,33 +17,57 @@ package org.thingsboard.server.dao.widget; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetTypeId; +import org.thingsboard.server.common.data.id.WidgetsBundleId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.widget.DeprecatedFilter; import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetTypeInfo; +import org.thingsboard.server.common.data.widget.WidgetsBundleWidget; +import org.thingsboard.server.dao.entity.AbstractCachedEntityService; +import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; +import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; +import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; +import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; + +import static org.thingsboard.server.dao.service.Validator.validateIds; @Service("WidgetTypeDaoService") @Slf4j public class WidgetTypeServiceImpl implements WidgetTypeService { public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; + public static final String INCORRECT_RESOURCE_ID = "Incorrect resourceId "; public static final String INCORRECT_BUNDLE_ALIAS = "Incorrect bundleAlias "; + public static final String INCORRECT_WIDGETS_BUNDLE_ID = "Incorrect widgetsBundleId "; + @Autowired private WidgetTypeDao widgetTypeDao; @Autowired private DataValidator widgetTypeValidator; + @Autowired + protected ApplicationEventPublisher eventPublisher; + + @Autowired + protected ImageService imageService; + @Override public WidgetType findWidgetTypeById(TenantId tenantId, WidgetTypeId widgetTypeId) { log.trace("Executing findWidgetTypeById [{}]", widgetTypeId); @@ -58,11 +82,29 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { return widgetTypeDao.findById(tenantId, widgetTypeId.getId()); } + @Override + public boolean widgetTypeExistsByTenantIdAndWidgetTypeId(TenantId tenantId, WidgetTypeId widgetTypeId) { + log.trace("Executing widgetTypeExistsByTenantIdAndWidgetTypeId, tenantId [{}], widgetTypeId [{}]", tenantId, widgetTypeId); + Validator.validateId(widgetTypeId, "Incorrect widgetTypeId " + widgetTypeId); + return widgetTypeDao.existsByTenantIdAndId(tenantId, widgetTypeId.getId()); + } + @Override public WidgetTypeDetails saveWidgetType(WidgetTypeDetails widgetTypeDetails) { log.trace("Executing saveWidgetType [{}]", widgetTypeDetails); widgetTypeValidator.validate(widgetTypeDetails, WidgetType::getTenantId); - return widgetTypeDao.save(widgetTypeDetails.getTenantId(), widgetTypeDetails); + try { + imageService.replaceBase64WithImageUrl(widgetTypeDetails); + WidgetTypeDetails result = widgetTypeDao.save(widgetTypeDetails.getTenantId(), widgetTypeDetails); + eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(result.getTenantId()) + .entityId(result.getId()).added(widgetTypeDetails.getId() == null).build()); + return result; + } catch (Exception t) { + AbstractCachedEntityService.checkConstraintViolation(t, + "uq_widget_type_fqn", "Widget type with such fqn already exists!"); + AbstractCachedEntityService.checkConstraintViolation(t, "widget_type_external_id_unq_key", "Widget type with such external id already exists!"); + throw t; + } } @Override @@ -70,50 +112,118 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { log.trace("Executing deleteWidgetType [{}]", widgetTypeId); Validator.validateId(widgetTypeId, "Incorrect widgetTypeId " + widgetTypeId); widgetTypeDao.removeById(tenantId, widgetTypeId.getId()); + eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(widgetTypeId).build()); + } + + @Override + public PageData findSystemWidgetTypesByPageLink(TenantId tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink) { + log.trace("Executing findSystemWidgetTypesByPageLink, fullSearch [{}], deprecatedFilter [{}], widgetTypes [{}], pageLink [{}]", fullSearch, deprecatedFilter, widgetTypes, pageLink); + Validator.validatePageLink(pageLink); + return widgetTypeDao.findSystemWidgetTypes(tenantId, fullSearch, deprecatedFilter, widgetTypes, pageLink); + } + + @Override + public PageData findAllTenantWidgetTypesByTenantIdAndPageLink(TenantId tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink) { + log.trace("Executing findAllTenantWidgetTypesByTenantIdAndPageLink, tenantId [{}], fullSearch [{}], deprecatedFilter [{}], widgetTypes [{}], pageLink [{}]", + tenantId, fullSearch, deprecatedFilter, widgetTypes, pageLink); + Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validatePageLink(pageLink); + return widgetTypeDao.findAllTenantWidgetTypesByTenantId(tenantId.getId(), fullSearch, deprecatedFilter, widgetTypes, pageLink); + } + + @Override + public PageData findTenantWidgetTypesByTenantIdAndPageLink(TenantId tenantId, boolean fullSearch, DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink) { + log.trace("Executing findTenantWidgetTypesByTenantIdAndPageLink, tenantId [{}], fullSearch [{}], deprecatedFilter [{}], widgetTypes [{}], pageLink [{}]", + tenantId, fullSearch, deprecatedFilter, widgetTypes, pageLink); + Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validatePageLink(pageLink); + return widgetTypeDao.findTenantWidgetTypesByTenantId(tenantId.getId(), fullSearch, deprecatedFilter, widgetTypes, pageLink); + } + + @Override + public List findWidgetTypesByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId) { + log.trace("Executing findWidgetTypesByWidgetsBundleId, tenantId [{}], widgetsBundleId [{}]", tenantId, widgetsBundleId); + Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validateId(widgetsBundleId, INCORRECT_WIDGETS_BUNDLE_ID + widgetsBundleId); + return widgetTypeDao.findWidgetTypesByWidgetsBundleId(tenantId.getId(), widgetsBundleId.getId()); } @Override - public List findWidgetTypesByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias) { - log.trace("Executing findWidgetTypesByTenantIdAndBundleAlias, tenantId [{}], bundleAlias [{}]", tenantId, bundleAlias); + public List findWidgetTypesDetailsByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId) { + log.trace("Executing findWidgetTypesDetailsByWidgetsBundleId, tenantId [{}], widgetsBundleId [{}]", tenantId, widgetsBundleId); Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateString(bundleAlias, INCORRECT_BUNDLE_ALIAS + bundleAlias); - return widgetTypeDao.findWidgetTypesByTenantIdAndBundleAlias(tenantId.getId(), bundleAlias); + Validator.validateId(widgetsBundleId, INCORRECT_WIDGETS_BUNDLE_ID + widgetsBundleId); + return widgetTypeDao.findWidgetTypesDetailsByWidgetsBundleId(tenantId.getId(), widgetsBundleId.getId()); + } @Override - public List findWidgetTypesDetailsByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias) { - log.trace("Executing findWidgetTypesDetailsByTenantIdAndBundleAlias, tenantId [{}], bundleAlias [{}]", tenantId, bundleAlias); + public PageData findWidgetTypesInfosByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId, boolean fullSearch, + DeprecatedFilter deprecatedFilter, List widgetTypes, PageLink pageLink) { + log.trace("Executing findWidgetTypesInfosByWidgetsBundleId, tenantId [{}], widgetsBundleId [{}], fullSearch [{}], deprecatedFilter [{}], widgetTypes [{}], pageLink [{}]", + tenantId, widgetsBundleId, fullSearch, deprecatedFilter, widgetTypes, pageLink); Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateString(bundleAlias, INCORRECT_BUNDLE_ALIAS + bundleAlias); - return widgetTypeDao.findWidgetTypesDetailsByTenantIdAndBundleAlias(tenantId.getId(), bundleAlias); + Validator.validateId(widgetsBundleId, INCORRECT_WIDGETS_BUNDLE_ID + widgetsBundleId); + Validator.validatePageLink(pageLink); + return widgetTypeDao.findWidgetTypesInfosByWidgetsBundleId(tenantId.getId(), widgetsBundleId.getId(), fullSearch, deprecatedFilter, widgetTypes, pageLink); } @Override - public List findWidgetTypesInfosByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias) { - log.trace("Executing findWidgetTypesInfosByTenantIdAndBundleAlias, tenantId [{}], bundleAlias [{}]", tenantId, bundleAlias); + public List findWidgetFqnsByWidgetsBundleId(TenantId tenantId, WidgetsBundleId widgetsBundleId) { + log.trace("Executing findWidgetTypesInfosByWidgetsBundleId, tenantId [{}], widgetsBundleId [{}]", tenantId, widgetsBundleId); Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateString(bundleAlias, INCORRECT_BUNDLE_ALIAS + bundleAlias); - return widgetTypeDao.findWidgetTypesInfosByTenantIdAndBundleAlias(tenantId.getId(), bundleAlias); + Validator.validateId(widgetsBundleId, INCORRECT_WIDGETS_BUNDLE_ID + widgetsBundleId); + return widgetTypeDao.findWidgetFqnsByWidgetsBundleId(tenantId.getId(), widgetsBundleId.getId()); } @Override - public WidgetType findWidgetTypeByTenantIdBundleAliasAndAlias(TenantId tenantId, String bundleAlias, String alias) { - log.trace("Executing findWidgetTypeByTenantIdBundleAliasAndAlias, tenantId [{}], bundleAlias [{}], alias [{}]", tenantId, bundleAlias, alias); + public WidgetType findWidgetTypeByTenantIdAndFqn(TenantId tenantId, String fqn) { + log.trace("Executing findWidgetTypeByTenantIdAndFqn, tenantId [{}], fqn [{}]", tenantId, fqn); Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateString(bundleAlias, INCORRECT_BUNDLE_ALIAS + bundleAlias); - Validator.validateString(alias, "Incorrect alias " + alias); - return widgetTypeDao.findByTenantIdBundleAliasAndAlias(tenantId.getId(), bundleAlias, alias); + Validator.validateString(fqn, "Incorrect fqn " + fqn); + return widgetTypeDao.findByTenantIdAndFqn(tenantId.getId(), fqn); } @Override - public void deleteWidgetTypesByTenantIdAndBundleAlias(TenantId tenantId, String bundleAlias) { - log.trace("Executing deleteWidgetTypesByTenantIdAndBundleAlias, tenantId [{}], bundleAlias [{}]", tenantId, bundleAlias); + public void updateWidgetsBundleWidgetTypes(TenantId tenantId, WidgetsBundleId widgetsBundleId, List widgetTypeIds) { + log.trace("Executing updateWidgetsBundleWidgetTypes, tenantId [{}], widgetsBundleId [{}], widgetTypeIds [{}]", tenantId, widgetsBundleId, widgetTypeIds); Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Validator.validateString(bundleAlias, INCORRECT_BUNDLE_ALIAS + bundleAlias); - List widgetTypes = widgetTypeDao.findWidgetTypesByTenantIdAndBundleAlias(tenantId.getId(), bundleAlias); - for (WidgetType widgetType : widgetTypes) { - deleteWidgetType(tenantId, new WidgetTypeId(widgetType.getUuidId())); + Validator.validateId(widgetsBundleId, INCORRECT_WIDGETS_BUNDLE_ID + widgetsBundleId); + Validator.checkNotNull(widgetTypeIds, "Incorrect widgetTypeIds " + widgetTypeIds); + if (!widgetTypeIds.isEmpty()) { + validateIds(widgetTypeIds, "Incorrect widgetTypeIds " + widgetTypeIds); + } + List bundleWidgets = new ArrayList<>(); + for (int index = 0; index < widgetTypeIds.size(); index++) { + bundleWidgets.add(new WidgetsBundleWidget(widgetsBundleId, widgetTypeIds.get(index), index)); + } + List existingBundleWidgets = widgetTypeDao.findWidgetsBundleWidgetsByWidgetsBundleId(tenantId.getId(), widgetsBundleId.getId()); + List toRemove = existingBundleWidgets.stream() + .map(WidgetsBundleWidget::getWidgetTypeId) + .filter(widgetTypeId -> bundleWidgets.stream().noneMatch(newBundleWidget -> + newBundleWidget.getWidgetTypeId().equals(widgetTypeId))).collect(Collectors.toList()); + for (WidgetTypeId widgetTypeId : toRemove) { + widgetTypeDao.removeWidgetTypeFromWidgetsBundle(widgetsBundleId.getId(), widgetTypeId.getId()); + } + for (WidgetsBundleWidget widgetsBundleWidget : bundleWidgets) { + widgetTypeDao.saveWidgetsBundleWidget(widgetsBundleWidget); } + eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId) + .entityId(widgetsBundleId).added(false).build()); + } + + @Override + public void updateWidgetsBundleWidgetFqns(TenantId tenantId, WidgetsBundleId widgetsBundleId, List widgetFqns) { + log.trace("Executing updateWidgetsBundleWidgetFqns, tenantId [{}], widgetsBundleId [{}], widgetFqns [{}]", tenantId, widgetsBundleId, widgetFqns); + List widgetTypeIds = widgetTypeDao.findWidgetTypeIdsByTenantIdAndFqns(tenantId.getId(), widgetFqns); + this.updateWidgetsBundleWidgetTypes(tenantId, widgetsBundleId, widgetTypeIds); + } + + @Override + public void deleteWidgetTypesByTenantId(TenantId tenantId) { + log.trace("Executing deleteWidgetTypesByTenantId, tenantId [{}]", tenantId); + Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + tenantWidgetTypeRemover.removeEntities(tenantId, tenantId); } @Override @@ -126,4 +236,18 @@ public class WidgetTypeServiceImpl implements WidgetTypeService { return EntityType.WIDGET_TYPE; } + private PaginatedRemover tenantWidgetTypeRemover = + new PaginatedRemover<>() { + + @Override + protected PageData findEntities(TenantId tenantId, TenantId id, PageLink pageLink) { + return widgetTypeDao.findTenantWidgetTypesByTenantId(id.getId(), false, DeprecatedFilter.ALL, null, pageLink); + } + + @Override + protected void removeEntity(TenantId tenantId, WidgetTypeInfo entity) { + deleteWidgetType(tenantId, new WidgetTypeId(entity.getUuidId())); + } + }; + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleDao.java b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleDao.java index e4b1762426..939f9e43e5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleDao.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.widget; +import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.page.PageData; @@ -22,13 +23,14 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ExportableEntityDao; +import org.thingsboard.server.dao.ImageContainerDao; import java.util.UUID; /** * The Interface WidgetsBundleDao. */ -public interface WidgetsBundleDao extends Dao, ExportableEntityDao { +public interface WidgetsBundleDao extends Dao, ExportableEntityDao, ImageContainerDao { /** * Save or update widgets bundle object @@ -54,7 +56,7 @@ public interface WidgetsBundleDao extends Dao, ExportableEntityDa * @param pageLink the page link * @return the list of widgets bundles objects */ - PageData findSystemWidgetsBundles(TenantId tenantId, PageLink pageLink); + PageData findSystemWidgetsBundles(TenantId tenantId, boolean fullSearch, PageLink pageLink); /** * Find tenant widgets bundles by tenantId and page link. @@ -72,7 +74,18 @@ public interface WidgetsBundleDao extends Dao, ExportableEntityDa * @param pageLink the page link * @return the list of widgets bundles objects */ - PageData findAllTenantWidgetsBundlesByTenantId(UUID tenantId, PageLink pageLink); + PageData findAllTenantWidgetsBundlesByTenantId(UUID tenantId, boolean fullSearch, PageLink pageLink); + + /** + * Find all tenant widgets bundles (does not include system) by tenantId and page link. + * + * @param tenantId the tenantId + * @param pageLink the page link + * @return the list of widgets bundles objects + */ + PageData findTenantWidgetsBundlesByTenantId(UUID tenantId, boolean fullSearch, PageLink pageLink); + + PageData findAllWidgetsBundles(PageLink pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java index 63a4ce0a77..468bea6876 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.widget; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.EntityId; @@ -27,7 +28,10 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.entity.AbstractCachedEntityService; +import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; +import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.exception.IncorrectParameterException; +import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; @@ -53,6 +57,12 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { @Autowired private DataValidator widgetsBundleValidator; + @Autowired + protected ApplicationEventPublisher eventPublisher; + + @Autowired + private ImageService imageService; + @Override public WidgetsBundle findWidgetsBundleById(TenantId tenantId, WidgetsBundleId widgetsBundleId) { log.trace("Executing findWidgetsBundleById [{}]", widgetsBundleId); @@ -65,9 +75,15 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { log.trace("Executing saveWidgetsBundle [{}]", widgetsBundle); widgetsBundleValidator.validate(widgetsBundle, WidgetsBundle::getTenantId); try { - return widgetsBundleDao.save(widgetsBundle.getTenantId(), widgetsBundle); + imageService.replaceBase64WithImageUrl(widgetsBundle, "bundle"); + WidgetsBundle result = widgetsBundleDao.save(widgetsBundle.getTenantId(), widgetsBundle); + eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(result.getTenantId()) + .entityId(result.getId()).added(widgetsBundle.getId() == null).build()); + return result; } catch (Exception e) { - AbstractCachedEntityService.checkConstraintViolation(e, "widgets_bundle_external_id_unq_key", "Widget Bundle with such external id already exists!"); + AbstractCachedEntityService.checkConstraintViolation(e, + "uq_widgets_bundle_alias", "Widgets Bundle with such alias already exists!"); + AbstractCachedEntityService.checkConstraintViolation(e, "widgets_bundle_external_id_unq_key", "Widgets Bundle with such external id already exists!"); throw e; } } @@ -80,7 +96,7 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { if (widgetsBundle == null) { throw new IncorrectParameterException("Unable to delete non-existent widgets bundle."); } - widgetTypeService.deleteWidgetTypesByTenantIdAndBundleAlias(widgetsBundle.getTenantId(), widgetsBundle.getAlias()); + eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(widgetsBundleId).build()); widgetsBundleDao.removeById(tenantId, widgetsBundleId.getId()); } @@ -93,10 +109,10 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { } @Override - public PageData findSystemWidgetsBundlesByPageLink(TenantId tenantId, PageLink pageLink) { - log.trace("Executing findSystemWidgetsBundles, pageLink [{}]", pageLink); + public PageData findSystemWidgetsBundlesByPageLink(TenantId tenantId, boolean fullSearch, PageLink pageLink) { + log.trace("Executing findSystemWidgetsBundles, fullSearch [{}], pageLink [{}]", fullSearch, pageLink); Validator.validatePageLink(pageLink); - return widgetsBundleDao.findSystemWidgetsBundles(tenantId, pageLink); + return widgetsBundleDao.findSystemWidgetsBundles(tenantId, fullSearch, pageLink); } @Override @@ -106,7 +122,7 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { PageLink pageLink = new PageLink(DEFAULT_WIDGETS_BUNDLE_LIMIT); PageData pageData; do { - pageData = findSystemWidgetsBundlesByPageLink(tenantId, pageLink); + pageData = findSystemWidgetsBundlesByPageLink(tenantId, false, pageLink); widgetsBundles.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -124,11 +140,19 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { } @Override - public PageData findAllTenantWidgetsBundlesByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { - log.trace("Executing findAllTenantWidgetsBundlesByTenantIdAndPageLink, tenantId [{}], pageLink [{}]", tenantId, pageLink); + public PageData findAllTenantWidgetsBundlesByTenantIdAndPageLink(TenantId tenantId, boolean fullSearch, PageLink pageLink) { + log.trace("Executing findAllTenantWidgetsBundlesByTenantIdAndPageLink, tenantId [{}], fullSearch [{}], pageLink [{}]", tenantId, fullSearch, pageLink); + Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + Validator.validatePageLink(pageLink); + return widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(tenantId.getId(), fullSearch, pageLink); + } + + @Override + public PageData findTenantWidgetsBundlesByTenantIdAndPageLink(TenantId tenantId, boolean fullSearch, PageLink pageLink) { + log.trace("Executing findTenantWidgetsBundlesByTenantIdAndPageLink, tenantId [{}], fullSearch [{}], pageLink [{}]", tenantId, fullSearch, pageLink); Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); Validator.validatePageLink(pageLink); - return widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(tenantId.getId(), pageLink); + return widgetsBundleDao.findTenantWidgetsBundlesByTenantId(tenantId.getId(), fullSearch, pageLink); } @Override @@ -139,7 +163,7 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { PageLink pageLink = new PageLink(DEFAULT_WIDGETS_BUNDLE_LIMIT); PageData pageData; do { - pageData = findAllTenantWidgetsBundlesByTenantIdAndPageLink(tenantId, pageLink); + pageData = findAllTenantWidgetsBundlesByTenantIdAndPageLink(tenantId, false, pageLink); widgetsBundles.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); diff --git a/dao/src/main/resources/sql/schema-entities-idx.sql b/dao/src/main/resources/sql/schema-entities-idx.sql index 4aad5fa1b2..df95a50d47 100644 --- a/dao/src/main/resources/sql/schema-entities-idx.sql +++ b/dao/src/main/resources/sql/schema-entities-idx.sql @@ -57,6 +57,8 @@ CREATE INDEX IF NOT EXISTS idx_asset_customer_id_and_type ON asset(tenant_id, cu CREATE INDEX IF NOT EXISTS idx_asset_type ON asset(tenant_id, type); +CREATE INDEX IF NOT EXISTS idx_asset_profile_id ON asset(tenant_id, asset_profile_id); + CREATE INDEX IF NOT EXISTS idx_attribute_kv_by_key_and_last_update_ts ON attribute_kv(entity_id, attribute_key, last_update_ts desc); CREATE INDEX IF NOT EXISTS idx_audit_log_tenant_id_and_created_time ON audit_log(tenant_id, created_time DESC); @@ -65,6 +67,8 @@ CREATE INDEX IF NOT EXISTS idx_audit_log_id ON audit_log(id); CREATE INDEX IF NOT EXISTS idx_edge_event_tenant_id_and_created_time ON edge_event(tenant_id, created_time DESC); +CREATE INDEX IF NOT EXISTS idx_edge_event_tenant_id_edge_id_created_time ON edge_event(tenant_id, edge_id, created_time DESC); + CREATE INDEX IF NOT EXISTS idx_edge_event_id ON edge_event(id); CREATE INDEX IF NOT EXISTS idx_rpc_tenant_id_device_id ON rpc(tenant_id, device_id); @@ -89,6 +93,8 @@ CREATE INDEX IF NOT EXISTS idx_rule_node_external_id ON rule_node(rule_chain_id, CREATE INDEX IF NOT EXISTS idx_rule_node_type ON rule_node(type); +CREATE INDEX IF NOT EXISTS idx_rule_node_type_configuration_version ON rule_node(type, configuration_version); + CREATE INDEX IF NOT EXISTS idx_api_usage_state_entity_id ON api_usage_state(entity_id); CREATE INDEX IF NOT EXISTS idx_alarm_comment_alarm_id ON alarm_comment(alarm_id); @@ -102,6 +108,8 @@ CREATE INDEX IF NOT EXISTS idx_notification_rule_tenant_id_trigger_type_created_ CREATE INDEX IF NOT EXISTS idx_notification_request_tenant_id_user_created_time ON notification_request(tenant_id, created_time DESC) WHERE originator_entity_type = 'USER'; +CREATE INDEX IF NOT EXISTS idx_notification_request_tenant_id ON notification_request(tenant_id); + CREATE INDEX IF NOT EXISTS idx_notification_request_rule_id_originator_entity_id ON notification_request(rule_id, originator_entity_id) WHERE originator_entity_type = 'ALARM'; @@ -110,4 +118,14 @@ CREATE INDEX IF NOT EXISTS idx_notification_request_status ON notification_reque CREATE INDEX IF NOT EXISTS idx_notification_id ON notification(id); +CREATE INDEX IF NOT EXISTS idx_notification_notification_request_id ON notification(request_id); + CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_created_time ON notification(recipient_id, created_time DESC); + +CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_unread ON notification(recipient_id) WHERE status <> 'READ'; + +CREATE INDEX IF NOT EXISTS idx_resource_etag ON resource(tenant_id, etag); + +CREATE INDEX IF NOT EXISTS idx_resource_etag ON resource(tenant_id, etag); + +CREATE INDEX IF NOT EXISTS idx_resource_type_public_resource_key ON resource(resource_type, public_resource_key); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 3b4861e015..d216380eaa 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -25,7 +25,7 @@ CREATE OR REPLACE PROCEDURE insert_tb_schema_settings() $$ BEGIN IF (SELECT COUNT(*) FROM tb_schema_settings) = 0 THEN - INSERT INTO tb_schema_settings (schema_version) VALUES (3003000); + INSERT INTO tb_schema_settings (schema_version) VALUES (3006000); END IF; END; $$; @@ -122,6 +122,7 @@ CREATE TABLE IF NOT EXISTS component_descriptor ( actions varchar(255), clazz varchar UNIQUE, configuration_descriptor varchar, + configuration_version int DEFAULT 0, name varchar(255), scope varchar(255), type varchar(255), @@ -180,6 +181,7 @@ CREATE TABLE IF NOT EXISTS rule_node ( created_time bigint NOT NULL, rule_chain_id uuid, additional_info varchar, + configuration_version int DEFAULT 0, configuration varchar(10000000), type varchar(255), name varchar(255), @@ -480,13 +482,17 @@ CREATE TABLE IF NOT EXISTS user_credentials ( CREATE TABLE IF NOT EXISTS widget_type ( id uuid NOT NULL CONSTRAINT widget_type_pkey PRIMARY KEY, created_time bigint NOT NULL, - alias varchar(255), - bundle_alias varchar(255), + fqn varchar(512), descriptor varchar(1000000), name varchar(255), tenant_id uuid, image varchar(1000000), - description varchar(255) + deprecated boolean NOT NULL DEFAULT false, + description varchar(1024), + tags text[], + external_id uuid, + CONSTRAINT uq_widget_type_fqn UNIQUE (tenant_id, fqn), + CONSTRAINT widget_type_external_id_unq_key UNIQUE (tenant_id, external_id) ); CREATE TABLE IF NOT EXISTS widgets_bundle ( @@ -496,11 +502,22 @@ CREATE TABLE IF NOT EXISTS widgets_bundle ( tenant_id uuid, title varchar(255), image varchar(1000000), - description varchar(255), + description varchar(1024), + widgets_bundle_order int, external_id uuid, + CONSTRAINT uq_widgets_bundle_alias UNIQUE (tenant_id, alias), CONSTRAINT widgets_bundle_external_id_unq_key UNIQUE (tenant_id, external_id) ); +CREATE TABLE IF NOT EXISTS widgets_bundle_widget ( + widgets_bundle_id uuid NOT NULL, + widget_type_id uuid NOT NULL, + widget_type_order int NOT NULL DEFAULT 0, + CONSTRAINT widgets_bundle_widget_pkey PRIMARY KEY (widgets_bundle_id, widget_type_id), + CONSTRAINT fk_widgets_bundle FOREIGN KEY (widgets_bundle_id) REFERENCES widgets_bundle(id) ON DELETE CASCADE, + CONSTRAINT fk_widget_type FOREIGN KEY (widget_type_id) REFERENCES widget_type(id) ON DELETE CASCADE +); + CREATE TABLE IF NOT EXISTS entity_view ( id uuid NOT NULL CONSTRAINT entity_view_pkey PRIMARY KEY, created_time bigint NOT NULL, @@ -681,6 +698,7 @@ CREATE TABLE IF NOT EXISTS api_usage_state ( db_storage varchar(32), re_exec varchar(32), js_exec varchar(32), + tbel_exec varchar(32), email_exec varchar(32), sms_exec varchar(32), alarm_exec varchar(32), @@ -696,8 +714,13 @@ CREATE TABLE IF NOT EXISTS resource ( resource_key varchar(255) NOT NULL, search_text varchar(255), file_name varchar(255) NOT NULL, - data varchar, + data bytea, etag varchar, + descriptor varchar, + preview bytea, + is_public boolean default true, + public_resource_key varchar(32) unique, + external_id uuid, CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key) ); @@ -718,6 +741,7 @@ CREATE TABLE IF NOT EXISTS edge ( ); CREATE TABLE IF NOT EXISTS edge_event ( + seq_id INT GENERATED ALWAYS AS IDENTITY, id uuid NOT NULL, created_time bigint NOT NULL, edge_id uuid, @@ -729,6 +753,7 @@ CREATE TABLE IF NOT EXISTS edge_event ( tenant_id uuid, ts bigint NOT NULL ) PARTITION BY RANGE(created_time); +ALTER TABLE IF EXISTS edge_event ALTER COLUMN seq_id SET CYCLE; CREATE TABLE IF NOT EXISTS rpc ( id uuid NOT NULL CONSTRAINT rpc_pkey PRIMARY KEY, @@ -822,7 +847,7 @@ CREATE TABLE IF NOT EXISTS notification_request ( targets VARCHAR(10000) NOT NULL, template_id UUID, template VARCHAR(10000000), - info VARCHAR(1000), + info VARCHAR(1000000), additional_config VARCHAR(1000), originator_entity_id UUID, originator_entity_type VARCHAR(32), @@ -834,8 +859,8 @@ CREATE TABLE IF NOT EXISTS notification_request ( CREATE TABLE IF NOT EXISTS notification ( id UUID NOT NULL, created_time BIGINT NOT NULL, - request_id UUID NULL CONSTRAINT fk_notification_request_id REFERENCES notification_request(id) ON DELETE CASCADE, - recipient_id UUID NOT NULL CONSTRAINT fk_notification_recipient_id REFERENCES tb_user(id) ON DELETE CASCADE, + request_id UUID, + recipient_id UUID NOT NULL, type VARCHAR(50) NOT NULL, subject VARCHAR(255), body VARCHAR(1000) NOT NULL, @@ -850,3 +875,10 @@ CREATE TABLE IF NOT EXISTS user_settings ( CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES tb_user(id) ON DELETE CASCADE, CONSTRAINT user_settings_pkey PRIMARY KEY (user_id, type) ); + +CREATE TABLE IF NOT EXISTS alarm_types ( + tenant_id uuid NOT NULL, + type varchar(255) NOT NULL, + CONSTRAINT tenant_id_type_unq_key UNIQUE (tenant_id, type), + CONSTRAINT fk_entity_tenant_id FOREIGN KEY (tenant_id) REFERENCES tenant(id) ON DELETE CASCADE +); diff --git a/dao/src/main/resources/sql/schema-views-and-functions.sql b/dao/src/main/resources/sql/schema-views-and-functions.sql index 3cd20ac0c4..1aa5647bde 100644 --- a/dao/src/main/resources/sql/schema-views-and-functions.sql +++ b/dao/src/main/resources/sql/schema-views-and-functions.sql @@ -114,6 +114,7 @@ BEGIN a_details, a_propagate, a_propagate_to_owner, a_propagate_to_tenant, a_propagation_types, false, 0, false, 0, NULL, 0); + INSERT INTO alarm_types (tenant_id, type) VALUES (t_id, a_type) ON CONFLICT (tenant_id, type) DO NOTHING; SELECT * INTO result FROM alarm_info a WHERE a.id = a_id AND a.tenant_id = t_id; RETURN json_build_object('success', true, 'created', true, 'modified', true, 'alarm', row_to_json(result))::text; ELSE @@ -284,3 +285,9 @@ BEGIN RETURN json_build_object('success', true, 'modified', modified, 'alarm', row_to_json(result))::text; END $$; + +DROP VIEW IF EXISTS widget_type_info_view CASCADE; +CREATE OR REPLACE VIEW widget_type_info_view AS +SELECT t.* + , COALESCE((t.descriptor::json->>'type')::text, '') as widget_type +FROM widget_type t; diff --git a/dao/src/test/java/org/thingsboard/server/dao/eventsourcing/DeleteEntityEventTest.java b/dao/src/test/java/org/thingsboard/server/dao/eventsourcing/DeleteEntityEventTest.java new file mode 100644 index 0000000000..39d228ceac --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/eventsourcing/DeleteEntityEventTest.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.eventsourcing; + +import org.junit.jupiter.api.Test; + +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.BDDAssertions.byLessThan; + +class DeleteEntityEventTest { + + @Test + void testBuilderDefaultTs() { + assertThat(DeleteEntityEvent.builder().build().getTs()) + .isCloseTo(System.currentTimeMillis(), byLessThan(TimeUnit.MINUTES.toMillis(1))); + + assertThat(DeleteEntityEvent.builder().ts(Long.MIN_VALUE).build().getTs()) + .isEqualTo(Long.MIN_VALUE); + assertThat(DeleteEntityEvent.builder().ts(Long.MAX_VALUE).build().getTs()) + .isEqualTo(Long.MAX_VALUE); + assertThat(DeleteEntityEvent.builder().ts(-1L).build().getTs()) + .isEqualTo(-1L); + assertThat(DeleteEntityEvent.builder().ts(0L).build().getTs()) + .isEqualTo(0L); + + assertThat(DeleteEntityEvent.builder().ts(1692175215000L).build().getTs()) + .isEqualTo(1692175215000L); + } + +} \ No newline at end of file diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java index e609b93289..91c6488447 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java @@ -59,6 +59,7 @@ import java.nio.ByteBuffer; import java.util.Comparator; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.UUID; import static org.junit.Assert.assertNotNull; @@ -80,7 +81,7 @@ public abstract class AbstractServiceTest { @Before public void beforeAbstractService() { - tenantId = createTenant(); + tenantId = createTenant().getId(); } @After @@ -107,29 +108,10 @@ public abstract class AbstractServiceTest { .data(JacksonUtil.toString(readFromResource("TestJsonData.json"))) .build(); } -// -// private ComponentDescriptor getOrCreateDescriptor(ComponentScope scope, ComponentType type, String clazz, String configurationDescriptorResource) throws IOException { -// return getOrCreateDescriptor(scope, type, clazz, configurationDescriptorResource, null); -// } -// -// private ComponentDescriptor getOrCreateDescriptor(ComponentScope scope, ComponentType type, String clazz, String configurationDescriptorResource, String actions) throws IOException { -// ComponentDescriptor descriptor = componentDescriptorService.findByClazz(clazz); -// if (descriptor == null) { -// descriptor = new ComponentDescriptor(); -// descriptor.setName("test"); -// descriptor.setClazz(clazz); -// descriptor.setScope(scope); -// descriptor.setType(type); -// descriptor.setActions(actions); -// descriptor.setConfigurationDescriptor(readFromResource(configurationDescriptorResource)); -// componentDescriptorService.saveComponent(descriptor); -// } -// return descriptor; -// } public JsonNode readFromResource(String resourceName) throws IOException { try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(resourceName)){ - return JacksonUtil.fromBytes(is.readAllBytes()); + return JacksonUtil.fromBytes(Objects.requireNonNull(is).readAllBytes()); } } @@ -172,12 +154,12 @@ public abstract class AbstractServiceTest { return assetProfile; } - public TenantId createTenant() { + public Tenant createTenant() { Tenant tenant = new Tenant(); tenant.setTitle("My tenant " + UUID.randomUUID()); Tenant savedTenant = tenantService.saveTenant(tenant); assertNotNull(savedTenant); - return savedTenant.getId(); + return savedTenant; } protected Edge constructEdge(TenantId tenantId, String name, String type) { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AlarmCommentServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AlarmCommentServiceTest.java index 22257242f5..341eb69583 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/AlarmCommentServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/AlarmCommentServiceTest.java @@ -28,8 +28,10 @@ import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmComment; import org.thingsboard.server.common.data.alarm.AlarmCommentInfo; import org.thingsboard.server.common.data.alarm.AlarmCommentType; +import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -59,11 +61,12 @@ public class AlarmCommentServiceTest extends AbstractServiceTest { @Before public void before() { - alarm = Alarm.builder().tenantId(tenantId).originator(new AssetId(Uuids.timeBased())) + alarm = alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.builder() + .tenantId(tenantId) + .originator(new AssetId(Uuids.timeBased())) .type(TEST_ALARM) .severity(AlarmSeverity.CRITICAL) - .startTs(System.currentTimeMillis()).build(); - alarm = alarmService.createOrUpdateAlarm(alarm).getAlarm(); + .startTs(System.currentTimeMillis()).build()).getAlarm(); user = new User(); user.setAuthority(Authority.TENANT_ADMIN); @@ -71,15 +74,14 @@ public class AlarmCommentServiceTest extends AbstractServiceTest { user.setEmail("tenant@thingsboard.org"); user.setFirstName("John"); user.setLastName("Brown"); - user = userService.saveUser(user); + user = userService.saveUser(TenantId.SYS_TENANT_ID, user); } @After public void after() { - alarmService.deleteAlarm(tenantId, alarm.getId()); + alarmService.delAlarm(tenantId, alarm.getId()); } - @Test public void testCreateAndFetchAlarmComment() throws ExecutionException, InterruptedException { AlarmComment alarmComment = AlarmComment.builder().alarmId(alarm.getId()) diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AlarmServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AlarmServiceTest.java index 5b0981b44a..9d42635d3d 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/AlarmServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/AlarmServiceTest.java @@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.page.TimePageLink; @@ -145,7 +146,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .status(AlarmStatus.ACTIVE_UNACK).pageLink( new TimePageLink(1, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); @@ -156,7 +157,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .status(AlarmStatus.ACTIVE_UNACK).pageLink( new TimePageLink(1, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(0, alarms.getData().size()); @@ -170,7 +171,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .status(AlarmStatus.ACTIVE_UNACK).pageLink( new TimePageLink(1, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); @@ -181,7 +182,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .status(AlarmStatus.ACTIVE_UNACK).pageLink( new TimePageLink(1, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); @@ -194,7 +195,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .status(AlarmStatus.ACTIVE_ACK).pageLink( new TimePageLink(1, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); @@ -205,7 +206,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .status(AlarmStatus.ACTIVE_UNACK).pageLink( new TimePageLink(1, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(0, alarms.getData().size()); @@ -217,7 +218,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .status(AlarmStatus.CLEARED_ACK).pageLink( new TimePageLink(1, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); @@ -244,11 +245,11 @@ public class AlarmServiceTest extends AbstractServiceTest { // Check child relation PageData alarms = alarmService.findAlarmsV2(tenantId, AlarmQueryV2.builder() .affectedEntityId(childId) - .severityList(Arrays.asList(AlarmSeverity.CRITICAL)) - .statusList(Arrays.asList(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.UNACK)).pageLink( + .severityList(Arrays.asList(AlarmSeverity.CRITICAL)) + .statusList(Arrays.asList(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.UNACK)).pageLink( new TimePageLink(1, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); @@ -260,7 +261,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .statusList(Arrays.asList(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.UNACK)).pageLink( new TimePageLink(1, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(0, alarms.getData().size()); @@ -275,7 +276,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .statusList(Arrays.asList(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.UNACK)).pageLink( new TimePageLink(1, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); @@ -287,7 +288,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .statusList(Arrays.asList(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.UNACK)).pageLink( new TimePageLink(1, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); @@ -301,7 +302,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .statusList(Arrays.asList(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.ACK)).pageLink( new TimePageLink(1, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); @@ -313,7 +314,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .statusList(Arrays.asList(AlarmSearchStatus.ACTIVE, AlarmSearchStatus.UNACK)).pageLink( new TimePageLink(1, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(0, alarms.getData().size()); @@ -326,7 +327,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .statusList(Arrays.asList(AlarmSearchStatus.CLEARED, AlarmSearchStatus.ACK)).pageLink( new TimePageLink(1, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); @@ -358,7 +359,7 @@ public class AlarmServiceTest extends AbstractServiceTest { tenantUser.setEmail(TEST_TENANT_EMAIL); tenantUser.setFirstName(TEST_TENANT_FIRST_NAME); tenantUser.setLastName(TEST_TENANT_LAST_NAME); - tenantUser = userService.saveUser(tenantUser); + tenantUser = userService.saveUser(TenantId.SYS_TENANT_ID, tenantUser); Assert.assertNotNull(tenantUser); @@ -370,7 +371,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .fetchOriginator(true) .pageLink(new TimePageLink(1, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); Assert.assertEquals(created, alarms.getData().get(0)); @@ -392,7 +393,7 @@ public class AlarmServiceTest extends AbstractServiceTest { tenantUser2.setEmail(2 + TEST_TENANT_EMAIL); tenantUser2.setFirstName(TEST_TENANT_FIRST_NAME); tenantUser2.setLastName(TEST_TENANT_LAST_NAME); - tenantUser2 = userService.saveUser(tenantUser2); + tenantUser2 = userService.saveUser(TenantId.SYS_TENANT_ID, tenantUser2); Assert.assertNotNull(tenantUser2); pageLink.setAssigneeId(tenantUser2.getId()); @@ -465,7 +466,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .status(AlarmStatus.ACTIVE_UNACK).pageLink( new TimePageLink(10, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); Assert.assertEquals(tenantAlarm, new AlarmInfo(alarms.getData().get(0))); @@ -751,7 +752,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .status(AlarmStatus.ACTIVE_UNACK).pageLink( new TimePageLink(10, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarmsInfoData.getData()); Assert.assertEquals(1, alarmsInfoData.getData().size()); Assert.assertEquals(created, new AlarmInfo(alarmsInfoData.getData().get(0))); @@ -762,7 +763,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .status(AlarmStatus.ACTIVE_UNACK).pageLink( new TimePageLink(10, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarmsInfoData.getData()); Assert.assertEquals(1, alarmsInfoData.getData().size()); Assert.assertEquals(created, new AlarmInfo(alarmsInfoData.getData().get(0))); @@ -773,7 +774,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .status(AlarmStatus.ACTIVE_UNACK).pageLink( new TimePageLink(10, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarmsInfoData.getData()); Assert.assertEquals(1, alarmsInfoData.getData().size()); Assert.assertEquals(created, new AlarmInfo(alarmsInfoData.getData().get(0))); @@ -898,7 +899,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .status(AlarmStatus.ACTIVE_UNACK).pageLink( new TimePageLink(1, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); @@ -909,12 +910,12 @@ public class AlarmServiceTest extends AbstractServiceTest { .status(AlarmStatus.ACTIVE_UNACK).pageLink( new TimePageLink(1, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); - Assert.assertTrue("Alarm was not deleted when expected", alarmService.deleteAlarm(tenantId, created.getId()).isSuccessful()); + Assert.assertTrue("Alarm was not deleted when expected", alarmService.delAlarm(tenantId, created.getId()).isSuccessful()); Alarm fetched = alarmService.findAlarmByIdAsync(tenantId, created.getId()).get(); @@ -925,7 +926,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .status(AlarmStatus.ACTIVE_UNACK).pageLink( new TimePageLink(1, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(0, alarms.getData().size()); @@ -935,7 +936,7 @@ public class AlarmServiceTest extends AbstractServiceTest { .status(AlarmStatus.ACTIVE_UNACK).pageLink( new TimePageLink(1, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) - ).build()).get(); + ).build()); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(0, alarms.getData().size()); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AssetProfileServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AssetProfileServiceTest.java index 391c00fa49..0d6f0d8075 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/AssetProfileServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/AssetProfileServiceTest.java @@ -24,6 +24,7 @@ import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.asset.AssetProfileInfo; @@ -35,11 +36,14 @@ import org.thingsboard.server.dao.exception.DataValidationException; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.stream.Collectors; +import static org.assertj.core.api.Assertions.assertThat; + @DaoSqlTest public class AssetProfileServiceTest extends AbstractServiceTest { @@ -272,4 +276,97 @@ public class AssetProfileServiceTest extends AbstractServiceTest { Assert.assertEquals(1, pageData.getTotalElements()); } + @Test + public void testFindAllassetProfilesByTenantId() { + int assetProfilesCount = 4; // 3 created + default + var assetProfiles = new ArrayList(4); + + var profileC = assetProfileService.saveAssetProfile( + createAssetProfile(tenantId, "profile C")); + assetProfiles.add(assetProfileService.saveAssetProfile(profileC)); + + + var profileA = assetProfileService.saveAssetProfile( + createAssetProfile(tenantId, "profile A")); + assetProfiles.add(assetProfileService.saveAssetProfile(profileA)); + + + var profileB = assetProfileService.saveAssetProfile( + createAssetProfile(tenantId, "profile B")); + assetProfiles.add(assetProfileService.saveAssetProfile(profileB)); + + + assetProfiles.add(assetProfileService.findDefaultAssetProfile(tenantId)); + + List sortedProfileInfos = assetProfiles.stream() + .map(profile -> new EntityInfo(profile.getId(), profile.getName())) + .sorted(Comparator.comparing(EntityInfo::getName)) + .collect(Collectors.toList()); + + var assetProfileInfos = assetProfileService + .findAssetProfileNamesByTenantId(tenantId, false); + + assertThat(assetProfileInfos).isNotNull(); + assertThat(assetProfileInfos).hasSize(assetProfilesCount); + assertThat(assetProfileInfos).isEqualTo(sortedProfileInfos); + } + + @Test + public void testFindActiveOnlyassetProfilesByTenantId() { + + String profileCName = "profile C"; + assetProfileService.saveAssetProfile( + createAssetProfile(tenantId, profileCName)); + + String profileAName = "profile A"; + assetProfileService.saveAssetProfile( + createAssetProfile(tenantId, profileAName)); + + String profileBName = "profile B"; + assetProfileService.saveAssetProfile( + createAssetProfile(tenantId, profileBName)); + + + var assetProfileInfos = assetProfileService + .findAssetProfileNamesByTenantId(tenantId, true); + + assertThat(assetProfileInfos).isNotNull(); + assertThat(assetProfileInfos).isEmpty(); + + var assetC = new Asset(); + assetC.setName("Test asset C"); + assetC.setType(profileCName); + assetC.setTenantId(tenantId); + + assetC = assetService.saveAsset(assetC); + + var assetA = new Asset(); + assetA.setName("Test asset A"); + assetA.setType(profileAName); + assetA.setTenantId(tenantId); + + assetA = assetService.saveAsset(assetA); + + var assetB = new Asset(); + assetB.setName("Test asset B"); + assetB.setType(profileBName); + assetB.setTenantId(tenantId); + + assetB = assetService.saveAsset(assetB); + + assetProfileInfos = assetProfileService + .findAssetProfileNamesByTenantId(tenantId, true); + + var expected = List.of( + new EntityInfo(assetA.getAssetProfileId(), profileAName), + new EntityInfo(assetB.getAssetProfileId(), profileBName), + new EntityInfo(assetC.getAssetProfileId(), profileCName) + ); + + assertThat(assetProfileInfos).isNotEmpty(); + assertThat(assetProfileInfos).hasSize(3); + assertThat(assetProfileInfos).isEqualTo(expected); + } + + } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AssetServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AssetServiceTest.java index adb97196fd..68e7174b19 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/AssetServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/AssetServiceTest.java @@ -20,16 +20,25 @@ import org.junit.Assert; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.DefaultTransactionDefinition; +import org.springframework.transaction.support.TransactionCallbackWithoutResult; +import org.springframework.transaction.support.TransactionTemplate; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetInfo; +import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.asset.AssetDao; +import org.thingsboard.server.dao.asset.AssetProfileService; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.exception.DataValidationException; @@ -37,6 +46,7 @@ import org.thingsboard.server.dao.exception.DataValidationException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.UUID; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; @@ -46,7 +56,13 @@ public class AssetServiceTest extends AbstractServiceTest { @Autowired AssetService assetService; @Autowired + AssetDao assetDao; + @Autowired CustomerService customerService; + @Autowired + private AssetProfileService assetProfileService; + @Autowired + private PlatformTransactionManager platformTransactionManager; private IdComparator idComparator = new IdComparator<>(); @@ -75,6 +91,29 @@ public class AssetServiceTest extends AbstractServiceTest { assetService.deleteAsset(tenantId, savedAsset.getId()); } + @Test + public void testShouldNotPutInCacheRolledbackAssetProfile() { + AssetProfile assetProfile = new AssetProfile(); + assetProfile.setName(StringUtils.randomAlphabetic(10)); + assetProfile.setTenantId(tenantId); + + Asset asset = new Asset(); + asset.setName("My asset" + StringUtils.randomAlphabetic(15)); + asset.setType(assetProfile.getName()); + asset.setTenantId(tenantId); + + DefaultTransactionDefinition def = new DefaultTransactionDefinition(); + TransactionStatus status = platformTransactionManager.getTransaction(def); + try { + assetProfileService.saveAssetProfile(assetProfile); + assetService.saveAsset(asset); + } finally { + platformTransactionManager.rollback(status); + } + AssetProfile assetProfileByName = assetProfileService.findAssetProfileByName(tenantId, assetProfile.getName()); + Assert.assertNull(assetProfileByName); + } + @Test public void testSaveAssetWithEmptyName() { Asset asset = new Asset(); @@ -85,6 +124,17 @@ public class AssetServiceTest extends AbstractServiceTest { }); } + @Test + public void testSaveDeviceWithNameContains0x00_thenDataValidationException() { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + asset.setType("default"); + asset.setName("F0929906\000\000\000\000\000\000\000\000\000"); + Assertions.assertThrows(DataValidationException.class, () -> { + assetService.saveAsset(asset); + }); + } + @Test public void testSaveAssetWithEmptyTenant() { Asset asset = new Asset(); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/DataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/DataValidatorTest.java index 40262ac2b8..0eafd9aeaf 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/DataValidatorTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/DataValidatorTest.java @@ -15,62 +15,93 @@ */ package org.thingsboard.server.dao.service; +import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.thingsboard.server.dao.exception.DataValidationException; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.spy; + +@Slf4j public class DataValidatorTest { - @Test - public void validateEmail() { - String email = "aZ1_!#$%&'*+/=?`{|}~^.-@mail.io"; - DataValidator.validateEmail(email); + DataValidator dataValidator; + + @BeforeEach + void setUp() { + dataValidator = spy(DataValidator.class); } - @Test - public void validateInvalidEmail1() { - String email = "test:1@mail.io"; - Assertions.assertThrows(DataValidationException.class, () -> { - DataValidator.validateEmail(email); - }); + @ParameterizedTest + @ValueSource(strings = { + "coffee", "1", "big box", "世界", "!", "--", "~!@#$%^&*()_+=-/|\\[]{};:'`\"?<>,.", "\uD83D\uDC0C", "\041", + "Gdy Pomorze nie pomoże, to pomoże może morze, a gdy morze nie pomoże, to pomoże może Gdańsk", + }) + void validateName_thenOK(final String name) { + dataValidator.validateString("Device name", name); + dataValidator.validateString("Asset name", name); + dataValidator.validateString("Asset profile name", name); + dataValidator.validateString("Alarm type", name); + dataValidator.validateString("Customer name", name); + dataValidator.validateString("Tenant name", name); } - @Test - public void validateInvalidEmail2() { - String email = "test()1@mail.io"; - Assertions.assertThrows(DataValidationException.class, () -> { - DataValidator.validateEmail(email); - }); + + @ParameterizedTest + @ValueSource(strings = { + "", " ", " ", "\n", "\r\n", "\t", "\000", "\000\000", "\001", "\002", "\040", "\u0000", "\u0000\u0000", + "F0929906\000\000\000\000\000\000\000\000\000", "\000\000\000F0929906", + "\u0000F0929906", "F092\u00009906", "F0929906\u0000" + }) + void validateName_thenDataValidationException(final String name) { + DataValidationException exception; + exception = Assertions.assertThrows(DataValidationException.class, () -> dataValidator.validateString("Asset name", name)); + log.warn("Exception message Asset name: {}", exception.getMessage()); + assertThat(exception.getMessage()).as("message Asset name").containsPattern("Asset name .*"); + + exception = Assertions.assertThrows(DataValidationException.class, () -> dataValidator.validateString("Device name", name)); + log.warn("Exception message Device name: {}", exception.getMessage()); + assertThat(exception.getMessage()).as("message Device name").containsPattern("Device name .*"); } - @Test - public void validateInvalidEmail3() { - String email = "test[]1@mail.io"; - Assertions.assertThrows(DataValidationException.class, () -> { - DataValidator.validateEmail(email); - }); + @ParameterizedTest + @ValueSource(strings = { + "aZ1_!#$%&'*+/=?`{|}~^.-@mail.io", "support@thingsboard.io", + }) + public void validateEmail(String email) { + DataValidator.validateEmail(email); } - @Test - public void validateInvalidEmail4() { - String email = "test\\1@mail.io"; - Assertions.assertThrows(DataValidationException.class, () -> { - DataValidator.validateEmail(email); - }); + @ParameterizedTest + @ValueSource(strings = { + "test:1@mail.io", "test()1@mail.io", "test[]1@mail.io", + "test\\1@mail.io", "test\"1@mail.io", "test<>1@mail.io", + }) + public void validateEmailInvalid(String email) { + Assertions.assertThrows(DataValidationException.class, () -> DataValidator.validateEmail(email)); } - @Test - public void validateInvalidEmail5() { - String email = "test\"1@mail.io"; - Assertions.assertThrows(DataValidationException.class, () -> { - DataValidator.validateEmail(email); - }); + @ParameterizedTest + @ValueSource(strings = { + "azAZ09_.-", "topic", + }) + public void validateQueueNameOrTopic(String value) { + DataValidator.validateQueueNameOrTopic(value, "name"); + DataValidator.validateQueueNameOrTopic(value, "topic"); } - @Test - public void validateInvalidEmail6() { - String email = "test<>1@mail.io"; - Assertions.assertThrows(DataValidationException.class, () -> { - DataValidator.validateEmail(email); - }); + @ParameterizedTest + @ValueSource(strings = { + "", " ", " ", "\n", "\r\n", "\t", "\000", "\000\000", "\001", "\002", "\040", "\u0000", "\u0000\u0000", + "topic@home", "!", ",", "Łódź", + "\uD83D\uDC0C", "\041", + "F0929906\000\000\000\000\000\000\000\000\000", + }) + public void validateQueueNameOrTopicInvalid(String value) { + Assertions.assertThrows(DataValidationException.class, () -> DataValidator.validateQueueNameOrTopic(value, "name")); + Assertions.assertThrows(DataValidationException.class, () -> DataValidator.validateQueueNameOrTopic(value, "topic")); } + } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceCredentialsCacheTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceCredentialsCacheTest.java index 44119af87a..000ab3728b 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceCredentialsCacheTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceCredentialsCacheTest.java @@ -127,10 +127,10 @@ public class DeviceCredentialsCacheTest extends AbstractServiceTest { when(deviceCredentialsDao.findByCredentialsId(SYSTEM_TENANT_ID, CREDENTIALS_ID_1)).thenReturn(null); - deviceCredentialsService.findDeviceCredentialsByCredentialsId(CREDENTIALS_ID_1); - deviceCredentialsService.findDeviceCredentialsByCredentialsId(CREDENTIALS_ID_1); + deviceCredentialsService.findDeviceCredentialsByCredentialsId(CREDENTIALS_ID_1); // cache miss, DB read (not found), Cache put null value + deviceCredentialsService.findDeviceCredentialsByCredentialsId(CREDENTIALS_ID_1); // cache hit (null value, credentials not found) - verify(deviceCredentialsDao, times(3)).findByCredentialsId(SYSTEM_TENANT_ID, CREDENTIALS_ID_1); + verify(deviceCredentialsDao, times(2)).findByCredentialsId(SYSTEM_TENANT_ID, CREDENTIALS_ID_1); } private DeviceCredentialsService unwrapDeviceCredentialsService() throws Exception { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceProfileServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceProfileServiceTest.java index 16a80e438d..ac3b603f13 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceProfileServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceProfileServiceTest.java @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.EntityInfo; import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.page.PageData; @@ -41,6 +42,7 @@ import org.thingsboard.server.dao.ota.OtaPackageService; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; @@ -372,4 +374,96 @@ public class DeviceProfileServiceTest extends AbstractServiceTest { Assert.assertEquals(1, pageData.getTotalElements()); } + @Test + public void testFindAllDeviceProfilesByTenantId() { + int deviceProfilesCount = 4; // 3 created + default + var deviceProfiles = new ArrayList(4); + + var profileC = deviceProfileService.saveDeviceProfile( + createDeviceProfile(tenantId, "profile C")); + deviceProfiles.add(deviceProfileService.saveDeviceProfile(profileC)); + + + var profileA = deviceProfileService.saveDeviceProfile( + createDeviceProfile(tenantId, "profile A")); + deviceProfiles.add(deviceProfileService.saveDeviceProfile(profileA)); + + + var profileB = deviceProfileService.saveDeviceProfile( + createDeviceProfile(tenantId, "profile B")); + deviceProfiles.add(deviceProfileService.saveDeviceProfile(profileB)); + + + deviceProfiles.add(deviceProfileService.findDefaultDeviceProfile(tenantId)); + + List sortedProfileInfos = deviceProfiles.stream() + .map(profile -> new EntityInfo(profile.getId(), profile.getName())) + .sorted(Comparator.comparing(EntityInfo::getName)) + .collect(Collectors.toList()); + + var deviceProfileInfos = deviceProfileService + .findDeviceProfileNamesByTenantId(tenantId, false); + + assertThat(deviceProfileInfos).isNotNull(); + assertThat(deviceProfileInfos).hasSize(deviceProfilesCount); + assertThat(deviceProfileInfos).isEqualTo(sortedProfileInfos); + } + + @Test + public void testFindActiveOnlyDeviceProfilesByTenantId() { + + String profileCName = "profile C"; + deviceProfileService.saveDeviceProfile( + createDeviceProfile(tenantId, profileCName)); + + String profileAName = "profile A"; + deviceProfileService.saveDeviceProfile( + createDeviceProfile(tenantId, profileAName)); + + String profileBName = "profile B"; + deviceProfileService.saveDeviceProfile( + createDeviceProfile(tenantId, profileBName)); + + + var deviceProfileInfos = deviceProfileService + .findDeviceProfileNamesByTenantId(tenantId, true); + + assertThat(deviceProfileInfos).isNotNull(); + assertThat(deviceProfileInfos).isEmpty(); + + var deviceC = new Device(); + deviceC.setName("Test Device C"); + deviceC.setType(profileCName); + deviceC.setTenantId(tenantId); + + deviceC = deviceService.saveDevice(deviceC); + + var deviceA = new Device(); + deviceA.setName("Test Device A"); + deviceA.setType(profileAName); + deviceA.setTenantId(tenantId); + + deviceA = deviceService.saveDevice(deviceA); + + var deviceB = new Device(); + deviceB.setName("Test Device B"); + deviceB.setType(profileBName); + deviceB.setTenantId(tenantId); + + deviceB = deviceService.saveDevice(deviceB); + + deviceProfileInfos = deviceProfileService + .findDeviceProfileNamesByTenantId(tenantId, true); + + var expected = List.of( + new EntityInfo(deviceA.getDeviceProfileId(), profileAName), + new EntityInfo(deviceB.getDeviceProfileId(), profileBName), + new EntityInfo(deviceC.getDeviceProfileId(), profileCName) + ); + + assertThat(deviceProfileInfos).isNotEmpty(); + assertThat(deviceProfileInfos).hasSize(3); + assertThat(deviceProfileInfos).isEqualTo(expected); + } + } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceTest.java index b19af16015..3e404f8d1d 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceTest.java @@ -22,6 +22,9 @@ import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.DefaultTransactionDefinition; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceInfo; @@ -32,6 +35,8 @@ import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; @@ -72,13 +77,15 @@ public class DeviceServiceTest extends AbstractServiceTest { OtaPackageService otaPackageService; @Autowired TenantProfileService tenantProfileService; + @Autowired + private PlatformTransactionManager platformTransactionManager; private IdComparator idComparator = new IdComparator<>(); private TenantId anotherTenantId; @Before public void before() { - anotherTenantId = createTenant(); + anotherTenantId = createTenant().getId(); } @After @@ -283,6 +290,17 @@ public class DeviceServiceTest extends AbstractServiceTest { }); } + @Test + public void testSaveDeviceWithNameContains0x00_thenDataValidationException() { + Device device = new Device(); + device.setType("default"); + device.setTenantId(tenantId); + device.setName("F0929906\000\000\000\000\000\000\000\000\000"); + Assertions.assertThrows(DataValidationException.class, () -> { + deviceService.saveDevice(device); + }); + } + @Test public void testSaveDeviceWithInvalidTenant() { Device device = new Device(); @@ -294,6 +312,28 @@ public class DeviceServiceTest extends AbstractServiceTest { }); } + @Test + public void testShouldNotPutInCacheRolledbackDeviceProfile() { + DeviceProfile deviceProfile = createDeviceProfile(tenantId, "New device Profile" + StringUtils.randomAlphabetic(5)); + + + Device device = new Device(); + device.setType(deviceProfile.getName()); + device.setTenantId(tenantId); + device.setName("My device"+ StringUtils.randomAlphabetic(5)); + + DefaultTransactionDefinition def = new DefaultTransactionDefinition(); + TransactionStatus status = platformTransactionManager.getTransaction(def); + try { + deviceProfileService.saveDeviceProfile(deviceProfile); + deviceService.saveDevice(device); + } finally { + platformTransactionManager.rollback(status); + } + DeviceProfile deviceProfileByName = deviceProfileService.findDeviceProfileByName(tenantId, deviceProfile.getName()); + Assert.assertNull(deviceProfileByName); + } + @Test public void testAssignDeviceToNonExistentCustomer() { Device device = new Device(); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/EdgeEventServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/EdgeEventServiceTest.java index 63958fe1e4..26e7cc2e1f 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/EdgeEventServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/EdgeEventServiceTest.java @@ -71,7 +71,7 @@ public class EdgeEventServiceTest extends AbstractServiceTest { EdgeEvent edgeEvent = generateEdgeEvent(tenantId, edgeId, deviceId, EdgeEventActionType.ADDED); edgeEventService.saveAsync(edgeEvent).get(); - PageData edgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, new TimePageLink(1), false); + PageData edgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, 0L, null, new TimePageLink(1)); Assert.assertFalse(edgeEvents.getData().isEmpty()); EdgeEvent saved = edgeEvents.getData().get(0); @@ -113,7 +113,7 @@ public class EdgeEventServiceTest extends AbstractServiceTest { Futures.allAsList(futures).get(); TimePageLink pageLink = new TimePageLink(2, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), startTime, endTime); - PageData edgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, true); + PageData edgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, 0L, null, pageLink); Assert.assertNotNull(edgeEvents.getData()); Assert.assertEquals(2, edgeEvents.getData().size()); @@ -122,7 +122,7 @@ public class EdgeEventServiceTest extends AbstractServiceTest { Assert.assertTrue(edgeEvents.hasNext()); Assert.assertNotNull(pageLink.nextPageLink()); - edgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink.nextPageLink(), true); + edgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, 0L, null, pageLink.nextPageLink()); Assert.assertNotNull(edgeEvents.getData()); Assert.assertEquals(1, edgeEvents.getData().size()); @@ -132,26 +132,6 @@ public class EdgeEventServiceTest extends AbstractServiceTest { edgeEventService.cleanupEvents(1); } - @Test - public void findEdgeEventsWithTsUpdateAndWithout() throws Exception { - EdgeId edgeId = new EdgeId(Uuids.timeBased()); - DeviceId deviceId = new DeviceId(Uuids.timeBased()); - TenantId tenantId = TenantId.fromUUID(Uuids.timeBased()); - TimePageLink pageLink = new TimePageLink(1, 0, null, new SortOrder("createdTime", SortOrder.Direction.ASC)); - - EdgeEvent edgeEventWithTsUpdate = generateEdgeEvent(tenantId, edgeId, deviceId, EdgeEventActionType.TIMESERIES_UPDATED); - edgeEventService.saveAsync(edgeEventWithTsUpdate).get(); - - PageData allEdgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, true); - PageData edgeEventsWithoutTsUpdate = edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, false); - - Assert.assertNotNull(allEdgeEvents.getData()); - Assert.assertNotNull(edgeEventsWithoutTsUpdate.getData()); - Assert.assertEquals(1, allEdgeEvents.getData().size()); - Assert.assertEquals(allEdgeEvents.getData().get(0).getUuidId(), edgeEventWithTsUpdate.getUuidId()); - Assert.assertTrue(edgeEventsWithoutTsUpdate.getData().isEmpty()); - } - private ListenableFuture saveEdgeEventWithProvidedTime(long time, EdgeId edgeId, EntityId entityId, TenantId tenantId) throws Exception { EdgeEvent edgeEvent = generateEdgeEvent(tenantId, edgeId, entityId, EdgeEventActionType.ADDED); edgeEvent.setId(new EdgeEventId(Uuids.startOf(time))); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/EdgeServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/EdgeServiceTest.java index 02515dcfbe..65a5797c08 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/EdgeServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/EdgeServiceTest.java @@ -44,6 +44,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.function.Function; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; @@ -639,7 +640,7 @@ public class EdgeServiceTest extends AbstractServiceTest { ruleChainMetaData3.setNodes(Arrays.asList(ruleNode1, ruleNode2)); ruleChainMetaData3.setFirstNodeIndex(0); ruleChainMetaData3.setRuleChainId(ruleChain3.getId()); - ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData3); + ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData3, Function.identity()); ruleChainService.assignRuleChainToEdge(tenantId, ruleChain3.getId(), savedEdge.getId()); @@ -649,4 +650,23 @@ public class EdgeServiceTest extends AbstractServiceTest { Assert.assertEquals("{\"Rule Chain #3\":[\"Rule Chain #1\",\"Rule Chain #2\"]}", missingToRelatedRuleChains); } + @Test + public void testFindEdgesByTenantProfileId() { + Tenant tenant1 = createTenant(); + Tenant tenant2 = createTenant(); + Assert.assertNotNull(tenant1); + Assert.assertNotNull(tenant2); + + Edge edge1 = constructEdge(tenant1.getId(), "Tenant1 edge", "default"); + Edge edge2 = constructEdge(tenant2.getId(), "Tenant2 edge", "default"); + Edge savedEdge1 = edgeService.saveEdge(edge1); + Edge savedEdge2 = edgeService.saveEdge(edge2); + Assert.assertNotNull(savedEdge1); + Assert.assertNotNull(savedEdge2); + Assert.assertEquals(tenant1.getTenantProfileId(), tenant2.getTenantProfileId()); + + PageData edgesPageData = edgeService.findEdgesByTenantProfileId(tenant2.getTenantProfileId(), + new PageLink(1000)); + Assert.assertEquals(2, edgesPageData.getTotalElements()); + } } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/EntityServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/EntityServiceTest.java index 77f5e87020..f41e92a305 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/EntityServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/EntityServiceTest.java @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.CustomerId; @@ -70,6 +71,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.device.DeviceService; @@ -79,6 +81,7 @@ import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.sql.relation.RelationRepository; import org.thingsboard.server.dao.timeseries.TimeseriesService; +import org.thingsboard.server.dao.user.UserService; import java.util.ArrayList; import java.util.Arrays; @@ -105,6 +108,8 @@ public class EntityServiceTest extends AbstractServiceTest { @Autowired AssetService assetService; @Autowired + UserService userService; + @Autowired AttributesService attributesService; @Autowired DeviceService deviceService; @@ -227,6 +232,41 @@ public class EntityServiceTest extends AbstractServiceTest { Assert.assertEquals(0, count); } + @Test + public void testCountHierarchicalUserEntitiesByQuery() throws InterruptedException { + List users = new ArrayList<>(); + createTestUserRelations(tenantId, users); + + RelationsQueryFilter filter = new RelationsQueryFilter(); + filter.setRootEntity(tenantId); + filter.setDirection(EntitySearchDirection.FROM); + + EntityDataPageLink pageLink = new EntityDataPageLink(10, 0, null, null); + List entityFields = Arrays.asList(new EntityKey(EntityKeyType.ENTITY_FIELD, "name"), new EntityKey(EntityKeyType.ENTITY_FIELD, "phone")); + + EntityDataQuery query = new EntityDataQuery(filter, pageLink, entityFields, null, null); + + PageData entityDataByQuery = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query); + List data = entityDataByQuery.getData(); + Assert.assertEquals(data.size(), 5); + data.forEach(entityData -> Assert.assertNotNull(entityData.getLatest().get(EntityKeyType.ENTITY_FIELD).get("phone"))); + + } + + private void createTestUserRelations(TenantId tenantId, List users) { + for (int i = 0; i < ENTITY_COUNT; i++) { + User user = new User(); + user.setTenantId(tenantId); + user.setAuthority(Authority.TENANT_ADMIN); + user.setEmail(StringUtils.randomAlphabetic(10) + "@gmail.com"); + user.setPhone(StringUtils.randomNumeric(10)); + user = userService.saveUser(tenantId, user); + users.add(user); + createRelation(tenantId, "Contains", tenantId, user.getId()); + } + } + + @Test public void testCountEdgeEntitiesByQuery() throws InterruptedException { List edges = new ArrayList<>(); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/OtaPackageServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/OtaPackageServiceTest.java index 85dc7cd7f0..03406a97f7 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/OtaPackageServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/OtaPackageServiceTest.java @@ -102,7 +102,7 @@ public class OtaPackageServiceTest extends AbstractServiceTest { assertThatThrownBy(() -> createAndSaveFirmware(tenantId, "2")) .isInstanceOf(DataValidationException.class) - .hasMessageContaining("Failed to create the ota package, files size limit is exhausted %d bytes!", DATA_SIZE); + .hasMessageContaining("Ota packages total size exceeds the maximum of %d bytes", DATA_SIZE); } @Test diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/RelationServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/RelationServiceTest.java index 3bbb71306b..148f969d48 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/RelationServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/RelationServiceTest.java @@ -131,6 +131,31 @@ public class RelationServiceTest extends AbstractServiceTest { Assert.assertFalse(relationService.checkRelation(SYSTEM_TENANT_ID, childId, subChildId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON)); } + @Test + public void testDeleteEntityCommonRelations() { + AssetId parentId = new AssetId(Uuids.timeBased()); + AssetId childId = new AssetId(Uuids.timeBased()); + AssetId subChildId = new AssetId(Uuids.timeBased()); + + EntityRelation relationA = new EntityRelation(parentId, childId, EntityRelation.CONTAINS_TYPE); + EntityRelation relationB = new EntityRelation(childId, subChildId, EntityRelation.CONTAINS_TYPE); + EntityRelation relationC = new EntityRelation(parentId, childId, EntityRelation.MANAGES_TYPE, RelationTypeGroup.EDGE); + EntityRelation relationD = new EntityRelation(childId, subChildId, EntityRelation.MANAGES_TYPE, RelationTypeGroup.EDGE); + + saveRelation(relationA); + saveRelation(relationB); + saveRelation(relationC); + saveRelation(relationD); + + relationService.deleteEntityCommonRelations(SYSTEM_TENANT_ID, childId); + + Assert.assertFalse(relationService.checkRelation(SYSTEM_TENANT_ID, parentId, childId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON)); + Assert.assertFalse(relationService.checkRelation(SYSTEM_TENANT_ID, childId, subChildId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON)); + + Assert.assertTrue(relationService.checkRelation(SYSTEM_TENANT_ID, parentId, childId, EntityRelation.MANAGES_TYPE, RelationTypeGroup.EDGE)); + Assert.assertTrue(relationService.checkRelation(SYSTEM_TENANT_ID, childId, subChildId, EntityRelation.MANAGES_TYPE, RelationTypeGroup.EDGE)); + } + @Test public void testFindFrom() throws ExecutionException, InterruptedException { AssetId parentA = new AssetId(Uuids.timeBased()); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/RuleChainServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/RuleChainServiceTest.java index e821a1e223..dd68e4ae76 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/RuleChainServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/RuleChainServiceTest.java @@ -40,6 +40,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.function.Function; /** * Created by igor on 3/13/18. @@ -280,7 +281,7 @@ public class RuleChainServiceTest extends AbstractServiceTest { ruleNodes.set(name3Index, ruleNode4); - Assert.assertTrue(ruleChainService.saveRuleChainMetaData(tenantId, savedRuleChainMetaData).isSuccess()); + Assert.assertTrue(ruleChainService.saveRuleChainMetaData(tenantId, savedRuleChainMetaData, Function.identity()).isSuccess()); RuleChainMetaData updatedRuleChainMetaData = ruleChainService.loadRuleChainMetaData(tenantId, savedRuleChainMetaData.getRuleChainId()); Assert.assertEquals(3, updatedRuleChainMetaData.getNodes().size()); @@ -311,14 +312,14 @@ public class RuleChainServiceTest extends AbstractServiceTest { @Test public void testUpdateRuleChainMetaDataWithCirclingRelation() { Assertions.assertThrows(DataValidationException.class, () -> { - ruleChainService.saveRuleChainMetaData(tenantId, createRuleChainMetadataWithCirclingRelation()); + ruleChainService.saveRuleChainMetaData(tenantId, createRuleChainMetadataWithCirclingRelation(), Function.identity()); }); } @Test public void testUpdateRuleChainMetaDataWithCirclingRelation2() { Assertions.assertThrows(DataValidationException.class, () -> { - ruleChainService.saveRuleChainMetaData(tenantId, createRuleChainMetadataWithCirclingRelation2()); + ruleChainService.saveRuleChainMetaData(tenantId, createRuleChainMetadataWithCirclingRelation2(), Function.identity()); }); } @@ -395,7 +396,7 @@ public class RuleChainServiceTest extends AbstractServiceTest { ruleChainMetaData.addConnectionInfo(0,2,"fail"); ruleChainMetaData.addConnectionInfo(1,2,"success"); - Assert.assertTrue(ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData).isSuccess()); + Assert.assertTrue(ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData, Function.identity()).isSuccess()); return ruleChainService.loadRuleChainMetaData(tenantId, ruleChainMetaData.getRuleChainId()); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/TbCacheSerializationTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/TbCacheSerializationTest.java new file mode 100644 index 0000000000..74306e6234 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/TbCacheSerializationTest.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.server.cache.TbTransactionalCache; +import org.thingsboard.server.common.data.EntitySubtype; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@DaoSqlTest +public class TbCacheSerializationTest extends AbstractServiceTest { + + @Autowired + TbTransactionalCache> alarmTypesCache; + + @Test + public void AlarmTypesSerializationTest() { + var typesCount = 13; + TenantId tenantId = new TenantId(UUID.randomUUID()); + List types = new ArrayList<>(typesCount); + for (int i = 0; i < typesCount; i++) { + types.add(new EntitySubtype(tenantId, EntityType.ALARM, "alarm_type_" + i)); + } + PageData alarmTypesPage = new PageData<>(types, 1, typesCount, false); + alarmTypesCache.put(tenantId, alarmTypesPage); + PageData foundAlarmTypes = alarmTypesCache.get(tenantId).get(); + Assert.assertEquals(alarmTypesPage, foundAlarmTypes); + } +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/TenantProfileServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/TenantProfileServiceTest.java index 51f8aabb57..94d3b8f98e 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/TenantProfileServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/TenantProfileServiceTest.java @@ -24,6 +24,7 @@ import org.junit.jupiter.api.Assertions; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityInfo; +import org.thingsboard.server.common.data.FSTUtils; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.id.TenantId; @@ -45,6 +46,9 @@ import java.util.Collections; import java.util.List; import java.util.stream.Collectors; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + @DaoSqlTest public class TenantProfileServiceTest extends AbstractServiceTest { @@ -188,17 +192,6 @@ public class TenantProfileServiceTest extends AbstractServiceTest { }); } - @Test - public void testSaveSameTenantProfileWithDifferentIsolatedTbRuleEngine() { - TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); - TenantProfile savedTenantProfile = tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, tenantProfile); - savedTenantProfile.setIsolatedTbRuleEngine(true); - addMainQueueConfig(savedTenantProfile); - Assertions.assertThrows(DataValidationException.class, () -> { - tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, savedTenantProfile); - }); - } - @Test public void testDeleteTenantProfileWithExistingTenant() { TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); @@ -306,6 +299,29 @@ public class TenantProfileServiceTest extends AbstractServiceTest { } + @Test + public void testTenantProfileSerialization_fst() { + TenantProfile tenantProfile = new TenantProfile(); + TenantProfileData profileData = new TenantProfileData(); + tenantProfile.setProfileData(profileData); + profileData.setConfiguration(new DefaultTenantProfileConfiguration()); + addMainQueueConfig(tenantProfile); + + byte[] serialized = assertDoesNotThrow(() -> { + return FSTUtils.encode(tenantProfile); + }); + assertDoesNotThrow(() -> { + FSTUtils.encode(profileData); + }); + + TenantProfile deserialized = assertDoesNotThrow(() -> { + return FSTUtils.decode(serialized); + }); + assertThat(deserialized).isEqualTo(tenantProfile); + assertThat(deserialized.getProfileData()).isNotNull(); + assertThat(deserialized.getProfileData().getQueueConfiguration()).isNotEmpty(); + } + public static TenantProfile createTenantProfile(String name) { TenantProfile tenantProfile = new TenantProfile(); tenantProfile.setName(name); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/TenantServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/TenantServiceTest.java index 5872a5846b..c7ae0aabb6 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/TenantServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/TenantServiceTest.java @@ -73,6 +73,7 @@ import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.widget.WidgetsBundleService; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -615,7 +616,7 @@ public class TenantServiceTest extends AbstractServiceTest { resource.setResourceType(ResourceType.LWM2M_MODEL); resource.setFileName("filename.txt"); resource.setResourceKey("Test resource key"); - resource.setData("Some super test data"); + resource.setData("Some super test data".getBytes(StandardCharsets.UTF_8)); return resourceService.saveResource(resource); } @@ -716,7 +717,7 @@ public class TenantServiceTest extends AbstractServiceTest { user.setFirstName("tenantAdmin"); user.setLastName("tenantAdmin"); user.setTenantId(tenant.getId()); - return userService.saveUser(user); + return userService.saveUser(TenantId.SYS_TENANT_ID, user); } private Tenant createAndSaveTenant(TenantProfile tenantProfile) { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/UserServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/UserServiceTest.java index fcd57e5b3a..711d3ed805 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/UserServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/UserServiceTest.java @@ -58,7 +58,7 @@ public class UserServiceTest extends AbstractServiceTest { tenantAdmin.setAuthority(Authority.TENANT_ADMIN); tenantAdmin.setTenantId(tenantId); tenantAdmin.setEmail("tenant@thingsboard.org"); - userService.saveUser(tenantAdmin); + userService.saveUser(TenantId.SYS_TENANT_ID, tenantAdmin); Customer customer = new Customer(); customer.setTenantId(tenantId); @@ -70,7 +70,7 @@ public class UserServiceTest extends AbstractServiceTest { customerUser.setTenantId(tenantId); customerUser.setCustomerId(savedCustomer.getId()); customerUser.setEmail("customer@thingsboard.org"); - customerUser = userService.saveUser(customerUser); + customerUser = userService.saveUser(tenantId, customerUser); userSettings = createUserSettings(customerUser.getId()); } @@ -114,7 +114,7 @@ public class UserServiceTest extends AbstractServiceTest { user.setAuthority(Authority.TENANT_ADMIN); user.setTenantId(tenantAdminUser.getTenantId()); user.setEmail("tenant2@thingsboard.org"); - User savedUser = userService.saveUser(user); + User savedUser = userService.saveUser(TenantId.SYS_TENANT_ID, user); Assert.assertNotNull(savedUser); Assert.assertNotNull(savedUser.getId()); Assert.assertTrue(savedUser.getCreatedTime() > 0); @@ -130,12 +130,12 @@ public class UserServiceTest extends AbstractServiceTest { savedUser.setFirstName("Joe"); savedUser.setLastName("Downs"); - userService.saveUser(savedUser); + userService.saveUser(TenantId.SYS_TENANT_ID, savedUser); savedUser = userService.findUserById(tenantId, savedUser.getId()); Assert.assertEquals("Joe", savedUser.getFirstName()); Assert.assertEquals("Downs", savedUser.getLastName()); - userService.deleteUser(tenantId, savedUser.getId()); + userService.deleteUser(tenantId, savedUser); } @Test @@ -143,7 +143,7 @@ public class UserServiceTest extends AbstractServiceTest { User tenantAdminUser = userService.findUserByEmail(tenantId, "tenant@thingsboard.org"); tenantAdminUser.setEmail("sysadmin@thingsboard.org"); Assertions.assertThrows(DataValidationException.class, () -> { - userService.saveUser(tenantAdminUser); + userService.saveUser(tenantId, tenantAdminUser); }); } @@ -152,7 +152,7 @@ public class UserServiceTest extends AbstractServiceTest { User tenantAdminUser = userService.findUserByEmail(tenantId, "tenant@thingsboard.org"); tenantAdminUser.setEmail("tenant_thingsboard.org"); Assertions.assertThrows(DataValidationException.class, () -> { - userService.saveUser(tenantAdminUser); + userService.saveUser(tenantId, tenantAdminUser); }); } @@ -161,7 +161,7 @@ public class UserServiceTest extends AbstractServiceTest { User tenantAdminUser = userService.findUserByEmail(tenantId, "tenant@thingsboard.org"); tenantAdminUser.setEmail(null); Assertions.assertThrows(DataValidationException.class, () -> { - userService.saveUser(tenantAdminUser); + userService.saveUser(tenantId, tenantAdminUser); }); } @@ -170,7 +170,7 @@ public class UserServiceTest extends AbstractServiceTest { User tenantAdminUser = userService.findUserByEmail(tenantId, "tenant@thingsboard.org"); tenantAdminUser.setTenantId(null); Assertions.assertThrows(DataValidationException.class, () -> { - userService.saveUser(tenantAdminUser); + userService.saveUser(tenantId, tenantAdminUser); }); } @@ -181,14 +181,14 @@ public class UserServiceTest extends AbstractServiceTest { user.setAuthority(Authority.TENANT_ADMIN); user.setTenantId(tenantAdminUser.getTenantId()); user.setEmail("tenant2@thingsboard.org"); - User savedUser = userService.saveUser(user); + User savedUser = userService.saveUser(TenantId.SYS_TENANT_ID, user); Assert.assertNotNull(savedUser); Assert.assertNotNull(savedUser.getId()); User foundUser = userService.findUserById(tenantId, savedUser.getId()); Assert.assertNotNull(foundUser); UserCredentials userCredentials = userService.findUserCredentialsByUserId(tenantId, foundUser.getId()); Assert.assertNotNull(userCredentials); - userService.deleteUser(tenantId, foundUser.getId()); + userService.deleteUser(tenantId, foundUser); userCredentials = userService.findUserCredentialsByUserId(tenantId, foundUser.getId()); foundUser = userService.findUserById(tenantId, foundUser.getId()); Assert.assertNull(foundUser); @@ -204,7 +204,7 @@ public class UserServiceTest extends AbstractServiceTest { Assert.assertEquals(1, users.size()); Assert.assertEquals(tenantAdminUser, users.get(0)); - TenantId secondTenantId = createTenant(); + TenantId secondTenantId = createTenant().getId(); List tenantAdmins = new ArrayList<>(); for (int i = 0; i < 124; i++) { @@ -212,7 +212,7 @@ public class UserServiceTest extends AbstractServiceTest { user.setAuthority(Authority.TENANT_ADMIN); user.setTenantId(secondTenantId); user.setEmail("testTenant" + i + "@thingsboard.org"); - tenantAdmins.add(userService.saveUser(user)); + tenantAdmins.add(userService.saveUser(TenantId.SYS_TENANT_ID, user)); } List loadedTenantAdmins = new ArrayList<>(); @@ -252,7 +252,7 @@ public class UserServiceTest extends AbstractServiceTest { String email = email1 + suffix + "@thingsboard.org"; email = i % 2 == 0 ? email.toLowerCase() : email.toUpperCase(); user.setEmail(email); - tenantAdminsEmail1.add(userService.saveUser(user)); + tenantAdminsEmail1.add(userService.saveUser(TenantId.SYS_TENANT_ID, user)); } String email2 = "testEmail2"; @@ -266,7 +266,7 @@ public class UserServiceTest extends AbstractServiceTest { String email = email2 + suffix + "@thingsboard.org"; email = i % 2 == 0 ? email.toLowerCase() : email.toUpperCase(); user.setEmail(email); - tenantAdminsEmail2.add(userService.saveUser(user)); + tenantAdminsEmail2.add(userService.saveUser(TenantId.SYS_TENANT_ID, user)); } List loadedTenantAdminsEmail1 = new ArrayList<>(); @@ -301,7 +301,7 @@ public class UserServiceTest extends AbstractServiceTest { Assert.assertEquals(tenantAdminsEmail2, loadedTenantAdminsEmail2); for (User user : loadedTenantAdminsEmail1) { - userService.deleteUser(tenantId, user.getId()); + userService.deleteUser(tenantId, user); } pageLink = new PageLink(4, 0, email1); @@ -310,7 +310,7 @@ public class UserServiceTest extends AbstractServiceTest { Assert.assertEquals(0, pageData.getData().size()); for (User user : loadedTenantAdminsEmail2) { - userService.deleteUser(tenantId, user.getId()); + userService.deleteUser(tenantId, user); } pageLink = new PageLink(4, 0, email2); @@ -343,7 +343,7 @@ public class UserServiceTest extends AbstractServiceTest { user.setTenantId(tenantId); user.setCustomerId(customerId); user.setEmail("testCustomer" + i + "@thingsboard.org"); - customerUsers.add(userService.saveUser(user)); + customerUsers.add(userService.saveUser(tenantId, user)); } List loadedCustomerUsers = new ArrayList<>(); @@ -390,7 +390,7 @@ public class UserServiceTest extends AbstractServiceTest { String email = email1 + suffix + "@thingsboard.org"; email = i % 2 == 0 ? email.toLowerCase() : email.toUpperCase(); user.setEmail(email); - customerUsersEmail1.add(userService.saveUser(user)); + customerUsersEmail1.add(userService.saveUser(tenantId, user)); } String email2 = "testEmail2"; @@ -405,7 +405,7 @@ public class UserServiceTest extends AbstractServiceTest { String email = email2 + suffix + "@thingsboard.org"; email = i % 2 == 0 ? email.toLowerCase() : email.toUpperCase(); user.setEmail(email); - customerUsersEmail2.add(userService.saveUser(user)); + customerUsersEmail2.add(userService.saveUser(tenantId, user)); } List loadedCustomerUsersEmail1 = new ArrayList<>(); @@ -440,7 +440,7 @@ public class UserServiceTest extends AbstractServiceTest { Assert.assertEquals(customerUsersEmail2, loadedCustomerUsersEmail2); for (User user : loadedCustomerUsersEmail1) { - userService.deleteUser(tenantId, user.getId()); + userService.deleteUser(tenantId, user); } pageLink = new PageLink(4, 0, email1); @@ -449,7 +449,7 @@ public class UserServiceTest extends AbstractServiceTest { Assert.assertEquals(0, pageData.getData().size()); for (User user : loadedCustomerUsersEmail2) { - userService.deleteUser(tenantId, user.getId()); + userService.deleteUser(tenantId, user); } pageLink = new PageLink(4, 0, email2); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/WidgetTypeServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/WidgetTypeServiceTest.java index 9a2b5e6180..c1802d9865 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/WidgetTypeServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/WidgetTypeServiceTest.java @@ -23,6 +23,7 @@ import org.junit.jupiter.api.Assertions; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetsBundle; @@ -35,12 +36,14 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; @DaoSqlTest public class WidgetTypeServiceTest extends AbstractServiceTest { @Autowired WidgetsBundleService widgetsBundleService; + @Autowired WidgetTypeService widgetTypeService; @@ -48,27 +51,19 @@ public class WidgetTypeServiceTest extends AbstractServiceTest { @Test public void testSaveWidgetType() throws IOException { - WidgetsBundle widgetsBundle = new WidgetsBundle(); - widgetsBundle.setTenantId(tenantId); - widgetsBundle.setTitle("Widgets bundle"); - WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); - - WidgetTypeDetails widgetType = new WidgetTypeDetails(); widgetType.setTenantId(tenantId); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); widgetType.setName("Widget Type"); widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); WidgetTypeDetails savedWidgetType = widgetTypeService.saveWidgetType(widgetType); Assert.assertNotNull(savedWidgetType); Assert.assertNotNull(savedWidgetType.getId()); - Assert.assertNotNull(savedWidgetType.getAlias()); + Assert.assertNotNull(savedWidgetType.getFqn()); Assert.assertTrue(savedWidgetType.getCreatedTime() > 0); Assert.assertEquals(widgetType.getTenantId(), savedWidgetType.getTenantId()); Assert.assertEquals(widgetType.getName(), savedWidgetType.getName()); Assert.assertEquals(widgetType.getDescriptor(), savedWidgetType.getDescriptor()); - Assert.assertEquals(savedWidgetsBundle.getAlias(), savedWidgetType.getBundleAlias()); savedWidgetType.setName("New Widget Type"); @@ -76,34 +71,13 @@ public class WidgetTypeServiceTest extends AbstractServiceTest { WidgetType foundWidgetType = widgetTypeService.findWidgetTypeById(tenantId, savedWidgetType.getId()); Assert.assertEquals(foundWidgetType.getName(), savedWidgetType.getName()); - widgetsBundleService.deleteWidgetsBundle(tenantId, savedWidgetsBundle.getId()); + widgetTypeService.deleteWidgetType(tenantId, savedWidgetType.getId()); } @Test public void testSaveWidgetTypeWithEmptyName() throws IOException { - WidgetsBundle widgetsBundle = new WidgetsBundle(); - widgetsBundle.setTenantId(tenantId); - widgetsBundle.setTitle("Widgets bundle"); - WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); - WidgetTypeDetails widgetType = new WidgetTypeDetails(); widgetType.setTenantId(tenantId); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); - widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); - try { - Assertions.assertThrows(DataValidationException.class, () -> { - widgetTypeService.saveWidgetType(widgetType); - }); - } finally { - widgetsBundleService.deleteWidgetsBundle(tenantId, savedWidgetsBundle.getId()); - } - } - - @Test - public void testSaveWidgetTypeWithEmptyBundleAlias() throws IOException { - WidgetTypeDetails widgetType = new WidgetTypeDetails(); - widgetType.setTenantId(tenantId); - widgetType.setName("Widget Type"); widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); Assertions.assertThrows(DataValidationException.class, () -> { widgetTypeService.saveWidgetType(widgetType); @@ -112,51 +86,19 @@ public class WidgetTypeServiceTest extends AbstractServiceTest { @Test public void testSaveWidgetTypeWithEmptyDescriptor() throws IOException { - WidgetsBundle widgetsBundle = new WidgetsBundle(); - widgetsBundle.setTenantId(tenantId); - widgetsBundle.setTitle("Widgets bundle"); - WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); - WidgetTypeDetails widgetType = new WidgetTypeDetails(); widgetType.setTenantId(tenantId); widgetType.setName("Widget Type"); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); widgetType.setDescriptor(JacksonUtil.fromString("{}", JsonNode.class)); - try { - Assertions.assertThrows(DataValidationException.class, () -> { - widgetTypeService.saveWidgetType(widgetType); - }); - } finally { - widgetsBundleService.deleteWidgetsBundle(tenantId, savedWidgetsBundle.getId()); - } + Assertions.assertThrows(DataValidationException.class, () -> { + widgetTypeService.saveWidgetType(widgetType); + }); } @Test public void testSaveWidgetTypeWithInvalidTenant() throws IOException { - WidgetsBundle widgetsBundle = new WidgetsBundle(); - widgetsBundle.setTenantId(tenantId); - widgetsBundle.setTitle("Widgets bundle"); - WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); - WidgetTypeDetails widgetType = new WidgetTypeDetails(); widgetType.setTenantId(TenantId.fromUUID(Uuids.timeBased())); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); - widgetType.setName("Widget Type"); - widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); - try { - Assertions.assertThrows(DataValidationException.class, () -> { - widgetTypeService.saveWidgetType(widgetType); - }); - } finally { - widgetsBundleService.deleteWidgetsBundle(tenantId, savedWidgetsBundle.getId()); - } - } - - @Test - public void testSaveWidgetTypeWithInvalidBundleAlias() throws IOException { - WidgetTypeDetails widgetType = new WidgetTypeDetails(); - widgetType.setTenantId(tenantId); - widgetType.setBundleAlias("some_alias"); widgetType.setName("Widget Type"); widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); Assertions.assertThrows(DataValidationException.class, () -> { @@ -166,14 +108,8 @@ public class WidgetTypeServiceTest extends AbstractServiceTest { @Test public void testUpdateWidgetTypeTenant() throws IOException { - WidgetsBundle widgetsBundle = new WidgetsBundle(); - widgetsBundle.setTenantId(tenantId); - widgetsBundle.setTitle("Widgets bundle"); - WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); - WidgetTypeDetails widgetType = new WidgetTypeDetails(); widgetType.setTenantId(tenantId); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); widgetType.setName("Widget Type"); widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); WidgetTypeDetails savedWidgetType = widgetTypeService.saveWidgetType(widgetType); @@ -183,106 +119,57 @@ public class WidgetTypeServiceTest extends AbstractServiceTest { widgetTypeService.saveWidgetType(savedWidgetType); }); } finally { - widgetsBundleService.deleteWidgetsBundle(tenantId, savedWidgetsBundle.getId()); + widgetTypeService.deleteWidgetType(tenantId, savedWidgetType.getId()); } } @Test - public void testUpdateWidgetTypeBundleAlias() throws IOException { - WidgetsBundle widgetsBundle = new WidgetsBundle(); - widgetsBundle.setTenantId(tenantId); - widgetsBundle.setTitle("Widgets bundle"); - WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); - + public void testUpdateWidgetTypeFqn() throws IOException { WidgetTypeDetails widgetType = new WidgetTypeDetails(); widgetType.setTenantId(tenantId); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); widgetType.setName("Widget Type"); widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); WidgetTypeDetails savedWidgetType = widgetTypeService.saveWidgetType(widgetType); - savedWidgetType.setBundleAlias("some_alias"); + savedWidgetType.setFqn("some_fqn"); try { Assertions.assertThrows(DataValidationException.class, () -> { widgetTypeService.saveWidgetType(savedWidgetType); }); } finally { - widgetsBundleService.deleteWidgetsBundle(tenantId, savedWidgetsBundle.getId()); - } - } - - @Test - public void testUpdateWidgetTypeAlias() throws IOException { - WidgetsBundle widgetsBundle = new WidgetsBundle(); - widgetsBundle.setTenantId(tenantId); - widgetsBundle.setTitle("Widgets bundle"); - WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); - - WidgetTypeDetails widgetType = new WidgetTypeDetails(); - widgetType.setTenantId(tenantId); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); - widgetType.setName("Widget Type"); - widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); - WidgetTypeDetails savedWidgetType = widgetTypeService.saveWidgetType(widgetType); - savedWidgetType.setAlias("some_alias"); - try { - Assertions.assertThrows(DataValidationException.class, () -> { - widgetTypeService.saveWidgetType(savedWidgetType); - }); - } finally { - widgetsBundleService.deleteWidgetsBundle(tenantId, savedWidgetsBundle.getId()); + widgetTypeService.deleteWidgetType(tenantId, savedWidgetType.getId()); } } @Test public void testFindWidgetTypeById() throws IOException { - WidgetsBundle widgetsBundle = new WidgetsBundle(); - widgetsBundle.setTenantId(tenantId); - widgetsBundle.setTitle("Widgets bundle"); - WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); - WidgetTypeDetails widgetType = new WidgetTypeDetails(); widgetType.setTenantId(tenantId); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); widgetType.setName("Widget Type"); widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); WidgetTypeDetails savedWidgetType = widgetTypeService.saveWidgetType(widgetType); WidgetTypeDetails foundWidgetType = widgetTypeService.findWidgetTypeDetailsById(tenantId, savedWidgetType.getId()); Assert.assertNotNull(foundWidgetType); Assert.assertEquals(savedWidgetType, foundWidgetType); - - widgetsBundleService.deleteWidgetsBundle(tenantId, savedWidgetsBundle.getId()); + widgetTypeService.deleteWidgetType(tenantId, savedWidgetType.getId()); } @Test - public void testFindWidgetTypeByTenantIdBundleAliasAndAlias() throws IOException { - WidgetsBundle widgetsBundle = new WidgetsBundle(); - widgetsBundle.setTenantId(tenantId); - widgetsBundle.setTitle("Widgets bundle"); - WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); - + public void testFindWidgetTypeByTenantIdAndFqn() throws IOException { WidgetTypeDetails widgetType = new WidgetTypeDetails(); widgetType.setTenantId(tenantId); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); widgetType.setName("Widget Type"); widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); WidgetType savedWidgetType = new WidgetType(widgetTypeService.saveWidgetType(widgetType)); - WidgetType foundWidgetType = widgetTypeService.findWidgetTypeByTenantIdBundleAliasAndAlias(tenantId, savedWidgetsBundle.getAlias(), savedWidgetType.getAlias()); + WidgetType foundWidgetType = widgetTypeService.findWidgetTypeByTenantIdAndFqn(tenantId, savedWidgetType.getFqn()); Assert.assertNotNull(foundWidgetType); Assert.assertEquals(savedWidgetType, foundWidgetType); - - widgetsBundleService.deleteWidgetsBundle(tenantId, savedWidgetsBundle.getId()); + widgetTypeService.deleteWidgetType(tenantId, savedWidgetType.getId()); } @Test public void testDeleteWidgetType() throws IOException { - WidgetsBundle widgetsBundle = new WidgetsBundle(); - widgetsBundle.setTenantId(tenantId); - widgetsBundle.setTitle("Widgets bundle"); - WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); - WidgetTypeDetails widgetType = new WidgetTypeDetails(); widgetType.setTenantId(tenantId); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); widgetType.setName("Widget Type"); widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); WidgetType savedWidgetType = widgetTypeService.saveWidgetType(widgetType); @@ -291,12 +178,10 @@ public class WidgetTypeServiceTest extends AbstractServiceTest { widgetTypeService.deleteWidgetType(tenantId, savedWidgetType.getId()); foundWidgetType = widgetTypeService.findWidgetTypeById(tenantId, savedWidgetType.getId()); Assert.assertNull(foundWidgetType); - - widgetsBundleService.deleteWidgetsBundle(tenantId, savedWidgetsBundle.getId()); } @Test - public void testFindWidgetTypesByTenantIdAndBundleAlias() throws IOException { + public void testFindWidgetTypesByTenantIdAndWidgetsBundleId() throws IOException { WidgetsBundle widgetsBundle = new WidgetsBundle(); widgetsBundle.setTenantId(tenantId); widgetsBundle.setTitle("Widgets bundle"); @@ -306,26 +191,60 @@ public class WidgetTypeServiceTest extends AbstractServiceTest { for (int i=0;i<121;i++) { WidgetTypeDetails widgetType = new WidgetTypeDetails(); widgetType.setTenantId(tenantId); - widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); widgetType.setName("Widget Type " + i); widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); widgetTypes.add(new WidgetType(widgetTypeService.saveWidgetType(widgetType))); } - List loadedWidgetTypes = widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(tenantId, savedWidgetsBundle.getAlias()); + List widgetTypeIds = widgetTypes.stream().map(WidgetType::getId).collect(Collectors.toList()); + + widgetTypeService.updateWidgetsBundleWidgetTypes(tenantId, savedWidgetsBundle.getId(), widgetTypeIds); + + List loadedWidgetTypes = widgetTypeService.findWidgetTypesByWidgetsBundleId(tenantId, savedWidgetsBundle.getId()); Collections.sort(widgetTypes, idComparator); Collections.sort(loadedWidgetTypes, idComparator); Assert.assertEquals(widgetTypes, loadedWidgetTypes); - widgetTypeService.deleteWidgetTypesByTenantIdAndBundleAlias(tenantId, savedWidgetsBundle.getAlias()); + for (WidgetTypeId id : widgetTypeIds) { + widgetTypeService.deleteWidgetType(tenantId, id); + } - loadedWidgetTypes = widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(tenantId, savedWidgetsBundle.getAlias()); + loadedWidgetTypes = widgetTypeService.findWidgetTypesByWidgetsBundleId(tenantId, savedWidgetsBundle.getId()); Assert.assertTrue(loadedWidgetTypes.isEmpty()); widgetsBundleService.deleteWidgetsBundle(tenantId, savedWidgetsBundle.getId()); } + @Test + public void testDeleteAllTypesFromWidgetsBundle() { + WidgetsBundle widgetsBundle = new WidgetsBundle(); + widgetsBundle.setTenantId(tenantId); + widgetsBundle.setTitle("Widgets bundle"); + WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); + + List widgetTypes = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + WidgetTypeDetails widgetType = new WidgetTypeDetails(); + widgetType.setTenantId(tenantId); + widgetType.setName("Widget Type " + i); + widgetType.setDescriptor(JacksonUtil.fromString("{ \"someKey\": \"someValue\" }", JsonNode.class)); + widgetTypes.add(new WidgetType(widgetTypeService.saveWidgetType(widgetType))); + } + + List widgetTypeIds = widgetTypes.stream().map(WidgetType::getId).collect(Collectors.toList()); + + widgetTypeService.updateWidgetsBundleWidgetTypes(tenantId, savedWidgetsBundle.getId(), widgetTypeIds); + + List loadedWidgetTypes = widgetTypeService.findWidgetTypesByWidgetsBundleId(tenantId, savedWidgetsBundle.getId()); + Assert.assertEquals(widgetTypes.size(), loadedWidgetTypes.size()); + + widgetTypeService.updateWidgetsBundleWidgetTypes(tenantId, savedWidgetsBundle.getId(), Collections.emptyList()); + + loadedWidgetTypes = widgetTypeService.findWidgetTypesByWidgetsBundleId(tenantId, savedWidgetsBundle.getId()); + Assert.assertEquals(0, loadedWidgetTypes.size()); + } + } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/WidgetsBundleServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/WidgetsBundleServiceTest.java index 0f5c90b4a8..da594bb9c7 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/WidgetsBundleServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/WidgetsBundleServiceTest.java @@ -174,7 +174,7 @@ public class WidgetsBundleServiceTest extends AbstractServiceTest { PageLink pageLink = new PageLink(19); PageData pageData = null; do { - pageData = widgetsBundleService.findSystemWidgetsBundlesByPageLink(tenantId, pageLink); + pageData = widgetsBundleService.findSystemWidgetsBundlesByPageLink(tenantId, false, pageLink); loadedWidgetsBundles.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -297,7 +297,7 @@ public class WidgetsBundleServiceTest extends AbstractServiceTest { PageLink pageLink = new PageLink(17); PageData pageData = null; do { - pageData = widgetsBundleService.findAllTenantWidgetsBundlesByTenantIdAndPageLink(tenantId, pageLink); + pageData = widgetsBundleService.findAllTenantWidgetsBundlesByTenantIdAndPageLink(tenantId, false, pageLink); loadedWidgetsBundles.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -314,7 +314,7 @@ public class WidgetsBundleServiceTest extends AbstractServiceTest { loadedWidgetsBundles.clear(); pageLink = new PageLink(14); do { - pageData = widgetsBundleService.findAllTenantWidgetsBundlesByTenantIdAndPageLink(tenantId, pageLink); + pageData = widgetsBundleService.findAllTenantWidgetsBundlesByTenantIdAndPageLink(tenantId, false, pageLink); loadedWidgetsBundles.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -336,7 +336,7 @@ public class WidgetsBundleServiceTest extends AbstractServiceTest { loadedWidgetsBundles.clear(); pageLink = new PageLink(18); do { - pageData = widgetsBundleService.findAllTenantWidgetsBundlesByTenantIdAndPageLink(tenantId, pageLink); + pageData = widgetsBundleService.findAllTenantWidgetsBundlesByTenantIdAndPageLink(tenantId, false, pageLink); loadedWidgetsBundles.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/validator/AdminSettingsDataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/validator/AdminSettingsDataValidatorTest.java new file mode 100644 index 0000000000..1f021b16e7 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/validator/AdminSettingsDataValidatorTest.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.settings.AdminSettingsService; + +import java.util.UUID; + +import static org.mockito.Mockito.verify; + +@SpringBootTest(classes = AdminSettingsDataValidator.class) +class AdminSettingsDataValidatorTest { + + @MockBean + AdminSettingsService adminSettingsService; + @SpyBean + AdminSettingsDataValidator validator; + TenantId tenantId = TenantId.fromUUID(UUID.fromString("9ef79cdf-37a8-4119-b682-2e7ed4e018da")); + + @Test + void testValidateNameInvocation() { + AdminSettings adminSettings = new AdminSettings(); + adminSettings.setKey("jwt"); + adminSettings.setJsonValue(JacksonUtil.toJsonNode("{}")); + + validator.validateDataImpl(tenantId, adminSettings); + verify(validator).validateString("Key", adminSettings.getKey()); + } + +} \ No newline at end of file diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/validator/AlarmDataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/validator/AlarmDataValidatorTest.java new file mode 100644 index 0000000000..fc91a099fa --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/validator/AlarmDataValidatorTest.java @@ -0,0 +1,61 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.tenant.TenantService; + +import java.util.UUID; + +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.Mockito.verify; + +@SpringBootTest(classes = AlarmDataValidator.class) +class AlarmDataValidatorTest { + + @MockBean + TenantService tenantService; + @SpyBean + AlarmDataValidator validator; + TenantId tenantId = TenantId.fromUUID(UUID.fromString("9ef79cdf-37a8-4119-b682-2e7ed4e018da")); + + @BeforeEach + void setUp() { + willReturn(true).given(tenantService).tenantExists(tenantId); + } + + @Test + void testValidateNameInvocation() { + Alarm alarm = new Alarm(); + alarm.setType("overheating"); + alarm.setOriginator(tenantId); + alarm.setSeverity(AlarmSeverity.CRITICAL); + alarm.setCleared(false); + alarm.setAcknowledged(false); + alarm.setTenantId(tenantId); + + validator.validateDataImpl(tenantId, alarm); + verify(validator).validateString("Alarm type", alarm.getType()); + } + +} \ No newline at end of file diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/validator/AssetDataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/validator/AssetDataValidatorTest.java new file mode 100644 index 0000000000..7922cdacfa --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/validator/AssetDataValidatorTest.java @@ -0,0 +1,85 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.asset.AssetDao; +import org.thingsboard.server.dao.customer.CustomerDao; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.tenant.TenantService; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.willReturn; + +@SpringBootTest(classes = AssetDataValidator.class) +@Slf4j +class AssetDataValidatorTest { + + @MockBean + AssetDao assetDao; + @MockBean + TenantService tenantService; + @MockBean + CustomerDao customerDao; + @Autowired + AssetDataValidator validator; + TenantId tenantId = TenantId.fromUUID(UUID.fromString("9ef79cdf-37a8-4119-b682-2e7ed4e018da")); + + @BeforeEach + void setUp() { + willReturn(true).given(tenantService).tenantExists(tenantId); + } + + @ParameterizedTest + @ValueSource(strings = { + "coffee", "1", "big box", "世界", "!", "--", "~!@#$%^&*()_+=-/|\\[]{};:'`\"?<>,.", "\uD83D\uDC0C", "\041", + "Gdy Pomorze nie pomoże, to pomoże może morze, a gdy morze nie pomoże, to pomoże może Gdańsk", + }) + void testAssetName_thenOK(final String name) { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + asset.setName(name); + validator.validateDataImpl(tenantId, asset); + } + + @ParameterizedTest + @ValueSource(strings = { + "", " ", " ", "\n", "\r\n", "\t", "\000", "\000\000", "\001", "\002", "\040", "\u0000", "\u0000\u0000", + "F0929906\000\000\000\000\000\000\000\000\000", "\000\000\000F0929906", + "\u0000F0929906", "F092\u00009906", "F0929906\u0000" + }) + void testAssetName_thenDataValidationException(final String name) { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + asset.setName(name); + + DataValidationException exception = Assertions.assertThrows(DataValidationException.class, () -> validator.validateDataImpl(tenantId, asset)); + log.warn("Exception message: {}", exception.getMessage()); + assertThat(exception.getMessage()).as("message Asset name").containsPattern("Asset name .*"); + } + +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/validator/AssetProfileDataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/validator/AssetProfileDataValidatorTest.java new file mode 100644 index 0000000000..66fdd54c86 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/validator/AssetProfileDataValidatorTest.java @@ -0,0 +1,71 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.asset.AssetProfileDao; +import org.thingsboard.server.dao.asset.AssetProfileService; +import org.thingsboard.server.dao.dashboard.DashboardService; +import org.thingsboard.server.dao.queue.QueueService; +import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.tenant.TenantService; + +import java.util.UUID; + +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.Mockito.verify; + +@SpringBootTest(classes = AssetProfileDataValidator.class) +class AssetProfileDataValidatorTest { + + @MockBean + AssetProfileDao assetProfileDao; + @MockBean + AssetProfileService assetProfileService; + @MockBean + TenantService tenantService; + @MockBean + QueueService queueService; + @MockBean + RuleChainService ruleChainService; + @MockBean + DashboardService dashboardService; + @SpyBean + AssetProfileDataValidator validator; + TenantId tenantId = TenantId.fromUUID(UUID.fromString("9ef79cdf-37a8-4119-b682-2e7ed4e018da")); + + @BeforeEach + void setUp() { + willReturn(true).given(tenantService).tenantExists(tenantId); + } + + @Test + void testValidateNameInvocation() { + AssetProfile assetProfile = new AssetProfile(); + assetProfile.setName("prod"); + assetProfile.setTenantId(tenantId); + + validator.validateDataImpl(tenantId, assetProfile); + verify(validator).validateString("Asset profile name", assetProfile.getName()); + } + +} \ No newline at end of file diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/validator/BaseOtaPackageDataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/validator/BaseOtaPackageDataValidatorTest.java new file mode 100644 index 0000000000..74fac9cfb9 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/validator/BaseOtaPackageDataValidatorTest.java @@ -0,0 +1,61 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.dao.device.DeviceProfileDao; +import org.thingsboard.server.dao.tenant.TenantService; + +import java.util.UUID; + +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +class BaseOtaPackageDataValidatorTest { + + + DeviceProfileDao deviceProfileDao = mock(DeviceProfileDao.class); + TenantService tenantService = mock(TenantService.class); + BaseOtaPackageDataValidator validator = spy(BaseOtaPackageDataValidator.class); + TenantId tenantId = TenantId.fromUUID(UUID.fromString("9ef79cdf-37a8-4119-b682-2e7ed4e018da")); + + @BeforeEach + void setUp() { + willReturn(true).given(tenantService).tenantExists(tenantId); + willReturn(tenantService).given(validator).getTenantService(); + willReturn(deviceProfileDao).given(validator).getDeviceProfileDao(); + } + + @Test + void testValidateNameInvocation() { + OtaPackageInfo otaPackageInfo = new OtaPackageInfo(); + otaPackageInfo.setTitle("fw"); + otaPackageInfo.setVersion("1.0"); + otaPackageInfo.setType(OtaPackageType.FIRMWARE); + otaPackageInfo.setTenantId(tenantId); + + validator.validateImpl(otaPackageInfo); + verify(validator).validateString("OtaPackage title", otaPackageInfo.getTitle()); + } + + +} \ No newline at end of file diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/validator/ComponentDescriptorDataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/validator/ComponentDescriptorDataValidatorTest.java new file mode 100644 index 0000000000..7effbf57ba --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/validator/ComponentDescriptorDataValidatorTest.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.plugin.ComponentDescriptor; +import org.thingsboard.server.common.data.plugin.ComponentScope; +import org.thingsboard.server.common.data.plugin.ComponentType; + +import static org.mockito.Mockito.verify; + +@SpringBootTest(classes = ComponentDescriptorDataValidator.class) +class ComponentDescriptorDataValidatorTest { + @SpyBean + ComponentDescriptorDataValidator validator; + + @Test + void testValidateNameInvocation() { + ComponentDescriptor plugin = new ComponentDescriptor(); + plugin.setType(ComponentType.ENRICHMENT); + plugin.setScope(ComponentScope.SYSTEM); + plugin.setName("originator attributes"); + plugin.setClazz("org.thingsboard.rule.engine.metadata.TbGetAttributesNode"); + validator.validateDataImpl(TenantId.SYS_TENANT_ID, plugin); + verify(validator).validateString("Component name", plugin.getName()); + } +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/validator/CustomerDataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/validator/CustomerDataValidatorTest.java new file mode 100644 index 0000000000..67329ba864 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/validator/CustomerDataValidatorTest.java @@ -0,0 +1,59 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.customer.CustomerDao; +import org.thingsboard.server.dao.tenant.TenantService; + +import java.util.UUID; + +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.Mockito.verify; + +@SpringBootTest(classes = CustomerDataValidator.class) +class CustomerDataValidatorTest { + + @MockBean + CustomerDao customerDao; + @MockBean + TenantService tenantService; + @SpyBean + CustomerDataValidator validator; + TenantId tenantId = TenantId.fromUUID(UUID.fromString("9ef79cdf-37a8-4119-b682-2e7ed4e018da")); + + @BeforeEach + void setUp() { + willReturn(true).given(tenantService).tenantExists(tenantId); + } + + @Test + void testValidateNameInvocation() { + Customer customer = new Customer(); + customer.setTitle("Customer A"); + customer.setTenantId(tenantId); + + validator.validateDataImpl(tenantId, customer); + verify(validator).validateString("Customer title", customer.getTitle()); + } + +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/validator/DashboardDataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/validator/DashboardDataValidatorTest.java new file mode 100644 index 0000000000..b2953ad912 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/validator/DashboardDataValidatorTest.java @@ -0,0 +1,56 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.tenant.TenantService; + +import java.util.UUID; + +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.Mockito.verify; + +@SpringBootTest(classes = DashboardDataValidator.class) +class DashboardDataValidatorTest { + + @MockBean + TenantService tenantService; + @SpyBean + DashboardDataValidator validator; + TenantId tenantId = TenantId.fromUUID(UUID.fromString("9ef79cdf-37a8-4119-b682-2e7ed4e018da")); + + @BeforeEach + void setUp() { + willReturn(true).given(tenantService).tenantExists(tenantId); + } + + @Test + void testValidateNameInvocation() { + Dashboard dashboard = new Dashboard(); + dashboard.setTitle("flight control"); + dashboard.setTenantId(tenantId); + + validator.validateDataImpl(tenantId, dashboard); + verify(validator).validateString("Dashboard title", dashboard.getTitle()); + } + +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/validator/DeviceDataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/validator/DeviceDataValidatorTest.java new file mode 100644 index 0000000000..4c4ed5a0e2 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/validator/DeviceDataValidatorTest.java @@ -0,0 +1,85 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.customer.CustomerDao; +import org.thingsboard.server.dao.device.DeviceDao; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.tenant.TenantService; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.willReturn; + +@SpringBootTest(classes = DeviceDataValidator.class) +@Slf4j +class DeviceDataValidatorTest { + + @MockBean + DeviceDao deviceDao; + @MockBean + TenantService tenantService; + @MockBean + CustomerDao customerDao; + @Autowired + DeviceDataValidator validator; + TenantId tenantId = TenantId.fromUUID(UUID.fromString("9ef79cdf-37a8-4119-b682-2e7ed4e018da")); + + @BeforeEach + void setUp() { + willReturn(true).given(tenantService).tenantExists(tenantId); + } + + @ParameterizedTest + @ValueSource(strings = { + "coffee", "1", "big box", "世界", "!", "--", "~!@#$%^&*()_+=-/|\\[]{};:'`\"?<>,.", "\uD83D\uDC0C", "\041", + "Gdy Pomorze nie pomoże, to pomoże może morze, a gdy morze nie pomoże, to pomoże może Gdańsk", + }) + void testDeviceName_thenOK(final String name) { + Device device = new Device(); + device.setTenantId(tenantId); + device.setName(name); + validator.validateDataImpl(tenantId, device); + } + + @ParameterizedTest + @ValueSource(strings = { + "", " ", " ", "\n", "\r\n", "\t", "\000", "\000\000", "\001", "\002", "\040", "\u0000", "\u0000\u0000", + "F0929906\000\000\000\000\000\000\000\000\000", "\000\000\000F0929906", + "\u0000F0929906", "F092\u00009906", "F0929906\u0000" + }) + void testDeviceName_thenDataValidationException(final String name) { + Device device = new Device(); + device.setTenantId(tenantId); + device.setName(name); + + DataValidationException exception = Assertions.assertThrows(DataValidationException.class, () -> validator.validateDataImpl(tenantId, device)); + log.warn("Exception message: {}", exception.getMessage()); + assertThat(exception.getMessage()).as("message Device name").containsPattern("Device name .*"); + } + +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidatorTest.java new file mode 100644 index 0000000000..fc066c201d --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidatorTest.java @@ -0,0 +1,83 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.DeviceProfileType; +import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.dashboard.DashboardService; +import org.thingsboard.server.dao.device.DeviceDao; +import org.thingsboard.server.dao.device.DeviceProfileDao; +import org.thingsboard.server.dao.device.DeviceProfileService; +import org.thingsboard.server.dao.queue.QueueService; +import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.tenant.TenantService; + +import java.util.UUID; + +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.Mockito.verify; + +@SpringBootTest(classes = DeviceProfileDataValidator.class) +class DeviceProfileDataValidatorTest { + + @MockBean + DeviceProfileDao deviceProfileDao; + @MockBean + DeviceProfileService deviceProfileService; + @MockBean + DeviceDao deviceDao; + @MockBean + TenantService tenantService; + @MockBean + QueueService queueService; + @MockBean + RuleChainService ruleChainService; + @MockBean + DashboardService dashboardService; + @SpyBean + DeviceProfileDataValidator validator; + TenantId tenantId = TenantId.fromUUID(UUID.fromString("9ef79cdf-37a8-4119-b682-2e7ed4e018da")); + + @BeforeEach + void setUp() { + willReturn(true).given(tenantService).tenantExists(tenantId); + } + + @Test + void testValidateNameInvocation() { + DeviceProfile deviceProfile = new DeviceProfile(); + deviceProfile.setName("default"); + deviceProfile.setType(DeviceProfileType.DEFAULT); + deviceProfile.setTransportType(DeviceTransportType.DEFAULT); + DeviceProfileData data = new DeviceProfileData(); + data.setTransportConfiguration(new DefaultDeviceProfileTransportConfiguration()); + deviceProfile.setProfileData(data); + deviceProfile.setTenantId(tenantId); + + validator.validateDataImpl(tenantId, deviceProfile); + verify(validator).validateString("Device profile name", deviceProfile.getName()); + } + +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/validator/EdgeDataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/validator/EdgeDataValidatorTest.java new file mode 100644 index 0000000000..d852b3680a --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/validator/EdgeDataValidatorTest.java @@ -0,0 +1,66 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.customer.CustomerDao; +import org.thingsboard.server.dao.edge.EdgeDao; +import org.thingsboard.server.dao.tenant.TenantService; + +import java.util.UUID; + +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.Mockito.verify; + +@SpringBootTest(classes = EdgeDataValidator.class) +class EdgeDataValidatorTest { + + @MockBean + EdgeDao edgeDao; + @MockBean + TenantService tenantService; + @MockBean + CustomerDao customerDao; + @SpyBean + EdgeDataValidator validator; + TenantId tenantId = TenantId.fromUUID(UUID.fromString("9ef79cdf-37a8-4119-b682-2e7ed4e018da")); + + @BeforeEach + void setUp() { + willReturn(true).given(tenantService).tenantExists(tenantId); + } + + @Test + void testValidateNameInvocation() { + Edge edge = new Edge(); + edge.setName("Edge 007"); + edge.setType("Silos"); + edge.setSecret("secret"); + edge.setRoutingKey("53c56104-d302-4d6e-97f5-a7a99c7effdc"); + edge.setTenantId(tenantId); + + validator.validateDataImpl(tenantId, edge); + verify(validator).validateString("Edge name", edge.getName()); + verify(validator).validateString("Edge type", edge.getType()); + } + +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/validator/EntityViewDataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/validator/EntityViewDataValidatorTest.java new file mode 100644 index 0000000000..0eebb15554 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/validator/EntityViewDataValidatorTest.java @@ -0,0 +1,64 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.customer.CustomerDao; +import org.thingsboard.server.dao.entityview.EntityViewDao; +import org.thingsboard.server.dao.tenant.TenantService; + +import java.util.UUID; + +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.Mockito.verify; + +@SpringBootTest(classes = EntityViewDataValidator.class) +class EntityViewDataValidatorTest { + + @MockBean + EntityViewDao entityViewDao; + @MockBean + TenantService tenantService; + @MockBean + CustomerDao customerDao; + @SpyBean + EntityViewDataValidator validator; + TenantId tenantId = TenantId.fromUUID(UUID.fromString("9ef79cdf-37a8-4119-b682-2e7ed4e018da")); + + @BeforeEach + void setUp() { + willReturn(true).given(tenantService).tenantExists(tenantId); + } + + @Test + void testValidateNameInvocation() { + EntityView entityView = new EntityView(); + entityView.setName("view"); + entityView.setType("default"); + entityView.setTenantId(tenantId); + + validator.validateDataImpl(tenantId, entityView); + verify(validator).validateString("Entity view name", entityView.getName()); + verify(validator).validateString("Entity view type", entityView.getType()); + } + +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/validator/ResourceDataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/validator/ResourceDataValidatorTest.java new file mode 100644 index 0000000000..985b80331f --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/validator/ResourceDataValidatorTest.java @@ -0,0 +1,69 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.resource.TbResourceDao; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import org.thingsboard.server.dao.tenant.TenantService; +import org.thingsboard.server.dao.widget.WidgetTypeDao; + +import java.util.UUID; + +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.Mockito.verify; + +@SpringBootTest(classes = ResourceDataValidator.class) +class ResourceDataValidatorTest { + + @MockBean + TbResourceDao resourceDao; + @MockBean + WidgetTypeDao widgetTypeDao; + @MockBean + TenantService tenantService; + @MockBean + TbTenantProfileCache tenantProfileCache; + @SpyBean + ResourceDataValidator validator; + TenantId tenantId = TenantId.fromUUID(UUID.fromString("9ef79cdf-37a8-4119-b682-2e7ed4e018da")); + + @BeforeEach + void setUp() { + willReturn(true).given(tenantService).tenantExists(tenantId); + } + + @Test + void testValidateNameInvocation() { + TbResource resource = new TbResource(); + resource.setTitle("rss"); + resource.setResourceType(ResourceType.PKCS_12); + resource.setFileName("cert.pem"); + resource.setResourceKey("19_1.0"); + resource.setTenantId(tenantId); + + validator.validateDataImpl(tenantId, resource); + verify(validator).validateString("Resource title", resource.getTitle()); + } + +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/validator/RuleChainDataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/validator/RuleChainDataValidatorTest.java new file mode 100644 index 0000000000..0fdd1a04d2 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/validator/RuleChainDataValidatorTest.java @@ -0,0 +1,61 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainType; +import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.tenant.TenantService; + +import java.util.UUID; + +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.Mockito.verify; + +@SpringBootTest(classes = RuleChainDataValidator.class) +class RuleChainDataValidatorTest { + + @MockBean + RuleChainService ruleChainService; + @MockBean + TenantService tenantService; + @SpyBean + RuleChainDataValidator validator; + TenantId tenantId = TenantId.fromUUID(UUID.fromString("9ef79cdf-37a8-4119-b682-2e7ed4e018da")); + + @BeforeEach + void setUp() { + willReturn(true).given(tenantService).tenantExists(tenantId); + } + + @Test + void testValidateNameInvocation() { + RuleChain ruleChain = new RuleChain(); + ruleChain.setName("generate daily report"); + ruleChain.setType(RuleChainType.CORE); + ruleChain.setTenantId(tenantId); + + validator.validateDataImpl(tenantId, ruleChain); + verify(validator).validateString("Rule chain name", ruleChain.getName()); + } + +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/validator/TenantDataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/validator/TenantDataValidatorTest.java new file mode 100644 index 0000000000..e6c6a96146 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/validator/TenantDataValidatorTest.java @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.tenant.TenantDao; + +import java.util.UUID; + +import static org.mockito.Mockito.verify; + +@SpringBootTest(classes = TenantDataValidator.class) +class TenantDataValidatorTest { + + @MockBean + TenantDao tenantDao; + @SpyBean + TenantDataValidator validator; + TenantId tenantId = TenantId.fromUUID(UUID.fromString("9ef79cdf-37a8-4119-b682-2e7ed4e018da")); + + @Test + void testValidateNameInvocation() { + Tenant tenant = new Tenant(); + tenant.setTitle("Monster corporation ©"); + tenant.setEmail("support@thingsboard.io"); + + validator.validateDataImpl(tenantId, tenant); + verify(validator).validateString("Tenant title", tenant.getTitle()); + } + +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidatorTest.java new file mode 100644 index 0000000000..e3bca21984 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidatorTest.java @@ -0,0 +1,56 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; +import org.thingsboard.server.dao.tenant.TenantProfileDao; +import org.thingsboard.server.dao.tenant.TenantProfileService; + +import java.util.UUID; + +import static org.mockito.Mockito.verify; + +@SpringBootTest(classes = TenantProfileDataValidator.class) +class TenantProfileDataValidatorTest { + + @MockBean + TenantProfileDao tenantProfileDao; + @MockBean + TenantProfileService tenantProfileService; + @SpyBean + TenantProfileDataValidator validator; + TenantId tenantId = TenantId.fromUUID(UUID.fromString("9ef79cdf-37a8-4119-b682-2e7ed4e018da")); + + @Test + void testValidateNameInvocation() { + TenantProfile tenantProfile = new TenantProfile(); + tenantProfile.setName("Sandbox"); + TenantProfileData tenantProfileData = new TenantProfileData(); + tenantProfileData.setConfiguration(new DefaultTenantProfileConfiguration()); + tenantProfile.setProfileData(tenantProfileData); + + validator.validateDataImpl(tenantId, tenantProfile); + verify(validator).validateString("Tenant profile name", tenantProfile.getName()); + } + +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/validator/WidgetTypeDataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/validator/WidgetTypeDataValidatorTest.java new file mode 100644 index 0000000000..209d2b4bb8 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/validator/WidgetTypeDataValidatorTest.java @@ -0,0 +1,63 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.widget.WidgetTypeDetails; +import org.thingsboard.server.dao.tenant.TenantService; +import org.thingsboard.server.dao.widget.WidgetTypeDao; +import org.thingsboard.server.dao.widget.WidgetsBundleDao; + +import java.util.UUID; + +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.Mockito.verify; + +@SpringBootTest(classes = WidgetTypeDataValidator.class) +class WidgetTypeDataValidatorTest { + @MockBean + WidgetTypeDao widgetTypeDao; + @MockBean + WidgetsBundleDao widgetsBundleDao; + @MockBean + TenantService tenantService; + @SpyBean + WidgetTypeDataValidator validator; + TenantId tenantId = TenantId.fromUUID(UUID.fromString("9ef79cdf-37a8-4119-b682-2e7ed4e018da")); + + @BeforeEach + void setUp() { + willReturn(true).given(tenantService).tenantExists(tenantId); + } + + @Test + void testValidateNameInvocation() { + WidgetTypeDetails widgetTypeDetails = new WidgetTypeDetails(); + widgetTypeDetails.setName("widget type gas"); + widgetTypeDetails.setDescriptor(JacksonUtil.toJsonNode("{\"content\":\"empty\"}")); + widgetTypeDetails.setTenantId(tenantId); + + validator.validateDataImpl(tenantId, widgetTypeDetails); + verify(validator).validateString("Widgets type name", widgetTypeDetails.getName()); + } + +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/validator/WidgetsBundleDataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/validator/WidgetsBundleDataValidatorTest.java new file mode 100644 index 0000000000..9e54f85689 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/validator/WidgetsBundleDataValidatorTest.java @@ -0,0 +1,59 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.validator; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.dao.tenant.TenantService; +import org.thingsboard.server.dao.widget.WidgetsBundleDao; + +import java.util.UUID; + +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.Mockito.verify; + +@SpringBootTest(classes = WidgetsBundleDataValidator.class) +class WidgetsBundleDataValidatorTest { + + @MockBean + WidgetsBundleDao widgetsBundleDao; + @MockBean + TenantService tenantService; + @SpyBean + WidgetsBundleDataValidator validator; + TenantId tenantId = TenantId.fromUUID(UUID.fromString("9ef79cdf-37a8-4119-b682-2e7ed4e018da")); + + @BeforeEach + void setUp() { + willReturn(true).given(tenantService).tenantExists(tenantId); + } + + @Test + void testValidateNameInvocation() { + WidgetsBundle widgetsBundle = new WidgetsBundle(); + widgetsBundle.setTitle("my fancy WB"); + widgetsBundle.setTenantId(tenantId); + + validator.validateDataImpl(tenantId, widgetsBundle); + verify(validator).validateString("Widgets bundle title", widgetsBundle.getTitle()); + } + +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java index 25231f822f..8f0122aa4d 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java @@ -20,6 +20,8 @@ import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; @@ -29,8 +31,12 @@ import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.dao.AbstractJpaDaoTest; import org.thingsboard.server.dao.alarm.AlarmDao; +import org.thingsboard.server.dao.tenant.TenantDao; +import org.thingsboard.server.dao.tenant.TenantProfileDao; import java.util.UUID; import java.util.concurrent.ExecutionException; @@ -54,6 +60,11 @@ public class JpaAlarmDaoTest extends AbstractJpaDaoTest { @Autowired private AlarmDao alarmDao; + @Autowired + protected TenantProfileDao tenantProfileDao; + + @Autowired + protected TenantDao tenantDao; @Test public void testFindLatestByOriginatorAndType() throws ExecutionException, InterruptedException, TimeoutException { @@ -88,7 +99,8 @@ public class JpaAlarmDaoTest extends AbstractJpaDaoTest { @Test public void createOrUpdateActiveAlarm() { - TenantId tenantId = TenantId.fromUUID(UUID.randomUUID()); + Tenant tenant = createTenant(); + TenantId tenantId = tenant.getId(); DeviceId deviceId = new DeviceId(UUID.randomUUID()); AlarmCreateOrUpdateActiveRequest request = AlarmCreateOrUpdateActiveRequest.builder() @@ -156,6 +168,9 @@ public class JpaAlarmDaoTest extends AbstractJpaDaoTest { assertTrue(result.isModified()); assertNotNull(result.getAlarm()); assertNotEquals(newAlarmId, result.getAlarm().getUuidId()); + + tenantDao.removeById(TenantId.SYS_TENANT_ID, tenant.getUuidId()); + tenantProfileDao.removeById(TenantId.SYS_TENANT_ID, tenant.getTenantProfileId().getId()); } @Test @@ -329,4 +344,23 @@ public class JpaAlarmDaoTest extends AbstractJpaDaoTest { return alarmDao.save(TenantId.fromUUID(tenantId), alarm); } + private Tenant createTenant() { + TenantProfile tenantProfile = new TenantProfile(); + tenantProfile.setName("My tenant profile " + UUID.randomUUID()); + TenantProfileData profileData = new TenantProfileData(); + profileData.setConfiguration(new DefaultTenantProfileConfiguration()); + tenantProfile.setProfileData(profileData); + var savedTenantProfile = tenantProfileDao.save(TenantId.SYS_TENANT_ID, tenantProfile); + assertNotNull(savedTenantProfile); + + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant " + UUID.randomUUID()); + tenant.setTenantProfileId(savedTenantProfile.getId()); + Tenant savedTenant = tenantDao.save(TenantId.SYS_TENANT_ID, tenant); + + assertNotNull(savedTenant); + + return savedTenant; + } + } diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/asset/JpaAssetDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/asset/JpaAssetDaoTest.java index ef68465256..1ffb740553 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/asset/JpaAssetDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/asset/JpaAssetDaoTest.java @@ -44,6 +44,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -94,6 +95,12 @@ public class JpaAssetDaoTest extends AbstractJpaDaoTest { savedAssetProfiles.clear(); } + @Test + public void testSaveDeviceName0x00_thenSomeDatabaseException() { + assertThatThrownBy(() -> assets.add( + saveAsset(UUID.randomUUID(), tenantId2, customerId2, "F0929906\000\000\000\000\000\000\000\000\000"))); + } + @Test public void testFindAssetsByTenantId() { PageLink pageLink = new PageLink(20, 0, "ASSET_"); diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/device/JpaDeviceDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/device/JpaDeviceDaoTest.java index dc174b7423..c8e6c2cfc5 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/device/JpaDeviceDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/device/JpaDeviceDaoTest.java @@ -45,6 +45,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -101,6 +102,12 @@ public class JpaDeviceDaoTest extends AbstractJpaDaoTest { } } + @Test + public void testSaveDeviceName0x00_thenSomeDatabaseException() { + Device device = getDevice(tenantId1, customerId1, "\u0000"); + assertThatThrownBy(() -> deviceIds.add(deviceDao.save(TenantId.fromUUID(tenantId1), device).getUuidId())); + } + @Test public void testFindDevicesByTenantId() { PageLink pageLink = new PageLink(15, 0, PREFIX_FOR_DEVICE_NAME); @@ -155,16 +162,16 @@ public class JpaDeviceDaoTest extends AbstractJpaDaoTest { return savedDevicesUUID; } - private Device getDevice(UUID tenantId, UUID customerID, int number) { - return getDevice(tenantId, customerID, Uuids.timeBased(), number); + private Device getDevice(UUID tenantId, UUID customerID, Object nameSuffix) { + return getDevice(tenantId, customerID, Uuids.timeBased(), nameSuffix); } - private Device getDevice(UUID tenantId, UUID customerID, UUID deviceId, int number) { + private Device getDevice(UUID tenantId, UUID customerID, UUID deviceId, Object nameSuffix) { Device device = new Device(); device.setId(new DeviceId(deviceId)); device.setTenantId(TenantId.fromUUID(tenantId)); device.setCustomerId(new CustomerId(customerID)); - device.setName(PREFIX_FOR_DEVICE_NAME + number); + device.setName(PREFIX_FOR_DEVICE_NAME + nameSuffix); device.setDeviceProfileId(savedDeviceProfile.getId()); return device; } diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDaoTest.java new file mode 100644 index 0000000000..2f1c73da01 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDaoTest.java @@ -0,0 +1,191 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.rule; + +import com.datastax.oss.driver.api.core.uuid.Uuids; +import com.google.common.util.concurrent.ListeningExecutorService; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.dao.AbstractJpaDaoTest; +import org.thingsboard.server.dao.rule.RuleChainDao; +import org.thingsboard.server.dao.rule.RuleNodeDao; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.Assert.assertEquals; + +public class JpaRuleNodeDaoTest extends AbstractJpaDaoTest { + + public static final int COUNT = 40; + public static final String PREFIX_FOR_RULE_NODE_NAME = "SEARCH_TEXT_"; + List ruleNodeIds; + TenantId tenantId1; + TenantId tenantId2; + RuleChainId ruleChainId1; + RuleChainId ruleChainId2; + + @Autowired + private RuleChainDao ruleChainDao; + + @Autowired + private RuleNodeDao ruleNodeDao; + + ListeningExecutorService executor; + + @Before + public void setUp() { + tenantId1 = TenantId.fromUUID(Uuids.timeBased()); + ruleChainId1 = new RuleChainId(UUID.randomUUID()); + tenantId2 = TenantId.fromUUID(Uuids.timeBased()); + ruleChainId2 = new RuleChainId(UUID.randomUUID()); + + ruleNodeIds = createRuleNodes(tenantId1, tenantId2, ruleChainId1, ruleChainId2, COUNT); + } + + @After + public void tearDown() throws Exception { + ruleNodeDao.removeAllByIds(ruleNodeIds); + if (executor != null) { + executor.shutdownNow(); + } + } + + @Test + public void testSaveRuleName0x00_thenSomeDatabaseException() { + RuleNode ruleNode = getRuleNode(ruleChainId1, "T", "\u0000"); + assertThatThrownBy(() -> ruleNodeIds.add(ruleNodeDao.save(tenantId1, ruleNode).getUuidId())); + } + + @Test + public void testFindRuleNodesByTenantIdAndType() { + List ruleNodes1 = ruleNodeDao.findRuleNodesByTenantIdAndType(tenantId1, "A", PREFIX_FOR_RULE_NODE_NAME); + assertEquals(20, ruleNodes1.size()); + + List ruleNodes2 = ruleNodeDao.findRuleNodesByTenantIdAndType(tenantId2, "B", PREFIX_FOR_RULE_NODE_NAME); + assertEquals(20, ruleNodes2.size()); + + ruleNodes1 = ruleNodeDao.findRuleNodesByTenantIdAndType(tenantId1, "A", null); + assertEquals(20, ruleNodes1.size()); + + ruleNodes2 = ruleNodeDao.findRuleNodesByTenantIdAndType(tenantId2, "B", null); + assertEquals(20, ruleNodes2.size()); + } + + @Test + public void testFindRuleNodesByType() { + PageData ruleNodes = ruleNodeDao.findAllRuleNodesByType( "A", new PageLink(10, 0, PREFIX_FOR_RULE_NODE_NAME)); + assertEquals(20, ruleNodes.getTotalElements()); + assertEquals(2, ruleNodes.getTotalPages()); + assertEquals(10, ruleNodes.getData().size()); + + ruleNodes = ruleNodeDao.findAllRuleNodesByType( "A", new PageLink(10, 0)); + assertEquals(20, ruleNodes.getTotalElements()); + assertEquals(2, ruleNodes.getTotalPages()); + assertEquals(10, ruleNodes.getData().size()); + } + + @Test + public void testFindRuleNodesByTypeAndVersionLessThan() { + PageData ruleNodes = ruleNodeDao.findAllRuleNodesByTypeAndVersionLessThan( "A", 1, new PageLink(10, 0, PREFIX_FOR_RULE_NODE_NAME)); + assertEquals(20, ruleNodes.getTotalElements()); + assertEquals(2, ruleNodes.getTotalPages()); + assertEquals(10, ruleNodes.getData().size()); + + ruleNodes = ruleNodeDao.findAllRuleNodesByTypeAndVersionLessThan( "A", 1, new PageLink(10, 0)); + assertEquals(20, ruleNodes.getTotalElements()); + assertEquals(2, ruleNodes.getTotalPages()); + assertEquals(10, ruleNodes.getData().size()); + } + + @Test + public void testFindRuleNodeIdsByTypeAndVersionLessThan() { + PageData ruleNodeIds = ruleNodeDao.findAllRuleNodeIdsByTypeAndVersionLessThan( "A", 1, new PageLink(10, 0, PREFIX_FOR_RULE_NODE_NAME)); + assertEquals(20, ruleNodeIds.getTotalElements()); + assertEquals(2, ruleNodeIds.getTotalPages()); + assertEquals(10, ruleNodeIds.getData().size()); + + ruleNodeIds = ruleNodeDao.findAllRuleNodeIdsByTypeAndVersionLessThan( "A", 1, new PageLink(10, 0)); + assertEquals(20, ruleNodeIds.getTotalElements()); + assertEquals(2, ruleNodeIds.getTotalPages()); + assertEquals(10, ruleNodeIds.getData().size()); + + // test - search text ignored + ruleNodeIds = ruleNodeDao.findAllRuleNodeIdsByTypeAndVersionLessThan( "A", 1, new PageLink(10, 0, StringUtils.randomAlphabetic(5))); + assertEquals(20, ruleNodeIds.getTotalElements()); + assertEquals(2, ruleNodeIds.getTotalPages()); + assertEquals(10, ruleNodeIds.getData().size()); + } + + @Test + public void testFindAllRuleNodeByIds() { + var fromUUIDs = ruleNodeIds.stream().map(RuleNodeId::new).collect(Collectors.toList()); + var ruleNodes = ruleNodeDao.findAllRuleNodeByIds(fromUUIDs); + assertEquals(40, ruleNodes.size()); + } + + private List createRuleNodes(TenantId tenantId1, TenantId tenantId2, RuleChainId ruleChainId1, RuleChainId ruleChainId2, int count) { + return createRuleNodes(tenantId1, tenantId2, ruleChainId1, ruleChainId2, "A", "B", count); + } + + private List createRuleNodes(TenantId tenantId1, TenantId tenantId2, + RuleChainId ruleChainId1, RuleChainId ruleChainId2, + String typeA, String typeB, int count) { + var chain1 = new RuleChain(ruleChainId1); + chain1.setTenantId(tenantId1); + chain1.setName(ruleChainId1.toString()); + ruleChainDao.save(tenantId1, chain1); + var chain2 = new RuleChain(ruleChainId2); + chain2.setTenantId(tenantId2); + chain2.setName(ruleChainId2.toString()); + ruleChainDao.save(tenantId2, chain2); + List savedRuleNodeIds = new ArrayList<>(); + for (int i = 0; i < count / 2; i++) { + savedRuleNodeIds.add(ruleNodeDao.save(tenantId1, getRuleNode(ruleChainId1, typeA, Integer.toString(i))).getUuidId()); + savedRuleNodeIds.add(ruleNodeDao.save(tenantId2, getRuleNode(ruleChainId2, typeB, Integer.toString(i + count / 2))).getUuidId()); + } + return savedRuleNodeIds; + } + + private RuleNode getRuleNode(RuleChainId ruleChainId, String type, String nameSuffix) { + return getRuleNode(ruleChainId, Uuids.timeBased(), type, nameSuffix); + } + + private RuleNode getRuleNode(RuleChainId ruleChainId, UUID ruleNodeId, String type, String nameSuffix) { + RuleNode ruleNode = new RuleNode(); + ruleNode.setId(new RuleNodeId(ruleNodeId)); + ruleNode.setRuleChainId(ruleChainId); + ruleNode.setName(nameSuffix); + ruleNode.setType(type); + ruleNode.setConfiguration(JacksonUtil.newObjectNode().put("searchHint", PREFIX_FOR_RULE_NODE_NAME + nameSuffix)); + ruleNode.setConfigurationVersion(0); + return ruleNode; + } +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDaoTest.java index b9e5e7fc5a..0a466d033c 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDaoTest.java @@ -15,19 +15,36 @@ */ package org.thingsboard.server.dao.sql.widget; +import com.datastax.oss.driver.api.core.uuid.Uuids; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.WidgetsBundleId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.page.SortOrder; +import org.thingsboard.server.common.data.widget.BaseWidgetType; +import org.thingsboard.server.common.data.widget.DeprecatedFilter; import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; +import org.thingsboard.server.common.data.widget.WidgetTypeInfo; +import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.common.data.widget.WidgetsBundleWidget; import org.thingsboard.server.dao.AbstractJpaDaoTest; import org.thingsboard.server.dao.widget.WidgetTypeDao; +import org.thingsboard.server.dao.widget.WidgetsBundleDao; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.List; +import java.util.Map; +import java.util.UUID; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -36,48 +53,306 @@ import static org.junit.Assert.assertNotNull; */ public class JpaWidgetTypeDaoTest extends AbstractJpaDaoTest { + // given search text should find a widget with tags, when searching by tags + final Map SHOULD_FIND_SEARCH_TO_TAGS_MAP = Map.of( + "A", new String[]{"a", "b", "c"}, + "test A test", new String[]{"a", "b", "c"}, + "test x y test", new String[]{"x y", "b", "c"}, + "x y test", new String[]{"x y", "b", "c"}, + "x y", new String[]{"x y", "b", "c"}, + "test x y", new String[]{"x y", "b", "c"} + ); + + // given search text should not find a widget with tags, when searching by tags + final Map SHOULDNT_FIND_SEARCH_TO_TAGS_MAP = Map.of( + "testA test", new String[]{"a", "b", "c"}, + "testx y test", new String[]{"x y", "b", "c"}, + "testx ytest", new String[]{"x y", "b", "c"}, + "x ytest", new String[]{"x y", "b", "c"}, + "testx y", new String[]{"x y", "b", "c"}, + "x", new String[]{"x y", "b", "c"} + ); + final String BUNDLE_ALIAS = "BUNDLE_ALIAS"; final int WIDGET_TYPE_COUNT = 3; - List widgetTypeList; + List widgetTypeList; + WidgetsBundle widgetsBundle; @Autowired private WidgetTypeDao widgetTypeDao; + @Autowired + private WidgetsBundleDao widgetsBundleDao; + @Before - public void setUp() { + public void setUp() throws InterruptedException { widgetTypeList = new ArrayList<>(); + + WidgetsBundle widgetsBundle = new WidgetsBundle(); + widgetsBundle.setAlias(BUNDLE_ALIAS); + widgetsBundle.setTitle(BUNDLE_ALIAS); + widgetsBundle.setId(new WidgetsBundleId(Uuids.timeBased())); + widgetsBundle.setTenantId(TenantId.SYS_TENANT_ID); + this.widgetsBundle = widgetsBundleDao.save(TenantId.SYS_TENANT_ID, widgetsBundle); + for (int i = 0; i < WIDGET_TYPE_COUNT; i++) { - widgetTypeList.add(createAndSaveWidgetType(i)); + Thread.sleep(2); + var widgetType = createAndSaveWidgetType(TenantId.SYS_TENANT_ID, i); + widgetTypeList.add(widgetType); + widgetTypeDao.saveWidgetsBundleWidget(new WidgetsBundleWidget(this.widgetsBundle.getId(), widgetType.getId(), i)); } + widgetTypeList.sort(Comparator.comparing(BaseWidgetType::getName)); + } + + WidgetTypeDetails createAndSaveWidgetType(TenantId tenantId, int number) { + WidgetTypeDetails widgetType = new WidgetTypeDetails(); + widgetType.setTenantId(tenantId); + widgetType.setName("WIDGET_TYPE_" + number); + widgetType.setDescription("WIDGET_TYPE_DESCRIPTION" + number); + widgetType.setFqn("FQN_" + number); + widgetType.setImage("/image/system/logo.png"); + var descriptor = JacksonUtil.newObjectNode(); + descriptor.put("type", number % 2 == 0 ? "latest" : "static"); + widgetType.setDescriptor(descriptor); + String[] tags = new String[]{"Tag1_"+number, "Tag2_"+number, "TEST_"+number}; + widgetType.setTags(tags); + return widgetTypeDao.save(TenantId.SYS_TENANT_ID, widgetType); } - WidgetType createAndSaveWidgetType(int number) { + WidgetTypeDetails createAndSaveWidgetType(TenantId tenantId, int number, String[] tags) { WidgetTypeDetails widgetType = new WidgetTypeDetails(); - widgetType.setTenantId(TenantId.SYS_TENANT_ID); + widgetType.setTenantId(tenantId); widgetType.setName("WIDGET_TYPE_" + number); - widgetType.setAlias("ALIAS_" + number); - widgetType.setBundleAlias(BUNDLE_ALIAS); + widgetType.setDescription("WIDGET_TYPE_DESCRIPTION" + number); + widgetType.setFqn("FQN_" + number); + widgetType.setImage("/image/tenant/logo.png"); + var descriptor = JacksonUtil.newObjectNode(); + descriptor.put("type", number % 2 == 0 ? "latest" : "static"); + widgetType.setDescriptor(descriptor); + widgetType.setTags(tags); return widgetTypeDao.save(TenantId.SYS_TENANT_ID, widgetType); } @After - public void deleteAllWidgetType() { + public void tearDown() { + widgetsBundleDao.removeById(TenantId.SYS_TENANT_ID, widgetsBundle.getUuidId()); for (WidgetType widgetType : widgetTypeList) { widgetTypeDao.removeById(TenantId.SYS_TENANT_ID, widgetType.getUuidId()); } } @Test - public void testFindByTenantIdAndBundleAlias() { - List widgetTypes = widgetTypeDao.findWidgetTypesByTenantIdAndBundleAlias(TenantId.SYS_TENANT_ID.getId(), BUNDLE_ALIAS); + public void testFindByWidgetsBundleId() { + List widgetTypes = widgetTypeDao.findWidgetTypesByWidgetsBundleId(TenantId.SYS_TENANT_ID.getId(), widgetsBundle.getUuidId()); assertEquals(WIDGET_TYPE_COUNT, widgetTypes.size()); } @Test - public void testFindByTenantIdAndBundleAliasAndAlias() { + public void testFindSystemWidgetTypes() { + PageData widgetTypes = widgetTypeDao.findSystemWidgetTypes(TenantId.SYS_TENANT_ID, true, DeprecatedFilter.ALL, Collections.singletonList("static"), + new PageLink(1024, 0, "TYPE_DESCRIPTION", new SortOrder("createdTime"))); + assertEquals(1, widgetTypes.getData().size()); + assertEquals(new WidgetTypeInfo(widgetTypeList.get(1)), widgetTypes.getData().get(0)); + + widgetTypes = widgetTypeDao.findSystemWidgetTypes(TenantId.SYS_TENANT_ID, true, DeprecatedFilter.ALL, Collections.emptyList(), + new PageLink(1024, 0, "hfgfd tag2_2 ghg", new SortOrder("createdTime"))); + assertEquals(1, widgetTypes.getData().size()); + assertEquals(new WidgetTypeInfo(widgetTypeList.get(2)), widgetTypes.getData().get(0)); + } + + @Test + public void testTagsSearchInFindBySystemWidgetTypes() { + for (var entry : SHOULD_FIND_SEARCH_TO_TAGS_MAP.entrySet()) { + String searchText = entry.getKey(); + String[] tags = entry.getValue(); + + WidgetTypeDetails savedWidgetType = createAndSaveWidgetType(TenantId.SYS_TENANT_ID, WIDGET_TYPE_COUNT + 1, tags); + + PageData widgetTypes = widgetTypeDao.findSystemWidgetTypes( + TenantId.SYS_TENANT_ID, true, DeprecatedFilter.ALL, null, new PageLink(10, 0, searchText) + ); + + assertThat(widgetTypes.getData()).hasSize(1); + assertThat(widgetTypes.getData().get(0).getId()).isEqualTo(savedWidgetType.getId()); + + widgetTypeDao.removeById(TenantId.SYS_TENANT_ID, savedWidgetType.getUuidId()); + } + + for (var entry : SHOULDNT_FIND_SEARCH_TO_TAGS_MAP.entrySet()) { + String searchText = entry.getKey(); + String[] tags = entry.getValue(); + + WidgetTypeDetails savedWidgetType = createAndSaveWidgetType(TenantId.SYS_TENANT_ID, WIDGET_TYPE_COUNT + 1, tags); + + PageData widgetTypes = widgetTypeDao.findSystemWidgetTypes( + TenantId.SYS_TENANT_ID, true, DeprecatedFilter.ALL, null, new PageLink(10, 0, searchText) + ); + + assertThat(widgetTypes.getData()).hasSize(0); + + widgetTypeDao.removeById(TenantId.SYS_TENANT_ID, savedWidgetType.getUuidId()); + } + } + + @Test + public void testFindTenantWidgetTypesByTenantId() { + UUID tenantId = Uuids.timeBased(); + for (int i = 0; i < WIDGET_TYPE_COUNT; i++) { + var widgetType = createAndSaveWidgetType(new TenantId(tenantId), i); + widgetTypeList.add(widgetType); + } + PageData widgetTypes = widgetTypeDao.findTenantWidgetTypesByTenantId(tenantId, true, DeprecatedFilter.ALL, null, + new PageLink(10, 0, "", new SortOrder("createdTime"))); + assertEquals(WIDGET_TYPE_COUNT, widgetTypes.getData().size()); + assertEquals(new WidgetTypeInfo(widgetTypeList.get(3)), widgetTypes.getData().get(0)); + assertEquals(new WidgetTypeInfo(widgetTypeList.get(4)), widgetTypes.getData().get(1)); + assertEquals(new WidgetTypeInfo(widgetTypeList.get(5)), widgetTypes.getData().get(2)); + } + + @Test + public void testTagsSearchInFindTenantWidgetTypesByTenantId() { + for (var entry : SHOULD_FIND_SEARCH_TO_TAGS_MAP.entrySet()) { + String searchText = entry.getKey(); + String[] tags = entry.getValue(); + + WidgetTypeDetails savedWidgetType = createAndSaveWidgetType(TenantId.SYS_TENANT_ID, WIDGET_TYPE_COUNT + 1, tags); + + PageData widgetTypes = widgetTypeDao.findTenantWidgetTypesByTenantId( + TenantId.SYS_TENANT_ID.getId(), true, DeprecatedFilter.ALL, null, new PageLink(10, 0, searchText) + ); + + assertThat(widgetTypes.getData()).hasSize(1); + assertThat(widgetTypes.getData().get(0).getId()).isEqualTo(savedWidgetType.getId()); + + widgetTypeDao.removeById(TenantId.SYS_TENANT_ID, savedWidgetType.getUuidId()); + } + + for (var entry : SHOULDNT_FIND_SEARCH_TO_TAGS_MAP.entrySet()) { + String searchText = entry.getKey(); + String[] tags = entry.getValue(); + + WidgetTypeDetails savedWidgetType = createAndSaveWidgetType(TenantId.SYS_TENANT_ID, WIDGET_TYPE_COUNT + 1, tags); + + PageData widgetTypes = widgetTypeDao.findTenantWidgetTypesByTenantId( + TenantId.SYS_TENANT_ID.getId(), true, DeprecatedFilter.ALL, null, new PageLink(10, 0, searchText) + ); + + assertThat(widgetTypes.getData()).hasSize(0); + + widgetTypeDao.removeById(TenantId.SYS_TENANT_ID, savedWidgetType.getUuidId()); + } + } + + @Test + public void testTagsSearchInFindAllTenantWidgetTypesByTenantId() { + for (var entry : SHOULD_FIND_SEARCH_TO_TAGS_MAP.entrySet()) { + String searchText = entry.getKey(); + String[] tags = entry.getValue(); + + WidgetTypeDetails savedWidgetType = createAndSaveWidgetType(TenantId.SYS_TENANT_ID, WIDGET_TYPE_COUNT + 1, tags); + + PageData widgetTypes = widgetTypeDao.findAllTenantWidgetTypesByTenantId( + TenantId.SYS_TENANT_ID.getId(), true, DeprecatedFilter.ALL, null, new PageLink(10, 0, searchText) + ); + + assertThat(widgetTypes.getData()).hasSize(1); + assertThat(widgetTypes.getData().get(0).getId()).isEqualTo(savedWidgetType.getId()); + + widgetTypeDao.removeById(TenantId.SYS_TENANT_ID, savedWidgetType.getUuidId()); + } + + for (var entry : SHOULDNT_FIND_SEARCH_TO_TAGS_MAP.entrySet()) { + String searchText = entry.getKey(); + String[] tags = entry.getValue(); + + WidgetTypeDetails savedWidgetType = createAndSaveWidgetType(TenantId.SYS_TENANT_ID, WIDGET_TYPE_COUNT + 1, tags); + + PageData widgetTypes = widgetTypeDao.findAllTenantWidgetTypesByTenantId( + TenantId.SYS_TENANT_ID.getId(), true, DeprecatedFilter.ALL, null, new PageLink(10, 0, searchText) + ); + + assertThat(widgetTypes.getData()).hasSize(0); + + widgetTypeDao.removeById(TenantId.SYS_TENANT_ID, savedWidgetType.getUuidId()); + } + } + + @Test + public void testFindByWidgetTypeInfosByBundleId() { + PageData widgetTypes = widgetTypeDao.findWidgetTypesInfosByWidgetsBundleId(TenantId.SYS_TENANT_ID.getId(), widgetsBundle.getUuidId(),true, DeprecatedFilter.ALL, Collections.singletonList("latest"), + new PageLink(1024, 0, "TYPE_DESCRIPTION", new SortOrder("createdTime"))); + assertEquals(2, widgetTypes.getData().size()); + assertEquals(new WidgetTypeInfo(widgetTypeList.get(0)), widgetTypes.getData().get(0)); + assertEquals(new WidgetTypeInfo(widgetTypeList.get(2)), widgetTypes.getData().get(1)); + + widgetTypes = widgetTypeDao.findWidgetTypesInfosByWidgetsBundleId(TenantId.SYS_TENANT_ID.getId(), widgetsBundle.getUuidId(), true, DeprecatedFilter.ALL, Collections.emptyList(), + new PageLink(1024, 0, "hfgfd TEST_0 ghg", new SortOrder("createdTime"))); + assertEquals(1, widgetTypes.getData().size()); + assertEquals(new WidgetTypeInfo(widgetTypeList.get(0)), widgetTypes.getData().get(0)); + } + + @Test + public void testTagsSearchInFindByWidgetTypeInfosByBundleId() { + for (var entry : SHOULD_FIND_SEARCH_TO_TAGS_MAP.entrySet()) { + String searchText = entry.getKey(); + String[] tags = entry.getValue(); + + WidgetTypeDetails savedWidgetType = createAndSaveWidgetType(TenantId.SYS_TENANT_ID, WIDGET_TYPE_COUNT + 1, tags); + widgetTypeDao.saveWidgetsBundleWidget(new WidgetsBundleWidget(this.widgetsBundle.getId(), savedWidgetType.getId(), WIDGET_TYPE_COUNT + 1)); + + PageData widgetTypes = widgetTypeDao.findWidgetTypesInfosByWidgetsBundleId( + TenantId.SYS_TENANT_ID.getId(), widgetsBundle.getUuidId(), true, DeprecatedFilter.ALL, null, new PageLink(10, 0, searchText) + ); + + assertThat(widgetTypes.getData()).hasSize(1); + assertThat(widgetTypes.getData().get(0).getId()).isEqualTo(savedWidgetType.getId()); + + widgetTypeDao.removeById(TenantId.SYS_TENANT_ID, savedWidgetType.getUuidId()); + } + + for (var entry : SHOULDNT_FIND_SEARCH_TO_TAGS_MAP.entrySet()) { + String searchText = entry.getKey(); + String[] tags = entry.getValue(); + + WidgetTypeDetails savedWidgetType = createAndSaveWidgetType(TenantId.SYS_TENANT_ID, WIDGET_TYPE_COUNT + 1, tags); + widgetTypeDao.saveWidgetsBundleWidget(new WidgetsBundleWidget(this.widgetsBundle.getId(), savedWidgetType.getId(), WIDGET_TYPE_COUNT + 1)); + + PageData widgetTypes = widgetTypeDao.findWidgetTypesInfosByWidgetsBundleId( + TenantId.SYS_TENANT_ID.getId(), widgetsBundle.getUuidId(), true, DeprecatedFilter.ALL, null, new PageLink(10, 0, searchText) + ); + + assertThat(widgetTypes.getData()).hasSize(0); + + widgetTypeDao.removeById(TenantId.SYS_TENANT_ID, savedWidgetType.getUuidId()); + } + } + + @Test + public void testFindByTenantIdAndFqn() { WidgetType result = widgetTypeList.get(0); assertNotNull(result); - WidgetType widgetType = widgetTypeDao.findByTenantIdBundleAliasAndAlias(TenantId.SYS_TENANT_ID.getId(), BUNDLE_ALIAS, "ALIAS_0"); + WidgetType widgetType = widgetTypeDao.findByTenantIdAndFqn(TenantId.SYS_TENANT_ID.getId(), "FQN_0"); assertEquals(result.getId(), widgetType.getId()); } + + @Test + public void testFindByTenantAndImageLink() { + var result = widgetTypeDao.findByImageLink("/image/system/logo.png", 5); + assertEquals(3, result.size()); + result = widgetTypeDao.findByImageLink("/image/system/logo2.png", 5); + assertEquals(0, result.size()); + } + + @Test + public void testFindByImageLink() { + TenantId tenantId = new TenantId(UUID.randomUUID()); + WidgetTypeDetails details = createAndSaveWidgetType(tenantId, 0, new String[]{"a"}); + details.setDescriptor(JacksonUtil.newObjectNode().put("bg", "/image/tenant/widget.png")); + widgetTypeDao.save(tenantId, details); + var result = widgetTypeDao.findByTenantAndImageLink(tenantId, "/image/tenant/widget.png", 3); + assertEquals(1, result.size()); + result = widgetTypeDao.findByTenantAndImageLink(tenantId, "/image/tenant/widget2.png", 3); + assertEquals(0, result.size()); + widgetTypeDao.removeById(tenantId, details.getUuidId()); + } } diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java index 209d4193d3..08ad0df630 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/widget/JpaWidgetsBundleDaoTest.java @@ -17,36 +17,77 @@ package org.thingsboard.server.dao.sql.widget; import com.datastax.oss.driver.api.core.uuid.Uuids; import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.page.SortOrder; +import org.thingsboard.server.common.data.widget.WidgetType; +import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.common.data.widget.WidgetsBundleWidget; import org.thingsboard.server.dao.AbstractJpaDaoTest; +import org.thingsboard.server.dao.widget.WidgetTypeDao; import org.thingsboard.server.dao.widget.WidgetsBundleDao; +import java.util.ArrayList; +import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.UUID; +import java.util.stream.Collectors; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; -/** - * Created by Valerii Sosliuk on 4/23/2017. - */ public class JpaWidgetsBundleDaoTest extends AbstractJpaDaoTest { + + // given search text should find a widget with tags, when searching by tags + final Map SHOULD_FIND_SEARCH_TO_TAGS_MAP = Map.of( + "A", new String[]{"a", "b", "c"}, + "test A test", new String[]{"a", "b", "c"}, + "test x y test", new String[]{"x y", "b", "c"}, + "x y test", new String[]{"x y", "b", "c"}, + "x y", new String[]{"x y", "b", "c"}, + "test x y", new String[]{"x y", "b", "c"} + ); + + // given search text should not find a widget with tags, when searching by tags + final Map SHOULDNT_FIND_SEARCH_TO_TAGS_MAP = Map.of( + "testA test", new String[]{"a", "b", "c"}, + "testx y test", new String[]{"x y", "b", "c"}, + "testx ytest", new String[]{"x y", "b", "c"}, + "x ytest", new String[]{"x y", "b", "c"}, + "testx y", new String[]{"x y", "b", "c"}, + "x", new String[]{"x y", "b", "c"} + ); + List widgetsBundles; + List widgetTypeList; + @Autowired private WidgetsBundleDao widgetsBundleDao; + @Autowired + private WidgetTypeDao widgetTypeDao; + + @Before + public void setUp() { + widgetTypeList = new ArrayList<>(); + } + @After public void tearDown() { for (WidgetsBundle widgetsBundle : widgetsBundles) { widgetsBundleDao.removeById(widgetsBundle.getTenantId(), widgetsBundle.getUuidId()); } - + for (WidgetType widgetType : widgetTypeList) { + widgetTypeDao.removeById(TenantId.SYS_TENANT_ID, widgetType.getUuidId()); + } } @Test @@ -72,23 +113,107 @@ public class JpaWidgetsBundleDaoTest extends AbstractJpaDaoTest { assertEquals(30, widgetsBundles.size()); // Get first page PageLink pageLink = new PageLink(10, 0, "WB"); - PageData widgetsBundles1 = widgetsBundleDao.findSystemWidgetsBundles(TenantId.SYS_TENANT_ID, pageLink); + PageData widgetsBundles1 = widgetsBundleDao.findSystemWidgetsBundles(TenantId.SYS_TENANT_ID, false, pageLink); assertEquals(10, widgetsBundles1.getData().size()); // Get next page pageLink = pageLink.nextPageLink(); - PageData widgetsBundles2 = widgetsBundleDao.findSystemWidgetsBundles(TenantId.SYS_TENANT_ID, pageLink); + PageData widgetsBundles2 = widgetsBundleDao.findSystemWidgetsBundles(TenantId.SYS_TENANT_ID, false, pageLink); assertEquals(10, widgetsBundles2.getData().size()); } + @Test + public void testFindSystemWidgetsBundlesFullSearch() { + createSystemWidgetBundles(30, "WB_"); + widgetsBundles = widgetsBundleDao.find(TenantId.SYS_TENANT_ID).stream().sorted(Comparator.comparing(WidgetsBundle::getTitle)).collect(Collectors.toList()); + assertEquals(30, widgetsBundles.size()); + + var widgetType1 = createAndSaveWidgetType(TenantId.SYS_TENANT_ID,1, "Test widget type 1", "This is the widget type 1", new String[]{"tag1", "Tag2", "TEST_TAG"}); + var widgetType2 = createAndSaveWidgetType(TenantId.SYS_TENANT_ID,2, "Test widget type 2", "This is the widget type 2", new String[]{"tag3", "Tag5", "TEST_Tag2"}); + + var widgetsBundle1 = widgetsBundles.get(10); + widgetTypeDao.saveWidgetsBundleWidget(new WidgetsBundleWidget(widgetsBundle1.getId(), widgetType1.getId(), 0)); + + var widgetsBundle2 = widgetsBundles.get(15); + widgetTypeDao.saveWidgetsBundleWidget(new WidgetsBundleWidget(widgetsBundle2.getId(), widgetType2.getId(), 0)); + + + var widgetsBundle3 = widgetsBundles.get(28); + widgetTypeDao.saveWidgetsBundleWidget(new WidgetsBundleWidget(widgetsBundle3.getId(), widgetType1.getId(), 0)); + widgetTypeDao.saveWidgetsBundleWidget(new WidgetsBundleWidget(widgetsBundle3.getId(), widgetType2.getId(), 1)); + + PageLink pageLink = new PageLink(10, 0, "widget type 1", new SortOrder("title")); + PageData widgetsBundles1 = widgetsBundleDao.findSystemWidgetsBundles(TenantId.SYS_TENANT_ID, true, pageLink); + assertEquals(2, widgetsBundles1.getData().size()); + assertEquals(widgetsBundle1, widgetsBundles1.getData().get(0)); + assertEquals(widgetsBundle3, widgetsBundles1.getData().get(1)); + + pageLink = new PageLink(10, 0, "Test widget type 2", new SortOrder("title")); + PageData widgetsBundles2 = widgetsBundleDao.findSystemWidgetsBundles(TenantId.SYS_TENANT_ID, true, pageLink); + assertEquals(2, widgetsBundles2.getData().size()); + assertEquals(widgetsBundle2, widgetsBundles2.getData().get(0)); + assertEquals(widgetsBundle3, widgetsBundles2.getData().get(1)); + + pageLink = new PageLink(10, 0, "ppp Fd v TAG1 tt", new SortOrder("title")); + PageData widgetsBundles3 = widgetsBundleDao.findSystemWidgetsBundles(TenantId.SYS_TENANT_ID, true, pageLink); + assertEquals(2, widgetsBundles3.getData().size()); + assertEquals(widgetsBundle1, widgetsBundles3.getData().get(0)); + assertEquals(widgetsBundle3, widgetsBundles3.getData().get(1)); + } + + @Test + public void testTagsSearchInFindBySystemWidgetTypes() { + for (var entry : SHOULD_FIND_SEARCH_TO_TAGS_MAP.entrySet()) { + String searchText = entry.getKey(); + String[] tags = entry.getValue(); + + WidgetsBundle systemWidgetBundle = createSystemWidgetBundle("Test Widget Bundle Alias", "Test Widget Bundle Title"); + WidgetType widgetType = createAndSaveWidgetType(TenantId.SYS_TENANT_ID, 0, "Test Widget Type Name", "Test Widget Type Description", tags); + widgetTypeDao.saveWidgetsBundleWidget(new WidgetsBundleWidget(systemWidgetBundle.getId(), widgetType.getId(), 0)); + + PageData widgetTypes = widgetsBundleDao.findSystemWidgetsBundles( + TenantId.SYS_TENANT_ID, true, new PageLink(10, 0, searchText) + ); + + assertThat(widgetTypes.getData()).hasSize(1); + assertThat(widgetTypes.getData().get(0).getId()).isEqualTo(systemWidgetBundle.getId()); + + widgetTypeDao.removeWidgetTypeFromWidgetsBundle(systemWidgetBundle.getUuidId(), widgetType.getUuidId()); + widgetTypeDao.removeById(TenantId.SYS_TENANT_ID, widgetType.getUuidId()); + widgetsBundleDao.removeById(TenantId.SYS_TENANT_ID, systemWidgetBundle.getUuidId()); + } + + for (var entry : SHOULDNT_FIND_SEARCH_TO_TAGS_MAP.entrySet()) { + String searchText = entry.getKey(); + String[] tags = entry.getValue(); + + WidgetsBundle systemWidgetBundle = createSystemWidgetBundle("Test Widget Bundle Alias", "Test Widget Bundle Title"); + WidgetType widgetType = createAndSaveWidgetType(TenantId.SYS_TENANT_ID, 0, "Test Widget Type Name", "Test Widget Type Description", tags); + widgetTypeDao.saveWidgetsBundleWidget(new WidgetsBundleWidget(systemWidgetBundle.getId(), widgetType.getId(), 0)); + + PageData widgetTypes = widgetsBundleDao.findSystemWidgetsBundles( + TenantId.SYS_TENANT_ID, true, new PageLink(10, 0, searchText) + ); + + assertThat(widgetTypes.getData()).hasSize(0); + + widgetTypeDao.removeWidgetTypeFromWidgetsBundle(systemWidgetBundle.getUuidId(), widgetType.getUuidId()); + widgetTypeDao.removeById(TenantId.SYS_TENANT_ID, widgetType.getUuidId()); + widgetsBundleDao.removeById(TenantId.SYS_TENANT_ID, systemWidgetBundle.getUuidId()); + } + + widgetsBundles = new ArrayList<>(); + widgetTypeList = new ArrayList<>(); + } + @Test public void testFindWidgetsBundlesByTenantId() { UUID tenantId1 = Uuids.timeBased(); UUID tenantId2 = Uuids.timeBased(); // Create a bunch of widgetBundles for (int i = 0; i < 10; i++) { - createWidgetBundles(3, tenantId1, "WB1_"); - createWidgetBundles(5, tenantId2, "WB2_"); - createSystemWidgetBundles(10, "WB_SYS_"); + createWidgetBundles(3, tenantId1, "WB1_" + i + "_"); + createWidgetBundles(5, tenantId2, "WB2_" + i + "_"); + createSystemWidgetBundles(10, "WB_SYS_" + i + "_"); } widgetsBundles = widgetsBundleDao.find(TenantId.SYS_TENANT_ID); assertEquals(180, widgetsBundleDao.find(TenantId.SYS_TENANT_ID).size()); @@ -112,30 +237,152 @@ public class JpaWidgetsBundleDaoTest extends AbstractJpaDaoTest { UUID tenantId2 = Uuids.timeBased(); // Create a bunch of widgetBundles for (int i = 0; i < 10; i++) { - createWidgetBundles(5, tenantId1, "WB1_"); - createWidgetBundles(3, tenantId2, "WB2_"); - createSystemWidgetBundles(2, "WB_SYS_"); + createWidgetBundles(5, tenantId1, "WB1_" + i + "_"); + createWidgetBundles(3, tenantId2, "WB2_" + i + "_"); + createSystemWidgetBundles(2, "WB_SYS_" + i + "_"); } widgetsBundles = widgetsBundleDao.find(TenantId.SYS_TENANT_ID); assertEquals(100, widgetsBundleDao.find(TenantId.SYS_TENANT_ID).size()); PageLink pageLink = new PageLink(30, 0, "WB"); - PageData widgetsBundles1 = widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(tenantId1, pageLink); + PageData widgetsBundles1 = widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(tenantId1, false, pageLink); assertEquals(30, widgetsBundles1.getData().size()); pageLink = pageLink.nextPageLink(); - PageData widgetsBundles2 = widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(tenantId1, pageLink); + PageData widgetsBundles2 = widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(tenantId1, false, pageLink); assertEquals(30, widgetsBundles2.getData().size()); pageLink = pageLink.nextPageLink(); - PageData widgetsBundles3 = widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(tenantId1, pageLink); + PageData widgetsBundles3 = widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(tenantId1, false, pageLink); assertEquals(10, widgetsBundles3.getData().size()); pageLink = pageLink.nextPageLink(); - PageData widgetsBundles4 = widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(tenantId1, pageLink); + PageData widgetsBundles4 = widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(tenantId1, false, pageLink); assertEquals(0, widgetsBundles4.getData().size()); } + @Test + public void testFindAllWidgetsBundlesByTenantIdFullSearch() { + UUID tenantId1 = Uuids.timeBased(); + UUID tenantId2 = Uuids.timeBased(); + for (int i = 0; i < 10; i++) { + createWidgetBundles(5, tenantId1, "WB1_" + i + "_"); + createWidgetBundles(3, tenantId2, "WB2_" + i + "_"); + createSystemWidgetBundles(2, "WB_SYS_" + i + "_"); + } + widgetsBundles = widgetsBundleDao.find(TenantId.SYS_TENANT_ID).stream().sorted(Comparator.comparing(WidgetsBundle::getTitle)).collect(Collectors.toList());; + assertEquals(100, widgetsBundles.size()); + + var widgetType1 = createAndSaveWidgetType(new TenantId(tenantId1), 1, "Test widget type 1", "This is the widget type 1", new String[]{"tag1", "Tag2", "TEST_TAG"}); + var widgetType2 = createAndSaveWidgetType(new TenantId(tenantId2), 2, "Test widget type 2", "This is the widget type 2", new String[]{"tag3", "Tag5", "TEST_Tag2"}); + + var widgetsBundle1 = widgetsBundles.stream().filter(widgetsBundle -> widgetsBundle.getTenantId().getId().equals(tenantId1)).collect(Collectors.toList()).get(10); + widgetTypeDao.saveWidgetsBundleWidget(new WidgetsBundleWidget(widgetsBundle1.getId(), widgetType1.getId(), 0)); + + var widgetsBundle2 = widgetsBundles.stream().filter(widgetsBundle -> widgetsBundle.getTenantId().getId().equals(tenantId2)).collect(Collectors.toList()).get(15); + widgetTypeDao.saveWidgetsBundleWidget(new WidgetsBundleWidget(widgetsBundle2.getId(), widgetType2.getId(), 0)); + + var widgetsBundle3 = widgetsBundles.stream().filter(widgetsBundle -> widgetsBundle.getTenantId().getId().equals(tenantId2)).collect(Collectors.toList()).get(28); + widgetTypeDao.saveWidgetsBundleWidget(new WidgetsBundleWidget(widgetsBundle3.getId(), widgetType1.getId(), 0)); + widgetTypeDao.saveWidgetsBundleWidget(new WidgetsBundleWidget(widgetsBundle3.getId(), widgetType2.getId(), 1)); + + PageLink pageLink = new PageLink(10, 0, "widget type 1", new SortOrder("title")); + PageData widgetsBundles1 = widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(tenantId1, true, pageLink); + assertEquals(1, widgetsBundles1.getData().size()); + assertEquals(widgetsBundle1, widgetsBundles1.getData().get(0)); + + pageLink = new PageLink(10, 0, "Test widget type 2", new SortOrder("title")); + PageData widgetsBundles2 = widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(tenantId1, true, pageLink); + assertEquals(0, widgetsBundles2.getData().size()); + + PageData widgetsBundles3 = widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(tenantId2, true, pageLink); + assertEquals(2, widgetsBundles3.getData().size()); + assertEquals(widgetsBundle2, widgetsBundles3.getData().get(0)); + assertEquals(widgetsBundle3, widgetsBundles3.getData().get(1)); + + pageLink = new PageLink(10, 0, "ttt Tag2 ffff hhhh", new SortOrder("title")); + PageData widgetsBundles4 = widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(tenantId1, true, pageLink); + assertEquals(1, widgetsBundles4.getData().size()); + assertEquals(widgetsBundle1, widgetsBundles4.getData().get(0)); + + PageData widgetsBundles5 = widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(tenantId2, true, pageLink); + assertEquals(1, widgetsBundles5.getData().size()); + assertEquals(widgetsBundle3, widgetsBundles5.getData().get(0)); + } + + @Test + public void testTagsSearchInFindAllWidgetsBundlesByTenantId() { + for (var entry : SHOULD_FIND_SEARCH_TO_TAGS_MAP.entrySet()) { + String searchText = entry.getKey(); + String[] tags = entry.getValue(); + + WidgetsBundle systemWidgetBundle = createSystemWidgetBundle("Test Widget Bundle Alias", "Test Widget Bundle Title"); + WidgetType widgetType = createAndSaveWidgetType(TenantId.SYS_TENANT_ID, 0, "Test Widget Type Name", "Test Widget Type Description", tags); + widgetTypeDao.saveWidgetsBundleWidget(new WidgetsBundleWidget(systemWidgetBundle.getId(), widgetType.getId(), 0)); + + PageData widgetTypes = widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId( + TenantId.SYS_TENANT_ID.getId(), true, new PageLink(10, 0, searchText) + ); + + assertThat(widgetTypes.getData()).hasSize(1); + assertThat(widgetTypes.getData().get(0).getId()).isEqualTo(systemWidgetBundle.getId()); + + widgetTypeDao.removeWidgetTypeFromWidgetsBundle(systemWidgetBundle.getUuidId(), widgetType.getUuidId()); + widgetTypeDao.removeById(TenantId.SYS_TENANT_ID, widgetType.getUuidId()); + widgetsBundleDao.removeById(TenantId.SYS_TENANT_ID, systemWidgetBundle.getUuidId()); + } + + for (var entry : SHOULDNT_FIND_SEARCH_TO_TAGS_MAP.entrySet()) { + String searchText = entry.getKey(); + String[] tags = entry.getValue(); + + WidgetsBundle systemWidgetBundle = createSystemWidgetBundle("Test Widget Bundle Alias", "Test Widget Bundle Title"); + WidgetType widgetType = createAndSaveWidgetType(TenantId.SYS_TENANT_ID, 0, "Test Widget Type Name", "Test Widget Type Description", tags); + widgetTypeDao.saveWidgetsBundleWidget(new WidgetsBundleWidget(systemWidgetBundle.getId(), widgetType.getId(), 0)); + + PageData widgetTypes = widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId( + TenantId.SYS_TENANT_ID.getId(), true, new PageLink(10, 0, searchText) + ); + + assertThat(widgetTypes.getData()).hasSize(0); + + widgetTypeDao.removeWidgetTypeFromWidgetsBundle(systemWidgetBundle.getUuidId(), widgetType.getUuidId()); + widgetTypeDao.removeById(TenantId.SYS_TENANT_ID, widgetType.getUuidId()); + widgetsBundleDao.removeById(TenantId.SYS_TENANT_ID, systemWidgetBundle.getUuidId()); + } + + widgetsBundles = new ArrayList<>(); + widgetTypeList = new ArrayList<>(); + } + + @Test + public void testOrderInFindAllWidgetsBundlesByTenantIdFullSearch() { + UUID tenantId1 = Uuids.timeBased(); + for (int i = 0; i < 10; i++) { + createWidgetsBundle(TenantId.fromUUID(tenantId1), "WB1_" + i, "WB1_" + (10-i), i % 2 == 1 ? null : (int)(Math.random() * 1000)); + createWidgetsBundle(TenantId.SYS_TENANT_ID, "WB_SYS_" + i, "WB_SYS_" + (10-i), i % 2 == 0 ? null : (int)(Math.random() * 1000)); + } + widgetsBundles = widgetsBundleDao.find(TenantId.SYS_TENANT_ID).stream().sorted((o1, o2) -> { + int result = 0; + if (o1.getOrder() != null && o2.getOrder() != null) { + result = o1.getOrder() - o2.getOrder(); + } else if (o1.getOrder() == null && o2.getOrder() != null) { + result = 1; + } else if (o1.getOrder() != null) { + result = -1; + } + if (result == 0) { + result = o1.getTitle().compareTo(o2.getTitle()); + } + return result; + }).collect(Collectors.toList());; + assertEquals(20, widgetsBundles.size()); + PageLink pageLink = new PageLink(100, 0, "", new SortOrder("title")); + PageData widgetsBundlesData = widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(tenantId1, true, pageLink); + assertEquals(20, widgetsBundlesData.getData().size()); + assertEquals(widgetsBundles, widgetsBundlesData.getData()); + } + @Test public void testSearchTextNotFound() { UUID tenantId = Uuids.timeBased(); @@ -144,29 +391,45 @@ public class JpaWidgetsBundleDaoTest extends AbstractJpaDaoTest { widgetsBundles = widgetsBundleDao.find(TenantId.SYS_TENANT_ID); assertEquals(10, widgetsBundleDao.find(TenantId.SYS_TENANT_ID).size()); PageLink textPageLink = new PageLink(30, 0, "TEXT_NOT_FOUND"); - PageData widgetsBundles4 = widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(tenantId, textPageLink); + PageData widgetsBundles4 = widgetsBundleDao.findAllTenantWidgetsBundlesByTenantId(tenantId, false, textPageLink); assertEquals(0, widgetsBundles4.getData().size()); } private void createWidgetBundles(int count, UUID tenantId, String prefix) { for (int i = 0; i < count; i++) { - WidgetsBundle widgetsBundle = new WidgetsBundle(); - widgetsBundle.setAlias(prefix + i); - widgetsBundle.setTitle(prefix + i); - widgetsBundle.setId(new WidgetsBundleId(Uuids.timeBased())); - widgetsBundle.setTenantId(TenantId.fromUUID(tenantId)); - widgetsBundleDao.save(TenantId.SYS_TENANT_ID, widgetsBundle); + createWidgetsBundle(TenantId.fromUUID(tenantId), prefix + i, prefix + i, null); } } private void createSystemWidgetBundles(int count, String prefix) { for (int i = 0; i < count; i++) { - WidgetsBundle widgetsBundle = new WidgetsBundle(); - widgetsBundle.setAlias(prefix + i); - widgetsBundle.setTitle(prefix + i); - widgetsBundle.setTenantId(TenantId.SYS_TENANT_ID); - widgetsBundle.setId(new WidgetsBundleId(Uuids.timeBased())); - widgetsBundleDao.save(TenantId.SYS_TENANT_ID, widgetsBundle); + createWidgetsBundle(TenantId.SYS_TENANT_ID, prefix + i, prefix + i, null); } } + + private WidgetsBundle createSystemWidgetBundle(String alias, String title) { + return createWidgetsBundle(TenantId.SYS_TENANT_ID, alias, title, null); + } + + private WidgetsBundle createWidgetsBundle(TenantId tenantId, String alias, String title, Integer order) { + WidgetsBundle widgetsBundle = new WidgetsBundle(); + widgetsBundle.setAlias(alias); + widgetsBundle.setTitle(title); + widgetsBundle.setTenantId(tenantId); + widgetsBundle.setId(new WidgetsBundleId(Uuids.timeBased())); + widgetsBundle.setOrder(order); + return widgetsBundleDao.save(TenantId.SYS_TENANT_ID, widgetsBundle); + } + + WidgetType createAndSaveWidgetType(TenantId tenantId, int number, String name, String description, String[] tags) { + WidgetTypeDetails widgetType = new WidgetTypeDetails(); + widgetType.setTenantId(tenantId); + widgetType.setName(name); + widgetType.setDescription(description); + widgetType.setTags(tags); + widgetType.setFqn("FQN_" + number); + var saved = widgetTypeDao.save(TenantId.SYS_TENANT_ID, widgetType); + this.widgetTypeList.add(saved); + return saved; + } } diff --git a/dao/src/test/java/org/thingsboard/server/dao/util/DeviceConnectivityUtilTest.java b/dao/src/test/java/org/thingsboard/server/dao/util/DeviceConnectivityUtilTest.java new file mode 100644 index 0000000000..c30cbacc2b --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/util/DeviceConnectivityUtilTest.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.util; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class DeviceConnectivityUtilTest { + + @Test + void testRootCaPemNaming() { + assertThat(DeviceConnectivityUtil.CA_ROOT_CERT_PEM).contains("root"); + assertThat(DeviceConnectivityUtil.CA_ROOT_CERT_PEM).contains("ca"); + assertThat(DeviceConnectivityUtil.CA_ROOT_CERT_PEM).endsWith(".pem"); + assertThat(DeviceConnectivityUtil.CA_ROOT_CERT_PEM).doesNotContainAnyWhitespaces(); + } + +} diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties index d89211cb2f..05f2cf7670 100644 --- a/dao/src/test/resources/application-test.properties +++ b/dao/src/test/resources/application-test.properties @@ -74,6 +74,12 @@ cache.specs.dashboardTitles.maxSize=10000 cache.specs.entityCount.timeToLiveInMinutes=1440 cache.specs.entityCount.maxSize=10000 +cache.specs.resourceInfo.timeToLiveInMinutes=1440 +cache.specs.resourceInfo.maxSize=10000 + +cache.specs.alarmTypes.timeToLiveInMinutes=60 +cache.specs.alarmTypes.maxSize=10000 + redis.connection.host=localhost redis.connection.port=6379 redis.connection.db=0 @@ -133,3 +139,5 @@ queue.rule-engine.queues[2].partitions=2 queue.rule-engine.queues[2].processing-strategy.retries=1 queue.rule-engine.queues[2].processing-strategy.pause-between-retries=0 queue.rule-engine.queues[2].processing-strategy.max-pause-between-retries=0 + +spring.jpa.properties.hibernate.dialect=org.thingsboard.server.dao.ThingsboardPostgreSQLDialect \ No newline at end of file diff --git a/dao/src/test/resources/sql/psql/drop-all-tables.sql b/dao/src/test/resources/sql/psql/drop-all-tables.sql index 2fca79e01e..9c772df45b 100644 --- a/dao/src/test/resources/sql/psql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/psql/drop-all-tables.sql @@ -19,6 +19,7 @@ DROP TABLE IF EXISTS admin_settings; DROP TABLE IF EXISTS entity_alarm; DROP TABLE IF EXISTS alarm_comment; DROP TABLE IF EXISTS alarm; +DROP TABLE IF EXISTS alarm_type; DROP TABLE IF EXISTS asset; DROP TABLE IF EXISTS audit_log; DROP TABLE IF EXISTS attribute_kv; @@ -37,6 +38,7 @@ DROP TABLE IF EXISTS ts_kv; DROP TABLE IF EXISTS ts_kv_latest; DROP TABLE IF EXISTS ts_kv_dictionary; DROP TABLE IF EXISTS user_credentials; +DROP TABLE IF EXISTS widgets_bundle_widget; DROP TABLE IF EXISTS widget_type; DROP TABLE IF EXISTS widgets_bundle; DROP TABLE IF EXISTS entity_view; diff --git a/dao/src/test/resources/sql/system-data.sql b/dao/src/test/resources/sql/system-data.sql index debf698623..7a79f1114d 100644 --- a/dao/src/test/resources/sql/system-data.sql +++ b/dao/src/test/resources/sql/system-data.sql @@ -43,6 +43,16 @@ VALUES ( '6eaaefa6-4612-11e7-a919-92ebcb67fe33', 1592576748000, '13814000-1dd2-1 "password": "" }' ); +INSERT INTO admin_settings ( id, created_time, tenant_id, key, json_value ) +VALUES ( '23199d80-6e7e-11ee-8829-ef9fd52a6141', 1697719852888, '13814000-1dd2-11b2-8080-808080808080', 'connectivity', '{ + "http":{"enabled":true,"host":"","port":"8080"}, + "https":{"enabled":false,"host":"","port":"443"}, + "mqtt":{"enabled":true,"host":"","port":"1883"}, + "mqtts":{"enabled":false,"host":"","port":"8883"}, + "coap":{"enabled":true,"host":"","port":"5683"}, + "coaps":{"enabled":false,"host":"","port":"5684"} +}' ); + INSERT INTO queue ( id, created_time, tenant_id, name, topic, poll_interval, partitions, consumer_per_partition, pack_processing_timeout, submit_strategy, processing_strategy ) VALUES ( '6eaaefa6-4612-11e7-a919-92ebcb67fe33', 1592576748000 ,'13814000-1dd2-11b2-8080-808080808080', 'Main' ,'tb_rule_engine.main', 25, 10, true, 2000, '{"type": "BURST", "batchSize": 1000}', diff --git a/docker/docker-compose.hybrid.yml b/docker/docker-compose.hybrid.yml index 29cd65a127..7f952fe6eb 100644 --- a/docker/docker-compose.hybrid.yml +++ b/docker/docker-compose.hybrid.yml @@ -19,7 +19,7 @@ version: '3.0' services: postgres: restart: always - image: "postgres:12" + image: "postgres:15" ports: - "5432" environment: diff --git a/docker/docker-compose.postgres.yml b/docker/docker-compose.postgres.yml index 7f5f69a61f..218642317b 100644 --- a/docker/docker-compose.postgres.yml +++ b/docker/docker-compose.postgres.yml @@ -19,7 +19,7 @@ version: '3.0' services: postgres: restart: always - image: "postgres:12" + image: "postgres:15" ports: - "5432" environment: diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index b4320577c7..942be3782a 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -235,6 +235,8 @@ services: tb-snmp-transport: restart: always image: "${DOCKER_REPO}/${SNMP_TRANSPORT_DOCKER_NAME}:${TB_VERSION}" + ports: + - "1620:1620/udp" environment: TB_SERVICE_ID: tb-snmp-transport JAVA_OPTS: "${JAVA_OPTS}" @@ -298,7 +300,7 @@ services: haproxy: restart: always container_name: "${LOAD_BALANCER_NAME}" - image: thingsboard/haproxy-certbot:1.3.0 + image: thingsboard/haproxy-certbot:2.2.31-alpine3.18 volumes: - ./haproxy/config:/config - ./haproxy/letsencrypt:/etc/letsencrypt diff --git a/docker/tb-snmp-transport.env b/docker/tb-snmp-transport.env index e2cc39d658..32160de100 100644 --- a/docker/tb-snmp-transport.env +++ b/docker/tb-snmp-transport.env @@ -1,6 +1,7 @@ ZOOKEEPER_ENABLED=true ZOOKEEPER_URL=zookeeper:2181 +SNMP_BIND_PORT=1620 METRICS_ENABLED=true METRICS_ENDPOINTS_EXPOSE=prometheus WEB_APPLICATION_ENABLE=true diff --git a/monitoring/pom.xml b/monitoring/pom.xml index cc3a78c04d..b52212b064 100644 --- a/monitoring/pom.xml +++ b/monitoring/pom.xml @@ -21,7 +21,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/config/MonitoringTarget.java b/monitoring/src/main/java/org/thingsboard/monitoring/config/MonitoringTarget.java index 0e62670f81..0176b14d54 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/config/MonitoringTarget.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/config/MonitoringTarget.java @@ -21,4 +21,8 @@ public interface MonitoringTarget { UUID getDeviceId(); + String getBaseUrl(); + + boolean isCheckDomainIps(); + } diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportMonitoringConfig.java b/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportMonitoringConfig.java index 77d702f779..c5f843192c 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportMonitoringConfig.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportMonitoringConfig.java @@ -23,9 +23,8 @@ import java.util.List; @Data public abstract class TransportMonitoringConfig implements MonitoringConfig { - private int requestTimeoutMs; - private List targets; + private int requestTimeoutMs; public abstract TransportType getTransportType(); diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportMonitoringTarget.java b/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportMonitoringTarget.java index 816f64fbce..e3277af0ff 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportMonitoringTarget.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/config/transport/TransportMonitoringTarget.java @@ -25,6 +25,7 @@ public class TransportMonitoringTarget implements MonitoringTarget { private String baseUrl; private DeviceConfig device; // set manually during initialization + private boolean checkDomainIps; @Override public UUID getDeviceId() { diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/data/Latencies.java b/monitoring/src/main/java/org/thingsboard/monitoring/data/Latencies.java index 3370d42462..52e73a64cd 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/data/Latencies.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/data/Latencies.java @@ -25,4 +25,8 @@ public class Latencies { return String.format("%sRequest", key); } + public static String wsUpdate(String key) { + return String.format("%sWsUpdate", key); + } + } diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/data/Latency.java b/monitoring/src/main/java/org/thingsboard/monitoring/data/Latency.java index c64291e30f..4b17bf179f 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/data/Latency.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/data/Latency.java @@ -15,53 +15,15 @@ */ package org.thingsboard.monitoring.data; -import com.google.common.util.concurrent.AtomicDouble; -import lombok.RequiredArgsConstructor; +import lombok.Data; -import java.util.concurrent.atomic.AtomicInteger; - -@RequiredArgsConstructor +@Data(staticConstructor = "of") public class Latency { - private final String key; - private final AtomicDouble latencySum = new AtomicDouble(); - private final AtomicInteger counter = new AtomicInteger(); - - public synchronized void report(double latencyInMs) { - latencySum.addAndGet(latencyInMs); - counter.incrementAndGet(); - } - - public synchronized double getAvg() { - return latencySum.get() / counter.get(); - } - - public boolean isNotEmpty() { - return counter.get() > 0; - } - - public synchronized void reset() { - latencySum.set(0.0); - counter.set(0); - } - - public String getKey() { - return key; - } - - public synchronized Latency snapshot() { - Latency snapshot = new Latency(key); - snapshot.latencySum.set(latencySum.get()); - snapshot.counter.set(counter.get()); - return snapshot; - } + private final double value; - @Override - public String toString() { - return "Latency{" + - "key='" + key + '\'' + - ", avgLatency=" + getAvg() + - '}'; + public String getFormattedValue() { + return String.format("%,.2f ms", value); } } diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/data/notification/HighLatencyNotification.java b/monitoring/src/main/java/org/thingsboard/monitoring/data/notification/HighLatencyNotification.java index 939cb7440f..5744e40d41 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/data/notification/HighLatencyNotification.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/data/notification/HighLatencyNotification.java @@ -21,11 +21,11 @@ import java.util.Collection; public class HighLatencyNotification implements Notification { - private final Collection latencies; + private final Collection highLatencies; private final int thresholdMs; - public HighLatencyNotification(Collection latencies, int thresholdMs) { - this.latencies = latencies; + public HighLatencyNotification(Collection highLatencies, int thresholdMs) { + this.highLatencies = highLatencies; this.thresholdMs = thresholdMs; } @@ -33,8 +33,8 @@ public class HighLatencyNotification implements Notification { public String getText() { StringBuilder text = new StringBuilder(); text.append("Some of the latencies are higher than ").append(thresholdMs).append(" ms:\n"); - latencies.forEach(latency -> { - text.append(String.format("[%s] %,.2f ms\n", latency.getKey(), latency.getAvg())); + highLatencies.forEach(latency -> { + text.append(String.format("[%s] %s\n", latency.getKey(), latency.getFormattedValue())); }); return text.toString(); } diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java b/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java index affca1ce09..290fd307c6 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java @@ -15,6 +15,7 @@ */ package org.thingsboard.monitoring.service; +import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -30,13 +31,17 @@ import org.thingsboard.monitoring.util.TbStopWatch; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; +import java.util.HashMap; +import java.util.Map; import java.util.UUID; @RequiredArgsConstructor @Slf4j public abstract class BaseHealthChecker { + @Getter protected final C config; + @Getter protected final T target; private Object info; @@ -48,6 +53,9 @@ public abstract class BaseHealthChecker> associates = new HashMap<>(); + public static final String TEST_TELEMETRY_KEY = "testData"; @PostConstruct @@ -84,6 +92,10 @@ public abstract class BaseHealthChecker { + healthChecker.check(wsClient); + }); } private void checkWsUpdate(WsClient wsClient, String testValue) { @@ -96,10 +108,9 @@ public abstract class BaseHealthChecker, T extends MonitoringTarget> { - @Autowired + @Autowired(required = false) private List configs; private final List> healthCheckers = new LinkedList<>(); private final List devices = new LinkedList<>(); @@ -54,18 +63,32 @@ public abstract class BaseMonitoringService, T ext @PostConstruct private void init() { + if (configs == null || configs.isEmpty()) { + return; + } tbClient.logIn(); configs.forEach(config -> { config.getTargets().forEach(target -> { - BaseHealthChecker healthChecker = (BaseHealthChecker) createHealthChecker(config, target); - log.info("Initializing {}", healthChecker.getClass().getSimpleName()); - healthChecker.initialize(tbClient); - devices.add(target.getDeviceId()); + BaseHealthChecker healthChecker = initHealthChecker(target, config); healthCheckers.add(healthChecker); + + if (target.isCheckDomainIps()) { + getAssociatedUrls(target.getBaseUrl()).forEach(url -> { + healthChecker.getAssociates().put(url, initHealthChecker(createTarget(url), config)); + }); + } }); }); } + private BaseHealthChecker initHealthChecker(T target, C config) { + BaseHealthChecker healthChecker = (BaseHealthChecker) createHealthChecker(config, target); + log.info("Initializing {} for {}", healthChecker.getClass().getSimpleName(), target.getBaseUrl()); + healthChecker.initialize(tbClient); + devices.add(target.getDeviceId()); + return healthChecker; + } + public final void runChecks() { if (healthCheckers.isEmpty()) { return; @@ -78,9 +101,8 @@ public abstract class BaseMonitoringService, T ext try (WsClient wsClient = wsClientFactory.createClient(accessToken)) { wsClient.subscribeForTelemetry(devices, TransportHealthChecker.TEST_TELEMETRY_KEY).waitForReply(); - for (BaseHealthChecker healthChecker : healthCheckers) { - healthChecker.check(wsClient); + check(healthChecker, wsClient); } } reporter.reportLatencies(tbClient); @@ -94,8 +116,61 @@ public abstract class BaseMonitoringService, T ext } } + private void check(BaseHealthChecker healthChecker, WsClient wsClient) throws Exception { + healthChecker.check(wsClient); + + T target = healthChecker.getTarget(); + if (target.isCheckDomainIps()) { + Set associatedUrls = getAssociatedUrls(target.getBaseUrl()); + Map> associates = healthChecker.getAssociates(); + Set prevAssociatedUrls = new HashSet<>(associates.keySet()); + + boolean changed = false; + for (String url : associatedUrls) { + if (!prevAssociatedUrls.contains(url)) { + BaseHealthChecker associate = initHealthChecker(createTarget(url), healthChecker.getConfig()); + associates.put(url, associate); + changed = true; + } + } + for (String url : prevAssociatedUrls) { + if (!associatedUrls.contains(url)) { + stopHealthChecker(healthChecker); + associates.remove(url); + changed = true; + } + } + if (changed) { + log.info("Updated IPs for {}: {} (old list: {})", target.getBaseUrl(), associatedUrls, prevAssociatedUrls); + } + } + } + + @SneakyThrows + private Set getAssociatedUrls(String baseUrl) { + URI url = new URI(baseUrl); + return Arrays.stream(InetAddress.getAllByName(url.getHost())) + .map(InetAddress::getHostAddress) + .map(ip -> { + try { + return new URI(url.getScheme(), null, ip, url.getPort(), "", null, null).toString(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + }) + .collect(Collectors.toSet()); + } + + private void stopHealthChecker(BaseHealthChecker healthChecker) throws Exception { + healthChecker.destroyClient(); + devices.remove(healthChecker.getTarget().getDeviceId()); + log.info("Stopped {} for {}", healthChecker.getClass().getSimpleName(), healthChecker.getTarget().getBaseUrl()); + } + protected abstract BaseHealthChecker createHealthChecker(C config, T target); + protected abstract T createTarget(String baseUrl); + protected abstract String getName(); } diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/service/MonitoringReporter.java b/monitoring/src/main/java/org/thingsboard/monitoring/service/MonitoringReporter.java index f41454a76a..aabf04caee 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/service/MonitoringReporter.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/service/MonitoringReporter.java @@ -63,25 +63,20 @@ public class MonitoringReporter { private String reportingAssetId; public void reportLatencies(TbClient tbClient) { - List latencies = this.latencies.values().stream() - .filter(Latency::isNotEmpty) - .map(latency -> { - Latency snapshot = latency.snapshot(); - latency.reset(); - return snapshot; - }) - .collect(Collectors.toList()); if (latencies.isEmpty()) { return; } - log.info("Latencies:\n{}", latencies.stream().map(latency -> latency.getKey() + ": " + latency.getAvg() + " ms") + log.debug("Latencies:\n{}", latencies.values().stream().map(latency -> latency.getKey() + ": " + latency.getFormattedValue()) .collect(Collectors.joining("\n")) + "\n"); - if (!latencyReportingEnabled) return; - if (latencies.stream().anyMatch(latency -> latency.getAvg() >= (double) latencyThresholdMs)) { - HighLatencyNotification highLatencyNotification = new HighLatencyNotification(latencies, latencyThresholdMs); + List highLatencies = latencies.values().stream() + .filter(latency -> latency.getValue() >= (double) latencyThresholdMs) + .collect(Collectors.toList()); + if (!highLatencies.isEmpty()) { + HighLatencyNotification highLatencyNotification = new HighLatencyNotification(highLatencies, latencyThresholdMs); notificationService.sendNotification(highLatencyNotification); + log.warn("{}", highLatencyNotification.getText()); } try { @@ -99,10 +94,11 @@ public class MonitoringReporter { } ObjectNode msg = JacksonUtil.newObjectNode(); - latencies.forEach(latency -> { - msg.set(latency.getKey(), new DoubleNode(latency.getAvg())); + latencies.values().forEach(latency -> { + msg.set(latency.getKey(), new DoubleNode(latency.getValue())); }); tbClient.saveEntityTelemetry(new AssetId(UUID.fromString(reportingAssetId)), "time", msg); + latencies.clear(); } catch (Exception e) { log.error("Failed to report latencies: {}", e.getMessage()); } @@ -112,7 +108,7 @@ public class MonitoringReporter { String latencyKey = key + "Latency"; double latencyInMs = (double) latencyInNanos / 1000_000; log.trace("Reporting latency [{}]: {} ms", key, latencyInMs); - latencies.computeIfAbsent(latencyKey, k -> new Latency(latencyKey)).report(latencyInMs); + latencies.put(latencyKey, Latency.of(latencyKey, latencyInMs)); } public void serviceFailure(Object serviceKey, Throwable error) { diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/service/transport/TransportsMonitoringService.java b/monitoring/src/main/java/org/thingsboard/monitoring/service/transport/TransportsMonitoringService.java index b3ce86e799..ca7c5a94da 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/service/transport/TransportsMonitoringService.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/service/transport/TransportsMonitoringService.java @@ -33,6 +33,13 @@ public final class TransportsMonitoringService extends BaseMonitoringService 20;" + }, + "externalId": null + }, + { + "additionalInfo": { + "description": "", + "layoutX": 427, + "layoutY": 541 + }, + "type": "org.thingsboard.rule.engine.filter.TbJsFilterNode", + "name": "Test TBEL script", + "debugMode": false, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "scriptLang": "TBEL", + "jsScript": "return msg.temperature > 20;", + "tbelScript": "var a = \"a\";\nvar b = \"b\";\nreturn a.equals(\"a\") && b.equals(\"b\");" + }, + "externalId": null + }, + { + "additionalInfo": { + "description": "", + "layoutX": 40, + "layoutY": 252 + }, + "type": "org.thingsboard.rule.engine.transform.TbTransformMsgNode", + "name": "Add arrival timestamp", + "debugMode": false, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "scriptLang": "TBEL", + "jsScript": "return {msg: msg, metadata: metadata, msgType: msgType};", + "tbelScript": "metadata.arrivalTs = Date.now();\nreturn {msg: msg, metadata: metadata, msgType: msgType};" + }, + "externalId": null + }, + { + "additionalInfo": { + "description": "", + "layoutX": 1467, + "layoutY": 267 + }, + "type": "org.thingsboard.rule.engine.transform.TbTransformMsgNode", + "name": "Calculate additional latencies", + "debugMode": true, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "scriptLang": "TBEL", + "jsScript": "return {msg: msg, metadata: metadata, msgType: msgType};", + "tbelScript": "var arrivalLatency = metadata.arrivalTs - metadata.ts;\nvar processingTime = Date.now() - metadata.arrivalTs;\nmsg = {\n arrivalLatency: arrivalLatency,\n processingTime: processingTime\n};\nreturn {msg: msg, metadata: metadata, msgType: msgType};" + }, + "externalId": null + }, + { + "additionalInfo": { + "description": "", + "layoutX": 1438, + "layoutY": 403 + }, + "type": "org.thingsboard.rule.engine.transform.TbChangeOriginatorNode", + "name": "To latencies asset", + "debugMode": false, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "originatorSource": "ENTITY", + "entityType": "ASSET", + "entityNamePattern": "[Monitoring] Latencies", + "relationsQuery": { + "direction": "FROM", + "maxLevel": 1, + "filters": [ + { + "relationType": "Contains", + "entityTypes": [] + } + ], + "fetchLastLevelOnly": false + } + }, + "externalId": null + }, + { + "additionalInfo": { + "description": null, + "layoutX": 1458, + "layoutY": 505 + }, + "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", + "name": "Save Timeseries", + "debugMode": true, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "defaultTTL": 0 + }, + "externalId": null + }, + { + "additionalInfo": { + "description": "", + "layoutX": 928, + "layoutY": 266 + }, + "type": "org.thingsboard.rule.engine.filter.TbCheckMessageNode", + "name": "Has testData", + "debugMode": false, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "messageNames": [ + "testData" + ], + "metadataNames": [], + "checkAllKeys": true + }, + "externalId": null + }, + { + "additionalInfo": { + "description": null, + "layoutX": 1203, + "layoutY": 327 + }, + "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", + "name": "Save Timeseries with TTL", + "debugMode": true, + "singletonMode": false, + "configurationVersion": 0, + "configuration": { + "defaultTTL": 180, + "skipLatestPersistence": null, + "useServerTs": null + }, + "externalId": null + } + ], + "connections": [ + { + "fromIndex": 2, + "toIndex": 1, + "type": "Post attributes" + }, + { + "fromIndex": 2, + "toIndex": 3, + "type": "RPC Request from Device" + }, + { + "fromIndex": 2, + "toIndex": 4, + "type": "Other" + }, + { + "fromIndex": 2, + "toIndex": 5, + "type": "RPC Request to Device" + }, + { + "fromIndex": 2, + "toIndex": 16, + "type": "Post telemetry" + }, + { + "fromIndex": 6, + "toIndex": 2, + "type": "False" + }, + { + "fromIndex": 6, + "toIndex": 7, + "type": "True" + }, + { + "fromIndex": 7, + "toIndex": 2, + "type": "False" + }, + { + "fromIndex": 7, + "toIndex": 8, + "type": "True" + }, + { + "fromIndex": 8, + "toIndex": 2, + "type": "Success" + }, + { + "fromIndex": 9, + "toIndex": 10, + "type": "Success" + }, + { + "fromIndex": 10, + "toIndex": 11, + "type": "True" + }, + { + "fromIndex": 11, + "toIndex": 6, + "type": "True" + }, + { + "fromIndex": 12, + "toIndex": 9, + "type": "Success" + }, + { + "fromIndex": 13, + "toIndex": 14, + "type": "Success" + }, + { + "fromIndex": 14, + "toIndex": 15, + "type": "Success" + }, + { + "fromIndex": 16, + "toIndex": 0, + "type": "False" + }, + { + "fromIndex": 16, + "toIndex": 17, + "type": "True" + }, + { + "fromIndex": 17, + "toIndex": 13, + "type": "Success" + } + ], + "ruleChainConnections": null + } +} \ No newline at end of file diff --git a/monitoring/src/main/resources/tb-monitoring.yml b/monitoring/src/main/resources/tb-monitoring.yml index 88979bff1a..57b9b39ec0 100644 --- a/monitoring/src/main/resources/tb-monitoring.yml +++ b/monitoring/src/main/resources/tb-monitoring.yml @@ -51,8 +51,10 @@ monitoring: # MQTT QoS qos: '${MQTT_QOS_LEVEL:1}' targets: - # MQTT transport base url, tcp://DOMAIN:1883 by default + # MQTT transport base url, tcp://DOMAIN:1883 by default - base_url: '${MQTT_TRANSPORT_BASE_URL:tcp://${monitoring.domain}:1883}' + # Whether to monitor IPs associated with the domain from base url + check_domain_ips: '${MQTT_TRANSPORT_CHECK_DOMAIN_IPS:false}' # To add more targets, use following environment variables: # monitoring.transports.mqtt.targets[1].base_url, monitoring.transports.mqtt.targets[2].base_url, etc. @@ -62,8 +64,10 @@ monitoring: # CoAP request timeout in milliseconds request_timeout_ms: '${COAP_REQUEST_TIMEOUT_MS:4000}' targets: - # CoAP transport base url, coap://DOMAIN by default + # CoAP transport base url, coap://DOMAIN by default - base_url: '${COAP_TRANSPORT_BASE_URL:coap://${monitoring.domain}}' + # Whether to monitor IPs associated with the domain from base url + check_domain_ips: '${COAP_TRANSPORT_CHECK_DOMAIN_IPS:false}' # To add more targets, use following environment variables: # monitoring.transports.coap.targets[1].base_url, monitoring.transports.coap.targets[2].base_url, etc. @@ -73,8 +77,10 @@ monitoring: # HTTP request timeout in milliseconds request_timeout_ms: '${HTTP_REQUEST_TIMEOUT_MS:4000}' targets: - # HTTP transport base url, http://DOMAIN by default + # HTTP transport base url, http://DOMAIN by default - base_url: '${HTTP_TRANSPORT_BASE_URL:http://${monitoring.domain}}' + # Whether to monitor IPs associated with the domain from base url + check_domain_ips: '${HTTP_TRANSPORT_CHECK_DOMAIN_IPS:false}' # To add more targets, use following environment variables: # monitoring.transports.http.targets[1].base_url, monitoring.transports.http.targets[2].base_url, etc. @@ -84,8 +90,10 @@ monitoring: # LwM2M request timeout in milliseconds request_timeout_ms: '${LWM2M_REQUEST_TIMEOUT_MS:4000}' targets: - # LwM2M transport base url, coap://DOMAIN:5685 by default + # LwM2M transport base url, coap://DOMAIN:5685 by default - base_url: '${LWM2M_TRANSPORT_BASE_URL:coap://${monitoring.domain}:5685}' + # Whether to monitor IPs associated with the domain from base url + check_domain_ips: '${LWM2M_TRANSPORT_CHECK_DOMAIN_IPS:false}' # To add more targets, use following environment variables: # monitoring.transports.lwm2m.targets[1].base_url, monitoring.transports.lwm2m.targets[2].base_url, etc. diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index 6413973d7c..f710ea0414 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java index e41bbc3449..7d87351bef 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java @@ -25,6 +25,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.StringJoiner; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -74,8 +75,8 @@ public class ThingsBoardDbInstaller { new File("./../../docker/docker-compose.yml"), new File("./../../docker/docker-compose.volumes.yml"), IS_HYBRID_MODE - ? new File("./../../docker/docker-compose.hybrid.yml") - : new File("./../../docker/docker-compose.postgres.yml"), + ? new File("./../../docker/docker-compose.hybrid.yml") + : new File("./../../docker/docker-compose.postgres.yml"), new File("./../../docker/docker-compose.postgres.volumes.yml"), resolveRedisComposeFile(), resolveRedisComposeVolumesFile() @@ -156,7 +157,7 @@ public class ThingsBoardDbInstaller { return env; } - public void createVolumes() { + public void createVolumes() { try { dockerCompose.withCommand("volume create " + postgresDataVolume); @@ -200,7 +201,7 @@ public class ThingsBoardDbInstaller { } } else if (IS_REDIS_SENTINEL) { additionalServices.append(" redis-master"); - dockerCompose.withCommand("volume create " + redisSentinelDataVolume +"-" + "master"); + dockerCompose.withCommand("volume create " + redisSentinelDataVolume + "-" + "master"); dockerCompose.invokeDocker(); additionalServices.append(" redis-slave"); @@ -226,7 +227,8 @@ public class ThingsBoardDbInstaller { try { dockerCompose.withCommand("down -v"); dockerCompose.invokeCompose(); - } catch (Exception ignored) {} + } catch (Exception ignored) { + } } } @@ -239,10 +241,23 @@ public class ThingsBoardDbInstaller { copyLogs(tbSnmpTransportLogVolume, "./target/tb-snmp-transport-logs/"); copyLogs(tbVcExecutorLogVolume, "./target/tb-vc-executor-logs/"); - dockerCompose.withCommand("volume rm -f " + postgresDataVolume + " " + tbLogVolume + - " " + tbCoapTransportLogVolume + " " + tbLwm2mTransportLogVolume + " " + tbHttpTransportLogVolume + - " " + tbMqttTransportLogVolume + " " + tbSnmpTransportLogVolume + " " + tbVcExecutorLogVolume + resolveRedisComposeVolumeLog()); - dockerCompose.invokeDocker(); + StringJoiner rmVolumesCommand = new StringJoiner(" ") + .add("volume rm -f") + .add(postgresDataVolume) + .add(tbLogVolume) + .add(tbCoapTransportLogVolume) + .add(tbLwm2mTransportLogVolume) + .add(tbHttpTransportLogVolume) + .add(tbMqttTransportLogVolume) + .add(tbSnmpTransportLogVolume) + .add(tbVcExecutorLogVolume) + .add(resolveRedisComposeVolumeLog()); + + if (IS_HYBRID_MODE) { + rmVolumesCommand.add(cassandraDataVolume); + } + + dockerCompose.withCommand(rmVolumesCommand.toString()); } private String resolveRedisComposeVolumeLog() { @@ -266,7 +281,7 @@ public class ThingsBoardDbInstaller { dockerCompose.withCommand("run -d --rm --name " + logsContainerName + " -v " + volumeName + ":/root alpine tail -f /dev/null"); dockerCompose.invokeDocker(); - dockerCompose.withCommand("cp " + logsContainerName + ":/root/. "+tbLogsDir.getAbsolutePath()); + dockerCompose.withCommand("cp " + logsContainerName + ":/root/. " + tbLogsDir.getAbsolutePath()); dockerCompose.invokeDocker(); dockerCompose.withCommand("rm -f " + logsContainerName); diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/WsClient.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/WsClient.java index 8ed53e2607..229153e2eb 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/WsClient.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/WsClient.java @@ -33,8 +33,8 @@ public class WsClient extends WebSocketClient { private WsTelemetryResponse message; private volatile boolean firstReplyReceived; - private CountDownLatch firstReply = new CountDownLatch(1); - private CountDownLatch latch = new CountDownLatch(1); + private final CountDownLatch firstReply = new CountDownLatch(1); + private final CountDownLatch latch = new CountDownLatch(1); private final long timeoutMultiplier; @@ -48,7 +48,8 @@ public class WsClient extends WebSocketClient { } @Override - public void onMessage(String message) { + public synchronized void onMessage(String message) { + log.error("WS onMessage: {}", message); if (!firstReplyReceived) { firstReplyReceived = true; firstReply.countDown(); @@ -66,12 +67,13 @@ public class WsClient extends WebSocketClient { } @Override - public void onClose(int code, String reason, boolean remote) { - log.info("ws is closed, due to [{}]", reason); + public synchronized void onClose(int code, String reason, boolean remote) { + log.error("WS onClose: [{}]", reason); } @Override - public void onError(Exception ex) { + public synchronized void onError(Exception ex) { + log.error("WS onError: ", ex); ex.printStackTrace(); } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttClientTest.java index 893c8b565c..6ad50196b3 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttClientTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttClientTest.java @@ -16,6 +16,7 @@ package org.thingsboard.server.msa.connectivity; import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; @@ -25,9 +26,11 @@ import io.netty.buffer.Unpooled; import io.netty.handler.codec.mqtt.MqttQoS; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import org.awaitility.Awaitility; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import org.thingsboard.common.util.AbstractListeningExecutor; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.mqtt.MqttClient; import org.thingsboard.mqtt.MqttClientConfig; @@ -74,8 +77,18 @@ import static org.thingsboard.server.msa.prototypes.DevicePrototypes.defaultDevi public class MqttClientTest extends AbstractContainerTest { private Device device; + AbstractListeningExecutor handlerExecutor; + @BeforeMethod public void setUp() throws Exception { + this.handlerExecutor = new AbstractListeningExecutor() { + @Override + protected int getThreadPollSize() { + return 4; + } + }; + handlerExecutor.init(); + testRestClient.login("tenant@thingsboard.org", "tenant"); device = testRestClient.postDevice("", defaultDevicePrototype("http_")); } @@ -83,6 +96,9 @@ public class MqttClientTest extends AbstractContainerTest { @AfterMethod public void tearDown() { testRestClient.deleteDeviceIfExists(device.getId()); + if (handlerExecutor != null) { + handlerExecutor.destroy(); + } } @Test public void telemetryUpload() throws Exception { @@ -322,8 +338,12 @@ public class MqttClientTest extends AbstractContainerTest { MqttClient mqttClient = getMqttClient(deviceCredentials, listener); testRestClient.deleteDeviceIfExists(device.getId()); - TimeUnit.SECONDS.sleep(3 * timeoutMultiplier); - assertThat(mqttClient.isConnected()).isFalse(); + + Awaitility + .await() + .alias("Check device connection.") + .atMost(10, TimeUnit.SECONDS) + .until(() -> !mqttClient.isConnected()); } @Test @@ -461,11 +481,16 @@ public class MqttClientTest extends AbstractContainerTest { return getMqttClient(deviceCredentials.getCredentialsId(), listener); } + private String getOwnerId() { + return "Tenant[" + device.getTenantId().getId() + "]MqttClientTestDevice[" + device.getId().getId() + "]"; + } + private MqttClient getMqttClient(String username, MqttMessageListener listener) throws InterruptedException, ExecutionException { MqttClientConfig clientConfig = new MqttClientConfig(); + clientConfig.setOwnerId(getOwnerId()); clientConfig.setClientId("MQTT client from test"); clientConfig.setUsername(username); - MqttClient mqttClient = MqttClient.create(clientConfig, listener); + MqttClient mqttClient = MqttClient.create(clientConfig, listener, handlerExecutor); mqttClient.connect("localhost", 1883).get(); return mqttClient; } @@ -479,9 +504,10 @@ public class MqttClientTest extends AbstractContainerTest { } @Override - public void onMessage(String topic, ByteBuf message) { + public ListenableFuture onMessage(String topic, ByteBuf message) { log.info("MQTT message [{}], topic [{}]", message.toString(StandardCharsets.UTF_8), topic); events.add(new MqttEvent(topic, message.toString(StandardCharsets.UTF_8))); + return Futures.immediateVoidFuture(); } } diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java index a038d4cf50..5e2a9f5fe6 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java +++ b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java @@ -16,6 +16,7 @@ package org.thingsboard.server.msa.connectivity; import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; @@ -32,6 +33,7 @@ import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import org.thingsboard.common.util.AbstractListeningExecutor; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.mqtt.MqttClient; @@ -76,8 +78,18 @@ public class MqttGatewayClientTest extends AbstractContainerTest { private MqttMessageListener listener; private JsonParser jsonParser = new JsonParser(); + AbstractListeningExecutor handlerExecutor; + @BeforeMethod public void createGateway() throws Exception { + this.handlerExecutor = new AbstractListeningExecutor() { + @Override + protected int getThreadPollSize() { + return 4; + } + }; + handlerExecutor.init(); + testRestClient.login("tenant@thingsboard.org", "tenant"); gatewayDevice = testRestClient.postDevice("", defaultGatewayPrototype()); DeviceCredentials gatewayDeviceCredentials = testRestClient.getDeviceCredentialsByDeviceId(gatewayDevice.getId()); @@ -94,6 +106,9 @@ public class MqttGatewayClientTest extends AbstractContainerTest { this.listener = null; this.mqttClient = null; this.createdDevice = null; + if (handlerExecutor != null) { + handlerExecutor.destroy(); + } } @Test @@ -403,11 +418,16 @@ public class MqttGatewayClientTest extends AbstractContainerTest { return testRestClient.getDeviceById(createdDeviceId); } + private String getOwnerId() { + return "Tenant[" + gatewayDevice.getTenantId().getId() + "]MqttGatewayClientTestDevice[" + gatewayDevice.getId().getId() + "]"; + } + private MqttClient getMqttClient(DeviceCredentials deviceCredentials, MqttMessageListener listener) throws InterruptedException, ExecutionException { MqttClientConfig clientConfig = new MqttClientConfig(); + clientConfig.setOwnerId(getOwnerId()); clientConfig.setClientId("MQTT client from test"); clientConfig.setUsername(deviceCredentials.getCredentialsId()); - MqttClient mqttClient = MqttClient.create(clientConfig, listener); + MqttClient mqttClient = MqttClient.create(clientConfig, listener, handlerExecutor); mqttClient.connect("localhost", 1883).get(); return mqttClient; } @@ -421,9 +441,10 @@ public class MqttGatewayClientTest extends AbstractContainerTest { } @Override - public void onMessage(String topic, ByteBuf message) { + public ListenableFuture onMessage(String topic, ByteBuf message) { log.info("MQTT message [{}], topic [{}]", message.toString(StandardCharsets.UTF_8), topic); events.add(new MqttEvent(topic, message.toString(StandardCharsets.UTF_8))); + return Futures.immediateVoidFuture(); } } diff --git a/msa/black-box-tests/src/test/resources/logback.xml b/msa/black-box-tests/src/test/resources/logback.xml index 0df6c199f4..9d1c96116c 100644 --- a/msa/black-box-tests/src/test/resources/logback.xml +++ b/msa/black-box-tests/src/test/resources/logback.xml @@ -25,6 +25,7 @@ + diff --git a/msa/black-box-tests/src/test/resources/tb-node/conf/logback.xml b/msa/black-box-tests/src/test/resources/tb-node/conf/logback.xml new file mode 100644 index 0000000000..e998063b5b --- /dev/null +++ b/msa/black-box-tests/src/test/resources/tb-node/conf/logback.xml @@ -0,0 +1,58 @@ + + + + + + + /var/log/thingsboard/${TB_SERVICE_ID}/thingsboard.log + + /var/log/thingsboard/${TB_SERVICE_ID}/thingsboard.%d{yyyy-MM-dd}.%i.log + 100MB + 30 + 3GB + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + + + + + + diff --git a/msa/black-box-tests/src/test/resources/tb-transports/coap/conf/logback.xml b/msa/black-box-tests/src/test/resources/tb-transports/coap/conf/logback.xml new file mode 100644 index 0000000000..397693c501 --- /dev/null +++ b/msa/black-box-tests/src/test/resources/tb-transports/coap/conf/logback.xml @@ -0,0 +1,57 @@ + + + + + + + /var/log/tb-coap-transport/${TB_SERVICE_ID}/tb-coap-transport.log + + /var/log/tb-coap-transport/${TB_SERVICE_ID}/tb-coap-transport.%d{yyyy-MM-dd}.%i.log + 100MB + 30 + 3GB + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/msa/black-box-tests/src/test/resources/tb-transports/http/conf/logback.xml b/msa/black-box-tests/src/test/resources/tb-transports/http/conf/logback.xml new file mode 100644 index 0000000000..6f9f63383e --- /dev/null +++ b/msa/black-box-tests/src/test/resources/tb-transports/http/conf/logback.xml @@ -0,0 +1,57 @@ + + + + + + + /var/log/tb-http-transport/${TB_SERVICE_ID}/tb-http-transport.log + + /var/log/tb-http-transport/${TB_SERVICE_ID}/tb-http-transport.%d{yyyy-MM-dd}.%i.log + 100MB + 30 + 3GB + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/msa/black-box-tests/src/test/resources/tb-transports/mqtt/conf/logback.xml b/msa/black-box-tests/src/test/resources/tb-transports/mqtt/conf/logback.xml new file mode 100644 index 0000000000..8d0e650a70 --- /dev/null +++ b/msa/black-box-tests/src/test/resources/tb-transports/mqtt/conf/logback.xml @@ -0,0 +1,55 @@ + + + + + + + /var/log/tb-mqtt-transport/${TB_SERVICE_ID}/tb-mqtt-transport.log + + /var/log/tb-mqtt-transport/${TB_SERVICE_ID}/tb-mqtt-transport.%d{yyyy-MM-dd}.%i.log + 100MB + 30 + 3GB + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/msa/js-executor/config/custom-environment-variables.yml b/msa/js-executor/config/custom-environment-variables.yml index 5ca66d71a1..c92ae78553 100644 --- a/msa/js-executor/config/custom-environment-variables.yml +++ b/msa/js-executor/config/custom-environment-variables.yml @@ -15,6 +15,7 @@ # queue_type: "TB_QUEUE_TYPE" #kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) +queue_prefix: "TB_QUEUE_PREFIX" request_topic: "REMOTE_JS_EVAL_REQUEST_TOPIC" http_port: "HTTP_PORT" # /livenessProbe diff --git a/msa/js-executor/config/default.yml b/msa/js-executor/config/default.yml index 43712f0f61..e6927c0bff 100644 --- a/msa/js-executor/config/default.yml +++ b/msa/js-executor/config/default.yml @@ -16,6 +16,7 @@ queue_type: "kafka" request_topic: "js_eval.requests" +queue_prefix: "" http_port: "8888" # /livenessProbe js: diff --git a/msa/js-executor/docker/Dockerfile b/msa/js-executor/docker/Dockerfile index f934628a96..ef22343442 100644 --- a/msa/js-executor/docker/Dockerfile +++ b/msa/js-executor/docker/Dockerfile @@ -14,7 +14,7 @@ # limitations under the License. # -FROM thingsboard/node:16.17.0-bullseye-slim +FROM thingsboard/node:16.20.2-bookworm-slim ENV NODE_ENV production ENV DOCKER_MODE true diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json index 7736db173f..a38ee36fba 100644 --- a/msa/js-executor/package.json +++ b/msa/js-executor/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-js-executor", "private": true, - "version": "3.5.2", + "version": "3.6.2", "description": "ThingsBoard JavaScript Executor Microservice", "main": "server.ts", "bin": "server.js", diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index fe92a77763..b3d76ad0ec 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT msa org.thingsboard.msa @@ -71,7 +71,7 @@ install-node-and-yarn - v16.15.1 + v16.20.2 v1.22.17 diff --git a/msa/js-executor/queue/awsSqsTemplate.ts b/msa/js-executor/queue/awsSqsTemplate.ts index c9951008c7..1486df802b 100644 --- a/msa/js-executor/queue/awsSqsTemplate.ts +++ b/msa/js-executor/queue/awsSqsTemplate.ts @@ -38,7 +38,8 @@ import uuid from 'uuid-random'; export class AwsSqsTemplate implements IQueue { private logger = _logger(`awsSqsTemplate`); - private requestTopic: string = config.get('request_topic'); + private queuePrefix: string = config.get('queue_prefix'); + private requestTopic: string = this.queuePrefix ? this.queuePrefix + "." + config.get('request_topic') : config.get('request_topic'); private accessKeyId: string = config.get('aws_sqs.access_key_id'); private secretAccessKey: string = config.get('aws_sqs.secret_access_key'); private region: string = config.get('aws_sqs.region'); diff --git a/msa/js-executor/queue/kafkaTemplate.ts b/msa/js-executor/queue/kafkaTemplate.ts index 5d1b6cc6c1..9759c095c9 100644 --- a/msa/js-executor/queue/kafkaTemplate.ts +++ b/msa/js-executor/queue/kafkaTemplate.ts @@ -61,7 +61,8 @@ export class KafkaTemplate implements IQueue { async init(): Promise { const kafkaBootstrapServers: string = config.get('kafka.bootstrap.servers'); - const requestTopic: string = config.get('request_topic'); + const queuePrefix: string = config.get('queue_prefix'); + const requestTopic: string = queuePrefix ? queuePrefix + "." + config.get('request_topic') : config.get('request_topic'); const useConfluent = config.get('kafka.use_confluent_cloud'); this.logger.info('Kafka Bootstrap Servers: %s', kafkaBootstrapServers); diff --git a/msa/js-executor/queue/pubSubTemplate.ts b/msa/js-executor/queue/pubSubTemplate.ts index 71c9b72d5f..a1b05ab7bf 100644 --- a/msa/js-executor/queue/pubSubTemplate.ts +++ b/msa/js-executor/queue/pubSubTemplate.ts @@ -26,7 +26,8 @@ export class PubSubTemplate implements IQueue { private logger = _logger(`pubSubTemplate`); private projectId: string = config.get('pubsub.project_id'); private credentials = JSON.parse(config.get('pubsub.service_account')); - private requestTopic: string = config.get('request_topic'); + private queuePrefix: string = config.get('queue_prefix'); + private requestTopic: string = this.queuePrefix ? this.queuePrefix + "." + config.get('request_topic') : config.get('request_topic'); private queueProperties: string = config.get('pubsub.queue_properties'); private pubSubClient: PubSub; diff --git a/msa/js-executor/queue/rabbitmqTemplate.ts b/msa/js-executor/queue/rabbitmqTemplate.ts index fca4b1e5bd..7bb078c047 100644 --- a/msa/js-executor/queue/rabbitmqTemplate.ts +++ b/msa/js-executor/queue/rabbitmqTemplate.ts @@ -24,7 +24,8 @@ import { Options, Replies } from 'amqplib/properties'; export class RabbitMqTemplate implements IQueue { private logger = _logger(`rabbitmqTemplate`); - private requestTopic: string = config.get('request_topic'); + private queuePrefix: string = config.get('queue_prefix'); + private requestTopic: string = this.queuePrefix ? this.queuePrefix + "." + config.get('request_topic') : config.get('request_topic'); private host = config.get('rabbitmq.host'); private port = config.get('rabbitmq.port'); private vhost = config.get('rabbitmq.virtual_host'); diff --git a/msa/js-executor/queue/serviceBusTemplate.ts b/msa/js-executor/queue/serviceBusTemplate.ts index 16950b7bce..70f0d68d3b 100644 --- a/msa/js-executor/queue/serviceBusTemplate.ts +++ b/msa/js-executor/queue/serviceBusTemplate.ts @@ -31,7 +31,8 @@ import { export class ServiceBusTemplate implements IQueue { private logger = _logger(`serviceBusTemplate`); - private requestTopic: string = config.get('request_topic'); + private queuePrefix: string = config.get('queue_prefix'); + private requestTopic: string = this.queuePrefix ? this.queuePrefix + "." + config.get('request_topic') : config.get('request_topic'); private namespaceName = config.get('service_bus.namespace_name'); private sasKeyName = config.get('service_bus.sas_key_name'); private sasKey = config.get('service_bus.sas_key'); diff --git a/msa/monitoring/pom.xml b/msa/monitoring/pom.xml index bb0539ac98..2721a84382 100644 --- a/msa/monitoring/pom.xml +++ b/msa/monitoring/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT msa diff --git a/msa/pom.xml b/msa/pom.xml index 451466f98a..2371540dd7 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard msa diff --git a/msa/tb-node/docker/Dockerfile b/msa/tb-node/docker/Dockerfile index 68e428992a..86fd04cad2 100644 --- a/msa/tb-node/docker/Dockerfile +++ b/msa/tb-node/docker/Dockerfile @@ -18,8 +18,7 @@ FROM thingsboard/openjdk11:bullseye-slim COPY start-tb-node.sh ${pkg.name}.deb /tmp/ -RUN echo 'networkaddress.cache.ttl=60' >> /etc/java-11-openjdk/security/java.security \ - && chmod a+x /tmp/*.sh \ +RUN chmod a+x /tmp/*.sh \ && mv /tmp/start-tb-node.sh /usr/bin && \ (yes | dpkg -i /tmp/${pkg.name}.deb) && \ rm /tmp/${pkg.name}.deb && \ diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index 3d39ba285b..4e6a268cac 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/tb/docker-cassandra/Dockerfile b/msa/tb/docker-cassandra/Dockerfile index 2f23aec399..0a0c36ed88 100644 --- a/msa/tb/docker-cassandra/Dockerfile +++ b/msa/tb/docker-cassandra/Dockerfile @@ -16,7 +16,7 @@ FROM thingsboard/openjdk11:bullseye-slim -ENV PG_MAJOR=12 +ENV PG_MAJOR=15 ENV DATA_FOLDER=/data diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index 94382be495..de9e55a12b 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT msa org.thingsboard.msa @@ -38,7 +38,7 @@ tb-postgres tb-cassandra /usr/share/${pkg.name} - 3.5.2 + 3.6.2 diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index 7c55ef4322..06f22351e9 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index 2d02b2f124..edd2a3f570 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/lwm2m/pom.xml b/msa/transport/lwm2m/pom.xml index 7a521a6718..a089dbf97b 100644 --- a/msa/transport/lwm2m/pom.xml +++ b/msa/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index 4ab24589dd..ea52fb5c8d 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index f12564c24f..d693264164 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml index c5b5617f69..ebfc0db272 100644 --- a/msa/transport/snmp/pom.xml +++ b/msa/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.msa transport - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT org.thingsboard.msa.transport diff --git a/msa/vc-executor-docker/docker/Dockerfile b/msa/vc-executor-docker/docker/Dockerfile index 653a5f2d35..81febe89f7 100644 --- a/msa/vc-executor-docker/docker/Dockerfile +++ b/msa/vc-executor-docker/docker/Dockerfile @@ -16,6 +16,11 @@ FROM thingsboard/openjdk11:bullseye-slim +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update \ + && apt-get install -yq --no-install-recommends ncdu \ + && rm -rf /var/lib/apt/lists/* + COPY start-tb-vc-executor.sh ${pkg.name}.deb /tmp/ RUN mkdir -p /home/thingsboard/.config/jgit \ diff --git a/msa/vc-executor-docker/pom.xml b/msa/vc-executor-docker/pom.xml index 162ae1f801..4d09d0fa92 100644 --- a/msa/vc-executor-docker/pom.xml +++ b/msa/vc-executor-docker/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/vc-executor/pom.xml b/msa/vc-executor/pom.xml index c78b3fd129..0ca1553eed 100644 --- a/msa/vc-executor/pom.xml +++ b/msa/vc-executor/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/vc-executor/src/main/java/org/thingsboard/server/vc/service/VersionControlTenantRoutingInfoService.java b/msa/vc-executor/src/main/java/org/thingsboard/server/vc/service/VersionControlTenantRoutingInfoService.java index fb33ff9931..b343a4791f 100644 --- a/msa/vc-executor/src/main/java/org/thingsboard/server/vc/service/VersionControlTenantRoutingInfoService.java +++ b/msa/vc-executor/src/main/java/org/thingsboard/server/vc/service/VersionControlTenantRoutingInfoService.java @@ -25,6 +25,6 @@ public class VersionControlTenantRoutingInfoService implements TenantRoutingInfo @Override public TenantRoutingInfo getRoutingInfo(TenantId tenantId) { //This dummy implementation is ok since Version Control service does not produce any rule engine messages. - return new TenantRoutingInfo(tenantId, false); + return new TenantRoutingInfo(tenantId, null, false); } } diff --git a/msa/vc-executor/src/main/resources/tb-vc-executor.yml b/msa/vc-executor/src/main/resources/tb-vc-executor.yml index 9d5ad35388..962c5303c7 100644 --- a/msa/vc-executor/src/main/resources/tb-vc-executor.yml +++ b/msa/vc-executor/src/main/resources/tb-vc-executor.yml @@ -14,13 +14,12 @@ # limitations under the License. # -# If you enabled process metrics you should also enable 'web-environment'. -spring.main.web-environment: "${WEB_APPLICATION_ENABLE:false}" -# If you enabled process metrics you should set 'web-application-type' to 'servlet' value. -spring.main.web-application-type: "${WEB_APPLICATION_TYPE:none}" - -spring.main.allow-circular-references: "true" +# Spring common parameters +spring.main.web-environment: "${WEB_APPLICATION_ENABLE:false}" # If you enabled process metrics you should also enable 'web-environment'. +spring.main.web-application-type: "${WEB_APPLICATION_TYPE:none}" # If you enabled process metrics you should set 'web-application-type' to 'servlet' value. +spring.main.allow-circular-references: "true" # Spring Boot configuration property that controls whether circular dependencies between beans are allowed. +# Server common parameters server: # Server bind address (has no effect if web-environment is disabled). address: "${HTTP_BIND_ADDRESS:0.0.0.0}" @@ -41,51 +40,86 @@ zk: session_timeout_ms: "${ZOOKEEPER_SESSION_TIMEOUT_MS:3000}" # Name of the directory in zookeeper 'filesystem' zk_dir: "${ZOOKEEPER_NODES_DIR:/thingsboard}" + # The recalculate_delay property is recommended in a microservices architecture setup for rule-engine services. + # This property provides a pause to ensure that when a rule-engine service is restarted, other nodes don't immediately attempt to recalculate their partitions. + # The delay is recommended because the initialization of rule chain actors is time-consuming. Avoiding unnecessary recalculations during a restart can enhance system performance and stability. + recalculate_delay: "${ZOOKEEPER_RECALCULATE_DELAY_MS:0}" +# Queue configuration parameters queue: type: "${TB_QUEUE_TYPE:kafka}" # in-memory or kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) + prefix: "${TB_QUEUE_PREFIX:}" # Global queue prefix. If specified, prefix is added before default topic name: 'prefix.default_topic_name'. Prefix is applied to all topics (and consumer groups for kafka). in_memory: stats: # For debug lvl print-interval-ms: "${TB_QUEUE_IN_MEMORY_STATS_PRINT_INTERVAL_MS:60000}" kafka: + # Kafka Bootstrap Servers bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}" ssl: + # Enable/Disable SSL Kafka communication enabled: "${TB_KAFKA_SSL_ENABLED:false}" + # The location of the trust store file truststore.location: "${TB_KAFKA_SSL_TRUSTSTORE_LOCATION:}" + # The password of trust store file if specified truststore.password: "${TB_KAFKA_SSL_TRUSTSTORE_PASSWORD:}" + # The location of the key store file. This is optional for the client and can be used for two-way authentication for the client keystore.location: "${TB_KAFKA_SSL_KEYSTORE_LOCATION:}" + # The store password for the key store file. This is optional for the client and only needed if ‘ssl.keystore.location’ is configured. Key store password is not supported for PEM format keystore.password: "${TB_KAFKA_SSL_KEYSTORE_PASSWORD:}" + # The password of the private key in the key store file or the PEM key specified in ‘keystore.key’ key.password: "${TB_KAFKA_SSL_KEY_PASSWORD:}" + # The number of acknowledgments the producer requires the leader to have received before considering a request complete. This controls the durability of records that are sent. The following settings are allowed:0,1 and all acks: "${TB_KAFKA_ACKS:all}" + # Number of retries. Resend any record whose send fails with a potentially transient error retries: "${TB_KAFKA_RETRIES:1}" compression.type: "${TB_KAFKA_COMPRESSION_TYPE:none}" # none or gzip + # Default batch size. This setting gives the upper bound of the batch size to be sent batch.size: "${TB_KAFKA_BATCH_SIZE:16384}" + # This variable creates a small amount of artificial delay—that is, rather than immediately sending out a record linger.ms: "${TB_KAFKA_LINGER_MS:1}" + # The maximum size of a request in bytes. This setting will limit the number of record batches the producer will send in a single request to avoid sending huge requests max.request.size: "${TB_KAFKA_MAX_REQUEST_SIZE:1048576}" + # The maximum number of unacknowledged requests the client will send on a single connection before blocking max.in.flight.requests.per.connection: "${TB_KAFKA_MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION:5}" + # The total bytes of memory the producer can use to buffer records waiting to be sent to the server buffer.memory: "${TB_BUFFER_MEMORY:33554432}" + # The multiple copies of data over the multiple brokers of Kafka replication_factor: "${TB_QUEUE_KAFKA_REPLICATION_FACTOR:1}" + # The maximum delay between invocations of poll() when using consumer group management. This places an upper bound on the amount of time that the consumer can be idle before fetching more records max_poll_interval_ms: "${TB_QUEUE_KAFKA_MAX_POLL_INTERVAL_MS:300000}" + # The maximum number of records returned in a single call to poll() max_poll_records: "${TB_QUEUE_KAFKA_MAX_POLL_RECORDS:8192}" + # The maximum amount of data per-partition the server will return. Records are fetched in batches by the consumer max_partition_fetch_bytes: "${TB_QUEUE_KAFKA_MAX_PARTITION_FETCH_BYTES:16777216}" + # The maximum amount of data the server will return. Records are fetched in batches by the consumer fetch_max_bytes: "${TB_QUEUE_KAFKA_FETCH_MAX_BYTES:134217728}" request.timeout.ms: "${TB_QUEUE_KAFKA_REQUEST_TIMEOUT_MS:30000}" # (30 seconds) # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms session.timeout.ms: "${TB_QUEUE_KAFKA_SESSION_TIMEOUT_MS:10000}" # (10 seconds) # refer to https://docs.confluent.io/platform/current/installation/configuration/consumer-configs.html#consumerconfigs_session.timeout.ms + auto_offset_reset: "${TB_QUEUE_KAFKA_AUTO_OFFSET_RESET:earliest}" # earliest, latest or none + # Enable/Disable using of Confluent Cloud use_confluent_cloud: "${TB_QUEUE_KAFKA_USE_CONFLUENT_CLOUD:false}" confluent: + # The endpoint identification algorithm used by clients to validate server hostname. The default value is https ssl.algorithm: "${TB_QUEUE_KAFKA_CONFLUENT_SSL_ALGORITHM:https}" + # The mechanism used to authenticate Schema Registry requests. SASL/PLAIN should only be used with TLS/SSL as a transport layer to ensure that clear passwords are not transmitted on the wire without encryption sasl.mechanism: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_MECHANISM:PLAIN}" + # Using JAAS Configuration for specifying multiple SASL mechanisms on a broker sasl.config: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_JAAS_CONFIG:org.apache.kafka.common.security.plain.PlainLoginModule required username=\"CLUSTER_API_KEY\" password=\"CLUSTER_API_SECRET\";}" + # Protocol used to communicate with brokers. Valid values are: PLAINTEXT, SSL, SASL_PLAINTEXT, SASL_SSL security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}" # Key-value properties for Kafka consumer per specific topic, e.g. tb_ota_package is a topic name for ota, tb_rule_engine.sq is a topic name for default SequentialByOriginator queue. # Check TB_QUEUE_CORE_OTA_TOPIC and TB_QUEUE_RE_SQ_TOPIC params consumer-properties-per-topic: tb_ota_package: + # Key-value properties for Kafka consumer per specific topic, e.g. tb_ota_package is a topic name for ota, tb_rule_engine.sq is a topic name for default SequentialByOriginator queue. Check TB_QUEUE_CORE_OTA_TOPIC and TB_QUEUE_RE_SQ_TOPIC params - key: max.poll.records + # Example of specific consumer properties value per topic value: "${TB_QUEUE_KAFKA_OTA_MAX_POLL_RECORDS:10}" tb_version_control: + # Example of specific consumer properties value per topic for VC - key: max.poll.interval.ms + # Example of specific consumer properties value per topic for VC value: "${TB_QUEUE_KAFKA_VC_MAX_POLL_INTERVAL_MS:600000}" # tb_rule_engine.sq: # - key: max.poll.records @@ -97,94 +131,157 @@ queue: # - key: "session.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/consumer-configs.html#consumerconfigs_session.timeout.ms # value: "${TB_QUEUE_KAFKA_SESSION_TIMEOUT_MS:10000}" # (10 seconds) topic-properties: + # Kafka properties for Core topics core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" + # Kafka properties for Notifications topics notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" - version-control: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" + # Kafka properties for Core topics + version-control: "${TB_QUEUE_KAFKA_VC_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" consumer-stats: + # Prints lag between consumer group offset and last messages offset in Kafka topics enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}" + # Statistics printing interval for Kafka's consumer-groups stats print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}" + # Time to wait for the stats-loading requests to Kafka to finis kafka-response-timeout-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_RESPONSE_TIMEOUT_MS:1000}" aws_sqs: + # Use the default credentials provider for AWS SQS use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" + # Access key ID from AWS IAM user access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" + # Secret access key from AWS IAM user secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}" + # Region from AWS account region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}" + # Number of threads per each AWS SQS queue in consumer threads_per_topic: "${TB_QUEUE_AWS_SQS_THREADS_PER_TOPIC:1}" queue-properties: + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds version-control: "${TB_QUEUE_AWS_SQS_VC_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds pubsub: + # Project ID from Google Cloud project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" + # API Credentials in JSON format service_account: "${TB_QUEUE_PUBSUB_SERVICE_ACCOUNT:YOUR_SERVICE_ACCOUNT}" + # Message size for PubSub queue.Value in bytes max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" #in bytes + # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" + # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" queue-properties: + # Pub/Sub properties for Core subscribers, messages which will commit after ackDeadlineInSec period can be consumed again core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # Pub/Sub properties for Version Control subscribers, messages which will commit after ackDeadlineInSec period can be consumed again notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # Pub/Sub properties for Transport Api subscribers, messages which will commit after ackDeadlineInSec period can be consumed again version-control: "${TB_QUEUE_PUBSUB_VC_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" service_bus: + # Azure namespace namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" + # Azure Service Bus Shared Access Signatures key name sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" + # Azure Service Bus Shared Access Signatures key sas_key: "${TB_QUEUE_SERVICE_BUS_SAS_KEY:YOUR_SAS_KEY}" + # Number of messages per a consumer max_messages: "${TB_QUEUE_SERVICE_BUS_MAX_MESSAGES:1000}" queue-properties: + # Azure Service Bus properties for Core queues core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus properties for Notification queues notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus properties for Version Control queues version-control: "${TB_QUEUE_SERVICE_BUS_VC_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" rabbitmq: + # By default empty exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" + # RabbitMQ host used to establish connection host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" + # RabbitMQ host used to establish a connection port: "${TB_QUEUE_RABBIT_MQ_PORT:5672}" + # Virtual hosts provide logical grouping and separation of resources virtual_host: "${TB_QUEUE_RABBIT_MQ_VIRTUAL_HOST:/}" + # Username for RabbitMQ user account username: "${TB_QUEUE_RABBIT_MQ_USERNAME:YOUR_USERNAME}" + # User password for RabbitMQ user account password: "${TB_QUEUE_RABBIT_MQ_PASSWORD:YOUR_PASSWORD}" + # Network connection between clients and RabbitMQ nodes can fail. RabbitMQ Java client supports automatic recovery of connections and topology (queues, exchanges, bindings, and consumers) automatic_recovery_enabled: "${TB_QUEUE_RABBIT_MQ_AUTOMATIC_RECOVERY_ENABLED:false}" + # The connection timeout for the RabbitMQ connection factory connection_timeout: "${TB_QUEUE_RABBIT_MQ_CONNECTION_TIMEOUT:60000}" + # RabbitMQ has a timeout for connection handshake. When clients run in heavily constrained environments, it may be necessary to increase the timeout handshake_timeout: "${TB_QUEUE_RABBIT_MQ_HANDSHAKE_TIMEOUT:10000}" queue-properties: + # RabbitMQ properties for Core queues core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ properties for Transport API queues transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ properties for Version Control queues version-control: "${TB_QUEUE_RABBIT_MQ_VC_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" partitions: hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 core: + # Default topic name of Kafka, RabbitMQ, etc. queue topic: "${TB_QUEUE_CORE_TOPIC:tb_core}" + # Interval in milliseconds to poll messages by Core microservices poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" + # Amount of partitions used by Core microservices partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" + # Timeout for processing a message pack by Core microservices pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:2000}" ota: + # Default topic name for OTA updates topic: "${TB_QUEUE_CORE_OTA_TOPIC:tb_ota_package}" + # The interval of processing the OTA updates for devices. Used to avoid any harm to the network due to many parallel OTA updates pack-interval-ms: "${TB_QUEUE_CORE_OTA_PACK_INTERVAL_MS:60000}" + # The size of OTA updates notifications fetched from the queue. The queue stores pairs of firmware and device ids pack-size: "${TB_QUEUE_CORE_OTA_PACK_SIZE:100}" + # Stats topic name for queue Kafka, RabbitMQ, etc. usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}" stats: + # Enable/disable statistics for Core microservices enabled: "${TB_QUEUE_CORE_STATS_ENABLED:true}" + # Statistics printing interval for Core microservices print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:60000}" vc: + # Default topic name for Kafka, RabbitMQ, etc. topic: "${TB_QUEUE_VC_TOPIC:tb_version_control}" + # Number of partitions to associate with this queue. Used for scaling the number of messages that can be processed in parallel partitions: "${TB_QUEUE_VC_PARTITIONS:10}" + # Interval in milliseconds between polling of the messages if no new messages arrive poll-interval: "${TB_QUEUE_VC_INTERVAL_MS:25}" - pack-processing-timeout: "${TB_QUEUE_VC_PACK_PROCESSING_TIMEOUT_MS:60000}" + # Timeout before retrying all failed and timed-out messages from the processing pack + pack-processing-timeout: "${TB_QUEUE_VC_PACK_PROCESSING_TIMEOUT_MS:180000}" + # Queue settings for Kafka, RabbitMQ, etc. Limit for single message size msg-chunk-size: "${TB_QUEUE_VC_MSG_CHUNK_SIZE:250000}" +# Version control parameters vc: # Pool size for handling export tasks - thread_pool_size: "${TB_VC_POOL_SIZE:2}" + thread_pool_size: "${TB_VC_POOL_SIZE:6}" git: # Pool size for handling the git IO operations io_pool_size: "${TB_VC_GIT_POOL_SIZE:3}" + # Default storing repository path repositories-folder: "${TB_VC_GIT_REPOSITORIES_FOLDER:${java.io.tmpdir}/repositories}" # Usage statistics parameters usage: stats: report: + # Enable/Disable the collection of statistics about API usage. Collected on a system and tenant level by default enabled: "${USAGE_STATS_REPORT_ENABLED:true}" + # Enable/Disable collection of statistics about API usage on a customer level enabled_per_customer: "${USAGE_STATS_REPORT_PER_CUSTOMER_ENABLED:false}" + # Interval of reporting the statistics. By default, the summarized statistics are sent every 10 seconds interval: "${USAGE_STATS_REPORT_INTERVAL:10}" +# Metrics parameters metrics: # Enable/disable actuator metrics. enabled: "${METRICS_ENABLED:false}" @@ -192,6 +289,7 @@ metrics: # Metrics percentiles returned by actuator for timer metrics. List of double values (divided by ,). percentiles: "${METRICS_TIMER_PERCENTILES:0.5}" +# General management parameters management: endpoints: web: @@ -199,7 +297,14 @@ management: # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). include: '${METRICS_ENDPOINTS_EXPOSE:info}' +# Service common properties service: - type: "${TB_SERVICE_TYPE:tb-vc-executor}" + type: "${TB_SERVICE_TYPE:tb-vc-executor}" # service type # Unique id for this service (autogenerated if empty) id: "${TB_SERVICE_ID:}" + +# Notification system parameters +notification_system: + rules: + # Semicolon-separated deduplication durations (in millis) for trigger types. Format: 'NotificationRuleTriggerType1:123;NotificationRuleTriggerType2:456' + deduplication_durations: "${TB_NOTIFICATION_RULES_DEDUPLICATION_DURATIONS:RATE_LIMITS:14400000;}" diff --git a/msa/web-ui/docker/Dockerfile b/msa/web-ui/docker/Dockerfile index cf5df5ee2c..18a19e0ead 100644 --- a/msa/web-ui/docker/Dockerfile +++ b/msa/web-ui/docker/Dockerfile @@ -14,7 +14,7 @@ # limitations under the License. # -FROM thingsboard/node:16.17.0-bullseye-slim +FROM thingsboard/node:16.20.2-bookworm-slim ENV NODE_ENV production ENV DOCKER_MODE true diff --git a/msa/web-ui/package.json b/msa/web-ui/package.json index edd232ba57..96b7169f98 100644 --- a/msa/web-ui/package.json +++ b/msa/web-ui/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-web-ui", "private": true, - "version": "3.5.2", + "version": "3.6.2", "description": "ThingsBoard Web UI Microservice", "main": "server.ts", "bin": "server.js", diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index 58efe59856..1b22066e2a 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT msa org.thingsboard.msa @@ -80,7 +80,7 @@ install-node-and-yarn - v16.15.1 + v16.20.2 v1.22.17 diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index d81be2fece..15576be6af 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard netty-mqtt - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT jar Netty MQTT Client @@ -35,6 +35,10 @@ + + org.thingsboard.common + util + io.netty netty-codec-mqtt diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttChannelHandler.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttChannelHandler.java index fa88fa0254..59744ed194 100644 --- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttChannelHandler.java +++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttChannelHandler.java @@ -16,6 +16,10 @@ package org.thingsboard.mqtt; import com.google.common.collect.ImmutableSet; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; @@ -34,9 +38,13 @@ import io.netty.handler.codec.mqtt.MqttQoS; import io.netty.handler.codec.mqtt.MqttSubAckMessage; import io.netty.handler.codec.mqtt.MqttUnsubAckMessage; import io.netty.util.CharsetUtil; +import io.netty.util.ReferenceCountUtil; import io.netty.util.concurrent.Promise; import lombok.extern.slf4j.Slf4j; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; + @Slf4j final class MqttChannelHandler extends SimpleChannelInboundHandler { @@ -117,27 +125,48 @@ final class MqttChannelHandler extends SimpleChannelInboundHandler super.channelInactive(ctx); } - private void invokeHandlersForIncomingPublish(MqttPublishMessage message) { - boolean handlerInvoked = false; - for (MqttSubscription subscription : ImmutableSet.copyOf(this.client.getSubscriptions().values())) { - if (subscription.matches(message.variableHeader().topicName())) { - if (subscription.isOnce() && subscription.isCalled()) { - continue; - } - message.payload().markReaderIndex(); - subscription.setCalled(true); - subscription.getHandler().onMessage(message.variableHeader().topicName(), message.payload()); - if (subscription.isOnce()) { - this.client.off(subscription.getTopic(), subscription.getHandler()); + ListenableFuture invokeHandlersForIncomingPublish(MqttPublishMessage message) { + var future = Futures.immediateVoidFuture(); + var handlerInvoked = new AtomicBoolean(); + try { + for (MqttSubscription subscription : ImmutableSet.copyOf(this.client.getSubscriptions().values())) { + if (subscription.matches(message.variableHeader().topicName())) { + future = Futures.transform(future, x -> { + if (subscription.isOnce() && subscription.isCalled()) { + return null; + } + message.payload().markReaderIndex(); + subscription.setCalled(true); + subscription.getHandler().onMessage(message.variableHeader().topicName(), message.payload()); + if (subscription.isOnce()) { + this.client.off(subscription.getTopic(), subscription.getHandler()); + } + message.payload().resetReaderIndex(); + handlerInvoked.set(true); + return null; + }, client.getHandlerExecutor()); } - message.payload().resetReaderIndex(); - handlerInvoked = true; } + future = Futures.transform(future, x -> { + if (!handlerInvoked.get() && client.getDefaultHandler() != null) { + client.getDefaultHandler().onMessage(message.variableHeader().topicName(), message.payload()); + } + return null; + }, client.getHandlerExecutor()); + } finally { + Futures.addCallback(future, new FutureCallback<>() { + @Override + public void onSuccess(Void result) { + message.payload().release(); + } + + @Override + public void onFailure(Throwable t) { + message.payload().release(); + } + }, MoreExecutors.directExecutor()); } - if (!handlerInvoked && client.getDefaultHandler() != null) { - client.getDefaultHandler().onMessage(message.variableHeader().topicName(), message.payload()); - } - message.payload().release(); + return future; } private void handleConack(Channel channel, MqttConnAckMessage message) { @@ -204,11 +233,13 @@ final class MqttChannelHandler extends SimpleChannelInboundHandler break; case AT_LEAST_ONCE: - invokeHandlersForIncomingPublish(message); + var future = invokeHandlersForIncomingPublish(message); if (message.variableHeader().packetId() != -1) { - MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0); - MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(message.variableHeader().packetId()); - channel.writeAndFlush(new MqttPubAckMessage(fixedHeader, variableHeader)); + future.addListener(() -> { + MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0); + MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(message.variableHeader().packetId()); + channel.writeAndFlush(new MqttPubAckMessage(fixedHeader, variableHeader)); + }, MoreExecutors.directExecutor()); } break; @@ -263,14 +294,20 @@ final class MqttChannelHandler extends SimpleChannelInboundHandler } private void handlePubrel(Channel channel, MqttMessage message) { + var future = Futures.immediateVoidFuture(); if (this.client.getQos2PendingIncomingPublishes().containsKey(((MqttMessageIdVariableHeader) message.variableHeader()).messageId())) { MqttIncomingQos2Publish incomingQos2Publish = this.client.getQos2PendingIncomingPublishes().get(((MqttMessageIdVariableHeader) message.variableHeader()).messageId()); - this.invokeHandlersForIncomingPublish(incomingQos2Publish.getIncomingPublish()); - this.client.getQos2PendingIncomingPublishes().remove(incomingQos2Publish.getIncomingPublish().variableHeader().packetId()); + future = invokeHandlersForIncomingPublish(incomingQos2Publish.getIncomingPublish()); + future = Futures.transform(future, x -> { + this.client.getQos2PendingIncomingPublishes().remove(incomingQos2Publish.getIncomingPublish().variableHeader().packetId()); + return null; + }, MoreExecutors.directExecutor()); } - MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBCOMP, false, MqttQoS.AT_MOST_ONCE, false, 0); - MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(((MqttMessageIdVariableHeader) message.variableHeader()).messageId()); - channel.writeAndFlush(new MqttMessage(fixedHeader, variableHeader)); + future.addListener(() -> { + MqttFixedHeader fixedHeader = new MqttFixedHeader(MqttMessageType.PUBCOMP, false, MqttQoS.AT_MOST_ONCE, false, 0); + MqttMessageIdVariableHeader variableHeader = MqttMessageIdVariableHeader.from(((MqttMessageIdVariableHeader) message.variableHeader()).messageId()); + channel.writeAndFlush(new MqttMessage(fixedHeader, variableHeader)); + }, MoreExecutors.directExecutor()); } private void handlePubcomp(MqttMessage message) { @@ -281,4 +318,21 @@ final class MqttChannelHandler extends SimpleChannelInboundHandler pendingPublish.getPayload().release(); pendingPublish.onPubcompReceived(); } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + try { + if (cause instanceof IOException) { + if (log.isDebugEnabled()) { + log.debug("[{}] IOException: ", client.getClientConfig().getOwnerId(), cause); + } else { + log.info("[{}] IOException: {}", client.getClientConfig().getOwnerId(), cause.getMessage()); + } + } else { + log.warn("[{}] exceptionCaught", client.getClientConfig().getOwnerId(), cause); + } + } finally { + ReferenceCountUtil.release(cause); + } + } } diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClient.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClient.java index 2fe179de31..536a76119f 100644 --- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClient.java +++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClient.java @@ -21,6 +21,7 @@ import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.handler.codec.mqtt.MqttQoS; import io.netty.util.concurrent.Future; +import org.thingsboard.common.util.ListeningExecutor; public interface MqttClient { @@ -71,6 +72,8 @@ public interface MqttClient { */ void setEventLoop(EventLoopGroup eventLoop); + ListeningExecutor getHandlerExecutor(); + /** * Subscribe on the given topic. When a message is received, MqttClient will invoke the {@link MqttHandler#onMessage(String, ByteBuf)} function of the given handler * @@ -180,8 +183,8 @@ public interface MqttClient { * @param config The config object to use while looking for settings * @param defaultHandler The handler for incoming messages that do not match any topic subscriptions */ - static MqttClient create(MqttClientConfig config, MqttHandler defaultHandler){ - return new MqttClientImpl(config, defaultHandler); + static MqttClient create(MqttClientConfig config, MqttHandler defaultHandler, ListeningExecutor handlerExecutor){ + return new MqttClientImpl(config, defaultHandler, handlerExecutor); } /** diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientConfig.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientConfig.java index 1e20a842c3..10cee5d5fc 100644 --- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientConfig.java +++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientConfig.java @@ -19,6 +19,8 @@ import io.netty.channel.Channel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.mqtt.MqttVersion; import io.netty.handler.ssl.SslContext; +import lombok.Getter; +import lombok.Setter; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -26,10 +28,12 @@ import java.util.Random; @SuppressWarnings({"WeakerAccess", "unused"}) public final class MqttClientConfig { - private final SslContext sslContext; private final String randomClientId; + @Getter + @Setter + private String ownerId; // [TenantId][IntegrationId] or [TenantId][RuleNodeId] for exceptions logging purposes private String clientId; private int timeoutSeconds = 60; private MqttVersion protocolVersion = MqttVersion.MQTT_3_1; diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java index f38a790be7..63d65a1cc2 100644 --- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java +++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientImpl.java @@ -46,6 +46,7 @@ import io.netty.util.concurrent.DefaultPromise; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Promise; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.ListeningExecutor; import java.util.Collections; import java.util.HashSet; @@ -88,13 +89,13 @@ final class MqttClientImpl implements MqttClient { private int port; private MqttClientCallback callback; + private final ListeningExecutor handlerExecutor; /** * Construct the MqttClientImpl with default config */ - public MqttClientImpl(MqttHandler defaultHandler) { - this.clientConfig = new MqttClientConfig(); - this.defaultHandler = defaultHandler; + public MqttClientImpl(MqttHandler defaultHandler, ListeningExecutor handlerExecutor) { + this(new MqttClientConfig(), defaultHandler, handlerExecutor); } /** @@ -103,9 +104,10 @@ final class MqttClientImpl implements MqttClient { * * @param clientConfig The config object to use while looking for settings */ - public MqttClientImpl(MqttClientConfig clientConfig, MqttHandler defaultHandler) { + public MqttClientImpl(MqttClientConfig clientConfig, MqttHandler defaultHandler, ListeningExecutor handlerExecutor) { this.clientConfig = clientConfig; this.defaultHandler = defaultHandler; + this.handlerExecutor = handlerExecutor; } /** @@ -227,6 +229,11 @@ final class MqttClientImpl implements MqttClient { this.eventLoop = eventLoop; } + @Override + public ListeningExecutor getHandlerExecutor() { + return this.handlerExecutor; + } + /** * Subscribe on the given topic. When a message is received, MqttClient will invoke the {@link MqttHandler#onMessage(String, ByteBuf)} function of the given handler * diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttHandler.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttHandler.java index 0ec03ff04b..21c07a17cd 100644 --- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttHandler.java +++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttHandler.java @@ -15,9 +15,10 @@ */ package org.thingsboard.mqtt; +import com.google.common.util.concurrent.ListenableFuture; import io.netty.buffer.ByteBuf; public interface MqttHandler { - void onMessage(String topic, ByteBuf payload); + ListenableFuture onMessage(String topic, ByteBuf payload); } diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttSubscription.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttSubscription.java index 6c4abb4c5c..c4bc9e38c1 100644 --- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttSubscription.java +++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttSubscription.java @@ -25,7 +25,7 @@ final class MqttSubscription { private final boolean once; - private boolean called; + private volatile boolean called; MqttSubscription(String topic, MqttHandler handler, boolean once) { if (topic == null) { diff --git a/netty-mqtt/src/test/java/org/thingsboard/mqtt/integration/MqttIntegrationTest.java b/netty-mqtt/src/test/java/org/thingsboard/mqtt/integration/MqttIntegrationTest.java index cb1b6b81fe..04c2be1740 100644 --- a/netty-mqtt/src/test/java/org/thingsboard/mqtt/integration/MqttIntegrationTest.java +++ b/netty-mqtt/src/test/java/org/thingsboard/mqtt/integration/MqttIntegrationTest.java @@ -26,6 +26,7 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.thingsboard.common.util.AbstractListeningExecutor; import org.thingsboard.mqtt.MqttClient; import org.thingsboard.mqtt.MqttClientConfig; import org.thingsboard.mqtt.MqttConnectResult; @@ -49,8 +50,18 @@ public class MqttIntegrationTest { MqttClient mqttClient; + AbstractListeningExecutor handlerExecutor; + @Before public void init() throws Exception { + this.handlerExecutor = new AbstractListeningExecutor() { + @Override + protected int getThreadPollSize() { + return 4; + } + }; + handlerExecutor.init(); + this.eventLoopGroup = new NioEventLoopGroup(); this.mqttServer = new MqttServer(); @@ -68,6 +79,9 @@ public class MqttIntegrationTest { if (this.eventLoopGroup != null) { this.eventLoopGroup.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS); } + if (this.handlerExecutor != null) { + this.handlerExecutor.destroy(); + } } @Test @@ -108,9 +122,10 @@ public class MqttIntegrationTest { private MqttClient initClient() throws Exception { MqttClientConfig config = new MqttClientConfig(); + config.setOwnerId("MqttIntegrationTest"); config.setTimeoutSeconds(KEEPALIVE_TIMEOUT_SECONDS); config.setReconnectDelay(RECONNECT_DELAY_SECONDS); - MqttClient client = MqttClient.create(config, null); + MqttClient client = MqttClient.create(config, null, handlerExecutor); client.setEventLoop(this.eventLoopGroup); Future connectFuture = client.connect(MQTT_HOST, this.mqttServer.getMqttPort()); diff --git a/packaging/java/scripts/control/deb/preinst b/packaging/java/scripts/control/deb/preinst index eebe378588..10a9b92679 100644 --- a/packaging/java/scripts/control/deb/preinst +++ b/packaging/java/scripts/control/deb/preinst @@ -10,7 +10,6 @@ if ! getent passwd ${pkg.user} >/dev/null; then adduser --quiet \ --system \ --ingroup ${pkg.user} \ - --quiet \ --disabled-login \ --disabled-password \ --home ${pkg.installFolder} \ @@ -19,4 +18,4 @@ if ! getent passwd ${pkg.user} >/dev/null; then ${pkg.user} fi -exit 0 \ No newline at end of file +exit 0 diff --git a/packaging/js/scripts/control/deb/preinst b/packaging/js/scripts/control/deb/preinst index d2ebea46d7..e51297c6c1 100644 --- a/packaging/js/scripts/control/deb/preinst +++ b/packaging/js/scripts/control/deb/preinst @@ -8,7 +8,6 @@ if ! getent passwd ${pkg.user} >/dev/null; then adduser --quiet \ --system \ --ingroup ${pkg.user} \ - --quiet \ --disabled-login \ --disabled-password \ --home ${pkg.installFolder} \ diff --git a/pom.xml b/pom.xml index 822400b2da..b6c6f02044 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT pom Thingsboard @@ -56,7 +56,7 @@ 3.11.14 31.1-jre 2.6.1 - 3.4 + 3.12.0 1.15 2.11.0 1.2 @@ -78,7 +78,7 @@ 3.8.1 3.21.9 1.42.1 - 1.0.6 + 1.1.5 1.18.18 1.2.4 1.2.5 @@ -121,6 +121,7 @@ 3.4.0 8.17.0 6.0.20.Final + 3.5.2 3.0.0 2.0.1.Final 1.7.2 @@ -152,6 +153,8 @@ 1.12.1 6.4.2 1.34.1 + 1.17 + 2.19.0 8.0.1 @@ -893,6 +896,11 @@ version-control ${project.version} + + org.thingsboard.common + proto + ${project.version} + org.thingsboard.common cache @@ -1924,6 +1932,11 @@ hibernate-validator ${hibernate-validator.version} + + io.hypersistence + hypersistence-utils-hibernate-55 + ${hypersistence-utils.version} + org.glassfish javax.el @@ -2029,6 +2042,21 @@ google-oauth-client ${google-oauth-client.version} + + org.apache.xmlgraphics + batik-transcoder + ${apache-xmlgraphics.version} + + + org.apache.xmlgraphics + batik-codec + ${apache-xmlgraphics.version} + + + com.drewnoakes + metadata-extractor + ${drewnoakes-metadata-extractor.version} + diff --git a/pull_request_template.md b/pull_request_template.md index f65aa0a1f3..584ee6f4fc 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -26,6 +26,7 @@ Put your PR description here instead of this sentence. - [ ] If new dependency was added: the dependency tree is checked for conflicts. - [ ] If new service was added: the service is marked with corresponding @TbCoreComponent, @TbRuleEngineComponent, @TbTransportComponent, etc. - [ ] If new REST API was added: the RestClient.java was updated, issue for [Python REST client](https://github.com/thingsboard/thingsboard-python-rest-client) is created. +- [ ] If new yml property was added: make sure a description is added (above or near the property). diff --git a/rest-client/pom.xml b/rest-client/pom.xml index 728ffc5040..ea223ae64c 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard rest-client diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index 51eb446d70..5fe2561f3f 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -84,7 +84,7 @@ import org.thingsboard.server.common.data.device.DeviceSearchQuery; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeInfo; -import org.thingsboard.server.common.data.edge.EdgeInstallInstructions; +import org.thingsboard.server.common.data.edge.EdgeInstructions; import org.thingsboard.server.common.data.edge.EdgeSearchQuery; import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; import org.thingsboard.server.common.data.id.AlarmCommentId; @@ -499,6 +499,15 @@ public class RestClient implements Closeable { return restTemplate.postForEntity(baseURL + "/api/alarm", alarm, Alarm.class).getBody(); } + public List getAlarmTypes(PageLink pageLink) { + return restTemplate.exchange( + baseURL + "/api/alarm/types?" + getUrlParams(pageLink), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }).getBody(); + } + public AlarmComment saveAlarmComment(AlarmId alarmId, AlarmComment alarmComment) { return restTemplate.postForEntity(baseURL + "/api/alarm/{alarmId}/comment", alarmComment, AlarmComment.class, alarmId.getId()).getBody(); } @@ -694,6 +703,7 @@ public class RestClient implements Closeable { }).getBody(); } + @Deprecated(since = "3.6.2") public List getAssetTypes() { return restTemplate.exchange(URI.create( baseURL + "/api/asset/types"), @@ -703,6 +713,15 @@ public class RestClient implements Closeable { }).getBody(); } + public List getAssetProfileNames(boolean activeOnly) { + return restTemplate.exchange( + baseURL + "/api/assetProfile/names?activeOnly={activeOnly}", + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, activeOnly).getBody(); + } + public BulkImportResult processAssetsBulkImport(BulkImportRequest request) { return restTemplate.exchange( baseURL + "/api/asset/bulk_import", @@ -1385,6 +1404,7 @@ public class RestClient implements Closeable { }).getBody(); } + @Deprecated(since = "3.6.2") public List getDeviceTypes() { return restTemplate.exchange( baseURL + "/api/device/types", @@ -1394,6 +1414,15 @@ public class RestClient implements Closeable { }).getBody(); } + public List getDeviceProfileNames(boolean activeOnly) { + return restTemplate.exchange( + baseURL + "/api/deviceProfile/names?activeOnly={activeOnly}", + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, activeOnly).getBody(); + } + public JsonNode claimDevice(String deviceName, ClaimRequest claimRequest) { return restTemplate.exchange( baseURL + "/api/customer/device/{deviceName}/claim", @@ -2364,7 +2393,8 @@ public class RestClient implements Closeable { boolean deleteAllDataForKeys, Long startTs, Long endTs, - boolean rewriteLatestIfDeleted) { + boolean rewriteLatestIfDeleted, + boolean deleteLatest) { Map params = new HashMap<>(); params.put("entityType", entityId.getEntityType().name()); params.put("entityId", entityId.getId().toString()); @@ -2373,17 +2403,34 @@ public class RestClient implements Closeable { params.put("startTs", startTs.toString()); params.put("endTs", endTs.toString()); params.put("rewriteLatestIfDeleted", String.valueOf(rewriteLatestIfDeleted)); + params.put("deleteLatest", String.valueOf(deleteLatest)); return restTemplate .exchange( - baseURL + "/api/plugins/telemetry/{entityType}/{entityId}/timeseries/delete?keys={keys}&deleteAllDataForKeys={deleteAllDataForKeys}&startTs={startTs}&endTs={endTs}&rewriteLatestIfDeleted={rewriteLatestIfDeleted}", + baseURL + "/api/plugins/telemetry/{entityType}/{entityId}/timeseries/delete?keys={keys}&deleteAllDataForKeys={deleteAllDataForKeys}&startTs={startTs}&endTs={endTs}&rewriteLatestIfDeleted={rewriteLatestIfDeleted}&deleteLatest={deleteLatest}", HttpMethod.DELETE, HttpEntity.EMPTY, Object.class, params) .getStatusCode() .is2xxSuccessful(); + } + public boolean deleteEntityLatestTimeseries(EntityId entityId, List keys) { + Map params = new HashMap<>(); + params.put("entityType", entityId.getEntityType().name()); + params.put("entityId", entityId.getId().toString()); + params.put("keys", listToString(keys)); + + return restTemplate + .exchange( + baseURL + "/api/plugins/telemetry/{entityType}/{entityId}/timeseries/latest/delete?keys={keys}", + HttpMethod.DELETE, + HttpEntity.EMPTY, + Object.class, + params) + .getStatusCode() + .is2xxSuccessful(); } public boolean deleteEntityAttributes(DeviceId deviceId, String scope, List keys) { @@ -3214,12 +3261,18 @@ public class RestClient implements Closeable { }).getBody(); } - public Optional getEdgeDockerInstallInstructions(EdgeId edgeId) { - ResponseEntity edgeInstallInstructionsResult = - restTemplate.getForEntity(baseURL + "/api/edge/instructions/{edgeId}", EdgeInstallInstructions.class, edgeId.getId()); + public Optional getEdgeInstallInstructions(EdgeId edgeId, String method) { + ResponseEntity edgeInstallInstructionsResult = + restTemplate.getForEntity(baseURL + "/api/edge/instructions/install/{edgeId}/{method}", EdgeInstructions.class, edgeId.getId(), method); return Optional.ofNullable(edgeInstallInstructionsResult.getBody()); } + public Optional getEdgeUpgradeInstructions(String edgeVersion, String method) { + ResponseEntity edgeUpgradeInstructionsResult = + restTemplate.getForEntity(baseURL + "/api/edge/instructions/upgrade/{edgeVersion}/{method}", EdgeInstructions.class, edgeVersion, method); + return Optional.ofNullable(edgeUpgradeInstructionsResult.getBody()); + } + public UUID saveEntitiesVersion(VersionCreateRequest request) { return restTemplate.postForEntity(baseURL + "/api/entities/vc/version", request, UUID.class).getBody(); } diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index 3ebb1f6c76..e477188b12 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index f8c45d388b..d9c42e9e34 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/EmptyNodeConfiguration.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/EmptyNodeConfiguration.java index 5c54687205..22ffe34769 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/EmptyNodeConfiguration.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/EmptyNodeConfiguration.java @@ -24,7 +24,6 @@ public class EmptyNodeConfiguration implements NodeConfiguration callback); + NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest, FutureCallback callback); + + void sendGeneralWebNotification(TenantId tenantId, UsersFilter recipients, NotificationTemplate template); void deleteNotificationRequest(TenantId tenantId, NotificationRequestId notificationRequestId); diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java index 05e9bf164f..c3955733ed 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java @@ -18,6 +18,7 @@ package org.thingsboard.rule.engine.api; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; @@ -34,9 +35,9 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.query.AlarmData; import org.thingsboard.server.common.data.query.AlarmDataQuery; -import org.thingsboard.server.dao.alarm.AlarmOperationResult; import java.util.Collection; @@ -54,6 +55,7 @@ public interface RuleEngineAlarmService { * Only one active alarm may exist for the pair {originatorId, alarmType} */ AlarmApiCallResult createAlarm(AlarmCreateOrUpdateActiveRequest request); + /** * Designed to update existing alarm. Accepts only part of the alarm fields. */ @@ -67,21 +69,6 @@ public interface RuleEngineAlarmService { AlarmApiCallResult unassignAlarm(TenantId tenantId, AlarmId alarmId, long assignTs); - /* - * Legacy API, before 3.5. - */ - @Deprecated(since = "3.5", forRemoval = true) - Alarm createOrUpdateAlarm(Alarm alarm); - - @Deprecated(since = "3.5", forRemoval = true) - ListenableFuture ackAlarm(TenantId tenantId, AlarmId alarmId, long ackTs); - - @Deprecated(since = "3.5", forRemoval = true) - ListenableFuture clearAlarm(TenantId tenantId, AlarmId alarmId, JsonNode details, long clearTs); - - @Deprecated(since = "3.5", forRemoval = true) - ListenableFuture clearAlarmForResult(TenantId tenantId, AlarmId alarmId, JsonNode details, long clearTs); - // Other API Boolean deleteAlarm(TenantId tenantId, AlarmId alarmId); @@ -91,7 +78,7 @@ public interface RuleEngineAlarmService { Alarm findLatestActiveByOriginatorAndType(TenantId tenantId, EntityId originator, String type); - ListenableFuture findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type); + Alarm findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type); AlarmInfo findAlarmInfoById(TenantId tenantId, AlarmId alarmId); @@ -99,15 +86,17 @@ public interface RuleEngineAlarmService { return Futures.immediateFuture(findAlarmInfoById(tenantId, alarmId)); } - ListenableFuture> findAlarms(TenantId tenantId, AlarmQuery query); + PageData findAlarms(TenantId tenantId, AlarmQuery query); - ListenableFuture> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query); + PageData findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query); - ListenableFuture> findAlarmsV2(TenantId tenantId, AlarmQueryV2 query); + PageData findAlarmsV2(TenantId tenantId, AlarmQueryV2 query); - ListenableFuture> findCustomerAlarmsV2(TenantId tenantId, CustomerId customerId, AlarmQueryV2 query); + PageData findCustomerAlarmsV2(TenantId tenantId, CustomerId customerId, AlarmQueryV2 query); AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus, AlarmStatus alarmStatus, String assigneeId); PageData findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection orderedEntityIds); + + PageData findAlarmTypesByTenantId(TenantId tenantId, PageLink pageLink); } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleNode.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleNode.java index 99d073df44..7037f2f3fe 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleNode.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleNode.java @@ -15,6 +15,7 @@ */ package org.thingsboard.rule.engine.api; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.plugin.ComponentClusteringMode; import org.thingsboard.server.common.data.plugin.ComponentScope; import org.thingsboard.server.common.data.plugin.ComponentType; @@ -47,7 +48,7 @@ public @interface RuleNode { ComponentScope scope() default ComponentScope.TENANT; - String[] relationTypes() default {"Success", "Failure"}; + String[] relationTypes() default {TbNodeConnectionType.SUCCESS, TbNodeConnectionType.FAILURE}; String[] uiResources() default {}; @@ -65,4 +66,6 @@ public @interface RuleNode { RuleChainType[] ruleChainTypes() default {RuleChainType.CORE, RuleChainType.EDGE}; + int version() default 0; + } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index 0ad8fc5e0f..c1de47a35c 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -16,6 +16,7 @@ package org.thingsboard.rule.engine.api; import io.netty.channel.EventLoopGroup; +import org.thingsboard.common.util.ExecutorProvider; import org.thingsboard.common.util.ListeningExecutor; import org.thingsboard.rule.engine.api.notification.SlackService; import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; @@ -36,6 +37,7 @@ import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rule.RuleNode; @@ -55,6 +57,7 @@ import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeService; +import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.nosql.CassandraStatementTask; import org.thingsboard.server.dao.nosql.TbResultSetFuture; @@ -167,6 +170,8 @@ public interface TbContext { void enqueueForTellFailure(TbMsg msg, String failureMessage); + void enqueueForTellFailure(TbMsg tbMsg, Throwable t); + void enqueueForTellNext(TbMsg msg, String relationType); void enqueueForTellNext(TbMsg msg, Set relationTypes); @@ -181,21 +186,52 @@ public interface TbContext { void ack(TbMsg tbMsg); + @Deprecated(since = "3.6.0", forRemoval = true) TbMsg newMsg(String queueName, String type, EntityId originator, TbMsgMetaData metaData, String data); + /** + * Creates a new TbMsg instance with the specified parameters. + * + *

Deprecated: This method is deprecated since version 3.6.0 and should only be used when you need to + * specify a custom message type that doesn't exist in the {@link TbMsgType} enum. For standard message types, + * it is recommended to use the {@link #newMsg(String, TbMsgType, EntityId, CustomerId, TbMsgMetaData, String)} + * method instead.

+ * + * @param queueName the name of the queue where the message will be sent + * @param type the type of the message + * @param originator the originator of the message + * @param customerId the ID of the customer associated with the message + * @param metaData the metadata of the message + * @param data the data of the message + * @return new TbMsg instance + */ + @Deprecated(since = "3.6.0") TbMsg newMsg(String queueName, String type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data); + @Deprecated(since = "3.6.0", forRemoval = true) TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data); + TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data); + + TbMsg newMsg(String queueName, TbMsgType type, EntityId originator, CustomerId customerId, TbMsgMetaData metaData, String data); + + TbMsg transformMsg(TbMsg origMsg, TbMsgType type, EntityId originator, TbMsgMetaData metaData, String data); + + TbMsg transformMsg(TbMsg origMsg, TbMsgMetaData metaData, String data); + + TbMsg transformMsgOriginator(TbMsg origMsg, EntityId originator); + TbMsg customerCreatedMsg(Customer customer, RuleNodeId ruleNodeId); TbMsg deviceCreatedMsg(Device device, RuleNodeId ruleNodeId); TbMsg assetCreatedMsg(Asset asset, RuleNodeId ruleNodeId); - // TODO: Does this changes the message? + @Deprecated(since = "3.6.0", forRemoval = true) TbMsg alarmActionMsg(Alarm alarm, RuleNodeId ruleNodeId, String action); + TbMsg alarmActionMsg(Alarm alarm, RuleNodeId ruleNodeId, TbMsgType actionMsgType); + TbMsg attributesUpdatedActionMsg(EntityId originator, RuleNodeId ruleNodeId, String scope, List attributes); TbMsg attributesDeletedActionMsg(EntityId originator, RuleNodeId ruleNodeId, String scope, List keys); @@ -210,7 +246,7 @@ public interface TbContext { void schedule(Runnable runnable, long delay, TimeUnit timeUnit); - void checkTenantEntity(EntityId entityId); + void checkTenantEntity(EntityId entityId) throws TbNodeException; boolean isLocalEntity(EntityId entityId); @@ -284,6 +320,8 @@ public interface TbContext { ListeningExecutor getNotificationExecutor(); + ExecutorProvider getPubSubRuleNodeExecutorProvider(); + MailService getMailService(boolean isSystem); SmsService getSmsService(); @@ -302,6 +340,8 @@ public interface TbContext { SlackService getSlackService(); + boolean isExternalNodeForceAck(); + /** * Creates JS Script Engine * @deprecated @@ -354,4 +394,6 @@ public interface TbContext { WidgetTypeService getWidgetTypeService(); RuleEngineApiUsageStateService getRuleEngineApiUsageStateService(); + + EntityService getEntityService(); } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbNode.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbNode.java index 18044eb05c..25364bf1db 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbNode.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbNode.java @@ -15,6 +15,8 @@ */ package org.thingsboard.rule.engine.api; +import com.fasterxml.jackson.databind.JsonNode; +import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; @@ -29,8 +31,24 @@ public interface TbNode { void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException; - default void destroy() {} - - default void onPartitionChangeMsg(TbContext ctx, PartitionChangeMsg msg) {} + default void destroy() { + } + + default void onPartitionChangeMsg(TbContext ctx, PartitionChangeMsg msg) { + } + + /** + * Upgrades the configuration from a specific version to the current version specified in the + * {@link RuleNode} annotation for the instance of {@link TbNode}. + * + * @param fromVersion The version from which the configuration needs to be upgraded. + * @param oldConfiguration The old configuration to be upgraded. + * @return A pair consisting of a Boolean flag indicating the success of the upgrade + * and a JsonNode representing the upgraded configuration. + * @throws TbNodeException If an error occurs during the upgrade process. + */ + default TbPair upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException { + return new TbPair<>(false, oldConfiguration); + } } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbNodeException.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbNodeException.java index 32341dbf2b..a8ce9d7e42 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbNodeException.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbNodeException.java @@ -15,17 +15,33 @@ */ package org.thingsboard.rule.engine.api; +import lombok.Getter; +import org.thingsboard.server.common.msg.TbActorError; + /** * Created by ashvayka on 19.01.18. */ -public class TbNodeException extends Exception { +public class TbNodeException extends Exception implements TbActorError { + + @Getter + private final boolean unrecoverable; public TbNodeException(String message) { + this(message, false); + } + + public TbNodeException(String message, boolean unrecoverable) { super(message); + this.unrecoverable = unrecoverable; } public TbNodeException(Exception e) { + this(e, false); + } + + public TbNodeException(Exception e, boolean unrecoverable) { super(e); + this.unrecoverable = unrecoverable; } } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbRelationTypes.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbRelationTypes.java deleted file mode 100644 index 2535762ad5..0000000000 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbRelationTypes.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.rule.engine.api; - -/** - * Created by ashvayka on 19.01.18. - */ -public final class TbRelationTypes { - - public static String SUCCESS = "Success"; - public static String FAILURE = "Failure"; - -} diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/msg/DeviceAttributes.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/msg/DeviceAttributes.java deleted file mode 100644 index 4d899c2845..0000000000 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/msg/DeviceAttributes.java +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.rule.engine.api.msg; - -import org.thingsboard.server.common.data.DataConstants; -import org.thingsboard.server.common.data.kv.AttributeKey; -import org.thingsboard.server.common.data.kv.AttributeKvEntry; - -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -/** - * @author Andrew Shvayka - */ -public class DeviceAttributes { - - private final Map clientSideAttributesMap; - private final Map serverPrivateAttributesMap; - private final Map serverPublicAttributesMap; - - public DeviceAttributes(List clientSideAttributes, List serverPrivateAttributes, List serverPublicAttributes) { - this.clientSideAttributesMap = mapAttributes(clientSideAttributes); - this.serverPrivateAttributesMap = mapAttributes(serverPrivateAttributes); - this.serverPublicAttributesMap = mapAttributes(serverPublicAttributes); - } - - private static Map mapAttributes(List attributes) { - Map result = new HashMap<>(); - for (AttributeKvEntry attribute : attributes) { - result.put(attribute.getKey(), attribute); - } - return result; - } - - public Collection getClientSideAttributes() { - return clientSideAttributesMap.values(); - } - - public Collection getServerSideAttributes() { - return serverPrivateAttributesMap.values(); - } - - public Collection getServerSidePublicAttributes() { - return serverPublicAttributesMap.values(); - } - - public Optional getClientSideAttribute(String attribute) { - return Optional.ofNullable(clientSideAttributesMap.get(attribute)); - } - - public Optional getServerPrivateAttribute(String attribute) { - return Optional.ofNullable(serverPrivateAttributesMap.get(attribute)); - } - - public Optional getServerPublicAttribute(String attribute) { - return Optional.ofNullable(serverPublicAttributesMap.get(attribute)); - } - - public void remove(AttributeKey key) { - Map map = getMapByScope(key.getScope()); - if (map != null) { - map.remove(key.getAttributeKey()); - } - } - - public void update(String scope, List values) { - Map map = getMapByScope(scope); - values.forEach(v -> map.put(v.getKey(), v)); - } - - private Map getMapByScope(String scope) { - Map map = null; - if (scope.equalsIgnoreCase(DataConstants.CLIENT_SCOPE)) { - map = clientSideAttributesMap; - } else if (scope.equalsIgnoreCase(DataConstants.SHARED_SCOPE)) { - map = serverPublicAttributesMap; - } else if (scope.equalsIgnoreCase(DataConstants.SERVER_SCOPE)) { - map = serverPrivateAttributesMap; - } - return map; - } - - @Override - public String toString() { - return "DeviceAttributes{" + - "clientSideAttributesMap=" + clientSideAttributesMap + - ", serverPrivateAttributesMap=" + serverPrivateAttributesMap + - ", serverPublicAttributesMap=" + serverPublicAttributesMap + - '}'; - } -} diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/msg/DeviceAttributesEventNotificationMsg.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/msg/DeviceAttributesEventNotificationMsg.java deleted file mode 100644 index 215322bde3..0000000000 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/msg/DeviceAttributesEventNotificationMsg.java +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.rule.engine.api.msg; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.ToString; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.kv.AttributeKey; -import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.msg.MsgType; -import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * @author Andrew Shvayka - */ -@ToString -@AllArgsConstructor -public class DeviceAttributesEventNotificationMsg implements ToDeviceActorNotificationMsg { - - @Getter - private final TenantId tenantId; - @Getter - private final DeviceId deviceId; - @Getter - private final Set deletedKeys; - @Getter - private final String scope; - @Getter - private final List values; - @Getter - private final boolean deleted; - - public static DeviceAttributesEventNotificationMsg onUpdate(TenantId tenantId, DeviceId deviceId, String scope, List values) { - return new DeviceAttributesEventNotificationMsg(tenantId, deviceId, null, scope, values, false); - } - - public static DeviceAttributesEventNotificationMsg onDelete(TenantId tenantId, DeviceId deviceId, String scope, List keys) { - Set keysToNotify = new HashSet<>(); - keys.forEach(key -> keysToNotify.add(new AttributeKey(scope, key))); - return new DeviceAttributesEventNotificationMsg(tenantId, deviceId, keysToNotify, null, null, true); - } - - @Override - public MsgType getMsgType() { - return MsgType.DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG; - } -} diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/msg/DeviceCredentialsUpdateNotificationMsg.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/msg/DeviceCredentialsUpdateNotificationMsg.java deleted file mode 100644 index 665ace9610..0000000000 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/msg/DeviceCredentialsUpdateNotificationMsg.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.rule.engine.api.msg; - -import lombok.Data; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.security.DeviceCredentials; -import org.thingsboard.server.common.msg.MsgType; -import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; - -/** - * @author Andrew Shvayka - */ -@Data -public class DeviceCredentialsUpdateNotificationMsg implements ToDeviceActorNotificationMsg { - - private static final long serialVersionUID = -3956907402411126990L; - - private final TenantId tenantId; - private final DeviceId deviceId; - - /** - * LwM2M - * @return - */ - private final DeviceCredentials deviceCredentials; - - @Override - public MsgType getMsgType() { - return MsgType.DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG; - } -} diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/msg/DeviceEdgeUpdateMsg.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/msg/DeviceEdgeUpdateMsg.java deleted file mode 100644 index 96fc263b81..0000000000 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/msg/DeviceEdgeUpdateMsg.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.rule.engine.api.msg; - -import lombok.AllArgsConstructor; -import lombok.Data; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.EdgeId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.MsgType; -import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; - -@Data -@AllArgsConstructor -public class DeviceEdgeUpdateMsg implements ToDeviceActorNotificationMsg { - - private final TenantId tenantId; - private final DeviceId deviceId; - private final EdgeId edgeId; - - @Override - public MsgType getMsgType() { - return MsgType.DEVICE_EDGE_UPDATE_TO_DEVICE_ACTOR_MSG; - } -} diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/msg/DeviceMetaData.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/msg/DeviceMetaData.java deleted file mode 100644 index 16486552cc..0000000000 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/msg/DeviceMetaData.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.rule.engine.api.msg; - -import lombok.Data; -import org.thingsboard.server.common.data.id.DeviceId; - -/** - * Contains basic device metadata; - * - * @author ashvayka - */ -@Data -public final class DeviceMetaData { - - final DeviceId deviceId; - final String deviceName; - final String deviceType; - final DeviceAttributes deviceAttributes; - -} diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/msg/DeviceNameOrTypeUpdateMsg.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/msg/DeviceNameOrTypeUpdateMsg.java deleted file mode 100644 index b85899e06d..0000000000 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/msg/DeviceNameOrTypeUpdateMsg.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.rule.engine.api.msg; - -import lombok.AllArgsConstructor; -import lombok.Data; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.msg.MsgType; -import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; - -@Data -@AllArgsConstructor -public class DeviceNameOrTypeUpdateMsg implements ToDeviceActorNotificationMsg { - - private static final long serialVersionUID = -5738949227650536685L; - - private final TenantId tenantId; - private final DeviceId deviceId; - private final String deviceName; - private final String deviceType; - - @Override - public MsgType getMsgType() { - return MsgType.DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG; - } -} diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/util/TbNodeUtils.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/util/TbNodeUtils.java index 45ac5f30f5..396d810b00 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/util/TbNodeUtils.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/util/TbNodeUtils.java @@ -15,7 +15,6 @@ */ package org.thingsboard.rule.engine.api.util; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import org.springframework.util.CollectionUtils; import org.thingsboard.common.util.JacksonUtil; @@ -44,7 +43,7 @@ public class TbNodeUtils { try { return JacksonUtil.treeToValue(configuration.getData(), clazz); } catch (IllegalArgumentException e) { - throw new TbNodeException(e); + throw new TbNodeException(e, true); } } @@ -85,6 +84,7 @@ public class TbNodeUtils { } } + @Deprecated(since = "3.6.1", forRemoval = true) public static List processPatterns(List patterns, TbMsgMetaData metaData) { if (!CollectionUtils.isEmpty(patterns)) { return patterns.stream().map(p -> processPattern(p, metaData)).collect(Collectors.toList()); diff --git a/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/util/TbNodeUtilsTest.java b/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/util/TbNodeUtilsTest.java index 956cfaf40c..cb5514a82b 100644 --- a/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/util/TbNodeUtilsTest.java +++ b/rule-engine/rule-engine-api/src/test/java/org/thingsboard/rule/engine/api/util/TbNodeUtilsTest.java @@ -22,6 +22,7 @@ import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -43,7 +44,7 @@ public class TbNodeUtilsTest { ObjectNode node = JacksonUtil.newObjectNode(); node.put("data_key", "data_value"); - TbMsg msg = TbMsg.newMsg("CUSTOM", TenantId.SYS_TENANT_ID, md, JacksonUtil.toString(node)); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, md, JacksonUtil.toString(node)); String result = TbNodeUtils.processPattern(pattern, msg); Assert.assertEquals("ABC metadata_value data_value", result); } @@ -57,7 +58,7 @@ public class TbNodeUtilsTest { ObjectNode node = JacksonUtil.newObjectNode(); node.put("key", "data_value"); - TbMsg msg = TbMsg.newMsg("CUSTOM", TenantId.SYS_TENANT_ID, md, JacksonUtil.toString(node)); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, md, JacksonUtil.toString(node)); String result = TbNodeUtils.processPattern(pattern, msg); Assert.assertEquals(pattern, result); } @@ -71,7 +72,7 @@ public class TbNodeUtilsTest { ObjectNode node = JacksonUtil.newObjectNode(); node.put("key", "data_value"); - TbMsg msg = TbMsg.newMsg("CUSTOM", TenantId.SYS_TENANT_ID, md, JacksonUtil.toString(node)); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, md, JacksonUtil.toString(node)); String result = TbNodeUtils.processPattern(pattern, msg); Assert.assertEquals("ABC metadata_value data_value", result); } @@ -92,7 +93,7 @@ public class TbNodeUtilsTest { ObjectNode node = JacksonUtil.newObjectNode(); node.set("key1", key1Node); - TbMsg msg = TbMsg.newMsg("CUSTOM", TenantId.SYS_TENANT_ID, md, JacksonUtil.toString(node)); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, md, JacksonUtil.toString(node)); String result = TbNodeUtils.processPattern(pattern, msg); Assert.assertEquals("ABC metadata_value value3", result); } @@ -113,7 +114,7 @@ public class TbNodeUtilsTest { ObjectNode node = JacksonUtil.newObjectNode(); node.set("key1", key1Node); - TbMsg msg = TbMsg.newMsg("CUSTOM", TenantId.SYS_TENANT_ID, md, JacksonUtil.toString(node)); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, md, JacksonUtil.toString(node)); String result = TbNodeUtils.processPattern(pattern, msg); Assert.assertEquals("ABC metadata_value $[key1.key2[0].key3]", result); } diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index ac493e79ce..07331d951e 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractAlarmNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractAlarmNode.java index 3ca55f5db9..57871c91ff 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractAlarmNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractAlarmNode.java @@ -26,6 +26,8 @@ import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.script.ScriptLanguage; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -55,13 +57,13 @@ public abstract class TbAbstractAlarmNode { if (alarmResult.alarm == null) { - ctx.tellNext(msg, "False"); + ctx.tellNext(msg, TbNodeConnectionType.FALSE); } else if (alarmResult.isCreated) { - tellNext(ctx, msg, alarmResult, DataConstants.ENTITY_CREATED, "Created"); + tellNext(ctx, msg, alarmResult, TbMsgType.ENTITY_CREATED, "Created"); } else if (alarmResult.isUpdated) { - tellNext(ctx, msg, alarmResult, DataConstants.ENTITY_UPDATED, "Updated"); + tellNext(ctx, msg, alarmResult, TbMsgType.ENTITY_UPDATED, "Updated"); } else if (alarmResult.isCleared) { - tellNext(ctx, msg, alarmResult, DataConstants.ALARM_CLEAR, "Cleared"); + tellNext(ctx, msg, alarmResult, TbMsgType.ALARM_CLEAR, "Cleared"); } else { ctx.tellSuccess(msg); } @@ -71,13 +73,13 @@ public abstract class TbAbstractAlarmNode processAlarm(TbContext ctx, TbMsg msg); - protected ListenableFuture buildAlarmDetails(TbContext ctx, TbMsg msg, JsonNode previousDetails) { + protected ListenableFuture buildAlarmDetails(TbMsg msg, JsonNode previousDetails) { try { TbMsg dummyMsg = msg; if (previousDetails != null) { TbMsgMetaData metaData = msg.getMetaData().copy(); metaData.putValue(PREV_ALARM_DETAILS, JacksonUtil.toString(previousDetails)); - dummyMsg = ctx.transformMsg(msg, msg.getType(), msg.getOriginator(), metaData, msg.getData()); + dummyMsg = TbMsg.transformMsgMetadata(msg, metaData); } return scriptEngine.executeJsonAsync(dummyMsg); } catch (Exception e) { @@ -96,7 +98,7 @@ public abstract class TbAbstractAlarmNode ctx.tellNext(toAlarmMsg(ctx, alarmResult, msg), alarmAction), throwable -> ctx.tellFailure(toAlarmMsg(ctx, alarmResult, msg), throwable)); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNode.java index 1ed7689bd6..ea53138d10 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNode.java @@ -57,8 +57,8 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; import static org.thingsboard.common.util.DonAsynchron.withCallback; -import static org.thingsboard.rule.engine.api.TbRelationTypes.FAILURE; -import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; +import static org.thingsboard.server.common.data.msg.TbNodeConnectionType.FAILURE; +import static org.thingsboard.server.common.data.msg.TbNodeConnectionType.SUCCESS; @Slf4j public abstract class TbAbstractRelationActionNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAlarmResult.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAlarmResult.java index 67384ebe23..85c3cb5a98 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAlarmResult.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAlarmResult.java @@ -37,6 +37,11 @@ public class TbAlarmResult { } public static TbAlarmResult fromAlarmResult(AlarmApiCallResult result) { - return new TbAlarmResult(result.isCreated(), result.isModified(), result.isSeverityChanged(), result.isCleared(), result.getAlarm()); + return new TbAlarmResult( + result.isCreated(), + result.isModified() && !result.isSeverityChanged(), + result.isSeverityChanged(), + result.isCleared(), + result.getAlarm()); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java index a8d7d4985f..d0215a441f 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java @@ -71,7 +71,7 @@ public class TbClearAlarmNode extends TbAbstractAlarmNode clearAlarm(TbContext ctx, TbMsg msg, Alarm alarm) { ctx.logJsEvalRequest(); - ListenableFuture asyncDetails = buildAlarmDetails(ctx, msg, alarm.getDetails()); + ListenableFuture asyncDetails = buildAlarmDetails(msg, alarm.getDetails()); return Futures.transform(asyncDetails, details -> { ctx.logJsEvalResponse(); AlarmApiCallResult result = ctx.getAlarmService().clearAlarm(ctx.getTenantId(), alarm.getId(), System.currentTimeMillis(), details); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java index 6f7bb7cf8d..af4c7bc0c2 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java @@ -36,8 +36,7 @@ import org.thingsboard.server.common.data.objects.AttributesEntityView; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.util.CollectionsUtil; import org.thingsboard.server.common.msg.TbMsg; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.common.adaptor.JsonConverter; import javax.annotation.Nullable; import java.util.ArrayList; @@ -45,7 +44,12 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; +import static org.thingsboard.server.common.data.msg.TbMsgType.ACTIVITY_EVENT; +import static org.thingsboard.server.common.data.msg.TbMsgType.ATTRIBUTES_DELETED; +import static org.thingsboard.server.common.data.msg.TbMsgType.ATTRIBUTES_UPDATED; +import static org.thingsboard.server.common.data.msg.TbMsgType.INACTIVITY_EVENT; +import static org.thingsboard.server.common.data.msg.TbMsgType.POST_ATTRIBUTES_REQUEST; +import static org.thingsboard.server.common.data.msg.TbNodeConnectionType.SUCCESS; @Slf4j @RuleNode( @@ -71,14 +75,11 @@ public class TbCopyAttributesToEntityViewNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { - if (DataConstants.ATTRIBUTES_UPDATED.equals(msg.getType()) || - DataConstants.ATTRIBUTES_DELETED.equals(msg.getType()) || - DataConstants.ACTIVITY_EVENT.equals(msg.getType()) || - DataConstants.INACTIVITY_EVENT.equals(msg.getType()) || - SessionMsgType.POST_ATTRIBUTES_REQUEST.name().equals(msg.getType())) { + if (msg.isTypeOneOf(ATTRIBUTES_UPDATED, ATTRIBUTES_DELETED, + ACTIVITY_EVENT, INACTIVITY_EVENT, POST_ATTRIBUTES_REQUEST)) { if (!msg.getMetaData().getData().isEmpty()) { long now = System.currentTimeMillis(); - String scope = msg.getType().equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name()) ? + String scope = msg.isTypeOf(POST_ATTRIBUTES_REQUEST) ? DataConstants.CLIENT_SCOPE : msg.getMetaData().getValue(DataConstants.SCOPE); ListenableFuture> entityViewsFuture = @@ -90,9 +91,9 @@ public class TbCopyAttributesToEntityViewNode implements TbNode { long startTime = entityView.getStartTimeMs(); long endTime = entityView.getEndTimeMs(); if ((endTime != 0 && endTime > now && startTime < now) || (endTime == 0 && startTime < now)) { - if (DataConstants.ATTRIBUTES_DELETED.equals(msg.getType())) { + if (msg.isTypeOf(ATTRIBUTES_DELETED)) { List attributes = new ArrayList<>(); - for (JsonElement element : new JsonParser().parse(msg.getData()).getAsJsonObject().get("attributes").getAsJsonArray()) { + for (JsonElement element : JsonParser.parseString(msg.getData()).getAsJsonObject().get("attributes").getAsJsonArray()) { if (element.isJsonPrimitive()) { JsonPrimitive value = element.getAsJsonPrimitive(); if (value.isString()) { @@ -107,7 +108,7 @@ public class TbCopyAttributesToEntityViewNode implements TbNode { getFutureCallback(ctx, msg, entityView)); } } else { - Set attributes = JsonConverter.convertToAttributes(new JsonParser().parse(msg.getData())); + Set attributes = JsonConverter.convertToAttributes(JsonParser.parseString(msg.getData())); List filteredAttributes = attributes.stream().filter(attr -> attributeContainsInEntityView(scope, attr.getKey(), entityView)).collect(Collectors.toList()); ctx.getTelemetryService().saveAndNotify(ctx.getTenantId(), entityView.getId(), scope, filteredAttributes, diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java index bf146a52fb..b08fd1a562 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java @@ -66,7 +66,7 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode future = createRelationIfAbsent(ctx, msg, entity, relationType); return Futures.transform(future, result -> { if (result && config.isChangeOriginatorToRelatedEntity()) { - TbMsg tbMsg = ctx.transformMsg(msg, msg.getType(), entity.getEntityId(), msg.getMetaData(), msg.getData()); + TbMsg tbMsg = ctx.transformMsgOriginator(msg, entity.getEntityId()); return new RelationContainer(tbMsg, result); } return new RelationContainer(msg, result); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java index 88f2be46a8..021123a308 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java @@ -24,17 +24,16 @@ import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; -import org.thingsboard.server.common.msg.session.SessionMsgType; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; -import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; - @Slf4j @RuleNode( type = ComponentType.ACTION, @@ -48,8 +47,6 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; ) public class TbMsgCountNode implements TbNode { - private static final String TB_MSG_COUNT_NODE_MSG = "TbMsgCountNodeMsg"; - private AtomicLong messagesProcessed = new AtomicLong(0); private final Gson gson = new Gson(); private UUID nextTickId; @@ -68,7 +65,7 @@ public class TbMsgCountNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { - if (msg.getType().equals(TB_MSG_COUNT_NODE_MSG) && msg.getId().equals(nextTickId)) { + if (msg.isTypeOf(TbMsgType.MSG_COUNT_SELF_MSG) && msg.getId().equals(nextTickId)) { JsonObject telemetryJson = new JsonObject(); telemetryJson.addProperty(this.telemetryPrefix + "_" + ctx.getServiceId(), messagesProcessed.longValue()); @@ -77,8 +74,8 @@ public class TbMsgCountNode implements TbNode { TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("delta", Long.toString(System.currentTimeMillis() - lastScheduledTs + delay)); - TbMsg tbMsg = TbMsg.newMsg(msg.getQueueName(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), ctx.getTenantId(), msg.getCustomerId(), metaData, gson.toJson(telemetryJson)); - ctx.enqueueForTellNext(tbMsg, SUCCESS); + TbMsg tbMsg = TbMsg.newMsg(msg.getQueueName(), TbMsgType.POST_TELEMETRY_REQUEST, ctx.getTenantId(), msg.getCustomerId(), metaData, gson.toJson(telemetryJson)); + ctx.enqueueForTellNext(tbMsg, TbNodeConnectionType.SUCCESS); scheduleTickMsg(ctx, tbMsg); } else { messagesProcessed.incrementAndGet(); @@ -93,7 +90,7 @@ public class TbMsgCountNode implements TbNode { } lastScheduledTs = lastScheduledTs + delay; long curDelay = Math.max(0L, (lastScheduledTs - curTs)); - TbMsg tickMsg = ctx.newMsg(null, TB_MSG_COUNT_NODE_MSG, ctx.getSelfId(), msg != null ? msg.getCustomerId() : null, new TbMsgMetaData(), ""); + TbMsg tickMsg = ctx.newMsg(null, TbMsgType.MSG_COUNT_SELF_MSG, ctx.getSelfId(), msg != null ? msg.getCustomerId() : null, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); nextTickId = tickMsg.getId(); ctx.tellSelf(tickMsg, curDelay); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sns/TbSnsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sns/TbSnsNode.java index f02434d4ee..e24c73f9d3 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sns/TbSnsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sns/TbSnsNode.java @@ -15,6 +15,7 @@ */ package org.thingsboard.rule.engine.aws.sns; +import com.amazonaws.ClientConfiguration; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AWSStaticCredentialsProvider; import com.amazonaws.auth.BasicAWSCredentials; @@ -26,10 +27,10 @@ import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.rule.engine.external.TbAbstractExternalNode; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -51,7 +52,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; configDirective = "tbExternalNodeSnsConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ij48cGF0aCBkPSJNMTMuMjMgMTAuNTZWMTBjLTEuOTQgMC0zLjk5LjM5LTMuOTkgMi42NyAwIDEuMTYuNjEgMS45NSAxLjYzIDEuOTUuNzYgMCAxLjQzLS40NyAxLjg2LTEuMjIuNTItLjkzLjUtMS44LjUtMi44NG0yLjcgNi41M2MtLjE4LjE2LS40My4xNy0uNjMuMDYtLjg5LS43NC0xLjA1LTEuMDgtMS41NC0xLjc5LTEuNDcgMS41LTIuNTEgMS45NS00LjQyIDEuOTUtMi4yNSAwLTQuMDEtMS4zOS00LjAxLTQuMTcgMC0yLjE4IDEuMTctMy42NCAyLjg2LTQuMzggMS40Ni0uNjQgMy40OS0uNzYgNS4wNC0uOTNWNy41YzAtLjY2LjA1LTEuNDEtLjMzLTEuOTYtLjMyLS40OS0uOTUtLjctMS41LS43LTEuMDIgMC0xLjkzLjUzLTIuMTUgMS42MS0uMDUuMjQtLjI1LjQ4LS40Ny40OWwtMi42LS4yOGMtLjIyLS4wNS0uNDYtLjIyLS40LS41Ni42LTMuMTUgMy40NS00LjEgNi00LjEgMS4zIDAgMyAuMzUgNC4wMyAxLjMzQzE3LjExIDQuNTUgMTcgNi4xOCAxNyA3Ljk1djQuMTdjMCAxLjI1LjUgMS44MSAxIDIuNDguMTcuMjUuMjEuNTQgMCAuNzFsLTIuMDYgMS43OGgtLjAxIj48L3BhdGg+PHBhdGggZD0iTTIwLjE2IDE5LjU0QzE4IDIxLjE0IDE0LjgyIDIyIDEyLjEgMjJjLTMuODEgMC03LjI1LTEuNDEtOS44NS0zLjc2LS4yLS4xOC0uMDItLjQzLjI1LS4yOSAyLjc4IDEuNjMgNi4yNSAyLjYxIDkuODMgMi42MSAyLjQxIDAgNS4wNy0uNSA3LjUxLTEuNTMuMzctLjE2LjY2LjI0LjMyLjUxIj48L3BhdGg+PHBhdGggZD0iTTIxLjA3IDE4LjVjLS4yOC0uMzYtMS44NS0uMTctMi41Ny0uMDgtLjE5LjAyLS4yMi0uMTYtLjAzLS4zIDEuMjQtLjg4IDMuMjktLjYyIDMuNTMtLjMzLjI0LjMtLjA3IDIuMzUtMS4yNCAzLjMyLS4xOC4xNi0uMzUuMDctLjI2LS4xMS4yNi0uNjcuODUtMi4xNC41Ny0yLjV6Ij48L3BhdGg+PC9zdmc+" ) -public class TbSnsNode implements TbNode { +public class TbSnsNode extends TbAbstractExternalNode { private static final String MESSAGE_ID = "messageId"; private static final String REQUEST_ID = "requestId"; @@ -62,6 +63,7 @@ public class TbSnsNode implements TbNode { @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { + super.init(ctx); this.config = TbNodeUtils.convert(configuration, TbSnsNodeConfiguration.class); AWSCredentials awsCredentials = new BasicAWSCredentials(this.config.getAccessKeyId(), this.config.getSecretAccessKey()); AWSStaticCredentialsProvider credProvider = new AWSStaticCredentialsProvider(awsCredentials); @@ -69,6 +71,9 @@ public class TbSnsNode implements TbNode { this.snsClient = AmazonSNSClient.builder() .withCredentials(credProvider) .withRegion(this.config.getRegion()) + .withClientConfiguration(new ClientConfiguration() + .withConnectionTimeout(10000) + .withRequestTimeout(5000)) .build(); } catch (Exception e) { throw new TbNodeException(e); @@ -77,9 +82,10 @@ public class TbSnsNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException { - withCallback(publishMessageAsync(ctx, msg), - ctx::tellSuccess, - t -> ctx.tellFailure(processException(ctx, msg, t), t)); + var tbMsg = ackIfNeeded(ctx, msg); + withCallback(publishMessageAsync(ctx, tbMsg), + m -> tellSuccess(ctx, m), + t -> tellFailure(ctx, processException(ctx, tbMsg, t), t)); } private ListenableFuture publishMessageAsync(TbContext ctx, TbMsg msg) { @@ -99,13 +105,13 @@ public class TbSnsNode implements TbNode { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(MESSAGE_ID, result.getMessageId()); metaData.putValue(REQUEST_ID, result.getSdkResponseMetadata().getRequestId()); - return ctx.transformMsg(origMsg, origMsg.getType(), origMsg.getOriginator(), metaData, origMsg.getData()); + return TbMsg.transformMsgMetadata(origMsg, metaData); } private TbMsg processException(TbContext ctx, TbMsg origMsg, Throwable t) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(ERROR, t.getClass() + ": " + t.getMessage()); - return ctx.transformMsg(origMsg, origMsg.getType(), origMsg.getOriginator(), metaData, origMsg.getData()); + return TbMsg.transformMsgMetadata(origMsg, metaData); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNode.java index 7386d30c7c..34072fbb8a 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNode.java @@ -15,6 +15,7 @@ */ package org.thingsboard.rule.engine.aws.sqs; +import com.amazonaws.ClientConfiguration; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AWSStaticCredentialsProvider; import com.amazonaws.auth.BasicAWSCredentials; @@ -27,10 +28,10 @@ import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.rule.engine.external.TbAbstractExternalNode; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; @@ -55,7 +56,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; configDirective = "tbExternalNodeSqsConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ij48cGF0aCBkPSJNMTMuMjMgMTAuNTZWMTBjLTEuOTQgMC0zLjk5LjM5LTMuOTkgMi42NyAwIDEuMTYuNjEgMS45NSAxLjYzIDEuOTUuNzYgMCAxLjQzLS40NyAxLjg2LTEuMjIuNTItLjkzLjUtMS44LjUtMi44NG0yLjcgNi41M2MtLjE4LjE2LS40My4xNy0uNjMuMDYtLjg5LS43NC0xLjA1LTEuMDgtMS41NC0xLjc5LTEuNDcgMS41LTIuNTEgMS45NS00LjQyIDEuOTUtMi4yNSAwLTQuMDEtMS4zOS00LjAxLTQuMTcgMC0yLjE4IDEuMTctMy42NCAyLjg2LTQuMzggMS40Ni0uNjQgMy40OS0uNzYgNS4wNC0uOTNWNy41YzAtLjY2LjA1LTEuNDEtLjMzLTEuOTYtLjMyLS40OS0uOTUtLjctMS41LS43LTEuMDIgMC0xLjkzLjUzLTIuMTUgMS42MS0uMDUuMjQtLjI1LjQ4LS40Ny40OWwtMi42LS4yOGMtLjIyLS4wNS0uNDYtLjIyLS40LS41Ni42LTMuMTUgMy40NS00LjEgNi00LjEgMS4zIDAgMyAuMzUgNC4wMyAxLjMzQzE3LjExIDQuNTUgMTcgNi4xOCAxNyA3Ljk1djQuMTdjMCAxLjI1LjUgMS44MSAxIDIuNDguMTcuMjUuMjEuNTQgMCAuNzFsLTIuMDYgMS43OGgtLjAxIj48L3BhdGg+PHBhdGggZD0iTTIwLjE2IDE5LjU0QzE4IDIxLjE0IDE0LjgyIDIyIDEyLjEgMjJjLTMuODEgMC03LjI1LTEuNDEtOS44NS0zLjc2LS4yLS4xOC0uMDItLjQzLjI1LS4yOSAyLjc4IDEuNjMgNi4yNSAyLjYxIDkuODMgMi42MSAyLjQxIDAgNS4wNy0uNSA3LjUxLTEuNTMuMzctLjE2LjY2LjI0LjMyLjUxIj48L3BhdGg+PHBhdGggZD0iTTIxLjA3IDE4LjVjLS4yOC0uMzYtMS44NS0uMTctMi41Ny0uMDgtLjE5LjAyLS4yMi0uMTYtLjAzLS4zIDEuMjQtLjg4IDMuMjktLjYyIDMuNTMtLjMzLjI0LjMtLjA3IDIuMzUtMS4yNCAzLjMyLS4xOC4xNi0uMzUuMDctLjI2LS4xMS4yNi0uNjcuODUtMi4xNC41Ny0yLjV6Ij48L3BhdGg+PC9zdmc+" ) -public class TbSqsNode implements TbNode { +public class TbSqsNode extends TbAbstractExternalNode { private static final String MESSAGE_ID = "messageId"; private static final String REQUEST_ID = "requestId"; @@ -69,6 +70,7 @@ public class TbSqsNode implements TbNode { @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { + super.init(ctx); this.config = TbNodeUtils.convert(configuration, TbSqsNodeConfiguration.class); AWSCredentials awsCredentials = new BasicAWSCredentials(this.config.getAccessKeyId(), this.config.getSecretAccessKey()); AWSStaticCredentialsProvider credProvider = new AWSStaticCredentialsProvider(awsCredentials); @@ -76,6 +78,9 @@ public class TbSqsNode implements TbNode { this.sqsClient = AmazonSQSClientBuilder.standard() .withCredentials(credProvider) .withRegion(this.config.getRegion()) + .withClientConfiguration(new ClientConfiguration() + .withConnectionTimeout(10000) + .withRequestTimeout(5000)) .build(); } catch (Exception e) { throw new TbNodeException(e); @@ -84,16 +89,17 @@ public class TbSqsNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { - withCallback(publishMessageAsync(ctx, msg), - ctx::tellSuccess, - t -> ctx.tellFailure(processException(ctx, msg, t), t)); + var tbMsg = ackIfNeeded(ctx, msg); + withCallback(publishMessageAsync(ctx, tbMsg), + m -> tellSuccess(ctx, m), + t -> tellFailure(ctx, processException(tbMsg, t), t)); } private ListenableFuture publishMessageAsync(TbContext ctx, TbMsg msg) { - return ctx.getExternalCallExecutor().executeAsync(() -> publishMessage(ctx, msg)); + return ctx.getExternalCallExecutor().executeAsync(() -> publishMessage(msg)); } - private TbMsg publishMessage(TbContext ctx, TbMsg msg) { + private TbMsg publishMessage(TbMsg msg) { String queueUrl = TbNodeUtils.processPattern(this.config.getQueueUrlPattern(), msg); SendMessageRequest sendMsgRequest = new SendMessageRequest(); sendMsgRequest.withQueueUrl(queueUrl); @@ -112,10 +118,10 @@ public class TbSqsNode implements TbNode { sendMsgRequest.withMessageGroupId(msg.getOriginator().toString()); } SendMessageResult result = this.sqsClient.sendMessage(sendMsgRequest); - return processSendMessageResult(ctx, msg, result); + return processSendMessageResult(msg, result); } - private TbMsg processSendMessageResult(TbContext ctx, TbMsg origMsg, SendMessageResult result) { + private TbMsg processSendMessageResult(TbMsg origMsg, SendMessageResult result) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(MESSAGE_ID, result.getMessageId()); metaData.putValue(REQUEST_ID, result.getSdkResponseMetadata().getRequestId()); @@ -128,13 +134,13 @@ public class TbSqsNode implements TbNode { if (!StringUtils.isEmpty(result.getSequenceNumber())) { metaData.putValue(SEQUENCE_NUMBER, result.getSequenceNumber()); } - return ctx.transformMsg(origMsg, origMsg.getType(), origMsg.getOriginator(), metaData, origMsg.getData()); + return TbMsg.transformMsgMetadata(origMsg, metaData); } - private TbMsg processException(TbContext ctx, TbMsg origMsg, Throwable t) { + private TbMsg processException(TbMsg origMsg, Throwable t) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(ERROR, t.getClass() + ": " + t.getMessage()); - return ctx.transformMsg(origMsg, origMsg.getType(), origMsg.getOriginator(), metaData, origMsg.getData()); + return TbMsg.transformMsgMetadata(origMsg, metaData); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/credentials/CertPemCredentials.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/credentials/CertPemCredentials.java index 0137887aaf..3824e71459 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/credentials/CertPemCredentials.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/credentials/CertPemCredentials.java @@ -20,68 +20,34 @@ import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import lombok.Data; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.codec.binary.Base64; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.openssl.PEMDecryptorProvider; -import org.bouncycastle.openssl.PEMEncryptedKeyPair; -import org.bouncycastle.openssl.PEMKeyPair; -import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; -import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder; -import org.bouncycastle.util.encoders.Hex; +import org.thingsboard.common.util.SslUtil; import org.thingsboard.server.common.data.StringUtils; -import javax.crypto.Cipher; -import javax.crypto.EncryptedPrivateKeyInfo; -import javax.crypto.SecretKey; -import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.PBEKeySpec; -import javax.crypto.spec.SecretKeySpec; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.TrustManagerFactory; -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.security.AlgorithmParameters; -import java.security.Key; -import java.security.KeyFactory; -import java.security.KeyPair; import java.security.KeyStore; -import java.security.MessageDigest; import java.security.PrivateKey; -import java.security.Security; +import java.security.cert.CertPath; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; -import java.security.spec.KeySpec; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.RSAPrivateCrtKeySpec; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.stream.Collectors; @Data @Slf4j @JsonIgnoreProperties(ignoreUnknown = true) public class CertPemCredentials implements ClientCredentials { - private static final String TLS_VERSION = "TLSv1.2"; + + public static final String PRIVATE_KEY_ALIAS = "private-key"; + public static final String X_509 = "X.509"; + public static final String CERT_ALIAS_PREFIX = "cert-"; + public static final String CA_CERT_CERT_ALIAS_PREFIX = "caCert-cert-"; protected String caCert; private String cert; private String privateKey; - private String password; - - static final String OPENSSL_ENCRYPTED_RSA_PRIVATEKEY_REGEX = "\\s*" - + "-----BEGIN RSA PRIVATE KEY-----" + "\\s*" - + "Proc-Type: 4,ENCRYPTED" + "\\s*" - + "DEK-Info:" + "\\s*([^\\s]+)" + "\\s+" - + "([\\s\\S]*)" - + "-----END RSA PRIVATE KEY-----" + "\\s*"; - - static final Pattern OPENSSL_ENCRYPTED_RSA_PRIVATEKEY_PATTERN = Pattern.compile(OPENSSL_ENCRYPTED_RSA_PRIVATEKEY_REGEX); + private String password = ""; @Override public CredentialsType getType() { @@ -91,7 +57,6 @@ public class CertPemCredentials implements ClientCredentials { @Override public SslContext initSslContext() { try { - Security.addProvider(new BouncyCastleProvider()); SslContextBuilder builder = SslContextBuilder.forClient(); if (StringUtils.hasLength(caCert)) { builder.trustManager(createAndInitTrustManagerFactory()); @@ -106,51 +71,13 @@ public class CertPemCredentials implements ClientCredentials { } } - private KeyManagerFactory createAndInitKeyManagerFactory() throws Exception { - List certHolders = readCertFile(cert); - Object keyObject = readPrivateKeyFile(privateKey); - char[] passwordCharArray = "".toCharArray(); - if (!StringUtils.isEmpty(password)) { - passwordCharArray = password.toCharArray(); - } - - JcaPEMKeyConverter keyConverter = new JcaPEMKeyConverter().setProvider("BC"); - - PrivateKey privateKey; - if (keyObject instanceof PEMEncryptedKeyPair) { - PEMDecryptorProvider provider = new JcePEMDecryptorProviderBuilder().build(passwordCharArray); - KeyPair key = keyConverter.getKeyPair(((PEMEncryptedKeyPair) keyObject).decryptKeyPair(provider)); - privateKey = key.getPrivate(); - } else if (keyObject instanceof PEMKeyPair) { - KeyPair key = keyConverter.getKeyPair((PEMKeyPair) keyObject); - privateKey = key.getPrivate(); - } else if (keyObject instanceof PrivateKey) { - privateKey = (PrivateKey) keyObject; - } else { - throw new RuntimeException("Unable to get private key from object: " + keyObject.getClass()); - } - - KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); - clientKeyStore.load(null, null); - for (X509Certificate certHolder : certHolders) { - clientKeyStore.setCertificateEntry("cert-" + certHolder.getSubjectDN().getName(), certHolder); - } - clientKeyStore.setKeyEntry("private-key", - privateKey, - passwordCharArray, - certHolders.toArray(new Certificate[]{})); - KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - keyManagerFactory.init(clientKeyStore, passwordCharArray); - return keyManagerFactory; - } - protected TrustManagerFactory createAndInitTrustManagerFactory() throws Exception { - List caCertHolders = readCertFile(caCert); + List caCerts = SslUtil.readCertFile(caCert); KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); caKeyStore.load(null, null); - for (X509Certificate caCertHolder : caCertHolders) { - caKeyStore.setCertificateEntry("caCert-cert-" + caCertHolder.getSubjectDN().getName(), caCertHolder); + for (X509Certificate caCert : caCerts) { + caKeyStore.setCertificateEntry(CA_CERT_CERT_ALIAS_PREFIX + caCert.getSubjectDN().getName(), caCert); } TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); @@ -158,170 +85,31 @@ public class CertPemCredentials implements ClientCredentials { return trustManagerFactory; } - List readCertFile(String fileContent) throws Exception { - if (fileContent == null || fileContent.trim().isEmpty()) { - return Collections.emptyList(); - } - - List certificates = new ArrayList<>(); - String[] pems = fileContent.trim().split("-----END CERTIFICATE-----"); - for (String pem : pems) { - if (pem.trim().isEmpty()) { - continue; - } - pem = pem.replace("-----BEGIN CERTIFICATE-----", "") - .replace("-----END CERTIFICATE-----", "") - .replaceAll("\\s", ""); - byte[] decoded = Base64.decodeBase64(pem); - CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); - try (InputStream inStream = new ByteArrayInputStream(decoded)) { - certificates.add((X509Certificate) certFactory.generateCertificate(inStream)); - } - } - return certificates; - } - - private PrivateKey readPrivateKeyFile(String fileContent) throws Exception { - PrivateKey privateKey = null; - if (fileContent != null && !fileContent.isEmpty()) { - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - KeySpec keySpec = getKeySpec(fileContent); - privateKey = keyFactory.generatePrivate(keySpec); - } - return privateKey; + private KeyManagerFactory createAndInitKeyManagerFactory() throws Exception { + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(loadKeyStore(), password.toCharArray()); + return kmf; } - private KeySpec getKeySpec(String encodedKey) throws Exception { - KeySpec keySpec = null; - Matcher matcher = OPENSSL_ENCRYPTED_RSA_PRIVATEKEY_PATTERN.matcher(encodedKey); - if (matcher.matches()) { - String encryptionDetails = matcher.group(1).trim(); - String encryptedKey = matcher.group(2).replaceAll("\\s", ""); - byte[] encryptedBinaryKey = java.util.Base64.getDecoder().decode(encryptedKey); - String[] encryptionDetailsParts = encryptionDetails.split(","); - if (encryptionDetailsParts.length == 2) { - String encryptionAlgorithm = encryptionDetailsParts[0]; - String encryptedAlgorithmParams = encryptionDetailsParts[1]; - byte[] pw = password.getBytes(); - byte[] iv = Hex.decode(encryptedAlgorithmParams); - - MessageDigest digest = MessageDigest.getInstance("MD5"); - digest.update(pw); - digest.update(iv, 0, 8); - - byte[] round1Digest = digest.digest(); - digest.update(round1Digest); - digest.update(pw); - digest.update(iv, 0, 8); - - byte[] round2Digest = digest.digest(); - Cipher cipher = null; - SecretKey secretKey = null; - byte[] key = null; + protected KeyStore loadKeyStore() throws Exception { + List certificates = SslUtil.readCertFile(this.cert); + PrivateKey privateKey = SslUtil.readPrivateKey(this.privateKey, password); - switch(encryptionAlgorithm) { - case "AES-256-CBC": - cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - key = new byte[32]; - System.arraycopy(round1Digest, 0, key, 0, 16); - System.arraycopy(round2Digest, 0, key, 16, 16); - secretKey = new SecretKeySpec(key, "AES"); - break; - case "AES-192-CBC": - cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - key = new byte[24]; - System.arraycopy(round1Digest, 0, key, 0, 16); - System.arraycopy(round2Digest, 0, key, 16, 8); - secretKey = new SecretKeySpec(key, "AES"); - break; - case "AES-128-CBC": - cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - key = new byte[16]; - System.arraycopy(round1Digest, 0, key, 0, 16); - secretKey = new SecretKeySpec(key, "AES"); - break; - case "DES-EDE3-CBC": - cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding"); - key = new byte[24]; - System.arraycopy(round1Digest, 0, key, 0, 16); - System.arraycopy(round2Digest, 0, key, 16, 8); - secretKey = new SecretKeySpec(key, "DESede"); - break; - case "DES-CBC": - cipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); - key = new byte[8]; - System.arraycopy(round1Digest, 0, key, 0, 8); - secretKey = new SecretKeySpec(key, "DES"); - break; - } - if (cipher != null) { - cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv)); - byte[] pkcs1 = cipher.doFinal(encryptedBinaryKey); - keySpec = decodeRSAPrivatePKCS1(pkcs1); - } else { - throw new RuntimeException("Unknown Encryption algorithm!"); - } - } else { - throw new RuntimeException("Wrong encryption details!"); - } - } else { - encodedKey = encodedKey.replaceAll(".*BEGIN.*PRIVATE KEY.*", "") - .replaceAll(".*END.*PRIVATE KEY.*", "") - .replaceAll("\\s", ""); - byte[] decoded = Base64.decodeBase64(encodedKey); - if (password == null || password.isEmpty()) { - keySpec = new PKCS8EncodedKeySpec(decoded); - } else { - PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray()); - - EncryptedPrivateKeyInfo privateKeyInfo = new EncryptedPrivateKeyInfo(decoded); - String algorithmName = privateKeyInfo.getAlgName(); - Cipher cipher = Cipher.getInstance(algorithmName); - SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(algorithmName); - - Key pbeKey = secretKeyFactory.generateSecret(pbeKeySpec); - AlgorithmParameters algParams = privateKeyInfo.getAlgParameters(); - cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams); - keySpec = privateKeyInfo.getKeySpec(cipher); - } + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null); + List unique = certificates.stream().distinct().collect(Collectors.toList()); + for (X509Certificate cert : unique) { + keyStore.setCertificateEntry(CERT_ALIAS_PREFIX + cert.getSubjectDN().getName(), cert); } - return keySpec; - } - - private static BigInteger derint(ByteBuffer input) { - int len = der(input, 0x02); - byte[] value = new byte[len]; - input.get(value); - return new BigInteger(+1, value); - } - private static int der(ByteBuffer input, int exp) { - int tag = input.get() & 0xFF; - if (tag != exp) throw new IllegalArgumentException("Unexpected tag"); - int n = input.get() & 0xFF; - if (n < 128) return n; - n &= 0x7F; - if ((n < 1) || (n > 2)) throw new IllegalArgumentException("Invalid length"); - int len = 0; - while (n-- > 0) { - len <<= 8; - len |= input.get() & 0xFF; + if (privateKey != null) { + CertificateFactory factory = CertificateFactory.getInstance(X_509); + CertPath certPath = factory.generateCertPath(certificates); + List path = certPath.getCertificates(); + Certificate[] x509Certificates = path.toArray(new Certificate[0]); + keyStore.setKeyEntry(PRIVATE_KEY_ALIAS, privateKey, password.toCharArray(), x509Certificates); } - return len; + return keyStore; } - static RSAPrivateCrtKeySpec decodeRSAPrivatePKCS1(byte[] encoded) { - ByteBuffer input = ByteBuffer.wrap(encoded); - if (der(input, 0x30) != input.remaining()) throw new IllegalArgumentException("Excess data"); - if (!BigInteger.ZERO.equals(derint(input))) throw new IllegalArgumentException("Unsupported version"); - BigInteger n = derint(input); - BigInteger e = derint(input); - BigInteger d = derint(input); - BigInteger p = derint(input); - BigInteger q = derint(input); - BigInteger ep = derint(input); - BigInteger eq = derint(input); - BigInteger c = derint(input); - return new RSAPrivateCrtKeySpec(n, e, d, p, q, ep, eq, c); - } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java index 51effe02ef..b31c98bc0f 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java @@ -28,8 +28,11 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.script.ScriptLanguage; import org.thingsboard.server.common.msg.TbMsg; @@ -41,7 +44,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import static org.thingsboard.common.util.DonAsynchron.withCallback; -import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; @Slf4j @RuleNode( @@ -58,8 +60,6 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; public class TbMsgGeneratorNode implements TbNode { - private static final String TB_MSG_GENERATOR_NODE_MSG = "TbMsgGeneratorNodeMsg"; - private TbMsgGeneratorNodeConfiguration config; private ScriptEngine scriptEngine; private long delay; @@ -97,7 +97,7 @@ public class TbMsgGeneratorNode implements TbNode { if (initialized.compareAndSet(false, true)) { this.scriptEngine = ctx.createScriptEngine(config.getScriptLang(), ScriptLanguage.TBEL.equals(config.getScriptLang()) ? config.getTbelScript() : config.getJsScript(), "prevMsg", "prevMetadata", "prevMsgType"); - scheduleTickMsg(ctx); + scheduleTickMsg(ctx, null); } } else if (initialized.compareAndSet(true, false)) { destroy(); @@ -107,14 +107,14 @@ public class TbMsgGeneratorNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { log.trace("onMsg, config {}, msg {}", config, msg); - if (initialized.get() && msg.getType().equals(TB_MSG_GENERATOR_NODE_MSG) && msg.getId().equals(nextTickId)) { + if (initialized.get() && msg.isTypeOf(TbMsgType.GENERATOR_NODE_SELF_MSG) && msg.getId().equals(nextTickId)) { TbStopWatch sw = TbStopWatch.create(); withCallback(generate(ctx, msg), m -> { log.trace("onMsg onSuccess callback, took {}ms, config {}, msg {}", sw.stopAndGetTotalTimeMillis(), config, msg); if (initialized.get() && (config.getMsgCount() == TbMsgGeneratorNodeConfiguration.UNLIMITED_MSG_COUNT || currentMsgCount < config.getMsgCount())) { - ctx.enqueueForTellNext(m, SUCCESS); - scheduleTickMsg(ctx); + ctx.enqueueForTellNext(m, TbNodeConnectionType.SUCCESS); + scheduleTickMsg(ctx, msg); currentMsgCount++; } }, @@ -122,14 +122,14 @@ public class TbMsgGeneratorNode implements TbNode { log.trace("onMsg onFailure callback, took {}ms, config {}, msg {}", sw.stopAndGetTotalTimeMillis(), config, msg, t); if (initialized.get() && (config.getMsgCount() == TbMsgGeneratorNodeConfiguration.UNLIMITED_MSG_COUNT || currentMsgCount < config.getMsgCount())) { ctx.tellFailure(msg, t); - scheduleTickMsg(ctx); + scheduleTickMsg(ctx, msg); currentMsgCount++; } }); } } - private void scheduleTickMsg(TbContext ctx) { + private void scheduleTickMsg(TbContext ctx, TbMsg msg) { log.trace("scheduleTickMsg, config {}", config); long curTs = System.currentTimeMillis(); if (lastScheduledTs == 0L) { @@ -137,7 +137,8 @@ public class TbMsgGeneratorNode implements TbNode { } lastScheduledTs = lastScheduledTs + delay; long curDelay = Math.max(0L, (lastScheduledTs - curTs)); - TbMsg tickMsg = ctx.newMsg(config.getQueueName(), TB_MSG_GENERATOR_NODE_MSG, ctx.getSelfId(), new TbMsgMetaData(), ""); + TbMsg tickMsg = ctx.newMsg(config.getQueueName(), TbMsgType.GENERATOR_NODE_SELF_MSG, ctx.getSelfId(), + getCustomerIdFromMsg(msg), TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); nextTickId = tickMsg.getId(); ctx.tellSelf(tickMsg, curDelay); } @@ -145,7 +146,7 @@ public class TbMsgGeneratorNode implements TbNode { private ListenableFuture generate(TbContext ctx, TbMsg msg) { log.trace("generate, config {}", config); if (prevMsg == null) { - prevMsg = ctx.newMsg(config.getQueueName(), "", originatorId, msg.getCustomerId(), new TbMsgMetaData(), "{}"); + prevMsg = ctx.newMsg(config.getQueueName(), TbMsg.EMPTY_STRING, originatorId, msg.getCustomerId(), TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); } if (initialized.get()) { ctx.logJsEvalRequest(); @@ -160,6 +161,10 @@ public class TbMsgGeneratorNode implements TbNode { } + private CustomerId getCustomerIdFromMsg(TbMsg msg) { + return msg != null ? msg.getCustomerId() : null; + } + @Override public void destroy() { log.trace("destroy, config {}", config); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java index 6890aa6bf4..043cc7793f 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java @@ -24,9 +24,10 @@ import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; -import org.thingsboard.rule.engine.api.TbRelationTypes; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.TbMsg; @@ -48,11 +49,10 @@ import java.util.concurrent.TimeUnit; configClazz = TbMsgDeduplicationNodeConfiguration.class, nodeDescription = "Deduplicate messages within the same originator entity for a configurable period " + "based on a specified deduplication strategy.", - nodeDetails = "Rule node allows you to select one of the following strategy to deduplicate messages:

" + - "FIRST - return first message that arrived during deduplication period.

" + - "LAST - return last message that arrived during deduplication period.

" + - "ALL - return all messages as a single JSON array message. " + - "Where each element represents object with msg and metadata inner properties.

", + nodeDetails = "Deduplication strategies:
  • FIRST - return first message that arrived during deduplication period.
  • " + + "
  • LAST - return last message that arrived during deduplication period.
  • " + + "
  • ALL - return all messages as a single JSON array message. " + + "Where each element represents object with msg and metadata inner properties.
", icon = "content_copy", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeMsgDeduplicationConfig" @@ -60,10 +60,7 @@ import java.util.concurrent.TimeUnit; @Slf4j public class TbMsgDeduplicationNode implements TbNode { - private static final String TB_MSG_DEDUPLICATION_TIMEOUT_MSG = "TbMsgDeduplicationNodeMsg"; public static final int TB_MSG_DEDUPLICATION_RETRY_DELAY = 10; - private static final String EMPTY_DATA = ""; - private static final TbMsgMetaData EMPTY_META_DATA = new TbMsgMetaData(); private TbMsgDeduplicationNodeConfiguration config; @@ -82,7 +79,7 @@ public class TbMsgDeduplicationNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException { - if (TB_MSG_DEDUPLICATION_TIMEOUT_MSG.equals(msg.getType())) { + if (msg.isTypeOf(TbMsgType.DEDUPLICATION_TIMEOUT_SELF_MSG)) { processDeduplication(ctx, msg.getOriginator()); } else { processOnRegularMsg(ctx, msg); @@ -196,7 +193,7 @@ public class TbMsgDeduplicationNode implements TbNode { private void enqueueForTellNextWithRetry(TbContext ctx, TbMsg msg, int retryAttempt) { if (config.getMaxRetries() > retryAttempt) { - ctx.enqueueForTellNext(msg, TbRelationTypes.SUCCESS, + ctx.enqueueForTellNext(msg, TbNodeConnectionType.SUCCESS, () -> { log.trace("[{}][{}][{}] Successfully enqueue deduplication result message!", ctx.getSelfId(), msg.getOriginator(), retryAttempt); }, @@ -210,7 +207,7 @@ public class TbMsgDeduplicationNode implements TbNode { } private void scheduleTickMsg(TbContext ctx, EntityId deduplicationId) { - ctx.tellSelf(ctx.newMsg(null, TB_MSG_DEDUPLICATION_TIMEOUT_MSG, deduplicationId, EMPTY_META_DATA, EMPTY_DATA), deduplicationInterval + 1); + ctx.tellSelf(ctx.newMsg(null, TbMsgType.DEDUPLICATION_TIMEOUT_SELF_MSG, deduplicationId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING), deduplicationInterval + 1); } private String getMergedData(List msgs) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java index 1f18aea86e..5414cc2bbe 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java @@ -23,6 +23,8 @@ import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -32,8 +34,6 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; -import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; - @Slf4j @RuleNode( type = ComponentType.ACTION, @@ -50,8 +50,6 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; ) public class TbMsgDelayNode implements TbNode { - private static final String TB_MSG_DELAY_NODE_MSG = "TbMsgDelayNodeMsg"; - private TbMsgDelayNodeConfiguration config; private Map pendingMsgs; @@ -63,7 +61,7 @@ public class TbMsgDelayNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { - if (msg.getType().equals(TB_MSG_DELAY_NODE_MSG)) { + if (msg.isTypeOf(TbMsgType.DELAY_TIMEOUT_SELF_MSG)) { TbMsg pendingMsg = pendingMsgs.remove(UUID.fromString(msg.getData())); if (pendingMsg != null) { ctx.enqueueForTellNext( @@ -75,13 +73,13 @@ public class TbMsgDelayNode implements TbNode { pendingMsg.getMetaData(), pendingMsg.getData() ), - SUCCESS + TbNodeConnectionType.SUCCESS ); } } else { if (pendingMsgs.size() < config.getMaxPendingMsgs()) { pendingMsgs.put(msg.getId(), msg); - TbMsg tickMsg = ctx.newMsg(null, TB_MSG_DELAY_NODE_MSG, ctx.getSelfId(), msg.getCustomerId(), new TbMsgMetaData(), msg.getId().toString()); + TbMsg tickMsg = ctx.newMsg(null, TbMsgType.DELAY_TIMEOUT_SELF_MSG, ctx.getSelfId(), msg.getCustomerId(), TbMsgMetaData.EMPTY, msg.getId().toString()); ctx.tellSelf(tickMsg, getDelay(msg)); ctx.ack(msg); } else { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/AbstractTbMsgPushNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/AbstractTbMsgPushNode.java index 318b38aa39..7888922dc6 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/AbstractTbMsgPushNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/AbstractTbMsgPushNode.java @@ -30,13 +30,23 @@ import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.TbMsg; -import org.thingsboard.server.common.msg.session.SessionMsgType; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; +import static org.thingsboard.server.common.data.msg.TbMsgType.ACTIVITY_EVENT; +import static org.thingsboard.server.common.data.msg.TbMsgType.ALARM; +import static org.thingsboard.server.common.data.msg.TbMsgType.ATTRIBUTES_DELETED; +import static org.thingsboard.server.common.data.msg.TbMsgType.ATTRIBUTES_UPDATED; +import static org.thingsboard.server.common.data.msg.TbMsgType.CONNECT_EVENT; +import static org.thingsboard.server.common.data.msg.TbMsgType.DISCONNECT_EVENT; +import static org.thingsboard.server.common.data.msg.TbMsgType.INACTIVITY_EVENT; +import static org.thingsboard.server.common.data.msg.TbMsgType.POST_ATTRIBUTES_REQUEST; +import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_REQUEST; +import static org.thingsboard.server.common.data.msg.TbMsgType.TIMESERIES_UPDATED; + @Slf4j public abstract class AbstractTbMsgPushNode implements TbNode { @@ -57,7 +67,7 @@ public abstract class AbstractTbMsgPushNode metadata = msg.getMetaData().getData(); - EdgeEventActionType actionType = getEdgeEventActionTypeByMsgType(msgType, metadata); + EdgeEventActionType actionType = getEdgeEventActionTypeByMsgType(msg); Map entityBody = new HashMap<>(); JsonNode dataJson = JacksonUtil.toJsonNode(msg.getData()); switch (actionType) { @@ -148,45 +157,31 @@ public abstract class AbstractTbMsgPushNode metadata) { + protected EdgeEventActionType getEdgeEventActionTypeByMsgType(TbMsg msg) { EdgeEventActionType actionType; - if (SessionMsgType.POST_TELEMETRY_REQUEST.name().equals(msgType) - || DataConstants.TIMESERIES_UPDATED.equals(msgType)) { + if (msg.isTypeOneOf(POST_TELEMETRY_REQUEST, TIMESERIES_UPDATED)) { actionType = EdgeEventActionType.TIMESERIES_UPDATED; - } else if (DataConstants.ATTRIBUTES_UPDATED.equals(msgType)) { + } else if (msg.isTypeOf(ATTRIBUTES_UPDATED)) { actionType = EdgeEventActionType.ATTRIBUTES_UPDATED; - } else if (SessionMsgType.POST_ATTRIBUTES_REQUEST.name().equals(msgType)) { + } else if (msg.isTypeOf(POST_ATTRIBUTES_REQUEST)) { actionType = EdgeEventActionType.POST_ATTRIBUTES; - } else if (DataConstants.ATTRIBUTES_DELETED.equals(msgType)) { + } else if (msg.isTypeOf(ATTRIBUTES_DELETED)) { actionType = EdgeEventActionType.ATTRIBUTES_DELETED; - } else if (DataConstants.CONNECT_EVENT.equals(msgType) - || DataConstants.DISCONNECT_EVENT.equals(msgType) - || DataConstants.ACTIVITY_EVENT.equals(msgType) - || DataConstants.INACTIVITY_EVENT.equals(msgType)) { - String scope = metadata.get(SCOPE); - if ( StringUtils.isEmpty(scope)) { - actionType = EdgeEventActionType.TIMESERIES_UPDATED; - } else { - actionType = EdgeEventActionType.ATTRIBUTES_UPDATED; - } + } else if (msg.isTypeOneOf(CONNECT_EVENT, DISCONNECT_EVENT, ACTIVITY_EVENT, INACTIVITY_EVENT)) { + String scope = msg.getMetaData().getValue(SCOPE); + actionType = StringUtils.isEmpty(scope) ? + EdgeEventActionType.TIMESERIES_UPDATED : EdgeEventActionType.ATTRIBUTES_UPDATED; } else { - log.warn("Unsupported msg type [{}]", msgType); - throw new IllegalArgumentException("Unsupported msg type: " + msgType); + String type = msg.getType(); + log.warn("Unsupported msg type [{}]", type); + throw new IllegalArgumentException("Unsupported msg type: " + type); } return actionType; } - protected boolean isSupportedMsgType(String msgType) { - return SessionMsgType.POST_TELEMETRY_REQUEST.name().equals(msgType) - || SessionMsgType.POST_ATTRIBUTES_REQUEST.name().equals(msgType) - || DataConstants.ATTRIBUTES_UPDATED.equals(msgType) - || DataConstants.ATTRIBUTES_DELETED.equals(msgType) - || DataConstants.TIMESERIES_UPDATED.equals(msgType) - || DataConstants.ALARM.equals(msgType) - || DataConstants.CONNECT_EVENT.equals(msgType) - || DataConstants.DISCONNECT_EVENT.equals(msgType) - || DataConstants.ACTIVITY_EVENT.equals(msgType) - || DataConstants.INACTIVITY_EVENT.equals(msgType); + protected boolean isSupportedMsgType(TbMsg msg) { + return msg.isTypeOneOf(POST_TELEMETRY_REQUEST, POST_ATTRIBUTES_REQUEST, ATTRIBUTES_UPDATED, + ATTRIBUTES_DELETED, TIMESERIES_UPDATED, ALARM, CONNECT_EVENT, DISCONNECT_EVENT, ACTIVITY_EVENT, INACTIVITY_EVENT); } protected boolean isSupportedOriginator(EntityType entityType) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/external/TbAbstractExternalNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/external/TbAbstractExternalNode.java new file mode 100644 index 0000000000..1fb000d709 --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/external/TbAbstractExternalNode.java @@ -0,0 +1,64 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.external; + +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNode; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; +import org.thingsboard.server.common.msg.TbMsg; + +public abstract class TbAbstractExternalNode implements TbNode { + + private boolean forceAck; + + public void init(TbContext ctx) { + this.forceAck = ctx.isExternalNodeForceAck(); + } + + protected void tellSuccess(TbContext ctx, TbMsg tbMsg) { + if (forceAck) { + ctx.enqueueForTellNext(tbMsg.copyWithNewCtx(), TbNodeConnectionType.SUCCESS); + } else { + ctx.tellSuccess(tbMsg); + } + } + + protected void tellFailure(TbContext ctx, TbMsg tbMsg, Throwable t) { + if (forceAck) { + if (t == null) { + ctx.enqueueForTellNext(tbMsg.copyWithNewCtx(), TbNodeConnectionType.FAILURE); + } else { + ctx.enqueueForTellFailure(tbMsg.copyWithNewCtx(), t); + } + } else { + if (t == null) { + ctx.tellNext(tbMsg, TbNodeConnectionType.FAILURE); + } else { + ctx.tellFailure(tbMsg, t); + } + } + } + + protected TbMsg ackIfNeeded(TbContext ctx, TbMsg msg) { + if (forceAck) { + ctx.ack(msg); + return msg.copyWithNewCtx(); + } else { + return msg; + } + } + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNode.java index 21e109e8f3..39c37f5c81 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNode.java @@ -31,10 +31,11 @@ import org.thingsboard.server.common.data.plugin.ComponentType; type = ComponentType.FILTER, name = "asset profile switch", customRelations = true, - relationTypes = {}, + relationTypes = {"default"}, configClazz = EmptyNodeConfiguration.class, nodeDescription = "Route incoming messages based on the name of the asset profile", - nodeDetails = "Route incoming messages based on the name of the asset profile. The asset profile name is case-sensitive", + nodeDetails = "Route incoming messages based on the name of the asset profile. The asset profile name is case-sensitive.

" + + "Output connections: Asset profile name or Failure", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbNodeEmptyConfig") public class TbAssetTypeSwitchNode extends TbAbstractTypeSwitchNode { @@ -42,7 +43,8 @@ public class TbAssetTypeSwitchNode extends TbAbstractTypeSwitchNode { @Override protected String getRelationType(TbContext ctx, EntityId originator) throws TbNodeException { if (!EntityType.ASSET.equals(originator.getEntityType())) { - throw new TbNodeException("Unsupported originator type: " + originator.getEntityType() + "! Only 'ASSET' type is allowed."); + throw new TbNodeException("Unsupported originator type: " + originator.getEntityType().getNormalName() + "!" + + " Only " + EntityType.ASSET.getNormalName() + " type is allowed."); } AssetProfile assetProfile = ctx.getAssetProfileCache().get(ctx.getTenantId(), (AssetId) originator); if (assetProfile == null) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java index dcaf6698cd..ca58e47554 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java @@ -18,7 +18,6 @@ package org.thingsboard.rule.engine.filter; import com.google.common.util.concurrent.FutureCallback; 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.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.RuleNode; @@ -28,24 +27,25 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.alarm.Alarm; -import org.thingsboard.server.common.data.alarm.AlarmStatus; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; import javax.annotation.Nullable; -import java.io.IOException; @Slf4j @RuleNode( type = ComponentType.FILTER, - name = "check alarm status", + name = "alarm status filter", configClazz = TbCheckAlarmStatusNodeConfig.class, - relationTypes = {"True", "False"}, + relationTypes = {TbNodeConnectionType.TRUE, TbNodeConnectionType.FALSE}, nodeDescription = "Checks alarm status.", - nodeDetails = "Checks the alarm status to match one of the specified statuses.", + nodeDetails = "Checks the alarm status to match one of the specified statuses.

" + + "Output connections: True, False, Failure.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeCheckAlarmStatusConfig") public class TbCheckAlarmStatusNode implements TbNode { + private TbCheckAlarmStatusNodeConfig config; @Override @@ -60,33 +60,24 @@ public class TbCheckAlarmStatusNode implements TbNode { ListenableFuture latest = ctx.getAlarmService().findAlarmByIdAsync(ctx.getTenantId(), alarm.getId()); - Futures.addCallback(latest, new FutureCallback() { + Futures.addCallback(latest, new FutureCallback<>() { @Override public void onSuccess(@Nullable Alarm result) { - if (result != null) { - boolean isPresent = false; - for (AlarmStatus alarmStatus : config.getAlarmStatusList()) { - if (result.getStatus() == alarmStatus) { - isPresent = true; - break; - } - } - if (isPresent) { - ctx.tellNext(msg, "True"); - } else { - ctx.tellNext(msg, "False"); - } - } else { + if (result == null) { ctx.tellFailure(msg, new TbNodeException("No such alarm found.")); + return; } + boolean isPresent = config.getAlarmStatusList().stream() + .anyMatch(alarmStatus -> result.getStatus() == alarmStatus); + ctx.tellNext(msg, isPresent ? TbNodeConnectionType.TRUE : TbNodeConnectionType.FALSE); } @Override public void onFailure(Throwable t) { ctx.tellFailure(msg, t); } - }, MoreExecutors.directExecutor()); - } catch (IllegalArgumentException e) { + }, ctx.getDbCallbackExecutor()); + } catch (Exception e) { log.error("Failed to parse alarm: [{}]", msg.getData()); throw new TbNodeException(e); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeConfig.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeConfig.java index d979eb10ca..4c15d635b8 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeConfig.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeConfig.java @@ -24,12 +24,14 @@ import java.util.List; @Data public class TbCheckAlarmStatusNodeConfig implements NodeConfiguration { + private List alarmStatusList; @Override public TbCheckAlarmStatusNodeConfig defaultConfiguration() { - TbCheckAlarmStatusNodeConfig config = new TbCheckAlarmStatusNodeConfig(); + var config = new TbCheckAlarmStatusNodeConfig(); config.setAlarmStatusList(Arrays.asList(AlarmStatus.ACTIVE_ACK, AlarmStatus.ACTIVE_UNACK)); return config; } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNode.java index 3853b3a62a..a5ee3b969b 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNode.java @@ -22,6 +22,7 @@ import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; @@ -33,12 +34,12 @@ import java.util.Map; @RuleNode( type = ComponentType.FILTER, name = "check fields presence", - relationTypes = {"True", "False"}, + relationTypes = {TbNodeConnectionType.TRUE, TbNodeConnectionType.FALSE}, configClazz = TbCheckMessageNodeConfiguration.class, nodeDescription = "Checks the presence of the specified fields in the message and/or metadata.", - nodeDetails = "Checks the presence of the specified fields in the message and/or metadata. " + - "By default, the rule node checks that all specified fields need to be present. " + - "Uncheck the 'Check that all specified fields are present' if the presence of at least one field is sufficient.", + nodeDetails = "By default, the rule node checks that all specified fields are present. " + + "Uncheck the 'Check that all selected fields are present' if the presence of at least one field is sufficient.

" + + "Output connections: True, False, Failure", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeCheckMessageConfig") public class TbCheckMessageNode implements TbNode { @@ -59,11 +60,10 @@ public class TbCheckMessageNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { try { - if (config.isCheckAllKeys()) { - ctx.tellNext(msg, allKeysData(msg) && allKeysMetadata(msg) ? "True" : "False"); - } else { - ctx.tellNext(msg, atLeastOneData(msg) || atLeastOneMetadata(msg) ? "True" : "False"); - } + String relationType = config.isCheckAllKeys() ? + allKeysData(msg) && allKeysMetadata(msg) ? TbNodeConnectionType.TRUE : TbNodeConnectionType.FALSE : + atLeastOneData(msg) || atLeastOneMetadata(msg) ? TbNodeConnectionType.TRUE : TbNodeConnectionType.FALSE; + ctx.tellNext(msg, relationType); } catch (Exception e) { ctx.tellFailure(msg, e); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeConfiguration.java index f145be3fd2..25a6efa417 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeConfiguration.java @@ -22,7 +22,7 @@ import java.util.Collections; import java.util.List; @Data -public class TbCheckMessageNodeConfiguration implements NodeConfiguration { +public class TbCheckMessageNodeConfiguration implements NodeConfiguration { private List messageNames; private List metadataNames; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java index 4a0d6b05e7..9933f9bb73 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java @@ -15,9 +15,10 @@ */ package org.thingsboard.rule.engine.filter; +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.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; @@ -25,12 +26,15 @@ import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.TbMsg; import java.util.List; @@ -43,16 +47,21 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; @Slf4j @RuleNode( type = ComponentType.FILTER, - name = "check relation", + name = "check relation presence", configClazz = TbCheckRelationNodeConfiguration.class, - relationTypes = {"True", "False"}, + version = 1, + relationTypes = {TbNodeConnectionType.TRUE, TbNodeConnectionType.FALSE}, nodeDescription = "Checks the presence of the relation between the originator of the message and other entities.", - nodeDetails = "If 'check relation to specific entity' is selected, one must specify a related entity. " + - "Otherwise, the rule node checks the presence of a relation to any entity that matches the direction and relation type criteria.", + nodeDetails = "If 'check relation to specific entity' is selected, you should specify a related entity. " + + "Otherwise, the rule node checks the presence of a relation to any entity. " + + "In both cases, relation lookup is based on configured direction and type.

" + + "Output connections: True, False, Failure", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeCheckRelationConfig") public class TbCheckRelationNode implements TbNode { + private static final String DIRECTION_PROPERTY_NAME = "direction"; + private TbCheckRelationNodeConfiguration config; private EntityId singleEntityId; @@ -60,6 +69,9 @@ public class TbCheckRelationNode implements TbNode { public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { this.config = TbNodeUtils.convert(configuration, TbCheckRelationNodeConfiguration.class); if (config.isCheckForSingleEntity()) { + if (StringUtils.isEmpty(config.getEntityType()) || StringUtils.isEmpty(config.getEntityId())) { + throw new TbNodeException("Entity should be specified!"); + } this.singleEntityId = EntityIdFactory.getByTypeAndId(config.getEntityType(), config.getEntityId()); ctx.checkTenantEntity(singleEntityId); } @@ -67,44 +79,56 @@ public class TbCheckRelationNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) throws TbNodeException { - ListenableFuture checkRelationFuture; - if (config.isCheckForSingleEntity()) { - checkRelationFuture = processSingle(ctx, msg); - } else { - checkRelationFuture = processList(ctx, msg); - } - withCallback(checkRelationFuture, filterResult -> ctx.tellNext(msg, filterResult ? "True" : "False"), t -> ctx.tellFailure(msg, t), ctx.getDbCallbackExecutor()); + ListenableFuture checkRelationFuture = config.isCheckForSingleEntity() ? + processSingle(ctx, msg) : processList(ctx, msg); + withCallback(checkRelationFuture, + filterResult -> ctx.tellNext(msg, filterResult ? TbNodeConnectionType.TRUE : TbNodeConnectionType.FALSE), + t -> ctx.tellFailure(msg, t), ctx.getDbCallbackExecutor()); } private ListenableFuture processSingle(TbContext ctx, TbMsg msg) { EntityId from; EntityId to; if (EntitySearchDirection.FROM.name().equals(config.getDirection())) { - from = singleEntityId; - to = msg.getOriginator(); - } else { to = singleEntityId; from = msg.getOriginator(); + } else { + from = singleEntityId; + to = msg.getOriginator(); } return ctx.getRelationService().checkRelationAsync(ctx.getTenantId(), from, to, config.getRelationType(), RelationTypeGroup.COMMON); } private ListenableFuture processList(TbContext ctx, TbMsg msg) { - if (EntitySearchDirection.FROM.name().equals(config.getDirection())) { - return Futures.transformAsync(ctx.getRelationService() - .findByToAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), config.getRelationType(), RelationTypeGroup.COMMON), this::isEmptyList, MoreExecutors.directExecutor()); - } else { - return Futures.transformAsync(ctx.getRelationService() - .findByFromAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), config.getRelationType(), RelationTypeGroup.COMMON), this::isEmptyList, MoreExecutors.directExecutor()); - } + ListenableFuture> relationListFuture = EntitySearchDirection.FROM.name().equals(config.getDirection()) ? + ctx.getRelationService().findByFromAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), config.getRelationType(), RelationTypeGroup.COMMON) : + ctx.getRelationService().findByToAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), config.getRelationType(), RelationTypeGroup.COMMON); + return Futures.transformAsync(relationListFuture, this::isEmptyList, ctx.getDbCallbackExecutor()); } private ListenableFuture isEmptyList(List entityRelations) { - if (entityRelations.isEmpty()) { - return Futures.immediateFuture(false); - } else { - return Futures.immediateFuture(true); + return entityRelations.isEmpty() ? Futures.immediateFuture(false) : Futures.immediateFuture(true); + } + + @Override + public TbPair upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException { + if (fromVersion == 0) { + var newConfigObjectNode = (ObjectNode) oldConfiguration; + if (!newConfigObjectNode.has(DIRECTION_PROPERTY_NAME)) { + throw new TbNodeException("property to update: '" + DIRECTION_PROPERTY_NAME + "' doesn't exists in configuration!"); + } + String direction = newConfigObjectNode.get(DIRECTION_PROPERTY_NAME).asText(); + if (EntitySearchDirection.TO.name().equals(direction)) { + newConfigObjectNode.put(DIRECTION_PROPERTY_NAME, EntitySearchDirection.FROM.name()); + return new TbPair<>(true, newConfigObjectNode); + } + if (EntitySearchDirection.FROM.name().equals(direction)) { + newConfigObjectNode.put(DIRECTION_PROPERTY_NAME, EntitySearchDirection.TO.name()); + return new TbPair<>(true, newConfigObjectNode); + } + throw new TbNodeException("property to update: '" + DIRECTION_PROPERTY_NAME + "' has invalid value!"); } + return new TbPair<>(false, oldConfiguration); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeConfiguration.java index aeda247583..1a8ab7068d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeConfiguration.java @@ -17,6 +17,7 @@ package org.thingsboard.rule.engine.filter; import lombok.Data; import org.thingsboard.rule.engine.api.NodeConfiguration; +import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntitySearchDirection; /** @@ -33,9 +34,9 @@ public class TbCheckRelationNodeConfiguration implements NodeConfigurationDevice profile name or Failure", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbNodeEmptyConfig") public class TbDeviceTypeSwitchNode extends TbAbstractTypeSwitchNode { @@ -42,7 +43,8 @@ public class TbDeviceTypeSwitchNode extends TbAbstractTypeSwitchNode { @Override protected String getRelationType(TbContext ctx, EntityId originator) throws TbNodeException { if (!EntityType.DEVICE.equals(originator.getEntityType())) { - throw new TbNodeException("Unsupported originator type: " + originator.getEntityType() + "! Only 'DEVICE' type is allowed."); + throw new TbNodeException("Unsupported originator type: " + originator.getEntityType().getNormalName() + + "! Only " + EntityType.DEVICE.getNormalName() + " type is allowed."); } DeviceProfile deviceProfile = ctx.getDeviceProfileCache().get(ctx.getTenantId(), (DeviceId) originator); if (deviceProfile == null) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java index 0bde9402a7..bcfaf78a65 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java @@ -22,6 +22,7 @@ import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.script.ScriptLanguage; @@ -32,7 +33,8 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; @Slf4j @RuleNode( type = ComponentType.FILTER, - name = "script", relationTypes = {"True", "False"}, + name = "script", + relationTypes = {TbNodeConnectionType.TRUE, TbNodeConnectionType.FALSE}, configClazz = TbJsFilterNodeConfiguration.class, nodeDescription = "Filter incoming messages using TBEL or JS script", nodeDetails = "Evaluates boolean function using incoming message. " + @@ -40,7 +42,8 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; "Script function should return boolean value and accepts three parameters:
" + "Message payload can be accessed via msg property. For example msg.temperature < 10;
" + "Message metadata can be accessed via metadata property. For example metadata.customerName === 'John';
" + - "Message type can be accessed via msgType property.", + "Message type can be accessed via msgType property.

" + + "Output connections: True, False, Failure", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeScriptConfig" ) @@ -62,7 +65,7 @@ public class TbJsFilterNode implements TbNode { withCallback(scriptEngine.executeFilterAsync(msg), filterResult -> { ctx.logJsEvalResponse(); - ctx.tellNext(msg, filterResult ? "True" : "False"); + ctx.tellNext(msg, filterResult ? TbNodeConnectionType.TRUE : TbNodeConnectionType.FALSE); }, t -> { ctx.tellFailure(msg, t); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java index 706978846a..f55a172c02 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java @@ -44,7 +44,8 @@ import java.util.Set; "If Array is empty - message not routed to next Node. " + "Message payload can be accessed via msg property. For example msg.temperature < 10;
" + "Message metadata can be accessed via metadata property. For example metadata.customerName === 'John';
" + - "Message type can be accessed via msgType property.", + "Message type can be accessed via msgType property.

" + + "Output connections: Custom connection(s) defined by switch node or Failure", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeSwitchConfig") public class TbJsSwitchNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNode.java index 765deb2ea7..ca8ba9d561 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNode.java @@ -21,6 +21,7 @@ import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; @@ -31,11 +32,12 @@ import org.thingsboard.server.common.msg.TbMsg; @Slf4j @RuleNode( type = ComponentType.FILTER, - name = "message type", + name = "message type filter", configClazz = TbMsgTypeFilterNodeConfiguration.class, - relationTypes = {"True", "False"}, + relationTypes = {TbNodeConnectionType.TRUE, TbNodeConnectionType.FALSE}, nodeDescription = "Filter incoming messages by Message Type", - nodeDetails = "If incoming MessageType is expected - send Message via True chain, otherwise False chain is used.", + nodeDetails = "If incoming message type is expected - send Message via True chain, otherwise False chain is used.

" + + "Output connections: True, False, Failure", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeMessageTypeConfig") public class TbMsgTypeFilterNode implements TbNode { @@ -49,7 +51,7 @@ public class TbMsgTypeFilterNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { - ctx.tellNext(msg, config.getMessageTypes().contains(msg.getType()) ? "True" : "False"); + ctx.tellNext(msg, config.getMessageTypes().contains(msg.getType()) ? TbNodeConnectionType.TRUE : TbNodeConnectionType.FALSE); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeConfiguration.java index fb326a6d50..1c0b080653 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeConfiguration.java @@ -17,11 +17,14 @@ package org.thingsboard.rule.engine.filter; import lombok.Data; import org.thingsboard.rule.engine.api.NodeConfiguration; -import org.thingsboard.server.common.msg.session.SessionMsgType; import java.util.Arrays; import java.util.List; +import static org.thingsboard.server.common.data.msg.TbMsgType.POST_ATTRIBUTES_REQUEST; +import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_REQUEST; +import static org.thingsboard.server.common.data.msg.TbMsgType.TO_SERVER_RPC_REQUEST; + /** * Created by ashvayka on 19.01.18. */ @@ -32,11 +35,11 @@ public class TbMsgTypeFilterNodeConfiguration implements NodeConfiguration etc. via corresponding chain, otherwise Other chain is used.", + nodeDetails = "Sends messages with message types \"Post attributes\", \"Post telemetry\", \"RPC Request\"" + + " etc. via corresponding chain, otherwise Other chain is used.

" + + "Output connections: Message type connection, Other - if message type is custom or Failure", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbNodeEmptyConfig") public class TbMsgTypeSwitchNode implements TbNode { @@ -52,83 +49,7 @@ public class TbMsgTypeSwitchNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { - String relationType; - if (msg.getType().equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name())) { - relationType = "Post attributes"; - } else if (msg.getType().equals(SessionMsgType.POST_TELEMETRY_REQUEST.name())) { - relationType = "Post telemetry"; - } else if (msg.getType().equals(SessionMsgType.TO_SERVER_RPC_REQUEST.name())) { - relationType = "RPC Request from Device"; - } else if (msg.getType().equals(DataConstants.ACTIVITY_EVENT)) { - relationType = "Activity Event"; - } else if (msg.getType().equals(DataConstants.INACTIVITY_EVENT)) { - relationType = "Inactivity Event"; - } else if (msg.getType().equals(DataConstants.CONNECT_EVENT)) { - relationType = "Connect Event"; - } else if (msg.getType().equals(DataConstants.DISCONNECT_EVENT)) { - relationType = "Disconnect Event"; - } else if (msg.getType().equals(DataConstants.ENTITY_CREATED)) { - relationType = "Entity Created"; - } else if (msg.getType().equals(DataConstants.ENTITY_UPDATED)) { - relationType = "Entity Updated"; - } else if (msg.getType().equals(DataConstants.ENTITY_DELETED)) { - relationType = "Entity Deleted"; - } else if (msg.getType().equals(DataConstants.ENTITY_ASSIGNED)) { - relationType = "Entity Assigned"; - } else if (msg.getType().equals(DataConstants.ENTITY_UNASSIGNED)) { - relationType = "Entity Unassigned"; - } else if (msg.getType().equals(DataConstants.ATTRIBUTES_UPDATED)) { - relationType = "Attributes Updated"; - } else if (msg.getType().equals(DataConstants.ATTRIBUTES_DELETED)) { - relationType = "Attributes Deleted"; - } else if (msg.getType().equals(DataConstants.ALARM_ACK)) { - relationType = "Alarm Acknowledged"; - } else if (msg.getType().equals(DataConstants.ALARM_CLEAR)) { - relationType = "Alarm Cleared"; - } else if (msg.getType().equals(DataConstants.ALARM_ASSIGNED)) { - relationType = "Alarm Assigned"; - } else if (msg.getType().equals(DataConstants.ALARM_UNASSIGNED)) { - relationType = "Alarm Unassigned"; - } else if (msg.getType().equals(DataConstants.COMMENT_CREATED)) { - relationType = "Comment Created"; - } else if (msg.getType().equals(DataConstants.COMMENT_UPDATED)) { - relationType = "Comment Updated"; - } else if (msg.getType().equals(DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE)) { - relationType = "RPC Request to Device"; - } else if (msg.getType().equals(DataConstants.ENTITY_ASSIGNED_FROM_TENANT)) { - relationType = "Entity Assigned From Tenant"; - } else if (msg.getType().equals(DataConstants.ENTITY_ASSIGNED_TO_TENANT)) { - relationType = "Entity Assigned To Tenant"; - } else if (msg.getType().equals(DataConstants.TIMESERIES_UPDATED)) { - relationType = "Timeseries Updated"; - } else if (msg.getType().equals(DataConstants.TIMESERIES_DELETED)) { - relationType = "Timeseries Deleted"; - } else if (msg.getType().equals(DataConstants.RPC_QUEUED)) { - relationType = "RPC Queued"; - } else if (msg.getType().equals(DataConstants.RPC_SENT)) { - relationType = "RPC Sent"; - } else if (msg.getType().equals(DataConstants.RPC_DELIVERED)) { - relationType = "RPC Delivered"; - } else if (msg.getType().equals(DataConstants.RPC_SUCCESSFUL)) { - relationType = "RPC Successful"; - } else if (msg.getType().equals(DataConstants.RPC_TIMEOUT)) { - relationType = "RPC Timeout"; - } else if (msg.getType().equals(DataConstants.RPC_EXPIRED)) { - relationType = "RPC Expired"; - } else if (msg.getType().equals(DataConstants.RPC_FAILED)) { - relationType = "RPC Failed"; - } else if (msg.getType().equals(DataConstants.RPC_DELETED)) { - relationType = "RPC Deleted"; - } else if (msg.getType().equals(DataConstants.RELATION_ADD_OR_UPDATE)) { - relationType = "Relation Added or Updated"; - } else if (msg.getType().equals(DataConstants.RELATION_DELETED)) { - relationType = "Relation Deleted"; - } else if (msg.getType().equals(DataConstants.RELATIONS_DELETED)) { - relationType = "All Relations Deleted"; - } else { - relationType = "Other"; - } - ctx.tellNext(msg, relationType); + ctx.tellNext(msg, msg.getInternalType().getRuleNodeConnection()); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNode.java index f4e13d761e..183721e72a 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNode.java @@ -21,6 +21,7 @@ import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.plugin.ComponentType; @@ -29,11 +30,12 @@ import org.thingsboard.server.common.msg.TbMsg; @Slf4j @RuleNode( type = ComponentType.FILTER, - name = "entity type", + name = "entity type filter", configClazz = TbOriginatorTypeFilterNodeConfiguration.class, - relationTypes = {"True", "False"}, + relationTypes = {TbNodeConnectionType.TRUE, TbNodeConnectionType.FALSE}, nodeDescription = "Filter incoming messages by the type of message originator entity", - nodeDetails = "Checks that the entity type of the incoming message originator matches one of the values specified in the filter.", + nodeDetails = "Checks that the entity type of the incoming message originator matches one of the values specified in the filter.

" + + "Output connections: True, False, Failure", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeOriginatorTypeConfig") public class TbOriginatorTypeFilterNode implements TbNode { @@ -48,7 +50,7 @@ public class TbOriginatorTypeFilterNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { EntityType originatorType = msg.getOriginator().getEntityType(); - ctx.tellNext(msg, config.getOriginatorTypes().contains(originatorType) ? "True" : "False"); + ctx.tellNext(msg, config.getOriginatorTypes().contains(originatorType) ? TbNodeConnectionType.TRUE : TbNodeConnectionType.FALSE); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNode.java index a920af5066..17b7d575f5 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNode.java @@ -19,8 +19,6 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.rule.engine.api.EmptyNodeConfiguration; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.rule.engine.api.TbNodeException; -import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.plugin.ComponentType; @@ -29,55 +27,17 @@ import org.thingsboard.server.common.data.plugin.ComponentType; type = ComponentType.FILTER, name = "entity type switch", configClazz = EmptyNodeConfiguration.class, - relationTypes = {"Device", "Asset", "Alarm", "Entity View", "Tenant", "Customer", "User", "Dashboard", "Rule chain", "Rule node", "Edge"}, + relationTypes = {}, // should always be empty. We add the relation types for this node in AnnotationComponentDiscoveryService. nodeDescription = "Route incoming messages by Message Originator Type", - nodeDetails = "Routes messages to chain according to the entity type ('Device', 'Asset', etc.).", + nodeDetails = "Routes messages to chain according to the entity type ('Device', 'Asset', etc.).

" + + "Output connections: Message originator type or Failure", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbNodeEmptyConfig") public class TbOriginatorTypeSwitchNode extends TbAbstractTypeSwitchNode { @Override - protected String getRelationType(TbContext ctx, EntityId originator) throws TbNodeException { - String relationType; - EntityType originatorType = originator.getEntityType(); - switch (originatorType) { - case TENANT: - relationType = "Tenant"; - break; - case CUSTOMER: - relationType = "Customer"; - break; - case USER: - relationType = "User"; - break; - case DASHBOARD: - relationType = "Dashboard"; - break; - case ASSET: - relationType = "Asset"; - break; - case DEVICE: - relationType = "Device"; - break; - case ENTITY_VIEW: - relationType = "Entity View"; - break; - case EDGE: - relationType = "Edge"; - break; - case RULE_CHAIN: - relationType = "Rule chain"; - break; - case RULE_NODE: - relationType = "Rule node"; - break; - case ALARM: - relationType = "Alarm"; - break; - default: - throw new TbNodeException("Unsupported originator type: " + originatorType); - } - return relationType; + protected String getRelationType(TbContext ctx, EntityId originator) { + return originator.getEntityType().getNormalName(); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNode.java index 35baaf461b..a92cafefd3 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNode.java @@ -21,7 +21,7 @@ import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; -import org.thingsboard.rule.engine.api.TbRelationTypes; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; @@ -48,7 +48,7 @@ public class TbCheckpointNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { - ctx.enqueueForTellNext(msg, queueName, TbRelationTypes.SUCCESS, () -> ctx.ack(msg), error -> ctx.tellFailure(msg, error)); + ctx.enqueueForTellNext(msg, queueName, TbNodeConnectionType.SUCCESS, () -> ctx.ack(msg), error -> ctx.tellFailure(msg, error)); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java index b0198e210c..3067947445 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java @@ -20,6 +20,8 @@ import com.google.api.core.ApiFutureCallback; import com.google.api.core.ApiFutures; import com.google.api.gax.core.CredentialsProvider; import com.google.api.gax.core.FixedCredentialsProvider; +import com.google.api.gax.core.FixedExecutorProvider; +import com.google.api.gax.retrying.RetrySettings; import com.google.auth.oauth2.ServiceAccountCredentials; import com.google.cloud.pubsub.v1.Publisher; import com.google.protobuf.ByteString; @@ -28,13 +30,14 @@ import com.google.pubsub.v1.PubsubMessage; import lombok.extern.slf4j.Slf4j; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.rule.engine.external.TbAbstractExternalNode; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.threeten.bp.Duration; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -53,7 +56,7 @@ import java.util.concurrent.TimeUnit; configDirective = "tbExternalNodePubSubConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiB2aWV3Qm94PSIwIDAgMTI4IDEyOCI+Cjx0aXRsZT5DbG91ZCBQdWJTdWI8L3RpdGxlPgo8Zz4KPHBhdGggZD0iTTEyNi40Nyw1OC4xMmwtMjYuMy00NS43NEExMS41NiwxMS41NiwwLDAsMCw5MC4zMSw2LjVIMzcuN2ExMS41NSwxMS41NSwwLDAsMC05Ljg2LDUuODhMMS41Myw1OGExMS40OCwxMS40OCwwLDAsMCwwLDExLjQ0bDI2LjMsNDZhMTEuNzcsMTEuNzcsMCwwLDAsOS44Niw2LjA5SDkwLjNhMTEuNzMsMTEuNzMsMCwwLDAsOS44Ny02LjA2bDI2LjMtNDUuNzRBMTEuNzMsMTEuNzMsMCwwLDAsMTI2LjQ3LDU4LjEyWiIgc3R5bGU9ImZpbGw6ICM3MzViMmYiLz4KPHBhdGggZD0iTTg5LjIyLDQ3Ljc0LDgzLjM2LDQ5bC0xNC42LTE0LjZMNjQuMDksNDMuMSw2MS41NSw1My4ybDQuMjksNC4yOUw1Ny42LDU5LjE4LDQ2LjMsNDcuODhsLTcuNjcsNy4zOEw1Mi43Niw2OS4zN2wtMTUsMTEuOUw3OCwxMjEuNUg5MC4zYTExLjczLDExLjczLDAsMCwwLDkuODctNi4wNmwyMC43Mi0zNloiIHN0eWxlPSJvcGFjaXR5OiAwLjA3MDAwMDAwMDI5ODAyMztpc29sYXRpb246IGlzb2xhdGUiLz4KPHBhdGggZD0iTTgyLjg2LDQ3YTUuMzIsNS4zMiwwLDEsMS0xLjk1LDcuMjdBNS4zMiw1LjMyLDAsMCwxLDgyLjg2LDQ3IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNMzkuODIsNTYuMThhNS4zMiw1LjMyLDAsMSwxLDcuMjctMS45NSw1LjMyLDUuMzIsMCwwLDEtNy4yNywxLjk1IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNNjkuMzIsODguODVBNS4zMiw1LjMyLDAsMSwxLDY0LDgzLjUyYTUuMzIsNS4zMiwwLDAsMSw1LjMyLDUuMzIiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxnPgo8cGF0aCBkPSJNNjQsNTIuOTRhMTEuMDYsMTEuMDYsMCwwLDEsMi40Ni4yOFYzOS4xNUg2MS41NFY1My4yMkExMS4wNiwxMS4wNiwwLDAsMSw2NCw1Mi45NFoiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxwYXRoIGQ9Ik03NC41Nyw2Ny4yNmExMSwxMSwwLDAsMS0yLjQ3LDQuMjVsMTIuMTksNywyLjQ2LTQuMjZaIiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNNTMuNDMsNjcuMjZsLTEyLjE4LDcsMi40Niw0LjI2LDEyLjE5LTdBMTEsMTEsMCwwLDEsNTMuNDMsNjcuMjZaIiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8L2c+CjxwYXRoIGQ9Ik03Mi42LDY0QTguNiw4LjYsMCwxLDEsNjQsNTUuNCw4LjYsOC42LDAsMCwxLDcyLjYsNjQiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxwYXRoIGQ9Ik0zOS4xLDcwLjU3YTYuNzYsNi43NiwwLDEsMS0yLjQ3LDkuMjMsNi43Niw2Ljc2LDAsMCwxLDIuNDctOS4yMyIgc3R5bGU9ImZpbGw6ICNmZmYiLz4KPHBhdGggZD0iTTgyLjE0LDgyLjI3YTYuNzYsNi43NiwwLDEsMSw5LjIzLTIuNDcsNi43NSw2Ljc1LDAsMCwxLTkuMjMsMi40NyIgc3R5bGU9ImZpbGw6ICNmZmYiLz4KPHBhdGggZD0iTTcwLjc2LDM5LjE1QTYuNzYsNi43NiwwLDEsMSw2NCwzMi4zOWE2Ljc2LDYuNzYsMCwwLDEsNi43Niw2Ljc2IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8L2c+Cjwvc3ZnPgo=" ) -public class TbPubSubNode implements TbNode { +public class TbPubSubNode extends TbAbstractExternalNode { private static final String MESSAGE_ID = "messageId"; private static final String ERROR = "error"; @@ -63,9 +66,10 @@ public class TbPubSubNode implements TbNode { @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { + super.init(ctx); + this.config = TbNodeUtils.convert(configuration, TbPubSubNodeConfiguration.class); try { - this.config = TbNodeUtils.convert(configuration, TbPubSubNodeConfiguration.class); - this.pubSubClient = initPubSubClient(); + this.pubSubClient = initPubSubClient(ctx); } catch (Exception e) { throw new TbNodeException(e); } @@ -73,6 +77,7 @@ public class TbPubSubNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { + msg = ackIfNeeded(ctx, msg); publishMessage(ctx, msg); } @@ -100,39 +105,51 @@ public class TbPubSubNode implements TbNode { ApiFuture messageIdFuture = this.pubSubClient.publish(pubsubMessageBuilder.build()); ApiFutures.addCallback(messageIdFuture, new ApiFutureCallback() { public void onSuccess(String messageId) { - TbMsg next = processPublishResult(ctx, msg, messageId); - ctx.tellSuccess(next); + TbMsg next = processPublishResult(msg, messageId); + tellSuccess(ctx, next); } public void onFailure(Throwable t) { - TbMsg next = processException(ctx, msg, t); - ctx.tellFailure(next, t); + TbMsg next = processException(msg, t); + tellFailure(ctx, next, t); } }, ctx.getExternalCallExecutor()); } - private TbMsg processPublishResult(TbContext ctx, TbMsg origMsg, String messageId) { + private TbMsg processPublishResult(TbMsg origMsg, String messageId) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(MESSAGE_ID, messageId); - return ctx.transformMsg(origMsg, origMsg.getType(), origMsg.getOriginator(), metaData, origMsg.getData()); + return TbMsg.transformMsgMetadata(origMsg, metaData); } - private TbMsg processException(TbContext ctx, TbMsg origMsg, Throwable t) { + private TbMsg processException(TbMsg origMsg, Throwable t) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(ERROR, t.getClass() + ": " + t.getMessage()); - return ctx.transformMsg(origMsg, origMsg.getType(), origMsg.getOriginator(), metaData, origMsg.getData()); + return TbMsg.transformMsgMetadata(origMsg, metaData); } - private Publisher initPubSubClient() throws IOException { + private Publisher initPubSubClient(TbContext ctx) throws IOException { ProjectTopicName topicName = ProjectTopicName.of(config.getProjectId(), config.getTopicName()); ServiceAccountCredentials credentials = ServiceAccountCredentials.fromStream( new ByteArrayInputStream(config.getServiceAccountKey().getBytes())); CredentialsProvider credProvider = FixedCredentialsProvider.create(credentials); + var retrySettings = RetrySettings.newBuilder() + .setTotalTimeout(Duration.ofSeconds(10)) + .setInitialRetryDelay(Duration.ofMillis(50)) + .setRetryDelayMultiplier(1.1) + .setMaxRetryDelay(Duration.ofSeconds(2)) + .setInitialRpcTimeout(Duration.ofSeconds(2)) + .setRpcTimeoutMultiplier(1) + .setMaxRpcTimeout(Duration.ofSeconds(10)) + .build(); + return Publisher.newBuilder(topicName) .setCredentialsProvider(credProvider) + .setRetrySettings(retrySettings) + .setExecutorProvider(FixedExecutorProvider.create(ctx.getPubSubRuleNodeExecutorProvider().getExecutor())) .build(); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/AbstractGeofencingNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/AbstractGeofencingNode.java index 1f1f1fd132..b263518c41 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/AbstractGeofencingNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/AbstractGeofencingNode.java @@ -48,14 +48,14 @@ public abstract class AbstractGeofencingNode getConfigClazz(); protected boolean checkMatches(TbMsg msg) throws TbNodeException { - JsonElement msgDataElement = new JsonParser().parse(msg.getData()); + JsonElement msgDataElement = JsonParser.parseString(msg.getData()); if (!msgDataElement.isJsonObject()) { - throw new TbNodeException("Incoming Message is not a valid JSON object"); + throw new TbNodeException("Incoming Message is not a valid JSON object!"); } JsonObject msgDataObj = msgDataElement.getAsJsonObject(); double latitude = getValueFromMessageByName(msg, msgDataObj, config.getLatitudeKeyName()); double longitude = getValueFromMessageByName(msg, msgDataObj, config.getLongitudeKeyName()); - List perimeters = getPerimeters(msg, msgDataObj); + List perimeters = getPerimeters(msg); boolean matches = false; for (Perimeter perimeter : perimeters) { if (checkMatches(perimeter, latitude, longitude)) { @@ -74,11 +74,11 @@ public abstract class AbstractGeofencingNode getPerimeters(TbMsg msg, JsonObject msgDataObj) throws TbNodeException { + protected List getPerimeters(TbMsg msg) throws TbNodeException { if (config.isFetchPerimeterInfoFromMessageMetadata()) { if (StringUtils.isEmpty(config.getPerimeterKeyName())) { // Old configuration before "perimeterKeyName" was introduced diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/GeoUtil.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/GeoUtil.java index 519a4274c1..4467dfe30b 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/GeoUtil.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/GeoUtil.java @@ -45,8 +45,6 @@ public class GeoUtil { private static final SpatialContext distCtx = SpatialContext.GEO; private static final JtsSpatialContext jtsCtx; - private static final JsonParser JSON_PARSER = new JsonParser(); - static { JtsSpatialContextFactory factory = new JtsSpatialContextFactory(); factory.normWrapLongitude = true; @@ -64,7 +62,7 @@ public class GeoUtil { throw new RuntimeException("Polygon string can't be empty or null!"); } - JsonArray polygonsJson = normalizePolygonsJson(JSON_PARSER.parse(polygonInString).getAsJsonArray()); + JsonArray polygonsJson = normalizePolygonsJson(JsonParser.parseString(polygonInString).getAsJsonArray()); List polygons = buildPolygonsFromJson(polygonsJson); Set holes = extractHolesFrom(polygons); polygons.removeIf(holes::contains); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java index 373118ebdd..20e7039be4 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; @@ -30,7 +31,7 @@ import org.thingsboard.server.common.msg.TbMsg; type = ComponentType.FILTER, name = "gps geofencing filter", configClazz = TbGpsGeofencingFilterNodeConfiguration.class, - relationTypes = {"True", "False"}, + relationTypes = {TbNodeConnectionType.TRUE, TbNodeConnectionType.FALSE}, nodeDescription = "Filter incoming messages by GPS based geofencing", nodeDetails = "Extracts latitude and longitude parameters from the incoming message and checks them according to configured perimeter.
" + "Configuration:

" + @@ -57,14 +58,15 @@ import org.thingsboard.server.common.msg.TbMsg; "

" + "{\"latitude\": 48.198618758582384, \"longitude\": 24.65322245153503, \"radius\": 100.0, \"radiusUnit\": \"METER\" }" + "

" + - "Available radius units: METER, KILOMETER, FOOT, MILE, NAUTICAL_MILE;", + "Available radius units: METER, KILOMETER, FOOT, MILE, NAUTICAL_MILE;

" + + "Output connections: True, False, Failure", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeGpsGeofencingConfig") public class TbGpsGeofencingFilterNode extends AbstractGeofencingNode { @Override public void onMsg(TbContext ctx, TbMsg msg) throws TbNodeException { - ctx.tellNext(msg, checkMatches(msg) ? "True" : "False"); + ctx.tellNext(msg, checkMatches(msg) ? TbNodeConnectionType.TRUE : TbNodeConnectionType.FALSE); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java index de1abea224..2e55657844 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java @@ -22,17 +22,17 @@ import org.apache.kafka.clients.producer.Producer; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.kafka.clients.producer.RecordMetadata; +import org.apache.kafka.common.config.SslConfigs; import org.apache.kafka.common.header.Headers; import org.apache.kafka.common.header.internals.RecordHeader; import org.apache.kafka.common.header.internals.RecordHeaders; import org.springframework.util.ReflectionUtils; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; -import org.thingsboard.rule.engine.api.TbRelationTypes; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.rule.engine.external.TbAbstractExternalNode; import org.thingsboard.server.common.data.exception.ThingsboardKafkaClientError; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; @@ -56,7 +56,7 @@ import java.util.Properties; configDirective = "tbExternalNodeKafkaConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUzOCIgaGVpZ2h0PSIyNTAwIiB2aWV3Qm94PSIwIDAgMjU2IDQxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTIwMS44MTYgMjMwLjIxNmMtMTYuMTg2IDAtMzAuNjk3IDcuMTcxLTQwLjYzNCAxOC40NjFsLTI1LjQ2My0xOC4wMjZjMi43MDMtNy40NDIgNC4yNTUtMTUuNDMzIDQuMjU1LTIzLjc5NyAwLTguMjE5LTEuNDk4LTE2LjA3Ni00LjExMi0yMy40MDhsMjUuNDA2LTE3LjgzNWM5LjkzNiAxMS4yMzMgMjQuNDA5IDE4LjM2NSA0MC41NDggMTguMzY1IDI5Ljg3NSAwIDU0LjE4NC0yNC4zMDUgNTQuMTg0LTU0LjE4NCAwLTI5Ljg3OS0yNC4zMDktNTQuMTg0LTU0LjE4NC01NC4xODQtMjkuODc1IDAtNTQuMTg0IDI0LjMwNS01NC4xODQgNTQuMTg0IDAgNS4zNDguODA4IDEwLjUwNSAyLjI1OCAxNS4zODlsLTI1LjQyMyAxNy44NDRjLTEwLjYyLTEzLjE3NS0yNS45MTEtMjIuMzc0LTQzLjMzMy0yNS4xODJ2LTMwLjY0YzI0LjU0NC01LjE1NSA0My4wMzctMjYuOTYyIDQzLjAzNy01My4wMTlDMTI0LjE3MSAyNC4zMDUgOTkuODYyIDAgNjkuOTg3IDAgNDAuMTEyIDAgMTUuODAzIDI0LjMwNSAxNS44MDMgNTQuMTg0YzAgMjUuNzA4IDE4LjAxNCA0Ny4yNDYgNDIuMDY3IDUyLjc2OXYzMS4wMzhDMjUuMDQ0IDE0My43NTMgMCAxNzIuNDAxIDAgMjA2Ljg1NGMwIDM0LjYyMSAyNS4yOTIgNjMuMzc0IDU4LjM1NSA2OC45NHYzMi43NzRjLTI0LjI5OSA1LjM0MS00Mi41NTIgMjcuMDExLTQyLjU1MiA1Mi44OTQgMCAyOS44NzkgMjQuMzA5IDU0LjE4NCA1NC4xODQgNTQuMTg0IDI5Ljg3NSAwIDU0LjE4NC0yNC4zMDUgNTQuMTg0LTU0LjE4NCAwLTI1Ljg4My0xOC4yNTMtNDcuNTUzLTQyLjU1Mi01Mi44OTR2LTMyLjc3NWE2OS45NjUgNjkuOTY1IDAgMCAwIDQyLjYtMjQuNzc2bDI1LjYzMyAxOC4xNDNjLTEuNDIzIDQuODQtMi4yMiA5Ljk0Ni0yLjIyIDE1LjI0IDAgMjkuODc5IDI0LjMwOSA1NC4xODQgNTQuMTg0IDU0LjE4NCAyOS44NzUgMCA1NC4xODQtMjQuMzA1IDU0LjE4NC01NC4xODQgMC0yOS44NzktMjQuMzA5LTU0LjE4NC01NC4xODQtNTQuMTg0em0wLTEyNi42OTVjMTQuNDg3IDAgMjYuMjcgMTEuNzg4IDI2LjI3IDI2LjI3MXMtMTEuNzgzIDI2LjI3LTI2LjI3IDI2LjI3LTI2LjI3LTExLjc4Ny0yNi4yNy0yNi4yN2MwLTE0LjQ4MyAxMS43ODMtMjYuMjcxIDI2LjI3LTI2LjI3MXptLTE1OC4xLTQ5LjMzN2MwLTE0LjQ4MyAxMS43ODQtMjYuMjcgMjYuMjcxLTI2LjI3czI2LjI3IDExLjc4NyAyNi4yNyAyNi4yN2MwIDE0LjQ4My0xMS43ODMgMjYuMjctMjYuMjcgMjYuMjdzLTI2LjI3MS0xMS43ODctMjYuMjcxLTI2LjI3em01Mi41NDEgMzA3LjI3OGMwIDE0LjQ4My0xMS43ODMgMjYuMjctMjYuMjcgMjYuMjdzLTI2LjI3MS0xMS43ODctMjYuMjcxLTI2LjI3YzAtMTQuNDgzIDExLjc4NC0yNi4yNyAyNi4yNzEtMjYuMjdzMjYuMjcgMTEuNzg3IDI2LjI3IDI2LjI3em0tMjYuMjcyLTExNy45N2MtMjAuMjA1IDAtMzYuNjQyLTE2LjQzNC0zNi42NDItMzYuNjM4IDAtMjAuMjA1IDE2LjQzNy0zNi42NDIgMzYuNjQyLTM2LjY0MiAyMC4yMDQgMCAzNi42NDEgMTYuNDM3IDM2LjY0MSAzNi42NDIgMCAyMC4yMDQtMTYuNDM3IDM2LjYzOC0zNi42NDEgMzYuNjM4em0xMzEuODMxIDY3LjE3OWMtMTQuNDg3IDAtMjYuMjctMTEuNzg4LTI2LjI3LTI2LjI3MXMxMS43ODMtMjYuMjcgMjYuMjctMjYuMjcgMjYuMjcgMTEuNzg3IDI2LjI3IDI2LjI3YzAgMTQuNDgzLTExLjc4MyAyNi4yNzEtMjYuMjcgMjYuMjcxeiIvPjwvc3ZnPg==" ) -public class TbKafkaNode implements TbNode { +public class TbKafkaNode extends TbAbstractExternalNode { private static final String OFFSET = "offset"; private static final String PARTITION = "partition"; @@ -78,6 +78,7 @@ public class TbKafkaNode implements TbNode { @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { + super.init(ctx); this.config = TbNodeUtils.convert(configuration, TbKafkaNodeConfiguration.class); this.initError = null; Properties properties = new Properties(); @@ -91,7 +92,14 @@ public class TbKafkaNode implements TbNode { properties.put(ProducerConfig.LINGER_MS_CONFIG, config.getLinger()); properties.put(ProducerConfig.BUFFER_MEMORY_CONFIG, config.getBufferMemory()); if (config.getOtherProperties() != null) { - config.getOtherProperties().forEach(properties::put); + config.getOtherProperties().forEach((k, v) -> { + if (SslConfigs.SSL_KEYSTORE_CERTIFICATE_CHAIN_CONFIG.equals(k) + || SslConfigs.SSL_KEYSTORE_KEY_CONFIG.equals(k) + || SslConfigs.SSL_TRUSTSTORE_CERTIFICATES_CONFIG.equals(k)) { + v = v.replace("\\n", "\n"); + } + properties.put(k, v); + }); } addMetadataKeyValuesAsKafkaHeaders = BooleanUtils.toBooleanDefaultIfNull(config.isAddMetadataKeyValuesAsKafkaHeaders(), false); toBytesCharset = config.getKafkaHeadersCharset() != null ? Charset.forName(config.getKafkaHeadersCharset()) : StandardCharsets.UTF_8; @@ -113,24 +121,25 @@ public class TbKafkaNode implements TbNode { public void onMsg(TbContext ctx, TbMsg msg) { String topic = TbNodeUtils.processPattern(config.getTopicPattern(), msg); String keyPattern = config.getKeyPattern(); + var tbMsg = ackIfNeeded(ctx, msg); try { if (initError != null) { - ctx.tellFailure(msg, new RuntimeException("Failed to initialize Kafka rule node producer: " + initError.getMessage())); + ctx.tellFailure(tbMsg, new RuntimeException("Failed to initialize Kafka rule node producer: " + initError.getMessage())); } else { ctx.getExternalCallExecutor().executeAsync(() -> { publish( ctx, - msg, + tbMsg, topic, keyPattern == null || keyPattern.isEmpty() ? null - : TbNodeUtils.processPattern(config.getKeyPattern(), msg) + : TbNodeUtils.processPattern(config.getKeyPattern(), tbMsg) ); return null; }); } } catch (Exception e) { - ctx.tellFailure(msg, e); + ctx.tellFailure(tbMsg, e); } } @@ -164,26 +173,24 @@ public class TbKafkaNode implements TbNode { private void processRecord(TbContext ctx, TbMsg msg, RecordMetadata metadata, Exception e) { if (e == null) { - TbMsg next = processResponse(ctx, msg, metadata); - ctx.tellNext(next, TbRelationTypes.SUCCESS); + tellSuccess(ctx, processResponse(msg, metadata)); } else { - TbMsg next = processException(ctx, msg, e); - ctx.tellFailure(next, e); + tellFailure(ctx, processException(msg, e), e); } } - private TbMsg processResponse(TbContext ctx, TbMsg origMsg, RecordMetadata recordMetadata) { + private TbMsg processResponse(TbMsg origMsg, RecordMetadata recordMetadata) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(OFFSET, String.valueOf(recordMetadata.offset())); metaData.putValue(PARTITION, String.valueOf(recordMetadata.partition())); metaData.putValue(TOPIC, recordMetadata.topic()); - return ctx.transformMsg(origMsg, origMsg.getType(), origMsg.getOriginator(), metaData, origMsg.getData()); + return TbMsg.transformMsgMetadata(origMsg, metaData); } - private TbMsg processException(TbContext ctx, TbMsg origMsg, Exception e) { + private TbMsg processException(TbMsg origMsg, Exception e) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(ERROR, e.getClass() + ": " + e.getMessage()); - return ctx.transformMsg(origMsg, origMsg.getType(), origMsg.getOriginator(), metaData, origMsg.getData()); + return TbMsg.transformMsgMetadata(origMsg, metaData); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java index 2af4978766..6869268517 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java @@ -15,11 +15,9 @@ */ package org.thingsboard.rule.engine.mail; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbEmail; @@ -28,24 +26,22 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; -import java.io.IOException; import java.util.HashMap; import java.util.Map; -import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; -import static org.thingsboard.rule.engine.mail.TbSendEmailNode.SEND_EMAIL_TYPE; - @Slf4j @RuleNode( type = ComponentType.TRANSFORMATION, name = "to email", configClazz = TbMsgToEmailNodeConfiguration.class, nodeDescription = "Transforms message to email message", - nodeDetails = "Transforms message to email message by populating email fields using values derived from message metadata. " + - "Set 'SEND_EMAIL' output message type.", + nodeDetails = "Transforms message to email message. If transformation completed successfully output message type will be set to SEND_EMAIL.

" + + "Output connections: Success, Failure.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbTransformationNodeToEmailConfig", icon = "email" @@ -56,12 +52,12 @@ public class TbMsgToEmailNode implements TbNode { private static final String DYNAMIC = "dynamic"; private TbMsgToEmailNodeConfiguration config; - private boolean isDynamicHtmlTemplate; + private boolean dynamicMailBodyType; @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { this.config = TbNodeUtils.convert(configuration, TbMsgToEmailNodeConfiguration.class); - this.isDynamicHtmlTemplate = DYNAMIC.equals(this.config.getMailBodyType()); + this.dynamicMailBodyType = DYNAMIC.equals(this.config.getMailBodyType()); } @Override @@ -69,31 +65,29 @@ public class TbMsgToEmailNode implements TbNode { try { TbEmail email = convert(msg); TbMsg emailMsg = buildEmailMsg(ctx, msg, email); - ctx.tellNext(emailMsg, SUCCESS); + ctx.tellNext(emailMsg, TbNodeConnectionType.SUCCESS); } catch (Exception ex) { log.warn("Can not convert message to email " + ex.getMessage()); ctx.tellFailure(msg, ex); } } - private TbMsg buildEmailMsg(TbContext ctx, TbMsg msg, TbEmail email) throws JsonProcessingException { + private TbMsg buildEmailMsg(TbContext ctx, TbMsg msg, TbEmail email) { String emailJson = JacksonUtil.toString(email); - return ctx.transformMsg(msg, SEND_EMAIL_TYPE, msg.getOriginator(), msg.getMetaData().copy(), emailJson); + return ctx.transformMsg(msg, TbMsgType.SEND_EMAIL, msg.getOriginator(), msg.getMetaData().copy(), emailJson); } - private TbEmail convert(TbMsg msg) throws IOException { + private TbEmail convert(TbMsg msg) { TbEmail.TbEmailBuilder builder = TbEmail.builder(); - builder.from(fromTemplate(this.config.getFromTemplate(), msg)); - builder.to(fromTemplate(this.config.getToTemplate(), msg)); - builder.cc(fromTemplate(this.config.getCcTemplate(), msg)); - builder.bcc(fromTemplate(this.config.getBccTemplate(), msg)); - if(isDynamicHtmlTemplate) { - builder.html(Boolean.parseBoolean(fromTemplate(this.config.getIsHtmlTemplate(), msg))); - } else { - builder.html(Boolean.parseBoolean(this.config.getMailBodyType())); - } - builder.subject(fromTemplate(this.config.getSubjectTemplate(), msg)); - builder.body(fromTemplate(this.config.getBodyTemplate(), msg)); + builder.from(fromTemplate(config.getFromTemplate(), msg)); + builder.to(fromTemplate(config.getToTemplate(), msg)); + builder.cc(fromTemplate(config.getCcTemplate(), msg)); + builder.bcc(fromTemplate(config.getBccTemplate(), msg)); + String htmlStr = dynamicMailBodyType ? + fromTemplate(config.getIsHtmlTemplate(), msg) : config.getMailBodyType(); + builder.html(Boolean.parseBoolean(htmlStr)); + builder.subject(fromTemplate(config.getSubjectTemplate(), msg)); + builder.body(fromTemplate(config.getBodyTemplate(), msg)); String imagesStr = msg.getMetaData().getValue(IMAGES); if (!StringUtils.isEmpty(imagesStr)) { Map imgMap = JacksonUtil.fromString(imagesStr, new TypeReference>() {}); @@ -103,11 +97,7 @@ public class TbMsgToEmailNode implements TbNode { } private String fromTemplate(String template, TbMsg msg) { - if (!StringUtils.isEmpty(template)) { - return TbNodeUtils.processPattern(template, msg); - } else { - return null; - } + return StringUtils.isNotEmpty(template) ? TbNodeUtils.processPattern(template, msg) : null; } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeConfiguration.java index 7b8ddefa7f..f31b5a3893 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeConfiguration.java @@ -19,7 +19,7 @@ import lombok.Data; import org.thingsboard.rule.engine.api.NodeConfiguration; @Data -public class TbMsgToEmailNodeConfiguration implements NodeConfiguration { +public class TbMsgToEmailNodeConfiguration implements NodeConfiguration { private String fromTemplate; private String toTemplate; @@ -28,16 +28,16 @@ public class TbMsgToEmailNodeConfiguration implements NodeConfiguration { private String subjectTemplate; private String bodyTemplate; private String isHtmlTemplate; - private String mailBodyType; + private String mailBodyType; // Plain Text -> false. HTML - true. Dynamic - value used from isHtmlTemplate. @Override public TbMsgToEmailNodeConfiguration defaultConfiguration() { - TbMsgToEmailNodeConfiguration configuration = new TbMsgToEmailNodeConfiguration(); - configuration.fromTemplate = "info@testmail.org"; - configuration.toTemplate = "${userEmail}"; - configuration.subjectTemplate = "Device ${deviceType} temperature high"; - configuration.bodyTemplate = "Device ${deviceName} has high temperature ${temp}"; - configuration.mailBodyType = "false"; + var configuration = new TbMsgToEmailNodeConfiguration(); + configuration.setFromTemplate("info@testmail.org"); + configuration.setToTemplate("${userEmail}"); + configuration.setSubjectTemplate("Device ${deviceType} temperature high"); + configuration.setBodyTemplate("Device ${deviceName} has high temperature $[temperature]"); + configuration.setMailBodyType("false"); return configuration; } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java index 50823a4c90..2f7aaf257d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java @@ -16,16 +16,17 @@ package org.thingsboard.rule.engine.mail; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.StringUtils; import org.springframework.mail.javamail.JavaMailSenderImpl; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbEmail; -import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.rule.engine.external.TbAbstractExternalNode; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; @@ -47,17 +48,17 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; configDirective = "tbExternalNodeSendEmailConfig", icon = "send" ) -public class TbSendEmailNode implements TbNode { +public class TbSendEmailNode extends TbAbstractExternalNode { private static final String MAIL_PROP = "mail."; - static final String SEND_EMAIL_TYPE = "SEND_EMAIL"; private TbSendEmailNodeConfiguration config; private JavaMailSenderImpl mailSender; @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { + super.init(ctx); + this.config = TbNodeUtils.convert(configuration, TbSendEmailNodeConfiguration.class); try { - this.config = TbNodeUtils.convert(configuration, TbSendEmailNodeConfiguration.class); if (!this.config.isUseSystemSmtpSettings()) { mailSender = createMailSender(); } @@ -69,14 +70,15 @@ public class TbSendEmailNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { try { - validateType(msg.getType()); + validateType(msg); TbEmail email = getEmail(msg); + var tbMsg = ackIfNeeded(ctx, msg); withCallback(ctx.getMailExecutor().executeAsync(() -> { - sendEmail(ctx, msg, email); + sendEmail(ctx, tbMsg, email); return null; }), - ok -> ctx.tellSuccess(msg), - fail -> ctx.tellFailure(msg, fail)); + ok -> tellSuccess(ctx, tbMsg), + fail -> tellFailure(ctx, tbMsg, fail)); } catch (Exception ex) { ctx.tellFailure(msg, ex); } @@ -98,8 +100,9 @@ public class TbSendEmailNode implements TbNode { return email; } - private void validateType(String type) { - if (!SEND_EMAIL_TYPE.equals(type)) { + private void validateType(TbMsg msg) { + if (!msg.isTypeOf(TbMsgType.SEND_EMAIL)) { + String type = msg.getType(); log.warn("Not expected msg type [{}] for SendEmail Node", type); throw new IllegalStateException("Not expected msg type " + type + " for SendEmail Node"); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java index 33692decc2..095612b4e1 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java @@ -19,6 +19,8 @@ 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.Data; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import net.objecthunter.exp4j.Expression; import net.objecthunter.exp4j.ExpressionBuilder; @@ -44,9 +46,10 @@ import java.math.BigDecimal; import java.math.RoundingMode; import java.util.List; import java.util.Optional; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Collectors; @@ -81,9 +84,8 @@ import static org.thingsboard.rule.engine.math.TbMathArgumentType.CONSTANT; ) public class TbMathNode implements TbNode { - private static final ConcurrentMap semaphores = new ConcurrentReferenceHashMap<>(); + private static final ConcurrentMap> locks = new ConcurrentReferenceHashMap<>(16, ConcurrentReferenceHashMap.ReferenceType.WEAK); private final ThreadLocal customExpression = new ThreadLocal<>(); - private TbMathNodeConfiguration config; private boolean msgBodyToJsonConversionRequired; @@ -108,51 +110,76 @@ public class TbMathNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { - var originator = msg.getOriginator(); - var originatorSemaphore = semaphores.computeIfAbsent(originator, tmp -> new Semaphore(1, true)); - boolean acquired = tryAcquire(originator, originatorSemaphore); + var semaphoreWithQueue = locks.computeIfAbsent(msg.getOriginator(), SemaphoreWithQueue::new); + semaphoreWithQueue.getQueue().add(new TbMsgTbContextBiFunction(msg, ctx, this::processMsgAsync)); - if (!acquired) { - ctx.tellFailure(msg, new RuntimeException("Failed to process message for originator synchronously")); - return; - } + tryProcessQueue(semaphoreWithQueue); + } - try { - var arguments = config.getArguments(); - Optional msgBodyOpt = convertMsgBodyIfRequired(msg); - var argumentValues = Futures.allAsList(arguments.stream() - .map(arg -> resolveArguments(ctx, msg, msgBodyOpt, arg)).collect(Collectors.toList())); - ListenableFuture resultMsgFuture = Futures.transformAsync(argumentValues, args -> - updateMsgAndDb(ctx, msg, msgBodyOpt, calculateResult(args)), ctx.getDbCallbackExecutor()); - DonAsynchron.withCallback(resultMsgFuture, resultMsg -> { - try { - ctx.tellSuccess(resultMsg); - } finally { - originatorSemaphore.release(); + void tryProcessQueue(SemaphoreWithQueue lockAndQueue) { + final Semaphore semaphore = lockAndQueue.getSemaphore(); + final Queue queue = lockAndQueue.getQueue(); + while (!queue.isEmpty()) { + // The semaphore have to be acquired before EACH poll and released before NEXT poll. + // Otherwise, some message will remain unprocessed in queue + if (!semaphore.tryAcquire()) { + return; + } + TbMsgTbContextBiFunction tbMsgTbContext = null; + try { + tbMsgTbContext = queue.poll(); + if (tbMsgTbContext == null) { + semaphore.release(); + continue; + } + final TbMsg msg = tbMsgTbContext.getMsg(); + if (!msg.getCallback().isMsgValid()) { + log.trace("[{}] Skipping non-valid message [{}]", lockAndQueue.getEntityId(), msg); + semaphore.release(); + continue; } - }, t -> { - try { - ctx.tellFailure(msg, t); - } finally { - originatorSemaphore.release(); + //DO PROCESSING + final TbContext ctx = tbMsgTbContext.getCtx(); + final ListenableFuture resultMsgFuture = tbMsgTbContext.getBiFunction().apply(ctx, msg); + DonAsynchron.withCallback(resultMsgFuture, resultMsg -> { + try { + ctx.tellSuccess(resultMsg); + } finally { + lockAndQueue.getSemaphore().release(); + tryProcessQueue(lockAndQueue); + } + }, t -> { + try { + ctx.tellFailure(msg, t); + } finally { + lockAndQueue.getSemaphore().release(); + tryProcessQueue(lockAndQueue); + } + }, ctx.getDbCallbackExecutor()); + } catch (Throwable t) { + semaphore.release(); + if (tbMsgTbContext == null) { // if no message polled, the loop become infinite, will throw exception + log.error("[{}] Failed to process TbMsgTbContext queue", lockAndQueue.getEntityId(), t); + throw t; } - }, ctx.getDbCallbackExecutor()); - } catch (Throwable e) { - originatorSemaphore.release(); - log.warn("[{}] Failed to process message: {}", originator, msg, e); - throw e; + TbMsg msg = tbMsgTbContext.getMsg(); + TbContext ctx = tbMsgTbContext.getCtx(); + log.warn("[{}] Failed to process message: {}", lockAndQueue.getEntityId(), msg, t); + ctx.tellFailure(msg, t); // you are not allowed to throw here, because queue will remain unprocessed + continue; // We are probably the last who process the queue. We have to continue poll until get successful callback or queue is empty + } + break; //submitted async exact one task. next poll will try on callback } } - private boolean tryAcquire(EntityId originator, Semaphore originatorSemaphore) { - boolean acquired; - try { - acquired = originatorSemaphore.tryAcquire(20, TimeUnit.SECONDS); - } catch (InterruptedException e) { - acquired = false; - log.debug("[{}] Failed to acquire semaphore", originator, e); - } - return acquired; + ListenableFuture processMsgAsync(TbContext ctx, TbMsg msg) { + var arguments = config.getArguments(); + Optional msgBodyOpt = convertMsgBodyIfRequired(msg); + var argumentValues = Futures.allAsList(arguments.stream() + .map(arg -> resolveArguments(ctx, msg, msgBodyOpt, arg)).collect(Collectors.toList())); + ListenableFuture resultMsgFuture = Futures.transformAsync(argumentValues, args -> + updateMsgAndDb(ctx, msg, msgBodyOpt, calculateResult(args)), ctx.getDbCallbackExecutor()); + return resultMsgFuture; } private ListenableFuture updateMsgAndDb(TbContext ctx, TbMsg msg, Optional msgBodyOpt, double result) { @@ -248,7 +275,7 @@ public class TbMathNode implements TbNode { } else { md.putValue(mathResultKey, Double.toString(toDoubleValue(mathResultDef, result))); } - return TbMsg.transformMsg(msg, md); + return TbMsg.transformMsgMetadata(msg, md); } private double calculateResult(List args) { @@ -347,7 +374,7 @@ public class TbMathNode implements TbNode { return function.apply(arg1.getValue(), arg2.getValue()); } - private ListenableFuture resolveArguments(TbContext ctx, TbMsg msg, Optional msgBodyOpt, TbMathArgument arg) { + ListenableFuture resolveArguments(TbContext ctx, TbMsg msg, Optional msgBodyOpt, TbMathArgument arg) { String argKey = getKeyFromTemplate(msg, arg.getType(), arg.getKey()); switch (arg.getType()) { case CONSTANT: @@ -402,4 +429,21 @@ public class TbMathNode implements TbNode { @Override public void destroy() { } + + @Data + @RequiredArgsConstructor + static public class SemaphoreWithQueue { + final EntityId entityId; + final Semaphore semaphore = new Semaphore(1); + final Queue queue = new ConcurrentLinkedQueue<>(); + } + + @Data + @RequiredArgsConstructor + static public class TbMsgTbContextBiFunction { + final TbMsg msg; + final TbContext ctx; + final BiFunction> biFunction; + } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNodeConfiguration.java index 1636898b8c..f4fccd3ae6 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNodeConfiguration.java @@ -32,9 +32,10 @@ public class TbMathNodeConfiguration implements NodeConfigurationSuccess, Other or Failure.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeCalculateDeltaConfig") public class CalculateDeltaNode implements TbNode { + private Map cache; private CalculateDeltaNodeConfiguration config; private TbContext ctx; @@ -66,7 +68,6 @@ public class CalculateDeltaNode implements TbNode { this.ctx = ctx; this.timeseriesService = ctx.getTimeseriesService(); this.useCache = config.isUseCache(); - if (useCache) { cache = new ConcurrentHashMap<>(); } @@ -74,51 +75,50 @@ public class CalculateDeltaNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { - if (msg.getType().equals(SessionMsgType.POST_TELEMETRY_REQUEST.name())) { - JsonNode json = JacksonUtil.toJsonNode(msg.getData()); - String inputKey = config.getInputValueKey(); - if (json.has(inputKey)) { - DonAsynchron.withCallback(getLastValue(msg.getOriginator()), - previousData -> { - double currentValue = json.get(inputKey).asDouble(); - long currentTs = msg.getMetaDataTs(); - - if (useCache) { - cache.put(msg.getOriginator(), new ValueWithTs(currentTs, currentValue)); - } - - BigDecimal delta = BigDecimal.valueOf(previousData != null ? currentValue - previousData.value : 0.0); - - if (config.isTellFailureIfDeltaIsNegative() && delta.doubleValue() < 0) { - ctx.tellNext(msg, TbRelationTypes.FAILURE); - return; - } - - - if (config.getRound() != null) { - delta = delta.setScale(config.getRound(), RoundingMode.HALF_UP); - } - - ObjectNode result = (ObjectNode) json; - if (delta.stripTrailingZeros().scale() > 0) { - result.put(config.getOutputValueKey(), delta.doubleValue()); - } else { - result.put(config.getOutputValueKey(), delta.longValueExact()); - } - - if (config.isAddPeriodBetweenMsgs()) { - long period = previousData != null ? currentTs - previousData.ts : 0; - result.put(config.getPeriodValueKey(), period); - } - ctx.tellSuccess(TbMsg.transformMsg(msg, msg.getType(), msg.getOriginator(), msg.getMetaData(), JacksonUtil.toString(result))); - }, - t -> ctx.tellFailure(msg, t), ctx.getDbCallbackExecutor()); - } else { - ctx.tellNext(msg, "Other"); - } - } else { - ctx.tellNext(msg, "Other"); + if (!msg.isTypeOf(TbMsgType.POST_TELEMETRY_REQUEST)) { + ctx.tellNext(msg, TbNodeConnectionType.OTHER); + return; + } + JsonNode json = JacksonUtil.toJsonNode(msg.getData()); + String inputKey = config.getInputValueKey(); + if (!json.has(inputKey)) { + ctx.tellNext(msg, TbNodeConnectionType.OTHER); + return; } + withCallback(getLastValue(msg.getOriginator()), + previousData -> { + double currentValue = json.get(inputKey).asDouble(); + long currentTs = msg.getMetaDataTs(); + + if (useCache) { + cache.put(msg.getOriginator(), new ValueWithTs(currentTs, currentValue)); + } + + BigDecimal delta = BigDecimal.valueOf(previousData != null ? currentValue - previousData.value : 0.0); + + if (config.isTellFailureIfDeltaIsNegative() && delta.doubleValue() < 0) { + ctx.tellFailure(msg, new IllegalArgumentException("Delta value is negative!")); + return; + } + + if (config.getRound() != null) { + delta = delta.setScale(config.getRound(), RoundingMode.HALF_UP); + } + + ObjectNode result = (ObjectNode) json; + if (delta.stripTrailingZeros().scale() > 0) { + result.put(config.getOutputValueKey(), delta.doubleValue()); + } else { + result.put(config.getOutputValueKey(), delta.longValueExact()); + } + + if (config.isAddPeriodBetweenMsgs()) { + long period = previousData != null ? currentTs - previousData.ts : 0; + result.put(config.getPeriodValueKey(), period); + } + ctx.tellSuccess(TbMsg.transformMsgData(msg, JacksonUtil.toString(result))); + }, + t -> ctx.tellFailure(msg, t), ctx.getDbCallbackExecutor()); } @Override @@ -128,18 +128,29 @@ public class CalculateDeltaNode implements TbNode { } } - private ListenableFuture fetchLatestValue(EntityId entityId) { + private ListenableFuture fetchLatestValueAsync(EntityId entityId) { return Futures.transform(timeseriesService.findLatest(ctx.getTenantId(), entityId, Collections.singletonList(config.getInputValueKey())), list -> extractValue(list.get(0)) , ctx.getDbCallbackExecutor()); } + private ValueWithTs fetchLatestValue(EntityId entityId) { + List tsKvEntries = timeseriesService.findLatestSync( + ctx.getTenantId(), + entityId, + Collections.singletonList(config.getInputValueKey())); + return extractValue(tsKvEntries.get(0)); + } + private ListenableFuture getLastValue(EntityId entityId) { - ValueWithTs latestValue; - if (useCache && (latestValue = cache.get(entityId)) != null) { + if (useCache) { + ValueWithTs latestValue; + if ((latestValue = cache.get(entityId)) == null) { + latestValue = fetchLatestValue(entityId); + } return Futures.immediateFuture(latestValue); } else { - return fetchLatestValue(entityId); + return fetchLatestValueAsync(entityId); } } @@ -181,4 +192,5 @@ public class CalculateDeltaNode implements TbNode { this.value = value; } } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeConfiguration.java index 6e7b77ad93..00ffa3305a 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeConfiguration.java @@ -22,6 +22,7 @@ import org.thingsboard.rule.engine.api.NodeConfiguration; @Data @JsonIgnoreProperties(ignoreUnknown = true) public class CalculateDeltaNodeConfiguration implements NodeConfiguration { + private String inputValueKey; private String outputValueKey; private boolean useCache; @@ -32,7 +33,7 @@ public class CalculateDeltaNodeConfiguration implements NodeConfiguration implements TbNode { +@Slf4j +public abstract class TbAbstractGetAttributesNode extends TbAbstractNodeWithFetchTo { private static final String VALUE = "value"; private static final String TS = "ts"; - - protected C config; - private boolean fetchToData; private boolean isTellFailureIfAbsent; private boolean getLatestValueWithTs; @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { - this.config = loadGetAttributesNodeConfig(configuration); - this.fetchToData = config.isFetchToData(); - this.getLatestValueWithTs = config.isGetLatestValueWithTs(); - this.isTellFailureIfAbsent = BooleanUtils.toBooleanDefaultIfNull(this.config.isTellFailureIfAbsent(), true); + super.init(ctx, configuration); + getLatestValueWithTs = config.isGetLatestValueWithTs(); + isTellFailureIfAbsent = BooleanUtils.toBooleanDefaultIfNull(config.isTellFailureIfAbsent(), true); } - protected abstract C loadGetAttributesNodeConfig(TbNodeConfiguration configuration) throws TbNodeException; - @Override public void onMsg(TbContext ctx, TbMsg msg) throws TbNodeException { - try { - withCallback( - findEntityIdAsync(ctx, msg), - entityId -> safePutAttributes(ctx, msg, entityId), - t -> ctx.tellFailure(msg, t), ctx.getDbCallbackExecutor()); - } catch (Throwable th) { - ctx.tellFailure(msg, th); - } + var msgDataAsObjectNode = TbMsgSource.DATA.equals(fetchTo) ? getMsgDataAsObjectNode(msg) : null; + withCallback( + findEntityIdAsync(ctx, msg), + entityId -> safePutAttributes(ctx, msg, msgDataAsObjectNode, entityId), + t -> ctx.tellFailure(msg, t), ctx.getDbCallbackExecutor()); } protected abstract ListenableFuture findEntityIdAsync(TbContext ctx, TbMsg msg); - private void safePutAttributes(TbContext ctx, TbMsg msg, T entityId) { - if (entityId == null || entityId.isNullUid()) { - ctx.tellNext(msg, FAILURE); - return; + protected TbPair upgradeRuleNodesWithOldPropertyToUseFetchTo( + JsonNode oldConfiguration, + String oldProperty, + String ifTrue, + String ifFalse + ) throws TbNodeException { + var newConfigObjectNode = (ObjectNode) oldConfiguration; + if (!newConfigObjectNode.has(oldProperty)) { + newConfigObjectNode.put(FETCH_TO_PROPERTY_NAME, TbMsgSource.METADATA.name()); + return new TbPair<>(true, newConfigObjectNode); } - JsonNode msgDataNode; - if (fetchToData) { - msgDataNode = JacksonUtil.toJsonNode(msg.getData()); - if (!msgDataNode.isObject()) { - ctx.tellFailure(msg, new IllegalArgumentException("Msg body is not an object!")); - return; - } - } else { - msgDataNode = null; + if (newConfigObjectNode.get(oldProperty).isNull()) { + newConfigObjectNode.remove(oldProperty); + newConfigObjectNode.put(FETCH_TO_PROPERTY_NAME, TbMsgSource.METADATA.name()); + return new TbPair<>(true, newConfigObjectNode); } - ConcurrentHashMap> failuresMap = new ConcurrentHashMap<>(); - ListenableFuture>>> allFutures = Futures.allAsList( - getLatestTelemetry(ctx, entityId, TbNodeUtils.processPatterns(config.getLatestTsKeyNames(), msg), failuresMap), - getAttrAsync(ctx, entityId, CLIENT_SCOPE, TbNodeUtils.processPatterns(config.getClientAttributeNames(), msg), failuresMap), - getAttrAsync(ctx, entityId, SHARED_SCOPE, TbNodeUtils.processPatterns(config.getSharedAttributeNames(), msg), failuresMap), - getAttrAsync(ctx, entityId, SERVER_SCOPE, TbNodeUtils.processPatterns(config.getServerAttributeNames(), msg), failuresMap) + return upgradeConfigurationToUseFetchTo(oldProperty, ifTrue, ifFalse, newConfigObjectNode); + } + + private void safePutAttributes(TbContext ctx, TbMsg msg, ObjectNode msgDataNode, T entityId) { + Set>> failuresPairSet = ConcurrentHashMap.newKeySet(); + var getKvEntryPairFutures = Futures.allAsList( + getLatestTelemetry(ctx, entityId, TbNodeUtils.processPatterns(config.getLatestTsKeyNames(), msg), failuresPairSet), + getAttrAsync(ctx, entityId, CLIENT_SCOPE, TbNodeUtils.processPatterns(config.getClientAttributeNames(), msg), failuresPairSet), + getAttrAsync(ctx, entityId, SHARED_SCOPE, TbNodeUtils.processPatterns(config.getSharedAttributeNames(), msg), failuresPairSet), + getAttrAsync(ctx, entityId, SERVER_SCOPE, TbNodeUtils.processPatterns(config.getServerAttributeNames(), msg), failuresPairSet) ); - withCallback(allFutures, futuresList -> { - TbMsgMetaData msgMetaData = msg.getMetaData().copy(); - futuresList.stream().filter(Objects::nonNull).forEach(kvEntriesMap -> { - kvEntriesMap.forEach((keyScope, kvEntryList) -> { - String prefix = getPrefix(keyScope); - kvEntryList.forEach(kvEntry -> { - String key = prefix + kvEntry.getKey(); - if (fetchToData) { - JacksonUtil.addKvEntry((ObjectNode) msgDataNode, kvEntry, key); - } else { - msgMetaData.putValue(key, kvEntry.getValueAsString()); - } - }); + withCallback(getKvEntryPairFutures, futuresList -> { + var msgMetaData = msg.getMetaData().copy(); + futuresList.stream().filter(Objects::nonNull).forEach(kvEntriesPair -> { + var keyScope = kvEntriesPair.getFirst(); + var kvEntryList = kvEntriesPair.getSecond(); + var prefix = getPrefix(keyScope); + kvEntryList.forEach(kvEntry -> { + String targetKey = prefix + kvEntry.getKey(); + enrichMessage(msgDataNode, msgMetaData, kvEntry, targetKey); }); }); - TbMsg outMsg = fetchToData ? - TbMsg.transformMsgData(msg, JacksonUtil.toString(msgDataNode)) : - TbMsg.transformMsg(msg, msgMetaData); - if (failuresMap.isEmpty()) { + TbMsg outMsg = transformMessage(msg, msgDataNode, msgMetaData); + if (failuresPairSet.isEmpty()) { ctx.tellSuccess(outMsg); } else { - ctx.tellFailure(outMsg, reportFailures(failuresMap)); + ctx.tellFailure(outMsg, reportFailures(failuresPairSet)); } - }, t -> ctx.tellFailure(msg, t), ctx.getDbCallbackExecutor()); + }, t -> ctx.tellFailure(msg, t), MoreExecutors.directExecutor()); } - private ListenableFuture>> getAttrAsync(TbContext ctx, EntityId entityId, String scope, List keys, ConcurrentHashMap> failuresMap) { + private ListenableFuture>> getAttrAsync( + TbContext ctx, + EntityId entityId, + String scope, + List keys, + Set>> failuresPairSet + ) { if (CollectionUtils.isEmpty(keys)) { return Futures.immediateFuture(null); } - ListenableFuture> attributeKvEntryListFuture = ctx.getAttributesService().find(ctx.getTenantId(), entityId, scope, keys); + var attributeKvEntryListFuture = ctx.getAttributesService().find(ctx.getTenantId(), entityId, scope, keys); return Futures.transform(attributeKvEntryListFuture, attributeKvEntryList -> { if (isTellFailureIfAbsent && attributeKvEntryList.size() != keys.size()) { - getNotExistingKeys(attributeKvEntryList, keys).forEach(key -> computeFailuresMap(scope, failuresMap, key)); + List nonExistentKeys = getNonExistentKeys(attributeKvEntryList, keys); + failuresPairSet.add(new TbPair<>(scope, nonExistentKeys)); } - Map> mapAttributeKvEntry = new HashMap<>(); - mapAttributeKvEntry.put(scope, attributeKvEntryList); - return mapAttributeKvEntry; - }, MoreExecutors.directExecutor()); + return new TbPair<>(scope, attributeKvEntryList); + }, ctx.getDbCallbackExecutor()); } - private ListenableFuture>> getLatestTelemetry(TbContext ctx, EntityId entityId, List keys, ConcurrentHashMap> failuresMap) { + private ListenableFuture>> getLatestTelemetry(TbContext ctx, EntityId entityId, List keys, Set>> failuresPairSet) { if (CollectionUtils.isEmpty(keys)) { return Futures.immediateFuture(null); } ListenableFuture> latestTelemetryFutures = ctx.getTimeseriesService().findLatest(ctx.getTenantId(), entityId, keys); return Futures.transform(latestTelemetryFutures, tsKvEntries -> { - List listTsKvEntry = new ArrayList<>(); + var listTsKvEntry = new ArrayList(); + var nonExistentKeys = new ArrayList(); tsKvEntries.forEach(tsKvEntry -> { if (tsKvEntry.getValue() == null) { if (isTellFailureIfAbsent) { - computeFailuresMap(LATEST_TS, failuresMap, tsKvEntry.getKey()); + nonExistentKeys.add(tsKvEntry.getKey()); } } else if (getLatestValueWithTs) { listTsKvEntry.add(getValueWithTs(tsKvEntry)); @@ -168,22 +163,23 @@ public abstract class TbAbstractGetAttributesNode> mapTsKvEntry = new HashMap<>(); - mapTsKvEntry.put(LATEST_TS, listTsKvEntry); - return mapTsKvEntry; - }, MoreExecutors.directExecutor()); + if (isTellFailureIfAbsent && !nonExistentKeys.isEmpty()) { + failuresPairSet.add(new TbPair<>(LATEST_TS, nonExistentKeys)); + } + return new TbPair<>(LATEST_TS, listTsKvEntry); + }, ctx.getDbCallbackExecutor()); } private TsKvEntry getValueWithTs(TsKvEntry tsKvEntry) { - ObjectMapper mapper = fetchToData ? JacksonUtil.OBJECT_MAPPER : JacksonUtil.ALLOW_UNQUOTED_FIELD_NAMES_MAPPER; - ObjectNode value = JacksonUtil.newObjectNode(mapper); + var mapper = TbMsgSource.DATA.equals(fetchTo) ? JacksonUtil.OBJECT_MAPPER : JacksonUtil.ALLOW_UNQUOTED_FIELD_NAMES_MAPPER; + var value = JacksonUtil.newObjectNode(mapper); value.put(TS, tsKvEntry.getTs()); JacksonUtil.addKvEntry(value, tsKvEntry, VALUE, mapper); return new BasicTsKvEntry(tsKvEntry.getTs(), new JsonDataEntry(tsKvEntry.getKey(), value.toString())); } private String getPrefix(String scope) { - String prefix = ""; + var prefix = ""; switch (scope) { case CLIENT_SCOPE: prefix = "cs_"; @@ -198,31 +194,20 @@ public abstract class TbAbstractGetAttributesNode getNotExistingKeys(List existingAttributesKvEntry, List allKeys) { + private List getNonExistentKeys(List existingAttributesKvEntry, List allKeys) { List existingKeys = existingAttributesKvEntry.stream().map(KvEntry::getKey).collect(Collectors.toList()); return allKeys.stream().filter(key -> !existingKeys.contains(key)).collect(Collectors.toList()); } - private void computeFailuresMap(String scope, ConcurrentHashMap> failuresMap, String key) { - List failures = failuresMap.computeIfAbsent(scope, k -> new ArrayList<>()); - failures.add(key); - } - - private RuntimeException reportFailures(ConcurrentHashMap> failuresMap) { - StringBuilder errorMessage = new StringBuilder("The following attribute/telemetry keys is not present in the DB: ").append("\n"); - if (failuresMap.containsKey(CLIENT_SCOPE)) { - errorMessage.append("\t").append("[" + CLIENT_SCOPE + "]:").append(failuresMap.get(CLIENT_SCOPE).toString()).append("\n"); - } - if (failuresMap.containsKey(SERVER_SCOPE)) { - errorMessage.append("\t").append("[" + SERVER_SCOPE + "]:").append(failuresMap.get(SERVER_SCOPE).toString()).append("\n"); - } - if (failuresMap.containsKey(SHARED_SCOPE)) { - errorMessage.append("\t").append("[" + SHARED_SCOPE + "]:").append(failuresMap.get(SHARED_SCOPE).toString()).append("\n"); - } - if (failuresMap.containsKey(LATEST_TS)) { - errorMessage.append("\t").append("[" + LATEST_TS + "]:").append(failuresMap.get(LATEST_TS).toString()).append("\n"); - } - failuresMap.clear(); + private RuntimeException reportFailures(Set>> failuresPairSet) { + var errorMessage = new StringBuilder("The following attribute/telemetry keys is not present in the DB: ").append("\n"); + failuresPairSet.forEach(failurePair -> { + String scope = failurePair.getFirst(); + List nonExistentKeys = failurePair.getSecond(); + errorMessage.append("\t").append("[").append(scope).append("]:").append(nonExistentKeys.toString()).append("\n"); + }); + failuresPairSet.clear(); return new RuntimeException(errorMessage.toString()); } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDataNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDataNode.java new file mode 100644 index 0000000000..003f5fd5ed --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDataNode.java @@ -0,0 +1,97 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.metadata; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.util.TbMsgSource; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.util.TbPair; +import org.thingsboard.server.common.msg.TbMsg; + +import static org.thingsboard.common.util.DonAsynchron.withCallback; + +@Slf4j +public abstract class TbAbstractGetEntityDataNode extends TbAbstractGetMappedDataNode { + + private final static String DATA_TO_FETCH_PROPERTY_NAME = "dataToFetch"; + private static final String OLD_DATA_TO_FETCH_PROPERTY_NAME = "telemetry"; + private final static String DATA_MAPPING_PROPERTY_NAME = "dataMapping"; + private static final String OLD_DATA_MAPPING_PROPERTY_NAME = "attrMapping"; + + private static final String DATA_TO_FETCH_VALIDATION_MSG = "DataToFetch property has invalid value: %s." + + " Only ATTRIBUTES and LATEST_TELEMETRY values supported!"; + + @Override + public void onMsg(TbContext ctx, TbMsg msg) { + var msgDataAsObjectNode = TbMsgSource.DATA.equals(fetchTo) ? getMsgDataAsObjectNode(msg) : null; + withCallback(findEntityAsync(ctx, msg.getOriginator()), + entityId -> processDataAndTell(ctx, msg, entityId, msgDataAsObjectNode), + t -> ctx.tellFailure(msg, t), ctx.getDbCallbackExecutor()); + } + + protected abstract ListenableFuture findEntityAsync(TbContext ctx, EntityId originator); + + protected void checkDataToFetchSupportedOrElseThrow(DataToFetch dataToFetch) throws TbNodeException { + if (dataToFetch == null || dataToFetch.equals(DataToFetch.FIELDS)) { + throw new TbNodeException(String.format(DATA_TO_FETCH_VALIDATION_MSG, dataToFetch)); + } + } + + protected void processDataAndTell(TbContext ctx, TbMsg msg, T entityId, ObjectNode msgDataAsJsonNode) { + DataToFetch dataToFetch = config.getDataToFetch(); + switch (dataToFetch) { + case ATTRIBUTES: + processAttributesKvEntryData(ctx, msg, entityId, msgDataAsJsonNode); + break; + case LATEST_TELEMETRY: + processTsKvEntryData(ctx, msg, entityId, msgDataAsJsonNode); + break; + case FIELDS: + processFieldsData(ctx, msg, entityId, msgDataAsJsonNode, true); + break; + } + } + + protected TbPair upgradeToUseFetchToAndDataToFetch(JsonNode oldConfiguration) throws TbNodeException { + var newConfigObjectNode = (ObjectNode) oldConfiguration; + if (!newConfigObjectNode.has(OLD_DATA_TO_FETCH_PROPERTY_NAME)) { + throw new TbNodeException("property to update: '" + OLD_DATA_TO_FETCH_PROPERTY_NAME + "' doesn't exists in configuration!"); + } + if (!newConfigObjectNode.has(OLD_DATA_MAPPING_PROPERTY_NAME)) { + throw new TbNodeException("property to update: '" + OLD_DATA_MAPPING_PROPERTY_NAME + "' doesn't exists in configuration!"); + } + newConfigObjectNode.set(DATA_MAPPING_PROPERTY_NAME, newConfigObjectNode.get(OLD_DATA_MAPPING_PROPERTY_NAME)); + newConfigObjectNode.remove(OLD_DATA_MAPPING_PROPERTY_NAME); + var value = newConfigObjectNode.get(OLD_DATA_TO_FETCH_PROPERTY_NAME).asText(); + if ("true".equals(value)) { + newConfigObjectNode.remove(OLD_DATA_TO_FETCH_PROPERTY_NAME); + newConfigObjectNode.put(DATA_TO_FETCH_PROPERTY_NAME, DataToFetch.LATEST_TELEMETRY.name()); + } else if ("false".equals(value)) { + newConfigObjectNode.remove(OLD_DATA_TO_FETCH_PROPERTY_NAME); + newConfigObjectNode.put(DATA_TO_FETCH_PROPERTY_NAME, DataToFetch.ATTRIBUTES.name()); + } else { + throw new TbNodeException("property to update: '" + OLD_DATA_TO_FETCH_PROPERTY_NAME + "' has unexpected value: " + value + ". Allowed values: true or false!"); + } + newConfigObjectNode.put(FETCH_TO_PROPERTY_NAME, TbMsgSource.METADATA.name()); + return new TbPair<>(true, newConfigObjectNode); + } + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDetailsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDetailsNode.java index e7b0a80930..e9b0cecd1a 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDetailsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDetailsNode.java @@ -15,167 +15,111 @@ */ package org.thingsboard.rule.engine.metadata; +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 com.google.gson.Gson; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.reflect.TypeToken; -import lombok.AllArgsConstructor; -import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.rule.engine.api.TbNode; -import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; -import org.thingsboard.rule.engine.util.EntityDetails; +import org.thingsboard.rule.engine.util.ContactBasedEntityDetails; +import org.thingsboard.rule.engine.util.TbMsgSource; import org.thingsboard.server.common.data.ContactBased; +import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; -import java.lang.reflect.Type; -import java.util.Map; +import java.util.List; import static org.thingsboard.common.util.DonAsynchron.withCallback; @Slf4j -public abstract class TbAbstractGetEntityDetailsNode implements TbNode { - - private static final Gson gson = new Gson(); - private static final JsonParser jsonParser = new JsonParser(); - private static final Type TYPE = new TypeToken>() { - }.getType(); - - protected C config; - - @Override - public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { - this.config = loadGetEntityDetailsNodeConfiguration(configuration); - } +public abstract class TbAbstractGetEntityDetailsNode extends TbAbstractNodeWithFetchTo { @Override public void onMsg(TbContext ctx, TbMsg msg) { - withCallback(getDetails(ctx, msg), + var msgDataAsObjectNode = TbMsgSource.DATA.equals(fetchTo) ? getMsgDataAsObjectNode(msg) : null; + withCallback(getDetails(ctx, msg, msgDataAsObjectNode), ctx::tellSuccess, t -> ctx.tellFailure(msg, t), ctx.getDbCallbackExecutor()); } - protected abstract C loadGetEntityDetailsNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException; - - protected abstract ListenableFuture getDetails(TbContext ctx, TbMsg msg); + protected abstract String getPrefix(); - protected abstract ListenableFuture getContactBasedListenableFuture(TbContext ctx, TbMsg msg); + protected abstract ListenableFuture> getContactBasedFuture(TbContext ctx, TbMsg msg); - protected MessageData getDataAsJson(TbMsg msg) { - if (this.config.isAddToMetadata()) { - return new MessageData(gson.toJsonTree(msg.getMetaData().getData(), TYPE), "metadata"); - } else { - return new MessageData(jsonParser.parse(msg.getData()), "data"); + protected void checkIfDetailsListIsNotEmptyOrElseThrow(List detailsList) throws TbNodeException { + if (detailsList == null || detailsList.isEmpty()) { + throw new TbNodeException("At least one entity detail should be selected!"); } } - protected ListenableFuture getTbMsgListenableFuture(TbContext ctx, TbMsg msg, MessageData messageData, String prefix) { - if (!this.config.getDetailsList().isEmpty()) { - ListenableFuture contactBasedListenableFuture = getContactBasedListenableFuture(ctx, msg); - ListenableFuture resultObject = addContactProperties(messageData.getData(), contactBasedListenableFuture, prefix); - return transformMsg(ctx, msg, resultObject, messageData); - } else { - return Futures.immediateFuture(msg); - } - } - - private ListenableFuture transformMsg(TbContext ctx, TbMsg msg, ListenableFuture propertiesFuture, MessageData messageData) { - return Futures.transformAsync(propertiesFuture, jsonElement -> { - if (jsonElement != null) { - if (messageData.getDataType().equals("metadata")) { - Map metadataMap = gson.fromJson(jsonElement.toString(), TYPE); - return Futures.immediateFuture(ctx.transformMsg(msg, msg.getType(), msg.getOriginator(), new TbMsgMetaData(metadataMap), msg.getData())); - } else { - return Futures.immediateFuture(ctx.transformMsg(msg, msg.getType(), msg.getOriginator(), msg.getMetaData(), gson.toJson(jsonElement))); - } - } else { - return Futures.immediateFuture(null); + private ListenableFuture getDetails(TbContext ctx, TbMsg msg, ObjectNode messageData) { + ListenableFuture> contactBasedFuture = getContactBasedFuture(ctx, msg); + return Futures.transformAsync(contactBasedFuture, contactBased -> { + if (contactBased == null) { + return Futures.immediateFuture(msg); } + var msgMetaData = msg.getMetaData().copy(); + fetchEntityDetailsToMsg(contactBased, messageData, msgMetaData); + return Futures.immediateFuture(transformMessage(msg, messageData, msgMetaData)); }, MoreExecutors.directExecutor()); } - private ListenableFuture addContactProperties(JsonElement data, ListenableFuture entityFuture, String prefix) { - return Futures.transformAsync(entityFuture, contactBased -> { - if (contactBased != null) { - JsonElement jsonElement = null; - for (EntityDetails entityDetails : this.config.getDetailsList()) { - jsonElement = setProperties(contactBased, data, entityDetails, prefix); - } - return Futures.immediateFuture(jsonElement); - } else { - return Futures.immediateFuture(null); + private void fetchEntityDetailsToMsg(ContactBased contactBased, ObjectNode messageData, TbMsgMetaData msgMetaData) { + String value = null; + for (var entityDetail : config.getDetailsList()) { + switch (entityDetail) { + case ID: + value = contactBased.getId().getId().toString(); + break; + case TITLE: + value = contactBased.getName(); + break; + case ADDRESS: + value = contactBased.getAddress(); + break; + case ADDRESS2: + value = contactBased.getAddress2(); + break; + case CITY: + value = contactBased.getCity(); + break; + case COUNTRY: + value = contactBased.getCountry(); + break; + case STATE: + value = contactBased.getState(); + break; + case EMAIL: + value = contactBased.getEmail(); + break; + case PHONE: + value = contactBased.getPhone(); + break; + case ZIP: + value = contactBased.getZip(); + break; + case ADDITIONAL_INFO: + if (contactBased.getAdditionalInfo().hasNonNull("description")) { + value = contactBased.getAdditionalInfo().get("description").asText(); + } + break; } - }, MoreExecutors.directExecutor()); - } - - private JsonElement setProperties(ContactBased entity, JsonElement data, EntityDetails entityDetails, String prefix) { - JsonObject dataAsObject = data.getAsJsonObject(); - switch (entityDetails) { - case ID: - dataAsObject.addProperty(prefix + "id", entity.getId().toString()); - break; - case TITLE: - dataAsObject.addProperty(prefix + "title", entity.getName()); - break; - case ADDRESS: - if (entity.getAddress() != null) { - dataAsObject.addProperty(prefix + "address", entity.getAddress()); - } - break; - case ADDRESS2: - if (entity.getAddress2() != null) { - dataAsObject.addProperty(prefix + "address2", entity.getAddress2()); - } - break; - case CITY: - if (entity.getCity() != null) dataAsObject.addProperty(prefix + "city", entity.getCity()); - break; - case COUNTRY: - if (entity.getCountry() != null) - dataAsObject.addProperty(prefix + "country", entity.getCountry()); - break; - case STATE: - if (entity.getState() != null) { - dataAsObject.addProperty(prefix + "state", entity.getState()); - } - break; - case EMAIL: - if (entity.getEmail() != null) { - dataAsObject.addProperty(prefix + "email", entity.getEmail()); - } - break; - case PHONE: - if (entity.getPhone() != null) { - dataAsObject.addProperty(prefix + "phone", entity.getPhone()); - } - break; - case ZIP: - if (entity.getZip() != null) { - dataAsObject.addProperty(prefix + "zip", entity.getZip()); - } - break; - case ADDITIONAL_INFO: - if (entity.getAdditionalInfo().hasNonNull("description")) { - dataAsObject.addProperty(prefix + "additionalInfo", entity.getAdditionalInfo().get("description").asText()); - } - break; + if (value == null) { + continue; + } + setDetail(entityDetail.getRuleEngineName(), value, messageData, msgMetaData); } - return dataAsObject; } - @Data - @AllArgsConstructor - private static class MessageData { - private JsonElement data; - private String dataType; + private void setDetail(String property, String value, ObjectNode messageData, TbMsgMetaData msgMetaData) { + String fieldName = getPrefix() + property; + if (TbMsgSource.METADATA.equals(fetchTo)) { + msgMetaData.putValue(fieldName, value); + } else if (TbMsgSource.DATA.equals(fetchTo)) { + messageData.put(fieldName, value); + } } - } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDetailsNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDetailsNodeConfiguration.java index b772bf34e4..d7f24d39b1 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDetailsNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetEntityDetailsNodeConfiguration.java @@ -16,16 +16,15 @@ package org.thingsboard.rule.engine.metadata; import lombok.Data; -import org.thingsboard.rule.engine.util.EntityDetails; +import lombok.EqualsAndHashCode; +import org.thingsboard.rule.engine.util.ContactBasedEntityDetails; import java.util.List; @Data -public abstract class TbAbstractGetEntityDetailsNodeConfiguration { +@EqualsAndHashCode(callSuper = true) +public abstract class TbAbstractGetEntityDetailsNodeConfiguration extends TbAbstractFetchToNodeConfiguration { - - private List detailsList; - - private boolean addToMetadata; + private List detailsList; } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetMappedDataNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetMappedDataNode.java new file mode 100644 index 0000000000..8362918e6d --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetMappedDataNode.java @@ -0,0 +1,154 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.metadata; + +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.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.rule.engine.util.EntitiesFieldsAsyncLoader; +import org.thingsboard.rule.engine.util.TbMsgSource; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.thingsboard.common.util.DonAsynchron.withCallback; +import static org.thingsboard.server.common.data.DataConstants.SERVER_SCOPE; + +@Slf4j +public abstract class TbAbstractGetMappedDataNode extends TbAbstractNodeWithFetchTo { + + protected void checkIfMappingIsNotEmptyOrElseThrow(Map dataMapping) throws TbNodeException { + if (dataMapping == null || dataMapping.isEmpty()) { + throw new TbNodeException("At least one mapping entry should be specified!"); + } + } + + protected void processFieldsData(TbContext ctx, TbMsg msg, T entityId, ObjectNode msgDataAsJsonNode, boolean ignoreNullStrings) { + var mappingsMap = processFieldsMappingPatterns(msg); + withCallback(getEntityFieldsAsync(ctx, entityId, mappingsMap, ignoreNullStrings), + data -> putFieldsDataAndTell(ctx, msg, msgDataAsJsonNode, data), + t -> ctx.tellFailure(msg, t), + MoreExecutors.directExecutor()); + } + + protected void processAttributesKvEntryData(TbContext ctx, TbMsg msg, T entityId, ObjectNode msgDataAsJsonNode) { + var mappingsMap = processKvEntryMappingPatterns(msg); + var sourceKeys = List.copyOf(mappingsMap.keySet()); + withCallback(getAttributesAsync(ctx, entityId, sourceKeys), + data -> putKvEntryDataAndTell(ctx, msg, data, mappingsMap, msgDataAsJsonNode), + t -> ctx.tellFailure(msg, t), + MoreExecutors.directExecutor()); + } + + protected void processTsKvEntryData(TbContext ctx, TbMsg msg, T entityId, ObjectNode msgDataAsJsonNode) { + var mappingsMap = processKvEntryMappingPatterns(msg); + var sourceKeys = List.copyOf(mappingsMap.keySet()); + withCallback(getLatestTelemetryAsync(ctx, entityId, sourceKeys), + data -> putKvEntryDataAndTell(ctx, msg, data, mappingsMap, msgDataAsJsonNode), + t -> ctx.tellFailure(msg, t), + MoreExecutors.directExecutor()); + } + + private void putFieldsDataAndTell(TbContext ctx, TbMsg msg, ObjectNode msgDataAsJsonNode, Map targetKeysToSourceValuesMap) { + TbMsgMetaData msgMetaData = msg.getMetaData().copy(); + for (var entry : targetKeysToSourceValuesMap.entrySet()) { + var targetKeyName = entry.getKey(); + var sourceFieldValue = entry.getValue(); + if (TbMsgSource.DATA.equals(fetchTo)) { + msgDataAsJsonNode.put(targetKeyName, sourceFieldValue); + } else if (TbMsgSource.METADATA.equals(fetchTo)) { + msgMetaData.putValue(targetKeyName, sourceFieldValue); + } + } + TbMsg outMsg = transformMessage(msg, msgDataAsJsonNode, msgMetaData); + ctx.tellSuccess(outMsg); + } + + private void putKvEntryDataAndTell(TbContext ctx, TbMsg msg, List data, Map map, ObjectNode msgData) { + var msgMetaData = msg.getMetaData().copy(); + for (KvEntry entry : data) { + String targetKey = map.get(entry.getKey()); + enrichMessage(msgData, msgMetaData, entry, targetKey); + } + ctx.tellSuccess(transformMessage(msg, msgData, msgMetaData)); + } + + private Map processFieldsMappingPatterns(TbMsg msg) { + var mappingsMap = new HashMap(); + config.getDataMapping().forEach((sourceField, targetKey) -> { + String patternProcessedTargetKey = TbNodeUtils.processPattern(targetKey, msg); + mappingsMap.put(sourceField, patternProcessedTargetKey); + }); + return mappingsMap; + } + + private Map processKvEntryMappingPatterns(TbMsg msg) { + var mappingsMap = new HashMap(); + config.getDataMapping().forEach((sourceKey, targetKey) -> { + String patternProcessedSourceKey = TbNodeUtils.processPattern(sourceKey, msg); + String patternProcessedTargetKey = TbNodeUtils.processPattern(targetKey, msg); + mappingsMap.put(patternProcessedSourceKey, patternProcessedTargetKey); + }); + return mappingsMap; + } + + private ListenableFuture> getEntityFieldsAsync(TbContext ctx, EntityId entityId, Map mappingsMap, boolean ignoreNullStrings) { + return Futures.transform(EntitiesFieldsAsyncLoader.findAsync(ctx, entityId), + fieldsData -> { + var targetKeysToSourceValuesMap = new HashMap(); + for (var mappingEntry : mappingsMap.entrySet()) { + var sourceFieldName = mappingEntry.getKey(); + var targetKeyName = mappingEntry.getValue(); + var sourceFieldValue = fieldsData.getFieldValue(sourceFieldName, ignoreNullStrings); + if (sourceFieldValue != null) { + targetKeysToSourceValuesMap.put(targetKeyName, sourceFieldValue); + } + } + return targetKeysToSourceValuesMap; + }, ctx.getDbCallbackExecutor() + ); + } + + private ListenableFuture> getAttributesAsync(TbContext ctx, EntityId entityId, List attrKeys) { + var latest = ctx.getAttributesService().find(ctx.getTenantId(), entityId, SERVER_SCOPE, attrKeys); + return Futures.transform(latest, l -> + l.stream() + .map(i -> (KvEntry) i) + .collect(Collectors.toList()), + ctx.getDbCallbackExecutor()); + } + + private ListenableFuture> getLatestTelemetryAsync(TbContext ctx, EntityId entityId, List timeseriesKeys) { + var latest = ctx.getTimeseriesService().findLatest(ctx.getTenantId(), entityId, timeseriesKeys); + return Futures.transform(latest, l -> + l.stream() + .map(i -> (KvEntry) i) + .collect(Collectors.toList()), + ctx.getDbCallbackExecutor()); + } + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractNodeWithFetchTo.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractNodeWithFetchTo.java new file mode 100644 index 0000000000..ef149aa4ad --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractNodeWithFetchTo.java @@ -0,0 +1,126 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.metadata; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.util.concurrent.AsyncFunction; +import com.google.common.util.concurrent.Futures; +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNode; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.util.TbMsgSource; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.util.TbPair; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; + +import java.util.Arrays; +import java.util.NoSuchElementException; + +@Slf4j +public abstract class TbAbstractNodeWithFetchTo implements TbNode { + + protected final static String FETCH_TO_PROPERTY_NAME = "fetchTo"; + + protected C config; + protected TbMsgSource fetchTo; + + @Override + public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { + config = loadNodeConfiguration(configuration); + if (config.getFetchTo() == null) { + throw new TbNodeException("FetchTo option can't be null! Allowed values: " + Arrays.toString(TbMsgSource.values())); + } + fetchTo = config.getFetchTo(); + } + + protected abstract C loadNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException; + + protected AsyncFunction checkIfEntityIsPresentOrThrow(String message) { + return id -> { + if (id == null || id.isNullUid()) { + return Futures.immediateFailedFuture(new NoSuchElementException(message)); + } + return Futures.immediateFuture(id); + }; + } + + protected ObjectNode getMsgDataAsObjectNode(TbMsg msg) { + var msgDataNode = JacksonUtil.toJsonNode(msg.getData()); + if (msgDataNode == null || !msgDataNode.isObject()) { + throw new IllegalArgumentException("Message body is not an object!"); + } + return (ObjectNode) msgDataNode; + } + + protected void enrichMessage(ObjectNode msgData, TbMsgMetaData metaData, KvEntry kvEntry, String targetKey) { + if (TbMsgSource.DATA.equals(fetchTo)) { + JacksonUtil.addKvEntry(msgData, kvEntry, targetKey); + } else if (TbMsgSource.METADATA.equals(fetchTo)) { + metaData.putValue(targetKey, kvEntry.getValueAsString()); + } + } + + protected TbMsg transformMessage(TbMsg msg, ObjectNode msgDataNode, TbMsgMetaData msgMetaData) { + switch (fetchTo) { + case DATA: + return TbMsg.transformMsgData(msg, JacksonUtil.toString(msgDataNode)); + case METADATA: + return TbMsg.transformMsgMetadata(msg, msgMetaData); + default: + log.debug("Unexpected FetchTo value: {}. Allowed values: {}", fetchTo, TbMsgSource.values()); + return msg; + } + } + + protected TbPair upgradeRuleNodesWithOldPropertyToUseFetchTo( + JsonNode oldConfiguration, + String oldProperty, + String ifTrue, + String ifFalse + ) throws TbNodeException { + var newConfig = (ObjectNode) oldConfiguration; + if (!newConfig.has(oldProperty)) { + throw new TbNodeException("property to update: '" + oldProperty + "' doesn't exists in configuration!"); + } + return upgradeConfigurationToUseFetchTo(oldProperty, ifTrue, ifFalse, newConfig); + } + + protected TbPair upgradeConfigurationToUseFetchTo( + String oldProperty, String ifTrue, + String ifFalse, ObjectNode newConfig + ) throws TbNodeException { + var value = newConfig.get(oldProperty).asText(); + if ("true".equals(value)) { + newConfig.remove(oldProperty); + newConfig.put(FETCH_TO_PROPERTY_NAME, ifTrue); + return new TbPair<>(true, newConfig); + } else if ("false".equals(value)) { + newConfig.remove(oldProperty); + newConfig.put(FETCH_TO_PROPERTY_NAME, ifFalse); + return new TbPair<>(true, newConfig); + } else { + throw new TbNodeException("property to update: '" + oldProperty + "' has unexpected value: " + + value + ". Allowed values: true or false!"); + } + } + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbEntityGetAttrNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbEntityGetAttrNode.java deleted file mode 100644 index 8031939c7d..0000000000 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbEntityGetAttrNode.java +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.rule.engine.metadata; - -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.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.rule.engine.api.TbNode; -import org.thingsboard.rule.engine.api.TbNodeConfiguration; -import org.thingsboard.rule.engine.api.TbNodeException; -import org.thingsboard.rule.engine.api.util.TbNodeUtils; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.KvEntry; -import org.thingsboard.server.common.data.kv.TsKvEntry; -import org.thingsboard.server.common.msg.TbMsg; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import static org.thingsboard.common.util.DonAsynchron.withCallback; -import static org.thingsboard.rule.engine.api.TbRelationTypes.FAILURE; -import static org.thingsboard.server.common.data.DataConstants.SERVER_SCOPE; - -@Slf4j -public abstract class TbEntityGetAttrNode implements TbNode { - - private TbGetEntityAttrNodeConfiguration config; - - @Override - public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { - this.config = TbNodeUtils.convert(configuration, TbGetEntityAttrNodeConfiguration.class); - } - - @Override - public void onMsg(TbContext ctx, TbMsg msg) { - try { - withCallback(findEntityAsync(ctx, msg.getOriginator()), - entityId -> safeGetAttributes(ctx, msg, entityId), - t -> ctx.tellFailure(msg, t), ctx.getDbCallbackExecutor()); - } catch (Throwable th) { - ctx.tellFailure(msg, th); - } - } - - private void safeGetAttributes(TbContext ctx, TbMsg msg, T entityId) { - if (entityId == null || entityId.isNullUid()) { - ctx.tellNext(msg, FAILURE); - return; - } - - Map mappingsMap = new HashMap<>(); - config.getAttrMapping().forEach((key, value) -> { - String processPatternKey = TbNodeUtils.processPattern(key, msg); - String processPatternValue = TbNodeUtils.processPattern(value, msg); - mappingsMap.put(processPatternKey, processPatternValue); - }); - - List keys = List.copyOf(mappingsMap.keySet()); - withCallback(config.isTelemetry() ? getLatestTelemetry(ctx, entityId, keys) : getAttributesAsync(ctx, entityId, keys), - attributes -> putAttributesAndTell(ctx, msg, attributes, mappingsMap), - t -> ctx.tellFailure(msg, t), ctx.getDbCallbackExecutor()); - } - - private ListenableFuture> getAttributesAsync(TbContext ctx, EntityId entityId, List attrKeys) { - ListenableFuture> latest = ctx.getAttributesService().find(ctx.getTenantId(), entityId, SERVER_SCOPE, attrKeys); - return Futures.transform(latest, l -> - l.stream().map(i -> (KvEntry) i).collect(Collectors.toList()), MoreExecutors.directExecutor()); - } - - private ListenableFuture> getLatestTelemetry(TbContext ctx, EntityId entityId, List timeseriesKeys) { - ListenableFuture> latest = ctx.getTimeseriesService().findLatest(ctx.getTenantId(), entityId, timeseriesKeys); - return Futures.transform(latest, l -> - l.stream().map(i -> (KvEntry) i).collect(Collectors.toList()), MoreExecutors.directExecutor()); - } - - - private void putAttributesAndTell(TbContext ctx, TbMsg msg, List attributes, Map map) { - attributes.forEach(r -> { - String attrName = map.get(r.getKey()); - msg.getMetaData().putValue(attrName, r.getValueAsString()); - }); - ctx.tellSuccess(msg); - } - - protected abstract ListenableFuture findEntityAsync(TbContext ctx, EntityId originator); - - public void setConfig(TbGetEntityAttrNodeConfiguration config) { - this.config = config; - } - -} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNode.java index 2aa3b7a837..c4770d625f 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNode.java @@ -16,23 +16,20 @@ package org.thingsboard.rule.engine.metadata; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.rule.engine.util.TbMsgSource; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.plugin.ComponentType; -import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; +import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.TbMsg; -import org.thingsboard.server.common.msg.TbMsgMetaData; import java.util.concurrent.ExecutionException; @@ -40,60 +37,68 @@ import java.util.concurrent.ExecutionException; @RuleNode( type = ComponentType.ENRICHMENT, name = "fetch device credentials", + version = 1, configClazz = TbFetchDeviceCredentialsNodeConfiguration.class, - nodeDescription = "Enrich the message body or metadata with the device credentials", - nodeDetails = "Adds credentialsType and credentials properties to the message metadata if the " + - "configuration parameter fetchToMetadata is set to true, otherwise, adds properties " + - "to the message data. If originator type is not DEVICE or rule node failed to get device credentials " + - "- send Message via Failure chain, otherwise Success chain is used.", + nodeDescription = "Adds device credentials to the message or message metadata", + nodeDetails = "if message originator type is Device and device credentials was successfully fetched, " + + "rule node enriches message or message metadata with credentialsType and credentials properties. " + + "Useful when you need to fetch device credentials and use them for further message processing. " + + "For example, use device credentials to interact with external systems.

" + + "Output connections: Success, Failure.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeFetchDeviceCredentialsConfig") -public class TbFetchDeviceCredentialsNode implements TbNode { +public class TbFetchDeviceCredentialsNode extends TbAbstractNodeWithFetchTo { private static final String CREDENTIALS = "credentials"; private static final String CREDENTIALS_TYPE = "credentialsType"; - TbFetchDeviceCredentialsNodeConfiguration config; - boolean fetchToMetadata; - @Override - public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { - this.config = TbNodeUtils.convert(configuration, TbFetchDeviceCredentialsNodeConfiguration.class); - this.fetchToMetadata = config.isFetchToMetadata(); + protected TbFetchDeviceCredentialsNodeConfiguration loadNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException { + return TbNodeUtils.convert(configuration, TbFetchDeviceCredentialsNodeConfiguration.class); } @Override public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException { - EntityId originator = msg.getOriginator(); + var originator = msg.getOriginator(); + var msgDataAsObjectNode = TbMsgSource.DATA.equals(fetchTo) ? getMsgDataAsObjectNode(msg) : null; if (!EntityType.DEVICE.equals(originator.getEntityType())) { ctx.tellFailure(msg, new RuntimeException("Unsupported originator type: " + originator.getEntityType() + "!")); return; } - DeviceId deviceId = new DeviceId(msg.getOriginator().getId()); - DeviceCredentials deviceCredentials = ctx.getDeviceCredentialsService().findDeviceCredentialsByDeviceId(ctx.getTenantId(), deviceId); + + var deviceId = new DeviceId(msg.getOriginator().getId()); + var deviceCredentials = ctx.getDeviceCredentialsService().findDeviceCredentialsByDeviceId(ctx.getTenantId(), deviceId); if (deviceCredentials == null) { ctx.tellFailure(msg, new RuntimeException("Failed to get Device Credentials for device: " + deviceId + "!")); return; } - - TbMsg transformedMsg; - DeviceCredentialsType credentialsType = deviceCredentials.getCredentialsType(); - JsonNode credentialsInfo = ctx.getDeviceCredentialsService().toCredentialsInfo(deviceCredentials); - if (fetchToMetadata) { - TbMsgMetaData metaData = msg.getMetaData(); + var credentialsType = deviceCredentials.getCredentialsType(); + var credentialsInfo = ctx.getDeviceCredentialsService().toCredentialsInfo(deviceCredentials); + var metaData = msg.getMetaData().copy(); + if (TbMsgSource.METADATA.equals(fetchTo)) { metaData.putValue(CREDENTIALS_TYPE, credentialsType.name()); if (credentialsType.equals(DeviceCredentialsType.ACCESS_TOKEN) || credentialsType.equals(DeviceCredentialsType.X509_CERTIFICATE)) { metaData.putValue(CREDENTIALS, credentialsInfo.asText()); } else { metaData.putValue(CREDENTIALS, JacksonUtil.toString(credentialsInfo)); } - transformedMsg = TbMsg.transformMsg(msg, msg.getType(), originator, metaData, msg.getData()); - } else { - ObjectNode data = (ObjectNode) JacksonUtil.toJsonNode(msg.getData()); - data.put(CREDENTIALS_TYPE, credentialsType.name()); - data.set(CREDENTIALS, credentialsInfo); - transformedMsg = TbMsg.transformMsg(msg, msg.getType(), originator, msg.getMetaData(), JacksonUtil.toString(data)); + } else if (TbMsgSource.DATA.equals(fetchTo)) { + msgDataAsObjectNode.put(CREDENTIALS_TYPE, credentialsType.name()); + msgDataAsObjectNode.set(CREDENTIALS, credentialsInfo); } + TbMsg transformedMsg = transformMessage(msg, msgDataAsObjectNode, metaData); ctx.tellSuccess(transformedMsg); } + + @Override + public TbPair upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException { + return fromVersion == 0 ? + upgradeRuleNodesWithOldPropertyToUseFetchTo( + oldConfiguration, + "fetchToMetadata", + TbMsgSource.METADATA.name(), + TbMsgSource.DATA.name()) : + new TbPair<>(false, oldConfiguration); + } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeConfiguration.java index 66fddb0770..70712b4370 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeConfiguration.java @@ -17,18 +17,20 @@ package org.thingsboard.rule.engine.metadata; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; +import lombok.EqualsAndHashCode; import org.thingsboard.rule.engine.api.NodeConfiguration; +import org.thingsboard.rule.engine.util.TbMsgSource; @Data +@EqualsAndHashCode(callSuper = true) @JsonIgnoreProperties(ignoreUnknown = true) -public class TbFetchDeviceCredentialsNodeConfiguration implements NodeConfiguration { - - private boolean fetchToMetadata; +public class TbFetchDeviceCredentialsNodeConfiguration extends TbAbstractFetchToNodeConfiguration implements NodeConfiguration { @Override public TbFetchDeviceCredentialsNodeConfiguration defaultConfiguration() { - TbFetchDeviceCredentialsNodeConfiguration configuration = new TbFetchDeviceCredentialsNodeConfiguration(); - configuration.setFetchToMetadata(true); + var configuration = new TbFetchDeviceCredentialsNodeConfiguration(); + configuration.setFetchTo(TbMsgSource.METADATA); return configuration; } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java index 7e42105e5d..9fbe5eb666 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java @@ -15,6 +15,7 @@ */ package org.thingsboard.rule.engine.metadata; +import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; @@ -23,8 +24,10 @@ import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.rule.engine.util.TbMsgSource; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.plugin.ComponentType; +import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.TbMsg; /** @@ -32,19 +35,20 @@ import org.thingsboard.server.common.msg.TbMsg; */ @Slf4j @RuleNode(type = ComponentType.ENRICHMENT, - name = "originator attributes", - configClazz = TbGetAttributesNodeConfiguration.class, - nodeDescription = "Enrich the message body or metadata with the originator attributes and/or timeseries data", - nodeDetails = "If Attributes enrichment configured, CLIENT/SHARED/SERVER attributes are added into Message data/metadata " + - "with specific prefix: cs/shared/ss. Latest telemetry value added into Message data/metadata without prefix. " + - "To access those attributes in other nodes this template can be used " + - "metadata.cs_temperature or metadata.shared_limit ", + name = "originator attributes", + configClazz = TbGetAttributesNodeConfiguration.class, + version = 1, + nodeDescription = "Adds attributes and/or latest timeseries data for the message originator to the message or message metadata", + nodeDetails = "Useful when you need to retrieve some attributes or the latest telemetry readings from the message originator " + + "that are not included in the incoming message to use them for further message processing. " + + "For example to filter messages based on the threshold value stored in the attributes.

" + + "Output connections: Success, Failure.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeOriginatorAttributesConfig") public class TbGetAttributesNode extends TbAbstractGetAttributesNode { @Override - protected TbGetAttributesNodeConfiguration loadGetAttributesNodeConfig(TbNodeConfiguration configuration) throws TbNodeException { + protected TbGetAttributesNodeConfiguration loadNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException { return TbNodeUtils.convert(configuration, TbGetAttributesNodeConfiguration.class); } @@ -53,4 +57,15 @@ public class TbGetAttributesNode extends TbAbstractGetAttributesNode upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException { + return fromVersion == 0 ? + upgradeRuleNodesWithOldPropertyToUseFetchTo( + oldConfiguration, + "fetchToData", + TbMsgSource.DATA.name(), + TbMsgSource.METADATA.name()) : + new TbPair<>(false, oldConfiguration); + } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeConfiguration.java index e587fb9c7a..fe15ff817f 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeConfiguration.java @@ -16,7 +16,9 @@ package org.thingsboard.rule.engine.metadata; import lombok.Data; +import lombok.EqualsAndHashCode; import org.thingsboard.rule.engine.api.NodeConfiguration; +import org.thingsboard.rule.engine.util.TbMsgSource; import java.util.Collections; import java.util.List; @@ -25,7 +27,8 @@ import java.util.List; * Created by ashvayka on 19.01.18. */ @Data -public class TbGetAttributesNodeConfiguration implements NodeConfiguration { +@EqualsAndHashCode(callSuper = true) +public class TbGetAttributesNodeConfiguration extends TbAbstractFetchToNodeConfiguration implements NodeConfiguration { private List clientAttributeNames; private List sharedAttributeNames; @@ -35,18 +38,18 @@ public class TbGetAttributesNodeConfiguration implements NodeConfiguration" + - "Useful when you store some parameters on the customer level and would like to use them for message processing.", + name = "customer attributes", + configClazz = TbGetEntityDataNodeConfiguration.class, + version = 1, + nodeDescription = "Adds message originator customer attributes or latest telemetry into message or message metadata", + nodeDetails = "Useful in multi-customer solutions where each customer has a different configuration or threshold set " + + "that is stored as customer attributes or telemetry data and used for dynamic message filtering, transformation, " + + "or actions such as alarm creation if the threshold is exceeded.

" + + "Output connections: Success, Failure.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeCustomerAttributesConfig") -public class TbGetCustomerAttributeNode extends TbEntityGetAttrNode { +public class TbGetCustomerAttributeNode extends TbAbstractGetEntityDataNode { + + private static final String CUSTOMER_NOT_FOUND_MESSAGE = "Failed to find customer for entity with id: %s and type: %s"; + + @Override + protected TbGetEntityDataNodeConfiguration loadNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException { + var config = TbNodeUtils.convert(configuration, TbGetEntityDataNodeConfiguration.class); + checkIfMappingIsNotEmptyOrElseThrow(config.getDataMapping()); + checkDataToFetchSupportedOrElseThrow(config.getDataToFetch()); + return config; + } @Override protected ListenableFuture findEntityAsync(TbContext ctx, EntityId originator) { - return EntitiesCustomerIdAsyncLoader.findEntityIdAsync(ctx, originator); + return Futures.transformAsync(EntitiesCustomerIdAsyncLoader.findEntityIdAsync(ctx, originator), + checkIfEntityIsPresentOrThrow(String.format(CUSTOMER_NOT_FOUND_MESSAGE, originator.getId(), originator.getEntityType().getNormalName())), + ctx.getDbCallbackExecutor() + ); + } + + @Override + public TbPair upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException { + return fromVersion == 0 ? upgradeToUseFetchToAndDataToFetch(oldConfiguration) : new TbPair<>(false, oldConfiguration); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java index bf9c689b30..f0b391ca54 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java @@ -15,90 +15,107 @@ */ package org.thingsboard.rule.engine.metadata; +import com.fasterxml.jackson.databind.JsonNode; 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.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; -import org.thingsboard.server.common.data.ContactBased; +import org.thingsboard.rule.engine.util.TbMsgSource; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.HasCustomerId; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.plugin.ComponentType; +import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.TbMsg; +import java.util.NoSuchElementException; + @Slf4j @RuleNode(type = ComponentType.ENRICHMENT, name = "customer details", configClazz = TbGetCustomerDetailsNodeConfiguration.class, - nodeDescription = "Enrich the message body or metadata with the corresponding customer details: title, address, email, phone, etc.", - nodeDetails = "If checkbox: Add selected details to the message metadata is selected, existing fields will be added to the message metadata instead of message data.

" + - "Note: only Device, Asset, and Entity View type are allowed.

" + - "If the originator of the message is not assigned to Customer, or originator type is not supported - Message will be forwarded to Failure chain, otherwise, Success chain will be used.", + version = 1, + nodeDescription = "Adds message originator customer details into message or message metadata", + nodeDetails = "Useful in multi-customer solutions where we need dynamically use customer contact information " + + "such as email, phone, address, etc., for notifications via email, SMS, and other notification providers.

" + + "Output connections: Success, Failure.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeEntityDetailsConfig") -public class TbGetCustomerDetailsNode extends TbAbstractGetEntityDetailsNode { +public class TbGetCustomerDetailsNode extends TbAbstractGetEntityDetailsNode { private static final String CUSTOMER_PREFIX = "customer_"; @Override - protected TbGetCustomerDetailsNodeConfiguration loadGetEntityDetailsNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException { - return TbNodeUtils.convert(configuration, TbGetCustomerDetailsNodeConfiguration.class); + protected TbGetCustomerDetailsNodeConfiguration loadNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException { + var config = TbNodeUtils.convert(configuration, TbGetCustomerDetailsNodeConfiguration.class); + checkIfDetailsListIsNotEmptyOrElseThrow(config.getDetailsList()); + return config; } @Override - protected ListenableFuture getDetails(TbContext ctx, TbMsg msg) { - return getTbMsgListenableFuture(ctx, msg, getDataAsJson(msg), CUSTOMER_PREFIX); + protected String getPrefix() { + return CUSTOMER_PREFIX; } @Override - protected ListenableFuture getContactBasedListenableFuture(TbContext ctx, TbMsg msg) { - return getCustomer(ctx, msg); - } - - private ListenableFuture getCustomer(TbContext ctx, TbMsg msg) { - ListenableFuture entityFuture; - switch (msg.getOriginator().getEntityType()) { // TODO: use EntityServiceRegistry + protected ListenableFuture getContactBasedFuture(TbContext ctx, TbMsg msg) { + switch (msg.getOriginator().getEntityType()) { case DEVICE: - entityFuture = Futures.immediateFuture(ctx.getDeviceService().findDeviceById(ctx.getTenantId(), (DeviceId) msg.getOriginator())); - break; + return Futures.transformAsync(ctx.getDeviceService().findDeviceByIdAsync(ctx.getTenantId(), new DeviceId(msg.getOriginator().getId())), + device -> getCustomerFuture(ctx, device, msg.getOriginator()), ctx.getDbCallbackExecutor()); case ASSET: - entityFuture = ctx.getAssetService().findAssetByIdAsync(ctx.getTenantId(), (AssetId) msg.getOriginator()); - break; + return Futures.transformAsync(ctx.getAssetService().findAssetByIdAsync(ctx.getTenantId(), new AssetId(msg.getOriginator().getId())), + asset -> getCustomerFuture(ctx, asset, msg.getOriginator()), ctx.getDbCallbackExecutor()); case ENTITY_VIEW: - entityFuture = ctx.getEntityViewService().findEntityViewByIdAsync(ctx.getTenantId(), (EntityViewId) msg.getOriginator()); - break; + return Futures.transformAsync(ctx.getEntityViewService().findEntityViewByIdAsync(ctx.getTenantId(), new EntityViewId(msg.getOriginator().getId())), + entityView -> getCustomerFuture(ctx, entityView, msg.getOriginator()), ctx.getDbCallbackExecutor()); case USER: - entityFuture = ctx.getUserService().findUserByIdAsync(ctx.getTenantId(), (UserId) msg.getOriginator()); - break; + return Futures.transformAsync(ctx.getUserService().findUserByIdAsync(ctx.getTenantId(), new UserId(msg.getOriginator().getId())), + user -> getCustomerFuture(ctx, user, msg.getOriginator()), ctx.getDbCallbackExecutor()); case EDGE: - entityFuture = ctx.getEdgeService().findEdgeByIdAsync(ctx.getTenantId(), (EdgeId) msg.getOriginator()); - break; + return Futures.transformAsync(ctx.getEdgeService().findEdgeByIdAsync(ctx.getTenantId(), new EdgeId(msg.getOriginator().getId())), + edge -> getCustomerFuture(ctx, edge, msg.getOriginator()), ctx.getDbCallbackExecutor()); default: - throw new RuntimeException(msg.getOriginator().getEntityType().getNormalName() + " entities not supported"); + return Futures.immediateFailedFuture(new NoSuchElementException("Entity with entityType '" + msg.getOriginator().getEntityType() + "' is not supported.")); } - return Futures.transformAsync(entityFuture, entity -> { - if (entity != null) { - if (!entity.getCustomerId().isNullUid()) { - return ctx.getCustomerService().findCustomerByIdAsync(ctx.getTenantId(), entity.getCustomerId()); - } else { - throw new RuntimeException(msg.getOriginator().getEntityType().getNormalName() + - (entity instanceof HasName ? " with name '" + ((HasName) entity).getName() + "'" : "") - + " is not assigned to Customer"); + } + + private ListenableFuture getCustomerFuture(TbContext ctx, HasCustomerId hasCustomerId, EntityId originator) { + if (hasCustomerId == null) { + return Futures.immediateFuture(null); + } else { + if (hasCustomerId.getCustomerId() == null || hasCustomerId.getCustomerId().isNullUid()) { + if (hasCustomerId instanceof HasName) { + var hasName = (HasName) hasCustomerId; + throw new RuntimeException(originator.getEntityType().getNormalName() + " with name '" + hasName.getName() + "' is not assigned to Customer!"); } + throw new RuntimeException(originator.getEntityType().getNormalName() + " with id '" + originator + "' is not assigned to Customer!"); } else { - return Futures.immediateFuture(null); + return ctx.getCustomerService().findCustomerByIdAsync(ctx.getTenantId(), hasCustomerId.getCustomerId()); } - }, MoreExecutors.directExecutor()); + } + } + + @Override + public TbPair upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException { + return fromVersion == 0 ? + upgradeRuleNodesWithOldPropertyToUseFetchTo( + oldConfiguration, + "addToMetadata", + TbMsgSource.METADATA.name(), + TbMsgSource.DATA.name()) : + new TbPair<>(false, oldConfiguration); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeConfiguration.java index 0d74d49942..370e7cd5a3 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeConfiguration.java @@ -16,18 +16,22 @@ package org.thingsboard.rule.engine.metadata; import lombok.Data; +import lombok.EqualsAndHashCode; import org.thingsboard.rule.engine.api.NodeConfiguration; +import org.thingsboard.rule.engine.util.TbMsgSource; import java.util.Collections; @Data +@EqualsAndHashCode(callSuper = true) public class TbGetCustomerDetailsNodeConfiguration extends TbAbstractGetEntityDetailsNodeConfiguration implements NodeConfiguration { - @Override public TbGetCustomerDetailsNodeConfiguration defaultConfiguration() { - TbGetCustomerDetailsNodeConfiguration configuration = new TbGetCustomerDetailsNodeConfiguration(); + var configuration = new TbGetCustomerDetailsNodeConfiguration(); configuration.setDetailsList(Collections.emptyList()); + configuration.setFetchTo(TbMsgSource.DATA); return configuration; } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNode.java index b0a221e46e..ac88005f51 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNode.java @@ -15,6 +15,8 @@ */ package org.thingsboard.rule.engine.metadata; +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.thingsboard.rule.engine.api.RuleNode; @@ -23,31 +25,51 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.util.EntitiesRelatedDeviceIdAsyncLoader; +import org.thingsboard.rule.engine.util.TbMsgSource; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.plugin.ComponentType; +import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.TbMsg; @Slf4j @RuleNode(type = ComponentType.ENRICHMENT, name = "related device attributes", configClazz = TbGetDeviceAttrNodeConfiguration.class, - nodeDescription = "Add Originators Related Device Attributes and Latest Telemetry value into Message Data or Metadata", - nodeDetails = "If Attributes enrichment configured, CLIENT/SHARED/SERVER attributes are added into Message data/metadata " + - "with specific prefix: cs/shared/ss. Latest telemetry value added into Message data/metadata without prefix. " + - "To access those attributes in other nodes this template can be used " + - "metadata.cs_temperature or metadata.shared_limit ", + version = 1, + nodeDescription = "Add originators related device attributes and/or latest telemetry values into message or message metadata", + nodeDetails = "Related device lookup based on the configured relation query. " + + "If multiple related devices are found, only first device is used for message enrichment, other entities are discarded. " + + "Useful when you need to retrieve attributes and/or latest telemetry values from device that has a relation " + + "to the message originator and use them for further message processing.

" + + "Output connections: Success, Failure.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeDeviceAttributesConfig") public class TbGetDeviceAttrNode extends TbAbstractGetAttributesNode { + private static final String RELATED_DEVICE_NOT_FOUND_MESSAGE = "Failed to find related device to message originator using relation query specified in the configuration!"; + @Override - protected TbGetDeviceAttrNodeConfiguration loadGetAttributesNodeConfig(TbNodeConfiguration configuration) throws TbNodeException { + protected TbGetDeviceAttrNodeConfiguration loadNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException { return TbNodeUtils.convert(configuration, TbGetDeviceAttrNodeConfiguration.class); } @Override protected ListenableFuture findEntityIdAsync(TbContext ctx, TbMsg msg) { - return EntitiesRelatedDeviceIdAsyncLoader.findDeviceAsync(ctx, msg.getOriginator(), config.getDeviceRelationsQuery()); + return Futures.transformAsync( + EntitiesRelatedDeviceIdAsyncLoader.findDeviceAsync(ctx, msg.getOriginator(), config.getDeviceRelationsQuery()), + checkIfEntityIsPresentOrThrow(RELATED_DEVICE_NOT_FOUND_MESSAGE), + ctx.getDbCallbackExecutor()); + } + + @Override + public TbPair upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException { + return fromVersion == 0 ? + upgradeRuleNodesWithOldPropertyToUseFetchTo( + oldConfiguration, + "fetchToData", + TbMsgSource.DATA.name(), + TbMsgSource.METADATA.name()) : + new TbPair<>(false, oldConfiguration); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeConfiguration.java index ac5b98134a..e6be83eb0c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeConfiguration.java @@ -16,29 +16,32 @@ package org.thingsboard.rule.engine.metadata; import lombok.Data; +import lombok.EqualsAndHashCode; import org.thingsboard.rule.engine.data.DeviceRelationsQuery; +import org.thingsboard.rule.engine.util.TbMsgSource; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntitySearchDirection; import java.util.Collections; @Data +@EqualsAndHashCode(callSuper = true) public class TbGetDeviceAttrNodeConfiguration extends TbGetAttributesNodeConfiguration { private DeviceRelationsQuery deviceRelationsQuery; @Override public TbGetDeviceAttrNodeConfiguration defaultConfiguration() { - TbGetDeviceAttrNodeConfiguration configuration = new TbGetDeviceAttrNodeConfiguration(); + var configuration = new TbGetDeviceAttrNodeConfiguration(); configuration.setClientAttributeNames(Collections.emptyList()); configuration.setSharedAttributeNames(Collections.emptyList()); configuration.setServerAttributeNames(Collections.emptyList()); configuration.setLatestTsKeyNames(Collections.emptyList()); configuration.setTellFailureIfAbsent(true); configuration.setGetLatestValueWithTs(false); - configuration.setFetchToData(false); + configuration.setFetchTo(TbMsgSource.METADATA); - DeviceRelationsQuery deviceRelationsQuery = new DeviceRelationsQuery(); + var deviceRelationsQuery = new DeviceRelationsQuery(); deviceRelationsQuery.setDirection(EntitySearchDirection.FROM); deviceRelationsQuery.setMaxLevel(1); deviceRelationsQuery.setRelationType(EntityRelation.CONTAINS_TYPE); @@ -48,4 +51,5 @@ public class TbGetDeviceAttrNodeConfiguration extends TbGetAttributesNodeConfigu return configuration; } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetEntityAttrNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetEntityAttrNodeConfiguration.java deleted file mode 100644 index 8661ffefe5..0000000000 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetEntityAttrNodeConfiguration.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.rule.engine.metadata; - -import lombok.Data; -import org.thingsboard.rule.engine.api.NodeConfiguration; - -import java.util.HashMap; -import java.util.Map; - -@Data -public class TbGetEntityAttrNodeConfiguration implements NodeConfiguration { - - private Map attrMapping; - private boolean isTelemetry = false; - - @Override - public TbGetEntityAttrNodeConfiguration defaultConfiguration() { - TbGetEntityAttrNodeConfiguration configuration = new TbGetEntityAttrNodeConfiguration(); - Map attrMapping = new HashMap<>(); - attrMapping.putIfAbsent("temperature", "tempo"); - configuration.setAttrMapping(attrMapping); - configuration.setTelemetry(false); - return configuration; - } -} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetEntityDataNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetEntityDataNodeConfiguration.java new file mode 100644 index 0000000000..ec57cba3d2 --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetEntityDataNodeConfiguration.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.metadata; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.rule.engine.api.NodeConfiguration; +import org.thingsboard.rule.engine.util.TbMsgSource; + +import java.util.HashMap; + +@Data +@EqualsAndHashCode(callSuper = true) +public class TbGetEntityDataNodeConfiguration extends TbGetMappedDataNodeConfiguration implements NodeConfiguration { + + private DataToFetch dataToFetch; + + @Override + public TbGetEntityDataNodeConfiguration defaultConfiguration() { + var configuration = new TbGetEntityDataNodeConfiguration(); + var dataMapping = new HashMap(); + dataMapping.putIfAbsent("alarmThreshold", "threshold"); + configuration.setDataMapping(dataMapping); + configuration.setDataToFetch(DataToFetch.ATTRIBUTES); + configuration.setFetchTo(TbMsgSource.METADATA); + return configuration; + } + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetMappedDataNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetMappedDataNodeConfiguration.java new file mode 100644 index 0000000000..68b8f0ff31 --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetMappedDataNodeConfiguration.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.metadata; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Map; + +@Data +@EqualsAndHashCode(callSuper = true) +public abstract class TbGetMappedDataNodeConfiguration extends TbAbstractFetchToNodeConfiguration { + + private Map dataMapping; + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsConfiguration.java index b39c342154..a5c96ada25 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsConfiguration.java @@ -16,25 +16,29 @@ package org.thingsboard.rule.engine.metadata; import lombok.Data; +import lombok.EqualsAndHashCode; import org.thingsboard.rule.engine.api.NodeConfiguration; +import org.thingsboard.rule.engine.util.TbMsgSource; import java.util.HashMap; import java.util.Map; @Data -public class TbGetOriginatorFieldsConfiguration implements NodeConfiguration { +@EqualsAndHashCode(callSuper = true) +public class TbGetOriginatorFieldsConfiguration extends TbGetMappedDataNodeConfiguration implements NodeConfiguration { - private Map fieldsMapping; private boolean ignoreNullStrings; @Override public TbGetOriginatorFieldsConfiguration defaultConfiguration() { - TbGetOriginatorFieldsConfiguration configuration = new TbGetOriginatorFieldsConfiguration(); - Map fieldsMapping = new HashMap<>(); - fieldsMapping.put("name", "originatorName"); - fieldsMapping.put("type", "originatorType"); - configuration.setFieldsMapping(fieldsMapping); + var configuration = new TbGetOriginatorFieldsConfiguration(); + var dataMapping = new HashMap(); + dataMapping.put("name", "originatorName"); + dataMapping.put("type", "originatorType"); + configuration.setDataMapping(dataMapping); configuration.setIgnoreNullStrings(false); + configuration.setFetchTo(TbMsgSource.METADATA); return configuration; } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java index ca314e00c0..cb7877d99a 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java @@ -15,22 +15,21 @@ */ package org.thingsboard.rule.engine.metadata; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; -import org.thingsboard.rule.engine.util.EntitiesFieldsAsyncLoader; +import org.thingsboard.rule.engine.util.TbMsgSource; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.plugin.ComponentType; +import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.TbMsg; -import static org.thingsboard.common.util.DonAsynchron.withCallback; +import java.util.concurrent.ExecutionException; /** * Created by ashvayka on 19.01.18. @@ -39,47 +38,44 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; @RuleNode(type = ComponentType.ENRICHMENT, name = "originator fields", configClazz = TbGetOriginatorFieldsConfiguration.class, - nodeDescription = "Add Message Originator fields values into Message Metadata", - nodeDetails = "Will fetch fields values specified in mapping. If specified field is not part of originator fields it will be ignored.", + version = 1, + nodeDescription = "Adds message originator fields values into message or message metadata", + nodeDetails = "Fetches fields values specified in the mapping. If specified field is not part of originator fields it will be ignored. " + + "Useful when you need to retrieve originator fields and use them for further message processing.

" + + "Output connections: Success, Failure.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeOriginatorFieldsConfig") -public class TbGetOriginatorFieldsNode implements TbNode { +public class TbGetOriginatorFieldsNode extends TbAbstractGetMappedDataNode { - private TbGetOriginatorFieldsConfiguration config; - private boolean ignoreNullStrings; + protected final static String DATA_MAPPING_PROPERTY_NAME = "dataMapping"; + protected static final String OLD_DATA_MAPPING_PROPERTY_NAME = "fieldsMapping"; @Override - public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { - config = TbNodeUtils.convert(configuration, TbGetOriginatorFieldsConfiguration.class); - ignoreNullStrings = config.isIgnoreNullStrings(); + protected TbGetOriginatorFieldsConfiguration loadNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException { + var config = TbNodeUtils.convert(configuration, TbGetOriginatorFieldsConfiguration.class); + checkIfMappingIsNotEmptyOrElseThrow(config.getDataMapping()); + return config; } @Override - public void onMsg(TbContext ctx, TbMsg msg) { - try { - withCallback(putEntityFields(ctx, msg.getOriginator(), msg), - i -> ctx.tellSuccess(msg), t -> ctx.tellFailure(msg, t), ctx.getDbCallbackExecutor()); - } catch (Throwable th) { - ctx.tellFailure(msg, th); - } + public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException { + var msgDataAsJsonNode = TbMsgSource.DATA.equals(fetchTo) ? getMsgDataAsObjectNode(msg) : null; + processFieldsData(ctx, msg, msg.getOriginator(), msgDataAsJsonNode, config.isIgnoreNullStrings()); } - private ListenableFuture putEntityFields(TbContext ctx, EntityId entityId, TbMsg msg) { - if (config.getFieldsMapping().isEmpty()) { - return Futures.immediateFuture(null); - } else { - return Futures.transform(EntitiesFieldsAsyncLoader.findAsync(ctx, entityId), - data -> { - config.getFieldsMapping().forEach((field, metaKey) -> { - String val = data.getFieldValue(field, ignoreNullStrings); - if (val != null) { - msg.getMetaData().putValue(metaKey, val); - } - }); - return null; - }, MoreExecutors.directExecutor() - ); + @Override + public TbPair upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException { + if (fromVersion == 0) { + var newConfigObjectNode = (ObjectNode) oldConfiguration; + if (!newConfigObjectNode.has(OLD_DATA_MAPPING_PROPERTY_NAME)) { + throw new TbNodeException("property to update: '" + OLD_DATA_MAPPING_PROPERTY_NAME + "' doesn't exists in configuration!"); + } + newConfigObjectNode.set(DATA_MAPPING_PROPERTY_NAME, newConfigObjectNode.get(OLD_DATA_MAPPING_PROPERTY_NAME)); + newConfigObjectNode.remove(OLD_DATA_MAPPING_PROPERTY_NAME); + newConfigObjectNode.put(FETCH_TO_PROPERTY_NAME, TbMsgSource.METADATA.name()); + return new TbPair<>(true, newConfigObjectNode); } + return new TbPair<>(false, oldConfiguration); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttrNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttrNodeConfiguration.java deleted file mode 100644 index 489e04f60e..0000000000 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttrNodeConfiguration.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.rule.engine.metadata; - -import lombok.Data; -import org.thingsboard.rule.engine.data.RelationsQuery; -import org.thingsboard.server.common.data.relation.EntityRelation; -import org.thingsboard.server.common.data.relation.EntitySearchDirection; -import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -@Data -public class TbGetRelatedAttrNodeConfiguration extends TbGetEntityAttrNodeConfiguration { - - private RelationsQuery relationsQuery; - - @Override - public TbGetRelatedAttrNodeConfiguration defaultConfiguration() { - TbGetRelatedAttrNodeConfiguration configuration = new TbGetRelatedAttrNodeConfiguration(); - Map attrMapping = new HashMap<>(); - attrMapping.putIfAbsent("temperature", "tempo"); - configuration.setAttrMapping(attrMapping); - configuration.setTelemetry(false); - - RelationsQuery relationsQuery = new RelationsQuery(); - relationsQuery.setDirection(EntitySearchDirection.FROM); - relationsQuery.setMaxLevel(1); - RelationEntityTypeFilter relationEntityTypeFilter = new RelationEntityTypeFilter(EntityRelation.CONTAINS_TYPE, Collections.emptyList()); - relationsQuery.setFilters(Collections.singletonList(relationEntityTypeFilter)); - configuration.setRelationsQuery(relationsQuery); - - return configuration; - } -} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java index b7ef24f36b..50e75c8cc0 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java @@ -15,7 +15,10 @@ */ package org.thingsboard.rule.engine.metadata; +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; @@ -24,33 +27,54 @@ import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.util.EntitiesRelatedEntityIdAsyncLoader; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.plugin.ComponentType; +import org.thingsboard.server.common.data.util.TbPair; +import java.util.Arrays; + +@Slf4j @RuleNode( type = ComponentType.ENRICHMENT, - name="related attributes", - configClazz = TbGetRelatedAttrNodeConfiguration.class, - nodeDescription = "Add Originators Related Entity Attributes or Latest Telemetry into Message Metadata", - nodeDetails = "Related Entity found using configured relation direction and Relation Type. " + - "If multiple Related Entities are found, only first Entity is used for attributes enrichment, other entities are discarded. " + - "If Attributes enrichment configured, server scope attributes are added into Message metadata. " + - "If Latest Telemetry enrichment configured, latest telemetry added into metadata. " + - "To access those attributes in other nodes this template can be used " + - "metadata.temperature.", + name = "related entity data", + configClazz = TbGetRelatedDataNodeConfiguration.class, + version = 1, + nodeDescription = "Adds originators related entity attributes or latest telemetry or fields into message or message metadata", + nodeDetails = "Related entity lookup based on the configured relation query. " + + "If multiple related entities are found, only first entity is used for message enrichment, other entities are discarded. " + + "Useful when you need to retrieve data from an entity that has a relation to the message originator and use them for further message processing.

" + + "Output connections: Success, Failure.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeRelatedAttributesConfig") +public class TbGetRelatedAttributeNode extends TbAbstractGetEntityDataNode { -public class TbGetRelatedAttributeNode extends TbEntityGetAttrNode { + private static final String RELATED_ENTITY_NOT_FOUND_MESSAGE = "Failed to find related entity to message originator using relation query specified in the configuration!"; - private TbGetRelatedAttrNodeConfiguration config; + @Override + public TbGetRelatedDataNodeConfiguration loadNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException { + var config = TbNodeUtils.convert(configuration, TbGetRelatedDataNodeConfiguration.class); + checkIfMappingIsNotEmptyOrElseThrow(config.getDataMapping()); + checkDataToFetchSupportedOrElseThrow(config.getDataToFetch()); + return config; + } @Override - public void init(TbContext context, TbNodeConfiguration configuration) throws TbNodeException { - this.config = TbNodeUtils.convert(configuration, TbGetRelatedAttrNodeConfiguration.class); - setConfig(config); + public ListenableFuture findEntityAsync(TbContext ctx, EntityId originator) { + var relatedAttrConfig = (TbGetRelatedDataNodeConfiguration) config; + return Futures.transformAsync( + EntitiesRelatedEntityIdAsyncLoader.findEntityAsync(ctx, originator, relatedAttrConfig.getRelationsQuery()), + checkIfEntityIsPresentOrThrow(RELATED_ENTITY_NOT_FOUND_MESSAGE), + ctx.getDbCallbackExecutor()); } @Override - protected ListenableFuture findEntityAsync(TbContext ctx, EntityId originator) { - return EntitiesRelatedEntityIdAsyncLoader.findEntityAsync(ctx, originator, config.getRelationsQuery()); + protected void checkDataToFetchSupportedOrElseThrow(DataToFetch dataToFetch) throws TbNodeException { + if (dataToFetch == null) { + throw new TbNodeException("DataToFetch property cannot be null! Supported values are: " + Arrays.toString(DataToFetch.values())); + } } + + @Override + public TbPair upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException { + return fromVersion == 0 ? upgradeToUseFetchToAndDataToFetch(oldConfiguration) : new TbPair<>(false, oldConfiguration); + } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedDataNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedDataNodeConfiguration.java new file mode 100644 index 0000000000..4cb0723365 --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedDataNodeConfiguration.java @@ -0,0 +1,54 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.metadata; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.rule.engine.data.RelationsQuery; +import org.thingsboard.rule.engine.util.TbMsgSource; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; + +import java.util.Collections; +import java.util.HashMap; + +@Data +@EqualsAndHashCode(callSuper = true) +public class TbGetRelatedDataNodeConfiguration extends TbGetEntityDataNodeConfiguration { + + private RelationsQuery relationsQuery; + + @Override + public TbGetRelatedDataNodeConfiguration defaultConfiguration() { + var configuration = new TbGetRelatedDataNodeConfiguration(); + var dataMapping = new HashMap(); + dataMapping.putIfAbsent("serialNumber", "sn"); + configuration.setDataMapping(dataMapping); + configuration.setDataToFetch(DataToFetch.ATTRIBUTES); + configuration.setFetchTo(TbMsgSource.METADATA); + + var relationsQuery = new RelationsQuery(); + var relationEntityTypeFilter = new RelationEntityTypeFilter(EntityRelation.CONTAINS_TYPE, Collections.emptyList()); + relationsQuery.setDirection(EntitySearchDirection.FROM); + relationsQuery.setMaxLevel(1); + relationsQuery.setFilters(Collections.singletonList(relationEntityTypeFilter)); + configuration.setRelationsQuery(relationsQuery); + + return configuration; + } + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java index b43aeb4e4b..1bf1b07530 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java @@ -37,16 +37,13 @@ import org.thingsboard.server.common.data.kv.ReadTsKvQuery; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import static org.thingsboard.rule.engine.metadata.TbGetTelemetryNodeConfiguration.FETCH_MODE_ALL; -import static org.thingsboard.rule.engine.metadata.TbGetTelemetryNodeConfiguration.FETCH_MODE_FIRST; -import static org.thingsboard.rule.engine.metadata.TbGetTelemetryNodeConfiguration.MAX_FETCH_SIZE; - /** * Created by mshvayka on 04.09.18. */ @@ -54,13 +51,11 @@ import static org.thingsboard.rule.engine.metadata.TbGetTelemetryNodeConfigurati @RuleNode(type = ComponentType.ENRICHMENT, name = "originator telemetry", configClazz = TbGetTelemetryNodeConfiguration.class, - nodeDescription = "Add Message Originator Telemetry for selected time range into Message Metadata\n", - nodeDetails = "The node allows you to select fetch mode: FIRST/LAST/ALL to fetch telemetry of certain time range that are added into Message metadata without any prefix. " + - "If selected fetch mode ALL Telemetry will be added like array into Message Metadata where key is Timestamp and value is value of Telemetry.
" + - "If selected fetch mode FIRST or LAST Telemetry will be added like string without Timestamp.
" + - "Also, the rule node allows you to select telemetry sampling order: ASC or DESC.
" + - "Aggregation feature allows you to fetch aggregated telemetry as a single value by AVG, COUNT, SUM, MIN, MAX, NONE.
" + - "Note: The maximum size of the fetched array is 1000 records.\n ", + nodeDescription = "Adds message originator telemetry for selected time range into message metadata", + nodeDetails = "Useful when you need to get telemetry data set from the message originator for a specific time range " + + "instead of fetching just the latest telemetry or if you need to get the closest telemetry to the fetch interval start or end. " + + "Also, this node can be used for telemetry aggregation within configured fetch interval.

" + + "Output connections: Success, Failure.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeGetTelemetryFromDatabase") public class TbGetTelemetryNode implements TbNode { @@ -79,7 +74,7 @@ public class TbGetTelemetryNode implements TbNode { public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { this.config = TbNodeUtils.convert(configuration, TbGetTelemetryNodeConfiguration.class); tsKeyNames = config.getLatestTsKeyNames(); - limit = config.getFetchMode().equals(FETCH_MODE_ALL) ? validateLimit(config.getLimit()) : 1; + limit = config.getFetchMode().equals(TbGetTelemetryNodeConfiguration.FETCH_MODE_ALL) ? validateLimit(config.getLimit()) : 1; fetchMode = config.getFetchMode(); orderByFetchAll = config.getOrderBy(); if (StringUtils.isEmpty(orderByFetchAll)) { @@ -89,7 +84,7 @@ public class TbGetTelemetryNode implements TbNode { } Aggregation parseAggregationConfig(String aggName) { - if (StringUtils.isEmpty(aggName) || !fetchMode.equals(FETCH_MODE_ALL)) { + if (StringUtils.isEmpty(aggName) || !fetchMode.equals(TbGetTelemetryNodeConfiguration.FETCH_MODE_ALL)) { return Aggregation.NONE; } return Aggregation.valueOf(aggName); @@ -105,8 +100,8 @@ public class TbGetTelemetryNode implements TbNode { List keys = TbNodeUtils.processPatterns(tsKeyNames, msg); ListenableFuture> list = ctx.getTimeseriesService().findAll(ctx.getTenantId(), msg.getOriginator(), buildQueries(interval, keys)); DonAsynchron.withCallback(list, data -> { - process(data, msg, keys); - ctx.tellSuccess(ctx.transformMsg(msg, msg.getType(), msg.getOriginator(), msg.getMetaData(), msg.getData())); + var metaData = updateMetadata(data, msg, keys); + ctx.tellSuccess(TbMsg.transformMsgMetadata(msg, metaData)); }, error -> ctx.tellFailure(msg, error), ctx.getDbCallbackExecutor()); } catch (Exception e) { ctx.tellFailure(msg, e); @@ -127,28 +122,29 @@ public class TbGetTelemetryNode implements TbNode { private String getOrderBy() { switch (fetchMode) { - case FETCH_MODE_ALL: + case TbGetTelemetryNodeConfiguration.FETCH_MODE_ALL: return orderByFetchAll; - case FETCH_MODE_FIRST: + case TbGetTelemetryNodeConfiguration.FETCH_MODE_FIRST: return ASC_ORDER; default: return DESC_ORDER; } } - private void process(List entries, TbMsg msg, List keys) { + private TbMsgMetaData updateMetadata(List entries, TbMsg msg, List keys) { ObjectNode resultNode = JacksonUtil.newObjectNode(JacksonUtil.ALLOW_UNQUOTED_FIELD_NAMES_MAPPER); - if (FETCH_MODE_ALL.equals(fetchMode)) { + if (TbGetTelemetryNodeConfiguration.FETCH_MODE_ALL.equals(fetchMode)) { entries.forEach(entry -> processArray(resultNode, entry)); } else { entries.forEach(entry -> processSingle(resultNode, entry)); } - + var copy = msg.getMetaData().copy(); for (String key : keys) { if (resultNode.has(key)) { - msg.getMetaData().putValue(key, resultNode.get(key).toString()); + copy.putValue(key, resultNode.get(key).toString()); } } + return copy; } private void processSingle(ObjectNode node, TsKvEntry entry) { @@ -219,7 +215,7 @@ public class TbGetTelemetryNode implements TbNode { if (limit != 0) { return limit; } else { - return MAX_FETCH_SIZE; + return TbGetTelemetryNodeConfiguration.MAX_FETCH_SIZE; } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java index 7b3f92c0aa..bcd54cd829 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java @@ -15,32 +15,50 @@ */ package org.thingsboard.rule.engine.metadata; +import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentType; +import org.thingsboard.server.common.data.util.TbPair; @Slf4j @RuleNode( type = ComponentType.ENRICHMENT, - name="tenant attributes", - configClazz = TbGetEntityAttrNodeConfiguration.class, - nodeDescription = "Add Originators Tenant Attributes or Latest Telemetry into Message Metadata", - nodeDetails = "If Attributes enrichment configured, server scope attributes are added into Message metadata. " + - "If Latest Telemetry enrichment configured, latest telemetry added into metadata. " + - "To access those attributes in other nodes this template can be used " + - "metadata.temperature.", + name = "tenant attributes", + configClazz = TbGetEntityDataNodeConfiguration.class, + version = 1, + nodeDescription = "Adds message originator tenant attributes or latest telemetry into message or message metadata", + nodeDetails = "Useful when you need to retrieve some common configuration or threshold set " + + "that is stored as tenant attributes or telemetry data and use it for further message processing.

" + + "Output connections: Success, Failure.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeTenantAttributesConfig") -public class TbGetTenantAttributeNode extends TbEntityGetAttrNode { +public class TbGetTenantAttributeNode extends TbAbstractGetEntityDataNode { @Override - protected ListenableFuture findEntityAsync(TbContext ctx, EntityId originator) { + public TbGetEntityDataNodeConfiguration loadNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException { + var config = TbNodeUtils.convert(configuration, TbGetEntityDataNodeConfiguration.class); + checkIfMappingIsNotEmptyOrElseThrow(config.getDataMapping()); + checkDataToFetchSupportedOrElseThrow(config.getDataToFetch()); + return config; + } + + @Override + public ListenableFuture findEntityAsync(TbContext ctx, EntityId originator) { return Futures.immediateFuture(ctx.getTenantId()); } + @Override + public TbPair upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException { + return fromVersion == 0 ? upgradeToUseFetchToAndDataToFetch(oldConfiguration) : new TbPair<>(false, oldConfiguration); + } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java index ab16f45ef7..54d95645e1 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java @@ -15,6 +15,7 @@ */ package org.thingsboard.rule.engine.metadata; +import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.thingsboard.rule.engine.api.RuleNode; @@ -22,36 +23,54 @@ import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; -import org.thingsboard.server.common.data.ContactBased; +import org.thingsboard.rule.engine.util.TbMsgSource; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentType; +import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.TbMsg; @Slf4j @RuleNode(type = ComponentType.ENRICHMENT, name = "tenant details", configClazz = TbGetTenantDetailsNodeConfiguration.class, - nodeDescription = "Adds fields from Tenant details to the message body or metadata", - nodeDetails = "If checkbox: Add selected details to the message metadata is selected, existing fields will be added to the message metadata instead of message data.

" + - "Note: only Device, Asset, and Entity View type are allowed.

" + - "If the originator of the message is not assigned to Tenant, or originator type is not supported - Message will be forwarded to Failure chain, otherwise, Success chain will be used.", + version = 1, + nodeDescription = "Adds message originator tenant details into message or message metadata", + nodeDetails = "Useful when we need to retrieve contact information from your tenant " + + "such as email, phone, address, etc., for notifications via email, SMS, and other notification providers.

" + + "Output connections: Success, Failure.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeEntityDetailsConfig") -public class TbGetTenantDetailsNode extends TbAbstractGetEntityDetailsNode { +public class TbGetTenantDetailsNode extends TbAbstractGetEntityDetailsNode { private static final String TENANT_PREFIX = "tenant_"; @Override - protected TbGetTenantDetailsNodeConfiguration loadGetEntityDetailsNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException { - return TbNodeUtils.convert(configuration, TbGetTenantDetailsNodeConfiguration.class); + protected TbGetTenantDetailsNodeConfiguration loadNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException { + var config = TbNodeUtils.convert(configuration, TbGetTenantDetailsNodeConfiguration.class); + checkIfDetailsListIsNotEmptyOrElseThrow(config.getDetailsList()); + return config; } @Override - protected ListenableFuture getDetails(TbContext ctx, TbMsg msg) { - return getTbMsgListenableFuture(ctx, msg, getDataAsJson(msg), TENANT_PREFIX); + protected String getPrefix() { + return TENANT_PREFIX; } @Override - protected ListenableFuture getContactBasedListenableFuture(TbContext ctx, TbMsg msg) { + protected ListenableFuture getContactBasedFuture(TbContext ctx, TbMsg msg) { return ctx.getTenantService().findTenantByIdAsync(ctx.getTenantId(), ctx.getTenantId()); } + + @Override + public TbPair upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException { + return fromVersion == 0 ? + upgradeRuleNodesWithOldPropertyToUseFetchTo( + oldConfiguration, + "addToMetadata", + TbMsgSource.METADATA.name(), + TbMsgSource.DATA.name()) : + new TbPair<>(false, oldConfiguration); + } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeConfiguration.java index 7770d6acb1..f50f44ec23 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeConfiguration.java @@ -16,18 +16,22 @@ package org.thingsboard.rule.engine.metadata; import lombok.Data; +import lombok.EqualsAndHashCode; import org.thingsboard.rule.engine.api.NodeConfiguration; +import org.thingsboard.rule.engine.util.TbMsgSource; import java.util.Collections; @Data +@EqualsAndHashCode(callSuper = true) public class TbGetTenantDetailsNodeConfiguration extends TbAbstractGetEntityDetailsNodeConfiguration implements NodeConfiguration { - @Override public TbGetTenantDetailsNodeConfiguration defaultConfiguration() { - TbGetTenantDetailsNodeConfiguration configuration = new TbGetTenantDetailsNodeConfiguration(); + var configuration = new TbGetTenantDetailsNodeConfiguration(); configuration.setDetailsList(Collections.emptyList()); + configuration.setFetchTo(TbMsgSource.DATA); return configuration; } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java index 478e733958..b9ee65675a 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java @@ -25,13 +25,13 @@ import org.thingsboard.mqtt.MqttClientConfig; import org.thingsboard.mqtt.MqttConnectResult; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.credentials.BasicCredentials; import org.thingsboard.rule.engine.credentials.ClientCredentials; import org.thingsboard.rule.engine.credentials.CredentialsType; +import org.thingsboard.rule.engine.external.TbAbstractExternalNode; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.plugin.ComponentClusteringMode; import org.thingsboard.server.common.data.plugin.ComponentType; @@ -40,6 +40,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; import javax.net.ssl.SSLException; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -55,9 +56,9 @@ import java.util.concurrent.TimeoutException; configDirective = "tbExternalNodeMqttConfig", icon = "call_split" ) -public class TbMqttNode implements TbNode { +public class TbMqttNode extends TbAbstractExternalNode { - private static final Charset UTF8 = Charset.forName("UTF-8"); + private static final Charset UTF8 = StandardCharsets.UTF_8; private static final String ERROR = "error"; @@ -67,8 +68,9 @@ public class TbMqttNode implements TbNode { @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { + super.init(ctx); + this.mqttNodeConfiguration = TbNodeUtils.convert(configuration, TbMqttNodeConfiguration.class); try { - this.mqttNodeConfiguration = TbNodeUtils.convert(configuration, TbMqttNodeConfiguration.class); this.mqttClient = initClient(ctx); } catch (Exception e) { throw new TbNodeException(e); @@ -78,22 +80,22 @@ public class TbMqttNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { String topic = TbNodeUtils.processPattern(this.mqttNodeConfiguration.getTopicPattern(), msg); - this.mqttClient.publish(topic, Unpooled.wrappedBuffer(msg.getData().getBytes(UTF8)), MqttQoS.AT_LEAST_ONCE, mqttNodeConfiguration.isRetainedMessage()) + var tbMsg = ackIfNeeded(ctx, msg); + this.mqttClient.publish(topic, Unpooled.wrappedBuffer(tbMsg.getData().getBytes(UTF8)), MqttQoS.AT_LEAST_ONCE, mqttNodeConfiguration.isRetainedMessage()) .addListener(future -> { if (future.isSuccess()) { - ctx.tellSuccess(msg); + tellSuccess(ctx, tbMsg); } else { - TbMsg next = processException(ctx, msg, future.cause()); - ctx.tellFailure(next, future.cause()); + tellFailure(ctx, processException(tbMsg, future.cause()), future.cause()); } } ); } - private TbMsg processException(TbContext ctx, TbMsg origMsg, Throwable e) { + private TbMsg processException(TbMsg origMsg, Throwable e) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(ERROR, e.getClass() + ": " + e.getMessage()); - return ctx.transformMsg(origMsg, origMsg.getType(), origMsg.getOriginator(), metaData, origMsg.getData()); + return TbMsg.transformMsgMetadata(origMsg, metaData); } @Override @@ -103,8 +105,13 @@ public class TbMqttNode implements TbNode { } } + String getOwnerId(TbContext ctx) { + return "Tenant[" + ctx.getTenantId().getId() + "]RuleNode[" + ctx.getSelf().getId().getId() + "]"; + } + protected MqttClient initClient(TbContext ctx) throws Exception { MqttClientConfig config = new MqttClientConfig(getSslContext()); + config.setOwnerId(getOwnerId(ctx)); if (!StringUtils.isEmpty(this.mqttNodeConfiguration.getClientId())) { config.setClientId(this.mqttNodeConfiguration.isAppendClientIdSuffix() ? this.mqttNodeConfiguration.getClientId() + "_" + ctx.getServiceId() : this.mqttNodeConfiguration.getClientId()); @@ -112,7 +119,7 @@ public class TbMqttNode implements TbNode { config.setCleanSession(this.mqttNodeConfiguration.isCleanSession()); prepareMqttClientConfig(config); - MqttClient client = MqttClient.create(config, null); + MqttClient client = MqttClient.create(config, null, ctx.getExternalCallExecutor()); client.setEventLoop(ctx.getSharedEventLoop()); Future connectFuture = client.connect(this.mqttNodeConfiguration.getHost(), this.mqttNodeConfiguration.getPort()); MqttConnectResult result; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/azure/TbAzureIotHubNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/azure/TbAzureIotHubNode.java index 5720f66038..3ea96fa967 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/azure/TbAzureIotHubNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/azure/TbAzureIotHubNode.java @@ -48,8 +48,9 @@ import javax.net.ssl.SSLException; public class TbAzureIotHubNode extends TbMqttNode { @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { + super.init(ctx); + this.mqttNodeConfiguration = TbNodeUtils.convert(configuration, TbMqttNodeConfiguration.class); try { - this.mqttNodeConfiguration = TbNodeUtils.convert(configuration, TbMqttNodeConfiguration.class); mqttNodeConfiguration.setPort(8883); mqttNodeConfiguration.setCleanSession(true); ClientCredentials credentials = mqttNodeConfiguration.getCredentials(); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java index 057702cb2b..1db5a7fb11 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java @@ -15,16 +15,19 @@ */ package org.thingsboard.rule.engine.notification; +import com.google.common.util.concurrent.FutureCallback; import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.rule.engine.external.TbAbstractExternalNode; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.notification.NotificationRequest; import org.thingsboard.server.common.data.notification.NotificationRequestConfig; +import org.thingsboard.server.common.data.notification.NotificationRequestStats; import org.thingsboard.server.common.data.notification.info.RuleEngineOriginatedNotificationInfo; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; @@ -42,12 +45,13 @@ import java.util.concurrent.ExecutionException; configDirective = "tbExternalNodeNotificationConfig", icon = "notifications" ) -public class TbNotificationNode implements TbNode { +public class TbNotificationNode extends TbAbstractExternalNode { private TbNotificationNodeConfiguration config; @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { + super.init(ctx); this.config = TbNodeUtils.convert(configuration, TbNotificationNodeConfiguration.class); } @@ -55,6 +59,8 @@ public class TbNotificationNode implements TbNode { public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException { RuleEngineOriginatedNotificationInfo notificationInfo = RuleEngineOriginatedNotificationInfo.builder() .msgOriginator(msg.getOriginator()) + .msgCustomerId(msg.getOriginator().getEntityType() == EntityType.CUSTOMER + && msg.getOriginator().equals(msg.getCustomerId()) ? null : msg.getCustomerId()) .msgMetadata(msg.getMetaData().getData()) .msgData(JacksonUtil.toFlatMap(JacksonUtil.toJsonNode(msg.getData()))) .msgType(msg.getType()) @@ -69,15 +75,25 @@ public class TbNotificationNode implements TbNode { .originatorEntityId(ctx.getSelf().getRuleChainId()) .build(); - DonAsynchron.withCallback(ctx.getNotificationExecutor().executeAsync(() -> { - return ctx.getNotificationCenter().processNotificationRequest(ctx.getTenantId(), notificationRequest, stats -> { - TbMsgMetaData metaData = msg.getMetaData().copy(); - metaData.putValue("notificationRequestResult", JacksonUtil.toString(stats)); - ctx.tellSuccess(TbMsg.transformMsg(msg, metaData)); - }); - }), - r -> {}, - e -> ctx.tellFailure(msg, e)); + var tbMsg = ackIfNeeded(ctx, msg); + + var callback = new FutureCallback() { + @Override + public void onSuccess(NotificationRequestStats stats) { + TbMsgMetaData metaData = tbMsg.getMetaData().copy(); + metaData.putValue("notificationRequestResult", JacksonUtil.toString(stats)); + tellSuccess(ctx, TbMsg.transformMsgMetadata(tbMsg, metaData)); + } + + @Override + public void onFailure(Throwable e) { + tellFailure(ctx, tbMsg, e); + } + }; + + var future = ctx.getNotificationExecutor().executeAsync(() -> + ctx.getNotificationCenter().processNotificationRequest(ctx.getTenantId(), notificationRequest, callback)); + DonAsynchron.withCallback(future, r -> {}, callback::onFailure); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNode.java index 544a864931..86e2c4bd1c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNode.java @@ -22,6 +22,7 @@ import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.rule.engine.external.TbAbstractExternalNode; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; @@ -37,12 +38,13 @@ import java.util.concurrent.ExecutionException; configDirective = "tbExternalNodeSlackConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTYsMTVBMiwyIDAgMCwxIDQsMTdBMiwyIDAgMCwxIDIsMTVBMiwyIDAgMCwxIDQsMTNINlYxNU03LDE1QTIsMiAwIDAsMSA5LDEzQTIsMiAwIDAsMSAxMSwxNVYyMEEyLDIgMCAwLDEgOSwyMkEyLDIgMCAwLDEgNywyMFYxNU05LDdBMiwyIDAgMCwxIDcsNUEyLDIgMCAwLDEgOSwzQTIsMiAwIDAsMSAxMSw1VjdIOU05LDhBMiwyIDAgMCwxIDExLDEwQTIsMiAwIDAsMSA5LDEySDRBMiwyIDAgMCwxIDIsMTBBMiwyIDAgMCwxIDQsOEg5TTE3LDEwQTIsMiAwIDAsMSAxOSw4QTIsMiAwIDAsMSAyMSwxMEEyLDIgMCAwLDEgMTksMTJIMTdWMTBNMTYsMTBBMiwyIDAgMCwxIDE0LDEyQTIsMiAwIDAsMSAxMiwxMFY1QTIsMiAwIDAsMSAxNCwzQTIsMiAwIDAsMSAxNiw1VjEwTTE0LDE4QTIsMiAwIDAsMSAxNiwyMEEyLDIgMCAwLDEgMTQsMjJBMiwyIDAgMCwxIDEyLDIwVjE4SDE0TTE0LDE3QTIsMiAwIDAsMSAxMiwxNUEyLDIgMCAwLDEgMTQsMTNIMTlBMiwyIDAgMCwxIDIxLDE1QTIsMiAwIDAsMSAxOSwxN0gxNFoiIC8+PC9zdmc+" ) -public class TbSlackNode implements TbNode { +public class TbSlackNode extends TbAbstractExternalNode { private TbSlackNodeConfiguration config; @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { + super.init(ctx); this.config = TbNodeUtils.convert(configuration, TbSlackNodeConfiguration.class); } @@ -59,11 +61,12 @@ public class TbSlackNode implements TbNode { } String message = TbNodeUtils.processPattern(config.getMessageTemplate(), msg); + var tbMsg = ackIfNeeded(ctx, msg); DonAsynchron.withCallback(ctx.getExternalCallExecutor().executeAsync(() -> { ctx.getSlackService().sendMessage(ctx.getTenantId(), token, config.getConversation().getId(), message); }), - r -> ctx.tellSuccess(msg), - e -> ctx.tellFailure(msg, e)); + r -> tellSuccess(ctx, tbMsg), + e -> tellFailure(ctx, tbMsg, e)); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java index f31896a8c9..6bb37438a1 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmRuleState.java @@ -41,7 +41,7 @@ import org.thingsboard.server.common.data.query.KeyFilterPredicate; import org.thingsboard.server.common.data.query.NumericFilterPredicate; import org.thingsboard.server.common.data.query.StringFilterPredicate; import org.thingsboard.server.common.msg.tools.SchedulerUtils; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.common.adaptor.JsonConverter; import java.time.Instant; import java.time.ZoneId; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java index 70f15935fc..9e2719bf53 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java @@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.device.profile.AlarmConditionSpecType; import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -189,7 +190,7 @@ class AlarmState { metaData.putValue(DataConstants.IS_CLEARED_ALARM, Boolean.TRUE.toString()); } setAlarmConditionMetadata(ruleState, metaData); - TbMsg newMsg = ctx.newMsg(lastMsgQueueName != null ? lastMsgQueueName : null, "ALARM", + TbMsg newMsg = ctx.newMsg(lastMsgQueueName != null ? lastMsgQueueName : null, TbMsgType.ALARM, originator, msg != null ? msg.getCustomerId() : null, metaData, data); ctx.enqueueForTellNext(newMsg, relationType); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java index 31f661417b..b38fd29af7 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java @@ -40,8 +40,7 @@ import org.thingsboard.server.common.data.query.EntityKey; import org.thingsboard.server.common.data.query.EntityKeyType; import org.thingsboard.server.common.data.rule.RuleNodeState; import org.thingsboard.server.common.msg.TbMsg; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; +import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.dao.sql.query.EntityKeyMapping; import java.util.ArrayList; @@ -55,6 +54,18 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; +import static org.thingsboard.server.common.data.msg.TbMsgType.ACTIVITY_EVENT; +import static org.thingsboard.server.common.data.msg.TbMsgType.ALARM_ACK; +import static org.thingsboard.server.common.data.msg.TbMsgType.ALARM_CLEAR; +import static org.thingsboard.server.common.data.msg.TbMsgType.ALARM_DELETE; +import static org.thingsboard.server.common.data.msg.TbMsgType.ATTRIBUTES_DELETED; +import static org.thingsboard.server.common.data.msg.TbMsgType.ATTRIBUTES_UPDATED; +import static org.thingsboard.server.common.data.msg.TbMsgType.ENTITY_ASSIGNED; +import static org.thingsboard.server.common.data.msg.TbMsgType.ENTITY_UNASSIGNED; +import static org.thingsboard.server.common.data.msg.TbMsgType.INACTIVITY_EVENT; +import static org.thingsboard.server.common.data.msg.TbMsgType.POST_ATTRIBUTES_REQUEST; +import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_REQUEST; + @Slf4j class DeviceState { @@ -136,24 +147,24 @@ class DeviceState { latestValues = fetchLatestValues(ctx, deviceId); } boolean stateChanged = false; - if (msg.getType().equals(SessionMsgType.POST_TELEMETRY_REQUEST.name())) { + if (msg.isTypeOf(POST_TELEMETRY_REQUEST)) { stateChanged = processTelemetry(ctx, msg); - } else if (msg.getType().equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name())) { + } else if (msg.isTypeOf(POST_ATTRIBUTES_REQUEST)) { stateChanged = processAttributesUpdateRequest(ctx, msg); - } else if (msg.getType().equals(DataConstants.ACTIVITY_EVENT) || msg.getType().equals(DataConstants.INACTIVITY_EVENT)) { + } else if (msg.isTypeOneOf(ACTIVITY_EVENT, INACTIVITY_EVENT)) { stateChanged = processDeviceActivityEvent(ctx, msg); - } else if (msg.getType().equals(DataConstants.ATTRIBUTES_UPDATED)) { + } else if (msg.isTypeOf(ATTRIBUTES_UPDATED)) { stateChanged = processAttributesUpdateNotification(ctx, msg); - } else if (msg.getType().equals(DataConstants.ATTRIBUTES_DELETED)) { + } else if (msg.isTypeOf(ATTRIBUTES_DELETED)) { stateChanged = processAttributesDeleteNotification(ctx, msg); - } else if (msg.getType().equals(DataConstants.ALARM_CLEAR)) { + } else if (msg.isTypeOf(ALARM_CLEAR)) { stateChanged = processAlarmClearNotification(ctx, msg); - } else if (msg.getType().equals(DataConstants.ALARM_ACK)) { + } else if (msg.isTypeOf(ALARM_ACK)) { processAlarmAckNotification(ctx, msg); - } else if (msg.getType().equals(DataConstants.ALARM_DELETE)) { + } else if (msg.isTypeOf(ALARM_DELETE)) { processAlarmDeleteNotification(ctx, msg); } else { - if (msg.getType().equals(DataConstants.ENTITY_ASSIGNED) || msg.getType().equals(DataConstants.ENTITY_UNASSIGNED)) { + if (msg.isTypeOneOf(ENTITY_ASSIGNED, ENTITY_UNASSIGNED)) { dynamicPredicateValueCtx.resetCustomer(); } ctx.tellSuccess(msg); @@ -213,7 +224,7 @@ class DeviceState { private boolean processAttributesDeleteNotification(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException { boolean stateChanged = false; List keys = new ArrayList<>(); - new JsonParser().parse(msg.getData()).getAsJsonObject().get("attributes").getAsJsonArray().forEach(e -> keys.add(e.getAsString())); + JsonParser.parseString(msg.getData()).getAsJsonObject().get("attributes").getAsJsonArray().forEach(e -> keys.add(e.getAsString())); String scope = msg.getMetaData().getValue(DataConstants.SCOPE); if (StringUtils.isEmpty(scope)) { scope = DataConstants.CLIENT_SCOPE; @@ -241,7 +252,7 @@ class DeviceState { private boolean processAttributes(TbContext ctx, TbMsg msg, String scope) throws ExecutionException, InterruptedException { boolean stateChanged = false; - Set attributes = JsonConverter.convertToAttributes(new JsonParser().parse(msg.getData())); + Set attributes = JsonConverter.convertToAttributes(JsonParser.parseString(msg.getData())); if (!attributes.isEmpty()) { SnapshotUpdate update = merge(latestValues, attributes, scope); for (DeviceProfileAlarm alarm : deviceProfile.getAlarmSettings()) { @@ -256,7 +267,7 @@ class DeviceState { protected boolean processTelemetry(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException { boolean stateChanged = false; - Map> tsKvMap = JsonConverter.convertToSortedTelemetry(new JsonParser().parse(msg.getData()), msg.getMetaDataTs()); + Map> tsKvMap = JsonConverter.convertToSortedTelemetry(JsonParser.parseString(msg.getData()), msg.getMetaDataTs()); // iterate over data by ts (ASC order). for (Map.Entry> entry : tsKvMap.entrySet()) { Long ts = entry.getKey(); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java index ba987678f3..5404b2ea0e 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java @@ -26,12 +26,12 @@ import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; -import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.plugin.ComponentType; @@ -60,9 +60,6 @@ import java.util.concurrent.TimeUnit; configDirective = "tbDeviceProfileConfig" ) public class TbDeviceProfileNode implements TbNode { - private static final String PERIODIC_MSG_TYPE = "TbDeviceProfilePeriodicMsg"; - private static final String PROFILE_UPDATE_MSG_TYPE = "TbDeviceProfileUpdateMsg"; - private static final String DEVICE_UPDATE_MSG_TYPE = "TbDeviceUpdateMsg"; private TbDeviceProfileNodeConfiguration config; private RuleEngineDeviceProfileCache cache; @@ -76,6 +73,10 @@ public class TbDeviceProfileNode implements TbNode { this.ctx = ctx; scheduleAlarmHarvesting(ctx, null); ctx.addDeviceProfileListeners(this::onProfileUpdate, this::onDeviceUpdate); + initAlarmRuleState(false); + } + + private void initAlarmRuleState(boolean printNewlyAddedDeviceStates) { if (config.isFetchAlarmRulesStateOnStart()) { log.info("[{}] Fetching alarm rule state", ctx.getSelfId()); int fetchCount = 0; @@ -86,7 +87,7 @@ public class TbDeviceProfileNode implements TbNode { for (RuleNodeState rns : states.getData()) { fetchCount++; if (rns.getEntityId().getEntityType().equals(EntityType.DEVICE) && ctx.isLocalEntity(rns.getEntityId())) { - getOrCreateDeviceState(ctx, new DeviceId(rns.getEntityId().getId()), rns); + getOrCreateDeviceState(ctx, new DeviceId(rns.getEntityId().getId()), rns, printNewlyAddedDeviceStates); } } } @@ -107,12 +108,12 @@ public class TbDeviceProfileNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException { EntityType originatorType = msg.getOriginator().getEntityType(); - if (msg.getType().equals(PERIODIC_MSG_TYPE)) { + if (msg.isTypeOf(TbMsgType.DEVICE_PROFILE_PERIODIC_SELF_MSG)) { scheduleAlarmHarvesting(ctx, msg); harvestAlarms(ctx, System.currentTimeMillis()); - } else if (msg.getType().equals(PROFILE_UPDATE_MSG_TYPE)) { + } else if (msg.isTypeOf(TbMsgType.DEVICE_PROFILE_UPDATE_SELF_MSG)) { updateProfile(ctx, new DeviceProfileId(UUID.fromString(msg.getData()))); - } else if (msg.getType().equals(DEVICE_UPDATE_MSG_TYPE)) { + } else if (msg.isTypeOf(TbMsgType.DEVICE_UPDATE_SELF_MSG)) { JsonNode data = JacksonUtil.toJsonNode(msg.getData()); DeviceId deviceId = new DeviceId(UUID.fromString(data.get("deviceId").asText())); if (data.has("profileId")) { @@ -123,14 +124,14 @@ public class TbDeviceProfileNode implements TbNode { } else { if (EntityType.DEVICE.equals(originatorType)) { DeviceId deviceId = new DeviceId(msg.getOriginator().getId()); - if (msg.getType().equals(DataConstants.ENTITY_UPDATED)) { + if (msg.isTypeOf(TbMsgType.ENTITY_UPDATED)) { invalidateDeviceProfileCache(deviceId, msg.getData()); ctx.tellSuccess(msg); - } else if (msg.getType().equals(DataConstants.ENTITY_DELETED)) { + } else if (msg.isTypeOf(TbMsgType.ENTITY_DELETED)) { removeDeviceState(deviceId); ctx.tellSuccess(msg); } else { - DeviceState deviceState = getOrCreateDeviceState(ctx, deviceId, null); + DeviceState deviceState = getOrCreateDeviceState(ctx, deviceId, null, false); if (deviceState != null) { deviceState.process(ctx, msg); } else { @@ -148,6 +149,7 @@ public class TbDeviceProfileNode implements TbNode { public void onPartitionChangeMsg(TbContext ctx, PartitionChangeMsg msg) { // Cleanup the cache for all entities that are no longer assigned to current server partitions deviceStates.entrySet().removeIf(entry -> !ctx.isLocalEntity(entry.getKey())); + initAlarmRuleState(true); } @Override @@ -156,20 +158,23 @@ public class TbDeviceProfileNode implements TbNode { deviceStates.clear(); } - protected DeviceState getOrCreateDeviceState(TbContext ctx, DeviceId deviceId, RuleNodeState rns) { + protected DeviceState getOrCreateDeviceState(TbContext ctx, DeviceId deviceId, RuleNodeState rns, boolean printNewlyAddedDeviceStates) { DeviceState deviceState = deviceStates.get(deviceId); if (deviceState == null) { DeviceProfile deviceProfile = cache.get(ctx.getTenantId(), deviceId); if (deviceProfile != null) { deviceState = new DeviceState(ctx, config, deviceId, new ProfileState(deviceProfile), rns); deviceStates.put(deviceId, deviceState); + if (printNewlyAddedDeviceStates) { + log.info("[{}][{}] Device [{}] was added during PartitionChangeMsg", ctx.getTenantId(), ctx.getSelfId(), deviceId); + } } } return deviceState; } protected void scheduleAlarmHarvesting(TbContext ctx, TbMsg msg) { - TbMsg periodicCheck = TbMsg.newMsg(PERIODIC_MSG_TYPE, ctx.getTenantId(), msg != null ? msg.getCustomerId() : null, TbMsgMetaData.EMPTY, "{}"); + TbMsg periodicCheck = TbMsg.newMsg(TbMsgType.DEVICE_PROFILE_PERIODIC_SELF_MSG, ctx.getTenantId(), msg != null ? msg.getCustomerId() : null, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); ctx.tellSelf(periodicCheck, TimeUnit.MINUTES.toMillis(1)); } @@ -194,7 +199,7 @@ public class TbDeviceProfileNode implements TbNode { } protected void onProfileUpdate(DeviceProfile profile) { - ctx.tellSelf(TbMsg.newMsg(PROFILE_UPDATE_MSG_TYPE, ctx.getTenantId(), TbMsgMetaData.EMPTY, profile.getId().getId().toString()), 0L); + ctx.tellSelf(TbMsg.newMsg(TbMsgType.DEVICE_PROFILE_UPDATE_SELF_MSG, ctx.getTenantId(), TbMsgMetaData.EMPTY, profile.getId().getId().toString()), 0L); } private void onDeviceUpdate(DeviceId deviceId, DeviceProfile deviceProfile) { @@ -203,7 +208,7 @@ public class TbDeviceProfileNode implements TbNode { if (deviceProfile != null) { msgData.put("deviceProfileId", deviceProfile.getId().getId().toString()); } - ctx.tellSelf(TbMsg.newMsg(DEVICE_UPDATE_MSG_TYPE, ctx.getTenantId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(msgData)), 0L); + ctx.tellSelf(TbMsg.newMsg(TbMsgType.DEVICE_UPDATE_SELF_MSG, ctx.getTenantId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(msgData)), 0L); } protected void invalidateDeviceProfileCache(DeviceId deviceId, String deviceJson) { @@ -216,7 +221,7 @@ public class TbDeviceProfileNode implements TbNode { removeDeviceState(deviceId); } } catch (IllegalArgumentException e) { - log.debug("[{}] Received device update notification with non-device msg body: [{}][{}]", ctx.getSelfId(), deviceId, e); + log.debug("[{}] Received device update notification with non-device msg body: [{}]", ctx.getSelfId(), deviceId, e); } } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java index 41dffe2fd8..be7a22f713 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java @@ -24,16 +24,17 @@ import com.rabbitmq.client.MessageProperties; import lombok.extern.slf4j.Slf4j; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.rule.engine.external.TbAbstractExternalNode; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import static org.thingsboard.common.util.DonAsynchron.withCallback; @@ -48,9 +49,9 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; configDirective = "tbExternalNodeRabbitMqConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHZlcnNpb249IjEuMSIgeT0iMHB4IiB4PSIwcHgiIHZpZXdCb3g9IjAgMCAxMDAwIDEwMDAiPjxwYXRoIHN0cm9rZS13aWR0aD0iLjg0OTU2IiBkPSJtODYwLjQ3IDQxNi4zMmgtMjYyLjAxYy0xMi45MTMgMC0yMy42MTgtMTAuNzA0LTIzLjYxOC0yMy42MTh2LTI3Mi43MWMwLTIwLjMwNS0xNi4yMjctMzYuMjc2LTM2LjI3Ni0zNi4yNzZoLTkzLjc5MmMtMjAuMzA1IDAtMzYuMjc2IDE2LjIyNy0zNi4yNzYgMzYuMjc2djI3MC44NGMtMC4yNTQ4NyAxNC4xMDMtMTEuNDY5IDI1LjU3Mi0yNS43NDIgMjUuNTcybC04NS42MzYgMC42Nzk2NWMtMTQuMTAzIDAtMjUuNTcyLTExLjQ2OS0yNS41NzItMjUuNTcybDAuNjc5NjUtMjcxLjUyYzAtMjAuMzA1LTE2LjIyNy0zNi4yNzYtMzYuMjc2LTM2LjI3NmgtOTMuNTM3Yy0yMC4zMDUgMC0zNi4yNzYgMTYuMjI3LTM2LjI3NiAzNi4yNzZ2NzYzLjg0YzAgMTguMDk2IDE0Ljc4MiAzMi40NTMgMzIuNDUzIDMyLjQ1M2g3MjIuODFjMTguMDk2IDAgMzIuNDUzLTE0Ljc4MiAzMi40NTMtMzIuNDUzdi00MzUuMzFjLTEuMTg5NC0xOC4xODEtMTUuMjkyLTMyLjE5OC0zMy4zODgtMzIuMTk4em0tMTIyLjY4IDI4Ny4wN2MwIDIzLjYxOC0xOC44NiA0Mi40NzgtNDIuNDc4IDQyLjQ3OGgtNzMuOTk3Yy0yMy42MTggMC00Mi40NzgtMTguODYtNDIuNDc4LTQyLjQ3OHYtNzQuMjUyYzAtMjMuNjE4IDE4Ljg2LTQyLjQ3OCA0Mi40NzgtNDIuNDc4aDczLjk5N2MyMy42MTggMCA0Mi40NzggMTguODYgNDIuNDc4IDQyLjQ3OHoiLz48L3N2Zz4=" ) -public class TbRabbitMqNode implements TbNode { +public class TbRabbitMqNode extends TbAbstractExternalNode { - private static final Charset UTF8 = Charset.forName("UTF-8"); + private static final Charset UTF8 = StandardCharsets.UTF_8; private static final String ERROR = "error"; @@ -61,6 +62,7 @@ public class TbRabbitMqNode implements TbNode { @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { + super.init(ctx); this.config = TbNodeUtils.convert(configuration, TbRabbitMqNodeConfiguration.class); ConnectionFactory factory = new ConnectionFactory(); factory.setHost(this.config.getHost()); @@ -82,12 +84,10 @@ public class TbRabbitMqNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { - withCallback(publishMessageAsync(ctx, msg), - ctx::tellSuccess, - t -> { - TbMsg next = processException(ctx, msg, t); - ctx.tellFailure(next, t); - }); + var tbMsg = ackIfNeeded(ctx, msg); + withCallback(publishMessageAsync(ctx, tbMsg), + m -> tellSuccess(ctx, m), + t -> tellFailure(ctx, processException(tbMsg, t), t)); } private ListenableFuture publishMessageAsync(TbContext ctx, TbMsg msg) { @@ -115,10 +115,10 @@ public class TbRabbitMqNode implements TbNode { return msg; } - private TbMsg processException(TbContext ctx, TbMsg origMsg, Throwable t) { + private TbMsg processException(TbMsg origMsg, Throwable t) { TbMsgMetaData metaData = origMsg.getMetaData().copy(); metaData.putValue(ERROR, t.getClass() + ": " + t.getMessage()); - return ctx.transformMsg(origMsg, origMsg.getType(), origMsg.getOriginator(), metaData, origMsg.getData()); + return TbMsg.transformMsgMetadata(origMsg, metaData); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java index 6f9541f5b1..1a2984ef4f 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java @@ -43,7 +43,6 @@ import org.springframework.web.util.UriComponentsBuilder; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeException; -import org.thingsboard.rule.engine.api.TbRelationTypes; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.credentials.BasicCredentials; import org.thingsboard.rule.engine.credentials.ClientCredentials; @@ -65,6 +64,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; +import java.util.function.Consumer; @Data @Slf4j @@ -182,37 +182,36 @@ public class TbHttpClient { } } - public void processMessage(TbContext ctx, TbMsg msg) { + public void processMessage(TbContext ctx, TbMsg msg, + Consumer onSuccess, + BiConsumer onFailure) { String endpointUrl = TbNodeUtils.processPattern(config.getRestEndpointUrlPattern(), msg); HttpHeaders headers = prepareHeaders(msg); HttpMethod method = HttpMethod.valueOf(config.getRequestMethod()); HttpEntity entity; - if(HttpMethod.GET.equals(method) || HttpMethod.HEAD.equals(method) || - HttpMethod.OPTIONS.equals(method) || HttpMethod.TRACE.equals(method) || - config.isIgnoreRequestBody()) { + if (HttpMethod.GET.equals(method) || HttpMethod.HEAD.equals(method) || + HttpMethod.OPTIONS.equals(method) || HttpMethod.TRACE.equals(method) || + config.isIgnoreRequestBody()) { entity = new HttpEntity<>(headers); } else { - entity = new HttpEntity<>(getData(msg), headers); + entity = new HttpEntity<>(getData(msg, config.isIgnoreRequestBody(), config.isParseToPlainText()), headers); } URI uri = buildEncodedUri(endpointUrl); ListenableFuture> future = httpClient.exchange( uri, method, entity, String.class); - future.addCallback(new ListenableFutureCallback>() { + future.addCallback(new ListenableFutureCallback<>() { @Override public void onFailure(Throwable throwable) { - TbMsg next = processException(ctx, msg, throwable); - ctx.tellFailure(next, throwable); + onFailure.accept(processException(msg, throwable), throwable); } @Override public void onSuccess(ResponseEntity responseEntity) { if (responseEntity.getStatusCode().is2xxSuccessful()) { - TbMsg next = processResponse(ctx, msg, responseEntity); - ctx.tellSuccess(next); + onSuccess.accept(processResponse(ctx, msg, responseEntity)); } else { - TbMsg next = processFailureResponse(ctx, msg, responseEntity); - ctx.tellNext(next, TbRelationTypes.FAILURE); + onFailure.accept(processFailureResponse(msg, responseEntity), null); } } }); @@ -243,12 +242,19 @@ public class TbHttpClient { return uri; } - private String getData(TbMsg msg) { - String data = msg.getData(); + private String getData(TbMsg tbMsg, boolean ignoreBody, boolean parseToPlainText) { + if (!ignoreBody && parseToPlainText) { + return parseJsonStringToPlainText(tbMsg.getData()); + } + return tbMsg.getData(); + } - if (config.isTrimDoubleQuotes()) { + protected String parseJsonStringToPlainText(String data) { + if (data.startsWith("\"") && data.endsWith("\"") && data.length() >= 2) { final String dataBefore = data; - data = data.replaceAll("^\"|\"$", "");; + try { + data = JacksonUtil.fromString(data, String.class); + } catch (Exception ignored) {} log.trace("Trimming double quotes. Before trim: [{}], after trim: [{}]", dataBefore, data); } @@ -261,8 +267,8 @@ public class TbHttpClient { metaData.putValue(STATUS_CODE, response.getStatusCode().value() + ""); metaData.putValue(STATUS_REASON, response.getStatusCode().getReasonPhrase()); headersToMetaData(response.getHeaders(), metaData::putValue); - String body = response.getBody() == null ? "{}" : response.getBody(); - return ctx.transformMsg(origMsg, origMsg.getType(), origMsg.getOriginator(), metaData, body); + String body = response.getBody() == null ? TbMsg.EMPTY_JSON_OBJECT : response.getBody(); + return ctx.transformMsg(origMsg, metaData, body); } void headersToMetaData(Map> headers, BiConsumer consumer) { @@ -280,17 +286,17 @@ public class TbHttpClient { }); } - private TbMsg processFailureResponse(TbContext ctx, TbMsg origMsg, ResponseEntity response) { + private TbMsg processFailureResponse(TbMsg origMsg, ResponseEntity response) { TbMsgMetaData metaData = origMsg.getMetaData(); metaData.putValue(STATUS, response.getStatusCode().name()); metaData.putValue(STATUS_CODE, response.getStatusCode().value() + ""); metaData.putValue(STATUS_REASON, response.getStatusCode().getReasonPhrase()); metaData.putValue(ERROR_BODY, response.getBody()); headersToMetaData(response.getHeaders(), metaData::putValue); - return ctx.transformMsg(origMsg, origMsg.getType(), origMsg.getOriginator(), metaData, origMsg.getData()); + return TbMsg.transformMsgMetadata(origMsg, metaData); } - private TbMsg processException(TbContext ctx, TbMsg origMsg, Throwable e) { + private TbMsg processException(TbMsg origMsg, Throwable e) { TbMsgMetaData metaData = origMsg.getMetaData(); metaData.putValue(ERROR, e.getClass() + ": " + e.getMessage()); if (e instanceof RestClientResponseException) { @@ -299,7 +305,7 @@ public class TbHttpClient { metaData.putValue(STATUS_CODE, restClientResponseException.getRawStatusCode() + ""); metaData.putValue(ERROR_BODY, restClientResponseException.getResponseBodyAsString()); } - return ctx.transformMsg(origMsg, origMsg.getType(), origMsg.getOriginator(), metaData, origMsg.getData()); + return TbMsg.transformMsgMetadata(origMsg, metaData); } private HttpHeaders prepareHeaders(TbMsg msg) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java index d2356c69f7..a37310331e 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java @@ -15,14 +15,17 @@ */ package org.thingsboard.rule.engine.rest; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.rule.engine.external.TbAbstractExternalNode; import org.thingsboard.server.common.data.plugin.ComponentType; +import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.TbMsg; @Slf4j @@ -30,6 +33,7 @@ import org.thingsboard.server.common.msg.TbMsg; type = ComponentType.EXTERNAL, name = "rest api call", configClazz = TbRestApiCallNodeConfiguration.class, + version = 1, nodeDescription = "Invoke REST API calls to external REST server", nodeDetails = "Will invoke REST API call GET | POST | PUT | DELETE to external REST server. " + "Message payload added into Request body. Configured attributes can be added into Headers from Message Metadata." + @@ -43,24 +47,28 @@ import org.thingsboard.server.common.msg.TbMsg; configDirective = "tbExternalNodeRestApiCallConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB2ZXJzaW9uPSIxLjEiIHk9IjBweCIgeD0iMHB4Ij48ZyB0cmFuc2Zvcm09Im1hdHJpeCguOTQ5NzUgMCAwIC45NDk3NSAxNy4xMiAyNi40OTIpIj48cGF0aCBkPSJtMTY5LjExIDEwOC41NGMtOS45MDY2IDAuMDczNC0xOS4wMTQgNi41NzI0LTIyLjAxNCAxNi40NjlsLTY5Ljk5MyAyMzEuMDhjLTMuNjkwNCAxMi4xODEgMy4yODkyIDI1LjIyIDE1LjQ2OSAyOC45MSAyLjIyNTkgMC42NzQ4MSA0LjQ5NjkgMSA2LjcyODUgMSA5Ljk3MjEgMCAxOS4xNjUtNi41MTUzIDIyLjE4Mi0xNi40NjdhNi41MjI0IDYuNTIyNCAwIDAgMCAwLjAwMiAtMC4wMDJsNjkuOTktMjMxLjA3YTYuNTIyNCA2LjUyMjQgMCAwIDAgMCAtMC4wMDJjMy42ODU1LTEyLjE4MS0zLjI4Ny0yNS4yMjUtMTUuNDcxLTI4LjkxMi0yLjI4MjUtMC42OTE0NS00LjYxMTYtMS4wMTY5LTYuODk4NC0xem04NC45ODggMGMtOS45MDQ4IDAuMDczNC0xOS4wMTggNi41Njc1LTIyLjAxOCAxNi40NjlsLTY5Ljk4NiAyMzEuMDhjLTMuNjg5OCAxMi4xNzkgMy4yODUzIDI1LjIxNyAxNS40NjUgMjguOTA4IDIuMjI5NyAwLjY3NjQ3IDQuNTAwOCAxLjAwMiA2LjczMjQgMS4wMDIgOS45NzIxIDAgMTkuMTY1LTYuNTE1MyAyMi4xODItMTYuNDY3YTYuNTIyNCA2LjUyMjQgMCAwIDAgMC4wMDIgLTAuMDAybDY5Ljk4OC0yMzEuMDdjMy42OTA4LTEyLjE4MS0zLjI4NTItMjUuMjIzLTE1LjQ2Ny0yOC45MTItMi4yODE0LTAuNjkyMzEtNC42MTA4LTEuMDE4OS02Ljg5ODQtMS4wMDJ6bS0yMTcuMjkgNDIuMjNjLTEyLjcyOS0wLjAwMDg3LTIzLjE4OCAxMC40NTYtMjMuMTg4IDIzLjE4NiAwLjAwMSAxMi43MjggMTAuNDU5IDIzLjE4NiAyMy4xODggMjMuMTg2IDEyLjcyNy0wLjAwMSAyMy4xODMtMTAuNDU5IDIzLjE4NC0yMy4xODYgMC4wMDA4NzYtMTIuNzI4LTEwLjQ1Ni0yMy4xODUtMjMuMTg0LTIzLjE4NnptMCAxNDYuNjRjLTEyLjcyNy0wLjAwMDg3LTIzLjE4NiAxMC40NTUtMjMuMTg4IDIzLjE4NC0wLjAwMDg3MyAxMi43MjkgMTAuNDU4IDIzLjE4OCAyMy4xODggMjMuMTg4IDEyLjcyOC0wLjAwMSAyMy4xODQtMTAuNDYgMjMuMTg0LTIzLjE4OC0wLjAwMS0xMi43MjYtMTAuNDU3LTIzLjE4My0yMy4xODQtMjMuMTg0em0yNzAuNzkgNDIuMjExYy0xMi43MjcgMC0yMy4xODQgMTAuNDU3LTIzLjE4NCAyMy4xODRzMTAuNDU1IDIzLjE4OCAyMy4xODQgMjMuMTg4aDE1NC45OGMxMi43MjkgMCAyMy4xODYtMTAuNDYgMjMuMTg2LTIzLjE4OCAwLjAwMS0xMi43MjgtMTAuNDU4LTIzLjE4NC0yMy4xODYtMjMuMTg0eiIgdHJhbnNmb3JtPSJtYXRyaXgoMS4wMzc2IDAgMCAxLjAzNzYgLTcuNTY3NiAtMTQuOTI1KSIgc3Ryb2tlLXdpZHRoPSIxLjI2OTMiLz48L2c+PC9zdmc+" ) -public class TbRestApiCallNode implements TbNode { +public class TbRestApiCallNode extends TbAbstractExternalNode { - private boolean useRedisQueueForMsgPersistence; + static final String PARSE_TO_PLAIN_TEXT = "parseToPlainText"; + static final String TRIM_DOUBLE_QUOTES = "trimDoubleQuotes"; protected TbHttpClient httpClient; @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { + super.init(ctx); TbRestApiCallNodeConfiguration config = TbNodeUtils.convert(configuration, TbRestApiCallNodeConfiguration.class); httpClient = new TbHttpClient(config, ctx.getSharedEventLoop()); - useRedisQueueForMsgPersistence = config.isUseRedisQueueForMsgPersistence(); - if (useRedisQueueForMsgPersistence) { + if (config.isUseRedisQueueForMsgPersistence()) { log.warn("[{}][{}] Usage of Redis Template is deprecated starting 2.5 and will have no affect", ctx.getTenantId(), ctx.getSelfId()); } } @Override public void onMsg(TbContext ctx, TbMsg msg) { - httpClient.processMessage(ctx, msg); + var tbMsg = ackIfNeeded(ctx, msg); + httpClient.processMessage(ctx, tbMsg, + m -> tellSuccess(ctx, m), + (m, t) -> tellFailure(ctx, m, t)); } @Override @@ -70,4 +78,21 @@ public class TbRestApiCallNode implements TbNode { } } + @Override + public TbPair upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException { + boolean hasChanges = false; + switch (fromVersion) { + case 0: + if (!oldConfiguration.has(PARSE_TO_PLAIN_TEXT) && oldConfiguration.has(TRIM_DOUBLE_QUOTES)) { + hasChanges = true; + ((ObjectNode) oldConfiguration).put(PARSE_TO_PLAIN_TEXT, oldConfiguration.get(TRIM_DOUBLE_QUOTES).booleanValue()); + ((ObjectNode) oldConfiguration).remove(TRIM_DOUBLE_QUOTES); + } + break; + default: + break; + } + return new TbPair<>(hasChanges, oldConfiguration); + } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java index 3fb28d474b..1ab4c8d0f8 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java @@ -37,7 +37,7 @@ public class TbRestApiCallNodeConfiguration implements NodeConfiguration future = ctx.getEdgeEventService().saveAsync(edgeEvent); Futures.addCallback(future, new FutureCallback<>() { @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java index 79bd6f9bf9..4ae8d0e78e 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java @@ -27,12 +27,13 @@ import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; -import org.thingsboard.rule.engine.api.TbRelationTypes; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; @@ -56,7 +57,6 @@ public class TbSendRPCRequestNode implements TbNode { private Random random = new Random(); private Gson gson = new Gson(); - private JsonParser jsonParser = new JsonParser(); private TbSendRpcRequestNodeConfiguration config; @Override @@ -66,7 +66,7 @@ public class TbSendRPCRequestNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { - JsonObject json = jsonParser.parse(msg.getData()).getAsJsonObject(); + JsonObject json = JsonParser.parseString(msg.getData()).getAsJsonObject(); String tmp; if (msg.getOriginator().getEntityType() != EntityType.DEVICE) { ctx.tellFailure(msg, new RuntimeException("Message originator is not a device entity!")); @@ -76,7 +76,7 @@ public class TbSendRPCRequestNode implements TbNode { ctx.tellFailure(msg, new RuntimeException("Params are not present in the message!")); } else { int requestId = json.has("requestId") ? json.get("requestId").getAsInt() : random.nextInt(); - boolean restApiCall = msg.getType().equals(DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE); + boolean restApiCall = msg.isTypeOf(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE); tmp = msg.getMetaData().getValue("oneway"); boolean oneway = !StringUtils.isEmpty(tmp) && Boolean.parseBoolean(tmp); @@ -116,8 +116,8 @@ public class TbSendRPCRequestNode implements TbNode { ctx.getRpcService().sendRpcRequestToDevice(request, ruleEngineDeviceRpcResponse -> { if (ruleEngineDeviceRpcResponse.getError().isEmpty()) { - TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getCustomerId(), msg.getMetaData(), ruleEngineDeviceRpcResponse.getResponse().orElse("{}")); - ctx.enqueueForTellNext(next, TbRelationTypes.SUCCESS); + TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getCustomerId(), msg.getMetaData(), ruleEngineDeviceRpcResponse.getResponse().orElse(TbMsg.EMPTY_JSON_OBJECT)); + ctx.enqueueForTellNext(next, TbNodeConnectionType.SUCCESS); } else { TbMsg next = ctx.newMsg(msg.getQueueName(), msg.getType(), msg.getOriginator(), msg.getCustomerId(), msg.getMetaData(), wrap("error", ruleEngineDeviceRpcResponse.getError().get().name())); ctx.enqueueForTellFailure(next, ruleEngineDeviceRpcResponse.getError().get().name()); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/sms/TbSendSmsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/sms/TbSendSmsNode.java index e92b15780a..55209e1f64 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/sms/TbSendSmsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/sms/TbSendSmsNode.java @@ -23,6 +23,7 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.sms.SmsSender; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.rule.engine.external.TbAbstractExternalNode; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; @@ -39,13 +40,14 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; configDirective = "tbExternalNodeSendSmsConfig", icon = "sms" ) -public class TbSendSmsNode implements TbNode { +public class TbSendSmsNode extends TbAbstractExternalNode { private TbSendSmsNodeConfiguration config; private SmsSender smsSender; @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { + super.init(ctx); try { this.config = TbNodeUtils.convert(configuration, TbSendSmsNodeConfiguration.class); if (!this.config.isUseSystemSmsSettings()) { @@ -58,15 +60,16 @@ public class TbSendSmsNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { + var tbMsg = ackIfNeeded(ctx, msg); try { withCallback(ctx.getSmsExecutor().executeAsync(() -> { - sendSms(ctx, msg); + sendSms(ctx, tbMsg); return null; }), - ok -> ctx.tellSuccess(msg), - fail -> ctx.tellFailure(msg, fail)); + ok -> tellSuccess(ctx, tbMsg), + fail -> tellFailure(ctx, tbMsg, fail)); } catch (Exception ex) { - ctx.tellFailure(msg, ex); + ctx.tellFailure(tbMsg, ex); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java index 9cf9bfa1bd..b2df498fb6 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java @@ -15,86 +15,142 @@ */ package org.thingsboard.rule.engine.telemetry; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; import com.google.gson.JsonParser; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.plugin.ComponentType; +import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.TbMsg; -import org.thingsboard.server.common.msg.session.SessionMsgType; -import org.thingsboard.server.common.transport.adaptor.JsonConverter; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; import static org.thingsboard.server.common.data.DataConstants.CLIENT_SCOPE; import static org.thingsboard.server.common.data.DataConstants.NOTIFY_DEVICE_METADATA_KEY; import static org.thingsboard.server.common.data.DataConstants.SCOPE; +import static org.thingsboard.server.common.data.msg.TbMsgType.POST_ATTRIBUTES_REQUEST; @Slf4j @RuleNode( type = ComponentType.ACTION, name = "save attributes", configClazz = TbMsgAttributesNodeConfiguration.class, + version = 2, nodeDescription = "Saves attributes data", nodeDetails = "Saves entity attributes based on configurable scope parameter. Expects messages with 'POST_ATTRIBUTES_REQUEST' message type. " + "If upsert(update/insert) operation is completed successfully rule node will send the incoming message via Success chain, otherwise, Failure chain is used. " + "Additionally if checkbox Send attributes updated notification is set to true, rule node will put the \"Attributes Updated\" " + - "event for SHARED_SCOPE and SERVER_SCOPE attributes updates to the corresponding rule engine queue.", + "event for SHARED_SCOPE and SERVER_SCOPE attributes updates to the corresponding rule engine queue." + + "Performance checkbox 'Save attributes only if the value changes' will skip attributes overwrites for values with no changes (avoid concurrent writes because this check is not transactional; will not update 'Last updated time' for skipped attributes).", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeAttributesConfig", icon = "file_upload" ) public class TbMsgAttributesNode implements TbNode { + static final String NOTIFY_DEVICE_KEY = "notifyDevice"; + static final String SEND_ATTRIBUTES_UPDATED_NOTIFICATION_KEY = "sendAttributesUpdatedNotification"; + static final String UPDATE_ATTRIBUTES_ONLY_ON_VALUE_CHANGE_KEY = "updateAttributesOnlyOnValueChange"; + private TbMsgAttributesNodeConfiguration config; @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { this.config = TbNodeUtils.convert(configuration, TbMsgAttributesNodeConfiguration.class); - if (config.getNotifyDevice() == null) { - config.setNotifyDevice(true); - } } @Override public void onMsg(TbContext ctx, TbMsg msg) { - if (!msg.getType().equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name())) { + if (!msg.isTypeOf(POST_ATTRIBUTES_REQUEST)) { ctx.tellFailure(msg, new IllegalArgumentException("Unsupported msg type: " + msg.getType())); return; } String src = msg.getData(); - List attributes = new ArrayList<>(JsonConverter.convertToAttributes(JsonParser.parseString(src))); - if (attributes.isEmpty()) { + List newAttributes = new ArrayList<>(JsonConverter.convertToAttributes(JsonParser.parseString(src))); + if (newAttributes.isEmpty()) { ctx.tellSuccess(msg); return; } String scope = getScope(msg.getMetaData().getValue(SCOPE)); boolean sendAttributesUpdateNotification = checkSendNotification(scope); + + if (!config.isUpdateAttributesOnlyOnValueChange()) { + saveAttr(newAttributes, ctx, msg, scope, sendAttributesUpdateNotification); + return; + } + + List keys = newAttributes.stream().map(KvEntry::getKey).collect(Collectors.toList()); + ListenableFuture> findFuture = ctx.getAttributesService().find(ctx.getTenantId(), msg.getOriginator(), scope, keys); + + DonAsynchron.withCallback(findFuture, + currentAttributes -> { + List attributesChanged = filterChangedAttr(currentAttributes, newAttributes); + saveAttr(attributesChanged, ctx, msg, scope, sendAttributesUpdateNotification); + }, + throwable -> ctx.tellFailure(msg, throwable), + MoreExecutors.directExecutor()); + } + + void saveAttr(List attributes, TbContext ctx, TbMsg msg, String scope, boolean sendAttributesUpdateNotification) { + if (attributes.isEmpty()) { + ctx.tellSuccess(msg); + return; + } ctx.getTelemetryService().saveAndNotify( ctx.getTenantId(), msg.getOriginator(), scope, attributes, - checkNotifyDevice(msg.getMetaData().getValue(NOTIFY_DEVICE_METADATA_KEY)), + config.isNotifyDevice() || checkNotifyDeviceMdValue(msg.getMetaData().getValue(NOTIFY_DEVICE_METADATA_KEY)), sendAttributesUpdateNotification ? new AttributesUpdateNodeCallback(ctx, msg, scope, attributes) : new TelemetryNodeCallback(ctx, msg) ); } + List filterChangedAttr(List currentAttributes, List newAttributes) { + if (currentAttributes == null || currentAttributes.isEmpty()) { + return newAttributes; + } + + Map currentAttrMap = currentAttributes.stream() + .collect(Collectors.toMap(AttributeKvEntry::getKey, Function.identity(), (existing, replacement) -> existing)); + + return newAttributes.stream() + .filter(item -> { + AttributeKvEntry cacheAttr = currentAttrMap.get(item.getKey()); + return cacheAttr == null + || !Objects.equals(item.getValue(), cacheAttr.getValue()) //JSON and String can be equals by value, but different by type + || !Objects.equals(item.getDataType(), cacheAttr.getDataType()); + }) + .collect(Collectors.toList()); + } + private boolean checkSendNotification(String scope) { return config.isSendAttributesUpdatedNotification() && !CLIENT_SCOPE.equals(scope); } - private boolean checkNotifyDevice(String notifyDeviceMdValue) { - return config.getNotifyDevice() || StringUtils.isEmpty(notifyDeviceMdValue) || Boolean.parseBoolean(notifyDeviceMdValue); + private boolean checkNotifyDeviceMdValue(String notifyDeviceMdValue) { + // Check for empty string for backward-compatibility. A while ago node always notified devices. + return StringUtils.isEmpty(notifyDeviceMdValue) || Boolean.parseBoolean(notifyDeviceMdValue); } private String getScope(String mdScopeValue) { @@ -104,4 +160,43 @@ public class TbMsgAttributesNode implements TbNode { return config.getScope(); } + @Override + public TbPair upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException { + boolean hasChanges = false; + switch (fromVersion) { + case 0: + if (!oldConfiguration.has(UPDATE_ATTRIBUTES_ONLY_ON_VALUE_CHANGE_KEY)) { + hasChanges = true; + ((ObjectNode) oldConfiguration).put(UPDATE_ATTRIBUTES_ONLY_ON_VALUE_CHANGE_KEY, false); + } + case 1: + // update notifyDevice. set true if null or property doesn't exist for backward-compatibility. + hasChanges = fixEscapedBooleanConfigParameter(oldConfiguration, NOTIFY_DEVICE_KEY, hasChanges, true); + // update sendAttributesUpdatedNotification. + hasChanges = fixEscapedBooleanConfigParameter(oldConfiguration, SEND_ATTRIBUTES_UPDATED_NOTIFICATION_KEY, hasChanges, false); + // update updateAttributesOnlyOnValueChange. + hasChanges = fixEscapedBooleanConfigParameter(oldConfiguration, UPDATE_ATTRIBUTES_ONLY_ON_VALUE_CHANGE_KEY, hasChanges, true); + break; + default: + break; + } + return new TbPair<>(hasChanges, oldConfiguration); + } + + private boolean fixEscapedBooleanConfigParameter(JsonNode oldConfiguration, String boolKey, boolean hasChanges, boolean valueIfNull) { + if (oldConfiguration.hasNonNull(boolKey)) { + var value = oldConfiguration.get(boolKey); + if (value.isTextual()) { + hasChanges = true; + ((ObjectNode) oldConfiguration) + .put(boolKey, value.asBoolean(valueIfNull)); + } + } else { + hasChanges = true; + ((ObjectNode) oldConfiguration) + .put(boolKey, valueIfNull); + } + return hasChanges; + } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeConfiguration.java index dd6140c4b5..d8502e76d9 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeConfiguration.java @@ -24,8 +24,9 @@ public class TbMsgAttributesNodeConfiguration implements NodeConfiguration> tsKvMap = JsonConverter.convertToTelemetry(new JsonParser().parse(src), ts); + Map> tsKvMap = JsonConverter.convertToTelemetry(JsonParser.parseString(src), ts); if (tsKvMap.isEmpty()) { ctx.tellFailure(msg, new IllegalArgumentException("Msg body is empty: " + src)); return; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/MultipleTbMsgsCallbackWrapper.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/MultipleTbMsgsCallbackWrapper.java index ee63201fde..142aa8c085 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/MultipleTbMsgsCallbackWrapper.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/MultipleTbMsgsCallbackWrapper.java @@ -39,7 +39,7 @@ public class MultipleTbMsgsCallbackWrapper implements TbMsgCallbackWrapper { @Override public void onFailure(Throwable t) { - callback.onFailure(new RuleEngineException(t.getMessage())); + callback.onFailure(new RuleEngineException(t.getMessage(), t)); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbAbstractTransformNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbAbstractTransformNode.java index 042c4c0c38..72570d4e2a 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbAbstractTransformNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbAbstractTransformNode.java @@ -22,8 +22,7 @@ import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; -import org.thingsboard.rule.engine.api.TbRelationTypes; -import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.queue.RuleEngineException; import org.thingsboard.server.common.msg.queue.TbMsgCallback; @@ -31,19 +30,18 @@ import org.thingsboard.server.common.msg.queue.TbMsgCallback; import java.util.List; import static org.thingsboard.common.util.DonAsynchron.withCallback; -import static org.thingsboard.rule.engine.api.TbRelationTypes.FAILURE; /** * Created by ashvayka on 19.01.18. */ @Slf4j -public abstract class TbAbstractTransformNode implements TbNode { +public abstract class TbAbstractTransformNode implements TbNode { - private TbTransformNodeConfiguration config; + protected C config; @Override - public void init(TbContext context, TbNodeConfiguration configuration) throws TbNodeException { - this.config = TbNodeUtils.convert(configuration, TbTransformNodeConfiguration.class); + public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { + config = loadNodeConfiguration(ctx, configuration); } @Override @@ -54,44 +52,33 @@ public abstract class TbAbstractTransformNode implements TbNode { MoreExecutors.directExecutor()); } + protected abstract C loadNodeConfiguration(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException; + protected void transformFailure(TbContext ctx, TbMsg msg, Throwable t) { ctx.tellFailure(msg, t); } - protected void transformSuccess(TbContext ctx, TbMsg msg, TbMsg m) { - if (m != null) { - ctx.tellSuccess(m); - } else { - ctx.tellNext(msg, FAILURE); - } - } - protected void transformSuccess(TbContext ctx, TbMsg msg, List msgs) { - if (msgs != null && !msgs.isEmpty()) { - if (msgs.size() == 1) { - ctx.tellSuccess(msgs.get(0)); - } else { - TbMsgCallbackWrapper wrapper = new MultipleTbMsgsCallbackWrapper(msgs.size(), new TbMsgCallback() { - @Override - public void onSuccess() { - ctx.ack(msg); - } - - @Override - public void onFailure(RuleEngineException e) { - ctx.tellFailure(msg, e); - } - }); - msgs.forEach(newMsg -> ctx.enqueueForTellNext(newMsg, TbRelationTypes.SUCCESS, wrapper::onSuccess, wrapper::onFailure)); - } + if (msgs == null || msgs.isEmpty()) { + ctx.tellFailure(msg, new RuntimeException("Message or messages list are empty!")); + } else if (msgs.size() == 1) { + ctx.tellSuccess(msgs.get(0)); } else { - ctx.tellNext(msg, FAILURE); + TbMsgCallbackWrapper wrapper = new MultipleTbMsgsCallbackWrapper(msgs.size(), new TbMsgCallback() { + @Override + public void onSuccess() { + ctx.ack(msg); + } + + @Override + public void onFailure(RuleEngineException e) { + ctx.tellFailure(msg, e); + } + }); + msgs.forEach(newMsg -> ctx.enqueueForTellNext(newMsg, TbNodeConnectionType.SUCCESS, wrapper::onSuccess, wrapper::onFailure)); } } protected abstract ListenableFuture> transform(TbContext ctx, TbMsg msg); - public void setConfig(TbTransformNodeConfiguration config) { - this.config = config; - } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbAbstractTransformNodeWithTbMsgSource.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbAbstractTransformNodeWithTbMsgSource.java new file mode 100644 index 0000000000..02e99d90fe --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbAbstractTransformNodeWithTbMsgSource.java @@ -0,0 +1,91 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.transform; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.thingsboard.rule.engine.api.TbNode; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.util.TbMsgSource; +import org.thingsboard.server.common.data.util.TbPair; + +public abstract class TbAbstractTransformNodeWithTbMsgSource implements TbNode { + + protected static final String FROM_METADATA_PROPERTY = "fromMetadata"; + + protected abstract String getNewKeyForUpgradeFromVersionZero(); + + protected abstract String getKeyToUpgradeFromVersionOne(); + + @Override + public TbPair upgrade(int fromVersion, JsonNode oldConfiguration) throws TbNodeException { + ObjectNode configToUpdate = (ObjectNode) oldConfiguration; + switch (fromVersion) { + case 0: + return upgradeToUseTbMsgSource(configToUpdate); + case 1: + return upgradeNodesWithVersionOneToUseTbMsgSource(configToUpdate); + default: + return new TbPair<>(false, oldConfiguration); + } + } + + private TbPair upgradeToUseTbMsgSource(ObjectNode configToUpdate) throws TbNodeException { + if (!configToUpdate.has(FROM_METADATA_PROPERTY)) { + throw new TbNodeException("property to update: '" + FROM_METADATA_PROPERTY + "' doesn't exists in configuration!"); + } + var value = configToUpdate.get(FROM_METADATA_PROPERTY).asText(); + if ("true".equals(value)) { + configToUpdate.remove(FROM_METADATA_PROPERTY); + configToUpdate.put(getNewKeyForUpgradeFromVersionZero(), TbMsgSource.METADATA.name()); + return new TbPair<>(true, configToUpdate); + } + if ("false".equals(value)) { + configToUpdate.remove(FROM_METADATA_PROPERTY); + configToUpdate.put(getNewKeyForUpgradeFromVersionZero(), TbMsgSource.DATA.name()); + return new TbPair<>(true, configToUpdate); + } + throw new TbNodeException("property to update: '" + FROM_METADATA_PROPERTY + "' has unexpected value: " + + value + ". Allowed values: true or false!"); + } + + private TbPair upgradeNodesWithVersionOneToUseTbMsgSource(ObjectNode configToUpdate) throws TbNodeException { + if (configToUpdate.has(getNewKeyForUpgradeFromVersionZero())) { + return new TbPair<>(false, configToUpdate); + } + return upgradeTbMsgSourceKey(configToUpdate, getKeyToUpgradeFromVersionOne()); + } + + private TbPair upgradeTbMsgSourceKey(ObjectNode configToUpdate, String oldPropertyKey) throws TbNodeException { + if (!configToUpdate.has(oldPropertyKey)) { + throw new TbNodeException("property to update: '" + oldPropertyKey + "' doesn't exists in configuration!"); + } + var value = configToUpdate.get(oldPropertyKey).asText(); + if (TbMsgSource.METADATA.name().equals(value)) { + configToUpdate.remove(oldPropertyKey); + configToUpdate.put(getNewKeyForUpgradeFromVersionZero(), TbMsgSource.METADATA.name()); + return new TbPair<>(true, configToUpdate); + } + if (TbMsgSource.DATA.name().equals(value)) { + configToUpdate.remove(oldPropertyKey); + configToUpdate.put(getNewKeyForUpgradeFromVersionZero(), TbMsgSource.DATA.name()); + return new TbPair<>(true, configToUpdate); + } + throw new TbNodeException("property to update: '" + oldPropertyKey + "' has unexpected value: " + + value + ". Allowed values: true or false!"); + } + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java index 911b811032..ea412be3fe 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java @@ -34,48 +34,52 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; -import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.NoSuchElementException; @Slf4j @RuleNode( type = ComponentType.TRANSFORMATION, name = "change originator", configClazz = TbChangeOriginatorNodeConfiguration.class, - nodeDescription = "Change Message Originator To Tenant/Customer/Related Entity/Alarm Originator", - nodeDetails = "Related Entity found using configured relation direction and Relation Type. " + - "If multiple Related Entities are found, only first Entity is used as new Originator, other entities are discarded.
" + - "Alarm Originator found only in case original Originator is Alarm entity.", + nodeDescription = "Change message originator to Tenant/Customer/Related Entity/Alarm Originator/Entity by name pattern.", + nodeDetails = "Configuration:
  • Customer - use customer of incoming message originator as new originator. " + + "Only for assigned to customer originators with one of the following type: 'User', 'Asset', 'Device'.
  • " + + "
  • Tenant - use current tenant as new originator.
  • " + + "
  • Related Entity - use related entity as new originator. Lookup based on configured relation query. " + + "If multiple related entities are found, only first entity is used as new originator, other entities are discarded.
  • " + + "
  • Alarm Originator - use alarm originator as new originator. Only if incoming message originator is alarm entity.
  • " + + "
  • Entity by name pattern - specify entity type and name pattern of new originator. Following entity types are supported: " + + "'Device', 'Asset', 'Entity View', 'Edge' or 'User'.
" + + "Output connections: Success, Failure.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbTransformationNodeChangeOriginatorConfig", icon = "find_replace" ) -public class TbChangeOriginatorNode extends TbAbstractTransformNode { +public class TbChangeOriginatorNode extends TbAbstractTransformNode { - protected static final String CUSTOMER_SOURCE = "CUSTOMER"; - protected static final String TENANT_SOURCE = "TENANT"; - protected static final String RELATED_SOURCE = "RELATED"; - protected static final String ALARM_ORIGINATOR_SOURCE = "ALARM_ORIGINATOR"; - protected static final String ENTITY_SOURCE = "ENTITY"; - - private TbChangeOriginatorNodeConfiguration config; + private static final String CUSTOMER_SOURCE = "CUSTOMER"; + private static final String TENANT_SOURCE = "TENANT"; + private static final String RELATED_SOURCE = "RELATED"; + private static final String ALARM_ORIGINATOR_SOURCE = "ALARM_ORIGINATOR"; + private static final String ENTITY_SOURCE = "ENTITY"; @Override - public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { - this.config = TbNodeUtils.convert(configuration, TbChangeOriginatorNodeConfiguration.class); + protected TbChangeOriginatorNodeConfiguration loadNodeConfiguration(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { + var config = TbNodeUtils.convert(configuration, TbChangeOriginatorNodeConfiguration.class); validateConfig(config); - setConfig(config); + return config; } @Override protected ListenableFuture> transform(TbContext ctx, TbMsg msg) { - ListenableFuture newOriginator = getNewOriginator(ctx, msg); - return Futures.transform(newOriginator, n -> { - if (n == null || n.isNullUid()) { - return null; + ListenableFuture newOriginatorFuture = getNewOriginator(ctx, msg); + return Futures.transformAsync(newOriginatorFuture, newOriginator -> { + if (newOriginator == null || newOriginator.isNullUid()) { + return Futures.immediateFailedFuture(new NoSuchElementException("Failed to find new originator!")); } - return Collections.singletonList((ctx.transformMsg(msg, msg.getType(), n, msg.getMetaData(), msg.getData()))); + return Futures.immediateFuture(List.of(ctx.transformMsgOriginator(msg, newOriginator))); }, ctx.getDbCallbackExecutor()); } @@ -129,7 +133,6 @@ public class TbChangeOriginatorNode extends TbAbstractTransformNode { } EntitiesByNameAndTypeLoader.checkEntityType(EntityType.valueOf(conf.getEntityType())); } - } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeConfiguration.java index cc85933a48..473e83b91c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeConfiguration.java @@ -25,7 +25,9 @@ import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; import java.util.Collections; @Data -public class TbChangeOriginatorNodeConfiguration extends TbTransformNodeConfiguration implements NodeConfiguration { +public class TbChangeOriginatorNodeConfiguration implements NodeConfiguration { + + private static final String CUSTOMER_SOURCE = "CUSTOMER"; private String originatorSource; @@ -36,7 +38,7 @@ public class TbChangeOriginatorNodeConfiguration extends TbTransformNodeConfigur @Override public TbChangeOriginatorNodeConfiguration defaultConfiguration() { TbChangeOriginatorNodeConfiguration configuration = new TbChangeOriginatorNodeConfiguration(); - configuration.setOriginatorSource(TbChangeOriginatorNode.CUSTOMER_SOURCE); + configuration.setOriginatorSource(CUSTOMER_SOURCE); RelationsQuery relationsQuery = new RelationsQuery(); relationsQuery.setDirection(EntitySearchDirection.FROM); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java index 7353d285c1..b96486fe4c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java @@ -21,87 +21,105 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.rule.engine.util.TbMsgSource; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; -import org.thingsboard.server.common.msg.TbMsgMetaData; -import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.regex.Pattern; +import java.util.stream.Collectors; @Slf4j @RuleNode( type = ComponentType.TRANSFORMATION, - name = "copy keys", + name = "copy key-value pairs", + version = 2, configClazz = TbCopyKeysNodeConfiguration.class, - nodeDescription = "Copies the msg or metadata keys with specified key names selected in the list", - nodeDetails = "Will fetch fields values specified in list. If specified field is not part of msg or metadata fields it will be ignored." + - "Returns transformed messages via Success chain", + nodeDescription = "Copies key-value pairs from message to message metadata or vice-versa.", + nodeDetails = "Copies key-value pairs from the message to message metadata, or vice-versa, according to the configured direction and keys. " + + "Regular expressions can be used to define which keys-value pairs to copy. Any configured key not found in the source will be ignored.

" + + "Output connections: Success, Failure.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbTransformationNodeCopyKeysConfig", icon = "content_copy" ) -public class TbCopyKeysNode implements TbNode { +public class TbCopyKeysNode extends TbAbstractTransformNodeWithTbMsgSource { private TbCopyKeysNodeConfiguration config; - private List patternKeys; - private boolean fromMetadata; + private TbMsgSource copyFrom; + private List compiledKeyPatterns; @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { this.config = TbNodeUtils.convert(configuration, TbCopyKeysNodeConfiguration.class); - this.fromMetadata = config.isFromMetadata(); - this.patternKeys = new ArrayList<>(); - config.getKeys().forEach(key -> { - this.patternKeys.add(Pattern.compile(key)); - }); + this.copyFrom = config.getCopyFrom(); + if (copyFrom == null) { + throw new TbNodeException("CopyFrom can't be null! Allowed values: " + Arrays.toString(TbMsgSource.values())); + } + this.compiledKeyPatterns = config.getKeys().stream().map(Pattern::compile).collect(Collectors.toList()); } @Override public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException { - TbMsgMetaData metaData = msg.getMetaData(); + var metaDataCopy = msg.getMetaData().copy(); String msgData = msg.getData(); boolean msgChanged = false; JsonNode dataNode = JacksonUtil.toJsonNode(msgData); if (dataNode.isObject()) { - if (fromMetadata) { - ObjectNode msgDataNode = (ObjectNode) dataNode; - Map metaDataMap = metaData.getData(); - for (Map.Entry entry : metaDataMap.entrySet()) { - String keyData = entry.getKey(); - if (checkKey(keyData)) { - msgChanged = true; - msgDataNode.put(keyData, entry.getValue()); + switch (copyFrom) { + case METADATA: + ObjectNode msgDataNode = (ObjectNode) dataNode; + Map metaDataMap = metaDataCopy.getData(); + for (Map.Entry entry : metaDataMap.entrySet()) { + String mdKey = entry.getKey(); + String mdValue = entry.getValue(); + if (matches(mdKey)) { + msgChanged = true; + msgDataNode.put(mdKey, mdValue); + } } - } - msgData = JacksonUtil.toString(msgDataNode); - } else { - Iterator> iteratorNode = dataNode.fields(); - while (iteratorNode.hasNext()) { - Map.Entry entry = iteratorNode.next(); - String keyData = entry.getKey(); - if (checkKey(keyData)) { - msgChanged = true; - metaData.putValue(keyData, JacksonUtil.toString(entry.getValue())); + msgData = JacksonUtil.toString(msgDataNode); + break; + case DATA: + Iterator> iteratorNode = dataNode.fields(); + while (iteratorNode.hasNext()) { + Map.Entry entry = iteratorNode.next(); + String msgKey = entry.getKey(); + JsonNode msgValue = entry.getValue(); + if (matches(msgKey)) { + msgChanged = true; + String value = msgValue.isTextual() ? + msgValue.asText() : JacksonUtil.toString(msgValue); + metaDataCopy.putValue(msgKey, value); + } } - } + break; + default: + log.debug("Unexpected CopyFrom value: {}. Allowed values: {}", copyFrom, TbMsgSource.values()); } } - if (msgChanged) { - ctx.tellSuccess(TbMsg.transformMsg(msg, msg.getType(), msg.getOriginator(), metaData, msgData)); - } else { - ctx.tellSuccess(msg); - } + ctx.tellSuccess(msgChanged ? TbMsg.transformMsg(msg, metaDataCopy, msgData) : msg); } - boolean checkKey(String key) { - return patternKeys.stream().anyMatch(pattern -> pattern.matcher(key).matches()); + @Override + protected String getNewKeyForUpgradeFromVersionZero() { + return "copyFrom"; } + + @Override + protected String getKeyToUpgradeFromVersionOne() { + return FROM_METADATA_PROPERTY; + } + + boolean matches(String key) { + return compiledKeyPatterns.stream().anyMatch(pattern -> pattern.matcher(key).matches()); + } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeConfiguration.java index 6bf9f1e343..5c8d7edf4b 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeConfiguration.java @@ -17,6 +17,7 @@ package org.thingsboard.rule.engine.transform; import lombok.Data; import org.thingsboard.rule.engine.api.NodeConfiguration; +import org.thingsboard.rule.engine.util.TbMsgSource; import java.util.Collections; import java.util.Set; @@ -24,14 +25,14 @@ import java.util.Set; @Data public class TbCopyKeysNodeConfiguration implements NodeConfiguration { - private boolean fromMetadata; + private TbMsgSource copyFrom; private Set keys; @Override public TbCopyKeysNodeConfiguration defaultConfiguration() { TbCopyKeysNodeConfiguration configuration = new TbCopyKeysNodeConfiguration(); configuration.setKeys(Collections.emptySet()); - configuration.setFromMetadata(false); + configuration.setCopyFrom(TbMsgSource.DATA); return configuration; } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java index 66435035d7..65ecdd42e2 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java @@ -21,84 +21,100 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.rule.engine.util.TbMsgSource; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.regex.Pattern; +import java.util.stream.Collectors; @Slf4j @RuleNode( type = ComponentType.TRANSFORMATION, - name = "delete keys", + name = "delete key-value pairs", + version = 2, configClazz = TbDeleteKeysNodeConfiguration.class, - nodeDescription = "Removes keys from the msg data or metadata with the specified key names selected in the list", - nodeDetails = "Will fetch fields (regex) values specified in list. If specified field (regex) is not part of msg " + - "or metadata fields it will be ignored. Returns transformed messages via Success chain", + nodeDescription = "Deletes key-value pairs from message or message metadata.", + nodeDetails = "Deletes key-value pairs from the message or message metadata according to the configured " + + "keys and/or regular expressions.

" + + "Output connections: Success, Failure.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbTransformationNodeDeleteKeysConfig", icon = "remove_circle" ) -public class TbDeleteKeysNode implements TbNode { +public class TbDeleteKeysNode extends TbAbstractTransformNodeWithTbMsgSource { private TbDeleteKeysNodeConfiguration config; - private List patternKeys; - private boolean fromMetadata; + private TbMsgSource deleteFrom; + private List compiledKeyPatterns; @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { this.config = TbNodeUtils.convert(configuration, TbDeleteKeysNodeConfiguration.class); - this.fromMetadata = config.isFromMetadata(); - this.patternKeys = new ArrayList<>(); - config.getKeys().forEach(key -> { - this.patternKeys.add(Pattern.compile(key)); - }); + this.deleteFrom = config.getDeleteFrom(); + if (deleteFrom == null) { + throw new TbNodeException("DeleteFrom can't be null! Allowed values: " + Arrays.toString(TbMsgSource.values())); + } + this.compiledKeyPatterns = config.getKeys().stream().map(Pattern::compile).collect(Collectors.toList()); } @Override public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException { - TbMsgMetaData metaData = msg.getMetaData(); - String msgData = msg.getData(); - List keysToDelete = new ArrayList<>(); - if (fromMetadata) { - Map metaDataMap = metaData.getData(); - metaDataMap.forEach((keyMetaData, valueMetaData) -> { - if (checkKey(keyMetaData)) { - keysToDelete.add(keyMetaData); + var metaDataCopy = msg.getMetaData().copy(); + var msgDataStr = msg.getData(); + boolean hasNoChanges = false; + switch (deleteFrom) { + case METADATA: + var metaDataMap = metaDataCopy.getData(); + var mdKeysToDelete = metaDataMap.keySet() + .stream() + .filter(this::matches) + .collect(Collectors.toList()); + mdKeysToDelete.forEach(metaDataMap::remove); + metaDataCopy = new TbMsgMetaData(metaDataMap); + hasNoChanges = mdKeysToDelete.isEmpty(); + break; + case DATA: + JsonNode dataNode = JacksonUtil.toJsonNode(msgDataStr); + if (dataNode.isObject()) { + var msgDataObject = (ObjectNode) dataNode; + var msgKeysToDelete = new ArrayList(); + dataNode.fieldNames().forEachRemaining(key -> { + if (matches(key)) { + msgKeysToDelete.add(key); + } + }); + msgDataObject.remove(msgKeysToDelete); + msgDataStr = JacksonUtil.toString(msgDataObject); + hasNoChanges = msgKeysToDelete.isEmpty(); } - }); - keysToDelete.forEach(key -> metaDataMap.remove(key)); - metaData = new TbMsgMetaData(metaDataMap); - } else { - JsonNode dataNode = JacksonUtil.toJsonNode(msgData); - if (dataNode.isObject()) { - ObjectNode msgDataObject = (ObjectNode) dataNode; - dataNode.fields().forEachRemaining(entry -> { - String keyData = entry.getKey(); - if (checkKey(keyData)) { - keysToDelete.add(keyData); - } - }); - msgDataObject.remove(keysToDelete); - msgData = JacksonUtil.toString(msgDataObject); - } - } - if (keysToDelete.isEmpty()) { - ctx.tellSuccess(msg); - } else { - ctx.tellSuccess(TbMsg.transformMsg(msg, msg.getType(), msg.getOriginator(), metaData, msgData)); + break; + default: + log.debug("Unexpected DeleteFrom value: {}. Allowed values: {}", deleteFrom, TbMsgSource.values()); } + ctx.tellSuccess(hasNoChanges ? msg : TbMsg.transformMsg(msg, metaDataCopy, msgDataStr)); } - boolean checkKey(String key) { - return patternKeys.stream().anyMatch(pattern -> pattern.matcher(key).matches()); + @Override + protected String getNewKeyForUpgradeFromVersionZero() { + return "deleteFrom"; } + + @Override + protected String getKeyToUpgradeFromVersionOne() { + return "dataToFetch"; + } + + boolean matches(String key) { + return compiledKeyPatterns.stream().anyMatch(pattern -> pattern.matcher(key).matches()); + } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeConfiguration.java index d0b22df409..d68b391df6 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeConfiguration.java @@ -17,6 +17,7 @@ package org.thingsboard.rule.engine.transform; import lombok.Data; import org.thingsboard.rule.engine.api.NodeConfiguration; +import org.thingsboard.rule.engine.util.TbMsgSource; import java.util.Collections; import java.util.Set; @@ -24,14 +25,14 @@ import java.util.Set; @Data public class TbDeleteKeysNodeConfiguration implements NodeConfiguration { - private boolean fromMetadata; + private TbMsgSource deleteFrom; private Set keys; @Override public TbDeleteKeysNodeConfiguration defaultConfiguration() { TbDeleteKeysNodeConfiguration configuration = new TbDeleteKeysNodeConfiguration(); configuration.setKeys(Collections.emptySet()); - configuration.setFromMetadata(false); + configuration.setDeleteFrom(TbMsgSource.DATA); return configuration; } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java index da7d765a95..b22ce20dcb 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java @@ -38,10 +38,8 @@ import java.util.concurrent.ExecutionException; name = "json path", configClazz = TbJsonPathNodeConfiguration.class, nodeDescription = "Transforms incoming message body using JSONPath expression.", - nodeDetails = "JSONPath expression specifies a path to an element or a set of elements in a JSON structure.
" - + "'$' represents the root object or array.
" - + "If JSONPath expression evaluation failed, incoming message routes via Failure chain, " - + "otherwise Success chain is used.", + nodeDetails = "JSONPath expression specifies a path to an element or a set of elements in a JSON structure.

" + + "Output connections: Success, Failure.", uiResources = {"static/rulenode/rulenode-core-config.js"}, icon = "functions", configDirective = "tbTransformationNodeJsonPathConfig" @@ -70,7 +68,7 @@ public class TbJsonPathNode implements TbNode { if (!TbJsonPathNodeConfiguration.DEFAULT_JSON_PATH.equals(this.jsonPathValue)) { try { Object jsonPathData = jsonPath.read(msg.getData(), this.configurationJsonPath); - ctx.tellSuccess(TbMsg.transformMsg(msg, msg.getType(), msg.getOriginator(), msg.getMetaData(), JacksonUtil.toString(jsonPathData))); + ctx.tellSuccess(TbMsg.transformMsgData(msg, JacksonUtil.toString(jsonPathData))); } catch (PathNotFoundException e) { ctx.tellFailure(msg, e); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java index 3fce7494ea..64850ee80a 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java @@ -21,14 +21,15 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.rule.engine.util.TbMsgSource; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; +import java.util.Arrays; import java.util.Map; import java.util.concurrent.ExecutionException; @@ -36,62 +37,86 @@ import java.util.concurrent.ExecutionException; @RuleNode( type = ComponentType.TRANSFORMATION, name = "rename keys", + version = 2, configClazz = TbRenameKeysNodeConfiguration.class, - nodeDescription = "Renames msg data or metadata keys to the new key names selected in the key mapping.", - nodeDetails = "If the key that is selected in the key mapping is missed in the selected msg source(data or metadata), it will be ignored." + - " Returns transformed messages via Success chain", + nodeDescription = "Renames message or message metadata keys.", + nodeDetails = "Renames keys in the message or message metadata according to the provided mapping. " + + "If key to rename doesn't exist in the specified source (message or message metadata) it will be ignored.

" + + "Output connections: Success, Failure.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbTransformationNodeRenameKeysConfig", icon = "find_replace" ) -public class TbRenameKeysNode implements TbNode { +public class TbRenameKeysNode extends TbAbstractTransformNodeWithTbMsgSource { private TbRenameKeysNodeConfiguration config; private Map renameKeysMapping; - private boolean fromMetadata; + private TbMsgSource renameIn; @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { this.config = TbNodeUtils.convert(configuration, TbRenameKeysNodeConfiguration.class); + this.renameIn = config.getRenameIn(); this.renameKeysMapping = config.getRenameKeysMapping(); - this.fromMetadata = config.isFromMetadata(); + if (renameIn == null) { + throw new TbNodeException("RenameIn can't be null! Allowed values: " + Arrays.toString(TbMsgSource.values())); + } + if (renameKeysMapping == null || renameKeysMapping.isEmpty()) { + throw new TbNodeException("At least one mapping entry should be specified!"); + } } @Override public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException { - TbMsgMetaData metaData = msg.getMetaData(); + TbMsgMetaData metaDataCopy = msg.getMetaData().copy(); String data = msg.getData(); boolean msgChanged = false; - if (fromMetadata) { - Map metaDataMap = metaData.getData(); - for (Map.Entry entry : renameKeysMapping.entrySet()) { - String nameKey = entry.getKey(); - if (metaDataMap.containsKey(nameKey)) { - msgChanged = true; - metaDataMap.put(entry.getValue(), metaDataMap.get(nameKey)); - metaDataMap.remove(nameKey); - } - } - metaData = new TbMsgMetaData(metaDataMap); - } else { - JsonNode dataNode = JacksonUtil.toJsonNode(msg.getData()); - if (dataNode.isObject()) { - ObjectNode msgData = (ObjectNode) dataNode; + switch (renameIn) { + case METADATA: + Map metaDataMap = metaDataCopy.getData(); for (Map.Entry entry : renameKeysMapping.entrySet()) { - String nameKey = entry.getKey(); - if (msgData.has(nameKey)) { + String currentKeyName = entry.getKey(); + String newKeyName = entry.getValue(); + if (metaDataMap.containsKey(currentKeyName)) { msgChanged = true; - msgData.set(entry.getValue(), msgData.get(nameKey)); - msgData.remove(nameKey); + String value = metaDataMap.get(currentKeyName); + metaDataMap.put(newKeyName, value); + metaDataMap.remove(currentKeyName); } } - data = JacksonUtil.toString(msgData); - } - } - if (msgChanged) { - ctx.tellSuccess(TbMsg.transformMsg(msg, msg.getType(), msg.getOriginator(), metaData, data)); - } else { - ctx.tellSuccess(msg); + metaDataCopy = new TbMsgMetaData(metaDataMap); + break; + case DATA: + JsonNode dataNode = JacksonUtil.toJsonNode(data); + if (dataNode.isObject()) { + ObjectNode msgData = (ObjectNode) dataNode; + for (Map.Entry entry : renameKeysMapping.entrySet()) { + String currentKeyName = entry.getKey(); + String newKeyName = entry.getValue(); + if (msgData.has(currentKeyName)) { + msgChanged = true; + JsonNode value = msgData.get(currentKeyName); + msgData.set(newKeyName, value); + msgData.remove(currentKeyName); + } + } + data = JacksonUtil.toString(msgData); + } + break; + default: + log.debug("Unexpected RenameIn value: {}. Allowed values: {}", renameIn, TbMsgSource.values()); } + ctx.tellSuccess(msgChanged ? TbMsg.transformMsg(msg, metaDataCopy, data) : msg); } + + @Override + protected String getNewKeyForUpgradeFromVersionZero() { + return "renameIn"; + } + + @Override + protected String getKeyToUpgradeFromVersionOne() { + return FROM_METADATA_PROPERTY; + } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeConfiguration.java index 0475ba4ca4..1e6d5e1483 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeConfiguration.java @@ -17,20 +17,21 @@ package org.thingsboard.rule.engine.transform; import lombok.Data; import org.thingsboard.rule.engine.api.NodeConfiguration; +import org.thingsboard.rule.engine.util.TbMsgSource; import java.util.Map; @Data public class TbRenameKeysNodeConfiguration implements NodeConfiguration { - private boolean fromMetadata; + private TbMsgSource renameIn; private Map renameKeysMapping; @Override public TbRenameKeysNodeConfiguration defaultConfiguration() { TbRenameKeysNodeConfiguration configuration = new TbRenameKeysNodeConfiguration(); - configuration.setRenameKeysMapping(Map.of("temp", "temperature")); - configuration.setFromMetadata(false); + configuration.setRenameKeysMapping(Map.of("temperatureCelsius", "temperature")); + configuration.setRenameIn(TbMsgSource.DATA); return configuration; } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java index ae7ceade97..40ddccbea7 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java @@ -25,8 +25,8 @@ import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; -import org.thingsboard.rule.engine.api.TbRelationTypes; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.queue.RuleEngineException; @@ -39,10 +39,10 @@ import java.util.concurrent.ExecutionException; type = ComponentType.TRANSFORMATION, name = "split array msg", configClazz = EmptyNodeConfiguration.class, - nodeDescription = "Split array message into several msgs", - nodeDetails = "Split the array fetched from the msg body. If the msg data is not a JSON array returns the " - + "incoming message as outbound message with Failure chain, otherwise returns " - + "inner objects of the extracted array as separate messages via Success chain.", + nodeDescription = "Split array message into several messages", + nodeDetails = "Splits an array message into individual elements, with each element sent as a separate message. " + + "All outbound messages will have the same type and metadata as the original array message.

" + + "Output connections: Success, Failure.", uiResources = {"static/rulenode/rulenode-core-config.js"}, icon = "content_copy", configDirective = "tbNodeEmptyConfig" @@ -64,7 +64,7 @@ public class TbSplitArrayMsgNode implements TbNode { if (data.isEmpty()) { ctx.ack(msg); } else if (data.size() == 1) { - ctx.tellSuccess(TbMsg.transformMsg(msg, msg.getType(), msg.getOriginator(), msg.getMetaData(), JacksonUtil.toString(data.get(0)))); + ctx.tellSuccess(TbMsg.transformMsgData(msg, JacksonUtil.toString(data.get(0)))); } else { TbMsgCallbackWrapper wrapper = new MultipleTbMsgsCallbackWrapper(data.size(), new TbMsgCallback() { @Override @@ -78,8 +78,8 @@ public class TbSplitArrayMsgNode implements TbNode { } }); data.forEach(msgNode -> { - TbMsg outMsg = TbMsg.transformMsg(msg, msg.getType(), msg.getOriginator(), msg.getMetaData(), JacksonUtil.toString(msgNode)); - ctx.enqueueForTellNext(outMsg, TbRelationTypes.SUCCESS, wrapper::onSuccess, wrapper::onFailure); + TbMsg outMsg = TbMsg.transformMsgData(msg, JacksonUtil.toString(msgNode)); + ctx.enqueueForTellNext(outMsg, TbNodeConnectionType.SUCCESS, wrapper::onSuccess, wrapper::onFailure); }); } } else { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java index 5c55cfb46d..79549edbd7 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java @@ -34,26 +34,26 @@ import java.util.List; configClazz = TbTransformMsgNodeConfiguration.class, nodeDescription = "Change Message payload, Metadata or Message type using JavaScript", nodeDetails = "JavaScript function receive 3 input parameters
" + - "metadata - is a Message metadata.
" + - "msg - is a Message payload.
" + - "msgType - is a Message type.
" + + "msg - is a message payload.
" + + "metadata - is a message metadata.
" + + "msgType - is a message type.
" + "Should return the following structure:
" + "{ msg: new payload,
   metadata: new metadata,
   msgType: new msgType }

" + - "All fields in resulting object are optional and will be taken from original message if not specified.", + "All fields in resulting object are optional and will be taken from original message if not specified.

" + + "Output connections: Success, Failure.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbTransformationNodeScriptConfig" ) -public class TbTransformMsgNode extends TbAbstractTransformNode { +public class TbTransformMsgNode extends TbAbstractTransformNode { - private TbTransformMsgNodeConfiguration config; private ScriptEngine scriptEngine; @Override - public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { - this.config = TbNodeUtils.convert(configuration, TbTransformMsgNodeConfiguration.class); + protected TbTransformMsgNodeConfiguration loadNodeConfiguration(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { + var config = TbNodeUtils.convert(configuration, TbTransformMsgNodeConfiguration.class); scriptEngine = ctx.createScriptEngine(config.getScriptLang(), ScriptLanguage.TBEL.equals(config.getScriptLang()) ? config.getTbelScript() : config.getJsScript()); - setConfig(config); + return config; } @Override @@ -62,12 +62,6 @@ public class TbTransformMsgNode extends TbAbstractTransformNode { return scriptEngine.executeUpdateAsync(msg); } - @Override - protected void transformSuccess(TbContext ctx, TbMsg msg, TbMsg m) { - ctx.logJsEvalResponse(); - super.transformSuccess(ctx, msg, m); - } - @Override protected void transformFailure(TbContext ctx, TbMsg msg, Throwable t) { ctx.logJsEvalFailure(); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeConfiguration.java index 2d4aeac161..34d465f49c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeConfiguration.java @@ -20,7 +20,7 @@ import org.thingsboard.rule.engine.api.NodeConfiguration; import org.thingsboard.server.common.data.script.ScriptLanguage; @Data -public class TbTransformMsgNodeConfiguration extends TbTransformNodeConfiguration implements NodeConfiguration { +public class TbTransformMsgNodeConfiguration implements NodeConfiguration { private ScriptLanguage scriptLang; private String jsScript; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformNodeConfiguration.java deleted file mode 100644 index 160bd6d8d1..0000000000 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformNodeConfiguration.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.rule.engine.transform; - -import lombok.Data; - -@Data -public class TbTransformNodeConfiguration { - -} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/ContactBasedEntityDetails.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/ContactBasedEntityDetails.java new file mode 100644 index 0000000000..83aaff6a6d --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/ContactBasedEntityDetails.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.util; + +import lombok.Getter; + +public enum ContactBasedEntityDetails { + + ID("id"), + TITLE("title"), + COUNTRY("country"), + CITY("city"), + STATE("state"), + ZIP("zip"), + ADDRESS("address"), + ADDRESS2("address2"), + PHONE("phone"), + EMAIL("email"), + ADDITIONAL_INFO("additionalInfo"); + + @Getter + private final String ruleEngineName; + + ContactBasedEntityDetails(String ruleEngineName) { + this.ruleEngineName = ruleEngineName; + } + +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesAlarmOriginatorIdAsyncLoader.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesAlarmOriginatorIdAsyncLoader.java index 7717f7f8a4..346d5e7903 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesAlarmOriginatorIdAsyncLoader.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesAlarmOriginatorIdAsyncLoader.java @@ -17,7 +17,6 @@ package org.thingsboard.rule.engine.util; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.server.common.data.alarm.Alarm; @@ -26,20 +25,19 @@ import org.thingsboard.server.common.data.id.EntityId; public class EntitiesAlarmOriginatorIdAsyncLoader { - public static ListenableFuture findEntityIdAsync(TbContext ctx, EntityId original) { - - switch (original.getEntityType()) { + public static ListenableFuture findEntityIdAsync(TbContext ctx, EntityId originator) { + switch (originator.getEntityType()) { case ALARM: - return getAlarmOriginatorAsync(ctx.getAlarmService().findAlarmByIdAsync(ctx.getTenantId(), (AlarmId) original)); + return getAlarmOriginatorAsync(ctx.getAlarmService().findAlarmByIdAsync(ctx.getTenantId(), (AlarmId) originator), ctx); default: - return Futures.immediateFailedFuture(new TbNodeException("Unexpected original EntityType " + original.getEntityType())); + return Futures.immediateFailedFuture(new TbNodeException("Unexpected originator EntityType " + originator.getEntityType())); } } - private static ListenableFuture getAlarmOriginatorAsync(ListenableFuture future) { - return Futures.transformAsync(future, in -> { - return in != null ? Futures.immediateFuture(in.getOriginator()) - : Futures.immediateFuture(null); - }, MoreExecutors.directExecutor()); + private static ListenableFuture getAlarmOriginatorAsync(ListenableFuture future, TbContext ctx) { + return Futures.transformAsync(future, in -> in != null ? + Futures.immediateFuture(in.getOriginator()) + : Futures.immediateFuture(null), ctx.getDbCallbackExecutor()); } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesCustomerIdAsyncLoader.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesCustomerIdAsyncLoader.java index 215f044729..5a19fa860f 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesCustomerIdAsyncLoader.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesCustomerIdAsyncLoader.java @@ -17,7 +17,6 @@ package org.thingsboard.rule.engine.util; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.server.common.data.HasCustomerId; @@ -29,24 +28,23 @@ import org.thingsboard.server.common.data.id.UserId; public class EntitiesCustomerIdAsyncLoader { - - public static ListenableFuture findEntityIdAsync(TbContext ctx, EntityId original) { - switch (original.getEntityType()) { + public static ListenableFuture findEntityIdAsync(TbContext ctx, EntityId originator) { + switch (originator.getEntityType()) { case CUSTOMER: - return Futures.immediateFuture((CustomerId) original); + return Futures.immediateFuture((CustomerId) originator); case USER: - return getCustomerAsync(ctx.getUserService().findUserByIdAsync(ctx.getTenantId(), (UserId) original)); + return toCustomerIdAsync(ctx, ctx.getUserService().findUserByIdAsync(ctx.getTenantId(), (UserId) originator)); case ASSET: - return getCustomerAsync(ctx.getAssetService().findAssetByIdAsync(ctx.getTenantId(), (AssetId) original)); + return toCustomerIdAsync(ctx, ctx.getAssetService().findAssetByIdAsync(ctx.getTenantId(), (AssetId) originator)); case DEVICE: - return getCustomerAsync(Futures.immediateFuture(ctx.getDeviceService().findDeviceById(ctx.getTenantId(), (DeviceId) original))); + return toCustomerIdAsync(ctx, Futures.immediateFuture(ctx.getDeviceService().findDeviceById(ctx.getTenantId(), (DeviceId) originator))); default: - return Futures.immediateFailedFuture(new TbNodeException("Unexpected original EntityType " + original.getEntityType())); + return Futures.immediateFailedFuture(new TbNodeException("Unexpected originator EntityType: " + originator.getEntityType())); } } - private static ListenableFuture getCustomerAsync(ListenableFuture future) { - return Futures.transformAsync(future, in -> in != null ? Futures.immediateFuture(in.getCustomerId()) - : Futures.immediateFuture(null), MoreExecutors.directExecutor()); + private static ListenableFuture toCustomerIdAsync(TbContext ctx, ListenableFuture future) { + return Futures.transform(future, in -> in != null ? in.getCustomerId() : null, ctx.getDbCallbackExecutor()); } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesFieldsAsyncLoader.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesFieldsAsyncLoader.java index 51d83b3bfb..6a287583b0 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesFieldsAsyncLoader.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesFieldsAsyncLoader.java @@ -17,7 +17,6 @@ package org.thingsboard.rule.engine.util; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.server.common.data.BaseData; @@ -30,47 +29,53 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.id.UserId; +import java.util.NoSuchElementException; import java.util.function.Function; public class EntitiesFieldsAsyncLoader { - public static ListenableFuture findAsync(TbContext ctx, EntityId original) { - switch (original.getEntityType()) { // TODO: use EntityServiceRegistry + public static ListenableFuture findAsync(TbContext ctx, EntityId originatorId) { + switch (originatorId.getEntityType()) { // TODO: use EntityServiceRegistry case TENANT: - return getAsync(ctx.getTenantService().findTenantByIdAsync(ctx.getTenantId(), (TenantId) original), - EntityFieldsData::new); + return toEntityFieldsDataAsync(ctx.getTenantService().findTenantByIdAsync(ctx.getTenantId(), (TenantId) originatorId), + EntityFieldsData::new, ctx); case CUSTOMER: - return getAsync(ctx.getCustomerService().findCustomerByIdAsync(ctx.getTenantId(), (CustomerId) original), - EntityFieldsData::new); + return toEntityFieldsDataAsync(ctx.getCustomerService().findCustomerByIdAsync(ctx.getTenantId(), (CustomerId) originatorId), + EntityFieldsData::new, ctx); case USER: - return getAsync(ctx.getUserService().findUserByIdAsync(ctx.getTenantId(), (UserId) original), - EntityFieldsData::new); + return toEntityFieldsDataAsync(ctx.getUserService().findUserByIdAsync(ctx.getTenantId(), (UserId) originatorId), + EntityFieldsData::new, ctx); case ASSET: - return getAsync(ctx.getAssetService().findAssetByIdAsync(ctx.getTenantId(), (AssetId) original), - EntityFieldsData::new); + return toEntityFieldsDataAsync(ctx.getAssetService().findAssetByIdAsync(ctx.getTenantId(), (AssetId) originatorId), + EntityFieldsData::new, ctx); case DEVICE: - return getAsync(Futures.immediateFuture(ctx.getDeviceService().findDeviceById(ctx.getTenantId(), (DeviceId) original)), - EntityFieldsData::new); + return toEntityFieldsDataAsync(Futures.immediateFuture(ctx.getDeviceService().findDeviceById(ctx.getTenantId(), (DeviceId) originatorId)), + EntityFieldsData::new, ctx); case ALARM: - return getAsync(ctx.getAlarmService().findAlarmByIdAsync(ctx.getTenantId(), (AlarmId) original), - EntityFieldsData::new); + return toEntityFieldsDataAsync(ctx.getAlarmService().findAlarmByIdAsync(ctx.getTenantId(), (AlarmId) originatorId), + EntityFieldsData::new, ctx); case RULE_CHAIN: - return getAsync(ctx.getRuleChainService().findRuleChainByIdAsync(ctx.getTenantId(), (RuleChainId) original), - EntityFieldsData::new); + return toEntityFieldsDataAsync(ctx.getRuleChainService().findRuleChainByIdAsync(ctx.getTenantId(), (RuleChainId) originatorId), + EntityFieldsData::new, ctx); case ENTITY_VIEW: - return getAsync(ctx.getEntityViewService().findEntityViewByIdAsync(ctx.getTenantId(), (EntityViewId) original), - EntityFieldsData::new); + return toEntityFieldsDataAsync(ctx.getEntityViewService().findEntityViewByIdAsync(ctx.getTenantId(), (EntityViewId) originatorId), + EntityFieldsData::new, ctx); default: - return Futures.immediateFailedFuture(new TbNodeException("Unexpected original EntityType " + original.getEntityType())); + return Futures.immediateFailedFuture(new TbNodeException("Unexpected originator EntityType: " + originatorId.getEntityType())); } } - private static ListenableFuture getAsync( - ListenableFuture future, Function converter) { + private static > ListenableFuture toEntityFieldsDataAsync( + ListenableFuture future, + Function converter, + TbContext ctx + ) { return Futures.transformAsync(future, in -> in != null ? Futures.immediateFuture(converter.apply(in)) - : Futures.immediateFailedFuture(new RuntimeException("Entity not found!")), MoreExecutors.directExecutor()); + : Futures.immediateFailedFuture(new NoSuchElementException("Entity not found!")), ctx.getDbCallbackExecutor()); } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesRelatedDeviceIdAsyncLoader.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesRelatedDeviceIdAsyncLoader.java index 0ea43026d1..b937f01b24 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesRelatedDeviceIdAsyncLoader.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesRelatedDeviceIdAsyncLoader.java @@ -17,39 +17,42 @@ package org.thingsboard.rule.engine.util; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; import org.apache.commons.collections.CollectionUtils; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.data.DeviceRelationsQuery; -import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.device.DeviceSearchQuery; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.relation.RelationsSearchParameters; -import org.thingsboard.server.dao.device.DeviceService; - -import java.util.List; public class EntitiesRelatedDeviceIdAsyncLoader { - public static ListenableFuture findDeviceAsync(TbContext ctx, EntityId originator, - DeviceRelationsQuery deviceRelationsQuery) { - DeviceService deviceService = ctx.getDeviceService(); - DeviceSearchQuery query = buildQuery(originator, deviceRelationsQuery); - - ListenableFuture> asyncDevices = deviceService.findDevicesByQuery(ctx.getTenantId(), query); - - return Futures.transformAsync(asyncDevices, d -> CollectionUtils.isNotEmpty(d) ? Futures.immediateFuture(d.get(0).getId()) - : Futures.immediateFuture(null), MoreExecutors.directExecutor()); + public static ListenableFuture findDeviceAsync( + TbContext ctx, + EntityId originator, + DeviceRelationsQuery deviceRelationsQuery + ) { + var deviceService = ctx.getDeviceService(); + var query = buildQuery(originator, deviceRelationsQuery); + var devicesListFuture = deviceService.findDevicesByQuery(ctx.getTenantId(), query); + return Futures.transformAsync(devicesListFuture, + deviceList -> CollectionUtils.isNotEmpty(deviceList) ? + Futures.immediateFuture(deviceList.get(0).getId()) + : Futures.immediateFuture(null), ctx.getDbCallbackExecutor()); } private static DeviceSearchQuery buildQuery(EntityId originator, DeviceRelationsQuery deviceRelationsQuery) { - DeviceSearchQuery query = new DeviceSearchQuery(); - RelationsSearchParameters parameters = new RelationsSearchParameters(originator, - deviceRelationsQuery.getDirection(), deviceRelationsQuery.getMaxLevel(), deviceRelationsQuery.isFetchLastLevelOnly()); + var query = new DeviceSearchQuery(); + var parameters = new RelationsSearchParameters( + originator, + deviceRelationsQuery.getDirection(), + deviceRelationsQuery.getMaxLevel(), + deviceRelationsQuery.isFetchLastLevelOnly() + ); query.setParameters(parameters); query.setRelationType(deviceRelationsQuery.getRelationType()); query.setDeviceTypes(deviceRelationsQuery.getDeviceTypes()); return query; } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesRelatedEntityIdAsyncLoader.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesRelatedEntityIdAsyncLoader.java index c80e709072..d0caac8876 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesRelatedEntityIdAsyncLoader.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesRelatedEntityIdAsyncLoader.java @@ -17,42 +17,49 @@ package org.thingsboard.rule.engine.util; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; import org.apache.commons.collections.CollectionUtils; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.data.RelationsQuery; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelationsQuery; import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.RelationsSearchParameters; -import org.thingsboard.server.dao.relation.RelationService; - -import java.util.List; public class EntitiesRelatedEntityIdAsyncLoader { - public static ListenableFuture findEntityAsync(TbContext ctx, EntityId originator, - RelationsQuery relationsQuery) { - RelationService relationService = ctx.getRelationService(); - EntityRelationsQuery query = buildQuery(originator, relationsQuery); - ListenableFuture> asyncRelation = relationService.findByQuery(ctx.getTenantId(), query); + public static ListenableFuture findEntityAsync( + TbContext ctx, + EntityId originator, + RelationsQuery relationsQuery + ) { + var relationService = ctx.getRelationService(); + var query = buildQuery(originator, relationsQuery); + var relationListFuture = relationService.findByQuery(ctx.getTenantId(), query); if (relationsQuery.getDirection() == EntitySearchDirection.FROM) { - return Futures.transformAsync(asyncRelation, r -> CollectionUtils.isNotEmpty(r) ? Futures.immediateFuture(r.get(0).getTo()) - : Futures.immediateFuture(null), MoreExecutors.directExecutor()); + return Futures.transformAsync(relationListFuture, + relationList -> CollectionUtils.isNotEmpty(relationList) ? + Futures.immediateFuture(relationList.get(0).getTo()) + : Futures.immediateFuture(null), ctx.getDbCallbackExecutor()); } else if (relationsQuery.getDirection() == EntitySearchDirection.TO) { - return Futures.transformAsync(asyncRelation, r -> CollectionUtils.isNotEmpty(r) ? Futures.immediateFuture(r.get(0).getFrom()) - : Futures.immediateFuture(null), MoreExecutors.directExecutor()); + return Futures.transformAsync(relationListFuture, + relationList -> CollectionUtils.isNotEmpty(relationList) ? + Futures.immediateFuture(relationList.get(0).getFrom()) + : Futures.immediateFuture(null), ctx.getDbCallbackExecutor()); } return Futures.immediateFailedFuture(new IllegalStateException("Unknown direction")); } private static EntityRelationsQuery buildQuery(EntityId originator, RelationsQuery relationsQuery) { - EntityRelationsQuery query = new EntityRelationsQuery(); - RelationsSearchParameters parameters = new RelationsSearchParameters(originator, - relationsQuery.getDirection(), relationsQuery.getMaxLevel(), relationsQuery.isFetchLastLevelOnly()); + var query = new EntityRelationsQuery(); + var parameters = new RelationsSearchParameters( + originator, + relationsQuery.getDirection(), + relationsQuery.getMaxLevel(), + relationsQuery.isFetchLastLevelOnly() + ); query.setParameters(parameters); query.setFilters(relationsQuery.getFilters()); return query; } + } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntityContainer.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntityContainer.java index 516b28846a..410f6b1ed9 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntityContainer.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntityContainer.java @@ -25,4 +25,4 @@ public class EntityContainer { private EntityId entityId; private EntityType entityType; -} \ No newline at end of file +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntityDetails.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntityDetails.java deleted file mode 100644 index 1ca00f2e5b..0000000000 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntityDetails.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.rule.engine.util; - -public enum EntityDetails { - - ID, TITLE, COUNTRY, CITY, STATE, ZIP, ADDRESS, ADDRESS2, PHONE, EMAIL, ADDITIONAL_INFO - -} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TbMsgSource.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TbMsgSource.java new file mode 100644 index 0000000000..e73b7976fa --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TbMsgSource.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.util; + +public enum TbMsgSource { + + DATA, + METADATA + +} diff --git a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js index 58f4b4ecdc..c1322f5a9b 100644 --- a/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js +++ b/rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js @@ -1 +1,25 @@ -System.register(["@angular/core","@shared/public-api","@ngrx/store","@angular/forms","@angular/common","@angular/material/checkbox","@angular/material/input","@angular/material/form-field","@angular/flex-layout/flex","@ngx-translate/core","@angular/platform-browser","@angular/material/select","@angular/material/core","@shared/components/queue/queue-autocomplete.component","@core/public-api","@shared/components/js-func.component","@angular/material/button","@shared/components/script-lang.component","@angular/cdk/keycodes","@angular/material/icon","@angular/material/chips","@shared/components/entity/entity-type-select.component","@shared/components/entity/entity-select.component","@angular/cdk/coercion","@shared/components/tb-error.component","@angular/material/tooltip","@angular/flex-layout/extended","@angular/material/list","@angular/cdk/drag-drop","rxjs/operators","@angular/material/autocomplete","@shared/pipe/highlight.pipe","rxjs","@angular/material/expansion","@home/components/public-api","@shared/components/entity/entity-subtype-list.component","@shared/components/relation/relation-type-autocomplete.component","@home/components/relation/relation-filters.component","@shared/components/file-input.component","@shared/components/button/toggle-password.component","@angular/material/radio","@angular/material/slide-toggle","@shared/components/entity/entity-list.component","@shared/components/notification/template-autocomplete.component","@shared/components/tb-checkbox.component","@home/components/sms/sms-provider-configuration.component","@shared/components/slack-conversation-autocomplete.component","@shared/components/entity/entity-autocomplete.component","@shared/components/entity/entity-type-list.component"],(function(e){"use strict";var t,r,n,a,o,i,l,s,m,u,p,d,c,f,g,y,x,b,h,C,F,v,L,k,I,T,N,q,S,M,A,G,E,D,V,P,R,w,O,H,K,B,U,z,j,_,$,J,Q,Y,W,X,Z,ee,te,re,ne,ae,oe,ie,le,se,me,ue,pe,de,ce,fe,ge,ye,xe,be,he,Ce,Fe,ve,Le,ke,Ie,Te,Ne,qe,Se,Me,Ae,Ge,Ee,De,Ve,Pe,Re,we,Oe,He,Ke;return{setters:[function(e){t=e,r=e.Component,n=e.Pipe,a=e.ViewChild,o=e.forwardRef,i=e.Input,l=e.NgModule},function(e){s=e.RuleNodeConfigurationComponent,m=e.AttributeScope,u=e.telemetryTypeTranslations,p=e.ServiceType,d=e.ScriptLanguage,c=e.AlarmSeverity,f=e.alarmSeverityTranslations,g=e.EntitySearchDirection,y=e.entitySearchDirectionTranslations,x=e.EntityType,b=e.PageComponent,h=e.MessageType,C=e.messageTypeNames,F=e,v=e.SharedModule,L=e.AggregationType,k=e.aggregationTranslations,I=e.NotificationType,T=e.SlackChanelType,N=e.SlackChanelTypesTranslateMap,q=e.alarmStatusTranslations,S=e.AlarmStatus},function(e){M=e},function(e){A=e,G=e.Validators,E=e.NgControl,D=e.NG_VALUE_ACCESSOR,V=e.NG_VALIDATORS,P=e.UntypedFormControl},function(e){R=e,w=e.CommonModule},function(e){O=e},function(e){H=e},function(e){K=e},function(e){B=e},function(e){U=e},function(e){z=e},function(e){j=e},function(e){_=e},function(e){$=e},function(e){J=e.getCurrentAuthState,Q=e,Y=e.isDefinedAndNotNull,W=e.isObject,X=e.isUndefinedOrNull,Z=e.isNotEmptyStr},function(e){ee=e},function(e){te=e},function(e){re=e},function(e){ne=e.ENTER,ae=e.COMMA,oe=e.SEMICOLON},function(e){ie=e},function(e){le=e},function(e){se=e},function(e){me=e},function(e){ue=e.coerceBooleanProperty},function(e){pe=e},function(e){de=e},function(e){ce=e},function(e){fe=e},function(e){ge=e},function(e){ye=e.tap,xe=e.map,be=e.mergeMap,he=e.takeUntil,Ce=e.startWith,Fe=e.share},function(e){ve=e},function(e){Le=e},function(e){ke=e.of,Ie=e.Subject},function(e){Te=e},function(e){Ne=e.HomeComponentsModule},function(e){qe=e},function(e){Se=e},function(e){Me=e},function(e){Ae=e},function(e){Ge=e},function(e){Ee=e},function(e){De=e},function(e){Ve=e},function(e){Pe=e},function(e){Re=e},function(e){we=e},function(e){Oe=e},function(e){He=e},function(e){Ke=e}],execute:function(){class Be extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.emptyConfigForm}onConfigurationSet(e){this.emptyConfigForm=this.fb.group({})}}e("EmptyConfigComponent",Be),Be.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Be,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Be.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Be,selector:"tb-node-empty-config",usesInheritance:!0,ngImport:t,template:"
",isInline:!0}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Be,decorators:[{type:r,args:[{selector:"tb-node-empty-config",template:"
"}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Ue{constructor(e){this.sanitizer=e}transform(e){return this.sanitizer.bypassSecurityTrustHtml(e)}}e("SafeHtmlPipe",Ue),Ue.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Ue,deps:[{token:z.DomSanitizer}],target:t.ɵɵFactoryTarget.Pipe}),Ue.ɵpipe=t.ɵɵngDeclarePipe({minVersion:"14.0.0",version:"15.2.9",ngImport:t,type:Ue,name:"safeHtml"}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Ue,decorators:[{type:n,args:[{name:"safeHtml"}]}],ctorParameters:function(){return[{type:z.DomSanitizer}]}});class ze extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.assignCustomerConfigForm}onConfigurationSet(e){this.assignCustomerConfigForm=this.fb.group({customerNamePattern:[e?e.customerNamePattern:null,[G.required,G.pattern(/.*\S.*/)]],createCustomerIfNotExists:[!!e&&e.createCustomerIfNotExists,[]],customerCacheExpiration:[e?e.customerCacheExpiration:null,[G.required,G.min(0)]]})}prepareOutputConfig(e){return e.customerNamePattern=e.customerNamePattern.trim(),e}}e("AssignCustomerConfigComponent",ze),ze.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:ze,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),ze.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:ze,selector:"tb-action-node-assign-to-customer-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.create-customer-if-not-exists\' | translate }}\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:ze,decorators:[{type:r,args:[{selector:"tb-action-node-assign-to-customer-config",template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.create-customer-if-not-exists\' | translate }}\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class je extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopeMap=m,this.attributeScopes=Object.keys(m),this.telemetryTypeTranslationsMap=u}configForm(){return this.attributesConfigForm}onConfigurationSet(e){this.attributesConfigForm=this.fb.group({scope:[e?e.scope:null,[G.required]],notifyDevice:[!e||e.notifyDevice,[]],sendAttributesUpdatedNotification:[!!e&&e.sendAttributesUpdatedNotification,[]]}),this.attributesConfigForm.get("scope").valueChanges.subscribe((e=>{e!==m.SHARED_SCOPE&&this.attributesConfigForm.get("notifyDevice").patchValue(!1,{emitEvent:!1}),e===m.CLIENT_SCOPE&&this.attributesConfigForm.get("sendAttributesUpdatedNotification").patchValue(!1,{emitEvent:!1})}))}}e("AttributesConfigComponent",je),je.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:je,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),je.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:je,selector:"tb-action-node-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
tb.rulenode.notify-device-hint
\n
\n
\n \n {{ \'tb.rulenode.send-attributes-updated-notification\' | translate }}\n \n
tb.rulenode.send-attributes-updated-notification-hint
\n
\n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:je,decorators:[{type:r,args:[{selector:"tb-action-node-attributes-config",template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
tb.rulenode.notify-device-hint
\n
\n
\n \n {{ \'tb.rulenode.send-attributes-updated-notification\' | translate }}\n \n
tb.rulenode.send-attributes-updated-notification-hint
\n
\n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class _e extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.serviceType=p.TB_RULE_ENGINE}configForm(){return this.checkPointConfigForm}onConfigurationSet(e){this.checkPointConfigForm=this.fb.group({queueName:[e?e.queueName:null,[G.required]]})}}e("CheckPointConfigComponent",_e),_e.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:_e,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),_e.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:_e,selector:"tb-action-node-check-point-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',dependencies:[{kind:"component",type:$.QueueAutocompleteComponent,selector:"tb-queue-autocomplete",inputs:["labelText","requiredText","autocompleteHint","subscriptSizing","required","queueType","disabled"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:_e,decorators:[{type:r,args:[{selector:"tb-action-node-check-point-config",template:'
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class $e extends s{constructor(e,t,r,n){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=r,this.translate=n,this.tbelEnabled=J(this.store).tbelEnabled,this.scriptLanguage=d}configForm(){return this.clearAlarmConfigForm}onConfigurationSet(e){this.clearAlarmConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:d.JS,[G.required]],alarmDetailsBuildJs:[e?e.alarmDetailsBuildJs:null,[]],alarmDetailsBuildTbel:[e?e.alarmDetailsBuildTbel:null,[]],alarmType:[e?e.alarmType:null,[G.required]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.clearAlarmConfigForm.get("scriptLang").value;t!==d.TBEL||this.tbelEnabled||(t=d.JS,this.clearAlarmConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.clearAlarmConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.clearAlarmConfigForm.get("alarmDetailsBuildJs").setValidators(t===d.JS?[G.required]:[]),this.clearAlarmConfigForm.get("alarmDetailsBuildJs").updateValueAndValidity({emitEvent:e}),this.clearAlarmConfigForm.get("alarmDetailsBuildTbel").setValidators(t===d.TBEL?[G.required]:[]),this.clearAlarmConfigForm.get("alarmDetailsBuildTbel").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=d.JS)),e}testScript(){const e=this.clearAlarmConfigForm.get("scriptLang").value,t=e===d.JS?"alarmDetailsBuildJs":"alarmDetailsBuildTbel",r=e===d.JS?"rulenode/clear_alarm_node_script_fn":"rulenode/tbel/clear_alarm_node_script_fn",n=this.clearAlarmConfigForm.get(t).value;this.nodeScriptTestService.testNodeScript(n,"json",this.translate.instant("tb.rulenode.details"),"Details",["msg","metadata","msgType"],this.ruleNodeId,r,e).subscribe((e=>{e&&this.clearAlarmConfigForm.get(t).setValue(e)}))}onValidate(){this.clearAlarmConfigForm.get("scriptLang").value===d.JS&&this.jsFuncComponent.validateOnSubmit()}}e("ClearAlarmConfigComponent",$e),$e.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:$e,deps:[{token:M.Store},{token:A.UntypedFormBuilder},{token:Q.NodeScriptTestService},{token:U.TranslateService}],target:t.ɵɵFactoryTarget.Component}),$e.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:$e,selector:"tb-action-node-clear-alarm-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n
\n \n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ee.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:te.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:re.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:$e,decorators:[{type:r,args:[{selector:"tb-action-node-clear-alarm-config",template:'
\n \n \n \n \n \n
\n \n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder},{type:Q.NodeScriptTestService},{type:U.TranslateService}]},propDecorators:{jsFuncComponent:[{type:a,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:a,args:["tbelFuncComponent",{static:!1}]}]}});class Je extends s{constructor(e,t,r,n){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=r,this.translate=n,this.alarmSeverities=Object.keys(c),this.alarmSeverityTranslationMap=f,this.separatorKeysCodes=[ne,ae,oe],this.tbelEnabled=J(this.store).tbelEnabled,this.scriptLanguage=d}configForm(){return this.createAlarmConfigForm}onConfigurationSet(e){this.createAlarmConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:d.JS,[G.required]],alarmDetailsBuildJs:[e?e.alarmDetailsBuildJs:null,[]],alarmDetailsBuildTbel:[e?e.alarmDetailsBuildTbel:null,[]],useMessageAlarmData:[!!e&&e.useMessageAlarmData,[]],overwriteAlarmDetails:[!!e&&e.overwriteAlarmDetails,[]],alarmType:[e?e.alarmType:null,[]],severity:[e?e.severity:null,[]],propagate:[!!e&&e.propagate,[]],relationTypes:[e?e.relationTypes:null,[]],propagateToOwner:[!!e&&e.propagateToOwner,[]],propagateToTenant:[!!e&&e.propagateToTenant,[]],dynamicSeverity:!1}),this.createAlarmConfigForm.get("dynamicSeverity").valueChanges.subscribe((e=>{e?this.createAlarmConfigForm.get("severity").patchValue("",{emitEvent:!1}):this.createAlarmConfigForm.get("severity").patchValue(this.alarmSeverities[0],{emitEvent:!1})}))}validatorTriggers(){return["useMessageAlarmData","overwriteAlarmDetails","scriptLang"]}updateValidators(e){const t=this.createAlarmConfigForm.get("useMessageAlarmData").value,r=this.createAlarmConfigForm.get("overwriteAlarmDetails").value;t?(this.createAlarmConfigForm.get("alarmType").setValidators([]),this.createAlarmConfigForm.get("severity").setValidators([])):(this.createAlarmConfigForm.get("alarmType").setValidators([G.required]),this.createAlarmConfigForm.get("severity").setValidators([G.required])),this.createAlarmConfigForm.get("alarmType").updateValueAndValidity({emitEvent:e}),this.createAlarmConfigForm.get("severity").updateValueAndValidity({emitEvent:e});let n=this.createAlarmConfigForm.get("scriptLang").value;n!==d.TBEL||this.tbelEnabled||(n=d.JS,this.createAlarmConfigForm.get("scriptLang").patchValue(n,{emitEvent:!1}),setTimeout((()=>{this.createAlarmConfigForm.updateValueAndValidity({emitEvent:!0})})));const a=!1===t||!0===r;this.createAlarmConfigForm.get("alarmDetailsBuildJs").setValidators(a&&n===d.JS?[G.required]:[]),this.createAlarmConfigForm.get("alarmDetailsBuildTbel").setValidators(a&&n===d.TBEL?[G.required]:[]),this.createAlarmConfigForm.get("alarmDetailsBuildJs").updateValueAndValidity({emitEvent:e}),this.createAlarmConfigForm.get("alarmDetailsBuildTbel").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=d.JS)),e}testScript(){const e=this.createAlarmConfigForm.get("scriptLang").value,t=e===d.JS?"alarmDetailsBuildJs":"alarmDetailsBuildTbel",r=e===d.JS?"rulenode/create_alarm_node_script_fn":"rulenode/tbel/create_alarm_node_script_fn",n=this.createAlarmConfigForm.get(t).value;this.nodeScriptTestService.testNodeScript(n,"json",this.translate.instant("tb.rulenode.details"),"Details",["msg","metadata","msgType"],this.ruleNodeId,r,e).subscribe((e=>{e&&this.createAlarmConfigForm.get(t).setValue(e)}))}removeKey(e,t){const r=this.createAlarmConfigForm.get(t).value,n=r.indexOf(e);n>=0&&(r.splice(n,1),this.createAlarmConfigForm.get(t).setValue(r,{emitEvent:!0}))}addKey(e,t){const r=e.input;let n=e.value;if((n||"").trim()){n=n.trim();let e=this.createAlarmConfigForm.get(t).value;e&&-1!==e.indexOf(n)||(e||(e=[]),e.push(n),this.createAlarmConfigForm.get(t).setValue(e,{emitEvent:!0}))}r&&(r.value="")}onValidate(){const e=this.createAlarmConfigForm.get("useMessageAlarmData").value,t=this.createAlarmConfigForm.get("overwriteAlarmDetails").value;if(!e||t){this.createAlarmConfigForm.get("scriptLang").value===d.JS&&this.jsFuncComponent.validateOnSubmit()}}}e("CreateAlarmConfigComponent",Je),Je.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Je,deps:[{token:M.Store},{token:A.UntypedFormBuilder},{token:Q.NodeScriptTestService},{token:U.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Je.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Je,selector:"tb-action-node-create-alarm-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-message-alarm-data\' | translate }}\n \n \n {{ \'tb.rulenode.overwrite-alarm-details\' | translate }}\n \n
\n \n \n \n \n \n
\n \n
\n
\n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.use-alarm-severity-pattern\' | translate }}\n \n \n tb.rulenode.alarm-severity\n \n \n {{ alarmSeverityTranslationMap.get(severity) | translate }}\n \n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n tb.rulenode.alarm-severity-pattern\n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.propagate\' | translate }}\n \n
\n \n tb.rulenode.relation-types-list\n \n \n {{key}}\n close\n \n \n \n tb.rulenode.relation-types-list-hint\n \n
\n \n {{ \'tb.rulenode.propagate-to-owner\' | translate }}\n \n \n {{ \'tb.rulenode.propagate-to-tenant\' | translate }}\n \n
\n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ee.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:te.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:ie.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:le.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:le.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:le.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:le.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:re.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Je,decorators:[{type:r,args:[{selector:"tb-action-node-create-alarm-config",template:'
\n \n {{ \'tb.rulenode.use-message-alarm-data\' | translate }}\n \n \n {{ \'tb.rulenode.overwrite-alarm-details\' | translate }}\n \n
\n \n \n \n \n \n
\n \n
\n
\n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.use-alarm-severity-pattern\' | translate }}\n \n \n tb.rulenode.alarm-severity\n \n \n {{ alarmSeverityTranslationMap.get(severity) | translate }}\n \n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n tb.rulenode.alarm-severity-pattern\n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.propagate\' | translate }}\n \n
\n \n tb.rulenode.relation-types-list\n \n \n {{key}}\n close\n \n \n \n tb.rulenode.relation-types-list-hint\n \n
\n \n {{ \'tb.rulenode.propagate-to-owner\' | translate }}\n \n \n {{ \'tb.rulenode.propagate-to-tenant\' | translate }}\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder},{type:Q.NodeScriptTestService},{type:U.TranslateService}]},propDecorators:{jsFuncComponent:[{type:a,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:a,args:["tbelFuncComponent",{static:!1}]}]}});class Qe extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(g),this.directionTypeTranslations=y,this.entityType=x}configForm(){return this.createRelationConfigForm}onConfigurationSet(e){this.createRelationConfigForm=this.fb.group({direction:[e?e.direction:null,[G.required]],entityType:[e?e.entityType:null,[G.required]],entityNamePattern:[e?e.entityNamePattern:null,[]],entityTypePattern:[e?e.entityTypePattern:null,[]],relationType:[e?e.relationType:null,[G.required]],createEntityIfNotExists:[!!e&&e.createEntityIfNotExists,[]],removeCurrentRelations:[!!e&&e.removeCurrentRelations,[]],changeOriginatorToRelatedEntity:[!!e&&e.changeOriginatorToRelatedEntity,[]],entityCacheExpiration:[e?e.entityCacheExpiration:null,[G.required,G.min(0)]]})}validatorTriggers(){return["entityType"]}updateValidators(e){const t=this.createRelationConfigForm.get("entityType").value;t?this.createRelationConfigForm.get("entityNamePattern").setValidators([G.required,G.pattern(/.*\S.*/)]):this.createRelationConfigForm.get("entityNamePattern").setValidators([]),!t||t!==x.DEVICE&&t!==x.ASSET?this.createRelationConfigForm.get("entityTypePattern").setValidators([]):this.createRelationConfigForm.get("entityTypePattern").setValidators([G.required,G.pattern(/.*\S.*/)]),this.createRelationConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e}),this.createRelationConfigForm.get("entityTypePattern").updateValueAndValidity({emitEvent:e})}prepareOutputConfig(e){return e.entityNamePattern=e.entityNamePattern?e.entityNamePattern.trim():null,e.entityTypePattern=e.entityTypePattern?e.entityTypePattern.trim():null,e}}e("CreateRelationConfigComponent",Qe),Qe.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Qe,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Qe.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Qe,selector:"tb-action-node-create-relation-config",usesInheritance:!0,ngImport:t,template:'
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.entity-type-pattern\n \n \n {{ \'tb.rulenode.entity-type-pattern-required\' | translate }}\n \n \n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n \n \n
\n \n {{ \'tb.rulenode.create-entity-if-not-exists\' | translate }}\n \n
tb.rulenode.create-entity-if-not-exists-hint
\n
\n \n {{ \'tb.rulenode.remove-current-relations\' | translate }}\n \n
tb.rulenode.remove-current-relations-hint
\n \n {{ \'tb.rulenode.change-originator-to-related-entity\' | translate }}\n \n
tb.rulenode.change-originator-to-related-entity-hint
\n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:se.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Qe,decorators:[{type:r,args:[{selector:"tb-action-node-create-relation-config",template:'
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.entity-type-pattern\n \n \n {{ \'tb.rulenode.entity-type-pattern-required\' | translate }}\n \n \n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n \n \n
\n \n {{ \'tb.rulenode.create-entity-if-not-exists\' | translate }}\n \n
tb.rulenode.create-entity-if-not-exists-hint
\n
\n \n {{ \'tb.rulenode.remove-current-relations\' | translate }}\n \n
tb.rulenode.remove-current-relations-hint
\n \n {{ \'tb.rulenode.change-originator-to-related-entity\' | translate }}\n \n
tb.rulenode.change-originator-to-related-entity-hint
\n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Ye extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(g),this.directionTypeTranslations=y,this.entityType=x}configForm(){return this.deleteRelationConfigForm}onConfigurationSet(e){this.deleteRelationConfigForm=this.fb.group({deleteForSingleEntity:[!!e&&e.deleteForSingleEntity,[]],direction:[e?e.direction:null,[G.required]],entityType:[e?e.entityType:null,[]],entityNamePattern:[e?e.entityNamePattern:null,[]],relationType:[e?e.relationType:null,[G.required]],entityCacheExpiration:[e?e.entityCacheExpiration:null,[G.required,G.min(0)]]})}validatorTriggers(){return["deleteForSingleEntity","entityType"]}updateValidators(e){const t=this.deleteRelationConfigForm.get("deleteForSingleEntity").value,r=this.deleteRelationConfigForm.get("entityType").value;t?this.deleteRelationConfigForm.get("entityType").setValidators([G.required]):this.deleteRelationConfigForm.get("entityType").setValidators([]),t&&r?this.deleteRelationConfigForm.get("entityNamePattern").setValidators([G.required,G.pattern(/.*\S.*/)]):this.deleteRelationConfigForm.get("entityNamePattern").setValidators([]),this.deleteRelationConfigForm.get("entityType").updateValueAndValidity({emitEvent:!1}),this.deleteRelationConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e})}prepareOutputConfig(e){return e.entityNamePattern=e.entityNamePattern?e.entityNamePattern.trim():null,e}}e("DeleteRelationConfigComponent",Ye),Ye.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Ye,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ye.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Ye,selector:"tb-action-node-delete-relation-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.delete-relation-to-specific-entity\' | translate }}\n \n
tb.rulenode.delete-relation-hint
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n \n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:se.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Ye,decorators:[{type:r,args:[{selector:"tb-action-node-delete-relation-config",template:'
\n \n {{ \'tb.rulenode.delete-relation-to-specific-entity\' | translate }}\n \n
tb.rulenode.delete-relation-hint
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n \n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class We extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.deviceProfile}onConfigurationSet(e){this.deviceProfile=this.fb.group({persistAlarmRulesState:[!!e&&e.persistAlarmRulesState,G.required],fetchAlarmRulesStateOnStart:[!!e&&e.fetchAlarmRulesStateOnStart,G.required]})}}e("DeviceProfileConfigComponent",We),We.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:We,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),We.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:We,selector:"tb-device-profile-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.persist-alarm-rules\' | translate }}\n \n \n {{ \'tb.rulenode.fetch-alarm-rules\' | translate }}\n \n
\n',dependencies:[{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:We,decorators:[{type:r,args:[{selector:"tb-device-profile-config",template:'
\n \n {{ \'tb.rulenode.persist-alarm-rules\' | translate }}\n \n \n {{ \'tb.rulenode.fetch-alarm-rules\' | translate }}\n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Xe extends s{constructor(e,t,r,n){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=r,this.translate=n,this.tbelEnabled=J(this.store).tbelEnabled,this.scriptLanguage=d,this.serviceType=p.TB_RULE_ENGINE}configForm(){return this.generatorConfigForm}onConfigurationSet(e){this.generatorConfigForm=this.fb.group({msgCount:[e?e.msgCount:null,[G.required,G.min(0)]],periodInSeconds:[e?e.periodInSeconds:null,[G.required,G.min(1)]],originator:[e?e.originator:null,[]],scriptLang:[e?e.scriptLang:d.JS,[G.required]],jsScript:[e?e.jsScript:null,[]],tbelScript:[e?e.tbelScript:null,[]],queueName:[e?e.queueName:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.generatorConfigForm.get("scriptLang").value;t!==d.TBEL||this.tbelEnabled||(t=d.JS,this.generatorConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.generatorConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.generatorConfigForm.get("jsScript").setValidators(t===d.JS?[G.required]:[]),this.generatorConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.generatorConfigForm.get("tbelScript").setValidators(t===d.TBEL?[G.required]:[]),this.generatorConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=d.JS),e.originatorId&&e.originatorType?e.originator={id:e.originatorId,entityType:e.originatorType}:e.originator=null,delete e.originatorId,delete e.originatorType),e}prepareOutputConfig(e){return e.originator?(e.originatorId=e.originator.id,e.originatorType=e.originator.entityType):(e.originatorId=null,e.originatorType=null),delete e.originator,e}testScript(){const e=this.generatorConfigForm.get("scriptLang").value,t=e===d.JS?"jsScript":"tbelScript",r=e===d.JS?"rulenode/generator_node_script_fn":"rulenode/tbel/generator_node_script_fn",n=this.generatorConfigForm.get(t).value;this.nodeScriptTestService.testNodeScript(n,"generate",this.translate.instant("tb.rulenode.generator"),"Generate",["prevMsg","prevMetadata","prevMsgType"],this.ruleNodeId,r,e).subscribe((e=>{e&&this.generatorConfigForm.get(t).setValue(e)}))}onValidate(){this.generatorConfigForm.get("scriptLang").value===d.JS&&this.jsFuncComponent.validateOnSubmit()}}var Ze;e("GeneratorConfigComponent",Xe),Xe.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Xe,deps:[{token:M.Store},{token:A.UntypedFormBuilder},{token:Q.NodeScriptTestService},{token:U.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Xe.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Xe,selector:"tb-action-node-generator-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.message-count\n \n \n {{ \'tb.rulenode.message-count-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-message-count-message\' | translate }}\n \n \n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-seconds-message\' | translate }}\n \n \n
\n \n \n \n
\n\n \n \n\n \n \n \n \n \n
\n \n
\n
\n',dependencies:[{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:me.EntitySelectComponent,selector:"tb-entity-select",inputs:["allowedEntityTypes","useAliasEntityTypes","required","disabled"]},{kind:"component",type:$.QueueAutocompleteComponent,selector:"tb-queue-autocomplete",inputs:["labelText","requiredText","autocompleteHint","subscriptSizing","required","queueType","disabled"]},{kind:"component",type:ee.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:te.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:re.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Xe,decorators:[{type:r,args:[{selector:"tb-action-node-generator-config",template:'
\n \n tb.rulenode.message-count\n \n \n {{ \'tb.rulenode.message-count-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-message-count-message\' | translate }}\n \n \n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-seconds-message\' | translate }}\n \n \n
\n \n \n \n
\n\n \n \n\n \n \n \n \n \n
\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder},{type:Q.NodeScriptTestService},{type:U.TranslateService}]},propDecorators:{jsFuncComponent:[{type:a,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:a,args:["tbelFuncComponent",{static:!1}]}]}}),function(e){e.CUSTOMER="CUSTOMER",e.TENANT="TENANT",e.RELATED="RELATED",e.ALARM_ORIGINATOR="ALARM_ORIGINATOR",e.ENTITY="ENTITY"}(Ze||(Ze={}));const et=new Map([[Ze.CUSTOMER,"tb.rulenode.originator-customer"],[Ze.TENANT,"tb.rulenode.originator-tenant"],[Ze.RELATED,"tb.rulenode.originator-related"],[Ze.ALARM_ORIGINATOR,"tb.rulenode.originator-alarm-originator"],[Ze.ENTITY,"tb.rulenode.originator-entity"]]);var tt;!function(e){e.CIRCLE="CIRCLE",e.POLYGON="POLYGON"}(tt||(tt={}));const rt=new Map([[tt.CIRCLE,"tb.rulenode.perimeter-circle"],[tt.POLYGON,"tb.rulenode.perimeter-polygon"]]);var nt;!function(e){e.MILLISECONDS="MILLISECONDS",e.SECONDS="SECONDS",e.MINUTES="MINUTES",e.HOURS="HOURS",e.DAYS="DAYS"}(nt||(nt={}));const at=new Map([[nt.MILLISECONDS,"tb.rulenode.time-unit-milliseconds"],[nt.SECONDS,"tb.rulenode.time-unit-seconds"],[nt.MINUTES,"tb.rulenode.time-unit-minutes"],[nt.HOURS,"tb.rulenode.time-unit-hours"],[nt.DAYS,"tb.rulenode.time-unit-days"]]);var ot;!function(e){e.METER="METER",e.KILOMETER="KILOMETER",e.FOOT="FOOT",e.MILE="MILE",e.NAUTICAL_MILE="NAUTICAL_MILE"}(ot||(ot={}));const it=new Map([[ot.METER,"tb.rulenode.range-unit-meter"],[ot.KILOMETER,"tb.rulenode.range-unit-kilometer"],[ot.FOOT,"tb.rulenode.range-unit-foot"],[ot.MILE,"tb.rulenode.range-unit-mile"],[ot.NAUTICAL_MILE,"tb.rulenode.range-unit-nautical-mile"]]);var lt;!function(e){e.ID="ID",e.TITLE="TITLE",e.COUNTRY="COUNTRY",e.STATE="STATE",e.CITY="CITY",e.ZIP="ZIP",e.ADDRESS="ADDRESS",e.ADDRESS2="ADDRESS2",e.PHONE="PHONE",e.EMAIL="EMAIL",e.ADDITIONAL_INFO="ADDITIONAL_INFO"}(lt||(lt={}));const st=new Map([[lt.ID,"tb.rulenode.entity-details-id"],[lt.TITLE,"tb.rulenode.entity-details-title"],[lt.COUNTRY,"tb.rulenode.entity-details-country"],[lt.STATE,"tb.rulenode.entity-details-state"],[lt.CITY,"tb.rulenode.entity-details-city"],[lt.ZIP,"tb.rulenode.entity-details-zip"],[lt.ADDRESS,"tb.rulenode.entity-details-address"],[lt.ADDRESS2,"tb.rulenode.entity-details-address2"],[lt.PHONE,"tb.rulenode.entity-details-phone"],[lt.EMAIL,"tb.rulenode.entity-details-email"],[lt.ADDITIONAL_INFO,"tb.rulenode.entity-details-additional_info"]]);var mt;!function(e){e.FIRST="FIRST",e.LAST="LAST",e.ALL="ALL"}(mt||(mt={}));const ut=new Map([[mt.FIRST,"tb.rulenode.first-message"],[mt.LAST,"tb.rulenode.last-message"],[mt.ALL,"tb.rulenode.all-messages"]]);var pt,dt;!function(e){e.ASC="ASC",e.DESC="DESC"}(pt||(pt={})),function(e){e.STANDARD="STANDARD",e.FIFO="FIFO"}(dt||(dt={}));const ct=new Map([[dt.STANDARD,"tb.rulenode.sqs-queue-standard"],[dt.FIFO,"tb.rulenode.sqs-queue-fifo"]]),ft=["anonymous","basic","cert.PEM"],gt=new Map([["anonymous","tb.rulenode.credentials-anonymous"],["basic","tb.rulenode.credentials-basic"],["cert.PEM","tb.rulenode.credentials-pem"]]),yt=["sas","cert.PEM"],xt=new Map([["sas","tb.rulenode.credentials-sas"],["cert.PEM","tb.rulenode.credentials-pem"]]);var bt;!function(e){e.GET="GET",e.POST="POST",e.PUT="PUT",e.DELETE="DELETE"}(bt||(bt={}));const ht=["US-ASCII","ISO-8859-1","UTF-8","UTF-16BE","UTF-16LE","UTF-16"],Ct=new Map([["US-ASCII","tb.rulenode.charset-us-ascii"],["ISO-8859-1","tb.rulenode.charset-iso-8859-1"],["UTF-8","tb.rulenode.charset-utf-8"],["UTF-16BE","tb.rulenode.charset-utf-16be"],["UTF-16LE","tb.rulenode.charset-utf-16le"],["UTF-16","tb.rulenode.charset-utf-16"]]);var Ft;!function(e){e.CUSTOM="CUSTOM",e.ADD="ADD",e.SUB="SUB",e.MULT="MULT",e.DIV="DIV",e.SIN="SIN",e.SINH="SINH",e.COS="COS",e.COSH="COSH",e.TAN="TAN",e.TANH="TANH",e.ACOS="ACOS",e.ASIN="ASIN",e.ATAN="ATAN",e.ATAN2="ATAN2",e.EXP="EXP",e.EXPM1="EXPM1",e.SQRT="SQRT",e.CBRT="CBRT",e.GET_EXP="GET_EXP",e.HYPOT="HYPOT",e.LOG="LOG",e.LOG10="LOG10",e.LOG1P="LOG1P",e.CEIL="CEIL",e.FLOOR="FLOOR",e.FLOOR_DIV="FLOOR_DIV",e.FLOOR_MOD="FLOOR_MOD",e.ABS="ABS",e.MIN="MIN",e.MAX="MAX",e.POW="POW",e.SIGNUM="SIGNUM",e.RAD="RAD",e.DEG="DEG"}(Ft||(Ft={}));const vt=new Map([[Ft.CUSTOM,{value:Ft.CUSTOM,name:"Custom Function",description:"Use this function to specify complex mathematical expression.",minArgs:1,maxArgs:16}],[Ft.ADD,{value:Ft.ADD,name:"Addition",description:"x + y",minArgs:2,maxArgs:2}],[Ft.SUB,{value:Ft.SUB,name:"Subtraction",description:"x - y",minArgs:2,maxArgs:2}],[Ft.MULT,{value:Ft.MULT,name:"Multiplication",description:"x * y",minArgs:2,maxArgs:2}],[Ft.DIV,{value:Ft.DIV,name:"Division",description:"x / y",minArgs:2,maxArgs:2}],[Ft.SIN,{value:Ft.SIN,name:"Sine",description:"Returns the trigonometric sine of an angle in radians.",minArgs:1,maxArgs:1}],[Ft.SINH,{value:Ft.SINH,name:"Hyperbolic sine",description:"Returns the hyperbolic sine of an argument.",minArgs:1,maxArgs:1}],[Ft.COS,{value:Ft.COS,name:"Cosine",description:"Returns the trigonometric cosine of an angle in radians.",minArgs:1,maxArgs:1}],[Ft.COSH,{value:Ft.COSH,name:"Hyperbolic cosine",description:"Returns the hyperbolic cosine of an argument.",minArgs:1,maxArgs:1}],[Ft.TAN,{value:Ft.TAN,name:"Tangent",description:"Returns the trigonometric tangent of an angle in radians",minArgs:1,maxArgs:1}],[Ft.TANH,{value:Ft.TANH,name:"Hyperbolic tangent",description:"Returns the hyperbolic tangent of an argument",minArgs:1,maxArgs:1}],[Ft.ACOS,{value:Ft.ACOS,name:"Arc cosine",description:"Returns the arc cosine of an argument",minArgs:1,maxArgs:1}],[Ft.ASIN,{value:Ft.ASIN,name:"Arc sine",description:"Returns the arc sine of an argument",minArgs:1,maxArgs:1}],[Ft.ATAN,{value:Ft.ATAN,name:"Arc tangent",description:"Returns the arc tangent of an argument",minArgs:1,maxArgs:1}],[Ft.ATAN2,{value:Ft.ATAN2,name:"2-argument arc tangent",description:"Returns the angle theta from the conversion of rectangular coordinates (x, y) to polar coordinates (r, theta)",minArgs:2,maxArgs:2}],[Ft.EXP,{value:Ft.EXP,name:"Exponential",description:"Returns Euler's number e raised to the power of an argument",minArgs:1,maxArgs:1}],[Ft.EXPM1,{value:Ft.EXPM1,name:"Exponential minus one",description:"Returns Euler's number e raised to the power of an argument minus one",minArgs:1,maxArgs:1}],[Ft.SQRT,{value:Ft.SQRT,name:"Square",description:"Returns the correctly rounded positive square root of an argument",minArgs:1,maxArgs:1}],[Ft.CBRT,{value:Ft.CBRT,name:"Cube root",description:"Returns the cube root of an argument",minArgs:1,maxArgs:1}],[Ft.GET_EXP,{value:Ft.GET_EXP,name:"Get exponent",description:"Returns the unbiased exponent used in the representation of an argument",minArgs:1,maxArgs:1}],[Ft.HYPOT,{value:Ft.HYPOT,name:"Square root",description:"Returns the square root of the squares of the arguments",minArgs:2,maxArgs:2}],[Ft.LOG,{value:Ft.LOG,name:"Logarithm",description:"Returns the natural logarithm of an argument",minArgs:1,maxArgs:1}],[Ft.LOG10,{value:Ft.LOG10,name:"Base 10 logarithm",description:"Returns the base 10 logarithm of an argument",minArgs:1,maxArgs:1}],[Ft.LOG1P,{value:Ft.LOG1P,name:"Logarithm of the sum",description:"Returns the natural logarithm of the sum of an argument",minArgs:1,maxArgs:1}],[Ft.CEIL,{value:Ft.CEIL,name:"Ceiling",description:"Returns the smallest (closest to negative infinity) of an argument",minArgs:1,maxArgs:1}],[Ft.FLOOR,{value:Ft.FLOOR,name:"Floor",description:"Returns the largest (closest to positive infinity) of an argument",minArgs:1,maxArgs:1}],[Ft.FLOOR_DIV,{value:Ft.FLOOR_DIV,name:"Floor division",description:"Returns the largest (closest to positive infinity) of the arguments",minArgs:2,maxArgs:2}],[Ft.FLOOR_MOD,{value:Ft.FLOOR_MOD,name:"Floor modulus",description:"Returns the floor modulus of the arguments",minArgs:2,maxArgs:2}],[Ft.ABS,{value:Ft.ABS,name:"Absolute",description:"Returns the absolute value of an argument",minArgs:1,maxArgs:1}],[Ft.MIN,{value:Ft.MIN,name:"Min",description:"Returns the smaller of the arguments",minArgs:2,maxArgs:2}],[Ft.MAX,{value:Ft.MAX,name:"Max",description:"Returns the greater of the arguments",minArgs:2,maxArgs:2}],[Ft.POW,{value:Ft.POW,name:"Raise to a power",description:"Returns the value of the first argument raised to the power of the second argument",minArgs:2,maxArgs:2}],[Ft.SIGNUM,{value:Ft.SIGNUM,name:"Sign of a real number",description:"Returns the signum function of the argument",minArgs:1,maxArgs:1}],[Ft.RAD,{value:Ft.RAD,name:"Radian",description:"Converts an angle measured in degrees to an approximately equivalent angle measured in radians",minArgs:1,maxArgs:1}],[Ft.DEG,{value:Ft.DEG,name:"Degrees",description:"Converts an angle measured in radians to an approximately equivalent angle measured in degrees.",minArgs:1,maxArgs:1}]]);var Lt,kt;!function(e){e.ATTRIBUTE="ATTRIBUTE",e.TIME_SERIES="TIME_SERIES",e.CONSTANT="CONSTANT",e.MESSAGE_BODY="MESSAGE_BODY",e.MESSAGE_METADATA="MESSAGE_METADATA"}(Lt||(Lt={})),function(e){e.ATTRIBUTE="ATTRIBUTE",e.TIME_SERIES="TIME_SERIES",e.MESSAGE_BODY="MESSAGE_BODY",e.MESSAGE_METADATA="MESSAGE_METADATA"}(kt||(kt={}));const It=new Map([[Lt.ATTRIBUTE,"tb.rulenode.attribute-type"],[Lt.TIME_SERIES,"tb.rulenode.time-series-type"],[Lt.CONSTANT,"tb.rulenode.constant-type"],[Lt.MESSAGE_BODY,"tb.rulenode.message-body-type"],[Lt.MESSAGE_METADATA,"tb.rulenode.message-metadata-type"]]),Tt=["x","y","z","a","b","c","d","k","l","m","n","o","p","r","s","t"];var Nt,qt;!function(e){e.SHARED_SCOPE="SHARED_SCOPE",e.SERVER_SCOPE="SERVER_SCOPE",e.CLIENT_SCOPE="CLIENT_SCOPE"}(Nt||(Nt={})),function(e){e.SHARED_SCOPE="SHARED_SCOPE",e.SERVER_SCOPE="SERVER_SCOPE"}(qt||(qt={}));const St=new Map([[Nt.SHARED_SCOPE,"tb.rulenode.shared-scope"],[Nt.SERVER_SCOPE,"tb.rulenode.server-scope"],[Nt.CLIENT_SCOPE,"tb.rulenode.client-scope"]]);class Mt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.perimeterType=tt,this.perimeterTypes=Object.keys(tt),this.perimeterTypeTranslationMap=rt,this.rangeUnits=Object.keys(ot),this.rangeUnitTranslationMap=it,this.timeUnits=Object.keys(nt),this.timeUnitsTranslationMap=at}configForm(){return this.geoActionConfigForm}onConfigurationSet(e){this.geoActionConfigForm=this.fb.group({latitudeKeyName:[e?e.latitudeKeyName:null,[G.required]],longitudeKeyName:[e?e.longitudeKeyName:null,[G.required]],perimeterType:[e?e.perimeterType:null,[G.required]],fetchPerimeterInfoFromMessageMetadata:[!!e&&e.fetchPerimeterInfoFromMessageMetadata,[]],perimeterKeyName:[e?e.perimeterKeyName:null,[]],centerLatitude:[e?e.centerLatitude:null,[]],centerLongitude:[e?e.centerLatitude:null,[]],range:[e?e.range:null,[]],rangeUnit:[e?e.rangeUnit:null,[]],polygonsDefinition:[e?e.polygonsDefinition:null,[]],minInsideDuration:[e?e.minInsideDuration:null,[G.required,G.min(1),G.max(2147483647)]],minInsideDurationTimeUnit:[e?e.minInsideDurationTimeUnit:null,[G.required]],minOutsideDuration:[e?e.minOutsideDuration:null,[G.required,G.min(1),G.max(2147483647)]],minOutsideDurationTimeUnit:[e?e.minOutsideDurationTimeUnit:null,[G.required]]})}validatorTriggers(){return["fetchPerimeterInfoFromMessageMetadata","perimeterType"]}updateValidators(e){const t=this.geoActionConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value,r=this.geoActionConfigForm.get("perimeterType").value;t?this.geoActionConfigForm.get("perimeterKeyName").setValidators([G.required]):this.geoActionConfigForm.get("perimeterKeyName").setValidators([]),t||r!==tt.CIRCLE?(this.geoActionConfigForm.get("centerLatitude").setValidators([]),this.geoActionConfigForm.get("centerLongitude").setValidators([]),this.geoActionConfigForm.get("range").setValidators([]),this.geoActionConfigForm.get("rangeUnit").setValidators([])):(this.geoActionConfigForm.get("centerLatitude").setValidators([G.required,G.min(-90),G.max(90)]),this.geoActionConfigForm.get("centerLongitude").setValidators([G.required,G.min(-180),G.max(180)]),this.geoActionConfigForm.get("range").setValidators([G.required,G.min(0)]),this.geoActionConfigForm.get("rangeUnit").setValidators([G.required])),t||r!==tt.POLYGON?this.geoActionConfigForm.get("polygonsDefinition").setValidators([]):this.geoActionConfigForm.get("polygonsDefinition").setValidators([G.required]),this.geoActionConfigForm.get("perimeterKeyName").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("centerLatitude").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("centerLongitude").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("range").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("rangeUnit").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("polygonsDefinition").updateValueAndValidity({emitEvent:e})}}e("GpsGeoActionConfigComponent",Mt),Mt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Mt,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Mt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Mt,selector:"tb-action-node-gps-geofencing-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.latitude-key-name\n \n \n {{ \'tb.rulenode.latitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.longitude-key-name\n \n \n {{ \'tb.rulenode.longitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.perimeter-type\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n \n {{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}\n \n \n tb.rulenode.perimeter-key-name\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.circle-center-latitude\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n tb.rulenode.circle-center-longitude\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.range\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n tb.rulenode.range-units\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n
\n
\n
\n \n tb.rulenode.polygon-definition\n \n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.min-inside-duration\n \n \n {{ \'tb.rulenode.min-inside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-inside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n tb.rulenode.min-outside-duration\n \n \n {{ \'tb.rulenode.min-outside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-outside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Mt,decorators:[{type:r,args:[{selector:"tb-action-node-gps-geofencing-config",template:'
\n \n tb.rulenode.latitude-key-name\n \n \n {{ \'tb.rulenode.latitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.longitude-key-name\n \n \n {{ \'tb.rulenode.longitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.perimeter-type\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n \n {{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}\n \n \n tb.rulenode.perimeter-key-name\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.circle-center-latitude\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n tb.rulenode.circle-center-longitude\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.range\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n tb.rulenode.range-units\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n
\n
\n
\n \n tb.rulenode.polygon-definition\n \n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.min-inside-duration\n \n \n {{ \'tb.rulenode.min-inside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-inside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n tb.rulenode.min-outside-duration\n \n \n {{ \'tb.rulenode.min-outside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-outside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class At extends s{constructor(e,t,r,n){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=r,this.translate=n,this.tbelEnabled=J(this.store).tbelEnabled,this.scriptLanguage=d}configForm(){return this.logConfigForm}onConfigurationSet(e){this.logConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:d.JS,[G.required]],jsScript:[e?e.jsScript:null,[]],tbelScript:[e?e.tbelScript:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.logConfigForm.get("scriptLang").value;t!==d.TBEL||this.tbelEnabled||(t=d.JS,this.logConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.logConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.logConfigForm.get("jsScript").setValidators(t===d.JS?[G.required]:[]),this.logConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.logConfigForm.get("tbelScript").setValidators(t===d.TBEL?[G.required]:[]),this.logConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=d.JS)),e}testScript(){const e=this.logConfigForm.get("scriptLang").value,t=e===d.JS?"jsScript":"tbelScript",r=e===d.JS?"rulenode/log_node_script_fn":"rulenode/tbel/log_node_script_fn",n=this.logConfigForm.get(t).value;this.nodeScriptTestService.testNodeScript(n,"string",this.translate.instant("tb.rulenode.to-string"),"ToString",["msg","metadata","msgType"],this.ruleNodeId,r,e).subscribe((e=>{e&&this.logConfigForm.get(t).setValue(e)}))}onValidate(){this.logConfigForm.get("scriptLang").value===d.JS&&this.jsFuncComponent.validateOnSubmit()}}e("LogConfigComponent",At),At.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:At,deps:[{token:M.Store},{token:A.UntypedFormBuilder},{token:Q.NodeScriptTestService},{token:U.TranslateService}],target:t.ɵɵFactoryTarget.Component}),At.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:At,selector:"tb-action-node-log-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n
\n \n
\n
\n',dependencies:[{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ee.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:te.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:re.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:At,decorators:[{type:r,args:[{selector:"tb-action-node-log-config",template:'
\n \n \n \n \n \n
\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder},{type:Q.NodeScriptTestService},{type:U.TranslateService}]},propDecorators:{jsFuncComponent:[{type:a,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:a,args:["tbelFuncComponent",{static:!1}]}]}});class Gt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.msgCountConfigForm}onConfigurationSet(e){this.msgCountConfigForm=this.fb.group({interval:[e?e.interval:null,[G.required,G.min(1)]],telemetryPrefix:[e?e.telemetryPrefix:null,[G.required]]})}}e("MsgCountConfigComponent",Gt),Gt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Gt,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Gt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Gt,selector:"tb-action-node-msg-count-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.interval-seconds\n \n \n {{ \'tb.rulenode.interval-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-interval-seconds-message\' | translate }}\n \n \n \n tb.rulenode.output-timeseries-key-prefix\n \n \n {{ \'tb.rulenode.output-timeseries-key-prefix-required\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Gt,decorators:[{type:r,args:[{selector:"tb-action-node-msg-count-config",template:'
\n \n tb.rulenode.interval-seconds\n \n \n {{ \'tb.rulenode.interval-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-interval-seconds-message\' | translate }}\n \n \n \n tb.rulenode.output-timeseries-key-prefix\n \n \n {{ \'tb.rulenode.output-timeseries-key-prefix-required\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Et extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.msgDelayConfigForm}onConfigurationSet(e){this.msgDelayConfigForm=this.fb.group({useMetadataPeriodInSecondsPatterns:[!!e&&e.useMetadataPeriodInSecondsPatterns,[]],periodInSeconds:[e?e.periodInSeconds:null,[]],periodInSecondsPattern:[e?e.periodInSecondsPattern:null,[]],maxPendingMsgs:[e?e.maxPendingMsgs:null,[G.required,G.min(1),G.max(1e5)]]})}validatorTriggers(){return["useMetadataPeriodInSecondsPatterns"]}updateValidators(e){this.msgDelayConfigForm.get("useMetadataPeriodInSecondsPatterns").value?(this.msgDelayConfigForm.get("periodInSecondsPattern").setValidators([G.required]),this.msgDelayConfigForm.get("periodInSeconds").setValidators([])):(this.msgDelayConfigForm.get("periodInSecondsPattern").setValidators([]),this.msgDelayConfigForm.get("periodInSeconds").setValidators([G.required,G.min(0)])),this.msgDelayConfigForm.get("periodInSecondsPattern").updateValueAndValidity({emitEvent:e}),this.msgDelayConfigForm.get("periodInSeconds").updateValueAndValidity({emitEvent:e})}}e("MsgDelayConfigComponent",Et),Et.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Et,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Et.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Et,selector:"tb-action-node-msg-delay-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}\n \n
tb.rulenode.use-metadata-period-in-seconds-patterns-hint
\n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-0-seconds-message\' | translate }}\n \n \n \n \n tb.rulenode.period-in-seconds-pattern\n \n \n {{ \'tb.rulenode.period-in-seconds-pattern-required\' | translate }}\n \n \n \n \n \n tb.rulenode.max-pending-messages\n \n \n {{ \'tb.rulenode.max-pending-messages-required\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Et,decorators:[{type:r,args:[{selector:"tb-action-node-msg-delay-config",template:'
\n \n {{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}\n \n
tb.rulenode.use-metadata-period-in-seconds-patterns-hint
\n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-0-seconds-message\' | translate }}\n \n \n \n \n tb.rulenode.period-in-seconds-pattern\n \n \n {{ \'tb.rulenode.period-in-seconds-pattern-required\' | translate }}\n \n \n \n \n \n tb.rulenode.max-pending-messages\n \n \n {{ \'tb.rulenode.max-pending-messages-required\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Dt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopes=Object.keys(m),this.telemetryTypeTranslationsMap=u}configForm(){return this.pushToCloudConfigForm}onConfigurationSet(e){this.pushToCloudConfigForm=this.fb.group({scope:[e?e.scope:null,[G.required]]})}}e("PushToCloudConfigComponent",Dt),Dt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Dt,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Dt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Dt,selector:"tb-action-node-push-to-cloud-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Dt,decorators:[{type:r,args:[{selector:"tb-action-node-push-to-cloud-config",template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Vt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopes=Object.keys(m),this.telemetryTypeTranslationsMap=u}configForm(){return this.pushToEdgeConfigForm}onConfigurationSet(e){this.pushToEdgeConfigForm=this.fb.group({scope:[e?e.scope:null,[G.required]]})}}e("PushToEdgeConfigComponent",Vt),Vt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Vt,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Vt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Vt,selector:"tb-action-node-push-to-edge-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Vt,decorators:[{type:r,args:[{selector:"tb-action-node-push-to-edge-config",template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Pt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.rpcReplyConfigForm}onConfigurationSet(e){this.rpcReplyConfigForm=this.fb.group({requestIdMetaDataAttribute:[e?e.requestIdMetaDataAttribute:null,[]]})}}e("RpcReplyConfigComponent",Pt),Pt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Pt,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Pt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Pt,selector:"tb-action-node-rpc-reply-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.request-id-metadata-attribute\n \n \n
\n',dependencies:[{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Pt,decorators:[{type:r,args:[{selector:"tb-action-node-rpc-reply-config",template:'
\n \n tb.rulenode.request-id-metadata-attribute\n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Rt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.rpcRequestConfigForm}onConfigurationSet(e){this.rpcRequestConfigForm=this.fb.group({timeoutInSeconds:[e?e.timeoutInSeconds:null,[G.required,G.min(0)]]})}}e("RpcRequestConfigComponent",Rt),Rt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Rt,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Rt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Rt,selector:"tb-action-node-rpc-request-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.timeout-sec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-message\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Rt,decorators:[{type:r,args:[{selector:"tb-action-node-rpc-request-config",template:'
\n \n tb.rulenode.timeout-sec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-message\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class wt extends b{get required(){return this.requiredValue}set required(e){this.requiredValue=ue(e)}constructor(e,t,r,n){super(e),this.store=e,this.translate=t,this.injector=r,this.fb=n,this.propagateChange=null,this.valueChangeSubscription=null}ngOnInit(){this.ngControl=this.injector.get(E),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.kvListFormGroup=this.fb.group({}),this.kvListFormGroup.addControl("keyVals",this.fb.array([]))}keyValsFormArray(){return this.kvListFormGroup.get("keyVals")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.kvListFormGroup.disable({emitEvent:!1}):this.kvListFormGroup.enable({emitEvent:!1})}writeValue(e){this.valueChangeSubscription&&this.valueChangeSubscription.unsubscribe();const t=[];if(e)for(const r of Object.keys(e))Object.prototype.hasOwnProperty.call(e,r)&&t.push(this.fb.group({key:[r,[G.required]],value:[e[r],[G.required]]}));this.kvListFormGroup.setControl("keyVals",this.fb.array(t)),this.valueChangeSubscription=this.kvListFormGroup.valueChanges.subscribe((()=>{this.updateModel()}))}removeKeyVal(e){this.kvListFormGroup.get("keyVals").removeAt(e)}addKeyVal(){this.kvListFormGroup.get("keyVals").push(this.fb.group({key:["",[G.required]],value:["",[G.required]]}))}validate(e){const t=this.kvListFormGroup.get("keyVals").value;if(!t.length&&this.required)return{kvMapRequired:!0};if(!this.kvListFormGroup.valid)return{kvFieldsRequired:!0};if(this.uniqueKeyValuePairValidator)for(const e of t)if(e.key===e.value)return{uniqueKeyValuePair:!0};return null}updateModel(){const e=this.kvListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.kvListFormGroup.valid)this.propagateChange(null);else{const t={};e.forEach((e=>{t[e.key]=e.value})),this.propagateChange(t)}}}e("KvMapConfigComponent",wt),wt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:wt,deps:[{token:M.Store},{token:U.TranslateService},{token:t.Injector},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),wt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:wt,selector:"tb-kv-map-config",inputs:{disabled:"disabled",uniqueKeyValuePairValidator:"uniqueKeyValuePairValidator",requiredText:"requiredText",keyText:"keyText",keyRequiredText:"keyRequiredText",valText:"valText",valRequiredText:"valRequiredText",hintText:"hintText",required:"required"},providers:[{provide:D,useExisting:o((()=>wt)),multi:!0},{provide:V,useExisting:o((()=>wt)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
\n {{ keyText | translate }}\n {{ valText | translate }}\n \n
\n
\n
\n \n \n \n {{ keyRequiredText | translate }}\n \n \n \n \n \n {{ valRequiredText | translate }}\n \n \n \n
\n
\n
\n \n
\n \n
\n
\n',styles:[":host .tb-kv-map-config{margin-bottom:16px}:host .tb-kv-map-config .header{padding-left:5px;padding-right:5px;padding-bottom:5px}:host .tb-kv-map-config .header .cell{padding-left:5px;padding-right:5px;color:#0000008a;font-size:12px;font-weight:700;white-space:nowrap}:host .tb-kv-map-config .body{padding-left:5px;padding-right:5px;padding-bottom:0;max-height:300px;overflow:auto}:host .tb-kv-map-config .body .cell{padding-left:5px;padding-right:5px}:host .tb-kv-map-config tb-error{display:block;margin-top:-12px}\n"],dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:pe.TbErrorComponent,selector:"tb-error",inputs:["error"]},{kind:"component",type:te.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:te.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:de.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:B.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:ce.DefaultShowHideDirective,selector:" [fxShow], [fxShow.print], [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl], [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl], [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg], [fxHide], [fxHide.print], [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl], [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl], [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]",inputs:["fxShow","fxShow.print","fxShow.xs","fxShow.sm","fxShow.md","fxShow.lg","fxShow.xl","fxShow.lt-sm","fxShow.lt-md","fxShow.lt-lg","fxShow.lt-xl","fxShow.gt-xs","fxShow.gt-sm","fxShow.gt-md","fxShow.gt-lg","fxHide","fxHide.print","fxHide.xs","fxHide.sm","fxHide.md","fxHide.lg","fxHide.xl","fxHide.lt-sm","fxHide.lt-md","fxHide.lt-lg","fxHide.lt-xl","fxHide.gt-xs","fxHide.gt-sm","fxHide.gt-md","fxHide.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormArrayName,selector:"[formArrayName]",inputs:["formArrayName"]},{kind:"pipe",type:R.AsyncPipe,name:"async"},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:wt,decorators:[{type:r,args:[{selector:"tb-kv-map-config",providers:[{provide:D,useExisting:o((()=>wt)),multi:!0},{provide:V,useExisting:o((()=>wt)),multi:!0}],template:'
\n
\n {{ keyText | translate }}\n {{ valText | translate }}\n \n
\n
\n
\n \n \n \n {{ keyRequiredText | translate }}\n \n \n \n \n \n {{ valRequiredText | translate }}\n \n \n \n
\n
\n
\n \n
\n \n
\n
\n',styles:[":host .tb-kv-map-config{margin-bottom:16px}:host .tb-kv-map-config .header{padding-left:5px;padding-right:5px;padding-bottom:5px}:host .tb-kv-map-config .header .cell{padding-left:5px;padding-right:5px;color:#0000008a;font-size:12px;font-weight:700;white-space:nowrap}:host .tb-kv-map-config .body{padding-left:5px;padding-right:5px;padding-bottom:0;max-height:300px;overflow:auto}:host .tb-kv-map-config .body .cell{padding-left:5px;padding-right:5px}:host .tb-kv-map-config tb-error{display:block;margin-top:-12px}\n"]}]}],ctorParameters:function(){return[{type:M.Store},{type:U.TranslateService},{type:t.Injector},{type:A.UntypedFormBuilder}]},propDecorators:{disabled:[{type:i}],uniqueKeyValuePairValidator:[{type:i}],requiredText:[{type:i}],keyText:[{type:i}],keyRequiredText:[{type:i}],valText:[{type:i}],valRequiredText:[{type:i}],hintText:[{type:i}],required:[{type:i}]}});class Ot extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.saveToCustomTableConfigForm}onConfigurationSet(e){this.saveToCustomTableConfigForm=this.fb.group({tableName:[e?e.tableName:null,[G.required,G.pattern(/.*\S.*/)]],fieldsMapping:[e?e.fieldsMapping:null,[G.required]]})}prepareOutputConfig(e){return e.tableName=e.tableName.trim(),e}}e("SaveToCustomTableConfigComponent",Ot),Ot.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Ot,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ot.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Ot,selector:"tb-action-node-custom-table-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.custom-table-name\n \n \n {{ \'tb.rulenode.custom-table-name-required\' | translate }}\n \n tb.rulenode.custom-table-hint\n \n \n \n \n
\n',dependencies:[{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:wt,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Ot,decorators:[{type:r,args:[{selector:"tb-action-node-custom-table-config",template:'
\n \n tb.rulenode.custom-table-name\n \n \n {{ \'tb.rulenode.custom-table-name-required\' | translate }}\n \n tb.rulenode.custom-table-hint\n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Ht extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.timeseriesConfigForm}onConfigurationSet(e){this.timeseriesConfigForm=this.fb.group({defaultTTL:[e?e.defaultTTL:null,[G.required,G.min(0)]],skipLatestPersistence:[!!e&&e.skipLatestPersistence,[]],useServerTs:[!!e&&e.useServerTs,[]]})}}e("TimeseriesConfigComponent",Ht),Ht.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Ht,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ht.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Ht,selector:"tb-action-node-timeseries-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.default-ttl\n \n \n {{ \'tb.rulenode.default-ttl-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-default-ttl-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.skip-latest-persistence\' | translate }}\n \n \n {{ \'tb.rulenode.use-server-ts\' | translate }}\n \n
tb.rulenode.use-server-ts-hint
\n
\n',dependencies:[{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Ht,decorators:[{type:r,args:[{selector:"tb-action-node-timeseries-config",template:'
\n \n tb.rulenode.default-ttl\n \n \n {{ \'tb.rulenode.default-ttl-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-default-ttl-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.skip-latest-persistence\' | translate }}\n \n \n {{ \'tb.rulenode.use-server-ts\' | translate }}\n \n
tb.rulenode.use-server-ts-hint
\n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Kt extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.unassignCustomerConfigForm}onConfigurationSet(e){this.unassignCustomerConfigForm=this.fb.group({customerNamePattern:[e?e.customerNamePattern:null,[G.required,G.pattern(/.*\S.*/)]],customerCacheExpiration:[e?e.customerCacheExpiration:null,[G.required,G.min(0)]]})}prepareOutputConfig(e){return e.customerNamePattern=e.customerNamePattern.trim(),e}}e("UnassignCustomerConfigComponent",Kt),Kt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Kt,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Kt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Kt,selector:"tb-action-node-un-assign-to-customer-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Kt,decorators:[{type:r,args:[{selector:"tb-action-node-un-assign-to-customer-config",template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Bt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopeMap=m,this.attributeScopes=Object.keys(m),this.telemetryTypeTranslationsMap=u,this.separatorKeysCodes=[ne,ae,oe]}configForm(){return this.deleteAttributesConfigForm}onConfigurationSet(e){this.deleteAttributesConfigForm=this.fb.group({scope:[e?e.scope:null,[G.required]],keys:[e?e.keys:null,[G.required]],sendAttributesDeletedNotification:[!!e&&e.sendAttributesDeletedNotification,[]],notifyDevice:[!!e&&e.notifyDevice,[]]}),this.deleteAttributesConfigForm.get("scope").valueChanges.subscribe((e=>{e!==m.SHARED_SCOPE&&this.deleteAttributesConfigForm.get("notifyDevice").patchValue(!1,{emitEvent:!1})}))}removeKey(e){const t=this.deleteAttributesConfigForm.get("keys").value,r=t.indexOf(e);r>=0&&(t.splice(r,1),this.deleteAttributesConfigForm.get("keys").patchValue(t,{emitEvent:!0}))}addKey(e){const t=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.deleteAttributesConfigForm.get("keys").value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.deleteAttributesConfigForm.get("keys").patchValue(e,{emitEvent:!0}))}t&&(t.value="")}}e("DeleteAttributesConfigComponent",Bt),Bt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Bt,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Bt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Bt,selector:"tb-action-node-delete-attributes-config",viewQueries:[{propertyName:"attributeChipList",first:!0,predicate:["attributeChipList"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'attribute.attributes-scope\' | translate }}\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n \n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.attributes-keys-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.send-attributes-deleted-notification\' | translate }}\n \n
tb.rulenode.send-attributes-deleted-notification-hint
\n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
tb.rulenode.notify-device-delete-hint
\n
\n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:ie.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:le.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:le.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:le.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:le.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Bt,decorators:[{type:r,args:[{selector:"tb-action-node-delete-attributes-config",template:'
\n \n {{ \'attribute.attributes-scope\' | translate }}\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n \n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.attributes-keys-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.send-attributes-deleted-notification\' | translate }}\n \n
tb.rulenode.send-attributes-deleted-notification-hint
\n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
tb.rulenode.notify-device-delete-hint
\n
\n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]},propDecorators:{attributeChipList:[{type:a,args:["attributeChipList"]}]}});class Ut extends b{get function(){return this.functionValue}set function(e){e&&this.functionValue!==e&&(this.functionValue=e,this.setupArgumentsFormGroup())}constructor(e,t,r,n){super(e),this.store=e,this.translate=t,this.injector=r,this.fb=n,this.maxArgs=16,this.minArgs=1,this.displayArgumentName=!1,this.mathFunctionMap=vt,this.ArgumentType=Lt,this.attributeScopeMap=St,this.argumentTypeResultMap=It,this.arguments=Object.values(Lt),this.attributeScope=Object.values(Nt),this.propagateChange=null,this.valueChangeSubscription=[]}ngOnInit(){this.ngControl=this.injector.get(E),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.argumentsFormGroup=this.fb.group({}),this.argumentsFormGroup.addControl("arguments",this.fb.array([])),this.setupArgumentsFormGroup()}onDrop(e){const t=this.argumentsFormArray(),r=t.at(e.previousIndex);t.removeAt(e.previousIndex),t.insert(e.currentIndex,r),this.updateArgumentNames()}argumentsFormArray(){return this.argumentsFormGroup.get("arguments")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.argumentsFormGroup.disable({emitEvent:!1}):this.argumentsFormGroup.enable({emitEvent:!1})}ngOnDestroy(){this.valueChangeSubscription.length&&this.valueChangeSubscription.forEach((e=>e.unsubscribe()))}writeValue(e){this.valueChangeSubscription.length&&this.valueChangeSubscription.forEach((e=>e.unsubscribe()));const t=[];e&&e.forEach(((e,r)=>{t.push(this.createArgumentControl(e,r))})),this.argumentsFormGroup.setControl("arguments",this.fb.array(t)),this.setupArgumentsFormGroup(),this.valueChangeSubscription.push(this.argumentsFormGroup.valueChanges.subscribe((()=>{this.updateModel()})))}removeArgument(e){this.argumentsFormGroup.get("arguments").removeAt(e),this.updateArgumentNames()}addArgument(){const e=this.argumentsFormGroup.get("arguments"),t=this.createArgumentControl(null,e.length);e.push(t)}validate(e){return this.argumentsFormGroup.valid?null:{argumentsRequired:!0}}setupArgumentsFormGroup(){if(this.function&&(this.maxArgs=this.mathFunctionMap.get(this.function).maxArgs,this.minArgs=this.mathFunctionMap.get(this.function).minArgs,this.displayArgumentName=this.function===Ft.CUSTOM),this.argumentsFormGroup){for(this.argumentsFormGroup.get("arguments").setValidators([G.minLength(this.minArgs),G.maxLength(this.maxArgs)]),this.argumentsFormGroup.get("arguments").value.length>this.maxArgs&&(this.argumentsFormGroup.get("arguments").controls.length=this.maxArgs);this.argumentsFormGroup.get("arguments").value.length{this.updateArgumentControlValidators(r),r.get("attributeScope").updateValueAndValidity({emitEvent:!0}),r.get("defaultValue").updateValueAndValidity({emitEvent:!0})}))),r}updateArgumentControlValidators(e){const t=e.get("type").value;t===Lt.ATTRIBUTE?e.get("attributeScope").enable():e.get("attributeScope").disable(),t&&t!==Lt.CONSTANT?e.get("defaultValue").enable():e.get("defaultValue").disable()}updateArgumentNames(){this.argumentsFormGroup.get("arguments").controls.forEach(((e,t)=>{e.get("name").setValue(Tt[t])}))}updateModel(){const e=this.argumentsFormGroup.get("arguments").value;e.length&&this.argumentsFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}}e("ArgumentsMapConfigComponent",Ut),Ut.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Ut,deps:[{token:M.Store},{token:U.TranslateService},{token:t.Injector},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ut.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Ut,selector:"tb-arguments-map-config",inputs:{disabled:"disabled",function:"function"},providers:[{provide:D,useExisting:o((()=>Ut)),multi:!0},{provide:V,useExisting:o((()=>Ut)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n\n
\n \n \n
\n \n
\n {{argumentControl.get(\'name\').value}}.\n
\n
\n \n tb.rulenode.argument-type-field-input\n \n \n {{ argumentTypeResultMap.get(argument) | translate }}\n \n \n \n tb.rulenode.argument-type-field-input-required\n \n \n \n tb.rulenode.argument-key-field-input\n \n \n tb.rulenode.argument-key-field-input-required\n \n \n \n tb.rulenode.constant-value-field-input\n \n \n tb.rulenode.constant-value-field-input-required\n \n \n
\n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n tb.rulenode.attribute-scope-field-input-required\n \n \n \n tb.rulenode.default-value-field-input\n \n \n
\n
\n \n
\n
\n
\n
\n
\n
\n tb.rulenode.no-arguments-prompt\n
\n
\n \n
\n
\n',styles:[":host .mat-mdc-list-item.tb-argument{border:solid rgba(0,0,0,.25) 1px;border-radius:4px;padding:10px 0;margin-bottom:10px}\n"],dependencies:[{kind:"directive",type:R.NgClass,selector:"[ngClass]",inputs:["class","ngClass"]},{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:te.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:te.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:de.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:fe.MatList,selector:"mat-list",exportAs:["matList"]},{kind:"component",type:fe.MatListItem,selector:"mat-list-item, a[mat-list-item], button[mat-list-item]",inputs:["activated"],exportAs:["matListItem"]},{kind:"directive",type:ge.CdkDropList,selector:"[cdkDropList], cdk-drop-list",inputs:["cdkDropListConnectedTo","cdkDropListData","cdkDropListOrientation","id","cdkDropListLockAxis","cdkDropListDisabled","cdkDropListSortingDisabled","cdkDropListEnterPredicate","cdkDropListSortPredicate","cdkDropListAutoScrollDisabled","cdkDropListAutoScrollStep"],outputs:["cdkDropListDropped","cdkDropListEntered","cdkDropListExited","cdkDropListSorted"],exportAs:["cdkDropList"]},{kind:"directive",type:ge.CdkDrag,selector:"[cdkDrag]",inputs:["cdkDragData","cdkDragLockAxis","cdkDragRootElement","cdkDragBoundary","cdkDragStartDelay","cdkDragFreeDragPosition","cdkDragDisabled","cdkDragConstrainPosition","cdkDragPreviewClass","cdkDragPreviewContainer"],outputs:["cdkDragStarted","cdkDragReleased","cdkDragEnded","cdkDragEntered","cdkDragExited","cdkDragDropped","cdkDragMoved"],exportAs:["cdkDrag"]},{kind:"directive",type:ge.CdkDragHandle,selector:"[cdkDragHandle]",inputs:["cdkDragHandleDisabled"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:B.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:ce.DefaultClassDirective,selector:" [ngClass], [ngClass.xs], [ngClass.sm], [ngClass.md], [ngClass.lg], [ngClass.xl], [ngClass.lt-sm], [ngClass.lt-md], [ngClass.lt-lg], [ngClass.lt-xl], [ngClass.gt-xs], [ngClass.gt-sm], [ngClass.gt-md], [ngClass.gt-lg]",inputs:["ngClass","ngClass.xs","ngClass.sm","ngClass.md","ngClass.lg","ngClass.xl","ngClass.lt-sm","ngClass.lt-md","ngClass.lt-lg","ngClass.lt-xl","ngClass.gt-xs","ngClass.gt-sm","ngClass.gt-md","ngClass.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormArrayName,selector:"[formArrayName]",inputs:["formArrayName"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Ut,decorators:[{type:r,args:[{selector:"tb-arguments-map-config",providers:[{provide:D,useExisting:o((()=>Ut)),multi:!0},{provide:V,useExisting:o((()=>Ut)),multi:!0}],template:'
\n\n
\n \n \n
\n \n
\n {{argumentControl.get(\'name\').value}}.\n
\n
\n \n tb.rulenode.argument-type-field-input\n \n \n {{ argumentTypeResultMap.get(argument) | translate }}\n \n \n \n tb.rulenode.argument-type-field-input-required\n \n \n \n tb.rulenode.argument-key-field-input\n \n \n tb.rulenode.argument-key-field-input-required\n \n \n \n tb.rulenode.constant-value-field-input\n \n \n tb.rulenode.constant-value-field-input-required\n \n \n
\n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n tb.rulenode.attribute-scope-field-input-required\n \n \n \n tb.rulenode.default-value-field-input\n \n \n
\n
\n \n
\n
\n
\n
\n
\n
\n tb.rulenode.no-arguments-prompt\n
\n
\n \n
\n
\n',styles:[":host .mat-mdc-list-item.tb-argument{border:solid rgba(0,0,0,.25) 1px;border-radius:4px;padding:10px 0;margin-bottom:10px}\n"]}]}],ctorParameters:function(){return[{type:M.Store},{type:U.TranslateService},{type:t.Injector},{type:A.UntypedFormBuilder}]},propDecorators:{disabled:[{type:i}],function:[{type:i}]}});class zt extends b{get required(){return this.requiredValue}set required(e){this.requiredValue=ue(e)}constructor(e,t,r,n){super(e),this.store=e,this.translate=t,this.injector=r,this.fb=n,this.searchText="",this.dirty=!1,this.mathOperation=[...vt.values()],this.propagateChange=null}ngOnInit(){this.mathFunctionForm=this.fb.group({operation:[""]}),this.filteredOptions=this.mathFunctionForm.get("operation").valueChanges.pipe(ye((e=>{let t;t="string"==typeof e&&Ft[e]?Ft[e]:null,this.updateView(t)})),xe((e=>(this.searchText=e||"",e?this._filter(e):this.mathOperation.slice()))))}_filter(e){const t=e.toLowerCase();return this.mathOperation.filter((e=>e.name.toLowerCase().includes(t)||e.value.toLowerCase().includes(t)))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.mathFunctionForm.disable({emitEvent:!1}):this.mathFunctionForm.enable({emitEvent:!1})}mathFunctionDisplayFn(e){if(e){const t=vt.get(e);return t.value+" | "+t.name}return""}writeValue(e){this.modelValue=e,this.mathFunctionForm.get("operation").setValue(e,{emitEvent:!1}),this.dirty=!0}updateView(e){this.modelValue!==e&&(this.modelValue=e,this.propagateChange(this.modelValue))}onFocus(){this.dirty&&(this.mathFunctionForm.get("operation").updateValueAndValidity({onlySelf:!0}),this.dirty=!1)}clear(){this.mathFunctionForm.get("operation").patchValue(""),setTimeout((()=>{this.operationInput.nativeElement.blur(),this.operationInput.nativeElement.focus()}),0)}}e("MathFunctionAutocompleteComponent",zt),zt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:zt,deps:[{token:M.Store},{token:U.TranslateService},{token:t.Injector},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),zt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:zt,selector:"tb-math-function-autocomplete",inputs:{required:"required",disabled:"disabled"},providers:[{provide:D,useExisting:o((()=>zt)),multi:!0}],viewQueries:[{propertyName:"operationInput",first:!0,predicate:["operationInput"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'\n tb.rulenode.functions-field-input\n \n \n \n \n \n \n {{ option.description }}\n \n \n \n tb.rulenode.no-option-found\n \n \n\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:te.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:ve.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple","hideSingleSelectionIndicator"],exportAs:["matAutocomplete"]},{kind:"directive",type:ve.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:R.AsyncPipe,name:"async"},{kind:"pipe",type:Le.HighlightPipe,name:"highlight"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:zt,decorators:[{type:r,args:[{selector:"tb-math-function-autocomplete",providers:[{provide:D,useExisting:o((()=>zt)),multi:!0}],template:'\n tb.rulenode.functions-field-input\n \n \n \n \n \n \n {{ option.description }}\n \n \n \n tb.rulenode.no-option-found\n \n \n\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:U.TranslateService},{type:t.Injector},{type:A.UntypedFormBuilder}]},propDecorators:{required:[{type:i}],disabled:[{type:i}],operationInput:[{type:a,args:["operationInput",{static:!0}]}]}});class jt extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.MathFunction=Ft,this.ArgumentTypeResult=kt,this.argumentTypeResultMap=It,this.attributeScopeMap=St,this.argumentsResult=Object.values(kt),this.attributeScopeResult=Object.values(qt)}configForm(){return this.mathFunctionConfigForm}onConfigurationSet(e){this.mathFunctionConfigForm=this.fb.group({operation:[e?e.operation:null,[G.required]],arguments:[e?e.arguments:null,[G.required]],customFunction:[e?e.customFunction:"",[G.required]],result:this.fb.group({type:[e?e.result.type:null,[G.required]],attributeScope:[e?e.result.attributeScope:null],key:[e?e.result.key:"",[G.required]],resultValuePrecision:[e?e.result.resultValuePrecision:0],addToBody:[!!e&&e.result.addToBody],addToMetadata:[!!e&&e.result.addToMetadata]})})}updateValidators(e){const t=this.mathFunctionConfigForm.get("operation").value,r=this.mathFunctionConfigForm.get("result").get("type").value;t===Ft.CUSTOM?this.mathFunctionConfigForm.get("customFunction").enable({emitEvent:!1}):this.mathFunctionConfigForm.get("customFunction").disable({emitEvent:!1}),r===kt.ATTRIBUTE?this.mathFunctionConfigForm.get("result").get("attributeScope").enable({emitEvent:!1}):this.mathFunctionConfigForm.get("result").get("attributeScope").disable({emitEvent:!1}),this.mathFunctionConfigForm.get("customFunction").updateValueAndValidity({emitEvent:e}),this.mathFunctionConfigForm.get("result").get("attributeScope").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["operation","result.type"]}}e("MathFunctionConfigComponent",jt),jt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:jt,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),jt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:jt,selector:"tb-action-node-math-function-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n tb.rulenode.argument-tile\n \n \n
\n
\n {{\'tb.rulenode.custom-expression-field-input\' | translate }} *\n \n \n \n tb.rulenode.custom-expression-field-input-required\n \n \n \n
\n
\n tb.rulenode.result-title\n
\n
\n \n tb.rulenode.type-field-input\n \n \n {{ argumentTypeResultMap.get(argument) | translate }}\n \n \n \n tb.rulenode.type-field-input-required\n \n \n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n \n tb.rulenode.key-field-input\n \n \n tb.rulenode.key-field-input-required\n \n \n
\n
\n \n tb.rulenode.number-floating-point-field-input\n \n \n \n
\n
\n
\n \n {{\'tb.rulenode.add-to-body-field-input\' | translate }}\n \n \n {{\'tb.rulenode.add-to-metadata-field-input\' | translate}}\n \n
\n
\n
\n
\n',styles:[":host ::ng-deep .fields-group{padding:0 16px 8px;margin:10px 0;border:1px groove rgba(0,0,0,.25);border-radius:4px}:host ::ng-deep .fields-group .mat-mdc-form-field .mat-mdc-form-field-infix{width:100%}:host ::ng-deep .fields-group legend{color:#000000b3;width:-moz-fit-content;width:fit-content}:host ::ng-deep .fields-group legend+*{display:block;margin-top:16px}:host ::ng-deep .fields-group legend+*.no-margin-top{margin-top:0}\n"],dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:B.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:A.FormGroupName,selector:"[formGroupName]",inputs:["formGroupName"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Ut,selector:"tb-arguments-map-config",inputs:["disabled","function"]},{kind:"component",type:zt,selector:"tb-math-function-autocomplete",inputs:["required","disabled"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:jt,decorators:[{type:r,args:[{selector:"tb-action-node-math-function-config",template:'
\n \n \n
\n tb.rulenode.argument-tile\n \n \n
\n
\n {{\'tb.rulenode.custom-expression-field-input\' | translate }} *\n \n \n \n tb.rulenode.custom-expression-field-input-required\n \n \n \n
\n
\n tb.rulenode.result-title\n
\n
\n \n tb.rulenode.type-field-input\n \n \n {{ argumentTypeResultMap.get(argument) | translate }}\n \n \n \n tb.rulenode.type-field-input-required\n \n \n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n \n tb.rulenode.key-field-input\n \n \n tb.rulenode.key-field-input-required\n \n \n
\n
\n \n tb.rulenode.number-floating-point-field-input\n \n \n \n
\n
\n
\n \n {{\'tb.rulenode.add-to-body-field-input\' | translate }}\n \n \n {{\'tb.rulenode.add-to-metadata-field-input\' | translate}}\n \n
\n
\n
\n
\n',styles:[":host ::ng-deep .fields-group{padding:0 16px 8px;margin:10px 0;border:1px groove rgba(0,0,0,.25);border-radius:4px}:host ::ng-deep .fields-group .mat-mdc-form-field .mat-mdc-form-field-infix{width:100%}:host ::ng-deep .fields-group legend{color:#000000b3;width:-moz-fit-content;width:fit-content}:host ::ng-deep .fields-group legend+*{display:block;margin-top:16px}:host ::ng-deep .fields-group legend+*.no-margin-top{margin-top:0}\n"]}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class _t{constructor(e,t){this.store=e,this.fb=t,this.subscriptSizing="fixed",this.searchText="",this.dirty=!1,this.messageTypes=["POST_ATTRIBUTES_REQUEST","POST_TELEMETRY_REQUEST"],this.propagateChange=e=>{},this.messageTypeFormGroup=this.fb.group({messageType:[null,[G.required,G.maxLength(255)]]})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}ngOnInit(){this.outputMessageTypes=this.messageTypeFormGroup.get("messageType").valueChanges.pipe(ye((e=>{this.updateView(e)})),xe((e=>e||"")),be((e=>this.fetchMessageTypes(e))))}writeValue(e){this.searchText="",this.modelValue=e,this.messageTypeFormGroup.get("messageType").patchValue(e,{emitEvent:!1}),this.dirty=!0}onFocus(){this.dirty&&(this.messageTypeFormGroup.get("messageType").updateValueAndValidity({onlySelf:!0,emitEvent:!0}),this.dirty=!1)}updateView(e){this.modelValue!==e&&(this.modelValue=e,this.propagateChange(this.modelValue))}displayMessageTypeFn(e){return e||void 0}fetchMessageTypes(e,t=!1){return this.searchText=e,ke(this.messageTypes).pipe(xe((r=>r.filter((r=>t?!!e&&r===e:!e||r.toUpperCase().startsWith(e.toUpperCase()))))))}clear(){this.messageTypeFormGroup.get("messageType").patchValue(null,{emitEvent:!0}),setTimeout((()=>{this.messageTypeInput.nativeElement.blur(),this.messageTypeInput.nativeElement.focus()}),0)}}e("OutputMessageTypeAutocompleteComponent",_t),_t.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:_t,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),_t.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:_t,selector:"tb-output-message-type-autocomplete",inputs:{autocompleteHint:"autocompleteHint",subscriptSizing:"subscriptSizing"},providers:[{provide:D,useExisting:o((()=>_t)),multi:!0}],viewQueries:[{propertyName:"messageTypeInput",first:!0,predicate:["messageTypeInput"],descendants:!0,static:!0}],ngImport:t,template:'\n \n \n \n \n {{msgType}}\n \n \n {{autocompleteHint | translate}}\n \n {{ \'tb.rulenode.output-message-type-required\' | translate }}\n \n \n {{ \'tb.rulenode.output-message-type-max-length\' | translate }}\n \n\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:te.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ie.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:K.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:ve.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple","hideSingleSelectionIndicator"],exportAs:["matAutocomplete"]},{kind:"directive",type:ve.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:R.AsyncPipe,name:"async"},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:_t,decorators:[{type:r,args:[{selector:"tb-output-message-type-autocomplete",providers:[{provide:D,useExisting:o((()=>_t)),multi:!0}],template:'\n \n \n \n \n {{msgType}}\n \n \n {{autocompleteHint | translate}}\n \n {{ \'tb.rulenode.output-message-type-required\' | translate }}\n \n \n {{ \'tb.rulenode.output-message-type-max-length\' | translate }}\n \n\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]},propDecorators:{messageTypeInput:[{type:a,args:["messageTypeInput",{static:!0}]}],autocompleteHint:[{type:i}],subscriptSizing:[{type:i}]}});class $t extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.destroy$=new Ie,this.serviceType=p.TB_RULE_ENGINE,this.deduplicationStrategie=mt,this.deduplicationStrategies=Object.keys(this.deduplicationStrategie),this.deduplicationStrategiesTranslations=ut}configForm(){return this.deduplicationConfigForm}onConfigurationSet(e){this.deduplicationConfigForm=this.fb.group({interval:[Y(e?.interval)?e.interval:null,[G.required,G.min(1)]],strategy:[Y(e?.strategy)?e.strategy:null,[G.required]],outMsgType:[Y(e?.outMsgType)?e.outMsgType:null,[G.required]],queueName:[Y(e?.queueName)?e.queueName:null,[G.required]],maxPendingMsgs:[Y(e?.maxPendingMsgs)?e.maxPendingMsgs:null,[G.required,G.min(1),G.max(1e3)]],maxRetries:[Y(e?.maxRetries)?e.maxRetries:null,[G.required,G.min(0),G.max(100)]]}),this.deduplicationConfigForm.get("strategy").valueChanges.pipe(he(this.destroy$)).subscribe((e=>{this.enableControl(e)}))}updateValidators(e){this.enableControl(this.deduplicationConfigForm.get("strategy").value)}validatorTriggers(){return["strategy"]}enableControl(e){e===this.deduplicationStrategie.ALL?(this.deduplicationConfigForm.get("outMsgType").enable({emitEvent:!1}),this.deduplicationConfigForm.get("queueName").enable({emitEvent:!1})):(this.deduplicationConfigForm.get("outMsgType").disable({emitEvent:!1}),this.deduplicationConfigForm.get("queueName").disable({emitEvent:!1}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}}e("DeduplicationConfigComponent",$t),$t.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:$t,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),$t.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:$t,selector:"tb-action-node-msg-deduplication-config",usesInheritance:!0,ngImport:t,template:"
\n \n {{'tb.rulenode.interval' | translate}}\n \n {{'tb.rulenode.interval-hint' | translate}}\n \n {{'tb.rulenode.interval-required' | translate}}\n \n \n {{'tb.rulenode.interval-min-error' | translate}}\n \n \n \n {{'tb.rulenode.strategy' | translate}}\n \n \n {{ deduplicationStrategiesTranslations.get(strategy) | translate }}\n \n \n \n {{'tb.rulenode.strategy-first-hint' | translate}}\n {{'tb.rulenode.strategy-last-hint' | translate}}\n \n {{'tb.rulenode.strategy-required' | translate}}\n \n \n
\n \n \n \n \n
\n \n \n \n
\n
Advanced settings
\n
\n
\n
\n \n \n {{'tb.rulenode.max-pending-msgs' | translate}}\n \n {{'tb.rulenode.max-pending-msgs-hint' | translate}}\n \n {{'tb.rulenode.max-pending-msgs-required' | translate}}\n \n \n {{'tb.rulenode.max-pending-msgs-max-error' | translate}}\n \n \n {{'tb.rulenode.max-pending-msgs-min-error' | translate}}\n \n \n \n {{'tb.rulenode.max-retries' | translate}}\n \n {{'tb.rulenode.max-retries-hint' | translate}}\n \n {{'tb.rulenode.max-retries-required' | translate}}\n \n \n {{'tb.rulenode.max-retries-max-error' | translate}}\n \n \n {{'tb.rulenode.max-retries-min-error' | translate}}\n \n \n \n
\n
\n",styles:[":host ::ng-deep .mat-expansion-panel.advanced-settings{border:none;box-shadow:none;padding:0}:host ::ng-deep .mat-expansion-panel.advanced-settings .mat-expansion-panel-body{padding:0}:host ::ng-deep .mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:not([aria-disabled=true]):hover{background:white}\n"],dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:$.QueueAutocompleteComponent,selector:"tb-queue-autocomplete",inputs:["labelText","requiredText","autocompleteHint","subscriptSizing","required","queueType","disabled"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:Te.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{kind:"component",type:Te.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{kind:"directive",type:Te.MatExpansionPanelTitle,selector:"mat-panel-title"},{kind:"directive",type:Te.MatExpansionPanelContent,selector:"ng-template[matExpansionPanelContent]"},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:_t,selector:"tb-output-message-type-autocomplete",inputs:["autocompleteHint","subscriptSizing"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:$t,decorators:[{type:r,args:[{selector:"tb-action-node-msg-deduplication-config",template:"
\n \n {{'tb.rulenode.interval' | translate}}\n \n {{'tb.rulenode.interval-hint' | translate}}\n \n {{'tb.rulenode.interval-required' | translate}}\n \n \n {{'tb.rulenode.interval-min-error' | translate}}\n \n \n \n {{'tb.rulenode.strategy' | translate}}\n \n \n {{ deduplicationStrategiesTranslations.get(strategy) | translate }}\n \n \n \n {{'tb.rulenode.strategy-first-hint' | translate}}\n {{'tb.rulenode.strategy-last-hint' | translate}}\n \n {{'tb.rulenode.strategy-required' | translate}}\n \n \n
\n \n \n \n \n
\n \n \n \n
\n
Advanced settings
\n
\n
\n
\n \n \n {{'tb.rulenode.max-pending-msgs' | translate}}\n \n {{'tb.rulenode.max-pending-msgs-hint' | translate}}\n \n {{'tb.rulenode.max-pending-msgs-required' | translate}}\n \n \n {{'tb.rulenode.max-pending-msgs-max-error' | translate}}\n \n \n {{'tb.rulenode.max-pending-msgs-min-error' | translate}}\n \n \n \n {{'tb.rulenode.max-retries' | translate}}\n \n {{'tb.rulenode.max-retries-hint' | translate}}\n \n {{'tb.rulenode.max-retries-required' | translate}}\n \n \n {{'tb.rulenode.max-retries-max-error' | translate}}\n \n \n {{'tb.rulenode.max-retries-min-error' | translate}}\n \n \n \n
\n
\n",styles:[":host ::ng-deep .mat-expansion-panel.advanced-settings{border:none;box-shadow:none;padding:0}:host ::ng-deep .mat-expansion-panel.advanced-settings .mat-expansion-panel-body{padding:0}:host ::ng-deep .mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:not([aria-disabled=true]):hover{background:white}\n"]}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Jt extends b{get required(){return this.requiredValue}set required(e){this.requiredValue=ue(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(g),this.directionTypeTranslations=y,this.entityType=x,this.propagateChange=null}ngOnInit(){this.deviceRelationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[G.required]],maxLevel:[null,[]],relationType:[null],deviceTypes:[null,[G.required]]}),this.deviceRelationsQueryFormGroup.valueChanges.subscribe((e=>{this.deviceRelationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.deviceRelationsQueryFormGroup.disable({emitEvent:!1}):this.deviceRelationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.deviceRelationsQueryFormGroup.reset(e,{emitEvent:!1})}}e("DeviceRelationsQueryConfigComponent",Jt),Jt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Jt,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Jt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Jt,selector:"tb-device-relations-query-config",inputs:{disabled:"disabled",required:"required"},providers:[{provide:D,useExisting:o((()=>Jt)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n
\n
relation.relation-type
\n \n \n
device.device-types
\n \n \n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:qe.EntitySubTypeListComponent,selector:"tb-entity-subtype-list",inputs:["required","disabled","entityType"]},{kind:"component",type:Se.RelationTypeAutocompleteComponent,selector:"tb-relation-type-autocomplete",inputs:["required","disabled","subscriptSizing"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Jt,decorators:[{type:r,args:[{selector:"tb-device-relations-query-config",providers:[{provide:D,useExisting:o((()=>Jt)),multi:!0}],template:'
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n
\n
relation.relation-type
\n \n \n
device.device-types
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]},propDecorators:{disabled:[{type:i}],required:[{type:i}]}});class Qt extends b{get required(){return this.requiredValue}set required(e){this.requiredValue=ue(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(g),this.directionTypeTranslations=y,this.propagateChange=null}ngOnInit(){this.relationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[G.required]],maxLevel:[null,[]],filters:[null]}),this.relationsQueryFormGroup.valueChanges.subscribe((e=>{this.relationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.relationsQueryFormGroup.disable({emitEvent:!1}):this.relationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.relationsQueryFormGroup.reset(e||{},{emitEvent:!1})}}e("RelationsQueryConfigComponent",Qt),Qt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Qt,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Qt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Qt,selector:"tb-relations-query-config",inputs:{disabled:"disabled",required:"required"},providers:[{provide:D,useExisting:o((()=>Qt)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n
\n
relation.relation-filters
\n \n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Me.RelationFiltersComponent,selector:"tb-relation-filters",inputs:["disabled","allowedEntityTypes"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Qt,decorators:[{type:r,args:[{selector:"tb-relations-query-config",providers:[{provide:D,useExisting:o((()=>Qt)),multi:!0}],template:'
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n
\n
relation.relation-filters
\n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]},propDecorators:{disabled:[{type:i}],required:[{type:i}]}});class Yt extends b{get required(){return this.requiredValue}set required(e){this.requiredValue=ue(e)}constructor(e,t,r,n){super(e),this.store=e,this.translate=t,this.truncate=r,this.fb=n,this.placeholder="tb.rulenode.message-type",this.separatorKeysCodes=[ne,ae,oe],this.messageTypes=[],this.messageTypesList=[],this.searchText="",this.propagateChange=e=>{},this.messageTypeConfigForm=this.fb.group({messageType:[null]});for(const e of Object.keys(h))this.messageTypesList.push({name:C.get(h[e]),value:e})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}ngOnInit(){this.filteredMessageTypes=this.messageTypeConfigForm.get("messageType").valueChanges.pipe(Ce(""),xe((e=>e||"")),be((e=>this.fetchMessageTypes(e))),Fe())}ngAfterViewInit(){}setDisabledState(e){this.disabled=e,this.disabled?this.messageTypeConfigForm.disable({emitEvent:!1}):this.messageTypeConfigForm.enable({emitEvent:!1})}writeValue(e){this.searchText="",this.messageTypes.length=0,e&&e.forEach((e=>{const t=this.messageTypesList.find((t=>t.value===e));t?this.messageTypes.push({name:t.name,value:t.value}):this.messageTypes.push({name:e,value:e})}))}displayMessageTypeFn(e){return e?e.name:void 0}textIsNotEmpty(e){return!!(e&&null!=e&&e.length>0)}createMessageType(e,t){e.preventDefault(),this.transformMessageType(t)}add(e){this.transformMessageType(e.value)}fetchMessageTypes(e){if(this.searchText=e,this.searchText&&this.searchText.length){const e=this.searchText.toUpperCase();return ke(this.messageTypesList.filter((t=>t.name.toUpperCase().includes(e))))}return ke(this.messageTypesList)}transformMessageType(e){if((e||"").trim()){let t=null;const r=e.trim(),n=this.messageTypesList.find((e=>e.name===r));t=n?{name:n.name,value:n.value}:{name:r,value:r},t&&this.addMessageType(t)}this.clear("")}remove(e){const t=this.messageTypes.indexOf(e);t>=0&&(this.messageTypes.splice(t,1),this.updateModel())}selected(e){this.addMessageType(e.option.value),this.clear("")}addMessageType(e){-1===this.messageTypes.findIndex((t=>t.value===e.value))&&(this.messageTypes.push(e),this.updateModel())}onFocus(){this.messageTypeConfigForm.get("messageType").updateValueAndValidity({onlySelf:!0,emitEvent:!0})}clear(e=""){this.messageTypeInput.nativeElement.value=e,this.messageTypeConfigForm.get("messageType").patchValue(null,{emitEvent:!0}),setTimeout((()=>{this.messageTypeInput.nativeElement.blur(),this.messageTypeInput.nativeElement.focus()}),0)}updateModel(){const e=this.messageTypes.map((e=>e.value));this.required?(this.chipList.errorState=!e.length,this.propagateChange(e.length>0?e:null)):(this.chipList.errorState=!1,this.propagateChange(e))}}e("MessageTypesConfigComponent",Yt),Yt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Yt,deps:[{token:M.Store},{token:U.TranslateService},{token:F.TruncatePipe},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Yt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Yt,selector:"tb-message-types-config",inputs:{required:"required",label:"label",placeholder:"placeholder",disabled:"disabled"},providers:[{provide:D,useExisting:o((()=>Yt)),multi:!0}],viewQueries:[{propertyName:"chipList",first:!0,predicate:["chipList"],descendants:!0},{propertyName:"matAutocomplete",first:!0,predicate:["messageTypeAutocomplete"],descendants:!0},{propertyName:"messageTypeInput",first:!0,predicate:["messageTypeInput"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'\n {{ label }}\n \n \n {{messageType.name}}\n close\n \n \n \n \n \n \n \n \n
\n
\n tb.rulenode.no-message-types-found\n
\n \n \n {{ translate.get(\'tb.rulenode.no-message-type-matching\',\n {messageType: truncate.transform(searchText, true, 6, '...')}) | async }}\n \n \n \n tb.rulenode.create-new-message-type\n \n
\n
\n
\n \n {{ \'tb.rulenode.message-types-required\' | translate }}\n \n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ie.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:ve.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple","hideSingleSelectionIndicator"],exportAs:["matAutocomplete"]},{kind:"directive",type:ve.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{kind:"directive",type:ve.MatAutocompleteOrigin,selector:"[matAutocompleteOrigin]",exportAs:["matAutocompleteOrigin"]},{kind:"component",type:le.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:le.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:le.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:le.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:R.AsyncPipe,name:"async"},{kind:"pipe",type:Le.HighlightPipe,name:"highlight"},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Yt,decorators:[{type:r,args:[{selector:"tb-message-types-config",providers:[{provide:D,useExisting:o((()=>Yt)),multi:!0}],template:'\n {{ label }}\n \n \n {{messageType.name}}\n close\n \n \n \n \n \n \n \n \n
\n
\n tb.rulenode.no-message-types-found\n
\n \n \n {{ translate.get(\'tb.rulenode.no-message-type-matching\',\n {messageType: truncate.transform(searchText, true, 6, '...')}) | async }}\n \n \n \n tb.rulenode.create-new-message-type\n \n
\n
\n
\n \n {{ \'tb.rulenode.message-types-required\' | translate }}\n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:U.TranslateService},{type:F.TruncatePipe},{type:A.UntypedFormBuilder}]},propDecorators:{required:[{type:i}],label:[{type:i}],placeholder:[{type:i}],disabled:[{type:i}],chipList:[{type:a,args:["chipList",{static:!1}]}],matAutocomplete:[{type:a,args:["messageTypeAutocomplete",{static:!1}]}],messageTypeInput:[{type:a,args:["messageTypeInput",{static:!1}]}]}});class Wt extends b{get required(){return this.requiredValue}set required(e){this.requiredValue=ue(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.subscriptions=[],this.disableCertPemCredentials=!1,this.passwordFieldRequired=!0,this.allCredentialsTypes=ft,this.credentialsTypeTranslationsMap=gt,this.propagateChange=e=>{}}ngOnInit(){this.credentialsConfigFormGroup=this.fb.group({type:[null,[G.required]],username:[null,[]],password:[null,[]],caCert:[null,[]],caCertFileName:[null,[]],privateKey:[null,[]],privateKeyFileName:[null,[]],cert:[null,[]],certFileName:[null,[]]}),this.subscriptions.push(this.credentialsConfigFormGroup.valueChanges.subscribe((()=>{this.updateView()}))),this.subscriptions.push(this.credentialsConfigFormGroup.get("type").valueChanges.subscribe((()=>{this.credentialsTypeChanged()})))}ngOnChanges(e){for(const t of Object.keys(e)){const r=e[t];if(!r.firstChange&&r.currentValue!==r.previousValue&&r.currentValue&&"disableCertPemCredentials"===t){"cert.PEM"===this.credentialsConfigFormGroup.get("type").value&&setTimeout((()=>{this.credentialsConfigFormGroup.get("type").patchValue("anonymous",{emitEvent:!0})}))}}}ngOnDestroy(){this.subscriptions.forEach((e=>e.unsubscribe()))}writeValue(e){Y(e)&&(this.credentialsConfigFormGroup.reset(e,{emitEvent:!1}),this.updateValidators())}setDisabledState(e){e?this.credentialsConfigFormGroup.disable({emitEvent:!1}):(this.credentialsConfigFormGroup.enable({emitEvent:!1}),this.updateValidators())}updateView(){let e=this.credentialsConfigFormGroup.value;const t=e.type;switch(t){case"anonymous":e={type:t};break;case"basic":e={type:t,username:e.username,password:e.password};break;case"cert.PEM":delete e.username}this.propagateChange(e)}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}validate(e){return this.credentialsConfigFormGroup.valid?null:{credentialsConfig:{valid:!1}}}credentialsTypeChanged(){this.credentialsConfigFormGroup.patchValue({username:null,password:null,caCert:null,caCertFileName:null,privateKey:null,privateKeyFileName:null,cert:null,certFileName:null}),this.updateValidators()}updateValidators(e=!1){const t=this.credentialsConfigFormGroup.get("type").value;switch(e&&this.credentialsConfigFormGroup.reset({type:t},{emitEvent:!1}),this.credentialsConfigFormGroup.setValidators([]),this.credentialsConfigFormGroup.get("username").setValidators([]),this.credentialsConfigFormGroup.get("password").setValidators([]),t){case"anonymous":break;case"basic":this.credentialsConfigFormGroup.get("username").setValidators([G.required]),this.credentialsConfigFormGroup.get("password").setValidators(this.passwordFieldRequired?[G.required]:[]);break;case"cert.PEM":this.credentialsConfigFormGroup.setValidators([this.requiredFilesSelected(G.required,[["caCert","caCertFileName"],["privateKey","privateKeyFileName","cert","certFileName"]])])}this.credentialsConfigFormGroup.get("username").updateValueAndValidity({emitEvent:e}),this.credentialsConfigFormGroup.get("password").updateValueAndValidity({emitEvent:e}),this.credentialsConfigFormGroup.updateValueAndValidity({emitEvent:e})}requiredFilesSelected(e,t=null){return r=>{t||(t=[Object.keys(r.controls)]);return r?.controls&&t.some((t=>t.every((t=>!e(r.controls[t])))))?null:{notAllRequiredFilesSelected:!0}}}}e("CredentialsConfigComponent",Wt),Wt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Wt,deps:[{token:M.Store},{token:A.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Wt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Wt,selector:"tb-credentials-config",inputs:{required:"required",disableCertPemCredentials:"disableCertPemCredentials",passwordFieldRequired:"passwordFieldRequired"},providers:[{provide:D,useExisting:o((()=>Wt)),multi:!0},{provide:V,useExisting:o((()=>Wt)),multi:!0}],usesInheritance:!0,usesOnChanges:!0,ngImport:t,template:'
\n \n \n tb.rulenode.credentials\n \n {{ credentialsTypeTranslationsMap.get(credentialsConfigFormGroup.get(\'type\').value) | translate }}\n \n \n \n \n tb.rulenode.credentials-type\n \n \n {{ credentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.username\n \n \n {{ \'tb.rulenode.username-required\' | translate }}\n \n \n \n tb.rulenode.password\n \n \n \n {{ \'tb.rulenode.password-required\' | translate }}\n \n \n \n \n
{{ \'tb.rulenode.credentials-pem-hint\' | translate }}
\n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n
\n
\n
\n
\n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:R.NgSwitch,selector:"[ngSwitch]",inputs:["ngSwitch"]},{kind:"directive",type:R.NgSwitchCase,selector:"[ngSwitchCase]",inputs:["ngSwitchCase"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:K.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:Te.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{kind:"component",type:Te.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{kind:"directive",type:Te.MatExpansionPanelTitle,selector:"mat-panel-title"},{kind:"directive",type:Te.MatExpansionPanelDescription,selector:"mat-panel-description"},{kind:"directive",type:Te.MatExpansionPanelContent,selector:"ng-template[matExpansionPanelContent]"},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Ae.FileInputComponent,selector:"tb-file-input",inputs:["label","accept","noFileText","inputId","allowedExtensions","dropLabel","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Ge.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Wt,decorators:[{type:r,args:[{selector:"tb-credentials-config",providers:[{provide:D,useExisting:o((()=>Wt)),multi:!0},{provide:V,useExisting:o((()=>Wt)),multi:!0}],template:'
\n \n \n tb.rulenode.credentials\n \n {{ credentialsTypeTranslationsMap.get(credentialsConfigFormGroup.get(\'type\').value) | translate }}\n \n \n \n \n tb.rulenode.credentials-type\n \n \n {{ credentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.username\n \n \n {{ \'tb.rulenode.username-required\' | translate }}\n \n \n \n tb.rulenode.password\n \n \n \n {{ \'tb.rulenode.password-required\' | translate }}\n \n \n \n \n
{{ \'tb.rulenode.credentials-pem-hint\' | translate }}
\n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n
\n
\n
\n
\n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.FormBuilder}]},propDecorators:{required:[{type:i}],disableCertPemCredentials:[{type:i}],passwordFieldRequired:[{type:i}]}});class Xt{}e("RulenodeCoreConfigCommonModule",Xt),Xt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Xt,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Xt.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.9",ngImport:t,type:Xt,declarations:[wt,Jt,Qt,Yt,Wt,Ue,Ut,zt,_t],imports:[w,v,Ne],exports:[wt,Jt,Qt,Yt,Wt,Ue,Ut,zt,_t]}),Xt.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Xt,imports:[w,v,Ne]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Xt,decorators:[{type:l,args:[{declarations:[wt,Jt,Qt,Yt,Wt,Ue,Ut,zt,_t],imports:[w,v,Ne],exports:[wt,Jt,Qt,Yt,Wt,Ue,Ut,zt,_t]}]}]});class Zt{}e("RuleNodeCoreConfigActionModule",Zt),Zt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Zt,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Zt.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.9",ngImport:t,type:Zt,declarations:[Bt,je,Ht,Rt,At,ze,$e,Je,Qe,Et,Ye,Xe,Mt,Gt,Pt,Ot,Kt,_e,We,Vt,Dt,jt,$t],imports:[w,v,Ne,Xt],exports:[Bt,je,Ht,Rt,At,ze,$e,Je,Qe,Et,Ye,Xe,Mt,Gt,Pt,Ot,Kt,_e,We,Vt,Dt,jt,$t]}),Zt.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Zt,imports:[w,v,Ne,Xt]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Zt,decorators:[{type:l,args:[{declarations:[Bt,je,Ht,Rt,At,ze,$e,Je,Qe,Et,Ye,Xe,Mt,Gt,Pt,Ot,Kt,_e,We,Vt,Dt,jt,$t],imports:[w,v,Ne,Xt],exports:[Bt,je,Ht,Rt,At,ze,$e,Je,Qe,Et,Ye,Xe,Mt,Gt,Pt,Ot,Kt,_e,We,Vt,Dt,jt,$t]}]}]});class er extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.separatorKeysCodes=[ne,ae,oe]}configForm(){return this.calculateDeltaConfigForm}onConfigurationSet(e){this.calculateDeltaConfigForm=this.fb.group({inputValueKey:[e?e.inputValueKey:null,[G.required]],outputValueKey:[e?e.outputValueKey:null,[G.required]],useCache:[e?e.useCache:null,[]],addPeriodBetweenMsgs:[!!e&&e.addPeriodBetweenMsgs,[]],periodValueKey:[e?e.periodValueKey:null,[]],round:[e?e.round:null,[G.min(0),G.max(15)]],tellFailureIfDeltaIsNegative:[e?e.tellFailureIfDeltaIsNegative:null,[]]})}updateValidators(e){this.calculateDeltaConfigForm.get("addPeriodBetweenMsgs").value?this.calculateDeltaConfigForm.get("periodValueKey").setValidators([G.required]):this.calculateDeltaConfigForm.get("periodValueKey").setValidators([]),this.calculateDeltaConfigForm.get("periodValueKey").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["addPeriodBetweenMsgs"]}}e("CalculateDeltaConfigComponent",er),er.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:er,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),er.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:er,selector:"tb-enrichment-node-calculate-delta-config",usesInheritance:!0,ngImport:t,template:'
\n
\n \n tb.rulenode.input-value-key\n \n \n {{ \'tb.rulenode.input-value-key-required\' | translate }}\n \n \n \n tb.rulenode.output-value-key\n \n \n {{ \'tb.rulenode.output-value-key-required\' | translate }}\n \n \n \n tb.rulenode.round\n \n \n {{ \'tb.rulenode.round-range\' | translate }}\n \n \n {{ \'tb.rulenode.round-range\' | translate }}\n \n \n
\n \n {{ \'tb.rulenode.use-cache\' | translate }}\n \n \n {{ \'tb.rulenode.tell-failure-if-delta-is-negative\' | translate }}\n \n \n {{ \'tb.rulenode.add-period-between-msgs\' | translate }}\n \n \n tb.rulenode.period-value-key\n \n \n {{ \'tb.rulenode.period-value-key-required\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:er,decorators:[{type:r,args:[{selector:"tb-enrichment-node-calculate-delta-config",template:'
\n
\n \n tb.rulenode.input-value-key\n \n \n {{ \'tb.rulenode.input-value-key-required\' | translate }}\n \n \n \n tb.rulenode.output-value-key\n \n \n {{ \'tb.rulenode.output-value-key-required\' | translate }}\n \n \n \n tb.rulenode.round\n \n \n {{ \'tb.rulenode.round-range\' | translate }}\n \n \n {{ \'tb.rulenode.round-range\' | translate }}\n \n \n
\n \n {{ \'tb.rulenode.use-cache\' | translate }}\n \n \n {{ \'tb.rulenode.tell-failure-if-delta-is-negative\' | translate }}\n \n \n {{ \'tb.rulenode.add-period-between-msgs\' | translate }}\n \n \n tb.rulenode.period-value-key\n \n \n {{ \'tb.rulenode.period-value-key-required\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class tr extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.customerAttributesConfigForm}onConfigurationSet(e){this.customerAttributesConfigForm=this.fb.group({telemetry:[!!e&&e.telemetry,[]],attrMapping:[e?e.attrMapping:null,[G.required]]})}}e("CustomerAttributesConfigComponent",tr),tr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:tr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),tr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:tr,selector:"tb-enrichment-node-customer-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n {{ \'tb.rulenode.latest-telemetry\' | translate }}\n \n \n \n
\n',dependencies:[{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:wt,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:tr,decorators:[{type:r,args:[{selector:"tb-enrichment-node-customer-attributes-config",template:'
\n \n \n {{ \'tb.rulenode.latest-telemetry\' | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class rr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.separatorKeysCodes=[ne,ae,oe]}configForm(){return this.deviceAttributesConfigForm}onConfigurationSet(e){this.deviceAttributesConfigForm=this.fb.group({deviceRelationsQuery:[e?e.deviceRelationsQuery:null,[G.required]],tellFailureIfAbsent:[!!e&&e.tellFailureIfAbsent,[]],fetchToData:[!!e&&e.fetchToData,[]],clientAttributeNames:[e?e.clientAttributeNames:null,[]],sharedAttributeNames:[e?e.sharedAttributeNames:null,[]],serverAttributeNames:[e?e.serverAttributeNames:null,[]],latestTsKeyNames:[e?e.latestTsKeyNames:null,[]],getLatestValueWithTs:[!!e&&e.getLatestValueWithTs,[]]})}removeKey(e,t){const r=this.deviceAttributesConfigForm.get(t).value,n=r.indexOf(e);n>=0&&(r.splice(n,1),this.deviceAttributesConfigForm.get(t).setValue(r,{emitEvent:!0}))}addKey(e,t){const r=e.input;let n=e.value;if((n||"").trim()){n=n.trim();let e=this.deviceAttributesConfigForm.get(t).value;e&&-1!==e.indexOf(n)||(e||(e=[]),e.push(n),this.deviceAttributesConfigForm.get(t).setValue(e,{emitEvent:!0}))}r&&(r.value="")}prepareInputConfig(e){return W(e)&&X(e?.fetchToData)&&(e.fetchToData=!1),e}}e("DeviceAttributesConfigComponent",rr),rr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:rr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),rr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:rr,selector:"tb-enrichment-node-device-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n {{ \'tb.rulenode.tell-failure-if-absent\' | translate }}\n \n
tb.rulenode.tell-failure-if-absent-hint
\n
{{ \'tb.rulenode.fetch-into\' | translate }}
\n \n \n {{ \'tb.rulenode.data\' | translate }}\n \n \n {{ \'tb.rulenode.metadata\' | translate }}\n \n \n \n tb.rulenode.client-attributes\n \n \n {{key}}\n close\n \n \n \n \n \n tb.rulenode.shared-attributes\n \n \n {{key}}\n close\n \n \n \n \n \n tb.rulenode.server-attributes\n \n \n {{key}}\n close\n \n \n \n \n \n tb.rulenode.latest-timeseries\n \n \n {{key}}\n close\n \n \n \n \n \n {{ \'tb.rulenode.get-latest-value-with-ts\' | translate }}\n \n
\n
\n',styles:[":host label.tb-title{margin-bottom:-10px}\n"],dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:ie.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:Ee.MatRadioGroup,selector:"mat-radio-group",exportAs:["matRadioGroup"]},{kind:"component",type:Ee.MatRadioButton,selector:"mat-radio-button",inputs:["disableRipple","tabIndex"],exportAs:["matRadioButton"]},{kind:"component",type:le.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:le.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:le.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:le.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Jt,selector:"tb-device-relations-query-config",inputs:["disabled","required"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:rr,decorators:[{type:r,args:[{selector:"tb-enrichment-node-device-attributes-config",template:'
\n \n \n \n \n {{ \'tb.rulenode.tell-failure-if-absent\' | translate }}\n \n
tb.rulenode.tell-failure-if-absent-hint
\n
{{ \'tb.rulenode.fetch-into\' | translate }}
\n \n \n {{ \'tb.rulenode.data\' | translate }}\n \n \n {{ \'tb.rulenode.metadata\' | translate }}\n \n \n \n tb.rulenode.client-attributes\n \n \n {{key}}\n close\n \n \n \n \n \n tb.rulenode.shared-attributes\n \n \n {{key}}\n close\n \n \n \n \n \n tb.rulenode.server-attributes\n \n \n {{key}}\n close\n \n \n \n \n \n tb.rulenode.latest-timeseries\n \n \n {{key}}\n close\n \n \n \n \n \n {{ \'tb.rulenode.get-latest-value-with-ts\' | translate }}\n \n
\n
\n',styles:[":host label.tb-title{margin-bottom:-10px}\n"]}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class nr extends s{constructor(e,t,r){super(e),this.store=e,this.translate=t,this.fb=r,this.entityDetailsTranslationsMap=st,this.entityDetailsList=[],this.searchText="",this.displayDetailsFn=this.displayDetails.bind(this);for(const e of Object.keys(lt))this.entityDetailsList.push(lt[e]);this.detailsFormControl=new P(""),this.filteredEntityDetails=this.detailsFormControl.valueChanges.pipe(Ce(""),xe((e=>e||"")),be((e=>this.fetchEntityDetails(e))),Fe())}ngOnInit(){super.ngOnInit()}configForm(){return this.entityDetailsConfigForm}prepareInputConfig(e){return this.searchText="",this.detailsFormControl.patchValue("",{emitEvent:!0}),this.detailsList=e?e.detailsList:[],e}prepareOutputConfig(e){return e.detailsList=this.detailsList,e}onConfigurationSet(e){this.entityDetailsConfigForm=this.fb.group({detailsList:[e?e.detailsList:null,[G.required]],addToMetadata:[!!e&&e.addToMetadata,[]]}),this.detailsList=e?e.detailsList:[]}displayDetails(e){return e?this.translate.instant(st.get(e)):void 0}fetchEntityDetails(e){if(this.searchText=e,this.searchText&&this.searchText.length){const e=this.searchText.toUpperCase();return ke(this.entityDetailsList.filter((t=>this.translate.instant(st.get(lt[t])).toUpperCase().includes(e))))}return ke(this.entityDetailsList)}detailsFieldSelected(e){this.addDetailsField(e.option.value),this.clear("")}removeDetailsField(e){const t=this.detailsList.indexOf(e);t>=0&&(this.detailsList.splice(t,1),this.entityDetailsConfigForm.get("detailsList").setValue(this.detailsList))}addDetailsField(e){this.detailsList||(this.detailsList=[]);-1===this.detailsList.indexOf(e)&&(this.detailsList.push(e),this.entityDetailsConfigForm.get("detailsList").setValue(this.detailsList))}onEntityDetailsInputFocus(){this.detailsFormControl.updateValueAndValidity({onlySelf:!0,emitEvent:!0})}clear(e=""){this.detailsInput.nativeElement.value=e,this.detailsFormControl.patchValue(null,{emitEvent:!0}),setTimeout((()=>{this.detailsInput.nativeElement.blur(),this.detailsInput.nativeElement.focus()}),0)}}e("EntityDetailsConfigComponent",nr),nr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:nr,deps:[{token:M.Store},{token:U.TranslateService},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),nr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:nr,selector:"tb-enrichment-node-entity-details-config",viewQueries:[{propertyName:"detailsInput",first:!0,predicate:["detailsInput"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.entity-details\n \n \n \n {{entityDetailsTranslationsMap.get(details) | translate}}\n \n close\n \n \n \n \n \n \n \n \n
\n
\n tb.rulenode.no-entity-details-matching\n
\n
\n
\n
\n {{ \'tb.rulenode.entity-details-list-empty\' | translate }}\n
\n \n {{ \'tb.rulenode.add-to-metadata\' | translate }}\n \n
tb.rulenode.add-to-metadata-hint
\n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:ie.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:ve.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple","hideSingleSelectionIndicator"],exportAs:["matAutocomplete"]},{kind:"directive",type:ve.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{kind:"directive",type:ve.MatAutocompleteOrigin,selector:"[matAutocompleteOrigin]",exportAs:["matAutocompleteOrigin"]},{kind:"component",type:le.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:le.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:le.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:le.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:R.AsyncPipe,name:"async"},{kind:"pipe",type:Le.HighlightPipe,name:"highlight"},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:nr,decorators:[{type:r,args:[{selector:"tb-enrichment-node-entity-details-config",template:'
\n \n tb.rulenode.entity-details\n \n \n \n {{entityDetailsTranslationsMap.get(details) | translate}}\n \n close\n \n \n \n \n \n \n \n \n
\n
\n tb.rulenode.no-entity-details-matching\n
\n
\n
\n
\n {{ \'tb.rulenode.entity-details-list-empty\' | translate }}\n
\n \n {{ \'tb.rulenode.add-to-metadata\' | translate }}\n \n
tb.rulenode.add-to-metadata-hint
\n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:U.TranslateService},{type:A.UntypedFormBuilder}]},propDecorators:{detailsInput:[{type:a,args:["detailsInput",{static:!1}]}]}});class ar extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.separatorKeysCodes=[ne,ae,oe],this.aggregationTypes=L,this.aggregations=Object.keys(L),this.aggregationTypesTranslations=k,this.fetchMode=mt,this.fetchModes=Object.keys(mt),this.samplingOrders=Object.keys(pt),this.timeUnits=Object.values(nt),this.timeUnitsTranslationMap=at}configForm(){return this.getTelemetryFromDatabaseConfigForm}onConfigurationSet(e){this.getTelemetryFromDatabaseConfigForm=this.fb.group({latestTsKeyNames:[e?e.latestTsKeyNames:null,[]],aggregation:[e?e.aggregation:null,[G.required]],fetchMode:[e?e.fetchMode:null,[G.required]],orderBy:[e?e.orderBy:null,[]],limit:[e?e.limit:null,[]],useMetadataIntervalPatterns:[!!e&&e.useMetadataIntervalPatterns,[]],startInterval:[e?e.startInterval:null,[]],startIntervalTimeUnit:[e?e.startIntervalTimeUnit:null,[]],endInterval:[e?e.endInterval:null,[]],endIntervalTimeUnit:[e?e.endIntervalTimeUnit:null,[]],startIntervalPattern:[e?e.startIntervalPattern:null,[]],endIntervalPattern:[e?e.endIntervalPattern:null,[]]})}validatorTriggers(){return["fetchMode","useMetadataIntervalPatterns"]}updateValidators(e){const t=this.getTelemetryFromDatabaseConfigForm.get("fetchMode").value,r=this.getTelemetryFromDatabaseConfigForm.get("useMetadataIntervalPatterns").value;t&&t===mt.ALL?(this.getTelemetryFromDatabaseConfigForm.get("aggregation").setValidators([G.required]),this.getTelemetryFromDatabaseConfigForm.get("orderBy").setValidators([G.required]),this.getTelemetryFromDatabaseConfigForm.get("limit").setValidators([G.required,G.min(2),G.max(1e3)])):(this.getTelemetryFromDatabaseConfigForm.get("aggregation").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("orderBy").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("limit").setValidators([])),r?(this.getTelemetryFromDatabaseConfigForm.get("startInterval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalTimeUnit").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("endInterval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalTimeUnit").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").setValidators([G.required]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").setValidators([G.required])):(this.getTelemetryFromDatabaseConfigForm.get("startInterval").setValidators([G.required,G.min(1),G.max(2147483647)]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalTimeUnit").setValidators([G.required]),this.getTelemetryFromDatabaseConfigForm.get("endInterval").setValidators([G.required,G.min(1),G.max(2147483647)]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalTimeUnit").setValidators([G.required]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").setValidators([])),this.getTelemetryFromDatabaseConfigForm.get("aggregation").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("orderBy").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("limit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("startInterval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("startIntervalTimeUnit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("endInterval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("endIntervalTimeUnit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").updateValueAndValidity({emitEvent:e})}removeKey(e,t){const r=this.getTelemetryFromDatabaseConfigForm.get(t).value,n=r.indexOf(e);n>=0&&(r.splice(n,1),this.getTelemetryFromDatabaseConfigForm.get(t).setValue(r,{emitEvent:!0}))}addKey(e,t){const r=e.input;let n=e.value;if((n||"").trim()){n=n.trim();let e=this.getTelemetryFromDatabaseConfigForm.get(t).value;e&&-1!==e.indexOf(n)||(e||(e=[]),e.push(n),this.getTelemetryFromDatabaseConfigForm.get(t).setValue(e,{emitEvent:!0}))}r&&(r.value="")}}e("GetTelemetryFromDatabaseConfigComponent",ar),ar.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:ar,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),ar.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:ar,selector:"tb-enrichment-node-get-telemetry-from-database",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.timeseries-key\n \n \n {{key}}\n close\n \n \n \n \n \n \n tb.rulenode.fetch-mode\n \n \n {{ mode }}\n \n \n tb.rulenode.fetch-mode-hint\n \n
\n \n aggregation.function\n \n \n {{ aggregationTypesTranslations.get(aggregationTypes[aggregation]) | translate }}\n \n \n \n \n tb.rulenode.order-by\n \n \n {{ order }}\n \n \n tb.rulenode.order-by-hint\n \n \n tb.rulenode.limit\n \n tb.rulenode.limit-hint\n \n
\n \n {{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}\n \n
tb.rulenode.use-metadata-interval-patterns-hint
\n
\n
\n \n tb.rulenode.start-interval\n \n \n {{ \'tb.rulenode.start-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.start-interval-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n tb.rulenode.end-interval\n \n \n {{ \'tb.rulenode.end-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.end-interval-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n \n tb.rulenode.start-interval-pattern\n \n \n {{ \'tb.rulenode.start-interval-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.end-interval-pattern\n \n \n {{ \'tb.rulenode.end-interval-pattern-required\' | translate }}\n \n \n \n \n
\n',styles:[":host label.tb-title{margin-bottom:-10px}\n"],dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:ie.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:le.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:le.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:le.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:le.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:ar,decorators:[{type:r,args:[{selector:"tb-enrichment-node-get-telemetry-from-database",template:'
\n \n tb.rulenode.timeseries-key\n \n \n {{key}}\n close\n \n \n \n \n \n \n tb.rulenode.fetch-mode\n \n \n {{ mode }}\n \n \n tb.rulenode.fetch-mode-hint\n \n
\n \n aggregation.function\n \n \n {{ aggregationTypesTranslations.get(aggregationTypes[aggregation]) | translate }}\n \n \n \n \n tb.rulenode.order-by\n \n \n {{ order }}\n \n \n tb.rulenode.order-by-hint\n \n \n tb.rulenode.limit\n \n tb.rulenode.limit-hint\n \n
\n \n {{ \'tb.rulenode.use-metadata-interval-patterns\' | translate }}\n \n
tb.rulenode.use-metadata-interval-patterns-hint
\n
\n
\n \n tb.rulenode.start-interval\n \n \n {{ \'tb.rulenode.start-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.start-interval-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n tb.rulenode.end-interval\n \n \n {{ \'tb.rulenode.end-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.end-interval-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n \n tb.rulenode.start-interval-pattern\n \n \n {{ \'tb.rulenode.start-interval-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.end-interval-pattern\n \n \n {{ \'tb.rulenode.end-interval-pattern-required\' | translate }}\n \n \n \n \n
\n',styles:[":host label.tb-title{margin-bottom:-10px}\n"]}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class or extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.separatorKeysCodes=[ne,ae,oe]}configForm(){return this.originatorAttributesConfigForm}onConfigurationSet(e){this.originatorAttributesConfigForm=this.fb.group({tellFailureIfAbsent:[!!e&&e.tellFailureIfAbsent,[]],fetchToData:[!!Y(e?.fetchToData)&&e.fetchToData,[]],clientAttributeNames:[e?e.clientAttributeNames:null,[]],sharedAttributeNames:[e?e.sharedAttributeNames:null,[]],serverAttributeNames:[e?e.serverAttributeNames:null,[]],latestTsKeyNames:[e?e.latestTsKeyNames:null,[]],getLatestValueWithTs:[!!e&&e.getLatestValueWithTs,[]]})}removeKey(e,t){const r=this.originatorAttributesConfigForm.get(t).value,n=r.indexOf(e);n>=0&&(r.splice(n,1),this.originatorAttributesConfigForm.get(t).setValue(r,{emitEvent:!0}))}addKey(e,t){const r=e.input;let n=e.value;if((n||"").trim()){n=n.trim();let e=this.originatorAttributesConfigForm.get(t).value;e&&-1!==e.indexOf(n)||(e||(e=[]),e.push(n),this.originatorAttributesConfigForm.get(t).setValue(e,{emitEvent:!0}))}r&&(r.value="")}prepareInputConfig(e){return W(e)&&X(e?.fetchToData)&&(e.fetchToData=!1),e}}e("OriginatorAttributesConfigComponent",or),or.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:or,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),or.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:or,selector:"tb-enrichment-node-originator-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.tell-failure-if-absent\' | translate }}\n \n
tb.rulenode.tell-failure-if-absent-hint
\n
{{ \'tb.rulenode.fetch-into\' | translate }}
\n \n \n {{ \'tb.rulenode.data\' | translate }}\n \n \n {{ \'tb.rulenode.metadata\' | translate }}\n \n \n \n tb.rulenode.client-attributes\n \n \n {{key}}\n close\n \n \n \n \n \n \n tb.rulenode.shared-attributes\n \n \n {{key}}\n close\n \n \n \n \n \n \n tb.rulenode.server-attributes\n \n \n {{key}}\n close\n \n \n \n \n \n \n tb.rulenode.latest-timeseries\n \n \n {{key}}\n close\n \n \n \n \n \n \n {{ \'tb.rulenode.get-latest-value-with-ts\' | translate }}\n \n
\n
\n',styles:[":host label.tb-title{margin-bottom:-10px}\n"],dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:ie.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:Ee.MatRadioGroup,selector:"mat-radio-group",exportAs:["matRadioGroup"]},{kind:"component",type:Ee.MatRadioButton,selector:"mat-radio-button",inputs:["disableRipple","tabIndex"],exportAs:["matRadioButton"]},{kind:"component",type:le.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:le.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:le.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:le.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:or,decorators:[{type:r,args:[{selector:"tb-enrichment-node-originator-attributes-config",template:'
\n \n {{ \'tb.rulenode.tell-failure-if-absent\' | translate }}\n \n
tb.rulenode.tell-failure-if-absent-hint
\n
{{ \'tb.rulenode.fetch-into\' | translate }}
\n \n \n {{ \'tb.rulenode.data\' | translate }}\n \n \n {{ \'tb.rulenode.metadata\' | translate }}\n \n \n \n tb.rulenode.client-attributes\n \n \n {{key}}\n close\n \n \n \n \n \n \n tb.rulenode.shared-attributes\n \n \n {{key}}\n close\n \n \n \n \n \n \n tb.rulenode.server-attributes\n \n \n {{key}}\n close\n \n \n \n \n \n \n tb.rulenode.latest-timeseries\n \n \n {{key}}\n close\n \n \n \n \n \n \n {{ \'tb.rulenode.get-latest-value-with-ts\' | translate }}\n \n
\n
\n',styles:[":host label.tb-title{margin-bottom:-10px}\n"]}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class ir extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.originatorFieldsConfigForm}onConfigurationSet(e){this.originatorFieldsConfigForm=this.fb.group({fieldsMapping:[e?e.fieldsMapping:null,[G.required]],ignoreNullStrings:[e?e.ignoreNullStrings:null]})}}e("OriginatorFieldsConfigComponent",ir),ir.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:ir,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),ir.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:ir,selector:"tb-enrichment-node-originator-fields-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n {{ "tb.rulenode.ignore-null-strings" | translate }}\n
{{ "tb.rulenode.ignore-null-strings-hint" | translate }}
\n
\n',dependencies:[{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:wt,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:ir,decorators:[{type:r,args:[{selector:"tb-enrichment-node-originator-fields-config",template:'
\n \n \n \n {{ "tb.rulenode.ignore-null-strings" | translate }}\n
{{ "tb.rulenode.ignore-null-strings-hint" | translate }}
\n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class lr extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.relatedAttributesConfigForm}onConfigurationSet(e){this.relatedAttributesConfigForm=this.fb.group({relationsQuery:[e?e.relationsQuery:null,[G.required]],telemetry:[!!e&&e.telemetry,[]],attrMapping:[e?e.attrMapping:null,[G.required]]})}}e("RelatedAttributesConfigComponent",lr),lr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:lr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),lr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:lr,selector:"tb-enrichment-node-related-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n {{ \'tb.rulenode.latest-telemetry\' | translate }}\n \n \n \n
\n',dependencies:[{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:wt,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"component",type:Qt,selector:"tb-relations-query-config",inputs:["disabled","required"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:lr,decorators:[{type:r,args:[{selector:"tb-enrichment-node-related-attributes-config",template:'
\n \n \n \n \n \n {{ \'tb.rulenode.latest-telemetry\' | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class sr extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.tenantAttributesConfigForm}onConfigurationSet(e){this.tenantAttributesConfigForm=this.fb.group({telemetry:[!!e&&e.telemetry,[]],attrMapping:[e?e.attrMapping:null,[G.required]]})}}e("TenantAttributesConfigComponent",sr),sr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:sr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),sr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:sr,selector:"tb-enrichment-node-tenant-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n {{ \'tb.rulenode.latest-telemetry\' | translate }}\n \n \n \n
\n',dependencies:[{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:wt,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:sr,decorators:[{type:r,args:[{selector:"tb-enrichment-node-tenant-attributes-config",template:'
\n \n \n {{ \'tb.rulenode.latest-telemetry\' | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class mr extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.fetchDeviceCredentialsConfigForm}onConfigurationSet(e){this.fetchDeviceCredentialsConfigForm=this.fb.group({fetchToMetadata:[e?e.fetchToMetadata:null,[]]})}}e("FetchDeviceCredentialsConfigComponent",mr),mr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:mr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),mr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:mr,selector:"./tb-enrichment-node-fetch-device-credentials-config",usesInheritance:!0,ngImport:t,template:'
\n {{ \'tb.rulenode.fetch-credentials-to-metadata\' | translate }}\n
\n',dependencies:[{kind:"component",type:De.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:mr,decorators:[{type:r,args:[{selector:"./tb-enrichment-node-fetch-device-credentials-config",template:'
\n {{ \'tb.rulenode.fetch-credentials-to-metadata\' | translate }}\n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class ur{}e("RulenodeCoreConfigEnrichmentModule",ur),ur.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:ur,deps:[],target:t.ɵɵFactoryTarget.NgModule}),ur.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.9",ngImport:t,type:ur,declarations:[tr,nr,rr,or,ir,ar,lr,sr,er,mr],imports:[w,v,Xt],exports:[tr,nr,rr,or,ir,ar,lr,sr,er,mr]}),ur.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:ur,imports:[w,v,Xt]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:ur,decorators:[{type:l,args:[{declarations:[tr,nr,rr,or,ir,ar,lr,sr,er,mr],imports:[w,v,Xt],exports:[tr,nr,rr,or,ir,ar,lr,sr,er,mr]}]}]});class pr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.allAzureIotHubCredentialsTypes=yt,this.azureIotHubCredentialsTypeTranslationsMap=xt}configForm(){return this.azureIotHubConfigForm}onConfigurationSet(e){this.azureIotHubConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[G.required]],host:[e?e.host:null,[G.required]],port:[e?e.port:null,[G.required,G.min(1),G.max(65535)]],connectTimeoutSec:[e?e.connectTimeoutSec:null,[G.required,G.min(1),G.max(200)]],clientId:[e?e.clientId:null,[G.required]],cleanSession:[!!e&&e.cleanSession,[]],ssl:[!!e&&e.ssl,[]],credentials:this.fb.group({type:[e&&e.credentials?e.credentials.type:null,[G.required]],sasKey:[e&&e.credentials?e.credentials.sasKey:null,[]],caCert:[e&&e.credentials?e.credentials.caCert:null,[]],caCertFileName:[e&&e.credentials?e.credentials.caCertFileName:null,[]],privateKey:[e&&e.credentials?e.credentials.privateKey:null,[]],privateKeyFileName:[e&&e.credentials?e.credentials.privateKeyFileName:null,[]],cert:[e&&e.credentials?e.credentials.cert:null,[]],certFileName:[e&&e.credentials?e.credentials.certFileName:null,[]],password:[e&&e.credentials?e.credentials.password:null,[]]})})}prepareOutputConfig(e){const t=e.credentials.type;return"sas"===t&&(e.credentials={type:t,sasKey:e.credentials.sasKey,caCert:e.credentials.caCert,caCertFileName:e.credentials.caCertFileName}),e}validatorTriggers(){return["credentials.type"]}updateValidators(e){const t=this.azureIotHubConfigForm.get("credentials"),r=t.get("type").value;switch(e&&t.reset({type:r},{emitEvent:!1}),t.get("sasKey").setValidators([]),t.get("privateKey").setValidators([]),t.get("privateKeyFileName").setValidators([]),t.get("cert").setValidators([]),t.get("certFileName").setValidators([]),r){case"sas":t.get("sasKey").setValidators([G.required]);break;case"cert.PEM":t.get("privateKey").setValidators([G.required]),t.get("privateKeyFileName").setValidators([G.required]),t.get("cert").setValidators([G.required]),t.get("certFileName").setValidators([G.required])}t.get("sasKey").updateValueAndValidity({emitEvent:e}),t.get("privateKey").updateValueAndValidity({emitEvent:e}),t.get("privateKeyFileName").updateValueAndValidity({emitEvent:e}),t.get("cert").updateValueAndValidity({emitEvent:e}),t.get("certFileName").updateValueAndValidity({emitEvent:e})}}e("AzureIotHubConfigComponent",pr),pr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:pr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),pr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:pr,selector:"tb-external-node-azure-iot-hub-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic\n \n \n {{ \'tb.rulenode.topic-required\' | translate }}\n \n \n \n \n tb.rulenode.hostname\n \n \n {{ \'tb.rulenode.hostname-required\' | translate }}\n \n \n \n tb.rulenode.device-id\n \n \n {{ \'tb.rulenode.device-id-required\' | translate }}\n \n \n \n \n \n tb.rulenode.credentials\n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(azureIotHubConfigForm.get(\'credentials.type\').value) | translate }}\n \n \n
\n \n tb.rulenode.credentials-type\n \n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.sas-key\n \n \n \n {{ \'tb.rulenode.sas-key-required\' | translate }}\n \n \n \n \n \n \n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n \n
\n
\n
\n
\n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n"],dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:R.NgSwitch,selector:"[ngSwitch]",inputs:["ngSwitch"]},{kind:"directive",type:R.NgSwitchCase,selector:"[ngSwitchCase]",inputs:["ngSwitchCase"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:K.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:Te.MatAccordion,selector:"mat-accordion",inputs:["multi","hideToggle","displayMode","togglePosition"],exportAs:["matAccordion"]},{kind:"component",type:Te.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{kind:"component",type:Te.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{kind:"directive",type:Te.MatExpansionPanelTitle,selector:"mat-panel-title"},{kind:"directive",type:Te.MatExpansionPanelDescription,selector:"mat-panel-description"},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:A.FormGroupName,selector:"[formGroupName]",inputs:["formGroupName"]},{kind:"component",type:Ae.FileInputComponent,selector:"tb-file-input",inputs:["label","accept","noFileText","inputId","allowedExtensions","dropLabel","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Ge.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:pr,decorators:[{type:r,args:[{selector:"tb-external-node-azure-iot-hub-config",template:'
\n \n tb.rulenode.topic\n \n \n {{ \'tb.rulenode.topic-required\' | translate }}\n \n \n \n \n tb.rulenode.hostname\n \n \n {{ \'tb.rulenode.hostname-required\' | translate }}\n \n \n \n tb.rulenode.device-id\n \n \n {{ \'tb.rulenode.device-id-required\' | translate }}\n \n \n \n \n \n tb.rulenode.credentials\n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(azureIotHubConfigForm.get(\'credentials.type\').value) | translate }}\n \n \n
\n \n tb.rulenode.credentials-type\n \n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.sas-key\n \n \n \n {{ \'tb.rulenode.sas-key-required\' | translate }}\n \n \n \n \n \n \n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n \n
\n
\n
\n
\n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n"]}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class dr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.ackValues=["all","-1","0","1"],this.ToByteStandartCharsetTypesValues=ht,this.ToByteStandartCharsetTypeTranslationMap=Ct}configForm(){return this.kafkaConfigForm}onConfigurationSet(e){this.kafkaConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[G.required]],keyPattern:[e?e.keyPattern:null],bootstrapServers:[e?e.bootstrapServers:null,[G.required]],retries:[e?e.retries:null,[G.min(0)]],batchSize:[e?e.batchSize:null,[G.min(0)]],linger:[e?e.linger:null,[G.min(0)]],bufferMemory:[e?e.bufferMemory:null,[G.min(0)]],acks:[e?e.acks:null,[G.required]],keySerializer:[e?e.keySerializer:null,[G.required]],valueSerializer:[e?e.valueSerializer:null,[G.required]],otherProperties:[e?e.otherProperties:null,[]],addMetadataKeyValuesAsKafkaHeaders:[!!e&&e.addMetadataKeyValuesAsKafkaHeaders,[]],kafkaHeadersCharset:[e?e.kafkaHeadersCharset:null,[]]})}validatorTriggers(){return["addMetadataKeyValuesAsKafkaHeaders"]}updateValidators(e){this.kafkaConfigForm.get("addMetadataKeyValuesAsKafkaHeaders").value?this.kafkaConfigForm.get("kafkaHeadersCharset").setValidators([G.required]):this.kafkaConfigForm.get("kafkaHeadersCharset").setValidators([]),this.kafkaConfigForm.get("kafkaHeadersCharset").updateValueAndValidity({emitEvent:e})}}e("KafkaConfigComponent",dr),dr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:dr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),dr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:dr,selector:"tb-external-node-kafka-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.key-pattern\n \n \n \n
tb.rulenode.key-pattern-hint
\n \n tb.rulenode.bootstrap-servers\n \n \n {{ \'tb.rulenode.bootstrap-servers-required\' | translate }}\n \n \n \n tb.rulenode.retries\n \n \n {{ \'tb.rulenode.min-retries-message\' | translate }}\n \n \n \n tb.rulenode.batch-size-bytes\n \n \n {{ \'tb.rulenode.min-batch-size-bytes-message\' | translate }}\n \n \n \n tb.rulenode.linger-ms\n \n \n {{ \'tb.rulenode.min-linger-ms-message\' | translate }}\n \n \n \n tb.rulenode.buffer-memory-bytes\n \n \n {{ \'tb.rulenode.min-buffer-memory-bytes-message\' | translate }}\n \n \n \n tb.rulenode.acks\n \n \n {{ ackValue }}\n \n \n \n \n tb.rulenode.key-serializer\n \n \n {{ \'tb.rulenode.key-serializer-required\' | translate }}\n \n \n \n tb.rulenode.value-serializer\n \n \n {{ \'tb.rulenode.value-serializer-required\' | translate }}\n \n \n \n \n \n \n {{ \'tb.rulenode.add-metadata-key-values-as-kafka-headers\' | translate }}\n \n
tb.rulenode.add-metadata-key-values-as-kafka-headers-hint
\n \n tb.rulenode.charset-encoding\n \n \n {{ ToByteStandartCharsetTypeTranslationMap.get(charset) | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:wt,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:dr,decorators:[{type:r,args:[{selector:"tb-external-node-kafka-config",template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.key-pattern\n \n \n \n
tb.rulenode.key-pattern-hint
\n \n tb.rulenode.bootstrap-servers\n \n \n {{ \'tb.rulenode.bootstrap-servers-required\' | translate }}\n \n \n \n tb.rulenode.retries\n \n \n {{ \'tb.rulenode.min-retries-message\' | translate }}\n \n \n \n tb.rulenode.batch-size-bytes\n \n \n {{ \'tb.rulenode.min-batch-size-bytes-message\' | translate }}\n \n \n \n tb.rulenode.linger-ms\n \n \n {{ \'tb.rulenode.min-linger-ms-message\' | translate }}\n \n \n \n tb.rulenode.buffer-memory-bytes\n \n \n {{ \'tb.rulenode.min-buffer-memory-bytes-message\' | translate }}\n \n \n \n tb.rulenode.acks\n \n \n {{ ackValue }}\n \n \n \n \n tb.rulenode.key-serializer\n \n \n {{ \'tb.rulenode.key-serializer-required\' | translate }}\n \n \n \n tb.rulenode.value-serializer\n \n \n {{ \'tb.rulenode.value-serializer-required\' | translate }}\n \n \n \n \n \n \n {{ \'tb.rulenode.add-metadata-key-values-as-kafka-headers\' | translate }}\n \n
tb.rulenode.add-metadata-key-values-as-kafka-headers-hint
\n \n tb.rulenode.charset-encoding\n \n \n {{ ToByteStandartCharsetTypeTranslationMap.get(charset) | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class cr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.subscriptions=[]}configForm(){return this.mqttConfigForm}onConfigurationSet(e){this.mqttConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[G.required]],host:[e?e.host:null,[G.required]],port:[e?e.port:null,[G.required,G.min(1),G.max(65535)]],connectTimeoutSec:[e?e.connectTimeoutSec:null,[G.required,G.min(1),G.max(200)]],clientId:[e?e.clientId:null,[]],appendClientIdSuffix:[{value:!!e&&e.appendClientIdSuffix,disabled:!(e&&Z(e.clientId))},[]],cleanSession:[!!e&&e.cleanSession,[]],retainedMessage:[!!e&&e.retainedMessage,[]],ssl:[!!e&&e.ssl,[]],credentials:[e?e.credentials:null,[]]}),this.subscriptions.push(this.mqttConfigForm.get("clientId").valueChanges.subscribe((e=>{Z(e)?this.mqttConfigForm.get("appendClientIdSuffix").enable({emitEvent:!1}):this.mqttConfigForm.get("appendClientIdSuffix").disable({emitEvent:!1})})))}ngOnDestroy(){this.subscriptions.forEach((e=>e.unsubscribe()))}}e("MqttConfigComponent",cr),cr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:cr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),cr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:cr,selector:"tb-external-node-mqtt-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n \n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n \n tb.rulenode.connect-timeout\n \n \n {{ \'tb.rulenode.connect-timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n
\n \n tb.rulenode.client-id\n \n {{\'tb.rulenode.client-id-hint\' | translate}}\n \n \n {{ \'tb.rulenode.append-client-id-suffix\' | translate }}\n \n
{{ "tb.rulenode.client-id-suffix-hint" | translate }}
\n \n {{ \'tb.rulenode.clean-session\' | translate }}\n \n \n {{ "tb.rulenode.retained-message" | translate }}\n \n \n {{ \'tb.rulenode.enable-ssl\' | translate }}\n \n \n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n"],dependencies:[{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Wt,selector:"tb-credentials-config",inputs:["required","disableCertPemCredentials","passwordFieldRequired"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:cr,decorators:[{type:r,args:[{selector:"tb-external-node-mqtt-config",template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n \n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n \n tb.rulenode.connect-timeout\n \n \n {{ \'tb.rulenode.connect-timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n
\n \n tb.rulenode.client-id\n \n {{\'tb.rulenode.client-id-hint\' | translate}}\n \n \n {{ \'tb.rulenode.append-client-id-suffix\' | translate }}\n \n
{{ "tb.rulenode.client-id-suffix-hint" | translate }}
\n \n {{ \'tb.rulenode.clean-session\' | translate }}\n \n \n {{ "tb.rulenode.retained-message" | translate }}\n \n \n {{ \'tb.rulenode.enable-ssl\' | translate }}\n \n \n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n"]}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class fr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.notificationType=I,this.entityType=x}configForm(){return this.notificationConfigForm}onConfigurationSet(e){this.notificationConfigForm=this.fb.group({templateId:[e?e.templateId:null,[G.required]],targets:[e?e.targets:[],[G.required]]})}}e("NotificationConfigComponent",fr),fr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:fr,deps:[{token:M.Store},{token:A.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),fr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:fr,selector:"tb-external-node-notification-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n
\n',dependencies:[{kind:"component",type:Ve.EntityListComponent,selector:"tb-entity-list",inputs:["entityType","subType","labelText","placeholderText","requiredText","required","disabled","subscriptSizing","hint"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Pe.TemplateAutocompleteComponent,selector:"tb-template-autocomplete",inputs:["required","allowCreate","disabled","notificationTypes"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:fr,decorators:[{type:r,args:[{selector:"tb-external-node-notification-config",template:'
\n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.FormBuilder}]}});class gr extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.pubSubConfigForm}onConfigurationSet(e){this.pubSubConfigForm=this.fb.group({projectId:[e?e.projectId:null,[G.required]],topicName:[e?e.topicName:null,[G.required]],serviceAccountKey:[e?e.serviceAccountKey:null,[G.required]],serviceAccountKeyFileName:[e?e.serviceAccountKeyFileName:null,[G.required]],messageAttributes:[e?e.messageAttributes:null,[]]})}}e("PubSubConfigComponent",gr),gr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:gr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),gr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:gr,selector:"tb-external-node-pub-sub-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.gcp-project-id\n \n \n {{ \'tb.rulenode.gcp-project-id-required\' | translate }}\n \n \n \n tb.rulenode.pubsub-topic-name\n \n \n {{ \'tb.rulenode.pubsub-topic-name-required\' | translate }}\n \n \n \n \n \n
\n \n \n
\n',dependencies:[{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Ae.FileInputComponent,selector:"tb-file-input",inputs:["label","accept","noFileText","inputId","allowedExtensions","dropLabel","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:wt,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:gr,decorators:[{type:r,args:[{selector:"tb-external-node-pub-sub-config",template:'
\n \n tb.rulenode.gcp-project-id\n \n \n {{ \'tb.rulenode.gcp-project-id-required\' | translate }}\n \n \n \n tb.rulenode.pubsub-topic-name\n \n \n {{ \'tb.rulenode.pubsub-topic-name-required\' | translate }}\n \n \n \n \n \n
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class yr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"]}configForm(){return this.rabbitMqConfigForm}onConfigurationSet(e){this.rabbitMqConfigForm=this.fb.group({exchangeNamePattern:[e?e.exchangeNamePattern:null,[]],routingKeyPattern:[e?e.routingKeyPattern:null,[]],messageProperties:[e?e.messageProperties:null,[]],host:[e?e.host:null,[G.required]],port:[e?e.port:null,[G.required,G.min(1),G.max(65535)]],virtualHost:[e?e.virtualHost:null,[]],username:[e?e.username:null,[]],password:[e?e.password:null,[]],automaticRecoveryEnabled:[!!e&&e.automaticRecoveryEnabled,[]],connectionTimeout:[e?e.connectionTimeout:null,[G.min(0)]],handshakeTimeout:[e?e.handshakeTimeout:null,[G.min(0)]],clientProperties:[e?e.clientProperties:null,[]]})}}e("RabbitMqConfigComponent",yr),yr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:yr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),yr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:yr,selector:"tb-external-node-rabbit-mq-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.exchange-name-pattern\n \n \n \n tb.rulenode.routing-key-pattern\n \n \n \n tb.rulenode.message-properties\n \n \n {{ property }}\n \n \n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n
\n \n tb.rulenode.virtual-host\n \n \n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n \n {{ \'tb.rulenode.automatic-recovery\' | translate }}\n \n \n tb.rulenode.connection-timeout-ms\n \n \n {{ \'tb.rulenode.min-connection-timeout-ms-message\' | translate }}\n \n \n \n tb.rulenode.handshake-timeout-ms\n \n \n {{ \'tb.rulenode.min-handshake-timeout-ms-message\' | translate }}\n \n \n \n \n \n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:K.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Ge.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"component",type:wt,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:yr,decorators:[{type:r,args:[{selector:"tb-external-node-rabbit-mq-config",template:'
\n \n tb.rulenode.exchange-name-pattern\n \n \n \n tb.rulenode.routing-key-pattern\n \n \n \n tb.rulenode.message-properties\n \n \n {{ property }}\n \n \n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n
\n \n tb.rulenode.virtual-host\n \n \n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n \n {{ \'tb.rulenode.automatic-recovery\' | translate }}\n \n \n tb.rulenode.connection-timeout-ms\n \n \n {{ \'tb.rulenode.min-connection-timeout-ms-message\' | translate }}\n \n \n \n tb.rulenode.handshake-timeout-ms\n \n \n {{ \'tb.rulenode.min-handshake-timeout-ms-message\' | translate }}\n \n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class xr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.proxySchemes=["http","https"],this.httpRequestTypes=Object.keys(bt)}configForm(){return this.restApiCallConfigForm}onConfigurationSet(e){this.restApiCallConfigForm=this.fb.group({restEndpointUrlPattern:[e?e.restEndpointUrlPattern:null,[G.required]],requestMethod:[e?e.requestMethod:null,[G.required]],useSimpleClientHttpFactory:[!!e&&e.useSimpleClientHttpFactory,[]],trimDoubleQuotes:[!!e&&e.trimDoubleQuotes,[]],ignoreRequestBody:[!!e&&e.ignoreRequestBody,[]],enableProxy:[!!e&&e.enableProxy,[]],useSystemProxyProperties:[!!e&&e.enableProxy,[]],proxyScheme:[e?e.proxyHost:null,[]],proxyHost:[e?e.proxyHost:null,[]],proxyPort:[e?e.proxyPort:null,[]],proxyUser:[e?e.proxyUser:null,[]],proxyPassword:[e?e.proxyPassword:null,[]],readTimeoutMs:[e?e.readTimeoutMs:null,[]],maxParallelRequestsCount:[e?e.maxParallelRequestsCount:null,[G.min(0)]],headers:[e?e.headers:null,[]],useRedisQueueForMsgPersistence:[!!e&&e.useRedisQueueForMsgPersistence,[]],trimQueue:[!!e&&e.trimQueue,[]],maxQueueSize:[e?e.maxQueueSize:null,[]],credentials:[e?e.credentials:null,[]]})}validatorTriggers(){return["useSimpleClientHttpFactory","useRedisQueueForMsgPersistence","enableProxy","useSystemProxyProperties"]}updateValidators(e){const t=this.restApiCallConfigForm.get("useSimpleClientHttpFactory").value,r=this.restApiCallConfigForm.get("useRedisQueueForMsgPersistence").value,n=this.restApiCallConfigForm.get("enableProxy").value,a=this.restApiCallConfigForm.get("useSystemProxyProperties").value;n&&!a?(this.restApiCallConfigForm.get("proxyHost").setValidators(n?[G.required]:[]),this.restApiCallConfigForm.get("proxyPort").setValidators(n?[G.required,G.min(1),G.max(65535)]:[])):(this.restApiCallConfigForm.get("proxyHost").setValidators([]),this.restApiCallConfigForm.get("proxyPort").setValidators([]),t?this.restApiCallConfigForm.get("readTimeoutMs").setValidators([]):this.restApiCallConfigForm.get("readTimeoutMs").setValidators([G.min(0)])),r?this.restApiCallConfigForm.get("maxQueueSize").setValidators([G.min(0)]):this.restApiCallConfigForm.get("maxQueueSize").setValidators([]),this.restApiCallConfigForm.get("readTimeoutMs").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("maxQueueSize").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("proxyHost").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("proxyPort").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("credentials").updateValueAndValidity({emitEvent:e})}}e("RestApiCallConfigComponent",xr),xr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:xr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),xr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:xr,selector:"tb-external-node-rest-api-call-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.endpoint-url-pattern\n \n \n {{ \'tb.rulenode.endpoint-url-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.request-method\n \n \n {{ requestType }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n \n {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}\n \n \n {{ \'tb.rulenode.trim-double-quotes\' | translate }}\n \n
tb.rulenode.trim-double-quotes-hint
\n \n {{ \'tb.rulenode.ignore-request-body\' | translate }}\n \n
\n \n {{ \'tb.rulenode.use-system-proxy-properties\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-scheme\n \n \n {{ proxyScheme }}\n \n \n \n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n
\n \n tb.rulenode.read-timeout\n \n tb.rulenode.read-timeout-hint\n \n \n tb.rulenode.max-parallel-requests-count\n \n tb.rulenode.max-parallel-requests-count-hint\n \n \n
\n \n \n \n {{ \'tb.rulenode.use-redis-queue\' | translate }}\n \n
\n \n {{ \'tb.rulenode.trim-redis-queue\' | translate }}\n \n \n tb.rulenode.redis-queue-max-size\n \n \n
\n \n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:wt,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"component",type:Wt,selector:"tb-credentials-config",inputs:["required","disableCertPemCredentials","passwordFieldRequired"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:xr,decorators:[{type:r,args:[{selector:"tb-external-node-rest-api-call-config",template:'
\n \n tb.rulenode.endpoint-url-pattern\n \n \n {{ \'tb.rulenode.endpoint-url-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.request-method\n \n \n {{ requestType }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n \n {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}\n \n \n {{ \'tb.rulenode.trim-double-quotes\' | translate }}\n \n
tb.rulenode.trim-double-quotes-hint
\n \n {{ \'tb.rulenode.ignore-request-body\' | translate }}\n \n
\n \n {{ \'tb.rulenode.use-system-proxy-properties\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-scheme\n \n \n {{ proxyScheme }}\n \n \n \n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n
\n \n tb.rulenode.read-timeout\n \n tb.rulenode.read-timeout-hint\n \n \n tb.rulenode.max-parallel-requests-count\n \n tb.rulenode.max-parallel-requests-count-hint\n \n \n
\n \n \n \n {{ \'tb.rulenode.use-redis-queue\' | translate }}\n \n
\n \n {{ \'tb.rulenode.trim-redis-queue\' | translate }}\n \n \n tb.rulenode.redis-queue-max-size\n \n \n
\n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class br extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.smtpProtocols=["smtp","smtps"],this.tlsVersions=["TLSv1","TLSv1.1","TLSv1.2","TLSv1.3"]}configForm(){return this.sendEmailConfigForm}onConfigurationSet(e){this.sendEmailConfigForm=this.fb.group({useSystemSmtpSettings:[!!e&&e.useSystemSmtpSettings,[]],smtpProtocol:[e?e.smtpProtocol:null,[]],smtpHost:[e?e.smtpHost:null,[]],smtpPort:[e?e.smtpPort:null,[]],timeout:[e?e.timeout:null,[]],enableTls:[!!e&&e.enableTls,[]],tlsVersion:[e?e.tlsVersion:null,[]],enableProxy:[!!e&&e.enableProxy,[]],proxyHost:[e?e.proxyHost:null,[]],proxyPort:[e?e.proxyPort:null,[]],proxyUser:[e?e.proxyUser:null,[]],proxyPassword:[e?e.proxyPassword:null,[]],username:[e?e.username:null,[]],password:[e?e.password:null,[]]})}validatorTriggers(){return["useSystemSmtpSettings","enableProxy"]}updateValidators(e){const t=this.sendEmailConfigForm.get("useSystemSmtpSettings").value,r=this.sendEmailConfigForm.get("enableProxy").value;t?(this.sendEmailConfigForm.get("smtpProtocol").setValidators([]),this.sendEmailConfigForm.get("smtpHost").setValidators([]),this.sendEmailConfigForm.get("smtpPort").setValidators([]),this.sendEmailConfigForm.get("timeout").setValidators([]),this.sendEmailConfigForm.get("proxyHost").setValidators([]),this.sendEmailConfigForm.get("proxyPort").setValidators([])):(this.sendEmailConfigForm.get("smtpProtocol").setValidators([G.required]),this.sendEmailConfigForm.get("smtpHost").setValidators([G.required]),this.sendEmailConfigForm.get("smtpPort").setValidators([G.required,G.min(1),G.max(65535)]),this.sendEmailConfigForm.get("timeout").setValidators([G.required,G.min(0)]),this.sendEmailConfigForm.get("proxyHost").setValidators(r?[G.required]:[]),this.sendEmailConfigForm.get("proxyPort").setValidators(r?[G.required,G.min(1),G.max(65535)]:[])),this.sendEmailConfigForm.get("smtpProtocol").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("smtpHost").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("smtpPort").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("timeout").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("proxyHost").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("proxyPort").updateValueAndValidity({emitEvent:e})}}e("SendEmailConfigComponent",br),br.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:br,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),br.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:br,selector:"tb-external-node-send-email-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}\n \n
\n \n tb.rulenode.smtp-protocol\n \n \n {{ smtpProtocol.toUpperCase() }}\n \n \n \n
\n \n tb.rulenode.smtp-host\n \n \n {{ \'tb.rulenode.smtp-host-required\' | translate }}\n \n \n \n tb.rulenode.smtp-port\n \n \n {{ \'tb.rulenode.smtp-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.timeout-msec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-msec-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.enable-tls\' | translate }}\n \n \n tb.rulenode.tls-version\n \n \n {{ tlsVersion }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n
\n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Re.TbCheckboxComponent,selector:"tb-checkbox",inputs:["disabled","trueValue","falseValue"],outputs:["valueChange"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:K.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Ge.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:br,decorators:[{type:r,args:[{selector:"tb-external-node-send-email-config",template:'
\n \n {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}\n \n
\n \n tb.rulenode.smtp-protocol\n \n \n {{ smtpProtocol.toUpperCase() }}\n \n \n \n
\n \n tb.rulenode.smtp-host\n \n \n {{ \'tb.rulenode.smtp-host-required\' | translate }}\n \n \n \n tb.rulenode.smtp-port\n \n \n {{ \'tb.rulenode.smtp-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.timeout-msec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-msec-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.enable-tls\' | translate }}\n \n \n tb.rulenode.tls-version\n \n \n {{ tlsVersion }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n
\n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class hr extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.sendSmsConfigForm}onConfigurationSet(e){this.sendSmsConfigForm=this.fb.group({numbersToTemplate:[e?e.numbersToTemplate:null,[G.required]],smsMessageTemplate:[e?e.smsMessageTemplate:null,[G.required]],useSystemSmsSettings:[!!e&&e.useSystemSmsSettings,[]],smsProviderConfiguration:[e?e.smsProviderConfiguration:null,[]]})}validatorTriggers(){return["useSystemSmsSettings"]}updateValidators(e){this.sendSmsConfigForm.get("useSystemSmsSettings").value?this.sendSmsConfigForm.get("smsProviderConfiguration").setValidators([]):this.sendSmsConfigForm.get("smsProviderConfiguration").setValidators([G.required]),this.sendSmsConfigForm.get("smsProviderConfiguration").updateValueAndValidity({emitEvent:e})}}e("SendSmsConfigComponent",hr),hr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:hr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),hr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:hr,selector:"tb-external-node-send-sms-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.numbers-to-template\n \n \n {{ \'tb.rulenode.numbers-to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.sms-message-template\n \n \n {{ \'tb.rulenode.sms-message-template-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.use-system-sms-settings\' | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:we.SmsProviderConfigurationComponent,selector:"tb-sms-provider-configuration",inputs:["required","disabled"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:hr,decorators:[{type:r,args:[{selector:"tb-external-node-send-sms-config",template:'
\n \n tb.rulenode.numbers-to-template\n \n \n {{ \'tb.rulenode.numbers-to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.sms-message-template\n \n \n {{ \'tb.rulenode.sms-message-template-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.use-system-sms-settings\' | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Cr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.slackChanelTypes=Object.keys(T),this.slackChanelTypesTranslateMap=N}configForm(){return this.slackConfigForm}onConfigurationSet(e){this.slackConfigForm=this.fb.group({botToken:[e?e.botToken:null],useSystemSettings:[!!e&&e.useSystemSettings],messageTemplate:[e?e.messageTemplate:null,[G.required]],conversationType:[e?e.conversationType:null,[G.required]],conversation:[e?e.conversation:null,[G.required]]})}validatorTriggers(){return["useSystemSettings"]}updateValidators(e){this.slackConfigForm.get("useSystemSettings").value?this.slackConfigForm.get("botToken").clearValidators():this.slackConfigForm.get("botToken").setValidators([G.required]),this.slackConfigForm.get("botToken").updateValueAndValidity({emitEvent:e})}}e("SlackConfigComponent",Cr),Cr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Cr,deps:[{token:M.Store},{token:A.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Cr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Cr,selector:"tb-external-node-slack-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.message-template\n \n \n {{ \'tb.rulenode.message-template-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.use-system-slack-settings\' | translate }}\n \n \n tb.rulenode.slack-api-token\n \n \n {{ \'tb.rulenode.slack-api-token-required\' | translate }}\n \n \n \n \n \n {{ slackChanelTypesTranslateMap.get(slackChanelType) | translate }}\n \n \n \n \n
\n',styles:[":host .tb-title{display:block;padding-bottom:6px}:host ::ng-deep .mat-mdc-radio-group{display:flex;flex-direction:row;margin-bottom:22px;gap:12px}:host ::ng-deep .mat-mdc-radio-group .mat-mdc-radio-button{flex:1 1 100%;padding:4px;border:1px solid rgba(0,0,0,.12);border-radius:6px}@media screen and (max-width: 599px){:host ::ng-deep .mat-mdc-radio-group{flex-direction:column}}\n"],dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Ee.MatRadioGroup,selector:"mat-radio-group",exportAs:["matRadioGroup"]},{kind:"component",type:Ee.MatRadioButton,selector:"mat-radio-button",inputs:["disableRipple","tabIndex"],exportAs:["matRadioButton"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Oe.SlackConversationAutocompleteComponent,selector:"tb-slack-conversation-autocomplete",inputs:["labelText","requiredText","required","disabled","slackChanelType","token"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Cr,decorators:[{type:r,args:[{selector:"tb-external-node-slack-config",template:'
\n \n tb.rulenode.message-template\n \n \n {{ \'tb.rulenode.message-template-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.use-system-slack-settings\' | translate }}\n \n \n tb.rulenode.slack-api-token\n \n \n {{ \'tb.rulenode.slack-api-token-required\' | translate }}\n \n \n \n \n \n {{ slackChanelTypesTranslateMap.get(slackChanelType) | translate }}\n \n \n \n \n
\n',styles:[":host .tb-title{display:block;padding-bottom:6px}:host ::ng-deep .mat-mdc-radio-group{display:flex;flex-direction:row;margin-bottom:22px;gap:12px}:host ::ng-deep .mat-mdc-radio-group .mat-mdc-radio-button{flex:1 1 100%;padding:4px;border:1px solid rgba(0,0,0,.12);border-radius:6px}@media screen and (max-width: 599px){:host ::ng-deep .mat-mdc-radio-group{flex-direction:column}}\n"]}]}],ctorParameters:function(){return[{type:M.Store},{type:A.FormBuilder}]}});class Fr extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.snsConfigForm}onConfigurationSet(e){this.snsConfigForm=this.fb.group({topicArnPattern:[e?e.topicArnPattern:null,[G.required]],accessKeyId:[e?e.accessKeyId:null,[G.required]],secretAccessKey:[e?e.secretAccessKey:null,[G.required]],region:[e?e.region:null,[G.required]]})}}e("SnsConfigComponent",Fr),Fr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Fr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Fr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Fr,selector:"tb-external-node-sns-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-arn-pattern\n \n \n {{ \'tb.rulenode.topic-arn-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Fr,decorators:[{type:r,args:[{selector:"tb-external-node-sns-config",template:'
\n \n tb.rulenode.topic-arn-pattern\n \n \n {{ \'tb.rulenode.topic-arn-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class vr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.sqsQueueType=dt,this.sqsQueueTypes=Object.keys(dt),this.sqsQueueTypeTranslationsMap=ct}configForm(){return this.sqsConfigForm}onConfigurationSet(e){this.sqsConfigForm=this.fb.group({queueType:[e?e.queueType:null,[G.required]],queueUrlPattern:[e?e.queueUrlPattern:null,[G.required]],delaySeconds:[e?e.delaySeconds:null,[G.min(0),G.max(900)]],messageAttributes:[e?e.messageAttributes:null,[]],accessKeyId:[e?e.accessKeyId:null,[G.required]],secretAccessKey:[e?e.secretAccessKey:null,[G.required]],region:[e?e.region:null,[G.required]]})}}e("SqsConfigComponent",vr),vr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:vr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),vr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:vr,selector:"tb-external-node-sqs-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.queue-type\n \n \n {{ sqsQueueTypeTranslationsMap.get(type) | translate }}\n \n \n \n \n tb.rulenode.queue-url-pattern\n \n \n {{ \'tb.rulenode.queue-url-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.delay-seconds\n \n \n {{ \'tb.rulenode.min-delay-seconds-message\' | translate }}\n \n \n {{ \'tb.rulenode.max-delay-seconds-message\' | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:wt,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:vr,decorators:[{type:r,args:[{selector:"tb-external-node-sqs-config",template:'
\n \n tb.rulenode.queue-type\n \n \n {{ sqsQueueTypeTranslationsMap.get(type) | translate }}\n \n \n \n \n tb.rulenode.queue-url-pattern\n \n \n {{ \'tb.rulenode.queue-url-pattern-required\' | translate }}\n \n \n \n \n tb.rulenode.delay-seconds\n \n \n {{ \'tb.rulenode.min-delay-seconds-message\' | translate }}\n \n \n {{ \'tb.rulenode.max-delay-seconds-message\' | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Lr{}e("RulenodeCoreConfigExternalModule",Lr),Lr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Lr,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Lr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.9",ngImport:t,type:Lr,declarations:[Fr,vr,gr,dr,cr,fr,yr,xr,br,pr,hr,Cr],imports:[w,v,Ne,Xt],exports:[Fr,vr,gr,dr,cr,fr,yr,xr,br,pr,hr,Cr]}),Lr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Lr,imports:[w,v,Ne,Xt]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Lr,decorators:[{type:l,args:[{declarations:[Fr,vr,gr,dr,cr,fr,yr,xr,br,pr,hr,Cr],imports:[w,v,Ne,Xt],exports:[Fr,vr,gr,dr,cr,fr,yr,xr,br,pr,hr,Cr]}]}]});class kr extends s{constructor(e,t,r){super(e),this.store=e,this.translate=t,this.fb=r,this.alarmStatusTranslationsMap=q,this.alarmStatusList=[],this.searchText="",this.displayStatusFn=this.displayStatus.bind(this);for(const e of Object.keys(S))this.alarmStatusList.push(S[e]);this.statusFormControl=new P(""),this.filteredAlarmStatus=this.statusFormControl.valueChanges.pipe(Ce(""),xe((e=>e||"")),be((e=>this.fetchAlarmStatus(e))),Fe())}ngOnInit(){super.ngOnInit()}configForm(){return this.alarmStatusConfigForm}prepareInputConfig(e){return this.searchText="",this.statusFormControl.patchValue("",{emitEvent:!0}),e}onConfigurationSet(e){this.alarmStatusConfigForm=this.fb.group({alarmStatusList:[e?e.alarmStatusList:null,[G.required]]})}displayStatus(e){return e?this.translate.instant(q.get(e)):void 0}fetchAlarmStatus(e){const t=this.getAlarmStatusList();if(this.searchText=e,this.searchText&&this.searchText.length){const e=this.searchText.toUpperCase();return ke(t.filter((t=>this.translate.instant(q.get(S[t])).toUpperCase().includes(e))))}return ke(t)}alarmStatusSelected(e){this.addAlarmStatus(e.option.value),this.clear("")}removeAlarmStatus(e){const t=this.alarmStatusConfigForm.get("alarmStatusList").value;if(t){const r=t.indexOf(e);r>=0&&(t.splice(r,1),this.alarmStatusConfigForm.get("alarmStatusList").setValue(t))}}addAlarmStatus(e){let t=this.alarmStatusConfigForm.get("alarmStatusList").value;t||(t=[]);-1===t.indexOf(e)&&(t.push(e),this.alarmStatusConfigForm.get("alarmStatusList").setValue(t))}getAlarmStatusList(){return this.alarmStatusList.filter((e=>-1===this.alarmStatusConfigForm.get("alarmStatusList").value.indexOf(e)))}onAlarmStatusInputFocus(){this.statusFormControl.updateValueAndValidity({onlySelf:!0,emitEvent:!0})}clear(e=""){this.alarmStatusInput.nativeElement.value=e,this.statusFormControl.patchValue(null,{emitEvent:!0}),setTimeout((()=>{this.alarmStatusInput.nativeElement.blur(),this.alarmStatusInput.nativeElement.focus()}),0)}}e("CheckAlarmStatusComponent",kr),kr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:kr,deps:[{token:M.Store},{token:U.TranslateService},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),kr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:kr,selector:"tb-filter-node-check-alarm-status-config",viewQueries:[{propertyName:"alarmStatusInput",first:!0,predicate:["alarmStatusInput"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.alarm-status-filter\n \n \n \n {{alarmStatusTranslationsMap.get(alarmStatus) | translate}}\n \n close\n \n \n \n \n \n \n \n \n
\n
\n tb.rulenode.no-alarm-status-matching\n
\n
\n
\n
\n
\n \n
\n\n\n\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:pe.TbErrorComponent,selector:"tb-error",inputs:["error"]},{kind:"component",type:ie.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:ve.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple","hideSingleSelectionIndicator"],exportAs:["matAutocomplete"]},{kind:"directive",type:ve.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{kind:"directive",type:ve.MatAutocompleteOrigin,selector:"[matAutocompleteOrigin]",exportAs:["matAutocompleteOrigin"]},{kind:"component",type:le.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:le.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:le.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:le.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:R.AsyncPipe,name:"async"},{kind:"pipe",type:Le.HighlightPipe,name:"highlight"},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:kr,decorators:[{type:r,args:[{selector:"tb-filter-node-check-alarm-status-config",template:'
\n \n tb.rulenode.alarm-status-filter\n \n \n \n {{alarmStatusTranslationsMap.get(alarmStatus) | translate}}\n \n close\n \n \n \n \n \n \n \n \n
\n
\n tb.rulenode.no-alarm-status-matching\n
\n
\n
\n
\n
\n \n
\n\n\n\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:U.TranslateService},{type:A.UntypedFormBuilder}]},propDecorators:{alarmStatusInput:[{type:a,args:["alarmStatusInput",{static:!1}]}]}});class Ir extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.separatorKeysCodes=[ne,ae,oe]}configForm(){return this.checkMessageConfigForm}onConfigurationSet(e){this.checkMessageConfigForm=this.fb.group({messageNames:[e?e.messageNames:null,[]],metadataNames:[e?e.metadataNames:null,[]],checkAllKeys:[!!e&&e.checkAllKeys,[]]})}validateConfig(){const e=this.checkMessageConfigForm.get("messageNames").value,t=this.checkMessageConfigForm.get("metadataNames").value;return e.length>0||t.length>0}removeMessageName(e){const t=this.checkMessageConfigForm.get("messageNames").value,r=t.indexOf(e);r>=0&&(t.splice(r,1),this.checkMessageConfigForm.get("messageNames").setValue(t,{emitEvent:!0}))}removeMetadataName(e){const t=this.checkMessageConfigForm.get("metadataNames").value,r=t.indexOf(e);r>=0&&(t.splice(r,1),this.checkMessageConfigForm.get("metadataNames").setValue(t,{emitEvent:!0}))}addMessageName(e){const t=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.checkMessageConfigForm.get("messageNames").value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.checkMessageConfigForm.get("messageNames").setValue(e,{emitEvent:!0}))}t&&(t.value="")}addMetadataName(e){const t=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.checkMessageConfigForm.get("metadataNames").value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.checkMessageConfigForm.get("metadataNames").setValue(e,{emitEvent:!0}))}t&&(t.value="")}}e("CheckMessageConfigComponent",Ir),Ir.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Ir,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ir.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Ir,selector:"tb-filter-node-check-message-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.data-keys\n \n \n {{messageName}}\n close\n \n \n \n tb.rulenode.separator-hint\n \n \n tb.rulenode.metadata-keys\n \n \n {{metadataName}}\n close\n \n \n \n tb.rulenode.separator-hint\n \n \n {{ \'tb.rulenode.check-all-keys\' | translate }}\n \n
tb.rulenode.check-all-keys-hint
\n
\n',styles:[":host label.tb-title{margin-bottom:-10px}\n"],dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:ie.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"component",type:le.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:le.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:le.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:le.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Ir,decorators:[{type:r,args:[{selector:"tb-filter-node-check-message-config",template:'
\n \n tb.rulenode.data-keys\n \n \n {{messageName}}\n close\n \n \n \n tb.rulenode.separator-hint\n \n \n tb.rulenode.metadata-keys\n \n \n {{metadataName}}\n close\n \n \n \n tb.rulenode.separator-hint\n \n \n {{ \'tb.rulenode.check-all-keys\' | translate }}\n \n
tb.rulenode.check-all-keys-hint
\n
\n',styles:[":host label.tb-title{margin-bottom:-10px}\n"]}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Tr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.entitySearchDirection=Object.keys(g),this.entitySearchDirectionTranslationsMap=y}configForm(){return this.checkRelationConfigForm}onConfigurationSet(e){this.checkRelationConfigForm=this.fb.group({checkForSingleEntity:[!!e&&e.checkForSingleEntity,[]],direction:[e?e.direction:null,[]],entityType:[e?e.entityType:null,e&&e.checkForSingleEntity?[G.required]:[]],entityId:[e?e.entityId:null,e&&e.checkForSingleEntity?[G.required]:[]],relationType:[e?e.relationType:null,[G.required]]})}validatorTriggers(){return["checkForSingleEntity"]}updateValidators(e){const t=this.checkRelationConfigForm.get("checkForSingleEntity").value;this.checkRelationConfigForm.get("entityType").setValidators(t?[G.required]:[]),this.checkRelationConfigForm.get("entityType").updateValueAndValidity({emitEvent:e}),this.checkRelationConfigForm.get("entityId").setValidators(t?[G.required]:[]),this.checkRelationConfigForm.get("entityId").updateValueAndValidity({emitEvent:e})}}e("CheckRelationConfigComponent",Tr),Tr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Tr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Tr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Tr,selector:"tb-filter-node-check-relation-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.check-relation-to-specific-entity\' | translate }}\n \n
tb.rulenode.check-relation-hint
\n \n relation.direction\n \n \n {{ entitySearchDirectionTranslationsMap.get(direction) | translate }}\n \n \n \n
\n \n \n \n \n
\n \n \n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:He.EntityAutocompleteComponent,selector:"tb-entity-autocomplete",inputs:["entityType","entitySubtype","excludeEntityIds","labelText","requiredText","appearance","required","disabled"],outputs:["entityChanged"]},{kind:"component",type:se.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"component",type:Se.RelationTypeAutocompleteComponent,selector:"tb-relation-type-autocomplete",inputs:["required","disabled","subscriptSizing"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Tr,decorators:[{type:r,args:[{selector:"tb-filter-node-check-relation-config",template:'
\n \n {{ \'tb.rulenode.check-relation-to-specific-entity\' | translate }}\n \n
tb.rulenode.check-relation-hint
\n \n relation.direction\n \n \n {{ entitySearchDirectionTranslationsMap.get(direction) | translate }}\n \n \n \n
\n \n \n \n \n
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Nr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.perimeterType=tt,this.perimeterTypes=Object.keys(tt),this.perimeterTypeTranslationMap=rt,this.rangeUnits=Object.keys(ot),this.rangeUnitTranslationMap=it}configForm(){return this.geoFilterConfigForm}onConfigurationSet(e){this.geoFilterConfigForm=this.fb.group({latitudeKeyName:[e?e.latitudeKeyName:null,[G.required]],longitudeKeyName:[e?e.longitudeKeyName:null,[G.required]],perimeterType:[e?e.perimeterType:null,[G.required]],fetchPerimeterInfoFromMessageMetadata:[!!e&&e.fetchPerimeterInfoFromMessageMetadata,[]],perimeterKeyName:[e?e.perimeterKeyName:null,[]],centerLatitude:[e?e.centerLatitude:null,[]],centerLongitude:[e?e.centerLatitude:null,[]],range:[e?e.range:null,[]],rangeUnit:[e?e.rangeUnit:null,[]],polygonsDefinition:[e?e.polygonsDefinition:null,[]]})}validatorTriggers(){return["fetchPerimeterInfoFromMessageMetadata","perimeterType"]}updateValidators(e){const t=this.geoFilterConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value,r=this.geoFilterConfigForm.get("perimeterType").value;t?this.geoFilterConfigForm.get("perimeterKeyName").setValidators([G.required]):this.geoFilterConfigForm.get("perimeterKeyName").setValidators([]),t||r!==tt.CIRCLE?(this.geoFilterConfigForm.get("centerLatitude").setValidators([]),this.geoFilterConfigForm.get("centerLongitude").setValidators([]),this.geoFilterConfigForm.get("range").setValidators([]),this.geoFilterConfigForm.get("rangeUnit").setValidators([])):(this.geoFilterConfigForm.get("centerLatitude").setValidators([G.required,G.min(-90),G.max(90)]),this.geoFilterConfigForm.get("centerLongitude").setValidators([G.required,G.min(-180),G.max(180)]),this.geoFilterConfigForm.get("range").setValidators([G.required,G.min(0)]),this.geoFilterConfigForm.get("rangeUnit").setValidators([G.required])),t||r!==tt.POLYGON?this.geoFilterConfigForm.get("polygonsDefinition").setValidators([]):this.geoFilterConfigForm.get("polygonsDefinition").setValidators([G.required]),this.geoFilterConfigForm.get("perimeterKeyName").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("centerLatitude").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("centerLongitude").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("range").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("rangeUnit").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("polygonsDefinition").updateValueAndValidity({emitEvent:e})}}e("GpsGeoFilterConfigComponent",Nr),Nr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Nr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Nr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Nr,selector:"tb-filter-node-gps-geofencing-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.latitude-key-name\n \n \n {{ \'tb.rulenode.latitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.longitude-key-name\n \n \n {{ \'tb.rulenode.longitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.perimeter-type\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n \n {{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}\n \n \n tb.rulenode.perimeter-key-name\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.circle-center-latitude\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n tb.rulenode.circle-center-longitude\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.range\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n tb.rulenode.range-units\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n
\n
\n
\n
\n \n tb.rulenode.polygon-definition\n \n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:O.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:A.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Nr,decorators:[{type:r,args:[{selector:"tb-filter-node-gps-geofencing-config",template:'
\n \n tb.rulenode.latitude-key-name\n \n \n {{ \'tb.rulenode.latitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.longitude-key-name\n \n \n {{ \'tb.rulenode.longitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.perimeter-type\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n \n {{ \'tb.rulenode.fetch-perimeter-info-from-message-metadata\' | translate }}\n \n \n tb.rulenode.perimeter-key-name\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.circle-center-latitude\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n tb.rulenode.circle-center-longitude\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.range\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n tb.rulenode.range-units\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n
\n
\n
\n
\n \n tb.rulenode.polygon-definition\n \n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class qr extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.messageTypeConfigForm}onConfigurationSet(e){this.messageTypeConfigForm=this.fb.group({messageTypes:[e?e.messageTypes:null,[G.required]]})}}e("MessageTypeConfigComponent",qr),qr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:qr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),qr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:qr,selector:"tb-filter-node-message-type-config",usesInheritance:!0,ngImport:t,template:'
\n \n
\n',dependencies:[{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Yt,selector:"tb-message-types-config",inputs:["required","label","placeholder","disabled"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:qr,decorators:[{type:r,args:[{selector:"tb-filter-node-message-type-config",template:'
\n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Sr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.allowedEntityTypes=[x.DEVICE,x.ASSET,x.ENTITY_VIEW,x.TENANT,x.CUSTOMER,x.USER,x.DASHBOARD,x.RULE_CHAIN,x.RULE_NODE]}configForm(){return this.originatorTypeConfigForm}onConfigurationSet(e){this.originatorTypeConfigForm=this.fb.group({originatorTypes:[e?e.originatorTypes:null,[G.required]]})}}e("OriginatorTypeConfigComponent",Sr),Sr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Sr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Sr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Sr,selector:"tb-filter-node-originator-type-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n
\n',dependencies:[{kind:"component",type:Ke.EntityTypeListComponent,selector:"tb-entity-type-list",inputs:["required","disabled","subscriptSizing","allowedEntityTypes","ignoreAuthorityFilter"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Sr,decorators:[{type:r,args:[{selector:"tb-filter-node-originator-type-config",template:'
\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Mr extends s{constructor(e,t,r,n){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=r,this.translate=n,this.tbelEnabled=J(this.store).tbelEnabled,this.scriptLanguage=d}configForm(){return this.scriptConfigForm}onConfigurationSet(e){this.scriptConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:d.JS,[G.required]],jsScript:[e?e.jsScript:null,[]],tbelScript:[e?e.tbelScript:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.scriptConfigForm.get("scriptLang").value;t!==d.TBEL||this.tbelEnabled||(t=d.JS,this.scriptConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.scriptConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.scriptConfigForm.get("jsScript").setValidators(t===d.JS?[G.required]:[]),this.scriptConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.scriptConfigForm.get("tbelScript").setValidators(t===d.TBEL?[G.required]:[]),this.scriptConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=d.JS)),e}testScript(){const e=this.scriptConfigForm.get("scriptLang").value,t=e===d.JS?"jsScript":"tbelScript",r=e===d.JS?"rulenode/filter_node_script_fn":"rulenode/tbel/filter_node_script_fn",n=this.scriptConfigForm.get(t).value;this.nodeScriptTestService.testNodeScript(n,"filter",this.translate.instant("tb.rulenode.filter"),"Filter",["msg","metadata","msgType"],this.ruleNodeId,r,e).subscribe((e=>{e&&this.scriptConfigForm.get(t).setValue(e)}))}onValidate(){this.scriptConfigForm.get("scriptLang").value===d.JS&&this.jsFuncComponent.validateOnSubmit()}}e("ScriptConfigComponent",Mr),Mr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Mr,deps:[{token:M.Store},{token:A.UntypedFormBuilder},{token:Q.NodeScriptTestService},{token:U.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Mr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Mr,selector:"tb-filter-node-script-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n
\n \n
\n
\n',dependencies:[{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ee.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:te.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:re.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Mr,decorators:[{type:r,args:[{selector:"tb-filter-node-script-config",template:'
\n \n \n \n \n \n
\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder},{type:Q.NodeScriptTestService},{type:U.TranslateService}]},propDecorators:{jsFuncComponent:[{type:a,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:a,args:["tbelFuncComponent",{static:!1}]}]}});class Ar extends s{constructor(e,t,r,n){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=r,this.translate=n,this.tbelEnabled=J(this.store).tbelEnabled,this.scriptLanguage=d}configForm(){return this.switchConfigForm}onConfigurationSet(e){this.switchConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:d.JS,[G.required]],jsScript:[e?e.jsScript:null,[]],tbelScript:[e?e.tbelScript:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.switchConfigForm.get("scriptLang").value;t!==d.TBEL||this.tbelEnabled||(t=d.JS,this.switchConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.switchConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.switchConfigForm.get("jsScript").setValidators(t===d.JS?[G.required]:[]),this.switchConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.switchConfigForm.get("tbelScript").setValidators(t===d.TBEL?[G.required]:[]),this.switchConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=d.JS)),e}testScript(){const e=this.switchConfigForm.get("scriptLang").value,t=e===d.JS?"jsScript":"tbelScript",r=e===d.JS?"rulenode/switch_node_script_fn":"rulenode/tbel/switch_node_script_fn",n=this.switchConfigForm.get(t).value;this.nodeScriptTestService.testNodeScript(n,"switch",this.translate.instant("tb.rulenode.switch"),"Switch",["msg","metadata","msgType"],this.ruleNodeId,r,e).subscribe((e=>{e&&this.switchConfigForm.get(t).setValue(e)}))}onValidate(){this.switchConfigForm.get("scriptLang").value===d.JS&&this.jsFuncComponent.validateOnSubmit()}}e("SwitchConfigComponent",Ar),Ar.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Ar,deps:[{token:M.Store},{token:A.UntypedFormBuilder},{token:Q.NodeScriptTestService},{token:U.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Ar.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Ar,selector:"tb-filter-node-switch-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n
\n \n
\n
\n',dependencies:[{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ee.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:te.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:re.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Ar,decorators:[{type:r,args:[{selector:"tb-filter-node-switch-config",template:'
\n \n \n \n \n \n
\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder},{type:Q.NodeScriptTestService},{type:U.TranslateService}]},propDecorators:{jsFuncComponent:[{type:a,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:a,args:["tbelFuncComponent",{static:!1}]}]}});class Gr{}e("RuleNodeCoreConfigFilterModule",Gr),Gr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Gr,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Gr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.9",ngImport:t,type:Gr,declarations:[Ir,Tr,Nr,qr,Sr,Mr,Ar,kr],imports:[w,v,Xt],exports:[Ir,Tr,Nr,qr,Sr,Mr,Ar,kr]}),Gr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Gr,imports:[w,v,Xt]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Gr,decorators:[{type:l,args:[{declarations:[Ir,Tr,Nr,qr,Sr,Mr,Ar,kr],imports:[w,v,Xt],exports:[Ir,Tr,Nr,qr,Sr,Mr,Ar,kr]}]}]});class Er extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.originatorSource=Ze,this.originatorSources=Object.keys(Ze),this.originatorSourceTranslationMap=et,this.allowedEntityTypes=[x.DEVICE,x.ASSET,x.ENTITY_VIEW,x.USER,x.EDGE]}configForm(){return this.changeOriginatorConfigForm}onConfigurationSet(e){this.changeOriginatorConfigForm=this.fb.group({originatorSource:[e?e.originatorSource:null,[G.required]],entityType:[e?e.entityType:null,[]],entityNamePattern:[e?e.entityNamePattern:null,[]],relationsQuery:[e?e.relationsQuery:null,[]]})}validatorTriggers(){return["originatorSource"]}updateValidators(e){const t=this.changeOriginatorConfigForm.get("originatorSource").value;t===Ze.RELATED?this.changeOriginatorConfigForm.get("relationsQuery").setValidators([G.required]):this.changeOriginatorConfigForm.get("relationsQuery").setValidators([]),t===Ze.ENTITY?(this.changeOriginatorConfigForm.get("entityType").setValidators([G.required]),this.changeOriginatorConfigForm.get("entityNamePattern").setValidators([G.required,G.pattern(/.*\S.*/)])):(this.changeOriginatorConfigForm.get("entityType").patchValue(null,{emitEvent:e}),this.changeOriginatorConfigForm.get("entityNamePattern").patchValue(null,{emitEvent:e}),this.changeOriginatorConfigForm.get("entityType").setValidators([]),this.changeOriginatorConfigForm.get("entityNamePattern").setValidators([])),this.changeOriginatorConfigForm.get("relationsQuery").updateValueAndValidity({emitEvent:e}),this.changeOriginatorConfigForm.get("entityType").updateValueAndValidity({emitEvent:e}),this.changeOriginatorConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e})}}e("ChangeOriginatorConfigComponent",Er),Er.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Er,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Er.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Er,selector:"tb-transformation-node-change-originator-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.originator-source\n \n \n {{ originatorSourceTranslationMap.get(source) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n \n \n
\n
\n \n \n \n
\n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:se.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:B.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Qt,selector:"tb-relations-query-config",inputs:["disabled","required"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Er,decorators:[{type:r,args:[{selector:"tb-transformation-node-change-originator-config",template:'
\n \n tb.rulenode.originator-source\n \n \n {{ originatorSourceTranslationMap.get(source) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n \n \n
\n
\n \n \n \n
\n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Dr extends s{constructor(e,t,r,n){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=r,this.translate=n,this.tbelEnabled=J(this.store).tbelEnabled,this.scriptLanguage=d}configForm(){return this.scriptConfigForm}onConfigurationSet(e){this.scriptConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:d.JS,[G.required]],jsScript:[e?e.jsScript:null,[G.required]],tbelScript:[e?e.tbelScript:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.scriptConfigForm.get("scriptLang").value;t!==d.TBEL||this.tbelEnabled||(t=d.JS,this.scriptConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.scriptConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.scriptConfigForm.get("jsScript").setValidators(t===d.JS?[G.required]:[]),this.scriptConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.scriptConfigForm.get("tbelScript").setValidators(t===d.TBEL?[G.required]:[]),this.scriptConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=d.JS)),e}testScript(){const e=this.scriptConfigForm.get("scriptLang").value,t=e===d.JS?"jsScript":"tbelScript",r=e===d.JS?"rulenode/transformation_node_script_fn":"rulenode/tbel/transformation_node_script_fn",n=this.scriptConfigForm.get(t).value;this.nodeScriptTestService.testNodeScript(n,"update",this.translate.instant("tb.rulenode.transformer"),"Transform",["msg","metadata","msgType"],this.ruleNodeId,r,e).subscribe((e=>{e&&this.scriptConfigForm.get(t).setValue(e)}))}onValidate(){this.scriptConfigForm.get("scriptLang").value===d.JS&&this.jsFuncComponent.validateOnSubmit()}}e("TransformScriptConfigComponent",Dr),Dr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Dr,deps:[{token:M.Store},{token:A.UntypedFormBuilder},{token:Q.NodeScriptTestService},{token:U.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Dr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Dr,selector:"tb-transformation-node-script-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n
\n \n
\n
\n',dependencies:[{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ee.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:te.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:re.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Dr,decorators:[{type:r,args:[{selector:"tb-transformation-node-script-config",template:'
\n \n \n \n \n \n
\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder},{type:Q.NodeScriptTestService},{type:U.TranslateService}]},propDecorators:{jsFuncComponent:[{type:a,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:a,args:["tbelFuncComponent",{static:!1}]}]}});class Vr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.mailBodyTypes=[{name:"tb.mail-body-type.plain-text",value:"false"},{name:"tb.mail-body-type.html",value:"true"},{name:"tb.mail-body-type.dynamic",value:"dynamic"}]}configForm(){return this.toEmailConfigForm}onConfigurationSet(e){this.toEmailConfigForm=this.fb.group({fromTemplate:[e?e.fromTemplate:null,[G.required]],toTemplate:[e?e.toTemplate:null,[G.required]],ccTemplate:[e?e.ccTemplate:null,[]],bccTemplate:[e?e.bccTemplate:null,[]],subjectTemplate:[e?e.subjectTemplate:null,[G.required]],mailBodyType:[e?e.mailBodyType:null],isHtmlTemplate:[e?e.isHtmlTemplate:null],bodyTemplate:[e?e.bodyTemplate:null,[G.required]]}),this.toEmailConfigForm.get("mailBodyType").valueChanges.pipe(Ce([e?.subjectTemplate])).subscribe((e=>{"dynamic"===e?(this.toEmailConfigForm.get("isHtmlTemplate").patchValue("",{emitEvent:!1}),this.toEmailConfigForm.get("isHtmlTemplate").setValidators(G.required)):this.toEmailConfigForm.get("isHtmlTemplate").clearValidators(),this.toEmailConfigForm.get("isHtmlTemplate").updateValueAndValidity()}))}}e("ToEmailConfigComponent",Vr),Vr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Vr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Vr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Vr,selector:"tb-transformation-node-to-email-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.from-template\n \n \n {{ \'tb.rulenode.from-template-required\' | translate }}\n \n \n \n \n tb.rulenode.to-template\n \n \n {{ \'tb.rulenode.to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.cc-template\n \n \n \n \n tb.rulenode.bcc-template\n \n \n \n \n tb.rulenode.subject-template\n \n \n {{ \'tb.rulenode.subject-template-required\' | translate }}\n \n \n \n \n tb.rulenode.mail-body-type\n \n \n {{ type.name | translate }}\n \n \n \n \n tb.rulenode.dynamic-mail-body-type\n \n \n \n \n tb.rulenode.body-template\n \n \n {{ \'tb.rulenode.body-template-required\' | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:j.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:_.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:U.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Vr,decorators:[{type:r,args:[{selector:"tb-transformation-node-to-email-config",template:'
\n \n tb.rulenode.from-template\n \n \n {{ \'tb.rulenode.from-template-required\' | translate }}\n \n \n \n \n tb.rulenode.to-template\n \n \n {{ \'tb.rulenode.to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.cc-template\n \n \n \n \n tb.rulenode.bcc-template\n \n \n \n \n tb.rulenode.subject-template\n \n \n {{ \'tb.rulenode.subject-template-required\' | translate }}\n \n \n \n \n tb.rulenode.mail-body-type\n \n \n {{ type.name | translate }}\n \n \n \n \n tb.rulenode.dynamic-mail-body-type\n \n \n \n \n tb.rulenode.body-template\n \n \n {{ \'tb.rulenode.body-template-required\' | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Pr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.separatorKeysCodes=[ne,ae,oe]}onConfigurationSet(e){this.copyKeysConfigForm=this.fb.group({fromMetadata:[e?e.fromMetadata:null,[G.required]],keys:[e?e.keys:null,[G.required]]})}configForm(){return this.copyKeysConfigForm}removeKey(e){const t=this.copyKeysConfigForm.get("keys").value,r=t.indexOf(e);r>=0&&(t.splice(r,1),this.copyKeysConfigForm.get("keys").patchValue(t,{emitEvent:!0}))}addKey(e){const t=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.copyKeysConfigForm.get("keys").value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.copyKeysConfigForm.get("keys").patchValue(e,{emitEvent:!0}))}t&&(t.value="")}}e("CopyKeysConfigComponent",Pr),Pr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Pr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Pr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Pr,selector:"tb-transformation-node-copy-keys-config",usesInheritance:!0,ngImport:t,template:'
\n
{{\'tb.rulenode.copy-from\' | translate}}
\n \n \n {{\'tb.rulenode.data-to-metadata\' | translate}}\n \n \n {{\'tb.rulenode.metadata-to-data\' | translate}}\n \n \n \n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.keys-required\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ie.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Ee.MatRadioGroup,selector:"mat-radio-group",exportAs:["matRadioGroup"]},{kind:"component",type:Ee.MatRadioButton,selector:"mat-radio-button",inputs:["disableRipple","tabIndex"],exportAs:["matRadioButton"]},{kind:"component",type:le.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:le.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:le.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:le.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Pr,decorators:[{type:r,args:[{selector:"tb-transformation-node-copy-keys-config",template:'
\n
{{\'tb.rulenode.copy-from\' | translate}}
\n \n \n {{\'tb.rulenode.data-to-metadata\' | translate}}\n \n \n {{\'tb.rulenode.metadata-to-data\' | translate}}\n \n \n \n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.keys-required\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Rr extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.renameKeysConfigForm}onConfigurationSet(e){this.renameKeysConfigForm=this.fb.group({fromMetadata:[e?e.fromMetadata:null,[G.required]],renameKeysMapping:[e?e.renameKeysMapping:null,[G.required]]})}}e("RenameKeysConfigComponent",Rr),Rr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Rr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Rr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Rr,selector:"tb-transformation-node-rename-keys-config",usesInheritance:!0,ngImport:t,template:'
\n
{{ \'tb.rulenode.rename-keys-in\' | translate }}
\n \n \n {{\'tb.rulenode.data\' | translate}}\n \n \n {{\'tb.rulenode.metadata\' | translate}}\n \n \n \n \n
\n',dependencies:[{kind:"directive",type:Ee.MatRadioGroup,selector:"mat-radio-group",exportAs:["matRadioGroup"]},{kind:"component",type:Ee.MatRadioButton,selector:"mat-radio-button",inputs:["disableRipple","tabIndex"],exportAs:["matRadioButton"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:wt,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Rr,decorators:[{type:r,args:[{selector:"tb-transformation-node-rename-keys-config",template:'
\n
{{ \'tb.rulenode.rename-keys-in\' | translate }}
\n \n \n {{\'tb.rulenode.data\' | translate}}\n \n \n {{\'tb.rulenode.metadata\' | translate}}\n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class wr extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.jsonPathConfigForm}onConfigurationSet(e){this.jsonPathConfigForm=this.fb.group({jsonPath:[e?e.jsonPath:null,[G.required]]})}}e("NodeJsonPathConfigComponent",wr),wr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:wr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),wr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:wr,selector:"tb-transformation-node-json-path-config",usesInheritance:!0,ngImport:t,template:"
\n \n {{ 'tb.rulenode.json-path-expression' | translate }}\n \n {{ 'tb.rulenode.json-path-expression-hint' | translate }}\n {{ 'tb.rulenode.json-path-expression-required' | translate }}\n \n
\n\n",dependencies:[{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatLabel,selector:"mat-label"},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:wr,decorators:[{type:r,args:[{selector:"tb-transformation-node-json-path-config",template:"
\n \n {{ 'tb.rulenode.json-path-expression' | translate }}\n \n {{ 'tb.rulenode.json-path-expression-hint' | translate }}\n {{ 'tb.rulenode.json-path-expression-required' | translate }}\n \n
\n\n"}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Or extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.separatorKeysCodes=[ne,ae,oe]}onConfigurationSet(e){this.deleteKeysConfigForm=this.fb.group({fromMetadata:[e?e.fromMetadata:null,[G.required]],keys:[e?e.keys:null,[G.required]]})}configForm(){return this.deleteKeysConfigForm}removeKey(e){const t=this.deleteKeysConfigForm.get("keys").value,r=t.indexOf(e);r>=0&&(t.splice(r,1),this.deleteKeysConfigForm.get("keys").patchValue(t,{emitEvent:!0}))}addKey(e){const t=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.deleteKeysConfigForm.get("keys").value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.deleteKeysConfigForm.get("keys").patchValue(e,{emitEvent:!0}))}t&&(t.value="")}}e("DeleteKeysConfigComponent",Or),Or.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Or,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Or.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Or,selector:"tb-transformation-node-delete-keys-config",usesInheritance:!0,ngImport:t,template:'
\n
{{\'tb.rulenode.delete-from\' | translate}}
\n \n \n {{\'tb.rulenode.data\' | translate}}\n \n \n {{\'tb.rulenode.metadata\' | translate}}\n \n \n \n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.keys-required\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:R.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:R.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ie.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:H.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:K.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:K.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:K.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Ee.MatRadioGroup,selector:"mat-radio-group",exportAs:["matRadioGroup"]},{kind:"component",type:Ee.MatRadioButton,selector:"mat-radio-button",inputs:["disableRipple","tabIndex"],exportAs:["matRadioButton"]},{kind:"component",type:le.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:le.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:le.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:le.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:B.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"},{kind:"pipe",type:Ue,name:"safeHtml"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Or,decorators:[{type:r,args:[{selector:"tb-transformation-node-delete-keys-config",template:'
\n
{{\'tb.rulenode.delete-from\' | translate}}
\n \n \n {{\'tb.rulenode.data\' | translate}}\n \n \n {{\'tb.rulenode.metadata\' | translate}}\n \n \n \n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.keys-required\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Hr{}e("RulenodeCoreConfigTransformModule",Hr),Hr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Hr,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Hr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.9",ngImport:t,type:Hr,declarations:[Er,Dr,Vr,Pr,Rr,wr,Or],imports:[w,v,Xt],exports:[Er,Dr,Vr,Pr,Rr,wr,Or]}),Hr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Hr,imports:[w,v,Xt]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Hr,decorators:[{type:l,args:[{declarations:[Er,Dr,Vr,Pr,Rr,wr,Or],imports:[w,v,Xt],exports:[Er,Dr,Vr,Pr,Rr,wr,Or]}]}]});class Kr extends s{constructor(e,t){super(e),this.store=e,this.fb=t,this.entityType=x}configForm(){return this.ruleChainInputConfigForm}onConfigurationSet(e){this.ruleChainInputConfigForm=this.fb.group({ruleChainId:[e?e.ruleChainId:null,[G.required]]})}}e("RuleChainInputComponent",Kr),Kr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Kr,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Kr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Kr,selector:"tb-flow-node-rule-chain-input-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',dependencies:[{kind:"component",type:He.EntityAutocompleteComponent,selector:"tb-entity-autocomplete",inputs:["entityType","entitySubtype","excludeEntityIds","labelText","requiredText","appearance","required","disabled"],outputs:["entityChanged"]},{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:A.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Kr,decorators:[{type:r,args:[{selector:"tb-flow-node-rule-chain-input-config",template:'
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Br extends s{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.ruleChainOutputConfigForm}onConfigurationSet(e){this.ruleChainOutputConfigForm=this.fb.group({})}}e("RuleChainOutputComponent",Br),Br.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Br,deps:[{token:M.Store},{token:A.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Br.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.9",type:Br,selector:"tb-flow-node-rule-chain-output-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
\n',dependencies:[{kind:"directive",type:B.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:A.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:A.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"pipe",type:U.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Br,decorators:[{type:r,args:[{selector:"tb-flow-node-rule-chain-output-config",template:'
\n
\n
\n'}]}],ctorParameters:function(){return[{type:M.Store},{type:A.UntypedFormBuilder}]}});class Ur{}e("RuleNodeCoreConfigFlowModule",Ur),Ur.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Ur,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Ur.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.9",ngImport:t,type:Ur,declarations:[Kr,Br],imports:[w,v,Xt],exports:[Kr,Br]}),Ur.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Ur,imports:[w,v,Xt]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:Ur,decorators:[{type:l,args:[{declarations:[Kr,Br],imports:[w,v,Xt],exports:[Kr,Br]}]}]});class zr{constructor(e){!function(e){e.setTranslation("en_US",{tb:{rulenode:{"create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","output-message-type":"Output message type","output-message-type-required":"Output message type is required","output-message-type-max-length":"Output message type should be less than 256","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.","customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","fetch-mode-hint":"If selected fetch mode 'ALL' you able to choose telemetry sampling order.","order-by":"Order by","order-by-hint":"Select to choose telemetry sampling order.",limit:"Limit","limit-hint":"Min limit value is 2, max - 1000. In case you want to fetch a single entry, select fetch mode 'FIRST' or 'LAST'.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Time value should be in a range from 1 to 2147483647.","start-interval-value-required":"Start interval value is required.","end-interval-value-required":"End interval value is required.",filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attribute keys","shared-attributes":"Shared attribute keys","server-attributes":"Server attribute keys","attributes-keys":"Attributes keys","attributes-keys-required":"Attributes keys are required","notify-device":"Notify device","send-attributes-updated-notification":"Send attributes updated notification","send-attributes-updated-notification-hint":"Send notification about updated attributes as a separate message to the rule engine queue.","send-attributes-deleted-notification":"Send attributes deleted notification","send-attributes-deleted-notification-hint":"Send notification about deleted attributes as a separate message to the rule engine queue.","fetch-credentials-to-metadata":"Fetch credentials to metadata","notify-device-hint":"If the message arrives from the device, we will push it back to the device by default.","notify-device-delete-hint":"Send notification about deleted attributes to device.","latest-timeseries":"Latest time-series data keys","timeseries-key":"Time-series key","data-keys":"Message field names","copy-from":"Copy from","data-to-metadata":"Data to metadata","metadata-to-data":"Metadata to data","use-regular-expression-hint":"Hint: use regular expression to copy keys by pattern",interval:"Interval","interval-required":"Interval is required","interval-hint":"Deduplication interval in seconds.","interval-min-error":"Min allowed value is 1","max-pending-msgs":"Max pending messages","max-pending-msgs-hint":"Maximum number of messages that are stored in memory for each unique deduplication id.","max-pending-msgs-required":"Max pending messages is required","max-pending-msgs-max-error":"Max allowed value is 1000","max-pending-msgs-min-error":"Min allowed value is 1","max-retries":"Max retries","max-retries-required":"Max retries is required","max-retries-hint":"Maximum number of retries to push the deduplicated messages into the queue. 10 seconds delay is used between retries","max-retries-max-error":"Max allowed value is 100","max-retries-min-error":"Min allowed value is 0",strategy:"Strategy","strategy-required":"Strategy is required","strategy-all-hint":"Return all messages that arrived during deduplication period as a single JSON array message. Where each element represents object with msg and metadata inner properties.","strategy-first-hint":"Return first message that arrived during deduplication period.","strategy-last-hint":"Return last message that arrived during deduplication period.","first-message":"First Message","last-message":"Last Message","all-messages":"All Messages","output-msg-type-hint":"The message type of the deduplication result.","queue-name-hint":"The queue name where the deduplication result will be published.",keys:"Keys","keys-required":"Keys are required","rename-keys-in":"Rename keys in",data:"Data",metadata:"Metadata","key-name":"Key name","key-name-required":"Key name is required","new-key-name":"New key name","new-key-name-required":"New key name is required","metadata-keys":"Metadata field names","json-path-expression":"JSON path expression","json-path-expression-required":"JSON path expression is required","json-path-expression-hint":"JSONPath specifies a path to an element or a set of elements in a JSON structure. '$' represents the root object or array.","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","relation-type-pattern":"Relation type pattern","relation-type-pattern-required":"Relation type pattern is required","relation-types-list":"Relation types to propagate","relation-types-list-hint":"If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","delete-from":"Delete from","use-regular-expression-delete-hint":"Use regular expression to delete keys by pattern","fetch-into":"Fetch into","attr-mapping":"Attributes mapping","source-attribute":"Source attribute key","source-attribute-required":"Source attribute key is required.","source-telemetry":"Source telemetry key","source-telemetry-required":"Source telemetry key is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","originator-alarm-originator":"Alarm Originator","originator-entity":"Entity","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","use-metadata-period-in-seconds-patterns":"Use period in seconds pattern","use-metadata-period-in-seconds-patterns-hint":"If selected, rule node use period in seconds interval pattern from message metadata or data assuming that intervals are in the seconds.","period-in-seconds-pattern":"Period in seconds pattern","period-in-seconds-pattern-required":"Period in seconds pattern is required","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required","alarm-severity-pattern":"Alarm severity pattern","alarm-status-filter":"Alarm status filter","alarm-status-list-empty":"Alarm status list is empty","no-alarm-status-matching":"No alarm status matching were found.",propagate:"Propagate alarm to related entities","propagate-to-owner":"Propagate alarm to entity owner (Customer or Tenant)","propagate-to-tenant":"Propagate alarm to Tenant",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":'Comma separated address list, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',"cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","body-template":"Body Template","body-template-required":"Body Template is required","dynamic-mail-body-type":"Dynamic mail body type","mail-body-type":"Mail body type","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory","ignore-request-body":"Without request body","trim-double-quotes":"Message without quotes","trim-double-quotes-hint":"If selected, request body message payload will be sent without double quotes, i.e. msg = message body","read-timeout":"Read timeout in millis","read-timeout-hint":"The value of 0 means an infinite timeout","max-parallel-requests-count":"Max number of parallel requests","max-parallel-requests-count-hint":"The value of 0 specifies no limit in parallel processing",headers:"Headers","headers-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in header/value fields',header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","key-pattern":"Key pattern","key-pattern-hint":"Hint: Optional. If a valid partition number is specified, it will be used when sending the record. If no partition is specified, the key will be used instead. If neither is specified, a partition will be assigned in a round-robin fashion.","topic-pattern-required":"Topic pattern is required",topic:"Topic","topic-required":"Topic is required","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","gcp-project-id":"GCP project ID","gcp-project-id-required":"GCP project ID is required","gcp-service-account-key":"GCP service account key file","gcp-service-account-key-required":"GCP service account key file is required","pubsub-topic-name":"Topic name","pubsub-topic-name-required":"Topic name is required","message-attributes":"Message attributes","message-attributes-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in name/value fields',"connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","client-id-hint":'Hint: Optional. Leave empty for auto-generated Client ID. Be careful when specifying the Client ID. Majority of the MQTT brokers will not allow multiple connections with the same Client ID. To connect to such brokers, your mqtt Client ID must be unique. When platform is running in a micro-services mode, the copy of rule node is launched in each micro-service. This will automatically lead to multiple mqtt clients with the same ID and may cause failures of the rule node. To avoid such failures enable "Add Service ID as suffix to Client ID" option below.',"append-client-id-suffix":"Add Service ID as suffix to Client ID","client-id-suffix-hint":'Hint: Optional. Applied when "Client ID" specified explicitly. If selected then Service ID will be added to Client ID as a suffix. Helps to avoid failures when platform is running in a micro-services mode.',"device-id":"Device ID","device-id-required":"Device ID is required.","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","credentials-pem-hint":"At least Server CA certificate file or a pair of Client certificate and Client private key files are required","credentials-sas":"Shared Access Signature","sas-key":"SAS Key","sas-key-required":"SAS Key is required.",hostname:"Hostname","hostname-required":"Hostname is required.","azure-ca-cert":"CA certificate file","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"Server CA certificate file","private-key":"Client private key file",cert:"Client certificate file","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-interval-patterns":"Use interval patterns","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata or data assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","overwrite-alarm-details":"Overwrite alarm details","use-alarm-severity-pattern":"Use alarm severity pattern","check-all-keys":"Check that all specified fields are present","check-all-keys-hint":"If selected, checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval-pattern":"Start interval pattern","end-interval-pattern":"End interval pattern","start-interval-pattern-required":"Start interval pattern is required","end-interval-pattern-required":"End interval pattern is required","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","tls-version":"TLS version","enable-proxy":"Enable proxy","use-system-proxy-properties":"Use system proxy properties","proxy-host":"Proxy host","proxy-host-required":"Proxy host is required.","proxy-port":"Proxy port","proxy-port-required":"Proxy port is required.","proxy-port-range":"Proxy port should be in a range from 1 to 65535.","proxy-user":"Proxy user","proxy-password":"Proxy password","proxy-scheme":"Proxy scheme","numbers-to-template":"Phone Numbers To Template","numbers-to-template-required":"Phone Numbers To Template is required","numbers-to-template-hint":'Comma separated Phone Numbers, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',"sms-message-template":"SMS message Template","sms-message-template-required":"SMS message Template is required","use-system-sms-settings":"Use system SMS provider settings","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'Press "Enter" to complete field input.',"entity-details":"Select entity details","entity-details-id":"Id","entity-details-title":"Title","entity-details-country":"Country","entity-details-state":"State","entity-details-city":"City","entity-details-zip":"Zip","entity-details-address":"Address","entity-details-address2":"Address2","entity-details-additional_info":"Additional Info","entity-details-phone":"Phone","entity-details-email":"Email","add-to-metadata":"Add selected details to message metadata","add-to-metadata-hint":"If selected, adds the selected details keys to the message metadata instead of message data.","entity-details-list-empty":"No entity details selected.","no-entity-details-matching":"No entity details matching were found.","custom-table-name":"Custom table name","custom-table-name-required":"Table Name is required","custom-table-hint":"Enter the table name without prefix 'cs_tb_'.","message-field":"Message field","message-field-required":"Message field is required.","table-col":"Table column","table-col-required":"Table column is required.","latitude-key-name":"Latitude key name","longitude-key-name":"Longitude key name","latitude-key-name-required":"Latitude key name is required.","longitude-key-name-required":"Longitude key name is required.","fetch-perimeter-info-from-message-metadata":"Fetch perimeter information from message metadata","perimeter-key-name":"Perimeter key name","perimeter-key-name-required":"Perimeter key name is required.","perimeter-circle":"Circle","perimeter-polygon":"Polygon","perimeter-type":"Perimeter type","circle-center-latitude":"Center latitude","circle-center-latitude-required":"Center latitude is required.","circle-center-longitude":"Center longitude","circle-center-longitude-required":"Center longitude is required.","range-unit-meter":"Meter","range-unit-kilometer":"Kilometer","range-unit-foot":"Foot","range-unit-mile":"Mile","range-unit-nautical-mile":"Nautical mile","range-units":"Range units",range:"Range","range-required":"Range is required.","polygon-definition":"Polygon definition","polygon-definition-required":"Polygon definition is required.","polygon-definition-hint":"Please, use the following format for manual definition of polygon: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].","min-inside-duration":"Minimal inside duration","min-inside-duration-value-required":"Minimal inside duration is required","min-inside-duration-time-unit":"Minimal inside duration time unit","min-outside-duration":"Minimal outside duration","min-outside-duration-value-required":"Minimal outside duration is required","min-outside-duration-time-unit":"Minimal outside duration time unit","tell-failure-if-absent":"Tell Failure","tell-failure-if-absent-hint":'If at least one selected key doesn\'t exist the outbound message will report "Failure".',"get-latest-value-with-ts":"Fetch timestamp for the latest telemetry values","get-latest-value-with-ts-hint":'If selected, the latest telemetry values will also include timestamp, e.g: "temp": "{"ts":1574329385897, "value":42}"',"use-redis-queue":"Use redis queue for message persistence","ignore-null-strings":"Ignore null strings","ignore-null-strings-hint":"If selected rule node will ignore entity fields with empty value.","trim-redis-queue":"Trim redis queue","redis-queue-max-size":"Redis queue max size","add-metadata-key-values-as-kafka-headers":"Add Message metadata key-value pairs to Kafka record headers","add-metadata-key-values-as-kafka-headers-hint":"If selected, key-value pairs from message metadata will be added to the outgoing records headers as byte arrays with predefined charset encoding.","charset-encoding":"Charset encoding","charset-encoding-required":"Charset encoding is required.","charset-us-ascii":"US-ASCII","charset-iso-8859-1":"ISO-8859-1","charset-utf-8":"UTF-8","charset-utf-16be":"UTF-16BE","charset-utf-16le":"UTF-16LE","charset-utf-16":"UTF-16","select-queue-hint":"The queue name can be selected from a drop-down list or add a custom name.","persist-alarm-rules":"Persist state of alarm rules","fetch-alarm-rules":"Fetch state of alarm rules","input-value-key":"Input value key","input-value-key-required":"Input value key is required.","output-value-key":"Output value key","output-value-key-required":"Output value key is required.",round:"Decimals","round-range":"Decimals should be in a range from 0 to 15.","use-cache":"Use cache for latest value","tell-failure-if-delta-is-negative":"Tell Failure if delta is negative","add-period-between-msgs":"Add period between messages","period-value-key":"Period value key","period-value-key-required":"Period value key is required.","general-pattern-hint":'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body',"alarm-severity-pattern-hint":'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body. Alarm severity should be system (CRITICAL, MAJOR etc.)',"output-node-name-hint":"The rule node name corresponds to the relation type of the output message, and it is used to forward messages to other rule nodes in the caller rule chain.","skip-latest-persistence":"Skip latest persistence","use-server-ts":"Use server ts","use-server-ts-hint":"Enable this setting to use the timestamp of the message processing instead of the timestamp from the message. Useful for all sorts of sequential processing if you merge messages from multiple sources (devices, assets, etc).","kv-map-pattern-hint":'Hint: use ${metadataKey} for value from metadata, $[messageKey] for value from message body to substitute "Source" and "Target" key names',"shared-scope":"Shared scope","server-scope":"Server scope","client-scope":"Client scope","attribute-type":"Attribute","constant-type":"Constant","time-series-type":"Time series","message-body-type":"Message body","message-metadata-type":"Message metadata","argument-tile":"Arguments","no-arguments-prompt":"No arguments configured","result-title":"Result","functions-field-input":"Functions","no-option-found":"No option found","argument-type-field-input":"Type","argument-type-field-input-required":"Argument type is required.","argument-key-field-input":"Key","argument-key-field-input-required":"Argument key is required.","constant-value-field-input":"Constant value","constant-value-field-input-required":"Constant value is required.","attribute-scope-field-input":"Attribute scope","attribute-scope-field-input-required":"Attribute scope os required.","default-value-field-input":"Default value","type-field-input":"Type","type-field-input-required":"Type is required.","key-field-input":"Key","key-field-input-required":"Key is required.","number-floating-point-field-input":"Number of digits after floating point","number-floating-point-field-input-hint":"Hint: use 0 to convert result to integer","add-to-body-field-input":"Add to message body","add-to-metadata-field-input":"Add to message metadata","custom-expression-field-input":"Mathematical Expression","custom-expression-field-input-required":"Mathematical expression is required","custom-expression-field-input-hint":"Hint: specify a mathematical expression to evaluate. For example, transform Fahrenheit to Celsius using (x - 32) / 1.8)","retained-message":"Retained","message-template":"Message template","message-template-required":"Message template is required","use-system-slack-settings":"Use system slack settings","slack-api-token":"Slack API token","slack-api-token-required":"Slack API token is required"},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry","unique-key-value-pair-error":"'{{valText}}' must be different from the current '{{keyText}}'"},"mail-body-type":{"plain-text":"Plain Text",html:"HTML",dynamic:"Dynamic"}}},!0)}(e)}}e("RuleNodeCoreConfigModule",zr),zr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:zr,deps:[{token:U.TranslateService}],target:t.ɵɵFactoryTarget.NgModule}),zr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.9",ngImport:t,type:zr,declarations:[Be],imports:[w,v],exports:[Zt,Gr,ur,Lr,Hr,Ur,Be]}),zr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:zr,imports:[w,v,Zt,Gr,ur,Lr,Hr,Ur]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.9",ngImport:t,type:zr,decorators:[{type:l,args:[{declarations:[Be],imports:[w,v],exports:[Zt,Gr,ur,Lr,Hr,Ur,Be]}]}],ctorParameters:function(){return[{type:U.TranslateService}]}})}}}));//# sourceMappingURL=rulenode-core-config.js.map +System.register(["@angular/core","@shared/public-api","@ngrx/store","@angular/forms","@angular/common","@angular/material/checkbox","@angular/material/input","@angular/material/form-field","@angular/flex-layout/flex","@ngx-translate/core","@angular/material/select","@angular/material/core","@angular/material/slide-toggle","@shared/components/hint-tooltip-icon.component","@shared/components/queue/queue-autocomplete.component","@core/public-api","@shared/components/js-func.component","@angular/material/button","@angular/material/icon","@angular/material/tooltip","@shared/components/script-lang.component","@angular/cdk/keycodes","@angular/material/chips","@shared/pipe/safe.pipe","@shared/components/entity/entity-type-select.component","@shared/components/entity/entity-select.component","@angular/cdk/coercion","@shared/components/tb-error.component","@angular/flex-layout/extended","@angular/material/list","@angular/cdk/drag-drop","rxjs/operators","@angular/material/autocomplete","@shared/pipe/highlight.pipe","@home/components/public-api","tslib","rxjs","@shared/components/help-popup.component","@shared/components/entity/entity-subtype-list.component","@shared/components/relation/relation-type-autocomplete.component","@home/components/relation/relation-filters.component","@angular/material/expansion","@shared/components/file-input.component","@shared/components/button/toggle-password.component","@shared/components/string-items-list.component","@shared/components/toggle-header.component","@shared/components/toggle-select.component","@shared/components/entity/entity-list.component","@shared/components/notification/template-autocomplete.component","@shared/components/tb-checkbox.component","@home/components/sms/sms-provider-configuration.component","@angular/material/radio","@shared/components/slack-conversation-autocomplete.component","@shared/components/entity/entity-autocomplete.component","@shared/components/entity/entity-type-list.component","@angular/cdk/platform"],(function(e){"use strict";var t,n,r,o,a,i,l,s,m,p,u,d,c,f,g,y,x,b,h,v,C,F,k,L,T,I,N,S,q,A,M,E,G,D,V,w,P,R,O,_,B,K,z,U,H,j,$,Q,J,Y,W,Z,X,ee,te,ne,re,oe,ae,ie,le,se,me,pe,ue,de,ce,fe,ge,ye,xe,be,he,ve,Ce,Fe,ke,Le,Te,Ie,Ne,Se,qe,Ae,Me,Ee,Ge,De,Ve,we,Pe,Re,Oe,_e,Be,Ke,ze,Ue,He,je,$e,Qe,Je,Ye,We,Ze,Xe,et,tt,nt,rt,ot,at,it,lt,st,mt,pt,ut,dt;return{setters:[function(e){t=e,n=e.Component,r=e.EventEmitter,o=e.ViewChild,a=e.forwardRef,i=e.Input,l=e.InjectionToken,s=e.Injectable,m=e.Inject,p=e.Optional,u=e.Directive,d=e.Output,c=e.NgModule},function(e){f=e.RuleNodeConfigurationComponent,g=e.AttributeScope,y=e.telemetryTypeTranslations,x=e.ServiceType,b=e.ScriptLanguage,h=e.AlarmSeverity,v=e.alarmSeverityTranslations,C=e.EntitySearchDirection,F=e.entitySearchDirectionTranslations,k=e.EntityType,L=e.entityFields,T=e.PageComponent,I=e.coerceBoolean,N=e.MessageType,S=e.messageTypeNames,q=e,A=e.AlarmStatus,M=e.alarmStatusTranslations,E=e.SharedModule,G=e.AggregationType,D=e.aggregationTranslations,V=e.NotificationType,w=e.SlackChanelType,P=e.SlackChanelTypesTranslateMap},function(e){R=e},function(e){O=e,_=e.Validators,B=e.NgControl,K=e.NG_VALUE_ACCESSOR,z=e.NG_VALIDATORS,U=e.FormArray,H=e.FormGroup},function(e){j=e,$=e.DOCUMENT,Q=e.CommonModule},function(e){J=e},function(e){Y=e},function(e){W=e},function(e){Z=e},function(e){X=e},function(e){ee=e},function(e){te=e},function(e){ne=e},function(e){re=e},function(e){oe=e},function(e){ae=e.getCurrentAuthState,ie=e,le=e.isEqual,se=e.isDefinedAndNotNull,me=e.deepTrim,pe=e.isObject,ue=e.isNotEmptyStr},function(e){de=e},function(e){ce=e},function(e){fe=e},function(e){ge=e},function(e){ye=e},function(e){xe=e.ENTER,be=e.COMMA,he=e.SEMICOLON},function(e){ve=e},function(e){Ce=e},function(e){Fe=e},function(e){ke=e},function(e){Le=e.coerceBooleanProperty,Te=e.coerceElement,Ie=e.coerceNumberProperty},function(e){Ne=e},function(e){Se=e},function(e){qe=e},function(e){Ae=e},function(e){Me=e.tap,Ee=e.map,Ge=e.startWith,De=e.mergeMap,Ve=e.share,we=e.takeUntil,Pe=e.auditTime},function(e){Re=e},function(e){Oe=e},function(e){_e=e.HomeComponentsModule},function(e){Be=e.__decorate},function(e){Ke=e.Subject,ze=e.takeUntil,Ue=e.of,He=e.EMPTY,je=e.fromEvent},function(e){$e=e},function(e){Qe=e},function(e){Je=e},function(e){Ye=e},function(e){We=e},function(e){Ze=e},function(e){Xe=e},function(e){et=e},function(e){tt=e},function(e){nt=e},function(e){rt=e},function(e){ot=e},function(e){at=e},function(e){it=e},function(e){lt=e},function(e){st=e},function(e){mt=e},function(e){pt=e},function(e){ut=e.normalizePassiveListenerOptions,dt=e}],execute:function(){class ct extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.emptyConfigForm}onConfigurationSet(e){this.emptyConfigForm=this.fb.group({})}}e("EmptyConfigComponent",ct),ct.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ct,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),ct.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ct,selector:"tb-node-empty-config",usesInheritance:!0,ngImport:t,template:"
",isInline:!0}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ct,decorators:[{type:n,args:[{selector:"tb-node-empty-config",template:"
"}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class ft extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.assignCustomerConfigForm}onConfigurationSet(e){this.assignCustomerConfigForm=this.fb.group({customerNamePattern:[e?e.customerNamePattern:null,[_.required,_.pattern(/.*\S.*/)]],createCustomerIfNotExists:[!!e&&e.createCustomerIfNotExists,[]],customerCacheExpiration:[e?e.customerCacheExpiration:null,[_.required,_.min(0)]]})}prepareOutputConfig(e){return e.customerNamePattern=e.customerNamePattern.trim(),e}}e("AssignCustomerConfigComponent",ft),ft.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ft,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),ft.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ft,selector:"tb-action-node-assign-to-customer-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.create-customer-if-not-exists\' | translate }}\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:J.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ft,decorators:[{type:n,args:[{selector:"tb-action-node-assign-to-customer-config",template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.create-customer-if-not-exists\' | translate }}\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class gt extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopeMap=g,this.attributeScopes=Object.keys(g),this.telemetryTypeTranslationsMap=y}configForm(){return this.attributesConfigForm}onConfigurationSet(e){this.attributesConfigForm=this.fb.group({scope:[e?e.scope:null,[_.required]],notifyDevice:[!e||e.notifyDevice,[]],sendAttributesUpdatedNotification:[!!e&&e.sendAttributesUpdatedNotification,[]],updateAttributesOnlyOnValueChange:[!!e&&e.updateAttributesOnlyOnValueChange,[]]}),this.attributesConfigForm.get("scope").valueChanges.subscribe((e=>{e!==g.SHARED_SCOPE&&this.attributesConfigForm.get("notifyDevice").patchValue(!1,{emitEvent:!1}),e===g.CLIENT_SCOPE&&this.attributesConfigForm.get("sendAttributesUpdatedNotification").patchValue(!1,{emitEvent:!1}),this.attributesConfigForm.get("updateAttributesOnlyOnValueChange").patchValue(!1,{emitEvent:!1})}))}}e("AttributesConfigComponent",gt),gt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gt,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),gt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:gt,selector:"tb-action-node-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n
\n \n {{ \'tb.rulenode.update-attributes-only-on-value-change\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.send-attributes-updated-notification\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
\n
\n
\n',dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:ne.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:re.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gt,decorators:[{type:n,args:[{selector:"tb-action-node-attributes-config",template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n
\n \n {{ \'tb.rulenode.update-attributes-only-on-value-change\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.send-attributes-updated-notification\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
\n
\n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class yt extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.serviceType=x.TB_RULE_ENGINE}configForm(){return this.checkPointConfigForm}onConfigurationSet(e){this.checkPointConfigForm=this.fb.group({queueName:[e?e.queueName:null,[_.required]]})}}e("CheckPointConfigComponent",yt),yt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yt,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),yt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:yt,selector:"tb-action-node-check-point-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',dependencies:[{kind:"component",type:oe.QueueAutocompleteComponent,selector:"tb-queue-autocomplete",inputs:["labelText","requiredText","autocompleteHint","subscriptSizing","required","queueType","disabled"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yt,decorators:[{type:n,args:[{selector:"tb-action-node-check-point-config",template:'
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class xt extends f{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=ae(this.store).tbelEnabled,this.scriptLanguage=b,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-details-function"}configForm(){return this.clearAlarmConfigForm}onConfigurationSet(e){this.clearAlarmConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:b.JS,[_.required]],alarmDetailsBuildJs:[e?e.alarmDetailsBuildJs:null,[]],alarmDetailsBuildTbel:[e?e.alarmDetailsBuildTbel:null,[]],alarmType:[e?e.alarmType:null,[_.required]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.clearAlarmConfigForm.get("scriptLang").value;t!==b.TBEL||this.tbelEnabled||(t=b.JS,this.clearAlarmConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.clearAlarmConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.clearAlarmConfigForm.get("alarmDetailsBuildJs").setValidators(t===b.JS?[_.required]:[]),this.clearAlarmConfigForm.get("alarmDetailsBuildJs").updateValueAndValidity({emitEvent:e}),this.clearAlarmConfigForm.get("alarmDetailsBuildTbel").setValidators(t===b.TBEL?[_.required]:[]),this.clearAlarmConfigForm.get("alarmDetailsBuildTbel").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=b.JS)),e}testScript(e){const t=this.clearAlarmConfigForm.get("scriptLang").value,n=t===b.JS?"alarmDetailsBuildJs":"alarmDetailsBuildTbel",r=t===b.JS?"rulenode/clear_alarm_node_script_fn":"rulenode/tbel/clear_alarm_node_script_fn",o=this.clearAlarmConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"json",this.translate.instant("tb.rulenode.details"),"Details",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.clearAlarmConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.clearAlarmConfigForm.get("scriptLang").value===b.JS&&this.jsFuncComponent.validateOnSubmit()}}e("ClearAlarmConfigComponent",xt),xt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xt,deps:[{token:R.Store},{token:O.UntypedFormBuilder},{token:ie.NodeScriptTestService},{token:X.TranslateService}],target:t.ɵɵFactoryTarget.Component}),xt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:xt,selector:"tb-action-node-clear-alarm-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n
\n \n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n',dependencies:[{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:de.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:ce.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ce.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:ye.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xt,decorators:[{type:n,args:[{selector:"tb-action-node-clear-alarm-config",template:'
\n \n \n \n \n \n \n \n
\n \n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder},{type:ie.NodeScriptTestService},{type:X.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class bt extends f{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.alarmSeverities=Object.keys(h),this.alarmSeverityTranslationMap=v,this.separatorKeysCodes=[xe,be,he],this.tbelEnabled=ae(this.store).tbelEnabled,this.scriptLanguage=b,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-details-function"}configForm(){return this.createAlarmConfigForm}onConfigurationSet(e){this.createAlarmConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:b.JS,[_.required]],alarmDetailsBuildJs:[e?e.alarmDetailsBuildJs:null,[]],alarmDetailsBuildTbel:[e?e.alarmDetailsBuildTbel:null,[]],useMessageAlarmData:[!!e&&e.useMessageAlarmData,[]],overwriteAlarmDetails:[!!e&&e.overwriteAlarmDetails,[]],alarmType:[e?e.alarmType:null,[]],severity:[e?e.severity:null,[]],propagate:[!!e&&e.propagate,[]],relationTypes:[e?e.relationTypes:null,[]],propagateToOwner:[!!e&&e.propagateToOwner,[]],propagateToTenant:[!!e&&e.propagateToTenant,[]],dynamicSeverity:!1}),this.createAlarmConfigForm.get("dynamicSeverity").valueChanges.subscribe((e=>{e?this.createAlarmConfigForm.get("severity").patchValue("",{emitEvent:!1}):this.createAlarmConfigForm.get("severity").patchValue(this.alarmSeverities[0],{emitEvent:!1})}))}validatorTriggers(){return["useMessageAlarmData","overwriteAlarmDetails","scriptLang"]}updateValidators(e){const t=this.createAlarmConfigForm.get("useMessageAlarmData").value,n=this.createAlarmConfigForm.get("overwriteAlarmDetails").value;t?(this.createAlarmConfigForm.get("alarmType").setValidators([]),this.createAlarmConfigForm.get("severity").setValidators([])):(this.createAlarmConfigForm.get("alarmType").setValidators([_.required]),this.createAlarmConfigForm.get("severity").setValidators([_.required])),this.createAlarmConfigForm.get("alarmType").updateValueAndValidity({emitEvent:e}),this.createAlarmConfigForm.get("severity").updateValueAndValidity({emitEvent:e});let r=this.createAlarmConfigForm.get("scriptLang").value;r!==b.TBEL||this.tbelEnabled||(r=b.JS,this.createAlarmConfigForm.get("scriptLang").patchValue(r,{emitEvent:!1}),setTimeout((()=>{this.createAlarmConfigForm.updateValueAndValidity({emitEvent:!0})})));const o=!1===t||!0===n;this.createAlarmConfigForm.get("alarmDetailsBuildJs").setValidators(o&&r===b.JS?[_.required]:[]),this.createAlarmConfigForm.get("alarmDetailsBuildTbel").setValidators(o&&r===b.TBEL?[_.required]:[]),this.createAlarmConfigForm.get("alarmDetailsBuildJs").updateValueAndValidity({emitEvent:e}),this.createAlarmConfigForm.get("alarmDetailsBuildTbel").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=b.JS)),e}testScript(e){const t=this.createAlarmConfigForm.get("scriptLang").value,n=t===b.JS?"alarmDetailsBuildJs":"alarmDetailsBuildTbel",r=t===b.JS?"rulenode/create_alarm_node_script_fn":"rulenode/tbel/create_alarm_node_script_fn",o=this.createAlarmConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"json",this.translate.instant("tb.rulenode.details"),"Details",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.createAlarmConfigForm.get(n).setValue(e),this.changeScript.emit())}))}removeKey(e,t){const n=this.createAlarmConfigForm.get(t).value,r=n.indexOf(e);r>=0&&(n.splice(r,1),this.createAlarmConfigForm.get(t).setValue(n,{emitEvent:!0}))}addKey(e,t){const n=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.createAlarmConfigForm.get(t).value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.createAlarmConfigForm.get(t).setValue(e,{emitEvent:!0}))}n&&(n.value="")}onValidate(){const e=this.createAlarmConfigForm.get("useMessageAlarmData").value,t=this.createAlarmConfigForm.get("overwriteAlarmDetails").value;if(!e||t){this.createAlarmConfigForm.get("scriptLang").value===b.JS&&this.jsFuncComponent.validateOnSubmit()}}}e("CreateAlarmConfigComponent",bt),bt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:bt,deps:[{token:R.Store},{token:O.UntypedFormBuilder},{token:ie.NodeScriptTestService},{token:X.TranslateService}],target:t.ɵɵFactoryTarget.Component}),bt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:bt,selector:"tb-action-node-create-alarm-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-message-alarm-data\' | translate }}\n \n \n {{ \'tb.rulenode.overwrite-alarm-details\' | translate }}\n \n
\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-alarm-severity-pattern\' | translate }}\n \n \n tb.rulenode.alarm-severity\n \n \n {{ alarmSeverityTranslationMap.get(severity) | translate }}\n \n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n tb.rulenode.alarm-severity-pattern\n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.propagate\' | translate }}\n \n
\n \n tb.rulenode.relation-types-list\n \n \n {{key}}\n close\n \n \n \n tb.rulenode.relation-types-list-hint\n \n
\n \n {{ \'tb.rulenode.propagate-to-owner\' | translate }}\n \n \n {{ \'tb.rulenode.propagate-to-tenant\' | translate }}\n \n
\n
\n',dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:de.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:ce.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ce.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:J.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:ve.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:ve.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:ve.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:ve.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:ye.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:Ce.SafePipe,name:"safe"},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:bt,decorators:[{type:n,args:[{selector:"tb-action-node-create-alarm-config",template:'
\n \n {{ \'tb.rulenode.use-message-alarm-data\' | translate }}\n \n \n {{ \'tb.rulenode.overwrite-alarm-details\' | translate }}\n \n
\n \n \n \n \n \n \n \n
\n \n
\n
\n
\n \n tb.rulenode.alarm-type\n \n \n {{ \'tb.rulenode.alarm-type-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-alarm-severity-pattern\' | translate }}\n \n \n tb.rulenode.alarm-severity\n \n \n {{ alarmSeverityTranslationMap.get(severity) | translate }}\n \n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n tb.rulenode.alarm-severity-pattern\n \n \n {{ \'tb.rulenode.alarm-severity-required\' | translate }}\n \n \n \n \n {{ \'tb.rulenode.propagate\' | translate }}\n \n
\n \n tb.rulenode.relation-types-list\n \n \n {{key}}\n close\n \n \n \n tb.rulenode.relation-types-list-hint\n \n
\n \n {{ \'tb.rulenode.propagate-to-owner\' | translate }}\n \n \n {{ \'tb.rulenode.propagate-to-tenant\' | translate }}\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder},{type:ie.NodeScriptTestService},{type:X.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class ht extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(C),this.directionTypeTranslations=F,this.entityType=k}configForm(){return this.createRelationConfigForm}onConfigurationSet(e){this.createRelationConfigForm=this.fb.group({direction:[e?e.direction:null,[_.required]],entityType:[e?e.entityType:null,[_.required]],entityNamePattern:[e?e.entityNamePattern:null,[]],entityTypePattern:[e?e.entityTypePattern:null,[]],relationType:[e?e.relationType:null,[_.required]],createEntityIfNotExists:[!!e&&e.createEntityIfNotExists,[]],removeCurrentRelations:[!!e&&e.removeCurrentRelations,[]],changeOriginatorToRelatedEntity:[!!e&&e.changeOriginatorToRelatedEntity,[]],entityCacheExpiration:[e?e.entityCacheExpiration:null,[_.required,_.min(0)]]})}validatorTriggers(){return["entityType"]}updateValidators(e){const t=this.createRelationConfigForm.get("entityType").value;t?this.createRelationConfigForm.get("entityNamePattern").setValidators([_.required,_.pattern(/.*\S.*/)]):this.createRelationConfigForm.get("entityNamePattern").setValidators([]),!t||t!==k.DEVICE&&t!==k.ASSET?this.createRelationConfigForm.get("entityTypePattern").setValidators([]):this.createRelationConfigForm.get("entityTypePattern").setValidators([_.required,_.pattern(/.*\S.*/)]),this.createRelationConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e}),this.createRelationConfigForm.get("entityTypePattern").updateValueAndValidity({emitEvent:e})}prepareOutputConfig(e){return e.entityNamePattern=e.entityNamePattern?e.entityNamePattern.trim():null,e.entityTypePattern=e.entityTypePattern?e.entityTypePattern.trim():null,e}}e("CreateRelationConfigComponent",ht),ht.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ht,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),ht.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ht,selector:"tb-action-node-create-relation-config",usesInheritance:!0,ngImport:t,template:'
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.entity-type-pattern\n \n \n {{ \'tb.rulenode.entity-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n {{ \'tb.rulenode.create-entity-if-not-exists\' | translate }}\n \n
tb.rulenode.create-entity-if-not-exists-hint
\n
\n \n {{ \'tb.rulenode.remove-current-relations\' | translate }}\n \n
tb.rulenode.remove-current-relations-hint
\n \n {{ \'tb.rulenode.change-originator-to-related-entity\' | translate }}\n \n
tb.rulenode.change-originator-to-related-entity-hint
\n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Fe.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"component",type:J.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ht,decorators:[{type:n,args:[{selector:"tb-action-node-create-relation-config",template:'
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.entity-type-pattern\n \n \n {{ \'tb.rulenode.entity-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n {{ \'tb.rulenode.create-entity-if-not-exists\' | translate }}\n \n
tb.rulenode.create-entity-if-not-exists-hint
\n
\n \n {{ \'tb.rulenode.remove-current-relations\' | translate }}\n \n
tb.rulenode.remove-current-relations-hint
\n \n {{ \'tb.rulenode.change-originator-to-related-entity\' | translate }}\n \n
tb.rulenode.change-originator-to-related-entity-hint
\n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class vt extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(C),this.directionTypeTranslations=F,this.entityType=k}configForm(){return this.deleteRelationConfigForm}onConfigurationSet(e){this.deleteRelationConfigForm=this.fb.group({deleteForSingleEntity:[!!e&&e.deleteForSingleEntity,[]],direction:[e?e.direction:null,[_.required]],entityType:[e?e.entityType:null,[]],entityNamePattern:[e?e.entityNamePattern:null,[]],relationType:[e?e.relationType:null,[_.required]],entityCacheExpiration:[e?e.entityCacheExpiration:null,[_.required,_.min(0)]]})}validatorTriggers(){return["deleteForSingleEntity","entityType"]}updateValidators(e){const t=this.deleteRelationConfigForm.get("deleteForSingleEntity").value,n=this.deleteRelationConfigForm.get("entityType").value;t?this.deleteRelationConfigForm.get("entityType").setValidators([_.required]):this.deleteRelationConfigForm.get("entityType").setValidators([]),t&&n?this.deleteRelationConfigForm.get("entityNamePattern").setValidators([_.required,_.pattern(/.*\S.*/)]):this.deleteRelationConfigForm.get("entityNamePattern").setValidators([]),this.deleteRelationConfigForm.get("entityType").updateValueAndValidity({emitEvent:!1}),this.deleteRelationConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e})}prepareOutputConfig(e){return e.entityNamePattern=e.entityNamePattern?e.entityNamePattern.trim():null,e}}e("DeleteRelationConfigComponent",vt),vt.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:vt,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),vt.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:vt,selector:"tb-action-node-delete-relation-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.delete-relation-to-specific-entity\' | translate }}\n \n
tb.rulenode.delete-relation-hint
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Fe.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"component",type:J.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:vt,decorators:[{type:n,args:[{selector:"tb-action-node-delete-relation-config",template:'
\n \n {{ \'tb.rulenode.delete-relation-to-specific-entity\' | translate }}\n \n
tb.rulenode.delete-relation-hint
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.relation-type-pattern\n \n \n {{ \'tb.rulenode.relation-type-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.entity-cache-expiration\n \n \n {{ \'tb.rulenode.entity-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.entity-cache-expiration-range\' | translate }}\n \n tb.rulenode.entity-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class Ct extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.deviceProfile}onConfigurationSet(e){this.deviceProfile=this.fb.group({persistAlarmRulesState:[!!e&&e.persistAlarmRulesState,_.required],fetchAlarmRulesStateOnStart:[!!e&&e.fetchAlarmRulesStateOnStart,_.required]})}}e("DeviceProfileConfigComponent",Ct),Ct.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ct,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ct.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Ct,selector:"tb-device-profile-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.persist-alarm-rules\' | translate }}\n \n \n {{ \'tb.rulenode.fetch-alarm-rules\' | translate }}\n \n
\n',dependencies:[{kind:"component",type:J.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ct,decorators:[{type:n,args:[{selector:"tb-device-profile-config",template:'
\n \n {{ \'tb.rulenode.persist-alarm-rules\' | translate }}\n \n \n {{ \'tb.rulenode.fetch-alarm-rules\' | translate }}\n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class Ft extends f{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=ae(this.store).tbelEnabled,this.scriptLanguage=b,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-generator-function",this.serviceType=x.TB_RULE_ENGINE}configForm(){return this.generatorConfigForm}onConfigurationSet(e){this.generatorConfigForm=this.fb.group({msgCount:[e?e.msgCount:null,[_.required,_.min(0)]],periodInSeconds:[e?e.periodInSeconds:null,[_.required,_.min(1)]],originator:[e?e.originator:null,[]],scriptLang:[e?e.scriptLang:b.JS,[_.required]],jsScript:[e?e.jsScript:null,[]],tbelScript:[e?e.tbelScript:null,[]],queueName:[e?e.queueName:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.generatorConfigForm.get("scriptLang").value;t!==b.TBEL||this.tbelEnabled||(t=b.JS,this.generatorConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.generatorConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.generatorConfigForm.get("jsScript").setValidators(t===b.JS?[_.required]:[]),this.generatorConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.generatorConfigForm.get("tbelScript").setValidators(t===b.TBEL?[_.required]:[]),this.generatorConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=b.JS),e.originatorId&&e.originatorType?e.originator={id:e.originatorId,entityType:e.originatorType}:e.originator=null,delete e.originatorId,delete e.originatorType),e}prepareOutputConfig(e){return e.originator?(e.originatorId=e.originator.id,e.originatorType=e.originator.entityType):(e.originatorId=null,e.originatorType=null),delete e.originator,e}testScript(e){const t=this.generatorConfigForm.get("scriptLang").value,n=t===b.JS?"jsScript":"tbelScript",r=t===b.JS?"rulenode/generator_node_script_fn":"rulenode/tbel/generator_node_script_fn",o=this.generatorConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"generate",this.translate.instant("tb.rulenode.generator"),"Generate",["prevMsg","prevMetadata","prevMsgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.generatorConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.generatorConfigForm.get("scriptLang").value===b.JS&&this.jsFuncComponent.validateOnSubmit()}}var kt;e("GeneratorConfigComponent",Ft),Ft.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ft,deps:[{token:R.Store},{token:O.UntypedFormBuilder},{token:ie.NodeScriptTestService},{token:X.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Ft.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Ft,selector:"tb-action-node-generator-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.message-count\n \n \n {{ \'tb.rulenode.message-count-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-message-count-message\' | translate }}\n \n \n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-seconds-message\' | translate }}\n \n \n
\n \n \n \n
\n\n \n \n\n \n \n \n \n \n \n \n
\n \n
\n
\n',dependencies:[{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ke.EntitySelectComponent,selector:"tb-entity-select",inputs:["allowedEntityTypes","useAliasEntityTypes","required","disabled"]},{kind:"component",type:oe.QueueAutocompleteComponent,selector:"tb-queue-autocomplete",inputs:["labelText","requiredText","autocompleteHint","subscriptSizing","required","queueType","disabled"]},{kind:"component",type:de.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:ce.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ce.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:ye.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ft,decorators:[{type:n,args:[{selector:"tb-action-node-generator-config",template:'
\n \n tb.rulenode.message-count\n \n \n {{ \'tb.rulenode.message-count-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-message-count-message\' | translate }}\n \n \n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-seconds-message\' | translate }}\n \n \n
\n \n \n \n
\n\n \n \n\n \n \n \n \n \n \n \n
\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder},{type:ie.NodeScriptTestService},{type:X.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}}),function(e){e.CUSTOMER="CUSTOMER",e.TENANT="TENANT",e.RELATED="RELATED",e.ALARM_ORIGINATOR="ALARM_ORIGINATOR",e.ENTITY="ENTITY"}(kt||(kt={}));const Lt=new Map([[kt.CUSTOMER,"tb.rulenode.originator-customer"],[kt.TENANT,"tb.rulenode.originator-tenant"],[kt.RELATED,"tb.rulenode.originator-related"],[kt.ALARM_ORIGINATOR,"tb.rulenode.originator-alarm-originator"],[kt.ENTITY,"tb.rulenode.originator-entity"]]),Tt=new Map([[kt.CUSTOMER,"tb.rulenode.originator-customer-desc"],[kt.TENANT,"tb.rulenode.originator-tenant-desc"],[kt.RELATED,"tb.rulenode.originator-related-entity-desc"],[kt.ALARM_ORIGINATOR,"tb.rulenode.originator-alarm-originator-desc"],[kt.ENTITY,"tb.rulenode.originator-entity-by-name-pattern-desc"]]),It=[L.createdTime,L.name,{value:"type",name:"tb.rulenode.profile-name",keyName:"originatorProfileName"},L.firstName,L.lastName,L.email,L.title,L.country,L.state,L.city,L.address,L.address2,L.zip,L.phone,L.label,{value:"id",name:"tb.rulenode.id",keyName:"id"},{value:"additionalInfo",name:"tb.rulenode.additional-info",keyName:"additionalInfo"}],Nt=new Map([["type","profileName"],["createdTime","createdTime"],["name","name"],["firstName","firstName"],["lastName","lastName"],["email","email"],["title","title"],["country","country"],["state","state"],["city","city"],["address","address"],["address2","address2"],["zip","zip"],["phone","phone"],["label","label"],["id","id"],["additionalInfo","additionalInfo"]]);var St;!function(e){e.CIRCLE="CIRCLE",e.POLYGON="POLYGON"}(St||(St={}));const qt=new Map([[St.CIRCLE,"tb.rulenode.perimeter-circle"],[St.POLYGON,"tb.rulenode.perimeter-polygon"]]);var At;!function(e){e.MILLISECONDS="MILLISECONDS",e.SECONDS="SECONDS",e.MINUTES="MINUTES",e.HOURS="HOURS",e.DAYS="DAYS"}(At||(At={}));const Mt=new Map([[At.MILLISECONDS,"tb.rulenode.time-unit-milliseconds"],[At.SECONDS,"tb.rulenode.time-unit-seconds"],[At.MINUTES,"tb.rulenode.time-unit-minutes"],[At.HOURS,"tb.rulenode.time-unit-hours"],[At.DAYS,"tb.rulenode.time-unit-days"]]);var Et;!function(e){e.METER="METER",e.KILOMETER="KILOMETER",e.FOOT="FOOT",e.MILE="MILE",e.NAUTICAL_MILE="NAUTICAL_MILE"}(Et||(Et={}));const Gt=new Map([[Et.METER,"tb.rulenode.range-unit-meter"],[Et.KILOMETER,"tb.rulenode.range-unit-kilometer"],[Et.FOOT,"tb.rulenode.range-unit-foot"],[Et.MILE,"tb.rulenode.range-unit-mile"],[Et.NAUTICAL_MILE,"tb.rulenode.range-unit-nautical-mile"]]);var Dt;!function(e){e.ID="ID",e.TITLE="TITLE",e.COUNTRY="COUNTRY",e.STATE="STATE",e.CITY="CITY",e.ZIP="ZIP",e.ADDRESS="ADDRESS",e.ADDRESS2="ADDRESS2",e.PHONE="PHONE",e.EMAIL="EMAIL",e.ADDITIONAL_INFO="ADDITIONAL_INFO"}(Dt||(Dt={}));const Vt=new Map([[Dt.ID,"tb.rulenode.entity-details-id"],[Dt.TITLE,"tb.rulenode.entity-details-title"],[Dt.COUNTRY,"tb.rulenode.entity-details-country"],[Dt.STATE,"tb.rulenode.entity-details-state"],[Dt.CITY,"tb.rulenode.entity-details-city"],[Dt.ZIP,"tb.rulenode.entity-details-zip"],[Dt.ADDRESS,"tb.rulenode.entity-details-address"],[Dt.ADDRESS2,"tb.rulenode.entity-details-address2"],[Dt.PHONE,"tb.rulenode.entity-details-phone"],[Dt.EMAIL,"tb.rulenode.entity-details-email"],[Dt.ADDITIONAL_INFO,"tb.rulenode.entity-details-additional_info"]]);var wt;!function(e){e.FIRST="FIRST",e.LAST="LAST",e.ALL="ALL"}(wt||(wt={}));const Pt=new Map([[wt.FIRST,"tb.rulenode.first"],[wt.LAST,"tb.rulenode.last"],[wt.ALL,"tb.rulenode.all"]]),Rt=new Map([[wt.FIRST,"tb.rulenode.first-mode-hint"],[wt.LAST,"tb.rulenode.last-mode-hint"],[wt.ALL,"tb.rulenode.all-mode-hint"]]);var Ot,_t;!function(e){e.ASC="ASC",e.DESC="DESC"}(Ot||(Ot={})),function(e){e.ATTRIBUTES="ATTRIBUTES",e.LATEST_TELEMETRY="LATEST_TELEMETRY",e.FIELDS="FIELDS"}(_t||(_t={}));const Bt=new Map([[_t.ATTRIBUTES,"tb.rulenode.attributes"],[_t.LATEST_TELEMETRY,"tb.rulenode.latest-telemetry"],[_t.FIELDS,"tb.rulenode.fields"]]),Kt=new Map([[_t.ATTRIBUTES,"tb.rulenode.add-mapped-attribute-to"],[_t.LATEST_TELEMETRY,"tb.rulenode.add-mapped-latest-telemetry-to"],[_t.FIELDS,"tb.rulenode.add-mapped-fields-to"]]),zt=new Map([[Ot.ASC,"tb.rulenode.ascending"],[Ot.DESC,"tb.rulenode.descending"]]);var Ut;!function(e){e.STANDARD="STANDARD",e.FIFO="FIFO"}(Ut||(Ut={}));const Ht=new Map([[Ut.STANDARD,"tb.rulenode.sqs-queue-standard"],[Ut.FIFO,"tb.rulenode.sqs-queue-fifo"]]),jt=["anonymous","basic","cert.PEM"],$t=new Map([["anonymous","tb.rulenode.credentials-anonymous"],["basic","tb.rulenode.credentials-basic"],["cert.PEM","tb.rulenode.credentials-pem"]]),Qt=["sas","cert.PEM"],Jt=new Map([["sas","tb.rulenode.credentials-sas"],["cert.PEM","tb.rulenode.credentials-pem"]]);var Yt;!function(e){e.GET="GET",e.POST="POST",e.PUT="PUT",e.DELETE="DELETE"}(Yt||(Yt={}));const Wt=["US-ASCII","ISO-8859-1","UTF-8","UTF-16BE","UTF-16LE","UTF-16"],Zt=new Map([["US-ASCII","tb.rulenode.charset-us-ascii"],["ISO-8859-1","tb.rulenode.charset-iso-8859-1"],["UTF-8","tb.rulenode.charset-utf-8"],["UTF-16BE","tb.rulenode.charset-utf-16be"],["UTF-16LE","tb.rulenode.charset-utf-16le"],["UTF-16","tb.rulenode.charset-utf-16"]]);var Xt;!function(e){e.CUSTOM="CUSTOM",e.ADD="ADD",e.SUB="SUB",e.MULT="MULT",e.DIV="DIV",e.SIN="SIN",e.SINH="SINH",e.COS="COS",e.COSH="COSH",e.TAN="TAN",e.TANH="TANH",e.ACOS="ACOS",e.ASIN="ASIN",e.ATAN="ATAN",e.ATAN2="ATAN2",e.EXP="EXP",e.EXPM1="EXPM1",e.SQRT="SQRT",e.CBRT="CBRT",e.GET_EXP="GET_EXP",e.HYPOT="HYPOT",e.LOG="LOG",e.LOG10="LOG10",e.LOG1P="LOG1P",e.CEIL="CEIL",e.FLOOR="FLOOR",e.FLOOR_DIV="FLOOR_DIV",e.FLOOR_MOD="FLOOR_MOD",e.ABS="ABS",e.MIN="MIN",e.MAX="MAX",e.POW="POW",e.SIGNUM="SIGNUM",e.RAD="RAD",e.DEG="DEG"}(Xt||(Xt={}));const en=new Map([[Xt.CUSTOM,{value:Xt.CUSTOM,name:"Custom Function",description:"Use this function to specify complex mathematical expression.",minArgs:1,maxArgs:16}],[Xt.ADD,{value:Xt.ADD,name:"Addition",description:"x + y",minArgs:2,maxArgs:2}],[Xt.SUB,{value:Xt.SUB,name:"Subtraction",description:"x - y",minArgs:2,maxArgs:2}],[Xt.MULT,{value:Xt.MULT,name:"Multiplication",description:"x * y",minArgs:2,maxArgs:2}],[Xt.DIV,{value:Xt.DIV,name:"Division",description:"x / y",minArgs:2,maxArgs:2}],[Xt.SIN,{value:Xt.SIN,name:"Sine",description:"Returns the trigonometric sine of an angle in radians.",minArgs:1,maxArgs:1}],[Xt.SINH,{value:Xt.SINH,name:"Hyperbolic sine",description:"Returns the hyperbolic sine of an argument.",minArgs:1,maxArgs:1}],[Xt.COS,{value:Xt.COS,name:"Cosine",description:"Returns the trigonometric cosine of an angle in radians.",minArgs:1,maxArgs:1}],[Xt.COSH,{value:Xt.COSH,name:"Hyperbolic cosine",description:"Returns the hyperbolic cosine of an argument.",minArgs:1,maxArgs:1}],[Xt.TAN,{value:Xt.TAN,name:"Tangent",description:"Returns the trigonometric tangent of an angle in radians",minArgs:1,maxArgs:1}],[Xt.TANH,{value:Xt.TANH,name:"Hyperbolic tangent",description:"Returns the hyperbolic tangent of an argument",minArgs:1,maxArgs:1}],[Xt.ACOS,{value:Xt.ACOS,name:"Arc cosine",description:"Returns the arc cosine of an argument",minArgs:1,maxArgs:1}],[Xt.ASIN,{value:Xt.ASIN,name:"Arc sine",description:"Returns the arc sine of an argument",minArgs:1,maxArgs:1}],[Xt.ATAN,{value:Xt.ATAN,name:"Arc tangent",description:"Returns the arc tangent of an argument",minArgs:1,maxArgs:1}],[Xt.ATAN2,{value:Xt.ATAN2,name:"2-argument arc tangent",description:"Returns the angle theta from the conversion of rectangular coordinates (x, y) to polar coordinates (r, theta)",minArgs:2,maxArgs:2}],[Xt.EXP,{value:Xt.EXP,name:"Exponential",description:"Returns Euler's number e raised to the power of an argument",minArgs:1,maxArgs:1}],[Xt.EXPM1,{value:Xt.EXPM1,name:"Exponential minus one",description:"Returns Euler's number e raised to the power of an argument minus one",minArgs:1,maxArgs:1}],[Xt.SQRT,{value:Xt.SQRT,name:"Square",description:"Returns the correctly rounded positive square root of an argument",minArgs:1,maxArgs:1}],[Xt.CBRT,{value:Xt.CBRT,name:"Cube root",description:"Returns the cube root of an argument",minArgs:1,maxArgs:1}],[Xt.GET_EXP,{value:Xt.GET_EXP,name:"Get exponent",description:"Returns the unbiased exponent used in the representation of an argument",minArgs:1,maxArgs:1}],[Xt.HYPOT,{value:Xt.HYPOT,name:"Square root",description:"Returns the square root of the squares of the arguments",minArgs:2,maxArgs:2}],[Xt.LOG,{value:Xt.LOG,name:"Logarithm",description:"Returns the natural logarithm of an argument",minArgs:1,maxArgs:1}],[Xt.LOG10,{value:Xt.LOG10,name:"Base 10 logarithm",description:"Returns the base 10 logarithm of an argument",minArgs:1,maxArgs:1}],[Xt.LOG1P,{value:Xt.LOG1P,name:"Logarithm of the sum",description:"Returns the natural logarithm of the sum of an argument",minArgs:1,maxArgs:1}],[Xt.CEIL,{value:Xt.CEIL,name:"Ceiling",description:"Returns the smallest (closest to negative infinity) of an argument",minArgs:1,maxArgs:1}],[Xt.FLOOR,{value:Xt.FLOOR,name:"Floor",description:"Returns the largest (closest to positive infinity) of an argument",minArgs:1,maxArgs:1}],[Xt.FLOOR_DIV,{value:Xt.FLOOR_DIV,name:"Floor division",description:"Returns the largest (closest to positive infinity) of the arguments",minArgs:2,maxArgs:2}],[Xt.FLOOR_MOD,{value:Xt.FLOOR_MOD,name:"Floor modulus",description:"Returns the floor modulus of the arguments",minArgs:2,maxArgs:2}],[Xt.ABS,{value:Xt.ABS,name:"Absolute",description:"Returns the absolute value of an argument",minArgs:1,maxArgs:1}],[Xt.MIN,{value:Xt.MIN,name:"Min",description:"Returns the smaller of the arguments",minArgs:2,maxArgs:2}],[Xt.MAX,{value:Xt.MAX,name:"Max",description:"Returns the greater of the arguments",minArgs:2,maxArgs:2}],[Xt.POW,{value:Xt.POW,name:"Raise to a power",description:"Returns the value of the first argument raised to the power of the second argument",minArgs:2,maxArgs:2}],[Xt.SIGNUM,{value:Xt.SIGNUM,name:"Sign of a real number",description:"Returns the signum function of the argument",minArgs:1,maxArgs:1}],[Xt.RAD,{value:Xt.RAD,name:"Radian",description:"Converts an angle measured in degrees to an approximately equivalent angle measured in radians",minArgs:1,maxArgs:1}],[Xt.DEG,{value:Xt.DEG,name:"Degrees",description:"Converts an angle measured in radians to an approximately equivalent angle measured in degrees.",minArgs:1,maxArgs:1}]]);var tn,nn,rn;!function(e){e.MESSAGE_BODY="MESSAGE_BODY",e.MESSAGE_METADATA="MESSAGE_METADATA",e.ATTRIBUTE="ATTRIBUTE",e.TIME_SERIES="TIME_SERIES",e.CONSTANT="CONSTANT"}(tn||(tn={})),function(e){e.MESSAGE_BODY="MESSAGE_BODY",e.MESSAGE_METADATA="MESSAGE_METADATA",e.ATTRIBUTE="ATTRIBUTE",e.TIME_SERIES="TIME_SERIES"}(nn||(nn={})),function(e){e.DATA="DATA",e.METADATA="METADATA"}(rn||(rn={}));const on=new Map([[rn.DATA,"tb.rulenode.message-to-metadata"],[rn.METADATA,"tb.rulenode.metadata-to-message"]]),an=(new Map([[rn.DATA,"tb.rulenode.from-message"],[rn.METADATA,"tb.rulenode.from-metadata"]]),new Map([[rn.DATA,"tb.rulenode.message"],[rn.METADATA,"tb.rulenode.metadata"]])),ln=new Map([[rn.DATA,"tb.rulenode.message"],[rn.METADATA,"tb.rulenode.message-metadata"]]),sn=new Map([[tn.MESSAGE_BODY,{name:"tb.rulenode.message-body-type",description:"Fetch argument value from incoming message"}],[tn.MESSAGE_METADATA,{name:"tb.rulenode.message-metadata-type",description:"Fetch argument value from incoming message metadata"}],[tn.ATTRIBUTE,{name:"tb.rulenode.attribute-type",description:"Fetch attribute value from database"}],[tn.TIME_SERIES,{name:"tb.rulenode.time-series-type",description:"Fetch latest time-series value from database"}],[tn.CONSTANT,{name:"tb.rulenode.constant-type",description:"Define constant value"}]]),mn=new Map([[nn.MESSAGE_BODY,{name:"tb.rulenode.message-body-type",description:"Add result to the outgoing message"}],[nn.MESSAGE_METADATA,{name:"tb.rulenode.message-metadata-type",description:"Add result to the outgoing message metadata"}],[nn.ATTRIBUTE,{name:"tb.rulenode.attribute-type",description:"Store result as an entity attribute in the database"}],[nn.TIME_SERIES,{name:"tb.rulenode.time-series-type",description:"Store result as an entity time-series in the database"}]]),pn=["x","y","z","a","b","c","d","k","l","m","n","o","p","r","s","t"];var un,dn;!function(e){e.SHARED_SCOPE="SHARED_SCOPE",e.SERVER_SCOPE="SERVER_SCOPE",e.CLIENT_SCOPE="CLIENT_SCOPE"}(un||(un={})),function(e){e.SHARED_SCOPE="SHARED_SCOPE",e.SERVER_SCOPE="SERVER_SCOPE"}(dn||(dn={}));const cn=new Map([[un.SHARED_SCOPE,"tb.rulenode.shared-scope"],[un.SERVER_SCOPE,"tb.rulenode.server-scope"],[un.CLIENT_SCOPE,"tb.rulenode.client-scope"]]);class fn extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.perimeterType=St,this.perimeterTypes=Object.keys(St),this.perimeterTypeTranslationMap=qt,this.rangeUnits=Object.keys(Et),this.rangeUnitTranslationMap=Gt,this.timeUnits=Object.keys(At),this.timeUnitsTranslationMap=Mt}configForm(){return this.geoActionConfigForm}onConfigurationSet(e){this.geoActionConfigForm=this.fb.group({latitudeKeyName:[e?e.latitudeKeyName:null,[_.required]],longitudeKeyName:[e?e.longitudeKeyName:null,[_.required]],perimeterType:[e?e.perimeterType:null,[_.required]],fetchPerimeterInfoFromMessageMetadata:[!!e&&e.fetchPerimeterInfoFromMessageMetadata,[]],perimeterKeyName:[e?e.perimeterKeyName:null,[]],centerLatitude:[e?e.centerLatitude:null,[]],centerLongitude:[e?e.centerLatitude:null,[]],range:[e?e.range:null,[]],rangeUnit:[e?e.rangeUnit:null,[]],polygonsDefinition:[e?e.polygonsDefinition:null,[]],minInsideDuration:[e?e.minInsideDuration:null,[_.required,_.min(1),_.max(2147483647)]],minInsideDurationTimeUnit:[e?e.minInsideDurationTimeUnit:null,[_.required]],minOutsideDuration:[e?e.minOutsideDuration:null,[_.required,_.min(1),_.max(2147483647)]],minOutsideDurationTimeUnit:[e?e.minOutsideDurationTimeUnit:null,[_.required]]})}validatorTriggers(){return["fetchPerimeterInfoFromMessageMetadata","perimeterType"]}updateValidators(e){const t=this.geoActionConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value,n=this.geoActionConfigForm.get("perimeterType").value;t?this.geoActionConfigForm.get("perimeterKeyName").setValidators([_.required]):this.geoActionConfigForm.get("perimeterKeyName").setValidators([]),t||n!==St.CIRCLE?(this.geoActionConfigForm.get("centerLatitude").setValidators([]),this.geoActionConfigForm.get("centerLongitude").setValidators([]),this.geoActionConfigForm.get("range").setValidators([]),this.geoActionConfigForm.get("rangeUnit").setValidators([])):(this.geoActionConfigForm.get("centerLatitude").setValidators([_.required,_.min(-90),_.max(90)]),this.geoActionConfigForm.get("centerLongitude").setValidators([_.required,_.min(-180),_.max(180)]),this.geoActionConfigForm.get("range").setValidators([_.required,_.min(0)]),this.geoActionConfigForm.get("rangeUnit").setValidators([_.required])),t||n!==St.POLYGON?this.geoActionConfigForm.get("polygonsDefinition").setValidators([]):this.geoActionConfigForm.get("polygonsDefinition").setValidators([_.required]),this.geoActionConfigForm.get("perimeterKeyName").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("centerLatitude").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("centerLongitude").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("range").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("rangeUnit").updateValueAndValidity({emitEvent:e}),this.geoActionConfigForm.get("polygonsDefinition").updateValueAndValidity({emitEvent:e})}}e("GpsGeoActionConfigComponent",fn),fn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:fn,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),fn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:fn,selector:"tb-action-node-gps-geofencing-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.latitude-key-name\n \n \n {{ \'tb.rulenode.latitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.longitude-key-name\n \n \n {{ \'tb.rulenode.longitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.perimeter-type\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n \n {{ \'tb.rulenode.fetch-perimeter-info-from-metadata\' | translate }}\n \n \n tb.rulenode.perimeter-key-name\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.circle-center-latitude\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n tb.rulenode.circle-center-longitude\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.range\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n tb.rulenode.range-units\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n
\n
\n
\n \n tb.rulenode.polygon-definition\n \n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.min-inside-duration\n \n \n {{ \'tb.rulenode.min-inside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-inside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n tb.rulenode.min-outside-duration\n \n \n {{ \'tb.rulenode.min-outside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-outside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n',dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:J.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:fn,decorators:[{type:n,args:[{selector:"tb-action-node-gps-geofencing-config",template:'
\n \n tb.rulenode.latitude-key-name\n \n \n {{ \'tb.rulenode.latitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.longitude-key-name\n \n \n {{ \'tb.rulenode.longitude-key-name-required\' | translate }}\n \n \n \n tb.rulenode.perimeter-type\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n \n {{ \'tb.rulenode.fetch-perimeter-info-from-metadata\' | translate }}\n \n \n tb.rulenode.perimeter-key-name\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.circle-center-latitude\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n tb.rulenode.circle-center-longitude\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.range\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n tb.rulenode.range-units\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n
\n
\n
\n \n tb.rulenode.polygon-definition\n \n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n \n tb.rulenode.min-inside-duration\n \n \n {{ \'tb.rulenode.min-inside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-inside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n tb.rulenode.min-outside-duration\n \n \n {{ \'tb.rulenode.min-outside-duration-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n tb.rulenode.min-outside-duration-time-unit\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class gn extends f{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=ae(this.store).tbelEnabled,this.scriptLanguage=b,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-to-string-function"}configForm(){return this.logConfigForm}onConfigurationSet(e){this.logConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:b.JS,[_.required]],jsScript:[e?e.jsScript:null,[]],tbelScript:[e?e.tbelScript:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.logConfigForm.get("scriptLang").value;t!==b.TBEL||this.tbelEnabled||(t=b.JS,this.logConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.logConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.logConfigForm.get("jsScript").setValidators(t===b.JS?[_.required]:[]),this.logConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.logConfigForm.get("tbelScript").setValidators(t===b.TBEL?[_.required]:[]),this.logConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=b.JS)),e}testScript(e){const t=this.logConfigForm.get("scriptLang").value,n=t===b.JS?"jsScript":"tbelScript",r=t===b.JS?"rulenode/log_node_script_fn":"rulenode/tbel/log_node_script_fn",o=this.logConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"string",this.translate.instant("tb.rulenode.to-string"),"ToString",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.logConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.logConfigForm.get("scriptLang").value===b.JS&&this.jsFuncComponent.validateOnSubmit()}}e("LogConfigComponent",gn),gn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gn,deps:[{token:R.Store},{token:O.UntypedFormBuilder},{token:ie.NodeScriptTestService},{token:X.TranslateService}],target:t.ɵɵFactoryTarget.Component}),gn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:gn,selector:"tb-action-node-log-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n
\n \n
\n
\n',dependencies:[{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:de.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:ce.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ce.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:ye.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gn,decorators:[{type:n,args:[{selector:"tb-action-node-log-config",template:'
\n \n \n \n \n \n \n \n
\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder},{type:ie.NodeScriptTestService},{type:X.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class yn extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.msgCountConfigForm}onConfigurationSet(e){this.msgCountConfigForm=this.fb.group({interval:[e?e.interval:null,[_.required,_.min(1)]],telemetryPrefix:[e?e.telemetryPrefix:null,[_.required]]})}}e("MsgCountConfigComponent",yn),yn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yn,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),yn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:yn,selector:"tb-action-node-msg-count-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.interval-seconds\n \n \n {{ \'tb.rulenode.interval-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-interval-seconds-message\' | translate }}\n \n \n \n tb.rulenode.output-timeseries-key-prefix\n \n \n {{ \'tb.rulenode.output-timeseries-key-prefix-required\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yn,decorators:[{type:n,args:[{selector:"tb-action-node-msg-count-config",template:'
\n \n tb.rulenode.interval-seconds\n \n \n {{ \'tb.rulenode.interval-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-interval-seconds-message\' | translate }}\n \n \n \n tb.rulenode.output-timeseries-key-prefix\n \n \n {{ \'tb.rulenode.output-timeseries-key-prefix-required\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class xn extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.msgDelayConfigForm}onConfigurationSet(e){this.msgDelayConfigForm=this.fb.group({useMetadataPeriodInSecondsPatterns:[!!e&&e.useMetadataPeriodInSecondsPatterns,[]],periodInSeconds:[e?e.periodInSeconds:null,[]],periodInSecondsPattern:[e?e.periodInSecondsPattern:null,[]],maxPendingMsgs:[e?e.maxPendingMsgs:null,[_.required,_.min(1),_.max(1e5)]]})}validatorTriggers(){return["useMetadataPeriodInSecondsPatterns"]}updateValidators(e){this.msgDelayConfigForm.get("useMetadataPeriodInSecondsPatterns").value?(this.msgDelayConfigForm.get("periodInSecondsPattern").setValidators([_.required]),this.msgDelayConfigForm.get("periodInSeconds").setValidators([])):(this.msgDelayConfigForm.get("periodInSecondsPattern").setValidators([]),this.msgDelayConfigForm.get("periodInSeconds").setValidators([_.required,_.min(0)])),this.msgDelayConfigForm.get("periodInSecondsPattern").updateValueAndValidity({emitEvent:e}),this.msgDelayConfigForm.get("periodInSeconds").updateValueAndValidity({emitEvent:e})}}e("MsgDelayConfigComponent",xn),xn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xn,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),xn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:xn,selector:"tb-action-node-msg-delay-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}\n \n
tb.rulenode.use-metadata-period-in-seconds-patterns-hint
\n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-0-seconds-message\' | translate }}\n \n \n \n \n tb.rulenode.period-in-seconds-pattern\n \n \n {{ \'tb.rulenode.period-in-seconds-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n \n tb.rulenode.max-pending-messages\n \n \n {{ \'tb.rulenode.max-pending-messages-required\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:J.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xn,decorators:[{type:n,args:[{selector:"tb-action-node-msg-delay-config",template:'
\n \n {{ \'tb.rulenode.use-metadata-period-in-seconds-patterns\' | translate }}\n \n
tb.rulenode.use-metadata-period-in-seconds-patterns-hint
\n \n tb.rulenode.period-seconds\n \n \n {{ \'tb.rulenode.period-seconds-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-period-0-seconds-message\' | translate }}\n \n \n \n \n tb.rulenode.period-in-seconds-pattern\n \n \n {{ \'tb.rulenode.period-in-seconds-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n \n tb.rulenode.max-pending-messages\n \n \n {{ \'tb.rulenode.max-pending-messages-required\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n {{ \'tb.rulenode.max-pending-messages-range\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class bn extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopes=Object.keys(g),this.telemetryTypeTranslationsMap=y}configForm(){return this.pushToCloudConfigForm}onConfigurationSet(e){this.pushToCloudConfigForm=this.fb.group({scope:[e?e.scope:null,[_.required]]})}}e("PushToCloudConfigComponent",bn),bn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:bn,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),bn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:bn,selector:"tb-action-node-push-to-cloud-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:bn,decorators:[{type:n,args:[{selector:"tb-action-node-push-to-cloud-config",template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class hn extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopes=Object.keys(g),this.telemetryTypeTranslationsMap=y}configForm(){return this.pushToEdgeConfigForm}onConfigurationSet(e){this.pushToEdgeConfigForm=this.fb.group({scope:[e?e.scope:null,[_.required]]})}}e("PushToEdgeConfigComponent",hn),hn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:hn,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),hn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:hn,selector:"tb-action-node-push-to-edge-config",usesInheritance:!0,ngImport:t,template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:hn,decorators:[{type:n,args:[{selector:"tb-action-node-push-to-edge-config",template:'
\n \n attribute.attributes-scope\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class vn extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.rpcReplyConfigForm}onConfigurationSet(e){this.rpcReplyConfigForm=this.fb.group({requestIdMetaDataAttribute:[e?e.requestIdMetaDataAttribute:null,[]]})}}e("RpcReplyConfigComponent",vn),vn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:vn,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),vn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:vn,selector:"tb-action-node-rpc-reply-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.request-id-metadata-attribute\n \n \n
\n',dependencies:[{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:vn,decorators:[{type:n,args:[{selector:"tb-action-node-rpc-reply-config",template:'
\n \n tb.rulenode.request-id-metadata-attribute\n \n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class Cn extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.rpcRequestConfigForm}onConfigurationSet(e){this.rpcRequestConfigForm=this.fb.group({timeoutInSeconds:[e?e.timeoutInSeconds:null,[_.required,_.min(0)]]})}}e("RpcRequestConfigComponent",Cn),Cn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Cn,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Cn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Cn,selector:"tb-action-node-rpc-request-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.timeout-sec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-message\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Cn,decorators:[{type:n,args:[{selector:"tb-action-node-rpc-request-config",template:'
\n \n tb.rulenode.timeout-sec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-message\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class Fn extends T{get required(){return this.requiredValue}set required(e){this.requiredValue=Le(e)}constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.propagateChange=null,this.valueChangeSubscription=null}ngOnInit(){this.ngControl=this.injector.get(B),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.kvListFormGroup=this.fb.group({}),this.kvListFormGroup.addControl("keyVals",this.fb.array([]))}keyValsFormArray(){return this.kvListFormGroup.get("keyVals")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.kvListFormGroup.disable({emitEvent:!1}):this.kvListFormGroup.enable({emitEvent:!1})}writeValue(e){this.valueChangeSubscription&&this.valueChangeSubscription.unsubscribe();const t=[];if(e)for(const n of Object.keys(e))Object.prototype.hasOwnProperty.call(e,n)&&t.push(this.fb.group({key:[n,[_.required]],value:[e[n],[_.required]]}));this.kvListFormGroup.setControl("keyVals",this.fb.array(t)),this.valueChangeSubscription=this.kvListFormGroup.valueChanges.subscribe((()=>{this.updateModel()}))}removeKeyVal(e){this.kvListFormGroup.get("keyVals").removeAt(e)}addKeyVal(){this.kvListFormGroup.get("keyVals").push(this.fb.group({key:["",[_.required]],value:["",[_.required]]}))}validate(e){const t=this.kvListFormGroup.get("keyVals").value;if(!t.length&&this.required)return{kvMapRequired:!0};if(!this.kvListFormGroup.valid)return{kvFieldsRequired:!0};if(this.uniqueKeyValuePairValidator)for(const e of t)if(e.key===e.value)return{uniqueKeyValuePair:!0};return null}updateModel(){const e=this.kvListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.kvListFormGroup.valid)this.propagateChange(null);else{const t={};e.forEach((e=>{t[e.key]=e.value})),this.propagateChange(t)}}}e("KvMapConfigOldComponent",Fn),Fn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Fn,deps:[{token:R.Store},{token:X.TranslateService},{token:t.Injector},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Fn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Fn,selector:"tb-kv-map-config-old",inputs:{disabled:"disabled",uniqueKeyValuePairValidator:"uniqueKeyValuePairValidator",requiredText:"requiredText",keyText:"keyText",keyRequiredText:"keyRequiredText",valText:"valText",valRequiredText:"valRequiredText",hintText:"hintText",required:"required"},providers:[{provide:K,useExisting:a((()=>Fn)),multi:!0},{provide:z,useExisting:a((()=>Fn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
\n {{ keyText | translate }}\n {{ valText | translate }}\n \n
\n
\n
\n \n \n \n {{ keyRequiredText | translate }}\n \n \n \n \n \n {{ valRequiredText | translate }}\n \n \n \n
\n
\n
\n \n
\n \n
\n
\n',styles:[":host .tb-kv-map-config{margin-bottom:16px}:host .tb-kv-map-config .header{padding-left:5px;padding-right:5px;padding-bottom:5px}:host .tb-kv-map-config .header .cell{padding-left:5px;padding-right:5px;color:#757575;font-size:12px;font-weight:700;white-space:nowrap}:host .tb-kv-map-config .body{padding-left:5px;padding-right:5px;padding-bottom:0;max-height:300px;overflow:auto}:host .tb-kv-map-config .body .cell{padding-left:5px;padding-right:5px}:host .tb-kv-map-config tb-error{display:block;margin-top:-12px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Ne.TbErrorComponent,selector:"tb-error",inputs:["noMargin","error"]},{kind:"component",type:ce.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ce.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:Z.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:Se.DefaultShowHideDirective,selector:" [fxShow], [fxShow.print], [fxShow.xs], [fxShow.sm], [fxShow.md], [fxShow.lg], [fxShow.xl], [fxShow.lt-sm], [fxShow.lt-md], [fxShow.lt-lg], [fxShow.lt-xl], [fxShow.gt-xs], [fxShow.gt-sm], [fxShow.gt-md], [fxShow.gt-lg], [fxHide], [fxHide.print], [fxHide.xs], [fxHide.sm], [fxHide.md], [fxHide.lg], [fxHide.xl], [fxHide.lt-sm], [fxHide.lt-md], [fxHide.lt-lg], [fxHide.lt-xl], [fxHide.gt-xs], [fxHide.gt-sm], [fxHide.gt-md], [fxHide.gt-lg]",inputs:["fxShow","fxShow.print","fxShow.xs","fxShow.sm","fxShow.md","fxShow.lg","fxShow.xl","fxShow.lt-sm","fxShow.lt-md","fxShow.lt-lg","fxShow.lt-xl","fxShow.gt-xs","fxShow.gt-sm","fxShow.gt-md","fxShow.gt-lg","fxHide","fxHide.print","fxHide.xs","fxHide.sm","fxHide.md","fxHide.lg","fxHide.xl","fxHide.lt-sm","fxHide.lt-md","fxHide.lt-lg","fxHide.lt-xl","fxHide.gt-xs","fxHide.gt-sm","fxHide.gt-md","fxHide.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormArrayName,selector:"[formArrayName]",inputs:["formArrayName"]},{kind:"pipe",type:j.AsyncPipe,name:"async"},{kind:"pipe",type:Ce.SafePipe,name:"safe"},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Fn,decorators:[{type:n,args:[{selector:"tb-kv-map-config-old",providers:[{provide:K,useExisting:a((()=>Fn)),multi:!0},{provide:z,useExisting:a((()=>Fn)),multi:!0}],template:'
\n
\n {{ keyText | translate }}\n {{ valText | translate }}\n \n
\n
\n
\n \n \n \n {{ keyRequiredText | translate }}\n \n \n \n \n \n {{ valRequiredText | translate }}\n \n \n \n
\n
\n
\n \n
\n \n
\n
\n',styles:[":host .tb-kv-map-config{margin-bottom:16px}:host .tb-kv-map-config .header{padding-left:5px;padding-right:5px;padding-bottom:5px}:host .tb-kv-map-config .header .cell{padding-left:5px;padding-right:5px;color:#757575;font-size:12px;font-weight:700;white-space:nowrap}:host .tb-kv-map-config .body{padding-left:5px;padding-right:5px;padding-bottom:0;max-height:300px;overflow:auto}:host .tb-kv-map-config .body .cell{padding-left:5px;padding-right:5px}:host .tb-kv-map-config tb-error{display:block;margin-top:-12px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:R.Store},{type:X.TranslateService},{type:t.Injector},{type:O.FormBuilder}]},propDecorators:{disabled:[{type:i}],uniqueKeyValuePairValidator:[{type:i}],requiredText:[{type:i}],keyText:[{type:i}],keyRequiredText:[{type:i}],valText:[{type:i}],valRequiredText:[{type:i}],hintText:[{type:i}],required:[{type:i}]}});class kn extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.saveToCustomTableConfigForm}onConfigurationSet(e){this.saveToCustomTableConfigForm=this.fb.group({tableName:[e?e.tableName:null,[_.required,_.pattern(/.*\S.*/)]],fieldsMapping:[e?e.fieldsMapping:null,[_.required]]})}prepareOutputConfig(e){return e.tableName=e.tableName.trim(),e}}e("SaveToCustomTableConfigComponent",kn),kn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:kn,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),kn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:kn,selector:"tb-action-node-custom-table-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.custom-table-name\n \n \n {{ \'tb.rulenode.custom-table-name-required\' | translate }}\n \n tb.rulenode.custom-table-hint\n \n \n \n \n
\n',dependencies:[{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Fn,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:kn,decorators:[{type:n,args:[{selector:"tb-action-node-custom-table-config",template:'
\n \n tb.rulenode.custom-table-name\n \n \n {{ \'tb.rulenode.custom-table-name-required\' | translate }}\n \n tb.rulenode.custom-table-hint\n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class Ln extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.timeseriesConfigForm}onConfigurationSet(e){this.timeseriesConfigForm=this.fb.group({defaultTTL:[e?e.defaultTTL:null,[_.required,_.min(0)]],skipLatestPersistence:[!!e&&e.skipLatestPersistence,[]],useServerTs:[!!e&&e.useServerTs,[]]})}}e("TimeseriesConfigComponent",Ln),Ln.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ln,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Ln.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Ln,selector:"tb-action-node-timeseries-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.default-ttl\n \n \n {{ \'tb.rulenode.default-ttl-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-default-ttl-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.skip-latest-persistence\' | translate }}\n \n \n {{ \'tb.rulenode.use-server-ts\' | translate }}\n \n
tb.rulenode.use-server-ts-hint
\n
\n',dependencies:[{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:J.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ln,decorators:[{type:n,args:[{selector:"tb-action-node-timeseries-config",template:'
\n \n tb.rulenode.default-ttl\n \n \n {{ \'tb.rulenode.default-ttl-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-default-ttl-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.skip-latest-persistence\' | translate }}\n \n \n {{ \'tb.rulenode.use-server-ts\' | translate }}\n \n
tb.rulenode.use-server-ts-hint
\n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class Tn extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.unassignCustomerConfigForm}onConfigurationSet(e){this.unassignCustomerConfigForm=this.fb.group({customerNamePattern:[e?e.customerNamePattern:null,[_.required,_.pattern(/.*\S.*/)]],customerCacheExpiration:[e?e.customerCacheExpiration:null,[_.required,_.min(0)]]})}prepareOutputConfig(e){return e.customerNamePattern=e.customerNamePattern.trim(),e}}e("UnassignCustomerConfigComponent",Tn),Tn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Tn,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Tn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Tn,selector:"tb-action-node-un-assign-to-customer-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n',dependencies:[{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Tn,decorators:[{type:n,args:[{selector:"tb-action-node-un-assign-to-customer-config",template:'
\n \n tb.rulenode.customer-name-pattern\n \n \n {{ \'tb.rulenode.customer-name-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.customer-cache-expiration\n \n \n {{ \'tb.rulenode.customer-cache-expiration-required\' | translate }}\n \n \n {{ \'tb.rulenode.customer-cache-expiration-range\' | translate }}\n \n tb.rulenode.customer-cache-expiration-hint\n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class In extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.attributeScopeMap=g,this.attributeScopes=Object.keys(g),this.telemetryTypeTranslationsMap=y,this.separatorKeysCodes=[xe,be,he]}configForm(){return this.deleteAttributesConfigForm}onConfigurationSet(e){this.deleteAttributesConfigForm=this.fb.group({scope:[e?e.scope:null,[_.required]],keys:[e?e.keys:null,[_.required]],sendAttributesDeletedNotification:[!!e&&e.sendAttributesDeletedNotification,[]],notifyDevice:[!!e&&e.notifyDevice,[]]}),this.deleteAttributesConfigForm.get("scope").valueChanges.subscribe((e=>{e!==g.SHARED_SCOPE&&this.deleteAttributesConfigForm.get("notifyDevice").patchValue(!1,{emitEvent:!1})}))}removeKey(e){const t=this.deleteAttributesConfigForm.get("keys").value,n=t.indexOf(e);n>=0&&(t.splice(n,1),this.deleteAttributesConfigForm.get("keys").patchValue(t,{emitEvent:!0}))}addKey(e){const t=e.input;let n=e.value;if((n||"").trim()){n=n.trim();let e=this.deleteAttributesConfigForm.get("keys").value;e&&-1!==e.indexOf(n)||(e||(e=[]),e.push(n),this.deleteAttributesConfigForm.get("keys").patchValue(e,{emitEvent:!0}))}t&&(t.value="")}}e("DeleteAttributesConfigComponent",In),In.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:In,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),In.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:In,selector:"tb-action-node-delete-attributes-config",viewQueries:[{propertyName:"attributeChipList",first:!0,predicate:["attributeChipList"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'attribute.attributes-scope\' | translate }}\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n \n {{ \'tb.rulenode.attributes-keys\' | translate }}\n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.attributes-keys-required\' | translate }}\n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.send-attributes-deleted-notification\' | translate }}\n \n
tb.rulenode.send-attributes-deleted-notification-hint
\n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
tb.rulenode.notify-device-on-delete-hint
\n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:J.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:ve.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:ve.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:ve.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:ve.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:In,decorators:[{type:n,args:[{selector:"tb-action-node-delete-attributes-config",template:'
\n \n {{ \'attribute.attributes-scope\' | translate }}\n \n \n {{ telemetryTypeTranslationsMap.get(scope) | translate }}\n \n \n \n \n {{ \'tb.rulenode.attributes-keys\' | translate }}\n \n \n {{key}}\n close\n \n \n \n {{ \'tb.rulenode.attributes-keys-required\' | translate }}\n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.send-attributes-deleted-notification\' | translate }}\n \n
tb.rulenode.send-attributes-deleted-notification-hint
\n
\n \n {{ \'tb.rulenode.notify-device\' | translate }}\n \n
tb.rulenode.notify-device-on-delete-hint
\n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]},propDecorators:{attributeChipList:[{type:o,args:["attributeChipList"]}]}});class Nn extends T{get function(){return this.functionValue}set function(e){e&&this.functionValue!==e&&(this.functionValue=e,this.setupArgumentsFormGroup(!0))}constructor(e,t){super(e),this.store=e,this.fb=t,this.maxArgs=16,this.minArgs=1,this.displayArgumentName=!1,this.mathFunctionMap=en,this.ArgumentType=tn,this.attributeScopeMap=cn,this.argumentTypeMap=sn,this.arguments=Object.values(tn),this.attributeScope=Object.values(un),this.propagateChange=null,this.valueChangeSubscription=[]}ngOnInit(){this.argumentsFormGroup=this.fb.group({arguments:this.fb.array([])}),this.valueChangeSubscription.push(this.argumentsFormGroup.valueChanges.subscribe((()=>{this.updateModel()}))),this.setupArgumentsFormGroup()}onDrop(e){const t=this.argumentsFormArray,n=t.at(e.previousIndex);t.removeAt(e.previousIndex),t.insert(e.currentIndex,n),this.updateArgumentNames()}get argumentsFormArray(){return this.argumentsFormGroup.get("arguments")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.argumentsFormGroup.disable({emitEvent:!1}):(this.argumentsFormGroup.enable({emitEvent:!1}),this.argumentsFormArray.controls.forEach((e=>this.updateArgumentControlValidators(e))))}ngOnDestroy(){this.valueChangeSubscription.length&&this.valueChangeSubscription.forEach((e=>e.unsubscribe()))}writeValue(e){const t=[];e&&e.forEach(((e,n)=>{t.push(this.createArgumentControl(e,n))})),this.argumentsFormGroup.setControl("arguments",this.fb.array(t),{emitEvent:!1}),this.setupArgumentsFormGroup()}removeArgument(e){this.argumentsFormArray.removeAt(e),this.updateArgumentNames()}addArgument(e=!0){const t=this.argumentsFormArray,n=this.createArgumentControl(null,t.length);t.push(n,{emitEvent:e})}validate(e){return this.argumentsFormGroup.valid?null:{argumentsRequired:!0}}setupArgumentsFormGroup(e=!1){if(this.function&&(this.maxArgs=this.mathFunctionMap.get(this.function).maxArgs,this.minArgs=this.mathFunctionMap.get(this.function).minArgs,this.displayArgumentName=this.function===Xt.CUSTOM),this.argumentsFormGroup){for(this.argumentsFormGroup.get("arguments").setValidators([_.minLength(this.minArgs),_.maxLength(this.maxArgs)]);this.argumentsFormArray.length>this.maxArgs;)this.removeArgument(this.maxArgs-1);for(;this.argumentsFormArray.length{this.updateArgumentControlValidators(n),n.get("attributeScope").updateValueAndValidity({emitEvent:!1}),n.get("defaultValue").updateValueAndValidity({emitEvent:!1})}))),n}updateArgumentControlValidators(e){const t=e.get("type").value;t===tn.ATTRIBUTE?e.get("attributeScope").enable({emitEvent:!1}):e.get("attributeScope").disable({emitEvent:!1}),t&&t!==tn.CONSTANT?e.get("defaultValue").enable({emitEvent:!1}):e.get("defaultValue").disable({emitEvent:!1})}updateArgumentNames(){this.argumentsFormArray.controls.forEach(((e,t)=>{e.get("name").setValue(pn[t])}))}updateModel(){const e=this.argumentsFormArray.value;e.length&&this.argumentsFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}}e("ArgumentsMapConfigComponent",Nn),Nn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Nn,deps:[{token:R.Store},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Nn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Nn,selector:"tb-arguments-map-config",inputs:{disabled:"disabled",function:"function"},providers:[{provide:K,useExisting:a((()=>Nn)),multi:!0},{provide:z,useExisting:a((()=>Nn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n\n
\n \n \n
\n \n
\n {{argumentControl.get(\'name\').value}}.\n
\n \n tb.rulenode.argument-source-field-input\n \n \n {{ argumentTypeMap.get(argumentControl.get(\'type\').value)?.name | translate }}\n \n \n {{ argumentTypeMap.get(argument).name | translate }}\n \n {{ argumentTypeMap.get(argument).description }}\n \n \n \n \n tb.rulenode.argument-source-field-input-required\n \n \n
\n \n tb.rulenode.argument-key-field-input\n \n \n help\n \n \n tb.rulenode.argument-key-field-input-required\n \n \n \n tb.rulenode.constant-value-field-input\n \n \n tb.rulenode.constant-value-field-input-required\n \n \n \n tb.rulenode.default-value-field-input\n \n \n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n tb.rulenode.attribute-scope-field-input-required\n \n \n
\n \n
\n
\n
\n
\n
\n
\n tb.rulenode.no-arguments-prompt\n
\n \n
\n',styles:[":host .mat-mdc-list-item.tb-argument{border:solid rgba(0,0,0,.25) 1px;border-radius:4px;padding:10px 0;margin-bottom:16px}:host .arguments-list{padding:0}\n"],dependencies:[{kind:"directive",type:j.NgClass,selector:"[ngClass]",inputs:["class","ngClass"]},{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ce.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ce.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"directive",type:ee.MatSelectTrigger,selector:"mat-select-trigger"},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:qe.MatList,selector:"mat-list",exportAs:["matList"]},{kind:"component",type:qe.MatListItem,selector:"mat-list-item, a[mat-list-item], button[mat-list-item]",inputs:["activated"],exportAs:["matListItem"]},{kind:"directive",type:Ae.CdkDropList,selector:"[cdkDropList], cdk-drop-list",inputs:["cdkDropListConnectedTo","cdkDropListData","cdkDropListOrientation","id","cdkDropListLockAxis","cdkDropListDisabled","cdkDropListSortingDisabled","cdkDropListEnterPredicate","cdkDropListSortPredicate","cdkDropListAutoScrollDisabled","cdkDropListAutoScrollStep"],outputs:["cdkDropListDropped","cdkDropListEntered","cdkDropListExited","cdkDropListSorted"],exportAs:["cdkDropList"]},{kind:"directive",type:Ae.CdkDrag,selector:"[cdkDrag]",inputs:["cdkDragData","cdkDragLockAxis","cdkDragRootElement","cdkDragBoundary","cdkDragStartDelay","cdkDragFreeDragPosition","cdkDragDisabled","cdkDragConstrainPosition","cdkDragPreviewClass","cdkDragPreviewContainer"],outputs:["cdkDragStarted","cdkDragReleased","cdkDragEnded","cdkDragEntered","cdkDragExited","cdkDragDropped","cdkDragMoved"],exportAs:["cdkDrag"]},{kind:"directive",type:Ae.CdkDragHandle,selector:"[cdkDragHandle]",inputs:["cdkDragHandleDisabled"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:Z.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:Se.DefaultClassDirective,selector:" [ngClass], [ngClass.xs], [ngClass.sm], [ngClass.md], [ngClass.lg], [ngClass.xl], [ngClass.lt-sm], [ngClass.lt-md], [ngClass.lt-lg], [ngClass.lt-xl], [ngClass.gt-xs], [ngClass.gt-sm], [ngClass.gt-md], [ngClass.gt-lg]",inputs:["ngClass","ngClass.xs","ngClass.sm","ngClass.md","ngClass.lg","ngClass.xl","ngClass.lt-sm","ngClass.lt-md","ngClass.lt-lg","ngClass.lt-xl","ngClass.gt-xs","ngClass.gt-sm","ngClass.gt-md","ngClass.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormArrayName,selector:"[formArrayName]",inputs:["formArrayName"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Nn,decorators:[{type:n,args:[{selector:"tb-arguments-map-config",providers:[{provide:K,useExisting:a((()=>Nn)),multi:!0},{provide:z,useExisting:a((()=>Nn)),multi:!0}],template:'
\n\n
\n \n \n
\n \n
\n {{argumentControl.get(\'name\').value}}.\n
\n \n tb.rulenode.argument-source-field-input\n \n \n {{ argumentTypeMap.get(argumentControl.get(\'type\').value)?.name | translate }}\n \n \n {{ argumentTypeMap.get(argument).name | translate }}\n \n {{ argumentTypeMap.get(argument).description }}\n \n \n \n \n tb.rulenode.argument-source-field-input-required\n \n \n
\n \n tb.rulenode.argument-key-field-input\n \n \n help\n \n \n tb.rulenode.argument-key-field-input-required\n \n \n \n tb.rulenode.constant-value-field-input\n \n \n tb.rulenode.constant-value-field-input-required\n \n \n \n tb.rulenode.default-value-field-input\n \n \n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n tb.rulenode.attribute-scope-field-input-required\n \n \n
\n \n
\n
\n
\n
\n
\n
\n tb.rulenode.no-arguments-prompt\n
\n \n
\n',styles:[":host .mat-mdc-list-item.tb-argument{border:solid rgba(0,0,0,.25) 1px;border-radius:4px;padding:10px 0;margin-bottom:16px}:host .arguments-list{padding:0}\n"]}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder}]},propDecorators:{disabled:[{type:i}],function:[{type:i}]}});class Sn extends T{get required(){return this.requiredValue}set required(e){this.requiredValue=Le(e)}constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.searchText="",this.dirty=!1,this.mathOperation=[...en.values()],this.propagateChange=null}ngOnInit(){this.mathFunctionForm=this.fb.group({operation:[""]}),this.filteredOptions=this.mathFunctionForm.get("operation").valueChanges.pipe(Me((e=>{let t;t="string"==typeof e&&Xt[e]?Xt[e]:null,this.updateView(t)})),Ee((e=>(this.searchText=e||"",e?this._filter(e):this.mathOperation.slice()))))}_filter(e){const t=e.toLowerCase();return this.mathOperation.filter((e=>e.name.toLowerCase().includes(t)||e.value.toLowerCase().includes(t)))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.mathFunctionForm.disable({emitEvent:!1}):this.mathFunctionForm.enable({emitEvent:!1})}mathFunctionDisplayFn(e){if(e){const t=en.get(e);return t.value+" | "+t.name}return""}writeValue(e){this.modelValue=e,this.mathFunctionForm.get("operation").setValue(e,{emitEvent:!1}),this.dirty=!0}updateView(e){this.modelValue!==e&&(this.modelValue=e,this.propagateChange(this.modelValue))}onFocus(){this.dirty&&(this.mathFunctionForm.get("operation").updateValueAndValidity({onlySelf:!0}),this.dirty=!1)}clear(){this.mathFunctionForm.get("operation").patchValue(""),setTimeout((()=>{this.operationInput.nativeElement.blur(),this.operationInput.nativeElement.focus()}),0)}}e("MathFunctionAutocompleteComponent",Sn),Sn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Sn,deps:[{token:R.Store},{token:X.TranslateService},{token:t.Injector},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Sn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Sn,selector:"tb-math-function-autocomplete",inputs:{required:"required",disabled:"disabled"},providers:[{provide:K,useExisting:a((()=>Sn)),multi:!0}],viewQueries:[{propertyName:"operationInput",first:!0,predicate:["operationInput"],descendants:!0,static:!0}],usesInheritance:!0,ngImport:t,template:'\n tb.rulenode.functions-field-input\n \n \n \n \n \n \n {{ option.description }}\n \n \n \n tb.rulenode.no-option-found\n \n \n\n',dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ce.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:Re.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple","hideSingleSelectionIndicator"],exportAs:["matAutocomplete"]},{kind:"directive",type:Re.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.AsyncPipe,name:"async"},{kind:"pipe",type:Oe.HighlightPipe,name:"highlight"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Sn,decorators:[{type:n,args:[{selector:"tb-math-function-autocomplete",providers:[{provide:K,useExisting:a((()=>Sn)),multi:!0}],template:'\n tb.rulenode.functions-field-input\n \n \n \n \n \n \n {{ option.description }}\n \n \n \n tb.rulenode.no-option-found\n \n \n\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:X.TranslateService},{type:t.Injector},{type:O.UntypedFormBuilder}]},propDecorators:{required:[{type:i}],disabled:[{type:i}],operationInput:[{type:o,args:["operationInput",{static:!0}]}]}});class qn extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.MathFunction=Xt,this.ArgumentTypeResult=nn,this.argumentTypeResultMap=mn,this.attributeScopeMap=cn,this.argumentsResult=Object.values(nn),this.attributeScopeResult=Object.values(dn)}configForm(){return this.mathFunctionConfigForm}onConfigurationSet(e){this.mathFunctionConfigForm=this.fb.group({operation:[e?e.operation:null,[_.required]],arguments:[e?e.arguments:null,[_.required]],customFunction:[e?e.customFunction:"",[_.required]],result:this.fb.group({type:[e?e.result.type:null,[_.required]],attributeScope:[e?e.result.attributeScope:null,[_.required]],key:[e?e.result.key:"",[_.required]],resultValuePrecision:[e?e.result.resultValuePrecision:0],addToBody:[!!e&&e.result.addToBody],addToMetadata:[!!e&&e.result.addToMetadata]})})}updateValidators(e){const t=this.mathFunctionConfigForm.get("operation").value,n=this.mathFunctionConfigForm.get("result.type").value;t===Xt.CUSTOM?(this.mathFunctionConfigForm.get("customFunction").enable({emitEvent:!1}),null===this.mathFunctionConfigForm.get("customFunction").value&&this.mathFunctionConfigForm.get("customFunction").patchValue("(x - 32) / 1.8",{emitEvent:!1})):this.mathFunctionConfigForm.get("customFunction").disable({emitEvent:!1}),n===nn.ATTRIBUTE?this.mathFunctionConfigForm.get("result.attributeScope").enable({emitEvent:!1}):this.mathFunctionConfigForm.get("result.attributeScope").disable({emitEvent:!1}),this.mathFunctionConfigForm.get("customFunction").updateValueAndValidity({emitEvent:e}),this.mathFunctionConfigForm.get("result.attributeScope").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["operation","result.type"]}}e("MathFunctionConfigComponent",qn),qn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:qn,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),qn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:qn,selector:"tb-action-node-math-function-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n tb.rulenode.argument-tile\n \n \n
\n
\n {{\'tb.rulenode.custom-expression-field-input\' | translate }} *\n \n \n \n tb.rulenode.custom-expression-field-input-required\n \n tb.rulenode.custom-expression-field-input-hint\n \n
\n
\n tb.rulenode.result-title\n
\n \n tb.rulenode.type-field-input\n \n \n {{ argumentTypeResultMap.get(mathFunctionConfigForm.get(\'result.type\').value)?.name | translate }}\n \n \n {{ argumentTypeResultMap.get(argument).name | translate }}\n \n {{ argumentTypeResultMap.get(argument).description }}\n \n \n \n \n tb.rulenode.type-field-input-required\n \n \n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n \n tb.rulenode.key-field-input\n \n help\n \n tb.rulenode.key-field-input-required\n \n \n
\n
\n \n tb.rulenode.number-floating-point-field-input\n \n \n \n
\n
\n \n {{\'tb.rulenode.add-to-message-field-input\' | translate }}\n \n \n {{\'tb.rulenode.add-to-metadata-field-input\' | translate}}\n \n
\n
\n
\n
\n',styles:[":host ::ng-deep .fields-group{padding:0 16px 8px;margin:10px 0;border:1px groove rgba(0,0,0,.25);border-radius:4px}:host ::ng-deep .fields-group .mat-mdc-form-field .mat-mdc-form-field-infix{width:100%}:host ::ng-deep .fields-group legend{color:#000000b3;width:-moz-fit-content;width:fit-content}:host ::ng-deep .fields-group legend+*{display:block}:host ::ng-deep .fields-group legend+*.no-margin-top{margin-top:0}\n"],dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:J.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"directive",type:ee.MatSelectTrigger,selector:"mat-select-trigger"},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:Z.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:O.FormGroupName,selector:"[formGroupName]",inputs:["formGroupName"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Nn,selector:"tb-arguments-map-config",inputs:["disabled","function"]},{kind:"component",type:Sn,selector:"tb-math-function-autocomplete",inputs:["required","disabled"]},{kind:"pipe",type:Ce.SafePipe,name:"safe"},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:qn,decorators:[{type:n,args:[{selector:"tb-action-node-math-function-config",template:'
\n \n \n
\n tb.rulenode.argument-tile\n \n \n
\n
\n {{\'tb.rulenode.custom-expression-field-input\' | translate }} *\n \n \n \n tb.rulenode.custom-expression-field-input-required\n \n tb.rulenode.custom-expression-field-input-hint\n \n
\n
\n tb.rulenode.result-title\n
\n \n tb.rulenode.type-field-input\n \n \n {{ argumentTypeResultMap.get(mathFunctionConfigForm.get(\'result.type\').value)?.name | translate }}\n \n \n {{ argumentTypeResultMap.get(argument).name | translate }}\n \n {{ argumentTypeResultMap.get(argument).description }}\n \n \n \n \n tb.rulenode.type-field-input-required\n \n \n
\n \n tb.rulenode.attribute-scope-field-input\n \n \n {{ attributeScopeMap.get(scope) | translate }}\n \n \n \n \n tb.rulenode.key-field-input\n \n help\n \n tb.rulenode.key-field-input-required\n \n \n
\n
\n \n tb.rulenode.number-floating-point-field-input\n \n \n \n
\n
\n \n {{\'tb.rulenode.add-to-message-field-input\' | translate }}\n \n \n {{\'tb.rulenode.add-to-metadata-field-input\' | translate}}\n \n
\n
\n
\n
\n',styles:[":host ::ng-deep .fields-group{padding:0 16px 8px;margin:10px 0;border:1px groove rgba(0,0,0,.25);border-radius:4px}:host ::ng-deep .fields-group .mat-mdc-form-field .mat-mdc-form-field-infix{width:100%}:host ::ng-deep .fields-group legend{color:#000000b3;width:-moz-fit-content;width:fit-content}:host ::ng-deep .fields-group legend+*{display:block}:host ::ng-deep .fields-group legend+*.no-margin-top{margin-top:0}\n"]}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class An{constructor(){this.textAlign="left"}}e("ExampleHintComponent",An),An.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:An,deps:[],target:t.ɵɵFactoryTarget.Component}),An.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:An,selector:"tb-example-hint",inputs:{hintText:"hintText",popupHelpLink:"popupHelpLink",textAlign:"textAlign"},ngImport:t,template:'
\n
\n
\n
\n
\n',styles:[":host .space-between{display:flex;justify-content:space-between;gap:20px}:host .space-between .see-example{display:flex;flex-shrink:0}:host .hint-text{width:100%}\n"],dependencies:[{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:$e.HelpPopupComponent,selector:"[tb-help-popup], [tb-help-popup-content]",inputs:["tb-help-popup","tb-help-popup-content","trigger-text","trigger-style","tb-help-popup-placement","tb-help-popup-style","hintMode"]},{kind:"pipe",type:Ce.SafePipe,name:"safe"},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:An,decorators:[{type:n,args:[{selector:"tb-example-hint",template:'
\n
\n
\n
\n
\n',styles:[":host .space-between{display:flex;justify-content:space-between;gap:20px}:host .space-between .see-example{display:flex;flex-shrink:0}:host .hint-text{width:100%}\n"]}]}],propDecorators:{hintText:[{type:i}],popupHelpLink:[{type:i}],textAlign:[{type:i}]}});class Mn{constructor(e,t){this.injector=e,this.fb=t,this.propagateChange=()=>{},this.destroy$=new Ke,this.disabled=!1,this.uniqueKeyValuePairValidator=!1,this.required=!1,this.duplicateValuesValidator=e=>e.controls.key.value===e.controls.value.value&&e.controls.key.value&&e.controls.value.value?{uniqueKeyValuePair:!0}:null,this.oneMapRequiredValidator=e=>e.get("keyVals").value.length,this.propagateNestedErrors=e=>{if(this.kvListFormGroup&&this.kvListFormGroup.get("keyVals")&&"VALID"===this.kvListFormGroup.get("keyVals")?.status)return null;const t={};if(this.kvListFormGroup&&this.kvListFormGroup.setErrors(null),e instanceof U||e instanceof H){if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;for(const n of Object.keys(e.controls)){const r=this.propagateNestedErrors(e.controls[n]);if(r&&Object.keys(r).length)for(const e of Object.keys(r))t[e]=!0}return t}if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;return le(t,{})?null:t}}ngOnInit(){this.ngControl=this.injector.get(B),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.kvListFormGroup=this.fb.group({keyVals:this.fb.array([])},{validators:[this.propagateNestedErrors,this.oneMapRequiredValidator]}),this.kvListFormGroup.valueChanges.pipe(ze(this.destroy$)).subscribe((()=>{this.updateModel()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}keyValsFormArray(){return this.kvListFormGroup.get("keyVals")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.kvListFormGroup.disable({emitEvent:!1}):this.kvListFormGroup.enable({emitEvent:!1})}writeValue(e){const t=Object.keys(e).map((t=>({key:t,value:e[t]})));if(this.keyValsFormArray().length===t.length)this.keyValsFormArray().patchValue(t,{emitEvent:!1});else{const e=[];t.forEach((t=>{e.push(this.fb.group({key:[t.key,[_.required,_.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],value:[t.value,[_.required,_.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]},{validators:this.uniqueKeyValuePairValidator?[this.duplicateValuesValidator]:[]}))})),this.kvListFormGroup.setControl("keyVals",this.fb.array(e,this.propagateNestedErrors),{emitEvent:!1})}}removeKeyVal(e){this.keyValsFormArray().removeAt(e)}addKeyVal(){this.keyValsFormArray().push(this.fb.group({key:["",[_.required,_.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],value:["",[_.required,_.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]},{validators:this.uniqueKeyValuePairValidator?[this.duplicateValuesValidator]:[]}))}validate(){const e=this.kvListFormGroup.get("keyVals").value;if(!e.length&&this.required)return{kvMapRequired:!0};if(!this.kvListFormGroup.valid)return{kvFieldsRequired:!0};if(this.uniqueKeyValuePairValidator)for(const t of e)if(t.key===t.value)return{uniqueKeyValuePair:!0};return null}updateModel(){const e=this.kvListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.kvListFormGroup.valid)this.propagateChange(null);else{const t={};e.forEach((e=>{t[e.key]=e.value})),this.propagateChange(t)}}}e("KvMapConfigComponent",Mn),Mn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Mn,deps:[{token:t.Injector},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Mn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Mn,selector:"tb-kv-map-config",inputs:{disabled:"disabled",uniqueKeyValuePairValidator:"uniqueKeyValuePairValidator",labelText:"labelText",requiredText:"requiredText",keyText:"keyText",keyRequiredText:"keyRequiredText",valText:"valText",valRequiredText:"valRequiredText",hintText:"hintText",popupHelpLink:"popupHelpLink",required:"required"},providers:[{provide:K,useExisting:a((()=>Mn)),multi:!0},{provide:z,useExisting:a((()=>Mn)),multi:!0}],ngImport:t,template:'
\n
\n
{{ labelText }}
\n
\n {{ requiredText }}\n
\n
\n tb.rulenode.map-fields-required\n
\n
\n {{ \'tb.key-val.unique-key-value-pair-error\' | translate:\n {\n valText: valText,\n keyText: keyText\n } }}\n
\n
\n
\n
\n
\n
{{ keyText }}
\n
{{ valText }}
\n
\n
\n
\n
\n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n \n
\n \n
\n',styles:[":host .field-space{flex:1 1 50%}:host .actions-header{width:40px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ce.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ce.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:An,selector:"tb-example-hint",inputs:["hintText","popupHelpLink","textAlign"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),Be([I()],Mn.prototype,"disabled",void 0),Be([I()],Mn.prototype,"uniqueKeyValuePairValidator",void 0),Be([I()],Mn.prototype,"required",void 0),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Mn,decorators:[{type:n,args:[{selector:"tb-kv-map-config",providers:[{provide:K,useExisting:a((()=>Mn)),multi:!0},{provide:z,useExisting:a((()=>Mn)),multi:!0}],template:'
\n
\n
{{ labelText }}
\n
\n {{ requiredText }}\n
\n
\n tb.rulenode.map-fields-required\n
\n
\n {{ \'tb.key-val.unique-key-value-pair-error\' | translate:\n {\n valText: valText,\n keyText: keyText\n } }}\n
\n
\n
\n
\n
\n
{{ keyText }}
\n
{{ valText }}
\n
\n
\n
\n
\n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n \n
\n \n
\n',styles:[":host .field-space{flex:1 1 50%}:host .actions-header{width:40px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:t.Injector},{type:O.FormBuilder}]},propDecorators:{disabled:[{type:i}],uniqueKeyValuePairValidator:[{type:i}],labelText:[{type:i}],requiredText:[{type:i}],keyText:[{type:i}],keyRequiredText:[{type:i}],valText:[{type:i}],valRequiredText:[{type:i}],hintText:[{type:i}],popupHelpLink:[{type:i}],required:[{type:i}]}});class En extends T{get required(){return this.requiredValue}set required(e){this.requiredValue=Le(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.values(C),this.directionTypeTranslations=F,this.entityType=k,this.propagateChange=null}ngOnInit(){this.deviceRelationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[_.required]],maxLevel:[null,[_.min(1)]],relationType:[null],deviceTypes:[null,[_.required]]}),this.deviceRelationsQueryFormGroup.valueChanges.subscribe((e=>{this.deviceRelationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.deviceRelationsQueryFormGroup.disable({emitEvent:!1}):this.deviceRelationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.deviceRelationsQueryFormGroup.reset(e,{emitEvent:!1})}}e("DeviceRelationsQueryConfigComponent",En),En.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:En,deps:[{token:R.Store},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),En.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:En,selector:"tb-device-relations-query-config",inputs:{disabled:"disabled",required:"required"},providers:[{provide:K,useExisting:a((()=>En)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n {{ \'tb.rulenode.max-relation-level-error\' | translate }}\n \n \n
\n
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n \n \n help\n \n
\n',styles:[":host .last-level-slide-toggle{margin:8px 0 24px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Qe.EntitySubTypeListComponent,selector:"tb-entity-subtype-list",inputs:["required","floatLabel","label","disabled","entityType","emptyInputPlaceholder","filledInputPlaceholder","appearance","subscriptSizing","additionalClasses"]},{kind:"component",type:Je.RelationTypeAutocompleteComponent,selector:"tb-relation-type-autocomplete",inputs:["showLabel","additionalClasses","appearance","required","disabled","subscriptSizing"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:ne.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:re.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:En,decorators:[{type:n,args:[{selector:"tb-device-relations-query-config",providers:[{provide:K,useExisting:a((()=>En)),multi:!0}],template:'
\n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n {{ \'tb.rulenode.max-relation-level-error\' | translate }}\n \n \n
\n
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n \n \n help\n \n
\n',styles:[":host .last-level-slide-toggle{margin:8px 0 24px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder}]},propDecorators:{disabled:[{type:i}],required:[{type:i}]}});class Gn extends T{get required(){return this.requiredValue}set required(e){this.requiredValue=Le(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.values(C),this.directionTypeTranslations=F,this.propagateChange=null}ngOnInit(){this.relationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[_.required]],maxLevel:[null,[_.min(1)]],filters:[null]}),this.relationsQueryFormGroup.valueChanges.subscribe((e=>{this.relationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.relationsQueryFormGroup.disable({emitEvent:!1}):this.relationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.relationsQueryFormGroup.reset(e||{},{emitEvent:!1})}}e("RelationsQueryConfigComponent",Gn),Gn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Gn,deps:[{token:R.Store},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Gn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Gn,selector:"tb-relations-query-config",inputs:{disabled:"disabled",required:"required"},providers:[{provide:K,useExisting:a((()=>Gn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.relations-query
\n
\n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n {{ \'tb.rulenode.max-relation-level-error\' | translate }}\n \n \n
\n
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n
\n
\n
relation.relation-filters
\n \n \n
\n
\n',dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:ne.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:re.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:Ye.RelationFiltersComponent,selector:"tb-relation-filters",inputs:["disabled","allowedEntityTypes"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Gn,decorators:[{type:n,args:[{selector:"tb-relations-query-config",providers:[{provide:K,useExisting:a((()=>Gn)),multi:!0}],template:'
\n
tb.rulenode.relations-query
\n
\n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n {{ \'tb.rulenode.max-relation-level-error\' | translate }}\n \n \n
\n
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n
\n
\n
relation.relation-filters
\n \n \n
\n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder}]},propDecorators:{disabled:[{type:i}],required:[{type:i}]}});class Dn extends T{get required(){return this.requiredValue}set required(e){this.requiredValue=Le(e)}constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.truncate=n,this.fb=r,this.placeholder="tb.rulenode.add-message-type",this.separatorKeysCodes=[xe,be,he],this.messageTypes=[],this.messageTypesList=[],this.searchText="",this.propagateChange=e=>{},this.messageTypeConfigForm=this.fb.group({messageType:[null]});for(const e of Object.keys(N))this.messageTypesList.push({name:S.get(N[e]),value:e})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}ngOnInit(){this.filteredMessageTypes=this.messageTypeConfigForm.get("messageType").valueChanges.pipe(Ge(""),Ee((e=>e||"")),De((e=>this.fetchMessageTypes(e))),Ve())}setDisabledState(e){this.disabled=e,this.disabled?this.messageTypeConfigForm.disable({emitEvent:!1}):this.messageTypeConfigForm.enable({emitEvent:!1})}writeValue(e){this.searchText="",this.messageTypes.length=0,e&&e.forEach((e=>{const t=this.messageTypesList.find((t=>t.value===e));t?this.messageTypes.push({name:t.name,value:t.value}):this.messageTypes.push({name:e,value:e})}))}displayMessageTypeFn(e){return e?e.name:void 0}textIsNotEmpty(e){return e&&e.length>0}createMessageType(e,t){e.preventDefault(),this.transformMessageType(t)}add(e){this.transformMessageType(e.value)}fetchMessageTypes(e){if(this.searchText=e,this.searchText&&this.searchText.length){const e=this.searchText.toUpperCase();return Ue(this.messageTypesList.filter((t=>t.name.toUpperCase().includes(e))))}return Ue(this.messageTypesList)}transformMessageType(e){if((e||"").trim()){let t;const n=e.trim(),r=this.messageTypesList.find((e=>e.name===n));t=r?{name:r.name,value:r.value}:{name:n,value:n},t&&this.addMessageType(t)}this.clear("")}remove(e){const t=this.messageTypes.indexOf(e);t>=0&&(this.messageTypes.splice(t,1),this.updateModel())}selected(e){this.addMessageType(e.option.value),this.clear("")}addMessageType(e){-1===this.messageTypes.findIndex((t=>t.value===e.value))&&(this.messageTypes.push(e),this.updateModel())}onFocus(){this.messageTypeConfigForm.get("messageType").updateValueAndValidity({onlySelf:!0,emitEvent:!0})}clear(e=""){this.messageTypeInput.nativeElement.value=e,this.messageTypeConfigForm.get("messageType").patchValue(null,{emitEvent:!0}),setTimeout((()=>{this.messageTypeInput.nativeElement.blur(),this.messageTypeInput.nativeElement.focus()}),0)}updateModel(){const e=this.messageTypes.map((e=>e.value));this.required?(this.chipList.errorState=!e.length,this.propagateChange(e.length>0?e:null)):(this.chipList.errorState=!1,this.propagateChange(e))}}e("MessageTypesConfigComponent",Dn),Dn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Dn,deps:[{token:R.Store},{token:X.TranslateService},{token:q.TruncatePipe},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Dn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Dn,selector:"tb-message-types-config",inputs:{required:"required",label:"label",placeholder:"placeholder",disabled:"disabled"},providers:[{provide:K,useExisting:a((()=>Dn)),multi:!0}],viewQueries:[{propertyName:"chipList",first:!0,predicate:["chipList"],descendants:!0},{propertyName:"matAutocomplete",first:!0,predicate:["messageTypeAutocomplete"],descendants:!0},{propertyName:"messageTypeInput",first:!0,predicate:["messageTypeInput"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'\n {{ label }}\n \n \n {{messageType.name}}\n close\n \n \n \n \n \n \n \n \n
\n
\n tb.rulenode.no-message-types-found\n
\n \n \n {{ \'tb.rulenode.no-message-type-matching\' | translate :\n {messageType: truncate.transform(searchText, true, 6, '...')}\n }}\n \n \n \n tb.rulenode.create-new-message-type\n \n
\n
\n
\n help\n \n {{ \'tb.rulenode.select-message-types-required\' | translate }}\n \n
\n',dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:Re.MatAutocomplete,selector:"mat-autocomplete",inputs:["disableRipple","hideSingleSelectionIndicator"],exportAs:["matAutocomplete"]},{kind:"directive",type:Re.MatAutocompleteTrigger,selector:"input[matAutocomplete], textarea[matAutocomplete]",exportAs:["matAutocompleteTrigger"]},{kind:"directive",type:Re.MatAutocompleteOrigin,selector:"[matAutocompleteOrigin]",exportAs:["matAutocompleteOrigin"]},{kind:"component",type:ve.MatChipGrid,selector:"mat-chip-grid",inputs:["tabIndex","disabled","placeholder","required","value","errorStateMatcher"],outputs:["change","valueChange"]},{kind:"directive",type:ve.MatChipInput,selector:"input[matChipInputFor]",inputs:["matChipInputFor","matChipInputAddOnBlur","matChipInputSeparatorKeyCodes","placeholder","id","disabled"],outputs:["matChipInputTokenEnd"],exportAs:["matChipInput","matChipInputFor"]},{kind:"directive",type:ve.MatChipRemove,selector:"[matChipRemove]"},{kind:"component",type:ve.MatChipRow,selector:"mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]",inputs:["color","disabled","disableRipple","tabIndex","editable"],outputs:["edited"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:j.AsyncPipe,name:"async"},{kind:"pipe",type:Oe.HighlightPipe,name:"highlight"},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Dn,decorators:[{type:n,args:[{selector:"tb-message-types-config",providers:[{provide:K,useExisting:a((()=>Dn)),multi:!0}],template:'\n {{ label }}\n \n \n {{messageType.name}}\n close\n \n \n \n \n \n \n \n \n
\n
\n tb.rulenode.no-message-types-found\n
\n \n \n {{ \'tb.rulenode.no-message-type-matching\' | translate :\n {messageType: truncate.transform(searchText, true, 6, '...')}\n }}\n \n \n \n tb.rulenode.create-new-message-type\n \n
\n
\n
\n help\n \n {{ \'tb.rulenode.select-message-types-required\' | translate }}\n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:X.TranslateService},{type:q.TruncatePipe},{type:O.FormBuilder}]},propDecorators:{required:[{type:i}],label:[{type:i}],placeholder:[{type:i}],disabled:[{type:i}],chipList:[{type:o,args:["chipList",{static:!1}]}],matAutocomplete:[{type:o,args:["messageTypeAutocomplete",{static:!1}]}],messageTypeInput:[{type:o,args:["messageTypeInput",{static:!1}]}]}});class Vn extends T{get required(){return this.requiredValue}set required(e){this.requiredValue=Le(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.subscriptions=[],this.disableCertPemCredentials=!1,this.passwordFieldRequired=!0,this.allCredentialsTypes=jt,this.credentialsTypeTranslationsMap=$t,this.propagateChange=e=>{}}ngOnInit(){this.credentialsConfigFormGroup=this.fb.group({type:[null,[_.required]],username:[null,[]],password:[null,[]],caCert:[null,[]],caCertFileName:[null,[]],privateKey:[null,[]],privateKeyFileName:[null,[]],cert:[null,[]],certFileName:[null,[]]}),this.subscriptions.push(this.credentialsConfigFormGroup.valueChanges.subscribe((()=>{this.updateView()}))),this.subscriptions.push(this.credentialsConfigFormGroup.get("type").valueChanges.subscribe((()=>{this.credentialsTypeChanged()})))}ngOnChanges(e){for(const t of Object.keys(e)){const n=e[t];if(!n.firstChange&&n.currentValue!==n.previousValue&&n.currentValue&&"disableCertPemCredentials"===t){"cert.PEM"===this.credentialsConfigFormGroup.get("type").value&&setTimeout((()=>{this.credentialsConfigFormGroup.get("type").patchValue("anonymous",{emitEvent:!0})}))}}}ngOnDestroy(){this.subscriptions.forEach((e=>e.unsubscribe()))}writeValue(e){se(e)&&(this.credentialsConfigFormGroup.reset(e,{emitEvent:!1}),this.updateValidators())}setDisabledState(e){e?this.credentialsConfigFormGroup.disable({emitEvent:!1}):(this.credentialsConfigFormGroup.enable({emitEvent:!1}),this.updateValidators())}updateView(){let e=this.credentialsConfigFormGroup.value;const t=e.type;switch(t){case"anonymous":e={type:t};break;case"basic":e={type:t,username:e.username,password:e.password};break;case"cert.PEM":delete e.username}this.propagateChange(e)}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}validate(e){return this.credentialsConfigFormGroup.valid?null:{credentialsConfig:{valid:!1}}}credentialsTypeChanged(){this.credentialsConfigFormGroup.patchValue({username:null,password:null,caCert:null,caCertFileName:null,privateKey:null,privateKeyFileName:null,cert:null,certFileName:null}),this.updateValidators()}updateValidators(e=!1){const t=this.credentialsConfigFormGroup.get("type").value;switch(e&&this.credentialsConfigFormGroup.reset({type:t},{emitEvent:!1}),this.credentialsConfigFormGroup.setValidators([]),this.credentialsConfigFormGroup.get("username").setValidators([]),this.credentialsConfigFormGroup.get("password").setValidators([]),t){case"anonymous":break;case"basic":this.credentialsConfigFormGroup.get("username").setValidators([_.required]),this.credentialsConfigFormGroup.get("password").setValidators(this.passwordFieldRequired?[_.required]:[]);break;case"cert.PEM":this.credentialsConfigFormGroup.setValidators([this.requiredFilesSelected(_.required,[["caCert","caCertFileName"],["privateKey","privateKeyFileName","cert","certFileName"]])])}this.credentialsConfigFormGroup.get("username").updateValueAndValidity({emitEvent:e}),this.credentialsConfigFormGroup.get("password").updateValueAndValidity({emitEvent:e}),this.credentialsConfigFormGroup.updateValueAndValidity({emitEvent:e})}requiredFilesSelected(e,t=null){return n=>{t||(t=[Object.keys(n.controls)]);return n?.controls&&t.some((t=>t.every((t=>!e(n.controls[t])))))?null:{notAllRequiredFilesSelected:!0}}}}e("CredentialsConfigComponent",Vn),Vn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Vn,deps:[{token:R.Store},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Vn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Vn,selector:"tb-credentials-config",inputs:{required:"required",disableCertPemCredentials:"disableCertPemCredentials",passwordFieldRequired:"passwordFieldRequired"},providers:[{provide:K,useExisting:a((()=>Vn)),multi:!0},{provide:z,useExisting:a((()=>Vn)),multi:!0}],usesInheritance:!0,usesOnChanges:!0,ngImport:t,template:'
\n \n \n tb.rulenode.credentials\n \n {{ credentialsTypeTranslationsMap.get(credentialsConfigFormGroup.get(\'type\').value) | translate }}\n \n \n \n \n tb.rulenode.credentials-type\n \n \n {{ credentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.username\n \n \n {{ \'tb.rulenode.username-required\' | translate }}\n \n \n \n tb.rulenode.password\n \n \n \n {{ \'tb.rulenode.password-required\' | translate }}\n \n \n \n \n
{{ \'tb.rulenode.credentials-pem-hint\' | translate }}
\n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n
\n
\n
\n
\n
\n',dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:j.NgSwitch,selector:"[ngSwitch]",inputs:["ngSwitch"]},{kind:"directive",type:j.NgSwitchCase,selector:"[ngSwitchCase]",inputs:["ngSwitchCase"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:We.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{kind:"component",type:We.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{kind:"directive",type:We.MatExpansionPanelTitle,selector:"mat-panel-title"},{kind:"directive",type:We.MatExpansionPanelDescription,selector:"mat-panel-description"},{kind:"directive",type:We.MatExpansionPanelContent,selector:"ng-template[matExpansionPanelContent]"},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Ze.FileInputComponent,selector:"tb-file-input",inputs:["label","hint","accept","noFileText","inputId","allowedExtensions","dropLabel","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Xe.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Vn,decorators:[{type:n,args:[{selector:"tb-credentials-config",providers:[{provide:K,useExisting:a((()=>Vn)),multi:!0},{provide:z,useExisting:a((()=>Vn)),multi:!0}],template:'
\n \n \n tb.rulenode.credentials\n \n {{ credentialsTypeTranslationsMap.get(credentialsConfigFormGroup.get(\'type\').value) | translate }}\n \n \n \n \n tb.rulenode.credentials-type\n \n \n {{ credentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.username\n \n \n {{ \'tb.rulenode.username-required\' | translate }}\n \n \n \n tb.rulenode.password\n \n \n \n {{ \'tb.rulenode.password-required\' | translate }}\n \n \n \n \n
{{ \'tb.rulenode.credentials-pem-hint\' | translate }}
\n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n
\n
\n
\n
\n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder}]},propDecorators:{required:[{type:i}],disableCertPemCredentials:[{type:i}],passwordFieldRequired:[{type:i}]}});const wn=new l("WindowToken","undefined"!=typeof window&&window.document?{providedIn:"root",factory:()=>window}:{providedIn:"root",factory:()=>{}});class Pn{constructor(e,t,n){this.ngZone=e,this.document=t,this.window=n,this.copySubject=new Ke,this.copyResponse$=this.copySubject.asObservable(),this.config={}}configure(e){this.config=e}copy(e){if(!this.isSupported||!e)return this.pushCopyResponse({isSuccess:!1,content:e});const t=this.copyFromContent(e);return t?this.pushCopyResponse({content:e,isSuccess:t}):this.pushCopyResponse({isSuccess:!1,content:e})}get isSupported(){return!!this.document.queryCommandSupported&&!!this.document.queryCommandSupported("copy")&&!!this.window}isTargetValid(e){if(e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement){if(e.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');return!0}throw new Error("Target should be input or textarea")}copyFromInputElement(e,t=!0){try{this.selectTarget(e);const n=this.copyText();return this.clearSelection(t?e:void 0,this.window),n&&this.isCopySuccessInIE11()}catch(e){return!1}}isCopySuccessInIE11(){const e=this.window.clipboardData;return!(e&&e.getData&&!e.getData("Text"))}copyFromContent(e,t=this.document.body){if(this.tempTextArea&&!t.contains(this.tempTextArea)&&this.destroy(this.tempTextArea.parentElement||void 0),!this.tempTextArea){this.tempTextArea=this.createTempTextArea(this.document,this.window);try{t.appendChild(this.tempTextArea)}catch(e){throw new Error("Container should be a Dom element")}}this.tempTextArea.value=e;const n=this.copyFromInputElement(this.tempTextArea,!1);return this.config.cleanUpAfterCopy&&this.destroy(this.tempTextArea.parentElement||void 0),n}destroy(e=this.document.body){this.tempTextArea&&(e.removeChild(this.tempTextArea),this.tempTextArea=void 0)}selectTarget(e){return e.select(),e.setSelectionRange(0,e.value.length),e.value.length}copyText(){return this.document.execCommand("copy")}clearSelection(e,t){e&&e.focus(),t.getSelection()?.removeAllRanges()}createTempTextArea(e,t){const n="rtl"===e.documentElement.getAttribute("dir");let r;r=e.createElement("textarea"),r.style.fontSize="12pt",r.style.border="0",r.style.padding="0",r.style.margin="0",r.style.position="absolute",r.style[n?"right":"left"]="-9999px";const o=t.pageYOffset||e.documentElement.scrollTop;return r.style.top=o+"px",r.setAttribute("readonly",""),r}pushCopyResponse(e){this.copySubject.observers.length>0&&this.ngZone.run((()=>{this.copySubject.next(e)}))}pushCopyReponse(e){this.pushCopyResponse(e)}}Pn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Pn,deps:[{token:t.NgZone},{token:$},{token:wn,optional:!0}],target:t.ɵɵFactoryTarget.Injectable}),Pn.ɵprov=t.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Pn,providedIn:"root"}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Pn,decorators:[{type:s,args:[{providedIn:"root"}]}],ctorParameters:function(){return[{type:t.NgZone},{type:void 0,decorators:[{type:m,args:[$]}]},{type:void 0,decorators:[{type:p},{type:m,args:[wn]}]}]}});class Rn{constructor(e,t,n,o){this.ngZone=e,this.host=t,this.renderer=n,this.clipboardSrv=o,this.cbOnSuccess=new r,this.cbOnError=new r,this.onClick=e=>{this.clipboardSrv.isSupported?this.targetElm&&this.clipboardSrv.isTargetValid(this.targetElm)?this.handleResult(this.clipboardSrv.copyFromInputElement(this.targetElm),this.targetElm.value,e):this.cbContent&&this.handleResult(this.clipboardSrv.copyFromContent(this.cbContent,this.container),this.cbContent,e):this.handleResult(!1,void 0,e)}}ngOnInit(){this.ngZone.runOutsideAngular((()=>{this.clickListener=this.renderer.listen(this.host.nativeElement,"click",this.onClick)}))}ngOnDestroy(){this.clickListener&&this.clickListener(),this.clipboardSrv.destroy(this.container)}handleResult(e,t,n){let r={isSuccess:e,content:t,successMessage:this.cbSuccessMsg,event:n};e?this.cbOnSuccess.observed&&this.ngZone.run((()=>{this.cbOnSuccess.emit(r)})):this.cbOnError.observed&&this.ngZone.run((()=>{this.cbOnError.emit(r)})),this.clipboardSrv.pushCopyResponse(r)}}Rn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Rn,deps:[{token:t.NgZone},{token:t.ElementRef},{token:t.Renderer2},{token:Pn}],target:t.ɵɵFactoryTarget.Directive}),Rn.ɵdir=t.ɵɵngDeclareDirective({minVersion:"12.0.0",version:"13.0.1",type:Rn,selector:"[ngxClipboard]",inputs:{targetElm:["ngxClipboard","targetElm"],container:"container",cbContent:"cbContent",cbSuccessMsg:"cbSuccessMsg"},outputs:{cbOnSuccess:"cbOnSuccess",cbOnError:"cbOnError"},ngImport:t}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:Rn,decorators:[{type:u,args:[{selector:"[ngxClipboard]"}]}],ctorParameters:function(){return[{type:t.NgZone},{type:t.ElementRef},{type:t.Renderer2},{type:Pn}]},propDecorators:{targetElm:[{type:i,args:["ngxClipboard"]}],container:[{type:i}],cbContent:[{type:i}],cbSuccessMsg:[{type:i}],cbOnSuccess:[{type:d}],cbOnError:[{type:d}]}});class On{constructor(e,t,n){this._clipboardService=e,this._viewContainerRef=t,this._templateRef=n}ngOnInit(){this._clipboardService.isSupported&&this._viewContainerRef.createEmbeddedView(this._templateRef)}}On.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:On,deps:[{token:Pn},{token:t.ViewContainerRef},{token:t.TemplateRef}],target:t.ɵɵFactoryTarget.Directive}),On.ɵdir=t.ɵɵngDeclareDirective({minVersion:"12.0.0",version:"13.0.1",type:On,selector:"[ngxClipboardIfSupported]",ngImport:t}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:On,decorators:[{type:u,args:[{selector:"[ngxClipboardIfSupported]"}]}],ctorParameters:function(){return[{type:Pn},{type:t.ViewContainerRef},{type:t.TemplateRef}]}});class _n{}_n.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:_n,deps:[],target:t.ɵɵFactoryTarget.NgModule}),_n.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:_n,declarations:[Rn,On],imports:[Q],exports:[Rn,On]}),_n.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:_n,imports:[[Q]]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"13.0.1",ngImport:t,type:_n,decorators:[{type:c,args:[{imports:[Q],declarations:[Rn,On],exports:[Rn,On]}]}]});class Bn{set required(e){this.requiredValue!==e&&(this.requiredValue=e,this.updateValidators())}get required(){return this.requiredValue}constructor(e){this.fb=e,this.subscriptSizing="fixed",this.messageTypes=[{name:"Post attributes",value:"POST_ATTRIBUTES_REQUEST"},{name:"Post telemetry",value:"POST_TELEMETRY_REQUEST"},{name:"Custom",value:""}],this.propagateChange=()=>{},this.destroy$=new Ke,this.messageTypeFormGroup=this.fb.group({messageTypeAlias:[null,[_.required]],messageType:[{value:null,disabled:!0},[_.maxLength(255)]]}),this.messageTypeFormGroup.get("messageTypeAlias").valueChanges.pipe(ze(this.destroy$)).subscribe((e=>this.updateMessageTypeValue(e))),this.messageTypeFormGroup.valueChanges.pipe(ze(this.destroy$)).subscribe((()=>this.updateView()))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}registerOnTouched(e){}registerOnChange(e){this.propagateChange=e}writeValue(e){this.modelValue=e;let t=this.messageTypes.find((t=>t.value===e));t||(t=this.messageTypes.find((e=>""===e.value))),this.messageTypeFormGroup.get("messageTypeAlias").patchValue(t,{emitEvent:!1}),this.messageTypeFormGroup.get("messageType").patchValue(e,{emitEvent:!1})}validate(){return this.messageTypeFormGroup.valid?null:{messageTypeInvalid:!0}}setDisabledState(e){this.disabled=e,e?this.messageTypeFormGroup.disable({emitEvent:!1}):(this.messageTypeFormGroup.enable({emitEvent:!1}),"Custom"!==this.messageTypeFormGroup.get("messageTypeAlias").value?.name&&this.messageTypeFormGroup.get("messageType").disable({emitEvent:!1}))}updateView(){const e=this.messageTypeFormGroup.getRawValue().messageType;this.modelValue!==e&&(this.modelValue=e,this.propagateChange(this.modelValue))}updateValidators(){this.messageTypeFormGroup.get("messageType").setValidators(this.required?[_.required,_.maxLength(255)]:[_.maxLength(255)]),this.messageTypeFormGroup.get("messageType").updateValueAndValidity({emitEvent:!1})}updateMessageTypeValue(e){"Custom"!==e?.name?this.messageTypeFormGroup.get("messageType").disable({emitEvent:!1}):this.messageTypeFormGroup.get("messageType").enable({emitEvent:!1}),this.messageTypeFormGroup.get("messageType").patchValue(e.value??null)}}e("OutputMessageTypeAutocompleteComponent",Bn),Bn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Bn,deps:[{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Bn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Bn,selector:"tb-output-message-type-autocomplete",inputs:{subscriptSizing:"subscriptSizing",disabled:"disabled",required:"required"},providers:[{provide:K,useExisting:a((()=>Bn)),multi:!0},{provide:z,useExisting:a((()=>Bn)),multi:!0}],ngImport:t,template:'
\n \n {{\'tb.rulenode.output-message-type\' | translate}}\n \n \n {{msgType.name}}\n \n \n \n \n {{\'tb.rulenode.message-type-value\' | translate}}\n \n \n \n {{ \'tb.rulenode.message-type-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.message-type-value-max-length\' | translate }}\n \n \n
\n\n',dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ce.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:Rn,selector:"[ngxClipboard]",inputs:["ngxClipboard","container","cbContent","cbSuccessMsg"],outputs:["cbOnSuccess","cbOnError"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),Be([I()],Bn.prototype,"disabled",void 0),Be([I()],Bn.prototype,"required",null),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Bn,decorators:[{type:n,args:[{selector:"tb-output-message-type-autocomplete",providers:[{provide:K,useExisting:a((()=>Bn)),multi:!0},{provide:z,useExisting:a((()=>Bn)),multi:!0}],template:'
\n \n {{\'tb.rulenode.output-message-type\' | translate}}\n \n \n {{msgType.name}}\n \n \n \n \n {{\'tb.rulenode.message-type-value\' | translate}}\n \n \n \n {{ \'tb.rulenode.message-type-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.message-type-value-max-length\' | translate }}\n \n \n
\n\n'}]}],ctorParameters:function(){return[{type:O.FormBuilder}]},propDecorators:{subscriptSizing:[{type:i}],disabled:[{type:i}],required:[{type:i}]}});class Kn{constructor(e,t){this.fb=e,this.translate=t,this.translation=an,this.propagateChange=()=>{},this.destroy$=new Ke,this.selectOptions=[]}ngOnInit(){this.initOptions(),this.chipControlGroup=this.fb.group({chipControl:[null,[]]}),this.chipControlGroup.get("chipControl").valueChanges.pipe(we(this.destroy$)).subscribe((e=>{e&&this.propagateChange(e)}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}initOptions(){for(const e of this.translation.keys())this.selectOptions.push({value:e,name:this.translate.instant(this.translation.get(e))})}writeValue(e){this.chipControlGroup.get("chipControl").patchValue(e,{emitEvent:!1})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){e?this.chipControlGroup.disable({emitEvent:!1}):this.chipControlGroup.enable({emitEvent:!1})}}e("MsgMetadataChipComponent",Kn),Kn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Kn,deps:[{token:O.FormBuilder},{token:X.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Kn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Kn,selector:"tb-msg-metadata-chip",inputs:{labelText:"labelText",translation:"translation"},providers:[{provide:K,useExisting:a((()=>Kn)),multi:!0}],ngImport:t,template:'
\n
{{ labelText }}
\n \n {{ option.name }}\n \n
\n',dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:ve.MatChipListbox,selector:"mat-chip-listbox",inputs:["tabIndex","multiple","aria-orientation","selectable","compareWith","required","hideSingleSelectionIndicator","value"],outputs:["change"]},{kind:"component",type:ve.MatChipOption,selector:"mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]",inputs:["color","disabled","disableRipple","tabIndex","selectable","selected"],outputs:["selectionChange"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Kn,decorators:[{type:n,args:[{selector:"tb-msg-metadata-chip",providers:[{provide:K,useExisting:a((()=>Kn)),multi:!0}],template:'
\n
{{ labelText }}
\n \n {{ option.name }}\n \n
\n'}]}],ctorParameters:function(){return[{type:O.FormBuilder},{type:X.TranslateService}]},propDecorators:{labelText:[{type:i}],translation:[{type:i}]}});class zn extends T{constructor(e,t,n,r){super(e),this.store=e,this.translate=t,this.injector=n,this.fb=r,this.destroy$=new Ke,this.sourceFieldSubcritption=[],this.propagateChange=null,this.disabled=!1,this.required=!1,this.oneMapRequiredValidator=e=>e.get("keyVals").value.length,this.propagateNestedErrors=e=>{if(this.svListFormGroup&&this.svListFormGroup.get("keyVals")&&"VALID"===this.svListFormGroup.get("keyVals")?.status)return null;const t={};if(this.svListFormGroup&&this.svListFormGroup.setErrors(null),e instanceof U||e instanceof H){if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;for(const n of Object.keys(e.controls)){const r=this.propagateNestedErrors(e.controls[n]);if(r&&Object.keys(r).length)for(const e of Object.keys(r))t[e]=!0}return t}if(e.errors)for(const n of Object.keys(e.errors))t[n]=!0;return le(t,{})?null:t}}ngOnInit(){this.ngControl=this.injector.get(B),null!=this.ngControl&&(this.ngControl.valueAccessor=this),this.svListFormGroup=this.fb.group({keyVals:this.fb.array([])},{validators:[this.propagateNestedErrors,this.oneMapRequiredValidator]}),this.svListFormGroup.valueChanges.pipe(we(this.destroy$)).subscribe((()=>{this.updateModel()}))}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}keyValsFormArray(){return this.svListFormGroup.get("keyVals")}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.svListFormGroup.disable({emitEvent:!1}):this.svListFormGroup.enable({emitEvent:!1})}writeValue(e){const t=Object.keys(e).map((t=>({key:t,value:e[t]})));if(this.keyValsFormArray().length===t.length)this.keyValsFormArray().patchValue(t,{emitEvent:!1});else{const e=[];t.forEach((t=>{e.push(this.fb.group({key:[t.key,[_.required]],value:[t.value,[_.required,_.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]}))})),this.svListFormGroup.setControl("keyVals",this.fb.array(e,this.propagateNestedErrors),{emitEvent:!1});for(const e of this.keyValsFormArray().controls)this.keyChangeSubscribe(e)}}filterSelectOptions(e){const t=[];for(const e of this.svListFormGroup.get("keyVals").value){const n=this.selectOptions.find((t=>t.value===e.key));n&&t.push(n)}const n=[];for(const r of this.selectOptions)se(t.find((e=>e.value===r.value)))&&r.value!==e?.get("key").value||n.push(r);return n}removeKeyVal(e){this.keyValsFormArray().removeAt(e),this.sourceFieldSubcritption[e].unsubscribe(),this.sourceFieldSubcritption.splice(e,1)}addKeyVal(){this.keyValsFormArray().push(this.fb.group({key:["",[_.required]],value:["",[_.required,_.pattern(/(?:.|\s)*\S(&:.|\s)*/)]]})),this.keyChangeSubscribe(this.keyValsFormArray().at(this.keyValsFormArray().length-1))}keyChangeSubscribe(e){this.sourceFieldSubcritption.push(e.get("key").valueChanges.pipe(we(this.destroy$)).subscribe((t=>{const n=Nt.get(t);e.get("value").patchValue(this.targetKeyPrefix+n[0].toUpperCase()+n.slice(1))})))}validate(e){return!this.svListFormGroup.get("keyVals").value.length&&this.required?{svMapRequired:!0}:this.svListFormGroup.valid?null:{svFieldsRequired:!0}}updateModel(){const e=this.svListFormGroup.get("keyVals").value;if(this.required&&!e.length||!this.svListFormGroup.valid)this.propagateChange(null);else{const t={};e.forEach((e=>{t[e.key]=e.value})),this.propagateChange(t)}}}e("SvMapConfigComponent",zn),zn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:zn,deps:[{token:R.Store},{token:X.TranslateService},{token:t.Injector},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),zn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:zn,selector:"tb-sv-map-config",inputs:{selectOptions:"selectOptions",disabled:"disabled",labelText:"labelText",requiredText:"requiredText",targetKeyPrefix:"targetKeyPrefix",selectText:"selectText",selectRequiredText:"selectRequiredText",valText:"valText",valRequiredText:"valRequiredText",hintText:"hintText",popupHelpLink:"popupHelpLink",required:"required"},providers:[{provide:K,useExisting:a((()=>zn)),multi:!0},{provide:z,useExisting:a((()=>zn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n
\n
{{ labelText }}
\n
\n tb.rulenode.map-fields-required\n
\n
\n {{ requiredText }}\n
\n
\n
\n
\n
\n
{{ selectText }}
\n
{{ valText }}
\n
\n
\n
\n
\n \n \n \n {{option.name}}\n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n \n
\n \n
\n',styles:[":host .field-space{flex:1 1 50%}:host .actions-header{width:40px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:j.NgClass,selector:"[ngClass]",inputs:["class","ngClass"]},{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:ce.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ce.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:Se.DefaultClassDirective,selector:" [ngClass], [ngClass.xs], [ngClass.sm], [ngClass.md], [ngClass.lg], [ngClass.xl], [ngClass.lt-sm], [ngClass.lt-md], [ngClass.lt-lg], [ngClass.lt-xl], [ngClass.gt-xs], [ngClass.gt-sm], [ngClass.gt-md], [ngClass.gt-lg]",inputs:["ngClass","ngClass.xs","ngClass.sm","ngClass.md","ngClass.lg","ngClass.xl","ngClass.lt-sm","ngClass.lt-md","ngClass.lt-lg","ngClass.lt-xl","ngClass.gt-xs","ngClass.gt-sm","ngClass.gt-md","ngClass.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormControlDirective,selector:"[formControl]",inputs:["formControl","disabled","ngModel"],outputs:["ngModelChange"],exportAs:["ngForm"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:An,selector:"tb-example-hint",inputs:["hintText","popupHelpLink","textAlign"]},{kind:"pipe",type:j.AsyncPipe,name:"async"},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),Be([I()],zn.prototype,"disabled",void 0),Be([I()],zn.prototype,"required",void 0),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:zn,decorators:[{type:n,args:[{selector:"tb-sv-map-config",providers:[{provide:K,useExisting:a((()=>zn)),multi:!0},{provide:z,useExisting:a((()=>zn)),multi:!0}],template:'
\n
\n
{{ labelText }}
\n
\n tb.rulenode.map-fields-required\n
\n
\n {{ requiredText }}\n
\n
\n
\n
\n
\n
{{ selectText }}
\n
{{ valText }}
\n
\n
\n
\n
\n \n \n \n {{option.name}}\n \n \n \n \n \n \n
\n \n
\n
\n
\n
\n
\n
\n \n
\n \n
\n',styles:[":host .field-space{flex:1 1 50%}:host .actions-header{width:40px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:R.Store},{type:X.TranslateService},{type:t.Injector},{type:O.FormBuilder}]},propDecorators:{selectOptions:[{type:i}],disabled:[{type:i}],labelText:[{type:i}],requiredText:[{type:i}],targetKeyPrefix:[{type:i}],selectText:[{type:i}],selectRequiredText:[{type:i}],valText:[{type:i}],valRequiredText:[{type:i}],hintText:[{type:i}],popupHelpLink:[{type:i}],required:[{type:i}]}});class Un extends T{get required(){return this.requiredValue}set required(e){this.requiredValue=Le(e)}constructor(e,t){super(e),this.store=e,this.fb=t,this.directionTypes=Object.keys(C),this.directionTypeTranslations=F,this.propagateChange=null}ngOnInit(){this.relationsQueryFormGroup=this.fb.group({fetchLastLevelOnly:[!1,[]],direction:[null,[_.required]],maxLevel:[null,[]],filters:[null]}),this.relationsQueryFormGroup.valueChanges.subscribe((e=>{this.relationsQueryFormGroup.valid?this.propagateChange(e):this.propagateChange(null)}))}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}setDisabledState(e){this.disabled=e,this.disabled?this.relationsQueryFormGroup.disable({emitEvent:!1}):this.relationsQueryFormGroup.enable({emitEvent:!1})}writeValue(e){this.relationsQueryFormGroup.reset(e||{},{emitEvent:!1})}}e("RelationsQueryConfigOldComponent",Un),Un.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Un,deps:[{token:R.Store},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Un.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Un,selector:"tb-relations-query-config-old",inputs:{disabled:"disabled",required:"required"},providers:[{provide:K,useExisting:a((()=>Un)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n
\n
relation.relation-filters
\n \n
\n',dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"component",type:J.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Ye.RelationFiltersComponent,selector:"tb-relation-filters",inputs:["disabled","allowedEntityTypes"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Un,decorators:[{type:n,args:[{selector:"tb-relations-query-config-old",providers:[{provide:K,useExisting:a((()=>Un)),multi:!0}],template:'
\n \n {{ \'alias.last-level-relation\' | translate }}\n \n
\n \n relation.direction\n \n \n {{ directionTypeTranslations.get(type) | translate }}\n \n \n \n \n tb.rulenode.max-relation-level\n \n \n
\n
relation.relation-filters
\n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder}]},propDecorators:{disabled:[{type:i}],required:[{type:i}]}});class Hn{constructor(e,t){this.translate=e,this.fb=t,this.propagateChange=e=>{},this.destroy$=new Ke,this.separatorKeysCodes=[xe,be,he],this.onTouched=()=>{}}ngOnInit(){this.attributeControlGroup=this.fb.group({clientAttributeNames:[[],[]],sharedAttributeNames:[[],[]],serverAttributeNames:[[],[]],latestTsKeyNames:[[],[]],getLatestValueWithTs:[!1,[]]},{validators:this.atLeastOne(_.required,["clientAttributeNames","sharedAttributeNames","serverAttributeNames","latestTsKeyNames"])}),this.attributeControlGroup.valueChanges.pipe(we(this.destroy$)).subscribe((e=>{this.propagateChange(this.preparePropagateValue(e))}))}preparePropagateValue(e){const t={};for(const n in e)t[n]="getLatestValueWithTs"===n||se(e[n])?e[n]:[];return t}validate(){return this.attributeControlGroup.valid?null:{atLeastOneRequired:!0}}atLeastOne(e,t=null){return n=>{t||(t=Object.keys(n.controls));return n?.controls&&t.some((t=>!e(n.controls[t])))?null:{atLeastOne:!0}}}writeValue(e){this.attributeControlGroup.setValue(e,{emitEvent:!1})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){this.onTouched=e}setDisabledState(e){e?this.attributeControlGroup.disable({emitEvent:!1}):this.attributeControlGroup.enable({emitEvent:!1})}ngOnDestroy(){this.destroy$.next(null),this.destroy$.complete()}}e("SelectAttributesComponent",Hn),Hn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Hn,deps:[{token:X.TranslateService},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Hn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Hn,selector:"tb-select-attributes",inputs:{popupHelpLink:"popupHelpLink"},providers:[{provide:K,useExisting:a((()=>Hn)),multi:!0},{provide:z,useExisting:Hn,multi:!0}],ngImport:t,template:'
\n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n {{ \'tb.rulenode.fetch-latest-telemetry-with-timestamp\' | translate }}\n \n
\n
\n\n\n help\n\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:j.NgTemplateOutlet,selector:"[ngTemplateOutlet]",inputs:["ngTemplateOutletContext","ngTemplateOutlet","ngTemplateOutletInjector"]},{kind:"component",type:et.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:W.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:ne.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:re.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:An,selector:"tb-example-hint",inputs:["hintText","popupHelpLink","textAlign"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Hn,decorators:[{type:n,args:[{selector:"tb-select-attributes",providers:[{provide:K,useExisting:a((()=>Hn)),multi:!0},{provide:z,useExisting:Hn,multi:!0}],template:'
\n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n {{ \'tb.rulenode.fetch-latest-telemetry-with-timestamp\' | translate }}\n \n
\n
\n\n\n help\n\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:X.TranslateService},{type:O.FormBuilder}]},propDecorators:{popupHelpLink:[{type:i}]}});class jn extends T{constructor(e,t){super(e),this.store=e,this.fb=t,this.propagateChange=null,this.destroy$=new Ke,this.alarmStatus=A,this.alarmStatusTranslations=M}ngOnInit(){this.alarmStatusGroup=this.fb.group({alarmStatus:[null,[]]}),this.alarmStatusGroup.get("alarmStatus").valueChanges.pipe(we(this.destroy$)).subscribe((e=>{this.propagateChange(e)}))}setDisabledState(e){e?this.alarmStatusGroup.disable({emitEvent:!1}):this.alarmStatusGroup.enable({emitEvent:!1})}registerOnChange(e){this.propagateChange=e}registerOnTouched(e){}ngOnDestroy(){this.destroy$.next(),this.destroy$.complete()}writeValue(e){this.alarmStatusGroup.get("alarmStatus").patchValue(e,{emitEvent:!1})}}e("AlarmStatusSelectComponent",jn),jn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:jn,deps:[{token:R.Store},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),jn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:jn,selector:"tb-alarm-status-select",providers:[{provide:K,useExisting:a((()=>jn)),multi:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n
\n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_UNACK) | translate }}\n \n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_ACK) | translate }}\n \n
\n
\n \n {{ alarmStatusTranslations.get(alarmStatus.CLEARED_UNACK) | translate }}\n \n \n {{ alarmStatusTranslations.get(alarmStatus.CLEARED_ACK) | translate }}\n \n
\n
\n
\n',styles:[":host .chip-listbox{max-width:460px;width:100%}:host .chip-listbox .toggle-column{display:flex;flex:1 1 100%;gap:8px}:host .chip-listbox .option{margin:0}@media screen and (max-width: 959px){:host .chip-listbox{max-width:360px}:host .chip-listbox .toggle-column{flex-direction:column}}:host ::ng-deep .chip-listbox .mdc-evolution-chip-set__chips{gap:8px}:host ::ng-deep .chip-listbox .option button{flex-basis:100%;justify-content:start}:host ::ng-deep .chip-listbox .option .mdc-evolution-chip__graphic{flex-grow:0}\n"],dependencies:[{kind:"component",type:ve.MatChipListbox,selector:"mat-chip-listbox",inputs:["tabIndex","multiple","aria-orientation","selectable","compareWith","required","hideSingleSelectionIndicator","value"],outputs:["change"]},{kind:"component",type:ve.MatChipOption,selector:"mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]",inputs:["color","disabled","disableRipple","tabIndex","selectable","selected"],outputs:["selectionChange"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:jn,decorators:[{type:n,args:[{selector:"tb-alarm-status-select",providers:[{provide:K,useExisting:a((()=>jn)),multi:!0}],template:'
\n \n
\n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_UNACK) | translate }}\n \n \n {{ alarmStatusTranslations.get(alarmStatus.ACTIVE_ACK) | translate }}\n \n
\n
\n \n {{ alarmStatusTranslations.get(alarmStatus.CLEARED_UNACK) | translate }}\n \n \n {{ alarmStatusTranslations.get(alarmStatus.CLEARED_ACK) | translate }}\n \n
\n
\n
\n',styles:[":host .chip-listbox{max-width:460px;width:100%}:host .chip-listbox .toggle-column{display:flex;flex:1 1 100%;gap:8px}:host .chip-listbox .option{margin:0}@media screen and (max-width: 959px){:host .chip-listbox{max-width:360px}:host .chip-listbox .toggle-column{flex-direction:column}}:host ::ng-deep .chip-listbox .mdc-evolution-chip-set__chips{gap:8px}:host ::ng-deep .chip-listbox .option button{flex-basis:100%;justify-content:start}:host ::ng-deep .chip-listbox .option .mdc-evolution-chip__graphic{flex-grow:0}\n"]}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder}]}});class $n{}e("RulenodeCoreConfigCommonModule",$n),$n.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:$n,deps:[],target:t.ɵɵFactoryTarget.NgModule}),$n.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:$n,declarations:[Mn,En,Gn,Dn,Vn,Nn,Sn,Bn,Fn,Kn,zn,Un,Hn,jn,An],imports:[Q,E,_e],exports:[Mn,En,Gn,Dn,Vn,Nn,Sn,Bn,Fn,Kn,zn,Un,Hn,jn,An]}),$n.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:$n,imports:[Q,E,_e]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:$n,decorators:[{type:c,args:[{declarations:[Mn,En,Gn,Dn,Vn,Nn,Sn,Bn,Fn,Kn,zn,Un,Hn,jn,An],imports:[Q,E,_e],exports:[Mn,En,Gn,Dn,Vn,Nn,Sn,Bn,Fn,Kn,zn,Un,Hn,jn,An]}]}]});class Qn{}e("RuleNodeCoreConfigActionModule",Qn),Qn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Qn,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Qn.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:Qn,declarations:[In,gt,Ln,Cn,gn,ft,xt,bt,ht,xn,vt,Ft,fn,yn,vn,kn,Tn,yt,Ct,hn,bn,qn],imports:[Q,E,_e,$n],exports:[In,gt,Ln,Cn,gn,ft,xt,bt,ht,xn,vt,Ft,fn,yn,vn,kn,Tn,yt,Ct,hn,bn,qn]}),Qn.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Qn,imports:[Q,E,_e,$n]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Qn,decorators:[{type:c,args:[{declarations:[In,gt,Ln,Cn,gn,ft,xt,bt,ht,xn,vt,Ft,fn,yn,vn,kn,Tn,yt,Ct,hn,bn,qn],imports:[Q,E,_e,$n],exports:[In,gt,Ln,Cn,gn,ft,xt,bt,ht,xn,vt,Ft,fn,yn,vn,kn,Tn,yt,Ct,hn,bn,qn]}]}]});class Jn extends f{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n,this.separatorKeysCodes=[xe,be,he]}configForm(){return this.calculateDeltaConfigForm}onConfigurationSet(e){this.calculateDeltaConfigForm=this.fb.group({inputValueKey:[e.inputValueKey,[_.required,_.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],outputValueKey:[e.outputValueKey,[_.required,_.pattern(/(?:.|\s)*\S(&:.|\s)*/)]],useCache:[e.useCache,[]],addPeriodBetweenMsgs:[e.addPeriodBetweenMsgs,[]],periodValueKey:[e.periodValueKey,[]],round:[e.round,[_.min(0),_.max(15)]],tellFailureIfDeltaIsNegative:[e.tellFailureIfDeltaIsNegative,[]]})}prepareInputConfig(e){return{inputValueKey:se(e?.inputValueKey)?e.inputValueKey:null,outputValueKey:se(e?.outputValueKey)?e.outputValueKey:null,useCache:!se(e?.useCache)||e.useCache,addPeriodBetweenMsgs:!!se(e?.addPeriodBetweenMsgs)&&e.addPeriodBetweenMsgs,periodValueKey:se(e?.periodValueKey)?e.periodValueKey:null,round:se(e?.round)?e.round:null,tellFailureIfDeltaIsNegative:!se(e?.tellFailureIfDeltaIsNegative)||e.tellFailureIfDeltaIsNegative}}prepareOutputConfig(e){return me(e)}updateValidators(e){this.calculateDeltaConfigForm.get("addPeriodBetweenMsgs").value?this.calculateDeltaConfigForm.get("periodValueKey").setValidators([_.required]):this.calculateDeltaConfigForm.get("periodValueKey").setValidators([]),this.calculateDeltaConfigForm.get("periodValueKey").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["addPeriodBetweenMsgs"]}}e("CalculateDeltaConfigComponent",Jn),Jn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Jn,deps:[{token:R.Store},{token:X.TranslateService},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Jn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Jn,selector:"tb-enrichment-node-calculate-delta-config",usesInheritance:!0,ngImport:t,template:"
\n
\n \n {{ 'tb.rulenode.input-value-key' | translate }}\n \n \n {{ 'tb.rulenode.input-value-key-required' | translate }}\n \n \n \n {{ 'tb.rulenode.output-value-key' | translate }}\n \n \n {{ 'tb.rulenode.output-value-key-required' | translate }}\n \n \n
\n \n {{ 'tb.rulenode.number-of-digits-after-floating-point' | translate }}\n \n \n {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }}\n \n \n {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }}\n \n \n
\n
\n \n {{ 'tb.rulenode.failure-if-delta-negative' | translate }}\n \n
\n
\n \n {{ 'tb.rulenode.use-caching' | translate }}\n \n
\n
\n
\n \n {{ 'tb.rulenode.add-time-difference-between-readings' | translate:\n { inputValueKey: calculateDeltaConfigForm.get('inputValueKey').valid ?\n calculateDeltaConfigForm.get('inputValueKey').value : 'tb.rulenode.input-value-key' | translate } }}\n \n
\n \n {{ 'tb.rulenode.period-value-key' | translate }}\n \n \n {{ 'tb.rulenode.period-value-key-required' | translate }}\n \n \n
\n
\n
\n",dependencies:[{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:ne.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:re.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Jn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-calculate-delta-config",template:"
\n
\n \n {{ 'tb.rulenode.input-value-key' | translate }}\n \n \n {{ 'tb.rulenode.input-value-key-required' | translate }}\n \n \n \n {{ 'tb.rulenode.output-value-key' | translate }}\n \n \n {{ 'tb.rulenode.output-value-key-required' | translate }}\n \n \n
\n \n {{ 'tb.rulenode.number-of-digits-after-floating-point' | translate }}\n \n \n {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }}\n \n \n {{ 'tb.rulenode.number-of-digits-after-floating-point-range' | translate }}\n \n \n
\n
\n \n {{ 'tb.rulenode.failure-if-delta-negative' | translate }}\n \n
\n
\n \n {{ 'tb.rulenode.use-caching' | translate }}\n \n
\n
\n
\n \n {{ 'tb.rulenode.add-time-difference-between-readings' | translate:\n { inputValueKey: calculateDeltaConfigForm.get('inputValueKey').valid ?\n calculateDeltaConfigForm.get('inputValueKey').value : 'tb.rulenode.input-value-key' | translate } }}\n \n
\n \n {{ 'tb.rulenode.period-value-key' | translate }}\n \n \n {{ 'tb.rulenode.period-value-key-required' | translate }}\n \n \n
\n
\n
\n"}]}],ctorParameters:function(){return[{type:R.Store},{type:X.TranslateService},{type:O.FormBuilder}]}});class Yn extends f{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.fetchToData=[],this.DataToFetch=_t;for(const e of Bt.keys())e!==_t.FIELDS&&this.fetchToData.push({value:e,name:this.translate.instant(Bt.get(e))})}configForm(){return this.customerAttributesConfigForm}prepareOutputConfig(e){const t={};for(const n of Object.keys(e.dataMapping))t[n.trim()]=e.dataMapping[n];return e.dataMapping=t,me(e)}prepareInputConfig(e){let t,n;return t=se(e?.telemetry)?e.telemetry?_t.LATEST_TELEMETRY:_t.ATTRIBUTES:se(e?.dataToFetch)?e.dataToFetch:_t.ATTRIBUTES,n=se(e?.attrMapping)?e.attrMapping:se(e?.dataMapping)?e.dataMapping:null,{dataToFetch:t,dataMapping:n,fetchTo:se(e?.fetchTo)?e.fetchTo:rn.METADATA}}selectTranslation(e,t){return this.customerAttributesConfigForm.get("dataToFetch").value===_t.LATEST_TELEMETRY?e:t}onConfigurationSet(e){this.customerAttributesConfigForm=this.fb.group({dataToFetch:[e.dataToFetch,[]],dataMapping:[e.dataMapping,[_.required]],fetchTo:[e.fetchTo]})}}e("CustomerAttributesConfigComponent",Yn),Yn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Yn,deps:[{token:R.Store},{token:O.FormBuilder},{token:X.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Yn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Yn,selector:"tb-enrichment-node-customer-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.mapping-of-customers
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}\n"],dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:Z.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:tt.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:nt.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:Mn,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","labelText","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"component",type:Kn,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Yn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-customer-attributes-config",template:'
\n
tb.rulenode.mapping-of-customers
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}\n"]}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder},{type:X.TranslateService}]}});class Wn extends f{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n}configForm(){return this.deviceAttributesConfigForm}onConfigurationSet(e){this.deviceAttributesConfigForm=this.fb.group({deviceRelationsQuery:[e.deviceRelationsQuery,[_.required]],tellFailureIfAbsent:[e.tellFailureIfAbsent,[]],fetchTo:[e.fetchTo,[]],attributesControl:[e.attributesControl,[]]})}prepareInputConfig(e){return pe(e)&&(e.attributesControl={clientAttributeNames:se(e?.clientAttributeNames)?e.clientAttributeNames:[],latestTsKeyNames:se(e?.latestTsKeyNames)?e.latestTsKeyNames:[],serverAttributeNames:se(e?.serverAttributeNames)?e.serverAttributeNames:[],sharedAttributeNames:se(e?.sharedAttributeNames)?e.sharedAttributeNames:[],getLatestValueWithTs:!!se(e?.getLatestValueWithTs)&&e.getLatestValueWithTs}),{deviceRelationsQuery:se(e?.deviceRelationsQuery)?e.deviceRelationsQuery:null,tellFailureIfAbsent:!se(e?.tellFailureIfAbsent)||e.tellFailureIfAbsent,fetchTo:se(e?.fetchTo)?e.fetchTo:rn.METADATA,attributesControl:e?e.attributesControl:null}}prepareOutputConfig(e){for(const t of Object.keys(e.attributesControl))e[t]=e.attributesControl[t];return delete e.attributesControl,e}}e("DeviceAttributesConfigComponent",Wn),Wn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Wn,deps:[{token:R.Store},{token:X.TranslateService},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Wn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Wn,selector:"tb-enrichment-node-device-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.device-relations-query
\n \n \n
\n
\n
\n
tb.rulenode.related-device-attributes
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n \n
\n
\n \n {{ \'tb.rulenode.tell-failure\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"component",type:ne.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:re.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:En,selector:"tb-device-relations-query-config",inputs:["disabled","required"]},{kind:"component",type:Kn,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"component",type:Hn,selector:"tb-select-attributes",inputs:["popupHelpLink"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Wn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-device-attributes-config",template:'
\n
\n
tb.rulenode.device-relations-query
\n \n \n
\n
\n
\n
tb.rulenode.related-device-attributes
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n \n
\n
\n \n {{ \'tb.rulenode.tell-failure\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:R.Store},{type:X.TranslateService},{type:O.FormBuilder}]}});class Zn extends f{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n,this.predefinedValues=[];for(const e of Object.keys(Dt))this.predefinedValues.push({value:Dt[e],name:this.translate.instant(Vt.get(Dt[e]))})}ngOnInit(){super.ngOnInit()}configForm(){return this.entityDetailsConfigForm}prepareInputConfig(e){let t;return t=se(e?.addToMetadata)?e.addToMetadata?rn.METADATA:rn.DATA:e?.fetchTo?e.fetchTo:rn.DATA,{detailsList:se(e?.detailsList)?e.detailsList:null,fetchTo:t}}onConfigurationSet(e){this.entityDetailsConfigForm=this.fb.group({detailsList:[e.detailsList,[_.required]],fetchTo:[e.fetchTo,[]]})}}e("EntityDetailsConfigComponent",Zn),Zn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Zn,deps:[{token:R.Store},{token:X.TranslateService},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Zn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Zn,selector:"tb-enrichment-node-entity-details-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n help\n \n \n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"component",type:et.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:W.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Kn,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Zn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-entity-details-config",template:'
\n \n \n help\n \n \n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:R.Store},{type:X.TranslateService},{type:O.FormBuilder}]}});class Xn extends f{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n,this.separatorKeysCodes=[xe,be,he],this.aggregationTypes=G,this.aggregations=Object.values(G),this.aggregationTypesTranslations=D,this.fetchMode=wt,this.samplingOrders=Object.values(Ot),this.samplingOrdersTranslate=zt,this.timeUnits=Object.values(At),this.timeUnitsTranslationMap=Mt,this.deduplicationStrategiesHintTranslations=Rt,this.headerOptions=[],this.timeUnitMap={[At.MILLISECONDS]:1,[At.SECONDS]:1e3,[At.MINUTES]:6e4,[At.HOURS]:36e5,[At.DAYS]:864e5},this.intervalValidator=()=>e=>e.get("startInterval").value*this.timeUnitMap[e.get("startIntervalTimeUnit").value]<=e.get("endInterval").value*this.timeUnitMap[e.get("endIntervalTimeUnit").value]?{intervalError:!0}:null;for(const e of Pt.keys())this.headerOptions.push({value:e,name:this.translate.instant(Pt.get(e))})}configForm(){return this.getTelemetryFromDatabaseConfigForm}onConfigurationSet(e){this.getTelemetryFromDatabaseConfigForm=this.fb.group({latestTsKeyNames:[e.latestTsKeyNames,[_.required]],aggregation:[e.aggregation,[_.required]],fetchMode:[e.fetchMode,[_.required]],orderBy:[e.orderBy,[]],limit:[e.limit,[]],useMetadataIntervalPatterns:[e.useMetadataIntervalPatterns,[]],interval:this.fb.group({startInterval:[e.interval.startInterval,[]],startIntervalTimeUnit:[e.interval.startIntervalTimeUnit,[]],endInterval:[e.interval.endInterval,[]],endIntervalTimeUnit:[e.interval.endIntervalTimeUnit,[]]}),startIntervalPattern:[e.startIntervalPattern,[]],endIntervalPattern:[e.endIntervalPattern,[]]})}validatorTriggers(){return["fetchMode","useMetadataIntervalPatterns"]}toggleChange(e){this.getTelemetryFromDatabaseConfigForm.get("fetchMode").patchValue(e,{emitEvent:!0})}prepareOutputConfig(e){return e.startInterval=e.interval.startInterval,e.startIntervalTimeUnit=e.interval.startIntervalTimeUnit,e.endInterval=e.interval.endInterval,e.endIntervalTimeUnit=e.interval.endIntervalTimeUnit,delete e.interval,me(e)}prepareInputConfig(e){return pe(e)&&(e.interval={startInterval:e.startInterval,startIntervalTimeUnit:e.startIntervalTimeUnit,endInterval:e.endInterval,endIntervalTimeUnit:e.endIntervalTimeUnit}),{latestTsKeyNames:se(e?.latestTsKeyNames)?e.latestTsKeyNames:null,aggregation:se(e?.aggregation)?e.aggregation:G.NONE,fetchMode:se(e?.fetchMode)?e.fetchMode:wt.FIRST,orderBy:se(e?.orderBy)?e.orderBy:Ot.ASC,limit:se(e?.limit)?e.limit:1e3,useMetadataIntervalPatterns:!!se(e?.useMetadataIntervalPatterns)&&e.useMetadataIntervalPatterns,interval:{startInterval:se(e?.interval?.startInterval)?e.interval.startInterval:2,startIntervalTimeUnit:se(e?.interval?.startIntervalTimeUnit)?e.interval.startIntervalTimeUnit:At.MINUTES,endInterval:se(e?.interval?.endInterval)?e.interval.endInterval:1,endIntervalTimeUnit:se(e?.interval?.endIntervalTimeUnit)?e.interval.endIntervalTimeUnit:At.MINUTES},startIntervalPattern:se(e?.startIntervalPattern)?e.startIntervalPattern:null,endIntervalPattern:se(e?.endIntervalPattern)?e.endIntervalPattern:null}}updateValidators(e){const t=this.getTelemetryFromDatabaseConfigForm.get("fetchMode").value,n=this.getTelemetryFromDatabaseConfigForm.get("useMetadataIntervalPatterns").value;t&&t===wt.ALL?(this.getTelemetryFromDatabaseConfigForm.get("aggregation").setValidators([_.required]),this.getTelemetryFromDatabaseConfigForm.get("orderBy").setValidators([_.required]),this.getTelemetryFromDatabaseConfigForm.get("limit").setValidators([_.required,_.min(2),_.max(1e3)])):(this.getTelemetryFromDatabaseConfigForm.get("aggregation").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("orderBy").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("limit").setValidators([])),n?(this.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval.startIntervalTimeUnit").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval.endIntervalTimeUnit").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("interval").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").setValidators([_.required,_.pattern(/(?:.|\s)*\S(&:.|\s)*/)]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").setValidators([_.required,_.pattern(/(?:.|\s)*\S(&:.|\s)*/)])):(this.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").setValidators([_.required,_.min(1),_.max(2147483647)]),this.getTelemetryFromDatabaseConfigForm.get("interval.startIntervalTimeUnit").setValidators([_.required]),this.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").setValidators([_.required,_.min(1),_.max(2147483647)]),this.getTelemetryFromDatabaseConfigForm.get("interval.endIntervalTimeUnit").setValidators([_.required]),this.getTelemetryFromDatabaseConfigForm.get("interval").setValidators([this.intervalValidator()]),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").setValidators([]),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").setValidators([])),this.getTelemetryFromDatabaseConfigForm.get("aggregation").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("orderBy").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("limit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.startInterval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.startIntervalTimeUnit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.endInterval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval.endIntervalTimeUnit").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("interval").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("startIntervalPattern").updateValueAndValidity({emitEvent:e}),this.getTelemetryFromDatabaseConfigForm.get("endIntervalPattern").updateValueAndValidity({emitEvent:e})}removeKey(e,t){const n=this.getTelemetryFromDatabaseConfigForm.get(t).value,r=n.indexOf(e);r>=0&&(n.splice(r,1),this.getTelemetryFromDatabaseConfigForm.get(t).setValue(n,{emitEvent:!0}))}clearChipGrid(){this.getTelemetryFromDatabaseConfigForm.get("latestTsKeyNames").patchValue([],{emitEvent:!0})}addKey(e,t){const n=e.input;let r=e.value;if((r||"").trim()){r=r.trim();let e=this.getTelemetryFromDatabaseConfigForm.get(t).value;e&&-1!==e.indexOf(r)||(e||(e=[]),e.push(r),this.getTelemetryFromDatabaseConfigForm.get(t).setValue(e,{emitEvent:!0}))}n&&(n.value="")}defaultPaddingEnable(){return this.getTelemetryFromDatabaseConfigForm.get("fetchMode").value===wt.ALL&&this.getTelemetryFromDatabaseConfigForm.get("aggregation").value===G.NONE}}e("GetTelemetryFromDatabaseConfigComponent",Xn),Xn.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Xn,deps:[{token:R.Store},{token:X.TranslateService},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Xn.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Xn,selector:"tb-enrichment-node-get-telemetry-from-database",usesInheritance:!0,ngImport:t,template:'
\n \n
\n help\n \n
\n
\n
tb.rulenode.fetch-interval
\n
\n \n {{ \'tb.rulenode.use-metadata-dynamic-interval\' | translate }}\n \n
\n
\n
\n \n {{ \'tb.rulenode.interval-start\' | translate }}\n \n \n {{ \'tb.rulenode.start-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n {{ \'tb.rulenode.time-unit\' | translate }}\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n {{ \'tb.rulenode.interval-end\' | translate }}\n \n \n {{ \'tb.rulenode.end-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n {{ \'tb.rulenode.time-unit\' | translate }}\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n error_outline\n
\n \n {{ \'tb.rulenode.fetch-timeseries-from-to\' | translate:\n {\n startInterval: getTelemetryFromDatabaseConfigForm.get(\'interval.startInterval\').value,\n endInterval: getTelemetryFromDatabaseConfigForm.get(\'interval.endInterval\').value,\n startIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get(\'interval.startIntervalTimeUnit\').value.toLowerCase(),\n endIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get(\'interval.endIntervalTimeUnit\').value.toLowerCase()\n } }}\n \n \n {{ "tb.rulenode.fetch-timeseries-from-to-invalid" | translate }}\n \n
\n
\n
\n \n
\n \n {{ \'tb.rulenode.start-interval\' | translate }}\n \n \n {{ \'tb.rulenode.start-interval-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.end-interval\' | translate }}\n \n \n {{ \'tb.rulenode.end-interval-required\' | translate }}\n \n \n \n \n
\n
\n
\n
\n
tb.rulenode.fetch-strategy
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n {{ deduplicationStrategiesHintTranslations.get(getTelemetryFromDatabaseConfigForm.get(\'fetchMode\').value) | translate }}\n
\n
\n
\n \n {{ \'aggregation.function\' | translate }}\n \n \n {{ aggregationTypesTranslations.get(aggregationTypes[aggregation]) | translate }}\n \n \n \n
\n \n {{ "tb.rulenode.order-by-timestamp" | translate }} \n \n \n {{ samplingOrdersTranslate.get(order) | translate }}\n \n \n \n \n {{ "tb.rulenode.limit" | translate }}\n \n {{ "tb.rulenode.limit-hint" | translate }}\n \n {{ \'tb.rulenode.limit-required\' | translate }}\n \n \n {{ \'tb.rulenode.limit-range\' | translate }}\n \n \n {{ \'tb.rulenode.limit-range\' | translate }}\n \n \n
\n
\n
\n
\n',styles:[":host .see-example{display:inline-block}:host .description-block{display:flex;align-items:center;border-radius:6px;border:1px solid #EAEAEA}:host .description-block .description-icon{font-size:24px;height:24px;min-height:24px;width:24px;min-width:24px;line-height:24px;color:#d9d9d9;margin:4px}:host .description-block .description-text{font-size:12px;line-height:16px;letter-spacing:.25px;margin:6px}:host .description-block.error{color:var(--mdc-theme-error, #f44336)}:host .description-block.error .description-icon{color:var(--mdc-theme-error, #f44336)}:host .item-center{align-items:center}:host .item-center .fetch-mod-toggle{width:100%}:host .hint-container{width:100%}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:$e.HelpPopupComponent,selector:"[tb-help-popup], [tb-help-popup-content]",inputs:["tb-help-popup","tb-help-popup-content","trigger-text","trigger-style","tb-help-popup-placement","tb-help-popup-style","hintMode"]},{kind:"component",type:et.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:ne.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:O.FormGroupName,selector:"[formGroupName]",inputs:["formGroupName"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:tt.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:nt.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:re.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:An,selector:"tb-example-hint",inputs:["hintText","popupHelpLink","textAlign"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Xn,decorators:[{type:n,args:[{selector:"tb-enrichment-node-get-telemetry-from-database",template:'
\n \n
\n help\n \n
\n
\n
tb.rulenode.fetch-interval
\n
\n \n {{ \'tb.rulenode.use-metadata-dynamic-interval\' | translate }}\n \n
\n
\n
\n \n {{ \'tb.rulenode.interval-start\' | translate }}\n \n \n {{ \'tb.rulenode.start-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n {{ \'tb.rulenode.time-unit\' | translate }}\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n \n {{ \'tb.rulenode.interval-end\' | translate }}\n \n \n {{ \'tb.rulenode.end-interval-value-required\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n {{ \'tb.rulenode.time-value-range\' | translate }}\n \n \n \n {{ \'tb.rulenode.time-unit\' | translate }}\n \n \n {{ timeUnitsTranslationMap.get(timeUnit) | translate }}\n \n \n \n
\n
\n error_outline\n
\n \n {{ \'tb.rulenode.fetch-timeseries-from-to\' | translate:\n {\n startInterval: getTelemetryFromDatabaseConfigForm.get(\'interval.startInterval\').value,\n endInterval: getTelemetryFromDatabaseConfigForm.get(\'interval.endInterval\').value,\n startIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get(\'interval.startIntervalTimeUnit\').value.toLowerCase(),\n endIntervalTimeUnit: getTelemetryFromDatabaseConfigForm.get(\'interval.endIntervalTimeUnit\').value.toLowerCase()\n } }}\n \n \n {{ "tb.rulenode.fetch-timeseries-from-to-invalid" | translate }}\n \n
\n
\n
\n \n
\n \n {{ \'tb.rulenode.start-interval\' | translate }}\n \n \n {{ \'tb.rulenode.start-interval-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.end-interval\' | translate }}\n \n \n {{ \'tb.rulenode.end-interval-required\' | translate }}\n \n \n \n \n
\n
\n
\n
\n
tb.rulenode.fetch-strategy
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n {{ deduplicationStrategiesHintTranslations.get(getTelemetryFromDatabaseConfigForm.get(\'fetchMode\').value) | translate }}\n
\n
\n
\n \n {{ \'aggregation.function\' | translate }}\n \n \n {{ aggregationTypesTranslations.get(aggregationTypes[aggregation]) | translate }}\n \n \n \n
\n \n {{ "tb.rulenode.order-by-timestamp" | translate }} \n \n \n {{ samplingOrdersTranslate.get(order) | translate }}\n \n \n \n \n {{ "tb.rulenode.limit" | translate }}\n \n {{ "tb.rulenode.limit-hint" | translate }}\n \n {{ \'tb.rulenode.limit-required\' | translate }}\n \n \n {{ \'tb.rulenode.limit-range\' | translate }}\n \n \n {{ \'tb.rulenode.limit-range\' | translate }}\n \n \n
\n
\n
\n
\n',styles:[":host .see-example{display:inline-block}:host .description-block{display:flex;align-items:center;border-radius:6px;border:1px solid #EAEAEA}:host .description-block .description-icon{font-size:24px;height:24px;min-height:24px;width:24px;min-width:24px;line-height:24px;color:#d9d9d9;margin:4px}:host .description-block .description-text{font-size:12px;line-height:16px;letter-spacing:.25px;margin:6px}:host .description-block.error{color:var(--mdc-theme-error, #f44336)}:host .description-block.error .description-icon{color:var(--mdc-theme-error, #f44336)}:host .item-center{align-items:center}:host .item-center .fetch-mod-toggle{width:100%}:host .hint-container{width:100%}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:R.Store},{type:X.TranslateService},{type:O.FormBuilder}]}});class er extends f{constructor(e,t,n){super(e),this.store=e,this.translate=t,this.fb=n}configForm(){return this.originatorAttributesConfigForm}onConfigurationSet(e){this.originatorAttributesConfigForm=this.fb.group({tellFailureIfAbsent:[e.tellFailureIfAbsent,[]],fetchTo:[e.fetchTo,[]],attributesControl:[e.attributesControl,[]]})}prepareInputConfig(e){return pe(e)&&(e.attributesControl={clientAttributeNames:se(e?.clientAttributeNames)?e.clientAttributeNames:[],latestTsKeyNames:se(e?.latestTsKeyNames)?e.latestTsKeyNames:[],serverAttributeNames:se(e?.serverAttributeNames)?e.serverAttributeNames:[],sharedAttributeNames:se(e?.sharedAttributeNames)?e.sharedAttributeNames:[],getLatestValueWithTs:!!se(e?.getLatestValueWithTs)&&e.getLatestValueWithTs}),{fetchTo:se(e?.fetchTo)?e.fetchTo:rn.METADATA,tellFailureIfAbsent:!!se(e?.tellFailureIfAbsent)&&e.tellFailureIfAbsent,attributesControl:se(e?.attributesControl)?e.attributesControl:null}}prepareOutputConfig(e){for(const t of Object.keys(e.attributesControl))e[t]=e.attributesControl[t];return delete e.attributesControl,e}}e("OriginatorAttributesConfigComponent",er),er.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:er,deps:[{token:R.Store},{token:X.TranslateService},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),er.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:er,selector:"tb-enrichment-node-originator-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
\n
tb.rulenode.originator-attributes
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n \n \n \n
\n
\n \n {{ \'tb.rulenode.tell-failure\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"component",type:ne.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:re.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:Kn,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"component",type:Hn,selector:"tb-select-attributes",inputs:["popupHelpLink"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:er,decorators:[{type:n,args:[{selector:"tb-enrichment-node-originator-attributes-config",template:'
\n
\n
\n
tb.rulenode.originator-attributes
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n \n \n \n
\n
\n \n {{ \'tb.rulenode.tell-failure\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:R.Store},{type:X.TranslateService},{type:O.FormBuilder}]}});class tr extends f{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.originatorFields=[];for(const e of It)this.originatorFields.push({value:e.value,name:this.translate.instant(e.name)})}configForm(){return this.originatorFieldsConfigForm}prepareOutputConfig(e){return me(e)}prepareInputConfig(e){return{dataMapping:se(e?.dataMapping)?e.dataMapping:null,ignoreNullStrings:se(e?.ignoreNullStrings)?e.ignoreNullStrings:null,fetchTo:se(e?.fetchTo)?e.fetchTo:rn.METADATA}}onConfigurationSet(e){this.originatorFieldsConfigForm=this.fb.group({dataMapping:[e.dataMapping,[_.required]],ignoreNullStrings:[e.ignoreNullStrings,[]],fetchTo:[e.fetchTo,[]]})}}e("OriginatorFieldsConfigComponent",tr),tr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:tr,deps:[{token:R.Store},{token:O.FormBuilder},{token:X.TranslateService}],target:t.ɵɵFactoryTarget.Component}),tr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:tr,selector:"tb-enrichment-node-originator-fields-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n
\n \n {{ \'tb.rulenode.skip-empty-fields\' | translate }}\n \n
\n
\n',dependencies:[{kind:"component",type:ne.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:re.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"component",type:Kn,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"component",type:zn,selector:"tb-sv-map-config",inputs:["selectOptions","disabled","labelText","requiredText","targetKeyPrefix","selectText","selectRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:tr,decorators:[{type:n,args:[{selector:"tb-enrichment-node-originator-fields-config",template:'
\n \n \n \n \n
\n \n {{ \'tb.rulenode.skip-empty-fields\' | translate }}\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder},{type:X.TranslateService}]}});class nr extends f{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.DataToFetch=_t,this.msgMetadataLabelTranslations=Kt,this.originatorFields=[],this.fetchToData=[];for(const e of Object.keys(It))this.originatorFields.push({value:It[e].value,name:this.translate.instant(It[e].name)});for(const e of Bt.keys())this.fetchToData.push({value:e,name:this.translate.instant(Bt.get(e))})}configForm(){return this.relatedAttributesConfigForm}prepareOutputConfig(e){e.dataToFetch===_t.FIELDS?(e.dataMapping=e.svMap,delete e.svMap):(e.dataMapping=e.kvMap,delete e.kvMap);const t={};if(e&&e.dataMapping)for(const n of Object.keys(e.dataMapping))t[n.trim()]=e.dataMapping[n];return e.dataMapping=t,delete e.svMap,delete e.kvMap,me(e)}prepareInputConfig(e){let t,n,r={[L.name.value]:`relatedEntity${this.translate.instant(L.name.name)}`},o={serialNumber:"sn"};return t=se(e?.telemetry)?e.telemetry?_t.LATEST_TELEMETRY:_t.ATTRIBUTES:se(e?.dataToFetch)?e.dataToFetch:_t.ATTRIBUTES,n=se(e?.attrMapping)?e.attrMapping:se(e?.dataMapping)?e.dataMapping:null,t===_t.FIELDS?r=n:o=n,{relationsQuery:se(e?.relationsQuery)?e.relationsQuery:null,dataToFetch:t,svMap:r,kvMap:o,fetchTo:se(e?.fetchTo)?e.fetchTo:rn.METADATA}}selectTranslation(e,t){return this.relatedAttributesConfigForm.get("dataToFetch").value===_t.LATEST_TELEMETRY?e:t}onConfigurationSet(e){this.relatedAttributesConfigForm=this.fb.group({relationsQuery:[e.relationsQuery,[_.required]],dataToFetch:[e.dataToFetch,[]],kvMap:[e.kvMap,[_.required]],svMap:[e.svMap,[_.required]],fetchTo:[e.fetchTo,[]]})}validatorTriggers(){return["dataToFetch"]}updateValidators(e){this.relatedAttributesConfigForm.get("dataToFetch").value===_t.FIELDS?(this.relatedAttributesConfigForm.get("svMap").enable({emitEvent:!1}),this.relatedAttributesConfigForm.get("kvMap").disable({emitEvent:!1}),this.relatedAttributesConfigForm.get("svMap").updateValueAndValidity()):(this.relatedAttributesConfigForm.get("svMap").disable({emitEvent:!1}),this.relatedAttributesConfigForm.get("kvMap").enable({emitEvent:!1}),this.relatedAttributesConfigForm.get("kvMap").updateValueAndValidity())}}e("RelatedAttributesConfigComponent",nr),nr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:nr,deps:[{token:R.Store},{token:O.FormBuilder},{token:X.TranslateService}],target:t.ɵɵFactoryTarget.Component}),nr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:nr,selector:"tb-enrichment-node-related-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n
tb.rulenode.data-to-fetch
\n \n \n {{ data.name }}\n \n \n \n \n \n \n \n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:tt.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:nt.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:Mn,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","labelText","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"component",type:Gn,selector:"tb-relations-query-config",inputs:["disabled","required"]},{kind:"component",type:Kn,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"component",type:zn,selector:"tb-sv-map-config",inputs:["selectOptions","disabled","labelText","requiredText","targetKeyPrefix","selectText","selectRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:nr,decorators:[{type:n,args:[{selector:"tb-enrichment-node-related-attributes-config",template:'
\n \n \n
\n
tb.rulenode.data-to-fetch
\n \n \n {{ data.name }}\n \n \n \n \n \n \n \n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder},{type:X.TranslateService}]}});class rr extends f{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.fetchToData=[],this.DataToFetch=_t;for(const e of Bt.keys())e!==_t.FIELDS&&this.fetchToData.push({value:e,name:this.translate.instant(Bt.get(e))})}configForm(){return this.tenantAttributesConfigForm}prepareInputConfig(e){let t,n;return t=se(e?.telemetry)?e.telemetry?_t.LATEST_TELEMETRY:_t.ATTRIBUTES:se(e?.dataToFetch)?e.dataToFetch:_t.ATTRIBUTES,n=se(e?.attrMapping)?e.attrMapping:se(e?.dataMapping)?e.dataMapping:null,{dataToFetch:t,dataMapping:n,fetchTo:se(e?.fetchTo)?e.fetchTo:rn.METADATA}}selectTranslation(e,t){return this.tenantAttributesConfigForm.get("dataToFetch").value===_t.LATEST_TELEMETRY?e:t}onConfigurationSet(e){this.tenantAttributesConfigForm=this.fb.group({dataToFetch:[e.dataToFetch,[]],dataMapping:[e.dataMapping,[_.required]],fetchTo:[e.fetchTo,[]]})}}e("TenantAttributesConfigComponent",rr),rr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:rr,deps:[{token:R.Store},{token:O.FormBuilder},{token:X.TranslateService}],target:t.ɵɵFactoryTarget.Component}),rr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:rr,selector:"tb-enrichment-node-tenant-attributes-config",usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.mapping-of-tenant
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}\n"],dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:Z.DefaultLayoutAlignDirective,selector:" [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg]",inputs:["fxLayoutAlign","fxLayoutAlign.xs","fxLayoutAlign.sm","fxLayoutAlign.md","fxLayoutAlign.lg","fxLayoutAlign.xl","fxLayoutAlign.lt-sm","fxLayoutAlign.lt-md","fxLayoutAlign.lt-lg","fxLayoutAlign.lt-xl","fxLayoutAlign.gt-xs","fxLayoutAlign.gt-sm","fxLayoutAlign.gt-md","fxLayoutAlign.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:tt.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:nt.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:Mn,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","labelText","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"component",type:Kn,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:rr,decorators:[{type:n,args:[{selector:"tb-enrichment-node-tenant-attributes-config",template:'
\n
tb.rulenode.mapping-of-tenant
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}\n"]}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder},{type:X.TranslateService}]}});class or extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.fetchDeviceCredentialsConfigForm}prepareInputConfig(e){return{fetchTo:se(e?.fetchTo)?e.fetchTo:rn.METADATA}}onConfigurationSet(e){this.fetchDeviceCredentialsConfigForm=this.fb.group({fetchTo:[e.fetchTo,[]]})}}e("FetchDeviceCredentialsConfigComponent",or),or.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:or,deps:[{token:R.Store},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),or.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:or,selector:"./tb-enrichment-node-fetch-device-credentials-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',dependencies:[{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Kn,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:or,decorators:[{type:n,args:[{selector:"./tb-enrichment-node-fetch-device-credentials-config",template:'
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder}]}});class ar{}e("RulenodeCoreConfigEnrichmentModule",ar),ar.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ar,deps:[],target:t.ɵɵFactoryTarget.NgModule}),ar.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:ar,declarations:[Yn,Zn,Wn,er,tr,Xn,nr,rr,Jn,or],imports:[Q,E,$n],exports:[Yn,Zn,Wn,er,tr,Xn,nr,rr,Jn,or]}),ar.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ar,imports:[Q,E,$n]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ar,decorators:[{type:c,args:[{declarations:[Yn,Zn,Wn,er,tr,Xn,nr,rr,Jn,or],imports:[Q,E,$n],exports:[Yn,Zn,Wn,er,tr,Xn,nr,rr,Jn,or]}]}]});class ir extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.allAzureIotHubCredentialsTypes=Qt,this.azureIotHubCredentialsTypeTranslationsMap=Jt}configForm(){return this.azureIotHubConfigForm}onConfigurationSet(e){this.azureIotHubConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[_.required]],host:[e?e.host:null,[_.required]],port:[e?e.port:null,[_.required,_.min(1),_.max(65535)]],connectTimeoutSec:[e?e.connectTimeoutSec:null,[_.required,_.min(1),_.max(200)]],clientId:[e?e.clientId:null,[_.required]],cleanSession:[!!e&&e.cleanSession,[]],ssl:[!!e&&e.ssl,[]],credentials:this.fb.group({type:[e&&e.credentials?e.credentials.type:null,[_.required]],sasKey:[e&&e.credentials?e.credentials.sasKey:null,[]],caCert:[e&&e.credentials?e.credentials.caCert:null,[]],caCertFileName:[e&&e.credentials?e.credentials.caCertFileName:null,[]],privateKey:[e&&e.credentials?e.credentials.privateKey:null,[]],privateKeyFileName:[e&&e.credentials?e.credentials.privateKeyFileName:null,[]],cert:[e&&e.credentials?e.credentials.cert:null,[]],certFileName:[e&&e.credentials?e.credentials.certFileName:null,[]],password:[e&&e.credentials?e.credentials.password:null,[]]})})}prepareOutputConfig(e){const t=e.credentials.type;return"sas"===t&&(e.credentials={type:t,sasKey:e.credentials.sasKey,caCert:e.credentials.caCert,caCertFileName:e.credentials.caCertFileName}),e}validatorTriggers(){return["credentials.type"]}updateValidators(e){const t=this.azureIotHubConfigForm.get("credentials"),n=t.get("type").value;switch(e&&t.reset({type:n},{emitEvent:!1}),t.get("sasKey").setValidators([]),t.get("privateKey").setValidators([]),t.get("privateKeyFileName").setValidators([]),t.get("cert").setValidators([]),t.get("certFileName").setValidators([]),n){case"sas":t.get("sasKey").setValidators([_.required]);break;case"cert.PEM":t.get("privateKey").setValidators([_.required]),t.get("privateKeyFileName").setValidators([_.required]),t.get("cert").setValidators([_.required]),t.get("certFileName").setValidators([_.required])}t.get("sasKey").updateValueAndValidity({emitEvent:e}),t.get("privateKey").updateValueAndValidity({emitEvent:e}),t.get("privateKeyFileName").updateValueAndValidity({emitEvent:e}),t.get("cert").updateValueAndValidity({emitEvent:e}),t.get("certFileName").updateValueAndValidity({emitEvent:e})}}e("AzureIotHubConfigComponent",ir),ir.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ir,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),ir.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ir,selector:"tb-external-node-azure-iot-hub-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic\n \n \n {{ \'tb.rulenode.topic-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.hostname\n \n \n {{ \'tb.rulenode.hostname-required\' | translate }}\n \n \n \n tb.rulenode.device-id\n \n \n {{ \'tb.rulenode.device-id-required\' | translate }}\n \n \n \n \n \n tb.rulenode.credentials\n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(azureIotHubConfigForm.get(\'credentials.type\').value) | translate }}\n \n \n
\n \n tb.rulenode.credentials-type\n \n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.sas-key\n \n \n \n {{ \'tb.rulenode.sas-key-required\' | translate }}\n \n \n \n \n \n \n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n \n
\n
\n
\n
\n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:j.NgSwitch,selector:"[ngSwitch]",inputs:["ngSwitch"]},{kind:"directive",type:j.NgSwitchCase,selector:"[ngSwitchCase]",inputs:["ngSwitchCase"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:We.MatAccordion,selector:"mat-accordion",inputs:["multi","hideToggle","displayMode","togglePosition"],exportAs:["matAccordion"]},{kind:"component",type:We.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{kind:"component",type:We.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{kind:"directive",type:We.MatExpansionPanelTitle,selector:"mat-panel-title"},{kind:"directive",type:We.MatExpansionPanelDescription,selector:"mat-panel-description"},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:O.FormGroupName,selector:"[formGroupName]",inputs:["formGroupName"]},{kind:"component",type:Ze.FileInputComponent,selector:"tb-file-input",inputs:["label","hint","accept","noFileText","inputId","allowedExtensions","dropLabel","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Xe.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ir,decorators:[{type:n,args:[{selector:"tb-external-node-azure-iot-hub-config",template:'
\n \n tb.rulenode.topic\n \n \n {{ \'tb.rulenode.topic-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.hostname\n \n \n {{ \'tb.rulenode.hostname-required\' | translate }}\n \n \n \n tb.rulenode.device-id\n \n \n {{ \'tb.rulenode.device-id-required\' | translate }}\n \n \n \n \n \n tb.rulenode.credentials\n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(azureIotHubConfigForm.get(\'credentials.type\').value) | translate }}\n \n \n
\n \n tb.rulenode.credentials-type\n \n \n {{ azureIotHubCredentialsTypeTranslationsMap.get(credentialsType) | translate }}\n \n \n \n {{ \'tb.rulenode.credentials-type-required\' | translate }}\n \n \n
\n \n \n \n \n tb.rulenode.sas-key\n \n \n \n {{ \'tb.rulenode.sas-key-required\' | translate }}\n \n \n \n \n \n \n \n \n \n \n \n \n \n tb.rulenode.private-key-password\n \n \n \n \n
\n
\n
\n
\n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class lr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.ackValues=["all","-1","0","1"],this.ToByteStandartCharsetTypesValues=Wt,this.ToByteStandartCharsetTypeTranslationMap=Zt}configForm(){return this.kafkaConfigForm}onConfigurationSet(e){this.kafkaConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[_.required]],keyPattern:[e?e.keyPattern:null],bootstrapServers:[e?e.bootstrapServers:null,[_.required]],retries:[e?e.retries:null,[_.min(0)]],batchSize:[e?e.batchSize:null,[_.min(0)]],linger:[e?e.linger:null,[_.min(0)]],bufferMemory:[e?e.bufferMemory:null,[_.min(0)]],acks:[e?e.acks:null,[_.required]],keySerializer:[e?e.keySerializer:null,[_.required]],valueSerializer:[e?e.valueSerializer:null,[_.required]],otherProperties:[e?e.otherProperties:null,[]],addMetadataKeyValuesAsKafkaHeaders:[!!e&&e.addMetadataKeyValuesAsKafkaHeaders,[]],kafkaHeadersCharset:[e?e.kafkaHeadersCharset:null,[]]})}validatorTriggers(){return["addMetadataKeyValuesAsKafkaHeaders"]}updateValidators(e){this.kafkaConfigForm.get("addMetadataKeyValuesAsKafkaHeaders").value?this.kafkaConfigForm.get("kafkaHeadersCharset").setValidators([_.required]):this.kafkaConfigForm.get("kafkaHeadersCharset").setValidators([]),this.kafkaConfigForm.get("kafkaHeadersCharset").updateValueAndValidity({emitEvent:e})}}e("KafkaConfigComponent",lr),lr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:lr,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),lr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:lr,selector:"tb-external-node-kafka-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.key-pattern\n \n tb.rulenode.general-pattern-hint\n \n
tb.rulenode.key-pattern-hint
\n \n tb.rulenode.bootstrap-servers\n \n \n {{ \'tb.rulenode.bootstrap-servers-required\' | translate }}\n \n \n \n tb.rulenode.retries\n \n \n {{ \'tb.rulenode.min-retries-message\' | translate }}\n \n \n \n tb.rulenode.batch-size-bytes\n \n \n {{ \'tb.rulenode.min-batch-size-bytes-message\' | translate }}\n \n \n \n tb.rulenode.linger-ms\n \n \n {{ \'tb.rulenode.min-linger-ms-message\' | translate }}\n \n \n \n tb.rulenode.buffer-memory-bytes\n \n \n {{ \'tb.rulenode.min-buffer-memory-bytes-message\' | translate }}\n \n \n \n tb.rulenode.acks\n \n \n {{ ackValue }}\n \n \n \n \n tb.rulenode.key-serializer\n \n \n {{ \'tb.rulenode.key-serializer-required\' | translate }}\n \n \n \n tb.rulenode.value-serializer\n \n \n {{ \'tb.rulenode.value-serializer-required\' | translate }}\n \n \n \n \n \n \n {{ \'tb.rulenode.add-metadata-key-values-as-kafka-headers\' | translate }}\n \n
tb.rulenode.add-metadata-key-values-as-kafka-headers-hint
\n \n tb.rulenode.charset-encoding\n \n \n {{ ToByteStandartCharsetTypeTranslationMap.get(charset) | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:J.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Fn,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:lr,decorators:[{type:n,args:[{selector:"tb-external-node-kafka-config",template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.key-pattern\n \n tb.rulenode.general-pattern-hint\n \n
tb.rulenode.key-pattern-hint
\n \n tb.rulenode.bootstrap-servers\n \n \n {{ \'tb.rulenode.bootstrap-servers-required\' | translate }}\n \n \n \n tb.rulenode.retries\n \n \n {{ \'tb.rulenode.min-retries-message\' | translate }}\n \n \n \n tb.rulenode.batch-size-bytes\n \n \n {{ \'tb.rulenode.min-batch-size-bytes-message\' | translate }}\n \n \n \n tb.rulenode.linger-ms\n \n \n {{ \'tb.rulenode.min-linger-ms-message\' | translate }}\n \n \n \n tb.rulenode.buffer-memory-bytes\n \n \n {{ \'tb.rulenode.min-buffer-memory-bytes-message\' | translate }}\n \n \n \n tb.rulenode.acks\n \n \n {{ ackValue }}\n \n \n \n \n tb.rulenode.key-serializer\n \n \n {{ \'tb.rulenode.key-serializer-required\' | translate }}\n \n \n \n tb.rulenode.value-serializer\n \n \n {{ \'tb.rulenode.value-serializer-required\' | translate }}\n \n \n \n \n \n \n {{ \'tb.rulenode.add-metadata-key-values-as-kafka-headers\' | translate }}\n \n
tb.rulenode.add-metadata-key-values-as-kafka-headers-hint
\n \n tb.rulenode.charset-encoding\n \n \n {{ ToByteStandartCharsetTypeTranslationMap.get(charset) | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class sr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.mqttConfigForm}onConfigurationSet(e){this.mqttConfigForm=this.fb.group({topicPattern:[e?e.topicPattern:null,[_.required]],host:[e?e.host:null,[_.required]],port:[e?e.port:null,[_.required,_.min(1),_.max(65535)]],connectTimeoutSec:[e?e.connectTimeoutSec:null,[_.required,_.min(1),_.max(200)]],clientId:[e?e.clientId:null,[]],appendClientIdSuffix:[{value:!!e&&e.appendClientIdSuffix,disabled:!(e&&ue(e.clientId))},[]],cleanSession:[!!e&&e.cleanSession,[]],retainedMessage:[!!e&&e.retainedMessage,[]],ssl:[!!e&&e.ssl,[]],credentials:[e?e.credentials:null,[]]})}updateValidators(e){ue(this.mqttConfigForm.get("clientId").value)?this.mqttConfigForm.get("appendClientIdSuffix").enable({emitEvent:!1}):this.mqttConfigForm.get("appendClientIdSuffix").disable({emitEvent:!1}),this.mqttConfigForm.get("appendClientIdSuffix").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["clientId"]}}e("MqttConfigComponent",sr),sr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:sr,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),sr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:sr,selector:"tb-external-node-mqtt-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n \n tb.rulenode.connect-timeout\n \n \n {{ \'tb.rulenode.connect-timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n
\n \n tb.rulenode.client-id\n \n {{\'tb.rulenode.client-id-hint\' | translate}}\n \n \n {{ \'tb.rulenode.append-client-id-suffix\' | translate }}\n \n
{{ "tb.rulenode.client-id-suffix-hint" | translate }}
\n \n {{ \'tb.rulenode.clean-session\' | translate }}\n \n \n {{ "tb.rulenode.retained-message" | translate }}\n \n \n {{ \'tb.rulenode.enable-ssl\' | translate }}\n \n \n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n"],dependencies:[{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:J.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Vn,selector:"tb-credentials-config",inputs:["required","disableCertPemCredentials","passwordFieldRequired"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:sr,decorators:[{type:n,args:[{selector:"tb-external-node-mqtt-config",template:'
\n \n tb.rulenode.topic-pattern\n \n \n {{ \'tb.rulenode.topic-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n \n tb.rulenode.connect-timeout\n \n \n {{ \'tb.rulenode.connect-timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n {{ \'tb.rulenode.connect-timeout-range\' | translate }}\n \n \n
\n \n tb.rulenode.client-id\n \n {{\'tb.rulenode.client-id-hint\' | translate}}\n \n \n {{ \'tb.rulenode.append-client-id-suffix\' | translate }}\n \n
{{ "tb.rulenode.client-id-suffix-hint" | translate }}
\n \n {{ \'tb.rulenode.clean-session\' | translate }}\n \n \n {{ "tb.rulenode.retained-message" | translate }}\n \n \n {{ \'tb.rulenode.enable-ssl\' | translate }}\n \n \n
\n',styles:[":host .tb-mqtt-credentials-panel-group{margin:0 6px}\n"]}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class mr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.notificationType=V,this.entityType=k}configForm(){return this.notificationConfigForm}onConfigurationSet(e){this.notificationConfigForm=this.fb.group({templateId:[e?e.templateId:null,[_.required]],targets:[e?e.targets:[],[_.required]]})}}e("NotificationConfigComponent",mr),mr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:mr,deps:[{token:R.Store},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),mr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:mr,selector:"tb-external-node-notification-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n
\n',dependencies:[{kind:"component",type:rt.EntityListComponent,selector:"tb-entity-list",inputs:["entityType","subType","labelText","placeholderText","requiredText","required","disabled","subscriptSizing","hint"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:ot.TemplateAutocompleteComponent,selector:"tb-template-autocomplete",inputs:["required","allowCreate","allowEdit","disabled","notificationTypes"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:mr,decorators:[{type:n,args:[{selector:"tb-external-node-notification-config",template:'
\n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder}]}});class pr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.pubSubConfigForm}onConfigurationSet(e){this.pubSubConfigForm=this.fb.group({projectId:[e?e.projectId:null,[_.required]],topicName:[e?e.topicName:null,[_.required]],serviceAccountKey:[e?e.serviceAccountKey:null,[_.required]],serviceAccountKeyFileName:[e?e.serviceAccountKeyFileName:null,[_.required]],messageAttributes:[e?e.messageAttributes:null,[]]})}}e("PubSubConfigComponent",pr),pr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:pr,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),pr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:pr,selector:"tb-external-node-pub-sub-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.gcp-project-id\n \n \n {{ \'tb.rulenode.gcp-project-id-required\' | translate }}\n \n \n \n tb.rulenode.pubsub-topic-name\n \n \n {{ \'tb.rulenode.pubsub-topic-name-required\' | translate }}\n \n \n \n \n \n
\n \n \n
\n',dependencies:[{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Ze.FileInputComponent,selector:"tb-file-input",inputs:["label","hint","accept","noFileText","inputId","allowedExtensions","dropLabel","contentConvertFunction","required","requiredAsError","disabled","existingFileName","readAsBinary","workFromFileObj","multipleFile"],outputs:["fileNameChanged"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Fn,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:Ce.SafePipe,name:"safe"},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:pr,decorators:[{type:n,args:[{selector:"tb-external-node-pub-sub-config",template:'
\n \n tb.rulenode.gcp-project-id\n \n \n {{ \'tb.rulenode.gcp-project-id-required\' | translate }}\n \n \n \n tb.rulenode.pubsub-topic-name\n \n \n {{ \'tb.rulenode.pubsub-topic-name-required\' | translate }}\n \n \n \n \n \n
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class ur extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"]}configForm(){return this.rabbitMqConfigForm}onConfigurationSet(e){this.rabbitMqConfigForm=this.fb.group({exchangeNamePattern:[e?e.exchangeNamePattern:null,[]],routingKeyPattern:[e?e.routingKeyPattern:null,[]],messageProperties:[e?e.messageProperties:null,[]],host:[e?e.host:null,[_.required]],port:[e?e.port:null,[_.required,_.min(1),_.max(65535)]],virtualHost:[e?e.virtualHost:null,[]],username:[e?e.username:null,[]],password:[e?e.password:null,[]],automaticRecoveryEnabled:[!!e&&e.automaticRecoveryEnabled,[]],connectionTimeout:[e?e.connectionTimeout:null,[_.min(0)]],handshakeTimeout:[e?e.handshakeTimeout:null,[_.min(0)]],clientProperties:[e?e.clientProperties:null,[]]})}}e("RabbitMqConfigComponent",ur),ur.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ur,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),ur.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:ur,selector:"tb-external-node-rabbit-mq-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.exchange-name-pattern\n \n \n \n tb.rulenode.routing-key-pattern\n \n \n \n tb.rulenode.message-properties\n \n \n {{ property }}\n \n \n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n
\n \n tb.rulenode.virtual-host\n \n \n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n \n {{ \'tb.rulenode.automatic-recovery\' | translate }}\n \n \n tb.rulenode.connection-timeout-ms\n \n \n {{ \'tb.rulenode.min-connection-timeout-ms-message\' | translate }}\n \n \n \n tb.rulenode.handshake-timeout-ms\n \n \n {{ \'tb.rulenode.min-handshake-timeout-ms-message\' | translate }}\n \n \n \n \n \n
\n',dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:J.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Xe.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"component",type:Fn,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:ur,decorators:[{type:n,args:[{selector:"tb-external-node-rabbit-mq-config",template:'
\n \n tb.rulenode.exchange-name-pattern\n \n \n \n tb.rulenode.routing-key-pattern\n \n \n \n tb.rulenode.message-properties\n \n \n {{ property }}\n \n \n \n
\n \n tb.rulenode.host\n \n \n {{ \'tb.rulenode.host-required\' | translate }}\n \n \n \n tb.rulenode.port\n \n \n {{ \'tb.rulenode.port-required\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n {{ \'tb.rulenode.port-range\' | translate }}\n \n \n
\n \n tb.rulenode.virtual-host\n \n \n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n \n {{ \'tb.rulenode.automatic-recovery\' | translate }}\n \n \n tb.rulenode.connection-timeout-ms\n \n \n {{ \'tb.rulenode.min-connection-timeout-ms-message\' | translate }}\n \n \n \n tb.rulenode.handshake-timeout-ms\n \n \n {{ \'tb.rulenode.min-handshake-timeout-ms-message\' | translate }}\n \n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class dr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.proxySchemes=["http","https"],this.httpRequestTypes=Object.keys(Yt)}configForm(){return this.restApiCallConfigForm}onConfigurationSet(e){this.restApiCallConfigForm=this.fb.group({restEndpointUrlPattern:[e?e.restEndpointUrlPattern:null,[_.required]],requestMethod:[e?e.requestMethod:null,[_.required]],useSimpleClientHttpFactory:[!!e&&e.useSimpleClientHttpFactory,[]],parseToPlainText:[!!e&&e.parseToPlainText,[]],ignoreRequestBody:[!!e&&e.ignoreRequestBody,[]],enableProxy:[!!e&&e.enableProxy,[]],useSystemProxyProperties:[!!e&&e.enableProxy,[]],proxyScheme:[e?e.proxyHost:null,[]],proxyHost:[e?e.proxyHost:null,[]],proxyPort:[e?e.proxyPort:null,[]],proxyUser:[e?e.proxyUser:null,[]],proxyPassword:[e?e.proxyPassword:null,[]],readTimeoutMs:[e?e.readTimeoutMs:null,[]],maxParallelRequestsCount:[e?e.maxParallelRequestsCount:null,[_.min(0)]],headers:[e?e.headers:null,[]],useRedisQueueForMsgPersistence:[!!e&&e.useRedisQueueForMsgPersistence,[]],trimQueue:[!!e&&e.trimQueue,[]],maxQueueSize:[e?e.maxQueueSize:null,[]],credentials:[e?e.credentials:null,[]]})}validatorTriggers(){return["useSimpleClientHttpFactory","useRedisQueueForMsgPersistence","enableProxy","useSystemProxyProperties"]}updateValidators(e){const t=this.restApiCallConfigForm.get("useSimpleClientHttpFactory").value,n=this.restApiCallConfigForm.get("useRedisQueueForMsgPersistence").value,r=this.restApiCallConfigForm.get("enableProxy").value,o=this.restApiCallConfigForm.get("useSystemProxyProperties").value;r&&!o?(this.restApiCallConfigForm.get("proxyHost").setValidators(r?[_.required]:[]),this.restApiCallConfigForm.get("proxyPort").setValidators(r?[_.required,_.min(1),_.max(65535)]:[])):(this.restApiCallConfigForm.get("proxyHost").setValidators([]),this.restApiCallConfigForm.get("proxyPort").setValidators([]),t?this.restApiCallConfigForm.get("readTimeoutMs").setValidators([]):this.restApiCallConfigForm.get("readTimeoutMs").setValidators([_.min(0)])),n?this.restApiCallConfigForm.get("maxQueueSize").setValidators([_.min(0)]):this.restApiCallConfigForm.get("maxQueueSize").setValidators([]),this.restApiCallConfigForm.get("readTimeoutMs").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("maxQueueSize").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("proxyHost").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("proxyPort").updateValueAndValidity({emitEvent:e}),this.restApiCallConfigForm.get("credentials").updateValueAndValidity({emitEvent:e})}}e("RestApiCallConfigComponent",dr),dr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:dr,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),dr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:dr,selector:"tb-external-node-rest-api-call-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.endpoint-url-pattern\n \n \n {{ \'tb.rulenode.endpoint-url-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.request-method\n \n \n {{ requestType }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n \n {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}\n \n \n {{ \'tb.rulenode.parse-to-plain-text\' | translate }}\n \n
tb.rulenode.parse-to-plain-text-hint
\n \n {{ \'tb.rulenode.ignore-request-body\' | translate }}\n \n
\n \n {{ \'tb.rulenode.use-system-proxy-properties\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-scheme\n \n \n {{ proxyScheme }}\n \n \n \n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n
\n \n tb.rulenode.read-timeout\n \n tb.rulenode.read-timeout-hint\n \n \n tb.rulenode.max-parallel-requests-count\n \n tb.rulenode.max-parallel-requests-count-hint\n \n \n
\n \n \n \n {{ \'tb.rulenode.use-redis-queue\' | translate }}\n \n
\n \n {{ \'tb.rulenode.trim-redis-queue\' | translate }}\n \n \n tb.rulenode.redis-queue-max-size\n \n \n
\n \n
\n',dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:J.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Vn,selector:"tb-credentials-config",inputs:["required","disableCertPemCredentials","passwordFieldRequired"]},{kind:"component",type:Fn,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:Ce.SafePipe,name:"safe"},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:dr,decorators:[{type:n,args:[{selector:"tb-external-node-rest-api-call-config",template:'
\n \n tb.rulenode.endpoint-url-pattern\n \n \n {{ \'tb.rulenode.endpoint-url-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.request-method\n \n \n {{ requestType }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n \n {{ \'tb.rulenode.use-simple-client-http-factory\' | translate }}\n \n \n {{ \'tb.rulenode.parse-to-plain-text\' | translate }}\n \n
tb.rulenode.parse-to-plain-text-hint
\n \n {{ \'tb.rulenode.ignore-request-body\' | translate }}\n \n
\n \n {{ \'tb.rulenode.use-system-proxy-properties\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-scheme\n \n \n {{ proxyScheme }}\n \n \n \n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n
\n \n tb.rulenode.read-timeout\n \n tb.rulenode.read-timeout-hint\n \n \n tb.rulenode.max-parallel-requests-count\n \n tb.rulenode.max-parallel-requests-count-hint\n \n \n
\n \n \n \n {{ \'tb.rulenode.use-redis-queue\' | translate }}\n \n
\n \n {{ \'tb.rulenode.trim-redis-queue\' | translate }}\n \n \n tb.rulenode.redis-queue-max-size\n \n \n
\n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class cr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.smtpProtocols=["smtp","smtps"],this.tlsVersions=["TLSv1","TLSv1.1","TLSv1.2","TLSv1.3"]}configForm(){return this.sendEmailConfigForm}onConfigurationSet(e){this.sendEmailConfigForm=this.fb.group({useSystemSmtpSettings:[!!e&&e.useSystemSmtpSettings,[]],smtpProtocol:[e?e.smtpProtocol:null,[]],smtpHost:[e?e.smtpHost:null,[]],smtpPort:[e?e.smtpPort:null,[]],timeout:[e?e.timeout:null,[]],enableTls:[!!e&&e.enableTls,[]],tlsVersion:[e?e.tlsVersion:null,[]],enableProxy:[!!e&&e.enableProxy,[]],proxyHost:[e?e.proxyHost:null,[]],proxyPort:[e?e.proxyPort:null,[]],proxyUser:[e?e.proxyUser:null,[]],proxyPassword:[e?e.proxyPassword:null,[]],username:[e?e.username:null,[]],password:[e?e.password:null,[]]})}validatorTriggers(){return["useSystemSmtpSettings","enableProxy"]}updateValidators(e){const t=this.sendEmailConfigForm.get("useSystemSmtpSettings").value,n=this.sendEmailConfigForm.get("enableProxy").value;t?(this.sendEmailConfigForm.get("smtpProtocol").setValidators([]),this.sendEmailConfigForm.get("smtpHost").setValidators([]),this.sendEmailConfigForm.get("smtpPort").setValidators([]),this.sendEmailConfigForm.get("timeout").setValidators([]),this.sendEmailConfigForm.get("proxyHost").setValidators([]),this.sendEmailConfigForm.get("proxyPort").setValidators([])):(this.sendEmailConfigForm.get("smtpProtocol").setValidators([_.required]),this.sendEmailConfigForm.get("smtpHost").setValidators([_.required]),this.sendEmailConfigForm.get("smtpPort").setValidators([_.required,_.min(1),_.max(65535)]),this.sendEmailConfigForm.get("timeout").setValidators([_.required,_.min(0)]),this.sendEmailConfigForm.get("proxyHost").setValidators(n?[_.required]:[]),this.sendEmailConfigForm.get("proxyPort").setValidators(n?[_.required,_.min(1),_.max(65535)]:[])),this.sendEmailConfigForm.get("smtpProtocol").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("smtpHost").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("smtpPort").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("timeout").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("proxyHost").updateValueAndValidity({emitEvent:e}),this.sendEmailConfigForm.get("proxyPort").updateValueAndValidity({emitEvent:e})}}e("SendEmailConfigComponent",cr),cr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:cr,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),cr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:cr,selector:"tb-external-node-send-email-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}\n \n
\n \n tb.rulenode.smtp-protocol\n \n \n {{ smtpProtocol.toUpperCase() }}\n \n \n \n
\n \n tb.rulenode.smtp-host\n \n \n {{ \'tb.rulenode.smtp-host-required\' | translate }}\n \n \n \n tb.rulenode.smtp-port\n \n \n {{ \'tb.rulenode.smtp-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.timeout-msec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-msec-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.enable-tls\' | translate }}\n \n \n tb.rulenode.tls-version\n \n \n {{ tlsVersion }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n
\n
\n',dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:at.TbCheckboxComponent,selector:"tb-checkbox",inputs:["disabled","trueValue","falseValue"],outputs:["valueChange"]},{kind:"component",type:J.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Xe.TogglePasswordComponent,selector:"tb-toggle-password"},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:cr,decorators:[{type:n,args:[{selector:"tb-external-node-send-email-config",template:'
\n \n {{ \'tb.rulenode.use-system-smtp-settings\' | translate }}\n \n
\n \n tb.rulenode.smtp-protocol\n \n \n {{ smtpProtocol.toUpperCase() }}\n \n \n \n
\n \n tb.rulenode.smtp-host\n \n \n {{ \'tb.rulenode.smtp-host-required\' | translate }}\n \n \n \n tb.rulenode.smtp-port\n \n \n {{ \'tb.rulenode.smtp-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n {{ \'tb.rulenode.smtp-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.timeout-msec\n \n \n {{ \'tb.rulenode.timeout-required\' | translate }}\n \n \n {{ \'tb.rulenode.min-timeout-msec-message\' | translate }}\n \n \n \n {{ \'tb.rulenode.enable-tls\' | translate }}\n \n \n tb.rulenode.tls-version\n \n \n {{ tlsVersion }}\n \n \n \n \n {{ \'tb.rulenode.enable-proxy\' | translate }}\n \n
\n
\n \n tb.rulenode.proxy-host\n \n \n {{ \'tb.rulenode.proxy-host-required\' | translate }}\n \n \n \n tb.rulenode.proxy-port\n \n \n {{ \'tb.rulenode.proxy-port-required\' | translate }}\n \n \n {{ \'tb.rulenode.proxy-port-range\' | translate }}\n \n \n
\n \n tb.rulenode.proxy-user\n \n \n \n tb.rulenode.proxy-password\n \n \n
\n \n tb.rulenode.username\n \n \n \n tb.rulenode.password\n \n \n \n
\n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class fr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.sendSmsConfigForm}onConfigurationSet(e){this.sendSmsConfigForm=this.fb.group({numbersToTemplate:[e?e.numbersToTemplate:null,[_.required]],smsMessageTemplate:[e?e.smsMessageTemplate:null,[_.required]],useSystemSmsSettings:[!!e&&e.useSystemSmsSettings,[]],smsProviderConfiguration:[e?e.smsProviderConfiguration:null,[]]})}validatorTriggers(){return["useSystemSmsSettings"]}updateValidators(e){this.sendSmsConfigForm.get("useSystemSmsSettings").value?this.sendSmsConfigForm.get("smsProviderConfiguration").setValidators([]):this.sendSmsConfigForm.get("smsProviderConfiguration").setValidators([_.required]),this.sendSmsConfigForm.get("smsProviderConfiguration").updateValueAndValidity({emitEvent:e})}}e("SendSmsConfigComponent",fr),fr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:fr,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),fr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:fr,selector:"tb-external-node-send-sms-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.numbers-to-template\n \n \n {{ \'tb.rulenode.numbers-to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.sms-message-template\n \n \n {{ \'tb.rulenode.sms-message-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-system-sms-settings\' | translate }}\n \n \n \n
\n',dependencies:[{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:J.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:it.SmsProviderConfigurationComponent,selector:"tb-sms-provider-configuration",inputs:["required","disabled"]},{kind:"pipe",type:Ce.SafePipe,name:"safe"},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:fr,decorators:[{type:n,args:[{selector:"tb-external-node-send-sms-config",template:'
\n \n tb.rulenode.numbers-to-template\n \n \n {{ \'tb.rulenode.numbers-to-template-required\' | translate }}\n \n \n \n \n tb.rulenode.sms-message-template\n \n \n {{ \'tb.rulenode.sms-message-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-system-sms-settings\' | translate }}\n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class gr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.slackChanelTypes=Object.keys(w),this.slackChanelTypesTranslateMap=P}configForm(){return this.slackConfigForm}onConfigurationSet(e){this.slackConfigForm=this.fb.group({botToken:[e?e.botToken:null],useSystemSettings:[!!e&&e.useSystemSettings],messageTemplate:[e?e.messageTemplate:null,[_.required]],conversationType:[e?e.conversationType:null,[_.required]],conversation:[e?e.conversation:null,[_.required]]})}validatorTriggers(){return["useSystemSettings"]}updateValidators(e){this.slackConfigForm.get("useSystemSettings").value?this.slackConfigForm.get("botToken").clearValidators():this.slackConfigForm.get("botToken").setValidators([_.required]),this.slackConfigForm.get("botToken").updateValueAndValidity({emitEvent:e})}}e("SlackConfigComponent",gr),gr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gr,deps:[{token:R.Store},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),gr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:gr,selector:"tb-external-node-slack-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.message-template\n \n \n {{ \'tb.rulenode.message-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-system-slack-settings\' | translate }}\n \n \n tb.rulenode.slack-api-token\n \n \n {{ \'tb.rulenode.slack-api-token-required\' | translate }}\n \n \n \n \n \n {{ slackChanelTypesTranslateMap.get(slackChanelType) | translate }}\n \n \n \n \n
\n',styles:[":host .tb-title{display:block;padding-bottom:6px}:host ::ng-deep .mat-mdc-radio-group{display:flex;flex-direction:row;margin-bottom:22px;gap:12px}:host ::ng-deep .mat-mdc-radio-group .mat-mdc-radio-button{flex:1 1 100%;padding:4px;border:1px solid rgba(0,0,0,.12);border-radius:6px}@media screen and (max-width: 599px){:host ::ng-deep .mat-mdc-radio-group{flex-direction:column}}\n"],dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:J.MatCheckbox,selector:"mat-checkbox",inputs:["disableRipple","color","tabIndex"],exportAs:["matCheckbox"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:lt.MatRadioGroup,selector:"mat-radio-group",exportAs:["matRadioGroup"]},{kind:"component",type:lt.MatRadioButton,selector:"mat-radio-button",inputs:["disableRipple","tabIndex"],exportAs:["matRadioButton"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:st.SlackConversationAutocompleteComponent,selector:"tb-slack-conversation-autocomplete",inputs:["labelText","requiredText","required","disabled","slackChanelType","token"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:gr,decorators:[{type:n,args:[{selector:"tb-external-node-slack-config",template:'
\n \n tb.rulenode.message-template\n \n \n {{ \'tb.rulenode.message-template-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n {{ \'tb.rulenode.use-system-slack-settings\' | translate }}\n \n \n tb.rulenode.slack-api-token\n \n \n {{ \'tb.rulenode.slack-api-token-required\' | translate }}\n \n \n \n \n \n {{ slackChanelTypesTranslateMap.get(slackChanelType) | translate }}\n \n \n \n \n
\n',styles:[":host .tb-title{display:block;padding-bottom:6px}:host ::ng-deep .mat-mdc-radio-group{display:flex;flex-direction:row;margin-bottom:22px;gap:12px}:host ::ng-deep .mat-mdc-radio-group .mat-mdc-radio-button{flex:1 1 100%;padding:4px;border:1px solid rgba(0,0,0,.12);border-radius:6px}@media screen and (max-width: 599px){:host ::ng-deep .mat-mdc-radio-group{flex-direction:column}}\n"]}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder}]}});class yr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.snsConfigForm}onConfigurationSet(e){this.snsConfigForm=this.fb.group({topicArnPattern:[e?e.topicArnPattern:null,[_.required]],accessKeyId:[e?e.accessKeyId:null,[_.required]],secretAccessKey:[e?e.secretAccessKey:null,[_.required]],region:[e?e.region:null,[_.required]]})}}e("SnsConfigComponent",yr),yr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yr,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),yr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:yr,selector:"tb-external-node-sns-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.topic-arn-pattern\n \n \n {{ \'tb.rulenode.topic-arn-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:yr,decorators:[{type:n,args:[{selector:"tb-external-node-sns-config",template:'
\n \n tb.rulenode.topic-arn-pattern\n \n \n {{ \'tb.rulenode.topic-arn-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class xr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.sqsQueueType=Ut,this.sqsQueueTypes=Object.keys(Ut),this.sqsQueueTypeTranslationsMap=Ht}configForm(){return this.sqsConfigForm}onConfigurationSet(e){this.sqsConfigForm=this.fb.group({queueType:[e?e.queueType:null,[_.required]],queueUrlPattern:[e?e.queueUrlPattern:null,[_.required]],delaySeconds:[e?e.delaySeconds:null,[_.min(0),_.max(900)]],messageAttributes:[e?e.messageAttributes:null,[]],accessKeyId:[e?e.accessKeyId:null,[_.required]],secretAccessKey:[e?e.secretAccessKey:null,[_.required]],region:[e?e.region:null,[_.required]]})}}e("SqsConfigComponent",xr),xr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xr,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),xr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:xr,selector:"tb-external-node-sqs-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.queue-type\n \n \n {{ sqsQueueTypeTranslationsMap.get(type) | translate }}\n \n \n \n \n tb.rulenode.queue-url-pattern\n \n \n {{ \'tb.rulenode.queue-url-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.delay-seconds\n \n \n {{ \'tb.rulenode.min-delay-seconds-message\' | translate }}\n \n \n {{ \'tb.rulenode.max-delay-seconds-message\' | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n',dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Fn,selector:"tb-kv-map-config-old",inputs:["disabled","uniqueKeyValuePairValidator","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","required"]},{kind:"pipe",type:Ce.SafePipe,name:"safe"},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:xr,decorators:[{type:n,args:[{selector:"tb-external-node-sqs-config",template:'
\n \n tb.rulenode.queue-type\n \n \n {{ sqsQueueTypeTranslationsMap.get(type) | translate }}\n \n \n \n \n tb.rulenode.queue-url-pattern\n \n \n {{ \'tb.rulenode.queue-url-pattern-required\' | translate }}\n \n tb.rulenode.general-pattern-hint\n \n \n tb.rulenode.delay-seconds\n \n \n {{ \'tb.rulenode.min-delay-seconds-message\' | translate }}\n \n \n {{ \'tb.rulenode.max-delay-seconds-message\' | translate }}\n \n \n \n
\n \n \n \n tb.rulenode.aws-access-key-id\n \n \n {{ \'tb.rulenode.aws-access-key-id-required\' | translate }}\n \n \n \n tb.rulenode.aws-secret-access-key\n \n \n {{ \'tb.rulenode.aws-secret-access-key-required\' | translate }}\n \n \n \n tb.rulenode.aws-region\n \n \n {{ \'tb.rulenode.aws-region-required\' | translate }}\n \n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class br{}e("RulenodeCoreConfigExternalModule",br),br.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:br,deps:[],target:t.ɵɵFactoryTarget.NgModule}),br.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:br,declarations:[yr,xr,pr,lr,sr,mr,ur,dr,cr,ir,fr,gr],imports:[Q,E,_e,$n],exports:[yr,xr,pr,lr,sr,mr,ur,dr,cr,ir,fr,gr]}),br.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:br,imports:[Q,E,_e,$n]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:br,decorators:[{type:c,args:[{declarations:[yr,xr,pr,lr,sr,mr,ur,dr,cr,ir,fr,gr],imports:[Q,E,_e,$n],exports:[yr,xr,pr,lr,sr,mr,ur,dr,cr,ir,fr,gr]}]}]});class hr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.searchText=""}configForm(){return this.alarmStatusConfigForm}prepareInputConfig(e){return{alarmStatusList:se(e?.alarmStatusList)?e.alarmStatusList:null}}onConfigurationSet(e){this.alarmStatusConfigForm=this.fb.group({alarmStatusList:[e.alarmStatusList,[_.required]]})}}e("CheckAlarmStatusComponent",hr),hr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:hr,deps:[{token:R.Store},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),hr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:hr,selector:"tb-filter-node-check-alarm-status-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.alarm-status
\n
\n tb.rulenode.alarm-required\n
\n
\n \n
\n\n\n\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:jn,selector:"tb-alarm-status-select"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:hr,decorators:[{type:n,args:[{selector:"tb-filter-node-check-alarm-status-config",template:'
\n
\n
tb.rulenode.alarm-status
\n
\n tb.rulenode.alarm-required\n
\n
\n \n
\n\n\n\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder}]}});class vr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.checkMessageConfigForm}prepareInputConfig(e){return{messageNames:se(e?.messageNames)?e.messageNames:[],metadataNames:se(e?.metadataNames)?e.metadataNames:[],checkAllKeys:!!se(e?.checkAllKeys)&&e.checkAllKeys}}prepareOutputConfig(e){return{messageNames:se(e?.messageNames)?e.messageNames:[],metadataNames:se(e?.metadataNames)?e.metadataNames:[],checkAllKeys:e.checkAllKeys}}atLeastOne(e,t=null){return n=>{t||(t=Object.keys(n.controls));return n?.controls&&t.some((t=>!e(n.controls[t])))?null:{atLeastOne:!0}}}onConfigurationSet(e){this.checkMessageConfigForm=this.fb.group({messageNames:[e.messageNames,[]],metadataNames:[e.metadataNames,[]],checkAllKeys:[e.checkAllKeys,[]]},{validators:this.atLeastOne(_.required,["messageNames","metadataNames"])})}get touchedValidationControl(){return["messageNames","metadataNames"].some((e=>this.checkMessageConfigForm.get(e).touched))}}e("CheckMessageConfigComponent",vr),vr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:vr,deps:[{token:R.Store},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),vr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:vr,selector:"tb-filter-node-check-message-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.fields-to-check
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n help\n \n \n help\n \n
\n \n {{ \'tb.rulenode.check-all-keys\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"component",type:et.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:W.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:ne.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:re.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:vr,decorators:[{type:n,args:[{selector:"tb-filter-node-check-message-config",template:'
\n
\n
tb.rulenode.fields-to-check
\n
\n tb.rulenode.at-least-one-field-required\n
\n
\n \n help\n \n \n help\n \n
\n \n {{ \'tb.rulenode.check-all-keys\' | translate }}\n \n
\n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder}]}});class Cr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.entitySearchDirection=Object.values(C),this.entitySearchDirectionTranslationsMap=F}configForm(){return this.checkRelationConfigForm}prepareInputConfig(e){return{checkForSingleEntity:!!se(e?.checkForSingleEntity)&&e.checkForSingleEntity,direction:se(e?.direction)?e.direction:null,entityType:se(e?.entityType)?e.entityType:null,entityId:se(e?.entityId)?e.entityId:null,relationType:se(e?.relationType)?e.relationType:null}}onConfigurationSet(e){this.checkRelationConfigForm=this.fb.group({checkForSingleEntity:[e.checkForSingleEntity,[]],direction:[e.direction,[]],entityType:[e.entityType,e&&e.checkForSingleEntity?[_.required]:[]],entityId:[e.entityId,e&&e.checkForSingleEntity?[_.required]:[]],relationType:[e.relationType,[_.required]]})}validatorTriggers(){return["checkForSingleEntity"]}updateValidators(e){const t=this.checkRelationConfigForm.get("checkForSingleEntity").value;this.checkRelationConfigForm.get("entityType").setValidators(t?[_.required]:[]),this.checkRelationConfigForm.get("entityType").updateValueAndValidity({emitEvent:e}),this.checkRelationConfigForm.get("entityId").setValidators(t?[_.required]:[]),this.checkRelationConfigForm.get("entityId").updateValueAndValidity({emitEvent:e})}}e("CheckRelationConfigComponent",Cr),Cr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Cr,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Cr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Cr,selector:"tb-filter-node-check-relation-config",usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.relation-search-parameters
\n
\n \n {{ \'relation.direction\' | translate }}\n \n \n {{ entitySearchDirectionTranslationsMap.get(direction) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n \n
\n \n {{ \'tb.rulenode.check-relation-to-specific-entity\' | translate }}\n \n
\n
\n \n \n \n \n
\n
\n
\n',styles:[":host .slide-toggle{margin-bottom:18px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:mt.EntityAutocompleteComponent,selector:"tb-entity-autocomplete",inputs:["entityType","entitySubtype","excludeEntityIds","labelText","requiredText","useFullEntityId","appearance","required","disabled"],outputs:["entityChanged"]},{kind:"component",type:Fe.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"component",type:Je.RelationTypeAutocompleteComponent,selector:"tb-relation-type-autocomplete",inputs:["showLabel","additionalClasses","appearance","required","disabled","subscriptSizing"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:ne.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:re.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Cr,decorators:[{type:n,args:[{selector:"tb-filter-node-check-relation-config",template:'
\n
tb.rulenode.relation-search-parameters
\n
\n \n {{ \'relation.direction\' | translate }}\n \n \n {{ entitySearchDirectionTranslationsMap.get(direction) | translate }} tb.rulenode.relations-query-config-direction-suffix\n \n \n \n \n \n
\n \n {{ \'tb.rulenode.check-relation-to-specific-entity\' | translate }}\n \n
\n
\n \n \n \n \n
\n
\n
\n',styles:[":host .slide-toggle{margin-bottom:18px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class Fr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.perimeterType=St,this.perimeterTypes=Object.values(St),this.perimeterTypeTranslationMap=qt,this.rangeUnits=Object.values(Et),this.rangeUnitTranslationMap=Gt,this.defaultPaddingEnable=!0}configForm(){return this.geoFilterConfigForm}prepareInputConfig(e){return{latitudeKeyName:se(e?.latitudeKeyName)?e.latitudeKeyName:null,longitudeKeyName:se(e?.longitudeKeyName)?e.longitudeKeyName:null,perimeterType:se(e?.perimeterType)?e.perimeterType:null,fetchPerimeterInfoFromMessageMetadata:!!se(e?.fetchPerimeterInfoFromMessageMetadata)&&e.fetchPerimeterInfoFromMessageMetadata,perimeterKeyName:se(e?.perimeterKeyName)?e.perimeterKeyName:null,centerLatitude:se(e?.centerLatitude)?e.centerLatitude:null,centerLongitude:se(e?.centerLongitude)?e.centerLongitude:null,range:se(e?.range)?e.range:null,rangeUnit:se(e?.rangeUnit)?e.rangeUnit:null,polygonsDefinition:se(e?.polygonsDefinition)?e.polygonsDefinition:null}}onConfigurationSet(e){this.geoFilterConfigForm=this.fb.group({latitudeKeyName:[e.latitudeKeyName,[_.required]],longitudeKeyName:[e.longitudeKeyName,[_.required]],perimeterType:[e.perimeterType,[_.required]],fetchPerimeterInfoFromMessageMetadata:[e.fetchPerimeterInfoFromMessageMetadata,[]],perimeterKeyName:[e.perimeterKeyName,[]],centerLatitude:[e.centerLatitude,[]],centerLongitude:[e.centerLongitude,[]],range:[e.range,[]],rangeUnit:[e.rangeUnit,[]],polygonsDefinition:[e.polygonsDefinition,[]]})}validatorTriggers(){return["fetchPerimeterInfoFromMessageMetadata","perimeterType"]}updateValidators(e){const t=this.geoFilterConfigForm.get("fetchPerimeterInfoFromMessageMetadata").value,n=this.geoFilterConfigForm.get("perimeterType").value;t?this.geoFilterConfigForm.get("perimeterKeyName").setValidators([_.required]):this.geoFilterConfigForm.get("perimeterKeyName").setValidators([]),t||n!==St.CIRCLE?(this.geoFilterConfigForm.get("centerLatitude").setValidators([]),this.geoFilterConfigForm.get("centerLongitude").setValidators([]),this.geoFilterConfigForm.get("range").setValidators([]),this.geoFilterConfigForm.get("rangeUnit").setValidators([]),this.defaultPaddingEnable=!0):(this.geoFilterConfigForm.get("centerLatitude").setValidators([_.required,_.min(-90),_.max(90)]),this.geoFilterConfigForm.get("centerLongitude").setValidators([_.required,_.min(-180),_.max(180)]),this.geoFilterConfigForm.get("range").setValidators([_.required,_.min(0)]),this.geoFilterConfigForm.get("rangeUnit").setValidators([_.required]),this.defaultPaddingEnable=!1),t||n!==St.POLYGON?this.geoFilterConfigForm.get("polygonsDefinition").setValidators([]):this.geoFilterConfigForm.get("polygonsDefinition").setValidators([_.required]),this.geoFilterConfigForm.get("perimeterKeyName").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("centerLatitude").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("centerLongitude").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("range").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("rangeUnit").updateValueAndValidity({emitEvent:e}),this.geoFilterConfigForm.get("polygonsDefinition").updateValueAndValidity({emitEvent:e})}}e("GpsGeoFilterConfigComponent",Fr),Fr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Fr,deps:[{token:R.Store},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Fr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Fr,selector:"tb-filter-node-gps-geofencing-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.coordinate-field-names
\n
\n
\n \n {{ \'tb.rulenode.latitude-field-name\' | translate }}\n \n \n {{ \'tb.rulenode.latitude-field-name-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.longitude-field-name\' | translate }}\n \n \n {{ \'tb.rulenode.longitude-field-name-required\' | translate }}\n \n \n
\n
tb.rulenode.coordinate-field-hint
\n
\n
\n
\n
tb.rulenode.geofence-configuration
\n
\n \n {{ \'tb.rulenode.perimeter-type\' | translate }}\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n
\n \n {{ \'tb.rulenode.fetch-perimeter-info-from-metadata\' | translate }}\n \n
\n \n {{ \'tb.rulenode.perimeter-key-name\' | translate }}\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n {{ \'tb.rulenode.perimeter-key-name-hint\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.circle-center-latitude\' | translate }}\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.circle-center-longitude\' | translate }}\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n {{ \'tb.rulenode.range\' | translate }}\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.range-units\' | translate }}\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n {{ \'tb.rulenode.range-units-required\' | translate }}\n \n \n
\n
\n \n {{ \'tb.rulenode.polygon-definition\' | translate }}\n \n {{ \'tb.rulenode.polygon-definition-hint\' | translate }}\n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n
\n',styles:[":host .slide-toggle{margin-bottom:18px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"component",type:ne.MatSlideToggle,selector:"mat-slide-toggle",inputs:["disabled","disableRipple","color","tabIndex"],exportAs:["matSlideToggle"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:Z.DefaultLayoutGapDirective,selector:" [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg]",inputs:["fxLayoutGap","fxLayoutGap.xs","fxLayoutGap.sm","fxLayoutGap.md","fxLayoutGap.lg","fxLayoutGap.xl","fxLayoutGap.lt-sm","fxLayoutGap.lt-md","fxLayoutGap.lt-lg","fxLayoutGap.lt-xl","fxLayoutGap.gt-xs","fxLayoutGap.gt-sm","fxLayoutGap.gt-md","fxLayoutGap.gt-lg"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.MinValidator,selector:"input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]",inputs:["min"]},{kind:"directive",type:O.MaxValidator,selector:"input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]",inputs:["max"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:re.HintTooltipIconComponent,selector:"[tb-hint-tooltip-icon]",inputs:["tb-hint-tooltip-icon","tooltipPosition","hintIcon"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Fr,decorators:[{type:n,args:[{selector:"tb-filter-node-gps-geofencing-config",template:'
\n
\n
tb.rulenode.coordinate-field-names
\n
\n
\n \n {{ \'tb.rulenode.latitude-field-name\' | translate }}\n \n \n {{ \'tb.rulenode.latitude-field-name-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.longitude-field-name\' | translate }}\n \n \n {{ \'tb.rulenode.longitude-field-name-required\' | translate }}\n \n \n
\n
tb.rulenode.coordinate-field-hint
\n
\n
\n
\n
tb.rulenode.geofence-configuration
\n
\n \n {{ \'tb.rulenode.perimeter-type\' | translate }}\n \n \n {{ perimeterTypeTranslationMap.get(type) | translate }}\n \n \n \n
\n \n {{ \'tb.rulenode.fetch-perimeter-info-from-metadata\' | translate }}\n \n
\n \n {{ \'tb.rulenode.perimeter-key-name\' | translate }}\n \n \n {{ \'tb.rulenode.perimeter-key-name-required\' | translate }}\n \n {{ \'tb.rulenode.perimeter-key-name-hint\' | translate }}\n \n
\n
\n \n {{ \'tb.rulenode.circle-center-latitude\' | translate }}\n \n \n {{ \'tb.rulenode.circle-center-latitude-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.circle-center-longitude\' | translate }}\n \n \n {{ \'tb.rulenode.circle-center-longitude-required\' | translate }}\n \n \n
\n
\n \n {{ \'tb.rulenode.range\' | translate }}\n \n \n {{ \'tb.rulenode.range-required\' | translate }}\n \n \n \n {{ \'tb.rulenode.range-units\' | translate }}\n \n \n {{ rangeUnitTranslationMap.get(type) | translate }}\n \n \n \n {{ \'tb.rulenode.range-units-required\' | translate }}\n \n \n
\n
\n \n {{ \'tb.rulenode.polygon-definition\' | translate }}\n \n {{ \'tb.rulenode.polygon-definition-hint\' | translate }}\n \n {{ \'tb.rulenode.polygon-definition-required\' | translate }}\n \n \n
\n
\n
\n',styles:[":host .slide-toggle{margin-bottom:18px}\n",':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder}]}});class kr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.messageTypeConfigForm}prepareInputConfig(e){return{messageTypes:se(e?.messageTypes)?e.messageTypes:null}}onConfigurationSet(e){this.messageTypeConfigForm=this.fb.group({messageTypes:[e.messageTypes,[_.required]]})}}e("MessageTypeConfigComponent",kr),kr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:kr,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),kr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:kr,selector:"tb-filter-node-message-type-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',dependencies:[{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Dn,selector:"tb-message-types-config",inputs:["required","label","placeholder","disabled"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:kr,decorators:[{type:n,args:[{selector:"tb-filter-node-message-type-config",template:'
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class Lr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.allowedEntityTypes=[k.DEVICE,k.ASSET,k.ENTITY_VIEW,k.TENANT,k.CUSTOMER,k.USER,k.DASHBOARD,k.RULE_CHAIN,k.RULE_NODE]}configForm(){return this.originatorTypeConfigForm}prepareInputConfig(e){return{originatorTypes:se(e?.originatorTypes)?e.originatorTypes:null}}onConfigurationSet(e){this.originatorTypeConfigForm=this.fb.group({originatorTypes:[e.originatorTypes,[_.required]]})}}e("OriginatorTypeConfigComponent",Lr),Lr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Lr,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Lr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Lr,selector:"tb-filter-node-originator-type-config",usesInheritance:!0,ngImport:t,template:'
\n \n help\n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"component",type:pt.EntityTypeListComponent,selector:"tb-entity-type-list",inputs:["required","additionalClasses","appearance","label","floatLabel","disabled","subscriptSizing","allowedEntityTypes","emptyInputPlaceholder","filledInputPlaceholder","ignoreAuthorityFilter"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:W.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:Z.DefaultFlexDirective,selector:" [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg]",inputs:["fxFlex","fxFlex.xs","fxFlex.sm","fxFlex.md","fxFlex.lg","fxFlex.xl","fxFlex.lt-sm","fxFlex.lt-md","fxFlex.lt-lg","fxFlex.lt-xl","fxFlex.gt-xs","fxFlex.gt-sm","fxFlex.gt-md","fxFlex.gt-lg"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Lr,decorators:[{type:n,args:[{selector:"tb-filter-node-originator-type-config",template:'
\n \n help\n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class Tr extends f{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=ae(this.store).tbelEnabled,this.scriptLanguage=b,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-filter-function"}configForm(){return this.scriptConfigForm}onConfigurationSet(e){this.scriptConfigForm=this.fb.group({scriptLang:[e.scriptLang,[_.required]],jsScript:[e.jsScript,[]],tbelScript:[e.tbelScript,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.scriptConfigForm.get("scriptLang").value;t!==b.TBEL||this.tbelEnabled||(t=b.JS,this.scriptConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.scriptConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.scriptConfigForm.get("jsScript").setValidators(t===b.JS?[_.required]:[]),this.scriptConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.scriptConfigForm.get("tbelScript").setValidators(t===b.TBEL?[_.required]:[]),this.scriptConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=b.JS)),{scriptLang:se(e?.scriptLang)?e.scriptLang:b.JS,jsScript:se(e?.jsScript)?e.jsScript:null,tbelScript:se(e?.tbelScript)?e.tbelScript:null}}testScript(e){const t=this.scriptConfigForm.get("scriptLang").value,n=t===b.JS?"jsScript":"tbelScript",r=t===b.JS?"rulenode/filter_node_script_fn":"rulenode/tbel/filter_node_script_fn",o=this.scriptConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"filter",this.translate.instant("tb.rulenode.filter"),"Filter",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.scriptConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.scriptConfigForm.get("scriptLang").value===b.JS&&this.jsFuncComponent.validateOnSubmit()}}e("ScriptConfigComponent",Tr),Tr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Tr,deps:[{token:R.Store},{token:O.UntypedFormBuilder},{token:ie.NodeScriptTestService},{token:X.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Tr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Tr,selector:"tb-filter-node-script-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n \n
\n',dependencies:[{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:de.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:ce.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ce.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:ye.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Tr,decorators:[{type:n,args:[{selector:"tb-filter-node-script-config",template:'
\n \n \n \n \n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder},{type:ie.NodeScriptTestService},{type:X.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class Ir extends f{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=ae(this.store).tbelEnabled,this.scriptLanguage=b,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-switch-function"}configForm(){return this.switchConfigForm}onConfigurationSet(e){this.switchConfigForm=this.fb.group({scriptLang:[e.scriptLang,[_.required]],jsScript:[e.jsScript,[]],tbelScript:[e.tbelScript,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.switchConfigForm.get("scriptLang").value;t!==b.TBEL||this.tbelEnabled||(t=b.JS,this.switchConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.switchConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.switchConfigForm.get("jsScript").setValidators(t===b.JS?[_.required]:[]),this.switchConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.switchConfigForm.get("tbelScript").setValidators(t===b.TBEL?[_.required]:[]),this.switchConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=b.JS)),{scriptLang:se(e?.scriptLang)?e.scriptLang:b.JS,jsScript:se(e?.jsScript)?e.jsScript:null,tbelScript:se(e?.tbelScript)?e.tbelScript:null}}testScript(e){const t=this.switchConfigForm.get("scriptLang").value,n=t===b.JS?"jsScript":"tbelScript",r=t===b.JS?"rulenode/switch_node_script_fn":"rulenode/tbel/switch_node_script_fn",o=this.switchConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"switch",this.translate.instant("tb.rulenode.switch"),"Switch",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.switchConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.switchConfigForm.get("scriptLang").value===b.JS&&this.jsFuncComponent.validateOnSubmit()}}e("SwitchConfigComponent",Ir),Ir.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ir,deps:[{token:R.Store},{token:O.UntypedFormBuilder},{token:ie.NodeScriptTestService},{token:X.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Ir.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Ir,selector:"tb-filter-node-switch-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n \n
\n',dependencies:[{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:de.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:ce.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ce.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:ye.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ir,decorators:[{type:n,args:[{selector:"tb-filter-node-switch-config",template:'
\n \n \n \n \n \n \n \n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder},{type:ie.NodeScriptTestService},{type:X.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}});class Nr{}e("RuleNodeCoreConfigFilterModule",Nr),Nr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Nr,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Nr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:Nr,declarations:[vr,Cr,Fr,kr,Lr,Tr,Ir,hr],imports:[Q,E,$n],exports:[vr,Cr,Fr,kr,Lr,Tr,Ir,hr]}),Nr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Nr,imports:[Q,E,$n]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Nr,decorators:[{type:c,args:[{declarations:[vr,Cr,Fr,kr,Lr,Tr,Ir,hr],imports:[Q,E,$n],exports:[vr,Cr,Fr,kr,Lr,Tr,Ir,hr]}]}]});class Sr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.originatorSource=kt,this.originatorSources=Object.keys(kt),this.originatorSourceTranslationMap=Lt,this.originatorSourceDescTranslationMap=Tt,this.allowedEntityTypes=[k.DEVICE,k.ASSET,k.ENTITY_VIEW,k.USER,k.EDGE]}configForm(){return this.changeOriginatorConfigForm}onConfigurationSet(e){this.changeOriginatorConfigForm=this.fb.group({originatorSource:[e?e.originatorSource:null,[_.required]],entityType:[e?e.entityType:null,[]],entityNamePattern:[e?e.entityNamePattern:null,[]],relationsQuery:[e?e.relationsQuery:null,[]]})}validatorTriggers(){return["originatorSource"]}updateValidators(e){const t=this.changeOriginatorConfigForm.get("originatorSource").value;t===kt.RELATED?this.changeOriginatorConfigForm.get("relationsQuery").setValidators([_.required]):this.changeOriginatorConfigForm.get("relationsQuery").setValidators([]),t===kt.ENTITY?(this.changeOriginatorConfigForm.get("entityType").setValidators([_.required]),this.changeOriginatorConfigForm.get("entityNamePattern").setValidators([_.required,_.pattern(/.*\S.*/)])):(this.changeOriginatorConfigForm.get("entityType").patchValue(null,{emitEvent:e}),this.changeOriginatorConfigForm.get("entityNamePattern").patchValue(null,{emitEvent:e}),this.changeOriginatorConfigForm.get("entityType").setValidators([]),this.changeOriginatorConfigForm.get("entityNamePattern").setValidators([])),this.changeOriginatorConfigForm.get("relationsQuery").updateValueAndValidity({emitEvent:e}),this.changeOriginatorConfigForm.get("entityType").updateValueAndValidity({emitEvent:e}),this.changeOriginatorConfigForm.get("entityNamePattern").updateValueAndValidity({emitEvent:e})}}e("ChangeOriginatorConfigComponent",Sr),Sr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Sr,deps:[{token:R.Store},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Sr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Sr,selector:"tb-transformation-node-change-originator-config",usesInheritance:!0,ngImport:t,template:'
\n \n tb.rulenode.new-originator\n \n \n \n {{ originatorSourceTranslationMap.get(changeOriginatorConfigForm.get(\'originatorSource\').value) | translate }}\n \n \n \n \n {{ originatorSourceTranslationMap.get(source) | translate }}\n \n
\n \n {{ originatorSourceDescTranslationMap.get(source) | translate }}\n \n
\n
\n
\n
\n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n \n
\n
\n \n \n
\n',dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:Fe.EntityTypeSelectComponent,selector:"tb-entity-type-select",inputs:["allowedEntityTypes","useAliasEntityTypes","filterAllowedEntityTypes","showLabel","required","disabled"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"directive",type:ee.MatSelectTrigger,selector:"mat-select-trigger"},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:qe.MatListItemTitle,selector:"[matListItemTitle]"},{kind:"directive",type:qe.MatListItemMeta,selector:"[matListItemMeta]"},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:Gn,selector:"tb-relations-query-config",inputs:["disabled","required"]},{kind:"component",type:An,selector:"tb-example-hint",inputs:["hintText","popupHelpLink","textAlign"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Sr,decorators:[{type:n,args:[{selector:"tb-transformation-node-change-originator-config",template:'
\n \n tb.rulenode.new-originator\n \n \n \n {{ originatorSourceTranslationMap.get(changeOriginatorConfigForm.get(\'originatorSource\').value) | translate }}\n \n \n \n \n {{ originatorSourceTranslationMap.get(source) | translate }}\n \n
\n \n {{ originatorSourceDescTranslationMap.get(source) | translate }}\n \n
\n
\n
\n
\n \n \n
\n \n \n \n tb.rulenode.entity-name-pattern\n \n \n {{ \'tb.rulenode.entity-name-pattern-required\' | translate }}\n \n \n
\n
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder}]}});class qr extends f{constructor(e,t,n,o){super(e),this.store=e,this.fb=t,this.nodeScriptTestService=n,this.translate=o,this.tbelEnabled=ae(this.store).tbelEnabled,this.scriptLanguage=b,this.changeScript=new r,this.hasScript=!0,this.testScriptLabel="tb.rulenode.test-transformer-function"}configForm(){return this.scriptConfigForm}onConfigurationSet(e){this.scriptConfigForm=this.fb.group({scriptLang:[e?e.scriptLang:b.JS,[_.required]],jsScript:[e?e.jsScript:null,[_.required]],tbelScript:[e?e.tbelScript:null,[]]})}validatorTriggers(){return["scriptLang"]}updateValidators(e){let t=this.scriptConfigForm.get("scriptLang").value;t!==b.TBEL||this.tbelEnabled||(t=b.JS,this.scriptConfigForm.get("scriptLang").patchValue(t,{emitEvent:!1}),setTimeout((()=>{this.scriptConfigForm.updateValueAndValidity({emitEvent:!0})}))),this.scriptConfigForm.get("jsScript").setValidators(t===b.JS?[_.required]:[]),this.scriptConfigForm.get("jsScript").updateValueAndValidity({emitEvent:e}),this.scriptConfigForm.get("tbelScript").setValidators(t===b.TBEL?[_.required]:[]),this.scriptConfigForm.get("tbelScript").updateValueAndValidity({emitEvent:e})}prepareInputConfig(e){return e&&(e.scriptLang||(e.scriptLang=b.JS)),e}testScript(e){const t=this.scriptConfigForm.get("scriptLang").value,n=t===b.JS?"jsScript":"tbelScript",r=t===b.JS?"rulenode/transformation_node_script_fn":"rulenode/tbel/transformation_node_script_fn",o=this.scriptConfigForm.get(n).value;this.nodeScriptTestService.testNodeScript(o,"update",this.translate.instant("tb.rulenode.transformer"),"Transform",["msg","metadata","msgType"],this.ruleNodeId,r,t,e).subscribe((e=>{e&&(this.scriptConfigForm.get(n).setValue(e),this.changeScript.emit())}))}onValidate(){this.scriptConfigForm.get("scriptLang").value===b.JS&&this.jsFuncComponent.validateOnSubmit()}}e("TransformScriptConfigComponent",qr),qr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:qr,deps:[{token:R.Store},{token:O.FormBuilder},{token:ie.NodeScriptTestService},{token:X.TranslateService}],target:t.ɵɵFactoryTarget.Component}),qr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:qr,selector:"tb-transformation-node-script-config",viewQueries:[{propertyName:"jsFuncComponent",first:!0,predicate:["jsFuncComponent"],descendants:!0},{propertyName:"tbelFuncComponent",first:!0,predicate:["tbelFuncComponent"],descendants:!0}],usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n \n \n \n
\n \n
\n
\n',dependencies:[{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:de.JsFuncComponent,selector:"tb-js-func",inputs:["functionTitle","functionName","functionArgs","validationArgs","resultType","disabled","fillHeight","minHeight","editorCompleter","globalVariables","disableUndefinedCheck","helpId","scriptLanguage","noValidate","required"]},{kind:"component",type:ce.MatButton,selector:" button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:ce.MatIconButton,selector:"button[mat-icon-button]",inputs:["disabled","disableRipple","color"],exportAs:["matButton"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:ye.TbScriptLangComponent,selector:"tb-script-lang",inputs:["disabled"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:qr,decorators:[{type:n,args:[{selector:"tb-transformation-node-script-config",template:'
\n \n \n \n \n \n \n \n
\n \n
\n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder},{type:ie.NodeScriptTestService},{type:X.TranslateService}]},propDecorators:{jsFuncComponent:[{type:o,args:["jsFuncComponent",{static:!1}]}],tbelFuncComponent:[{type:o,args:["tbelFuncComponent",{static:!1}]}]}}); +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +const Ar=ut({passive:!0});class Mr{constructor(e,t){this._platform=e,this._ngZone=t,this._monitoredElements=new Map}monitor(e){if(!this._platform.isBrowser)return He;const t=Te(e),n=this._monitoredElements.get(t);if(n)return n.subject;const r=new Ke,o="cdk-text-field-autofilled",a=e=>{"cdk-text-field-autofill-start"!==e.animationName||t.classList.contains(o)?"cdk-text-field-autofill-end"===e.animationName&&t.classList.contains(o)&&(t.classList.remove(o),this._ngZone.run((()=>r.next({target:e.target,isAutofilled:!1})))):(t.classList.add(o),this._ngZone.run((()=>r.next({target:e.target,isAutofilled:!0}))))};return this._ngZone.runOutsideAngular((()=>{t.addEventListener("animationstart",a,Ar),t.classList.add("cdk-text-field-autofill-monitored")})),this._monitoredElements.set(t,{subject:r,unlisten:()=>{t.removeEventListener("animationstart",a,Ar)}}),r}stopMonitoring(e){const t=Te(e),n=this._monitoredElements.get(t);n&&(n.unlisten(),n.subject.complete(),t.classList.remove("cdk-text-field-autofill-monitored"),t.classList.remove("cdk-text-field-autofilled"),this._monitoredElements.delete(t))}ngOnDestroy(){this._monitoredElements.forEach(((e,t)=>this.stopMonitoring(t)))}}Mr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Mr,deps:[{token:dt.Platform},{token:t.NgZone}],target:t.ɵɵFactoryTarget.Injectable}),Mr.ɵprov=t.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Mr,providedIn:"root"}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Mr,decorators:[{type:s,args:[{providedIn:"root"}]}],ctorParameters:function(){return[{type:dt.Platform},{type:t.NgZone}]}});class Er{constructor(e,t){this._elementRef=e,this._autofillMonitor=t,this.cdkAutofill=new r}ngOnInit(){this._autofillMonitor.monitor(this._elementRef).subscribe((e=>this.cdkAutofill.emit(e)))}ngOnDestroy(){this._autofillMonitor.stopMonitoring(this._elementRef)}}Er.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Er,deps:[{token:t.ElementRef},{token:Mr}],target:t.ɵɵFactoryTarget.Directive}),Er.ɵdir=t.ɵɵngDeclareDirective({minVersion:"14.0.0",version:"15.2.0-rc.0",type:Er,selector:"[cdkAutofill]",outputs:{cdkAutofill:"cdkAutofill"},ngImport:t}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Er,decorators:[{type:u,args:[{selector:"[cdkAutofill]"}]}],ctorParameters:function(){return[{type:t.ElementRef},{type:Mr}]},propDecorators:{cdkAutofill:[{type:d}]}}); +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +class Gr{get minRows(){return this._minRows}set minRows(e){this._minRows=Ie(e),this._setMinHeight()}get maxRows(){return this._maxRows}set maxRows(e){this._maxRows=Ie(e),this._setMaxHeight()}get enabled(){return this._enabled}set enabled(e){e=Le(e),this._enabled!==e&&((this._enabled=e)?this.resizeToFitContent(!0):this.reset())}get placeholder(){return this._textareaElement.placeholder}set placeholder(e){this._cachedPlaceholderHeight=void 0,e?this._textareaElement.setAttribute("placeholder",e):this._textareaElement.removeAttribute("placeholder"),this._cacheTextareaPlaceholderHeight()}constructor(e,t,n,r){this._elementRef=e,this._platform=t,this._ngZone=n,this._destroyed=new Ke,this._enabled=!0,this._previousMinRows=-1,this._isViewInited=!1,this._handleFocusEvent=e=>{this._hasFocus="focus"===e.type},this._document=r,this._textareaElement=this._elementRef.nativeElement}_setMinHeight(){const e=this.minRows&&this._cachedLineHeight?this.minRows*this._cachedLineHeight+"px":null;e&&(this._textareaElement.style.minHeight=e)}_setMaxHeight(){const e=this.maxRows&&this._cachedLineHeight?this.maxRows*this._cachedLineHeight+"px":null;e&&(this._textareaElement.style.maxHeight=e)}ngAfterViewInit(){this._platform.isBrowser&&(this._initialHeight=this._textareaElement.style.height,this.resizeToFitContent(),this._ngZone.runOutsideAngular((()=>{const e=this._getWindow();je(e,"resize").pipe(Pe(16),we(this._destroyed)).subscribe((()=>this.resizeToFitContent(!0))),this._textareaElement.addEventListener("focus",this._handleFocusEvent),this._textareaElement.addEventListener("blur",this._handleFocusEvent)})),this._isViewInited=!0,this.resizeToFitContent(!0))}ngOnDestroy(){this._textareaElement.removeEventListener("focus",this._handleFocusEvent),this._textareaElement.removeEventListener("blur",this._handleFocusEvent),this._destroyed.next(),this._destroyed.complete()}_cacheTextareaLineHeight(){if(this._cachedLineHeight)return;let e=this._textareaElement.cloneNode(!1);e.rows=1,e.style.position="absolute",e.style.visibility="hidden",e.style.border="none",e.style.padding="0",e.style.height="",e.style.minHeight="",e.style.maxHeight="",e.style.overflow="hidden",this._textareaElement.parentNode.appendChild(e),this._cachedLineHeight=e.clientHeight,e.remove(),this._setMinHeight(),this._setMaxHeight()}_measureScrollHeight(){const e=this._textareaElement,t=e.style.marginBottom||"",n=this._platform.FIREFOX,r=n&&this._hasFocus,o=n?"cdk-textarea-autosize-measuring-firefox":"cdk-textarea-autosize-measuring";r&&(e.style.marginBottom=`${e.clientHeight}px`),e.classList.add(o);const a=e.scrollHeight-4;return e.classList.remove(o),r&&(e.style.marginBottom=t),a}_cacheTextareaPlaceholderHeight(){if(!this._isViewInited||null!=this._cachedPlaceholderHeight)return;if(!this.placeholder)return void(this._cachedPlaceholderHeight=0);const e=this._textareaElement.value;this._textareaElement.value=this._textareaElement.placeholder,this._cachedPlaceholderHeight=this._measureScrollHeight(),this._textareaElement.value=e}ngDoCheck(){this._platform.isBrowser&&this.resizeToFitContent()}resizeToFitContent(e=!1){if(!this._enabled)return;if(this._cacheTextareaLineHeight(),this._cacheTextareaPlaceholderHeight(),!this._cachedLineHeight)return;const t=this._elementRef.nativeElement,n=t.value;if(!e&&this._minRows===this._previousMinRows&&n===this._previousValue)return;const r=this._measureScrollHeight(),o=Math.max(r,this._cachedPlaceholderHeight||0);t.style.height=`${o}px`,this._ngZone.runOutsideAngular((()=>{"undefined"!=typeof requestAnimationFrame?requestAnimationFrame((()=>this._scrollToCaretPosition(t))):setTimeout((()=>this._scrollToCaretPosition(t)))})),this._previousValue=n,this._previousMinRows=this._minRows}reset(){void 0!==this._initialHeight&&(this._textareaElement.style.height=this._initialHeight)}_noopInputHandler(){}_getDocument(){return this._document||document}_getWindow(){return this._getDocument().defaultView||window}_scrollToCaretPosition(e){const{selectionStart:t,selectionEnd:n}=e;!this._destroyed.isStopped&&this._hasFocus&&e.setSelectionRange(t,n)}}Gr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Gr,deps:[{token:t.ElementRef},{token:dt.Platform},{token:t.NgZone},{token:$,optional:!0}],target:t.ɵɵFactoryTarget.Directive}),Gr.ɵdir=t.ɵɵngDeclareDirective({minVersion:"14.0.0",version:"15.2.0-rc.0",type:Gr,selector:"textarea[cdkTextareaAutosize]",inputs:{minRows:["cdkAutosizeMinRows","minRows"],maxRows:["cdkAutosizeMaxRows","maxRows"],enabled:["cdkTextareaAutosize","enabled"],placeholder:"placeholder"},host:{attributes:{rows:"1"},listeners:{input:"_noopInputHandler()"},classAttribute:"cdk-textarea-autosize"},exportAs:["cdkTextareaAutosize"],ngImport:t}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Gr,decorators:[{type:u,args:[{selector:"textarea[cdkTextareaAutosize]",exportAs:"cdkTextareaAutosize",host:{class:"cdk-textarea-autosize",rows:"1","(input)":"_noopInputHandler()"}}]}],ctorParameters:function(){return[{type:t.ElementRef},{type:dt.Platform},{type:t.NgZone},{type:void 0,decorators:[{type:p},{type:m,args:[$]}]}]},propDecorators:{minRows:[{type:i,args:["cdkAutosizeMinRows"]}],maxRows:[{type:i,args:["cdkAutosizeMaxRows"]}],enabled:[{type:i,args:["cdkTextareaAutosize"]}],placeholder:[{type:i}]}}); +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +class Dr{}Dr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Dr,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Dr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.0-rc.0",ngImport:t,type:Dr,declarations:[Er,Gr],exports:[Er,Gr]}),Dr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Dr}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.0-rc.0",ngImport:t,type:Dr,decorators:[{type:c,args:[{declarations:[Er,Gr],exports:[Er,Gr]}]}]});class Vr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.mailBodyTypes=[{name:"tb.mail-body-type.plain-text",description:"tb.mail-body-type.plain-text-description",value:"false"},{name:"tb.mail-body-type.html",description:"tb.mail-body-type.html-text-description",value:"true"},{name:"tb.mail-body-type.use-body-type-template",description:"tb.mail-body-type.dynamic-text-description",value:"dynamic"}]}configForm(){return this.toEmailConfigForm}onConfigurationSet(e){this.toEmailConfigForm=this.fb.group({fromTemplate:[e?e.fromTemplate:null,[_.required]],toTemplate:[e?e.toTemplate:null,[_.required]],ccTemplate:[e?e.ccTemplate:null,[]],bccTemplate:[e?e.bccTemplate:null,[]],subjectTemplate:[e?e.subjectTemplate:null,[_.required]],mailBodyType:[e?e.mailBodyType:null],isHtmlTemplate:[e?e.isHtmlTemplate:null,[_.required]],bodyTemplate:[e?e.bodyTemplate:null,[_.required]]})}prepareInputConfig(e){return{fromTemplate:se(e?.fromTemplate)?e.fromTemplate:null,toTemplate:se(e?.toTemplate)?e.toTemplate:null,ccTemplate:se(e?.ccTemplate)?e.ccTemplate:null,bccTemplate:se(e?.bccTemplate)?e.bccTemplate:null,subjectTemplate:se(e?.subjectTemplate)?e.subjectTemplate:null,mailBodyType:se(e?.mailBodyType)?e.mailBodyType:null,isHtmlTemplate:se(e?.isHtmlTemplate)?e.isHtmlTemplate:null,bodyTemplate:se(e?.bodyTemplate)?e.bodyTemplate:null}}updateValidators(e){"dynamic"===this.toEmailConfigForm.get("mailBodyType").value?this.toEmailConfigForm.get("isHtmlTemplate").enable({emitEvent:!1}):this.toEmailConfigForm.get("isHtmlTemplate").disable({emitEvent:!1}),this.toEmailConfigForm.get("isHtmlTemplate").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["mailBodyType"]}getBodyTypeName(){return this.mailBodyTypes.find((e=>e.value===this.toEmailConfigForm.get("mailBodyType").value)).name}}e("ToEmailConfigComponent",Vr),Vr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Vr,deps:[{token:R.Store},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Vr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Vr,selector:"tb-transformation-node-to-email-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
tb.rulenode.email-sender
\n
\n \n tb.rulenode.from-template\n \n \n {{ \'tb.rulenode.email-from-template-hint\' | translate }}\n \n \n
\n
\n
\n
\n \n {{ \'tb.rulenode.from-template-required\' | translate }}\n \n
\n
\n
\n
\n
\n
tb.rulenode.recipients
\n \n \n
\n
\n \n tb.rulenode.to-template\n \n \n {{ \'tb.rulenode.to-template-required\' | translate }}\n \n \n \n tb.rulenode.cc-template\n \n \n \n tb.rulenode.bcc-template\n \n \n
\n
\n
\n
tb.rulenode.message-subject-and-content
\n \n \n
\n \n tb.rulenode.subject-template\n \n \n {{ \'tb.rulenode.subject-template-required\' | translate }}\n \n \n \n tb.rulenode.mail-body-type\n \n \n \n {{ getBodyTypeName() | translate }}\n \n \n \n \n {{ type.name | translate }}\n \n
\n \n {{ type.description | translate }}\n \n
\n
\n
\n \n tb.rulenode.body-type-template\n \n tb.mail-body-type.after-template-evaluation-hint\n \n \n tb.rulenode.body-template\n \n \n {{ \'tb.rulenode.body-template-required\' | translate }}\n \n \n
\n
\n
\n',styles:[":host .input-bottom-double-hint{display:inline-flex}:host .input-bottom-double-hint .see-example{flex-shrink:0;padding-right:16px}:host textarea.tb-enable-vertical-resize{resize:vertical}\n"],dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:$e.HelpPopupComponent,selector:"[tb-help-popup], [tb-help-popup-content]",inputs:["tb-help-popup","tb-help-popup-content","trigger-text","trigger-style","tb-help-popup-placement","tb-help-popup-style","hintMode"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:Gr,selector:"textarea[cdkTextareaAutosize]",inputs:["cdkAutosizeMinRows","cdkAutosizeMaxRows","cdkTextareaAutosize","placeholder"],exportAs:["cdkTextareaAutosize"]},{kind:"component",type:ee.MatSelect,selector:"mat-select",inputs:["disabled","disableRipple","tabIndex","hideSingleSelectionIndicator"],exportAs:["matSelect"]},{kind:"directive",type:ee.MatSelectTrigger,selector:"mat-select-trigger"},{kind:"component",type:te.MatOption,selector:"mat-option",exportAs:["matOption"]},{kind:"directive",type:qe.MatListItemTitle,selector:"[matListItemTitle]"},{kind:"directive",type:qe.MatListItemMeta,selector:"[matListItemMeta]"},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"component",type:An,selector:"tb-example-hint",inputs:["hintText","popupHelpLink","textAlign"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Vr,decorators:[{type:n,args:[{selector:"tb-transformation-node-to-email-config",template:'
\n
\n
tb.rulenode.email-sender
\n
\n \n tb.rulenode.from-template\n \n \n {{ \'tb.rulenode.email-from-template-hint\' | translate }}\n \n \n
\n
\n
\n
\n \n {{ \'tb.rulenode.from-template-required\' | translate }}\n \n
\n
\n
\n
\n
\n
tb.rulenode.recipients
\n \n \n
\n
\n \n tb.rulenode.to-template\n \n \n {{ \'tb.rulenode.to-template-required\' | translate }}\n \n \n \n tb.rulenode.cc-template\n \n \n \n tb.rulenode.bcc-template\n \n \n
\n
\n
\n
tb.rulenode.message-subject-and-content
\n \n \n
\n \n tb.rulenode.subject-template\n \n \n {{ \'tb.rulenode.subject-template-required\' | translate }}\n \n \n \n tb.rulenode.mail-body-type\n \n \n \n {{ getBodyTypeName() | translate }}\n \n \n \n \n {{ type.name | translate }}\n \n
\n \n {{ type.description | translate }}\n \n
\n
\n
\n \n tb.rulenode.body-type-template\n \n tb.mail-body-type.after-template-evaluation-hint\n \n \n tb.rulenode.body-template\n \n \n {{ \'tb.rulenode.body-template-required\' | translate }}\n \n \n
\n
\n
\n',styles:[":host .input-bottom-double-hint{display:inline-flex}:host .input-bottom-double-hint .see-example{flex-shrink:0;padding-right:16px}:host textarea.tb-enable-vertical-resize{resize:vertical}\n"]}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder}]}});class wr extends f{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.copyFrom=[],this.translation=on;for(const e of this.translation.keys())this.copyFrom.push({value:e,name:this.translate.instant(this.translation.get(e))})}onConfigurationSet(e){this.copyKeysConfigForm=this.fb.group({copyFrom:[e.copyFrom,[_.required]],keys:[e?e.keys:null,[_.required]]})}configForm(){return this.copyKeysConfigForm}prepareInputConfig(e){let t;return t=se(e?.fromMetadata)?e.copyFrom?rn.METADATA:rn.DATA:se(e?.copyFrom)?e.copyFrom:rn.DATA,{keys:se(e?.keys)?e.keys:null,copyFrom:t}}}e("CopyKeysConfigComponent",wr),wr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:wr,deps:[{token:R.Store},{token:O.FormBuilder},{token:X.TranslateService}],target:t.ɵɵFactoryTarget.Component}),wr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:wr,selector:"tb-transformation-node-copy-keys-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n help\n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"component",type:et.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:W.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Kn,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:wr,decorators:[{type:n,args:[{selector:"tb-transformation-node-copy-keys-config",template:'
\n \n \n \n \n help\n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder},{type:X.TranslateService}]}});class Pr extends f{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.renameIn=[],this.translation=ln;for(const e of this.translation.keys())this.renameIn.push({value:e,name:this.translate.instant(this.translation.get(e))})}configForm(){return this.renameKeysConfigForm}onConfigurationSet(e){this.renameKeysConfigForm=this.fb.group({renameIn:[e?e.renameIn:null,[_.required]],renameKeysMapping:[e?e.renameKeysMapping:null,[_.required]]})}prepareInputConfig(e){let t;return t=se(e?.fromMetadata)?e.fromMetadata?rn.METADATA:rn.DATA:se(e?.renameIn)?e?.renameIn:rn.DATA,{renameKeysMapping:se(e?.renameKeysMapping)?e.renameKeysMapping:null,renameIn:t}}}e("RenameKeysConfigComponent",Pr),Pr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Pr,deps:[{token:R.Store},{token:O.FormBuilder},{token:X.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Pr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Pr,selector:"tb-transformation-node-rename-keys-config",usesInheritance:!0,ngImport:t,template:'
\n
tb.rulenode.rename-keys-in
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}:host .fx-centered{display:flex;width:100%;justify-content:space-around}\n"],dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:tt.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:nt.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:Mn,selector:"tb-kv-map-config",inputs:["disabled","uniqueKeyValuePairValidator","labelText","requiredText","keyText","keyRequiredText","valText","valRequiredText","hintText","popupHelpLink","required"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Pr,decorators:[{type:n,args:[{selector:"tb-transformation-node-rename-keys-config",template:'
\n
tb.rulenode.rename-keys-in
\n
\n
\n \n \n {{ data.name }}\n \n \n
\n
\n \n \n
\n',styles:[":host .fetch-to-data-toggle{max-width:420px;width:100%}:host .fx-centered{display:flex;width:100%;justify-content:space-around}\n"]}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder},{type:X.TranslateService}]}});class Rr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.jsonPathConfigForm}onConfigurationSet(e){this.jsonPathConfigForm=this.fb.group({jsonPath:[e?e.jsonPath:null,[_.required]]})}}e("NodeJsonPathConfigComponent",Rr),Rr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Rr,deps:[{token:R.Store},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),Rr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Rr,selector:"tb-transformation-node-json-path-config",usesInheritance:!0,ngImport:t,template:"
\n \n {{ 'tb.rulenode.json-path-expression' | translate }}\n \n {{ 'tb.rulenode.json-path-expression-hint' | translate }}\n {{ 'tb.rulenode.json-path-expression-required' | translate }}\n \n
\n",dependencies:[{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatHint,selector:"mat-hint",inputs:["align","id"]},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Rr,decorators:[{type:n,args:[{selector:"tb-transformation-node-json-path-config",template:"
\n \n {{ 'tb.rulenode.json-path-expression' | translate }}\n \n {{ 'tb.rulenode.json-path-expression-hint' | translate }}\n {{ 'tb.rulenode.json-path-expression-required' | translate }}\n \n
\n"}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder}]}});class Or extends f{constructor(e,t,n){super(e),this.store=e,this.fb=t,this.translate=n,this.deleteFrom=[],this.translation=an;for(const e of this.translation.keys())this.deleteFrom.push({value:e,name:this.translate.instant(this.translation.get(e))})}onConfigurationSet(e){this.deleteKeysConfigForm=this.fb.group({deleteFrom:[e.deleteFrom,[_.required]],keys:[e?e.keys:null,[_.required]]})}prepareInputConfig(e){let t;return t=se(e?.fromMetadata)?e.fromMetadata?rn.METADATA:rn.DATA:se(e?.deleteFrom)?e?.deleteFrom:rn.DATA,{keys:se(e?.keys)?e.keys:null,deleteFrom:t}}configForm(){return this.deleteKeysConfigForm}}e("DeleteKeysConfigComponent",Or),Or.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Or,deps:[{token:R.Store},{token:O.FormBuilder},{token:X.TranslateService}],target:t.ɵɵFactoryTarget.Component}),Or.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Or,selector:"tb-transformation-node-delete-keys-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n \n \n help\n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n'],dependencies:[{kind:"component",type:et.StringItemsListComponent,selector:"tb-string-items-list",inputs:["required","disabled","label","placeholder","hint","requiredText","floatLabel","appearance","editable","subscriptSizing","predefinedValues"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:W.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"component",type:Kn,selector:"tb-msg-metadata-chip",inputs:["labelText","translation"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Or,decorators:[{type:n,args:[{selector:"tb-transformation-node-delete-keys-config",template:'
\n \n \n \n \n help\n \n \n
\n',styles:[':host .margin-8{margin:8px}:host .tb-error{letter-spacing:.25px;color:var(--mdc-theme-error, #f44336)}:host .tb-required:after{content:"*";font-size:16px;color:#000000de}.same-width-component-row{display:flex;flex-wrap:nowrap;gap:16px}@media screen and (max-width: 599px){.same-width-component-row{gap:8px}}.same-width-component-row>*{flex:1}\n']}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder},{type:X.TranslateService}]}});class _r extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.serviceType=x.TB_RULE_ENGINE,this.deduplicationStrategie=wt,this.deduplicationStrategies=Object.keys(this.deduplicationStrategie),this.deduplicationStrategiesTranslations=Pt}configForm(){return this.deduplicationConfigForm}onConfigurationSet(e){this.deduplicationConfigForm=this.fb.group({interval:[se(e?.interval)?e.interval:null,[_.required,_.min(1)]],strategy:[se(e?.strategy)?e.strategy:null,[_.required]],outMsgType:[se(e?.outMsgType)?e.outMsgType:null,[_.required]],queueName:[se(e?.queueName)?e.queueName:null,[_.required]],maxPendingMsgs:[se(e?.maxPendingMsgs)?e.maxPendingMsgs:null,[_.required,_.min(1),_.max(1e3)]],maxRetries:[se(e?.maxRetries)?e.maxRetries:null,[_.required,_.min(0),_.max(100)]]})}prepareInputConfig(e){return e||(e={}),e.outMsgType||(e.outMsgType="POST_TELEMETRY_REQUEST"),super.prepareInputConfig(e)}updateValidators(e){this.deduplicationConfigForm.get("strategy").value===this.deduplicationStrategie.ALL?(this.deduplicationConfigForm.get("queueName").enable({emitEvent:!1}),this.deduplicationConfigForm.get("outMsgType").enable({emitEvent:!1})):(this.deduplicationConfigForm.get("outMsgType").disable({emitEvent:!1}),this.deduplicationConfigForm.get("queueName").disable({emitEvent:!1})),this.deduplicationConfigForm.get("outMsgType").updateValueAndValidity({emitEvent:e}),this.deduplicationConfigForm.get("queueName").updateValueAndValidity({emitEvent:e})}validatorTriggers(){return["strategy"]}}e("DeduplicationConfigComponent",_r),_r.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:_r,deps:[{token:R.Store},{token:O.FormBuilder}],target:t.ɵɵFactoryTarget.Component}),_r.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:_r,selector:"tb-action-node-msg-deduplication-config",usesInheritance:!0,ngImport:t,template:'
\n \n {{\'tb.rulenode.interval\' | translate}}\n \n \n {{\'tb.rulenode.interval-required\' | translate}}\n \n \n {{\'tb.rulenode.interval-min-error\' | translate}}\n \n help\n \n
\n
\n
tb.rulenode.strategy
\n \n \n {{ deduplicationStrategiesTranslations.get(strategy) | translate }}\n \n \n \n \n \n \n \n \n
\n \n \n \n \n
\n
\n
\n \n \n tb.rulenode.advanced-settings\n \n
\n \n {{\'tb.rulenode.max-pending-msgs\' | translate}}\n \n \n {{\'tb.rulenode.max-pending-msgs-required\' | translate}}\n \n \n {{\'tb.rulenode.max-pending-msgs-max-error\' | translate}}\n \n \n {{\'tb.rulenode.max-pending-msgs-min-error\' | translate}}\n \n help\n \n \n {{\'tb.rulenode.max-retries\' | translate}}\n \n \n {{\'tb.rulenode.max-retries-required\' | translate}}\n \n \n {{\'tb.rulenode.max-retries-max-error\' | translate}}\n \n \n {{\'tb.rulenode.max-retries-min-error\' | translate}}\n \n help\n \n
\n
\n
\n
\n
\n',dependencies:[{kind:"directive",type:j.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:j.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:oe.QueueAutocompleteComponent,selector:"tb-queue-autocomplete",inputs:["labelText","requiredText","autocompleteHint","subscriptSizing","required","queueType","disabled"]},{kind:"component",type:fe.MatIcon,selector:"mat-icon",inputs:["color","inline","svgIcon","fontSet","fontIcon"],exportAs:["matIcon"]},{kind:"directive",type:Y.MatInput,selector:"input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]",inputs:["disabled","id","placeholder","name","required","type","errorStateMatcher","aria-describedby","value","readonly"],exportAs:["matInput"]},{kind:"component",type:W.MatFormField,selector:"mat-form-field",inputs:["hideRequiredMarker","color","floatLabel","appearance","subscriptSizing","hintLabel"],exportAs:["matFormField"]},{kind:"directive",type:W.MatLabel,selector:"mat-label"},{kind:"directive",type:W.MatError,selector:"mat-error, [matError]",inputs:["id"]},{kind:"directive",type:W.MatSuffix,selector:"[matSuffix], [matIconSuffix], [matTextSuffix]",inputs:["matTextSuffix"]},{kind:"directive",type:ge.MatTooltip,selector:"[matTooltip]",exportAs:["matTooltip"]},{kind:"component",type:We.MatExpansionPanel,selector:"mat-expansion-panel",inputs:["disabled","expanded","hideToggle","togglePosition"],outputs:["opened","closed","expandedChange","afterExpand","afterCollapse"],exportAs:["matExpansionPanel"]},{kind:"component",type:We.MatExpansionPanelHeader,selector:"mat-expansion-panel-header",inputs:["tabIndex","expandedHeight","collapsedHeight"]},{kind:"directive",type:We.MatExpansionPanelTitle,selector:"mat-panel-title"},{kind:"directive",type:O.DefaultValueAccessor,selector:"input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]"},{kind:"directive",type:O.NumberValueAccessor,selector:"input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]"},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]},{kind:"directive",type:X.TranslateDirective,selector:"[translate],[ngx-translate]",inputs:["translate","translateParams"]},{kind:"directive",type:tt.ToggleOption,selector:"tb-toggle-option",inputs:["value"]},{kind:"component",type:nt.ToggleSelectComponent,selector:"tb-toggle-select",inputs:["disabled","selectMediaBreakpoint","appearance","disablePagination"]},{kind:"component",type:Bn,selector:"tb-output-message-type-autocomplete",inputs:["subscriptSizing","disabled","required"]},{kind:"component",type:An,selector:"tb-example-hint",inputs:["hintText","popupHelpLink","textAlign"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:_r,decorators:[{type:n,args:[{selector:"tb-action-node-msg-deduplication-config",template:'
\n \n {{\'tb.rulenode.interval\' | translate}}\n \n \n {{\'tb.rulenode.interval-required\' | translate}}\n \n \n {{\'tb.rulenode.interval-min-error\' | translate}}\n \n help\n \n
\n
\n
tb.rulenode.strategy
\n \n \n {{ deduplicationStrategiesTranslations.get(strategy) | translate }}\n \n \n \n \n \n \n \n \n
\n \n \n \n \n
\n
\n
\n \n \n tb.rulenode.advanced-settings\n \n
\n \n {{\'tb.rulenode.max-pending-msgs\' | translate}}\n \n \n {{\'tb.rulenode.max-pending-msgs-required\' | translate}}\n \n \n {{\'tb.rulenode.max-pending-msgs-max-error\' | translate}}\n \n \n {{\'tb.rulenode.max-pending-msgs-min-error\' | translate}}\n \n help\n \n \n {{\'tb.rulenode.max-retries\' | translate}}\n \n \n {{\'tb.rulenode.max-retries-required\' | translate}}\n \n \n {{\'tb.rulenode.max-retries-max-error\' | translate}}\n \n \n {{\'tb.rulenode.max-retries-min-error\' | translate}}\n \n help\n \n
\n
\n
\n
\n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.FormBuilder}]}});class Br{}e("RulenodeCoreConfigTransformModule",Br),Br.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Br,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Br.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:Br,declarations:[Sr,qr,Vr,wr,Pr,Rr,Or,_r],imports:[Q,E,$n],exports:[Sr,qr,Vr,wr,Pr,Rr,Or,_r]}),Br.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Br,imports:[Q,E,$n]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Br,decorators:[{type:c,args:[{declarations:[Sr,qr,Vr,wr,Pr,Rr,Or,_r],imports:[Q,E,$n],exports:[Sr,qr,Vr,wr,Pr,Rr,Or,_r]}]}]});class Kr extends f{constructor(e,t){super(e),this.store=e,this.fb=t,this.entityType=k}configForm(){return this.ruleChainInputConfigForm}onConfigurationSet(e){this.ruleChainInputConfigForm=this.fb.group({ruleChainId:[e?e.ruleChainId:null,[_.required]]})}}e("RuleChainInputComponent",Kr),Kr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Kr,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),Kr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:Kr,selector:"tb-flow-node-rule-chain-input-config",usesInheritance:!0,ngImport:t,template:'
\n \n \n
\n',dependencies:[{kind:"component",type:mt.EntityAutocompleteComponent,selector:"tb-entity-autocomplete",inputs:["entityType","entitySubtype","excludeEntityIds","labelText","requiredText","useFullEntityId","appearance","required","disabled"],outputs:["entityChanged"]},{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:O.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.RequiredValidator,selector:":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]",inputs:["required"]},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"directive",type:O.FormControlName,selector:"[formControlName]",inputs:["formControlName","disabled","ngModel"],outputs:["ngModelChange"]}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Kr,decorators:[{type:n,args:[{selector:"tb-flow-node-rule-chain-input-config",template:'
\n \n \n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class zr extends f{constructor(e,t){super(e),this.store=e,this.fb=t}configForm(){return this.ruleChainOutputConfigForm}onConfigurationSet(e){this.ruleChainOutputConfigForm=this.fb.group({})}}e("RuleChainOutputComponent",zr),zr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:zr,deps:[{token:R.Store},{token:O.UntypedFormBuilder}],target:t.ɵɵFactoryTarget.Component}),zr.ɵcmp=t.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"15.2.10",type:zr,selector:"tb-flow-node-rule-chain-output-config",usesInheritance:!0,ngImport:t,template:'
\n
\n
\n',dependencies:[{kind:"directive",type:Z.DefaultLayoutDirective,selector:" [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]",inputs:["fxLayout","fxLayout.xs","fxLayout.sm","fxLayout.md","fxLayout.lg","fxLayout.xl","fxLayout.lt-sm","fxLayout.lt-md","fxLayout.lt-lg","fxLayout.lt-xl","fxLayout.gt-xs","fxLayout.gt-sm","fxLayout.gt-md","fxLayout.gt-lg"]},{kind:"directive",type:O.NgControlStatusGroup,selector:"[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]"},{kind:"directive",type:O.FormGroupDirective,selector:"[formGroup]",inputs:["formGroup"],outputs:["ngSubmit"],exportAs:["ngForm"]},{kind:"pipe",type:X.TranslatePipe,name:"translate"}]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:zr,decorators:[{type:n,args:[{selector:"tb-flow-node-rule-chain-output-config",template:'
\n
\n
\n'}]}],ctorParameters:function(){return[{type:R.Store},{type:O.UntypedFormBuilder}]}});class Ur{}e("RuleNodeCoreConfigFlowModule",Ur),Ur.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ur,deps:[],target:t.ɵɵFactoryTarget.NgModule}),Ur.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:Ur,declarations:[Kr,zr],imports:[Q,E,$n],exports:[Kr,zr]}),Ur.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ur,imports:[Q,E,$n]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Ur,decorators:[{type:c,args:[{declarations:[Kr,zr],imports:[Q,E,$n],exports:[Kr,zr]}]}]});class Hr{constructor(e){!function(e){e.setTranslation("en_US",{tb:{rulenode:{id:"Id","additional-info":"Additional Info","advanced-settings":"Advanced settings","create-entity-if-not-exists":"Create new entity if not exists","create-entity-if-not-exists-hint":"Create a new entity set above if it does not exist.","entity-name-pattern":"Name pattern","entity-name-pattern-required":"Name pattern is required","entity-name-pattern-hint":"Name pattern field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","copy-message-type":"Copy message type","entity-type-pattern":"Type pattern","entity-type-pattern-required":"Type pattern is required","message-type-value":"Message type value","message-type-value-required":"Message type value is required","message-type-value-max-length":"Message type value should be less than 256","output-message-type":"Output message type","entity-cache-expiration":"Entities cache expiration time (sec)","entity-cache-expiration-hint":"Specifies maximum time interval allowed to store found entity records. 0 value means that records will never expire.","entity-cache-expiration-required":"Entities cache expiration time is required.","entity-cache-expiration-range":"Entities cache expiration time should be greater than or equal to 0.","customer-name-pattern":"Customer name pattern","customer-name-pattern-required":"Customer name pattern is required","create-customer-if-not-exists":"Create new customer if not exists","customer-cache-expiration":"Customers cache expiration time (sec)","customer-cache-expiration-hint":"Specifies maximum time interval allowed to store found customer records. 0 value means that records will never expire.","customer-cache-expiration-required":"Customers cache expiration time is required.","customer-cache-expiration-range":"Customers cache expiration time should be greater than or equal to 0.","interval-start":"Interval start","interval-end":"Interval end","time-unit":"Time unit","fetch-mode":"Fetch mode","order-by-timestamp":"Order by timestamp",limit:"Limit","limit-hint":"Min limit value is 2, max - 1000. If you want to fetch a single entry, select fetch mode 'First' or 'Last'.","limit-required":"Limit is required.","limit-range":"Limit should be in a range from 2 to 1000.","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Allowing range from 1 to 2147483647.","start-interval-value-required":"Interval start is required.","end-interval-value-required":"Interval end is required.",filter:"Filter",switch:"Switch","math-templatization-tooltip":"This field support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","add-message-type":"Add message type","select-message-types-required":"At least one message type should be selected.","select-message-types":"Select message types","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one.","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","attributes-keys":"Attributes keys","attributes-keys-required":"Attributes keys are required","notify-device":"Force notification to the device","send-attributes-updated-notification":"Send attributes updated notification","send-attributes-updated-notification-hint":"Send notification about updated attributes as a separate message to the rule engine queue.","send-attributes-deleted-notification":"Send attributes deleted notification","send-attributes-deleted-notification-hint":"Send notification about deleted attributes as a separate message to the rule engine queue.","update-attributes-only-on-value-change":"Save attributes only if the value changes","update-attributes-only-on-value-change-hint":"Updates the attributes on every incoming message disregarding if their value has changed. Increases API usage and reduces performance.","update-attributes-only-on-value-change-hint-enabled":"Updates the attributes only if their value has changed. If the value is not changed, no update to the attribute timestamp nor attribute change notification will be sent.","fetch-credentials-to-metadata":"Fetch credentials to metadata","notify-device-on-update-hint":"If enabled, force notification to the device about shared attributes update. If disabled, the notification behavior is controlled by the 'notifyDevice' parameter from the incoming message metadata. To turn off the notification, the message metadata must contain the 'notifyDevice' parameter set to 'false'. Any other case will trigger the notification to the device.","notify-device-on-delete-hint":"If enabled, force notification to the device about shared attributes removal. If disabled, the notification behavior is controlled by the 'notifyDevice' parameter from the incoming message metadata. To turn on the notification, the message metadata must contain the 'notifyDevice' parameter set to 'true'. In any other case, the notification will not be triggered to the device.","latest-timeseries":"Latest time-series data keys","timeseries-keys":"Timeseries keys","timeseries-keys-required":"At least one timeseries key should be selected.","add-timeseries-key":"Add timeseries key","add-message-field":"Add message field","relation-search-parameters":"Relation search parameters","add-metadata-field":"Add metadata field","data-keys":"Message field names","copy-from":"Copy from","data-to-metadata":"Data to metadata","metadata-to-data":"Metadata to data","use-regular-expression-hint":"Use regular expression to copy keys by pattern.\n\nTips & tricks:\nPress 'Enter' to complete field name input.\nPress 'Backspace' to delete field name. Multiple field names supported.",interval:"Interval","interval-required":"Interval is required","interval-hint":"Deduplication interval in seconds.","interval-min-error":"Min allowed value is 1","max-pending-msgs":"Max pending messages","max-pending-msgs-hint":"Maximum number of messages that are stored in memory for each unique deduplication id.","max-pending-msgs-required":"Max pending messages is required","max-pending-msgs-max-error":"Max allowed value is 1000","max-pending-msgs-min-error":"Min allowed value is 1","max-retries":"Max retries","max-retries-required":"Max retries is required","max-retries-hint":"Maximum number of retries to push the deduplicated messages into the queue. 10 seconds delay is used between retries","max-retries-max-error":"Max allowed value is 100","max-retries-min-error":"Min allowed value is 0",strategy:"Strategy","strategy-required":"Strategy is required","strategy-all-hint":"Return all messages that arrived during deduplication period as a single JSON array message. Where each element represents object with msg and metadata inner properties.","strategy-first-hint":"Return first message that arrived during deduplication period.","strategy-last-hint":"Return last message that arrived during deduplication period.",first:"First",last:"Last",all:"All","output-msg-type-hint":"The message type of the deduplication result.","queue-name-hint":"The queue name where the deduplication result will be published.",keys:"Keys","keys-required":"Keys are required","rename-keys-in":"Rename keys in",data:"Data",message:"Message",metadata:"Metadata","current-key-name":"Current key name","key-name-required":"Key name is required","new-key-name":"New key name","new-key-name-required":"New key name is required","metadata-keys":"Metadata field names","json-path-expression":"JSON path expression","json-path-expression-required":"JSON path expression is required","json-path-expression-hint":"JSONPath specifies a path to an element or a set of elements in a JSON structure. '$' represents the root object or array.","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","max-relation-level-error":"Value should be greater than 0 or unspecified.","relation-type":"Relation type","relation-type-pattern":"Relation type pattern","relation-type-pattern-required":"Relation type pattern is required","relation-types-list":"Relation types to propagate","relation-types-list-hint":"If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","add-telemetry-key":"Add telemetry key","delete-from":"Delete from","use-regular-expression-delete-hint":"Use regular expression to delete keys by pattern.\n\nTips & tricks:\nPress 'Enter' to complete field name input.\nPress 'Backspace' to delete field name.\nMultiple field names supported.","fetch-into":"Fetch into","attr-mapping":"Attributes mapping:","source-attribute":"Source attribute key","source-attribute-required":"Source attribute key is required.","source-telemetry":"Source telemetry key","source-telemetry-required":"Source telemetry key is required.","target-key":"Target key","target-key-required":"Target key is required.","attr-mapping-required":"At least one mapping entry should be specified.","fields-mapping":"Fields mapping","relations-query-config-direction-suffix":"originator","profile-name":"Profile name","fetch-circle-parameter-info-from-metadata-hint":'Metadata field \'{{perimeterKeyName}}\' should be defined in next format: {"latitude":48.196, "longitude":24.6532, "radius":100.0, "radiusUnit":"METER"}',"fetch-poligon-parameter-info-from-metadata-hint":"Metadata field '{{perimeterKeyName}}' should be defined in next format: [[48.19736,24.65235],[48.19800,24.65060],...,[48.19849,24.65420]]","short-templatization-tooltip":"Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","fields-mapping-required":"At least one field mapping should be specified.","at-least-one-field-required":"At least one input field must have a value(s) provided.","originator-fields-sv-map-hint":"Target key fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","sv-map-hint":"Only target key fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","new-originator":"New originator","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related entity","originator-alarm-originator":"Alarm Originator","originator-entity":"Entity by name pattern","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","use-metadata-period-in-seconds-patterns":"Use period in seconds pattern","use-metadata-period-in-seconds-patterns-hint":"If selected, rule node use period in seconds interval pattern from message metadata or data assuming that intervals are in the seconds.","period-in-seconds-pattern":"Period in seconds pattern","period-in-seconds-pattern-required":"Period in seconds pattern is required","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","select-entity-types":"Select entity types","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required","alarm-severity-pattern":"Alarm severity pattern","alarm-status-filter":"Alarm status filter","alarm-status-list-empty":"Alarm status list is empty","no-alarm-status-matching":"No alarm status matching were found.",propagate:"Propagate alarm to related entities","propagate-to-owner":"Propagate alarm to entity owner (Customer or Tenant)","propagate-to-tenant":"Propagate alarm to Tenant",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From","from-template-required":"From is required","message-to-metadata":"Message to metadata","metadata-to-message":"Metadata to message","from-message":"From message","from-metadata":"From metadata","to-template":"To","to-template-required":"To Template is required","mail-address-list-template-hint":'Comma separated address list, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',"cc-template":"Cc","bcc-template":"Bcc","subject-template":"Subject","subject-template-required":"Subject Template is required","body-template":"Body","body-template-required":"Body Template is required","dynamic-mail-body-type":"Dynamic mail body type","mail-body-type":"Mail body type","body-type-template":"Body type template","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","request-method":"Request method","use-simple-client-http-factory":"Use simple client HTTP factory","ignore-request-body":"Without request body","parse-to-plain-text":"Parse to plain text","parse-to-plain-text-hint":'If selected, request body message payload will be transformed from JSON string to plain text, e.g. msg = "Hello,\\t\\"world\\"" will be parsed to Hello, "world"',"read-timeout":"Read timeout in millis","read-timeout-hint":"The value of 0 means an infinite timeout","max-parallel-requests-count":"Max number of parallel requests","max-parallel-requests-count-hint":"The value of 0 specifies no limit in parallel processing",headers:"Headers","headers-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in header/value fields',header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","key-pattern":"Key pattern","key-pattern-hint":"Optional. If a valid partition number is specified, it will be used when sending the record. If no partition is specified, the key will be used instead. If neither is specified, a partition will be assigned in a round-robin fashion.","topic-pattern-required":"Topic pattern is required",topic:"Topic","topic-required":"Topic is required","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","gcp-project-id":"GCP project ID","gcp-project-id-required":"GCP project ID is required","gcp-service-account-key":"GCP service account key file","gcp-service-account-key-required":"GCP service account key file is required","pubsub-topic-name":"Topic name","pubsub-topic-name-required":"Topic name is required","message-attributes":"Message attributes","message-attributes-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body in name/value fields',"connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","client-id-hint":'Optional. Leave empty for auto-generated Client ID. Be careful when specifying the Client ID. Majority of the MQTT brokers will not allow multiple connections with the same Client ID. To connect to such brokers, your mqtt Client ID must be unique. When platform is running in a micro-services mode, the copy of rule node is launched in each micro-service. This will automatically lead to multiple mqtt clients with the same ID and may cause failures of the rule node. To avoid such failures enable "Add Service ID as suffix to Client ID" option below.',"append-client-id-suffix":"Add Service ID as suffix to Client ID","client-id-suffix-hint":'Optional. Applied when "Client ID" specified explicitly. If selected then Service ID will be added to Client ID as a suffix. Helps to avoid failures when platform is running in a micro-services mode.',"device-id":"Device ID","device-id-required":"Device ID is required.","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","credentials-pem-hint":"At least Server CA certificate file or a pair of Client certificate and Client private key files are required","credentials-sas":"Shared Access Signature","sas-key":"SAS Key","sas-key-required":"SAS Key is required.",hostname:"Hostname","hostname-required":"Hostname is required.","azure-ca-cert":"CA certificate file","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"Server CA certificate file","private-key":"Client private key file",cert:"Client certificate file","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","use-metadata-dynamic-interval":"Use dynamic interval","metadata-dynamic-interval-hint":"Interval start and end input fields support templatization. Note that the substituted template value should be set in milliseconds. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","use-metadata-interval-patterns-hint":"If selected, rule node use start and end interval patterns from message metadata or data assuming that intervals are in the milliseconds.","use-message-alarm-data":"Use message alarm data","overwrite-alarm-details":"Overwrite alarm details","use-alarm-severity-pattern":"Use alarm severity pattern","check-all-keys":"Check that all specified fields are present","check-all-keys-hint":"If selected, checks that all specified keys are present in the message data and metadata.","check-relation-to-specific-entity":"Check relation to specific entity","check-relation-to-specific-entity-tooltip":"If enabled, checks the presence of relation with a specific entity otherwise, checks the presence of relation with any entity. In both cases, relation lookup is based on configured direction and type.","check-relation-hint":"Checks existence of relation to specific entity or to any entity based on direction and relation type.","delete-relation-to-specific-entity":"Delete relation to specific entity","delete-relation-hint":"Deletes relation from the originator of the incoming message to the specified entity or list of entities based on direction and type.","remove-current-relations":"Remove current relations","remove-current-relations-hint":"Removes current relations from the originator of the incoming message based on direction and type.","change-originator-to-related-entity":"Change originator to related entity","change-originator-to-related-entity-hint":"Used to process submitted message as a message from another entity.","start-interval":"Interval start","end-interval":"Interval end","start-interval-required":"Interval start is required.","end-interval-required":"Interval end is required.","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","tls-version":"TLS version","enable-proxy":"Enable proxy","use-system-proxy-properties":"Use system proxy properties","proxy-host":"Proxy host","proxy-host-required":"Proxy host is required.","proxy-port":"Proxy port","proxy-port-required":"Proxy port is required.","proxy-port-range":"Proxy port should be in a range from 1 to 65535.","proxy-user":"Proxy user","proxy-password":"Proxy password","proxy-scheme":"Proxy scheme","numbers-to-template":"Phone Numbers To Template","numbers-to-template-required":"Phone Numbers To Template is required","numbers-to-template-hint":'Comma separated Phone Numbers, use ${metadataKey} for value from metadata, $[messageKey] for value from message body',"sms-message-template":"SMS message Template","sms-message-template-required":"SMS message Template is required","use-system-sms-settings":"Use system SMS provider settings","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter","interval-seconds":"Interval in seconds","interval-seconds-required":"Interval is required.","min-interval-seconds-message":"Only 1 second minimum interval is allowed.","output-timeseries-key-prefix":"Output timeseries key prefix","output-timeseries-key-prefix-required":"Output timeseries key prefix required.","separator-hint":'Press "Enter" to complete field input.',"select-details":"Select details","entity-details-id":"Id","entity-details-title":"Title","entity-details-country":"Country","entity-details-state":"State","entity-details-city":"City","entity-details-zip":"Zip","entity-details-address":"Address","entity-details-address2":"Address2","entity-details-additional_info":"Additional Info","entity-details-phone":"Phone","entity-details-email":"Email","email-sender":"Email sender","fields-to-check":"Fields to check","add-detail":"Add detail","check-all-keys-tooltip":"If enabled, checks the presence of all fields listed in the message and metadata field names within the incoming message and its metadata.","fields-to-check-hint":'Press "Enter" to complete field name input. Multiple field names supported.',"entity-details-list-empty":"At least one detail should be selected.","alarm-status":"Alarm status","alarm-required":"At least one alarm status should be selected.","no-entity-details-matching":"No entity details matching were found.","custom-table-name":"Custom table name","custom-table-name-required":"Table Name is required","custom-table-hint":"Enter the table name without prefix 'cs_tb_'.","message-field":"Message field","message-field-required":"Message field is required.","table-col":"Table column","table-col-required":"Table column is required.","latitude-field-name":"Latitude field name","longitude-field-name":"Longitude field name","latitude-field-name-required":"Latitude field name is required.","longitude-field-name-required":"Longitude field name is required.","fetch-perimeter-info-from-metadata":"Fetch perimeter information from metadata","fetch-perimeter-info-from-metadata-tooltip":"If perimeter type is set to 'Polygon' the value of metadata field '{{perimeterKeyName}}' will be set as perimeter definition without additional parsing of the value. Otherwise, if perimeter type is set to 'Circle' the value of '{{perimeterKeyName}}' metadata field will be parsed to extract 'latitude', 'longitude', 'radius', 'radiusUnit' fields that uses for circle perimeter definition.","perimeter-key-name":"Perimeter key name","perimeter-key-name-hint":"Metadata field name that includes perimeter information.","perimeter-key-name-required":"Perimeter key name is required.","perimeter-circle":"Circle","perimeter-polygon":"Polygon","perimeter-type":"Perimeter type","circle-center-latitude":"Center latitude","circle-center-latitude-required":"Center latitude is required.","circle-center-longitude":"Center longitude","circle-center-longitude-required":"Center longitude is required.","range-unit-meter":"Meter","range-unit-kilometer":"Kilometer","range-unit-foot":"Foot","range-unit-mile":"Mile","range-unit-nautical-mile":"Nautical mile","range-units":"Range units","range-units-required":"Range units is required.",range:"Range","range-required":"Range is required.","polygon-definition":"Polygon definition","polygon-definition-required":"Polygon definition is required.","polygon-definition-hint":"Use the following format for manual definition of polygon: [[lat1,lon1],[lat2,lon2], ... ,[latN,lonN]].","min-inside-duration":"Minimal inside duration","min-inside-duration-value-required":"Minimal inside duration is required","min-inside-duration-time-unit":"Minimal inside duration time unit","min-outside-duration":"Minimal outside duration","min-outside-duration-value-required":"Minimal outside duration is required","min-outside-duration-time-unit":"Minimal outside duration time unit","tell-failure-if-absent":"Tell Failure","tell-failure-if-absent-hint":'If at least one selected key doesn\'t exist the outbound message will report "Failure".',"get-latest-value-with-ts":"Fetch timestamp for the latest telemetry values","get-latest-value-with-ts-hint":'If selected, the latest telemetry values will also include timestamp, e.g: "temp": "{"ts":1574329385897, "value":42}"',"use-redis-queue":"Use redis queue for message persistence","ignore-null-strings":"Ignore null strings","ignore-null-strings-hint":"If selected rule node will ignore entity fields with empty value.","trim-redis-queue":"Trim redis queue","redis-queue-max-size":"Redis queue max size","add-metadata-key-values-as-kafka-headers":"Add Message metadata key-value pairs to Kafka record headers","add-metadata-key-values-as-kafka-headers-hint":"If selected, key-value pairs from message metadata will be added to the outgoing records headers as byte arrays with predefined charset encoding.","charset-encoding":"Charset encoding","charset-encoding-required":"Charset encoding is required.","charset-us-ascii":"US-ASCII","charset-iso-8859-1":"ISO-8859-1","charset-utf-8":"UTF-8","charset-utf-16be":"UTF-16BE","charset-utf-16le":"UTF-16LE","charset-utf-16":"UTF-16","select-queue-hint":"The queue name can be selected from a drop-down list or add a custom name.","persist-alarm-rules":"Persist state of alarm rules","fetch-alarm-rules":"Fetch state of alarm rules","input-value-key":"Input value key","input-value-key-required":"Input value key is required.","output-value-key":"Output value key","output-value-key-required":"Output value key is required.","number-of-digits-after-floating-point":"Number of digits after floating point","number-of-digits-after-floating-point-range":"Number of digits after floating point should be in a range from 0 to 15.","failure-if-delta-negative":"Tell Failure if delta is negative","failure-if-delta-negative-tooltip":"Rule node forces failure of message processing if delta value is negative.","use-caching":"Use caching","use-caching-tooltip":'Rule node will cache the value of "{{inputValueKey}}" that arrives from the incoming message to improve performance. Note that the cache will not be updated if you modify the "{{inputValueKey}}" value elsewhere.',"add-time-difference-between-readings":'Add the time difference between "{{inputValueKey}}" readings',"add-time-difference-between-readings-tooltip":'If enabled, the rule node will add the "{{periodValueKey}}" to the outbound message.',"period-value-key":"Period value key","period-value-key-required":"Period value key is required.","general-pattern-hint":"Use ${metadataKey} for value from metadata, $[messageKey] for value from message body.","alarm-severity-pattern-hint":'Use ${metadataKey} for value from metadata, $[messageKey] for value from message body. Alarm severity should be system (CRITICAL, MAJOR etc.)',"output-node-name-hint":"The rule node name corresponds to the relation type of the output message, and it is used to forward messages to other rule nodes in the caller rule chain.","skip-latest-persistence":"Skip latest persistence","use-server-ts":"Use server ts","use-server-ts-hint":"Enable this setting to use the timestamp of the message processing instead of the timestamp from the message. Useful for all sorts of sequential processing if you merge messages from multiple sources (devices, assets, etc).","kv-map-pattern-hint":"All input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","shared-scope":"Shared scope","server-scope":"Server scope","client-scope":"Client scope","attribute-type":"Attribute","constant-type":"Constant","time-series-type":"Time-series","message-body-type":"Message","message-metadata-type":"Metadata","argument-tile":"Arguments","no-arguments-prompt":"No arguments configured","result-title":"Result","functions-field-input":"Functions","no-option-found":"No option found","argument-source-field-input":"Source","argument-source-field-input-required":"Argument source is required.","argument-key-field-input":"Key","argument-key-field-input-required":"Argument key is required.","constant-value-field-input":"Constant value","constant-value-field-input-required":"Constant value is required.","attribute-scope-field-input":"Attribute scope","attribute-scope-field-input-required":"Attribute scope os required.","default-value-field-input":"Default value","type-field-input":"Type","type-field-input-required":"Type is required.","key-field-input":"Key","add-entity-type":"Add entity type","add-device-profile":"Add device profile","key-field-input-required":"Key is required.","number-floating-point-field-input":"Number of digits after floating point","number-floating-point-field-input-hint":"Use 0 to convert result to integer","add-to-message-field-input":"Add to message","add-to-metadata-field-input":"Add to metadata","custom-expression-field-input":"Mathematical Expression","custom-expression-field-input-required":"Mathematical expression is required","custom-expression-field-input-hint":"Specify a mathematical expression to evaluate. Default expression demonstrates how to transform Fahrenheit to Celsius","retained-message":"Retained","attributes-mapping":"Attributes mapping","latest-telemetry-mapping":"Latest telemetry mapping","add-mapped-attribute-to":"Add mapped attributes to","add-mapped-latest-telemetry-to":"Add mapped latest telemetry to","add-mapped-fields-to":"Add mapped fields to","add-selected-details-to":"Add selected details to","clear-selected-types":"Clear selected types","clear-selected-details":"Clear selected details","clear-selected-fields":"Clear selected fields","clear-selected-keys":"Clear selected keys","geofence-configuration":"Geofence configuration","coordinate-field-names":"Coordinate field names","coordinate-field-hint":"Rule node tries to retrieve the specified fields from the message. If they are not present, it will look them up in the metadata.","fetch-credentials-to":"Fetch credentials to","add-originator-attributes-to":"Add originator attributes to","originator-attributes":"Originator attributes","fetch-latest-telemetry-with-timestamp":"Fetch latest telemetry with timestamp","fetch-latest-telemetry-with-timestamp-tooltip":'If selected, latest telemetry values will be added to the outbound metadata with timestamp, e.g: "{{latestTsKeyName}}": "{"ts":1574329385897, "value":42}"',"tell-failure":"Tell failure if any of the attributes are missing","tell-failure-tooltip":'If at least one selected key doesn\'t exist the outbound message will report "Failure".',"created-time":"Created time","chip-help":"Press 'Enter' to complete {{inputName}} input. \nPress 'Backspace' to delete {{inputName}}. \nMultiple values supported.",detail:"detail","field-name":"field name","device-profile":"device profile","entity-type":"entity type","message-type":"message type","timeseries-key":"timeseries key",type:"Type","first-name":"First name","last-name":"Last name",label:"Label","originator-fields-mapping":"Originator fields mapping","add-mapped-originator-fields-to":"Add mapped originator fields to",fields:"Fields","skip-empty-fields":"Skip empty fields","skip-empty-fields-tooltip":"Fields with empty values will not be added to the output message/output metadata.","fetch-interval":"Fetch interval","fetch-strategy":"Fetch strategy","fetch-timeseries-from-to":"Fetch timeseries from {{startInterval}} {{startIntervalTimeUnit}} ago to {{endInterval}} {{endIntervalTimeUnit}} ago.","fetch-timeseries-from-to-invalid":'Fetch timeseries invalid ("Interval start" should be less than "Interval end").',"use-metadata-dynamic-interval-tooltip":"If selected, the rule node will use dynamic interval start and end based on the message and metadata patterns.","all-mode-hint":'If selected fetch mode "All" rule node will retrieve telemetry from the fetch interval with configurable query parameters.',"first-mode-hint":'If selected fetch mode "First" rule node will retrieve the closest telemetry to the fetch interval\'s start.',"last-mode-hint":'If selected fetch mode "Last" rule node will retrieve the closest telemetry to the fetch interval\'s end.',ascending:"Ascending",descending:"Descending",min:"Min",max:"Max",average:"Average",sum:"Sum",count:"Count",none:"None","last-level-relation-tooltip":"If selected, the rule node will search related entities only on the level set in the max relation level.","last-level-device-relation-tooltip":"If selected, the rule node will search related devices only on the level set in the max relation level.","data-to-fetch":"Data to fetch","mapping-of-customers":"Mapping of customer's","map-fields-required":"All mapping fields are required.",attributes:"Attributes","related-device-attributes":"Related device attributes","add-selected-attributes-to":"Add selected attributes to","device-profiles":"Device profiles","mapping-of-tenant":"Mapping of tenant's","add-attribute-key":"Add attribute key","message-template":"Message template","message-template-required":"Message template is required","use-system-slack-settings":"Use system slack settings","slack-api-token":"Slack API token","slack-api-token-required":"Slack API token is required","keys-mapping":"keys mapping","add-key":"Add key",recipients:"Recipients","message-subject-and-content":"Message subject and content","template-rules-hint":"Both input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","originator-customer-desc":"Use customer of incoming message originator as new originator.","originator-tenant-desc":"Use current tenant as new originator.","originator-related-entity-desc":"Use related entity as new originator. Lookup based on configured relation type and direction.","originator-alarm-originator-desc":"Use alarm originator as new originator. Only if incoming message originator is alarm entity.","originator-entity-by-name-pattern-desc":"Use entity fetched from DB as new originator. Lookup based on entity type and specified name pattern.","email-from-template-hint":"Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata.","recipients-block-main-hint":"Comma-separated address list. All input fields support templatization. Use $[messageKey] to extract value from the message and ${metadataKey} to extract value from the metadata."},"key-val":{key:"Key",value:"Value","see-examples":"See examples.","remove-entry":"Remove entry","remove-mapping-entry":"Remove mapping entry","add-mapping-entry":"Add mapping","add-entry":"Add entry","copy-key-values-from":"Copy key-values from","delete-key-values":"Delete key-values","delete-key-values-from":"Delete key-values from","at-least-one-key-error":"At least one key should be selected.","unique-key-value-pair-error":"'{{keyText}}' must be different from the '{{valText}}'!"},"mail-body-type":{"plain-text":"Plain text",html:"HTML",dynamic:"Dynamic","use-body-type-template":"Use body type template","plain-text-description":"Simple, unformatted text with no special styling or formating.","html-text-description":"Allows you to use HTML tags for formatting, links and images in your mai body.","dynamic-text-description":"Allows to use Plain Text or HTML body type dynamically based on templatization feature.","after-template-evaluation-hint":"After template evaluation value should be true for HTML, and false for Plain text."}}},!0)}(e)}}e("RuleNodeCoreConfigModule",Hr),Hr.ɵfac=t.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Hr,deps:[{token:X.TranslateService}],target:t.ɵɵFactoryTarget.NgModule}),Hr.ɵmod=t.ɵɵngDeclareNgModule({minVersion:"14.0.0",version:"15.2.10",ngImport:t,type:Hr,declarations:[ct],imports:[Q,E],exports:[Qn,Nr,ar,br,Br,Ur,ct]}),Hr.ɵinj=t.ɵɵngDeclareInjector({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Hr,imports:[Q,E,Qn,Nr,ar,br,Br,Ur]}),t.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"15.2.10",ngImport:t,type:Hr,decorators:[{type:c,args:[{declarations:[ct],imports:[Q,E],exports:[Qn,Nr,ar,br,Br,Ur,ct]}]}],ctorParameters:function(){return[{type:X.TranslateService}]}})}}}));//# sourceMappingURL=rulenode-core-config.js.map diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/TestDbCallbackExecutor.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/TestDbCallbackExecutor.java new file mode 100644 index 0000000000..ed9053e2e2 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/TestDbCallbackExecutor.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.common.util.ListeningExecutor; + +import java.util.concurrent.Callable; + +public class TestDbCallbackExecutor implements ListeningExecutor { + + @Override + public ListenableFuture executeAsync(Callable task) { + try { + return Futures.immediateFuture(task.call()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public void execute(Runnable command) { + command.run(); + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAlarmNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAlarmNodeTest.java index 132383c064..33c6071769 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAlarmNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAlarmNodeTest.java @@ -18,7 +18,6 @@ package org.thingsboard.rule.engine.action; import com.datastax.oss.driver.api.core.uuid.Uuids; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; import org.apache.commons.lang3.NotImplementedException; import org.junit.Before; import org.junit.Test; @@ -29,15 +28,18 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ListeningExecutor; +import org.thingsboard.rule.engine.TestDbCallbackExecutor; import org.thingsboard.rule.engine.api.RuleEngineAlarmService; import org.thingsboard.rule.engine.api.ScriptEngine; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; import org.thingsboard.server.common.data.alarm.AlarmInfo; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.DeviceId; @@ -45,6 +47,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.script.ScriptLanguage; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; @@ -52,7 +55,6 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; import javax.script.ScriptException; import java.io.IOException; -import java.util.concurrent.Callable; import java.util.function.Consumer; import static org.junit.Assert.assertEquals; @@ -68,11 +70,6 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import static org.thingsboard.server.common.data.DataConstants.IS_CLEARED_ALARM; -import static org.thingsboard.server.common.data.DataConstants.IS_EXISTING_ALARM; -import static org.thingsboard.server.common.data.DataConstants.IS_NEW_ALARM; -import static org.thingsboard.server.common.data.alarm.AlarmSeverity.CRITICAL; -import static org.thingsboard.server.common.data.alarm.AlarmSeverity.WARNING; @RunWith(MockitoJUnitRunner.class) public class TbAlarmNodeTest { @@ -105,28 +102,14 @@ public class TbAlarmNodeTest { @Before public void before() { - dbExecutor = new ListeningExecutor() { - @Override - public ListenableFuture executeAsync(Callable task) { - try { - return Futures.immediateFuture(task.call()); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public void execute(Runnable command) { - command.run(); - } - }; + dbExecutor = new TestDbCallbackExecutor(); } @Test - public void newAlarmCanBeCreated() throws ScriptException, IOException { + public void newAlarmCanBeCreated() { initWithCreateAlarmScript(); metaData.putValue("key", "value"); - TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); long ts = msg.getTs(); when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null)); @@ -136,7 +119,7 @@ public class TbAlarmNodeTest { .endTs(ts) .tenantId(tenantId) .originator(originator) - .severity(CRITICAL) + .severity(AlarmSeverity.CRITICAL) .propagate(true) .type("SomeType") .details(null) @@ -154,16 +137,16 @@ public class TbAlarmNodeTest { verify(ctx).tellNext(any(), eq("Created")); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(TbMsgType.class); ArgumentCaptor originatorCaptor = ArgumentCaptor.forClass(EntityId.class); ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class); ArgumentCaptor dataCaptor = ArgumentCaptor.forClass(String.class); verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); - assertEquals("ALARM", typeCaptor.getValue()); + assertEquals(TbMsgType.ALARM, typeCaptor.getValue()); assertEquals(originator, originatorCaptor.getValue()); assertEquals("value", metadataCaptor.getValue().getValue("key")); - assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(IS_NEW_ALARM)); + assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(DataConstants.IS_NEW_ALARM)); assertNotSame(metaData, metadataCaptor.getValue()); Alarm actualAlarm = JacksonUtil.fromBytes(dataCaptor.getValue().getBytes(), Alarm.class); @@ -171,10 +154,10 @@ public class TbAlarmNodeTest { } @Test - public void buildDetailsThrowsException() throws ScriptException, IOException { + public void buildDetailsThrowsException() { initWithCreateAlarmScript(); metaData.putValue("key", "value"); - TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFailedFuture(new NotImplementedException("message"))); when(alarmService.findLatestActiveByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(null); @@ -194,10 +177,10 @@ public class TbAlarmNodeTest { } @Test - public void ifAlarmClearedCreateNew() throws ScriptException, IOException { + public void ifAlarmClearedCreateNew() { initWithCreateAlarmScript(); metaData.putValue("key", "value"); - TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); long ts = msg.getTs(); Alarm clearedAlarm = Alarm.builder().cleared(true).acknowledged(true).build(); @@ -209,7 +192,7 @@ public class TbAlarmNodeTest { .endTs(ts) .tenantId(tenantId) .originator(originator) - .severity(CRITICAL) + .severity(AlarmSeverity.CRITICAL) .propagate(true) .type("SomeType") .details(null) @@ -228,16 +211,16 @@ public class TbAlarmNodeTest { verify(ctx).tellNext(any(), eq("Created")); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(TbMsgType.class); ArgumentCaptor originatorCaptor = ArgumentCaptor.forClass(EntityId.class); ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class); ArgumentCaptor dataCaptor = ArgumentCaptor.forClass(String.class); verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); - assertEquals("ALARM", typeCaptor.getValue()); + assertEquals(TbMsgType.ALARM, typeCaptor.getValue()); assertEquals(originator, originatorCaptor.getValue()); assertEquals("value", metadataCaptor.getValue().getValue("key")); - assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(IS_NEW_ALARM)); + assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(DataConstants.IS_NEW_ALARM)); assertNotSame(metaData, metadataCaptor.getValue()); @@ -246,13 +229,13 @@ public class TbAlarmNodeTest { } @Test - public void alarmCanBeUpdated() throws IOException { + public void alarmCanBeUpdated() { initWithCreateAlarmScript(); metaData.putValue("key", "value"); - TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); long oldEndDate = System.currentTimeMillis(); - Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(originator).severity(WARNING).endTs(oldEndDate).build(); + Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(originator).severity(AlarmSeverity.WARNING).endTs(oldEndDate).build(); when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null)); when(alarmService.findLatestActiveByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(activeAlarm); @@ -260,7 +243,7 @@ public class TbAlarmNodeTest { Alarm expectedAlarm = Alarm.builder() .tenantId(tenantId) .originator(originator) - .severity(CRITICAL) + .severity(AlarmSeverity.CRITICAL) .propagate(true) .type("SomeType") .details(null) @@ -279,16 +262,16 @@ public class TbAlarmNodeTest { verify(ctx).tellNext(any(), eq("Updated")); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(TbMsgType.class); ArgumentCaptor originatorCaptor = ArgumentCaptor.forClass(EntityId.class); ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class); ArgumentCaptor dataCaptor = ArgumentCaptor.forClass(String.class); verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); - assertEquals("ALARM", typeCaptor.getValue()); + assertEquals(TbMsgType.ALARM, typeCaptor.getValue()); assertEquals(originator, originatorCaptor.getValue()); assertEquals("value", metadataCaptor.getValue().getValue("key")); - assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(IS_EXISTING_ALARM)); + assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(DataConstants.IS_EXISTING_ALARM)); assertNotSame(metaData, metadataCaptor.getValue()); Alarm actualAlarm = JacksonUtil.fromBytes(dataCaptor.getValue().getBytes(), Alarm.class); @@ -297,19 +280,19 @@ public class TbAlarmNodeTest { } @Test - public void alarmCanBeCleared() throws ScriptException, IOException { + public void alarmCanBeCleared() { initWithClearAlarmScript(); metaData.putValue("key", "value"); - TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); long oldEndDate = System.currentTimeMillis(); - Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(originator).severity(WARNING).endTs(oldEndDate).build(); + Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(originator).severity(AlarmSeverity.WARNING).endTs(oldEndDate).build(); Alarm expectedAlarm = Alarm.builder() .tenantId(tenantId) .originator(originator) .cleared(true) - .severity(WARNING) + .severity(AlarmSeverity.WARNING) .propagate(false) .type("SomeType") .details(null) @@ -332,16 +315,16 @@ public class TbAlarmNodeTest { verify(ctx).tellNext(any(), eq("Cleared")); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(TbMsgType.class); ArgumentCaptor originatorCaptor = ArgumentCaptor.forClass(EntityId.class); ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class); ArgumentCaptor dataCaptor = ArgumentCaptor.forClass(String.class); verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); - assertEquals("ALARM", typeCaptor.getValue()); + assertEquals(TbMsgType.ALARM, typeCaptor.getValue()); assertEquals(originator, originatorCaptor.getValue()); assertEquals("value", metadataCaptor.getValue().getValue("key")); - assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(IS_CLEARED_ALARM)); + assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(DataConstants.IS_CLEARED_ALARM)); assertNotSame(metaData, metadataCaptor.getValue()); Alarm actualAlarm = JacksonUtil.fromBytes(dataCaptor.getValue().getBytes(), Alarm.class); @@ -352,18 +335,18 @@ public class TbAlarmNodeTest { public void alarmCanBeClearedWithAlarmOriginator() throws ScriptException, IOException { initWithClearAlarmScript(); metaData.putValue("key", "value"); - TbMsg msg = TbMsg.newMsg("USER", alarmOriginator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, alarmOriginator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); long oldEndDate = System.currentTimeMillis(); AlarmId id = new AlarmId(alarmOriginator.getId()); - Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(originator).severity(WARNING).endTs(oldEndDate).build(); + Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(originator).severity(AlarmSeverity.WARNING).endTs(oldEndDate).build(); activeAlarm.setId(id); Alarm expectedAlarm = Alarm.builder() .tenantId(tenantId) .originator(originator) .cleared(true) - .severity(WARNING) + .severity(AlarmSeverity.WARNING) .propagate(false) .type("SomeType") .details(null) @@ -387,16 +370,16 @@ public class TbAlarmNodeTest { verify(ctx).tellNext(any(), eq("Cleared")); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(TbMsgType.class); ArgumentCaptor originatorCaptor = ArgumentCaptor.forClass(EntityId.class); ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class); ArgumentCaptor dataCaptor = ArgumentCaptor.forClass(String.class); verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); - assertEquals("ALARM", typeCaptor.getValue()); + assertEquals(TbMsgType.ALARM, typeCaptor.getValue()); assertEquals(alarmOriginator, originatorCaptor.getValue()); assertEquals("value", metadataCaptor.getValue().getValue("key")); - assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(IS_CLEARED_ALARM)); + assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(DataConstants.IS_CLEARED_ALARM)); assertNotSame(metaData, metadataCaptor.getValue()); Alarm actualAlarm = JacksonUtil.fromBytes(dataCaptor.getValue().getBytes(), Alarm.class); @@ -425,14 +408,14 @@ public class TbAlarmNodeTest { String rawJson = "{\"alarmSeverity\": \"WARNING\", \"passed\": 5}"; metaData.putValue("key", "value"); - TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); long ts = msg.getTs(); Alarm expectedAlarm = Alarm.builder() .startTs(ts) .endTs(ts) .tenantId(tenantId) .originator(originator) - .severity(WARNING) + .severity(AlarmSeverity.WARNING) .propagate(true) .type("SomeType") .details(null) @@ -454,16 +437,16 @@ public class TbAlarmNodeTest { verify(ctx).tellNext(any(), eq("Created")); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(TbMsgType.class); ArgumentCaptor originatorCaptor = ArgumentCaptor.forClass(EntityId.class); ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class); ArgumentCaptor dataCaptor = ArgumentCaptor.forClass(String.class); verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); - assertEquals("ALARM", typeCaptor.getValue()); + assertEquals(TbMsgType.ALARM, typeCaptor.getValue()); assertEquals(originator, originatorCaptor.getValue()); assertEquals("value", metadataCaptor.getValue().getValue("key")); - assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(IS_NEW_ALARM)); + assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(DataConstants.IS_NEW_ALARM)); assertNotSame(metaData, metadataCaptor.getValue()); Alarm actualAlarm = JacksonUtil.fromBytes(dataCaptor.getValue().getBytes(), Alarm.class); @@ -491,14 +474,14 @@ public class TbAlarmNodeTest { node.init(ctx, nodeConfiguration); metaData.putValue("alarmSeverity", "WARNING"); - TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); long ts = msg.getTs(); Alarm expectedAlarm = Alarm.builder() .startTs(ts) .endTs(ts) .tenantId(tenantId) .originator(originator) - .severity(WARNING) + .severity(AlarmSeverity.WARNING) .propagate(true) .type("SomeType") .details(null) @@ -520,15 +503,15 @@ public class TbAlarmNodeTest { verify(ctx).tellNext(any(), eq("Created")); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(TbMsgType.class); ArgumentCaptor originatorCaptor = ArgumentCaptor.forClass(EntityId.class); ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class); ArgumentCaptor dataCaptor = ArgumentCaptor.forClass(String.class); verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); - assertEquals("ALARM", typeCaptor.getValue()); + assertEquals(TbMsgType.ALARM, typeCaptor.getValue()); assertEquals(originator, originatorCaptor.getValue()); - assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(IS_NEW_ALARM)); + assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(DataConstants.IS_NEW_ALARM)); assertNotSame(metaData, metadataCaptor.getValue()); Alarm actualAlarm = JacksonUtil.fromBytes(dataCaptor.getValue().getBytes(), Alarm.class); @@ -540,7 +523,7 @@ public class TbAlarmNodeTest { for (int i = 0; i < 10; i++) { var config = new TbCreateAlarmNodeConfiguration(); config.setPropagateToTenant(true); - config.setSeverity(CRITICAL.name()); + config.setSeverity(AlarmSeverity.CRITICAL.name()); config.setAlarmType("SomeType" + i); config.setScriptLang(ScriptLanguage.JS); config.setAlarmDetailsBuildJs("DETAILS"); @@ -557,14 +540,14 @@ public class TbAlarmNodeTest { node.init(ctx, nodeConfiguration); metaData.putValue("key", "value"); - TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); long ts = msg.getTs(); Alarm expectedAlarm = Alarm.builder() .startTs(ts) .endTs(ts) .tenantId(tenantId) .originator(originator) - .severity(CRITICAL) + .severity(AlarmSeverity.CRITICAL) .propagateToTenant(true) .type("SomeType" + i) .details(null) @@ -585,16 +568,16 @@ public class TbAlarmNodeTest { verify(ctx, atMost(10)).tellNext(any(), eq("Created")); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(TbMsgType.class); ArgumentCaptor originatorCaptor = ArgumentCaptor.forClass(EntityId.class); ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class); ArgumentCaptor dataCaptor = ArgumentCaptor.forClass(String.class); verify(ctx, atMost(10)).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); - assertEquals("ALARM", typeCaptor.getValue()); + assertEquals(TbMsgType.ALARM, typeCaptor.getValue()); assertEquals(originator, originatorCaptor.getValue()); assertEquals("value", metadataCaptor.getValue().getValue("key")); - assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(IS_NEW_ALARM)); + assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(DataConstants.IS_NEW_ALARM)); assertNotSame(metaData, metadataCaptor.getValue()); Alarm actualAlarm = JacksonUtil.fromBytes(dataCaptor.getValue().getBytes(), Alarm.class); @@ -606,7 +589,7 @@ public class TbAlarmNodeTest { try { TbCreateAlarmNodeConfiguration config = new TbCreateAlarmNodeConfiguration(); config.setPropagate(true); - config.setSeverity(CRITICAL.name()); + config.setSeverity(AlarmSeverity.CRITICAL.name()); config.setAlarmType("SomeType"); config.setScriptLang(ScriptLanguage.JS); config.setAlarmDetailsBuildJs("DETAILS"); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeTest.java index 22df15631e..f36cc94540 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeTest.java @@ -17,7 +17,6 @@ package org.thingsboard.rule.engine.action; import com.datastax.oss.driver.api.core.uuid.Uuids; import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -26,17 +25,19 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ListeningExecutor; +import org.thingsboard.rule.engine.TestDbCallbackExecutor; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; -import org.thingsboard.rule.engine.api.TbRelationTypes; -import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.RelationTypeGroup; @@ -47,7 +48,6 @@ import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.relation.RelationService; import java.util.Collections; -import java.util.concurrent.Callable; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; @@ -58,8 +58,6 @@ import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class TbCreateRelationNodeTest { - private static final String RELATION_TYPE_CONTAINS = "Contains"; - private TbCreateRelationNode node; @Mock @@ -78,21 +76,7 @@ public class TbCreateRelationNodeTest { @Before public void before() { - dbExecutor = new ListeningExecutor() { - @Override - public ListenableFuture executeAsync(Callable task) { - try { - return Futures.immediateFuture(task.call()); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public void execute(Runnable command) { - command.run(); - } - }; + dbExecutor = new TestDbCallbackExecutor(); } @Test @@ -111,15 +95,15 @@ public class TbCreateRelationNodeTest { TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("name", "AssetName"); metaData.putValue("type", "AssetType"); - msg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId, metaData, TbMsgDataType.JSON, "{}", ruleChainId, ruleNodeId); + msg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, deviceId, metaData, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); - when(ctx.getRelationService().checkRelationAsync(any(), eq(assetId), eq(deviceId), eq(RELATION_TYPE_CONTAINS), eq(RelationTypeGroup.COMMON))) + when(ctx.getRelationService().checkRelationAsync(any(), eq(assetId), eq(deviceId), eq(EntityRelation.CONTAINS_TYPE), eq(RelationTypeGroup.COMMON))) .thenReturn(Futures.immediateFuture(false)); - when(ctx.getRelationService().saveRelationAsync(any(), eq(new EntityRelation(assetId, deviceId, RELATION_TYPE_CONTAINS, RelationTypeGroup.COMMON)))) + when(ctx.getRelationService().saveRelationAsync(any(), eq(new EntityRelation(assetId, deviceId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON)))) .thenReturn(Futures.immediateFuture(true)); node.onMsg(ctx, msg); - verify(ctx).tellNext(msg, TbRelationTypes.SUCCESS); + verify(ctx).tellNext(msg, TbNodeConnectionType.SUCCESS); } @Test @@ -138,19 +122,19 @@ public class TbCreateRelationNodeTest { TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("name", "AssetName"); metaData.putValue("type", "AssetType"); - msg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId, metaData, TbMsgDataType.JSON, "{}", ruleChainId, ruleNodeId); + msg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, deviceId, metaData, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); EntityRelation relation = new EntityRelation(); - when(ctx.getRelationService().findByToAndTypeAsync(any(), eq(msg.getOriginator()), eq(RELATION_TYPE_CONTAINS), eq(RelationTypeGroup.COMMON))) + when(ctx.getRelationService().findByToAndTypeAsync(any(), eq(msg.getOriginator()), eq(EntityRelation.CONTAINS_TYPE), eq(RelationTypeGroup.COMMON))) .thenReturn(Futures.immediateFuture(Collections.singletonList(relation))); when(ctx.getRelationService().deleteRelationAsync(any(), eq(relation))).thenReturn(Futures.immediateFuture(true)); - when(ctx.getRelationService().checkRelationAsync(any(), eq(assetId), eq(deviceId), eq(RELATION_TYPE_CONTAINS), eq(RelationTypeGroup.COMMON))) + when(ctx.getRelationService().checkRelationAsync(any(), eq(assetId), eq(deviceId), eq(EntityRelation.CONTAINS_TYPE), eq(RelationTypeGroup.COMMON))) .thenReturn(Futures.immediateFuture(false)); - when(ctx.getRelationService().saveRelationAsync(any(), eq(new EntityRelation(assetId, deviceId, RELATION_TYPE_CONTAINS, RelationTypeGroup.COMMON)))) + when(ctx.getRelationService().saveRelationAsync(any(), eq(new EntityRelation(assetId, deviceId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON)))) .thenReturn(Futures.immediateFuture(true)); node.onMsg(ctx, msg); - verify(ctx).tellNext(msg, TbRelationTypes.SUCCESS); + verify(ctx).tellNext(msg, TbNodeConnectionType.SUCCESS); } @Test @@ -169,20 +153,17 @@ public class TbCreateRelationNodeTest { TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue("name", "AssetName"); metaData.putValue("type", "AssetType"); - msg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId, metaData, TbMsgDataType.JSON, "{}", ruleChainId, ruleNodeId); + msg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, deviceId, metaData, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); - when(ctx.getRelationService().checkRelationAsync(any(), eq(assetId), eq(deviceId), eq(RELATION_TYPE_CONTAINS), eq(RelationTypeGroup.COMMON))) + when(ctx.getRelationService().checkRelationAsync(any(), eq(assetId), eq(deviceId), eq(EntityRelation.CONTAINS_TYPE), eq(RelationTypeGroup.COMMON))) .thenReturn(Futures.immediateFuture(false)); - when(ctx.getRelationService().saveRelationAsync(any(), eq(new EntityRelation(assetId, deviceId, RELATION_TYPE_CONTAINS, RelationTypeGroup.COMMON)))) + when(ctx.getRelationService().saveRelationAsync(any(), eq(new EntityRelation(assetId, deviceId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON)))) .thenReturn(Futures.immediateFuture(true)); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor originatorCaptor = ArgumentCaptor.forClass(EntityId.class); - ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class); - ArgumentCaptor dataCaptor = ArgumentCaptor.forClass(String.class); - verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); + verify(ctx).transformMsgOriginator(msgCaptor.capture(), originatorCaptor.capture()); assertEquals(assetId, originatorCaptor.getValue()); } @@ -201,9 +182,9 @@ public class TbCreateRelationNodeTest { private TbCreateRelationNodeConfiguration createRelationNodeConfig() { TbCreateRelationNodeConfiguration configuration = new TbCreateRelationNodeConfiguration(); configuration.setDirection(EntitySearchDirection.FROM.name()); - configuration.setRelationType(RELATION_TYPE_CONTAINS); + configuration.setRelationType(EntityRelation.CONTAINS_TYPE); configuration.setEntityCacheExpiration(300); - configuration.setEntityType("ASSET"); + configuration.setEntityType(EntityType.ASSET.name()); configuration.setEntityNamePattern("${name}"); configuration.setEntityTypePattern("${type}"); configuration.setCreateEntityIfNotExists(false); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java index f12288da3a..885002a694 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java @@ -24,6 +24,7 @@ import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.script.ScriptLanguage; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -49,7 +50,7 @@ public class TbLogNodeTest { TbLogNode node = new TbLogNode(); String data = "{\"key\": \"value\"}"; TbMsgMetaData metaData = new TbMsgMetaData(Map.of("mdKey1", "mdValue1", "mdKey2", "23")); - TbMsg msg = TbMsg.newMsg("POST_TELEMETRY", TenantId.SYS_TENANT_ID, metaData, data); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, metaData, data); String logMessage = node.toLogMessage(msg); log.info(logMessage); @@ -65,7 +66,7 @@ public class TbLogNodeTest { void givenEmptyDataMsg_whenToLog_thenReturnString() { TbLogNode node = new TbLogNode(); TbMsgMetaData metaData = new TbMsgMetaData(Collections.emptyMap()); - TbMsg msg = TbMsg.newMsg("POST_TELEMETRY", TenantId.SYS_TENANT_ID, metaData, ""); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, metaData, ""); String logMessage = node.toLogMessage(msg); log.info(logMessage); @@ -81,7 +82,7 @@ public class TbLogNodeTest { void givenNullDataMsg_whenToLog_thenReturnString() { TbLogNode node = new TbLogNode(); TbMsgMetaData metaData = new TbMsgMetaData(Collections.emptyMap()); - TbMsg msg = TbMsg.newMsg("POST_TELEMETRY", TenantId.SYS_TENANT_ID, metaData, null); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TenantId.SYS_TENANT_ID, metaData, null); String logMessage = node.toLogMessage(msg); log.info(logMessage); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/credentials/CertPemCredentialsTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/credentials/CertPemCredentialsTest.java index 904f470001..2cecd1490d 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/credentials/CertPemCredentialsTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/credentials/CertPemCredentialsTest.java @@ -18,21 +18,36 @@ package org.thingsboard.rule.engine.credentials; import org.apache.commons.io.FileUtils; import org.junit.Assert; import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.thingsboard.common.util.SslUtil; import java.io.File; import java.io.IOException; +import java.security.Key; +import java.security.KeyStore; +import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.List; +import java.util.stream.Stream; + +import static org.thingsboard.rule.engine.credentials.CertPemCredentials.CERT_ALIAS_PREFIX; +import static org.thingsboard.rule.engine.credentials.CertPemCredentials.PRIVATE_KEY_ALIAS; public class CertPemCredentialsTest { - private final CertPemCredentials credentials = new CertPemCredentials(); + private static final String PASS = "test"; + private static final String EMPTY_PASS = ""; + private static final String RSA = "RSA"; + private static final String EC = "EC"; @Test public void testChainOfCertificates() throws Exception { String fileContent = fileContent("pem/tb-cloud-chain.pem"); - List x509Certificates = credentials.readCertFile(fileContent); + List x509Certificates = SslUtil.readCertFile(fileContent); Assert.assertEquals(4, x509Certificates.size()); Assert.assertEquals("CN=*.thingsboard.cloud, O=\"ThingsBoard, Inc.\", ST=New York, C=US", @@ -49,7 +64,7 @@ public class CertPemCredentialsTest { public void testSingleCertificate() throws Exception { String fileContent = fileContent("pem/tb-cloud.pem"); - List x509Certificates = credentials.readCertFile(fileContent); + List x509Certificates = SslUtil.readCertFile(fileContent); Assert.assertEquals(1, x509Certificates.size()); Assert.assertEquals("CN=*.thingsboard.cloud, O=\"ThingsBoard, Inc.\", ST=New York, C=US", @@ -60,11 +75,43 @@ public class CertPemCredentialsTest { public void testEmptyFileContent() throws Exception { String fileContent = fileContent("pem/empty.pem"); - List x509Certificates = credentials.readCertFile(fileContent); + List x509Certificates = SslUtil.readCertFile(fileContent); Assert.assertEquals(0, x509Certificates.size()); } + private static Stream testLoadKeyStore() { + return Stream.of( + Arguments.of("pem/rsa_cert.pem", "pem/rsa_key.pem", EMPTY_PASS, RSA), + Arguments.of("pem/rsa_encrypted_cert.pem", "pem/rsa_encrypted_key.pem", PASS, RSA), + Arguments.of("pem/rsa_encrypted_traditional_cert.pem", "pem/rsa_encrypted_traditional_key.pem", PASS, RSA), + Arguments.of("pem/ec_cert.pem", "pem/ec_key.pem", EMPTY_PASS, EC) + ); + } + + @ParameterizedTest + @MethodSource + public void testLoadKeyStore(String certPath, String keyPath, String password, String algorithm) throws Exception { + CertPemCredentials certPemCredentials = new CertPemCredentials(); + String certContent = fileContent(certPath); + certPemCredentials.setCert(certContent); + certPemCredentials.setPrivateKey(fileContent(keyPath)); + certPemCredentials.setPassword(password); + KeyStore keyStore = certPemCredentials.loadKeyStore(); + Assertions.assertNotNull(keyStore); + Key key = keyStore.getKey(PRIVATE_KEY_ALIAS, password.toCharArray()); + Assertions.assertNotNull(key); + Assertions.assertEquals(algorithm, key.getAlgorithm()); + + List certs = SslUtil.readCertFile(certContent); + for (X509Certificate cert : certs) { + String alias = CERT_ALIAS_PREFIX + cert.getIssuerDN().getName(); + Certificate certificate = keyStore.getCertificate(alias); + Assertions.assertNotNull(certificate); + Assertions.assertEquals(new String(cert.getEncoded()), new String(certificate.getEncoded())); + } + } + private String fileContent(String fileName) throws IOException { ClassLoader classLoader = getClass().getClassLoader(); File file = new File(classLoader.getResource(fileName).getFile()); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNodeTest.java index 84deafe7e4..f44326a8d5 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNodeTest.java @@ -35,12 +35,12 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; -import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeService; @@ -53,6 +53,9 @@ import static org.mockito.Mockito.verify; @RunWith(MockitoJUnitRunner.class) public class TbMsgPushToEdgeNodeTest { + private static final List MISC_EVENTS = List.of(TbMsgType.CONNECT_EVENT, TbMsgType.DISCONNECT_EVENT, + TbMsgType.ACTIVITY_EVENT, TbMsgType.INACTIVITY_EVENT); + TbMsgPushToEdgeNode node; private final TenantId tenantId = TenantId.fromUUID(UUID.randomUUID()); @@ -81,8 +84,8 @@ public class TbMsgPushToEdgeNodeTest { Mockito.when(ctx.getEdgeService()).thenReturn(edgeService); Mockito.when(edgeService.findRelatedEdgeIdsByEntityId(tenantId, deviceId, new PageLink(TbMsgPushToEdgeNode.DEFAULT_PAGE_SIZE))).thenReturn(new PageData<>()); - TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), - TbMsgDataType.JSON, "{}", null, null); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, + TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, null, null); node.onMsg(ctx, msg); @@ -102,8 +105,8 @@ public class TbMsgPushToEdgeNodeTest { PageData edgePageData = new PageData<>(List.of(edgeId), 1, 1, false); Mockito.when(edgeService.findRelatedEdgeIdsByEntityId(tenantId, userId, new PageLink(TbMsgPushToEdgeNode.DEFAULT_PAGE_SIZE))).thenReturn(edgePageData); - TbMsg msg = TbMsg.newMsg(DataConstants.ATTRIBUTES_UPDATED, userId, new TbMsgMetaData(), - TbMsgDataType.JSON, "{}", null, null); + TbMsg msg = TbMsg.newMsg(TbMsgType.ATTRIBUTES_UPDATED, userId, TbMsgMetaData.EMPTY, + TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, null, null); node.onMsg(ctx, msg); @@ -112,9 +115,7 @@ public class TbMsgPushToEdgeNodeTest { @Test public void testMiscEventsProcessedAsAttributesUpdated() { - List miscEvents = List.of(DataConstants.CONNECT_EVENT, DataConstants.DISCONNECT_EVENT, - DataConstants.ACTIVITY_EVENT, DataConstants.INACTIVITY_EVENT); - for (String event : miscEvents) { + for (var event : MISC_EVENTS) { TbMsgMetaData metaData = new TbMsgMetaData(); metaData.putValue(DataConstants.SCOPE, DataConstants.SERVER_SCOPE); testEvent(event, metaData, EdgeEventActionType.ATTRIBUTES_UPDATED, "kv"); @@ -123,14 +124,12 @@ public class TbMsgPushToEdgeNodeTest { @Test public void testMiscEventsProcessedAsTimeseriesUpdated() { - List miscEvents = List.of(DataConstants.CONNECT_EVENT, DataConstants.DISCONNECT_EVENT, - DataConstants.ACTIVITY_EVENT, DataConstants.INACTIVITY_EVENT); - for (String event : miscEvents) { - testEvent(event, new TbMsgMetaData(), EdgeEventActionType.TIMESERIES_UPDATED, "data"); + for (var event : MISC_EVENTS) { + testEvent(event, TbMsgMetaData.EMPTY, EdgeEventActionType.TIMESERIES_UPDATED, "data"); } } - private void testEvent(String event, TbMsgMetaData metaData, EdgeEventActionType expectedType, String dataKey) { + private void testEvent(TbMsgType event, TbMsgMetaData metaData, EdgeEventActionType expectedType, String dataKey) { Mockito.when(ctx.getTenantId()).thenReturn(tenantId); Mockito.when(ctx.getEdgeService()).thenReturn(edgeService); Mockito.when(ctx.getEdgeEventService()).thenReturn(edgeEventService); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNodeTest.java index 4ddf75f29c..785be659fd 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNodeTest.java @@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.queue.TbMsgCallback; @@ -49,34 +50,30 @@ import static org.mockito.Mockito.when; class TbAssetTypeSwitchNodeTest { - TenantId tenantId; - AssetId assetId; - AssetId assetIdDeleted; - AssetProfile assetProfile; - TbContext ctx; - TbAssetTypeSwitchNode node; - EmptyNodeConfiguration config; - TbMsgCallback callback; - RuleEngineAssetProfileCache assetProfileCache; + private AssetId assetId; + private AssetId assetIdDeleted; + private TbContext ctx; + private TbAssetTypeSwitchNode node; + private TbMsgCallback callback; @BeforeEach void setUp() throws TbNodeException { - tenantId = new TenantId(UUID.randomUUID()); + TenantId tenantId = new TenantId(UUID.randomUUID()); assetId = new AssetId(UUID.randomUUID()); assetIdDeleted = new AssetId(UUID.randomUUID()); - assetProfile = new AssetProfile(); + AssetProfile assetProfile = new AssetProfile(); assetProfile.setTenantId(tenantId); assetProfile.setName("TestAssetProfile"); //node - config = new EmptyNodeConfiguration(); + EmptyNodeConfiguration config = new EmptyNodeConfiguration(); node = new TbAssetTypeSwitchNode(); node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); //init mock ctx = mock(TbContext.class); - assetProfileCache = mock(RuleEngineAssetProfileCache.class); + RuleEngineAssetProfileCache assetProfileCache = mock(RuleEngineAssetProfileCache.class); callback = mock(TbMsgCallback.class); when(ctx.getTenantId()).thenReturn(tenantId); @@ -121,6 +118,7 @@ class TbAssetTypeSwitchNodeTest { } private TbMsg getTbMsg(EntityId entityId) { - return TbMsg.newMsg("POST_ATTRIBUTES_REQUEST", entityId, new TbMsgMetaData(), "{}", callback); + return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT, callback); } + } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java new file mode 100644 index 0000000000..a146b95ee1 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNodeTest.java @@ -0,0 +1,165 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.filter; + +import com.google.common.util.concurrent.Futures; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.TestDbCallbackExecutor; +import org.thingsboard.rule.engine.api.RuleEngineAlarmService; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class TbCheckAlarmStatusNodeTest { + + private static final TenantId TENANT_ID = new TenantId(UUID.randomUUID()); + private static final DeviceId DEVICE_ID = new DeviceId(UUID.randomUUID()); + private static final AlarmId ALARM_ID = new AlarmId(UUID.randomUUID()); + private static final TestDbCallbackExecutor DB_EXECUTOR = new TestDbCallbackExecutor(); + + private TbCheckAlarmStatusNode node; + + private TbContext ctx; + private RuleEngineAlarmService alarmService; + + @BeforeEach + void setUp() throws TbNodeException { + var config = new TbCheckAlarmStatusNodeConfig().defaultConfiguration(); + + ctx = mock(TbContext.class); + alarmService = mock(RuleEngineAlarmService.class); + + when(ctx.getTenantId()).thenReturn(TENANT_ID); + when(ctx.getAlarmService()).thenReturn(alarmService); + when(ctx.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + node = new TbCheckAlarmStatusNode(); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + } + + @AfterEach + void tearDown() { + node.destroy(); + } + + @Test + void givenActiveAlarm_whenOnMsg_then_True() throws TbNodeException { + // GIVEN + var alarm = new Alarm(); + alarm.setId(ALARM_ID); + alarm.setOriginator(DEVICE_ID); + alarm.setType("General Alarm"); + + String msgData = JacksonUtil.toString(alarm); + TbMsg msg = getTbMsg(msgData); + + when(alarmService.findAlarmByIdAsync(TENANT_ID, ALARM_ID)).thenReturn(Futures.immediateFuture(alarm)); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(msg); + } + + @Test + void givenClearedAlarm_whenOnMsg_then_False() throws TbNodeException { + // GIVEN + var alarm = new Alarm(); + alarm.setId(ALARM_ID); + alarm.setOriginator(DEVICE_ID); + alarm.setType("General Alarm"); + alarm.setCleared(true); + + String msgData = JacksonUtil.toString(alarm); + TbMsg msg = getTbMsg(msgData); + + when(alarmService.findAlarmByIdAsync(TENANT_ID, ALARM_ID)).thenReturn(Futures.immediateFuture(alarm)); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.FALSE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(msg); + } + + @Test + void givenDeletedAlarm_whenOnMsg_then_Failure() throws TbNodeException { + // GIVEN + var alarm = new Alarm(); + alarm.setId(ALARM_ID); + alarm.setOriginator(DEVICE_ID); + alarm.setType("General Alarm"); + alarm.setCleared(true); + + String msgData = JacksonUtil.toString(alarm); + TbMsg msg = getTbMsg(msgData); + + when(alarmService.findAlarmByIdAsync(TENANT_ID, ALARM_ID)).thenReturn(Futures.immediateFuture(null)); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + ArgumentCaptor throwableCaptor = ArgumentCaptor.forClass(Throwable.class); + verify(ctx, times(1)).tellFailure(newMsgCaptor.capture(), throwableCaptor.capture()); + verify(ctx, never()).tellSuccess(any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(msg); + Throwable value = throwableCaptor.getValue(); + assertThat(value).isInstanceOf(TbNodeException.class).hasMessage("No such alarm found."); + } + + private TbMsg getTbMsg(String msgData) { + return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, msgData); + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeTest.java new file mode 100644 index 0000000000..e69cc55d7e --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckMessageNodeTest.java @@ -0,0 +1,202 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.filter; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; + +import java.util.List; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +class TbCheckMessageNodeTest { + + private static final DeviceId DEVICE_ID = new DeviceId(UUID.randomUUID()); + private static final TbMsg EMPTY_POST_ATTRIBUTES_MSG = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + + private TbCheckMessageNode node; + + private TbContext ctx; + + @BeforeEach + void setUp() { + ctx = mock(TbContext.class); + node = new TbCheckMessageNode(); + } + + @AfterEach + void tearDown() { + node.destroy(); + } + + @Test + void givenDefaultConfig_whenOnMsg_then_True() throws TbNodeException { + // GIVEN + var configuration = new TbCheckMessageNodeConfiguration().defaultConfiguration(); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(configuration))); + + // WHEN + node.onMsg(ctx, EMPTY_POST_ATTRIBUTES_MSG); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(EMPTY_POST_ATTRIBUTES_MSG); + } + + @Test + void givenCustomConfigWithoutCheckAllKeysAndWithEmptyLists_whenOnMsg_then_False() throws TbNodeException { + // GIVEN + var configuration = new TbCheckMessageNodeConfiguration().defaultConfiguration(); + configuration.setCheckAllKeys(false); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(configuration))); + + // WHEN + node.onMsg(ctx, EMPTY_POST_ATTRIBUTES_MSG); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.FALSE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(EMPTY_POST_ATTRIBUTES_MSG); + } + + @Test + void givenCustomConfigWithCheckAllKeys_whenOnMsg_then_True() throws TbNodeException { + // GIVEN + var configuration = new TbCheckMessageNodeConfiguration().defaultConfiguration(); + configuration.setMessageNames(List.of("temperature-0")); + configuration.setMetadataNames(List.of("deviceName", "deviceType", "ts")); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(configuration))); + + TbMsg tbMsg = getTbMsg(); + + // WHEN + node.onMsg(ctx, tbMsg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(tbMsg); + } + + @Test + void givenCustomConfigWithCheckAllKeys_whenOnMsg_then_False() throws TbNodeException { + // GIVEN + var configuration = new TbCheckMessageNodeConfiguration().defaultConfiguration(); + configuration.setMessageNames(List.of("temperature-0", "temperature-1")); + configuration.setMetadataNames(List.of("deviceName", "deviceType", "ts")); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(configuration))); + + TbMsg tbMsg = getTbMsg(); + + // WHEN + node.onMsg(ctx, tbMsg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.FALSE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(tbMsg); + } + + @Test + void givenCustomConfigWithoutCheckAllKeys_whenOnMsg_then_True() throws TbNodeException { + // GIVEN + var configuration = new TbCheckMessageNodeConfiguration().defaultConfiguration(); + configuration.setMessageNames(List.of("temperature-0", "temperature-1")); + configuration.setCheckAllKeys(false); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(configuration))); + + TbMsg tbMsg = getTbMsg(); + + // WHEN + node.onMsg(ctx, tbMsg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(tbMsg); + } + + @Test + void givenCustomConfigWithoutCheckAllKeysAndEmptyMsg_whenOnMsg_then_False() throws TbNodeException { + // GIVEN + var configuration = new TbCheckMessageNodeConfiguration().defaultConfiguration(); + configuration.setMessageNames(List.of("temperature-0", "temperature-1")); + configuration.setCheckAllKeys(false); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(configuration))); + + TbMsg tbMsg = getTbMsg(true); + + // WHEN + node.onMsg(ctx, tbMsg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.FALSE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(tbMsg); + } + + private TbMsg getTbMsg() { + return getTbMsg(false); + } + + private TbMsg getTbMsg(boolean emptyData) { + String data = emptyData ? TbMsg.EMPTY_JSON_OBJECT : "{\"temperature-0\": 25}"; + var metadata = new TbMsgMetaData(); + metadata.putValue(DataConstants.DEVICE_NAME, "Test Device"); + metadata.putValue(DataConstants.DEVICE_TYPE, DataConstants.DEFAULT_DEVICE_TYPE); + metadata.putValue("ts", String.valueOf(System.currentTimeMillis())); + return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, DEVICE_ID, metadata, data); + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeTest.java new file mode 100644 index 0000000000..bf7fa31f18 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbCheckRelationNodeTest.java @@ -0,0 +1,313 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.filter; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.util.concurrent.Futures; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.TestDbCallbackExecutor; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.util.TbPair; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.dao.relation.RelationService; + +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class TbCheckRelationNodeTest { + + private static final TenantId TENANT_ID = new TenantId(UUID.randomUUID()); + private static final DeviceId ORIGINATOR_ID = new DeviceId(UUID.randomUUID()); + private static final TestDbCallbackExecutor DB_EXECUTOR = new TestDbCallbackExecutor(); + private static final TbMsg EMPTY_POST_ATTRIBUTES_MSG = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, ORIGINATOR_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + + private TbCheckRelationNode node; + + private TbContext ctx; + private RelationService relationService; + + @BeforeEach + void setUp() { + ctx = mock(TbContext.class); + relationService = mock(RelationService.class); + + when(ctx.getTenantId()).thenReturn(TENANT_ID); + when(ctx.getRelationService()).thenReturn(relationService); + when(ctx.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + node = new TbCheckRelationNode(); + } + + @AfterEach + void tearDown() { + node.destroy(); + } + + @Test + void givenDefaultConfig_whenInit_then_throwException() { + // GIVEN + var config = new TbCheckRelationNodeConfiguration().defaultConfiguration(); + + // WHEN + var exception = assertThrows(TbNodeException.class, () -> node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))); + + // THEN + assertThat(exception.getMessage()).isEqualTo("Entity should be specified!"); + } + + @Test + void givenCustomConfigWithCheckRelationToSpecificEntity_whenOnMsg_then_True() throws TbNodeException { + // GIVEN + var config = new TbCheckRelationNodeConfiguration().defaultConfiguration(); + + AssetId assetId = new AssetId(UUID.randomUUID()); + config.setEntityType(assetId.getEntityType().name()); + config.setEntityId(assetId.getId().toString()); + + when(relationService.checkRelationAsync(TENANT_ID, ORIGINATOR_ID, assetId, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(true)); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + // WHEN + node.onMsg(ctx, EMPTY_POST_ATTRIBUTES_MSG); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(EMPTY_POST_ATTRIBUTES_MSG); + } + + @Test + void givenCustomConfigWithCheckRelationToSpecificEntity_whenOnMsg_then_False() throws TbNodeException { + // GIVEN + var config = new TbCheckRelationNodeConfiguration().defaultConfiguration(); + + AssetId assetId = new AssetId(UUID.randomUUID()); + config.setEntityType(assetId.getEntityType().name()); + config.setEntityId(assetId.getId().toString()); + + when(relationService.checkRelationAsync(TENANT_ID, ORIGINATOR_ID, assetId, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(false)); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + // WHEN + node.onMsg(ctx, EMPTY_POST_ATTRIBUTES_MSG); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.FALSE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(EMPTY_POST_ATTRIBUTES_MSG); + } + + @Test + void givenCustomConfigWithCheckRelationToSpecificEntityAndDirectionTo_whenOnMsg_then_True() throws TbNodeException { + // GIVEN + var config = new TbCheckRelationNodeConfiguration().defaultConfiguration(); + + AssetId assetId = new AssetId(UUID.randomUUID()); + config.setEntityType(assetId.getEntityType().name()); + config.setEntityId(assetId.getId().toString()); + config.setDirection(EntitySearchDirection.TO.name()); + + when(relationService.checkRelationAsync(TENANT_ID, assetId, ORIGINATOR_ID, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(true)); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + // WHEN + node.onMsg(ctx, EMPTY_POST_ATTRIBUTES_MSG); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(EMPTY_POST_ATTRIBUTES_MSG); + } + + @Test + void givenCustomConfigWithCheckRelationToSpecificEntityAndDirectionTo_whenOnMsg_then_False() throws TbNodeException { + // GIVEN + var config = new TbCheckRelationNodeConfiguration().defaultConfiguration(); + + AssetId assetId = new AssetId(UUID.randomUUID()); + config.setEntityType(assetId.getEntityType().name()); + config.setEntityId(assetId.getId().toString()); + config.setDirection(EntitySearchDirection.TO.name()); + + when(relationService.checkRelationAsync(TENANT_ID, assetId, ORIGINATOR_ID, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(false)); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + // WHEN + node.onMsg(ctx, EMPTY_POST_ATTRIBUTES_MSG); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.FALSE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(EMPTY_POST_ATTRIBUTES_MSG); + } + + @Test + void givenCustomConfig_whenOnMsg_then_True() throws TbNodeException { + // GIVEN + var config = new TbCheckRelationNodeConfiguration().defaultConfiguration(); + config.setCheckForSingleEntity(false); + var entityRelation = new EntityRelation(); + entityRelation.setFrom(ORIGINATOR_ID); + entityRelation.setTo(new AssetId(UUID.randomUUID())); + entityRelation.setType(EntityRelation.CONTAINS_TYPE); + entityRelation.setTypeGroup(RelationTypeGroup.COMMON); + + when(relationService.findByFromAndTypeAsync(TENANT_ID, ORIGINATOR_ID, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(List.of(entityRelation))); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + // WHEN + node.onMsg(ctx, EMPTY_POST_ATTRIBUTES_MSG); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE)); + verify(ctx, never()).tellFailure(any(), any()); + verify(relationService, never()).findByToAndTypeAsync(any(), any(), anyString(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(EMPTY_POST_ATTRIBUTES_MSG); + } + + @Test + void givenCustomConfig_whenOnMsg_then_False() throws TbNodeException { + // GIVEN + var config = new TbCheckRelationNodeConfiguration().defaultConfiguration(); + config.setCheckForSingleEntity(false); + + when(relationService.findByFromAndTypeAsync(TENANT_ID, ORIGINATOR_ID, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(Collections.emptyList())); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + // WHEN + node.onMsg(ctx, EMPTY_POST_ATTRIBUTES_MSG); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.FALSE)); + verify(ctx, never()).tellFailure(any(), any()); + verify(relationService, never()).findByToAndTypeAsync(any(), any(), anyString(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(EMPTY_POST_ATTRIBUTES_MSG); + } + + @Test + void givenCustomConfigDirectionTo_whenOnMsg_then_True() throws TbNodeException { + // GIVEN + var config = new TbCheckRelationNodeConfiguration().defaultConfiguration(); + config.setCheckForSingleEntity(false); + config.setDirection(EntitySearchDirection.TO.name()); + var entityRelation = new EntityRelation(); + entityRelation.setFrom(new AssetId(UUID.randomUUID())); + entityRelation.setTo(ORIGINATOR_ID); + entityRelation.setType(EntityRelation.CONTAINS_TYPE); + entityRelation.setTypeGroup(RelationTypeGroup.COMMON); + + when(relationService.findByToAndTypeAsync(TENANT_ID, ORIGINATOR_ID, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(List.of(entityRelation))); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + // WHEN + node.onMsg(ctx, EMPTY_POST_ATTRIBUTES_MSG); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE)); + verify(ctx, never()).tellFailure(any(), any()); + verify(relationService, never()).findByFromAndTypeAsync(any(), any(), anyString(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(EMPTY_POST_ATTRIBUTES_MSG); + } + + @Test + void givenCustomConfigDirectionTo_whenOnMsg_then_False() throws TbNodeException { + // GIVEN + var config = new TbCheckRelationNodeConfiguration().defaultConfiguration(); + config.setCheckForSingleEntity(false); + config.setDirection(EntitySearchDirection.TO.name()); + + when(relationService.findByToAndTypeAsync(TENANT_ID, ORIGINATOR_ID, config.getRelationType(), RelationTypeGroup.COMMON)).thenReturn(Futures.immediateFuture(Collections.emptyList())); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + // WHEN + node.onMsg(ctx, EMPTY_POST_ATTRIBUTES_MSG); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.FALSE)); + verify(ctx, never()).tellFailure(any(), any()); + verify(relationService, never()).findByFromAndTypeAsync(any(), any(), anyString(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(EMPTY_POST_ATTRIBUTES_MSG); + } + + @Test + void givenOldConfig_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception { + // GIVEN + var config = new TbCheckRelationNodeConfiguration().defaultConfiguration(); + config.setEntityType(ORIGINATOR_ID.getEntityType().name()); + config.setEntityId(ORIGINATOR_ID.getId().toString()); + String oldConfig = "{\"checkForSingleEntity\":true,\"direction\":\"TO\",\"entityType\":\"" + config.getEntityType() + "\",\"entityId\":\"" + config.getEntityId() + "\",\"relationType\":\"Contains\"}"; + JsonNode configJson = JacksonUtil.toJsonNode(oldConfig); + // WHEN + TbPair upgrade = node.upgrade(0, configJson); + // THEN + assertTrue(upgrade.getFirst()); + assertEquals(config, JacksonUtil.treeToValue(upgrade.getSecond(), config.getClass())); + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNodeTest.java index e7b3ccd7f1..a6f4a6e3cf 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNodeTest.java @@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.queue.TbMsgCallback; @@ -49,34 +50,30 @@ import static org.mockito.Mockito.when; class TbDeviceTypeSwitchNodeTest { - TenantId tenantId; - DeviceId deviceId; - DeviceId deviceIdDeleted; - DeviceProfile deviceProfile; - TbContext ctx; - TbDeviceTypeSwitchNode node; - EmptyNodeConfiguration config; - TbMsgCallback callback; - RuleEngineDeviceProfileCache deviceProfileCache; + private DeviceId deviceId; + private DeviceId deviceIdDeleted; + private TbContext ctx; + private TbDeviceTypeSwitchNode node; + private TbMsgCallback callback; @BeforeEach void setUp() throws TbNodeException { - tenantId = new TenantId(UUID.randomUUID()); + TenantId tenantId = new TenantId(UUID.randomUUID()); deviceId = new DeviceId(UUID.randomUUID()); deviceIdDeleted = new DeviceId(UUID.randomUUID()); - deviceProfile = new DeviceProfile(); + DeviceProfile deviceProfile = new DeviceProfile(); deviceProfile.setTenantId(tenantId); deviceProfile.setName("TestDeviceProfile"); //node - config = new EmptyNodeConfiguration(); + EmptyNodeConfiguration config = new EmptyNodeConfiguration(); node = new TbDeviceTypeSwitchNode(); node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); //init mock ctx = mock(TbContext.class); - deviceProfileCache = mock(RuleEngineDeviceProfileCache.class); + RuleEngineDeviceProfileCache deviceProfileCache = mock(RuleEngineDeviceProfileCache.class); callback = mock(TbMsgCallback.class); when(ctx.getTenantId()).thenReturn(tenantId); @@ -121,6 +118,6 @@ class TbDeviceTypeSwitchNodeTest { } private TbMsg getTbMsg(EntityId entityId) { - return TbMsg.newMsg("POST_ATTRIBUTES_REQUEST", entityId, new TbMsgMetaData(), "{}", callback); + return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT, callback); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java index 4cfe38f3fc..639c1a7b1a 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsFilterNodeTest.java @@ -29,6 +29,8 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.script.ScriptLanguage; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; @@ -51,25 +53,25 @@ public class TbJsFilterNodeTest { @Mock private ScriptEngine scriptEngine; - private RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); - private RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); + private final RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); + private final RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); @Test - public void falseEvaluationDoNotSendMsg() throws TbNodeException, ScriptException { + public void falseEvaluationDoNotSendMsg() throws TbNodeException { initWithScript(); - TbMsg msg = TbMsg.newMsg("USER", null, new TbMsgMetaData(), TbMsgDataType.JSON, "{}", ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, null, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); when(scriptEngine.executeFilterAsync(msg)).thenReturn(Futures.immediateFuture(false)); node.onMsg(ctx, msg); verify(ctx).getDbCallbackExecutor(); - verify(ctx).tellNext(msg, "False"); + verify(ctx).tellNext(msg, TbNodeConnectionType.FALSE); } @Test public void exceptionInJsThrowsException() throws TbNodeException { initWithScript(); TbMsgMetaData metaData = new TbMsgMetaData(); - TbMsg msg = TbMsg.newMsg("USER", null, metaData, TbMsgDataType.JSON, "{}", ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, null, metaData, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); when(scriptEngine.executeFilterAsync(msg)).thenReturn(Futures.immediateFailedFuture(new ScriptException("error"))); @@ -81,12 +83,12 @@ public class TbJsFilterNodeTest { public void metadataConditionCanBeTrue() throws TbNodeException { initWithScript(); TbMsgMetaData metaData = new TbMsgMetaData(); - TbMsg msg = TbMsg.newMsg("USER", null, metaData, TbMsgDataType.JSON, "{}", ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, null, metaData, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); when(scriptEngine.executeFilterAsync(msg)).thenReturn(Futures.immediateFuture(true)); node.onMsg(ctx, msg); verify(ctx).getDbCallbackExecutor(); - verify(ctx).tellNext(msg, "True"); + verify(ctx).tellNext(msg, TbNodeConnectionType.TRUE); } private void initWithScript() throws TbNodeException { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java index f4f852c6bd..3343c5fb92 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbJsSwitchNodeTest.java @@ -29,6 +29,7 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.script.ScriptLanguage; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; @@ -47,8 +48,8 @@ public class TbJsSwitchNodeTest { @Mock private ScriptEngine scriptEngine; - private RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); - private RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); + private final RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); + private final RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); @Test public void multipleRoutesAreAllowed() throws TbNodeException { @@ -58,7 +59,7 @@ public class TbJsSwitchNodeTest { metaData.putValue("humidity", "99"); String rawJson = "{\"name\": \"Vit\", \"passed\": 5}"; - TbMsg msg = TbMsg.newMsg( "USER", null, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, null, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); when(scriptEngine.executeSwitchAsync(msg)).thenReturn(Futures.immediateFuture(Sets.newHashSet("one", "three"))); node.onMsg(ctx, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeTest.java new file mode 100644 index 0000000000..4e2e5dc430 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNodeTest.java @@ -0,0 +1,103 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.filter; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.thingsboard.server.common.data.msg.TbMsgType.ATTRIBUTES_UPDATED; +import static org.thingsboard.server.common.data.msg.TbMsgType.POST_ATTRIBUTES_REQUEST; + +class TbMsgTypeFilterNodeTest { + + private DeviceId deviceId; + private TbContext ctx; + private TbMsgTypeFilterNode node; + + @BeforeEach + void setUp() throws TbNodeException { + ctx = mock(TbContext.class); + var config = new TbMsgTypeFilterNodeConfiguration().defaultConfiguration(); + deviceId = new DeviceId(UUID.randomUUID()); + node = new TbMsgTypeFilterNode(); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + } + + @AfterEach + void tearDown() { + node.destroy(); + } + + @Test + void givenPostAttributes_whenOnMsg_then_True() { + // GIVEN + TbMsg msg = getTbMsg(deviceId, POST_ATTRIBUTES_REQUEST); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(msg); + } + + @Test + void givenAttributesUpdated_whenOnMsg_then_False() { + // GIVEN + TbMsg msg = getTbMsg(deviceId, ATTRIBUTES_UPDATED); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.FALSE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(msg); + } + + private TbMsg getTbMsg(EntityId entityId, TbMsgType msgType) { + return TbMsg.newMsg(msgType, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNodeTest.java new file mode 100644 index 0000000000..603c23cd05 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNodeTest.java @@ -0,0 +1,95 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.filter; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +class TbMsgTypeSwitchNodeTest { + + private static final DeviceId DEVICE_ID = new DeviceId(UUID.randomUUID()); + + private TbMsgTypeSwitchNode node; + + private TbContext ctx; + + @BeforeEach + void setUp() { + ctx = mock(TbContext.class); + node = new TbMsgTypeSwitchNode(); + } + + @AfterEach + void tearDown() { + node.destroy(); + } + + @Test + void givenAllTypes_whenOnMsg_then_allTypesSupported() throws TbNodeException { + // GIVEN + List tbMsgList = new ArrayList<>(); + var tbMsgTypes = TbMsgType.values(); + for (var msgType : tbMsgTypes) { + tbMsgList.add(getTbMsg(msgType)); + } + + // WHEN + for (TbMsg tbMsg : tbMsgList) { + node.onMsg(ctx, tbMsg); + } + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + ArgumentCaptor nodeConnectionCapture = ArgumentCaptor.forClass(String.class); + verify(ctx, times(tbMsgList.size())).tellNext(newMsgCaptor.capture(), nodeConnectionCapture.capture()); + verify(ctx, never()).tellFailure(any(), any()); + var resultMsgs = newMsgCaptor.getAllValues(); + var resultNodeConnections = nodeConnectionCapture.getAllValues(); + for (int i = 0; i < resultMsgs.size(); i++) { + var msg = resultMsgs.get(i); + assertThat(msg).isNotNull(); + assertThat(msg.getType()).isNotNull(); + assertThat(msg.getType()).isEqualTo(msg.getInternalType().name()); + assertThat(msg).isSameAs(tbMsgList.get(i)); + assertThat(resultNodeConnections.get(i)) + .isEqualTo(msg.getInternalType().getRuleNodeConnection()); + } + } + + private TbMsg getTbMsg(TbMsgType msgType) { + return TbMsg.newMsg(msgType, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNodeTest.java new file mode 100644 index 0000000000..3ed566b7e6 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNodeTest.java @@ -0,0 +1,102 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.filter; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +class TbOriginatorTypeFilterNodeTest { + + private TbContext ctx; + private TbOriginatorTypeFilterNode node; + + @BeforeEach + void setUp() throws TbNodeException { + ctx = mock(TbContext.class); + var config = new TbOriginatorTypeFilterNodeConfiguration().defaultConfiguration(); + node = new TbOriginatorTypeFilterNode(); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + } + + @AfterEach + void tearDown() { + node.destroy(); + } + + @Test + void givenDevice_whenOnMsg_then_True() { + // GIVEN + DeviceId deviceId = new DeviceId(UUID.randomUUID()); + TbMsg msg = getTbMsg(deviceId); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(msg); + } + + @Test + void givenAsset_whenOnMsg_then_False() { + // GIVEN + AssetId assetId = new AssetId(UUID.randomUUID()); + TbMsg msg = getTbMsg(assetId); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.FALSE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(msg); + } + + private TbMsg getTbMsg(EntityId entityId) { + return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNodeTest.java new file mode 100644 index 0000000000..796a7106fc --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNodeTest.java @@ -0,0 +1,96 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.filter; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +class TbOriginatorTypeSwitchNodeTest { + + private static final UUID RANDOM_UUID = UUID.randomUUID(); + + private TbOriginatorTypeSwitchNode node; + + private TbContext ctx; + + @BeforeEach + void setUp() { + ctx = mock(TbContext.class); + node = new TbOriginatorTypeSwitchNode(); + } + + @AfterEach + void tearDown() { + node.destroy(); + } + + @Test + void givenAllTypes_whenOnMsg_then_allTypesSupported() throws TbNodeException { + // GIVEN + List tbMsgList = new ArrayList<>(); + var entityTypes = EntityType.values(); + for (var entityType : entityTypes) { + var entityId = EntityIdFactory.getByTypeAndUuid(entityType, RANDOM_UUID); + tbMsgList.add(getTbMsg(entityId)); + } + + // WHEN + for (TbMsg tbMsg : tbMsgList) { + node.onMsg(ctx, tbMsg); + } + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + ArgumentCaptor nodeConnectionCapture = ArgumentCaptor.forClass(String.class); + verify(ctx, times(tbMsgList.size())).tellNext(newMsgCaptor.capture(), nodeConnectionCapture.capture()); + verify(ctx, never()).tellFailure(any(), any()); + var resultMsgs = newMsgCaptor.getAllValues(); + var resultNodeConnections = nodeConnectionCapture.getAllValues(); + for (int i = 0; i < resultMsgs.size(); i++) { + var msg = resultMsgs.get(i); + assertThat(msg).isNotNull(); + assertThat(msg).isSameAs(tbMsgList.get(i)); + assertThat(resultNodeConnections.get(i)) + .isEqualTo(msg.getOriginator().getEntityType().getNormalName()); + } + } + + private TbMsg getTbMsg(EntityId entityId) { + return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/GeoUtilTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/GeoUtilTest.java new file mode 100644 index 0000000000..f53a876954 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/GeoUtilTest.java @@ -0,0 +1,163 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.geo; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class GeoUtilTest { + + public static final String SIMPLE_RECT = "[[51.903762928405555,23.642220786948297],[44.669801219635644,41.83345155830211]]"; + public static final String SIMPLE_RECT_WITH_HOLE_IN_CENTER = "[[[44.66980121963565,23.642220786948297],[44.66980121963565,41.83345155830211],[51.903762928405555,41.83345155830211],[51.903762928405555,23.642220786948297]],[[46.10464044504632,26.234282119122227],[50.8755868028522,26.25625220459488],[51.04164771375101,38.5595000692786],[45.99790855491869,38.75723083853248]]]"; + public static final String SAND_CLOCK = "[[47.45865912532852,25.531200822337155],[49.76760268310416,29.353995694578202],[51.42691963936519,25.355440138555966],[51.413219087617655,39.41629484105169],[49.78179072754938,33.37452133607305],[47.81395478511215,38.867042704235466]]"; + public static final String SAND_CLOCK_WITH_HOLE_IN_CENTER = "[[[51.426919639365195,25.355440138555966],[49.76760268310416,29.353995694578202],[47.45865912532852,25.531200822337155],[47.81395478511215,38.867042704235466],[49.78179072754938,33.37452133607305],[51.413219087617655,39.41629484105169]],[[49.8243299406579,30.210829028011513],[50.34591034217041,31.04569227597222],[49.56853374046142,32.53965808811239],[49.02409241675058,31.57297432731579]]]"; + public static final String SELF_INTERSECTING = "[[47.42893833699058,27.178954662008522],[51.71367987390804,37.46095466320854],[51.659197648757306,27.947907653551276],[47.41407351681856,37.46095466320854]]"; + public static final String SELF_INTERSECTING_WITH_HOLES = "[[[[47.42893833699058,27.17895466200852],[47.41407351681856,37.46095466320853],[49.63741575793274,32.47858934221699]],[[47.84342032696093,29.20045703244998],[49.124803688667576,32.38611942598416],[47.858163521459254,34.714948486085035]]],[[[51.659197648757306,27.947907653551276],[49.63741575793274,32.47858934221699],[51.71367987390804,37.46095466320853]],[[51.20718653775245,30.738363015535448],[51.317169097567344,34.58312797324915],[50.1351109330126,32.51793993882006]]]]"; + + public static final Coordinates POINT_INSIDE_SIMPLE_RECT_CENTER = new Coordinates(48.37082198780869, 32.673342414527355); + public static final Coordinates POINT_INSIDE_SIMPLE_RECT_NEAR_BORDER = new Coordinates(48.42916753187315,40.956064637716224); + public static final Coordinates POINT_OUTSIDE_SIMPLE_RECT = new Coordinates(52.94806646045028,32.91501335472649); + public static final Coordinates POINT_INSIDE_SAND_CLOCK_CENTER = new Coordinates(49.993588800145105,31.289062500000004); + public static final Coordinates POINT_INSIDE_SAND_CLOCK_NEAR_BORDER = new Coordinates(47.798651123976306,26.895045405470082); + public static final Coordinates POINT_OUTSIDE_SAND_CLOCK_1 = new Coordinates(49.553754212665936,28.03748985004787); + public static final Coordinates POINT_OUTSIDE_SAND_CLOCK_2 = new Coordinates(46.9802466961145,32.321728498980335); + public static final Coordinates POINT_INSIDE_SELF_INTERSECTING_UPPER_CENTER = new Coordinates(50.750366308834884,32.51952867922265); + public static final Coordinates POINT_INSIDE_SELF_INTERSECTING_LOWER_CENTER = new Coordinates(48.1371117277312,32.40967825185941); + public static final Coordinates POINT_INSIDE_SELF_INTERSECTING_NEAR_BORDER = new Coordinates(51.16552151942722,35.66125090181154); + public static final Coordinates POINT_OUTSIDE_SELF_INTERSECTING_1= new Coordinates(49.66777277299077,33.26651158529272); + public static final Coordinates POINT_OUTSIDE_SELF_INTERSECTING_2 = new Coordinates(47.10052840114779,32.16800731166027); + public static final Coordinates POINT_OUTSIDE_SELF_INTERSECTING_3 = new Coordinates(78.76578380252519,15.646485040786361); + + + @Test + public void testPointsInSimplePolygons() { + Assert.assertTrue("Polygon " + SIMPLE_RECT + " must contain the dot " + POINT_INSIDE_SIMPLE_RECT_CENTER, + GeoUtil.contains(SIMPLE_RECT, POINT_INSIDE_SIMPLE_RECT_CENTER) + ); + Assert.assertTrue("Polygon " + SIMPLE_RECT + " must contain the dot " + POINT_INSIDE_SIMPLE_RECT_NEAR_BORDER, + GeoUtil.contains(SIMPLE_RECT, POINT_INSIDE_SIMPLE_RECT_NEAR_BORDER) + ); + Assert.assertTrue("Polygon " + SIMPLE_RECT_WITH_HOLE_IN_CENTER + " must contain the dot " + + POINT_INSIDE_SIMPLE_RECT_NEAR_BORDER, + GeoUtil.contains(SIMPLE_RECT_WITH_HOLE_IN_CENTER, POINT_INSIDE_SIMPLE_RECT_NEAR_BORDER) + ); + + Assert.assertFalse("Polygon " + SIMPLE_RECT + " must not contain the dot " + + POINT_OUTSIDE_SIMPLE_RECT, + GeoUtil.contains(SIMPLE_RECT, POINT_OUTSIDE_SIMPLE_RECT) + ); + Assert.assertFalse("Polygon " + SIMPLE_RECT_WITH_HOLE_IN_CENTER + " must not contain the dot " + + POINT_OUTSIDE_SIMPLE_RECT, + GeoUtil.contains(SIMPLE_RECT_WITH_HOLE_IN_CENTER, POINT_OUTSIDE_SIMPLE_RECT) + ); + Assert.assertFalse("Polygon " + SIMPLE_RECT_WITH_HOLE_IN_CENTER + " must not contain the dot " + + POINT_INSIDE_SIMPLE_RECT_CENTER, + GeoUtil.contains(SIMPLE_RECT_WITH_HOLE_IN_CENTER, POINT_INSIDE_SIMPLE_RECT_CENTER) + ); + } + + @Test + public void testPointsInComplexPolygons() { + Assert.assertTrue("Polygon " + SAND_CLOCK + " must contain the dot " + POINT_INSIDE_SAND_CLOCK_CENTER, + GeoUtil.contains(SAND_CLOCK, POINT_INSIDE_SAND_CLOCK_CENTER) + ); + Assert.assertTrue("Polygon " + SAND_CLOCK + " must contain the dot " + POINT_INSIDE_SAND_CLOCK_NEAR_BORDER, + GeoUtil.contains(SAND_CLOCK, POINT_INSIDE_SAND_CLOCK_NEAR_BORDER) + ); + Assert.assertTrue("Polygon " + SAND_CLOCK_WITH_HOLE_IN_CENTER + " must contain the dot " + + POINT_INSIDE_SAND_CLOCK_NEAR_BORDER, + GeoUtil.contains(SAND_CLOCK_WITH_HOLE_IN_CENTER, POINT_INSIDE_SAND_CLOCK_NEAR_BORDER) + ); + + Assert.assertFalse("Polygon " + SAND_CLOCK + " must not contain the dot " + + POINT_OUTSIDE_SAND_CLOCK_1, + GeoUtil.contains(SAND_CLOCK, POINT_OUTSIDE_SAND_CLOCK_1) + ); + Assert.assertFalse("Polygon " + SAND_CLOCK + " must not contain the dot " + + POINT_OUTSIDE_SAND_CLOCK_2, + GeoUtil.contains(SAND_CLOCK, POINT_OUTSIDE_SAND_CLOCK_2) + ); + Assert.assertFalse("Polygon " + SAND_CLOCK_WITH_HOLE_IN_CENTER + " must not contain the dot " + + POINT_INSIDE_SAND_CLOCK_CENTER, + GeoUtil.contains(SAND_CLOCK_WITH_HOLE_IN_CENTER, POINT_INSIDE_SAND_CLOCK_CENTER) + ); + Assert.assertFalse("Polygon " + SAND_CLOCK_WITH_HOLE_IN_CENTER + " must not contain the dot " + + POINT_OUTSIDE_SAND_CLOCK_1, + GeoUtil.contains(SAND_CLOCK_WITH_HOLE_IN_CENTER, POINT_OUTSIDE_SAND_CLOCK_1) + ); + Assert.assertFalse("Polygon " + SAND_CLOCK_WITH_HOLE_IN_CENTER + " must not contain the dot " + + POINT_OUTSIDE_SAND_CLOCK_2, + GeoUtil.contains(SAND_CLOCK_WITH_HOLE_IN_CENTER, POINT_OUTSIDE_SAND_CLOCK_2) + ); + } + + @Test + public void testPointsInSelfIntersectingPolygons() { + Assert.assertTrue("Polygon " + SELF_INTERSECTING + " must contain the dot " + + POINT_INSIDE_SELF_INTERSECTING_UPPER_CENTER, + GeoUtil.contains(SELF_INTERSECTING, POINT_INSIDE_SELF_INTERSECTING_UPPER_CENTER) + ); + Assert.assertTrue("Polygon " + SELF_INTERSECTING + " must contain the dot " + + POINT_INSIDE_SELF_INTERSECTING_LOWER_CENTER, + GeoUtil.contains(SELF_INTERSECTING, POINT_INSIDE_SELF_INTERSECTING_LOWER_CENTER) + ); + Assert.assertTrue("Polygon " + SELF_INTERSECTING + " must contain the dot " + + POINT_INSIDE_SELF_INTERSECTING_NEAR_BORDER, + GeoUtil.contains(SELF_INTERSECTING, POINT_INSIDE_SELF_INTERSECTING_NEAR_BORDER) + ); + Assert.assertTrue("Polygon " + SELF_INTERSECTING_WITH_HOLES + " must contain the dot " + + POINT_INSIDE_SAND_CLOCK_NEAR_BORDER, + GeoUtil.contains(SELF_INTERSECTING_WITH_HOLES, POINT_INSIDE_SELF_INTERSECTING_NEAR_BORDER) + ); + + Assert.assertFalse("Polygon " + SELF_INTERSECTING + " must not contain the dot " + + POINT_OUTSIDE_SELF_INTERSECTING_1, + GeoUtil.contains(SELF_INTERSECTING, POINT_OUTSIDE_SELF_INTERSECTING_1) + ); + Assert.assertFalse("Polygon " + SELF_INTERSECTING + " must not contain the dot " + + POINT_OUTSIDE_SELF_INTERSECTING_2, + GeoUtil.contains(SELF_INTERSECTING, POINT_OUTSIDE_SELF_INTERSECTING_2) + ); + Assert.assertFalse("Polygon " + SELF_INTERSECTING + " must not contain the dot " + + POINT_OUTSIDE_SELF_INTERSECTING_3, + GeoUtil.contains(SELF_INTERSECTING, POINT_OUTSIDE_SELF_INTERSECTING_3) + ); + Assert.assertFalse("Polygon " + SELF_INTERSECTING_WITH_HOLES + " must not contain the dot " + + POINT_OUTSIDE_SELF_INTERSECTING_1, + GeoUtil.contains(SELF_INTERSECTING_WITH_HOLES, POINT_OUTSIDE_SELF_INTERSECTING_1) + ); + Assert.assertFalse("Polygon " + SELF_INTERSECTING_WITH_HOLES + " must not contain the dot " + + POINT_OUTSIDE_SELF_INTERSECTING_2, + GeoUtil.contains(SELF_INTERSECTING_WITH_HOLES, POINT_OUTSIDE_SELF_INTERSECTING_2) + ); + Assert.assertFalse("Polygon " + SELF_INTERSECTING_WITH_HOLES + " must not contain the dot " + + POINT_OUTSIDE_SELF_INTERSECTING_3, + GeoUtil.contains(SELF_INTERSECTING_WITH_HOLES, POINT_OUTSIDE_SELF_INTERSECTING_3) + ); + Assert.assertFalse("Polygon " + SELF_INTERSECTING_WITH_HOLES + " must not contain the dot " + + POINT_INSIDE_SELF_INTERSECTING_UPPER_CENTER, + GeoUtil.contains(SELF_INTERSECTING_WITH_HOLES, POINT_INSIDE_SELF_INTERSECTING_UPPER_CENTER) + ); + Assert.assertFalse("Polygon " + SELF_INTERSECTING_WITH_HOLES + " must not contain the dot " + + POINT_INSIDE_SELF_INTERSECTING_LOWER_CENTER, + GeoUtil.contains(SELF_INTERSECTING_WITH_HOLES, POINT_INSIDE_SELF_INTERSECTING_LOWER_CENTER) + ); + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGeoUtilTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGeoUtilTest.java deleted file mode 100644 index 6a03a8f362..0000000000 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGeoUtilTest.java +++ /dev/null @@ -1,163 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.rule.engine.geo; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; - -@RunWith(MockitoJUnitRunner.class) -public class TbGeoUtilTest { - - public static final String SIMPLE_RECT = "[[51.903762928405555,23.642220786948297],[44.669801219635644,41.83345155830211]]"; - public static final String SIMPLE_RECT_WITH_HOLE_IN_CENTER = "[[[44.66980121963565,23.642220786948297],[44.66980121963565,41.83345155830211],[51.903762928405555,41.83345155830211],[51.903762928405555,23.642220786948297]],[[46.10464044504632,26.234282119122227],[50.8755868028522,26.25625220459488],[51.04164771375101,38.5595000692786],[45.99790855491869,38.75723083853248]]]"; - public static final String SAND_CLOCK = "[[47.45865912532852,25.531200822337155],[49.76760268310416,29.353995694578202],[51.42691963936519,25.355440138555966],[51.413219087617655,39.41629484105169],[49.78179072754938,33.37452133607305],[47.81395478511215,38.867042704235466]]"; - public static final String SAND_CLOCK_WITH_HOLE_IN_CENTER = "[[[51.426919639365195,25.355440138555966],[49.76760268310416,29.353995694578202],[47.45865912532852,25.531200822337155],[47.81395478511215,38.867042704235466],[49.78179072754938,33.37452133607305],[51.413219087617655,39.41629484105169]],[[49.8243299406579,30.210829028011513],[50.34591034217041,31.04569227597222],[49.56853374046142,32.53965808811239],[49.02409241675058,31.57297432731579]]]"; - public static final String SELF_INTERSECTING = "[[47.42893833699058,27.178954662008522],[51.71367987390804,37.46095466320854],[51.659197648757306,27.947907653551276],[47.41407351681856,37.46095466320854]]"; - public static final String SELF_INTERSECTING_WITH_HOLES = "[[[[47.42893833699058,27.17895466200852],[47.41407351681856,37.46095466320853],[49.63741575793274,32.47858934221699]],[[47.84342032696093,29.20045703244998],[49.124803688667576,32.38611942598416],[47.858163521459254,34.714948486085035]]],[[[51.659197648757306,27.947907653551276],[49.63741575793274,32.47858934221699],[51.71367987390804,37.46095466320853]],[[51.20718653775245,30.738363015535448],[51.317169097567344,34.58312797324915],[50.1351109330126,32.51793993882006]]]]"; - - public static final Coordinates POINT_INSIDE_SIMPLE_RECT_CENTER = new Coordinates(48.37082198780869, 32.673342414527355); - public static final Coordinates POINT_INSIDE_SIMPLE_RECT_NEAR_BORDER = new Coordinates(48.42916753187315,40.956064637716224); - public static final Coordinates POINT_OUTSIDE_SIMPLE_RECT = new Coordinates(52.94806646045028,32.91501335472649); - public static final Coordinates POINT_INSIDE_SAND_CLOCK_CENTER = new Coordinates(49.993588800145105,31.289062500000004); - public static final Coordinates POINT_INSIDE_SAND_CLOCK_NEAR_BORDER = new Coordinates(47.798651123976306,26.895045405470082); - public static final Coordinates POINT_OUTSIDE_SAND_CLOCK_1 = new Coordinates(49.553754212665936,28.03748985004787); - public static final Coordinates POINT_OUTSIDE_SAND_CLOCK_2 = new Coordinates(46.9802466961145,32.321728498980335); - public static final Coordinates POINT_INSIDE_SELF_INTERSECTING_UPPER_CENTER = new Coordinates(50.750366308834884,32.51952867922265); - public static final Coordinates POINT_INSIDE_SELF_INTERSECTING_LOWER_CENTER = new Coordinates(48.1371117277312,32.40967825185941); - public static final Coordinates POINT_INSIDE_SELF_INTERSECTING_NEAR_BORDER = new Coordinates(51.16552151942722,35.66125090181154); - public static final Coordinates POINT_OUTSIDE_SELF_INTERSECTING_1= new Coordinates(49.66777277299077,33.26651158529272); - public static final Coordinates POINT_OUTSIDE_SELF_INTERSECTING_2 = new Coordinates(47.10052840114779,32.16800731166027); - public static final Coordinates POINT_OUTSIDE_SELF_INTERSECTING_3 = new Coordinates(78.76578380252519,15.646485040786361); - - - @Test - public void testPointsInSimplePolygons() { - Assert.assertTrue("Polygon " + SIMPLE_RECT + " must contain the dot " + POINT_INSIDE_SIMPLE_RECT_CENTER, - GeoUtil.contains(SIMPLE_RECT, POINT_INSIDE_SIMPLE_RECT_CENTER) - ); - Assert.assertTrue("Polygon " + SIMPLE_RECT + " must contain the dot " + POINT_INSIDE_SIMPLE_RECT_NEAR_BORDER, - GeoUtil.contains(SIMPLE_RECT, POINT_INSIDE_SIMPLE_RECT_NEAR_BORDER) - ); - Assert.assertTrue("Polygon " + SIMPLE_RECT_WITH_HOLE_IN_CENTER + " must contain the dot " - + POINT_INSIDE_SIMPLE_RECT_NEAR_BORDER, - GeoUtil.contains(SIMPLE_RECT_WITH_HOLE_IN_CENTER, POINT_INSIDE_SIMPLE_RECT_NEAR_BORDER) - ); - - Assert.assertFalse("Polygon " + SIMPLE_RECT + " must not contain the dot " - + POINT_OUTSIDE_SIMPLE_RECT, - GeoUtil.contains(SIMPLE_RECT, POINT_OUTSIDE_SIMPLE_RECT) - ); - Assert.assertFalse("Polygon " + SIMPLE_RECT_WITH_HOLE_IN_CENTER + " must not contain the dot " - + POINT_OUTSIDE_SIMPLE_RECT, - GeoUtil.contains(SIMPLE_RECT_WITH_HOLE_IN_CENTER, POINT_OUTSIDE_SIMPLE_RECT) - ); - Assert.assertFalse("Polygon " + SIMPLE_RECT_WITH_HOLE_IN_CENTER + " must not contain the dot " - + POINT_INSIDE_SIMPLE_RECT_CENTER, - GeoUtil.contains(SIMPLE_RECT_WITH_HOLE_IN_CENTER, POINT_INSIDE_SIMPLE_RECT_CENTER) - ); - } - - @Test - public void testPointsInComplexPolygons() { - Assert.assertTrue("Polygon " + SAND_CLOCK + " must contain the dot " + POINT_INSIDE_SAND_CLOCK_CENTER, - GeoUtil.contains(SAND_CLOCK, POINT_INSIDE_SAND_CLOCK_CENTER) - ); - Assert.assertTrue("Polygon " + SAND_CLOCK + " must contain the dot " + POINT_INSIDE_SAND_CLOCK_NEAR_BORDER, - GeoUtil.contains(SAND_CLOCK, POINT_INSIDE_SAND_CLOCK_NEAR_BORDER) - ); - Assert.assertTrue("Polygon " + SAND_CLOCK_WITH_HOLE_IN_CENTER + " must contain the dot " - + POINT_INSIDE_SAND_CLOCK_NEAR_BORDER, - GeoUtil.contains(SAND_CLOCK_WITH_HOLE_IN_CENTER, POINT_INSIDE_SAND_CLOCK_NEAR_BORDER) - ); - - Assert.assertFalse("Polygon " + SAND_CLOCK + " must not contain the dot " - + POINT_OUTSIDE_SAND_CLOCK_1, - GeoUtil.contains(SAND_CLOCK, POINT_OUTSIDE_SAND_CLOCK_1) - ); - Assert.assertFalse("Polygon " + SAND_CLOCK + " must not contain the dot " - + POINT_OUTSIDE_SAND_CLOCK_2, - GeoUtil.contains(SAND_CLOCK, POINT_OUTSIDE_SAND_CLOCK_2) - ); - Assert.assertFalse("Polygon " + SAND_CLOCK_WITH_HOLE_IN_CENTER + " must not contain the dot " - + POINT_INSIDE_SAND_CLOCK_CENTER, - GeoUtil.contains(SAND_CLOCK_WITH_HOLE_IN_CENTER, POINT_INSIDE_SAND_CLOCK_CENTER) - ); - Assert.assertFalse("Polygon " + SAND_CLOCK_WITH_HOLE_IN_CENTER + " must not contain the dot " - + POINT_OUTSIDE_SAND_CLOCK_1, - GeoUtil.contains(SAND_CLOCK_WITH_HOLE_IN_CENTER, POINT_OUTSIDE_SAND_CLOCK_1) - ); - Assert.assertFalse("Polygon " + SAND_CLOCK_WITH_HOLE_IN_CENTER + " must not contain the dot " - + POINT_OUTSIDE_SAND_CLOCK_2, - GeoUtil.contains(SAND_CLOCK_WITH_HOLE_IN_CENTER, POINT_OUTSIDE_SAND_CLOCK_2) - ); - } - - @Test - public void testPointsInSelfIntersectingPolygons() { - Assert.assertTrue("Polygon " + SELF_INTERSECTING + " must contain the dot " - + POINT_INSIDE_SELF_INTERSECTING_UPPER_CENTER, - GeoUtil.contains(SELF_INTERSECTING, POINT_INSIDE_SELF_INTERSECTING_UPPER_CENTER) - ); - Assert.assertTrue("Polygon " + SELF_INTERSECTING + " must contain the dot " - + POINT_INSIDE_SELF_INTERSECTING_LOWER_CENTER, - GeoUtil.contains(SELF_INTERSECTING, POINT_INSIDE_SELF_INTERSECTING_LOWER_CENTER) - ); - Assert.assertTrue("Polygon " + SELF_INTERSECTING + " must contain the dot " - + POINT_INSIDE_SELF_INTERSECTING_NEAR_BORDER, - GeoUtil.contains(SELF_INTERSECTING, POINT_INSIDE_SELF_INTERSECTING_NEAR_BORDER) - ); - Assert.assertTrue("Polygon " + SELF_INTERSECTING_WITH_HOLES + " must contain the dot " - + POINT_INSIDE_SAND_CLOCK_NEAR_BORDER, - GeoUtil.contains(SELF_INTERSECTING_WITH_HOLES, POINT_INSIDE_SELF_INTERSECTING_NEAR_BORDER) - ); - - Assert.assertFalse("Polygon " + SELF_INTERSECTING + " must not contain the dot " - + POINT_OUTSIDE_SELF_INTERSECTING_1, - GeoUtil.contains(SELF_INTERSECTING, POINT_OUTSIDE_SELF_INTERSECTING_1) - ); - Assert.assertFalse("Polygon " + SELF_INTERSECTING + " must not contain the dot " - + POINT_OUTSIDE_SELF_INTERSECTING_2, - GeoUtil.contains(SELF_INTERSECTING, POINT_OUTSIDE_SELF_INTERSECTING_2) - ); - Assert.assertFalse("Polygon " + SELF_INTERSECTING + " must not contain the dot " - + POINT_OUTSIDE_SELF_INTERSECTING_3, - GeoUtil.contains(SELF_INTERSECTING, POINT_OUTSIDE_SELF_INTERSECTING_3) - ); - Assert.assertFalse("Polygon " + SELF_INTERSECTING_WITH_HOLES + " must not contain the dot " - + POINT_OUTSIDE_SELF_INTERSECTING_1, - GeoUtil.contains(SELF_INTERSECTING_WITH_HOLES, POINT_OUTSIDE_SELF_INTERSECTING_1) - ); - Assert.assertFalse("Polygon " + SELF_INTERSECTING_WITH_HOLES + " must not contain the dot " - + POINT_OUTSIDE_SELF_INTERSECTING_2, - GeoUtil.contains(SELF_INTERSECTING_WITH_HOLES, POINT_OUTSIDE_SELF_INTERSECTING_2) - ); - Assert.assertFalse("Polygon " + SELF_INTERSECTING_WITH_HOLES + " must not contain the dot " - + POINT_OUTSIDE_SELF_INTERSECTING_3, - GeoUtil.contains(SELF_INTERSECTING_WITH_HOLES, POINT_OUTSIDE_SELF_INTERSECTING_3) - ); - Assert.assertFalse("Polygon " + SELF_INTERSECTING_WITH_HOLES + " must not contain the dot " - + POINT_INSIDE_SELF_INTERSECTING_UPPER_CENTER, - GeoUtil.contains(SELF_INTERSECTING_WITH_HOLES, POINT_INSIDE_SELF_INTERSECTING_UPPER_CENTER) - ); - Assert.assertFalse("Polygon " + SELF_INTERSECTING_WITH_HOLES + " must not contain the dot " - + POINT_INSIDE_SELF_INTERSECTING_LOWER_CENTER, - GeoUtil.contains(SELF_INTERSECTING_WITH_HOLES, POINT_INSIDE_SELF_INTERSECTING_LOWER_CENTER) - ); - } - -} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeTest.java new file mode 100644 index 0000000000..f4585d0f96 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNodeTest.java @@ -0,0 +1,458 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.geo; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +class TbGpsGeofencingFilterNodeTest { + + private static final double CIRCLE_RANGE = 1.0; + private static final Coordinates CIRCLE_CENTER = new Coordinates(49.0384, 31.4513); + private static final Coordinates POINT_INSIDE_CIRCLE = new Coordinates(49.0354, 31.4513); // distance from center: 0.334 km + private static final Coordinates POINT_OUTSIDE_CIRCLE = new Coordinates(49.0284, 31.4513); // distance from center: 1.112 km + + private TbContext ctx; + private TbGpsGeofencingFilterNode node; + + @BeforeEach + void setUp() { + ctx = mock(TbContext.class); + node = new TbGpsGeofencingFilterNode(); + } + + @AfterEach + void tearDown() { + node.destroy(); + } + + // Exception tests + + @Test + void givenDefaultConfig_whenOnMsg_thenExceptionInvalidMsg() throws TbNodeException { + // GIVEN + var config = new TbGpsGeofencingFilterNodeConfiguration().defaultConfiguration(); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + DeviceId deviceId = new DeviceId(UUID.randomUUID()); + TbMsg msg = getEmptyArrayTbMsg(deviceId); + + // WHEN + var exception = assertThrows(TbNodeException.class, () -> node.onMsg(ctx, msg)); + + // THEN + assertThat(exception.getMessage()).isEqualTo("Incoming Message is not a valid JSON object!"); + } + + @Test + void givenDefaultConfig_whenOnMsg_thenExceptionMissingPerimeterDefinitionNewVersion() throws TbNodeException { + // GIVEN + var config = new TbGpsGeofencingFilterNodeConfiguration().defaultConfiguration(); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + DeviceId deviceId = new DeviceId(UUID.randomUUID()); + TbMsg msg = getTbMsg(deviceId, TbMsgMetaData.EMPTY, + GeoUtilTest.POINT_INSIDE_SIMPLE_RECT_CENTER.getLatitude(), GeoUtilTest.POINT_INSIDE_SIMPLE_RECT_CENTER.getLongitude()); + + // WHEN + var exception = assertThrows(TbNodeException.class, () -> node.onMsg(ctx, msg)); + + // THEN + assertThat(exception.getMessage()).isEqualTo("Missing perimeter definition!"); + } + + @Test + void givenTypePolygonAndConfigWithoutPerimeterKeyName_whenOnMsg_thenExceptionMissingPerimeterDefinitionOldVersion() throws TbNodeException { + // GIVEN + var config = new TbGpsGeofencingFilterNodeConfiguration().defaultConfiguration(); + config.setPerimeterKeyName(null); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + DeviceId deviceId = new DeviceId(UUID.randomUUID()); + TbMsg msg = getTbMsg(deviceId, TbMsgMetaData.EMPTY, + GeoUtilTest.POINT_INSIDE_SIMPLE_RECT_CENTER.getLatitude(), GeoUtilTest.POINT_INSIDE_SIMPLE_RECT_CENTER.getLongitude()); + + // WHEN + var exception = assertThrows(TbNodeException.class, () -> node.onMsg(ctx, msg)); + + // THEN + assertThat(exception.getMessage()).isEqualTo("Missing perimeter definition!"); + } + + // Polygon tests + + @Test + void givenTypePolygonAndConfigWithoutPerimeterKeyName_whenOnMsg_thenTrue() throws TbNodeException { + // GIVEN + var config = new TbGpsGeofencingFilterNodeConfiguration().defaultConfiguration(); + config.setPerimeterKeyName(null); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + DeviceId deviceId = new DeviceId(UUID.randomUUID()); + TbMsgMetaData metadata = getMetadataForOldVersionPolygonPerimeter(); + TbMsg msg = getTbMsg(deviceId, metadata, + GeoUtilTest.POINT_INSIDE_SIMPLE_RECT_CENTER.getLatitude(), GeoUtilTest.POINT_INSIDE_SIMPLE_RECT_CENTER.getLongitude()); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(msg); + } + + @Test + void givenTypePolygonAndConfigWithoutPerimeterKeyName_whenOnMsg_thenFalse() throws TbNodeException { + // GIVEN + var config = new TbGpsGeofencingFilterNodeConfiguration().defaultConfiguration(); + config.setPerimeterKeyName(null); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + DeviceId deviceId = new DeviceId(UUID.randomUUID()); + TbMsgMetaData metadata = getMetadataForOldVersionPolygonPerimeter(); + TbMsg msg = getTbMsg(deviceId, metadata, + GeoUtilTest.POINT_OUTSIDE_SIMPLE_RECT.getLatitude(), GeoUtilTest.POINT_OUTSIDE_SIMPLE_RECT.getLongitude()); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.FALSE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(msg); + } + + @Test + void givenDefaultConfig_whenOnMsg_thenTrue() throws TbNodeException { + // GIVEN + var config = new TbGpsGeofencingFilterNodeConfiguration().defaultConfiguration(); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + DeviceId deviceId = new DeviceId(UUID.randomUUID()); + TbMsgMetaData metadata = getMetadataForNewVersionPolygonPerimeter(); + TbMsg msg = getTbMsg(deviceId, metadata, + GeoUtilTest.POINT_INSIDE_SIMPLE_RECT_CENTER.getLatitude(), GeoUtilTest.POINT_INSIDE_SIMPLE_RECT_CENTER.getLongitude()); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(msg); + } + + @Test + void givenDefaultConfig_whenOnMsg_thenFalse() throws TbNodeException { + // GIVEN + var config = new TbGpsGeofencingFilterNodeConfiguration().defaultConfiguration(); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + DeviceId deviceId = new DeviceId(UUID.randomUUID()); + TbMsgMetaData metadata = getMetadataForNewVersionPolygonPerimeter(); + TbMsg msg = getTbMsg(deviceId, metadata, + GeoUtilTest.POINT_OUTSIDE_SIMPLE_RECT.getLatitude(), GeoUtilTest.POINT_OUTSIDE_SIMPLE_RECT.getLongitude()); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.FALSE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(msg); + } + + @Test + void givenTypePolygonAndConfigWithPolygonDefined_whenOnMsg_thenTrue() throws TbNodeException { + // GIVEN + var config = new TbGpsGeofencingFilterNodeConfiguration().defaultConfiguration(); + config.setFetchPerimeterInfoFromMessageMetadata(false); + config.setPolygonsDefinition(GeoUtilTest.SIMPLE_RECT); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + DeviceId deviceId = new DeviceId(UUID.randomUUID()); + TbMsg msg = getTbMsg(deviceId, TbMsgMetaData.EMPTY, + GeoUtilTest.POINT_INSIDE_SIMPLE_RECT_CENTER.getLatitude(), GeoUtilTest.POINT_INSIDE_SIMPLE_RECT_CENTER.getLongitude()); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(msg); + } + + @Test + void givenTypePolygonAndConfigWithPolygonDefined_whenOnMsg_thenFalse() throws TbNodeException { + // GIVEN + var config = new TbGpsGeofencingFilterNodeConfiguration().defaultConfiguration(); + config.setFetchPerimeterInfoFromMessageMetadata(false); + config.setPolygonsDefinition(GeoUtilTest.SIMPLE_RECT); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + DeviceId deviceId = new DeviceId(UUID.randomUUID()); + TbMsg msg = getTbMsg(deviceId, TbMsgMetaData.EMPTY, + GeoUtilTest.POINT_OUTSIDE_SIMPLE_RECT.getLatitude(), GeoUtilTest.POINT_OUTSIDE_SIMPLE_RECT.getLongitude()); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.FALSE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(msg); + } + + private TbMsgMetaData getMetadataForOldVersionPolygonPerimeter() { + var metadata = new TbMsgMetaData(); + metadata.putValue("perimeter", GeoUtilTest.SIMPLE_RECT); + return metadata; + } + + private TbMsgMetaData getMetadataForNewVersionPolygonPerimeter() { + var metadata = new TbMsgMetaData(); + metadata.putValue("ss_perimeter", GeoUtilTest.SIMPLE_RECT); + return metadata; + } + + // Circle tests + + @Test + void givenTypeCircleAndConfigWithoutPerimeterKeyName_whenOnMsg_thenTrue() throws TbNodeException { + // GIVEN + var config = new TbGpsGeofencingFilterNodeConfiguration().defaultConfiguration(); + config.setPerimeterKeyName(null); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + DeviceId deviceId = new DeviceId(UUID.randomUUID()); + TbMsgMetaData metadata = getMetadataForOldVersionCirclePerimeter(); + TbMsg msg = getTbMsg(deviceId, metadata, + POINT_INSIDE_CIRCLE.getLatitude(), POINT_INSIDE_CIRCLE.getLongitude()); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(msg); + } + + @Test + void givenTypeCircleAndConfigWithoutPerimeterKeyName_whenOnMsg_thenFalse() throws TbNodeException { + // GIVEN + var config = new TbGpsGeofencingFilterNodeConfiguration().defaultConfiguration(); + config.setPerimeterKeyName(null); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + DeviceId deviceId = new DeviceId(UUID.randomUUID()); + TbMsgMetaData metadata = getMetadataForOldVersionCirclePerimeter(); + TbMsg msg = getTbMsg(deviceId, metadata, + POINT_OUTSIDE_CIRCLE.getLatitude(), POINT_OUTSIDE_CIRCLE.getLongitude()); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.FALSE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(msg); + } + + @Test + void givenTypeCircle_whenOnMsg_thenTrue() throws TbNodeException { + // GIVEN + var config = new TbGpsGeofencingFilterNodeConfiguration().defaultConfiguration(); + config.setPerimeterType(PerimeterType.CIRCLE); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + DeviceId deviceId = new DeviceId(UUID.randomUUID()); + TbMsgMetaData metadata = getMetadataForNewVersionCirclePerimeter(); + TbMsg msg = getTbMsg(deviceId, metadata, + POINT_INSIDE_CIRCLE.getLatitude(), POINT_INSIDE_CIRCLE.getLongitude()); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(msg); + } + + @Test + void givenTypeCircle_whenOnMsg_thenFalse() throws TbNodeException { + // GIVEN + var config = new TbGpsGeofencingFilterNodeConfiguration().defaultConfiguration(); + config.setPerimeterType(PerimeterType.CIRCLE); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + DeviceId deviceId = new DeviceId(UUID.randomUUID()); + TbMsgMetaData metadata = getMetadataForNewVersionCirclePerimeter(); + TbMsg msg = getTbMsg(deviceId, metadata, + POINT_OUTSIDE_CIRCLE.getLatitude(), POINT_OUTSIDE_CIRCLE.getLongitude()); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.FALSE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(msg); + } + + @Test + void givenTypeCircleAndConfigWithCircleDefined_whenOnMsg_thenTrue() throws TbNodeException { + // GIVEN + var config = new TbGpsGeofencingFilterNodeConfiguration().defaultConfiguration(); + config.setFetchPerimeterInfoFromMessageMetadata(false); + config.setPerimeterType(PerimeterType.CIRCLE); + config.setCenterLatitude(CIRCLE_CENTER.getLatitude()); + config.setCenterLongitude(CIRCLE_CENTER.getLongitude()); + config.setRange(CIRCLE_RANGE); + config.setRangeUnit(RangeUnit.KILOMETER); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + DeviceId deviceId = new DeviceId(UUID.randomUUID()); + TbMsg msg = getTbMsg(deviceId, TbMsgMetaData.EMPTY, + POINT_INSIDE_CIRCLE.getLatitude(), POINT_INSIDE_CIRCLE.getLongitude()); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.TRUE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(msg); + } + + @Test + void givenTypeCircleAndConfigWithCircleDefined_whenOnMsg_thenFalse() throws TbNodeException { + // GIVEN + var config = new TbGpsGeofencingFilterNodeConfiguration().defaultConfiguration(); + config.setFetchPerimeterInfoFromMessageMetadata(false); + config.setPerimeterType(PerimeterType.CIRCLE); + config.setCenterLatitude(CIRCLE_CENTER.getLatitude()); + config.setCenterLongitude(CIRCLE_CENTER.getLongitude()); + config.setRange(CIRCLE_RANGE); + config.setRangeUnit(RangeUnit.KILOMETER); + node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + DeviceId deviceId = new DeviceId(UUID.randomUUID()); + TbMsg msg = getTbMsg(deviceId, TbMsgMetaData.EMPTY, + POINT_OUTSIDE_CIRCLE.getLatitude(), POINT_OUTSIDE_CIRCLE.getLongitude()); + + // WHEN + node.onMsg(ctx, msg); + + // THEN + ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, times(1)).tellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.FALSE)); + verify(ctx, never()).tellFailure(any(), any()); + TbMsg newMsg = newMsgCaptor.getValue(); + assertThat(newMsg).isNotNull(); + assertThat(newMsg).isSameAs(msg); + } + + private TbMsgMetaData getMetadataForOldVersionCirclePerimeter() { + var metadata = new TbMsgMetaData(); + metadata.putValue("centerLatitude", String.valueOf(CIRCLE_CENTER.getLatitude())); + metadata.putValue("centerLongitude", String.valueOf(CIRCLE_CENTER.getLongitude())); + metadata.putValue("range", String.valueOf(CIRCLE_RANGE)); + metadata.putValue("rangeUnit", String.valueOf(RangeUnit.KILOMETER)); + return metadata; + } + + private TbMsgMetaData getMetadataForNewVersionCirclePerimeter() { + ObjectNode perimeter = JacksonUtil.newObjectNode(); + perimeter.put("latitude", CIRCLE_CENTER.getLatitude()); + perimeter.put("longitude", CIRCLE_CENTER.getLongitude()); + perimeter.put("radius", CIRCLE_RANGE); + perimeter.put("radiusUnit", String.valueOf(RangeUnit.KILOMETER)); + var metadata = new TbMsgMetaData(); + metadata.putValue("ss_perimeter", JacksonUtil.toString(perimeter)); + return metadata; + } + + private TbMsg getTbMsg(EntityId entityId, TbMsgMetaData metadata, double latitude, double longitude) { + String data = "{\"latitude\": " + latitude + ", \"longitude\": " + longitude + "}"; + return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, metadata, data); + } + + private TbMsg getEmptyArrayTbMsg(EntityId entityId) { + return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java index c95183717f..9d732301f8 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNodeTest.java @@ -15,12 +15,15 @@ */ package org.thingsboard.rule.engine.mail; -import com.datastax.oss.driver.api.core.uuid.Uuids; -import org.junit.Test; -import org.junit.runner.RunWith; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbEmail; @@ -28,84 +31,134 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.RuleChainId; -import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; -import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; -import java.io.IOException; +import java.util.UUID; +import java.util.stream.Stream; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotSame; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -@RunWith(MockitoJUnitRunner.class) public class TbMsgToEmailNodeTest { - private TbMsgToEmailNode emailNode; + private static final int EXPECTED_TEMPERATURE = 30; + private static final String EXPECTED_DEVICE_NAME = "TH-001"; + private static final String EXPECTED_DEVICE_TYPE = "thermostat"; + private static final String EXPECTED_SUBJECT = "Device " + EXPECTED_DEVICE_TYPE + " temperature high"; + private static final String EXPECTED_BODY = "Device " + EXPECTED_DEVICE_NAME + " has high temperature " + EXPECTED_TEMPERATURE; + private static final String EXPECTED_TO_EMAIL = "user@email.io"; + private static final String DYNAMIC_MAIL_BODY_TYPE = "dynamic"; + + private EntityId originator; + private TbMsgToEmailNode node; + private TbMsgToEmailNodeConfiguration config; + + private TbContext ctxMock; + + @BeforeEach + void setUp() throws TbNodeException { + ctxMock = mock(TbContext.class); + originator = new DeviceId(UUID.randomUUID()); + config = new TbMsgToEmailNodeConfiguration().defaultConfiguration(); + node = new TbMsgToEmailNode(); + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + } + + @AfterEach + void tearDown() { + node.destroy(); + } - @Mock - private TbContext ctx; + @Test + void givenDefaultConfig_whenVerify_thenOK() { + // GIVEN-WHEN-THEN + assertThat(config.getFromTemplate()).isEqualTo("info@testmail.org"); + assertThat(config.getToTemplate()).isEqualTo("${userEmail}"); + assertThat(config.getSubjectTemplate()).isEqualTo("Device ${deviceType} temperature high"); + assertThat(config.getBodyTemplate()).isEqualTo("Device ${deviceName} has high temperature $[temperature]"); + } - private EntityId originator = new DeviceId(Uuids.timeBased()); - private TbMsgMetaData metaData = new TbMsgMetaData(); - private String rawJson = "{\"name\": \"temp\", \"passed\": 5 , \"complex\": {\"val\":12, \"count\":100}}"; + @ParameterizedTest + @MethodSource("MailBodyTypeTestConfig") + public void givenMailBodyTypeTestConfig_whenOnMsg_thenVerify(MailBodyTypeTestConfig testConfig) throws TbNodeException { + // GIVEN + String mailBodyType = testConfig.getMailBodyType(); + config.setMailBodyType(mailBodyType); + if (DYNAMIC_MAIL_BODY_TYPE.equals(mailBodyType)) { + config.setIsHtmlTemplate("${html}"); + } + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + + var md = new TbMsgMetaData(); + md.putValue("userEmail", EXPECTED_TO_EMAIL); + md.putValue("deviceType", EXPECTED_DEVICE_TYPE); + md.putValue("deviceName", EXPECTED_DEVICE_NAME); + if (testConfig.getIsHtmlTemplateMdValue() != null) { + md.putValue("html", testConfig.getIsHtmlTemplateMdValue()); + } - private RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); - private RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); + var msgDataStr = "{\"temperature\": " + EXPECTED_TEMPERATURE + "}"; + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, md, msgDataStr); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var msgCaptor = ArgumentCaptor.forClass(TbMsg.class); + var typeCaptor = ArgumentCaptor.forClass(TbMsgType.class); + var originatorCaptor = ArgumentCaptor.forClass(EntityId.class); + var metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class); + var dataCaptor = ArgumentCaptor.forClass(String.class); + verify(ctxMock).transformMsg( + msgCaptor.capture(), + typeCaptor.capture(), + originatorCaptor.capture(), + metadataCaptor.capture(), + dataCaptor.capture() + ); + verify(ctxMock, never()).tellFailure(any(), any()); + + Assertions.assertEquals(TbMsgType.SEND_EMAIL, typeCaptor.getValue()); + Assertions.assertEquals(originator, originatorCaptor.getValue()); + Assertions.assertNotSame(md, metadataCaptor.getValue()); + + var actual = JacksonUtil.fromBytes(dataCaptor.getValue().getBytes(), TbEmail.class); + var expected = getExpectedTbEmail(testConfig.isExpectedHtmlValue()); + + Assertions.assertEquals(expected, actual); + } - @Test - public void msgCanBeConverted() throws IOException { - initWithScript(); - metaData.putValue("username", "oreo"); - metaData.putValue("userEmail", "user@email.io"); - metaData.putValue("name", "temp"); - metaData.putValue("passed", "5"); - metaData.putValue("count", "100"); - TbMsg msg = TbMsg.newMsg( "USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); - - emailNode.onMsg(ctx, msg); - - ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(String.class); - ArgumentCaptor originatorCaptor = ArgumentCaptor.forClass(EntityId.class); - ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class); - ArgumentCaptor dataCaptor = ArgumentCaptor.forClass(String.class); - verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); - - - assertEquals("SEND_EMAIL", typeCaptor.getValue()); - assertEquals(originator, originatorCaptor.getValue()); - assertEquals("oreo", metadataCaptor.getValue().getValue("username")); - assertNotSame(metaData, metadataCaptor.getValue()); - - TbEmail actual = JacksonUtil.fromBytes(dataCaptor.getValue().getBytes(), TbEmail.class); - - TbEmail expected = TbEmail.builder() - .from("test@mail.org") - .to("user@email.io") - .subject("Hi oreo there") - .body("temp is to high. Current 5 and 100") + private TbEmail getExpectedTbEmail(boolean html) { + return TbEmail.builder() + .from(config.getFromTemplate()) + .to(EXPECTED_TO_EMAIL) + .subject(EXPECTED_SUBJECT) + .body(EXPECTED_BODY) + .html(html) .build(); - assertEquals(expected, actual); } - private void initWithScript() { - try { - TbMsgToEmailNodeConfiguration config = new TbMsgToEmailNodeConfiguration(); - config.setFromTemplate("test@mail.org"); - config.setToTemplate("${userEmail}"); - config.setSubjectTemplate("Hi ${username} there"); - config.setBodyTemplate("${name} is to high. Current ${passed} and ${count}"); - config.setMailBodyType("false"); - TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); - - emailNode = new TbMsgToEmailNode(); - emailNode.init(ctx, nodeConfiguration); - } catch (TbNodeException ex) { - throw new IllegalStateException(ex); - } + static Stream MailBodyTypeTestConfig() { + return Stream.of( + new MailBodyTypeTestConfig(false, "false", null), + new MailBodyTypeTestConfig(false, null, null), + new MailBodyTypeTestConfig(false, DYNAMIC_MAIL_BODY_TYPE, "false"), + new MailBodyTypeTestConfig(true, DYNAMIC_MAIL_BODY_TYPE, "true"), + new MailBodyTypeTestConfig(true, "true", null) + ); + } + + @Data + @RequiredArgsConstructor + static class MailBodyTypeTestConfig { + private final boolean expectedHtmlValue; + private final String mailBodyType; + private final String isHtmlTemplateMdValue; } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java index 2efc438f8f..9eec38cfae 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/math/TbMathNodeTest.java @@ -15,21 +15,24 @@ */ package org.thingsboard.rule.engine.math; -import com.datastax.oss.driver.api.core.uuid.Uuids; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.util.concurrent.Futures; import lombok.extern.slf4j.Slf4j; -import org.awaitility.Awaitility; -import org.junit.After; +import org.apache.commons.lang3.tuple.Triple; +import org.assertj.core.api.SoftAssertions; import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; -import org.springframework.test.util.ReflectionTestUtils; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.verification.Timeout; import org.thingsboard.common.util.AbstractListeningExecutor; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; @@ -45,6 +48,7 @@ import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.DoubleDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.dao.attributes.AttributesService; @@ -53,26 +57,48 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService; import java.util.Arrays; import java.util.List; import java.util.Optional; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.Semaphore; +import java.util.UUID; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; - +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyDouble; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.willAnswer; +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.BDDMockito.willReturn; +import static org.mockito.BDDMockito.willThrow; import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; @Slf4j -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class TbMathNodeTest { - private EntityId originator = new DeviceId(Uuids.timeBased()); - private TenantId tenantId = TenantId.fromUUID(Uuids.timeBased()); + static final int RULE_DISPATCHER_POOL_SIZE = 2; + static final int DB_CALLBACK_POOL_SIZE = 3; + static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5); + private final EntityId originator = DeviceId.fromString("ccd71696-0586-422d-940e-755a41ec3b0d"); + private final TenantId tenantId = TenantId.fromUUID(UUID.fromString("e7f46b23-0c7d-42f5-9b06-fc35ab17af8a")); - @Mock + @Mock(lenient = true) private TbContext ctx; @Mock private AttributesService attributesService; @@ -80,35 +106,27 @@ public class TbMathNodeTest { private TimeseriesService tsService; @Mock private RuleEngineTelemetryService telemetryService; - private AbstractListeningExecutor dbExecutor; + private AbstractListeningExecutor dbCallbackExecutor; + private AbstractListeningExecutor ruleEngineDispatcherExecutor; - @Before + @BeforeEach public void before() { - dbExecutor = new AbstractListeningExecutor() { - @Override - protected int getThreadPollSize() { - return 3; - } - }; - dbExecutor.init(); - initMocks(); + dbCallbackExecutor = new DBCallbackExecutor(); + dbCallbackExecutor.init(); + ruleEngineDispatcherExecutor = new RuleDispatcherExecutor(); + ruleEngineDispatcherExecutor.init(); + + willReturn(dbCallbackExecutor).given(ctx).getDbCallbackExecutor(); + willReturn(attributesService).given(ctx).getAttributesService(); + willReturn(telemetryService).given(ctx).getTelemetryService(); + willReturn(tsService).given(ctx).getTimeseriesService(); + willReturn(tenantId).given(ctx).getTenantId(); } - @After + @AfterEach public void after() { - dbExecutor.destroy(); - } - - private void initMocks() { - Mockito.reset(ctx); - Mockito.reset(attributesService); - Mockito.reset(tsService); - Mockito.reset(telemetryService); - lenient().when(ctx.getAttributesService()).thenReturn(attributesService); - lenient().when(ctx.getTelemetryService()).thenReturn(telemetryService); - lenient().when(ctx.getTimeseriesService()).thenReturn(tsService); - lenient().when(ctx.getTenantId()).thenReturn(tenantId); - lenient().when(ctx.getDbCallbackExecutor()).thenReturn(dbExecutor); + ruleEngineDispatcherExecutor.executor().shutdownNow(); + dbCallbackExecutor.executor().shutdownNow(); } private TbMathNode initNode(TbRuleNodeMathFunctionType operation, TbMathResult result, TbMathArgument... arguments) { @@ -119,7 +137,15 @@ public class TbMathNodeTest { return initNode(TbRuleNodeMathFunctionType.CUSTOM, expression, result, arguments); } + private TbMathNode initNodeWithCustomFunction(TbContext ctx, String expression, TbMathResult result, TbMathArgument... arguments) { + return initNode(ctx, TbRuleNodeMathFunctionType.CUSTOM, expression, result, arguments); + } + private TbMathNode initNode(TbRuleNodeMathFunctionType operation, String expression, TbMathResult result, TbMathArgument... arguments) { + return initNode(this.ctx, operation, expression, result, arguments); + } + + private TbMathNode initNode(TbContext ctx, TbRuleNodeMathFunctionType operation, String expression, TbMathResult result, TbMathArgument... arguments) { try { TbMathNodeConfiguration configuration = new TbMathNodeConfiguration(); configuration.setOperation(operation); @@ -149,139 +175,137 @@ public class TbMathNodeTest { metaData.putValue("key2", "argumentA"); ObjectNode msgNode = JacksonUtil.newObjectNode() .put("key3", "argumentB").put("argumentA", 2).put("argumentB", 2); - TbMsg msg = TbMsg.newMsg("TEST", originator, metaData, msgNode.toString()); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, msgNode.toString()); node.onMsg(ctx, msg); - ConcurrentMap semaphores = (ConcurrentMap) ReflectionTestUtils.getField(node, "semaphores"); - Assert.assertNotNull(semaphores); - Semaphore originatorSemaphore = semaphores.get(originator); - Assert.assertNotNull(originatorSemaphore); - metaData.putValue("key1", "secondMsgResult"); metaData.putValue("key2", "argumentC"); msgNode = JacksonUtil.newObjectNode() .put("key3", "argumentD").put("argumentC", 4).put("argumentD", 3); - msg = TbMsg.newMsg("TEST", originator, metaData, msgNode.toString()); + msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, msgNode.toString()); node.onMsg(ctx, msg); - Awaitility.await("Semaphore released").atMost(5, TimeUnit.SECONDS).until(semaphores.get(originator)::tryAcquire); - ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - Mockito.verify(ctx, Mockito.times(2)).tellSuccess(msgCaptor.capture()); + verify(ctx, timeout(TIMEOUT).times(2)).tellSuccess(msgCaptor.capture()); List resultMsgs = msgCaptor.getAllValues(); - Assert.assertFalse(resultMsgs.isEmpty()); - Assert.assertEquals(2, resultMsgs.size()); + assertFalse(resultMsgs.isEmpty()); + assertEquals(2, resultMsgs.size()); for (int i = 0; i < resultMsgs.size(); i++) { TbMsg outMsg = resultMsgs.get(i); - Assert.assertNotNull(outMsg); - Assert.assertNotNull(outMsg.getData()); + assertNotNull(outMsg); + assertNotNull(outMsg.getData()); var resultJson = JacksonUtil.toJsonNode(outMsg.getData()); String resultKey = i == 0 ? "firstMsgResult" : "secondMsgResult"; - Assert.assertTrue(resultJson.has(resultKey)); - Assert.assertEquals(i == 0 ? 10 : 17, resultJson.get(resultKey).asInt()); + assertTrue(resultJson.has(resultKey)); + assertEquals(i == 0 ? 10 : 17, resultJson.get(resultKey).asInt()); } - semaphores.remove(originator); } - @Test - public void testSimpleFunctions() { - testSimpleTwoArgumentFunction(TbRuleNodeMathFunctionType.ADD, 2.1, 2.2, 4.3); - testSimpleTwoArgumentFunction(TbRuleNodeMathFunctionType.SUB, 2.1, 2.2, -0.1); - testSimpleTwoArgumentFunction(TbRuleNodeMathFunctionType.MULT, 2.1, 2.0, 4.2); - testSimpleTwoArgumentFunction(TbRuleNodeMathFunctionType.DIV, 4.2, 2.0, 2.1); - - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.SIN, Math.toRadians(30), 0.5); - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.SIN, Math.toRadians(90), 1.0); - - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.SINH, Math.toRadians(0), 0.0); - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.COSH, Math.toRadians(0), 1.0); - - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.COS, Math.toRadians(60), 0.5); - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.COS, Math.toRadians(0), 1.0); - - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.TAN, Math.toRadians(45), 1); - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.TAN, Math.toRadians(0), 0); - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.TANH, 90, 1); - - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.ACOS, 0.5, 1.05); - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.ASIN, 0.5, 0.52); - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.ATAN, 0.5, 0.46); - testSimpleTwoArgumentFunction(TbRuleNodeMathFunctionType.ATAN2, 0.5, 0.3, 1.03); - - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.EXP, 1, 2.72); - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.EXPM1, 1, 1.72); - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.ABS, -1, 1); - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.SQRT, 4, 2); - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.CBRT, 8, 2); - - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.GET_EXP, 4, 2); - testSimpleTwoArgumentFunction(TbRuleNodeMathFunctionType.HYPOT, 4, 5, 6.4); - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.LOG, 4, 1.39); - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.LOG10, 4, 0.6); - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.LOG1P, 4, 1.61); - - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.CEIL, 1.55, 2); - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.FLOOR, 23.97, 23); - testSimpleTwoArgumentFunction(TbRuleNodeMathFunctionType.FLOOR_DIV, 5, 3, 1); - testSimpleTwoArgumentFunction(TbRuleNodeMathFunctionType.FLOOR_MOD, 6, 3, 0); - - testSimpleTwoArgumentFunction(TbRuleNodeMathFunctionType.MIN, 5, 3, 3); - testSimpleTwoArgumentFunction(TbRuleNodeMathFunctionType.MAX, 5, 3, 5); - testSimpleTwoArgumentFunction(TbRuleNodeMathFunctionType.POW, 5, 3, 125); - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.SIGNUM, 0.55, 1); - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.RAD, 5, 0.09); - testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType.DEG, 5, 286.48); + private static Stream testSimpleTwoArgumentFunction() { + return Stream.of( + Arguments.of(TbRuleNodeMathFunctionType.ADD, 2.1, 2.2, 4.3), + Arguments.of(TbRuleNodeMathFunctionType.SUB, 2.1, 2.2, -0.1), + Arguments.of(TbRuleNodeMathFunctionType.MULT, 2.1, 2.0, 4.2), + Arguments.of(TbRuleNodeMathFunctionType.DIV, 4.2, 2.0, 2.1), + Arguments.of(TbRuleNodeMathFunctionType.ATAN2, 0.5, 0.3, 1.03), + Arguments.of(TbRuleNodeMathFunctionType.HYPOT, 4, 5, 6.4), + Arguments.of(TbRuleNodeMathFunctionType.FLOOR_DIV, 5, 3, 1), + Arguments.of(TbRuleNodeMathFunctionType.FLOOR_MOD, 6, 3, 0), + Arguments.of(TbRuleNodeMathFunctionType.MIN, 5, 3, 3), + Arguments.of(TbRuleNodeMathFunctionType.MAX, 5, 3, 5), + Arguments.of(TbRuleNodeMathFunctionType.POW, 5, 3, 125) + ); } - private void testSimpleTwoArgumentFunction(TbRuleNodeMathFunctionType function, double arg1, double arg2, double result) { - initMocks(); - + @ParameterizedTest + @MethodSource + public void testSimpleTwoArgumentFunction(TbRuleNodeMathFunctionType function, double arg1, double arg2, double result) { var node = initNode(function, new TbMathResult(TbMathArgumentType.MESSAGE_BODY, "result", 2, false, false, null), new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a"), new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "b") ); - TbMsg msg = TbMsg.newMsg("TEST", originator, new TbMsgMetaData(), JacksonUtil.newObjectNode().put("a", arg1).put("b", arg2).toString()); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", arg1).put("b", arg2).toString()); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - Mockito.verify(ctx, Mockito.timeout(5000).times(1)).tellSuccess(msgCaptor.capture()); + verify(ctx, timeout(TIMEOUT).times(1)).tellSuccess(msgCaptor.capture()); TbMsg resultMsg = msgCaptor.getValue(); - Assert.assertNotNull(resultMsg); - Assert.assertNotNull(resultMsg.getData()); + assertNotNull(resultMsg); + assertNotNull(resultMsg.getData()); var resultJson = JacksonUtil.toJsonNode(resultMsg.getData()); - Assert.assertTrue(resultJson.has("result")); - Assert.assertEquals(result, resultJson.get("result").asDouble(), 0d); + assertTrue(resultJson.has("result")); + assertEquals(result, resultJson.get("result").asDouble(), 0d); } - private void testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType function, double arg1, double result) { - initMocks(); + private static Stream testSimpleOneArgumentFunction() { + return Stream.of( + Arguments.of(TbRuleNodeMathFunctionType.SIN, Math.toRadians(30), 0.5), + Arguments.of(TbRuleNodeMathFunctionType.SIN, Math.toRadians(90), 1.0), + + Arguments.of(TbRuleNodeMathFunctionType.SINH, Math.toRadians(0), 0.0), + Arguments.of(TbRuleNodeMathFunctionType.COSH, Math.toRadians(0), 1.0), + + Arguments.of(TbRuleNodeMathFunctionType.COS, Math.toRadians(60), 0.5), + Arguments.of(TbRuleNodeMathFunctionType.COS, Math.toRadians(0), 1.0), + + Arguments.of(TbRuleNodeMathFunctionType.TAN, Math.toRadians(45), 1), + Arguments.of(TbRuleNodeMathFunctionType.TAN, Math.toRadians(0), 0), + Arguments.of(TbRuleNodeMathFunctionType.TANH, 90, 1), + Arguments.of(TbRuleNodeMathFunctionType.ACOS, 0.5, 1.05), + Arguments.of(TbRuleNodeMathFunctionType.ASIN, 0.5, 0.52), + Arguments.of(TbRuleNodeMathFunctionType.ATAN, 0.5, 0.46), + + Arguments.of(TbRuleNodeMathFunctionType.EXP, 1, 2.72), + Arguments.of(TbRuleNodeMathFunctionType.EXPM1, 1, 1.72), + Arguments.of(TbRuleNodeMathFunctionType.ABS, -1, 1), + Arguments.of(TbRuleNodeMathFunctionType.SQRT, 4, 2), + Arguments.of(TbRuleNodeMathFunctionType.CBRT, 8, 2), + + Arguments.of(TbRuleNodeMathFunctionType.GET_EXP, 4, 2), + + Arguments.of(TbRuleNodeMathFunctionType.LOG, 4, 1.39), + Arguments.of(TbRuleNodeMathFunctionType.LOG10, 4, 0.6), + Arguments.of(TbRuleNodeMathFunctionType.LOG1P, 4, 1.61), + + Arguments.of(TbRuleNodeMathFunctionType.CEIL, 1.55, 2), + Arguments.of(TbRuleNodeMathFunctionType.FLOOR, 23.97, 23), + + Arguments.of(TbRuleNodeMathFunctionType.SIGNUM, 0.55, 1), + Arguments.of(TbRuleNodeMathFunctionType.RAD, 5, 0.09), + Arguments.of(TbRuleNodeMathFunctionType.DEG, 5, 286.48) + ); + } + + @ParameterizedTest + @MethodSource + public void testSimpleOneArgumentFunction(TbRuleNodeMathFunctionType function, double arg1, double result) { var node = initNode(function, new TbMathResult(TbMathArgumentType.MESSAGE_BODY, "result", 2, false, false, null), new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg("TEST", originator, new TbMsgMetaData(), JacksonUtil.newObjectNode().put("a", arg1).toString()); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", arg1).toString()); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - Mockito.verify(ctx, Mockito.timeout(5000)).tellSuccess(msgCaptor.capture()); + verify(ctx, timeout(TIMEOUT).times(1)).tellSuccess(msgCaptor.capture()); TbMsg resultMsg = msgCaptor.getValue(); - Assert.assertNotNull(resultMsg); - Assert.assertNotNull(resultMsg.getData()); + assertNotNull(resultMsg); + assertNotNull(resultMsg.getData()); var resultJson = JacksonUtil.toJsonNode(resultMsg.getData()); - Assert.assertTrue(resultJson.has("result")); - Assert.assertEquals(result, resultJson.get("result").asDouble(), 0d); + assertTrue(resultJson.has("result")); + assertEquals(result, resultJson.get("result").asDouble(), 0d); } @Test @@ -292,19 +316,19 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "b") ); - TbMsg msg = TbMsg.newMsg("TEST", originator, new TbMsgMetaData(), JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - Mockito.verify(ctx, Mockito.timeout(5000)).tellSuccess(msgCaptor.capture()); + verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); TbMsg resultMsg = msgCaptor.getValue(); - Assert.assertNotNull(resultMsg); - Assert.assertNotNull(resultMsg.getData()); + assertNotNull(resultMsg); + assertNotNull(resultMsg.getData()); var resultJson = JacksonUtil.toJsonNode(resultMsg.getData()); - Assert.assertTrue(resultJson.has("result")); - Assert.assertEquals(4, resultJson.get("result").asInt()); + assertTrue(resultJson.has("result")); + assertEquals(4, resultJson.get("result").asInt()); } @Test @@ -315,20 +339,20 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "b") ); - TbMsg msg = TbMsg.newMsg("TEST", originator, new TbMsgMetaData(), JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString()); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - Mockito.verify(ctx, Mockito.timeout(5000)).tellSuccess(msgCaptor.capture()); + verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); TbMsg resultMsg = msgCaptor.getValue(); - Assert.assertNotNull(resultMsg); - Assert.assertNotNull(resultMsg.getData()); - Assert.assertNotNull(resultMsg.getMetaData()); + assertNotNull(resultMsg); + assertNotNull(resultMsg.getData()); + assertNotNull(resultMsg.getMetaData()); var result = resultMsg.getMetaData().getValue("result"); - Assert.assertNotNull(result); - Assert.assertEquals("4", result); + assertNotNull(result); + assertEquals("4", result); } @Test @@ -339,25 +363,25 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.TIME_SERIES, "b") ); - TbMsg msg = TbMsg.newMsg("TEST", originator, new TbMsgMetaData(), JacksonUtil.newObjectNode().toString()); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().toString()); - Mockito.when(attributesService.find(tenantId, originator, DataConstants.SERVER_SCOPE, "a")) + when(attributesService.find(tenantId, originator, DataConstants.SERVER_SCOPE, "a")) .thenReturn(Futures.immediateFuture(Optional.of(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("a", 2.0))))); - Mockito.when(tsService.findLatest(tenantId, originator, "b")) + when(tsService.findLatest(tenantId, originator, "b")) .thenReturn(Futures.immediateFuture(Optional.of(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry("b", 2L))))); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - Mockito.verify(ctx, Mockito.timeout(5000)).tellSuccess(msgCaptor.capture()); + verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); TbMsg resultMsg = msgCaptor.getValue(); - Assert.assertNotNull(resultMsg); - Assert.assertNotNull(resultMsg.getData()); + assertNotNull(resultMsg); + assertNotNull(resultMsg.getData()); var resultJson = JacksonUtil.toJsonNode(resultMsg.getData()); - Assert.assertTrue(resultJson.has("result")); - Assert.assertEquals(4, resultJson.get("result").asInt()); + assertTrue(resultJson.has("result")); + assertEquals(4, resultJson.get("result").asInt()); } @Test @@ -367,19 +391,19 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg("TEST", originator, new TbMsgMetaData(), JacksonUtil.newObjectNode().put("a", 5).toString()); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 5).toString()); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - Mockito.verify(ctx, Mockito.timeout(5000)).tellSuccess(msgCaptor.capture()); + verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); TbMsg resultMsg = msgCaptor.getValue(); - Assert.assertNotNull(resultMsg); - Assert.assertNotNull(resultMsg.getData()); + assertNotNull(resultMsg); + assertNotNull(resultMsg.getData()); var resultJson = JacksonUtil.toJsonNode(resultMsg.getData()); - Assert.assertTrue(resultJson.has("result")); - Assert.assertEquals(2.236, resultJson.get("result").asDouble(), 0.0); + assertTrue(resultJson.has("result")); + assertEquals(2.236, resultJson.get("result").asDouble(), 0.0); } @Test @@ -389,19 +413,19 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg("TEST", originator, new TbMsgMetaData(), JacksonUtil.newObjectNode().put("a", 5).toString()); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 5).toString()); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - Mockito.verify(ctx, Mockito.timeout(5000)).tellSuccess(msgCaptor.capture()); + verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); TbMsg resultMsg = msgCaptor.getValue(); - Assert.assertNotNull(resultMsg); - Assert.assertNotNull(resultMsg.getData()); + assertNotNull(resultMsg); + assertNotNull(resultMsg.getData()); var result = resultMsg.getMetaData().getValue("result"); - Assert.assertNotNull(result); - Assert.assertEquals("2.236", result); + assertNotNull(result); + assertEquals("2.236", result); } @Test @@ -411,23 +435,23 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg("TEST", originator, new TbMsgMetaData(), JacksonUtil.newObjectNode().put("a", 5).toString()); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 5).toString()); - Mockito.when(telemetryService.saveAttrAndNotify(any(), any(), anyString(), anyString(), anyDouble())) + when(telemetryService.saveAttrAndNotify(any(), any(), anyString(), anyString(), anyDouble())) .thenReturn(Futures.immediateFuture(null)); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - Mockito.verify(ctx, Mockito.timeout(5000)).tellSuccess(msgCaptor.capture()); - Mockito.verify(telemetryService, times(1)).saveAttrAndNotify(any(), any(), anyString(), anyString(), anyDouble()); + verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); + verify(telemetryService, times(1)).saveAttrAndNotify(any(), any(), anyString(), anyString(), anyDouble()); TbMsg resultMsg = msgCaptor.getValue(); - Assert.assertNotNull(resultMsg); - Assert.assertNotNull(resultMsg.getData()); + assertNotNull(resultMsg); + assertNotNull(resultMsg.getData()); var result = resultMsg.getMetaData().getValue("result"); - Assert.assertNotNull(result); - Assert.assertEquals("2.236", result); + assertNotNull(result); + assertEquals("2.236", result); } @Test @@ -437,22 +461,22 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg("TEST", originator, new TbMsgMetaData(), JacksonUtil.newObjectNode().put("a", 5).toString()); - Mockito.when(telemetryService.saveAndNotify(any(), any(), any(TsKvEntry.class))) + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 5).toString()); + when(telemetryService.saveAndNotify(any(), any(), any(TsKvEntry.class))) .thenReturn(Futures.immediateFuture(null)); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - verify(ctx, Mockito.timeout(5000)).tellSuccess(msgCaptor.capture()); - Mockito.verify(telemetryService, times(1)).saveAndNotify(any(), any(), any(TsKvEntry.class)); + verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); + verify(telemetryService, times(1)).saveAndNotify(any(), any(), any(TsKvEntry.class)); TbMsg resultMsg = msgCaptor.getValue(); - Assert.assertNotNull(resultMsg); - Assert.assertNotNull(resultMsg.getData()); + assertNotNull(resultMsg); + assertNotNull(resultMsg.getData()); var resultJson = JacksonUtil.toJsonNode(resultMsg.getData()); - Assert.assertTrue(resultJson.has("result")); - Assert.assertEquals(2.236, resultJson.get("result").asDouble(), 0.0); + assertTrue(resultJson.has("result")); + assertEquals(2.236, resultJson.get("result").asDouble(), 0.0); } @Test @@ -462,27 +486,27 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg("TEST", originator, new TbMsgMetaData(), JacksonUtil.newObjectNode().put("a", 5).toString()); - Mockito.when(telemetryService.saveAndNotify(any(), any(), any(TsKvEntry.class))) + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 5).toString()); + when(telemetryService.saveAndNotify(any(), any(), any(TsKvEntry.class))) .thenReturn(Futures.immediateFuture(null)); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - verify(ctx, Mockito.timeout(5000)).tellSuccess(msgCaptor.capture()); - Mockito.verify(telemetryService, times(1)).saveAndNotify(any(), any(), any(TsKvEntry.class)); + verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); + verify(telemetryService, times(1)).saveAndNotify(any(), any(), any(TsKvEntry.class)); TbMsg resultMsg = msgCaptor.getValue(); - Assert.assertNotNull(resultMsg); - Assert.assertNotNull(resultMsg.getData()); + assertNotNull(resultMsg); + assertNotNull(resultMsg.getData()); var resultMetadata = resultMsg.getMetaData().getValue("result"); var resultData = JacksonUtil.toJsonNode(resultMsg.getData()); - Assert.assertTrue(resultData.has("result")); - Assert.assertEquals(2.236, resultData.get("result").asDouble(), 0.0); + assertTrue(resultData.has("result")); + assertEquals(2.236, resultData.get("result").asDouble(), 0.0); - Assert.assertNotNull(resultMetadata); - Assert.assertEquals("2.236", resultMetadata); + assertNotNull(resultMetadata); + assertEquals("2.236", resultMetadata); } @Test @@ -493,18 +517,18 @@ public class TbMathNodeTest { new TbMathResult(TbMathArgumentType.MESSAGE_METADATA, "result", 3, false, false, null), tbMathArgument ); - TbMsg msg = TbMsg.newMsg("TEST", originator, new TbMsgMetaData(), JacksonUtil.newObjectNode().put("a", 10).toString()); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 10).toString()); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - Mockito.verify(ctx, Mockito.timeout(5000)).tellSuccess(msgCaptor.capture()); + verify(ctx, timeout(TIMEOUT)).tellSuccess(msgCaptor.capture()); TbMsg resultMsg = msgCaptor.getValue(); - Assert.assertNotNull(resultMsg); - Assert.assertNotNull(resultMsg.getData()); + assertNotNull(resultMsg); + assertNotNull(resultMsg.getData()); var result = resultMsg.getMetaData().getValue("result"); - Assert.assertNotNull(result); - Assert.assertEquals("2.236", result); + assertNotNull(result); + assertEquals("2.236", result); } @Test @@ -513,11 +537,12 @@ public class TbMathNodeTest { new TbMathResult(TbMathArgumentType.TIME_SERIES, "result", 3, true, false, DataConstants.SERVER_SCOPE), new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "TestKey") ); - TbMsg msg = TbMsg.newMsg("TEST", originator, new TbMsgMetaData(), JacksonUtil.newObjectNode().put("a", 10).toString()); - Throwable thrown = assertThrows(RuntimeException.class, () -> { - node.onMsg(ctx, msg); - }); - Assert.assertNotNull(thrown.getMessage()); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 10).toString()); + node.onMsg(ctx, msg); + + ArgumentCaptor tCaptor = ArgumentCaptor.forClass(Throwable.class); + Mockito.verify(ctx, Mockito.timeout(5000)).tellFailure(eq(msg), tCaptor.capture()); + Assert.assertNotNull(tCaptor.getValue().getMessage()); } @Test @@ -527,10 +552,203 @@ public class TbMathNodeTest { new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a") ); - TbMsg msg = TbMsg.newMsg("TEST", originator, new TbMsgMetaData(), "[]"); - Throwable thrown = assertThrows(RuntimeException.class, () -> { - node.onMsg(ctx, msg); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + node.onMsg(ctx, msg); + + ArgumentCaptor tCaptor = ArgumentCaptor.forClass(Throwable.class); + Mockito.verify(ctx, Mockito.timeout(5000)).tellFailure(eq(msg), tCaptor.capture()); + Assert.assertNotNull(tCaptor.getValue().getMessage()); + } + + @Test + public void testExp4j_concurrent() { + TbMathNode node = spy(initNodeWithCustomFunction("2a+3b", + new TbMathResult(TbMathArgumentType.MESSAGE_BODY, "result", 2, false, false, null), + new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a"), + new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "b") + )); + EntityId originatorSlow = DeviceId.fromString("7f01170d-6bba-419c-b95c-2b4c3ba32f30"); + EntityId originatorFast = DeviceId.fromString("c45360ff-7906-4102-a2ae-3495a86168d0"); + CountDownLatch slowProcessingLatch = new CountDownLatch(1); + + List slowMsgList = IntStream.range(0, 5) + .mapToObj(x -> TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originatorSlow, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString())) + .collect(Collectors.toList()); + List fastMsgList = IntStream.range(0, 2) + .mapToObj(x -> TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originatorFast, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString())) + .collect(Collectors.toList()); + + assertThat(slowMsgList.size()).as("slow msgs >= rule-dispatcher pool size").isGreaterThanOrEqualTo(RULE_DISPATCHER_POOL_SIZE); + + log.debug("rule-dispatcher [{}], db-callback [{}], slowMsg [{}], fastMsg [{}]", RULE_DISPATCHER_POOL_SIZE, DB_CALLBACK_POOL_SIZE, slowMsgList.size(), fastMsgList.size()); + + willAnswer(invocation -> { + TbMsg msg = invocation.getArgument(1); + log.debug("\uD83D\uDC0C processMsgAsync slow originator [{}][{}]", msg.getOriginator(), msg); + try { + assertThat(slowProcessingLatch.await(30, TimeUnit.SECONDS)).as("await on slowProcessingLatch").isTrue(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return invocation.callRealMethod(); + }).given(node).processMsgAsync(eq(ctx), argThat(slowMsgList::contains)); + + willAnswer(invocation -> { + TbMsg msg = invocation.getArgument(1); + log.debug("\u26A1\uFE0F processMsgAsync FAST originator [{}][{}]", msg.getOriginator(), msg); + return invocation.callRealMethod(); + }).given(node).processMsgAsync(eq(ctx), argThat(fastMsgList::contains)); + + willAnswer(invocation -> { + TbMsg msg = invocation.getArgument(1); + log.debug("submit slow originator onMsg [{}][{}]", msg.getOriginator(), msg); + return invocation.callRealMethod(); + }).given(node).onMsg(eq(ctx), argThat(slowMsgList::contains)); + + willAnswer(invocation -> { + TbMsg msg = invocation.getArgument(1); + log.debug("submit FAST originator onMsg [{}][{}]", msg.getOriginator(), msg); + return invocation.callRealMethod(); + }).given(node).onMsg(eq(ctx), argThat(fastMsgList::contains)); + + // submit slow msg may block all rule engine dispatcher threads + slowMsgList.forEach(msg -> ruleEngineDispatcherExecutor.executeAsync(() -> node.onMsg(ctx, msg))); + // wait until dispatcher threads started with all slowMsg + verify(node, timeout(TIMEOUT).times(slowMsgList.size())).onMsg(eq(ctx), argThat(slowMsgList::contains)); + + // submit fast have to return immediately + fastMsgList.forEach(msg -> ruleEngineDispatcherExecutor.executeAsync(() -> node.onMsg(ctx, msg))); + // wait until all fast messages processed + verify(ctx, timeout(TIMEOUT).times(fastMsgList.size())).tellSuccess(any()); + + slowProcessingLatch.countDown(); + + verify(ctx, timeout(TIMEOUT).times(fastMsgList.size() + slowMsgList.size())).tellSuccess(any()); + + verify(ctx, never()).tellFailure(any(), any()); + } + + @Test + public void testExp4j_concurrentBySingleOriginator_processMsgAsyncException() { + TbMathNode node = spy(initNodeWithCustomFunction("2a+3b", + new TbMathResult(TbMathArgumentType.MESSAGE_BODY, "result", 2, false, false, null), + new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a"), + new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "b") + )); + + willThrow(new RuntimeException("Message body has no 'delta'")).given(node).resolveArguments(any(), any(), any(), any()); + + EntityId originatorSlow = DeviceId.fromString("7f01170d-6bba-419c-b95c-2b4c3ba32f30"); + CountDownLatch slowProcessingLatch = new CountDownLatch(1); + + List slowMsgList = IntStream.range(0, 5) + .mapToObj(x -> TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originatorSlow, TbMsgMetaData.EMPTY, JacksonUtil.newObjectNode().put("a", 2).put("b", 2).toString())) + .collect(Collectors.toList()); + + assertThat(slowMsgList.size()).as("slow msgs >= rule-dispatcher pool size").isGreaterThanOrEqualTo(RULE_DISPATCHER_POOL_SIZE); + + log.debug("rule-dispatcher [{}], db-callback [{}], slowMsg [{}]", RULE_DISPATCHER_POOL_SIZE, DB_CALLBACK_POOL_SIZE, slowMsgList.size()); + + willAnswer(invocation -> { + TbMsg msg = invocation.getArgument(1); + if (slowProcessingLatch.getCount() > 0) { + log.debug("Await on slowProcessingLatch before processMsgAsync"); + try { + assertThat(slowProcessingLatch.await(30, TimeUnit.SECONDS)).as("await on slowProcessingLatch").isTrue(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + log.debug("\uD83D\uDC0C processMsgAsync with exception [{}][{}]", msg.getOriginator(), msg); + return invocation.callRealMethod(); + }).given(node).processMsgAsync(eq(ctx), argThat(slowMsgList::contains)); + + willAnswer(invocation -> { + TbMsg msg = invocation.getArgument(1); + log.debug("submit slow originator onMsg [{}][{}]", msg.getOriginator(), msg); + return invocation.callRealMethod(); + }).given(node).onMsg(eq(ctx), argThat(slowMsgList::contains)); + + // submit slow msg may block all rule engine dispatcher threads + slowMsgList.forEach(msg -> ruleEngineDispatcherExecutor.executeAsync(() -> node.onMsg(ctx, msg))); + // wait until dispatcher threads started with all slowMsg + verify(node, new Timeout(TimeUnit.SECONDS.toMillis(5), times(slowMsgList.size()))).onMsg(eq(ctx), argThat(slowMsgList::contains)); + + slowProcessingLatch.countDown(); + + verify(ctx, new Timeout(TimeUnit.SECONDS.toMillis(5), times(slowMsgList.size()))).tellFailure(any(), any()); + verify(ctx, never()).tellSuccess(any()); + + } + + @Test + public void testExp4j_concurrentBySingleOriginator_SingleMsg_manyNodesWithDifferentOutput() { + assertThat(RULE_DISPATCHER_POOL_SIZE).as("dispatcher pool size have to be > 1").isGreaterThan(1); + CountDownLatch processingLatch = new CountDownLatch(1); + List> ctxNodes = IntStream.range(0, RULE_DISPATCHER_POOL_SIZE * 2) + .mapToObj(x -> { + final TbContext ctx = mock(TbContext.class); // many rule nodes - many contexts + willReturn(dbCallbackExecutor).given(ctx).getDbCallbackExecutor(); + final String resultKey = "result" + x; + final TbMathNode node = spy(initNodeWithCustomFunction(ctx, "2a+3b", + new TbMathResult(TbMathArgumentType.MESSAGE_METADATA, resultKey, 1, false, true, null), + new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "a"), + new TbMathArgument(TbMathArgumentType.MESSAGE_BODY, "b"))); + willAnswer(invocation -> { + if (processingLatch.getCount() > 0) { + log.debug("Await on processingLatch before processMsgAsync"); + try { + assertThat(processingLatch.await(30, TimeUnit.SECONDS)).as("await on processingLatch").isTrue(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + log.debug("\uD83D\uDC0C processMsgAsync on node with expected resultKey [{}]", resultKey); + return invocation.callRealMethod(); + }).given(node).processMsgAsync(any(), any()); + willAnswer(invocation -> { + TbMsg msg = invocation.getArgument(1); + log.debug("submit originator onMsg [{}][{}]", msg.getOriginator(), msg); + return invocation.callRealMethod(); + }).given(node).onMsg(any(), any()); + return Triple.of(ctx, resultKey, node); + }) + .collect(Collectors.toList()); + ctxNodes.forEach(ctxNode -> ruleEngineDispatcherExecutor.executeAsync(() -> ctxNode.getRight() + .onMsg(ctxNode.getLeft(), TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, TbMsgMetaData.EMPTY, "{\"a\":2,\"b\":2}")))); + ctxNodes.forEach(ctxNode -> verify(ctxNode.getRight(), timeout(5000)).onMsg(eq(ctxNode.getLeft()), any())); + processingLatch.countDown(); + + SoftAssertions softly = new SoftAssertions(); + ctxNodes.forEach(ctxNode -> { + final TbContext ctx = ctxNode.getLeft(); + final String resultKey = ctxNode.getMiddle(); + ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctx, timeout(5000)).tellSuccess(msgCaptor.capture()); + + TbMsg resultMsg = msgCaptor.getValue(); + assertThat(resultMsg).as("result msg non null for result key " + resultKey).isNotNull(); + log.debug("asserting result key [{}] in metadata [{}]", resultKey, resultMsg.getMetaData().getData()); + softly.assertThat(resultMsg.getMetaData().getValue(resultKey)).as("asserting result key " + resultKey) + .isEqualTo("10.0"); }); - Assert.assertNotNull(thrown.getMessage()); + + softly.assertAll(); + verify(ctx, never()).tellFailure(any(), any()); + } + + static class RuleDispatcherExecutor extends AbstractListeningExecutor { + @Override + protected int getThreadPollSize() { + return RULE_DISPATCHER_POOL_SIZE; + } } + + static class DBCallbackExecutor extends AbstractListeningExecutor { + @Override + protected int getThreadPollSize() { + return DB_CALLBACK_POOL_SIZE; + } + } + } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/AbstractAttributeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/AbstractAttributeNodeTest.java deleted file mode 100644 index f0fcb3c6ce..0000000000 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/AbstractAttributeNodeTest.java +++ /dev/null @@ -1,231 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.rule.engine.metadata; - -import com.datastax.oss.driver.api.core.uuid.Uuids; -import com.google.common.collect.Lists; -import com.google.common.util.concurrent.Futures; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.rule.engine.api.TbNodeConfiguration; -import org.thingsboard.rule.engine.api.TbNodeException; -import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.asset.Asset; -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.RuleChainId; -import org.thingsboard.server.common.data.id.RuleNodeId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; -import org.thingsboard.server.common.data.kv.BasicTsKvEntry; -import org.thingsboard.server.common.data.kv.StringDataEntry; -import org.thingsboard.server.common.data.kv.TsKvEntry; -import org.thingsboard.server.common.msg.TbMsg; -import org.thingsboard.server.common.msg.TbMsgDataType; -import org.thingsboard.server.common.msg.TbMsgMetaData; -import org.thingsboard.server.dao.asset.AssetService; -import org.thingsboard.server.dao.attributes.AttributesService; -import org.thingsboard.server.dao.device.DeviceService; -import org.thingsboard.server.dao.timeseries.TimeseriesService; -import org.thingsboard.server.dao.user.UserService; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyCollection; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.same; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.thingsboard.rule.engine.api.TbRelationTypes.FAILURE; -import static org.thingsboard.server.common.data.DataConstants.SERVER_SCOPE; - -@RunWith(MockitoJUnitRunner.class) -public abstract class AbstractAttributeNodeTest { - final CustomerId customerId = new CustomerId(Uuids.timeBased()); - final TenantId tenantId = TenantId.fromUUID(Uuids.timeBased()); - final RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); - final RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); - final String keyAttrConf = "${word}"; - final String valueAttrConf = "${result}"; - @Mock - TbContext ctx; - @Mock - AttributesService attributesService; - @Mock - TimeseriesService timeseriesService; - @Mock - UserService userService; - @Mock - AssetService assetService; - @Mock - DeviceService deviceService; - TbMsg msg; - Map metaData; - TbEntityGetAttrNode node; - - void init(TbEntityGetAttrNode node) throws TbNodeException { - TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(getTbNodeConfig())); - - metaData = new HashMap<>(); - metaData.putIfAbsent("word", "temperature"); - metaData.putIfAbsent("result", "answer"); - - this.node = node; - this.node.init(null, nodeConfiguration); - } - - void errorThrownIfCannotLoadAttributes(User user) { - msg = TbMsg.newMsg("USER", user.getId(), new TbMsgMetaData(), TbMsgDataType.JSON, "{}", ruleChainId, ruleNodeId); - - when(ctx.getAttributesService()).thenReturn(attributesService); - when(attributesService.find(any(), eq(getEntityId()), eq(SERVER_SCOPE), anyCollection())) - .thenThrow(new IllegalStateException("something wrong")); - - node.onMsg(ctx, msg); - final ArgumentCaptor captor = ArgumentCaptor.forClass(Throwable.class); - verify(ctx).tellFailure(same(msg), captor.capture()); - - Throwable value = captor.getValue(); - assertEquals("something wrong", value.getMessage()); - assertTrue(msg.getMetaData().getData().isEmpty()); - } - - void errorThrownIfCannotLoadAttributesAsync(User user) { - - msg = TbMsg.newMsg("USER", user.getId(), new TbMsgMetaData(), TbMsgDataType.JSON, "{}", ruleChainId, ruleNodeId); - - when(ctx.getAttributesService()).thenReturn(attributesService); - when(attributesService.find(any(), eq(getEntityId()), eq(SERVER_SCOPE), anyCollection())) - .thenReturn(Futures.immediateFailedFuture(new IllegalStateException("something wrong"))); - - node.onMsg(ctx, msg); - final ArgumentCaptor captor = ArgumentCaptor.forClass(Throwable.class); - verify(ctx).tellFailure(same(msg), captor.capture()); - - Throwable value = captor.getValue(); - assertEquals("something wrong", value.getMessage()); - assertTrue(msg.getMetaData().getData().isEmpty()); - } - - void failedChainUsedIfCustomerCannotBeFound(User user) { - msg = TbMsg.newMsg("USER", user.getId(), new TbMsgMetaData(), TbMsgDataType.JSON, "{}", ruleChainId, ruleNodeId); - - node.onMsg(ctx, msg); - verify(ctx).tellNext(msg, FAILURE); - assertTrue(msg.getMetaData().getData().isEmpty()); - } - - void entityAttributeAddedInMetadata(EntityId entityId, String type) { - msg = TbMsg.newMsg(type, entityId, new TbMsgMetaData(metaData), TbMsgDataType.JSON, "{}", ruleChainId, ruleNodeId); - entityAttributeFetched(getEntityId()); - } - - void usersCustomerAttributesFetched(User user) { - msg = TbMsg.newMsg("USER", user.getId(), new TbMsgMetaData(metaData), TbMsgDataType.JSON, "{}", ruleChainId, ruleNodeId); - - entityAttributeFetched(getEntityId()); - } - - void assetsCustomerAttributesFetched(Asset asset) { - msg = TbMsg.newMsg("ASSET", asset.getId(), new TbMsgMetaData(metaData), TbMsgDataType.JSON, "{}", ruleChainId, ruleNodeId); - - entityAttributeFetched(getEntityId()); - } - - void deviceCustomerAttributesFetched(Device device) { - msg = TbMsg.newMsg("DEVICE", device.getId(), new TbMsgMetaData(metaData), TbMsgDataType.JSON, "{}", ruleChainId, ruleNodeId); - - entityAttributeFetched(getEntityId()); - } - - void deviceCustomerTelemetryFetched(Device device) throws TbNodeException { - TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(getTbNodeConfigForTelemetry())); - - TbEntityGetAttrNode node = getEmptyNode(); - node.init(null, nodeConfiguration); - - msg = TbMsg.newMsg("DEVICE", device.getId(), new TbMsgMetaData(metaData), TbMsgDataType.JSON, "{}", ruleChainId, ruleNodeId); - - List timeseries = Lists.newArrayList(new BasicTsKvEntry(1L, new StringDataEntry("temperature", "highest"))); - - when(ctx.getTimeseriesService()).thenReturn(timeseriesService); - when(timeseriesService.findLatest(any(), eq(getEntityId()), anyCollection())) - .thenReturn(Futures.immediateFuture(timeseries)); - - node.onMsg(ctx, msg); - verify(ctx).tellSuccess(msg); - assertEquals(msg.getMetaData().getValue("answer"), "highest"); - } - - void entityAttributeFetched(EntityId entityId) { - List attributes = Lists.newArrayList(new BaseAttributeKvEntry(new StringDataEntry("temperature", "high"), 1L)); - - when(ctx.getAttributesService()).thenReturn(attributesService); - when(attributesService.find(any(), eq(entityId), eq(SERVER_SCOPE), anyCollection())) - .thenReturn(Futures.immediateFuture(attributes)); - - node.onMsg(ctx, msg); - verify(ctx).tellSuccess(msg); - assertEquals(msg.getMetaData().getValue("answer"), "high"); - } - - TbGetEntityAttrNodeConfiguration getTbNodeConfig() { - return getConfig(false); - } - - TbGetEntityAttrNodeConfiguration getTbNodeConfigForTelemetry() { - return getConfig(true); - } - - private TbGetEntityAttrNodeConfiguration getConfig(boolean isTelemetry) { - TbGetEntityAttrNodeConfiguration config = new TbGetEntityAttrNodeConfiguration(); - Map conf = new HashMap<>(); - conf.put(keyAttrConf, valueAttrConf); - config.setAttrMapping(conf); - config.setTelemetry(isTelemetry); - return config; - } - - protected abstract TbEntityGetAttrNode getEmptyNode(); - - abstract EntityId getEntityId(); - - void mockFindDevice(Device device) { - when(ctx.getDeviceService()).thenReturn(deviceService); - when(deviceService.findDeviceById(any(), eq(device.getId()))).thenReturn(device); - } - - void mockFindAsset(Asset asset) { - when(ctx.getAssetService()).thenReturn(assetService); - when(assetService.findAssetByIdAsync(any(), eq(asset.getId()))).thenReturn(Futures.immediateFuture(asset)); - } - - void mockFindUser(User user) { - when(ctx.getUserService()).thenReturn(userService); - when(userService.findUserByIdAsync(any(), eq(user.getId()))).thenReturn(Futures.immediateFuture(user)); - } -} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeTest.java new file mode 100644 index 0000000000..0fd8793e82 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNodeTest.java @@ -0,0 +1,460 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.metadata; + +import com.google.common.util.concurrent.Futures; +import lombok.RequiredArgsConstructor; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.ListeningExecutor; +import org.thingsboard.rule.engine.TestDbCallbackExecutor; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.BooleanDataEntry; +import org.thingsboard.server.common.data.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.kv.JsonDataEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.dao.timeseries.TimeseriesService; + +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anySet; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class CalculateDeltaNodeTest { + + private static final DeviceId DUMMY_DEVICE_ORIGINATOR = new DeviceId(UUID.randomUUID()); + private static final TenantId TENANT_ID = new TenantId(UUID.randomUUID()); + private static final ListeningExecutor DB_EXECUTOR = new TestDbCallbackExecutor(); + @Mock + private TbContext ctxMock; + @Mock + private TimeseriesService timeseriesServiceMock; + private CalculateDeltaNode node; + private CalculateDeltaNodeConfiguration config; + private TbNodeConfiguration nodeConfiguration; + + @BeforeEach + public void setUp() throws TbNodeException { + node = new CalculateDeltaNode(); + config = new CalculateDeltaNodeConfiguration().defaultConfiguration(); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + when(ctxMock.getTimeseriesService()).thenReturn(timeseriesServiceMock); + + node.init(ctxMock, nodeConfiguration); + } + + @Test + public void givenDefaultConfig_whenDefaultConfiguration_thenVerify() { + assertEquals(config.getInputValueKey(), "pulseCounter"); + assertEquals(config.getOutputValueKey(), "delta"); + assertTrue(config.isUseCache()); + assertFalse(config.isAddPeriodBetweenMsgs()); + assertEquals(config.getPeriodValueKey(), "periodInMs"); + assertTrue(config.isTellFailureIfDeltaIsNegative()); + } + + @Test + public void givenInvalidMsgType_whenOnMsg_thenShouldTellNextOther() { + // GIVEN + var msgData = "{\"pulseCounter\": 42}"; + var msg = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + verify(ctxMock, times(1)).tellNext(eq(msg), eq(TbNodeConnectionType.OTHER)); + verify(ctxMock, never()).tellSuccess(any()); + verify(ctxMock, never()).tellFailure(any(), any()); + } + + @Test + public void givenInvalidMsgDataType_whenOnMsg_thenShouldTellNextOther() { + // GIVEN + var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + verify(ctxMock, times(1)).tellNext(eq(msg), eq(TbNodeConnectionType.OTHER)); + verify(ctxMock, never()).tellSuccess(any()); + verify(ctxMock, never()).tellFailure(any(), any()); + } + + + @Test + public void givenInputKeyIsNotPresent_whenOnMsg_thenShouldTellNextOther() { + // GIVEN + var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + verify(ctxMock, times(1)).tellNext(eq(msg), eq(TbNodeConnectionType.OTHER)); + verify(ctxMock, never()).tellSuccess(any()); + verify(ctxMock, never()).tellFailure(any(), any()); + } + + @Test + public void givenDoubleValue_whenOnMsgAndCachingOff_thenShouldTellSuccess() throws TbNodeException { + // GIVEN + config.setRound(1); + config.setInputValueKey("temperature"); + config.setOutputValueKey("temp_delta"); + config.setUseCache(false); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + node.init(ctxMock, nodeConfiguration); + + mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new DoubleDataEntry("temperature", 40.5))); + + var msgData = "{\"temperature\": 42,\"airPressure\":123}"; + var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMsgCaptor.capture()); + verify(ctxMock, never()).tellNext(any(), anyString()); + verify(ctxMock, never()).tellNext(any(), anySet()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgData = "{\"temperature\":42,\"airPressure\":123,\"temp_delta\":1.5}"; + + assertEquals(expectedMsgData, actualMsgCaptor.getValue().getData()); + } + + @Test + public void givenLongStringValue_whenOnMsgAndCachingOff_thenShouldTellSuccess() throws TbNodeException { + // GIVEN + config.setInputValueKey("temperature"); + config.setOutputValueKey("temp_delta"); + config.setUseCache(false); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + node.init(ctxMock, nodeConfiguration); + + mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry("temperature", 40L))); + + var msgData = "{\"temperature\": 42,\"airPressure\":123}"; + var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMsgCaptor.capture()); + verify(ctxMock, never()).tellNext(any(), anyString()); + verify(ctxMock, never()).tellNext(any(), anySet()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgData = "{\"temperature\":42,\"airPressure\":123,\"temp_delta\":2}"; + + assertEquals(expectedMsgData, actualMsgCaptor.getValue().getData()); + } + + @Test + public void givenValidStringValue_whenOnMsgAndCachingOff_thenShouldTellSuccess() throws TbNodeException { + // GIVEN + config.setInputValueKey("temperature"); + config.setOutputValueKey("temp_delta"); + config.setUseCache(false); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + node.init(ctxMock, nodeConfiguration); + + mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry("temperature", "40.0"))); + + var msgData = "{\"temperature\": 42,\"airPressure\":123}"; + var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMsgCaptor.capture()); + verify(ctxMock, never()).tellNext(any(), anyString()); + verify(ctxMock, never()).tellNext(any(), anySet()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgData = "{\"temperature\":42,\"airPressure\":123,\"temp_delta\":2}"; + + assertEquals(expectedMsgData, actualMsgCaptor.getValue().getData()); + } + + @Test + public void givenTwoMessagesAndPeriodOnAndCachingOn_whenOnMsg_thenVerify() throws TbNodeException { + // STAGE 1 + // GIVEN + config.setInputValueKey("temperature"); + config.setOutputValueKey("temp_delta"); + config.setPeriodValueKey("ts_delta"); + config.setAddPeriodBetweenMsgs(true); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + node.init(ctxMock, nodeConfiguration); + + mockFindLatest(new BasicTsKvEntry(1L, new DoubleDataEntry("temperature", 40.0))); + + var msgData = "{\"temperature\": 42,\"airPressure\":123}"; + var firstMsgMetaData = new TbMsgMetaData(); + firstMsgMetaData.putValue("ts", String.valueOf(3L)); + var firstMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, firstMsgMetaData, msgData); + + // WHEN + node.onMsg(ctxMock, firstMsg); + + // THEN + var actualMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMsgCaptor.capture()); + verify(ctxMock, never()).tellNext(any(), anyString()); + verify(ctxMock, never()).tellNext(any(), anySet()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgData = "{\"temperature\":42,\"airPressure\":123,\"temp_delta\":2,\"ts_delta\":2}"; + + assertEquals(expectedMsgData, actualMsgCaptor.getValue().getData()); + + // STAGE 2 + // GIVEN + reset(ctxMock); + reset(timeseriesServiceMock); + + var secondMsgMetaData = new TbMsgMetaData(); + secondMsgMetaData.putValue("ts", String.valueOf(6L)); + var secondMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, secondMsgMetaData, msgData); + + // WHEN + node.onMsg(ctxMock, secondMsg); + + // THEN + actualMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(timeseriesServiceMock, never()).findLatest(any(), any(), anyList()); + verify(ctxMock, times(1)).tellSuccess(actualMsgCaptor.capture()); + verify(ctxMock, never()).tellNext(any(), anyString()); + verify(ctxMock, never()).tellNext(any(), anySet()); + verify(ctxMock, never()).tellFailure(any(), any()); + + expectedMsgData = "{\"temperature\":42,\"airPressure\":123,\"temp_delta\":0,\"ts_delta\":3}"; + + assertEquals(expectedMsgData, actualMsgCaptor.getValue().getData()); + } + + @Test + public void givenLastValueIsNull_whenOnMsgAndCachingOff_thenDeltaShouldBeZero() throws TbNodeException { + // GIVEN + config.setInputValueKey("temperature"); + config.setOutputValueKey("temp_delta"); + config.setUseCache(false); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + node.init(ctxMock, nodeConfiguration); + + mockFindLatestAsync(new BasicTsKvEntry(System.currentTimeMillis(), new DoubleDataEntry("temperature", null))); + + var msgData = "{\"temperature\": 42,\"airPressure\":123}"; + var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMsgCaptor.capture()); + verify(ctxMock, never()).tellNext(any(), anyString()); + verify(ctxMock, never()).tellNext(any(), anySet()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgData = "{\"temperature\":42,\"airPressure\":123,\"temp_delta\":0}"; + + assertEquals(expectedMsgData, actualMsgCaptor.getValue().getData()); + } + + @Test + public void givenNegativeDeltaAndTellFailureIfNegativeDeltaTrue_whenOnMsg_thenShouldTellFailure() throws TbNodeException { + // GIVEN + config.setTellFailureIfDeltaIsNegative(true); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + node.init(ctxMock, nodeConfiguration); + + mockFindLatest(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry("pulseCounter", 200L))); + + var msgData = "{\"pulseCounter\":\"123\"}"; + var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + var actualExceptionCaptor = ArgumentCaptor.forClass(Exception.class); + + verify(ctxMock, times(1)).tellFailure(actualMsgCaptor.capture(), actualExceptionCaptor.capture()); + verify(ctxMock, never()).tellSuccess(any()); + verify(ctxMock, never()).tellNext(any(), anyString()); + verify(ctxMock, never()).tellNext(any(), anySet()); + + var expectedExceptionMsg = "Delta value is negative!"; + var actualException = actualExceptionCaptor.getValue(); + + assertEquals(msg, actualMsgCaptor.getValue()); + assertInstanceOf(IllegalArgumentException.class, actualException); + assertEquals(expectedExceptionMsg, actualException.getMessage()); + } + + @Test + public void givenNegativeDeltaAndTellFailureIfNegativeDeltaFalse_whenOnMsg_thenShouldTellSuccess() throws TbNodeException { + // GIVEN + config.setTellFailureIfDeltaIsNegative(false); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + node.init(ctxMock, nodeConfiguration); + + mockFindLatest(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry("pulseCounter", 200L))); + + var msgData = "{\"pulseCounter\":\"123\"}"; + var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMsgCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + verify(ctxMock, never()).tellNext(any(), anyString()); + verify(ctxMock, never()).tellNext(any(), anySet()); + + String expectedMsgData = "{\"pulseCounter\":\"123\",\"delta\":-77}"; + assertEquals(expectedMsgData, actualMsgCaptor.getValue().getData()); + } + + @Test + public void givenInvalidStringValue_whenOnMsg_thenException() { + // GIVEN + mockFindLatest(new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry("pulseCounter", "high"))); + + var msgData = "{\"pulseCounter\":\"123\"}"; + var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + + // WHEN-THEN + Assertions.assertThatThrownBy(() -> node.onMsg(ctxMock, msg)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Calculation failed. Unable to parse value [high] of telemetry [pulseCounter] to Double"); + } + + @Test + public void givenBooleanValue_whenOnMsg_thenException() { + // GIVEN + mockFindLatest(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry("pulseCounter", false))); + + var msgData = "{\"pulseCounter\":true}"; + var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + + // WHEN-THEN + Assertions.assertThatThrownBy(() -> node.onMsg(ctxMock, msg)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Calculation failed. Boolean values are not supported!"); + } + + @Test + public void givenJsonValue_whenOnMsg_thenException() { + // GIVEN + mockFindLatest(new BasicTsKvEntry(System.currentTimeMillis(), new JsonDataEntry("pulseCounter", "{\"isActive\":false}"))); + + var msgData = "{\"pulseCounter\":{\"isActive\":true}}"; + var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, msgData); + + // WHEN-THEN + Assertions.assertThatThrownBy(() -> node.onMsg(ctxMock, msg)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Calculation failed. JSON values are not supported!"); + } + + private void mockFindLatest(TsKvEntry tsKvEntry) { + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + when(timeseriesServiceMock.findLatestSync( + eq(TENANT_ID), eq(DUMMY_DEVICE_ORIGINATOR), argThat(new ListMatcher<>(List.of(tsKvEntry.getKey()))) + )).thenReturn(List.of(tsKvEntry)); + } + + private void mockFindLatestAsync(TsKvEntry tsKvEntry) { + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + when(timeseriesServiceMock.findLatest( + eq(TENANT_ID), eq(DUMMY_DEVICE_ORIGINATOR), argThat(new ListMatcher<>(List.of(tsKvEntry.getKey()))) + )).thenReturn(Futures.immediateFuture(List.of(tsKvEntry))); + } + + @RequiredArgsConstructor + private static class ListMatcher implements ArgumentMatcher> { + + private final List expectedList; + + @Override + public boolean matches(List actualList) { + if (actualList == expectedList) { + return true; + } + if (actualList.size() != expectedList.size()) { + return false; + } + return actualList.containsAll(expectedList); + } + + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbAbstractGetAttributesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbAbstractGetAttributesNodeTest.java deleted file mode 100644 index b2b96052cc..0000000000 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbAbstractGetAttributesNodeTest.java +++ /dev/null @@ -1,368 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.rule.engine.metadata; - -import com.datastax.oss.driver.api.core.uuid.Uuids; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.common.util.concurrent.Futures; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; -import org.thingsboard.common.util.AbstractListeningExecutor; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.rule.engine.api.TbNodeConfiguration; -import org.thingsboard.rule.engine.api.TbNodeException; -import org.thingsboard.server.common.data.DataConstants; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; -import org.thingsboard.server.common.data.kv.BasicTsKvEntry; -import org.thingsboard.server.common.data.kv.JsonDataEntry; -import org.thingsboard.server.common.data.kv.StringDataEntry; -import org.thingsboard.server.common.data.kv.TsKvEntry; -import org.thingsboard.server.common.msg.TbMsg; -import org.thingsboard.server.common.msg.TbMsgMetaData; -import org.thingsboard.server.dao.attributes.AttributesService; -import org.thingsboard.server.dao.timeseries.TimeseriesService; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.lenient; -import static org.mockito.Mockito.never; - -@RunWith(MockitoJUnitRunner.class) -public class TbAbstractGetAttributesNodeTest { - - private EntityId originator = new DeviceId(Uuids.timeBased()); - private TenantId tenantId = TenantId.fromUUID(Uuids.timeBased()); - - @Mock - private TbContext ctx; - @Mock - private AttributesService attributesService; - @Mock - private TimeseriesService tsService; - private AbstractListeningExecutor dbExecutor; - - private List clientAttributes; - private List serverAttributes; - private List sharedAttributes; - private List tsKeys; - private long ts; - - @Before - public void before() throws TbNodeException { - dbExecutor = new AbstractListeningExecutor() { - @Override - protected int getThreadPollSize() { - return 3; - } - }; - dbExecutor.init(); - - Mockito.reset(ctx); - Mockito.reset(attributesService); - Mockito.reset(tsService); - - Mockito.reset(ctx); - Mockito.reset(attributesService); - Mockito.reset(tsService); - - lenient().when(ctx.getAttributesService()).thenReturn(attributesService); - lenient().when(ctx.getTimeseriesService()).thenReturn(tsService); - lenient().when(ctx.getTenantId()).thenReturn(tenantId); - lenient().when(ctx.getDbCallbackExecutor()).thenReturn(dbExecutor); - - clientAttributes = getAttributeNames("client"); - serverAttributes = getAttributeNames("server"); - sharedAttributes = getAttributeNames("shared"); - tsKeys = List.of("temperature", "humidity", "unknown"); - ts = System.currentTimeMillis(); - - Mockito.when(attributesService.find(tenantId, originator, DataConstants.CLIENT_SCOPE, clientAttributes)) - .thenReturn(Futures.immediateFuture(getListAttributeKvEntry(clientAttributes, ts))); - - - Mockito.when(attributesService.find(tenantId, originator, DataConstants.SERVER_SCOPE, serverAttributes)) - .thenReturn(Futures.immediateFuture(getListAttributeKvEntry(serverAttributes, ts))); - - - Mockito.when(attributesService.find(tenantId, originator, DataConstants.SHARED_SCOPE, sharedAttributes)) - .thenReturn(Futures.immediateFuture(getListAttributeKvEntry(sharedAttributes, ts))); - - Mockito.when(tsService.findLatest(tenantId, originator, tsKeys)) - .thenReturn(Futures.immediateFuture(getListTsKvEntry(tsKeys, ts))); - } - - @After - public void after() { - dbExecutor.destroy(); - } - - @Test - public void fetchToMetadata_whenOnMsg_then_success() throws Exception { - TbGetAttributesNode node = initNode(false, false, false); - TbMsg msg = getTbMsg(originator); - node.onMsg(ctx, msg); - - // check msg - TbMsg resultMsg = checkMsg(true); - - //check attributes - checkAttributes(resultMsg, false, "cs_", clientAttributes); - checkAttributes(resultMsg, false, "ss_", serverAttributes); - checkAttributes(resultMsg, false, "shared_", sharedAttributes); - - //check timeseries - checkTs(resultMsg, false, false, tsKeys); - } - - @Test - public void fetchToMetadata_latestWithTs_whenOnMsg_then_success() throws Exception { - TbGetAttributesNode node = initNode(false, true, false); - TbMsg msg = getTbMsg(originator); - node.onMsg(ctx, msg); - - // check msg - TbMsg resultMsg = checkMsg(true); - - //check attributes - checkAttributes(resultMsg, false, "cs_", clientAttributes); - checkAttributes(resultMsg, false, "ss_", serverAttributes); - checkAttributes(resultMsg, false, "shared_", sharedAttributes); - - //check timeseries with ts - checkTs(resultMsg, false, true, tsKeys); - } - - @Test - public void fetchToData_whenOnMsg_then_success() throws Exception { - TbGetAttributesNode node = initNode(true, false, false); - TbMsg msg = getTbMsg(originator); - node.onMsg(ctx, msg); - - // check msg - TbMsg resultMsg = checkMsg(true); - - //check attributes - checkAttributes(resultMsg, true, "cs_", clientAttributes); - checkAttributes(resultMsg, true, "ss_", serverAttributes); - checkAttributes(resultMsg, true, "shared_", sharedAttributes); - - //check timeseries - checkTs(resultMsg, true, false, tsKeys); - } - - @Test - public void fetchToData_latestWithTs_whenOnMsg_then_success() throws Exception { - TbGetAttributesNode node = initNode(true, true, false); - TbMsg msg = getTbMsg(originator); - node.onMsg(ctx, msg); - - // check msg - TbMsg resultMsg = checkMsg(true); - - //check attributes - checkAttributes(resultMsg, true, "cs_", clientAttributes); - checkAttributes(resultMsg, true, "ss_", serverAttributes); - checkAttributes(resultMsg, true, "shared_", sharedAttributes); - - //check timeseries with ts - checkTs(resultMsg, true, true, tsKeys); - } - - @Test - public void fetchToMetadata_whenOnMsg_then_failure() throws Exception { - TbGetAttributesNode node = initNode(false, false, true); - TbMsg msg = getTbMsg(originator); - node.onMsg(ctx, msg); - - // check msg - TbMsg actualMsg = checkMsg(false); - - //check attributes - checkAttributes(actualMsg, false, "cs_", clientAttributes); - checkAttributes(actualMsg, false, "ss_", serverAttributes); - checkAttributes(actualMsg, false, "shared_", sharedAttributes); - - //check timeseries with ts - checkTs(actualMsg, false, false, tsKeys); - } - - @Test - public void fetchToData_whenOnMsg_then_failure() throws Exception { - TbGetAttributesNode node = initNode(true, true, true); - TbMsg msg = getTbMsg(originator); - node.onMsg(ctx, msg); - - // check msg - TbMsg actualMsg = checkMsg(false); - - //check attributes - checkAttributes(actualMsg, true, "cs_", clientAttributes); - checkAttributes(actualMsg, true, "ss_", serverAttributes); - checkAttributes(actualMsg, true, "shared_", sharedAttributes); - - //check timeseries with ts - checkTs(actualMsg, true, true, tsKeys); - } - - @Test - public void fetchToData_whenOnMsg_and_data_is_not_object_then_failure() throws Exception { - TbGetAttributesNode node = initNode(true, true, true); - TbMsg msg = TbMsg.newMsg("TEST", originator, new TbMsgMetaData(), "[]"); - node.onMsg(ctx, msg); - - ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); - ArgumentCaptor exceptionCaptor = ArgumentCaptor.forClass(IllegalArgumentException.class); - Mockito.verify(ctx, never()).tellSuccess(any()); - Mockito.verify(ctx, Mockito.timeout(5000)).tellFailure(newMsgCaptor.capture(), exceptionCaptor.capture()); - - Assert.assertSame(msg, newMsgCaptor.getValue()); - Assert.assertNotNull(exceptionCaptor.getValue()); - } - - private TbMsg checkMsg(boolean checkSuccess) { - ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - if (checkSuccess) { - Mockito.verify(ctx, Mockito.timeout(5000)).tellSuccess(msgCaptor.capture()); - } else { - ArgumentCaptor exceptionCaptor = ArgumentCaptor.forClass(RuntimeException.class); - Mockito.verify(ctx, never()).tellSuccess(any()); - Mockito.verify(ctx, Mockito.timeout(5000)).tellFailure(msgCaptor.capture(), exceptionCaptor.capture()); - RuntimeException exception = exceptionCaptor.getValue(); - Assert.assertNotNull(exception); - Assert.assertNotNull(exception.getMessage()); - Assert.assertTrue(exception.getMessage().startsWith("The following attribute/telemetry keys is not present in the DB:")); - } - - TbMsg resultMsg = msgCaptor.getValue(); - Assert.assertNotNull(resultMsg); - Assert.assertNotNull(resultMsg.getMetaData()); - Assert.assertNotNull(resultMsg.getData()); - return resultMsg; - } - - private void checkAttributes(TbMsg actualMsg, boolean fetchToData, String prefix, List attributes) { - JsonNode msgData = JacksonUtil.toJsonNode(actualMsg.getData()); - attributes.stream() - .filter(attribute -> !attribute.equals("unknown")) - .forEach(attribute -> { - String result; - if (fetchToData) { - result = msgData.get(prefix + attribute).asText(); - } else { - result = actualMsg.getMetaData().getValue(prefix + attribute); - } - Assert.assertNotNull(result); - Assert.assertEquals(attribute + "_value", result); - }); - } - - private void checkTs(TbMsg actualMsg, boolean fetchToData, boolean getLatestValueWithTs, List tsKeys) { - JsonNode msgData = JacksonUtil.toJsonNode(actualMsg.getData()); - long value = 1L; - for (String key : tsKeys) { - if (key.equals("unknown")) { - continue; - } - String actualValue; - String expectedValue; - if (getLatestValueWithTs) { - expectedValue = "{\"ts\":" + ts + ",\"value\":{\"data\":" + value + "}}"; - } else { - expectedValue = "{\"data\":" + value + "}"; - } - if (fetchToData) { - actualValue = JacksonUtil.toString(msgData.get(key)); - } else { - actualValue = actualMsg.getMetaData().getValue(key); - } - Assert.assertNotNull(actualValue); - Assert.assertEquals(expectedValue, actualValue); - value++; - } - } - - private TbGetAttributesNode initNode(boolean fetchToData, boolean getLatestValueWithTs, boolean isTellFailureIfAbsent) throws TbNodeException { - TbGetAttributesNodeConfiguration config = new TbGetAttributesNodeConfiguration(); - config.setClientAttributeNames(List.of("client_attr_1", "client_attr_2", "${client_attr_metadata}", "unknown")); - config.setServerAttributeNames(List.of("server_attr_1", "server_attr_2", "${server_attr_metadata}", "unknown")); - config.setSharedAttributeNames(List.of("shared_attr_1", "shared_attr_2", "$[shared_attr_data]", "unknown")); - config.setLatestTsKeyNames(List.of("temperature", "humidity", "unknown")); - config.setFetchToData(fetchToData); - config.setGetLatestValueWithTs(getLatestValueWithTs); - config.setTellFailureIfAbsent(isTellFailureIfAbsent); - TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); - TbGetAttributesNode node = new TbGetAttributesNode(); - node.init(ctx, nodeConfiguration); - return node; - } - - private TbMsg getTbMsg(EntityId entityId) { - ObjectNode msgData = JacksonUtil.newObjectNode(); - msgData.put("shared_attr_data", "shared_attr_3"); - - TbMsgMetaData msgMetaData = new TbMsgMetaData(); - msgMetaData.putValue("client_attr_metadata", "client_attr_3"); - msgMetaData.putValue("server_attr_metadata", "server_attr_3"); - - return TbMsg.newMsg("TEST", entityId, msgMetaData, JacksonUtil.toString(msgData)); - } - - private List getAttributeNames(String prefix) { - return List.of(prefix + "_attr_1", prefix + "_attr_2", prefix + "_attr_3", "unknown"); - } - - private List getListAttributeKvEntry(List attributes, long ts) { - return attributes.stream() - .filter(attribute -> !attribute.equals("unknown")) - .map(attribute -> toAttributeKvEntry(ts, attribute)) - .collect(Collectors.toList()); - } - - private BaseAttributeKvEntry toAttributeKvEntry(long ts, String attribute) { - return new BaseAttributeKvEntry(ts, new StringDataEntry(attribute, attribute + "_value")); - } - - private List getListTsKvEntry(List keys, long ts) { - long value = 1L; - List kvEntries = new ArrayList<>(); - for (String key : keys) { - if (key.equals("unknown")) { - continue; - } - String dataValue = "{\"data\":" + value + "}"; - kvEntries.add(new BasicTsKvEntry(ts, new JsonDataEntry(key, dataValue))); - value++; - } - return kvEntries; - } - -} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeTest.java index c50f6e5dcc..c5044eab37 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNodeTest.java @@ -15,18 +15,27 @@ */ package org.thingsboard.rule.engine.metadata; +import com.fasterxml.jackson.databind.JsonNode; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.util.TbMsgSource; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.common.data.security.DeviceCredentialsType; +import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.queue.TbMsgCallback; @@ -36,46 +45,35 @@ import java.util.Map; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.willAnswer; -import static org.mockito.BDDMockito.willReturn; -import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.thingsboard.server.common.data.security.DeviceCredentialsType.ACCESS_TOKEN; +@ExtendWith(MockitoExtension.class) public class TbFetchDeviceCredentialsNodeTest { - DeviceId deviceId; - TbFetchDeviceCredentialsNode node; - TbFetchDeviceCredentialsNodeConfiguration config; - TbNodeConfiguration nodeConfiguration; - TbContext ctx; - TbMsgCallback callback; - DeviceCredentialsService deviceCredentialsService; + + @Mock + private TbContext ctxMock; + @Mock + private TbMsgCallback callbackMock; + @Mock + private DeviceCredentialsService deviceCredentialsServiceMock; + @Spy + private TbFetchDeviceCredentialsNode node; + private DeviceId deviceId; + private TbFetchDeviceCredentialsNodeConfiguration config; @BeforeEach void setUp() throws TbNodeException { deviceId = new DeviceId(UUID.randomUUID()); - callback = mock(TbMsgCallback.class); - ctx = mock(TbContext.class); config = new TbFetchDeviceCredentialsNodeConfiguration().defaultConfiguration(); - config.setFetchToMetadata(true); - nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); - node = spy(new TbFetchDeviceCredentialsNode()); - node.init(ctx, nodeConfiguration); - deviceCredentialsService = mock(DeviceCredentialsService.class); - - willReturn(deviceCredentialsService).given(ctx).getDeviceCredentialsService(); - willAnswer(invocation -> { - DeviceCredentials deviceCredentials = new DeviceCredentials(); - deviceCredentials.setCredentialsType(ACCESS_TOKEN); - return deviceCredentials; - }).given(deviceCredentialsService).findDeviceCredentialsByDeviceId(any(), any()); - willAnswer(invocation -> { - return JacksonUtil.newObjectNode(); - }).given(deviceCredentialsService).toCredentialsInfo(any()); + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); } @AfterEach @@ -86,25 +84,36 @@ public class TbFetchDeviceCredentialsNodeTest { @Test void givenDefaultConfig_whenInit_thenOK() { assertThat(node.config).isEqualTo(config); - assertThat(node.fetchToMetadata).isEqualTo(true); + assertThat(node.fetchTo).isEqualTo(TbMsgSource.METADATA); } @Test void givenDefaultConfig_whenVerify_thenOK() { - TbFetchDeviceCredentialsNodeConfiguration defaultConfig = new TbFetchDeviceCredentialsNodeConfiguration().defaultConfiguration(); - assertThat(defaultConfig.isFetchToMetadata()).isEqualTo(true); + var defaultConfig = new TbFetchDeviceCredentialsNodeConfiguration().defaultConfiguration(); + assertThat(defaultConfig.getFetchTo()).isEqualTo(TbMsgSource.METADATA); } @Test - void givenMsg_whenOnMsg_thenVerifyOutput() throws Exception { - node.onMsg(ctx, getTbMsg(deviceId)); + void givenValidMsg_whenOnMsg_thenVerifyOutput() throws Exception { + // GIVEN + doReturn(deviceCredentialsServiceMock).when(ctxMock).getDeviceCredentialsService(); + doAnswer(invocation -> { + DeviceCredentials deviceCredentials = new DeviceCredentials(); + deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); + return deviceCredentials; + }).when(deviceCredentialsServiceMock).findDeviceCredentialsByDeviceId(any(), any()); + doAnswer(invocation -> JacksonUtil.newObjectNode()).when(deviceCredentialsServiceMock).toCredentialsInfo(any()); + + // WHEN + node.onMsg(ctxMock, getTbMsg(deviceId)); - ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); - verify(ctx, times(1)).tellSuccess(newMsgCaptor.capture()); - verify(ctx, never()).tellFailure(any(), any()); - verify(deviceCredentialsService, times(1)).findDeviceCredentialsByDeviceId(any(), any()); + // THEN + var newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctxMock, times(1)).tellSuccess(newMsgCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + verify(deviceCredentialsServiceMock, times(1)).findDeviceCredentialsByDeviceId(any(), any()); - TbMsg newMsg = newMsgCaptor.getValue(); + var newMsg = newMsgCaptor.getValue(); assertThat(newMsg).isNotNull(); assertThat(newMsg.getMetaData().getData().containsKey("credentials")).isEqualTo(true); @@ -112,42 +121,59 @@ public class TbFetchDeviceCredentialsNodeTest { } @Test - void givenUnsupportedOriginatorType_whenOnMsg_thenTellFailure() throws Exception { - node.onMsg(ctx, getTbMsg(new CustomerId(UUID.randomUUID()))); + void givenUnsupportedOriginatorType_whenOnMsg_thenShouldTellFailure() throws Exception { + // GIVEN + var randomCustomerId = new CustomerId(UUID.randomUUID()); - ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); - ArgumentCaptor exceptionCaptor = ArgumentCaptor.forClass(Exception.class); - verify(ctx, never()).tellSuccess(any()); - verify(ctx, times(1)).tellFailure(newMsgCaptor.capture(), exceptionCaptor.capture()); + // WHEN + node.onMsg(ctxMock, getTbMsg(randomCustomerId)); + + // THEN + var newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + var exceptionCaptor = ArgumentCaptor.forClass(Exception.class); + verify(ctxMock, never()).tellSuccess(any()); + verify(ctxMock, times(1)).tellFailure(newMsgCaptor.capture(), exceptionCaptor.capture()); assertThat(exceptionCaptor.getValue()).isInstanceOf(RuntimeException.class); } @Test - void givenGetDeviceCredentials_whenOnMsg_thenTellFailure() throws Exception { - willAnswer(invocation -> { - return null; - }).given(deviceCredentialsService).findDeviceCredentialsByDeviceId(any(), any()); + void givenGetDeviceCredentials_whenOnMsg_thenShouldTellFailure() throws Exception { + // GIVEN + doReturn(deviceCredentialsServiceMock).when(ctxMock).getDeviceCredentialsService(); + willAnswer(invocation -> null).given(deviceCredentialsServiceMock).findDeviceCredentialsByDeviceId(any(), any()); - node.onMsg(ctx, getTbMsg(deviceId)); + // WHEN + node.onMsg(ctxMock, getTbMsg(deviceId)); - ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); - ArgumentCaptor exceptionCaptor = ArgumentCaptor.forClass(Exception.class); - verify(ctx, never()).tellSuccess(any()); - verify(ctx, times(1)).tellFailure(newMsgCaptor.capture(), exceptionCaptor.capture()); + // THEN + var newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + var exceptionCaptor = ArgumentCaptor.forClass(Exception.class); + verify(ctxMock, never()).tellSuccess(any()); + verify(ctxMock, times(1)).tellFailure(newMsgCaptor.capture(), exceptionCaptor.capture()); assertThat(exceptionCaptor.getValue()).isInstanceOf(RuntimeException.class); } + @Test + void givenOldConfig_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception { + String oldConfig = "{\"fetchToMetadata\":true}"; + JsonNode configJson = JacksonUtil.toJsonNode(oldConfig); + TbPair upgrade = node.upgrade(0, configJson); + assertTrue(upgrade.getFirst()); + assertEquals(config, JacksonUtil.treeToValue(upgrade.getSecond(), config.getClass())); + } + private TbMsg getTbMsg(EntityId entityId) { final Map mdMap = Map.of( "country", "US", "city", "NY" ); - final TbMsgMetaData metaData = new TbMsgMetaData(mdMap); + final var metaData = new TbMsgMetaData(mdMap); final String data = "{\"TestAttribute_1\": \"humidity\", \"TestAttribute_2\": \"voltage\"}"; - return TbMsg.newMsg("POST_ATTRIBUTES_REQUEST", entityId, metaData, data, callback); + return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, metaData, data, callbackMock); } + } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeTest.java new file mode 100644 index 0000000000..1b8c95ab22 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNodeTest.java @@ -0,0 +1,423 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.metadata; + +import com.datastax.oss.driver.api.core.uuid.Uuids; +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.util.concurrent.Futures; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.thingsboard.common.util.AbstractListeningExecutor; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.util.TbMsgSource; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.JsonDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.util.TbPair; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.timeseries.TimeseriesService; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class TbGetAttributesNodeTest { + + private static final EntityId ORIGINATOR = new DeviceId(Uuids.timeBased()); + private static final TenantId TENANT_ID = TenantId.fromUUID(Uuids.timeBased()); + private AbstractListeningExecutor dbExecutor; + + @Mock + private TbContext ctxMock; + @Mock + private AttributesService attributesServiceMock; + @Mock + private TimeseriesService timeseriesServiceMock; + + private List clientAttributes; + private List serverAttributes; + private List sharedAttributes; + private List tsKeys; + private long ts; + private TbGetAttributesNode node; + + @Before + public void before() throws TbNodeException { + dbExecutor = new AbstractListeningExecutor() { + @Override + protected int getThreadPollSize() { + return 3; + } + }; + dbExecutor.init(); + + when(ctxMock.getAttributesService()).thenReturn(attributesServiceMock); + when(ctxMock.getTimeseriesService()).thenReturn(timeseriesServiceMock); + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + when(ctxMock.getDbCallbackExecutor()).thenReturn(dbExecutor); + + clientAttributes = getAttributeNames("client"); + serverAttributes = getAttributeNames("server"); + sharedAttributes = getAttributeNames("shared"); + tsKeys = List.of("temperature", "humidity", "unknown"); + ts = System.currentTimeMillis(); + + when(attributesServiceMock.find(TENANT_ID, ORIGINATOR, DataConstants.CLIENT_SCOPE, clientAttributes)) + .thenReturn(Futures.immediateFuture(getListAttributeKvEntry(clientAttributes, ts))); + + when(attributesServiceMock.find(TENANT_ID, ORIGINATOR, DataConstants.SERVER_SCOPE, serverAttributes)) + .thenReturn(Futures.immediateFuture(getListAttributeKvEntry(serverAttributes, ts))); + + when(attributesServiceMock.find(TENANT_ID, ORIGINATOR, DataConstants.SHARED_SCOPE, sharedAttributes)) + .thenReturn(Futures.immediateFuture(getListAttributeKvEntry(sharedAttributes, ts))); + + when(timeseriesServiceMock.findLatest(TENANT_ID, ORIGINATOR, tsKeys)) + .thenReturn(Futures.immediateFuture(getListTsKvEntry(tsKeys, ts))); + } + + @After + public void after() { + dbExecutor.destroy(); + } + + @Test + public void givenFetchAttributesToMetadata_whenOnMsg_thenShouldTellSuccess() throws Exception { + // GIVEN + node = initNode(TbMsgSource.METADATA, false, false); + var msg = getTbMsg(ORIGINATOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var resultMsg = checkMsg(true); + + checkAttributes(resultMsg, TbMsgSource.METADATA, "cs_", clientAttributes); + checkAttributes(resultMsg, TbMsgSource.METADATA, "ss_", serverAttributes); + checkAttributes(resultMsg, TbMsgSource.METADATA, "shared_", sharedAttributes); + + checkTs(resultMsg, TbMsgSource.METADATA, false, tsKeys); + } + + @Test + public void givenFetchLatestTimeseriesToMetadata_whenOnMsg_thenShouldTellSuccess() throws Exception { + // GIVEN + node = initNode(TbMsgSource.METADATA, true, false); + var msg = getTbMsg(ORIGINATOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var resultMsg = checkMsg(true); + + checkAttributes(resultMsg, TbMsgSource.METADATA, "cs_", clientAttributes); + checkAttributes(resultMsg, TbMsgSource.METADATA, "ss_", serverAttributes); + checkAttributes(resultMsg, TbMsgSource.METADATA, "shared_", sharedAttributes); + + checkTs(resultMsg, TbMsgSource.METADATA, true, tsKeys); + } + + @Test + public void givenFetchAttributesToData_whenOnMsg_thenShouldTellSuccess() throws Exception { + // GIVEN + node = initNode(TbMsgSource.DATA, false, false); + var msg = getTbMsg(ORIGINATOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var resultMsg = checkMsg(true); + + checkAttributes(resultMsg, TbMsgSource.DATA, "cs_", clientAttributes); + checkAttributes(resultMsg, TbMsgSource.DATA, "ss_", serverAttributes); + checkAttributes(resultMsg, TbMsgSource.DATA, "shared_", sharedAttributes); + + checkTs(resultMsg, TbMsgSource.DATA, false, tsKeys); + } + + @Test + public void givenFetchLatestTimeseriesToData_whenOnMsg_thenShouldTellSuccess() throws Exception { + // GIVEN + node = initNode(TbMsgSource.DATA, true, false); + var msg = getTbMsg(ORIGINATOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var resultMsg = checkMsg(true); + + checkAttributes(resultMsg, TbMsgSource.DATA, "cs_", clientAttributes); + checkAttributes(resultMsg, TbMsgSource.DATA, "ss_", serverAttributes); + checkAttributes(resultMsg, TbMsgSource.DATA, "shared_", sharedAttributes); + + checkTs(resultMsg, TbMsgSource.DATA, true, tsKeys); + } + + @Test + public void givenFetchAttributesToMetadata_whenOnMsg_thenShouldTellFailure() throws Exception { + // GIVEN + node = initNode(TbMsgSource.METADATA, false, true); + var msg = getTbMsg(ORIGINATOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMsg = checkMsg(false); + + checkAttributes(actualMsg, TbMsgSource.METADATA, "cs_", clientAttributes); + checkAttributes(actualMsg, TbMsgSource.METADATA, "ss_", serverAttributes); + checkAttributes(actualMsg, TbMsgSource.METADATA, "shared_", sharedAttributes); + + checkTs(actualMsg, TbMsgSource.METADATA, false, tsKeys); + } + + @Test + public void givenFetchLatestTimeseriesToData_whenOnMsg_thenShouldTellFailure() throws Exception { + // GIVEN + node = initNode(TbMsgSource.DATA, true, true); + var msg = getTbMsg(ORIGINATOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMsg = checkMsg(false); + + checkAttributes(actualMsg, TbMsgSource.DATA, "cs_", clientAttributes); + checkAttributes(actualMsg, TbMsgSource.DATA, "ss_", serverAttributes); + checkAttributes(actualMsg, TbMsgSource.DATA, "shared_", sharedAttributes); + + checkTs(actualMsg, TbMsgSource.DATA, true, tsKeys); + } + + @Test + public void givenFetchLatestTimeseriesToDataAndDataIsNotJsonObject_whenOnMsg_thenException() throws Exception { + // GIVEN + node = initNode(TbMsgSource.DATA, true, true); + var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + + // WHEN + var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); + + // THEN + verify(ctxMock, never()).tellSuccess(any()); + assertThat(exception.getMessage()).isEqualTo("Message body is not an object!"); + } + + @Test + public void givenOldConfig_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception { + var defaultConfig = new TbGetAttributesNodeConfiguration().defaultConfiguration(); + var node = new TbGetAttributesNode(); + String oldConfig = "{\"fetchToData\":false," + + "\"clientAttributeNames\":[]," + + "\"sharedAttributeNames\":[]," + + "\"serverAttributeNames\":[]," + + "\"latestTsKeyNames\":[]," + + "\"tellFailureIfAbsent\":true," + + "\"getLatestValueWithTs\":false}"; + JsonNode configJson = JacksonUtil.toJsonNode(oldConfig); + TbPair upgrade = node.upgrade(0, configJson); + Assertions.assertTrue(upgrade.getFirst()); + Assertions.assertEquals(defaultConfig, JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass())); + } + + @Test + public void givenOldConfigWithNoFetchToDataProperty_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception { + var defaultConfig = new TbGetAttributesNodeConfiguration().defaultConfiguration(); + var node = new TbGetAttributesNode(); + String oldConfig = "{\"clientAttributeNames\":[]," + + "\"sharedAttributeNames\":[]," + + "\"serverAttributeNames\":[]," + + "\"latestTsKeyNames\":[]," + + "\"tellFailureIfAbsent\":true," + + "\"getLatestValueWithTs\":false}"; + JsonNode configJson = JacksonUtil.toJsonNode(oldConfig); + TbPair upgrade = node.upgrade(0, configJson); + Assertions.assertTrue(upgrade.getFirst()); + Assertions.assertEquals(defaultConfig, JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass())); + } + + @Test + public void givenOldConfigWithNullFetchToDataProperty_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception { + var defaultConfig = new TbGetAttributesNodeConfiguration().defaultConfiguration(); + var node = new TbGetAttributesNode(); + String oldConfig = "{\"fetchToData\":null," + + "\"clientAttributeNames\":[]," + + "\"sharedAttributeNames\":[]," + + "\"serverAttributeNames\":[]," + + "\"latestTsKeyNames\":[]," + + "\"tellFailureIfAbsent\":true," + + "\"getLatestValueWithTs\":false}"; + JsonNode configJson = JacksonUtil.toJsonNode(oldConfig); + TbPair upgrade = node.upgrade(0, configJson); + Assertions.assertTrue(upgrade.getFirst()); + Assertions.assertEquals(defaultConfig, JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass())); + } + + private TbMsg checkMsg(boolean checkSuccess) { + var msgCaptor = ArgumentCaptor.forClass(TbMsg.class); + if (checkSuccess) { + verify(ctxMock, timeout(5000)).tellSuccess(msgCaptor.capture()); + } else { + var exceptionCaptor = ArgumentCaptor.forClass(RuntimeException.class); + verify(ctxMock, never()).tellSuccess(any()); + verify(ctxMock, timeout(5000)).tellFailure(msgCaptor.capture(), exceptionCaptor.capture()); + var exception = exceptionCaptor.getValue(); + assertNotNull(exception); + assertNotNull(exception.getMessage()); + assertTrue(exception.getMessage().startsWith("The following attribute/telemetry keys is not present in the DB:")); + } + + var resultMsg = msgCaptor.getValue(); + assertNotNull(resultMsg); + assertNotNull(resultMsg.getMetaData()); + assertNotNull(resultMsg.getData()); + return resultMsg; + } + + private void checkAttributes(TbMsg actualMsg, TbMsgSource fetchTo, String prefix, List attributes) { + var msgData = JacksonUtil.toJsonNode(actualMsg.getData()); + attributes.stream() + .filter(attribute -> !attribute.equals("unknown")) + .forEach(attribute -> { + String result = null; + if (TbMsgSource.DATA.equals(fetchTo)) { + result = msgData.get(prefix + attribute).asText(); + } else if (TbMsgSource.METADATA.equals(fetchTo)) { + result = actualMsg.getMetaData().getValue(prefix + attribute); + } + assertNotNull(result); + assertEquals(attribute + "_value", result); + }); + } + + private void checkTs(TbMsg actualMsg, TbMsgSource fetchTo, boolean getLatestValueWithTs, List tsKeys) { + var msgData = JacksonUtil.toJsonNode(actualMsg.getData()); + long value = 1L; + for (var key : tsKeys) { + if (key.equals("unknown")) { + continue; + } + String actualValue = null; + String expectedValue; + if (getLatestValueWithTs) { + expectedValue = "{\"ts\":" + ts + ",\"value\":{\"data\":" + value + "}}"; + } else { + expectedValue = "{\"data\":" + value + "}"; + } + if (TbMsgSource.DATA.equals(fetchTo)) { + actualValue = JacksonUtil.toString(msgData.get(key)); + } else if (TbMsgSource.METADATA.equals(fetchTo)) { + actualValue = actualMsg.getMetaData().getValue(key); + } + assertNotNull(actualValue); + assertEquals(expectedValue, actualValue); + value++; + } + } + + private TbGetAttributesNode initNode(TbMsgSource fetchTo, boolean getLatestValueWithTs, boolean isTellFailureIfAbsent) throws TbNodeException { + var config = new TbGetAttributesNodeConfiguration(); + config.setClientAttributeNames(List.of("client_attr_1", "client_attr_2", "${client_attr_metadata}", "unknown")); + config.setServerAttributeNames(List.of("server_attr_1", "server_attr_2", "${server_attr_metadata}", "unknown")); + config.setSharedAttributeNames(List.of("shared_attr_1", "shared_attr_2", "$[shared_attr_data]", "unknown")); + config.setLatestTsKeyNames(List.of("temperature", "humidity", "unknown")); + config.setFetchTo(fetchTo); + config.setGetLatestValueWithTs(getLatestValueWithTs); + config.setTellFailureIfAbsent(isTellFailureIfAbsent); + + var nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + var node = new TbGetAttributesNode(); + node.init(ctxMock, nodeConfiguration); + return node; + } + + private TbMsg getTbMsg(EntityId entityId) { + var msgData = JacksonUtil.newObjectNode(); + msgData.put("shared_attr_data", "shared_attr_3"); + + var msgMetaData = new TbMsgMetaData(); + msgMetaData.putValue("client_attr_metadata", "client_attr_3"); + msgMetaData.putValue("server_attr_metadata", "server_attr_3"); + + return TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, entityId, msgMetaData, JacksonUtil.toString(msgData)); + } + + private List getAttributeNames(String prefix) { + return List.of(prefix + "_attr_1", prefix + "_attr_2", prefix + "_attr_3", "unknown"); + } + + private List getListAttributeKvEntry(List attributesList, long ts) { + return attributesList.stream() + .filter(attribute -> !attribute.equals("unknown")) + .map(attribute -> toAttributeKvEntry(ts, attribute)) + .collect(Collectors.toList()); + } + + private BaseAttributeKvEntry toAttributeKvEntry(long ts, String attribute) { + return new BaseAttributeKvEntry(ts, new StringDataEntry(attribute, attribute + "_value")); + } + + private List getListTsKvEntry(List keysList, long ts) { + long value = 1L; + var kvEntriesList = new ArrayList(); + for (var key : keysList) { + if (key.equals("unknown")) { + continue; + } + String dataValue = "{\"data\":" + value + "}"; + kvEntriesList.add(new BasicTsKvEntry(ts, new JsonDataEntry(key, dataValue))); + value++; + } + return kvEntriesList; + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java index 10b19add34..d200ec70d8 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNodeTest.java @@ -15,101 +15,479 @@ */ package org.thingsboard.rule.engine.metadata; -import com.datastax.oss.driver.api.core.uuid.Uuids; +import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.Futures; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.ListeningExecutor; +import org.thingsboard.rule.engine.TestDbCallbackExecutor; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.util.TbMsgSource; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.util.TbPair; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.timeseries.TimeseriesService; +import org.thingsboard.server.dao.user.UserService; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; import java.util.UUID; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) -public class TbGetCustomerAttributeNodeTest extends AbstractAttributeNodeTest { - User user = new User(); - Asset asset = new Asset(); - Device device = new Device(); +@ExtendWith(MockitoExtension.class) +public class TbGetCustomerAttributeNodeTest { - @Before - public void initDataForTests() throws TbNodeException { - init(new TbGetCustomerAttributeNode()); - user.setCustomerId(customerId); - user.setId(new UserId(UUID.randomUUID())); + private static final DeviceId DUMMY_DEVICE_ORIGINATOR = new DeviceId(UUID.randomUUID()); + private static final TenantId TENANT_ID = new TenantId(UUID.randomUUID()); + private static final CustomerId CUSTOMER_ID = new CustomerId(UUID.randomUUID()); + private static final ListeningExecutor DB_EXECUTOR = new TestDbCallbackExecutor(); + @Mock + private TbContext ctxMock; + @Mock + private AttributesService attributesServiceMock; + @Mock + private TimeseriesService timeseriesServiceMock; + @Mock + private UserService userServiceMock; + @Mock + private AssetService assetServiceMock; + @Mock + private DeviceService deviceServiceMock; + private TbGetCustomerAttributeNode node; + private TbGetEntityDataNodeConfiguration config; + private TbNodeConfiguration nodeConfiguration; + private TbMsg msg; - asset.setCustomerId(customerId); - asset.setId(new AssetId(UUID.randomUUID())); + @BeforeEach + public void setUp() { + node = new TbGetCustomerAttributeNode(); + config = new TbGetEntityDataNodeConfiguration().defaultConfiguration(); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + } + + @Test + public void givenConfigWithNullFetchTo_whenInit_thenException() { + // GIVEN + config.setFetchTo(null); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); - device.setCustomerId(customerId); - device.setId(new DeviceId(Uuids.timeBased())); + // WHEN + var exception = assertThrows(TbNodeException.class, () -> node.init(ctxMock, nodeConfiguration)); + + // THEN + assertThat(exception.getMessage()).isEqualTo("FetchTo option can't be null! Allowed values: " + Arrays.toString(TbMsgSource.values())); + verify(ctxMock, never()).tellSuccess(any()); } - @Override - protected TbEntityGetAttrNode getEmptyNode() { - return new TbGetCustomerAttributeNode(); + @Test + public void givenConfigWithNullDataToFetch_whenInit_thenException() { + // GIVEN + config.setDataToFetch(null); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + + // WHEN + var exception = assertThrows(TbNodeException.class, () -> node.init(ctxMock, nodeConfiguration)); + + // THEN + assertThat(exception.getMessage()).isEqualTo("DataToFetch property has invalid value: null. Only ATTRIBUTES and LATEST_TELEMETRY values supported!"); + verify(ctxMock, never()).tellSuccess(any()); } - @Override - EntityId getEntityId() { - return customerId; + @Test + public void givenConfigWithUnsupportedDataToFetch_whenInit_thenException() { + // GIVEN + config.setDataToFetch(DataToFetch.FIELDS); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + + // WHEN + var exception = assertThrows(TbNodeException.class, () -> node.init(ctxMock, nodeConfiguration)); + + // THEN + assertThat(exception.getMessage()).isEqualTo("DataToFetch property has invalid value: FIELDS. Only ATTRIBUTES and LATEST_TELEMETRY values supported!"); + verify(ctxMock, never()).tellSuccess(any()); } @Test - public void errorThrownIfCannotLoadAttributes() { - mockFindUser(user); - errorThrownIfCannotLoadAttributes(user); + public void givenDefaultConfig_whenInit_thenOK() throws TbNodeException { + // GIVEN + + // WHEN + node.init(ctxMock, nodeConfiguration); + + // THEN + assertThat(node.config).isEqualTo(config); + assertThat(config.getDataMapping()).isEqualTo(Map.of("alarmThreshold", "threshold")); + assertThat(config.getDataToFetch()).isEqualTo(DataToFetch.ATTRIBUTES); + assertThat(node.fetchTo).isEqualTo(TbMsgSource.METADATA); } @Test - public void errorThrownIfCannotLoadAttributesAsync() { - mockFindUser(user); - errorThrownIfCannotLoadAttributesAsync(user); + public void givenCustomConfig_whenInit_thenOK() throws TbNodeException { + // GIVEN + config.setDataMapping(Map.of( + "sourceAttr1", "targetKey1", + "sourceAttr2", "targetKey2", + "sourceAttr3", "targetKey3")); + config.setDataToFetch(DataToFetch.LATEST_TELEMETRY); + config.setFetchTo(TbMsgSource.DATA); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + + // WHEN + node.init(ctxMock, nodeConfiguration); + + // THEN + assertThat(node.config).isEqualTo(config); + assertThat(config.getDataMapping()).isEqualTo(Map.of( + "sourceAttr1", "targetKey1", + "sourceAttr2", "targetKey2", + "sourceAttr3", "targetKey3")); + assertThat(config.getDataToFetch()).isEqualTo(DataToFetch.LATEST_TELEMETRY); + assertThat(node.fetchTo).isEqualTo(TbMsgSource.DATA); } @Test - public void failedChainUsedIfCustomerCannotBeFound() { - when(ctx.getUserService()).thenReturn(userService); - when(userService.findUserByIdAsync(any(), eq(user.getId()))).thenReturn(Futures.immediateFuture(null)); - failedChainUsedIfCustomerCannotBeFound(user); + public void givenEmptyAttributesMapping_whenInit_thenException() { + // GIVEN + var expectedExceptionMessage = "At least one mapping entry should be specified!"; + + config.setDataMapping(Collections.emptyMap()); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + + // WHEN + var exception = assertThrows(TbNodeException.class, () -> node.init(ctxMock, nodeConfiguration)); + + // THEN + assertThat(exception.getMessage()).isEqualTo(expectedExceptionMessage); + verify(ctxMock, never()).tellSuccess(any()); } @Test - public void customerAttributeAddedInMetadata() { - entityAttributeAddedInMetadata(customerId, "CUSTOMER"); + public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { + // GIVEN + node.fetchTo = TbMsgSource.DATA; + msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + + // WHEN + var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); + + // THEN + assertThat(exception.getMessage()).isEqualTo("Message body is not an object!"); + verify(ctxMock, never()).tellSuccess(any()); + } + + @Test + public void givenDidNotFindEntity_whenOnMsg_thenShouldTellFailure() { + // GIVEN + var userId = new UserId(UUID.randomUUID()); + + msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, userId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + + when(ctxMock.getUserService()).thenReturn(userServiceMock); + doReturn(Futures.immediateFuture(null)).when(userServiceMock).findUserByIdAsync(eq(TENANT_ID), eq(userId)); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + var actualExceptionCaptor = ArgumentCaptor.forClass(Throwable.class); + + verify(ctxMock, never()).tellSuccess(any()); + verify(ctxMock, times(1)) + .tellFailure(actualMessageCaptor.capture(), actualExceptionCaptor.capture()); + + var actualMessage = actualMessageCaptor.getValue(); + var actualException = actualExceptionCaptor.getValue(); + + var expectedExceptionMessage = String.format( + "Failed to find customer for entity with id: %s and type: %s", + userId.getId(), userId.getEntityType().getNormalName()); + + assertEquals(msg, actualMessage); + assertEquals(expectedExceptionMessage, actualException.getMessage()); + assertInstanceOf(NoSuchElementException.class, actualException); + } + + @Test + public void givenFetchAttributesToData_whenOnMsg_thenShouldFetchAttributesToData() { + // GIVEN + var device = new Device(new DeviceId(UUID.randomUUID())); + device.setCustomerId(CUSTOMER_ID); + + prepareMsgAndConfig(TbMsgSource.DATA, DataToFetch.ATTRIBUTES, device.getId()); + + List attributesList = List.of( + new BaseAttributeKvEntry(new StringDataEntry("sourceKey1", "sourceValue1"), 1L), + new BaseAttributeKvEntry(new StringDataEntry("sourceKey2", "sourceValue2"), 2L), + new BaseAttributeKvEntry(new StringDataEntry("sourceKey3", "sourceValue3"), 3L) + ); + var expectedPatternProcessedKeysList = List.of("sourceKey1", "sourceKey2", "sourceKey3"); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + + when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); + doReturn(device).when(deviceServiceMock).findDeviceById(eq(TENANT_ID), eq(device.getId())); + + when(ctxMock.getAttributesService()).thenReturn(attributesServiceMock); + when(attributesServiceMock.find(eq(TENANT_ID), eq(CUSTOMER_ID), eq(DataConstants.SERVER_SCOPE), argThat(new ListMatcher<>(expectedPatternProcessedKeysList)))) + .thenReturn(Futures.immediateFuture(attributesList)); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgData = "{\"temp\":42," + + "\"humidity\":77," + + "\"messageBodyPattern1\":\"targetKey2\"," + + "\"messageBodyPattern2\":\"sourceKey3\"," + + "\"targetKey1\":\"sourceValue1\"," + + "\"targetKey2\":\"sourceValue2\"," + + "\"targetKey3\":\"sourceValue3\"}"; + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(expectedMsgData); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(msg.getMetaData()); } @Test - public void usersCustomerAttributesFetched() { - mockFindUser(user); - usersCustomerAttributesFetched(user); + public void givenFetchAttributesToMetaData_whenOnMsg_thenShouldFetchAttributesToMetaData() { + // GIVEN + var user = new User(new UserId(UUID.randomUUID())); + user.setCustomerId(CUSTOMER_ID); + + prepareMsgAndConfig(TbMsgSource.METADATA, DataToFetch.ATTRIBUTES, user.getId()); + + List attributesList = List.of( + new BaseAttributeKvEntry(new StringDataEntry("sourceKey1", "sourceValue1"), 1L), + new BaseAttributeKvEntry(new StringDataEntry("sourceKey2", "sourceValue2"), 2L), + new BaseAttributeKvEntry(new StringDataEntry("sourceKey3", "sourceValue3"), 3L) + ); + var expectedPatternProcessedKeysList = List.of("sourceKey1", "sourceKey2", "sourceKey3"); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + + when(ctxMock.getUserService()).thenReturn(userServiceMock); + doReturn(Futures.immediateFuture(user)).when(userServiceMock).findUserByIdAsync(eq(TENANT_ID), eq(user.getId())); + + when(ctxMock.getAttributesService()).thenReturn(attributesServiceMock); + when(attributesServiceMock.find(eq(TENANT_ID), eq(CUSTOMER_ID), eq(DataConstants.SERVER_SCOPE), argThat(new ListMatcher<>(expectedPatternProcessedKeysList)))) + .thenReturn(Futures.immediateFuture(attributesList)); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgMetaData = new TbMsgMetaData(Map.of( + "metaDataPattern1", "sourceKey2", + "metaDataPattern2", "targetKey3", + "targetKey1", "sourceValue1", + "targetKey2", "sourceValue2", + "targetKey3", "sourceValue3" + )); + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(msg.getData()); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(expectedMsgMetaData); } @Test - public void assetsCustomerAttributesFetched() { - mockFindAsset(asset); - assetsCustomerAttributesFetched(asset); + public void givenFetchTelemetryToData_whenOnMsg_thenShouldFetchTelemetryToData() { + // GIVEN + var customer = new Customer(new CustomerId(UUID.randomUUID())); + + prepareMsgAndConfig(TbMsgSource.DATA, DataToFetch.LATEST_TELEMETRY, customer.getId()); + + List timeseriesList = List.of( + new BasicTsKvEntry(1L, new StringDataEntry("sourceKey1", "sourceValue1")), + new BasicTsKvEntry(1L, new StringDataEntry("sourceKey2", "sourceValue2")), + new BasicTsKvEntry(1L, new StringDataEntry("sourceKey3", "sourceValue3")) + ); + var expectedPatternProcessedKeysList = List.of("sourceKey1", "sourceKey2", "sourceKey3"); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + + when(ctxMock.getTimeseriesService()).thenReturn(timeseriesServiceMock); + when(timeseriesServiceMock.findLatest(eq(TENANT_ID), eq(customer.getId()), argThat(new ListMatcher<>(expectedPatternProcessedKeysList)))) + .thenReturn(Futures.immediateFuture(timeseriesList)); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgData = "{\"temp\":42," + + "\"humidity\":77," + + "\"messageBodyPattern1\":\"targetKey2\"," + + "\"messageBodyPattern2\":\"sourceKey3\"," + + "\"targetKey1\":\"sourceValue1\"," + + "\"targetKey2\":\"sourceValue2\"," + + "\"targetKey3\":\"sourceValue3\"}"; + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(expectedMsgData); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(msg.getMetaData()); } @Test - public void deviceCustomerAttributesFetched() { - mockFindDevice(device); - deviceCustomerAttributesFetched(device); + public void givenFetchTelemetryToMetaData_whenOnMsg_thenShouldFetchTelemetryToMetaData() { + // GIVEN + var asset = new Asset(new AssetId(UUID.randomUUID())); + asset.setCustomerId(new CustomerId(UUID.randomUUID())); + + prepareMsgAndConfig(TbMsgSource.METADATA, DataToFetch.LATEST_TELEMETRY, asset.getId()); + + List timeseriesList = List.of( + new BasicTsKvEntry(1L, new StringDataEntry("sourceKey1", "sourceValue1")), + new BasicTsKvEntry(1L, new StringDataEntry("sourceKey2", "sourceValue2")), + new BasicTsKvEntry(1L, new StringDataEntry("sourceKey3", "sourceValue3")) + ); + var expectedPatternProcessedKeysList = List.of("sourceKey1", "sourceKey2", "sourceKey3"); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + + when(ctxMock.getAssetService()).thenReturn(assetServiceMock); + doReturn(Futures.immediateFuture(asset)).when(assetServiceMock).findAssetByIdAsync(eq(TENANT_ID), eq(asset.getId())); + + when(ctxMock.getTimeseriesService()).thenReturn(timeseriesServiceMock); + when(timeseriesServiceMock.findLatest(eq(TENANT_ID), eq(asset.getCustomerId()), argThat(new ListMatcher<>(expectedPatternProcessedKeysList)))) + .thenReturn(Futures.immediateFuture(timeseriesList)); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgMetaData = new TbMsgMetaData(Map.of( + "metaDataPattern1", "sourceKey2", + "metaDataPattern2", "targetKey3", + "targetKey1", "sourceValue1", + "targetKey2", "sourceValue2", + "targetKey3", "sourceValue3" + )); + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(msg.getData()); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(expectedMsgMetaData); } @Test - public void deviceCustomerTelemetryFetched() throws TbNodeException { - mockFindDevice(device); - deviceCustomerTelemetryFetched(device); + public void givenOldConfig_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception { + var defaultConfig = new TbGetEntityDataNodeConfiguration().defaultConfiguration(); + var node = new TbGetCustomerAttributeNode(); + String oldConfig = "{\"attrMapping\":{\"alarmThreshold\":\"threshold\"},\"telemetry\":false}"; + JsonNode configJson = JacksonUtil.toJsonNode(oldConfig); + TbPair upgrade = node.upgrade(0, configJson); + Assertions.assertTrue(upgrade.getFirst()); + Assertions.assertEquals(defaultConfig, JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass())); + } + + private void prepareMsgAndConfig(TbMsgSource fetchTo, DataToFetch dataToFetch, EntityId originator) { + config.setDataMapping(Map.of( + "sourceKey1", "targetKey1", + "${metaDataPattern1}", "$[messageBodyPattern1]", + "$[messageBodyPattern2]", "${metaDataPattern2}")); + config.setDataToFetch(dataToFetch); + config.setFetchTo(fetchTo); + + node.config = config; + node.fetchTo = fetchTo; + + var msgMetaData = new TbMsgMetaData(); + msgMetaData.putValue("metaDataPattern1", "sourceKey2"); + msgMetaData.putValue("metaDataPattern2", "targetKey3"); + + var msgData = "{\"temp\":42,\"humidity\":77,\"messageBodyPattern1\":\"targetKey2\",\"messageBodyPattern2\":\"sourceKey3\"}"; + + msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, msgMetaData, msgData); } + + @RequiredArgsConstructor + private static class ListMatcher implements ArgumentMatcher> { + + private final List expectedList; + + @Override + public boolean matches(List actualList) { + if (actualList == expectedList) { + return true; + } + if (actualList.size() != expectedList.size()) { + return false; + } + return actualList.containsAll(expectedList); + } + + } + } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeTest.java new file mode 100644 index 0000000000..16db712ef9 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNodeTest.java @@ -0,0 +1,470 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.metadata; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.util.concurrent.Futures; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.ListeningExecutor; +import org.thingsboard.rule.engine.TestDbCallbackExecutor; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.util.ContactBasedEntityDetails; +import org.thingsboard.rule.engine.util.TbMsgSource; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.util.TbPair; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.customer.CustomerService; +import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.edge.EdgeService; +import org.thingsboard.server.dao.entityview.EntityViewService; +import org.thingsboard.server.dao.user.UserService; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class TbGetCustomerDetailsNodeTest { + + private static final DeviceId DUMMY_DEVICE_ORIGINATOR = new DeviceId(UUID.randomUUID()); + private static final TenantId TENANT_ID = new TenantId(UUID.randomUUID()); + private static final ListeningExecutor DB_EXECUTOR = new TestDbCallbackExecutor(); + @Mock + private TbContext ctxMock; + @Mock + private CustomerService customerServiceMock; + @Mock + private DeviceService deviceServiceMock; + @Mock + private AssetService assetServiceMock; + @Mock + private EntityViewService entityViewServiceMock; + @Mock + private UserService userServiceMock; + @Mock + private EdgeService edgeServiceMock; + private TbGetCustomerDetailsNode node; + private TbGetCustomerDetailsNodeConfiguration config; + private TbNodeConfiguration nodeConfiguration; + private TbMsg msg; + private Customer customer; + + @BeforeEach + public void setUp() { + node = new TbGetCustomerDetailsNode(); + config = new TbGetCustomerDetailsNodeConfiguration().defaultConfiguration(); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + customer = new Customer(); + customer.setId(new CustomerId(UUID.randomUUID())); + customer.setTitle("Customer title"); + customer.setCountry("Customer country"); + customer.setCity("Customer city"); + customer.setState("Customer state"); + customer.setZip("123456"); + customer.setAddress("Customer address 1"); + customer.setAddress2("Customer address 2"); + customer.setPhone("+123456789"); + customer.setEmail("email@tenant.com"); + customer.setAdditionalInfo(JacksonUtil.toJsonNode("{\"someProperty\":\"someValue\",\"description\":\"Customer description\"}")); + } + + @Test + public void givenConfigWithNullFetchTo_whenInit_thenException() { + // GIVEN + config.setDetailsList(List.of(ContactBasedEntityDetails.ID)); + config.setFetchTo(null); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + + // WHEN + var exception = assertThrows(TbNodeException.class, () -> node.init(ctxMock, nodeConfiguration)); + + // THEN + assertThat(exception.getMessage()).isEqualTo("FetchTo option can't be null! Allowed values: " + Arrays.toString(TbMsgSource.values())); + verify(ctxMock, never()).tellSuccess(any()); + } + + @Test + public void givenDefaultConfig_whenInit_thenOK() { + assertThat(config.getDetailsList()).isEqualTo(Collections.emptyList()); + assertThat(config.getFetchTo()).isEqualTo(TbMsgSource.DATA); + } + + @Test + public void givenCustomConfig_whenInit_thenOK() throws TbNodeException { + // GIVEN + config.setDetailsList(List.of(ContactBasedEntityDetails.ID, ContactBasedEntityDetails.PHONE)); + config.setFetchTo(TbMsgSource.METADATA); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + + // WHEN + node.init(ctxMock, nodeConfiguration); + + // THEN + assertThat(node.config).isEqualTo(config); + assertThat(config.getDetailsList()).isEqualTo(List.of(ContactBasedEntityDetails.ID, ContactBasedEntityDetails.PHONE)); + assertThat(config.getFetchTo()).isEqualTo(TbMsgSource.METADATA); + assertThat(node.fetchTo).isEqualTo(TbMsgSource.METADATA); + } + + @Test + public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { + // GIVEN + node.fetchTo = TbMsgSource.DATA; + msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + + // WHEN + var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); + + // THEN + assertThat(exception.getMessage()).isEqualTo("Message body is not an object!"); + verify(ctxMock, never()).tellSuccess(any()); + } + + @Test + public void givenAllEntityDetailsAndFetchToData_whenOnMsg_thenShouldTellSuccessAndFetchAllToData() { + // GIVEN + var device = new Device(); + device.setId(new DeviceId(UUID.randomUUID())); + device.setCustomerId(customer.getId()); + + prepareMsgAndConfig(TbMsgSource.DATA, List.of(ContactBasedEntityDetails.values()), device.getId()); + + when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); + when(deviceServiceMock.findDeviceByIdAsync(eq(TENANT_ID), eq(device.getId()))).thenReturn(Futures.immediateFuture(device)); + + mockFindCustomer(); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgData = "{\"dataKey1\":123,\"dataKey2\":\"dataValue2\"," + + "\"customer_id\":\"" + customer.getId() + "\"," + + "\"customer_title\":\"" + customer.getTitle() + "\"," + + "\"customer_country\":\"" + customer.getCountry() + "\"," + + "\"customer_city\":\"" + customer.getCity() + "\"," + + "\"customer_state\":\"" + customer.getState() + "\"," + + "\"customer_zip\":\"" + customer.getZip() + "\"," + + "\"customer_address\":\"" + customer.getAddress() + "\"," + + "\"customer_address2\":\"" + customer.getAddress2() + "\"," + + "\"customer_phone\":\"" + customer.getPhone() + "\"," + + "\"customer_email\":\"" + customer.getEmail() + "\"," + + "\"customer_additionalInfo\":\"" + customer.getAdditionalInfo().get("description").asText() + "\"}"; + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(expectedMsgData); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(msg.getMetaData()); + } + + @Test + public void givenSomeEntityDetailsAndFetchToMetadata_whenOnMsg_thenShouldTellSuccessAndFetchSomeToMetaData() { + // GIVEN + var asset = new Asset(); + asset.setId(new AssetId(UUID.randomUUID())); + asset.setCustomerId(customer.getId()); + + prepareMsgAndConfig(TbMsgSource.METADATA, List.of(ContactBasedEntityDetails.ID, ContactBasedEntityDetails.TITLE, ContactBasedEntityDetails.PHONE), asset.getId()); + + when(ctxMock.getAssetService()).thenReturn(assetServiceMock); + when(assetServiceMock.findAssetByIdAsync(eq(TENANT_ID), eq(asset.getId()))).thenReturn(Futures.immediateFuture(asset)); + + mockFindCustomer(); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgMetaData = new TbMsgMetaData(msg.getMetaData().getData()); + expectedMsgMetaData.putValue("customer_id", customer.getId().getId().toString()); + expectedMsgMetaData.putValue("customer_title", customer.getTitle()); + expectedMsgMetaData.putValue("customer_phone", customer.getPhone()); + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(msg.getData()); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(expectedMsgMetaData); + } + + @Test + public void givenNotPresentEntityDetailsAndFetchToData_whenOnMsg_thenShouldTellSuccessAndFetchNothingToData() { + // GIVEN + customer.setZip(null); + customer.setAddress(null); + customer.setAddress2(null); + + var user = new User(); + user.setId(new UserId(UUID.randomUUID())); + user.setCustomerId(customer.getId()); + + prepareMsgAndConfig(TbMsgSource.DATA, List.of(ContactBasedEntityDetails.ZIP, ContactBasedEntityDetails.ADDRESS, ContactBasedEntityDetails.ADDRESS2), user.getId()); + + when(ctxMock.getUserService()).thenReturn(userServiceMock); + when(userServiceMock.findUserByIdAsync(eq(TENANT_ID), eq(user.getId()))).thenReturn(Futures.immediateFuture(user)); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + mockFindCustomer(); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(msg.getData()); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(msg.getMetaData()); + } + + @Test + public void givenDidNotFindCustomer_whenOnMsg_thenShouldTellSuccessAndFetchNothingToData() { + // GIVEN + var edge = new Edge(); + edge.setId(new EdgeId(UUID.randomUUID())); + edge.setCustomerId(customer.getId()); + + prepareMsgAndConfig(TbMsgSource.DATA, List.of(ContactBasedEntityDetails.ZIP, ContactBasedEntityDetails.ADDRESS, ContactBasedEntityDetails.ADDRESS2), edge.getId()); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + + when(ctxMock.getEdgeService()).thenReturn(edgeServiceMock); + when(edgeServiceMock.findEdgeByIdAsync(eq(TENANT_ID), eq(edge.getId()))).thenReturn(Futures.immediateFuture(edge)); + + when(ctxMock.getCustomerService()).thenReturn(customerServiceMock); + when(customerServiceMock.findCustomerByIdAsync(eq(TENANT_ID), eq(customer.getId()))).thenReturn(Futures.immediateFuture(null)); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(msg.getData()); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(msg.getMetaData()); + } + + @Test + public void givenDidNotFindOriginator_whenOnMsg_thenShouldTellSuccessAndFetchNothingToData() { + // GIVEN + var edge = new Edge(); + edge.setId(new EdgeId(UUID.randomUUID())); + edge.setCustomerId(customer.getId()); + + prepareMsgAndConfig(TbMsgSource.DATA, List.of(ContactBasedEntityDetails.ZIP, ContactBasedEntityDetails.ADDRESS, ContactBasedEntityDetails.ADDRESS2), edge.getId()); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + + when(ctxMock.getEdgeService()).thenReturn(edgeServiceMock); + when(edgeServiceMock.findEdgeByIdAsync(eq(TENANT_ID), eq(edge.getId()))).thenReturn(Futures.immediateFuture(null)); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(msg.getData()); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(msg.getMetaData()); + } + + @Test + public void givenOriginatorNotAssignedToCustomer_whenOnMsg_thenShouldTellFailureAndFetchNothingToData() { + // GIVEN + var device = new Device(); + device.setId(new DeviceId(UUID.randomUUID())); + device.setName("Thermostat"); + + prepareMsgAndConfig(TbMsgSource.DATA, List.of(ContactBasedEntityDetails.ZIP, ContactBasedEntityDetails.ADDRESS, ContactBasedEntityDetails.ADDRESS2), device.getId()); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + + when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); + when(deviceServiceMock.findDeviceByIdAsync(eq(TENANT_ID), eq(device.getId()))).thenReturn(Futures.immediateFuture(device)); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + var actualExceptionCaptor = ArgumentCaptor.forClass(Exception.class); + + verify(ctxMock, times(1)).tellFailure(actualMessageCaptor.capture(), actualExceptionCaptor.capture()); + verify(ctxMock, never()).tellSuccess(any()); + + var actualMsg = actualMessageCaptor.getValue(); + var actualException = actualExceptionCaptor.getValue(); + + assertThat(actualMsg.getData()).isEqualTo(msg.getData()); + assertThat(actualMsg.getMetaData()).isEqualTo(msg.getMetaData()); + + assertThat(actualException).isInstanceOf(RuntimeException.class); + assertThat(actualException.getMessage()).isEqualTo("Device with name 'Thermostat' is not assigned to Customer!"); + } + + @Test + public void givenNullDescriptionAndAddInfoEntityDetails_whenOnMsg_thenShouldTellSuccessAndFetchNothingToData() { + // GIVEN + customer.setAdditionalInfo(JacksonUtil.toJsonNode("{\"someProperty\":\"someValue\",\"description\":null}")); + + var device = new Device(); + device.setId(new DeviceId(UUID.randomUUID())); + device.setCustomerId(customer.getId()); + + prepareMsgAndConfig(TbMsgSource.DATA, List.of(ContactBasedEntityDetails.ADDITIONAL_INFO), device.getId()); + + when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); + when(deviceServiceMock.findDeviceByIdAsync(eq(TENANT_ID), eq(device.getId()))).thenReturn(Futures.immediateFuture(device)); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + mockFindCustomer(); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(msg.getData()); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(msg.getMetaData()); + } + + @Test + public void givenUnsupportedEntityType_whenOnMsg_thenShouldTellFailureAndFetchNothingToMetaData() { + // GIVEN + var dashboard = new Dashboard(); + dashboard.setId(new DashboardId(UUID.randomUUID())); + + prepareMsgAndConfig(TbMsgSource.METADATA, List.of(ContactBasedEntityDetails.STATE), dashboard.getId()); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + var actualExceptionCaptor = ArgumentCaptor.forClass(Exception.class); + + verify(ctxMock, times(1)).tellFailure(actualMessageCaptor.capture(), actualExceptionCaptor.capture()); + verify(ctxMock, never()).tellSuccess(any()); + + var actualMsg = actualMessageCaptor.getValue(); + var actualException = actualExceptionCaptor.getValue(); + + assertThat(actualMsg.getData()).isEqualTo(msg.getData()); + assertThat(actualMsg.getMetaData()).isEqualTo(msg.getMetaData()); + + assertThat(actualException).isInstanceOf(NoSuchElementException.class); + assertThat(actualException.getMessage()).isEqualTo("Entity with entityType 'DASHBOARD' is not supported."); + } + + @Test + public void givenOldConfig_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception { + var defaultConfig = new TbGetCustomerDetailsNodeConfiguration().defaultConfiguration(); + var node = new TbGetCustomerDetailsNode(); + String oldConfig = "{\"detailsList\":[],\"addToMetadata\":false}"; + JsonNode configJson = JacksonUtil.toJsonNode(oldConfig); + TbPair upgrade = node.upgrade(0, configJson); + Assertions.assertTrue(upgrade.getFirst()); + Assertions.assertEquals(defaultConfig, JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass())); + } + + private void prepareMsgAndConfig(TbMsgSource fetchTo, List detailsList, EntityId originator) { + config.setDetailsList(detailsList); + config.setFetchTo(fetchTo); + + node.config = config; + node.fetchTo = fetchTo; + + var msgMetaData = new TbMsgMetaData(); + msgMetaData.putValue("metaKey1", "metaValue1"); + msgMetaData.putValue("metaKey2", "metaValue2"); + + var msgData = "{\"dataKey1\":123,\"dataKey2\":\"dataValue2\"}"; + + msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, msgMetaData, msgData); + } + + private void mockFindCustomer() { + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + when(ctxMock.getCustomerService()).thenReturn(customerServiceMock); + when(customerServiceMock.findCustomerByIdAsync(eq(TENANT_ID), eq(customer.getId()))).thenReturn(Futures.immediateFuture(customer)); + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeTest.java new file mode 100644 index 0000000000..550cc6a884 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNodeTest.java @@ -0,0 +1,82 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.metadata; + +import com.fasterxml.jackson.databind.JsonNode; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.util.TbPair; + +public class TbGetDeviceAttrNodeTest { + + @Test + public void givenOldConfig_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception { + var defaultConfig = new TbGetDeviceAttrNodeConfiguration().defaultConfiguration(); + var node = new TbGetDeviceAttrNode(); + String oldConfig = "{\"fetchToData\":false," + + "\"clientAttributeNames\":[]," + + "\"sharedAttributeNames\":[]," + + "\"serverAttributeNames\":[]," + + "\"latestTsKeyNames\":[]," + + "\"tellFailureIfAbsent\":true," + + "\"getLatestValueWithTs\":false," + + "\"deviceRelationsQuery\":{\"direction\":\"FROM\",\"maxLevel\":1,\"relationType\":\"Contains\",\"deviceTypes\":[\"default\"]," + + "\"fetchLastLevelOnly\":false}}"; + JsonNode configJson = JacksonUtil.toJsonNode(oldConfig); + TbPair upgrade = node.upgrade(0, configJson); + Assertions.assertTrue(upgrade.getFirst()); + Assertions.assertEquals(defaultConfig, JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass())); + } + + @Test + public void givenOldConfigWithNoFetchToDataProperty_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception { + var defaultConfig = new TbGetDeviceAttrNodeConfiguration().defaultConfiguration(); + var node = new TbGetDeviceAttrNode(); + String oldConfig = "{\"clientAttributeNames\":[]," + + "\"sharedAttributeNames\":[]," + + "\"serverAttributeNames\":[]," + + "\"latestTsKeyNames\":[]," + + "\"tellFailureIfAbsent\":true," + + "\"getLatestValueWithTs\":false," + + "\"deviceRelationsQuery\":{\"direction\":\"FROM\",\"maxLevel\":1,\"relationType\":\"Contains\",\"deviceTypes\":[\"default\"]," + + "\"fetchLastLevelOnly\":false}}"; + JsonNode configJson = JacksonUtil.toJsonNode(oldConfig); + TbPair upgrade = node.upgrade(0, configJson); + Assertions.assertTrue(upgrade.getFirst()); + Assertions.assertEquals(defaultConfig, JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass())); + } + + @Test + public void givenOldConfigWithNullFetchToDataProperty_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception { + var defaultConfig = new TbGetDeviceAttrNodeConfiguration().defaultConfiguration(); + var node = new TbGetDeviceAttrNode(); + String oldConfig = "{\"fetchToData\":null," + + "\"clientAttributeNames\":[]," + + "\"sharedAttributeNames\":[]," + + "\"serverAttributeNames\":[]," + + "\"latestTsKeyNames\":[]," + + "\"tellFailureIfAbsent\":true," + + "\"getLatestValueWithTs\":false," + + "\"deviceRelationsQuery\":{\"direction\":\"FROM\",\"maxLevel\":1,\"relationType\":\"Contains\",\"deviceTypes\":[\"default\"]," + + "\"fetchLastLevelOnly\":false}}"; + JsonNode configJson = JacksonUtil.toJsonNode(oldConfig); + TbPair upgrade = node.upgrade(0, configJson); + Assertions.assertTrue(upgrade.getFirst()); + Assertions.assertEquals(defaultConfig, JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass())); + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java new file mode 100644 index 0000000000..ded7400ea5 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNodeTest.java @@ -0,0 +1,385 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.metadata; + +import com.fasterxml.jackson.databind.JsonNode; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.ListeningExecutor; +import org.thingsboard.rule.engine.TestDbCallbackExecutor; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.util.TbMsgSource; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.util.TbPair; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.dao.device.DeviceService; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class TbGetOriginatorFieldsNodeTest { + + private static final DeviceId DUMMY_DEVICE_ORIGINATOR = new DeviceId(UUID.randomUUID()); + private static final TenantId DUMMY_TENANT_ID = new TenantId(UUID.randomUUID()); + private static final ListeningExecutor DB_EXECUTOR = new TestDbCallbackExecutor(); + @Mock + private TbContext ctxMock; + @Mock + private DeviceService deviceServiceMock; + private TbGetOriginatorFieldsNode node; + private TbGetOriginatorFieldsConfiguration config; + private TbNodeConfiguration nodeConfiguration; + private TbMsg msg; + + @BeforeEach + public void setUp() { + node = new TbGetOriginatorFieldsNode(); + config = new TbGetOriginatorFieldsConfiguration().defaultConfiguration(); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + } + + @Test + public void givenConfigWithNullFetchTo_whenInit_thenException() { + // GIVEN + config.setFetchTo(null); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + + // WHEN + var exception = assertThrows(TbNodeException.class, () -> node.init(ctxMock, nodeConfiguration)); + + // THEN + assertThat(exception.getMessage()).isEqualTo("FetchTo option can't be null! Allowed values: " + Arrays.toString(TbMsgSource.values())); + verify(ctxMock, never()).tellSuccess(any()); + } + + @Test + public void givenDefaultConfig_whenInit_thenOK() throws TbNodeException { + // GIVEN-WHEN + node.init(ctxMock, nodeConfiguration); + + // THEN + assertThat(node.config).isEqualTo(config); + assertThat(config.getDataMapping()).isEqualTo(Map.of( + "name", "originatorName", + "type", "originatorType")); + assertThat(config.isIgnoreNullStrings()).isEqualTo(false); + assertThat(config.getFetchTo()).isEqualTo(TbMsgSource.METADATA); + assertThat(node.fetchTo).isEqualTo(TbMsgSource.METADATA); + } + + @Test + public void givenCustomConfig_whenInit_thenOK() throws TbNodeException { + // GIVEN + config.setDataMapping(Map.of( + "email", "originatorEmail", + "title", "originatorTitle", + "country", "originatorCountry")); + config.setIgnoreNullStrings(true); + config.setFetchTo(TbMsgSource.DATA); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + + // WHEN + node.init(ctxMock, nodeConfiguration); + + // THEN + assertThat(node.config).isEqualTo(config); + assertThat(config.getDataMapping()).isEqualTo(Map.of( + "email", "originatorEmail", + "title", "originatorTitle", + "country", "originatorCountry")); + assertThat(config.isIgnoreNullStrings()).isEqualTo(true); + assertThat(config.getFetchTo()).isEqualTo(TbMsgSource.DATA); + assertThat(node.fetchTo).isEqualTo(TbMsgSource.DATA); + } + + @Test + public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { + // GIVEN + node.fetchTo = TbMsgSource.DATA; + msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + + // WHEN + var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); + + // THEN + assertThat(exception.getMessage()).isEqualTo("Message body is not an object!"); + verify(ctxMock, never()).tellSuccess(any()); + } + + @Test + public void givenValidMsgAndFetchToData_whenOnMsg_thenShouldTellSuccessAndFetchToData() throws TbNodeException, ExecutionException, InterruptedException { + // GIVEN + var device = new Device(); + device.setId(DUMMY_DEVICE_ORIGINATOR); + device.setName("Test device"); + device.setType("Test device type"); + + config.setDataMapping(Map.of( + "name", "originatorName", + "type", "originatorType", + "label", "originatorLabel")); + config.setIgnoreNullStrings(true); + config.setFetchTo(TbMsgSource.DATA); + + node.config = config; + node.fetchTo = TbMsgSource.DATA; + var msgMetaData = new TbMsgMetaData(); + var msgData = "{\"temp\":42,\"humidity\":77}"; + msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, msgMetaData, msgData); + + when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); + when(ctxMock.getTenantId()).thenReturn(DUMMY_TENANT_ID); + when(deviceServiceMock.findDeviceById(eq(DUMMY_TENANT_ID), eq(device.getId()))).thenReturn(device); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgData = "{\"temp\":42,\"humidity\":77,\"originatorName\":\"Test device\",\"originatorType\":\"Test device type\"}"; + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(expectedMsgData); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(msgMetaData); + } + + @Test + public void givenDeviceWithEmptyLabel_whenOnMsg_thenShouldTellSuccessAndFetchToData() throws TbNodeException, ExecutionException, InterruptedException { + // GIVEN + var device = new Device(); + device.setId(DUMMY_DEVICE_ORIGINATOR); + device.setName("Test device"); + device.setType("Test device type"); + device.setLabel(""); + + config.setDataMapping(Map.of( + "name", "originatorName", + "type", "originatorType", + "label", "originatorLabel")); + config.setIgnoreNullStrings(true); + config.setFetchTo(TbMsgSource.DATA); + + node.config = config; + node.fetchTo = TbMsgSource.DATA; + var msgMetaData = new TbMsgMetaData(); + var msgData = "{\"temp\":42,\"humidity\":77}"; + msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, msgMetaData, msgData); + + when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); + when(ctxMock.getTenantId()).thenReturn(DUMMY_TENANT_ID); + when(deviceServiceMock.findDeviceById(eq(DUMMY_TENANT_ID), eq(device.getId()))).thenReturn(device); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgData = "{\"temp\":42,\"humidity\":77,\"originatorName\":\"Test device\",\"originatorType\":\"Test device type\"}"; + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(expectedMsgData); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(msgMetaData); + } + + @Test + public void givenValidMsgAndFetchToMetaData_whenOnMsg_thenShouldTellSuccessAndFetchToMetaData() throws TbNodeException, ExecutionException, InterruptedException { + // GIVEN + var device = new Device(); + device.setId(DUMMY_DEVICE_ORIGINATOR); + device.setName("Test device"); + device.setType("Test device type"); + + config.setDataMapping(Map.of( + "name", "originatorName", + "type", "originatorType", + "label", "originatorLabel")); + config.setIgnoreNullStrings(true); + config.setFetchTo(TbMsgSource.METADATA); + + node.config = config; + node.fetchTo = TbMsgSource.METADATA; + var msgMetaData = new TbMsgMetaData(Map.of( + "testKey1", "testValue1", + "testKey2", "123")); + var msgData = "[\"value1\",\"value2\"]"; + msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, msgMetaData, msgData); + + when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); + when(ctxMock.getTenantId()).thenReturn(DUMMY_TENANT_ID); + when(deviceServiceMock.findDeviceById(eq(DUMMY_TENANT_ID), eq(device.getId()))).thenReturn(device); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgMetaData = new TbMsgMetaData(Map.of( + "testKey1", "testValue1", + "testKey2", "123", + "originatorName", "Test device", + "originatorType", "Test device type" + )); + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(msgData); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(expectedMsgMetaData); + } + + @Test + public void givenNullEntityFieldsAndIgnoreNullStringsFalse_whenOnMsg_thenShouldTellSuccessAndFetchNullField() throws TbNodeException, ExecutionException, InterruptedException { + // GIVEN + var device = new Device(); + device.setId(DUMMY_DEVICE_ORIGINATOR); + device.setName("Test device"); + device.setType("Test device type"); + + config.setDataMapping(Map.of( + "name", "originatorName", + "type", "originatorType", + "label", "originatorLabel")); + config.setIgnoreNullStrings(false); + config.setFetchTo(TbMsgSource.METADATA); + + node.config = config; + node.fetchTo = TbMsgSource.METADATA; + var msgMetaData = new TbMsgMetaData(Map.of( + "testKey1", "testValue1", + "testKey2", "123")); + var msgData = "[\"value1\",\"value2\"]"; + msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, msgMetaData, msgData); + + when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); + when(ctxMock.getTenantId()).thenReturn(DUMMY_TENANT_ID); + when(deviceServiceMock.findDeviceById(eq(DUMMY_TENANT_ID), eq(device.getId()))).thenReturn(device); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgMetaData = new TbMsgMetaData(Map.of( + "testKey1", "testValue1", + "testKey2", "123", + "originatorName", "Test device", + "originatorType", "Test device type", + "originatorLabel", "null" + )); + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(msgData); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(expectedMsgMetaData); + } + + @Test + public void givenEmptyFieldsMapping_whenInit_thenException() { + // GIVEN + config.setDataMapping(Collections.emptyMap()); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + + // WHEN + var exception = assertThrows(TbNodeException.class, () -> node.init(ctxMock, nodeConfiguration)); + + // THEN + assertThat(exception.getMessage()).isEqualTo("At least one mapping entry should be specified!"); + verify(ctxMock, never()).tellSuccess(any()); + } + + @Test + public void givenUnsupportedEntityType_whenOnMsg_thenShouldTellFailureWithSameMsg() throws TbNodeException, ExecutionException, InterruptedException { + // GIVEN + config.setDataMapping(Map.of( + "name", "originatorName", + "type", "originatorType", + "label", "originatorLabel")); + config.setIgnoreNullStrings(false); + config.setFetchTo(TbMsgSource.METADATA); + + node.config = config; + node.fetchTo = TbMsgSource.METADATA; + var msgMetaData = new TbMsgMetaData(Map.of( + "testKey1", "testValue1", + "testKey2", "123")); + var msgData = "[\"value1\",\"value2\"]"; + msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, new DashboardId(UUID.randomUUID()), msgMetaData, msgData); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctxMock, times(1)).tellFailure(actualMessageCaptor.capture(), any()); + verify(ctxMock, never()).tellSuccess(any()); + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(msgData); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(msgMetaData); + } + + @Test + public void givenOldConfig_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception { + var defaultConfig = new TbGetOriginatorFieldsConfiguration().defaultConfiguration(); + var node = new TbGetOriginatorFieldsNode(); + String oldConfig = "{\"fieldsMapping\":{\"name\":\"originatorName\",\"type\":\"originatorType\"},\"ignoreNullStrings\":false}"; + JsonNode configJson = JacksonUtil.toJsonNode(oldConfig); + TbPair upgrade = node.upgrade(0, configJson); + Assertions.assertTrue(upgrade.getFirst()); + Assertions.assertEquals(defaultConfig, JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass())); + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java index 8a41805d26..b236c3097c 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNodeTest.java @@ -15,150 +15,602 @@ */ package org.thingsboard.rule.engine.metadata; +import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.Futures; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.common.util.ListeningExecutor; +import org.thingsboard.rule.engine.TestDbCallbackExecutor; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.data.RelationsQuery; +import org.thingsboard.rule.engine.util.TbMsgSource; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; +import org.thingsboard.server.common.data.util.TbPair; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.timeseries.TimeseriesService; -import java.util.HashMap; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.UUID; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) -public class TbGetRelatedAttributeNodeTest extends AbstractAttributeNodeTest { - User user = new User(); - Asset asset = new Asset(); - Device device = new Device(); +@ExtendWith(MockitoExtension.class) +public class TbGetRelatedAttributeNodeTest { + + private static final EntityId DUMMY_DEVICE_ORIGINATOR = new DeviceId(UUID.randomUUID()); + private static final TenantId TENANT_ID = new TenantId(UUID.randomUUID()); + private static final ListeningExecutor DB_EXECUTOR = new TestDbCallbackExecutor(); + @Mock + private TbContext ctxMock; + @Mock + private AttributesService attributesServiceMock; + @Mock + private TimeseriesService timeseriesServiceMock; @Mock - private RelationService relationService; + private RelationService relationServiceMock; + @Mock + private DeviceService deviceServiceMock; + private TbGetRelatedAttributeNode node; + private TbGetRelatedDataNodeConfiguration config; + private TbNodeConfiguration nodeConfiguration; private EntityRelation entityRelation; + private TbMsg msg; - @Before - public void initDataForTests() throws TbNodeException { - init(new TbGetRelatedAttributeNode()); + @BeforeEach + public void setUp() { + node = new TbGetRelatedAttributeNode(); + config = new TbGetRelatedDataNodeConfiguration().defaultConfiguration(); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); entityRelation = new EntityRelation(); - entityRelation.setTo(customerId); - entityRelation.setType(EntityRelation.CONTAINS_TYPE); - when(ctx.getRelationService()).thenReturn(relationService); + } - user.setCustomerId(customerId); - user.setId(new UserId(UUID.randomUUID())); - entityRelation.setFrom(user.getId()); + @Test + public void givenConfigWithNullFetchTo_whenInit_thenException() { + // GIVEN + config.setFetchTo(null); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); - asset.setCustomerId(customerId); - asset.setId(new AssetId(UUID.randomUUID())); + // WHEN + var exception = assertThrows(TbNodeException.class, () -> node.init(ctxMock, nodeConfiguration)); - device.setCustomerId(customerId); - device.setId(new DeviceId(UUID.randomUUID())); + // THEN + assertThat(exception.getMessage()).isEqualTo("FetchTo option can't be null! Allowed values: " + Arrays.toString(TbMsgSource.values())); + verify(ctxMock, never()).tellSuccess(any()); } - @Override - protected TbEntityGetAttrNode getEmptyNode() { - return new TbGetRelatedAttributeNode(); + @Test + public void givenConfigWithNullDataToFetch_whenInit_thenException() { + // GIVEN + config.setDataToFetch(null); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + + // WHEN + var exception = assertThrows(TbNodeException.class, () -> node.init(ctxMock, nodeConfiguration)); + + // THEN + assertThat(exception.getMessage()).isEqualTo("DataToFetch property cannot be null! Supported values are: " + Arrays.toString(DataToFetch.values())); + verify(ctxMock, never()).tellSuccess(any()); } - @Override - TbGetEntityAttrNodeConfiguration getTbNodeConfig() { - return getConfig(false); + @Test + public void givenDefaultConfig_whenInit_thenOK() throws TbNodeException { + // GIVEN + + // WHEN + node.init(ctxMock, nodeConfiguration); + + // THEN + var nodeConfig = (TbGetRelatedDataNodeConfiguration) node.config; + assertThat(nodeConfig).isEqualTo(config); + assertThat(nodeConfig.getDataMapping()).isEqualTo(Map.of("serialNumber", "sn")); + assertThat(nodeConfig.getDataToFetch()).isEqualTo(DataToFetch.ATTRIBUTES); + assertThat(node.fetchTo).isEqualTo(TbMsgSource.METADATA); + + var relationsQuery = new RelationsQuery(); + var relationEntityTypeFilter = new RelationEntityTypeFilter(EntityRelation.CONTAINS_TYPE, Collections.emptyList()); + relationsQuery.setDirection(EntitySearchDirection.FROM); + relationsQuery.setMaxLevel(1); + relationsQuery.setFilters(Collections.singletonList(relationEntityTypeFilter)); + + assertThat(nodeConfig.getRelationsQuery()).isEqualTo(relationsQuery); } - @Override - TbGetEntityAttrNodeConfiguration getTbNodeConfigForTelemetry() { - return getConfig(true); + @Test + public void givenCustomConfig_whenInit_thenOK() throws TbNodeException { + // GIVEN + config.setDataMapping(Map.of( + "sourceAttr1", "targetKey1", + "sourceAttr2", "targetKey2", + "sourceAttr3", "targetKey3")); + config.setDataToFetch(DataToFetch.LATEST_TELEMETRY); + config.setFetchTo(TbMsgSource.DATA); + + var relationsQuery = new RelationsQuery(); + var relationEntityTypeFilter = new RelationEntityTypeFilter(EntityRelation.CONTAINS_TYPE, Collections.emptyList()); + relationsQuery.setDirection(EntitySearchDirection.FROM); + relationsQuery.setMaxLevel(1); + relationsQuery.setFilters(Collections.singletonList(relationEntityTypeFilter)); + + config.setRelationsQuery(relationsQuery); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + + // WHEN + node.init(ctxMock, nodeConfiguration); + + // THEN + var nodeConfig = (TbGetRelatedDataNodeConfiguration) node.config; + assertThat(nodeConfig).isEqualTo(config); + assertThat(nodeConfig.getDataMapping()).isEqualTo(Map.of( + "sourceAttr1", "targetKey1", + "sourceAttr2", "targetKey2", + "sourceAttr3", "targetKey3" + )); + assertThat(nodeConfig.getDataToFetch()).isEqualTo(DataToFetch.LATEST_TELEMETRY); + assertThat(node.fetchTo).isEqualTo(TbMsgSource.DATA); + assertThat(nodeConfig.getRelationsQuery()).isEqualTo(relationsQuery); } - private TbGetEntityAttrNodeConfiguration getConfig(boolean isTelemetry) { - TbGetRelatedAttrNodeConfiguration config = new TbGetRelatedAttrNodeConfiguration(); - config = config.defaultConfiguration(); - Map conf = new HashMap<>(); - conf.put(keyAttrConf, valueAttrConf); - config.setAttrMapping(conf); - config.setTelemetry(isTelemetry); - return config; + @Test + public void givenEmptyAttributesMapping_whenInit_thenException() { + // GIVEN + var expectedExceptionMessage = "At least one mapping entry should be specified!"; + + config.setDataMapping(Collections.emptyMap()); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + + // WHEN + var exception = assertThrows(TbNodeException.class, () -> node.init(ctxMock, nodeConfiguration)); + + // THEN + assertThat(exception.getMessage()).isEqualTo(expectedExceptionMessage); + verify(ctxMock, never()).tellSuccess(any()); } - @Override - EntityId getEntityId() { - return customerId; + @Test + public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { + // GIVEN + node.fetchTo = TbMsgSource.DATA; + msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + + // WHEN + var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); + + // THEN + assertThat(exception.getMessage()).isEqualTo("Message body is not an object!"); + verify(ctxMock, never()).tellSuccess(any()); } @Test - public void errorThrownIfCannotLoadAttributes() { - entityRelation.setFrom(user.getId()); - entityRelation.setTo(customerId); - when(relationService.findByQuery(any(), any())).thenReturn(Futures.immediateFuture(List.of(entityRelation))); - errorThrownIfCannotLoadAttributes(user); + public void givenDidNotFindEntity_whenOnMsg_thenShouldTellFailure() { + // GIVEN + prepareMsgAndConfig(TbMsgSource.METADATA, DataToFetch.ATTRIBUTES, DUMMY_DEVICE_ORIGINATOR); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + + when(ctxMock.getRelationService()).thenReturn(relationServiceMock); + doReturn(Futures.immediateFuture(null)).when(relationServiceMock).findByQuery(eq(TENANT_ID), any()); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + var actualExceptionCaptor = ArgumentCaptor.forClass(Throwable.class); + + verify(ctxMock, never()).tellSuccess(any()); + verify(ctxMock, times(1)) + .tellFailure(actualMessageCaptor.capture(), actualExceptionCaptor.capture()); + + var actualMessage = actualMessageCaptor.getValue(); + var actualException = actualExceptionCaptor.getValue(); + + var expectedExceptionMessage = "Failed to find related entity to message originator using relation query specified in the configuration!"; + + assertEquals(msg, actualMessage); + assertEquals(expectedExceptionMessage, actualException.getMessage()); + assertInstanceOf(NoSuchElementException.class, actualException); } @Test - public void errorThrownIfCannotLoadAttributesAsync() { - entityRelation.setFrom(user.getId()); - entityRelation.setTo(customerId); - when(relationService.findByQuery(any(), any())).thenReturn(Futures.immediateFuture(List.of(entityRelation))); - errorThrownIfCannotLoadAttributesAsync(user); + public void givenFetchAttributesToData_whenOnMsg_thenShouldFetchAttributesToData() { + // GIVEN + var customer = new Customer(new CustomerId(UUID.randomUUID())); + var user = new User(new UserId(UUID.randomUUID())); + + prepareMsgAndConfig(TbMsgSource.DATA, DataToFetch.ATTRIBUTES, customer.getId()); + + entityRelation.setFrom(customer.getId()); + entityRelation.setTo(user.getId()); + entityRelation.setType(EntityRelation.CONTAINS_TYPE); + + List attributes = List.of( + new BaseAttributeKvEntry(new StringDataEntry("sourceKey1", "sourceValue1"), 1L), + new BaseAttributeKvEntry(new StringDataEntry("sourceKey2", "sourceValue2"), 2L), + new BaseAttributeKvEntry(new StringDataEntry("sourceKey3", "sourceValue3"), 3L) + ); + var expectedPatternProcessedKeysList = List.of("sourceKey1", "sourceKey2", "sourceKey3"); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + + when(ctxMock.getRelationService()).thenReturn(relationServiceMock); + doReturn(Futures.immediateFuture(List.of(entityRelation))).when(relationServiceMock).findByQuery(eq(TENANT_ID), any()); + + when(ctxMock.getAttributesService()).thenReturn(attributesServiceMock); + when(attributesServiceMock.find(eq(TENANT_ID), eq(user.getId()), eq(DataConstants.SERVER_SCOPE), argThat(new ListMatcher<>(expectedPatternProcessedKeysList)))) + .thenReturn(Futures.immediateFuture(attributes)); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgData = "{\"temp\":42," + + "\"humidity\":77," + + "\"messageBodyPattern1\":\"targetKey2\"," + + "\"messageBodyPattern2\":\"sourceKey3\"," + + "\"targetKey1\":\"sourceValue1\"," + + "\"targetKey2\":\"sourceValue2\"," + + "\"targetKey3\":\"sourceValue3\"}"; + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(expectedMsgData); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(msg.getMetaData()); } @Test - public void failedChainUsedIfCustomerCannotBeFound() { - entityRelation.setFrom(customerId); - entityRelation.setTo(null); - when(relationService.findByQuery(any(), any())).thenReturn(Futures.immediateFuture(List.of(entityRelation))); - failedChainUsedIfCustomerCannotBeFound(user); + public void givenFetchAttributesToMetaData_whenOnMsg_thenShouldFetchAttributesToMetaData() { + // GIVEN + var firstCustomer = new Customer(new CustomerId(UUID.randomUUID())); + var secondCustomer = new Customer(new CustomerId(UUID.randomUUID())); + + prepareMsgAndConfig(TbMsgSource.METADATA, DataToFetch.ATTRIBUTES, firstCustomer.getId()); + + entityRelation.setFrom(firstCustomer.getId()); + entityRelation.setTo(secondCustomer.getId()); + entityRelation.setType(EntityRelation.MANAGES_TYPE); + + List attributes = List.of( + new BaseAttributeKvEntry(new StringDataEntry("sourceKey1", "sourceValue1"), 1L), + new BaseAttributeKvEntry(new StringDataEntry("sourceKey2", "sourceValue2"), 2L), + new BaseAttributeKvEntry(new StringDataEntry("sourceKey3", "sourceValue3"), 3L) + ); + var expectedPatternProcessedKeysList = List.of("sourceKey1", "sourceKey2", "sourceKey3"); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + + when(ctxMock.getRelationService()).thenReturn(relationServiceMock); + doReturn(Futures.immediateFuture(List.of(entityRelation))).when(relationServiceMock).findByQuery(eq(TENANT_ID), any()); + + when(ctxMock.getAttributesService()).thenReturn(attributesServiceMock); + when(attributesServiceMock.find(eq(TENANT_ID), eq(secondCustomer.getId()), eq(DataConstants.SERVER_SCOPE), argThat(new ListMatcher<>(expectedPatternProcessedKeysList)))) + .thenReturn(Futures.immediateFuture(attributes)); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgMetaData = new TbMsgMetaData(Map.of( + "metaDataPattern1", "sourceKey2", + "metaDataPattern2", "targetKey3", + "targetKey1", "sourceValue1", + "targetKey2", "sourceValue2", + "targetKey3", "sourceValue3" + )); + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(msg.getData()); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(expectedMsgMetaData); } @Test - public void customerAttributeAddedInMetadata() { - entityRelation.setFrom(customerId); - entityRelation.setTo(customerId); - when(relationService.findByQuery(any(), any())).thenReturn(Futures.immediateFuture(List.of(entityRelation))); - entityAttributeAddedInMetadata(customerId, "CUSTOMER"); + public void givenFetchTelemetryToData_whenOnMsg_thenShouldFetchTelemetryToData() { + // GIVEN + var dashboard = new Dashboard(new DashboardId(UUID.randomUUID())); + var entityView = new EntityView(new EntityViewId(UUID.randomUUID())); + + prepareMsgAndConfig(TbMsgSource.DATA, DataToFetch.LATEST_TELEMETRY, dashboard.getId()); + + entityRelation.setFrom(dashboard.getId()); + entityRelation.setTo(entityView.getId()); + entityRelation.setType(EntityRelation.CONTAINS_TYPE); + + List timeseries = List.of( + new BasicTsKvEntry(1L, new StringDataEntry("sourceKey1", "sourceValue1")), + new BasicTsKvEntry(1L, new StringDataEntry("sourceKey2", "sourceValue2")), + new BasicTsKvEntry(1L, new StringDataEntry("sourceKey3", "sourceValue3")) + ); + var expectedPatternProcessedKeysList = List.of("sourceKey1", "sourceKey2", "sourceKey3"); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + + when(ctxMock.getRelationService()).thenReturn(relationServiceMock); + doReturn(Futures.immediateFuture(List.of(entityRelation))).when(relationServiceMock).findByQuery(eq(TENANT_ID), any()); + + when(ctxMock.getTimeseriesService()).thenReturn(timeseriesServiceMock); + when(timeseriesServiceMock.findLatest(eq(TENANT_ID), eq(entityView.getId()), argThat(new ListMatcher<>(expectedPatternProcessedKeysList)))) + .thenReturn(Futures.immediateFuture(timeseries)); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgData = "{\"temp\":42," + + "\"humidity\":77," + + "\"messageBodyPattern1\":\"targetKey2\"," + + "\"messageBodyPattern2\":\"sourceKey3\"," + + "\"targetKey1\":\"sourceValue1\"," + + "\"targetKey2\":\"sourceValue2\"," + + "\"targetKey3\":\"sourceValue3\"}"; + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(expectedMsgData); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(msg.getMetaData()); } @Test - public void usersCustomerAttributesFetched() { - entityRelation.setFrom(user.getId()); - entityRelation.setTo(customerId); - when(relationService.findByQuery(any(), any())).thenReturn(Futures.immediateFuture(List.of(entityRelation))); - usersCustomerAttributesFetched(user); + public void givenFetchTelemetryToMetaData_whenOnMsg_thenShouldFetchTelemetryToMetaData() { + // GIVEN + var tenant = new Tenant(new TenantId(UUID.randomUUID())); + var device = new Device(new DeviceId(UUID.randomUUID())); + + prepareMsgAndConfig(TbMsgSource.METADATA, DataToFetch.LATEST_TELEMETRY, tenant.getId()); + + entityRelation.setFrom(tenant.getId()); + entityRelation.setTo(device.getId()); + entityRelation.setType(EntityRelation.CONTAINS_TYPE); + + List timeseries = List.of( + new BasicTsKvEntry(1L, new StringDataEntry("sourceKey1", "sourceValue1")), + new BasicTsKvEntry(1L, new StringDataEntry("sourceKey2", "sourceValue2")), + new BasicTsKvEntry(1L, new StringDataEntry("sourceKey3", "sourceValue3")) + ); + var expectedPatternProcessedKeysList = List.of("sourceKey1", "sourceKey2", "sourceKey3"); + + when(ctxMock.getTenantId()).thenReturn(tenant.getId()); + + when(ctxMock.getRelationService()).thenReturn(relationServiceMock); + doReturn(Futures.immediateFuture(List.of(entityRelation))).when(relationServiceMock).findByQuery(eq(tenant.getId()), any()); + + when(ctxMock.getTimeseriesService()).thenReturn(timeseriesServiceMock); + when(timeseriesServiceMock.findLatest(eq(tenant.getId()), eq(device.getId()), argThat(new ListMatcher<>(expectedPatternProcessedKeysList)))) + .thenReturn(Futures.immediateFuture(timeseries)); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgMetaData = new TbMsgMetaData(Map.of( + "metaDataPattern1", "sourceKey2", + "metaDataPattern2", "targetKey3", + "targetKey1", "sourceValue1", + "targetKey2", "sourceValue2", + "targetKey3", "sourceValue3" + )); + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(msg.getData()); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(expectedMsgMetaData); } @Test - public void assetsCustomerAttributesFetched() { + public void givenFetchFieldsToData_whenOnMsg_thenShouldFetchFieldsToData() { + // GIVEN + var device = new Device(); + device.setId(new DeviceId(UUID.randomUUID())); + device.setName("Device Name"); + var asset = new Asset(new AssetId(UUID.randomUUID())); + + prepareMsgAndConfig(TbMsgSource.DATA, DataToFetch.FIELDS, asset.getId()); + entityRelation.setFrom(asset.getId()); - entityRelation.setTo(customerId); - when(relationService.findByQuery(any(), any())).thenReturn(Futures.immediateFuture(List.of(entityRelation))); - assetsCustomerAttributesFetched(asset); + entityRelation.setTo(device.getId()); + entityRelation.setType(EntityRelation.CONTAINS_TYPE); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + + when(ctxMock.getRelationService()).thenReturn(relationServiceMock); + doReturn(Futures.immediateFuture(List.of(entityRelation))).when(relationServiceMock).findByQuery(eq(TENANT_ID), any()); + + when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + when(deviceServiceMock.findDeviceById(eq(TENANT_ID), eq(device.getId()))).thenReturn(device); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgData = "{\"temp\":42,\"humidity\":77,\"messageBodyPattern\":\"relatedEntityId\"," + + "\"relatedEntityId\":\"" + device.getId().getId() + "\",\"relatedEntityName\":\"" + device.getName() + "\"}"; + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(expectedMsgData); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(msg.getMetaData()); } @Test - public void deviceCustomerAttributesFetched() { - entityRelation.setFrom(device.getId()); - entityRelation.setTo(customerId); - when(relationService.findByQuery(any(), any())).thenReturn(Futures.immediateFuture(List.of(entityRelation))); - deviceCustomerAttributesFetched(device); + public void givenFetchFieldsToMetadata_whenOnMsg_thenShouldFetchFieldsToMetadata() { + // GIVEN + var device = new Device(); + device.setId(new DeviceId(UUID.randomUUID())); + device.setName("Device Name"); + var asset = new Asset(new AssetId(UUID.randomUUID())); + + prepareMsgAndConfig(TbMsgSource.METADATA, DataToFetch.FIELDS, asset.getId()); + + entityRelation.setFrom(asset.getId()); + entityRelation.setTo(device.getId()); + entityRelation.setType(EntityRelation.CONTAINS_TYPE); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + + when(ctxMock.getRelationService()).thenReturn(relationServiceMock); + doReturn(Futures.immediateFuture(List.of(entityRelation))).when(relationServiceMock).findByQuery(eq(TENANT_ID), any()); + + when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + when(deviceServiceMock.findDeviceById(eq(TENANT_ID), eq(device.getId()))).thenReturn(device); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgMetadata = new TbMsgMetaData(Map.of( + "metaDataPattern", "relatedEntityName", + "relatedEntityId", device.getId().getId().toString(), + "relatedEntityName", device.getName() + )); + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(msg.getData()); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(expectedMsgMetadata); } @Test - public void deviceCustomerTelemetryFetched() throws TbNodeException { - entityRelation.setFrom(device.getId()); - entityRelation.setTo(customerId); - when(relationService.findByQuery(any(), any())).thenReturn(Futures.immediateFuture(List.of(entityRelation))); - deviceCustomerTelemetryFetched(device); + public void givenOldConfig_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception { + var defaultConfig = new TbGetRelatedDataNodeConfiguration().defaultConfiguration(); + var node = new TbGetRelatedAttributeNode(); + String oldConfig = "{\"attrMapping\":{\"serialNumber\":\"sn\"}," + + "\"relationsQuery\":{\"direction\":\"FROM\",\"maxLevel\":1," + + "\"filters\":[{\"relationType\":\"Contains\",\"entityTypes\":[]}]," + + "\"fetchLastLevelOnly\":false}," + + "\"telemetry\":false}"; + JsonNode configJson = JacksonUtil.toJsonNode(oldConfig); + TbPair upgrade = node.upgrade(0, configJson); + Assertions.assertTrue(upgrade.getFirst()); + Assertions.assertEquals(defaultConfig, JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass())); } + + private void prepareMsgAndConfig(TbMsgSource fetchTo, DataToFetch dataToFetch, EntityId originator) { + + config.setDataToFetch(dataToFetch); + config.setFetchTo(fetchTo); + node.config = config; + node.fetchTo = fetchTo; + var msgMetaData = new TbMsgMetaData(); + String msgData; + if (dataToFetch.equals(DataToFetch.FIELDS)) { + config.setDataMapping(Map.of( + "id", "$[messageBodyPattern]", + "name", "${metaDataPattern}")); + msgMetaData.putValue("metaDataPattern", "relatedEntityName"); + msgData = "{\"temp\":42,\"humidity\":77,\"messageBodyPattern\":\"relatedEntityId\"}"; + } else { + config.setDataMapping(Map.of( + "sourceKey1", "targetKey1", + "${metaDataPattern1}", "$[messageBodyPattern1]", + "$[messageBodyPattern2]", "${metaDataPattern2}")); + msgMetaData.putValue("metaDataPattern1", "sourceKey2"); + msgMetaData.putValue("metaDataPattern2", "targetKey3"); + msgData = "{\"temp\":42,\"humidity\":77,\"messageBodyPattern1\":\"targetKey2\",\"messageBodyPattern2\":\"sourceKey3\"}"; + } + + msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, msgMetaData, msgData); + } + + @RequiredArgsConstructor + private static class ListMatcher implements ArgumentMatcher> { + + private final List expectedList; + + @Override + public boolean matches(List actualList) { + if (actualList == expectedList) { + return true; + } + if (actualList.size() != expectedList.size()) { + return false; + } + return actualList.containsAll(expectedList); + } + + } + } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java index 510e97dd9c..f0c0e76419 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNodeTest.java @@ -15,94 +15,408 @@ */ package org.thingsboard.rule.engine.metadata; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.util.concurrent.Futures; +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.TestDbCallbackExecutor; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; -import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.asset.Asset; -import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.rule.engine.util.TbMsgSource; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.util.TbPair; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.timeseries.TimeseriesService; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; import java.util.UUID; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) -public class TbGetTenantAttributeNodeTest extends AbstractAttributeNodeTest { +@ExtendWith(MockitoExtension.class) +public class TbGetTenantAttributeNodeTest { - User user = new User(); - Asset asset = new Asset(); - Device device = new Device(); + private static final DeviceId DUMMY_DEVICE_ORIGINATOR = new DeviceId(UUID.randomUUID()); + private static final TenantId TENANT_ID = new TenantId(UUID.randomUUID()); + private static final TestDbCallbackExecutor DB_EXECUTOR = new TestDbCallbackExecutor(); + @Mock + private TbContext ctxMock; + @Mock + private AttributesService attributesServiceMock; + @Mock + private TimeseriesService timeseriesServiceMock; + private TbGetTenantAttributeNode node; + private TbGetEntityDataNodeConfiguration config; + private TbNodeConfiguration nodeConfiguration; + private TbMsg msg; - @Before - public void initDataForTests() throws TbNodeException { - init(new TbGetTenantAttributeNode()); + @BeforeEach + public void setUp() { + node = new TbGetTenantAttributeNode(); + config = new TbGetEntityDataNodeConfiguration().defaultConfiguration(); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + } - user.setTenantId(tenantId); - user.setId(new UserId(UUID.randomUUID())); + @Test + public void givenConfigWithNullFetchTo_whenInit_thenException() { + // GIVEN + config.setFetchTo(null); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); - asset.setTenantId(tenantId); - asset.setId(new AssetId(UUID.randomUUID())); + // WHEN + var exception = assertThrows(TbNodeException.class, () -> node.init(ctxMock, nodeConfiguration)); - device.setTenantId(tenantId); - device.setId(new DeviceId(UUID.randomUUID())); + // THEN + assertThat(exception.getMessage()).isEqualTo("FetchTo option can't be null! Allowed values: " + Arrays.toString(TbMsgSource.values())); + verify(ctxMock, never()).tellSuccess(any()); + } + + @Test + public void givenConfigWithNullDataToFetch_whenInit_thenException() { + // GIVEN + config.setDataToFetch(null); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); - when(ctx.getTenantId()).thenReturn(tenantId); + // WHEN + var exception = assertThrows(TbNodeException.class, () -> node.init(ctxMock, nodeConfiguration)); + + // THEN + assertThat(exception.getMessage()).isEqualTo("DataToFetch property has invalid value: null. Only ATTRIBUTES and LATEST_TELEMETRY values supported!"); + verify(ctxMock, never()).tellSuccess(any()); } - @Override - protected TbEntityGetAttrNode getEmptyNode() { - return new TbGetTenantAttributeNode(); + @Test + public void givenConfigWithUnsupportedDataToFetch_whenInit_thenException() { + // GIVEN + config.setDataToFetch(DataToFetch.FIELDS); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + + // WHEN + var exception = assertThrows(TbNodeException.class, () -> node.init(ctxMock, nodeConfiguration)); + + // THEN + assertThat(exception.getMessage()).isEqualTo("DataToFetch property has invalid value: FIELDS. Only ATTRIBUTES and LATEST_TELEMETRY values supported!"); + verify(ctxMock, never()).tellSuccess(any()); } - @Override - EntityId getEntityId() { - return tenantId; + @Test + public void givenDefaultConfig_whenInit_thenOK() throws TbNodeException { + // GIVEN + + // WHEN + node.init(ctxMock, nodeConfiguration); + + // THEN + assertThat(node.config).isEqualTo(config); + assertThat(config.getDataMapping()).isEqualTo(Map.of("alarmThreshold", "threshold")); + assertThat(config.getDataToFetch()).isEqualTo(DataToFetch.ATTRIBUTES); + assertThat(node.fetchTo).isEqualTo(TbMsgSource.METADATA); } @Test - public void errorThrownIfCannotLoadAttributes() { - errorThrownIfCannotLoadAttributes(user); + public void givenCustomConfig_whenInit_thenOK() throws TbNodeException { + // GIVEN + config.setDataMapping(Map.of( + "sourceAttr1", "targetKey1", + "sourceAttr2", "targetKey2", + "sourceAttr3", "targetKey3")); + config.setDataToFetch(DataToFetch.LATEST_TELEMETRY); + config.setFetchTo(TbMsgSource.DATA); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + + // WHEN + node.init(ctxMock, nodeConfiguration); + + // THEN + assertThat(node.config).isEqualTo(config); + assertThat(config.getDataMapping()).isEqualTo(Map.of( + "sourceAttr1", "targetKey1", + "sourceAttr2", "targetKey2", + "sourceAttr3", "targetKey3")); + assertThat(config.getDataToFetch()).isEqualTo(DataToFetch.LATEST_TELEMETRY); + assertThat(node.fetchTo).isEqualTo(TbMsgSource.DATA); } @Test - public void errorThrownIfCannotLoadAttributesAsync() { - errorThrownIfCannotLoadAttributesAsync(user); + public void givenEmptyAttributesMapping_whenInit_thenException() { + // GIVEN + var expectedExceptionMessage = "At least one mapping entry should be specified!"; + + config.setDataMapping(Collections.emptyMap()); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + + // WHEN + var exception = assertThrows(TbNodeException.class, () -> node.init(ctxMock, nodeConfiguration)); + + // THEN + assertThat(exception.getMessage()).isEqualTo(expectedExceptionMessage); + verify(ctxMock, never()).tellSuccess(any()); } @Test - public void failedChainUsedIfTenantIdFromCtxCannotBeFound() { - when(ctx.getTenantId()).thenReturn(null); - failedChainUsedIfCustomerCannotBeFound(user); + public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { + // GIVEN + node.fetchTo = TbMsgSource.DATA; + msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + + // WHEN + var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); + + // THEN + assertThat(exception.getMessage()).isEqualTo("Message body is not an object!"); + verify(ctxMock, never()).tellSuccess(any()); } @Test - public void customerAttributeAddedInMetadata() { - entityAttributeAddedInMetadata(tenantId, "TENANT"); + public void givenFetchAttributesToData_whenOnMsg_thenShouldFetchAttributesToData() { + // GIVEN + var deviceId = new DeviceId(UUID.randomUUID()); + + prepareMsgAndConfig(TbMsgSource.DATA, DataToFetch.ATTRIBUTES, deviceId); + + List attributesList = List.of( + new BaseAttributeKvEntry(new StringDataEntry("sourceKey1", "sourceValue1"), 1L), + new BaseAttributeKvEntry(new StringDataEntry("sourceKey2", "sourceValue2"), 2L), + new BaseAttributeKvEntry(new StringDataEntry("sourceKey3", "sourceValue3"), 3L) + ); + var expectedPatternProcessedKeysList = List.of("sourceKey1", "sourceKey2", "sourceKey3"); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + + when(ctxMock.getAttributesService()).thenReturn(attributesServiceMock); + when(attributesServiceMock.find(eq(TENANT_ID), eq(TENANT_ID), eq(DataConstants.SERVER_SCOPE), argThat(new ListMatcher<>(expectedPatternProcessedKeysList)))) + .thenReturn(Futures.immediateFuture(attributesList)); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgData = "{\"temp\":42," + + "\"humidity\":77," + + "\"messageBodyPattern1\":\"targetKey2\"," + + "\"messageBodyPattern2\":\"sourceKey3\"," + + "\"targetKey1\":\"sourceValue1\"," + + "\"targetKey2\":\"sourceValue2\"," + + "\"targetKey3\":\"sourceValue3\"}"; + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(expectedMsgData); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(msg.getMetaData()); } @Test - public void usersCustomerAttributesFetched() { - usersCustomerAttributesFetched(user); + public void givenFetchAttributesToMetaData_whenOnMsg_thenShouldFetchAttributesToMetaData() { + // GIVEN + prepareMsgAndConfig(TbMsgSource.METADATA, DataToFetch.ATTRIBUTES, TENANT_ID); + + List attributesList = List.of( + new BaseAttributeKvEntry(new StringDataEntry("sourceKey1", "sourceValue1"), 1L), + new BaseAttributeKvEntry(new StringDataEntry("sourceKey2", "sourceValue2"), 2L), + new BaseAttributeKvEntry(new StringDataEntry("sourceKey3", "sourceValue3"), 3L) + ); + var expectedPatternProcessedKeysList = List.of("sourceKey1", "sourceKey2", "sourceKey3"); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + + when(ctxMock.getAttributesService()).thenReturn(attributesServiceMock); + when(attributesServiceMock.find(eq(TENANT_ID), eq(TENANT_ID), eq(DataConstants.SERVER_SCOPE), argThat(new ListMatcher<>(expectedPatternProcessedKeysList)))) + .thenReturn(Futures.immediateFuture(attributesList)); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgMetaData = new TbMsgMetaData(Map.of( + "metaDataPattern1", "sourceKey2", + "metaDataPattern2", "targetKey3", + "targetKey1", "sourceValue1", + "targetKey2", "sourceValue2", + "targetKey3", "sourceValue3" + )); + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(msg.getData()); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(expectedMsgMetaData); } @Test - public void assetsCustomerAttributesFetched() { - assetsCustomerAttributesFetched(asset); + public void givenFetchTelemetryToData_whenOnMsg_thenShouldFetchTelemetryToData() { + // GIVEN + var customerId = new CustomerId(UUID.randomUUID()); + + prepareMsgAndConfig(TbMsgSource.DATA, DataToFetch.LATEST_TELEMETRY, customerId); + + List timeseries = List.of( + new BasicTsKvEntry(1L, new StringDataEntry("sourceKey1", "sourceValue1")), + new BasicTsKvEntry(1L, new StringDataEntry("sourceKey2", "sourceValue2")), + new BasicTsKvEntry(1L, new StringDataEntry("sourceKey3", "sourceValue3")) + ); + var expectedPatternProcessedKeysList = List.of("sourceKey1", "sourceKey2", "sourceKey3"); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + + when(ctxMock.getTimeseriesService()).thenReturn(timeseriesServiceMock); + when(timeseriesServiceMock.findLatest(eq(TENANT_ID), eq(TENANT_ID), argThat(new ListMatcher<>(expectedPatternProcessedKeysList)))) + .thenReturn(Futures.immediateFuture(timeseries)); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgData = "{\"temp\":42," + + "\"humidity\":77," + + "\"messageBodyPattern1\":\"targetKey2\"," + + "\"messageBodyPattern2\":\"sourceKey3\"," + + "\"targetKey1\":\"sourceValue1\"," + + "\"targetKey2\":\"sourceValue2\"," + + "\"targetKey3\":\"sourceValue3\"}"; + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(expectedMsgData); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(msg.getMetaData()); } @Test - public void deviceCustomerAttributesFetched() { - deviceCustomerAttributesFetched(device); + public void givenFetchTelemetryToMetaData_whenOnMsg_thenShouldFetchTelemetryToMetaData() { + // GIVEN + var ruleChainId = new RuleChainId(UUID.randomUUID()); + + prepareMsgAndConfig(TbMsgSource.METADATA, DataToFetch.LATEST_TELEMETRY, ruleChainId); + + List timeseries = List.of( + new BasicTsKvEntry(1L, new StringDataEntry("sourceKey1", "sourceValue1")), + new BasicTsKvEntry(1L, new StringDataEntry("sourceKey2", "sourceValue2")), + new BasicTsKvEntry(1L, new StringDataEntry("sourceKey3", "sourceValue3")) + ); + var expectedPatternProcessedKeysList = List.of("sourceKey1", "sourceKey2", "sourceKey3"); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + + when(ctxMock.getTimeseriesService()).thenReturn(timeseriesServiceMock); + when(timeseriesServiceMock.findLatest(eq(TENANT_ID), eq(TENANT_ID), argThat(new ListMatcher<>(expectedPatternProcessedKeysList)))) + .thenReturn(Futures.immediateFuture(timeseries)); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgMetaData = new TbMsgMetaData(Map.of( + "metaDataPattern1", "sourceKey2", + "metaDataPattern2", "targetKey3", + "targetKey1", "sourceValue1", + "targetKey2", "sourceValue2", + "targetKey3", "sourceValue3" + )); + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(msg.getData()); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(expectedMsgMetaData); } @Test - public void deviceCustomerTelemetryFetched() throws TbNodeException { - deviceCustomerTelemetryFetched(device); + public void givenOldConfig_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception { + var defaultConfig = new TbGetEntityDataNodeConfiguration().defaultConfiguration(); + var node = new TbGetTenantAttributeNode(); + String oldConfig = "{\"attrMapping\":{\"alarmThreshold\":\"threshold\"},\"telemetry\":false}"; + JsonNode configJson = JacksonUtil.toJsonNode(oldConfig); + TbPair upgrade = node.upgrade(0, configJson); + Assertions.assertTrue(upgrade.getFirst()); + Assertions.assertEquals(defaultConfig, JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass())); } + + private void prepareMsgAndConfig(TbMsgSource fetchTo, DataToFetch dataToFetch, EntityId originator) { + config.setDataMapping(Map.of( + "sourceKey1", "targetKey1", + "${metaDataPattern1}", "$[messageBodyPattern1]", + "$[messageBodyPattern2]", "${metaDataPattern2}")); + config.setDataToFetch(dataToFetch); + config.setFetchTo(fetchTo); + + node.config = config; + node.fetchTo = fetchTo; + + var msgMetaData = new TbMsgMetaData(); + msgMetaData.putValue("metaDataPattern1", "sourceKey2"); + msgMetaData.putValue("metaDataPattern2", "targetKey3"); + + var msgData = "{\"temp\":42,\"humidity\":77,\"messageBodyPattern1\":\"targetKey2\",\"messageBodyPattern2\":\"sourceKey3\"}"; + + msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, msgMetaData, msgData); + } + + @RequiredArgsConstructor + private static class ListMatcher implements ArgumentMatcher> { + + private final List expectedList; + + @Override + public boolean matches(List actualList) { + if (actualList == expectedList) { + return true; + } + if (actualList.size() != expectedList.size()) { + return false; + } + return actualList.containsAll(expectedList); + } + + } + } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeTest.java new file mode 100644 index 0000000000..93da628de0 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNodeTest.java @@ -0,0 +1,301 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.metadata; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.util.concurrent.Futures; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.util.ContactBasedEntityDetails; +import org.thingsboard.rule.engine.util.TbMsgSource; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.util.TbPair; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.dao.tenant.TenantService; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class TbGetTenantDetailsNodeTest { + + private static final DeviceId DUMMY_DEVICE_ORIGINATOR = new DeviceId(UUID.randomUUID()); + @Mock + private TbContext ctxMock; + @Mock + private TenantService tenantServiceMock; + private TbGetTenantDetailsNode node; + private TbGetTenantDetailsNodeConfiguration config; + private TbNodeConfiguration nodeConfiguration; + private TbMsg msg; + private Tenant tenant; + + @BeforeEach + public void setUp() { + node = new TbGetTenantDetailsNode(); + config = new TbGetTenantDetailsNodeConfiguration().defaultConfiguration(); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + tenant = new Tenant(); + tenant.setId(new TenantId(UUID.randomUUID())); + tenant.setTitle("Tenant title"); + tenant.setCountry("Tenant country"); + tenant.setCity("Tenant city"); + tenant.setState("Tenant state"); + tenant.setZip("123456"); + tenant.setAddress("Tenant address 1"); + tenant.setAddress2("Tenant address 2"); + tenant.setPhone("+123456789"); + tenant.setEmail("email@tenant.com"); + tenant.setAdditionalInfo(JacksonUtil.toJsonNode("{\"someProperty\":\"someValue\",\"description\":\"Tenant description\"}")); + } + + @Test + public void givenConfigWithNullFetchTo_whenInit_thenException() { + // GIVEN + config.setDetailsList(List.of(ContactBasedEntityDetails.ID)); + config.setFetchTo(null); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + + // WHEN + var exception = assertThrows(TbNodeException.class, () -> node.init(ctxMock, nodeConfiguration)); + + // THEN + assertThat(exception.getMessage()).isEqualTo("FetchTo option can't be null! Allowed values: " + Arrays.toString(TbMsgSource.values())); + verify(ctxMock, never()).tellSuccess(any()); + } + + @Test + public void givenDefaultConfig_whenInit_thenOK() { + // THEN + assertThat(config.getDetailsList()).isEqualTo(Collections.emptyList()); + assertThat(config.getFetchTo()).isEqualTo(TbMsgSource.DATA); + } + + @Test + public void givenCustomConfig_whenInit_thenOK() throws TbNodeException { + // GIVEN + config.setDetailsList(List.of(ContactBasedEntityDetails.ID, ContactBasedEntityDetails.PHONE)); + config.setFetchTo(TbMsgSource.METADATA); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + + // WHEN + node.init(ctxMock, nodeConfiguration); + + // THEN + assertThat(node.config).isEqualTo(config); + assertThat(config.getDetailsList()).isEqualTo(List.of(ContactBasedEntityDetails.ID, ContactBasedEntityDetails.PHONE)); + assertThat(config.getFetchTo()).isEqualTo(TbMsgSource.METADATA); + assertThat(node.fetchTo).isEqualTo(TbMsgSource.METADATA); + } + + @Test + public void givenMsgDataIsNotAnJsonObjectAndFetchToData_whenOnMsg_thenException() { + // GIVEN + node.fetchTo = TbMsgSource.DATA; + msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY); + + // WHEN + var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg)); + + // THEN + assertThat(exception.getMessage()).isEqualTo("Message body is not an object!"); + verify(ctxMock, never()).tellSuccess(any()); + } + + @Test + public void givenAllEntityDetailsAndFetchToData_whenOnMsg_thenShouldTellSuccessAndFetchAllToData() { + // GIVEN + prepareMsgAndConfig(TbMsgSource.DATA, List.of(ContactBasedEntityDetails.values())); + + mockFindTenant(); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgData = "{\"dataKey1\":123,\"dataKey2\":\"dataValue2\"," + + "\"tenant_id\":\"" + tenant.getId() + "\"," + + "\"tenant_title\":\"" + tenant.getTitle() + "\"," + + "\"tenant_country\":\"" + tenant.getCountry() + "\"," + + "\"tenant_city\":\"" + tenant.getCity() + "\"," + + "\"tenant_state\":\"" + tenant.getState() + "\"," + + "\"tenant_zip\":\"" + tenant.getZip() + "\"," + + "\"tenant_address\":\"" + tenant.getAddress() + "\"," + + "\"tenant_address2\":\"" + tenant.getAddress2() + "\"," + + "\"tenant_phone\":\"" + tenant.getPhone() + "\"," + + "\"tenant_email\":\"" + tenant.getEmail() + "\"," + + "\"tenant_additionalInfo\":\"" + tenant.getAdditionalInfo().get("description").asText() + "\"}"; + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(expectedMsgData); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(msg.getMetaData()); + } + + @Test + public void givenSomeEntityDetailsAndFetchToMetadata_whenOnMsg_thenShouldTellSuccessAndFetchSomeToMetaData() { + // GIVEN + prepareMsgAndConfig(TbMsgSource.METADATA, List.of(ContactBasedEntityDetails.ID, ContactBasedEntityDetails.TITLE, ContactBasedEntityDetails.PHONE)); + + mockFindTenant(); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + var expectedMsgMetaData = new TbMsgMetaData(msg.getMetaData().getData()); + expectedMsgMetaData.putValue("tenant_id", tenant.getId().getId().toString()); + expectedMsgMetaData.putValue("tenant_title", tenant.getTitle()); + expectedMsgMetaData.putValue("tenant_phone", tenant.getPhone()); + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(msg.getData()); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(expectedMsgMetaData); + } + + @Test + public void givenNotPresentEntityDetailsAndFetchToData_whenOnMsg_thenShouldTellSuccessAndFetchNothingToData() { + // GIVEN + tenant.setZip(null); + tenant.setAddress(null); + tenant.setAddress2(null); + + prepareMsgAndConfig(TbMsgSource.DATA, List.of(ContactBasedEntityDetails.ZIP, ContactBasedEntityDetails.ADDRESS, ContactBasedEntityDetails.ADDRESS2)); + + mockFindTenant(); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(msg.getData()); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(msg.getMetaData()); + } + + @Test + public void givenDidNotFindTenant_whenOnMsg_thenShouldTellSuccessAndFetchNothingToData() { + // GIVEN + prepareMsgAndConfig(TbMsgSource.DATA, List.of(ContactBasedEntityDetails.ZIP, ContactBasedEntityDetails.ADDRESS, ContactBasedEntityDetails.ADDRESS2)); + + when(ctxMock.getTenantId()).thenReturn(tenant.getId()); + when(ctxMock.getTenantService()).thenReturn(tenantServiceMock); + when(tenantServiceMock.findTenantByIdAsync(eq(tenant.getId()), eq(tenant.getId()))).thenReturn(Futures.immediateFuture(null)); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(msg.getData()); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(msg.getMetaData()); + } + + @Test + public void givenNullDescriptionAndAddInfoEntityDetails_whenOnMsg_thenShouldTellSuccessAndFetchNothingToData() { + // GIVEN + tenant.setAdditionalInfo(JacksonUtil.toJsonNode("{\"someProperty\":\"someValue\",\"description\":null}")); + + prepareMsgAndConfig(TbMsgSource.DATA, List.of(ContactBasedEntityDetails.ADDITIONAL_INFO)); + + mockFindTenant(); + + // WHEN + node.onMsg(ctxMock, msg); + + // THEN + var actualMessageCaptor = ArgumentCaptor.forClass(TbMsg.class); + + verify(ctxMock, times(1)).tellSuccess(actualMessageCaptor.capture()); + verify(ctxMock, never()).tellFailure(any(), any()); + + assertThat(actualMessageCaptor.getValue().getData()).isEqualTo(msg.getData()); + assertThat(actualMessageCaptor.getValue().getMetaData()).isEqualTo(msg.getMetaData()); + } + + @Test + public void givenOldConfig_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception { + var defaultConfig = new TbGetTenantDetailsNodeConfiguration().defaultConfiguration(); + var node = new TbGetTenantDetailsNode(); + String oldConfig = "{\"detailsList\":[],\"addToMetadata\":false}"; + JsonNode configJson = JacksonUtil.toJsonNode(oldConfig); + TbPair upgrade = node.upgrade(0, configJson); + Assertions.assertTrue(upgrade.getFirst()); + Assertions.assertEquals(defaultConfig, JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass())); + } + + private void prepareMsgAndConfig(TbMsgSource fetchTo, List detailsList) { + config.setDetailsList(detailsList); + config.setFetchTo(fetchTo); + + node.config = config; + node.fetchTo = fetchTo; + + var msgMetaData = new TbMsgMetaData(); + msgMetaData.putValue("metaKey1", "metaValue1"); + msgMetaData.putValue("metaKey2", "metaValue2"); + + var msgData = "{\"dataKey1\":123,\"dataKey2\":\"dataValue2\"}"; + + msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DUMMY_DEVICE_ORIGINATOR, msgMetaData, msgData); + } + + private void mockFindTenant() { + when(ctxMock.getTenantId()).thenReturn(tenant.getId()); + when(ctxMock.getTenantService()).thenReturn(tenantServiceMock); + when(tenantServiceMock.findTenantByIdAsync(eq(tenant.getId()), eq(tenant.getId()))).thenReturn(Futures.immediateFuture(tenant)); + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java index d33b2af030..eef77a5304 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java @@ -22,7 +22,6 @@ import org.mockito.ArgumentCaptor; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.RuleEngineAlarmService; import org.thingsboard.rule.engine.api.TbContext; -import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; @@ -39,12 +38,12 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.device.profile.SimpleAlarmConditionSpec; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.query.BooleanFilterPredicate; import org.thingsboard.server.common.data.query.EntityKeyValueType; import org.thingsboard.server.common.data.query.FilterPredicateValue; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; -import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.device.DeviceService; @@ -92,9 +91,10 @@ public class DeviceStateTest { }); when(ctx.getAlarmService()).thenReturn(alarmService); - when(ctx.newMsg(any(), any(), any(), any(), any(), any())).thenAnswer(invocationOnMock -> { + when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), any())).thenAnswer(invocationOnMock -> { + TbMsgType type = invocationOnMock.getArgument(1); String data = invocationOnMock.getArgument(invocationOnMock.getArguments().length - 1); - return TbMsg.newMsg(null, null, new TbMsgMetaData(), data); + return TbMsg.newMsg(type, null, TbMsgMetaData.EMPTY, data); }); } @@ -106,8 +106,8 @@ public class DeviceStateTest { DeviceId deviceId = new DeviceId(UUID.randomUUID()); DeviceState deviceState = createDeviceState(deviceId, alarmConfig); - TbMsg attributeUpdateMsg = TbMsg.newMsg(SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), - deviceId, new TbMsgMetaData(), "{ \"enabled\": false }"); + TbMsg attributeUpdateMsg = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, + deviceId, TbMsgMetaData.EMPTY, "{ \"enabled\": false }"); deviceState.process(ctx, attributeUpdateMsg); @@ -115,11 +115,11 @@ public class DeviceStateTest { verify(ctx).enqueueForTellNext(resultMsgCaptor.capture(), eq("Alarm Created")); Alarm alarm = JacksonUtil.fromString(resultMsgCaptor.getValue().getData(), Alarm.class); - deviceState.process(ctx, TbMsg.newMsg(DataConstants.ALARM_CLEAR, deviceId, new TbMsgMetaData(), JacksonUtil.toString(alarm))); + deviceState.process(ctx, TbMsg.newMsg(TbMsgType.ALARM_CLEAR, deviceId, TbMsgMetaData.EMPTY, JacksonUtil.toString(alarm))); reset(ctx); String deletedAttributes = "{ \"attributes\": [ \"other\" ] }"; - deviceState.process(ctx, TbMsg.newMsg(DataConstants.ATTRIBUTES_DELETED, deviceId, new TbMsgMetaData(), deletedAttributes)); + deviceState.process(ctx, TbMsg.newMsg(TbMsgType.ATTRIBUTES_DELETED, deviceId, TbMsgMetaData.EMPTY, deletedAttributes)); verify(ctx, never()).enqueueForTellNext(any(), anyString()); } @@ -129,17 +129,17 @@ public class DeviceStateTest { DeviceId deviceId = new DeviceId(UUID.randomUUID()); DeviceState deviceState = createDeviceState(deviceId, alarmConfig); - TbMsg attributeUpdateMsg = TbMsg.newMsg(SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), - deviceId, new TbMsgMetaData(), "{ \"enabled\": false }"); + TbMsg attributeUpdateMsg = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, + deviceId, TbMsgMetaData.EMPTY, "{ \"enabled\": false }"); deviceState.process(ctx, attributeUpdateMsg); ArgumentCaptor resultMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); verify(ctx).enqueueForTellNext(resultMsgCaptor.capture(), eq("Alarm Created")); Alarm alarm = JacksonUtil.fromString(resultMsgCaptor.getValue().getData(), Alarm.class); - deviceState.process(ctx, TbMsg.newMsg(DataConstants.ALARM_CLEAR, deviceId, new TbMsgMetaData(), JacksonUtil.toString(alarm))); + deviceState.process(ctx, TbMsg.newMsg(TbMsgType.ALARM_CLEAR, deviceId, TbMsgMetaData.EMPTY, JacksonUtil.toString(alarm))); - TbMsg alarmDeleteNotification = TbMsg.newMsg(DataConstants.ALARM_DELETE, deviceId, new TbMsgMetaData(), JacksonUtil.toString(alarm)); + TbMsg alarmDeleteNotification = TbMsg.newMsg(TbMsgType.ALARM_DELETE, deviceId, TbMsgMetaData.EMPTY, JacksonUtil.toString(alarm)); assertDoesNotThrow(() -> { deviceState.process(ctx, alarmDeleteNotification); }); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java index 80ed769a71..ad23b7492b 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java @@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; import org.thingsboard.server.common.data.alarm.AlarmInfo; +import org.thingsboard.server.common.data.alarm.AlarmModificationRequest; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.device.profile.AlarmCondition; import org.thingsboard.server.common.data.device.profile.AlarmConditionFilter; @@ -54,6 +55,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.query.BooleanFilterPredicate; import org.thingsboard.server.common.data.query.DynamicValue; import org.thingsboard.server.common.data.query.DynamicValueSourceType; @@ -64,7 +66,6 @@ import org.thingsboard.server.common.data.query.NumericFilterPredicate; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; -import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey; @@ -122,8 +123,8 @@ public class TbDeviceProfileNodeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 42); - TbMsg msg = TbMsg.newMsg("123456789", deviceId, new TbMsgMetaData(), - TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + TbMsg msg = TbMsg.newMsg("123456789", deviceId, TbMsgMetaData.EMPTY, + TbMsgDataType.JSON, JacksonUtil.toString(data)); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); @@ -141,7 +142,7 @@ public class TbDeviceProfileNodeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 42); - TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); @@ -193,25 +194,25 @@ public class TbDeviceProfileNodeTest { Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")).thenReturn(null); registerCreateAlarmMock(alarmService.createAlarm(any()), true); - TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); - Mockito.when(ctx.newMsg(Mockito.any(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())).thenReturn(theMsg); + TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())).thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 42); - TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg); verify(ctx).tellSuccess(msg); verify(ctx).enqueueForTellNext(theMsg, "Alarm Created"); verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); - TbMsg theMsg2 = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), "2"); - Mockito.when(ctx.newMsg(Mockito.any(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())).thenReturn(theMsg2); + TbMsg theMsg2 = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, "2"); + when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())).thenReturn(theMsg2); registerCreateAlarmMock(alarmService.updateAlarm(any()), false); - TbMsg msg2 = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg2 = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg2); verify(ctx).tellSuccess(msg2); @@ -219,6 +220,94 @@ public class TbDeviceProfileNodeTest { } + @Test + public void testAlarmSeverityUpdate() throws Exception { + init(); + + DeviceProfile deviceProfile = new DeviceProfile(); + DeviceProfileData deviceProfileData = new DeviceProfileData(); + + AlarmConditionFilter tempFilter = new AlarmConditionFilter(); + tempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); + tempFilter.setValueType(EntityKeyValueType.NUMERIC); + NumericFilterPredicate temperaturePredicate = new NumericFilterPredicate(); + temperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); + temperaturePredicate.setValue(new FilterPredicateValue<>(30.0)); + tempFilter.setPredicate(temperaturePredicate); + AlarmCondition alarmTempCondition = new AlarmCondition(); + alarmTempCondition.setCondition(Collections.singletonList(tempFilter)); + AlarmRule alarmTempRule = new AlarmRule(); + alarmTempRule.setCondition(alarmTempCondition); + + AlarmConditionFilter highTempFilter = new AlarmConditionFilter(); + highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); + highTempFilter.setValueType(EntityKeyValueType.NUMERIC); + NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); + highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); + highTemperaturePredicate.setValue(new FilterPredicateValue<>(50.0)); + highTempFilter.setPredicate(highTemperaturePredicate); + AlarmCondition alarmHighTempCondition = new AlarmCondition(); + alarmHighTempCondition.setCondition(Collections.singletonList(highTempFilter)); + AlarmRule alarmHighTempRule = new AlarmRule(); + alarmHighTempRule.setCondition(alarmHighTempCondition); + DeviceProfileAlarm dpa = new DeviceProfileAlarm(); + dpa.setId("highTemperatureAlarmID1"); + dpa.setAlarmType("highTemperatureAlarm1"); + + TreeMap createRules = new TreeMap<>(); + + createRules.put(AlarmSeverity.WARNING, alarmTempRule); + createRules.put(AlarmSeverity.CRITICAL, alarmHighTempRule); + + dpa.setCreateRules(createRules); + + deviceProfileData.setAlarms(Collections.singletonList(dpa)); + deviceProfile.setProfileData(deviceProfileData); + + Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); + Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) + .thenReturn(Futures.immediateFuture(Collections.emptyList())); + Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm1")).thenReturn(null); + registerCreateAlarmMock(alarmService.createAlarm(any()), true); + + TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())).thenReturn(theMsg); + + ObjectNode data = JacksonUtil.newObjectNode(); + data.put("temperature", 42); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, + TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + node.onMsg(ctx, msg); + verify(ctx).tellSuccess(msg); + verify(ctx).enqueueForTellNext(theMsg, "Alarm Created"); + verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); + + TbMsg theMsg2 = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())).thenReturn(theMsg2); + + AlarmInfo alarm = new AlarmInfo(new Alarm(new AlarmId(UUID.randomUUID()))); + alarm.setSeverity(AlarmSeverity.CRITICAL); + + Alarm oldAlarm = new Alarm(new AlarmId(UUID.randomUUID())); + oldAlarm.setSeverity(AlarmSeverity.WARNING); + var result = AlarmApiCallResult.builder() + .successful(true) + .created(false) + .modified(true) + .alarm(alarm) + .old(oldAlarm) + .build(); + + when(alarmService.updateAlarm(any())).thenReturn(result); + + data.put("temperature", 52); + TbMsg msg2 = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, + TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); + node.onMsg(ctx, msg2); + verify(ctx).tellSuccess(msg2); + verify(ctx).enqueueForTellNext(theMsg2, "Alarm Severity Updated"); + } + @Test public void testConstantKeyFilterSimple() throws Exception { init(); @@ -286,13 +375,13 @@ public class TbDeviceProfileNodeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(attrListListenableFuture); - TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); - Mockito.when(ctx.newMsg(Mockito.any(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) + TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + Mockito.when(ctx.newMsg(Mockito.any(), Mockito.any(TbMsgType.class), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 21); - TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg); @@ -373,13 +462,13 @@ public class TbDeviceProfileNodeTest { Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), Mockito.anyString(), Mockito.anyString())) .thenReturn(attrListListenableFuture); - TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); - Mockito.when(ctx.newMsg(Mockito.any(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) + TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 21); - TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg); @@ -442,13 +531,13 @@ public class TbDeviceProfileNodeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(listListenableFutureWithLess); - TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); - Mockito.when(ctx.newMsg(Mockito.any(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) + TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg); @@ -536,13 +625,13 @@ public class TbDeviceProfileNodeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(listListenableFuture); - TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); - Mockito.when(ctx.newMsg(Mockito.any(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) + TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg); @@ -557,7 +646,7 @@ public class TbDeviceProfileNodeTest { Thread.sleep(halfOfAlarmDelay); - TbMsg msg2 = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg2 = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg2); @@ -660,13 +749,13 @@ public class TbDeviceProfileNodeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(listNoDurationAttribute); - TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); - Mockito.when(ctx.newMsg(Mockito.any(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) + TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 150); - TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg); @@ -681,7 +770,7 @@ public class TbDeviceProfileNodeTest { Thread.sleep(halfOfAlarmDelay); - TbMsg msg2 = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg2 = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg2); @@ -769,13 +858,13 @@ public class TbDeviceProfileNodeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(listListenableFuture); - TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); - Mockito.when(ctx.newMsg(Mockito.any(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) + TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 150); - TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg); @@ -784,7 +873,7 @@ public class TbDeviceProfileNodeTest { verify(ctx, Mockito.never()).tellNext(theMsg, "Alarm Created"); data.put("temperature", 151); - TbMsg msg2 = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg2 = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg2); @@ -885,13 +974,13 @@ public class TbDeviceProfileNodeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(listNoDurationAttribute); - TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); - Mockito.when(ctx.newMsg(Mockito.any(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) + TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 150); - TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg); @@ -900,7 +989,7 @@ public class TbDeviceProfileNodeTest { verify(ctx, Mockito.never()).tellNext(theMsg, "Alarm Created"); data.put("temperature", 151); - TbMsg msg2 = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg2 = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg2); @@ -981,13 +1070,13 @@ public class TbDeviceProfileNodeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(listListenableFuture); - TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); - Mockito.when(ctx.newMsg(Mockito.any(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) + TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg); @@ -1002,7 +1091,7 @@ public class TbDeviceProfileNodeTest { Thread.sleep(halfOfAlarmDelay); - TbMsg msg2 = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg2 = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg2); @@ -1079,13 +1168,13 @@ public class TbDeviceProfileNodeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(listListenableFuture); - TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); - Mockito.when(ctx.newMsg(Mockito.any(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) + TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg); @@ -1161,13 +1250,13 @@ public class TbDeviceProfileNodeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(listListenableFutureActiveSchedule); - TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); - Mockito.when(ctx.newMsg(Mockito.any(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) + TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); // Mockito.reset(ctx); @@ -1257,11 +1346,11 @@ public class TbDeviceProfileNodeTest { Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(listListenableFutureInactiveSchedule); - TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); + TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 35); - TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg); @@ -1335,13 +1424,13 @@ public class TbDeviceProfileNodeTest { Mockito.when(attributesService.find(eq(tenantId), eq(customerId), eq(DataConstants.SERVER_SCOPE), Mockito.anyString())) .thenReturn(optionalListenableFutureWithLess); - TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); - Mockito.when(ctx.newMsg(Mockito.any(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) + TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 25); - TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg); @@ -1408,13 +1497,13 @@ public class TbDeviceProfileNodeTest { Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(DataConstants.SERVER_SCOPE), Mockito.anyString())) .thenReturn(optionalListenableFutureWithLess); - TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); - Mockito.when(ctx.newMsg(Mockito.any(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) + TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 40); - TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg); @@ -1491,13 +1580,13 @@ public class TbDeviceProfileNodeTest { Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(DataConstants.SERVER_SCOPE), Mockito.anyString())) .thenReturn(optionalListenableFutureWithLess); - TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); - Mockito.when(ctx.newMsg(Mockito.any(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) + TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 150L); - TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg); @@ -1576,13 +1665,13 @@ public class TbDeviceProfileNodeTest { Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(DataConstants.SERVER_SCOPE), Mockito.anyString())) .thenReturn(optionalListenableFutureWithLess); - TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); - Mockito.when(ctx.newMsg(Mockito.any(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) + TbMsg theMsg = TbMsg.newMsg(TbMsgType.ALARM, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + when(ctx.newMsg(any(), any(TbMsgType.class), any(), any(), any(), Mockito.anyString())) .thenReturn(theMsg); ObjectNode data = JacksonUtil.newObjectNode(); data.put("temperature", 150L); - TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, JacksonUtil.toString(data), null, null); node.onMsg(ctx, msg); @@ -1605,9 +1694,9 @@ public class TbDeviceProfileNodeTest { private void registerCreateAlarmMock(AlarmApiCallResult a, boolean created) { when(a).thenAnswer(invocationOnMock -> { -// AlarmCreateOrUpdateActiveRequest request = invocationOnMock.getArgument(0); AlarmInfo alarm = new AlarmInfo(new Alarm(new AlarmId(UUID.randomUUID()))); - alarm.setSeverity(AlarmSeverity.CRITICAL); + AlarmModificationRequest request = invocationOnMock.getArgument(0); + alarm.setSeverity(request.getSeverity()); return AlarmApiCallResult.builder() .successful(true) .created(created) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java index 14ab6cfbbf..bd69939f94 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbHttpClientTest.java @@ -18,26 +18,29 @@ package org.thingsboard.rule.engine.rest; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; -import org.assertj.core.api.Assertions; -import org.awaitility.Awaitility; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.mockserver.integration.ClientAndServer; import org.springframework.util.LinkedMultiValueMap; import org.springframework.web.client.AsyncRestTemplate; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import java.net.URI; import java.util.List; import java.util.Map; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -45,6 +48,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.willCallRealMethod; import static org.mockito.Mockito.mock; @@ -60,13 +64,13 @@ public class TbHttpClientTest { EventLoopGroup eventLoop; TbHttpClient client; - @Before + @BeforeEach public void setUp() throws Exception { client = mock(TbHttpClient.class); - willCallRealMethod().given(client).getSharedOrCreateEventLoopGroup(any()); + when(client.getSharedOrCreateEventLoopGroup(any())).thenCallRealMethod(); } - @After + @AfterEach public void tearDown() throws Exception { if (eventLoop != null) { eventLoop.shutdownGracefully(); @@ -90,7 +94,7 @@ public class TbHttpClientTest { Mockito.when(client.buildEncodedUri(any())).thenCallRealMethod(); String url = "http://localhost:8080/"; URI uri = client.buildEncodedUri(url); - Assert.assertEquals(url, uri.toString()); + Assertions.assertEquals(url, uri.toString()); } @Test @@ -113,7 +117,7 @@ public class TbHttpClientTest { String url = "http://192.168.1.1/data?d={\"a\": 12}"; String expected = "http://192.168.1.1/data?d=%7B%22a%22:%2012%7D"; URI uri = client.buildEncodedUri(url); - Assert.assertEquals(expected, uri.toString()); + Assertions.assertEquals(expected, uri.toString()); } @Test @@ -144,16 +148,15 @@ public class TbHttpClientTest { var httpClient = new TbHttpClient(config, eventLoop); httpClient.setHttpClient(asyncRestTemplate); - var msg = TbMsg.newMsg("GET", new DeviceId(EntityId.NULL_UUID), TbMsgMetaData.EMPTY, "{}"); + var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, new DeviceId(EntityId.NULL_UUID), TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); var successMsg = TbMsg.newMsg( - "SUCCESS", msg.getOriginator(), + TbMsgType.POST_TELEMETRY_REQUEST, msg.getOriginator(), msg.getMetaData(), msg.getData() ); var ctx = mock(TbContext.class); when(ctx.transformMsg( - eq(msg), eq(msg.getType()), - eq(msg.getOriginator()), + eq(msg), eq(msg.getMetaData()), eq(msg.getData()) )).thenReturn(successMsg); @@ -161,28 +164,28 @@ public class TbHttpClientTest { var capturedData = ArgumentCaptor.forClass(String.class); when(ctx.transformMsg( - eq(msg), eq(msg.getType()), - eq(msg.getOriginator()), + eq(msg), any(), capturedData.capture() )).thenReturn(successMsg); - httpClient.processMessage(ctx, msg); - - Awaitility.await() - .atMost(30, TimeUnit.SECONDS) - .until(() -> { - try { - verify(ctx, times(1)).tellSuccess(any()); - return true; - } catch (Exception e) { - return false; - } + CountDownLatch latch = new CountDownLatch(1); + + httpClient.processMessage(ctx, msg, + m -> { + ctx.tellSuccess(msg); + latch.countDown(); + }, + (m, t) -> { + ctx.tellFailure(m, t); + latch.countDown(); }); + latch.await(5, TimeUnit.SECONDS); + verify(ctx, times(1)).tellSuccess(any()); verify(ctx, times(0)).tellFailure(any(), any()); - Assert.assertEquals(successResponseBody, capturedData.getValue()); + Assertions.assertEquals(successResponseBody, capturedData.getValue()); } private ClientAndServer setUpDummyServer(String host, String path, String paramKey, String paramVal, String successResponseBody) { @@ -218,9 +221,21 @@ public class TbHttpClientTest { Map data = metaData.getData(); - Assertions.assertThat(data).hasSize(2); - Assertions.assertThat(data.get("Content-Type")).isEqualTo("binary"); - Assertions.assertThat(data.get("Set-Cookie")).isEqualTo("[\"sap-context=sap-client=075; path=/\",\"sap-token=sap-client=075; path=/\"]"); + Assertions.assertEquals(2, data.size()); + Assertions.assertEquals(data.get("Content-Type"), "binary"); + Assertions.assertEquals(data.get("Set-Cookie"), "[\"sap-context=sap-client=075; path=/\",\"sap-token=sap-client=075; path=/\"]"); } -} \ No newline at end of file + @ParameterizedTest + @ValueSource(strings = { "false", "\"", "\"\"", "\"This is a string with double quotes\"", "Path: /home/developer/test.txt", + "First line\nSecond line\n\nFourth line", "Before\rAfter", "Tab\tSeparated\tValues", "Test\bbackspace", "[]", + "[1, 2, 3]", "{\"key\": \"value\"}", "{\n\"temperature\": 25.5,\n\"humidity\": 50.2\n\"}", "Expression: (a + b) * c", + "世界", "Україна", "\u1F1FA\u1F1E6", "🇺🇦"}) + public void testParseJsonStringToPlainText(String original) { + Mockito.when(client.parseJsonStringToPlainText(anyString())).thenCallRealMethod(); + + String serialized = JacksonUtil.toString(original); + Assertions.assertNotNull(serialized); + Assertions.assertEquals(original, client.parseJsonStringToPlainText(serialized)); + } +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java index 9185a31118..0a71a799fc 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java @@ -16,7 +16,7 @@ package org.thingsboard.rule.engine.rest; import com.datastax.oss.driver.api.core.uuid.Uuids; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.JsonNode; import org.apache.http.HttpException; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; @@ -27,6 +27,7 @@ import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpRequestHandler; import org.junit.After; import org.junit.Test; +import org.junit.jupiter.api.Assertions; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; @@ -39,6 +40,8 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -91,7 +94,9 @@ public class TbRestApiCallNodeTest { @After public void teardown() { - server.stop(); + if (server != null) { + server.stop(); + } } @Test @@ -136,23 +141,18 @@ public class TbRestApiCallNodeTest { config.setRestEndpointUrlPattern(String.format("http://localhost:%d%s", server.getLocalPort(), path)); initWithConfig(config); - TbMsg msg = TbMsg.newMsg( "USER", originator, metaData, TbMsgDataType.JSON, "{}", ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); restNode.onMsg(ctx, msg); assertTrue("Server handled request", latch.await(10, TimeUnit.SECONDS)); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(String.class); - ArgumentCaptor originatorCaptor = ArgumentCaptor.forClass(EntityId.class); ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class); ArgumentCaptor dataCaptor = ArgumentCaptor.forClass(String.class); - verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); - + verify(ctx).transformMsg(msgCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); - assertEquals("USER", typeCaptor.getValue()); - assertEquals(originator, originatorCaptor.getValue()); assertNotSame(metaData, metadataCaptor.getValue()); - assertEquals("{}", dataCaptor.getValue()); + assertEquals(TbMsg.EMPTY_JSON_OBJECT, dataCaptor.getValue()); } @Test @@ -202,22 +202,34 @@ public class TbRestApiCallNodeTest { config.setRestEndpointUrlPattern(String.format("http://localhost:%d%s", server.getLocalPort(), path)); initWithConfig(config); - TbMsg msg = TbMsg.newMsg( "USER", originator, metaData, TbMsgDataType.JSON, "{}", ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); restNode.onMsg(ctx, msg); assertTrue("Server handled request", latch.await(10, TimeUnit.SECONDS)); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(String.class); - ArgumentCaptor originatorCaptor = ArgumentCaptor.forClass(EntityId.class); ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class); ArgumentCaptor dataCaptor = ArgumentCaptor.forClass(String.class); - verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); + verify(ctx).transformMsg(msgCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); - assertEquals("USER", typeCaptor.getValue()); - assertEquals(originator, originatorCaptor.getValue()); assertNotSame(metaData, metadataCaptor.getValue()); - assertEquals("{}", dataCaptor.getValue()); + assertEquals(TbMsg.EMPTY_JSON_OBJECT, dataCaptor.getValue()); + } + + @Test + public void givenOldConfig_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception { + var defaultConfig = new TbRestApiCallNodeConfiguration().defaultConfiguration(); + var node = new TbRestApiCallNode(); + String oldConfig = "{\"restEndpointUrlPattern\":\"http://localhost/api\",\"requestMethod\":\"POST\"," + + "\"useSimpleClientHttpFactory\":false,\"ignoreRequestBody\":false,\"enableProxy\":false," + + "\"useSystemProxyProperties\":false,\"proxyScheme\":null,\"proxyHost\":null,\"proxyPort\":0," + + "\"proxyUser\":null,\"proxyPassword\":null,\"readTimeoutMs\":0,\"maxParallelRequestsCount\":0," + + "\"headers\":{\"Content-Type\":\"application/json\"},\"useRedisQueueForMsgPersistence\":false," + + "\"trimQueue\":null,\"maxQueueSize\":null,\"credentials\":{\"type\":\"anonymous\"},\"trimDoubleQuotes\":true}"; + JsonNode configJson = JacksonUtil.toJsonNode(oldConfig); + TbPair upgrade = node.upgrade(0, configJson); + Assertions.assertTrue(upgrade.getFirst()); + Assertions.assertTrue(JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass()).isParseToPlainText()); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java index 27faa8313a..7b12dbb2b1 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java @@ -31,10 +31,10 @@ import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; -import org.thingsboard.server.common.msg.session.SessionMsgType; import org.thingsboard.server.dao.edge.EdgeEventService; import java.util.UUID; @@ -80,7 +80,7 @@ public class TbSendRPCReplyNodeTest { Mockito.when(ctx.getRpcService()).thenReturn(rpcService); - TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, getDefaultMetadata(), + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, getDefaultMetadata(), TbMsgDataType.JSON, DUMMY_DATA, null, null); node.onMsg(ctx, msg); @@ -99,7 +99,7 @@ public class TbSendRPCReplyNodeTest { TbMsgMetaData defaultMetadata = getDefaultMetadata(); defaultMetadata.putValue(DataConstants.EDGE_ID, UUID.randomUUID().toString()); defaultMetadata.putValue(DataConstants.DEVICE_ID, UUID.randomUUID().toString()); - TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, defaultMetadata, + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, defaultMetadata, TbMsgDataType.JSON, DUMMY_DATA, null, null); node.onMsg(ctx, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeConfigurationTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeConfigurationTest.java new file mode 100644 index 0000000000..07833607d4 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeConfigurationTest.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.telemetry; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class TbMsgAttributesNodeConfigurationTest { + + @Test + void testDefaultConfig_givenUpdateAttributesOnlyOnValueChange_thenTrue_sinceVersion1() { + assertThat(new TbMsgAttributesNodeConfiguration().defaultConfiguration().isUpdateAttributesOnlyOnValueChange()).isTrue(); + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java new file mode 100644 index 0000000000..fe2edc941b --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNodeTest.java @@ -0,0 +1,243 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.telemetry; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.ArgumentCaptor; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +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.JsonDataEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.util.TbPair; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.willCallRealMethod; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.thingsboard.server.common.data.DataConstants.NOTIFY_DEVICE_METADATA_KEY; + +@Slf4j +class TbMsgAttributesNodeTest { + + private TenantId tenantId; + private DeviceId deviceId; + private TbMsgAttributesNode node; + + @BeforeEach + void setUp() { + tenantId = new TenantId(UUID.fromString("6c18691e-4470-4766-9739-aface71d761f")); + deviceId = new DeviceId(UUID.fromString("b66159d7-c77e-45e8-bb41-a8f557f434c1")); + node = spy(TbMsgAttributesNode.class); + } + + @Test + void testFilterChangedAttr_whenCurrentAttributesEmpty_thenReturnNewAttributes() { + List newAttributes = new ArrayList<>(); + + List filtered = node.filterChangedAttr(Collections.emptyList(), newAttributes); + assertThat(filtered).isSameAs(newAttributes); + } + + @Test + void testFilterChangedAttr_whenCurrentAttributesContainsInAnyOrderNewAttributes_thenReturnEmptyList() { + List currentAttributes = List.of( + new BaseAttributeKvEntry(1694000000L, new StringDataEntry("address", "Peremohy ave 1")), + new BaseAttributeKvEntry(1694000000L, new BooleanDataEntry("valid", true)), + new BaseAttributeKvEntry(1694000000L, new LongDataEntry("counter", 100L)), + new BaseAttributeKvEntry(1694000000L, new DoubleDataEntry("temp", -18.35)), + new BaseAttributeKvEntry(1694000000L, new JsonDataEntry("json", "{\"warning\":\"out of paper\"}")) + ); + List newAttributes = new ArrayList<>(currentAttributes); + newAttributes.add(newAttributes.get(0)); + newAttributes.remove(0); + assertThat(newAttributes).hasSize(currentAttributes.size()); + assertThat(currentAttributes).isNotEmpty(); + assertThat(newAttributes).containsExactlyInAnyOrderElementsOf(currentAttributes); + + List filtered = node.filterChangedAttr(currentAttributes, newAttributes); + assertThat(filtered).isEmpty(); //no changes + } + + @Test + void testFilterChangedAttr_whenCurrentAttributesContainsInAnyOrderNewAttributes_thenReturnExpectedList() { + List currentAttributes = List.of( + new BaseAttributeKvEntry(1694000000L, new StringDataEntry("address", "Peremohy ave 1")), + new BaseAttributeKvEntry(1694000000L, new BooleanDataEntry("valid", true)), + new BaseAttributeKvEntry(1694000000L, new LongDataEntry("counter", 100L)), + new BaseAttributeKvEntry(1694000000L, new DoubleDataEntry("temp", -18.35)), + new BaseAttributeKvEntry(1694000000L, new JsonDataEntry("json", "{\"warning\":\"out of paper\"}")) + ); + List newAttributes = List.of( + new BaseAttributeKvEntry(1694000999L, new JsonDataEntry("json", "{\"status\":\"OK\"}")), // value changed, reordered + new BaseAttributeKvEntry(1694000999L, new StringDataEntry("valid", "true")), //type changed + new BaseAttributeKvEntry(1694000999L, new LongDataEntry("counter", 101L)), //value changed + new BaseAttributeKvEntry(1694000999L, new DoubleDataEntry("temp", -18.35)), + new BaseAttributeKvEntry(1694000999L, new StringDataEntry("address", "Peremohy ave 1")) // reordered + ); + List expected = List.of( + new BaseAttributeKvEntry(1694000999L, new StringDataEntry("valid", "true")), + new BaseAttributeKvEntry(1694000999L, new LongDataEntry("counter", 101L)), + new BaseAttributeKvEntry(1694000999L, new JsonDataEntry("json", "{\"status\":\"OK\"}")) + ); + + List filtered = node.filterChangedAttr(currentAttributes, newAttributes); + assertThat(filtered).containsExactlyInAnyOrderElementsOf(expected); + } + + // Notify device backward-compatibility test arguments + private static Stream givenNotifyDeviceMdValue_whenSaveAndNotify_thenVerifyExpectedArgumentForNotifyDeviceInSaveAndNotifyMethod() { + return Stream.of( + Arguments.of(null, true), + Arguments.of("null", false), + Arguments.of("true", true), + Arguments.of("false", false) + ); + } + + // Notify device backward-compatibility test + @ParameterizedTest + @MethodSource + void givenNotifyDeviceMdValue_whenSaveAndNotify_thenVerifyExpectedArgumentForNotifyDeviceInSaveAndNotifyMethod(String mdValue, boolean expectedArgumentValue) throws TbNodeException { + var ctxMock = mock(TbContext.class); + var telemetryServiceMock = mock(RuleEngineTelemetryService.class); + ObjectNode defaultConfig = (ObjectNode) JacksonUtil.valueToTree(new TbMsgAttributesNodeConfiguration().defaultConfiguration()); + defaultConfig.put("notifyDevice", false); + var tbNodeConfiguration = new TbNodeConfiguration(defaultConfig); + + assertThat(defaultConfig.has("notifyDevice")).as("pre condition has notifyDevice").isTrue(); + + when(ctxMock.getTenantId()).thenReturn(tenantId); + when(ctxMock.getTelemetryService()).thenReturn(telemetryServiceMock); + willCallRealMethod().given(node).init(any(TbContext.class), any(TbNodeConfiguration.class)); + willCallRealMethod().given(node).saveAttr(any(), eq(ctxMock), any(TbMsg.class), anyString(), anyBoolean()); + + node.init(ctxMock, tbNodeConfiguration); + + TbMsgMetaData md = new TbMsgMetaData(); + if (mdValue != null) { + md.putValue(NOTIFY_DEVICE_METADATA_KEY, mdValue); + } + // dummy list with one ts kv to pass the empty list check. + var testTbMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, md, TbMsg.EMPTY_STRING); + List testAttrList = List.of(new BaseAttributeKvEntry(0L, new StringDataEntry("testKey", "testValue"))); + + node.saveAttr(testAttrList, ctxMock, testTbMsg, DataConstants.SHARED_SCOPE, false); + + ArgumentCaptor notifyDeviceCaptor = ArgumentCaptor.forClass(Boolean.class); + + verify(telemetryServiceMock, times(1)).saveAndNotify( + eq(tenantId), eq(deviceId), eq(DataConstants.SHARED_SCOPE), + eq(testAttrList), notifyDeviceCaptor.capture(), any() + ); + boolean notifyDevice = notifyDeviceCaptor.getValue(); + assertThat(notifyDevice).isEqualTo(expectedArgumentValue); + } + + + // Rule nodes upgrade + private static Stream givenFromVersionAndConfig_whenUpgrade_thenVerifyHasChangesAndConfig() { + return Stream.of( + // default config for version 0 + Arguments.of(0, + "{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":\"false\",\"sendAttributesUpdatedNotification\":\"false\"}", + true, + "{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":false,\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":false}"), + // default config for version 1 with upgrade from version 0 + Arguments.of(0, + "{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":false,\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":true}", + false, + "{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":false,\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":true}"), + // all flags are booleans + Arguments.of(1, + "{\"scope\":\"SHARED_SCOPE\",\"notifyDevice\":true,\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":true}", + false, + "{\"scope\":\"SHARED_SCOPE\",\"notifyDevice\":true,\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":true}"), + // no boolean flags set + Arguments.of(1, + "{\"scope\":\"CLIENT_SCOPE\"}", + true, + "{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":true,\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":true}"), + // all flags are boolean strings + Arguments.of(1, + "{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":\"false\",\"sendAttributesUpdatedNotification\":\"false\",\"updateAttributesOnlyOnValueChange\":\"true\"}", + true, + "{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":false,\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":true}"), + // at least one flag is boolean string + Arguments.of(1, + "{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":\"false\",\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":true}", + true, + "{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":false,\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":true}"), + // notify device flag is null + Arguments.of(1, + "{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":\"null\",\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":true}", + true, + "{\"scope\":\"CLIENT_SCOPE\",\"notifyDevice\":true,\"sendAttributesUpdatedNotification\":false,\"updateAttributesOnlyOnValueChange\":true}") + ); + } + + @ParameterizedTest + @MethodSource + void givenFromVersionAndConfig_whenUpgrade_thenVerifyHasChangesAndConfig(int givenVersion, String givenConfigStr, boolean hasChanges, String expectedConfigStr) throws TbNodeException { + // GIVEN + willCallRealMethod().given(node).upgrade(anyInt(), any()); + JsonNode givenConfig = JacksonUtil.toJsonNode(givenConfigStr); + JsonNode expectedConfig = JacksonUtil.toJsonNode(expectedConfigStr); + + // WHEN + TbPair upgradeResult = node.upgrade(givenVersion, givenConfig); + + // THEN + assertThat(upgradeResult.getFirst()).isEqualTo(hasChanges); + ObjectNode upgradedConfig = (ObjectNode) upgradeResult.getSecond(); + assertThat(upgradedConfig).isEqualTo(expectedConfig); + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java index c0839f5b61..632243676f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNodeTest.java @@ -25,7 +25,9 @@ import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.queue.TbMsgCallback; @@ -49,10 +51,6 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.thingsboard.server.common.data.DataConstants.NOTIFY_DEVICE_METADATA_KEY; -import static org.thingsboard.server.common.data.DataConstants.SCOPE; -import static org.thingsboard.server.common.data.DataConstants.SERVER_SCOPE; -import static org.thingsboard.server.common.data.DataConstants.SHARED_SCOPE; @Slf4j public class TbMsgDeleteAttributesNodeTest { @@ -94,7 +92,7 @@ public class TbMsgDeleteAttributesNodeTest { @Test void givenDefaultConfig_whenVerify_thenOK() { TbMsgDeleteAttributesNodeConfiguration defaultConfig = new TbMsgDeleteAttributesNodeConfiguration().defaultConfiguration(); - assertThat(defaultConfig.getScope()).isEqualTo(SERVER_SCOPE); + assertThat(defaultConfig.getScope()).isEqualTo(DataConstants.SERVER_SCOPE); assertThat(defaultConfig.getKeys()).isEqualTo(Collections.emptyList()); } @@ -115,7 +113,7 @@ public class TbMsgDeleteAttributesNodeTest { void givenMsg_whenOnMsg_thenVerifyOutput_SendAttributesDeletedNotification_NotifyDevice() throws Exception { config.setSendAttributesDeletedNotification(true); config.setNotifyDevice(true); - config.setScope(SHARED_SCOPE); + config.setScope(DataConstants.SHARED_SCOPE); nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); node.init(ctx, nodeConfiguration); onMsg_thenVerifyOutput(true, true, false); @@ -135,12 +133,12 @@ public class TbMsgDeleteAttributesNodeTest { ); TbMsgMetaData metaData = new TbMsgMetaData(mdMap); if (notifyDeviceMetadata) { - metaData.putValue(NOTIFY_DEVICE_METADATA_KEY, "true"); - metaData.putValue(SCOPE, SHARED_SCOPE); + metaData.putValue(DataConstants.NOTIFY_DEVICE_METADATA_KEY, "true"); + metaData.putValue(DataConstants.SCOPE, DataConstants.SHARED_SCOPE); } final String data = "{\"TestAttribute_2\": \"humidity\", \"TestAttribute_3\": \"voltage\"}"; - TbMsg msg = TbMsg.newMsg("POST_ATTRIBUTES_REQUEST", deviceId, metaData, data, callback); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, deviceId, metaData, data, callback); node.onMsg(ctx, msg); ArgumentCaptor successCaptor = ArgumentCaptor.forClass(Runnable.class); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java index 1f4ed9bc95..8ea7806ae2 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java @@ -17,7 +17,6 @@ package org.thingsboard.rule.engine.transform; import com.datastax.oss.driver.api.core.uuid.Uuids; import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -26,6 +25,7 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ListeningExecutor; +import org.thingsboard.rule.engine.TestDbCallbackExecutor; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; @@ -35,12 +35,13 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.dao.asset.AssetService; -import java.util.concurrent.Callable; +import java.util.NoSuchElementException; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; @@ -48,11 +49,12 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.thingsboard.rule.engine.api.TbRelationTypes.FAILURE; @RunWith(MockitoJUnitRunner.class) public class TbChangeOriginatorNodeTest { + private static final String CUSTOMER_SOURCE = "CUSTOMER"; + private TbChangeOriginatorNode node; @Mock @@ -64,21 +66,7 @@ public class TbChangeOriginatorNodeTest { @Before public void before() { - dbExecutor = new ListeningExecutor() { - @Override - public ListenableFuture executeAsync(Callable task) { - try { - return Futures.immediateFuture(task.call()); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public void execute(Runnable command) { - command.run(); - } - }; + dbExecutor = new TestDbCallbackExecutor(); } @Test @@ -92,7 +80,7 @@ public class TbChangeOriginatorNodeTest { RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); - TbMsg msg = TbMsg.newMsg( "ASSET", assetId, new TbMsgMetaData(), TbMsgDataType.JSON, "{}", ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, assetId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); when(ctx.getAssetService()).thenReturn(assetService); when(assetService.findAssetByIdAsync(any(),eq( assetId))).thenReturn(Futures.immediateFuture(asset)); @@ -100,11 +88,8 @@ public class TbChangeOriginatorNodeTest { node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor originatorCaptor = ArgumentCaptor.forClass(EntityId.class); - ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class); - ArgumentCaptor dataCaptor = ArgumentCaptor.forClass(String.class); - verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); + verify(ctx).transformMsgOriginator(msgCaptor.capture(), originatorCaptor.capture()); assertEquals(customerId, originatorCaptor.getValue()); } @@ -120,18 +105,15 @@ public class TbChangeOriginatorNodeTest { RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); - TbMsg msg = TbMsg.newMsg( "ASSET", assetId, new TbMsgMetaData(), TbMsgDataType.JSON,"{}", ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, assetId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON,TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); when(ctx.getAssetService()).thenReturn(assetService); when(assetService.findAssetByIdAsync(any(), eq(assetId))).thenReturn(Futures.immediateFuture(asset)); node.onMsg(ctx, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - ArgumentCaptor typeCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor originatorCaptor = ArgumentCaptor.forClass(EntityId.class); - ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class); - ArgumentCaptor dataCaptor = ArgumentCaptor.forClass(String.class); - verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture()); + verify(ctx).transformMsgOriginator(msgCaptor.capture(), originatorCaptor.capture()); assertEquals(customerId, originatorCaptor.getValue()); } @@ -147,18 +129,22 @@ public class TbChangeOriginatorNodeTest { RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); - TbMsg msg = TbMsg.newMsg( "ASSET", assetId, new TbMsgMetaData(), TbMsgDataType.JSON,"{}", ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, assetId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON,TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); when(ctx.getAssetService()).thenReturn(assetService); when(assetService.findAssetByIdAsync(any(), eq(assetId))).thenReturn(Futures.immediateFuture(null)); + ArgumentCaptor exceptionCaptor = ArgumentCaptor.forClass(NoSuchElementException.class); + node.onMsg(ctx, msg); - verify(ctx).tellNext(same(msg), same(FAILURE)); + verify(ctx).tellFailure(same(msg), exceptionCaptor.capture()); + + assertEquals("Failed to find new originator!", exceptionCaptor.getValue().getMessage()); } public void init() throws TbNodeException { TbChangeOriginatorNodeConfiguration config = new TbChangeOriginatorNodeConfiguration(); - config.setOriginatorSource(TbChangeOriginatorNode.CUSTOMER_SOURCE); + config.setOriginatorSource(CUSTOMER_SOURCE); TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); when(ctx.getDbCallbackExecutor()).thenReturn(dbExecutor); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java index 2666cae477..ae58298e0d 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbCopyKeysNodeTest.java @@ -16,16 +16,22 @@ package org.thingsboard.rule.engine.transform; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.ArgumentCaptor; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.util.TbMsgSource; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.queue.TbMsgCallback; @@ -34,13 +40,13 @@ import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; public class TbCopyKeysNodeTest { @@ -58,7 +64,7 @@ public class TbCopyKeysNodeTest { ctx = mock(TbContext.class); config = new TbCopyKeysNodeConfiguration().defaultConfiguration(); config.setKeys(Set.of("TestKey_1", "TestKey_2", "TestKey_3", "(\\w*)Data(\\w*)")); - config.setFromMetadata(true); + config.setCopyFrom(TbMsgSource.METADATA); nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); node = spy(new TbCopyKeysNode()); node.init(ctx, nodeConfiguration); @@ -73,16 +79,15 @@ public class TbCopyKeysNodeTest { void givenDefaultConfig_whenVerify_thenOK() { TbCopyKeysNodeConfiguration defaultConfig = new TbCopyKeysNodeConfiguration().defaultConfiguration(); assertThat(defaultConfig.getKeys()).isEqualTo(Collections.emptySet()); - assertThat(defaultConfig.isFromMetadata()).isEqualTo(false); + assertThat(defaultConfig.getCopyFrom()).isEqualTo(TbMsgSource.DATA); } @Test void givenMsgFromMetadata_whenOnMsg_thenVerifyOutput() throws Exception { - String data = "{}"; - node.onMsg(ctx, getTbMsg(deviceId, data)); + node.onMsg(ctx, getTbMsg(deviceId, TbMsg.EMPTY_JSON_OBJECT)); ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); - verify(ctx, times(1)).tellSuccess(newMsgCaptor.capture()); + verify(ctx).tellSuccess(newMsgCaptor.capture()); verify(ctx, never()).tellFailure(any(), any()); TbMsg newMsg = newMsgCaptor.getValue(); @@ -95,23 +100,30 @@ public class TbCopyKeysNodeTest { @Test void givenMsgFromMsg_whenOnMsg_thenVerifyOutput() throws Exception { - config.setFromMetadata(false); + config.setCopyFrom(TbMsgSource.DATA); + config.setKeys(Set.of(".*Key$")); nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); node.init(ctx, nodeConfiguration); - String data = "{\"DigitData\":22.5,\"TempDataValue\":10.5}"; + String data = "{\"nullKey\":null,\"stringKey\":\"value1\",\"booleanKey\":true,\"doubleKey\":42.0,\"longKey\":73," + + "\"jsonKey\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}"; node.onMsg(ctx, getTbMsg(deviceId, data)); ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); - verify(ctx, times(1)).tellSuccess(newMsgCaptor.capture()); + verify(ctx).tellSuccess(newMsgCaptor.capture()); verify(ctx, never()).tellFailure(any(), any()); TbMsg newMsg = newMsgCaptor.getValue(); assertThat(newMsg).isNotNull(); Map metaDataMap = newMsg.getMetaData().getData(); - assertThat(metaDataMap.containsKey("DigitData")).isEqualTo(true); - assertThat(metaDataMap.containsKey("TempDataValue")).isEqualTo(true); + assertThat(metaDataMap.get("nullKey")).isEqualTo("null"); + assertThat(metaDataMap.get("stringKey")).isEqualTo("value1"); + assertThat(metaDataMap.get("booleanKey")).isEqualTo("true"); + assertThat(metaDataMap.get("doubleKey")).isEqualTo("42.0"); + assertThat(metaDataMap.get("longKey")).isEqualTo("73"); + assertThat(metaDataMap.get("jsonKey")) + .isEqualTo("{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}"); } @Test @@ -125,7 +137,7 @@ public class TbCopyKeysNodeTest { node.onMsg(ctx, msg); ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); - verify(ctx, times(1)).tellSuccess(newMsgCaptor.capture()); + verify(ctx).tellSuccess(newMsgCaptor.capture()); verify(ctx, never()).tellFailure(any(), any()); TbMsg newMsg = newMsgCaptor.getValue(); @@ -136,12 +148,11 @@ public class TbCopyKeysNodeTest { @Test void givenMsgDataNotJSONObject_whenOnMsg_thenTVerifyOutput() throws Exception { - String data = "[]"; - TbMsg msg = getTbMsg(deviceId, data); + TbMsg msg = getTbMsg(deviceId, TbMsg.EMPTY_JSON_ARRAY); node.onMsg(ctx, msg); ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); - verify(ctx, times(1)).tellSuccess(newMsgCaptor.capture()); + verify(ctx).tellSuccess(newMsgCaptor.capture()); verify(ctx, never()).tellFailure(any(), any()); TbMsg newMsg = newMsgCaptor.getValue(); @@ -150,6 +161,34 @@ public class TbCopyKeysNodeTest { assertThat(newMsg).isSameAs(msg); } + private static Stream givenFromVersionAndConfig_whenUpgrade_thenVerifyUpgradeResultAndConfig() { + return Stream.of( + Arguments.of(0, "{\"fromMetadata\":false,\"keys\":[\"temperature\"]}", true, "{\"copyFrom\":\"DATA\",\"keys\":[\"temperature\"]}"), + Arguments.of(0, "{\"fromMetadata\":true,\"keys\":[\"temperature\"]}", true, "{\"copyFrom\":\"METADATA\",\"keys\":[\"temperature\"]}"), + Arguments.of(1, "{\"fromMetadata\":\"METADATA\",\"keys\":[\"temperature\"]}", true, "{\"copyFrom\":\"METADATA\",\"keys\":[\"temperature\"]}"), + Arguments.of(1, "{\"fromMetadata\":\"DATA\",\"keys\":[\"temperature\"]}", true, "{\"copyFrom\":\"DATA\",\"keys\":[\"temperature\"]}"), + Arguments.of(1, "{\"copyFrom\":\"METADATA\",\"keys\":[\"temperature\"]}", false, "{\"copyFrom\":\"METADATA\",\"keys\":[\"temperature\"]}"), + Arguments.of(1, "{\"copyFrom\":\"DATA\",\"keys\":[\"temperature\"]}", false, "{\"copyFrom\":\"DATA\",\"keys\":[\"temperature\"]}") + ); + } + + @ParameterizedTest + @MethodSource + void givenFromVersionAndConfig_whenUpgrade_thenVerifyUpgradeResultAndConfig(int givenVersion, String givenConfigStr, + boolean hasChanges, String expectedConfigStr) throws Exception { + // GIVEN + JsonNode givenConfig = JacksonUtil.toJsonNode(givenConfigStr); + JsonNode expectedConfig = JacksonUtil.toJsonNode(expectedConfigStr); + + // WHEN + var upgradeResult = node.upgrade(givenVersion, givenConfig); + + // THEN + assertThat(upgradeResult.getFirst()).isEqualTo(hasChanges); + ObjectNode upgradedConfig = (ObjectNode) upgradeResult.getSecond(); + assertThat(upgradedConfig).isEqualTo(expectedConfig); + } + private TbMsg getTbMsg(EntityId entityId, String data) { final Map mdMap = Map.of( "TestKey_1", "Test", @@ -157,7 +196,7 @@ public class TbCopyKeysNodeTest { "voltageDataValue", "220", "city", "NY" ); - return TbMsg.newMsg("POST_ATTRIBUTES_REQUEST", entityId, new TbMsgMetaData(mdMap), data, callback); + return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, new TbMsgMetaData(mdMap), data, callback); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeTest.java index 7152c25acd..5acc9f2073 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNodeTest.java @@ -16,16 +16,22 @@ package org.thingsboard.rule.engine.transform; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.ArgumentCaptor; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.util.TbMsgSource; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.queue.TbMsgCallback; @@ -34,13 +40,13 @@ import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; public class TbDeleteKeysNodeTest { @@ -58,7 +64,7 @@ public class TbDeleteKeysNodeTest { ctx = mock(TbContext.class); config = new TbDeleteKeysNodeConfiguration().defaultConfiguration(); config.setKeys(Set.of("TestKey_1", "TestKey_2", "TestKey_3", "(\\w*)Data(\\w*)")); - config.setFromMetadata(true); + config.setDeleteFrom(TbMsgSource.METADATA); nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); node = spy(new TbDeleteKeysNode()); node.init(ctx, nodeConfiguration); @@ -73,16 +79,15 @@ public class TbDeleteKeysNodeTest { void givenDefaultConfig_whenVerify_thenOK() { TbDeleteKeysNodeConfiguration defaultConfig = new TbDeleteKeysNodeConfiguration().defaultConfiguration(); assertThat(defaultConfig.getKeys()).isEqualTo(Collections.emptySet()); - assertThat(defaultConfig.isFromMetadata()).isEqualTo(false); + assertThat(defaultConfig.getDeleteFrom()).isEqualTo(TbMsgSource.DATA); } @Test - void givenMsgFromMetadata_whenOnMsg_thenVerifyOutput() throws Exception { - String data = "{}"; - node.onMsg(ctx, getTbMsg(deviceId, data)); + void givenDeleteFromMetadata_whenOnMsg_thenVerifyOutput() throws Exception { + node.onMsg(ctx, getTbMsg(deviceId, TbMsg.EMPTY_JSON_OBJECT)); ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); - verify(ctx, times(1)).tellSuccess(newMsgCaptor.capture()); + verify(ctx).tellSuccess(newMsgCaptor.capture()); verify(ctx, never()).tellFailure(any(), any()); TbMsg newMsg = newMsgCaptor.getValue(); @@ -94,8 +99,8 @@ public class TbDeleteKeysNodeTest { } @Test - void givenMsgFromMsg_whenOnMsg_thenVerifyOutput() throws Exception { - config.setFromMetadata(false); + void givenDeleteFromMsgConfig_whenOnMsg_thenVerifyOutput() throws Exception { + config.setDeleteFrom(TbMsgSource.DATA); nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); node.init(ctx, nodeConfiguration); @@ -103,7 +108,7 @@ public class TbDeleteKeysNodeTest { node.onMsg(ctx, getTbMsg(deviceId, data)); ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); - verify(ctx, times(1)).tellSuccess(newMsgCaptor.capture()); + verify(ctx).tellSuccess(newMsgCaptor.capture()); verify(ctx, never()).tellFailure(any(), any()); TbMsg newMsg = newMsgCaptor.getValue(); @@ -124,7 +129,7 @@ public class TbDeleteKeysNodeTest { node.onMsg(ctx, getTbMsg(deviceId, data)); ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); - verify(ctx, times(1)).tellSuccess(newMsgCaptor.capture()); + verify(ctx).tellSuccess(newMsgCaptor.capture()); verify(ctx, never()).tellFailure(any(), any()); TbMsg newMsg = newMsgCaptor.getValue(); @@ -133,6 +138,34 @@ public class TbDeleteKeysNodeTest { assertThat(newMsg.getData()).isEqualTo(data); } + private static Stream givenFromVersionAndConfig_whenUpgrade_thenVerifyUpgradeResultAndConfig() { + return Stream.of( + Arguments.of(0, "{\"fromMetadata\":false,\"keys\":[\"temperature\"]}", true, "{\"deleteFrom\":\"DATA\",\"keys\":[\"temperature\"]}"), + Arguments.of(0, "{\"fromMetadata\":true,\"keys\":[\"temperature\"]}", true, "{\"deleteFrom\":\"METADATA\",\"keys\":[\"temperature\"]}"), + Arguments.of(1, "{\"dataToFetch\":\"METADATA\",\"keys\":[\"temperature\"]}", true, "{\"deleteFrom\":\"METADATA\",\"keys\":[\"temperature\"]}"), + Arguments.of(1, "{\"dataToFetch\":\"DATA\",\"keys\":[\"temperature\"]}", true, "{\"deleteFrom\":\"DATA\",\"keys\":[\"temperature\"]}"), + Arguments.of(1, "{\"deleteFrom\":\"METADATA\",\"keys\":[\"temperature\"]}", false, "{\"deleteFrom\":\"METADATA\",\"keys\":[\"temperature\"]}"), + Arguments.of(1, "{\"deleteFrom\":\"DATA\",\"keys\":[\"temperature\"]}", false, "{\"deleteFrom\":\"DATA\",\"keys\":[\"temperature\"]}") + ); + } + + @ParameterizedTest + @MethodSource + void givenFromVersionAndConfig_whenUpgrade_thenVerifyUpgradeResultAndConfig(int givenVersion, String givenConfigStr, + boolean hasChanges, String expectedConfigStr) throws Exception { + // GIVEN + JsonNode givenConfig = JacksonUtil.toJsonNode(givenConfigStr); + JsonNode expectedConfig = JacksonUtil.toJsonNode(expectedConfigStr); + + // WHEN + var upgradeResult = node.upgrade(givenVersion, givenConfig); + + // THEN + assertThat(upgradeResult.getFirst()).isEqualTo(hasChanges); + ObjectNode upgradedConfig = (ObjectNode) upgradeResult.getSecond(); + assertThat(upgradedConfig).isEqualTo(expectedConfig); + } + private TbMsg getTbMsg(EntityId entityId, String data) { final Map mdMap = Map.of( "TestKey_1", "Test", @@ -140,7 +173,7 @@ public class TbDeleteKeysNodeTest { "voltageDataValue", "220", "city", "NY" ); - return TbMsg.newMsg("POST_ATTRIBUTES_REQUEST", entityId, new TbMsgMetaData(mdMap), data, callback); + return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, new TbMsgMetaData(mdMap), data, callback); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbJsonPathNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbJsonPathNodeTest.java index 0ca486a3e0..a86d902007 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbJsonPathNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbJsonPathNodeTest.java @@ -27,6 +27,7 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.queue.TbMsgCallback; @@ -170,6 +171,6 @@ public class TbJsonPathNodeTest { Map mdMap = Map.of("country", "US", "city", "NY" ); - return TbMsg.newMsg("POST_ATTRIBUTES_REQUEST", entityId, new TbMsgMetaData(mdMap), data, callback); + return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, new TbMsgMetaData(mdMap), data, callback); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java index 7bfa5c8bfc..be6aea6fa5 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbMsgDeduplicationNodeTest.java @@ -30,7 +30,6 @@ import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; -import org.thingsboard.rule.engine.api.TbRelationTypes; import org.thingsboard.rule.engine.deduplication.DeduplicationStrategy; import org.thingsboard.rule.engine.deduplication.TbMsgDeduplicationNode; import org.thingsboard.rule.engine.deduplication.TbMsgDeduplicationNodeConfiguration; @@ -39,9 +38,10 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; -import org.thingsboard.server.common.msg.session.SessionMsgType; import java.util.ArrayList; import java.util.List; @@ -70,8 +70,6 @@ import static org.mockito.Mockito.when; @Slf4j public class TbMsgDeduplicationNodeTest { - private static final String TB_MSG_DEDUPLICATION_TIMEOUT_MSG = "TbMsgDeduplicationNodeMsg"; - private TbContext ctx; private final ThingsBoardThreadFactory factory = ThingsBoardThreadFactory.forName("de-duplication-node-test"); @@ -97,12 +95,12 @@ public class TbMsgDeduplicationNodeTest { when(ctx.getTenantId()).thenReturn(tenantId); doAnswer((Answer) invocationOnMock -> { - String type = (String) (invocationOnMock.getArguments())[1]; + TbMsgType type = (TbMsgType) (invocationOnMock.getArguments())[1]; EntityId originator = (EntityId) (invocationOnMock.getArguments())[2]; TbMsgMetaData metaData = (TbMsgMetaData) (invocationOnMock.getArguments())[3]; String data = (String) (invocationOnMock.getArguments())[4]; return TbMsg.newMsg(type, originator, metaData.copy(), data); - }).when(ctx).newMsg(isNull(), eq(TB_MSG_DEDUPLICATION_TIMEOUT_MSG), nullable(EntityId.class), any(TbMsgMetaData.class), any(String.class)); + }).when(ctx).newMsg(isNull(), eq(TbMsgType.DEDUPLICATION_TIMEOUT_SELF_MSG), nullable(EntityId.class), any(TbMsgMetaData.class), any(String.class)); node = spy(new TbMsgDeduplicationNode()); config = new TbMsgDeduplicationNodeConfiguration().defaultConfiguration(); } @@ -173,7 +171,7 @@ public class TbMsgDeduplicationNodeTest { verify(ctx, times(msgCount)).ack(any()); verify(ctx, times(1)).tellFailure(eq(msgToReject), any()); verify(node, times(msgCount + wantedNumberOfTellSelfInvocation + 1)).onMsg(eq(ctx), any()); - verify(ctx, times(1)).enqueueForTellNext(newMsgCaptor.capture(), eq(TbRelationTypes.SUCCESS), successCaptor.capture(), failureCaptor.capture()); + verify(ctx, times(1)).enqueueForTellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.SUCCESS), successCaptor.capture(), failureCaptor.capture()); TbMsg firstMsg = inputMsgs.get(0); TbMsg actualMsg = newMsgCaptor.getValue(); @@ -221,7 +219,7 @@ public class TbMsgDeduplicationNodeTest { verify(ctx, times(msgCount)).ack(any()); verify(ctx, times(1)).tellFailure(eq(msgToReject), any()); verify(node, times(msgCount + wantedNumberOfTellSelfInvocation + 1)).onMsg(eq(ctx), any()); - verify(ctx, times(1)).enqueueForTellNext(newMsgCaptor.capture(), eq(TbRelationTypes.SUCCESS), successCaptor.capture(), failureCaptor.capture()); + verify(ctx, times(1)).enqueueForTellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.SUCCESS), successCaptor.capture(), failureCaptor.capture()); TbMsg actualMsg = newMsgCaptor.getValue(); // msg ids should be different because we create new msg before enqueueForTellNext @@ -242,7 +240,7 @@ public class TbMsgDeduplicationNodeTest { config.setInterval(deduplicationInterval); config.setStrategy(DeduplicationStrategy.ALL); - config.setOutMsgType(SessionMsgType.POST_ATTRIBUTES_REQUEST.name()); + config.setOutMsgType(TbMsgType.POST_ATTRIBUTES_REQUEST.name()); config.setQueueName(DataConstants.HP_QUEUE_NAME); nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); node.init(ctx, nodeConfiguration); @@ -263,7 +261,7 @@ public class TbMsgDeduplicationNodeTest { verify(ctx, times(msgCount)).ack(any()); verify(node, times(msgCount + wantedNumberOfTellSelfInvocation)).onMsg(eq(ctx), any()); - verify(ctx, times(1)).enqueueForTellNext(newMsgCaptor.capture(), eq(TbRelationTypes.SUCCESS), successCaptor.capture(), failureCaptor.capture()); + verify(ctx, times(1)).enqueueForTellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.SUCCESS), successCaptor.capture(), failureCaptor.capture()); Assertions.assertEquals(1, newMsgCaptor.getAllValues().size()); TbMsg outMessage = newMsgCaptor.getAllValues().get(0); @@ -282,7 +280,7 @@ public class TbMsgDeduplicationNodeTest { config.setInterval(deduplicationInterval); config.setStrategy(DeduplicationStrategy.ALL); - config.setOutMsgType(SessionMsgType.POST_ATTRIBUTES_REQUEST.name()); + config.setOutMsgType(TbMsgType.POST_ATTRIBUTES_REQUEST.name()); config.setQueueName(DataConstants.HP_QUEUE_NAME); nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); node.init(ctx, nodeConfiguration); @@ -309,7 +307,7 @@ public class TbMsgDeduplicationNodeTest { verify(ctx, times(msgCount)).ack(any()); verify(node, times(msgCount + wantedNumberOfTellSelfInvocation)).onMsg(eq(ctx), any()); - verify(ctx, times(2)).enqueueForTellNext(newMsgCaptor.capture(), eq(TbRelationTypes.SUCCESS), successCaptor.capture(), failureCaptor.capture()); + verify(ctx, times(2)).enqueueForTellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.SUCCESS), successCaptor.capture(), failureCaptor.capture()); List resultMsgs = newMsgCaptor.getAllValues(); Assertions.assertEquals(2, resultMsgs.size()); @@ -363,7 +361,7 @@ public class TbMsgDeduplicationNodeTest { verify(ctx, times(msgCount)).ack(any()); verify(node, times(msgCount + wantedNumberOfTellSelfInvocation)).onMsg(eq(ctx), any()); - verify(ctx, times(2)).enqueueForTellNext(newMsgCaptor.capture(), eq(TbRelationTypes.SUCCESS), successCaptor.capture(), failureCaptor.capture()); + verify(ctx, times(2)).enqueueForTellNext(newMsgCaptor.capture(), eq(TbNodeConnectionType.SUCCESS), successCaptor.capture(), failureCaptor.capture()); List resultMsgs = newMsgCaptor.getAllValues(); Assertions.assertEquals(2, resultMsgs.size()); @@ -414,7 +412,7 @@ public class TbMsgDeduplicationNodeTest { metaData.putValue("ts", String.valueOf(ts)); return TbMsg.newMsg( DataConstants.MAIN_QUEUE_NAME, - SessionMsgType.POST_TELEMETRY_REQUEST.name(), + TbMsgType.POST_TELEMETRY_REQUEST, deviceId, metaData, JacksonUtil.toString(dataNode)); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeTest.java index 9eade69e4b..23f6e33f26 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbRenameKeysNodeTest.java @@ -16,22 +16,29 @@ package org.thingsboard.rule.engine.transform; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.ArgumentCaptor; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.util.TbMsgSource; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.queue.TbMsgCallback; import java.util.Map; import java.util.UUID; +import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -69,8 +76,8 @@ public class TbRenameKeysNodeTest { @Test void givenDefaultConfig_whenVerify_thenOK() { TbRenameKeysNodeConfiguration defaultConfig = new TbRenameKeysNodeConfiguration().defaultConfiguration(); - assertThat(defaultConfig.getRenameKeysMapping()).isEqualTo(Map.of("temp", "temperature")); - assertThat(defaultConfig.isFromMetadata()).isEqualTo(false); + assertThat(defaultConfig.getRenameKeysMapping()).isEqualTo(Map.of("temperatureCelsius", "temperature")); + assertThat(defaultConfig.getRenameIn()).isEqualTo(TbMsgSource.DATA); } @Test @@ -94,7 +101,7 @@ public class TbRenameKeysNodeTest { void givenMetadata_whenOnMsg_thenVerifyOutput() throws Exception { config = new TbRenameKeysNodeConfiguration().defaultConfiguration(); config.setRenameKeysMapping(Map.of("TestKey_1", "Attribute_1", "TestKey_2", "Attribute_2")); - config.setFromMetadata(true); + config.setRenameIn(TbMsgSource.METADATA); nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); node.init(ctx, nodeConfiguration); @@ -134,8 +141,7 @@ public class TbRenameKeysNodeTest { @Test void givenMsgDataNotJSONObject_whenOnMsg_thenVerifyOutput() throws Exception { - String data = "[]"; - TbMsg msg = getTbMsg(deviceId, data); + TbMsg msg = getTbMsg(deviceId, TbMsg.EMPTY_JSON_ARRAY); node.onMsg(ctx, msg); ArgumentCaptor newMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); @@ -148,12 +154,40 @@ public class TbRenameKeysNodeTest { assertThat(newMsg).isSameAs(msg); } + private static Stream givenFromVersionAndConfig_whenUpgrade_thenVerifyUpgradeResultAndConfig() { + return Stream.of( + Arguments.of(0, "{\"fromMetadata\":false,\"renameKeysMapping\":{\"temp\":\"temperature\"}}", true, "{\"renameIn\":\"DATA\",\"renameKeysMapping\":{\"temp\":\"temperature\"}}"), + Arguments.of(0, "{\"fromMetadata\":true,\"renameKeysMapping\":{\"temp\":\"temperature\"}}", true, "{\"renameIn\":\"METADATA\",\"renameKeysMapping\":{\"temp\":\"temperature\"}}"), + Arguments.of(1, "{\"fromMetadata\":\"METADATA\",\"renameKeysMapping\":{\"temp\":\"temperature\"}}", true, "{\"renameIn\":\"METADATA\",\"renameKeysMapping\":{\"temp\":\"temperature\"}}"), + Arguments.of(1, "{\"fromMetadata\":\"DATA\",\"renameKeysMapping\":{\"temp\":\"temperature\"}}", true, "{\"renameIn\":\"DATA\",\"renameKeysMapping\":{\"temp\":\"temperature\"}}"), + Arguments.of(1, "{\"renameIn\":\"METADATA\",\"renameKeysMapping\":{\"temp\":\"temperature\"}}", false, "{\"renameIn\":\"METADATA\",\"renameKeysMapping\":{\"temp\":\"temperature\"}}"), + Arguments.of(1, "{\"renameIn\":\"DATA\",\"renameKeysMapping\":{\"temp\":\"temperature\"}}", false, "{\"renameIn\":\"DATA\",\"renameKeysMapping\":{\"temp\":\"temperature\"}}") + ); + } + + @ParameterizedTest + @MethodSource + void givenFromVersionAndConfig_whenUpgrade_thenVerifyUpgradeResultAndConfig(int givenVersion, String givenConfigStr, + boolean hasChanges, String expectedConfigStr) throws Exception { + // GIVEN + JsonNode givenConfig = JacksonUtil.toJsonNode(givenConfigStr); + JsonNode expectedConfig = JacksonUtil.toJsonNode(expectedConfigStr); + + // WHEN + var upgradeResult = node.upgrade(givenVersion, givenConfig); + + // THEN + assertThat(upgradeResult.getFirst()).isEqualTo(hasChanges); + ObjectNode upgradedConfig = (ObjectNode) upgradeResult.getSecond(); + assertThat(upgradedConfig).isEqualTo(expectedConfig); + } + private TbMsg getTbMsg(EntityId entityId, String data) { final Map mdMap = Map.of( "TestKey_1", "Test", "country", "US", "city", "NY" ); - return TbMsg.newMsg("POST_ATTRIBUTES_REQUEST", entityId, new TbMsgMetaData(mdMap), data, callback); + return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, new TbMsgMetaData(mdMap), data, callback); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNodeTest.java index 67ebf6f6fc..e7e2ae16b2 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNodeTest.java @@ -27,6 +27,7 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.queue.TbMsgCallback; @@ -82,8 +83,7 @@ public class TbSplitArrayMsgNodeTest { @Test void givenZeroMsg_whenOnMsg_thenVerifyOutput() throws Exception { - String data = "[]"; - VerifyOutputMsg(data); + VerifyOutputMsg(TbMsg.EMPTY_JSON_ARRAY); } @Test @@ -132,6 +132,6 @@ public class TbSplitArrayMsgNodeTest { "country", "US", "city", "NY" ); - return TbMsg.newMsg("POST_ATTRIBUTES_REQUEST", entityId, new TbMsgMetaData(mdMap), data, callback); + return TbMsg.newMsg(TbMsgType.POST_ATTRIBUTES_REQUEST, entityId, new TbMsgMetaData(mdMap), data, callback); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java index 71348a1a23..56c94fd4ff 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbTransformMsgNodeTest.java @@ -29,6 +29,7 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.script.ScriptLanguage; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; @@ -60,8 +61,8 @@ public class TbTransformMsgNodeTest { RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); - TbMsg msg = TbMsg.newMsg( "USER", null, metaData, TbMsgDataType.JSON,rawJson, ruleChainId, ruleNodeId); - TbMsg transformedMsg = TbMsg.newMsg( "USER", null, metaData, TbMsgDataType.JSON, "{new}", ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, null, metaData, TbMsgDataType.JSON,rawJson, ruleChainId, ruleNodeId); + TbMsg transformedMsg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, null, metaData, TbMsgDataType.JSON, "{new}", ruleChainId, ruleNodeId); when(scriptEngine.executeUpdateAsync(msg)).thenReturn(Futures.immediateFuture(Collections.singletonList(transformedMsg))); node.onMsg(ctx, msg); @@ -80,7 +81,7 @@ public class TbTransformMsgNodeTest { RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); - TbMsg msg = TbMsg.newMsg( "USER", null, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, null, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); when(scriptEngine.executeUpdateAsync(msg)).thenReturn(Futures.immediateFailedFuture(new IllegalStateException("error"))); node.onMsg(ctx, msg); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/EntitiesCustomerIdAsyncLoaderTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/EntitiesCustomerIdAsyncLoaderTest.java new file mode 100644 index 0000000000..c675b97d08 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/EntitiesCustomerIdAsyncLoaderTest.java @@ -0,0 +1,154 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.util; + +import com.google.common.util.concurrent.Futures; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.common.util.ListeningExecutor; +import org.thingsboard.rule.engine.TestDbCallbackExecutor; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.user.UserService; + +import java.util.EnumSet; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class EntitiesCustomerIdAsyncLoaderTest { + + private static final EnumSet SUPPORTED_ENTITY_TYPES = EnumSet.of( + EntityType.CUSTOMER, + EntityType.USER, + EntityType.ASSET, + EntityType.DEVICE + ); + private static final ListeningExecutor DB_EXECUTOR = new TestDbCallbackExecutor(); + @Mock + private TbContext ctxMock; + @Mock + private UserService userServiceMock; + @Mock + private AssetService assetServiceMock; + @Mock + private DeviceService deviceServiceMock; + + @Test + public void givenCustomerEntityType_whenFindEntityIdAsync_thenOK() throws ExecutionException, InterruptedException { + // GIVEN + var customer = new Customer(new CustomerId(UUID.randomUUID())); + + // WHEN + var actualCustomerId = EntitiesCustomerIdAsyncLoader.findEntityIdAsync(ctxMock, customer.getId()).get(); + + // THEN + assertEquals(customer.getId(), actualCustomerId); + } + + @Test + public void givenUserEntityType_whenFindEntityIdAsync_thenOK() throws ExecutionException, InterruptedException { + // GIVEN + var user = new User(new UserId(UUID.randomUUID())); + var expectedCustomerId = new CustomerId(UUID.randomUUID()); + user.setCustomerId(expectedCustomerId); + + when(ctxMock.getUserService()).thenReturn(userServiceMock); + doReturn(Futures.immediateFuture(user)).when(userServiceMock).findUserByIdAsync(any(), any()); + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + var actualCustomerId = EntitiesCustomerIdAsyncLoader.findEntityIdAsync(ctxMock, user.getId()).get(); + + // THEN + assertEquals(expectedCustomerId, actualCustomerId); + } + + @Test + public void givenAssetEntityType_whenFindEntityIdAsync_thenOK() throws ExecutionException, InterruptedException { + // GIVEN + var asset = new Asset(new AssetId(UUID.randomUUID())); + var expectedCustomerId = new CustomerId(UUID.randomUUID()); + asset.setCustomerId(expectedCustomerId); + + when(ctxMock.getAssetService()).thenReturn(assetServiceMock); + doReturn(Futures.immediateFuture(asset)).when(assetServiceMock).findAssetByIdAsync(any(), any()); + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + var actualCustomerId = EntitiesCustomerIdAsyncLoader.findEntityIdAsync(ctxMock, asset.getId()).get(); + + // THEN + assertEquals(expectedCustomerId, actualCustomerId); + } + + @Test + public void givenDeviceEntityType_whenFindEntityIdAsync_thenOK() throws ExecutionException, InterruptedException { + // GIVEN + var device = new Device(new DeviceId(UUID.randomUUID())); + var expectedCustomerId = new CustomerId(UUID.randomUUID()); + device.setCustomerId(expectedCustomerId); + + when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); + doReturn(device).when(deviceServiceMock).findDeviceById(any(), any()); + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + var actualCustomerId = EntitiesCustomerIdAsyncLoader.findEntityIdAsync(ctxMock, device.getId()).get(); + + // THEN + assertEquals(expectedCustomerId, actualCustomerId); + } + + @Test + public void givenUnsupportedEntityTypes_whenFindEntityIdAsync_thenException() { + for (var entityType : EntityType.values()) { + if (!SUPPORTED_ENTITY_TYPES.contains(entityType)) { + var entityId = EntityIdFactory.getByTypeAndUuid(entityType, UUID.randomUUID()); + + var expectedExceptionMsg = "org.thingsboard.rule.engine.api.TbNodeException: Unexpected originator EntityType: " + entityType; + + var exception = assertThrows(ExecutionException.class, + () -> EntitiesCustomerIdAsyncLoader.findEntityIdAsync(ctxMock, entityId).get()); + + assertInstanceOf(TbNodeException.class, exception.getCause()); + assertEquals(expectedExceptionMsg, exception.getMessage()); + } + } + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/EntitiesFieldsAsyncLoaderTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/EntitiesFieldsAsyncLoaderTest.java new file mode 100644 index 0000000000..d60f2256e1 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/EntitiesFieldsAsyncLoaderTest.java @@ -0,0 +1,260 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.util; + +import com.google.common.util.concurrent.Futures; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.common.util.ListeningExecutor; +import org.thingsboard.rule.engine.TestDbCallbackExecutor; +import org.thingsboard.rule.engine.api.RuleEngineAlarmService; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntityFieldsData; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.EntityViewId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UUIDBased; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.customer.CustomerService; +import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.entityview.EntityViewService; +import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.tenant.TenantService; +import org.thingsboard.server.dao.user.UserService; + +import java.util.EnumSet; +import java.util.NoSuchElementException; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class EntitiesFieldsAsyncLoaderTest { + + private static final ListeningExecutor DB_EXECUTOR = new TestDbCallbackExecutor(); + private static EnumSet SUPPORTED_ENTITY_TYPES; + private static UUID RANDOM_UUID; + private static TenantId TENANT_ID; + @Mock + private TbContext ctxMock; + @Mock + private TenantService tenantServiceMock; + @Mock + private CustomerService customerServiceMock; + @Mock + private UserService userServiceMock; + @Mock + private AssetService assetServiceMock; + @Mock + private DeviceService deviceServiceMock; + @Mock + private RuleEngineAlarmService ruleEngineAlarmServiceMock; + @Mock + private RuleChainService ruleChainServiceMock; + @Mock + private EntityViewService entityViewServiceMock; + + @BeforeAll + public static void setup() { + RANDOM_UUID = UUID.randomUUID(); + TENANT_ID = new TenantId(UUID.randomUUID()); + SUPPORTED_ENTITY_TYPES = EnumSet.of( + EntityType.TENANT, + EntityType.CUSTOMER, + EntityType.USER, + EntityType.ASSET, + EntityType.DEVICE, + EntityType.ALARM, + EntityType.RULE_CHAIN, + EntityType.ENTITY_VIEW + ); + } + + @Test + public void givenSupportedEntityTypes_whenFindAsync_thenOK() throws ExecutionException, InterruptedException { + for (var entityType : SUPPORTED_ENTITY_TYPES) { + var entityId = EntityIdFactory.getByTypeAndUuid(entityType, RANDOM_UUID); + + initMocks(entityType, false); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + + var actualEntityFieldsData = EntitiesFieldsAsyncLoader.findAsync(ctxMock, entityId).get(); + var expectedEntityFieldsData = new EntityFieldsData(getEntityFromEntityId(entityId)); + + Assertions.assertEquals(expectedEntityFieldsData, actualEntityFieldsData); + } + } + + @Test + public void givenUnsupportedEntityTypes_whenFindAsync_thenException() { + for (var entityType : EntityType.values()) { + if (!SUPPORTED_ENTITY_TYPES.contains(entityType)) { + var entityId = EntityIdFactory.getByTypeAndUuid(entityType, RANDOM_UUID); + + var expectedExceptionMsg = "org.thingsboard.rule.engine.api.TbNodeException: Unexpected originator EntityType: " + entityType; + + var exception = assertThrows(ExecutionException.class, + () -> EntitiesFieldsAsyncLoader.findAsync(ctxMock, entityId).get()); + + assertInstanceOf(TbNodeException.class, exception.getCause()); + assertThat(exception.getMessage()).isEqualTo(expectedExceptionMsg); + } + } + } + + @Test + public void givenSupportedTypeButEntityDoesNotExist_whenFindAsync_thenException() { + for (var entityType : SUPPORTED_ENTITY_TYPES) { + var entityId = EntityIdFactory.getByTypeAndUuid(entityType, RANDOM_UUID); + + initMocks(entityType, true); + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + + var expectedExceptionMsg = "java.util.NoSuchElementException: Entity not found!"; + + var exception = assertThrows(ExecutionException.class, + () -> EntitiesFieldsAsyncLoader.findAsync(ctxMock, entityId).get()); + + assertInstanceOf(NoSuchElementException.class, exception.getCause()); + assertThat(exception.getMessage()).isEqualTo(expectedExceptionMsg); + } + } + + private void initMocks(EntityType entityType, boolean entityDoesNotExist) { + switch (entityType) { + case TENANT: + var tenant = Futures.immediateFuture(entityDoesNotExist ? null : new Tenant(new TenantId(RANDOM_UUID))); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + when(ctxMock.getTenantService()).thenReturn(tenantServiceMock); + doReturn(tenant).when(tenantServiceMock).findTenantByIdAsync(eq(TENANT_ID), any()); + + break; + case CUSTOMER: + var customer = Futures.immediateFuture(entityDoesNotExist ? null : new Customer(new CustomerId(RANDOM_UUID))); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + when(ctxMock.getCustomerService()).thenReturn(customerServiceMock); + doReturn(customer).when(customerServiceMock).findCustomerByIdAsync(eq(TENANT_ID), any()); + + break; + case USER: + var user = Futures.immediateFuture(entityDoesNotExist ? null : new User(new UserId(RANDOM_UUID))); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + when(ctxMock.getUserService()).thenReturn(userServiceMock); + doReturn(user).when(userServiceMock).findUserByIdAsync(eq(TENANT_ID), any()); + + break; + case ASSET: + var asset = Futures.immediateFuture(entityDoesNotExist ? null : new Asset(new AssetId(RANDOM_UUID))); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + when(ctxMock.getAssetService()).thenReturn(assetServiceMock); + doReturn(asset).when(assetServiceMock).findAssetByIdAsync(eq(TENANT_ID), any()); + + break; + case DEVICE: + var device = entityDoesNotExist ? null : new Device(new DeviceId(RANDOM_UUID)); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); + doReturn(device).when(deviceServiceMock).findDeviceById(eq(TENANT_ID), any()); + + break; + case ALARM: + var alarm = Futures.immediateFuture(entityDoesNotExist ? null : new Alarm(new AlarmId(RANDOM_UUID))); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + when(ctxMock.getAlarmService()).thenReturn(ruleEngineAlarmServiceMock); + doReturn(alarm).when(ruleEngineAlarmServiceMock).findAlarmByIdAsync(eq(TENANT_ID), any()); + + break; + case RULE_CHAIN: + var ruleChain = Futures.immediateFuture(entityDoesNotExist ? null : new RuleChain(new RuleChainId(RANDOM_UUID))); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + when(ctxMock.getRuleChainService()).thenReturn(ruleChainServiceMock); + doReturn(ruleChain).when(ruleChainServiceMock).findRuleChainByIdAsync(eq(TENANT_ID), any()); + + break; + case ENTITY_VIEW: + var entityView = Futures.immediateFuture(entityDoesNotExist ? null : new EntityView(new EntityViewId(RANDOM_UUID))); + + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + when(ctxMock.getEntityViewService()).thenReturn(entityViewServiceMock); + doReturn(entityView).when(entityViewServiceMock).findEntityViewByIdAsync(eq(TENANT_ID), any()); + + break; + default: + throw new RuntimeException("Unexpected EntityType: " + entityType); + } + } + + private BaseData getEntityFromEntityId(EntityId entityId) { + switch (entityId.getEntityType()) { + case TENANT: + return new Tenant((TenantId) entityId); + case CUSTOMER: + return new Customer((CustomerId) entityId); + case USER: + return new User((UserId) entityId); + case ASSET: + return new Asset((AssetId) entityId); + case DEVICE: + return new Device((DeviceId) entityId); + case ALARM: + return new Alarm((AlarmId) entityId); + case RULE_CHAIN: + return new RuleChain((RuleChainId) entityId); + case ENTITY_VIEW: + return new EntityView((EntityViewId) entityId); + default: + throw new RuntimeException("Unexpected EntityType: " + entityId.getEntityType()); + } + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/EntitiesRelatedDeviceIdAsyncLoaderTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/EntitiesRelatedDeviceIdAsyncLoaderTest.java new file mode 100644 index 0000000000..c54072fb53 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/EntitiesRelatedDeviceIdAsyncLoaderTest.java @@ -0,0 +1,137 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.util; + +import com.google.common.util.concurrent.Futures; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.common.util.ListeningExecutor; +import org.thingsboard.rule.engine.TestDbCallbackExecutor; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.data.DeviceRelationsQuery; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.device.DeviceSearchQuery; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationsSearchParameters; +import org.thingsboard.server.dao.device.DeviceService; + +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class EntitiesRelatedDeviceIdAsyncLoaderTest { + + private static final EntityId DUMMY_ORIGINATOR = new DeviceId(UUID.randomUUID()); + private static final TenantId TENANT_ID = new TenantId(UUID.randomUUID()); + private static final ListeningExecutor DB_EXECUTOR = new TestDbCallbackExecutor(); + @Mock + private TbContext ctxMock; + @Mock + private DeviceService deviceServiceMock; + + @Test + public void givenDeviceRelationsQuery_whenFindDeviceAsync_ShouldBuildCorrectDeviceSearchQuery() { + // GIVEN + var deviceRelationsQuery = new DeviceRelationsQuery(); + deviceRelationsQuery.setDeviceTypes(List.of("Device type 1", "Device type 2", "default")); + deviceRelationsQuery.setDirection(EntitySearchDirection.FROM); + deviceRelationsQuery.setMaxLevel(2); + deviceRelationsQuery.setRelationType(EntityRelation.CONTAINS_TYPE); + + var expectedDeviceSearchQuery = new DeviceSearchQuery(); + var parameters = new RelationsSearchParameters( + DUMMY_ORIGINATOR, + deviceRelationsQuery.getDirection(), + deviceRelationsQuery.getMaxLevel(), + deviceRelationsQuery.isFetchLastLevelOnly() + ); + expectedDeviceSearchQuery.setParameters(parameters); + expectedDeviceSearchQuery.setRelationType(deviceRelationsQuery.getRelationType()); + expectedDeviceSearchQuery.setDeviceTypes(deviceRelationsQuery.getDeviceTypes()); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); + when(deviceServiceMock.findDevicesByQuery(eq(TENANT_ID), eq(expectedDeviceSearchQuery))) + .thenReturn(Futures.immediateFuture(null)); + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + EntitiesRelatedDeviceIdAsyncLoader.findDeviceAsync(ctxMock, DUMMY_ORIGINATOR, deviceRelationsQuery); + + // THEN + verify(deviceServiceMock, times(1)).findDevicesByQuery(eq(TENANT_ID), eq(expectedDeviceSearchQuery)); + } + + @Test + public void givenSeveralDevicesFound_whenFindDeviceAsync_ShouldKeepOneAndDiscardOthers() throws Exception { + // GIVEN + var deviceRelationsQuery = new DeviceRelationsQuery(); + deviceRelationsQuery.setDeviceTypes(List.of("Device type 1", "Device type 2", "default")); + deviceRelationsQuery.setDirection(EntitySearchDirection.FROM); + deviceRelationsQuery.setMaxLevel(2); + deviceRelationsQuery.setRelationType(EntityRelation.CONTAINS_TYPE); + + var expectedDeviceSearchQuery = new DeviceSearchQuery(); + var parameters = new RelationsSearchParameters( + DUMMY_ORIGINATOR, + deviceRelationsQuery.getDirection(), + deviceRelationsQuery.getMaxLevel(), + deviceRelationsQuery.isFetchLastLevelOnly() + ); + expectedDeviceSearchQuery.setParameters(parameters); + expectedDeviceSearchQuery.setRelationType(deviceRelationsQuery.getRelationType()); + expectedDeviceSearchQuery.setDeviceTypes(deviceRelationsQuery.getDeviceTypes()); + + var device1 = new Device(new DeviceId(UUID.randomUUID())); + device1.setName("Device 1"); + var device2 = new Device(new DeviceId(UUID.randomUUID())); + device1.setName("Device 2"); + var device3 = new Device(new DeviceId(UUID.randomUUID())); + device1.setName("Device 3"); + + var devicesList = List.of(device1, device2, device3); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + when(ctxMock.getDeviceService()).thenReturn(deviceServiceMock); + when(deviceServiceMock.findDevicesByQuery(eq(TENANT_ID), eq(expectedDeviceSearchQuery))) + .thenReturn(Futures.immediateFuture(devicesList)); + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + var entityIdFuture = EntitiesRelatedDeviceIdAsyncLoader.findDeviceAsync(ctxMock, DUMMY_ORIGINATOR, deviceRelationsQuery); + + // THEN + assertNotNull(entityIdFuture); + + var actualEntityId = entityIdFuture.get(); + assertNotNull(actualEntityId); + assertEquals(device1.getId(), actualEntityId); + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/EntitiesRelatedEntityIdAsyncLoaderTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/EntitiesRelatedEntityIdAsyncLoaderTest.java new file mode 100644 index 0000000000..e51a3704d1 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/EntitiesRelatedEntityIdAsyncLoaderTest.java @@ -0,0 +1,221 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.rule.engine.util; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatchers; +import org.thingsboard.common.util.ListeningExecutor; +import org.thingsboard.rule.engine.TestDbCallbackExecutor; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.data.RelationsQuery; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntityRelationsQuery; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.common.data.relation.RelationsSearchParameters; +import org.thingsboard.server.dao.relation.RelationService; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.thingsboard.common.util.DonAsynchron.withCallback; + +public class EntitiesRelatedEntityIdAsyncLoaderTest { + + private static final EntityId ASSET_ORIGINATOR_ID = new AssetId(UUID.randomUUID()); + private static final TenantId TENANT_ID = new TenantId(UUID.randomUUID()); + private static final ListeningExecutor DB_EXECUTOR = new TestDbCallbackExecutor(); + + private TbContext ctxMock; + private RelationService relationServiceMock; + + private RelationsQuery relationsQuery; + + @BeforeEach + void setUp() { + ctxMock = mock(TbContext.class); + relationServiceMock = mock(RelationService.class); + when(ctxMock.getRelationService()).thenReturn(relationServiceMock); + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + relationsQuery = new RelationsQuery(); + relationsQuery.setDirection(EntitySearchDirection.FROM); + relationsQuery.setMaxLevel(1); + RelationEntityTypeFilter entityTypeFilter = new RelationEntityTypeFilter( + EntityRelation.CONTAINS_TYPE, Collections.emptyList() + ); + relationsQuery.setFilters(Collections.singletonList(entityTypeFilter)); + } + + @Test + public void givenRelationsQuery_whenFindEntityAsync_ShouldBuildCorrectEntityRelationsQuery() { + // GIVEN + var expectedEntityRelationsQuery = new EntityRelationsQuery(); + var parameters = new RelationsSearchParameters( + ASSET_ORIGINATOR_ID, + relationsQuery.getDirection(), + relationsQuery.getMaxLevel(), + relationsQuery.isFetchLastLevelOnly() + ); + expectedEntityRelationsQuery.setParameters(parameters); + expectedEntityRelationsQuery.setFilters(relationsQuery.getFilters()); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + when(ctxMock.getRelationService()).thenReturn(relationServiceMock); + when(relationServiceMock.findByQuery(eq(TENANT_ID), eq(expectedEntityRelationsQuery))) + .thenReturn(Futures.immediateFuture(null)); + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + EntitiesRelatedEntityIdAsyncLoader.findEntityAsync(ctxMock, ASSET_ORIGINATOR_ID, relationsQuery); + + // THEN + verify(relationServiceMock, times(1)).findByQuery(eq(TENANT_ID), eq(expectedEntityRelationsQuery)); + } + + + @Test + public void givenSeveralEntitiesFound_whenFindEntityAsync_ShouldKeepOneAndDiscardOthers() throws Exception { + // GIVEN + var expectedEntityRelationsQuery = new EntityRelationsQuery(); + var parameters = new RelationsSearchParameters( + ASSET_ORIGINATOR_ID, + relationsQuery.getDirection(), + relationsQuery.getMaxLevel(), + relationsQuery.isFetchLastLevelOnly() + ); + expectedEntityRelationsQuery.setParameters(parameters); + expectedEntityRelationsQuery.setFilters(relationsQuery.getFilters()); + + var device1 = new Device(new DeviceId(UUID.randomUUID())); + device1.setName("Device 1"); + var device2 = new Device(new DeviceId(UUID.randomUUID())); + device1.setName("Device 2"); + var device3 = new Device(new DeviceId(UUID.randomUUID())); + device3.setName("Device 3"); + + var entityRelationDevice1 = new EntityRelation(); + entityRelationDevice1.setFrom(ASSET_ORIGINATOR_ID); + entityRelationDevice1.setTo(device1.getId()); + entityRelationDevice1.setType(EntityRelation.CONTAINS_TYPE); + + var entityRelationDevice2 = new EntityRelation(); + entityRelationDevice2.setFrom(ASSET_ORIGINATOR_ID); + entityRelationDevice2.setTo(device2.getId()); + entityRelationDevice2.setType(EntityRelation.CONTAINS_TYPE); + + var entityRelationDevice3 = new EntityRelation(); + entityRelationDevice3.setFrom(ASSET_ORIGINATOR_ID); + entityRelationDevice3.setTo(device3.getId()); + entityRelationDevice3.setType(EntityRelation.CONTAINS_TYPE); + + var expectedEntityRelationsList = List.of(entityRelationDevice1, entityRelationDevice2, entityRelationDevice3); + + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + when(ctxMock.getRelationService()).thenReturn(relationServiceMock); + when(relationServiceMock.findByQuery(eq(TENANT_ID), eq(expectedEntityRelationsQuery))) + .thenReturn(Futures.immediateFuture(expectedEntityRelationsList)); + when(ctxMock.getDbCallbackExecutor()).thenReturn(DB_EXECUTOR); + + // WHEN + var deviceIdFuture = EntitiesRelatedEntityIdAsyncLoader.findEntityAsync(ctxMock, ASSET_ORIGINATOR_ID, relationsQuery); + + // THEN + assertNotNull(deviceIdFuture); + + var actualDeviceId = deviceIdFuture.get(); + assertNotNull(actualDeviceId); + assertEquals(device1.getId(), actualDeviceId); + } + + + @Test + public void givenRelationQuery_whenFindEntityAsync_thenOK() { + // GIVEN + List entityRelations = new ArrayList<>(); + entityRelations.add(createEntityRelation(TENANT_ID, ASSET_ORIGINATOR_ID)); + when(relationServiceMock.findByQuery(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(Futures.immediateFuture(entityRelations)); + + // WHEN + ListenableFuture entityIdFuture = EntitiesRelatedEntityIdAsyncLoader.findEntityAsync(ctxMock, TENANT_ID, relationsQuery); + + // THEN + verifyEntityIdFuture(entityIdFuture, ASSET_ORIGINATOR_ID); + } + + @Test + public void givenRelationQuery_whenFindEntityAsync_thenReturnNull() { + // GIVEN + List entityRelations = new ArrayList<>(); + when(relationServiceMock.findByQuery(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(Futures.immediateFuture(entityRelations)); + + // WHEN + ListenableFuture entityIdFuture = EntitiesRelatedEntityIdAsyncLoader.findEntityAsync(ctxMock, TENANT_ID, relationsQuery); + + // THEN + verifyEntityIdFuture(entityIdFuture, null); + } + + @Test + public void givenRelationQuery_whenFindEntityAsync_thenFailure() { + // GIVEN + relationsQuery.setDirection(null); + List entityRelations = new ArrayList<>(); + entityRelations.add(createEntityRelation(TENANT_ID, ASSET_ORIGINATOR_ID)); + + when(relationServiceMock.findByQuery(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(Futures.immediateFuture(entityRelations)); + + // WHEN + ListenableFuture entityIdFuture = EntitiesRelatedEntityIdAsyncLoader.findEntityAsync(ctxMock, TENANT_ID, relationsQuery); + + // THEN + verifyEntityIdFuture(entityIdFuture, ASSET_ORIGINATOR_ID); + } + + private void verifyEntityIdFuture(ListenableFuture entityIdFuture, EntityId assetId) { + withCallback(entityIdFuture, + entityId -> assertThat(entityId).isEqualTo(assetId), + throwable -> assertThat(throwable).isInstanceOf(IllegalStateException.class), ctxMock.getDbCallbackExecutor()); + } + + private static EntityRelation createEntityRelation(EntityId from, EntityId to) { + EntityRelation relation = new EntityRelation(); + relation.setFrom(from); + relation.setTo(to); + relation.setType(EntityRelation.CONTAINS_TYPE); + relation.setTypeGroup(RelationTypeGroup.COMMON); + return relation; + } +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/TenantIdLoaderTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/TenantIdLoaderTest.java index 2cd19eef9a..0871a82550 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/TenantIdLoaderTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/util/TenantIdLoaderTest.java @@ -61,7 +61,6 @@ import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetsBundle; -import org.thingsboard.server.dao.alarm.AlarmCommentService; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; @@ -70,7 +69,6 @@ import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.notification.NotificationRequestService; import org.thingsboard.server.dao.notification.NotificationRuleService; -import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; import org.thingsboard.server.dao.ota.OtaPackageService; @@ -104,8 +102,6 @@ public class TenantIdLoaderTest { @Mock private RuleEngineAlarmService alarmService; @Mock - private AlarmCommentService alarmCommentService; - @Mock private RuleChainService ruleChainService; @Mock private EntityViewService entityViewService; @@ -138,8 +134,6 @@ public class TenantIdLoaderTest { @Mock private NotificationRequestService notificationRequestService; @Mock - private NotificationService notificationService; - @Mock private NotificationRuleService notificationRuleService; private TenantId tenantId; @@ -359,9 +353,8 @@ public class TenantIdLoaderTest { doReturn(notificationRule).when(notificationRuleService).findNotificationRuleById(eq(tenantId), any()); break; default: - throw new RuntimeException("Unexpected original EntityType " + entityType); + throw new RuntimeException("Unexpected originator EntityType " + entityType); } - } private EntityId getEntityId(EntityType entityType) { diff --git a/rule-engine/rule-engine-components/src/test/resources/pem/ec_cert.pem b/rule-engine/rule-engine-components/src/test/resources/pem/ec_cert.pem new file mode 100644 index 0000000000..f22f61d3a1 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/resources/pem/ec_cert.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIICCDCCAa2gAwIBAgIUGx/SZqIWza/i/gaKFUVIyTEu2oMwCgYIKoZIzj0EAwIw +WTELMAkGA1UEBhMCVUExDTALBgNVBAgMBEtZSVYxDTALBgNVBAcMBEtZSVYxCzAJ +BgNVBAoMAlRCMQswCQYDVQQLDAJUQjESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIz +MTAxNjEyMjMyMVoXDTI0MTAxNTEyMjMyMVowWTELMAkGA1UEBhMCVUExDTALBgNV +BAgMBEtZSVYxDTALBgNVBAcMBEtZSVYxCzAJBgNVBAoMAlRCMQswCQYDVQQLDAJU +QjESMBAGA1UEAwwJbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +z4MgawieJfVc5zUOPiw5WFxfHGJf7dOMsHvudDxdOs27PXPbJfi09BVJ3+JjNxA2 +wQz9KUk877oWRYrN/e+MbKNTMFEwHQYDVR0OBBYEFDTV8VD3m+8IBQOBJ+V/bcbl +4preMB8GA1UdIwQYMBaAFDTV8VD3m+8IBQOBJ+V/bcbl4preMA8GA1UdEwEB/wQF +MAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhAOgIkl8j8m51W7pWlNUAuUnHnOVhVjGr +h8Rc6cbwTapKAiEA2CLrduTweXEF5fBRtWyOsG8c9af6+MWHKmwHL1IDw9Q= +-----END CERTIFICATE----- diff --git a/rule-engine/rule-engine-components/src/test/resources/pem/ec_key.pem b/rule-engine/rule-engine-components/src/test/resources/pem/ec_key.pem new file mode 100644 index 0000000000..74e0716259 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/resources/pem/ec_key.pem @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIIEd0mMh0EEy3fMbOpbUY6kW0oAYcaYoTvoVpZxDr5qZoAoGCCqGSM49 +AwEHoUQDQgAEz4MgawieJfVc5zUOPiw5WFxfHGJf7dOMsHvudDxdOs27PXPbJfi0 +9BVJ3+JjNxA2wQz9KUk877oWRYrN/e+MbA== +-----END EC PRIVATE KEY----- \ No newline at end of file diff --git a/rule-engine/rule-engine-components/src/test/resources/pem/rsa_cert.pem b/rule-engine/rule-engine-components/src/test/resources/pem/rsa_cert.pem new file mode 100644 index 0000000000..f89fce7444 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/resources/pem/rsa_cert.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFkzCCA3ugAwIBAgIUUQa3cWUVoF58dzg8ycb/y7SdCj8wDQYJKoZIhvcNAQEL +BQAwWTELMAkGA1UEBhMCVUExDTALBgNVBAgMBEtZSVYxDTALBgNVBAcMBEtZSVYx +CzAJBgNVBAoMAlRCMQswCQYDVQQLDAJUQjESMBAGA1UEAwwJbG9jYWxob3N0MB4X +DTIzMTAxMzEyMzcwMVoXDTI0MTAxMjEyMzcwMVowWTELMAkGA1UEBhMCVUExDTAL +BgNVBAgMBEtZSVYxDTALBgNVBAcMBEtZSVYxCzAJBgNVBAoMAlRCMQswCQYDVQQL +DAJUQjESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAsHn27cH+pYFI0eJYer8ww29g/xlKgr9aarYlkILeXnBhPPHBCXG+ +FegeMpHa8FUPANIqYJiwM13altO6hMLPa0J7+nQhwF5NCbxzAdi/kU8ofhIwJH+K +gOsD3BKdR7Ua7KMDQFnGTFRR9ZxsuYZ/0AHuzPHwxSLUvvMbiWbu5P2FYMrEyyLo +uVVihZPkeBhcnI6SJRyCdMdMy282nWQ+47gAUI3cFa7dXxUcXvRbbToMNPTIDUy4 +VhxJYhL4T6ED0Ds7tZRsG71LcMfw2RQUgiS1FuYh+O7N8lUMukMy2/umQluM0+qB +CYWa2p1UCbVzlrW1qgKQm1Q8E91XSR9KL/zdO8m9/uNeI1jyJu6i1cibWR7gnh6J +ChLxouQlrBzuLzSz7PG8q1MOWi+oHYJWSvmsckbQDhwEsfhFrYVgndJdxnmlkzvS +1OP7RGSYXLfMF+ZxC2YEJiU65QACCl2IHknyNiL8Jg5ahXgZMNshyfvOv5RB5jnz +4vzRpGhUYCcyLzORT+5gY9ZYbX/51cOomQV1ryTTQs+zA8mfEVLjbbLqvYdI84LC +3chMdcOm8Z9U1xdb2FX/c724XDyPnQNy1PLggzqvOFZzLeey0nBVUWyVrcCydbS5 +PAvVoAucO8kqP6b7uB7QnDeGaCiAVF+9QaXxjyQEdLEu3z5JMM7uH4UCAwEAAaNT +MFEwHQYDVR0OBBYEFHXrT3L+O3kJ2xNZ4Lh1ThGG6M1vMB8GA1UdIwQYMBaAFHXr +T3L+O3kJ2xNZ4Lh1ThGG6M1vMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggIBABFDqkTdxsJyu5L2x3WSpw4jw4vgJYlgUvTSeU8i54DaSzncLZdpWsqb +37LFHkvlquIfvOi9f9EBT2KuZwaajPQBNE4m7kLchoAv8Mc8a2EXhN2caXamnN3F +vWAb4QW/VHKKz2vWprfARwqQO58TEPgzU4FcW1lPpX2ULBeoS5kZDDEgyfaFZETF +FnsSb9E3/YuH6sJCu880kbW8BIyQmbUytrbn+16J/iaZBwc+iD49t2VBLDOsr1x4 +5qzxknG3h9wiz9ob9v6hWFfMpdiK12S0P5FVsUkCpxoae8jc8rPS7W3HaYowFjVR +OHOjtWy5/SV2rypKShjg9manf6iwGdTGkD0qoqsRs9JQFabjNR23IQv+1OUbrEVC +DbS65IjwLJlIZBX8JuJaU3I8zqj/9q7TtRDp1NCiG5W0NgipERRCciWaLJ+Fz6Lu +QzhI2ZOJrl49hmr6e0bsyNUv9l89WcbKm3/IC+V7o80uADYCOaz2jDGfKbvcPHzN +mTma8qVsjpcedttsvNMyZOsM/Rpk+dbChgReRVvcmzQV0izEvJJBWFr4HrfcM6Ev +sZrnUiT8ENUZqiK40d+T3Q6JheHwm+ENI1aUDkYCpoWZ/PzKe+Bj8lR8dPvmeVrc +eiwS37nMFO/5X7aIkszTouScNO99cN0UqPldfJo+8ZTbai5VFxGD +-----END CERTIFICATE----- diff --git a/rule-engine/rule-engine-components/src/test/resources/pem/rsa_encrypted_cert.pem b/rule-engine/rule-engine-components/src/test/resources/pem/rsa_encrypted_cert.pem new file mode 100644 index 0000000000..2f4e55a612 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/resources/pem/rsa_encrypted_cert.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDkzCCAnugAwIBAgIUKAylzm/K5OfbXSjm1zY9bX1a8HQwDQYJKoZIhvcNAQEL +BQAwWTELMAkGA1UEBhMCVUExDTALBgNVBAgMBEtZSVYxDTALBgNVBAcMBEtZSVYx +CzAJBgNVBAoMAlRCMQswCQYDVQQLDAJUQjESMBAGA1UEAwwJbG9jYWxob3N0MB4X +DTIzMTAxNjIxMDQwNVoXDTI0MTAxNTIxMDQwNVowWTELMAkGA1UEBhMCVUExDTAL +BgNVBAgMBEtZSVYxDTALBgNVBAcMBEtZSVYxCzAJBgNVBAoMAlRCMQswCQYDVQQL +DAJUQjESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEA87nLliszEWml8QvyAC+H80NZCxf4TcG826NBOp0AUPJ8xQBHCzc1 +t1ohVm2/fn2VJZAYXG2xSVcHyXjjjv3iGLE2AIDbXh06/yFg4TVjlbrWrAHFehyN +FwrK8ez36oGLa3ZVq+mx1fLfBQw5mStbh09NXmKTzqP6m9ggKtt63cUwoWdUTemT +qrjryJd69LiJi+MVqtbKO2j30/lgAZmaHtbojl9EcvWfeXLb20TnXRIctaIS1VGo +SluzjbNQErdN/VRW4RAOP6UFsK0xID2EuLODBmAWnI49fXO/OS+u3Kd3suABE0o9 +slfDXqNTp0r5N0OoSAFcc4EsV3+9Gf+mqwIDAQABo1MwUTAdBgNVHQ4EFgQUhS5K +XQDxGvaBCpKY1de+JZl8zjYwHwYDVR0jBBgwFoAUhS5KXQDxGvaBCpKY1de+JZl8 +zjYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAxez4vLtBCBNM +l6AQghViNAR9iiwYMxUwKwlU+uZftRGnT+6dXgfTR3PV6LCfMMmtuNs0JTGy0ff8 +erbzfZxExvHfIFXCwepwTWawQhvRRn9GHOJXIzESDRRhsXoJDzd0JVOx0wWxp1cz +EUts+ZbKLoC+kIhsOGY+0a+sopeV2rMO5bUMpA8P0mKZlGynEGMLzKxz65E/IA9h +EQKpJjpvYfN+7eUkF6ZRXNV2LI/8BCoG6mOVoOMEXnloPwwBtOevoCB43U3sT9Er +WQWgZdbeI4gEyEqgMTibNogZZF0KW+5as3iv7avDd8pCgONvD0iwKSlvi9RNjiw8 +p6bwNmBcuA== +-----END CERTIFICATE----- diff --git a/rule-engine/rule-engine-components/src/test/resources/pem/rsa_encrypted_key.pem b/rule-engine/rule-engine-components/src/test/resources/pem/rsa_encrypted_key.pem new file mode 100644 index 0000000000..045a44ce28 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/resources/pem/rsa_encrypted_key.pem @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQILTHGLs8mGUkCAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBAUnb+mChJ9Wu9F7q6ingLYBIIE +0Mwe8Zl6fs5kiT1AL7gXrSSXmyOVvxDFt0V3TX1w399VIadcUUO0RQeEqXoMEUzl +5at99Xmoo7ByvZSPWCdV4d/j5Yw+Z15euxzclSZJnmBgvQx8cFPLCTTaqlgv5r/Y +lTzBrczbgruMFKtzkvHgvZYiagccOtFHDNC1fUBcUR8dkOOsgTiy4QCVo5kkXHt/ +rblE/uVWI8/E318WZBZaz68HcmGIG6ivdMEsKskSKrH6zA3eLjyGB+zSAIPRB/Mp +s7Rj+RK74zFSYyaq6fgdTG8lug2f3rHImSOtQThcfme5XL4P66rUgJsh/sml1vqH +e848VArGoVy3wfvkss6CyXIJevhFh2xVWRyVqG1nraw2QssEnVqIZvdAnaJJONX8 +r1trjHkZ1JD0nO9Mns1c/bw6hjK6W3UwGfgEMM9VQ7wNI2B6CFOXHKTHg6r3us4k +UqaQtfbpTv+d0YKF/rolDcK+mK/rkxP3rtJA7Ud8nQ4VjxyYX4jTs3/BzDkP7Tsj +5gKy9e1zuTF+MUWs3G5oKGQUKVcbgoYJ+iOqgVSd1JbecRo7Pl8XgDv3I9RWHzUr +EAMjVJjRU9tJuPvILFBkpl1/OPC9sGxJz9Hy9qLtEGhGLUhNz6XmIy/aWPCyA4ea +ZES/n7f+aYmXIxulcxS7MUejkwl1EtNqVyrKvLiRBXjBk2HPCb7Te8fRu/LpHZXN +D7wjymg1fGZPPFzdKh7wMdAKiK50KIMGXTxS6kHb6qW/755oSUjWRLPGcPCfdbjn +UiC5WC9FCog6jfRq1rMlz5b8yjyb+UbJ6N4qFSHeQf+7WLeS0Di8k3cLDSWl6T7M +z82ePof2V2TrADNpXvAcR78uiDfUpfa7DhkimvbBZpRVaaQVU7unxUPVc93WgCWV +a7kBuFJAGt+Wt1LPPD/5KOQ5pRINSoh4VhiZzmnY/m7RDPWEaL5gsMjF5bFoP2UZ +MuyAiTmvO299lJDrdQyQds7yafO9PrTE4msuqpuZSHW7ZZIdRO6EXlfZ1We3icWr ++jE47bUIEl04k1PvXyv9LeoqlHZJTagxZIerMOEwq976MaVR7RJbqUpRUV9FFNCL +gTouPCwUcVtLCaTYQjz/+12/YeVkiBHIWkI8Vv5Mn3Vkwy303ygGCQ+brht1e8x+ +BbgzSpiX8aHiEuDAKooewxnKrf3Dk9BcwbnftxajOZcZ3iphk07t5VLRy86zLKCq +ZOY+KymcDCGaOPnSHFrZK3lZOOT+BB9Vi6EYAkxZCZgoDsb/voMEdpPlxK7ultf6 +is5/JQeQbeP9wbNh4Ru2x3p5Ir4wffhh1KT3UsMobusosTo55ErhMHPvH5amppwq +IrxdM7heo7JMaNKmtol4y45IqSt58iluF5m2Ds4m85xjDteRgEOjtNBStFxPCMAB +KUEzRxEaplAcJfzYzpYtoHZuZ8W3Gi7yeXQ+BV8Q9DeaZc5DDDhIcIkOoHOAKhit +d7Gpr8hpwc60AgHRjua8OdbhM4ntT1xnyDEqZbP8mN+UBAohOHMrqo+f4DL1ibB9 +qSwfdLiVItsEBqlfANV3i9rEeKNH5tOFwFCmmH1yBCSDCtWPmPUJ5tZae6fIetK5 +uSstFXLaDpm6fcHgkeqrnyteWpnk5X5SQQ+fMHPjQ2vp +-----END ENCRYPTED PRIVATE KEY----- diff --git a/rule-engine/rule-engine-components/src/test/resources/pem/rsa_encrypted_traditional_cert.pem b/rule-engine/rule-engine-components/src/test/resources/pem/rsa_encrypted_traditional_cert.pem new file mode 100644 index 0000000000..38ab2e42fe --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/resources/pem/rsa_encrypted_traditional_cert.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDkzCCAnugAwIBAgIUIo+5l07ZrQR/LxEEmUbnn4yxCwIwDQYJKoZIhvcNAQEL +BQAwWTELMAkGA1UEBhMCVUExDTALBgNVBAgMBEtZSVYxDTALBgNVBAcMBEtZSVYx +CzAJBgNVBAoMAlRCMQswCQYDVQQLDAJUQjESMBAGA1UEAwwJbG9jYWxob3N0MB4X +DTIzMTAxNjIxMDMxNVoXDTI0MTAxNTIxMDMxNVowWTELMAkGA1UEBhMCVUExDTAL +BgNVBAgMBEtZSVYxDTALBgNVBAcMBEtZSVYxCzAJBgNVBAoMAlRCMQswCQYDVQQL +DAJUQjESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEA87nLliszEWml8QvyAC+H80NZCxf4TcG826NBOp0AUPJ8xQBHCzc1 +t1ohVm2/fn2VJZAYXG2xSVcHyXjjjv3iGLE2AIDbXh06/yFg4TVjlbrWrAHFehyN +FwrK8ez36oGLa3ZVq+mx1fLfBQw5mStbh09NXmKTzqP6m9ggKtt63cUwoWdUTemT +qrjryJd69LiJi+MVqtbKO2j30/lgAZmaHtbojl9EcvWfeXLb20TnXRIctaIS1VGo +SluzjbNQErdN/VRW4RAOP6UFsK0xID2EuLODBmAWnI49fXO/OS+u3Kd3suABE0o9 +slfDXqNTp0r5N0OoSAFcc4EsV3+9Gf+mqwIDAQABo1MwUTAdBgNVHQ4EFgQUhS5K +XQDxGvaBCpKY1de+JZl8zjYwHwYDVR0jBBgwFoAUhS5KXQDxGvaBCpKY1de+JZl8 +zjYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAiUTgjnsVIg90 +Dm+XSlscIPbZEj/mJanoFFfAfbVJz1DadygG9viVUMf3jVQBcsJGeBDckR2b3OHY +82cQVpdu3Heqld+gnfsyi8QBi7EdK4i0q8NVqFgpw83KxNm9xt7xrgHtxhE0kWfW +dpTgeIu0hFf0qLUObw/g8+0awBuxNY2crLtLXQM/dRgtv5Zt/DilW3jMLAE5wke+ +/HM4/emOJO6DSI9BC8iUsmNpIpq45267jcjpczNBo3ap7Bad+jM/paRDng9Uavvr +VCsaJFaL5HG6TtNXN60npBouOWnivPzUeuTI4PnjGRgdp3lgb0IuXbuwxIW6FVG/ +73RHc0gGOA== +-----END CERTIFICATE----- diff --git a/rule-engine/rule-engine-components/src/test/resources/pem/rsa_encrypted_traditional_key.pem b/rule-engine/rule-engine-components/src/test/resources/pem/rsa_encrypted_traditional_key.pem new file mode 100644 index 0000000000..0b0e128a25 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/resources/pem/rsa_encrypted_traditional_key.pem @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,FB5DE36A7A8B25DA + +skR15rUvZmdLzGqU5/BF4Yc3E6dxtXTvlOhuGnqH/idItMKUMWIIlQ7ZfWYF/CrC +CkeAUqhF4y+y+2eR4ejUzZKs6bYjTtkXAXAqQvCsTrTBdQSCcLwbHWLWMro0UG24 +e23Rx8kD0YC7VHqyr08NlLh94wR7kanhEeRUbmKBonZT/I9AZ5ntiBxq9QVBtc8M +f7LKIsnQDcd39cVXSo3LOJ/x7YVB//Ln1R1dexwxE0sXOyLq2hhrxzfHGuGXXW41 +/3+CeTgmX9Rzpawrq9vbVabPUgFcJlrogNRSnUAm9kz4b2zadCxEaCejVmhBy4wx +z2AJGcmE+D4VkQK1AAj5+AQQrPOIIFQnyGjwHkJSTGVTcKmttRYZBvUjdfQfj1fK +NsKOSZLzZGknM8Pz5MHgHqk70C7f+nm0uVhVuAiykA4PY4JdCAuTWJxMM2LWM3/q +rYCEMwxCGa6U92dakfC+W+d9pAbeN+xYOWkDrqG7BdAYg1P70cuMRPdd5bsq3YPp +G4n5NVyQvLlocGhZgC7NVzUtc18+rGblV4D657+GZwJnBZJN4TYey4+r2D5fv9rO +kcRVwfR704BbUBkjIzVzD7nXtBbr3ni8HSqde3g4aVL4WyV9XNvjUsYyrZ0u9Mt1 +IccAsa1xBquUNMxwO1H1mFLtzPKmFzKtlzqiiDsRQoRylwUa1k03sHKUflZRa8Sf +g4MpTRzK+vo1opMemlonty5YbvWXKlH68ioo49L8N457Y0hIUJOQgywg80NxT33t +x8y66lawd1Iv+Q7pptVxJtA4JmcdPvGwBLJZY4DacMyp/JqchAQfSQfmQ0tC+RfJ +z2By/s5wOEuVDksgp8RF1gn+VmvLyOoLK7tq+zpMO1mhfYTCMgSiz2GkNdiW6i25 +gjNWN/F62YL+9VJo4+olrcsYDFiiJq+deQk3H1tQJzu6qECfDqKDyw7IunvTwFil +5/d43LvLbRj75Kf/++xwTjfHudeTgw02/yPyELnURkUazvkOFsn7n8tU46Qm5TWh +fPFXSYxRf3m9rkKZB98YOJo595RuZyiYg9dEQX8Gybl1/7H7l4Cvw6yp71kgLrrI +JRYEt9pmWbQX97UFC3WTVMdKWakFziYVGPvFKkIzrHgcutbQVNsZ7GbO+rdWMIxr +SfUe6jCEclzGQjI9Ep4PTLjZvbusUMkoUjGasAluXFXDC4RKtpuXd4RmbOLdVuyN +OnZ5KZHFjrv4ch5PakRTViWFWSddV5CJ4fMkCG9qUHKrUWGjOvzu5rbcUzq3xJZG +9loIvlA4ekEAhQPHwx69uBmUwnCgyB9CosQGmUlwmC3KALA/EiXklTA6w0fGiiPk +uLA9oBGrVcD8Peug9Owfmj4fWbxJGP7x1UR/nZWpynIfzME0AD8MK5uqoWmQTG8I +cLSjVAB1CO//AZe4LpYQulMPq4dipmE+YnKLi0WPXuZVARDciAGGV2BfH6Iv8j9k +o9IoklcpGg22zXLoGn4tu+7Y5GcoV/mx68Gun1E/QFuY59damAqoc82EEsxr+UX0 +4vGX+KZn283xSYiilE3qpCZOER0ZUFInphUwJzzYfW3mW/AWR78tFQIQiuVKV2/a +-----END RSA PRIVATE KEY----- diff --git a/rule-engine/rule-engine-components/src/test/resources/pem/rsa_key.pem b/rule-engine/rule-engine-components/src/test/resources/pem/rsa_key.pem new file mode 100644 index 0000000000..8a92a68908 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/resources/pem/rsa_key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +wOqb/axEbvcj5sV6/4p2zz6AzFPEmQ== +-----END PRIVATE KEY----- diff --git a/tools/pom.xml b/tools/pom.xml index 1cdd789ffb..925946e8c5 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard tools diff --git a/tools/src/main/python/check_yml_file.py b/tools/src/main/python/check_yml_file.py new file mode 100644 index 0000000000..ea848c8cc8 --- /dev/null +++ b/tools/src/main/python/check_yml_file.py @@ -0,0 +1,134 @@ +# +# Copyright © 2016-2023 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import sys +import re + + +def extract_properties_with_comments(yaml_file_path): + properties = {} + + with open(yaml_file_path, 'r') as file: + lines = file.readlines() + index = 0 + key_level_map = {0: ''} + parse_line('', '', key_level_map, 0, index, lines, properties) + + return properties + + +def parse_line(table_name, comment, key_level_map, parent_line_level, index, lines, properties): + if index >= len(lines): + return + line = lines[index] + line_level = (len(line) - len(line.lstrip())) if line.strip() else 0 + line = line.strip() + # if line is empty - parse next line + if not line: + index = index + 1 + parse_line(table_name, comment, key_level_map, line_level, index, lines, properties) + # if line is a comment - save comment and parse next line + else: + if line_level == 0: + key_level_map = {0: ''} + if line.startswith('#'): + if line_level == 0: + table_name = line.lstrip('#') + elif line_level == parent_line_level: + comment = comment + '\n' + line.lstrip('#') + else: + comment = line.lstrip('#') + index = index + 1 + parse_line(table_name, comment, key_level_map, line_level, index, lines, properties) + else: + # Check if it's a property line + if ':' in line: + # clean comment if level was changed + if line_level != parent_line_level: + comment = '' + key, value = line.split(':', 1) + if key.startswith('- '): + key = key.lstrip('- ') + key_level_map[line_level] = key + value = value.strip() + if value.split('#')[0]: + current_key = '' + for k in key_level_map.keys(): + if k <= line_level: + current_key = ((current_key + '.') if current_key else '') + key_level_map[k] + properties[current_key] = (value, comment, table_name) + comment = '' + index = index + 1 + parse_line(table_name, comment, key_level_map, line_level, index, lines, properties) + +def extract_property_info(properties): + rows = [] + for property_name, value in properties.items(): + if '#' in value[0]: + value_parts = value[0].split('#') + comment = value_parts[1] + else: + comment = value[1] + pattern = r'\"\$\{(.*?)\:(.*?)\}\"' + match = re.match(pattern, value[0]) + if match is not None: + rows.append((property_name, match.group(1), match.group(2), comment, value[2])) + else: + rows.append((property_name, "", value[0].split('#')[0], comment, value[2])) + return rows + +def check_descriptions(properties): + variables_without_description = [] + for row in properties: + # Extract information from the tuple + property_name, env_variable, default_value, comment, table_name = row + if comment == '' or len(comment) < 5 : + variables_without_description.append(property_name) + + return variables_without_description + + +def check_yml(total_list, input_yaml_file): + # Parse yml file to map where key is property key path with '.' separator + # and value is an object (env_name_with_default_value, comment, table_name) + properties = extract_properties_with_comments(input_yaml_file) + + # Extract property information (extract env name, default value and comment nearby property) + property_info = extract_property_info(properties) + + # Check all properties have descriptions + variables_without_description = check_descriptions(property_info) + total_list.extend(variables_without_description) + if len(variables_without_description) > 0: + print(f"Check {input_yaml_file}. There are some yml properties without valid description: (total {len(variables_without_description)}) {variables_without_description}.") + +if __name__ == '__main__': + sys.setrecursionlimit(10000) + files_to_check = ["application/src/main/resources/thingsboard.yml", + "transport/http/src/main/resources/tb-http-transport.yml", + "transport/mqtt/src/main/resources/tb-mqtt-transport.yml", + "transport/coap/src/main/resources/tb-coap-transport.yml", + "transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml", + "transport/snmp/src/main/resources/tb-snmp-transport.yml", + "msa/vc-executor/src/main/resources/tb-vc-executor.yml"] + + total_list = [] + for file in files_to_check: + check_yml(total_list, file) + if len(total_list) > 0: + exit(1) + else: + print("All yml properties have valid description.") \ No newline at end of file diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index d7572a1523..47dea23ba8 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml index 332709e3fa..ab5deff3f7 100644 --- a/transport/coap/src/main/resources/tb-coap-transport.yml +++ b/transport/coap/src/main/resources/tb-coap-transport.yml @@ -14,13 +14,12 @@ # limitations under the License. # -# If you enabled process metrics you should also enable 'web-environment'. -spring.main.web-environment: "${WEB_APPLICATION_ENABLE:false}" -# If you enabled process metrics you should set 'web-application-type' to 'servlet' value. -spring.main.web-application-type: "${WEB_APPLICATION_TYPE:none}" - -spring.main.allow-circular-references: "true" +# Spring common parameters +spring.main.web-environment: "${WEB_APPLICATION_ENABLE:false}" # If you enabled process metrics you should also enable 'web-environment'. +spring.main.web-application-type: "${WEB_APPLICATION_TYPE:none}" # If you enabled process metrics you should set 'web-application-type' to 'servlet' value. +spring.main.allow-circular-references: "true" # Spring Boot configuration property that controls whether circular dependencies between beans are allowed. +# Server common parameters server: # Server bind address (has no effect if web-environment is disabled). address: "${HTTP_BIND_ADDRESS:0.0.0.0}" @@ -41,17 +40,31 @@ zk: session_timeout_ms: "${ZOOKEEPER_SESSION_TIMEOUT_MS:3000}" # Name of the directory in zookeeper 'filesystem' zk_dir: "${ZOOKEEPER_NODES_DIR:/thingsboard}" + # The recalculate_delay property is recommended in a microservices architecture setup for rule-engine services. + # This property provides a pause to ensure that when a rule-engine service is restarted, other nodes don't immediately attempt to recalculate their partitions. + # The delay is recommended because the initialization of rule chain actors is time-consuming. Avoiding unnecessary recalculations during a restart can enhance system performance and stability. + recalculate_delay: "${ZOOKEEPER_RECALCULATE_DELAY_MS:0}" +# Cache parameters cache: + # caffeine or redis type: "${CACHE_TYPE:redis}" + # Deliberately placed outside the 'specs' group above + entityLimits: + timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_LIMITS_TTL:5}" # Entity limits cache TTL + maxSize: "${CACHE_SPECS_ENTITY_LIMITS_MAX_SIZE:100000}" # 0 means the cache is disabled +# Redis configuration parameters redis: - # standalone or cluster or sentinel connection: + # standalone or cluster or sentinel type: "${REDIS_CONNECTION_TYPE:standalone}" standalone: + # Redis connection host host: "${REDIS_HOST:localhost}" + # Redis connection port port: "${REDIS_PORT:6379}" + # Use default Redis configuration file useDefaultClientConfig: "${REDIS_USE_DEFAULT_CLIENT_CONFIG:true}" # this value may be used only if you used not default ClientConfig clientName: "${REDIS_CLIENT_NAME:standalone}" @@ -83,26 +96,43 @@ redis: password: "${REDIS_PASSWORD:}" # pool config pool_config: + # Maximum number of connections that can be allocated by the connection pool maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:128}" + # Maximum number of idle connections that can be maintained in the pool without being closed maxIdle: "${REDIS_POOL_CONFIG_MAX_IDLE:128}" + # Minumum number of idle connections that can be maintained in the pool without being closed minIdle: "${REDIS_POOL_CONFIG_MIN_IDLE:16}" + # Enable/Disable PING command send when a connection is borrowed testOnBorrow: "${REDIS_POOL_CONFIG_TEST_ON_BORROW:true}" + # The property is used to specify whether to test the connection before returning it to the connection pool. testOnReturn: "${REDIS_POOL_CONFIG_TEST_ON_RETURN:true}" + # The property is used in the context of connection pooling in Redis testWhileIdle: "${REDIS_POOL_CONFIG_TEST_WHILE_IDLE:true}" + # Minimum amount of time that an idle connection should be idle before it can be evicted from the connection pool. Value set in milliseconds minEvictableMs: "${REDIS_POOL_CONFIG_MIN_EVICTABLE_MS:60000}" + # Specifies the time interval in milliseconds between two consecutive eviction runs evictionRunsMs: "${REDIS_POOL_CONFIG_EVICTION_RUNS_MS:30000}" + # Maximum time in milliseconds where a client is willing to wait for a connection from the pool when all connections are exhausted maxWaitMills: "${REDIS_POOL_CONFIG_MAX_WAIT_MS:60000}" + # Specifies the number of connections to test for eviction during each eviction run numberTestsPerEvictionRun: "${REDIS_POOL_CONFIG_NUMBER_TESTS_PER_EVICTION_RUN:3}" + # Determines the behavior when a thread requests a connection from the pool but there are no available connections and the pool cannot create more due to the maxTotal configuration blockWhenExhausted: "${REDIS_POOL_CONFIG_BLOCK_WHEN_EXHAUSTED:true}" # COAP server parameters transport: coap: + # CoAP bind-address bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}" + # CoAP bind port bind_port: "${COAP_BIND_PORT:5683}" + # CoaP processing timeout in milliseconds timeout: "${COAP_TIMEOUT:10000}" + # CoaP piggyback response timeout in milliseconds piggyback_timeout: "${COAP_PIGGYBACK_TIMEOUT:500}" + # Default PSM Activity Timer if not specified in device profile psm_activity_timer: "${COAP_PSM_ACTIVITY_TIMER:10000}" + # Default PSM Activity Timer if not specified in device profile paging_transmission_window: "${COAP_PAGING_TRANSMISSION_WINDOW:10000}" dtls: # Enable/disable DTLS 1.2 support @@ -140,10 +170,14 @@ transport: x509: # Skip certificate validity check for client certificates. skip_validity_check_for_client_cert: "${TB_COAP_X509_DTLS_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}" + # Inactivity timeout of DTLS session. Used to clean cache dtls_session_inactivity_timeout: "${TB_COAP_X509_DTLS_SESSION_INACTIVITY_TIMEOUT:86400000}" + # Interval of periodic eviction of the timed-out DTLS sessions dtls_session_report_timeout: "${TB_COAP_X509_DTLS_SESSION_REPORT_TIMEOUT:1800000}" sessions: + # Inactivity timeout for device session in transport service. The last activity time of the device session is updated if the device sends any message, including keepalive messages inactivity_timeout: "${TB_TRANSPORT_SESSIONS_INACTIVITY_TIMEOUT:300000}" + # Interval of periodic check for expired sessions and report of the changes to session last activity time report_timeout: "${TB_TRANSPORT_SESSIONS_REPORT_TIMEOUT:3000}" json: # Cast String data types to Numeric if possible when processing Telemetry/Attributes JSON @@ -151,43 +185,75 @@ transport: # Maximum allowed string value length when processing Telemetry/Attributes JSON (0 value disables string value length check) max_string_value_length: "${JSON_MAX_STRING_VALUE_LENGTH:0}" log: + # Enable/Disable log of transport messages to telemetry. For example, logging of LwM2M registration update enabled: "${TB_TRANSPORT_LOG_ENABLED:true}" + # Maximum length of the log message. The content will be truncated to the specified value if needed max_length: "${TB_TRANSPORT_LOG_MAX_LENGTH:1024}" stats: + # Enable/Disable the collection of transport statistics enabled: "${TB_TRANSPORT_STATS_ENABLED:true}" + # Interval of transport statistics logging print-interval-ms: "${TB_TRANSPORT_STATS_PRINT_INTERVAL_MS:60000}" +# Queue configuration parameters queue: type: "${TB_QUEUE_TYPE:kafka}" # kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) + prefix: "${TB_QUEUE_PREFIX:}" # Global queue prefix. If specified, prefix is added before default topic name: 'prefix.default_topic_name'. Prefix is applied to all topics (and consumer groups for kafka). kafka: + # Kafka Bootstrap Servers bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}" ssl: + # Enable/Disable SSL Kafka communication enabled: "${TB_KAFKA_SSL_ENABLED:false}" + # The location of the trust store file truststore.location: "${TB_KAFKA_SSL_TRUSTSTORE_LOCATION:}" + # The password of trust store file if specified truststore.password: "${TB_KAFKA_SSL_TRUSTSTORE_PASSWORD:}" + # The location of the key store file. This is optional for the client and can be used for two-way authentication for the client keystore.location: "${TB_KAFKA_SSL_KEYSTORE_LOCATION:}" + # The store password for the key store file. This is optional for the client and only needed if ‘ssl.keystore.location’ is configured. Key store password is not supported for PEM format keystore.password: "${TB_KAFKA_SSL_KEYSTORE_PASSWORD:}" + # The password of the private key in the key store file or the PEM key specified in ‘keystore.key’ key.password: "${TB_KAFKA_SSL_KEY_PASSWORD:}" + # The number of acknowledgments the producer requires the leader to have received before considering a request complete. This controls the durability of records that are sent. The following settings are allowed:0,1 and all acks: "${TB_KAFKA_ACKS:all}" + # Number of retries. Resend any record whose send fails with a potentially transient error retries: "${TB_KAFKA_RETRIES:1}" + # The compression type for all data generated by the producer. The default is none (i.e. no compression). Valid values none or gzip compression.type: "${TB_KAFKA_COMPRESSION_TYPE:none}" # none or gzip + # Default batch size. This setting gives the upper bound of the batch size to be sent batch.size: "${TB_KAFKA_BATCH_SIZE:16384}" + # This variable creates a small amount of artificial delay—that is, rather than immediately sending out a record linger.ms: "${TB_KAFKA_LINGER_MS:1}" + # The maximum size of a request in bytes. This setting will limit the number of record batches the producer will send in a single request to avoid sending huge requests max.request.size: "${TB_KAFKA_MAX_REQUEST_SIZE:1048576}" + # The maximum number of unacknowledged requests the client will send on a single connection before blocking max.in.flight.requests.per.connection: "${TB_KAFKA_MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION:5}" + # The total bytes of memory the producer can use to buffer records waiting to be sent to the server buffer.memory: "${TB_BUFFER_MEMORY:33554432}" + # The multiple copies of data over the multiple brokers of Kafka replication_factor: "${TB_QUEUE_KAFKA_REPLICATION_FACTOR:1}" + # The maximum delay between invocations of poll() when using consumer group management. This places an upper bound on the amount of time that the consumer can be idle before fetching more records max_poll_interval_ms: "${TB_QUEUE_KAFKA_MAX_POLL_INTERVAL_MS:300000}" + # The maximum number of records returned in a single call to poll() max_poll_records: "${TB_QUEUE_KAFKA_MAX_POLL_RECORDS:8192}" + # The maximum amount of data per-partition the server will return. Records are fetched in batches by the consumer max_partition_fetch_bytes: "${TB_QUEUE_KAFKA_MAX_PARTITION_FETCH_BYTES:16777216}" + # The maximum amount of data the server will return. Records are fetched in batches by the consumer fetch_max_bytes: "${TB_QUEUE_KAFKA_FETCH_MAX_BYTES:134217728}" request.timeout.ms: "${TB_QUEUE_KAFKA_REQUEST_TIMEOUT_MS:30000}" # (30 seconds) # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms session.timeout.ms: "${TB_QUEUE_KAFKA_SESSION_TIMEOUT_MS:10000}" # (10 seconds) # refer to https://docs.confluent.io/platform/current/installation/configuration/consumer-configs.html#consumerconfigs_session.timeout.ms + auto_offset_reset: "${TB_QUEUE_KAFKA_AUTO_OFFSET_RESET:earliest}" # earliest, latest or none + # Enable/Disable using of Confluent Cloud use_confluent_cloud: "${TB_QUEUE_KAFKA_USE_CONFLUENT_CLOUD:false}" confluent: + # The endpoint identification algorithm used by clients to validate server hostname. The default value is https ssl.algorithm: "${TB_QUEUE_KAFKA_CONFLUENT_SSL_ALGORITHM:https}" + # The mechanism used to authenticate Schema Registry requests. SASL/PLAIN should only be used with TLS/SSL as a transport layer to ensure that clear passwords are not transmitted on the wire without encryption sasl.mechanism: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_MECHANISM:PLAIN}" + # Using JAAS Configuration for specifying multiple SASL mechanisms on a broker sasl.config: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_JAAS_CONFIG:org.apache.kafka.common.security.plain.PlainLoginModule required username=\"CLUSTER_API_KEY\" password=\"CLUSTER_API_SECRET\";}" + # Protocol used to communicate with brokers. Valid values are: PLAINTEXT, SSL, SASL_PLAINTEXT, SASL_SSL security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}" other-inline: "${TB_QUEUE_KAFKA_OTHER_PROPERTIES:}" # In this section you can specify custom parameters (semicolon separated) for Kafka consumer/producer/admin # Example "metrics.recording.level:INFO;metrics.sample.window.ms:30000" other: # DEPRECATED. In this section you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside @@ -196,75 +262,132 @@ queue: # - key: "session.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/consumer-configs.html#consumerconfigs_session.timeout.ms # value: "${TB_QUEUE_KAFKA_SESSION_TIMEOUT_MS:10000}" # (10 seconds) topic-properties: + # Kafka properties for Rule Engine rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" + # Kafka properties for Core topics core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" + # Kafka properties for Transport Api topics transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}" + # Kafka properties for Notifications topics notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" aws_sqs: + # Use the default credentials provider for AWS SQS use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" + # Access key ID from AWS IAM user access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" + # Secret access key from AWS IAM user secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}" + # Region from AWS account region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}" + # Number of threads per each AWS SQS queue in consumer threads_per_topic: "${TB_QUEUE_AWS_SQS_THREADS_PER_TOPIC:1}" queue-properties: + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds rule-engine: "${TB_QUEUE_AWS_SQS_RE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds pubsub: + # Project ID from Google Cloud project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" + # API Credentials in JSON format service_account: "${TB_QUEUE_PUBSUB_SERVICE_ACCOUNT:YOUR_SERVICE_ACCOUNT}" - max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" #in bytes + # Message size for PubSub queue. Value in bytes + max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" + # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" + # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" queue-properties: + # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # Pub/Sub properties for Core subscribers, messages which will commit after ackDeadlineInSec period can be consumed again core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # Pub/Sub properties for Transport API subscribers, messages which will commit after ackDeadlineInSec period can be consumed again transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # Pub/Sub properties for Version Control subscribers, messages which will commit after ackDeadlineInSec period can be consumed again notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" service_bus: + # Azure namespace namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" + # Azure Service Bus Shared Access Signatures key name sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" + # Azure Service Bus Shared Access Signatures key sas_key: "${TB_QUEUE_SERVICE_BUS_SAS_KEY:YOUR_SAS_KEY}" + # Number of messages per a consumer max_messages: "${TB_QUEUE_SERVICE_BUS_MAX_MESSAGES:1000}" queue-properties: + # Azure Service Bus properties for Rule Engine queues rule-engine: "${TB_QUEUE_SERVICE_BUS_RE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus properties for Core queues core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus properties for Transport Api queues transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus properties for Notification queues notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" rabbitmq: + # By default empty exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" + # RabbitMQ host used to establish connection host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" + # RabbitMQ host used to establish a connection port: "${TB_QUEUE_RABBIT_MQ_PORT:5672}" + # Virtual hosts provide logical grouping and separation of resources virtual_host: "${TB_QUEUE_RABBIT_MQ_VIRTUAL_HOST:/}" + # Username for RabbitMQ user account username: "${TB_QUEUE_RABBIT_MQ_USERNAME:YOUR_USERNAME}" + # User password for RabbitMQ user account password: "${TB_QUEUE_RABBIT_MQ_PASSWORD:YOUR_PASSWORD}" + # Network connection between clients and RabbitMQ nodes can fail. RabbitMQ Java client supports automatic recovery of connections and topology (queues, exchanges, bindings, and consumers) automatic_recovery_enabled: "${TB_QUEUE_RABBIT_MQ_AUTOMATIC_RECOVERY_ENABLED:false}" + # The connection timeout for the RabbitMQ connection factory connection_timeout: "${TB_QUEUE_RABBIT_MQ_CONNECTION_TIMEOUT:60000}" + # RabbitMQ has a timeout for connection handshake. When clients run in heavily constrained environments, it may be necessary to increase the timeout handshake_timeout: "${TB_QUEUE_RABBIT_MQ_HANDSHAKE_TIMEOUT:10000}" queue-properties: + # RabbitMQ properties for Rule Engine queues rule-engine: "${TB_QUEUE_RABBIT_MQ_RE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ properties for Core queues core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ properties for Transport API queues transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ properties for Notification queues notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" partitions: - hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" + hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 transport_api: + # Topic used to consume api requests from transport microservices requests_topic: "${TB_QUEUE_TRANSPORT_API_REQUEST_TOPIC:tb_transport.api.requests}" + # Topic used to produce api responses to transport microservices responses_topic: "${TB_QUEUE_TRANSPORT_API_RESPONSE_TOPIC:tb_transport.api.responses}" + # Maximum pending api requests from transport microservices to be handled by server max_pending_requests: "${TB_QUEUE_TRANSPORT_MAX_PENDING_REQUESTS:10000}" + # Maximum timeout in milliseconds to handle api request from transport microservice by server max_requests_timeout: "${TB_QUEUE_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}" + # Amount of threads used to invoke callbacks max_callback_threads: "${TB_QUEUE_TRANSPORT_MAX_CALLBACK_THREADS:100}" + # Interval in milliseconds to poll api requests from transport microservices request_poll_interval: "${TB_QUEUE_TRANSPORT_REQUEST_POLL_INTERVAL_MS:25}" + # Interval in milliseconds to poll api response from transport microservices response_poll_interval: "${TB_QUEUE_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}" core: + # Default topic name of Kafka, RabbitMQ, etc. queue topic: "${TB_QUEUE_CORE_TOPIC:tb_core}" + # Interval in milliseconds to poll messages by Core microservices poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" + # Amount of partitions used by Core microservices partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" + # Timeout for processing a message pack by Core microservices pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}" + # Default topic name for queue Kafka, RabbitMQ, etc. usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}" stats: + # Enable/disable statistics for Core microservices enabled: "${TB_QUEUE_CORE_STATS_ENABLED:false}" + # Statistics printing interval for Core microservices print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:10000}" js: # JS Eval request topic @@ -278,19 +401,26 @@ queue: # JS response poll interval response_poll_interval: "${REMOTE_JS_RESPONSE_POLL_INTERVAL_MS:25}" rule-engine: + # Deprecated. It will be removed in the nearest releases topic: "${TB_QUEUE_RULE_ENGINE_TOPIC:tb_rule_engine}" + # Interval in milliseconds to poll messages by Rule Engine poll-interval: "${TB_QUEUE_RULE_ENGINE_POLL_INTERVAL_MS:25}" + # Timeout for processing a message pack of Rule Engine pack-processing-timeout: "${TB_QUEUE_RULE_ENGINE_PACK_PROCESSING_TIMEOUT_MS:60000}" stats: + # Enable/disable statistics for Rule Engine enabled: "${TB_QUEUE_RULE_ENGINE_STATS_ENABLED:true}" + # Statistics printing interval for Rule Engine print-interval-ms: "${TB_QUEUE_RULE_ENGINE_STATS_PRINT_INTERVAL_MS:60000}" transport: # For high priority notifications that require minimum latency and processing time notifications_topic: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_TOPIC:tb_transport.notifications}" + # Interval in milliseconds to poll messages poll_interval: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_POLL_INTERVAL_MS:25}" +# Service common properties service: - type: "${TB_SERVICE_TYPE:tb-transport}" + type: "${TB_SERVICE_TYPE:tb-transport}" # service type # Unique id for this service (autogenerated if empty) id: "${TB_SERVICE_ID:}" @@ -298,17 +428,28 @@ service: usage: stats: report: + # Enable/Disable the collection of statistics about API usage. Collected on a system and tenant level by default enabled: "${USAGE_STATS_REPORT_ENABLED:true}" + # Enable/Disable collection of statistics about API usage on a customer level enabled_per_customer: "${USAGE_STATS_REPORT_PER_CUSTOMER_ENABLED:false}" + # Interval of reporting the statistics. By default, the summarized statistics are sent every 10 seconds interval: "${USAGE_STATS_REPORT_INTERVAL:10}" +# Metrics parameters metrics: # Enable/disable actuator metrics. enabled: "${METRICS_ENABLED:false}" +# General management parameters management: endpoints: web: exposure: # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). include: '${METRICS_ENDPOINTS_EXPOSE:info}' + +# Notification system parameters +notification_system: + rules: + # Semicolon-separated deduplication durations (in millis) for trigger types. Format: 'NotificationRuleTriggerType1:123;NotificationRuleTriggerType2:456' + deduplication_durations: "${TB_NOTIFICATION_RULES_DEDUPLICATION_DURATIONS:RATE_LIMITS:14400000;}" diff --git a/transport/http/pom.xml b/transport/http/pom.xml index 3a991911ce..4220cf97ef 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/http/src/main/resources/tb-http-transport.yml b/transport/http/src/main/resources/tb-http-transport.yml index c8a45c161b..74be81e667 100644 --- a/transport/http/src/main/resources/tb-http-transport.yml +++ b/transport/http/src/main/resources/tb-http-transport.yml @@ -14,8 +14,7 @@ # limitations under the License. # -spring.main.allow-circular-references: "true" - +# Server common properties server: # Server bind address address: "${HTTP_BIND_ADDRESS:0.0.0.0}" @@ -54,6 +53,11 @@ server: # Enable/disable HTTP/2 support enabled: "${HTTP2_ENABLED:true}" +# Spring common parameters +spring.main.allow-circular-references: "true" # Spring Boot configuration property that controls whether circular dependencies between beans are allowed. +spring.servlet.multipart.max-file-size: "${SPRING_SERVLET_MULTIPART_MAX_FILE_SIZE:50MB}" # Total file size cannot exceed 50MB when configuring file uploads +spring.servlet.multipart.max-request-size: "${SPRING_SERVLET_MULTIPART_MAX_REQUEST_SIZE:50MB}" # Total request size for a multipart/form-data cannot exceed 50MB + # Zookeeper connection parameters. Used for service discovery. zk: # Enable/disable zookeeper discovery service. @@ -68,17 +72,32 @@ zk: session_timeout_ms: "${ZOOKEEPER_SESSION_TIMEOUT_MS:3000}" # Name of the directory in zookeeper 'filesystem' zk_dir: "${ZOOKEEPER_NODES_DIR:/thingsboard}" + # The recalculate_delay property is recommended in a microservices architecture setup for rule-engine services. + # This property provides a pause to ensure that when a rule-engine service is restarted, other nodes don't immediately attempt to recalculate their partitions. + # The delay is recommended because the initialization of rule chain actors is time-consuming. Avoiding unnecessary recalculations during a restart can enhance system performance and stability. + recalculate_delay: "${ZOOKEEPER_RECALCULATE_DELAY_MS:0}" +# Cache parameters cache: + # caffeine or redis type: "${CACHE_TYPE:redis}" + # Deliberately placed outside the 'specs' group above + entityLimits: + timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_LIMITS_TTL:5}" # Entity limits cache TTL + maxSize: "${CACHE_SPECS_ENTITY_LIMITS_MAX_SIZE:100000}" # 0 means the cache is disabled +# Redis configuration parameters redis: # standalone or cluster or sentinel connection: + # Redis deployment type: Standalone (single Redis node deployment) OR Cluster type: "${REDIS_CONNECTION_TYPE:standalone}" standalone: + # Redis connection host host: "${REDIS_HOST:localhost}" + # Redis connection port port: "${REDIS_PORT:6379}" + # Use default Redis configuration file useDefaultClientConfig: "${REDIS_USE_DEFAULT_CLIENT_CONFIG:true}" # this value may be used only if you used not default ClientConfig clientName: "${REDIS_CLIENT_NAME:standalone}" @@ -110,25 +129,40 @@ redis: password: "${REDIS_PASSWORD:}" # pool config pool_config: + # Maximum number of connections that can be allocated by the connection pool maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:128}" + # Maximum number of idle connections that can be maintained in the pool without being closed maxIdle: "${REDIS_POOL_CONFIG_MAX_IDLE:128}" + # Minumum number of idle connections that can be maintained in the pool without being closed minIdle: "${REDIS_POOL_CONFIG_MIN_IDLE:16}" + # Enable/Disable PING command send when a connection is borrowed testOnBorrow: "${REDIS_POOL_CONFIG_TEST_ON_BORROW:true}" + # The property is used to specify whether to test the connection before returning it to the connection pool. testOnReturn: "${REDIS_POOL_CONFIG_TEST_ON_RETURN:true}" + # The property is used in the context of connection pooling in Redis testWhileIdle: "${REDIS_POOL_CONFIG_TEST_WHILE_IDLE:true}" + # Minimum amount of time that an idle connection should be idle before it can be evicted from the connection pool. Value set in milliseconds minEvictableMs: "${REDIS_POOL_CONFIG_MIN_EVICTABLE_MS:60000}" + # Specifies the time interval in milliseconds between two consecutive eviction runs evictionRunsMs: "${REDIS_POOL_CONFIG_EVICTION_RUNS_MS:30000}" + # Maximum time in milliseconds where a client is willing to wait for a connection from the pool when all connections are exhausted maxWaitMills: "${REDIS_POOL_CONFIG_MAX_WAIT_MS:60000}" + # Specifies the number of connections to test for eviction during each eviction run numberTestsPerEvictionRun: "${REDIS_POOL_CONFIG_NUMBER_TESTS_PER_EVICTION_RUN:3}" + # Determines the behavior when a thread requests a connection from the pool but there are no available connections and the pool cannot create more due to the maxTotal configuration blockWhenExhausted: "${REDIS_POOL_CONFIG_BLOCK_WHEN_EXHAUSTED:true}" # HTTP server parameters transport: http: + # HTTP request processing timeout in milliseconds request_timeout: "${HTTP_REQUEST_TIMEOUT:60000}" + # HTTP maximum request processing timeout in milliseconds max_request_timeout: "${HTTP_MAX_REQUEST_TIMEOUT:300000}" sessions: + # Inactivity timeout for device session in transport service. The last activity time of the device session is updated if device sends any message, including keepalive messages inactivity_timeout: "${TB_TRANSPORT_SESSIONS_INACTIVITY_TIMEOUT:300000}" + # Interval of periodic check for expired sessions and report of the changes to session last activity time report_timeout: "${TB_TRANSPORT_SESSIONS_REPORT_TIMEOUT:3000}" json: # Cast String data types to Numeric if possible when processing Telemetry/Attributes JSON @@ -136,43 +170,74 @@ transport: # Maximum allowed string value length when processing Telemetry/Attributes JSON (0 value disables string value length check) max_string_value_length: "${JSON_MAX_STRING_VALUE_LENGTH:0}" log: + # Enable/Disable log of transport messages to telemetry. For example, logging of LwM2M registration update enabled: "${TB_TRANSPORT_LOG_ENABLED:true}" + # Maximum length of the log message. The content will be truncated to the specified value if needed max_length: "${TB_TRANSPORT_LOG_MAX_LENGTH:1024}" stats: + # Enable/Disable collection of transport statistics enabled: "${TB_TRANSPORT_STATS_ENABLED:true}" + # Interval of transport statistics logging print-interval-ms: "${TB_TRANSPORT_STATS_PRINT_INTERVAL_MS:60000}" +# Queue configuration parameters queue: type: "${TB_QUEUE_TYPE:kafka}" # kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) + prefix: "${TB_QUEUE_PREFIX:}" # Global queue prefix. If specified, prefix is added before default topic name: 'prefix.default_topic_name'. Prefix is applied to all topics (and consumer groups for kafka) . kafka: + # Kafka Bootstrap Servers bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}" ssl: + # Enable/Disable SSL Kafka communication enabled: "${TB_KAFKA_SSL_ENABLED:false}" + # The location of the trust store file truststore.location: "${TB_KAFKA_SSL_TRUSTSTORE_LOCATION:}" + # The password of trust store file if specified truststore.password: "${TB_KAFKA_SSL_TRUSTSTORE_PASSWORD:}" + # The location of the key store file. This is optional for client and can be used for two-way authentication for client keystore.location: "${TB_KAFKA_SSL_KEYSTORE_LOCATION:}" + # The store password for the key store file. This is optional for client and only needed if ‘ssl.keystore.location’ is configured. Key store password is not supported for PEM format keystore.password: "${TB_KAFKA_SSL_KEYSTORE_PASSWORD:}" + # The password of the private key in the key store file or the PEM key specified in ‘keystore.key’ key.password: "${TB_KAFKA_SSL_KEY_PASSWORD:}" + # The number of acknowledgments the producer requires the leader to have received before considering a request complete. This controls the durability of records that are sent. The following settings are allowed:0,1 and all acks: "${TB_KAFKA_ACKS:all}" + # Number of retries. Resend any record whose send fails with a potentially transient error retries: "${TB_KAFKA_RETRIES:1}" compression.type: "${TB_KAFKA_COMPRESSION_TYPE:none}" # none or gzip + # Default batch size. This setting gives the upper bound of the batch size to be sent batch.size: "${TB_KAFKA_BATCH_SIZE:16384}" + # This variable creates a small amount of artificial delay—that is, rather than immediately sending out a record linger.ms: "${TB_KAFKA_LINGER_MS:1}" + # The maximum size of a request in bytes. This setting will limit the number of record batches the producer will send in a single request to avoid sending huge requests max.request.size: "${TB_KAFKA_MAX_REQUEST_SIZE:1048576}" + # The maximum number of unacknowledged requests the client will send on a single connection before blocking max.in.flight.requests.per.connection: "${TB_KAFKA_MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION:5}" + # The total bytes of memory the producer can use to buffer records waiting to be sent to the server buffer.memory: "${TB_BUFFER_MEMORY:33554432}" + # The multiple copies of data over the multiple brokers of Kafka replication_factor: "${TB_QUEUE_KAFKA_REPLICATION_FACTOR:1}" + # The maximum delay between invocations of poll() when using consumer group management. This places an upper bound on the amount of time that the consumer can be idle before fetching more records max_poll_interval_ms: "${TB_QUEUE_KAFKA_MAX_POLL_INTERVAL_MS:300000}" + # The maximum number of records returned in a single call to poll() max_poll_records: "${TB_QUEUE_KAFKA_MAX_POLL_RECORDS:8192}" + # The maximum amount of data per-partition the server will return. Records are fetched in batches by the consumer max_partition_fetch_bytes: "${TB_QUEUE_KAFKA_MAX_PARTITION_FETCH_BYTES:16777216}" + # The maximum amount of data the server will return. Records are fetched in batches by the consumer fetch_max_bytes: "${TB_QUEUE_KAFKA_FETCH_MAX_BYTES:134217728}" request.timeout.ms: "${TB_QUEUE_KAFKA_REQUEST_TIMEOUT_MS:30000}" # (30 seconds) # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms session.timeout.ms: "${TB_QUEUE_KAFKA_SESSION_TIMEOUT_MS:10000}" # (10 seconds) # refer to https://docs.confluent.io/platform/current/installation/configuration/consumer-configs.html#consumerconfigs_session.timeout.ms + auto_offset_reset: "${TB_QUEUE_KAFKA_AUTO_OFFSET_RESET:earliest}" # earliest, latest or none + # Enable/Disable using of Confluent Cloud use_confluent_cloud: "${TB_QUEUE_KAFKA_USE_CONFLUENT_CLOUD:false}" confluent: + # The endpoint identification algorithm used by clients to validate server host name. The default value is https ssl.algorithm: "${TB_QUEUE_KAFKA_CONFLUENT_SSL_ALGORITHM:https}" + # The mechanism used to authenticate Schema Registry requests. SASL/PLAIN should only be used with TLS/SSL as transport layer to ensure that clear passwords are not transmitted on the wire without encryption sasl.mechanism: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_MECHANISM:PLAIN}" + # Using JAAS Configuration for specifying multiple SASL mechanisms on a broker sasl.config: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_JAAS_CONFIG:org.apache.kafka.common.security.plain.PlainLoginModule required username=\"CLUSTER_API_KEY\" password=\"CLUSTER_API_SECRET\";}" + # Protocol used to communicate with brokers. Valid values are: PLAINTEXT, SSL, SASL_PLAINTEXT, SASL_SSL security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}" other-inline: "${TB_QUEUE_KAFKA_OTHER_PROPERTIES:}" # In this section you can specify custom parameters (semicolon separated) for Kafka consumer/producer/admin # Example "metrics.recording.level:INFO;metrics.sample.window.ms:30000" other: # DEPRECATED. In this section you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside @@ -181,75 +246,133 @@ queue: # - key: "session.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/consumer-configs.html#consumerconfigs_session.timeout.ms # value: "${TB_QUEUE_KAFKA_SESSION_TIMEOUT_MS:10000}" # (10 seconds) topic-properties: + # Kafka properties for Rule Engine rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" + # Kafka properties for Core topics core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" + # Kafka properties for Transport Api topics transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}" + # Kafka properties for Notifications topics notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" aws_sqs: + # Use default credentials provider for AWS SQS use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" + # Access key ID from AWS IAM user access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" + # Secret access key from AWS IAM user secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}" + # Region from AWS account region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}" + # Number of threads per each AWS SQS queue in consumer threads_per_topic: "${TB_QUEUE_AWS_SQS_THREADS_PER_TOPIC:1}" queue-properties: + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds rule-engine: "${TB_QUEUE_AWS_SQS_RE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds + transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds pubsub: + # Project ID from Google Cloud project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" + # API Credentials in JSON format service_account: "${TB_QUEUE_PUBSUB_SERVICE_ACCOUNT:YOUR_SERVICE_ACCOUNT}" - max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" #in bytes + # Message size for PubSub queue.Value in bytes + max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" + # Number of messages per a consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" + # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" queue-properties: + # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consume again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # Pub/Sub properties for Core subscribers, messages which will commit after ackDeadlineInSec period can be consume again core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # Pub/Sub properties for Transport API subscribers, messages which will commit after ackDeadlineInSec period can be consume again transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # Pub/Sub properties for Version Control subscribers, messages which will commit after ackDeadlineInSec period can be consume again notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" service_bus: + # Azure namespace namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" + # Azure Service Bus Shared Access Signatures key name sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" + # Azure Service Bus Shared Access Signatures key sas_key: "${TB_QUEUE_SERVICE_BUS_SAS_KEY:YOUR_SAS_KEY}" + # Number of messages per a consumer max_messages: "${TB_QUEUE_SERVICE_BUS_MAX_MESSAGES:1000}" queue-properties: + # Azure Service Bus properties for Rule Engine queues rule-engine: "${TB_QUEUE_SERVICE_BUS_RE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus properties for Core queues core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus properties for Transport Api queues transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus properties for Notification queues notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" rabbitmq: + # By default empty exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" + # RabbitMQ host used to establish connection host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" + # RabbitMQ host used to establish a connection port: "${TB_QUEUE_RABBIT_MQ_PORT:5672}" + # Virtual hosts provide logical grouping and separation of resources virtual_host: "${TB_QUEUE_RABBIT_MQ_VIRTUAL_HOST:/}" + # Username for RabbitMQ user account username: "${TB_QUEUE_RABBIT_MQ_USERNAME:YOUR_USERNAME}" + # User password for RabbitMQ user account password: "${TB_QUEUE_RABBIT_MQ_PASSWORD:YOUR_PASSWORD}" + # Network connection between clients and RabbitMQ nodes can fail. RabbitMQ Java client supports automatic recovery of connections and topology (queues, exchanges, bindings, and consumers) automatic_recovery_enabled: "${TB_QUEUE_RABBIT_MQ_AUTOMATIC_RECOVERY_ENABLED:false}" + # The connection timeout for the RabbitMQ connection factory connection_timeout: "${TB_QUEUE_RABBIT_MQ_CONNECTION_TIMEOUT:60000}" + # RabbitMQ has a timeout for connection handshake. When clients run in heavily constrained environments, it may be necessary to increase the timeout handshake_timeout: "${TB_QUEUE_RABBIT_MQ_HANDSHAKE_TIMEOUT:10000}" queue-properties: + # RabbitMQ properties for Rule Engine queues rule-engine: "${TB_QUEUE_RABBIT_MQ_RE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ properties for Core queues core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ properties for Transport API queues transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ properties for Notification queues notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" partitions: - hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" + hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 transport_api: + # Topic used to consume api requests from transport microservices requests_topic: "${TB_QUEUE_TRANSPORT_API_REQUEST_TOPIC:tb_transport.api.requests}" + # Topic used to produce api responses to transport microservices responses_topic: "${TB_QUEUE_TRANSPORT_API_RESPONSE_TOPIC:tb_transport.api.responses}" + # Maximum pending api requests from transport microservices to be handled by server max_pending_requests: "${TB_QUEUE_TRANSPORT_MAX_PENDING_REQUESTS:10000}" + # Maximum timeout in milliseconds to handle api request from transport microservice by server max_requests_timeout: "${TB_QUEUE_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}" + # Amount of threads used to invoke callbacks max_callback_threads: "${TB_QUEUE_TRANSPORT_MAX_CALLBACK_THREADS:100}" + # Interval in milliseconds to poll api requests from transport microservices request_poll_interval: "${TB_QUEUE_TRANSPORT_REQUEST_POLL_INTERVAL_MS:25}" + # Interval in milliseconds to poll api response from transport microservices response_poll_interval: "${TB_QUEUE_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}" core: + # Default topic name of Kafka, RabbitMQ, etc. queue topic: "${TB_QUEUE_CORE_TOPIC:tb_core}" + # Interval in milliseconds to poll messages by Core microservices poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" + # Amount of partitions used by Core microservices partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" + # Timeout for processing a message pack by Core microservices pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}" + # Default topic name for queue Kafka, RabbitMQ, etc. usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}" stats: + # Enable/disable statistics for Core microservices enabled: "${TB_QUEUE_CORE_STATS_ENABLED:false}" + # Statistics printing interval for Core microservices print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:10000}" js: # JS Eval request topic @@ -263,19 +386,26 @@ queue: # JS response poll interval response_poll_interval: "${REMOTE_JS_RESPONSE_POLL_INTERVAL_MS:25}" rule-engine: + # Deprecated. It will be removed in the nearest releases topic: "${TB_QUEUE_RULE_ENGINE_TOPIC:tb_rule_engine}" + # Interval in milliseconds to poll messages by Rule Engine poll-interval: "${TB_QUEUE_RULE_ENGINE_POLL_INTERVAL_MS:25}" + # Timeout for processing a message pack of Rule Engine pack-processing-timeout: "${TB_QUEUE_RULE_ENGINE_PACK_PROCESSING_TIMEOUT_MS:60000}" stats: + # Enable/disable statistics for Rule Engine enabled: "${TB_QUEUE_RULE_ENGINE_STATS_ENABLED:true}" + # Statistics printing interval for Rule Engine print-interval-ms: "${TB_QUEUE_RULE_ENGINE_STATS_PRINT_INTERVAL_MS:60000}" transport: # For high priority notifications that require minimum latency and processing time notifications_topic: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_TOPIC:tb_transport.notifications}" + # Interval in milliseconds to poll messages poll_interval: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_POLL_INTERVAL_MS:25}" +# General service parameters service: - type: "${TB_SERVICE_TYPE:tb-transport}" + type: "${TB_SERVICE_TYPE:tb-transport}" # type of service # Unique id for this service (autogenerated if empty) id: "${TB_SERVICE_ID:}" @@ -283,17 +413,28 @@ service: usage: stats: report: + # Enable/Disable the collection of statistics about API usage. Collected on a system and tenant level by default enabled: "${USAGE_STATS_REPORT_ENABLED:true}" + # Enable/Disable collection of statistics about API usage on a customer level enabled_per_customer: "${USAGE_STATS_REPORT_PER_CUSTOMER_ENABLED:false}" + # Interval of reporting the statistics. By default, the summarized statistics are sent every 10 seconds interval: "${USAGE_STATS_REPORT_INTERVAL:10}" +# Metrics parameters metrics: # Enable/disable actuator metrics. enabled: "${METRICS_ENABLED:false}" +# General management parameters management: endpoints: web: exposure: # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). include: '${METRICS_ENDPOINTS_EXPOSE:info}' + +# Notification system parameters +notification_system: + rules: + # Semicolon-separated deduplication durations (in millis) for trigger types. Format: 'NotificationRuleTriggerType1:123;NotificationRuleTriggerType2:456' + deduplication_durations: "${TB_NOTIFICATION_RULES_DEDUPLICATION_DURATIONS:RATE_LIMITS:14400000;}" diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml index e8f639ba85..f9b9368db1 100644 --- a/transport/lwm2m/pom.xml +++ b/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml index 5de2484860..10dbac101c 100644 --- a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml +++ b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml @@ -14,13 +14,12 @@ # limitations under the License. # -# If you enabled process metrics you should also enable 'web-environment'. -spring.main.web-environment: "${WEB_APPLICATION_ENABLE:false}" -# If you enabled process metrics you should set 'web-application-type' to 'servlet' value. -spring.main.web-application-type: "${WEB_APPLICATION_TYPE:none}" - -spring.main.allow-circular-references: "true" +# Spring common parameters +spring.main.web-environment: "${WEB_APPLICATION_ENABLE:false}" # If you enabled process metrics you should also enable 'web-environment'. +spring.main.web-application-type: "${WEB_APPLICATION_TYPE:none}" # If you enabled process metrics you should set 'web-application-type' to 'servlet' value. +spring.main.allow-circular-references: "true" # Spring Boot configuration property that controls whether circular dependencies between beans are allowed. +# Server common parameters server: # Server bind address (has no effect if web-environment is disabled). address: "${HTTP_BIND_ADDRESS:0.0.0.0}" @@ -41,17 +40,31 @@ zk: session_timeout_ms: "${ZOOKEEPER_SESSION_TIMEOUT_MS:3000}" # Name of the directory in zookeeper 'filesystem' zk_dir: "${ZOOKEEPER_NODES_DIR:/thingsboard}" + # The recalculate_delay property is recommended in a microservices architecture setup for rule-engine services. + # This property provides a pause to ensure that when a rule-engine service is restarted, other nodes don't immediately attempt to recalculate their partitions. + # The delay is recommended because the initialization of rule chain actors is time-consuming. Avoiding unnecessary recalculations during a restart can enhance system performance and stability. + recalculate_delay: "${ZOOKEEPER_RECALCULATE_DELAY_MS:0}" +# Cache parameters cache: + # caffeine or redis type: "${CACHE_TYPE:redis}" + # Deliberately placed outside the 'specs' group above + entityLimits: + timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_LIMITS_TTL:5}" # Entity limits cache TTL + maxSize: "${CACHE_SPECS_ENTITY_LIMITS_MAX_SIZE:100000}" # 0 means the cache is disabled +# Redis configuration parameters redis: - # standalone or cluster or sentinel connection: + # standalone or cluster or sentinel type: "${REDIS_CONNECTION_TYPE:standalone}" standalone: + # Redis connection host host: "${REDIS_HOST:localhost}" + # Redis connection port port: "${REDIS_PORT:6379}" + # Use default Redis configuration file useDefaultClientConfig: "${REDIS_USE_DEFAULT_CLIENT_CONFIG:true}" # this value may be used only if you used not default ClientConfig clientName: "${REDIS_CLIENT_NAME:standalone}" @@ -83,22 +96,35 @@ redis: password: "${REDIS_PASSWORD:}" # pool config pool_config: + # Maximum number of connections that can be allocated by the connection pool maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:128}" + # Maximum number of idle connections that can be maintained in the pool without being closed maxIdle: "${REDIS_POOL_CONFIG_MAX_IDLE:128}" + # Minumum number of idle connections that can be maintained in the pool without being closed minIdle: "${REDIS_POOL_CONFIG_MIN_IDLE:16}" + # Enable/Disable PING command send when a connection is borrowed testOnBorrow: "${REDIS_POOL_CONFIG_TEST_ON_BORROW:true}" + # The property is used to specify whether to test the connection before returning it to the connection pool. testOnReturn: "${REDIS_POOL_CONFIG_TEST_ON_RETURN:true}" + # The property is used in the context of connection pooling in Redis testWhileIdle: "${REDIS_POOL_CONFIG_TEST_WHILE_IDLE:true}" + # Minimum amount of time that an idle connection should be idle before it can be evicted from the connection pool. Value set in milliseconds minEvictableMs: "${REDIS_POOL_CONFIG_MIN_EVICTABLE_MS:60000}" + # Specifies the time interval in milliseconds between two consecutive eviction runs evictionRunsMs: "${REDIS_POOL_CONFIG_EVICTION_RUNS_MS:30000}" + # Maximum time in milliseconds where a client is willing to wait for a connection from the pool when all connections are exhausted maxWaitMills: "${REDIS_POOL_CONFIG_MAX_WAIT_MS:60000}" + # Specifies the number of connections to test for eviction during each eviction run numberTestsPerEvictionRun: "${REDIS_POOL_CONFIG_NUMBER_TESTS_PER_EVICTION_RUN:3}" + # Determines the behavior when a thread requests a connection from the pool but there are no available connections and the pool cannot create more due to the maxTotal configuration blockWhenExhausted: "${REDIS_POOL_CONFIG_BLOCK_WHEN_EXHAUSTED:true}" # LWM2M server parameters transport: sessions: + # Inactivity timeout for device session in transport service. The last activity time of the device session is updated if the device sends any message, including keepalive messages inactivity_timeout: "${TB_TRANSPORT_SESSIONS_INACTIVITY_TIMEOUT:300000}" + # Interval of periodic check for expired sessions and report of the changes to session last activity time report_timeout: "${TB_TRANSPORT_SESSIONS_REPORT_TIMEOUT:3000}" json: # Cast String data types to Numeric if possible when processing Telemetry/Attributes JSON @@ -106,11 +132,14 @@ transport: # Maximum allowed string value length when processing Telemetry/Attributes JSON (0 value disables string value length check) max_string_value_length: "${JSON_MAX_STRING_VALUE_LENGTH:0}" client_side_rpc: + # Processing timeout interval of the RPC command on the CLIENT SIDE. Time in milliseconds timeout: "${CLIENT_SIDE_RPC_TIMEOUT:60000}" # Enable/disable http/mqtt/coap transport protocols (has higher priority than certain protocol's 'enabled' property) api_enabled: "${TB_TRANSPORT_API_ENABLED:true}" log: + # Enable/Disable log of transport messages to telemetry. For example, logging of LwM2M registration update enabled: "${TB_TRANSPORT_LOG_ENABLED:true}" + # Maximum length of the log message. The content will be truncated to the specified value if needed max_length: "${TB_TRANSPORT_LOG_MAX_LENGTH:1024}" # Local LwM2M transport parameters lwm2m: @@ -120,11 +149,16 @@ transport: # RFC7925_RETRANSMISSION_TIMEOUT_IN_MILLISECONDS = 9000 retransmission_timeout: "${LWM2M_DTLS_RETRANSMISSION_TIMEOUT_MS:9000}" server: + # LwM2M Server ID id: "${LWM2M_SERVER_ID:123}" + # LwM2M server bind address. Bind to all interfaces by default bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}" + # LwM2M server bind port bind_port: "${LWM2M_BIND_PORT:5685}" security: + # LwM2M server bind address for DTLS. Bind to all interfaces by default bind_address: "${LWM2M_SECURITY_BIND_ADDRESS:0.0.0.0}" + # LwM2M server bind port for DTLS bind_port: "${LWM2M_SECURITY_BIND_PORT:5686}" # Server X509 Certificates support credentials: @@ -155,12 +189,18 @@ transport: # Only Certificate_x509: skip_validity_check_for_client_cert: "${TB_LWM2M_SERVER_SECURITY_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}" bootstrap: + # Enable/disable Bootstrap Server enabled: "${LWM2M_ENABLED_BS:true}" + # Default value in Lwm2mClient after start in mode Bootstrap for the object : name "LWM2M Security" field: "Short Server ID" (deviceProfile: Bootstrap.BOOTSTRAP SERVER.Short ID) id: "${LWM2M_SERVER_ID_BS:111}" + # LwM2M bootstrap server bind address. Bind to all interfaces by default bind_address: "${LWM2M_BS_BIND_ADDRESS:0.0.0.0}" + # LwM2M bootstrap server bind port bind_port: "${LWM2M_BS_BIND_PORT:5687}" security: + # LwM2M bootstrap server bind address for DTLS. Bind to all interfaces by default bind_address: "${LWM2M_BS_SECURITY_BIND_ADDRESS:0.0.0.0}" + # LwM2M bootstrap server bind address for DTLS. Bind to all interfaces by default bind_port: "${LWM2M_BS_SECURITY_BIND_PORT:5688}" # Bootstrap server X509 Certificates support credentials: @@ -207,53 +247,92 @@ transport: store_file: "${LWM2M_TRUST_KEY_STORE:lwm2mtruststorechain.jks}" # Password used to access the key store store_password: "${LWM2M_TRUST_KEY_STORE_PASSWORD:server_ks_password}" + # Set usage of recommended cipher suites; true - allow only recommended cipher suites; false - allow not recommended cipher suites recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}" + # Set usage of recommended supported groups (curves); true - allow only recommended supported groups, false - allow not recommended supported groups recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:true}" + # Timeout of LwM2M operation timeout: "${LWM2M_TIMEOUT:120000}" + # Thread pool size for processing of the LwM2M uplinks uplink_pool_size: "${LWM2M_UPLINK_POOL_SIZE:10}" + # Thread pool size for processing of the LwM2M downlinks downlink_pool_size: "${LWM2M_DOWNLINK_POOL_SIZE:10}" + # Thread pool size for processing of the OTA updates ota_pool_size: "${LWM2M_OTA_POOL_SIZE:10}" + # Period of cleanup for the registrations in store clean_period_in_sec: "${LWM2M_CLEAN_PERIOD_IN_SEC:2}" + # PSM Activity Timer if not specified in the device profile psm_activity_timer: "${LWM2M_PSM_ACTIVITY_TIMER:10000}" + # Paging Transmission Window for eDRX support if not specified in the device profile paging_transmission_window: "${LWM2M_PAGING_TRANSMISSION_WINDOW:10000}" network_config: # In this section you can specify custom parameters for LwM2M network configuration and expose the env variables to configure outside # - key: "PROTOCOL_STAGE_THREAD_COUNT" # value: "${LWM2M_PROTOCOL_STAGE_THREAD_COUNT:4}" stats: + # Enable/Disable the collection of transport statistics enabled: "${TB_TRANSPORT_STATS_ENABLED:true}" + # Interval of transport statistics logging print-interval-ms: "${TB_TRANSPORT_STATS_PRINT_INTERVAL_MS:60000}" +# Queue configuration properties queue: type: "${TB_QUEUE_TYPE:kafka}" # kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) + prefix: "${TB_QUEUE_PREFIX:}" # Global queue prefix. If specified, prefix is added before default topic name: 'prefix.default_topic_name'. Prefix is applied to all topics (and consumer groups for kafka). kafka: + # Kafka Bootstrap Servers bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}" ssl: + # Enable/Disable SSL Kafka communication enabled: "${TB_KAFKA_SSL_ENABLED:false}" + # The location of the trust store file truststore.location: "${TB_KAFKA_SSL_TRUSTSTORE_LOCATION:}" + # The password of trust store file if specified truststore.password: "${TB_KAFKA_SSL_TRUSTSTORE_PASSWORD:}" + # The location of the key store file. This is optional for the client and can be used for two-way authentication for the client keystore.location: "${TB_KAFKA_SSL_KEYSTORE_LOCATION:}" + # The store password for the key store file. This is optional for the client and only needed if ‘ssl.keystore.location’ is configured. Key store password is not supported for PEM format keystore.password: "${TB_KAFKA_SSL_KEYSTORE_PASSWORD:}" + # The password of the private key in the key store file or the PEM key specified in ‘keystore.key’ key.password: "${TB_KAFKA_SSL_KEY_PASSWORD:}" + # The number of acknowledgments the producer requires the leader to have received before considering a request complete. This controls the durability of records that are sent. The following settings are allowed:0,1 and all acks: "${TB_KAFKA_ACKS:all}" + # Number of retries. Resend any record whose send fails with a potentially transient error retries: "${TB_KAFKA_RETRIES:1}" + # The compression type for all data generated by the producer. The default is none (i.e. no compression). Valid values none or gzip compression.type: "${TB_KAFKA_COMPRESSION_TYPE:none}" # none or gzip + # Default batch size. This setting gives the upper bound of the batch size to be sent batch.size: "${TB_KAFKA_BATCH_SIZE:16384}" + # This variable creates a small amount of artificial delay—that is, rather than immediately sending out a record linger.ms: "${TB_KAFKA_LINGER_MS:1}" + # The maximum size of a request in bytes. This setting will limit the number of record batches the producer will send in a single request to avoid sending huge requests max.request.size: "${TB_KAFKA_MAX_REQUEST_SIZE:1048576}" + # The maximum number of unacknowledged requests the client will send on a single connection before blocking max.in.flight.requests.per.connection: "${TB_KAFKA_MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION:5}" + # The total bytes of memory the producer can use to buffer records waiting to be sent to the server buffer.memory: "${TB_BUFFER_MEMORY:33554432}" + # The multiple copies of data over the multiple brokers of Kafka replication_factor: "${TB_QUEUE_KAFKA_REPLICATION_FACTOR:1}" + # The maximum delay between invocations of poll() when using consumer group management. This places an upper bound on the amount of time that the consumer can be idle before fetching more records max_poll_interval_ms: "${TB_QUEUE_KAFKA_MAX_POLL_INTERVAL_MS:300000}" + # The maximum number of records returned in a single call to poll() max_poll_records: "${TB_QUEUE_KAFKA_MAX_POLL_RECORDS:8192}" + # The maximum amount of data per-partition the server will return. Records are fetched in batches by the consumer max_partition_fetch_bytes: "${TB_QUEUE_KAFKA_MAX_PARTITION_FETCH_BYTES:16777216}" + # The maximum amount of data the server will return. Records are fetched in batches by the consumer fetch_max_bytes: "${TB_QUEUE_KAFKA_FETCH_MAX_BYTES:134217728}" request.timeout.ms: "${TB_QUEUE_KAFKA_REQUEST_TIMEOUT_MS:30000}" # (30 seconds) # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms session.timeout.ms: "${TB_QUEUE_KAFKA_SESSION_TIMEOUT_MS:10000}" # (10 seconds) # refer to https://docs.confluent.io/platform/current/installation/configuration/consumer-configs.html#consumerconfigs_session.timeout.ms + auto_offset_reset: "${TB_QUEUE_KAFKA_AUTO_OFFSET_RESET:earliest}" # earliest, latest or none + # Enable/Disable using of Confluent Cloud use_confluent_cloud: "${TB_QUEUE_KAFKA_USE_CONFLUENT_CLOUD:false}" confluent: + # The endpoint identification algorithm used by clients to validate server hostname. The default value is https ssl.algorithm: "${TB_QUEUE_KAFKA_CONFLUENT_SSL_ALGORITHM:https}" + # The mechanism used to authenticate Schema Registry requests. SASL/PLAIN should only be used with TLS/SSL as a transport layer to ensure that clear passwords are not transmitted on the wire without encryption sasl.mechanism: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_MECHANISM:PLAIN}" + # Using JAAS Configuration for specifying multiple SASL mechanisms on a broker sasl.config: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_JAAS_CONFIG:org.apache.kafka.common.security.plain.PlainLoginModule required username=\"CLUSTER_API_KEY\" password=\"CLUSTER_API_SECRET\";}" + # Protocol used to communicate with brokers. Valid values are: PLAINTEXT, SSL, SASL_PLAINTEXT, SASL_SSL security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}" other-inline: "${TB_QUEUE_KAFKA_OTHER_PROPERTIES:}" # In this section you can specify custom parameters (semicolon separated) for Kafka consumer/producer/admin # Example "metrics.recording.level:INFO;metrics.sample.window.ms:30000" other: # DEPRECATED. In this section you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside @@ -262,74 +341,130 @@ queue: # - key: "session.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/consumer-configs.html#consumerconfigs_session.timeout.ms # value: "${TB_QUEUE_KAFKA_SESSION_TIMEOUT_MS:10000}" # (10 seconds) topic-properties: + # Kafka properties for Rule Engine rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" + # Kafka properties for Core topics core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" + # Kafka properties for Transport Api topics transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}" + # Kafka properties for Notifications topics notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" aws_sqs: + # Use the default credentials provider for AWS SQS use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" + # Access key ID from AWS IAM user access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" + # Secret access key from AWS IAM user secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}" + # Region from AWS account region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}" + # Number of threads per each AWS SQS queue in consumer threads_per_topic: "${TB_QUEUE_AWS_SQS_THREADS_PER_TOPIC:1}" queue-properties: + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds rule-engine: "${TB_QUEUE_AWS_SQS_RE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds pubsub: + # Project ID from Google Cloud project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" + # API Credentials in JSON format service_account: "${TB_QUEUE_PUBSUB_SERVICE_ACCOUNT:YOUR_SERVICE_ACCOUNT}" - max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" #in bytes + # Message size for PubSub queue. Value in bytes + max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" + # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" + # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" queue-properties: + # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # Pub/Sub properties for Core subscribers, messages which will commit after ackDeadlineInSec period can be consumed again core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # Pub/Sub properties for Transport API subscribers, messages which will commit after ackDeadlineInSec period can be consumed again transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # Pub/Sub properties for Version Control subscribers, messages which will commit after ackDeadlineInSec period can be consumed again notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" service_bus: + # Azure namespace namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" + # Azure Service Bus Shared Access Signatures key name sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" + # Azure Service Bus Shared Access Signatures key sas_key: "${TB_QUEUE_SERVICE_BUS_SAS_KEY:YOUR_SAS_KEY}" + # Number of messages per a consumer max_messages: "${TB_QUEUE_SERVICE_BUS_MAX_MESSAGES:1000}" queue-properties: + # Azure Service Bus properties for Rule Engine queues rule-engine: "${TB_QUEUE_SERVICE_BUS_RE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus properties for Core queues core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus properties for Transport Api queues transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus properties for Notification queues notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" rabbitmq: + # By default empty exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" + # RabbitMQ host used to establish connection host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" + # RabbitMQ host used to establish a connection port: "${TB_QUEUE_RABBIT_MQ_PORT:5672}" + # Virtual hosts provide logical grouping and separation of resources virtual_host: "${TB_QUEUE_RABBIT_MQ_VIRTUAL_HOST:/}" + # Username for RabbitMQ user account username: "${TB_QUEUE_RABBIT_MQ_USERNAME:YOUR_USERNAME}" + # User password for RabbitMQ user account password: "${TB_QUEUE_RABBIT_MQ_PASSWORD:YOUR_PASSWORD}" + # Network connection between clients and RabbitMQ nodes can fail. RabbitMQ Java client supports automatic recovery of connections and topology (queues, exchanges, bindings, and consumers) automatic_recovery_enabled: "${TB_QUEUE_RABBIT_MQ_AUTOMATIC_RECOVERY_ENABLED:false}" + # The connection timeout for the RabbitMQ connection factory connection_timeout: "${TB_QUEUE_RABBIT_MQ_CONNECTION_TIMEOUT:60000}" + # RabbitMQ has a timeout for connection handshake. When clients run in heavily constrained environments, it may be necessary to increase the timeout handshake_timeout: "${TB_QUEUE_RABBIT_MQ_HANDSHAKE_TIMEOUT:10000}" queue-properties: + # RabbitMQ properties for Rule Engine queues rule-engine: "${TB_QUEUE_RABBIT_MQ_RE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ properties for Core queues core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ properties for Transport API queues transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ properties for Notification queues notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" partitions: - hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" + hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 transport_api: + # Topic used to consume api requests from transport microservices requests_topic: "${TB_QUEUE_TRANSPORT_API_REQUEST_TOPIC:tb_transport.api.requests}" + # Topic used to produce api responses to transport microservices responses_topic: "${TB_QUEUE_TRANSPORT_API_RESPONSE_TOPIC:tb_transport.api.responses}" + # Maximum pending api requests from transport microservices to be handled by server max_pending_requests: "${TB_QUEUE_TRANSPORT_MAX_PENDING_REQUESTS:10000}" + # Maximum timeout in milliseconds to handle api request from transport microservice by server max_requests_timeout: "${TB_QUEUE_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}" + # Amount of threads used to invoke callbacks max_callback_threads: "${TB_QUEUE_TRANSPORT_MAX_CALLBACK_THREADS:100}" + # Interval in milliseconds to poll api requests from transport microservices request_poll_interval: "${TB_QUEUE_TRANSPORT_REQUEST_POLL_INTERVAL_MS:25}" + # Interval in milliseconds to poll api response from transport microservices response_poll_interval: "${TB_QUEUE_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}" core: + # Default topic name of Kafka, RabbitMQ, etc. queue topic: "${TB_QUEUE_CORE_TOPIC:tb_core}" + # Interval in milliseconds to poll messages by Core microservices poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" + # Amount of partitions used by Core microservices partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" + # Timeout for processing a message pack by Core microservices pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}" stats: + # Enable/disable statistics for Core microservices enabled: "${TB_QUEUE_CORE_STATS_ENABLED:false}" + # Statistics printing interval for Core microservices print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:10000}" js: # JS Eval request topic @@ -345,19 +480,26 @@ queue: # JS response auto commit interval response_auto_commit_interval: "${REMOTE_JS_RESPONSE_AUTO_COMMIT_INTERVAL_MS:100}" rule-engine: + # Deprecated. It will be removed in the nearest releases topic: "${TB_QUEUE_RULE_ENGINE_TOPIC:tb_rule_engine}" + # Interval in milliseconds to poll messages by Rule Engine poll-interval: "${TB_QUEUE_RULE_ENGINE_POLL_INTERVAL_MS:25}" + # Timeout for processing a message pack of Rule Engine pack-processing-timeout: "${TB_QUEUE_RULE_ENGINE_PACK_PROCESSING_TIMEOUT_MS:60000}" stats: + # Enable/disable statistics for Rule Engine enabled: "${TB_QUEUE_RULE_ENGINE_STATS_ENABLED:true}" + # Statistics printing interval for Rule Engine print-interval-ms: "${TB_QUEUE_RULE_ENGINE_STATS_PRINT_INTERVAL_MS:60000}" transport: # For high priority notifications that require minimum latency and processing time notifications_topic: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_TOPIC:tb_transport.notifications}" + # Interval in milliseconds to poll messages poll_interval: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_POLL_INTERVAL_MS:25}" +# Service common parameters service: - type: "${TB_SERVICE_TYPE:tb-transport}" + type: "${TB_SERVICE_TYPE:tb-transport}" # service type # Unique id for this service (autogenerated if empty) id: "${TB_SERVICE_ID:}" @@ -365,17 +507,28 @@ service: usage: stats: report: + # Enable/Disable the collection of statistics about API usage. Collected on a system and tenant level by default enabled: "${USAGE_STATS_REPORT_ENABLED:true}" + # Enable/Disable collection of statistics about API usage on a customer level enabled_per_customer: "${USAGE_STATS_REPORT_PER_CUSTOMER_ENABLED:false}" + # Interval of reporting the statistics. By default, the summarized statistics are sent every 10 seconds interval: "${USAGE_STATS_REPORT_INTERVAL:10}" +# Metrics parameters metrics: # Enable/disable actuator metrics. enabled: "${METRICS_ENABLED:false}" +# General management parameters management: endpoints: web: exposure: # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). include: '${METRICS_ENDPOINTS_EXPOSE:info}' + +# Notification system parameters +notification_system: + rules: + # Semicolon-separated deduplication durations (in millis) for trigger types. Format: 'NotificationRuleTriggerType1:123;NotificationRuleTriggerType2:456' + deduplication_durations: "${TB_NOTIFICATION_RULES_DEDUPLICATION_DURATIONS:RATE_LIMITS:14400000;}" diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index 85ea0c86f1..8df709ed35 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml index ed25d00732..0eb8b4315b 100644 --- a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml +++ b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml @@ -14,13 +14,12 @@ # limitations under the License. # -# If you enabled process metrics you should also enable 'web-environment'. -spring.main.web-environment: "${WEB_APPLICATION_ENABLE:false}" -# If you enabled process metrics you should set 'web-application-type' to 'servlet' value. -spring.main.web-application-type: "${WEB_APPLICATION_TYPE:none}" - -spring.main.allow-circular-references: "true" +# Spring common parameters +spring.main.web-environment: "${WEB_APPLICATION_ENABLE:false}" # If you enabled process metrics you should also enable 'web-environment'. +spring.main.web-application-type: "${WEB_APPLICATION_TYPE:none}" # If you enabled process metrics you should set 'web-application-type' to 'servlet' value. +spring.main.allow-circular-references: "true" # Spring Boot configuration property that controls whether circular dependencies between beans are allowed. +# Server common parameters server: # Server bind address (has no effect if web-environment is disabled). address: "${HTTP_BIND_ADDRESS:0.0.0.0}" @@ -41,17 +40,32 @@ zk: session_timeout_ms: "${ZOOKEEPER_SESSION_TIMEOUT_MS:3000}" # Name of the directory in zookeeper 'filesystem' zk_dir: "${ZOOKEEPER_NODES_DIR:/thingsboard}" + # The recalculate_delay property is recommended in a microservices architecture setup for rule-engine services. + # This property provides a pause to ensure that when a rule-engine service is restarted, other nodes don't immediately attempt to recalculate their partitions. + # The delay is recommended because the initialization of rule chain actors is time-consuming. Avoiding unnecessary recalculations during a restart can enhance system performance and stability. + recalculate_delay: "${ZOOKEEPER_RECALCULATE_DELAY_MS:0}" +# Cache parameters cache: + # caffeine or redis type: "${CACHE_TYPE:redis}" + # Deliberately placed outside the 'specs' group above + entityLimits: + timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_LIMITS_TTL:5}" # Entity limits cache TTL + maxSize: "${CACHE_SPECS_ENTITY_LIMITS_MAX_SIZE:100000}" # 0 means the cache is disabled +# Redis configuration parameters redis: # standalone or cluster or sentinel connection: + # Redis deployment type: Standalone (single Redis node deployment) OR Cluster type: "${REDIS_CONNECTION_TYPE:standalone}" standalone: + # Redis connection host host: "${REDIS_HOST:localhost}" + # Redis connection port port: "${REDIS_PORT:6379}" + # Use default Redis configuration file useDefaultClientConfig: "${REDIS_USE_DEFAULT_CLIENT_CONFIG:true}" # this value may be used only if you used not default ClientConfig clientName: "${REDIS_CLIENT_NAME:standalone}" @@ -83,39 +97,58 @@ redis: password: "${REDIS_PASSWORD:}" # pool config pool_config: + # Maximum number of connections that can be allocated by the connection pool maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:128}" + # Maximum number of idle connections that can be maintained in the pool without being closed maxIdle: "${REDIS_POOL_CONFIG_MAX_IDLE:128}" + # Minumum number of idle connections that can be maintained in the pool without being closed minIdle: "${REDIS_POOL_CONFIG_MIN_IDLE:16}" + # Enable/Disable PING command send when a connection is borrowed testOnBorrow: "${REDIS_POOL_CONFIG_TEST_ON_BORROW:true}" + # The property is used to specify whether to test the connection before returning it to the connection pool. testOnReturn: "${REDIS_POOL_CONFIG_TEST_ON_RETURN:true}" + # The property is used in the context of connection pooling in Redis testWhileIdle: "${REDIS_POOL_CONFIG_TEST_WHILE_IDLE:true}" + # Minimum amount of time that an idle connection should be idle before it can be evicted from the connection pool. Value set in milliseconds minEvictableMs: "${REDIS_POOL_CONFIG_MIN_EVICTABLE_MS:60000}" + # Specifies the time interval in milliseconds between two consecutive eviction runs evictionRunsMs: "${REDIS_POOL_CONFIG_EVICTION_RUNS_MS:30000}" + # Maximum time in milliseconds where a client is willing to wait for a connection from the pool when all connections are exhausted maxWaitMills: "${REDIS_POOL_CONFIG_MAX_WAIT_MS:60000}" + # Specifies the number of connections to test for eviction during each eviction run numberTestsPerEvictionRun: "${REDIS_POOL_CONFIG_NUMBER_TESTS_PER_EVICTION_RUN:3}" + # Determines the behavior when a thread requests a connection from the pool but there are no available connections and the pool cannot create more due to the maxTotal configuration blockWhenExhausted: "${REDIS_POOL_CONFIG_BLOCK_WHEN_EXHAUSTED:true}" # MQTT server parameters transport: mqtt: + # MQTT bind-address bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}" + # MQTT bind port bind_port: "${MQTT_BIND_PORT:1883}" # Enable proxy protocol support. Disabled by default. If enabled, supports both v1 and v2. # Useful to get the real IP address of the client in the logs and for rate limits. proxy_enabled: "${MQTT_PROXY_PROTOCOL_ENABLED:false}" + # MQTT processing timeout in milliseconds timeout: "${MQTT_TIMEOUT:10000}" msg_queue_size_per_device_limit: "${MQTT_MSG_QUEUE_SIZE_PER_DEVICE_LIMIT:100}" # messages await in the queue before device connected state. This limit works on low level before TenantProfileLimits mechanism netty: + # Netty leak detector level leak_detector_level: "${NETTY_LEAK_DETECTOR_LVL:DISABLED}" + # Netty BOSS threads count boss_group_thread_count: "${NETTY_BOSS_GROUP_THREADS:1}" + # Netty worker threads count worker_group_thread_count: "${NETTY_WORKER_GROUP_THREADS:12}" + # Max payload size in bytes max_payload_size: "${NETTY_MAX_PAYLOAD_SIZE:65536}" + # Enables TCP keepalive. This means that TCP starts sending keepalive probes when a connection is idle for some time so_keep_alive: "${NETTY_SO_KEEPALIVE:false}" # MQTT SSL configuration ssl: # Enable/disable SSL support enabled: "${MQTT_SSL_ENABLED:false}" - # MQTT SSL bind address + # MQTT SSL bind-address bind_address: "${MQTT_SSL_BIND_ADDRESS:0.0.0.0}" # MQTT SSL bind port bind_port: "${MQTT_SSL_BIND_PORT:8883}" @@ -148,7 +181,9 @@ transport: # Skip certificate validity check for client certificates. skip_validity_check_for_client_cert: "${MQTT_SSL_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}" sessions: + # Inactivity timeout for device session in transport service. The last activity time of the device session is updated if the device sends any message, including keepalive messages inactivity_timeout: "${TB_TRANSPORT_SESSIONS_INACTIVITY_TIMEOUT:300000}" + # Interval of periodic check for expired sessions and report of the changes to session last activity time report_timeout: "${TB_TRANSPORT_SESSIONS_REPORT_TIMEOUT:3000}" json: # Cast String data types to Numeric if possible when processing Telemetry/Attributes JSON @@ -156,12 +191,17 @@ transport: # Maximum allowed string value length when processing Telemetry/Attributes JSON (0 value disables string value length check) max_string_value_length: "${JSON_MAX_STRING_VALUE_LENGTH:0}" log: + # Enable/Disable log of transport messages to telemetry. For example, logging of LwM2M registration update enabled: "${TB_TRANSPORT_LOG_ENABLED:true}" + # Maximum length of the log message. The content will be truncated to the specified value if needed max_length: "${TB_TRANSPORT_LOG_MAX_LENGTH:1024}" stats: + # Enable/Disable the collection of transport statistics enabled: "${TB_TRANSPORT_STATS_ENABLED:true}" + # Interval of transport statistics logging print-interval-ms: "${TB_TRANSPORT_STATS_PRINT_INTERVAL_MS:60000}" client_side_rpc: + # Processing timeout interval of the RPC command on the CLIENT SIDE. Time in milliseconds timeout: "${CLIENT_SIDE_RPC_TIMEOUT:60000}" rate_limits: # Enable or disable generic rate limits. Device and Tenant specific rate limits are controlled in Tenant Profile. @@ -171,38 +211,65 @@ transport: # Timeout to expire block IP addresses ip_block_timeout: "${TB_TRANSPORT_IP_BLOCK_TIMEOUT:60000}" - +# Queue configuration parameters queue: type: "${TB_QUEUE_TYPE:kafka}" # kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) + prefix: "${TB_QUEUE_PREFIX:}" # Global queue prefix. If specified, prefix is added before default topic name: 'prefix.default_topic_name'. Prefix is applied to all topics (and consumer groups for kafka). kafka: + # Kafka Bootstrap Servers bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}" ssl: + # Enable/Disable SSL Kafka communication enabled: "${TB_KAFKA_SSL_ENABLED:false}" + # The location of the trust store file truststore.location: "${TB_KAFKA_SSL_TRUSTSTORE_LOCATION:}" + # The password of trust store file if specified truststore.password: "${TB_KAFKA_SSL_TRUSTSTORE_PASSWORD:}" + # The location of the key store file. This is optional for the client and can be used for two-way authentication for the client keystore.location: "${TB_KAFKA_SSL_KEYSTORE_LOCATION:}" + # The store password for the key store file. This is optional for the client and only needed if ‘ssl.keystore.location’ is configured. Key store password is not supported for PEM format keystore.password: "${TB_KAFKA_SSL_KEYSTORE_PASSWORD:}" + # The password of the private key in the key store file or the PEM key specified in ‘keystore.key’ key.password: "${TB_KAFKA_SSL_KEY_PASSWORD:}" + # The number of acknowledgments the producer requires the leader to have received before considering a request complete. This controls the durability of records that are sent. The following settings are allowed:0,1 and all acks: "${TB_KAFKA_ACKS:all}" + # Number of retries. Resend any record whose send fails with a potentially transient error retries: "${TB_KAFKA_RETRIES:1}" + # The compression type for all data generated by the producer. The default is none (i.e. no compression). Valid values none or gzip compression.type: "${TB_KAFKA_COMPRESSION_TYPE:none}" # none or gzip + # Default batch size. This setting gives the upper bound of the batch size to be sent batch.size: "${TB_KAFKA_BATCH_SIZE:16384}" + # This variable creates a small amount of artificial delay—that is, rather than immediately sending out a record linger.ms: "${TB_KAFKA_LINGER_MS:1}" + # The maximum size of a request in bytes. This setting will limit the number of record batches the producer will send in a single request to avoid sending huge requests max.request.size: "${TB_KAFKA_MAX_REQUEST_SIZE:1048576}" + # The maximum number of unacknowledged requests the client will send on a single connection before blocking max.in.flight.requests.per.connection: "${TB_KAFKA_MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION:5}" + # The total bytes of memory the producer can use to buffer records waiting to be sent to the server buffer.memory: "${TB_BUFFER_MEMORY:33554432}" + # The multiple copies of data over the multiple brokers of Kafka replication_factor: "${TB_QUEUE_KAFKA_REPLICATION_FACTOR:1}" + # The maximum delay between invocations of poll() when using consumer group management. This places an upper bound on the amount of time that the consumer can be idle before fetching more records max_poll_interval_ms: "${TB_QUEUE_KAFKA_MAX_POLL_INTERVAL_MS:300000}" + # The maximum number of records returned in a single call to poll() max_poll_records: "${TB_QUEUE_KAFKA_MAX_POLL_RECORDS:8192}" + # The maximum amount of data per-partition the server will return. Records are fetched in batches by the consumer max_partition_fetch_bytes: "${TB_QUEUE_KAFKA_MAX_PARTITION_FETCH_BYTES:16777216}" + # The maximum amount of data the server will return. Records are fetched in batches by the consumer fetch_max_bytes: "${TB_QUEUE_KAFKA_FETCH_MAX_BYTES:134217728}" request.timeout.ms: "${TB_QUEUE_KAFKA_REQUEST_TIMEOUT_MS:30000}" # (30 seconds) # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms session.timeout.ms: "${TB_QUEUE_KAFKA_SESSION_TIMEOUT_MS:10000}" # (10 seconds) # refer to https://docs.confluent.io/platform/current/installation/configuration/consumer-configs.html#consumerconfigs_session.timeout.ms + auto_offset_reset: "${TB_QUEUE_KAFKA_AUTO_OFFSET_RESET:earliest}" # earliest, latest or none + # Enable/Disable using of Confluent Cloud use_confluent_cloud: "${TB_QUEUE_KAFKA_USE_CONFLUENT_CLOUD:false}" confluent: + # The endpoint identification algorithm used by clients to validate server hostname. The default value is https ssl.algorithm: "${TB_QUEUE_KAFKA_CONFLUENT_SSL_ALGORITHM:https}" + # The mechanism used to authenticate Schema Registry requests. SASL/PLAIN should only be used with TLS/SSL as a transport layer to ensure that clear passwords are not transmitted on the wire without encryption sasl.mechanism: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_MECHANISM:PLAIN}" + # Using JAAS Configuration for specifying multiple SASL mechanisms on a broker sasl.config: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_JAAS_CONFIG:org.apache.kafka.common.security.plain.PlainLoginModule required username=\"CLUSTER_API_KEY\" password=\"CLUSTER_API_SECRET\";}" + # Protocol used to communicate with brokers. Valid values are: PLAINTEXT, SSL, SASL_PLAINTEXT, SASL_SSL security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}" other-inline: "${TB_QUEUE_KAFKA_OTHER_PROPERTIES:}" # In this section you can specify custom parameters (semicolon separated) for Kafka consumer/producer/admin # Example "metrics.recording.level:INFO;metrics.sample.window.ms:30000" other: # DEPRECATED. In this section you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside @@ -211,75 +278,132 @@ queue: # - key: "session.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/consumer-configs.html#consumerconfigs_session.timeout.ms # value: "${TB_QUEUE_KAFKA_SESSION_TIMEOUT_MS:10000}" # (10 seconds) topic-properties: + # Kafka properties for Rule Engine rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" + # Kafka properties for Core topics core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" + # Kafka properties for Transport Api topics transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}" + # Kafka properties for Notifications topics notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" aws_sqs: + # Use the default credentials provider for AWS SQS use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" + # Access key ID from AWS IAM user access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" + # Secret access key from AWS IAM user secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}" + # Region from AWS account region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}" + # Number of threads per each AWS SQS queue in consumer threads_per_topic: "${TB_QUEUE_AWS_SQS_THREADS_PER_TOPIC:1}" queue-properties: + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds rule-engine: "${TB_QUEUE_AWS_SQS_RE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds pubsub: + # Project ID from Google Cloud project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" + # API Credentials in JSON format service_account: "${TB_QUEUE_PUBSUB_SERVICE_ACCOUNT:YOUR_SERVICE_ACCOUNT}" - max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" #in bytes + # Message size for PubSub queue.Value in bytes + max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" + # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" + # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" queue-properties: + # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # Pub/Sub properties for Core subscribers, messages which will commit after ackDeadlineInSec period can be consumed again core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # Pub/Sub properties for Transport API subscribers, messages which will commit after ackDeadlineInSec period can be consumed again transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # Pub/Sub properties for Version Control subscribers, messages which will commit after ackDeadlineInSec period can be consumed again notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" service_bus: + # Azure namespace namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" + # Azure Service Bus Shared Access Signatures key name sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" + # Azure Service Bus Shared Access Signatures key sas_key: "${TB_QUEUE_SERVICE_BUS_SAS_KEY:YOUR_SAS_KEY}" + # Number of messages per a consumer max_messages: "${TB_QUEUE_SERVICE_BUS_MAX_MESSAGES:1000}" queue-properties: + # Azure Service Bus properties for Rule Engine queues rule-engine: "${TB_QUEUE_SERVICE_BUS_RE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus properties for Core queues core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus properties for Transport Api queues transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus properties for Notification queues notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" rabbitmq: + # By default empty exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" + # RabbitMQ host used to establish connection host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" + # RabbitMQ host used to establish a connection port: "${TB_QUEUE_RABBIT_MQ_PORT:5672}" + # Virtual hosts provide logical grouping and separation of resources virtual_host: "${TB_QUEUE_RABBIT_MQ_VIRTUAL_HOST:/}" + # Username for RabbitMQ user account username: "${TB_QUEUE_RABBIT_MQ_USERNAME:YOUR_USERNAME}" + # User password for RabbitMQ user account password: "${TB_QUEUE_RABBIT_MQ_PASSWORD:YOUR_PASSWORD}" + # Network connection between clients and RabbitMQ nodes can fail. RabbitMQ Java client supports automatic recovery of connections and topology (queues, exchanges, bindings, and consumers) automatic_recovery_enabled: "${TB_QUEUE_RABBIT_MQ_AUTOMATIC_RECOVERY_ENABLED:false}" + # The connection timeout for the RabbitMQ connection factory connection_timeout: "${TB_QUEUE_RABBIT_MQ_CONNECTION_TIMEOUT:60000}" + # RabbitMQ has a timeout for connection handshake. When clients run in heavily constrained environments, it may be necessary to increase the timeout handshake_timeout: "${TB_QUEUE_RABBIT_MQ_HANDSHAKE_TIMEOUT:10000}" queue-properties: + # RabbitMQ properties for Rule Engine queues rule-engine: "${TB_QUEUE_RABBIT_MQ_RE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ properties for Core queues core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ properties for Transport API queues transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ properties for Notification queues notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" partitions: - hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" + hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 transport_api: + # Topic used to consume api requests from transport microservices requests_topic: "${TB_QUEUE_TRANSPORT_API_REQUEST_TOPIC:tb_transport.api.requests}" + # Topic used to produce api responses to transport microservices responses_topic: "${TB_QUEUE_TRANSPORT_API_RESPONSE_TOPIC:tb_transport.api.responses}" + # Maximum pending api requests from transport microservices to be handled by server max_pending_requests: "${TB_QUEUE_TRANSPORT_MAX_PENDING_REQUESTS:10000}" + # Maximum timeout in milliseconds to handle api request from transport microservice by server max_requests_timeout: "${TB_QUEUE_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}" + # Amount of threads used to invoke callbacks max_callback_threads: "${TB_QUEUE_TRANSPORT_MAX_CALLBACK_THREADS:100}" + # Interval in milliseconds to poll api requests from transport microservices request_poll_interval: "${TB_QUEUE_TRANSPORT_REQUEST_POLL_INTERVAL_MS:25}" + # Interval in milliseconds to poll api response from transport microservices response_poll_interval: "${TB_QUEUE_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}" core: + # Default topic name of Kafka, RabbitMQ, etc. queue topic: "${TB_QUEUE_CORE_TOPIC:tb_core}" + # Interval in milliseconds to poll messages by Core microservices poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" + # Amount of partitions used by Core microservices partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" + # Timeout for processing a message pack by Core microservices pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}" + # Default topic name for queue Kafka, RabbitMQ, etc. usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}" stats: + # Enable/disable statistics for Core microservices enabled: "${TB_QUEUE_CORE_STATS_ENABLED:false}" + # Statistics printing interval for Core microservices print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:10000}" js: # JS Eval request topic @@ -293,19 +417,26 @@ queue: # JS response poll interval response_poll_interval: "${REMOTE_JS_RESPONSE_POLL_INTERVAL_MS:25}" rule-engine: + # Deprecated. It will be removed in the nearest releases topic: "${TB_QUEUE_RULE_ENGINE_TOPIC:tb_rule_engine}" + # Interval in milliseconds to poll messages by Rule Engine poll-interval: "${TB_QUEUE_RULE_ENGINE_POLL_INTERVAL_MS:25}" + # Timeout for processing a message pack of Rule Engine pack-processing-timeout: "${TB_QUEUE_RULE_ENGINE_PACK_PROCESSING_TIMEOUT_MS:60000}" stats: + # Enable/disable statistics for Rule Engine enabled: "${TB_QUEUE_RULE_ENGINE_STATS_ENABLED:true}" + # Statistics printing interval for Rule Engine print-interval-ms: "${TB_QUEUE_RULE_ENGINE_STATS_PRINT_INTERVAL_MS:60000}" transport: # For high priority notifications that require minimum latency and processing time notifications_topic: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_TOPIC:tb_transport.notifications}" + # Interval in milliseconds to poll messages poll_interval: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_POLL_INTERVAL_MS:25}" +# Service common properties service: - type: "${TB_SERVICE_TYPE:tb-transport}" + type: "${TB_SERVICE_TYPE:tb-transport}" # service type # Unique id for this service (autogenerated if empty) id: "${TB_SERVICE_ID:}" @@ -313,17 +444,28 @@ service: usage: stats: report: + # Enable/Disable the collection of statistics about API usage. Collected on a system and tenant level by default enabled: "${USAGE_STATS_REPORT_ENABLED:true}" + # Enable/Disable collection of statistics about API usage on a customer level enabled_per_customer: "${USAGE_STATS_REPORT_PER_CUSTOMER_ENABLED:false}" + # Interval of reporting the statistics. By default, the summarized statistics are sent every 10 seconds interval: "${USAGE_STATS_REPORT_INTERVAL:10}" +# Metrics parameters metrics: # Enable/disable actuator metrics. enabled: "${METRICS_ENABLED:false}" +# General management parameters management: endpoints: web: exposure: # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). include: '${METRICS_ENDPOINTS_EXPOSE:info}' + +# Notification system parameters +notification_system: + rules: + # Semicolon-separated deduplication durations (in millis) for trigger types. Format: 'NotificationRuleTriggerType1:123;NotificationRuleTriggerType2:456' + deduplication_durations: "${TB_NOTIFICATION_RULES_DEDUPLICATION_DURATIONS:RATE_LIMITS:14400000;}" diff --git a/transport/pom.xml b/transport/pom.xml index ad614a8d2c..0b0e90c710 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard transport diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml index e5ae7ce756..671de0e33c 100644 --- a/transport/snmp/pom.xml +++ b/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT transport diff --git a/transport/snmp/src/main/resources/tb-snmp-transport.yml b/transport/snmp/src/main/resources/tb-snmp-transport.yml index f7fa3902b8..d139b641a6 100644 --- a/transport/snmp/src/main/resources/tb-snmp-transport.yml +++ b/transport/snmp/src/main/resources/tb-snmp-transport.yml @@ -14,13 +14,12 @@ # limitations under the License. # -# If you enabled process metrics you should also enable 'web-environment'. -spring.main.web-environment: "${WEB_APPLICATION_ENABLE:false}" -# If you enabled process metrics you should set 'web-application-type' to 'servlet' value. -spring.main.web-application-type: "${WEB_APPLICATION_TYPE:none}" - -spring.main.allow-circular-references: "true" +# Spring common parameters +spring.main.web-environment: "${WEB_APPLICATION_ENABLE:false}" # If you enabled process metrics you should also enable 'web-environment'. +spring.main.web-application-type: "${WEB_APPLICATION_TYPE:none}" # If you enabled process metrics you should set 'web-application-type' to 'servlet' value. +spring.main.allow-circular-references: "true" # Spring Boot configuration property that controls whether circular dependencies between beans are allowed. +# Server common parameters server: # Server bind address (has no effect if web-environment is disabled). address: "${HTTP_BIND_ADDRESS:0.0.0.0}" @@ -41,17 +40,31 @@ zk: session_timeout_ms: "${ZOOKEEPER_SESSION_TIMEOUT_MS:3000}" # Name of the directory in zookeeper 'filesystem' zk_dir: "${ZOOKEEPER_NODES_DIR:/thingsboard}" + # The recalculate_delay property is recommended in a microservices architecture setup for rule-engine services. + # This property provides a pause to ensure that when a rule-engine service is restarted, other nodes don't immediately attempt to recalculate their partitions. + # The delay is recommended because the initialization of rule chain actors is time-consuming. Avoiding unnecessary recalculations during a restart can enhance system performance and stability. + recalculate_delay: "${ZOOKEEPER_RECALCULATE_DELAY_MS:0}" +# Cache parameters cache: + # caffeine or redis type: "${CACHE_TYPE:redis}" + # Deliberately placed outside the 'specs' group above + entityLimits: + timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_LIMITS_TTL:5}" # Entity limits cache TTL + maxSize: "${CACHE_SPECS_ENTITY_LIMITS_MAX_SIZE:100000}" # 0 means the cache is disabled +# Redis configuration parameters redis: - # standalone or cluster or sentinel connection: + # standalone or cluster or sentinel type: "${REDIS_CONNECTION_TYPE:standalone}" standalone: + # Redis connection host host: "${REDIS_HOST:localhost}" + # Redis connection port port: "${REDIS_PORT:6379}" + # Use default Redis configuration file useDefaultClientConfig: "${REDIS_USE_DEFAULT_CLIENT_CONFIG:true}" # this value may be used only if you used not default ClientConfig clientName: "${REDIS_CLIENT_NAME:standalone}" @@ -83,28 +96,50 @@ redis: password: "${REDIS_PASSWORD:}" # pool config pool_config: + # Maximum number of connections that can be allocated by the connection pool maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:128}" + # Maximum number of idle connections that can be maintained in the pool without being closed maxIdle: "${REDIS_POOL_CONFIG_MAX_IDLE:128}" + # Minumum number of idle connections that can be maintained in the pool without being closed minIdle: "${REDIS_POOL_CONFIG_MIN_IDLE:16}" + # Enable/Disable PING command send when a connection is borrowed testOnBorrow: "${REDIS_POOL_CONFIG_TEST_ON_BORROW:true}" + # The property is used to specify whether to test the connection before returning it to the connection pool. testOnReturn: "${REDIS_POOL_CONFIG_TEST_ON_RETURN:true}" + # The property is used in the context of connection pooling in Redis testWhileIdle: "${REDIS_POOL_CONFIG_TEST_WHILE_IDLE:true}" + # Minimum amount of time that an idle connection should be idle before it can be evicted from the connection pool. Value set in milliseconds minEvictableMs: "${REDIS_POOL_CONFIG_MIN_EVICTABLE_MS:60000}" + # Specifies the time interval in milliseconds between two consecutive eviction runs evictionRunsMs: "${REDIS_POOL_CONFIG_EVICTION_RUNS_MS:30000}" + # Maximum time in milliseconds where a client is willing to wait for a connection from the pool when all connections are exhausted maxWaitMills: "${REDIS_POOL_CONFIG_MAX_WAIT_MS:60000}" + # Specifies the number of connections to test for eviction during each eviction run numberTestsPerEvictionRun: "${REDIS_POOL_CONFIG_NUMBER_TESTS_PER_EVICTION_RUN:3}" + # Determines the behavior when a thread requests a connection from the pool but there are no available connections and the pool cannot create more due to the maxTotal configuration blockWhenExhausted: "${REDIS_POOL_CONFIG_BLOCK_WHEN_EXHAUSTED:true}" +# Snmp server parameters transport: snmp: + # Enable/disable SNMP transport protocol enabled: "${SNMP_ENABLED:true}" + # Snmp bind port + bind_port: "${SNMP_BIND_PORT:1620}" response_processing: # parallelism level for executor (workStealingPool) that is responsible for handling responses from SNMP devices parallelism_level: "${SNMP_RESPONSE_PROCESSING_PARALLELISM_LEVEL:20}" # to configure SNMP to work over UDP or TCP underlying_protocol: "${SNMP_UNDERLYING_PROTOCOL:udp}" + # Batch size to request OID mappings from the device (useful when the device profile has multiple hundreds of OID mappings) + max_request_oids: "${SNMP_MAX_REQUEST_OIDS:100}" + response: + # To ignore SNMP response values that do not match the data type of the configured OID mapping (by default false - will throw an error if any value of the response not match configured data types) + ignore_type_cast_errors: "${SNMP_RESPONSE_IGNORE_TYPE_CAST_ERRORS:false}" sessions: + # Inactivity timeout for device session in transport service. The last activity time of the device session is updated if the device sends any message, including keepalive messages inactivity_timeout: "${TB_TRANSPORT_SESSIONS_INACTIVITY_TIMEOUT:300000}" + # Interval of periodic check for expired sessions and report of the changes to session last activity time report_timeout: "${TB_TRANSPORT_SESSIONS_REPORT_TIMEOUT:3000}" json: # Cast String data types to Numeric if possible when processing Telemetry/Attributes JSON @@ -112,43 +147,75 @@ transport: # Maximum allowed string value length when processing Telemetry/Attributes JSON (0 value disables string value length check) max_string_value_length: "${JSON_MAX_STRING_VALUE_LENGTH:0}" log: + # Enable/Disable log of transport messages to telemetry. For example, logging of LwM2M registration update enabled: "${TB_TRANSPORT_LOG_ENABLED:true}" + # Maximum length of the log message. The content will be truncated to the specified value if needed max_length: "${TB_TRANSPORT_LOG_MAX_LENGTH:1024}" stats: + # Enable/Disable collection of transport statistics enabled: "${TB_TRANSPORT_STATS_ENABLED:true}" + # Interval of transport statistics logging print-interval-ms: "${TB_TRANSPORT_STATS_PRINT_INTERVAL_MS:60000}" +# Queue configuration parameters queue: type: "${TB_QUEUE_TYPE:kafka}" # kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) + prefix: "${TB_QUEUE_PREFIX:}" # Global queue prefix. If specified, prefix is added before default topic name: 'prefix.default_topic_name'. Prefix is applied to all topics (and consumer groups for kafka). kafka: + # Kafka Bootstrap Servers bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}" ssl: + # Enable/Disable SSL Kafka communication enabled: "${TB_KAFKA_SSL_ENABLED:false}" + # The location of the trust store file truststore.location: "${TB_KAFKA_SSL_TRUSTSTORE_LOCATION:}" + # The password of trust store file if specified truststore.password: "${TB_KAFKA_SSL_TRUSTSTORE_PASSWORD:}" + # The location of the key store file. This is optional for the client and can be used for two-way authentication for the client keystore.location: "${TB_KAFKA_SSL_KEYSTORE_LOCATION:}" + # The store password for the key store file. This is optional for the client and only needed if ‘ssl.keystore.location’ is configured. Key store password is not supported for PEM format keystore.password: "${TB_KAFKA_SSL_KEYSTORE_PASSWORD:}" + # The password of the private key in the key store file or the PEM key specified in ‘keystore.key’ key.password: "${TB_KAFKA_SSL_KEY_PASSWORD:}" + # The number of acknowledgments the producer requires the leader to have received before considering a request complete. This controls the durability of records that are sent. The following settings are allowed:0,1 and all acks: "${TB_KAFKA_ACKS:all}" + # Number of retries. Resend any record whose send fails with a potentially transient error retries: "${TB_KAFKA_RETRIES:1}" + # The compression type for all data generated by the producer. The default is none (i.e. no compression). Valid values none or gzip compression.type: "${TB_KAFKA_COMPRESSION_TYPE:none}" # none or gzip + # Default batch size. This setting gives the upper bound of the batch size to be sent batch.size: "${TB_KAFKA_BATCH_SIZE:16384}" + # This variable creates a small amount of artificial delay—that is, rather than immediately sending out a record linger.ms: "${TB_KAFKA_LINGER_MS:1}" + # The maximum size of a request in bytes. This setting will limit the number of record batches the producer will send in a single request to avoid sending huge requests max.request.size: "${TB_KAFKA_MAX_REQUEST_SIZE:1048576}" + # The maximum number of unacknowledged requests the client will send on a single connection before blocking max.in.flight.requests.per.connection: "${TB_KAFKA_MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION:5}" + # The total bytes of memory the producer can use to buffer records waiting to be sent to the server buffer.memory: "${TB_BUFFER_MEMORY:33554432}" + # The multiple copies of data over the multiple brokers of Kafka replication_factor: "${TB_QUEUE_KAFKA_REPLICATION_FACTOR:1}" + # The maximum delay between invocations of poll() when using consumer group management. This places an upper bound on the amount of time that the consumer can be idle before fetching more records max_poll_interval_ms: "${TB_QUEUE_KAFKA_MAX_POLL_INTERVAL_MS:300000}" + # The maximum number of records returned in a single call to poll() max_poll_records: "${TB_QUEUE_KAFKA_MAX_POLL_RECORDS:8192}" + # The maximum amount of data per-partition the server will return. Records are fetched in batches by the consumer max_partition_fetch_bytes: "${TB_QUEUE_KAFKA_MAX_PARTITION_FETCH_BYTES:16777216}" + # The maximum amount of data the server will return. Records are fetched in batches by the consumer fetch_max_bytes: "${TB_QUEUE_KAFKA_FETCH_MAX_BYTES:134217728}" request.timeout.ms: "${TB_QUEUE_KAFKA_REQUEST_TIMEOUT_MS:30000}" # (30 seconds) # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms session.timeout.ms: "${TB_QUEUE_KAFKA_SESSION_TIMEOUT_MS:10000}" # (10 seconds) # refer to https://docs.confluent.io/platform/current/installation/configuration/consumer-configs.html#consumerconfigs_session.timeout.ms + auto_offset_reset: "${TB_QUEUE_KAFKA_AUTO_OFFSET_RESET:earliest}" # earliest, latest or none + # Enable/Disable using of Confluent Cloud use_confluent_cloud: "${TB_QUEUE_KAFKA_USE_CONFLUENT_CLOUD:false}" confluent: + # The endpoint identification algorithm used by clients to validate server hostname. The default value is https ssl.algorithm: "${TB_QUEUE_KAFKA_CONFLUENT_SSL_ALGORITHM:https}" + # The mechanism used to authenticate Schema Registry requests. SASL/PLAIN should only be used with TLS/SSL as a transport layer to ensure that clear passwords are not transmitted on the wire without encryption sasl.mechanism: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_MECHANISM:PLAIN}" + # Using JAAS Configuration for specifying multiple SASL mechanisms on a broker sasl.config: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_JAAS_CONFIG:org.apache.kafka.common.security.plain.PlainLoginModule required username=\"CLUSTER_API_KEY\" password=\"CLUSTER_API_SECRET\";}" + # Protocol used to communicate with brokers. Valid values are: PLAINTEXT, SSL, SASL_PLAINTEXT, SASL_SSL security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}" other-inline: "${TB_QUEUE_KAFKA_OTHER_PROPERTIES:}" # In this section you can specify custom parameters (semicolon separated) for Kafka consumer/producer/admin # Example "metrics.recording.level:INFO;metrics.sample.window.ms:30000" other: # DEPRECATED. In this section you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside @@ -157,79 +224,139 @@ queue: # - key: "session.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/consumer-configs.html#consumerconfigs_session.timeout.ms # value: "${TB_QUEUE_KAFKA_SESSION_TIMEOUT_MS:10000}" # (10 seconds) topic-properties: + # Kafka properties for Rule Engine rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" + # Kafka properties for Core topics core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" + # Kafka properties for Transport Api topics transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}" + # Kafka properties for Notifications topics notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" consumer-stats: + # Prints lag between consumer group offset and last messages offset in Kafka topics enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}" + # Statistics printing interval for Kafka's consumer-groups stats print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}" + # Time to wait for the stats-loading requests to Kafka to finis kafka-response-timeout-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_RESPONSE_TIMEOUT_MS:1000}" aws_sqs: + # Use the default credentials provider for AWS SQS use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" + # Access key ID from AWS IAM user access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" + # Secret access key from AWS IAM user secret_access_key: "${TB_QUEUE_AWS_SQS_SECRET_ACCESS_KEY:YOUR_SECRET}" + # Region from AWS account region: "${TB_QUEUE_AWS_SQS_REGION:YOUR_REGION}" + # Number of threads per each AWS SQS queue in consumer threads_per_topic: "${TB_QUEUE_AWS_SQS_THREADS_PER_TOPIC:1}" queue-properties: + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds rule-engine: "${TB_QUEUE_AWS_SQS_RE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds core: "${TB_QUEUE_AWS_SQS_CORE_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds transport-api: "${TB_QUEUE_AWS_SQS_TA_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" + # AWS SQS queue properties. VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds notifications: "${TB_QUEUE_AWS_SQS_NOTIFICATIONS_QUEUE_PROPERTIES:VisibilityTimeout:30;MaximumMessageSize:262144;MessageRetentionPeriod:604800}" - # VisibilityTimeout in seconds;MaximumMessageSize in bytes;MessageRetentionPeriod in seconds pubsub: + # Project ID from Google Cloud project_id: "${TB_QUEUE_PUBSUB_PROJECT_ID:YOUR_PROJECT_ID}" + # API Credentials in JSON format service_account: "${TB_QUEUE_PUBSUB_SERVICE_ACCOUNT:YOUR_SERVICE_ACCOUNT}" - max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" #in bytes + # Message size for PubSub queue. Value in bytes + max_msg_size: "${TB_QUEUE_PUBSUB_MAX_MSG_SIZE:1048576}" + # Number of messages per consumer max_messages: "${TB_QUEUE_PUBSUB_MAX_MESSAGES:1000}" + # Thread pool size for pubsub queue executor provider. If not set - default pubsub executor provider value will be used (5 * number of available processors) + executor_thread_pool_size: "${TB_QUEUE_PUBSUB_EXECUTOR_THREAD_POOL_SIZE:0}" queue-properties: + # Pub/Sub properties for Rule Engine subscribers, messages which will commit after ackDeadlineInSec period can be consumed again rule-engine: "${TB_QUEUE_PUBSUB_RE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # Pub/Sub properties for Core subscribers, messages which will commit after ackDeadlineInSec period can be consumed again core: "${TB_QUEUE_PUBSUB_CORE_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # Pub/Sub properties for Transport API subscribers, messages which will commit after ackDeadlineInSec period can be consumed again transport-api: "${TB_QUEUE_PUBSUB_TA_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" + # Pub/Sub properties for Version Control subscribers, messages which will commit after ackDeadlineInSec period can be consumed again notifications: "${TB_QUEUE_PUBSUB_NOTIFICATIONS_QUEUE_PROPERTIES:ackDeadlineInSec:30;messageRetentionInSec:604800}" service_bus: + # Azure namespace namespace_name: "${TB_QUEUE_SERVICE_BUS_NAMESPACE_NAME:YOUR_NAMESPACE_NAME}" + # Azure Service Bus Shared Access Signatures key name sas_key_name: "${TB_QUEUE_SERVICE_BUS_SAS_KEY_NAME:YOUR_SAS_KEY_NAME}" + # Azure Service Bus Shared Access Signatures key sas_key: "${TB_QUEUE_SERVICE_BUS_SAS_KEY:YOUR_SAS_KEY}" + # Number of messages per a consumer max_messages: "${TB_QUEUE_SERVICE_BUS_MAX_MESSAGES:1000}" queue-properties: + # Azure Service Bus properties for Rule Engine queues rule-engine: "${TB_QUEUE_SERVICE_BUS_RE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus properties for Core queues core: "${TB_QUEUE_SERVICE_BUS_CORE_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus properties for Transport Api queues transport-api: "${TB_QUEUE_SERVICE_BUS_TA_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" + # Azure Service Bus properties for Notification queues notifications: "${TB_QUEUE_SERVICE_BUS_NOTIFICATIONS_QUEUE_PROPERTIES:lockDurationInSec:30;maxSizeInMb:1024;messageTimeToLiveInSec:604800}" rabbitmq: + # By default empty exchange_name: "${TB_QUEUE_RABBIT_MQ_EXCHANGE_NAME:}" + # RabbitMQ host used to establish connection host: "${TB_QUEUE_RABBIT_MQ_HOST:localhost}" + # RabbitMQ host used to establish a connection port: "${TB_QUEUE_RABBIT_MQ_PORT:5672}" + # Virtual hosts provide logical grouping and separation of resources virtual_host: "${TB_QUEUE_RABBIT_MQ_VIRTUAL_HOST:/}" + # Username for RabbitMQ user account username: "${TB_QUEUE_RABBIT_MQ_USERNAME:YOUR_USERNAME}" + # User password for RabbitMQ user account password: "${TB_QUEUE_RABBIT_MQ_PASSWORD:YOUR_PASSWORD}" + # Network connection between clients and RabbitMQ nodes can fail. RabbitMQ Java client supports automatic recovery of connections and topology (queues, exchanges, bindings, and consumers) automatic_recovery_enabled: "${TB_QUEUE_RABBIT_MQ_AUTOMATIC_RECOVERY_ENABLED:false}" + # The connection timeout for the RabbitMQ connection factory connection_timeout: "${TB_QUEUE_RABBIT_MQ_CONNECTION_TIMEOUT:60000}" + # RabbitMQ has a timeout for connection handshake. When clients run in heavily constrained environments, it may be necessary to increase the timeout handshake_timeout: "${TB_QUEUE_RABBIT_MQ_HANDSHAKE_TIMEOUT:10000}" queue-properties: + # RabbitMQ properties for Rule Engine queues rule-engine: "${TB_QUEUE_RABBIT_MQ_RE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ properties for Core queues core: "${TB_QUEUE_RABBIT_MQ_CORE_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ properties for Transport API queues transport-api: "${TB_QUEUE_RABBIT_MQ_TA_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" + # RabbitMQ properties for Notification queues notifications: "${TB_QUEUE_RABBIT_MQ_NOTIFICATIONS_QUEUE_PROPERTIES:x-max-length-bytes:1048576000;x-message-ttl:604800000}" partitions: - hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" + hash_function_name: "${TB_QUEUE_PARTITIONS_HASH_FUNCTION_NAME:murmur3_128}" # murmur3_32, murmur3_128 or sha256 transport_api: + # Topic used to consume api requests from transport microservices requests_topic: "${TB_QUEUE_TRANSPORT_API_REQUEST_TOPIC:tb_transport.api.requests}" + # Topic used to produce api responses to transport microservices responses_topic: "${TB_QUEUE_TRANSPORT_API_RESPONSE_TOPIC:tb_transport.api.responses}" + # Maximum pending api requests from transport microservices to be handled by server max_pending_requests: "${TB_QUEUE_TRANSPORT_MAX_PENDING_REQUESTS:10000}" + # Maximum timeout in milliseconds to handle api request from transport microservice by server max_requests_timeout: "${TB_QUEUE_TRANSPORT_MAX_REQUEST_TIMEOUT:10000}" + # Amount of threads used to invoke callbacks max_callback_threads: "${TB_QUEUE_TRANSPORT_MAX_CALLBACK_THREADS:100}" + # Interval in milliseconds to poll api requests from transport microservices request_poll_interval: "${TB_QUEUE_TRANSPORT_REQUEST_POLL_INTERVAL_MS:25}" + # Interval in milliseconds to poll api response from transport microservices response_poll_interval: "${TB_QUEUE_TRANSPORT_RESPONSE_POLL_INTERVAL_MS:25}" core: + # Default topic name of Kafka, RabbitMQ, etc. queue topic: "${TB_QUEUE_CORE_TOPIC:tb_core}" + # Interval in milliseconds to poll messages by Core microservices poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" + # Amount of partitions used by Core microservices partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" + # Timeout for processing a message pack by Core microservices pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:60000}" + # Stats topic name for queue Kafka, RabbitMQ, etc. usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}" stats: + # Enable/disable statistics for Core microservices enabled: "${TB_QUEUE_CORE_STATS_ENABLED:false}" + # Statistics printing interval for Core microservices print-interval-ms: "${TB_QUEUE_CORE_STATS_PRINT_INTERVAL_MS:10000}" js: # JS Eval request topic @@ -243,19 +370,26 @@ queue: # JS response poll interval response_poll_interval: "${REMOTE_JS_RESPONSE_POLL_INTERVAL_MS:25}" rule-engine: + # Deprecated. It will be removed in the nearest releases topic: "${TB_QUEUE_RULE_ENGINE_TOPIC:tb_rule_engine}" + # Interval in milliseconds to poll messages by Rule Engine poll-interval: "${TB_QUEUE_RULE_ENGINE_POLL_INTERVAL_MS:25}" + # Timeout for processing a message pack of Rule Engine pack-processing-timeout: "${TB_QUEUE_RULE_ENGINE_PACK_PROCESSING_TIMEOUT_MS:60000}" stats: + # Enable/disable statistics for Rule Engine enabled: "${TB_QUEUE_RULE_ENGINE_STATS_ENABLED:true}" + # Statistics printing interval for Rule Engine print-interval-ms: "${TB_QUEUE_RULE_ENGINE_STATS_PRINT_INTERVAL_MS:60000}" transport: # For high priority notifications that require minimum latency and processing time notifications_topic: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_TOPIC:tb_transport.notifications}" + # Interval in milliseconds to poll messages poll_interval: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_POLL_INTERVAL_MS:25}" +# Service common parameters service: - type: "${TB_SERVICE_TYPE:tb-transport}" + type: "${TB_SERVICE_TYPE:tb-transport}" # service type # Unique id for this service (autogenerated if empty) id: "${TB_SERVICE_ID:}" @@ -263,17 +397,28 @@ service: usage: stats: report: + # Enable/Disable the collection of statistics about API usage. Collected on a system and tenant level by default enabled: "${USAGE_STATS_REPORT_ENABLED:true}" + # Enable/Disable collection of statistics about API usage on a customer level enabled_per_customer: "${USAGE_STATS_REPORT_PER_CUSTOMER_ENABLED:false}" + # Interval of reporting the statistics. By default, the summarized statistics are sent every 10 seconds interval: "${USAGE_STATS_REPORT_INTERVAL:10}" +# Metrics parameters metrics: # Enable/disable actuator metrics. enabled: "${METRICS_ENABLED:false}" +# General management parameters management: endpoints: web: exposure: # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). include: '${METRICS_ENDPOINTS_EXPOSE:info}' + +# Notification system parameters +notification_system: + rules: + # Semicolon-separated deduplication durations (in millis) for trigger types. Format: 'NotificationRuleTriggerType1:123;NotificationRuleTriggerType2:456' + deduplication_durations: "${TB_NOTIFICATION_RULES_DEDUPLICATION_DURATIONS:RATE_LIMITS:14400000;}" diff --git a/ui-ngx/angular.json b/ui-ngx/angular.json index 86058d45ca..92981aa90f 100644 --- a/ui-ngx/angular.json +++ b/ui-ngx/angular.json @@ -82,6 +82,7 @@ ], "styles": [ "src/styles.scss", + "src/form.scss", "node_modules/jquery.terminal/css/jquery.terminal.min.css", "node_modules/tooltipster/dist/css/tooltipster.bundle.min.css", "node_modules/tooltipster/dist/css/plugins/tooltipster/sideTip/themes/tooltipster-sideTip-shadow.min.css", diff --git a/ui-ngx/package.json b/ui-ngx/package.json index 4112e24fce..ea64501e6b 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -1,11 +1,11 @@ { "name": "thingsboard", - "version": "3.5.2", + "version": "3.6.2", "scripts": { "ng": "ng", "start": "node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng serve --configuration development --host 0.0.0.0 --open", "build": "ng build", - "build:prod": "node --max_old_space_size=3072 ./node_modules/@angular/cli/bin/ng build --configuration production --vendor-chunk", + "build:prod": "node --max_old_space_size=4096 ./node_modules/@angular/cli/bin/ng build --configuration production --vendor-chunk", "build:types": "node generate-types.js", "test": "ng test", "lint": "ng lint", @@ -44,6 +44,7 @@ "@ngrx/store-devtools": "^15.4.0", "@ngx-translate/core": "^14.0.0", "@ngx-translate/http-loader": "^7.0.0", + "@svgdotjs/svg.js": "^3.2.0", "@tinymce/tinymce-angular": "^7.0.0", "ace-builds": "1.4.13", "ace-diff": "^3.0.3", @@ -53,6 +54,7 @@ "core-js": "^3.29.1", "date-fns": "2.0.0-alpha.27", "dayjs": "1.11.4", + "echarts": "^5.4.3", "flot": "https://github.com/thingsboard/flot.git#0.9-work", "flot.curvedlines": "https://github.com/MichaelZinsmaier/CurvedLines.git#master", "font-awesome": "^4.7.0", diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml index 5b5949a005..d2751b0220 100644 --- a/ui-ngx/pom.xml +++ b/ui-ngx/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.5.2-SNAPSHOT + 3.6.2-SNAPSHOT thingsboard org.thingsboard @@ -56,7 +56,7 @@ install-node-and-yarn - v16.15.1 + v16.20.2 v1.22.17 diff --git a/ui-ngx/src/app/app.component.ts b/ui-ngx/src/app/app.component.ts index 8f612da5a1..67e2fd7b42 100644 --- a/ui-ngx/src/app/app.component.ts +++ b/ui-ngx/src/app/app.component.ts @@ -30,6 +30,7 @@ import { combineLatest } from 'rxjs'; import { selectIsAuthenticated, selectIsUserLoaded } from '@core/auth/auth.selectors'; import { distinctUntilChanged, filter, map, skip } from 'rxjs/operators'; import { AuthService } from '@core/auth/auth.service'; +import { svgIcons, svgIconsUrl } from '@shared/models/icon.models'; @Component({ selector: 'tb-root', @@ -55,44 +56,18 @@ export class AppComponent implements OnInit { } }); - this.matIconRegistry.addSvgIconLiteral( - 'google-logo', - this.domSanitizer.bypassSecurityTrustHtml( - '' - ) - ); - - this.matIconRegistry.addSvgIconLiteral( - 'github-logo', - this.domSanitizer.bypassSecurityTrustHtml( - '' - ) - ); - - this.matIconRegistry.addSvgIconLiteral( - 'facebook-logo', - this.domSanitizer.bypassSecurityTrustHtml( - '' - ) - ); - - this.matIconRegistry.addSvgIconLiteral( - 'apple-logo', - this.domSanitizer.bypassSecurityTrustHtml( - '' - ) - ); - - this.matIconRegistry.addSvgIconLiteral( - 'queues-list', - this.domSanitizer.bypassSecurityTrustHtml( - '' + - '' + - '' + - '' + - '' - ) - ); + for (const svgIcon of Object.keys(svgIcons)) { + this.matIconRegistry.addSvgIconLiteral( + svgIcon, + this.domSanitizer.bypassSecurityTrustHtml( + svgIcons[svgIcon] + ) + ); + } + + for (const svgIcon of Object.keys(svgIconsUrl)) { + this.matIconRegistry.addSvgIcon(svgIcon, this.domSanitizer.bypassSecurityTrustResourceUrl(svgIconsUrl[svgIcon])); + } this.storageService.testLocalStorage(); diff --git a/ui-ngx/src/app/core/api/alarm-data.service.ts b/ui-ngx/src/app/core/api/alarm-data.service.ts index b65f69179b..283960ea8d 100644 --- a/ui-ngx/src/app/core/api/alarm-data.service.ts +++ b/ui-ngx/src/app/core/api/alarm-data.service.ts @@ -16,7 +16,7 @@ import { SubscriptionTimewindow } from '@shared/models/time/time.models'; import { Datasource, DatasourceType } from '@shared/models/widget.models'; -import { PageData } from '@shared/models/page/page-data'; +import { emptyPageData, PageData } from '@shared/models/page/page-data'; import { AlarmData, AlarmDataPageLink, KeyFilter } from '@shared/models/query/query.models'; import { Injectable } from '@angular/core'; import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service'; @@ -26,6 +26,7 @@ import { AlarmSubscriptionDataKey } from '@core/api/alarm-data-subscription'; import { deepClone } from '@core/utils'; +import { of } from 'rxjs'; export interface AlarmDataListener { subscriptionTimewindow?: SubscriptionTimewindow; @@ -50,6 +51,7 @@ export class AlarmDataService { const alarmSource = listener.alarmSource; listener.alarmDataSubscriptionOptions = this.createAlarmSubscriptionOptions(listener, pageLink, keyFilters); if (alarmSource.type === DatasourceType.entity && (!alarmSource.entityFilter || !pageLink)) { + listener.alarmsLoaded(emptyPageData(), 0, 0); return; } listener.subscription = new AlarmDataSubscription(listener, this.telemetryService); diff --git a/ui-ngx/src/app/core/api/alias-controller.ts b/ui-ngx/src/app/core/api/alias-controller.ts index be08d50d69..971f238d83 100644 --- a/ui-ngx/src/app/core/api/alias-controller.ts +++ b/ui-ngx/src/app/core/api/alias-controller.ts @@ -252,10 +252,9 @@ export class AliasController implements IAliasController { private resolveDatasource(datasource: Datasource, forceFilter = false): Observable { const newDatasource = deepClone(datasource); - if (newDatasource.type === DatasourceType.device) { - newDatasource.type = DatasourceType.entity; - } - if (newDatasource.type === DatasourceType.entity || newDatasource.type === DatasourceType.entityCount + if (newDatasource.type === DatasourceType.entity + || newDatasource.type === DatasourceType.device + || newDatasource.type === DatasourceType.entityCount || newDatasource.type === DatasourceType.alarmCount) { if (newDatasource.filterId) { newDatasource.keyFilters = this.getKeyFilters(newDatasource.filterId); @@ -263,7 +262,8 @@ export class AliasController implements IAliasController { if (newDatasource.type === DatasourceType.alarmCount) { newDatasource.alarmFilter = this.entityService.resolveAlarmFilter(newDatasource.alarmFilterConfig, false); } - if (newDatasource.deviceId) { + if (newDatasource.type === DatasourceType.device) { + newDatasource.type = DatasourceType.entity; newDatasource.entityFilter = singleEntityFilterFromDeviceId(newDatasource.deviceId); if (forceFilter) { return this.entityService.findSingleEntityInfoByEntityFilter(newDatasource.entityFilter, diff --git a/ui-ngx/src/app/core/api/entity-data-subscription.ts b/ui-ngx/src/app/core/api/entity-data-subscription.ts index 25cb47ca0a..1e0f31c4d0 100644 --- a/ui-ngx/src/app/core/api/entity-data-subscription.ts +++ b/ui-ngx/src/app/core/api/entity-data-subscription.ts @@ -42,6 +42,7 @@ import { EntityCountCmd, EntityDataCmd, IndexedSubscriptionData, + NOT_SUPPORTED, SubscriptionData, TelemetrySubscriber } from '@shared/models/telemetry/telemetry.models'; @@ -786,7 +787,7 @@ export class EntityDataSubscription { private reportNotSupported(keys: AggKey[], isUpdate: boolean) { const indexedData: IndexedSubscriptionData = []; for (const key of keys) { - indexedData[key.id] = [[0, 'Not supported!']]; + indexedData[key.id] = [[0, NOT_SUPPORTED]]; } for (let dataIndex = 0; dataIndex < this.pageData.data.length; dataIndex++) { this.onIndexedData(indexedData, dataIndex, true, diff --git a/ui-ngx/src/app/core/api/widget-api.models.ts b/ui-ngx/src/app/core/api/widget-api.models.ts index 115bf6e222..5cfc83d8d9 100644 --- a/ui-ngx/src/app/core/api/widget-api.models.ts +++ b/ui-ngx/src/app/core/api/widget-api.models.ts @@ -89,6 +89,7 @@ export interface WidgetActionsApi { handleWidgetAction: ($event: Event, descriptor: WidgetActionDescriptor, entityId?: EntityId, entityName?: string, additionalParams?: any, entityLabel?: string) => void; elementClick: ($event: Event) => void; + cardClick: ($event: Event) => void; getActiveEntityInfo: () => SubscriptionEntityInfo; openDashboardStateInSeparateDialog: (targetDashboardStateId: string, params?: StateParams, dialogTitle?: string, hideDashboardToolbar?: boolean, dialogWidth?: number, dialogHeight?: number) => void; @@ -284,6 +285,8 @@ export interface IWidgetSubscription { legendData: LegendData; + readonly firstDatasource?: Datasource; + datasourcePages?: PageData[]; dataPages?: PageData>[]; datasources?: Array; diff --git a/ui-ngx/src/app/core/api/widget-subscription.ts b/ui-ngx/src/app/core/api/widget-subscription.ts index 440e61148e..0381aae63b 100644 --- a/ui-ngx/src/app/core/api/widget-subscription.ts +++ b/ui-ngx/src/app/core/api/widget-subscription.ts @@ -81,6 +81,7 @@ import { distinct, filter, map, switchMap, takeUntil } from 'rxjs/operators'; import { AlarmDataListener } from '@core/api/alarm-data.service'; import { RpcStatus } from '@shared/models/rpc.models'; import { EventEmitter } from '@angular/core'; +import { NOT_SUPPORTED } from '@shared/models/telemetry/telemetry.models'; const moment = moment_; @@ -160,6 +161,16 @@ export class WidgetSubscription implements IWidgetSubscription { warnOnPageDataOverflow: boolean; ignoreDataUpdateOnIntervalTick: boolean; + get firstDatasource(): Datasource { + if (this.type === widgetType.alarm) { + return this.alarmSource; + } else if (this.datasources?.length) { + return this.datasources[0]; + } else { + return null; + } + } + datasourcePages: PageData[]; dataPages: PageData>[]; entityDataListeners: Array; @@ -1526,12 +1537,12 @@ export class WidgetSubscription implements IWidgetSubscription { } if (this.type === widgetType.latest) { const prevData = currentData.data; - if (!data.data.length) { + if (!data.data.length && !prevData.length) { update = false; } else if (prevData && prevData[0] && prevData[0].length > 1 && data.data.length > 0) { const prevTs = prevData[0][0]; const prevValue = prevData[0][1]; - if (prevTs === data.data[0][0] && prevValue === data.data[0][1]) { + if (prevTs === data.data[0][0] && prevValue === data.data[0][1] && data.data[0][1] !== NOT_SUPPORTED) { update = false; } } @@ -1549,6 +1560,8 @@ export class WidgetSubscription implements IWidgetSubscription { } this.notifyDataLoaded(); this.onDataUpdated(detectChanges); + } else if (this.loadingData) { + this.notifyDataLoaded(); } } diff --git a/ui-ngx/src/app/core/auth/auth.actions.ts b/ui-ngx/src/app/core/auth/auth.actions.ts index 67b122d67f..a6bf97c527 100644 --- a/ui-ngx/src/app/core/auth/auth.actions.ts +++ b/ui-ngx/src/app/core/auth/auth.actions.ts @@ -15,17 +15,21 @@ /// import { Action } from '@ngrx/store'; -import { User } from '@shared/models/user.model'; +import { AuthUser, User } from '@shared/models/user.model'; import { AuthPayload } from '@core/auth/auth.models'; +import { UserSettings } from '@shared/models/user-settings.models'; export enum AuthActionTypes { AUTHENTICATED = '[Auth] Authenticated', UNAUTHENTICATED = '[Auth] Unauthenticated', LOAD_USER = '[Auth] Load User', UPDATE_USER_DETAILS = '[Auth] Update User Details', + UPDATE_AUTH_USER = '[Auth] Update Auth User', UPDATE_LAST_PUBLIC_DASHBOARD_ID = '[Auth] Update Last Public Dashboard Id', UPDATE_HAS_REPOSITORY = '[Auth] Change Has Repository', - UPDATE_OPENED_MENU_SECTION = '[Preferences] Update Opened Menu Section' + UPDATE_OPENED_MENU_SECTION = '[Preferences] Update Opened Menu Section', + PUT_USER_SETTINGS = '[Preferences] Put user settings', + DELETE_USER_SETTINGS = '[Preferences] Delete user settings', } export class ActionAuthAuthenticated implements Action { @@ -50,6 +54,12 @@ export class ActionAuthUpdateUserDetails implements Action { constructor(readonly payload: { userDetails: User }) {} } +export class ActionAuthUpdateAuthUser implements Action { + readonly type = AuthActionTypes.UPDATE_AUTH_USER; + + constructor(readonly payload: Partial) {} +} + export class ActionAuthUpdateLastPublicDashboardId implements Action { readonly type = AuthActionTypes.UPDATE_LAST_PUBLIC_DASHBOARD_ID; @@ -68,6 +78,19 @@ export class ActionPreferencesUpdateOpenedMenuSection implements Action { constructor(readonly payload: { path: string; opened: boolean }) {} } +export class ActionPreferencesPutUserSettings implements Action { + readonly type = AuthActionTypes.PUT_USER_SETTINGS; + + constructor(readonly payload: Partial) {} +} + +export class ActionPreferencesDeleteUserSettings implements Action { + readonly type = AuthActionTypes.DELETE_USER_SETTINGS; + + constructor(readonly payload: Array>) {} +} + export type AuthActions = ActionAuthAuthenticated | ActionAuthUnauthenticated | ActionAuthLoadUser | ActionAuthUpdateUserDetails | ActionAuthUpdateLastPublicDashboardId | ActionAuthUpdateHasRepository | - ActionPreferencesUpdateOpenedMenuSection; + ActionPreferencesUpdateOpenedMenuSection | ActionPreferencesPutUserSettings | ActionPreferencesDeleteUserSettings | + ActionAuthUpdateAuthUser; diff --git a/ui-ngx/src/app/core/auth/auth.effects.ts b/ui-ngx/src/app/core/auth/auth.effects.ts index d2ebe2b4c5..3e5eb28d72 100644 --- a/ui-ngx/src/app/core/auth/auth.effects.ts +++ b/ui-ngx/src/app/core/auth/auth.effects.ts @@ -39,4 +39,18 @@ export class AuthEffects { withLatestFrom(this.store.pipe(select(selectAuthState))), mergeMap(([action, state]) => this.userSettingsService.putUserSettings({ openedMenuSections: state.userSettings.openedMenuSections })) ), {dispatch: false}); + + putUserSettings = createEffect(() => this.actions$.pipe( + ofType( + AuthActionTypes.PUT_USER_SETTINGS, + ), + mergeMap((state) => this.userSettingsService.putUserSettings(state.payload)) + ), {dispatch: false}); + + deleteUserSettings = createEffect(() => this.actions$.pipe( + ofType( + AuthActionTypes.DELETE_USER_SETTINGS, + ), + mergeMap((state) => this.userSettingsService.deleteUserSettings(state.payload)) + ), {dispatch: false}); } diff --git a/ui-ngx/src/app/core/auth/auth.models.ts b/ui-ngx/src/app/core/auth/auth.models.ts index 607a2cb78d..79214f8210 100644 --- a/ui-ngx/src/app/core/auth/auth.models.ts +++ b/ui-ngx/src/app/core/auth/auth.models.ts @@ -25,6 +25,7 @@ export interface SysParamsState { tbelEnabled: boolean; persistDeviceStateToTelemetry: boolean; userSettings: UserSettings; + maxResourceSize: number; } export interface SysParams extends SysParamsState { diff --git a/ui-ngx/src/app/core/auth/auth.reducer.ts b/ui-ngx/src/app/core/auth/auth.reducer.ts index 0847654399..6e51e4ca62 100644 --- a/ui-ngx/src/app/core/auth/auth.reducer.ts +++ b/ui-ngx/src/app/core/auth/auth.reducer.ts @@ -16,7 +16,8 @@ import { AuthPayload, AuthState } from './auth.models'; import { AuthActions, AuthActionTypes } from './auth.actions'; -import { initialUserSettings } from '@shared/models/user-settings.models'; +import { initialUserSettings, UserSettings } from '@shared/models/user-settings.models'; +import { unset } from '@core/utils'; const emptyUserAuthState: AuthPayload = { authUser: null, @@ -28,6 +29,7 @@ const emptyUserAuthState: AuthPayload = { hasRepository: false, tbelEnabled: false, persistDeviceStateToTelemetry: false, + maxResourceSize: 0, userSettings: initialUserSettings }; @@ -42,6 +44,7 @@ export const authReducer = ( state: AuthState = initialState, action: AuthActions ): AuthState => { + let userSettings: UserSettings; switch (action.type) { case AuthActionTypes.AUTHENTICATED: return { ...state, isAuthenticated: true, ...action.payload }; @@ -56,6 +59,10 @@ export const authReducer = ( case AuthActionTypes.UPDATE_USER_DETAILS: return { ...state, ...action.payload}; + case AuthActionTypes.UPDATE_AUTH_USER: + const authUser = {...state.authUser, ...action.payload}; + return { ...state, ...{ authUser }}; + case AuthActionTypes.UPDATE_LAST_PUBLIC_DASHBOARD_ID: return { ...state, ...action.payload}; @@ -71,7 +78,16 @@ export const authReducer = ( } else { openedMenuSections.delete(action.payload.path); } - const userSettings = {...state.userSettings, ...{ openedMenuSections: Array.from(openedMenuSections)}}; + userSettings = {...state.userSettings, ...{ openedMenuSections: Array.from(openedMenuSections)}}; + return { ...state, ...{ userSettings }}; + + case AuthActionTypes.PUT_USER_SETTINGS: + userSettings = {...state.userSettings, ...action.payload}; + return { ...state, ...{ userSettings }}; + + case AuthActionTypes.DELETE_USER_SETTINGS: + userSettings = {...state.userSettings}; + action.payload.forEach(path => unset(userSettings, path)); return { ...state, ...{ userSettings }}; default: diff --git a/ui-ngx/src/app/core/auth/auth.selectors.ts b/ui-ngx/src/app/core/auth/auth.selectors.ts index 2e8406293e..4bf2f2276d 100644 --- a/ui-ngx/src/app/core/auth/auth.selectors.ts +++ b/ui-ngx/src/app/core/auth/auth.selectors.ts @@ -21,6 +21,7 @@ import { AuthState } from './auth.models'; import { take } from 'rxjs/operators'; import { AuthUser } from '@shared/models/user.model'; import { UserSettings } from '@shared/models/user-settings.models'; +import { getDescendantProp } from '@core/utils'; export const selectAuthState = createFeatureSelector< AuthState>( 'auth' @@ -76,6 +77,11 @@ export const selectUserSettings = createSelector( (state: AuthState) => state.userSettings ); +export const selectUserSettingsProperty = (path: NestedKeyOf) => createSelector( + selectAuthState, + (state: AuthState) => getDescendantProp(state.userSettings, path) +); + export const selectOpenedMenuSections = createSelector( selectAuthState, (state: AuthState) => state.userSettings.openedMenuSections diff --git a/ui-ngx/src/app/core/auth/auth.service.ts b/ui-ngx/src/app/core/auth/auth.service.ts index 87c6561315..875cef30d1 100644 --- a/ui-ngx/src/app/core/auth/auth.service.ts +++ b/ui-ngx/src/app/core/auth/auth.service.ts @@ -27,7 +27,12 @@ import { defaultHttpOptions, defaultHttpOptionsFromConfig, RequestConfig } from import { UserService } from '../http/user.service'; import { Store } from '@ngrx/store'; import { AppState } from '../core.state'; -import { ActionAuthAuthenticated, ActionAuthLoadUser, ActionAuthUnauthenticated } from './auth.actions'; +import { + ActionAuthAuthenticated, + ActionAuthLoadUser, + ActionAuthUnauthenticated, + ActionAuthUpdateAuthUser +} from './auth.actions'; import { getCurrentAuthState, getCurrentAuthUser } from './auth.selectors'; import { Authority } from '@shared/models/authority.enum'; import { ActionSettingsChangeLanguage } from '@app/core/settings/settings.actions'; @@ -244,7 +249,7 @@ export class AuthService { if (authState && authState.authUser) { if (authState.authUser.authority === Authority.TENANT_ADMIN || authState.authUser.authority === Authority.CUSTOMER_USER) { if ((this.userHasDefaultDashboard(authState) && authState.forceFullscreen) || authState.authUser.isPublic) { - if (path === 'profile' || path === 'security') { + if (path.startsWith('account')) { if (this.userHasProfile(authState.authUser)) { return false; } else { @@ -365,7 +370,8 @@ export class AuthService { data: { title: translations['login.error'], message: loginError, - ok: translations['action.close'] + ok: translations['action.close'], + textMode: true } }; this.dialog.open(AlertDialogComponent, dialogConfig); @@ -385,10 +391,10 @@ export class AuthService { } else if (authPayload.authUser) { authPayload.authUser.authority = Authority.ANONYMOUS; } - if (authPayload.authUser.isPublic) { + if (authPayload.authUser?.isPublic) { authPayload.forceFullscreen = true; } - if (authPayload.authUser.isPublic) { + if (authPayload.authUser?.isPublic) { this.loadSystemParams().subscribe( (sysParams) => { authPayload = {...authPayload, ...sysParams}; @@ -399,10 +405,10 @@ export class AuthService { loadUserSubject.error(err); } ); - } else if (authPayload.authUser.authority === Authority.PRE_VERIFICATION_TOKEN) { + } else if (authPayload.authUser?.authority === Authority.PRE_VERIFICATION_TOKEN) { loadUserSubject.next(authPayload); loadUserSubject.complete(); - } else if (authPayload.authUser.userId) { + } else if (authPayload.authUser?.userId) { this.userService.getUser(authPayload.authUser.userId).subscribe( (user) => { authPayload.userDetails = user; @@ -419,8 +425,8 @@ export class AuthService { } else { userLang = null; } - this.notifyUserLang(userLang); loadUserSubject.next(authPayload); + this.notifyUserLang(userLang); loadUserSubject.complete(); }, (err) => { @@ -480,6 +486,7 @@ export class AuthService { } else { this.updateAndValidateTokens(loginResponse.token, loginResponse.refreshToken, true); } + this.updatedAuthUserFromToken(loginResponse.token); this.refreshTokenSubject.next(loginResponse); this.refreshTokenSubject.complete(); this.refreshTokenSubject = null; @@ -493,6 +500,18 @@ export class AuthService { return response; } + private updatedAuthUserFromToken(token: string) { + const authUser = getCurrentAuthUser(this.store); + const tokenData = this.jwtHelper.decodeToken(token); + if (authUser && tokenData && ['sub', 'firstName', 'lastName'].some(value => authUser[value] !== tokenData[value])) { + this.store.dispatch(new ActionAuthUpdateAuthUser({ + sub: tokenData.sub, + firstName: tokenData.firstName, + lastName: tokenData.lastName, + })); + } + } + private validateJwtToken(doRefresh): Observable { const subject = new ReplaySubject(); if (!AuthService.isTokenValid('jwt_token')) { diff --git a/ui-ngx/src/app/core/core.module.ts b/ui-ngx/src/app/core/core.module.ts index 837d530fbf..bc3d4874a4 100644 --- a/ui-ngx/src/app/core/core.module.ts +++ b/ui-ngx/src/app/core/core.module.ts @@ -34,7 +34,7 @@ import { import { TranslateHttpLoader } from '@ngx-translate/http-loader'; import { TbMissingTranslationHandler } from './translate/missing-translate-handler'; import { MatButtonModule } from '@angular/material/button'; -import { MatDialogModule } from '@angular/material/dialog'; +import { MAT_DIALOG_DEFAULT_OPTIONS, MatDialogConfig, MatDialogModule } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { FlexLayoutModule } from '@angular/flex-layout'; import { TranslateDefaultCompiler } from '@core/translate/translate-default-compiler'; @@ -100,6 +100,13 @@ export function HttpLoaderFactory(http: HttpClient) { useClass: GlobalHttpInterceptor, multi: true }, + { + provide: MAT_DIALOG_DEFAULT_OPTIONS, + useValue: { + ...new MatDialogConfig(), + restoreFocus: false + } + }, WINDOW_PROVIDERS ], exports: [] diff --git a/ui-ngx/src/app/core/guards/confirm-on-exit.guard.ts b/ui-ngx/src/app/core/guards/confirm-on-exit.guard.ts index f3a72e7aae..e28578fe61 100644 --- a/ui-ngx/src/app/core/guards/confirm-on-exit.guard.ts +++ b/ui-ngx/src/app/core/guards/confirm-on-exit.guard.ts @@ -28,10 +28,12 @@ import { isDefined } from '../utils'; export interface HasConfirmForm { confirmForm(): UntypedFormGroup; + confirmOnExitMessage?: string; } export interface HasDirtyFlag { isDirty: boolean; + confirmOnExitMessage?: string; } @Injectable({ @@ -66,9 +68,10 @@ export class ConfirmOnExitGuard implements CanDeactivate { if (dialogResult) { @@ -85,4 +88,10 @@ export class ConfirmOnExitGuard implements CanDeactivate { @@ -52,12 +52,12 @@ export class AlarmService { return this.http.post('/api/alarm', alarm, defaultHttpOptionsFromConfig(config)); } - public ackAlarm(alarmId: string, config?: RequestConfig): Observable { - return this.http.post(`/api/alarm/${alarmId}/ack`, null, defaultHttpOptionsFromConfig(config)); + public ackAlarm(alarmId: string, config?: RequestConfig): Observable { + return this.http.post(`/api/alarm/${alarmId}/ack`, null, defaultHttpOptionsFromConfig(config)); } - public clearAlarm(alarmId: string, config?: RequestConfig): Observable { - return this.http.post(`/api/alarm/${alarmId}/clear`, null, defaultHttpOptionsFromConfig(config)); + public clearAlarm(alarmId: string, config?: RequestConfig): Observable { + return this.http.post(`/api/alarm/${alarmId}/clear`, null, defaultHttpOptionsFromConfig(config)); } public assignAlarm(alarmId: string, assigneeId: string, config?: RequestConfig): Observable { @@ -68,8 +68,8 @@ export class AlarmService { return this.http.delete(`/api/alarm/${alarmId}/assign`, defaultHttpOptionsFromConfig(config)); } - public deleteAlarm(alarmId: string, config?: RequestConfig): Observable { - return this.http.delete(`/api/alarm/${alarmId}`, defaultHttpOptionsFromConfig(config)); + public deleteAlarm(alarmId: string, config?: RequestConfig): Observable { + return this.http.delete(`/api/alarm/${alarmId}`, defaultHttpOptionsFromConfig(config)); } public getAlarms(query: AlarmQuery, @@ -108,4 +108,8 @@ export class AlarmService { defaultHttpOptionsFromConfig(config)); } + public getAlarmTypes(pageLink: PageLink, config?: RequestConfig): Observable> { + return this.http.get>(`/api/alarm/types${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); + } + } diff --git a/ui-ngx/src/app/core/http/asset-profile.service.ts b/ui-ngx/src/app/core/http/asset-profile.service.ts index 0434f5f960..029e1afe5c 100644 --- a/ui-ngx/src/app/core/http/asset-profile.service.ts +++ b/ui-ngx/src/app/core/http/asset-profile.service.ts @@ -40,6 +40,10 @@ export class AssetProfileService { return this.http.get(`/api/assetProfile/${assetProfileId}`, defaultHttpOptionsFromConfig(config)); } + public exportAssetProfile(assetProfileId: string, config?: RequestConfig): Observable { + return this.http.get(`/api/assetProfile/${assetProfileId}?inlineImages=true`, defaultHttpOptionsFromConfig(config)); + } + public saveAssetProfile(assetProfile: AssetProfile, config?: RequestConfig): Observable { return this.http.post('/api/assetProfile', assetProfile, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/core/http/asset.service.ts b/ui-ngx/src/app/core/http/asset.service.ts index 6751e0e38f..3c45a9e95b 100644 --- a/ui-ngx/src/app/core/http/asset.service.ts +++ b/ui-ngx/src/app/core/http/asset.service.ts @@ -20,9 +20,9 @@ import { Observable } from 'rxjs'; import { HttpClient } from '@angular/common/http'; import { PageLink } from '@shared/models/page/page-link'; import { PageData } from '@shared/models/page/page-data'; -import { EntitySubtype } from '@app/shared/models/entity-type.models'; -import { Asset, AssetInfo, AssetSearchQuery } from '@app/shared/models/asset.models'; -import { BulkImportRequest, BulkImportResult } from '@home/components/import-export/import-export.models'; +import { EntitySubtype } from '@shared/models/entity-type.models'; +import { Asset, AssetInfo, AssetSearchQuery } from '@shared/models/asset.models'; +import { BulkImportRequest, BulkImportResult } from '@shared/import-export/import-export.models'; @Injectable({ providedIn: 'root' diff --git a/ui-ngx/src/app/core/http/attribute.service.ts b/ui-ngx/src/app/core/http/attribute.service.ts index 67132d8983..3ca29e425e 100644 --- a/ui-ngx/src/app/core/http/attribute.service.ts +++ b/ui-ngx/src/app/core/http/attribute.service.ts @@ -32,12 +32,18 @@ export class AttributeService { private http: HttpClient ) { } - public getEntityAttributes(entityId: EntityId, attributeScope: AttributeScope, + public getEntityAttributes(entityId: EntityId, attributeScope?: AttributeScope, keys?: Array, config?: RequestConfig): Observable> { - let url = `/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/values/attributes/${attributeScope}`; + let url = `/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/values/attributes`; + + if (attributeScope) { + url += `/${attributeScope}`; + } + if (keys && keys.length) { url += `?keys=${keys.join(',')}`; } + return this.http.get>(url, defaultHttpOptionsFromConfig(config)); } @@ -50,10 +56,19 @@ export class AttributeService { } public deleteEntityTimeseries(entityId: EntityId, timeseries: Array, deleteAllDataForKeys = false, - startTs?: number, endTs?: number, config?: RequestConfig): Observable { + startTs?: number, endTs?: number, rewriteLatestIfDeleted = false, deleteLatest = true, + config?: RequestConfig): Observable { const keys = timeseries.map(attribute => encodeURIComponent(attribute.key)).join(','); - let url = `/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/timeseries/delete` + - `?keys=${keys}&deleteAllDataForKeys=${deleteAllDataForKeys}`; + let url = `/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/timeseries/delete?keys=${keys}`; + if (isDefinedAndNotNull(deleteAllDataForKeys)) { + url += `&deleteAllDataForKeys=${deleteAllDataForKeys}`; + } + if (isDefinedAndNotNull(rewriteLatestIfDeleted)) { + url += `&rewriteLatestIfDeleted=${rewriteLatestIfDeleted}`; + } + if (isDefinedAndNotNull(deleteLatest)) { + url += `&deleteLatest=${deleteLatest}`; + } if (isDefinedAndNotNull(startTs)) { url += `&startTs=${startTs}`; } @@ -103,7 +118,8 @@ export class AttributeService { }); let deleteEntityTimeseriesObservable: Observable; if (deleteTimeseries.length) { - deleteEntityTimeseriesObservable = this.deleteEntityTimeseries(entityId, deleteTimeseries, true, null, null, config); + deleteEntityTimeseriesObservable = this.deleteEntityTimeseries(entityId, deleteTimeseries, true, + null, null, false, true, config); } else { deleteEntityTimeseriesObservable = of(null); } diff --git a/ui-ngx/src/app/core/http/dashboard.service.ts b/ui-ngx/src/app/core/http/dashboard.service.ts index 706967b70d..fc15e7aa74 100644 --- a/ui-ngx/src/app/core/http/dashboard.service.ts +++ b/ui-ngx/src/app/core/http/dashboard.service.ts @@ -71,6 +71,10 @@ export class DashboardService { return this.http.get(`/api/dashboard/${dashboardId}`, defaultHttpOptionsFromConfig(config)); } + public exportDashboard(dashboardId: string, config?: RequestConfig): Observable { + return this.http.get(`/api/dashboard/${dashboardId}?inlineImages=true`, defaultHttpOptionsFromConfig(config)); + } + public getDashboardInfo(dashboardId: string, config?: RequestConfig): Observable { return this.http.get(`/api/dashboard/info/${dashboardId}`, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/core/http/device-profile.service.ts b/ui-ngx/src/app/core/http/device-profile.service.ts index 047374783e..fd7753a6ab 100644 --- a/ui-ngx/src/app/core/http/device-profile.service.ts +++ b/ui-ngx/src/app/core/http/device-profile.service.ts @@ -53,6 +53,10 @@ export class DeviceProfileService { return this.http.get(`/api/deviceProfile/${deviceProfileId}`, defaultHttpOptionsFromConfig(config)); } + public exportDeviceProfile(deviceProfileId: string, config?: RequestConfig): Observable { + return this.http.get(`/api/deviceProfile/${deviceProfileId}?inlineImages=true`, defaultHttpOptionsFromConfig(config)); + } + public getLwm2mObjects(sortOrder: SortOrder, objectIds?: string[], searchText?: string, config?: RequestConfig): Observable> { let url = `/api/resource/lwm2m/?sortProperty=${sortOrder.property}&sortOrder=${sortOrder.direction}`; diff --git a/ui-ngx/src/app/core/http/device.service.ts b/ui-ngx/src/app/core/http/device.service.ts index 018e202e81..55c1dd5b57 100644 --- a/ui-ngx/src/app/core/http/device.service.ts +++ b/ui-ngx/src/app/core/http/device.service.ts @@ -25,13 +25,16 @@ import { ClaimResult, Device, DeviceCredentials, - DeviceInfo, DeviceInfoQuery, - DeviceSearchQuery -} from '@app/shared/models/device.models'; -import { EntitySubtype } from '@app/shared/models/entity-type.models'; + DeviceInfo, + DeviceInfoQuery, + DeviceSearchQuery, + PublishTelemetryCommand +} from '@shared/models/device.models'; +import { EntitySubtype } from '@shared/models/entity-type.models'; import { AuthService } from '@core/auth/auth.service'; -import { BulkImportRequest, BulkImportResult } from '@home/components/import-export/import-export.models'; +import { BulkImportRequest, BulkImportResult } from '@shared/import-export/import-export.models'; import { PersistentRpc, RpcStatus } from '@shared/models/rpc.models'; +import { ResourcesService } from '@core/services/resources.service'; @Injectable({ providedIn: 'root' @@ -39,7 +42,8 @@ import { PersistentRpc, RpcStatus } from '@shared/models/rpc.models'; export class DeviceService { constructor( - private http: HttpClient + private http: HttpClient, + private resourcesService: ResourcesService ) { } public getDeviceInfosByQuery(deviceInfoQuery: DeviceInfoQuery, config?: RequestConfig): Observable> { @@ -87,6 +91,13 @@ export class DeviceService { return this.http.post('/api/device', device, defaultHttpOptionsFromConfig(config)); } + public saveDeviceWithCredentials(device: Device, credentials: DeviceCredentials, config?: RequestConfig): Observable { + return this.http.post('/api/device-with-credentials', { + device, + credentials + }, defaultHttpOptionsFromConfig(config)); + } + public deleteDevice(deviceId: string, config?: RequestConfig) { return this.http.delete(`/api/device/${deviceId}`, defaultHttpOptionsFromConfig(config)); } @@ -201,4 +212,11 @@ export class DeviceService { return this.http.post('/api/device/bulk_import', entitiesData, defaultHttpOptionsFromConfig(config)); } + public getDevicePublishTelemetryCommands(deviceId: string, config?: RequestConfig): Observable { + return this.http.get(`/api/device-connectivity/${deviceId}`, defaultHttpOptionsFromConfig(config)); + } + + public downloadGatewayDockerComposeFile(deviceId: string): Observable { + return this.resourcesService.downloadResource(`/api/device-connectivity/gateway-launch/${deviceId}/docker-compose/download`); + } } diff --git a/ui-ngx/src/app/core/http/edge.service.ts b/ui-ngx/src/app/core/http/edge.service.ts index 078ada56aa..d3050dfab6 100644 --- a/ui-ngx/src/app/core/http/edge.service.ts +++ b/ui-ngx/src/app/core/http/edge.service.ts @@ -21,9 +21,9 @@ import { HttpClient } from '@angular/common/http'; import { PageLink, TimePageLink } from '@shared/models/page/page-link'; import { PageData } from '@shared/models/page/page-data'; import { EntitySubtype } from '@app/shared/models/entity-type.models'; -import { Edge, EdgeEvent, EdgeInfo, EdgeInstallInstructions, EdgeSearchQuery } from '@shared/models/edge.models'; +import { Edge, EdgeEvent, EdgeInfo, EdgeInstructions, EdgeSearchQuery } from '@shared/models/edge.models'; import { EntityId } from '@shared/models/id/entity-id'; -import { BulkImportRequest, BulkImportResult } from '@home/components/import-export/import-export.models'; +import { BulkImportRequest, BulkImportResult } from '@shared/import-export/import-export.models'; @Injectable({ providedIn: 'root' @@ -114,7 +114,15 @@ export class EdgeService { return this.http.post('/api/edge/bulk_import', entitiesData, defaultHttpOptionsFromConfig(config)); } - public getEdgeDockerInstallInstructions(edgeId: string, config?: RequestConfig): Observable { - return this.http.get(`/api/edge/instructions/${edgeId}`, defaultHttpOptionsFromConfig(config)); + public getEdgeInstallInstructions(edgeId: string, method: string = 'ubuntu', config?: RequestConfig): Observable { + return this.http.get(`/api/edge/instructions/install/${edgeId}/${method}`, defaultHttpOptionsFromConfig(config)); + } + + public getEdgeUpgradeInstructions(edgeVersion: string, method: string = 'ubuntu', config?: RequestConfig): Observable { + return this.http.get(`/api/edge/instructions/upgrade/${edgeVersion}/${method}`, defaultHttpOptionsFromConfig(config)); + } + + public isEdgeUpgradeAvailable(edgeId: string, config?: RequestConfig): Observable { + return this.http.get(`/api/edge/${edgeId}/upgrade/available`, defaultHttpOptionsFromConfig(config)); } } diff --git a/ui-ngx/src/app/core/http/entity.service.ts b/ui-ngx/src/app/core/http/entity.service.ts index 12f6300b93..968329d36a 100644 --- a/ui-ngx/src/app/core/http/entity.service.ts +++ b/ui-ngx/src/app/core/http/entity.service.ts @@ -41,7 +41,7 @@ import { AttributeScope, DataKeyType } from '@shared/models/telemetry/telemetry. import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils'; import { RuleChainService } from '@core/http/rule-chain.service'; import { AliasInfo, StateParams, SubscriptionInfo } from '@core/api/widget-api.models'; -import { DataKey, Datasource, DatasourceType, KeyInfo } from '@app/shared/models/widget.models'; +import { DataKey, Datasource, DatasourceType, DeprecatedFilter, KeyInfo } from '@app/shared/models/widget.models'; import { UtilsService } from '@core/services/utils.service'; import { AliasFilterType, @@ -65,7 +65,9 @@ import { Device, DeviceCredentialsType } from '@shared/models/device.models'; import { AttributeService } from '@core/http/attribute.service'; import { AlarmData, - AlarmDataQuery, AlarmFilter, AlarmFilterConfig, + AlarmDataQuery, + AlarmFilter, + AlarmFilterConfig, createDefaultEntityDataPageLink, EntityData, EntityDataQuery, @@ -92,6 +94,8 @@ import { NotificationService } from '@core/http/notification.service'; import { TenantProfileService } from '@core/http/tenant-profile.service'; import { NotificationType } from '@shared/models/notification.models'; import { UserId } from '@shared/models/id/user-id'; +import { AlarmService } from '@core/http/alarm.service'; +import { ResourceService } from '@core/http/resource.service'; @Injectable({ providedIn: 'root' @@ -119,7 +123,9 @@ export class EntityService { private assetProfileService: AssetProfileService, private utils: UtilsService, private queueService: QueueService, - private notificationService: NotificationService + private notificationService: NotificationService, + private alarmService: AlarmService, + private resourceService: ResourceService ) { } private getEntityObservable(entityType: EntityType, entityId: string, @@ -155,7 +161,7 @@ export class EntityService { observable = this.ruleChainService.getRuleChain(entityId, config); break; case EntityType.ALARM: - console.error('Get Alarm Entity is not implemented!'); + observable = this.alarmService.getAlarm(entityId, config); break; case EntityType.OTA_PACKAGE: observable = this.otaPackageService.getOtaPackageInfo(entityId, config); @@ -416,7 +422,11 @@ export class EntityService { break; case EntityType.WIDGETS_BUNDLE: pageLink.sortOrder.property = 'title'; - entitiesObservable = this.widgetService.getWidgetBundles(pageLink, config); + entitiesObservable = this.widgetService.getWidgetBundles(pageLink, false, true, config); + break; + case EntityType.WIDGET_TYPE: + pageLink.sortOrder.property = 'name'; + entitiesObservable = this.widgetService.getWidgetTypes(pageLink, true, false, DeprecatedFilter.ALL, null, config); break; case EntityType.NOTIFICATION_TARGET: pageLink.sortOrder.property = 'name'; @@ -430,6 +440,10 @@ export class EntityService { pageLink.sortOrder.property = 'name'; entitiesObservable = this.notificationService.getNotificationRules(pageLink, config); break; + case EntityType.TB_RESOURCE: + pageLink.sortOrder.property = 'title'; + entitiesObservable = this.resourceService.getTenantResources(pageLink, config); + break; } return entitiesObservable; } diff --git a/ui-ngx/src/app/core/http/image.service.ts b/ui-ngx/src/app/core/http/image.service.ts new file mode 100644 index 0000000000..b0fb772130 --- /dev/null +++ b/ui-ngx/src/app/core/http/image.service.ts @@ -0,0 +1,135 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { PageLink } from '@shared/models/page/page-link'; +import { defaultHttpOptionsFromConfig, defaultHttpUploadOptions, RequestConfig } from '@core/http/http-utils'; +import { Observable, of } from 'rxjs'; +import { PageData } from '@shared/models/page/page-data'; +import { + NO_IMAGE_DATA_URI, + ImageResourceInfo, + imageResourceType, + ImageResourceType, + IMAGES_URL_PREFIX, isImageResourceUrl, ImageExportData, removeTbImagePrefix +} from '@shared/models/resource.models'; +import { catchError, map, switchMap } from 'rxjs/operators'; +import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; +import { blobToBase64 } from '@core/utils'; +import { ResourcesService } from '@core/services/resources.service'; + +@Injectable({ + providedIn: 'root' +}) +export class ImageService { + constructor( + private http: HttpClient, + private sanitizer: DomSanitizer, + private resourcesService: ResourcesService + ) { + } + + public uploadImage(file: File, title: string, config?: RequestConfig): Observable { + if (!config) { + config = {}; + } + const formData = new FormData(); + formData.append('file', file); + formData.append('title', title); + return this.http.post('/api/image', formData, + defaultHttpUploadOptions(config.ignoreLoading, config.ignoreErrors, config.resendRequest)); + } + + public updateImage(type: ImageResourceType, key: string, file: File, config?: RequestConfig): Observable { + if (!config) { + config = {}; + } + const formData = new FormData(); + formData.append('file', file); + return this.http.put(`${IMAGES_URL_PREFIX}/${type}/${encodeURIComponent(key)}`, formData, + defaultHttpUploadOptions(config.ignoreLoading, config.ignoreErrors, config.resendRequest)); + } + + public updateImageInfo(imageInfo: ImageResourceInfo, config?: RequestConfig): Observable { + const type = imageResourceType(imageInfo); + const key = encodeURIComponent(imageInfo.resourceKey); + return this.http.put(`${IMAGES_URL_PREFIX}/${type}/${key}/info`, + imageInfo, defaultHttpOptionsFromConfig(config)); + } + + public updateImagePublicStatus(imageInfo: ImageResourceInfo, isPublic: boolean, config?: RequestConfig): Observable { + const type = imageResourceType(imageInfo); + const key = encodeURIComponent(imageInfo.resourceKey); + return this.http.put(`${IMAGES_URL_PREFIX}/${type}/${key}/public/${isPublic}`, + imageInfo, defaultHttpOptionsFromConfig(config)); + } + + public getImages(pageLink: PageLink, includeSystemImages = false, config?: RequestConfig): Observable> { + return this.http.get>( + `${IMAGES_URL_PREFIX}${pageLink.toQuery()}&includeSystemImages=${includeSystemImages}`, + defaultHttpOptionsFromConfig(config)); + } + + public getImageInfo(type: ImageResourceType, key: string, config?: RequestConfig): Observable { + return this.http.get(`${IMAGES_URL_PREFIX}/${type}/${encodeURIComponent(key)}/info`, + defaultHttpOptionsFromConfig(config)); + } + + public getImageDataUrl(imageUrl: string, preview = false, asString = false, emptyUrl = NO_IMAGE_DATA_URI): Observable { + const parts = imageUrl.split('/'); + const key = parts[parts.length - 1]; + parts[parts.length - 1] = encodeURIComponent(key); + const encodedUrl = parts.join('/'); + const imageLink = preview ? (encodedUrl + '/preview') : encodedUrl; + const options = defaultHttpOptionsFromConfig({ignoreLoading: true, ignoreErrors: true}); + return this.http + .get(imageLink, {...options, ...{ responseType: 'blob' } }).pipe( + switchMap(val => blobToBase64(val).pipe( + map((dataUrl) => asString ? dataUrl : this.sanitizer.bypassSecurityTrustUrl(dataUrl)) + )), + catchError(() => of(asString ? emptyUrl : this.sanitizer.bypassSecurityTrustUrl(emptyUrl))) + ); + } + + public resolveImageUrl(imageUrl: string, preview = false, asString = false, emptyUrl = NO_IMAGE_DATA_URI): Observable { + imageUrl = removeTbImagePrefix(imageUrl); + if (isImageResourceUrl(imageUrl)) { + return this.getImageDataUrl(imageUrl, preview, asString, emptyUrl); + } else { + return of(asString ? imageUrl : this.sanitizer.bypassSecurityTrustUrl(imageUrl)); + } + } + + public downloadImage(type: ImageResourceType, key: string): Observable { + return this.resourcesService.downloadResource(`${IMAGES_URL_PREFIX}/${type}/${encodeURIComponent(key)}`); + } + + public deleteImage(type: ImageResourceType, key: string, force = false, config?: RequestConfig) { + return this.http.delete(`${IMAGES_URL_PREFIX}/${type}/${encodeURIComponent(key)}?force=${force}`, defaultHttpOptionsFromConfig(config)); + } + + public exportImage(type: ImageResourceType, key: string, config?: RequestConfig): Observable { + return this.http.get(`${IMAGES_URL_PREFIX}/${type}/${encodeURIComponent(key)}/export`, + defaultHttpOptionsFromConfig(config)); + } + + public importImage(imageData: ImageExportData, config?: RequestConfig): Observable { + return this.http.put('/api/image/import', + imageData, defaultHttpOptionsFromConfig(config)); + } + +} diff --git a/ui-ngx/src/app/core/http/notification.service.ts b/ui-ngx/src/app/core/http/notification.service.ts index ec64621aeb..20e10c91e2 100644 --- a/ui-ngx/src/app/core/http/notification.service.ts +++ b/ui-ngx/src/app/core/http/notification.service.ts @@ -31,6 +31,7 @@ import { NotificationTarget, NotificationTemplate, NotificationType, + NotificationUserSettings, SlackChanelType, SlackConversation } from '@shared/models/notification.models'; @@ -174,4 +175,12 @@ export class NotificationService { } return this.http.get>(url, defaultHttpOptionsFromConfig(config)); } + + public getNotificationUserSettings(config?: RequestConfig): Observable { + return this.http.get(`/api/notification/settings/user`, defaultHttpOptionsFromConfig(config)); + } + + public saveNotificationUserSettings(settings: NotificationUserSettings, config?: RequestConfig): Observable { + return this.http.post('/api/notification/settings/user', settings, defaultHttpOptionsFromConfig(config)); + } } diff --git a/ui-ngx/src/app/core/http/ota-package.service.ts b/ui-ngx/src/app/core/http/ota-package.service.ts index bc3131fba5..6a8e995fb8 100644 --- a/ui-ngx/src/app/core/http/ota-package.service.ts +++ b/ui-ngx/src/app/core/http/ota-package.service.ts @@ -27,12 +27,13 @@ import { OtaPagesIds, OtaUpdateType } from '@shared/models/ota-package.models'; -import { catchError, map, mergeMap } from 'rxjs/operators'; +import { catchError, mergeMap } from 'rxjs/operators'; import { deepClone } from '@core/utils'; import { BaseData } from '@shared/models/base-data'; import { EntityId } from '@shared/models/id/entity-id'; import { TranslateService } from '@ngx-translate/core'; import { DialogService } from '@core/services/dialog.service'; +import { ResourcesService } from '@core/services/resources.service'; @Injectable({ providedIn: 'root' @@ -41,7 +42,8 @@ export class OtaPackageService { constructor( private http: HttpClient, private translate: TranslateService, - private dialogService: DialogService + private dialogService: DialogService, + private resourcesService: ResourcesService ) { } @@ -65,31 +67,7 @@ export class OtaPackageService { } public downloadOtaPackage(otaPackageId: string): Observable { - return this.http.get(`/api/otaPackage/${otaPackageId}/download`, { responseType: 'arraybuffer', observe: 'response' }).pipe( - map((response) => { - const headers = response.headers; - const filename = headers.get('x-filename'); - const contentType = headers.get('content-type'); - const linkElement = document.createElement('a'); - try { - const blob = new Blob([response.body], { type: contentType }); - const url = URL.createObjectURL(blob); - linkElement.setAttribute('href', url); - linkElement.setAttribute('download', filename); - const clickEvent = new MouseEvent('click', - { - view: window, - bubbles: true, - cancelable: false - } - ); - linkElement.dispatchEvent(clickEvent); - return null; - } catch (e) { - throw e; - } - }) - ); + return this.resourcesService.downloadResource(`/api/otaPackage/${otaPackageId}/download`); } public saveOtaPackage(otaPackage: OtaPackage, config?: RequestConfig): Observable { diff --git a/ui-ngx/src/app/core/http/resource.service.ts b/ui-ngx/src/app/core/http/resource.service.ts index f7cb8441d1..86ffff87cf 100644 --- a/ui-ngx/src/app/core/http/resource.service.ts +++ b/ui-ngx/src/app/core/http/resource.service.ts @@ -21,15 +21,17 @@ import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-uti import { forkJoin, Observable, of } from 'rxjs'; import { PageData } from '@shared/models/page/page-data'; import { Resource, ResourceInfo, ResourceType } from '@shared/models/resource.models'; -import { catchError, map, mergeMap } from 'rxjs/operators'; +import { catchError, mergeMap } from 'rxjs/operators'; import { isNotEmptyStr } from '@core/utils'; +import { ResourcesService } from '@core/services/resources.service'; @Injectable({ providedIn: 'root' }) export class ResourceService { constructor( - private http: HttpClient + private http: HttpClient, + private resourcesService: ResourcesService ) { } @@ -42,6 +44,10 @@ export class ResourceService { return this.http.get>(url, defaultHttpOptionsFromConfig(config)); } + public getTenantResources(pageLink: PageLink, config?: RequestConfig): Observable> { + return this.http.get>(`/api/resource/tenant${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); + } + public getResource(resourceId: string, config?: RequestConfig): Observable { return this.http.get(`/api/resource/${resourceId}`, defaultHttpOptionsFromConfig(config)); } @@ -51,34 +57,7 @@ export class ResourceService { } public downloadResource(resourceId: string): Observable { - return this.http.get(`/api/resource/${resourceId}/download`, { - responseType: 'arraybuffer', - observe: 'response' - }).pipe( - map((response) => { - const headers = response.headers; - const filename = headers.get('x-filename'); - const contentType = headers.get('content-type'); - const linkElement = document.createElement('a'); - try { - const blob = new Blob([response.body], {type: contentType}); - const url = URL.createObjectURL(blob); - linkElement.setAttribute('href', url); - linkElement.setAttribute('download', filename); - const clickEvent = new MouseEvent('click', - { - view: window, - bubbles: true, - cancelable: false - } - ); - linkElement.dispatchEvent(clickEvent); - return null; - } catch (e) { - throw e; - } - }) - ); + return this.resourcesService.downloadResource(`/api/resource/${resourceId}/download`); } public saveResources(resources: Resource[], config?: RequestConfig): Observable { diff --git a/ui-ngx/src/app/core/http/widget.service.ts b/ui-ngx/src/app/core/http/widget.service.ts index 5f5f0f42df..d7b14c726c 100644 --- a/ui-ngx/src/app/core/http/widget.service.ts +++ b/ui-ngx/src/app/core/http/widget.service.ts @@ -22,16 +22,16 @@ import { PageLink } from '@shared/models/page/page-link'; import { PageData } from '@shared/models/page/page-data'; import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; import { - Widget, + BaseWidgetType, + DeprecatedFilter, + fullWidgetTypeFqn, WidgetType, widgetType, WidgetTypeDetails, WidgetTypeInfo, widgetTypesData } from '@shared/models/widget.models'; -import { UtilsService } from '@core/services/utils.service'; import { TranslateService } from '@ngx-translate/core'; -import { ResourcesService } from '../services/resources.service'; import { toWidgetInfo, toWidgetTypeDetails, WidgetInfo } from '@app/modules/home/models/widget-component.models'; import { filter, map, mergeMap, tap } from 'rxjs/operators'; import { WidgetTypeId } from '@shared/models/id/widget-type-id'; @@ -47,16 +47,12 @@ export class WidgetService { private systemWidgetsBundles: Array; private tenantWidgetsBundles: Array; - private widgetTypeInfosCache = new Map>(); - private widgetsInfoInMemoryCache = new Map(); private loadWidgetsBundleCacheSubject: ReplaySubject; constructor( private http: HttpClient, - private utils: UtilsService, - private resources: ResourcesService, private translate: TranslateService, private router: Router ) { @@ -89,9 +85,12 @@ export class WidgetService { ); } - public getWidgetBundles(pageLink: PageLink, config?: RequestConfig): Observable> { - return this.http.get>(`/api/widgetsBundles${pageLink.toQuery()}`, - defaultHttpOptionsFromConfig(config)); + public getWidgetBundles(pageLink: PageLink, fullSearch = false, + tenantOnly = false, config?: RequestConfig): Observable> { + return this.http.get>( + `/api/widgetsBundles${pageLink.toQuery()}&tenantOnly=${tenantOnly}&fullSearch=${fullSearch}`, + defaultHttpOptionsFromConfig(config) + ); } public getWidgetsBundle(widgetsBundleId: string, @@ -99,6 +98,11 @@ export class WidgetService { return this.http.get(`/api/widgetsBundle/${widgetsBundleId}`, defaultHttpOptionsFromConfig(config)); } + public exportWidgetsBundle(widgetsBundleId: string, + config?: RequestConfig): Observable { + return this.http.get(`/api/widgetsBundle/${widgetsBundleId}?inlineImages=true`, defaultHttpOptionsFromConfig(config)); + } + public saveWidgetsBundle(widgetsBundle: WidgetsBundle, config?: RequestConfig): Observable { return this.http.post('/api/widgetsBundle', widgetsBundle, @@ -109,108 +113,80 @@ export class WidgetService { ); } + public updateWidgetsBundleWidgetTypes(widgetsBundleId: string, widgetTypeIds: Array, + config?: RequestConfig): Observable { + return this.http.post(`/api/widgetsBundle/${widgetsBundleId}/widgetTypes`, widgetTypeIds, + defaultHttpOptionsFromConfig(config)); + } + + public updateWidgetsBundleWidgetFqns(widgetsBundleId: string, widgetTypeFqns: Array, + config?: RequestConfig): Observable { + return this.http.post(`/api/widgetsBundle/${widgetsBundleId}/widgetTypeFqns`, widgetTypeFqns, + defaultHttpOptionsFromConfig(config)); + } + public deleteWidgetsBundle(widgetsBundleId: string, config?: RequestConfig) { return this.getWidgetsBundle(widgetsBundleId, config).pipe( mergeMap((widgetsBundle) => this.http.delete(`/api/widgetsBundle/${widgetsBundleId}`, defaultHttpOptionsFromConfig(config)).pipe( tap(() => { this.invalidateWidgetsBundleCache(); - this.widgetsBundleDeleted(widgetsBundle); }) ) )); } - public getBundleWidgetTypes(bundleAlias: string, isSystem: boolean, + public getBundleWidgetTypes(widgetsBundleId: string, config?: RequestConfig): Observable> { - return this.http.get>(`/api/widgetTypes?isSystem=${isSystem}&bundleAlias=${bundleAlias}`, + return this.http.get>(`/api/widgetTypes?widgetsBundleId=${widgetsBundleId}`, defaultHttpOptionsFromConfig(config)); } - public getBundleWidgetTypesDetails(bundleAlias: string, isSystem: boolean, + public exportBundleWidgetTypesDetails(widgetsBundleId: string, config?: RequestConfig): Observable> { - return this.http.get>(`/api/widgetTypesDetails?isSystem=${isSystem}&bundleAlias=${bundleAlias}`, + return this.http.get>(`/api/widgetTypesDetails?widgetsBundleId=${widgetsBundleId}&inlineImages=true`, defaultHttpOptionsFromConfig(config)); } - public getBundleWidgetTypeInfos(bundleAlias: string, isSystem: boolean, - config?: RequestConfig): Observable> { - const key = bundleAlias + (isSystem ? '_sys' : ''); - if (this.widgetTypeInfosCache.has(key)) { - return of(this.widgetTypeInfosCache.get(key)); - } else { - return this.http.get>(`/api/widgetTypesInfos?isSystem=${isSystem}&bundleAlias=${bundleAlias}`, - defaultHttpOptionsFromConfig(config)).pipe( - tap((res) => this.widgetTypeInfosCache.set(key, res) ) - ); - } + public getBundleWidgetTypeFqns(widgetsBundleId: string, + config?: RequestConfig): Observable> { + return this.http.get>(`/api/widgetTypeFqns?widgetsBundleId=${widgetsBundleId}`, + defaultHttpOptionsFromConfig(config)); } - public loadBundleLibraryWidgets(bundleAlias: string, isSystem: boolean, - config?: RequestConfig): Observable> { - return this.getBundleWidgetTypes(bundleAlias, isSystem, config).pipe( - map((types) => { - types = types.sort((a, b) => { - let result = widgetType[b.descriptor.type].localeCompare(widgetType[a.descriptor.type]); - if (result === 0) { - result = b.createdTime - a.createdTime; - } - return result; - }); - const widgetTypes = new Array(); - let top = 0; - const lastTop = [0, 0, 0]; - let col = 0; - let column = 0; - types.forEach((type) => { - const widgetTypeInfo = toWidgetInfo(type); - const sizeX = 8; - const sizeY = Math.floor(widgetTypeInfo.sizeY); - const widget: Widget = { - typeId: type.id, - isSystemType: isSystem, - bundleAlias, - typeAlias: widgetTypeInfo.alias, - type: widgetTypeInfo.type, - title: widgetTypeInfo.widgetName, - sizeX, - sizeY, - row: top, - col, - config: JSON.parse(widgetTypeInfo.defaultConfig) - }; - - widget.config.title = widgetTypeInfo.widgetName; - - widgetTypes.push(widget); - top += sizeY; - if (top > lastTop[column] + 10) { - lastTop[column] = top; - column++; - if (column > 2) { - column = 0; - } - top = lastTop[column]; - col = column * 8; - } - }); - return widgetTypes; - }) + public getBundleWidgetTypeInfosList(widgetsBundleId: string, + config?: RequestConfig): Observable> { + return this.getBundleWidgetTypeInfos(new PageLink(1024), widgetsBundleId, false, DeprecatedFilter.ALL, null, config).pipe( + map((data) => data.data) ); } - public getWidgetType(bundleAlias: string, widgetTypeAlias: string, isSystem: boolean, - config?: RequestConfig): Observable { - return this.http.get(`/api/widgetType?isSystem=${isSystem}&bundleAlias=${bundleAlias}&alias=${widgetTypeAlias}`, + public getBundleWidgetTypeInfos(pageLink: PageLink, + widgetsBundleId: string, + fullSearch = false, + deprecatedFilter = DeprecatedFilter.ALL, + widgetTypes: Array = null, + config?: RequestConfig): Observable> { + + let url = + `/api/widgetTypesInfos${pageLink.toQuery()}&widgetsBundleId=${widgetsBundleId}` + + `&fullSearch=${fullSearch}&deprecatedFilter=${deprecatedFilter}`; + if (widgetTypes && widgetTypes.length) { + url += `&widgetTypeList=${widgetTypes.join(',')}`; + } + return this.http.get>(url, defaultHttpOptionsFromConfig(config)); + } + + public getWidgetType(fullFqn: string, config?: RequestConfig): Observable { + return this.http.get(`/api/widgetType?fqn=${fullFqn}`, defaultHttpOptionsFromConfig(config)); } public saveWidgetTypeDetails(widgetInfo: WidgetInfo, id: WidgetTypeId, - bundleAlias: string, createdTime: number, config?: RequestConfig): Observable { - const widgetTypeDetails = toWidgetTypeDetails(widgetInfo, id, undefined, bundleAlias, createdTime); + const widgetTypeDetails = toWidgetTypeDetails(widgetInfo, id, undefined, createdTime); return this.http.post('/api/widgetType', widgetTypeDetails, defaultHttpOptionsFromConfig(config)).pipe( tap((savedWidgetType) => { @@ -220,78 +196,96 @@ export class WidgetService { public saveImportedWidgetTypeDetails(widgetTypeDetails: WidgetTypeDetails, config?: RequestConfig): Observable { - return this.http.post('/api/widgetType', widgetTypeDetails, + return this.http.post('/api/widgetType?updateExistingByFqn=true', widgetTypeDetails, defaultHttpOptionsFromConfig(config)).pipe( tap((savedWidgetType) => { this.widgetTypeUpdated(savedWidgetType); })); } - public deleteWidgetType(bundleAlias: string, widgetTypeAlias: string, isSystem: boolean, - config?: RequestConfig) { - return this.getWidgetType(bundleAlias, widgetTypeAlias, isSystem, config).pipe( - mergeMap((widgetTypeInstance) => this.http.delete(`/api/widgetType/${widgetTypeInstance.id.id}`, - defaultHttpOptionsFromConfig(config)).pipe( - tap(() => { - this.widgetTypeUpdated(widgetTypeInstance); - }) - ) - )); - } - public getWidgetTypeById(widgetTypeId: string, config?: RequestConfig): Observable { return this.http.get(`/api/widgetType/${widgetTypeId}`, defaultHttpOptionsFromConfig(config)); } + public exportWidgetType(widgetTypeId: string, + config?: RequestConfig): Observable { + return this.http.get(`/api/widgetType/${widgetTypeId}?inlineImages=true`, + defaultHttpOptionsFromConfig(config)); + } + + public getWidgetTypeInfoById(widgetTypeId: string, + config?: RequestConfig): Observable { + return this.http.get(`/api/widgetTypeInfo/${widgetTypeId}`, + defaultHttpOptionsFromConfig(config)); + } + + public saveWidgetType(widgetTypeDetails: WidgetTypeDetails, + config?: RequestConfig): Observable { + return this.http.post(`/api/widgetType`, widgetTypeDetails, + defaultHttpOptionsFromConfig(config)); + } + + public deleteWidgetType(widgetTypeId: string, + config?: RequestConfig) { + return this.getWidgetTypeById(widgetTypeId, config).pipe( + mergeMap((widgetTypeDetails) => + this.http.delete(`/api/widgetType/${widgetTypeId}`, defaultHttpOptionsFromConfig(config)).pipe( + tap(() => { + this.widgetTypeUpdated(widgetTypeDetails); + }) + ) + )); + } + + public getWidgetTypes(pageLink: PageLink, tenantOnly = false, + fullSearch = false, deprecatedFilter = DeprecatedFilter.ALL, widgetTypes: Array = null, + config?: RequestConfig): Observable> { + let url = + `/api/widgetTypes${pageLink.toQuery()}&tenantOnly=${tenantOnly}&fullSearch=${fullSearch}&deprecatedFilter=${deprecatedFilter}`; + if (widgetTypes && widgetTypes.length) { + url += `&widgetTypeList=${widgetTypes.join(',')}`; + } + return this.http.get>(url, defaultHttpOptionsFromConfig(config)); + } + + public addWidgetFqnToWidgetBundle(widgetsBundleId: string, fqn: string, config?: RequestConfig) { + return this.getBundleWidgetTypeFqns(widgetsBundleId, config).pipe( + mergeMap(widgetsBundleFqn => { + widgetsBundleFqn.push(fqn); + return this.updateWidgetsBundleWidgetFqns(widgetsBundleId, widgetsBundleFqn, config); + }) + ); + } + public getWidgetTemplate(widgetTypeParam: widgetType, config?: RequestConfig): Observable { const templateWidgetType = widgetTypesData.get(widgetTypeParam); - return this.getWidgetType(templateWidgetType.template.bundleAlias, templateWidgetType.template.alias, true, + return this.getWidgetType(templateWidgetType.template.fullFqn, config).pipe( map((result) => { const widgetInfo = toWidgetInfo(result); - widgetInfo.alias = undefined; + widgetInfo.fullFqn = undefined; return widgetInfo; }) ); } - public createWidgetInfoCacheKey(bundleAlias: string, widgetTypeAlias: string, isSystem: boolean): string { - return `${isSystem ? 'sys_' : ''}${bundleAlias}_${widgetTypeAlias}`; + public getWidgetInfoFromCache(fullFqn: string): WidgetInfo | undefined { + return this.widgetsInfoInMemoryCache.get(fullFqn); } - public getWidgetInfoFromCache(bundleAlias: string, widgetTypeAlias: string, isSystem: boolean): WidgetInfo | undefined { - const key = this.createWidgetInfoCacheKey(bundleAlias, widgetTypeAlias, isSystem); - return this.widgetsInfoInMemoryCache.get(key); + public putWidgetInfoToCache(widgetInfo: WidgetInfo) { + this.widgetsInfoInMemoryCache.set(widgetInfo.fullFqn, widgetInfo); } - public putWidgetInfoToCache(widgetInfo: WidgetInfo, bundleAlias: string, widgetTypeAlias: string, isSystem: boolean) { - const key = this.createWidgetInfoCacheKey(bundleAlias, widgetTypeAlias, isSystem); - this.widgetsInfoInMemoryCache.set(key, widgetInfo); + private widgetTypeUpdated(updatedWidgetType: BaseWidgetType): void { + this.deleteWidgetInfoFromCache(fullWidgetTypeFqn(updatedWidgetType)); } - private widgetTypeUpdated(updatedWidgetType: WidgetType): void { - this.deleteWidgetInfoFromCache(updatedWidgetType.bundleAlias, updatedWidgetType.alias, updatedWidgetType.tenantId.id === NULL_UUID); - } - - private widgetsBundleDeleted(widgetsBundle: WidgetsBundle): void { - this.deleteWidgetsBundleFromCache(widgetsBundle.alias, widgetsBundle.tenantId.id === NULL_UUID); - } - - public deleteWidgetInfoFromCache(bundleAlias: string, widgetTypeAlias: string, isSystem: boolean) { - const key = this.createWidgetInfoCacheKey(bundleAlias, widgetTypeAlias, isSystem); - this.widgetsInfoInMemoryCache.delete(key); - } - - private deleteWidgetsBundleFromCache(bundleAlias: string, isSystem: boolean) { - const key = (isSystem ? 'sys_' : '') + bundleAlias; - this.widgetsInfoInMemoryCache.forEach((widgetInfo, cacheKey) => { - if (cacheKey.startsWith(key)) { - this.widgetsInfoInMemoryCache.delete(cacheKey); - } - }); + public deleteWidgetInfoFromCache(fullFqn: string) { + this.widgetsInfoInMemoryCache.delete(fullFqn); } private loadWidgetsBundleCache(config?: RequestConfig): Observable { @@ -336,6 +330,5 @@ export class WidgetService { this.systemWidgetsBundles = undefined; this.tenantWidgetsBundles = undefined; this.loadWidgetsBundleCacheSubject = undefined; - this.widgetTypeInfosCache.clear(); } } diff --git a/ui-ngx/src/app/core/public-api.ts b/ui-ngx/src/app/core/public-api.ts index bbf97cde57..cf36240cea 100644 --- a/ui-ngx/src/app/core/public-api.ts +++ b/ui-ngx/src/app/core/public-api.ts @@ -18,7 +18,7 @@ export * from './api/public-api'; export * from './http/public-api'; export * from './local-storage/local-storage.service'; export * from './services/public-api'; -export * from './ws/telemetry-websocket.service'; +export * from './ws/public-api'; export * from './core.state'; export * from './core.module'; export * from './utils'; diff --git a/ui-ngx/src/app/core/services/dashboard-utils.service.ts b/ui-ngx/src/app/core/services/dashboard-utils.service.ts index 8355ce2f61..0e8cba3f80 100644 --- a/ui-ngx/src/app/core/services/dashboard-utils.service.ts +++ b/ui-ngx/src/app/core/services/dashboard-utils.service.ts @@ -29,13 +29,20 @@ import { GridSettings, WidgetLayout } from '@shared/models/dashboard.models'; -import { deepClone, isDefined, isDefinedAndNotNull, isString, isUndefined } from '@core/utils'; import { - DataKey, + deepClone, + isDefined, + isDefinedAndNotNull, + isEmptyStr, + isNotEmptyStr, + isString, + isUndefined +} from '@core/utils'; +import { Datasource, datasourcesHasOnlyComparisonAggregation, DatasourceType, - defaultLegendConfig, + defaultLegendConfig, isValidWidgetFullFqn, Widget, WidgetConfig, WidgetConfigMode, @@ -215,12 +222,20 @@ export class DashboardUtilsService { public validateAndUpdateWidget(widget: Widget): Widget { widget.config = this.validateAndUpdateWidgetConfig(widget.config, widget.type); - // Temp workaround - if (widget.isSystemType && widget.bundleAlias === 'charts' && widget.typeAlias === 'timeseries') { - widget.typeAlias = 'basic_timeseries'; + widget = this.validateAndUpdateWidgetTypeFqn(widget); + if (isDefined((widget as any).title)) { + delete (widget as any).title; + } + if (isDefined((widget as any).image)) { + delete (widget as any).image; } - if (widget.isSystemType && widget.bundleAlias === 'charts' && - ['state_chart', 'basic_timeseries', 'timeseries_bars_flot'].includes(widget.typeAlias)) { + if (isDefined((widget as any).description)) { + delete (widget as any).description; + } + // Temp workaround + if (['system.charts.state_chart', + 'system.charts.basic_timeseries', + 'system.charts.timeseries_bars_flot'].includes(widget.typeFullFqn)) { const widgetConfig = widget.config; const widgetSettings = widget.config.settings; if (isDefinedAndNotNull(widgetConfig.showLegend)) { @@ -239,6 +254,29 @@ export class DashboardUtilsService { return widget; } + private validateAndUpdateWidgetTypeFqn(widget: Widget): Widget { + const w = widget as any; + if (!isValidWidgetFullFqn(widget.typeFullFqn)) { + if (isDefinedAndNotNull(w.isSystemType) && isNotEmptyStr(w.bundleAlias) && isNotEmptyStr(w.typeAlias)) { + widget.typeFullFqn = (w.isSystemType ? 'system' : 'tenant') + '.' + w.bundleAlias + '.' + w.typeAlias; + } + } + if (isDefined(w.isSystemType)) { + delete w.isSystemType; + } + if (isDefined(w.bundleAlias)) { + delete w.bundleAlias; + } + if (isDefined(w.typeAlias)) { + delete w.typeAlias; + } + // Temp workaround + if (widget.typeFullFqn === 'system.charts.timeseries') { + widget.typeFullFqn = 'system.charts.basic_timeseries'; + } + return widget; + } + public validateAndUpdateWidgetConfig(widgetConfig: WidgetConfig | undefined, type: widgetType): WidgetConfig { if (!widgetConfig) { widgetConfig = {}; @@ -348,10 +386,8 @@ export class DashboardUtilsService { private convertDatasourcesFromWidgetType(widgetTypeDescriptor: WidgetTypeDescriptor, config: WidgetConfig, datasources?: Datasource[]): Datasource[] { const newDatasources: Datasource[] = []; - if (datasources) { - datasources.forEach(datasource => { - newDatasources.push(this.convertDatasourceFromWidgetType(widgetTypeDescriptor, config, datasource)); - }); + if (datasources?.length) { + newDatasources.push(this.convertDatasourceFromWidgetType(widgetTypeDescriptor, config, datasources[0])); } return newDatasources; } @@ -361,6 +397,7 @@ export class DashboardUtilsService { const newDatasource = deepClone(datasource); if (newDatasource.type === DatasourceType.function) { newDatasource.type = DatasourceType.entity; + newDatasource.name = ''; if (widgetTypeDescriptor.hasBasicMode && config.configMode === WidgetConfigMode.basic) { newDatasource.type = DatasourceType.device; } @@ -486,6 +523,14 @@ export class DashboardUtilsService { return widgetsArray; } + public isEmptyDashboard(dashboard: Dashboard): boolean { + if (dashboard?.configuration?.widgets) { + return Object.keys(dashboard?.configuration?.widgets).length === 0; + } else { + return true; + } + } + public addWidgetToLayout(dashboard: Dashboard, targetState: string, targetLayout: DashboardLayoutId, diff --git a/ui-ngx/src/app/core/services/dialog.service.ts b/ui-ngx/src/app/core/services/dialog.service.ts index be0c24a958..c5f54f9cf0 100644 --- a/ui-ngx/src/app/core/services/dialog.service.ts +++ b/ui-ngx/src/app/core/services/dialog.service.ts @@ -21,11 +21,11 @@ import { TranslateService } from '@ngx-translate/core'; import { AuthService } from '@core/auth/auth.service'; import { ColorPickerDialogComponent, - ColorPickerDialogData + ColorPickerDialogData, ColorPickerDialogResult } from '@shared/components/dialog/color-picker-dialog.component'; import { MaterialIconsDialogComponent, - MaterialIconsDialogData + MaterialIconsDialogData, MaterialIconsDialogResult } from '@shared/components/dialog/material-icons-dialog.component'; import { ConfirmDialogComponent } from '@shared/components/dialog/confirm-dialog.component'; import { AlertDialogComponent } from '@shared/components/dialog/alert-dialog.component'; @@ -96,25 +96,29 @@ export class DialogService { return dialogRef.afterClosed(); } - colorPicker(color: string): Observable { - return this.dialog.open(ColorPickerDialogComponent, + colorPicker(color: string, colorClearButton = false): Observable { + return this.dialog.open(ColorPickerDialogComponent, { disableClose: true, panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { - color - } + color, + colorClearButton + }, + autoFocus: false }).afterClosed(); } - materialIconPicker(icon: string): Observable { - return this.dialog.open(MaterialIconsDialogComponent, + materialIconPicker(icon: string, iconClearButton = false): Observable { + return this.dialog.open(MaterialIconsDialogComponent, { disableClose: true, panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { - icon - } + icon, + iconClearButton + }, + autoFocus: false }).afterClosed(); } diff --git a/ui-ngx/src/app/core/services/dynamic-component-factory.service.ts b/ui-ngx/src/app/core/services/dynamic-component-factory.service.ts index 84134f04a9..b7e21f7b22 100644 --- a/ui-ngx/src/app/core/services/dynamic-component-factory.service.ts +++ b/ui-ngx/src/app/core/services/dynamic-component-factory.service.ts @@ -33,11 +33,17 @@ import { catchError, map, mergeMap } from 'rxjs/operators'; @NgModule() export abstract class DynamicComponentModule implements OnDestroy { + // eslint-disable-next-line @angular-eslint/contextual-lifecycle ngOnDestroy(): void { } } +interface DynamicComponentData { + componentType: Type; + componentModuleRef: NgModuleRef; +} + interface DynamicComponentModuleData { moduleRef: NgModuleRef; moduleType: Type; @@ -48,22 +54,22 @@ interface DynamicComponentModuleData { }) export class DynamicComponentFactoryService { - private dynamicComponentModulesMap = new Map, DynamicComponentModuleData>(); + private dynamicComponentModulesMap = new Map, DynamicComponentModuleData>(); constructor(private compiler: Compiler, private injector: Injector) { } - public createDynamicComponentFactory( + public createDynamicComponent( componentType: Type, template: string, modules?: Type[], preserveWhitespaces?: boolean, compileAttempt = 1, - styles?: string[]): Observable> { + styles?: string[]): Observable> { return from(import('@angular/compiler')).pipe( mergeMap(() => { - const comp = this.createDynamicComponent(componentType, template, preserveWhitespaces, styles); + const comp = this._createDynamicComponent(componentType, template, preserveWhitespaces, styles); let moduleImports: Type[] = [CommonModule]; if (modules) { moduleImports = [...moduleImports, ...modules]; @@ -82,17 +88,19 @@ export class DynamicComponentFactoryService { this.compiler.clearCacheFor(module.moduleType); throw e; } - const factory = moduleRef.componentFactoryResolver.resolveComponentFactory(comp); - this.dynamicComponentModulesMap.set(factory, { + this.dynamicComponentModulesMap.set(comp, { moduleRef, moduleType: module.moduleType }); - return factory; + return { + componentType: comp, + componentModuleRef: moduleRef + }; }), catchError((error) => { if (compileAttempt === 1) { ɵresetCompiledComponents(); - return this.createDynamicComponentFactory(componentType, template, modules, preserveWhitespaces, ++compileAttempt, styles); + return this.createDynamicComponent(componentType, template, modules, preserveWhitespaces, ++compileAttempt, styles); } else { throw error; } @@ -102,16 +110,16 @@ export class DynamicComponentFactoryService { ); } - public destroyDynamicComponentFactory(factory: ComponentFactory) { - const moduleData = this.dynamicComponentModulesMap.get(factory); + public destroyDynamicComponent(componentType: Type) { + const moduleData = this.dynamicComponentModulesMap.get(componentType); if (moduleData) { moduleData.moduleRef.destroy(); this.compiler.clearCacheFor(moduleData.moduleType); - this.dynamicComponentModulesMap.delete(factory); + this.dynamicComponentModulesMap.delete(componentType); } } - private createDynamicComponent(componentType: Type, template: string, preserveWhitespaces?: boolean, styles?: string[]): Type { + private _createDynamicComponent(componentType: Type, template: string, preserveWhitespaces?: boolean, styles?: string[]): Type { // noinspection AngularMissingOrInvalidDeclarationInModule return Component({ template, diff --git a/ui-ngx/src/app/core/services/material-icons-codepoints.raw b/ui-ngx/src/app/core/services/material-icons-codepoints.raw deleted file mode 100644 index a1f9b8bbc7..0000000000 --- a/ui-ngx/src/app/core/services/material-icons-codepoints.raw +++ /dev/null @@ -1,2142 +0,0 @@ -10k e951 -10mp e952 -11mp e953 -123 eb8d -12mp e954 -13mp e955 -14mp e956 -15mp e957 -16mp e958 -17mp e959 -18mp e95a -19mp e95b -1k e95c -1k_plus e95d -1x_mobiledata efcd -20mp e95e -21mp e95f -22mp e960 -23mp e961 -24mp e962 -2k e963 -2k_plus e964 -2mp e965 -30fps efce -30fps_select efcf -360 e577 -3d_rotation e84d -3g_mobiledata efd0 -3k e966 -3k_plus e967 -3mp e968 -3p efd1 -4g_mobiledata efd2 -4g_plus_mobiledata efd3 -4k e072 -4k_plus e969 -4mp e96a -5g ef38 -5k e96b -5k_plus e96c -5mp e96d -60fps efd4 -60fps_select efd5 -6_ft_apart f21e -6k e96e -6k_plus e96f -6mp e970 -7k e971 -7k_plus e972 -7mp e973 -8k e974 -8k_plus e975 -8mp e976 -9k e977 -9k_plus e978 -9mp e979 -abc eb94 -ac_unit eb3b -access_alarm e190 -access_alarms e191 -access_time e192 -access_time_filled efd6 -accessibility e84e -accessibility_new e92c -accessible e914 -accessible_forward e934 -account_balance e84f -account_balance_wallet e850 -account_box e851 -account_circle e853 -account_tree e97a -ad_units ef39 -adb e60e -add e145 -add_a_photo e439 -add_alarm e193 -add_alert e003 -add_box e146 -add_business e729 -add_call e0e8 -add_card eb86 -add_chart e97b -add_circle e147 -add_circle_outline e148 -add_comment e266 -add_ic_call e97c -add_link e178 -add_location e567 -add_location_alt ef3a -add_moderator e97d -add_photo_alternate e43e -add_reaction e1d3 -add_road ef3b -add_shopping_cart e854 -add_task f23a -add_to_drive e65c -add_to_home_screen e1fe -add_to_photos e39d -add_to_queue e05c -addchart ef3c -adf_scanner eada -adjust e39e -admin_panel_settings ef3d -adobe ea96 -ads_click e762 -agriculture ea79 -air efd8 -airline_seat_flat e630 -airline_seat_flat_angled e631 -airline_seat_individual_suite e632 -airline_seat_legroom_extra e633 -airline_seat_legroom_normal e634 -airline_seat_legroom_reduced e635 -airline_seat_recline_extra e636 -airline_seat_recline_normal e637 -airline_stops e7d0 -airlines e7ca -airplane_ticket efd9 -airplanemode_active e195 -airplanemode_inactive e194 -airplanemode_off e194 -airplanemode_on e195 -airplay e055 -airport_shuttle eb3c -alarm e855 -alarm_add e856 -alarm_off e857 -alarm_on e858 -album e019 -align_horizontal_center e00f -align_horizontal_left e00d -align_horizontal_right e010 -align_vertical_bottom e015 -align_vertical_center e011 -align_vertical_top e00c -all_inbox e97f -all_inclusive eb3d -all_out e90b -alt_route f184 -alternate_email e0e6 -amp_stories ea13 -analytics ef3e -anchor f1cd -android e859 -animation e71c -announcement e85a -aod efda -apartment ea40 -api f1b7 -app_blocking ef3f -app_registration ef40 -app_settings_alt ef41 -app_shortcut eae4 -apple ea80 -approval e982 -apps e5c3 -apps_outage e7cc -architecture ea3b -archive e149 -area_chart e770 -arrow_back e5c4 -arrow_back_ios e5e0 -arrow_back_ios_new e2ea -arrow_circle_down f181 -arrow_circle_left eaa7 -arrow_circle_right eaaa -arrow_circle_up f182 -arrow_downward e5db -arrow_drop_down e5c5 -arrow_drop_down_circle e5c6 -arrow_drop_up e5c7 -arrow_forward e5c8 -arrow_forward_ios e5e1 -arrow_left e5de -arrow_right e5df -arrow_right_alt e941 -arrow_upward e5d8 -art_track e060 -article ef42 -aspect_ratio e85b -assessment e85c -assignment e85d -assignment_ind e85e -assignment_late e85f -assignment_return e860 -assignment_returned e861 -assignment_turned_in e862 -assistant e39f -assistant_direction e988 -assistant_navigation e989 -assistant_photo e3a0 -assured_workload eb6f -atm e573 -attach_email ea5e -attach_file e226 -attach_money e227 -attachment e2bc -attractions ea52 -attribution efdb -audio_file eb82 -audiotrack e3a1 -auto_awesome e65f -auto_awesome_mosaic e660 -auto_awesome_motion e661 -auto_delete ea4c -auto_fix_high e663 -auto_fix_normal e664 -auto_fix_off e665 -auto_graph e4fb -auto_stories e666 -autofps_select efdc -autorenew e863 -av_timer e01b -baby_changing_station f19b -back_hand e764 -backpack f19c -backspace e14a -backup e864 -backup_table ef43 -badge ea67 -bakery_dining ea53 -balance eaf6 -balcony e58f -ballot e172 -bar_chart e26b -batch_prediction f0f5 -bathroom efdd -bathtub ea41 -battery_0_bar ebdc -battery_1_bar ebd9 -battery_2_bar ebe0 -battery_3_bar ebdd -battery_4_bar ebe2 -battery_5_bar ebd4 -battery_6_bar ebd2 -battery_alert e19c -battery_charging_full e1a3 -battery_full e1a4 -battery_saver efde -battery_std e1a5 -battery_unknown e1a6 -beach_access eb3e -bed efdf -bedroom_baby efe0 -bedroom_child efe1 -bedroom_parent efe2 -bedtime ef44 -bedtime_off eb76 -beenhere e52d -bento f1f4 -bike_scooter ef45 -biotech ea3a -blender efe3 -block e14b -block_flipped ef46 -bloodtype efe4 -bluetooth e1a7 -bluetooth_audio e60f -bluetooth_connected e1a8 -bluetooth_disabled e1a9 -bluetooth_drive efe5 -bluetooth_searching e1aa -blur_circular e3a2 -blur_linear e3a3 -blur_off e3a4 -blur_on e3a5 -bolt ea0b -book e865 -book_online f217 -bookmark e866 -bookmark_add e598 -bookmark_added e599 -bookmark_border e867 -bookmark_outline e867 -bookmark_remove e59a -bookmarks e98b -border_all e228 -border_bottom e229 -border_clear e22a -border_color e22b -border_horizontal e22c -border_inner e22d -border_left e22e -border_outer e22f -border_right e230 -border_style e231 -border_top e232 -border_vertical e233 -boy eb67 -branding_watermark e06b -breakfast_dining ea54 -brightness_1 e3a6 -brightness_2 e3a7 -brightness_3 e3a8 -brightness_4 e3a9 -brightness_5 e3aa -brightness_6 e3ab -brightness_7 e3ac -brightness_auto e1ab -brightness_high e1ac -brightness_low e1ad -brightness_medium e1ae -broken_image e3ad -browse_gallery ebd1 -browser_not_supported ef47 -browser_updated e7cf -brunch_dining ea73 -brush e3ae -bubble_chart e6dd -bug_report e868 -build e869 -build_circle ef48 -bungalow e591 -burst_mode e43c -bus_alert e98f -business e0af -business_center eb3f -cabin e589 -cable efe6 -cached e86a -cake e7e9 -calculate ea5f -calendar_month ebcc -calendar_today e935 -calendar_view_day e936 -calendar_view_month efe7 -calendar_view_week efe8 -call e0b0 -call_end e0b1 -call_made e0b2 -call_merge e0b3 -call_missed e0b4 -call_missed_outgoing e0e4 -call_received e0b5 -call_split e0b6 -call_to_action e06c -camera e3af -camera_alt e3b0 -camera_enhance e8fc -camera_front e3b1 -camera_indoor efe9 -camera_outdoor efea -camera_rear e3b2 -camera_roll e3b3 -cameraswitch efeb -campaign ef49 -cancel e5c9 -cancel_presentation e0e9 -cancel_schedule_send ea39 -candlestick_chart ead4 -car_crash ebf2 -car_rental ea55 -car_repair ea56 -card_giftcard e8f6 -card_membership e8f7 -card_travel e8f8 -carpenter f1f8 -cases e992 -casino eb40 -cast e307 -cast_connected e308 -cast_for_education efec -castle eab1 -catching_pokemon e508 -category e574 -celebration ea65 -cell_tower ebba -cell_wifi e0ec -center_focus_strong e3b4 -center_focus_weak e3b5 -chair efed -chair_alt efee -chalet e585 -change_circle e2e7 -change_history e86b -charging_station f19d -chat e0b7 -chat_bubble e0ca -chat_bubble_outline e0cb -check e5ca -check_box e834 -check_box_outline_blank e835 -check_circle e86c -check_circle_outline e92d -checklist e6b1 -checklist_rtl e6b3 -checkroom f19e -chevron_left e5cb -chevron_right e5cc -child_care eb41 -child_friendly eb42 -chrome_reader_mode e86d -church eaae -circle ef4a -circle_notifications e994 -class e86e -clean_hands f21f -cleaning_services f0ff -clear e14c -clear_all e0b8 -close e5cd -close_fullscreen f1cf -closed_caption e01c -closed_caption_disabled f1dc -closed_caption_off e996 -cloud e2bd -cloud_circle e2be -cloud_done e2bf -cloud_download e2c0 -cloud_off e2c1 -cloud_queue e2c2 -cloud_sync eb5a -cloud_upload e2c3 -cloudy_snowing e810 -co2 e7b0 -co_present eaf0 -code e86f -code_off e4f3 -coffee efef -coffee_maker eff0 -collections e3b6 -collections_bookmark e431 -color_lens e3b7 -colorize e3b8 -comment e0b9 -comment_bank ea4e -comments_disabled e7a2 -commit eaf5 -commute e940 -compare e3b9 -compare_arrows e915 -compass_calibration e57c -compost e761 -compress e94d -computer e30a -confirmation_num e638 -confirmation_number e638 -connect_without_contact f223 -connected_tv e998 -connecting_airports e7c9 -construction ea3c -contact_mail e0d0 -contact_page f22e -contact_phone e0cf -contact_support e94c -contactless ea71 -contacts e0ba -content_copy e14d -content_cut e14e -content_paste e14f -content_paste_go ea8e -content_paste_off e4f8 -content_paste_search ea9b -contrast eb37 -control_camera e074 -control_point e3ba -control_point_duplicate e3bb -cookie eaac -copy_all e2ec -copyright e90c -coronavirus f221 -corporate_fare f1d0 -cottage e587 -countertops f1f7 -create e150 -create_new_folder e2cc -credit_card e870 -credit_card_off e4f4 -credit_score eff1 -crib e588 -crisis_alert ebe9 -crop e3be -crop_16_9 e3bc -crop_3_2 e3bd -crop_5_4 e3bf -crop_7_5 e3c0 -crop_din e3c1 -crop_free e3c2 -crop_landscape e3c3 -crop_original e3c4 -crop_portrait e3c5 -crop_rotate e437 -crop_square e3c6 -cruelty_free e799 -css eb93 -currency_bitcoin ebc5 -currency_exchange eb70 -currency_franc eafa -currency_lira eaef -currency_pound eaf1 -currency_ruble eaec -currency_rupee eaf7 -currency_yen eafb -currency_yuan eaf9 -cyclone ebd5 -dangerous e99a -dark_mode e51c -dashboard e871 -dashboard_customize e99b -data_array ead1 -data_exploration e76f -data_object ead3 -data_saver_off eff2 -data_saver_on eff3 -data_thresholding eb9f -data_usage e1af -date_range e916 -deblur eb77 -deck ea42 -dehaze e3c7 -delete e872 -delete_forever e92b -delete_outline e92e -delete_sweep e16c -delivery_dining ea72 -density_large eba9 -density_medium eb9e -density_small eba8 -departure_board e576 -description e873 -deselect ebb6 -design_services f10a -desktop_access_disabled e99d -desktop_mac e30b -desktop_windows e30c -details e3c8 -developer_board e30d -developer_board_off e4ff -developer_mode e1b0 -device_hub e335 -device_thermostat e1ff -device_unknown e339 -devices e1b1 -devices_fold ebde -devices_other e337 -dialer_sip e0bb -dialpad e0bc -diamond ead5 -difference eb7d -dining eff4 -dinner_dining ea57 -directions e52e -directions_bike e52f -directions_boat e532 -directions_boat_filled eff5 -directions_bus e530 -directions_bus_filled eff6 -directions_car e531 -directions_car_filled eff7 -directions_ferry e532 -directions_off f10f -directions_railway e534 -directions_railway_filled eff8 -directions_run e566 -directions_subway e533 -directions_subway_filled eff9 -directions_train e534 -directions_transit e535 -directions_transit_filled effa -directions_walk e536 -dirty_lens ef4b -disabled_by_default f230 -disabled_visible e76e -disc_full e610 -discord ea6c -discount ebc9 -display_settings eb97 -dnd_forwardslash e611 -dns e875 -do_disturb f08c -do_disturb_alt f08d -do_disturb_off f08e -do_disturb_on f08f -do_not_disturb e612 -do_not_disturb_alt e611 -do_not_disturb_off e643 -do_not_disturb_on e644 -do_not_disturb_on_total_silence effb -do_not_step f19f -do_not_touch f1b0 -dock e30e -document_scanner e5fa -domain e7ee -domain_add eb62 -domain_disabled e0ef -domain_verification ef4c -done e876 -done_all e877 -done_outline e92f -donut_large e917 -donut_small e918 -door_back effc -door_front effd -door_sliding effe -doorbell efff -double_arrow ea50 -downhill_skiing e509 -download f090 -download_done f091 -download_for_offline f000 -downloading f001 -drafts e151 -drag_handle e25d -drag_indicator e945 -draw e746 -drive_eta e613 -drive_file_move e675 -drive_file_move_outline e9a1 -drive_file_move_rtl e76d -drive_file_rename_outline e9a2 -drive_folder_upload e9a3 -dry f1b3 -dry_cleaning ea58 -duo e9a5 -dvr e1b2 -dynamic_feed ea14 -dynamic_form f1bf -e_mobiledata f002 -earbuds f003 -earbuds_battery f004 -east f1df -eco ea35 -edgesensor_high f005 -edgesensor_low f006 -edit e3c9 -edit_attributes e578 -edit_calendar e742 -edit_location e568 -edit_location_alt e1c5 -edit_note e745 -edit_notifications e525 -edit_off e950 -edit_road ef4d -egg eacc -egg_alt eac8 -eject e8fb -elderly f21a -elderly_woman eb69 -electric_bike eb1b -electric_car eb1c -electric_moped eb1d -electric_rickshaw eb1e -electric_scooter eb1f -electrical_services f102 -elevator f1a0 -email e0be -emergency e1eb -emergency_recording ebf4 -emergency_share ebf6 -emoji_emotions ea22 -emoji_events ea23 -emoji_flags ea1a -emoji_food_beverage ea1b -emoji_nature ea1c -emoji_objects ea24 -emoji_people ea1d -emoji_symbols ea1e -emoji_transportation ea1f -engineering ea3d -enhance_photo_translate e8fc -enhanced_encryption e63f -equalizer e01d -error e000 -error_outline e001 -escalator f1a1 -escalator_warning f1ac -euro ea15 -euro_symbol e926 -ev_station e56d -event e878 -event_available e614 -event_busy e615 -event_note e616 -event_repeat eb7b -event_seat e903 -exit_to_app e879 -expand e94f -expand_circle_down e7cd -expand_less e5ce -expand_more e5cf -explicit e01e -explore e87a -explore_off e9a8 -exposure e3ca -exposure_minus_1 e3cb -exposure_minus_2 e3cc -exposure_neg_1 e3cb -exposure_neg_2 e3cc -exposure_plus_1 e3cd -exposure_plus_2 e3ce -exposure_zero e3cf -extension e87b -extension_off e4f5 -face e87c -face_retouching_natural ef4e -face_retouching_off f007 -facebook f234 -fact_check f0c5 -factory ebbc -family_restroom f1a2 -fast_forward e01f -fast_rewind e020 -fastfood e57a -favorite e87d -favorite_border e87e -favorite_outline e87e -fax ead8 -featured_play_list e06d -featured_video e06e -feed f009 -feedback e87f -female e590 -fence f1f6 -festival ea68 -fiber_dvr e05d -fiber_manual_record e061 -fiber_new e05e -fiber_pin e06a -fiber_smart_record e062 -file_copy e173 -file_download e2c4 -file_download_done e9aa -file_download_off e4fe -file_open eaf3 -file_present ea0e -file_upload e2c6 -filter e3d3 -filter_1 e3d0 -filter_2 e3d1 -filter_3 e3d2 -filter_4 e3d4 -filter_5 e3d5 -filter_6 e3d6 -filter_7 e3d7 -filter_8 e3d8 -filter_9 e3d9 -filter_9_plus e3da -filter_alt ef4f -filter_alt_off eb32 -filter_b_and_w e3db -filter_center_focus e3dc -filter_drama e3dd -filter_frames e3de -filter_hdr e3df -filter_list e152 -filter_list_alt e94e -filter_list_off eb57 -filter_none e3e0 -filter_tilt_shift e3e2 -filter_vintage e3e3 -find_in_page e880 -find_replace e881 -fingerprint e90d -fire_extinguisher f1d8 -fire_hydrant f1a3 -fireplace ea43 -first_page e5dc -fit_screen ea10 -fitbit e82b -fitness_center eb43 -flag e153 -flag_circle eaf8 -flaky ef50 -flare e3e4 -flash_auto e3e5 -flash_off e3e6 -flash_on e3e7 -flashlight_off f00a -flashlight_on f00b -flatware f00c -flight e539 -flight_class e7cb -flight_land e904 -flight_takeoff e905 -flip e3e8 -flip_camera_android ea37 -flip_camera_ios ea38 -flip_to_back e882 -flip_to_front e883 -flood ebe6 -flourescent f00d -flutter_dash e00b -fmd_bad f00e -fmd_good f00f -foggy e818 -folder e2c7 -folder_copy ebbd -folder_delete eb34 -folder_off eb83 -folder_open e2c8 -folder_shared e2c9 -folder_special e617 -folder_zip eb2c -follow_the_signs f222 -font_download e167 -font_download_off e4f9 -food_bank f1f2 -forest ea99 -fork_left eba0 -fork_right ebac -format_align_center e234 -format_align_justify e235 -format_align_left e236 -format_align_right e237 -format_bold e238 -format_clear e239 -format_color_fill e23a -format_color_reset e23b -format_color_text e23c -format_indent_decrease e23d -format_indent_increase e23e -format_italic e23f -format_line_spacing e240 -format_list_bulleted e241 -format_list_numbered e242 -format_list_numbered_rtl e267 -format_overline eb65 -format_paint e243 -format_quote e244 -format_shapes e25e -format_size e245 -format_strikethrough e246 -format_textdirection_l_to_r e247 -format_textdirection_r_to_l e248 -format_underline e249 -format_underlined e249 -fort eaad -forum e0bf -forward e154 -forward_10 e056 -forward_30 e057 -forward_5 e058 -forward_to_inbox f187 -foundation f200 -free_breakfast eb44 -free_cancellation e748 -front_hand e769 -fullscreen e5d0 -fullscreen_exit e5d1 -functions e24a -g_mobiledata f010 -g_translate e927 -gamepad e30f -games e021 -garage f011 -gavel e90e -generating_tokens e749 -gesture e155 -get_app e884 -gif e908 -gif_box e7a3 -girl eb68 -gite e58b -goat 10fffd -golf_course eb45 -gpp_bad f012 -gpp_good f013 -gpp_maybe f014 -gps_fixed e1b3 -gps_not_fixed e1b4 -gps_off e1b5 -grade e885 -gradient e3e9 -grading ea4f -grain e3ea -graphic_eq e1b8 -grass f205 -grid_3x3 f015 -grid_4x4 f016 -grid_goldenratio f017 -grid_off e3eb -grid_on e3ec -grid_view e9b0 -group e7ef -group_add e7f0 -group_off e747 -group_remove e7ad -group_work e886 -groups f233 -h_mobiledata f018 -h_plus_mobiledata f019 -hail e9b1 -handshake ebcb -handyman f10b -hardware ea59 -hd e052 -hdr_auto f01a -hdr_auto_select f01b -hdr_enhanced_select ef51 -hdr_off e3ed -hdr_off_select f01c -hdr_on e3ee -hdr_on_select f01d -hdr_plus f01e -hdr_strong e3f1 -hdr_weak e3f2 -headphones f01f -headphones_battery f020 -headset e310 -headset_mic e311 -headset_off e33a -healing e3f3 -health_and_safety e1d5 -hearing e023 -hearing_disabled f104 -heart_broken eac2 -height ea16 -help e887 -help_center f1c0 -help_outline e8fd -hevc f021 -hexagon eb39 -hide_image f022 -hide_source f023 -high_quality e024 -highlight e25f -highlight_alt ef52 -highlight_off e888 -highlight_remove e888 -hiking e50a -history e889 -history_edu ea3e -history_toggle_off f17d -hive eaa6 -hls eb8a -hls_off eb8c -holiday_village e58a -home e88a -home_filled e9b2 -home_max f024 -home_mini f025 -home_repair_service f100 -home_work ea09 -horizontal_distribute e014 -horizontal_rule f108 -horizontal_split e947 -hot_tub eb46 -hotel e53a -hotel_class e743 -hourglass_bottom ea5c -hourglass_disabled ef53 -hourglass_empty e88b -hourglass_full e88c -hourglass_top ea5b -house ea44 -house_siding f202 -houseboat e584 -how_to_reg e174 -how_to_vote e175 -html eb7e -http e902 -https e88d -hub e9f4 -hvac f10e -ice_skating e50b -icecream ea69 -image e3f4 -image_aspect_ratio e3f5 -image_not_supported f116 -image_search e43f -imagesearch_roller e9b4 -import_contacts e0e0 -import_export e0c3 -important_devices e912 -inbox e156 -incomplete_circle e79b -indeterminate_check_box e909 -info e88e -info_outline e88f -input e890 -insert_chart e24b -insert_chart_outlined e26a -insert_comment e24c -insert_drive_file e24d -insert_emoticon e24e -insert_invitation e24f -insert_link e250 -insert_page_break eaca -insert_photo e251 -insights f092 -install_desktop eb71 -install_mobile eb72 -integration_instructions ef54 -interests e7c8 -interpreter_mode e83b -inventory e179 -inventory_2 e1a1 -invert_colors e891 -invert_colors_off e0c4 -invert_colors_on e891 -ios_share e6b8 -iron e583 -iso e3f6 -javascript eb7c -join_full eaeb -join_inner eaf4 -join_left eaf2 -join_right eaea -kayaking e50c -kebab_dining e842 -key e73c -key_off eb84 -keyboard e312 -keyboard_alt f028 -keyboard_arrow_down e313 -keyboard_arrow_left e314 -keyboard_arrow_right e315 -keyboard_arrow_up e316 -keyboard_backspace e317 -keyboard_capslock e318 -keyboard_command eae0 -keyboard_command_key eae7 -keyboard_control e5d3 -keyboard_control_key eae6 -keyboard_double_arrow_down ead0 -keyboard_double_arrow_left eac3 -keyboard_double_arrow_right eac9 -keyboard_double_arrow_up eacf -keyboard_hide e31a -keyboard_option eadf -keyboard_option_key eae8 -keyboard_return e31b -keyboard_tab e31c -keyboard_voice e31d -king_bed ea45 -kitchen eb47 -kitesurfing e50d -label e892 -label_important e937 -label_important_outline e948 -label_off e9b6 -label_outline e893 -lan eb2f -landscape e3f7 -landslide ebd7 -language e894 -laptop e31e -laptop_chromebook e31f -laptop_mac e320 -laptop_windows e321 -last_page e5dd -launch e895 -layers e53b -layers_clear e53c -leaderboard f20c -leak_add e3f8 -leak_remove e3f9 -leave_bags_at_home f21b -legend_toggle f11b -lens e3fa -lens_blur f029 -library_add e02e -library_add_check e9b7 -library_books e02f -library_music e030 -light f02a -light_mode e518 -lightbulb e0f0 -lightbulb_outline e90f -line_axis ea9a -line_style e919 -line_weight e91a -linear_scale e260 -link e157 -link_off e16f -linked_camera e438 -liquor ea60 -list e896 -list_alt e0ee -live_help e0c6 -live_tv e639 -living f02b -local_activity e53f -local_airport e53d -local_atm e53e -local_attraction e53f -local_bar e540 -local_cafe e541 -local_car_wash e542 -local_convenience_store e543 -local_dining e556 -local_drink e544 -local_fire_department ef55 -local_florist e545 -local_gas_station e546 -local_grocery_store e547 -local_hospital e548 -local_hotel e549 -local_laundry_service e54a -local_library e54b -local_mall e54c -local_movies e54d -local_offer e54e -local_parking e54f -local_pharmacy e550 -local_phone e551 -local_pizza e552 -local_play e553 -local_police ef56 -local_post_office e554 -local_print_shop e555 -local_printshop e555 -local_restaurant e556 -local_see e557 -local_shipping e558 -local_taxi e559 -location_city e7f1 -location_disabled e1b6 -location_history e55a -location_off e0c7 -location_on e0c8 -location_pin f1db -location_searching e1b7 -lock e897 -lock_clock ef57 -lock_open e898 -lock_outline e899 -lock_reset eade -login ea77 -logo_dev ead6 -logout e9ba -looks e3fc -looks_3 e3fb -looks_4 e3fd -looks_5 e3fe -looks_6 e3ff -looks_one e400 -looks_two e401 -loop e028 -loupe e402 -low_priority e16d -loyalty e89a -lte_mobiledata f02c -lte_plus_mobiledata f02d -luggage f235 -lunch_dining ea61 -mail e158 -mail_outline e0e1 -male e58e -man e4eb -manage_accounts f02e -manage_history ebe7 -manage_search f02f -map e55b -maps_home_work f030 -maps_ugc ef58 -margin e9bb -mark_as_unread e9bc -mark_chat_read f18b -mark_chat_unread f189 -mark_email_read f18c -mark_email_unread f18a -mark_unread_chat_alt eb9d -markunread e159 -markunread_mailbox e89b -masks f218 -maximize e930 -media_bluetooth_off f031 -media_bluetooth_on f032 -mediation efa7 -medical_information ebed -medical_services f109 -medication f033 -medication_liquid ea87 -meeting_room eb4f -memory e322 -menu e5d2 -menu_book ea19 -menu_open e9bd -merge eb98 -merge_type e252 -message e0c9 -messenger e0ca -messenger_outline e0cb -mic e029 -mic_external_off ef59 -mic_external_on ef5a -mic_none e02a -mic_off e02b -microwave f204 -military_tech ea3f -minimize e931 -minor_crash ebf1 -miscellaneous_services f10c -missed_video_call e073 -mms e618 -mobile_friendly e200 -mobile_off e201 -mobile_screen_share e0e7 -mobiledata_off f034 -mode f097 -mode_comment e253 -mode_edit e254 -mode_edit_outline f035 -mode_night f036 -mode_of_travel e7ce -mode_standby f037 -model_training f0cf -monetization_on e263 -money e57d -money_off e25c -money_off_csred f038 -monitor ef5b -monitor_heart eaa2 -monitor_weight f039 -monochrome_photos e403 -mood e7f2 -mood_bad e7f3 -moped eb28 -more e619 -more_horiz e5d3 -more_time ea5d -more_vert e5d4 -mosque eab2 -motion_photos_auto f03a -motion_photos_off e9c0 -motion_photos_on e9c1 -motion_photos_pause f227 -motion_photos_paused e9c2 -motorcycle e91b -mouse e323 -move_down eb61 -move_to_inbox e168 -move_up eb64 -movie e02c -movie_creation e404 -movie_filter e43a -moving e501 -mp e9c3 -multiline_chart e6df -multiple_stop f1b9 -multitrack_audio e1b8 -museum ea36 -music_note e405 -music_off e440 -music_video e063 -my_library_add e02e -my_library_books e02f -my_library_music e030 -my_location e55c -nat ef5c -nature e406 -nature_people e407 -navigate_before e408 -navigate_next e409 -navigation e55d -near_me e569 -near_me_disabled f1ef -nearby_error f03b -nearby_off f03c -network_cell e1b9 -network_check e640 -network_locked e61a -network_ping ebca -network_wifi e1ba -network_wifi_1_bar ebe4 -network_wifi_2_bar ebd6 -network_wifi_3_bar ebe1 -new_label e609 -new_releases e031 -newspaper eb81 -next_plan ef5d -next_week e16a -nfc e1bb -night_shelter f1f1 -nightlife ea62 -nightlight f03d -nightlight_round ef5e -nights_stay ea46 -no_accounts f03e -no_backpack f237 -no_cell f1a4 -no_crash ebf0 -no_drinks f1a5 -no_encryption e641 -no_encryption_gmailerrorred f03f -no_flash f1a6 -no_food f1a7 -no_luggage f23b -no_meals f1d6 -no_meals_ouline f229 -no_meeting_room eb4e -no_photography f1a8 -no_sim e0cc -no_stroller f1af -no_transfer f1d5 -noise_aware ebec -noise_control_off ebf3 -nordic_walking e50e -north f1e0 -north_east f1e1 -north_west f1e2 -not_accessible f0fe -not_interested e033 -not_listed_location e575 -not_started f0d1 -note e06f -note_add e89c -note_alt f040 -notes e26c -notification_add e399 -notification_important e004 -notifications e7f4 -notifications_active e7f7 -notifications_none e7f5 -notifications_off e7f6 -notifications_on e7f7 -notifications_paused e7f8 -now_wallpaper e1bc -now_widgets e1bd -numbers eac7 -offline_bolt e932 -offline_pin e90a -offline_share e9c5 -ondemand_video e63a -online_prediction f0eb -opacity e91c -open_in_browser e89d -open_in_full f1ce -open_in_new e89e -open_in_new_off e4f6 -open_with e89f -other_houses e58c -outbond f228 -outbound e1ca -outbox ef5f -outdoor_grill ea47 -outgoing_mail f0d2 -outlet f1d4 -outlined_flag e16e -output ebbe -padding e9c8 -pages e7f9 -pageview e8a0 -paid f041 -palette e40a -pan_tool e925 -pan_tool_alt ebb9 -panorama e40b -panorama_fish_eye e40c -panorama_fisheye e40c -panorama_horizontal e40d -panorama_horizontal_select ef60 -panorama_photosphere e9c9 -panorama_photosphere_select e9ca -panorama_vertical e40e -panorama_vertical_select ef61 -panorama_wide_angle e40f -panorama_wide_angle_select ef62 -paragliding e50f -park ea63 -party_mode e7fa -password f042 -pattern f043 -pause e034 -pause_circle e1a2 -pause_circle_filled e035 -pause_circle_outline e036 -pause_presentation e0ea -payment e8a1 -payments ef63 -paypal ea8d -pedal_bike eb29 -pending ef64 -pending_actions f1bb -pentagon eb50 -people e7fb -people_alt ea21 -people_outline e7fc -percent eb58 -perm_camera_mic e8a2 -perm_contact_cal e8a3 -perm_contact_calendar e8a3 -perm_data_setting e8a4 -perm_device_info e8a5 -perm_device_information e8a5 -perm_identity e8a6 -perm_media e8a7 -perm_phone_msg e8a8 -perm_scan_wifi e8a9 -person e7fd -person_add e7fe -person_add_alt ea4d -person_add_alt_1 ef65 -person_add_disabled e9cb -person_off e510 -person_outline e7ff -person_pin e55a -person_pin_circle e56a -person_remove ef66 -person_remove_alt_1 ef67 -person_search f106 -personal_injury e6da -personal_video e63b -pest_control f0fa -pest_control_rodent f0fd -pets e91d -phishing ead7 -phone e0cd -phone_android e324 -phone_bluetooth_speaker e61b -phone_callback e649 -phone_disabled e9cc -phone_enabled e9cd -phone_forwarded e61c -phone_in_talk e61d -phone_iphone e325 -phone_locked e61e -phone_missed e61f -phone_paused e620 -phonelink e326 -phonelink_erase e0db -phonelink_lock e0dc -phonelink_off e327 -phonelink_ring e0dd -phonelink_setup e0de -photo e410 -photo_album e411 -photo_camera e412 -photo_camera_back ef68 -photo_camera_front ef69 -photo_filter e43b -photo_library e413 -photo_size_select_actual e432 -photo_size_select_large e433 -photo_size_select_small e434 -php eb8f -piano e521 -piano_off e520 -picture_as_pdf e415 -picture_in_picture e8aa -picture_in_picture_alt e911 -pie_chart e6c4 -pie_chart_outline f044 -pie_chart_outlined e6c5 -pin f045 -pin_drop e55e -pin_end e767 -pin_invoke e763 -pinch eb38 -pivot_table_chart e9ce -pix eaa3 -place e55f -plagiarism ea5a -play_arrow e037 -play_circle e1c4 -play_circle_fill e038 -play_circle_filled e038 -play_circle_outline e039 -play_disabled ef6a -play_for_work e906 -play_lesson f047 -playlist_add e03b -playlist_add_check e065 -playlist_add_check_circle e7e6 -playlist_add_circle e7e5 -playlist_play e05f -playlist_remove eb80 -plumbing f107 -plus_one e800 -podcasts f048 -point_of_sale f17e -policy ea17 -poll e801 -polyline ebbb -polymer e8ab -pool eb48 -portable_wifi_off e0ce -portrait e416 -post_add ea20 -power e63c -power_input e336 -power_off e646 -power_settings_new e8ac -precision_manufacturing f049 -pregnant_woman e91e -present_to_all e0df -preview f1c5 -price_change f04a -price_check f04b -print e8ad -print_disabled e9cf -priority_high e645 -privacy_tip f0dc -private_connectivity e744 -production_quantity_limits e1d1 -psychology ea4a -public e80b -public_off f1ca -publish e255 -published_with_changes f232 -punch_clock eaa8 -push_pin f10d -qr_code ef6b -qr_code_2 e00a -qr_code_scanner f206 -query_builder e8ae -query_stats e4fc -question_answer e8af -question_mark eb8b -queue e03c -queue_music e03d -queue_play_next e066 -quick_contacts_dialer e0cf -quick_contacts_mail e0d0 -quickreply ef6c -quiz f04c -quora ea98 -r_mobiledata f04d -radar f04e -radio e03e -radio_button_checked e837 -radio_button_off e836 -radio_button_on e837 -radio_button_unchecked e836 -railway_alert e9d1 -ramen_dining ea64 -ramp_left eb9c -ramp_right eb96 -rate_review e560 -raw_off f04f -raw_on f050 -read_more ef6d -real_estate_agent e73a -receipt e8b0 -receipt_long ef6e -recent_actors e03f -recommend e9d2 -record_voice_over e91f -rectangle eb54 -recycling e760 -reddit eaa0 -redeem e8b1 -redo e15a -reduce_capacity f21c -refresh e5d5 -remember_me f051 -remove e15b -remove_circle e15c -remove_circle_outline e15d -remove_done e9d3 -remove_from_queue e067 -remove_moderator e9d4 -remove_red_eye e417 -remove_shopping_cart e928 -reorder e8fe -repeat e040 -repeat_on e9d6 -repeat_one e041 -repeat_one_on e9d7 -replay e042 -replay_10 e059 -replay_30 e05a -replay_5 e05b -replay_circle_filled e9d8 -reply e15e -reply_all e15f -report e160 -report_gmailerrorred f052 -report_off e170 -report_problem e8b2 -request_page f22c -request_quote f1b6 -reset_tv e9d9 -restart_alt f053 -restaurant e56c -restaurant_menu e561 -restore e8b3 -restore_from_trash e938 -restore_page e929 -reviews f054 -rice_bowl f1f5 -ring_volume e0d1 -rocket eba5 -rocket_launch eb9b -roller_skating ebcd -roofing f201 -room e8b4 -room_preferences f1b8 -room_service eb49 -rotate_90_degrees_ccw e418 -rotate_90_degrees_cw eaab -rotate_left e419 -rotate_right e41a -roundabout_left eb99 -roundabout_right eba3 -rounded_corner e920 -route eacd -router e328 -rowing e921 -rss_feed e0e5 -rsvp f055 -rtt e9ad -rule f1c2 -rule_folder f1c9 -run_circle ef6f -running_with_errors e51d -rv_hookup e642 -safety_check ebef -safety_divider e1cc -sailing e502 -sanitizer f21d -satellite e562 -satellite_alt eb3a -save e161 -save_alt e171 -save_as eb60 -saved_search ea11 -savings e2eb -scale eb5f -scanner e329 -scatter_plot e268 -schedule e8b5 -schedule_send ea0a -schema e4fd -school e80c -science ea4b -score e269 -scoreboard ebd0 -screen_lock_landscape e1be -screen_lock_portrait e1bf -screen_lock_rotation e1c0 -screen_rotation e1c1 -screen_rotation_alt ebee -screen_search_desktop ef70 -screen_share e0e2 -screenshot f056 -scuba_diving ebce -sd e9dd -sd_card e623 -sd_card_alert f057 -sd_storage e1c2 -search e8b6 -search_off ea76 -security e32a -security_update f058 -security_update_good f059 -security_update_warning f05a -segment e94b -select_all e162 -self_improvement ea78 -sell f05b -send e163 -send_and_archive ea0c -send_time_extension eadb -send_to_mobile f05c -sensor_door f1b5 -sensor_window f1b4 -sensors e51e -sensors_off e51f -sentiment_dissatisfied e811 -sentiment_neutral e812 -sentiment_satisfied e813 -sentiment_satisfied_alt e0ed -sentiment_very_dissatisfied e814 -sentiment_very_satisfied e815 -set_meal f1ea -settings e8b8 -settings_accessibility f05d -settings_applications e8b9 -settings_backup_restore e8ba -settings_bluetooth e8bb -settings_brightness e8bd -settings_cell e8bc -settings_display e8bd -settings_ethernet e8be -settings_input_antenna e8bf -settings_input_component e8c0 -settings_input_composite e8c1 -settings_input_hdmi e8c2 -settings_input_svideo e8c3 -settings_overscan e8c4 -settings_phone e8c5 -settings_power e8c6 -settings_remote e8c7 -settings_suggest f05e -settings_system_daydream e1c3 -settings_voice e8c8 -severe_cold ebd3 -share e80d -share_arrival_time e524 -share_location f05f -shield e9e0 -shield_moon eaa9 -shop e8c9 -shop_2 e19e -shop_two e8ca -shopify ea9d -shopping_bag f1cc -shopping_basket e8cb -shopping_cart e8cc -shopping_cart_checkout eb88 -short_text e261 -shortcut f060 -show_chart e6e1 -shower f061 -shuffle e043 -shuffle_on e9e1 -shutter_speed e43d -sick f220 -sign_language ebe5 -signal_cellular_0_bar f0a8 -signal_cellular_4_bar e1c8 -signal_cellular_alt e202 -signal_cellular_alt_1_bar ebdf -signal_cellular_alt_2_bar ebe3 -signal_cellular_connected_no_internet_0_bar f0ac -signal_cellular_connected_no_internet_4_bar e1cd -signal_cellular_no_sim e1ce -signal_cellular_nodata f062 -signal_cellular_null e1cf -signal_cellular_off e1d0 -signal_wifi_0_bar f0b0 -signal_wifi_4_bar e1d8 -signal_wifi_4_bar_lock e1d9 -signal_wifi_bad f063 -signal_wifi_connected_no_internet_4 f064 -signal_wifi_off e1da -signal_wifi_statusbar_4_bar f065 -signal_wifi_statusbar_connected_no_internet_4 f066 -signal_wifi_statusbar_null f067 -signpost eb91 -sim_card e32b -sim_card_alert e624 -sim_card_download f068 -single_bed ea48 -sip f069 -skateboarding e511 -skip_next e044 -skip_previous e045 -sledding e512 -slideshow e41b -slow_motion_video e068 -smart_button f1c1 -smart_display f06a -smart_screen f06b -smart_toy f06c -smartphone e32c -smoke_free eb4a -smoking_rooms eb4b -sms e625 -sms_failed e626 -snapchat ea6e -snippet_folder f1c7 -snooze e046 -snowboarding e513 -snowing e80f -snowmobile e503 -snowshoeing e514 -soap f1b2 -social_distance e1cb -sort e164 -sort_by_alpha e053 -sos ebf7 -soup_kitchen e7d3 -source f1c4 -south f1e3 -south_america e7e4 -south_east f1e4 -south_west f1e5 -spa eb4c -space_bar e256 -space_dashboard e66b -spatial_audio ebeb -spatial_audio_off ebe8 -spatial_tracking ebea -speaker e32d -speaker_group e32e -speaker_notes e8cd -speaker_notes_off e92a -speaker_phone e0d2 -speed e9e4 -spellcheck e8ce -splitscreen f06d -spoke e9a7 -sports ea30 -sports_bar f1f3 -sports_baseball ea51 -sports_basketball ea26 -sports_cricket ea27 -sports_esports ea28 -sports_football ea29 -sports_golf ea2a -sports_gymnastics ebc4 -sports_handball ea33 -sports_hockey ea2b -sports_kabaddi ea34 -sports_martial_arts eae9 -sports_mma ea2c -sports_motorsports ea2d -sports_rugby ea2e -sports_score f06e -sports_soccer ea2f -sports_tennis ea32 -sports_volleyball ea31 -square eb36 -square_foot ea49 -ssid_chart eb66 -stacked_bar_chart e9e6 -stacked_line_chart f22b -stadium eb90 -stairs f1a9 -star e838 -star_border e83a -star_border_purple500 f099 -star_half e839 -star_outline f06f -star_purple500 f09a -star_rate f0ec -stars e8d0 -start e089 -stay_current_landscape e0d3 -stay_current_portrait e0d4 -stay_primary_landscape e0d5 -stay_primary_portrait e0d6 -sticky_note_2 f1fc -stop e047 -stop_circle ef71 -stop_screen_share e0e3 -storage e1db -store e8d1 -store_mall_directory e563 -storefront ea12 -storm f070 -straight eb95 -straighten e41c -stream e9e9 -streetview e56e -strikethrough_s e257 -stroller f1ae -style e41d -subdirectory_arrow_left e5d9 -subdirectory_arrow_right e5da -subject e8d2 -subscript f111 -subscriptions e064 -subtitles e048 -subtitles_off ef72 -subway e56f -summarize f071 -sunny e81a -sunny_snowing e819 -superscript f112 -supervised_user_circle e939 -supervisor_account e8d3 -support ef73 -support_agent f0e2 -surfing e515 -surround_sound e049 -swap_calls e0d7 -swap_horiz e8d4 -swap_horizontal_circle e933 -swap_vert e8d5 -swap_vert_circle e8d6 -swap_vertical_circle e8d6 -swipe e9ec -swipe_down eb53 -swipe_down_alt eb30 -swipe_left eb59 -swipe_left_alt eb33 -swipe_right eb52 -swipe_right_alt eb56 -swipe_up eb2e -swipe_up_alt eb35 -swipe_vertical eb51 -switch_access_shortcut e7e1 -switch_access_shortcut_add e7e2 -switch_account e9ed -switch_camera e41e -switch_left f1d1 -switch_right f1d2 -switch_video e41f -synagogue eab0 -sync e627 -sync_alt ea18 -sync_disabled e628 -sync_lock eaee -sync_problem e629 -system_security_update f072 -system_security_update_good f073 -system_security_update_warning f074 -system_update e62a -system_update_alt e8d7 -system_update_tv e8d7 -tab e8d8 -tab_unselected e8d9 -table_bar ead2 -table_chart e265 -table_restaurant eac6 -table_rows f101 -table_view f1be -tablet e32f -tablet_android e330 -tablet_mac e331 -tag e9ef -tag_faces e420 -takeout_dining ea74 -tap_and_play e62b -tapas f1e9 -task f075 -task_alt e2e6 -taxi_alert ef74 -telegram ea6b -temple_buddhist eab3 -temple_hindu eaaf -terminal eb8e -terrain e564 -text_decrease eadd -text_fields e262 -text_format e165 -text_increase eae2 -text_rotate_up e93a -text_rotate_vertical e93b -text_rotation_angledown e93c -text_rotation_angleup e93d -text_rotation_down e93e -text_rotation_none e93f -text_snippet f1c6 -textsms e0d8 -texture e421 -theater_comedy ea66 -theaters e8da -thermostat f076 -thermostat_auto f077 -thumb_down e8db -thumb_down_alt e816 -thumb_down_off_alt e9f2 -thumb_up e8dc -thumb_up_alt e817 -thumb_up_off_alt e9f3 -thumbs_up_down e8dd -thunderstorm ebdb -tiktok ea7e -time_to_leave e62c -timelapse e422 -timeline e922 -timer e425 -timer_10 e423 -timer_10_select f07a -timer_3 e424 -timer_3_select f07b -timer_off e426 -tips_and_updates e79a -tire_repair ebc8 -title e264 -toc e8de -today e8df -toggle_off e9f5 -toggle_on e9f6 -token ea25 -toll e8e0 -tonality e427 -topic f1c8 -touch_app e913 -tour ef75 -toys e332 -track_changes e8e1 -traffic e565 -train e570 -tram e571 -transfer_within_a_station e572 -transform e428 -transgender e58d -transit_enterexit e579 -translate e8e2 -travel_explore e2db -trending_down e8e3 -trending_flat e8e4 -trending_neutral e8e4 -trending_up e8e5 -trip_origin e57b -try f07c -tsunami ebd8 -tty f1aa -tune e429 -tungsten f07d -turn_left eba6 -turn_right ebab -turn_sharp_left eba7 -turn_sharp_right ebaa -turn_slight_left eba4 -turn_slight_right eb9a -turned_in e8e6 -turned_in_not e8e7 -tv e333 -tv_off e647 -two_wheeler e9f9 -u_turn_left eba1 -u_turn_right eba2 -umbrella f1ad -unarchive e169 -undo e166 -unfold_less e5d6 -unfold_more e5d7 -unpublished f236 -unsubscribe e0eb -upcoming f07e -update e923 -update_disabled e075 -upgrade f0fb -upload f09b -upload_file e9fc -usb e1e0 -usb_off e4fa -vaccines e138 -vape_free ebc6 -vaping_rooms ebcf -verified ef76 -verified_user e8e8 -vertical_align_bottom e258 -vertical_align_center e259 -vertical_align_top e25a -vertical_distribute e076 -vertical_split e949 -vibration e62d -video_call e070 -video_camera_back f07f -video_camera_front f080 -video_collection e04a -video_file eb87 -video_label e071 -video_library e04a -video_settings ea75 -video_stable f081 -videocam e04b -videocam_off e04c -videogame_asset e338 -videogame_asset_off e500 -view_agenda e8e9 -view_array e8ea -view_carousel e8eb -view_column e8ec -view_comfortable e42a -view_comfy e42a -view_comfy_alt eb73 -view_compact e42b -view_compact_alt eb74 -view_cozy eb75 -view_day e8ed -view_headline e8ee -view_in_ar e9fe -view_kanban eb7f -view_list e8ef -view_module e8f0 -view_quilt e8f1 -view_sidebar f114 -view_stream e8f2 -view_timeline eb85 -view_week e8f3 -vignette e435 -villa e586 -visibility e8f4 -visibility_off e8f5 -voice_chat e62e -voice_over_off e94a -voicemail e0d9 -volcano ebda -volume_down e04d -volume_down_alt e79c -volume_mute e04e -volume_off e04f -volume_up e050 -volunteer_activism ea70 -vpn_key e0da -vpn_key_off eb7a -vpn_lock e62f -vrpano f082 -wallet_giftcard e8f6 -wallet_membership e8f7 -wallet_travel e8f8 -wallpaper e1bc -warehouse ebb8 -warning e002 -warning_amber f083 -wash f1b1 -watch e334 -watch_later e924 -watch_off eae3 -water f084 -water_damage f203 -water_drop e798 -waterfall_chart ea00 -waves e176 -waving_hand e766 -wb_auto e42c -wb_cloudy e42d -wb_incandescent e42e -wb_iridescent e436 -wb_shade ea01 -wb_sunny e430 -wb_twighlight ea02 -wb_twilight e1c6 -wc e63d -web e051 -web_asset e069 -web_asset_off e4f7 -web_stories e595 -webhook eb92 -wechat ea81 -weekend e16b -west f1e6 -whatsapp ea9c -whatshot e80e -wheelchair_pickup f1ab -where_to_vote e177 -widgets e1bd -wifi e63e -wifi_1_bar e4ca -wifi_2_bar e4d9 -wifi_calling ef77 -wifi_calling_3 f085 -wifi_channel eb6a -wifi_find eb31 -wifi_lock e1e1 -wifi_off e648 -wifi_password eb6b -wifi_protected_setup f0fc -wifi_tethering e1e2 -wifi_tethering_error ead9 -wifi_tethering_error_rounded f086 -wifi_tethering_off f087 -window f088 -wine_bar f1e8 -woman e13e -woo_commerce ea6d -wordpress ea9f -work e8f9 -work_off e942 -work_outline e943 -workspace_premium e7af -workspaces e1a0 -workspaces_filled ea0d -workspaces_outline ea0f -wrap_text e25b -wrong_location ef78 -wysiwyg f1c3 -yard f089 -youtube_searched_for e8fa -zoom_in e8ff -zoom_in_map eb2d -zoom_out e900 -zoom_out_map e56b diff --git a/ui-ngx/src/app/core/services/menu.models.ts b/ui-ngx/src/app/core/services/menu.models.ts index 2043eeadea..47072943be 100644 --- a/ui-ngx/src/app/core/services/menu.models.ts +++ b/ui-ngx/src/app/core/services/menu.models.ts @@ -24,7 +24,6 @@ export interface MenuSection extends HasUUID{ type: MenuSectionType; path: string; icon: string; - isMdiIcon?: boolean; pages?: Array; opened?: boolean; disabled?: boolean; @@ -39,6 +38,5 @@ export interface HomeSection { export interface HomeSectionPlace { name: string; icon: string; - isMdiIcon?: boolean; path: string; } diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index b33c552eb1..924d741cf6 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/ui-ngx/src/app/core/services/menu.service.ts @@ -111,8 +111,7 @@ export class MenuService { name: 'tenant-profile.tenant-profiles', type: 'link', path: '/tenantProfiles', - icon: 'mdi:alpha-t-box', - isMdiIcon: true + icon: 'mdi:alpha-t-box' }, { id: 'resources', @@ -125,16 +124,38 @@ export class MenuService { id: 'widget_library', name: 'widget.widget-library', type: 'link', - path: '/resources/widgets-bundles', - icon: 'now_widgets' + path: '/resources/widgets-library', + icon: 'now_widgets', + pages: [ + { + id: 'widget_types', + name: 'widget.widgets', + type: 'link', + path: '/resources/widgets-library/widget-types', + icon: 'now_widgets' + }, + { + id: 'widgets_bundles', + name: 'widgets-bundle.widgets-bundles', + type: 'link', + path: '/resources/widgets-library/widgets-bundles', + icon: 'now_widgets' + } + ] + }, + { + id: 'images', + name: 'image.gallery', + type: 'link', + path: '/resources/images', + icon: 'filter' }, { id: 'resources_library', name: 'resource.resources-library', type: 'link', path: '/resources/resources-library', - icon: 'mdi:rhombus-split', - isMdiIcon: true + icon: 'mdi:rhombus-split' } ] }, @@ -144,7 +165,6 @@ export class MenuService { type: 'link', path: '/notification', icon: 'mdi:message-badge', - isMdiIcon: true, pages: [ { id: 'notification_inbox', @@ -176,8 +196,7 @@ export class MenuService { fullName: 'notification.notification-templates', type: 'link', path: '/notification/templates', - icon: 'mdi:message-draw', - isMdiIcon: true + icon: 'mdi:message-draw' }, { id: 'notification_rules', @@ -185,8 +204,7 @@ export class MenuService { fullName: 'notification.notification-rules', type: 'link', path: '/notification/rules', - icon: 'mdi:message-cog', - isMdiIcon: true + icon: 'mdi:message-cog' } ] }, @@ -218,8 +236,7 @@ export class MenuService { fullName: 'admin.notifications-settings', type: 'link', path: '/settings/notifications', - icon: 'mdi:message-badge', - isMdiIcon: true + icon: 'mdi:message-badge' }, { id: 'queues', @@ -250,16 +267,14 @@ export class MenuService { name: 'admin.2fa.2fa', type: 'link', path: '/security-settings/2fa', - icon: 'mdi:two-factor-authentication', - isMdiIcon: true + icon: 'mdi:two-factor-authentication' }, { id: 'oauth2', name: 'admin.oauth2.oauth2', type: 'link', path: '/security-settings/oauth2', - icon: 'mdi:shield-account', - isMdiIcon: true + icon: 'mdi:shield-account' } ] } @@ -281,7 +296,6 @@ export class MenuService { { name: 'tenant-profile.tenant-profiles', icon: 'mdi:alpha-t-box', - isMdiIcon: true, path: '/tenantProfiles' }, ] @@ -292,7 +306,7 @@ export class MenuService { { name: 'widget.widget-library', icon: 'now_widgets', - path: '/widgets-bundles' + path: '/resources/widgets-library', } ] }, @@ -327,7 +341,6 @@ export class MenuService { { name: 'admin.2fa.2fa', icon: 'mdi:two-factor-authentication', - isMdiIcon: true, path: '/settings/2fa' }, { @@ -361,8 +374,7 @@ export class MenuService { name: 'alarm.alarms', type: 'link', path: '/alarms', - icon: 'mdi:alert-outline', - isMdiIcon: true + icon: 'mdi:alert-outline' }, { id: 'dashboards', @@ -413,16 +425,14 @@ export class MenuService { name: 'device-profile.device-profiles', type: 'link', path: '/profiles/deviceProfiles', - icon: 'mdi:alpha-d-box', - isMdiIcon: true + icon: 'mdi:alpha-d-box' }, { id: 'asset_profiles', name: 'asset-profile.asset-profiles', type: 'link', path: '/profiles/assetProfiles', - icon: 'mdi:alpha-a-box', - isMdiIcon: true + icon: 'mdi:alpha-a-box' } ] }, @@ -505,16 +515,38 @@ export class MenuService { id: 'widget_library', name: 'widget.widget-library', type: 'link', - path: '/resources/widgets-bundles', - icon: 'now_widgets' + path: '/resources/widgets-library', + icon: 'now_widgets', + pages: [ + { + id: 'widget_types', + name: 'widget.widgets', + type: 'link', + path: '/resources/widgets-library/widget-types', + icon: 'now_widgets' + }, + { + id: 'widgets_bundles', + name: 'widgets-bundle.widgets-bundles', + type: 'link', + path: '/resources/widgets-library/widgets-bundles', + icon: 'now_widgets' + } + ] + }, + { + id: 'images', + name: 'image.gallery', + type: 'link', + path: '/resources/images', + icon: 'filter' }, { id: 'resources_library', name: 'resource.resources-library', type: 'link', path: '/resources/resources-library', - icon: 'mdi:rhombus-split', - isMdiIcon: true + icon: 'mdi:rhombus-split' } ] }, @@ -524,7 +556,6 @@ export class MenuService { type: 'link', path: '/notification', icon: 'mdi:message-badge', - isMdiIcon: true, pages: [ { id: 'notification_inbox', @@ -556,8 +587,7 @@ export class MenuService { fullName: 'notification.notification-templates', type: 'link', path: '/notification/templates', - icon: 'mdi:message-draw', - isMdiIcon: true + icon: 'mdi:message-draw' }, { id: 'notification_rules', @@ -565,8 +595,7 @@ export class MenuService { fullName: 'notification.notification-rules', type: 'link', path: '/notification/rules', - icon: 'mdi:message-cog', - isMdiIcon: true + icon: 'mdi:message-cog' } ] }, @@ -598,8 +627,7 @@ export class MenuService { fullName: 'admin.notifications-settings', type: 'link', path: '/settings/notifications', - icon: 'mdi:message-badge', - isMdiIcon: true + icon: 'mdi:message-badge' }, { id: 'repository_settings', @@ -673,7 +701,6 @@ export class MenuService { { name: 'asset-profile.asset-profiles', icon: 'mdi:alpha-a-box', - isMdiIcon: true, path: '/profiles/assetProfiles' } ] @@ -689,7 +716,6 @@ export class MenuService { { name: 'device-profile.device-profiles', icon: 'mdi:alpha-d-box', - isMdiIcon: true, path: '/profiles/deviceProfiles' }, { @@ -814,8 +840,7 @@ export class MenuService { name: 'alarm.alarms', type: 'link', path: '/alarms', - icon: 'mdi:alert-outline', - isMdiIcon: true + icon: 'mdi:alert-outline' }, { id: 'dashboards', @@ -874,7 +899,6 @@ export class MenuService { type: 'link', path: '/notification', icon: 'mdi:message-badge', - isMdiIcon: true, pages: [ { id: 'notification_inbox', diff --git a/ui-ngx/src/app/core/services/resources.service.ts b/ui-ngx/src/app/core/services/resources.service.ts index 70084153e8..727869e955 100644 --- a/ui-ngx/src/app/core/services/resources.service.ts +++ b/ui-ngx/src/app/core/services/resources.service.ts @@ -33,7 +33,7 @@ import { AuthService } from '@core/auth/auth.service'; import { select, Store } from '@ngrx/store'; import { selectIsAuthenticated } from '@core/auth/auth.selectors'; import { AppState } from '@core/core.state'; -import { tap } from 'rxjs/operators'; +import { map, tap } from 'rxjs/operators'; declare const System; @@ -47,6 +47,7 @@ export interface ModulesWithFactories { }) export class ResourcesService { + private loadedJsonResources: { [url: string]: ReplaySubject } = {}; private loadedResources: { [url: string]: ReplaySubject } = {}; private loadedModules: { [url: string]: ReplaySubject[]> } = {}; private loadedModulesAndFactories: { [url: string]: ReplaySubject } = {}; @@ -61,6 +62,32 @@ export class ResourcesService { this.store.pipe(select(selectIsAuthenticated)).subscribe(() => this.clearModulesCache()); } + public loadJsonResource(url: string, postProcess?: (data: T) => T): Observable { + if (this.loadedJsonResources[url]) { + return this.loadedJsonResources[url].asObservable(); + } + const subject = new ReplaySubject(); + this.loadedJsonResources[url] = subject; + const req$ = (url.endsWith('.raw') || url.endsWith('.svg') ? + this.http.get(url, {responseType: 'text'}) : this.http.get(url)) as Observable; + req$.subscribe( + { + next: (o) => { + if (postProcess) { + o = postProcess(o); + } + this.loadedJsonResources[url].next(o); + this.loadedJsonResources[url].complete(); + }, + error: () => { + this.loadedJsonResources[url].error(new Error(`Unable to load ${url}`)); + delete this.loadedJsonResources[url]; + } + } + ); + return subject.asObservable(); + } + public loadResource(url: string): Observable { if (this.loadedResources[url]) { return this.loadedResources[url].asObservable(); @@ -79,6 +106,37 @@ export class ResourcesService { return this.loadResourceByType(fileType, url); } + public downloadResource(downloadUrl: string): Observable { + return this.http.get(downloadUrl, { + responseType: 'arraybuffer', + observe: 'response' + }).pipe( + map((response) => { + const headers = response.headers; + const filename = headers.get('x-filename'); + const contentType = headers.get('content-type'); + const linkElement = document.createElement('a'); + try { + const blob = new Blob([response.body], {type: contentType}); + const url = URL.createObjectURL(blob); + linkElement.setAttribute('href', url); + linkElement.setAttribute('download', filename); + const clickEvent = new MouseEvent('click', + { + view: window, + bubbles: true, + cancelable: false + } + ); + linkElement.dispatchEvent(clickEvent); + return null; + } catch (e) { + throw e; + } + }) + ); + } + public loadFactories(resourceId: string | TbResourceId, modulesMap: IModulesMap): Observable { const url = this.getDownloadUrl(resourceId); if (this.loadedModulesAndFactories[url]) { diff --git a/ui-ngx/src/app/core/services/script/node-script-test.service.ts b/ui-ngx/src/app/core/services/script/node-script-test.service.ts index 1a33275111..fa30934f06 100644 --- a/ui-ngx/src/app/core/services/script/node-script-test.service.ts +++ b/ui-ngx/src/app/core/services/script/node-script-test.service.ts @@ -23,8 +23,8 @@ import { NodeScriptTestDialogComponent, NodeScriptTestDialogData } from '@shared/components/dialog/node-script-test-dialog.component'; -import { sortObjectKeys } from '@core/utils'; import { ScriptLanguage } from '@shared/models/rule-node.models'; +import { DebugRuleNodeEventBody } from '@shared/models/event.models'; @Injectable({ providedIn: 'root' @@ -37,56 +37,52 @@ export class NodeScriptTestService { testNodeScript(script: string, scriptType: string, functionTitle: string, functionName: string, argNames: string[], ruleNodeId: string, helpId?: string, - scriptLang?: ScriptLanguage): Observable { - if (ruleNodeId) { + scriptLang?: ScriptLanguage, debugEventBody?: DebugRuleNodeEventBody): Observable { + if (ruleNodeId && !debugEventBody) { return this.ruleChainService.getLatestRuleNodeDebugInput(ruleNodeId).pipe( switchMap((debugIn) => { - let msg: any; - let metadata: {[key: string]: string}; - let msgType: string; - if (debugIn) { - if (debugIn.data) { - try { - msg = JSON.parse(debugIn.data); - } catch (e) {} - } - if (debugIn.metadata) { - try { - metadata = JSON.parse(debugIn.metadata); - } catch (e) {} - } - msgType = debugIn.msgType; - } return this.openTestScriptDialog(script, scriptType, functionTitle, - functionName, argNames, msg, metadata, msgType, helpId, scriptLang); + functionName, argNames, debugIn, helpId, scriptLang); }) ); } else { return this.openTestScriptDialog(script, scriptType, functionTitle, - functionName, argNames, null, null, null, helpId, scriptLang); + functionName, argNames, debugEventBody, helpId, scriptLang); } } - private openTestScriptDialog(script: string, scriptType: string, - functionTitle: string, functionName: string, argNames: string[], - msg?: any, metadata?: {[key: string]: string}, msgType?: string, helpId?: string, + private openTestScriptDialog(script: string, scriptType: string, functionTitle: string, functionName: string, + argNames: string[], eventBody: DebugRuleNodeEventBody, helpId?: string, scriptLang?: ScriptLanguage): Observable { + let msg: any; + let metadata: {[key: string]: string}; + let msgType: string; + if (eventBody && eventBody.data) { + try { + msg = JSON.parse(eventBody.data); + } catch (e) {} + } if (!msg) { msg = { temperature: 22.4, humidity: 78 }; } + if (eventBody && eventBody.metadata) { + try { + metadata = JSON.parse(eventBody.metadata); + } catch (e) {} + } if (!metadata) { metadata = { deviceName: 'Test Device', deviceType: 'default', ts: new Date().getTime() + '' }; - } else { - metadata = sortObjectKeys(metadata); } - if (!msgType) { + if (eventBody && eventBody.msgType) { + msgType = eventBody.msgType; + } else { msgType = 'POST_TELEMETRY_REQUEST'; } return this.dialog.open(NodeScriptTestDialogComponent, diff --git a/ui-ngx/src/app/core/services/utils.service.ts b/ui-ngx/src/app/core/services/utils.service.ts index f82c6c3072..a6065dfe62 100644 --- a/ui-ngx/src/app/core/services/utils.service.ts +++ b/ui-ngx/src/app/core/services/utils.service.ts @@ -21,32 +21,31 @@ import { Inject, Injectable, NgZone } from '@angular/core'; import { WINDOW } from '@core/services/window.service'; import { ExceptionData } from '@app/shared/models/error.models'; import { + base64toObj, + base64toString, baseUrl, createLabelFromDatasource, deepClone, deleteNullProperties, - guid, hashCode, + guid, + hashCode, isDefined, isDefinedAndNotNull, isString, isUndefined, objToBase64, - objToBase64URI, - base64toString, - base64toObj + objToBase64URI } from '@core/utils'; import { WindowMessage } from '@shared/models/window-message.model'; import { TranslateService } from '@ngx-translate/core'; import { customTranslationsPrefix, i18nPrefix } from '@app/shared/models/constants'; import { DataKey, Datasource, DatasourceType, KeyInfo } from '@shared/models/widget.models'; -import { EntityType } from '@shared/models/entity-type.models'; import { DataKeyType } from '@app/shared/models/telemetry/telemetry.models'; -import { alarmFields } from '@shared/models/alarm.models'; +import { alarmFields, alarmSeverityTranslations, alarmStatusTranslations } from '@shared/models/alarm.models'; import { materialColors } from '@app/shared/models/material.models'; import { WidgetInfo } from '@home/models/widget-component.models'; import jsonSchemaDefaults from 'json-schema-defaults'; -import materialIconsCodepoints from '!raw-loader!./material-icons-codepoints.raw'; -import { Observable, of, ReplaySubject } from 'rxjs'; +import { Observable } from 'rxjs'; import { publishReplay, refCount } from 'rxjs/operators'; import { WidgetContext } from '@app/modules/home/models/widget-component.models'; import { @@ -56,6 +55,8 @@ import { TelemetryType } from '@shared/models/telemetry/telemetry.models'; import { EntityId } from '@shared/models/id/entity-id'; +import { DatePipe } from '@angular/common'; +import { entityTypeTranslations } from '@shared/models/entity-type.models'; const i18nRegExp = new RegExp(`{${i18nPrefix}:[^{}]+}`, 'g'); @@ -86,13 +87,6 @@ const defaultAlarmFields: Array = [ alarmFields.status.keyName ]; -const commonMaterialIcons: Array = ['more_horiz', 'more_vert', 'open_in_new', - 'visibility', 'play_arrow', 'arrow_back', 'arrow_downward', - 'arrow_forward', 'arrow_upwards', 'close', 'refresh', 'menu', 'show_chart', 'multiline_chart', 'pie_chart', 'insert_chart', 'people', - 'person', 'domain', 'devices_other', 'now_widgets', 'dashboards', 'map', 'pin_drop', 'my_location', 'extension', 'search', - 'settings', 'notifications', 'notifications_active', 'info', 'info_outline', 'warning', 'list', 'file_download', 'import_export', - 'share', 'add', 'edit', 'done', 'delete']; - // @dynamic @Injectable({ providedIn: 'root' @@ -121,10 +115,9 @@ export class UtilsService { defaultAlarmDataKeys: Array = []; - materialIcons: Array = []; - constructor(@Inject(WINDOW) private window: Window, private zone: NgZone, + private datePipe: DatePipe, private translate: TranslateService) { let frame: Element = null; try { @@ -180,6 +173,27 @@ export class UtilsService { return deepClone(this.defaultAlarmDataKeys); } + public defaultAlarmFieldContent(key: DataKey | {name: string}, value: any): string { + if (isDefined(value)) { + const alarmField = alarmFields[key.name]; + if (alarmField) { + if (alarmField.time) { + return value ? this.datePipe.transform(value, 'yyyy-MM-dd HH:mm:ss') : ''; + } else if (alarmField === alarmFields.severity) { + return this.translate.instant(alarmSeverityTranslations.get(value)); + } else if (alarmField === alarmFields.status) { + return alarmStatusTranslations.get(value) ? this.translate.instant(alarmStatusTranslations.get(value)) : value; + } else if (alarmField === alarmFields.originatorType) { + return this.translate.instant(entityTypeTranslations.get(value).type); + } else if (alarmField.value === alarmFields.assignee.value) { + return ''; + } + } + return value; + } + return ''; + } + public generateObjectFromJsonSchema(schema: any): any { const obj = jsonSchemaDefaults(schema); deleteNullProperties(obj); @@ -282,13 +296,9 @@ export class UtilsService { public validateDatasources(datasources: Array): Array { datasources.forEach((datasource) => { - // @ts-ignore - if (datasource.type === 'device') { - datasource.type = DatasourceType.entity; - datasource.entityType = EntityType.DEVICE; - if (datasource.deviceId) { - datasource.entityId = datasource.deviceId; - } else if (datasource.deviceAliasId) { + if (datasource.type === DatasourceType.device) { + if (datasource.deviceAliasId) { + datasource.type = DatasourceType.entity; datasource.entityAliasId = datasource.deviceAliasId; } if (datasource.deviceName) { @@ -310,31 +320,6 @@ export class UtilsService { return datasources; } - public getMaterialIcons(): Observable> { - if (this.materialIcons.length) { - return of(this.materialIcons); - } else { - const materialIconsSubject = new ReplaySubject>(); - this.zone.runOutsideAngular(() => { - const codepointsArray = materialIconsCodepoints - .split('\n') - .filter((codepoint) => codepoint && codepoint.length); - codepointsArray.forEach((codepoint) => { - const values = codepoint.split(' '); - if (values && values.length === 2) { - this.materialIcons.push(values[0]); - } - }); - materialIconsSubject.next(this.materialIcons); - }); - return materialIconsSubject.asObservable(); - } - } - - public getCommonMaterialIcons(): Array { - return commonMaterialIcons; - } - public getMaterialColor(index: number) { const colorIndex = index % materialColors.length; return materialColors[colorIndex].value; @@ -415,7 +400,7 @@ export class UtilsService { public stringToHslColor(str: string, saturationPercentage: number, lightnessPercentage: number): string { if (str && str.length) { - let hue = hashCode(str) % 360; + const hue = hashCode(str) % 360; return `hsl(${hue}, ${saturationPercentage}%, ${lightnessPercentage}%)`; } } diff --git a/ui-ngx/src/app/core/translate/missing-translate-handler.ts b/ui-ngx/src/app/core/translate/missing-translate-handler.ts index d5202a22ae..4d89e1453d 100644 --- a/ui-ngx/src/app/core/translate/missing-translate-handler.ts +++ b/ui-ngx/src/app/core/translate/missing-translate-handler.ts @@ -20,7 +20,19 @@ import { customTranslationsPrefix } from '@app/shared/models/constants'; export class TbMissingTranslationHandler implements MissingTranslationHandler { handle(params: MissingTranslationHandlerParams) { if (params.key && !params.key.startsWith(customTranslationsPrefix)) { - console.warn('Translation for ' + params.key + ' doesn\'t exist'); + console.warn('Translation for \'' + params.key + '\' doesn\'t exist'); + let translations: any; + const parts = params.key.split('.'); + for (let i=parts.length-1; i>=0; i--) { + const newTranslations = {}; + if (i === parts.length-1) { + newTranslations[parts[i]] = params.key; + } else { + newTranslations[parts[i]] = translations; + } + translations = newTranslations; + } + params.translateService.setTranslation(params.translateService.currentLang, translations, true); } } } diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts index 4018fd491f..feeb5de02f 100644 --- a/ui-ngx/src/app/core/utils.ts +++ b/ui-ngx/src/app/core/utils.ts @@ -15,12 +15,12 @@ /// import _ from 'lodash'; -import { Observable, Subject } from 'rxjs'; +import { from, Observable, Subject } from 'rxjs'; import { finalize, share } from 'rxjs/operators'; import { Datasource, DatasourceData, FormattedData, ReplaceInfo } from '@app/shared/models/widget.models'; import { EntityId } from '@shared/models/id/entity-id'; import { NULL_UUID } from '@shared/models/id/has-uuid'; -import { EntityType, baseDetailsPageByEntityType } from '@shared/models/entity-type.models'; +import { baseDetailsPageByEntityType, EntityType } from '@shared/models/entity-type.models'; import { HttpErrorResponse } from '@angular/common/http'; import { TranslateService } from '@ngx-translate/core'; import { serverErrorCodesTranslations } from '@shared/models/constants'; @@ -126,20 +126,11 @@ export function isString(value: any): boolean { return typeof value === 'string'; } -export function isEmpty(obj: any): boolean { - for (const key of Object.keys(obj)) { - if (Object.prototype.hasOwnProperty.call(obj, key)) { - return false; - } - } - return true; -} - export function isLiteralObject(value: any) { return (!!value) && (value.constructor === Object); } -export function formatValue(value: any, dec?: number, units?: string, showZeroDecimals?: boolean): string | undefined { +export const formatValue = (value: any, dec?: number, units?: string, showZeroDecimals?: boolean): string | undefined => { if (isDefinedAndNotNull(value) && isNumeric(value) && (isDefinedAndNotNull(dec) || isDefinedAndNotNull(units) || Number(value).toString() === value)) { let formatted: string | number = Number(value); @@ -159,6 +150,16 @@ export function formatValue(value: any, dec?: number, units?: string, showZeroDe } } +export const formatNumberValue = (value: any, dec?: number): number | undefined => { + if (isDefinedAndNotNull(value) && isNumeric(value)) { + let formatted: string | number = Number(value); + if (isDefinedAndNotNull(dec)) { + formatted = formatted.toFixed(dec); + } + return Number(formatted); + } +} + export function objectValues(obj: any): any[] { return Object.keys(obj).map(e => obj[e]); } @@ -189,9 +190,7 @@ export function objToBase64(obj: any): string { } export function base64toString(b64Encoded: string): string { - return decodeURIComponent(atob(b64Encoded).split('').map((c) => { - return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); - }).join('')); + return decodeURIComponent(atob(b64Encoded).split('').map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)).join('')); } export function objToBase64URI(obj: any): string { @@ -199,12 +198,24 @@ export function objToBase64URI(obj: any): string { } export function base64toObj(b64Encoded: string): any { - const json = decodeURIComponent(atob(b64Encoded).split('').map((c) => { - return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); - }).join('')); + const json = decodeURIComponent(atob(b64Encoded).split('').map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)).join('')); return JSON.parse(json); } +export function stringToBase64(value: string): string { + return btoa(encodeURIComponent(value).replace(/%([0-9A-F]{2})/g, + function toSolidBytes(match, p1) { + return String.fromCharCode(Number('0x' + p1)); + })); +} + +export const blobToBase64 = (blob: Blob): Observable => from(new Promise((resolve) => { + const reader = new FileReader(); + reader.onloadend = () => resolve(reader.result as string); + reader.readAsDataURL(blob); + } + )); + const scrollRegex = /(auto|scroll)/; function parentNodes(node: Node, nodes: Node[]): Node[] { @@ -320,9 +331,31 @@ export function extractType(target: any, keysOfProps: (keyof T return _.pick(target, keysOfProps); } -export function isEqual(a: any, b: any): boolean { - return _.isEqual(a, b); -} +export const isEqual = (a: any, b: any): boolean => _.isEqual(a, b); + +export const isEmpty = (a: any): boolean => _.isEmpty(a); + +export const unset = (object: any, path: string | symbol): boolean => _.unset(object, path); + +export const isEqualIgnoreUndefined = (a: any, b: any): boolean => { + if (a === b) { + return true; + } + if (isDefinedAndNotNull(a) && isDefinedAndNotNull(b)) { + return isEqual(a, b); + } else { + return (isUndefinedOrNull(a) || !a) && (isUndefinedOrNull(b) || !b); + } +}; + +export const isArraysEqualIgnoreUndefined = (a: any[], b: any[]): boolean => { + const res = isEqualIgnoreUndefined(a, b); + if (!res) { + return (isUndefinedOrNull(a) || !a?.length) && (isUndefinedOrNull(b) || !b?.length); + } else { + return res; + } +}; export function mergeDeep(target: T, ...sources: T[]): T { return _.merge(target, ...sources); @@ -342,9 +375,7 @@ const SNAKE_CASE_REGEXP = /[A-Z]/g; export function snakeCase(name: string, separator: string): string { separator = separator || '_'; - return name.replace(SNAKE_CASE_REGEXP, (letter, pos) => { - return (pos ? separator : '') + letter.toLowerCase(); - }); + return name.replace(SNAKE_CASE_REGEXP, (letter, pos) => (pos ? separator : '') + letter.toLowerCase()); } export function getDescendantProp(obj: any, path: string): any { @@ -368,7 +399,7 @@ export function insertVariable(pattern: string, name: string, value: any): strin return result; } -export function createLabelFromDatasource(datasource: Datasource, pattern: string): string { +export const createLabelFromDatasource = (datasource: Datasource, pattern: string): string => { let label = pattern; if (!datasource) { return label; @@ -393,20 +424,23 @@ export function createLabelFromDatasource(datasource: Datasource, pattern: strin match = varsRegex.exec(pattern); } return label; -} +}; + +export const hasDatasourceLabelsVariables = (pattern: string): boolean => varsRegex.test(pattern) !== null; -export function formattedDataFormDatasourceData(input: DatasourceData[], dataIndex?: number): FormattedData[] { +export function formattedDataFormDatasourceData(input: DatasourceData[], dataIndex?: number, ts?: number): FormattedData[] { return _(input).groupBy(el => el.datasource.entityName + el.datasource.entityType) .values().value().map((entityArray, i) => { const datasource = entityArray[0].datasource; const obj = formattedDataFromDatasource(datasource, i); entityArray.filter(el => el.data.length).forEach(el => { const index = isDefined(dataIndex) ? dataIndex : el.data.length - 1; - if (!obj.hasOwnProperty(el.dataKey.label) || el.data[index][1] !== '') { - obj[el.dataKey.label] = el.data[index][1]; - obj[el.dataKey.label + '|ts'] = el.data[index][0]; + const dataSet = isDefined(ts) ? el.data.find(data => data[0] === ts) : el.data[index]; + if (dataSet !== undefined && (!obj.hasOwnProperty(el.dataKey.label) || dataSet[1] !== '')) { + obj[el.dataKey.label] = dataSet[1]; + obj[el.dataKey.label + '|ts'] = dataSet[0]; if (el.dataKey.label.toLowerCase() === 'type') { - obj.deviceType = el.data[index][1]; + obj.deviceType = dataSet[1]; } } }); @@ -681,7 +715,7 @@ export function getEntityDetailsPageURL(id: string, entityType: EntityType): str } export function parseHttpErrorMessage(errorResponse: HttpErrorResponse, - translate: TranslateService, responseType?: string): {message: string, timeout: number} { + translate: TranslateService, responseType?: string): {message: string; timeout: number} { let error = null; let errorMessage: string; let timeout = 0; @@ -741,7 +775,7 @@ export function genNextLabel(name: string, datasources: Datasource[]): string { if (datasource) { if (datasource.dataKeys) { datasource.dataKeys.forEach((dataKey) => { - if (dataKey.label === label) { + if (dataKey?.label === label) { i++; label = name + ' ' + i; matches = true; @@ -750,7 +784,7 @@ export function genNextLabel(name: string, datasources: Datasource[]): string { } if (datasource.latestDataKeys) { datasource.latestDataKeys.forEach((dataKey) => { - if (dataKey.label === label) { + if (dataKey?.label === label) { i++; label = name + ' ' + i; matches = true; @@ -763,3 +797,34 @@ export function genNextLabel(name: string, datasources: Datasource[]): string { } return label; } + +export const getOS = (): string => { + const userAgent = window.navigator.userAgent.toLowerCase(); + const macosPlatforms = /(macintosh|macintel|macppc|mac68k|macos|mac_powerpc)/i; + const windowsPlatforms = /(win32|win64|windows|wince)/i; + const iosPlatforms = /(iphone|ipad|ipod|darwin|ios)/i; + let os = null; + + if (macosPlatforms.test(userAgent)) { + os = 'macos'; + } else if (iosPlatforms.test(userAgent)) { + os = 'ios'; + } else if (windowsPlatforms.test(userAgent)) { + os = 'windows'; + } else if (/android/.test(userAgent)) { + os = 'android'; + } else if (/linux/.test(userAgent)) { + os = 'linux'; + } + + return os; +}; + + +export const camelCase = (str: string): string => { + return _.camelCase(str); +}; + +export const convertKeysToCamelCase = (obj: Record): Record => { + return _.mapKeys(obj, (value, key) => _.camelCase(key)); +}; diff --git a/ui-ngx/src/app/core/ws/notification-websocket.service.ts b/ui-ngx/src/app/core/ws/notification-websocket.service.ts index 8f219c029d..cb33c23635 100644 --- a/ui-ngx/src/app/core/ws/notification-websocket.service.ts +++ b/ui-ngx/src/app/core/ws/notification-websocket.service.ts @@ -15,117 +15,45 @@ /// import { Inject, Injectable, NgZone } from '@angular/core'; +import { + TelemetryPluginCmdsWrapper, + TelemetrySubscriber, + WebsocketDataMsg +} from '@shared/models/telemetry/telemetry.models'; +import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { AuthService } from '@core/auth/auth.service'; import { WINDOW } from '@core/services/window.service'; -import { - isNotificationCountUpdateMsg, - isNotificationsUpdateMsg, - MarkAllAsReadCmd, - MarkAsReadCmd, - NotificationCountUpdate, - NotificationPluginCmdWrapper, - NotificationSubscriber, - NotificationsUpdate, - UnreadCountSubCmd, - UnreadSubCmd, - UnsubscribeCmd, - WebsocketNotificationMsg -} from '@shared/models/websocket/notification-ws.models'; import { WebsocketService } from '@core/ws/websocket.service'; - // @dynamic @Injectable({ providedIn: 'root' }) -export class NotificationWebsocketService extends WebsocketService { +export class NotificationWebsocketService extends WebsocketService { - cmdWrapper: NotificationPluginCmdWrapper; - - constructor(protected store: Store, + constructor(private telemetryWebsocketService: TelemetryWebsocketService, + protected store: Store, protected authService: AuthService, protected ngZone: NgZone, @Inject(WINDOW) protected window: Window) { - super(store, authService, ngZone, 'api/ws/plugins/notifications', new NotificationPluginCmdWrapper(), window); - this.errorName = 'WebSocket Notification Error'; + super(store, authService, ngZone, 'api/ws/plugins/telemetry', new TelemetryPluginCmdsWrapper(), window); } - public subscribe(subscriber: NotificationSubscriber) { - this.isActive = true; - subscriber.subscriptionCommands.forEach( - (subscriptionCommand) => { - const cmdId = this.nextCmdId(); - this.subscribersMap.set(cmdId, subscriber); - subscriptionCommand.cmdId = cmdId; - if (subscriptionCommand instanceof UnreadCountSubCmd) { - this.cmdWrapper.unreadCountSubCmd = subscriptionCommand; - } else if (subscriptionCommand instanceof UnreadSubCmd) { - this.cmdWrapper.unreadSubCmd = subscriptionCommand; - } else if (subscriptionCommand instanceof MarkAsReadCmd) { - this.cmdWrapper.markAsReadCmd = subscriptionCommand; - this.subscribersMap.delete(cmdId); - } else if (subscriptionCommand instanceof MarkAllAsReadCmd) { - this.cmdWrapper.markAllAsReadCmd = subscriptionCommand; - this.subscribersMap.delete(cmdId); - } - } - ); - if (this.cmdWrapper.unreadCountSubCmd || this.cmdWrapper.unreadSubCmd) { - this.subscribersCount++; - } - this.publishCommands(); + public subscribe(subscriber: TelemetrySubscriber) { + this.telemetryWebsocketService.subscribe(subscriber); } - public update(subscriber: NotificationSubscriber) { - if (!this.isReconnect) { - subscriber.subscriptionCommands.forEach( - (subscriptionCommand) => { - if (subscriptionCommand.cmdId && subscriptionCommand instanceof UnreadSubCmd) { - this.cmdWrapper.unreadSubCmd = subscriptionCommand; - } - } - ); - this.publishCommands(); - } + public update(subscriber: TelemetrySubscriber) { + this.telemetryWebsocketService.update(subscriber); } - public unsubscribe(subscriber: NotificationSubscriber) { - if (this.isActive) { - subscriber.subscriptionCommands.forEach( - (subscriptionCommand) => { - if (subscriptionCommand instanceof UnreadCountSubCmd - || subscriptionCommand instanceof UnreadSubCmd) { - const unreadCountUnsubscribeCmd = new UnsubscribeCmd(); - unreadCountUnsubscribeCmd.cmdId = subscriptionCommand.cmdId; - this.cmdWrapper.unsubCmd = unreadCountUnsubscribeCmd; - } - const cmdId = subscriptionCommand.cmdId; - if (cmdId) { - this.subscribersMap.delete(cmdId); - } - } - ); - this.reconnectSubscribers.delete(subscriber); - this.subscribersCount--; - this.publishCommands(); - } + public unsubscribe(subscriber: TelemetrySubscriber) { + this.telemetryWebsocketService.unsubscribe(subscriber); } - processOnMessage(message: WebsocketNotificationMsg) { - let subscriber: NotificationSubscriber; - if (isNotificationCountUpdateMsg(message)) { - subscriber = this.subscribersMap.get(message.cmdId); - if (subscriber) { - subscriber.onNotificationCountUpdate(new NotificationCountUpdate(message)); - } - } else if (isNotificationsUpdateMsg(message)) { - subscriber = this.subscribersMap.get(message.cmdId); - if (subscriber) { - subscriber.onNotificationsUpdate(new NotificationsUpdate(message)); - } - } + processOnMessage(message: WebsocketDataMsg) { + this.telemetryWebsocketService.processOnMessage(message); } - } diff --git a/ui-ngx/src/app/core/ws/public-api.ts b/ui-ngx/src/app/core/ws/public-api.ts new file mode 100644 index 0000000000..d0f5fa8832 --- /dev/null +++ b/ui-ngx/src/app/core/ws/public-api.ts @@ -0,0 +1,19 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +export * from './notification-websocket.service'; +export * from './telemetry-websocket.service'; +export * from './websocket.service'; diff --git a/ui-ngx/src/app/core/ws/telemetry-websocket.service.ts b/ui-ngx/src/app/core/ws/telemetry-websocket.service.ts index 56c35328fb..f8fe055136 100644 --- a/ui-ngx/src/app/core/ws/telemetry-websocket.service.ts +++ b/ui-ngx/src/app/core/ws/telemetry-websocket.service.ts @@ -16,28 +16,36 @@ import { Inject, Injectable, NgZone } from '@angular/core'; import { - AlarmCountCmd, AlarmCountUnsubscribeCmd, + AlarmCountCmd, + AlarmCountUnsubscribeCmd, AlarmCountUpdate, AlarmDataCmd, AlarmDataUnsubscribeCmd, AlarmDataUpdate, - AttributesSubscriptionCmd, EntityCountCmd, EntityCountUnsubscribeCmd, EntityCountUpdate, EntityDataCmd, EntityDataUnsubscribeCmd, EntityDataUpdate, - GetHistoryCmd, isAlarmCountUpdateMsg, + isAlarmCountUpdateMsg, isAlarmDataUpdateMsg, isEntityCountUpdateMsg, isEntityDataUpdateMsg, + isNotificationCountUpdateMsg, + isNotificationsUpdateMsg, + MarkAllAsReadCmd, + MarkAsReadCmd, + NotificationCountUpdate, + NotificationSubscriber, + NotificationsUpdate, SubscriptionCmd, SubscriptionUpdate, - TelemetryFeature, TelemetryPluginCmdsWrapper, TelemetrySubscriber, - TimeseriesSubscriptionCmd, + UnreadCountSubCmd, + UnreadSubCmd, + UnsubscribeCmd, WebsocketDataMsg } from '@app/shared/models/telemetry/telemetry.models'; import { Store } from '@ngrx/store'; @@ -58,7 +66,7 @@ export class TelemetryWebsocketService extends WebsocketService { const cmdId = this.nextCmdId(); - this.subscribersMap.set(cmdId, subscriber); - subscriptionCommand.cmdId = cmdId; - if (subscriptionCommand instanceof SubscriptionCmd) { - if (subscriptionCommand.getType() === TelemetryFeature.TIMESERIES) { - this.cmdWrapper.tsSubCmds.push(subscriptionCommand as TimeseriesSubscriptionCmd); - } else { - this.cmdWrapper.attrSubCmds.push(subscriptionCommand as AttributesSubscriptionCmd); - } - } else if (subscriptionCommand instanceof GetHistoryCmd) { - this.cmdWrapper.historyCmds.push(subscriptionCommand); - } else if (subscriptionCommand instanceof EntityDataCmd) { - this.cmdWrapper.entityDataCmds.push(subscriptionCommand); - } else if (subscriptionCommand instanceof AlarmDataCmd) { - this.cmdWrapper.alarmDataCmds.push(subscriptionCommand); - } else if (subscriptionCommand instanceof EntityCountCmd) { - this.cmdWrapper.entityCountCmds.push(subscriptionCommand); - } else if (subscriptionCommand instanceof AlarmCountCmd) { - this.cmdWrapper.alarmCountCmds.push(subscriptionCommand); + if (!(subscriptionCommand instanceof MarkAsReadCmd) && !(subscriptionCommand instanceof MarkAllAsReadCmd)) { + this.subscribersMap.set(cmdId, subscriber); } + subscriptionCommand.cmdId = cmdId; + this.cmdWrapper.cmds.push(subscriptionCommand); } ); this.subscribersCount++; @@ -95,8 +89,8 @@ export class TelemetryWebsocketService extends WebsocketService { - if (subscriptionCommand.cmdId && subscriptionCommand instanceof EntityDataCmd) { - this.cmdWrapper.entityDataCmds.push(subscriptionCommand); + if (subscriptionCommand.cmdId && (subscriptionCommand instanceof EntityDataCmd || subscriptionCommand instanceof UnreadSubCmd)) { + this.cmdWrapper.cmds.push(subscriptionCommand); } } ); @@ -110,27 +104,27 @@ export class TelemetryWebsocketService extends WebsocketService { if (subscriptionCommand instanceof SubscriptionCmd) { subscriptionCommand.unsubscribe = true; - if (subscriptionCommand.getType() === TelemetryFeature.TIMESERIES) { - this.cmdWrapper.tsSubCmds.push(subscriptionCommand as TimeseriesSubscriptionCmd); - } else { - this.cmdWrapper.attrSubCmds.push(subscriptionCommand as AttributesSubscriptionCmd); - } + this.cmdWrapper.cmds.push(subscriptionCommand); } else if (subscriptionCommand instanceof EntityDataCmd) { const entityDataUnsubscribeCmd = new EntityDataUnsubscribeCmd(); entityDataUnsubscribeCmd.cmdId = subscriptionCommand.cmdId; - this.cmdWrapper.entityDataUnsubscribeCmds.push(entityDataUnsubscribeCmd); + this.cmdWrapper.cmds.push(entityDataUnsubscribeCmd); } else if (subscriptionCommand instanceof AlarmDataCmd) { const alarmDataUnsubscribeCmd = new AlarmDataUnsubscribeCmd(); alarmDataUnsubscribeCmd.cmdId = subscriptionCommand.cmdId; - this.cmdWrapper.alarmDataUnsubscribeCmds.push(alarmDataUnsubscribeCmd); + this.cmdWrapper.cmds.push(alarmDataUnsubscribeCmd); } else if (subscriptionCommand instanceof EntityCountCmd) { const entityCountUnsubscribeCmd = new EntityCountUnsubscribeCmd(); entityCountUnsubscribeCmd.cmdId = subscriptionCommand.cmdId; - this.cmdWrapper.entityCountUnsubscribeCmds.push(entityCountUnsubscribeCmd); + this.cmdWrapper.cmds.push(entityCountUnsubscribeCmd); } else if (subscriptionCommand instanceof AlarmCountCmd) { const alarmCountUnsubscribeCmd = new AlarmCountUnsubscribeCmd(); alarmCountUnsubscribeCmd.cmdId = subscriptionCommand.cmdId; - this.cmdWrapper.alarmCountUnsubscribeCmds.push(alarmCountUnsubscribeCmd); + this.cmdWrapper.cmds.push(alarmCountUnsubscribeCmd); + } else if (subscriptionCommand instanceof UnreadCountSubCmd || subscriptionCommand instanceof UnreadSubCmd) { + const notificationsUnsubCmds = new UnsubscribeCmd(); + notificationsUnsubCmds.cmdId = subscriptionCommand.cmdId; + this.cmdWrapper.cmds.push(notificationsUnsubCmds); } const cmdId = subscriptionCommand.cmdId; if (cmdId) { @@ -145,29 +139,28 @@ export class TelemetryWebsocketService extends WebsocketService implements WsServ lastCmdId = 0; subscribersCount = 0; - subscribersMap = new Map(); + subscribersMap = new Map(); - reconnectSubscribers = new Set(); + reconnectSubscribers = new Set(); - notificationUri: string; + wsUri: string; - dataStream: WebSocketSubject; + dataStream: WebSocketSubject; errorName = 'WebSocket Error'; @@ -69,23 +74,23 @@ export abstract class WebsocketService implements WsServ if (!port) { port = '443'; } - this.notificationUri = 'wss:'; + this.wsUri = 'wss:'; } else { if (!port) { port = '80'; } - this.notificationUri = 'ws:'; + this.wsUri = 'ws:'; } - this.notificationUri += `//${this.window.location.hostname}:${port}/${apiEndpoint}`; + this.wsUri += `//${this.window.location.hostname}:${port}/${apiEndpoint}`; } - abstract subscribe(subscriber: T); + abstract subscribe(subscriber: WsSubscriber); abstract update(subscriber: T); abstract unsubscribe(subscriber: T); - abstract processOnMessage(message: any); + abstract processOnMessage(message: WebsocketDataMsg); protected nextCmdId(): number { this.lastCmdId++; @@ -97,7 +102,9 @@ export abstract class WebsocketService implements WsServ this.dataStream.next(this.cmdWrapper.preparePublishCommands(MAX_PUBLISH_COMMANDS)); this.checkToClose(); } - this.tryOpenSocket(); + if (this.subscribersCount > 0) { + this.tryOpenSocket(); + } } private checkToClose() { @@ -156,13 +163,13 @@ export abstract class WebsocketService implements WsServ } private openSocket(token: string) { - const uri = `${this.notificationUri}?token=${token}`; - this.dataStream = webSocket( + const uri = `${this.wsUri}`; + this.dataStream = webSocket( { url: uri, openObserver: { next: () => { - this.onOpen(); + this.onOpen(token); } }, closeObserver: { @@ -174,9 +181,9 @@ export abstract class WebsocketService implements WsServ ); this.dataStream.subscribe({ - next: (message) => { + next: (message: CmdUpdateMsg) => { this.ngZone.runOutsideAngular(() => { - this.onMessage(message as WebsocketNotificationMsg); + this.onMessage(message); }); }, error: (error) => { @@ -185,9 +192,10 @@ export abstract class WebsocketService implements WsServ }); } - private onOpen() { + private onOpen(token: string) { this.isOpening = false; this.isOpened = true; + this.cmdWrapper.setAuth(token); if (this.reconnectTimer) { clearTimeout(this.reconnectTimer); this.reconnectTimer = null; @@ -206,11 +214,11 @@ export abstract class WebsocketService implements WsServ } } - private onMessage(message: WebsocketNotificationMsg) { + private onMessage(message: CmdUpdateMsg) { if (message.errorCode) { this.showWsError(message.errorCode, message.errorMsg); } else { - this.processOnMessage(message); + this.processOnMessage(message as WebsocketDataMsg); } this.checkToClose(); } @@ -257,5 +265,4 @@ export abstract class WebsocketService implements WsServ message, type: 'error' })); } - } diff --git a/ui-ngx/src/app/modules/common/modules-map.ts b/ui-ngx/src/app/modules/common/modules-map.ts index 58f434a77f..41e10d5de3 100644 --- a/ui-ngx/src/app/modules/common/modules-map.ts +++ b/ui-ngx/src/app/modules/common/modules-map.ts @@ -83,15 +83,19 @@ import * as TbCore from '@core/public-api'; import * as TbShared from '@shared/public-api'; import * as TbHomeComponents from '@home/components/public-api'; -import * as MillisecondsToTimeStringPipe from '@shared/pipe/milliseconds-to-time-string.pipe'; +import * as DateAgoPipe from '@shared/pipe/date-ago.pipe'; import * as EnumToArrayPipe from '@shared/pipe/enum-to-array.pipe'; -import * as HighlightPipe from '@shared/pipe/highlight.pipe'; -import * as TruncatePipe from '@shared/pipe/truncate.pipe'; -import * as TbJsonPipe from '@shared/pipe/tbJson.pipe'; import * as FileSizePipe from '@shared/pipe/file-size.pipe'; +import * as HighlightPipe from '@shared/pipe/highlight.pipe'; +import * as KeyboardShortcutPipe from '@shared/pipe/keyboard-shortcut.pipe'; +import * as MillisecondsToTimeStringPipe from '@shared/pipe/milliseconds-to-time-string.pipe'; import * as NospacePipe from '@shared/pipe/nospace.pipe'; +import * as SafePipe from '@shared/pipe/safe.pipe'; import * as SelectableColumnsPipe from '@shared/pipe/selectable-columns.pipe'; -import * as KeyboardShortcutPipe from '@shared/pipe/keyboard-shortcut.pipe'; +import * as ShortNumberPipe from '@shared/pipe/short-number.pipe'; +import * as TbJsonPipe from '@shared/pipe/tbJson.pipe'; +import * as TruncatePipe from '@shared/pipe/truncate.pipe'; +import * as ImagePipe from '@shared/pipe/image.pipe'; import * as coercion from '@shared/decorators/coercion'; import * as enumerable from '@shared/decorators/enumerable'; @@ -177,7 +181,16 @@ import * as CopyButtonComponent from '@shared/components/button/copy-button.comp import * as TogglePasswordComponent from '@shared/components/button/toggle-password.component'; import * as ProtobufContentComponent from '@shared/components/protobuf-content.component'; import * as SlackConversationAutocompleteComponent from '@shared/components/slack-conversation-autocomplete.component'; +import * as StringItemsListComponent from '@shared/components/string-items-list.component'; import * as ToggleHeaderComponent from '@shared/components/toggle-header.component'; +import * as ToggleSelectComponent from '@shared/components/toggle-select.component'; +import * as UnitInputComponent from '@shared/components/unit-input.component'; +import * as MaterialIconsComponent from '@shared/components/material-icons.component'; +import * as TbIconComponent from '@shared/components/icon.component'; +import * as HintTooltipIconComponent from '@shared/components/hint-tooltip-icon.component'; +import * as ScrollGridComponent from '@shared/components/grid/scroll-grid.component'; +import * as GalleryImageInputComponent from '@shared/components/image/gallery-image-input.component'; +import * as MultipleGalleryImageInputComponent from '@shared/components/image/multiple-gallery-image-input.component'; import * as AddEntityDialogComponent from '@home/components/entity/add-entity-dialog.component'; import * as EntitiesTableComponent from '@home/components/entity/entities-table.component'; @@ -218,10 +231,10 @@ import * as CustomActionPrettyEditorComponent from '@home/components/widget/acti import * as MobileActionEditorComponent from '@home/components/widget/action/mobile-action-editor.component'; import * as CustomDialogService from '@home/components/widget/dialog/custom-dialog.service'; import * as CustomDialogContainerComponent from '@home/components/widget/dialog/custom-dialog-container.component'; -import * as ImportDialogComponent from '@home/components/import-export/import-dialog.component'; +import * as ImportDialogComponent from '@shared/import-export/import-dialog.component'; import * as AddWidgetToDashboardDialogComponent from '@home/components/attribute/add-widget-to-dashboard-dialog.component'; -import * as ImportDialogCsvComponent from '@home/components/import-export/import-dialog-csv.component'; -import * as TableColumnsAssignmentComponent from '@home/components/import-export/table-columns-assignment.component'; +import * as ImportDialogCsvComponent from '@shared/import-export/import-dialog-csv.component'; +import * as TableColumnsAssignmentComponent from '@shared/import-export/table-columns-assignment.component'; import * as EventContentDialogComponent from '@home/components/event/event-content-dialog.component'; import * as SharedHomeComponentsModule from '@home/components/shared-home-components.module'; import * as SelectTargetLayoutDialogComponent from '@home/components/dashboard/select-target-layout-dialog.component'; @@ -380,20 +393,28 @@ class ModulesMap implements IModulesMap { '@shared/public-api': TbShared, '@home/components/public-api': TbHomeComponents, - '@shared/pipe/milliseconds-to-time-string.pipe': MillisecondsToTimeStringPipe, + '@shared/pipe/date-ago.pipe': DateAgoPipe, '@shared/pipe/enum-to-array.pipe': EnumToArrayPipe, - '@shared/pipe/highlight.pipe': HighlightPipe, - '@shared/pipe/truncate.pipe': TruncatePipe, - '@shared/pipe/tbJson.pipe': TbJsonPipe, '@shared/pipe/file-size.pipe': FileSizePipe, + '@shared/pipe/highlight.pipe': HighlightPipe, + '@shared/pipe/keyboard-shortcut.pipe': KeyboardShortcutPipe, + '@shared/pipe/milliseconds-to-time-string.pipe': MillisecondsToTimeStringPipe, '@shared/pipe/nospace.pipe': NospacePipe, + '@shared/pipe/safe.pipe': SafePipe, '@shared/pipe/selectable-columns.pipe': SelectableColumnsPipe, - '@shared/pipe/keyboard-shortcut.pipe': KeyboardShortcutPipe, + '@shared/pipe/short-number.pipe': ShortNumberPipe, + '@shared/pipe/tbJson.pipe': TbJsonPipe, + '@shared/pipe/truncate.pipe': TruncatePipe, + '@shared/pipe/image.pipe': ImagePipe, '@shared/decorators/coercion': coercion, '@shared/decorators/enumerable': enumerable, '@shared/decorators/tb-inject': TbInject, + '@shared/import-export/import-dialog.component': ImportDialogComponent, + '@shared/import-export/import-dialog-csv.component': ImportDialogCsvComponent, + '@shared/import-export/table-columns-assignment.component': TableColumnsAssignmentComponent, + '@shared/components/footer.component': FooterComponent, '@shared/components/logo.component': LogoComponent, '@shared/components/footer-fab-buttons.component': FooterFabButtonsComponent, @@ -475,7 +496,16 @@ class ModulesMap implements IModulesMap { '@shared/components/button/toggle-password.component': TogglePasswordComponent, '@shared/components/protobuf-content.component': ProtobufContentComponent, '@shared/components/slack-conversation-autocomplete.component': SlackConversationAutocompleteComponent, + '@shared/components/string-items-list.component': StringItemsListComponent, '@shared/components/toggle-header.component': ToggleHeaderComponent, + '@shared/components/toggle-select.component': ToggleSelectComponent, + '@shared/components/unit-input.component': UnitInputComponent, + '@shared/components/material-icons.component': MaterialIconsComponent, + '@shared/components/icon.component': TbIconComponent, + '@shared/components/hint-tooltip-icon.component': HintTooltipIconComponent, + '@shared/components/grid/scroll-grid.component': ScrollGridComponent, + '@shared/components/image/gallery-image-input.component': GalleryImageInputComponent, + '@shared/components/image/multiple-gallery-image-input.component': MultipleGalleryImageInputComponent, '@home/components/entity/add-entity-dialog.component': AddEntityDialogComponent, '@home/components/entity/entities-table.component': EntitiesTableComponent, @@ -516,10 +546,7 @@ class ModulesMap implements IModulesMap { '@home/components/widget/action/mobile-action-editor.component': MobileActionEditorComponent, '@home/components/widget/dialog/custom-dialog.service': CustomDialogService, '@home/components/widget/dialog/custom-dialog-container.component': CustomDialogContainerComponent, - '@home/components/import-export/import-dialog.component': ImportDialogComponent, '@home/components/attribute/add-widget-to-dashboard-dialog.component': AddWidgetToDashboardDialogComponent, - '@home/components/import-export/import-dialog-csv.component': ImportDialogCsvComponent, - '@home/components/import-export/table-columns-assignment.component': TableColumnsAssignmentComponent, '@home/components/event/event-content-dialog.component': EventContentDialogComponent, '@home/components/shared-home-components.module': SharedHomeComponentsModule, '@home/components/dashboard/select-target-layout-dialog.component': SelectTargetLayoutDialogComponent, diff --git a/ui-ngx/src/app/modules/dashboard/dashboard-pages.routing.module.ts b/ui-ngx/src/app/modules/dashboard/dashboard-pages.routing.module.ts index 12f27f2dcc..a89f467e1e 100644 --- a/ui-ngx/src/app/modules/dashboard/dashboard-pages.routing.module.ts +++ b/ui-ngx/src/app/modules/dashboard/dashboard-pages.routing.module.ts @@ -25,6 +25,7 @@ import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; import { DashboardResolver } from '@app/modules/home/pages/dashboard/dashboard-routing.module'; import { UtilsService } from '@core/services/utils.service'; import { Widget } from '@app/shared/models/widget.models'; +import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; @Injectable() export class WidgetEditorDashboardResolver implements Resolve { @@ -37,13 +38,8 @@ export class WidgetEditorDashboardResolver implements Resolve { resolve(route: ActivatedRouteSnapshot): Dashboard { const editWidgetInfo = this.utils.editWidgetInfo; const widget: Widget = { - isSystemType: true, - bundleAlias: 'customWidgetBundle', - typeAlias: 'customWidget', + typeFullFqn: 'system.customWidget', type: editWidgetInfo.type, - title: 'My widget', - image: null, - description: null, sizeX: editWidgetInfo.sizeX * 2, sizeY: editWidgetInfo.sizeY * 2, row: 2, @@ -59,6 +55,7 @@ const routes: Routes = [ { path: 'dashboard/:dashboardId', component: DashboardPageComponent, + canDeactivate: [ConfirmOnExitGuard], data: { breadcrumb: { skip: true @@ -75,6 +72,7 @@ const routes: Routes = [ { path: 'widget-editor', component: DashboardPageComponent, + canDeactivate: [ConfirmOnExitGuard], data: { breadcrumb: { skip: true diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.html b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.html index 8f43f27964..8d178aaa2c 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-panel.component.html @@ -26,10 +26,14 @@ #userAutocomplete="matAutocomplete" [displayWith]="displayUserFn" (optionSelected)="selected($event)"> - + account_circle {{ assigneeNotSetText | translate }} + + account_circle + {{ assignedToCurrentUserText | translate }} + ('AlarmAssigneePanelData'); @@ -59,15 +60,19 @@ export interface AlarmAssigneePanelData { }) export class AlarmAssigneePanelComponent implements OnInit, AfterViewInit, OnDestroy { + assigneeOptions = AlarmAssigneeOption; + private dirty = false; alarmId: string; assigneeId?: string; + assigneeOption?: AlarmAssigneeOption = null; assigneeNotSetText = 'alarm.unassigned'; + assignedToCurrentUserText = ''; - reassigned: boolean = false; + reassigned = false; selectUserFormGroup: FormGroup; @@ -77,6 +82,14 @@ export class AlarmAssigneePanelComponent implements OnInit, AfterViewInit, OnDe searchText = ''; + get displayAssigneeNotSet(): boolean { + return !!this.assigneeId; + } + + get displayAssignedToCurrentUser(): boolean { + return false; + } + private destroy$ = new Subject(); constructor(@Inject(ALARM_ASSIGNEE_PANEL_DATA) public data: AlarmAssigneePanelData, @@ -124,8 +137,8 @@ export class AlarmAssigneePanelComponent implements OnInit, AfterViewInit, OnDe selected(event: MatAutocompleteSelectedEvent): void { this.clear(); - const user: User = event.option.value; - if (user) { + if (event.option.value?.id) { + const user: User = event.option.value; this.assign(user); } else { this.unassign(); diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-select-panel.component.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-select-panel.component.ts index 1526616833..cd8ce2ec33 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-select-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-select-panel.component.ts @@ -36,11 +36,14 @@ import { emptyPageData } from '@shared/models/page/page-data'; import { OverlayRef } from '@angular/cdk/overlay'; import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { UtilsService } from '@core/services/utils.service'; +import { AlarmAssigneeOption } from '@shared/models/alarm.models'; export const ALARM_ASSIGNEE_SELECT_PANEL_DATA = new InjectionToken('AlarmAssigneeSelectPanelData'); export interface AlarmAssigneeSelectPanelData { assigneeId?: string; + assigneeOption?: AlarmAssigneeOption; + userMode?: boolean; } @Component({ @@ -50,11 +53,15 @@ export interface AlarmAssigneeSelectPanelData { }) export class AlarmAssigneeSelectPanelComponent implements OnInit, AfterViewInit, OnDestroy { + assigneeOptions = AlarmAssigneeOption; + private dirty = false; assigneeId?: string; + assigneeOption?: AlarmAssigneeOption; assigneeNotSetText = 'alarm.assignee-not-set'; + assignedToCurrentUserText = this.data.userMode ? 'alarm.assigned-to-me' : 'alarm.assigned-to-current-user'; selectUserFormGroup: FormGroup; @@ -67,6 +74,15 @@ export class AlarmAssigneeSelectPanelComponent implements OnInit, AfterViewInit userSelected = false; result?: UserEmailInfo; + optionResult?: AlarmAssigneeOption; + + get displayAssigneeNotSet(): boolean { + return this.assigneeOption !== AlarmAssigneeOption.noAssignee; + } + + get displayAssignedToCurrentUser(): boolean { + return this.assigneeOption !== AlarmAssigneeOption.currentUser; + } private destroy$ = new Subject(); @@ -77,6 +93,7 @@ export class AlarmAssigneeSelectPanelComponent implements OnInit, AfterViewInit private fb: FormBuilder, private utilsService: UtilsService) { this.assigneeId = data.assigneeId; + this.assigneeOption = data.assigneeOption; this.selectUserFormGroup = this.fb.group({ user: [null] }); @@ -112,7 +129,11 @@ export class AlarmAssigneeSelectPanelComponent implements OnInit, AfterViewInit selected(event: MatAutocompleteSelectedEvent): void { this.clear(); this.userSelected = true; - this.result = event.option.value; + if (event.option.value?.id) { + this.result = event.option.value; + } else { + this.optionResult = event.option.value; + } this.overlayRef.dispose(); } diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-select.component.html b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-select.component.html index 2ad7229365..c07a4cb3c6 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-select.component.html +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-select.component.html @@ -16,16 +16,16 @@ --> - - alarm.assignee + subscriptSizing="dynamic" [appearance]="inline ? 'outline' : 'fill'"> + alarm.assignee - {{ getUserInitials() }} - account_circle - arrow_drop_down + account_circle + arrow_drop_down diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-select.component.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-select.component.ts index 4d1f6021be..50918bfd0d 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-select.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee-select.component.ts @@ -30,6 +30,8 @@ import { AlarmAssigneeSelectPanelComponent, AlarmAssigneeSelectPanelData } from '@home/components/alarm/alarm-assignee-select-panel.component'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { AlarmAssigneeOption } from '@shared/models/alarm.models'; @Component({ selector: 'tb-alarm-assignee-select', @@ -47,8 +49,17 @@ export class AlarmAssigneeSelectComponent implements OnInit, ControlValueAccesso @Input() disabled: boolean; + @coerceBoolean() + @Input() + inline = false; + + @coerceBoolean() + @Input() + userMode = false; + assigneeFormGroup: UntypedFormGroup; assignee?: User | UserEmailInfo; + assigneeOption?: AlarmAssigneeOption; private propagateChange = (_: any) => {}; @@ -82,7 +93,15 @@ export class AlarmAssigneeSelectComponent implements OnInit, ControlValueAccesso } } - writeValue(userId?: UserId): void { + writeValue(value?: UserId | AlarmAssigneeOption): void { + let userId: UserId; + if (value && (value as UserId).id) { + userId = value as UserId; + this.assigneeOption = null; + } else { + userId = null; + this.assigneeOption = value ? value as AlarmAssigneeOption : AlarmAssigneeOption.noAssignee; + } const userObservable = userId ? this.userService.getUser(userId.id, {ignoreErrors: true}).pipe( catchError(() => of(null)) ) : of(null); @@ -92,15 +111,31 @@ export class AlarmAssigneeSelectComponent implements OnInit, ControlValueAccesso }), map((user) => this.getAssignee(user)) ).subscribe((assignee) => { - this.assigneeFormGroup.get('assignee').patchValue(assignee, {emitEvent: false}); + if (assignee) { + this.assigneeFormGroup.get('assignee').patchValue(assignee, {emitEvent: false}); + } else { + if (!this.assigneeOption) { + this.assigneeOption = AlarmAssigneeOption.noAssignee; + } + assignee = this.getAssigneeOption(this.assigneeOption); + this.assigneeFormGroup.get('assignee').patchValue(assignee, {emitEvent: false}); + } }); } - private getAssignee(user?: User| UserEmailInfo): string { + private getAssignee(user?: User| UserEmailInfo): string | null { if (user) { return this.getUserDisplayName(user); } else { + return null; + } + } + + private getAssigneeOption(assigneeOption: AlarmAssigneeOption): string { + if (assigneeOption === AlarmAssigneeOption.noAssignee) { return this.translateService.instant('alarm.assignee-not-set'); + } else { + return this.translateService.instant(this.userMode ? 'alarm.assigned-to-me' : 'alarm.assigned-to-current-user'); } } @@ -169,7 +204,9 @@ export class AlarmAssigneeSelectComponent implements OnInit, ControlValueAccesso { provide: ALARM_ASSIGNEE_SELECT_PANEL_DATA, useValue: { - assigneeId: this.assignee?.id?.id + assigneeId: this.assignee?.id?.id, + assigneeOption: this.assigneeOption, + userMode: this.userMode } as AlarmAssigneeSelectPanelData }, { @@ -183,8 +220,14 @@ export class AlarmAssigneeSelectComponent implements OnInit, ControlValueAccesso component.onDestroy(() => { if (component.instance.userSelected) { this.assignee = component.instance.result; - this.assigneeFormGroup.get('assignee').patchValue(this.getAssignee(this.assignee), {emitEvent: false}); - this.propagateChange(this.assignee?.id); + this.assigneeOption = component.instance.optionResult; + if (this.assignee) { + this.assigneeFormGroup.get('assignee').patchValue(this.getAssignee(this.assignee), {emitEvent: false}); + this.propagateChange(this.assignee?.id); + } else if (this.assigneeOption) { + this.assigneeFormGroup.get('assignee').patchValue(this.getAssigneeOption(this.assigneeOption), {emitEvent: false}); + this.propagateChange(this.assigneeOption); + } } }); } diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.scss b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.scss index 82f10650cf..3aed0ccb5e 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.scss +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-assignee.component.scss @@ -19,20 +19,14 @@ justify-content: center; align-items: center; border-radius: 50%; - width: 28px; - height: 28px; min-width: 28px; min-height: 28px; color: white; font-size: 13px; font-weight: 700; - margin-left: 12px; - margin-right: 20px; } .unassigned-icon { - width: 28px; - height: 28px; font-size: 28px; color: rgba(0, 0, 0, 0.38); overflow: visible; @@ -40,3 +34,14 @@ margin-right: 20px; padding: 0; } + +.user-avatar, .unassigned-icon { + width: 28px; + height: 28px; + margin-left: 12px; + margin-right: 20px; + &.inline { + margin-left: 0; + margin-right: 8px; + } +} diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.html b/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.html index ac2bb73d05..40cb2cf406 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.html +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-comment.component.html @@ -36,7 +36,7 @@ (click)="changeSortDirection()" [matTooltip]="getSortDirectionTooltipText()" matTooltipPosition="above"> - + {{ getSortDirectionIcon() }} + diff --git a/ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.ts b/ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.ts index 442bd495c3..65f4c0512d 100644 --- a/ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.ts @@ -19,16 +19,17 @@ import { ErrorStateMatcher } from '@angular/material/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms'; +import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms'; import { EntityId } from '@shared/models/id/entity-id'; import { Router } from '@angular/router'; import { DialogComponent } from '@app/shared/components/dialog.component'; -import { AttributeData, AttributeScope } from '@shared/models/telemetry/telemetry.models'; +import { AttributeData, AttributeScope, LatestTelemetry, TelemetryType } from '@shared/models/telemetry/telemetry.models'; import { AttributeService } from '@core/http/attribute.service'; +import { Observable } from 'rxjs'; export interface AddAttributeDialogData { entityId: EntityId; - attributeScope: AttributeScope; + attributeScope: TelemetryType; } @Component({ @@ -40,17 +41,19 @@ export interface AddAttributeDialogData { export class AddAttributeDialogComponent extends DialogComponent implements OnInit, ErrorStateMatcher { - attributeFormGroup: UntypedFormGroup; + attributeFormGroup: FormGroup; submitted = false; + isTelemetry = false; + constructor(protected store: Store, protected router: Router, @Inject(MAT_DIALOG_DATA) public data: AddAttributeDialogData, private attributeService: AttributeService, @SkipSelf() private errorStateMatcher: ErrorStateMatcher, public dialogRef: MatDialogRef, - public fb: UntypedFormBuilder) { + public fb: FormBuilder) { super(store, router, dialogRef); } @@ -59,14 +62,20 @@ export class AddAttributeDialogComponent extends DialogComponent { - this.dialogRef.close(true); - } - ); + let task: Observable; + if (this.data.attributeScope === LatestTelemetry.LATEST_TELEMETRY) { + task = this.attributeService.saveEntityTimeseries(this.data.entityId, + this.data.attributeScope, [attribute]); + } else { + task = this.attributeService.saveEntityAttributes(this.data.entityId, + this.data.attributeScope as AttributeScope, [attribute]); + } + task.subscribe(() => this.dialogRef.close(true)); } } diff --git a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html index 32ebb28ae9..0a072b5377 100644 --- a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html +++ b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html @@ -34,7 +34,7 @@ diff --git a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.scss b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.scss index 831762d1e4..b33dfdbb20 100644 --- a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.scss +++ b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.scss @@ -104,7 +104,7 @@ } mat-cell.tb-value-cell { cursor: pointer; - mat-icon { + .mat-icon { height: 24px; width: 24px; font-size: 24px; diff --git a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts index e9d67a3fdd..8bd711b175 100644 --- a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts @@ -23,6 +23,7 @@ import { Injector, Input, NgZone, + OnDestroy, OnInit, StaticProvider, ViewChild, @@ -38,8 +39,8 @@ import { TranslateService } from '@ngx-translate/core'; import { MatDialog } from '@angular/material/dialog'; import { DialogService } from '@core/services/dialog.service'; import { Direction, SortOrder } from '@shared/models/page/sort-order'; -import { fromEvent, merge } from 'rxjs'; -import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators'; +import { forkJoin, merge, Observable, Subject } from 'rxjs'; +import { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators'; import { EntityId } from '@shared/models/id/entity-id'; import { AttributeData, @@ -49,6 +50,7 @@ import { LatestTelemetry, TelemetryType, telemetryTypeTranslations, + TimeseriesDeleteStrategy, toTelemetryType } from '@shared/models/telemetry/telemetry.models'; import { AttributeDatasource } from '@home/models/datasource/attribute-datasource'; @@ -86,6 +88,8 @@ import { deepClone } from '@core/utils'; import { Filters } from '@shared/models/query/query.models'; import { hidePageSizePixelValue } from '@shared/models/constants'; import { ResizeObserver } from '@juggle/resize-observer'; +import { DeleteTimeseriesPanelComponent } from '@home/components/attribute/delete-timeseries-panel.component'; +import { FormBuilder } from '@angular/forms'; @Component({ @@ -94,12 +98,13 @@ import { ResizeObserver } from '@juggle/resize-observer'; styleUrls: ['./attribute-table.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) -export class AttributeTableComponent extends PageComponent implements AfterViewInit, OnInit { +export class AttributeTableComponent extends PageComponent implements AfterViewInit, OnInit, OnDestroy { telemetryTypeTranslationsMap = telemetryTypeTranslations; isClientSideTelemetryTypeMap = isClientSideTelemetryType; latestTelemetryTypes = LatestTelemetry; + attributeScopeTypes = AttributeScope; mode: 'default' | 'widget' = 'default'; @@ -178,6 +183,10 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; + textSearch = this.fb.control('', {nonNullable: true}); + + private destroy$ = new Subject(); + constructor(protected store: Store, private attributeService: AttributeService, private telemetryWsService: TelemetryWebsocketService, @@ -192,7 +201,8 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI private widgetService: WidgetService, private zone: NgZone, private cd: ChangeDetectorRef, - private elementRef: ElementRef) { + private elementRef: ElementRef, + private fb: FormBuilder) { super(store); this.dirtyValue = !this.activeValue; const sortOrder: SortOrder = { property: 'key', direction: Direction.ASC }; @@ -215,6 +225,8 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI if (this.widgetResize$) { this.widgetResize$.disconnect(); } + this.destroy$.next(); + this.destroy$.complete(); } attributeScopeChanged(attributeScope: TelemetryType) { @@ -225,25 +237,21 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI } ngAfterViewInit() { + this.textSearch.valueChanges.pipe( + debounceTime(150), + distinctUntilChanged((prev, current) => (this.pageLink.textSearch ?? '') === current.trim()), + takeUntil(this.destroy$) + ).subscribe((value) => { + this.paginator.pageIndex = 0; + this.pageLink.textSearch = value.trim(); + this.updateData(); + }); + + this.sort.sortChange.pipe(takeUntil(this.destroy$)).subscribe(() => this.paginator.pageIndex = 0); - fromEvent(this.searchInputField.nativeElement, 'keyup') - .pipe( - debounceTime(150), - distinctUntilChanged(), - tap(() => { - this.paginator.pageIndex = 0; - this.updateData(); - }) - ) - .subscribe(); - - this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0); - - merge(this.sort.sortChange, this.paginator.page) - .pipe( - tap(() => this.updateData()) - ) - .subscribe(); + merge(this.sort.sortChange, this.paginator.page).pipe( + takeUntil(this.destroy$) + ).subscribe(() => this.updateData()); this.viewsInited = true; if (this.activeValue && this.entityIdValue) { @@ -261,7 +269,6 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI enterFilterMode() { this.textSearchMode = true; - this.pageLink.textSearch = ''; setTimeout(() => { this.searchInputField.nativeElement.focus(); this.searchInputField.nativeElement.setSelectionRange(0, 0); @@ -270,9 +277,7 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI exitFilterMode() { this.textSearchMode = false; - this.pageLink.textSearch = null; - this.paginator.pageIndex = 0; - this.updateData(); + this.textSearch.reset(); } resetSortAndFilter(update: boolean = true) { @@ -289,6 +294,7 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI this.selectedWidgetsBundleAlias = null; this.attributeScope = this.defaultAttributeScope; this.pageLink.textSearch = null; + this.textSearch.reset('', {emitEvent: false}); if (this.viewsInited) { this.paginator.pageIndex = 0; const sortable = this.sort.sortables.get('key'); @@ -313,7 +319,7 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { entityId: this.entityIdValue, - attributeScope: this.attributeScope as AttributeScope + attributeScope: this.attributeScope } }).afterClosed().subscribe( (res) => { @@ -378,6 +384,84 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI }); } + deleteTimeseries($event: Event, telemetry?: AttributeData) { + if ($event) { + $event.stopPropagation(); + } + const target = $event.target || $event.srcElement || $event.currentTarget; + const config = new OverlayConfig({ + panelClass: 'tb-filter-panel', + backdropClass: 'cdk-overlay-transparent-backdrop', + hasBackdrop: true, + maxWidth: 488, + width: '100%' + }); + const connectedPosition: ConnectedPosition = { + originX: 'start', + originY: 'top', + overlayX: 'end', + overlayY: 'top' + }; + config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement) + .withPositions([connectedPosition]); + const overlayRef = this.overlay.create(config); + overlayRef.backdropClick().subscribe(() => { + overlayRef.dispose(); + }); + + const providers: StaticProvider[] = [ + { + provide: OverlayRef, + useValue: overlayRef + } + ]; + const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); + const componentRef = overlayRef.attach(new ComponentPortal(DeleteTimeseriesPanelComponent, + this.viewContainerRef, injector)); + componentRef.onDestroy(() => { + if (componentRef.instance.result !== null) { + const result = componentRef.instance.result; + const deleteTimeseries = telemetry ? [telemetry]: this.dataSource.selection.selected; + const tasks: Observable[] = []; + let deleteAllDataForKeys = false; + let rewriteLatestIfDeleted = false; + let startTs = null; + let endTs = null; + let deleteLatest = true; + switch (result.strategy) { + case TimeseriesDeleteStrategy.DELETE_ALL_DATA: + deleteAllDataForKeys = true; + break; + case TimeseriesDeleteStrategy.DELETE_ALL_DATA_EXCEPT_LATEST_VALUE: + deleteAllDataForKeys = true; + deleteLatest = false; + break; + case TimeseriesDeleteStrategy.DELETE_LATEST_VALUE: + rewriteLatestIfDeleted = result.rewriteLatest; + for (const ts of deleteTimeseries) { + startTs = ts.lastUpdateTs; + endTs = startTs + 1; + tasks.push(this.attributeService.deleteEntityTimeseries(this.entityIdValue, [ts], + deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted, deleteLatest)); + } + break; + case TimeseriesDeleteStrategy.DELETE_ALL_DATA_FOR_TIME_PERIOD: + startTs = result.startDateTime.getTime(); + endTs = result.endDateTime.getTime(); + rewriteLatestIfDeleted = result.rewriteLatest; + break; + } + if (tasks.length) { + forkJoin(tasks).subscribe(() => this.reloadAttributes()); + } else { + this.attributeService.deleteEntityTimeseries(this.entityIdValue, deleteTimeseries, deleteAllDataForKeys, + startTs, endTs, rewriteLatestIfDeleted, deleteLatest) + .subscribe(() => this.reloadAttributes()); + } + } + }); + } + deleteAttributes($event: Event) { if ($event) { $event.stopPropagation(); @@ -402,6 +486,14 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI } } + deleteTelemetry($event: Event) { + if (this.attributeScope === this.latestTelemetryTypes.LATEST_TELEMETRY) { + this.deleteTimeseries($event); + } else { + this.deleteAttributes($event); + } + } + enterWidgetMode() { this.mode = 'widget'; this.widgetsList = []; @@ -409,7 +501,7 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI this.widgetsLoaded = false; this.widgetBundleSet = false; this.widgetsCarouselIndex = 0; - this.selectedWidgetsBundleAlias = 'cards'; + this.selectedWidgetsBundleAlias = 'tables'; const entityAlias: EntityAlias = { id: this.utils.guid(), @@ -421,9 +513,7 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI // @ts-ignore const stateController: IStateController = { - getStateParams(): StateParams { - return {}; - } + getStateParams: (): StateParams => ({}) }; const filters: Filters = {}; @@ -475,9 +565,7 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI this.widgetsCarouselIndex = 0; if (widgetsBundle) { this.widgetsLoaded = false; - const bundleAlias = widgetsBundle.alias; - const isSystem = widgetsBundle.tenantId.id === NULL_UUID; - this.widgetService.getBundleWidgetTypes(bundleAlias, isSystem).subscribe( + this.widgetService.getBundleWidgetTypes(widgetsBundle.id.id).subscribe( (widgetTypes) => { widgetTypes = widgetTypes.sort((a, b) => { let result = widgetType[b.descriptor.type].localeCompare(widgetType[a.descriptor.type]); @@ -493,11 +581,8 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI const sizeY = widgetInfo.sizeY * 2; const col = Math.floor(Math.max(0, (20 - sizeX) / 2)); const widget: Widget = { - isSystemType: isSystem, - bundleAlias, - typeAlias: widgetInfo.alias, + typeFullFqn: widgetInfo.fullFqn, type: widgetInfo.type, - title: widgetInfo.widgetName, sizeX, sizeY, row: 0, diff --git a/ui-ngx/src/app/modules/home/components/attribute/delete-timeseries-panel.component.html b/ui-ngx/src/app/modules/home/components/attribute/delete-timeseries-panel.component.html new file mode 100644 index 0000000000..5778fc6805 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/attribute/delete-timeseries-panel.component.html @@ -0,0 +1,68 @@ + + + +

{{ "attribute.delete-timeseries.delete-strategy" | translate }}

+ + +
+ + + attribute.delete-timeseries.strategy + + + {{ strategiesTranslationsMap.get(strategy) | translate }} + + + +
+ + attribute.delete-timeseries.start-time + + + + + + attribute.delete-timeseries.ends-on + + + + +
+ + {{ "attribute.delete-timeseries.rewrite-latest-value" | translate }} + + +
+ + + +
diff --git a/ui-ngx/src/app/modules/home/components/attribute/delete-timeseries-panel.component.scss b/ui-ngx/src/app/modules/home/components/attribute/delete-timeseries-panel.component.scss new file mode 100644 index 0000000000..b0c0ec0621 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/attribute/delete-timeseries-panel.component.scss @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import '../../../../../scss/constants'; + +:host { + width: 100%; + + .mat-toolbar { + background: none; + } + + .tb-form-settings { + display: flex; + flex-direction: column; + gap: 16px; + padding-top: 0; + } + + .tb-select-interval { + display: flex; + flex-direction: row; + gap: 16px; + @media #{$mat-xs} { + flex-direction: column; + gap: 0; + } + } + + .tb-slide-toggle { + margin-bottom: 8px; + } +} diff --git a/ui-ngx/src/app/modules/home/components/attribute/delete-timeseries-panel.component.ts b/ui-ngx/src/app/modules/home/components/attribute/delete-timeseries-panel.component.ts new file mode 100644 index 0000000000..df2eb931df --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/attribute/delete-timeseries-panel.component.ts @@ -0,0 +1,138 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, InjectionToken, OnDestroy, OnInit } from '@angular/core'; +import { OverlayRef } from '@angular/cdk/overlay'; +import { + TimeseriesDeleteStrategy, + timeseriesDeleteStrategyTranslations +} from '@shared/models/telemetry/telemetry.models'; +import { MINUTE } from '@shared/models/time/time.models'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; + +export const DELETE_TIMESERIES_PANEL_DATA = new InjectionToken('DeleteTimeseriesPanelData'); + + +export interface DeleteTimeseriesPanelResult { + strategy: TimeseriesDeleteStrategy; + startDateTime: Date; + endDateTime: Date; + rewriteLatest: boolean; +} + +@Component({ + selector: 'tb-delete-timeseries-panel', + templateUrl: './delete-timeseries-panel.component.html', + styleUrls: ['./delete-timeseries-panel.component.scss'] +}) +export class DeleteTimeseriesPanelComponent implements OnInit, OnDestroy { + + deleteTimeseriesFormGroup: FormGroup; + + result: DeleteTimeseriesPanelResult = null; + + strategiesTranslationsMap = timeseriesDeleteStrategyTranslations; + + private destroy$ = new Subject(); + + constructor(private overlayRef: OverlayRef, + private fb: FormBuilder) { } + + ngOnInit(): void { + const today = new Date(); + this.deleteTimeseriesFormGroup = this.fb.group({ + strategy: [TimeseriesDeleteStrategy.DELETE_ALL_DATA], + startDateTime: [ + { value: new Date(today.getFullYear(), today.getMonth() - 1, today.getDate()), disabled: true }, + Validators.required + ], + endDateTime: [{ value: today, disabled: true }, Validators.required], + rewriteLatest: [true] + }) + + this.deleteTimeseriesFormGroup.get('strategy').valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(value => { + if (value === TimeseriesDeleteStrategy.DELETE_ALL_DATA_FOR_TIME_PERIOD) { + this.deleteTimeseriesFormGroup.get('startDateTime').enable({onlySelf: true, emitEvent: false}); + this.deleteTimeseriesFormGroup.get('endDateTime').enable({onlySelf: true, emitEvent: false}); + } else { + this.deleteTimeseriesFormGroup.get('startDateTime').disable({onlySelf: true, emitEvent: false}); + this.deleteTimeseriesFormGroup.get('endDateTime').disable({onlySelf: true, emitEvent: false}); + } + }) + this.deleteTimeseriesFormGroup.get('startDateTime').valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(value => this.onStartDateTimeChange(value)); + this.deleteTimeseriesFormGroup.get('endDateTime').valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(value => this.onEndDateTimeChange(value)); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + delete(): void { + if (this.deleteTimeseriesFormGroup.valid) { + this.result = this.deleteTimeseriesFormGroup.value; + this.overlayRef.dispose(); + } else { + this.deleteTimeseriesFormGroup.markAllAsTouched(); + } + } + + cancel(): void { + this.overlayRef.dispose(); + } + + isPeriodStrategy(): boolean { + return this.deleteTimeseriesFormGroup.get('strategy').value === TimeseriesDeleteStrategy.DELETE_ALL_DATA_FOR_TIME_PERIOD; + } + + isDeleteLatestStrategy(): boolean { + return this.deleteTimeseriesFormGroup.get('strategy').value === TimeseriesDeleteStrategy.DELETE_LATEST_VALUE; + } + + private onStartDateTimeChange(newStartDateTime: Date) { + if (newStartDateTime) { + const endDateTimeTs = this.deleteTimeseriesFormGroup.get('endDateTime').value.getTime(); + if (newStartDateTime.getTime() >= endDateTimeTs) { + this.deleteTimeseriesFormGroup.get('startDateTime') + .patchValue(new Date(endDateTimeTs - MINUTE), {onlySelf: true, emitEvent: false}); + } else { + this.deleteTimeseriesFormGroup.get('startDateTime') + .patchValue(newStartDateTime, {onlySelf: true, emitEvent: false}); + } + } + } + + private onEndDateTimeChange(newEndDateTime: Date) { + if (newEndDateTime) { + const startDateTimeTs = this.deleteTimeseriesFormGroup.get('startDateTime').value.getTime(); + if (newEndDateTime.getTime() <= startDateTimeTs) { + this.deleteTimeseriesFormGroup.get('endDateTime') + .patchValue(new Date(startDateTimeTs + MINUTE), {onlySelf: true, emitEvent: false}); + } else { + this.deleteTimeseriesFormGroup.get('endDateTime') + .patchValue(newEndDateTime, {onlySelf: true, emitEvent: false}); + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/attribute/edit-attribute-value-panel.component.html b/ui-ngx/src/app/modules/home/components/attribute/edit-attribute-value-panel.component.html index 9cefd62825..2ec3455adf 100644 --- a/ui-ngx/src/app/modules/home/components/attribute/edit-attribute-value-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/attribute/edit-attribute-value-panel.component.html @@ -33,7 +33,7 @@ diff --git a/ui-ngx/src/app/modules/home/components/attribute/edit-attribute-value-panel.component.ts b/ui-ngx/src/app/modules/home/components/attribute/edit-attribute-value-panel.component.ts index 539c9930db..4ce038a4ed 100644 --- a/ui-ngx/src/app/modules/home/components/attribute/edit-attribute-value-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/attribute/edit-attribute-value-panel.component.ts @@ -62,6 +62,11 @@ export class EditAttributeValuePanelComponent extends PageComponent implements O return originalErrorState || customErrorState; } + invalid(): boolean { + const value = this.attributeFormGroup.get('value').value; + return !Array.isArray(value) && this.attributeFormGroup.invalid; + } + cancel(): void { this.overlayRef.dispose(); } diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.html b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.html index 5af51031bf..a3d2442e4f 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.html @@ -15,24 +15,19 @@ limitations under the License. --> -
+ -

widget.add

- : {{data.widgetInfo.widgetName}} -
- - -
+
+

{{'widget.add' | translate}}:

+ {{data.widgetInfo.widgetName}} +
+ + {{ 'widget.basic-mode' | translate }} + {{ 'widget.advanced-mode' | translate }} + +
+
- - -
-
- - - -
- -
-
-
+
+ + + +
+ +
+
diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.scss b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.scss new file mode 100644 index 0000000000..6f7eb4bd3b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.scss @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import '../../../../../scss/constants'; + +.tb-add-widget-dialog { + .mat-mdc-dialog-content { + padding: 0; + position: relative; + } + @media #{$mat-gt-xs} { + width: 900px; + .mat-mdc-dialog-content { + height: 900px; + } + } + .tb-add-widget-toolbar { + flex: 1; + min-width: 0; + display: flex; + flex-direction: row; + place-content: center flex-start; + align-items: center; + gap: 8px; + } +} diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts index ada22bba94..ffc453c42a 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts @@ -14,12 +14,12 @@ /// limitations under the License. /// -import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; +import { Component, Inject, OnInit, SkipSelf, ViewChild, ViewEncapsulation } from '@angular/core'; import { ErrorStateMatcher } from '@angular/material/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, FormGroupDirective, NgForm } from '@angular/forms'; +import { FormGroupDirective, NgForm, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; import { Router } from '@angular/router'; import { DialogComponent } from '@app/shared/components/dialog.component'; import { Widget, WidgetConfigMode, widgetTypesData } from '@shared/models/widget.models'; @@ -28,6 +28,7 @@ import { IAliasController, IStateController } from '@core/api/widget-api.models' import { WidgetConfigComponentData, WidgetInfo } from '@home/models/widget-component.models'; import { isDefined, isDefinedAndNotNull, isString } from '@core/utils'; import { TranslateService } from '@ngx-translate/core'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; export interface AddWidgetDialogData { dashboard: Dashboard; @@ -41,12 +42,14 @@ export interface AddWidgetDialogData { selector: 'tb-add-widget-dialog', templateUrl: './add-widget-dialog.component.html', providers: [/*{provide: ErrorStateMatcher, useExisting: AddWidgetDialogComponent}*/], - styleUrls: [] + styleUrls: ['./add-widget-dialog.component.scss'], + encapsulation: ViewEncapsulation.None }) export class AddWidgetDialogComponent extends DialogComponent implements OnInit, ErrorStateMatcher { - widgetConfigModes = WidgetConfigMode; + @ViewChild('widgetConfigComponent') + widgetConfigComponent: WidgetConfigComponent; widgetFormGroup: UntypedFormGroup; @@ -59,17 +62,17 @@ export class AddWidgetDialogComponent extends DialogComponent
-
+
Top % @@ -40,8 +40,8 @@
-
{{ 'dashboard.no-image' | translate }}
- +
{{ 'dashboard.no-image' | translate }}
+
Right % @@ -53,18 +53,18 @@
-
+
+
-
- - +
-
-
+
@@ -71,11 +70,10 @@ [syncStateWithQueryParam]="syncStateWithQueryParam" [states]="dashboardConfiguration.states"> -
-
+
- + + + + @@ -360,18 +367,10 @@ [isReadOnly]="true" (closeDetails)="onEditWidgetClosed()">
- - + + {{ 'widget.basic-mode' | translate }} + {{ 'widget.advanced-mode' | translate }} +
-
+ (closeSearch)="dashboardWidgetSelectComponent.search = ''"> +
-
+
+ [(ngModel)]="dashboardWidgetSelectComponent.search" + placeholder="{{ ((!dashboardWidgetSelectComponent.widgetsBundle && dashboardWidgetSelectComponent.selectWidgetMode === 'bundles') + ? 'widgets-bundle.search' : 'widget.search') | translate }}">
-
+
+ + {{ 'widgets-bundle.widgets-bundles' | translate }} + {{ 'widget.all-widgets' | translate }} + + + {{ 'widget.all' | translate }} + {{ 'widget.actual' | translate }} + {{ 'widget.deprecated' | translate }} +
diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.scss b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.scss index d8d1de2905..09ba2d7f95 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.scss +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.scss @@ -125,7 +125,11 @@ div.tb-dashboard-page { mat-drawer-container.tb-dashboard-layouts { width: 100%; &.tb-shrinked { - width: 40%; + width: max(40%, calc(100% - 956px)); + + @media screen and (min-width: 2000px) { + width: max(40%, calc(100% - 1192px)); + } } } @@ -135,11 +139,11 @@ div.tb-dashboard-page { } @media #{$mat-gt-md} { - width: 75% !important; + width: min(85%, 956px) !important; } - @media #{$mat-gt-xl} { - width: 60% !important; + @media screen and (min-width: 2000px) { + width: min(60%, 1192px) !important; } } } diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts index 0faa98a978..8e2d58e232 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts @@ -55,7 +55,7 @@ import { } from '@app/shared/models/dashboard.models'; import { WINDOW } from '@core/services/window.service'; import { WindowMessage } from '@shared/models/window-message.model'; -import { deepClone, guid, isDefined, isDefinedAndNotNull, isNotEmptyStr } from '@app/core/utils'; +import { deepClone, guid, isDefined, isDefinedAndNotNull, isEqual, isNotEmptyStr } from '@app/core/utils'; import { DashboardContext, DashboardPageInitData, @@ -71,7 +71,6 @@ import { MediaBreakpoints } from '@shared/models/constants'; import { AuthUser } from '@shared/models/user.model'; import { getCurrentAuthState } from '@core/auth/auth.selectors'; import { - DatasourceType, Widget, WidgetConfig, WidgetInfo, @@ -120,7 +119,7 @@ import { ManageDashboardStatesDialogComponent, ManageDashboardStatesDialogData } from '@home/components/dashboard-page/states/manage-dashboard-states-dialog.component'; -import { ImportExportService } from '@home/components/import-export/import-export.service'; +import { ImportExportService } from '@shared/import-export/import-export.service'; import { AuthState } from '@app/core/auth/auth.models'; import { FiltersDialogComponent, FiltersDialogData } from '@home/components/filter/filters-dialog.component'; import { Filters } from '@shared/models/query/query.models'; @@ -146,10 +145,11 @@ import { IAliasController } from '@core/api/widget-api.models'; import { MatButton } from '@angular/material/button'; import { VersionControlComponent } from '@home/components/vc/version-control.component'; import { TbPopoverService } from '@shared/components/popover.service'; -import { tap } from 'rxjs/operators'; +import { map, tap } from 'rxjs/operators'; import { LayoutFixedSize, LayoutWidthType } from '@home/components/dashboard-page/layout/layout.models'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { ResizeObserver } from '@juggle/resize-observer'; +import { HasDirtyFlag } from '@core/guards/confirm-on-exit.guard'; // @dynamic @Component({ @@ -159,7 +159,17 @@ import { ResizeObserver } from '@juggle/resize-observer'; encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush }) -export class DashboardPageComponent extends PageComponent implements IDashboardController, OnInit, AfterViewInit, OnDestroy { +export class DashboardPageComponent extends PageComponent implements IDashboardController, HasDirtyFlag, OnInit, AfterViewInit, OnDestroy { + + private forcePristine = false; + + get isDirty(): boolean { + return this.isEdit && !this.forcePristine; + } + + set isDirty(value: boolean) { + this.forcePristine = !value; + } authState: AuthState = getCurrentAuthState(this.store); @@ -182,7 +192,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC } get hideToolbar(): boolean { - return (this.hideToolbarValue || this.hideToolbarSetting()) && !this.isEdit; + return ((this.hideToolbarValue || this.hideToolbarSetting()) && !this.isEdit) || (this.isEditingWidget || this.isAddingWidget); } @Input() @@ -220,7 +230,6 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC forceDashboardMobileMode = false; isAddingWidget = false; isAddingWidgetClosed = true; - searchBundle = ''; filterWidgetTypes: widgetType[] = null; isToolbarOpened = false; @@ -341,7 +350,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC private viewContainerRef: ViewContainerRef, private cd: ChangeDetectorRef, private sanitizer: DomSanitizer, - public elRef: ElementRef) { + public elRef: ElementRef, + private injector: Injector) { super(store); if (isDefinedAndNotNull(embeddedValue)) { this.embedded = embeddedValue; @@ -406,6 +416,9 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC this.updateLayoutSizes(); }); this.dashboardResize$.observe(this.dashboardContainer.nativeElement); + if (!this.widgetEditMode && !this.readonly && this.dashboardUtils.isEmptyDashboard(this.dashboard)) { + this.setEditMode(true, false); + } } private init(data: DashboardPageInitData) { @@ -655,12 +668,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC } } - public get dashboardLogo(): SafeUrl { - if (!this.dashboardLogoCache) { - const logo = this.dashboard.configuration.settings.dashboardLogoUrl || this.defaultDashboardLogo; - this.dashboardLogoCache = this.sanitizer.bypassSecurityTrustUrl(logo); - } - return this.dashboardLogoCache; + public get dashboardLogo(): string { + return this.dashboard.configuration.settings.dashboardLogoUrl || this.defaultDashboardLogo; } public showRightLayoutSwitch(): boolean { @@ -859,15 +868,21 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC $event.stopPropagation(); } this.dialog.open(ManageDashboardStatesDialogComponent, { + {states: {[id: string]: DashboardState}; widgets: {[id: string]: Widget}}>(ManageDashboardStatesDialogComponent, { disableClose: true, panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { - states: deepClone(this.dashboard.configuration.states) + states: deepClone(this.dashboard.configuration.states), + widgets: deepClone(this.dashboard.configuration.widgets) as {[id: string]: Widget} } - }).afterClosed().subscribe((states) => { - if (states) { - this.updateStates(states); + }).afterClosed().subscribe((result) => { + if (result) { + if (!isEqual(result.widgets, this.dashboard.configuration.widgets)) { + this.dashboard.configuration.widgets = result.widgets; + } + if (result.states) { + this.updateStates(result.states); + } } }); } @@ -910,6 +925,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC $event.stopPropagation(); } this.importExport.importWidget(this.dashboard, this.dashboardCtx.state, + this.editMissingAliases.bind(this), this.selectTargetLayout.bind(this), this.entityAliasesUpdated.bind(this), this.filtersUpdated.bind(this)).subscribe( (importData) => { if (importData) { @@ -926,6 +942,30 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC ); } + private editMissingAliases(widgets: Array, isSingleWidget: boolean, + customTitle: string, missingEntityAliases: EntityAliases): Observable { + return this.dialog.open(EntityAliasesDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + entityAliases: missingEntityAliases, + widgets, + customTitle, + isSingleWidget, + disableAdd: true + } + }).afterClosed().pipe( + map((updatedEntityAliases) => { + if (updatedEntityAliases) { + return updatedEntityAliases; + } else { + throw new Error('Unable to resolve missing entity aliases!'); + } + } + )); + } + public currentDashboardIdChanged(dashboardId: string) { if (!this.widgetEditMode) { this.dashboardCtx.stateController.cleanupPreservedStates(); @@ -943,6 +983,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC public toggleDashboardEditMode() { this.setEditMode(!this.isEdit, true); + this.notifyDashboardToggleEditMode(); } public saveDashboard() { @@ -1050,6 +1091,16 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC this.dashboardCtx.aliasController.updateFilters(this.dashboard.configuration.filters); } + private notifyDashboardToggleEditMode() { + if (this.widgetEditMode) { + const message: WindowMessage = { + type: 'widgetEditModeToggle', + data: this.isEdit + }; + this.window.parent.postMessage(JSON.stringify(message), '*'); + } + } + private notifyDashboardUpdated() { if (this.widgetEditMode) { const widget = this.layouts.main.layoutCtx.widgets.widgetByIndex(0); @@ -1139,19 +1190,15 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC addWidgetFromType(widget: WidgetInfo) { this.onAddWidgetClosed(); - this.searchBundle = ''; - this.widgetComponentService.getWidgetInfo(widget.bundleAlias, widget.typeAlias, widget.isSystemType).subscribe( + this.widgetComponentService.getWidgetInfo(widget.typeFullFqn).subscribe( (widgetTypeInfo) => { const config: WidgetConfig = this.dashboardUtils.widgetConfigFromWidgetType(widgetTypeInfo); - config.title = 'New ' + widgetTypeInfo.widgetName; + if (!config.title) { + config.title = 'New ' + widgetTypeInfo.widgetName; + } let newWidget: Widget = { - isSystemType: widget.isSystemType, - bundleAlias: widget.bundleAlias, - typeAlias: widgetTypeInfo.alias, + typeFullFqn: widgetTypeInfo.fullFqn, type: widgetTypeInfo.type, - title: 'New widget', - image: null, - description: null, sizeX: widgetTypeInfo.sizeX, sizeY: widgetTypeInfo.sizeY, config, @@ -1166,6 +1213,8 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC Widget>(AddWidgetDialogComponent, { disableClose: true, panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + maxWidth: '95vw', + injector: this.injector, data: { dashboard: this.dashboard, aliasController: this.dashboardCtx.aliasController, @@ -1413,13 +1462,10 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC return widgetContextActions; } - widgetBundleSelected(){ - this.searchBundle = ''; - } - clearSelectedWidgetBundle() { - this.searchBundle = ''; + this.dashboardWidgetSelectComponent.search = ''; this.dashboardWidgetSelectComponent.widgetsBundle = null; + this.dashboardWidgetSelectComponent.selectWidgetMode = 'bundles'; } editWidgetsTypesToDisplay($event: Event) { @@ -1469,10 +1515,6 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC this.cd.markForCheck(); } - onCloseSearchBundle() { - this.searchBundle = ''; - } - public updateDashboardImage($event: Event) { if ($event) { $event.stopPropagation(); diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.html b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.html index 2090e5f42c..fd4d77b507 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.html @@ -34,7 +34,7 @@ dashboard.state-controller - {{stateControllerId}} + {{ getStatesControllerTranslation(stateControllerId) }} @@ -55,10 +55,10 @@ {{ 'dashboard.display-dashboard-logo' | translate }} - - +
dashboard.toolbar-settings @@ -144,10 +144,10 @@ openOnInput formControlName="backgroundColor"> - - + dashboard.background-size-mode diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.ts index 4844211355..d71f4a2c8a 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.ts @@ -19,14 +19,12 @@ import { ErrorStateMatcher } from '@angular/material/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms'; +import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms'; import { Router } from '@angular/router'; import { DialogComponent } from '@app/shared/components/dialog.component'; -import { UtilsService } from '@core/services/utils.service'; import { TranslateService } from '@ngx-translate/core'; import { DashboardSettings, GridSettings, StateControllerId } from '@app/shared/models/dashboard.models'; import { isDefined, isUndefined } from '@core/utils'; -import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; import { StatesControllerService } from './states/states-controller.service'; export interface DashboardSettingsDialogData { @@ -46,21 +44,23 @@ export class DashboardSettingsDialogComponent extends DialogComponent([ + ['default', 'dashboard.state-controller-default'], + ]); + constructor(protected store: Store, protected router: Router, @Inject(MAT_DIALOG_DATA) public data: DashboardSettingsDialogData, @SkipSelf() private errorStateMatcher: ErrorStateMatcher, public dialogRef: MatDialogRef, - private fb: UntypedFormBuilder, - private utils: UtilsService, - private dashboardUtils: DashboardUtilsService, + private fb: FormBuilder, private translate: TranslateService, private statesControllerService: StatesControllerService) { super(store, router, dialogRef); @@ -182,7 +182,7 @@ export class DashboardSettingsDialogComponent extends DialogComponent -
-
-
-
- -
- {{ widget.title }} +
+ + + + + + + + + + + + + +
+
+ {{item.name}}
-
- {{widget.title}} - {{ 'widget.' + widget.type | translate }} - - {{ widget.description }} - +
+
+
{{ 'widget.' + item.widgetType + '-short' | translate }}
+
i
+
+
widget.deprecated
- -
-
+
+
+
+ {{ item.title }} +
+ + -
{{ 'widget.loading-widgets' | translate }}
- - + + widgets-bundle.empty - + class="mat-headline-5 tb-absolute-fill">{{(selectWidgetMode === 'bundles' ? 'widgets-bundle.empty' : 'widget.no-widgets-text') | translate}} -
+ + -
-
- -
- {{ widgetsBundle.title }} + + + + +
+
+ {{item.title}}
-
- {{ widgetsBundle.title }} - widgets-bundle.system - - {{ widgetsBundle.description }} - +
+
sys
+
i
- -
-
+
+
+
+ {{ item.title }} +
+
+ -
{{ 'widgets-bundle.loading-widgets-bundles' | translate }}
- + + widgets-bundle.no-widgets-bundles-text -
+ + +
+
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.scss b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.scss index a82f9c2f8b..642f6cc122 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.scss +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.scss @@ -13,79 +13,135 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@import '../../../../../scss/constants'; -:host{ - min-width: 100%; - min-height: 100%; - position: absolute; +.tb-dashboard-widget-select { background-color: #cfd8dc; + position: absolute; + inset: 0; - .widget-select { - padding: 12px 0 12px 12px; - background-color: #cfd8dc; - .mat-card-container { - flex: 0 0 100%; - max-width: 100%; + .mat-mdc-card.tb-widget-preview-card { + cursor: pointer; + transition: box-shadow 0.2s; + padding: 12px; - &:hover { - .mat-mdc-card { - box-shadow: 0 2px 6px 6px rgb(0 0 0 / 20%), 0 1px 4px 2px rgb(0 0 0 / 14%), 0 1px 6px 0 rgb(0 0 0 / 12%) - } - } + &:hover { + box-shadow: 0 2px 6px 6px rgb(0 0 0 / 20%), 0 1px 4px 2px rgb(0 0 0 / 14%), 0 1px 6px 0 rgb(0 0 0 / 12%) + } - .mat-mdc-card { - cursor: pointer; - transition: box-shadow 0.2s; - padding: 16px; + .title-container { + height: 32px; + display: flex; + flex-direction: row; + gap: 4px; - .preview-container { - text-align: center; - margin: auto 0; - } + .widget-title { + flex: 1; + color: rgba(0, 0, 0, 0.76); + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: 16px; + letter-spacing: 0.2px; + overflow: hidden; + text-overflow: ellipsis; + -webkit-line-clamp: 2; + display: -webkit-box; + -webkit-box-orient: vertical; + } - .preview { - max-width: 100%; - max-height: 100%; - object-fit: contain; - } + .title-items-container { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 2px; + } - .mat-mdc-card-title { - font-size: 20px; - line-height: normal; - margin-bottom: 8px; - } + .widget-deprecated { + display: flex; + height: 12px; + padding: 0 2px; + justify-content: flex-end; + align-items: center; + align-self: stretch; + color: rgba(209, 39, 48, 0.72); + font-size: 11px; + font-style: normal; + font-weight: 400; + letter-spacing: 0.2px; + } + + .title-items { + display: flex; + align-items: flex-start; + gap: 8px; - .mat-mdc-card-subtitle { - margin-bottom: 16px; - margin-top: -4px; - line-height: normal; - font-weight: normal; + .widget-type { + padding: 1px 4px; + border-radius: 4px; + background: rgba(236, 236, 236, 0.64); + color: rgba(0, 0, 0, 0.54); + font-size: 11px; + font-style: normal; + font-weight: 500; + line-height: 16px; + letter-spacing: 0.017px; } - .mat-mdc-card-content { - font-size: 14px; - line-height: 18px; - color: #666; - margin-bottom: 16px; - padding: 0; + .info-banner { + display: flex; + width: 18px; + padding: 1px 0; + justify-content: center; + align-items: center; + border-radius: 4px; + color: #fff; + font-size: 11px; + font-style: normal; + font-weight: 600; + line-height: 16px; + letter-spacing: 0.017px; + &:before { + opacity: 0.5; + } + & > span { + z-index: 1; + } } } } - @media #{$mat-gt-xs} { - .mat-card-container { - flex: 0 0 50%; - max-width: 50%; + &.loading-cell { + height: 100%; + min-height: 200px; + + .preview-container, .title-container { + background: linear-gradient(110deg, #ececec 8%, #f5f5f5 18%, #ececec 33%); + border-radius: 5px; + background-size: 200% 100%; + animation: 1s shine linear infinite; } } - @media screen and (min-width: 2000px) { - .mat-card-container { - flex: 0 0 33.333333%; - max-width: 33.333333%; - } + .preview-container { + position: relative; + } + + .preview-spacer { + margin-top: 80%; + } + + .preview { + position: absolute; + inset: 0; + object-fit: contain; + width: 100%; + height: 100%; } } } +@keyframes shine { + to { + background-position-x: -200%; + } +} diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.ts index bfe1c492ec..0e73c35724 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-widget-select.component.ts @@ -14,140 +14,222 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; import { IAliasController } from '@core/api/widget-api.models'; import { NULL_UUID } from '@shared/models/id/has-uuid'; import { WidgetService } from '@core/http/widget.service'; -import { WidgetInfo, widgetType } from '@shared/models/widget.models'; -import { distinctUntilChanged, map, publishReplay, refCount, share, switchMap, tap } from 'rxjs/operators'; -import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs'; -import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; -import { isDefinedAndNotNull } from '@core/utils'; +import { + DeprecatedFilter, + fullWidgetTypeFqn, + WidgetInfo, + widgetType, + WidgetTypeInfo +} from '@shared/models/widget.models'; +import { debounceTime, distinctUntilChanged, map, skip } from 'rxjs/operators'; +import { BehaviorSubject, combineLatest } from 'rxjs'; +import { isObject } from '@core/utils'; +import { PageLink } from '@shared/models/page/page-link'; +import { Direction } from '@shared/models/page/sort-order'; +import { GridEntitiesFetchFunction, ScrollGridColumns } from '@shared/components/grid/scroll-grid-datasource'; +import { ItemSizeStrategy } from '@shared/components/grid/scroll-grid.component'; + +type selectWidgetMode = 'bundles' | 'allWidgets'; + +interface WidgetsFilter { + search: string; + filter: widgetType[]; + deprecatedFilter: DeprecatedFilter; +} + +interface BundleWidgetsFilter extends WidgetsFilter { + widgetsBundleId: string; +} @Component({ selector: 'tb-dashboard-widget-select', templateUrl: './dashboard-widget-select.component.html', - styleUrls: ['./dashboard-widget-select.component.scss'] + styleUrls: ['./dashboard-widget-select.component.scss'], + encapsulation: ViewEncapsulation.None }) export class DashboardWidgetSelectComponent implements OnInit { - private search$ = new BehaviorSubject(''); + private searchSubject = new BehaviorSubject(''); + private search$ = this.searchSubject.asObservable().pipe( + debounceTime(150)); + private filterWidgetTypes$ = new BehaviorSubject>(null); - private widgetsInfo: Observable>; - private widgetsBundleValue: WidgetsBundle; + private deprecatedFilter$ = new BehaviorSubject(DeprecatedFilter.ACTUAL); + private selectWidgetMode$ = new BehaviorSubject('bundles'); + private widgetsBundle$ = new BehaviorSubject(null); + widgetTypes = new Set(); + hasDeprecated = false; + + @Input() + aliasController: IAliasController; - widgets$: Observable>; - loadingWidgetsSubject: BehaviorSubject = new BehaviorSubject(false); - loadingWidgets$ = this.loadingWidgetsSubject.pipe( - share() - ); - widgetsBundles$: Observable>; - loadingWidgetBundlesSubject: BehaviorSubject = new BehaviorSubject(true); - loadingWidgetBundles$ = this.loadingWidgetBundlesSubject.pipe( - share() - ); + @Input() + set search(search: string) { + this.searchSubject.next(search); + } - set widgetsBundle(widgetBundle: WidgetsBundle) { - if (this.widgetsBundleValue !== widgetBundle) { - this.widgetsBundleValue = widgetBundle; - if (widgetBundle === null) { + get search(): string { + return this.searchSubject.value; + } + + @Input() + set filterWidgetTypes(widgetTypes: Array) { + this.filterWidgetTypes$.next(widgetTypes); + } + + get filterWidgetTypes(): Array { + return this.filterWidgetTypes$.value; + } + + @Input() + set selectWidgetMode(mode: selectWidgetMode) { + if (this.selectWidgetMode$.value !== mode) { + if (mode === 'bundles' && this.widgetsBundle$.value === null) { this.widgetTypes.clear(); + this.hasDeprecated = false; + } else { + this.widgetTypes = new Set(Object.keys(widgetType).map(t => t as widgetType)); + this.hasDeprecated = true; } this.filterWidgetTypes$.next(null); - this.widgetsInfo = null; + this.deprecatedFilter$.next(DeprecatedFilter.ACTUAL); + this.selectWidgetMode$.next(mode); } } - get widgetsBundle(): WidgetsBundle { - return this.widgetsBundleValue; + get selectWidgetMode(): selectWidgetMode { + return this.selectWidgetMode$.value; } @Input() - aliasController: IAliasController; + set deprecatedFilter(filter: DeprecatedFilter) { + this.deprecatedFilter$.next(filter); + } - @Input() - set searchBundle(search: string) { - this.search$.next(search); + get deprecatedFilter(): DeprecatedFilter { + return this.deprecatedFilter$.value; } - @Input() - set filterWidgetTypes(widgetTypes: Array) { - this.filterWidgetTypes$.next(widgetTypes); + set widgetsBundle(widgetBundle: WidgetsBundle) { + if (this.widgetsBundle$.value !== widgetBundle) { + if (widgetBundle === null && this.selectWidgetMode$.value !== 'allWidgets') { + this.widgetTypes.clear(); + this.hasDeprecated = false; + } else { + this.widgetTypes = new Set(Object.keys(widgetType).map(t => t as widgetType)); + this.hasDeprecated = true; + } + this.filterWidgetTypes$.next(null); + this.deprecatedFilter$.next(DeprecatedFilter.ACTUAL); + this.widgetsBundle$.next(widgetBundle); + } } - get filterWidgetTypes(): Array { - return this.filterWidgetTypes$.value; + get widgetsBundle(): WidgetsBundle { + return this.widgetsBundle$.value; } @Output() widgetSelected: EventEmitter = new EventEmitter(); - @Output() - widgetsBundleSelected: EventEmitter = new EventEmitter(); + columns: ScrollGridColumns = { + columns: 2, + breakpoints: { + 'screen and (min-width: 2000px)': 5, + 'screen and (min-width: 1097px)': 4, + 'gt-sm': 3, + 'screen and (min-width: 721px)': 4, + 'screen and (min-width: 485px)': 3 + } + }; + + gridWidgetsItemSizeStrategy: ItemSizeStrategy = { + defaultItemSize: 160, + itemSizeFunction: itemWidth => (itemWidth - 24) * 0.8 + 76 + }; + + widgetBundlesFetchFunction: GridEntitiesFetchFunction; + allWidgetsFetchFunction: GridEntitiesFetchFunction; + widgetsFetchFunction: GridEntitiesFetchFunction; + + widgetsBundleFilter = ''; + allWidgetsFilter: WidgetsFilter = {search: '', filter: null, deprecatedFilter: DeprecatedFilter.ACTUAL}; + widgetsFilter: BundleWidgetsFilter = {search: '', filter: null, deprecatedFilter: DeprecatedFilter.ACTUAL, widgetsBundleId: null}; constructor(private widgetsService: WidgetService, - private sanitizer: DomSanitizer, private cd: ChangeDetectorRef) { - this.widgetsBundles$ = this.search$.asObservable().pipe( + + this.widgetBundlesFetchFunction = (pageSize, page, filter) => { + const pageLink = new PageLink(pageSize, page, filter, { + property: 'title', + direction: Direction.ASC + }); + return this.widgetsService.getWidgetBundles(pageLink, true); + }; + + this.allWidgetsFetchFunction = (pageSize, page, filter) => { + const pageLink = new PageLink(pageSize, page, filter.search, { + property: 'name', + direction: Direction.ASC + }); + return this.widgetsService.getWidgetTypes(pageLink, false, true, filter.deprecatedFilter, filter.filter); + }; + + this.widgetsFetchFunction = (pageSize, page, filter) => { + const pageLink = new PageLink(pageSize, page, filter.search, { + property: 'name', + direction: Direction.ASC + }); + return this.widgetsService.getBundleWidgetTypeInfos(pageLink, filter.widgetsBundleId, + true, filter.deprecatedFilter, filter.filter); + }; + + this.search$.pipe( distinctUntilChanged(), - switchMap(search => this.fetchWidgetBundle(search)) + skip(1) + ).subscribe( + (search) => { + this.widgetsBundleFilter = search; + this.cd.markForCheck(); + } ); - this.widgets$ = combineLatest([this.search$.asObservable(), this.filterWidgetTypes$.asObservable()]).pipe( + + combineLatest({search: this.search$, filter: this.filterWidgetTypes$.asObservable(), + deprecatedFilter: this.deprecatedFilter$.asObservable()}).pipe( distinctUntilChanged((oldValue, newValue) => JSON.stringify(oldValue) === JSON.stringify(newValue)), - switchMap(search => this.fetchWidget(...search)) + skip(1) + ).subscribe( + (filter) => { + this.allWidgetsFilter = filter; + this.cd.markForCheck(); + } ); - } - ngOnInit(): void { + combineLatest({search: this.search$, widgetsBundleId: this.widgetsBundle$.pipe(map(wb => wb !== null ? wb.id.id : null)), + filter: this.filterWidgetTypes$.asObservable(), deprecatedFilter: this.deprecatedFilter$.asObservable()}).pipe( + distinctUntilChanged((oldValue, newValue) => JSON.stringify(oldValue) === JSON.stringify(newValue)), + skip(1) + ).subscribe( + (filter) => { + if (filter.widgetsBundleId) { + this.widgetsFilter = filter; + this.cd.markForCheck(); + } + } + ); } - private getWidgets(): Observable> { - if (!this.widgetsInfo) { - if (this.widgetsBundle !== null) { - const bundleAlias = this.widgetsBundle.alias; - const isSystem = this.widgetsBundle.tenantId.id === NULL_UUID; - this.loadingWidgetsSubject.next(true); - this.widgetsInfo = this.widgetsService.getBundleWidgetTypeInfos(bundleAlias, isSystem).pipe( - map(widgets => { - widgets = widgets.sort((a, b) => b.createdTime - a.createdTime); - const widgetTypes = new Set(); - const widgetInfos = widgets.map((widgetTypeInfo) => { - widgetTypes.add(widgetTypeInfo.widgetType); - const widget: WidgetInfo = { - isSystemType: isSystem, - bundleAlias, - typeAlias: widgetTypeInfo.alias, - type: widgetTypeInfo.widgetType, - title: widgetTypeInfo.name, - image: widgetTypeInfo.image, - description: widgetTypeInfo.description - }; - return widget; - } - ); - setTimeout(() => { - this.widgetTypes = widgetTypes; - this.cd.markForCheck(); - }); - return widgetInfos; - }), - tap(() => { - this.loadingWidgetsSubject.next(false); - }), - publishReplay(1), - refCount() - ); - } else { - this.widgetsInfo = of([]); - } - } - return this.widgetsInfo; + ngOnInit(): void { } - onWidgetClicked($event: Event, widget: WidgetInfo): void { - this.widgetSelected.emit(widget); + onWidgetClicked($event: Event, widget: WidgetTypeInfo): void { + this.widgetSelected.emit(this.toWidgetInfo(widget)); } isSystem(item: WidgetsBundle): boolean { @@ -157,45 +239,24 @@ export class DashboardWidgetSelectComponent implements OnInit { selectBundle($event: Event, bundle: WidgetsBundle) { $event.preventDefault(); this.widgetsBundle = bundle; - this.search$.next(''); - this.widgetsBundleSelected.emit(bundle); - } - - getPreviewImage(imageUrl: string | null): SafeUrl | string { - if (isDefinedAndNotNull(imageUrl)) { - return this.sanitizer.bypassSecurityTrustUrl(imageUrl); + if (bundle.title?.toLowerCase().includes(this.search.toLowerCase()) || + bundle.description?.toLowerCase().includes(this.search.toLowerCase())) { + this.searchSubject.next(''); } - return '/assets/widget-preview-empty.svg'; - } - - private getWidgetsBundle(): Observable> { - return this.widgetsService.getAllWidgetsBundles().pipe( - tap(() => this.loadingWidgetBundlesSubject.next(false)), - publishReplay(1), - refCount() - ); } - private fetchWidgetBundle(search: string): Observable> { - return this.getWidgetsBundle().pipe( - map(bundles => search ? bundles.filter( - bundle => ( - bundle.title?.toLowerCase().includes(search.toLowerCase()) || - bundle.description?.toLowerCase().includes(search.toLowerCase()) - )) : bundles - ) - ); + isObject(value: any): boolean { + return isObject(value); } - private fetchWidget(search: string, filter: widgetType[]): Observable> { - return this.getWidgets().pipe( - map(widgets => filter ? widgets.filter((widget) => filter.includes(widget.type)) : widgets), - map(widgets => search ? widgets.filter( - widget => ( - widget.title?.toLowerCase().includes(search.toLowerCase()) || - widget.description?.toLowerCase().includes(search.toLowerCase()) - )) : widgets - ) - ); + private toWidgetInfo(widgetTypeInfo: WidgetTypeInfo): WidgetInfo { + return { + typeFullFqn: fullWidgetTypeFqn(widgetTypeInfo), + type: widgetTypeInfo.widgetType, + title: widgetTypeInfo.name, + image: widgetTypeInfo.image, + description: widgetTypeInfo.description, + deprecated: widgetTypeInfo.deprecated + }; } } diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.html b/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.html index 837c0abcad..644fb642f6 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.html @@ -18,12 +18,12 @@
-
-
- - -
+
+ +
+ + +
- - +
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.scss b/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.scss index 9777decdb0..9c4e20a7a5 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.scss @@ -14,9 +14,17 @@ * limitations under the License. */ :host { + .widget-preview-background { + position: absolute; + top: 68px; + left: 0; + right: 0; + bottom: 0; + background: #fff; + } .widget-preview-section { position: absolute; - top: 72px; + top: 0; left: 16px; right: 16px; bottom: 16px; diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.ts index 2528ada4ae..e5a32a7a3e 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/edit-widget.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -26,6 +26,7 @@ import { WidgetComponentService } from '@home/components/widget/widget-component import { WidgetConfigComponentData } from '../../models/widget-component.models'; import { isDefined, isDefinedAndNotNull, isString } from '@core/utils'; import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; @Component({ selector: 'tb-edit-widget', @@ -34,6 +35,9 @@ import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; }) export class EditWidgetComponent extends PageComponent implements OnInit, OnChanges { + @ViewChild('widgetConfigComponent') + widgetConfigComponent: WidgetConfigComponent; + @Input() dashboard: Dashboard; @@ -64,17 +68,12 @@ export class EditWidgetComponent extends PageComponent implements OnInit, OnChan previewMode = false; - hasBasicMode = false; - get widgetConfigMode(): WidgetConfigMode { - return this.hasBasicMode ? (this.widgetConfig?.config?.configMode || WidgetConfigMode.advanced) : WidgetConfigMode.advanced; + return this.widgetConfigComponent?.widgetConfigMode; } set widgetConfigMode(widgetConfigMode: WidgetConfigMode) { - if (this.hasBasicMode) { - this.widgetConfig.config.configMode = widgetConfigMode; - this.widgetFormGroup.markAsDirty(); - } + this.widgetConfigComponent.setWidgetConfigMode(widgetConfigMode); } private currentWidgetConfigChanged = false; @@ -169,9 +168,9 @@ export class EditWidgetComponent extends PageComponent implements OnInit, OnChan settingsDirective: widgetInfo.settingsDirective, dataKeySettingsDirective: widgetInfo.dataKeySettingsDirective, latestDataKeySettingsDirective: widgetInfo.latestDataKeySettingsDirective, + hasBasicMode: isDefinedAndNotNull(widgetInfo.hasBasicMode) ? widgetInfo.hasBasicMode : false, basicModeDirective: widgetInfo.basicModeDirective }; - this.hasBasicMode = isDefinedAndNotNull(widgetInfo.hasBasicMode) ? widgetInfo.hasBasicMode : false; this.widgetFormGroup.reset({widgetConfig: this.widgetConfig}); } } diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/layout/dashboard-layout.component.html b/ui-ngx/src/app/modules/home/components/dashboard-page/layout/dashboard-layout.component.html index f724f81f04..d43f2af10f 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/layout/dashboard-layout.component.html +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/layout/dashboard-layout.component.html @@ -23,7 +23,7 @@
; hotKeys: Hotkey[] = []; @@ -92,6 +94,7 @@ export class DashboardLayoutComponent extends PageComponent implements ILayoutCo constructor(protected store: Store, private translate: TranslateService, private itembuffer: ItemBufferService, + private imagePipe: ImagePipe, private sanitizer: DomSanitizer) { super(store); this.initHotKeys(); @@ -188,8 +191,10 @@ export class DashboardLayoutComponent extends PageComponent implements ILayoutCo 'background-attachment': 'scroll', 'background-size': this.layoutCtx.gridSettings.backgroundSizeMode || '100%', 'background-position': '0% 0%'}; - this.backgroundImage = this.layoutCtx.gridSettings.backgroundImageUrl ? - this.sanitizer.bypassSecurityTrustStyle('url(' + this.layoutCtx.gridSettings.backgroundImageUrl + ')') : 'none'; + this.backgroundImage$ = this.layoutCtx.gridSettings.backgroundImageUrl ? + this.imagePipe.transform(this.layoutCtx.gridSettings.backgroundImageUrl, {asString: true, ignoreLoadingImage: true}).pipe( + map((imageUrl) => this.sanitizer.bypassSecurityTrustStyle('url(' + imageUrl + ')')) + ) : of('none'); } reload() { diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.html b/ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.html index 8e6bf449e7..a260016760 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/states/manage-dashboard-states-dialog.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+

dashboard.manage-states

@@ -96,7 +96,7 @@ - +
@@ -107,6 +107,13 @@ (click)="editState($event, state)"> edit + + *ngIf="!deviceCredentialsMqttFormGroup.get('userName').value && !disabled; else copyUserName"> autorenew @@ -85,7 +86,7 @@ matTooltip="{{ 'device.generate-password' | translate }}" matTooltipPosition="above" (click)="generate('password')" - *ngIf="!deviceCredentialsMqttFormGroup.get('password').value; else copyPassword"> + *ngIf="!deviceCredentialsMqttFormGroup.get('password').value && !disabled; else copyPassword"> autorenew diff --git a/ui-ngx/src/app/modules/home/components/device/device-credentials.component.html b/ui-ngx/src/app/modules/home/components/device/device-credentials.component.html index 303c46ef70..fd666e1398 100644 --- a/ui-ngx/src/app/modules/home/components/device/device-credentials.component.html +++ b/ui-ngx/src/app/modules/home/components/device/device-credentials.component.html @@ -16,14 +16,14 @@ -->
- - device.credentials-type - - +
+
device.credentials-type
+ + {{ credentialTypeNamesMap.get(credentialsType) }} - - - + + +
@@ -36,13 +36,14 @@ matTooltip="{{ 'device.generate-access-token' | translate }}" matTooltipPosition="above" (click)="generate('credentialsId')" - *ngIf="!deviceCredentialsFormGroup.get('credentialsId').value; else copyAccessToken"> + *ngIf="!deviceCredentialsFormGroup.get('credentialsId').value && !disabled; else copyAccessToken"> autorenew DeviceCredentialsComponent), multi: true, }], - styleUrls: [] + styleUrls: ['./device-credentials.component.scss'] }) export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, Validator, OnDestroy { @@ -73,9 +74,13 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, } } + @Input() + @coerceBoolean() + initAccessToken = false; + private destroy$ = new Subject(); - deviceCredentialsFormGroup: UntypedFormGroup; + deviceCredentialsFormGroup: FormGroup; deviceCredentialsType = DeviceCredentialsType; @@ -83,9 +88,10 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, credentialTypeNamesMap = credentialTypeNames; - private propagateChange = (v: any) => {}; + private propagateChange = null; + private propagateChangePending = false; - constructor(public fb: UntypedFormBuilder) { + constructor(public fb: FormBuilder) { this.deviceCredentialsFormGroup = this.fb.group({ credentialsType: [DeviceCredentialsType.ACCESS_TOKEN], credentialsId: [null], @@ -98,8 +104,8 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, }); this.deviceCredentialsFormGroup.get('credentialsType').valueChanges.pipe( takeUntil(this.destroy$) - ).subscribe(() => { - this.credentialsTypeChanged(); + ).subscribe((value) => { + this.credentialsTypeChanged(value); }); } @@ -107,6 +113,10 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, if (this.disabled) { this.deviceCredentialsFormGroup.disable({emitEvent: false}); } + if (this.initAccessToken && !this.deviceCredentialsFormGroup.get('credentialsId').value && + this.deviceCredentialsFormGroup.get('credentialsType').value === DeviceCredentialsType.ACCESS_TOKEN) { + this.deviceCredentialsFormGroup.get('credentialsId').patchValue(generateSecret(20)); + } } ngOnDestroy() { @@ -128,11 +138,21 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, updateView() { const deviceCredentialsValue = this.deviceCredentialsFormGroup.value; - this.propagateChange(deviceCredentialsValue); + if (this.propagateChange) { + this.propagateChange(deviceCredentialsValue); + } else { + this.propagateChangePending = true; + } } registerOnChange(fn: any): void { this.propagateChange = fn; + if (this.propagateChangePending) { + this.propagateChangePending = false; + setTimeout(() => { + this.updateView(); + }, 0); + } } registerOnTouched(fn: any): void {} @@ -144,11 +164,10 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, } else { this.deviceCredentialsFormGroup.enable({emitEvent: false}); this.updateValidators(); - this.deviceCredentialsFormGroup.updateValueAndValidity(); } } - public validate(c: UntypedFormControl) { + public validate(c: FormControl) { return this.deviceCredentialsFormGroup.valid ? null : { deviceCredentials: { valid: false, @@ -156,12 +175,15 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, }; } - credentialsTypeChanged(): void { + credentialsTypeChanged(type: DeviceCredentialsType): void { this.deviceCredentialsFormGroup.patchValue({ credentialsId: null, credentialsValue: null }); this.updateValidators(); + if (type === DeviceCredentialsType.ACCESS_TOKEN && this.initAccessToken) { + this.deviceCredentialsFormGroup.get('credentialsId').patchValue(generateSecret(20)); + } } updateValidators(): void { diff --git a/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-config.ts b/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-config.ts index 5889ebcdf2..f970ad80f4 100644 --- a/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/edge/edge-downlink-table-config.ts @@ -107,9 +107,15 @@ export class EdgeDownlinkTableConfig extends EntityTableConfig('createdTime', 'event.event-time', this.datePipe, '120px'), new EntityTableColumn('type', 'event.type', '25%', - entity => this.translate.instant(edgeEventTypeTranslations.get(entity.type)), entity => ({}), false), + entity => { + let key = edgeEventTypeTranslations.get(entity.type); + return key ? this.translate.instant(key) : entity.type; + }, entity => ({}), false), new EntityTableColumn('action', 'edge.event-action', '25%', - entity => this.translate.instant(edgeEventActionTypeTranslations.get(entity.action)), entity => ({}), false), + entity => { + let key = edgeEventActionTypeTranslations.get(entity.action); + return key ? this.translate.instant(key) : entity.action; + }, entity => ({}), false), new EntityTableColumn('entityId', 'edge.entity-id', '40%', (entity) => entity.entityId ? entity.entityId : '', () => ({}), false), new EntityTableColumn('status', 'event.status', '10%', diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html index 65f4bed2ae..94119f492b 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html @@ -60,7 +60,7 @@ (click)="entitiesTableConfig.addActionDescriptors[0].onAction($event)" matTooltip="{{ entitiesTableConfig.addActionDescriptors[0].name }}" matTooltipPosition="above"> - {{entitiesTableConfig.addActionDescriptors[0].icon}} + {{entitiesTableConfig.addActionDescriptors[0].icon}} @@ -86,8 +86,7 @@ matTooltip="{{ actionDescriptor.name }}" matTooltipPosition="above" (click)="actionDescriptor.onAction($event)"> - - {{actionDescriptor.icon}} + {{actionDescriptor.icon}}
@@ -166,7 +165,12 @@ [matTooltip]="cellTooltip(entity, column, row)" matTooltipPosition="above" [ngStyle]="cellStyle(entity, column, row)"> - + + + + + + @@ -177,7 +181,7 @@ tooltipText="{{ column.actionCell.nameFunction ? column.actionCell.nameFunction(entity) : column.actionCell.name }}" tooltipPosition="above" [icon]="column.actionCell.icon" - [mdiIcon]="column.actionCell.mdiIcon" [style]="column.actionCell.style"> + [style]="column.actionCell.style"> @@ -186,9 +190,9 @@ matTooltip="{{ column.actionCell.nameFunction ? column.actionCell.nameFunction(entity) : column.actionCell.name }}" matTooltipPosition="above" (click)="column.actionCell.onAction($event, entity)"> - + {{column.actionCell.icon}} - + @@ -204,8 +208,9 @@ matTooltip="{{ column.actionDescriptor.nameFunction ? column.actionDescriptor.nameFunction(entity) : column.actionDescriptor.name }}" matTooltipPosition="above" (click)="column.actionDescriptor.onAction($event, entity)"> - - {{column.actionDescriptor.icon}} + + {{column.actionDescriptor.icon}} + @@ -218,18 +223,18 @@ -
+
-
+
diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts index b868fd1e47..1eb2566029 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts @@ -24,6 +24,7 @@ import { EventEmitter, Input, OnChanges, + OnDestroy, OnInit, SimpleChanges, ViewChild @@ -36,9 +37,9 @@ import { MatDialog } from '@angular/material/dialog'; import { MatPaginator } from '@angular/material/paginator'; import { MatSort, SortDirection } from '@angular/material/sort'; import { EntitiesDataSource } from '@home/models/datasource/entity-datasource'; -import { catchError, debounceTime, distinctUntilChanged, map, skip, tap } from 'rxjs/operators'; +import { catchError, debounceTime, distinctUntilChanged, map, skip, takeUntil } from 'rxjs/operators'; import { Direction, SortOrder } from '@shared/models/page/sort-order'; -import { forkJoin, fromEvent, merge, Observable, of, Subscription } from 'rxjs'; +import { forkJoin, merge, Observable, of, Subject, Subscription } from 'rxjs'; import { TranslateService } from '@ngx-translate/core'; import { BaseData, HasId } from '@shared/models/base-data'; import { ActivatedRoute, QueryParamsHandling, Router } from '@angular/router'; @@ -47,6 +48,7 @@ import { CellActionDescriptorType, EntityActionTableColumn, EntityColumn, + EntityLinkTableColumn, EntityTableColumn, EntityTableConfig, GroupActionDescriptor, @@ -59,12 +61,13 @@ import { AddEntityDialogData, EntityAction } from '@home/models/entity/entity-co import { calculateIntervalStartEndTime, HistoryWindowType, Timewindow } from '@shared/models/time/time.models'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { TbAnchorComponent } from '@shared/components/tb-anchor.component'; -import { isDefined, isEmptyStr, isEqual, isString, isUndefined } from '@core/utils'; +import { isDefined, isEqual, isNotEmptyStr, isUndefined } from '@core/utils'; import { HasUUID } from '@shared/models/id/has-uuid'; import { ResizeObserver } from '@juggle/resize-observer'; import { hidePageSizePixelValue } from '@shared/models/constants'; import { EntitiesTableAction, IEntitiesTableComponent } from '@home/models/entity/entity-table-component.models'; import { EntityDetailsPanelComponent } from '@home/components/entity/entity-details-panel.component'; +import { FormBuilder } from '@angular/forms'; @Component({ selector: 'tb-entities-table', @@ -72,7 +75,7 @@ import { EntityDetailsPanelComponent } from '@home/components/entity/entity-deta styleUrls: ['./entities-table.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) -export class EntitiesTableComponent extends PageComponent implements IEntitiesTableComponent, AfterViewInit, OnInit, OnChanges { +export class EntitiesTableComponent extends PageComponent implements IEntitiesTableComponent, AfterViewInit, OnInit, OnChanges, OnDestroy { @Input() entitiesTableConfig: EntityTableConfig>; @@ -121,12 +124,13 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa @ViewChild('entityDetailsPanel') entityDetailsPanel: EntityDetailsPanelComponent; + textSearch = this.fb.control('', {nonNullable: true}); + private updateDataSubscription: Subscription; private viewInited = false; private widgetResize$: ResizeObserver; - - private rxSubscriptions = new Array(); + private destroy$ = new Subject(); constructor(protected store: Store, public route: ActivatedRoute, @@ -137,7 +141,8 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa private cd: ChangeDetectorRef, private router: Router, private componentFactoryResolver: ComponentFactoryResolver, - private elementRef: ElementRef) { + private elementRef: ElementRef, + private fb: FormBuilder) { super(store); } @@ -145,11 +150,11 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa if (this.entitiesTableConfig) { this.init(this.entitiesTableConfig); } else { - this.rxSubscriptions.push(this.route.data.subscribe( - (data) => { + this.route.data.pipe( + takeUntil(this.destroy$) + ).subscribe((data) => { this.init(data.entitiesTableConfig); - } - )); + }); } this.widgetResize$ = new ResizeObserver(() => { const showHidePageSize = this.elementRef.nativeElement.offsetWidth < hidePageSizePixelValue; @@ -165,10 +170,8 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa if (this.widgetResize$) { this.widgetResize$.disconnect(); } - this.rxSubscriptions.forEach((subscription) => { - subscription.unsubscribe(); - }); - this.rxSubscriptions.length = 0; + this.destroy$.next(); + this.destroy$.complete(); } ngOnChanges(changes: SimpleChanges): void { @@ -268,9 +271,12 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa if (routerQueryParams.hasOwnProperty('pageSize')) { this.pageLink.pageSize = Number(routerQueryParams.pageSize); } - if (routerQueryParams.hasOwnProperty('textSearch') && !isEmptyStr(routerQueryParams.textSearch)) { + const textSearchParam = routerQueryParams.textSearch; + if (isNotEmptyStr(textSearchParam)) { + const decodedTextSearch = decodeURI(textSearchParam); this.textSearchMode = true; - this.pageLink.textSearch = decodeURI(routerQueryParams.textSearch); + this.pageLink.textSearch = decodedTextSearch.trim(); + this.textSearch.setValue(decodedTextSearch, {emitEvent: false}); } } this.dataSource = this.entitiesTableConfig.dataSource(this.dataLoaded.bind(this)); @@ -304,37 +310,46 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa } ngAfterViewInit() { - - fromEvent(this.searchInputField.nativeElement, 'keyup') - .pipe( - debounceTime(150), - distinctUntilChanged(), - tap(() => { - const queryParams: PageQueryParam = { - textSearch: isString(this.pageLink.textSearch) && this.pageLink.textSearch !== '' ? encodeURI(this.pageLink.textSearch) : null - }; - if (this.displayPagination) { - this.paginator.pageIndex = 0; - queryParams.page = null; - } - this.updatedRouterParamsAndData(queryParams); - }) - ) - .subscribe(); + this.textSearch.valueChanges.pipe( + debounceTime(150), + distinctUntilChanged((prev, current) => (this.pageLink.textSearch ?? '') === current.trim()), + takeUntil(this.destroy$) + ).subscribe(value => { + if (this.pageMode) { + const queryParams: PageQueryParam = { + textSearch: isNotEmptyStr(value) ? encodeURI(value) : null, + page: null + }; + this.updatedRouterParamsAndData(queryParams); + } else { + this.pageLink.textSearch = isNotEmptyStr(value) ? value.trim() : null; + if (this.displayPagination) { + this.paginator.pageIndex = 0; + } + this.updateData(); + } + }); if (this.pageMode) { - this.route.queryParams.pipe(skip(1)).subscribe((params: PageQueryParam) => { + this.route.queryParams.pipe( + skip(1), + takeUntil(this.destroy$) + ).subscribe((params: PageQueryParam) => { if (this.displayPagination) { this.paginator.pageIndex = Number(params.page) || 0; this.paginator.pageSize = Number(params.pageSize) || this.defaultPageSize; } this.sort.active = params.property || this.entitiesTableConfig.defaultSortOrder.property; this.sort.direction = (params.direction || this.entitiesTableConfig.defaultSortOrder.direction).toLowerCase() as SortDirection; - if (params.hasOwnProperty('textSearch') && !isEmptyStr(params.textSearch)) { + const textSearchParam = params.textSearch; + if (isNotEmptyStr(textSearchParam)) { + const decodedTextSearch = decodeURI(textSearchParam); this.textSearchMode = true; - this.pageLink.textSearch = decodeURI(params.textSearch); + this.pageLink.textSearch = decodedTextSearch.trim(); + this.textSearch.setValue(decodedTextSearch, {emitEvent: false}); } else { this.pageLink.textSearch = null; + this.textSearch.reset('', {emitEvent: false}); } this.updateData(); }); @@ -366,20 +381,16 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa ); if (this.displayPagination) { paginatorSubscription$ = this.paginator.page.asObservable().pipe( - map((data) => { - return { - page: data.pageIndex === 0 ? null : data.pageIndex, - pageSize: data.pageSize === this.defaultPageSize ? null : data.pageSize - }; - }) + map((data) => ({ + page: data.pageIndex === 0 ? null : data.pageIndex, + pageSize: data.pageSize === this.defaultPageSize ? null : data.pageSize + })) ); } this.updateDataSubscription = ((this.displayPagination ? merge(sortSubscription$, paginatorSubscription$) : sortSubscription$) as Observable).pipe( - tap((queryParams) => { - this.updatedRouterParamsAndData(queryParams); - }) - ).subscribe(); + takeUntil(this.destroy$) + ).subscribe(queryParams => this.updatedRouterParamsAndData(queryParams)); } addEnabled() { @@ -572,7 +583,6 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa enterFilterMode() { this.textSearchMode = true; - this.pageLink.textSearch = ''; setTimeout(() => { this.searchInputField.nativeElement.focus(); this.searchInputField.nativeElement.setSelectionRange(0, 0); @@ -581,20 +591,13 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa exitFilterMode() { this.textSearchMode = false; - this.pageLink.textSearch = null; - const queryParams: PageQueryParam = { - textSearch: null - }; - if (this.displayPagination) { - this.paginator.pageIndex = 0; - queryParams.page = null; - } - this.updatedRouterParamsAndData(queryParams); + this.textSearch.reset(); } resetSortAndFilter(update: boolean = true, preserveTimewindow: boolean = false) { this.textSearchMode = false; this.pageLink.textSearch = null; + this.textSearch.reset('', {emitEvent: false}); if (this.entitiesTableConfig.useTimePageLink && !preserveTimewindow) { this.timewindow = this.entitiesTableConfig.defaultTimewindowInterval; } @@ -611,7 +614,7 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa columnsUpdated(resetData: boolean = false) { this.entityColumns = this.entitiesTableConfig.columns.filter( - (column) => column instanceof EntityTableColumn) + (column) => column instanceof EntityTableColumn || column instanceof EntityLinkTableColumn) .map(column => column as EntityTableColumn>); this.actionColumns = this.entitiesTableConfig.columns.filter( (column) => column instanceof EntityActionTableColumn) @@ -637,6 +640,10 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa } } + cellActionDescriptorsUpdated() { + this.cellActionDescriptors = [...this.entitiesTableConfig.cellActionDescriptors]; + } + headerCellStyle(column: EntityColumn>) { const index = this.entitiesTableConfig.columns.indexOf(column); let res = this.headerCellStyleCache[index]; @@ -664,7 +671,7 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa } cellContent(entity: BaseData, column: EntityColumn>, row: number) { - if (column instanceof EntityTableColumn) { + if (column instanceof EntityTableColumn || column instanceof EntityLinkTableColumn) { const col = this.entitiesTableConfig.columns.indexOf(column); const index = row * this.entitiesTableConfig.columns.length + col; let res = this.cellContentCache[index]; diff --git a/ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.html b/ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.html index e56402627a..2b681b6e5b 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.html +++ b/ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.html @@ -42,7 +42,7 @@
-
diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html index d1cd61f579..3f7da38fa6 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html @@ -81,6 +81,6 @@ matTooltip="{{ (dynamicMode ? 'filter.switch-to-default-value' : 'filter.switch-to-dynamic-value') | translate }}" matTooltipPosition="above" (click)="dynamicMode = !dynamicMode"> - + {{ dynamicMode ? 'mdi:numeric' : 'mdi:variable' }}
diff --git a/ui-ngx/src/app/modules/home/components/filter/key-filter-dialog.component.ts b/ui-ngx/src/app/modules/home/components/filter/key-filter-dialog.component.ts index d8b1a21f6a..b210605d95 100644 --- a/ui-ngx/src/app/modules/home/components/filter/key-filter-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/key-filter-dialog.component.ts @@ -70,7 +70,8 @@ export class KeyFilterDialogComponent extends entityKeyTypes = this.data.telemetryKeysOnly ? [EntityKeyType.ATTRIBUTE, EntityKeyType.TIME_SERIES, EntityKeyType.CONSTANT] : - [EntityKeyType.ENTITY_FIELD, EntityKeyType.ATTRIBUTE, EntityKeyType.TIME_SERIES]; + [EntityKeyType.ENTITY_FIELD, EntityKeyType.ATTRIBUTE, EntityKeyType.CLIENT_ATTRIBUTE, + EntityKeyType.SERVER_ATTRIBUTE, EntityKeyType.SHARED_ATTRIBUTE, EntityKeyType.TIME_SERIES]; entityKeyTypeTranslations = entityKeyTypeTranslationMap; diff --git a/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.html b/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.html index c29ae4ca72..bd6526ccbb 100644 --- a/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.html @@ -34,7 +34,7 @@
-
diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index a6e2cc03dc..e8374f6534 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -51,11 +51,7 @@ import { CustomActionPrettyEditorComponent } from '@home/components/widget/actio import { MobileActionEditorComponent } from '@home/components/widget/action/mobile-action-editor.component'; import { CustomDialogService } from '@home/components/widget/dialog/custom-dialog.service'; import { CustomDialogContainerComponent } from '@home/components/widget/dialog/custom-dialog-container.component'; -import { ImportExportService } from '@home/components/import-export/import-export.service'; -import { ImportDialogComponent } from '@home/components/import-export/import-dialog.component'; import { AddWidgetToDashboardDialogComponent } from '@home/components/attribute/add-widget-to-dashboard-dialog.component'; -import { ImportDialogCsvComponent } from '@home/components/import-export/import-dialog-csv.component'; -import { TableColumnsAssignmentComponent } from '@home/components/import-export/table-columns-assignment.component'; import { EventContentDialogComponent } from '@home/components/event/event-content-dialog.component'; import { SharedHomeComponentsModule } from '@home/components/shared-home-components.module'; import { SelectTargetLayoutDialogComponent } from '@home/components/dashboard/select-target-layout-dialog.component'; @@ -177,6 +173,7 @@ import { } from '@home/components/widget/action/manage-widget-actions-dialog.component'; import { WidgetConfigComponentsModule } from '@home/components/widget/config/widget-config-components.module'; import { BasicWidgetConfigModule } from '@home/components/widget/config/basic/basic-widget-config.module'; +import { DeleteTimeseriesPanelComponent } from '@home/components/attribute/delete-timeseries-panel.component'; @NgModule({ declarations: @@ -205,6 +202,7 @@ import { BasicWidgetConfigModule } from '@home/components/widget/config/basic/ba AttributeTableComponent, AddAttributeDialogComponent, EditAttributeValuePanelComponent, + DeleteTimeseriesPanelComponent, AliasesEntitySelectPanelComponent, AliasesEntitySelectComponent, AliasesEntityAutocompleteComponent, @@ -224,12 +222,9 @@ import { BasicWidgetConfigModule } from '@home/components/widget/config/basic/ba CustomActionPrettyEditorComponent, MobileActionEditorComponent, CustomDialogContainerComponent, - ImportDialogComponent, - ImportDialogCsvComponent, SelectTargetLayoutDialogComponent, SelectTargetStateDialogComponent, AddWidgetToDashboardDialogComponent, - TableColumnsAssignmentComponent, BooleanFilterPredicateComponent, StringFilterPredicateComponent, NumericFilterPredicateComponent, @@ -368,9 +363,6 @@ import { BasicWidgetConfigModule } from '@home/components/widget/config/basic/ba CustomActionPrettyEditorComponent, MobileActionEditorComponent, CustomDialogContainerComponent, - ImportDialogComponent, - ImportDialogCsvComponent, - TableColumnsAssignmentComponent, SelectTargetLayoutDialogComponent, SelectTargetStateDialogComponent, BooleanFilterPredicateComponent, @@ -461,7 +453,6 @@ import { BasicWidgetConfigModule } from '@home/components/widget/config/basic/ba providers: [ WidgetComponentService, CustomDialogService, - ImportExportService, {provide: EMBED_DASHBOARD_DIALOG_TOKEN, useValue: EmbedDashboardDialogComponent}, {provide: COMPLEX_FILTER_PREDICATE_DIALOG_COMPONENT_TOKEN, useValue: ComplexFilterPredicateDialogComponent}, {provide: DASHBOARD_PAGE_COMPONENT_TOKEN, useValue: DashboardPageComponent}, diff --git a/ui-ngx/src/app/modules/home/components/import-export/import-dialog-csv.component.ts b/ui-ngx/src/app/modules/home/components/import-export/import-dialog-csv.component.ts deleted file mode 100644 index 9782f18ae6..0000000000 --- a/ui-ngx/src/app/modules/home/components/import-export/import-dialog-csv.component.ts +++ /dev/null @@ -1,303 +0,0 @@ -/// -/// Copyright © 2016-2023 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -import { AfterViewInit, Component, ElementRef, Inject, OnDestroy, Renderer2, ViewChild } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; -import { Router } from '@angular/router'; -import { DialogComponent } from '@app/shared/components/dialog.component'; -import { EntityType } from '@shared/models/entity-type.models'; -import { TranslateService } from '@ngx-translate/core'; -import { ActionNotificationShow } from '@core/notification/notification.actions'; -import { MatStepper } from '@angular/material/stepper'; -import { - BulkImportRequest, - BulkImportResult, - ColumnMapping, - convertCSVToJson, - CsvColumnParam, - CSVDelimiter, - CsvToJsonConfig, - CsvToJsonResult, - ImportEntityColumnType -} from '@home/components/import-export/import-export.models'; -import { ImportExportService } from '@home/components/import-export/import-export.service'; -import { TableColumnsAssignmentComponent } from '@home/components/import-export/table-columns-assignment.component'; -import { Ace } from 'ace-builds'; -import { getAce } from '@shared/models/ace/ace.models'; - -export interface ImportDialogCsvData { - entityType: EntityType; - importTitle: string; - importFileLabel: string; -} - -@Component({ - selector: 'tb-import-csv-dialog', - templateUrl: './import-dialog-csv.component.html', - providers: [], - styleUrls: ['./import-dialog-csv.component.scss'] -}) -export class ImportDialogCsvComponent extends DialogComponent - implements AfterViewInit, OnDestroy { - - @ViewChild('importStepper', {static: true}) importStepper: MatStepper; - - @ViewChild('columnsAssignmentComponent', {static: true}) - columnsAssignmentComponent: TableColumnsAssignmentComponent; - - @ViewChild('failureDetailsEditor') - failureDetailsEditorElmRef: ElementRef; - - entityType: EntityType; - importTitle: string; - importFileLabel: string; - - delimiters: { key: CSVDelimiter, value: string }[] = [{ - key: ',', - value: ',' - }, { - key: ';', - value: ';' - }, { - key: '|', - value: '|' - }, { - key: '\t', - value: 'Tab' - }]; - - selectedIndex = 0; - - selectFileFormGroup: UntypedFormGroup; - importParametersFormGroup: UntypedFormGroup; - columnTypesFormGroup: UntypedFormGroup; - - isImportData = false; - statistical: BulkImportResult; - - aceEditor: Ace.Editor; - - private allowAssignColumn: ImportEntityColumnType[]; - private initEditorComponent = false; - private parseData: CsvToJsonResult; - - constructor(protected store: Store, - protected router: Router, - @Inject(MAT_DIALOG_DATA) public data: ImportDialogCsvData, - public dialogRef: MatDialogRef, - public translate: TranslateService, - private importExport: ImportExportService, - private fb: UntypedFormBuilder, - private renderer: Renderer2) { - super(store, router, dialogRef); - this.entityType = data.entityType; - this.importTitle = data.importTitle; - this.importFileLabel = data.importFileLabel; - - this.selectFileFormGroup = this.fb.group( - { - importData: [null, [Validators.required]] - } - ); - this.importParametersFormGroup = this.fb.group({ - delim: [',', [Validators.required]], - isHeader: [true, []], - isUpdate: [true, []], - }); - this.columnTypesFormGroup = this.fb.group({ - columnsParam: [[], []] - }); - } - - ngAfterViewInit() { - let columns = this.columnsAssignmentComponent.columnTypes; - if (this.entityType === EntityType.DEVICE) { - columns = columns.concat(this.columnsAssignmentComponent.columnDeviceCredentials); - } - this.allowAssignColumn = columns.map(column => column.value); - } - - ngOnDestroy(): void { - if (this.aceEditor) { - this.aceEditor.destroy(); - } - super.ngOnDestroy(); - } - - cancel(): void { - this.dialogRef.close(false); - } - - previousStep() { - this.importStepper.previous(); - } - - nextStep(step: number) { - switch (step) { - case 2: - this.importStepper.next(); - break; - case 3: - const importData: string = this.selectFileFormGroup.get('importData').value; - const parseData = this.parseCSV(importData); - if (parseData === -1) { - this.importStepper.previous(); - this.importStepper.selected.reset(); - } else { - this.parseData = parseData as CsvToJsonResult; - const columnsParam = this.createColumnsData(); - this.columnTypesFormGroup.patchValue({columnsParam}, {emitEvent: true}); - this.importStepper.next(); - } - break; - case 4: - this.importStepper.next(); - this.isImportData = true; - this.addEntities(); - break; - case 6: - this.dialogRef.close(true); - break; - } - } - - private parseCSV(importData: string): CsvToJsonResult | number { - const config: CsvToJsonConfig = { - delim: this.importParametersFormGroup.get('delim').value, - header: this.importParametersFormGroup.get('isHeader').value - }; - return convertCSVToJson(importData, config, - (messageId, params) => { - this.store.dispatch(new ActionNotificationShow( - { - message: this.translate.instant(messageId, params), - type: 'error' - })); - } - ); - } - - private createColumnsData(): CsvColumnParam[] { - const columnsParam: CsvColumnParam[] = []; - const isHeader: boolean = this.importParametersFormGroup.get('isHeader').value; - for (let i = 0; i < this.parseData.headers.length; i++) { - let columnParam: CsvColumnParam; - let findEntityColumnType: ImportEntityColumnType; - if (isHeader) { - const headerColumnName = this.parseData.headers[i].toUpperCase(); - findEntityColumnType = this.allowAssignColumn.find(column => column === headerColumnName); - } - if (isHeader && findEntityColumnType) { - columnParam = { - type: findEntityColumnType, - key: this.parseData.headers[i].toLowerCase(), - sampleData: this.parseData.rows[0][i] - }; - } else { - columnParam = { - type: ImportEntityColumnType.serverAttribute, - key: isHeader ? this.parseData.headers[i] : '', - sampleData: this.parseData.rows[0][i] - }; - } - columnsParam.push(columnParam); - } - return columnsParam; - } - - - private addEntities() { - const entitiesData: BulkImportRequest = { - file: this.selectFileFormGroup.get('importData').value, - mapping: { - columns: this.processingColumnsParams(), - delimiter: this.importParametersFormGroup.get('delim').value, - header: this.importParametersFormGroup.get('isHeader').value, - update: this.importParametersFormGroup.get('isUpdate').value - } - }; - this.importExport.bulkImportEntities(entitiesData, this.entityType, {ignoreErrors: true}).subscribe( - (result) => { - this.statistical = result; - this.isImportData = false; - this.importStepper.next(); - } - ); - } - - private processingColumnsParams(): Array { - const parameterColumns: CsvColumnParam[] = this.columnTypesFormGroup.get('columnsParam').value; - const allowKeyForTypeColumns: ImportEntityColumnType[] = [ - ImportEntityColumnType.serverAttribute, - ImportEntityColumnType.timeseries, - ImportEntityColumnType.sharedAttribute - ]; - return parameterColumns.map(column => ({ - type: column.type, - key: allowKeyForTypeColumns.some(type => type === column.type) ? column.key : undefined - })); - } - - initEditor() { - if (!this.initEditorComponent) { - this.createEditor(this.failureDetailsEditorElmRef, this.statistical.errorsList); - } - } - - private createEditor(editorElementRef: ElementRef, contents: string[]): void { - const editorElement = editorElementRef.nativeElement; - let editorOptions: Partial = { - mode: 'ace/mode/java', - theme: 'ace/theme/github', - showGutter: false, - showPrintMargin: false, - readOnly: true - }; - - const advancedOptions = { - enableSnippets: false, - enableBasicAutocompletion: false, - enableLiveAutocompletion: false - }; - - editorOptions = {...editorOptions, ...advancedOptions}; - const content = contents.map(error => error.replace('\n', '')).join('\n'); - getAce().subscribe( - (ace) => { - this.aceEditor = ace.edit(editorElement, editorOptions); - this.aceEditor.session.setUseWrapMode(false); - this.aceEditor.setValue(content, -1); - this.updateEditorSize(editorElement, content, this.aceEditor); - } - ); - } - - private updateEditorSize(editorElement: any, content: string, editor: Ace.Editor) { - let newHeight = 200; - if (content && content.length > 0) { - const lines = content.split('\n'); - newHeight = 16 * lines.length + 24; - } - const minHeight = Math.min(200, newHeight); - this.renderer.setStyle(editorElement, 'minHeight', minHeight.toString() + 'px'); - this.renderer.setStyle(editorElement, 'height', newHeight.toString() + 'px'); - editor.resize(); - } - -} diff --git a/ui-ngx/src/app/modules/home/components/import-export/import-export.models.ts b/ui-ngx/src/app/modules/home/components/import-export/import-export.models.ts deleted file mode 100644 index fddfa80d91..0000000000 --- a/ui-ngx/src/app/modules/home/components/import-export/import-export.models.ts +++ /dev/null @@ -1,226 +0,0 @@ -/// -/// Copyright © 2016-2023 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -import { Widget, WidgetTypeDetails } from '@app/shared/models/widget.models'; -import { DashboardLayoutId } from '@shared/models/dashboard.models'; -import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; - -export interface ImportWidgetResult { - widget: Widget; - layoutId: DashboardLayoutId; -} - -export interface WidgetsBundleItem { - widgetsBundle: WidgetsBundle; - widgetTypes: WidgetTypeDetails[]; -} - -export interface CsvToJsonConfig { - delim?: string; - header?: boolean; -} - -export interface CsvToJsonResult { - headers?: string[]; - rows?: any[][]; -} - -export type CSVDelimiter = ',' | ';' | '|' | '\t'; - -export enum ImportEntityColumnType { - name = 'NAME', - type = 'TYPE', - label = 'LABEL', - clientAttribute = 'CLIENT_ATTRIBUTE', - sharedAttribute = 'SHARED_ATTRIBUTE', - serverAttribute = 'SERVER_ATTRIBUTE', - timeseries = 'TIMESERIES', - accessToken = 'ACCESS_TOKEN', - x509 = 'X509', - mqttClientId = 'MQTT_CLIENT_ID', - mqttUserName = 'MQTT_USER_NAME', - mqttPassword = 'MQTT_PASSWORD', - lwm2mClientEndpoint = 'LWM2M_CLIENT_ENDPOINT', - lwm2mClientSecurityConfigMode = 'LWM2M_CLIENT_SECURITY_CONFIG_MODE', - lwm2mClientIdentity = 'LWM2M_CLIENT_IDENTITY', - lwm2mClientKey = 'LWM2M_CLIENT_KEY', - lwm2mClientCert = 'LWM2M_CLIENT_CERT', - lwm2mBootstrapServerSecurityMode = 'LWM2M_BOOTSTRAP_SERVER_SECURITY_MODE', - lwm2mBootstrapServerClientPublicKeyOrId = 'LWM2M_BOOTSTRAP_SERVER_PUBLIC_KEY_OR_ID', - lwm2mBootstrapServerClientSecretKey = 'LWM2M_BOOTSTRAP_SERVER_SECRET_KEY', - lwm2mServerSecurityMode = 'LWM2M_SERVER_SECURITY_MODE', - lwm2mServerClientPublicKeyOrId = 'LWM2M_SERVER_CLIENT_PUBLIC_KEY_OR_ID', - lwm2mServerClientSecretKey = 'LWM2M_SERVER_CLIENT_SECRET_KEY', - isGateway = 'IS_GATEWAY', - description = 'DESCRIPTION', - routingKey = 'ROUTING_KEY', - secret = 'SECRET' -} - -export const importEntityColumnTypeTranslations = new Map( - [ - [ImportEntityColumnType.name, 'import.column-type.name'], - [ImportEntityColumnType.type, 'import.column-type.type'], - [ImportEntityColumnType.label, 'import.column-type.label'], - [ImportEntityColumnType.clientAttribute, 'import.column-type.client-attribute'], - [ImportEntityColumnType.sharedAttribute, 'import.column-type.shared-attribute'], - [ImportEntityColumnType.serverAttribute, 'import.column-type.server-attribute'], - [ImportEntityColumnType.timeseries, 'import.column-type.timeseries'], - [ImportEntityColumnType.accessToken, 'import.column-type.access-token'], - [ImportEntityColumnType.x509, 'import.column-type.x509'], - [ImportEntityColumnType.mqttClientId, 'import.column-type.mqtt.client-id'], - [ImportEntityColumnType.mqttUserName, 'import.column-type.mqtt.user-name'], - [ImportEntityColumnType.mqttPassword, 'import.column-type.mqtt.password'], - [ImportEntityColumnType.lwm2mClientEndpoint, 'import.column-type.lwm2m.client-endpoint'], - [ImportEntityColumnType.lwm2mClientSecurityConfigMode, 'import.column-type.lwm2m.security-config-mode'], - [ImportEntityColumnType.lwm2mClientIdentity, 'import.column-type.lwm2m.client-identity'], - [ImportEntityColumnType.lwm2mClientKey, 'import.column-type.lwm2m.client-key'], - [ImportEntityColumnType.lwm2mClientCert, 'import.column-type.lwm2m.client-cert'], - [ImportEntityColumnType.lwm2mBootstrapServerSecurityMode, 'import.column-type.lwm2m.bootstrap-server-security-mode'], - [ImportEntityColumnType.lwm2mBootstrapServerClientPublicKeyOrId, 'import.column-type.lwm2m.bootstrap-server-public-key-id'], - [ImportEntityColumnType.lwm2mBootstrapServerClientSecretKey, 'import.column-type.lwm2m.bootstrap-server-secret-key'], - [ImportEntityColumnType.lwm2mServerSecurityMode, 'import.column-type.lwm2m.lwm2m-server-security-mode'], - [ImportEntityColumnType.lwm2mServerClientPublicKeyOrId, 'import.column-type.lwm2m.lwm2m-server-public-key-id'], - [ImportEntityColumnType.lwm2mServerClientSecretKey, 'import.column-type.lwm2m.lwm2m-server-secret-key'], - [ImportEntityColumnType.isGateway, 'import.column-type.isgateway'], - [ImportEntityColumnType.description, 'import.column-type.description'], - [ImportEntityColumnType.routingKey, 'import.column-type.routing-key'], - [ImportEntityColumnType.secret, 'import.column-type.secret'] - ] -); - -export interface CsvColumnParam { - type: ImportEntityColumnType; - key: string; - sampleData: any; -} - -export interface ColumnMapping { - type: ImportEntityColumnType; - key?: string; -} - -export interface BulkImportRequest { - file: string; - mapping: { - columns: Array; - delimiter: CSVDelimiter; - header: boolean; - update: boolean; - }; -} - -export interface BulkImportResult { - created: number; - updated: number; - errors: number; - errorsList: Array; -} - -export interface FileType { - mimeType: string; - extension: string; -} - -export const TEXT_TYPE: FileType = { - mimeType: 'text/plain', - extension: 'txt' -}; - -export const JSON_TYPE: FileType = { - mimeType: 'text/json', - extension: 'json' -}; - -export const ZIP_TYPE: FileType = { - mimeType: 'application/zip', - extension: 'zip' -}; - -export const CSV_TYPE: FileType = { - mimeType: 'attachament/csv', - extension: 'csv' -}; - -export function convertCSVToJson(csvdata: string, config: CsvToJsonConfig, - onError: (messageId: string, params?: any) => void): CsvToJsonResult | number { - config = config || {}; - const delim = config.delim || ','; - const header = config.header || false; - const result: CsvToJsonResult = {}; - const csvlines = csvdata.split(/[\r\n]+/); - const csvheaders = splitCSV(csvlines[0], delim); - if (csvheaders.length < 2) { - onError('import.import-csv-number-columns-error'); - return -1; - } - const csvrows = header ? csvlines.slice(1, csvlines.length) : csvlines; - result.headers = csvheaders; - result.rows = []; - for (const row of csvrows) { - if (row.length === 0) { - break; - } - const rowitems: any[] = splitCSV(row, delim); - if (rowitems.length !== result.headers.length) { - onError('import.import-csv-invalid-format-error', {line: (header ? result.rows.length + 2 : result.rows.length + 1)}); - return -1; - } - for (let i = 0; i < rowitems.length; i++) { - rowitems[i] = convertStringToJSType(rowitems[i]); - } - result.rows.push(rowitems); - } - return result; -} - -function splitCSV(str: string, sep: string): string[] { - let foo: string[]; - let x: number; - let tl: string; - for (foo = str.split(sep = sep || ','), x = foo.length - 1, tl; x >= 0; x--) { - if (foo[x].replace(/"\s+$/, '"').charAt(foo[x].length - 1) === '"') { - if ((tl = foo[x].replace(/^\s+"/, '"')).length > 1 && tl.charAt(0) === '"') { - foo[x] = foo[x].replace(/^\s*"|"\s*$/g, '').replace(/""/g, '"'); - } else if (x) { - foo.splice(x - 1, 2, [foo[x - 1], foo[x]].join(sep)); - } else { - foo = foo.shift().split(sep).concat(foo); - } - } else { - foo[x].replace(/""/g, '"'); - } - } - return foo; -} - -function isNumeric(str: any): boolean { - str = str.replace(',', '.'); - return (str - parseFloat(str) + 1) >= 0 && Number(str).toString() === str; -} - -function convertStringToJSType(str: string): any { - if (isNumeric(str.replace(',', '.'))) { - return parseFloat(str.replace(',', '.')); - } - if (str.search(/^(true|false)$/im) === 0) { - return str.toLowerCase() === 'true'; - } - if (str === '') { - return null; - } - return str; -} diff --git a/ui-ngx/src/app/modules/home/components/import-export/import-export.service.ts b/ui-ngx/src/app/modules/home/components/import-export/import-export.service.ts deleted file mode 100644 index a6e7e7f89e..0000000000 --- a/ui-ngx/src/app/modules/home/components/import-export/import-export.service.ts +++ /dev/null @@ -1,1013 +0,0 @@ -/// -/// Copyright © 2016-2023 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -import { Inject, Injectable } from '@angular/core'; -import { DashboardService } from '@core/http/dashboard.service'; -import { TranslateService } from '@ngx-translate/core'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { ActionNotificationShow } from '@core/notification/notification.actions'; -import { Dashboard, DashboardLayoutId } from '@shared/models/dashboard.models'; -import { deepClone, isDefined, isObject, isString, isUndefined } from '@core/utils'; -import { WINDOW } from '@core/services/window.service'; -import { DOCUMENT } from '@angular/common'; -import { - AliasesInfo, - AliasFilterType, - EntityAlias, - EntityAliases, - EntityAliasFilter, - EntityAliasInfo -} from '@shared/models/alias.models'; -import { MatDialog } from '@angular/material/dialog'; -import { ImportDialogComponent, ImportDialogData } from '@home/components/import-export/import-dialog.component'; -import { forkJoin, Observable, of } from 'rxjs'; -import { catchError, map, mergeMap, tap } from 'rxjs/operators'; -import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; -import { EntityService } from '@core/http/entity.service'; -import { Widget, WidgetSize, WidgetType, WidgetTypeDetails } from '@shared/models/widget.models'; -import { - EntityAliasesDialogComponent, - EntityAliasesDialogData -} from '@home/components/alias/entity-aliases-dialog.component'; -import { ItemBufferService, WidgetItem } from '@core/services/item-buffer.service'; -import { - BulkImportRequest, - BulkImportResult, - CSV_TYPE, - FileType, - ImportWidgetResult, - JSON_TYPE, - TEXT_TYPE, - WidgetsBundleItem, - ZIP_TYPE -} from './import-export.models'; -import { EntityType } from '@shared/models/entity-type.models'; -import { UtilsService } from '@core/services/utils.service'; -import { WidgetService } from '@core/http/widget.service'; -import { NULL_UUID } from '@shared/models/id/has-uuid'; -import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; -import { ImportEntitiesResultInfo, ImportEntityData } from '@shared/models/entity.models'; -import { RequestConfig } from '@core/http/http-utils'; -import { RuleChain, RuleChainImport, RuleChainMetaData, RuleChainType } from '@shared/models/rule-chain.models'; -import { RuleChainService } from '@core/http/rule-chain.service'; -import { FiltersInfo } from '@shared/models/query/query.models'; -import { DeviceProfileService } from '@core/http/device-profile.service'; -import { DeviceProfile } from '@shared/models/device.models'; -import { TenantProfile } from '@shared/models/tenant.model'; -import { TenantProfileService } from '@core/http/tenant-profile.service'; -import { DeviceService } from '@core/http/device.service'; -import { AssetService } from '@core/http/asset.service'; -import { EdgeService } from '@core/http/edge.service'; -import { RuleNode } from '@shared/models/rule-node.models'; -import { AssetProfileService } from '@core/http/asset-profile.service'; -import { AssetProfile } from '@shared/models/asset.models'; - -// @dynamic -@Injectable() -export class ImportExportService { - - constructor(@Inject(WINDOW) private window: Window, - @Inject(DOCUMENT) private document: Document, - private store: Store, - private translate: TranslateService, - private dashboardService: DashboardService, - private dashboardUtils: DashboardUtilsService, - private widgetService: WidgetService, - private deviceProfileService: DeviceProfileService, - private assetProfileService: AssetProfileService, - private tenantProfileService: TenantProfileService, - private entityService: EntityService, - private ruleChainService: RuleChainService, - private deviceService: DeviceService, - private assetService: AssetService, - private edgeService: EdgeService, - private utils: UtilsService, - private itembuffer: ItemBufferService, - private dialog: MatDialog) { - - } - - public exportDashboard(dashboardId: string) { - this.dashboardService.getDashboard(dashboardId).subscribe( - (dashboard) => { - let name = dashboard.title; - name = name.toLowerCase().replace(/\W/g, '_'); - this.exportToPc(this.prepareDashboardExport(dashboard), name); - }, - (e) => { - this.handleExportError(e, 'dashboard.export-failed-error'); - } - ); - } - - public importDashboard(): Observable { - return this.openImportDialog('dashboard.import', 'dashboard.dashboard-file').pipe( - mergeMap((dashboard: Dashboard) => { - if (!this.validateImportedDashboard(dashboard)) { - this.store.dispatch(new ActionNotificationShow( - {message: this.translate.instant('dashboard.invalid-dashboard-file-error'), - type: 'error'})); - throw new Error('Invalid dashboard file'); - } else { - dashboard = this.dashboardUtils.validateAndUpdateDashboard(dashboard); - let aliasIds = null; - const entityAliases = dashboard.configuration.entityAliases; - if (entityAliases) { - aliasIds = Object.keys(entityAliases); - } - if (aliasIds && aliasIds.length > 0) { - return this.processEntityAliases(entityAliases, aliasIds).pipe( - mergeMap((missingEntityAliases) => { - if (Object.keys(missingEntityAliases).length > 0) { - return this.editMissingAliases(this.dashboardUtils.getWidgetsArray(dashboard), - false, 'dashboard.dashboard-import-missing-aliases-title', - missingEntityAliases).pipe( - mergeMap((updatedEntityAliases) => { - for (const aliasId of Object.keys(updatedEntityAliases)) { - entityAliases[aliasId] = updatedEntityAliases[aliasId]; - } - return this.saveImportedDashboard(dashboard); - }) - ); - } else { - return this.saveImportedDashboard(dashboard); - } - } - )); - } else { - return this.saveImportedDashboard(dashboard); - } - } - }), - catchError((err) => { - return of(null); - }) - ); - } - - public exportWidget(dashboard: Dashboard, sourceState: string, sourceLayout: DashboardLayoutId, widget: Widget) { - const widgetItem = this.itembuffer.prepareWidgetItem(dashboard, sourceState, sourceLayout, widget); - let name = widgetItem.widget.config.title; - name = name.toLowerCase().replace(/\W/g, '_'); - this.exportToPc(this.prepareExport(widgetItem), name); - } - - public importWidget(dashboard: Dashboard, targetState: string, - targetLayoutFunction: () => Observable, - onAliasesUpdateFunction: () => void, - onFiltersUpdateFunction: () => void): Observable { - return this.openImportDialog('dashboard.import-widget', 'dashboard.widget-file').pipe( - mergeMap((widgetItem: WidgetItem) => { - if (!this.validateImportedWidget(widgetItem)) { - this.store.dispatch(new ActionNotificationShow( - {message: this.translate.instant('dashboard.invalid-widget-file-error'), - type: 'error'})); - throw new Error('Invalid widget file'); - } else { - let widget = widgetItem.widget; - widget = this.dashboardUtils.validateAndUpdateWidget(widget); - const aliasesInfo = this.prepareAliasesInfo(widgetItem.aliasesInfo); - const filtersInfo: FiltersInfo = widgetItem.filtersInfo || { - datasourceFilters: {} - }; - const originalColumns = widgetItem.originalColumns; - const originalSize = widgetItem.originalSize; - - const datasourceAliases = aliasesInfo.datasourceAliases; - const targetDeviceAliases = aliasesInfo.targetDeviceAliases; - if (datasourceAliases || targetDeviceAliases) { - const entityAliases: EntityAliases = {}; - const datasourceAliasesMap: {[aliasId: string]: number} = {}; - const targetDeviceAliasesMap: {[aliasId: string]: number} = {}; - let aliasId: string; - let datasourceIndex: number; - if (datasourceAliases) { - for (const strIndex of Object.keys(datasourceAliases)) { - datasourceIndex = Number(strIndex); - aliasId = this.utils.guid(); - datasourceAliasesMap[aliasId] = datasourceIndex; - entityAliases[aliasId] = {id: aliasId, ...datasourceAliases[datasourceIndex]}; - } - } - if (targetDeviceAliases) { - for (const strIndex of Object.keys(targetDeviceAliases)) { - datasourceIndex = Number(strIndex); - aliasId = this.utils.guid(); - targetDeviceAliasesMap[aliasId] = datasourceIndex; - entityAliases[aliasId] = {id: aliasId, ...targetDeviceAliases[datasourceIndex]}; - } - } - const aliasIds = Object.keys(entityAliases); - if (aliasIds.length > 0) { - return this.processEntityAliases(entityAliases, aliasIds).pipe( - mergeMap((missingEntityAliases) => { - if (Object.keys(missingEntityAliases).length > 0) { - return this.editMissingAliases([widget], - false, 'dashboard.widget-import-missing-aliases-title', - missingEntityAliases).pipe( - mergeMap((updatedEntityAliases) => { - for (const id of Object.keys(updatedEntityAliases)) { - const entityAlias = updatedEntityAliases[id]; - let index; - if (isDefined(datasourceAliasesMap[id])) { - index = datasourceAliasesMap[id]; - datasourceAliases[index] = entityAlias; - } else if (isDefined(targetDeviceAliasesMap[id])) { - index = targetDeviceAliasesMap[id]; - targetDeviceAliases[index] = entityAlias; - } - } - return this.addImportedWidget(dashboard, targetState, targetLayoutFunction, widget, - aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize); - } - )); - } else { - return this.addImportedWidget(dashboard, targetState, targetLayoutFunction, widget, - aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize); - } - } - ) - ); - } else { - return this.addImportedWidget(dashboard, targetState, targetLayoutFunction, widget, - aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize); - } - } else { - return this.addImportedWidget(dashboard, targetState, targetLayoutFunction, widget, - aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize); - } - } - }), - catchError((err) => { - return of(null); - }) - ); - } - - public exportWidgetType(widgetTypeId: string) { - this.widgetService.getWidgetTypeById(widgetTypeId).subscribe( - (widgetTypeDetails) => { - if (isDefined(widgetTypeDetails.bundleAlias)) { - delete widgetTypeDetails.bundleAlias; - } - let name = widgetTypeDetails.name; - name = name.toLowerCase().replace(/\W/g, '_'); - this.exportToPc(this.prepareExport(widgetTypeDetails), name); - }, - (e) => { - this.handleExportError(e, 'widget-type.export-failed-error'); - } - ); - } - - public importWidgetType(bundleAlias: string): Observable { - return this.openImportDialog('widget-type.import', 'widget-type.widget-type-file').pipe( - mergeMap((widgetTypeDetails: WidgetTypeDetails) => { - if (!this.validateImportedWidgetTypeDetails(widgetTypeDetails)) { - this.store.dispatch(new ActionNotificationShow( - {message: this.translate.instant('widget-type.invalid-widget-type-file-error'), - type: 'error'})); - throw new Error('Invalid widget type file'); - } else { - widgetTypeDetails.bundleAlias = bundleAlias; - return this.widgetService.saveImportedWidgetTypeDetails(widgetTypeDetails); - } - }), - catchError((err) => { - return of(null); - }) - ); - } - - public exportWidgetsBundle(widgetsBundleId: string) { - this.widgetService.getWidgetsBundle(widgetsBundleId).subscribe( - (widgetsBundle) => { - const bundleAlias = widgetsBundle.alias; - const isSystem = widgetsBundle.tenantId.id === NULL_UUID; - this.widgetService.getBundleWidgetTypesDetails(bundleAlias, isSystem).subscribe( - (widgetTypesDetails) => { - widgetTypesDetails = widgetTypesDetails.sort((a, b) => a.createdTime - b.createdTime); - const widgetsBundleItem: WidgetsBundleItem = { - widgetsBundle: this.prepareExport(widgetsBundle), - widgetTypes: [] - }; - for (const widgetTypeDetails of widgetTypesDetails) { - if (isDefined(widgetTypeDetails.bundleAlias)) { - delete widgetTypeDetails.bundleAlias; - } - widgetsBundleItem.widgetTypes.push(this.prepareExport(widgetTypeDetails)); - } - let name = widgetsBundle.title; - name = name.toLowerCase().replace(/\W/g, '_'); - this.exportToPc(widgetsBundleItem, name); - }, - (e) => { - this.handleExportError(e, 'widgets-bundle.export-failed-error'); - } - ); - }, - (e) => { - this.handleExportError(e, 'widgets-bundle.export-failed-error'); - } - ); - } - - public importWidgetsBundle(): Observable { - return this.openImportDialog('widgets-bundle.import', 'widgets-bundle.widgets-bundle-file').pipe( - mergeMap((widgetsBundleItem: WidgetsBundleItem) => { - if (!this.validateImportedWidgetsBundle(widgetsBundleItem)) { - this.store.dispatch(new ActionNotificationShow( - {message: this.translate.instant('widgets-bundle.invalid-widgets-bundle-file-error'), - type: 'error'})); - throw new Error('Invalid widgets bundle file'); - } else { - const widgetsBundle = widgetsBundleItem.widgetsBundle; - return this.widgetService.saveWidgetsBundle(widgetsBundle).pipe( - mergeMap((savedWidgetsBundle) => { - const bundleAlias = savedWidgetsBundle.alias; - const widgetTypesDetails = widgetsBundleItem.widgetTypes; - if (widgetTypesDetails.length) { - const saveWidgetTypesObservables: Array> = []; - for (const widgetTypeDetails of widgetTypesDetails) { - widgetTypeDetails.bundleAlias = bundleAlias; - saveWidgetTypesObservables.push(this.widgetService.saveImportedWidgetTypeDetails(widgetTypeDetails)); - } - return forkJoin(saveWidgetTypesObservables).pipe( - map(() => savedWidgetsBundle) - ); - } else { - return of(savedWidgetsBundle); - } - } - )); - } - }), - catchError((err) => { - return of(null); - }) - ); - } - - public bulkImportEntities(entitiesData: BulkImportRequest, entityType: EntityType, config?: RequestConfig): Observable { - switch (entityType) { - case EntityType.DEVICE: - return this.deviceService.bulkImportDevices(entitiesData, config); - case EntityType.ASSET: - return this.assetService.bulkImportAssets(entitiesData, config); - case EntityType.EDGE: - return this.edgeService.bulkImportEdges(entitiesData, config); - } - } - - public importEntities(entitiesData: ImportEntityData[], entityType: EntityType, updateData: boolean, - importEntityCompleted?: () => void, config?: RequestConfig): Observable { - let partSize = 100; - partSize = entitiesData.length > partSize ? partSize : entitiesData.length; - - let statisticalInfo: ImportEntitiesResultInfo = {}; - const importEntitiesObservables: Observable[] = []; - for (let i = 0; i < partSize; i++) { - let saveEntityPromise: Observable; - saveEntityPromise = this.entityService.saveEntityParameters(entityType, entitiesData[i], updateData, config); - const importEntityPromise = saveEntityPromise.pipe( - tap((res) => { - if (importEntityCompleted) { - importEntityCompleted(); - } - }) - ); - importEntitiesObservables.push(importEntityPromise); - } - return forkJoin(importEntitiesObservables).pipe( - mergeMap((responses) => { - for (const response of responses) { - statisticalInfo = this.sumObject(statisticalInfo, response); - } - entitiesData.splice(0, partSize); - if (entitiesData.length) { - return this.importEntities(entitiesData, entityType, updateData, importEntityCompleted, config).pipe( - map((response) => { - return this.sumObject(statisticalInfo, response) as ImportEntitiesResultInfo; - }) - ); - } else { - return of(statisticalInfo); - } - }) - ); - } - - public exportRuleChain(ruleChainId: string) { - this.ruleChainService.getRuleChain(ruleChainId).pipe( - mergeMap(ruleChain => { - return this.ruleChainService.getRuleChainMetadata(ruleChainId).pipe( - map((ruleChainMetaData) => { - const ruleChainExport: RuleChainImport = { - ruleChain: this.prepareRuleChain(ruleChain), - metadata: this.prepareRuleChainMetaData(ruleChainMetaData) - }; - return ruleChainExport; - }) - ); - }) - ).subscribe((ruleChainExport) => { - let name = ruleChainExport.ruleChain.name; - name = name.toLowerCase().replace(/\W/g, '_'); - this.exportToPc(ruleChainExport, name); - }, - (e) => { - this.handleExportError(e, 'rulechain.export-failed-error'); - } - ); - } - - public importRuleChain(expectedRuleChainType: RuleChainType): Observable { - return this.openImportDialog('rulechain.import', 'rulechain.rulechain-file').pipe( - mergeMap((ruleChainImport: RuleChainImport) => { - if (!this.validateImportedRuleChain(ruleChainImport)) { - this.store.dispatch(new ActionNotificationShow( - {message: this.translate.instant('rulechain.invalid-rulechain-file-error'), - type: 'error'})); - throw new Error('Invalid rule chain file'); - } else if (ruleChainImport.ruleChain.type !== expectedRuleChainType) { - this.store.dispatch(new ActionNotificationShow( - {message: this.translate.instant('rulechain.invalid-rulechain-type-error', {expectedRuleChainType}), - type: 'error'})); - throw new Error('Invalid rule chain type'); - } else { - return this.processOldRuleChainConnections(ruleChainImport); - } - }), - catchError((err) => { - return of(null); - }) - ); - } - - private processOldRuleChainConnections(ruleChainImport: RuleChainImport): Observable { - const metadata = ruleChainImport.metadata; - if ((metadata as any).ruleChainConnections) { - const ruleChainNameResolveObservables: Observable[] = []; - for (const ruleChainConnection of (metadata as any).ruleChainConnections) { - if (ruleChainConnection.targetRuleChainId && ruleChainConnection.targetRuleChainId.id) { - const ruleChainNode: RuleNode = { - name: '', - debugMode: false, - singletonMode: false, - type: 'org.thingsboard.rule.engine.flow.TbRuleChainInputNode', - configuration: { - ruleChainId: ruleChainConnection.targetRuleChainId.id - }, - additionalInfo: ruleChainConnection.additionalInfo - }; - ruleChainNameResolveObservables.push(this.ruleChainService.getRuleChain(ruleChainNode.configuration.ruleChainId, - {ignoreErrors: true, ignoreLoading: true}).pipe( - catchError(err => { - return of({name: 'Rule Chain Input'} as RuleChain); - }), - map((ruleChain => { - ruleChainNode.name = ruleChain.name; - return null; - }) - ) - )); - const toIndex = metadata.nodes.length; - metadata.nodes.push(ruleChainNode); - metadata.connections.push({ - toIndex, - fromIndex: ruleChainConnection.fromIndex, - type: ruleChainConnection.type - }); - } - } - if (ruleChainNameResolveObservables.length) { - return forkJoin(ruleChainNameResolveObservables).pipe( - map(() => ruleChainImport) - ); - } else { - return of(ruleChainImport); - } - } else { - return of(ruleChainImport); - } - } - - public exportDeviceProfile(deviceProfileId: string) { - this.deviceProfileService.getDeviceProfile(deviceProfileId).subscribe( - (deviceProfile) => { - let name = deviceProfile.name; - name = name.toLowerCase().replace(/\W/g, '_'); - this.exportToPc(this.prepareProfileExport(deviceProfile), name); - }, - (e) => { - this.handleExportError(e, 'device-profile.export-failed-error'); - } - ); - } - - public importDeviceProfile(): Observable { - return this.openImportDialog('device-profile.import', 'device-profile.device-profile-file').pipe( - mergeMap((deviceProfile: DeviceProfile) => { - if (!this.validateImportedDeviceProfile(deviceProfile)) { - this.store.dispatch(new ActionNotificationShow( - {message: this.translate.instant('device-profile.invalid-device-profile-file-error'), - type: 'error'})); - throw new Error('Invalid device profile file'); - } else { - return this.deviceProfileService.saveDeviceProfile(deviceProfile); - } - }), - catchError((err) => { - return of(null); - }) - ); - } - - public exportAssetProfile(assetProfileId: string) { - this.assetProfileService.getAssetProfile(assetProfileId).subscribe( - (assetProfile) => { - let name = assetProfile.name; - name = name.toLowerCase().replace(/\W/g, '_'); - this.exportToPc(this.prepareProfileExport(assetProfile), name); - }, - (e) => { - this.handleExportError(e, 'asset-profile.export-failed-error'); - } - ); - } - - public importAssetProfile(): Observable { - return this.openImportDialog('asset-profile.import', 'asset-profile.asset-profile-file').pipe( - mergeMap((assetProfile: AssetProfile) => { - if (!this.validateImportedAssetProfile(assetProfile)) { - this.store.dispatch(new ActionNotificationShow( - {message: this.translate.instant('asset-profile.invalid-asset-profile-file-error'), - type: 'error'})); - throw new Error('Invalid asset profile file'); - } else { - return this.assetProfileService.saveAssetProfile(assetProfile); - } - }), - catchError((err) => { - return of(null); - }) - ); - } - - public exportTenantProfile(tenantProfileId: string) { - this.tenantProfileService.getTenantProfile(tenantProfileId).subscribe( - (tenantProfile) => { - let name = tenantProfile.name; - name = name.toLowerCase().replace(/\W/g, '_'); - this.exportToPc(this.prepareProfileExport(tenantProfile), name); - }, - (e) => { - this.handleExportError(e, 'tenant-profile.export-failed-error'); - } - ); - } - - public importTenantProfile(): Observable { - return this.openImportDialog('tenant-profile.import', 'tenant-profile.tenant-profile-file').pipe( - mergeMap((tenantProfile: TenantProfile) => { - if (!this.validateImportedTenantProfile(tenantProfile)) { - this.store.dispatch(new ActionNotificationShow( - {message: this.translate.instant('tenant-profile.invalid-tenant-profile-file-error'), - type: 'error'})); - throw new Error('Invalid tenant profile file'); - } else { - return this.tenantProfileService.saveTenantProfile(tenantProfile); - } - }), - catchError(() => { - return of(null); - }) - ); - } - - private processCSVCell(cellData: any): any { - if (isString(cellData)) { - let result = cellData.replace(/"/g, '""'); - if (result.search(/([",\n])/g) >= 0) { - result = `"${result}"`; - } - return result; - } - return cellData; - } - - public exportCsv(data: {[key: string]: any}[], filename: string) { - let colsHead: string; - let colsData: string; - if (data && data.length) { - colsHead = Object.keys(data[0]).map(key => [this.processCSVCell(key)]).join(';'); - colsData = data.map(obj => [ - Object.keys(obj).map(col => [ - this.processCSVCell(obj[col]) - ]).join(';') - ]).join('\n'); - } else { - colsHead = ''; - colsData = ''; - } - const csvData = `${colsHead}\n${colsData}`; - this.downloadFile(csvData, filename, CSV_TYPE); - } - - public exportText(data: string | Array, filename: string) { - let content = data; - if (Array.isArray(data)) { - content = data.join('\n'); - } - this.downloadFile(content, filename, TEXT_TYPE); - } - - public exportJSZip(data: object, filename: string) { - import('jszip').then((JSZip) => { - const jsZip = new JSZip.default(); - for (const keyName in data) { - if (data.hasOwnProperty(keyName)) { - const valueData = data[keyName]; - jsZip.file(keyName, valueData); - } - } - jsZip.generateAsync({type: 'blob'}).then(content => { - this.downloadFile(content, filename, ZIP_TYPE); - }); - }); - } - - private prepareRuleChain(ruleChain: RuleChain): RuleChain { - ruleChain = this.prepareExport(ruleChain); - if (ruleChain.firstRuleNodeId) { - ruleChain.firstRuleNodeId = null; - } - ruleChain.root = false; - return ruleChain; - } - - private prepareRuleChainMetaData(ruleChainMetaData: RuleChainMetaData) { - delete ruleChainMetaData.ruleChainId; - for (let i = 0; i < ruleChainMetaData.nodes.length; i++) { - const node = ruleChainMetaData.nodes[i]; - delete node.ruleChainId; - ruleChainMetaData.nodes[i] = this.prepareExport(node); - } - return ruleChainMetaData; - } - - private validateImportedRuleChain(ruleChainImport: RuleChainImport): boolean { - if (isUndefined(ruleChainImport.ruleChain) - || isUndefined(ruleChainImport.metadata) - || isUndefined(ruleChainImport.ruleChain.name)) { - return false; - } - if (isUndefined(ruleChainImport.ruleChain.type)) { - ruleChainImport.ruleChain.type = RuleChainType.CORE; - } - return true; - } - - private validateImportedDeviceProfile(deviceProfile: DeviceProfile): boolean { - if (isUndefined(deviceProfile.name) - || isUndefined(deviceProfile.type) - || isUndefined(deviceProfile.transportType) - || isUndefined(deviceProfile.provisionType) - || isUndefined(deviceProfile.profileData)) { - return false; - } - return true; - } - - private validateImportedAssetProfile(assetProfile: AssetProfile): boolean { - if (isUndefined(assetProfile.name)) { - return false; - } - return true; - } - - private validateImportedTenantProfile(tenantProfile: TenantProfile): boolean { - return isDefined(tenantProfile.name) - && isDefined(tenantProfile.profileData) - && isDefined(tenantProfile.isolatedTbRuleEngine); - } - - private sumObject(obj1: any, obj2: any): any { - Object.keys(obj2).map((key) => { - if (isObject(obj2[key])) { - obj1[key] = obj1[key] || {}; - obj1[key] = {...obj1[key], ...this.sumObject(obj1[key], obj2[key])}; - } else if (isString(obj2[key])) { - obj1[key] = (obj1[key] || '') + `${obj2[key]}\n`; - } else { - obj1[key] = (obj1[key] || 0) + obj2[key]; - } - }); - return obj1; - } - - private handleExportError(e: any, errorDetailsMessageId: string) { - let message = e; - if (!message) { - message = this.translate.instant('error.unknown-error'); - } - this.store.dispatch(new ActionNotificationShow( - {message: this.translate.instant(errorDetailsMessageId, {error: message}), - type: 'error'})); - } - - private validateImportedDashboard(dashboard: Dashboard): boolean { - if (isUndefined(dashboard.title) || isUndefined(dashboard.configuration)) { - return false; - } - return true; - } - - private validateImportedWidget(widgetItem: WidgetItem): boolean { - if (isUndefined(widgetItem.widget) - || isUndefined(widgetItem.aliasesInfo) - || isUndefined(widgetItem.originalColumns)) { - return false; - } - const widget = widgetItem.widget; - if (isUndefined(widget.isSystemType) || - isUndefined(widget.bundleAlias) || - isUndefined(widget.typeAlias) || - isUndefined(widget.type)) { - return false; - } - return true; - } - - private validateImportedWidgetTypeDetails(widgetTypeDetails: WidgetTypeDetails): boolean { - if (isUndefined(widgetTypeDetails.name) - || isUndefined(widgetTypeDetails.descriptor)) { - return false; - } - return true; - } - - private validateImportedWidgetsBundle(widgetsBundleItem: WidgetsBundleItem): boolean { - if (isUndefined(widgetsBundleItem.widgetsBundle)) { - return false; - } - if (isUndefined(widgetsBundleItem.widgetTypes)) { - return false; - } - const widgetsBundle = widgetsBundleItem.widgetsBundle; - if (isUndefined(widgetsBundle.title)) { - return false; - } - const widgetTypesDetails = widgetsBundleItem.widgetTypes; - for (const widgetTypeDetails of widgetTypesDetails) { - if (!this.validateImportedWidgetTypeDetails(widgetTypeDetails)) { - return false; - } - } - return true; - } - - private saveImportedDashboard(dashboard: Dashboard): Observable { - return this.dashboardService.saveDashboard(dashboard); - } - - private addImportedWidget(dashboard: Dashboard, targetState: string, - targetLayoutFunction: () => Observable, - widget: Widget, aliasesInfo: AliasesInfo, - filtersInfo: FiltersInfo, - onAliasesUpdateFunction: () => void, - onFiltersUpdateFunction: () => void, - originalColumns: number, originalSize: WidgetSize): Observable { - return targetLayoutFunction().pipe( - mergeMap((targetLayout) => { - return this.itembuffer.addWidgetToDashboard(dashboard, targetState, targetLayout, - widget, aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, - originalColumns, originalSize, -1, -1).pipe( - map(() => ({widget, layoutId: targetLayout} as ImportWidgetResult)) - ); - } - )); - } - - private processEntityAliases(entityAliases: EntityAliases, aliasIds: string[]): Observable { - const tasks: Observable[] = []; - for (const aliasId of aliasIds) { - const entityAlias = entityAliases[aliasId]; - tasks.push( - this.entityService.checkEntityAlias(entityAlias).pipe( - map((result) => { - if (!result) { - const missingEntityAlias = deepClone(entityAlias); - missingEntityAlias.filter = null; - return missingEntityAlias; - } - return null; - } - ) - ) - ); - } - return forkJoin(tasks).pipe( - map((missingAliasesArray) => { - missingAliasesArray = missingAliasesArray.filter(alias => alias !== null); - const missingEntityAliases: EntityAliases = {}; - for (const missingAlias of missingAliasesArray) { - missingEntityAliases[missingAlias.id] = missingAlias; - } - return missingEntityAliases; - } - ) - ); - } - - private editMissingAliases(widgets: Array, isSingleWidget: boolean, - customTitle: string, missingEntityAliases: EntityAliases): Observable { - return this.dialog.open(EntityAliasesDialogComponent, { - disableClose: true, - panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], - data: { - entityAliases: missingEntityAliases, - widgets, - customTitle, - isSingleWidget, - disableAdd: true - } - }).afterClosed().pipe( - map((updatedEntityAliases) => { - if (updatedEntityAliases) { - return updatedEntityAliases; - } else { - throw new Error('Unable to resolve missing entity aliases!'); - } - } - )); - } - - private prepareAliasesInfo(aliasesInfo: AliasesInfo): AliasesInfo { - const datasourceAliases = aliasesInfo.datasourceAliases; - const targetDeviceAliases = aliasesInfo.targetDeviceAliases; - if (datasourceAliases || targetDeviceAliases) { - if (datasourceAliases) { - for (const strIndex of Object.keys(datasourceAliases)) { - const datasourceIndex = Number(strIndex); - datasourceAliases[datasourceIndex] = this.prepareEntityAlias(datasourceAliases[datasourceIndex]); - } - } - if (targetDeviceAliases) { - for (const strIndex of Object.keys(targetDeviceAliases)) { - const datasourceIndex = Number(strIndex); - targetDeviceAliases[datasourceIndex] = this.prepareEntityAlias(targetDeviceAliases[datasourceIndex]); - } - } - } - return aliasesInfo; - } - - private prepareEntityAlias(aliasInfo: EntityAliasInfo): EntityAliasInfo { - let alias: string; - let filter: EntityAliasFilter; - if (aliasInfo.deviceId) { - alias = aliasInfo.aliasName; - filter = { - type: AliasFilterType.entityList, - entityType: EntityType.DEVICE, - entityList: [aliasInfo.deviceId], - resolveMultiple: false - }; - } else if (aliasInfo.deviceFilter) { - alias = aliasInfo.aliasName; - filter = { - type: aliasInfo.deviceFilter.useFilter ? AliasFilterType.entityName : AliasFilterType.entityList, - entityType: EntityType.DEVICE, - resolveMultiple: false - }; - if (filter.type === AliasFilterType.entityList) { - filter.entityList = aliasInfo.deviceFilter.deviceList; - } else { - filter.entityNameFilter = aliasInfo.deviceFilter.deviceNameFilter; - } - } else if (aliasInfo.entityFilter) { - alias = aliasInfo.aliasName; - filter = { - type: aliasInfo.entityFilter.useFilter ? AliasFilterType.entityName : AliasFilterType.entityList, - entityType: aliasInfo.entityType, - resolveMultiple: false - }; - if (filter.type === AliasFilterType.entityList) { - filter.entityList = aliasInfo.entityFilter.entityList; - } else { - filter.entityNameFilter = aliasInfo.entityFilter.entityNameFilter; - } - } else { - alias = aliasInfo.alias; - filter = aliasInfo.filter; - } - return { - alias, - filter - }; - } - - private openImportDialog(importTitle: string, importFileLabel: string): Observable { - return this.dialog.open(ImportDialogComponent, { - disableClose: true, - panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], - data: { - importTitle, - importFileLabel - } - }).afterClosed().pipe( - map((importedData) => { - if (importedData) { - return importedData; - } else { - throw new Error('No file selected!'); - } - } - )); - } - - private exportToPc(data: any, filename: string) { - if (!data) { - console.error('No data'); - return; - } - this.exportJson(data, filename); - } - - private exportJson(data: any, filename: string) { - if (isObject(data)) { - data = JSON.stringify(data, null, 2); - } - this.downloadFile(data, filename, JSON_TYPE); - } - - private downloadFile(data: any, filename: string, fileType: FileType) { - if (!filename) { - filename = 'download'; - } - filename += '.' + fileType.extension; - const blob = new Blob([data], {type: fileType.mimeType}); - // @ts-ignore - if (this.window.navigator && this.window.navigator.msSaveOrOpenBlob) { - // @ts-ignore - this.window.navigator.msSaveOrOpenBlob(blob, filename); - } else { - const e = this.document.createEvent('MouseEvents'); - const a = this.document.createElement('a'); - a.download = filename; - a.href = URL.createObjectURL(blob); - a.dataset.downloadurl = [fileType.mimeType, a.download, a.href].join(':'); - // @ts-ignore - e.initEvent('click', true, false, this.window, - 0, 0, 0, 0, 0, false, false, false, false, 0, null); - a.dispatchEvent(e); - } - } - - private prepareDashboardExport(dashboard: Dashboard): Dashboard { - dashboard = this.prepareExport(dashboard); - delete dashboard.assignedCustomers; - return dashboard; - } - - private prepareProfileExport(profile: T): T { - profile = this.prepareExport(profile); - profile.default = false; - return profile; - } - - private prepareExport(data: any): any { - const exportedData = deepClone(data); - if (isDefined(exportedData.id)) { - delete exportedData.id; - } - if (isDefined(exportedData.createdTime)) { - delete exportedData.createdTime; - } - if (isDefined(exportedData.tenantId)) { - delete exportedData.tenantId; - } - if (isDefined(exportedData.customerId)) { - delete exportedData.customerId; - } - return exportedData; - } - -} diff --git a/ui-ngx/src/app/modules/home/components/import-export/table-columns-assignment.component.ts b/ui-ngx/src/app/modules/home/components/import-export/table-columns-assignment.component.ts deleted file mode 100644 index 2a65444970..0000000000 --- a/ui-ngx/src/app/modules/home/components/import-export/table-columns-assignment.component.ts +++ /dev/null @@ -1,237 +0,0 @@ -/// -/// Copyright © 2016-2023 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -import { Component, ElementRef, forwardRef, Input, OnInit } from '@angular/core'; -import { ControlValueAccessor, UntypedFormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, Validator } from '@angular/forms'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { EntityType } from '@shared/models/entity-type.models'; -import { - CsvColumnParam, - ImportEntityColumnType, - importEntityColumnTypeTranslations -} from '@home/components/import-export/import-export.models'; -import { BehaviorSubject, Observable } from 'rxjs'; -import { CollectionViewer, DataSource } from '@angular/cdk/collections'; - -@Component({ - selector: 'tb-table-columns-assignment', - templateUrl: './table-columns-assignment.component.html', - styleUrls: ['./table-columns-assignment.component.scss'], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => TableColumnsAssignmentComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => TableColumnsAssignmentComponent), - multi: true, - } - ] -}) -export class TableColumnsAssignmentComponent implements OnInit, ControlValueAccessor, Validator { - - @Input() entityType: EntityType; - - @Input() disabled: boolean; - - dataSource = new CsvColumnsDatasource(); - - displayedColumns = ['order', 'sampleData', 'type', 'key']; - - columnTypes: AssignmentColumnType[] = []; - - columnDeviceCredentials: AssignmentColumnType[] = []; - - columnTypesTranslations = importEntityColumnTypeTranslations; - - readonly entityTypeDevice = EntityType.DEVICE; - - private columns: CsvColumnParam[]; - - private valid = true; - - private propagateChangePending = false; - private propagateChange = null; - - constructor(public elementRef: ElementRef, - protected store: Store) { - } - - ngOnInit(): void { - this.columnTypes.push( - { value: ImportEntityColumnType.name }, - { value: ImportEntityColumnType.type }, - { value: ImportEntityColumnType.label }, - { value: ImportEntityColumnType.description }, - ); - switch (this.entityType) { - case EntityType.DEVICE: - this.columnTypes.push( - { value: ImportEntityColumnType.sharedAttribute }, - { value: ImportEntityColumnType.serverAttribute }, - { value: ImportEntityColumnType.timeseries }, - { value: ImportEntityColumnType.isGateway } - ); - this.columnDeviceCredentials.push( - { value: ImportEntityColumnType.accessToken }, - { value: ImportEntityColumnType.x509 }, - { value: ImportEntityColumnType.mqttClientId }, - { value: ImportEntityColumnType.mqttUserName }, - { value: ImportEntityColumnType.mqttPassword }, - { value: ImportEntityColumnType.lwm2mClientEndpoint }, - { value: ImportEntityColumnType.lwm2mClientSecurityConfigMode }, - { value: ImportEntityColumnType.lwm2mClientIdentity }, - { value: ImportEntityColumnType.lwm2mClientKey }, - { value: ImportEntityColumnType.lwm2mClientCert }, - { value: ImportEntityColumnType.lwm2mBootstrapServerSecurityMode }, - { value: ImportEntityColumnType.lwm2mBootstrapServerClientPublicKeyOrId }, - { value: ImportEntityColumnType.lwm2mBootstrapServerClientSecretKey }, - { value: ImportEntityColumnType.lwm2mServerSecurityMode }, - { value: ImportEntityColumnType.lwm2mServerClientPublicKeyOrId }, - { value: ImportEntityColumnType.lwm2mServerClientSecretKey }, - ); - break; - case EntityType.ASSET: - this.columnTypes.push( - { value: ImportEntityColumnType.serverAttribute }, - { value: ImportEntityColumnType.timeseries } - ); - break; - case EntityType.EDGE: - this.columnTypes.push( - { value: ImportEntityColumnType.routingKey }, - { value: ImportEntityColumnType.secret }, - { value: ImportEntityColumnType.serverAttribute }, - { value: ImportEntityColumnType.timeseries } - ); - break; - } - } - - registerOnChange(fn: any): void { - this.propagateChange = fn; - if (this.propagateChangePending) { - this.propagateChange(this.columns); - this.propagateChangePending = false; - } - } - - registerOnTouched(fn: any): void { - } - - setDisabledState(isDisabled: boolean): void { - this.disabled = isDisabled; - } - - columnsUpdated() { - const isSelectName = this.columns.findIndex((column) => column.type === ImportEntityColumnType.name) > -1; - const isSelectType = this.columns.findIndex((column) => column.type === ImportEntityColumnType.type) > -1; - const isSelectLabel = this.columns.findIndex((column) => column.type === ImportEntityColumnType.label) > -1; - const isSelectDescription = this.columns.findIndex((column) => column.type === ImportEntityColumnType.description) > -1; - const hasInvalidColumn = this.columns.findIndex((column) => !this.columnValid(column)) > -1; - - this.valid = isSelectName && isSelectType && !hasInvalidColumn; - - this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.name).disabled = isSelectName; - this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.type).disabled = isSelectType; - this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.label).disabled = isSelectLabel; - this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.description).disabled = isSelectDescription; - - if (this.entityType === EntityType.DEVICE) { - const isSelectGateway = this.columns.findIndex((column) => column.type === ImportEntityColumnType.isGateway) > -1; - - const isGatewayColumnType = this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.isGateway); - if (isGatewayColumnType) { - isGatewayColumnType.disabled = isSelectGateway; - } - - this.columnDeviceCredentials.forEach((columnCredential) => { - columnCredential.disabled = this.columns.findIndex(column => column.type === columnCredential.value) > -1; - }); - } - - if (this.entityType === EntityType.EDGE) { - const isSelectRoutingKey = this.columns.findIndex((column) => column.type === ImportEntityColumnType.routingKey) > -1; - const isSelectSecret = this.columns.findIndex((column) => column.type === ImportEntityColumnType.secret) > -1; - - this.valid = this.valid && isSelectSecret && isSelectRoutingKey; - - this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.routingKey).disabled = isSelectRoutingKey; - this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.secret).disabled = isSelectSecret; - } - - if (this.propagateChange) { - this.propagateChange(this.columns); - } else { - this.propagateChangePending = true; - } - } - - public isColumnTypeDiffers(columnType: ImportEntityColumnType): boolean { - return columnType === ImportEntityColumnType.clientAttribute || - columnType === ImportEntityColumnType.sharedAttribute || - columnType === ImportEntityColumnType.serverAttribute || - columnType === ImportEntityColumnType.timeseries; - } - - private columnValid(column: CsvColumnParam): boolean { - if (this.isColumnTypeDiffers(column.type)) { - return column.key && column.key.trim().length > 0; - } else { - return true; - } - } - - public validate(c: UntypedFormControl) { - return (this.valid) ? null : { - columnsInvalid: true - }; - } - - writeValue(value: CsvColumnParam[]): void { - this.columns = value; - this.dataSource.setColumns(this.columns); - this.columnsUpdated(); - } -} - -interface AssignmentColumnType { - value: ImportEntityColumnType; - disabled?: boolean; -} - -class CsvColumnsDatasource implements DataSource { - - private columnsSubject = new BehaviorSubject([]); - - constructor() {} - - connect(collectionViewer: CollectionViewer): Observable> { - return this.columnsSubject.asObservable(); - } - - disconnect(collectionViewer: CollectionViewer): void { - this.columnsSubject.complete(); - } - - setColumns(columns: CsvColumnParam[]) { - this.columnsSubject.next(columns); - } - -} diff --git a/ui-ngx/src/app/modules/home/components/notification/notification-bell.component.ts b/ui-ngx/src/app/modules/home/components/notification/notification-bell.component.ts index 8ee5771fe7..33b126c663 100644 --- a/ui-ngx/src/app/modules/home/components/notification/notification-bell.component.ts +++ b/ui-ngx/src/app/modules/home/components/notification/notification-bell.component.ts @@ -25,11 +25,11 @@ import { } from '@angular/core'; import { NotificationWebsocketService } from '@core/ws/notification-websocket.service'; import { BehaviorSubject, ReplaySubject, Subscription } from 'rxjs'; -import { distinctUntilChanged, map, share, tap } from 'rxjs/operators'; +import { distinctUntilChanged, map, share, skip, tap } from 'rxjs/operators'; import { MatButton } from '@angular/material/button'; import { TbPopoverService } from '@shared/components/popover.service'; import { ShowNotificationPopoverComponent } from '@home/components/notification/show-notification-popover.component'; -import { NotificationSubscriber } from '@shared/models/websocket/notification-ws.models'; +import { NotificationSubscriber } from '@shared/models/telemetry/telemetry.models'; import { select, Store } from '@ngrx/store'; import { selectIsAuthenticated } from '@core/auth/auth.selectors'; import { AppState } from '@core/core.state'; @@ -77,11 +77,11 @@ export class NotificationBellComponent implements OnDestroy { if ($event) { $event.stopPropagation(); } - this.unsubscribeSubscription(); const trigger = createVersionButton._elementRef.nativeElement; if (this.popoverService.hasPopover(trigger)) { this.popoverService.hidePopover(trigger); } else { + this.unsubscribeSubscription(); const showNotificationPopover = this.popoverService.displayPopover(trigger, this.renderer, this.viewContainerRef, ShowNotificationPopoverComponent, 'bottom', true, null, { @@ -100,7 +100,9 @@ export class NotificationBellComponent implements OnDestroy { private initSubscription() { this.notificationSubscriber = NotificationSubscriber.createNotificationCountSubscription(this.notificationWsService, this.zone); - this.notificationCountSubscriber = this.notificationSubscriber.notificationCount$.subscribe(value => this.countSubject.next(value)); + this.notificationCountSubscriber = this.notificationSubscriber.notificationCount$.pipe( + skip(1), + ).subscribe(value => this.countSubject.next(value)); this.notificationSubscriber.subscribe(); } @@ -108,5 +110,6 @@ export class NotificationBellComponent implements OnDestroy { private unsubscribeSubscription() { this.notificationCountSubscriber.unsubscribe(); this.notificationSubscriber.unsubscribe(); + this.notificationSubscriber = null; } } diff --git a/ui-ngx/src/app/modules/home/components/notification/show-notification-popover.component.html b/ui-ngx/src/app/modules/home/components/notification/show-notification-popover.component.html index 0a0a5e3cdb..fa622c30bf 100644 --- a/ui-ngx/src/app/modules/home/components/notification/show-notification-popover.component.html +++ b/ui-ngx/src/app/modules/home/components/notification/show-notification-popover.component.html @@ -24,24 +24,32 @@
-
-
-
- - - -
+ +
+
+
+ + + +
+
+
+ +
+
-
- -
- -
+ empty notification notification.no-notifications-yet + +
+ +
notification.loading-notifications
+
+
diff --git a/ui-ngx/src/app/modules/home/components/notification/show-notification-popover.component.ts b/ui-ngx/src/app/modules/home/components/notification/show-notification-popover.component.ts index 5e94289055..7865da5c29 100644 --- a/ui-ngx/src/app/modules/home/components/notification/show-notification-popover.component.ts +++ b/ui-ngx/src/app/modules/home/components/notification/show-notification-popover.component.ts @@ -22,9 +22,9 @@ import { AppState } from '@core/core.state'; import { Notification, NotificationRequest } from '@shared/models/notification.models'; import { NotificationWebsocketService } from '@core/ws/notification-websocket.service'; import { BehaviorSubject, Observable, ReplaySubject, Subscription } from 'rxjs'; -import { share, tap } from 'rxjs/operators'; +import { map, share, skip, tap } from 'rxjs/operators'; import { Router } from '@angular/router'; -import { NotificationSubscriber } from '@shared/models/websocket/notification-ws.models'; +import { NotificationSubscriber } from '@shared/models/telemetry/telemetry.models'; @Component({ selector: 'tb-show-notification-popover', @@ -46,6 +46,7 @@ export class ShowNotificationPopoverComponent extends PageComponent implements O private notificationCountSubscriber: Subscription; notifications$: Observable; + loadNotification = false; constructor(protected store: Store, private notificationWsService: NotificationWebsocketService, @@ -58,12 +59,21 @@ export class ShowNotificationPopoverComponent extends PageComponent implements O ngOnInit() { this.notificationSubscriber = NotificationSubscriber.createNotificationsSubscription(this.notificationWsService, this.zone, 6); this.notifications$ = this.notificationSubscriber.notifications$.pipe( + map(value => { + if (Array.isArray(value)) { + this.loadNotification = true; + return value; + } + return []; + }), share({ connector: () => new ReplaySubject(1) }), tap(() => setTimeout(() => this.cd.markForCheck())) ); - this.notificationCountSubscriber = this.notificationSubscriber.notificationCount$.subscribe(value => this.counter.next(value)); + this.notificationCountSubscriber = this.notificationSubscriber.notificationCount$.pipe( + skip(1), + ).subscribe(value => this.counter.next(value)); this.notificationSubscriber.subscribe(); } diff --git a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html index 779bc94f8f..4480d76fab 100644 --- a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html @@ -78,11 +78,10 @@ {{ 'device-profile.type-required' | translate }} - - + device-profile.description diff --git a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.scss b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.scss index ca54ae2053..0018264075 100644 --- a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.scss @@ -40,12 +40,10 @@ padding: 0 !important; .mat-stepper-horizontal { - display: flex; - height: 100%; overflow: hidden; .mat-horizontal-stepper-wrapper { - flex: 1 1 100%; + max-height: 100%; } .mat-horizontal-content-container { diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-duration-predicate-value.component.html b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-duration-predicate-value.component.html index d4d8644aad..6fe2c7a101 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-duration-predicate-value.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-duration-predicate-value.component.html @@ -71,6 +71,6 @@ matTooltip="{{ (dynamicMode ? 'filter.switch-to-default-value' : 'filter.switch-to-dynamic-value') | translate }}" matTooltipPosition="above" (click)="dynamicMode = !dynamicMode"> - + {{ dynamicMode ? 'mdi:numeric' : 'mdi:variable' }}
diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.html b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.html index 5e7ca9b096..134651a1fd 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.html @@ -22,7 +22,7 @@
- {{ alarmRuleFormGroup.get('alarmDetails').value ? ('device-profile.alarm-rule-details' | translate) + ': ' : ('device-profile.add-alarm-rule-details' | translate) }} + {{ ('device-profile.alarm-rule-additional-info' | translate) + ': ' }} -

{{ 'device-profile.alarm-rule-details' | translate }}

+

{{ 'device-profile.edit-alarm-rule-additional-info' | translate }}

diff --git a/ui-ngx/src/app/modules/home/components/profile/asset-profile-autocomplete.component.html b/ui-ngx/src/app/modules/home/components/profile/asset-profile-autocomplete.component.html index 87ee2a7fa1..78368d4174 100644 --- a/ui-ngx/src/app/modules/home/components/profile/asset-profile-autocomplete.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/asset-profile-autocomplete.component.html @@ -25,8 +25,9 @@ (keydown)="assetProfileEnter($event)" (keypress)="assetProfileEnter($event)" [matAutocomplete]="assetProfileAutocomplete" - [fxShow]="!showDetailsPageLink || !disabled || !selectAssetProfileFormGroup.get('assetProfile').value"> - + [fxShow]="!showDetailsPageLink || !useAssetProfileLink || !disabled || !selectAssetProfileFormGroup.get('assetProfile').value"> + {{ displayAssetProfileFn(selectAssetProfileFormGroup.get('assetProfile').value) }} - + { this.modelValue = new AssetProfileId(profile.id.id); - this.assetProfileURL = getEntityDetailsPageURL(this.modelValue.id, this.modelValue.entityType); + if (this.useAssetProfileLink) { + this.assetProfileURL = getEntityDetailsPageURL(this.modelValue.id, this.modelValue.entityType); + } this.selectAssetProfileFormGroup.get('assetProfile').patchValue(profile, {emitEvent: false}); this.assetProfileChanged.emit(profile); } diff --git a/ui-ngx/src/app/modules/home/components/profile/asset-profile.component.html b/ui-ngx/src/app/modules/home/components/profile/asset-profile.component.html index dcd1899321..88b55d508b 100644 --- a/ui-ngx/src/app/modules/home/components/profile/asset-profile.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/asset-profile.component.html @@ -83,11 +83,10 @@ [ruleChainType]="edgeRuleChainType"> {{'asset-profile.default-edge-rule-chain-hint' | translate}} - - + asset-profile.description diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html b/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html index 2a118f6300..574fa350d0 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html @@ -25,8 +25,9 @@ (keydown)="deviceProfileEnter($event)" (keypress)="deviceProfileEnter($event)" [matAutocomplete]="deviceProfileAutocomplete" - [fxShow]="!showDetailsPageLink || !disabled || !selectDeviceProfileFormGroup.get('deviceProfile').value"> - + [fxShow]="!showDetailsPageLink || !useDeviceProfileLink || !disabled || !selectDeviceProfileFormGroup.get('deviceProfile').value"> + {{ displayDeviceProfileFn(selectDeviceProfileFormGroup.get('deviceProfile').value) }} - + { this.modelValue = new DeviceProfileId(profile.id.id); - this.deviceProfileURL = getEntityDetailsPageURL(this.modelValue.id, this.modelValue.entityType); + if (this.useDeviceProfileLink) { + this.deviceProfileURL = getEntityDetailsPageURL(this.modelValue.id, this.modelValue.entityType); + } this.selectDeviceProfileFormGroup.get('deviceProfile').patchValue(profile, {emitEvent: false}); this.deviceProfileChanged.emit(profile); } diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html index 7f9505e3ff..af0330953f 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html @@ -108,11 +108,10 @@ {{ 'device-profile.type-required' | translate }} - - + device-profile.description diff --git a/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.ts b/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.ts index ac128b3ec5..298b4fe575 100644 --- a/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/queue/tenant-profile-queues.component.ts @@ -173,7 +173,8 @@ export class TenantProfileQueuesComponent implements ControlValueAccessor, Valid }, topic: '', additionalInfo: { - description: '' + description: '', + customProperties: '' } }; this.idMap.push(queue.id); diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant-profile-autocomplete.component.html b/ui-ngx/src/app/modules/home/components/profile/tenant-profile-autocomplete.component.html index 2c5a019b70..cd7fadab2b 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant-profile-autocomplete.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/tenant-profile-autocomplete.component.html @@ -35,7 +35,7 @@ (click)="clear()"> close - + - +

{{ (isAdd ? 'tenant-profile.add' : 'tenant-profile.edit' ) | translate }}

diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant-profile-dialog.component.scss b/ui-ngx/src/app/modules/home/components/profile/tenant-profile-dialog.component.scss new file mode 100644 index 0000000000..a881fa5023 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/tenant-profile-dialog.component.scss @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host ::ng-deep { + tb-tenant-profile { + .mat-padding { + padding: 0; + + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant-profile-dialog.component.ts b/ui-ngx/src/app/modules/home/components/profile/tenant-profile-dialog.component.ts index 96ead6405d..b5f910821a 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant-profile-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/tenant-profile-dialog.component.ts @@ -43,7 +43,7 @@ export interface TenantProfileDialogData { selector: 'tb-tenant-profile-dialog', templateUrl: './tenant-profile-dialog.component.html', providers: [{provide: ErrorStateMatcher, useExisting: TenantProfileDialogComponent}], - styleUrls: [] + styleUrls: ['tenant-profile-dialog.component.scss'] }) export class TenantProfileDialogComponent extends DialogComponent implements ErrorStateMatcher, AfterViewInit { diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts b/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts index 0257fb35af..3f048ab2e3 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts @@ -56,11 +56,11 @@ export class TenantProfileComponent extends EntityComponent { const mainQueue = [ { id: guid(), - consumerPerPartition: true, + consumerPerPartition: false, name: 'Main', - packProcessingTimeout: 2000, - partitions: 10, - pollInterval: 25, + packProcessingTimeout: 10000, + partitions: 1, + pollInterval: 2000, processingStrategy: { failurePercentage: 0, maxPauseBetweenRetries: 3, @@ -74,7 +74,56 @@ export class TenantProfileComponent extends EntityComponent { }, topic: 'tb_rule_engine.main', additionalInfo: { - description: '' + description: '', + customProperties: '' + } + }, + { + id: guid(), + name: 'HighPriority', + topic: 'tb_rule_engine.hp', + pollInterval: 2000, + partitions: 1, + consumerPerPartition: false, + packProcessingTimeout: 10000, + submitStrategy: { + type: 'BURST', + batchSize: 100 + }, + processingStrategy: { + type: 'RETRY_FAILED_AND_TIMED_OUT', + retries: 0, + failurePercentage: 0, + pauseBetweenRetries: 5, + maxPauseBetweenRetries: 5 + }, + additionalInfo: { + description: '', + customProperties: '' + } + }, + { + id: guid(), + name: 'SequentialByOriginator', + topic: 'tb_rule_engine.sq', + pollInterval: 2000, + partitions: 1, + consumerPerPartition: false, + packProcessingTimeout: 10000, + submitStrategy: { + type: 'SEQUENTIAL_BY_ORIGINATOR', + batchSize: 100 + }, + processingStrategy: { + type: 'RETRY_FAILED_AND_TIMED_OUT', + retries: 3, + failurePercentage: 0, + pauseBetweenRetries: 5, + maxPauseBetweenRetries: 5 + }, + additionalInfo: { + description: '', + customProperties: '' } } ]; @@ -118,9 +167,6 @@ export class TenantProfileComponent extends EntityComponent { if (this.entityForm) { if (this.isEditValue) { this.entityForm.enable({emitEvent: false}); - if (!this.isAdd) { - this.entityForm.get('isolatedTbRuleEngine').disable({emitEvent: false}); - } } else { this.entityForm.disable({emitEvent: false}); } diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html index c2872d4145..29006d4ad8 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html @@ -169,15 +169,15 @@
- tenant-profile.max-transport-data-points + tenant-profile.max-tbel-executions - - {{ 'tenant-profile.max-transport-data-points-required' | translate}} + + {{ 'tenant-profile.max-tbel-executions-required' | translate}} - - {{ 'tenant-profile.max-transport-data-points-range' | translate}} + + {{ 'tenant-profile.max-tbel-executions-range' | translate}} @@ -196,7 +196,19 @@ -
+ + tenant-profile.max-transport-data-points + + + {{ 'tenant-profile.max-transport-data-points-required' | translate}} + + + {{ 'tenant-profile.max-transport-data-points-range' | translate}} + + +
@@ -262,6 +274,34 @@
+
+ + tenant-profile.queue-stats-ttl-days + + + {{ 'tenant-profile.queue-stats-ttl-days-required' | translate}} + + + {{ 'tenant-profile.queue-stats-ttl-days-range' | translate}} + + + + + tenant-profile.rule-engine-exceptions-ttl-days + + + {{ 'tenant-profile.rule-engine-exceptions-ttl-days-required' | translate}} + + + {{ 'tenant-profile.rule-engine-exceptions-ttl-days-range' | translate}} + + + +
@@ -346,6 +386,22 @@
+
+ + tenant-profile.maximum-resource-size + + + {{ 'tenant-profile.maximum-resource-size-required' | translate}} + + + {{ 'tenant-profile.maximum-resource-size-range' | translate}} + + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts index a4915f924b..7718cd4b2f 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts @@ -67,6 +67,7 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA maxRuleChains: [null, [Validators.required, Validators.min(0)]], maxResourcesInBytes: [null, [Validators.required, Validators.min(0)]], maxOtaPackagesInBytes: [null, [Validators.required, Validators.min(0)]], + maxResourceSize: [null, [Validators.required, Validators.min(0)]], transportTenantMsgRateLimit: [null, []], transportTenantTelemetryMsgRateLimit: [null, []], transportTenantTelemetryDataPointsRateLimit: [null, []], @@ -81,6 +82,7 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA maxTransportDataPoints: [null, [Validators.required, Validators.min(0)]], maxREExecutions: [null, [Validators.required, Validators.min(0)]], maxJSExecutions: [null, [Validators.required, Validators.min(0)]], + maxTbelExecutions: [null, [Validators.required, Validators.min(0)]], maxDPStorageDays: [null, [Validators.required, Validators.min(0)]], maxRuleNodeExecutionsPerMessage: [null, [Validators.required, Validators.min(0)]], maxEmails: [null, [Validators.required, Validators.min(0)]], @@ -90,6 +92,8 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA defaultStorageTtlDays: [null, [Validators.required, Validators.min(0)]], alarmsTtlDays: [null, [Validators.required, Validators.min(0)]], rpcTtlDays: [null, [Validators.required, Validators.min(0)]], + queueStatsTtlDays: [null, [Validators.required, Validators.min(0)]], + ruleEngineExceptionsTtlDays: [null, [Validators.required, Validators.min(0)]], tenantServerRestLimitsConfiguration: [null, []], customerServerRestLimitsConfiguration: [null, []], maxWsSessionsPerTenant: [null, [Validators.min(0)]], diff --git a/ui-ngx/src/app/modules/home/components/public-api.ts b/ui-ngx/src/app/modules/home/components/public-api.ts index 34b1960365..aca4d33e39 100644 --- a/ui-ngx/src/app/modules/home/components/public-api.ts +++ b/ui-ngx/src/app/modules/home/components/public-api.ts @@ -15,3 +15,5 @@ /// export * from './home-components.module'; + +export * from './widget/config/widget-config.component.models'; diff --git a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html index 56b20d8aa8..53262717f9 100644 --- a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html +++ b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html @@ -91,9 +91,10 @@
- + queue.retries + {{ 'queue.retries-required' | translate }} @@ -102,9 +103,10 @@ {{ 'queue.retries-min-value' | translate }} - + queue.failure-percentage + {{ 'queue.failure-percentage-required' | translate }} @@ -119,9 +121,10 @@ {{ 'queue.failure-percentage-max-value' | translate }} - + queue.pause-between-retries + {{ 'queue.pause-between-retries-required' | translate }} @@ -130,9 +133,10 @@ {{ 'queue.pause-between-retries-min-value' | translate }} - + queue.max-pause-between-retries + {{ 'queue.max-pause-between-retries-required' | translate }} @@ -203,6 +207,11 @@ + + queue.custom-properties + + queue.custom-properties-hint + queue.description diff --git a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts index b123cf2ad5..e4fbcd031b 100644 --- a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts +++ b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts @@ -117,7 +117,8 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestr }), topic: [''], additionalInfo: this.fb.group({ - description: [''] + description: [''], + customProperties: [''] }) }); this.valueChange$ = this.queueFormGroup.valueChanges.subscribe(() => { diff --git a/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.html b/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.html index f1d390bc77..062bba86d6 100644 --- a/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.html +++ b/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.html @@ -15,55 +15,47 @@ limitations under the License. --> -
-
-
-
- relation.type - entity.entity-types -   -
+
+
+
+
{{ 'relation.relation-type' | translate }}
+
{{ 'entity.entity-types' | translate }}
+
-
-
+
-
- - - - -
+
+ relation.any-relation +
-
- relation.any-relation -
-
diff --git a/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.scss b/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.scss index b18f226f4f..648076be4c 100644 --- a/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.scss +++ b/ui-ngx/src/app/modules/home/components/relation/relation-filters.component.scss @@ -14,44 +14,15 @@ * limitations under the License. */ :host { - .tb-relation-filters { - max-width: calc(100vw - 48px); - overflow: hidden; - - .container{ - width: 100%; - } - - .header { - padding: 0 5px 5px; - - .cell { - padding-right: 5px; - padding-left: 5px; - font-size: 12px; - font-weight: 700; - color: rgba(0, 0, 0, .54); - white-space: nowrap; - } - } - - .body { - padding: 0 5px 20px; - max-height: 300px; - overflow: auto; - - .row { - padding-top: 5px; - } + .flex-50 { + flex: 1 1 50%; + } - .cell { - padding-right: 5px; - padding-left: 5px; - } - } + .actions-header { + width: 40px + } - .any-filter{ - margin: 10px 0 20px; - } + .entity-type-list { + display: flex; } } diff --git a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.html b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.html index c96b22a1cc..cf1186fa67 100644 --- a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.html +++ b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.html @@ -62,7 +62,7 @@   +
+ +
+
+
widget-config.appearance
+ +
+
+
widget-config.card-appearance
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
widget-config.show-card-buttons
+ + {{ 'fullscreen.fullscreen' | translate }} + +
+
+
{{ 'widget-config.card-border-radius' | translate }}
+ + + +
+
+ + + diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarm-count-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarm-count-basic-config.component.ts new file mode 100644 index 0000000000..782d2d4547 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarm-count-basic-config.component.ts @@ -0,0 +1,107 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { DatasourceType, WidgetConfig, widgetType, } from '@shared/models/widget.models'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { isUndefined } from '@core/utils'; +import { getAlarmFilterConfig, setAlarmFilterConfig } from '@shared/models/widget-settings.models'; +import { UtilsService } from '@core/services/utils.service'; +import { AlarmSearchStatus } from '@shared/models/alarm.models'; +import { + countDefaultSettings, + CountWidgetSettings +} from '@home/components/widget/lib/count/count-widget.models'; + +@Component({ + selector: 'tb-alarm-count-basic-config', + templateUrl: './alarm-count-basic-config.component.html', + styleUrls: ['../basic-config.scss'] +}) +export class AlarmCountBasicConfigComponent extends BasicWidgetConfigComponent { + + alarmSearchStatus = AlarmSearchStatus; + + alarmCountWidgetConfigForm: UntypedFormGroup; + + constructor(protected store: Store, + protected widgetConfigComponent: WidgetConfigComponent, + private utils: UtilsService, + private fb: UntypedFormBuilder) { + super(store, widgetConfigComponent); + } + + protected configForm(): UntypedFormGroup { + return this.alarmCountWidgetConfigForm; + } + + protected setupDefaults(configData: WidgetConfigComponentData) { + let datasources = configData.config.datasources; + if (!datasources || !datasources.length) { + datasources = [{}]; + configData.config.datasources = datasources; + } + datasources[0].type = DatasourceType.alarmCount; + datasources[0].alarmFilterConfig = {statusList: [AlarmSearchStatus.ACTIVE]}; + datasources[0].dataKeys = [this.utils.createKey({name: 'count'}, DataKeyType.count)]; + } + + protected onConfigSet(configData: WidgetConfigComponentData) { + const settings: CountWidgetSettings = {...countDefaultSettings(true), ...(configData.config.settings || {})}; + this.alarmCountWidgetConfigForm = this.fb.group({ + alarmFilterConfig: [getAlarmFilterConfig(configData.config.datasources), []], + + settings: [settings, []], + + backgroundColor: [configData.config.backgroundColor, []], + cardButtons: [this.getCardButtons(configData.config), []], + borderRadius: [configData.config.borderRadius, []], + + actions: [configData.config.actions || {}, []] + }); + } + + protected prepareOutputConfig(config: any): WidgetConfigComponentData { + setAlarmFilterConfig(config.alarmFilterConfig, this.widgetConfig.config.datasources); + + this.widgetConfig.config.settings = {...(this.widgetConfig.config.settings || {}), ...config.settings}; + + this.widgetConfig.config.backgroundColor = config.backgroundColor; + this.setCardButtons(config.cardButtons, this.widgetConfig.config); + this.widgetConfig.config.borderRadius = config.borderRadius; + + this.widgetConfig.config.actions = config.actions; + return this.widgetConfig; + } + + private getCardButtons(config: WidgetConfig): string[] { + const buttons: string[] = []; + if (isUndefined(config.enableFullscreen) || config.enableFullscreen) { + buttons.push('fullscreen'); + } + return buttons; + } + + private setCardButtons(buttons: string[], config: WidgetConfig) { + config.enableFullscreen = buttons.includes('fullscreen'); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarms-table-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarms-table-basic-config.component.html new file mode 100644 index 0000000000..c2e8de37c4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarms-table-basic-config.component.html @@ -0,0 +1,139 @@ + + + + + + +
+
+
alarm.filter
+ +
+ +
+ + +
+
widget-config.appearance
+
+ + {{ 'widget-config.card-title' | translate }} + +
+ + + + + + + +
+
+
+ + {{ 'widget-config.card-icon' | translate }} + +
+ + + + +
+
+
+
+
widgets.table.table-buttons
+
+ + {{ 'widgets.table.display-alarm-activity' | translate }} + + + {{ 'widgets.table.display-alarm-details' | translate }} + + + {{ 'widgets.table.allow-alarms-assign' | translate }} + + + {{ 'widgets.table.allow-alarms-ack' | translate }} + + + {{ 'widgets.table.allow-alarms-clear' | translate }} + +
+
+
+
widget-config.card-appearance
+
+
widget-config.show-card-buttons
+ + {{ 'action.search' | translate }} + {{ 'alarm.alarm-filter' | translate }} + {{ 'widgets.table.columns-to-display' | translate }} + {{ 'fullscreen.fullscreen' | translate }} + +
+
+
{{ 'widget-config.text-color' | translate }}
+ + +
+
+
{{ 'widget-config.background-color' | translate }}
+ + +
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarms-table-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarms-table-basic-config.component.ts new file mode 100644 index 0000000000..4f61aea583 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/alarm/alarms-table-basic-config.component.ts @@ -0,0 +1,185 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { DataKey, Datasource, WidgetConfig } from '@shared/models/widget.models'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { isUndefined } from '@core/utils'; +import { + getTimewindowConfig, + setTimewindowConfig +} from '@home/components/widget/config/timewindow-config-panel.component'; + +@Component({ + selector: 'tb-alarms-table-basic-config', + templateUrl: './alarms-table-basic-config.component.html', + styleUrls: ['../basic-config.scss'] +}) +export class AlarmsTableBasicConfigComponent extends BasicWidgetConfigComponent { + + public get alarmSource(): Datasource { + const datasources: Datasource[] = this.alarmsTableWidgetConfigForm.get('datasources').value; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + + alarmsTableWidgetConfigForm: UntypedFormGroup; + + constructor(protected store: Store, + protected widgetConfigComponent: WidgetConfigComponent, + private fb: UntypedFormBuilder) { + super(store, widgetConfigComponent); + } + + protected configForm(): UntypedFormGroup { + return this.alarmsTableWidgetConfigForm; + } + + protected onConfigSet(configData: WidgetConfigComponentData) { + this.alarmsTableWidgetConfigForm = this.fb.group({ + timewindowConfig: [getTimewindowConfig(configData.config), []], + alarmFilterConfig: [configData.config.alarmFilterConfig, []], + datasources: [[configData.config.alarmSource], []], + columns: [this.getColumns(configData.config.alarmSource), []], + showTitle: [configData.config.showTitle, []], + title: [configData.config.settings?.alarmsTitle, []], + titleFont: [configData.config.titleFont, []], + titleColor: [configData.config.titleColor, []], + showTitleIcon: [configData.config.showTitleIcon, []], + titleIcon: [configData.config.titleIcon, []], + iconColor: [configData.config.iconColor, []], + + displayActivity: [configData.config.settings?.displayActivity, []], + displayDetails: [configData.config.settings?.displayDetails, []], + allowAssign: [configData.config.settings?.allowAssign, []], + allowAcknowledgment: [configData.config.settings?.allowAcknowledgment, []], + allowClear: [configData.config.settings?.allowClear, []], + + cardButtons: [this.getCardButtons(configData.config), []], + color: [configData.config.color, []], + backgroundColor: [configData.config.backgroundColor, []], + actions: [configData.config.actions || {}, []] + }); + } + + protected prepareOutputConfig(config: any): WidgetConfigComponentData { + setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig); + this.widgetConfig.config.alarmFilterConfig = config.alarmFilterConfig; + this.widgetConfig.config.alarmSource = config.datasources[0]; + this.setColumns(config.columns, this.widgetConfig.config.alarmSource); + this.widgetConfig.config.actions = config.actions; + this.widgetConfig.config.showTitle = config.showTitle; + this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + + this.widgetConfig.config.settings.alarmsTitle = config.title; + this.widgetConfig.config.settings.displayActivity = config.displayActivity; + this.widgetConfig.config.settings.displayDetails = config.displayDetails; + this.widgetConfig.config.settings.allowAssign = config.allowAssign; + this.widgetConfig.config.settings.allowAcknowledgment = config.allowAcknowledgment; + this.widgetConfig.config.settings.allowClear = config.allowClear; + + this.widgetConfig.config.titleFont = config.titleFont; + this.widgetConfig.config.titleColor = config.titleColor; + this.widgetConfig.config.showTitleIcon = config.showTitleIcon; + this.widgetConfig.config.titleIcon = config.titleIcon; + this.widgetConfig.config.iconColor = config.iconColor; + this.setCardButtons(config.cardButtons, this.widgetConfig.config); + this.widgetConfig.config.color = config.color; + this.widgetConfig.config.backgroundColor = config.backgroundColor; + return this.widgetConfig; + } + + protected validatorTriggers(): string[] { + return ['showTitle', 'showTitleIcon']; + } + + protected updateValidators(emitEvent: boolean, trigger?: string) { + const showTitle: boolean = this.alarmsTableWidgetConfigForm.get('showTitle').value; + const showTitleIcon: boolean = this.alarmsTableWidgetConfigForm.get('showTitleIcon').value; + if (showTitle) { + this.alarmsTableWidgetConfigForm.get('title').enable(); + this.alarmsTableWidgetConfigForm.get('titleFont').enable(); + this.alarmsTableWidgetConfigForm.get('titleColor').enable(); + this.alarmsTableWidgetConfigForm.get('showTitleIcon').enable({emitEvent: false}); + if (showTitleIcon) { + this.alarmsTableWidgetConfigForm.get('titleIcon').enable(); + this.alarmsTableWidgetConfigForm.get('iconColor').enable(); + } else { + this.alarmsTableWidgetConfigForm.get('titleIcon').disable(); + this.alarmsTableWidgetConfigForm.get('iconColor').disable(); + } + } else { + this.alarmsTableWidgetConfigForm.get('title').disable(); + this.alarmsTableWidgetConfigForm.get('titleFont').disable(); + this.alarmsTableWidgetConfigForm.get('titleColor').disable(); + this.alarmsTableWidgetConfigForm.get('showTitleIcon').disable({emitEvent: false}); + this.alarmsTableWidgetConfigForm.get('titleIcon').disable(); + this.alarmsTableWidgetConfigForm.get('iconColor').disable(); + } + this.alarmsTableWidgetConfigForm.get('title').updateValueAndValidity({emitEvent}); + this.alarmsTableWidgetConfigForm.get('titleFont').updateValueAndValidity({emitEvent}); + this.alarmsTableWidgetConfigForm.get('titleColor').updateValueAndValidity({emitEvent}); + this.alarmsTableWidgetConfigForm.get('showTitleIcon').updateValueAndValidity({emitEvent: false}); + this.alarmsTableWidgetConfigForm.get('titleIcon').updateValueAndValidity({emitEvent}); + this.alarmsTableWidgetConfigForm.get('iconColor').updateValueAndValidity({emitEvent}); + } + + private getColumns(alarmSource?: Datasource): DataKey[] { + if (alarmSource) { + return alarmSource.dataKeys || []; + } + return []; + } + + private setColumns(columns: DataKey[], alarmSource?: Datasource) { + if (alarmSource) { + alarmSource.dataKeys = columns; + } + } + + private getCardButtons(config: WidgetConfig): string[] { + const buttons: string[] = []; + if (isUndefined(config.settings?.enableSearch) || config.settings?.enableSearch) { + buttons.push('search'); + } + if (isUndefined(config.settings?.enableFilter) || config.settings?.enableFilter) { + buttons.push('filter'); + } + if (isUndefined(config.settings?.enableSelectColumnDisplay) || config.settings?.enableSelectColumnDisplay) { + buttons.push('columnsToDisplay'); + } + if (isUndefined(config.enableFullscreen) || config.enableFullscreen) { + buttons.push('fullscreen'); + } + return buttons; + } + + private setCardButtons(buttons: string[], config: WidgetConfig) { + config.settings.enableSearch = buttons.includes('search'); + config.settings.enableFilter = buttons.includes('filter'); + config.settings.enableSelectColumnDisplay = buttons.includes('columnsToDisplay'); + config.enableFullscreen = buttons.includes('fullscreen'); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-config.scss b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-config.scss index d29594fce3..0f88b8f1dc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-config.scss +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-config.scss @@ -13,8 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +@import '../../../../../../../scss/constants'; + :host { display: flex; flex-direction: column; gap: 16px; + @media #{$mat-xs} { + gap: 8px; + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts index 73ebfb37c7..5e95d162cd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/basic-widget-config.module.ts @@ -27,17 +27,95 @@ import { } from '@home/components/widget/config/basic/common/widget-actions-panel.component'; import { EntitiesTableBasicConfigComponent -} from '@home/components/widget/config/basic/cards/entities-table-basic-config.component'; +} from '@home/components/widget/config/basic/entity/entities-table-basic-config.component'; import { DataKeysPanelComponent } from '@home/components/widget/config/basic/common/data-keys-panel.component'; import { DataKeyRowComponent } from '@home/components/widget/config/basic/common/data-key-row.component'; +import { + TimeseriesTableBasicConfigComponent +} from '@home/components/widget/config/basic/cards/timeseries-table-basic-config.component'; +import { FlotBasicConfigComponent } from '@home/components/widget/config/basic/chart/flot-basic-config.component'; +import { + AlarmsTableBasicConfigComponent +} from '@home/components/widget/config/basic/alarm/alarms-table-basic-config.component'; +import { + ValueCardBasicConfigComponent +} from '@home/components/widget/config/basic/cards/value-card-basic-config.component'; +import { + AggregatedValueCardBasicConfigComponent +} from '@home/components/widget/config/basic/cards/aggregated-value-card-basic-config.component'; +import { + AggregatedDataKeyRowComponent +} from '@home/components/widget/config/basic/cards/aggregated-data-key-row.component'; +import { + AggregatedDataKeysPanelComponent +} from '@home/components/widget/config/basic/cards/aggregated-data-keys-panel.component'; +import { + AlarmCountBasicConfigComponent +} from '@home/components/widget/config/basic/alarm/alarm-count-basic-config.component'; +import { + EntityCountBasicConfigComponent +} from '@home/components/widget/config/basic/entity/entity-count-basic-config.component'; +import { + BatteryLevelBasicConfigComponent +} from '@home/components/widget/config/basic/indicator/battery-level-basic-config.component'; +import { + WindSpeedDirectionBasicConfigComponent +} from '@home/components/widget/config/basic/weather/wind-speed-direction-basic-config.component'; +import { + SignalStrengthBasicConfigComponent +} from '@home/components/widget/config/basic/indicator/signal-strength-basic-config.component'; +import { + ValueChartCardBasicConfigComponent +} from '@home/components/widget/config/basic/cards/value-chart-card-basic-config.component'; +import { + ProgressBarBasicConfigComponent +} from '@home/components/widget/config/basic/cards/progress-bar-basic-config.component'; +import { + RadialGaugeBasicConfigComponent +} from '@home/components/widget/config/basic/gauge/radial-gauge-basic-config.component'; +import { + ThermometerScaleGaugeBasicConfigComponent +} from '@home/components/widget/config/basic/gauge/thermometer-scale-gauge-basic-config.component'; +import { + CompassGaugeBasicConfigComponent +} from '@home/components/widget/config/basic/gauge/compass-gauge-basic-config.component'; +import { + LiquidLevelCardBasicConfigComponent +} from '@home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component'; +import { + DoughnutBasicConfigComponent +} from '@home/components/widget/config/basic/chart/doughnut-basic-config.component'; +import { + RangeChartBasicConfigComponent +} from '@home/components/widget/config/basic/chart/range-chart-basic-config.component'; @NgModule({ declarations: [ WidgetActionsPanelComponent, SimpleCardBasicConfigComponent, EntitiesTableBasicConfigComponent, + TimeseriesTableBasicConfigComponent, + FlotBasicConfigComponent, + AlarmsTableBasicConfigComponent, + ValueCardBasicConfigComponent, + AggregatedValueCardBasicConfigComponent, + AggregatedDataKeyRowComponent, + AggregatedDataKeysPanelComponent, DataKeyRowComponent, - DataKeysPanelComponent + DataKeysPanelComponent, + AlarmCountBasicConfigComponent, + EntityCountBasicConfigComponent, + BatteryLevelBasicConfigComponent, + WindSpeedDirectionBasicConfigComponent, + SignalStrengthBasicConfigComponent, + ValueChartCardBasicConfigComponent, + ProgressBarBasicConfigComponent, + RadialGaugeBasicConfigComponent, + ThermometerScaleGaugeBasicConfigComponent, + CompassGaugeBasicConfigComponent, + LiquidLevelCardBasicConfigComponent, + DoughnutBasicConfigComponent, + RangeChartBasicConfigComponent ], imports: [ CommonModule, @@ -48,8 +126,28 @@ import { DataKeyRowComponent } from '@home/components/widget/config/basic/common WidgetActionsPanelComponent, SimpleCardBasicConfigComponent, EntitiesTableBasicConfigComponent, + TimeseriesTableBasicConfigComponent, + FlotBasicConfigComponent, + AlarmsTableBasicConfigComponent, + ValueCardBasicConfigComponent, + AggregatedValueCardBasicConfigComponent, + AggregatedDataKeyRowComponent, + AggregatedDataKeysPanelComponent, DataKeyRowComponent, - DataKeysPanelComponent + DataKeysPanelComponent, + AlarmCountBasicConfigComponent, + EntityCountBasicConfigComponent, + BatteryLevelBasicConfigComponent, + WindSpeedDirectionBasicConfigComponent, + SignalStrengthBasicConfigComponent, + ValueChartCardBasicConfigComponent, + ProgressBarBasicConfigComponent, + RadialGaugeBasicConfigComponent, + ThermometerScaleGaugeBasicConfigComponent, + CompassGaugeBasicConfigComponent, + LiquidLevelCardBasicConfigComponent, + DoughnutBasicConfigComponent, + RangeChartBasicConfigComponent ] }) export class BasicWidgetConfigModule { @@ -57,5 +155,23 @@ export class BasicWidgetConfigModule { export const basicWidgetConfigComponentsMap: {[key: string]: Type} = { 'tb-simple-card-basic-config': SimpleCardBasicConfigComponent, - 'tb-entities-table-basic-config': EntitiesTableBasicConfigComponent + 'tb-entities-table-basic-config': EntitiesTableBasicConfigComponent, + 'tb-timeseries-table-basic-config': TimeseriesTableBasicConfigComponent, + 'tb-flot-basic-config': FlotBasicConfigComponent, + 'tb-alarms-table-basic-config': AlarmsTableBasicConfigComponent, + 'tb-value-card-basic-config': ValueCardBasicConfigComponent, + 'tb-aggregated-value-card-basic-config': AggregatedValueCardBasicConfigComponent, + 'tb-alarm-count-basic-config': AlarmCountBasicConfigComponent, + 'tb-entity-count-basic-config': EntityCountBasicConfigComponent, + 'tb-battery-level-basic-config': BatteryLevelBasicConfigComponent, + 'tb-wind-speed-direction-basic-config': WindSpeedDirectionBasicConfigComponent, + 'tb-signal-strength-basic-config': SignalStrengthBasicConfigComponent, + 'tb-value-chart-card-basic-config': ValueChartCardBasicConfigComponent, + 'tb-progress-bar-basic-config': ProgressBarBasicConfigComponent, + 'tb-radial-gauge-basic-config': RadialGaugeBasicConfigComponent, + 'tb-thermometer-scale-gauge-basic-config': ThermometerScaleGaugeBasicConfigComponent, + 'tb-compass-gauge-basic-config': CompassGaugeBasicConfigComponent, + 'tb-liquid-level-card-basic-config': LiquidLevelCardBasicConfigComponent, + 'tb-doughnut-basic-config': DoughnutBasicConfigComponent, + 'tb-range-chart-basic-config': RangeChartBasicConfigComponent }; diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.html new file mode 100644 index 0000000000..2c8c78f3ce --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.html @@ -0,0 +1,97 @@ + +
+ + + + {{ aggregatedValueCardKeyPositionTranslationMap.get(position) | translate }} + + + + + + +
+
+
+ +
+
+ +
+
+
+ +
+
+ + +
+
+ + + +
+
+ + +
+
+ + +
+
+ +
+
+ +
+
+ + + f() + + + + + + + + {{ (modelValue?.aggregationType || aggregationTypes.NONE) }} + ({{ 'datakey.latest-value' | translate }}) + + + ({{ 'datakey.delta' | translate }}:{{ (modelValue?.comparisonResultType === comparisonResultTypes.DELTA_PERCENT ? 'datakey.percent' : 'datakey.absolute') | translate }}) + ({{ 'datakey.delta-calculation-result-previous-value' | translate }}) + + diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.scss b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.scss new file mode 100644 index 0000000000..ce12a6fde4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.scss @@ -0,0 +1,87 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import '../../../../../../../../scss/constants'; + +.tb-aggregated-data-key-row { + .mat-mdc-form-field.tb-inline-field.tb-aggregation-field { + .mat-mdc-text-field-wrapper:not(.mdc-text-field--outlined) { + padding-left: 8px; + padding-right: 0; + .mat-mdc-form-field-infix { + padding-top: 0; + padding-bottom: 6px; + .mdc-evolution-chip-set .mdc-evolution-chip { + margin: 0; + } + input.mat-mdc-chip-input { + height: 32px; + margin-left: 0; + } + } + } + .mat-mdc-chip.mat-mdc-standard-chip.tb-datakey-chip { + .tb-attribute-chip { + .tb-chip-labels { + background: transparent; + } + } + } + } + + .tb-position-field { + width: 132px; + min-width: 132px; + } + + .tb-aggregation-field { + flex: 1; + min-width: 120px; + } + + .tb-units-field, .tb-decimals-field, .tb-font-field, .tb-color-field, tb-arrow-field { + display: flex; + flex-direction: row; + place-content: center; + align-items: center; + } + + .tb-units-field { + width: 80px; + min-width: 80px; + } + + .tb-decimals-field { + width: 60px; + min-width: 60px; + } + + .tb-font-field, .tb-color-field, .tb-arrow-field { + width: 40px; + min-width: 40px; + display: none; + @media #{$mat-gt-xs} { + display: block; + } + } + + .tb-units-field, .tb-decimals-field { + display: none; + @media #{$mat-gt-sm} { + display: block; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts new file mode 100644 index 0000000000..0f7bfe648e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts @@ -0,0 +1,253 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + ChangeDetectorRef, + Component, + EventEmitter, + forwardRef, + Input, + OnChanges, + OnInit, + Output, + SimpleChanges, + ViewEncapsulation +} from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { + ComparisonResultType, + DataKey, + DataKeyConfigMode, + DatasourceType, JsonSettingsSchema, Widget, + widgetType +} from '@shared/models/widget.models'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { AggregationType } from '@shared/models/time/time.models'; +import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { TranslateService } from '@ngx-translate/core'; +import { TruncatePipe } from '@shared/pipe/truncate.pipe'; +import { + DataKeyConfigDialogComponent, + DataKeyConfigDialogData +} from '@home/components/widget/config/data-key-config-dialog.component'; +import { deepClone, formatValue } from '@core/utils'; +import { + AggregatedValueCardKeyPosition, + aggregatedValueCardKeyPositionTranslations, + AggregatedValueCardKeySettings +} from '@home/components/widget/lib/cards/aggregated-value-card.models'; + +@Component({ + selector: 'tb-aggregated-data-key-row', + templateUrl: './aggregated-data-key-row.component.html', + styleUrls: ['./aggregated-data-key-row.component.scss', '../../data-keys.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => AggregatedDataKeyRowComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class AggregatedDataKeyRowComponent implements ControlValueAccessor, OnInit, OnChanges { + + aggregatedValueCardKeyPositions: AggregatedValueCardKeyPosition[] = + Object.keys(AggregatedValueCardKeyPosition).map(value => AggregatedValueCardKeyPosition[value]); + + aggregatedValueCardKeyPositionTranslationMap = aggregatedValueCardKeyPositionTranslations; + + dataKeyTypes = DataKeyType; + + aggregationTypes = AggregationType; + + comparisonResultTypes = ComparisonResultType; + + @Input() + disabled: boolean; + + @Input() + datasourceType: DatasourceType; + + @Input() + keyName: string; + + @Input() + index: number; + + @Output() + keyRemoved = new EventEmitter(); + + keyRowFormGroup: UntypedFormGroup; + + modelValue: DataKey; + + valuePreviewFn = this._valuePreviewFn.bind(this); + + get callbacks(): DataKeysCallbacks { + return this.widgetConfigComponent.widgetConfigCallbacks; + } + + get widget(): Widget { + return this.widgetConfigComponent.widget; + } + + get latestDataKeySettingsSchema(): JsonSettingsSchema { + return this.widgetConfigComponent.modelValue?.latestDataKeySettingsSchema; + } + + get latestDataKeySettingsDirective(): string { + return this.widgetConfigComponent.modelValue?.latestDataKeySettingsDirective; + } + + get isEntityDatasource(): boolean { + return [DatasourceType.device, DatasourceType.entity].includes(this.datasourceType); + } + + private propagateChange = (_val: any) => {}; + + constructor(private fb: UntypedFormBuilder, + private dialog: MatDialog, + private cd: ChangeDetectorRef, + public translate: TranslateService, + public truncate: TruncatePipe, + private widgetConfigComponent: WidgetConfigComponent) { + } + + ngOnInit() { + this.keyRowFormGroup = this.fb.group({ + position: [null, []], + units: [null, []], + decimals: [null, []], + font: [null, []], + color: [null, []], + showArrow: [null, []] + }); + this.keyRowFormGroup.valueChanges.subscribe( + () => this.updateModel() + ); + } + + ngOnChanges(changes: SimpleChanges): void { + for (const propName of Object.keys(changes)) { + const change = changes[propName]; + if (!change.firstChange && change.currentValue !== change.previousValue) { + if (['keyName'].includes(propName)) { + if (change.currentValue) { + this.modelValue.name = change.currentValue; + setTimeout(() => { + this.updateModel(); + }, 0); + } + } + } + } + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.keyRowFormGroup.disable({emitEvent: false}); + } else { + this.keyRowFormGroup.enable({emitEvent: false}); + } + } + + writeValue(value: DataKey): void { + this.modelValue = value || {} as DataKey; + const settings: AggregatedValueCardKeySettings = (this.modelValue.settings || {}); + this.keyRowFormGroup.patchValue( + { + position: settings.position || AggregatedValueCardKeyPosition.center, + units: value?.units, + decimals: value?.decimals, + font: settings.font, + color: settings.color, + showArrow: settings.showArrow + }, {emitEvent: false} + ); + this.cd.markForCheck(); + } + + dataKeyHasPostprocessing(): boolean { + return !!this.modelValue?.postFuncBody; + } + + editKey() { + this.dialog.open(DataKeyConfigDialogComponent, + { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + dataKey: deepClone(this.modelValue), + dataKeyConfigMode: DataKeyConfigMode.general, + dataKeySettingsSchema: this.latestDataKeySettingsSchema, + dataKeySettingsDirective: this.latestDataKeySettingsDirective, + dashboard: null, + aliasController: null, + widget: this.widget, + widgetType: widgetType.latest, + deviceId: null, + entityAliasId: null, + showPostProcessing: true, + callbacks: this.callbacks, + hideDataKeyName: true, + hideDataKeyLabel: true, + hideDataKeyColor: true + } + }).afterClosed().subscribe((updatedDataKey) => { + if (updatedDataKey) { + this.modelValue = updatedDataKey; + const settings: AggregatedValueCardKeySettings = (this.modelValue.settings || {}); + this.keyRowFormGroup.get('position').patchValue(settings.position, {emitEvent: false}); + this.keyRowFormGroup.get('font').patchValue(settings.font, {emitEvent: false}); + this.keyRowFormGroup.get('color').patchValue(settings.color, {emitEvent: false}); + this.keyRowFormGroup.get('showArrow').patchValue(settings.showArrow, {emitEvent: false}); + this.keyRowFormGroup.get('units').patchValue(this.modelValue.units, {emitEvent: false}); + this.keyRowFormGroup.get('decimals').patchValue(this.modelValue.decimals, {emitEvent: false}); + this.updateModel(); + this.cd.markForCheck(); + } + }); + } + + private updateModel() { + const value = this.keyRowFormGroup.value; + this.modelValue.settings = this.modelValue.settings || {}; + this.modelValue.settings.position = value.position; + this.modelValue.settings.font = value.font; + this.modelValue.settings.color = value.color; + this.modelValue.settings.showArrow = value.showArrow; + this.modelValue.units = value.units; + this.modelValue.decimals = value.decimals; + this.propagateChange(this.modelValue); + } + + private _valuePreviewFn(): string { + const units: string = this.keyRowFormGroup.get('units').value; + const decimals: number = this.keyRowFormGroup.get('decimals').value; + return formatValue(22, decimals, units, true); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-keys-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-keys-panel.component.html new file mode 100644 index 0000000000..f8503b3c4d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-keys-panel.component.html @@ -0,0 +1,52 @@ + +
+
{{ 'widgets.aggregated-value-card.values' | translate }}
+
+
+
widgets.aggregated-value-card.position
+
widgets.aggregated-value-card.aggregation
+
widget-config.units-short
+
widget-config.decimals-short
+
widgets.aggregated-value-card.font
+
widgets.aggregated-value-card.color
+
widgets.aggregated-value-card.arrow
+
+
+
+
+ + +
+
+
+
+ +
+
+ + {{ 'widgets.aggregated-value-card.no-values' | translate }} + diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-keys-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-keys-panel.component.scss new file mode 100644 index 0000000000..6ce4e5b9b5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-keys-panel.component.scss @@ -0,0 +1,68 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import '../../../../../../../../scss/constants'; + +.tb-aggregated-data-keys-panel { + .tb-form-table-header-cell { + &.tb-position-header { + width: 132px; + min-width: 132px; + } + + &.tb-aggregation-header { + flex: 1; + min-width: 120px; + } + + &.tb-units-header { + width: 80px; + min-width: 80px; + } + + &.tb-decimals-header { + width: 60px; + min-width: 60px; + } + + &.tb-font-header, &.tb-color-header, &.tb-arrow-header { + width: 40px; + min-width: 40px; + display: none; + @media #{$mat-gt-xs} { + display: block; + } + } + + &.tb-actions-header { + width: 40px; + min-width: 40px; + } + + &.tb-units-header, &.tb-decimals-header { + display: none; + @media #{$mat-gt-sm} { + display: block; + } + } + } + .tb-form-table-body { + tb-aggregated-data-key-row { + overflow: hidden; + } + } +} + diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-keys-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-keys-panel.component.ts new file mode 100644 index 0000000000..d8b7f07f92 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-keys-panel.component.ts @@ -0,0 +1,173 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + ChangeDetectorRef, + Component, + forwardRef, + Input, + OnChanges, + OnInit, + SimpleChanges, + ViewEncapsulation +} from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormArray, + UntypedFormBuilder, + UntypedFormGroup +} from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { DataKey, DatasourceType, widgetType } from '@shared/models/widget.models'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { UtilsService } from '@core/services/utils.service'; +import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { aggregatedValueCardDefaultKeySettings } from '@home/components/widget/lib/cards/aggregated-value-card.models'; + +@Component({ + selector: 'tb-aggregated-data-keys-panel', + templateUrl: './aggregated-data-keys-panel.component.html', + styleUrls: ['./aggregated-data-keys-panel.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => AggregatedDataKeysPanelComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class AggregatedDataKeysPanelComponent implements ControlValueAccessor, OnInit, OnChanges { + + @Input() + disabled: boolean; + + @Input() + datasourceType: DatasourceType; + + @Input() + keyName: string; + + dataKeyType: DataKeyType; + + keysListFormGroup: UntypedFormGroup; + + get widgetType(): widgetType { + return this.widgetConfigComponent.widgetType; + } + + get callbacks(): DataKeysCallbacks { + return this.widgetConfigComponent.widgetConfigCallbacks; + } + + get noKeys(): boolean { + const keys: DataKey[] = this.keysListFormGroup.get('keys').value; + return keys.length === 0; + } + + private propagateChange = (_val: any) => {}; + + constructor(private fb: UntypedFormBuilder, + private dialog: MatDialog, + private cd: ChangeDetectorRef, + private utils: UtilsService, + private widgetConfigComponent: WidgetConfigComponent) { + } + + ngOnInit() { + this.keysListFormGroup = this.fb.group({ + keys: [this.fb.array([]), []] + }); + this.keysListFormGroup.valueChanges.subscribe( + (val) => this.propagateChange(this.keysListFormGroup.get('keys').value) + ); + this.updateParams(); + } + + ngOnChanges(changes: SimpleChanges): void { + for (const propName of Object.keys(changes)) { + const change = changes[propName]; + if (!change.firstChange && change.currentValue !== change.previousValue) { + if (['datasourceType'].includes(propName)) { + this.updateParams(); + } + } + } + } + + private updateParams() { + if (this.datasourceType === DatasourceType.function) { + this.dataKeyType = DataKeyType.function; + } else { + this.dataKeyType = DataKeyType.timeseries; + } + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.keysListFormGroup.disable({emitEvent: false}); + } else { + this.keysListFormGroup.enable({emitEvent: false}); + } + } + + writeValue(value: DataKey[] | undefined): void { + this.keysListFormGroup.setControl('keys', this.prepareKeysFormArray(value), {emitEvent: false}); + } + + keysFormArray(): UntypedFormArray { + return this.keysListFormGroup.get('keys') as UntypedFormArray; + } + + trackByKey(index: number, keyControl: AbstractControl): any { + return keyControl; + } + + removeKey(index: number) { + (this.keysListFormGroup.get('keys') as UntypedFormArray).removeAt(index); + } + + addKey() { + const dataKey = this.callbacks.generateDataKey(this.keyName, this.dataKeyType, null); + dataKey.decimals = 0; + dataKey.settings = {...aggregatedValueCardDefaultKeySettings}; + const keysArray = this.keysListFormGroup.get('keys') as UntypedFormArray; + const keyControl = this.fb.control(dataKey, []); + keysArray.push(keyControl); + } + + private prepareKeysFormArray(keys: DataKey[] | undefined): UntypedFormArray { + const keysControls: Array = []; + if (keys) { + keys.forEach((key) => { + keysControls.push(this.fb.control(key, [])); + }); + } + return this.fb.array(keysControls); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-value-card-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-value-card-basic-config.component.html new file mode 100644 index 0000000000..99508e5139 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-value-card-basic-config.component.html @@ -0,0 +1,154 @@ + + + + + + +
+
widget-config.appearance
+
+ + {{ 'widgets.aggregated-value-card.auto-scale' | translate }} + +
+
+ + {{ 'widget-config.title' | translate }} + +
+ + + + + + + +
+
+
+ + {{ 'widgets.value-card.icon' | translate }} + +
+ + + + + + + + +
+
+
+ + {{ 'widgets.aggregated-value-card.subtitle' | translate }} + +
+ + + + + + + +
+
+
+ + {{ 'widgets.value-card.date' | translate }} + +
+ + + + + +
+
+
+ + {{ 'widgets.aggregated-value-card.chart' | translate }} + +
+ + + +
widget-config.decimals-suffix
+
+ + +
+
+
+ + +
+
widget-config.card-appearance
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
widget-config.show-card-buttons
+ + {{ 'fullscreen.fullscreen' | translate }} + +
+
+
{{ 'widget-config.card-border-radius' | translate }}
+ + + +
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-value-card-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-value-card-basic-config.component.ts new file mode 100644 index 0000000000..1956526ca0 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-value-card-basic-config.component.ts @@ -0,0 +1,410 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ChangeDetectorRef, Component, Injector } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { ComparisonResultType, DataKey, Datasource, WidgetConfig, } from '@shared/models/widget.models'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { + getTimewindowConfig, + setTimewindowConfig +} from '@home/components/widget/config/timewindow-config-panel.component'; +import { isUndefined } from '@core/utils'; +import { + ColorType, + constantColor, + cssSizeToStrSize, + DateFormatProcessor, + DateFormatSettings, + getDataKey, + resolveCssSize +} from '@shared/models/widget-settings.models'; +import { + aggregatedValueCardDefaultSettings, + AggregatedValueCardKeyPosition, + AggregatedValueCardKeySettings, + AggregatedValueCardWidgetSettings +} from '@home/components/widget/lib/cards/aggregated-value-card.models'; +import { + AggregationType, + HistoryWindowType, + HOUR, + QuickTimeInterval, + TimewindowType +} from '@shared/models/time/time.models'; + +@Component({ + selector: 'tb-aggregated-value-card-basic-config', + templateUrl: './aggregated-value-card-basic-config.component.html', + styleUrls: ['../basic-config.scss'] +}) +export class AggregatedValueCardBasicConfigComponent extends BasicWidgetConfigComponent { + + public get datasource(): Datasource { + const datasources: Datasource[] = this.aggregatedValueCardWidgetConfigForm.get('datasources').value; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + + public get keyName(): string { + const dataKey = getDataKey(this.aggregatedValueCardWidgetConfigForm.get('datasources').value); + if (dataKey) { + return dataKey.name; + } else { + return null; + } + } + + aggregatedValueCardWidgetConfigForm: UntypedFormGroup; + + datePreviewFn = this._datePreviewFn.bind(this); + + constructor(protected store: Store, + protected widgetConfigComponent: WidgetConfigComponent, + private cd: ChangeDetectorRef, + private $injector: Injector, + private fb: UntypedFormBuilder) { + super(store, widgetConfigComponent); + } + + protected configForm(): UntypedFormGroup { + return this.aggregatedValueCardWidgetConfigForm; + } + + protected setupDefaults(configData: WidgetConfigComponentData) { + super.setupDefaults(configData); + configData.config.useDashboardTimewindow = false; + configData.config.displayTimewindow = true; + configData.config.timewindow = { + selectedTab: TimewindowType.HISTORY, + history: { + historyType: HistoryWindowType.INTERVAL, + quickInterval: QuickTimeInterval.CURRENT_MONTH_SO_FAR, + }, + aggregation: { + type: AggregationType.AVG, + interval: 12 * HOUR, + limit: 5000 + } + }; + } + + protected defaultDataKeys(configData: WidgetConfigComponentData): DataKey[] { + return [ + { name: 'watermeter', label: 'Watermeter', type: DataKeyType.timeseries, color: 'rgba(0, 0, 0, 0.87)', units: 'm³', decimals: 0 } + ]; + } + + protected defaultLatestDataKeys(configData: WidgetConfigComponentData): DataKey[] { + return this.createDefaultAggregatedValueLatestDataKeys(configData, 'watermeter', 'm³', 0); + } + + createDefaultAggregatedValueLatestDataKeys(configData: WidgetConfigComponentData, keyName: string, + units: string, decimals: number): DataKey[] { + let centerKeySettings: AggregatedValueCardKeySettings = { + position: AggregatedValueCardKeyPosition.center, + font: { + family: 'Roboto', + size: 52, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '1' + }, + color: constantColor('rgba(0, 0, 0, 0.87)'), + showArrow: false + }; + let rightTopKeySettings: AggregatedValueCardKeySettings = { + position: AggregatedValueCardKeyPosition.rightTop, + font: { + family: 'Roboto', + size: 14, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '1' + }, + color: { + color: 'rgba(0, 0, 0, 0.87)', + type: ColorType.range, + rangeList: [ + {to: 0, color: '#198038'}, + {from: 0, to: 0, color: 'rgba(0, 0, 0, 0.87)'}, + {from: 0, color: '#D12730'} + ], + colorFunction: '' + }, + showArrow: true + }; + let rightBottomKeySettings: AggregatedValueCardKeySettings = { + position: AggregatedValueCardKeyPosition.rightBottom, + font: { + family: 'Roboto', + size: 11, + sizeUnit: 'px', + style: 'normal', + weight: '400', + lineHeight: '1' + }, + color: constantColor('rgba(0, 0, 0, 0.38)'), + showArrow: false + }; + const datasources: Datasource[] = configData.config.datasources; + if (datasources?.length) { + const latestDataKeys = datasources[0].latestDataKeys; + if (latestDataKeys) { + const center = latestDataKeys.find(dataKey => dataKey.settings?.position === AggregatedValueCardKeyPosition.center); + if (center) { + centerKeySettings = {...centerKeySettings, ...center.settings}; + } + const rightTop = latestDataKeys.find(dataKey => dataKey.settings?.position === AggregatedValueCardKeyPosition.rightTop); + if (rightTop) { + rightTopKeySettings = {...rightTopKeySettings, ...rightTop.settings}; + } + const rightBottom = latestDataKeys.find(dataKey => dataKey.settings?.position === AggregatedValueCardKeyPosition.rightBottom); + if (rightBottom) { + rightBottomKeySettings = {...rightBottomKeySettings, ...rightBottom.settings}; + } + } + } + return [ + { + name: keyName, label: 'Latest', type: DataKeyType.timeseries, units, decimals, + aggregationType: AggregationType.NONE, + settings: centerKeySettings + }, + { + name: keyName, label: 'Delta percent', type: DataKeyType.timeseries, units: '%', decimals: 0, + aggregationType: AggregationType.AVG, + comparisonEnabled: true, + timeForComparison: 'previousInterval', + comparisonResultType: ComparisonResultType.DELTA_PERCENT, + settings: rightTopKeySettings + }, + { + name: keyName, label: 'Delta absolute', type: DataKeyType.timeseries, units, decimals: 1, + aggregationType: AggregationType.AVG, + comparisonEnabled: true, + timeForComparison: 'previousInterval', + comparisonResultType: ComparisonResultType.DELTA_ABSOLUTE, + settings: rightBottomKeySettings + } + ]; + } + + protected onConfigSet(configData: WidgetConfigComponentData) { + const settings: AggregatedValueCardWidgetSettings = {...aggregatedValueCardDefaultSettings, ...(configData.config.settings || {})}; + const dataKey = getDataKey(configData.config.datasources); + const keyName = dataKey?.name; + const iconSize = resolveCssSize(configData.config.iconSize); + this.aggregatedValueCardWidgetConfigForm = this.fb.group({ + timewindowConfig: [getTimewindowConfig(configData.config), []], + datasources: [configData.config.datasources, []], + + autoScale: [settings.autoScale, []], + showTitle: [configData.config.showTitle, []], + title: [configData.config.title, []], + titleFont: [configData.config.titleFont, []], + titleColor: [configData.config.titleColor, []], + + showIcon: [configData.config.showTitleIcon, []], + iconSize: [iconSize[0], [Validators.min(0)]], + iconSizeUnit: [iconSize[1], []], + icon: [configData.config.titleIcon, []], + iconColor: [configData.config.iconColor, []], + + showSubtitle: [settings.showSubtitle, []], + subtitle: [settings.subtitle, []], + subtitleFont: [settings.subtitleFont, []], + subtitleColor: [settings.subtitleColor, []], + + showDate: [settings.showDate, []], + dateFormat: [settings.dateFormat, []], + dateFont: [settings.dateFont, []], + dateColor: [settings.dateColor, []], + + showChart: [settings.showChart, []], + chartUnits: [dataKey?.units, []], + chartDecimals: [dataKey?.decimals, []], + chartColor: [dataKey?.color, []], + + values: [this.getValues(configData.config.datasources, keyName), []], + + background: [settings.background, []], + + cardButtons: [this.getCardButtons(configData.config), []], + borderRadius: [configData.config.borderRadius, []], + + actions: [configData.config.actions || {}, []] + }); + } + + protected prepareOutputConfig(config: any): WidgetConfigComponentData { + setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig); + this.widgetConfig.config.datasources = config.datasources; + + this.widgetConfig.config.showTitle = config.showTitle; + this.widgetConfig.config.title = config.title; + this.widgetConfig.config.titleFont = config.titleFont; + this.widgetConfig.config.titleColor = config.titleColor; + + this.widgetConfig.config.showTitleIcon = config.showIcon; + this.widgetConfig.config.iconSize = cssSizeToStrSize(config.iconSize, config.iconSizeUnit); + this.widgetConfig.config.titleIcon = config.icon; + this.widgetConfig.config.iconColor = config.iconColor; + + this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + + this.widgetConfig.config.settings.autoScale = config.autoScale; + this.widgetConfig.config.settings.showSubtitle = config.showSubtitle; + this.widgetConfig.config.settings.subtitle = config.subtitle; + this.widgetConfig.config.settings.subtitleFont = config.subtitleFont; + this.widgetConfig.config.settings.subtitleColor = config.subtitleColor; + + this.widgetConfig.config.settings.showDate = config.showDate; + this.widgetConfig.config.settings.dateFormat = config.dateFormat; + this.widgetConfig.config.settings.dateFont = config.dateFont; + this.widgetConfig.config.settings.dateColor = config.dateColor; + + this.widgetConfig.config.settings.showChart = config.showChart; + + const dataKey = getDataKey(this.widgetConfig.config.datasources); + if (dataKey) { + dataKey.units = config.chartUnits; + dataKey.decimals = config.chartDecimals; + dataKey.color = config.chartColor; + } + + this.setValues(config.values, this.widgetConfig.config.datasources); + + this.widgetConfig.config.settings.background = config.background; + + this.setCardButtons(config.cardButtons, this.widgetConfig.config); + this.widgetConfig.config.borderRadius = config.borderRadius; + + this.widgetConfig.config.actions = config.actions; + return this.widgetConfig; + } + + protected validatorTriggers(): string[] { + return ['showTitle', 'showIcon', 'showSubtitle', 'showDate', 'showChart']; + } + + protected updateValidators(emitEvent: boolean, trigger?: string) { + const showTitle: boolean = this.aggregatedValueCardWidgetConfigForm.get('showTitle').value; + const showIcon: boolean = this.aggregatedValueCardWidgetConfigForm.get('showIcon').value; + const showSubtitle: boolean = this.aggregatedValueCardWidgetConfigForm.get('showSubtitle').value; + const showDate: boolean = this.aggregatedValueCardWidgetConfigForm.get('showDate').value; + const showChart: boolean = this.aggregatedValueCardWidgetConfigForm.get('showChart').value; + + if (showTitle) { + this.aggregatedValueCardWidgetConfigForm.get('title').enable(); + this.aggregatedValueCardWidgetConfigForm.get('titleFont').enable(); + this.aggregatedValueCardWidgetConfigForm.get('titleColor').enable(); + this.aggregatedValueCardWidgetConfigForm.get('showIcon').enable({emitEvent: false}); + if (showIcon) { + this.aggregatedValueCardWidgetConfigForm.get('iconSize').enable(); + this.aggregatedValueCardWidgetConfigForm.get('iconSizeUnit').enable(); + this.aggregatedValueCardWidgetConfigForm.get('icon').enable(); + this.aggregatedValueCardWidgetConfigForm.get('iconColor').enable(); + } else { + this.aggregatedValueCardWidgetConfigForm.get('iconSize').disable(); + this.aggregatedValueCardWidgetConfigForm.get('iconSizeUnit').disable(); + this.aggregatedValueCardWidgetConfigForm.get('icon').disable(); + this.aggregatedValueCardWidgetConfigForm.get('iconColor').disable(); + } + } else { + this.aggregatedValueCardWidgetConfigForm.get('title').disable(); + this.aggregatedValueCardWidgetConfigForm.get('titleFont').disable(); + this.aggregatedValueCardWidgetConfigForm.get('titleColor').disable(); + this.aggregatedValueCardWidgetConfigForm.get('showIcon').disable({emitEvent: false}); + this.aggregatedValueCardWidgetConfigForm.get('iconSize').disable(); + this.aggregatedValueCardWidgetConfigForm.get('iconSizeUnit').disable(); + this.aggregatedValueCardWidgetConfigForm.get('icon').disable(); + this.aggregatedValueCardWidgetConfigForm.get('iconColor').disable(); + } + + if (showSubtitle) { + this.aggregatedValueCardWidgetConfigForm.get('subtitle').enable(); + this.aggregatedValueCardWidgetConfigForm.get('subtitleFont').enable(); + this.aggregatedValueCardWidgetConfigForm.get('subtitleColor').enable(); + } else { + this.aggregatedValueCardWidgetConfigForm.get('subtitle').disable(); + this.aggregatedValueCardWidgetConfigForm.get('subtitleFont').disable(); + this.aggregatedValueCardWidgetConfigForm.get('subtitleColor').disable(); + } + + if (showDate) { + this.aggregatedValueCardWidgetConfigForm.get('dateFormat').enable(); + this.aggregatedValueCardWidgetConfigForm.get('dateFont').enable(); + this.aggregatedValueCardWidgetConfigForm.get('dateColor').enable(); + } else { + this.aggregatedValueCardWidgetConfigForm.get('dateFormat').disable(); + this.aggregatedValueCardWidgetConfigForm.get('dateFont').disable(); + this.aggregatedValueCardWidgetConfigForm.get('dateColor').disable(); + } + + if (showChart) { + this.aggregatedValueCardWidgetConfigForm.get('chartUnits').enable(); + this.aggregatedValueCardWidgetConfigForm.get('chartDecimals').enable(); + this.aggregatedValueCardWidgetConfigForm.get('chartColor').enable(); + } else { + this.aggregatedValueCardWidgetConfigForm.get('chartUnits').disable(); + this.aggregatedValueCardWidgetConfigForm.get('chartDecimals').disable(); + this.aggregatedValueCardWidgetConfigForm.get('chartColor').disable(); + } + } + + private getValues(datasources: Datasource[], keyName: string): DataKey[] { + if (datasources && datasources.length) { + return (datasources[0].latestDataKeys || []).filter(k => k.name === keyName); + } + return []; + } + + private setValues(values: DataKey[], datasources?: Datasource[]) { + if (datasources && datasources.length) { + datasources[0].latestDataKeys = values; + } + } + + private getCardButtons(config: WidgetConfig): string[] { + const buttons: string[] = []; + if (isUndefined(config.enableFullscreen) || config.enableFullscreen) { + buttons.push('fullscreen'); + } + return buttons; + } + + private setCardButtons(buttons: string[], config: WidgetConfig) { + config.enableFullscreen = buttons.includes('fullscreen'); + } + + private _datePreviewFn(): string { + const dateFormat: DateFormatSettings = this.aggregatedValueCardWidgetConfigForm.get('dateFormat').value; + const processor = DateFormatProcessor.fromSettings(this.$injector, dateFormat); + processor.update(Date.now()); + return processor.formatted; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.html deleted file mode 100644 index ee76bdf472..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - - - -
-
widget-config.appearance
-
- - {{ 'widget-config.card-title' | translate }} - - - - -
-
- - {{ 'widget-config.card-icon' | translate }} - -
- - - - - -
-
-
-
{{ 'widget-config.text-color' | translate }}
-
- - - -
-
-
-
{{ 'widget-config.background' | translate }}
-
- - - -
-
-
- - -
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.ts deleted file mode 100644 index 4ddd068bb4..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/entities-table-basic-config.component.ts +++ /dev/null @@ -1,154 +0,0 @@ -/// -/// Copyright © 2016-2023 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -import { Component } from '@angular/core'; -import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; -import { WidgetConfigComponentData } from '@home/models/widget-component.models'; -import { - DataKey, - Datasource, - datasourcesHasAggregation, - datasourcesHasOnlyComparisonAggregation -} from '@shared/models/widget.models'; -import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; -import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; - -@Component({ - selector: 'tb-entities-table-basic-config', - templateUrl: './entities-table-basic-config.component.html', - styleUrls: ['../basic-config.scss'] -}) -export class EntitiesTableBasicConfigComponent extends BasicWidgetConfigComponent { - - public get displayTimewindowConfig(): boolean { - const datasources = this.entitiesTableWidgetConfigForm.get('datasources').value; - return datasourcesHasAggregation(datasources); - } - - public onlyHistoryTimewindow(): boolean { - const datasources = this.entitiesTableWidgetConfigForm.get('datasources').value; - return datasourcesHasOnlyComparisonAggregation(datasources); - } - - public get datasource(): Datasource { - const datasources: Datasource[] = this.entitiesTableWidgetConfigForm.get('datasources').value; - if (datasources && datasources.length) { - return datasources[0]; - } else { - return null; - } - } - - entitiesTableWidgetConfigForm: UntypedFormGroup; - - constructor(protected store: Store, - protected widgetConfigComponent: WidgetConfigComponent, - private fb: UntypedFormBuilder) { - super(store, widgetConfigComponent); - } - - protected configForm(): UntypedFormGroup { - return this.entitiesTableWidgetConfigForm; - } - - protected setupDefaults(configData: WidgetConfigComponentData) { - this.setupDefaultDatasource(configData, [{ name: 'name', type: DataKeyType.entityField }]); - } - - protected onConfigSet(configData: WidgetConfigComponentData) { - this.entitiesTableWidgetConfigForm = this.fb.group({ - timewindowConfig: [{ - useDashboardTimewindow: configData.config.useDashboardTimewindow, - displayTimewindow: configData.config.useDashboardTimewindow, - timewindow: configData.config.timewindow - }, []], - datasources: [configData.config.datasources, []], - columns: [this.getColumns(configData.config.datasources), []], - showTitle: [configData.config.showTitle, []], - title: [configData.config.settings?.entitiesTitle, []], - showTitleIcon: [configData.config.showTitleIcon, []], - titleIcon: [configData.config.titleIcon, []], - iconColor: [configData.config.iconColor, []], - color: [configData.config.color, []], - backgroundColor: [configData.config.backgroundColor, []], - actions: [configData.config.actions || {}, []] - }); - } - - protected prepareOutputConfig(config: any): WidgetConfigComponentData { - this.widgetConfig.config.useDashboardTimewindow = config.timewindowConfig.useDashboardTimewindow; - this.widgetConfig.config.displayTimewindow = config.timewindowConfig.displayTimewindow; - this.widgetConfig.config.timewindow = config.timewindowConfig.timewindow; - this.widgetConfig.config.datasources = config.datasources; - this.setColumns(config.columns, this.widgetConfig.config.datasources); - this.widgetConfig.config.actions = config.actions; - this.widgetConfig.config.showTitle = config.showTitle; - this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; - this.widgetConfig.config.settings.entitiesTitle = config.title; - this.widgetConfig.config.showTitleIcon = config.showTitleIcon; - this.widgetConfig.config.titleIcon = config.titleIcon; - this.widgetConfig.config.iconColor = config.iconColor; - this.widgetConfig.config.color = config.color; - this.widgetConfig.config.backgroundColor = config.backgroundColor; - return this.widgetConfig; - } - - protected validatorTriggers(): string[] { - return ['showTitle', 'showTitleIcon']; - } - - protected updateValidators(emitEvent: boolean, trigger?: string) { - const showTitle: boolean = this.entitiesTableWidgetConfigForm.get('showTitle').value; - const showTitleIcon: boolean = this.entitiesTableWidgetConfigForm.get('showTitleIcon').value; - if (showTitle) { - this.entitiesTableWidgetConfigForm.get('title').enable(); - this.entitiesTableWidgetConfigForm.get('showTitleIcon').enable({emitEvent: false}); - if (showTitleIcon) { - this.entitiesTableWidgetConfigForm.get('titleIcon').enable(); - this.entitiesTableWidgetConfigForm.get('iconColor').enable(); - } else { - this.entitiesTableWidgetConfigForm.get('titleIcon').disable(); - this.entitiesTableWidgetConfigForm.get('iconColor').disable(); - } - } else { - this.entitiesTableWidgetConfigForm.get('title').disable(); - this.entitiesTableWidgetConfigForm.get('showTitleIcon').disable({emitEvent: false}); - this.entitiesTableWidgetConfigForm.get('titleIcon').disable(); - this.entitiesTableWidgetConfigForm.get('iconColor').disable(); - } - this.entitiesTableWidgetConfigForm.get('title').updateValueAndValidity({emitEvent}); - this.entitiesTableWidgetConfigForm.get('showTitleIcon').updateValueAndValidity({emitEvent: false}); - this.entitiesTableWidgetConfigForm.get('titleIcon').updateValueAndValidity({emitEvent}); - this.entitiesTableWidgetConfigForm.get('iconColor').updateValueAndValidity({emitEvent}); - } - - private getColumns(datasources?: Datasource[]): DataKey[] { - if (datasources && datasources.length) { - return datasources[0].dataKeys || []; - } - return []; - } - - private setColumns(columns: DataKey[], datasources?: Datasource[]) { - if (datasources && datasources.length) { - datasources[0].dataKeys = columns; - } - } - -} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/progress-bar-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/progress-bar-basic-config.component.html new file mode 100644 index 0000000000..06be2b26a8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/progress-bar-basic-config.component.html @@ -0,0 +1,174 @@ + + + + + + +
+
widget-config.appearance
+ + + {{ progressBarLayoutTranslationMap.get(layout) | translate }} + + +
+ + {{ 'widgets.progress-bar.auto-scale' | translate }} + +
+
+ + {{ 'widget-config.title' | translate }} + +
+ + + + + + + +
+
+
+ + {{ 'widgets.progress-bar.icon' | translate }} + +
+ + + + + + + + +
+
+
+ + {{ 'widgets.progress-bar.value' | translate }} + +
+ + + +
widget-config.decimals-suffix
+
+ + + + +
+
+
+
{{ 'widgets.progress-bar.range' | translate }}
+
+
widgets.progress-bar.min
+ + + +
widgets.progress-bar.max
+ + + +
+
+
+ + {{ 'widgets.progress-bar.range-ticks' | translate }} + +
+ + + + +
+
+
+
+
widgets.progress-bar.bar
+
+
{{ 'widgets.progress-bar.bar-color' | translate }}
+ + +
+
+
{{ 'widgets.progress-bar.bar-background' | translate }}
+ + +
+
+
+
widget-config.card-appearance
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
widget-config.show-card-buttons
+ + {{ 'fullscreen.fullscreen' | translate }} + +
+
+
{{ 'widget-config.card-border-radius' | translate }}
+ + + +
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/progress-bar-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/progress-bar-basic-config.component.ts new file mode 100644 index 0000000000..7a2a1d7291 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/progress-bar-basic-config.component.ts @@ -0,0 +1,263 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { + DataKey, + datasourcesHasAggregation, + datasourcesHasOnlyComparisonAggregation, + WidgetConfig, +} from '@shared/models/widget.models'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { + getTimewindowConfig, + setTimewindowConfig +} from '@home/components/widget/config/timewindow-config-panel.component'; +import { formatValue, isUndefined } from '@core/utils'; +import { cssSizeToStrSize, resolveCssSize } from '@shared/models/widget-settings.models'; +import { + progressBarDefaultSettings, + ProgressBarLayout, + progressBarLayoutImages, + progressBarLayouts, + progressBarLayoutTranslations, + ProgressBarWidgetSettings +} from '@home/components/widget/lib/cards/progress-bar-widget.models'; + +@Component({ + selector: 'tb-progress-bar-basic-config', + templateUrl: './progress-bar-basic-config.component.html', + styleUrls: ['../basic-config.scss'] +}) +export class ProgressBarBasicConfigComponent extends BasicWidgetConfigComponent { + + public get displayTimewindowConfig(): boolean { + const datasources = this.progressBarWidgetConfigForm.get('datasources').value; + return datasourcesHasAggregation(datasources); + } + + public onlyHistoryTimewindow(): boolean { + const datasources = this.progressBarWidgetConfigForm.get('datasources').value; + return datasourcesHasOnlyComparisonAggregation(datasources); + } + + progressBarLayout = ProgressBarLayout; + + progressBarLayouts = progressBarLayouts; + + progressBarLayoutTranslationMap = progressBarLayoutTranslations; + progressBarLayoutImageMap = progressBarLayoutImages; + + progressBarWidgetConfigForm: UntypedFormGroup; + + valuePreviewFn = this._valuePreviewFn.bind(this); + + constructor(protected store: Store, + protected widgetConfigComponent: WidgetConfigComponent, + private fb: UntypedFormBuilder) { + super(store, widgetConfigComponent); + } + + protected configForm(): UntypedFormGroup { + return this.progressBarWidgetConfigForm; + } + + protected defaultDataKeys(configData: WidgetConfigComponentData): DataKey[] { + return [{ name: 'humidity', label: 'humidity', type: DataKeyType.timeseries }]; + } + + protected onConfigSet(configData: WidgetConfigComponentData) { + const settings: ProgressBarWidgetSettings = {...progressBarDefaultSettings, ...(configData.config.settings || {})}; + const iconSize = resolveCssSize(configData.config.iconSize); + this.progressBarWidgetConfigForm = this.fb.group({ + timewindowConfig: [getTimewindowConfig(configData.config), []], + datasources: [configData.config.datasources, []], + + layout: [settings.layout, []], + autoScale: [settings.autoScale, []], + + showTitle: [configData.config.showTitle, []], + title: [configData.config.title, []], + titleFont: [configData.config.titleFont, []], + titleColor: [configData.config.titleColor, []], + + showIcon: [configData.config.showTitleIcon, []], + iconSize: [iconSize[0], [Validators.min(0)]], + iconSizeUnit: [iconSize[1], []], + icon: [configData.config.titleIcon, []], + iconColor: [configData.config.iconColor, []], + + showValue: [settings.showValue, []], + units: [configData.config.units, []], + decimals: [configData.config.decimals, []], + valueFont: [settings.valueFont, []], + valueColor: [settings.valueColor, []], + + tickMin: [settings.tickMin, []], + tickMax: [settings.tickMax, []], + + showTicks: [settings.showTicks, []], + ticksFont: [settings.ticksFont, []], + ticksColor: [settings.ticksColor, []], + + barColor: [settings.barColor, []], + barBackground: [settings.barBackground, []], + + background: [settings.background, []], + + cardButtons: [this.getCardButtons(configData.config), []], + borderRadius: [configData.config.borderRadius, []], + + actions: [configData.config.actions || {}, []] + }); + } + + protected prepareOutputConfig(config: any): WidgetConfigComponentData { + setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig); + this.widgetConfig.config.datasources = config.datasources; + + this.widgetConfig.config.showTitle = config.showTitle; + this.widgetConfig.config.title = config.title; + this.widgetConfig.config.titleFont = config.titleFont; + this.widgetConfig.config.titleColor = config.titleColor; + + this.widgetConfig.config.showTitleIcon = config.showIcon; + this.widgetConfig.config.iconSize = cssSizeToStrSize(config.iconSize, config.iconSizeUnit); + this.widgetConfig.config.titleIcon = config.icon; + this.widgetConfig.config.iconColor = config.iconColor; + + this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + + this.widgetConfig.config.settings.layout = config.layout; + this.widgetConfig.config.settings.autoScale = config.autoScale; + + this.widgetConfig.config.settings.showValue = config.showValue; + this.widgetConfig.config.units = config.units; + this.widgetConfig.config.decimals = config.decimals; + this.widgetConfig.config.settings.valueFont = config.valueFont; + this.widgetConfig.config.settings.valueColor = config.valueColor; + + this.widgetConfig.config.settings.tickMin = config.tickMin; + this.widgetConfig.config.settings.tickMax = config.tickMax; + + this.widgetConfig.config.settings.showTicks = config.showTicks; + this.widgetConfig.config.settings.ticksFont = config.ticksFont; + this.widgetConfig.config.settings.ticksColor = config.ticksColor; + + this.widgetConfig.config.settings.barColor = config.barColor; + this.widgetConfig.config.settings.barBackground = config.barBackground; + + this.widgetConfig.config.settings.background = config.background; + + this.setCardButtons(config.cardButtons, this.widgetConfig.config); + this.widgetConfig.config.borderRadius = config.borderRadius; + + this.widgetConfig.config.actions = config.actions; + return this.widgetConfig; + } + + protected validatorTriggers(): string[] { + return ['showTitle', 'showIcon', 'showValue', 'showTicks', 'layout']; + } + + protected updateValidators(emitEvent: boolean, trigger?: string) { + const showTitle: boolean = this.progressBarWidgetConfigForm.get('showTitle').value; + const showIcon: boolean = this.progressBarWidgetConfigForm.get('showIcon').value; + const showValue: boolean = this.progressBarWidgetConfigForm.get('showValue').value; + const showTicks: boolean = this.progressBarWidgetConfigForm.get('showTicks').value; + const layout: ProgressBarLayout = this.progressBarWidgetConfigForm.get('layout').value; + + const ticksEnabled = layout === ProgressBarLayout.default; + + if (showTitle) { + this.progressBarWidgetConfigForm.get('title').enable(); + this.progressBarWidgetConfigForm.get('titleFont').enable(); + this.progressBarWidgetConfigForm.get('titleColor').enable(); + this.progressBarWidgetConfigForm.get('showIcon').enable({emitEvent: false}); + if (showIcon) { + this.progressBarWidgetConfigForm.get('iconSize').enable(); + this.progressBarWidgetConfigForm.get('iconSizeUnit').enable(); + this.progressBarWidgetConfigForm.get('icon').enable(); + this.progressBarWidgetConfigForm.get('iconColor').enable(); + } else { + this.progressBarWidgetConfigForm.get('iconSize').disable(); + this.progressBarWidgetConfigForm.get('iconSizeUnit').disable(); + this.progressBarWidgetConfigForm.get('icon').disable(); + this.progressBarWidgetConfigForm.get('iconColor').disable(); + } + } else { + this.progressBarWidgetConfigForm.get('title').disable(); + this.progressBarWidgetConfigForm.get('titleFont').disable(); + this.progressBarWidgetConfigForm.get('titleColor').disable(); + this.progressBarWidgetConfigForm.get('showIcon').disable({emitEvent: false}); + this.progressBarWidgetConfigForm.get('iconSize').disable(); + this.progressBarWidgetConfigForm.get('iconSizeUnit').disable(); + this.progressBarWidgetConfigForm.get('icon').disable(); + this.progressBarWidgetConfigForm.get('iconColor').disable(); + } + + if (showValue) { + this.progressBarWidgetConfigForm.get('units').enable(); + this.progressBarWidgetConfigForm.get('decimals').enable(); + this.progressBarWidgetConfigForm.get('valueFont').enable(); + this.progressBarWidgetConfigForm.get('valueColor').enable(); + } else { + this.progressBarWidgetConfigForm.get('units').disable(); + this.progressBarWidgetConfigForm.get('decimals').disable(); + this.progressBarWidgetConfigForm.get('valueFont').disable(); + this.progressBarWidgetConfigForm.get('valueColor').disable(); + } + + if (ticksEnabled) { + this.progressBarWidgetConfigForm.get('showTicks').enable({emitEvent: false}); + if (showTicks) { + this.progressBarWidgetConfigForm.get('ticksFont').enable(); + this.progressBarWidgetConfigForm.get('ticksColor').enable(); + } else { + this.progressBarWidgetConfigForm.get('ticksFont').disable(); + this.progressBarWidgetConfigForm.get('ticksColor').disable(); + } + } else { + this.progressBarWidgetConfigForm.get('showTicks').disable({emitEvent: false}); + this.progressBarWidgetConfigForm.get('ticksFont').disable(); + this.progressBarWidgetConfigForm.get('ticksColor').disable(); + } + } + + private getCardButtons(config: WidgetConfig): string[] { + const buttons: string[] = []; + if (isUndefined(config.enableFullscreen) || config.enableFullscreen) { + buttons.push('fullscreen'); + } + return buttons; + } + + private setCardButtons(buttons: string[], config: WidgetConfig) { + config.enableFullscreen = buttons.includes('fullscreen'); + } + + private _valuePreviewFn(): string { + const units: string = this.widgetConfig.config.units; + const decimals: number = this.widgetConfig.config.decimals; + return formatValue(78, decimals, units, true); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.html index 28cd0aaa6e..92bd2e44ab 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/simple-card-basic-config.component.html @@ -28,15 +28,15 @@ hideDataKeyDecimals formControlName="datasources"> -
-
widget-config.appearance
-
-
widgets.simple-card.label
+
+
widget-config.appearance
+
+
widgets.simple-card.label
-
+
widgets.simple-card.label-position
@@ -49,35 +49,35 @@
-
+
widget-config.units-short
- - +
-
+
widget-config.decimals-short
- +
-
+
+
widget-config.show-card-buttons
+ + {{ 'fullscreen.fullscreen' | translate }} + +
+
{{ 'widget-config.text-color' | translate }}
-
- - - -
+ +
-
+
{{ 'widget-config.background' | translate }}
-
- - - -
+ +
+ + + + + + + +
+
widget-config.card-appearance
+
+ + {{ 'widget-config.card-title' | translate }} + +
+ + + + + + + +
+
+
+ + {{ 'widget-config.card-icon' | translate }} + +
+ + + + +
+
+
+
widget-config.show-card-buttons
+ + {{ 'action.search' | translate }} + {{ 'widgets.table.columns-to-display' | translate }} + {{ 'fullscreen.fullscreen' | translate }} + +
+
+
{{ 'widget-config.text-color' | translate }}
+ + +
+
+
{{ 'widget-config.background-color' | translate }}
+ + +
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/timeseries-table-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/timeseries-table-basic-config.component.ts new file mode 100644 index 0000000000..b99c005da0 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/timeseries-table-basic-config.component.ts @@ -0,0 +1,183 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { DataKey, Datasource, WidgetConfig } from '@shared/models/widget.models'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { deepClone, isUndefined } from '@core/utils'; +import { + getTimewindowConfig, + setTimewindowConfig +} from '@home/components/widget/config/timewindow-config-panel.component'; + +@Component({ + selector: 'tb-timeseries-table-basic-config', + templateUrl: './timeseries-table-basic-config.component.html', + styleUrls: ['../basic-config.scss'] +}) +export class TimeseriesTableBasicConfigComponent extends BasicWidgetConfigComponent { + + public get datasource(): Datasource { + const datasources: Datasource[] = this.timeseriesTableWidgetConfigForm.get('datasources').value; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + + timeseriesTableWidgetConfigForm: UntypedFormGroup; + + constructor(protected store: Store, + protected widgetConfigComponent: WidgetConfigComponent, + private fb: UntypedFormBuilder) { + super(store, widgetConfigComponent); + } + + protected configForm(): UntypedFormGroup { + return this.timeseriesTableWidgetConfigForm; + } + + protected defaultDataKeys(configData: WidgetConfigComponentData): DataKey[] { + return [{ name: 'temperature', label: 'Temperature', type: DataKeyType.timeseries, units: '°C', decimals: 0 }]; + } + + protected onConfigSet(configData: WidgetConfigComponentData) { + this.timeseriesTableWidgetConfigForm = this.fb.group({ + timewindowConfig: [getTimewindowConfig(configData.config), []], + datasources: [configData.config.datasources, []], + columns: [this.getColumns(configData.config.datasources), []], + showTitle: [configData.config.showTitle, []], + title: [configData.config.title, []], + titleFont: [configData.config.titleFont, []], + titleColor: [configData.config.titleColor, []], + showTitleIcon: [configData.config.showTitleIcon, []], + titleIcon: [configData.config.titleIcon, []], + iconColor: [configData.config.iconColor, []], + cardButtons: [this.getCardButtons(configData.config), []], + color: [configData.config.color, []], + backgroundColor: [configData.config.backgroundColor, []], + actions: [configData.config.actions || {}, []] + }); + } + + protected prepareOutputConfig(config: any): WidgetConfigComponentData { + setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig); + this.widgetConfig.config.datasources = config.datasources; + this.setColumns(config.columns, this.widgetConfig.config.datasources); + this.widgetConfig.config.actions = config.actions; + this.widgetConfig.config.showTitle = config.showTitle; + this.widgetConfig.config.title = config.title; + this.widgetConfig.config.titleFont = config.titleFont; + this.widgetConfig.config.titleColor = config.titleColor; + this.widgetConfig.config.showTitleIcon = config.showTitleIcon; + this.widgetConfig.config.titleIcon = config.titleIcon; + this.widgetConfig.config.iconColor = config.iconColor; + this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + this.setCardButtons(config.cardButtons, this.widgetConfig.config); + this.widgetConfig.config.color = config.color; + this.widgetConfig.config.backgroundColor = config.backgroundColor; + return this.widgetConfig; + } + + protected validatorTriggers(): string[] { + return ['showTitle', 'showTitleIcon']; + } + + protected updateValidators(emitEvent: boolean, trigger?: string) { + const showTitle: boolean = this.timeseriesTableWidgetConfigForm.get('showTitle').value; + const showTitleIcon: boolean = this.timeseriesTableWidgetConfigForm.get('showTitleIcon').value; + if (showTitle) { + this.timeseriesTableWidgetConfigForm.get('title').enable(); + this.timeseriesTableWidgetConfigForm.get('titleFont').enable(); + this.timeseriesTableWidgetConfigForm.get('titleColor').enable(); + this.timeseriesTableWidgetConfigForm.get('showTitleIcon').enable({emitEvent: false}); + if (showTitleIcon) { + this.timeseriesTableWidgetConfigForm.get('titleIcon').enable(); + this.timeseriesTableWidgetConfigForm.get('iconColor').enable(); + } else { + this.timeseriesTableWidgetConfigForm.get('titleIcon').disable(); + this.timeseriesTableWidgetConfigForm.get('iconColor').disable(); + } + } else { + this.timeseriesTableWidgetConfigForm.get('title').disable(); + this.timeseriesTableWidgetConfigForm.get('titleFont').disable(); + this.timeseriesTableWidgetConfigForm.get('titleColor').disable(); + this.timeseriesTableWidgetConfigForm.get('showTitleIcon').disable({emitEvent: false}); + this.timeseriesTableWidgetConfigForm.get('titleIcon').disable(); + this.timeseriesTableWidgetConfigForm.get('iconColor').disable(); + } + this.timeseriesTableWidgetConfigForm.get('title').updateValueAndValidity({emitEvent}); + this.timeseriesTableWidgetConfigForm.get('titleFont').updateValueAndValidity({emitEvent}); + this.timeseriesTableWidgetConfigForm.get('titleColor').updateValueAndValidity({emitEvent}); + this.timeseriesTableWidgetConfigForm.get('showTitleIcon').updateValueAndValidity({emitEvent: false}); + this.timeseriesTableWidgetConfigForm.get('titleIcon').updateValueAndValidity({emitEvent}); + this.timeseriesTableWidgetConfigForm.get('iconColor').updateValueAndValidity({emitEvent}); + } + + private getColumns(datasources?: Datasource[]): DataKey[] { + if (datasources && datasources.length) { + const dataKeys = deepClone(datasources[0].dataKeys) || []; + dataKeys.forEach(k => { + (k as any).latest = false; + }); + const latestDataKeys = deepClone(datasources[0].latestDataKeys) || []; + latestDataKeys.forEach(k => { + (k as any).latest = true; + }); + return dataKeys.concat(latestDataKeys); + } + return []; + } + + private setColumns(columns: DataKey[], datasources?: Datasource[]) { + if (datasources && datasources.length) { + const dataKeys = deepClone(columns.filter(c => !(c as any).latest)); + dataKeys.forEach(k => delete (k as any).latest); + const latestDataKeys = deepClone(columns.filter(c => (c as any).latest)); + latestDataKeys.forEach(k => delete (k as any).latest); + datasources[0].dataKeys = dataKeys; + datasources[0].latestDataKeys = latestDataKeys; + } + } + + private getCardButtons(config: WidgetConfig): string[] { + const buttons: string[] = []; + if (isUndefined(config.settings?.enableSearch) || config.settings?.enableSearch) { + buttons.push('search'); + } + if (isUndefined(config.settings?.enableSelectColumnDisplay) || config.settings?.enableSelectColumnDisplay) { + buttons.push('columnsToDisplay'); + } + if (isUndefined(config.enableFullscreen) || config.enableFullscreen) { + buttons.push('fullscreen'); + } + return buttons; + } + + private setCardButtons(buttons: string[], config: WidgetConfig) { + config.settings.enableSearch = buttons.includes('search'); + config.settings.enableSelectColumnDisplay = buttons.includes('columnsToDisplay'); + config.enableFullscreen = buttons.includes('fullscreen'); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.html new file mode 100644 index 0000000000..097c2dd949 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.html @@ -0,0 +1,133 @@ + + + + + + +
+
widget-config.appearance
+ + + {{ valueCardLayoutTranslationMap.get(layout) | translate }} + + +
+ + {{ 'widgets.value-card.auto-scale' | translate }} + +
+
+ + {{ 'widgets.value-card.label' | translate }} + +
+ + + + + + + +
+
+
+ + {{ 'widgets.value-card.icon' | translate }} + +
+ + + + + + + + +
+
+
+
widgets.value-card.value
+
+ + + +
widget-config.decimals-suffix
+
+ + + + +
+
+
+ + {{ 'widgets.value-card.date' | translate }} + +
+ + + + + +
+
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
widget-config.show-card-buttons
+ + {{ 'fullscreen.fullscreen' | translate }} + +
+
+
{{ 'widget-config.card-border-radius' | translate }}
+ + + +
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.ts new file mode 100644 index 0000000000..19eeb2e301 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.ts @@ -0,0 +1,290 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ChangeDetectorRef, Component, Injector } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { + DataKey, + datasourcesHasAggregation, + datasourcesHasOnlyComparisonAggregation, + WidgetConfig, +} from '@shared/models/widget.models'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { + getTimewindowConfig, + setTimewindowConfig +} from '@home/components/widget/config/timewindow-config-panel.component'; +import { formatValue, isDefinedAndNotNull, isUndefined } from '@core/utils'; +import { + DateFormatProcessor, + DateFormatSettings, + getLabel, + setLabel +} from '@shared/models/widget-settings.models'; +import { + valueCardDefaultSettings, + ValueCardLayout, + valueCardLayoutImages, + valueCardLayouts, + valueCardLayoutTranslations, + ValueCardWidgetSettings +} from '@home/components/widget/lib/cards/value-card-widget.models'; + +@Component({ + selector: 'tb-value-card-basic-config', + templateUrl: './value-card-basic-config.component.html', + styleUrls: ['../basic-config.scss'] +}) +export class ValueCardBasicConfigComponent extends BasicWidgetConfigComponent { + + public get displayTimewindowConfig(): boolean { + const datasources = this.valueCardWidgetConfigForm.get('datasources').value; + return datasourcesHasAggregation(datasources); + } + + public onlyHistoryTimewindow(): boolean { + const datasources = this.valueCardWidgetConfigForm.get('datasources').value; + return datasourcesHasOnlyComparisonAggregation(datasources); + } + + valueCardLayouts: ValueCardLayout[] = []; + + valueCardLayoutTranslationMap = valueCardLayoutTranslations; + valueCardLayoutImageMap = valueCardLayoutImages; + + horizontal = false; + + valueCardWidgetConfigForm: UntypedFormGroup; + + valuePreviewFn = this._valuePreviewFn.bind(this); + + datePreviewFn = this._datePreviewFn.bind(this); + + get dateEnabled(): boolean { + const layout: ValueCardLayout = this.valueCardWidgetConfigForm.get('layout').value; + return ![ValueCardLayout.vertical, ValueCardLayout.simplified].includes(layout); + } + + get iconEnabled(): boolean { + const layout: ValueCardLayout = this.valueCardWidgetConfigForm.get('layout').value; + return layout !== ValueCardLayout.simplified; + } + + constructor(protected store: Store, + protected widgetConfigComponent: WidgetConfigComponent, + private cd: ChangeDetectorRef, + private $injector: Injector, + private fb: UntypedFormBuilder) { + super(store, widgetConfigComponent); + } + + protected configForm(): UntypedFormGroup { + return this.valueCardWidgetConfigForm; + } + + protected setupConfig(widgetConfig: WidgetConfigComponentData) { + const params = widgetConfig.typeParameters as any; + this.horizontal = isDefinedAndNotNull(params.horizontal) ? params.horizontal : false; + this.valueCardLayouts = valueCardLayouts(this.horizontal); + super.setupConfig(widgetConfig); + } + + protected defaultDataKeys(configData: WidgetConfigComponentData): DataKey[] { + return [{ name: 'temperature', label: 'Temperature', type: DataKeyType.timeseries }]; + } + + protected onConfigSet(configData: WidgetConfigComponentData) { + const settings: ValueCardWidgetSettings = {...valueCardDefaultSettings(this.horizontal), ...(configData.config.settings || {})}; + this.valueCardWidgetConfigForm = this.fb.group({ + timewindowConfig: [getTimewindowConfig(configData.config), []], + datasources: [configData.config.datasources, []], + layout: [settings.layout, []], + autoScale: [settings.autoScale, []], + + showLabel: [settings.showLabel, []], + label: [getLabel(configData.config.datasources), []], + labelFont: [settings.labelFont, []], + labelColor: [settings.labelColor, []], + + showIcon: [settings.showIcon, []], + iconSize: [settings.iconSize, [Validators.min(0)]], + iconSizeUnit: [settings.iconSizeUnit, []], + icon: [settings.icon, []], + iconColor: [settings.iconColor, []], + + units: [configData.config.units, []], + decimals: [configData.config.decimals, []], + valueFont: [settings.valueFont, []], + valueColor: [settings.valueColor, []], + + showDate: [settings.showDate, []], + dateFormat: [settings.dateFormat, []], + dateFont: [settings.dateFont, []], + dateColor: [settings.dateColor, []], + + background: [settings.background, []], + + cardButtons: [this.getCardButtons(configData.config), []], + borderRadius: [configData.config.borderRadius, []], + + actions: [configData.config.actions || {}, []] + }); + } + + protected prepareOutputConfig(config: any): WidgetConfigComponentData { + setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig); + this.widgetConfig.config.datasources = config.datasources; + + this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + + this.widgetConfig.config.settings.layout = config.layout; + this.widgetConfig.config.settings.autoScale = config.autoScale; + + this.widgetConfig.config.settings.showLabel = config.showLabel; + setLabel(config.label, this.widgetConfig.config.datasources); + this.widgetConfig.config.settings.labelFont = config.labelFont; + this.widgetConfig.config.settings.labelColor = config.labelColor; + + this.widgetConfig.config.settings.showIcon = config.showIcon; + this.widgetConfig.config.settings.iconSize = config.iconSize; + this.widgetConfig.config.settings.iconSizeUnit = config.iconSizeUnit; + this.widgetConfig.config.settings.icon = config.icon; + this.widgetConfig.config.settings.iconColor = config.iconColor; + + this.widgetConfig.config.units = config.units; + this.widgetConfig.config.decimals = config.decimals; + this.widgetConfig.config.settings.valueFont = config.valueFont; + this.widgetConfig.config.settings.valueColor = config.valueColor; + + this.widgetConfig.config.settings.showDate = config.showDate; + this.widgetConfig.config.settings.dateFormat = config.dateFormat; + this.widgetConfig.config.settings.dateFont = config.dateFont; + this.widgetConfig.config.settings.dateColor = config.dateColor; + + this.widgetConfig.config.settings.background = config.background; + + this.setCardButtons(config.cardButtons, this.widgetConfig.config); + this.widgetConfig.config.borderRadius = config.borderRadius; + + this.widgetConfig.config.actions = config.actions; + return this.widgetConfig; + } + + protected validatorTriggers(): string[] { + return ['layout', 'showLabel', 'showIcon', 'showDate']; + } + + protected updateValidators(emitEvent: boolean, trigger?: string) { + const layout: ValueCardLayout = this.valueCardWidgetConfigForm.get('layout').value; + const showLabel: boolean = this.valueCardWidgetConfigForm.get('showLabel').value; + const showIcon: boolean = this.valueCardWidgetConfigForm.get('showIcon').value; + const showDate: boolean = this.valueCardWidgetConfigForm.get('showDate').value; + + const dateEnabled = ![ValueCardLayout.vertical, ValueCardLayout.simplified].includes(layout); + const iconEnabled = layout !== ValueCardLayout.simplified; + + if (showLabel) { + this.valueCardWidgetConfigForm.get('label').enable(); + this.valueCardWidgetConfigForm.get('labelFont').enable(); + this.valueCardWidgetConfigForm.get('labelColor').enable(); + } else { + this.valueCardWidgetConfigForm.get('label').disable(); + this.valueCardWidgetConfigForm.get('labelFont').disable(); + this.valueCardWidgetConfigForm.get('labelColor').disable(); + } + + if (iconEnabled) { + this.valueCardWidgetConfigForm.get('showIcon').enable({emitEvent: false}); + if (showIcon) { + this.valueCardWidgetConfigForm.get('iconSize').enable(); + this.valueCardWidgetConfigForm.get('iconSizeUnit').enable(); + this.valueCardWidgetConfigForm.get('icon').enable(); + this.valueCardWidgetConfigForm.get('iconColor').enable(); + } else { + this.valueCardWidgetConfigForm.get('iconSize').disable(); + this.valueCardWidgetConfigForm.get('iconSizeUnit').disable(); + this.valueCardWidgetConfigForm.get('icon').disable(); + this.valueCardWidgetConfigForm.get('iconColor').disable(); + } + } else { + this.valueCardWidgetConfigForm.get('showIcon').disable({emitEvent: false}); + this.valueCardWidgetConfigForm.get('iconSize').disable(); + this.valueCardWidgetConfigForm.get('iconSizeUnit').disable(); + this.valueCardWidgetConfigForm.get('icon').disable(); + this.valueCardWidgetConfigForm.get('iconColor').disable(); + } + + if (dateEnabled) { + this.valueCardWidgetConfigForm.get('showDate').enable({emitEvent: false}); + if (showDate) { + this.valueCardWidgetConfigForm.get('dateFormat').enable(); + this.valueCardWidgetConfigForm.get('dateFont').enable(); + this.valueCardWidgetConfigForm.get('dateColor').enable(); + } else { + this.valueCardWidgetConfigForm.get('dateFormat').disable(); + this.valueCardWidgetConfigForm.get('dateFont').disable(); + this.valueCardWidgetConfigForm.get('dateColor').disable(); + } + } else { + this.valueCardWidgetConfigForm.get('showDate').disable({emitEvent: false}); + this.valueCardWidgetConfigForm.get('dateFormat').disable(); + this.valueCardWidgetConfigForm.get('dateFont').disable(); + this.valueCardWidgetConfigForm.get('dateColor').disable(); + } + this.valueCardWidgetConfigForm.get('showIcon').updateValueAndValidity({emitEvent: false}); + this.valueCardWidgetConfigForm.get('showDate').updateValueAndValidity({emitEvent: false}); + this.valueCardWidgetConfigForm.get('label').updateValueAndValidity({emitEvent}); + this.valueCardWidgetConfigForm.get('labelFont').updateValueAndValidity({emitEvent}); + this.valueCardWidgetConfigForm.get('labelColor').updateValueAndValidity({emitEvent}); + this.valueCardWidgetConfigForm.get('iconSize').updateValueAndValidity({emitEvent}); + this.valueCardWidgetConfigForm.get('iconSizeUnit').updateValueAndValidity({emitEvent}); + this.valueCardWidgetConfigForm.get('icon').updateValueAndValidity({emitEvent}); + this.valueCardWidgetConfigForm.get('iconColor').updateValueAndValidity({emitEvent}); + this.valueCardWidgetConfigForm.get('dateFormat').updateValueAndValidity({emitEvent}); + this.valueCardWidgetConfigForm.get('dateFont').updateValueAndValidity({emitEvent}); + this.valueCardWidgetConfigForm.get('dateColor').updateValueAndValidity({emitEvent}); + } + + private getCardButtons(config: WidgetConfig): string[] { + const buttons: string[] = []; + if (isUndefined(config.enableFullscreen) || config.enableFullscreen) { + buttons.push('fullscreen'); + } + return buttons; + } + + private setCardButtons(buttons: string[], config: WidgetConfig) { + config.enableFullscreen = buttons.includes('fullscreen'); + } + + private _valuePreviewFn(): string { + const units: string = this.valueCardWidgetConfigForm.get('units').value; + const decimals: number = this.valueCardWidgetConfigForm.get('decimals').value; + return formatValue(22, decimals, units, true); + } + + private _datePreviewFn(): string { + const dateFormat: DateFormatSettings = this.valueCardWidgetConfigForm.get('dateFormat').value; + const processor = DateFormatProcessor.fromSettings(this.$injector, dateFormat); + processor.update(Date.now()); + return processor.formatted; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-chart-card-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-chart-card-basic-config.component.html new file mode 100644 index 0000000000..0b0c3f6d61 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-chart-card-basic-config.component.html @@ -0,0 +1,137 @@ + + + + + + +
+
widget-config.appearance
+ + + {{ valueChartCardLayoutTranslationMap.get(layout) | translate }} + + +
+ + {{ 'widgets.value-chart-card.auto-scale' | translate }} + +
+
+ + {{ 'widget-config.title' | translate }} + +
+ + + + + + + +
+
+
+ + {{ 'widgets.value-chart-card.icon' | translate }} + +
+ + + + + + + + +
+
+
+ + {{ 'widgets.value-chart-card.value' | translate }} + +
+ + + +
widget-config.decimals-suffix
+
+ + + + +
+
+
+
{{ 'widgets.value-chart-card.chart' | translate }}
+ + +
+
+
+
widget-config.card-appearance
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
widget-config.show-card-buttons
+ + {{ 'fullscreen.fullscreen' | translate }} + +
+
+
{{ 'widget-config.card-border-radius' | translate }}
+ + + +
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-chart-card-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-chart-card-basic-config.component.ts new file mode 100644 index 0000000000..8980a3ced4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-chart-card-basic-config.component.ts @@ -0,0 +1,252 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { DataKey, Datasource, WidgetConfig, } from '@shared/models/widget.models'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { + getTimewindowConfig, + setTimewindowConfig +} from '@home/components/widget/config/timewindow-config-panel.component'; +import { formatValue, isUndefined } from '@core/utils'; +import { cssSizeToStrSize, getDataKey, resolveCssSize } from '@shared/models/widget-settings.models'; +import { + valueCartCardLayouts, + valueChartCardDefaultSettings, + valueChartCardLayoutImages, + valueChartCardLayoutTranslations, + ValueChartCardWidgetSettings +} from '@home/components/widget/lib/cards/value-chart-card-widget.models'; + +@Component({ + selector: 'tb-value-chart-card-basic-config', + templateUrl: './value-chart-card-basic-config.component.html', + styleUrls: ['../basic-config.scss'] +}) +export class ValueChartCardBasicConfigComponent extends BasicWidgetConfigComponent { + + public get datasource(): Datasource { + const datasources: Datasource[] = this.valueChartCardWidgetConfigForm.get('datasources').value; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + + valueChartCardLayouts = valueCartCardLayouts; + + valueChartCardLayoutTranslationMap = valueChartCardLayoutTranslations; + valueChartCardLayoutImageMap = valueChartCardLayoutImages; + + valueChartCardWidgetConfigForm: UntypedFormGroup; + + valuePreviewFn = this._valuePreviewFn.bind(this); + + constructor(protected store: Store, + protected widgetConfigComponent: WidgetConfigComponent, + private fb: UntypedFormBuilder) { + super(store, widgetConfigComponent); + } + + protected configForm(): UntypedFormGroup { + return this.valueChartCardWidgetConfigForm; + } + + protected defaultDataKeys(configData: WidgetConfigComponentData): DataKey[] { + return [ + { name: 'temperature', label: 'Temperature', type: DataKeyType.timeseries, color: 'rgba(63, 82, 221, 1)'} + ]; + } + + protected defaultLatestDataKeys(configData: WidgetConfigComponentData): DataKey[] { + return [{ name: 'temperature', label: 'Latest', type: DataKeyType.timeseries}]; + } + + protected onConfigSet(configData: WidgetConfigComponentData) { + const settings: ValueChartCardWidgetSettings = {...valueChartCardDefaultSettings, ...(configData.config.settings || {})}; + const dataKey = getDataKey(configData.config.datasources); + const iconSize = resolveCssSize(configData.config.iconSize); + this.valueChartCardWidgetConfigForm = this.fb.group({ + timewindowConfig: [getTimewindowConfig(configData.config), []], + datasources: [configData.config.datasources, []], + + layout: [settings.layout, []], + autoScale: [settings.autoScale, []], + + showTitle: [configData.config.showTitle, []], + title: [configData.config.title, []], + titleFont: [configData.config.titleFont, []], + titleColor: [configData.config.titleColor, []], + + showIcon: [configData.config.showTitleIcon, []], + iconSize: [iconSize[0], [Validators.min(0)]], + iconSizeUnit: [iconSize[1], []], + icon: [configData.config.titleIcon, []], + iconColor: [configData.config.iconColor, []], + + showValue: [settings.showValue, []], + units: [configData.config.units, []], + decimals: [configData.config.decimals, []], + valueFont: [settings.valueFont, []], + valueColor: [settings.valueColor, []], + + chartColor: [dataKey?.color, []], + + background: [settings.background, []], + + cardButtons: [this.getCardButtons(configData.config), []], + borderRadius: [configData.config.borderRadius, []], + + actions: [configData.config.actions || {}, []] + }); + } + + protected prepareOutputConfig(config: any): WidgetConfigComponentData { + setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig); + this.widgetConfig.config.datasources = config.datasources; + + this.widgetConfig.config.showTitle = config.showTitle; + this.widgetConfig.config.title = config.title; + this.widgetConfig.config.titleFont = config.titleFont; + this.widgetConfig.config.titleColor = config.titleColor; + + this.widgetConfig.config.showTitleIcon = config.showIcon; + this.widgetConfig.config.iconSize = cssSizeToStrSize(config.iconSize, config.iconSizeUnit); + this.widgetConfig.config.titleIcon = config.icon; + this.widgetConfig.config.iconColor = config.iconColor; + + this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + + this.widgetConfig.config.settings.layout = config.layout; + this.widgetConfig.config.settings.autoScale = config.autoScale; + + this.widgetConfig.config.settings.showValue = config.showValue; + this.widgetConfig.config.units = config.units; + this.widgetConfig.config.decimals = config.decimals; + this.widgetConfig.config.settings.valueFont = config.valueFont; + this.widgetConfig.config.settings.valueColor = config.valueColor; + + const dataKey = getDataKey(this.widgetConfig.config.datasources); + if (dataKey) { + dataKey.color = config.chartColor; + this.updateLatestValues(dataKey, this.widgetConfig.config.datasources); + } + + this.widgetConfig.config.settings.background = config.background; + + this.setCardButtons(config.cardButtons, this.widgetConfig.config); + this.widgetConfig.config.borderRadius = config.borderRadius; + + this.widgetConfig.config.actions = config.actions; + return this.widgetConfig; + } + + protected validatorTriggers(): string[] { + return ['showTitle', 'showIcon', 'showValue']; + } + + protected updateValidators(emitEvent: boolean, trigger?: string) { + const showTitle: boolean = this.valueChartCardWidgetConfigForm.get('showTitle').value; + const showIcon: boolean = this.valueChartCardWidgetConfigForm.get('showIcon').value; + const showValue: boolean = this.valueChartCardWidgetConfigForm.get('showValue').value; + + if (showTitle) { + this.valueChartCardWidgetConfigForm.get('title').enable(); + this.valueChartCardWidgetConfigForm.get('titleFont').enable(); + this.valueChartCardWidgetConfigForm.get('titleColor').enable(); + this.valueChartCardWidgetConfigForm.get('showIcon').enable({emitEvent: false}); + if (showIcon) { + this.valueChartCardWidgetConfigForm.get('iconSize').enable(); + this.valueChartCardWidgetConfigForm.get('iconSizeUnit').enable(); + this.valueChartCardWidgetConfigForm.get('icon').enable(); + this.valueChartCardWidgetConfigForm.get('iconColor').enable(); + } else { + this.valueChartCardWidgetConfigForm.get('iconSize').disable(); + this.valueChartCardWidgetConfigForm.get('iconSizeUnit').disable(); + this.valueChartCardWidgetConfigForm.get('icon').disable(); + this.valueChartCardWidgetConfigForm.get('iconColor').disable(); + } + } else { + this.valueChartCardWidgetConfigForm.get('title').disable(); + this.valueChartCardWidgetConfigForm.get('titleFont').disable(); + this.valueChartCardWidgetConfigForm.get('titleColor').disable(); + this.valueChartCardWidgetConfigForm.get('showIcon').disable({emitEvent: false}); + this.valueChartCardWidgetConfigForm.get('iconSize').disable(); + this.valueChartCardWidgetConfigForm.get('iconSizeUnit').disable(); + this.valueChartCardWidgetConfigForm.get('icon').disable(); + this.valueChartCardWidgetConfigForm.get('iconColor').disable(); + } + + if (showValue) { + this.valueChartCardWidgetConfigForm.get('units').enable(); + this.valueChartCardWidgetConfigForm.get('decimals').enable(); + this.valueChartCardWidgetConfigForm.get('valueFont').enable(); + this.valueChartCardWidgetConfigForm.get('valueColor').enable(); + } else { + this.valueChartCardWidgetConfigForm.get('units').disable(); + this.valueChartCardWidgetConfigForm.get('decimals').disable(); + this.valueChartCardWidgetConfigForm.get('valueFont').disable(); + this.valueChartCardWidgetConfigForm.get('valueColor').disable(); + } + } + + private updateLatestValues(sourceDataKey: DataKey, datasources?: Datasource[]) { + if (datasources && datasources.length) { + let latestDataKeys = datasources[0].latestDataKeys; + if (!latestDataKeys) { + latestDataKeys = []; + datasources[0].latestDataKeys = latestDataKeys; + } + let dataKey: DataKey; + if (!latestDataKeys.length) { + dataKey = {...sourceDataKey}; + latestDataKeys.push(dataKey); + } else { + dataKey = latestDataKeys[0]; + dataKey = {...dataKey, ...sourceDataKey}; + latestDataKeys[0] = dataKey; + } + dataKey.label = 'Latest'; + dataKey.units = null; + dataKey.decimals = null; + } + } + + private getCardButtons(config: WidgetConfig): string[] { + const buttons: string[] = []; + if (isUndefined(config.enableFullscreen) || config.enableFullscreen) { + buttons.push('fullscreen'); + } + return buttons; + } + + private setCardButtons(buttons: string[], config: WidgetConfig) { + config.enableFullscreen = buttons.includes('fullscreen'); + } + + private _valuePreviewFn(): string { + const units: string = this.widgetConfig.config.units; + const decimals: number = this.widgetConfig.config.decimals; + return formatValue(22, decimals, units, true); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/doughnut-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/doughnut-basic-config.component.html new file mode 100644 index 0000000000..00c4929f3c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/doughnut-basic-config.component.html @@ -0,0 +1,255 @@ + + + + + + + + +
+
widget-config.card-appearance
+ + + {{ doughnutLayoutTranslationMap.get(layout) | translate }} + + +
+ + {{ 'widgets.doughnut.auto-scale' | translate }} + +
+
+ + {{ 'widget-config.card-title' | translate }} + +
+ + + + + + + +
+
+
+ + {{ 'widget-config.card-icon' | translate }} + +
+ + + + +
+
+
+ + {{ 'widgets.doughnut.clockwise-layout' | translate }} + +
+
+ + {{ 'widgets.doughnut.sort-series' | translate }} + +
+
+
{{ 'widgets.doughnut.central-total-value' | translate }}
+
+ + + + +
+
+
+
widget-config.units-short
+ + +
+
+
widget-config.decimals-short
+ + + +
+
+
+ + + + + {{ 'widget-config.legend' | translate }} + + + + +
+
{{ 'legend.position' | translate }}
+ + + + {{ doughnutLegendPositionTranslationMap.get(pos) | translate }} + + + +
+
+
{{ 'widgets.doughnut.legend-label' | translate }}
+
+ + + + +
+
+
+
{{ 'widgets.doughnut.legend-value' | translate }}
+
+ + + + +
+
+
+
+
+
+ + + + + {{ 'widgets.doughnut.tooltip' | translate }} + + + + +
+
{{ 'widgets.doughnut.tooltip-value' | translate }}
+
+ + + + {{ doughnutTooltipValueTypeTranslationMap.get(type) | translate }} + + + + + +
widget-config.decimals-suffix
+
+ + + + +
+
+
+
{{ 'widgets.doughnut.tooltip-background-color' | translate }}
+ + +
+
+
{{ 'widgets.doughnut.tooltip-background-blur' | translate }}
+ + +
px
+
+
+
+
+
+
+
widget-config.card-appearance
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
widget-config.show-card-buttons
+ + {{ 'fullscreen.fullscreen' | translate }} + +
+
+
{{ 'widget-config.card-border-radius' | translate }}
+ + + +
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/doughnut-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/doughnut-basic-config.component.ts new file mode 100644 index 0000000000..f8149bc97d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/doughnut-basic-config.component.ts @@ -0,0 +1,341 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { + DataKey, + Datasource, + datasourcesHasAggregation, + datasourcesHasOnlyComparisonAggregation, + WidgetConfig +} from '@shared/models/widget.models'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { formatValue, isDefinedAndNotNull, isUndefined } from '@core/utils'; +import { + getTimewindowConfig, + setTimewindowConfig +} from '@home/components/widget/config/timewindow-config-panel.component'; +import { + doughnutDefaultSettings, + DoughnutLayout, + doughnutLayoutImages, + doughnutLayouts, + doughnutLayoutTranslations, + doughnutLegendPositions, + doughnutLegendPositionTranslations, + DoughnutTooltipValueType, + doughnutTooltipValueTypes, + doughnutTooltipValueTypeTranslations, + DoughnutWidgetSettings, + horizontalDoughnutLayoutImages +} from '@home/components/widget/lib/chart/doughnut-widget.models'; + +@Component({ + selector: 'tb-doughnut-basic-config', + templateUrl: './doughnut-basic-config.component.html', + styleUrls: ['../basic-config.scss'] +}) +export class DoughnutBasicConfigComponent extends BasicWidgetConfigComponent { + + public get datasource(): Datasource { + const datasources: Datasource[] = this.doughnutWidgetConfigForm.get('datasources').value; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + + public get displayTimewindowConfig(): boolean { + const datasources = this.doughnutWidgetConfigForm.get('datasources').value; + return datasourcesHasAggregation(datasources); + } + + public onlyHistoryTimewindow(): boolean { + const datasources = this.doughnutWidgetConfigForm.get('datasources').value; + return datasourcesHasOnlyComparisonAggregation(datasources); + } + + doughnutLayouts = doughnutLayouts; + + doughnutLayoutTranslationMap = doughnutLayoutTranslations; + + horizontal = false; + + doughnutLayoutImageMap: Map; + + doughnutLegendPositions = doughnutLegendPositions; + + doughnutLegendPositionTranslationMap = doughnutLegendPositionTranslations; + + doughnutTooltipValueTypes = doughnutTooltipValueTypes; + + doughnutTooltipValueTypeTranslationMap = doughnutTooltipValueTypeTranslations; + + doughnutWidgetConfigForm: UntypedFormGroup; + + valuePreviewFn = this._valuePreviewFn.bind(this); + + tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); + + get totalEnabled(): boolean { + const layout: DoughnutLayout = this.doughnutWidgetConfigForm.get('layout').value; + return layout === DoughnutLayout.with_total; + } + + constructor(protected store: Store, + protected widgetConfigComponent: WidgetConfigComponent, + private fb: UntypedFormBuilder) { + super(store, widgetConfigComponent); + } + + protected configForm(): UntypedFormGroup { + return this.doughnutWidgetConfigForm; + } + + protected setupConfig(widgetConfig: WidgetConfigComponentData) { + const params = widgetConfig.typeParameters as any; + this.horizontal = isDefinedAndNotNull(params.horizontal) ? params.horizontal : false; + this.doughnutLayoutImageMap = this.horizontal ? horizontalDoughnutLayoutImages : doughnutLayoutImages; + super.setupConfig(widgetConfig); + } + + protected defaultDataKeys(configData: WidgetConfigComponentData): DataKey[] { + return [{ name: 'windPower', label: 'Wind power', type: DataKeyType.timeseries, units: '', decimals: 0, color: '#08872B' }, + { name: 'solarPower', label: 'Solar power', type: DataKeyType.timeseries, units: '', decimals: 0, color: '#FF4D5A' }]; + } + + protected onConfigSet(configData: WidgetConfigComponentData) { + const settings: DoughnutWidgetSettings = {...doughnutDefaultSettings(this.horizontal), ...(configData.config.settings || {})}; + this.doughnutWidgetConfigForm = this.fb.group({ + timewindowConfig: [getTimewindowConfig(configData.config), []], + datasources: [configData.config.datasources, []], + + series: [this.getSeries(configData.config.datasources), []], + + layout: [settings.layout, []], + autoScale: [settings.autoScale, []], + clockwise: [settings.clockwise, []], + sortSeries: [settings.sortSeries, []], + + showTitle: [configData.config.showTitle, []], + title: [configData.config.title, []], + titleFont: [configData.config.titleFont, []], + titleColor: [configData.config.titleColor, []], + showTitleIcon: [configData.config.showTitleIcon, []], + titleIcon: [configData.config.titleIcon, []], + iconColor: [configData.config.iconColor, []], + + totalValueFont: [settings.totalValueFont, []], + totalValueColor: [settings.totalValueColor, []], + + units: [configData.config.units, []], + decimals: [configData.config.decimals, []], + + showLegend: [settings.showLegend, []], + legendPosition: [settings.legendPosition, []], + legendLabelFont: [settings.legendLabelFont, []], + legendLabelColor: [settings.legendLabelColor, []], + legendValueFont: [settings.legendValueFont, []], + legendValueColor: [settings.legendValueColor, []], + + showTooltip: [settings.showTooltip, []], + tooltipValueType: [settings.tooltipValueType, []], + tooltipValueDecimals: [settings.tooltipValueDecimals, []], + tooltipValueFont: [settings.tooltipValueFont, []], + tooltipValueColor: [settings.tooltipValueColor, []], + tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], + tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], + + background: [settings.background, []], + + cardButtons: [this.getCardButtons(configData.config), []], + borderRadius: [configData.config.borderRadius, []], + + actions: [configData.config.actions || {}, []] + }); + } + + protected prepareOutputConfig(config: any): WidgetConfigComponentData { + setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig); + this.widgetConfig.config.datasources = config.datasources; + this.setSeries(config.series, this.widgetConfig.config.datasources); + + this.widgetConfig.config.showTitle = config.showTitle; + this.widgetConfig.config.title = config.title; + this.widgetConfig.config.titleFont = config.titleFont; + this.widgetConfig.config.titleColor = config.titleColor; + + this.widgetConfig.config.showTitleIcon = config.showTitleIcon; + this.widgetConfig.config.titleIcon = config.titleIcon; + this.widgetConfig.config.iconColor = config.iconColor; + + this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + + this.widgetConfig.config.settings.layout = config.layout; + this.widgetConfig.config.settings.autoScale = config.autoScale; + this.widgetConfig.config.settings.clockwise = config.clockwise; + this.widgetConfig.config.settings.sortSeries = config.sortSeries; + + this.widgetConfig.config.settings.totalValueFont = config.totalValueFont; + this.widgetConfig.config.settings.totalValueColor = config.totalValueColor; + + this.widgetConfig.config.units = config.units; + this.widgetConfig.config.decimals = config.decimals; + + this.widgetConfig.config.settings.showLegend = config.showLegend; + this.widgetConfig.config.settings.legendPosition = config.legendPosition; + this.widgetConfig.config.settings.legendLabelFont = config.legendLabelFont; + this.widgetConfig.config.settings.legendLabelColor = config.legendLabelColor; + this.widgetConfig.config.settings.legendValueFont = config.legendValueFont; + this.widgetConfig.config.settings.legendValueColor = config.legendValueColor; + + this.widgetConfig.config.settings.showTooltip = config.showTooltip; + this.widgetConfig.config.settings.tooltipValueType = config.tooltipValueType; + this.widgetConfig.config.settings.tooltipValueDecimals = config.tooltipValueDecimals; + this.widgetConfig.config.settings.tooltipValueFont = config.tooltipValueFont; + this.widgetConfig.config.settings.tooltipValueColor = config.tooltipValueColor; + this.widgetConfig.config.settings.tooltipBackgroundColor = config.tooltipBackgroundColor; + this.widgetConfig.config.settings.tooltipBackgroundBlur = config.tooltipBackgroundBlur; + + this.widgetConfig.config.settings.background = config.background; + + this.setCardButtons(config.cardButtons, this.widgetConfig.config); + this.widgetConfig.config.borderRadius = config.borderRadius; + + this.widgetConfig.config.actions = config.actions; + + return this.widgetConfig; + } + + protected validatorTriggers(): string[] { + return ['layout', 'showTitle', 'showTitleIcon', 'showLegend', 'showTooltip']; + } + + protected updateValidators(emitEvent: boolean, trigger?: string) { + const layout: DoughnutLayout = this.doughnutWidgetConfigForm.get('layout').value; + const showTitle: boolean = this.doughnutWidgetConfigForm.get('showTitle').value; + const showTitleIcon: boolean = this.doughnutWidgetConfigForm.get('showTitleIcon').value; + const showLegend: boolean = this.doughnutWidgetConfigForm.get('showLegend').value; + const showTooltip: boolean = this.doughnutWidgetConfigForm.get('showTooltip').value; + + const totalEnabled = layout === DoughnutLayout.with_total; + + if (showTitle) { + this.doughnutWidgetConfigForm.get('title').enable(); + this.doughnutWidgetConfigForm.get('titleFont').enable(); + this.doughnutWidgetConfigForm.get('titleColor').enable(); + this.doughnutWidgetConfigForm.get('showTitleIcon').enable({emitEvent: false}); + if (showTitleIcon) { + this.doughnutWidgetConfigForm.get('titleIcon').enable(); + this.doughnutWidgetConfigForm.get('iconColor').enable(); + } else { + this.doughnutWidgetConfigForm.get('titleIcon').disable(); + this.doughnutWidgetConfigForm.get('iconColor').disable(); + } + } else { + this.doughnutWidgetConfigForm.get('title').disable(); + this.doughnutWidgetConfigForm.get('titleFont').disable(); + this.doughnutWidgetConfigForm.get('titleColor').disable(); + this.doughnutWidgetConfigForm.get('showTitleIcon').disable({emitEvent: false}); + this.doughnutWidgetConfigForm.get('titleIcon').disable(); + this.doughnutWidgetConfigForm.get('iconColor').disable(); + } + if (showLegend) { + this.doughnutWidgetConfigForm.get('legendPosition').enable(); + this.doughnutWidgetConfigForm.get('legendLabelFont').enable(); + this.doughnutWidgetConfigForm.get('legendLabelColor').enable(); + this.doughnutWidgetConfigForm.get('legendValueFont').enable(); + this.doughnutWidgetConfigForm.get('legendValueColor').enable(); + } else { + this.doughnutWidgetConfigForm.get('legendPosition').disable(); + this.doughnutWidgetConfigForm.get('legendLabelFont').disable(); + this.doughnutWidgetConfigForm.get('legendLabelColor').disable(); + this.doughnutWidgetConfigForm.get('legendValueFont').disable(); + this.doughnutWidgetConfigForm.get('legendValueColor').disable(); + } + if (showTooltip) { + this.doughnutWidgetConfigForm.get('tooltipValueType').enable(); + this.doughnutWidgetConfigForm.get('tooltipValueDecimals').enable(); + this.doughnutWidgetConfigForm.get('tooltipValueFont').enable(); + this.doughnutWidgetConfigForm.get('tooltipValueColor').enable(); + this.doughnutWidgetConfigForm.get('tooltipBackgroundColor').enable(); + this.doughnutWidgetConfigForm.get('tooltipBackgroundBlur').enable(); + } else { + this.doughnutWidgetConfigForm.get('tooltipValueType').disable(); + this.doughnutWidgetConfigForm.get('tooltipValueDecimals').disable(); + this.doughnutWidgetConfigForm.get('tooltipValueFont').disable(); + this.doughnutWidgetConfigForm.get('tooltipValueColor').disable(); + this.doughnutWidgetConfigForm.get('tooltipBackgroundColor').disable(); + this.doughnutWidgetConfigForm.get('tooltipBackgroundBlur').disable(); + } + if (totalEnabled) { + this.doughnutWidgetConfigForm.get('totalValueFont').enable(); + this.doughnutWidgetConfigForm.get('totalValueColor').enable(); + } else { + this.doughnutWidgetConfigForm.get('totalValueFont').disable(); + this.doughnutWidgetConfigForm.get('totalValueColor').disable(); + } + } + + private getSeries(datasources?: Datasource[]): DataKey[] { + if (datasources && datasources.length) { + return datasources[0].dataKeys || []; + } + return []; + } + + private setSeries(series: DataKey[], datasources?: Datasource[]) { + if (datasources && datasources.length) { + datasources[0].dataKeys = series; + } + } + + private getCardButtons(config: WidgetConfig): string[] { + const buttons: string[] = []; + if (isUndefined(config.enableFullscreen) || config.enableFullscreen) { + buttons.push('fullscreen'); + } + return buttons; + } + + private setCardButtons(buttons: string[], config: WidgetConfig) { + config.enableFullscreen = buttons.includes('fullscreen'); + } + + private _valuePreviewFn(): string { + const units: string = this.doughnutWidgetConfigForm.get('units').value; + const decimals: number = this.doughnutWidgetConfigForm.get('decimals').value; + return formatValue(110, decimals, units, false); + } + + private _tooltipValuePreviewFn(): string { + const tooltipValueType: DoughnutTooltipValueType = this.doughnutWidgetConfigForm.get('tooltipValueType').value; + const decimals: number = this.doughnutWidgetConfigForm.get('tooltipValueDecimals').value; + if (tooltipValueType === DoughnutTooltipValueType.percentage) { + return formatValue(35, decimals, '%', false); + } else { + const units: string = this.doughnutWidgetConfigForm.get('units').value; + return formatValue(110, decimals, units, false); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.html new file mode 100644 index 0000000000..6bd31f41eb --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.html @@ -0,0 +1,130 @@ + + + + + + + + +
+
widget-config.card-appearance
+
+ + {{ 'widget-config.card-title' | translate }} + +
+ + + + + + + +
+
+
+ + {{ 'widget-config.card-icon' | translate }} + +
+ + + + +
+
+
+
widget-config.show-card-buttons
+ + {{ 'fullscreen.fullscreen' | translate }} + +
+
+
{{ 'widget-config.text-color' | translate }}
+ + +
+
+
{{ 'widget-config.background-color' | translate }}
+ + +
+
+
+
widgets.chart.chart-appearance
+
+ + {{ 'widgets.chart.vertical-grid-lines' | translate }} + +
+
+ + {{ 'widgets.chart.horizontal-grid-lines' | translate }} + +
+
+ + + + + {{ 'widget-config.legend' | translate }} + + + + + + + + +
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.ts new file mode 100644 index 0000000000..2b2618e25f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/flot-basic-config.component.ts @@ -0,0 +1,178 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { DataKey, Datasource, WidgetConfig } from '@shared/models/widget.models'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { isUndefined } from '@core/utils'; +import { + getTimewindowConfig, + setTimewindowConfig +} from '@home/components/widget/config/timewindow-config-panel.component'; + +@Component({ + selector: 'tb-flot-basic-config', + templateUrl: './flot-basic-config.component.html', + styleUrls: ['../basic-config.scss'] +}) +export class FlotBasicConfigComponent extends BasicWidgetConfigComponent { + + public get datasource(): Datasource { + const datasources: Datasource[] = this.flotWidgetConfigForm.get('datasources').value; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + + flotWidgetConfigForm: UntypedFormGroup; + + constructor(protected store: Store, + protected widgetConfigComponent: WidgetConfigComponent, + private fb: UntypedFormBuilder) { + super(store, widgetConfigComponent); + } + + protected configForm(): UntypedFormGroup { + return this.flotWidgetConfigForm; + } + + protected defaultDataKeys(configData: WidgetConfigComponentData): DataKey[] { + return [{ name: 'temperature', label: 'Temperature', type: DataKeyType.timeseries, units: '°C', decimals: 0 }]; + } + + protected onConfigSet(configData: WidgetConfigComponentData) { + this.flotWidgetConfigForm = this.fb.group({ + timewindowConfig: [getTimewindowConfig(configData.config), []], + datasources: [configData.config.datasources, []], + series: [this.getSeries(configData.config.datasources), []], + showTitle: [configData.config.showTitle, []], + title: [configData.config.title, []], + titleFont: [configData.config.titleFont, []], + titleColor: [configData.config.titleColor, []], + showTitleIcon: [configData.config.showTitleIcon, []], + titleIcon: [configData.config.titleIcon, []], + iconColor: [configData.config.iconColor, []], + cardButtons: [this.getCardButtons(configData.config), []], + color: [configData.config.color, []], + backgroundColor: [configData.config.backgroundColor, []], + verticalLines: [configData.config.settings?.grid?.verticalLines, []], + horizontalLines: [configData.config.settings?.grid?.horizontalLines, []], + showLegend: [configData.config.settings?.showLegend, []], + legendConfig: [configData.config.settings?.legendConfig, []], + actions: [configData.config.actions || {}, []] + }); + } + + protected prepareOutputConfig(config: any): WidgetConfigComponentData { + setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig); + this.widgetConfig.config.datasources = config.datasources; + this.setSeries(config.series, this.widgetConfig.config.datasources); + this.widgetConfig.config.actions = config.actions; + this.widgetConfig.config.showTitle = config.showTitle; + this.widgetConfig.config.title = config.title; + this.widgetConfig.config.titleFont = config.titleFont; + this.widgetConfig.config.titleColor = config.titleColor; + this.widgetConfig.config.showTitleIcon = config.showTitleIcon; + this.widgetConfig.config.titleIcon = config.titleIcon; + this.widgetConfig.config.iconColor = config.iconColor; + this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + this.setCardButtons(config.cardButtons, this.widgetConfig.config); + this.widgetConfig.config.color = config.color; + this.widgetConfig.config.backgroundColor = config.backgroundColor; + this.widgetConfig.config.settings.grid = this.widgetConfig.config.settings.grid || {}; + this.widgetConfig.config.settings.grid.verticalLines = config.verticalLines; + this.widgetConfig.config.settings.grid.horizontalLines = config.horizontalLines; + this.widgetConfig.config.settings.showLegend = config.showLegend; + this.widgetConfig.config.settings.legendConfig = config.legendConfig; + return this.widgetConfig; + } + + protected validatorTriggers(): string[] { + return ['showTitle', 'showTitleIcon', 'showLegend']; + } + + protected updateValidators(emitEvent: boolean, trigger?: string) { + const showTitle: boolean = this.flotWidgetConfigForm.get('showTitle').value; + const showTitleIcon: boolean = this.flotWidgetConfigForm.get('showTitleIcon').value; + const showLegend: boolean = this.flotWidgetConfigForm.get('showLegend').value; + if (showTitle) { + this.flotWidgetConfigForm.get('title').enable(); + this.flotWidgetConfigForm.get('titleFont').enable(); + this.flotWidgetConfigForm.get('titleColor').enable(); + this.flotWidgetConfigForm.get('showTitleIcon').enable({emitEvent: false}); + if (showTitleIcon) { + this.flotWidgetConfigForm.get('titleIcon').enable(); + this.flotWidgetConfigForm.get('iconColor').enable(); + } else { + this.flotWidgetConfigForm.get('titleIcon').disable(); + this.flotWidgetConfigForm.get('iconColor').disable(); + } + } else { + this.flotWidgetConfigForm.get('title').disable(); + this.flotWidgetConfigForm.get('titleFont').disable(); + this.flotWidgetConfigForm.get('titleColor').disable(); + this.flotWidgetConfigForm.get('showTitleIcon').disable({emitEvent: false}); + this.flotWidgetConfigForm.get('titleIcon').disable(); + this.flotWidgetConfigForm.get('iconColor').disable(); + } + if (showLegend) { + this.flotWidgetConfigForm.get('legendConfig').enable(); + } else { + this.flotWidgetConfigForm.get('legendConfig').disable(); + } + this.flotWidgetConfigForm.get('title').updateValueAndValidity({emitEvent}); + this.flotWidgetConfigForm.get('titleFont').updateValueAndValidity({emitEvent}); + this.flotWidgetConfigForm.get('titleColor').updateValueAndValidity({emitEvent}); + this.flotWidgetConfigForm.get('showTitleIcon').updateValueAndValidity({emitEvent: false}); + this.flotWidgetConfigForm.get('titleIcon').updateValueAndValidity({emitEvent}); + this.flotWidgetConfigForm.get('iconColor').updateValueAndValidity({emitEvent}); + this.flotWidgetConfigForm.get('legendConfig').updateValueAndValidity({emitEvent}); + } + + private getSeries(datasources?: Datasource[]): DataKey[] { + if (datasources && datasources.length) { + return datasources[0].dataKeys || []; + } + return []; + } + + private setSeries(series: DataKey[], datasources?: Datasource[]) { + if (datasources && datasources.length) { + datasources[0].dataKeys = series; + } + } + + private getCardButtons(config: WidgetConfig): string[] { + const buttons: string[] = []; + if (isUndefined(config.enableFullscreen) || config.enableFullscreen) { + buttons.push('fullscreen'); + } + return buttons; + } + + private setCardButtons(buttons: string[], config: WidgetConfig) { + config.enableFullscreen = buttons.includes('fullscreen'); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html new file mode 100644 index 0000000000..66b4054df1 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html @@ -0,0 +1,222 @@ + + + + + + +
+
widget-config.appearance
+
+ + {{ 'widget-config.title' | translate }} + +
+ + + + + + + +
+
+
+ + {{ 'widgets.value-chart-card.icon' | translate }} + +
+ + + + + + + + +
+
+
+
+
widgets.range-chart.chart
+
+ + {{ 'widgets.range-chart.data-zoom' | translate }} + +
+
+
{{ 'widgets.range-chart.range-colors' | translate }}
+ + +
+
+
{{ 'widgets.range-chart.out-of-range-color' | translate }}
+ + +
+
+ + {{ 'widgets.range-chart.fill-area' | translate }} + +
+
+
widget-config.units-short
+ + +
+
+
widget-config.decimals-short
+ + + +
+
+
+ + + + + {{ 'widget-config.legend' | translate }} + + + + +
+
{{ 'legend.position' | translate }}
+ + + + {{ legendPositionTranslationMap.get(pos) | translate }} + + + +
+
+
{{ 'widgets.range-chart.legend-label' | translate }}
+
+ + + + +
+
+
+
+
+
+ + + + + {{ 'widgets.range-chart.tooltip' | translate }} + + + + +
+
{{ 'widgets.range-chart.tooltip-value' | translate }}
+
+ + + + +
+
+
+ + {{ 'widgets.range-chart.tooltip-date' | translate }} + +
+ + + + + +
+
+
+
{{ 'widgets.range-chart.tooltip-background-color' | translate }}
+ + +
+
+
{{ 'widgets.range-chart.tooltip-background-blur' | translate }}
+ + +
px
+
+
+
+
+
+
+
widget-config.card-appearance
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
widget-config.show-card-buttons
+ + {{ 'fullscreen.fullscreen' | translate }} + +
+
+
{{ 'widget-config.card-border-radius' | translate }}
+ + + +
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts new file mode 100644 index 0000000000..5b22637296 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts @@ -0,0 +1,269 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ChangeDetectorRef, Component, Injector } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { DataKey, legendPositions, legendPositionTranslationMap, WidgetConfig, } from '@shared/models/widget.models'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { + getTimewindowConfig, + setTimewindowConfig +} from '@home/components/widget/config/timewindow-config-panel.component'; +import { formatValue, isUndefined } from '@core/utils'; +import { + cssSizeToStrSize, + DateFormatProcessor, + DateFormatSettings, + resolveCssSize +} from '@shared/models/widget-settings.models'; +import { + rangeChartDefaultSettings, + RangeChartWidgetSettings +} from '@home/components/widget/lib/chart/range-chart-widget.models'; + +@Component({ + selector: 'tb-range-chart-basic-config', + templateUrl: './range-chart-basic-config.component.html', + styleUrls: ['../basic-config.scss'] +}) +export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent { + + legendPositions = legendPositions; + + legendPositionTranslationMap = legendPositionTranslationMap; + + rangeChartWidgetConfigForm: UntypedFormGroup; + + tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); + + tooltipDatePreviewFn = this._tooltipDatePreviewFn.bind(this); + + constructor(protected store: Store, + protected widgetConfigComponent: WidgetConfigComponent, + private $injector: Injector, + private fb: UntypedFormBuilder) { + super(store, widgetConfigComponent); + } + + protected configForm(): UntypedFormGroup { + return this.rangeChartWidgetConfigForm; + } + + protected defaultDataKeys(configData: WidgetConfigComponentData): DataKey[] { + return [{ name: 'temperature', label: 'Temperature', type: DataKeyType.timeseries }]; + } + + protected onConfigSet(configData: WidgetConfigComponentData) { + const settings: RangeChartWidgetSettings = {...rangeChartDefaultSettings, ...(configData.config.settings || {})}; + const iconSize = resolveCssSize(configData.config.iconSize); + this.rangeChartWidgetConfigForm = this.fb.group({ + timewindowConfig: [getTimewindowConfig(configData.config), []], + datasources: [configData.config.datasources, []], + + showTitle: [configData.config.showTitle, []], + title: [configData.config.title, []], + titleFont: [configData.config.titleFont, []], + titleColor: [configData.config.titleColor, []], + + showIcon: [configData.config.showTitleIcon, []], + iconSize: [iconSize[0], [Validators.min(0)]], + iconSizeUnit: [iconSize[1], []], + icon: [configData.config.titleIcon, []], + iconColor: [configData.config.iconColor, []], + + dataZoom: [settings.dataZoom, []], + rangeColors: [settings.rangeColors, []], + outOfRangeColor: [settings.outOfRangeColor, []], + fillArea: [settings.fillArea, []], + units: [configData.config.units, []], + decimals: [configData.config.decimals, []], + + showLegend: [settings.showLegend, []], + legendPosition: [settings.legendPosition, []], + legendLabelFont: [settings.legendLabelFont, []], + legendLabelColor: [settings.legendLabelColor, []], + + showTooltip: [settings.showTooltip, []], + tooltipValueFont: [settings.tooltipValueFont, []], + tooltipValueColor: [settings.tooltipValueColor, []], + tooltipShowDate: [settings.tooltipShowDate, []], + tooltipDateFormat: [settings.tooltipDateFormat, []], + tooltipDateFont: [settings.tooltipDateFont, []], + tooltipDateColor: [settings.tooltipDateColor, []], + tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], + tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], + + background: [settings.background, []], + + cardButtons: [this.getCardButtons(configData.config), []], + borderRadius: [configData.config.borderRadius, []], + + actions: [configData.config.actions || {}, []] + }); + } + + protected prepareOutputConfig(config: any): WidgetConfigComponentData { + setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig); + this.widgetConfig.config.datasources = config.datasources; + + this.widgetConfig.config.showTitle = config.showTitle; + this.widgetConfig.config.title = config.title; + this.widgetConfig.config.titleFont = config.titleFont; + this.widgetConfig.config.titleColor = config.titleColor; + + this.widgetConfig.config.showTitleIcon = config.showIcon; + this.widgetConfig.config.iconSize = cssSizeToStrSize(config.iconSize, config.iconSizeUnit); + this.widgetConfig.config.titleIcon = config.icon; + this.widgetConfig.config.iconColor = config.iconColor; + + this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + + this.widgetConfig.config.settings.dataZoom = config.dataZoom; + this.widgetConfig.config.settings.rangeColors = config.rangeColors; + this.widgetConfig.config.settings.outOfRangeColor = config.outOfRangeColor; + this.widgetConfig.config.settings.fillArea = config.fillArea; + this.widgetConfig.config.units = config.units; + this.widgetConfig.config.decimals = config.decimals; + + this.widgetConfig.config.settings.showLegend = config.showLegend; + this.widgetConfig.config.settings.legendPosition = config.legendPosition; + this.widgetConfig.config.settings.legendLabelFont = config.legendLabelFont; + this.widgetConfig.config.settings.legendLabelColor = config.legendLabelColor; + + this.widgetConfig.config.settings.showTooltip = config.showTooltip; + this.widgetConfig.config.settings.tooltipValueFont = config.tooltipValueFont; + this.widgetConfig.config.settings.tooltipValueColor = config.tooltipValueColor; + this.widgetConfig.config.settings.tooltipShowDate = config.tooltipShowDate; + this.widgetConfig.config.settings.tooltipDateFormat = config.tooltipDateFormat; + this.widgetConfig.config.settings.tooltipDateFont = config.tooltipDateFont; + this.widgetConfig.config.settings.tooltipDateColor = config.tooltipDateColor; + this.widgetConfig.config.settings.tooltipBackgroundColor = config.tooltipBackgroundColor; + this.widgetConfig.config.settings.tooltipBackgroundBlur = config.tooltipBackgroundBlur; + + this.widgetConfig.config.settings.background = config.background; + + this.setCardButtons(config.cardButtons, this.widgetConfig.config); + this.widgetConfig.config.borderRadius = config.borderRadius; + + this.widgetConfig.config.actions = config.actions; + return this.widgetConfig; + } + + protected validatorTriggers(): string[] { + return ['showTitle', 'showIcon', 'showLegend', 'showTooltip', 'tooltipShowDate']; + } + + protected updateValidators(emitEvent: boolean, trigger?: string) { + const showTitle: boolean = this.rangeChartWidgetConfigForm.get('showTitle').value; + const showIcon: boolean = this.rangeChartWidgetConfigForm.get('showIcon').value; + const showLegend: boolean = this.rangeChartWidgetConfigForm.get('showLegend').value; + const showTooltip: boolean = this.rangeChartWidgetConfigForm.get('showTooltip').value; + const tooltipShowDate: boolean = this.rangeChartWidgetConfigForm.get('tooltipShowDate').value; + + if (showTitle) { + this.rangeChartWidgetConfigForm.get('title').enable(); + this.rangeChartWidgetConfigForm.get('titleFont').enable(); + this.rangeChartWidgetConfigForm.get('titleColor').enable(); + this.rangeChartWidgetConfigForm.get('showIcon').enable({emitEvent: false}); + if (showIcon) { + this.rangeChartWidgetConfigForm.get('iconSize').enable(); + this.rangeChartWidgetConfigForm.get('iconSizeUnit').enable(); + this.rangeChartWidgetConfigForm.get('icon').enable(); + this.rangeChartWidgetConfigForm.get('iconColor').enable(); + } else { + this.rangeChartWidgetConfigForm.get('iconSize').disable(); + this.rangeChartWidgetConfigForm.get('iconSizeUnit').disable(); + this.rangeChartWidgetConfigForm.get('icon').disable(); + this.rangeChartWidgetConfigForm.get('iconColor').disable(); + } + } else { + this.rangeChartWidgetConfigForm.get('title').disable(); + this.rangeChartWidgetConfigForm.get('titleFont').disable(); + this.rangeChartWidgetConfigForm.get('titleColor').disable(); + this.rangeChartWidgetConfigForm.get('showIcon').disable({emitEvent: false}); + this.rangeChartWidgetConfigForm.get('iconSize').disable(); + this.rangeChartWidgetConfigForm.get('iconSizeUnit').disable(); + this.rangeChartWidgetConfigForm.get('icon').disable(); + this.rangeChartWidgetConfigForm.get('iconColor').disable(); + } + + if (showLegend) { + this.rangeChartWidgetConfigForm.get('legendPosition').enable(); + this.rangeChartWidgetConfigForm.get('legendLabelFont').enable(); + this.rangeChartWidgetConfigForm.get('legendLabelColor').enable(); + } else { + this.rangeChartWidgetConfigForm.get('legendPosition').disable(); + this.rangeChartWidgetConfigForm.get('legendLabelFont').disable(); + this.rangeChartWidgetConfigForm.get('legendLabelColor').disable(); + } + + if (showTooltip) { + this.rangeChartWidgetConfigForm.get('tooltipValueFont').enable(); + this.rangeChartWidgetConfigForm.get('tooltipValueColor').enable(); + this.rangeChartWidgetConfigForm.get('tooltipShowDate').enable({emitEvent: false}); + this.rangeChartWidgetConfigForm.get('tooltipBackgroundColor').enable(); + this.rangeChartWidgetConfigForm.get('tooltipBackgroundBlur').enable(); + if (tooltipShowDate) { + this.rangeChartWidgetConfigForm.get('tooltipDateFormat').enable(); + this.rangeChartWidgetConfigForm.get('tooltipDateFont').enable(); + this.rangeChartWidgetConfigForm.get('tooltipDateColor').enable(); + } else { + this.rangeChartWidgetConfigForm.get('tooltipDateFormat').disable(); + this.rangeChartWidgetConfigForm.get('tooltipDateFont').disable(); + this.rangeChartWidgetConfigForm.get('tooltipDateColor').disable(); + } + } else { + this.rangeChartWidgetConfigForm.get('tooltipValueFont').disable(); + this.rangeChartWidgetConfigForm.get('tooltipValueColor').disable(); + this.rangeChartWidgetConfigForm.get('tooltipShowDate').disable({emitEvent: false}); + this.rangeChartWidgetConfigForm.get('tooltipDateFormat').disable(); + this.rangeChartWidgetConfigForm.get('tooltipDateFont').disable(); + this.rangeChartWidgetConfigForm.get('tooltipDateColor').disable(); + this.rangeChartWidgetConfigForm.get('tooltipBackgroundColor').disable(); + this.rangeChartWidgetConfigForm.get('tooltipBackgroundBlur').disable(); + } + } + + private getCardButtons(config: WidgetConfig): string[] { + const buttons: string[] = []; + if (isUndefined(config.enableFullscreen) || config.enableFullscreen) { + buttons.push('fullscreen'); + } + return buttons; + } + + private setCardButtons(buttons: string[], config: WidgetConfig) { + config.enableFullscreen = buttons.includes('fullscreen'); + } + + private _tooltipValuePreviewFn(): string { + const units: string = this.rangeChartWidgetConfigForm.get('units').value; + const decimals: number = this.rangeChartWidgetConfigForm.get('decimals').value; + return formatValue(22, decimals, units, false); + } + + private _tooltipDatePreviewFn(): string { + const dateFormat: DateFormatSettings = this.rangeChartWidgetConfigForm.get('tooltipDateFormat').value; + const processor = DateFormatProcessor.fromSettings(this.$injector, dateFormat); + processor.update(Date.now()); + return processor.formatted; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html index 0ae6ffb05e..e0ddd15993 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html @@ -15,14 +15,20 @@ limitations under the License. --> -
- - - + + + {{ 'datakey.timeseries' | translate }} + {{ 'datakey.latest' | translate }} + + + + +
-
+
timeline
-
+
@@ -61,8 +67,8 @@ #keyInput [formControl]="keyFormControl" matAutocompleteOrigin - [fxHide]="!!modelValue.type" - [readonly]="!!modelValue.type" + [fxHide]="!!modelValue?.type" + [readonly]="!!modelValue?.type" #origin="matAutocompleteOrigin" [matAutocompleteConnectedTo]="origin" (focusin)="onKeyInputFocus()" @@ -133,7 +139,7 @@ - +
@@ -141,16 +147,36 @@ formControlName="color">
-
- + - +
-
- +
+ +
widget-config.decimals-suffix
+
+
+ +
+ +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.scss b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.scss index 5b3d4f1a80..9a2872a421 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.scss @@ -13,15 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -.tb-data-key-row { - height: 38px; - display: flex; - flex-direction: row; - gap: 12px; - padding-left: 12px; +@import '../../../../../../../../scss/constants'; + +.tb-data-key-row { .mat-mdc-form-field.tb-inline-field.tb-key-field { .mat-mdc-text-field-wrapper:not(.mdc-text-field--outlined) { + padding-left: 8px; + padding-right: 0; .mat-mdc-form-field-infix { padding-top: 0; padding-bottom: 6px; @@ -43,11 +42,75 @@ } } + .tb-source-field { + width: 108px; + min-width: 108px; + } + + .tb-key-field { + flex: 1; + min-width: 150px; + @media #{$mat-gt-sm} { + flex: 1 1 60%; + } + } + + .tb-label-field { + flex: 1 1 40%; + min-width: 100px; + } + .tb-color-field, .tb-units-field, .tb-decimals-field { - width: 60px; display: flex; flex-direction: row; place-content: center; align-items: center; } + + .tb-units-field { + width: 80px; + min-width: 80px; + } + + .tb-color-field { + width: 40px; + min-width: 40px; + } + + .tb-decimals-field { + width: 60px; + min-width: 60px; + } + + .tb-label-field, .tb-units-field, .tb-color-field, .tb-decimals-field { + display: none; + @media #{$mat-gt-sm} { + display: block; + } + } + + .tb-settings-button { + width: 40px; + min-width: 40px; + } + + &.tb-single-row { + padding: 0; + .tb-units-field, .tb-decimals-field { + min-width: 0; + width: auto; + } + .tb-units-field { + tb-unit-input { + width: auto; + max-width: 140px; + } + } + .tb-decimals-field { + .mat-mdc-form-field { + width: auto; + max-width: 140px; + } + } + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts index d434ef74e3..a6b6c426a0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts @@ -18,10 +18,12 @@ import { ChangeDetectorRef, Component, ElementRef, + EventEmitter, forwardRef, Input, OnChanges, OnInit, + Output, SimpleChanges, ViewChild, ViewEncapsulation @@ -33,12 +35,19 @@ import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, - ValidationErrors + ValidationErrors, + Validators } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; -import { DataKey, DatasourceType, JsonSettingsSchema, Widget, widgetType } from '@shared/models/widget.models'; -import { DataKeysPanelComponent } from '@home/components/widget/config/basic/common/data-keys-panel.component'; +import { + DataKey, + DataKeyConfigMode, + DatasourceType, + JsonSettingsSchema, + Widget, + widgetType +} from '@shared/models/widget.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { AggregationType } from '@shared/models/time/time.models'; import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; @@ -56,10 +65,15 @@ import { import { deepClone } from '@core/utils'; import { Dashboard } from '@shared/models/dashboard.models'; import { IAliasController } from '@core/api/widget-api.models'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { alarmFields } from '@shared/models/alarm.models'; +import { UtilsService } from '@core/services/utils.service'; + +export const dataKeyValid = (key: DataKey): boolean => !!key && !!key.type && !!key.name; export const dataKeyRowValidator = (control: AbstractControl): ValidationErrors | null => { const dataKey: DataKey = control.value; - if (!dataKey || !dataKey.type || !dataKey.name) { + if (!dataKeyValid(dataKey)) { return { dataKey: true }; @@ -95,6 +109,10 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan @Input() disabled: boolean; + @Input() + @coerceBoolean() + required = false; + @Input() datasourceType: DatasourceType; @@ -104,6 +122,52 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan @Input() deviceId: string; + @Input() + @coerceBoolean() + hasAdditionalLatestDataKeys = false; + + @Input() + @coerceBoolean() + hideDataKeyLabel = false; + + @Input() + @coerceBoolean() + hideDataKeyColor = false; + + @Input() + @coerceBoolean() + hideDataKeyUnits = false; + + @Input() + @coerceBoolean() + hideDataKeyDecimals = false; + + @Input() + @coerceBoolean() + hideUnits = false; + + @Input() + @coerceBoolean() + hideDecimals = false; + + @Input() + @coerceBoolean() + singleRow = true; + + @Input() + dataKeyType: DataKeyType; + + @Input() + keySettingsTitle: string; + + @Input() + removeKeyTitle: string; + + @Output() + keyRemoved = new EventEmitter(); + + keysFormControl: UntypedFormControl; + keyFormControl: UntypedFormControl; keyRowFormGroup: UntypedFormGroup; @@ -114,25 +178,12 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan keySearchText = ''; + alarmKeys: Array; + functionTypeKeys: Array; + private latestKeySearchTextResult: Array = null; private keyFetchObservable$: Observable> = null; - get dataKeyType(): DataKeyType { - return this.dataKeysPanelComponent.dataKeyType; - } - - get alarmKeys(): Array { - return this.dataKeysPanelComponent.alarmKeys; - } - - get functionTypeKeys(): Array { - return this.dataKeysPanelComponent.functionTypeKeys; - } - - get hideDataKeyColor(): boolean { - return this.dataKeysPanelComponent.hideDataKeyColor; - } - get widgetType(): widgetType { return this.widgetConfigComponent.widgetType; } @@ -153,7 +204,7 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan return this.widgetConfigComponent.aliasController; } - get datakeySettingsSchema(): JsonSettingsSchema { + get dataKeySettingsSchema(): JsonSettingsSchema { return this.widgetConfigComponent.modelValue?.dataKeySettingsSchema; } @@ -161,12 +212,24 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan return this.widgetConfigComponent.modelValue?.dataKeySettingsDirective; } + get latestDataKeySettingsSchema(): JsonSettingsSchema { + return this.widgetConfigComponent.modelValue?.latestDataKeySettingsSchema; + } + + get latestDataKeySettingsDirective(): string { + return this.widgetConfigComponent.modelValue?.latestDataKeySettingsDirective; + } + get isEntityDatasource(): boolean { return [DatasourceType.device, DatasourceType.entity].includes(this.datasourceType); } get displayUnitsOrDigits() { - return this.modelValue.type && ![ DataKeyType.alarm, DataKeyType.entityField, DataKeyType.count ].includes(this.modelValue.type); + return this.modelValue?.type && ![ DataKeyType.alarm, DataKeyType.entityField, DataKeyType.count ].includes(this.modelValue?.type); + } + + get isLatestDataKeys(): boolean { + return this.hasAdditionalLatestDataKeys && this.keyRowFormGroup.get('latest').value === true; } private propagateChange = (_val: any) => {}; @@ -176,18 +239,39 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan private cd: ChangeDetectorRef, public translate: TranslateService, public truncate: TruncatePipe, - private dataKeysPanelComponent: DataKeysPanelComponent, + private utils: UtilsService, private widgetConfigComponent: WidgetConfigComponent) { } ngOnInit() { + this.alarmKeys = []; + for (const name of Object.keys(alarmFields)) { + this.alarmKeys.push({ + name, + type: DataKeyType.alarm + }); + } + this.functionTypeKeys = []; + for (const type of this.utils.getPredefinedFunctionsList()) { + this.functionTypeKeys.push({ + name: type, + type: DataKeyType.function + }); + } this.keyFormControl = this.fb.control(''); + this.keysFormControl = this.fb.control([], this.required ? [Validators.required] : []); this.keyRowFormGroup = this.fb.group({ label: [null, []], color: [null, []], units: [null, []], decimals: [null, []], }); + if (this.hasAdditionalLatestDataKeys) { + this.keyRowFormGroup.addControl('latest', this.fb.control(false)); + this.keyRowFormGroup.valueChanges.subscribe( + () => this.clearKeySearchCache() + ); + } this.keyRowFormGroup.valueChanges.subscribe( () => this.updateModel() ); @@ -246,14 +330,16 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; if (isDisabled) { + this.keysFormControl.disable({emitEvent: false}); this.keyRowFormGroup.disable({emitEvent: false}); } else { + this.keysFormControl.enable({emitEvent: false}); this.keyRowFormGroup.enable({emitEvent: false}); } } writeValue(value: DataKey): void { - this.modelValue = value || {} as DataKey; + this.modelValue = (value?.name && value?.type) ? value : null; this.keyRowFormGroup.patchValue( { label: value?.label, @@ -262,6 +348,12 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan decimals: value?.decimals }, {emitEvent: false} ); + if (this.hasAdditionalLatestDataKeys) { + this.keyRowFormGroup.patchValue({ + latest: (value as any)?.latest + }, {emitEvent: false}); + } + this.keysFormControl.patchValue(this.modelValue ? [this.modelValue] : [], {emitEvent: false}); this.cd.markForCheck(); } @@ -291,15 +383,16 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan } } - editKey() { + editKey(advanced = false) { this.dialog.open(DataKeyConfigDialogComponent, { disableClose: true, panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { dataKey: deepClone(this.modelValue), - dataKeySettingsSchema: this.datakeySettingsSchema, - dataKeySettingsDirective: this.dataKeySettingsDirective, + dataKeyConfigMode: advanced ? DataKeyConfigMode.advanced : DataKeyConfigMode.general, + dataKeySettingsSchema: this.isLatestDataKeys ? this.latestDataKeySettingsSchema : this.dataKeySettingsSchema, + dataKeySettingsDirective: this.isLatestDataKeys ? this.latestDataKeySettingsDirective : this.dataKeySettingsDirective, dashboard: this.dashboard, aliasController: this.aliasController, widget: this.widget, @@ -308,10 +401,10 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan entityAliasId: this.entityAliasId, showPostProcessing: this.widgetType !== widgetType.alarm, callbacks: this.callbacks, - hideDataKeyLabel: false, + hideDataKeyLabel: this.hideDataKeyLabel, hideDataKeyColor: this.hideDataKeyColor, - hideDataKeyUnits: !this.displayUnitsOrDigits, - hideDataKeyDecimals: !this.displayUnitsOrDigits + hideDataKeyUnits: this.hideDataKeyUnits || !this.displayUnitsOrDigits, + hideDataKeyDecimals: this.hideDataKeyDecimals || !this.displayUnitsOrDigits } }).afterClosed().subscribe((updatedDataKey) => { if (updatedDataKey) { @@ -321,12 +414,13 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan this.keyRowFormGroup.get('units').patchValue(this.modelValue.units, {emitEvent: false}); this.keyRowFormGroup.get('decimals').patchValue(this.modelValue.decimals, {emitEvent: false}); this.updateModel(); + this.cd.markForCheck(); } }); } removeKey() { - this.modelValue = {} as DataKey; + this.modelValue = null; this.updateModel(); this.clearKeyChip(); } @@ -348,7 +442,7 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan } onKeyInputFocus() { - if (!this.modelValue.type) { + if (!this.modelValue?.type) { this.keyFormControl.updateValueAndValidity({onlySelf: true, emitEvent: true}); } } @@ -374,7 +468,7 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan } else if (this.datasourceType === DatasourceType.entity && this.entityAliasId || this.datasourceType === DatasourceType.device && this.deviceId) { const dataKeyTypes = [DataKeyType.timeseries]; - if (this.widgetType === widgetType.latest || this.widgetType === widgetType.alarm) { + if (this.isLatestDataKeys || this.widgetType === widgetType.latest || this.widgetType === widgetType.alarm) { dataKeyTypes.push(DataKeyType.attribute); dataKeyTypes.push(DataKeyType.entityField); if (this.widgetType === widgetType.alarm) { @@ -403,7 +497,7 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan } private addKeyFromChipValue(chip: DataKey) { - this.modelValue = this.callbacks.generateDataKey(chip.name, chip.type, this.datakeySettingsSchema); + this.modelValue = this.callbacks.generateDataKey(chip.name, chip.type, this.dataKeySettingsSchema); if (!this.keyRowFormGroup.get('label').value) { this.keyRowFormGroup.get('label').patchValue(this.modelValue.label, {emitEvent: false}); } @@ -418,8 +512,11 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan } private updateModel() { - const value: DataKey = this.keyRowFormGroup.value; - this.modelValue = {...this.modelValue, ...value}; + this.keysFormControl.patchValue(this.modelValue ? [this.modelValue] : [], {emitEvent: false}); + if (this.modelValue !== null) { + const value: DataKey = this.keyRowFormGroup.value; + this.modelValue = {...this.modelValue, ...value}; + } this.propagateChange(this.modelValue); } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.html index 754f0052c9..e7a738dc82 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.html @@ -15,38 +15,49 @@ limitations under the License. --> -
-
{{ panelTitle }}
-
-
-
datakey.key
-
datakey.label
-
datakey.color
-
widget-config.units-short
-
widget-config.decimals-short
-
+
+
{{ panelTitle }}
+
+
+
datakey.source
+
datakey.key
+
datakey.label
+
datakey.color
+
widget-config.units-short
+
widget-config.decimals-short
+
-
-
+
+ [entityAliasId]="entityAliasId" + [hasAdditionalLatestDataKeys]="hasAdditionalLatestDataKeys" + [hideDataKeyColor]="hideDataKeyColor" + [hideDecimals]="hideDecimals" + [hideUnits]="hideUnits" + [hideDataKeyDecimals]="hideDataKeyDecimals" + [hideDataKeyUnits]="hideDataKeyUnits" + [dataKeyType]="dataKeyType" + [singleRow]="false" + [keySettingsTitle]="keySettingsTitle" + [removeKeyTitle]="removeKeyTitle" + (keyRemoved)="removeKey($index)"> -
- -
+
+ +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style-panel.component.scss new file mode 100644 index 0000000000..d3c2dfb4fb --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style-panel.component.scss @@ -0,0 +1,54 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import '../../../../../../scss/constants'; + +.tb-timewindow-style-panel { + width: 100%; + display: flex; + flex-direction: column; + gap: 16px; + @media #{$mat-gt-xs} { + min-width: 320px; + } + .tb-timewindow-style-title { + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.87); + } + .tb-form-row { + .fixed-title-width { + min-width: 120px; + } + &.timewindow-preview { + align-items: flex-start; + tb-timewindow { + font-size: 14px; + opacity: .85; + } + } + } + .tb-timewindow-style-panel-buttons { + height: 60px; + display: flex; + flex-direction: row; + gap: 16px; + justify-content: flex-end; + align-items: flex-end; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style-panel.component.ts new file mode 100644 index 0000000000..85e7e5a4be --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style-panel.component.ts @@ -0,0 +1,109 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { defaultTimewindowStyle, TimewindowStyle } from '@shared/models/widget-settings.models'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { Timewindow } from '@shared/models/time/time.models'; +import { deepClone } from '@core/utils'; + +@Component({ + selector: 'tb-timewindow-style-panel', + templateUrl: './timewindow-style-panel.component.html', + providers: [], + styleUrls: ['./timewindow-style-panel.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class TimewindowStylePanelComponent extends PageComponent implements OnInit { + + @Input() + timewindowStyle: TimewindowStyle; + + @Input() + previewValue: Timewindow; + + @Input() + popover: TbPopoverComponent; + + @Output() + timewindowStyleApplied = new EventEmitter(); + + timewindowStyleFormGroup: UntypedFormGroup; + + previewTimewindowStyle: TimewindowStyle; + + constructor(private fb: UntypedFormBuilder, + protected store: Store) { + super(store); + } + + ngOnInit(): void { + const computedTimewindowStyle = {...defaultTimewindowStyle, ...(this.timewindowStyle || {})}; + this.timewindowStyleFormGroup = this.fb.group( + { + showIcon: [computedTimewindowStyle.showIcon, []], + iconSize: [computedTimewindowStyle.iconSize, []], + icon: [computedTimewindowStyle.icon, []], + iconPosition: [computedTimewindowStyle.iconPosition, []], + font: [computedTimewindowStyle.font, []], + color: [computedTimewindowStyle.color, []], + displayTypePrefix: [computedTimewindowStyle.displayTypePrefix, []] + } + ); + this.updatePreviewStyle(this.timewindowStyle); + this.updateTimewindowStyleEnabledState(); + this.timewindowStyleFormGroup.valueChanges.subscribe((timewindowStyle: TimewindowStyle) => { + if (this.timewindowStyleFormGroup.valid) { + this.updatePreviewStyle(timewindowStyle); + setTimeout(() => {this.popover?.updatePosition();}, 0); + } + }); + this.timewindowStyleFormGroup.get('showIcon').valueChanges.subscribe(() => { + this.updateTimewindowStyleEnabledState(); + }); + } + + cancel() { + this.popover?.hide(); + } + + applyTimewindowStyle() { + const timewindowStyle = this.timewindowStyleFormGroup.getRawValue(); + this.timewindowStyleApplied.emit(timewindowStyle); + } + + private updateTimewindowStyleEnabledState() { + const showIcon: boolean = this.timewindowStyleFormGroup.get('showIcon').value; + if (showIcon) { + this.timewindowStyleFormGroup.get('iconSize').enable({emitEvent: false}); + this.timewindowStyleFormGroup.get('icon').enable({emitEvent: false}); + this.timewindowStyleFormGroup.get('iconPosition').enable({emitEvent: false}); + } else { + this.timewindowStyleFormGroup.get('iconSize').disable({emitEvent: false}); + this.timewindowStyleFormGroup.get('icon').disable({emitEvent: false}); + this.timewindowStyleFormGroup.get('iconPosition').disable({emitEvent: false}); + } + } + + private updatePreviewStyle(timewindowStyle: TimewindowStyle) { + this.previewTimewindowStyle = deepClone(timewindowStyle); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style.component.html b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style.component.html new file mode 100644 index 0000000000..2bd981fffc --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style.component.html @@ -0,0 +1,25 @@ + + diff --git a/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style.component.ts new file mode 100644 index 0000000000..ad6d3e1b85 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/timewindow-style.component.ts @@ -0,0 +1,97 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, forwardRef, Input, OnInit, Renderer2, ViewContainerRef } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { TimewindowStyle } from '@shared/models/widget-settings.models'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { Timewindow } from '@shared/models/time/time.models'; +import { TimewindowStylePanelComponent } from '@home/components/widget/config/timewindow-style-panel.component'; + +@Component({ + selector: 'tb-timewindow-style', + templateUrl: './timewindow-style.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => TimewindowStyleComponent), + multi: true + } + ] +}) +export class TimewindowStyleComponent implements OnInit, ControlValueAccessor { + + @Input() + disabled: boolean; + + @Input() + previewValue: Timewindow; + + private modelValue: TimewindowStyle; + + private propagateChange = null; + + constructor(private popoverService: TbPopoverService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef) {} + + ngOnInit(): void { + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + } + + writeValue(value: TimewindowStyle): void { + this.modelValue = value; + } + + openTimewindowStylePopup($event: Event, matButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = matButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const ctx: any = { + timewindowStyle: this.modelValue, + previewValue: this.previewValue + }; + const timewindowStylePanelPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, TimewindowStylePanelComponent, 'left', true, null, + ctx, + {}, + {}, {}, true); + timewindowStylePanelPopover.tbComponentRef.instance.popover = timewindowStylePanelPopover; + timewindowStylePanelPopover.tbComponentRef.instance.timewindowStyleApplied.subscribe((timewindowStyle) => { + timewindowStylePanelPopover.hide(); + this.modelValue = timewindowStyle; + this.propagateChange(this.modelValue); + }); + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts index 186a89537e..ea0d27dcf6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/widget-config-components.module.ts @@ -29,7 +29,9 @@ import { FilterSelectComponent } from '@home/components/filter/filter-select.com import { WidgetSettingsModule } from '@home/components/widget/lib/settings/widget-settings.module'; import { WidgetSettingsComponent } from '@home/components/widget/config/widget-settings.component'; import { TimewindowConfigPanelComponent } from '@home/components/widget/config/timewindow-config-panel.component'; -import { WidgetUnitsComponent } from '@home/components/widget/config/widget-units.component'; +import { WidgetSettingsCommonModule } from '@home/components/widget/lib/settings/common/widget-settings-common.module'; +import { TimewindowStyleComponent } from '@home/components/widget/config/timewindow-style.component'; +import { TimewindowStylePanelComponent } from '@home/components/widget/config/timewindow-style-panel.component'; @NgModule({ declarations: @@ -43,14 +45,16 @@ import { WidgetUnitsComponent } from '@home/components/widget/config/widget-unit DatasourcesComponent, EntityAliasSelectComponent, FilterSelectComponent, + TimewindowStyleComponent, + TimewindowStylePanelComponent, TimewindowConfigPanelComponent, - WidgetUnitsComponent, WidgetSettingsComponent ], imports: [ CommonModule, SharedModule, - WidgetSettingsModule + WidgetSettingsModule, + WidgetSettingsCommonModule ], exports: [ AlarmAssigneeSelectComponent, @@ -62,9 +66,11 @@ import { WidgetUnitsComponent } from '@home/components/widget/config/widget-unit DatasourcesComponent, EntityAliasSelectComponent, FilterSelectComponent, + TimewindowStyleComponent, + TimewindowStylePanelComponent, TimewindowConfigPanelComponent, - WidgetUnitsComponent, - WidgetSettingsComponent + WidgetSettingsComponent, + WidgetSettingsCommonModule ] }) export class WidgetConfigComponentsModule { } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts b/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts index 9bda9345dc..d021af6b20 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/widget-config.component.models.ts @@ -69,11 +69,11 @@ export abstract class BasicWidgetConfigComponent extends PageComponent implement ngOnInit() {} ngAfterViewInit(): void { - setTimeout(() => { - if (!this.validateConfig()) { - this.onConfigChanged(this.prepareOutputConfig(this.configForm().getRawValue())); - } - }, 0); + if (!this.validateConfig()) { + setTimeout(() => { + this.onConfigChanged(this.prepareOutputConfig(this.configForm().getRawValue())); + }, 0); + } } protected setupConfig(widgetConfig: WidgetConfigComponentData) { @@ -97,7 +97,34 @@ export abstract class BasicWidgetConfigComponent extends PageComponent implement }); } - protected setupDefaults(configData: WidgetConfigComponentData) {} + protected setupDefaults(configData: WidgetConfigComponentData) { + const params = configData.typeParameters; + let dataKeys: DataKey[]; + let latestDataKeys: DataKey[]; + if (params.defaultDataKeysFunction) { + dataKeys = params.defaultDataKeysFunction(this, configData); + } + if (params.defaultLatestDataKeysFunction) { + latestDataKeys = params.defaultLatestDataKeysFunction(this, configData); + } + if (!dataKeys) { + dataKeys = this.defaultDataKeys(configData); + } + if (!latestDataKeys) { + latestDataKeys = this.defaultLatestDataKeys(configData); + } + if (dataKeys || latestDataKeys) { + this.setupDefaultDatasource(configData, dataKeys, latestDataKeys); + } + } + + protected defaultDataKeys(configData: WidgetConfigComponentData): DataKey[] { + return null; + } + + protected defaultLatestDataKeys(configData: WidgetConfigComponentData): DataKey[] { + return null; + } protected updateValidators(emitEvent: boolean, trigger?: string) { } @@ -119,7 +146,7 @@ export abstract class BasicWidgetConfigComponent extends PageComponent implement return this.configForm().valid; } - protected setupDefaultDatasource(configData: WidgetConfigComponentData, keys?: DataKey[]) { + protected setupDefaultDatasource(configData: WidgetConfigComponentData, keys?: DataKey[], latestKeys?: DataKey[]) { let datasources = configData.config.datasources; if (!datasources || !datasources.length) { datasources = [ @@ -135,23 +162,61 @@ export abstract class BasicWidgetConfigComponent extends PageComponent implement dataKeys = []; datasources[0].dataKeys = dataKeys; } + let latestDataKeys = datasources[0].latestDataKeys; + if (!latestDataKeys) { + latestDataKeys = []; + datasources[0].latestDataKeys = latestDataKeys; + } if (keys && keys.length) { dataKeys.length = 0; keys.forEach(key => { - const dataKey = - this.widgetConfigComponent.widgetConfigCallbacks.generateDataKey(key.name, key.type, configData.dataKeySettingsSchema); - if (key.label) { - dataKey.label = key.label; - } - if (key.units) { - dataKey.units = key.units; - } - if (isDefinedAndNotNull(key.decimals)) { - dataKey.decimals = key.decimals; - } + const dataKey = this.constructDataKey(configData, key); dataKeys.push(dataKey); }); } + if (latestKeys && latestKeys.length) { + latestDataKeys.length = 0; + latestKeys.forEach(key => { + const dataKey = this.constructDataKey(configData, key); + latestDataKeys.push(dataKey); + }); + } + } + + protected constructDataKey(configData: WidgetConfigComponentData, key: DataKey): DataKey { + const dataKey = + this.widgetConfigComponent.widgetConfigCallbacks.generateDataKey(key.name, key.type, configData.dataKeySettingsSchema); + if (key.label) { + dataKey.label = key.label; + } + if (key.units) { + dataKey.units = key.units; + } + if (isDefinedAndNotNull(key.decimals)) { + dataKey.decimals = key.decimals; + } + if (key.color) { + dataKey.color = key.color; + } + if (isDefinedAndNotNull(key.settings)) { + dataKey.settings = key.settings; + } + if (isDefinedAndNotNull(key.aggregationType)) { + dataKey.aggregationType = key.aggregationType; + } + if (isDefinedAndNotNull(key.comparisonEnabled)) { + dataKey.comparisonEnabled = key.comparisonEnabled; + } + if (isDefinedAndNotNull(key.timeForComparison)) { + dataKey.timeForComparison = key.timeForComparison; + } + if (isDefinedAndNotNull(key.comparisonCustomIntervalValue)) { + dataKey.comparisonCustomIntervalValue = key.comparisonCustomIntervalValue; + } + if (isDefinedAndNotNull(key.comparisonResultType)) { + dataKey.comparisonResultType = key.comparisonResultType; + } + return dataKey; } protected abstract configForm(): UntypedFormGroup; diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts index cc482bfd4e..65480ec3b8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts @@ -29,10 +29,14 @@ import { ViewContainerRef } from '@angular/core'; import { + AbstractControl, ControlValueAccessor, + NG_VALIDATORS, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup, + ValidationErrors, + Validator, Validators } from '@angular/forms'; import { Subscription } from 'rxjs'; @@ -44,6 +48,7 @@ import { widgetSettingsComponentsMap } from '@home/components/widget/lib/setting import { Dashboard } from '@shared/models/dashboard.models'; import { WidgetService } from '@core/http/widget.service'; import { IAliasController } from '@core/api/widget-api.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; @Component({ selector: 'tb-widget-settings', @@ -53,9 +58,14 @@ import { IAliasController } from '@core/api/widget-api.models'; provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => WidgetSettingsComponent), multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => WidgetSettingsComponent), + multi: true }] }) -export class WidgetSettingsComponent implements ControlValueAccessor, OnInit, OnDestroy, AfterViewInit, OnChanges { +export class WidgetSettingsComponent implements ControlValueAccessor, OnInit, OnDestroy, AfterViewInit, OnChanges, Validator { @ViewChild('definedSettingsContent', {read: ViewContainerRef, static: true}) definedSettingsContainer: ViewContainerRef; @@ -73,6 +83,9 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnInit, On @Input() widget: Widget; + @Input() + widgetConfig: WidgetConfigComponentData; + private settingsDirective: string; definedDirectiveError: string; @@ -85,7 +98,6 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnInit, On private definedSettingsComponent: IWidgetSettingsComponent; private widgetSettingsFormData: JsonFormComponentData; - private propagateChange = (v: any) => { }; constructor(private translate: TranslateService, @@ -126,6 +138,11 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnInit, On this.definedSettingsComponent.aliasController = this.aliasController; } } + if (propName === 'widgetConfig') { + if (this.definedSettingsComponent) { + this.definedSettingsComponent.widgetConfig = this.widgetConfig; + } + } } } } @@ -184,11 +201,7 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnInit, On private updateModel(settings: WidgetSettings) { this.widgetSettingsFormData.model = settings; - if (this.definedSettingsComponent || this.widgetSettingsFormGroup.valid) { - this.propagateChange(this.widgetSettingsFormData); - } else { - this.propagateChange(null); - } + this.propagateChange(this.widgetSettingsFormData); } private validateDefinedDirective() { @@ -214,6 +227,7 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnInit, On this.definedSettingsComponent.aliasController = this.aliasController; this.definedSettingsComponent.dashboard = this.dashboard; this.definedSettingsComponent.widget = this.widget; + this.definedSettingsComponent.widgetConfig = this.widgetConfig; this.definedSettingsComponent.functionScopeVariables = this.widgetService.getWidgetScopeVariables(); this.changeSubscription = this.definedSettingsComponent.settingsChanged.subscribe((settings) => { this.updateModel(settings); @@ -222,9 +236,24 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnInit, On } } - validate() { + validate(control: AbstractControl): ValidationErrors | null { if (this.useDefinedDirective()) { - this.definedSettingsComponent.validate(); + if (!this.definedSettingsComponent.validateSettings()) { + return { + widgetSettings: { + valid: false + } + }; + } + } else if (this.useJsonForm()) { + if (!this.widgetSettingsFormGroup.valid) { + return { + widgetSettings: { + valid: false + } + }; + } } + return null; } } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-units.component.html b/ui-ngx/src/app/modules/home/components/widget/config/widget-units.component.html deleted file mode 100644 index a2ed480388..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-units.component.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-units.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/widget-units.component.ts deleted file mode 100644 index e61511698d..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-units.component.ts +++ /dev/null @@ -1,68 +0,0 @@ -/// -/// Copyright © 2016-2023 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -import { Component, forwardRef, Input, OnInit } from '@angular/core'; -import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR, UntypedFormBuilder } from '@angular/forms'; - -@Component({ - selector: 'tb-widget-units', - templateUrl: './widget-units.component.html', - styleUrls: [], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => WidgetUnitsComponent), - multi: true - } - ] -}) -export class WidgetUnitsComponent implements ControlValueAccessor, OnInit { - - @Input() - disabled: boolean; - - unitsFormControl: FormControl; - - private propagateChange = (_val: any) => {}; - - constructor(private fb: UntypedFormBuilder) { - } - - ngOnInit() { - this.unitsFormControl = this.fb.control('', []); - this.unitsFormControl.valueChanges.subscribe(val => this.propagateChange(val)); - } - - writeValue(units?: string): void { - this.unitsFormControl.patchValue(units, {emitEvent: false}); - } - - registerOnChange(fn: any): void { - this.propagateChange = fn; - } - - registerOnTouched(fn: any): void { - } - - setDisabledState(isDisabled: boolean): void { - this.disabled = isDisabled; - if (this.disabled) { - this.unitsFormControl.disable({emitEvent: false}); - } else { - this.unitsFormControl.enable({emitEvent: false}); - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog-container.component.ts b/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog-container.component.ts index dc3e9c7ac2..3c20317b13 100644 --- a/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog-container.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog-container.component.ts @@ -20,8 +20,8 @@ import { ComponentFactory, ComponentRef, HostBinding, Inject, - Injector, - OnDestroy, + Injector, NgModuleRef, + OnDestroy, Type, ViewContainerRef } from '@angular/core'; import { DialogComponent } from '@shared/components/dialog.component'; @@ -35,11 +35,13 @@ import { } from '@home/components/widget/dialog/custom-dialog.component'; import { DialogService } from '@core/services/dialog.service'; import { TranslateService } from '@ngx-translate/core'; +import { DynamicComponentModule } from '@core/services/dynamic-component-factory.service'; export interface CustomDialogContainerData { controller: (instance: CustomDialogComponent) => void; data?: any; - customComponentFactory: ComponentFactory; + customComponentType: Type; + customComponentModuleRef: NgModuleRef; } @Component({ @@ -77,7 +79,8 @@ export class CustomDialogContainerComponent extends DialogComponent> + private customModules: Array>; constructor( - private translate: TranslateService, - private authService: AuthService, private dynamicComponentFactoryService: DynamicComponentFactoryService, @Inject(SHARED_MODULE_TOKEN) private sharedModule: Type, @Inject(SHARED_HOME_COMPONENTS_MODULE_TOKEN) private sharedHomeComponentsModule: Type, @Inject(HOME_COMPONENTS_MODULE_TOKEN) private homeComponentsModule: Type, + @Inject(WIDGET_COMPONENTS_MODULE_TOKEN) private widgetComponentsModule: Type, public dialog: MatDialog ) { } @@ -52,16 +53,18 @@ export class CustomDialogService { customDialog(template: string, controller: (instance: CustomDialogComponent) => void, data?: any, config?: MatDialogConfig): Observable { - const modules = [this.sharedModule, CommonModule, this.sharedHomeComponentsModule, this.homeComponentsModule]; + const modules = [this.sharedModule, CommonModule, this.sharedHomeComponentsModule, this.homeComponentsModule, + this.widgetComponentsModule]; if (Array.isArray(this.customModules)) { modules.push(...this.customModules); } - return this.dynamicComponentFactoryService.createDynamicComponentFactory( + return this.dynamicComponentFactoryService.createDynamicComponent( class CustomDialogComponentInstance extends CustomDialogComponent {}, template, modules).pipe( - mergeMap((factory) => { + mergeMap((componentData) => { const dialogData: CustomDialogContainerData = { controller, - customComponentFactory: factory, + customComponentType: componentData.componentType, + customComponentModuleRef: componentData.componentModuleRef, data }; let dialogConfig: MatDialogConfig = { @@ -76,7 +79,7 @@ export class CustomDialogService { CustomDialogContainerComponent, dialogConfig).afterClosed().pipe( tap(() => { - this.dynamicComponentFactoryService.destroyDynamicComponentFactory(factory); + this.dynamicComponentFactoryService.destroyDynamicComponent(componentData.componentType); }) ); } diff --git a/ui-ngx/src/app/modules/home/components/widget/dialog/embed-dashboard-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/dialog/embed-dashboard-dialog.component.html index 36837414ed..e45da616f3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/dialog/embed-dashboard-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/dialog/embed-dashboard-dialog.component.html @@ -35,7 +35,7 @@ type="button" [disabled]="(isLoading$ | async)" (click)="close()" cdkFocusInitial> - Close + {{ 'action.close' | translate }}
diff --git a/ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts index 22fac750b5..0cca709152 100644 --- a/ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts @@ -15,7 +15,7 @@ /// import { PageComponent } from '@shared/components/page.component'; -import { Directive, Injector, OnDestroy, OnInit } from '@angular/core'; +import { Directive, Injector, OnDestroy, OnInit, TemplateRef } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { IDynamicWidgetComponent, WidgetContext } from '@home/models/widget-component.models'; @@ -48,6 +48,7 @@ import { Router } from '@angular/router'; import { TbInject } from '@shared/decorators/tb-inject'; import { MillisecondsToTimeStringPipe } from '@shared/pipe/milliseconds-to-time-string.pipe'; import { UserSettingsService } from '@core/http/user-settings.service'; +import { ImagePipe } from '@shared/pipe/image.pipe'; @Directive() // eslint-disable-next-line @angular-eslint/directive-class-suffix @@ -67,7 +68,8 @@ export class DynamicWidgetComponent extends PageComponent implements IDynamicWid @TbInject(UntypedFormBuilder) public fb: UntypedFormBuilder, @TbInject(Injector) public readonly $injector: Injector, @TbInject('widgetContext') public readonly ctx: WidgetContext, - @TbInject('errorMessages') public readonly errorMessages: string[]) { + @TbInject('errorMessages') public readonly errorMessages: string[], + @TbInject('widgetTitlePanel') public readonly widgetTitlePanel: TemplateRef) { super(store); this.ctx.$injector = $injector; this.ctx.deviceService = $injector.get(DeviceService); @@ -86,6 +88,7 @@ export class DynamicWidgetComponent extends PageComponent implements IDynamicWid this.ctx.userSettingsService = $injector.get(UserSettingsService); this.ctx.telemetryWsService = $injector.get(TelemetryWebsocketService); this.ctx.date = $injector.get(DatePipe); + this.ctx.imagePipe = $injector.get(ImagePipe); this.ctx.milliSecondsToTimeString = $injector.get(MillisecondsToTimeStringPipe); this.ctx.translate = $injector.get(TranslateService); this.ctx.http = $injector.get(HttpClient); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.html new file mode 100644 index 0000000000..30671fb04b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.html @@ -0,0 +1,185 @@ + +
+
+ +
+ + +   + + + +
+
+ +
+ + {{ translate.get('alarm.selected-alarms', + {count: alarmsDatasource.selection.selected.length}) | async }} + + + + +
+
+
+ + + + + + + + + + + + + + {{ column.title }} + + + + + + + + {{ getUserInitials(alarm) }} + + + {{ getUserDisplayName(alarm) }} + + + + account_circle + + alarm.unassigned + + + + + + + + + + + +
+ + + + +
+
+ + + + + + +
+
+
+ + +
+ {{ noDataDisplayMessageText }} + {{ 'common.loading' | translate }} +
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.scss similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.scss rename to ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.scss diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts new file mode 100644 index 0000000000..20f123972a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts @@ -0,0 +1,1358 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + EventEmitter, + Injector, + Input, + NgZone, + OnDestroy, + OnInit, + StaticProvider, + ViewChild, + ViewContainerRef +} from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { WidgetAction, WidgetContext } from '@home/models/widget-component.models'; +import { DataKey, WidgetActionDescriptor, WidgetConfig } from '@shared/models/widget.models'; +import { IWidgetSubscription } from '@core/api/widget-api.models'; +import { UtilsService } from '@core/services/utils.service'; +import { TranslateService } from '@ngx-translate/core'; +import { deepClone, hashCode, isDefined, isDefinedAndNotNull, isNumber, isObject, isUndefined } from '@core/utils'; +import cssjs from '@core/css/css'; +import { sortItems } from '@shared/models/page/page-link'; +import { Direction } from '@shared/models/page/sort-order'; +import { CollectionViewer, DataSource, SelectionModel } from '@angular/cdk/collections'; +import { BehaviorSubject, forkJoin, fromEvent, merge, Observable, Subject, Subscription } from 'rxjs'; +import { emptyPageData, PageData } from '@shared/models/page/page-data'; +import { debounceTime, distinctUntilChanged, map, take, takeUntil, tap } from 'rxjs/operators'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort, SortDirection } from '@angular/material/sort'; +import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; +import { + CellContentInfo, + CellStyleInfo, + checkHasActions, + constructTableCssString, + DisplayColumn, + EntityColumn, + entityDataSortOrderFromString, + findColumnByEntityKey, + findEntityKeyByColumnDef, + fromEntityColumnDef, + getAlarmValue, + getCellContentInfo, + getCellStyleInfo, + getColumnDefaultVisibility, + getColumnSelectionAvailability, + getColumnWidth, + getHeaderTitle, + getRowStyleInfo, + getTableCellButtonActions, + noDataMessage, + prepareTableCellButtonActions, + RowStyleInfo, + TableCellButtonActionDescriptor, + TableWidgetDataKeySettings, + TableWidgetSettings, + widthStyle +} from '@home/components/widget/lib/table-widget.models'; +import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; +import { ComponentPortal } from '@angular/cdk/portal'; +import { + DISPLAY_COLUMNS_PANEL_DATA, + DisplayColumnsPanelComponent, + DisplayColumnsPanelData +} from '@home/components/widget/lib/display-columns-panel.component'; +import { AlarmDataInfo, alarmFields, AlarmInfo, alarmSeverityColors, AlarmStatus } from '@shared/models/alarm.models'; +import { DatePipe } from '@angular/common'; +import { + AlarmDetailsDialogComponent, + AlarmDetailsDialogData +} from '@home/components/alarm/alarm-details-dialog.component'; +import { MatDialog } from '@angular/material/dialog'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { DialogService } from '@core/services/dialog.service'; +import { AlarmService } from '@core/http/alarm.service'; +import { + AlarmData, + AlarmDataPageLink, + dataKeyToEntityKey, + dataKeyTypeToEntityKeyType, + entityDataPageLinkSortDirection, + KeyFilter +} from '@app/shared/models/query/query.models'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { entityFields } from '@shared/models/entity.models'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { hidePageSizePixelValue } from '@shared/models/constants'; +import { + ALARM_ASSIGNEE_PANEL_DATA, + AlarmAssigneePanelComponent, + AlarmAssigneePanelData +} from '@home/components/alarm/alarm-assignee-panel.component'; +import { + AlarmCommentDialogComponent, + AlarmCommentDialogData +} from '@home/components/alarm/alarm-comment-dialog.component'; +import { EntityService } from '@core/http/entity.service'; +import { + ALARM_FILTER_CONFIG_DATA, + AlarmFilterConfigComponent, + AlarmFilterConfigData +} from '@home/components/alarm/alarm-filter-config.component'; +import { getCurrentAuthUser } from '@core/auth/auth.selectors'; +import { FormBuilder } from '@angular/forms'; +import { DEFAULT_OVERLAY_POSITIONS } from '@shared/models/overlay.models'; + +interface AlarmsTableWidgetSettings extends TableWidgetSettings { + alarmsTitle: string; + enableSelectColumnDisplay: boolean; + defaultSortOrder: string; + enableSelection: boolean; + enableStatusFilter?: boolean; + enableFilter: boolean; + displayActivity: boolean; + displayDetails: boolean; + allowAcknowledgment: boolean; + allowClear: boolean; + allowAssign: boolean; +} + +interface AlarmWidgetActionDescriptor extends TableCellButtonActionDescriptor { + details?: boolean; + acknowledge?: boolean; + clear?: boolean; + activity?: boolean; +} + +@Component({ + selector: 'tb-alarms-table-widget', + templateUrl: './alarms-table-widget.component.html', + styleUrls: ['./alarms-table-widget.component.scss', './../table-widget.scss'] +}) +export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, OnDestroy, AfterViewInit { + + + @Input() + ctx: WidgetContext; + + @ViewChild('searchInput') searchInputField: ElementRef; + @ViewChild(MatPaginator) paginator: MatPaginator; + @ViewChild(MatSort) sort: MatSort; + + textSearch = this.fb.control('', {nonNullable: true}); + + public enableSelection = true; + public displayPagination = true; + public enableStickyHeader = true; + public enableStickyAction = false; + public showCellActionsMenu = true; + public pageSizeOptions; + public pageLink: AlarmDataPageLink; + public sortOrderProperty: string; + public textSearchMode = false; + public hidePageSize = false; + public columns: Array = []; + public displayedColumns: string[] = []; + public alarmsDatasource: AlarmsDatasource; + public noDataDisplayMessageText: string; + public hasRowAction: boolean; + private setCellButtonAction: boolean; + + private cellContentCache: Array = []; + private cellStyleCache: Array = []; + private rowStyleCache: Array = []; + + private settings: AlarmsTableWidgetSettings; + private widgetConfig: WidgetConfig; + private subscription: IWidgetSubscription; + private widgetResize$: ResizeObserver; + private destroy$ = new Subject(); + + private displayActivity = false; + private displayDetails = true; + public allowAcknowledgment = true; + private allowClear = true; + public allowAssign = true; + + private defaultPageSize = 10; + private defaultSortOrder = '-' + alarmFields.createdTime.value; + + private contentsInfo: {[key: string]: CellContentInfo} = {}; + private stylesInfo: {[key: string]: CellStyleInfo} = {}; + private columnWidth: {[key: string]: string} = {}; + private columnDefaultVisibility: {[key: string]: boolean} = {}; + private columnSelectionAvailability: {[key: string]: boolean} = {}; + + private rowStylesInfo: RowStyleInfo; + + private widgetTimewindowChanged$: Subscription; + + private searchAction: WidgetAction = { + name: 'action.search', + show: true, + icon: 'search', + onAction: () => { + this.enterFilterMode(); + } + }; + + private columnDisplayAction: WidgetAction = { + name: 'entity.columns-to-display', + show: true, + icon: 'view_column', + onAction: ($event) => { + this.editColumnsToDisplay($event); + } + }; + + private alarmFilterAction: WidgetAction = { + name: 'alarm.alarm-filter', + show: true, + onAction: ($event) => { + this.editAlarmFilter($event); + }, + icon: 'filter_list' + }; + + constructor(protected store: Store, + private elementRef: ElementRef, + private ngZone: NgZone, + private overlay: Overlay, + private viewContainerRef: ViewContainerRef, + private utils: UtilsService, + public translate: TranslateService, + private domSanitizer: DomSanitizer, + private datePipe: DatePipe, + private dialog: MatDialog, + private dialogService: DialogService, + private entityService: EntityService, + private alarmService: AlarmService, + private cd: ChangeDetectorRef, + private fb: FormBuilder) { + super(store); + this.pageLink = { + page: 0, + pageSize: this.defaultPageSize, + textSearch: null + }; + } + + ngOnInit(): void { + this.ctx.$scope.alarmsTableWidget = this; + this.settings = this.ctx.settings; + this.widgetConfig = this.ctx.widgetConfig; + this.subscription = this.ctx.defaultSubscription; + this.initializeConfig(); + this.updateAlarmSource(); + this.ctx.updateWidgetParams(); + + if (this.displayPagination) { + this.widgetTimewindowChanged$ = this.ctx.defaultSubscription.widgetTimewindowChanged$.subscribe( + () => this.pageLink.page = 0 + ); + this.widgetResize$ = new ResizeObserver(() => { + const showHidePageSize = this.elementRef.nativeElement.offsetWidth < hidePageSizePixelValue; + if (showHidePageSize !== this.hidePageSize) { + this.hidePageSize = showHidePageSize; + this.cd.markForCheck(); + } + }); + this.widgetResize$.observe(this.elementRef.nativeElement); + } + } + + ngOnDestroy(): void { + if (this.widgetTimewindowChanged$) { + this.widgetTimewindowChanged$.unsubscribe(); + this.widgetTimewindowChanged$ = null; + } + if (this.widgetResize$) { + this.widgetResize$.disconnect(); + } + this.destroy$.next(); + this.destroy$.complete(); + } + + ngAfterViewInit(): void { + this.textSearch.valueChanges.pipe( + debounceTime(150), + distinctUntilChanged((prev, current) => (this.pageLink.textSearch ?? '') === current.trim()), + takeUntil(this.destroy$) + ).subscribe((value) => { + this.resetPageIndex(); + this.pageLink.textSearch = value.trim(); + this.updateData(); + }); + + if (this.displayPagination) { + this.sort.sortChange.pipe(takeUntil(this.destroy$)).subscribe(() => this.paginator.pageIndex = 0); + } + ((this.displayPagination ? merge(this.sort.sortChange, this.paginator.page) : this.sort.sortChange) as Observable).pipe( + takeUntil(this.destroy$) + ).subscribe(() => this.updateData()); + this.updateData(); + } + + public onDataUpdated() { + this.alarmsDatasource.updateAlarms(); + this.clearCache(); + this.ctx.detectChanges(); + } + + public pageLinkSortDirection(): SortDirection { + return entityDataPageLinkSortDirection(this.pageLink); + } + + private initializeConfig() { + this.ctx.widgetActions = [this.searchAction, this.alarmFilterAction, this.columnDisplayAction]; + + this.displayActivity = isDefined(this.settings.displayActivity) ? this.settings.displayActivity : false; + this.displayDetails = isDefined(this.settings.displayDetails) ? this.settings.displayDetails : true; + this.allowAcknowledgment = isDefined(this.settings.allowAcknowledgment) ? this.settings.allowAcknowledgment : true; + this.allowClear = isDefined(this.settings.allowClear) ? this.settings.allowClear : true; + this.allowAssign = isDefined(this.settings.allowAssign) ? this.settings.allowAssign : true; + + if (this.settings.alarmsTitle && this.settings.alarmsTitle.length) { + this.ctx.widgetTitle = this.settings.alarmsTitle; + } else { + this.ctx.widgetTitle = this.translate.instant('alarm.alarms'); + } + + this.enableSelection = isDefined(this.settings.enableSelection) ? this.settings.enableSelection : true; + if (!this.allowAcknowledgment && !this.allowClear) { + this.enableSelection = false; + } + + this.searchAction.show = isDefined(this.settings.enableSearch) ? this.settings.enableSearch : true; + this.displayPagination = isDefined(this.settings.displayPagination) ? this.settings.displayPagination : true; + this.enableStickyHeader = isDefined(this.settings.enableStickyHeader) ? this.settings.enableStickyHeader : true; + this.enableStickyAction = isDefined(this.settings.enableStickyAction) ? this.settings.enableStickyAction : false; + this.showCellActionsMenu = isDefined(this.settings.showCellActionsMenu) ? this.settings.showCellActionsMenu : true; + this.columnDisplayAction.show = isDefined(this.settings.enableSelectColumnDisplay) ? this.settings.enableSelectColumnDisplay : true; + let enableFilter; + if (isDefined(this.settings.enableFilter)) { + enableFilter = this.settings.enableFilter; + } else if (isDefined(this.settings.enableStatusFilter)) { + enableFilter = this.settings.enableStatusFilter; + } else { + enableFilter = true; + } + this.alarmFilterAction.show = enableFilter; + + this.rowStylesInfo = getRowStyleInfo(this.settings, 'alarm, ctx'); + + const pageSize = this.settings.defaultPageSize; + if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) { + this.defaultPageSize = pageSize; + } + this.pageSizeOptions = [this.defaultPageSize, this.defaultPageSize * 2, this.defaultPageSize * 3]; + this.pageLink.pageSize = this.displayPagination ? this.defaultPageSize : 1024; + + const alarmFilter = this.entityService.resolveAlarmFilter(this.widgetConfig.alarmFilterConfig, false); + this.pageLink = {...this.pageLink, ...alarmFilter}; + + this.noDataDisplayMessageText = + noDataMessage(this.widgetConfig.noDataDisplayMessage, 'alarm.no-alarms-prompt', this.utils, this.translate); + + const cssString = constructTableCssString(this.widgetConfig); + const cssParser = new cssjs(); + cssParser.testMode = false; + const namespace = 'alarms-table-' + hashCode(cssString); + cssParser.cssPreviewNamespace = namespace; + cssParser.createStyleElement(namespace, cssString); + $(this.elementRef.nativeElement).addClass(namespace); + } + + private updateAlarmSource() { + + if (this.enableSelection) { + this.displayedColumns.push('select'); + } + + const latestDataKeys: Array = []; + + if (this.subscription.alarmSource) { + this.subscription.alarmSource.dataKeys.forEach((alarmDataKey) => { + const dataKey: EntityColumn = deepClone(alarmDataKey) as EntityColumn; + const keySettings: TableWidgetDataKeySettings = dataKey.settings; + dataKey.entityKey = dataKeyToEntityKey(alarmDataKey); + dataKey.label = this.utils.customTranslation(dataKey.label, dataKey.label); + dataKey.title = getHeaderTitle(dataKey, keySettings, this.utils); + dataKey.def = 'def' + this.columns.length; + if (dataKey.type === DataKeyType.alarm && !isDefined(keySettings.columnWidth)) { + const alarmField = alarmFields[dataKey.name]; + if (alarmField && alarmField.time) { + keySettings.columnWidth = '120px'; + } + if (alarmField && alarmField.keyName === alarmFields.assignee.keyName) { + keySettings.columnWidth = '120px'; + } + } + this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings, 'value, alarm, ctx'); + this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, alarm, ctx'); + this.contentsInfo[dataKey.def].units = dataKey.units; + this.contentsInfo[dataKey.def].decimals = dataKey.decimals; + this.columnWidth[dataKey.def] = getColumnWidth(keySettings); + this.columnDefaultVisibility[dataKey.def] = getColumnDefaultVisibility(keySettings, this.ctx); + this.columnSelectionAvailability[dataKey.def] = getColumnSelectionAvailability(keySettings); + this.columns.push(dataKey); + + if (dataKey.type !== DataKeyType.alarm) { + latestDataKeys.push(dataKey); + } + }); + this.displayedColumns.push(...this.columns.filter(column => this.columnDefaultVisibility[column.def]) + .map(column => column.def)); + } + if (this.settings.defaultSortOrder && this.settings.defaultSortOrder.length) { + this.defaultSortOrder = this.utils.customTranslation(this.settings.defaultSortOrder, this.settings.defaultSortOrder); + } + this.pageLink.sortOrder = entityDataSortOrderFromString(this.defaultSortOrder, this.columns); + let sortColumn: EntityColumn; + if (this.pageLink.sortOrder) { + sortColumn = findColumnByEntityKey(this.pageLink.sortOrder.key, this.columns); + } + this.sortOrderProperty = sortColumn ? sortColumn.def : null; + + const actionCellDescriptors: AlarmWidgetActionDescriptor[] = []; + if (this.displayActivity) { + actionCellDescriptors.push( + { + displayName: this.translate.instant('alarm-activity.activity'), + icon: 'comment', + activity: true + } as AlarmWidgetActionDescriptor + ); + } + + if (this.displayDetails) { + actionCellDescriptors.push( + { + displayName: this.translate.instant('alarm.details'), + icon: 'more_horiz', + details: true + } as AlarmWidgetActionDescriptor + ); + } + + if (this.allowAcknowledgment) { + actionCellDescriptors.push( + { + displayName: this.translate.instant('alarm.acknowledge'), + icon: 'done', + acknowledge: true + } as AlarmWidgetActionDescriptor + ); + } + + if (this.allowClear) { + actionCellDescriptors.push( + { + displayName: this.translate.instant('alarm.clear'), + icon: 'clear', + clear: true + } as AlarmWidgetActionDescriptor + ); + } + + this.setCellButtonAction = !!(actionCellDescriptors.length + this.ctx.actionsApi.getActionDescriptors('actionCellButton').length); + this.hasRowAction = !!this.ctx.actionsApi.getActionDescriptors('rowClick').length; + + if (this.setCellButtonAction) { + this.displayedColumns.push('actions'); + } + + this.alarmsDatasource = new AlarmsDatasource(this.subscription, latestDataKeys, this.ngZone, this.ctx, actionCellDescriptors); + if (this.enableSelection) { + this.alarmsDatasource.selectionModeChanged$.subscribe((selectionMode) => { + const hideTitlePanel = selectionMode || this.textSearchMode; + if (this.ctx.hideTitlePanel !== hideTitlePanel) { + this.ctx.hideTitlePanel = hideTitlePanel; + this.ctx.detectChanges(true); + } else { + this.ctx.detectChanges(); + } + }); + } + } + + private editColumnsToDisplay($event: Event) { + if ($event) { + $event.stopPropagation(); + } + const target = $event.target || $event.srcElement || $event.currentTarget; + const config = new OverlayConfig({ + panelClass: 'tb-panel-container', + backdropClass: 'cdk-overlay-transparent-backdrop', + hasBackdrop: true, + height: 'fit-content', + maxHeight: '75vh' + }); + config.positionStrategy = this.overlay.position() + .flexibleConnectedTo(target as HTMLElement) + .withPositions(DEFAULT_OVERLAY_POSITIONS); + + const overlayRef = this.overlay.create(config); + overlayRef.backdropClick().subscribe(() => { + overlayRef.dispose(); + }); + + const columns: DisplayColumn[] = this.columns.map(column => ({ + title: column.title, + def: column.def, + display: this.displayedColumns.indexOf(column.def) > -1, + selectable: this.columnSelectionAvailability[column.def] + })); + + const providers: StaticProvider[] = [ + { + provide: DISPLAY_COLUMNS_PANEL_DATA, + useValue: { + columns, + columnsUpdated: (newColumns) => { + this.displayedColumns = newColumns.filter(column => column.display).map(column => column.def); + if (this.enableSelection) { + this.displayedColumns.unshift('select'); + } + if (this.setCellButtonAction) { + this.displayedColumns.push('actions'); + } + this.clearCache(); + } + } as DisplayColumnsPanelData + }, + { + provide: OverlayRef, + useValue: overlayRef + } + ]; + + const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); + const componentRef = overlayRef.attach(new ComponentPortal(DisplayColumnsPanelComponent, + this.viewContainerRef, injector)); + + const resizeWindows$ = fromEvent(window, 'resize').subscribe(() => { + overlayRef.updatePosition(); + }); + componentRef.onDestroy(() => { + resizeWindows$.unsubscribe(); + }); + + this.ctx.detectChanges(); + } + + private resetPageIndex(): void { + if (this.displayPagination) { + this.paginator.pageIndex = 0; + } + } + + private editAlarmFilter($event: Event) { + if ($event) { + $event.stopPropagation(); + } + const target = $event.target || $event.srcElement || $event.currentTarget; + const config = new OverlayConfig({ + panelClass: 'tb-filter-panel', + backdropClass: 'cdk-overlay-transparent-backdrop', + hasBackdrop: true, + height: 'fit-content', + maxHeight: '75vh' + }); + config.positionStrategy = this.overlay.position() + .flexibleConnectedTo(target as HTMLElement) + .withPositions(DEFAULT_OVERLAY_POSITIONS); + + const overlayRef = this.overlay.create(config); + overlayRef.backdropClick().subscribe(() => { + overlayRef.dispose(); + }); + const authUser = getCurrentAuthUser(this.store); + const assignedToCurrentUser = isDefinedAndNotNull(this.pageLink.assigneeId) && this.pageLink.assigneeId.id === authUser.userId; + const assigneeId = assignedToCurrentUser ? null : this.pageLink.assigneeId; + const providers: StaticProvider[] = [ + { + provide: ALARM_FILTER_CONFIG_DATA, + useValue: { + panelMode: true, + userMode: true, + alarmFilterConfig: { + statusList: deepClone(this.pageLink.statusList), + severityList: deepClone(this.pageLink.severityList), + typeList: deepClone(this.pageLink.typeList), + searchPropagatedAlarms: this.pageLink.searchPropagatedAlarms, + assignedToCurrentUser, + assigneeId + }, + initialAlarmFilterConfig: deepClone(this.widgetConfig.alarmFilterConfig) + } as AlarmFilterConfigData + }, + { + provide: OverlayRef, + useValue: overlayRef + } + ]; + const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); + const componentRef = overlayRef.attach(new ComponentPortal(AlarmFilterConfigComponent, + this.viewContainerRef, injector)); + + const resizeWindows$ = fromEvent(window, 'resize').subscribe(() => { + overlayRef.updatePosition(); + }); + + componentRef.onDestroy(() => { + resizeWindows$.unsubscribe(); + if (componentRef.instance.panelResult) { + const result = componentRef.instance.panelResult; + const alarmFilter = this.entityService.resolveAlarmFilter(result, false); + this.pageLink = {...this.pageLink, ...alarmFilter}; + this.resetPageIndex(); + this.updateData(); + } + }); + this.ctx.detectChanges(); + } + + private enterFilterMode() { + this.textSearchMode = true; + this.ctx.hideTitlePanel = true; + this.ctx.detectChanges(true); + setTimeout(() => { + this.searchInputField.nativeElement.focus(); + this.searchInputField.nativeElement.setSelectionRange(0, 0); + }, 10); + } + + exitFilterMode() { + this.textSearchMode = false; + this.textSearch.reset(); + this.ctx.hideTitlePanel = false; + this.ctx.detectChanges(true); + } + + private updateData() { + if (this.displayPagination) { + this.pageLink.page = this.paginator.pageIndex; + this.pageLink.pageSize = this.paginator.pageSize; + } else { + this.pageLink.page = 0; + } + const key = findEntityKeyByColumnDef(this.sort.active, this.columns); + if (key) { + this.pageLink.sortOrder = { + key, + direction: Direction[this.sort.direction.toUpperCase()] + }; + } else { + this.pageLink.sortOrder = null; + } + const sortOrderLabel = fromEntityColumnDef(this.sort.active, this.columns); + const keyFilters: KeyFilter[] = null; // TODO: + this.alarmsDatasource.loadAlarms(this.pageLink, sortOrderLabel, keyFilters); + this.ctx.detectChanges(); + } + + public trackByColumnDef(index, column: EntityColumn) { + return column.def; + } + + public trackByAlarmId(index: number, alarm: AlarmData) { + return alarm.id.id; + } + + public trackByActionCellDescriptionId(index: number, action: WidgetActionDescriptor) { + return action.id; + } + + public headerStyle(key: EntityColumn): any { + const columnWidth = this.columnWidth[key.def]; + return widthStyle(columnWidth); + } + + public rowStyle(alarm: AlarmDataInfo, row: number): any { + let res = this.rowStyleCache[row]; + if (!res) { + res = {}; + if (alarm && this.rowStylesInfo.useRowStyleFunction && this.rowStylesInfo.rowStyleFunction) { + try { + res = this.rowStylesInfo.rowStyleFunction(alarm, this.ctx); + if (!isObject(res)) { + throw new TypeError(`${res === null ? 'null' : typeof res} instead of style object`); + } + if (Array.isArray(res)) { + throw new TypeError(`Array instead of style object`); + } + } catch (e) { + res = {}; + console.warn(`Row style function in widget '${this.ctx.widgetTitle}' ` + + `returns '${e}'. Please check your row style function.`); + } + } + this.rowStyleCache[row] = res; + } + return res; + } + + public cellStyle(alarm: AlarmDataInfo, key: EntityColumn, row: number): any { + const col = this.columns.indexOf(key); + const index = row * this.columns.length + col; + let res = this.cellStyleCache[index]; + if (!res) { + res = {}; + if (alarm && key) { + const styleInfo = this.stylesInfo[key.def]; + const value = getAlarmValue(alarm, key); + if (styleInfo.useCellStyleFunction && styleInfo.cellStyleFunction) { + try { + res = styleInfo.cellStyleFunction(value, alarm, this.ctx); + if (!isObject(res)) { + throw new TypeError(`${res === null ? 'null' : typeof res} instead of style object`); + } + if (Array.isArray(res)) { + throw new TypeError(`Array instead of style object`); + } + } catch (e) { + res = {}; + console.warn(`Cell style function for data key '${key.label}' in widget '${this.ctx.widgetTitle}' ` + + `returns '${e}'. Please check your cell style function.`); + } + } else { + res = this.defaultStyle(key, value); + } + } + this.cellStyleCache[index] = res; + } + if (!res.width) { + const columnWidth = this.columnWidth[key.def]; + res = Object.assign(res, widthStyle(columnWidth)); + } + return res; + } + + public cellContent(alarm: AlarmDataInfo, key: EntityColumn, row: number): SafeHtml { + const col = this.columns.indexOf(key); + const index = row * this.columns.length + col; + let res = this.cellContentCache[index]; + if (isUndefined(res)) { + res = ''; + if (alarm && key) { + const contentInfo = this.contentsInfo[key.def]; + const value = getAlarmValue(alarm, key); + let content = ''; + if (contentInfo.useCellContentFunction && contentInfo.cellContentFunction) { + try { + content = contentInfo.cellContentFunction(value, alarm, this.ctx); + } catch (e) { + content = '' + value; + } + } else { + content = this.defaultContent(key, contentInfo, value); + } + + if (isDefined(content)) { + content = this.utils.customTranslation(content, content); + switch (typeof content) { + case 'string': + res = this.domSanitizer.bypassSecurityTrustHtml(content); + break; + default: + res = content; + } + } + } + this.cellContentCache[index] = res; + } + return res; + } + + public onRowClick($event: Event, alarm: AlarmDataInfo) { + if ($event) { + $event.stopPropagation(); + } + this.alarmsDatasource.toggleCurrentAlarm(alarm); + const descriptors = this.ctx.actionsApi.getActionDescriptors('rowClick'); + if (descriptors.length) { + let entityId; + let entityName; + let entityLabel; + if (alarm && alarm.originator) { + entityId = alarm.originator; + entityName = alarm.originatorName; + entityLabel = alarm.originatorLabel; + } + this.ctx.actionsApi.handleWidgetAction($event, descriptors[0], entityId, entityName, {alarm}, entityLabel); + } + } + + public onActionButtonClick($event: Event, alarm: AlarmDataInfo, actionDescriptor: AlarmWidgetActionDescriptor) { + if (actionDescriptor.details) { + this.openAlarmDetails($event, alarm); + } else if (actionDescriptor.acknowledge) { + this.ackAlarm($event, alarm); + } else if (actionDescriptor.clear) { + this.clearAlarm($event, alarm); + } else if (actionDescriptor.activity) { + this.openAlarmActivity($event, alarm); + } else { + if ($event) { + $event.stopPropagation(); + } + let entityId; + let entityName; + let entityLabel; + if (alarm && alarm.originator) { + entityId = alarm.originator; + entityName = alarm.originatorName; + entityLabel = alarm.originatorLabel; + } + this.ctx.actionsApi.handleWidgetAction($event, actionDescriptor, entityId, entityName, {alarm}, entityLabel); + } + } + + public actionEnabled(alarm: AlarmDataInfo, actionDescriptor: AlarmWidgetActionDescriptor): boolean { + if (actionDescriptor.acknowledge) { + return (alarm.status === AlarmStatus.ACTIVE_UNACK || + alarm.status === AlarmStatus.CLEARED_UNACK); + } else if (actionDescriptor.clear) { + return (alarm.status === AlarmStatus.ACTIVE_ACK || + alarm.status === AlarmStatus.ACTIVE_UNACK); + } + return true; + } + + private openAlarmDetails($event: Event, alarm: AlarmDataInfo) { + if ($event) { + $event.stopPropagation(); + } + if (alarm && alarm.id && alarm.id.id !== NULL_UUID) { + this.dialog.open + (AlarmDetailsDialogComponent, + { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + alarmId: alarm.id.id, + allowAcknowledgment: this.allowAcknowledgment, + allowClear: this.allowClear, + displayDetails: true, + allowAssign: this.allowAssign + } + }).afterClosed().subscribe( + (res) => { + if (res) { + this.subscription.update(); + } + } + ); + } + } + + private ackAlarm($event: Event, alarm: AlarmDataInfo) { + if ($event) { + $event.stopPropagation(); + } + if (alarm && alarm.id && alarm.id.id !== NULL_UUID) { + this.dialogService.confirm( + this.translate.instant('alarm.aknowledge-alarm-title'), + this.translate.instant('alarm.aknowledge-alarm-text'), + this.translate.instant('action.no'), + this.translate.instant('action.yes') + ).subscribe((res) => { + if (res) { + if (res) { + this.alarmService.ackAlarm(alarm.id.id).subscribe(() => { + this.subscription.update(); + }); + } + } + }); + } + } + + public ackAlarms($event: Event) { + if ($event) { + $event.stopPropagation(); + } + if (this.alarmsDatasource.selection.hasValue()) { + const unacknowledgedAlarms = this.alarmsDatasource.selection.selected.filter( + alarm => alarm.id.id !== NULL_UUID && !alarm.acknowledged + ); + let title = ''; + let content = ''; + if (!unacknowledgedAlarms.length) { + title = this.translate.instant('alarm.selected-alarms', {count: unacknowledgedAlarms.length}); + content = this.translate.instant('alarm.selected-alarms-are-acknowledged'); + this.dialogService.alert( + title, + content + ).subscribe(); + } else { + title = this.translate.instant('alarm.aknowledge-alarms-title', {count: unacknowledgedAlarms.length}); + content = this.translate.instant('alarm.aknowledge-alarms-text', {count: unacknowledgedAlarms.length}); + this.dialogService.confirm( + title, + content, + this.translate.instant('action.no'), + this.translate.instant('action.yes') + ).subscribe((res) => { + if (res) { + const tasks: Observable[] = []; + for (const alarm of unacknowledgedAlarms) { + tasks.push(this.alarmService.ackAlarm(alarm.id.id)); + } + forkJoin(tasks).subscribe(() => { + this.alarmsDatasource.clearSelection(); + this.subscription.update(); + }); + } + }); + } + } + } + + private clearAlarm($event: Event, alarm: AlarmDataInfo) { + if ($event) { + $event.stopPropagation(); + } + if (alarm && alarm.id && alarm.id.id !== NULL_UUID) { + this.dialogService.confirm( + this.translate.instant('alarm.clear-alarm-title'), + this.translate.instant('alarm.clear-alarm-text'), + this.translate.instant('action.no'), + this.translate.instant('action.yes') + ).subscribe((res) => { + if (res) { + if (res) { + this.alarmService.clearAlarm(alarm.id.id).subscribe(() => { + this.subscription.update(); + }); + } + } + }); + } + } + + public clearAlarms($event: Event) { + if ($event) { + $event.stopPropagation(); + } + if (this.alarmsDatasource.selection.hasValue()) { + const activeAlarms = this.alarmsDatasource.selection.selected.filter( + alarm => alarm.id.id !== NULL_UUID && !alarm.cleared + ); + let title = ''; + let content = ''; + if (!activeAlarms.length) { + title = this.translate.instant('alarm.selected-alarms', {count: activeAlarms.length}); + content = this.translate.instant('alarm.selected-alarms-are-cleared'); + this.dialogService.alert( + title, + content + ).subscribe(); + } else { + title = this.translate.instant('alarm.clear-alarms-title', {count: activeAlarms.length}); + content = this.translate.instant('alarm.clear-alarms-text', {count: activeAlarms.length}); + this.dialogService.confirm( + title, + content, + this.translate.instant('action.no'), + this.translate.instant('action.yes') + ).subscribe((res) => { + if (res) { + const tasks: Observable[] = []; + for (const alarm of activeAlarms) { + tasks.push(this.alarmService.clearAlarm(alarm.id.id)); + } + forkJoin(tasks).subscribe(() => { + this.alarmsDatasource.clearSelection(); + this.subscription.update(); + }); + } + }); + } + } + } + + private openAlarmActivity($event: Event, alarm: AlarmDataInfo) { + if ($event) { + $event.stopPropagation(); + } + if (alarm && alarm.id && alarm.id.id !== NULL_UUID) { + this.dialog.open + (AlarmCommentDialogComponent, + { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + alarmId: alarm.id.id + } + }).afterClosed(); + } + } + + private defaultContent(key: EntityColumn, contentInfo: CellContentInfo, value: any): any { + if (isDefined(value)) { + const alarmField = alarmFields[key.name]; + if (alarmField) { + return this.utils.defaultAlarmFieldContent(key, value); + } + const entityField = entityFields[key.name]; + if (entityField) { + if (entityField.time) { + return this.datePipe.transform(value, 'yyyy-MM-dd HH:mm:ss'); + } + } + const decimals = (contentInfo.decimals || contentInfo.decimals === 0) ? contentInfo.decimals : this.ctx.widgetConfig.decimals; + const units = contentInfo.units || this.ctx.widgetConfig.units; + return this.ctx.utils.formatValue(value, decimals, units, true); + } else { + return ''; + } + } + + private defaultStyle(key: EntityColumn, value: any): any { + if (isDefined(value)) { + const alarmField = alarmFields[key.name]; + if (alarmField) { + if (alarmField.value === alarmFields.severity.value) { + return { + fontWeight: 'bold', + color: alarmSeverityColors.get(value) + }; + } else { + return {}; + } + } else { + return {}; + } + } else { + return {}; + } + } + + isSorting(column: EntityColumn): boolean { + return column.type === DataKeyType.alarm && column.name.startsWith('details.'); + } + + private clearCache() { + this.cellContentCache.length = 0; + this.cellStyleCache.length = 0; + this.rowStyleCache.length = 0; + } + + getUserDisplayName(entity: AlarmInfo) { + let displayName = ''; + if ((entity.assignee.firstName && entity.assignee.firstName.length > 0) || + (entity.assignee.lastName && entity.assignee.lastName.length > 0)) { + if (entity.assignee.firstName) { + displayName += entity.assignee.firstName; + } + if (entity.assignee.lastName) { + if (displayName.length > 0) { + displayName += ' '; + } + displayName += entity.assignee.lastName; + } + } else { + displayName = entity.assignee.email; + } + return displayName; + } + + getUserInitials(entity: AlarmInfo): string { + let initials = ''; + if (entity.assignee.firstName && entity.assignee.firstName.length || + entity.assignee.lastName && entity.assignee.lastName.length) { + if (entity.assignee.firstName) { + initials += entity.assignee.firstName.charAt(0); + } + if (entity.assignee.lastName) { + initials += entity.assignee.lastName.charAt(0); + } + } else { + initials += entity.assignee.email.charAt(0); + } + return initials.toUpperCase(); + } + + getAvatarBgColor(entity: AlarmInfo) { + return this.utils.stringToHslColor(this.getUserDisplayName(entity), 40, 60); + } + + openAlarmAssigneePanel($event: Event, entity: AlarmInfo) { + if ($event) { + $event.stopPropagation(); + } + const target = $event.target || $event.srcElement || $event.currentTarget; + const config = new OverlayConfig(); + config.backdropClass = 'cdk-overlay-transparent-backdrop'; + config.hasBackdrop = true; + const connectedPosition: ConnectedPosition = { + originX: 'end', + originY: 'bottom', + overlayX: 'end', + overlayY: 'top' + }; + config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement) + .withPositions([connectedPosition]); + config.minWidth = '260px'; + const overlayRef = this.overlay.create(config); + overlayRef.backdropClick().subscribe(() => { + overlayRef.dispose(); + }); + const providers: StaticProvider[] = [ + { + provide: ALARM_ASSIGNEE_PANEL_DATA, + useValue: { + alarmId: entity.id.id, + assigneeId: entity.assigneeId?.id + } as AlarmAssigneePanelData + }, + { + provide: OverlayRef, + useValue: overlayRef + } + ]; + const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); + overlayRef.attach(new ComponentPortal(AlarmAssigneePanelComponent, + this.viewContainerRef, injector)); + } +} + +class AlarmsDatasource implements DataSource { + + private alarmsSubject = new BehaviorSubject([]); + private pageDataSubject = new BehaviorSubject>(emptyPageData()); + + public selection = new SelectionModel(true, [], false, + (alarm1: AlarmDataInfo, alarm2: AlarmDataInfo) => alarm1.id.id === alarm2.id.id); + + private selectionModeChanged = new EventEmitter(); + + public selectionModeChanged$ = this.selectionModeChanged.asObservable(); + + private currentAlarm: AlarmDataInfo = null; + + public dataLoading = true; + public countCellButtonAction = 0; + + private appliedPageLink: AlarmDataPageLink; + private appliedSortOrderLabel: string; + + private reserveSpaceForHiddenAction = true; + private cellButtonActions: TableCellButtonActionDescriptor[]; + private readonly usedShowCellActionFunction: boolean; + + constructor(private subscription: IWidgetSubscription, + private dataKeys: Array, + private ngZone: NgZone, + private widgetContext: WidgetContext, + actionCellDescriptors: AlarmWidgetActionDescriptor[]) { + this.cellButtonActions = actionCellDescriptors.concat(getTableCellButtonActions(widgetContext)); + this.usedShowCellActionFunction = this.cellButtonActions.some(action => action.useShowActionCellButtonFunction); + if (this.widgetContext.settings.reserveSpaceForHiddenAction) { + this.reserveSpaceForHiddenAction = coerceBooleanProperty(this.widgetContext.settings.reserveSpaceForHiddenAction); + } + } + + connect(collectionViewer: CollectionViewer): Observable> { + return this.alarmsSubject.asObservable(); + } + + disconnect(collectionViewer: CollectionViewer): void { + this.alarmsSubject.complete(); + this.pageDataSubject.complete(); + } + + loadAlarms(pageLink: AlarmDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) { + this.dataLoading = true; + // this.clear(); + this.appliedPageLink = pageLink; + this.appliedSortOrderLabel = sortOrderLabel; + this.subscription.subscribeForAlarms(pageLink, keyFilters); + } + + private clear() { + if (this.selection.hasValue()) { + this.selection.clear(); + this.onSelectionModeChanged(false); + } + this.alarmsSubject.next([]); + this.pageDataSubject.next(emptyPageData()); + } + + updateAlarms() { + const subscriptionAlarms = this.subscription.alarms; + let alarms = new Array(); + let maxCellButtonAction = 0; + let isEmptySelection = false; + const dynamicWidthCellButtonActions = this.usedShowCellActionFunction && !this.reserveSpaceForHiddenAction; + subscriptionAlarms.data.forEach((alarmData) => { + const alarm = this.alarmDataToInfo(alarmData); + alarms.push(alarm); + if (dynamicWidthCellButtonActions && alarm.actionCellButtons.length > maxCellButtonAction) { + maxCellButtonAction = alarm.actionCellButtons.length; + } + }); + if (!dynamicWidthCellButtonActions && this.cellButtonActions.length && alarms.length) { + maxCellButtonAction = alarms[0].actionCellButtons.length; + } + if (this.appliedSortOrderLabel && this.appliedSortOrderLabel.length) { + const asc = this.appliedPageLink.sortOrder.direction === Direction.ASC; + alarms = alarms.sort((a, b) => sortItems(a, b, this.appliedSortOrderLabel, asc)); + } + if (this.selection.hasValue()) { + const alarmIds = alarms.map((alarm) => alarm.id.id); + const toRemove = this.selection.selected.filter(alarm => alarmIds.indexOf(alarm.id.id) === -1); + this.selection.deselect(...toRemove); + if (this.selection.isEmpty()) { + isEmptySelection = true; + } + } + const alarmsPageData: PageData = { + data: alarms, + totalPages: subscriptionAlarms.totalPages, + totalElements: subscriptionAlarms.totalElements, + hasNext: subscriptionAlarms.hasNext + }; + this.ngZone.run(() => { + if (isEmptySelection) { + this.onSelectionModeChanged(false); + } + this.alarmsSubject.next(alarms); + this.pageDataSubject.next(alarmsPageData); + this.countCellButtonAction = maxCellButtonAction; + this.dataLoading = false; + }); + } + + private alarmDataToInfo(alarmData: AlarmData): AlarmDataInfo { + const alarm: AlarmDataInfo = deepClone(alarmData); + delete alarm.latest; + const latest = alarmData.latest; + this.dataKeys.forEach((dataKey, index) => { + const type = dataKeyTypeToEntityKeyType(dataKey.type); + let value = ''; + if (type) { + if (latest && latest[type]) { + const tsVal = latest[type][dataKey.name]; + if (tsVal) { + value = tsVal.value; + } + } + } + alarm[dataKey.label] = value; + }); + if (this.cellButtonActions.length) { + if (this.usedShowCellActionFunction) { + alarm.actionCellButtons = prepareTableCellButtonActions(this.widgetContext, this.cellButtonActions, + alarm, this.reserveSpaceForHiddenAction); + alarm.hasActions = checkHasActions(alarm.actionCellButtons); + } else { + alarm.actionCellButtons = this.cellButtonActions; + alarm.hasActions = true; + } + } + return alarm; + } + + isAllSelected(): Observable { + const numSelected = this.selection.selected.length; + return this.alarmsSubject.pipe( + map((alarms) => numSelected === alarms.length) + ); + } + + isEmpty(): Observable { + return this.alarmsSubject.pipe( + map((alarms) => !alarms.length) + ); + } + + total(): Observable { + return this.pageDataSubject.pipe( + map((pageData) => pageData.totalElements) + ); + } + + toggleSelection(alarm: AlarmDataInfo) { + const hasValue = this.selection.hasValue(); + this.selection.toggle(alarm); + if (hasValue !== this.selection.hasValue()) { + this.onSelectionModeChanged(this.selection.hasValue()); + } + } + + isSelected(alarm: AlarmDataInfo): boolean { + return this.selection.isSelected(alarm); + } + + clearSelection() { + if (this.selection.hasValue()) { + this.selection.clear(); + this.onSelectionModeChanged(false); + } + } + + masterToggle() { + this.alarmsSubject.pipe( + tap((alarms) => { + const numSelected = this.selection.selected.length; + if (numSelected === alarms.length) { + this.selection.clear(); + if (numSelected > 0) { + this.onSelectionModeChanged(false); + } + } else { + alarms.forEach(row => { + this.selection.select(row); + }); + if (numSelected === 0) { + this.onSelectionModeChanged(true); + } + } + }), + take(1) + ).subscribe(); + } + + public toggleCurrentAlarm(alarm: AlarmDataInfo): boolean { + if (this.currentAlarm !== alarm) { + this.currentAlarm = alarm; + return true; + } else { + return false; + } + } + + public isCurrentAlarm(alarm: AlarmDataInfo): boolean { + return (this.currentAlarm && alarm && this.currentAlarm.id && alarm.id) && + (this.currentAlarm.id.id === alarm.id.id); + } + + private onSelectionModeChanged(selectionMode: boolean) { + this.selectionModeChanged.emit(selectionMode); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html deleted file mode 100644 index 9a23e86bf8..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html +++ /dev/null @@ -1,184 +0,0 @@ - -
-
- -
- - -   - - - -
-
- -
- - {{ translate.get('alarm.selected-alarms', - {count: alarmsDatasource.selection.selected.length}) | async }} - - - - -
-
-
- - - - - - - - - - - - - - {{ column.title }} - - - - - - - - {{ getUserInitials(alarm) }} - - - {{ getUserDisplayName(alarm) }} - - - - account_circle - - alarm.unassigned - - - - - - - - - - - -
- - - - -
-
- - - - - - -
-
-
- - -
- {{ noDataDisplayMessageText }} - {{ 'common.loading' | translate }} -
- - -
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts deleted file mode 100644 index 6b0537d968..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts +++ /dev/null @@ -1,1365 +0,0 @@ -/// -/// Copyright © 2016-2023 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -import { - AfterViewInit, - ChangeDetectorRef, - Component, - ElementRef, - EventEmitter, - Injector, - Input, - NgZone, - OnInit, - StaticProvider, - ViewChild, - ViewContainerRef -} from '@angular/core'; -import { PageComponent } from '@shared/components/page.component'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { WidgetAction, WidgetContext } from '@home/models/widget-component.models'; -import { DataKey, WidgetActionDescriptor, WidgetConfig } from '@shared/models/widget.models'; -import { IWidgetSubscription } from '@core/api/widget-api.models'; -import { UtilsService } from '@core/services/utils.service'; -import { TranslateService } from '@ngx-translate/core'; -import { - createLabelFromDatasource, - deepClone, - hashCode, - isDefined, - isDefinedAndNotNull, - isNumber, - isObject, - isUndefined -} from '@core/utils'; -import cssjs from '@core/css/css'; -import { sortItems } from '@shared/models/page/page-link'; -import { Direction } from '@shared/models/page/sort-order'; -import { CollectionViewer, DataSource, SelectionModel } from '@angular/cdk/collections'; -import { BehaviorSubject, forkJoin, fromEvent, merge, Observable, Subscription } from 'rxjs'; -import { emptyPageData, PageData } from '@shared/models/page/page-data'; -import { entityTypeTranslations } from '@shared/models/entity-type.models'; -import { debounceTime, distinctUntilChanged, map, take, tap } from 'rxjs/operators'; -import { MatPaginator } from '@angular/material/paginator'; -import { MatSort, SortDirection } from '@angular/material/sort'; -import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; -import { - CellContentInfo, - CellStyleInfo, - checkHasActions, - constructTableCssString, - DisplayColumn, - EntityColumn, - entityDataSortOrderFromString, - findColumnByEntityKey, - findEntityKeyByColumnDef, - fromEntityColumnDef, - getAlarmValue, - getCellContentInfo, - getCellStyleInfo, - getColumnDefaultVisibility, - getColumnSelectionAvailability, - getColumnWidth, - getHeaderTitle, - getRowStyleInfo, - getTableCellButtonActions, - noDataMessage, - prepareTableCellButtonActions, - RowStyleInfo, - TableCellButtonActionDescriptor, - TableWidgetDataKeySettings, - TableWidgetSettings, - widthStyle -} from '@home/components/widget/lib/table-widget.models'; -import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; -import { ComponentPortal } from '@angular/cdk/portal'; -import { - DISPLAY_COLUMNS_PANEL_DATA, - DisplayColumnsPanelComponent, - DisplayColumnsPanelData -} from '@home/components/widget/lib/display-columns-panel.component'; -import { - AlarmDataInfo, - alarmFields, - AlarmInfo, - alarmSeverityColors, - alarmSeverityTranslations, - AlarmStatus, - alarmStatusTranslations -} from '@shared/models/alarm.models'; -import { DatePipe } from '@angular/common'; -import { - AlarmDetailsDialogComponent, - AlarmDetailsDialogData -} from '@home/components/alarm/alarm-details-dialog.component'; -import { MatDialog } from '@angular/material/dialog'; -import { NULL_UUID } from '@shared/models/id/has-uuid'; -import { DialogService } from '@core/services/dialog.service'; -import { AlarmService } from '@core/http/alarm.service'; -import { - AlarmData, - AlarmDataPageLink, - dataKeyToEntityKey, - dataKeyTypeToEntityKeyType, - entityDataPageLinkSortDirection, - KeyFilter -} from '@app/shared/models/query/query.models'; -import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; -import { entityFields } from '@shared/models/entity.models'; -import { coerceBooleanProperty } from '@angular/cdk/coercion'; -import { ResizeObserver } from '@juggle/resize-observer'; -import { hidePageSizePixelValue } from '@shared/models/constants'; -import { - ALARM_ASSIGNEE_PANEL_DATA, - AlarmAssigneePanelComponent, - AlarmAssigneePanelData -} from '@home/components/alarm/alarm-assignee-panel.component'; -import { - AlarmCommentDialogComponent, - AlarmCommentDialogData -} from '@home/components/alarm/alarm-comment-dialog.component'; -import { EntityService } from '@core/http/entity.service'; -import { - ALARM_FILTER_CONFIG_DATA, - AlarmFilterConfigComponent, - AlarmFilterConfigData -} from '@home/components/alarm/alarm-filter-config.component'; - -interface AlarmsTableWidgetSettings extends TableWidgetSettings { - alarmsTitle: string; - enableSelectColumnDisplay: boolean; - defaultSortOrder: string; - enableSelection: boolean; - enableStatusFilter?: boolean; - enableFilter: boolean; - displayActivity: boolean; - displayDetails: boolean; - allowAcknowledgment: boolean; - allowClear: boolean; - allowAssign: boolean; -} - -interface AlarmWidgetActionDescriptor extends TableCellButtonActionDescriptor { - details?: boolean; - acknowledge?: boolean; - clear?: boolean; - activity?: boolean; -} - -@Component({ - selector: 'tb-alarms-table-widget', - templateUrl: './alarms-table-widget.component.html', - styleUrls: ['./alarms-table-widget.component.scss', './table-widget.scss'] -}) -export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, AfterViewInit { - - @Input() - ctx: WidgetContext; - - @ViewChild('searchInput') searchInputField: ElementRef; - @ViewChild(MatPaginator) paginator: MatPaginator; - @ViewChild(MatSort) sort: MatSort; - - public enableSelection = true; - public displayPagination = true; - public enableStickyHeader = true; - public enableStickyAction = false; - public pageSizeOptions; - public pageLink: AlarmDataPageLink; - public sortOrderProperty: string; - public textSearchMode = false; - public hidePageSize = false; - public columns: Array = []; - public displayedColumns: string[] = []; - public alarmsDatasource: AlarmsDatasource; - public noDataDisplayMessageText: string; - private setCellButtonAction: boolean; - - private cellContentCache: Array = []; - private cellStyleCache: Array = []; - private rowStyleCache: Array = []; - - private settings: AlarmsTableWidgetSettings; - private widgetConfig: WidgetConfig; - private subscription: IWidgetSubscription; - private widgetResize$: ResizeObserver; - - private alarmsTitlePattern: string; - - private displayActivity = false; - private displayDetails = true; - public allowAcknowledgment = true; - private allowClear = true; - public allowAssign = true; - - private defaultPageSize = 10; - private defaultSortOrder = '-' + alarmFields.createdTime.value; - - private contentsInfo: {[key: string]: CellContentInfo} = {}; - private stylesInfo: {[key: string]: CellStyleInfo} = {}; - private columnWidth: {[key: string]: string} = {}; - private columnDefaultVisibility: {[key: string]: boolean} = {}; - private columnSelectionAvailability: {[key: string]: boolean} = {}; - - private rowStylesInfo: RowStyleInfo; - - private widgetTimewindowChanged$: Subscription; - - private searchAction: WidgetAction = { - name: 'action.search', - show: true, - icon: 'search', - onAction: () => { - this.enterFilterMode(); - } - }; - - private columnDisplayAction: WidgetAction = { - name: 'entity.columns-to-display', - show: true, - icon: 'view_column', - onAction: ($event) => { - this.editColumnsToDisplay($event); - } - }; - - private alarmFilterAction: WidgetAction = { - name: 'alarm.alarm-filter', - show: true, - onAction: ($event) => { - this.editAlarmFilter($event); - }, - icon: 'filter_list' - }; - - constructor(protected store: Store, - private elementRef: ElementRef, - private ngZone: NgZone, - private overlay: Overlay, - private viewContainerRef: ViewContainerRef, - private utils: UtilsService, - public translate: TranslateService, - private domSanitizer: DomSanitizer, - private datePipe: DatePipe, - private dialog: MatDialog, - private dialogService: DialogService, - private entityService: EntityService, - private alarmService: AlarmService, - private cd: ChangeDetectorRef) { - super(store); - this.pageLink = { - page: 0, - pageSize: this.defaultPageSize, - textSearch: null - }; - } - - ngOnInit(): void { - this.ctx.$scope.alarmsTableWidget = this; - this.settings = this.ctx.settings; - this.widgetConfig = this.ctx.widgetConfig; - this.subscription = this.ctx.defaultSubscription; - this.initializeConfig(); - this.updateAlarmSource(); - this.ctx.updateWidgetParams(); - - if (this.displayPagination) { - this.widgetTimewindowChanged$ = this.ctx.defaultSubscription.widgetTimewindowChanged$.subscribe( - () => this.pageLink.page = 0 - ); - this.widgetResize$ = new ResizeObserver(() => { - const showHidePageSize = this.elementRef.nativeElement.offsetWidth < hidePageSizePixelValue; - if (showHidePageSize !== this.hidePageSize) { - this.hidePageSize = showHidePageSize; - this.cd.markForCheck(); - } - }); - this.widgetResize$.observe(this.elementRef.nativeElement); - } - } - - ngOnDestroy(): void { - if (this.widgetTimewindowChanged$) { - this.widgetTimewindowChanged$.unsubscribe(); - this.widgetTimewindowChanged$ = null; - } - if (this.widgetResize$) { - this.widgetResize$.disconnect(); - } - } - - ngAfterViewInit(): void { - fromEvent(this.searchInputField.nativeElement, 'keyup') - .pipe( - debounceTime(150), - distinctUntilChanged(), - tap(() => { - this.resetPageIndex(); - this.updateData(); - }) - ) - .subscribe(); - - if (this.displayPagination) { - this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0); - } - ((this.displayPagination ? merge(this.sort.sortChange, this.paginator.page) : this.sort.sortChange) as Observable) - .pipe( - tap(() => this.updateData()) - ) - .subscribe(); - this.updateData(); - } - - public onDataUpdated() { - this.updateTitle(true); - this.alarmsDatasource.updateAlarms(); - this.clearCache(); - this.ctx.detectChanges(); - } - - public pageLinkSortDirection(): SortDirection { - return entityDataPageLinkSortDirection(this.pageLink); - } - - private initializeConfig() { - this.ctx.widgetActions = [this.searchAction, this.alarmFilterAction, this.columnDisplayAction]; - - this.displayActivity = isDefined(this.settings.displayActivity) ? this.settings.displayActivity : false; - this.displayDetails = isDefined(this.settings.displayDetails) ? this.settings.displayDetails : true; - this.allowAcknowledgment = isDefined(this.settings.allowAcknowledgment) ? this.settings.allowAcknowledgment : true; - this.allowClear = isDefined(this.settings.allowClear) ? this.settings.allowClear : true; - this.allowAssign = isDefined(this.settings.allowAssign) ? this.settings.allowAssign : true; - - if (this.settings.alarmsTitle && this.settings.alarmsTitle.length) { - this.alarmsTitlePattern = this.utils.customTranslation(this.settings.alarmsTitle, this.settings.alarmsTitle); - } else { - this.alarmsTitlePattern = this.translate.instant('alarm.alarms'); - } - - this.updateTitle(false); - - this.enableSelection = isDefined(this.settings.enableSelection) ? this.settings.enableSelection : true; - if (!this.allowAcknowledgment && !this.allowClear) { - this.enableSelection = false; - } - - this.searchAction.show = isDefined(this.settings.enableSearch) ? this.settings.enableSearch : true; - this.displayPagination = isDefined(this.settings.displayPagination) ? this.settings.displayPagination : true; - this.enableStickyHeader = isDefined(this.settings.enableStickyHeader) ? this.settings.enableStickyHeader : true; - this.enableStickyAction = isDefined(this.settings.enableStickyAction) ? this.settings.enableStickyAction : false; - this.columnDisplayAction.show = isDefined(this.settings.enableSelectColumnDisplay) ? this.settings.enableSelectColumnDisplay : true; - let enableFilter; - if (isDefined(this.settings.enableFilter)) { - enableFilter = this.settings.enableFilter; - } else if (isDefined(this.settings.enableStatusFilter)) { - enableFilter = this.settings.enableStatusFilter; - } else { - enableFilter = true; - } - this.alarmFilterAction.show = enableFilter; - - this.rowStylesInfo = getRowStyleInfo(this.settings, 'alarm, ctx'); - - const pageSize = this.settings.defaultPageSize; - if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) { - this.defaultPageSize = pageSize; - } - this.pageSizeOptions = [this.defaultPageSize, this.defaultPageSize * 2, this.defaultPageSize * 3]; - this.pageLink.pageSize = this.displayPagination ? this.defaultPageSize : 1024; - - const alarmFilter = this.entityService.resolveAlarmFilter(this.widgetConfig.alarmFilterConfig); - this.pageLink = {...this.pageLink, ...alarmFilter}; - - this.noDataDisplayMessageText = - noDataMessage(this.widgetConfig.noDataDisplayMessage, 'alarm.no-alarms-prompt', this.utils, this.translate); - - const cssString = constructTableCssString(this.widgetConfig); - const cssParser = new cssjs(); - cssParser.testMode = false; - const namespace = 'alarms-table-' + hashCode(cssString); - cssParser.cssPreviewNamespace = namespace; - cssParser.createStyleElement(namespace, cssString); - $(this.elementRef.nativeElement).addClass(namespace); - } - - private updateTitle(updateWidgetParams = false) { - const newTitle = createLabelFromDatasource(this.subscription.alarmSource, this.alarmsTitlePattern); - if (this.ctx.widgetTitle !== newTitle) { - this.ctx.widgetTitle = newTitle; - if (updateWidgetParams) { - this.ctx.updateWidgetParams(); - } - } - } - - private updateAlarmSource() { - - if (this.enableSelection) { - this.displayedColumns.push('select'); - } - - const latestDataKeys: Array = []; - - if (this.subscription.alarmSource) { - this.subscription.alarmSource.dataKeys.forEach((alarmDataKey) => { - const dataKey: EntityColumn = deepClone(alarmDataKey) as EntityColumn; - const keySettings: TableWidgetDataKeySettings = dataKey.settings; - dataKey.entityKey = dataKeyToEntityKey(alarmDataKey); - dataKey.label = this.utils.customTranslation(dataKey.label, dataKey.label); - dataKey.title = getHeaderTitle(dataKey, keySettings, this.utils); - dataKey.def = 'def' + this.columns.length; - if (dataKey.type === DataKeyType.alarm && !isDefined(keySettings.columnWidth)) { - const alarmField = alarmFields[dataKey.name]; - if (alarmField && alarmField.time) { - keySettings.columnWidth = '120px'; - } - if (alarmField && alarmField.keyName === alarmFields.assignee.keyName) { - keySettings.columnWidth = '120px' - } - } - this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings, 'value, alarm, ctx'); - this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, alarm, ctx'); - this.contentsInfo[dataKey.def].units = dataKey.units; - this.contentsInfo[dataKey.def].decimals = dataKey.decimals; - this.columnWidth[dataKey.def] = getColumnWidth(keySettings); - this.columnDefaultVisibility[dataKey.def] = getColumnDefaultVisibility(keySettings, this.ctx); - this.columnSelectionAvailability[dataKey.def] = getColumnSelectionAvailability(keySettings); - this.columns.push(dataKey); - - if (dataKey.type !== DataKeyType.alarm) { - latestDataKeys.push(dataKey); - } - }); - this.displayedColumns.push(...this.columns.filter(column => this.columnDefaultVisibility[column.def]) - .map(column => column.def)); - } - if (this.settings.defaultSortOrder && this.settings.defaultSortOrder.length) { - this.defaultSortOrder = this.utils.customTranslation(this.settings.defaultSortOrder, this.settings.defaultSortOrder); - } - this.pageLink.sortOrder = entityDataSortOrderFromString(this.defaultSortOrder, this.columns); - let sortColumn: EntityColumn; - if (this.pageLink.sortOrder) { - sortColumn = findColumnByEntityKey(this.pageLink.sortOrder.key, this.columns); - } - this.sortOrderProperty = sortColumn ? sortColumn.def : null; - - const actionCellDescriptors: AlarmWidgetActionDescriptor[] = []; - if (this.displayActivity) { - actionCellDescriptors.push( - { - displayName: this.translate.instant('alarm-activity.activity'), - icon: 'comment', - activity: true - } as AlarmWidgetActionDescriptor - ); - } - - if (this.displayDetails) { - actionCellDescriptors.push( - { - displayName: this.translate.instant('alarm.details'), - icon: 'more_horiz', - details: true - } as AlarmWidgetActionDescriptor - ); - } - - if (this.allowAcknowledgment) { - actionCellDescriptors.push( - { - displayName: this.translate.instant('alarm.acknowledge'), - icon: 'done', - acknowledge: true - } as AlarmWidgetActionDescriptor - ); - } - - if (this.allowClear) { - actionCellDescriptors.push( - { - displayName: this.translate.instant('alarm.clear'), - icon: 'clear', - clear: true - } as AlarmWidgetActionDescriptor - ); - } - - this.setCellButtonAction = !!(actionCellDescriptors.length + this.ctx.actionsApi.getActionDescriptors('actionCellButton').length); - - if (this.setCellButtonAction) { - this.displayedColumns.push('actions'); - } - - this.alarmsDatasource = new AlarmsDatasource(this.subscription, latestDataKeys, this.ngZone, this.ctx, actionCellDescriptors); - if (this.enableSelection) { - this.alarmsDatasource.selectionModeChanged$.subscribe((selectionMode) => { - const hideTitlePanel = selectionMode || this.textSearchMode; - if (this.ctx.hideTitlePanel !== hideTitlePanel) { - this.ctx.hideTitlePanel = hideTitlePanel; - this.ctx.detectChanges(true); - } else { - this.ctx.detectChanges(); - } - }); - } - } - - private editColumnsToDisplay($event: Event) { - if ($event) { - $event.stopPropagation(); - } - const target = $event.target || $event.srcElement || $event.currentTarget; - const config = new OverlayConfig(); - config.backdropClass = 'cdk-overlay-transparent-backdrop'; - config.hasBackdrop = true; - const connectedPosition: ConnectedPosition = { - originX: 'end', - originY: 'bottom', - overlayX: 'end', - overlayY: 'top' - }; - config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement) - .withPositions([connectedPosition]); - - const overlayRef = this.overlay.create(config); - overlayRef.backdropClick().subscribe(() => { - overlayRef.dispose(); - }); - - const columns: DisplayColumn[] = this.columns.map(column => { - return { - title: column.title, - def: column.def, - display: this.displayedColumns.indexOf(column.def) > -1, - selectable: this.columnSelectionAvailability[column.def] - }; - }); - - const providers: StaticProvider[] = [ - { - provide: DISPLAY_COLUMNS_PANEL_DATA, - useValue: { - columns, - columnsUpdated: (newColumns) => { - this.displayedColumns = newColumns.filter(column => column.display).map(column => column.def); - if (this.enableSelection) { - this.displayedColumns.unshift('select'); - } - if (this.setCellButtonAction) { - this.displayedColumns.push('actions'); - } - this.clearCache(); - } - } as DisplayColumnsPanelData - }, - { - provide: OverlayRef, - useValue: overlayRef - } - ]; - const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); - overlayRef.attach(new ComponentPortal(DisplayColumnsPanelComponent, - this.viewContainerRef, injector)); - this.ctx.detectChanges(); - } - - private resetPageIndex(): void { - if (this.displayPagination) { - this.paginator.pageIndex = 0; - } - } - - private editAlarmFilter($event: Event) { - if ($event) { - $event.stopPropagation(); - } - const target = $event.target || $event.srcElement || $event.currentTarget; - const config = new OverlayConfig(); - config.backdropClass = 'cdk-overlay-transparent-backdrop'; - config.panelClass = 'tb-filter-panel'; - config.hasBackdrop = true; - const connectedPosition: ConnectedPosition = { - originX: 'end', - originY: 'bottom', - overlayX: 'end', - overlayY: 'top' - }; - config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement) - .withPositions([connectedPosition]); - - const overlayRef = this.overlay.create(config); - overlayRef.backdropClick().subscribe(() => { - overlayRef.dispose(); - }); - const providers: StaticProvider[] = [ - { - provide: ALARM_FILTER_CONFIG_DATA, - useValue: { - panelMode: true, - userMode: true, - alarmFilterConfig: { - statusList: deepClone(this.pageLink.statusList), - severityList: deepClone(this.pageLink.severityList), - typeList: deepClone(this.pageLink.typeList), - searchPropagatedAlarms: this.pageLink.searchPropagatedAlarms, - assignedToCurrentUser: isDefinedAndNotNull(this.pageLink.assigneeId) - } - } as AlarmFilterConfigData - }, - { - provide: OverlayRef, - useValue: overlayRef - } - ]; - const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); - const componentRef = overlayRef.attach(new ComponentPortal(AlarmFilterConfigComponent, - this.viewContainerRef, injector)); - componentRef.onDestroy(() => { - if (componentRef.instance.panelResult) { - const result = componentRef.instance.panelResult; - const alarmFilter = this.entityService.resolveAlarmFilter(result); - this.pageLink = {...this.pageLink, ...alarmFilter}; - this.resetPageIndex(); - this.updateData(); - } - }); - this.ctx.detectChanges(); - } - - private enterFilterMode() { - this.textSearchMode = true; - this.pageLink.textSearch = ''; - this.ctx.hideTitlePanel = true; - this.ctx.detectChanges(true); - setTimeout(() => { - this.searchInputField.nativeElement.focus(); - this.searchInputField.nativeElement.setSelectionRange(0, 0); - }, 10); - } - - exitFilterMode() { - this.textSearchMode = false; - this.pageLink.textSearch = null; - this.resetPageIndex(); - this.updateData(); - this.ctx.hideTitlePanel = false; - this.ctx.detectChanges(true); - } - - private updateData() { - if (this.displayPagination) { - this.pageLink.page = this.paginator.pageIndex; - this.pageLink.pageSize = this.paginator.pageSize; - } else { - this.pageLink.page = 0; - } - const key = findEntityKeyByColumnDef(this.sort.active, this.columns); - if (key) { - this.pageLink.sortOrder = { - key, - direction: Direction[this.sort.direction.toUpperCase()] - }; - } else { - this.pageLink.sortOrder = null; - } - const sortOrderLabel = fromEntityColumnDef(this.sort.active, this.columns); - const keyFilters: KeyFilter[] = null; // TODO: - this.alarmsDatasource.loadAlarms(this.pageLink, sortOrderLabel, keyFilters); - this.ctx.detectChanges(); - } - - public trackByColumnDef(index, column: EntityColumn) { - return column.def; - } - - public trackByAlarmId(index: number, alarm: AlarmData) { - return alarm.id.id; - } - - public trackByActionCellDescriptionId(index: number, action: WidgetActionDescriptor) { - return action.id; - } - - public headerStyle(key: EntityColumn): any { - const columnWidth = this.columnWidth[key.def]; - return widthStyle(columnWidth); - } - - public rowStyle(alarm: AlarmDataInfo, row: number): any { - let res = this.rowStyleCache[row]; - if (!res) { - res = {}; - if (alarm && this.rowStylesInfo.useRowStyleFunction && this.rowStylesInfo.rowStyleFunction) { - try { - res = this.rowStylesInfo.rowStyleFunction(alarm, this.ctx); - if (!isObject(res)) { - throw new TypeError(`${res === null ? 'null' : typeof res} instead of style object`); - } - if (Array.isArray(res)) { - throw new TypeError(`Array instead of style object`); - } - } catch (e) { - res = {}; - console.warn(`Row style function in widget '${this.ctx.widgetTitle}' ` + - `returns '${e}'. Please check your row style function.`); - } - } - this.rowStyleCache[row] = res; - } - return res; - } - - public cellStyle(alarm: AlarmDataInfo, key: EntityColumn, row: number): any { - const col = this.columns.indexOf(key); - const index = row * this.columns.length + col; - let res = this.cellStyleCache[index]; - if (!res) { - res = {}; - if (alarm && key) { - const styleInfo = this.stylesInfo[key.def]; - const value = getAlarmValue(alarm, key); - if (styleInfo.useCellStyleFunction && styleInfo.cellStyleFunction) { - try { - res = styleInfo.cellStyleFunction(value, alarm, this.ctx); - if (!isObject(res)) { - throw new TypeError(`${res === null ? 'null' : typeof res} instead of style object`); - } - if (Array.isArray(res)) { - throw new TypeError(`Array instead of style object`); - } - } catch (e) { - res = {}; - console.warn(`Cell style function for data key '${key.label}' in widget '${this.ctx.widgetTitle}' ` + - `returns '${e}'. Please check your cell style function.`); - } - } else { - res = this.defaultStyle(key, value); - } - } - this.cellStyleCache[index] = res; - } - if (!res.width) { - const columnWidth = this.columnWidth[key.def]; - res = Object.assign(res, widthStyle(columnWidth)); - } - return res; - } - - public cellContent(alarm: AlarmDataInfo, key: EntityColumn, row: number): SafeHtml { - const col = this.columns.indexOf(key); - const index = row * this.columns.length + col; - let res = this.cellContentCache[index]; - if (isUndefined(res)) { - res = ''; - if (alarm && key) { - const contentInfo = this.contentsInfo[key.def]; - const value = getAlarmValue(alarm, key); - let content = ''; - if (contentInfo.useCellContentFunction && contentInfo.cellContentFunction) { - try { - content = contentInfo.cellContentFunction(value, alarm, this.ctx); - } catch (e) { - content = '' + value; - } - } else { - content = this.defaultContent(key, contentInfo, value); - } - - if (isDefined(content)) { - content = this.utils.customTranslation(content, content); - switch (typeof content) { - case 'string': - res = this.domSanitizer.bypassSecurityTrustHtml(content); - break; - default: - res = content; - } - } - } - this.cellContentCache[index] = res; - } - return res; - } - - public onRowClick($event: Event, alarm: AlarmDataInfo) { - if ($event) { - $event.stopPropagation(); - } - this.alarmsDatasource.toggleCurrentAlarm(alarm); - const descriptors = this.ctx.actionsApi.getActionDescriptors('rowClick'); - if (descriptors.length) { - let entityId; - let entityName; - let entityLabel; - if (alarm && alarm.originator) { - entityId = alarm.originator; - entityName = alarm.originatorName; - entityLabel = alarm.originatorLabel; - } - this.ctx.actionsApi.handleWidgetAction($event, descriptors[0], entityId, entityName, {alarm}, entityLabel); - } - } - - public onActionButtonClick($event: Event, alarm: AlarmDataInfo, actionDescriptor: AlarmWidgetActionDescriptor) { - if (actionDescriptor.details) { - this.openAlarmDetails($event, alarm); - } else if (actionDescriptor.acknowledge) { - this.ackAlarm($event, alarm); - } else if (actionDescriptor.clear) { - this.clearAlarm($event, alarm); - } else if (actionDescriptor.activity) { - this.openAlarmActivity($event, alarm); - } else { - if ($event) { - $event.stopPropagation(); - } - let entityId; - let entityName; - let entityLabel; - if (alarm && alarm.originator) { - entityId = alarm.originator; - entityName = alarm.originatorName; - entityLabel = alarm.originatorLabel; - } - this.ctx.actionsApi.handleWidgetAction($event, actionDescriptor, entityId, entityName, {alarm}, entityLabel); - } - } - - public actionEnabled(alarm: AlarmDataInfo, actionDescriptor: AlarmWidgetActionDescriptor): boolean { - if (actionDescriptor.acknowledge) { - return (alarm.status === AlarmStatus.ACTIVE_UNACK || - alarm.status === AlarmStatus.CLEARED_UNACK); - } else if (actionDescriptor.clear) { - return (alarm.status === AlarmStatus.ACTIVE_ACK || - alarm.status === AlarmStatus.ACTIVE_UNACK); - } - return true; - } - - private openAlarmDetails($event: Event, alarm: AlarmDataInfo) { - if ($event) { - $event.stopPropagation(); - } - if (alarm && alarm.id && alarm.id.id !== NULL_UUID) { - this.dialog.open - (AlarmDetailsDialogComponent, - { - disableClose: true, - panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], - data: { - alarmId: alarm.id.id, - allowAcknowledgment: this.allowAcknowledgment, - allowClear: this.allowClear, - displayDetails: true, - allowAssign: this.allowAssign - } - }).afterClosed().subscribe( - (res) => { - if (res) { - this.subscription.update(); - } - } - ); - } - } - - private ackAlarm($event: Event, alarm: AlarmDataInfo) { - if ($event) { - $event.stopPropagation(); - } - if (alarm && alarm.id && alarm.id.id !== NULL_UUID) { - this.dialogService.confirm( - this.translate.instant('alarm.aknowledge-alarm-title'), - this.translate.instant('alarm.aknowledge-alarm-text'), - this.translate.instant('action.no'), - this.translate.instant('action.yes') - ).subscribe((res) => { - if (res) { - if (res) { - this.alarmService.ackAlarm(alarm.id.id).subscribe(() => { - this.subscription.update(); - }); - } - } - }); - } - } - - public ackAlarms($event: Event) { - if ($event) { - $event.stopPropagation(); - } - if (this.alarmsDatasource.selection.hasValue()) { - const alarmIds = this.alarmsDatasource.selection.selected.filter( - (alarmId) => alarmId !== NULL_UUID - ); - if (alarmIds.length) { - const title = this.translate.instant('alarm.aknowledge-alarms-title', {count: alarmIds.length}); - const content = this.translate.instant('alarm.aknowledge-alarms-text', {count: alarmIds.length}); - this.dialogService.confirm( - title, - content, - this.translate.instant('action.no'), - this.translate.instant('action.yes') - ).subscribe((res) => { - if (res) { - if (res) { - const tasks: Observable[] = []; - for (const alarmId of alarmIds) { - tasks.push(this.alarmService.ackAlarm(alarmId)); - } - forkJoin(tasks).subscribe(() => { - this.alarmsDatasource.clearSelection(); - this.subscription.update(); - }); - } - } - }); - } - } - } - - private clearAlarm($event: Event, alarm: AlarmDataInfo) { - if ($event) { - $event.stopPropagation(); - } - if (alarm && alarm.id && alarm.id.id !== NULL_UUID) { - this.dialogService.confirm( - this.translate.instant('alarm.clear-alarm-title'), - this.translate.instant('alarm.clear-alarm-text'), - this.translate.instant('action.no'), - this.translate.instant('action.yes') - ).subscribe((res) => { - if (res) { - if (res) { - this.alarmService.clearAlarm(alarm.id.id).subscribe(() => { - this.subscription.update(); - }); - } - } - }); - } - } - - public clearAlarms($event: Event) { - if ($event) { - $event.stopPropagation(); - } - if (this.alarmsDatasource.selection.hasValue()) { - const alarmIds = this.alarmsDatasource.selection.selected.filter( - (alarmId) => alarmId !== NULL_UUID - ); - if (alarmIds.length) { - const title = this.translate.instant('alarm.clear-alarms-title', {count: alarmIds.length}); - const content = this.translate.instant('alarm.clear-alarms-text', {count: alarmIds.length}); - this.dialogService.confirm( - title, - content, - this.translate.instant('action.no'), - this.translate.instant('action.yes') - ).subscribe((res) => { - if (res) { - if (res) { - const tasks: Observable[] = []; - for (const alarmId of alarmIds) { - tasks.push(this.alarmService.clearAlarm(alarmId)); - } - forkJoin(tasks).subscribe(() => { - this.alarmsDatasource.clearSelection(); - this.subscription.update(); - }); - } - } - }); - } - } - } - - private openAlarmActivity($event: Event, alarm: AlarmDataInfo) { - if ($event) { - $event.stopPropagation(); - } - if (alarm && alarm.id && alarm.id.id !== NULL_UUID) { - this.dialog.open - (AlarmCommentDialogComponent, - { - disableClose: true, - panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], - data: { - alarmId: alarm.id.id - } - }).afterClosed() - } - } - - private defaultContent(key: EntityColumn, contentInfo: CellContentInfo, value: any): any { - if (isDefined(value)) { - const alarmField = alarmFields[key.name]; - if (alarmField) { - if (alarmField.time) { - return value ? this.datePipe.transform(value, 'yyyy-MM-dd HH:mm:ss') : ''; - } else if (alarmField.value === alarmFields.severity.value) { - return this.translate.instant(alarmSeverityTranslations.get(value)); - } else if (alarmField.value === alarmFields.status.value) { - return alarmStatusTranslations.get(value) ? this.translate.instant(alarmStatusTranslations.get(value)) : value; - } else if (alarmField.value === alarmFields.originatorType.value) { - return this.translate.instant(entityTypeTranslations.get(value).type); - } else if (alarmField.value === alarmFields.assignee.value) { - return ''; - } - else { - return value; - } - } - const entityField = entityFields[key.name]; - if (entityField) { - if (entityField.time) { - return this.datePipe.transform(value, 'yyyy-MM-dd HH:mm:ss'); - } - } - const decimals = (contentInfo.decimals || contentInfo.decimals === 0) ? contentInfo.decimals : this.ctx.widgetConfig.decimals; - const units = contentInfo.units || this.ctx.widgetConfig.units; - return this.ctx.utils.formatValue(value, decimals, units, true); - } else { - return ''; - } - } - - private defaultStyle(key: EntityColumn, value: any): any { - if (isDefined(value)) { - const alarmField = alarmFields[key.name]; - if (alarmField) { - if (alarmField.value === alarmFields.severity.value) { - return { - fontWeight: 'bold', - color: alarmSeverityColors.get(value) - }; - } else { - return {}; - } - } else { - return {}; - } - } else { - return {}; - } - } - - isSorting(column: EntityColumn): boolean { - return column.type === DataKeyType.alarm && column.name.startsWith('details.'); - } - - private clearCache() { - this.cellContentCache.length = 0; - this.cellStyleCache.length = 0; - this.rowStyleCache.length = 0; - } - - getUserDisplayName(entity: AlarmInfo) { - let displayName = ''; - if ((entity.assignee.firstName && entity.assignee.firstName.length > 0) || - (entity.assignee.lastName && entity.assignee.lastName.length > 0)) { - if (entity.assignee.firstName) { - displayName += entity.assignee.firstName; - } - if (entity.assignee.lastName) { - if (displayName.length > 0) { - displayName += ' '; - } - displayName += entity.assignee.lastName; - } - } else { - displayName = entity.assignee.email; - } - return displayName; - } - - getUserInitials(entity: AlarmInfo): string { - let initials = ''; - if (entity.assignee.firstName && entity.assignee.firstName.length || - entity.assignee.lastName && entity.assignee.lastName.length) { - if (entity.assignee.firstName) { - initials += entity.assignee.firstName.charAt(0); - } - if (entity.assignee.lastName) { - initials += entity.assignee.lastName.charAt(0); - } - } else { - initials += entity.assignee.email.charAt(0); - } - return initials.toUpperCase(); - } - - getAvatarBgColor(entity: AlarmInfo) { - return this.utils.stringToHslColor(this.getUserDisplayName(entity), 40, 60); - } - - openAlarmAssigneePanel($event: Event, entity: AlarmInfo) { - if ($event) { - $event.stopPropagation(); - } - const target = $event.target || $event.srcElement || $event.currentTarget; - const config = new OverlayConfig(); - config.backdropClass = 'cdk-overlay-transparent-backdrop'; - config.hasBackdrop = true; - const connectedPosition: ConnectedPosition = { - originX: 'end', - originY: 'bottom', - overlayX: 'end', - overlayY: 'top' - }; - config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement) - .withPositions([connectedPosition]); - config.minWidth = '260px'; - const overlayRef = this.overlay.create(config); - overlayRef.backdropClick().subscribe(() => { - overlayRef.dispose(); - }); - const providers: StaticProvider[] = [ - { - provide: ALARM_ASSIGNEE_PANEL_DATA, - useValue: { - alarmId: entity.id.id, - assigneeId: entity.assigneeId?.id - } as AlarmAssigneePanelData - }, - { - provide: OverlayRef, - useValue: overlayRef - } - ]; - const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); - overlayRef.attach(new ComponentPortal(AlarmAssigneePanelComponent, - this.viewContainerRef, injector)); - } -} - -class AlarmsDatasource implements DataSource { - - private alarmsSubject = new BehaviorSubject([]); - private pageDataSubject = new BehaviorSubject>(emptyPageData()); - - public selection = new SelectionModel(true, [], false); - - private selectionModeChanged = new EventEmitter(); - - public selectionModeChanged$ = this.selectionModeChanged.asObservable(); - - private currentAlarm: AlarmDataInfo = null; - - public dataLoading = true; - public countCellButtonAction = 0; - - private appliedPageLink: AlarmDataPageLink; - private appliedSortOrderLabel: string; - - private reserveSpaceForHiddenAction = true; - private cellButtonActions: TableCellButtonActionDescriptor[]; - private readonly usedShowCellActionFunction: boolean; - - constructor(private subscription: IWidgetSubscription, - private dataKeys: Array, - private ngZone: NgZone, - private widgetContext: WidgetContext, - actionCellDescriptors: AlarmWidgetActionDescriptor[]) { - this.cellButtonActions = actionCellDescriptors.concat(getTableCellButtonActions(widgetContext)); - this.usedShowCellActionFunction = this.cellButtonActions.some(action => action.useShowActionCellButtonFunction); - if (this.widgetContext.settings.reserveSpaceForHiddenAction) { - this.reserveSpaceForHiddenAction = coerceBooleanProperty(this.widgetContext.settings.reserveSpaceForHiddenAction); - } - } - - connect(collectionViewer: CollectionViewer): Observable> { - return this.alarmsSubject.asObservable(); - } - - disconnect(collectionViewer: CollectionViewer): void { - this.alarmsSubject.complete(); - this.pageDataSubject.complete(); - } - - loadAlarms(pageLink: AlarmDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) { - this.dataLoading = true; - // this.clear(); - this.appliedPageLink = pageLink; - this.appliedSortOrderLabel = sortOrderLabel; - this.subscription.subscribeForAlarms(pageLink, keyFilters); - } - - private clear() { - if (this.selection.hasValue()) { - this.selection.clear(); - this.onSelectionModeChanged(false); - } - this.alarmsSubject.next([]); - this.pageDataSubject.next(emptyPageData()); - } - - updateAlarms() { - const subscriptionAlarms = this.subscription.alarms; - let alarms = new Array(); - let maxCellButtonAction = 0; - let isEmptySelection = false; - const dynamicWidthCellButtonActions = this.usedShowCellActionFunction && !this.reserveSpaceForHiddenAction; - subscriptionAlarms.data.forEach((alarmData) => { - const alarm = this.alarmDataToInfo(alarmData); - alarms.push(alarm); - if (dynamicWidthCellButtonActions && alarm.actionCellButtons.length > maxCellButtonAction) { - maxCellButtonAction = alarm.actionCellButtons.length; - } - }); - if (!dynamicWidthCellButtonActions && this.cellButtonActions.length && alarms.length) { - maxCellButtonAction = alarms[0].actionCellButtons.length; - } - if (this.appliedSortOrderLabel && this.appliedSortOrderLabel.length) { - const asc = this.appliedPageLink.sortOrder.direction === Direction.ASC; - alarms = alarms.sort((a, b) => sortItems(a, b, this.appliedSortOrderLabel, asc)); - } - if (this.selection.hasValue()) { - const alarmIds = alarms.map((alarm) => alarm.id.id); - const toRemove = this.selection.selected.filter(alarmId => alarmIds.indexOf(alarmId) === -1); - this.selection.deselect(...toRemove); - if (this.selection.isEmpty()) { - isEmptySelection = true; - } - } - const alarmsPageData: PageData = { - data: alarms, - totalPages: subscriptionAlarms.totalPages, - totalElements: subscriptionAlarms.totalElements, - hasNext: subscriptionAlarms.hasNext - }; - this.ngZone.run(() => { - if (isEmptySelection) { - this.onSelectionModeChanged(false); - } - this.alarmsSubject.next(alarms); - this.pageDataSubject.next(alarmsPageData); - this.countCellButtonAction = maxCellButtonAction; - this.dataLoading = false; - }); - } - - private alarmDataToInfo(alarmData: AlarmData): AlarmDataInfo { - const alarm: AlarmDataInfo = deepClone(alarmData); - delete alarm.latest; - const latest = alarmData.latest; - this.dataKeys.forEach((dataKey, index) => { - const type = dataKeyTypeToEntityKeyType(dataKey.type); - let value = ''; - if (type) { - if (latest && latest[type]) { - const tsVal = latest[type][dataKey.name]; - if (tsVal) { - value = tsVal.value; - } - } - } - alarm[dataKey.label] = value; - }); - if (this.cellButtonActions.length) { - if (this.usedShowCellActionFunction) { - alarm.actionCellButtons = prepareTableCellButtonActions(this.widgetContext, this.cellButtonActions, - alarm, this.reserveSpaceForHiddenAction); - alarm.hasActions = checkHasActions(alarm.actionCellButtons); - } else { - alarm.actionCellButtons = this.cellButtonActions; - alarm.hasActions = true; - } - } - return alarm; - } - - isAllSelected(): Observable { - const numSelected = this.selection.selected.length; - return this.alarmsSubject.pipe( - map((alarms) => numSelected === alarms.length) - ); - } - - isEmpty(): Observable { - return this.alarmsSubject.pipe( - map((alarms) => !alarms.length) - ); - } - - total(): Observable { - return this.pageDataSubject.pipe( - map((pageData) => pageData.totalElements) - ); - } - - toggleSelection(alarm: AlarmDataInfo) { - const hasValue = this.selection.hasValue(); - this.selection.toggle(alarm.id.id); - if (hasValue !== this.selection.hasValue()) { - this.onSelectionModeChanged(this.selection.hasValue()); - } - } - - isSelected(alarm: AlarmDataInfo): boolean { - return this.selection.isSelected(alarm.id.id); - } - - clearSelection() { - if (this.selection.hasValue()) { - this.selection.clear(); - this.onSelectionModeChanged(false); - } - } - - masterToggle() { - this.alarmsSubject.pipe( - tap((alarms) => { - const numSelected = this.selection.selected.length; - if (numSelected === alarms.length) { - this.selection.clear(); - if (numSelected > 0) { - this.onSelectionModeChanged(false); - } - } else { - alarms.forEach(row => { - this.selection.select(row.id.id); - }); - if (numSelected === 0) { - this.onSelectionModeChanged(true); - } - } - }), - take(1) - ).subscribe(); - } - - public toggleCurrentAlarm(alarm: AlarmDataInfo): boolean { - if (this.currentAlarm !== alarm) { - this.currentAlarm = alarm; - return true; - } else { - return false; - } - } - - public isCurrentAlarm(alarm: AlarmDataInfo): boolean { - return (this.currentAlarm && alarm && this.currentAlarm.id && alarm.id) && - (this.currentAlarm.id.id === alarm.id.id); - } - - private onSelectionModeChanged(selectionMode: boolean) { - this.selectionModeChanged.emit(selectionMode); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/analogue-gauge.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-gauge.models.ts index 31f40594ba..cdfe601b17 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/analogue-gauge.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-gauge.models.ts @@ -85,7 +85,7 @@ export abstract class TbBaseGauge { if (cellData.data.length > 0) { const tvPair = cellData.data[cellData.data.length - 1]; - const value = tvPair[1]; + const value = parseFloat(tvPair[1]); if (value !== this.gauge.value) { this.gauge.value = value; } @@ -130,7 +130,7 @@ export abstract class TbAnalogueGauge +
+
+
+ + +
+ + + + + +
+ + +
{{ subtitle$ | async }}
+
+ +
+
+
+ + + + + + +
+
+ + + +
+
+ + + + + + +
+
+
+
+ +
+
+
{{tickMax$ | async}}
+
{{tickMin$ | async}}
+
+
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+ + arrow_upward + arrow_downward +
+
+ {{ value.value }} + {{ value.units }} +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.scss new file mode 100644 index 0000000000..3f7d33d57b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.scss @@ -0,0 +1,163 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + .tb-aggregated-value-card-panel { + width: 100%; + height: 100%; + position: relative; + display: flex; + flex-direction: column; + align-items: stretch; + gap: 2px; + padding: 20px 24px 24px; + > div:not(.tb-value-card-overlay) { + z-index: 1; + } + .tb-aggregated-value-card-overlay { + position: absolute; + top: 12px; + left: 12px; + bottom: 12px; + right: 12px; + } + > div.tb-aggregated-value-card-title-panel { + display: flex; + flex-direction: column; + .tb-aggregated-value-card-subtitle { + margin-left: 28px; + } + } + .tb-aggregated-value-card-values, .tb-aggregated-value-card-chart { + flex: 1; + min-height: 0; + overflow: hidden; + } + .tb-aggregated-value-card-values { + padding: 8px 0; + position: relative; + display: flex; + align-items: center; + justify-content: center; + } + .tb-aggregated-value-card-values-container { + width: 100%; + height: 100%; + position: relative; + display: grid; + grid-template-columns: minmax(0, 1fr) fit-content(100%) minmax(0, 1fr); + .tb-aggregated-value-card-values-section { + display: flex; + flex-direction: column; + justify-content: center; + overflow: hidden; + &.left { + align-items: flex-start; + } + &.center { + align-items: center; + } + &.right { + align-items: flex-end; + } + } + } + .tb-aggregated-value-card-chart { + display: flex; + gap: 8px; + flex-direction: row; + .tb-aggregated-value-card-chart-ticks { + height: 100%; + display: flex; + flex-direction: column; + place-content: flex-end space-between; + align-items: flex-end; + font-size: 11px; + line-height: 16px; + font-weight: 400; + color: rgba(0, 0, 0, 0.38); + } + .tb-aggregated-value-card-chart-container { + position: relative; + flex: 1; + margin-top: 8px; + margin-bottom: 8px; + .tb-aggregated-value-card-chart-element { + width: 100%; + height: 100%; + } + .tb-aggregated-value-card-chart-boundary { + position: absolute; + width: 6px; + height: 6px; + &.top { + top: 0; + border-top: 2px solid rgba(0,0,0,0.38); + } + &.left { + left: 0; + border-left: 2px solid rgba(0,0,0,0.38); + } + &.right { + right: 0; + border-right: 2px solid rgba(0,0,0,0.38); + } + &.bottom { + bottom: 0; + border-bottom: 2px solid rgba(0,0,0,0.38); + } + } + } + } + .tb-aggregated-value-card-value { + white-space: nowrap; + min-height: 0; + display: flex; + flex-direction: row; + place-content: center; + align-items: center; + .value-arrow-container { + display: flex; + } + .value-text { + line-height: 1; + } + .value-arrow { + font-size: 1.1em; + height: 1.1em; + line-height: 1.1em; + min-width: 1em; + width: 1em; + } + .units { + font-size: 85%; + padding-left: 0.2em; + &.small { + font-size: 50%; + } + } + } + } +} + +:host ::ng-deep { + .tb-aggregated-value-card-panel { + > div.tb-aggregated-value-card-title-panel { + .tb-widget-title { + padding: 0; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts new file mode 100644 index 0000000000..863c42985a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts @@ -0,0 +1,296 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + Input, + OnDestroy, + OnInit, + Renderer2, + TemplateRef, + ViewChild +} from '@angular/core'; +import { + aggregatedValueCardDefaultSettings, + AggregatedValueCardKeyPosition, + AggregatedValueCardValue, + AggregatedValueCardWidgetSettings, + computeAggregatedCardValue, + getTsValueByLatestDataKey +} from '@home/components/widget/lib/cards/aggregated-value-card.models'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { Observable } from 'rxjs'; +import { + backgroundStyle, + ComponentStyle, + DateFormatProcessor, + getDataKey, + getLatestSingleTsValue, + overlayStyle, + textStyle +} from '@shared/models/widget-settings.models'; +import { TbFlot } from '@home/components/widget/lib/flot-widget'; +import { TbFlotKeySettings, TbFlotSettings } from '@home/components/widget/lib/flot-widget.models'; +import { DataKey } from '@shared/models/widget.models'; +import { formatNumberValue, formatValue, isDefined, isDefinedAndNotNull, isNumeric } from '@core/utils'; +import { map } from 'rxjs/operators'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { DomSanitizer } from '@angular/platform-browser'; + +const valuesLayoutHeight = 66; +const valuesLayoutVerticalPadding = 16; + +@Component({ + selector: 'tb-aggregated-value-card-widget', + templateUrl: './aggregated-value-card-widget.component.html', + styleUrls: ['./aggregated-value-card-widget.component.scss'] +}) +export class AggregatedValueCardWidgetComponent implements OnInit, AfterViewInit, OnDestroy { + + @ViewChild('chartElement', {static: false}) chartElement: ElementRef; + + @ViewChild('valueCardValues', {static: false}) + valueCardValues: ElementRef; + + @ViewChild('valueCardValueContainer', {static: false}) + valueCardValueContainer: ElementRef; + + aggregatedValueCardKeyPosition = AggregatedValueCardKeyPosition; + + settings: AggregatedValueCardWidgetSettings; + + @Input() + ctx: WidgetContext; + + @Input() + widgetTitlePanel: TemplateRef; + + showSubtitle = true; + subtitle$: Observable; + subtitleStyle: ComponentStyle = {}; + subtitleColor: string; + + showValues = false; + + values: {[key: string]: AggregatedValueCardValue} = {}; + + showChart = true; + + showDate = true; + dateFormat: DateFormatProcessor; + dateStyle: ComponentStyle = {}; + dateColor: string; + + backgroundStyle$: Observable; + overlayStyle: ComponentStyle = {}; + + private flot: TbFlot; + private flotDataKey: DataKey; + + private lastUpdateTs: number; + + tickMin$: Observable; + tickMax$: Observable; + + private panelResize$: ResizeObserver; + + constructor(private imagePipe: ImagePipe, + private sanitizer: DomSanitizer, + private renderer: Renderer2, + private cd: ChangeDetectorRef) { + } + + ngOnInit(): void { + this.ctx.$scope.aggregatedValueCardWidget = this; + this.settings = {...aggregatedValueCardDefaultSettings, ...this.ctx.settings}; + this.showSubtitle = this.settings.showSubtitle && this.ctx.datasources?.length > 0; + const subtitle = this.settings.subtitle; + this.subtitle$ = this.ctx.registerLabelPattern(subtitle, this.subtitle$); + this.subtitleStyle = textStyle(this.settings.subtitleFont); + this.subtitleColor = this.settings.subtitleColor; + + const dataKey = getDataKey(this.ctx.defaultSubscription.datasources); + if (dataKey?.name && this.ctx.defaultSubscription.firstDatasource?.latestDataKeys?.length) { + const dataKeys = this.ctx.defaultSubscription.firstDatasource?.latestDataKeys; + for (const position of Object.keys(AggregatedValueCardKeyPosition)) { + const value = computeAggregatedCardValue(dataKeys, dataKey?.name, AggregatedValueCardKeyPosition[position]); + if (value) { + this.values[position] = value; + } + } + this.showValues = !!Object.keys(this.values).length; + } + + this.showChart = this.settings.showChart; + if (this.showChart) { + if (this.ctx.defaultSubscription.firstDatasource?.dataKeys?.length) { + this.flotDataKey = this.ctx.defaultSubscription.firstDatasource?.dataKeys[0]; + this.flotDataKey.settings = { + fillLines: false, + showLines: true, + lineWidth: 2 + } as TbFlotKeySettings; + } + } + + this.showDate = this.settings.showDate; + this.dateFormat = DateFormatProcessor.fromSettings(this.ctx.$injector, this.settings.dateFormat); + this.dateStyle = textStyle(this.settings.dateFont); + this.dateColor = this.settings.dateColor; + + this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); + this.overlayStyle = overlayStyle(this.settings.background.overlay); + } + + ngAfterViewInit(): void { + if (this.showChart && this.ctx.datasources?.length) { + const settings = { + shadowSize: 0, + enableSelection: false, + smoothLines: false, + grid: { + tickColor: 'rgba(0,0,0,0.12)', + horizontalLines: true, + verticalLines: false, + outlineWidth: 0, + minBorderMargin: 0, + margin: 0 + }, + yaxis: { + showLabels: false, + tickGenerator: 'return [(axis.max + axis.min) / 2];' + }, + xaxis: { + showLabels: false + } + } as TbFlotSettings; + this.flot = new TbFlot(this.ctx, 'line', $(this.chartElement.nativeElement), settings); + this.tickMin$ = this.flot.yMin$.pipe( + map((value) => formatValue(value, (this.flotDataKey?.decimals || this.ctx.decimals)) + )); + this.tickMax$ = this.flot.yMax$.pipe( + map((value) => formatValue(value, (this.flotDataKey?.decimals || this.ctx.decimals)) + )); + } + if (this.settings.autoScale && this.showValues) { + this.renderer.setStyle(this.valueCardValueContainer.nativeElement, 'height', valuesLayoutHeight + 'px'); + this.renderer.setStyle(this.valueCardValueContainer.nativeElement, 'overflow', 'visible'); + this.renderer.setStyle(this.valueCardValueContainer.nativeElement, 'position', 'absolute'); + this.panelResize$ = new ResizeObserver(() => { + this.onValueCardValuesResize(); + }); + this.panelResize$.observe(this.valueCardValues.nativeElement); + this.onValueCardValuesResize(); + } + } + + ngOnDestroy() { + if (this.panelResize$) { + this.panelResize$.disconnect(); + } + } + + public onInit() { + const borderRadius = this.ctx.$widgetElement.css('borderRadius'); + this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; + this.cd.detectChanges(); + } + + public onDataUpdated() { + const tsValue = getLatestSingleTsValue(this.ctx.data); + let ts; + if (tsValue) { + ts = tsValue[0]; + } + + if (this.showChart) { + this.flot.update(); + } + + this.updateLastUpdateTs(ts); + this.cd.detectChanges(); + } + + public onLatestDataUpdated() { + if (this.showValues) { + for (const aggValue of Object.values(this.values)) { + const tsValue = getTsValueByLatestDataKey(this.ctx.latestData, aggValue.key); + let ts; + let value; + if (tsValue && isDefinedAndNotNull(tsValue[1]) && isNumeric(tsValue[1])) { + ts = tsValue[0]; + value = tsValue[1]; + aggValue.value = formatValue(value, (aggValue.key.decimals || this.ctx.decimals), null, false); + } else { + aggValue.value = 'N/A'; + } + const numeric = formatNumberValue(value, (aggValue.key.decimals || this.ctx.decimals)); + aggValue.color.update(numeric); + if (aggValue.showArrow && isDefined(numeric)) { + aggValue.upArrow = numeric > 0; + aggValue.downArrow = numeric < 0; + } else { + aggValue.upArrow = aggValue.downArrow = false; + } + this.updateLastUpdateTs(ts); + } + this.cd.detectChanges(); + } + } + + public onResize() { + if (this.showChart) { + this.flot.resize(); + } + } + + public onEditModeChanged() { + if (this.showChart) { + this.flot.checkMouseEvents(); + } + } + + public onDestroy() { + if (this.showChart) { + this.flot.destroy(); + } + } + + private updateLastUpdateTs(ts: number) { + if (ts && (!this.lastUpdateTs || ts > this.lastUpdateTs)) { + this.lastUpdateTs = ts; + this.dateFormat.update(ts); + } + } + + private onValueCardValuesResize() { + const panelWidth = this.valueCardValues.nativeElement.getBoundingClientRect().width; + const panelHeight = this.valueCardValues.nativeElement.getBoundingClientRect().height - valuesLayoutVerticalPadding; + const targetWidth = panelWidth; + const minAspect = 0.25; + const aspect = Math.min(panelHeight / targetWidth, minAspect); + const targetHeight = targetWidth * aspect; + const scale = targetHeight / valuesLayoutHeight; + const width = targetWidth / scale; + this.renderer.setStyle(this.valueCardValueContainer.nativeElement, 'width', width + 'px'); + this.renderer.setStyle(this.valueCardValueContainer.nativeElement, 'transform', `scale(${scale})`); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card.models.ts new file mode 100644 index 0000000000..1d02d71cdf --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card.models.ts @@ -0,0 +1,163 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + BackgroundSettings, + BackgroundType, + ColorProcessor, + ColorSettings, + ColorType, + ComponentStyle, + constantColor, + DateFormatSettings, + Font, + lastUpdateAgoDateFormat, + textStyle +} from '@shared/models/widget-settings.models'; +import { ComparisonResultType, DataKey, DatasourceData } from '@shared/models/widget.models'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { AggregationType } from '@shared/models/time/time.models'; + +export interface AggregatedValueCardWidgetSettings { + autoScale: boolean; + showSubtitle: boolean; + subtitle: string; + subtitleFont: Font; + subtitleColor: string; + showDate: boolean; + dateFormat: DateFormatSettings; + dateFont: Font; + dateColor: string; + showChart: boolean; + background: BackgroundSettings; +} + +export enum AggregatedValueCardKeyPosition { + center = 'center', + rightTop = 'rightTop', + rightBottom = 'rightBottom', + leftTop = 'leftTop', + leftBottom = 'leftBottom' +} + +export const aggregatedValueCardKeyPositionTranslations = new Map( + [ + [AggregatedValueCardKeyPosition.center, 'widgets.aggregated-value-card.position-center'], + [AggregatedValueCardKeyPosition.rightTop, 'widgets.aggregated-value-card.position-right-top'], + [AggregatedValueCardKeyPosition.rightBottom, 'widgets.aggregated-value-card.position-right-bottom'], + [AggregatedValueCardKeyPosition.leftTop, 'widgets.aggregated-value-card.position-left-top'], + [AggregatedValueCardKeyPosition.leftBottom, 'widgets.aggregated-value-card.position-left-bottom'] + ] +); + +export interface AggregatedValueCardKeySettings { + position: AggregatedValueCardKeyPosition; + font: Font; + color: ColorSettings; + showArrow: boolean; +} + +export interface AggregatedValueCardValue { + key: DataKey; + value: string; + units: string; + style: ComponentStyle; + color: ColorProcessor; + center: boolean; + showArrow: boolean; + upArrow: boolean; + downArrow: boolean; +} + +export const computeAggregatedCardValue = + (dataKeys: DataKey[], keyName: string, position: AggregatedValueCardKeyPosition): AggregatedValueCardValue => { + const key = dataKeys.find(dataKey => ( dataKey.name === keyName && (dataKey.settings?.position === position || + (!dataKey.settings?.position && position === AggregatedValueCardKeyPosition.center)) )); + if (key) { + const settings: AggregatedValueCardKeySettings = key.settings; + return { + key, + value: '', + units: key.units, + style: textStyle(settings.font), + color: ColorProcessor.fromSettings(settings.color), + center: position === AggregatedValueCardKeyPosition.center, + showArrow: settings.showArrow, + upArrow: false, + downArrow: false + }; + } +}; + +export const getTsValueByLatestDataKey = (latestData: Array, dataKey: DataKey): [number, any] => { + if (latestData?.length) { + const dsData = latestData.find(data => data.dataKey === dataKey); + if (dsData?.data?.length) { + return dsData.data[0]; + } + } + return null; +}; + +export const aggregatedValueCardDefaultSettings: AggregatedValueCardWidgetSettings = { + autoScale: true, + showSubtitle: true, + subtitle: '${entityName}', + subtitleFont: { + family: 'Roboto', + size: 12, + sizeUnit: 'px', + style: 'normal', + weight: '400', + lineHeight: '16px' + }, + subtitleColor: 'rgba(0, 0, 0, 0.38)', + showDate: true, + dateFormat: lastUpdateAgoDateFormat(), + dateFont: { + family: 'Roboto', + size: 12, + sizeUnit: 'px', + style: 'normal', + weight: '400', + lineHeight: '16px' + }, + dateColor: 'rgba(0, 0, 0, 0.38)', + showChart: true, + background: { + type: BackgroundType.color, + color: '#fff', + overlay: { + enabled: false, + color: 'rgba(255,255,255,0.72)', + blur: 3 + } + } +}; + +export const aggregatedValueCardDefaultKeySettings: AggregatedValueCardKeySettings = { + position: AggregatedValueCardKeyPosition.center, + font: { + family: 'Roboto', + size: 14, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '1' + }, + color: constantColor('rgba(0, 0, 0, 0.87)'), + showArrow: false +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/progress-bar-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/cards/progress-bar-widget.component.html new file mode 100644 index 0000000000..71b41ffc88 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/progress-bar-widget.component.html @@ -0,0 +1,42 @@ + +
+
+
+ + + +
+
+ + + +
{{ valueText }}
+
+
+
+
+
+
+
{{ settings.tickMin }}
+
{{ settings.tickMax }}
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/progress-bar-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/cards/progress-bar-widget.component.scss new file mode 100644 index 0000000000..7f83997987 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/progress-bar-widget.component.scss @@ -0,0 +1,84 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.tb-progress-bar-panel { + width: 100%; + height: 100%; + position: relative; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 20px 24px 24px 24px; + > div:not(.tb-progress-bar-overlay) { + z-index: 1; + } + .tb-progress-bar-overlay { + position: absolute; + top: 12px; + left: 12px; + bottom: 12px; + right: 12px; + } + div.tb-widget-title { + padding: 0; + } + .tb-progress-bar-container { + width: 100%; + height: 100%; + position: relative; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: flex-start; + &.simplified { + justify-content: center; + } + } + .tb-progress-bar-content { + display: flex; + flex-direction: column; + align-self: stretch; + gap: 4px; + .tb-progress-bar-title-value-row { + display: flex; + flex-direction: row; + justify-content: space-between; + &.flex-end { + justify-content: flex-end; + } + .tb-progress-bar-value { + white-space: nowrap; + } + } + .tb-progress-bar { + display: flex; + flex-direction: column; + .tb-progress-bar-background { + border-radius: 12px; + .tb-progress-bar-progress { + height: 5px; + border-radius: 8px; + transition: background-color .2s, width .2s; + } + } + .tb-progress-bar-ticks { + display: flex; + flex-direction: row; + justify-content: space-between; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/progress-bar-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/progress-bar-widget.component.ts new file mode 100644 index 0000000000..066f0eff39 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/progress-bar-widget.component.ts @@ -0,0 +1,228 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + Input, + OnDestroy, + OnInit, + Renderer2, + TemplateRef, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { formatValue, isDefinedAndNotNull, isNumeric } from '@core/utils'; +import { DatePipe } from '@angular/common'; +import { + backgroundStyle, + ColorProcessor, + ComponentStyle, + getDataKey, + getSingleTsValue, + overlayStyle, + resolveCssSize, + textStyle +} from '@shared/models/widget-settings.models'; +import { WidgetComponent } from '@home/components/widget/widget.component'; +import { progressBarDefaultSettings, ProgressBarLayout, ProgressBarWidgetSettings } from './progress-bar-widget.models'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { Observable } from 'rxjs'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { DomSanitizer } from '@angular/platform-browser'; + +const defaultLayoutHeight = 80; +const simplifiedLayoutHeight = 75; +const defaultAspect = defaultLayoutHeight / 150; +const simplifiedAspect = simplifiedLayoutHeight / 150; + +@Component({ + selector: 'tb-progress-bar-widget', + templateUrl: './progress-bar-widget.component.html', + styleUrls: ['./progress-bar-widget.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class ProgressBarWidgetComponent implements OnInit, OnDestroy, AfterViewInit { + + @ViewChild('progressBarPanel', {static: true}) + progressBarPanel: ElementRef; + + @ViewChild('progressBarContainer', {static: true}) + progressBarContainer: ElementRef; + + progressBarLayout = ProgressBarLayout; + + settings: ProgressBarWidgetSettings; + + @Input() + ctx: WidgetContext; + + @Input() + widgetTitlePanel: TemplateRef; + + layout: ProgressBarLayout; + + layoutClass = ''; + + showTitleValueRow = true; + titleValueRowClass = ''; + + showValue = true; + valueText = 'N/A'; + valueStyle: ComponentStyle = {}; + valueColor: ColorProcessor; + + barWidth = '0%'; + barColor: ColorProcessor; + + showTicks = true; + ticksStyle: ComponentStyle = {}; + + value: number; + + backgroundStyle$: Observable; + overlayStyle: ComponentStyle = {}; + + progressBarPanelResize$: ResizeObserver; + + private decimals = 0; + private units = ''; + + constructor(private date: DatePipe, + private imagePipe: ImagePipe, + private sanitizer: DomSanitizer, + private widgetComponent: WidgetComponent, + private renderer: Renderer2, + private cd: ChangeDetectorRef) { + } + + ngOnInit(): void { + this.ctx.$scope.progressBarWidget = this; + this.settings = {...progressBarDefaultSettings, ...this.ctx.settings}; + + this.decimals = this.ctx.decimals; + this.units = this.ctx.units; + const dataKey = getDataKey(this.ctx.datasources); + if (isDefinedAndNotNull(dataKey?.decimals)) { + this.decimals = dataKey.decimals; + } + if (dataKey?.units) { + this.units = dataKey.units; + } + + this.layout = this.settings.layout; + + this.showValue = this.settings.showValue; + this.valueStyle = textStyle(this.settings.valueFont); + this.valueColor = ColorProcessor.fromSettings(this.settings.valueColor); + + this.showTitleValueRow = this.showValue || + (this.layout === ProgressBarLayout.simplified && this.widgetComponent.dashboardWidget.showWidgetTitlePanel); + + this.layoutClass = (this.layout === ProgressBarLayout.simplified || !this.widgetComponent.dashboardWidget.showWidgetTitlePanel) + ? 'simplified' : ''; + + this.titleValueRowClass = (this.layout === ProgressBarLayout.simplified && + !this.widgetComponent.dashboardWidget.showWidgetTitlePanel) ? 'flex-end' : ''; + + this.barColor = ColorProcessor.fromSettings(this.settings.barColor); + + this.showTicks = this.settings.showTicks && this.layout === ProgressBarLayout.default; + if (this.showTicks) { + this.ticksStyle = textStyle(this.settings.ticksFont); + this.ticksStyle.color = this.settings.ticksColor; + } + + this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); + this.overlayStyle = overlayStyle(this.settings.background.overlay); + } + + ngAfterViewInit() { + if (this.settings.autoScale) { + this.renderer.setStyle(this.progressBarContainer.nativeElement, 'overflow', 'visible'); + this.renderer.setStyle(this.progressBarContainer.nativeElement, 'position', 'absolute'); + this.progressBarPanelResize$ = new ResizeObserver(() => { + this.onResize(); + }); + this.progressBarPanelResize$.observe(this.progressBarPanel.nativeElement); + this.onResize(); + } + } + + ngOnDestroy() { + } + + public onInit() { + const borderRadius = this.ctx.$widgetElement.css('borderRadius'); + this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; + this.cd.detectChanges(); + } + + public onDataUpdated() { + const tsValue = getSingleTsValue(this.ctx.data); + this.value = 0; + if (tsValue && isDefinedAndNotNull(tsValue[1]) && isNumeric(tsValue[1])) { + this.value = tsValue[1]; + this.valueText = formatValue(this.value, this.decimals, this.units, false); + } else { + this.valueText = 'N/A'; + } + this.valueColor.update(this.value); + this.barColor.update(this.value); + const range = this.settings.tickMax - this.settings.tickMin; + let barWidthValue = ((this.value - this.settings.tickMin) / range) * 100; + barWidthValue = Math.max(0, Math.min(100, barWidthValue)); + this.barWidth = `${barWidthValue}%`; + this.cd.detectChanges(); + } + + private onResize() { + const paddingLeft = getComputedStyle(this.progressBarPanel.nativeElement).paddingLeft; + const paddingRight = getComputedStyle(this.progressBarPanel.nativeElement).paddingRight; + const paddingTop = getComputedStyle(this.progressBarPanel.nativeElement).paddingTop; + const paddingBottom = getComputedStyle(this.progressBarPanel.nativeElement).paddingBottom; + const pLeft = resolveCssSize(paddingLeft)[0]; + const pRight = resolveCssSize(paddingRight)[0]; + const pTop = resolveCssSize(paddingTop)[0]; + const pBottom = resolveCssSize(paddingBottom)[0]; + const panelWidth = this.progressBarPanel.nativeElement.getBoundingClientRect().width - (pLeft + pRight); + const panelHeight = this.progressBarPanel.nativeElement.getBoundingClientRect().height - (pTop + pBottom); + const defaultLayout = this.layout === ProgressBarLayout.default && this.widgetComponent.dashboardWidget.showWidgetTitlePanel; + let minAspect = defaultLayout ? defaultAspect : simplifiedAspect; + let layoutHeight = defaultLayout ? defaultLayoutHeight : simplifiedLayoutHeight; + if (!this.showTitleValueRow) { + minAspect -= (40 / 150); + layoutHeight -= 40; + } + if (!this.showTicks) { + minAspect -= (10 / 150); + layoutHeight -= 10; + } + const aspect = Math.min(panelHeight / panelWidth, minAspect); + const targetWidth = panelWidth; + const targetHeight = targetWidth * aspect; + const scale = targetHeight / layoutHeight; + const width = targetWidth / scale; + const height = panelHeight / scale; + this.renderer.setStyle(this.progressBarContainer.nativeElement, 'width', width + 'px'); + this.renderer.setStyle(this.progressBarContainer.nativeElement, 'height', height + 'px'); + this.renderer.setStyle(this.progressBarContainer.nativeElement, 'transform', `scale(${scale})`); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/progress-bar-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/progress-bar-widget.models.ts new file mode 100644 index 0000000000..d0aff79ba5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/progress-bar-widget.models.ts @@ -0,0 +1,98 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + BackgroundSettings, + BackgroundType, + ColorSettings, + constantColor, + Font +} from '@shared/models/widget-settings.models'; + +export enum ProgressBarLayout { + default = 'default', + simplified = 'simplified' +} + +export const progressBarLayouts = Object.keys(ProgressBarLayout) as ProgressBarLayout[]; + +export const progressBarLayoutTranslations = new Map( + [ + [ProgressBarLayout.default, 'widgets.progress-bar.layout-default'], + [ProgressBarLayout.simplified, 'widgets.progress-bar.layout-simplified'] + ] +); + +export const progressBarLayoutImages = new Map( + [ + [ProgressBarLayout.default, 'assets/widget/progress-bar/default-layout.svg'], + [ProgressBarLayout.simplified, 'assets/widget/progress-bar/simplified-layout.svg'] + ] +); + +export interface ProgressBarWidgetSettings { + layout: ProgressBarLayout; + autoScale: boolean; + showValue: boolean; + valueFont: Font; + valueColor: ColorSettings; + showTicks: boolean; + tickMin: number; + tickMax: number; + ticksFont: Font; + ticksColor: string; + barColor: ColorSettings; + barBackground: string; + background: BackgroundSettings; +} + +export const progressBarDefaultSettings: ProgressBarWidgetSettings = { + layout: ProgressBarLayout.default, + autoScale: true, + showValue: true, + valueFont: { + family: 'Roboto', + size: 24, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '32px' + }, + valueColor: constantColor('rgba(0, 0, 0, 0.87)'), + showTicks: true, + tickMin: 0, + tickMax: 100, + ticksFont: { + family: 'Roboto', + size: 11, + sizeUnit: 'px', + style: 'normal', + weight: '400', + lineHeight: '16px' + }, + ticksColor: 'rgba(0,0,0,0.54)', + barColor: constantColor('rgba(63, 82, 221, 1)'), + barBackground: 'rgba(0, 0, 0, 0.04)', + background: { + type: BackgroundType.color, + color: '#fff', + overlay: { + enabled: false, + color: 'rgba(255,255,255,0.72)', + blur: 3 + } + } +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.html new file mode 100644 index 0000000000..5ee040558a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.html @@ -0,0 +1,75 @@ + +
+
+
+ +
+
+ + + + + + + + + + + + +
+ + +
+ +
+ + + + + + +
+ +
+
+
+
+ +
+ +
+ + +
+
+
+ + {{ icon }} + + +
{{ label$ | async }}
+
+ +
+
+ +
{{ valueText }}
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.scss new file mode 100644 index 0000000000..1ea9825751 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.scss @@ -0,0 +1,95 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + .tb-value-card-panel { + width: 100%; + height: 100%; + position: relative; + display: flex; + align-items: center; + justify-content: center; + padding: 24px; + &.horizontal, &.horizontal_reversed { + padding: 0 24px; + } + > div:not(.tb-value-card-overlay), > tb-icon { + z-index: 1; + } + .tb-value-card-overlay { + position: absolute; + top: 12px; + left: 12px; + bottom: 12px; + right: 12px; + } + > div.tb-value-card-title-panel { + position: absolute; + top: 12px; + left: 12px; + right: 12px; + z-index: 2; + } + .tb-value-card-content { + width: 100%; + height: 100%; + position: relative; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 16px; + &.square { + justify-content: space-evenly; + gap: 0; + } + + &.horizontal { + flex-direction: row; + } + + &.horizontal_reversed { + flex-direction: row-reverse; + } + + .tb-value-card-icon-row { + display: flex; + flex-direction: row; + align-items: center; + gap: 8px; + } + + &.horizontal_reversed { + .tb-value-card-icon-row { + flex-direction: row-reverse; + } + + .tb-value-card-label-row { + align-items: flex-end; + } + } + + .tb-value-card-label-row { + display: flex; + flex-direction: column; + justify-content: center; + } + + .tb-value-card-value { + white-space: nowrap; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts new file mode 100644 index 0000000000..27899f6e83 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts @@ -0,0 +1,218 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + Input, + OnDestroy, + OnInit, + Renderer2, + TemplateRef, + ViewChild +} from '@angular/core'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { formatValue, isDefinedAndNotNull } from '@core/utils'; +import { + backgroundStyle, + ColorProcessor, + ComponentStyle, + DateFormatProcessor, + getDataKey, + getLabel, + getSingleTsValue, + iconStyle, + overlayStyle, + textStyle +} from '@shared/models/widget-settings.models'; +import { valueCardDefaultSettings, ValueCardLayout, ValueCardWidgetSettings } from './value-card-widget.models'; +import { WidgetComponent } from '@home/components/widget/widget.component'; +import { Observable } from 'rxjs'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { DomSanitizer } from '@angular/platform-browser'; + +const squareLayoutSize = 160; +const squareLayoutPadding = 48; +const horizontalLayoutHeight = 80; + +@Component({ + selector: 'tb-value-card-widget', + templateUrl: './value-card-widget.component.html', + styleUrls: ['./value-card-widget.component.scss'] +}) +export class ValueCardWidgetComponent implements OnInit, AfterViewInit, OnDestroy { + + @ViewChild('valueCardPanel', {static: false}) + valueCardPanel: ElementRef; + + @ViewChild('valueCardContent', {static: false}) + valueCardContent: ElementRef; + + settings: ValueCardWidgetSettings; + + valueCardLayout = ValueCardLayout; + + @Input() + ctx: WidgetContext; + + @Input() + widgetTitlePanel: TemplateRef; + + layout: ValueCardLayout; + showIcon = true; + icon = ''; + iconStyle: ComponentStyle = {}; + iconColor: ColorProcessor; + + showLabel = true; + label$: Observable; + labelStyle: ComponentStyle = {}; + labelColor: ColorProcessor; + + valueText = 'N/A'; + valueStyle: ComponentStyle = {}; + valueColor: ColorProcessor; + + showDate = true; + dateFormat: DateFormatProcessor; + dateStyle: ComponentStyle = {}; + dateColor: ColorProcessor; + + backgroundStyle$: Observable; + overlayStyle: ComponentStyle = {}; + + private panelResize$: ResizeObserver; + + private horizontal = false; + private decimals = 0; + private units = ''; + + constructor(private imagePipe: ImagePipe, + private sanitizer: DomSanitizer, + private renderer: Renderer2, + private widgetComponent: WidgetComponent, + private cd: ChangeDetectorRef) { + } + + ngOnInit(): void { + const params = this.widgetComponent.typeParameters as any; + this.horizontal = isDefinedAndNotNull(params.horizontal) ? params.horizontal : false; + this.ctx.$scope.valueCardWidget = this; + this.settings = {...valueCardDefaultSettings(this.horizontal), ...this.ctx.settings}; + + this.decimals = this.ctx.decimals; + this.units = this.ctx.units; + const dataKey = getDataKey(this.ctx.datasources); + if (isDefinedAndNotNull(dataKey?.decimals)) { + this.decimals = dataKey.decimals; + } + if (dataKey?.units) { + this.units = dataKey.units; + } + + this.layout = this.settings.layout; + + this.showIcon = this.settings.showIcon; + this.icon = this.settings.icon; + this.iconStyle = iconStyle(this.settings.iconSize, this.settings.iconSizeUnit ); + this.iconColor = ColorProcessor.fromSettings(this.settings.iconColor); + + this.showLabel = this.settings.showLabel; + const label = getLabel(this.ctx.datasources); + this.label$ = this.ctx.registerLabelPattern(label, this.label$); + this.labelStyle = textStyle(this.settings.labelFont); + this.labelColor = ColorProcessor.fromSettings(this.settings.labelColor); + this.valueStyle = textStyle(this.settings.valueFont); + this.valueColor = ColorProcessor.fromSettings(this.settings.valueColor); + + this.showDate = this.settings.showDate; + this.dateFormat = DateFormatProcessor.fromSettings(this.ctx.$injector, this.settings.dateFormat); + this.dateStyle = textStyle(this.settings.dateFont); + this.dateColor = ColorProcessor.fromSettings(this.settings.dateColor); + + this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); + this.overlayStyle = overlayStyle(this.settings.background.overlay); + } + + public ngAfterViewInit() { + if (this.settings.autoScale) { + if (!this.horizontal) { + this.renderer.setStyle(this.valueCardContent.nativeElement, 'width', squareLayoutSize + 'px'); + } + const height = this.horizontal ? horizontalLayoutHeight : squareLayoutSize; + this.renderer.setStyle(this.valueCardContent.nativeElement, 'height', height + 'px'); + this.renderer.setStyle(this.valueCardContent.nativeElement, 'overflow', 'visible'); + this.renderer.setStyle(this.valueCardContent.nativeElement, 'position', 'absolute'); + this.panelResize$ = new ResizeObserver(() => { + this.onResize(); + }); + this.panelResize$.observe(this.valueCardPanel.nativeElement); + this.onResize(); + } + } + + ngOnDestroy() { + if (this.panelResize$) { + this.panelResize$.disconnect(); + } + } + + public onInit() { + const borderRadius = this.ctx.$widgetElement.css('borderRadius'); + this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; + this.cd.detectChanges(); + } + + public onDataUpdated() { + const tsValue = getSingleTsValue(this.ctx.data); + let ts; + let value; + if (tsValue && isDefinedAndNotNull(tsValue[1]) && tsValue[0] !== 0) { + ts = tsValue[0]; + value = tsValue[1]; + this.valueText = formatValue(value, this.decimals, this.units, false); + } else { + this.valueText = 'N/A'; + } + this.dateFormat.update(ts); + this.iconColor.update(value); + this.labelColor.update(value); + this.valueColor.update(value); + this.dateColor.update(value); + this.cd.detectChanges(); + } + + private onResize() { + const panelWidth = this.valueCardPanel.nativeElement.getBoundingClientRect().width - squareLayoutPadding; + const panelHeight = this.valueCardPanel.nativeElement.getBoundingClientRect().height - (this.horizontal ? 0 : squareLayoutPadding); + let scale: number; + if (!this.horizontal) { + const size = Math.min(panelWidth, panelHeight); + scale = size / squareLayoutSize; + } else { + const targetWidth = panelWidth; + const aspect = Math.min(panelHeight / targetWidth, 0.25); + const targetHeight = targetWidth * aspect; + scale = targetHeight / horizontalLayoutHeight; + const width = targetWidth / scale; + this.renderer.setStyle(this.valueCardContent.nativeElement, 'width', width + 'px'); + } + this.renderer.setStyle(this.valueCardContent.nativeElement, 'transform', `scale(${scale})`); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.models.ts new file mode 100644 index 0000000000..481c50d254 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.models.ts @@ -0,0 +1,132 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + BackgroundSettings, + BackgroundType, + ColorSettings, + constantColor, + cssUnit, DateFormatSettings, + Font, lastUpdateAgoDateFormat +} from '@shared/models/widget-settings.models'; + +export enum ValueCardLayout { + square = 'square', + vertical = 'vertical', + centered = 'centered', + simplified = 'simplified', + horizontal = 'horizontal', + horizontal_reversed = 'horizontal_reversed' +} + +export const valueCardLayouts = (horizontal: boolean): ValueCardLayout[] => { + if (horizontal) { + return [ValueCardLayout.horizontal, ValueCardLayout.horizontal_reversed]; + } else { + return [ValueCardLayout.square, ValueCardLayout.vertical, ValueCardLayout.centered, ValueCardLayout.simplified]; + } +}; + +export const valueCardLayoutTranslations = new Map( + [ + [ValueCardLayout.square, 'widgets.value-card.layout-square'], + [ValueCardLayout.vertical, 'widgets.value-card.layout-vertical'], + [ValueCardLayout.centered, 'widgets.value-card.layout-centered'], + [ValueCardLayout.simplified, 'widgets.value-card.layout-simplified'], + [ValueCardLayout.horizontal, 'widgets.value-card.layout-horizontal'], + [ValueCardLayout.horizontal_reversed, 'widgets.value-card.layout-horizontal-reversed'] + ] +); + +export const valueCardLayoutImages = new Map( + [ + [ValueCardLayout.square, 'assets/widget/value-card/square-layout.svg'], + [ValueCardLayout.vertical, 'assets/widget/value-card/vertical-layout.svg'], + [ValueCardLayout.centered, 'assets/widget/value-card/centered-layout.svg'], + [ValueCardLayout.simplified, 'assets/widget/value-card/simplified-layout.svg'], + [ValueCardLayout.horizontal, 'assets/widget/value-card/horizontal-layout.svg'], + [ValueCardLayout.horizontal_reversed, 'assets/widget/value-card/horizontal-reversed-layout.svg'] + ] +); + +export interface ValueCardWidgetSettings { + layout: ValueCardLayout; + autoScale: boolean; + showLabel: boolean; + labelFont: Font; + labelColor: ColorSettings; + showIcon: boolean; + icon: string; + iconSize: number; + iconSizeUnit: cssUnit; + iconColor: ColorSettings; + valueFont: Font; + valueColor: ColorSettings; + showDate: boolean; + dateFormat: DateFormatSettings; + dateFont: Font; + dateColor: ColorSettings; + background: BackgroundSettings; +} + +export const valueCardDefaultSettings = (horizontal: boolean): ValueCardWidgetSettings => ({ + layout: horizontal ? ValueCardLayout.horizontal : ValueCardLayout.square, + autoScale: true, + showLabel: true, + labelFont: { + family: 'Roboto', + size: 16, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '1.5' + }, + labelColor: constantColor('rgba(0, 0, 0, 0.87)'), + showIcon: true, + icon: 'thermostat', + iconSize: 40, + iconSizeUnit: 'px', + iconColor: constantColor('#5469FF'), + valueFont: { + family: 'Roboto', + size: 52, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '100%' + }, + valueColor: constantColor('rgba(0, 0, 0, 0.87)'), + showDate: true, + dateFormat: lastUpdateAgoDateFormat(), + dateFont: { + family: 'Roboto', + size: 12, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '1.33' + }, + dateColor: constantColor('rgba(0, 0, 0, 0.38)'), + background: { + type: BackgroundType.color, + color: '#fff', + overlay: { + enabled: false, + color: 'rgba(255,255,255,0.72)', + blur: 3 + } + } +}); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.html new file mode 100644 index 0000000000..b8847a5ba0 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.html @@ -0,0 +1,25 @@ + +
+
+ +
+
{{ valueText }}
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.scss new file mode 100644 index 0000000000..3ea61fdcec --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.scss @@ -0,0 +1,58 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.tb-value-chart-card-panel { + width: 100%; + height: 100%; + position: relative; + display: flex; + flex-direction: column; + gap: 8px; + padding: 20px 24px 24px 24px; + > div:not(.tb-value-chart-card-overlay) { + z-index: 1; + } + div.tb-widget-title { + padding: 0; + } + .tb-value-chart-card-overlay { + position: absolute; + top: 12px; + left: 12px; + bottom: 12px; + right: 12px; + } + .tb-value-chart-card-content { + min-height: 0; + flex: 1; + display: flex; + align-items: flex-end; + gap: 16px; + position: relative; + &.left { + flex-direction: row; + } + &.right { + flex-direction: row-reverse; + } + .tb-value-chart-card-value { + white-space: nowrap; + } + .tb-value-chart-card-chart { + flex: 1; + height: 100%; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.ts new file mode 100644 index 0000000000..9a1317f87e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.ts @@ -0,0 +1,258 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + Input, + OnDestroy, + OnInit, + Renderer2, + TemplateRef, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { formatValue, isDefinedAndNotNull, isNumeric } from '@core/utils'; +import { + backgroundStyle, + ColorProcessor, + ComponentStyle, + getDataKey, + overlayStyle, resolveCssSize, + textStyle +} from '@shared/models/widget-settings.models'; +import { WidgetComponent } from '@home/components/widget/widget.component'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { + valueChartCardDefaultSettings, + ValueChartCardLayout, + ValueChartCardWidgetSettings +} from '@home/components/widget/lib/cards/value-chart-card-widget.models'; +import { TbFlot } from '@home/components/widget/lib/flot-widget'; +import { DataKey } from '@shared/models/widget.models'; +import { TbFlotKeySettings, TbFlotSettings } from '@home/components/widget/lib/flot-widget.models'; +import { getTsValueByLatestDataKey } from '@home/components/widget/lib/cards/aggregated-value-card.models'; +import { Observable } from 'rxjs'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { DomSanitizer } from '@angular/platform-browser'; + +const layoutHeight = 56; +const valueRelativeMaxWidth = 0.5; + +@Component({ + selector: 'tb-value-chart-card-widget', + templateUrl: './value-chart-card-widget.component.html', + styleUrls: ['./value-chart-card-widget.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class ValueChartCardWidgetComponent implements OnInit, AfterViewInit, OnDestroy { + + @ViewChild('chartElement', {static: false}) + chartElement: ElementRef; + + @ViewChild('valueChartCardContent', {static: false}) + valueChartCardContent: ElementRef; + + @ViewChild('valueChartCardValue', {static: false}) + valueChartCardValue: ElementRef; + + settings: ValueChartCardWidgetSettings; + + @Input() + ctx: WidgetContext; + + @Input() + widgetTitlePanel: TemplateRef; + + layout: ValueChartCardLayout; + + showValue = true; + valueText = 'N/A'; + valueStyle: ComponentStyle = {}; + valueColor: ColorProcessor; + + backgroundStyle$: Observable; + overlayStyle: ComponentStyle = {}; + + private flot: TbFlot; + private flotDataKey: DataKey; + + private valueKey: DataKey; + private contentResize$: ResizeObserver; + + private decimals = 0; + private units = ''; + + private valueFontSize: number; + + constructor(private imagePipe: ImagePipe, + private sanitizer: DomSanitizer, + private renderer: Renderer2, + private widgetComponent: WidgetComponent, + private cd: ChangeDetectorRef) { + } + + ngOnInit(): void { + this.ctx.$scope.valueChartCardWidget = this; + this.settings = {...valueChartCardDefaultSettings, ...this.ctx.settings}; + + if (this.showValue) { + this.decimals = this.ctx.decimals; + this.units = this.ctx.units; + const dataKey = getDataKey(this.ctx.datasources); + if (dataKey?.name && this.ctx.defaultSubscription.firstDatasource?.latestDataKeys?.length) { + const dataKeys = this.ctx.defaultSubscription.firstDatasource?.latestDataKeys; + this.valueKey = dataKeys?.find(k => k.name === dataKey.name); + if (isDefinedAndNotNull(this.valueKey?.decimals)) { + this.decimals = this.valueKey.decimals; + } + if (this.valueKey?.units) { + this.units = dataKey.units; + } + } + } + + this.layout = this.settings.layout; + + this.showValue = this.settings.showValue; + this.valueStyle = textStyle(this.settings.valueFont); + this.valueColor = ColorProcessor.fromSettings(this.settings.valueColor); + + this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); + this.overlayStyle = overlayStyle(this.settings.background.overlay); + + if (this.ctx.defaultSubscription.firstDatasource?.dataKeys?.length) { + this.flotDataKey = this.ctx.defaultSubscription.firstDatasource?.dataKeys[0]; + this.flotDataKey.settings = { + fillLines: false, + showLines: true, + lineWidth: 2 + } as TbFlotKeySettings; + } + } + + public ngAfterViewInit() { + const settings = { + shadowSize: 0, + enableSelection: false, + smoothLines: true, + grid: { + tickColor: 'rgba(0,0,0,0.12)', + horizontalLines: false, + verticalLines: false, + outlineWidth: 0, + minBorderMargin: 0, + margin: 0 + }, + yaxis: { + showLabels: false, + tickGenerator: 'return [(axis.max + axis.min) / 2];' + }, + xaxis: { + showLabels: false + } + } as TbFlotSettings; + this.flot = new TbFlot(this.ctx, 'line', $(this.chartElement.nativeElement), settings); + + this.contentResize$ = new ResizeObserver(() => { + this.onResize(); + }); + this.contentResize$.observe(this.valueChartCardContent.nativeElement); + this.onResize(); + } + + ngOnDestroy() { + if (this.contentResize$) { + this.contentResize$.disconnect(); + } + } + + public onInit() { + const borderRadius = this.ctx.$widgetElement.css('borderRadius'); + this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; + this.cd.detectChanges(); + } + + public onDataUpdated() { + this.flot.update(); + } + + public onLatestDataUpdated() { + if (this.showValue && this.valueKey) { + const tsValue = getTsValueByLatestDataKey(this.ctx.latestData, this.valueKey); + let value; + if (tsValue && isDefinedAndNotNull(tsValue[1]) && isNumeric(tsValue[1])) { + value = tsValue[1]; + this.valueText = formatValue(value, this.decimals, this.units, false); + } else { + this.valueText = 'N/A'; + } + this.valueColor.update(value); + this.cd.detectChanges(); + setTimeout(() => { + this.onResize(false); + }, 0); + } + } + + public onEditModeChanged() { + this.flot.checkMouseEvents(); + } + + public onDestroy() { + this.flot.destroy(); + } + + private onResize(fitTargetHeight = true) { + if (this.settings.autoScale && this.showValue) { + const contentWidth = this.valueChartCardContent.nativeElement.getBoundingClientRect().width; + const contentHeight = this.valueChartCardContent.nativeElement.getBoundingClientRect().height; + if (!this.valueFontSize) { + const fontSize = getComputedStyle(this.valueChartCardValue.nativeElement).fontSize; + this.valueFontSize = resolveCssSize(fontSize)[0]; + } + const valueRelativeHeight = Math.min(this.valueFontSize / layoutHeight, 1); + const targetValueHeight = contentHeight * valueRelativeHeight; + const maxValueWidth = contentWidth * valueRelativeMaxWidth; + this.setValueFontSize(targetValueHeight, maxValueWidth, fitTargetHeight); + } + this.flot.resize(); + } + + private setValueFontSize(targetHeight: number, maxWidth: number, fitTargetHeight = true) { + const fontSize = getComputedStyle(this.valueChartCardValue.nativeElement).fontSize; + let valueFontSize = resolveCssSize(fontSize)[0]; + this.renderer.setStyle(this.valueChartCardValue.nativeElement, 'fontSize', valueFontSize + 'px'); + this.renderer.setStyle(this.valueChartCardValue.nativeElement, 'lineHeight', '1'); + let valueHeight = this.valueChartCardValue.nativeElement.getBoundingClientRect().height; + while (fitTargetHeight && valueHeight < targetHeight) { + valueFontSize++; + this.renderer.setStyle(this.valueChartCardValue.nativeElement, 'fontSize', valueFontSize + 'px'); + valueHeight = this.valueChartCardValue.nativeElement.getBoundingClientRect().height; + } + let valueWidth = this.valueChartCardValue.nativeElement.getBoundingClientRect().width; + while ((valueHeight > targetHeight || valueWidth > maxWidth) && valueFontSize > 6) { + valueFontSize--; + this.renderer.setStyle(this.valueChartCardValue.nativeElement, 'fontSize', valueFontSize + 'px'); + valueWidth = this.valueChartCardValue.nativeElement.getBoundingClientRect().width; + valueHeight = this.valueChartCardValue.nativeElement.getBoundingClientRect().height; + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.models.ts new file mode 100644 index 0000000000..e7a4a6d356 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.models.ts @@ -0,0 +1,77 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + BackgroundSettings, + BackgroundType, + ColorSettings, + constantColor, + Font +} from '@shared/models/widget-settings.models'; + +export enum ValueChartCardLayout { + left = 'left', + right = 'right' +} + +export const valueCartCardLayouts = Object.keys(ValueChartCardLayout) as ValueChartCardLayout[]; + +export const valueChartCardLayoutTranslations = new Map( + [ + [ValueChartCardLayout.left, 'widgets.value-chart-card.layout-left'], + [ValueChartCardLayout.right, 'widgets.value-chart-card.layout-right'] + ] +); + +export const valueChartCardLayoutImages = new Map( + [ + [ValueChartCardLayout.left, 'assets/widget/value-chart-card/left-layout.svg'], + [ValueChartCardLayout.right, 'assets/widget/value-chart-card/right-layout.svg'] + ] +); + +export interface ValueChartCardWidgetSettings { + layout: ValueChartCardLayout; + autoScale: boolean; + showValue: boolean; + valueFont: Font; + valueColor: ColorSettings; + background: BackgroundSettings; +} + +export const valueChartCardDefaultSettings: ValueChartCardWidgetSettings = { + layout: ValueChartCardLayout.left, + autoScale: true, + showValue: true, + valueFont: { + family: 'Roboto', + size: 28, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '32px' + }, + valueColor: constantColor('rgba(0, 0, 0, 0.87)'), + background: { + type: BackgroundType.color, + color: '#fff', + overlay: { + enabled: false, + color: 'rgba(255,255,255,0.72)', + blur: 3 + } + } +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.html new file mode 100644 index 0000000000..c39d277355 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.html @@ -0,0 +1,37 @@ + +
+
+ +
+
+
+
+
+
+
+
{{ legendItem.label }}
+
+
{{ legendItem.value }}
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.scss new file mode 100644 index 0000000000..60c5f81191 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.scss @@ -0,0 +1,110 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.tb-doughnut-panel { + width: 100%; + height: 100%; + position: relative; + display: flex; + flex-direction: column; + gap: 16px; + padding: 20px 24px 24px 24px; + > div:not(.tb-doughnut-overlay) { + z-index: 1; + } + .tb-doughnut-overlay { + position: absolute; + top: 12px; + left: 12px; + bottom: 12px; + right: 12px; + } + div.tb-widget-title { + padding: 0; + } + .tb-doughnut-content { + flex: 1; + min-width: 0; + min-height: 0; + display: flex; + flex-direction: column; + gap: 16px; + &.legend-top { + flex-direction: column-reverse; + } + &.legend-right { + flex-direction: row; + } + &.legend-left { + flex-direction: row-reverse; + } + .tb-doughnut-shape { + flex: 1; + min-width: 0; + min-height: 0; + display: flex; + align-items: center; + justify-content: center; + } + .tb-doughnut-legend { + display: flex; + justify-content: space-around; + align-items: center; + align-self: stretch; + flex-wrap: wrap; + gap: 8px; + .tb-doughnut-legend-item { + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + user-select: none; + &.pointer { + cursor: pointer; + } + .tb-doughnut-legend-item-label { + display: flex; + align-items: center; + gap: 4px; + color: #ccc; + .tb-doughnut-legend-item-label-circle { + width: 8px; + height: 8px; + border-radius: 50%; + background-color: #ccc; + } + } + .tb-doughnut-legend-item-value { + padding-left: 12px; + color: #ccc; + } + } + } + &.legend-right, &.legend-left { + gap: 24px; + .tb-doughnut-legend { + flex-direction: column; + justify-content: center; + align-items: stretch; + .tb-doughnut-legend-item { + flex-direction: row; + justify-content: space-between; + align-items: center; + } + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.ts new file mode 100644 index 0000000000..928445d316 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.ts @@ -0,0 +1,549 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + Input, + OnDestroy, + OnInit, + Renderer2, + TemplateRef, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { + doughnutDefaultSettings, + DoughnutLayout, + DoughnutLegendPosition, + DoughnutTooltipValueType, + DoughnutWidgetSettings +} from '@home/components/widget/lib/chart/doughnut-widget.models'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { + backgroundStyle, + ColorProcessor, + ComponentStyle, + overlayStyle, + textStyle +} from '@shared/models/widget-settings.models'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { WidgetComponent } from '@home/components/widget/widget.component'; +import * as echarts from 'echarts/core'; +import { TranslateService } from '@ngx-translate/core'; +import { PieDataItemOption } from 'echarts/types/src/chart/pie/PieSeries'; +import { formatValue, isDefinedAndNotNull, isNumeric } from '@core/utils'; +import { SVG, Svg, Text } from '@svgdotjs/svg.js'; +import { DataKey } from '@shared/models/widget.models'; +import { TooltipComponent, TooltipComponentOption } from 'echarts/components'; +import { PieChart, PieSeriesOption } from 'echarts/charts'; +import { SVGRenderer } from 'echarts/renderers'; +import { Observable } from 'rxjs'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { DomSanitizer } from '@angular/platform-browser'; + +echarts.use([ + TooltipComponent, + PieChart, + SVGRenderer +]); + +type EChartsOption = echarts.ComposeOption< + | TooltipComponentOption + | PieSeriesOption +>; +type ECharts = echarts.ECharts; + +const shapeSize = 134; +const shapeSegmentWidth = 13.4; + +interface DoughnutDataItem { + id: number; + dataKey: DataKey; + value: number; + hasValue: boolean; + enabled: boolean; +} + +interface DoughnutLegendItem { + id: number; + color: string; + label: string; + value: string; + hasValue: boolean; + enabled: boolean; + total?: boolean; +} + +@Component({ + selector: 'tb-doughnut-widget', + templateUrl: './doughnut-widget.component.html', + styleUrls: ['./doughnut-widget.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class DoughnutWidgetComponent implements OnInit, OnDestroy, AfterViewInit { + + @ViewChild('doughnutContent', {static: false}) + doughnutContent: ElementRef; + + @ViewChild('doughnutShape', {static: false}) + doughnutShape: ElementRef; + + @ViewChild('doughnutLegend', {static: false}) + doughnutLegend: ElementRef; + + settings: DoughnutWidgetSettings; + + @Input() + ctx: WidgetContext; + + @Input() + widgetTitlePanel: TemplateRef; + + showLegend: boolean; + legendClass: string; + + totalValueColor: ColorProcessor; + + backgroundStyle$: Observable; + overlayStyle: ComponentStyle = {}; + + legendItems: DoughnutLegendItem[]; + legendLabelStyle: ComponentStyle; + legendValueStyle: ComponentStyle; + disabledLegendLabelStyle: ComponentStyle; + disabledLegendValueStyle: ComponentStyle; + + private shapeResize$: ResizeObserver; + private legendHorizontal: boolean; + + private decimals = 0; + private units = ''; + + private total = 0; + private totalText = 'N/A'; + private scale = 1; + + private dataItems: DoughnutDataItem[] = []; + + private drawDoughnutPending = false; + private showTotal = false; + private doughnutChart: ECharts; + private doughnutOptions: EChartsOption; + private svgShape: Svg; + private totalTextNode: Text; + + constructor(private imagePipe: ImagePipe, + private sanitizer: DomSanitizer, + private widgetComponent: WidgetComponent, + private renderer: Renderer2, + private translate: TranslateService, + private cd: ChangeDetectorRef) { + } + + ngOnInit(): void { + const params = this.widgetComponent.typeParameters as any; + const horizontal = isDefinedAndNotNull(params.horizontal) ? params.horizontal : false; + this.ctx.$scope.doughnutWidget = this; + this.settings = {...doughnutDefaultSettings(horizontal), ...this.ctx.settings}; + + this.decimals = this.ctx.decimals; + this.units = this.ctx.units; + + this.showLegend = this.settings.showLegend; + this.showTotal = this.settings.layout === DoughnutLayout.with_total; + + if (this.showTotal) { + this.totalValueColor = ColorProcessor.fromSettings(this.settings.totalValueColor); + } + + this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); + this.overlayStyle = overlayStyle(this.settings.background.overlay); + + if (this.showLegend) { + this.legendItems = []; + this.legendClass = `legend-${this.settings.legendPosition}`; + this.legendHorizontal = [DoughnutLegendPosition.left, DoughnutLegendPosition.right].includes(this.settings.legendPosition); + this.legendLabelStyle = textStyle(this.settings.legendLabelFont); + this.disabledLegendLabelStyle = textStyle(this.settings.legendLabelFont); + this.legendLabelStyle.color = this.settings.legendLabelColor; + this.legendValueStyle = textStyle(this.settings.legendValueFont); + this.disabledLegendValueStyle = textStyle(this.settings.legendValueFont); + this.legendValueStyle.color = this.settings.legendValueColor; + } + let counter = 0; + if (this.ctx.datasources.length) { + for (const datasource of this.ctx.datasources) { + const dataKeys = datasource.dataKeys; + for (const dataKey of dataKeys) { + const id = counter++; + this.dataItems.push({ + id, + dataKey, + value: 0, + hasValue: false, + enabled: true + }); + if (this.showLegend) { + this.legendItems.push( + { + id, + value: '--', + label: dataKey.label, + color: dataKey.color, + enabled: true, + hasValue: false + } + ); + } + } + } + } + if (this.settings.sortSeries) { + this.dataItems.sort((a, b) => a.dataKey.label.localeCompare(b.dataKey.label)); + if (this.showLegend) { + this.legendItems.sort((a, b) => a.label.localeCompare(b.label)); + } + } + if (this.showLegend && !this.showTotal) { + this.legendItems.push( + { + id: null, + value: '--', + label: this.translate.instant('widgets.doughnut.total'), + color: 'rgba(0, 0, 0, 0.06)', + enabled: true, + hasValue: false, + total: true + } + ); + } + } + + ngAfterViewInit() { + if (this.drawDoughnutPending) { + this.drawDoughnut(); + } + } + + ngOnDestroy() { + if (this.shapeResize$) { + this.shapeResize$.disconnect(); + } + if (this.doughnutChart) { + this.doughnutChart.dispose(); + } + } + + public onInit() { + const borderRadius = this.ctx.$widgetElement.css('borderRadius'); + this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; + if (this.doughnutShape) { + this.drawDoughnut(); + } else { + this.drawDoughnutPending = true; + } + this.cd.detectChanges(); + } + + public onDataUpdated() { + for (const dsData of this.ctx.data) { + let value = 0; + const tsValue = dsData.data[0]; + const dataItem = this.dataItems.find(item => item.dataKey === dsData.dataKey); + if (tsValue && isDefinedAndNotNull(tsValue[1]) && isNumeric(tsValue[1])) { + value = tsValue[1]; + dataItem.hasValue = true; + dataItem.value = Number(value); + } else { + dataItem.hasValue = false; + dataItem.value = 0; + } + } + this.updateSeriesData(); + if (this.showLegend) { + this.cd.detectChanges(); + if (this.legendHorizontal) { + setTimeout(() => { + this.onResize(); + }); + } + } + } + + private updateSeriesData(renderTotal = true) { + this.total = 0; + this.totalText = 'N/A'; + let hasValue = false; + const seriesData: PieDataItemOption[] = []; + const enabledDataItems = this.dataItems.filter(item => item.enabled && item.hasValue); + for (const dataItem of this.dataItems) { + if (dataItem.enabled && dataItem.hasValue) { + hasValue = true; + this.total += dataItem.value; + seriesData.push( + {id: dataItem.id, value: dataItem.value, name: dataItem.dataKey.label, itemStyle: {color: dataItem.dataKey.color}} + ); + if (enabledDataItems.length > 1) { + seriesData.push({ + value: 0, name: '', itemStyle: {color: 'transparent'}, emphasis: {disabled: true} + }); + } + } + if (this.showLegend) { + const legendItem = this.legendItems.find(item => item.id === dataItem.id); + if (dataItem.hasValue) { + legendItem.hasValue = true; + legendItem.value = formatValue(dataItem.value, this.decimals, this.units, false); + } else { + legendItem.hasValue = false; + legendItem.value = '--'; + } + } + } + for (let i= 1; i < seriesData.length; i+=2) { + seriesData[i].value = this.total / 100; + } + if (this.showTotal || this.showLegend) { + if (hasValue) { + this.totalText = formatValue(this.total, this.decimals, this.units, false); + if (this.showLegend && !this.showTotal) { + this.legendItems[this.legendItems.length - 1].hasValue = true; + this.legendItems[this.legendItems.length - 1].value = this.totalText; + } + } else if (this.showLegend && !this.showTotal) { + this.legendItems[this.legendItems.length - 1].hasValue = false; + this.legendItems[this.legendItems.length - 1].value = '--'; + } + } + this.doughnutOptions.series[0].data = seriesData; + this.doughnutChart.setOption(this.doughnutOptions); + if (this.showTotal) { + this.totalValueColor.update(this.total); + if (renderTotal) { + this.renderTotal(); + } + } + } + + public onLegendItemEnter(item: DoughnutLegendItem) { + if (!item.total && item.enabled && item.hasValue) { + const dataIndex = this.doughnutOptions.series[0].data.findIndex(d => d.id === item.id); + if (dataIndex > -1) { + this.doughnutChart.dispatchAction({ + type: 'highlight', + dataIndex + }); + } + } + } + + public onLegendItemLeave(item: DoughnutLegendItem) { + if (!item.total && item.enabled && item.hasValue) { + const dataIndex = this.doughnutOptions.series[0].data.findIndex(d => d.id === item.id); + if (dataIndex > -1) { + this.doughnutChart.dispatchAction({ + type: 'downplay', + dataIndex + }); + } + } + } + + public toggleLegendItem(item: DoughnutLegendItem) { + if (!item.total && item.hasValue) { + const enable = !item.enabled; + const dataItem = this.dataItems.find(d => d.id === item.id); + if (dataItem) { + dataItem.enabled = enable; + this.updateSeriesData(); + item.enabled = enable; + if (enable) { + const dataIndex = this.doughnutOptions.series[0].data.findIndex(d => d.id === item.id); + if (dataIndex > -1) { + this.doughnutChart.dispatchAction({ + type: 'highlight', + dataIndex + }); + } + } + } + } + } + + private drawDoughnut() { + const shapeWidth = this.doughnutShape.nativeElement.getBoundingClientRect().width; + const shapeHeight = this.doughnutShape.nativeElement.getBoundingClientRect().height; + const size = this.settings.autoScale ? shapeSize : Math.min(shapeWidth, shapeHeight); + const innerRadius = size / 2 - shapeSegmentWidth; + const outerRadius = size / 2; + this.doughnutChart = echarts.init(this.doughnutShape.nativeElement, null, { + renderer: 'svg', + width: this.settings.autoScale ? shapeSize : undefined, + height: this.settings.autoScale ? shapeSize : undefined, + }); + this.doughnutOptions = { + tooltip: { + trigger: this.settings.showTooltip ? 'item' : 'none', + confine: false, + appendToBody: true + }, + series: [ + { + type: 'pie', + clockwise: this.settings.clockwise, + radius: [innerRadius, outerRadius], + avoidLabelOverlap: false, + itemStyle: { + borderRadius: '50%', + borderWidth: 0, + borderColor: '#fff' + }, + label: { + show: false + }, + emphasis: { + scale: false, + itemStyle: { + borderColor: '#fff', + borderWidth: 2, + shadowColor: 'rgba(0, 0, 0, 0.24)', + shadowBlur: 8 + }, + label: { + show: false + } + } + } + ] + }; + if (this.settings.showTooltip) { + this.doughnutOptions.series[0].tooltip = { + formatter: (params) => { + if (!params.name) { + return null; + } + let value: string; + if (this.settings.tooltipValueType === DoughnutTooltipValueType.percentage) { + const percents = params.value / this.total * 100; + value = formatValue(percents, this.settings.tooltipValueDecimals, '%', false); + } else { + value = formatValue(params.value, this.settings.tooltipValueDecimals, this.units, false); + } + const textElement: HTMLElement = this.renderer.createElement('div'); + this.renderer.setStyle(textElement, 'display', 'inline-flex'); + this.renderer.setStyle(textElement, 'align-items', 'center'); + this.renderer.setStyle(textElement, 'gap', '8px'); + const labelElement: HTMLElement = this.renderer.createElement('div'); + this.renderer.appendChild(labelElement, this.renderer.createText(params.name)); + this.renderer.setStyle(labelElement, 'font-family', 'Roboto'); + this.renderer.setStyle(labelElement, 'font-size', '11px'); + this.renderer.setStyle(labelElement, 'font-style', 'normal'); + this.renderer.setStyle(labelElement, 'font-weight', '400'); + this.renderer.setStyle(labelElement, 'line-height', '16px'); + this.renderer.setStyle(labelElement, 'letter-spacing', '0.25px'); + this.renderer.setStyle(labelElement, 'color', 'rgba(0, 0, 0, 0.38)'); + const valueElement: HTMLElement = this.renderer.createElement('div'); + this.renderer.appendChild(valueElement, this.renderer.createText(value)); + this.renderer.setStyle(valueElement, 'font-family', this.settings.tooltipValueFont.family); + this.renderer.setStyle(valueElement, 'font-size', this.settings.tooltipValueFont.size + this.settings.tooltipValueFont.sizeUnit); + this.renderer.setStyle(valueElement, 'font-style', this.settings.tooltipValueFont.style); + this.renderer.setStyle(valueElement, 'font-weight', this.settings.tooltipValueFont.weight); + this.renderer.setStyle(valueElement, 'line-height', this.settings.tooltipValueFont.lineHeight); + this.renderer.setStyle(valueElement, 'color', this.settings.tooltipValueColor); + this.renderer.appendChild(textElement, labelElement); + this.renderer.appendChild(textElement, valueElement); + return textElement; + }, + padding: [4, 8], + backgroundColor: this.settings.tooltipBackgroundColor, + extraCssText: `line-height: 1; backdrop-filter: blur(${this.settings.tooltipBackgroundBlur}px);` + }; + this.doughnutOptions.series[0].tooltip.position = (pos) => [pos[0] + 10, pos[1] + 10]; + } + this.updateSeriesData(false); + + this.renderer.setStyle(this.doughnutChart.getDom().firstChild, 'overflow', 'visible'); + if (this.settings.autoScale) { + this.renderer.setStyle(this.doughnutChart.getDom().firstChild, 'position', 'absolute'); + } + this.renderer.setStyle(this.doughnutChart.getDom().firstChild.firstChild, 'overflow', 'visible'); + + this.svgShape = SVG(this.doughnutChart.getDom().firstChild.firstChild).toRoot(); + + if (this.showTotal) { + this.totalTextNode = this.svgShape.text('').font({ + family: 'Roboto', + leading: 1 + }).attr({'text-anchor': 'middle'}); + this.renderTotal(); + } + + this.shapeResize$ = new ResizeObserver(() => { + this.onResize(); + }); + this.shapeResize$.observe(this.doughnutContent.nativeElement); + this.onResize(); + } + + private renderTotal() { + this.totalTextNode.text(add => { + add.tspan(this.translate.instant('widgets.doughnut.total')).font({size: '12px', weight: 400}).fill('rgba(0, 0, 0, 0.38)'); + add.tspan('').newLine().font({size: '4px'}); + add.tspan(this.totalText).newLine().font( + {family: this.settings.totalValueFont.family, + size: this.settings.totalValueFont.size + this.settings.totalValueFont.sizeUnit, + weight: this.settings.totalValueFont.weight, + style: this.settings.totalValueFont.style} + ).fill(this.totalValueColor.color); + }).center(this.svgShape.bbox().width / 2, this.svgShape.bbox().height / 2); + } + + private onResize() { + if (this.legendHorizontal) { + this.renderer.setStyle(this.doughnutShape.nativeElement, 'max-width', null); + this.renderer.setStyle(this.doughnutShape.nativeElement, 'min-width', null); + this.renderer.setStyle(this.doughnutLegend.nativeElement, 'flex', null); + } + const shapeWidth = this.doughnutShape.nativeElement.getBoundingClientRect().width; + const shapeHeight = this.doughnutShape.nativeElement.getBoundingClientRect().height; + const size = Math.min(shapeWidth, shapeHeight); + if (this.legendHorizontal) { + this.renderer.setStyle(this.doughnutShape.nativeElement, 'max-width', `${size}px`); + this.renderer.setStyle(this.doughnutShape.nativeElement, 'min-width', `${size}px`); + this.renderer.setStyle(this.doughnutLegend.nativeElement, 'flex', '1'); + } + if (!this.settings.autoScale) { + const innerRadius = size / 2 - shapeSegmentWidth; + const outerRadius = size / 2; + this.doughnutOptions.series[0].radius = [innerRadius, outerRadius]; + this.doughnutChart.setOption(this.doughnutOptions); + } else { + this.scale = size / shapeSize; + this.renderer.setStyle(this.doughnutChart.getDom().firstChild, 'transform', `scale(${this.scale})`); + } + if (!this.settings.autoScale) { + this.doughnutChart.resize(); + } + if (this.showTotal) { + this.totalTextNode.center((this.settings.autoScale ? shapeSize : shapeWidth) / 2, + (this.settings.autoScale ? shapeSize : shapeHeight) / 2); + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.models.ts new file mode 100644 index 0000000000..1771dbbb8a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.models.ts @@ -0,0 +1,165 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + BackgroundSettings, + BackgroundType, + ColorSettings, + constantColor, + Font +} from '@shared/models/widget-settings.models'; + +export enum DoughnutLayout { + default = 'default', + with_total = 'with_total' +} + +export const doughnutLayouts = Object.keys(DoughnutLayout) as DoughnutLayout[]; + +export const doughnutLayoutTranslations = new Map( + [ + [DoughnutLayout.default, 'widgets.doughnut.layout-default'], + [DoughnutLayout.with_total, 'widgets.doughnut.layout-with-total'] + ] +); + +export const doughnutLayoutImages = new Map( + [ + [DoughnutLayout.default, 'assets/widget/doughnut/default-layout.svg'], + [DoughnutLayout.with_total, 'assets/widget/doughnut/with-total-layout.svg'] + ] +); + +export const horizontalDoughnutLayoutImages = new Map( + [ + [DoughnutLayout.default, 'assets/widget/doughnut/horizontal-default-layout.svg'], + [DoughnutLayout.with_total, 'assets/widget/doughnut/horizontal-with-total-layout.svg'] + ] +); + +export enum DoughnutLegendPosition { + top = 'top', + bottom = 'bottom', + left = 'left', + right = 'right' +} + +export const doughnutLegendPositions = Object.keys(DoughnutLegendPosition) as DoughnutLegendPosition[]; + +export const doughnutLegendPositionTranslations = new Map( + [ + [DoughnutLegendPosition.top, 'widgets.doughnut.legend-position-top'], + [DoughnutLegendPosition.bottom, 'widgets.doughnut.legend-position-bottom'], + [DoughnutLegendPosition.left, 'widgets.doughnut.legend-position-left'], + [DoughnutLegendPosition.right, 'widgets.doughnut.legend-position-right'] + ] +); + +export enum DoughnutTooltipValueType { + absolute = 'absolute', + percentage = 'percentage' +} + +export const doughnutTooltipValueTypes = Object.keys(DoughnutTooltipValueType) as DoughnutTooltipValueType[]; + +export const doughnutTooltipValueTypeTranslations = new Map( + [ + [DoughnutTooltipValueType.absolute, 'widgets.doughnut.tooltip-value-type-absolute'], + [DoughnutTooltipValueType.percentage, 'widgets.doughnut.tooltip-value-type-percentage'] + ] +); + +export interface DoughnutWidgetSettings { + layout: DoughnutLayout; + autoScale: boolean; + clockwise: boolean; + sortSeries: boolean; + totalValueFont: Font; + totalValueColor: ColorSettings; + showLegend: boolean; + legendPosition: DoughnutLegendPosition; + legendLabelFont: Font; + legendLabelColor: string; + legendValueFont: Font; + legendValueColor: string; + showTooltip: boolean; + tooltipValueType: DoughnutTooltipValueType; + tooltipValueDecimals: number; + tooltipValueFont: Font; + tooltipValueColor: string; + tooltipBackgroundColor: string; + tooltipBackgroundBlur: number; + background: BackgroundSettings; +} + +export const doughnutDefaultSettings = (horizontal: boolean): DoughnutWidgetSettings => ({ + layout: DoughnutLayout.default, + autoScale: true, + clockwise: false, + sortSeries: false, + totalValueFont: { + family: 'Roboto', + size: 24, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '1' + }, + totalValueColor: constantColor('rgba(0, 0, 0, 0.87)'), + showLegend: true, + legendPosition: horizontal ? DoughnutLegendPosition.right : DoughnutLegendPosition.bottom, + legendLabelFont: { + family: 'Roboto', + size: 12, + sizeUnit: 'px', + style: 'normal', + weight: '400', + lineHeight: '16px' + }, + legendLabelColor: 'rgba(0, 0, 0, 0.38)', + legendValueFont: { + family: 'Roboto', + size: 14, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '20px' + }, + legendValueColor: 'rgba(0, 0, 0, 0.87)', + showTooltip: true, + tooltipValueType: DoughnutTooltipValueType.percentage, + tooltipValueDecimals: 0, + tooltipValueFont: { + family: 'Roboto', + size: 13, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '16px' + }, + tooltipValueColor: 'rgba(0, 0, 0, 0.76)', + tooltipBackgroundColor: 'rgba(255, 255, 255, 0.76)', + tooltipBackgroundBlur: 4, + background: { + type: BackgroundType.color, + color: '#fff', + overlay: { + enabled: false, + color: 'rgba(255,255,255,0.72)', + blur: 3 + } + } +}); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.html new file mode 100644 index 0000000000..62b7784aa4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.html @@ -0,0 +1,34 @@ + +
+
+ +
+
+
+
+
+
+
+
{{ rangeItem.label }}
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.scss new file mode 100644 index 0000000000..d9b42c6acb --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.scss @@ -0,0 +1,105 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.tb-range-chart-panel { + width: 100%; + height: 100%; + position: relative; + display: flex; + flex-direction: column; + gap: 16px; + padding: 20px 24px 24px 24px; + > div:not(.tb-range-chart-overlay) { + z-index: 1; + } + .tb-range-chart-overlay { + position: absolute; + top: 12px; + left: 12px; + bottom: 12px; + right: 12px; + } + div.tb-widget-title { + padding: 0; + } + .tb-range-chart-content { + flex: 1; + min-width: 0; + min-height: 0; + display: flex; + flex-direction: column; + gap: 16px; + &.legend-top { + flex-direction: column-reverse; + } + &.legend-right { + flex-direction: row; + } + &.legend-left { + flex-direction: row-reverse; + } + .tb-range-chart-shape { + flex: 1; + min-width: 0; + min-height: 0; + display: flex; + align-items: center; + justify-content: center; + } + .tb-range-chart-legend { + display: flex; + justify-content: center; + align-items: center; + align-self: stretch; + flex-wrap: wrap; + column-gap: 24px; + row-gap: 8px; + .tb-range-chart-legend-item { + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + user-select: none; + cursor: pointer; + .tb-range-chart-legend-item-label { + display: flex; + align-items: center; + gap: 4px; + color: #ccc; + .tb-range-chart-legend-item-label-circle { + width: 8px; + height: 8px; + border-radius: 50%; + background-color: #ccc; + } + } + } + } + &.legend-right, &.legend-left { + gap: 24px; + .tb-range-chart-legend { + flex-direction: column-reverse; + justify-content: flex-end; + align-items: stretch; + .tb-range-chart-legend-item { + flex-direction: row; + justify-content: space-between; + align-items: center; + } + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts new file mode 100644 index 0000000000..4625e4a2a9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts @@ -0,0 +1,532 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + Input, + OnDestroy, + OnInit, + Renderer2, + TemplateRef, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { + backgroundStyle, + ColorRange, + ComponentStyle, + DateFormatProcessor, filterIncludingColorRanges, + getDataKey, + overlayStyle, + sortedColorRange, + textStyle +} from '@shared/models/widget-settings.models'; +import { ResizeObserver } from '@juggle/resize-observer'; +import * as echarts from 'echarts/core'; +import { formatValue, isDefinedAndNotNull, isNumber } from '@core/utils'; +import { + DataZoomComponent, + DataZoomComponentOption, + GridComponent, + GridComponentOption, + MarkLineComponent, + MarkLineComponentOption, + TooltipComponent, + TooltipComponentOption, + VisualMapComponent, + VisualMapComponentOption +} from 'echarts/components'; +import { LineChart, LineSeriesOption, } from 'echarts/charts'; +import { CanvasRenderer } from 'echarts/renderers'; +import { rangeChartDefaultSettings, RangeChartWidgetSettings } from './range-chart-widget.models'; +import { DataSet } from '@shared/models/widget.models'; +import { Observable } from 'rxjs'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { DomSanitizer } from '@angular/platform-browser'; + +echarts.use([ + TooltipComponent, + GridComponent, + VisualMapComponent, + DataZoomComponent, + MarkLineComponent, + LineChart, + CanvasRenderer +]); + +type EChartsOption = echarts.ComposeOption< + | TooltipComponentOption + | GridComponentOption + | VisualMapComponentOption + | DataZoomComponentOption + | MarkLineComponentOption + | LineSeriesOption +>; + +type ECharts = echarts.ECharts; + +interface VisualPiece { + lt?: number; + gt?: number; + lte?: number; + gte?: number; + value?: number; + color?: string; +} + +interface RangeItem { + index: number; + from?: number; + to?: number; + piece: VisualPiece; + color: string; + label: string; + visible: boolean; + enabled: boolean; +} + +const rangeItemLabel = (from?: number, to?: number): string => { + if (isNumber(from) && isNumber(to)) { + if (from === to) { + return `${from}`; + } else { + return `${from} - ${to}`; + } + } else if (isNumber(from)) { + return `≥ ${from}`; + } else if (isNumber(to)) { + return `< ${to}`; + } else { + return null; + } +}; + +const toVisualPiece = (color: string, from?: number, to?: number): VisualPiece => { + const piece: VisualPiece = { + color + }; + if (isNumber(from) && isNumber(to)) { + if (from === to) { + piece.value = from; + } else { + piece.gte = from; + piece.lt = to; + } + } else if (isNumber(from)) { + piece.gte = from; + } else if (isNumber(to)) { + piece.lt = to; + } + return piece; +}; + +const toRangeItems = (colorRanges: Array): RangeItem[] => { + const rangeItems: RangeItem[] = []; + let counter = 0; + const ranges = sortedColorRange(filterIncludingColorRanges(colorRanges)).filter(r => isNumber(r.from) || isNumber(r.to)); + for (let i = 0; i < ranges.length; i++) { + const range = ranges[i]; + let from = range.from; + const to = range.to; + if (i > 0) { + const prevRange = ranges[i - 1]; + if (isNumber(prevRange.to) && isNumber(from) && from < prevRange.to) { + from = prevRange.to; + } + } + rangeItems.push( + { + index: counter++, + color: range.color, + enabled: true, + visible: true, + from, + to, + label: rangeItemLabel(from, to), + piece: toVisualPiece(range.color, from, to) + } + ); + if (!isNumber(from) || !isNumber(to)) { + const value = !isNumber(from) ? to : from; + rangeItems.push( + { + index: counter++, + color: 'transparent', + enabled: true, + visible: false, + label: '', + piece: { gt: value - 0.000000001, lt: value + 0.000000001, color: 'transparent'} + } + ); + } + } + return rangeItems; +}; + +const toNamedData = (data: DataSet): {name: string; value: [number, any]}[] => { + if (!data?.length) { + return []; + } else { + return data.map(d => ({ + name: d[0] + '', + value: d + })); + } +}; + +const getMarkPoints = (ranges: Array): number[] => { + const points = new Set(); + for (const range of ranges) { + if (range.visible) { + if (isNumber(range.from)) { + points.add(range.from); + } + if (isNumber(range.to)) { + points.add(range.to); + } + } + } + return Array.from(points).sort(); +}; + +@Component({ + selector: 'tb-range-chart-widget', + templateUrl: './range-chart-widget.component.html', + styleUrls: ['./range-chart-widget.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class RangeChartWidgetComponent implements OnInit, OnDestroy, AfterViewInit { + + @ViewChild('chartShape', {static: false}) + chartShape: ElementRef; + + settings: RangeChartWidgetSettings; + + @Input() + ctx: WidgetContext; + + @Input() + widgetTitlePanel: TemplateRef; + + showLegend: boolean; + legendClass: string; + + backgroundStyle$: Observable; + overlayStyle: ComponentStyle = {}; + + legendLabelStyle: ComponentStyle; + disabledLegendLabelStyle: ComponentStyle; + visibleRangeItems: RangeItem[]; + + private rangeItems: RangeItem[]; + + private shapeResize$: ResizeObserver; + + private decimals = 0; + private units = ''; + + private drawChartPending = false; + private rangeChart: ECharts; + private rangeChartOptions: EChartsOption; + private selectedRanges: {[key: number]: boolean} = {}; + + private tooltipDateFormat: DateFormatProcessor; + + constructor(private imagePipe: ImagePipe, + private sanitizer: DomSanitizer, + private renderer: Renderer2, + private cd: ChangeDetectorRef) { + } + + ngOnInit(): void { + this.ctx.$scope.rangeChartWidget = this; + this.settings = {...rangeChartDefaultSettings, ...this.ctx.settings}; + + this.decimals = this.ctx.decimals; + this.units = this.ctx.units; + const dataKey = getDataKey(this.ctx.datasources); + if (isDefinedAndNotNull(dataKey?.decimals)) { + this.decimals = dataKey.decimals; + } + if (dataKey?.units) { + this.units = dataKey.units; + } + + + this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); + this.overlayStyle = overlayStyle(this.settings.background.overlay); + + this.rangeItems = toRangeItems(this.settings.rangeColors); + this.visibleRangeItems = this.rangeItems.filter(item => item.visible); + for (const range of this.rangeItems) { + this.selectedRanges[range.index] = true; + } + + this.showLegend = this.settings.showLegend && !!this.rangeItems.length; + + if (this.showLegend) { + this.legendClass = `legend-${this.settings.legendPosition}`; + this.legendLabelStyle = textStyle(this.settings.legendLabelFont); + this.disabledLegendLabelStyle = textStyle(this.settings.legendLabelFont); + this.legendLabelStyle.color = this.settings.legendLabelColor; + } + + if (this.settings.showTooltip && this.settings.tooltipShowDate) { + this.tooltipDateFormat = DateFormatProcessor.fromSettings(this.ctx.$injector, this.settings.tooltipDateFormat); + } + } + + ngAfterViewInit() { + if (this.drawChartPending) { + this.drawChart(); + } + } + + ngOnDestroy() { + if (this.shapeResize$) { + this.shapeResize$.disconnect(); + } + if (this.rangeChart) { + this.rangeChart.dispose(); + } + } + + public onInit() { + const borderRadius = this.ctx.$widgetElement.css('borderRadius'); + this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; + if (this.chartShape) { + this.drawChart(); + } else { + this.drawChartPending = true; + } + this.cd.detectChanges(); + } + + public onDataUpdated() { + if (this.rangeChart) { + this.rangeChart.setOption({ + xAxis: { + min: this.ctx.defaultSubscription.timeWindow.minTime, + max: this.ctx.defaultSubscription.timeWindow.maxTime + }, + series: [ + {data: this.ctx.data?.length ? toNamedData(this.ctx.data[0].data) : []} + ], + visualMap: { + selected: this.selectedRanges + } + }); + } + } + + public toggleRangeItem(item: RangeItem) { + item.enabled = !item.enabled; + this.selectedRanges[item.index] = item.enabled; + this.rangeChart.dispatchAction({ + type: 'selectDataRange', + selected: this.selectedRanges + }); + } + + private drawChart() { + const dataKey = getDataKey(this.ctx.datasources); + this.rangeChart = echarts.init(this.chartShape.nativeElement, null, { + renderer: 'canvas', + }); + this.rangeChartOptions = { + tooltip: { + trigger: 'none' + }, + grid: { + containLabel: true, + top: '30', + left: 0, + right: 0, + bottom: this.settings.dataZoom ? 60 : 0 + }, + xAxis: { + type: 'time', + axisTick: { + show: true + }, + axisLabel: { + hideOverlap: true, + fontSize: 10 + }, + axisLine: { + onZero: false + }, + min: this.ctx.defaultSubscription.timeWindow.minTime, + max: this.ctx.defaultSubscription.timeWindow.maxTime + }, + yAxis: { + type: 'value', + axisLabel: { + formatter: value => formatValue(value, this.decimals, this.units, false) + } + }, + series: [{ + type: 'line', + name: dataKey?.label, + smooth: false, + showSymbol: false, + animation: true, + areaStyle: this.settings.fillArea ? {} : undefined, + data: this.ctx.data?.length ? toNamedData(this.ctx.data[0].data) : [], + markLine: this.rangeItems.length ? { + animation: true, + symbol: ['circle', 'arrow'], + symbolSize: [5, 7], + lineStyle: { + width: 1, + type: [3, 3], + color: '#37383b' + }, + label: { + position: 'insideEndTop', + color: '#37383b', + backgroundColor: 'rgba(255,255,255,0.56)', + padding: [4, 5], + borderRadius: 4, + formatter: params => formatValue(params.value, this.decimals, this.units, false) + }, + emphasis: { + disabled: true + }, + data: getMarkPoints(this.rangeItems).map(point => ({ yAxis: point })) + } : undefined + }], + dataZoom: [ + { + type: 'inside', + disabled: !this.settings.dataZoom + }, + { + type: 'slider', + show: this.settings.dataZoom, + showDetail: false, + right: 10 + } + ], + visualMap: { + show: false, + type: 'piecewise', + selected: this.selectedRanges, + pieces: this.rangeItems.map(item => item.piece), + outOfRange: { + color: this.settings.outOfRangeColor + }, + inRange: !this.rangeItems.length ? { + color: this.settings.outOfRangeColor + } : undefined + } + }; + + if (this.settings.showTooltip) { + this.rangeChartOptions.tooltip = { + trigger: 'axis', + formatter: (params) => { + if (!params.length || !params[0]) { + return null; + } + const seriesParams = params[0]; + const value = formatValue(seriesParams.value[1], this.decimals, this.units, false); + const tooltipElement: HTMLElement = this.renderer.createElement('div'); + this.renderer.setStyle(tooltipElement, 'display', 'flex'); + this.renderer.setStyle(tooltipElement, 'flex-direction', 'column'); + this.renderer.setStyle(tooltipElement, 'align-items', 'flex-start'); + this.renderer.setStyle(tooltipElement, 'gap', '4px'); + if (this.settings.tooltipShowDate) { + const dateElement: HTMLElement = this.renderer.createElement('div'); + const ts = seriesParams.value[0]; + this.tooltipDateFormat.update(ts); + this.renderer.appendChild(dateElement, this.renderer.createText(this.tooltipDateFormat.formatted)); + this.renderer.setStyle(dateElement, 'font-family', this.settings.tooltipDateFont.family); + this.renderer.setStyle(dateElement, 'font-size', this.settings.tooltipDateFont.size + this.settings.tooltipDateFont.sizeUnit); + this.renderer.setStyle(dateElement, 'font-style', this.settings.tooltipDateFont.style); + this.renderer.setStyle(dateElement, 'font-weight', this.settings.tooltipDateFont.weight); + this.renderer.setStyle(dateElement, 'line-height', this.settings.tooltipDateFont.lineHeight); + this.renderer.setStyle(dateElement, 'color', this.settings.tooltipDateColor); + this.renderer.appendChild(tooltipElement, dateElement); + } + const labelValueElement: HTMLElement = this.renderer.createElement('div'); + this.renderer.setStyle(labelValueElement, 'display', 'flex'); + this.renderer.setStyle(labelValueElement, 'flex-direction', 'row'); + this.renderer.setStyle(labelValueElement, 'align-items', 'center'); + this.renderer.setStyle(labelValueElement, 'align-self', 'stretch'); + this.renderer.setStyle(labelValueElement, 'gap', '12px'); + this.renderer.appendChild(tooltipElement, labelValueElement); + const labelElement: HTMLElement = this.renderer.createElement('div'); + this.renderer.setStyle(labelElement, 'display', 'flex'); + this.renderer.setStyle(labelElement, 'align-items', 'center'); + this.renderer.setStyle(labelElement, 'gap', '8px'); + this.renderer.appendChild(labelValueElement, labelElement); + const circleElement: HTMLElement = this.renderer.createElement('div'); + this.renderer.setStyle(circleElement, 'width', '8px'); + this.renderer.setStyle(circleElement, 'height', '8px'); + this.renderer.setStyle(circleElement, 'border-radius', '50%'); + this.renderer.setStyle(circleElement, 'background', seriesParams.color); + this.renderer.appendChild(labelElement, circleElement); + const labelTextElement: HTMLElement = this.renderer.createElement('div'); + this.renderer.appendChild(labelTextElement, this.renderer.createText(seriesParams.seriesName)); + this.renderer.setStyle(labelTextElement, 'font-family', 'Roboto'); + this.renderer.setStyle(labelTextElement, 'font-size', '12px'); + this.renderer.setStyle(labelTextElement, 'font-style', 'normal'); + this.renderer.setStyle(labelTextElement, 'font-weight', '400'); + this.renderer.setStyle(labelTextElement, 'line-height', '16px'); + this.renderer.setStyle(labelTextElement, 'letter-spacing', '0.4px'); + this.renderer.setStyle(labelTextElement, 'color', 'rgba(0, 0, 0, 0.76)'); + this.renderer.appendChild(labelElement, labelTextElement); + const valueElement: HTMLElement = this.renderer.createElement('div'); + this.renderer.appendChild(valueElement, this.renderer.createText(value)); + this.renderer.setStyle(valueElement, 'font-family', this.settings.tooltipValueFont.family); + this.renderer.setStyle(valueElement, 'font-size', this.settings.tooltipValueFont.size + this.settings.tooltipValueFont.sizeUnit); + this.renderer.setStyle(valueElement, 'font-style', this.settings.tooltipValueFont.style); + this.renderer.setStyle(valueElement, 'font-weight', this.settings.tooltipValueFont.weight); + this.renderer.setStyle(valueElement, 'line-height', this.settings.tooltipValueFont.lineHeight); + this.renderer.setStyle(valueElement, 'color', this.settings.tooltipValueColor); + this.renderer.appendChild(labelValueElement, valueElement); + return tooltipElement; + }, + padding: [8, 12], + backgroundColor: this.settings.tooltipBackgroundColor, + extraCssText: `line-height: 1; backdrop-filter: blur(${this.settings.tooltipBackgroundBlur}px);` + }; + } + + this.rangeChart.setOption(this.rangeChartOptions); + + this.shapeResize$ = new ResizeObserver(() => { + this.onResize(); + }); + this.shapeResize$.observe(this.chartShape.nativeElement); + this.onResize(); + } + + private onResize() { + const width = this.rangeChart.getWidth(); + const height = this.rangeChart.getHeight(); + const shapeWidth = this.chartShape.nativeElement.offsetWidth; + const shapeHeight = this.chartShape.nativeElement.offsetHeight; + if (width !== shapeWidth || height !== shapeHeight) { + this.rangeChart.resize(); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts new file mode 100644 index 0000000000..6bcd0c1497 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts @@ -0,0 +1,103 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + BackgroundSettings, + BackgroundType, + ColorRange, + DateFormatSettings, + Font, simpleDateFormat +} from '@shared/models/widget-settings.models'; +import { LegendPosition } from '@shared/models/widget.models'; + +export interface RangeChartWidgetSettings { + dataZoom: boolean; + rangeColors: Array; + outOfRangeColor: string; + fillArea: boolean; + showLegend: boolean; + legendPosition: LegendPosition; + legendLabelFont: Font; + legendLabelColor: string; + showTooltip: boolean; + tooltipValueFont: Font; + tooltipValueColor: string; + tooltipShowDate: boolean; + tooltipDateFormat: DateFormatSettings; + tooltipDateFont: Font; + tooltipDateColor: string; + tooltipBackgroundColor: string; + tooltipBackgroundBlur: number; + background: BackgroundSettings; +} + +export const rangeChartDefaultSettings: RangeChartWidgetSettings = { + dataZoom: true, + rangeColors: [ + {to: -20, color: '#234CC7'}, + {from: -20, to: 0, color: '#305AD7'}, + {from: 0, to: 10, color: '#7191EF'}, + {from: 10, to: 20, color: '#FFA600'}, + {from: 20, to: 30, color: '#F36900'}, + {from: 30, to: 40, color: '#F04022'}, + {from: 40, color: '#D81838'} + ], + outOfRangeColor: '#ccc', + fillArea: true, + showLegend: true, + legendPosition: LegendPosition.top, + legendLabelFont: { + family: 'Roboto', + size: 12, + sizeUnit: 'px', + style: 'normal', + weight: '400', + lineHeight: '16px' + }, + legendLabelColor: 'rgba(0, 0, 0, 0.76)', + showTooltip: true, + tooltipValueFont: { + family: 'Roboto', + size: 12, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '16px' + }, + tooltipValueColor: 'rgba(0, 0, 0, 0.76)', + tooltipShowDate: true, + tooltipDateFormat: simpleDateFormat('dd MMM yyyy HH:mm'), + tooltipDateFont: { + family: 'Roboto', + size: 11, + sizeUnit: 'px', + style: 'normal', + weight: '400', + lineHeight: '16px' + }, + tooltipDateColor: 'rgba(0, 0, 0, 0.76)', + tooltipBackgroundColor: 'rgba(255, 255, 255, 0.76)', + tooltipBackgroundBlur: 4, + background: { + type: BackgroundType.color, + color: '#fff', + overlay: { + enabled: false, + color: 'rgba(255,255,255,0.72)', + blur: 3 + } + } +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.html new file mode 100644 index 0000000000..a8c8ccf675 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.html @@ -0,0 +1,39 @@ + +
+
+
+ +
+
+
+
+
+ {{ icon }} +
+
+
{{ label }}
+
+
{{ valueText }}
+
+
+
+ chevron_right +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.scss new file mode 100644 index 0000000000..e2d526a3c7 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.scss @@ -0,0 +1,96 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + .tb-count-panel { + width: 100%; + height: 100%; + position: relative; + display: flex; + align-items: center; + justify-content: center; + padding: 12px; + &.tb-count-pointer { + cursor: pointer; + } + .tb-count-panel-content { + width: 100%; + height: 100%; + position: relative; + display: flex; + flex-direction: row; + align-items: center; + gap: 12px; + .tb-count-panel-column { + flex: 1; + display: flex; + flex-direction: column; + gap: 12px; + overflow: hidden; + } + .tb-count-panel-row { + flex: 1; + display: flex; + flex-direction: row; + align-items: center; + gap: 12px; + } + .tb-count-icon-panel { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + .mat-icon { + z-index: 1; + } + } + .tb-count-icon-background-panel { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + .tb-count-label-value-panel { + flex: 1; + display: flex; + flex-direction: column; + place-content: flex-start space-around; + align-items: flex-start; + gap: 0; + overflow: hidden; + &.tb-count-layout-row { + flex-direction: row; + align-items: center; + } + .tb-count-label { + overflow: hidden; + text-overflow: ellipsis; + width: 100%; + } + } + } + } +} + +:host ::ng-deep { + .tb-count-panel { + .tb-widget-title { + padding: 0; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.ts new file mode 100644 index 0000000000..63208d9665 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.ts @@ -0,0 +1,214 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + Input, + OnDestroy, + OnInit, + Renderer2, + TemplateRef, + ViewChild +} from '@angular/core'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { formatValue } from '@core/utils'; +import { + ColorProcessor, + ComponentStyle, + getSingleTsValue, + iconStyle, + textStyle +} from '@shared/models/widget-settings.models'; +import { + CountCardLayout, + countDefaultSettings, + CountWidgetSettings +} from '@home/components/widget/lib/count/count-widget.models'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { UtilsService } from '@core/services/utils.service'; + +const layoutHeight = 36; +const layoutHeightWithTitle = 60; +const layoutPadding = 24; + +@Component({ + selector: 'tb-count-widget', + templateUrl: './count-widget.component.html', + styleUrls: ['./count-widget.component.scss'] +}) +export class CountWidgetComponent implements OnInit, AfterViewInit, OnDestroy { + + @ViewChild('countPanel', {static: false}) + countPanel: ElementRef; + + @ViewChild('countPanelContent', {static: false}) + countPanelContent: ElementRef; + + settings: CountWidgetSettings; + + countCardLayout = CountCardLayout; + + @Input() + ctx: WidgetContext; + + @Input() + widgetTitlePanel: TemplateRef; + + @coerceBoolean() + @Input() + alarmElseEntity: boolean; + + layout: CountCardLayout; + + showLabel = true; + label: string; + labelStyle: ComponentStyle = {}; + labelColor: ColorProcessor; + + showIcon = true; + icon = ''; + iconStyle: ComponentStyle = {}; + iconColor: ColorProcessor; + + showIconBackground = true; + iconBackgroundSize: string; + iconBackgroundStyle: ComponentStyle = {}; + iconBackgroundColor: ColorProcessor; + + valueText = 'N/A'; + valueStyle: ComponentStyle = {}; + valueColor: ColorProcessor; + + showChevron = false; + chevronStyle: ComponentStyle = {}; + + hasCardClickAction = false; + + private panelResize$: ResizeObserver; + private hasTitle = false; + + constructor(private renderer: Renderer2, + private utils: UtilsService, + private cd: ChangeDetectorRef) { + } + + ngOnInit(): void { + this.ctx.$scope.countWidget = this; + this.settings = {...countDefaultSettings(this.alarmElseEntity), ...this.ctx.settings}; + + this.layout = this.settings.layout; + + this.showLabel = this.settings.showLabel; + this.label = this.utils.customTranslation(this.settings.label, this.settings.label); + this.labelStyle = textStyle(this.settings.labelFont); + this.labelColor = ColorProcessor.fromSettings(this.settings.labelColor); + + this.showIcon = this.settings.showIcon; + this.icon = this.settings.icon; + this.iconStyle = iconStyle(this.settings.iconSize, this.settings.iconSizeUnit); + this.iconColor = ColorProcessor.fromSettings(this.settings.iconColor); + + this.showIconBackground = this.settings.showIconBackground; + if (this.showIconBackground) { + this.iconBackgroundSize = this.settings.iconBackgroundSize + this.settings.iconBackgroundSizeUnit; + } else { + this.iconBackgroundSize = this.settings.iconSize + this.settings.iconSizeUnit; + } + this.iconBackgroundStyle = { + width: this.iconBackgroundSize, + height: this.iconBackgroundSize, + borderRadius: '4px' + }; + this.iconBackgroundColor = ColorProcessor.fromSettings(this.settings.iconBackgroundColor); + + this.valueStyle = textStyle(this.settings.valueFont); + this.valueColor = ColorProcessor.fromSettings(this.settings.valueColor); + + this.showChevron = this.settings.showChevron; + this.chevronStyle = iconStyle(this.settings.chevronSize, this.settings.chevronSizeUnit); + this.chevronStyle.color = this.settings.chevronColor; + + this.hasCardClickAction = this.ctx.actionsApi.getActionDescriptors('cardClick').length > 0; + this.hasTitle = this.ctx.widgetConfig.showTitle; + } + + public ngAfterViewInit() { + if (this.settings.autoScale) { + const height = this.hasTitle ? layoutHeightWithTitle : layoutHeight; + this.renderer.setStyle(this.countPanelContent.nativeElement, 'height', height + 'px'); + this.renderer.setStyle(this.countPanelContent.nativeElement, 'overflow', 'visible'); + this.renderer.setStyle(this.countPanelContent.nativeElement, 'position', 'absolute'); + this.panelResize$ = new ResizeObserver(() => { + this.onResize(); + }); + this.panelResize$.observe(this.countPanel.nativeElement); + this.onResize(); + } + } + + ngOnDestroy() { + if (this.panelResize$) { + this.panelResize$.disconnect(); + } + } + + public onInit() { + } + + public onDataUpdated() { + const tsValue = getSingleTsValue(this.ctx.data); + let value: any; + if (tsValue) { + value = tsValue[1]; + this.valueText = formatValue(value, 0); + } else { + this.valueText = 'N/A'; + } + this.labelColor.update(value); + this.iconColor.update(value); + this.iconBackgroundColor.update(value); + this.valueColor.update(value); + this.cd.detectChanges(); + } + + public cardClick($event: Event) { + this.ctx.actionsApi.cardClick($event); + } + + private onResize() { + const panelWidth = this.countPanel.nativeElement.getBoundingClientRect().width - layoutPadding; + const panelHeight = this.countPanel.nativeElement.getBoundingClientRect().height - layoutPadding; + const targetWidth = panelWidth; + let minAspect = 0.25; + if (this.settings.showChevron) { + minAspect -= 0.05; + } + if (this.hasTitle) { + minAspect += 0.15; + } + const aspect = Math.min(panelHeight / targetWidth, minAspect); + const targetHeight = targetWidth * aspect; + const height = this.hasTitle ? layoutHeightWithTitle : layoutHeight; + const scale = targetHeight / height; + const width = targetWidth / scale; + this.renderer.setStyle(this.countPanelContent.nativeElement, 'width', width + 'px'); + this.renderer.setStyle(this.countPanelContent.nativeElement, 'transform', `scale(${scale})`); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.models.ts new file mode 100644 index 0000000000..f33c1f0523 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.models.ts @@ -0,0 +1,124 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + ColorSettings, + ColorType, + constantColor, + cssUnit, + defaultColorFunction, + Font +} from '@shared/models/widget-settings.models'; + +export enum CountCardLayout { + column = 'column', + row = 'row' +} + +export const countCardLayouts = Object.keys(CountCardLayout) as CountCardLayout[]; + +export const countCardLayoutTranslations = new Map( + [ + [CountCardLayout.column, 'widgets.count.layout-column'], + [CountCardLayout.row, 'widgets.count.layout-row'] + ] +); + +export const alarmCountCardLayoutImages = new Map( + [ + [CountCardLayout.column, 'assets/widget/alarm-count/column-layout.svg'], + [CountCardLayout.row, 'assets/widget/alarm-count/row-layout.svg'] + ] +); + +export const entityCountCardLayoutImages = new Map( + [ + [CountCardLayout.column, 'assets/widget/entity-count/column-layout.svg'], + [CountCardLayout.row, 'assets/widget/entity-count/row-layout.svg'] + ] +); + +export interface CountWidgetSettings { + layout: CountCardLayout; + autoScale: boolean; + showLabel: boolean; + label: string; + labelFont: Font; + labelColor: ColorSettings; + showIcon: boolean; + icon: string; + iconSize: number; + iconSizeUnit: cssUnit; + iconColor: ColorSettings; + showIconBackground: boolean; + iconBackgroundSize: number; + iconBackgroundSizeUnit: cssUnit; + iconBackgroundColor: ColorSettings; + valueFont: Font; + valueColor: ColorSettings; + showChevron: boolean; + chevronSize: number; + chevronSizeUnit: cssUnit; + chevronColor: string; +} + +export const countDefaultSettings = (alarmElseEntity: boolean): CountWidgetSettings => ({ + layout: CountCardLayout.column, + autoScale: true, + showLabel: true, + label: alarmElseEntity ? 'Total' : 'Devices', + labelFont: { + family: 'Roboto', + size: 12, + sizeUnit: 'px', + style: 'normal', + weight: '400', + lineHeight: '16px' + }, + labelColor: constantColor('rgba(0, 0, 0, 0.54)'), + showIcon: true, + icon: alarmElseEntity ? 'warning' : 'devices', + iconSize: 20, + iconSizeUnit: 'px', + iconColor: constantColor('rgba(255, 255, 255, 1)'), + showIconBackground: true, + iconBackgroundSize: 36, + iconBackgroundSizeUnit: 'px', + iconBackgroundColor: alarmElseEntity + ? { + color: 'rgba(0, 105, 92, 1)', + type: ColorType.range, + rangeList: [ + {from: 0, to: 0, color: 'rgba(0, 105, 92, 1)'}, + {from: 1, color: 'rgba(209, 39, 48, 1)'} + ], + colorFunction: defaultColorFunction + } + : constantColor('rgba(241, 141, 23, 1)'), + valueFont: { + family: 'Roboto', + size: 20, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '24px' + }, + valueColor: constantColor('rgba(0, 0, 0, 0.87)'), + showChevron: false, + chevronSize: 24, + chevronSizeUnit: 'px', + chevronColor: 'rgba(0, 0, 0, 0.38)' +}); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.scss index b1abff0335..7017c61de0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.scss @@ -17,7 +17,7 @@ width: 100%; height: 100%; min-width: 300px; - overflow: hidden; + overflow: auto; background: #fff; border-radius: 4px; box-shadow: diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.component.scss index f5844d8aac..9b2d35e030 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.component.scss @@ -71,7 +71,7 @@ background-size: 18px 18px; } - mat-icon.node-icon { + .mat-icon.node-icon { width: 22px; min-width: 22px; height: 22px; @@ -109,7 +109,7 @@ background-size: 24px 24px; } - mat-icon.node-icon { + .mat-icon.node-icon { width: 40px; min-width: 40px; height: 40px; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.component.html deleted file mode 100644 index 55f7694642..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.component.html +++ /dev/null @@ -1,51 +0,0 @@ - -
-
- -
- - -   - - - -
-
-
- -
-
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.component.scss deleted file mode 100644 index 6731690b0b..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.component.scss +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -:host-context(.tb-has-timewindow) { - .tb-entities-hierarchy { - mat-toolbar { - height: 60px; - max-height: 60px; - .mat-toolbar-tools { - height: 60px; - max-height: 60px; - } - } - } -} - -:host { - .tb-entities-hierarchy { - mat-toolbar.mat-mdc-table-toolbar:not([color="primary"]) { - background: transparent; - } - mat-toolbar { - min-height: 39px; - max-height: 39px; - .mat-toolbar-tools { - min-height: 39px; - max-height: 39px; - } - } - - .tb-entities-nav-tree-panel { - overflow-x: auto; - overflow-y: auto; - } - } -} - -:host ::ng-deep { - .tb-nav-tree-container { - &.jstree-proton { - .jstree-anchor { - div.node-icon { - display: inline-block; - width: 22px; - height: 22px; - margin-right: 2px; - margin-bottom: 2px; - background-color: transparent; - background-repeat: no-repeat; - background-attachment: scroll; - background-position: center center; - background-size: 18px 18px; - } - - mat-icon.node-icon { - width: 22px; - min-width: 22px; - height: 22px; - min-height: 22px; - margin-right: 2px; - margin-bottom: 2px; - color: inherit; - vertical-align: middle; - - &.material-icons { - font-size: 18px; - line-height: 22px; - text-align: center; - } - } - - &.jstree-hovered:not(.jstree-clicked), - &.jstree-disabled { - div.node-icon { - opacity: .5; - } - } - } - } - } - - @media (max-width: 768px) { - .tb-nav-tree-container { - &.jstree-proton-responsive { - .jstree-anchor { - div.node-icon { - width: 40px; - height: 40px; - margin: 0; - background-size: 24px 24px; - } - - mat-icon.node-icon { - width: 40px; - min-width: 40px; - height: 40px; - min-height: 40px; - margin: 0; - - &.material-icons { - font-size: 24px; - line-height: 40px; - } - } - } - } - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.component.ts deleted file mode 100644 index fce781f1a2..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.component.ts +++ /dev/null @@ -1,480 +0,0 @@ -/// -/// Copyright © 2016-2023 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core'; -import { PageComponent } from '@shared/components/page.component'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { WidgetAction, WidgetContext } from '@home/models/widget-component.models'; -import { DatasourceData, DatasourceType, WidgetConfig, widgetType } from '@shared/models/widget.models'; -import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; -import { UtilsService } from '@core/services/utils.service'; -import cssjs from '@core/css/css'; -import { fromEvent } from 'rxjs'; -import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators'; -import { constructTableCssString } from '@home/components/widget/lib/table-widget.models'; -import { Overlay } from '@angular/cdk/overlay'; -import { - LoadNodesCallback, - NavTreeEditCallbacks, - NodesCallback, - NodeSearchCallback, - NodeSelectedCallback, - NodesInsertedCallback -} from '@shared/components/nav-tree.component'; -import { EntityType } from '@shared/models/entity-type.models'; -import { deepClone, hashCode } from '@core/utils'; -import { - defaultNodeIconFunction, - defaultNodeOpenedFunction, - defaultNodeRelationQueryFunction, - defaultNodesSortFunction, - EntitiesHierarchyWidgetSettings, - HierarchyNavTreeNode, - HierarchyNodeContext, - HierarchyNodeDatasource, - iconUrlHtml, - loadNodeCtxFunction, - materialIconHtml, - NodeDisabledFunction, - NodeHasChildrenFunction, - NodeIconFunction, - NodeOpenedFunction, - NodeRelationQueryFunction, - NodesSortFunction, - NodeTextFunction -} from '@home/components/widget/lib/entities-hierarchy-widget.models'; -import { EntityRelationsQuery } from '@shared/models/relation.models'; -import { AliasFilterType, RelationsQueryFilter } from '@shared/models/alias.models'; -import { EntityFilter } from '@shared/models/query/query.models'; - -@Component({ - selector: 'tb-entities-hierarchy-widget', - templateUrl: './entities-hierarchy-widget.component.html', - styleUrls: ['./entities-hierarchy-widget.component.scss'] -}) -export class EntitiesHierarchyWidgetComponent extends PageComponent implements OnInit, AfterViewInit { - - @Input() - ctx: WidgetContext; - - @ViewChild('searchInput') searchInputField: ElementRef; - - public toastTargetId = 'entities-hierarchy-' + this.utils.guid(); - - public textSearchMode = false; - public textSearch = null; - - public nodeEditCallbacks: NavTreeEditCallbacks = {}; - - private settings: EntitiesHierarchyWidgetSettings; - private widgetConfig: WidgetConfig; - private subscription: IWidgetSubscription; - private datasources: Array; - - private nodesMap: {[nodeId: string]: HierarchyNavTreeNode} = {}; - private pendingUpdateNodeTasks: {[nodeId: string]: () => void} = {}; - private nodeIdCounter = 0; - - private nodeRelationQueryFunction: NodeRelationQueryFunction; - private nodeIconFunction: NodeIconFunction; - private nodeTextFunction: NodeTextFunction; - private nodeDisabledFunction: NodeDisabledFunction; - private nodeOpenedFunction: NodeOpenedFunction; - private nodeHasChildrenFunction: NodeHasChildrenFunction; - private nodesSortFunction: NodesSortFunction; - - private searchAction: WidgetAction = { - name: 'action.search', - show: true, - icon: 'search', - onAction: () => { - this.enterFilterMode(); - } - }; - - constructor(protected store: Store, - private elementRef: ElementRef, - private overlay: Overlay, - private viewContainerRef: ViewContainerRef, - private utils: UtilsService) { - super(store); - } - - ngOnInit(): void { - this.ctx.$scope.entitiesHierarchyWidget = this; - this.settings = this.ctx.settings; - this.widgetConfig = this.ctx.widgetConfig; - this.subscription = this.ctx.defaultSubscription; - this.datasources = this.subscription.datasources as Array; - this.initializeConfig(); - this.ctx.updateWidgetParams(); - } - - ngAfterViewInit(): void { - fromEvent(this.searchInputField.nativeElement, 'keyup') - .pipe( - debounceTime(150), - distinctUntilChanged(), - tap(() => { - this.updateSearchNodes(); - }) - ) - .subscribe(); - } - - public onDataUpdated() { - this.updateNodeData(this.subscription.data); - } - - private initializeConfig() { - this.ctx.widgetActions = [this.searchAction]; - - const testNodeCtx: HierarchyNodeContext = { - entity: { - id: { - entityType: EntityType.DEVICE, - id: '123' - }, - name: 'TEST DEV1', - label: '' - }, - data: {}, - level: 2 - }; - const parentNodeCtx = deepClone(testNodeCtx); - parentNodeCtx.level = 1; - testNodeCtx.parentNodeCtx = parentNodeCtx; - - this.nodeRelationQueryFunction = loadNodeCtxFunction(this.settings.nodeRelationQueryFunction, 'widgetCtx, nodeCtx', this.ctx, testNodeCtx); - this.nodeIconFunction = loadNodeCtxFunction(this.settings.nodeIconFunction, 'widgetCtx, nodeCtx', this.ctx, testNodeCtx); - this.nodeTextFunction = loadNodeCtxFunction(this.settings.nodeTextFunction, 'widgetCtx, nodeCtx', this.ctx, testNodeCtx); - this.nodeDisabledFunction = loadNodeCtxFunction(this.settings.nodeDisabledFunction, 'widgetCtx, nodeCtx', this.ctx, testNodeCtx); - this.nodeOpenedFunction = loadNodeCtxFunction(this.settings.nodeOpenedFunction, 'widgetCtx, nodeCtx', this.ctx, testNodeCtx); - this.nodeHasChildrenFunction = loadNodeCtxFunction(this.settings.nodeHasChildrenFunction, 'widgetCtx, nodeCtx', this.ctx, testNodeCtx); - - const testNodeCtx2 = deepClone(testNodeCtx); - testNodeCtx2.entity.name = 'TEST DEV2'; - - this.nodesSortFunction = loadNodeCtxFunction(this.settings.nodesSortFunction, 'widgetCtx, nodeCtx1,nodeCtx2', this.ctx, testNodeCtx, testNodeCtx2); - - this.nodeRelationQueryFunction = this.nodeRelationQueryFunction || defaultNodeRelationQueryFunction; - this.nodeIconFunction = this.nodeIconFunction || defaultNodeIconFunction; - this.nodeTextFunction = this.nodeTextFunction || ((widgetCtx, nodeCtx) => nodeCtx.entity.name); - this.nodeDisabledFunction = this.nodeDisabledFunction || (() => false); - this.nodeOpenedFunction = this.nodeOpenedFunction || defaultNodeOpenedFunction; - this.nodeHasChildrenFunction = this.nodeHasChildrenFunction || (() => true); - this.nodesSortFunction = this.nodesSortFunction || defaultNodesSortFunction; - - const cssString = constructTableCssString(this.widgetConfig); - const cssParser = new cssjs(); - cssParser.testMode = false; - const namespace = 'entities-hierarchy-' + hashCode(cssString); - cssParser.cssPreviewNamespace = namespace; - cssParser.createStyleElement(namespace, cssString); - $(this.elementRef.nativeElement).addClass(namespace); - } - - private enterFilterMode() { - this.textSearchMode = true; - this.textSearch = ''; - this.ctx.hideTitlePanel = true; - this.ctx.detectChanges(true); - setTimeout(() => { - this.searchInputField.nativeElement.focus(); - this.searchInputField.nativeElement.setSelectionRange(0, 0); - }, 10); - } - - exitFilterMode() { - this.textSearchMode = false; - this.textSearch = null; - this.updateSearchNodes(); - this.ctx.hideTitlePanel = false; - this.ctx.detectChanges(true); - } - - private updateSearchNodes() { - if (this.textSearch != null) { - this.nodeEditCallbacks.search(this.textSearch); - } else { - this.nodeEditCallbacks.clearSearch(); - } - } - - private updateNodeData(subscriptionData: Array) { - const affectedNodes: string[] = []; - if (subscriptionData) { - subscriptionData.forEach((datasourceData) => { - const datasource = datasourceData.datasource as HierarchyNodeDatasource; - if (datasource.nodeId) { - const node = this.nodesMap[datasource.nodeId]; - const key = datasourceData.dataKey.label; - let value; - if (datasourceData.data && datasourceData.data.length) { - value = datasourceData.data[0][1]; - } - if (node.data.nodeCtx.data[key] !== value) { - if (affectedNodes.indexOf(datasource.nodeId) === -1) { - affectedNodes.push(datasource.nodeId); - } - node.data.nodeCtx.data[key] = value; - } - } - }); - } - affectedNodes.forEach((nodeId) => { - const node: HierarchyNavTreeNode = this.nodeEditCallbacks.getNode(nodeId); - if (node) { - this.updateNodeStyle(this.nodesMap[nodeId]); - } else { - this.pendingUpdateNodeTasks[nodeId] = () => { - this.updateNodeStyle(this.nodesMap[nodeId]); - }; - } - }); - } - - public loadNodes: LoadNodesCallback = (node, cb) => { - if (node.id === '#') { - const childNodes: HierarchyNavTreeNode[] = []; - let dataIndex = 0; - this.datasources.forEach((childDatasource, index) => { - const datasourceData = this.subscription.data.slice(dataIndex); - childNodes.push(this.datasourceToNode(childDatasource as HierarchyNodeDatasource, datasourceData)); - dataIndex += childDatasource.dataKeys.length; - }); - cb(this.prepareNodes(childNodes)); - } else { - if (node.data && node.data.nodeCtx.entity && node.data.nodeCtx.entity.id && node.data.datasource.type === DatasourceType.entity - && node.data.nodeCtx.entity.id.entityType !== 'function') { - this.loadChildren(node, node.data.datasource, cb); - /* (error) => { // TODO: - let errorText = 'Failed to get relations!'; - if (error && error.status === 400) { - errorText = 'Invalid relations query returned by \'Node relations query function\'! Please check widget configuration!'; - } - this.showError(errorText); - } - ); - */ - } else { - cb([]); - } - } - } - - public onNodeSelected: NodeSelectedCallback = (node, event) => { - let nodeId; - if (!node) { - nodeId = -1; - } else { - nodeId = node.id; - } - if (nodeId !== -1) { - const selectedNode = this.nodesMap[nodeId]; - if (selectedNode) { - const descriptors = this.ctx.actionsApi.getActionDescriptors('nodeSelected'); - if (descriptors.length) { - const entity = selectedNode.data.nodeCtx.entity; - this.ctx.actionsApi.handleWidgetAction(event, descriptors[0], entity.id, entity.name,{ nodeCtx: selectedNode.data.nodeCtx }, entity.label); - } - } - } - } - - public onNodesInserted: NodesInsertedCallback = (nodes) => { - if (nodes) { - nodes.forEach((nodeId) => { - const task = this.pendingUpdateNodeTasks[nodeId]; - if (task) { - task(); - delete this.pendingUpdateNodeTasks[nodeId]; - } - }); - } - } - - public searchCallback: NodeSearchCallback = (searchText, node) => { - const theNode = this.nodesMap[node.id]; - if (theNode && theNode.data.searchText) { - return theNode.data.searchText.includes(searchText.toLowerCase()); - } - return false; - } - - private updateNodeStyle(node: HierarchyNavTreeNode) { - const newText = this.prepareNodeText(node); - if (node.text !== newText) { - node.text = newText; - this.nodeEditCallbacks.updateNode(node.id, node.text); - } - const newDisabled = this.nodeDisabledFunction(this.ctx, node.data.nodeCtx); - if (node.state.disabled !== newDisabled) { - node.state.disabled = newDisabled; - if (node.state.disabled) { - this.nodeEditCallbacks.disableNode(node.id); - } else { - this.nodeEditCallbacks.enableNode(node.id); - } - } - const newHasChildren = this.nodeHasChildrenFunction(this.ctx, node.data.nodeCtx); - if (node.children !== newHasChildren) { - node.children = newHasChildren; - this.nodeEditCallbacks.setNodeHasChildren(node.id, node.children); - } - } - - private prepareNodes(nodes: HierarchyNavTreeNode[]): HierarchyNavTreeNode[] { - nodes = nodes.filter((node) => node !== null); - nodes.sort((node1, node2) => this.nodesSortFunction(this.ctx, node1.data.nodeCtx, node2.data.nodeCtx)); - return nodes; - } - - private prepareNodeText(node: HierarchyNavTreeNode): string { - const nodeIcon = this.prepareNodeIcon(node.data.nodeCtx); - const nodeText = this.nodeTextFunction(this.ctx, node.data.nodeCtx); - node.data.searchText = nodeText ? nodeText.replace(/<[^>]+>/g, '').toLowerCase() : ''; - return nodeIcon + nodeText; - } - - private prepareNodeIcon(nodeCtx: HierarchyNodeContext): string { - let iconInfo = this.nodeIconFunction(this.ctx, nodeCtx); - if (iconInfo) { - if (iconInfo === 'default') { - iconInfo = defaultNodeIconFunction(this.ctx, nodeCtx); - } - if (iconInfo && iconInfo !== 'default' && (iconInfo.iconUrl || iconInfo.materialIcon)) { - if (iconInfo.materialIcon) { - return materialIconHtml(iconInfo.materialIcon); - } else { - return iconUrlHtml(iconInfo.iconUrl); - } - } else { - return ''; - } - } else { - return ''; - } - } - - private datasourceToNode(datasource: HierarchyNodeDatasource, - data: DatasourceData[], - parentNodeCtx?: HierarchyNodeContext): HierarchyNavTreeNode { - const node: HierarchyNavTreeNode = { - id: (++this.nodeIdCounter) + '' - }; - this.nodesMap[node.id] = node; - datasource.nodeId = node.id; - node.icon = false; - const nodeCtx: HierarchyNodeContext = { - parentNodeCtx, - entity: { - id: { - id: datasource.entityId, - entityType: datasource.entityType - }, - name: datasource.entityName, - label: datasource.entityLabel ? datasource.entityLabel : datasource.entityName - }, - data: {} - }; - datasource.dataKeys.forEach((dataKey, index) => { - const keyData = data[index].data; - if (keyData && keyData.length && keyData[0].length > 1) { - nodeCtx.data[dataKey.label] = keyData[0][1]; - } else { - nodeCtx.data[dataKey.label] = ''; - } - }); - nodeCtx.level = parentNodeCtx ? parentNodeCtx.level + 1 : 1; - node.data = { - datasource, - nodeCtx - }; - node.state = { - disabled: this.nodeDisabledFunction(this.ctx, node.data.nodeCtx), - opened: this.nodeOpenedFunction(this.ctx, node.data.nodeCtx) - }; - node.text = this.prepareNodeText(node); - node.children = this.nodeHasChildrenFunction(this.ctx, node.data.nodeCtx); - return node; - } - - private loadChildren(parentNode: HierarchyNavTreeNode, datasource: HierarchyNodeDatasource, childrenNodesLoadCb: NodesCallback) { - const nodeCtx = parentNode.data.nodeCtx; - nodeCtx.childrenNodesLoaded = false; - const entityFilter = this.prepareNodeRelationsQueryFilter(nodeCtx); - const childrenDatasource = { - dataKeys: datasource.dataKeys, - type: DatasourceType.entity, - filterId: datasource.filterId, - entityFilter - } as HierarchyNodeDatasource; - const subscriptionOptions: WidgetSubscriptionOptions = { - type: widgetType.latest, - datasources: [childrenDatasource], - callbacks: { - onSubscriptionMessage: (subscription, message) => { - this.ctx.showToast(message.severity, message.message, undefined, - 'bottom', 'left', this.toastTargetId); - }, - onInitialPageDataChanged: (subscription) => { - this.ctx.subscriptionApi.removeSubscription(subscription.id); - this.nodeEditCallbacks.refreshNode(parentNode.id); - }, - onDataUpdated: subscription => { - if (nodeCtx.childrenNodesLoaded) { - this.updateNodeData(subscription.data); - } else { - const datasourcesPageData = subscription.datasourcePages[0]; - const dataPageData = subscription.dataPages[0]; - const childNodes: HierarchyNavTreeNode[] = []; - datasourcesPageData.data.forEach((childDatasource, index) => { - childNodes.push(this.datasourceToNode(childDatasource as HierarchyNodeDatasource, dataPageData.data[index], nodeCtx)); - }); - nodeCtx.childrenNodesLoaded = true; - childrenNodesLoadCb(this.prepareNodes(childNodes)); - } - } - } - }; - this.ctx.subscriptionApi.createSubscription(subscriptionOptions, true); - } - - private prepareNodeRelationQuery(nodeCtx: HierarchyNodeContext): EntityRelationsQuery { - let relationQuery = this.nodeRelationQueryFunction(this.ctx, nodeCtx); - if (relationQuery && relationQuery === 'default') { - relationQuery = defaultNodeRelationQueryFunction(this.ctx, nodeCtx); - } - return relationQuery as EntityRelationsQuery; - } - - private prepareNodeRelationsQueryFilter(nodeCtx: HierarchyNodeContext): EntityFilter { - const relationQuery = this.prepareNodeRelationQuery(nodeCtx); - return { - rootEntity: { - id: relationQuery.parameters.rootId, - entityType: relationQuery.parameters.rootType - }, - direction: relationQuery.parameters.direction, - filters: relationQuery.filters, - maxLevel: relationQuery.parameters.maxLevel, - fetchLastLevelOnly: relationQuery.parameters.fetchLastLevelOnly, - type: AliasFilterType.relationsQuery - } as RelationsQueryFilter; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.models.ts deleted file mode 100644 index 64267a5637..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.models.ts +++ /dev/null @@ -1,171 +0,0 @@ -/// -/// Copyright © 2016-2023 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -import { BaseData } from '@shared/models/base-data'; -import { EntityId } from '@shared/models/id/entity-id'; -import { NavTreeNode, NodesCallback } from '@shared/components/nav-tree.component'; -import { Datasource } from '@shared/models/widget.models'; -import { isDefined, isUndefined } from '@core/utils'; -import { EntityRelationsQuery, EntitySearchDirection, RelationTypeGroup } from '@shared/models/relation.models'; -import { EntityType } from '@shared/models/entity-type.models'; -import { WidgetContext } from '@home/models/widget-component.models'; - -export interface EntitiesHierarchyWidgetSettings { - nodeRelationQueryFunction: string; - nodeHasChildrenFunction: string; - nodeOpenedFunction: string; - nodeDisabledFunction: string; - nodeIconFunction: string; - nodeTextFunction: string; - nodesSortFunction: string; -} - -export interface HierarchyNodeContext { - parentNodeCtx?: HierarchyNodeContext; - entity: BaseData; - childrenNodesLoaded?: boolean; - level?: number; - data: {[key: string]: any}; -} - -export interface HierarchyNavTreeNode extends NavTreeNode { - data?: { - datasource: HierarchyNodeDatasource; - nodeCtx: HierarchyNodeContext; - searchText?: string; - }; -} - -export interface HierarchyNodeDatasource extends Datasource { - nodeId: string; -} - -export interface HierarchyNodeIconInfo { - iconUrl?: string; - materialIcon?: string; -} - -export type NodeRelationQueryFunction = (widgetCtx: WidgetContext, nodeCtx: HierarchyNodeContext) => EntityRelationsQuery | 'default'; -export type NodeTextFunction = (widgetCtx: WidgetContext, nodeCtx: HierarchyNodeContext) => string; -export type NodeDisabledFunction = (widgetCtx: WidgetContext, nodeCtx: HierarchyNodeContext) => boolean; -export type NodeIconFunction = (widgetCtx: WidgetContext, nodeCtx: HierarchyNodeContext) => HierarchyNodeIconInfo | 'default'; -export type NodeOpenedFunction = (widgetCtx: WidgetContext, nodeCtx: HierarchyNodeContext) => boolean; -export type NodeHasChildrenFunction = (widgetCtx: WidgetContext, nodeCtx: HierarchyNodeContext) => boolean; -export type NodesSortFunction = (widgetCtx: WidgetContext, nodeCtx1: HierarchyNodeContext, nodeCtx2: HierarchyNodeContext) => number; - -export function loadNodeCtxFunction any>(functionBody: string, argNames: string, ...args: any[]): F { - let nodeCtxFunction: F = null; - if (isDefined(functionBody) && functionBody.length) { - try { - nodeCtxFunction = new Function(argNames, functionBody) as F; - const res = nodeCtxFunction.apply(null, args); - if (isUndefined(res)) { - nodeCtxFunction = null; - } - } catch (e) { - nodeCtxFunction = null; - } - } - return nodeCtxFunction; -} - -export function materialIconHtml(materialIcon: string): string { - return '' + materialIcon + ''; -} - -export function iconUrlHtml(iconUrl: string): string { - return '
 
'; -} - -export const defaultNodeRelationQueryFunction: NodeRelationQueryFunction = (widgetContext, nodeCtx) => { - const entity = nodeCtx.entity; - const query: EntityRelationsQuery = { - parameters: { - rootId: entity.id.id, - rootType: entity.id.entityType as EntityType, - direction: EntitySearchDirection.FROM, - relationTypeGroup: RelationTypeGroup.COMMON, - maxLevel: 1 - }, - filters: [ - { - relationType: 'Contains', - entityTypes: [] - } - ] - }; - return query; -}; - -export const defaultNodeIconFunction: NodeIconFunction = (widgetContext, nodeCtx) => { - let materialIcon = 'insert_drive_file'; - const entity = nodeCtx.entity; - if (entity && entity.id && entity.id.entityType) { - switch (entity.id.entityType as EntityType | string) { - case 'function': - materialIcon = 'functions'; - break; - case EntityType.DEVICE: - materialIcon = 'devices_other'; - break; - case EntityType.ASSET: - materialIcon = 'domain'; - break; - case EntityType.TENANT: - materialIcon = 'supervisor_account'; - break; - case EntityType.CUSTOMER: - materialIcon = 'supervisor_account'; - break; - case EntityType.USER: - materialIcon = 'account_circle'; - break; - case EntityType.DASHBOARD: - materialIcon = 'dashboards'; - break; - case EntityType.ALARM: - materialIcon = 'notifications_active'; - break; - case EntityType.ENTITY_VIEW: - materialIcon = 'view_quilt'; - break; - } - } - return { - materialIcon - }; -}; - -export const defaultNodeOpenedFunction: NodeOpenedFunction = (widgetCtx, nodeCtx) => { - return nodeCtx.level <= 4; -}; - -export const defaultNodesSortFunction: NodesSortFunction = (widgetCtx, nodeCtx1, nodeCtx2) => { - let result = 0; - if (!nodeCtx1.entity.id.entityType || !nodeCtx2.entity.id.entityType ) { - if (nodeCtx1.entity.id.entityType) { - result = 1; - } else if (nodeCtx2.entity.id.entityType) { - result = -1; - } - } else { - result = nodeCtx1.entity.id.entityType.localeCompare(nodeCtx2.entity.id.entityType); - } - if (result === 0) { - result = nodeCtx1.entity.name.localeCompare(nodeCtx2.entity.name); - } - return result; -}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html deleted file mode 100644 index d89eaeda27..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html +++ /dev/null @@ -1,112 +0,0 @@ - -
-
- -
- - -   - - - -
-
-
- - - {{ column.title }} - - - - - - - -
- - - - -
-
- - - - - - -
-
-
- - -
- {{ noDataDisplayMessageText }} - {{ 'common.loading' | translate }} -
- - -
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts deleted file mode 100644 index 00ddd527d4..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts +++ /dev/null @@ -1,892 +0,0 @@ -/// -/// Copyright © 2016-2023 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -import { - AfterViewInit, - ChangeDetectorRef, - Component, - ElementRef, - Injector, - Input, - NgZone, - OnInit, - StaticProvider, - ViewChild, - ViewContainerRef -} from '@angular/core'; -import { PageComponent } from '@shared/components/page.component'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { WidgetAction, WidgetContext } from '@home/models/widget-component.models'; -import { - DataKey, - Datasource, - DatasourceData, - WidgetActionDescriptor, - WidgetConfig -} from '@shared/models/widget.models'; -import { IWidgetSubscription } from '@core/api/widget-api.models'; -import { UtilsService } from '@core/services/utils.service'; -import { TranslateService } from '@ngx-translate/core'; -import { - createLabelFromDatasource, - deepClone, - hashCode, - isDefined, - isNumber, - isObject, - isUndefined -} from '@core/utils'; -import cssjs from '@core/css/css'; -import { CollectionViewer, DataSource } from '@angular/cdk/collections'; -import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; -import { BehaviorSubject, fromEvent, merge, Observable } from 'rxjs'; -import { emptyPageData, PageData } from '@shared/models/page/page-data'; -import { EntityId } from '@shared/models/id/entity-id'; -import { entityTypeTranslations } from '@shared/models/entity-type.models'; -import { debounceTime, distinctUntilChanged, map, tap } from 'rxjs/operators'; -import { MatPaginator } from '@angular/material/paginator'; -import { MatSort, SortDirection } from '@angular/material/sort'; -import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; -import { - CellContentInfo, - CellStyleInfo, - checkHasActions, - constructTableCssString, - DisplayColumn, - EntityColumn, - EntityData, - entityDataSortOrderFromString, - findColumnByEntityKey, - findEntityKeyByColumnDef, - fromEntityColumnDef, - getCellContentInfo, - getCellStyleInfo, - getColumnDefaultVisibility, - getColumnSelectionAvailability, - getColumnWidth, - getHeaderTitle, - getEntityValue, - getRowStyleInfo, - getTableCellButtonActions, - noDataMessage, - prepareTableCellButtonActions, - RowStyleInfo, - TableCellButtonActionDescriptor, - TableWidgetDataKeySettings, - TableWidgetSettings, - widthStyle -} from '@home/components/widget/lib/table-widget.models'; -import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; -import { ComponentPortal } from '@angular/cdk/portal'; -import { - DISPLAY_COLUMNS_PANEL_DATA, - DisplayColumnsPanelComponent, - DisplayColumnsPanelData -} from '@home/components/widget/lib/display-columns-panel.component'; -import { - dataKeyToEntityKey, - Direction, - EntityDataPageLink, - entityDataPageLinkSortDirection, - EntityKeyType, - KeyFilter -} from '@shared/models/query/query.models'; -import { sortItems } from '@shared/models/page/page-link'; -import { entityFields } from '@shared/models/entity.models'; -import { DatePipe } from '@angular/common'; -import { coerceBooleanProperty } from '@angular/cdk/coercion'; -import { ResizeObserver } from '@juggle/resize-observer'; -import { hidePageSizePixelValue } from '@shared/models/constants'; -import { AggregationType } from '@shared/models/time/time.models'; - -interface EntitiesTableWidgetSettings extends TableWidgetSettings { - entitiesTitle: string; - enableSelectColumnDisplay: boolean; - defaultSortOrder: string; - displayEntityName: boolean; - entityNameColumnTitle: string; - displayEntityLabel: boolean; - entityLabelColumnTitle: string; - displayEntityType: boolean; -} - -@Component({ - selector: 'tb-entities-table-widget', - templateUrl: './entities-table-widget.component.html', - styleUrls: ['./entities-table-widget.component.scss', './table-widget.scss'] -}) -export class EntitiesTableWidgetComponent extends PageComponent implements OnInit, AfterViewInit { - - @Input() - ctx: WidgetContext; - - @ViewChild('searchInput') searchInputField: ElementRef; - @ViewChild(MatPaginator) paginator: MatPaginator; - @ViewChild(MatSort) sort: MatSort; - - public displayPagination = true; - public enableStickyHeader = true; - public enableStickyAction = true; - public pageSizeOptions; - public pageLink: EntityDataPageLink; - public sortOrderProperty: string; - public textSearchMode = false; - public hidePageSize = false; - public columns: Array = []; - public displayedColumns: string[] = []; - public entityDatasource: EntityDatasource; - public noDataDisplayMessageText: string; - private setCellButtonAction: boolean; - - private cellContentCache: Array = []; - private cellStyleCache: Array = []; - private rowStyleCache: Array = []; - - private settings: EntitiesTableWidgetSettings; - private widgetConfig: WidgetConfig; - private subscription: IWidgetSubscription; - private widgetResize$: ResizeObserver; - - private entitiesTitlePattern: string; - - private defaultPageSize = 10; - private defaultSortOrder = 'entityName'; - - private contentsInfo: {[key: string]: CellContentInfo} = {}; - private stylesInfo: {[key: string]: CellStyleInfo} = {}; - private columnWidth: {[key: string]: string} = {}; - private columnDefaultVisibility: {[key: string]: boolean} = {}; - private columnSelectionAvailability: {[key: string]: boolean} = {}; - - private rowStylesInfo: RowStyleInfo; - - private searchAction: WidgetAction = { - name: 'action.search', - show: true, - icon: 'search', - onAction: () => { - this.enterFilterMode(); - } - }; - - private columnDisplayAction: WidgetAction = { - name: 'entity.columns-to-display', - show: true, - icon: 'view_column', - onAction: ($event) => { - this.editColumnsToDisplay($event); - } - }; - - constructor(protected store: Store, - private elementRef: ElementRef, - private ngZone: NgZone, - private overlay: Overlay, - private viewContainerRef: ViewContainerRef, - private utils: UtilsService, - private datePipe: DatePipe, - private translate: TranslateService, - private domSanitizer: DomSanitizer, - private cd: ChangeDetectorRef) { - super(store); - this.pageLink = { - page: 0, - pageSize: this.defaultPageSize, - textSearch: null, - dynamic: true - }; - } - - ngOnInit(): void { - this.ctx.$scope.entitiesTableWidget = this; - this.settings = this.ctx.settings; - this.widgetConfig = this.ctx.widgetConfig; - this.subscription = this.ctx.defaultSubscription; - this.initializeConfig(); - this.updateDatasources(); - this.ctx.updateWidgetParams(); - if (this.displayPagination) { - this.widgetResize$ = new ResizeObserver(() => { - const showHidePageSize = this.elementRef.nativeElement.offsetWidth < hidePageSizePixelValue; - if (showHidePageSize !== this.hidePageSize) { - this.hidePageSize = showHidePageSize; - this.cd.markForCheck(); - } - }); - this.widgetResize$.observe(this.elementRef.nativeElement); - } - } - - ngOnDestroy(): void { - if (this.widgetResize$) { - this.widgetResize$.disconnect(); - } - } - - ngAfterViewInit(): void { - fromEvent(this.searchInputField.nativeElement, 'keyup') - .pipe( - debounceTime(150), - distinctUntilChanged(), - tap(() => { - if (this.displayPagination) { - this.paginator.pageIndex = 0; - } - this.updateData(); - }) - ) - .subscribe(); - - if (this.displayPagination) { - this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0); - } - ((this.displayPagination ? merge(this.sort.sortChange, this.paginator.page) : this.sort.sortChange) as Observable) - .pipe( - tap(() => this.updateData()) - ) - .subscribe(); - this.updateData(); - } - - public onDataUpdated() { - this.updateTitle(true); - this.entityDatasource.dataUpdated(); - this.clearCache(); - this.ctx.detectChanges(); - } - - public pageLinkSortDirection(): SortDirection { - return entityDataPageLinkSortDirection(this.pageLink); - } - - private initializeConfig() { - this.ctx.widgetActions = [this.searchAction, this.columnDisplayAction]; - - this.setCellButtonAction = !!this.ctx.actionsApi.getActionDescriptors('actionCellButton').length; - - if (this.settings.entitiesTitle && this.settings.entitiesTitle.length) { - this.entitiesTitlePattern = this.utils.customTranslation(this.settings.entitiesTitle, this.settings.entitiesTitle); - } else { - this.entitiesTitlePattern = this.translate.instant('entity.entities'); - } - - this.updateTitle(false); - - this.searchAction.show = isDefined(this.settings.enableSearch) ? this.settings.enableSearch : true; - this.displayPagination = isDefined(this.settings.displayPagination) ? this.settings.displayPagination : true; - this.enableStickyHeader = isDefined(this.settings.enableStickyHeader) ? this.settings.enableStickyHeader : true; - this.enableStickyAction = isDefined(this.settings.enableStickyAction) ? this.settings.enableStickyAction : true; - this.columnDisplayAction.show = isDefined(this.settings.enableSelectColumnDisplay) ? this.settings.enableSelectColumnDisplay : true; - - this.rowStylesInfo = getRowStyleInfo(this.settings, 'entity, ctx'); - - const pageSize = this.settings.defaultPageSize; - if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) { - this.defaultPageSize = pageSize; - } - this.pageSizeOptions = [this.defaultPageSize, this.defaultPageSize * 2, this.defaultPageSize * 3]; - this.pageLink.pageSize = this.displayPagination ? this.defaultPageSize : 1024; - - this.noDataDisplayMessageText = - noDataMessage(this.widgetConfig.noDataDisplayMessage, 'entity.no-entities-prompt', this.utils, this.translate); - - const cssString = constructTableCssString(this.widgetConfig); - const cssParser = new cssjs(); - cssParser.testMode = false; - const namespace = 'entities-table-' + hashCode(cssString); - cssParser.cssPreviewNamespace = namespace; - cssParser.createStyleElement(namespace, cssString); - $(this.elementRef.nativeElement).addClass(namespace); - } - - private updateTitle(updateWidgetParams = false) { - const newTitle = createLabelFromDatasource(this.subscription.datasources[0], this.entitiesTitlePattern); - if (this.ctx.widgetTitle !== newTitle) { - this.ctx.widgetTitle = newTitle; - if (updateWidgetParams) { - this.ctx.updateWidgetParams(); - } - } - } - - private updateDatasources() { - - const displayEntityName = isDefined(this.settings.displayEntityName) ? this.settings.displayEntityName : true; - const displayEntityLabel = isDefined(this.settings.displayEntityLabel) ? this.settings.displayEntityLabel : false; - let entityNameColumnTitle: string; - let entityLabelColumnTitle: string; - if (this.settings.entityNameColumnTitle && this.settings.entityNameColumnTitle.length) { - entityNameColumnTitle = this.utils.customTranslation(this.settings.entityNameColumnTitle, this.settings.entityNameColumnTitle); - } else { - entityNameColumnTitle = this.translate.instant('entity.entity-name'); - } - if (this.settings.entityLabelColumnTitle && this.settings.entityLabelColumnTitle.length) { - entityLabelColumnTitle = this.utils.customTranslation(this.settings.entityLabelColumnTitle, this.settings.entityLabelColumnTitle); - } else { - entityLabelColumnTitle = this.translate.instant('entity.entity-label'); - } - const displayEntityType = isDefined(this.settings.displayEntityType) ? this.settings.displayEntityType : true; - - if (displayEntityName) { - this.columns.push( - { - name: 'entityName', - label: 'entityName', - def: 'entityName', - title: entityNameColumnTitle, - sortable: true, - entityKey: { - key: 'name', - type: EntityKeyType.ENTITY_FIELD - } - } as EntityColumn - ); - this.contentsInfo.entityName = { - useCellContentFunction: false - }; - this.stylesInfo.entityName = { - useCellStyleFunction: false - }; - this.columnWidth.entityName = '0px'; - this.columnDefaultVisibility.entityName = true; - this.columnSelectionAvailability.entityName = true; - } - if (displayEntityLabel) { - this.columns.push( - { - name: 'entityLabel', - label: 'entityLabel', - def: 'entityLabel', - title: entityLabelColumnTitle, - sortable: true, - entityKey: { - key: 'label', - type: EntityKeyType.ENTITY_FIELD - } - } as EntityColumn - ); - this.contentsInfo.entityLabel = { - useCellContentFunction: false - }; - this.stylesInfo.entityLabel = { - useCellStyleFunction: false - }; - this.columnWidth.entityLabel = '0px'; - this.columnDefaultVisibility.entityLabel = true; - this.columnSelectionAvailability.entityLabel = true; - } - if (displayEntityType) { - this.columns.push( - { - name: 'entityType', - label: 'entityType', - def: 'entityType', - title: this.translate.instant('entity.entity-type'), - sortable: true, - entityKey: { - key: 'entityType', - type: EntityKeyType.ENTITY_FIELD - } - } as EntityColumn - ); - this.contentsInfo.entityType = { - useCellContentFunction: false - }; - this.stylesInfo.entityType = { - useCellStyleFunction: false - }; - this.columnWidth.entityType = '0px'; - this.columnDefaultVisibility.entityType = true; - this.columnSelectionAvailability.entityType = true; - } - - const dataKeys: Array = []; - - const datasource = this.subscription.options.datasources ? this.subscription.options.datasources[0] : null; - - if (datasource && datasource.dataKeys) { - datasource.dataKeys.forEach((entityDataKey) => { - const dataKey: EntityColumn = deepClone(entityDataKey) as EntityColumn; - dataKey.entityKey = dataKeyToEntityKey(entityDataKey); - if (dataKey.type === DataKeyType.function) { - dataKey.name = dataKey.label; - } - dataKeys.push(dataKey); - - const keySettings: TableWidgetDataKeySettings = dataKey.settings; - dataKey.label = this.utils.customTranslation(dataKey.label, dataKey.label); - dataKey.title = getHeaderTitle(dataKey, keySettings, this.utils); - dataKey.def = 'def' + this.columns.length; - dataKey.sortable = !dataKey.usePostProcessing && (!dataKey.aggregationType || dataKey.aggregationType === AggregationType.NONE); - if (dataKey.type === DataKeyType.entityField && - !isDefined(keySettings.columnWidth) || keySettings.columnWidth === '0px') { - const entityField = entityFields[dataKey.name]; - if (entityField && entityField.time) { - keySettings.columnWidth = '120px'; - } - } - - this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings, 'value, entity, ctx'); - this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, entity, ctx'); - this.contentsInfo[dataKey.def].units = dataKey.units; - this.contentsInfo[dataKey.def].decimals = dataKey.decimals; - this.columnWidth[dataKey.def] = getColumnWidth(keySettings); - this.columnDefaultVisibility[dataKey.def] = getColumnDefaultVisibility(keySettings, this.ctx); - this.columnSelectionAvailability[dataKey.def] = getColumnSelectionAvailability(keySettings); - this.columns.push(dataKey); - }); - this.displayedColumns.push(...this.columns.filter(column => this.columnDefaultVisibility[column.def]) - .map(column => column.def)); - } - - if (this.settings.defaultSortOrder && this.settings.defaultSortOrder.length) { - this.defaultSortOrder = this.utils.customTranslation(this.settings.defaultSortOrder, this.settings.defaultSortOrder); - } - - this.pageLink.sortOrder = entityDataSortOrderFromString(this.defaultSortOrder, this.columns); - let sortColumn: EntityColumn; - if (this.pageLink.sortOrder) { - sortColumn = findColumnByEntityKey(this.pageLink.sortOrder.key, this.columns); - } - this.sortOrderProperty = sortColumn ? sortColumn.def : null; - - if (this.setCellButtonAction) { - this.displayedColumns.push('actions'); - } - this.entityDatasource = new EntityDatasource(this.translate, dataKeys, this.subscription, this.ngZone, this.ctx); - } - - private editColumnsToDisplay($event: Event) { - if ($event) { - $event.stopPropagation(); - } - const target = $event.target || $event.currentTarget; - const config = new OverlayConfig(); - config.backdropClass = 'cdk-overlay-transparent-backdrop'; - config.hasBackdrop = true; - const connectedPosition: ConnectedPosition = { - originX: 'end', - originY: 'bottom', - overlayX: 'end', - overlayY: 'top' - }; - config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement) - .withPositions([connectedPosition]); - - const overlayRef = this.overlay.create(config); - overlayRef.backdropClick().subscribe(() => { - overlayRef.dispose(); - }); - - const columns: DisplayColumn[] = this.columns.map(column => { - return { - title: column.title, - def: column.def, - display: this.displayedColumns.indexOf(column.def) > -1, - selectable: this.columnSelectionAvailability[column.def] - }; - }); - - const providers: StaticProvider[] = [ - { - provide: DISPLAY_COLUMNS_PANEL_DATA, - useValue: { - columns, - columnsUpdated: (newColumns) => { - this.displayedColumns = newColumns.filter(column => column.display).map(column => column.def); - if (this.setCellButtonAction) { - this.displayedColumns.push('actions'); - } - this.clearCache(); - } - } as DisplayColumnsPanelData - }, - { - provide: OverlayRef, - useValue: overlayRef - } - ]; - const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); - overlayRef.attach(new ComponentPortal(DisplayColumnsPanelComponent, - this.viewContainerRef, injector)); - this.ctx.detectChanges(); - } - - private enterFilterMode() { - this.textSearchMode = true; - this.pageLink.textSearch = ''; - this.ctx.hideTitlePanel = true; - this.ctx.detectChanges(true); - setTimeout(() => { - this.searchInputField.nativeElement.focus(); - this.searchInputField.nativeElement.setSelectionRange(0, 0); - }, 10); - } - - exitFilterMode() { - this.textSearchMode = false; - this.pageLink.textSearch = null; - if (this.displayPagination) { - this.paginator.pageIndex = 0; - } - this.updateData(); - this.ctx.hideTitlePanel = false; - this.ctx.detectChanges(true); - } - - private updateData() { - if (this.displayPagination) { - this.pageLink.page = this.paginator.pageIndex; - this.pageLink.pageSize = this.paginator.pageSize; - } else { - this.pageLink.page = 0; - } - const key = findEntityKeyByColumnDef(this.sort.active, this.columns); - if (key) { - this.pageLink.sortOrder = { - key, - direction: Direction[this.sort.direction.toUpperCase()] - }; - } else { - this.pageLink.sortOrder = null; - } - const sortOrderLabel = fromEntityColumnDef(this.sort.active, this.columns); - const keyFilters: KeyFilter[] = null; // TODO: - this.entityDatasource.loadEntities(this.pageLink, sortOrderLabel, keyFilters); - this.ctx.detectChanges(); - } - - public trackByColumnDef(index, column: EntityColumn) { - return column.def; - } - - public trackByEntityId(index: number, entity: EntityData) { - return entity.id.id; - } - - public trackByActionCellDescriptionId(index: number, action: WidgetActionDescriptor) { - return action.id; - } - - public headerStyle(key: EntityColumn): any { - const columnWidth = this.columnWidth[key.def]; - return widthStyle(columnWidth); - } - - public rowStyle(entity: EntityData, row: number): any { - let res = this.rowStyleCache[row]; - if (!res) { - res = {}; - if (entity && this.rowStylesInfo.useRowStyleFunction && this.rowStylesInfo.rowStyleFunction) { - try { - res = this.rowStylesInfo.rowStyleFunction(entity, this.ctx); - if (!isObject(res)) { - throw new TypeError(`${res === null ? 'null' : typeof res} instead of style object`); - } - if (Array.isArray(res)) { - throw new TypeError(`Array instead of style object`); - } - } catch (e) { - res = {}; - console.warn(`Row style function in widget '${this.ctx.widgetTitle}' ` + - `returns '${e}'. Please check your row style function.`); - } - } - this.rowStyleCache[row] = res; - } - return res; - } - - public cellStyle(entity: EntityData, key: EntityColumn, row: number): any { - const col = this.columns.indexOf(key); - const index = row * this.columns.length + col; - let res = this.cellStyleCache[index]; - if (!res) { - res = {}; - if (entity && key) { - const styleInfo = this.stylesInfo[key.def]; - const value = getEntityValue(entity, key); - if (styleInfo.useCellStyleFunction && styleInfo.cellStyleFunction) { - try { - res = styleInfo.cellStyleFunction(value, entity, this.ctx); - if (!isObject(res)) { - throw new TypeError(`${res === null ? 'null' : typeof res} instead of style object`); - } - if (Array.isArray(res)) { - throw new TypeError(`Array instead of style object`); - } - } catch (e) { - res = {}; - console.warn(`Cell style function for data key '${key.label}' in widget '${this.ctx.widgetTitle}' ` + - `returns '${e}'. Please check your cell style function.`); - } - } - this.cellStyleCache[index] = res; - } - } - if (!res.width) { - const columnWidth = this.columnWidth[key.def]; - res = Object.assign(res, widthStyle(columnWidth)); - } - return res; - } - - public cellContent(entity: EntityData, key: EntityColumn, row: number): SafeHtml { - const col = this.columns.indexOf(key); - const index = row * this.columns.length + col; - let res = this.cellContentCache[index]; - if (isUndefined(res)) { - res = ''; - if (entity && key) { - const contentInfo = this.contentsInfo[key.def]; - const value = getEntityValue(entity, key); - let content: string; - if (contentInfo.useCellContentFunction && contentInfo.cellContentFunction) { - try { - content = contentInfo.cellContentFunction(value, entity, this.ctx); - } catch (e) { - content = '' + value; - } - } else { - content = this.defaultContent(key, contentInfo, value); - } - - if (isDefined(content)) { - content = this.utils.customTranslation(content, content); - switch (typeof content) { - case 'string': - res = this.domSanitizer.bypassSecurityTrustHtml(content); - break; - default: - res = content; - } - } - } - this.cellContentCache[index] = res; - } - return res; - } - - private defaultContent(key: EntityColumn, contentInfo: CellContentInfo, value: any): any { - if (isDefined(value)) { - const entityField = entityFields[key.name]; - if (entityField) { - if (entityField.time) { - return this.datePipe.transform(value, 'yyyy-MM-dd HH:mm:ss'); - } - } - const decimals = (contentInfo.decimals || contentInfo.decimals === 0) ? contentInfo.decimals : this.ctx.widgetConfig.decimals; - const units = contentInfo.units || this.ctx.widgetConfig.units; - return this.ctx.utils.formatValue(value, decimals, units, true); - } else { - return ''; - } - } - - public onRowClick($event: Event, entity: EntityData, isDouble?: boolean) { - if ($event) { - $event.stopPropagation(); - } - this.entityDatasource.toggleCurrentEntity(entity); - const actionSourceId = isDouble ? 'rowDoubleClick' : 'rowClick'; - const descriptors = this.ctx.actionsApi.getActionDescriptors(actionSourceId); - if (descriptors.length) { - let entityId; - let entityName; - let entityLabel; - if (entity) { - entityId = entity.id; - entityName = entity.entityName; - entityLabel = entity.entityLabel; - } - this.ctx.actionsApi.handleWidgetAction($event, descriptors[0], entityId, entityName, {entity}, entityLabel); - } - } - - public onActionButtonClick($event: Event, entity: EntityData, actionDescriptor: WidgetActionDescriptor) { - if ($event) { - $event.stopPropagation(); - } - let entityId; - let entityName; - let entityLabel; - if (entity) { - entityId = entity.id; - entityName = entity.entityName; - entityLabel = entity.entityLabel; - } - this.ctx.actionsApi.handleWidgetAction($event, actionDescriptor, entityId, entityName, {entity}, entityLabel); - } - - private clearCache() { - this.cellContentCache.length = 0; - this.cellStyleCache.length = 0; - this.rowStyleCache.length = 0; - } -} - -class EntityDatasource implements DataSource { - - private entitiesSubject = new BehaviorSubject([]); - private pageDataSubject = new BehaviorSubject>(emptyPageData()); - - private currentEntity: EntityData = null; - - public dataLoading = true; - public countCellButtonAction = 0; - - private appliedPageLink: EntityDataPageLink; - private appliedSortOrderLabel: string; - - private reserveSpaceForHiddenAction = true; - private cellButtonActions: TableCellButtonActionDescriptor[]; - private readonly usedShowCellActionFunction: boolean; - - constructor( - private translate: TranslateService, - private dataKeys: Array, - private subscription: IWidgetSubscription, - private ngZone: NgZone, - private widgetContext: WidgetContext - ) { - this.cellButtonActions = getTableCellButtonActions(widgetContext); - this.usedShowCellActionFunction = this.cellButtonActions.some(action => action.useShowActionCellButtonFunction); - if (this.widgetContext.settings.reserveSpaceForHiddenAction) { - this.reserveSpaceForHiddenAction = coerceBooleanProperty(this.widgetContext.settings.reserveSpaceForHiddenAction); - } - } - - connect(collectionViewer: CollectionViewer): Observable> { - return this.entitiesSubject.asObservable(); - } - - disconnect(collectionViewer: CollectionViewer): void { - this.entitiesSubject.complete(); - this.pageDataSubject.complete(); - } - - loadEntities(pageLink: EntityDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) { - this.dataLoading = true; - // this.clear(); - this.appliedPageLink = pageLink; - this.appliedSortOrderLabel = sortOrderLabel; - this.subscription.subscribeForPaginatedData(0, pageLink, keyFilters); - } - - private clear() { - this.entitiesSubject.next([]); - this.pageDataSubject.next(emptyPageData()); - } - - dataUpdated() { - const datasourcesPageData = this.subscription.datasourcePages[0]; - const dataPageData = this.subscription.dataPages[0]; - let entities = new Array(); - let maxCellButtonAction = 0; - const dynamicWidthCellButtonActions = this.usedShowCellActionFunction && !this.reserveSpaceForHiddenAction; - datasourcesPageData.data.forEach((datasource, index) => { - const entity = this.datasourceToEntityData(datasource, dataPageData.data[index]); - entities.push(entity); - if (dynamicWidthCellButtonActions && entity.actionCellButtons.length > maxCellButtonAction) { - maxCellButtonAction = entity.actionCellButtons.length; - } - }); - if (this.appliedSortOrderLabel && this.appliedSortOrderLabel.length) { - const asc = this.appliedPageLink.sortOrder.direction === Direction.ASC; - entities = entities.sort((a, b) => sortItems(a, b, this.appliedSortOrderLabel, asc)); - } - if (!dynamicWidthCellButtonActions && this.cellButtonActions.length && entities.length) { - maxCellButtonAction = entities[0].actionCellButtons.length; - } - const entitiesPageData: PageData = { - data: entities, - totalPages: datasourcesPageData.totalPages, - totalElements: datasourcesPageData.totalElements, - hasNext: datasourcesPageData.hasNext - }; - this.ngZone.run(() => { - this.entitiesSubject.next(entities); - this.pageDataSubject.next(entitiesPageData); - this.countCellButtonAction = maxCellButtonAction; - this.dataLoading = false; - }); - } - - private datasourceToEntityData(datasource: Datasource, data: DatasourceData[]): EntityData { - const entity: EntityData = { - id: {} as EntityId, - entityName: datasource.entityName, - entityLabel: datasource.entityLabel ? datasource.entityLabel : datasource.entityName - }; - if (datasource.entityId) { - entity.id.id = datasource.entityId; - } - if (datasource.entityType) { - entity.id.entityType = datasource.entityType; - entity.entityType = this.translate.instant(entityTypeTranslations.get(datasource.entityType).type); - } else { - entity.entityType = ''; - } - this.dataKeys.forEach((dataKey, index) => { - const keyData = data[index].data; - if (keyData && keyData.length && keyData[0].length > 1) { - if (data[index].dataKey.type !== DataKeyType.entityField || !entity.hasOwnProperty(dataKey.label)) { - entity[dataKey.label] = keyData[0][1]; - } - } else { - entity[dataKey.label] = ''; - } - }); - if (this.cellButtonActions.length) { - if (this.usedShowCellActionFunction) { - entity.actionCellButtons = prepareTableCellButtonActions(this.widgetContext, this.cellButtonActions, - entity, this.reserveSpaceForHiddenAction); - entity.hasActions = checkHasActions(entity.actionCellButtons); - } else { - entity.actionCellButtons = this.cellButtonActions; - entity.hasActions = true; - } - } - return entity; - } - - isEmpty(): Observable { - return this.entitiesSubject.pipe( - map((entities) => !entities.length) - ); - } - - total(): Observable { - return this.pageDataSubject.pipe( - map((pageData) => pageData.totalElements) - ); - } - - public toggleCurrentEntity(entity: EntityData): boolean { - if (this.currentEntity !== entity) { - this.currentEntity = entity; - return true; - } else { - return false; - } - } - - public isCurrentEntity(entity: EntityData): boolean { - return (this.currentEntity && entity && this.currentEntity.id && entity.id) && - (this.currentEntity.id.id === entity.id.id); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-hierarchy-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-hierarchy-widget.component.html new file mode 100644 index 0000000000..9ed3965532 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-hierarchy-widget.component.html @@ -0,0 +1,51 @@ + +
+
+ +
+ + +   + + + +
+
+
+ +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-hierarchy-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-hierarchy-widget.component.scss new file mode 100644 index 0000000000..426d81b723 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-hierarchy-widget.component.scss @@ -0,0 +1,121 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host-context(.tb-has-timewindow) { + .tb-entities-hierarchy { + mat-toolbar { + height: 60px; + max-height: 60px; + .mat-toolbar-tools { + height: 60px; + max-height: 60px; + } + } + } +} + +:host { + .tb-entities-hierarchy { + mat-toolbar.mat-mdc-table-toolbar:not([color="primary"]) { + background: transparent; + } + mat-toolbar { + min-height: 39px; + max-height: 39px; + .mat-toolbar-tools { + min-height: 39px; + max-height: 39px; + } + } + + .tb-entities-nav-tree-panel { + overflow-x: auto; + overflow-y: auto; + } + } +} + +:host ::ng-deep { + .tb-nav-tree-container { + &.jstree-proton { + .jstree-anchor { + div.node-icon { + display: inline-block; + width: 22px; + height: 22px; + margin-right: 2px; + margin-bottom: 2px; + background-color: transparent; + background-repeat: no-repeat; + background-attachment: scroll; + background-position: center center; + background-size: 18px 18px; + } + + .mat-icon.node-icon { + width: 22px; + min-width: 22px; + height: 22px; + min-height: 22px; + margin-right: 2px; + margin-bottom: 2px; + color: inherit; + vertical-align: middle; + + &.material-icons { + font-size: 18px; + line-height: 22px; + text-align: center; + } + } + + &.jstree-hovered:not(.jstree-clicked), + &.jstree-disabled { + div.node-icon { + opacity: .5; + } + } + } + } + } + + @media (max-width: 768px) { + .tb-nav-tree-container { + &.jstree-proton-responsive { + .jstree-anchor { + div.node-icon { + width: 40px; + height: 40px; + margin: 0; + background-size: 24px 24px; + } + + .mat-icon.node-icon { + width: 40px; + min-width: 40px; + height: 40px; + min-height: 40px; + margin: 0; + + &.material-icons { + font-size: 24px; + line-height: 40px; + } + } + } + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-hierarchy-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-hierarchy-widget.component.ts new file mode 100644 index 0000000000..17e95601c1 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-hierarchy-widget.component.ts @@ -0,0 +1,493 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + AfterViewInit, + Component, + ElementRef, + Input, + OnDestroy, + OnInit, + ViewChild, + ViewContainerRef +} from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { WidgetAction, WidgetContext } from '@home/models/widget-component.models'; +import { DatasourceData, DatasourceType, WidgetConfig, widgetType } from '@shared/models/widget.models'; +import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; +import { UtilsService } from '@core/services/utils.service'; +import cssjs from '@core/css/css'; +import { debounceTime, distinctUntilChanged, skip, startWith, takeUntil } from 'rxjs/operators'; +import { constructTableCssString } from '@home/components/widget/lib/table-widget.models'; +import { Overlay } from '@angular/cdk/overlay'; +import { + LoadNodesCallback, + NavTreeEditCallbacks, + NodesCallback, + NodeSearchCallback, + NodeSelectedCallback, + NodesInsertedCallback +} from '@shared/components/nav-tree.component'; +import { EntityType } from '@shared/models/entity-type.models'; +import { deepClone, hashCode, isDefinedAndNotNull, isEmptyStr } from '@core/utils'; +import { + defaultNodeIconFunction, + defaultNodeOpenedFunction, + defaultNodeRelationQueryFunction, + defaultNodesSortFunction, + EntitiesHierarchyWidgetSettings, + HierarchyNavTreeNode, + HierarchyNodeContext, + HierarchyNodeDatasource, + iconUrlHtml, + loadNodeCtxFunction, + materialIconHtml, + NodeDisabledFunction, + NodeHasChildrenFunction, + NodeIconFunction, + NodeOpenedFunction, + NodeRelationQueryFunction, + NodesSortFunction, + NodeTextFunction +} from '@home/components/widget/lib/entity/entities-hierarchy-widget.models'; +import { EntityRelationsQuery } from '@shared/models/relation.models'; +import { AliasFilterType, RelationsQueryFilter } from '@shared/models/alias.models'; +import { EntityFilter } from '@shared/models/query/query.models'; +import { FormBuilder } from '@angular/forms'; +import { Subject } from 'rxjs'; + +@Component({ + selector: 'tb-entities-hierarchy-widget', + templateUrl: './entities-hierarchy-widget.component.html', + styleUrls: ['./entities-hierarchy-widget.component.scss'] +}) +export class EntitiesHierarchyWidgetComponent extends PageComponent implements OnInit, AfterViewInit, OnDestroy { + + @Input() + ctx: WidgetContext; + + @ViewChild('searchInput') searchInputField: ElementRef; + + public toastTargetId = 'entities-hierarchy-' + this.utils.guid(); + + textSearchMode = false; + textSearch = this.fb.control('', {nonNullable: true}); + + public nodeEditCallbacks: NavTreeEditCallbacks = {}; + + private settings: EntitiesHierarchyWidgetSettings; + private widgetConfig: WidgetConfig; + private subscription: IWidgetSubscription; + private datasources: Array; + + private nodesMap: {[nodeId: string]: HierarchyNavTreeNode} = {}; + private pendingUpdateNodeTasks: {[nodeId: string]: () => void} = {}; + private nodeIdCounter = 0; + + private nodeRelationQueryFunction: NodeRelationQueryFunction; + private nodeIconFunction: NodeIconFunction; + private nodeTextFunction: NodeTextFunction; + private nodeDisabledFunction: NodeDisabledFunction; + private nodeOpenedFunction: NodeOpenedFunction; + private nodeHasChildrenFunction: NodeHasChildrenFunction; + private nodesSortFunction: NodesSortFunction; + + private searchAction: WidgetAction = { + name: 'action.search', + show: true, + icon: 'search', + onAction: () => { + this.enterFilterMode(); + } + }; + + private destroy$ = new Subject(); + + constructor(protected store: Store, + private elementRef: ElementRef, + private overlay: Overlay, + private viewContainerRef: ViewContainerRef, + private utils: UtilsService, + private fb: FormBuilder) { + super(store); + } + + ngOnInit(): void { + this.ctx.$scope.entitiesHierarchyWidget = this; + this.settings = this.ctx.settings; + this.widgetConfig = this.ctx.widgetConfig; + this.subscription = this.ctx.defaultSubscription; + this.datasources = this.subscription.datasources as Array; + this.initializeConfig(); + this.ctx.updateWidgetParams(); + } + + ngAfterViewInit(): void { + this.textSearch.valueChanges.pipe( + debounceTime(150), + startWith(''), + distinctUntilChanged((a: string, b: string) => a.trim() === b.trim()), + skip(1), + takeUntil(this.destroy$) + ).subscribe((value) => { + if (isDefinedAndNotNull(value) && !isEmptyStr(value)) { + this.nodeEditCallbacks.search(value.trim()); + } else { + this.nodeEditCallbacks.clearSearch(); + } + }); + } + + ngOnDestroy() { + super.ngOnDestroy(); + this.destroy$.next(); + this.destroy$.complete(); + } + + public onDataUpdated() { + this.updateNodeData(this.subscription.data); + } + + private initializeConfig() { + this.ctx.widgetActions = [this.searchAction]; + + const testNodeCtx: HierarchyNodeContext = { + entity: { + id: { + entityType: EntityType.DEVICE, + id: '123' + }, + name: 'TEST DEV1', + label: '' + }, + data: {}, + level: 2 + }; + const parentNodeCtx = deepClone(testNodeCtx); + parentNodeCtx.level = 1; + testNodeCtx.parentNodeCtx = parentNodeCtx; + + this.nodeRelationQueryFunction = loadNodeCtxFunction(this.settings.nodeRelationQueryFunction, 'widgetCtx, nodeCtx', this.ctx, testNodeCtx); + this.nodeIconFunction = loadNodeCtxFunction(this.settings.nodeIconFunction, 'widgetCtx, nodeCtx', this.ctx, testNodeCtx); + this.nodeTextFunction = loadNodeCtxFunction(this.settings.nodeTextFunction, 'widgetCtx, nodeCtx', this.ctx, testNodeCtx); + this.nodeDisabledFunction = loadNodeCtxFunction(this.settings.nodeDisabledFunction, 'widgetCtx, nodeCtx', this.ctx, testNodeCtx); + this.nodeOpenedFunction = loadNodeCtxFunction(this.settings.nodeOpenedFunction, 'widgetCtx, nodeCtx', this.ctx, testNodeCtx); + this.nodeHasChildrenFunction = loadNodeCtxFunction(this.settings.nodeHasChildrenFunction, 'widgetCtx, nodeCtx', this.ctx, testNodeCtx); + + const testNodeCtx2 = deepClone(testNodeCtx); + testNodeCtx2.entity.name = 'TEST DEV2'; + + this.nodesSortFunction = loadNodeCtxFunction(this.settings.nodesSortFunction, 'widgetCtx, nodeCtx1,nodeCtx2', this.ctx, testNodeCtx, testNodeCtx2); + + this.nodeRelationQueryFunction = this.nodeRelationQueryFunction || defaultNodeRelationQueryFunction; + this.nodeIconFunction = this.nodeIconFunction || defaultNodeIconFunction; + this.nodeTextFunction = this.nodeTextFunction || ((widgetCtx, nodeCtx) => nodeCtx.entity.name); + this.nodeDisabledFunction = this.nodeDisabledFunction || (() => false); + this.nodeOpenedFunction = this.nodeOpenedFunction || defaultNodeOpenedFunction; + this.nodeHasChildrenFunction = this.nodeHasChildrenFunction || (() => true); + this.nodesSortFunction = this.nodesSortFunction || defaultNodesSortFunction; + + const cssString = constructTableCssString(this.widgetConfig); + const cssParser = new cssjs(); + cssParser.testMode = false; + const namespace = 'entities-hierarchy-' + hashCode(cssString); + cssParser.cssPreviewNamespace = namespace; + cssParser.createStyleElement(namespace, cssString); + $(this.elementRef.nativeElement).addClass(namespace); + } + + private enterFilterMode() { + this.textSearchMode = true; + this.ctx.hideTitlePanel = true; + this.ctx.detectChanges(true); + setTimeout(() => { + this.searchInputField.nativeElement.focus(); + this.searchInputField.nativeElement.setSelectionRange(0, 0); + }, 10); + } + + exitFilterMode() { + this.textSearchMode = false; + this.textSearch.reset(); + this.ctx.hideTitlePanel = false; + this.ctx.detectChanges(true); + } + + private updateNodeData(subscriptionData: Array) { + const affectedNodes: string[] = []; + if (subscriptionData) { + subscriptionData.forEach((datasourceData) => { + const datasource = datasourceData.datasource as HierarchyNodeDatasource; + if (datasource.nodeId) { + const node = this.nodesMap[datasource.nodeId]; + const key = datasourceData.dataKey.label; + let value; + if (datasourceData.data && datasourceData.data.length) { + value = datasourceData.data[0][1]; + } + if (node.data.nodeCtx.data[key] !== value) { + if (affectedNodes.indexOf(datasource.nodeId) === -1) { + affectedNodes.push(datasource.nodeId); + } + node.data.nodeCtx.data[key] = value; + } + } + }); + } + affectedNodes.forEach((nodeId) => { + const node: HierarchyNavTreeNode = this.nodeEditCallbacks.getNode(nodeId); + if (node) { + this.updateNodeStyle(this.nodesMap[nodeId]); + } else { + this.pendingUpdateNodeTasks[nodeId] = () => { + this.updateNodeStyle(this.nodesMap[nodeId]); + }; + } + }); + } + + public loadNodes: LoadNodesCallback = (node, cb) => { + if (node.id === '#') { + const childNodes: HierarchyNavTreeNode[] = []; + let dataIndex = 0; + this.datasources.forEach((childDatasource, index) => { + const datasourceData = this.subscription.data.slice(dataIndex); + childNodes.push(this.datasourceToNode(childDatasource as HierarchyNodeDatasource, datasourceData)); + dataIndex += childDatasource.dataKeys.length; + }); + cb(this.prepareNodes(childNodes)); + } else { + if (node.data && node.data.nodeCtx.entity && node.data.nodeCtx.entity.id && node.data.datasource.type === DatasourceType.entity + && node.data.nodeCtx.entity.id.entityType !== 'function') { + this.loadChildren(node, node.data.datasource, cb); + /* (error) => { // TODO: + let errorText = 'Failed to get relations!'; + if (error && error.status === 400) { + errorText = 'Invalid relations query returned by \'Node relations query function\'! Please check widget configuration!'; + } + this.showError(errorText); + } + ); + */ + } else { + cb([]); + } + } + } + + public onNodeSelected: NodeSelectedCallback = (node, event) => { + let nodeId; + if (!node) { + nodeId = -1; + } else { + nodeId = node.id; + } + if (nodeId !== -1) { + const selectedNode = this.nodesMap[nodeId]; + if (selectedNode) { + const descriptors = this.ctx.actionsApi.getActionDescriptors('nodeSelected'); + if (descriptors.length) { + const entity = selectedNode.data.nodeCtx.entity; + this.ctx.actionsApi.handleWidgetAction(event, descriptors[0], entity.id, entity.name,{ nodeCtx: selectedNode.data.nodeCtx }, entity.label); + } + } + } + } + + public onNodesInserted: NodesInsertedCallback = (nodes) => { + if (nodes) { + nodes.forEach((nodeId) => { + const task = this.pendingUpdateNodeTasks[nodeId]; + if (task) { + task(); + delete this.pendingUpdateNodeTasks[nodeId]; + } + }); + } + } + + public searchCallback: NodeSearchCallback = (searchText, node) => { + const theNode = this.nodesMap[node.id]; + if (theNode && theNode.data.searchText) { + return theNode.data.searchText.includes(searchText.toLowerCase()); + } + return false; + } + + private updateNodeStyle(node: HierarchyNavTreeNode) { + const newText = this.prepareNodeText(node); + if (node.text !== newText) { + node.text = newText; + this.nodeEditCallbacks.updateNode(node.id, node.text); + } + const newDisabled = this.nodeDisabledFunction(this.ctx, node.data.nodeCtx); + if (node.state.disabled !== newDisabled) { + node.state.disabled = newDisabled; + if (node.state.disabled) { + this.nodeEditCallbacks.disableNode(node.id); + } else { + this.nodeEditCallbacks.enableNode(node.id); + } + } + const newHasChildren = this.nodeHasChildrenFunction(this.ctx, node.data.nodeCtx); + if (node.children !== newHasChildren) { + node.children = newHasChildren; + this.nodeEditCallbacks.setNodeHasChildren(node.id, node.children); + } + } + + private prepareNodes(nodes: HierarchyNavTreeNode[]): HierarchyNavTreeNode[] { + nodes = nodes.filter((node) => node !== null); + nodes.sort((node1, node2) => this.nodesSortFunction(this.ctx, node1.data.nodeCtx, node2.data.nodeCtx)); + return nodes; + } + + private prepareNodeText(node: HierarchyNavTreeNode): string { + const nodeIcon = this.prepareNodeIcon(node.data.nodeCtx); + const nodeText = this.nodeTextFunction(this.ctx, node.data.nodeCtx); + node.data.searchText = nodeText ? nodeText.replace(/<[^>]+>/g, '').toLowerCase() : ''; + return nodeIcon + nodeText; + } + + private prepareNodeIcon(nodeCtx: HierarchyNodeContext): string { + let iconInfo = this.nodeIconFunction(this.ctx, nodeCtx); + if (iconInfo) { + if (iconInfo === 'default') { + iconInfo = defaultNodeIconFunction(this.ctx, nodeCtx); + } + if (iconInfo && iconInfo !== 'default' && (iconInfo.iconUrl || iconInfo.materialIcon)) { + if (iconInfo.materialIcon) { + return materialIconHtml(iconInfo.materialIcon); + } else { + return iconUrlHtml(iconInfo.iconUrl); + } + } else { + return ''; + } + } else { + return ''; + } + } + + private datasourceToNode(datasource: HierarchyNodeDatasource, + data: DatasourceData[], + parentNodeCtx?: HierarchyNodeContext): HierarchyNavTreeNode { + const node: HierarchyNavTreeNode = { + id: (++this.nodeIdCounter) + '' + }; + this.nodesMap[node.id] = node; + datasource.nodeId = node.id; + node.icon = false; + const nodeCtx: HierarchyNodeContext = { + parentNodeCtx, + entity: { + id: { + id: datasource.entityId, + entityType: datasource.entityType + }, + name: datasource.entityName, + label: datasource.entityLabel ? datasource.entityLabel : datasource.entityName + }, + data: {} + }; + datasource.dataKeys.forEach((dataKey, index) => { + const keyData = data[index].data; + if (keyData && keyData.length && keyData[0].length > 1) { + nodeCtx.data[dataKey.label] = keyData[0][1]; + } else { + nodeCtx.data[dataKey.label] = ''; + } + }); + nodeCtx.level = parentNodeCtx ? parentNodeCtx.level + 1 : 1; + node.data = { + datasource, + nodeCtx + }; + node.state = { + disabled: this.nodeDisabledFunction(this.ctx, node.data.nodeCtx), + opened: this.nodeOpenedFunction(this.ctx, node.data.nodeCtx) + }; + node.text = this.prepareNodeText(node); + node.children = this.nodeHasChildrenFunction(this.ctx, node.data.nodeCtx); + return node; + } + + private loadChildren(parentNode: HierarchyNavTreeNode, datasource: HierarchyNodeDatasource, childrenNodesLoadCb: NodesCallback) { + const nodeCtx = parentNode.data.nodeCtx; + nodeCtx.childrenNodesLoaded = false; + const entityFilter = this.prepareNodeRelationsQueryFilter(nodeCtx); + const childrenDatasource = { + dataKeys: datasource.dataKeys, + type: DatasourceType.entity, + filterId: datasource.filterId, + entityFilter + } as HierarchyNodeDatasource; + const subscriptionOptions: WidgetSubscriptionOptions = { + type: widgetType.latest, + datasources: [childrenDatasource], + callbacks: { + onSubscriptionMessage: (subscription, message) => { + this.ctx.showToast(message.severity, message.message, undefined, + 'bottom', 'left', this.toastTargetId); + }, + onInitialPageDataChanged: (subscription) => { + this.ctx.subscriptionApi.removeSubscription(subscription.id); + this.nodeEditCallbacks.refreshNode(parentNode.id); + }, + onDataUpdated: subscription => { + if (nodeCtx.childrenNodesLoaded) { + this.updateNodeData(subscription.data); + } else { + const datasourcesPageData = subscription.datasourcePages[0]; + const dataPageData = subscription.dataPages[0]; + const childNodes: HierarchyNavTreeNode[] = []; + datasourcesPageData.data.forEach((childDatasource, index) => { + childNodes.push(this.datasourceToNode(childDatasource as HierarchyNodeDatasource, dataPageData.data[index], nodeCtx)); + }); + nodeCtx.childrenNodesLoaded = true; + childrenNodesLoadCb(this.prepareNodes(childNodes)); + } + } + } + }; + this.ctx.subscriptionApi.createSubscription(subscriptionOptions, true); + } + + private prepareNodeRelationQuery(nodeCtx: HierarchyNodeContext): EntityRelationsQuery { + let relationQuery = this.nodeRelationQueryFunction(this.ctx, nodeCtx); + if (relationQuery && relationQuery === 'default') { + relationQuery = defaultNodeRelationQueryFunction(this.ctx, nodeCtx); + } + return relationQuery as EntityRelationsQuery; + } + + private prepareNodeRelationsQueryFilter(nodeCtx: HierarchyNodeContext): EntityFilter { + const relationQuery = this.prepareNodeRelationQuery(nodeCtx); + return { + rootEntity: { + id: relationQuery.parameters.rootId, + entityType: relationQuery.parameters.rootType + }, + direction: relationQuery.parameters.direction, + filters: relationQuery.filters, + maxLevel: relationQuery.parameters.maxLevel, + fetchLastLevelOnly: relationQuery.parameters.fetchLastLevelOnly, + type: AliasFilterType.relationsQuery + } as RelationsQueryFilter; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-hierarchy-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-hierarchy-widget.models.ts new file mode 100644 index 0000000000..907878368d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-hierarchy-widget.models.ts @@ -0,0 +1,171 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { BaseData } from '@shared/models/base-data'; +import { EntityId } from '@shared/models/id/entity-id'; +import { NavTreeNode, NodesCallback } from '@shared/components/nav-tree.component'; +import { Datasource } from '@shared/models/widget.models'; +import { isDefined, isUndefined } from '@core/utils'; +import { EntityRelationsQuery, EntitySearchDirection, RelationTypeGroup } from '@shared/models/relation.models'; +import { EntityType } from '@shared/models/entity-type.models'; +import { WidgetContext } from '@home/models/widget-component.models'; + +export interface EntitiesHierarchyWidgetSettings { + nodeRelationQueryFunction: string; + nodeHasChildrenFunction: string; + nodeOpenedFunction: string; + nodeDisabledFunction: string; + nodeIconFunction: string; + nodeTextFunction: string; + nodesSortFunction: string; +} + +export interface HierarchyNodeContext { + parentNodeCtx?: HierarchyNodeContext; + entity: BaseData; + childrenNodesLoaded?: boolean; + level?: number; + data: {[key: string]: any}; +} + +export interface HierarchyNavTreeNode extends NavTreeNode { + data?: { + datasource: HierarchyNodeDatasource; + nodeCtx: HierarchyNodeContext; + searchText?: string; + }; +} + +export interface HierarchyNodeDatasource extends Datasource { + nodeId: string; +} + +export interface HierarchyNodeIconInfo { + iconUrl?: string; + materialIcon?: string; +} + +export type NodeRelationQueryFunction = (widgetCtx: WidgetContext, nodeCtx: HierarchyNodeContext) => EntityRelationsQuery | 'default'; +export type NodeTextFunction = (widgetCtx: WidgetContext, nodeCtx: HierarchyNodeContext) => string; +export type NodeDisabledFunction = (widgetCtx: WidgetContext, nodeCtx: HierarchyNodeContext) => boolean; +export type NodeIconFunction = (widgetCtx: WidgetContext, nodeCtx: HierarchyNodeContext) => HierarchyNodeIconInfo | 'default'; +export type NodeOpenedFunction = (widgetCtx: WidgetContext, nodeCtx: HierarchyNodeContext) => boolean; +export type NodeHasChildrenFunction = (widgetCtx: WidgetContext, nodeCtx: HierarchyNodeContext) => boolean; +export type NodesSortFunction = (widgetCtx: WidgetContext, nodeCtx1: HierarchyNodeContext, nodeCtx2: HierarchyNodeContext) => number; + +export function loadNodeCtxFunction any>(functionBody: string, argNames: string, ...args: any[]): F { + let nodeCtxFunction: F = null; + if (isDefined(functionBody) && functionBody.length) { + try { + nodeCtxFunction = new Function(argNames, functionBody) as F; + const res = nodeCtxFunction.apply(null, args); + if (isUndefined(res)) { + nodeCtxFunction = null; + } + } catch (e) { + nodeCtxFunction = null; + } + } + return nodeCtxFunction; +} + +export function materialIconHtml(materialIcon: string): string { + return '' + materialIcon + ''; +} + +export function iconUrlHtml(iconUrl: string): string { + return '
 
'; +} + +export const defaultNodeRelationQueryFunction: NodeRelationQueryFunction = (widgetContext, nodeCtx) => { + const entity = nodeCtx.entity; + const query: EntityRelationsQuery = { + parameters: { + rootId: entity.id.id, + rootType: entity.id.entityType as EntityType, + direction: EntitySearchDirection.FROM, + relationTypeGroup: RelationTypeGroup.COMMON, + maxLevel: 1 + }, + filters: [ + { + relationType: 'Contains', + entityTypes: [] + } + ] + }; + return query; +}; + +export const defaultNodeIconFunction: NodeIconFunction = (widgetContext, nodeCtx) => { + let materialIcon = 'insert_drive_file'; + const entity = nodeCtx.entity; + if (entity && entity.id && entity.id.entityType) { + switch (entity.id.entityType as EntityType | string) { + case 'function': + materialIcon = 'functions'; + break; + case EntityType.DEVICE: + materialIcon = 'devices_other'; + break; + case EntityType.ASSET: + materialIcon = 'domain'; + break; + case EntityType.TENANT: + materialIcon = 'supervisor_account'; + break; + case EntityType.CUSTOMER: + materialIcon = 'supervisor_account'; + break; + case EntityType.USER: + materialIcon = 'account_circle'; + break; + case EntityType.DASHBOARD: + materialIcon = 'dashboards'; + break; + case EntityType.ALARM: + materialIcon = 'notifications_active'; + break; + case EntityType.ENTITY_VIEW: + materialIcon = 'view_quilt'; + break; + } + } + return { + materialIcon + }; +}; + +export const defaultNodeOpenedFunction: NodeOpenedFunction = (widgetCtx, nodeCtx) => { + return nodeCtx.level <= 4; +}; + +export const defaultNodesSortFunction: NodesSortFunction = (widgetCtx, nodeCtx1, nodeCtx2) => { + let result = 0; + if (!nodeCtx1.entity.id.entityType || !nodeCtx2.entity.id.entityType ) { + if (nodeCtx1.entity.id.entityType) { + result = 1; + } else if (nodeCtx2.entity.id.entityType) { + result = -1; + } + } else { + result = nodeCtx1.entity.id.entityType.localeCompare(nodeCtx2.entity.id.entityType); + } + if (result === 0) { + result = nodeCtx1.entity.name.localeCompare(nodeCtx2.entity.name); + } + return result; +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.html new file mode 100644 index 0000000000..f0a248f341 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.html @@ -0,0 +1,113 @@ + +
+
+ +
+ + +   + + + +
+
+
+ + + {{ column.title }} + + + + + + + +
+ + + + +
+
+ + + + + + +
+
+
+ + +
+ {{ noDataDisplayMessageText }} + {{ 'common.loading' | translate }} +
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.scss similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.scss rename to ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.scss diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts new file mode 100644 index 0000000000..20855b248e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts @@ -0,0 +1,882 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + Injector, + Input, + NgZone, + OnDestroy, + OnInit, + StaticProvider, + ViewChild, + ViewContainerRef +} from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { WidgetAction, WidgetContext } from '@home/models/widget-component.models'; +import { + DataKey, + Datasource, + DatasourceData, + WidgetActionDescriptor, + WidgetConfig +} from '@shared/models/widget.models'; +import { IWidgetSubscription } from '@core/api/widget-api.models'; +import { UtilsService } from '@core/services/utils.service'; +import { TranslateService } from '@ngx-translate/core'; +import { deepClone, hashCode, isDefined, isNumber, isObject, isUndefined } from '@core/utils'; +import cssjs from '@core/css/css'; +import { CollectionViewer, DataSource } from '@angular/cdk/collections'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { BehaviorSubject, fromEvent, merge, Observable, Subject } from 'rxjs'; +import { emptyPageData, PageData } from '@shared/models/page/page-data'; +import { EntityId } from '@shared/models/id/entity-id'; +import { entityTypeTranslations } from '@shared/models/entity-type.models'; +import { debounceTime, distinctUntilChanged, map, takeUntil } from 'rxjs/operators'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort, SortDirection } from '@angular/material/sort'; +import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; +import { + CellContentInfo, + CellStyleInfo, + checkHasActions, + constructTableCssString, + DisplayColumn, + EntityColumn, + EntityData, + entityDataSortOrderFromString, + findColumnByEntityKey, + findEntityKeyByColumnDef, + fromEntityColumnDef, + getCellContentInfo, + getCellStyleInfo, + getColumnDefaultVisibility, + getColumnSelectionAvailability, + getColumnWidth, + getEntityValue, + getHeaderTitle, + getRowStyleInfo, + getTableCellButtonActions, + noDataMessage, + prepareTableCellButtonActions, + RowStyleInfo, + TableCellButtonActionDescriptor, + TableWidgetDataKeySettings, + TableWidgetSettings, + widthStyle +} from '@home/components/widget/lib/table-widget.models'; +import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; +import { ComponentPortal } from '@angular/cdk/portal'; +import { + DISPLAY_COLUMNS_PANEL_DATA, + DisplayColumnsPanelComponent, + DisplayColumnsPanelData +} from '@home/components/widget/lib/display-columns-panel.component'; +import { Direction } from '@shared/models/page/sort-order'; +import { + dataKeyToEntityKey, + EntityDataPageLink, + entityDataPageLinkSortDirection, + EntityKeyType, + KeyFilter +} from '@shared/models/query/query.models'; +import { sortItems } from '@shared/models/page/page-link'; +import { entityFields } from '@shared/models/entity.models'; +import { DatePipe } from '@angular/common'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { hidePageSizePixelValue } from '@shared/models/constants'; +import { AggregationType } from '@shared/models/time/time.models'; +import { FormBuilder } from '@angular/forms'; +import { DEFAULT_OVERLAY_POSITIONS } from '@shared/models/overlay.models'; + +interface EntitiesTableWidgetSettings extends TableWidgetSettings { + entitiesTitle: string; + enableSelectColumnDisplay: boolean; + defaultSortOrder: string; + displayEntityName: boolean; + entityNameColumnTitle: string; + displayEntityLabel: boolean; + entityLabelColumnTitle: string; + displayEntityType: boolean; +} + +@Component({ + selector: 'tb-entities-table-widget', + templateUrl: './entities-table-widget.component.html', + styleUrls: ['./entities-table-widget.component.scss', './../table-widget.scss'] +}) +export class EntitiesTableWidgetComponent extends PageComponent implements OnInit, AfterViewInit, OnDestroy { + + @Input() + ctx: WidgetContext; + + @ViewChild('searchInput') searchInputField: ElementRef; + @ViewChild(MatPaginator) paginator: MatPaginator; + @ViewChild(MatSort) sort: MatSort; + + textSearch = this.fb.control('', {nonNullable: true}); + + public displayPagination = true; + public enableStickyHeader = true; + public enableStickyAction = true; + public showCellActionsMenu = true; + public pageSizeOptions; + public pageLink: EntityDataPageLink; + public sortOrderProperty: string; + public textSearchMode = false; + public hidePageSize = false; + public columns: Array = []; + public displayedColumns: string[] = []; + public entityDatasource: EntityDatasource; + public noDataDisplayMessageText: string; + public hasRowAction: boolean; + private setCellButtonAction: boolean; + + private cellContentCache: Array = []; + private cellStyleCache: Array = []; + private rowStyleCache: Array = []; + + private settings: EntitiesTableWidgetSettings; + private widgetConfig: WidgetConfig; + private subscription: IWidgetSubscription; + private widgetResize$: ResizeObserver; + private destroy$ = new Subject(); + + private defaultPageSize = 10; + private defaultSortOrder = 'entityName'; + + private contentsInfo: {[key: string]: CellContentInfo} = {}; + private stylesInfo: {[key: string]: CellStyleInfo} = {}; + private columnWidth: {[key: string]: string} = {}; + private columnDefaultVisibility: {[key: string]: boolean} = {}; + private columnSelectionAvailability: {[key: string]: boolean} = {}; + + private rowStylesInfo: RowStyleInfo; + + private searchAction: WidgetAction = { + name: 'action.search', + show: true, + icon: 'search', + onAction: () => { + this.enterFilterMode(); + } + }; + + private columnDisplayAction: WidgetAction = { + name: 'entity.columns-to-display', + show: true, + icon: 'view_column', + onAction: ($event) => { + this.editColumnsToDisplay($event); + } + }; + + constructor(protected store: Store, + private elementRef: ElementRef, + private ngZone: NgZone, + private overlay: Overlay, + private viewContainerRef: ViewContainerRef, + private utils: UtilsService, + private datePipe: DatePipe, + private translate: TranslateService, + private domSanitizer: DomSanitizer, + private cd: ChangeDetectorRef, + private fb: FormBuilder) { + super(store); + this.pageLink = { + page: 0, + pageSize: this.defaultPageSize, + textSearch: null, + dynamic: true + }; + } + + ngOnInit(): void { + this.ctx.$scope.entitiesTableWidget = this; + this.settings = this.ctx.settings; + this.widgetConfig = this.ctx.widgetConfig; + this.subscription = this.ctx.defaultSubscription; + this.initializeConfig(); + this.updateDatasources(); + this.ctx.updateWidgetParams(); + if (this.displayPagination) { + this.widgetResize$ = new ResizeObserver(() => { + const showHidePageSize = this.elementRef.nativeElement.offsetWidth < hidePageSizePixelValue; + if (showHidePageSize !== this.hidePageSize) { + this.hidePageSize = showHidePageSize; + this.cd.markForCheck(); + } + }); + this.widgetResize$.observe(this.elementRef.nativeElement); + } + } + + ngOnDestroy(): void { + if (this.widgetResize$) { + this.widgetResize$.disconnect(); + } + this.destroy$.next(); + this.destroy$.complete(); + } + + ngAfterViewInit(): void { + this.textSearch.valueChanges.pipe( + debounceTime(150), + distinctUntilChanged((prev, current) => (this.pageLink.textSearch ?? '') === current.trim()), + takeUntil(this.destroy$) + ).subscribe((value) => { + if (this.displayPagination) { + this.paginator.pageIndex = 0; + } + this.pageLink.textSearch = value.trim(); + this.updateData(); + }); + + if (this.displayPagination) { + this.sort.sortChange.pipe(takeUntil(this.destroy$)).subscribe(() => this.paginator.pageIndex = 0); + } + ((this.displayPagination ? merge(this.sort.sortChange, this.paginator.page) : this.sort.sortChange) as Observable).pipe( + takeUntil(this.destroy$) + ).subscribe(() => this.updateData()); + this.updateData(); + } + + public onDataUpdated() { + this.entityDatasource.dataUpdated(); + this.clearCache(); + this.ctx.detectChanges(); + } + + public pageLinkSortDirection(): SortDirection { + return entityDataPageLinkSortDirection(this.pageLink); + } + + private initializeConfig() { + this.ctx.widgetActions = [this.searchAction, this.columnDisplayAction]; + + this.setCellButtonAction = !!this.ctx.actionsApi.getActionDescriptors('actionCellButton').length; + + this.hasRowAction = !!this.ctx.actionsApi.getActionDescriptors('rowClick').length || + !!this.ctx.actionsApi.getActionDescriptors('rowDoubleClick').length; + + if (this.settings.entitiesTitle && this.settings.entitiesTitle.length) { + this.ctx.widgetTitle = this.settings.entitiesTitle; + } else { + this.ctx.widgetTitle = this.translate.instant('entity.entities'); + } + + this.searchAction.show = isDefined(this.settings.enableSearch) ? this.settings.enableSearch : true; + this.displayPagination = isDefined(this.settings.displayPagination) ? this.settings.displayPagination : true; + this.enableStickyHeader = isDefined(this.settings.enableStickyHeader) ? this.settings.enableStickyHeader : true; + this.enableStickyAction = isDefined(this.settings.enableStickyAction) ? this.settings.enableStickyAction : true; + this.showCellActionsMenu = isDefined(this.settings.showCellActionsMenu) ? this.settings.showCellActionsMenu : true; + this.columnDisplayAction.show = isDefined(this.settings.enableSelectColumnDisplay) ? this.settings.enableSelectColumnDisplay : true; + + this.rowStylesInfo = getRowStyleInfo(this.settings, 'entity, ctx'); + + const pageSize = this.settings.defaultPageSize; + if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) { + this.defaultPageSize = pageSize; + } + this.pageSizeOptions = [this.defaultPageSize, this.defaultPageSize * 2, this.defaultPageSize * 3]; + this.pageLink.pageSize = this.displayPagination ? this.defaultPageSize : 1024; + + this.noDataDisplayMessageText = + noDataMessage(this.widgetConfig.noDataDisplayMessage, 'entity.no-entities-prompt', this.utils, this.translate); + + const cssString = constructTableCssString(this.widgetConfig); + const cssParser = new cssjs(); + cssParser.testMode = false; + const namespace = 'entities-table-' + hashCode(cssString); + cssParser.cssPreviewNamespace = namespace; + cssParser.createStyleElement(namespace, cssString); + $(this.elementRef.nativeElement).addClass(namespace); + } + + private updateDatasources() { + + const displayEntityName = isDefined(this.settings.displayEntityName) ? this.settings.displayEntityName : true; + const displayEntityLabel = isDefined(this.settings.displayEntityLabel) ? this.settings.displayEntityLabel : false; + let entityNameColumnTitle: string; + let entityLabelColumnTitle: string; + if (this.settings.entityNameColumnTitle && this.settings.entityNameColumnTitle.length) { + entityNameColumnTitle = this.utils.customTranslation(this.settings.entityNameColumnTitle, this.settings.entityNameColumnTitle); + } else { + entityNameColumnTitle = this.translate.instant('entity.entity-name'); + } + if (this.settings.entityLabelColumnTitle && this.settings.entityLabelColumnTitle.length) { + entityLabelColumnTitle = this.utils.customTranslation(this.settings.entityLabelColumnTitle, this.settings.entityLabelColumnTitle); + } else { + entityLabelColumnTitle = this.translate.instant('entity.entity-label'); + } + const displayEntityType = isDefined(this.settings.displayEntityType) ? this.settings.displayEntityType : true; + + if (displayEntityName) { + this.columns.push( + { + name: 'entityName', + label: 'entityName', + def: 'entityName', + title: entityNameColumnTitle, + sortable: true, + entityKey: { + key: 'name', + type: EntityKeyType.ENTITY_FIELD + } + } as EntityColumn + ); + this.contentsInfo.entityName = { + useCellContentFunction: false + }; + this.stylesInfo.entityName = { + useCellStyleFunction: false + }; + this.columnWidth.entityName = '0px'; + this.columnDefaultVisibility.entityName = true; + this.columnSelectionAvailability.entityName = true; + } + if (displayEntityLabel) { + this.columns.push( + { + name: 'entityLabel', + label: 'entityLabel', + def: 'entityLabel', + title: entityLabelColumnTitle, + sortable: true, + entityKey: { + key: 'label', + type: EntityKeyType.ENTITY_FIELD + } + } as EntityColumn + ); + this.contentsInfo.entityLabel = { + useCellContentFunction: false + }; + this.stylesInfo.entityLabel = { + useCellStyleFunction: false + }; + this.columnWidth.entityLabel = '0px'; + this.columnDefaultVisibility.entityLabel = true; + this.columnSelectionAvailability.entityLabel = true; + } + if (displayEntityType) { + this.columns.push( + { + name: 'entityType', + label: 'entityType', + def: 'entityType', + title: this.translate.instant('entity.entity-type'), + sortable: true, + entityKey: { + key: 'entityType', + type: EntityKeyType.ENTITY_FIELD + } + } as EntityColumn + ); + this.contentsInfo.entityType = { + useCellContentFunction: false + }; + this.stylesInfo.entityType = { + useCellStyleFunction: false + }; + this.columnWidth.entityType = '0px'; + this.columnDefaultVisibility.entityType = true; + this.columnSelectionAvailability.entityType = true; + } + + const dataKeys: Array = []; + + const datasource = this.subscription.options.datasources ? this.subscription.options.datasources[0] : null; + + if (datasource && datasource.dataKeys) { + datasource.dataKeys.forEach((entityDataKey) => { + const dataKey: EntityColumn = deepClone(entityDataKey) as EntityColumn; + dataKey.entityKey = dataKeyToEntityKey(entityDataKey); + if (dataKey.type === DataKeyType.function) { + dataKey.name = dataKey.label; + } + dataKeys.push(dataKey); + + const keySettings: TableWidgetDataKeySettings = dataKey.settings; + dataKey.label = this.utils.customTranslation(dataKey.label, dataKey.label); + dataKey.title = getHeaderTitle(dataKey, keySettings, this.utils); + dataKey.def = 'def' + this.columns.length; + dataKey.sortable = !dataKey.usePostProcessing && (!dataKey.aggregationType || dataKey.aggregationType === AggregationType.NONE); + if (dataKey.type === DataKeyType.entityField && + !isDefined(keySettings.columnWidth) || keySettings.columnWidth === '0px') { + const entityField = entityFields[dataKey.name]; + if (entityField && entityField.time) { + keySettings.columnWidth = '120px'; + } + } + + this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings, 'value, entity, ctx'); + this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, entity, ctx'); + this.contentsInfo[dataKey.def].units = dataKey.units; + this.contentsInfo[dataKey.def].decimals = dataKey.decimals; + this.columnWidth[dataKey.def] = getColumnWidth(keySettings); + this.columnDefaultVisibility[dataKey.def] = getColumnDefaultVisibility(keySettings, this.ctx); + this.columnSelectionAvailability[dataKey.def] = getColumnSelectionAvailability(keySettings); + this.columns.push(dataKey); + }); + this.displayedColumns.push(...this.columns.filter(column => this.columnDefaultVisibility[column.def]) + .map(column => column.def)); + } + + if (this.settings.defaultSortOrder && this.settings.defaultSortOrder.length) { + this.defaultSortOrder = this.utils.customTranslation(this.settings.defaultSortOrder, this.settings.defaultSortOrder); + } + + this.pageLink.sortOrder = entityDataSortOrderFromString(this.defaultSortOrder, this.columns); + let sortColumn: EntityColumn; + if (this.pageLink.sortOrder) { + sortColumn = findColumnByEntityKey(this.pageLink.sortOrder.key, this.columns); + } + this.sortOrderProperty = sortColumn ? sortColumn.def : null; + + if (this.setCellButtonAction) { + this.displayedColumns.push('actions'); + } + this.entityDatasource = new EntityDatasource(this.translate, dataKeys, this.subscription, this.ngZone, this.ctx); + } + + private editColumnsToDisplay($event: Event) { + if ($event) { + $event.stopPropagation(); + } + const target = $event.target || $event.srcElement || $event.currentTarget; + const config = new OverlayConfig({ + panelClass: 'tb-panel-container', + backdropClass: 'cdk-overlay-transparent-backdrop', + hasBackdrop: true, + height: 'fit-content', + maxHeight: '75vh' + }); + config.positionStrategy = this.overlay.position() + .flexibleConnectedTo(target as HTMLElement) + .withPositions(DEFAULT_OVERLAY_POSITIONS); + + const overlayRef = this.overlay.create(config); + overlayRef.backdropClick().subscribe(() => { + overlayRef.dispose(); + }); + + const columns: DisplayColumn[] = this.columns.map(column => ({ + title: column.title, + def: column.def, + display: this.displayedColumns.indexOf(column.def) > -1, + selectable: this.columnSelectionAvailability[column.def] + })); + + const providers: StaticProvider[] = [ + { + provide: DISPLAY_COLUMNS_PANEL_DATA, + useValue: { + columns, + columnsUpdated: (newColumns) => { + this.displayedColumns = newColumns.filter(column => column.display).map(column => column.def); + if (this.setCellButtonAction) { + this.displayedColumns.push('actions'); + } + this.clearCache(); + } + } as DisplayColumnsPanelData + }, + { + provide: OverlayRef, + useValue: overlayRef + } + ]; + + const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); + const componentRef = overlayRef.attach(new ComponentPortal(DisplayColumnsPanelComponent, + this.viewContainerRef, injector)); + + const resizeWindows$ = fromEvent(window, 'resize').subscribe(() => { + overlayRef.updatePosition(); + }); + componentRef.onDestroy(() => { + resizeWindows$.unsubscribe(); + }); + + this.ctx.detectChanges(); + } + + private enterFilterMode() { + this.textSearchMode = true; + this.ctx.hideTitlePanel = true; + this.ctx.detectChanges(true); + setTimeout(() => { + this.searchInputField.nativeElement.focus(); + this.searchInputField.nativeElement.setSelectionRange(0, 0); + }, 10); + } + + exitFilterMode() { + this.textSearchMode = false; + this.textSearch.reset(); + this.ctx.hideTitlePanel = false; + this.ctx.detectChanges(true); + } + + private updateData() { + if (this.displayPagination) { + this.pageLink.page = this.paginator.pageIndex; + this.pageLink.pageSize = this.paginator.pageSize; + } else { + this.pageLink.page = 0; + } + const key = findEntityKeyByColumnDef(this.sort.active, this.columns); + if (key) { + this.pageLink.sortOrder = { + key, + direction: Direction[this.sort.direction.toUpperCase()] + }; + } else { + this.pageLink.sortOrder = null; + } + const sortOrderLabel = fromEntityColumnDef(this.sort.active, this.columns); + const keyFilters: KeyFilter[] = null; // TODO: + this.entityDatasource.loadEntities(this.pageLink, sortOrderLabel, keyFilters); + this.ctx.detectChanges(); + } + + public trackByColumnDef(index, column: EntityColumn) { + return column.def; + } + + public trackByEntityId(index: number, entity: EntityData) { + return entity.id.id; + } + + public trackByActionCellDescriptionId(index: number, action: WidgetActionDescriptor) { + return action.id; + } + + public headerStyle(key: EntityColumn): any { + const columnWidth = this.columnWidth[key.def]; + return widthStyle(columnWidth); + } + + public rowStyle(entity: EntityData, row: number): any { + let res = this.rowStyleCache[row]; + if (!res) { + res = {}; + if (entity && this.rowStylesInfo.useRowStyleFunction && this.rowStylesInfo.rowStyleFunction) { + try { + res = this.rowStylesInfo.rowStyleFunction(entity, this.ctx); + if (!isObject(res)) { + throw new TypeError(`${res === null ? 'null' : typeof res} instead of style object`); + } + if (Array.isArray(res)) { + throw new TypeError(`Array instead of style object`); + } + } catch (e) { + res = {}; + console.warn(`Row style function in widget '${this.ctx.widgetTitle}' ` + + `returns '${e}'. Please check your row style function.`); + } + } + this.rowStyleCache[row] = res; + } + return res; + } + + public cellStyle(entity: EntityData, key: EntityColumn, row: number): any { + const col = this.columns.indexOf(key); + const index = row * this.columns.length + col; + let res = this.cellStyleCache[index]; + if (!res) { + res = {}; + if (entity && key) { + const styleInfo = this.stylesInfo[key.def]; + const value = getEntityValue(entity, key); + if (styleInfo.useCellStyleFunction && styleInfo.cellStyleFunction) { + try { + res = styleInfo.cellStyleFunction(value, entity, this.ctx); + if (!isObject(res)) { + throw new TypeError(`${res === null ? 'null' : typeof res} instead of style object`); + } + if (Array.isArray(res)) { + throw new TypeError(`Array instead of style object`); + } + } catch (e) { + res = {}; + console.warn(`Cell style function for data key '${key.label}' in widget '${this.ctx.widgetTitle}' ` + + `returns '${e}'. Please check your cell style function.`); + } + } + this.cellStyleCache[index] = res; + } + } + if (!res.width) { + const columnWidth = this.columnWidth[key.def]; + res = Object.assign(res, widthStyle(columnWidth)); + } + return res; + } + + public cellContent(entity: EntityData, key: EntityColumn, row: number): SafeHtml { + const col = this.columns.indexOf(key); + const index = row * this.columns.length + col; + let res = this.cellContentCache[index]; + if (isUndefined(res)) { + res = ''; + if (entity && key) { + const contentInfo = this.contentsInfo[key.def]; + const value = getEntityValue(entity, key); + let content: string; + if (contentInfo.useCellContentFunction && contentInfo.cellContentFunction) { + try { + content = contentInfo.cellContentFunction(value, entity, this.ctx); + } catch (e) { + content = '' + value; + } + } else { + content = this.defaultContent(key, contentInfo, value); + } + + if (isDefined(content)) { + content = this.utils.customTranslation(content, content); + switch (typeof content) { + case 'string': + res = this.domSanitizer.bypassSecurityTrustHtml(content); + break; + default: + res = content; + } + } + } + this.cellContentCache[index] = res; + } + return res; + } + + private defaultContent(key: EntityColumn, contentInfo: CellContentInfo, value: any): any { + if (isDefined(value)) { + const entityField = entityFields[key.name]; + if (entityField) { + if (entityField.time) { + return this.datePipe.transform(value, 'yyyy-MM-dd HH:mm:ss'); + } + } + const decimals = (contentInfo.decimals || contentInfo.decimals === 0) ? contentInfo.decimals : this.ctx.widgetConfig.decimals; + const units = contentInfo.units || this.ctx.widgetConfig.units; + return this.ctx.utils.formatValue(value, decimals, units, true); + } else { + return ''; + } + } + + public onRowClick($event: Event, entity: EntityData, isDouble?: boolean) { + if ($event) { + $event.stopPropagation(); + } + this.entityDatasource.toggleCurrentEntity(entity); + const actionSourceId = isDouble ? 'rowDoubleClick' : 'rowClick'; + const descriptors = this.ctx.actionsApi.getActionDescriptors(actionSourceId); + if (descriptors.length) { + let entityId; + let entityName; + let entityLabel; + if (entity) { + entityId = entity.id; + entityName = entity.entityName; + entityLabel = entity.entityLabel; + } + this.ctx.actionsApi.handleWidgetAction($event, descriptors[0], entityId, entityName, {entity}, entityLabel); + } + } + + public onActionButtonClick($event: Event, entity: EntityData, actionDescriptor: WidgetActionDescriptor) { + if ($event) { + $event.stopPropagation(); + } + let entityId; + let entityName; + let entityLabel; + if (entity) { + entityId = entity.id; + entityName = entity.entityName; + entityLabel = entity.entityLabel; + } + this.ctx.actionsApi.handleWidgetAction($event, actionDescriptor, entityId, entityName, {entity}, entityLabel); + } + + private clearCache() { + this.cellContentCache.length = 0; + this.cellStyleCache.length = 0; + this.rowStyleCache.length = 0; + } +} + +class EntityDatasource implements DataSource { + + private entitiesSubject = new BehaviorSubject([]); + private pageDataSubject = new BehaviorSubject>(emptyPageData()); + + private currentEntity: EntityData = null; + + public dataLoading = true; + public countCellButtonAction = 0; + + private appliedPageLink: EntityDataPageLink; + private appliedSortOrderLabel: string; + + private reserveSpaceForHiddenAction = true; + private cellButtonActions: TableCellButtonActionDescriptor[]; + private readonly usedShowCellActionFunction: boolean; + + constructor( + private translate: TranslateService, + private dataKeys: Array, + private subscription: IWidgetSubscription, + private ngZone: NgZone, + private widgetContext: WidgetContext + ) { + this.cellButtonActions = getTableCellButtonActions(widgetContext); + this.usedShowCellActionFunction = this.cellButtonActions.some(action => action.useShowActionCellButtonFunction); + if (this.widgetContext.settings.reserveSpaceForHiddenAction) { + this.reserveSpaceForHiddenAction = coerceBooleanProperty(this.widgetContext.settings.reserveSpaceForHiddenAction); + } + } + + connect(collectionViewer: CollectionViewer): Observable> { + return this.entitiesSubject.asObservable(); + } + + disconnect(collectionViewer: CollectionViewer): void { + this.entitiesSubject.complete(); + this.pageDataSubject.complete(); + } + + loadEntities(pageLink: EntityDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) { + this.dataLoading = true; + // this.clear(); + this.appliedPageLink = pageLink; + this.appliedSortOrderLabel = sortOrderLabel; + this.subscription.subscribeForPaginatedData(0, pageLink, keyFilters); + } + + private clear() { + this.entitiesSubject.next([]); + this.pageDataSubject.next(emptyPageData()); + } + + dataUpdated() { + const datasourcesPageData = this.subscription.datasourcePages[0]; + const dataPageData = this.subscription.dataPages[0]; + let entities = new Array(); + let maxCellButtonAction = 0; + const dynamicWidthCellButtonActions = this.usedShowCellActionFunction && !this.reserveSpaceForHiddenAction; + datasourcesPageData.data.forEach((datasource, index) => { + const entity = this.datasourceToEntityData(datasource, dataPageData.data[index]); + entities.push(entity); + if (dynamicWidthCellButtonActions && entity.actionCellButtons.length > maxCellButtonAction) { + maxCellButtonAction = entity.actionCellButtons.length; + } + }); + if (this.appliedSortOrderLabel && this.appliedSortOrderLabel.length) { + const asc = this.appliedPageLink.sortOrder.direction === Direction.ASC; + entities = entities.sort((a, b) => sortItems(a, b, this.appliedSortOrderLabel, asc)); + } + if (!dynamicWidthCellButtonActions && this.cellButtonActions.length && entities.length) { + maxCellButtonAction = entities[0].actionCellButtons.length; + } + const entitiesPageData: PageData = { + data: entities, + totalPages: datasourcesPageData.totalPages, + totalElements: datasourcesPageData.totalElements, + hasNext: datasourcesPageData.hasNext + }; + this.ngZone.run(() => { + this.entitiesSubject.next(entities); + this.pageDataSubject.next(entitiesPageData); + this.countCellButtonAction = maxCellButtonAction; + this.dataLoading = false; + }); + } + + private datasourceToEntityData(datasource: Datasource, data: DatasourceData[]): EntityData { + const entity: EntityData = { + id: {} as EntityId, + entityName: datasource.entityName, + entityLabel: datasource.entityLabel ? datasource.entityLabel : datasource.entityName + }; + if (datasource.entityId) { + entity.id.id = datasource.entityId; + } + if (datasource.entityType) { + entity.id.entityType = datasource.entityType; + entity.entityType = this.translate.instant(entityTypeTranslations.get(datasource.entityType).type); + } else { + entity.entityType = ''; + } + this.dataKeys.forEach((dataKey, index) => { + const keyData = data[index].data; + if (keyData && keyData.length && keyData[0].length > 1) { + if (data[index].dataKey.type !== DataKeyType.entityField || !entity.hasOwnProperty(dataKey.label)) { + entity[dataKey.label] = keyData[0][1]; + } + } else { + entity[dataKey.label] = ''; + } + }); + if (this.cellButtonActions.length) { + if (this.usedShowCellActionFunction) { + entity.actionCellButtons = prepareTableCellButtonActions(this.widgetContext, this.cellButtonActions, + entity, this.reserveSpaceForHiddenAction); + entity.hasActions = checkHasActions(entity.actionCellButtons); + } else { + entity.actionCellButtons = this.cellButtonActions; + entity.hasActions = true; + } + } + return entity; + } + + isEmpty(): Observable { + return this.entitiesSubject.pipe( + map((entities) => !entities.length) + ); + } + + total(): Observable { + return this.pageDataSubject.pipe( + map((pageData) => pageData.totalElements) + ); + } + + public toggleCurrentEntity(entity: EntityData): boolean { + if (this.currentEntity !== entity) { + this.currentEntity = entity; + return true; + } else { + return false; + } + } + + public isCurrentEntity(entity: EntityData): boolean { + return (this.currentEntity && entity && this.currentEntity.id && entity.id) && + (this.currentEntity.id.id === entity.id.id); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.component.ts index 9272295a27..1494e987ce 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.component.ts @@ -60,7 +60,9 @@ export class FlotWidgetComponent implements OnInit { this.settings = this.ctx.settings; this.chartType = this.chartType || 'line'; this.configureLegend(); - this.flot = new TbFlot(this.ctx, this.chartType, $(this.flotElement.nativeElement)); + if (this.ctx.datasources?.length) { + this.flot = new TbFlot(this.ctx, this.chartType, $(this.flotElement.nativeElement)); + } } private configureLegend(): void { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.models.ts index 2cc6b40300..7faa9e9de3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.models.ts @@ -132,10 +132,12 @@ export interface TbFlotYAxisSettings { ticksFormatter: string; tickDecimals: number; tickSize: number; + tickGenerator: string; } export interface TbFlotBaseSettings { stack: boolean; + enableSelection: boolean; shadowSize: number; fontColor: string; fontSize: number; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts b/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts index c347fa52fc..9f131e5c15 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts @@ -18,7 +18,8 @@ import { WidgetContext } from '@home/models/widget-component.models'; import { createLabelFromDatasource, - deepClone, formattedDataFormDatasourceData, + deepClone, + formattedDataFormDatasourceData, insertVariable, isDefined, isDefinedAndNotNull, @@ -59,6 +60,7 @@ import { AggregationType } from '@shared/models/time/time.models'; import { CancelAnimationFrame } from '@core/services/raf.service'; import { UtilsService } from '@core/services/utils.service'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { BehaviorSubject } from 'rxjs'; import Timeout = NodeJS.Timeout; const moment = moment_; @@ -117,6 +119,9 @@ export class TbFlot { private mouseleaveHandler = this.onFlotMouseLeave.bind(this); private flotClickHandler = this.onFlotClick.bind(this); + private enableSelection: boolean; + private selectionMode: 'x' | null; + private readonly showTooltip: boolean; private readonly animatedPie: boolean; private pieDataAnimationDuration: number; @@ -127,10 +132,18 @@ export class TbFlot { private pieAnimationLastTime: number; private pieAnimationCaf: CancelAnimationFrame; - constructor(private ctx: WidgetContext, private readonly chartType: ChartType, private $flotElement?: JQuery) { + private yMinSubject = new BehaviorSubject(-1); + private yMaxSubject = new BehaviorSubject(1); + + yMin$ = this.yMinSubject.asObservable(); + yMax$ = this.yMaxSubject.asObservable(); + + constructor(private ctx: WidgetContext, private readonly chartType: ChartType, private $flotElement?: JQuery, settings?: TbFlotSettings) { this.chartType = this.chartType || 'line'; - this.settings = ctx.settings as TbFlotSettings; + this.settings = settings || (ctx.settings as TbFlotSettings); this.utils = this.ctx.$injector.get(UtilsService); + this.enableSelection = isDefined(this.settings.enableSelection) ? this.settings.enableSelection : true; + this.selectionMode = this.enableSelection ? 'x' : null; this.showTooltip = isDefined(this.settings.showTooltip) ? this.settings.showTooltip : true; this.tooltip = this.showTooltip ? $('#flot-series-tooltip') : null; if (this.tooltip?.length === 0) { @@ -167,7 +180,7 @@ export class TbFlot { }; if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'state') { - this.options.selection = { mode : 'x' }; + this.options.selection = { mode: this.selectionMode, touch: true }; this.options.xaxes = []; this.xaxis = { mode: 'time', @@ -204,6 +217,12 @@ export class TbFlot { } else { this.yaxis.tickSize = null; } + if (this.settings.yaxis.tickGenerator?.length) { + try { + this.yaxis.ticks = new Function('axis', + this.settings.yaxis.tickGenerator); + } catch (e) {} + } if (isNumber(this.settings.yaxis.tickDecimals)) { this.yaxis.tickDecimals = this.settings.yaxis.tickDecimals; } else { @@ -396,7 +415,6 @@ export class TbFlot { for (let i = 0; i < this.subscription.data.length; i++) { const series = this.subscription.data[i] as TbFlotSeries; - colors.push(series.dataKey.color); const keySettings = series.dataKey.settings; series.dataKey.tooltipValueFormatFunction = tooltipValueFormatFunction; if (keySettings.tooltipValueFormatter && keySettings.tooltipValueFormatter.length) { @@ -447,8 +465,8 @@ export class TbFlot { apply: true }; } - const lineColor = tinycolor(series.dataKey.color); + colors.push(lineColor.toRgbString()); lineColor.setAlpha(.75); series.highlightColor = lineColor.toRgbString(); @@ -712,6 +730,15 @@ export class TbFlot { } } + public updateSeriesColor(color: string) { + if (this.subscription?.data?.length) { + const series = this.subscription.data[0] as TbFlotSeries; + series.dataKey.color = color; + series.color = color; + series.highlightColor = tinycolor(color).setAlpha(.75).toRgbString(); + } + } + private latestDataByDataIndex(index: number): FormattedData { if (this.latestData[index]) { return this.latestData[index]; @@ -797,6 +824,8 @@ export class TbFlot { clearTimeout(this.resizeTimeoutHandle); this.resizeTimeoutHandle = null; } + this.yMinSubject.complete(); + this.yMaxSubject.complete(); } private createPlot() { @@ -813,6 +842,7 @@ export class TbFlot { } else { this.plot = $.plot(this.$element, this.subscription.data, this.options) as JQueryPlot; } + this.updateYMinMax(); } else { this.createPlotTimeoutHandle = setTimeout(this.createPlot.bind(this), 30); } @@ -825,6 +855,20 @@ export class TbFlot { this.plot.setupGrid(); } this.plot.draw(); + this.updateYMinMax(); + } + + private updateYMinMax() { + if (this.plot?.getYAxes().length) { + const min = this.plot?.getYAxes()[0].min; + const max = this.plot?.getYAxes()[0].max; + if (this.yMinSubject.value !== min) { + this.yMinSubject.next(min); + } + if (this.yMaxSubject.value !== max) { + this.yMaxSubject.next(max); + } + } } private redrawPlot() { @@ -1014,7 +1058,9 @@ export class TbFlot { const series = this.subscription.data[i] as TbFlotSeries; this.substituteLabelPatterns(series, i); } - this.updateData(); + if (this.plot) { + this.updateData(); + } this.ctx.detectChanges(); } @@ -1251,7 +1297,7 @@ export class TbFlot { this.$element.css('pointer-events', ''); this.$element.addClass('mouse-events'); if (this.chartType !== 'pie') { - this.options.selection = {mode: 'x'}; + this.options.selection = {mode: this.selectionMode, touch: true}; this.$element.bind('plotselected', this.flotSelectHandler); this.$element.bind('dblclick', this.dblclickHandler); } @@ -1576,10 +1622,10 @@ export class TbFlot { const descriptors = this.ctx.actionsApi.getActionDescriptors('sliceClick'); if ($event && descriptors.length) { $event.stopPropagation(); - const entityInfo = this.ctx.actionsApi.getActiveEntityInfo(); - const entityId = entityInfo ? entityInfo.entityId : null; - const entityName = entityInfo ? entityInfo.entityName : null; - const entityLabel = entityInfo ? entityInfo.entityLabel : null; + const datasource = item.series.datasource; + const entityId = datasource ? datasource.entity?.id : null; + const entityName = datasource ? datasource.entityName : null; + const entityLabel = datasource ? datasource.entityLabel : null; this.ctx.actionsApi.handleWidgetAction($event, descriptors[0], entityId, entityName, item, entityLabel); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.html new file mode 100644 index 0000000000..ed327c1c90 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.html @@ -0,0 +1,53 @@ + +
+
{{ 'gateway.docker-label' | translate }}
+
+
device.connectivity.install-necessary-client-tools
+ +
+ +
+
gateway.download-configuration-file
+
+
gateway.download-docker-compose
+ +
+
+ +
+
gateway.launch-gateway
+
gateway.launch-docker-compose
+ +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.scss new file mode 100644 index 0000000000..6922398c00 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.scss @@ -0,0 +1,71 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + .tb-commands-hint { + color: inherit; + font-weight: normal; + flex: 1; + } +} + +:host ::ng-deep { + .tb-markdown-view { + .start-code { + .code-wrapper { + padding: 0; + + pre[class*=language-] { + margin: 0; + background: #F3F6FA; + border-color: #305680; + padding-right: 38px; + overflow: scroll; + padding-bottom: 4px; + + &::-webkit-scrollbar { + width: 4px; + height: 4px; + } + } + } + button.clipboard-btn { + right: -2px; + p { + color: #305680; + } + p, div { + background-color: #F3F6FA; + } + div { + img { + display: none; + } + &:after { + content: ""; + position: initial; + display: block; + width: 18px; + height: 18px; + background: #305680; + mask-image: url(/assets/copy-code-icon.svg); + mask-repeat: no-repeat; + } + } + } + } + } +} + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.ts new file mode 100644 index 0000000000..24aefe0e8a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.ts @@ -0,0 +1,42 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, Input } from '@angular/core'; +import { DeviceService } from '@core/http/device.service'; + +@Component({ + selector: 'tb-gateway-command', + templateUrl: './device-gateway-command.component.html', + styleUrls: ['./device-gateway-command.component.scss'] +}) + +export class DeviceGatewayCommandComponent { + + @Input() + deviceId: string; + + constructor(private deviceService: DeviceService) { + } + + download($event: Event) { + if ($event) { + $event.stopPropagation(); + } + if (this.deviceId) { + this.deviceService.downloadGatewayDockerComposeFile(this.deviceId).subscribe(() => {}); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.html new file mode 100644 index 0000000000..7ddf0c8cd9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.html @@ -0,0 +1,733 @@ + + +

gateway.gateway-configuration

+
+ + +
+
+
+ + {{ 'gateway.remote-configuration' | translate }} + +
+
+ + {{ 'gateway.remote-shell' | translate }} + +
+
+ + gateway.thingsboard-host + + info_outlined + + + {{ 'gateway.thingsboard-host-required' | translate }} + + + + gateway.thingsboard-port + + + {{ 'gateway.thingsboard-port-required' | translate }} + + + {{ 'gateway.thingsboard-port-min' | translate }} + + + {{ 'gateway.thingsboard-port-max' | translate }} + + + {{ 'gateway.thingsboard-port-pattern' | translate }} + + info_outlined + + +
+
+
+
security.security
+ + + {{ securityType.value | translate }} + + + security.access-token + + + {{ 'security.access-token-required' | translate }} + + info_outlined + + +
+ + security.clientId + + + {{ 'security.clientId-required' | translate }} + + info_outlined + + + + security.username + + + {{ 'security.username-required' | translate }} + + info_outlined + + +
+ + security.password + + info_outlined + + + + + +
+
+
+
+ +
+
+
+ + gateway.logs.date-format + + + {{ 'gateway.logs.date-format-required' | translate }} + + info_outlined + + + + gateway.logs.log-format + + + {{ 'gateway.logs.log-format-required' | translate }} + + info_outlined + + +
+
+
+
gateway.logs.remote
+
+ + {{ 'gateway.logs.remote-logs' | translate }} + +
+ + gateway.logs.level + + {{ logLevel }} + + +
+
+
gateway.logs.local
+ + {{ localLogsConfigTranslateMap.get(logConfig) }} + + +
+ + gateway.logs.level + + {{ logLevel }} + + + + gateway.logs.file-path + + + {{ 'gateway.logs.file-path-required' | translate }} + + +
+
+
+ + gateway.logs.saving-period + + + {{ 'gateway.logs.saving-period-required' | translate }} + + + {{ 'gateway.logs.saving-period-min' | translate }} + + + + + + {{ period.value | translate }} + + + +
+ + gateway.logs.backup-count + + + {{ 'gateway.logs.backup-count-required' | translate }} + + + {{ 'gateway.logs.backup-count-min' | translate }} + + info_outlined + + +
+
+
+
+
+ +
+
+
gateway.storage
+
gateway.hints.storage
+ + + {{ storageTypesTranslationMap.get(storageType) | translate }} + + +
{{ 'gateway.hints.' + gatewayConfigGroup.get('storage.type').value | translate }}
+ +
+ + gateway.storage-read-record-count + + + {{ 'gateway.storage-read-record-count-required' | translate }} + + + {{ 'gateway.storage-read-record-count-min' | translate }} + + + {{ 'gateway.storage-read-record-count-pattern' | translate }} + + info_outlined + + + + gateway.storage-max-records + + + {{ 'gateway.storage-max-records-required' | translate }} + + + {{ 'gateway.storage-max-records-min' | translate }} + + + {{ 'gateway.storage-max-records-pattern' | translate }} + + info_outlined + + +
+
+
+ + gateway.storage-data-folder-path + + + {{ 'gateway.storage-data-folder-path-required' | translate }} + + info_outlined + + + + gateway.storage-max-files + + + {{ 'gateway.storage-max-files-required' | translate }} + + + {{ 'gateway.storage-max-files-min' | translate }} + + + {{ 'gateway.storage-max-files-pattern' | translate }} + + info_outlined + + +
+
+ + gateway.storage-max-read-record-count + + + {{ 'gateway.storage-max-read-record-count-required' | translate }} + + + {{ 'gateway.storage-max-read-record-count-min' | translate }} + + + {{ 'gateway.storage-max-read-record-count-pattern' | translate }} + + info_outlined + + + + gateway.storage-max-file-records + + + {{ 'gateway.storage-max-records-required' | translate }} + + + {{ 'gateway.storage-max-records-min' | translate }} + + + {{ 'gateway.storage-max-records-pattern' | translate }} + + info_outlined + + +
+
+
+
+ + gateway.storage-path + + + {{ 'gateway.storage-path-required' | translate }} + + info_outlined + + + + gateway.messages-ttl-check-in-hours + + + {{ 'gateway.messages-ttl-check-in-hours-required' | translate }} + + + {{ 'gateway.messages-ttl-check-in-hours-min' | translate }} + + + {{ 'gateway.messages-ttl-check-in-hours-pattern' | translate }} + + info_outlined + + +
+ + gateway.messages-ttl-in-days + + + {{ 'gateway.messages-ttl-in-days-required' | translate }} + + + {{ 'gateway.messages-ttl-in-days-min' | translate }} + + + {{ 'gateway.messages-ttl-in-days-pattern' | translate }} + + info_outlined + + +
+
+
+
+
+ +
+
+ + {{ 'gateway.grpc' | translate }} + +
+ + {{ 'gateway.permit-without-calls' | translate }} + +
+
+
+ + gateway.server-port + + info_outlined + + + {{ 'gateway.thingsboard-port-required' | translate }} + + + {{ 'gateway.thingsboard-port-min' | translate }} + + + {{ 'gateway.thingsboard-port-max' | translate }} + + + {{ 'gateway.thingsboard-port-pattern' | translate }} + + + + gateway.grpc-keep-alive-timeout + + info_outlined + + + {{ 'gateway.grpc-keep-alive-timeout-required' | translate }} + + + {{ 'gateway.grpc-keep-alive-timeout-min' | translate }} + + + {{ 'gateway.grpc-keep-alive-timeout-pattern' | translate }} + + +
+
+ + gateway.grpc-keep-alive + + info_outlined + + + {{ 'gateway.grpc-keep-alive-required' | translate }} + + + {{ 'gateway.grpc-keep-alive-min' | translate }} + + + {{ 'gateway.grpc-keep-alive-pattern' | translate }} + + + + gateway.grpc-min-time-between-pings + + info_outlined + + + {{ 'gateway.grpc-min-time-between-pings-required' | translate }} + + + {{ 'gateway.grpc-min-time-between-pings-min' | translate }} + + + {{ 'gateway.grpc-min-time-between-pings-pattern' | translate }} + + +
+
+ + gateway.grpc-max-pings-without-data + + info_outlined + + + {{ 'gateway.grpc-max-pings-without-data-required' | translate }} + + + {{ 'gateway.grpc-max-pings-without-data-min' | translate }} + + + {{ 'gateway.grpc-max-pings-without-data-pattern' | translate }} + + + + gateway.grpc-min-ping-interval-without-data + + info_outlined + + + {{ 'gateway.grpc-min-ping-interval-without-data-required' | translate }} + + + {{ 'gateway.grpc-min-ping-interval-without-data-min' | translate }} + + + {{ 'gateway.grpc-min-ping-interval-without-data-pattern' | translate }} + + +
+
+
+
+
+ +
+
+ + {{ 'gateway.statistics.statistics' | translate }} + + + gateway.statistics.send-period + + + {{ 'gateway.statistics.send-period-required' | translate }} + + + {{ 'gateway.statistics.send-period-min' | translate }} + + + {{ 'gateway.statistics.send-period-pattern' | translate }} + + +
+
+
gateway.statistics.commands
+
gateway.hints.commands
+ +
+
+
+ + gateway.statistics.attribute-name + + + {{ 'gateway.statistics.attribute-name-required' | translate }} + + info_outlined + + + + gateway.statistics.timeout + + + {{ 'gateway.statistics.timeout-required' | translate }} + + + {{ 'gateway.statistics.timeout-min' | translate }} + + + {{ 'gateway.statistics.timeout-pattern' | translate }} + + info_outlined + + +
+ + gateway.statistics.command + + + {{ 'gateway.statistics.command-required' | translate }} + + + {{ 'gateway.statistics.command-pattern' | translate }} + + info_outlined + + +
+ +
+ +
+
+
+
+ +
+
+
+ + {{ 'gateway.checking-device-activity' | translate }} + +
+
+ + gateway.inactivity-timeout-seconds + + + {{ 'gateway.inactivity-timeout-seconds-required' | translate }} + + + {{ 'gateway.inactivity-timeout-seconds-min' | translate }} + + + {{ 'gateway.inactivity-timeout-seconds-pattern' | translate }} + + info_outlined + + + + gateway.inactivity-check-period-seconds + + + {{ 'gateway.inactivity-check-period-seconds-required' | translate }} + + + {{ 'gateway.inactivity-check-period-seconds-min' | translate }} + + + {{ 'gateway.inactivity-check-period-seconds-pattern' | translate }} + + info_outlined + + +
+
+
+
gateway.advanced
+
+ + gateway.min-pack-send-delay + + + {{ 'gateway.min-pack-send-delay-required' | translate }} + + + {{ 'gateway.min-pack-send-delay-min' | translate }} + + info_outlined + + + + gateway.mqtt-qos + + + {{ 'gateway.mqtt-qos-required' | translate }} + + + {{ 'gateway.mqtt-qos-range' | translate }} + + + {{ 'gateway.mqtt-qos-range' | translate }} + + info_outlined + + +
+ + gateway.statistics.check-connectors-configuration + + + {{ 'gateway.statistics.check-connectors-configuration-required' | translate }} + + + {{ 'gateway.statistics.check-connectors-configuration-min' | translate }} + + + {{ 'gateway.statistics.check-connectors-configuration-pattern' | translate }} + + +
+
+
+
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.scss new file mode 100644 index 0000000000..ad4e6cea00 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.scss @@ -0,0 +1,101 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + width: 100%; + height: 100%; + display: grid; + grid-template-rows: min-content minmax(auto, 1fr) min-content; + + .configuration-block { + display: flex; + flex-direction: column; + gap: 16px; + } + + .mat-toolbar { + grid-row: 1; + background: transparent; + color: rgba(0, 0, 0, .87) !important; + } + + .tab-group-block { + min-width: 0; + height: 100%; + min-height: 0; + grid-row: 2; + } + + .toggle-group { + margin-right: auto; + } + + .first-capital { + text-transform: capitalize; + } + + textarea { + resize: none; + } + + .saving-period { + flex: 1; + } + + .statistics-container { + width: 50%; + + .command-container { + width: 100%; + } + } + + .actions { + grid-row: 3; + padding: 8px; + display: flex; + gap: 8px; + justify-content: flex-end; + flex: 1; + } + + mat-form-field { + mat-error { + display: none !important; + } + + mat-error:first-child { + display: block !important; + } + } +} + +:host ::ng-deep { + .pointer-event { + pointer-events: all; + } + + .toggle-group span { + padding: 0 25px; + } + + .mat-mdc-form-field-icon-suffix { + color: #E0E0E0; + &:hover { + color: #9E9E9E; + } + } +} + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.ts new file mode 100644 index 0000000000..1df76f2716 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.ts @@ -0,0 +1,661 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; +import { + FormArray, + FormBuilder, + FormControl, + FormGroup, + ValidationErrors, + ValidatorFn, + Validators +} from '@angular/forms'; +import { EntityId } from '@shared/models/id/entity-id'; +import { MatDialog, MatDialogRef } from '@angular/material/dialog'; +import { AttributeService } from '@core/http/attribute.service'; +import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; +import { + GatewayRemoteConfigurationDialogComponent, + GatewayRemoteConfigurationDialogData +} from '@home/components/widget/lib/gateway/gateway-remote-configuration-dialog'; +import { DeviceService } from '@core/http/device.service'; +import { Observable, of } from 'rxjs'; +import { mergeMap } from 'rxjs/operators'; +import { DeviceCredentials, DeviceCredentialsType } from '@shared/models/device.models'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { + GatewayLogLevel, + GecurityTypesTranslationsMap, + LocalLogsConfigTranslateMap, + LocalLogsConfigs, + LogSavingPeriod, + LogSavingPeriodTranslations, + SecurityTypes, + StorageTypes, + StorageTypesTranslationMap +} from './gateway-widget.models'; +import { deepTrim } from '@core/utils'; + +@Component({ + selector: 'tb-gateway-configuration', + templateUrl: './gateway-configuration.component.html', + styleUrls: ['./gateway-configuration.component.scss'] +}) +export class GatewayConfigurationComponent implements OnInit { + + gatewayConfigGroup: FormGroup; + + StorageTypes = StorageTypes; + storageTypes = Object.values(StorageTypes) as StorageTypes[]; + storageTypesTranslationMap = StorageTypesTranslationMap; + + logSavingPeriods = LogSavingPeriodTranslations; + + localLogsConfigs = Object.keys(LocalLogsConfigs) as LocalLogsConfigs[]; + localLogsConfigTranslateMap = LocalLogsConfigTranslateMap; + + securityTypes = GecurityTypesTranslationsMap; + + gatewayLogLevel = Object.values(GatewayLogLevel); + + @Input() + device: EntityId; + + @Input() + dialogRef: MatDialogRef; + + logSelector: FormControl; + + private initialCredentials: DeviceCredentials; + + constructor(private fb: FormBuilder, + private attributeService: AttributeService, + private deviceService: DeviceService, + private cd: ChangeDetectorRef, + private dialog: MatDialog) { + } + + ngOnInit() { + this.gatewayConfigGroup = this.fb.group({ + thingsboard: this.fb.group({ + host: [window.location.hostname, [Validators.required, Validators.pattern(/^[^\s]+$/)]], + port: [1883, [Validators.required, Validators.min(1), Validators.max(65535), Validators.pattern(/^-?[0-9]+$/)]], + remoteShell: [false, []], + remoteConfiguration: [true, []], + checkConnectorsConfigurationInSeconds: [60, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], + statistics: this.fb.group({ + enable: [true, []], + statsSendPeriodInSeconds: [3600, [Validators.required, Validators.min(60), Validators.pattern(/^-?[0-9]+$/)]], + commands: this.fb.array([], []) + }), + maxPayloadSizeBytes: [1024, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], + minPackSendDelayMS: [200, [Validators.required, Validators.min(0), Validators.pattern(/^-?[0-9]+$/)]], + minPackSizeToSend: [500, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], + handleDeviceRenaming: [true, []], + checkingDeviceActivity: this.fb.group({ + checkDeviceInactivity: [false, []], + inactivityTimeoutSeconds: [200, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], + inactivityCheckPeriodSeconds: [500, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]] + }), + security: this.fb.group({ + type: [SecurityTypes.ACCESS_TOKEN, [Validators.required]], + accessToken: [null, [Validators.required, Validators.pattern(/^[^.\s]+$/)]], + clientId: [null, [Validators.pattern(/^[^.\s]+$/)]], + username: [null, [Validators.pattern(/^[^.\s]+$/)]], + password: [null, [Validators.pattern(/^[^.\s]+$/)]], + caCert: [null, []], + cert: [null, []], + privateKey: [null, []], + }), + qos: [1, [Validators.min(0), Validators.max(1), Validators.required, Validators.pattern(/^[^.\s]+$/)]] + }), + storage: this.fb.group({ + type: [StorageTypes.MEMORY, [Validators.required]], + read_records_count: [100, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required, Validators.pattern(/^[^.\s]+$/)]], + max_records_count: [100000, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required, Validators.pattern(/^[^.\s]+$/)]], + data_folder_path: ['./data/', [Validators.pattern(/^[^\s]+$/)]], + max_file_count: [10, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], + max_read_records_count: [10, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], + max_records_per_file: [10000, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], + data_file_path: ['./data/data.db', [Validators.pattern(/^[^\s]+$/)]], + messages_ttl_check_in_hours: [1, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], + messages_ttl_in_days: [7, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], + + }), + grpc: this.fb.group({ + enabled: [false, []], + serverPort: [9595, [Validators.required, Validators.min(1), Validators.max(65535), Validators.pattern(/^-?[0-9]+$/)]], + keepAliveTimeMs: [10000, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], + keepAliveTimeoutMs: [5000, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], + keepalivePermitWithoutCalls: [true, []], + maxPingsWithoutData: [0, [Validators.required, Validators.min(0), Validators.pattern(/^-?[0-9]+$/)]], + minTimeBetweenPingsMs: [10000, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], + minPingIntervalWithoutDataMs: [5000, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], + }), + connectors: this.fb.array([]), + logs: this.fb.group({ + dateFormat: ['%Y-%m-%d %H:%M:%S', [Validators.required, Validators.pattern(/^[^\s].*[^\s]$/)]], + logFormat: ['%(asctime)s - |%(levelname)s| - [%(filename)s] - %(module)s - %(funcName)s - %(lineno)d - %(message)s', + [Validators.required, Validators.pattern(/^[^\s].*[^\s]$/)]], + type: ['remote', [Validators.required]], + remote: this.fb.group({ + enabled: [false], + logLevel: [GatewayLogLevel.INFO, [Validators.required]], + }), + local: this.fb.group({}) + }) + }); + + this.gatewayConfigGroup.get('thingsboard.security.password').valueChanges.subscribe(password => { + if (password && password !== '') { + this.gatewayConfigGroup.get('thingsboard.security.username').setValidators([Validators.required]); + } else { + this.gatewayConfigGroup.get('thingsboard.security.username').clearValidators(); + } + this.gatewayConfigGroup.get('thingsboard.security.username').updateValueAndValidity({emitEvent: false}); + }); + + this.toggleRpcFields(false); + + this.gatewayConfigGroup.get('thingsboard.remoteConfiguration').valueChanges.subscribe(enabled => { + if (!enabled) { + this.openConfigurationConfirmDialog(); + } + }); + + this.logSelector = this.fb.control(LocalLogsConfigs.service); + + for (const localLogsConfigsKey of Object.keys(LocalLogsConfigs)) { + this.addLocalLogConfig(localLogsConfigsKey, {}); + } + + const checkingDeviceActivityGroup = this.gatewayConfigGroup.get('thingsboard.checkingDeviceActivity') as FormGroup; + checkingDeviceActivityGroup.get('checkDeviceInactivity').valueChanges.subscribe(enabled => { + checkingDeviceActivityGroup.updateValueAndValidity(); + if (enabled) { + checkingDeviceActivityGroup.get('inactivityTimeoutSeconds').setValidators([Validators.min(1), Validators.required, Validators.pattern(/^-?[0-9]+$/)]); + checkingDeviceActivityGroup.get('inactivityCheckPeriodSeconds').setValidators([Validators.min(1), Validators.required, Validators.pattern(/^-?[0-9]+$/)]); + } else { + checkingDeviceActivityGroup.get('inactivityTimeoutSeconds').clearValidators(); + checkingDeviceActivityGroup.get('inactivityCheckPeriodSeconds').clearValidators(); + } + checkingDeviceActivityGroup.get('inactivityTimeoutSeconds').updateValueAndValidity({emitEvent: false}); + checkingDeviceActivityGroup.get('inactivityCheckPeriodSeconds').updateValueAndValidity({emitEvent: false}); + }); + + this.gatewayConfigGroup.get('grpc.enabled').valueChanges.subscribe(value => { + this.toggleRpcFields(value); + }); + + const securityGroup = this.gatewayConfigGroup.get('thingsboard.security') as FormGroup; + securityGroup.get('type').valueChanges.subscribe(type => { + this.removeAllSecurityValidators(); + if (type === SecurityTypes.ACCESS_TOKEN) { + securityGroup.get('accessToken').addValidators([Validators.required, Validators.pattern(/^[^.\s]+$/)]); + securityGroup.get('accessToken').updateValueAndValidity(); + } else if (type === SecurityTypes.TLS_PRIVATE_KEY) { + securityGroup.get('caCert').addValidators([Validators.required]); + securityGroup.get('caCert').updateValueAndValidity(); + securityGroup.get('privateKey').addValidators([Validators.required]); + securityGroup.get('privateKey').updateValueAndValidity(); + securityGroup.get('cert').addValidators([Validators.required]); + securityGroup.get('cert').updateValueAndValidity(); + } else if (type === SecurityTypes.TLS_ACCESS_TOKEN) { + securityGroup.get('accessToken').addValidators([Validators.required, Validators.pattern(/^[^.\s]+$/)]); + securityGroup.get('accessToken').updateValueAndValidity(); + securityGroup.get('caCert').addValidators([Validators.required]); + securityGroup.get('caCert').updateValueAndValidity(); + } else if (type === SecurityTypes.USERNAME_PASSWORD) { + securityGroup.addValidators([this.atLeastOneRequired(Validators.required, ['clientId', 'username'])]); + } + securityGroup.updateValueAndValidity(); + }); + + securityGroup.get('caCert').valueChanges.subscribe(() => this.cd.detectChanges()); + securityGroup.get('privateKey').valueChanges.subscribe(() => this.cd.detectChanges()); + securityGroup.get('cert').valueChanges.subscribe(() => this.cd.detectChanges()); + + const storageGroup = this.gatewayConfigGroup.get('storage') as FormGroup; + storageGroup.get('type').valueChanges.subscribe(type => { + this.removeAllStorageValidators(); + if (type === StorageTypes.MEMORY) { + storageGroup.get('read_records_count').addValidators( + [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]); + storageGroup.get('max_records_count').addValidators( + [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]); + storageGroup.get('read_records_count').updateValueAndValidity({emitEvent: false}); + storageGroup.get('max_records_count').updateValueAndValidity({emitEvent: false}); + } else if (type === StorageTypes.FILE) { + storageGroup.get('data_folder_path').addValidators([Validators.required, Validators.pattern(/^[^.\s]+$/)]); + storageGroup.get('max_file_count').addValidators( + [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]); + storageGroup.get('max_read_records_count').addValidators( + [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]); + storageGroup.get('max_records_per_file').addValidators( + [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]); + storageGroup.get('data_folder_path').updateValueAndValidity({emitEvent: false}); + storageGroup.get('max_file_count').updateValueAndValidity({emitEvent: false}); + storageGroup.get('max_read_records_count').updateValueAndValidity({emitEvent: false}); + storageGroup.get('max_records_per_file').updateValueAndValidity({emitEvent: false}); + } else if (type === StorageTypes.SQLITE) { + storageGroup.get('data_file_path').addValidators([Validators.required, Validators.pattern(/^[^.\s]+$/)]); + storageGroup.get('messages_ttl_check_in_hours').addValidators( + [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]); + storageGroup.get('messages_ttl_in_days').addValidators( + [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]); + storageGroup.get('data_file_path').updateValueAndValidity({emitEvent: false}); + storageGroup.get('messages_ttl_check_in_hours').updateValueAndValidity({emitEvent: false}); + storageGroup.get('messages_ttl_in_days').updateValueAndValidity({emitEvent: false}); + } + }); + + this.fetchConfigAttribute(this.device); + } + + private atLeastOneRequired(validator: ValidatorFn, controls: string[] = null) { + return (group: FormGroup): ValidationErrors | null => { + if (!controls) { + controls = Object.keys(group.controls); + } + const hasAtLeastOne = group?.controls && controls.some(k => !validator(group.controls[k])); + + return hasAtLeastOne ? null : {atLeastOne: true}; + }; + } + + private fetchConfigAttribute(entityId: EntityId) { + if (entityId.id === NULL_UUID) { + return; + } + this.attributeService.getEntityAttributes(entityId, AttributeScope.CLIENT_SCOPE, + ['general_configuration', 'grpc_configuration', 'logs_configuration', 'storage_configuration', 'RemoteLoggingLevel']).pipe( + mergeMap(attributes => attributes.length ? of(attributes) : this.attributeService.getEntityAttributes( + entityId, AttributeScope.SHARED_SCOPE, ['general_configuration', 'grpc_configuration', + 'logs_configuration', 'storage_configuration', 'RemoteLoggingLevel'])) + ).subscribe(attributes => { + if (attributes.length) { + const general_configuration = attributes.find(attribute => attribute.key === 'general_configuration')?.value; + const grpc_configuration = attributes.find(attribute => attribute.key === 'grpc_configuration')?.value; + const logs_configuration = attributes.find(attribute => attribute.key === 'logs_configuration')?.value; + const storage_configuration = attributes.find(attribute => attribute.key === 'storage_configuration')?.value; + const remoteLoggingLevel = attributes.find(attribute => attribute.key === 'RemoteLoggingLevel')?.value; + if (general_configuration) { + const configObj = {thingsboard: general_configuration}; + if (configObj.thingsboard.statistics && configObj.thingsboard.statistics.commands) { + for (const command of configObj.thingsboard.statistics.commands) { + this.addCommand(command); + } + delete configObj.thingsboard.statistics.commands; + } + this.gatewayConfigGroup.patchValue(configObj, {emitEvent: false}); + this.gatewayConfigGroup.markAsPristine(); + if (!configObj.thingsboard.remoteConfiguration) { + this.gatewayConfigGroup.disable({emitEvent: false}); + } + this.checkAndFetchCredentials(configObj.thingsboard.security); + } + if (grpc_configuration) { + const configObj = {grpc: grpc_configuration}; + this.gatewayConfigGroup.patchValue(configObj, {emitEvent: false}); + this.toggleRpcFields(grpc_configuration.enabled); + } + if (logs_configuration) { + const configObj = {logs: this.logsToObj(logs_configuration)}; + this.gatewayConfigGroup.patchValue(configObj, {emitEvent: false}); + this.cd.detectChanges(); + } + if (storage_configuration) { + const configObj = {storage: storage_configuration}; + this.gatewayConfigGroup.patchValue(configObj, {emitEvent: false}); + } + if (remoteLoggingLevel) { + const remoteLogsFormGroup = this.gatewayConfigGroup.get('logs.remote'); + remoteLogsFormGroup.patchValue({ + enabled: remoteLoggingLevel !== GatewayLogLevel.NONE, + logLevel: remoteLoggingLevel + }, {emitEvent: false}); + remoteLogsFormGroup.markAsPristine(); + } + this.cd.detectChanges(); + } else { + this.checkAndFetchCredentials(); + } + }); + } + + private checkAndFetchCredentials(security: any = {}): void { + if (security.type !== SecurityTypes.TLS_PRIVATE_KEY) { + this.deviceService.getDeviceCredentials(this.device.id).subscribe(credentials => { + this.initialCredentials = credentials; + if (credentials.credentialsType === DeviceCredentialsType.ACCESS_TOKEN || security.type === SecurityTypes.TLS_ACCESS_TOKEN) { + this.gatewayConfigGroup.get('thingsboard.security.type').setValue(security.type === SecurityTypes.TLS_ACCESS_TOKEN + ? SecurityTypes.TLS_ACCESS_TOKEN + : SecurityTypes.ACCESS_TOKEN); + this.gatewayConfigGroup.get('thingsboard.security.accessToken').setValue(credentials.credentialsId); + if(security.type === SecurityTypes.TLS_ACCESS_TOKEN) { + this.gatewayConfigGroup.get('thingsboard.security.caCert').setValue(security.caCert); + } + } else if (credentials.credentialsType === DeviceCredentialsType.MQTT_BASIC) { + const parsedValue = JSON.parse(credentials.credentialsValue); + this.gatewayConfigGroup.get('thingsboard.security.type').setValue(SecurityTypes.USERNAME_PASSWORD); + this.gatewayConfigGroup.get('thingsboard.security.clientId').setValue(parsedValue.clientId); + this.gatewayConfigGroup.get('thingsboard.security.username').setValue(parsedValue.userName); + this.gatewayConfigGroup.get('thingsboard.security.password').setValue(parsedValue.password, {emitEvent: false}); + } else if (credentials.credentialsType === DeviceCredentialsType.X509_CERTIFICATE) { + //if sertificate is present set sertificate as present + } + }); + } + } + + private logsToObj(logsConfig: any) { + const logsObject = { + local: {} + }; + const logFormat = logsConfig.formatters.LogFormatter.format; + const dateFormat = logsConfig.formatters.LogFormatter.datefmt; + for (const localLogsConfigsKey of Object.keys(LocalLogsConfigs)) { + const handlerKey = localLogsConfigsKey + 'Handler'; + logsObject[localLogsConfigsKey] = { + logLevel: logsConfig.loggers[localLogsConfigsKey].level, + filePath: logsConfig.handlers[handlerKey].filename.split('/' + localLogsConfigsKey)[0], + backupCount: logsConfig.handlers[handlerKey].backupCount, + savingTime: logsConfig.handlers[handlerKey].interval, + savingPeriod: logsConfig.handlers[handlerKey].when, + }; + } + + + return {local: logsObject, logFormat, dateFormat}; + } + + private toggleRpcFields(enable: boolean) { + const grpcGroup = this.gatewayConfigGroup.get('grpc') as FormGroup; + if (enable) { + grpcGroup.get('serverPort').enable({emitEvent: false}); + grpcGroup.get('keepAliveTimeMs').enable({emitEvent: false}); + grpcGroup.get('keepAliveTimeoutMs').enable({emitEvent: false}); + grpcGroup.get('keepalivePermitWithoutCalls').enable({emitEvent: false}); + grpcGroup.get('maxPingsWithoutData').enable({emitEvent: false}); + grpcGroup.get('minTimeBetweenPingsMs').enable({emitEvent: false}); + grpcGroup.get('minPingIntervalWithoutDataMs').enable({emitEvent: false}); + } else { + grpcGroup.get('serverPort').disable({emitEvent: false}); + grpcGroup.get('keepAliveTimeMs').disable({emitEvent: false}); + grpcGroup.get('keepAliveTimeoutMs').disable({emitEvent: false}); + grpcGroup.get('keepalivePermitWithoutCalls').disable({emitEvent: false}); + grpcGroup.get('maxPingsWithoutData').disable({emitEvent: false}); + grpcGroup.get('minTimeBetweenPingsMs').disable({emitEvent: false}); + grpcGroup.get('minPingIntervalWithoutDataMs').disable({emitEvent: false}); + } + } + + + addCommand(command: any = {}): void { + const commandsFormArray = this.commandFormArray(); + const commandFormGroup = this.fb.group({ + attributeOnGateway: [command.attributeOnGateway || null, [Validators.required, Validators.pattern(/^[^.\s]+$/)]], + command: [command.command || null, [Validators.required, Validators.pattern(/^(?=\S).*\S$/)]], + timeout: [command.timeout || null, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.pattern(/^[^.\s]+$/)]], + }); + commandsFormArray.push(commandFormGroup); + } + + private addLocalLogConfig(name: string, config: any): void { + const localLogsFormGroup = this.gatewayConfigGroup.get('logs.local') as FormGroup; + const configGroup = this.fb.group({ + logLevel: [config.logLevel || GatewayLogLevel.INFO, [Validators.required]], + filePath: [config.filePath || './logs', [Validators.required]], + backupCount: [config.backupCount || 7, [Validators.required, Validators.min(0)]], + savingTime: [config.savingTime || 3, [Validators.required, Validators.min(0)]], + savingPeriod: [config.savingPeriod || LogSavingPeriod.days, [Validators.required]] + }); + localLogsFormGroup.addControl(name, configGroup); + } + + getLogFormGroup(value: string): FormGroup { + return this.gatewayConfigGroup.get(`logs.local.${value}`) as FormGroup; + } + + commandFormArray(): FormArray { + return this.gatewayConfigGroup.get('thingsboard.statistics.commands') as FormArray; + } + + removeCommandControl(index: number, event: any): void { + if (event.pointerType === '') { + return; + } + this.commandFormArray().removeAt(index); + this.gatewayConfigGroup.markAsDirty(); + } + + private removeAllSecurityValidators(): void { + const securityGroup = this.gatewayConfigGroup.get('thingsboard.security') as FormGroup; + securityGroup.clearValidators(); + for (const controlsKey in securityGroup.controls) { + if (controlsKey !== 'type') { + securityGroup.controls[controlsKey].clearValidators(); + securityGroup.controls[controlsKey].setErrors(null); + securityGroup.controls[controlsKey].updateValueAndValidity(); + } + } + } + + private removeAllStorageValidators(): void { + const storageGroup = this.gatewayConfigGroup.get('storage') as FormGroup; + for (const storageKey in storageGroup.controls) { + if (storageKey !== 'type') { + storageGroup.controls[storageKey].clearValidators(); + storageGroup.controls[storageKey].setErrors(null); + storageGroup.controls[storageKey].updateValueAndValidity(); + } + } + } + + private removeEmpty(obj: any) { + return Object.fromEntries( + Object.entries(obj) + .filter(([_, v]) => v != null) + .map(([k, v]) => [k, v === Object(v) ? this.removeEmpty(v) : v]) + ); + } + + private generateLogsFile(logsObj: any) { + const logAttrObj = { + version: 1, + disable_existing_loggers: false, + formatters: { + LogFormatter: { + class: 'logging.Formatter', + format: logsObj.logFormat, + datefmt: logsObj.dateFormat, + } + }, + handlers: { + consoleHandler: { + class: 'logging.StreamHandler', + formatter: 'LogFormatter', + level: 'DEBUG', + stream: 'ext://sys.stdout' + }, + databaseHandler: { + class: 'thingsboard_gateway.tb_utility.tb_handler.TimedRotatingFileHandler', + formatter: 'LogFormatter', + filename: './logs/database.log', + backupCount: 1, + encoding: 'utf-8' + } + }, + loggers: { + database: { + handlers: ['databaseHandler', 'consoleHandler'], + level: 'DEBUG', + propagate: false + } + }, + root: { + level: 'ERROR', + handlers: [ + 'consoleHandler' + ] + }, + ts: new Date().getTime() + }; + for (const key of Object.keys(logsObj.local)) { + logAttrObj.handlers[key + 'Handler'] = this.createHandlerObj(logsObj.local[key], key); + logAttrObj.loggers[key] = this.createLoggerObj(logsObj.local[key], key); + } + return logAttrObj; + } + + private createHandlerObj(logObj: any, key: string) { + return { + class: 'thingsboard_gateway.tb_utility.tb_handler.TimedRotatingFileHandler', + formatter: 'LogFormatter', + filename: `${logObj.filePath}/${key}.log`, + backupCount: logObj.backupCount, + interval: logObj.savingTime, + when: logObj.savingPeriod, + encoding: 'utf-8' + }; + } + + private createLoggerObj(logObj: any, key: string) { + return { + handlers: [`${key}Handler`, 'consoleHandler'], + level: logObj.logLevel, + propagate: false + }; + } + + saveConfig(): void { + const value = deepTrim(this.removeEmpty(this.gatewayConfigGroup.value)); + value.thingsboard.statistics.commands = Object.values(value.thingsboard.statistics.commands); + const attributes = []; + attributes.push({ + key: 'RemoteLoggingLevel', + value: value.logs.remote.enabled ? value.logs.remote.logLevel : GatewayLogLevel.NONE + }); + delete value.connectors; + attributes.push({ + key: 'logs_configuration', + value: this.generateLogsFile(value.logs) + }); + value.grpc.ts = new Date().getTime(); + attributes.push({ + key: 'grpc_configuration', + value: value.grpc + }); + value.storage.ts = new Date().getTime(); + attributes.push({ + key: 'storage_configuration', + value: value.storage + }); + value.thingsboard.ts = new Date().getTime(); + attributes.push({ + key: 'general_configuration', + value: value.thingsboard + }); + + + this.attributeService.saveEntityAttributes(this.device, AttributeScope.SHARED_SCOPE, attributes).subscribe(_ => { + this.updateCredentials(value.thingsboard.security).subscribe(() => { + if (this.dialogRef) { + this.dialogRef.close(); + } else { + this.gatewayConfigGroup.markAsPristine(); + this.cd.detectChanges(); + } + }); + }); + } + + private updateCredentials(securityConfig: any): Observable { + let updateCredentials = false; + let newCredentials = {}; + if (securityConfig.type === SecurityTypes.USERNAME_PASSWORD) { + if (this.initialCredentials.credentialsType !== DeviceCredentialsType.MQTT_BASIC) { + updateCredentials = true; + } else { + const parsedCredentials = JSON.parse(this.initialCredentials.credentialsValue); + updateCredentials = !( + parsedCredentials.clientId === securityConfig.clientId && + parsedCredentials.userName === securityConfig.username && + parsedCredentials.password === securityConfig.password); + } + if (updateCredentials) { + const credentialsValue: { clientId?: string; userName?: string; password?: string } = {}; + const credentialsType = DeviceCredentialsType.MQTT_BASIC; + if (securityConfig.clientId) { + credentialsValue.clientId = securityConfig.clientId; + } + if (securityConfig.username) { + credentialsValue.userName = securityConfig.username; + } + if (securityConfig.password) { + credentialsValue.password = securityConfig.password; + } + newCredentials = { + credentialsType, + credentialsValue: JSON.stringify(credentialsValue) + }; + } + } else if (securityConfig.type === SecurityTypes.ACCESS_TOKEN || securityConfig.type === SecurityTypes.TLS_ACCESS_TOKEN) { + if (this.initialCredentials.credentialsType !== DeviceCredentialsType.ACCESS_TOKEN) { + updateCredentials = true; + } else { + updateCredentials = this.initialCredentials.credentialsId !== securityConfig.accessToken; + } + if (updateCredentials) { + newCredentials = { + credentialsType: DeviceCredentialsType.ACCESS_TOKEN, + credentialsId: securityConfig.accessToken + }; + } + } + + if (updateCredentials) { + return this.deviceService.saveDeviceCredentials({...this.initialCredentials,...newCredentials}); + } + return of(null); + } + + cancel(): void { + if (this.dialogRef) { + this.dialogRef.close(); + } + } + + private openConfigurationConfirmDialog(): void { + this.deviceService.getDevice(this.device.id).subscribe(gateway => { + this.dialog.open + (GatewayRemoteConfigurationDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + gatewayName: gateway.name + } + }).afterClosed().subscribe( + (res) => { + if (!res) { + this.gatewayConfigGroup.get('thingsboard.remoteConfiguration').setValue(true, {emitEvent: false}); + } + } + ); + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.html new file mode 100644 index 0000000000..0c21261091 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.html @@ -0,0 +1,193 @@ + +
+
+ +

{{ 'gateway.connectors' | translate }}

+ + +
+
+ + + + {{ 'gateway.connectors-table-enabled' | translate }} + + + + + + + + {{ 'gateway.connectors-table-name' | translate }} + + {{ attribute.key }} + + + + + {{ 'gateway.connectors-table-type' | translate }} + + + {{ returnType(attribute) }} + + + + + {{ 'gateway.configuration' | translate }} + + +
+ {{ isConnectorSynced(attribute) ? 'sync' : 'out of sync' }} +
+
+
+ + + {{ 'gateway.connectors-table-status' | translate }} + + + + + + + + {{ 'gateway.connectors-table-actions' | translate }} + + +
+ + + +
+
+ + + + + + +
+
+
+ + +
+
+
+
+
+
+ + {{ 'gateway.connectors-table-name' | translate }} + + + + {{ 'gateway.connectors-table-type' | translate }} + + {{ type.value }} + + + + {{ 'gateway.connectors-table-key' | translate }} + + + + {{ 'gateway.connectors-table-class' | translate }} + + + + gateway.remote-logging-level + + {{ logLevel }} + + +
+
+
+ + +
+ +
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.scss new file mode 100644 index 0000000000..8de99ac9f9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.scss @@ -0,0 +1,104 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import '../../../../../../../scss/constants'; + +:host { + width: 100%; + height: 100%; + display: block; + overflow-x: auto; + padding: 0; + + .connector-container { + height: 100%; + width: 100%; + flex-direction: row; + @media #{$mat-lt-lg} { + flex-direction: column; + } + + .table-section { + min-height: 35vh; + overflow: hidden; + + .table-container { + overflow: auto; + } + } + + .flex { + flex: 1; + } + + .input-container { + height: auto; + } + + .section-container { + background-color: #fff; + } + } + + .mat-toolbar { + background: transparent; + color: rgba(0, 0, 0, .87) !important; + } + + .mat-mdc-slide-toggle { + margin: 0 8px; + } + + .status { + text-align: center; + border-radius: 16px; + font-weight: 500; + width: fit-content; + padding: 5px 15px; + &-sync { + background: rgba(25, 128, 56, .06); + color: rgb(25, 128, 56); + } + &-unsync { + background: rgba(203, 37, 48, .06); + color: rgb(203, 37, 48); + } + } + + mat-row { + cursor: pointer; + } + + .dot { + height: 12px; + width: 12px; + background-color: #bbb; + border-radius: 50%; + display: inline-block; + } + + .hasErrors { + background-color: rgb(203, 37, 48); + } + + .noErrors { + background-color: rgb(25, 128, 56); + } +} + +:host ::ng-deep tb-json-object-edit > div { + flex-grow: 1; +} + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts new file mode 100644 index 0000000000..6f1fc90f5e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts @@ -0,0 +1,506 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, Input, NgZone, ViewChild } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { FormBuilder, FormGroup, UntypedFormControl, ValidatorFn, Validators } from '@angular/forms'; +import { EntityId } from '@shared/models/id/entity-id'; +import { AttributeService } from '@core/http/attribute.service'; +import { TranslateService } from '@ngx-translate/core'; +import { forkJoin, Observable } from 'rxjs'; +import { AttributeData, AttributeScope } from '@shared/models/telemetry/telemetry.models'; +import { PageComponent } from '@shared/components/page.component'; +import { PageLink } from '@shared/models/page/page-link'; +import { AttributeDatasource } from '@home/models/datasource/attribute-datasource'; +import { Direction, SortOrder } from '@shared/models/page/sort-order'; +import { MatSort } from '@angular/material/sort'; +import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service'; +import { MatTableDataSource } from '@angular/material/table'; +import { ActionNotificationShow } from '@core/notification/notification.actions'; +import { DialogService } from '@core/services/dialog.service'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { camelCase, deepClone, isString } from '@core/utils'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; +import { DatasourceType, widgetType } from '@shared/models/widget.models'; +import { UtilsService } from '@core/services/utils.service'; +import { EntityType } from '@shared/models/entity-type.models'; +import { + GatewayConnector, + GatewayConnectorDefaultTypesTranslates, + GatewayLogLevel +} from './gateway-widget.models'; + +@Component({ + selector: 'tb-gateway-connector', + templateUrl: './gateway-connectors.component.html', + styleUrls: ['./gateway-connectors.component.scss'] +}) +export class GatewayConnectorComponent extends PageComponent implements AfterViewInit { + + pageLink: PageLink; + + dataSource: MatTableDataSource; + + displayedColumns = ['enabled', 'key', 'type', 'syncStatus', 'errors', 'actions']; + + gatewayConnectorDefaultTypes = GatewayConnectorDefaultTypesTranslates; + + @Input() + ctx: WidgetContext; + + @Input() + device: EntityId; + + @ViewChild('nameInput') nameInput: ElementRef; + @ViewChild(MatSort, {static: false}) sort: MatSort; + + connectorForm: FormGroup; + + textSearchMode: boolean; + + activeConnectors: Array; + + gatewayLogLevel = Object.values(GatewayLogLevel); + + private inactiveConnectors: Array; + + private attributeDataSource: AttributeDatasource; + + private inactiveConnectorsDataSource: AttributeDatasource; + + private serverDataSource: AttributeDatasource; + + private activeData: Array = []; + + private inactiveData: Array = []; + + private sharedAttributeData: Array = []; + + private initialConnector: GatewayConnector; + + private subscriptionOptions: WidgetSubscriptionOptions = { + callbacks: { + onDataUpdated: () => this.ctx.ngZone.run(() => { + this.onDataUpdated(); + }), + onDataUpdateError: (subscription, e) => this.ctx.ngZone.run(() => { + this.onDataUpdateError(e); + }) + } + }; + + private subscription: IWidgetSubscription; + + constructor(protected store: Store, + private fb: FormBuilder, + private translate: TranslateService, + private attributeService: AttributeService, + private dialogService: DialogService, + private telemetryWsService: TelemetryWebsocketService, + private zone: NgZone, + private utils: UtilsService, + private cd: ChangeDetectorRef) { + super(store); + const sortOrder: SortOrder = {property: 'key', direction: Direction.ASC}; + this.pageLink = new PageLink(1000, 0, null, sortOrder); + this.attributeDataSource = new AttributeDatasource(this.attributeService, this.telemetryWsService, this.zone, this.translate); + this.inactiveConnectorsDataSource = new AttributeDatasource(this.attributeService, this.telemetryWsService, this.zone, this.translate); + this.serverDataSource = new AttributeDatasource(this.attributeService, this.telemetryWsService, this.zone, this.translate); + this.dataSource = new MatTableDataSource([]); + this.connectorForm = this.fb.group({ + name: ['', [Validators.required, this.uniqNameRequired()]], + type: ['', [Validators.required]], + logLevel: ['', [Validators.required]], + key: ['auto'], + class: [''], + configuration: [''], + configurationJson: [{}, [Validators.required]] + }); + this.connectorForm.disable(); + } + + ngAfterViewInit() { + this.connectorForm.valueChanges.subscribe(() => { + this.cd.detectChanges(); + }); + + this.connectorForm.get('type').valueChanges.subscribe(type=> { + if(type && !this.initialConnector) { + this.attributeService.getEntityAttributes(this.device, AttributeScope.CLIENT_SCOPE, + [`${type.toUpperCase()}_DEFAULT_CONFIG`], {ignoreErrors: true}).subscribe(defaultConfig=>{ + if (defaultConfig && defaultConfig.length) { + this.connectorForm.get('configurationJson').setValue( + isString(defaultConfig[0].value) ? + JSON.parse(defaultConfig[0].value) : + defaultConfig[0].value); + this.cd.detectChanges(); + } + }) + } + }); + + this.dataSource.sort = this.sort; + this.dataSource.sortingDataAccessor = (data: AttributeData, sortHeaderId: string) => { + if (sortHeaderId === 'syncStatus') { + return this.isConnectorSynced(data) ? 1 : 0; + } else if (sortHeaderId === 'enabled') { + return this.activeConnectors.includes(data.key) ? 1 : 0; + } + return data[sortHeaderId] || data.value[sortHeaderId]; + }; + + if (this.device) { + if (this.device.id === NULL_UUID) { + return; + } + forkJoin([ + this.attributeService.getEntityAttributes(this.device, AttributeScope.SHARED_SCOPE, ['active_connectors']), + this.attributeService.getEntityAttributes(this.device, AttributeScope.SERVER_SCOPE, ['inactive_connectors']) + ]).subscribe(attributes => { + if (attributes.length) { + this.activeConnectors = attributes[0].length ? attributes[0][0].value : []; + this.activeConnectors = isString(this.activeConnectors) ? JSON.parse(this.activeConnectors as any) : this.activeConnectors; + this.inactiveConnectors = attributes[1].length ? attributes[1][0].value : []; + this.inactiveConnectors = isString(this.inactiveConnectors) + ? JSON.parse(this.inactiveConnectors as any) + : this.inactiveConnectors; + this.updateData(true); + } else { + this.activeConnectors = []; + this.inactiveConnectors = []; + this.updateData(true); + } + }); + } + } + + private uniqNameRequired(): ValidatorFn { + return (c: UntypedFormControl) => { + const newName = c.value.trim().toLowerCase(); + const found = this.dataSource.data.find((connectorAttr) => { + const connectorData = connectorAttr.value; + return connectorData.name.toLowerCase() === newName; + }); + if (found) { + if (this.initialConnector && this.initialConnector.name.toLowerCase() === newName) { + return null; + } + return { + duplicateName: { + valid: false + } + }; + } + return null; + }; + } + + saveConnector(): void { + const value = this.connectorForm.value; + value.configuration = camelCase(value.name) + '.json'; + if (value.type !== 'grpc') { + delete value.key; + } + if (value.type !== 'custom') { + delete value.class; + } + value.ts = new Date().getTime(); + const attributesToSave = [{ + key: value.name, + value + }]; + const attributesToDelete = []; + const scope = (!this.initialConnector || this.activeConnectors.includes(this.initialConnector.name)) + ? AttributeScope.SHARED_SCOPE + : AttributeScope.SERVER_SCOPE; + let updateActiveConnectors = false; + if (this.initialConnector && this.initialConnector.name !== value.name) { + attributesToDelete.push({key: this.initialConnector.name}); + updateActiveConnectors = true; + const activeIndex = this.activeConnectors.indexOf(this.initialConnector.name); + const inactiveIndex = this.inactiveConnectors.indexOf(this.initialConnector.name); + if (activeIndex !== -1) { + this.activeConnectors.splice(activeIndex, 1); + } + if (inactiveIndex !== -1) { + this.inactiveConnectors.splice(inactiveIndex, 1); + } + } + if (!this.activeConnectors.includes(value.name) && scope === AttributeScope.SHARED_SCOPE) { + this.activeConnectors.push(value.name); + updateActiveConnectors = true; + } + if (!this.inactiveConnectors.includes(value.name) && scope === AttributeScope.SERVER_SCOPE) { + this.inactiveConnectors.push(value.name); + updateActiveConnectors = true; + } + const tasks = [this.attributeService.saveEntityAttributes(this.device, scope, attributesToSave)]; + if (updateActiveConnectors) { + tasks.push(this.attributeService.saveEntityAttributes(this.device, scope, [{ + key: scope === AttributeScope.SHARED_SCOPE ? 'active_connectors' : 'inactive_connectors', + value: scope === AttributeScope.SHARED_SCOPE ? this.activeConnectors : this.inactiveConnectors + }])); + } + + if (attributesToDelete.length) { + tasks.push(this.attributeService.deleteEntityAttributes(this.device, scope, attributesToDelete)); + } + forkJoin(tasks).subscribe(_ => { + this.initialConnector = value; + this.showToast('Update Successful'); + this.updateData(true); + }); + } + + private updateData(reload: boolean = false) { + this.pageLink.sortOrder.property = this.sort.active; + this.pageLink.sortOrder.direction = Direction[this.sort.direction.toUpperCase()]; + this.attributeDataSource.loadAttributes(this.device, AttributeScope.CLIENT_SCOPE, this.pageLink, reload).subscribe(data => { + this.activeData = data.data.filter(value => this.activeConnectors.includes(value.key)); + this.combineData(); + this.generateSubscription(); + }); + this.inactiveConnectorsDataSource.loadAttributes(this.device, AttributeScope.SHARED_SCOPE, this.pageLink, reload).subscribe(data => { + this.sharedAttributeData = data.data.filter(value => this.activeConnectors.includes(value.key)); + this.combineData(); + }); + this.serverDataSource.loadAttributes(this.device, AttributeScope.SERVER_SCOPE, this.pageLink, reload).subscribe(data => { + this.inactiveData = data.data.filter(value => this.inactiveConnectors.includes(value.key)); + this.combineData(); + }); + } + + isConnectorSynced(attribute: AttributeData) { + const connectorData = attribute.value; + if (!connectorData.ts) { + return false; + } + const clientIndex = this.activeData.findIndex(data => { + const sharedData = data.value; + return sharedData.name === connectorData.name; + }); + if (clientIndex === -1) { + return false; + } + const sharedIndex = this.sharedAttributeData.findIndex(data => { + const sharedData = data.value; + return sharedData.name === connectorData.name && sharedData.ts && sharedData.ts <= connectorData.ts; + }); + return sharedIndex !== -1; + } + + private combineData() { + this.dataSource.data = [...this.activeData, ...this.inactiveData, ...this.sharedAttributeData].filter((item, index, self) => + index === self.findIndex((t) => t.key === item.key) + ).map(attribute => { + attribute.value = typeof attribute.value === 'string' ? JSON.parse(attribute.value) : attribute.value; + return attribute; + }); + } + + addAttribute(): void { + if (this.connectorForm.disabled) { + this.connectorForm.enable(); + } + this.nameInput.nativeElement.focus(); + this.clearOutConnectorForm(); + } + + private clearOutConnectorForm(): void { + this.initialConnector = null; + this.connectorForm.setValue({ + name: '', + type: 'mqtt', + logLevel: GatewayLogLevel.INFO, + key: 'auto', + class: '', + configuration: '', + configurationJson: {} + }); + this.connectorForm.markAsPristine(); + } + + selectConnector(attribute: AttributeData): void { + if (this.connectorForm.disabled) { + this.connectorForm.enable(); + } + const connector = attribute.value; + if (!connector.configuration) { + connector.configuration = ''; + } + if (!connector.key) { + connector.key = 'auto'; + } + this.initialConnector = connector; + this.connectorForm.patchValue(connector); + this.connectorForm.markAsPristine(); + } + + isSameConnector(attribute: AttributeData): boolean { + if (!this.initialConnector) { + return false; + } + const connector = attribute.value; + return this.initialConnector.name === connector.name; + } + + showToast(message: string) { + this.store.dispatch(new ActionNotificationShow( + { + message, + type: 'success', + duration: 1000, + verticalPosition: 'top', + horizontalPosition: 'right', + target: 'dashboardRoot', + forceDismiss: true + })); + } + + returnType(attribute: AttributeData): string { + const value = attribute.value; + return this.gatewayConnectorDefaultTypes.get(value.type); + } + + deleteConnector(attribute: AttributeData, $event: Event): void { + if ($event) { + $event.stopPropagation(); + } + const title = `Delete connector ${attribute.key}?`; + const content = `All connector data will be deleted.`; + this.dialogService.confirm(title, content, 'Cancel', 'Delete').subscribe(result => { + if (result) { + const tasks: Array> = []; + const scope = (this.initialConnector && this.activeConnectors.includes(this.initialConnector.name)) + ? AttributeScope.SHARED_SCOPE + : AttributeScope.SERVER_SCOPE; + tasks.push(this.attributeService.deleteEntityAttributes(this.device, scope, [attribute])); + const activeIndex = this.activeConnectors.indexOf(attribute.key); + const inactiveIndex = this.inactiveConnectors.indexOf(attribute.key); + if (activeIndex !== -1) { + this.activeConnectors.splice(activeIndex, 1); + } + if (inactiveIndex !== -1) { + this.inactiveConnectors.splice(inactiveIndex, 1); + } + tasks.push(this.attributeService.saveEntityAttributes(this.device, scope, [{ + key: scope === AttributeScope.SHARED_SCOPE ? 'active_connectors' : 'inactive_connectors', + value: scope === AttributeScope.SHARED_SCOPE ? this.activeConnectors : this.inactiveConnectors + }])); + forkJoin(tasks).subscribe(() => { + if (this.initialConnector ? this.initialConnector.name === attribute.key : true) { + this.clearOutConnectorForm(); + this.cd.detectChanges(); + this.connectorForm.disable(); + } + this.updateData(true); + }); + } + }); + } + + connectorLogs(attribute: AttributeData, $event: Event): void { + if ($event) { + $event.stopPropagation(); + } + const params = deepClone(this.ctx.stateController.getStateParams()); + params.connector_logs = attribute; + params.targetEntityParamName = 'connector_logs'; + this.ctx.stateController.openState('connector_logs', params); + } + + connectorRpc(attribute: AttributeData, $event: Event): void { + if ($event) { + $event.stopPropagation(); + } + const params = deepClone(this.ctx.stateController.getStateParams()); + params.connector_rpc = attribute; + params.targetEntityParamName = 'connector_rpc'; + this.ctx.stateController.openState('connector_rpc', params); + } + + + enableConnector(attribute: AttributeData): void { + const wasEnabled = this.activeConnectors.includes(attribute.key); + const scopeOld = wasEnabled ? AttributeScope.SHARED_SCOPE : AttributeScope.SERVER_SCOPE; + const scopeNew = !wasEnabled ? AttributeScope.SHARED_SCOPE : AttributeScope.SERVER_SCOPE; + attribute.value.ts = new Date().getTime(); + const tasks = [this.attributeService.saveEntityAttributes(this.device, AttributeScope.SHARED_SCOPE, [{ + key: 'active_connectors', + value: this.activeConnectors + }]), this.attributeService.saveEntityAttributes(this.device, AttributeScope.SERVER_SCOPE, [{ + key: 'inactive_connectors', + value: this.inactiveConnectors + }]), this.attributeService.deleteEntityAttributes(this.device, scopeOld, [attribute]), + this.attributeService.saveEntityAttributes(this.device, scopeNew, [attribute])]; + if (wasEnabled) { + const index = this.activeConnectors.indexOf(attribute.key); + this.activeConnectors.splice(index, 1); + this.inactiveConnectors.push(attribute.key); + } else { + const index = this.inactiveConnectors.indexOf(attribute.key); + this.inactiveConnectors.splice(index, 1); + this.activeConnectors.push(attribute.key); + } + forkJoin(tasks).subscribe(_ => { + this.updateData(true); + }); + } + + private onDataUpdateError(e: any) { + const exceptionData = this.utils.parseException(e); + let errorText = exceptionData.name; + if (exceptionData.message) { + errorText += ': ' + exceptionData.message; + } + console.error(errorText); + } + + private onDataUpdated() { + this.cd.detectChanges(); + } + + private generateSubscription() { + if (this.subscription) { + this.subscription.unsubscribe(); + } + if (this.device) { + const subscriptionInfo = [{ + type: DatasourceType.entity, + entityType: EntityType.DEVICE, + entityId: this.device.id, + entityName: 'Gateway', + timeseries: [] + }]; + this.dataSource.data.forEach(value => { + subscriptionInfo[0].timeseries.push({name: `${value.key}_ERRORS_COUNT`, label: `${value.key}_ERRORS_COUNT`}); + }); + this.ctx.subscriptionApi.createSubscriptionFromInfo(widgetType.latest, subscriptionInfo, this.subscriptionOptions, + false, true).subscribe(subscription => { + this.subscription = subscription; + }); + } + } + + getErrorsCount(attribute: AttributeData): string { + const connectorName = attribute.key; + const connector = this.subscription && this.subscription.data + .find(data => data && data.dataKey.name === `${connectorName}_ERRORS_COUNT`); + return (connector && this.activeConnectors.includes(connectorName)) ? (connector.data[0][1] || 0) : 'Inactive'; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.html index aa75aa8d1e..c089ea9816 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.html @@ -187,7 +187,7 @@ -
{{ 'gateway.connectors' | translate | uppercase }}
+
{{ 'gateway.connectors-config' | translate | uppercase }}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.ts index 9840db6efe..6980aca106 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.ts @@ -57,7 +57,7 @@ import { AttributeService } from '@core/http/attribute.service'; import { AttributeData, AttributeScope } from '@shared/models/telemetry/telemetry.models'; import { forkJoin, Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; -import { ImportExportService } from '@home/components/import-export/import-export.service'; +import { ImportExportService } from '@shared/import-export/import-export.service'; import { ResizeObserver } from '@juggle/resize-observer'; // @dynamic @@ -286,6 +286,7 @@ export class GatewayFormComponent extends PageComponent implements OnInit, OnDes panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { jsonValue: config, + required: true, title: this.translate.instant('gateway.title-connectors-json', {typeName: type}) } }).afterClosed().subscribe( diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html new file mode 100644 index 0000000000..e195dcece9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html @@ -0,0 +1,57 @@ + + + + + + {{ 'widgets.gateway.created-time' | translate }} + + {{ attribute.ts | date:'yyyy-MM-dd HH:mm:ss' }} + + + + {{ 'widgets.gateway.status' | translate }} + + {{ attribute.status }} + + + + {{ 'widgets.gateway.message' | translate }} + + {{ attribute.message }} + + + + +
+{{ 'attribute.no-telemetry-text' | translate }} + + + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.scss new file mode 100644 index 0000000000..8aa334b819 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.scss @@ -0,0 +1,56 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + overflow-x: auto; + padding: 0; + + + .status { + border-radius: 20px; + font-weight: 500; + padding: 5px 15px; + } + + .status-debug { + color: green; + background: rgba(0, 128, 0, 0.1); + } + + .status-warning { + color: orange; + background: rgba(255, 165, 0, 0.1); + } + + .status-error { + color: red; + background: rgba(255, 0, 0, 0.1); + } + + .status-info { + color: blue; + background: rgba(0, 0, 128, 0.1); + } + + .msg-status-exception { + color: red; + } + +} + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.ts new file mode 100644 index 0000000000..721cea4ae6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.ts @@ -0,0 +1,182 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { AfterViewInit, Component, ElementRef, Input, ViewChild } from '@angular/core'; +import { MatDialogRef } from '@angular/material/dialog'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { PageLink } from '@shared/models/page/page-link'; +import { Direction, SortOrder } from '@shared/models/page/sort-order'; +import { MatSort } from '@angular/material/sort'; +import { MatTableDataSource } from '@angular/material/table'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { MatPaginator } from '@angular/material/paginator'; +import { GatewayLogData, GatewayStatus, LogLink } from './gateway-widget.models'; + +@Component({ + selector: 'tb-gateway-logs', + templateUrl: './gateway-logs.component.html', + styleUrls: ['./gateway-logs.component.scss'] +}) +export class GatewayLogsComponent implements AfterViewInit { + + pageLink: PageLink; + + dataSource: MatTableDataSource; + + displayedColumns = ['ts', 'status', 'message']; + + @Input() + ctx: WidgetContext; + + @Input() + dialogRef: MatDialogRef; + + @ViewChild('searchInput') searchInputField: ElementRef; + @ViewChild(MatSort) sort: MatSort; + @ViewChild(MatPaginator) paginator: MatPaginator; + + textSearchMode: boolean; + + logLinks: Array; + + activeLink: LogLink; + + gatewayLogLinks: Array = [ + { + name: 'General', + key: 'LOGS' + }, { + name: 'Service', + key: 'SERVICE_LOGS' + }, + { + name: 'Connection', + key: 'CONNECTION_LOGS' + }, { + name: 'Storage', + key: 'STORAGE_LOGS' + }, + { + key: 'EXTENSIONS_LOGS', + name: 'Extension' + }]; + + + constructor() { + const sortOrder: SortOrder = {property: 'ts', direction: Direction.DESC}; + this.pageLink = new PageLink(10, 0, null, sortOrder); + this.dataSource = new MatTableDataSource([]); + } + + ngAfterViewInit() { + this.dataSource.sort = this.sort; + this.dataSource.paginator = this.paginator; + this.ctx.defaultSubscription.onTimewindowChangeFunction = timewindow => { + this.ctx.defaultSubscription.options.timeWindowConfig = timewindow; + this.ctx.defaultSubscription.updateDataSubscriptions(); + return timewindow; + }; + if (this.ctx.settings.isConnectorLog && this.ctx.settings.connectorLogState) { + const connector = this.ctx.stateController.getStateParams()[this.ctx.settings.connectorLogState]; + this.logLinks = [{ + key: `${connector.key}_LOGS`, + name: 'Connector', + filterFn: (attrData) => !attrData.message.includes(`_converter.py`) + }, { + key: `${connector.key}_LOGS`, + name: 'Converter', + filterFn: (attrData) => attrData.message.includes(`_converter.py`) + }]; + } else { + this.logLinks = this.gatewayLogLinks; + } + this.activeLink = this.logLinks[0]; + this.changeSubscription(); + } + + + private updateData() { + if (this.ctx.defaultSubscription.data.length && this.ctx.defaultSubscription.data[0]) { + let attrData = this.ctx.defaultSubscription.data[0].data.map(data => { + const result = { + ts: data[0], + key: this.activeLink.key, + message: data[1], + status: 'INVALID LOG FORMAT' as GatewayStatus + }; + + try { + result.message = /\[(.*)/.exec(data[1])[0]; + } catch (e) { + result.message = data[1]; + } + + try { + result.status = data[1].match(/\|(\w+)\|/)[1]; + } catch (e) { + result.status = 'INVALID LOG FORMAT' as GatewayStatus; + } + + return result; + }); + if (this.activeLink.filterFn) { + attrData = attrData.filter(data => this.activeLink.filterFn(data)); + } + this.dataSource.data = attrData; + } + } + + onTabChanged(link: LogLink) { + this.activeLink = link; + this.changeSubscription(); + } + + statusClass(status: GatewayStatus): string { + switch (status) { + case GatewayStatus.DEBUG: + return 'status status-debug'; + case GatewayStatus.WARNING: + return 'status status-warning'; + case GatewayStatus.ERROR: + case GatewayStatus.EXCEPTION: + return 'status status-error'; + default: + return 'status status-info'; + } + } + + statusClassMsg(status?: GatewayStatus): string { + if (status === GatewayStatus.EXCEPTION) { + return 'msg-status-exception'; + } + } + + private changeSubscription() { + if (this.ctx.datasources && this.ctx.datasources[0].entity && this.ctx.defaultSubscription.options.datasources) { + this.ctx.defaultSubscription.options.datasources[0].dataKeys = [{ + name: this.activeLink.key, + type: DataKeyType.timeseries, + settings: {} + }]; + this.ctx.defaultSubscription.unsubscribe(); + this.ctx.defaultSubscription.updateDataSubscriptions(); + this.ctx.defaultSubscription.callbacks.onDataUpdated = () => { + this.updateData(); + }; + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.html new file mode 100644 index 0000000000..adb7d0b197 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.html @@ -0,0 +1,52 @@ + + + warning +

gateway.configuration-delete-dialog-header

+ + +
+
+ + + gateway.configuration-delete-dialog-input + + + {{ 'gateway.configuration-delete-dialog-input-required' | translate }} + + +
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.ts new file mode 100644 index 0000000000..68c19d83a5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.ts @@ -0,0 +1,57 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { DialogComponent } from '@shared/components/dialog.component'; +import { Router } from '@angular/router'; +import { FormBuilder, FormControl } from '@angular/forms'; + +export interface GatewayRemoteConfigurationDialogData { + gatewayName: string; +} + +@Component({ + selector: 'gateway-remote-configuration-dialog', + templateUrl: './gateway-remote-configuration-dialog.html' +}) + +export class GatewayRemoteConfigurationDialogComponent extends + DialogComponent{ + + gatewayName: string; + gatewayControl: FormControl; + + constructor(protected store: Store, + protected router: Router, + @Inject(MAT_DIALOG_DATA) public data: GatewayRemoteConfigurationDialogData, + public dialogRef: MatDialogRef, + private fb: FormBuilder) { + super(store, router, dialogRef); + this.gatewayName = this.data.gatewayName; + this.gatewayControl = this.fb.control(''); + } + + close(): void { + this.dialogRef.close(); + } + + turnOff(): void { + this.dialogRef.close(true); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.html new file mode 100644 index 0000000000..8a96f1849b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.html @@ -0,0 +1,65 @@ + +
+ + + {{ 'gateway.statistics.command' | translate }} + + + {{ command }} + + + + + {{ 'gateway.statistics.timeout-ms' | translate }} + + + {{ 'gateway.statistics.timeout-min' | translate }} + + + + + + {{ 'gateway.statistics.command' | translate }} + + + + {{ 'widget-config.datasource-parameters' | translate }} + + edit + + + + +
+
+ {{ 'gateway.rpc-command-result' | translate }} +
schedule + {{ resultTime | date: 'yyyy/MM/dd HH:mm:ss' }}
+ +
+ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.scss new file mode 100644 index 0000000000..5dab1d91c1 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.scss @@ -0,0 +1,73 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + width: 100%; + height: 100%; + overflow-x: auto; + display: flex; + flex-direction: column; + padding: 0; + + .command-form { + flex-wrap: nowrap; + padding: 0 5px 5px; + + & > button { + margin-top: 10px; + } + } + + .result-block { + padding: 0 5px; + display: flex; + flex-direction: column; + flex: 1; + + & > span { + font-weight: 600; + position: relative; + font-size: 14px; + margin-bottom: 10px; + + + .result-time { + font-weight: 400; + font-size: 14px; + line-height: 32px; + position: absolute; + left: 0; + top: 25px; + z-index: 5; + color: rgba(0, 0, 0, 0.54); + + span { + padding-left: 10px; + } + } + } + + tb-json-content { + flex: 1; + } + } +} + +:host ::ng-deep { + .tb-json-content { + height: 100%; + } +} + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.ts new file mode 100644 index 0000000000..d19624e257 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.ts @@ -0,0 +1,116 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { AfterViewInit, Component, Input } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { MatDialog, MatDialogRef } from '@angular/material/dialog'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { ContentType } from '@shared/models/constants'; +import { + JsonObjectEditDialogComponent, + JsonObjectEditDialogData +} from '@shared/components/dialog/json-object-edit-dialog.component'; +import { jsonRequired } from '@shared/components/json-object-edit.component'; + +@Component({ + selector: 'tb-gateway-service-rpc', + templateUrl: './gateway-service-rpc.component.html', + styleUrls: ['./gateway-service-rpc.component.scss'] +}) +export class GatewayServiceRPCComponent implements AfterViewInit { + + @Input() + ctx: WidgetContext; + + contentTypes = ContentType; + + resultTime: number | null; + + @Input() + dialogRef: MatDialogRef; + + commandForm: FormGroup; + + isConnector: boolean; + + RPCCommands: Array = [ + 'Ping', + 'Stats', + 'Devices', + 'Update', + 'Version', + 'Restart', + 'Reboot' + ]; + + private connectorType: string; + + constructor(private fb: FormBuilder, + private dialog: MatDialog) { + this.commandForm = this.fb.group({ + command: [null,[Validators.required]], + time: [60, [Validators.required, Validators.min(1)]], + params: ['{}', [jsonRequired]], + result: [null] + }); + } + + ngAfterViewInit() { + this.isConnector = this.ctx.settings.isConnector; + if (!this.isConnector) { + this.commandForm.get('command').setValue(this.RPCCommands[0]); + } else { + this.connectorType = this.ctx.stateController.getStateParams().connector_rpc.value.type; + } + } + + sendCommand() { + this.resultTime = null; + const formValues = this.commandForm.value; + const commandPrefix = this.isConnector ? `${this.connectorType}_` : 'gateway_'; + this.ctx.controlApi.sendTwoWayCommand(commandPrefix+formValues.command.toLowerCase(), formValues.params,formValues.time).subscribe({ + next: resp => { + this.resultTime = new Date().getTime(); + this.commandForm.get('result').setValue(JSON.stringify(resp)) + }, + error: error => { + this.resultTime = new Date().getTime(); + console.error(error); + this.commandForm.get('result').setValue(JSON.stringify(error.error)); + } + }); + } + + openEditJSONDialog($event: Event) { + if ($event) { + $event.stopPropagation(); + } + this.dialog.open(JsonObjectEditDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + jsonValue: JSON.parse(this.commandForm.get('params').value), + required: true + } + }).afterClosed().subscribe( + (res) => { + if (res) { + this.commandForm.get('params').setValue(JSON.stringify(res)); + } + } + ); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.html new file mode 100644 index 0000000000..8671b58f46 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.html @@ -0,0 +1,79 @@ + + +
+ + + {{ 'gateway.statistics.statistic' | translate }} + + + {{ key }} + + + {{ command.attributeOnGateway }} + + + + + {{'gateway.statistics.statistic-commands-empty' | translate }} + + + {{ 'gateway.statistics.command' | translate }} + + + +
+
+ + + {{ 'widgets.gateway.created-time' | translate }} + + {{row[0]| date:'yyyy-MM-dd HH:mm:ss' }} + + + + {{ 'widgets.gateway.message' | translate }} + + {{ row[1] }} + + + + +
+ {{ 'attribute.no-telemetry-text' | translate }} +
+
+ +
+
+
+
+
+
+ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.scss new file mode 100644 index 0000000000..e704120560 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.scss @@ -0,0 +1,86 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + width: 100%; + height: 100%; + padding: 0; + + + .statistics-container { + height: 100%; + overflow-y: auto; + + mat-card { + width: 40%; + height: 100%; + margin-right: 35px; + padding: 15px; + } + + @media only screen and (max-width: 750px) { + mat-card { + width: 100%; + } + } + + .chart-box, .chart-container { + height: 100%; + flex-grow: 1; + } + + .chart-box { + overflow: auto; + } + + & > * { + height: 100%; + } + } + + .legend { + flex-wrap: wrap; + width: 100%; + padding-top: 8px; + padding-bottom: 4px; + margin-top: 15px; + + .legend-keys { + .legend-label { + padding: 2px 20px 2px 10px; + white-space: nowrap; + + &.hidden-label { + text-decoration: line-through; + opacity: .6; + } + + &:focus { + outline: none; + } + } + + .legend-line { + display: inline-block; + width: 15px; + height: 3px; + text-align: left; + vertical-align: middle; + outline: none; + } + } + } +} + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts new file mode 100644 index 0000000000..503f5643bf --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts @@ -0,0 +1,286 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { AfterViewInit, Component, ElementRef, Input, ViewChild } from '@angular/core'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { AttributeService } from '@core/http/attribute.service'; +import { AttributeData, AttributeScope } from '@shared/models/telemetry/telemetry.models'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { TbFlot } from '@home/components/widget/lib/flot-widget'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { IWidgetSubscription, SubscriptionInfo, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; +import { UtilsService } from '@core/services/utils.service'; +import { DatasourceType, LegendConfig, LegendData, LegendPosition, widgetType } from '@shared/models/widget.models'; +import { EntityType } from '@shared/models/entity-type.models'; +import { EntityId } from '@shared/models/id/entity-id'; +import { BaseData } from '@shared/models/base-data'; +import { PageLink } from '@shared/models/page/page-link'; +import { Direction, SortOrder } from '@shared/models/page/sort-order'; +import { MatTableDataSource } from '@angular/material/table'; +import { MatSort } from '@angular/material/sort'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; + +@Component({ + selector: 'tb-gateway-statistics', + templateUrl: './gateway-statistics.component.html', + styleUrls: ['./gateway-statistics.component.scss'] +}) +export class GatewayStatisticsComponent implements AfterViewInit { + + @ViewChild(MatSort) sort: MatSort; + @ViewChild('statisticChart') statisticChart: ElementRef; + + @Input() + ctx: WidgetContext; + + @Input() + public general: boolean; + + public isNumericData = false; + public dataTypeDefined: boolean = false; + public chartInited: boolean; + private flot: TbFlot; + private flotCtx: WidgetContext; + public statisticForm: FormGroup; + public statisticsKeys = []; + public commands = []; + public commandObj: any; + public dataSource: MatTableDataSource; + public pageLink: PageLink; + private resize$: ResizeObserver; + private subscription: IWidgetSubscription; + private subscriptionInfo: SubscriptionInfo []; + public legendData: LegendData; + public displayedColumns: Array; + private subscriptionOptions: WidgetSubscriptionOptions = { + callbacks: { + onDataUpdated: () => this.ctx.ngZone.run(() => { + this.onDataUpdated(); + }), + onDataUpdateError: (subscription, e) => this.ctx.ngZone.run(() => { + this.onDataUpdateError(e); + }) + }, + useDashboardTimewindow: false, + legendConfig: { + position: LegendPosition.bottom + } as LegendConfig + }; + + + constructor(private fb: FormBuilder, + private attributeService: AttributeService, + private utils: UtilsService) { + const sortOrder: SortOrder = {property: '0', direction: Direction.DESC}; + this.pageLink = new PageLink(Number.POSITIVE_INFINITY, 0, null, sortOrder); + this.displayedColumns = ['0', '1']; + this.dataSource = new MatTableDataSource([]); + this.statisticForm = this.fb.group({ + statisticKey: [null, []] + }); + + this.statisticForm.get('statisticKey').valueChanges.subscribe(value => { + this.commandObj = null; + if (this.commands.length) { + this.commandObj = this.commands.find(command => command.attributeOnGateway === value); + } + if (this.subscriptionInfo) { + this.createChartsSubscription(this.ctx.defaultSubscription.datasources[0].entity, value); + } + }); + } + + + ngAfterViewInit() { + this.dataSource.sort = this.sort; + this.sort.sortChange.subscribe(() => this.sortData()); + this.init(); + if (this.ctx.defaultSubscription.datasources.length) { + const gateway = this.ctx.defaultSubscription.datasources[0].entity; + if (gateway.id.id === NULL_UUID) { + return; + } + if (!this.general) { + this.attributeService.getEntityAttributes(gateway.id, AttributeScope.SHARED_SCOPE, ['general_configuration']) + .subscribe((resp: AttributeData[]) => { + if (resp && resp.length) { + this.commands = resp[0].value.statistics.commands; + if (!this.statisticForm.get('statisticKey').value && this.commands && this.commands.length) { + this.statisticForm.get('statisticKey').setValue(this.commands[0].attributeOnGateway); + this.createChartsSubscription(gateway, this.commands[0].attributeOnGateway); + } + } + }); + } else { + this.attributeService.getEntityTimeseriesLatest(gateway.id).subscribe( + data => { + const connectorsTs = Object.keys(data) + .filter(el => el.includes('ConnectorEventsProduced') || el.includes('ConnectorEventsSent')); + this.createGeneralChartsSubscription(gateway, connectorsTs); + }); + } + } + } + + public sortData() { + this.dataSource.sortData(this.dataSource.data, this.sort); + } + + public onLegendKeyHiddenChange(index: number) { + this.legendData.keys[index].dataKey.hidden = !this.legendData.keys[index].dataKey.hidden; + this.subscription.updateDataVisibility(index); + } + + private createChartsSubscription(gateway: BaseData, attr: string) { + const subscriptionInfo = [{ + type: DatasourceType.entity, + entityType: EntityType.DEVICE, + entityId: gateway.id.id, + entityName: gateway.name, + timeseries: [] + }]; + + subscriptionInfo[0].timeseries = [{name: attr, label: attr}]; + this.subscriptionInfo = subscriptionInfo; + this.changeSubscription(subscriptionInfo); + this.ctx.defaultSubscription.unsubscribe(); + } + + private createGeneralChartsSubscription(gateway: BaseData, attrData: string[]) { + const subscriptionInfo = [{ + type: DatasourceType.entity, + entityType: EntityType.DEVICE, + entityId: gateway.id.id, + entityName: gateway.name, + timeseries: [] + }]; + subscriptionInfo[0].timeseries = []; + if (attrData?.length) { + attrData.forEach(attr => { + subscriptionInfo[0].timeseries.push({name: attr, label: attr}); + }); + } + this.ctx.defaultSubscription.datasources[0].dataKeys.forEach(dataKey => { + subscriptionInfo[0].timeseries.push({name: dataKey.name, label: dataKey.label}); + }); + + this.changeSubscription(subscriptionInfo); + this.ctx.defaultSubscription.unsubscribe(); + } + + private init = () => { + this.flotCtx = { + $scope: this.ctx.$scope, + $injector: this.ctx.$injector, + utils: this.ctx.utils, + isMobile: this.ctx.isMobile, + isEdit: this.ctx.isEdit, + subscriptionApi: this.ctx.subscriptionApi, + detectChanges: this.ctx.detectChanges, + settings: this.ctx.settings + } as WidgetContext; + }; + + private updateChart = () => { + if (this.flot && this.ctx.defaultSubscription.data.length) { + this.flot.update(); + } + }; + + private resize = () => { + if (this.flot) { + this.flot.resize(); + } + }; + + private reset() { + if (this.resize$) { + this.resize$.disconnect(); + } + if (this.subscription) { + this.subscription.unsubscribe(); + } + if (this.flot) { + this.flot.destroy(); + } + } + + private onDataUpdateError(e: any) { + const exceptionData = this.utils.parseException(e); + let errorText = exceptionData.name; + if (exceptionData.message) { + errorText += ': ' + exceptionData.message; + } + console.error(errorText); + } + + private onDataUpdated() { + this.isDataOnlyNumbers(); + if (this.isNumericData) { + if (this.chartInited) { + if (this.flot) { + this.flot.update(); + } + } else { + this.initChart(); + } + } + } + + private initChart() { + this.chartInited = true; + this.flotCtx.$container = $(this.statisticChart.nativeElement); + this.resize$.observe(this.statisticChart.nativeElement); + this.flot = new TbFlot(this.flotCtx as WidgetContext, 'line'); + this.flot.update(); + } + + private isDataOnlyNumbers() { + if (this.general) { + this.isNumericData = true; + return; + } + this.dataSource.data = this.subscription.data.length ? this.subscription.data[0].data : []; + if (this.dataSource.data.length && !this.dataTypeDefined) { + this.dataTypeDefined = true; + this.isNumericData = this.dataSource.data.every(data => !isNaN(+data[1])); + } + } + + + private changeSubscription(subscriptionInfo: SubscriptionInfo[]) { + if (this.subscription) { + this.reset(); + } + if (this.ctx.datasources[0].entity) { + this.ctx.subscriptionApi.createSubscriptionFromInfo(widgetType.timeseries, subscriptionInfo, this.subscriptionOptions, + false, true).subscribe(subscription => { + this.dataTypeDefined = false; + this.subscription = subscription; + this.isDataOnlyNumbers(); + this.legendData = this.subscription.legendData; + this.flotCtx.defaultSubscription = subscription; + this.resize$ = new ResizeObserver(() => { + this.resize(); + }); + this.ctx.detectChanges(); + if (this.isNumericData) { + this.initChart(); + } + }); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts new file mode 100644 index 0000000000..d58c33079a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts @@ -0,0 +1,142 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +export enum StorageTypes { + MEMORY = 'memory', + FILE = 'file', + SQLITE = 'sqlite' +} + +export enum DeviceGatewayStatus { + EXCEPTION = 'EXCEPTION' +} + +export enum GatewayLogLevel { + NONE = 'NONE', + CRITICAL = 'CRITICAL', + ERROR = 'ERROR', + WARNING = 'WARNING', + INFO = 'INFO', + DEBUG = 'DEBUG' +} + +export const GatewayStatus = { + ...GatewayLogLevel, + ...DeviceGatewayStatus +}; + +export type GatewayStatus = DeviceGatewayStatus | GatewayLogLevel; + +export enum LogSavingPeriod { + days = 'D', + hours = 'H', + minutes = 'M', + seconds = 'S' +} + +export enum LocalLogsConfigs { + service = 'service', + connector = 'connector', + converter = 'converter', + tb_connection = 'tb_connection', + storage = 'storage', + extension = 'extension' +} + +export const LocalLogsConfigTranslateMap = new Map([ + [LocalLogsConfigs.service, 'Service'], + [LocalLogsConfigs.connector, 'Connector'], + [LocalLogsConfigs.converter, 'Converter'], + [LocalLogsConfigs.tb_connection, 'TB Connection'], + [LocalLogsConfigs.storage, 'Storage'], + [LocalLogsConfigs.extension, 'Extension'] +]); + +export const LogSavingPeriodTranslations = new Map( + [ + [LogSavingPeriod.days, 'gateway.logs.days'], + [LogSavingPeriod.hours, 'gateway.logs.hours'], + [LogSavingPeriod.minutes, 'gateway.logs.minutes'], + [LogSavingPeriod.seconds, 'gateway.logs.seconds'] + ] +); + +export const StorageTypesTranslationMap = new Map( + [ + [StorageTypes.MEMORY, 'gateway.storage-types.memory-storage'], + [StorageTypes.FILE, 'gateway.storage-types.file-storage'], + [StorageTypes.SQLITE, 'gateway.storage-types.sqlite'] + ] +); + +export enum SecurityTypes { + ACCESS_TOKEN = 'accessToken', + USERNAME_PASSWORD = 'usernamePassword', + TLS_ACCESS_TOKEN = 'tlsAccessToken', + TLS_PRIVATE_KEY = 'tlsPrivateKey' +} + +export const GecurityTypesTranslationsMap = new Map( + [ + [SecurityTypes.ACCESS_TOKEN, 'gateway.security-types.access-token'], + [SecurityTypes.USERNAME_PASSWORD, 'gateway.security-types.username-password'], + [SecurityTypes.TLS_ACCESS_TOKEN, 'gateway.security-types.tls-access-token'], + // [SecurityTypes.TLS_PRIVATE_KEY, 'gateway.security-types.tls-private-key'], + ] +); + +export interface GatewayConnector { + name: string; + type: string; + configuration?: string; + configurationJson: string; + logLevel: string; + key?: string; +} + + +export const GatewayConnectorDefaultTypesTranslates = new Map([ + ['mqtt', 'MQTT'], + ['modbus', 'MODBUS'], + ['grpc', 'GRPC'], + ['opcua', 'OPCUA'], + ['opcua_asyncio', 'OPCUA ASYNCIO'], + ['ble', 'BLE'], + ['request', 'REQUEST'], + ['can', 'CAN'], + ['bacnet', 'BACNET'], + ['odbc', 'ODBC'], + ['rest', 'REST'], + ['snmp', 'SNMP'], + ['ftp', 'FTP'], + ['socket', 'SOCKET'], + ['xmpp', 'XMPP'], + ['ocpp', 'OCPP'], + ['custom', 'CUSTOM'] +]); + +export interface LogLink { + name: string; + key: string; + filterFn?: (arg: any) => boolean; +} + +export interface GatewayLogData { + ts: number; + key: string; + message: string; + status: GatewayStatus; +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/add-doc-link-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/add-doc-link-dialog.component.html index b276301ec8..1eaf791577 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/add-doc-link-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/add-doc-link-dialog.component.html @@ -22,7 +22,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/add-quick-link-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/add-quick-link-dialog.component.html index 6eaf4ca205..75d60e046f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/add-quick-link-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/add-quick-link-dialog.component.html @@ -22,7 +22,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-link.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-link.component.html index 2d6ea9b3fa..52e6b54519 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-link.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-link.component.html @@ -38,8 +38,8 @@
- - + +
@@ -47,7 +47,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-links-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-links-widget.component.html index 1ccc2e8b24..e4b56e491b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-links-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/doc-links-widget.component.html @@ -23,7 +23,7 @@ matTooltipPosition="above" mat-icon-button (click)="edit()"> - edit + edit
@@ -32,7 +32,7 @@ [href]="docLink.link" target="_blank"> @@ -43,7 +43,7 @@ matTooltip="{{ 'widgets.documentation.add-link' | translate }}" matTooltipPosition="above" (click)="addLink()"> - add + add
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/edit-links-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/edit-links-dialog.component.html index e70e8cd5c2..baddc057c2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/edit-links-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/edit-links-dialog.component.html @@ -22,7 +22,7 @@
@@ -60,7 +60,7 @@ matTooltip="{{ 'action.drag' | translate }}" matTooltipPosition="above" class="tb-drag-handle"> - drag_indicator + drag_indicator
@@ -71,7 +71,7 @@ matTooltip="{{ (mode === 'docs' ? 'widgets.documentation.add-link' : 'widgets.quick-links.add-link') | translate }}" matTooltipPosition="above" (click)="addLink()"> - add + add
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/getting-started-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/getting-started-widget.component.html index 17ede099dc..9a575b001a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/getting-started-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/getting-started-widget.component.html @@ -111,21 +111,10 @@
- + + Ubuntu + MacOS + Windows diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page-widget.scss b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page-widget.scss index 4cad7a6971..912ebbb63e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page-widget.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page-widget.scss @@ -67,50 +67,4 @@ color: inherit; } } - - .tb-no-data-available { - flex: 1; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - } - - .tb-no-data-bg { - margin: 10px; - position: relative; - flex: 1; - width: 100%; - max-height: 100px; - &:before { - content: ""; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: #305680; - -webkit-mask-image: url(/assets/home/no_data_folder_bg.svg); - -webkit-mask-repeat: no-repeat; - -webkit-mask-size: contain; - -webkit-mask-position: center; - mask-image: url(/assets/home/no_data_folder_bg.svg); - mask-repeat: no-repeat; - mask-size: contain; - mask-position: center; - } - } - - .tb-no-data-text { - font-weight: 500; - font-size: 14px; - line-height: 20px; - letter-spacing: 0.25px; - color: rgba(0, 0, 0, 0.54); - @media #{$mat-md-lg} { - font-size: 12px; - line-height: 16px; - } - } - } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page.scss b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page.scss index a488d77c6e..4adf70d97d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/home-page.scss @@ -23,6 +23,7 @@ letter-spacing: 0.2px; color: rgba(0, 0, 0, 0.76); .mat-icon { + vertical-align: bottom; margin-right: 10px; color: rgba(0, 0, 0, 0.54); font-size: 20px; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-link.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-link.component.html index 0dec41e71d..da1e9977a1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-link.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-link.component.html @@ -25,21 +25,19 @@ (focusin)="onFocus()" required [matAutocomplete]="linkAutocomplete"> - {{ quickLink.icon }} - + {{ quickLink.icon }} - {{ link.icon }} - + {{ link.icon }} @@ -58,8 +56,8 @@
- - + +
@@ -67,8 +65,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-links-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-links-widget.component.html index 95c2818f1c..58d3d98ee3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-links-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/quick-links-widget.component.html @@ -23,7 +23,7 @@ matTooltipPosition="above" mat-icon-button (click)="edit()"> - edit + edit
@@ -32,8 +32,7 @@ [routerLink]="quickLink.path"> @@ -44,7 +43,7 @@ matTooltip="{{ 'widgets.quick-links.add-link' | translate }}" matTooltipPosition="above" (click)="addLink()"> - add + add
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.html index ba7614934d..b2a085b917 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.html @@ -20,16 +20,9 @@
{{ 'widgets.recent-dashboards.title' | translate }}
- + + {{ 'widgets.recent-dashboards.last' | translate }} + {{ 'widgets.recent-dashboards.starred' | translate }} {{ 'dashboard.add' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.html index a0beeacdb2..61e7c448b4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/usage-info-widget.component.html @@ -19,16 +19,9 @@
{{ 'widgets.usage-info.title' | translate }} - + + {{ 'widgets.usage-info.entities' | translate }} + {{ 'widgets.usage-info.api-calls' | translate }}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.html new file mode 100644 index 0000000000..688fdcc216 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.html @@ -0,0 +1,43 @@ + +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
{{ valueText }}
+
+
+
+ +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.scss new file mode 100644 index 0000000000..fbf244a383 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.scss @@ -0,0 +1,159 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.tb-battery-level-panel { + width: 100%; + height: 100%; + position: relative; + display: flex; + flex-direction: column; + gap: 16px; + padding: 20px 24px 24px 24px; + &.tb-battery-level-pointer { + cursor: pointer; + } + > div:not(.tb-battery-level-overlay) { + z-index: 1; + } + .tb-battery-level-overlay { + position: absolute; + top: 12px; + left: 12px; + bottom: 12px; + right: 12px; + } + div.tb-widget-title { + padding: 0; + } + .tb-battery-level-content { + min-height: 0; + flex: 1; + display: flex; + justify-content: center; + &.vertical { + flex-direction: row; + gap: 16px; + .tb-battery-level-value-box { + align-items: center; + .tb-battery-level-value { + padding: 8px 12px; + } + } + } + &.horizontal { + flex-direction: column-reverse; + gap: 8px; + align-items: center; + .tb-battery-level-value-box { + .tb-battery-level-value { + padding: 4px 6px; + } + } + } + .tb-battery-level-box { + display: flex; + align-items: center; + .tb-battery-level-rectangle { + width: 100%; + height: 100%; + position: relative; + .tb-battery-level-shape { + position: absolute; + inset: 0; + mask-repeat: no-repeat; + mask-size: cover; + mask-position: center; + } + .tb-battery-level-container { + position: absolute; + display: flex; + gap: 3%; + } + .tb-battery-level-indicator-box { + width: 100%; + height: 100%; + &.solid { + background-repeat: no-repeat; + transition: background 0.2s ease-out; + } + &.divided { + transition: opacity 0.2s ease-out; + } + } + &.vertical { + .tb-battery-level-shape { + mask-image: url(/assets/widget/battery-level/battery-shape-vertical.svg); + } + .tb-battery-level-container { + flex-direction: column-reverse; + } + &.solid { + .tb-battery-level-container { + inset: 8.85% 6.25% 3.54% 6.25%; + } + } + &.divided { + .tb-battery-level-container { + inset: 9.73% 7.81% 4.42% 7.81%; + } + } + .tb-battery-level-indicator-box { + &.solid { + border-radius: 10.7% / 6%; + background-position: 0 101%; + } + &.divided { + border-radius: 7.14% / 17.8%; + } + } + } + &.horizontal { + .tb-battery-level-shape { + mask-image: url(/assets/widget/battery-level/battery-shape-horizontal.svg); + } + .tb-battery-level-container { + inset: 6.25% 8.85% 6.25% 3.54%; + flex-direction: row; + } + &.solid { + .tb-battery-level-container { + inset: 6.25% 8.85% 6.25% 3.54%; + } + } + &.divided { + .tb-battery-level-container { + inset: 7.81% 9.73% 7.81% 4.42%; + } + } + .tb-battery-level-indicator-box { + &.solid { + border-radius: 6% / 10.7%; + background-position: -1% 0%; + } + &.divided { + border-radius: 17.8% / 7.14%; + } + } + } + } + } + .tb-battery-level-value-box { + display: flex; + .tb-battery-level-value { + white-space: nowrap; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.ts new file mode 100644 index 0000000000..cd9b48e64a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.ts @@ -0,0 +1,338 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + Input, + OnDestroy, + OnInit, + Renderer2, + TemplateRef, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { formatValue, isDefinedAndNotNull, isNumeric } from '@core/utils'; +import { DatePipe } from '@angular/common'; +import { + backgroundStyle, + ColorProcessor, + ComponentStyle, + getDataKey, + getSingleTsValue, + overlayStyle, + textStyle +} from '@shared/models/widget-settings.models'; +import { WidgetComponent } from '@home/components/widget/widget.component'; +import { + batteryLevelDefaultSettings, + BatteryLevelLayout, + BatteryLevelWidgetSettings +} from '@home/components/widget/lib/indicator/battery-level-widget.models'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { Observable } from 'rxjs'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { DomSanitizer } from '@angular/platform-browser'; + +const verticalBatteryDimensions = { + shapeAspectRatio: 64 / 113, + widthRatio: { + valueTopBottomPaddingRatio: 8 / 64, + valueLeftRightPaddingRatio: 12 / 64, + valueFontSizeRatio: 20 / 64, + valueLineHeightRaio: 24 / 64 + }, + heightRatio: { + valueTopBottomPaddingRatio: 8 / 113, + valueLeftRightPaddingRatio: 12 / 113, + valueFontSizeRatio: 20 / 113, + valueLineHeightRaio: 24 / 113 + } +}; + +const horizontalBatteryDimensions = { + shapeAspectRatio: 113 / 64, + heightRatio: { + valueTopBottomPaddingRatio: 4 / 64, + valueFontSizeRatio: 20 / 64, + valueLineHeightRatio: 24 / 64 + } +}; + +@Component({ + selector: 'tb-battery-level-widget', + templateUrl: './battery-level-widget.component.html', + styleUrls: ['./battery-level-widget.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class BatteryLevelWidgetComponent implements OnInit, OnDestroy, AfterViewInit { + + @ViewChild('batteryLevelContent', {static: true}) + batteryLevelContent: ElementRef; + + @ViewChild('batteryLevelBox', {static: true}) + batteryLevelBox: ElementRef; + + @ViewChild('batteryLevelRectangle', {static: true}) + batteryLevelRectangle: ElementRef; + + @ViewChild('batteryLevelValueBox', {static: false}) + batteryLevelValueBox: ElementRef; + + @ViewChild('batteryLevelValue', {static: false}) + batteryLevelValue: ElementRef; + + settings: BatteryLevelWidgetSettings; + + @Input() + ctx: WidgetContext; + + @Input() + widgetTitlePanel: TemplateRef; + + layout: BatteryLevelLayout; + layoutClass = 'vertical'; + + vertical = true; + solid = true; + + showValue = true; + autoScaleValueSize = true; + valueText = 'N/A'; + valueStyle: ComponentStyle = {}; + valueColor: ColorProcessor; + + value: number; + + batteryFillValue: number; + + batterySections: boolean[]; + dividedBorderRadius: string; + dividedGap: string; + + batteryLevelColor: ColorProcessor; + + batteryShapeColor: ColorProcessor; + + backgroundStyle$: Observable; + overlayStyle: ComponentStyle = {}; + + batteryBoxResize$: ResizeObserver; + + hasCardClickAction = false; + + private decimals = 0; + private units = ''; + + constructor(private date: DatePipe, + private imagePipe: ImagePipe, + private sanitizer: DomSanitizer, + private widgetComponent: WidgetComponent, + private renderer: Renderer2, + private cd: ChangeDetectorRef) { + } + + ngOnInit(): void { + this.ctx.$scope.batteryLevelWidget = this; + this.settings = {...batteryLevelDefaultSettings, ...this.ctx.settings}; + + this.decimals = this.ctx.decimals; + this.units = this.ctx.units; + const dataKey = getDataKey(this.ctx.datasources); + if (isDefinedAndNotNull(dataKey?.decimals)) { + this.decimals = dataKey.decimals; + } + if (dataKey?.units) { + this.units = dataKey.units; + } + + this.layout = this.settings.layout; + + this.vertical = [BatteryLevelLayout.vertical_solid, BatteryLevelLayout.vertical_divided].includes(this.layout); + this.layoutClass = this.vertical ? 'vertical' : 'horizontal'; + this.solid = [BatteryLevelLayout.vertical_solid, BatteryLevelLayout.horizontal_solid].includes(this.layout); + if (!this.solid) { + let sectionsCount = this.settings.sectionsCount; + if (!sectionsCount) { + sectionsCount = 4; + } + sectionsCount = Math.min(Math.max(sectionsCount, 2), 20); + this.batterySections = Array.from(Array(sectionsCount), () => false); + const gap = 1 + (24 - sectionsCount) / 10; + this.dividedGap = `${gap}%`; + const containerAspect = 0.5567; + const sectionHeight = (100 - (gap * (sectionsCount - 1))) / sectionsCount; + const sectionAspect = 100 * containerAspect / sectionHeight; + const rad1 = 8.425 - sectionsCount * 0.32125; + const rad2 = rad1 * sectionAspect; + if (this.vertical) { + this.dividedBorderRadius = `${rad1}% / ${rad2}%`; + } else { + this.dividedBorderRadius = `${rad2}% / ${rad1}%`; + } + } + + this.showValue = this.settings.showValue; + this.autoScaleValueSize = this.showValue && this.settings.autoScaleValueSize; + this.valueStyle = textStyle(this.settings.valueFont); + this.valueColor = ColorProcessor.fromSettings(this.settings.valueColor); + + this.batteryLevelColor = ColorProcessor.fromSettings(this.settings.batteryLevelColor); + + this.batteryShapeColor = ColorProcessor.fromSettings(this.settings.batteryShapeColor); + + this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); + this.overlayStyle = overlayStyle(this.settings.background.overlay); + + this.hasCardClickAction = this.ctx.actionsApi.getActionDescriptors('cardClick').length > 0; + } + + ngAfterViewInit() { + this.batteryBoxResize$ = new ResizeObserver(() => { + this.onResize(); + }); + this.batteryBoxResize$.observe(this.batteryLevelContent.nativeElement); + if (this.showValue) { + this.batteryBoxResize$.observe(this.batteryLevelValueBox.nativeElement); + } + this.onResize(); + } + + ngOnDestroy() { + if (this.batteryBoxResize$) { + this.batteryBoxResize$.disconnect(); + } + } + + public onInit() { + const borderRadius = this.ctx.$widgetElement.css('borderRadius'); + this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; + this.cd.detectChanges(); + } + + public onDataUpdated() { + const tsValue = getSingleTsValue(this.ctx.data); + this.batteryFillValue = 0; + if (tsValue && isDefinedAndNotNull(tsValue[1]) && isNumeric(tsValue[1])) { + this.value = tsValue[1]; + this.batteryFillValue = this.parseBatteryFillValue(this.value); + this.valueText = formatValue(this.value, this.decimals, this.units, false); + } else { + this.valueText = 'N/A'; + } + if (!this.solid) { + const sectionSize = 100 / this.batterySections.length; + for (let i=0; i sectionSize * i; + } + } + this.valueColor.update(this.value); + this.batteryLevelColor.update(this.value); + this.batteryShapeColor.update(this.value); + this.cd.detectChanges(); + } + + parseBatteryFillValue(value: number) { + if (value < 0) { + return 0; + } else if (value > 100) { + return 100; + } else { + return value; + } + } + + public trackBySection(index: number): number { + return index; + } + + public cardClick($event: Event) { + this.ctx.actionsApi.cardClick($event); + } + + private onResize() { + if (this.vertical) { + if (this.batteryLevelValue) { + const contentWidth = this.batteryLevelContent.nativeElement.getBoundingClientRect().width; + const boxWidth = (contentWidth - 16) / 2; + const boxHeight = this.batteryLevelContent.nativeElement.getBoundingClientRect().height; + const ratios = contentWidth > boxHeight ? verticalBatteryDimensions.heightRatio : verticalBatteryDimensions.widthRatio; + const boxSize = contentWidth > boxHeight ? boxHeight : boxWidth; + const topBottomValuePadding = ratios.valueTopBottomPaddingRatio * boxSize; + const leftRightValuePadding = ratios.valueLeftRightPaddingRatio * boxSize; + const valuePadding = `${topBottomValuePadding}px ${leftRightValuePadding}px`; + this.renderer.setStyle(this.batteryLevelValue.nativeElement, 'padding', valuePadding); + if (this.autoScaleValueSize) { + const valueFontSize = ratios.valueFontSizeRatio * boxSize; + const valueLineHeight = ratios.valueLineHeightRaio * boxSize; + this.renderer.setStyle(this.batteryLevelValue.nativeElement, 'minWidth', '0'); + this.setValueFontSize(valueFontSize, valueLineHeight, boxWidth); + } + const fontSize = parseInt(window.getComputedStyle(this.batteryLevelValue.nativeElement).fontSize, 10) || 10; + this.renderer.setStyle(this.batteryLevelValue.nativeElement, 'minWidth', `${Math.min(fontSize*4, boxWidth)}px`); + } + let height = this.batteryLevelContent.nativeElement.getBoundingClientRect().height; + const width = height * verticalBatteryDimensions.shapeAspectRatio; + this.renderer.setStyle(this.batteryLevelBox.nativeElement, 'width', width + 'px'); + const realWidth = this.batteryLevelBox.nativeElement.getBoundingClientRect().width; + if (realWidth < width) { + height = realWidth / verticalBatteryDimensions.shapeAspectRatio; + this.renderer.setStyle(this.batteryLevelRectangle.nativeElement, 'height', height + 'px'); + } else { + this.renderer.setStyle(this.batteryLevelRectangle.nativeElement, 'height', null); + } + } else { + const width = this.batteryLevelContent.nativeElement.getBoundingClientRect().width; + let height = width / horizontalBatteryDimensions.shapeAspectRatio; + this.renderer.setStyle(this.batteryLevelBox.nativeElement, 'height', height + 'px'); + const realHeight = this.batteryLevelBox.nativeElement.getBoundingClientRect().height; + if (realHeight < height) { + height = realHeight; + const newWidth = height * horizontalBatteryDimensions.shapeAspectRatio; + this.renderer.setStyle(this.batteryLevelRectangle.nativeElement, 'width', newWidth + 'px'); + } else { + this.renderer.setStyle(this.batteryLevelRectangle.nativeElement, 'width', null); + } + if (this.batteryLevelValue) { + const ratios = horizontalBatteryDimensions.heightRatio; + const valuePadding = `${(ratios.valueTopBottomPaddingRatio * height)}px 6px`; + this.renderer.setStyle(this.batteryLevelValue.nativeElement, 'padding', valuePadding); + if (this.autoScaleValueSize) { + const valueFontSize = ratios.valueFontSizeRatio * height; + const valueLineHeight = ratios.valueLineHeightRatio * height; + const boxWidth = this.batteryLevelContent.nativeElement.getBoundingClientRect().width; + this.setValueFontSize(valueFontSize, valueLineHeight, boxWidth); + } + } + } + } + + private setValueFontSize(valueFontSize: number, valueLineHeight: number, maxWidth: number) { + this.renderer.setStyle(this.batteryLevelValue.nativeElement, 'fontSize', valueFontSize + 'px'); + this.renderer.setStyle(this.batteryLevelValue.nativeElement, 'lineHeight', valueLineHeight + 'px'); + let valueWidth = this.batteryLevelValue.nativeElement.getBoundingClientRect().width; + while (valueWidth > maxWidth && valueFontSize > 6) { + valueFontSize--; + valueLineHeight--; + this.renderer.setStyle(this.batteryLevelValue.nativeElement, 'fontSize', valueFontSize + 'px'); + this.renderer.setStyle(this.batteryLevelValue.nativeElement, 'lineHeight', valueLineHeight + 'px'); + valueWidth = this.batteryLevelValue.nativeElement.getBoundingClientRect().width; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.models.ts new file mode 100644 index 0000000000..cb4f894ca7 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.models.ts @@ -0,0 +1,110 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + BackgroundSettings, + BackgroundType, + ColorSettings, + ColorType, + constantColor, + defaultColorFunction, + Font +} from '@shared/models/widget-settings.models'; + +export enum BatteryLevelLayout { + vertical_solid = 'vertical_solid', + horizontal_solid = 'horizontal_solid', + vertical_divided = 'vertical_divided', + horizontal_divided = 'horizontal_divided' +} + +export const batteryLevelLayouts = Object.keys(BatteryLevelLayout) as BatteryLevelLayout[]; + +export const batteryLevelLayoutTranslations = new Map( + [ + [BatteryLevelLayout.vertical_solid, 'widgets.battery-level.layout-vertical-solid'], + [BatteryLevelLayout.horizontal_solid, 'widgets.battery-level.layout-horizontal-solid'], + [BatteryLevelLayout.vertical_divided, 'widgets.battery-level.layout-vertical-divided'], + [BatteryLevelLayout.horizontal_divided, 'widgets.battery-level.layout-horizontal-divided'] + ] +); + +export const batteryLevelLayoutImages = new Map( + [ + [BatteryLevelLayout.vertical_solid, 'assets/widget/battery-level/vertical-solid-layout.svg'], + [BatteryLevelLayout.horizontal_solid, 'assets/widget/battery-level/horizontal-solid-layout.svg'], + [BatteryLevelLayout.vertical_divided, 'assets/widget/battery-level/vertical-divided-layout.svg'], + [BatteryLevelLayout.horizontal_divided, 'assets/widget/battery-level/horizontal-divided-layout.svg'] + ] +); + +export interface BatteryLevelWidgetSettings { + layout: BatteryLevelLayout; + sectionsCount: number; + showValue: boolean; + autoScaleValueSize: boolean; + valueFont: Font; + valueColor: ColorSettings; + batteryLevelColor: ColorSettings; + batteryShapeColor: ColorSettings; + background: BackgroundSettings; +} + +export const batteryLevelDefaultSettings: BatteryLevelWidgetSettings = { + layout: BatteryLevelLayout.vertical_solid, + sectionsCount: 4, + showValue: true, + autoScaleValueSize: true, + valueFont: { + family: 'Roboto', + size: 20, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '24px' + }, + valueColor: constantColor('rgba(0, 0, 0, 0.87)'), + batteryLevelColor: { + color: 'rgba(224, 224, 224, 1)', + type: ColorType.range, + rangeList: [ + {from: null, to: 25, color: 'rgba(227, 71, 71, 1)'}, + {from: 25, to: 50, color: 'rgba(246, 206, 67, 1)'}, + {from: 50, to: null, color: 'rgba(92, 223, 144, 1)'} + ], + colorFunction: defaultColorFunction + }, + batteryShapeColor: { + color: 'rgba(224, 224, 224, 0.32)', + type: ColorType.range, + rangeList: [ + {from: null, to: 25, color: 'rgba(227, 71, 71, 0.32)'}, + {from: 25, to: 50, color: 'rgba(246, 206, 67, 0.32)'}, + {from: 50, to: null, color: 'rgba(92, 223, 144, 0.32)'} + ], + colorFunction: defaultColorFunction + }, + background: { + type: BackgroundType.color, + color: '#fff', + overlay: { + enabled: false, + color: 'rgba(255,255,255,0.72)', + blur: 3 + } + } +}; + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.html new file mode 100644 index 0000000000..430d9260b5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.html @@ -0,0 +1,29 @@ + +
+
+ +
+
+
+
+ {{ error }} +
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.scss new file mode 100644 index 0000000000..046d1a7d3e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.scss @@ -0,0 +1,72 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.tb-liquid-level-panel { + width: 100%; + height: 100%; + position: relative; + display: flex; + flex-direction: column; + gap: 16px; + padding: 20px 24px 24px 24px; + &.tb-liquid-level-pointer { + cursor: pointer; + } + > div:not(.tb-liquid-level-overlay) { + z-index: 1; + } + .tb-liquid-level-overlay { + position: absolute; + top: 12px; + left: 12px; + bottom: 12px; + right: 12px; + } + div.tb-widget-title { + padding: 0; + } + .tb-liquid-level-content { + min-height: 0; + flex: 1; + display: flex; + justify-content: center; + } + .tb-liquid-level-error { + position: absolute; + display: flex; + inset: 0; + width: 100%; + height: 100%; + align-items: center; + justify-content: center; + + &-container { + border-radius: 4px; + margin: 16px; + padding: 8px 12px; + background: #848484; + display: flex; + flex-direction: column; + gap: 8px; + + & > * { + font-weight: 500; + letter-spacing: .25px; + text-align: center; + color: #fff; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.ts new file mode 100644 index 0000000000..bd6743c2a5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.component.ts @@ -0,0 +1,583 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + ChangeDetectorRef, + Component, + ElementRef, + Input, + OnInit, + TemplateRef, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { isDefined, isDefinedAndNotNull, isEmptyStr, isNotEmptyStr, isNumeric, isUndefinedOrNull } from '@core/utils'; +import { + CapacityUnits, + ConversionType, + convertLiters, + createAbsoluteLayout, + createPercentLayout, + extractValue, + levelCardDefaultSettings, + LevelCardLayout, + LevelCardWidgetSettings, + LiquidWidgetDataSourceType, + Shapes, + SvgInfo, + SvgLimits, + svgMapping +} from '@home/components/widget/lib/indicator/liquid-level-widget.models'; +import { forkJoin, Observable, of } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; +import { EntityId } from '@shared/models/id/entity-id'; +import { + backgroundStyle, + ColorProcessor, + ComponentStyle, + cssTextFromInlineStyle, + DateFormatProcessor, + inlineTextStyle, + overlayStyle +} from '@shared/models/widget-settings.models'; +import { ResourcesService } from '@core/services/resources.service'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { TranslateService } from '@ngx-translate/core'; +import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { DomSanitizer } from '@angular/platform-browser'; + +@Component({ + selector: 'tb-liquid-level-widget', + templateUrl: './liquid-level-widget.component.html', + styleUrls: ['./liquid-level-widget.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class LiquidLevelWidgetComponent implements OnInit { + + @ViewChild('liquidLevelContent', {static: true}) + liquidLevelContent: ElementRef; + + @Input() + ctx: WidgetContext; + + @Input() + widgetTitlePanel: TemplateRef; + + backgroundStyle$: Observable; + overlayStyle: ComponentStyle = {}; + + hasCardClickAction = false; + + errorsMsg: string[] = []; + + private svgParams: SvgInfo; + + private svg: JQuery; + private tooltip: ITooltipsterInstance; + private overlayContainer: JQuery; + private shape: Shapes; + + private settings: LevelCardWidgetSettings; + + private tankColor: ColorProcessor; + private valueColor: ColorProcessor; + private liquidColor: ColorProcessor; + private backgroundOverlayColor: ColorProcessor; + private tooltipLevelColor: ColorProcessor; + + private tooltipDateFormat: DateFormatProcessor; + + private volume: number; + private tooltipContent: string; + private widgetUnits: string; + + private capacityUnits = Object.values(CapacityUnits); + + constructor(private imagePipe: ImagePipe, + private sanitizer: DomSanitizer, + private cd: ChangeDetectorRef, + private resourcesService: ResourcesService, + private translate: TranslateService) { + } + + ngOnInit(): void { + this.ctx.$scope.liquidLevelWidget = this; + this.settings = {...levelCardDefaultSettings, ...this.ctx.settings}; + this.declareStyles(); + + this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); + this.overlayStyle = overlayStyle(this.settings.background.overlay); + + this.hasCardClickAction = this.ctx.actionsApi.getActionDescriptors('cardClick').length > 0; + + this.getData().subscribe(data => { + if (data) { + const { svg, volume, units } = data; + if (svg && isNotEmptyStr(svg) && this.liquidLevelContent.nativeElement) { + const jQueryContainerElement = $(this.liquidLevelContent.nativeElement); + jQueryContainerElement.html(svg); + this.svg = jQueryContainerElement.find('svg'); + this.createSVG(); + this.createValueElement(); + + if (this.settings.showTooltip && (this.settings.showTooltipLevel || this.settings.showTooltipDate)) { + this.createTooltip(); + } + } + + if (isDefined(volume) && !isNaN(Number(volume))) { + this.volume = Number(volume); + } + + if (units) { + this.widgetUnits = units; + } + + this.update(true); + } + }); + } + + private declareStyles(): void { + this.tankColor = ColorProcessor.fromSettings(this.settings.tankColor); + this.valueColor = ColorProcessor.fromSettings(this.settings.valueColor); + this.liquidColor = ColorProcessor.fromSettings(this.settings.liquidColor); + this.backgroundOverlayColor = ColorProcessor.fromSettings(this.settings.backgroundOverlayColor); + this.tooltipLevelColor = ColorProcessor.fromSettings(this.settings.tooltipLevelColor); + + this.tooltipDateFormat = DateFormatProcessor.fromSettings(this.ctx.$injector, this.settings.tooltipDateFormat); + } + + private getData(): Observable<{ svg: string; volume: number; units: string }> { + if (this.ctx.datasources?.length) { + const entityId: EntityId = { + entityType: this.ctx.datasources[0].entityType, + id: this.ctx.datasources[0].entityId + }; + + return this.getShape(entityId).pipe( + switchMap(shape => { + this.shape = shape; + this.svgParams = svgMapping.get(shape); + if (this.svgParams) { + return forkJoin([ + this.resourcesService.loadJsonResource(this.svgParams.svg), + this.getTankersParams(entityId) + ]).pipe( + map(params => ({svg: params[0], ...params[1]})) + ); + } + return of(null); + }) + ); + } + + return of(null); + } + + public onInit() { + const borderRadius = this.ctx.$widgetElement.css('borderRadius'); + this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; + this.cd.detectChanges(); + } + + public update(ignoreAnimation?: boolean) { + if (this.svg) { + this.updateData(ignoreAnimation); + } + } + + public destroy() { + + } + + private createSVG() { + if (this.svg) { + this.svg.css('display', 'block') + .css('overflow', 'hidden') + .css('width', '100%') + .css('height', '100%'); + } + } + + private updateData(ignoreAnimation?: boolean) { + const data = this.ctx.data[0]?.data[0]; + if (data && isDefinedAndNotNull(data[1])) { + const percentage = isNumeric(data[1]) ? this.convertInputData(data[1]) : 0; + this.updateSvg(percentage, ignoreAnimation); + this.updateValueElement(data[1], percentage); + + if (this.settings.showTooltip && (this.settings.showTooltipLevel || this.settings.showTooltipDate)) { + this.updateTooltip(data); + } + } + } + + private createTooltip(): void { + this.tooltipContent = this.getTooltipContent(); + + import('tooltipster').then(() => { + if ($.tooltipster && this.tooltip) { + this.tooltip.destroy(); + this.tooltip = null; + } + this.tooltip = this.svg.tooltipster({ + contentAsHTML: true, + theme: 'tooltipster-shadow', + side: 'top', + delay: 10, + distance: this.settings.showTitle ? -33 : -63, + triggerClose: { + click: true, + tap: true, + scroll: true, + mouseleave: true + }, + animation: 'grow', + updateAnimation: null, + trackOrigin: true, + functionBefore: (instance, helper) => { + instance.content(this.tooltipContent); + }, + functionReady: (instance, helper) => { + const tooltipsterBoxStyles: JQuery.PlainObject = { + backgroundColor: this.settings.tooltipBackgroundColor, + backdropFilter: `blur(${this.settings.tooltipBackgroundBlur}px)`, + width: '100%', + height: '100%' + }; + + $(helper.tooltip).css('max-width', this.svg.width() + 'px'); + $(helper.tooltip).css('max-height', this.svg.height() + 'px'); + $(helper.tooltip).find('.tooltipster-box').css(tooltipsterBoxStyles); + $(helper.tooltip).find('.tooltipster-arrow').empty(); + + instance.reposition(); + } + }).tooltipster('instance'); + }); + } + + private createValueElement(): void { + const jQueryContainerElement = $(this.liquidLevelContent.nativeElement); + const containerOverlay = jQueryContainerElement.find('.container-overlay'); + const percentageOverlay = jQueryContainerElement.find('.percentage-overlay'); + const absoluteOverlay = jQueryContainerElement.find('.absolute-overlay'); + + if (this.settings.layout === LevelCardLayout.absolute && !this.errorsMsg.length) { + this.overlayContainer = absoluteOverlay; + percentageOverlay.css('visibility', 'hidden'); + if (!this.settings.showBackgroundOverlay) { + absoluteOverlay.css('visibility', 'hidden'); + } + } else if (this.settings.layout === LevelCardLayout.percentage && !this.errorsMsg.length) { + this.overlayContainer = percentageOverlay; + absoluteOverlay.css('visibility', 'hidden'); + if (!this.settings.showBackgroundOverlay) { + percentageOverlay.css('visibility', 'hidden'); + } + } else { + containerOverlay.css('visibility', 'hidden'); + } + + if (this.overlayContainer) { + this.overlayContainer.attr('fill', this.backgroundOverlayColor.color); + this.overlayContainer.removeAttr('fill-opacity'); + } + } + + private getShape(entityId: EntityId): Observable { + if (this.settings.tankSelectionType === LiquidWidgetDataSourceType.attribute && entityId.id !== NULL_UUID) { + return this.ctx.attributeService.getEntityAttributes(entityId, null, [this.settings.shapeAttributeName]) + .pipe(map(attributes => { + const shape = extractValue(attributes, this.settings.shapeAttributeName); + if (!shape || !svgMapping.has(shape)) { + this.createdErrorMgs(this.settings.shapeAttributeName, isUndefinedOrNull(shape) || isEmptyStr(shape)); + return this.settings.selectedShape; + } + return shape; + } + )); + } + return of(this.settings.selectedShape); + } + + private getTankersParams(entityId: EntityId): Observable<{ volume: number; units: string }> { + const isVolumeStatic = this.settings.layout !== LevelCardLayout.absolute + && this.settings.datasourceUnits === CapacityUnits.percent + || this.settings.volumeSource === LiquidWidgetDataSourceType.static; + const isUnitStatic = this.settings.layout !== LevelCardLayout.absolute || + this.settings.widgetUnitsSource === LiquidWidgetDataSourceType.static; + + const attributeKeys: string[] = []; + + if (!isVolumeStatic) { + attributeKeys.push(this.settings.volumeAttributeName); + } + + if (!isUnitStatic) { + attributeKeys.push(this.settings.widgetUnitsAttributeName); + } + + if (!attributeKeys.length || entityId.id === NULL_UUID) { + return of({ + volume: this.settings.volumeConstant, + units: this.settings.units + }); + } + + return this.ctx.attributeService.getEntityAttributes(entityId, null, attributeKeys).pipe( + map(attributes => { + let volume = isVolumeStatic ? this.settings.volumeConstant : extractValue(attributes, this.settings.volumeAttributeName); + let units = isUnitStatic ? this.settings.units : extractValue(attributes, this.settings.widgetUnitsAttributeName); + + if (!isVolumeStatic && (!volume || !isNumeric(volume) || volume < 0.1)) { + this.createdErrorMgs(this.settings.volumeAttributeName, isUndefinedOrNull(volume) || isEmptyStr(volume)); + volume = this.settings.volumeConstant; + } + + if (!isUnitStatic) { + if (isNotEmptyStr(units)) { + const normalizeUnits = units.normalize().trim(); + units = this.capacityUnits.find(unit => unit.normalize() === normalizeUnits); + } + if (isUndefinedOrNull(units) || !isNotEmptyStr(units)) { + this.createdErrorMgs(this.settings.widgetUnitsAttributeName, isUndefinedOrNull(units) || isEmptyStr(units)); + units = this.settings.units; + } + } + + return { + volume, + units + }; + }) + ); + } + + private createdErrorMgs(attributeName: string, isEmpty = false) { + if (isEmpty) { + this.errorsMsg.push(this.translate.instant('widgets.liquid-level-card.attribute-key-not-set', {attributeName})); + } else { + this.errorsMsg.push(this.translate.instant('widgets.liquid-level-card.attribute-key-invalid', {attributeName})); + } + this.cd.markForCheck(); + } + + private updateSvg(percentage: number, ignoreAnimation?: boolean) { + const yLimits: SvgLimits = { + min: this.svgParams.limits.min, + max: this.svgParams.limits.max + }; + + const newYPos = this.calculatePosition(percentage, yLimits); + this.updateShapeColor(percentage); + this.updateLevel(newYPos, percentage, ignoreAnimation); + } + + private calculatePosition(percentage: number, limits: SvgLimits): number { + if (percentage >= 100) { + return limits.max; + } if (percentage <= 0) { + return limits.min; + } + return limits.min + (percentage / 100) * (limits.max - limits.min); + } + + private updateTooltip(value: [number, any]): void { + this.tooltipContent = this.getTooltipContent(value); + + if (this.tooltip) { + this.tooltip.content(this.tooltipContent); + } + } + + private updateLevel(newY: number, percentage: number, ignoreAnimation = false): void { + this.liquidColor.update(percentage); + const jQueryContainerElement = $(this.liquidLevelContent.nativeElement); + const fill = jQueryContainerElement.find('.tb-liquid-fill'); + const surfaces = jQueryContainerElement.find('.tb-liquid-surface'); + const surfacePositionAttr = this.shape !== Shapes.vCylinder ? 'y' : 'cy'; + const animationSpeed = 500; + + const levelColor = this.errorsMsg.length ? 'transparent' : this.liquidColor.color; + + if (ignoreAnimation) { + fill.css({y : newY}); + } else { + fill.animate({y : newY}, animationSpeed); + } + fill.attr('fill', levelColor); + + surfaces.each((index, element) => { + const $element = $(element); + if (ignoreAnimation) { + $element.css({[surfacePositionAttr]: newY}); + } else { + $element.animate({[surfacePositionAttr]: newY}, animationSpeed); + } + if ($element.hasClass('tb-liquid')) { + $element.attr('fill', levelColor); + } + }); + } + + private updateShapeColor(value: number): void { + const jQueryContainerElement = $(this.liquidLevelContent.nativeElement); + const shapeStrokes = jQueryContainerElement.find('.tb-shape-stroke'); + const shapeFill = jQueryContainerElement.find('.tb-shape-fill'); + this.tankColor.update(value); + + const shapeColor = this.errorsMsg.length ? '#CACACA' : this.tankColor.color; + + shapeStrokes.each((index, element) => { + $(element).attr('stroke', shapeColor); + }); + + shapeFill.each((index, element) => { + $(element).attr('fill', shapeColor); + }); + } + + private updateValueElement(data: any, percentage: number): void { + let content: string; + let container: JQuery; + const jQueryContainerElement = $(this.liquidLevelContent.nativeElement); + let value = 'N/A'; + + if (isNumeric(data)) { + value = convertLiters(this.convertOutputData(percentage), this.widgetUnits as CapacityUnits, ConversionType.from) + .toFixed(this.settings.decimals || 0); + } + this.valueColor.update(value); + const valueTextStyle = cssTextFromInlineStyle({...inlineTextStyle(this.settings.valueFont), + color: this.valueColor.color}); + this.backgroundOverlayColor.update(percentage); + if (this.overlayContainer) { + this.overlayContainer.attr('fill', this.backgroundOverlayColor.color); + } + + if (this.settings.layout === LevelCardLayout.absolute) { + const volumeInLiters: number = convertLiters(this.volume, this.settings.volumeUnits as CapacityUnits, ConversionType.to); + const volume = convertLiters(volumeInLiters, this.widgetUnits as CapacityUnits, ConversionType.from) + .toFixed(this.settings.decimals || 0); + + const volumeTextStyle = cssTextFromInlineStyle({...inlineTextStyle(this.settings.volumeFont), + color: this.settings.volumeColor}); + + container = jQueryContainerElement.find('.absolute-value-container'); + content = createAbsoluteLayout({inputValue: value, volume}, + {valueStyle: valueTextStyle, volumeStyle: volumeTextStyle}, this.widgetUnits); + + } else if (this.settings.layout === LevelCardLayout.percentage) { + container = jQueryContainerElement.find('.percentage-value-container'); + content = createPercentLayout(value, valueTextStyle); + } + + if (content && container) { + container.html(content); + } + } + + private getTooltipContent(value?: [number, any]): string { + const contentValue = value || [0, '']; + let tooltipValue: string | number = 'N/A'; + + if (isNumeric(contentValue[1])) { + tooltipValue = this.convertTooltipData(contentValue[1]); + } + + this.tooltipLevelColor.update(tooltipValue); + this.tooltipDateFormat.update(contentValue[0]); + + const levelTextStyle = cssTextFromInlineStyle({...inlineTextStyle(this.settings.tooltipLevelFont), + color: this.tooltipLevelColor.color}); + + const dateTextStyle = cssTextFromInlineStyle({...inlineTextStyle(this.settings.tooltipDateFont), + color: this.settings.tooltipDateColor, overflow: 'hidden', 'text-overflow': 'ellipsis', 'white-space': 'nowrap'}); + + let content = `
`; + + if (this.settings.showTooltipLevel) { + const levelValue = typeof tooltipValue == 'number' + ? `${tooltipValue.toFixed(this.settings.tooltipLevelDecimals)} ${this.settings.tooltipUnits}` + : 'N/A'; + content += this.createTooltipContent( + this.ctx.translate.instant('widgets.liquid-level-card.level'), + levelValue, + levelTextStyle + ); + } + + if (this.settings.showTooltipDate) { + content += this.createTooltipContent( + this.ctx.translate.instant('widgets.liquid-level-card.last-update'), + this.tooltipDateFormat.formatted, + dateTextStyle + ); + } + + content += '
'; + + return content; + } + + private createTooltipContent(labelText: string, contentValue: string, textStyle: string): string { + return `
+ + +
`; + } + + private convertInputData(value: any): number { + if (this.settings.datasourceUnits !== CapacityUnits.percent) { + return (convertLiters(Number(value), this.settings.datasourceUnits, ConversionType.to) / + convertLiters(this.volume, this.settings.volumeUnits, ConversionType.to)) * 100; + } + + return Number(value); + } + + private convertOutputData(value: number): number { + if (this.widgetUnits !== CapacityUnits.percent) { + return convertLiters(this.volume * (value / 100), this.settings.volumeUnits, ConversionType.to); + } + + return value; + } + + private convertTooltipData(value: number): number { + const percentage = this.convertInputData(value); + if (this.settings.tooltipUnits !== CapacityUnits.percent) { + const liters = this.convertOutputData(percentage); + + return convertLiters(liters, this.settings.tooltipUnits, ConversionType.from); + } else { + return percentage; + } + } + + public cardClick($event: Event) { + this.ctx.actionsApi.cardClick($event); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.models.ts new file mode 100644 index 0000000000..fcccc78ca9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/liquid-level-widget.models.ts @@ -0,0 +1,623 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + BackgroundSettings, + BackgroundType, + ColorSettings, + constantColor, + cssTextFromInlineStyle, + DateFormatSettings, + Font, + lastUpdateAgoDateFormat +} from '@shared/models/widget-settings.models'; +import { DataKey, WidgetConfig } from '@shared/models/widget.models'; +import { AttributeData, DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { forkJoin, Observable, of } from 'rxjs'; +import { singleEntityFilterFromDeviceId } from '@shared/models/query/query.models'; +import { EntityType } from '@shared/models/entity-type.models'; +import { catchError, map, mergeMap } from 'rxjs/operators'; +import { EntityService } from '@core/http/entity.service'; +import { IAliasController } from '@core/api/widget-api.models'; +import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; +import { ResourcesService } from '@core/services/resources.service'; +import { FormGroup } from '@angular/forms'; + +export interface SvgInfo { + svg: string; + limits: SvgLimits; +} + +export interface SvgLimits { + min: number; + max: number; +} + +export interface LevelCardWidgetSettings extends WidgetConfig { + tankSelectionType: LiquidWidgetDataSourceType; + selectedShape: Shapes; + shapeAttributeName: string; + tankColor: ColorSettings; + datasourceUnits: CapacityUnits; + layout: LevelCardLayout; + volumeSource: LiquidWidgetDataSourceType; + volumeConstant: number; + volumeAttributeName: string; + volumeUnits: CapacityUnits; + volumeFont: Font; + volumeColor: string; + liquidColor: ColorSettings; + valueFont: Font; + widgetUnitsSource: LiquidWidgetDataSourceType; + widgetUnitsAttributeName: string; + valueColor: ColorSettings; + showBackgroundOverlay: boolean; + backgroundOverlayColor: ColorSettings; + showTooltip: boolean; + showTooltipLevel: boolean; + tooltipUnits: CapacityUnits; + tooltipLevelDecimals: number; + tooltipLevelFont: Font; + tooltipLevelColor: ColorSettings; + showTooltipDate: boolean; + tooltipDateFormat: DateFormatSettings; + tooltipDateFont: Font; + tooltipDateColor: string; + tooltipBackgroundColor: string; + tooltipBackgroundBlur: number; + background: BackgroundSettings; +} + +export enum Shapes { + vOval = 'Vertical Oval', + vCylinder = 'Vertical Cylinder', + vCapsule = 'Vertical Capsule', + rectangle = 'Rectangle', + hOval = 'Horizontal Oval', + hEllipse = 'Horizontal Ellipse', + hDishEnds = 'Horizontal Dish Ends', + hCylinder = 'Horizontal Cylinder', + hCapsule = 'Horizontal Capsule', + hElliptical_2_1 = 'Horizontal 2:1 Elliptical' +} + +export enum CapacityUnits { + percent = '%', + liters = 'L', + cubicMillimeter = 'mm³', + cubicCentimeter = 'cm³', + cubicMeter = 'm³', + cubicKilometer = 'km³', + milliliter = 'mL', + hectoliter = 'hl', + cubicInch = 'in³', + cubicFoot = 'ft³', + cubicYard = 'yd³', + fluidOunce = 'fl-oz', + pint = 'pt', + quart = 'qt', + gallon = 'gal', + oilBarrels = 'bbl' +} + +export enum LevelCardLayout { + simple = 'simple', + percentage = 'percentage', + absolute = 'absolute' +} + +export enum LiquidWidgetDataSourceType { + static = 'static', + attribute = 'attribute' +} + +export enum ConversionType { + to = 'to', + from = 'from', +} + +export const svgMapping = new Map( + [ + [ + Shapes.vOval, + { + svg: 'assets/widget/liquid-level/shapes/vertical-oval.svg', + limits: { min: 171, max: 51 } + } + ], + [ + Shapes.vCylinder, + { + svg: 'assets/widget/liquid-level/shapes/vertical-cylinder.svg', + limits: { min: 161, max: 55 } + } + ], + [ + Shapes.vCapsule, + { + svg: 'assets/widget/liquid-level/shapes/vertical-capsule.svg', + limits: { min: 197, max: 25 } + } + ], + [ + Shapes.rectangle, + { + svg: 'assets/widget/liquid-level/shapes/rectangle.svg', + limits: { min: 169, max: 52 } + } + ], + [ + Shapes.hOval, + { + svg: 'assets/widget/liquid-level/shapes/horizontal-oval.svg', + limits: { min: 160, max: 63 } + } + ], + [ + Shapes.hEllipse, + { + svg: 'assets/widget/liquid-level/shapes/horizontal-ellipse.svg', + limits: { min: 160, max: 64 } + } + ], + [ + Shapes.hDishEnds, + { + svg: 'assets/widget/liquid-level/shapes/horizontal-dish-ends.svg', + limits: { min: 173, max: 50 } + } + ], + [ + Shapes.hCylinder, + { + svg: 'assets/widget/liquid-level/shapes/horizontal-cylinder.svg', + limits: { min: 165, max: 56 } + } + ], + [ + Shapes.hCapsule, + { + svg: 'assets/widget/liquid-level/shapes/horizontal-capsule.svg', + limits: { min: 171, max: 52 } + } + ], + [ + Shapes.hElliptical_2_1, + { + svg: 'assets/widget/liquid-level/shapes/horizontal-2_1-elliptical.svg', + limits: { min: 173, max: 50 } + } + ] + ] +); + +export const levelCardLayoutTranslations = new Map( + [ + [LevelCardLayout.simple, 'widgets.liquid-level-card.layout-simple'], + [LevelCardLayout.percentage, 'widgets.liquid-level-card.layout-percentage'], + [LevelCardLayout.absolute, 'widgets.liquid-level-card.layout-absolute'] + ] +); + +export const LiquidWidgetDataSourceTypeTranslations = new Map( + [ + [LiquidWidgetDataSourceType.static, 'widgets.liquid-level-card.static'], + [LiquidWidgetDataSourceType.attribute, 'widgets.liquid-level-card.attribute'] + ] +); + +export const ShapesTranslations = new Map( + [ + [Shapes.vOval, 'widgets.liquid-level-card.v-oval'], + [Shapes.vCylinder, 'widgets.liquid-level-card.v-cylinder'], + [Shapes.vCapsule, 'widgets.liquid-level-card.v-capsule'], + [Shapes.rectangle, 'widgets.liquid-level-card.rectangle'], + [Shapes.hOval, 'widgets.liquid-level-card.h-oval'], + [Shapes.hEllipse, 'widgets.liquid-level-card.h-ellipse'], + [Shapes.hDishEnds, 'widgets.liquid-level-card.h-dish-ends'], + [Shapes.hCylinder, 'widgets.liquid-level-card.h-cylinder'], + [Shapes.hCapsule, 'widgets.liquid-level-card.h-capsule'], + [Shapes.hElliptical_2_1, 'widgets.liquid-level-card.h-elliptical_2_1'] + ] +); + +export const levelCardDefaultSettings: LevelCardWidgetSettings = { + tankSelectionType: LiquidWidgetDataSourceType.static, + selectedShape: Shapes.vCylinder, + shapeAttributeName: 'tankShape', + tankColor: constantColor('#242770'), + datasourceUnits: CapacityUnits.percent, + layout: LevelCardLayout.percentage, + showTitle: false, + title: 'Liquid level', + titleFont: { + family: 'Roboto', + size: 16, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '1.5' + }, + titleColor: 'rgba(0, 0, 0, 0.87)', + showTitleIcon: false, + titleIcon: 'water_drop', + iconColor: '#5469FF', + volumeSource: LiquidWidgetDataSourceType.static, + volumeConstant: 500, + volumeUnits: CapacityUnits.liters, + volumeAttributeName: 'volume', + volumeFont: { + family: 'Roboto', + size: 14, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '100%' + }, + volumeColor: 'rgba(0, 0, 0, 0.18)', + units: CapacityUnits.percent as string, + widgetUnitsSource: LiquidWidgetDataSourceType.static, + widgetUnitsAttributeName: 'units', + decimals: 0, + liquidColor: constantColor('#7A8BFF'), + valueFont: { + family: 'Roboto', + size: 24, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '100%' + }, + valueColor: constantColor('rgba(0, 0, 0, 0.87)'), + showBackgroundOverlay: true, + backgroundOverlayColor: constantColor('rgba(255, 255, 255, 0.76)'), + showTooltip: true, + showTooltipLevel: true, + tooltipUnits: CapacityUnits.percent, + tooltipLevelDecimals: 0, + tooltipLevelFont: { + family: 'Roboto', + size: 13, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '100%' + }, + tooltipLevelColor: constantColor('rgba(0, 0, 0, 0.76)'), + showTooltipDate: true, + tooltipDateFormat: lastUpdateAgoDateFormat(), + tooltipDateFont: { + family: 'Roboto', + size: 13, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '100%' + }, + tooltipDateColor: 'rgba(0, 0, 0, 0.76)', + tooltipBackgroundColor: 'rgba(255, 255, 255, 0.76)', + tooltipBackgroundBlur: 3, + background: { + type: BackgroundType.color, + color: '#fff', + overlay: { + enabled: false, + color: 'rgba(255,255,255,0.72)', + blur: 3 + } + } +}; + +export const convertLiters = (value: number, units: CapacityUnits, conversionType: ConversionType): number => { + let factor: number; + + switch (units) { + case CapacityUnits.liters: + return value; + case CapacityUnits.cubicMillimeter: + factor = 1e6; + break; + case CapacityUnits.cubicCentimeter: + case CapacityUnits.milliliter: + factor = 1e3; + break; + case CapacityUnits.cubicMeter: + factor = 1e-3; + break; + case CapacityUnits.cubicKilometer: + factor = 1e-15; + break; + case CapacityUnits.hectoliter: + factor = 0.01; + break; + case CapacityUnits.cubicInch: + factor = 61.0237; + break; + case CapacityUnits.cubicFoot: + factor = 1 / 28.3168; + break; + case CapacityUnits.cubicYard: + factor = 1 / 764.555; + break; + case CapacityUnits.fluidOunce: + factor = 33.814; + break; + case CapacityUnits.pint: + factor = 1 / 0.473176; + break; + case CapacityUnits.quart: + factor = 1 / 0.946353; + break; + case CapacityUnits.gallon: + factor = 1 / 3.78541; + break; + case CapacityUnits.oilBarrels: + factor = 1 / 158.987; + break; + default: + return value; + // throw new Error(`Unknown unit: ${units}`); + } + + return conversionType === ConversionType.to ? value / factor : value * factor; +}; + +export const extractValue = (attributes: Array, attributeName: string): T | undefined => { + return attributes.find(attr => attr.key === attributeName)?.value; +}; + +export const valueContainerStyleDefaults = cssTextFromInlineStyle({ + width: '100%', + height: '100%', + display: 'flex', + gap: '8px', + 'text-align': 'center', + 'align-items': 'center', + 'align-content': 'center', + 'justify-content': 'center', + 'font-family': 'Roboto, Helvetica Neue, sans-serif' +}); + +export const valueTextStyleDefaults = 'letter-spacing: normal; font-style: normal; font-weight: 500; line-height: 100%;' + + ' font-size: 24px; font-family: Roboto, Helvetica Neue, sans-serif; color: #000000DE'; + +export const volumeTextStyleDefaults = 'letter-spacing: normal; font-style: normal; font-weight: 500;' + + ' line-height: 100%; font-size: 14px; font-family: Roboto, Helvetica Neue, sans-serif; color: rgba(0, 0, 0, 0.18)'; + +export const createAbsoluteLayout = (values?: {inputValue: number | string; volume: number | string}, + styles?: {valueStyle: string; volumeStyle: string}, + units?: string): string => { + const inputValue = values?.inputValue ? values?.inputValue : 500; + const valueTextStyle = styles?.valueStyle ? styles?.valueStyle : valueTextStyleDefaults; + const volume = values?.volume ? values?.volume : 1000; + const volumeTextStyle = styles?.volumeStyle ? styles?.volumeStyle : volumeTextStyleDefaults; + const displayUnits = units ? units : CapacityUnits.liters; + + return `
+
+ +
+ +
+ +
`; +}; + +export const createPercentLayout = (value: number | string = 50, valueTextStyle: string = valueTextStyleDefaults): string => + `
+ +
`; + +export const optionsFilter = (searchText: string): ((key: DataKey) => boolean) => + key => key?.name.toUpperCase().includes(searchText?.toUpperCase()); + +export const fetchEntityKeysForDevice = (deviceId: string, dataKeyTypes: Array, + entityService: EntityService): Observable> => { + const entityFilter = singleEntityFilterFromDeviceId(deviceId); + return entityService.getEntityKeysByEntityFilter( + entityFilter, + dataKeyTypes, [EntityType.DEVICE], + {ignoreLoading: true, ignoreErrors: true} + ).pipe( + catchError(() => of([])) + ); +}; + +export const fetchEntityKeys = (entityAliasId: string, dataKeyTypes: Array, + entityService: EntityService, aliasController: IAliasController): Observable> => + aliasController.getAliasInfo(entityAliasId).pipe( + mergeMap((aliasInfo) => entityService.getEntityKeysByEntityFilter( + aliasInfo.entityFilter, + dataKeyTypes, [], + {ignoreLoading: true, ignoreErrors: true} + )), + catchError(() => of([] as Array)) + ); + + +export const createShapeLayout = (svg: string, layout: LevelCardLayout, sanitizer: DomSanitizer): SafeUrl => { + if (svg && layout) { + const parser = new DOMParser(); + const svgImage = parser.parseFromString(svg, 'image/svg+xml'); + + if (layout === LevelCardLayout.simple) { + svgImage.querySelector('.container-overlay').remove(); + } else if (layout === LevelCardLayout.percentage) { + svgImage.querySelector('.absolute-overlay').remove(); + svgImage.querySelector('.percentage-value-container').innerHTML = createPercentLayout(); + } else { + svgImage.querySelector('.absolute-value-container').innerHTML = createAbsoluteLayout(); + svgImage.querySelector('.percentage-overlay').remove(); + } + + const encodedSvg = encodeURIComponent(svgImage.documentElement.outerHTML); + + return sanitizer.bypassSecurityTrustResourceUrl(`data:image/svg+xml,${encodedSvg}`); + } +}; + +export const loadSvgShapesMapping = (resourcesService: ResourcesService): Observable> => { + const obsArray: Array> = []; + const shapesImageMap: Map = new Map(); + svgMapping.forEach((value, shape) => { + const obs = resourcesService.loadJsonResource(value.svg).pipe( + map((svg) => ({svg, shape})) + ); + + obsArray.push(obs); + }); + + return forkJoin(obsArray).pipe( + map(svgData => { + for (const data of svgData) { + shapesImageMap.set(data.shape, data.svg); + } + return shapesImageMap; + }) + ); +}; + +export const updatedFormSettingsValidators = (formGroup: FormGroup) => { + const datasourceUnits: string = formGroup.get('datasourceUnits').value; + const layout: LevelCardLayout = formGroup.get('layout').value; + const volumeSource: LiquidWidgetDataSourceType = formGroup.get('volumeSource').value; + const widgetUnitsSource: LiquidWidgetDataSourceType = formGroup.get('widgetUnitsSource').value; + const showTooltipLevel: boolean = formGroup.get('showTooltipLevel').value; + const showTooltipDate: boolean = formGroup.get('showTooltipDate').value; + const showTooltip: boolean = formGroup.get('showTooltip').value; + const tankSelectionType: LiquidWidgetDataSourceType = formGroup.get('tankSelectionType').value; + + if (tankSelectionType === LiquidWidgetDataSourceType.static) { + formGroup.get('selectedShape').enable({emitEvent: false}); + formGroup.get('shapeAttributeName').disable({emitEvent: false}); + } else { + formGroup.get('selectedShape').disable({emitEvent: false}); + formGroup.get('shapeAttributeName').enable({emitEvent: false}); + } + + switch (layout) { + case LevelCardLayout.simple: + case LevelCardLayout.percentage: + formGroup.get('widgetUnitsSource').disable({emitEvent: false}); + formGroup.get('units').disable({emitEvent: false}); + formGroup.get('widgetUnitsAttributeName').disable({emitEvent: false}); + + if (datasourceUnits !== CapacityUnits.percent) { + formGroup.get('volumeSource').enable({emitEvent: false}); + formGroup.get('volumeUnits').enable({emitEvent: false}); + if (volumeSource === LiquidWidgetDataSourceType.static) { + formGroup.get('volumeConstant').enable({emitEvent: false}); + formGroup.get('volumeAttributeName').disable({emitEvent: false}); + } else { + formGroup.get('volumeConstant').disable({emitEvent: false}); + formGroup.get('volumeAttributeName').enable({emitEvent: false}); + } + } else { + formGroup.get('volumeSource').disable({emitEvent: false}); + formGroup.get('volumeConstant').disable({emitEvent: false}); + formGroup.get('volumeAttributeName').disable({emitEvent: false}); + formGroup.get('volumeUnits').disable({emitEvent: false}); + } + + if (layout === LevelCardLayout.simple) { + formGroup.get('decimals')?.disable({emitEvent: false}); + formGroup.get('valueFont').disable({emitEvent: false}); + formGroup.get('valueColor').disable({emitEvent: false}); + } else { + formGroup.get('decimals')?.enable({emitEvent: false}); + formGroup.get('valueFont').enable({emitEvent: false}); + formGroup.get('valueColor').enable({emitEvent: false}); + } + + formGroup.get('volumeFont').disable({emitEvent: false}); + formGroup.get('volumeColor').disable({emitEvent: false}); + + break; + case LevelCardLayout.absolute: + formGroup.get('widgetUnitsSource').enable({emitEvent: false}); + if (widgetUnitsSource === LiquidWidgetDataSourceType.static) { + formGroup.get('units').enable({emitEvent: false}); + formGroup.get('widgetUnitsAttributeName').disable({emitEvent: false}); + } else { + formGroup.get('units').disable({emitEvent: false}); + formGroup.get('widgetUnitsAttributeName').enable({emitEvent: false}); + } + + formGroup.get('volumeSource').enable({emitEvent: false}); + formGroup.get('volumeUnits').enable({emitEvent: false}); + if (volumeSource === LiquidWidgetDataSourceType.static) { + formGroup.get('volumeConstant').enable({emitEvent: false}); + formGroup.get('volumeAttributeName').disable({emitEvent: false}); + } else { + formGroup.get('volumeConstant').disable({emitEvent: false}); + formGroup.get('volumeAttributeName').enable({emitEvent: false}); + } + + if (formGroup.get('decimals')) { + formGroup.get('decimals').enable({emitEvent: false}); + } + formGroup.get('valueFont').enable({emitEvent: false}); + formGroup.get('valueColor').enable({emitEvent: false}); + + formGroup.get('volumeFont').enable({emitEvent: false}); + formGroup.get('volumeColor').enable({emitEvent: false}); + + break; + } + + if (showTooltip) { + formGroup.get('showTooltipLevel').enable({emitEvent: false}); + formGroup.get('showTooltipDate').enable({emitEvent: false}); + formGroup.get('tooltipBackgroundColor').enable({emitEvent: false}); + formGroup.get('tooltipBackgroundBlur').enable({emitEvent: false}); + + if (showTooltipLevel) { + formGroup.get('tooltipLevelDecimals').enable({emitEvent: false}); + formGroup.get('tooltipLevelFont').enable({emitEvent: false}); + formGroup.get('tooltipLevelColor').enable({emitEvent: false}); + formGroup.get('tooltipUnits').enable({emitEvent: false}); + } else { + formGroup.get('tooltipUnits').disable({emitEvent: false}); + formGroup.get('tooltipLevelDecimals').disable({emitEvent: false}); + formGroup.get('tooltipLevelFont').disable({emitEvent: false}); + formGroup.get('tooltipLevelColor').disable({emitEvent: false}); + } + + if (showTooltipDate) { + formGroup.get('tooltipDateFormat').enable({emitEvent: false}); + formGroup.get('tooltipDateFont').enable({emitEvent: false}); + formGroup.get('tooltipDateColor').enable({emitEvent: false}); + } else { + formGroup.get('tooltipDateFormat').disable({emitEvent: false}); + formGroup.get('tooltipDateFont').disable({emitEvent: false}); + formGroup.get('tooltipDateColor').disable({emitEvent: false}); + } + } else { + formGroup.get('showTooltipLevel').disable({emitEvent: false}); + formGroup.get('showTooltipDate').disable({emitEvent: false}); + formGroup.get('tooltipBackgroundColor').disable({emitEvent: false}); + formGroup.get('tooltipBackgroundBlur').disable({emitEvent: false}); + + formGroup.get('tooltipUnits').disable({emitEvent: false}); + formGroup.get('tooltipLevelDecimals').disable({emitEvent: false}); + formGroup.get('tooltipLevelFont').disable({emitEvent: false}); + formGroup.get('tooltipLevelColor').disable({emitEvent: false}); + + formGroup.get('tooltipDateFormat').disable({emitEvent: false}); + formGroup.get('tooltipDateFont').disable({emitEvent: false}); + formGroup.get('tooltipDateColor').disable({emitEvent: false}); + } +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/signal-strength-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/signal-strength-widget.component.html new file mode 100644 index 0000000000..f857c186d7 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/signal-strength-widget.component.html @@ -0,0 +1,40 @@ + +
+
+
+ +
+
+
+
+
+
+
+
+
widgets.signal-strength.value
+
{{ tooltipValueText }}
+
+
+
widgets.signal-strength.last-update
+
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/signal-strength-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/signal-strength-widget.component.scss new file mode 100644 index 0000000000..c2c14ffb76 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/signal-strength-widget.component.scss @@ -0,0 +1,90 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.tb-signal-strength-panel { + width: 100%; + height: 100%; + position: relative; + display: flex; + flex-direction: column; + gap: 16px; + padding: 20px 24px 24px 24px; + &.tb-signal-strength-pointer { + cursor: pointer; + } + > div:not(.tb-signal-strength-overlay) { + z-index: 1; + } + .tb-signal-strength-overlay { + position: absolute; + top: 12px; + left: 12px; + bottom: 12px; + right: 12px; + } + .tb-signal-strength-title-panel { + display: flex; + flex-direction: column; + .tb-widget-title { + padding: 0; + } + } + .tb-signal-strength-content { + flex: 1; + min-width: 0; + min-height: 0; + position: relative; + .tb-signal-strength-shape { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + } + .tb-signal-strength-tooltip-container { + z-index: 10; + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + &:hover { + .tb-signal-strength-tooltip { + opacity: 1; + } + } + .tb-signal-strength-tooltip { + position: absolute; + z-index: 20; + pointer-events: none; + border: 1px solid rgba(0, 0, 0, 0.12); + border-radius: 4px; + display: flex; + flex-direction: column; + gap: 8px; + padding: 12px; + opacity: 0; + transition: opacity .35s; + .tb-signal-strength-tooltip-row { + display: flex; + justify-content: space-between; + align-items: center; + align-self: stretch; + gap: 8px; + } + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/signal-strength-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/signal-strength-widget.component.ts new file mode 100644 index 0000000000..f5e63d70dc --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/signal-strength-widget.component.ts @@ -0,0 +1,392 @@ + +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + Input, + OnDestroy, + OnInit, + Renderer2, + TemplateRef, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { + backgroundStyle, + ColorProcessor, + ComponentStyle, + DateFormatProcessor, + getDataKey, + getSingleTsValue, + overlayStyle, + textStyle +} from '@shared/models/widget-settings.models'; +import { WidgetComponent } from '@home/components/widget/widget.component'; +import { formatValue, isDefinedAndNotNull, isNumeric, isUndefinedOrNull } from '@core/utils'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { Element, G, Svg, SVG } from '@svgdotjs/svg.js'; +import { + signalBarActive, + signalStrengthDefaultSettings, + SignalStrengthLayout, + SignalStrengthWidgetSettings +} from '@home/components/widget/lib/indicator/signal-strength-widget.models'; +import tinycolor from 'tinycolor2'; +import { TranslateService } from '@ngx-translate/core'; +import { Observable } from 'rxjs'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { DomSanitizer } from '@angular/platform-browser'; + +const shapeWidth = 149; +const shapeHeight = 113; +const shapeAspect = shapeWidth / shapeHeight; + +@Component({ + selector: 'tb-signal-strength-widget', + templateUrl: './signal-strength-widget.component.html', + styleUrls: ['./signal-strength-widget.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class SignalStrengthWidgetComponent implements OnInit, OnDestroy, AfterViewInit { + + @ViewChild('signalStrengthShape', {static: false}) + signalStrengthShape: ElementRef; + + @ViewChild('signalStrengthTooltip', {static: false}) + signalStrengthTooltip: ElementRef; + + settings: SignalStrengthWidgetSettings; + + @Input() + ctx: WidgetContext; + + @Input() + widgetTitlePanel: TemplateRef; + + layout: SignalStrengthLayout; + + activeBarsColor: ColorProcessor; + + showDate = false; + dateFormat: DateFormatProcessor; + dateStyle: ComponentStyle = {}; + + showTooltip = false; + + tooltipStyle: ComponentStyle = {}; + + showTooltipValue = false; + tooltipValueText = 'N/A'; + tooltipValueLabelStyle: ComponentStyle = { + color: 'rgba(0, 0, 0, 0.38)' + }; + tooltipValueStyle: ComponentStyle = {}; + + showTooltipDate = false; + tooltipDateFormat: DateFormatProcessor; + tooltipDateLabelStyle: ComponentStyle = { + color: 'rgba(0, 0, 0, 0.38)' + }; + tooltipDateStyle: ComponentStyle = {}; + + backgroundStyle$: Observable; + overlayStyle: ComponentStyle = {}; + + shapeResize$: ResizeObserver; + + hasCardClickAction = false; + + private decimals = 0; + private units = ''; + + private drawSvgShapePending = false; + private svgShape: Svg; + private bars: Element[] = Array.from(Array(4), () => null); + private centerTextElement: G; + private lastCenterText: string; + + private inactiveBarsColorHex: string; + private inactiveBarsOpacity: number; + + private rssi = -100; + private noSignal = false; + private noData = false; + + constructor(public widgetComponent: WidgetComponent, + private imagePipe: ImagePipe, + private sanitizer: DomSanitizer, + private translate: TranslateService, + private renderer: Renderer2, + private cd: ChangeDetectorRef) { + } + + ngOnInit(): void { + this.ctx.$scope.signalStrengthWidget = this; + this.settings = {...signalStrengthDefaultSettings, ...this.ctx.settings}; + + this.layout = this.settings.layout; + + this.showDate = this.settings.showDate; + if (this.showDate) { + this.dateFormat = DateFormatProcessor.fromSettings(this.ctx.$injector, this.settings.dateFormat); + this.dateStyle = textStyle(this.settings.dateFont); + this.dateStyle.color = this.settings.dateColor; + } + + this.activeBarsColor = ColorProcessor.fromSettings(this.settings.activeBarsColor); + const inactiveBarsColor = tinycolor(this.settings.inactiveBarsColor); + this.inactiveBarsColorHex = inactiveBarsColor.toHexString(); + this.inactiveBarsOpacity = inactiveBarsColor.getAlpha(); + + this.showTooltip = this.settings.showTooltip && (this.settings.showTooltipValue || this.settings.showTooltipDate); + + if (this.showTooltip) { + this.tooltipStyle = { + background: this.settings.tooltipBackgroundColor, + backdropFilter: `blur(${this.settings.tooltipBackgroundBlur}px)` + }; + } + + this.showTooltipValue = this.showTooltip && this.settings.showTooltipValue; + this.showTooltipDate = this.showTooltip && this.settings.showTooltipDate; + + if (this.showTooltipValue) { + this.decimals = this.ctx.decimals; + this.units = this.ctx.units; + const dataKey = getDataKey(this.ctx.datasources); + if (isDefinedAndNotNull(dataKey?.decimals)) { + this.decimals = dataKey.decimals; + } + if (dataKey?.units) { + this.units = dataKey.units; + } + this.tooltipValueStyle = textStyle(this.settings.tooltipValueFont); + this.tooltipValueStyle.color = this.settings.tooltipValueColor; + this.tooltipValueLabelStyle = {...this.tooltipValueStyle, ...this.tooltipValueLabelStyle}; + } + + if (this.showTooltipDate) { + this.tooltipDateFormat = DateFormatProcessor.fromSettings(this.ctx.$injector, + {...this.settings.tooltipDateFormat, ...{hideLastUpdatePrefix: true}}); + this.tooltipDateStyle = textStyle(this.settings.tooltipDateFont); + this.tooltipDateStyle.color = this.settings.tooltipDateColor; + this.tooltipDateLabelStyle = {...this.tooltipDateStyle, ...this.tooltipDateLabelStyle}; + } + + this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); + this.overlayStyle = overlayStyle(this.settings.background.overlay); + + this.hasCardClickAction = this.ctx.actionsApi.getActionDescriptors('cardClick').length > 0; + } + + ngAfterViewInit() { + if (this.drawSvgShapePending) { + this.drawSvg(); + } + } + + ngOnDestroy() { + if (this.shapeResize$) { + this.shapeResize$.disconnect(); + } + } + + public onInit() { + const borderRadius = this.ctx.$widgetElement.css('borderRadius'); + this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; + if (this.signalStrengthShape) { + this.drawSvg(); + } else { + this.drawSvgShapePending = true; + } + this.cd.detectChanges(); + } + + public onDataUpdated() { + const tsValue = getSingleTsValue(this.ctx.data); + let ts; + let value; + if (tsValue) { + ts = tsValue[0]; + value = tsValue[1]; + } else { + if (this.showTooltipValue) { + this.tooltipValueText = 'N/A'; + } + } + + this.noData = isUndefinedOrNull(value) || !isNumeric(value); + if (!this.noData) { + this.rssi = Number(value); + if (this.showTooltipValue) { + this.tooltipValueText = formatValue(value, this.decimals, this.units, false); + } + } else { + this.rssi = -100; + if (this.showTooltipValue) { + this.tooltipValueText = 'N/A'; + } + } + + this.noSignal = this.rssi <= -100; + + this.activeBarsColor.update(this.rssi); + + if (this.showDate) { + this.dateFormat.update(ts); + } + + if (this.showTooltipDate) { + this.tooltipDateFormat.update(ts); + } + + this.cd.detectChanges(); + this.renderValues(); + } + + public cardClick($event: Event) { + this.ctx.actionsApi.cardClick($event); + } + + private drawSvg() { + this.svgShape = SVG().addTo(this.signalStrengthShape.nativeElement).size(shapeWidth, shapeHeight); + this.renderer.setStyle(this.svgShape.node, 'overflow', 'visible'); + this.renderer.setStyle(this.svgShape.node, 'user-select', 'none'); + + // Draw bars + + if (this.layout === SignalStrengthLayout.wifi) { + + this.bars[0] = this.svgShape.path('m 74.5,101.5 c 4.9706,0 9,-4.0294 9,-9 0,-4.9706 -4.0294,-9 -9,-9 -4.9706,0 ' + + '-9,4.0294 -9,9 0,4.9706 4.0294,9 9,9 z').fill(this.inactiveBarsColorHex).opacity(this.inactiveBarsOpacity); + + this.bars[1] = this.svgShape.path('M 100.475,69.1453 C 93.3268,62.9321 84.0946,59.5 74.5299,59.5 c -9.5647,0 -18.7969,3.4321 ' + + '-25.9455,9.6453 -0.6041,0.5111 -1.0983,1.1344 -1.4539,1.8338 -0.3556,0.6994 -0.5656,1.4609 -0.6177,2.2402 -0.0521,0.7794 ' + + '0.0547,1.5612 0.3142,2.2999 0.2595,0.7386 0.6665,1.4196 1.1974,2.0032 0.5308,0.5836 1.175,1.0582 1.8951,1.3964 0.72,0.3381 ' + + '1.5016,0.533 2.2994,0.5733 0.7977,0.0404 1.5957,-0.0747 2.3476,-0.3384 0.7519,-0.2638 1.4427,-0.671 2.0323,-1.198 ' + + '4.9407,-4.2934 11.321,-6.665 17.9311,-6.665 6.6101,0 12.9904,2.3716 17.9311,6.665 1.1957,1.0392 2.7646,1.5713 ' + + '4.3616,1.4795 1.5969,-0.0919 3.0911,-0.8003 4.1544,-1.9694 1.062,-1.1691 1.607,-2.7031 1.513,-4.2645 ' + + '-0.094,-1.5615 -0.819,-3.0225 -2.015,-4.0616 z').fill(this.inactiveBarsColorHex).opacity(this.inactiveBarsOpacity); + + this.bars[2] = this.svgShape.path('m 74.5,35.5001 c -16.3337,-0.0218 -32.061,5.9702 -43.9755,16.7543 -0.6032,0.5216 ' + + '-1.0925,1.1541 -1.4391,1.8606 -0.3467,0.7065 -0.5436,1.4726 -0.5795,2.2536 -0.0358,0.7809 0.0903,1.5608 0.371,2.2941 ' + + '0.2806,0.7332 0.71,1.4051 1.2632,1.976 0.5531,0.5709 1.2188,1.0295 1.958,1.3489 0.7392,0.3194 1.5371,0.493 2.3467,0.5109 ' + + '0.8097,0.0178 1.6149,-0.1206 2.3684,-0.4071 0.7535,-0.2865 1.4402,-0.7153 2.0197,-1.2613 9.6742,-8.7299 22.4234,-13.5862 ' + + '35.6671,-13.5862 13.2437,0 25.993,4.8563 35.667,13.5862 0.58,0.546 1.266,0.9748 2.02,1.2613 0.753,0.2865 1.559,0.4249 ' + + '2.368,0.4071 0.81,-0.0179 1.608,-0.1915 2.347,-0.5109 0.739,-0.3194 1.405,-0.778 1.958,-1.3489 0.553,-0.5709 0.983,-1.2428 ' + + '1.263,-1.976 0.281,-0.7333 0.407,-1.5132 0.371,-2.2941 -0.036,-0.781 -0.233,-1.5471 -0.579,-2.2536 -0.347,-0.7065 ' + + '-0.836,-1.339 -1.44,-1.8606 ' + + 'C 106.561,41.4703 90.8337,35.4783 74.5,35.5001 Z').fill(this.inactiveBarsColorHex).opacity(this.inactiveBarsOpacity); + + this.bars[3] = this.svgShape.path('M 134.464,33.9289 C 117.976,19.4835 96.6509,11.5 74.5537,11.5 c -22.0972,0 -43.422,7.9835 ' + + '-59.9102,22.4289 -0.6196,0.5198 -1.1273,1.1559 -1.493,1.871 -0.3658,0.7151 -0.5823,1.4948 -0.6368,2.2933 -0.0545,0.7984 ' + + '0.054,1.5995 0.3193,2.3562 0.2652,0.7567 0.6818,1.4536 1.2253,2.05 0.5434,0.5963 1.2028,1.08 1.9393,1.4225 0.7366,0.3426 ' + + '1.5354,0.5372 2.3496,0.5723 0.8142,0.0351 1.6273,-0.0899 2.3916,-0.3677 0.7643,-0.2778 1.4643,-0.7028 2.0589,-1.25 ' + + '14.2455,-12.4752 32.6666,-19.3694 51.7545,-19.3694 19.0879,0 37.5088,6.8942 51.7548,19.3694 1.209,1.0617 2.798,1.6084 ' + + '4.418,1.5196 1.619,-0.0887 3.137,-0.8056 4.218,-1.9929 1.081,-1.1873 1.638,-2.7477 1.547,-4.338 -0.09,-1.5903 ' + + '-0.82,-3.0802 -2.029,-4.142 z').fill(this.inactiveBarsColorHex).opacity(this.inactiveBarsOpacity); + } else { + this.bars[0] = this.svgShape.rect(18, 37).radius(4).move(19, 75).fill(this.inactiveBarsColorHex).opacity(this.inactiveBarsOpacity); + this.bars[1] = this.svgShape.rect(18, 61).radius(4).move(50, 50).fill(this.inactiveBarsColorHex).opacity(this.inactiveBarsOpacity); + this.bars[2] = this.svgShape.rect(18, 86).radius(4).move(80, 26).fill(this.inactiveBarsColorHex).opacity(this.inactiveBarsOpacity); + this.bars[3] = this.svgShape.rect(18, 111).radius(4).move(111, 1).fill(this.inactiveBarsColorHex).opacity(this.inactiveBarsOpacity); + } + + this.shapeResize$ = new ResizeObserver(() => { + this.onResize(); + }); + this.shapeResize$.observe(this.signalStrengthShape.nativeElement); + this.onResize(); + + this.renderValues(); + } + + private renderValues() { + if (this.svgShape) { + const activeBarsColor = tinycolor(this.activeBarsColor.color); + const activeBarsColorHex = activeBarsColor.toHexString(); + const activeBarsOpacity = activeBarsColor.getAlpha(); + for (let index = 0; index < this.bars.length; index++) { + const bar = this.bars[index]; + const active = signalBarActive(this.rssi, index); + const newFill = active ? activeBarsColorHex : this.inactiveBarsColorHex; + const newOpacity = active ? activeBarsOpacity : this.inactiveBarsOpacity; + if (newFill !== bar.fill() || newOpacity !== bar.opacity()) { + bar.timeline().finish(); + bar.animate(200).attr({fill: newFill, opacity: newOpacity}); + } + } + const centerText = this.noData ? 'N/A' : (this.noSignal ? this.translate.instant('widgets.signal-strength.no-signal') : ''); + this.renderCenterText(centerText); + } + } + + private renderCenterText(text: string) { + if (this.lastCenterText !== text) { + if (this.centerTextElement) { + this.centerTextElement.remove(); + this.centerTextElement = null; + } + if (text) { + this.centerTextElement = this.svgShape.group(); + const textElement = this.centerTextElement.text(text).font( + { + family: 'Roboto', + weight: '500', + style: 'normal', + size: '12px', + leading: 1.333 + } + ).fill('#fff'); + const bounds = textElement.bbox(); + this.centerTextElement.rect(bounds.width + 16, bounds.height + 8) + .move(bounds.x - 8, bounds.y - 4).radius(4).fill('#848484').insertBefore(textElement); + this.centerTextElement.center(shapeWidth / 2, shapeHeight / 2); + } + this.lastCenterText = text; + } + } + + private onResize() { + const shapeContainerWidth = this.signalStrengthShape.nativeElement.getBoundingClientRect().width; + const shapeContainerHeight = this.signalStrengthShape.nativeElement.getBoundingClientRect().height; + const shapeContainerAspect = shapeContainerWidth / shapeContainerHeight; + let scale: number; + if (shapeAspect >= shapeContainerAspect) { + scale = shapeContainerWidth / shapeWidth; + } else { + scale = shapeContainerHeight / shapeHeight; + } + this.renderer.setStyle(this.svgShape.node, 'transform', `scale(${scale})`); + if (this.showTooltip) { + const targetWidth = shapeContainerWidth; + const minAspect = 0.45; + const aspect = Math.min(shapeContainerHeight / targetWidth, minAspect); + const targetHeight = targetWidth * aspect; + const height = 80; + scale = targetHeight / height; + const width = targetWidth / scale; + this.renderer.setStyle(this.signalStrengthTooltip.nativeElement, 'width', width + 'px'); + this.renderer.setStyle(this.signalStrengthTooltip.nativeElement, 'transform', `scale(${scale})`); + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/signal-strength-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/signal-strength-widget.models.ts new file mode 100644 index 0000000000..59c782f5d4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/signal-strength-widget.models.ts @@ -0,0 +1,141 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + BackgroundSettings, + BackgroundType, + ColorSettings, + ColorType, + DateFormatSettings, + defaultColorFunction, + Font, + lastUpdateAgoDateFormat +} from '@shared/models/widget-settings.models'; + +export enum SignalStrengthLayout { + wifi = 'wifi', + cellular_bar = 'cellular_bar' +} + +export const signalStrengthLayouts = Object.keys(SignalStrengthLayout) as SignalStrengthLayout[]; + +export const signalStrengthLayoutTranslations = new Map( + [ + [SignalStrengthLayout.wifi, 'widgets.signal-strength.layout-wifi'], + [SignalStrengthLayout.cellular_bar, 'widgets.signal-strength.layout-cellular-bar'] + ] +); + +export const signalStrengthLayoutImages = new Map( + [ + [SignalStrengthLayout.wifi, 'assets/widget/signal-strength/wifi-layout.svg'], + [SignalStrengthLayout.cellular_bar, 'assets/widget/signal-strength/cellular-bar-layout.svg'] + ] +); + +export interface SignalStrengthWidgetSettings { + layout: SignalStrengthLayout; + showDate: boolean; + dateFormat: DateFormatSettings; + dateFont: Font; + dateColor: string; + activeBarsColor: ColorSettings; + inactiveBarsColor: string; + showTooltip: boolean; + showTooltipValue: boolean; + tooltipValueFont: Font; + tooltipValueColor: string; + showTooltipDate: boolean; + tooltipDateFormat: DateFormatSettings; + tooltipDateFont: Font; + tooltipDateColor: string; + tooltipBackgroundColor: string; + tooltipBackgroundBlur: number; + background: BackgroundSettings; +} + +export const signalStrengthDefaultSettings: SignalStrengthWidgetSettings = { + layout: SignalStrengthLayout.wifi, + showDate: false, + dateFormat: lastUpdateAgoDateFormat(), + dateFont: { + family: 'Roboto', + size: 12, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '16px' + }, + dateColor: 'rgba(0, 0, 0, 0.38)', + activeBarsColor: { + color: 'rgba(92, 223, 144, 1)', + type: ColorType.range, + rangeList: [ + {to: -85, color: 'rgba(227, 71, 71, 1)'}, + {from: -85, to: -70, color: 'rgba(255, 122, 0, 1)'}, + {from: -70, to: -55, color: 'rgba(246, 206, 67, 1)'}, + {from: -55, color: 'rgba(92, 223, 144, 1)'} + ], + colorFunction: defaultColorFunction + }, + inactiveBarsColor: 'rgba(224, 224, 224, 1)', + showTooltip: true, + showTooltipValue: true, + tooltipValueFont: { + family: 'Roboto', + size: 13, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '16px' + }, + tooltipValueColor: 'rgba(0,0,0,0.76)', + showTooltipDate: true, + tooltipDateFormat: lastUpdateAgoDateFormat(), + tooltipDateFont: { + family: 'Roboto', + size: 13, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '16px' + }, + tooltipDateColor: 'rgba(0,0,0,0.76)', + tooltipBackgroundColor: 'rgba(255,255,255,0.72)', + tooltipBackgroundBlur: 3, + background: { + type: BackgroundType.color, + color: '#fff', + overlay: { + enabled: false, + color: 'rgba(255,255,255,0.72)', + blur: 3 + } + } +}; + +export const signalBarActive = (rssi: number, index: number): boolean => { + switch (index) { + case 0: + return rssi > -100; + case 1: + return rssi >= -85; + case 2: + return rssi >= -70; + case 3: + return rssi >= -55; + } +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/json-input-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/json-input-widget.component.html index 24fe07d92b..40636a9d1b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/json-input-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/json-input-widget.component.html @@ -26,14 +26,14 @@
-
+
- - - {{ getErrorMessageText(key.settings,'required') }} - - - {{ getErrorMessageText(key.settings,'invalidJSON') | translate }} - - -
-
- - - {{key.settings.icon}} - - icon - - - {{key.label}} - -
-
- - {{key.label}} - - - - - {{ getErrorMessageText(key.settings, 'required') }} - - - {{ getErrorMessageText(key.settings, 'invalidDate') }} - - -
-
- - {{key.label}} - - - {{ getCustomTranslationText(option.label ? option.label : option.value) }} - - - - {{ getErrorMessageText(key.settings, 'required') }} - - -
-
+
+ + + {{key.label}} + + + + + + {{ getErrorMessageText(key.settings, 'required') }} + + + + {{key.label}} + + + + + + {{ getErrorMessageText(key.settings,'required') }} + + + {{ getErrorMessageText(key.settings,'min') }} + + + {{ getErrorMessageText(key.settings,'max') }} + + + + {{key.label}} + + + + + + + {{ getErrorMessageText(key.settings,'required') }} + + + {{ getErrorMessageText(key.settings,'invalidJSON') | translate }} + + + + {{key.label}} + + + + + {{ getErrorMessageText(key.settings, 'required') }} + + + {{ getErrorMessageText(key.settings, 'invalidDate') }} + + + + {{key.label}} + + + {{ getCustomTranslationText(option.label ? option.label : option.value) }} + + + + + + + {{ getErrorMessageText(key.settings, 'required') }} + + + + {{key.label}} + + + + + + + + {{ getErrorMessageText(key.settings, 'required') }} + + + + + + + {{key.label}} + + + + + + {{key.label}} + +
-
-
-
+
+
{{ 'widgets.input-widgets.no-entity-selected' | translate }}
-
+
{{ 'widgets.input-widgets.not-allowed-entity' | translate }}
+ + {{key.settings.icon}} + + icon + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.scss index d0dd324e52..b47ae689a3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.scss @@ -21,14 +21,14 @@ flex-direction: column; .tb-multiple-input-container { - padding: 0 8px; + padding: 8px 8px 0; flex: 1 1 100%; overflow-x: hidden; overflow-y: auto; } .fields-group { - padding: 0 8px; + padding: 8px 8px 0; margin: 10px 0; border: 1px groove rgba(0, 0, 0, .25); @@ -37,12 +37,8 @@ } } - .input-field { - padding-right: 10px; - - mat-form-field { - margin-bottom: 5px; - } + .tb-multiple-input-layout { + display: grid; } .mat-mdc-slide-toggle { @@ -51,33 +47,61 @@ margin-bottom: 8px; } - .date-time-input { - .mat-mdc-form-field { - width: 100%; - margin: 2px 0; + &--buttons-container { + display: flex; + flex-direction: row; + align-items: center; + justify-content: end; + &__button { + max-height: 50px; + margin-right:20px; } } - .vertical-alignment { + &--errors-container { + height: 100%; + display: flex; flex-direction: column; + align-items: center; + justify-content: center; + &__error { + text-align: center; + font-size: 18px; + color: #a0a0a0; + } } } } :host ::ng-deep { .tb-multiple-input { - .mat-mdc-slide-toggle .mdc-form-field { - width: 100%; - & > label { - display: flex; - align-items: center; + .mat-mdc-slide-toggle, .mat-mdc-checkbox { + .mdc-form-field { width: 100%; - margin: 0; - .mat-icon { - margin-left: 8px; - margin-right: 8px; + & > label { + display: flex; + align-items: center; + width: 100%; + margin: 0; + .mat-icon { + margin-right: 8px; + } + } + } + } + .mat-mdc-slide-toggle { + .mdc-form-field { + & > label { + .mat-icon { + margin-left: 8px; + } } } } + .color-input { + .mat-mdc-form-field-icon-suffix { + padding: 0 8px 0 4px; + } + } } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts index 534be7c676..6b4feda426 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts @@ -48,14 +48,15 @@ import { } from '@shared/components/dialog/json-object-edit-dialog.component'; import { MatDialog } from '@angular/material/dialog'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; +import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field'; type FieldAlignment = 'row' | 'column'; type MultipleInputWidgetDataKeyType = 'server' | 'shared' | 'timeseries'; export type MultipleInputWidgetDataKeyValueType = 'string' | 'double' | 'integer' | 'JSON' | 'booleanCheckbox' | 'booleanSwitch' | - 'dateTime' | 'date' | 'time' | 'select'; -type MultipleInputWidgetDataKeyEditableType = 'editable' | 'disabled' | 'readonly'; + 'dateTime' | 'date' | 'time' | 'select' | 'color'; +export type MultipleInputWidgetDataKeyEditableType = 'editable' | 'disabled' | 'readonly'; type ConvertGetValueFunction = (value: any, ctx: WidgetContext) => any; type ConvertSetValueFunction = (value: any, originValue: any, ctx: WidgetContext) => any; @@ -71,6 +72,8 @@ interface MultipleInputWidgetSettings { groupTitle: string; fieldsAlignment: FieldAlignment; fieldsInRow: number; + columnGap: number; + rowGap: number; attributesShared?: boolean; } @@ -88,6 +91,8 @@ interface MultipleInputWidgetDataKeySettings { isEditable: MultipleInputWidgetDataKeyEditableType; disabledOnDataKey: string; dataKeyHidden: boolean; + appearance: MatFormFieldAppearance; + subscriptSizing: SubscriptSizing; step?: number; minValue?: number; maxValue?: number; @@ -98,8 +103,8 @@ interface MultipleInputWidgetDataKeySettings { invalidJsonErrorMessage?: string; useCustomIcon: boolean; icon: string; - customIcon: string ; - safeCustomIcon?: SafeUrl; + customIcon: string; + customIconUrl?: string; inputTypeNumber?: boolean; readOnly?: boolean; disabledOnCondition?: boolean; @@ -148,8 +153,7 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni private isSavingInProgress = false; isVerticalAlignment: boolean; - inputWidthSettings: string; - changeAlignment: boolean; + columns: number; saveButtonLabel: string; resetButtonLabel: string; @@ -202,10 +206,7 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni private initializeConfig() { if (this.settings.widgetTitle && this.settings.widgetTitle.length) { - const titlePatternText = this.utils.customTranslation(this.settings.widgetTitle, this.settings.widgetTitle); - this.ctx.widgetTitle = createLabelFromDatasource(this.datasources[0], titlePatternText); - } else { - this.ctx.widgetTitle = this.ctx.widgetConfig.title; + this.ctx.widgetTitle = this.settings.widgetTitle; } this.settings.groupTitle = this.settings.groupTitle || '${entityName}'; @@ -231,15 +232,15 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni if (isUndefined(this.settings.fieldsInRow)) { this.settings.fieldsInRow = 2; } - // For backward compatibility - this.isVerticalAlignment = !(this.settings.fieldsAlignment === 'row'); - - if (!this.isVerticalAlignment && this.settings.fieldsInRow) { - this.inputWidthSettings = 100 / this.settings.fieldsInRow + '%'; + if (isUndefined(this.settings.columnGap)) { + this.settings.columnGap = 10; + } + if (isUndefined(this.settings.rowGap)) { + this.settings.rowGap = 5; } - this.updateWidgetDisplaying(); + this.updateColumns(); } private updateDatasources() { @@ -287,6 +288,15 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni dataKey.settings.isEditable = 'editable'; } } + + if (isUndefined(dataKey.settings.appearance)) { + dataKey.settings.appearance = 'outline'; + } + + if (isUndefined(dataKey.settings.subscriptSizing)) { + dataKey.settings.subscriptSizing = 'fixed'; + } + // For backward compatibility if (dataKey.settings.dataKeyValueType === 'select') { @@ -302,7 +312,7 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni } if (dataKey.settings.useCustomIcon && isDefinedAndNotNull(dataKey.settings.customIcon)) { - dataKey.settings.safeCustomIcon = this.sanitizer.bypassSecurityTrustUrl(dataKey.settings.customIcon); + dataKey.settings.customIconUrl = dataKey.settings.customIcon; } if (dataKey.settings.useGetValueFunction && dataKey.settings.getValueFunctionBody.length) { @@ -449,9 +459,8 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni } if (key.settings.isEditable === 'editable' && key.settings.disabledOnDataKey) { - const conditions = data.filter((item) => { - return source.datasource === item.datasource && item.dataKey.name === key.settings.disabledOnDataKey; - }); + const conditions = data.filter((item) => + source.datasource === item.datasource && item.dataKey.name === key.settings.disabledOnDataKey); if (conditions && conditions.length) { if (conditions[0].data.length) { if (conditions[0].data[0][1] === 'false') { @@ -491,8 +500,17 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni return data; } - private updateWidgetDisplaying() { - this.changeAlignment = (this.ctx.$container && this.ctx.$container[0].offsetWidth < 620); + private updateColumns() { + const changeAlignment = (this.ctx.$container && this.ctx.$container[0].offsetWidth < 620); + if (changeAlignment) { + this.columns = 1; + } else { + if (!this.isVerticalAlignment && this.settings.fieldsInRow) { + this.columns = this.settings.fieldsInRow; + } else { + this.columns = 1; + } + } } public onDataUpdated() { @@ -501,7 +519,7 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni } private resize() { - this.updateWidgetDisplaying(); + this.updateColumns(); this.ctx.detectChanges(); } @@ -586,7 +604,8 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni } public inputChanged(source: MultipleInputWidgetSource, key: MultipleInputWidgetDataKey) { - if (!this.settings.showActionButtons && !this.isSavingInProgress && this.multipleInputFormGroup.get(key.formId).valid) { + const control = this.multipleInputFormGroup.get(key.formId); + if (!this.settings.showActionButtons && !this.isSavingInProgress && (Array.isArray(control.value) || control.valid)) { this.isSavingInProgress = true; const dataToSave: MultipleInputWidgetSource = { datasource: source.datasource, @@ -596,6 +615,13 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni } } + public colorChanged(source: MultipleInputWidgetSource, key: MultipleInputWidgetDataKey, color: string) { + this.multipleInputFormGroup.get(key.formId).setValue(color); + this.multipleInputFormGroup.get(key.formId).markAsDirty(); + this.multipleInputFormGroup.get(key.formId).markAsTouched(); + this.inputChanged(source, key); + } + public saveForm() { if (this.settings.showActionButtons) { this.save(); @@ -734,7 +760,7 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni this.multipleInputFormGroup.reset(undefined, {emitEvent: false}); this.sources.forEach((source) => { for (const key of this.visibleKeys(source)) { - this.multipleInputFormGroup.get(key.formId).patchValue(key.value, {emitEvent: false}); + this.multipleInputFormGroup.get(key.formId).patchValue(key.value); } }); this.multipleInputFormGroup.markAsPristine(); @@ -750,20 +776,33 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { jsonValue: formControl.value, + required: key.settings.required, title: key.settings.dialogTitle, saveLabel: key.settings.saveButtonLabel, cancelLabel: key.settings.cancelButtonLabel } }).afterClosed().subscribe( (res) => { - if (!isEqual(res, formControl.value)) { + if (isDefined(res) && !isEqual(res, formControl.value)) { formControl.patchValue(res); formControl.markAsDirty(); - if(!this.settings.showActionButtons) { + if (!this.settings.showActionButtons) { this.inputChanged(source, key); } } } ); } + + invalid(): boolean { + for (const source of this.sources) { + for (const key of this.visibleKeys(source)) { + const control = this.multipleInputFormGroup.get(key.formId); + if (!Array.isArray(control.value) && control.invalid) { + return true; + } + } + } + return false; + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/navigation-card-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/navigation-card-widget.component.html index 0d33c487b7..77da2cabbb 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/navigation-card-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/navigation-card-widget.component.html @@ -16,6 +16,6 @@ --> - {{settings.icon}} + {{settings.icon}} {{translatedName}} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/navigation-card-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/navigation-card-widget.component.scss index a04f82dce7..b9c3e034a7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/navigation-card-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/navigation-card-widget.component.scss @@ -31,7 +31,7 @@ display: flex; flex-direction: column; align-items: center; - mat-icon { + .mat-icon { margin: auto !important; } span.mdc-button__label { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/navigation-cards-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/navigation-cards-widget.component.html index dbd4d50981..d63a4ef76f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/navigation-cards-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/navigation-cards-widget.component.html @@ -25,8 +25,7 @@ - {{place.icon}} - + {{place.icon}} {{place.name}} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/navigation-cards-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/navigation-cards-widget.component.scss index f3710fbc81..7d41450655 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/navigation-cards-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/navigation-cards-widget.component.scss @@ -55,7 +55,7 @@ display: flex; flex-direction: column; align-items: center; - mat-icon { + .mat-icon { margin: auto; } span.mdc-button__label { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.html index 93b0742930..66f19f717a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.html @@ -80,7 +80,7 @@ matTooltip="{{ actionDescriptor.displayName }}" matTooltipPosition="above" (click)="onActionButtonClick($event, column, actionDescriptor)"> - {{ actionDescriptor.icon }} + {{ actionDescriptor.icon }}
@@ -88,14 +88,14 @@ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarm-count-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarm-count-widget-settings.component.html new file mode 100644 index 0000000000..ab8e7635dd --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarm-count-widget-settings.component.html @@ -0,0 +1,23 @@ + + +
+
widgets.alarm-count.alarm-count-card-style
+ +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarm-count-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarm-count-widget-settings.component.ts new file mode 100644 index 0000000000..6a36891c7e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarm-count-widget-settings.component.ts @@ -0,0 +1,62 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { countDefaultSettings } from '@home/components/widget/lib/count/count-widget.models'; + +@Component({ + selector: 'tb-alarm-count-widget-settings', + templateUrl: './alarm-count-widget-settings.component.html', + styleUrls: ['./../widget-settings.scss'] +}) +export class AlarmCountWidgetSettingsComponent extends WidgetSettingsComponent { + + alarmCountWidgetSettingsForm: UntypedFormGroup; + + constructor(protected store: Store, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.alarmCountWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return {...countDefaultSettings(true)}; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.alarmCountWidgetSettingsForm = this.fb.group({ + alarmCountSettings: [settings.alarmCountSettings, []], + }); + } + + protected prepareInputSettings(settings: WidgetSettings): WidgetSettings { + return { + alarmCountSettings: settings + }; + } + + protected prepareOutputSettings(settings: any): WidgetSettings { + return settings.alarmCountSettings; + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-key-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-key-settings.component.html index a2ccedb34c..f408c6d93e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-key-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-key-settings.component.html @@ -15,21 +15,56 @@ limitations under the License. --> -
- - widgets.table.custom-title - - - - widgets.table.column-width - - -
- widgets.table.cell-style + +
+
widgets.table.column-settings
+
+
{{ 'widgets.table.custom-title' | translate }}
+ + + +
+
+
{{ 'widgets.table.column-width' | translate }}
+ + + +
+
+
{{ 'widgets.table.default-column-visibility' | translate }}
+ + + + {{ 'widgets.table.column-visibility-visible' | translate }} + + + {{ 'widgets.table.column-visibility-hidden' | translate }} + + + {{ 'widgets.table.column-visibility-hidden-mobile' | translate }} + + + +
+
+
{{ 'widgets.table.column-selection-to-display' | translate }}
+ + + + {{ 'widgets.table.column-selection-to-display-enabled' | translate }} + + + {{ 'widgets.table.column-selection-to-display-disabled' | translate }} + + + +
+
+
- - + {{ 'widgets.table.use-cell-style-function' | translate }} @@ -48,13 +83,12 @@ -
-
- widgets.table.cell-content +
+
- - + {{ 'widgets.table.use-cell-content-function' | translate }} @@ -73,27 +107,5 @@ - - - widgets.table.default-column-visibility - - - {{ 'widgets.table.column-visibility-visible' | translate }} - - - {{ 'widgets.table.column-visibility-hidden' | translate }} - - - - - widgets.table.column-selection-to-display - - - {{ 'widgets.table.column-selection-to-display-enabled' | translate }} - - - {{ 'widgets.table.column-selection-to-display-disabled' | translate }} - - - - +
+ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.html index 534f12493d..e304def2c8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.html @@ -15,38 +15,65 @@ limitations under the License. --> -
-
- widgets.table.common-table-settings - - widgets.table.alarms-table-title - - -
-
- - {{ 'widgets.table.enable-alarms-selection' | translate }} - - - {{ 'widgets.table.enable-alarms-search' | translate }} - - - {{ 'widgets.table.enable-select-column-display' | translate }} - - - {{ 'widgets.table.enable-alarm-filter' | translate }} - -
-
- - {{ 'widgets.table.enable-sticky-header' | translate }} - - - {{ 'widgets.table.enable-sticky-action' | translate }} - -
-
- + +
+
widgets.table.table-header
+
+
{{ 'widgets.table.alarms-table-title' | translate }}
+ + + +
+
+ + {{ 'widgets.table.enable-sticky-header' | translate }} + +
+
+
widgets.table.header-buttons
+ + {{ 'widgets.table.enable-alarms-search' | translate }} + + + {{ 'widgets.table.enable-select-column-display' | translate }} + + + {{ 'widgets.table.enable-alarm-filter' | translate }} + +
+
+
+
widgets.table.columns
+ + {{ 'widgets.table.enable-alarms-selection' | translate }} + +
+ + {{ 'widgets.table.enable-sticky-action' | translate }} + + + {{ 'widgets.table.show-cell-actions-menu-mobile' | translate }} + +
+
+
widgets.table.table-buttons
+ + {{ 'widgets.table.display-alarm-activity' | translate }} + + + {{ 'widgets.table.display-alarm-details' | translate }} + + + {{ 'widgets.table.allow-alarms-assign' | translate }} + + + {{ 'widgets.table.allow-alarms-ack' | translate }} + + + {{ 'widgets.table.allow-alarms-clear' | translate }} + +
+ widgets.table.hidden-cell-button-display-mode @@ -57,48 +84,34 @@ -
- - {{ 'widgets.table.display-alarm-activity' | translate }} - - - {{ 'widgets.table.display-alarm-details' | translate }} - - - {{ 'widgets.table.allow-alarms-ack' | translate }} - - - {{ 'widgets.table.allow-alarms-clear' | translate }} - - - {{ 'widgets.table.allow-alarms-assign' | translate }} - -
- - {{ 'widgets.table.display-pagination' | translate }} - - - widgets.table.default-page-size - - -
- - widgets.table.default-sort-order - + + widgets.table.default-sort-order + + +
+
+
widgets.table.pagination
+ + {{ 'widgets.table.display-pagination' | translate }} + +
+
widgets.table.default-page-size
+ + -
- -
- widgets.table.row-style +
+ +
+
widgets.table.rows
- {{ 'widgets.table.use-row-style-function' | translate }} - + widget-config.advanced-settings @@ -112,5 +125,5 @@ - - +
+ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.ts index 73dd5bfec0..fff6e2c8fc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component.ts @@ -47,11 +47,13 @@ export class AlarmsTableWidgetSettingsComponent extends WidgetSettingsComponent enableFilter: true, enableStickyHeader: true, enableStickyAction: true, + showCellActionsMenu: true, reserveSpaceForHiddenAction: 'true', displayDetails: true, allowAcknowledgment: true, allowClear: true, allowAssign: true, + displayActivity: true, displayPagination: true, defaultPageSize: 10, defaultSortOrder: '-createdTime', @@ -69,6 +71,7 @@ export class AlarmsTableWidgetSettingsComponent extends WidgetSettingsComponent enableFilter: [settings.enableFilter, []], enableStickyHeader: [settings.enableStickyHeader, []], enableStickyAction: [settings.enableStickyAction, []], + showCellActionsMenu: [settings.showCellActionsMenu, []], reserveSpaceForHiddenAction: [settings.reserveSpaceForHiddenAction, []], displayDetails: [settings.displayDetails, []], allowAcknowledgment: [settings.allowAcknowledgment, []], diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-key-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-key-settings.component.html new file mode 100644 index 0000000000..30e0a7ed15 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-key-settings.component.html @@ -0,0 +1,48 @@ + + +
+
widgets.aggregated-value-card.value-appearance
+
+
{{ 'widgets.aggregated-value-card.position' | translate }}
+ + + + {{ aggregatedValueCardKeyPositionTranslationMap.get(position) | translate }} + + + +
+
+
{{ 'widgets.aggregated-value-card.font' | translate }}
+ + +
+
+
{{ 'widgets.aggregated-value-card.color' | translate }}
+ + +
+
+ + {{ 'widgets.aggregated-value-card.display-up-down-arrow' | translate }} + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-key-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-key-settings.component.ts new file mode 100644 index 0000000000..e527c3b3d4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-key-settings.component.ts @@ -0,0 +1,64 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { + aggregatedValueCardDefaultKeySettings, + AggregatedValueCardKeyPosition, + aggregatedValueCardKeyPositionTranslations +} from '@home/components/widget/lib/cards/aggregated-value-card.models'; +import { constantColor } from '@shared/models/widget-settings.models'; + +@Component({ + selector: 'tb-aggregated-value-card-key-settings', + templateUrl: './aggregated-value-card-key-settings.component.html', + styleUrls: ['./../widget-settings.scss'] +}) +export class AggregatedValueCardKeySettingsComponent extends WidgetSettingsComponent { + + aggregatedValueCardKeyPositions: AggregatedValueCardKeyPosition[] = + Object.keys(AggregatedValueCardKeyPosition).map(value => AggregatedValueCardKeyPosition[value]); + + aggregatedValueCardKeyPositionTranslationMap = aggregatedValueCardKeyPositionTranslations; + + aggregatedValueCardKeySettingsForm: UntypedFormGroup; + + constructor(protected store: Store, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.aggregatedValueCardKeySettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return {...aggregatedValueCardDefaultKeySettings}; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.aggregatedValueCardKeySettingsForm = this.fb.group({ + position: [settings.position, []], + font: [settings.font, []], + color: [settings.color, []], + showArrow: [settings.showArrow, []] + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-widget-settings.component.html new file mode 100644 index 0000000000..2e196f0598 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-widget-settings.component.html @@ -0,0 +1,70 @@ + + +
+
widgets.aggregated-value-card.aggregated-value-card-style
+
+ + {{ 'widgets.aggregated-value-card.auto-scale' | translate }} + +
+
+ + {{ 'widgets.aggregated-value-card.subtitle' | translate }} + +
+ + + + + + + +
+
+
+ + {{ 'widgets.value-card.date' | translate }} + +
+ + + + + +
+
+
+ + {{ 'widgets.aggregated-value-card.chart' | translate }} + +
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-widget-settings.component.ts new file mode 100644 index 0000000000..d11ac6c8c8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/aggregated-value-card-widget-settings.component.ts @@ -0,0 +1,112 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, Injector } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { DateFormatProcessor, DateFormatSettings } from '@shared/models/widget-settings.models'; +import { aggregatedValueCardDefaultSettings } from '@home/components/widget/lib/cards/aggregated-value-card.models'; + +@Component({ + selector: 'tb-aggregated-value-card-widget-settings', + templateUrl: './aggregated-value-card-widget-settings.component.html', + styleUrls: [] +}) +export class AggregatedValueCardWidgetSettingsComponent extends WidgetSettingsComponent { + + aggregatedValueCardWidgetSettingsForm: UntypedFormGroup; + + datePreviewFn = this._datePreviewFn.bind(this); + + constructor(protected store: Store, + private $injector: Injector, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.aggregatedValueCardWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return {...aggregatedValueCardDefaultSettings}; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.aggregatedValueCardWidgetSettingsForm = this.fb.group({ + + autoScale: [settings.autoScale, []], + showSubtitle: [settings.showSubtitle, []], + subtitle: [settings.subtitle, []], + subtitleFont: [settings.subtitleFont, []], + subtitleColor: [settings.subtitleColor, []], + + showDate: [settings.showDate, []], + dateFormat: [settings.dateFormat, []], + dateFont: [settings.dateFont, []], + dateColor: [settings.dateColor, []], + + showChart: [settings.showChart, []], + + background: [settings.background, []] + }); + } + + protected validatorTriggers(): string[] { + return ['showSubtitle', 'showDate']; + } + + protected updateValidators(emitEvent: boolean) { + const showSubtitle: boolean = this.aggregatedValueCardWidgetSettingsForm.get('showSubtitle').value; + const showDate: boolean = this.aggregatedValueCardWidgetSettingsForm.get('showDate').value; + + if (showSubtitle) { + this.aggregatedValueCardWidgetSettingsForm.get('subtitle').enable(); + this.aggregatedValueCardWidgetSettingsForm.get('subtitleFont').enable(); + this.aggregatedValueCardWidgetSettingsForm.get('subtitleColor').enable(); + } else { + this.aggregatedValueCardWidgetSettingsForm.get('subtitle').disable(); + this.aggregatedValueCardWidgetSettingsForm.get('subtitleFont').disable(); + this.aggregatedValueCardWidgetSettingsForm.get('subtitleColor').disable(); + } + + if (showDate) { + this.aggregatedValueCardWidgetSettingsForm.get('dateFormat').enable(); + this.aggregatedValueCardWidgetSettingsForm.get('dateFont').enable(); + this.aggregatedValueCardWidgetSettingsForm.get('dateColor').enable(); + } else { + this.aggregatedValueCardWidgetSettingsForm.get('dateFormat').disable(); + this.aggregatedValueCardWidgetSettingsForm.get('dateFont').disable(); + this.aggregatedValueCardWidgetSettingsForm.get('dateColor').disable(); + } + + this.aggregatedValueCardWidgetSettingsForm.get('subtitle').updateValueAndValidity({emitEvent}); + this.aggregatedValueCardWidgetSettingsForm.get('subtitleFont').updateValueAndValidity({emitEvent}); + this.aggregatedValueCardWidgetSettingsForm.get('subtitleColor').updateValueAndValidity({emitEvent}); + this.aggregatedValueCardWidgetSettingsForm.get('dateFormat').updateValueAndValidity({emitEvent}); + this.aggregatedValueCardWidgetSettingsForm.get('dateFont').updateValueAndValidity({emitEvent}); + this.aggregatedValueCardWidgetSettingsForm.get('dateColor').updateValueAndValidity({emitEvent}); + } + + private _datePreviewFn(): string { + const dateFormat: DateFormatSettings = this.aggregatedValueCardWidgetSettingsForm.get('dateFormat').value; + const processor = DateFormatProcessor.fromSettings(this.$injector, dateFormat); + processor.update(Date.now()); + return processor.formatted; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-table-key-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-table-key-settings.component.html deleted file mode 100644 index d57f56cb59..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-table-key-settings.component.html +++ /dev/null @@ -1,102 +0,0 @@ - -
- - widgets.table.custom-title - - - - widgets.table.column-width - - -
- widgets.table.cell-style - - - - - {{ 'widgets.table.use-cell-style-function' | translate }} - - - - widget-config.advanced-settings - - - - - - - -
-
- widgets.table.cell-content - - - - - {{ 'widgets.table.use-cell-content-function' | translate }} - - - - widget-config.advanced-settings - - - - - - - -
- - widgets.table.default-column-visibility - - - {{ 'widgets.table.column-visibility-visible' | translate }} - - - {{ 'widgets.table.column-visibility-hidden' | translate }} - - - {{ 'widgets.table.column-visibility-hidden-mobile' | translate }} - - - - - widgets.table.column-selection-to-display - - - {{ 'widgets.table.column-selection-to-display-enabled' | translate }} - - - {{ 'widgets.table.column-selection-to-display-disabled' | translate }} - - - -
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-table-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-table-widget-settings.component.html deleted file mode 100644 index c0680dfeb9..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-table-widget-settings.component.html +++ /dev/null @@ -1,116 +0,0 @@ - -
-
- widgets.table.common-table-settings - - widgets.table.entities-table-title - - -
-
- - {{ 'widgets.table.enable-search' | translate }} - - - {{ 'widgets.table.enable-select-column-display' | translate }} - -
-
- - {{ 'widgets.table.enable-sticky-header' | translate }} - - - {{ 'widgets.table.enable-sticky-action' | translate }} - -
-
- - widgets.table.hidden-cell-button-display-mode - - - {{ 'widgets.table.show-empty-space-hidden-action' | translate }} - - - {{ 'widgets.table.dont-reserve-space-hidden-action' | translate }} - - - -
-
- - {{ 'widgets.table.display-entity-name' | translate }} - - - widgets.table.entity-name-column-title - - -
-
- - {{ 'widgets.table.display-entity-label' | translate }} - - - widgets.table.entity-label-column-title - - -
- - {{ 'widgets.table.display-entity-type' | translate }} - -
- - {{ 'widgets.table.display-pagination' | translate }} - - - widgets.table.default-page-size - - -
- - widgets.table.default-sort-order - - -
-
-
- widgets.table.row-style - - - - - {{ 'widgets.table.use-row-style-function' | translate }} - - - - widget-config.advanced-settings - - - - - - - -
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-table-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-table-widget-settings.component.ts deleted file mode 100644 index a65422ae2d..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-table-widget-settings.component.ts +++ /dev/null @@ -1,118 +0,0 @@ -/// -/// Copyright © 2016-2023 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -import { Component } from '@angular/core'; -import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; -import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; - -@Component({ - selector: 'tb-entities-table-widget-settings', - templateUrl: './entities-table-widget-settings.component.html', - styleUrls: ['./../widget-settings.scss'] -}) -export class EntitiesTableWidgetSettingsComponent extends WidgetSettingsComponent { - - entitiesTableWidgetSettingsForm: UntypedFormGroup; - - constructor(protected store: Store, - private fb: UntypedFormBuilder) { - super(store); - } - - protected settingsForm(): UntypedFormGroup { - return this.entitiesTableWidgetSettingsForm; - } - - protected defaultSettings(): WidgetSettings { - return { - entitiesTitle: '', - enableSearch: true, - enableSelectColumnDisplay: true, - enableStickyHeader: true, - enableStickyAction: true, - reserveSpaceForHiddenAction: 'true', - displayEntityName: true, - entityNameColumnTitle: '', - displayEntityLabel: false, - entityLabelColumnTitle: '', - displayEntityType: true, - displayPagination: true, - defaultPageSize: 10, - defaultSortOrder: 'entityName', - useRowStyleFunction: false, - rowStyleFunction: '' - }; - } - - protected onSettingsSet(settings: WidgetSettings) { - this.entitiesTableWidgetSettingsForm = this.fb.group({ - entitiesTitle: [settings.entitiesTitle, []], - enableSearch: [settings.enableSearch, []], - enableSelectColumnDisplay: [settings.enableSelectColumnDisplay, []], - enableStickyHeader: [settings.enableStickyHeader, []], - enableStickyAction: [settings.enableStickyAction, []], - reserveSpaceForHiddenAction: [settings.reserveSpaceForHiddenAction, []], - displayEntityName: [settings.displayEntityName, []], - entityNameColumnTitle: [settings.entityNameColumnTitle, []], - displayEntityLabel: [settings.displayEntityLabel, []], - entityLabelColumnTitle: [settings.entityLabelColumnTitle, []], - displayEntityType: [settings.displayEntityType, []], - displayPagination: [settings.displayPagination, []], - defaultPageSize: [settings.defaultPageSize, [Validators.min(1)]], - defaultSortOrder: [settings.defaultSortOrder, []], - useRowStyleFunction: [settings.useRowStyleFunction, []], - rowStyleFunction: [settings.rowStyleFunction, [Validators.required]] - }); - } - - protected validatorTriggers(): string[] { - return ['useRowStyleFunction', 'displayPagination', 'displayEntityName', 'displayEntityLabel']; - } - - protected updateValidators(emitEvent: boolean) { - const useRowStyleFunction: boolean = this.entitiesTableWidgetSettingsForm.get('useRowStyleFunction').value; - const displayPagination: boolean = this.entitiesTableWidgetSettingsForm.get('displayPagination').value; - const displayEntityName: boolean = this.entitiesTableWidgetSettingsForm.get('displayEntityName').value; - const displayEntityLabel: boolean = this.entitiesTableWidgetSettingsForm.get('displayEntityLabel').value; - if (useRowStyleFunction) { - this.entitiesTableWidgetSettingsForm.get('rowStyleFunction').enable(); - } else { - this.entitiesTableWidgetSettingsForm.get('rowStyleFunction').disable(); - } - if (displayPagination) { - this.entitiesTableWidgetSettingsForm.get('defaultPageSize').enable(); - } else { - this.entitiesTableWidgetSettingsForm.get('defaultPageSize').disable(); - } - if (displayEntityName) { - this.entitiesTableWidgetSettingsForm.get('entityNameColumnTitle').enable(); - } else { - this.entitiesTableWidgetSettingsForm.get('entityNameColumnTitle').disable(); - } - if (displayEntityLabel) { - this.entitiesTableWidgetSettingsForm.get('entityLabelColumnTitle').enable(); - } else { - this.entitiesTableWidgetSettingsForm.get('entityLabelColumnTitle').disable(); - } - this.entitiesTableWidgetSettingsForm.get('rowStyleFunction').updateValueAndValidity({emitEvent}); - this.entitiesTableWidgetSettingsForm.get('defaultPageSize').updateValueAndValidity({emitEvent}); - this.entitiesTableWidgetSettingsForm.get('entityNameColumnTitle').updateValueAndValidity({emitEvent}); - this.entitiesTableWidgetSettingsForm.get('entityLabelColumnTitle').updateValueAndValidity({emitEvent}); - } - -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-label.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-label.component.ts index d91baebb53..af32954e62 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-label.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-label.component.ts @@ -33,7 +33,7 @@ export interface LabelWidgetLabel { @Component({ selector: 'tb-label-widget-label', templateUrl: './label-widget-label.component.html', - styleUrls: ['./label-widget-label.component.scss', './../widget-settings.scss'], + styleUrls: ['./label-widget-label.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-settings.component.html index d639e29631..d34e439e98 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/label-widget-settings.component.html @@ -16,10 +16,10 @@ -->
- - +
widgets.label-widget.labels
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/progress-bar-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/progress-bar-widget-settings.component.html new file mode 100644 index 0000000000..6c4559a0d5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/progress-bar-widget-settings.component.html @@ -0,0 +1,97 @@ + + +
+
widgets.progress-bar.progress-bar-card-style
+ + + {{ progressBarLayoutTranslationMap.get(layout) | translate }} + + +
+ + {{ 'widgets.progress-bar.auto-scale' | translate }} + +
+
+ + {{ 'widgets.progress-bar.value' | translate }} + +
+ + + + +
+
+
+
{{ 'widgets.progress-bar.range' | translate }}
+
+
widgets.progress-bar.min
+ + + +
widgets.progress-bar.max
+ + + +
+
+
+ + {{ 'widgets.progress-bar.range-ticks' | translate }} + +
+ + + + +
+
+
+
{{ 'widgets.progress-bar.bar-color' | translate }}
+ + +
+
+
{{ 'widgets.progress-bar.bar-background' | translate }}
+ + +
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/progress-bar-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/progress-bar-widget-settings.component.ts new file mode 100644 index 0000000000..b56d8bff3b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/progress-bar-widget-settings.component.ts @@ -0,0 +1,133 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, Injector } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { formatValue } from '@core/utils'; +import { + progressBarDefaultSettings, + ProgressBarLayout, + progressBarLayoutImages, + progressBarLayouts, + progressBarLayoutTranslations +} from '@home/components/widget/lib/cards/progress-bar-widget.models'; + +@Component({ + selector: 'tb-progress-bar-widget-settings', + templateUrl: './progress-bar-widget-settings.component.html', + styleUrls: [] +}) +export class ProgressBarWidgetSettingsComponent extends WidgetSettingsComponent { + + progressBarLayout = ProgressBarLayout; + + progressBarLayouts = progressBarLayouts; + + progressBarLayoutTranslationMap = progressBarLayoutTranslations; + progressBarLayoutImageMap = progressBarLayoutImages; + + progressBarWidgetSettingsForm: UntypedFormGroup; + + valuePreviewFn = this._valuePreviewFn.bind(this); + + constructor(protected store: Store, + private $injector: Injector, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.progressBarWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return {...progressBarDefaultSettings}; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.progressBarWidgetSettingsForm = this.fb.group({ + layout: [settings.layout, []], + autoScale: [settings.autoScale, []], + + showValue: [settings.showValue, []], + valueFont: [settings.valueFont, []], + valueColor: [settings.valueColor, []], + + tickMin: [settings.tickMin, []], + tickMax: [settings.tickMax, []], + + showTicks: [settings.showTicks, []], + ticksFont: [settings.ticksFont, []], + ticksColor: [settings.ticksColor, []], + + barColor: [settings.barColor, []], + barBackground: [settings.barBackground, []], + + background: [settings.background, []] + }); + } + + protected validatorTriggers(): string[] { + return ['showValue', 'showTicks', 'layout']; + } + + protected updateValidators(emitEvent: boolean) { + const showValue: boolean = this.progressBarWidgetSettingsForm.get('showValue').value; + const showTicks: boolean = this.progressBarWidgetSettingsForm.get('showTicks').value; + const layout: ProgressBarLayout = this.progressBarWidgetSettingsForm.get('layout').value; + + const ticksEnabled = layout === ProgressBarLayout.default; + + if (showValue) { + this.progressBarWidgetSettingsForm.get('valueFont').enable(); + this.progressBarWidgetSettingsForm.get('valueColor').enable(); + } else { + this.progressBarWidgetSettingsForm.get('valueFont').disable(); + this.progressBarWidgetSettingsForm.get('valueColor').disable(); + } + + if (ticksEnabled) { + this.progressBarWidgetSettingsForm.get('showTicks').enable({emitEvent: false}); + if (showTicks) { + this.progressBarWidgetSettingsForm.get('ticksFont').enable(); + this.progressBarWidgetSettingsForm.get('ticksColor').enable(); + } else { + this.progressBarWidgetSettingsForm.get('ticksFont').disable(); + this.progressBarWidgetSettingsForm.get('ticksColor').disable(); + } + } else { + this.progressBarWidgetSettingsForm.get('showTicks').disable({emitEvent: false}); + this.progressBarWidgetSettingsForm.get('ticksFont').disable(); + this.progressBarWidgetSettingsForm.get('ticksColor').disable(); + } + + this.progressBarWidgetSettingsForm.get('valueFont').updateValueAndValidity({emitEvent}); + this.progressBarWidgetSettingsForm.get('valueColor').updateValueAndValidity({emitEvent}); + this.progressBarWidgetSettingsForm.get('showTicks').updateValueAndValidity({emitEvent: false}); + this.progressBarWidgetSettingsForm.get('ticksFont').updateValueAndValidity({emitEvent}); + this.progressBarWidgetSettingsForm.get('ticksColor').updateValueAndValidity({emitEvent}); + } + + private _valuePreviewFn(): string { + const units: string = this.widgetConfig.config.units; + const decimals: number = this.widgetConfig.config.decimals; + return formatValue(78, decimals, units, true); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/simple-card-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/simple-card-widget-settings.component.html index 1ee182f923..dc201c1cc6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/simple-card-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/simple-card-widget-settings.component.html @@ -15,9 +15,9 @@ limitations under the License. --> -
-
widgets.simple-card.label
-
+
+
widgets.simple-card.label
+
widgets.simple-card.label-position
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-key-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-key-settings.component.html index 2dbedf96bd..165f4eb0f7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-key-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-key-settings.component.html @@ -15,18 +15,49 @@ limitations under the License. --> -
-
- widgets.table.cell-style + +
+
widgets.table.column-settings
+
+
{{ 'widgets.table.default-column-visibility' | translate }}
+ + + + {{ 'widgets.table.column-visibility-visible' | translate }} + + + {{ 'widgets.table.column-visibility-hidden' | translate }} + + + {{ 'widgets.table.column-visibility-hidden-mobile' | translate }} + + + +
+
+
{{ 'widgets.table.column-selection-to-display' | translate }}
+ + + + {{ 'widgets.table.column-selection-to-display-enabled' | translate }} + + + {{ 'widgets.table.column-selection-to-display-disabled' | translate }} + + + +
+
+
- {{ 'widgets.table.use-cell-style-function' | translate }} - + widget-config.advanced-settings @@ -40,18 +71,17 @@ -
-
- widgets.table.cell-content +
+
- {{ 'widgets.table.use-cell-content-function' | translate }} - + widget-config.advanced-settings @@ -65,30 +95,5 @@ -
- - widgets.table.default-column-visibility - - - {{ 'widgets.table.column-visibility-visible' | translate }} - - - {{ 'widgets.table.column-visibility-hidden' | translate }} - - - {{ 'widgets.table.column-visibility-hidden-mobile' | translate }} - - - - - widgets.table.column-selection-to-display - - - {{ 'widgets.table.column-selection-to-display-enabled' | translate }} - - - {{ 'widgets.table.column-selection-to-display-disabled' | translate }} - - - -
+ + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-latest-key-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-latest-key-settings.component.html index 8fb1629b13..fd34b66af6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-latest-key-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-latest-key-settings.component.html @@ -15,25 +15,58 @@ limitations under the License. --> -
- - {{ 'widgets.table.show-latest-data-column' | translate }} - - - widgets.table.latest-data-column-order - - -
- widgets.table.cell-style + +
+
widgets.table.column-settings
+ + {{ 'widgets.table.show-latest-data-column' | translate }} + +
+
widgets.table.latest-data-column-order
+ + + +
+
+
{{ 'widgets.table.default-column-visibility' | translate }}
+ + + + {{ 'widgets.table.column-visibility-visible' | translate }} + + + {{ 'widgets.table.column-visibility-hidden' | translate }} + + + {{ 'widgets.table.column-visibility-hidden-mobile' | translate }} + + + +
+
+
{{ 'widgets.table.column-selection-to-display' | translate }}
+ + + + {{ 'widgets.table.column-selection-to-display-enabled' | translate }} + + + {{ 'widgets.table.column-selection-to-display-disabled' | translate }} + + + +
+
+
- {{ 'widgets.table.use-cell-style-function' | translate }} - + widget-config.advanced-settings @@ -47,18 +80,17 @@ -
-
- widgets.table.cell-content + +
- {{ 'widgets.table.use-cell-content-function' | translate }} - + widget-config.advanced-settings @@ -72,30 +104,6 @@ -
- - widgets.table.default-column-visibility - - - {{ 'widgets.table.column-visibility-visible' | translate }} - - - {{ 'widgets.table.column-visibility-hidden' | translate }} - - - {{ 'widgets.table.column-visibility-hidden-mobile' | translate }} - - - - - widgets.table.column-selection-to-display - - - {{ 'widgets.table.column-selection-to-display-enabled' | translate }} - - - {{ 'widgets.table.column-selection-to-display-disabled' | translate }} - - - -
+ + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.html index ed4428fe45..63d46b4a31 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.html @@ -15,28 +15,36 @@ limitations under the License. --> -
-
- widgets.table.common-table-settings -
-
- - {{ 'widgets.table.enable-search' | translate }} - - - {{ 'widgets.table.enable-select-column-display' | translate }} - -
-
- - {{ 'widgets.table.enable-sticky-header' | translate }} - - - {{ 'widgets.table.enable-sticky-action' | translate }} - -
-
- + +
+
widgets.table.table-header
+ + {{ 'widgets.table.enable-sticky-header' | translate }} + + + {{ 'widgets.table.enable-search' | translate }} + + + {{ 'widgets.table.enable-select-column-display' | translate }} + +
+
+
widgets.table.columns
+ +
+ + {{ 'widgets.table.display-timestamp' | translate }} + + +
+ + + {{ 'widgets.table.enable-sticky-action' | translate }} + + + {{ 'widgets.table.show-cell-actions-menu-mobile' | translate }} + + widgets.table.hidden-cell-button-display-mode @@ -47,41 +55,39 @@ -
- - {{ 'widgets.table.display-timestamp' | translate }} - - - {{ 'widgets.table.display-milliseconds' | translate }} - -
- - {{ 'widgets.table.display-pagination' | translate }} - - - widgets.table.default-page-size - - -
- - {{ 'widgets.table.use-entity-label-tab-name' | translate }} - - - {{ 'widgets.table.hide-empty-lines' | translate }} - -
-
-
- widgets.table.row-style + +
+
widgets.table.pagination
+ + {{ 'widgets.table.display-pagination' | translate }} + +
+
widgets.table.default-page-size
+ + + +
+
+
+
widgets.table.table-tabs
+ + {{ 'widgets.table.use-entity-label-tab-name' | translate }} + +
+
+
widgets.table.rows
+ + {{ 'widgets.table.hide-empty-lines' | translate }} + - {{ 'widgets.table.use-row-style-function' | translate }} - + widget-config.advanced-settings @@ -95,5 +101,5 @@ -
-
+ + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.ts index 0a57402870..9e142f1b4a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.ts @@ -44,9 +44,10 @@ export class TimeseriesTableWidgetSettingsComponent extends WidgetSettingsCompon enableSelectColumnDisplay: true, enableStickyHeader: true, enableStickyAction: true, + showCellActionsMenu: true, reserveSpaceForHiddenAction: 'true', showTimestamp: true, - showMilliseconds: false, + dateFormat: {format: 'yyyy-MM-dd HH:mm:ss'}, displayPagination: true, useEntityLabel: false, defaultPageSize: 10, @@ -58,14 +59,21 @@ export class TimeseriesTableWidgetSettingsComponent extends WidgetSettingsCompon } protected onSettingsSet(settings: WidgetSettings) { + // For backward compatibility + const dateFormat = settings.dateFormat; + if (settings?.showMilliseconds) { + dateFormat.format = 'yyyy-MM-dd HH:mm:ss.SSS'; + } + this.timeseriesTableWidgetSettingsForm = this.fb.group({ enableSearch: [settings.enableSearch, []], enableSelectColumnDisplay: [settings.enableSelectColumnDisplay, []], enableStickyHeader: [settings.enableStickyHeader, []], enableStickyAction: [settings.enableStickyAction, []], + showCellActionsMenu: [settings.showCellActionsMenu, []], reserveSpaceForHiddenAction: [settings.reserveSpaceForHiddenAction, []], showTimestamp: [settings.showTimestamp, []], - showMilliseconds: [settings.showMilliseconds, []], + dateFormat: [dateFormat, []], displayPagination: [settings.displayPagination, []], useEntityLabel: [settings.useEntityLabel, []], defaultPageSize: [settings.defaultPageSize, [Validators.min(1)]], diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/value-card-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/value-card-widget-settings.component.html new file mode 100644 index 0000000000..48622640e9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/value-card-widget-settings.component.html @@ -0,0 +1,97 @@ + + +
+
widgets.value-card.value-card-style
+ + + {{ valueCardLayoutTranslationMap.get(layout) | translate }} + + +
+ + {{ 'widgets.value-card.auto-scale' | translate }} + +
+
+ + {{ 'widgets.value-card.label' | translate }} + +
+ + + + +
+
+
+ + {{ 'widgets.value-card.icon' | translate }} + +
+ + + + + + + + +
+
+
+
widgets.value-card.value
+
+ + + + +
+
+
+ + {{ 'widgets.value-card.date' | translate }} + +
+ + + + + +
+
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/value-card-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/value-card-widget-settings.component.ts new file mode 100644 index 0000000000..4f4b3b6eec --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/value-card-widget-settings.component.ts @@ -0,0 +1,201 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, Injector } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { + valueCardDefaultSettings, + ValueCardLayout, valueCardLayoutImages, + valueCardLayouts, valueCardLayoutTranslations +} from '@home/components/widget/lib/cards/value-card-widget.models'; +import { formatValue, isDefinedAndNotNull } from '@core/utils'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { + DateFormatProcessor, + DateFormatSettings, + getLabel +} from '@shared/models/widget-settings.models'; + +@Component({ + selector: 'tb-value-card-widget-settings', + templateUrl: './value-card-widget-settings.component.html', + styleUrls: [] +}) +export class ValueCardWidgetSettingsComponent extends WidgetSettingsComponent { + + valueCardLayouts: ValueCardLayout[] = []; + + valueCardLayoutTranslationMap = valueCardLayoutTranslations; + valueCardLayoutImageMap = valueCardLayoutImages; + + horizontal = false; + + valueCardWidgetSettingsForm: UntypedFormGroup; + + valuePreviewFn = this._valuePreviewFn.bind(this); + + datePreviewFn = this._datePreviewFn.bind(this); + + + get label(): string { + return getLabel(this.widgetConfig.config.datasources); + } + + get dateEnabled(): boolean { + const layout: ValueCardLayout = this.valueCardWidgetSettingsForm.get('layout').value; + return ![ValueCardLayout.vertical, ValueCardLayout.simplified].includes(layout); + } + + get iconEnabled(): boolean { + const layout: ValueCardLayout = this.valueCardWidgetSettingsForm.get('layout').value; + return layout !== ValueCardLayout.simplified; + } + + constructor(protected store: Store, + private $injector: Injector, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.valueCardWidgetSettingsForm; + } + + protected onWidgetConfigSet(widgetConfig: WidgetConfigComponentData) { + const params = widgetConfig.typeParameters as any; + this.horizontal = isDefinedAndNotNull(params.horizontal) ? params.horizontal : false; + this.valueCardLayouts = valueCardLayouts(this.horizontal); + } + + protected defaultSettings(): WidgetSettings { + return valueCardDefaultSettings(this.horizontal); + } + + protected onSettingsSet(settings: WidgetSettings) { + this.valueCardWidgetSettingsForm = this.fb.group({ + layout: [settings.layout, []], + autoScale: [settings.autoScale, []], + + showLabel: [settings.showLabel, []], + labelFont: [settings.labelFont, []], + labelColor: [settings.labelColor, []], + + showIcon: [settings.showIcon, []], + iconSize: [settings.iconSize, [Validators.min(0)]], + iconSizeUnit: [settings.iconSizeUnit, []], + icon: [settings.icon, []], + iconColor: [settings.iconColor, []], + + valueFont: [settings.valueFont, []], + valueColor: [settings.valueColor, []], + + showDate: [settings.showDate, []], + dateFormat: [settings.dateFormat, []], + dateFont: [settings.dateFont, []], + dateColor: [settings.dateColor, []], + + background: [settings.background, []] + }); + } + + protected validatorTriggers(): string[] { + return ['layout', 'showLabel', 'showIcon', 'showDate']; + } + + protected updateValidators(emitEvent: boolean) { + const layout: ValueCardLayout = this.valueCardWidgetSettingsForm.get('layout').value; + const showLabel: boolean = this.valueCardWidgetSettingsForm.get('showLabel').value; + const showIcon: boolean = this.valueCardWidgetSettingsForm.get('showIcon').value; + const showDate: boolean = this.valueCardWidgetSettingsForm.get('showDate').value; + + const dateEnabled = ![ValueCardLayout.vertical, ValueCardLayout.simplified].includes(layout); + const iconEnabled = layout !== ValueCardLayout.simplified; + + if (showLabel) { + this.valueCardWidgetSettingsForm.get('labelFont').enable(); + this.valueCardWidgetSettingsForm.get('labelColor').enable(); + } else { + this.valueCardWidgetSettingsForm.get('labelFont').disable(); + this.valueCardWidgetSettingsForm.get('labelColor').disable(); + } + + if (iconEnabled) { + this.valueCardWidgetSettingsForm.get('showIcon').enable({emitEvent: false}); + if (showIcon) { + this.valueCardWidgetSettingsForm.get('iconSize').enable(); + this.valueCardWidgetSettingsForm.get('iconSizeUnit').enable(); + this.valueCardWidgetSettingsForm.get('icon').enable(); + this.valueCardWidgetSettingsForm.get('iconColor').enable(); + } else { + this.valueCardWidgetSettingsForm.get('iconSize').disable(); + this.valueCardWidgetSettingsForm.get('iconSizeUnit').disable(); + this.valueCardWidgetSettingsForm.get('icon').disable(); + this.valueCardWidgetSettingsForm.get('iconColor').disable(); + } + } else { + this.valueCardWidgetSettingsForm.get('showIcon').disable({emitEvent: false}); + this.valueCardWidgetSettingsForm.get('iconSize').disable(); + this.valueCardWidgetSettingsForm.get('iconSizeUnit').disable(); + this.valueCardWidgetSettingsForm.get('icon').disable(); + this.valueCardWidgetSettingsForm.get('iconColor').disable(); + } + + if (dateEnabled) { + this.valueCardWidgetSettingsForm.get('showDate').enable({emitEvent: false}); + if (showDate) { + this.valueCardWidgetSettingsForm.get('dateFormat').enable(); + this.valueCardWidgetSettingsForm.get('dateFont').enable(); + this.valueCardWidgetSettingsForm.get('dateColor').enable(); + } else { + this.valueCardWidgetSettingsForm.get('dateFormat').disable(); + this.valueCardWidgetSettingsForm.get('dateFont').disable(); + this.valueCardWidgetSettingsForm.get('dateColor').disable(); + } + } else { + this.valueCardWidgetSettingsForm.get('showDate').disable({emitEvent: false}); + this.valueCardWidgetSettingsForm.get('dateFormat').disable(); + this.valueCardWidgetSettingsForm.get('dateFont').disable(); + this.valueCardWidgetSettingsForm.get('dateColor').disable(); + } + this.valueCardWidgetSettingsForm.get('showIcon').updateValueAndValidity({emitEvent: false}); + this.valueCardWidgetSettingsForm.get('showDate').updateValueAndValidity({emitEvent: false}); + this.valueCardWidgetSettingsForm.get('labelFont').updateValueAndValidity({emitEvent}); + this.valueCardWidgetSettingsForm.get('labelColor').updateValueAndValidity({emitEvent}); + this.valueCardWidgetSettingsForm.get('iconSize').updateValueAndValidity({emitEvent}); + this.valueCardWidgetSettingsForm.get('iconSizeUnit').updateValueAndValidity({emitEvent}); + this.valueCardWidgetSettingsForm.get('icon').updateValueAndValidity({emitEvent}); + this.valueCardWidgetSettingsForm.get('iconColor').updateValueAndValidity({emitEvent}); + this.valueCardWidgetSettingsForm.get('dateFormat').updateValueAndValidity({emitEvent}); + this.valueCardWidgetSettingsForm.get('dateFont').updateValueAndValidity({emitEvent}); + this.valueCardWidgetSettingsForm.get('dateColor').updateValueAndValidity({emitEvent}); + } + + private _valuePreviewFn(): string { + const units: string = this.widgetConfig.config.units; + const decimals: number = this.widgetConfig.config.decimals; + return formatValue(22, decimals, units, true); + } + + private _datePreviewFn(): string { + const dateFormat: DateFormatSettings = this.valueCardWidgetSettingsForm.get('dateFormat').value; + const processor = DateFormatProcessor.fromSettings(this.$injector, dateFormat); + processor.update(Date.now()); + return processor.formatted; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/value-chart-card-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/value-chart-card-widget-settings.component.html new file mode 100644 index 0000000000..9e9ed12027 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/value-chart-card-widget-settings.component.html @@ -0,0 +1,57 @@ + + +
+
widgets.value-chart-card.value-chart-card-style
+ + + {{ valueChartCardLayoutTranslationMap.get(layout) | translate }} + + +
+ + {{ 'widgets.value-chart-card.auto-scale' | translate }} + +
+
+ + {{ 'widgets.value-chart-card.value' | translate }} + +
+ + + + +
+
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/value-chart-card-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/value-chart-card-widget-settings.component.ts new file mode 100644 index 0000000000..88db47b3ad --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/value-chart-card-widget-settings.component.ts @@ -0,0 +1,98 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, Injector } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { formatValue } from '@core/utils'; +import { + valueCartCardLayouts, + valueChartCardDefaultSettings, + valueChartCardLayoutImages, + valueChartCardLayoutTranslations +} from '@home/components/widget/lib/cards/value-chart-card-widget.models'; + +@Component({ + selector: 'tb-value-chart-card-widget-settings', + templateUrl: './value-chart-card-widget-settings.component.html', + styleUrls: [] +}) +export class ValueChartCardWidgetSettingsComponent extends WidgetSettingsComponent { + + valueChartCardLayouts = valueCartCardLayouts; + + valueChartCardLayoutTranslationMap = valueChartCardLayoutTranslations; + valueChartCardLayoutImageMap = valueChartCardLayoutImages; + + valueChartCardWidgetSettingsForm: UntypedFormGroup; + + valuePreviewFn = this._valuePreviewFn.bind(this); + + constructor(protected store: Store, + private $injector: Injector, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.valueChartCardWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return {...valueChartCardDefaultSettings}; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.valueChartCardWidgetSettingsForm = this.fb.group({ + layout: [settings.layout, []], + autoScale: [settings.autoScale, []], + + showValue: [settings.showValue, []], + valueFont: [settings.valueFont, []], + valueColor: [settings.valueColor, []], + + background: [settings.background, []] + }); + } + + protected validatorTriggers(): string[] { + return ['showValue']; + } + + protected updateValidators(emitEvent: boolean) { + const showValue: boolean = this.valueChartCardWidgetSettingsForm.get('showValue').value; + + if (showValue) { + this.valueChartCardWidgetSettingsForm.get('valueFont').enable(); + this.valueChartCardWidgetSettingsForm.get('valueColor').enable(); + } else { + this.valueChartCardWidgetSettingsForm.get('valueFont').disable(); + this.valueChartCardWidgetSettingsForm.get('valueColor').disable(); + } + + this.valueChartCardWidgetSettingsForm.get('valueFont').updateValueAndValidity({emitEvent}); + this.valueChartCardWidgetSettingsForm.get('valueColor').updateValueAndValidity({emitEvent}); + } + + private _valuePreviewFn(): string { + const units: string = this.widgetConfig.config.units; + const decimals: number = this.widgetConfig.config.decimals; + return formatValue(22, decimals, units, true); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-widget-settings.component.html new file mode 100644 index 0000000000..a0e76da09c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-widget-settings.component.html @@ -0,0 +1,163 @@ + + +
+
widgets.doughnut.doughnut-card-style
+ + + {{ doughnutLayoutTranslationMap.get(layout) | translate }} + + +
+ + {{ 'widgets.doughnut.auto-scale' | translate }} + +
+
+ + {{ 'widgets.doughnut.clockwise-layout' | translate }} + +
+
+ + {{ 'widgets.doughnut.sort-series' | translate }} + +
+
+
{{ 'widgets.doughnut.central-total-value' | translate }}
+
+ + + + +
+
+
+ + + + + {{ 'widget-config.legend' | translate }} + + + + +
+
{{ 'legend.position' | translate }}
+ + + + {{ doughnutLegendPositionTranslationMap.get(pos) | translate }} + + + +
+
+
{{ 'widgets.doughnut.legend-label' | translate }}
+
+ + + + +
+
+
+
{{ 'widgets.doughnut.legend-value' | translate }}
+
+ + + + +
+
+
+
+
+
+ + + + + {{ 'widgets.doughnut.tooltip' | translate }} + + + + +
+
{{ 'widgets.doughnut.tooltip-value' | translate }}
+
+ + + + {{ doughnutTooltipValueTypeTranslationMap.get(type) | translate }} + + + + + +
widget-config.decimals-suffix
+
+ + + + +
+
+
+
{{ 'widgets.doughnut.tooltip-background-color' | translate }}
+ + +
+
+
{{ 'widgets.doughnut.tooltip-background-blur' | translate }}
+ + +
px
+
+
+
+
+
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-widget-settings.component.ts new file mode 100644 index 0000000000..220fc7c320 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/doughnut-widget-settings.component.ts @@ -0,0 +1,185 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { formatValue, isDefinedAndNotNull } from '@core/utils'; +import { + doughnutDefaultSettings, + DoughnutLayout, + doughnutLayoutImages, + doughnutLayouts, + doughnutLayoutTranslations, + doughnutLegendPositions, + doughnutLegendPositionTranslations, + DoughnutTooltipValueType, + doughnutTooltipValueTypes, + doughnutTooltipValueTypeTranslations, + horizontalDoughnutLayoutImages +} from '@home/components/widget/lib/chart/doughnut-widget.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; + +@Component({ + selector: 'tb-doughnut-widget-settings', + templateUrl: './doughnut-widget-settings.component.html', + styleUrls: [] +}) +export class DoughnutWidgetSettingsComponent extends WidgetSettingsComponent { + + get totalEnabled(): boolean { + const layout: DoughnutLayout = this.doughnutWidgetSettingsForm.get('layout').value; + return layout === DoughnutLayout.with_total; + } + + doughnutLayouts = doughnutLayouts; + + doughnutLayoutTranslationMap = doughnutLayoutTranslations; + + horizontal = false; + + doughnutLayoutImageMap: Map; + + doughnutLegendPositions = doughnutLegendPositions; + + doughnutLegendPositionTranslationMap = doughnutLegendPositionTranslations; + + doughnutTooltipValueTypes = doughnutTooltipValueTypes; + + doughnutTooltipValueTypeTranslationMap = doughnutTooltipValueTypeTranslations; + + doughnutWidgetSettingsForm: UntypedFormGroup; + + valuePreviewFn = this._valuePreviewFn.bind(this); + + tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); + + constructor(protected store: Store, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.doughnutWidgetSettingsForm; + } + + protected onWidgetConfigSet(widgetConfig: WidgetConfigComponentData) { + const params = widgetConfig.typeParameters as any; + this.horizontal = isDefinedAndNotNull(params.horizontal) ? params.horizontal : false; + this.doughnutLayoutImageMap = this.horizontal ? horizontalDoughnutLayoutImages : doughnutLayoutImages; + } + + protected defaultSettings(): WidgetSettings { + return doughnutDefaultSettings(this.horizontal); + } + + protected onSettingsSet(settings: WidgetSettings) { + this.doughnutWidgetSettingsForm = this.fb.group({ + layout: [settings.layout, []], + autoScale: [settings.autoScale, []], + clockwise: [settings.clockwise, []], + sortSeries: [settings.sortSeries, []], + + totalValueFont: [settings.totalValueFont, []], + totalValueColor: [settings.totalValueColor, []], + + showLegend: [settings.showLegend, []], + legendPosition: [settings.legendPosition, []], + legendLabelFont: [settings.legendLabelFont, []], + legendLabelColor: [settings.legendLabelColor, []], + legendValueFont: [settings.legendValueFont, []], + legendValueColor: [settings.legendValueColor, []], + + showTooltip: [settings.showTooltip, []], + tooltipValueType: [settings.tooltipValueType, []], + tooltipValueDecimals: [settings.tooltipValueDecimals, []], + tooltipValueFont: [settings.tooltipValueFont, []], + tooltipValueColor: [settings.tooltipValueColor, []], + tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], + tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], + + background: [settings.background, []] + }); + } + + protected validatorTriggers(): string[] { + return ['layout', 'showLegend', 'showTooltip']; + } + + protected updateValidators(emitEvent: boolean) { + const layout: DoughnutLayout = this.doughnutWidgetSettingsForm.get('layout').value; + const showLegend: boolean = this.doughnutWidgetSettingsForm.get('showLegend').value; + const showTooltip: boolean = this.doughnutWidgetSettingsForm.get('showTooltip').value; + + const totalEnabled = layout === DoughnutLayout.with_total; + + if (showLegend) { + this.doughnutWidgetSettingsForm.get('legendPosition').enable(); + this.doughnutWidgetSettingsForm.get('legendLabelFont').enable(); + this.doughnutWidgetSettingsForm.get('legendLabelColor').enable(); + this.doughnutWidgetSettingsForm.get('legendValueFont').enable(); + this.doughnutWidgetSettingsForm.get('legendValueColor').enable(); + } else { + this.doughnutWidgetSettingsForm.get('legendPosition').disable(); + this.doughnutWidgetSettingsForm.get('legendLabelFont').disable(); + this.doughnutWidgetSettingsForm.get('legendLabelColor').disable(); + this.doughnutWidgetSettingsForm.get('legendValueFont').disable(); + this.doughnutWidgetSettingsForm.get('legendValueColor').disable(); + } + if (showTooltip) { + this.doughnutWidgetSettingsForm.get('tooltipValueType').enable(); + this.doughnutWidgetSettingsForm.get('tooltipValueDecimals').enable(); + this.doughnutWidgetSettingsForm.get('tooltipValueFont').enable(); + this.doughnutWidgetSettingsForm.get('tooltipValueColor').enable(); + this.doughnutWidgetSettingsForm.get('tooltipBackgroundColor').enable(); + this.doughnutWidgetSettingsForm.get('tooltipBackgroundBlur').enable(); + } else { + this.doughnutWidgetSettingsForm.get('tooltipValueType').disable(); + this.doughnutWidgetSettingsForm.get('tooltipValueDecimals').disable(); + this.doughnutWidgetSettingsForm.get('tooltipValueFont').disable(); + this.doughnutWidgetSettingsForm.get('tooltipValueColor').disable(); + this.doughnutWidgetSettingsForm.get('tooltipBackgroundColor').disable(); + this.doughnutWidgetSettingsForm.get('tooltipBackgroundBlur').disable(); + } + if (totalEnabled) { + this.doughnutWidgetSettingsForm.get('totalValueFont').enable(); + this.doughnutWidgetSettingsForm.get('totalValueColor').enable(); + } else { + this.doughnutWidgetSettingsForm.get('totalValueFont').disable(); + this.doughnutWidgetSettingsForm.get('totalValueColor').disable(); + } + } + + private _valuePreviewFn(): string { + const units: string = this.widgetConfig.config.units; + const decimals: number = this.widgetConfig.config.decimals; + return formatValue(110, decimals, units, false); + } + + private _tooltipValuePreviewFn(): string { + const tooltipValueType: DoughnutTooltipValueType = this.doughnutWidgetSettingsForm.get('tooltipValueType').value; + const decimals: number = this.doughnutWidgetSettingsForm.get('tooltipValueDecimals').value; + if (tooltipValueType === DoughnutTooltipValueType.percentage) { + return formatValue(35, decimals, '%', false); + } else { + const units: string = this.widgetConfig.config.units; + return formatValue(110, decimals, units, false); + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.html index 2bebfeef8a..a6938fa086 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.html @@ -15,81 +15,85 @@ limitations under the License. --> -
-
- widgets.chart.common-settings - + +
+ {{ 'widgets.chart.data-is-hidden-by-default' | translate }} - - + + {{ 'widgets.chart.disable-data-hiding' | translate }} - - + + {{ 'widgets.chart.remove-from-legend' | translate }} - - + + {{ 'widgets.chart.exclude-from-stacking' | translate }} - -
-
- widgets.chart.line-settings + + +
- {{ 'widgets.chart.show-line' | translate }} - + widget-config.advanced-settings -
- - widgets.chart.line-width - +
+
{{ 'widgets.chart.line-width' | translate }}
+ + + px - - {{ 'widgets.chart.fill-line' | translate }} - - - widgets.chart.fill-line-opacity - +
+ + {{ 'widgets.chart.fill-line' | translate }} + +
+
{{ 'widgets.chart.fill-line-opacity' | translate }}
+ + -
+
-
-
- widgets.chart.points-settings + +
- {{ 'widgets.chart.show-points' | translate }} - + widget-config.advanced-settings -
-
- - widgets.chart.points-line-width - - - - widgets.chart.points-radius - - -
- - widgets.chart.point-shape +
+
{{ 'widgets.chart.points-line-width' | translate }}
+ + + px + +
+
+
{{ 'widgets.chart.points-radius' | translate }}
+ + + px + +
+
+
{{ 'widgets.chart.point-shape' | translate }}
+ {{ 'widgets.chart.point-shape-circle' | translate }} @@ -111,19 +115,19 @@ - - -
+
+ + -
-
- widgets.chart.tooltip-settings + +
+
widgets.chart.tooltip-settings
-
-
- widgets.chart.yaxis-settings - + +
+
widgets.chart.vertical-axis
+ {{ 'widgets.chart.show-separate-axis' | translate }} - - widgets.chart.axis-title - - -
- - widgets.chart.min-scale-value - +
+
widgets.chart.axis-title
+ + + +
+
+
widgets.chart.min-scale-value
+ + + +
+
+
widgets.chart.max-scale-value
+ + - - widgets.chart.max-scale-value - +
+
+
{{ 'widgets.chart.axis-position' | translate }}
+ + + + {{ 'widgets.chart.axis-position-left' | translate }} + + + {{ 'widgets.chart.axis-position-right' | translate }} + + -
- - widgets.chart.axis-position - - - {{ 'widgets.chart.axis-position-left' | translate }} - - - {{ 'widgets.chart.axis-position-right' | translate }} - - - -
- widgets.chart.yaxis-tick-labels-settings -
- - widgets.chart.tick-step-size - +
+
+
widgets.chart.ticks
+
+
widget-config.decimals-short
+ + - - widgets.chart.number-of-decimals - +
+
+
widgets.chart.tick-step-size
+ + -
+ - - -
- widgets.chart.thresholds -
-
-
- - -
-
-
- widgets.chart.no-thresholds -
-
- +
+
+
+
+
widgets.chart.thresholds
+ +
+
+
+ +
-
-
- widgets.chart.comparison-settings - + +
+
widgets.chart.comparison-settings
+ - - + {{ 'widgets.chart.show-values-for-comparison' | translate }} - - widget-config.advanced-settings - -
- - widgets.chart.comparison-values-label - +
+
widgets.chart.comparison-values-label
+ + - +
+
+
{{ 'widgets.chart.comparison-line-color' | translate }}
+ -
+
-
- + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.ts index b2c88e5fcb..f254eaeb2e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-key-settings.component.ts @@ -225,6 +225,7 @@ export class FlotKeySettingsComponent extends PageComponent implements OnInit, C this.flotKeySettingsFormGroup.disable({emitEvent: false}); } else { this.flotKeySettingsFormGroup.enable({emitEvent: false}); + this.updateValidators(false); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-latest-key-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-latest-key-settings.component.html index 61f1587306..286d26481b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-latest-key-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-latest-key-settings.component.html @@ -15,34 +15,34 @@ limitations under the License. --> -
-
- widgets.chart.threshold-settings - + +
+
widgets.chart.threshold-settings
+ - {{ 'widgets.chart.use-as-threshold' | translate }} - - widget-config.advanced-settings - -
- - widgets.chart.threshold-line-width - +
+
widgets.chart.threshold-line-width
+ + + px - +
+
+
{{ 'widgets.chart.threshold-color' | translate }}
+ -
+
-
-
+ + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-pie-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-pie-widget-settings.component.html index 307a494954..3d5e608ad2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-pie-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-pie-widget-settings.component.html @@ -21,15 +21,24 @@
widgets.chart.radius - + + + {{ 'widgets.chart.common-pie-settings-range-error' | translate }} + widgets.chart.inner-radius - + + + {{ 'widgets.chart.common-pie-settings-range-error' | translate }} + widgets.chart.tilt - + + + {{ 'widgets.chart.common-pie-settings-range-error' | translate }} +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-pie-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-pie-widget-settings.component.ts index 9b18ace983..0627af3581 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-pie-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-pie-widget-settings.component.ts @@ -59,9 +59,9 @@ export class FlotPieWidgetSettingsComponent extends WidgetSettingsComponent { // Common pie settings - radius: [settings.radius, [Validators.min(0)]], - innerRadius: [settings.innerRadius, [Validators.min(0)]], - tilt: [settings.tilt, [Validators.min(0)]], + radius: [settings.radius, [Validators.min(0), Validators.max(1)]], + innerRadius: [settings.innerRadius, [Validators.min(0), Validators.max(1)]], + tilt: [settings.tilt, [Validators.min(0), Validators.max(1)]], // Stroke settings diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.html index 353e716ad9..be2ae39255 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.html @@ -15,44 +15,34 @@ limitations under the License. --> - - -
- -
-
{{ thresholdText() }}
-
-
-
-
-
- - -
-
- -
- -
- -
- - widgets.chart.line-width - - - - -
-
-
-
-
+
+ + +
+
{{ thresholdText() }}
+ + + + +
+
+ + +
+
widgets.chart.line-width
+ + + px + +
+
+
+ +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.scss index 53eeafb799..81f5d17bc6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.scss @@ -13,28 +13,67 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -:host { - display: block; - .mat-expansion-panel { + +.tb-flot-threshold { + display: flex; + flex-direction: row; + align-items: start; + gap: 4px; + .mat-expansion-panel.tb-settings.tb-flot-threshold-settings { box-shadow: none; - &.flot-threshold { - border: 1px groove rgba(0, 0, 0, .25); - .mat-expansion-panel-header { - padding: 0 24px 0 8px; - &.mat-expanded { - height: 48px; + border-radius: 6px; + border: 1px solid rgba(0, 0, 0, 0.12); + .mat-expansion-panel-header { + height: 56px; + border-radius: 0; + display: flex; + flex-direction: row; + align-items: stretch; + .mat-content { + overflow: hidden; + } + .tb-threshold-header { + flex: 1; + display: flex; + flex-direction: row; + gap: 16px; + align-items: center; + padding-left: 16px; + overflow: hidden; + .mat-divider-vertical { + height: 100%; } } + .tb-threshold-text { + flex: 1; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 16px; + letter-spacing: 0.15px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + .mat-expansion-indicator { + margin-right: 22px; + margin-left: 22px; + margin-top: 12px; + } } - } -} - -:host ::ng-deep { - .mat-expansion-panel { - &.flot-threshold { - .mat-expansion-panel-body { - padding: 0 8px 8px; + > .mat-expansion-panel-content { + > .mat-expansion-panel-body { + padding: 16px !important; + } + } + &.mat-expanded { + .mat-expansion-panel-header { + border-bottom: 1px solid rgba(0, 0, 0, 0.12); } } } + > .mdc-icon-button { + margin-top: 4px; + color: rgba(0, 0, 0, 0.54); + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.ts index 9b997ed424..eb55c12f86 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-threshold.component.ts @@ -15,8 +15,14 @@ /// import { ValueSourceProperty } from '@home/components/widget/lib/settings/common/value-source.component'; -import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; -import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { Component, EventEmitter, forwardRef, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormGroup, + Validators +} from '@angular/forms'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -28,14 +34,15 @@ import { TbFlotKeyThreshold } from '@home/components/widget/lib/flot-widget.mode @Component({ selector: 'tb-flot-threshold', templateUrl: './flot-threshold.component.html', - styleUrls: ['./flot-threshold.component.scss', './../widget-settings.scss'], + styleUrls: ['./flot-threshold.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => FlotThresholdComponent), multi: true } - ] + ], + encapsulation: ViewEncapsulation.None }) export class FlotThresholdComponent extends PageComponent implements OnInit, ControlValueAccessor { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.html index 5422c034f6..4ea648c3b4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.html @@ -15,30 +15,33 @@ limitations under the License. --> -
-
- widgets.chart.common-settings - + +
+
widgets.chart.common-settings
+ {{ 'widgets.chart.enable-stacking-mode' | translate }} - -
- - widgets.chart.line-shadow-size - + + + {{ 'widgets.chart.enable-selection-mode' | translate }} + + + {{ 'widgets.chart.display-smooth-lines' | translate }} + +
+
widgets.chart.line-shadow-size
+ + - - {{ 'widgets.chart.display-smooth-lines' | translate }} - -
-
- - widgets.chart.default-bar-width - +
+
+
widgets.chart.default-bar-width
+ + - - widgets.chart.bar-alignment +
+
+
{{ 'widgets.chart.bar-alignment' | translate }}
+ {{ 'widgets.chart.bar-alignment-left' | translate }} @@ -51,211 +54,241 @@ -
-
- - widgets.chart.default-font-size - + +
+
widgets.chart.thresholds-line-width
+ + - - -
- - widgets.chart.thresholds-line-width - - - -
- widget-config.legend + +
+
{{ 'widgets.chart.default-font' | translate }}
+
+ + + px + + + +
+
+ +
+
widget-config.legend
- - - - {{ 'widget-config.display-legend' | translate }} + + + + {{ 'widget-config.legend' | translate }} - + widget-config.advanced-settings - + + -
-
- widgets.chart.tooltip-settings + +
+
widgets.chart.axis
+
+
widgets.chart.vertical-axis
+
+
widgets.chart.axis-title
+ + + +
+
+
widgets.chart.min-scale-value
+ + + +
+
+
widgets.chart.max-scale-value
+ + + +
+
+
widgets.chart.ticks
+ + + + + {{ 'widgets.chart.ticks' | translate }} + + + + widget-config.advanced-settings + + + +
+
{{ 'widget-config.color' | translate }}
+ + +
+
+
widget-config.decimals-short
+ + + +
+
+
widgets.chart.tick-step-size
+ + + +
+ + +
+
+
+
+
+
widgets.chart.horizontal-axis
+
+
widgets.chart.axis-title
+ + + +
+
+
widgets.chart.ticks
+ + + + + {{ 'widgets.chart.ticks' | translate }} + + + + widget-config.advanced-settings + + + +
+
{{ 'widget-config.color' | translate }}
+ + +
+
+
+
+
+
+
+
widgets.chart.chart-background
+
+ + {{ 'widgets.chart.vertical-grid-lines' | translate }} + +
+
+ + {{ 'widgets.chart.horizontal-grid-lines' | translate }} + +
+
+
{{ 'widgets.chart.grid-lines-color' | translate }}
+ + +
+
+
{{ 'widgets.chart.border' | translate }}
+
+ + + px + + + +
+
+
+
{{ 'widgets.chart.background-color' | translate }}
+ + +
+
+
+
widgets.chart.tooltip
- - {{ 'widgets.chart.show-tooltip' | translate }} + {{ 'widgets.chart.tooltip' | translate }} - + widget-config.advanced-settings -
- +
+ {{ 'widgets.chart.hover-individual-points' | translate }} - +
+
+ {{ 'widgets.chart.show-cumulative-values' | translate }} - +
+
+ {{ 'widgets.chart.hide-zero-false-values' | translate }} - - -
+
+ + -
-
- widgets.chart.grid-settings - - {{ 'widgets.chart.show-vertical-lines' | translate }} - - - {{ 'widgets.chart.show-horizontal-lines' | translate }} - - - widgets.chart.grid-outline-border-width - - - - - - - - -
-
- widgets.chart.xaxis-settings - - widgets.chart.axis-title - - -
- widgets.chart.xaxis-tick-labels-settings - - - - - {{ 'widgets.chart.show-tick-labels' | translate }} - - - - widget-config.advanced-settings - - - - - - - -
-
-
- widgets.chart.yaxis-settings - - widgets.chart.axis-title - - -
- - widgets.chart.min-scale-value - - - - widgets.chart.max-scale-value - - -
-
- widgets.chart.yaxis-tick-labels-settings - - - - - {{ 'widgets.chart.show-tick-labels' | translate }} - - - - widget-config.advanced-settings - - - - - -
- - widgets.chart.tick-step-size - - - - widgets.chart.number-of-decimals - - -
- - -
-
-
-
-
- widgets.chart.comparison-settings + +
+
widgets.chart.comparison-settings
- {{ 'widgets.chart.enable-comparison' | translate }} - + widget-config.advanced-settings -
- - widgets.chart.time-for-comparison +
+
{{ 'widgets.chart.time-for-comparison' | translate }}
+ {{ 'widgets.chart.time-for-comparison-previous-interval' | translate }} @@ -277,18 +310,29 @@ - - widgets.chart.custom-interval-value - +
+
+
widgets.chart.custom-interval-value
+ + -
- widgets.chart.comparison-x-axis-settings - - widgets.chart.axis-title - +
+
+
widgets.chart.comparison-x-axis-settings
+
+
widgets.chart.axis-title
+ + - - widgets.chart.axis-position +
+
+ + {{ 'widgets.chart.show-tick-labels' | translate }} + +
+
+
{{ 'widgets.chart.axis-position' | translate }}
+ {{ 'widgets.chart.axis-position-top' | translate }} @@ -298,31 +342,28 @@ - - {{ 'widgets.chart.show-tick-labels' | translate }} - -
- + + - -
- widgets.chart.custom-legend-settings + +
+
widgets.chart.custom-legend-settings
- - - + + + {{ 'widgets.chart.enable-custom-legend' | translate }} - + widget-config.advanced-settings -
- widgets.chart.label-keys-list +
+
widgets.chart.label-keys-list
@@ -348,8 +389,8 @@
-
+
-
- + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.ts index 98c4acd62b..9241f60dda 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/flot-widget-settings.component.ts @@ -45,6 +45,7 @@ import { defaultLegendConfig, widgetType } from '@shared/models/widget.models'; export const flotDefaultSettings = (chartType: ChartType): Partial => { const settings: Partial = { stack: false, + enableSelection: true, fontColor: '#545454', fontSize: 10, showTooltip: true, @@ -73,7 +74,8 @@ export const flotDefaultSettings = (chartType: ChartType): Partial + +
+
widgets.range-chart.range-chart-card-style
+
+ + {{ 'widgets.range-chart.data-zoom' | translate }} + +
+
+
{{ 'widgets.range-chart.range-colors' | translate }}
+ + +
+
+
{{ 'widgets.range-chart.out-of-range-color' | translate }}
+ + +
+
+ + {{ 'widgets.range-chart.fill-area' | translate }} + +
+
+ + + + + {{ 'widget-config.legend' | translate }} + + + + +
+
{{ 'legend.position' | translate }}
+ + + + {{ legendPositionTranslationMap.get(pos) | translate }} + + + +
+
+
{{ 'widgets.range-chart.legend-label' | translate }}
+
+ + + + +
+
+
+
+
+
+ + + + + {{ 'widgets.range-chart.tooltip' | translate }} + + + + +
+
{{ 'widgets.range-chart.tooltip-value' | translate }}
+
+ + + + +
+
+
+ + {{ 'widgets.range-chart.tooltip-date' | translate }} + +
+ + + + + +
+
+
+
{{ 'widgets.range-chart.tooltip-background-color' | translate }}
+ + +
+
+
{{ 'widgets.range-chart.tooltip-background-blur' | translate }}
+ + +
px
+
+
+
+
+
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts new file mode 100644 index 0000000000..f5fa02065a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts @@ -0,0 +1,147 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, Injector } from '@angular/core'; +import { + legendPositions, + legendPositionTranslationMap, + WidgetSettings, + WidgetSettingsComponent +} from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { formatValue } from '@core/utils'; +import { rangeChartDefaultSettings } from '@home/components/widget/lib/chart/range-chart-widget.models'; +import { DateFormatProcessor, DateFormatSettings } from '@shared/models/widget-settings.models'; + +@Component({ + selector: 'tb-range-chart-widget-settings', + templateUrl: './range-chart-widget-settings.component.html', + styleUrls: [] +}) +export class RangeChartWidgetSettingsComponent extends WidgetSettingsComponent { + + legendPositions = legendPositions; + + legendPositionTranslationMap = legendPositionTranslationMap; + + rangeChartWidgetSettingsForm: UntypedFormGroup; + + tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); + + tooltipDatePreviewFn = this._tooltipDatePreviewFn.bind(this); + + constructor(protected store: Store, + private $injector: Injector, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.rangeChartWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return {...rangeChartDefaultSettings}; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.rangeChartWidgetSettingsForm = this.fb.group({ + dataZoom: [settings.dataZoom, []], + rangeColors: [settings.rangeColors, []], + outOfRangeColor: [settings.outOfRangeColor, []], + fillArea: [settings.fillArea, []], + + showLegend: [settings.showLegend, []], + legendPosition: [settings.legendPosition, []], + legendLabelFont: [settings.legendLabelFont, []], + legendLabelColor: [settings.legendLabelColor, []], + + showTooltip: [settings.showTooltip, []], + tooltipValueFont: [settings.tooltipValueFont, []], + tooltipValueColor: [settings.tooltipValueColor, []], + tooltipShowDate: [settings.tooltipShowDate, []], + tooltipDateFormat: [settings.tooltipDateFormat, []], + tooltipDateFont: [settings.tooltipDateFont, []], + tooltipDateColor: [settings.tooltipDateColor, []], + tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], + tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], + + background: [settings.background, []] + }); + } + + protected validatorTriggers(): string[] { + return ['showLegend', 'showTooltip', 'tooltipShowDate']; + } + + protected updateValidators(emitEvent: boolean) { + const showLegend: boolean = this.rangeChartWidgetSettingsForm.get('showLegend').value; + const showTooltip: boolean = this.rangeChartWidgetSettingsForm.get('showTooltip').value; + const tooltipShowDate: boolean = this.rangeChartWidgetSettingsForm.get('tooltipShowDate').value; + + if (showLegend) { + this.rangeChartWidgetSettingsForm.get('legendPosition').enable(); + this.rangeChartWidgetSettingsForm.get('legendLabelFont').enable(); + this.rangeChartWidgetSettingsForm.get('legendLabelColor').enable(); + } else { + this.rangeChartWidgetSettingsForm.get('legendPosition').disable(); + this.rangeChartWidgetSettingsForm.get('legendLabelFont').disable(); + this.rangeChartWidgetSettingsForm.get('legendLabelColor').disable(); + } + + if (showTooltip) { + this.rangeChartWidgetSettingsForm.get('tooltipValueFont').enable(); + this.rangeChartWidgetSettingsForm.get('tooltipValueColor').enable(); + this.rangeChartWidgetSettingsForm.get('tooltipShowDate').enable({emitEvent: false}); + this.rangeChartWidgetSettingsForm.get('tooltipBackgroundColor').enable(); + this.rangeChartWidgetSettingsForm.get('tooltipBackgroundBlur').enable(); + if (tooltipShowDate) { + this.rangeChartWidgetSettingsForm.get('tooltipDateFormat').enable(); + this.rangeChartWidgetSettingsForm.get('tooltipDateFont').enable(); + this.rangeChartWidgetSettingsForm.get('tooltipDateColor').enable(); + } else { + this.rangeChartWidgetSettingsForm.get('tooltipDateFormat').disable(); + this.rangeChartWidgetSettingsForm.get('tooltipDateFont').disable(); + this.rangeChartWidgetSettingsForm.get('tooltipDateColor').disable(); + } + } else { + this.rangeChartWidgetSettingsForm.get('tooltipValueFont').disable(); + this.rangeChartWidgetSettingsForm.get('tooltipValueColor').disable(); + this.rangeChartWidgetSettingsForm.get('tooltipShowDate').disable({emitEvent: false}); + this.rangeChartWidgetSettingsForm.get('tooltipDateFormat').disable(); + this.rangeChartWidgetSettingsForm.get('tooltipDateFont').disable(); + this.rangeChartWidgetSettingsForm.get('tooltipDateColor').disable(); + this.rangeChartWidgetSettingsForm.get('tooltipBackgroundColor').disable(); + this.rangeChartWidgetSettingsForm.get('tooltipBackgroundBlur').disable(); + } + } + + private _tooltipValuePreviewFn(): string { + const units: string = this.widgetConfig.config.units; + const decimals: number = this.widgetConfig.config.decimals; + return formatValue(22, decimals, units, false); + } + + private _tooltipDatePreviewFn(): string { + const dateFormat: DateFormatSettings = this.rangeChartWidgetSettingsForm.get('tooltipDateFormat').value; + const processor = DateFormatProcessor.fromSettings(this.$injector, dateFormat); + processor.update(Date.now()); + return processor.formatted; + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings-panel.component.html new file mode 100644 index 0000000000..6dbc1a5fa3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings-panel.component.html @@ -0,0 +1,81 @@ + +
+
widgets.background.background-settings
+
+
+
widgets.background.background
+ + + {{ backgroundTypeTranslationsMap.get(type) | translate }} + + +
+ +
+
widgets.color.color
+ + +
+
+
+
widgets.background.overlay
+ + {{ 'widgets.background.enable-overlay' | translate }} + +
+
widgets.color.color
+ + +
+
+
widgets.background.blur
+ + +
px
+
+
+
+
+
+ widgets.background.preview +
+
+
+
+
+
+
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings-panel.component.scss new file mode 100644 index 0000000000..2e0cf6b2a6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings-panel.component.scss @@ -0,0 +1,79 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import '../../../../../../../../scss/constants'; + +.tb-background-settings-panel { + width: 620px; + display: flex; + flex-direction: column; + gap: 16px; + @media #{$mat-lt-md} { + width: 90vw; + } + .tb-background-settings-title { + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.87); + } + .tb-background-form-panel { + height: 192px; + .tb-background-color-field { + height: auto; + } + } + .tb-background-settings-preview { + flex: 1; + background: rgba(0, 0, 0, 0.04); + display: flex; + flex-direction: column; + padding: 12px 16px 24px 16px; + align-items: center; + gap: 12px; + } + .tb-background-settings-preview-title { + align-self: stretch; + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: 24px; + color: rgba(0, 0, 0, 0.38); + } + .tb-background-settings-preview-box { + position: relative; + width: 136px; + height: 118px; + border-radius: 2.666px; + } + .tb-background-settings-preview-overlay { + position: absolute; + border-radius: 2.666px; + top: 7.998px; + bottom: 7.998px; + left: 7.998px; + right: 7.998px; + } + .tb-background-settings-panel-buttons { + height: 40px; + display: flex; + flex-direction: row; + gap: 16px; + justify-content: flex-end; + align-items: flex-end; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings-panel.component.ts new file mode 100644 index 0000000000..6ee4ffaf00 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings-panel.component.ts @@ -0,0 +1,126 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { + backgroundStyle, + overlayStyle, + BackgroundSettings, + BackgroundType, + backgroundTypeTranslations, ComponentStyle +} from '@shared/models/widget-settings.models'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { Observable } from 'rxjs'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { DomSanitizer } from '@angular/platform-browser'; + +@Component({ + selector: 'tb-background-settings-panel', + templateUrl: './background-settings-panel.component.html', + providers: [], + styleUrls: ['./background-settings-panel.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class BackgroundSettingsPanelComponent extends PageComponent implements OnInit { + + @Input() + backgroundSettings: BackgroundSettings; + + @Input() + popover: TbPopoverComponent; + + @Output() + backgroundSettingsApplied = new EventEmitter(); + + backgroundType = BackgroundType; + + backgroundTypes = Object.keys(BackgroundType) as BackgroundType[]; + + backgroundTypeTranslationsMap = backgroundTypeTranslations; + + backgroundSettingsFormGroup: UntypedFormGroup; + + backgroundStyle$: Observable; + overlayStyle: ComponentStyle = {}; + + constructor(private fb: UntypedFormBuilder, + private imagePipe: ImagePipe, + private sanitizer: DomSanitizer, + protected store: Store, + private cd: ChangeDetectorRef) { + super(store); + } + + ngOnInit(): void { + this.backgroundSettingsFormGroup = this.fb.group( + { + type: [this.backgroundSettings?.type, []], + imageUrl: [this.backgroundSettings?.imageUrl, []], + color: [this.backgroundSettings?.color, []], + overlay: this.fb.group({ + enabled: [this.backgroundSettings?.overlay?.enabled, []], + color: [this.backgroundSettings?.overlay?.color, []], + blur: [this.backgroundSettings?.overlay?.blur, []] + }) + } + ); + this.backgroundSettingsFormGroup.get('type').valueChanges.subscribe(() => { + setTimeout(() => {this.popover?.updatePosition();}, 0); + }); + this.backgroundSettingsFormGroup.get('overlay').get('enabled').valueChanges.subscribe(() => { + this.updateValidators(); + }); + this.backgroundSettingsFormGroup.valueChanges.subscribe(() => { + this.updateBackgroundStyle(); + }); + this.updateValidators(); + this.updateBackgroundStyle(); + } + + cancel() { + this.popover?.hide(); + } + + applyColorSettings() { + const backgroundSettings = this.backgroundSettingsFormGroup.getRawValue(); + this.backgroundSettingsApplied.emit(backgroundSettings); + } + + private updateValidators() { + const overlayEnabled: boolean = this.backgroundSettingsFormGroup.get('overlay').get('enabled').value; + if (overlayEnabled) { + this.backgroundSettingsFormGroup.get('overlay').get('color').enable({emitEvent: false}); + this.backgroundSettingsFormGroup.get('overlay').get('blur').enable({emitEvent: false}); + } else { + this.backgroundSettingsFormGroup.get('overlay').get('color').disable({emitEvent: false}); + this.backgroundSettingsFormGroup.get('overlay').get('blur').disable({emitEvent: false}); + } + this.backgroundSettingsFormGroup.get('overlay').get('color').updateValueAndValidity({emitEvent: false}); + this.backgroundSettingsFormGroup.get('overlay').get('blur').updateValueAndValidity({emitEvent: false}); + } + + private updateBackgroundStyle() { + const background: BackgroundSettings = this.backgroundSettingsFormGroup.value; + this.backgroundStyle$ = backgroundStyle(background, this.imagePipe, this.sanitizer, true); + this.overlayStyle = overlayStyle(background.overlay); + this.cd.markForCheck(); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings.component.html new file mode 100644 index 0000000000..f3304eeca0 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings.component.html @@ -0,0 +1,30 @@ + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings.component.scss new file mode 100644 index 0000000000..6f73fbffa4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings.component.scss @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +button.mat-mdc-button-base.tb-box-button.tb-background-settings { + padding: 0; + .mat-mdc-button-persistent-ripple { + z-index: 2; + } + .tb-color-preview { + width: 38px; + min-width: 38px; + height: 38px; + &.box { + .tb-color-result { + &:after { + border: none; + } + } + .tb-color-overlay { + position: absolute; + border-radius: 3px; + top: 4px; + bottom: 4px; + left: 4px; + right: 4px; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings.component.ts new file mode 100644 index 0000000000..5f49c9993c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/background-settings.component.ts @@ -0,0 +1,138 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + ChangeDetectorRef, + Component, + forwardRef, + Input, + OnInit, + Renderer2, + ViewContainerRef, + ViewEncapsulation +} from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { + BackgroundSettings, + backgroundStyle, + BackgroundType, + ComponentStyle, + overlayStyle, validateAndUpdateBackgroundSettings +} from '@shared/models/widget-settings.models'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { + BackgroundSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/background-settings-panel.component'; +import { Observable, of } from 'rxjs'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { DomSanitizer } from '@angular/platform-browser'; + +@Component({ + selector: 'tb-background-settings', + templateUrl: './background-settings.component.html', + styleUrls: ['./background-settings.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => BackgroundSettingsComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class BackgroundSettingsComponent implements OnInit, ControlValueAccessor { + + @Input() + disabled = false; + + backgroundType = BackgroundType; + + modelValue: BackgroundSettings; + + backgroundStyle$: Observable; + + overlayStyle: ComponentStyle = {}; + + private propagateChange = null; + + constructor(private imagePipe: ImagePipe, + private sanitizer: DomSanitizer, + private popoverService: TbPopoverService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef, + private cd: ChangeDetectorRef) {} + + ngOnInit(): void { + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + if (this.disabled !== isDisabled) { + this.disabled = isDisabled; + this.updateBackgroundStyle(); + } + } + + writeValue(value: BackgroundSettings): void { + this.modelValue = validateAndUpdateBackgroundSettings(value); + this.updateBackgroundStyle(); + } + + openBackgroundSettingsPopup($event: Event, matButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = matButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const ctx: any = { + backgroundSettings: this.modelValue + }; + const backgroundSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, BackgroundSettingsPanelComponent, 'left', true, null, + ctx, + {}, + {}, {}, true); + backgroundSettingsPanelPopover.tbComponentRef.instance.popover = backgroundSettingsPanelPopover; + backgroundSettingsPanelPopover.tbComponentRef.instance.backgroundSettingsApplied.subscribe((backgroundSettings) => { + backgroundSettingsPanelPopover.hide(); + this.modelValue = backgroundSettings; + this.updateBackgroundStyle(); + this.propagateChange(this.modelValue); + }); + } + } + + private updateBackgroundStyle() { + if (!this.disabled) { + this.backgroundStyle$ = backgroundStyle(this.modelValue, this.imagePipe, this.sanitizer, true); + this.overlayStyle = overlayStyle(this.modelValue.overlay); + } else { + this.backgroundStyle$ = of({}); + this.overlayStyle = {}; + } + this.cd.markForCheck(); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.html new file mode 100644 index 0000000000..8db7f074de --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.html @@ -0,0 +1,50 @@ + +
{{ panelTitle }}
+
+
+
+
+
widgets.color.from
+ + + +
widgets.color.to
+ + + + + +
+ +
+
+
+ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.ts new file mode 100644 index 0000000000..f135d77092 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-list.component.ts @@ -0,0 +1,136 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + FormGroup, + NG_VALUE_ACCESSOR, + UntypedFormArray, + UntypedFormBuilder, + UntypedFormGroup +} from '@angular/forms'; +import { ColorRange } from '@shared/models/widget-settings.models'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; + +@Component({ + selector: 'tb-color-range-list', + templateUrl: './color-range-list.component.html', + styleUrls: ['color-settings-panel.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ColorRangeListComponent), + multi: true + } + ] +}) +export class ColorRangeListComponent implements OnInit, ControlValueAccessor, OnDestroy { + + @Input() + disabled: boolean; + + @Input() + popover: TbPopoverComponent; + + @Input() + panelTitle: string; + + modelValue: any; + + colorRangeListFormGroup: UntypedFormGroup; + + private destroy$ = new Subject(); + + private propagateChange = null; + + constructor(private fb: UntypedFormBuilder) {} + + ngOnInit(): void { + this.colorRangeListFormGroup = this.fb.group({ + rangeList: this.fb.array([]) + }); + + this.colorRangeListFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => this.updateModel()); + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + } + + writeValue(value: any): void { + if (value && value?.length) { + value.forEach((r) => this.rangeListFormArray.push(this.colorRangeControl(r), {emitEvent: false})); + } + } + + private colorRangeControl(range: ColorRange): UntypedFormGroup { + return this.fb.group({ + from: [range?.from, []], + to: [range?.to, []], + color: [range?.color, []] + }); + } + + get rangeListFormArray(): UntypedFormArray { + return this.colorRangeListFormGroup.get('rangeList') as UntypedFormArray; + } + + get rangeListFormGroups(): FormGroup[] { + return this.rangeListFormArray.controls as FormGroup[]; + } + + trackByRange(index: number, rangeControl: AbstractControl): any { + return rangeControl; + } + + removeRange(index: number) { + this.rangeListFormArray.removeAt(index); + this.colorRangeListFormGroup.markAsDirty(); + setTimeout(() => {this.popover?.updatePosition();}, 0); + } + + addRange() { + const newRange: ColorRange = { + color: 'rgba(0,0,0,0.87)' + }; + this.rangeListFormArray.push(this.colorRangeControl(newRange)); + this.colorRangeListFormGroup.markAsDirty(); + setTimeout(() => {this.popover?.updatePosition();}, 0); + } + + updateModel() { + this.propagateChange(this.colorRangeListFormGroup.get('rangeList').value); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-panel.component.html new file mode 100644 index 0000000000..449562f184 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-panel.component.html @@ -0,0 +1,57 @@ + +
+
widgets.color.color-settings
+
+ + +
+
+ + + +
+ +
+
+
+ + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-panel.component.ts new file mode 100644 index 0000000000..bdc9500605 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-panel.component.ts @@ -0,0 +1,78 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { ColorRange } from '@shared/models/widget-settings.models'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { deepClone } from '@core/utils'; +import { + ColorRangeSettingsComponent +} from '@home/components/widget/lib/settings/common/color-range-settings.component'; + +@Component({ + selector: 'tb-color-range-panel', + templateUrl: './color-range-panel.component.html', + providers: [], + styleUrls: ['./color-settings-panel.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class ColorRangePanelComponent extends PageComponent implements OnInit { + + @Input() + colorRangeSettings: Array; + + @Input() + popover: TbPopoverComponent; + + @Input() + settingsComponents: ColorRangeSettingsComponent[]; + + @Output() + colorRangeApplied = new EventEmitter>(); + + colorRangeFormGroup: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder, + protected store: Store) { + super(store); + } + + ngOnInit(): void { + this.colorRangeFormGroup = this.fb.group({ + rangeList: [this.colorRangeSettings, []] + }); + } + + copyColorSettings(comp: ColorRangeSettingsComponent) { + this.colorRangeSettings = deepClone(comp.modelValue); + this.colorRangeFormGroup.get('rangeList').patchValue(this.colorRangeSettings || [], {emitEvent: false}); + this.colorRangeFormGroup.markAsDirty(); + } + + cancel() { + this.popover?.hide(); + } + + applyColorRangeSettings() { + const colorRangeSettings = this.colorRangeFormGroup.get('rangeList').value; + this.colorRangeApplied.emit(colorRangeSettings); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-settings.component.html new file mode 100644 index 0000000000..cf0298e99a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-settings.component.html @@ -0,0 +1,27 @@ + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-settings.component.ts new file mode 100644 index 0000000000..31cafc6d96 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-range-settings.component.ts @@ -0,0 +1,168 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + Component, + forwardRef, + Injectable, + Input, + OnDestroy, + OnInit, + Renderer2, + ViewContainerRef +} from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { ColorRange, ComponentStyle } from '@shared/models/widget-settings.models'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { ColorRangePanelComponent } from '@home/components/widget/lib/settings/common/color-range-panel.component'; + +@Injectable() +export class ColorRangeSettingsComponentService { + + private colorSettingsComponents = new Set(); + + constructor() {} + + public registerColorSettingsComponent(comp: ColorRangeSettingsComponent) { + this.colorSettingsComponents.add(comp); + } + + public unregisterColorSettingsComponent(comp: ColorRangeSettingsComponent) { + this.colorSettingsComponents.delete(comp); + } + + public getOtherColorSettingsComponents(comp: ColorRangeSettingsComponent): ColorRangeSettingsComponent[] { + const result: ColorRangeSettingsComponent[] = []; + for (const component of this.colorSettingsComponents.values()) { + if (component.settingsKey && component.modelValue && component !== comp) { + result.push(component); + } + } + return result; + } + +} + +@Component({ + selector: 'tb-color-range-settings', + templateUrl: './color-range-settings.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ColorRangeSettingsComponent), + multi: true + } + ] +}) +export class ColorRangeSettingsComponent implements OnInit, ControlValueAccessor, OnDestroy { + + @Input() + disabled: boolean; + + @Input() + settingsKey: string; + + modelValue: Array; + + colorStyle: ComponentStyle = {}; + + private propagateChange = null; + + constructor(private popoverService: TbPopoverService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef, + private colorSettingsComponentService: ColorRangeSettingsComponentService) {} + + ngOnInit(): void { + this.colorSettingsComponentService.registerColorSettingsComponent(this); + } + + ngOnDestroy() { + this.colorSettingsComponentService.unregisterColorSettingsComponent(this); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + this.updateColorStyle(); + } + + writeValue(value: Array): void { + this.modelValue = value; + this.updateColorStyle(); + } + + openColorRangeSettingsPopup($event: Event, matButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = matButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const ctx: any = { + colorRangeSettings: this.modelValue, + settingsComponents: this.colorSettingsComponentService.getOtherColorSettingsComponents(this) + }; + const colorRangeSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, ColorRangePanelComponent, 'left', true, null, + ctx, + {}, + {}, {}, true); + colorRangeSettingsPanelPopover.tbComponentRef.instance.popover = colorRangeSettingsPanelPopover; + colorRangeSettingsPanelPopover.tbComponentRef.instance.colorRangeApplied.subscribe((colorRangeSettings) => { + colorRangeSettingsPanelPopover.hide(); + this.modelValue = colorRangeSettings; + this.updateColorStyle(); + this.propagateChange(this.modelValue); + }); + } + } + + private updateColorStyle() { + if (!this.disabled && this.modelValue) { + let colors: string[] = []; + if (this.modelValue.length) { + const rangeColors = this.modelValue.slice(0, Math.min(3, this.modelValue.length)).map(r => r.color); + colors = colors.concat(rangeColors); + } + if (!colors.length) { + this.colorStyle = {}; + } else if (colors.length === 1) { + this.colorStyle = {backgroundColor: colors[0]}; + } else { + const gradientValues: string[] = []; + const step = 100 / colors.length; + for (let i = 0; i < colors.length; i++) { + gradientValues.push(`${colors[i]} ${step*i}%`); + gradientValues.push(`${colors[i]} ${step*(i+1)}%`); + } + this.colorStyle = {background: `linear-gradient(90deg, ${gradientValues.join(', ')})`}; + } + } else { + this.colorStyle = {}; + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.html new file mode 100644 index 0000000000..1864e781dd --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.html @@ -0,0 +1,88 @@ + +
+
widgets.color.color-settings
+
+ + + {{ colorTypeTranslationsMap.get(type) | translate }} + + +
+
+
widgets.color.color
+ + +
+
+
+
+ + +
+
+ +
+
+ + + +
+ +
+
+
+ + + +
+
+ + +
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.scss new file mode 100644 index 0000000000..0036723b92 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.scss @@ -0,0 +1,85 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import '../../../../../../../../scss/constants'; + +.tb-color-settings-panel { + width: 500px; + height: 470px; + display: flex; + flex-direction: column; + gap: 16px; + @media #{$mat-xs} { + width: 90vw; + } + .tb-color-settings-title { + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.87); + } + .tb-color-ranges-panel { + flex: 1; + min-height: 0; + gap: 16px; + display: flex; + flex-direction: column; + } + .tb-color-ranges { + flex: 1; + gap: 12px; + display: flex; + flex-direction: column; + overflow: auto; + } + .tb-form-row { + height: auto; + .tb-value-range-text { + width: 64px; + font-size: 14px; + color: rgba(0, 0, 0, 0.38); + @media #{$mat-xs} { + width: auto; + } + &.tb-value-range-text-to { + text-align: center; + } + } + } + button.mat-mdc-button-base.tb-add-color-range { + &:not(:disabled) { + color: rgba(0, 0, 0, 0.54); + } + &:disabled { + color: rgba(0, 0, 0, 0.12); + } + } + .tb-color-settings-panel-body { + flex: 1; + display: flex; + flex-direction: column; + min-height: 0; + } + .tb-color-settings-panel-buttons { + height: 40px; + display: flex; + flex-direction: row; + gap: 16px; + justify-content: flex-end; + align-items: flex-end; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.ts new file mode 100644 index 0000000000..6ae04ec94e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings-panel.component.ts @@ -0,0 +1,100 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { ColorSettings, ColorType, colorTypeTranslations } from '@shared/models/widget-settings.models'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { deepClone } from '@core/utils'; +import { WidgetService } from '@core/http/widget.service'; +import { ColorSettingsComponent } from '@home/components/widget/lib/settings/common/color-settings.component'; + +@Component({ + selector: 'tb-color-settings-panel', + templateUrl: './color-settings-panel.component.html', + providers: [], + styleUrls: ['./color-settings-panel.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class ColorSettingsPanelComponent extends PageComponent implements OnInit { + + @Input() + colorSettings: ColorSettings; + + @Input() + popover: TbPopoverComponent; + + @Input() + settingsComponents: ColorSettingsComponent[]; + + @Output() + colorSettingsApplied = new EventEmitter(); + + colorType = ColorType; + + colorTypes = Object.keys(ColorType) as ColorType[]; + + colorTypeTranslationsMap = colorTypeTranslations; + + colorSettingsFormGroup: UntypedFormGroup; + + functionScopeVariables = this.widgetService.getWidgetScopeVariables(); + + constructor(private fb: UntypedFormBuilder, + private widgetService: WidgetService, + protected store: Store) { + super(store); + } + + ngOnInit(): void { + this.colorSettingsFormGroup = this.fb.group( + { + type: [this.colorSettings?.type || ColorType.constant, []], + color: [this.colorSettings?.color, []], + rangeList: [this.colorSettings?.rangeList, []], + colorFunction: [this.colorSettings?.colorFunction, []] + } + ); + this.colorSettingsFormGroup.get('type').valueChanges.subscribe(() => { + setTimeout(() => {this.popover?.updatePosition();}, 0); + }); + } + + copyColorSettings(comp: ColorSettingsComponent) { + const sourceSettings = deepClone(comp.modelValue); + this.colorSettings = sourceSettings; + this.colorSettingsFormGroup.patchValue({ + type: this.colorSettings.type, + color: this.colorSettings.color, + colorFunction: this.colorSettings.colorFunction, + rangeList: this.colorSettings.rangeList || [] + }, {emitEvent: false}); + this.colorSettingsFormGroup.markAsDirty(); + } + + cancel() { + this.popover?.hide(); + } + + applyColorSettings() { + const colorSettings = this.colorSettingsFormGroup.value; + this.colorSettingsApplied.emit(colorSettings); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings.component.html new file mode 100644 index 0000000000..b5e26cc9ac --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings.component.html @@ -0,0 +1,30 @@ + + + +
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings.component.ts new file mode 100644 index 0000000000..39893d27f0 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/color-settings.component.ts @@ -0,0 +1,170 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + Component, + forwardRef, + Injectable, + Input, + OnDestroy, + OnInit, + Renderer2, + ViewContainerRef +} from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { ColorSettings, ColorType, ComponentStyle } from '@shared/models/widget-settings.models'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { + ColorSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/color-settings-panel.component'; + +@Injectable() +export class ColorSettingsComponentService { + + private colorSettingsComponents = new Set(); + + constructor() {} + + public registerColorSettingsComponent(comp: ColorSettingsComponent) { + this.colorSettingsComponents.add(comp); + } + + public unregisterColorSettingsComponent(comp: ColorSettingsComponent) { + this.colorSettingsComponents.delete(comp); + } + + public getOtherColorSettingsComponents(comp: ColorSettingsComponent): ColorSettingsComponent[] { + const result: ColorSettingsComponent[] = []; + for (const component of this.colorSettingsComponents.values()) { + if (component.settingsKey && component.modelValue && component !== comp) { + result.push(component); + } + } + return result; + } + +} + +@Component({ + selector: 'tb-color-settings', + templateUrl: './color-settings.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ColorSettingsComponent), + multi: true + } + ] +}) +export class ColorSettingsComponent implements OnInit, ControlValueAccessor, OnDestroy { + + @Input() + disabled: boolean; + + @Input() + settingsKey: string; + + colorType = ColorType; + + modelValue: ColorSettings; + + colorStyle: ComponentStyle = {}; + + private propagateChange = null; + + constructor(private popoverService: TbPopoverService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef, + private colorSettingsComponentService: ColorSettingsComponentService) {} + + ngOnInit(): void { + this.colorSettingsComponentService.registerColorSettingsComponent(this); + } + + ngOnDestroy() { + this.colorSettingsComponentService.unregisterColorSettingsComponent(this); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + this.updateColorStyle(); + } + + writeValue(value: ColorSettings): void { + this.modelValue = value; + this.updateColorStyle(); + } + + openColorSettingsPopup($event: Event, matButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = matButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const ctx: any = { + colorSettings: this.modelValue, + settingsComponents: this.colorSettingsComponentService.getOtherColorSettingsComponents(this) + }; + const colorSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, ColorSettingsPanelComponent, 'left', true, null, + ctx, + {}, + {}, {}, true); + colorSettingsPanelPopover.tbComponentRef.instance.popover = colorSettingsPanelPopover; + colorSettingsPanelPopover.tbComponentRef.instance.colorSettingsApplied.subscribe((colorSettings) => { + colorSettingsPanelPopover.hide(); + this.modelValue = colorSettings; + this.updateColorStyle(); + this.propagateChange(this.modelValue); + }); + } + } + + private updateColorStyle() { + if (!this.disabled && this.modelValue) { + let colors: string[] = [this.modelValue.color]; + if (this.modelValue.type === ColorType.range && this.modelValue.rangeList?.length) { + const rangeColors = this.modelValue.rangeList.slice(0, Math.min(2, this.modelValue.rangeList.length)).map(r => r.color); + colors = colors.concat(rangeColors); + } + if (colors.length === 1) { + this.colorStyle = {backgroundColor: colors[0]}; + } else { + const gradientValues: string[] = []; + const step = 100 / colors.length; + for (let i = 0; i < colors.length; i++) { + gradientValues.push(`${colors[i]} ${step*i}%`); + gradientValues.push(`${colors[i]} ${step*(i+1)}%`); + } + this.colorStyle = {background: `linear-gradient(90deg, ${gradientValues.join(', ')})`}; + } + } else { + this.colorStyle = {}; + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/count-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/count-widget-settings.component.html new file mode 100644 index 0000000000..60559721d6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/count-widget-settings.component.html @@ -0,0 +1,110 @@ + + + + + {{ countCardLayoutTranslationMap.get(layout) | translate }} + + +
+ + {{ 'widgets.count.auto-scale' | translate }} + +
+
+ + {{ 'widgets.count.label' | translate }} + +
+ + + + + + + +
+
+
+ + {{ 'widgets.count.icon' | translate }} + +
+ + + + + + + + +
+
+
+ + {{ 'widgets.count.icon-background' | translate }} + +
+ + + + + + +
+
+
+
widgets.count.value
+
+ + + + +
+
+
+ + {{ 'widgets.count.chevron' | translate }} + +
+ + + + + + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/count-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/count-widget-settings.component.ts new file mode 100644 index 0000000000..02e2ad4dc2 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/count-widget-settings.component.ts @@ -0,0 +1,203 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + AbstractControl, + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormGroup, + Validators +} from '@angular/forms'; +import {Store} from '@ngrx/store'; +import {AppState} from '@core/core.state'; +import { + alarmCountCardLayoutImages, + CountCardLayout, + countCardLayouts, + countCardLayoutTranslations, + CountWidgetSettings, entityCountCardLayoutImages +} from '@home/components/widget/lib/count/count-widget.models'; +import {PageComponent} from '@shared/components/page.component'; +import {Component, forwardRef, Input, OnInit} from '@angular/core'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { + valueCardLayoutImages, + valueCardLayoutTranslations +} from '@home/components/widget/lib/cards/value-card-widget.models'; + +@Component({ + selector: 'tb-count-widget-settings', + templateUrl: './count-widget-settings.component.html', + styleUrls: ['./../widget-settings.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => CountWidgetSettingsComponent), + multi: true + } + ] +}) +export class CountWidgetSettingsComponent extends PageComponent implements OnInit, ControlValueAccessor { + + @Input() + disabled: boolean; + + @coerceBoolean() + @Input() + alarmElseEntity: boolean; + + private propagateChange = null; + + countCardLayouts = countCardLayouts; + + countCardLayoutTranslationMap = countCardLayoutTranslations; + countCardLayoutImageMap: Map; + + countWidgetConfigForm: UntypedFormGroup; + + constructor(protected store: Store, + private fb: UntypedFormBuilder) { + super(store); + } + + ngOnInit(): void { + this.countCardLayoutImageMap = this.alarmElseEntity ? alarmCountCardLayoutImages : entityCountCardLayoutImages; + this.countWidgetConfigForm = this.fb.group({ + layout: [null, []], + autoScale: [null, []], + + showLabel: [null, []], + label: [null, []], + labelFont: [null, []], + labelColor: [null, []], + + showIcon: [null, []], + iconSize: [null, [Validators.min(0)]], + iconSizeUnit: [null, []], + icon: [null, []], + iconColor: [null, []], + + showIconBackground: [null, []], + iconBackgroundSize: [null, [Validators.min(0)]], + iconBackgroundSizeUnit: [null, []], + iconBackgroundColor: [null, []], + + valueFont: [null, []], + valueColor: [null, []], + + showChevron: [null, []], + chevronSize: [null, [Validators.min(0)]], + chevronSizeUnit: [null, []], + chevronColor: [null, []], + }); + this.countWidgetConfigForm.valueChanges.subscribe(() => { + this.updateModel(); + }); + for (const trigger of ['showLabel', 'showIcon', 'showIconBackground', 'showChevron']) { + const path = trigger.split('.'); + let control: AbstractControl = this.countWidgetConfigForm; + for (const part of path) { + control = this.countWidgetConfigForm.get(part); + } + control.valueChanges.subscribe(() => { + this.updateValidators(); + }); + } + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.countWidgetConfigForm.disable({emitEvent: false}); + } else { + this.countWidgetConfigForm.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(value: CountWidgetSettings): void { + this.countWidgetConfigForm.patchValue( + value, {emitEvent: false} + ); + this.updateValidators(); + } + + private updateModel() { + const value: CountWidgetSettings = this.countWidgetConfigForm.value; + this.propagateChange(value); + } + + protected updateValidators() { + const showLabel: boolean = this.countWidgetConfigForm.get('showLabel').value; + const showIcon: boolean = this.countWidgetConfigForm.get('showIcon').value; + const showIconBackground: boolean = this.countWidgetConfigForm.get('showIconBackground').value; + const showChevron: boolean = this.countWidgetConfigForm.get('showChevron').value; + + if (showLabel) { + this.countWidgetConfigForm.get('label').enable({emitEvent: false}); + this.countWidgetConfigForm.get('labelFont').enable({emitEvent: false}); + this.countWidgetConfigForm.get('labelColor').enable({emitEvent: false}); + } else { + this.countWidgetConfigForm.get('label').disable({emitEvent: false}); + this.countWidgetConfigForm.get('labelFont').disable({emitEvent: false}); + this.countWidgetConfigForm.get('labelColor').disable({emitEvent: false}); + } + + if (showIcon) { + this.countWidgetConfigForm.get('iconSize').enable({emitEvent: false}); + this.countWidgetConfigForm.get('iconSizeUnit').enable({emitEvent: false}); + this.countWidgetConfigForm.get('icon').enable({emitEvent: false}); + this.countWidgetConfigForm.get('iconColor').enable({emitEvent: false}); + } else { + this.countWidgetConfigForm.get('iconSize').disable({emitEvent: false}); + this.countWidgetConfigForm.get('iconSizeUnit').disable({emitEvent: false}); + this.countWidgetConfigForm.get('icon').disable({emitEvent: false}); + this.countWidgetConfigForm.get('iconColor').disable({emitEvent: false}); + } + + if (showIconBackground) { + this.countWidgetConfigForm.get('iconBackgroundSize').enable({emitEvent: false}); + this.countWidgetConfigForm.get('iconBackgroundSizeUnit').enable({emitEvent: false}); + this.countWidgetConfigForm.get('iconBackgroundColor').enable({emitEvent: false}); + } else { + this.countWidgetConfigForm.get('iconBackgroundSize').disable({emitEvent: false}); + this.countWidgetConfigForm.get('iconBackgroundSizeUnit').disable({emitEvent: false}); + this.countWidgetConfigForm.get('iconBackgroundColor').disable({emitEvent: false}); + } + + if (showChevron) { + this.countWidgetConfigForm.get('chevronSize').enable({emitEvent: false}); + this.countWidgetConfigForm.get('chevronSizeUnit').enable({emitEvent: false}); + this.countWidgetConfigForm.get('chevronColor').enable({emitEvent: false}); + } else { + this.countWidgetConfigForm.get('chevronSize').disable({emitEvent: false}); + this.countWidgetConfigForm.get('chevronSizeUnit').disable({emitEvent: false}); + this.countWidgetConfigForm.get('chevronColor').disable({emitEvent: false}); + } + } + + protected readonly valueCardLayoutTranslations = valueCardLayoutTranslations; + protected readonly valueCardLayoutImages = valueCardLayoutImages; +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.html new file mode 100644 index 0000000000..1c00141b1d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.html @@ -0,0 +1,24 @@ + + + + + + {{ cssUnit }} + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.ts new file mode 100644 index 0000000000..61a6bc4865 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/css-unit-select.component.ts @@ -0,0 +1,87 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; +import { cssUnit, cssUnits } from '@shared/models/widget-settings.models'; +import { coerceBoolean } from '@shared/decorators/coercion'; + +@Component({ + selector: 'tb-css-unit-select', + templateUrl: './css-unit-select.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => CssUnitSelectComponent), + multi: true + } + ] +}) +export class CssUnitSelectComponent implements OnInit, ControlValueAccessor { + + @Input() + disabled: boolean; + + @Input() + @coerceBoolean() + allowEmpty = false; + + cssUnitsList = cssUnits; + + cssUnitFormControl: UntypedFormControl; + + modelValue: cssUnit; + + private propagateChange = null; + + constructor() {} + + ngOnInit(): void { + this.cssUnitFormControl = new UntypedFormControl(); + this.cssUnitFormControl.valueChanges.subscribe((value: cssUnit) => { + this.updateModel(value); + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.cssUnitFormControl.disable({emitEvent: false}); + } else { + this.cssUnitFormControl.enable({emitEvent: false}); + } + } + + writeValue(value: cssUnit): void { + this.modelValue = value; + this.cssUnitFormControl.patchValue(this.modelValue, {emitEvent: false}); + } + + updateModel(value: cssUnit): void { + if (this.modelValue !== value) { + this.modelValue = value; + this.propagateChange(this.modelValue); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-select.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-select.component.html new file mode 100644 index 0000000000..7310d0c403 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-select.component.html @@ -0,0 +1,26 @@ + + + + {{ dateFormatDisplayValue(dateFormat) }} + + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-select.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-select.component.ts new file mode 100644 index 0000000000..2aba8866ed --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-select.component.ts @@ -0,0 +1,151 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, forwardRef, Input, OnInit, Renderer2, ViewChild, ViewContainerRef } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; +import { compareDateFormats, dateFormats, DateFormatSettings } from '@shared/models/widget-settings.models'; +import { TranslateService } from '@ngx-translate/core'; +import { DatePipe } from '@angular/common'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { deepClone } from '@core/utils'; +import { + DateFormatSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/date-format-settings-panel.component'; +import { coerceBoolean } from '@shared/decorators/coercion'; + +@Component({ + selector: 'tb-date-format-select', + templateUrl: './date-format-select.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DateFormatSelectComponent), + multi: true + } + ] +}) +export class DateFormatSelectComponent implements OnInit, ControlValueAccessor { + + @ViewChild('customFormatButton', {static: false}) + customFormatButton: MatButton; + + @Input() + disabled: boolean; + + @Input() + @coerceBoolean() + excludeLastUpdateAgo = false; + + dateFormatList: DateFormatSettings[]; + + dateFormatsCompare = compareDateFormats; + + dateFormatFormControl: UntypedFormControl; + + modelValue: DateFormatSettings; + + private propagateChange = null; + + private formatCache: {[format: string]: string} = {}; + + constructor(private translate: TranslateService, + private date: DatePipe, + private popoverService: TbPopoverService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef) {} + + ngOnInit(): void { + this.dateFormatList = this.excludeLastUpdateAgo ? + dateFormats.filter(format => !format.lastUpdateAgo) : dateFormats; + this.dateFormatFormControl = new UntypedFormControl(); + this.dateFormatFormControl.valueChanges.subscribe((value: DateFormatSettings) => { + this.updateModel(value); + if (value?.custom) { + setTimeout(() => { + this.openDateFormatSettingsPopup(null, this.customFormatButton); + }, 0); + } + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.dateFormatFormControl.disable({emitEvent: false}); + } else { + this.dateFormatFormControl.enable({emitEvent: false}); + } + } + + writeValue(value: DateFormatSettings): void { + this.modelValue = value; + this.dateFormatFormControl.patchValue(this.modelValue, {emitEvent: false}); + } + + updateModel(value: DateFormatSettings): void { + if (!compareDateFormats(this.modelValue, value)) { + this.modelValue = value; + this.propagateChange(this.modelValue); + } + } + + dateFormatDisplayValue(value: DateFormatSettings): string { + if (value.custom) { + return this.translate.instant('date.custom-date'); + } else if (value.lastUpdateAgo) { + return this.translate.instant('date.last-update-n-ago'); + } else { + if (!this.formatCache[value.format]) { + this.formatCache[value.format] = this.date.transform(Date.now(), value.format); + } + return this.formatCache[value.format]; + } + } + + openDateFormatSettingsPopup($event: Event, matButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = matButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const ctx: any = { + dateFormat: deepClone(this.modelValue) + }; + const dateFormatSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, DateFormatSettingsPanelComponent, 'top', true, null, + ctx, + {}, + {}, {}, true); + dateFormatSettingsPanelPopover.tbComponentRef.instance.popover = dateFormatSettingsPanelPopover; + dateFormatSettingsPanelPopover.tbComponentRef.instance.dateFormatApplied.subscribe((dateFormat) => { + dateFormatSettingsPanelPopover.hide(); + this.modelValue = dateFormat; + this.propagateChange(this.modelValue); + }); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-settings-panel.component.html new file mode 100644 index 0000000000..279b48fb3a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-settings-panel.component.html @@ -0,0 +1,47 @@ + +
+
date.custom-date
+
+
date.format
+ + +
+
+
+ +
+
date.preview
+
{{ previewText }}
+
+
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-settings-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-settings-panel.component.scss new file mode 100644 index 0000000000..7ca3237b5d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-settings-panel.component.scss @@ -0,0 +1,69 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import '../../../../../../../../scss/constants'; + +.tb-date-format-settings-panel { + width: 500px; + display: flex; + flex-direction: column; + gap: 16px; + @media #{$mat-xs} { + width: 90vw; + } + .tb-date-format-settings-title { + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.87); + } + .tb-form-row { + .fixed-title-width { + min-width: 120px; + } + &.date-format-preview { + align-items: flex-start; + .preview-text { + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; + letter-spacing: 0.2px; + color: rgba(0, 0, 0, 0.38); + } + } + .mat-mdc-form-field.tb-date-format-input { + .mat-mdc-text-field-wrapper.mdc-text-field--outlined { + .mat-mdc-form-field-flex { + .mat-mdc-form-field-icon-suffix { + display: flex; + align-items: center; + line-height: normal; + } + } + } + } + } + .tb-date-format-settings-panel-buttons { + height: 60px; + display: flex; + flex-direction: row; + gap: 16px; + justify-content: flex-end; + align-items: flex-end; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-settings-panel.component.ts new file mode 100644 index 0000000000..be120b80f6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/date-format-settings-panel.component.ts @@ -0,0 +1,70 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { DateFormatSettings } from '@shared/models/widget-settings.models'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { UntypedFormControl, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { DatePipe } from '@angular/common'; + +@Component({ + selector: 'tb-date-format-settings-panel', + templateUrl: './date-format-settings-panel.component.html', + providers: [], + styleUrls: ['./date-format-settings-panel.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class DateFormatSettingsPanelComponent extends PageComponent implements OnInit { + + @Input() + dateFormat: DateFormatSettings; + + @Input() + popover: TbPopoverComponent; + + @Output() + dateFormatApplied = new EventEmitter(); + + dateFormatFormControl: UntypedFormControl; + + previewText = ''; + + constructor(private date: DatePipe, + protected store: Store) { + super(store); + } + + ngOnInit(): void { + this.dateFormatFormControl = new UntypedFormControl(this.dateFormat.format, [Validators.required]); + this.dateFormatFormControl.valueChanges.subscribe((value: string) => { + this.previewText = this.date.transform(Date.now(), value); + }); + this.previewText = this.date.transform(Date.now(), this.dateFormat.format); + } + + cancel() { + this.popover?.hide(); + } + + applyDateFormat() { + this.dateFormat.format = this.dateFormatFormControl.value; + this.dateFormatApplied.emit(this.dateFormat); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.html new file mode 100644 index 0000000000..c5687be400 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.html @@ -0,0 +1,111 @@ + +
+
widgets.widget-font.font-settings
+
+
widgets.widget-font.size
+
+ + + + +
+
widgets.widget-font.auto
+
+
+
widgets.widget-font.font-family
+ + + + + + + + + +
+
+
widgets.widget-font.font-weight
+ + + + + + {{ fontWeightTranslationsMap.has(weight) ? (fontWeightTranslationsMap.get(weight) | translate) : weight }} + + + +
+
+
widgets.widget-font.font-style
+ + + + + + {{ fontStyleTranslationsMap.get(style) | translate }} + + + +
+
+
widgets.widget-font.line-height
+ + + +
widgets.widget-font.auto
+
+ +
+
widgets.widget-font.preview
+
{{ previewText }}
+
+
+ + + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.scss new file mode 100644 index 0000000000..03b0ee511b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.scss @@ -0,0 +1,55 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.tb-font-settings-panel { + width: 100%; + display: flex; + flex-direction: column; + gap: 16px; + .tb-font-settings-title { + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.87); + } + .tb-font-settings-auto { + padding: 8px 12px; + color: rgba(0, 0, 0, 0.38); + } + .tb-form-row { + .fixed-title-width { + min-width: 120px; + } + &.font-preview { + align-items: flex-start; + .preview-text { + max-height: 300px; + max-width: 400px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + } + } + .tb-font-settings-panel-buttons { + height: 60px; + display: flex; + flex-direction: row; + gap: 16px; + justify-content: flex-end; + align-items: flex-end; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.ts new file mode 100644 index 0000000000..562001e3c4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings-panel.component.ts @@ -0,0 +1,161 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + Component, + ElementRef, + EventEmitter, + Input, + OnInit, + Output, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { + commonFonts, + ComponentStyle, + Font, + fontStyles, + fontStyleTranslations, + fontWeights, + fontWeightTranslations, isFontPartiallySet, + textStyle +} from '@shared/models/widget-settings.models'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { Observable } from 'rxjs'; +import { map, startWith, tap } from 'rxjs/operators'; +import { coerceBoolean } from '@shared/decorators/coercion'; + +@Component({ + selector: 'tb-font-settings-panel', + templateUrl: './font-settings-panel.component.html', + providers: [], + styleUrls: ['./font-settings-panel.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class FontSettingsPanelComponent extends PageComponent implements OnInit { + + @Input() + font: Font; + + @Input() + previewText = 'AaBbCcDd'; + + @Input() + initialPreviewStyle: ComponentStyle; + + @Input() + @coerceBoolean() + clearButton = false; + + @Input() + @coerceBoolean() + autoScale = false; + + @Input() + @coerceBoolean() + disabledLineHeight = false; + + @Input() + popover: TbPopoverComponent; + + @Output() + fontApplied = new EventEmitter(); + + @ViewChild('familyInput', {static: true}) familyInput: ElementRef; + + fontWeightsList = fontWeights; + + fontWeightTranslationsMap = fontWeightTranslations; + + fontStylesList = fontStyles; + + fontStyleTranslationsMap = fontStyleTranslations; + + fontFormGroup: UntypedFormGroup; + + filteredFontFamilies: Observable>; + + familySearchText = ''; + + previewStyle: ComponentStyle = {}; + + constructor(private fb: UntypedFormBuilder, + protected store: Store) { + super(store); + } + + ngOnInit(): void { + this.fontFormGroup = this.fb.group( + { + size: [{value: this.font?.size, disabled: this.autoScale}, [Validators.min(0)]], + sizeUnit: [{ value: (this.font?.sizeUnit || 'px'), disabled: this.autoScale}, []], + family: [this.font?.family, []], + weight: [this.font?.weight, []], + style: [this.font?.style, []], + lineHeight: [{ value: this.font?.lineHeight, disabled: this.autoScale || this.disabledLineHeight }, []] + } + ); + this.updatePreviewStyle(this.font); + this.fontFormGroup.valueChanges.subscribe((font: Font) => { + if (this.fontFormGroup.valid) { + this.updatePreviewStyle(font); + setTimeout(() => {this.popover?.updatePosition();}, 0); + } + }); + this.filteredFontFamilies = this.fontFormGroup.get('family').valueChanges + .pipe( + startWith(''), + tap((searchText) => { this.familySearchText = searchText || ''; }), + map(() => commonFonts.filter(f => f.toUpperCase().includes(this.familySearchText.toUpperCase()))) + ); + } + + clearFamily() { + this.fontFormGroup.get('family').patchValue(null, {emitEvent: true}); + setTimeout(() => { + this.familyInput.nativeElement.blur(); + this.familyInput.nativeElement.focus(); + }, 0); + } + + cancel() { + this.popover?.hide(); + } + + applyFont() { + const font = this.fontFormGroup.value; + this.fontApplied.emit(font); + } + + clearDisabled(): boolean { + return !isFontPartiallySet(this.fontFormGroup.value); + } + + clearFont() { + this.fontFormGroup.reset({sizeUnit: 'px'}); + this.fontFormGroup.markAsDirty(); + } + + private updatePreviewStyle(font: Font) { + this.previewStyle = {...(this.initialPreviewStyle || {}), ...textStyle(font)}; + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings.component.html new file mode 100644 index 0000000000..574ce98c1d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings.component.html @@ -0,0 +1,25 @@ + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings.component.ts new file mode 100644 index 0000000000..0fc3b20da1 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/font-settings.component.ts @@ -0,0 +1,122 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, forwardRef, Input, OnInit, Renderer2, ViewContainerRef } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { ComponentStyle, Font } from '@shared/models/widget-settings.models'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { FontSettingsPanelComponent } from '@home/components/widget/lib/settings/common/font-settings-panel.component'; +import { isDefinedAndNotNull } from '@core/utils'; +import { coerceBoolean } from '@shared/decorators/coercion'; + +@Component({ + selector: 'tb-font-settings', + templateUrl: './font-settings.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => FontSettingsComponent), + multi: true + } + ] +}) +export class FontSettingsComponent implements OnInit, ControlValueAccessor { + + @Input() + disabled: boolean; + + @Input() + previewText: string | (() => string); + + @Input() + initialPreviewStyle: ComponentStyle; + + @Input() + @coerceBoolean() + clearButton = false; + + @Input() + @coerceBoolean() + autoScale = false; + + @Input() + @coerceBoolean() + disabledLineHeight = false; + + private modelValue: Font; + + private propagateChange = null; + + constructor(private popoverService: TbPopoverService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef) {} + + ngOnInit(): void { + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + } + + writeValue(value: Font): void { + this.modelValue = value; + } + + openFontSettingsPopup($event: Event, matButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = matButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const ctx: any = { + font: this.modelValue, + initialPreviewStyle: this.initialPreviewStyle, + clearButton: this.clearButton, + autoScale: this.autoScale, + disabledLineHeight: this.disabledLineHeight + }; + if (isDefinedAndNotNull(this.previewText)) { + const previewText = typeof this.previewText === 'string' ? this.previewText : this.previewText(); + if (previewText) { + ctx.previewText = previewText; + } + } + const fontSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, FontSettingsPanelComponent, 'left', true, null, + ctx, + {}, + {}, {}, true); + fontSettingsPanelPopover.tbComponentRef.instance.popover = fontSettingsPanelPopover; + fontSettingsPanelPopover.tbComponentRef.instance.fontApplied.subscribe((font) => { + fontSettingsPanelPopover.hide(); + this.modelValue = font; + this.propagateChange(this.modelValue); + }); + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/image-cards-select.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/image-cards-select.component.html new file mode 100644 index 0000000000..35d7157fb4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/image-cards-select.component.html @@ -0,0 +1,43 @@ + +
+ + +
+
{{ label }}
+ + + {{ expanded ? 'expand_less' : 'expand_more' }} + +
+
+ + + +
+
+
{{ option.name }}
+
+ +
+
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/image-cards-select.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/image-cards-select.component.scss new file mode 100644 index 0000000000..0cda7679e5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/image-cards-select.component.scss @@ -0,0 +1,121 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.tb-image-cards-select.tb-form-panel { + .tb-form-row { + transition: all .3s; + &.expanded { + padding: 11px 7px 11px 16px; + } + } + .tb-image-cards-value-field { + cursor: pointer; + user-select: none; + input { + cursor: pointer; + pointer-events: none; + } + } + .mat-expansion-panel { + &.tb-settings { + > .mat-expansion-panel-header { + height: auto; + .mat-content { + margin: 0; + } + .tb-form-row { + font-weight: normal; + font-size: 16px; + color: rgba(0, 0, 0, 0.87); + } + .mat-expansion-indicator { + display: none; + } + } + > .mat-expansion-panel-content { + > .mat-expansion-panel-body { + padding: 0 16px 16px !important; + } + } + } + } + .tb-image-cards-option { + width: 100%; + height: 100%; + cursor: pointer; + padding: 8px 12px 12px 12px; + display: flex; + flex-direction: column; + gap: 8px; + align-items: start; + position: relative; + .tb-image-cards-option-background { + border-radius: 4px; + position: absolute; + top: 1px; + left: 1px; + right: 1px; + bottom: 1px; + background: rgba(0, 0, 0, 0.04); + } + &:before { + content: unset; + border-radius: 4px; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + } + .tb-image-cards-option-title { + z-index: 1; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 16px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.54); + } + .tb-image-cards-option-image-container { + z-index: 1; + flex: 1; + width: 100%; + min-height: 0; + display: flex; + justify-content: center; + + .tb-image-cards-option-image { + max-width: 100%; + max-height: 100%; + } + } + &.selected { + .tb-image-cards-option-background { + background: #305680; + opacity: 0.04; + } + &:before { + content: ""; + border: 1px solid #305680; + opacity: 0.32; + } + .tb-image-cards-option-title { + font-size: 13px; + font-weight: 500; + color: #305680; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/image-cards-select.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/image-cards-select.component.ts new file mode 100644 index 0000000000..4e33000825 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/image-cards-select.component.ts @@ -0,0 +1,235 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + AfterContentInit, + Component, + ContentChildren, + Directive, + ElementRef, + forwardRef, + Input, + OnChanges, + OnDestroy, + OnInit, + QueryList, + SimpleChanges, + ViewEncapsulation +} from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { EMPTY, Observable, Subject } from 'rxjs'; +import { map, share, startWith, takeUntil } from 'rxjs/operators'; +import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout'; +import { SafeUrl } from '@angular/platform-browser'; +import { resolveBreakpoint } from '@shared/models/constants'; + +export interface ImageCardsSelectOption { + name: string; + value: any; + image: string | SafeUrl; +} + +export interface ImageCardsColumns { + columns: number; + breakpoints?: {[breakpoint: string]: number}; +} + +@Directive( + { + // eslint-disable-next-line @angular-eslint/directive-selector + selector: 'tb-image-cards-select-option', + } +) +export class ImageCardsSelectOptionDirective { + + @Input() value: any; + + @Input() image: string | SafeUrl; + + get viewValue(): string { + return (this._element?.nativeElement.textContent || '').trim(); + } + + constructor( + private _element: ElementRef + ) {} +} + +@Component({ + selector: 'tb-image-cards-select', + templateUrl: './image-cards-select.component.html', + styleUrls: ['./image-cards-select.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ImageCardsSelectComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class ImageCardsSelectComponent implements ControlValueAccessor, OnInit, OnChanges, AfterContentInit, OnDestroy { + + @ContentChildren(ImageCardsSelectOptionDirective) imageCardsSelectOptions: QueryList; + + @Input() + @coerceBoolean() + disabled: boolean; + + @Input() + cols: ImageCardsColumns | number = 4; + + @Input() + rowHeight = '9:5'; + + @Input() + label: string; + + valueFormControl: UntypedFormControl; + + options: ImageCardsSelectOption[] = []; + + modelValue: any; + + expanded = false; + + cols$: Observable; + + private propagateChange = null; + + private _destroyed = new Subject(); + + constructor(private breakpointObserver: BreakpointObserver) { + this.valueFormControl = new UntypedFormControl(''); + } + + ngOnInit(): void { + this._initCols(); + } + + ngOnChanges(changes: SimpleChanges): void { + for (const propName of Object.keys(changes)) { + const change = changes[propName]; + if (!change.firstChange && change.currentValue !== change.previousValue) { + if (['cols'].includes(propName)) { + this._initCols(); + } + } + } + } + + ngAfterContentInit(): void { + this.imageCardsSelectOptions.changes.pipe(startWith(null), takeUntil(this._destroyed)).subscribe(() => { + this.syncImageCardsSelectOptions(); + }); + } + + ngOnDestroy() { + this._destroyed.next(); + this._destroyed.complete(); + } + + private _initCols() { + const gridColumns = this._detectColumns(); + let state: Observable; + if (typeof this.cols === 'object' && this.cols.breakpoints) { + const breakpoints = Object.keys(this.cols.breakpoints); + state = this.breakpointObserver.observe(breakpoints.map(breakpoint => resolveBreakpoint(breakpoint))); + } else { + state = EMPTY; + } + this.cols$ = state.pipe( + map(() => this._detectColumns()), + startWith(gridColumns), + share() + ); + } + + private _detectColumns(): number { + if (typeof this.cols !== 'object') { + return this.cols; + } else { + let columns = this.cols.columns; + if (this.cols.breakpoints) { + for (const breakpoint of Object.keys(this.cols.breakpoints)) { + const breakpointValue = resolveBreakpoint(breakpoint); + if (this.breakpointObserver.isMatched(breakpointValue)) { + columns = this.cols.breakpoints[breakpoint]; + break; + } + } + } + return columns; + } + } + + private syncImageCardsSelectOptions() { + if (this.imageCardsSelectOptions?.length) { + this.options.length = 0; + this.imageCardsSelectOptions.forEach(option => { + this.options.push( + { name: option.viewValue, + value: option.value, + image: option.image + } + ); + }); + this.updateDisplayValue(); + } + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.valueFormControl.disable(); + } else { + this.valueFormControl.enable(); + } + } + + writeValue(value: any): void { + this.modelValue = value; + this.updateDisplayValue(); + } + + updateModel(value: any) { + this.modelValue = value; + this.updateDisplayValue(); + this.propagateChange(this.modelValue); + this.expanded = false; + } + + toggleSelectPanel($event: Event) { + $event.stopPropagation(); + if (!this.disabled) { + this.expanded = !this.expanded; + } + } + + private updateDisplayValue() { + const currentOption = this.options.find(o => o.value === this.modelValue); + const displayValue = currentOption ? currentOption.name : ''; + this.valueFormControl.patchValue(displayValue, {emitEvent: false}); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/legend-config.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/legend-config.component.html index f7e2234afd..349bc0cd04 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/legend-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/legend-config.component.html @@ -15,20 +15,21 @@ limitations under the License. --> -
-
- - legend.direction - + +
+
{{ 'legend.direction' | translate }}
+ + {{ legendDirectionTranslations.get(legendDirection[direction]) | translate }} - - legend.position - +
+
+
{{ 'legend.position' | translate }}
+ + @@ -37,28 +38,17 @@
-
- - {{ 'legend.show-min' | translate }} - - - {{ 'legend.show-max' | translate }} - -
-
- - {{ 'legend.show-avg' | translate }} - - - {{ 'legend.show-total' | translate }} - -
-
- - {{ 'legend.show-latest' | translate }} - - - {{ 'legend.sort-legend' | translate }} - +
+
legend.show-values
+ + {{ 'legend.min-option' | translate }} + {{ 'legend.max-option' | translate }} + {{ 'legend.average-option' | translate }} + {{ 'legend.total-option' | translate }} + {{ 'legend.latest-option' | translate }} +
- + + {{ 'legend.sort-legend' | translate }} + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/legend-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/legend-config.component.ts index c8de7872e8..15b2bbe316 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/legend-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/legend-config.component.ts @@ -15,7 +15,7 @@ /// import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; -import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { isDefined } from '@core/utils'; import { LegendConfig, @@ -30,7 +30,7 @@ import { Subscription } from 'rxjs'; @Component({ selector: 'tb-legend-config', templateUrl: './legend-config.component.html', - styleUrls: [], + styleUrls: ['./../widget-settings.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, @@ -62,12 +62,8 @@ export class LegendConfigComponent implements OnInit, OnDestroy, ControlValueAcc this.legendConfigForm = this.fb.group({ direction: [null, []], position: [null, []], - sortDataKeys: [null, []], - showMin: [null, []], - showMax: [null, []], - showAvg: [null, []], - showTotal: [null, []], - showLatest: [null, []] + showValues: [[], []], + sortDataKeys: [null, []] }); this.legendSettingsFormDirectionChanges$ = this.legendConfigForm.get('direction').valueChanges .subscribe((direction: LegendDirection) => { @@ -121,18 +117,49 @@ export class LegendConfigComponent implements OnInit, OnDestroy, ControlValueAcc this.legendConfigForm.patchValue({ direction: legendConfig.direction, position: legendConfig.position, - sortDataKeys: isDefined(legendConfig.sortDataKeys) ? legendConfig.sortDataKeys : false, - showMin: isDefined(legendConfig.showMin) ? legendConfig.showMin : false, - showMax: isDefined(legendConfig.showMax) ? legendConfig.showMax : false, - showAvg: isDefined(legendConfig.showAvg) ? legendConfig.showAvg : false, - showTotal: isDefined(legendConfig.showTotal) ? legendConfig.showTotal : false, - showLatest: isDefined(legendConfig.showLatest) ? legendConfig.showLatest : false + showValues: this.getShowValues(legendConfig), + sortDataKeys: isDefined(legendConfig.sortDataKeys) ? legendConfig.sortDataKeys : false }, {emitEvent: false}); } this.onDirectionChanged(legendConfig.direction); } private legendConfigUpdated() { - this.propagateChange(this.legendConfigForm.value); + const configValue = this.legendConfigForm.value; + const legendConfig: Partial = { + direction: configValue.direction, + position: configValue.position, + sortDataKeys: configValue.sortDataKeys + }; + this.setShowValues(configValue.showValues, legendConfig); + this.propagateChange(legendConfig); + } + + private getShowValues(legendConfig: LegendConfig): string[] { + const showValues: string[] = []; + if (isDefined(legendConfig.showMin) && legendConfig.showMin) { + showValues.push('min'); + } + if (isDefined(legendConfig.showMax) && legendConfig.showMax) { + showValues.push('max'); + } + if (isDefined(legendConfig.showAvg) && legendConfig.showAvg) { + showValues.push('average'); + } + if (isDefined(legendConfig.showTotal) && legendConfig.showTotal) { + showValues.push('total'); + } + if (isDefined(legendConfig.showLatest) && legendConfig.showLatest) { + showValues.push('latest'); + } + return showValues; + } + + private setShowValues(showValues: string[], legendConfig: Partial) { + legendConfig.showMin = showValues.includes('min'); + legendConfig.showMax = showValues.includes('max'); + legendConfig.showAvg = showValues.includes('average'); + legendConfig.showTotal = showValues.includes('total'); + legendConfig.showLatest = showValues.includes('latest'); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.html index 46e3f87664..726aed776e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.html @@ -15,27 +15,27 @@ limitations under the License. --> -
- - widgets.value-source.value-source - - + +
+ + {{ 'widgets.value-source.predefined-value' | translate }} - - + + {{ 'widgets.value-source.entity-attribute' | translate }} - - - - - widgets.value-source.value - - -
- - {{ 'widgets.value-source.source-entity-alias' | translate }} - + +
+
+
widgets.value-source.value
+ + + +
+
+
widgets.value-source.source-entity-alias
+ + @@ -53,9 +53,11 @@ - - {{ 'widgets.value-source.source-entity-attribute' | translate }} - +
+
widgets.value-source.source-entity-attribute
+ + @@ -73,5 +75,5 @@ -
- +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.ts index 6417ae4bcb..603b12bcb5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/value-source.component.ts @@ -14,8 +14,8 @@ /// limitations under the License. /// -import { Component, ElementRef, forwardRef, HostBinding, Input, OnInit, ViewChild } from '@angular/core'; -import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -39,7 +39,7 @@ export interface ValueSourceProperty { @Component({ selector: 'tb-value-source', templateUrl: './value-source.component.html', - styleUrls: [], + styleUrls: ['./../widget-settings.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, @@ -50,8 +50,6 @@ export interface ValueSourceProperty { }) export class ValueSourceComponent extends PageComponent implements OnInit, ControlValueAccessor { - @HostBinding('style.display') display = 'block'; - @ViewChild('entityAliasInput') entityAliasInput: ElementRef; @ViewChild('keyInput') keyInput: ElementRef; @@ -212,15 +210,13 @@ export class ValueSourceComponent extends PageComponent implements OnInit, Contr private fetchEntityKeys(entityAliasId: string, dataKeyTypes: Array): Observable> { return this.aliasController.getAliasInfo(entityAliasId).pipe( - mergeMap((aliasInfo) => { - return this.entityService.getEntityKeysByEntityFilter( + mergeMap((aliasInfo) => this.entityService.getEntityKeysByEntityFilter( aliasInfo.entityFilter, dataKeyTypes, [], {ignoreLoading: true, ignoreErrors: true} ).pipe( catchError(() => of([])) - ); - }), + )), catchError(() => of([] as Array)) ); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts new file mode 100644 index 0000000000..7c2022c5d4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts @@ -0,0 +1,107 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SharedModule } from '@shared/shared.module'; +import { SharedHomeComponentsModule } from '@home/components/shared-home-components.module'; +import { WidgetFontComponent } from '@home/components/widget/lib/settings/common/widget-font.component'; +import { ValueSourceComponent } from '@home/components/widget/lib/settings/common/value-source.component'; +import { LegendConfigComponent } from '@home/components/widget/lib/settings/common/legend-config.component'; +import { + ImageCardsSelectComponent, + ImageCardsSelectOptionDirective +} from '@home/components/widget/lib/settings/common/image-cards-select.component'; +import { FontSettingsComponent } from '@home/components/widget/lib/settings/common/font-settings.component'; +import { FontSettingsPanelComponent } from '@home/components/widget/lib/settings/common/font-settings-panel.component'; +import { + ColorSettingsComponent, + ColorSettingsComponentService +} from '@home/components/widget/lib/settings/common/color-settings.component'; +import { + ColorSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/color-settings-panel.component'; +import { CssUnitSelectComponent } from '@home/components/widget/lib/settings/common/css-unit-select.component'; +import { DateFormatSelectComponent } from '@home/components/widget/lib/settings/common/date-format-select.component'; +import { + DateFormatSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/date-format-settings-panel.component'; +import { BackgroundSettingsComponent } from '@home/components/widget/lib/settings/common/background-settings.component'; +import { + BackgroundSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/background-settings-panel.component'; +import { + CountWidgetSettingsComponent +} from '@home/components/widget/lib/settings/common/count-widget-settings.component'; +import { ColorRangeListComponent } from '@home/components/widget/lib/settings/common/color-range-list.component'; +import { ColorRangePanelComponent } from '@home/components/widget/lib/settings/common/color-range-panel.component'; +import { + ColorRangeSettingsComponent, ColorRangeSettingsComponentService +} from '@home/components/widget/lib/settings/common/color-range-settings.component'; + +@NgModule({ + declarations: [ + ImageCardsSelectOptionDirective, + ImageCardsSelectComponent, + FontSettingsComponent, + FontSettingsPanelComponent, + ColorSettingsComponent, + ColorSettingsPanelComponent, + CssUnitSelectComponent, + DateFormatSelectComponent, + DateFormatSettingsPanelComponent, + BackgroundSettingsComponent, + BackgroundSettingsPanelComponent, + ValueSourceComponent, + LegendConfigComponent, + WidgetFontComponent, + CountWidgetSettingsComponent, + ColorRangeListComponent, + ColorRangePanelComponent, + ColorRangeSettingsComponent + ], + imports: [ + CommonModule, + SharedModule, + SharedHomeComponentsModule + ], + exports: [ + ImageCardsSelectOptionDirective, + ImageCardsSelectComponent, + FontSettingsComponent, + FontSettingsPanelComponent, + ColorSettingsComponent, + ColorSettingsPanelComponent, + CssUnitSelectComponent, + DateFormatSelectComponent, + DateFormatSettingsPanelComponent, + BackgroundSettingsComponent, + BackgroundSettingsPanelComponent, + ValueSourceComponent, + LegendConfigComponent, + WidgetFontComponent, + CountWidgetSettingsComponent, + ColorRangeListComponent, + ColorRangePanelComponent, + ColorRangeSettingsComponent + ], + providers: [ + ColorSettingsComponentService, + ColorRangeSettingsComponentService + ] +}) +export class WidgetSettingsCommonModule { +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/persistent-table-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/persistent-table-widget-settings.component.ts index b275e35973..8989794e86 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/persistent-table-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/control/persistent-table-widget-settings.component.ts @@ -115,7 +115,7 @@ export class PersistentTableWidgetSettingsComponent extends WidgetSettingsCompon }); } - protected validateSettings(): boolean { + public validateSettings(): boolean { const displayColumns: string[] = this.persistentTableWidgetSettingsForm.get('displayColumns').value; this.columnsChipList.errorState = !displayColumns?.length; return super.validateSettings(); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-hierarchy-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-hierarchy-widget-settings.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-hierarchy-widget-settings.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-hierarchy-widget-settings.component.html diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-hierarchy-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-hierarchy-widget-settings.component.ts similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-hierarchy-widget-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-hierarchy-widget-settings.component.ts diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-key-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-key-settings.component.html new file mode 100644 index 0000000000..faad357026 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-key-settings.component.html @@ -0,0 +1,111 @@ + + +
+
widgets.table.column-settings
+
+
{{ 'widgets.table.custom-title' | translate }}
+ + + +
+
+
{{ 'widgets.table.column-width' | translate }}
+ + + +
+
+
{{ 'widgets.table.default-column-visibility' | translate }}
+ + + + {{ 'widgets.table.column-visibility-visible' | translate }} + + + {{ 'widgets.table.column-visibility-hidden' | translate }} + + + {{ 'widgets.table.column-visibility-hidden-mobile' | translate }} + + + +
+
+
{{ 'widgets.table.column-selection-to-display' | translate }}
+ + + + {{ 'widgets.table.column-selection-to-display-enabled' | translate }} + + + {{ 'widgets.table.column-selection-to-display-disabled' | translate }} + + + +
+
+
+ + + + + {{ 'widgets.table.use-cell-style-function' | translate }} + + + + widget-config.advanced-settings + + + + + + + +
+
+ + + + + {{ 'widgets.table.use-cell-content-function' | translate }} + + + + widget-config.advanced-settings + + + + + + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-table-key-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-key-settings.component.ts similarity index 100% rename from ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/entities-table-key-settings.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-key-settings.component.ts diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-widget-settings.component.html new file mode 100644 index 0000000000..f7afe557ff --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-widget-settings.component.html @@ -0,0 +1,126 @@ + + +
+
widgets.table.table-header
+
+
{{ 'widgets.table.entities-table-title' | translate }}
+ + + +
+
+ + {{ 'widgets.table.enable-sticky-header' | translate }} + +
+
+
widgets.table.header-buttons
+ + {{ 'widgets.table.enable-search' | translate }} + + + {{ 'widgets.table.enable-select-column-display' | translate }} + +
+
+
+
widgets.table.columns
+
+ + {{ 'widgets.table.display-entity-name' | translate }} + + + + +
+
+ + {{ 'widgets.table.display-entity-label' | translate }} + + + + +
+
+ + {{ 'widgets.table.display-entity-type' | translate }} + +
+
+ + {{ 'widgets.table.enable-sticky-action' | translate }} + + + {{ 'widgets.table.show-cell-actions-menu-mobile' | translate }} + +
+ + widgets.table.hidden-cell-button-display-mode + + + {{ 'widgets.table.show-empty-space-hidden-action' | translate }} + + + {{ 'widgets.table.dont-reserve-space-hidden-action' | translate }} + + + + + widgets.table.default-sort-order + + +
+
+
widgets.table.pagination
+ + {{ 'widgets.table.display-pagination' | translate }} + +
+
widgets.table.default-page-size
+ + + +
+
+
+
widgets.table.rows
+ + + + + {{ 'widgets.table.use-row-style-function' | translate }} + + + + widget-config.advanced-settings + + + + + + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-widget-settings.component.ts new file mode 100644 index 0000000000..402d6bba4f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entities-table-widget-settings.component.ts @@ -0,0 +1,120 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; + +@Component({ + selector: 'tb-entities-table-widget-settings', + templateUrl: './entities-table-widget-settings.component.html', + styleUrls: ['./../widget-settings.scss'] +}) +export class EntitiesTableWidgetSettingsComponent extends WidgetSettingsComponent { + + entitiesTableWidgetSettingsForm: UntypedFormGroup; + + constructor(protected store: Store, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.entitiesTableWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return { + entitiesTitle: '', + enableSearch: true, + enableSelectColumnDisplay: true, + enableStickyHeader: true, + enableStickyAction: true, + showCellActionsMenu: true, + reserveSpaceForHiddenAction: 'true', + displayEntityName: true, + entityNameColumnTitle: '', + displayEntityLabel: false, + entityLabelColumnTitle: '', + displayEntityType: true, + displayPagination: true, + defaultPageSize: 10, + defaultSortOrder: 'entityName', + useRowStyleFunction: false, + rowStyleFunction: '' + }; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.entitiesTableWidgetSettingsForm = this.fb.group({ + entitiesTitle: [settings.entitiesTitle, []], + enableSearch: [settings.enableSearch, []], + enableSelectColumnDisplay: [settings.enableSelectColumnDisplay, []], + enableStickyHeader: [settings.enableStickyHeader, []], + enableStickyAction: [settings.enableStickyAction, []], + showCellActionsMenu: [settings.showCellActionsMenu, []], + reserveSpaceForHiddenAction: [settings.reserveSpaceForHiddenAction, []], + displayEntityName: [settings.displayEntityName, []], + entityNameColumnTitle: [settings.entityNameColumnTitle, []], + displayEntityLabel: [settings.displayEntityLabel, []], + entityLabelColumnTitle: [settings.entityLabelColumnTitle, []], + displayEntityType: [settings.displayEntityType, []], + displayPagination: [settings.displayPagination, []], + defaultPageSize: [settings.defaultPageSize, [Validators.min(1)]], + defaultSortOrder: [settings.defaultSortOrder, []], + useRowStyleFunction: [settings.useRowStyleFunction, []], + rowStyleFunction: [settings.rowStyleFunction, [Validators.required]] + }); + } + + protected validatorTriggers(): string[] { + return ['useRowStyleFunction', 'displayPagination', 'displayEntityName', 'displayEntityLabel']; + } + + protected updateValidators(emitEvent: boolean) { + const useRowStyleFunction: boolean = this.entitiesTableWidgetSettingsForm.get('useRowStyleFunction').value; + const displayPagination: boolean = this.entitiesTableWidgetSettingsForm.get('displayPagination').value; + const displayEntityName: boolean = this.entitiesTableWidgetSettingsForm.get('displayEntityName').value; + const displayEntityLabel: boolean = this.entitiesTableWidgetSettingsForm.get('displayEntityLabel').value; + if (useRowStyleFunction) { + this.entitiesTableWidgetSettingsForm.get('rowStyleFunction').enable(); + } else { + this.entitiesTableWidgetSettingsForm.get('rowStyleFunction').disable(); + } + if (displayPagination) { + this.entitiesTableWidgetSettingsForm.get('defaultPageSize').enable(); + } else { + this.entitiesTableWidgetSettingsForm.get('defaultPageSize').disable(); + } + if (displayEntityName) { + this.entitiesTableWidgetSettingsForm.get('entityNameColumnTitle').enable(); + } else { + this.entitiesTableWidgetSettingsForm.get('entityNameColumnTitle').disable(); + } + if (displayEntityLabel) { + this.entitiesTableWidgetSettingsForm.get('entityLabelColumnTitle').enable(); + } else { + this.entitiesTableWidgetSettingsForm.get('entityLabelColumnTitle').disable(); + } + this.entitiesTableWidgetSettingsForm.get('rowStyleFunction').updateValueAndValidity({emitEvent}); + this.entitiesTableWidgetSettingsForm.get('defaultPageSize').updateValueAndValidity({emitEvent}); + this.entitiesTableWidgetSettingsForm.get('entityNameColumnTitle').updateValueAndValidity({emitEvent}); + this.entitiesTableWidgetSettingsForm.get('entityLabelColumnTitle').updateValueAndValidity({emitEvent}); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entity-count-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entity-count-widget-settings.component.html new file mode 100644 index 0000000000..e38fab6e27 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entity-count-widget-settings.component.html @@ -0,0 +1,23 @@ + + +
+
widgets.entity-count.entity-count-card-style
+ +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entity-count-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entity-count-widget-settings.component.ts new file mode 100644 index 0000000000..348c893316 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/entity/entity-count-widget-settings.component.ts @@ -0,0 +1,62 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { countDefaultSettings } from '@home/components/widget/lib/count/count-widget.models'; + +@Component({ + selector: 'tb-entity-count-widget-settings', + templateUrl: './entity-count-widget-settings.component.html', + styleUrls: ['./../widget-settings.scss'] +}) +export class EntityCountWidgetSettingsComponent extends WidgetSettingsComponent { + + entityCountWidgetSettingsForm: UntypedFormGroup; + + constructor(protected store: Store, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.entityCountWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return {...countDefaultSettings(false)}; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.entityCountWidgetSettingsForm = this.fb.group({ + entityCountSettings: [settings.entityCountSettings, []], + }); + } + + protected prepareInputSettings(settings: WidgetSettings): WidgetSettings { + return { + entityCountSettings: settings + }; + } + + protected prepareOutputSettings(settings: any): WidgetSettings { + return settings.entityCountSettings; + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-logs-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-logs-settings.component.html new file mode 100644 index 0000000000..ae91ca6c08 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-logs-settings.component.html @@ -0,0 +1,27 @@ + +
+
+ + {{ "widgets.gateway.show-connector" | translate }} + + + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-logs-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-logs-settings.component.ts new file mode 100644 index 0000000000..c543ee99fb --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-logs-settings.component.ts @@ -0,0 +1,67 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; + +@Component({ + selector: 'tb-gateway-logs-settings', + templateUrl: './gateway-logs-settings.component.html', + styleUrls: ['../widget-settings.scss'] +}) +export class GatewayLogsSettingsComponent extends WidgetSettingsComponent { + + gatewayLogSettingForm: FormGroup; + + constructor(protected store: Store, + private fb: FormBuilder) { + super(store); + } + + protected settingsForm(): FormGroup { + return this.gatewayLogSettingForm; + } + + protected defaultSettings(): WidgetSettings { + return { + isConnectorLog: false, + connectorLogState: '' + }; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.gatewayLogSettingForm = this.fb.group({ + isConnectorLog: [false, []], + connectorLogState: ['', Validators.required] + }); + } + + protected validatorTriggers(): string[] { + return ['isConnectorLog']; + } + + protected updateValidators(emitEvent: boolean) { + const isConnectorLog: boolean = this.gatewayLogSettingForm.get('isConnectorLog').value; + if (isConnectorLog) { + this.gatewayLogSettingForm.get('connectorLogState').enable({emitEvent}); + } else { + this.gatewayLogSettingForm.get('connectorLogState').disable({emitEvent}); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-service-rpc-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-service-rpc-settings.component.html new file mode 100644 index 0000000000..2c7c0b5dd5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-service-rpc-settings.component.html @@ -0,0 +1,24 @@ + +
+
+ + {{ "widgets.gateway.show-connector" | translate }} + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-service-rpc-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-service-rpc-settings.component.ts new file mode 100644 index 0000000000..5b0402236b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-service-rpc-settings.component.ts @@ -0,0 +1,52 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { FormBuilder, FormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; + +@Component({ + selector: 'tb-gateway-service-rpc-settings', + templateUrl: './gateway-service-rpc-settings.component.html', + styleUrls: ['../widget-settings.scss'] +}) +export class GatewayServiceRPCSettingsComponent extends WidgetSettingsComponent { + + gatewayServiceRPCSettingForm: FormGroup; + + constructor(protected store: Store, + private fb: FormBuilder) { + super(store); + } + + protected settingsForm(): FormGroup { + return this.gatewayServiceRPCSettingForm; + } + + protected defaultSettings(): WidgetSettings { + return { + isConnector: false, + }; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.gatewayServiceRPCSettingForm = this.fb.group({ + isConnector: [false, []] + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/analogue-compass-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/analogue-compass-widget-settings.component.html index bbb7874523..0f3b318664 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/analogue-compass-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/analogue-compass-widget-settings.component.html @@ -15,153 +15,127 @@ limitations under the License. --> -
-
- widgets.gauge.ticks-settings - - widgets.gauge.major-ticks-names - - - {{tickName}} - cancel - - - - - - -
- - widgets.gauge.minor-ticks-count - - - - -
- - {{ 'widgets.gauge.show-stroke-ticks' | translate }} - -
- widgets.gauge.major-ticks-font - -
-
-
- widgets.gauge.plate-settings - - - - {{ 'widgets.gauge.show-plate-border' | translate }} - -
- + +
+
widgets.gauge.ticks-settings
+
+
widgets.gauge.labels
+
+ + +
+
+
+
widgets.gauge.label-style
+
+ + + + +
+
+ +
+
widgets.gauge.major-ticks
+ - - widgets.gauge.border-width - - -
-
-
- widgets.gauge.needle-settings - - widgets.gauge.needle-circle-size - - -
- +
+
+
widgets.gauge.minor-ticks
+
+ + + + + +
+
+ +
+ + {{ 'widgets.gauge.show-stroke-ticks' | translate }} + +
+ + +
+
widgets.gauge.plate-settings
+
+
widgets.gauge.plate-color
+ - +
+
+ + {{ 'widgets.gauge.show-plate-border' | translate }} + +
+ + + + + +
+
+
+ +
+
widgets.gauge.needle-settings
+
+
widgets.gauge.needle-circle
+
+ + + + + +
+
+
+
widgets.gauge.needle-color
+ - - -
- widgets.gauge.animation-settings +
+
+ +
- {{ 'widgets.gauge.enable-animation' | translate }} - + widget-config.advanced-settings -
- - widgets.gauge.animation-duration - - - - widgets.gauge.animation-rule - - - {{ 'widgets.gauge.animation-linear' | translate }} - - - {{ 'widgets.gauge.animation-quad' | translate }} - - - {{ 'widgets.gauge.animation-quint' | translate }} - - - {{ 'widgets.gauge.animation-cycle' | translate }} - - - {{ 'widgets.gauge.animation-bounce' | translate }} - - - {{ 'widgets.gauge.animation-elastic' | translate }} - - - {{ 'widgets.gauge.animation-dequad' | translate }} - - - {{ 'widgets.gauge.animation-dequint' | translate }} - - - {{ 'widgets.gauge.animation-decycle' | translate }} - - - {{ 'widgets.gauge.animation-debounce' | translate }} - - - {{ 'widgets.gauge.animation-delastic' | translate }} - - - - - widgets.gauge.animation-target +
+
widgets.gauge.animation-target
+ {{ 'widgets.gauge.animation-target-needle' | translate }} @@ -171,8 +145,53 @@ -
+
+
+
widgets.gauge.animation-duration-rule
+
+ + + + + + + {{ 'widgets.gauge.animation-linear' | translate }} + + + {{ 'widgets.gauge.animation-quad' | translate }} + + + {{ 'widgets.gauge.animation-quint' | translate }} + + + {{ 'widgets.gauge.animation-cycle' | translate }} + + + {{ 'widgets.gauge.animation-bounce' | translate }} + + + {{ 'widgets.gauge.animation-elastic' | translate }} + + + {{ 'widgets.gauge.animation-dequad' | translate }} + + + {{ 'widgets.gauge.animation-dequint' | translate }} + + + {{ 'widgets.gauge.animation-decycle' | translate }} + + + {{ 'widgets.gauge.animation-debounce' | translate }} + + + {{ 'widgets.gauge.animation-delastic' | translate }} + + + +
+
- - + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/analogue-compass-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/analogue-compass-widget-settings.component.ts index 5a2a2b2678..6ae485c49b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/analogue-compass-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/analogue-compass-widget-settings.component.ts @@ -19,9 +19,7 @@ import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { Component } from '@angular/core'; -import { MatChipInputEvent } from '@angular/material/chips'; import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; -import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; @Component({ selector: 'tb-analogue-compass-widget-settings', @@ -81,6 +79,7 @@ export class AnalogueCompassWidgetSettingsComponent extends WidgetSettingsCompon colorMinorTicks: [settings.colorMinorTicks, []], showStrokeTicks: [settings.showStrokeTicks, []], majorTickFont: [settings.majorTickFont, []], + majorTickColor: [settings.majorTickFont.color, []], // Plate settings colorPlate: [settings.colorPlate, []], @@ -131,41 +130,8 @@ export class AnalogueCompassWidgetSettingsComponent extends WidgetSettingsCompon this.analogueCompassWidgetSettingsForm.get('animationTarget').updateValueAndValidity({emitEvent}); } - majorTicksNamesList(): string[] { - return this.analogueCompassWidgetSettingsForm.get('majorTicks').value; - } - - removeMajorTickName(tickName: string): void { - const tickNames: string[] = this.analogueCompassWidgetSettingsForm.get('majorTicks').value; - const index = tickNames.indexOf(tickName); - if (index >= 0) { - tickNames.splice(index, 1); - this.analogueCompassWidgetSettingsForm.get('majorTicks').setValue(tickNames); - this.analogueCompassWidgetSettingsForm.get('majorTicks').markAsDirty(); - } - } - - addMajorTickName(event: MatChipInputEvent): void { - const input = event.chipInput.inputElement; - const value = event.value; - - const tickNames: string[] = this.analogueCompassWidgetSettingsForm.get('majorTicks').value; - - if ((value || '').trim()) { - tickNames.push(value.trim()); - this.analogueCompassWidgetSettingsForm.get('majorTicks').setValue(tickNames); - this.analogueCompassWidgetSettingsForm.get('majorTicks').markAsDirty(); - } - - if (input) { - input.value = ''; - } - } - - majorTickNameDrop(event: CdkDragDrop): void { - const tickNames: string[] = this.analogueCompassWidgetSettingsForm.get('majorTicks').value; - moveItemInArray(tickNames, event.previousIndex, event.currentIndex); - this.analogueCompassWidgetSettingsForm.get('majorTicks').setValue(tickNames); - this.analogueCompassWidgetSettingsForm.get('majorTicks').markAsDirty(); + protected prepareOutputSettings(settings) { + settings.majorTickFont.color = this.analogueCompassWidgetSettingsForm.get('majorTickColor').value; + return settings; } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/analogue-gauge-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/analogue-gauge-widget-settings.component.html index c2264d7aca..7769dab6a3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/analogue-gauge-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/analogue-gauge-widget-settings.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+ @@ -24,330 +24,382 @@ - - -
- widgets.gauge.ticks-settings -
- - widgets.gauge.min-value - - - - widgets.gauge.max-value - - -
-
- - widgets.gauge.major-ticks-count - - - - -
-
- - widgets.gauge.minor-ticks-count - - - - -
-
- widgets.gauge.tick-numbers-font - -
-
-
- widgets.gauge.unit-title-settings - - - - - {{ 'widgets.gauge.show-unit-title' | translate }} - - - - widget-config.advanced-settings - - - -
- - widgets.gauge.unit-title - - -
- widgets.gauge.title-font - -
-
-
-
-
-
- widgets.gauge.units-settings -
- widgets.gauge.units-font - -
-
-
- widgets.gauge.value-box-settings + +
+
widgets.gauge.ticks-settings
+
+
widgets.gauge.ticks
+
+
widgets.gauge.min-value-short
+ + + +
widgets.gauge.max-value-short
+ + + + + + + +
+
+ +
+
widgets.gauge.major-ticks-count
+
+ + + + + +
+
+ +
+
widgets.gauge.minor-ticks-count
+
+ + + + + +
+
+
+ +
+
widgets.gauge.units-settings
+
+ + {{ 'widgets.gauge.units-title' | translate }} + +
+ + + + + + + +
+
+ +
+
widget-config.units-short
+
+ + + + +
+
+
+ +
- - {{ 'widgets.gauge.show-value-box' | translate }} + {{ 'widgets.gauge.value-box' | translate }} - + widget-config.advanced-settings -
- - widgets.gauge.value-int - +
+
widgets.gauge.value-int
+ + -
- widgets.gauge.value-font - -
-
- +
+
+
widgets.gauge.value-text
+
+
widgets.gauge.value-text
+
+ + + + +
+
+
+
widgets.gauge.value-text-shadow
+ - +
+
+
+
widgets.gauge.value-box
+
+
widgets.gauge.rect-stroke-color-start
+ + +
+ +
+
widgets.gauge.rect-stroke-color-end
+ -
-
- +
+ +
+
widgets.gauge.background-color
+ - +
+ +
+
widgets.gauge.shadow-color
+ -
- + + - -
- widgets.gauge.plate-settings - - - - {{ 'widgets.gauge.show-plate-border' | translate }} - -
-
- widgets.gauge.needle-settings -
- + + +
+
widgets.gauge.plate-settings
+
+
widgets.gauge.plate-color
+ - +
+
+ + {{ 'widgets.gauge.show-plate-border' | translate }} + +
+
+ +
+
widgets.gauge.needle-settings
+
+
widgets.gauge.needle-color-start
+ -
-
- + + +
+
widgets.gauge.needle-color-end
+ - +
+ +
+
widgets.gauge.needle-color-shadow-up
+ -
-
-
- widgets.gauge.highlights-settings - - widgets.gauge.highlights-width - - -
- widgets.gauge.highlights -
-
-
- - -
-
-
- widgets.gauge.no-highlights -
-
- -
+
+ +
+
widgets.gauge.needle-color-shadow-down
+ + +
+ + +
+
widgets.gauge.scale-settings
+
+
widgets.gauge.scale
+
+ + + + +
-
-
-
- widgets.gauge.animation-settings + + + +
- {{ 'widgets.gauge.enable-animation' | translate }} - + widget-config.advanced-settings -
- - widgets.gauge.animation-duration - - - - widgets.gauge.animation-rule - - - {{ 'widgets.gauge.animation-linear' | translate }} - - - {{ 'widgets.gauge.animation-quad' | translate }} - - - {{ 'widgets.gauge.animation-quint' | translate }} - - - {{ 'widgets.gauge.animation-cycle' | translate }} - - - {{ 'widgets.gauge.animation-bounce' | translate }} - - - {{ 'widgets.gauge.animation-elastic' | translate }} - - - {{ 'widgets.gauge.animation-dequad' | translate }} - - - {{ 'widgets.gauge.animation-dequint' | translate }} - - - {{ 'widgets.gauge.animation-decycle' | translate }} - - - {{ 'widgets.gauge.animation-debounce' | translate }} - - - {{ 'widgets.gauge.animation-delastic' | translate }} - - - -
+
+
widgets.gauge.animation-duration-rule
+
+ + + + + + + {{ 'widgets.gauge.animation-linear' | translate }} + + + {{ 'widgets.gauge.animation-quad' | translate }} + + + {{ 'widgets.gauge.animation-quint' | translate }} + + + {{ 'widgets.gauge.animation-cycle' | translate }} + + + {{ 'widgets.gauge.animation-bounce' | translate }} + + + {{ 'widgets.gauge.animation-elastic' | translate }} + + + {{ 'widgets.gauge.animation-dequad' | translate }} + + + {{ 'widgets.gauge.animation-dequint' | translate }} + + + {{ 'widgets.gauge.animation-decycle' | translate }} + + + {{ 'widgets.gauge.animation-debounce' | translate }} + + + {{ 'widgets.gauge.animation-delastic' | translate }} + + + +
+
-
- + + -
- widgets.gauge.radial-gauge-settings -
- - widgets.gauge.start-ticks-angle - +
+
widgets.gauge.radial-gauge-settings
+
+
widgets.gauge.start-ticks-angle
+ + + +
+
+
widgets.gauge.ticks-angle
+ + - - widgets.gauge.ticks-angle - +
+
+
widgets.gauge.needle-circle-size
+ + -
- - widgets.gauge.needle-circle-size - - -
+ + + +
-
- widgets.gauge.linear-gauge-settings -
- - widgets.gauge.bar-stroke-width - - - - -
-
- +
+
widgets.gauge.linear-gauge-settings
+
+
widgets.gauge.bar-stroke
+
+ + + + + +
+
+ +
+
widgets.gauge.bar-background-color
+ - +
+
+
widgets.gauge.bar-background-color-end
+ -
-
- + + +
+
widgets.gauge.progress-bar-color-start
+ - +
+
+
widgets.gauge.progress-bar-color-end
+ -
-
+ + + + +
+ + +
+
{{ 'widgets.gauge.arrow-and-scale-color' | translate }}
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/analogue-gauge-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/analogue-gauge-widget-settings.component.ts index 07f2be69bd..48c0cca8f8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/analogue-gauge-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/analogue-gauge-widget-settings.component.ts @@ -15,11 +15,9 @@ /// import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; -import { AbstractControl, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { GaugeHighlight } from '@home/components/widget/lib/settings/gauge/gauge-highlight.component'; -import { CdkDragDrop } from '@angular/cdk/drag-drop'; export class AnalogueGaugeWidgetSettingsComponent extends WidgetSettingsComponent { @@ -116,22 +114,27 @@ export class AnalogueGaugeWidgetSettingsComponent extends WidgetSettingsComponen maxValue: [settings.maxValue, []], majorTicksCount: [settings.majorTicksCount, [Validators.min(0)]], colorMajorTicks: [settings.colorMajorTicks, []], - minorTicks: [settings.majorTicksCount, [Validators.min(0)]], + minorTicks: [settings.minorTicks, [Validators.min(0)]], colorMinorTicks: [settings.colorMinorTicks, []], numbersFont: [settings.numbersFont, []], + numbersColor: [settings.numbersFont.color, []], // Unit title settings showUnitTitle: [settings.showUnitTitle, []], unitTitle: [settings.unitTitle, []], titleFont: [settings.titleFont, []], + titleColor: [settings.titleFont.color, []], // Units settings unitsFont: [settings.unitsFont, []], + unitsColor: [settings.unitsFont.color, []], // Value box settings valueBox: [settings.valueBox, []], valueInt: [settings.valueInt, [Validators.min(0)]], valueFont: [settings.valueFont, []], + valueColor: [settings.valueFont.color, []], + valueColorShadow: [settings.valueFont.shadowColor, []], colorValueBoxRect: [settings.colorValueBoxRect, []], colorValueBoxRectEnd: [settings.colorValueBoxRectEnd, []], colorValueBoxBackground: [settings.colorValueBoxBackground, []], @@ -149,7 +152,7 @@ export class AnalogueGaugeWidgetSettingsComponent extends WidgetSettingsComponen // Highlights settings highlightsWidth: [settings.highlightsWidth, [Validators.min(0)]], - highlights: this.prepareHighlightsFormArray(settings.highlights), + highlights: [settings.highlights, []], // Animation settings animation: [settings.animation, []], @@ -170,13 +173,16 @@ export class AnalogueGaugeWidgetSettingsComponent extends WidgetSettingsComponen if (showUnitTitle) { this.analogueGaugeWidgetSettingsForm.get('unitTitle').enable(); this.analogueGaugeWidgetSettingsForm.get('titleFont').enable(); + this.analogueGaugeWidgetSettingsForm.get('titleColor').enable(); } else { this.analogueGaugeWidgetSettingsForm.get('unitTitle').disable(); this.analogueGaugeWidgetSettingsForm.get('titleFont').disable(); + this.analogueGaugeWidgetSettingsForm.get('titleColor').disable(); } if (valueBox) { this.analogueGaugeWidgetSettingsForm.get('valueInt').enable(); this.analogueGaugeWidgetSettingsForm.get('valueFont').enable(); + this.analogueGaugeWidgetSettingsForm.get('valueColor').enable(); this.analogueGaugeWidgetSettingsForm.get('colorValueBoxRect').enable(); this.analogueGaugeWidgetSettingsForm.get('colorValueBoxRectEnd').enable(); this.analogueGaugeWidgetSettingsForm.get('colorValueBoxBackground').enable(); @@ -184,6 +190,7 @@ export class AnalogueGaugeWidgetSettingsComponent extends WidgetSettingsComponen } else { this.analogueGaugeWidgetSettingsForm.get('valueInt').disable(); this.analogueGaugeWidgetSettingsForm.get('valueFont').disable(); + this.analogueGaugeWidgetSettingsForm.get('valueColor').disable(); this.analogueGaugeWidgetSettingsForm.get('colorValueBoxRect').disable(); this.analogueGaugeWidgetSettingsForm.get('colorValueBoxRectEnd').disable(); this.analogueGaugeWidgetSettingsForm.get('colorValueBoxBackground').disable(); @@ -198,8 +205,10 @@ export class AnalogueGaugeWidgetSettingsComponent extends WidgetSettingsComponen } this.analogueGaugeWidgetSettingsForm.get('unitTitle').updateValueAndValidity({emitEvent}); this.analogueGaugeWidgetSettingsForm.get('titleFont').updateValueAndValidity({emitEvent}); + this.analogueGaugeWidgetSettingsForm.get('titleColor').updateValueAndValidity({emitEvent}); this.analogueGaugeWidgetSettingsForm.get('valueInt').updateValueAndValidity({emitEvent}); this.analogueGaugeWidgetSettingsForm.get('valueFont').updateValueAndValidity({emitEvent}); + this.analogueGaugeWidgetSettingsForm.get('valueColor').updateValueAndValidity({emitEvent}); this.analogueGaugeWidgetSettingsForm.get('colorValueBoxRect').updateValueAndValidity({emitEvent}); this.analogueGaugeWidgetSettingsForm.get('colorValueBoxRectEnd').updateValueAndValidity({emitEvent}); this.analogueGaugeWidgetSettingsForm.get('colorValueBoxBackground').updateValueAndValidity({emitEvent}); @@ -208,50 +217,17 @@ export class AnalogueGaugeWidgetSettingsComponent extends WidgetSettingsComponen this.analogueGaugeWidgetSettingsForm.get('animationRule').updateValueAndValidity({emitEvent}); } - protected doUpdateSettings(settingsForm: UntypedFormGroup, settings: WidgetSettings) { - settingsForm.setControl('highlights', this.prepareHighlightsFormArray(settings.highlights), {emitEvent: false}); - } - - private prepareHighlightsFormArray(highlights: GaugeHighlight[] | undefined): UntypedFormArray { - const highlightsControls: Array = []; - if (highlights) { - highlights.forEach((highlight) => { - highlightsControls.push(this.fb.control(highlight, [Validators.required])); - }); + protected prepareOutputSettings(settings) { + settings.numbersFont.color = this.analogueGaugeWidgetSettingsForm.get('numbersColor').value; + if (settings.titleFont) { + settings.titleFont.color = this.analogueGaugeWidgetSettingsForm.get('titleColor').value; } - return this.fb.array(highlightsControls); - } - - highlightsFormArray(): UntypedFormArray { - return this.analogueGaugeWidgetSettingsForm.get('highlights') as UntypedFormArray; - } - - public trackByHighlightControl(index: number, highlightControl: AbstractControl): any { - return highlightControl; - } - - public removeHighlight(index: number) { - (this.analogueGaugeWidgetSettingsForm.get('highlights') as UntypedFormArray).removeAt(index); - } - - public addHighlight() { - const highlight: GaugeHighlight = { - from: null, - to: null, - color: null - }; - const highlightsArray = this.analogueGaugeWidgetSettingsForm.get('highlights') as UntypedFormArray; - const highlightControl = this.fb.control(highlight, [Validators.required]); - (highlightControl as any).new = true; - highlightsArray.push(highlightControl); - this.analogueGaugeWidgetSettingsForm.updateValueAndValidity(); - } - - highlightDrop(event: CdkDragDrop) { - const highlightsArray = this.analogueGaugeWidgetSettingsForm.get('highlights') as UntypedFormArray; - const highlight = highlightsArray.at(event.previousIndex); - highlightsArray.removeAt(event.previousIndex); - highlightsArray.insert(event.currentIndex, highlight); + settings.unitsFont.color = this.analogueGaugeWidgetSettingsForm.get('unitsColor').value; + if (settings.valueFont) { + settings.valueFont.color = this.analogueGaugeWidgetSettingsForm.get('valueColor').value; + settings.valueFont.shadowColor = this.analogueGaugeWidgetSettingsForm.get('valueColorShadow').value; + } + return settings; } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/fixed-color-level.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/fixed-color-level.component.ts index 9baad9677c..8bd92dca8a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/fixed-color-level.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/fixed-color-level.component.ts @@ -50,7 +50,7 @@ export function fixedColorLevelValidator(control: AbstractControl): ValidationEr @Component({ selector: 'tb-fixed-color-level', templateUrl: './fixed-color-level.component.html', - styleUrls: ['./fixed-color-level.component.scss', './../widget-settings.scss'], + styleUrls: ['./fixed-color-level.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/gauge-highlight.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/gauge-highlight.component.html deleted file mode 100644 index f92edfcde5..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/gauge-highlight.component.html +++ /dev/null @@ -1,61 +0,0 @@ - - - -
- -
-
{{ highlightRangeText() }}
-
-
-
-
-
- - -
-
- -
- -
-
- - widgets.gauge.highlight-from - - - - widgets.gauge.highlight-to - - -
- - -
-
-
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/gauge-highlight.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/gauge-highlight.component.scss deleted file mode 100644 index 8b61fb7e3d..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/gauge-highlight.component.scss +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -:host { - display: block; - .mat-expansion-panel { - box-shadow: none; - &.gauge-highlight { - border: 1px groove rgba(0, 0, 0, .25); - .mat-expansion-panel-header { - padding: 0 24px 0 8px; - &.mat-expanded { - height: 48px; - } - } - } - } -} - -:host ::ng-deep { - .mat-expansion-panel { - &.gauge-highlight { - .mat-expansion-panel-body { - padding: 0 8px 8px; - } - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/gauge-highlight.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/gauge-highlight.component.ts deleted file mode 100644 index 33f44f0111..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/gauge-highlight.component.ts +++ /dev/null @@ -1,116 +0,0 @@ -/// -/// Copyright © 2016-2023 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; -import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; -import { PageComponent } from '@shared/components/page.component'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { TranslateService } from '@ngx-translate/core'; -import { isNumber } from '@core/utils'; - -export interface GaugeHighlight { - from: number; - to: number; - color: string; -} - -@Component({ - selector: 'tb-gauge-highlight', - templateUrl: './gauge-highlight.component.html', - styleUrls: ['./gauge-highlight.component.scss', './../widget-settings.scss'], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => GaugeHighlightComponent), - multi: true - } - ] -}) -export class GaugeHighlightComponent extends PageComponent implements OnInit, ControlValueAccessor { - - @Input() - disabled: boolean; - - @Input() - expanded = false; - - @Output() - removeHighlight = new EventEmitter(); - - private modelValue: GaugeHighlight; - - private propagateChange = null; - - public gaugeHighlightFormGroup: UntypedFormGroup; - - constructor(protected store: Store, - private translate: TranslateService, - private fb: UntypedFormBuilder) { - super(store); - } - - ngOnInit(): void { - this.gaugeHighlightFormGroup = this.fb.group({ - from: [null, []], - to: [null, []], - color: [null, []] - }); - this.gaugeHighlightFormGroup.valueChanges.subscribe(() => { - this.updateModel(); - }); - } - - registerOnChange(fn: any): void { - this.propagateChange = fn; - } - - registerOnTouched(fn: any): void { - } - - setDisabledState(isDisabled: boolean): void { - this.disabled = isDisabled; - if (isDisabled) { - this.gaugeHighlightFormGroup.disable({emitEvent: false}); - } else { - this.gaugeHighlightFormGroup.enable({emitEvent: false}); - } - } - - writeValue(value: GaugeHighlight): void { - this.modelValue = value; - this.gaugeHighlightFormGroup.patchValue( - value, {emitEvent: false} - ); - } - - highlightRangeText(): string { - const value: GaugeHighlight = this.gaugeHighlightFormGroup.value; - const from = isNumber(value.from) ? value.from : 0; - const to = isNumber(value.to) ? value.to : 0; - return `${from} - ${to}`; - } - - private updateModel() { - const value: GaugeHighlight = this.gaugeHighlightFormGroup.value; - this.modelValue = value; - if (this.gaugeHighlightFormGroup.valid) { - this.propagateChange(this.modelValue); - } else { - this.propagateChange(null); - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.ts index ef334d6ee2..51665a3afa 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/tick-value.component.ts @@ -27,7 +27,7 @@ import { IAliasController } from '@core/api/widget-api.models'; @Component({ selector: 'tb-tick-value', templateUrl: './tick-value.component.html', - styleUrls: ['./tick-value.component.scss', './../widget-settings.scss'], + styleUrls: ['./tick-value.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gpio/gpio-item.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gpio/gpio-item.component.ts index d6e6d675ad..636fd9f0fe 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gpio/gpio-item.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gpio/gpio-item.component.ts @@ -56,7 +56,7 @@ export const gpioItemValidator = (hasColor: boolean): ValidatorFn => (control: A @Component({ selector: 'tb-gpio-item', templateUrl: './gpio-item.component.html', - styleUrls: ['./gpio-item.component.scss', './../widget-settings.scss'], + styleUrls: ['./gpio-item.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/battery-level-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/battery-level-widget-settings.component.html new file mode 100644 index 0000000000..3bd7055728 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/battery-level-widget-settings.component.html @@ -0,0 +1,71 @@ + + +
+
widgets.battery-level.battery-level-card-style
+ + + {{ batteryLevelLayoutTranslationMap.get(layout) | translate }} + + +
+
{{ 'widgets.battery-level.sections-count' | translate }}
+ + + +
+
+ + {{ 'widgets.battery-level.value' | translate }} + +
+ + {{ 'widgets.battery-level.auto-scale' | translate }} + + + + + +
+
+
+
{{ 'widgets.battery-level.battery-level-color' | translate }}
+ + +
+
+
{{ 'widgets.battery-level.battery-shape-color' | translate }}
+ + +
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/battery-level-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/battery-level-widget-settings.component.ts new file mode 100644 index 0000000000..6c6fecf662 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/battery-level-widget-settings.component.ts @@ -0,0 +1,119 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, Injector } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { formatValue } from '@core/utils'; +import { + batteryLevelDefaultSettings, BatteryLevelLayout, + batteryLevelLayoutImages, + batteryLevelLayouts, + batteryLevelLayoutTranslations +} from '@home/components/widget/lib/indicator/battery-level-widget.models'; + +@Component({ + selector: 'tb-battery-level-widget-settings', + templateUrl: './battery-level-widget-settings.component.html', + styleUrls: [] +}) +export class BatteryLevelWidgetSettingsComponent extends WidgetSettingsComponent { + + batteryLevelLayouts = batteryLevelLayouts; + + batteryLevelLayoutTranslationMap = batteryLevelLayoutTranslations; + batteryLevelLayoutImageMap = batteryLevelLayoutImages; + + batteryLevelWidgetSettingsForm: UntypedFormGroup; + + valuePreviewFn = this._valuePreviewFn.bind(this); + + get sectionsCountEnabled(): boolean { + const layout: BatteryLevelLayout = this.batteryLevelWidgetSettingsForm.get('layout').value; + return [BatteryLevelLayout.vertical_divided, BatteryLevelLayout.horizontal_divided].includes(layout); + } + + constructor(protected store: Store, + private $injector: Injector, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.batteryLevelWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return {...batteryLevelDefaultSettings}; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.batteryLevelWidgetSettingsForm = this.fb.group({ + layout: [settings.layout, []], + sectionsCount: [settings.sectionsCount, [Validators.min(2), Validators.max(20)]], + + showValue: [settings.showValue, []], + autoScaleValueSize: [settings.autoScaleValueSize, []], + valueFont: [settings.valueFont, []], + valueColor: [settings.valueColor, []], + + batteryLevelColor: [settings.batteryLevelColor, []], + batteryShapeColor: [settings.batteryShapeColor, []], + + background: [settings.background, []] + }); + } + + protected validatorTriggers(): string[] { + return ['showValue', 'layout']; + } + + protected updateValidators(emitEvent: boolean) { + const showValue: boolean = this.batteryLevelWidgetSettingsForm.get('showValue').value; + const layout: BatteryLevelLayout = this.batteryLevelWidgetSettingsForm.get('layout').value; + const divided = [BatteryLevelLayout.vertical_divided, BatteryLevelLayout.horizontal_divided].includes(layout); + + if (showValue) { + this.batteryLevelWidgetSettingsForm.get('autoScaleValueSize').enable(); + this.batteryLevelWidgetSettingsForm.get('valueFont').enable(); + this.batteryLevelWidgetSettingsForm.get('valueColor').enable(); + } else { + this.batteryLevelWidgetSettingsForm.get('autoScaleValueSize').disable(); + this.batteryLevelWidgetSettingsForm.get('valueFont').disable(); + this.batteryLevelWidgetSettingsForm.get('valueColor').disable(); + } + + if (divided) { + this.batteryLevelWidgetSettingsForm.get('sectionsCount').enable(); + } else { + this.batteryLevelWidgetSettingsForm.get('sectionsCount').disable(); + } + + this.batteryLevelWidgetSettingsForm.get('autoScaleValueSize').updateValueAndValidity({emitEvent}); + this.batteryLevelWidgetSettingsForm.get('valueFont').updateValueAndValidity({emitEvent}); + this.batteryLevelWidgetSettingsForm.get('valueColor').updateValueAndValidity({emitEvent}); + this.batteryLevelWidgetSettingsForm.get('sectionsCount').updateValueAndValidity({emitEvent}); + } + + private _valuePreviewFn(): string { + const units: string = this.widgetConfig.config.units; + const decimals: number = this.widgetConfig.config.decimals; + return formatValue(22, decimals, units, true); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html new file mode 100644 index 0000000000..6b401924e1 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html @@ -0,0 +1,261 @@ + + +
+
+
+
+
widgets.liquid-level-card.shape
+ + + {{ DataSourceTypeTranslations.get(type) | translate }} + + +
+ + + {{ ShapesTranslationMap.get(shape) | translate }} + + + +
+
widgets.liquid-level-card.shape-attribute-name
+ + +
+
+
widgets.liquid-level-card.shape-by-attribute
+
+
+
+
+
+
+
+
widgets.liquid-level-card.layout
+ + + {{ LevelCardLayoutTranslationMap.get(layout) | translate }} + + +
+
+
widgets.liquid-level-card.units
+
+
widgets.liquid-level-card.datasource-units
+ + +
+
+
widgets.liquid-level-card.widget-units
+
+ + + + {{ DataSourceTypeTranslations.get(type) | translate }} + + + + + + + + + +
+
+
+
widgets.liquid-level-card.total-volume
+
+ + + + {{ DataSourceTypeTranslations.get(type) | translate }} + + + + + + + warning + + + + + + + + +
+
+
+
+
widgets.liquid-level-card.color-and-font
+
+
widgets.liquid-level-card.tank-color
+ + +
+
+
widgets.liquid-level-card.liquid-color
+ + +
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
widgets.liquid-level-card.value
+
+ + + + +
+
+
+
widgets.liquid-level-card.total-volume
+
+ + + + +
+
+
+ + {{ 'widgets.liquid-level-card.background-overlay' | translate }} + + + +
+
+
+ + + + + {{ 'widget-config.tooltip' | translate }} + + + + +
+ + {{ 'widgets.liquid-level-card.level' | translate }} + +
+ + + + +
widget-config.decimals-suffix
+
+ + + + +
+
+
+ + {{ 'widgets.value-card.date' | translate }} + +
+ + + + + +
+
+
+
widgets.liquid-level-card.tooltip-background
+ + +
+
+
widgets.liquid-level-card.background-blur
+ + +
px
+
+
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts new file mode 100644 index 0000000000..14f906d453 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts @@ -0,0 +1,306 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ChangeDetectorRef, Component, Injector, ViewChild } from '@angular/core'; +import { + DataKey, + Datasource, + DatasourceType, + WidgetSettings, + WidgetSettingsComponent +} from '@shared/models/widget.models'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { formatValue, isDefined } from '@core/utils'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { DateFormatProcessor, DateFormatSettings } from '@shared/models/widget-settings.models'; +import { + CapacityUnits, + createShapeLayout, + fetchEntityKeys, + fetchEntityKeysForDevice, + levelCardDefaultSettings, + LevelCardLayout, + levelCardLayoutTranslations, + LiquidWidgetDataSourceType, + LiquidWidgetDataSourceTypeTranslations, + loadSvgShapesMapping, + optionsFilter, + Shapes, + ShapesTranslations, + updatedFormSettingsValidators +} from '@home/components/widget/lib/indicator/liquid-level-widget.models'; +import { UnitsType } from '@shared/models/unit.models'; +import { ImageCardsSelectComponent } from '@home/components/widget/lib/settings/common/image-cards-select.component'; +import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; +import { Observable, of, ReplaySubject } from 'rxjs'; +import { map, share, tap } from 'rxjs/operators'; +import { ResourcesService } from '@core/services/resources.service'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { UtilsService } from '@core/services/utils.service'; +import { EntityService } from '@core/http/entity.service'; + +@Component({ + selector: 'liquid-level-card-widget-settings', + templateUrl: './liquid-level-card-widget-settings.component.html', + styleUrls: [] +}) +export class LiquidLevelCardWidgetSettingsComponent extends WidgetSettingsComponent { + + @ViewChild('layoutsImageCardsSelect', { static: false }) layoutsImageCardsSelect: ImageCardsSelectComponent; + + @ViewChild('shapesImageCardsSelect', { static: false }) shapesImageCardsSelect: ImageCardsSelectComponent; + + public get volumeInput(): boolean { + const datasourceUnits = this.levelCardWidgetSettingsForm.get('datasourceUnits').value; + const layout: LevelCardLayout = this.levelCardWidgetSettingsForm.get('layout').value; + + if (layout === LevelCardLayout.absolute) { + return true; + } + return datasourceUnits !== CapacityUnits.percent; + } + + private get datasource(): Datasource { + if (this.widgetConfig.config.datasources && this.widgetConfig.config.datasources) { + return this.widgetConfig.config.datasources[0]; + } else { + return null; + } + } + + LevelCardLayout = LevelCardLayout; + LevelCardLayouts = Object.values(LevelCardLayout) as LevelCardLayout[]; + LevelCardLayoutTranslationMap = levelCardLayoutTranslations; + + DataSourceType = LiquidWidgetDataSourceType; + DataSourceTypes = Object.values(LiquidWidgetDataSourceType) as LiquidWidgetDataSourceType[]; + DataSourceTypeTranslations = LiquidWidgetDataSourceTypeTranslations; + + shapes = Object.values(Shapes) as Shapes[]; + shapesImageMap: Map = new Map(); + ShapesTranslationMap = ShapesTranslations; + + unitsType = UnitsType; + + levelCardWidgetSettingsForm: FormGroup; + + valuePreviewFn = this._valuePreviewFn.bind(this); + + tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); + + totalVolumeValuePreviewFn = this._totalVolumeValuePreviewFn.bind(this); + + datePreviewFn = this._datePreviewFn.bind(this); + + private keySearchText: string; + private latestKeySearchTextResult: Array; + + private functionTypeKeys: Array = []; + + private lastKeysId: string; + private lastFetchedKeys: Array; + + constructor(protected store: Store, + private $injector: Injector, + private fb: FormBuilder, + private resourcesService: ResourcesService, + private sanitizer: DomSanitizer, + private cd: ChangeDetectorRef, + private utils: UtilsService, + private entityService: EntityService) { + super(store); + } + + protected settingsForm(): FormGroup { + return this.levelCardWidgetSettingsForm; + } + + protected onWidgetConfigSet(widgetConfig: WidgetConfigComponentData) { + this.createSvgShapesMapping(); + + for (const type of this.utils.getPredefinedFunctionsList()) { + this.functionTypeKeys.push({ + name: type, + type: DataKeyType.function + }); + } + } + + protected defaultSettings(): WidgetSettings { + return levelCardDefaultSettings; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.levelCardWidgetSettingsForm = this.fb.group({ + tankSelectionType: [settings.tankSelectionType, []], + selectedShape: [settings.selectedShape, [Validators.required]], + shapeAttributeName: [settings.shapeAttributeName, [Validators.required]], + tankColor: [settings.tankColor, []], + datasourceUnits: [settings.datasourceUnits, [Validators.required]], + + layout: [settings.layout, []], + + volumeSource: [settings.volumeSource, []], + volumeConstant: [settings.volumeConstant, [Validators.required, Validators.min(0.1)]], + volumeAttributeName: [settings.volumeAttributeName, [Validators.required]], + volumeUnits: [settings.volumeUnits, [Validators.required]], + volumeFont: [settings.volumeFont, []], + volumeColor: [settings.volumeColor, []], + valueFont: [settings.valueFont, []], + valueColor: [settings.valueColor, []], + units: [settings.units, [Validators.required]], + widgetUnitsSource: [settings.widgetUnitsSource, [Validators.required]], + widgetUnitsAttributeName: [settings.widgetUnitsAttributeName, [Validators.required]], + showBackgroundOverlay: [settings.showBackgroundOverlay, []], + backgroundOverlayColor: [settings.backgroundOverlayColor, []], + + liquidColor: [settings.liquidColor, []], + background: [settings.background], + + showTooltip: [settings.showTooltip, []], + showTooltipLevel: [settings.showTooltipLevel, []], + tooltipUnits: [settings.tooltipUnits, []], + tooltipLevelDecimals: [settings.tooltipLevelDecimals, []], + tooltipLevelFont: [settings.tooltipLevelFont, []], + tooltipLevelColor: [settings.tooltipLevelColor, []], + showTooltipDate: [settings.showTooltipDate, []], + tooltipDateFormat: [settings.tooltipDateFormat, []], + tooltipDateFont: [settings.tooltipDateFont, []], + tooltipDateColor: [settings.tooltipDateColor, []], + tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], + tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], + }); + + this.levelCardWidgetSettingsForm.get('selectedShape').valueChanges.subscribe(() => { + this.cd.detectChanges(); + this.layoutsImageCardsSelect?.imageCardsSelectOptions.notifyOnChanges(); + }); + } + + protected validatorTriggers(): string[] { + return [ + 'showBackgroundOverlay', 'showTooltip', 'showTooltipLevel', 'tankSelectionType', 'datasourceUnits', + 'showTooltipDate', 'layout', 'volumeSource', 'widgetUnitsSource' + ]; + } + + protected updateValidators(emitEvent: boolean, trigger?: string) { + updatedFormSettingsValidators(this.levelCardWidgetSettingsForm); + + if (this.levelCardWidgetSettingsForm.get('showBackgroundOverlay').value) { + this.levelCardWidgetSettingsForm.get('backgroundOverlayColor').enable({emitEvent: false}); + } else { + this.levelCardWidgetSettingsForm.get('backgroundOverlayColor').disable({emitEvent: false}); + } + } + + private createSvgShapesMapping(): void { + loadSvgShapesMapping(this.resourcesService).subscribe(shapeMap => { + this.shapesImageMap = shapeMap; + + this.cd.detectChanges(); + this.layoutsImageCardsSelect?.imageCardsSelectOptions.notifyOnChanges(); + this.shapesImageCardsSelect?.imageCardsSelectOptions.notifyOnChanges(); + }); + } + + createShape(svg: string, layout: LevelCardLayout): SafeUrl { + return createShapeLayout(svg, layout, this.sanitizer); + } + + private _valuePreviewFn(): string { + const units: string = this.widgetConfig.config.units; + const decimals: number = this.widgetConfig.config.decimals; + return formatValue(32, decimals, units, true); + } + + private _tooltipValuePreviewFn() { + const units: string = this.levelCardWidgetSettingsForm.get('tooltipUnits').value; + const decimals: number = this.levelCardWidgetSettingsForm.get('tooltipLevelDecimals').value; + return formatValue(32, decimals, units, true); + } + + private _totalVolumeValuePreviewFn() { + const value = this.levelCardWidgetSettingsForm.get('volumeConstant').value; + const datasourceUnits = this.levelCardWidgetSettingsForm.get('datasourceUnits').value; + const decimals: number = this.widgetConfig.config.decimals; + let units: string = this.widgetConfig.config.units; + + if (datasourceUnits !== CapacityUnits.percent) { + units = datasourceUnits; + } + + return formatValue((isDefined(value) ? value : 500), decimals, units, true); + } + + private _datePreviewFn(): string { + const dateFormat: DateFormatSettings = this.levelCardWidgetSettingsForm.get('tooltipDateFormat').value; + const processor = DateFormatProcessor.fromSettings(this.$injector, dateFormat); + processor.update(Date.now()); + return processor.formatted; + } + + public fetchOptions(searchText: string): Observable> { + if (this.keySearchText !== searchText) { + this.keySearchText = searchText; + const dataKeyFilter = optionsFilter(this.keySearchText); + return this.getKeys().pipe( + tap(res => this.lastFetchedKeys !== res ? this.lastFetchedKeys = res : []), + map(name => name?.filter(dataKeyFilter).map(key => key.name)), + tap(res => this.latestKeySearchTextResult = res) + ); + } + return of(this.latestKeySearchTextResult); + } + + private getKeys(): Observable> { + let fetchObservable: Observable>; + if (this.datasource?.type === DatasourceType.function) { + fetchObservable = of(this.functionTypeKeys); + } else if (this.datasource?.type === DatasourceType.entity && this.datasource?.entityAliasId || + this.datasource?.type === DatasourceType.device && this.datasource?.deviceId) { + if (this.datasource?.type === DatasourceType.device) { + if (this.lastKeysId !== this.datasource?.deviceId || !this.lastFetchedKeys) { + this.lastKeysId = this.datasource.deviceId; + fetchObservable = fetchEntityKeysForDevice(this.datasource?.deviceId, [DataKeyType.attribute], + this.entityService); + } else { + fetchObservable = of(this.lastFetchedKeys); + } + } else { + if (this.lastKeysId !== this.datasource?.entityAliasId || !this.lastFetchedKeys) { + this.lastKeysId = this.datasource.entityAliasId; + fetchObservable = fetchEntityKeys(this.datasource?.entityAliasId, [DataKeyType.attribute], + this.entityService, this.aliasController); + } else { + fetchObservable = of(this.lastFetchedKeys); + } + } + } else { + fetchObservable = of([]); + } + return fetchObservable.pipe( + share({ + connector: () => new ReplaySubject(1), + resetOnError: false, + resetOnComplete: false, + resetOnRefCountZero: false + }) + ); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/signal-strength-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/signal-strength-widget-settings.component.html new file mode 100644 index 0000000000..808bbe11d9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/signal-strength-widget-settings.component.html @@ -0,0 +1,124 @@ + + +
+
widgets.signal-strength.signal-strength-card-style
+ + + {{ signalStrengthLayoutTranslationMap.get(layout) | translate }} + + +
+ + {{ 'widgets.signal-strength.date' | translate }} + +
+ + + + + +
+
+
+
{{ 'widgets.signal-strength.active-bars-color' | translate }}
+ + +
+
+
{{ 'widgets.signal-strength.inactive-bars-color' | translate }}
+ + +
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
+ + + + + {{ 'widgets.signal-strength.tooltip' | translate }} + + + + +
+ + {{ 'widgets.signal-strength.value' | translate }} + +
+ + + + +
+
+
+ + {{ 'widgets.signal-strength.date' | translate }} + +
+ + + + + +
+
+
+
{{ 'widgets.signal-strength.background-color' | translate }}
+ + +
+
+
widgets.signal-strength.background-blur
+ + +
px
+
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/signal-strength-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/signal-strength-widget-settings.component.ts new file mode 100644 index 0000000000..e7994462b6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/signal-strength-widget-settings.component.ts @@ -0,0 +1,167 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, Injector } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { formatValue } from '@core/utils'; +import { + signalStrengthDefaultSettings, + signalStrengthLayoutImages, + signalStrengthLayouts, + signalStrengthLayoutTranslations +} from '@home/components/widget/lib/indicator/signal-strength-widget.models'; +import { DateFormatProcessor, DateFormatSettings } from '@shared/models/widget-settings.models'; + +@Component({ + selector: 'tb-signal-strength-widget-settings', + templateUrl: './signal-strength-widget-settings.component.html', + styleUrls: ['./../widget-settings.scss'], +}) +export class SignalStrengthWidgetSettingsComponent extends WidgetSettingsComponent { + + signalStrengthLayouts = signalStrengthLayouts; + + signalStrengthLayoutTranslationMap = signalStrengthLayoutTranslations; + signalStrengthLayoutImageMap = signalStrengthLayoutImages; + + signalStrengthWidgetSettingsForm: UntypedFormGroup; + + datePreviewFn = this._datePreviewFn.bind(this); + tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); + tooltipDatePreviewFn = this._tooltipDatePreviewFn.bind(this); + + constructor(protected store: Store, + private $injector: Injector, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.signalStrengthWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return {...signalStrengthDefaultSettings}; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.signalStrengthWidgetSettingsForm = this.fb.group({ + layout: [settings.layout, []], + + showDate: [settings.showDate, []], + dateFormat: [settings.dateFormat, []], + dateFont: [settings.dateFont, []], + dateColor: [settings.dateColor, []], + + activeBarsColor: [settings.activeBarsColor, []], + inactiveBarsColor: [settings.inactiveBarsColor, []], + + showTooltip: [settings.showTooltip, []], + + showTooltipValue: [settings.showTooltipValue, []], + tooltipValueFont: [settings.tooltipValueFont, []], + tooltipValueColor: [settings.tooltipValueColor, []], + + showTooltipDate: [settings.showTooltipDate, []], + tooltipDateFormat: [settings.tooltipDateFormat, []], + tooltipDateFont: [settings.tooltipDateFont, []], + tooltipDateColor: [settings.tooltipDateColor, []], + + tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], + tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], + + background: [settings.background, []] + }); + } + + protected validatorTriggers(): string[] { + return ['showDate', 'showTooltip', 'showTooltipValue', 'showTooltipDate']; + } + + protected updateValidators(emitEvent: boolean) { + const showDate: boolean = this.signalStrengthWidgetSettingsForm.get('showDate').value; + const showTooltip: boolean = this.signalStrengthWidgetSettingsForm.get('showTooltip').value; + const showTooltipValue: boolean = this.signalStrengthWidgetSettingsForm.get('showTooltipValue').value; + const showTooltipDate: boolean = this.signalStrengthWidgetSettingsForm.get('showTooltipDate').value; + + if (showDate) { + this.signalStrengthWidgetSettingsForm.get('dateFormat').enable(); + this.signalStrengthWidgetSettingsForm.get('dateFont').enable(); + this.signalStrengthWidgetSettingsForm.get('dateColor').enable(); + } else { + this.signalStrengthWidgetSettingsForm.get('dateFormat').disable(); + this.signalStrengthWidgetSettingsForm.get('dateFont').disable(); + this.signalStrengthWidgetSettingsForm.get('dateColor').disable(); + } + + if (showTooltip) { + this.signalStrengthWidgetSettingsForm.get('showTooltipValue').enable({emitEvent: false}); + this.signalStrengthWidgetSettingsForm.get('showTooltipDate').enable({emitEvent: false}); + this.signalStrengthWidgetSettingsForm.get('tooltipBackgroundColor').enable(); + this.signalStrengthWidgetSettingsForm.get('tooltipBackgroundBlur').enable(); + if (showTooltipValue) { + this.signalStrengthWidgetSettingsForm.get('tooltipValueFont').enable(); + this.signalStrengthWidgetSettingsForm.get('tooltipValueColor').enable(); + } else { + this.signalStrengthWidgetSettingsForm.get('tooltipValueFont').disable(); + this.signalStrengthWidgetSettingsForm.get('tooltipValueColor').disable(); + } + if (showTooltipDate) { + this.signalStrengthWidgetSettingsForm.get('tooltipDateFormat').enable(); + this.signalStrengthWidgetSettingsForm.get('tooltipDateFont').enable(); + this.signalStrengthWidgetSettingsForm.get('tooltipDateColor').enable(); + } else { + this.signalStrengthWidgetSettingsForm.get('tooltipDateFormat').disable(); + this.signalStrengthWidgetSettingsForm.get('tooltipDateFont').disable(); + this.signalStrengthWidgetSettingsForm.get('tooltipDateColor').disable(); + } + } else { + this.signalStrengthWidgetSettingsForm.get('showTooltipValue').disable({emitEvent: false}); + this.signalStrengthWidgetSettingsForm.get('showTooltipDate').disable({emitEvent: false}); + this.signalStrengthWidgetSettingsForm.get('tooltipBackgroundColor').disable(); + this.signalStrengthWidgetSettingsForm.get('tooltipBackgroundBlur').disable(); + this.signalStrengthWidgetSettingsForm.get('tooltipValueFont').disable(); + this.signalStrengthWidgetSettingsForm.get('tooltipValueColor').disable(); + this.signalStrengthWidgetSettingsForm.get('tooltipDateFormat').disable(); + this.signalStrengthWidgetSettingsForm.get('tooltipDateFont').disable(); + this.signalStrengthWidgetSettingsForm.get('tooltipDateColor').disable(); + } + } + + private _datePreviewFn(): string { + const dateFormat: DateFormatSettings = this.signalStrengthWidgetSettingsForm.get('dateFormat').value; + const processor = DateFormatProcessor.fromSettings(this.$injector, dateFormat); + processor.update(Date.now()); + return processor.formatted; + } + + private _tooltipValuePreviewFn(): string { + const units: string = this.widgetConfig.config.units; + const decimals: number = this.widgetConfig.config.decimals; + return formatValue(-76, decimals, units, true); + } + + private _tooltipDatePreviewFn(): string { + const dateFormat: DateFormatSettings = this.signalStrengthWidgetSettingsForm.get('tooltipDateFormat').value; + const processor = DateFormatProcessor.fromSettings(this.$injector, {...dateFormat, ...{hideLastUpdatePrefix: true}}); + processor.update(Date.now()); + return processor.formatted; + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/datakey-select-option.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/datakey-select-option.component.ts index a546537bf7..1d304aef64 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/datakey-select-option.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/datakey-select-option.component.ts @@ -46,7 +46,7 @@ export const dataKeySelectOptionValidator = (control: AbstractControl) => { @Component({ selector: 'tb-datakey-select-option', templateUrl: './datakey-select-option.component.html', - styleUrls: ['./datakey-select-option.component.scss', './../widget-settings.scss'], + styleUrls: ['./datakey-select-option.component.scss'], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.html index 3c62810000..f3c745a665 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.html @@ -69,6 +69,9 @@ {{ 'widgets.input-widgets.datakey-value-type-json' | translate }} + + {{ 'widgets.input-widgets.datakey-value-type-color' | translate }} + @@ -90,11 +93,36 @@
- + widgets.input-widgets.disable-on-datakey-name +
+ + widgets.input-widgets.field-appearance + + + {{ 'widgets.input-widgets.appearance-fill' | translate }} + + + {{ 'widgets.input-widgets.appearance-outline' | translate }} + + + + + widgets.input-widgets.subscript-sizing + + + {{ 'widgets.input-widgets.subscript-sizing-fixed' | translate }} + + + {{ 'widgets.input-widgets.subscript-sizing-dynamic' | translate }} + + + +
@@ -205,7 +233,9 @@
-
+
widgets.input-widgets.icon-settings {{ 'widgets.input-widgets.use-custom-icon' | translate }} @@ -215,10 +245,10 @@ iconClearButton formControlName="icon"> - - +
widgets.input-widgets.value-conversion-settings diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.ts index 7eff1bdc80..785ae0cca6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-key-settings.component.ts @@ -24,7 +24,10 @@ import { dataKeySelectOptionValidator } from '@home/components/widget/lib/settings/input/datakey-select-option.component'; import { CdkDragDrop } from '@angular/cdk/drag-drop'; -import { MultipleInputWidgetDataKeyValueType } from '@home/components/widget/lib/multiple-input-widget.component'; +import { + MultipleInputWidgetDataKeyEditableType, + MultipleInputWidgetDataKeyValueType +} from '@home/components/widget/lib/multiple-input-widget.component'; @Component({ selector: 'tb-update-multiple-attributes-key-settings', @@ -52,6 +55,8 @@ export class UpdateMultipleAttributesKeySettingsComponent extends WidgetSettings required: false, isEditable: 'editable', disabledOnDataKey: '', + appearance: 'outline', + subscriptSizing: 'fixed', slideToggleLabelPosition: 'after', selectOptions: [], @@ -92,6 +97,8 @@ export class UpdateMultipleAttributesKeySettingsComponent extends WidgetSettings required: [settings.required, []], isEditable: [settings.isEditable, []], disabledOnDataKey: [settings.disabledOnDataKey, []], + appearance: [settings.appearance, []], + subscriptSizing: [settings.subscriptSizing, []], // Slide toggle settings @@ -146,7 +153,7 @@ export class UpdateMultipleAttributesKeySettingsComponent extends WidgetSettings const dataKeyValueType: MultipleInputWidgetDataKeyValueType = this.updateMultipleAttributesKeySettingsForm.get('dataKeyValueType').value; const required: boolean = this.updateMultipleAttributesKeySettingsForm.get('required').value; - const isEditable: string = this.updateMultipleAttributesKeySettingsForm.get('isEditable').value; + const isEditable: MultipleInputWidgetDataKeyEditableType = this.updateMultipleAttributesKeySettingsForm.get('isEditable').value; const useCustomIcon: boolean = this.updateMultipleAttributesKeySettingsForm.get('useCustomIcon').value; const useGetValueFunction: boolean = this.updateMultipleAttributesKeySettingsForm.get('useGetValueFunction').value; const useSetValueFunction: boolean = this.updateMultipleAttributesKeySettingsForm.get('useSetValueFunction').value; @@ -163,10 +170,15 @@ export class UpdateMultipleAttributesKeySettingsComponent extends WidgetSettings this.updateMultipleAttributesKeySettingsForm.get('useGetValueFunction').enable({emitEvent: false}); this.updateMultipleAttributesKeySettingsForm.get('useSetValueFunction').enable({emitEvent: false}); - if (isEditable !== 'disabled') { + if (isEditable === 'editable') { this.updateMultipleAttributesKeySettingsForm.get('disabledOnDataKey').enable({emitEvent: false}); } + if (!['booleanSwitch', 'booleanCheckbox'].includes(dataKeyValueType)) { + this.updateMultipleAttributesKeySettingsForm.get('appearance').enable({emitEvent: false}); + this.updateMultipleAttributesKeySettingsForm.get('subscriptSizing').enable({emitEvent: false}); + } + if (dataKeyValueType === 'booleanSwitch') { this.updateMultipleAttributesKeySettingsForm.get('slideToggleLabelPosition').enable({emitEvent: false}); } else if (dataKeyValueType === 'select') { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-widget-settings.component.html index 9c250e5968..c1536040dd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-widget-settings.component.html @@ -72,7 +72,7 @@
- widgets.input-widgets.fields-alignment + widgets.input-widgets.layout
widgets.input-widgets.fields-alignment @@ -90,5 +90,15 @@
+
+ + widgets.input-widgets.row-gap + + + + widgets.input-widgets.column-gap + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-widget-settings.component.ts index c1cf455f9c..9b55e131a8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/input/update-multiple-attributes-widget-settings.component.ts @@ -49,7 +49,9 @@ export class UpdateMultipleAttributesWidgetSettingsComponent extends WidgetSetti showGroupTitle: false, groupTitle: '', fieldsAlignment: 'row', - fieldsInRow: 2 + fieldsInRow: 2, + rowGap: 5, + columnGap: 10 }; } @@ -77,6 +79,11 @@ export class UpdateMultipleAttributesWidgetSettingsComponent extends WidgetSetti fieldsAlignment: [settings.fieldsAlignment, []], fieldsInRow: [settings.fieldsInRow, [Validators.min(1)]], + + // Layout gap + + rowGap: [settings.rowGap, [Validators.min(0)]], + columnGap: [settings.columnGap, [Validators.min(0)]] }); } @@ -105,13 +112,16 @@ export class UpdateMultipleAttributesWidgetSettingsComponent extends WidgetSetti } if (fieldsAlignment === 'row') { this.updateMultipleAttributesWidgetSettingsForm.get('fieldsInRow').enable(); + this.updateMultipleAttributesWidgetSettingsForm.get('columnGap').enable(); } else { this.updateMultipleAttributesWidgetSettingsForm.get('fieldsInRow').disable(); + this.updateMultipleAttributesWidgetSettingsForm.get('columnGap').disable(); } this.updateMultipleAttributesWidgetSettingsForm.get('updateAllValues').updateValueAndValidity({emitEvent}); this.updateMultipleAttributesWidgetSettingsForm.get('saveButtonLabel').updateValueAndValidity({emitEvent}); this.updateMultipleAttributesWidgetSettingsForm.get('resetButtonLabel').updateValueAndValidity({emitEvent}); this.updateMultipleAttributesWidgetSettingsForm.get('groupTitle').updateValueAndValidity({emitEvent}); this.updateMultipleAttributesWidgetSettingsForm.get('fieldsInRow').updateValueAndValidity({emitEvent}); + this.updateMultipleAttributesWidgetSettingsForm.get('columnGap').updateValueAndValidity({emitEvent}); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/image-map-provider-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/image-map-provider-settings.component.html index c111f84e9f..a9d7a2c8ff 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/image-map-provider-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/image-map-provider-settings.component.html @@ -16,9 +16,9 @@ -->
- - +
widgets.maps.image-map-background-from-entity-attribute
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/markers-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/markers-settings.component.html index 2b5de971b5..8de4820690 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/markers-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/markers-settings.component.html @@ -170,10 +170,10 @@ - - + widgets.maps.custom-marker-image-size @@ -186,10 +186,10 @@ functionTitle="{{ 'widgets.maps.marker-image-function' | translate }}" helpId="widget/lib/map/marker_image_fn"> - - + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-marker-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-marker-settings.component.html index fd14bebdf7..0f31ab54a7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-marker-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/map/trip-animation-marker-settings.component.html @@ -70,10 +70,10 @@ - - + widgets.maps.custom-marker-image-size @@ -86,10 +86,10 @@ functionTitle="{{ 'widgets.maps.marker-image-function' | translate }}" helpId="widget/lib/map/marker_image_fn"> - - + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/weather/wind-speed-direction-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/weather/wind-speed-direction-widget-settings.component.html new file mode 100644 index 0000000000..6168db2f98 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/weather/wind-speed-direction-widget-settings.component.html @@ -0,0 +1,104 @@ + + +
+
widgets.wind-speed-direction.wind-speed-direction-card-style
+ + + {{ windSpeedDirectionLayoutTranslationMap.get(layout) | translate }} + + +
+
{{ (hasCenterValue ? 'widgets.wind-speed-direction.center-value' : 'widgets.wind-speed-direction.wind-direction') | translate }}
+
+ + + + +
+
+
+
{{ 'widgets.wind-speed-direction.ticks-color' | translate }}
+ + +
+
+
{{ 'widgets.wind-speed-direction.ticks-labels-type' | translate }}
+ + + + {{ 'widgets.wind-speed-direction.directional-names' | translate }} + + + {{ 'widgets.wind-speed-direction.degrees' | translate }} + + + +
+
+
{{ 'widgets.wind-speed-direction.major-ticks' | translate }}
+
+ + + + +
+
+
+
{{ 'widgets.wind-speed-direction.minor-ticks' | translate }}
+
+ + + + +
+
+
+
{{ 'widgets.wind-speed-direction.arrow-color' | translate }}
+ + +
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/weather/wind-speed-direction-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/weather/wind-speed-direction-widget-settings.component.ts new file mode 100644 index 0000000000..aa33842cb9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/weather/wind-speed-direction-widget-settings.component.ts @@ -0,0 +1,138 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, Injector } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { formatValue, isDefinedAndNotNull } from '@core/utils'; +import { + windSpeedDirectionDefaultSettings, + WindSpeedDirectionLayout, + windSpeedDirectionLayoutImages, + windSpeedDirectionLayouts, + windSpeedDirectionLayoutTranslations +} from '@home/components/widget/lib/weather/wind-speed-direction-widget.models'; +import { getDataKey } from '@shared/models/widget-settings.models'; + +@Component({ + selector: 'tb-wind-speed-direction-widget-settings', + templateUrl: './wind-speed-direction-widget-settings.component.html', + styleUrls: [] +}) +export class WindSpeedDirectionWidgetSettingsComponent extends WidgetSettingsComponent { + + get hasCenterValue(): boolean { + return !!getDataKey(this.widgetConfig.config.datasources, 1); + } + + get majorTicksFontEnabled(): boolean { + const layout: WindSpeedDirectionLayout = this.windSpeedDirectionWidgetSettingsForm.get('layout').value; + return [ WindSpeedDirectionLayout.default, WindSpeedDirectionLayout.advanced ].includes(layout); + } + + get minorTicksFontEnabled(): boolean { + const layout: WindSpeedDirectionLayout = this.windSpeedDirectionWidgetSettingsForm.get('layout').value; + return layout === WindSpeedDirectionLayout.advanced; + } + + windSpeedDirectionLayouts = windSpeedDirectionLayouts; + + windSpeedDirectionLayoutTranslationMap = windSpeedDirectionLayoutTranslations; + windSpeedDirectionLayoutImageMap = windSpeedDirectionLayoutImages; + + windSpeedDirectionWidgetSettingsForm: UntypedFormGroup; + + centerValuePreviewFn = this._centerValuePreviewFn.bind(this); + + constructor(protected store: Store, + private $injector: Injector, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.windSpeedDirectionWidgetSettingsForm; + } + + protected defaultSettings(): WidgetSettings { + return {...windSpeedDirectionDefaultSettings}; + } + + protected onSettingsSet(settings: WidgetSettings) { + this.windSpeedDirectionWidgetSettingsForm = this.fb.group({ + layout: [settings.layout, []], + + centerValueFont: [settings.centerValueFont, []], + centerValueColor: [settings.centerValueColor, []], + + ticksColor: [settings.ticksColor, []], + directionalNamesElseDegrees: [settings.directionalNamesElseDegrees, []], + + majorTicksFont: [settings.majorTicksFont, []], + majorTicksColor: [settings.majorTicksColor, []], + + minorTicksFont: [settings.minorTicksFont, []], + minorTicksColor: [settings.minorTicksColor, []], + + arrowColor: [settings.arrowColor, []], + + background: [settings.background, []] + }); + } + + protected validatorTriggers(): string[] { + return ['layout']; + } + + protected updateValidators(emitEvent: boolean) { + const layout: WindSpeedDirectionLayout = this.windSpeedDirectionWidgetSettingsForm.get('layout').value; + + const majorTicksFontEnabled = [ WindSpeedDirectionLayout.default, WindSpeedDirectionLayout.advanced ].includes(layout); + const minorTicksFontEnabled = layout === WindSpeedDirectionLayout.advanced; + + if (majorTicksFontEnabled) { + this.windSpeedDirectionWidgetSettingsForm.get('majorTicksFont').enable(); + } else { + this.windSpeedDirectionWidgetSettingsForm.get('majorTicksFont').disable(); + } + + if (minorTicksFontEnabled) { + this.windSpeedDirectionWidgetSettingsForm.get('minorTicksFont').enable(); + } else { + this.windSpeedDirectionWidgetSettingsForm.get('minorTicksFont').disable(); + } + } + + private _centerValuePreviewFn(): string { + const centerValueDataKey = getDataKey(this.widgetConfig.config.datasources, 1); + if (centerValueDataKey) { + let units: string = this.widgetConfig.config.units; + let decimals: number = this.widgetConfig.config.decimals; + if (isDefinedAndNotNull(centerValueDataKey?.decimals)) { + decimals = centerValueDataKey.decimals; + } + if (centerValueDataKey?.units) { + units = centerValueDataKey.units; + } + return formatValue(25, decimals, units, true); + } else { + return '225°'; + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts index 2dae32a922..5d1bce1745 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts @@ -32,7 +32,6 @@ import { import { MarkdownWidgetSettingsComponent } from '@home/components/widget/lib/settings/cards/markdown-widget-settings.component'; -import { WidgetFontComponent } from '@home/components/widget/lib/settings/common/widget-font.component'; import { LabelWidgetLabelComponent } from '@home/components/widget/lib/settings/cards/label-widget-label.component'; import { LabelWidgetSettingsComponent } from '@home/components/widget/lib/settings/cards/label-widget-settings.component'; import { @@ -43,23 +42,22 @@ import { } from '@home/components/widget/lib/settings/cards/dashboard-state-widget-settings.component'; import { EntitiesHierarchyWidgetSettingsComponent -} from '@home/components/widget/lib/settings/cards/entities-hierarchy-widget-settings.component'; +} from '@home/components/widget/lib/settings/entity/entities-hierarchy-widget-settings.component'; import { HtmlCardWidgetSettingsComponent } from '@home/components/widget/lib/settings/cards/html-card-widget-settings.component'; import { EntitiesTableWidgetSettingsComponent -} from '@home/components/widget/lib/settings/cards/entities-table-widget-settings.component'; +} from '@home/components/widget/lib/settings/entity/entities-table-widget-settings.component'; import { EntitiesTableKeySettingsComponent -} from '@home/components/widget/lib/settings/cards/entities-table-key-settings.component'; +} from '@home/components/widget/lib/settings/entity/entities-table-key-settings.component'; import { AlarmsTableWidgetSettingsComponent } from '@home/components/widget/lib/settings/alarm/alarms-table-widget-settings.component'; import { AlarmsTableKeySettingsComponent } from '@home/components/widget/lib/settings/alarm/alarms-table-key-settings.component'; -import { GaugeHighlightComponent } from '@home/components/widget/lib/settings/gauge/gauge-highlight.component'; import { AnalogueRadialGaugeWidgetSettingsComponent } from '@home/components/widget/lib/settings/gauge/analogue-radial-gauge-widget-settings.component'; @@ -72,7 +70,6 @@ import { import { DigitalGaugeWidgetSettingsComponent } from '@home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component'; -import { ValueSourceComponent } from '@home/components/widget/lib/settings/common/value-source.component'; import { FixedColorLevelComponent } from '@home/components/widget/lib/settings/gauge/fixed-color-level.component'; import { TickValueComponent } from '@home/components/widget/lib/settings/gauge/tick-value.component'; import { FlotWidgetSettingsComponent } from '@home/components/widget/lib/settings/chart/flot-widget-settings.component'; @@ -259,13 +256,58 @@ import { import { TripAnimationPointSettingsComponent } from '@home/components/widget/lib/settings/map/trip-animation-point-settings.component'; +import { + GatewayLogsSettingsComponent +} from '@home/components/widget/lib/settings/gateway/gateway-logs-settings.component'; +import { + GatewayServiceRPCSettingsComponent +} from '@home/components/widget/lib/settings/gateway/gateway-service-rpc-settings.component'; import { DocLinksWidgetSettingsComponent } from '@home/components/widget/lib/settings/home-page/doc-links-widget-settings.component'; import { QuickLinksWidgetSettingsComponent } from '@home/components/widget/lib/settings/home-page/quick-links-widget-settings.component'; -import { LegendConfigComponent } from '@home/components/widget/lib/settings/common/legend-config.component'; +import { + ValueCardWidgetSettingsComponent +} from '@home/components/widget/lib/settings/cards/value-card-widget-settings.component'; +import { WidgetSettingsCommonModule } from '@home/components/widget/lib/settings/common/widget-settings-common.module'; +import { + AggregatedValueCardKeySettingsComponent +} from '@home/components/widget/lib/settings/cards/aggregated-value-card-key-settings.component'; +import { + AggregatedValueCardWidgetSettingsComponent +} from '@home/components/widget/lib/settings/cards/aggregated-value-card-widget-settings.component'; +import { + AlarmCountWidgetSettingsComponent +} from '@home/components/widget/lib/settings/alarm/alarm-count-widget-settings.component'; +import { + EntityCountWidgetSettingsComponent +} from '@home/components/widget/lib/settings/entity/entity-count-widget-settings.component'; +import { + BatteryLevelWidgetSettingsComponent +} from '@home/components/widget/lib/settings/indicator/battery-level-widget-settings.component'; +import { + WindSpeedDirectionWidgetSettingsComponent +} from '@home/components/widget/lib/settings/weather/wind-speed-direction-widget-settings.component'; +import { + SignalStrengthWidgetSettingsComponent +} from '@home/components/widget/lib/settings/indicator/signal-strength-widget-settings.component'; +import { + ValueChartCardWidgetSettingsComponent +} from '@home/components/widget/lib/settings/cards/value-chart-card-widget-settings.component'; +import { + ProgressBarWidgetSettingsComponent +} from '@home/components/widget/lib/settings/cards/progress-bar-widget-settings.component'; +import { + LiquidLevelCardWidgetSettingsComponent +} from '@home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component'; +import { + DoughnutWidgetSettingsComponent +} from '@home/components/widget/lib/settings/chart/doughnut-widget-settings.component'; +import { + RangeChartWidgetSettingsComponent +} from '@home/components/widget/lib/settings/chart/range-chart-widget-settings.component'; @NgModule({ declarations: [ @@ -274,7 +316,6 @@ import { LegendConfigComponent } from '@home/components/widget/lib/settings/comm TimeseriesTableKeySettingsComponent, TimeseriesTableLatestKeySettingsComponent, MarkdownWidgetSettingsComponent, - WidgetFontComponent, LabelWidgetLabelComponent, LabelWidgetSettingsComponent, SimpleCardWidgetSettingsComponent, @@ -285,13 +326,10 @@ import { LegendConfigComponent } from '@home/components/widget/lib/settings/comm EntitiesTableKeySettingsComponent, AlarmsTableWidgetSettingsComponent, AlarmsTableKeySettingsComponent, - GaugeHighlightComponent, AnalogueRadialGaugeWidgetSettingsComponent, AnalogueLinearGaugeWidgetSettingsComponent, AnalogueCompassWidgetSettingsComponent, DigitalGaugeWidgetSettingsComponent, - ValueSourceComponent, - LegendConfigComponent, FixedColorLevelComponent, TickValueComponent, FlotWidgetSettingsComponent, @@ -365,14 +403,30 @@ import { LegendConfigComponent } from '@home/components/widget/lib/settings/comm TripAnimationPointSettingsComponent, MapWidgetSettingsComponent, RouteMapWidgetSettingsComponent, + GatewayLogsSettingsComponent, + GatewayServiceRPCSettingsComponent, TripAnimationWidgetSettingsComponent, DocLinksWidgetSettingsComponent, - QuickLinksWidgetSettingsComponent + QuickLinksWidgetSettingsComponent, + ValueCardWidgetSettingsComponent, + AggregatedValueCardKeySettingsComponent, + AggregatedValueCardWidgetSettingsComponent, + AlarmCountWidgetSettingsComponent, + EntityCountWidgetSettingsComponent, + BatteryLevelWidgetSettingsComponent, + WindSpeedDirectionWidgetSettingsComponent, + SignalStrengthWidgetSettingsComponent, + ValueChartCardWidgetSettingsComponent, + ProgressBarWidgetSettingsComponent, + LiquidLevelCardWidgetSettingsComponent, + DoughnutWidgetSettingsComponent, + RangeChartWidgetSettingsComponent ], imports: [ CommonModule, SharedModule, - SharedHomeComponentsModule + SharedHomeComponentsModule, + WidgetSettingsCommonModule ], exports: [ QrCodeWidgetSettingsComponent, @@ -380,7 +434,6 @@ import { LegendConfigComponent } from '@home/components/widget/lib/settings/comm TimeseriesTableKeySettingsComponent, TimeseriesTableLatestKeySettingsComponent, MarkdownWidgetSettingsComponent, - WidgetFontComponent, LabelWidgetLabelComponent, LabelWidgetSettingsComponent, SimpleCardWidgetSettingsComponent, @@ -391,13 +444,10 @@ import { LegendConfigComponent } from '@home/components/widget/lib/settings/comm EntitiesTableKeySettingsComponent, AlarmsTableWidgetSettingsComponent, AlarmsTableKeySettingsComponent, - GaugeHighlightComponent, AnalogueRadialGaugeWidgetSettingsComponent, AnalogueLinearGaugeWidgetSettingsComponent, AnalogueCompassWidgetSettingsComponent, DigitalGaugeWidgetSettingsComponent, - ValueSourceComponent, - LegendConfigComponent, FixedColorLevelComponent, TickValueComponent, FlotWidgetSettingsComponent, @@ -471,9 +521,24 @@ import { LegendConfigComponent } from '@home/components/widget/lib/settings/comm TripAnimationPointSettingsComponent, MapWidgetSettingsComponent, RouteMapWidgetSettingsComponent, + GatewayLogsSettingsComponent, + GatewayServiceRPCSettingsComponent, TripAnimationWidgetSettingsComponent, DocLinksWidgetSettingsComponent, - QuickLinksWidgetSettingsComponent + QuickLinksWidgetSettingsComponent, + ValueCardWidgetSettingsComponent, + AggregatedValueCardKeySettingsComponent, + AggregatedValueCardWidgetSettingsComponent, + AlarmCountWidgetSettingsComponent, + EntityCountWidgetSettingsComponent, + BatteryLevelWidgetSettingsComponent, + WindSpeedDirectionWidgetSettingsComponent, + SignalStrengthWidgetSettingsComponent, + ValueChartCardWidgetSettingsComponent, + ProgressBarWidgetSettingsComponent, + LiquidLevelCardWidgetSettingsComponent, + DoughnutWidgetSettingsComponent, + RangeChartWidgetSettingsComponent ] }) export class WidgetSettingsModule { @@ -541,6 +606,21 @@ export const widgetSettingsComponentsMap: {[key: string]: Type  
-
+
@@ -99,7 +101,8 @@ - diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts index 9342a2dc4c..8b689b54da 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts @@ -56,9 +56,9 @@ import cssjs from '@core/css/css'; import { PageLink } from '@shared/models/page/page-link'; import { Direction, SortOrder, sortOrderFromString } from '@shared/models/page/sort-order'; import { CollectionViewer, DataSource } from '@angular/cdk/collections'; -import { BehaviorSubject, fromEvent, merge, Observable, of, Subscription } from 'rxjs'; +import { BehaviorSubject, fromEvent, merge, Observable, of, Subject, Subscription } from 'rxjs'; import { emptyPageData, PageData } from '@shared/models/page/page-data'; -import { catchError, debounceTime, distinctUntilChanged, map, tap } from 'rxjs/operators'; +import { catchError, debounceTime, distinctUntilChanged, map, skip, startWith, takeUntil } from 'rxjs/operators'; import { MatPaginator } from '@angular/material/paginator'; import { MatSort } from '@angular/material/sort'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; @@ -69,7 +69,9 @@ import { constructTableCssString, DisplayColumn, getCellContentInfo, - getCellStyleInfo, getColumnDefaultVisibility, getColumnSelectionAvailability, + getCellStyleInfo, + getColumnDefaultVisibility, + getColumnSelectionAvailability, getRowStyleInfo, getTableCellButtonActions, noDataMessage, @@ -79,7 +81,7 @@ import { TableWidgetDataKeySettings, TableWidgetSettings } from '@home/components/widget/lib/table-widget.models'; -import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; +import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; import { SubscriptionEntityInfo } from '@core/api/widget-api.models'; import { DatePipe } from '@angular/common'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; @@ -90,11 +92,15 @@ import { DisplayColumnsPanelComponent } from '@home/components/widget/lib/display-columns-panel.component'; import { ComponentPortal } from '@angular/cdk/portal'; +import { FormBuilder } from '@angular/forms'; +import { DEFAULT_OVERLAY_POSITIONS } from '@shared/models/overlay.models'; +import { DateFormatSettings } from '@shared/models/widget-settings.models'; export interface TimeseriesTableWidgetSettings extends TableWidgetSettings { showTimestamp: boolean; showMilliseconds: boolean; hideEmptyLines: boolean; + dateFormat: DateFormatSettings; } interface TimeseriesWidgetLatestDataKeySettings extends TableWidgetDataKeySettings { @@ -151,16 +157,19 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI @ViewChildren(MatPaginator) paginators: QueryList; @ViewChildren(MatSort) sorts: QueryList; + textSearch = this.fb.control('', {nonNullable: true}); + public displayPagination = true; public enableStickyHeader = true; public enableStickyAction = true; + public showCellActionsMenu = true; public pageSizeOptions; public textSearchMode = false; public hidePageSize = false; - public textSearch: string = null; public sources: TimeseriesTableSource[]; public sourceIndex: number; public noDataDisplayMessageText: string; + public hasRowAction: boolean; private setCellButtonAction: boolean; private cellContentCache: Array = []; @@ -187,6 +196,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI private subscriptions: Subscription[] = []; private widgetTimewindowChanged$: Subscription; private widgetResize$: ResizeObserver; + private destroy$ = new Subject(); private searchAction: WidgetAction = { name: 'action.search', @@ -214,7 +224,8 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI private translate: TranslateService, private domSanitizer: DomSanitizer, private datePipe: DatePipe, - private cd: ChangeDetectorRef) { + private cd: ChangeDetectorRef, + private fb: FormBuilder) { super(store); } @@ -260,24 +271,24 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI } ngAfterViewInit(): void { - fromEvent(this.searchInputField.nativeElement, 'keyup') - .pipe( - debounceTime(150), - distinctUntilChanged(), - tap(() => { - this.sources.forEach((source) => { - source.pageLink.textSearch = this.textSearch; - if (this.displayPagination) { - source.pageLink.page = 0; - } - }); - this.loadCurrentSourceRow(); - this.ctx.detectChanges(); - }) - ) - .subscribe(); + this.textSearch.valueChanges.pipe( + debounceTime(150), + startWith(''), + distinctUntilChanged((a: string, b: string) => a.trim() === b.trim()), + skip(1), + takeUntil(this.destroy$) + ).subscribe((textSearch) => { + this.sources.forEach((source) => { + source.pageLink.textSearch = textSearch.trim(); + if (this.displayPagination) { + source.pageLink.page = 0; + } + }); + this.loadCurrentSourceRow(); + this.ctx.detectChanges(); + }); - this.sorts.changes.subscribe(() => { + this.sorts.changes.pipe(takeUntil(this.destroy$)).subscribe(() => { this.initSubscriptionsToSortAndPaginator(); }); @@ -300,16 +311,23 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI this.ctx.widgetActions = [this.searchAction, this.columnDisplayAction]; this.setCellButtonAction = !!this.ctx.actionsApi.getActionDescriptors('actionCellButton').length; + this.hasRowAction = !!this.ctx.actionsApi.getActionDescriptors('rowClick').length; this.searchAction.show = isDefined(this.settings.enableSearch) ? this.settings.enableSearch : true; this.columnDisplayAction.show = isDefined(this.settings.enableSelectColumnDisplay) ? this.settings.enableSelectColumnDisplay : true; this.displayPagination = isDefined(this.settings.displayPagination) ? this.settings.displayPagination : true; this.enableStickyHeader = isDefined(this.settings.enableStickyHeader) ? this.settings.enableStickyHeader : true; this.enableStickyAction = isDefined(this.settings.enableStickyAction) ? this.settings.enableStickyAction : true; + this.showCellActionsMenu = isDefined(this.settings.showCellActionsMenu) ? this.settings.showCellActionsMenu : true; this.hideEmptyLines = isDefined(this.settings.hideEmptyLines) ? this.settings.hideEmptyLines : false; this.useEntityLabel = isDefined(this.widgetConfig.settings.useEntityLabel) ? this.widgetConfig.settings.useEntityLabel : false; this.showTimestamp = this.settings.showTimestamp !== false; - this.dateFormatFilter = (this.settings.showMilliseconds !== true) ? 'yyyy-MM-dd HH:mm:ss' : 'yyyy-MM-dd HH:mm:ss.SSS'; + // For backward compatibility + if (isDefined(this.settings?.showMilliseconds) && this.settings?.showMilliseconds) { + this.dateFormatFilter = 'yyyy-MM-dd HH:mm:ss.SSS'; + } else { + this.dateFormatFilter = isDefined(this.settings.dateFormat?.format) ? this.settings.dateFormat?.format : 'yyyy-MM-dd HH:mm:ss'; + } this.rowStylesInfo = getRowStyleInfo(this.settings, 'rowData, ctx'); @@ -388,9 +406,11 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI this.sources.push(source); } } - this.prepareDisplayedColumn(); - this.sources[this.sourceIndex].displayedColumns = - this.displayedColumns[this.sourceIndex].filter(value => value.display).map(value => value.def); + if (this.sources.length) { + this.prepareDisplayedColumn(); + this.sources[this.sourceIndex].displayedColumns = + this.displayedColumns[this.sourceIndex].filter(value => value.display).map(value => value.def); + } this.updateActiveEntityInfo(); } @@ -398,48 +418,58 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI if ($event) { $event.stopPropagation(); } - const target = $event.target || $event.currentTarget; - const config = new OverlayConfig(); - config.backdropClass = 'cdk-overlay-transparent-backdrop'; - config.hasBackdrop = true; - const connectedPosition: ConnectedPosition = { - originX: 'end', - originY: 'bottom', - overlayX: 'end', - overlayY: 'top' - }; - config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement) - .withPositions([connectedPosition]); - - const overlayRef = this.overlay.create(config); - overlayRef.backdropClick().subscribe(() => { - overlayRef.dispose(); - }); - const source = this.sources[this.sourceIndex]; + if (this.sources.length) { + const target = $event.target || $event.srcElement || $event.currentTarget; + const config = new OverlayConfig({ + panelClass: 'tb-panel-container', + backdropClass: 'cdk-overlay-transparent-backdrop', + hasBackdrop: true, + height: 'fit-content', + maxHeight: '75vh' + }); + config.positionStrategy = this.overlay.position() + .flexibleConnectedTo(target as HTMLElement) + .withPositions(DEFAULT_OVERLAY_POSITIONS); - this.prepareDisplayedColumn(); + const overlayRef = this.overlay.create(config); + overlayRef.backdropClick().subscribe(() => { + overlayRef.dispose(); + }); + + const source = this.sources[this.sourceIndex]; - const providers: StaticProvider[] = [ - { - provide: DISPLAY_COLUMNS_PANEL_DATA, - useValue: { - columns: this.displayedColumns[this.sourceIndex], - columnsUpdated: (newColumns) => { - source.displayedColumns = newColumns.filter(value => value.display).map(value => value.def); - this.clearCache(); + this.prepareDisplayedColumn(); + + const providers: StaticProvider[] = [ + { + provide: DISPLAY_COLUMNS_PANEL_DATA, + useValue: { + columns: this.displayedColumns[this.sourceIndex], + columnsUpdated: (newColumns) => { + source.displayedColumns = newColumns.filter(value => value.display).map(value => value.def); + this.clearCache(); + } } + }, + { + provide: OverlayRef, + useValue: overlayRef } - }, - { - provide: OverlayRef, - useValue: overlayRef - } - ]; + ]; - const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); - overlayRef.attach(new ComponentPortal(DisplayColumnsPanelComponent, - this.viewContainerRef, injector)); - this.ctx.detectChanges(); + const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); + const componentRef = overlayRef.attach(new ComponentPortal(DisplayColumnsPanelComponent, + this.viewContainerRef, injector)); + + const resizeWindows$ = fromEvent(window, 'resize').subscribe(() => { + overlayRef.updatePosition(); + }); + componentRef.onDestroy(() => { + resizeWindows$.unsubscribe(); + }); + + this.ctx.detectChanges(); + } } private prepareDisplayedColumn() { @@ -452,7 +482,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI } else if (value === 'actions') { title = 'Actions'; } else { - title = header.dataKey.name; + title = header.dataKey.label; } return { title, @@ -551,9 +581,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI observables.push(paginator.page); } this.updateData(sort, paginator); - this.subscriptions.push(merge(...observables).pipe( - tap(() => this.updateData(sort, paginator)) - ).subscribe()); + this.subscriptions.push(merge(...observables).subscribe(() => this.updateData(sort, paginator))); }); } @@ -565,10 +593,6 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI private enterFilterMode() { this.textSearchMode = true; - this.textSearch = ''; - this.sources.forEach((source) => { - source.pageLink.textSearch = this.textSearch; - }); this.ctx.hideTitlePanel = true; this.ctx.detectChanges(true); setTimeout(() => { @@ -579,13 +603,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI exitFilterMode() { this.textSearchMode = false; - this.textSearch = null; - this.sources.forEach((source) => { - source.pageLink.textSearch = this.textSearch; - if (this.displayPagination) { - source.pageLink.page = 0; - } - }); + this.textSearch.reset(); this.loadCurrentSourceRow(); this.ctx.hideTitlePanel = false; this.ctx.detectChanges(true); @@ -868,7 +886,7 @@ class TimeseriesDatasource implements DataSource { const rowsMap: {[timestamp: number]: TimeseriesRow} = {}; for (let d = 0; d < data.length; d++) { const columnData = data[d].data; - columnData.forEach((cellData, index) => { + columnData.forEach((cellData) => { const timestamp = cellData[0]; let row = rowsMap[timestamp]; if (!row) { @@ -877,7 +895,7 @@ class TimeseriesDatasource implements DataSource { }; if (this.cellButtonActions.length) { if (this.usedShowCellActionFunction) { - const parsedData = formattedDataFormDatasourceData(data, index); + const parsedData = formattedDataFormDatasourceData(data, undefined, timestamp); row.actionCellButtons = prepareTableCellButtonActions(this.widgetContext, this.cellButtonActions, parsedData[0], this.reserveSpaceForHiddenAction); row.hasActions = checkHasActions(row.actionCellButtons); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/trip-animation/trip-animation.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/trip-animation/trip-animation.component.scss index d379c9ff8a..4118a26800 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/trip-animation/trip-animation.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/trip-animation/trip-animation.component.scss @@ -54,7 +54,7 @@ line-height: 24px; z-index: 999; - mat-icon { + .mat-icon { width: 24px; height: 24px; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/weather/wind-speed-direction-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/weather/wind-speed-direction-widget.component.html new file mode 100644 index 0000000000..425a2cd172 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/weather/wind-speed-direction-widget.component.html @@ -0,0 +1,25 @@ + +
+
+ +
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/weather/wind-speed-direction-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/weather/wind-speed-direction-widget.component.scss new file mode 100644 index 0000000000..eda98fe6ba --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/weather/wind-speed-direction-widget.component.scss @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.tb-wind-speed-direction-panel { + width: 100%; + height: 100%; + position: relative; + display: flex; + flex-direction: column; + gap: 16px; + padding: 20px 24px 24px 24px; + &.tb-wind-speed-direction-pointer { + cursor: pointer; + } + > div:not(.tb-wind-speed-direction-overlay) { + z-index: 1; + } + .tb-wind-speed-direction-overlay { + position: absolute; + top: 12px; + left: 12px; + bottom: 12px; + right: 12px; + } + div.tb-widget-title { + padding: 0; + } + .tb-wind-speed-direction-content { + flex: 1; + min-width: 0; + min-height: 0; + .tb-wind-speed-direction-shape { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/weather/wind-speed-direction-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/weather/wind-speed-direction-widget.component.ts new file mode 100644 index 0000000000..be836dba17 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/weather/wind-speed-direction-widget.component.ts @@ -0,0 +1,339 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + Input, + OnDestroy, + OnInit, + Renderer2, + TemplateRef, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { + windSpeedDirectionDefaultSettings, + WindSpeedDirectionLayout, + WindSpeedDirectionWidgetSettings +} from '@home/components/widget/lib/weather/wind-speed-direction-widget.models'; +import { + backgroundStyle, + ColorProcessor, + ComponentStyle, + Font, + getDataKey, + getSingleTsValueByDataKey, + overlayStyle +} from '@shared/models/widget-settings.models'; +import { WidgetComponent } from '@home/components/widget/widget.component'; +import { formatValue, isDefinedAndNotNull, isNumeric } from '@core/utils'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { Path, Svg, SVG, Text } from '@svgdotjs/svg.js'; +import { DataKey } from '@shared/models/widget.models'; +import { Observable } from 'rxjs'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { DomSanitizer } from '@angular/platform-browser'; + +const shapeSize = 180; +const cx = shapeSize / 2; +const cy = shapeSize / 2; +const ticksDiameter = 140; + +const ticksTextMap: {[angle: number]: string} = { + 0: 'N', + 45: 'NE', + 90: 'E', + 135: 'SE', + 180: 'S', + 225: 'SW', + 270: 'W', + 315: 'NW' +}; + +@Component({ + selector: 'tb-wind-speed-direction-widget', + templateUrl: './wind-speed-direction-widget.component.html', + styleUrls: ['./wind-speed-direction-widget.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class WindSpeedDirectionWidgetComponent implements OnInit, OnDestroy, AfterViewInit { + + @ViewChild('windSpeedDirectionShape', {static: false}) + windSpeedDirectionShape: ElementRef; + + settings: WindSpeedDirectionWidgetSettings; + + @Input() + ctx: WidgetContext; + + @Input() + widgetTitlePanel: TemplateRef; + + layout: WindSpeedDirectionLayout; + + centerValueColor: ColorProcessor; + + backgroundStyle$: Observable; + overlayStyle: ComponentStyle = {}; + + shapeResize$: ResizeObserver; + + hasCardClickAction = false; + + private decimals = 0; + private units = ''; + + private drawSvgShapePending = false; + private svgShape: Svg; + private arrow: Path; + private centerValueTextNode: Text; + + private windDirectionDataKey: DataKey; + private centerValueDataKey: DataKey; + + private windDirection = 0; + private centerValueText = 'N/A'; + + constructor(private widgetComponent: WidgetComponent, + private imagePipe: ImagePipe, + private sanitizer: DomSanitizer, + private renderer: Renderer2, + private cd: ChangeDetectorRef) { + } + + ngOnInit(): void { + this.ctx.$scope.windSpeedDirectionWidget = this; + this.settings = {...windSpeedDirectionDefaultSettings, ...this.ctx.settings}; + + this.windDirectionDataKey = getDataKey(this.ctx.datasources, 0); + this.centerValueDataKey = getDataKey(this.ctx.datasources, 1); + + if (this.centerValueDataKey) { + this.decimals = this.ctx.decimals; + this.units = this.ctx.units; + if (isDefinedAndNotNull(this.centerValueDataKey.decimals)) { + this.decimals = this.centerValueDataKey.decimals; + } + if (this.centerValueDataKey.units) { + this.units = this.centerValueDataKey.units; + } + } + + this.layout = this.settings.layout; + + this.centerValueColor = ColorProcessor.fromSettings(this.settings.centerValueColor); + + this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); + this.overlayStyle = overlayStyle(this.settings.background.overlay); + + this.hasCardClickAction = this.ctx.actionsApi.getActionDescriptors('cardClick').length > 0; + } + + ngAfterViewInit() { + if (this.drawSvgShapePending) { + this.drawSvg(); + } + } + + ngOnDestroy() { + if (this.shapeResize$) { + this.shapeResize$.disconnect(); + } + } + + public onInit() { + const borderRadius = this.ctx.$widgetElement.css('borderRadius'); + this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; + if (this.windSpeedDirectionShape) { + this.drawSvg(); + } else { + this.drawSvgShapePending = true; + } + this.cd.detectChanges(); + } + + public onDataUpdated() { + let value = 0; + this.windDirection = 0; + this.centerValueText = 'N/A'; + if (this.windDirectionDataKey) { + const windDirectionTsValue = getSingleTsValueByDataKey(this.ctx.data, this.windDirectionDataKey); + if (windDirectionTsValue && isDefinedAndNotNull(windDirectionTsValue[1]) && isNumeric(windDirectionTsValue[1])) { + this.windDirection = windDirectionTsValue[1]; + if (!this.centerValueDataKey) { + value = this.windDirection; + this.centerValueText = formatValue(value, 0, '', false) + '°'; + } + } + } + if (this.centerValueDataKey) { + const centerValueTsValue = getSingleTsValueByDataKey(this.ctx.data, this.centerValueDataKey); + if (centerValueTsValue && isDefinedAndNotNull(centerValueTsValue[1]) && isNumeric(centerValueTsValue[1])) { + value = centerValueTsValue[1]; + this.centerValueText = formatValue(value, this.decimals, '', false); + } + } + this.centerValueColor.update(value); + this.renderValues(); + } + + public cardClick($event: Event) { + this.ctx.actionsApi.cardClick($event); + } + + private drawSvg() { + this.svgShape = SVG().addTo(this.windSpeedDirectionShape.nativeElement).size(shapeSize, shapeSize); + this.renderer.setStyle(this.svgShape.node, 'overflow', 'visible'); + this.renderer.setStyle(this.svgShape.node, 'user-select', 'none'); + + // Draw ticks + + const ticksYStart = (shapeSize - ticksDiameter) / 2; + for (let i = 0; i < 360; i += 3) { + if (i !== 0) { + let color: string; + let width: number; + let height: number; + if (i % 90 === 0) { + // Major ticks + color = this.settings.majorTicksColor; + width = 2; + height = 8; + } else if (i % 45 === 0) { + // Minor ticks + color = this.settings.minorTicksColor; + width = 2; + height = 8; + } else { + color = this.settings.ticksColor; + width = 1.2; + height = 3; + } + this.svgShape.line(cx, ticksYStart, cx, ticksYStart + height).attr({ + 'stroke-width': width, + stroke: color + }).rotate(i, cx, cy); + } + } + + // Draw pointer + this.svgShape.path('m 89.152,20.470002 c 0.3917,-0.626669 1.3043,-0.626669 1.696,0 l 3.1958,5.1132 ' + + 'c 0.4162,0.66605 -0.0626,1.53 -0.848,1.53 h -6.3916 c -0.7854,0 -1.2642,-0.86395 -0.848,-1.53 z') + .fill(this.settings.majorTicksColor); + + let x: number; + let y: number; + let degree: number; + + const drawMajorTicksText = [ WindSpeedDirectionLayout.default, WindSpeedDirectionLayout.advanced ].includes(this.settings.layout); + const drawMinorTicksText = this.settings.layout === WindSpeedDirectionLayout.advanced; + + if (drawMajorTicksText) { + // Draw major ticks text + for (let i = 0; i < 4; i += 1) { + degree = i * 90; + if (i % 2 === 0) { + x = cx; + y = i === 0 ? 10 : shapeSize - 10; + } else { + y = cy; + x = i === 3 ? 10 : shapeSize - 10; + } + this.drawTickText(degree, this.settings.majorTicksFont, this.settings.majorTicksColor, x, y); + } + } + + if (drawMinorTicksText) { + // Draw minor ticks text + for (let i = 0; i < 4; i += 1) { + degree = 45 + (i * 90); + if (i < 2) { + x = shapeSize - 30; + y = i === 0 ? 30 : shapeSize - 30; + } else { + x = 30; + y = i === 3 ? 30 : shapeSize - 30; + } + this.drawTickText(degree, this.settings.minorTicksFont, this.settings.minorTicksColor, x, y); + } + } + + // Draw arrow + this.arrow = this.svgShape.path('m 89.263587,23.438382 c 0.388942,-0.392146 1.022181,-0.388549 1.414649,0 ' + + 'l 6.389758,6.389 c 0.392414,0.388462 0.394911,1.022828 0.0059,1.415 -0.388987,0.392109 -1.022226,0.383311 -1.41408,-0.006 ' + + 'l -4.6762,-4.676 v 28.417 h -2 v -28.417 l -4.637642,4.676 ' + + 'c -0.388878,0.392069 -1.022053,0.394895 -1.414202,0.006 -0.392082,-0.388967 -0.394683,-1.022852 -0.0057,-1.415 ' + + 'z M 88.983614,154.85438 h -2.217 v 2 h 6.434 v -2 h -2.217 v -29.939 h -2 z').fill(this.settings.arrowColor); + + // Draw value + this.centerValueTextNode = this.svgShape.text('').font({ + family: this.settings.centerValueFont.family, + weight: this.settings.centerValueFont.weight, + style: this.settings.centerValueFont.style + }).attr({x: '50%', y: '50%', 'text-anchor': 'middle'}); + if (!this.units) { + this.centerValueTextNode.attr({'dominant-baseline': 'middle'}); + } + + this.shapeResize$ = new ResizeObserver(() => { + this.onResize(); + }); + this.shapeResize$.observe(this.windSpeedDirectionShape.nativeElement); + this.onResize(); + + this.renderValues(); + } + + private drawTickText(degree: number, font: Font, color: string, x: number, y: number) { + const tickText = this.settings.directionalNamesElseDegrees ? ticksTextMap[degree] : degree + ''; + this.svgShape.text(tickText).font({ + family: font.family, + weight: font.weight, + style: font.style, + size: this.settings.directionalNamesElseDegrees ? '14px' : '10px' + }).fill(color).center(x, y); + } + + private renderValues() { + if (this.svgShape) { + this.arrow.timeline().finish(); + this.arrow.animate(800).transform({rotate: this.windDirection}); + this.renderCenterValueText(); + } + } + + private renderCenterValueText() { + this.centerValueTextNode.text(add => { + add.tspan(this.centerValueText).font({size: '24px'}); + if (this.units) { + add.tspan(this.units).newLine().font({size: '14px'}); + } + }).fill(this.centerValueColor.color); + } + + private onResize() { + const shapeWidth = this.windSpeedDirectionShape.nativeElement.getBoundingClientRect().width; + const shapeHeight = this.windSpeedDirectionShape.nativeElement.getBoundingClientRect().height; + const size = Math.min(shapeWidth, shapeHeight); + const scale = size / shapeSize; + this.renderer.setStyle(this.svgShape.node, 'transform', `scale(${scale})`); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/weather/wind-speed-direction-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/weather/wind-speed-direction-widget.models.ts new file mode 100644 index 0000000000..6359696f25 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/weather/wind-speed-direction-widget.models.ts @@ -0,0 +1,105 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { BatteryLevelLayout } from '@home/components/widget/lib/indicator/battery-level-widget.models'; +import { + BackgroundSettings, + BackgroundType, + ColorSettings, + constantColor, + Font +} from '@shared/models/widget-settings.models'; + +export enum WindSpeedDirectionLayout { + default = 'default', + advanced = 'advanced', + simplified = 'simplified' +} + +export const windSpeedDirectionLayouts = Object.keys(WindSpeedDirectionLayout) as WindSpeedDirectionLayout[]; + +export const windSpeedDirectionLayoutTranslations = new Map( + [ + [WindSpeedDirectionLayout.default, 'widgets.wind-speed-direction.layout-default'], + [WindSpeedDirectionLayout.advanced, 'widgets.wind-speed-direction.layout-advanced'], + [WindSpeedDirectionLayout.simplified, 'widgets.wind-speed-direction.layout-simplified'] + ] +); + +export const windSpeedDirectionLayoutImages = new Map( + [ + [WindSpeedDirectionLayout.default, 'assets/widget/wind-speed-direction/default-layout.svg'], + [WindSpeedDirectionLayout.advanced, 'assets/widget/wind-speed-direction/advanced-layout.svg'], + [WindSpeedDirectionLayout.simplified, 'assets/widget/wind-speed-direction/simplified-layout.svg'] + ] +); + +export interface WindSpeedDirectionWidgetSettings { + layout: WindSpeedDirectionLayout; + centerValueFont: Font; + centerValueColor: ColorSettings; + ticksColor: string; + arrowColor: string; + directionalNamesElseDegrees: boolean; + majorTicksColor: string; + majorTicksFont: Font; + minorTicksColor: string; + minorTicksFont: Font; + background: BackgroundSettings; +} + +export const windSpeedDirectionDefaultSettings: WindSpeedDirectionWidgetSettings = { + layout: WindSpeedDirectionLayout.default, + centerValueFont: { + family: 'Roboto', + size: 24, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '32px' + }, + centerValueColor: constantColor('rgba(0, 0, 0, 0.87)'), + ticksColor: 'rgba(0, 0, 0, 0.12)', + arrowColor: 'rgba(0, 0, 0, 0.87)', + directionalNamesElseDegrees: true, + majorTicksColor: 'rgba(158, 158, 158, 1)', + majorTicksFont: { + family: 'Roboto', + size: 14, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '20px' + }, + minorTicksColor: 'rgba(0, 0, 0, 0.12)', + minorTicksFont: { + family: 'Roboto', + size: 14, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '20px' + }, + background: { + type: BackgroundType.color, + color: '#fff', + overlay: { + enabled: false, + color: 'rgba(255,255,255,0.72)', + blur: 3 + } + } +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts index e3ff270197..c29df7f243 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts @@ -29,7 +29,13 @@ import { import cssjs from '@core/css/css'; import { UtilsService } from '@core/services/utils.service'; import { ModulesWithFactories, ResourcesService } from '@core/services/resources.service'; -import { Widget, widgetActionSources, WidgetControllerDescriptor, WidgetType } from '@shared/models/widget.models'; +import { + IWidgetSettingsComponent, + Widget, + widgetActionSources, + WidgetControllerDescriptor, + WidgetType +} from '@shared/models/widget.models'; import { catchError, map, mergeMap, switchMap, tap } from 'rxjs/operators'; import { isFunction, isUndefined } from '@core/utils'; import { TranslateService } from '@ngx-translate/core'; @@ -47,6 +53,8 @@ import moment from 'moment'; import { IModulesMap } from '@modules/common/modules-map.models'; import { HOME_COMPONENTS_MODULE_TOKEN } from '@home/components/tokens'; import { widgetSettingsComponentsMap } from '@home/components/widget/lib/settings/widget-settings.module'; +import { basicWidgetConfigComponentsMap } from '@home/components/widget/config/basic/basic-widget-config.module'; +import { IBasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; @Injectable() export class WidgetComponentService { @@ -85,7 +93,8 @@ export class WidgetComponentService { this.editingWidgetType = toWidgetType( { widgetName: this.utils.editWidgetInfo.widgetName, - alias: 'customWidget', + fullFqn: 'system.customWidget', + deprecated: false, type: this.utils.editWidgetInfo.type, sizeX: this.utils.editWidgetInfo.sizeX, sizeY: this.utils.editWidgetInfo.sizeY, @@ -102,7 +111,7 @@ export class WidgetComponentService { hasBasicMode: this.utils.editWidgetInfo.hasBasicMode, basicModeDirective: this.utils.editWidgetInfo.basicModeDirective, defaultConfig: this.utils.editWidgetInfo.defaultConfig - }, new WidgetTypeId('1'), new TenantId( NULL_UUID ), 'customWidgetBundle', undefined + }, new WidgetTypeId('1'), new TenantId( NULL_UUID ), undefined ); } const initSubject = new ReplaySubject(); @@ -212,7 +221,7 @@ export class WidgetComponentService { } public getInstantWidgetInfo(widget: Widget): WidgetInfo { - const widgetInfo = this.widgetService.getWidgetInfoFromCache(widget.bundleAlias, widget.typeAlias, widget.isSystemType); + const widgetInfo = this.widgetService.getWidgetInfoFromCache(widget.typeFullFqn); if (widgetInfo) { return widgetInfo; } else { @@ -220,42 +229,41 @@ export class WidgetComponentService { } } - public getWidgetInfo(bundleAlias: string, widgetTypeAlias: string, isSystem: boolean): Observable { + public getWidgetInfo(fullFqn: string): Observable { return this.init().pipe( - mergeMap(() => this.getWidgetInfoInternal(bundleAlias, widgetTypeAlias, isSystem)) + mergeMap(() => this.getWidgetInfoInternal(fullFqn)) ); } - public clearWidgetInfo(widgetInfo: WidgetInfo, bundleAlias: string, widgetTypeAlias: string, isSystem: boolean): void { - this.dynamicComponentFactoryService.destroyDynamicComponentFactory(widgetInfo.componentFactory); - this.widgetService.deleteWidgetInfoFromCache(bundleAlias, widgetTypeAlias, isSystem); + public clearWidgetInfo(widgetInfo: WidgetInfo): void { + this.dynamicComponentFactoryService.destroyDynamicComponent(widgetInfo.componentType); + this.widgetService.deleteWidgetInfoFromCache(widgetInfo.fullFqn); } - private getWidgetInfoInternal(bundleAlias: string, widgetTypeAlias: string, isSystem: boolean): Observable { + private getWidgetInfoInternal(fullFqn: string): Observable { const widgetInfoSubject = new ReplaySubject(); - const widgetInfo = this.widgetService.getWidgetInfoFromCache(bundleAlias, widgetTypeAlias, isSystem); + const widgetInfo = this.widgetService.getWidgetInfoFromCache(fullFqn); if (widgetInfo) { widgetInfoSubject.next(widgetInfo); widgetInfoSubject.complete(); } else { if (this.utils.widgetEditMode) { - this.loadWidget(this.editingWidgetType, bundleAlias, isSystem, widgetInfoSubject); + this.loadWidget(this.editingWidgetType, widgetInfoSubject); } else { - const key = this.widgetService.createWidgetInfoCacheKey(bundleAlias, widgetTypeAlias, isSystem); - let fetchQueue = this.widgetsInfoFetchQueue.get(key); + let fetchQueue = this.widgetsInfoFetchQueue.get(fullFqn); if (fetchQueue) { fetchQueue.push(widgetInfoSubject); } else { fetchQueue = new Array>(); - this.widgetsInfoFetchQueue.set(key, fetchQueue); - this.widgetService.getWidgetType(bundleAlias, widgetTypeAlias, isSystem, {ignoreErrors: true}).subscribe( + this.widgetsInfoFetchQueue.set(fullFqn, fetchQueue); + this.widgetService.getWidgetType(fullFqn, {ignoreErrors: true}).subscribe( (widgetType) => { - this.loadWidget(widgetType, bundleAlias, isSystem, widgetInfoSubject); + this.loadWidget(widgetType, widgetInfoSubject); }, () => { widgetInfoSubject.next(this.missingWidgetType); widgetInfoSubject.complete(); - this.resolveWidgetsInfoFetchQueue(key, this.missingWidgetType); + this.resolveWidgetsInfoFetchQueue(fullFqn, this.missingWidgetType); } ); } @@ -264,19 +272,18 @@ export class WidgetComponentService { return widgetInfoSubject.asObservable(); } - private loadWidget(widgetType: WidgetType, bundleAlias: string, isSystem: boolean, widgetInfoSubject: Subject) { + private loadWidget(widgetType: WidgetType, widgetInfoSubject: Subject) { const widgetInfo = toWidgetInfo(widgetType); - const key = this.widgetService.createWidgetInfoCacheKey(bundleAlias, widgetInfo.alias, isSystem); let widgetControllerDescriptor: WidgetControllerDescriptor = null; try { - widgetControllerDescriptor = this.createWidgetControllerDescriptor(widgetInfo, key); + widgetControllerDescriptor = this.createWidgetControllerDescriptor(widgetInfo); } catch (e) { const details = this.utils.parseException(e); const errorMessage = `Failed to compile widget script. \n Error: ${details.message}`; - this.processWidgetLoadError([errorMessage], key, widgetInfoSubject); + this.processWidgetLoadError([errorMessage], widgetInfo.fullFqn, widgetInfoSubject); } if (widgetControllerDescriptor) { - const widgetNamespace = `widget-type-${(isSystem ? 'sys-' : '')}${bundleAlias}-${widgetInfo.alias}`; + const widgetNamespace = `widget-type-${widgetInfo.fullFqn.replace(/\./g, '-')}`; this.loadWidgetResources(widgetInfo, widgetNamespace, [SharedModule, WidgetComponentsModule, this.homeComponentsModule]).subscribe( () => { if (widgetControllerDescriptor.settingsSchema) { @@ -291,15 +298,15 @@ export class WidgetComponentService { widgetInfo.typeParameters = widgetControllerDescriptor.typeParameters; widgetInfo.actionSources = widgetControllerDescriptor.actionSources; widgetInfo.widgetTypeFunction = widgetControllerDescriptor.widgetTypeFunction; - this.widgetService.putWidgetInfoToCache(widgetInfo, bundleAlias, widgetInfo.alias, isSystem); + this.widgetService.putWidgetInfoToCache(widgetInfo); if (widgetInfoSubject) { widgetInfoSubject.next(widgetInfo); widgetInfoSubject.complete(); } - this.resolveWidgetsInfoFetchQueue(key, widgetInfo); + this.resolveWidgetsInfoFetchQueue(widgetInfo.fullFqn, widgetInfo); }, (errorMessages: string[]) => { - this.processWidgetLoadError(errorMessages, key, widgetInfoSubject); + this.processWidgetLoadError(errorMessages, widgetInfo.fullFqn, widgetInfoSubject); } ); } @@ -362,13 +369,14 @@ export class WidgetComponentService { return of(resolvedModules); } else { this.registerWidgetSettingsForms(widgetInfo, resolvedModules.factories); - return this.dynamicComponentFactoryService.createDynamicComponentFactory( + return this.dynamicComponentFactoryService.createDynamicComponent( class DynamicWidgetComponentInstance extends DynamicWidgetComponent {}, widgetInfo.templateHtml, resolvedModules.modules ).pipe( - map((factory) => { - widgetInfo.componentFactory = factory; + map((componentData) => { + widgetInfo.componentType = componentData.componentType; + widgetInfo.componentModuleRef = componentData.componentModuleRef; return null; }), catchError(e => { @@ -397,6 +405,7 @@ export class WidgetComponentService { private registerWidgetSettingsForms(widgetInfo: WidgetInfo, factories: ComponentFactory[]) { const directives: string[] = []; + const basicDirectives: string[] = []; if (widgetInfo.settingsDirective && widgetInfo.settingsDirective.length) { directives.push(widgetInfo.settingsDirective); } @@ -407,18 +416,25 @@ export class WidgetComponentService { directives.push(widgetInfo.latestDataKeySettingsDirective); } if (widgetInfo.basicModeDirective && widgetInfo.basicModeDirective.length) { - directives.push(widgetInfo.basicModeDirective); + basicDirectives.push(widgetInfo.basicModeDirective); } + + this.expandSettingComponentMap(widgetSettingsComponentsMap, directives, factories); + this.expandSettingComponentMap(basicWidgetConfigComponentsMap, basicDirectives, factories); + } + + private expandSettingComponentMap(settingsComponentsMap: {[key: string]: Type}, + directives: string[], factories: ComponentFactory[]): void { if (directives.length) { factories.filter((factory) => directives.includes(factory.selector)) .forEach((foundFactory) => { - widgetSettingsComponentsMap[foundFactory.selector] = foundFactory.componentType; + settingsComponentsMap[foundFactory.selector] = foundFactory.componentType; }); } } - private createWidgetControllerDescriptor(widgetInfo: WidgetInfo, name: string): WidgetControllerDescriptor { - let widgetTypeFunctionBody = `return function _${name} (ctx) {\n` + + private createWidgetControllerDescriptor(widgetInfo: WidgetInfo): WidgetControllerDescriptor { + let widgetTypeFunctionBody = `return function _${widgetInfo.fullFqn.replace(/\./g, '_')} (ctx) {\n` + ' var self = this;\n' + ' self.ctx = ctx;\n\n'; /*+ @@ -540,6 +556,24 @@ export class WidgetComponentService { if (isUndefined(result.typeParameters.processNoDataByWidget)) { result.typeParameters.processNoDataByWidget = false; } + if (isUndefined(result.typeParameters.previewWidth)) { + result.typeParameters.previewWidth = '100%'; + } + if (isUndefined(result.typeParameters.previewHeight)) { + result.typeParameters.previewHeight = '70%'; + } + if (isUndefined(result.typeParameters.embedTitlePanel)) { + result.typeParameters.embedTitlePanel = false; + } + if (isUndefined(result.typeParameters.hideDataSettings)) { + result.typeParameters.hideDataSettings = false; + } + if (!isFunction(result.typeParameters.defaultDataKeysFunction)) { + result.typeParameters.defaultDataKeysFunction = null; + } + if (!isFunction(result.typeParameters.defaultLatestDataKeysFunction)) { + result.typeParameters.defaultLatestDataKeysFunction = null; + } if (isFunction(widgetTypeInstance.actionSources)) { result.actionSources = widgetTypeInstance.actionSources(); } else { @@ -556,18 +590,18 @@ export class WidgetComponentService { } } - private processWidgetLoadError(errorMessages: string[], cacheKey: string, widgetInfoSubject: Subject) { + private processWidgetLoadError(errorMessages: string[], fullFqn: string, widgetInfoSubject: Subject) { if (widgetInfoSubject) { widgetInfoSubject.error({ widgetInfo: this.errorWidgetType, errorMessages }); } - this.resolveWidgetsInfoFetchQueue(cacheKey, this.errorWidgetType, errorMessages); + this.resolveWidgetsInfoFetchQueue(fullFqn, this.errorWidgetType, errorMessages); } - private resolveWidgetsInfoFetchQueue(key: string, widgetInfo: WidgetInfo, errorMessages?: string[]) { - const fetchQueue = this.widgetsInfoFetchQueue.get(key); + private resolveWidgetsInfoFetchQueue(fullFqn: string, widgetInfo: WidgetInfo, errorMessages?: string[]) { + const fetchQueue = this.widgetsInfoFetchQueue.get(fullFqn); if (fetchQueue) { fetchQueue.forEach(subject => { if (!errorMessages) { @@ -580,7 +614,7 @@ export class WidgetComponentService { }); } }); - this.widgetsInfoFetchQueue.delete(key); + this.widgetsInfoFetchQueue.delete(fullFqn); } } } diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts index ce2e7055cc..6679ed0cca 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts @@ -17,12 +17,12 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { SharedModule } from '@app/shared/shared.module'; -import { EntitiesTableWidgetComponent } from '@home/components/widget/lib/entities-table-widget.component'; +import { EntitiesTableWidgetComponent } from '@home/components/widget/lib/entity/entities-table-widget.component'; import { DisplayColumnsPanelComponent } from '@home/components/widget/lib/display-columns-panel.component'; -import { AlarmsTableWidgetComponent } from '@home/components/widget/lib/alarms-table-widget.component'; +import { AlarmsTableWidgetComponent } from '@home/components/widget/lib/alarm/alarms-table-widget.component'; import { SharedHomeComponentsModule } from '@home/components/shared-home-components.module'; import { TimeseriesTableWidgetComponent } from '@home/components/widget/lib/timeseries-table-widget.component'; -import { EntitiesHierarchyWidgetComponent } from '@home/components/widget/lib/entities-hierarchy-widget.component'; +import { EntitiesHierarchyWidgetComponent } from '@home/components/widget/lib/entity/entities-hierarchy-widget.component'; import { RpcWidgetsModule } from '@home/components/widget/lib/rpc/rpc-widgets.module'; import { DateRangeNavigatorPanelComponent, @@ -43,6 +43,30 @@ import { HomePageWidgetsModule } from '@home/components/widget/lib/home-page/hom import { WIDGET_COMPONENTS_MODULE_TOKEN } from '@home/components/tokens'; import { FlotWidgetComponent } from '@home/components/widget/lib/flot-widget.component'; import { LegendComponent } from '@home/components/widget/lib/legend.component'; +import { GatewayConnectorComponent } from '@home/components/widget/lib/gateway/gateway-connectors.component'; +import { GatewayLogsComponent } from '@home/components/widget/lib/gateway/gateway-logs.component'; +import { GatewayStatisticsComponent } from '@home/components/widget/lib/gateway/gateway-statistics.component'; +import { GatewayServiceRPCComponent } from '@home/components/widget/lib/gateway/gateway-service-rpc.component'; +import { DeviceGatewayCommandComponent } from '@home/components/widget/lib/gateway/device-gateway-command.component'; +import { GatewayConfigurationComponent } from '@home/components/widget/lib/gateway/gateway-configuration.component'; +import { + GatewayRemoteConfigurationDialogComponent +} from '@home/components/widget/lib/gateway/gateway-remote-configuration-dialog'; +import { ValueCardWidgetComponent } from '@home/components/widget/lib/cards/value-card-widget.component'; +import { + AggregatedValueCardWidgetComponent +} from '@home/components/widget/lib/cards/aggregated-value-card-widget.component'; +import { CountWidgetComponent } from '@home/components/widget/lib/count/count-widget.component'; +import { BatteryLevelWidgetComponent } from '@home/components/widget/lib/indicator/battery-level-widget.component'; +import { + WindSpeedDirectionWidgetComponent +} from '@home/components/widget/lib/weather/wind-speed-direction-widget.component'; +import { SignalStrengthWidgetComponent } from '@home/components/widget/lib/indicator/signal-strength-widget.component'; +import { ValueChartCardWidgetComponent } from '@home/components/widget/lib/cards/value-chart-card-widget.component'; +import { ProgressBarWidgetComponent } from '@home/components/widget/lib/cards/progress-bar-widget.component'; +import { LiquidLevelWidgetComponent } from '@home/components/widget/lib/indicator/liquid-level-widget.component'; +import { DoughnutWidgetComponent } from '@home/components/widget/lib/chart/doughnut-widget.component'; +import { RangeChartWidgetComponent } from '@home/components/widget/lib/chart/range-chart-widget.component'; @NgModule({ declarations: @@ -66,7 +90,25 @@ import { LegendComponent } from '@home/components/widget/lib/legend.component'; MarkdownWidgetComponent, SelectEntityDialogComponent, LegendComponent, - FlotWidgetComponent + FlotWidgetComponent, + GatewayConnectorComponent, + GatewayLogsComponent, + GatewayStatisticsComponent, + GatewayServiceRPCComponent, + DeviceGatewayCommandComponent, + GatewayConfigurationComponent, + GatewayRemoteConfigurationDialogComponent, + ValueCardWidgetComponent, + AggregatedValueCardWidgetComponent, + CountWidgetComponent, + BatteryLevelWidgetComponent, + WindSpeedDirectionWidgetComponent, + SignalStrengthWidgetComponent, + ValueChartCardWidgetComponent, + ProgressBarWidgetComponent, + LiquidLevelWidgetComponent, + DoughnutWidgetComponent, + RangeChartWidgetComponent ], imports: [ CommonModule, @@ -94,7 +136,25 @@ import { LegendComponent } from '@home/components/widget/lib/legend.component'; QrCodeWidgetComponent, MarkdownWidgetComponent, LegendComponent, - FlotWidgetComponent + FlotWidgetComponent, + GatewayConnectorComponent, + GatewayLogsComponent, + GatewayStatisticsComponent, + GatewayServiceRPCComponent, + DeviceGatewayCommandComponent, + GatewayConfigurationComponent, + GatewayRemoteConfigurationDialogComponent, + ValueCardWidgetComponent, + AggregatedValueCardWidgetComponent, + CountWidgetComponent, + BatteryLevelWidgetComponent, + WindSpeedDirectionWidgetComponent, + SignalStrengthWidgetComponent, + ValueChartCardWidgetComponent, + ProgressBarWidgetComponent, + LiquidLevelWidgetComponent, + DoughnutWidgetComponent, + RangeChartWidgetComponent ], providers: [ {provide: WIDGET_COMPONENTS_MODULE_TOKEN, useValue: WidgetComponentsModule } diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html index 1766b79009..cdd1d8e2da 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html @@ -17,11 +17,13 @@ -->
- - - - + + +
+ + +
@@ -31,35 +33,49 @@
-
-
widget-config.card-title
+
+
widget-config.card-title
{{ 'widget-config.display-title' | translate }} -
- - widget-config.title - - - - widget-config.title-tooltip - +
+
widget-config.title
+
+ + + + + + + +
+
+
+
widget-config.title-tooltip
+ +
-
+
{{ 'widget-config.display-icon' | translate }} -
+
+ + + - - - -
@@ -73,45 +89,46 @@
-
-
widget-config.card-style
-
-
{{ 'widget-config.text' | translate }}
-
- - - -
+
+
widget-config.card-style
+
+
{{ 'widget-config.text-color' | translate }}
+ +
-
-
{{ 'widget-config.background' | translate }}
-
- - - -
+
+
{{ 'widget-config.background-color' | translate }}
+ +
-
+
{{ 'widget-config.padding' | translate }}
- +
-
+
{{ 'widget-config.margin' | translate }}
- +
+
+
{{ 'widget-config.border-radius' | translate }}
+ + + +
{{ 'widget-config.drop-shadow' | translate }} @@ -124,7 +141,6 @@ @@ -135,15 +151,15 @@
-
-
widget-config.card-buttons
+
+
widget-config.card-buttons
{{ 'widget-config.enable-fullscreen' | translate }}
-
+
-
+
{{ 'widget-config.mobile-hide' | translate }}
-
+
{{ 'widget-config.desktop-hide' | translate }}
-
-
+
+
widget-config.order
- +
-
+
widget-config.height
- +
@@ -205,8 +221,16 @@ formControlName="timewindowConfig"> -
- +
+
+
alarm.filter
+ +
+
-
-
widget-config.target-device
+
widget-config.target-device
-
widget-config.alarm-source
+ [formGroup]="dataSettings" class="tb-form-panel" > +
widget-config.alarm-source
-
-
widget-config.limits
-
+
+
widget-config.limits
+
widget-config.data-page-size
- +
-
-
widget-config.data-settings
-
-
widget-config.units
- +
widget-config.data-settings
+
+
widget-config.units-by-default
+ - +
-
-
widget-config.decimals
- +
+
widget-config.decimals-by-default
+
-
-
widget-config.no-data-display-message
+
+
widget-config.no-data-display-message
@@ -274,6 +298,7 @@ [aliasController]="aliasController" [dashboard]="dashboard" [widget]="widget" + [widgetConfig]="modelValue" formControlName="settings">
diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.scss b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.scss index 91b3368ad0..701bfec099 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.scss @@ -20,16 +20,36 @@ .tb-widget-config { display: flex; flex-direction: column; - gap: 16px; + gap: 8px; .tb-widget-config-header { - padding: 24px 24px 8px; - height: 56px; + padding: 24px 24px 0; display: flex; - flex-direction: row; - align-items: center; - justify-content: space-between; + gap: 12px; + flex-direction: column-reverse; + align-items: flex-start; + @media #{$mat-gt-sm} { + gap: 0; + flex-direction: row; + align-items: center; + justify-content: space-between; + } + .tb-widget-config-header-components { + width: 100%; + flex: 1; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + } } .tb-widget-config-content { + & > .mat-content { + padding-top: 8px; + @media #{$mat-xs} { + padding-left: 8px; + padding-right: 8px; + } + } flex: 1; overflow: auto; & > div { @@ -39,6 +59,9 @@ display: flex; flex-direction: column; gap: 16px; + @media #{$mat-xs} { + gap: 8px; + } } } .tb-basic-mode-directive-error { diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts index 999e7e55ff..1fc2effaca 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.ts @@ -21,10 +21,8 @@ import { ComponentRef, forwardRef, Input, - OnChanges, OnDestroy, OnInit, - SimpleChanges, ViewChild, ViewContainerRef } from '@angular/core'; @@ -43,13 +41,14 @@ import { widgetType } from '@shared/models/widget.models'; import { + AsyncValidator, ControlValueAccessor, - NG_VALIDATORS, + NG_ASYNC_VALIDATORS, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, - Validator, + ValidationErrors, Validators } from '@angular/forms'; import { WidgetConfigComponentData } from '@home/models/widget-component.models'; @@ -61,7 +60,7 @@ import { UtilsService } from '@core/services/utils.service'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { TranslateService } from '@ngx-translate/core'; import { EntityType } from '@shared/models/entity-type.models'; -import { Observable, of, Subscription } from 'rxjs'; +import { Observable, of, Subject, Subscription } from 'rxjs'; import { IBasicWidgetConfigComponent, WidgetConfigCallbacks @@ -70,7 +69,7 @@ import { EntityAliasDialogComponent, EntityAliasDialogData } from '@home/components/alias/entity-alias-dialog.component'; -import { catchError, mergeMap, tap } from 'rxjs/operators'; +import { catchError, map, mergeMap, tap } from 'rxjs/operators'; import { MatDialog } from '@angular/material/dialog'; import { EntityService } from '@core/http/entity.service'; import { JsonFormComponentData } from '@shared/components/json-form/json-form-component.models'; @@ -104,13 +103,13 @@ const defaultSettingsForm = [ multi: true }, { - provide: NG_VALIDATORS, + provide: NG_ASYNC_VALIDATORS, useExisting: forwardRef(() => WidgetConfigComponent), multi: true, } ] }) -export class WidgetConfigComponent extends PageComponent implements OnInit, OnDestroy, ControlValueAccessor, Validator, OnChanges { +export class WidgetConfigComponent extends PageComponent implements OnInit, OnDestroy, ControlValueAccessor, AsyncValidator { @ViewChild('basicModeContainer', {read: ViewContainerRef, static: false}) basicModeContainer: ViewContainerRef; @@ -149,7 +148,6 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe @Input() disabled: boolean; - @Input() widgetConfigMode = WidgetConfigMode.advanced; widgetType: widgetType; @@ -184,6 +182,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe private createBasicModeComponentTimeout: Timeout; private basicModeComponentRef: ComponentRef; private basicModeComponent: IBasicWidgetConfigComponent; + private basicModeComponent$: Subject = null; private basicModeComponentChangeSubscription: Subscription; private dataSettingsChangesSubscription: Subscription; @@ -212,6 +211,8 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe this.advancedSettings = this.fb.group({}); this.widgetSettings = this.fb.group({ title: [null, []], + titleFont: [null, []], + titleColor: [null, []], showTitleIcon: [null, []], titleIcon: [null, []], iconColor: [null, []], @@ -224,6 +225,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe color: [null, []], padding: [null, []], margin: [null, []], + borderRadius: [null, []], widgetStyle: [null, []], widgetCss: [null, []], titleStyle: [null, []], @@ -251,19 +253,6 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe }); } - ngOnChanges(changes: SimpleChanges): void { - for (const propName of Object.keys(changes)) { - const change = changes[propName]; - if (!change.firstChange && change.currentValue !== change.previousValue) { - if (propName === 'widgetConfigMode') { - if (this.hasBasicModeDirective) { - this.setupConfig(); - } - } - } - } - } - ngOnDestroy(): void { this.destroyBasicModeComponent(); this.removeChangeSubscriptions(); @@ -366,7 +355,8 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe this.dataSettings.addControl('timewindowConfig', this.fb.control({ useDashboardTimewindow: true, displayTimewindow: true, - timewindow: null + timewindow: null, + timewindowStyle: null })); if (this.widgetType === widgetType.alarm) { this.dataSettings.addControl('alarmFilterConfig', this.fb.control(null)); @@ -403,9 +393,22 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe writeValue(value: WidgetConfigComponentData): void { this.modelValue = value; this.widgetType = this.modelValue?.widgetType; + this.widgetConfigMode = this.modelValue?.hasBasicMode ? + (this.modelValue?.config?.configMode || WidgetConfigMode.advanced) : WidgetConfigMode.advanced; this.setupConfig(this.isAdd); } + setWidgetConfigMode(widgetConfigMode: WidgetConfigMode) { + if (this.modelValue?.hasBasicMode && this.widgetConfigMode !== widgetConfigMode) { + this.widgetConfigMode = widgetConfigMode; + this.modelValue.config.configMode = widgetConfigMode; + if (this.hasBasicModeDirective) { + this.setupConfig(); + } + this.propagateChange(this.modelValue); + } + } + private setupConfig(isAdd = false) { if (this.modelValue) { this.destroyBasicModeComponent(); @@ -434,7 +437,13 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe this.basicModeComponentChangeSubscription = this.basicModeComponent.widgetConfigChanged.subscribe((data) => { this.modelValue = data; this.propagateChange(this.modelValue); + this.cd.markForCheck(); }); + if (this.basicModeComponent$) { + this.basicModeComponent$.next(this.basicModeComponent); + this.basicModeComponent$.complete(); + this.basicModeComponent$ = null; + } this.cd.markForCheck(); }, 0); } @@ -472,6 +481,8 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe const displayWidgetTitle = isDefined(config.showTitle) ? config.showTitle : false; this.widgetSettings.patchValue({ title: config.title, + titleFont: config.titleFont, + titleColor: config.titleColor, showTitleIcon: isDefined(config.showTitleIcon) && displayWidgetTitle ? config.showTitleIcon : false, titleIcon: isDefined(config.titleIcon) ? config.titleIcon : '', iconColor: isDefined(config.iconColor) ? config.iconColor : 'rgba(0, 0, 0, 0.87)', @@ -484,6 +495,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe color: config.color, padding: config.padding, margin: config.margin, + borderRadius: config.borderRadius, widgetStyle: isDefined(config.widgetStyle) ? config.widgetStyle : {}, widgetCss: isDefined(config.widgetCss) ? config.widgetCss : '', titleStyle: isDefined(config.titleStyle) ? config.titleStyle : { @@ -511,7 +523,8 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe useDashboardTimewindow, displayTimewindow: isDefined(config.displayTimewindow) ? config.displayTimewindow : true, - timewindow: config.timewindow + timewindow: config.timewindow, + timewindowStyle: config.timewindowStyle }, {emitEvent: false}); } if (this.modelValue.isDataEnabled) { @@ -578,11 +591,15 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe const showTitleIcon: boolean = this.widgetSettings.get('showTitleIcon').value; if (showTitle) { this.widgetSettings.get('title').enable({emitEvent: false}); + this.widgetSettings.get('titleFont').enable({emitEvent: false}); + this.widgetSettings.get('titleColor').enable({emitEvent: false}); this.widgetSettings.get('titleTooltip').enable({emitEvent: false}); this.widgetSettings.get('titleStyle').enable({emitEvent: false}); this.widgetSettings.get('showTitleIcon').enable({emitEvent: false}); } else { this.widgetSettings.get('title').disable({emitEvent: false}); + this.widgetSettings.get('titleFont').disable({emitEvent: false}); + this.widgetSettings.get('titleColor').disable({emitEvent: false}); this.widgetSettings.get('titleTooltip').disable({emitEvent: false}); this.widgetSettings.get('titleStyle').disable({emitEvent: false}); this.widgetSettings.get('showTitleIcon').disable({emitEvent: false}); @@ -712,7 +729,7 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe } public get displayAppearanceDataSettings(): boolean { - return this.displayUnitsConfig || this.displayNoDataDisplayMessageConfig; + return !this.modelValue?.typeParameters?.hideDataSettings && (this.displayUnitsConfig || this.displayNoDataDisplayMessageConfig); } public get displayUnitsConfig(): boolean { @@ -866,40 +883,65 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe return stateId => stateId.toLowerCase().indexOf(lowercaseQuery) === 0; } - public validate(c: UntypedFormControl) { - if (this.basicModeComponent) { - if (!this.basicModeComponent.validateConfig()) { + public validate(c: UntypedFormControl): Observable { + const basicComponentMode = this.hasBasicModeDirective && this.widgetConfigMode === WidgetConfigMode.basic; + let comp$: Observable; + if (basicComponentMode) { + if (this.basicModeComponent) { + comp$ = of(this.basicModeComponent); + } else { + if (this.useDefinedBasicModeDirective) { + this.basicModeComponent$ = new Subject(); + comp$ = this.basicModeComponent$; + } else { + comp$ = of(null); + } + } + } else { + comp$ = of(null); + } + return comp$.pipe( + map((comp) => this.doValidate(basicComponentMode, comp)) + ); + } + + private doValidate(basicComponentMode: boolean, basicModeComponent?: IBasicWidgetConfigComponent): ValidationErrors | null { + if (basicComponentMode) { + if (!basicModeComponent || !basicModeComponent.validateConfig()) { return { basicWidgetConfig: { valid: false } }; } - } else if (!this.dataSettings.valid) { - return { - dataSettings: { - valid: false - } - }; - } else if (!this.widgetSettings.valid) { - return { - widgetSettings: { - valid: false - } - }; - } else if (!this.layoutSettings.valid) { - return { - widgetSettings: { - valid: false - } - }; - } else if (!this.advancedSettings.valid || (this.displayAdvancedAppearance && !this.modelValue.config.settings)) { - return { - advancedSettings: { - valid: false - } - }; - } else if (this.modelValue) { + } else { + if (!this.dataSettings.valid) { + return { + dataSettings: { + valid: false + } + }; + } else if (!this.widgetSettings.valid) { + return { + widgetSettings: { + valid: false + } + }; + } else if (!this.layoutSettings.valid) { + return { + widgetSettings: { + valid: false + } + }; + } else if (!this.advancedSettings.valid) { + return { + advancedSettings: { + valid: false + } + }; + } + } + if (this.modelValue) { const config = this.modelValue.config; if (this.widgetType === widgetType.rpc && this.modelValue.isDataEnabled) { if (!this.widgetEditMode && (!config.targetDeviceAliasIds || !config.targetDeviceAliasIds.length)) { @@ -913,4 +955,5 @@ export class WidgetConfigComponent extends PageComponent implements OnInit, OnDe } return null; } + } diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.html b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.html index 23082a121b..1e3fd33171 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.html @@ -19,7 +19,6 @@ [fullscreenBackgroundStyle]="dashboardStyle" [fullscreenBackgroundImage]="backgroundImage" (fullscreenChanged)="onFullscreenChanged($event)" - fxLayout="column" class="tb-widget" [ngClass]="{ 'tb-highlighted': isHighlighted(widget), @@ -32,85 +31,93 @@ (mousedown)="onMouseDown($event)" (click)="onClicked($event)" (contextmenu)="onContextMenu($event)"> -
-
- - {{widget.titleIcon}} - {{widget.customTranslatedTitle}} - - - -
+
+ + +
-
- + + [isMobile]="isMobile" + [widgetTitlePanel]="widgetTitlePanel">
+ +
+
+ {{widget.titleIcon}} +
+ {{widget.title$ | async}} +
+
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.scss b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.scss index 4c7264ce29..a61dc463db 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.scss @@ -14,9 +14,14 @@ * limitations under the License. */ -tb-widget.tb-widget { - position: relative; - height: 100%; +.tb-widget-container { + position: absolute; + inset: 0; +} + +.tb-widget { + position: absolute; + inset: 0; margin: 0; overflow: hidden; outline: none; @@ -25,15 +30,20 @@ tb-widget.tb-widget { } div.tb-widget { - position: relative; - height: 100%; - margin: 0; - overflow: hidden; - outline: none; - - transition: all .2s ease-in-out; + display: flex; + flex-direction: column; + .tb-widget-header { + display: flex; + flex-direction: row; + place-content: flex-start space-between; + align-items: flex-start; + } .tb-widget-title { + display: flex; + flex-direction: column; + place-content: flex-start center; + align-items: flex-start; max-height: 65px; padding-top: 5px; padding-left: 5px; @@ -42,27 +52,32 @@ div.tb-widget { tb-timewindow { font-size: 14px; opacity: .85; - margin: 0; } - .title { + .title-row { + display: flex; + flex-direction: row; + place-content: center flex-start; + align-items: center; + gap: 4px; width: 100%; + } + + .title { overflow: hidden; text-overflow: ellipsis; + white-space: nowrap; line-height: 24px; - letter-spacing: .01em; + letter-spacing: normal; margin: 0; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 2; - - &.single-row{ - -webkit-line-clamp: 1; - } } } .tb-widget-actions { + display: flex; + flex-direction: row; + place-content: center flex-start; + align-items: center; z-index: 19; margin: 5px 0 0; @@ -82,7 +97,7 @@ div.tb-widget { margin: 0 !important; line-height: 20px; - mat-icon { + .mat-icon { width: 20px; min-width: 20px; height: 20px; @@ -104,13 +119,11 @@ div.tb-widget { } .tb-widget-content { + flex: 1; + position: relative; &.tb-no-interaction { pointer-events: none; } - tb-widget { - position: relative; - width: 100%; - } } &.tb-highlighted { diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.ts index bf7042d233..c022015c8f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.ts @@ -15,6 +15,7 @@ /// import { + AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, @@ -62,7 +63,7 @@ export class WidgetComponentAction { encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush }) -export class WidgetContainerComponent extends PageComponent implements OnInit, OnDestroy { +export class WidgetContainerComponent extends PageComponent implements OnInit, AfterViewInit, OnDestroy { @HostBinding('class') widgetContainerClass = 'tb-widget-container'; @@ -131,6 +132,10 @@ export class WidgetContainerComponent extends PageComponent implements OnInit, O } } + ngAfterViewInit(): void { + this.widget.widgetContext.$widgetElement = $(this.tbWidgetElement.nativeElement); + } + ngOnDestroy(): void { if (this.cssClass) { const el = this.document.getElementById(this.cssClass); diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-preview.component.html b/ui-ngx/src/app/modules/home/components/widget/widget-preview.component.html index 870cb08513..74ba76bae1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-preview.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/widget-preview.component.html @@ -16,8 +16,11 @@ --> ) { @@ -67,13 +77,15 @@ export class WidgetPreviewComponent extends PageComponent implements OnInit, OnC } private loadPreviewWidget() { - const widget = deepClone(this.widget); - widget.sizeX = 24; - widget.sizeY = this.widget.sizeY * 2; - widget.row = 0; - widget.col = 0; - widget.config = this.widgetConfig; - this.widgets = [widget]; + if (this.widget) { + const widget = deepClone(this.widget); + widget.sizeX = 24; + widget.sizeY = this.widget.sizeY * 2; + widget.row = 0; + widget.col = 0; + widget.config = this.widgetConfig; + this.widgets = [widget]; + } } } diff --git a/ui-ngx/src/app/modules/home/components/widget/widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/widget.component.scss index ac942225c8..ef3d7007e0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/widget.component.scss @@ -30,6 +30,7 @@ align-items: center; justify-content: center; background: rgba(255, 255, 255, .75); + z-index: 2; span { color: #000; diff --git a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts index 7e9b9cb6e7..432bfd5f42 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts @@ -32,6 +32,7 @@ import { Optional, Renderer2, SimpleChanges, + TemplateRef, Type, ViewChild, ViewContainerRef, @@ -125,6 +126,9 @@ import { IModulesMap } from '@modules/common/modules-map.models'; }) export class WidgetComponent extends PageComponent implements OnInit, AfterViewInit, OnChanges, OnDestroy { + @Input() + widgetTitlePanel: TemplateRef; + @Input() isEdit: boolean; @@ -249,6 +253,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI getActionDescriptors: this.getActionDescriptors.bind(this), handleWidgetAction: this.handleWidgetAction.bind(this), elementClick: this.elementClick.bind(this), + cardClick: this.cardClick.bind(this), getActiveEntityInfo: this.getActiveEntityInfo.bind(this), openDashboardStateInSeparateDialog: this.openDashboardStateInSeparateDialog.bind(this), openDashboardStateInPopover: this.openDashboardStateInPopover.bind(this) @@ -297,7 +302,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI this.subscriptionContext.widgetUtils = this.widgetContext.utils; this.subscriptionContext.getServerTimeDiff = this.dashboardService.getServerTimeDiff.bind(this.dashboardService); - this.widgetComponentService.getWidgetInfo(this.widget.bundleAlias, this.widget.typeAlias, this.widget.isSystemType).subscribe( + this.widgetComponentService.getWidgetInfo(this.widget.typeFullFqn).subscribe( (widgetInfo) => { this.widgetInfo = widgetInfo; this.loadFromWidgetInfo(); @@ -308,9 +313,6 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI this.loadFromWidgetInfo(); } ); - setTimeout(() => { - this.dashboardWidget.updateWidgetParams(); - }, 0); const noDataDisplayMessage = this.widget.config.noDataDisplayMessage; if (isNotEmptyStr(noDataDisplayMessage)) { @@ -387,7 +389,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI this.handleWidgetException(e); } } - this.widgetContext.destroyed = true; + this.widgetContext.destroy(); this.destroyDynamicWidgetComponent(); } } @@ -403,12 +405,13 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI private loadFromWidgetInfo() { this.widgetContext.widgetNamespace = - `widget-type-${(this.widget.isSystemType ? 'sys-' : '')}${this.widget.bundleAlias}-${this.widget.typeAlias}`; + `widget-type-${this.widget.typeFullFqn.replace(/\./g, '-')}`; const elem = this.elementRef.nativeElement; elem.classList.add('tb-widget'); elem.classList.add(this.widgetContext.widgetNamespace); this.widgetType = this.widgetInfo.widgetTypeFunction; this.typeParameters = this.widgetInfo.typeParameters; + this.widgetContext.embedTitlePanel = this.typeParameters.embedTitlePanel; if (!this.widgetType) { this.widgetTypeInstance = {}; @@ -479,6 +482,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI } if (!this.widgetContext.inited && this.isReady()) { this.widgetContext.inited = true; + this.dashboardWidget.updateWidgetParams(); this.widgetContext.detectContainerChanges(); if (this.cafs.init) { this.cafs.init(); @@ -492,7 +496,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI if (this.dataUpdatePending) { this.widgetTypeInstance.onDataUpdated(); setTimeout(() => { - this.dashboardWidget.updateCustomHeaderActions(true); + this.dashboardWidget.updateParamsFromData(true); }, 0); this.dataUpdatePending = false; } @@ -734,6 +738,10 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI { provide: 'errorMessages', useValue: this.errorMessages + }, + { + provide: 'widgetTitlePanel', + useValue: this.widgetTitlePanel } ], parent: this.injector @@ -744,7 +752,8 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI this.widgetContext.$containerParent = $(containerElement); try { - this.dynamicWidgetComponentRef = this.widgetContentContainer.createComponent(this.widgetInfo.componentFactory, 0, injector); + this.dynamicWidgetComponentRef = this.widgetContentContainer.createComponent(this.widgetInfo.componentType, + {index: 0, injector, ngModuleRef: this.widgetInfo.componentModuleRef}); this.cd.detectChanges(); } catch (e) { if (this.dynamicWidgetComponentRef) { @@ -753,8 +762,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI } this.widgetContentContainer.clear(); this.handleWidgetException(e); - this.widgetComponentService.clearWidgetInfo(this.widgetInfo, this.widget.bundleAlias, this.widget.typeAlias, - this.widget.isSystemType); + this.widgetComponentService.clearWidgetInfo(this.widgetInfo); throw e; } @@ -844,7 +852,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI if (this.widgetInstanceInited) { this.widgetTypeInstance.onDataUpdated(); setTimeout(() => { - this.dashboardWidget.updateCustomHeaderActions(true); + this.dashboardWidget.updateParamsFromData(true); }, 0); } else { this.dataUpdatePending = true; @@ -1411,6 +1419,19 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI } } + private cardClick($event: Event) { + const descriptors = this.getActionDescriptors('cardClick'); + if (descriptors.length) { + $event.stopPropagation(); + const descriptor = descriptors[0]; + const entityInfo = this.getActiveEntityInfo(); + const entityId = entityInfo ? entityInfo.entityId : null; + const entityName = entityInfo ? entityInfo.entityName : null; + const entityLabel = entityInfo && entityInfo.entityLabel ? entityInfo.entityLabel : null; + this.handleWidgetAction($event, descriptor, entityId, entityName, null, entityLabel); + } + } + private loadCustomActionResources(actionNamespace: string, customCss: string, customResources: Array, actionDescriptor: WidgetActionDescriptor): Observable { const resourceTasks: Observable[] = []; diff --git a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html index c1485cf450..06528041ab 100644 --- a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html @@ -27,9 +27,12 @@ -
- + check @@ -54,133 +57,50 @@ {{ 'device.label-max-length' | translate }} -
- - - device.wizard.existing-device-profile - - - device.wizard.new-device-profile - - -
- - - - device-profile.new-device-profile-name - - - {{ 'device-profile.new-device-profile-name-required' | translate }} - - -
-
- - -
-
- - -
-
-
- + + +
+ {{ 'device.is-gateway' | translate }} - - + + {{ 'device.overwrite-activity-time' | translate }} - +
- + + + device.description - + - -
- {{ 'device-profile.transport-configuration' | translate }} - device-profile.transport-type - - - {{deviceTransportTypeTranslations.get(type) | translate}} - - - - {{deviceTransportTypeHints.get(transportConfigFormGroup.get('transportType').value) | translate}} - - - {{ 'device-profile.transport-type-required' | translate }} - - - - -
-
- -
- {{'device-profile.alarm-rules-with-count' | translate: - {count: alarmRulesFormGroup.get('alarms').value ? - alarmRulesFormGroup.get('alarms').value.length : 0} }} - - -
-
- -
- {{ 'device-profile.device-provisioning' | translate }} - - -
-
- + {{ 'device.credentials' | translate }}
- {{ 'device.wizard.add-credentials' | translate }}
- - {{ 'customer.customer' | translate }} -
- - -
-
-
-
+
+
@@ -192,7 +112,7 @@ (click)="nextStep()">{{ 'action.next-with-label' | translate:{label: (getFormLabel(this.selectedIndex+1) | translate)} }}
-
+
diff --git a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.scss b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.scss index 0fe18467fd..2f35b3e60d 100644 --- a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.scss @@ -18,49 +18,62 @@ :host { height: 100%; display: grid; + grid-template-rows: min-content 4px auto min-content; - .dialog-actions-row { - padding: 8px; + .toggle-group { + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: 16px; + margin-bottom: 16px; + } + + @media #{$mat-sm} { + min-width: 470px; + } + + @media #{$mat-gt-sm} { + min-width: 650px; } } -:host-context(.tb-fullscreen-dialog .mat-mdc-dialog-container) { - @media #{$mat-lt-sm} { - .mat-mdc-dialog-content { - max-height: 75vh; +:host-context(.mat-mdc-dialog-container) { + .tb-dialog-actions { + padding: 0; + grid-row: 4; + + .dialog-actions-row { + padding: 8px; + display: flex; + gap: 8px; + justify-content: flex-end; + flex: 1; } } - .invisible{ - visibility: hidden; + .mat-mdc-dialog-content { + grid-row: 3; + padding: 0; } + } :host ::ng-deep { .mat-mdc-dialog-content { - display: flex; - flex-direction: column; - height: 100%; - padding: 0 !important; - .mat-stepper-horizontal { display: flex; height: 100%; overflow: hidden; .mat-horizontal-stepper-wrapper { - flex: 1 1 100%; + width: 100%; } .mat-horizontal-content-container { - height: 680px; + height: 500px; max-height: 100%; - width: 100%;; overflow-y: auto; scrollbar-gutter: stable; - @media #{$mat-gt-sm} { - min-width: 500px; - } } } } diff --git a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts index 77916d379c..215c77a1bd 100644 --- a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts @@ -14,201 +14,82 @@ /// limitations under the License. /// -import { Component, Inject, OnDestroy, SkipSelf, ViewChild } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { Component, ViewChild } from '@angular/core'; +import { MatDialogRef } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { DialogComponent } from '@shared/components/dialog.component'; import { Router } from '@angular/router'; -import { - createDeviceProfileConfiguration, - createDeviceProfileTransportConfiguration, - DeviceProfile, - DeviceProfileInfo, - DeviceProfileType, - DeviceProvisionConfiguration, - DeviceProvisionType, - DeviceTransportType, - deviceTransportTypeHintMap, - deviceTransportTypeTranslationMap -} from '@shared/models/device.models'; -import { MatStepper } from '@angular/material/stepper'; -import { AddEntityDialogData } from '@home/models/entity/entity-component.models'; -import { BaseData, HasId } from '@shared/models/base-data'; +import { Device, DeviceProfileInfo, DeviceTransportType } from '@shared/models/device.models'; +import { MatStepper, StepperOrientation } from '@angular/material/stepper'; import { EntityType } from '@shared/models/entity-type.models'; -import { DeviceProfileService } from '@core/http/device-profile.service'; -import { EntityId } from '@shared/models/id/entity-id'; -import { Observable, of, Subscription, throwError } from 'rxjs'; -import { catchError, map, mergeMap, tap } from 'rxjs/operators'; +import { Observable, throwError } from 'rxjs'; +import { catchError, map } from 'rxjs/operators'; import { DeviceService } from '@core/http/device.service'; -import { ErrorStateMatcher } from '@angular/material/core'; import { StepperSelectionEvent } from '@angular/cdk/stepper'; -import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout'; +import { BreakpointObserver } from '@angular/cdk/layout'; import { MediaBreakpoints } from '@shared/models/constants'; -import { RuleChainId } from '@shared/models/id/rule-chain-id'; -import { ServiceType } from '@shared/models/queue.models'; import { deepTrim } from '@core/utils'; +import { CustomerId } from '@shared/models/id/customer-id'; +import { HttpErrorResponse } from '@angular/common/http'; @Component({ selector: 'tb-device-wizard', templateUrl: './device-wizard-dialog.component.html', - providers: [], styleUrls: ['./device-wizard-dialog.component.scss'] }) -export class DeviceWizardDialogComponent extends - DialogComponent implements OnDestroy, ErrorStateMatcher { +export class DeviceWizardDialogComponent extends DialogComponent { @ViewChild('addDeviceWizardStepper', {static: true}) addDeviceWizardStepper: MatStepper; - selectedIndex = 0; - - showNext = true; - - createProfile = false; - - entityType = EntityType; - - deviceTransportTypes = Object.values(DeviceTransportType); - - deviceTransportTypeTranslations = deviceTransportTypeTranslationMap; + stepperOrientation: Observable; - deviceTransportTypeHints = deviceTransportTypeHintMap; + stepperLabelPosition: Observable<'bottom' | 'end'>; - deviceWizardFormGroup: UntypedFormGroup; - - transportConfigFormGroup: UntypedFormGroup; - - alarmRulesFormGroup: UntypedFormGroup; + selectedIndex = 0; - provisionConfigFormGroup: UntypedFormGroup; + credentialsOptionalStep = true; - credentialsFormGroup: UntypedFormGroup; + showNext = true; - customerFormGroup: UntypedFormGroup; + entityType = EntityType; - labelPosition: MatStepper['labelPosition'] = 'end'; + deviceWizardFormGroup: FormGroup; - serviceType = ServiceType.TB_RULE_ENGINE; + credentialsFormGroup: FormGroup; - private subscriptions: Subscription[] = []; private currentDeviceProfileTransportType = DeviceTransportType.DEFAULT; constructor(protected store: Store, protected router: Router, - @Inject(MAT_DIALOG_DATA) public data: AddEntityDialogData>, - @SkipSelf() private errorStateMatcher: ErrorStateMatcher, - public dialogRef: MatDialogRef, - private deviceProfileService: DeviceProfileService, + public dialogRef: MatDialogRef, private deviceService: DeviceService, private breakpointObserver: BreakpointObserver, - private fb: UntypedFormBuilder) { + private fb: FormBuilder) { super(store, router, dialogRef); + + this.stepperOrientation = this.breakpointObserver.observe(MediaBreakpoints['gt-sm']) + .pipe(map(({matches}) => matches ? 'horizontal' : 'vertical')); + + this.stepperLabelPosition = this.breakpointObserver.observe(MediaBreakpoints['gt-sm']) + .pipe(map(({matches}) => matches ? 'end' : 'bottom')); + this.deviceWizardFormGroup = this.fb.group({ name: ['', [Validators.required, Validators.maxLength(255)]], label: ['', Validators.maxLength(255)], gateway: [false], overwriteActivityTime: [false], - addProfileType: [0], + customerId: [null], deviceProfileId: [null, Validators.required], - newDeviceProfileTitle: [{value: null, disabled: true}], - defaultRuleChainId: [{value: null, disabled: true}], - defaultQueueName: [{value: null, disabled: true}], description: [''] } ); - this.subscriptions.push(this.deviceWizardFormGroup.get('addProfileType').valueChanges.subscribe( - (addProfileType: number) => { - if (addProfileType === 0) { - this.deviceWizardFormGroup.get('deviceProfileId').setValidators([Validators.required]); - this.deviceWizardFormGroup.get('deviceProfileId').enable(); - this.deviceWizardFormGroup.get('newDeviceProfileTitle').setValidators(null); - this.deviceWizardFormGroup.get('newDeviceProfileTitle').disable(); - this.deviceWizardFormGroup.get('defaultRuleChainId').disable(); - this.deviceWizardFormGroup.get('defaultQueueName').disable(); - this.deviceWizardFormGroup.updateValueAndValidity(); - this.createProfile = false; - } else { - this.deviceWizardFormGroup.get('deviceProfileId').setValidators(null); - this.deviceWizardFormGroup.get('deviceProfileId').disable(); - this.deviceWizardFormGroup.get('newDeviceProfileTitle').setValidators([Validators.required]); - this.deviceWizardFormGroup.get('newDeviceProfileTitle').enable(); - this.deviceWizardFormGroup.get('defaultRuleChainId').enable(); - this.deviceWizardFormGroup.get('defaultQueueName').enable(); - - this.deviceWizardFormGroup.updateValueAndValidity(); - this.createProfile = true; - } - } - )); - - this.transportConfigFormGroup = this.fb.group( - { - transportType: [DeviceTransportType.DEFAULT, Validators.required], - transportConfiguration: [createDeviceProfileTransportConfiguration(DeviceTransportType.DEFAULT), Validators.required] - } - ); - - this.subscriptions.push(this.transportConfigFormGroup.get('transportType').valueChanges.subscribe((transportType) => { - this.deviceProfileTransportTypeChanged(transportType); - })); - - this.alarmRulesFormGroup = this.fb.group({ - alarms: [null] - } - ); - - this.provisionConfigFormGroup = this.fb.group( - { - provisionConfiguration: [{ - type: DeviceProvisionType.DISABLED - } as DeviceProvisionConfiguration, [Validators.required]] - } - ); - this.credentialsFormGroup = this.fb.group({ - setCredential: [false], - credential: [{value: null, disabled: true}] + credential: [] } ); - - this.subscriptions.push(this.credentialsFormGroup.get('setCredential').valueChanges.subscribe((value) => { - if (value) { - this.credentialsFormGroup.get('credential').enable(); - } else { - this.credentialsFormGroup.get('credential').disable(); - } - })); - - this.customerFormGroup = this.fb.group({ - customerId: [null] - } - ); - - this.labelPosition = this.breakpointObserver.isMatched(MediaBreakpoints['gt-sm']) ? 'end' : 'bottom'; - - this.subscriptions.push(this.breakpointObserver - .observe(MediaBreakpoints['gt-sm']) - .subscribe((state: BreakpointState) => { - if (state.matches) { - this.labelPosition = 'end'; - } else { - this.labelPosition = 'bottom'; - } - } - )); - } - - ngOnDestroy() { - super.ngOnDestroy(); - this.subscriptions.forEach(s => s.unsubscribe()); - } - - isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean { - const originalErrorState = this.errorStateMatcher.isErrorState(control, form); - const customErrorState = !!(control && control.invalid); - return originalErrorState || customErrorState; } cancel(): void { @@ -224,24 +105,11 @@ export class DeviceWizardDialogComponent extends } getFormLabel(index: number): string { - if (index > 0) { - if (!this.createProfile) { - index += 3; - } - } switch (index) { case 0: return 'device.wizard.device-details'; case 1: - return 'device-profile.transport-configuration'; - case 2: - return 'device-profile.alarm-rules'; - case 3: - return 'device-profile.device-provisioning'; - case 4: return 'device.credentials'; - case 5: - return 'customer.customer'; } } @@ -249,102 +117,50 @@ export class DeviceWizardDialogComponent extends return this.addDeviceWizardStepper?._steps?.length - 1; } - private deviceProfileTransportTypeChanged(deviceTransportType: DeviceTransportType): void { - this.transportConfigFormGroup.patchValue( - {transportConfiguration: createDeviceProfileTransportConfiguration(deviceTransportType)}); - const setCredentialBox = this.credentialsFormGroup.get('setCredential'); - if (deviceTransportType === DeviceTransportType.LWM2M) { - setCredentialBox.patchValue(true); - setCredentialBox.disable(); - } else { - setCredentialBox.patchValue(false); - setCredentialBox.enable(); - } - } - add(): void { if (this.allValid()) { - this.createDeviceProfile().pipe( - mergeMap(profileId => this.createDevice(profileId)), - mergeMap(device => this.saveCredentials(device)) - ).subscribe( - (created) => { - this.dialogRef.close(created); - } + this.createDevice().subscribe( + (device) => this.dialogRef.close(device) ); } } get deviceTransportType(): DeviceTransportType { - if (this.deviceWizardFormGroup.get('addProfileType').value) { - return this.transportConfigFormGroup.get('transportType').value; - } else { - return this.currentDeviceProfileTransportType; - } + return this.currentDeviceProfileTransportType; } deviceProfileChanged(deviceProfile: DeviceProfileInfo) { if (deviceProfile) { this.currentDeviceProfileTransportType = deviceProfile.transportType; + this.credentialsOptionalStep = this.currentDeviceProfileTransportType !== DeviceTransportType.LWM2M; } } - private createDeviceProfile(): Observable { - if (this.deviceWizardFormGroup.get('addProfileType').value) { - const deviceProvisionConfiguration: DeviceProvisionConfiguration = this.provisionConfigFormGroup.get('provisionConfiguration').value; - const provisionDeviceKey = deviceProvisionConfiguration.provisionDeviceKey; - delete deviceProvisionConfiguration.provisionDeviceKey; - const deviceProfile: DeviceProfile = { - name: this.deviceWizardFormGroup.get('newDeviceProfileTitle').value, - type: DeviceProfileType.DEFAULT, - defaultQueueName: this.deviceWizardFormGroup.get('defaultQueueName').value, - transportType: this.transportConfigFormGroup.get('transportType').value, - provisionType: deviceProvisionConfiguration.type, - provisionDeviceKey, - profileData: { - configuration: createDeviceProfileConfiguration(DeviceProfileType.DEFAULT), - transportConfiguration: this.transportConfigFormGroup.get('transportConfiguration').value, - alarms: this.alarmRulesFormGroup.get('alarms').value, - provisionConfiguration: deviceProvisionConfiguration - } - }; - if (this.deviceWizardFormGroup.get('defaultRuleChainId').value) { - deviceProfile.defaultRuleChainId = new RuleChainId(this.deviceWizardFormGroup.get('defaultRuleChainId').value); - } - return this.deviceProfileService.saveDeviceProfile(deepTrim(deviceProfile)).pipe( - tap((profile) => { - this.currentDeviceProfileTransportType = profile.transportType; - this.deviceWizardFormGroup.patchValue({ - deviceProfileId: profile.id, - addProfileType: 0 - }); - }), - map(profile => profile.id) - ); - } else { - return of(this.deviceWizardFormGroup.get('deviceProfileId').value); - } - } - - private createDevice(profileId): Observable> { - const device = { + private createDevice(): Observable { + const device: Device = { name: this.deviceWizardFormGroup.get('name').value, label: this.deviceWizardFormGroup.get('label').value, - deviceProfileId: profileId, + deviceProfileId: this.deviceWizardFormGroup.get('deviceProfileId').value, additionalInfo: { gateway: this.deviceWizardFormGroup.get('gateway').value, overwriteActivityTime: this.deviceWizardFormGroup.get('overwriteActivityTime').value, description: this.deviceWizardFormGroup.get('description').value }, - customerId: null + customerId: this.deviceWizardFormGroup.get('customerId').value }; - if (this.customerFormGroup.get('customerId').value) { - device.customerId = { - entityType: EntityType.CUSTOMER, - id: this.customerFormGroup.get('customerId').value - }; + if (this.addDeviceWizardStepper.steps.last.completed || this.addDeviceWizardStepper.selectedIndex > 0) { + return this.deviceService.saveDeviceWithCredentials(deepTrim(device), deepTrim(this.credentialsFormGroup.value.credential)).pipe( + catchError((e: HttpErrorResponse) => { + if (e.error.message.includes('Device credentials')) { + this.addDeviceWizardStepper.selectedIndex = 1; + } else { + this.addDeviceWizardStepper.selectedIndex = 0; + } + return throwError(() => e); + }) + ); } - return this.data.entitiesTableConfig.saveEntity(deepTrim(device)).pipe( + return this.deviceService.saveDevice(deepTrim(device)).pipe( catchError(e => { this.addDeviceWizardStepper.selectedIndex = 0; return throwError(e); @@ -352,31 +168,8 @@ export class DeviceWizardDialogComponent extends ); } - private saveCredentials(device: BaseData): Observable { - if (this.credentialsFormGroup.get('setCredential').value) { - return this.deviceService.getDeviceCredentials(device.id.id).pipe( - mergeMap( - (deviceCredentials) => { - const deviceCredentialsValue = {...deviceCredentials, ...this.credentialsFormGroup.value.credential}; - return this.deviceService.saveDeviceCredentials(deviceCredentialsValue).pipe( - catchError(e => { - this.addDeviceWizardStepper.selectedIndex = 1; - return this.deviceService.deleteDevice(device.id.id).pipe( - mergeMap(() => { - return throwError(e); - } - )); - }) - ); - } - ), - map(() => true)); - } - return of(true); - } - allValid(): boolean { - if (this.addDeviceWizardStepper.steps.find((item, index) => { + return !this.addDeviceWizardStepper.steps.find((item, index) => { if (item.stepControl.invalid) { item.interacted = true; this.addDeviceWizardStepper.selectedIndex = index; @@ -384,19 +177,11 @@ export class DeviceWizardDialogComponent extends } else { return false; } - } )) { - return false; - } else { - return true; - } + }); } changeStep($event: StepperSelectionEvent): void { this.selectedIndex = $event.selectedIndex; - if (this.selectedIndex === this.maxStepperIndex) { - this.showNext = false; - } else { - this.showNext = true; - } + this.showNext = this.selectedIndex !== this.maxStepperIndex; } } diff --git a/ui-ngx/src/app/modules/home/dialogs/home-dialogs.service.ts b/ui-ngx/src/app/modules/home/dialogs/home-dialogs.service.ts index 1b6af3bf43..b009000548 100644 --- a/ui-ngx/src/app/modules/home/dialogs/home-dialogs.service.ts +++ b/ui-ngx/src/app/modules/home/dialogs/home-dialogs.service.ts @@ -21,7 +21,7 @@ import { Observable } from 'rxjs'; import { ImportDialogCsvComponent, ImportDialogCsvData -} from '@home/components/import-export/import-dialog-csv.component'; +} from '@shared/import-export/import-dialog-csv.component'; @Injectable() export class HomeDialogsService { diff --git a/ui-ngx/src/app/modules/home/home.component.html b/ui-ngx/src/app/modules/home/home.component.html index 727ac490c3..697134759f 100644 --- a/ui-ngx/src/app/modules/home/home.component.html +++ b/ui-ngx/src/app/modules/home/home.component.html @@ -57,7 +57,7 @@
diff --git a/ui-ngx/src/app/modules/home/home.component.ts b/ui-ngx/src/app/modules/home/home.component.ts index 6e045b9786..f2985a93cc 100644 --- a/ui-ngx/src/app/modules/home/home.component.ts +++ b/ui-ngx/src/app/modules/home/home.component.ts @@ -14,10 +14,10 @@ /// limitations under the License. /// -import { AfterViewInit, Component, ElementRef, Inject, OnInit, ViewChild } from '@angular/core'; -import { fromEvent } from 'rxjs'; +import { AfterViewInit, Component, ElementRef, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { startWith, skip, Subject } from 'rxjs'; import { Store } from '@ngrx/store'; -import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators'; +import { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators'; import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout'; import { PageComponent } from '@shared/components/page.component'; @@ -31,13 +31,14 @@ import { WINDOW } from '@core/services/window.service'; import { instanceOfSearchableComponent, ISearchableComponent } from '@home/models/searchable-component.models'; import { ActiveComponentService } from '@core/services/active-component.service'; import { RouterTabsComponent } from '@home/components/router-tabs.component'; +import { FormBuilder } from '@angular/forms'; @Component({ selector: 'tb-home', templateUrl: './home.component.html', styleUrls: ['./home.component.scss'] }) -export class HomeComponent extends PageComponent implements AfterViewInit, OnInit { +export class HomeComponent extends PageComponent implements AfterViewInit, OnInit, OnDestroy { authState: AuthState = getCurrentAuthState(this.store); @@ -60,14 +61,17 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni searchEnabled = false; showSearch = false; - searchText = ''; + textSearch = this.fb.control('', {nonNullable: true}); hideLoadingBar = false; + private destroy$ = new Subject(); + constructor(protected store: Store, @Inject(WINDOW) private window: Window, private activeComponentService: ActiveComponentService, - public breakpointObserver: BreakpointObserver) { + public breakpointObserver: BreakpointObserver, + private fb: FormBuilder) { super(store); } @@ -79,6 +83,7 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni this.breakpointObserver .observe(MediaBreakpoints['gt-sm']) + .pipe(takeUntil(this.destroy$)) .subscribe((state: BreakpointState) => { if (state.matches) { this.sidenavMode = 'side'; @@ -91,16 +96,19 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni ); } + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + ngAfterViewInit() { - fromEvent(this.searchInputField.nativeElement, 'keyup') - .pipe( - debounceTime(150), - distinctUntilChanged(), - tap(() => { - this.searchTextUpdated(); - }) - ) - .subscribe(); + this.textSearch.valueChanges.pipe( + debounceTime(150), + startWith(''), + distinctUntilChanged((a: string, b: string) => a.trim() === b.trim()), + skip(1), + takeUntil(this.destroy$) + ).subscribe(value => this.searchTextUpdated(value.trim())); } sidenavClicked() { @@ -136,7 +144,7 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni private updateActiveComponent(activeComponent: any) { this.showSearch = false; - this.searchText = ''; + this.textSearch.reset('', {emitEvent: false}); this.activeComponent = activeComponent; this.hideLoadingBar = activeComponent && activeComponent instanceof RouterTabsComponent; if (this.activeComponent && instanceOfSearchableComponent(this.activeComponent)) { @@ -165,16 +173,15 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni closeSearch() { if (this.searchEnabled) { this.showSearch = false; - if (this.searchText.length) { - this.searchText = ''; - this.searchTextUpdated(); + if (this.textSearch.value.length) { + this.textSearch.reset(); } } } - private searchTextUpdated() { + private searchTextUpdated(searchText: string) { if (this.searchableComponent) { - this.searchableComponent.onSearchTextUpdated(this.searchText); + this.searchableComponent.onSearchTextUpdated(searchText); } } } diff --git a/ui-ngx/src/app/modules/home/menu/menu-link.component.html b/ui-ngx/src/app/modules/home/menu/menu-link.component.html index ab5b1faa27..0efc34a31c 100644 --- a/ui-ngx/src/app/modules/home/menu/menu-link.component.html +++ b/ui-ngx/src/app/modules/home/menu/menu-link.component.html @@ -16,7 +16,6 @@ --> - {{section.icon}} - + {{section.icon}} {{section.name | translate}} diff --git a/ui-ngx/src/app/modules/home/menu/menu-toggle.component.html b/ui-ngx/src/app/modules/home/menu/menu-toggle.component.html index de4f16b163..448c67a513 100644 --- a/ui-ngx/src/app/modules/home/menu/menu-toggle.component.html +++ b/ui-ngx/src/app/modules/home/menu/menu-toggle.component.html @@ -16,8 +16,7 @@ --> - {{section.icon}} - + {{section.icon}} {{section.name | translate}} diff --git a/ui-ngx/src/app/modules/home/menu/side-menu.component.scss b/ui-ngx/src/app/modules/home/menu/side-menu.component.scss index fc9df865a1..dbba5e78a9 100644 --- a/ui-ngx/src/app/modules/home/menu/side-menu.component.scss +++ b/ui-ngx/src/app/modules/home/menu/side-menu.component.scss @@ -49,7 +49,7 @@ &.tb-active { background-color: rgba(255, 255, 255, .15); } - mat-icon { + .mat-icon { margin-right: 8px; margin-left: 0; min-width: 1.125rem; diff --git a/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts b/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts index 2c79ef7b68..b218504ad1 100644 --- a/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/dashboard-component.models.ts @@ -27,12 +27,21 @@ import { WidgetLayout, WidgetLayouts } from '@app/shared/models/dashboard.models import { IDashboardWidget, WidgetAction, WidgetContext, WidgetHeaderAction } from './widget-component.models'; import { Timewindow } from '@shared/models/time/time.models'; import { Observable, of, Subject } from 'rxjs'; -import { formattedDataFormDatasourceData, guid, isDefined, isEqual, isUndefined } from '@app/core/utils'; +import { + convertKeysToCamelCase, + formattedDataFormDatasourceData, + guid, + isDefined, + isEmpty, + isEqual, + isUndefined +} from '@app/core/utils'; import { IterableDiffer, KeyValueDiffer } from '@angular/core'; import { IAliasController, IStateController } from '@app/core/api/widget-api.models'; import { enumerable } from '@shared/decorators/enumerable'; import { UtilsService } from '@core/services/utils.service'; import { TbPopoverComponent } from '@shared/components/popover.component'; +import { ComponentStyle, iconStyle, textStyle } from '@shared/models/widget-settings.models'; export interface WidgetsData { widgets: Array; @@ -328,16 +337,16 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget { backgroundColor: string; padding: string; margin: string; + borderRadius: string; - title: string; - customTranslatedTitle: string; + title$: Observable; titleTooltip: string; showTitle: boolean; - titleStyle: {[klass: string]: any}; + titleStyle: ComponentStyle; titleIcon: string; showTitleIcon: boolean; - titleIconStyle: {[klass: string]: any}; + titleIconStyle: ComponentStyle; dropShadow: boolean; enableFullscreen: boolean; @@ -350,7 +359,7 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget { onlyHistoryTimewindow: boolean; - style: {[klass: string]: any}; + style: ComponentStyle; showWidgetTitlePanel: boolean; showWidgetActions: boolean; @@ -427,26 +436,25 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget { this.backgroundColor = this.widget.config.backgroundColor || '#fff'; this.padding = this.widget.config.padding || '8px'; this.margin = this.widget.config.margin || '0px'; + this.borderRadius = this.widget.config.borderRadius; - this.title = isDefined(this.widgetContext.widgetTitle) + const title = isDefined(this.widgetContext.widgetTitle) && this.widgetContext.widgetTitle.length ? this.widgetContext.widgetTitle : this.widget.config.title; - this.customTranslatedTitle = this.dashboard.utils.customTranslation(this.title, this.title); + this.title$ = this.widgetContext.registerLabelPattern(title, this.title$); this.titleTooltip = isDefined(this.widgetContext.widgetTitleTooltip) && this.widgetContext.widgetTitleTooltip.length ? this.widgetContext.widgetTitleTooltip : this.widget.config.titleTooltip; this.titleTooltip = this.dashboard.utils.customTranslation(this.titleTooltip, this.titleTooltip); this.showTitle = isDefined(this.widget.config.showTitle) ? this.widget.config.showTitle : true; - this.titleStyle = this.widget.config.titleStyle ? this.widget.config.titleStyle : {}; - + this.titleStyle = {...(this.widget.config.titleStyle || {}), ...textStyle(this.widget.config.titleFont)}; + if (this.widget.config.titleColor) { + this.titleStyle.color = this.widget.config.titleColor; + } this.titleIcon = isDefined(this.widget.config.titleIcon) ? this.widget.config.titleIcon : ''; this.showTitleIcon = isDefined(this.widget.config.showTitleIcon) ? this.widget.config.showTitleIcon : false; - this.titleIconStyle = {}; + this.titleIconStyle = this.widget.config.iconSize ? iconStyle(this.widget.config.iconSize) : {}; if (this.widget.config.iconColor) { this.titleIconStyle.color = this.widget.config.iconColor; } - if (this.widget.config.iconSize) { - this.titleIconStyle.fontSize = this.widget.config.iconSize; - } - this.dropShadow = isDefined(this.widget.config.dropShadow) ? this.widget.config.dropShadow : true; this.enableFullscreen = isDefined(this.widget.config.enableFullscreen) ? this.widget.config.enableFullscreen : true; @@ -478,9 +486,10 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget { color: this.color, backgroundColor: this.backgroundColor, padding: this.padding, - margin: this.margin}; - if (this.widget.config.widgetStyle) { - this.style = {...this.style, ...this.widget.config.widgetStyle}; + margin: this.margin, + borderRadius: this.borderRadius }; + if (!isEmpty(this.widget.config.widgetStyle)) { + this.style = {...this.style, ...convertKeysToCamelCase(this.widget.config.widgetStyle)}; } this.showWidgetTitlePanel = this.widgetContext.hideTitlePanel ? false : @@ -488,14 +497,22 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget { this.showWidgetActions = !this.widgetContext.hideTitlePanel; - this.updateCustomHeaderActions(); + this.updateParamsFromData(); this.widgetActions = this.widgetContext.widgetActions ? this.widgetContext.widgetActions : []; if (detectChanges) { this.widgetContext.detectContainerChanges(); } } - updateCustomHeaderActions(detectChanges = false) { + updateParamsFromData(detectChanges = false) { + this.widgetContext.updateLabelPatterns(); + const update = this.updateCustomHeaderActions(); + if (update && detectChanges) { + this.widgetContext.detectContainerChanges(); + } + } + + private updateCustomHeaderActions(): boolean { let customHeaderActions: Array; if (this.widgetContext.customHeaderActions) { let data: FormattedData[] = []; @@ -508,10 +525,9 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget { } if (!isEqual(this.customHeaderActions, customHeaderActions)) { this.customHeaderActions = customHeaderActions; - if (detectChanges) { - this.widgetContext.detectContainerChanges(); - } + return true; } + return false; } private filterCustomHeaderAction(action: WidgetHeaderAction, data: FormattedData[]): boolean { diff --git a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts index e9fb01c54a..78ee9ee7bc 100644 --- a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts +++ b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts @@ -57,8 +57,7 @@ export interface CellActionDescriptor> { name: string; nameFunction?: (entity: T) => string; icon?: string; - mdiIcon?: string; - mdiIconFunction?: (entity: T) => string; + iconFunction?: (entity: T) => string; style?: any; isEnabled: (entity: T) => boolean; onAction: ($event: MouseEvent, entity: T) => any; @@ -75,12 +74,11 @@ export interface GroupActionDescriptor> { export interface HeaderActionDescriptor { name: string; icon: string; - isMdiIcon?: boolean; isEnabled: () => boolean; onAction: ($event: MouseEvent) => void; } -export type EntityTableColumnType = 'content' | 'action'; +export type EntityTableColumnType = 'content' | 'action' | 'link'; export class BaseEntityTableColumn> { constructor(public type: EntityTableColumnType, @@ -117,6 +115,16 @@ export class EntityActionTableColumn> extends BaseEnti } } +export class EntityLinkTableColumn> extends BaseEntityTableColumn { + constructor(public key: string, + public title: string, + public width: string = '0px', + public cellContentFunction: CellContentFunction = (entity, property) => entity[property] ? entity[property] : '', + public entityURL: (entity) => string) { + super('link', key, title, width, false); + } +} + export class DateEntityTableColumn> extends EntityTableColumn { constructor(key: string, title: string, @@ -132,7 +140,7 @@ export class DateEntityTableColumn> extends EntityTabl } } -export type EntityColumn> = EntityTableColumn | EntityActionTableColumn; +export type EntityColumn> = EntityTableColumn | EntityActionTableColumn | EntityLinkTableColumn; export class EntityTableConfig, P extends PageLink = PageLink, L extends BaseData = T> { diff --git a/ui-ngx/src/app/modules/home/models/entity/entity-table-component.models.ts b/ui-ngx/src/app/modules/home/models/entity/entity-table-component.models.ts index 033747f6d3..a6e5ada7bf 100644 --- a/ui-ngx/src/app/modules/home/models/entity/entity-table-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/entity/entity-table-component.models.ts @@ -80,6 +80,7 @@ export interface IEntitiesTableComponent { exitFilterMode(): void; resetSortAndFilter(update?: boolean, preserveTimewindow?: boolean): void; columnsUpdated(resetData?: boolean): void; + cellActionDescriptorsUpdated(): void; headerCellStyle(column: EntityColumn>): any; clearCellCache(col: number, row: number): void; cellContent(entity: BaseData, column: EntityColumn>, row: number): any; diff --git a/ui-ngx/src/app/modules/home/models/services.map.ts b/ui-ngx/src/app/modules/home/models/services.map.ts index 0608507295..4c5a8f4c04 100644 --- a/ui-ngx/src/app/modules/home/models/services.map.ts +++ b/ui-ngx/src/app/modules/home/models/services.map.ts @@ -33,7 +33,7 @@ import { UserService } from '@core/http/user.service'; import { AlarmService } from '@core/http/alarm.service'; import { Router } from '@angular/router'; import { BroadcastService } from '@core/services/broadcast.service'; -import { ImportExportService } from '@home/components/import-export/import-export.service'; +import { ImportExportService } from '@shared/import-export/import-export.service'; import { DeviceProfileService } from '@core/http/device-profile.service'; import { AssetProfileService } from '@core/http/asset-profile.service'; import { OtaPackageService } from '@core/http/ota-package.service'; diff --git a/ui-ngx/src/app/modules/home/models/widget-component.models.ts b/ui-ngx/src/app/modules/home/models/widget-component.models.ts index c00d0a8e82..17154f001b 100644 --- a/ui-ngx/src/app/modules/home/models/widget-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/widget-component.models.ts @@ -20,6 +20,7 @@ import { Datasource, DatasourceData, FormattedData, + fullWidgetTypeFqn, JsonSettingsSchema, Widget, WidgetActionDescriptor, @@ -30,6 +31,7 @@ import { widgetType, WidgetTypeDescriptor, WidgetTypeDetails, + widgetTypeFqn, WidgetTypeParameters } from '@shared/models/widget.models'; import { Timewindow, WidgetTimewindow } from '@shared/models/time/time.models'; @@ -45,13 +47,13 @@ import { WidgetActionsApi, WidgetSubscriptionApi } from '@core/api/widget-api.models'; -import { ChangeDetectorRef, ComponentFactory, Injector, NgZone, Type } from '@angular/core'; +import { ChangeDetectorRef, Injector, NgModuleRef, NgZone, Type } from '@angular/core'; import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { RafService } from '@core/services/raf.service'; import { WidgetTypeId } from '@shared/models/id/widget-type-id'; import { TenantId } from '@shared/models/id/tenant-id'; import { WidgetLayout } from '@shared/models/dashboard.models'; -import { formatValue, isDefined } from '@core/utils'; +import { createLabelFromDatasource, formatValue, hasDatasourceLabelsVariables, isDefined } from '@core/utils'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { @@ -84,13 +86,15 @@ import { DomSanitizer } from '@angular/platform-browser'; import { Router } from '@angular/router'; import { EdgeService } from '@core/http/edge.service'; import * as RxJS from 'rxjs'; +import { BehaviorSubject, Observable } from 'rxjs'; import * as RxJSOperators from 'rxjs/operators'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { EntityId } from '@shared/models/id/entity-id'; -import { AlarmQuery, AlarmSearchStatus, AlarmStatus} from '@app/shared/models/alarm.models'; -import { MillisecondsToTimeStringPipe, TelemetrySubscriber } from '@app/shared/public-api'; +import { AlarmQuery, AlarmSearchStatus, AlarmStatus } from '@app/shared/models/alarm.models'; +import { ImagePipe, MillisecondsToTimeStringPipe, TelemetrySubscriber } from '@app/shared/public-api'; import { UserId } from '@shared/models/id/user-id'; import { UserSettingsService } from '@core/http/user-settings.service'; +import { DynamicComponentModule } from '@core/services/dynamic-component-factory.service'; export interface IWidgetAction { name: string; @@ -185,6 +189,7 @@ export class WidgetContext { telemetryWsService: TelemetryWebsocketService; telemetrySubscribers?: TelemetrySubscriber[]; date: DatePipe; + imagePipe: ImagePipe; milliSecondsToTimeString: MillisecondsToTimeStringPipe; translate: TranslateService; http: HttpClient; @@ -200,6 +205,8 @@ export class WidgetContext { subscriptions: {[id: string]: IWidgetSubscription} = {}; defaultSubscription: IWidgetSubscription = null; + labelPatterns = new Map, LabelVariablePattern>(); + timewindowFunctions: TimewindowFunctions = { onUpdateTimewindow: (startTimeMs, endTimeMs, interval) => { if (this.defaultSubscription) { @@ -243,6 +250,7 @@ export class WidgetContext { formatValue }; + $widgetElement: JQuery; $container: JQuery; $containerParent: JQuery; width: number; @@ -264,6 +272,8 @@ export class WidgetContext { hiddenData?: Array<{data: DataSet}>; timeWindow?: WidgetTimewindow; + embedTitlePanel?: boolean; + hideTitlePanel = false; widgetTitle?: string; @@ -309,6 +319,23 @@ export class WidgetContext { }); } + registerLabelPattern(label: string, label$: Observable): Observable { + let labelPattern = label$ ? this.labelPatterns.get(label$) : null; + if (labelPattern) { + labelPattern.setupPattern(label); + } else { + labelPattern = new LabelVariablePattern(label, this); + this.labelPatterns.set(labelPattern.label$, labelPattern); + } + return labelPattern.label$; + } + + updateLabelPatterns() { + for (const labelPattern of this.labelPatterns.values()) { + labelPattern.update(); + } + } + showSuccessToast(message: string, duration: number = 1000, verticalPosition: NotificationVerticalPosition = 'bottom', horizontalPosition: NotificationHorizontalPosition = 'left', @@ -403,6 +430,14 @@ export class WidgetContext { this.widgetActions = undefined; } + destroy() { + for (const labelPattern of this.labelPatterns.values()) { + labelPattern.destroy(); + } + this.labelPatterns.clear(); + this.destroyed = true; + } + closeDialog(resultData: any = null) { const dialogRef = this.$scope.dialogRef || this.stateController.dashboardCtrl.dashboardCtx.getDashboard().dialogRef; if (dialogRef) { @@ -414,15 +449,53 @@ export class WidgetContext { return new PageLink(pageSize, page, textSearch, sortOrder); } - timePageLink(startTime: number, endTime: number, pageSize: number, page: number = 0, textSearch: string = null, sortOrder: SortOrder = null) { + timePageLink(startTime: number, endTime: number, pageSize: number, page: number = 0, + textSearch: string = null, sortOrder: SortOrder = null) { return new TimePageLink(pageSize, page, textSearch, sortOrder, startTime, endTime); } - alarmQuery(entityId: EntityId, pageLink: TimePageLink, searchStatus: AlarmSearchStatus, status: AlarmStatus, fetchOriginator: boolean, assigneeId: UserId) { + alarmQuery(entityId: EntityId, pageLink: TimePageLink, searchStatus: AlarmSearchStatus, + status: AlarmStatus, fetchOriginator: boolean, assigneeId: UserId) { return new AlarmQuery(entityId, pageLink, searchStatus, status, fetchOriginator, assigneeId); } } +export class LabelVariablePattern { + + private pattern: string; + private hasVariables: boolean; + + private labelSubject = new BehaviorSubject(''); + + public label$ = this.labelSubject.asObservable(); + + constructor(label: string, + private ctx: WidgetContext) { + this.setupPattern(label); + } + + setupPattern(label: string) { + this.pattern = this.ctx.dashboard.utils.customTranslation(label, label); + this.hasVariables = hasDatasourceLabelsVariables(this.pattern); + this.update(); + } + + update() { + let label = this.pattern; + const datasource = this.ctx.defaultSubscription?.firstDatasource; + if (this.hasVariables && datasource) { + label = createLabelFromDatasource(datasource, label); + } + if (this.labelSubject.value !== label) { + this.labelSubject.next(label); + } + } + + destroy() { + this.labelSubject.complete(); + } +} + export interface IDynamicWidgetComponent { readonly ctx: WidgetContext; readonly errorMessages: string[]; @@ -437,13 +510,16 @@ export interface IDynamicWidgetComponent { export interface WidgetInfo extends WidgetTypeDescriptor, WidgetControllerDescriptor { widgetName: string; - alias: string; + fullFqn: string; + deprecated: boolean; typeSettingsSchema?: string | any; typeDataKeySettingsSchema?: string | any; typeLatestDataKeySettingsSchema?: string | any; image?: string; description?: string; - componentFactory?: ComponentFactory; + tags?: string[]; + componentType?: Type; + componentModuleRef?: NgModuleRef; } export interface WidgetConfigComponentData { @@ -460,13 +536,15 @@ export interface WidgetConfigComponentData { settingsDirective: string; dataKeySettingsDirective: string; latestDataKeySettingsDirective: string; + hasBasicMode: boolean; basicModeDirective: string; } export const MissingWidgetType: WidgetInfo = { type: widgetType.latest, widgetName: 'Widget type not found', - alias: 'undefined', + fullFqn: 'undefined', + deprecated: false, sizeX: 8, sizeY: 6, resources: [], @@ -490,7 +568,8 @@ export const MissingWidgetType: WidgetInfo = { export const ErrorWidgetType: WidgetInfo = { type: widgetType.latest, widgetName: 'Error loading widget', - alias: 'error', + fullFqn: 'error', + deprecated: false, sizeX: 8, sizeY: 6, resources: [], @@ -529,48 +608,38 @@ export interface WidgetTypeInstance { onDestroy?: () => void; } -export function detailsToWidgetInfo(widgetTypeDetailsEntity: WidgetTypeDetails): WidgetInfo { +export const toWidgetInfo = (widgetTypeEntity: WidgetType): WidgetInfo => ({ + widgetName: widgetTypeEntity.name, + fullFqn: fullWidgetTypeFqn(widgetTypeEntity), + deprecated: widgetTypeEntity.deprecated, + type: widgetTypeEntity.descriptor.type, + sizeX: widgetTypeEntity.descriptor.sizeX, + sizeY: widgetTypeEntity.descriptor.sizeY, + resources: widgetTypeEntity.descriptor.resources, + templateHtml: widgetTypeEntity.descriptor.templateHtml, + templateCss: widgetTypeEntity.descriptor.templateCss, + controllerScript: widgetTypeEntity.descriptor.controllerScript, + settingsSchema: widgetTypeEntity.descriptor.settingsSchema, + dataKeySettingsSchema: widgetTypeEntity.descriptor.dataKeySettingsSchema, + latestDataKeySettingsSchema: widgetTypeEntity.descriptor.latestDataKeySettingsSchema, + settingsDirective: widgetTypeEntity.descriptor.settingsDirective, + dataKeySettingsDirective: widgetTypeEntity.descriptor.dataKeySettingsDirective, + latestDataKeySettingsDirective: widgetTypeEntity.descriptor.latestDataKeySettingsDirective, + hasBasicMode: widgetTypeEntity.descriptor.hasBasicMode, + basicModeDirective: widgetTypeEntity.descriptor.basicModeDirective, + defaultConfig: widgetTypeEntity.descriptor.defaultConfig +}); + +export const detailsToWidgetInfo = (widgetTypeDetailsEntity: WidgetTypeDetails): WidgetInfo => { const widgetInfo = toWidgetInfo(widgetTypeDetailsEntity); widgetInfo.image = widgetTypeDetailsEntity.image; widgetInfo.description = widgetTypeDetailsEntity.description; + widgetInfo.tags = widgetTypeDetailsEntity.tags; return widgetInfo; -} - -export function toWidgetInfo(widgetTypeEntity: WidgetType): WidgetInfo { - return { - widgetName: widgetTypeEntity.name, - alias: widgetTypeEntity.alias, - type: widgetTypeEntity.descriptor.type, - sizeX: widgetTypeEntity.descriptor.sizeX, - sizeY: widgetTypeEntity.descriptor.sizeY, - resources: widgetTypeEntity.descriptor.resources, - templateHtml: widgetTypeEntity.descriptor.templateHtml, - templateCss: widgetTypeEntity.descriptor.templateCss, - controllerScript: widgetTypeEntity.descriptor.controllerScript, - settingsSchema: widgetTypeEntity.descriptor.settingsSchema, - dataKeySettingsSchema: widgetTypeEntity.descriptor.dataKeySettingsSchema, - latestDataKeySettingsSchema: widgetTypeEntity.descriptor.latestDataKeySettingsSchema, - settingsDirective: widgetTypeEntity.descriptor.settingsDirective, - dataKeySettingsDirective: widgetTypeEntity.descriptor.dataKeySettingsDirective, - latestDataKeySettingsDirective: widgetTypeEntity.descriptor.latestDataKeySettingsDirective, - hasBasicMode: widgetTypeEntity.descriptor.hasBasicMode, - basicModeDirective: widgetTypeEntity.descriptor.basicModeDirective, - defaultConfig: widgetTypeEntity.descriptor.defaultConfig - }; -} - -export function toWidgetTypeDetails(widgetInfo: WidgetInfo, id: WidgetTypeId, tenantId: TenantId, - bundleAlias: string, createdTime: number): WidgetTypeDetails { - const widgetTypeEntity = toWidgetType(widgetInfo, id, tenantId, bundleAlias, createdTime); - const widgetTypeDetails: WidgetTypeDetails = {...widgetTypeEntity, - description: widgetInfo.description, - image: widgetInfo.image - }; - return widgetTypeDetails; -} +}; -export function toWidgetType(widgetInfo: WidgetInfo, id: WidgetTypeId, tenantId: TenantId, - bundleAlias: string, createdTime: number): WidgetType { +export const toWidgetType = (widgetInfo: WidgetInfo, id: WidgetTypeId, tenantId: TenantId, + createdTime: number): WidgetType => { const descriptor: WidgetTypeDescriptor = { type: widgetInfo.type, sizeX: widgetInfo.sizeX, @@ -593,15 +662,26 @@ export function toWidgetType(widgetInfo: WidgetInfo, id: WidgetTypeId, tenantId: id, tenantId, createdTime, - bundleAlias, - alias: widgetInfo.alias, + fqn: widgetTypeFqn(widgetInfo.fullFqn), name: widgetInfo.widgetName, + deprecated: widgetInfo.deprecated, descriptor }; -} +}; + +export const toWidgetTypeDetails = (widgetInfo: WidgetInfo, id: WidgetTypeId, tenantId: TenantId, + createdTime: number): WidgetTypeDetails => { + const widgetTypeEntity = toWidgetType(widgetInfo, id, tenantId, createdTime); + return { + ...widgetTypeEntity, + description: widgetInfo.description, + tags: widgetInfo.tags, + image: widgetInfo.image + }; +}; -export function updateEntityParams(params: StateParams, targetEntityParamName?: string, targetEntityId?: EntityId, - entityName?: string, entityLabel?: string) { +export const updateEntityParams = (params: StateParams, targetEntityParamName?: string, targetEntityId?: EntityId, + entityName?: string, entityLabel?: string) => { if (targetEntityId) { let targetEntityParams: StateParams; if (targetEntityParamName && targetEntityParamName.length) { @@ -622,4 +702,4 @@ export function updateEntityParams(params: StateParams, targetEntityParamName?: targetEntityParams.entityLabel = entityLabel; } } -} +}; diff --git a/ui-ngx/src/app/modules/home/pages/account/account-routing.module.ts b/ui-ngx/src/app/modules/home/pages/account/account-routing.module.ts new file mode 100644 index 0000000000..e2c997f654 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/account/account-routing.module.ts @@ -0,0 +1,65 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { inject, NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { RouterTabsComponent } from '@home/components/router-tabs.component'; +import { Authority } from '@shared/models/authority.enum'; +import { securityRoutes } from '@home/pages/security/security-routing.module'; +import { profileRoutes } from '@home/pages/profile/profile-routing.module'; +import { getCurrentAuthState } from '@core/auth/auth.selectors'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { + notificationUserSettingsRoutes +} from '@home/pages/notification/settings/notification-settings-routing.modules'; + +const routes: Routes = [ + { + path: 'account', + component: RouterTabsComponent, + data: { + auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], + breadcrumb: { + label: 'account.account', + icon: 'account_circle' + }, + useChildrenRoutesForTabs: true, + }, + resolve: { + replaceUrl: () => getCurrentAuthState(inject(Store)).forceFullscreen + }, + children: [ + { + path: '', + children: [], + data: { + auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], + redirectTo: '/account/profile', + } + }, + ...profileRoutes, + ...securityRoutes, + ...notificationUserSettingsRoutes + ] + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class AccountRoutingModule { } diff --git a/ui-ngx/src/app/modules/home/pages/account/account.module.ts b/ui-ngx/src/app/modules/home/pages/account/account.module.ts new file mode 100644 index 0000000000..df178607ce --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/account/account.module.ts @@ -0,0 +1,28 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { NgModule } from '@angular/core'; +import { AccountRoutingModule } from '@home/pages/account/account-routing.module'; +import { CommonModule } from '@angular/common'; + +@NgModule({ + declarations: [ ], + imports: [ + CommonModule, + AccountRoutingModule + ] +}) +export class AccountModule { } diff --git a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts index adc87a7886..0526313da4 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts @@ -36,9 +36,10 @@ import { QueuesTableConfigResolver } from '@home/pages/admin/queue/queues-table- import { RepositoryAdminSettingsComponent } from '@home/pages/admin/repository-admin-settings.component'; import { AutoCommitAdminSettingsComponent } from '@home/pages/admin/auto-commit-admin-settings.component'; import { TwoFactorAuthSettingsComponent } from '@home/pages/admin/two-factor-auth-settings.component'; -import { widgetsBundlesRoutes } from '@home/pages/widget/widget-library-routing.module'; +import { widgetsLibraryRoutes } from '@home/pages/widget/widget-library-routing.module'; import { RouterTabsComponent } from '@home/components/router-tabs.component'; import { auditLogsRoutes } from '@home/pages/audit-log/audit-log-routing.module'; +import { ImageGalleryComponent } from '@shared/components/image/image-gallery.component'; @Injectable() export class OAuth2LoginProcessingUrlResolver implements Resolve { @@ -67,10 +68,29 @@ const routes: Routes = [ children: [], data: { auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], - redirectTo: '/resources/widgets-bundles' + redirectTo: '/resources/widgets-library' } }, - ...widgetsBundlesRoutes, + ...widgetsLibraryRoutes, + { + path: 'images', + data: { + breadcrumb: { + label: 'image.gallery', + icon: 'filter' + } + }, + children: [ + { + path: '', + component: ImageGalleryComponent, + data: { + auth: [Authority.TENANT_ADMIN, Authority.SYS_ADMIN], + title: 'image.gallery', + }, + } + ] + }, { path: 'resources-library', data: { @@ -319,8 +339,7 @@ const routes: Routes = [ title: 'admin.2fa.2fa', breadcrumb: { label: 'admin.2fa.2fa', - icon: 'mdi:two-factor-authentication', - isMdiIcon: true + icon: 'mdi:two-factor-authentication' } } }, diff --git a/ui-ngx/src/app/modules/home/pages/admin/general-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/general-settings.component.html index da0e9fede3..7522266237 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/general-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/general-settings.component.html @@ -15,39 +15,115 @@ limitations under the License. --> -
- - - -
- admin.general-settings -
-
-
- - -
- -
-
- - admin.base-url - - - {{ 'admin.base-url-required' | translate }} - - - + + + +
+ admin.general-settings +
+
+
+ + +
+ + +
+ + admin.base-url + + + {{ 'admin.base-url-required' | translate }} + + +
+ {{ 'admin.prohibit-different-url' | translate }} - -
admin.prohibit-different-url-hint
-
- + +
+
+
+ + +
+ +
+
+ + + +
admin.device-connectivity.device-connectivity
+
+
+ + +
+ +
+
+ + {{ "admin.device-connectivity.http-s" | translate }} + {{ 'admin.device-connectivity.mqtt-s' | translate }} + {{ 'admin.device-connectivity.coap-s' | translate }} + +
{{ 'admin.device-connectivity.hint' | translate }}
+
+
+ + + + + + + + + + + + + +
+ + {{ 'admin.device-connectivity.' + protocol | translate }} + +
+ + admin.device-connectivity.host + + + + admin.device-connectivity.port + + + {{ 'admin.device-connectivity.port-pattern' | translate }} + + + {{ 'admin.device-connectivity.port-range' | translate }} + + +
-
+ +
+ + +
-
-
-
+ + + diff --git a/ui-ngx/src/app/modules/home/pages/admin/general-settings.component.scss b/ui-ngx/src/app/modules/home/pages/admin/general-settings.component.scss index 4b2d51d1e9..80dcf76da8 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/general-settings.component.scss +++ b/ui-ngx/src/app/modules/home/pages/admin/general-settings.component.scss @@ -13,11 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@import "../../../../../scss/constants"; + :host { -} + .settings-card { + .toggle-group { + max-width: 670px; + width: 100%; + margin: 0 auto; + + .tb-form-hint { + text-align: center; + } + } -:host ::ng-deep { - .mat-checkbox-layout { - white-space: normal; + @media #{$mat-xs} { + .tb-form-row { + gap: 0; + } + } } } diff --git a/ui-ngx/src/app/modules/home/pages/admin/general-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/general-settings.component.ts index 9aee40d431..00af937934 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/general-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/general-settings.component.ts @@ -14,62 +14,130 @@ /// limitations under the License. /// -import { Component, OnInit } from '@angular/core'; +import { Component, OnDestroy } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { PageComponent } from '@shared/components/page.component'; -import { Router } from '@angular/router'; -import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; -import { AdminSettings, GeneralSettings } from '@shared/models/settings.models'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { + AdminSettings, + DeviceConnectivityProtocol, + DeviceConnectivitySettings, + GeneralSettings +} from '@shared/models/settings.models'; import { AdminService } from '@core/http/admin.service'; import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'tb-general-settings', templateUrl: './general-settings.component.html', styleUrls: ['./general-settings.component.scss', './settings-card.scss'] }) -export class GeneralSettingsComponent extends PageComponent implements OnInit, HasConfirmForm { +export class GeneralSettingsComponent extends PageComponent implements HasConfirmForm, OnDestroy { - generalSettings: UntypedFormGroup; - adminSettings: AdminSettings; + generalSettings: FormGroup; + deviceConnectivitySettingsForm: FormGroup; + + protocol: DeviceConnectivityProtocol = 'http'; + + private adminSettings: AdminSettings; + private deviceConnectivitySettings: AdminSettings; + + private readonly destroy$ = new Subject(); constructor(protected store: Store, - private router: Router, private adminService: AdminService, - public fb: UntypedFormBuilder) { + public fb: FormBuilder) { super(store); + this.buildGeneralServerSettingsForm(); + this.adminService.getAdminSettings('general') + .subscribe(adminSettings => this.processGeneralSettings(adminSettings)); + this.buildDeviceConnectivitySettingsForm(); + this.adminService.getAdminSettings('connectivity') + .subscribe(deviceConnectivitySettings => this.processDeviceConnectivitySettings(deviceConnectivitySettings)); } - ngOnInit() { - this.buildGeneralServerSettingsForm(); - this.adminService.getAdminSettings('general').subscribe( - (adminSettings) => { - this.adminSettings = adminSettings; - this.generalSettings.reset(this.adminSettings.jsonValue); - } - ); + ngOnDestroy() { + super.ngOnDestroy(); + this.destroy$.next(); + this.destroy$.complete(); } - buildGeneralServerSettingsForm() { + private buildGeneralServerSettingsForm() { this.generalSettings = this.fb.group({ baseUrl: ['', [Validators.required]], prohibitDifferentUrl: ['',[]] }); } + private buildDeviceConnectivitySettingsForm() { + this.deviceConnectivitySettingsForm = this.fb.group({ + http: this.buildDeviceConnectivityInfoForm(), + https: this.buildDeviceConnectivityInfoForm(), + mqtt: this.buildDeviceConnectivityInfoForm(), + mqtts: this.buildDeviceConnectivityInfoForm(), + coap: this.buildDeviceConnectivityInfoForm(), + coaps: this.buildDeviceConnectivityInfoForm() + }); + } + + private buildDeviceConnectivityInfoForm(): FormGroup { + const formGroup = this.fb.group({ + enabled: [false, []], + host: [{value: '', disabled: true}], + port: [{value: null, disabled: true}, [Validators.min(1), Validators.max(65535), Validators.pattern('[0-9]*')]] + }); + formGroup.get('enabled').valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(value => { + if (value) { + formGroup.get('host').enable({emitEvent: false}); + formGroup.get('port').enable({emitEvent: false}); + } else { + formGroup.get('host').disable({emitEvent: false}); + formGroup.get('port').disable({emitEvent: false}); + } + }); + return formGroup; + } + save(): void { this.adminSettings.jsonValue = {...this.adminSettings.jsonValue, ...this.generalSettings.value}; - this.adminService.saveAdminSettings(this.adminSettings).subscribe( - (adminSettings) => { - this.adminSettings = adminSettings; - this.generalSettings.reset(this.adminSettings.jsonValue); - } - ); + this.adminService.saveAdminSettings(this.adminSettings) + .subscribe(adminSettings => this.processGeneralSettings(adminSettings)); + } + + saveDeviceConnectivitySettings(): void { + this.deviceConnectivitySettings.jsonValue = { + ...this.deviceConnectivitySettings.jsonValue, + ...this.deviceConnectivitySettingsForm.getRawValue() + }; + this.adminService.saveAdminSettings(this.deviceConnectivitySettings) + .subscribe(deviceConnectivitySettings => this.processDeviceConnectivitySettings(deviceConnectivitySettings)); + } + + discardGeneralSettings(): void { + this.generalSettings.reset(this.adminSettings.jsonValue); + } + + discardDeviceConnectivitySettings(): void { + this.deviceConnectivitySettingsForm.reset(this.deviceConnectivitySettings.jsonValue); + } + + private processGeneralSettings(generalSettings: AdminSettings): void { + this.adminSettings = generalSettings; + this.generalSettings.reset(this.adminSettings.jsonValue); + } + + private processDeviceConnectivitySettings(deviceConnectivitySettings: AdminSettings): void { + this.deviceConnectivitySettings = deviceConnectivitySettings; + this.deviceConnectivitySettingsForm.reset(this.deviceConnectivitySettings.jsonValue); } - confirmForm(): UntypedFormGroup { - return this.generalSettings; + confirmForm(): FormGroup { + return this.generalSettings.dirty ? this.generalSettings : this.deviceConnectivitySettingsForm; } } diff --git a/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.html b/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.html index 2762e0d43a..571be3d75d 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.html @@ -304,7 +304,7 @@ [copyText]="mailSettings.get('redirectUri').value" tooltipText="{{ 'admin.oauth2.copy-redirect-uri' | translate }}" tooltipPosition="above" - mdiIcon="mdi:clipboard-arrow-left"> + icon="mdi:clipboard-arrow-left">
@@ -331,7 +331,7 @@
diff --git a/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.scss b/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.scss index cd6c18af9e..e8da0e042a 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.scss +++ b/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.scss @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@import "../../../../../theme"; +@import "../../../../../scss/constants"; :host { .fields-group { diff --git a/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.ts b/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.ts index 6815c81e31..d95c3d6096 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.ts @@ -416,6 +416,9 @@ export class MailServerComponent extends PageComponent implements OnInit, OnDest private get mailSettingsFormValue(): MailServerSettings { const formValue = this.mailSettings.getRawValue() as Required; delete formValue.changePassword; + if (!isDefinedAndNotNull(formValue.password)) { + delete formValue.password; + } return formValue; } diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html index bc5ffd3ae5..61a3dfc848 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html @@ -37,7 +37,7 @@
- + @@ -59,7 +59,7 @@
-
@@ -100,7 +100,7 @@ [copyText]="redirectURI(domainInfo)" tooltipText="{{ 'admin.oauth2.copy-redirect-uri' | translate }}" tooltipPosition="above" - mdiIcon="mdi:clipboard-arrow-left"> + icon="mdi:clipboard-arrow-left"> @@ -112,7 +112,7 @@ [copyText]="redirectURIMixed(domainInfo)" tooltipText="{{ 'admin.oauth2.copy-redirect-uri' | translate }}" tooltipPosition="above" - mdiIcon="mdi:clipboard-arrow-left"> + icon="mdi:clipboard-arrow-left">
@@ -146,7 +146,7 @@ admin.oauth2.no-mobile-apps
-
@@ -171,7 +171,7 @@ [copyText]="mobileInfo.get('appSecret').value" tooltipText="{{ 'admin.oauth2.copy-mobile-app-secret' | translate }}" tooltipPosition="above" - mdiIcon="mdi:clipboard-arrow-left"> + icon="mdi:clipboard-arrow-left"> {{ 'admin.oauth2.invalid-mobile-app-secret' | translate }} @@ -203,7 +203,7 @@
admin.oauth2.providers
- diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts index 34f35611b4..d274f6728e 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts @@ -570,4 +570,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha trackByParams(index: number): number { return index; } + + trackByItem(i, item) { + return item; + } } diff --git a/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library-table-config.resolve.ts b/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library-table-config.resolve.ts index 733e5ef2b3..52ded64e29 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library-table-config.resolve.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library-table-config.resolve.ts @@ -84,7 +84,7 @@ export class ResourcesLibraryTableConfigResolver implements Resolve this.translate.instant('resource.delete-resources-text'); this.config.entitiesFetchFunction = pageLink => this.resourceService.getResources(pageLink, this.config.componentsData.resourceType); - this.config.loadEntity = id => this.resourceService.getResource(id.id); + this.config.loadEntity = id => this.resourceService.getResourceInfo(id.id); this.config.saveEntity = resource => this.saveResource(resource); this.config.deleteEntity = id => this.resourceService.deleteResource(id.id); diff --git a/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html b/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html index f955d26f0d..09fa714c24 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html @@ -69,7 +69,9 @@ impleme readonly resourceTypes: ResourceType[] = Object.values(this.resourceType); readonly resourceTypesTranslationMap = ResourceTypeTranslationMap; + maxResourceSize = getCurrentAuthState(this.store).maxResourceSize; + private destroy$ = new Subject(); constructor(protected store: Store, @@ -98,7 +101,6 @@ export class ResourcesLibraryComponent extends EntityComponent impleme } updateForm(entity: Resource) { - this.entity.name = entity.title; if (this.isEdit) { this.entityForm.get('resourceType').disable({emitEvent: false}); if (entity.resourceType !== ResourceType.JS_MODULE) { diff --git a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html index ef29248767..886dd79ef6 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html @@ -51,24 +51,37 @@
admin.password-policy
- - admin.minimum-password-length - - - {{ 'admin.minimum-password-length-required' | translate }} - - - {{ 'admin.minimum-password-length-range' | translate }} - - - {{ 'admin.minimum-password-length-range' | translate }} - - +
+ + admin.minimum-password-length + + + {{ 'admin.minimum-password-length-required' | translate }} + + + {{ 'admin.minimum-password-length-range' | translate }} + + + {{ 'admin.minimum-password-length-range' | translate }} + + + + admin.maximum-password-length + + + + {{ 'admin.maximum-password-length-min' | translate }} + + + {{ 'admin.maximum-password-length-less-min' | translate }} + + +
admin.minimum-uppercase-letters @@ -140,9 +153,16 @@
- - admin.allow-whitespace - +
+ + admin.allow-whitespace + + + + {{'admin.force-reset-password-if-no-valid' | translate}} + + +
diff --git a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts index cdaf0747af..a2d43e1a8f 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts @@ -19,7 +19,14 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { PageComponent } from '@shared/components/page.component'; import { Router } from '@angular/router'; -import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; +import { + AbstractControl, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, ValidationErrors, + ValidatorFn, + Validators +} from '@angular/forms'; import { JwtSettings, SecuritySettings } from '@shared/models/settings.models'; import { AdminService } from '@core/http/admin.service'; import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard'; @@ -28,7 +35,10 @@ import { randomAlphanumeric } from '@core/utils'; import { AuthService } from '@core/auth/auth.service'; import { DialogService } from '@core/services/dialog.service'; import { TranslateService } from '@ngx-translate/core'; -import { Observable, of } from 'rxjs'; +import { forkJoin, Observable, of } from 'rxjs'; +import { MatCheckboxChange } from '@angular/material/checkbox'; +import { AlarmInfo } from '@shared/models/alarm.models'; +import { QueueProcessingStrategyTypes, QueueProcessingStrategyTypesMap } from '@shared/models/queue.models'; @Component({ selector: 'tb-security-settings', @@ -67,14 +77,16 @@ export class SecuritySettingsComponent extends PageComponent implements HasConfi userLockoutNotificationEmail: ['', []], passwordPolicy: this.fb.group( { - minimumLength: [null, [Validators.required, Validators.min(5), Validators.max(50)]], + minimumLength: [null, [Validators.required, Validators.min(6), Validators.max(50)]], + maximumLength: [null, [Validators.min(6), this.maxPasswordValidation()]], minimumUppercaseLetters: [null, Validators.min(0)], minimumLowercaseLetters: [null, Validators.min(0)], minimumDigits: [null, Validators.min(0)], minimumSpecialCharacters: [null, Validators.min(0)], passwordExpirationPeriodDays: [null, Validators.min(0)], passwordReuseFrequencyDays: [null, Validators.min(0)], - allowWhitespaces: [true] + allowWhitespaces: [true], + forceUserToResetPasswordIfNotValid: [false] } ) }); @@ -113,6 +125,18 @@ export class SecuritySettingsComponent extends PageComponent implements HasConfi })).subscribe(() => {}); } + private maxPasswordValidation(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const value: string = control.value; + if (value) { + if (value < control.parent.value?.minimumLength) { + return {lessMin: true}; + } + } + return null; + }; + } + discardSetting() { this.securitySettingsFormGroup.reset(this.securitySettings); } @@ -189,5 +213,4 @@ export class SecuritySettingsComponent extends PageComponent implements HasConfi confirmForm(): UntypedFormGroup { return this.securitySettingsFormGroup.dirty ? this.securitySettingsFormGroup : this.jwtSecuritySettingsFormGroup; } - } diff --git a/ui-ngx/src/app/modules/home/pages/api-usage/api-usage-routing.module.ts b/ui-ngx/src/app/modules/home/pages/api-usage/api-usage-routing.module.ts index a38fbf9958..7d4bb5fe14 100644 --- a/ui-ngx/src/app/modules/home/pages/api-usage/api-usage-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/api-usage/api-usage-routing.module.ts @@ -14,10 +14,21 @@ /// limitations under the License. /// -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { inject, NgModule } from '@angular/core'; +import { ActivatedRouteSnapshot, ResolveFn, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { Authority } from '@shared/models/authority.enum'; import { ApiUsageComponent } from '@home/pages/api-usage/api-usage.component'; +import { Dashboard } from '@shared/models/dashboard.models'; +import { ResourcesService } from '@core/services/resources.service'; +import { Observable } from 'rxjs'; + +const apiUsageDashboardJson = '/assets/dashboard/api_usage.json'; + +export const apiUsageDashboardResolver: ResolveFn = ( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot, + resourcesService = inject(ResourcesService) +): Observable => resourcesService.loadJsonResource(apiUsageDashboardJson); const routes: Routes = [ { @@ -30,6 +41,9 @@ const routes: Routes = [ label: 'api-usage.api-usage', icon: 'insert_chart' } + }, + resolve: { + apiUsageDashboard: apiUsageDashboardResolver } } ]; diff --git a/ui-ngx/src/app/modules/home/pages/api-usage/api-usage.component.ts b/ui-ngx/src/app/modules/home/pages/api-usage/api-usage.component.ts index 0bcae0801b..e84a23fb66 100644 --- a/ui-ngx/src/app/modules/home/pages/api-usage/api-usage.component.ts +++ b/ui-ngx/src/app/modules/home/pages/api-usage/api-usage.component.ts @@ -14,28 +14,24 @@ /// limitations under the License. /// -import { Component, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { PageComponent } from '@shared/components/page.component'; -import apiUsageDashboardJson from '!raw-loader!./api_usage_json.raw'; import { Dashboard } from '@shared/models/dashboard.models'; +import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'tb-api-usage', templateUrl: './api-usage.component.html', styleUrls: ['./api-usage.component.scss'] }) -export class ApiUsageComponent extends PageComponent implements OnInit { +export class ApiUsageComponent extends PageComponent { - apiUsageDashboard: Dashboard; + apiUsageDashboard: Dashboard = this.route.snapshot.data.apiUsageDashboard; - constructor(protected store: Store) { + constructor(protected store: Store, + private route: ActivatedRoute) { super(store); } - - ngOnInit() { - this.apiUsageDashboard = JSON.parse(apiUsageDashboardJson); - } - } diff --git a/ui-ngx/src/app/modules/home/pages/api-usage/api_usage_json.raw b/ui-ngx/src/app/modules/home/pages/api-usage/api_usage_json.raw deleted file mode 100644 index 769849cc9e..0000000000 --- a/ui-ngx/src/app/modules/home/pages/api-usage/api_usage_json.raw +++ /dev/null @@ -1,4774 +0,0 @@ -{ - "title": "Api Usage", - "image": null, - "mobileHide": false, - "mobileOrder": null, - "configuration": { - "description": "", - "widgets": { - "fd6df872-2ddf-0921-3929-2e7f55062fad": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "html_value_card", - "type": "latest", - "title": "New widget", - "sizeX": 7.5, - "sizeY": 3, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "jsExecutionApiState", - "type": "timeseries", - "label": "apiState", - "color": "#2196f3", - "settings": {}, - "_hash": 0.8830669138660703, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value : 'ENABLED';" - }, - { - "name": "jsExecutionLimit", - "type": "timeseries", - "label": "limit", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5463603803546802, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "jsExecutionCount", - "type": "timeseries", - "label": "count", - "color": "#f44336", - "settings": {}, - "_hash": 0.5564241862015964, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "jsExecutionApiState", - "type": "timeseries", - "label": "apiStateClass", - "color": "#ffc107", - "settings": {}, - "_hash": 0.8737107059960671, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value.toLowerCase() : 'enabled';" - }, - { - "name": "jsExecutionApiState", - "type": "timeseries", - "label": "cardId", - "color": "#607d8b", - "settings": {}, - "_hash": 0.051659774305067296, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return (Math.random()*100000).toFixed(0);" - }, - { - "name": "jsExecutionApiState", - "type": "timeseries", - "label": "title", - "color": "#9c27b0", - "settings": {}, - "_hash": 0.7673280949238444, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.javascript}\";" - }, - { - "name": "jsExecutionApiState", - "type": "timeseries", - "label": "unit", - "color": "#8bc34a", - "settings": {}, - "_hash": 0.7926918686567068, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.executions}\";" - } - ] - } - ], - "timewindow": { - "realtime": { - "timewindowMs": 60000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "#666666", - "padding": "0", - "settings": { - "cardHtml": "
\n \n \n
\n
\n
\n
${title}
\n
${apiState}
\n
\n
\n
${unit}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n
\n
", - "cardCss": ".card {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n}\n\n.card > img {\n height: 0;\n}\n\n.card .content {\n flex: 1; \n padding: 13px 13px 0;\n display: flex;\n box-sizing: border-box;\n}\n\n.card .content .column {\n display: flex;\n flex-direction: column; \n justify-content: space-around;\n flex: 1;\n}\n\n.card .content .title-row {\n display: flex;\n flex-direction: row;\n padding-bottom: 10px;\n}\n\n.card .title {\n flex: 1;\n font-size: 20px;\n font-weight: 400;\n color: #666666;\n}\n\n.card .state {\n text-transform: uppercase;\n font-size: 20px;\n font-weight: bold;\n}\n\n.card.enabled .state {\n color: #00B260;\n}\n\n.card.warning .state {\n color: #FFAD6F;\n}\n\n.card.disabled .state {\n color: #F73243;\n}\n\n.card .bar-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.card .bar {\n flex: 1;\n max-height: 30px;\n margin-top: 3.5px;\n margin-bottom: 4px;\n background-color: #F0F0F0;\n border: 1px solid #DADCDB;\n border-radius: 2px;\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2);\n}\n\n.card.enabled .bar {\n border-color: #00B260;\n background-color: #F0FBF7;\n}\n\n.card.warning .bar {\n border-color: #FFAD6F;\n background-color: #FFFAF6;\n}\n\n.card.disabled .bar {\n border-color: #F73243;\n background-color: #FFF0F0;\n}\n\n.card .bar .bar-fill {\n background-color: #F0F0F0;\n border-radius: 2px;\n height: 100%;\n width: 0%;\n}\n\n.card.enabled .bar-fill {\n background-color: #00C46C;\n}\n\n.card.warning .bar-fill {\n background-color: #FFD099;\n}\n\n.card.disabled .bar-fill {\n background-color: #FF9494;\n}\n\n.card .bar-labels {\n height: 20px;\n font-size: 16px;\n color: #666;\n display: flex;\n flex-direction: row;\n}\n\n\n.card .mat-mdc-button-base {\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.card .mdc-button__label {\n pointer-events: none;\n}\n\n.card .action-row {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n padding: 8px 0;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .card .title {\n font-size: 12px;\n }\n .card .state {\n font-size: 12px;\n }\n .card .unit {\n font-size: 8px;\n }\n .card .bar-labels {\n font-size: 8px;\n }\n .card .mat-mdc-button-base {\n font-size: 8px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1599px) {\n .card .title {\n font-size: 14px;\n }\n .card .state {\n font-size: 14px;\n }\n .card .unit {\n font-size: 10px;\n }\n .card .bar-labels {\n font-size: 10px;\n }\n .card .mat-mdc-button-base {\n font-size: 10px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1600px) and (max-width: 1919px) {\n .card .title {\n font-size: 16px;\n }\n .card .state {\n font-size: 16px;\n }\n .card .unit {\n font-size: 12px;\n }\n .card .bar-labels {\n font-size: 12px;\n }\n .card .mat-mdc-button-base {\n font-size: 12px;\n }\n .card .action-row {\n padding: 0;\n }\n} \n\n\n" - }, - "title": "JavaScript functions", - "dropShadow": true, - "enableFullscreen": false, - "widgetStyle": { - "cursor": "default" - }, - "titleStyle": { - "fontSize": "20px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "showLegend": false, - "actions": { - "elementClick": [ - { - "name": "javascript_functions_details", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "javascript_functions", - "setEntityId": false, - "stateEntityParamName": null, - "openRightLayout": false, - "id": "d4961bea-84de-e1af-e50f-666b98d34cd5" - } - ] - }, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "fd6df872-2ddf-0921-3929-2e7f55062fad" - }, - "7e235874-461b-e7c2-2fdd-d8762a020773": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "html_value_card", - "type": "latest", - "title": "New widget", - "sizeX": 7.5, - "sizeY": 3, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "dbApiState", - "type": "timeseries", - "label": "apiState", - "color": "#2196f3", - "settings": {}, - "_hash": 0.8830669138660703, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value : 'ENABLED';" - }, - { - "name": "storageDataPointsLimit", - "type": "timeseries", - "label": "limit", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5463603803546802, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "storageDataPointsCount", - "type": "timeseries", - "label": "count", - "color": "#f44336", - "settings": {}, - "_hash": 0.5564241862015964, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "dbApiState", - "type": "timeseries", - "label": "apiStateClass", - "color": "#ffc107", - "settings": {}, - "_hash": 0.8737107059960671, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value.toLowerCase() : 'enabled';" - }, - { - "name": "dbApiState", - "type": "timeseries", - "label": "cardId", - "color": "#607d8b", - "settings": {}, - "_hash": 0.051659774305067296, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return (Math.random()*100000).toFixed(0);" - }, - { - "name": "dbApiState", - "type": "timeseries", - "label": "title", - "color": "#9c27b0", - "settings": {}, - "_hash": 0.6301889725474652, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.telemetry}\";" - }, - { - "name": "dbApiState", - "type": "timeseries", - "label": "unit", - "color": "#8bc34a", - "settings": {}, - "_hash": 0.0027742924142306613, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.data-points-storage-days}\";" - } - ] - } - ], - "timewindow": { - "realtime": { - "timewindowMs": 60000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "#666666", - "padding": "0", - "settings": { - "cardHtml": "
\n \n \n
\n
\n
\n
${title}
\n
${apiState}
\n
\n
\n
${unit}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n
\n
", - "cardCss": ".card {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n}\n\n.card > img {\n height: 0;\n}\n\n.card .content {\n flex: 1; \n padding: 13px 13px 0;\n display: flex;\n box-sizing: border-box;\n}\n\n.card .content .column {\n display: flex;\n flex-direction: column; \n justify-content: space-around;\n flex: 1;\n}\n\n.card .content .title-row {\n display: flex;\n flex-direction: row;\n padding-bottom: 10px;\n}\n\n.card .title {\n flex: 1;\n font-size: 20px;\n font-weight: 400;\n color: #666666;\n}\n\n.card .state {\n text-transform: uppercase;\n font-size: 20px;\n font-weight: bold;\n}\n\n.card.enabled .state {\n color: #00B260;\n}\n\n.card.warning .state {\n color: #FFAD6F;\n}\n\n.card.disabled .state {\n color: #F73243;\n}\n\n.card .bar-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.card .bar {\n flex: 1;\n max-height: 30px;\n margin-top: 3.5px;\n margin-bottom: 4px;\n background-color: #F0F0F0;\n border: 1px solid #DADCDB;\n border-radius: 2px;\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2);\n}\n\n.card.enabled .bar {\n border-color: #00B260;\n background-color: #F0FBF7;\n}\n\n.card.warning .bar {\n border-color: #FFAD6F;\n background-color: #FFFAF6;\n}\n\n.card.disabled .bar {\n border-color: #F73243;\n background-color: #FFF0F0;\n}\n\n.card .bar .bar-fill {\n background-color: #F0F0F0;\n border-radius: 2px;\n height: 100%;\n width: 0%;\n}\n\n.card.enabled .bar-fill {\n background-color: #00C46C;\n}\n\n.card.warning .bar-fill {\n background-color: #FFD099;\n}\n\n.card.disabled .bar-fill {\n background-color: #FF9494;\n}\n\n.card .bar-labels {\n height: 20px;\n font-size: 16px;\n color: #666;\n display: flex;\n flex-direction: row;\n}\n\n\n.card .mat-mdc-button-base {\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.card .mdc-button__label {\n pointer-events: none;\n}\n\n.card .action-row {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n padding: 8px 0;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .card .title {\n font-size: 12px;\n }\n .card .state {\n font-size: 12px;\n }\n .card .unit {\n font-size: 8px;\n }\n .card .bar-labels {\n font-size: 8px;\n }\n .card .mat-mdc-button-base {\n font-size: 8px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1599px) {\n .card .title {\n font-size: 14px;\n }\n .card .state {\n font-size: 14px;\n }\n .card .unit {\n font-size: 10px;\n }\n .card .bar-labels {\n font-size: 10px;\n }\n .card .mat-mdc-button-base {\n font-size: 10px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1600px) and (max-width: 1919px) {\n .card .title {\n font-size: 16px;\n }\n .card .state {\n font-size: 16px;\n }\n .card .unit {\n font-size: 12px;\n }\n .card .bar-labels {\n font-size: 12px;\n }\n .card .mat-mdc-button-base {\n font-size: 12px;\n }\n .card .action-row {\n padding: 0;\n }\n} \n\n\n" - }, - "title": "Telemetry persistence", - "dropShadow": true, - "enableFullscreen": false, - "widgetStyle": { - "cursor": "default" - }, - "titleStyle": { - "fontSize": "20px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "showLegend": false, - "actions": { - "elementClick": [ - { - "name": "telemetry_persistence_details", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "telemetry_persistence", - "setEntityId": false, - "stateEntityParamName": null, - "openRightLayout": false, - "id": "6248831c-5b3f-8879-8548-afcf43f10610" - } - ] - }, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "7e235874-461b-e7c2-2fdd-d8762a020773" - }, - "08545554-a0e8-05c7-66df-6000cfeff8a4": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "html_value_card", - "type": "latest", - "title": "New widget", - "sizeX": 7.5, - "sizeY": 3, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "ruleEngineApiState", - "type": "timeseries", - "label": "apiState", - "color": "#2196f3", - "settings": {}, - "_hash": 0.8830669138660703, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value : 'ENABLED';" - }, - { - "name": "ruleEngineExecutionLimit", - "type": "timeseries", - "label": "limit", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5463603803546802, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "ruleEngineExecutionCount", - "type": "timeseries", - "label": "count", - "color": "#f44336", - "settings": {}, - "_hash": 0.5564241862015964, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "ruleEngineApiState", - "type": "timeseries", - "label": "apiStateClass", - "color": "#ffc107", - "settings": {}, - "_hash": 0.8737107059960671, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value.toLowerCase() : 'enabled';" - }, - { - "name": "ruleEngineApiState", - "type": "timeseries", - "label": "cardId", - "color": "#607d8b", - "settings": {}, - "_hash": 0.051659774305067296, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return (Math.random()*100000).toFixed(0);" - }, - { - "name": "ruleEngineApiState", - "type": "timeseries", - "label": "title", - "color": "#9c27b0", - "settings": {}, - "_hash": 0.3551317421302518, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.rule-engine}\";" - }, - { - "name": "ruleEngineApiState", - "type": "timeseries", - "label": "unit", - "color": "#8bc34a", - "settings": {}, - "_hash": 0.5100381746798048, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.executions}\";" - } - ] - } - ], - "timewindow": { - "realtime": { - "timewindowMs": 60000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "#666666", - "padding": "0", - "settings": { - "cardCss": ".card {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n}\n\n.card > img {\n height: 0;\n}\n\n.card .content {\n flex: 1; \n padding: 13px 13px 0;\n display: flex;\n box-sizing: border-box;\n}\n\n.card .content .column {\n display: flex;\n flex-direction: column; \n justify-content: space-around;\n flex: 1;\n}\n\n.card .content .title-row {\n display: flex;\n flex-direction: row;\n padding-bottom: 10px;\n}\n\n.card .title {\n flex: 1;\n font-size: 20px;\n font-weight: 400;\n color: #666666;\n}\n\n.card .state {\n text-transform: uppercase;\n font-size: 20px;\n font-weight: bold;\n}\n\n.card.enabled .state {\n color: #00B260;\n}\n\n.card.warning .state {\n color: #FFAD6F;\n}\n\n.card.disabled .state {\n color: #F73243;\n}\n\n.card .bar-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.card .bar {\n flex: 1;\n max-height: 30px;\n margin-top: 3.5px;\n margin-bottom: 4px;\n background-color: #F0F0F0;\n border: 1px solid #DADCDB;\n border-radius: 2px;\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2);\n}\n\n.card.enabled .bar {\n border-color: #00B260;\n background-color: #F0FBF7;\n}\n\n.card.warning .bar {\n border-color: #FFAD6F;\n background-color: #FFFAF6;\n}\n\n.card.disabled .bar {\n border-color: #F73243;\n background-color: #FFF0F0;\n}\n\n.card .bar .bar-fill {\n background-color: #F0F0F0;\n border-radius: 2px;\n height: 100%;\n width: 0%;\n}\n\n.card.enabled .bar-fill {\n background-color: #00C46C;\n}\n\n.card.warning .bar-fill {\n background-color: #FFD099;\n}\n\n.card.disabled .bar-fill {\n background-color: #FF9494;\n}\n\n.card .bar-labels {\n height: 20px;\n font-size: 16px;\n color: #666;\n display: flex;\n flex-direction: row;\n}\n\n\n.card .mat-mdc-button-base {\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.card .mdc-button__label {\n pointer-events: none;\n}\n\n.card .action-row {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n padding: 8px 0;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .card .title {\n font-size: 12px;\n }\n .card .state {\n font-size: 12px;\n }\n .card .unit {\n font-size: 8px;\n }\n .card .bar-labels {\n font-size: 8px;\n }\n .card .mat-mdc-button-base {\n font-size: 8px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1599px) {\n .card .title {\n font-size: 14px;\n }\n .card .state {\n font-size: 14px;\n }\n .card .unit {\n font-size: 10px;\n }\n .card .bar-labels {\n font-size: 10px;\n }\n .card .mat-mdc-button-base {\n font-size: 10px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1600px) and (max-width: 1919px) {\n .card .title {\n font-size: 16px;\n }\n .card .state {\n font-size: 16px;\n }\n .card .unit {\n font-size: 12px;\n }\n .card .bar-labels {\n font-size: 12px;\n }\n .card .mat-mdc-button-base {\n font-size: 12px;\n }\n .card .action-row {\n padding: 0;\n }\n} \n\n\n", - "cardHtml": "
\n \n \n
\n
\n
\n
${title}
\n
${apiState}
\n
\n
\n
${unit}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n
\n
" - }, - "title": "Rule Engine execution", - "dropShadow": true, - "enableFullscreen": false, - "widgetStyle": { - "cursor": "default" - }, - "titleStyle": { - "fontSize": "20px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "showLegend": false, - "actions": { - "elementClick": [ - { - "name": "rule_engine_execution_details", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "rule_engine_execution", - "setEntityId": false, - "stateEntityParamName": null, - "openRightLayout": false, - "id": "3c30248f-0cd8-fb97-a917-bc1e09984a79" - }, - { - "name": "rule_engine_statistics_details", - "icon": "show_chart", - "type": "openDashboardState", - "targetDashboardStateId": "rule_engine_statistics", - "setEntityId": false, - "stateEntityParamName": null, - "openRightLayout": false, - "id": "04e4565a-9e24-23df-f376-f2ec70a8165f" - } - ] - }, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "08545554-a0e8-05c7-66df-6000cfeff8a4" - }, - "a245c67e-53ec-d299-fa89-69fe2062ccb2": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "html_value_card", - "type": "latest", - "title": "New widget", - "sizeX": 7.5, - "sizeY": 3, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "transportApiState", - "type": "timeseries", - "label": "apiState", - "color": "#2196f3", - "settings": {}, - "_hash": 0.8830669138660703, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value : 'ENABLED';" - }, - { - "name": "transportMsgLimit", - "type": "timeseries", - "label": "limit", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5463603803546802, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "transportMsgCount", - "type": "timeseries", - "label": "count", - "color": "#f44336", - "settings": {}, - "_hash": 0.5564241862015964, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "transportApiState", - "type": "timeseries", - "label": "apiStateClass", - "color": "#ffc107", - "settings": {}, - "_hash": 0.8737107059960671, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value.toLowerCase() : 'enabled';" - }, - { - "name": "transportApiState", - "type": "timeseries", - "label": "cardId", - "color": "#607d8b", - "settings": {}, - "_hash": 0.051659774305067296, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return (Math.random()*100000).toFixed(0);" - }, - { - "name": "transportDataPointsLimit", - "type": "timeseries", - "label": "pointsLimit", - "color": "#9c27b0", - "settings": {}, - "_hash": 0.22082255831864894, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "transportDataPointsCount", - "type": "timeseries", - "label": "pointsCount", - "color": "#8bc34a", - "settings": {}, - "_hash": 0.6340356364819146, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "transportApiState", - "type": "timeseries", - "label": "title", - "color": "#3f51b5", - "settings": {}, - "_hash": 0.6894070537030252, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.transport}\";" - } - ] - } - ], - "timewindow": { - "realtime": { - "timewindowMs": 60000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "#666666", - "padding": "0", - "settings": { - "cardHtml": "
\n \n \n
\n
\n
\n
\n ${title}\n
\n
${apiState}
\n
\n
\n
\n
\n
{i18n:api-usage.messages}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
{i18n:api-usage.data-points}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n
\n
", - "cardCss": ".card {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n}\n\n.card > img {\n height: 0;\n}\n\n.card .content {\n flex: 1; \n padding: 13px 13px 0;\n display: flex;\n box-sizing: border-box;\n}\n\n.card .content .column {\n display: flex;\n flex-direction: column; \n justify-content: space-around;\n flex: 1;\n}\n\n.card .content .title-row {\n display: flex;\n flex-direction: row;\n padding-bottom: 10px;\n}\n\n.card .title {\n flex: 1;\n font-size: 20px;\n font-weight: 400;\n color: #666666;\n}\n\n.card .state {\n text-transform: uppercase;\n font-size: 20px;\n font-weight: bold;\n}\n\n.card.enabled .state {\n color: #00B260;\n}\n\n.card.warning .state {\n color: #FFAD6F;\n}\n\n.card.disabled .state {\n color: #F73243;\n}\n\n.card .bars-row {\n flex: 1;\n display: flex;\n flex-direction: row;\n}\n\n.card .bar-column {\n flex: 1;\n display: flex;\n flex-direction: column;\n}\n\n.card .bar-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.card .bar {\n flex: 1;\n max-height: 30px;\n margin-top: 3.5px;\n margin-bottom: 4px;\n background-color: #F0F0F0;\n border: 1px solid #DADCDB;\n border-radius: 2px;\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2);\n}\n\n.card.enabled .bar {\n border-color: #00B260;\n background-color: #F0FBF7;\n}\n\n.card.warning .bar {\n border-color: #FFAD6F;\n background-color: #FFFAF6;\n}\n\n.card.disabled .bar {\n border-color: #F73243;\n background-color: #FFF0F0;\n}\n\n.card .bar .bar-fill {\n background-color: #F0F0F0;\n border-radius: 2px;\n height: 100%;\n width: 0%;\n}\n\n.card.enabled .bar-fill {\n background-color: #00C46C;\n}\n\n.card.warning .bar-fill {\n background-color: #FFD099;\n}\n\n.card.disabled .bar-fill {\n background-color: #FF9494;\n}\n\n.card .bar-labels {\n height: 20px;\n font-size: 16px;\n color: #666;\n display: flex;\n flex-direction: row;\n}\n\n.card .mat-mdc-button-base {\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.card .mdc-button__label {\n pointer-events: none;\n}\n\n.card .action-row {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n padding: 8px 0;\n}\n\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .card .title {\n font-size: 12px;\n }\n .card .state {\n font-size: 12px;\n }\n .card .unit {\n font-size: 8px;\n }\n .card .bar-labels {\n font-size: 6px;\n }\n .card .mat-mdc-button-base {\n font-size: 8px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1599px) {\n .card .title {\n font-size: 14px;\n }\n .card .state {\n font-size: 14px;\n }\n .card .unit {\n font-size: 10px;\n }\n .card .bar-labels {\n font-size: 8px;\n }\n .card .mat-mdc-button-base {\n font-size: 10px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1600px) and (max-width: 1919px) {\n .card .title {\n font-size: 16px;\n }\n .card .state {\n font-size: 16px;\n }\n .card .unit {\n font-size: 12px;\n }\n .card .bar-labels {\n font-size: 12px;\n }\n .card .mat-mdc-button-base {\n font-size: 12px;\n }\n .card .action-row {\n padding: 0;\n }\n} \n\n" - }, - "title": "Transport", - "dropShadow": true, - "enableFullscreen": false, - "widgetStyle": { - "cursor": "default" - }, - "titleStyle": { - "fontSize": "20px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "showLegend": false, - "actions": { - "elementClick": [ - { - "name": "transport_details", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "transport", - "setEntityId": false, - "stateEntityParamName": null, - "openRightLayout": false, - "id": "46b7cefe-e1f2-67c1-4055-3a214520f869" - } - ] - }, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "a245c67e-53ec-d299-fa89-69fe2062ccb2" - }, - "a151ae60-0326-6116-d818-9070dda8e9c7": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "html_value_card", - "type": "latest", - "title": "New widget", - "sizeX": 7.5, - "sizeY": 3, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "alarmApiState", - "type": "timeseries", - "label": "apiState", - "color": "#2196f3", - "settings": {}, - "_hash": 0.8830669138660703, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value : 'ENABLED';" - }, - { - "name": "createdAlarmsLimit", - "type": "timeseries", - "label": "limit", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5463603803546802, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "createdAlarmsCount", - "type": "timeseries", - "label": "count", - "color": "#f44336", - "settings": {}, - "_hash": 0.5564241862015964, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "alarmApiState", - "type": "timeseries", - "label": "apiStateClass", - "color": "#ffc107", - "settings": {}, - "_hash": 0.8737107059960671, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value.toLowerCase() : 'enabled';" - }, - { - "name": "alarmApiState", - "type": "timeseries", - "label": "cardId", - "color": "#607d8b", - "settings": {}, - "_hash": 0.051659774305067296, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return (Math.random()*100000).toFixed(0);" - }, - { - "name": "alarmApiState", - "type": "timeseries", - "label": "title", - "color": "#9c27b0", - "settings": {}, - "_hash": 0.43439375716502227, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.alarm}\";" - }, - { - "name": "alarmApiState", - "type": "timeseries", - "label": "unit", - "color": "#8bc34a", - "settings": {}, - "_hash": 0.9964061963495883, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.alarms-created}\";" - } - ] - } - ], - "timewindow": { - "realtime": { - "timewindowMs": 60000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "#666666", - "padding": "0", - "settings": { - "cardHtml": "
\n \n \n
\n
\n
\n
${title}
\n
${apiState}
\n
\n
\n
${unit}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n
\n
", - "cardCss": ".card {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n}\n\n.card > img {\n height: 0;\n}\n\n.card .content {\n flex: 1; \n padding: 13px 13px 0;\n display: flex;\n box-sizing: border-box;\n}\n\n.card .content .column {\n display: flex;\n flex-direction: column; \n justify-content: space-around;\n flex: 1;\n}\n\n.card .content .title-row {\n display: flex;\n flex-direction: row;\n padding-bottom: 10px;\n}\n\n.card .title {\n flex: 1;\n font-size: 20px;\n font-weight: 400;\n color: #666666;\n}\n\n.card .state {\n text-transform: uppercase;\n font-size: 20px;\n font-weight: bold;\n}\n\n.card.enabled .state {\n color: #00B260;\n}\n\n.card.warning .state {\n color: #FFAD6F;\n}\n\n.card.disabled .state {\n color: #F73243;\n}\n\n.card .bar-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.card .bar {\n flex: 1;\n max-height: 30px;\n margin-top: 3.5px;\n margin-bottom: 4px;\n background-color: #F0F0F0;\n border: 1px solid #DADCDB;\n border-radius: 2px;\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2);\n}\n\n.card.enabled .bar {\n border-color: #00B260;\n background-color: #F0FBF7;\n}\n\n.card.warning .bar {\n border-color: #FFAD6F;\n background-color: #FFFAF6;\n}\n\n.card.disabled .bar {\n border-color: #F73243;\n background-color: #FFF0F0;\n}\n\n.card .bar .bar-fill {\n background-color: #F0F0F0;\n border-radius: 2px;\n height: 100%;\n width: 0%;\n}\n\n.card.enabled .bar-fill {\n background-color: #00C46C;\n}\n\n.card.warning .bar-fill {\n background-color: #FFD099;\n}\n\n.card.disabled .bar-fill {\n background-color: #FF9494;\n}\n\n.card .bar-labels {\n height: 20px;\n font-size: 16px;\n color: #666;\n display: flex;\n flex-direction: row;\n}\n\n\n.card .mat-mdc-button-base {\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.card .mdc-button__label {\n pointer-events: none;\n}\n\n.card .action-row {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n padding: 8px 0;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .card .title {\n font-size: 12px;\n }\n .card .state {\n font-size: 12px;\n }\n .card .unit {\n font-size: 8px;\n }\n .card .bar-labels {\n font-size: 8px;\n }\n .card .mat-mdc-button-base {\n font-size: 8px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1599px) {\n .card .title {\n font-size: 14px;\n }\n .card .state {\n font-size: 14px;\n }\n .card .unit {\n font-size: 10px;\n }\n .card .bar-labels {\n font-size: 10px;\n }\n .card .mat-mdc-button-base {\n font-size: 10px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1600px) and (max-width: 1919px) {\n .card .title {\n font-size: 16px;\n }\n .card .state {\n font-size: 16px;\n }\n .card .unit {\n font-size: 12px;\n }\n .card .bar-labels {\n font-size: 12px;\n }\n .card .mat-mdc-button-base {\n font-size: 12px;\n }\n .card .action-row {\n padding: 0;\n }\n} \n\n\n" - }, - "title": "Alarm created", - "dropShadow": true, - "enableFullscreen": false, - "widgetStyle": { - "cursor": "default" - }, - "titleStyle": { - "fontSize": "20px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "showLegend": false, - "actions": { - "elementClick": [ - { - "name": "email_messages_details", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "alarms_created", - "setEntityId": false, - "stateEntityParamName": null, - "openInSeparateDialog": null, - "dialogTitle": null, - "dialogHideDashboardToolbar": true, - "dialogWidth": null, - "dialogHeight": null, - "openRightLayout": false, - "id": "946ba769-84ac-1507-6baa-94701de8967b" - } - ] - }, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "a151ae60-0326-6116-d818-9070dda8e9c7" - }, - "68e16e98-0420-f72c-4848-41dedffd3904": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "ruleEngineExecutionCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.rule-engine-executions}", - "color": "#ab00ff", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 0, - "realtime": { - "timewindowMs": 86400000, - "interval": 3600000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", - "min": 0, - "tickDecimals": 0 - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 2400000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "{i18n:api-usage.rule-engine-hourly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "showLegend": true, - "actions": { - "headerButton": [ - { - "name": "{i18n:api-usage.view-statistics}", - "icon": "show_chart", - "type": "openDashboardState", - "targetDashboardStateId": "rule_engine_statistics", - "setEntityId": false, - "stateEntityParamName": null, - "openRightLayout": false, - "id": "f9f08190-9ed9-d802-5b7a-c57ff84b5648" - }, - { - "name": "{i18n:api-usage.view-details}", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "rule_engine_execution", - "setEntityId": false, - "stateEntityParamName": null, - "openRightLayout": false, - "id": "1aec196b-44ba-ddf4-c4dc-c3f60c1eb6fc" - } - ] - }, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "row": 0, - "col": 0, - "id": "68e16e98-0420-f72c-4848-41dedffd3904" - }, - "2aa6b499-6e27-b315-6833-89c4d58485ce": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "transportMsgCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.transport-messages}", - "color": "#2196f3", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "transportDataPointsCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.transport-data-points}", - "color": "#4caf50", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.46849996721308895, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 0, - "realtime": { - "timewindowMs": 86400000, - "interval": 3600000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", - "min": 0, - "tickDecimals": 0 - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": true, - "tooltipIndividual": false, - "defaultBarWidth": 2400000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "tooltipCumulative": false - }, - "title": "{i18n:api-usage.transport-hourly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "showLegend": true, - "actions": { - "headerButton": [ - { - "name": "{i18n:api-usage.view-details}", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "transport", - "setEntityId": false, - "stateEntityParamName": null, - "openRightLayout": false, - "id": "6ef12f6a-0266-25cf-6ca5-5dcb772252c6" - } - ] - }, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "row": 0, - "col": 0, - "id": "2aa6b499-6e27-b315-6833-89c4d58485ce" - }, - "d890cea3-fba0-6474-9a21-fa780230dc62": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "jsExecutionCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.javascript-executions}", - "color": "#ff9900", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 0, - "realtime": { - "timewindowMs": 86400000, - "interval": 3600000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", - "min": 0, - "tickDecimals": 0 - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 2400000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "{i18n:api-usage.javascript-functions-hourly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "showLegend": true, - "actions": { - "headerButton": [ - { - "name": "{i18n:api-usage.view-details}", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "javascript_functions", - "setEntityId": false, - "stateEntityParamName": null, - "openRightLayout": false, - "id": "4687d3f6-8800-a3b6-26e5-0d33f3b828a9" - } - ] - }, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "row": 0, - "col": 0, - "id": "d890cea3-fba0-6474-9a21-fa780230dc62" - }, - "84b6cfa5-1449-e0f2-3560-f810d2dd7ead": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "storageDataPointsCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.data-points-storage-days}", - "color": "#1039ee", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 0, - "realtime": { - "timewindowMs": 86400000, - "interval": 3600000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", - "min": 0, - "tickDecimals": 0 - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 2400000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "{i18n:api-usage.telemetry-persistence-hourly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "showLegend": true, - "actions": { - "headerButton": [ - { - "name": "{i18n:api-usage.view-details}", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "telemetry_persistence", - "setEntityId": false, - "stateEntityParamName": null, - "openRightLayout": false, - "id": "16707efb-e572-bd02-c219-55fc1b0f672a" - } - ] - }, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "row": 0, - "col": 0, - "id": "84b6cfa5-1449-e0f2-3560-f810d2dd7ead" - }, - "d296b566-a000-7402-ae9d-c815381c5435": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "createdAlarmsCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.alarms-created}", - "color": "#d35a00", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 0, - "realtime": { - "timewindowMs": 86400000, - "interval": 3600000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", - "min": 0, - "tickDecimals": 0 - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 2400000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "{i18n:api-usage.alarms-created-hourly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "showLegend": true, - "actions": { - "headerButton": [ - { - "name": "{i18n:api-usage.view-details}", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "alarms_created", - "setEntityId": false, - "stateEntityParamName": null, - "openInSeparateDialog": null, - "dialogTitle": null, - "dialogHideDashboardToolbar": true, - "dialogWidth": null, - "dialogHeight": null, - "openRightLayout": false, - "id": "371882f9-ea23-3abc-fca8-9449c5dfdd6b" - } - ] - }, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "row": 0, - "col": 0, - "id": "d296b566-a000-7402-ae9d-c815381c5435" - }, - "00a02464-9509-911b-3b5e-21fb37629822": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "emailCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.email-messages}", - "color": "#4caf50", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.1348755140779876, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "smsCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.sms-messages}", - "color": "#f36021", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 0, - "realtime": { - "timewindowMs": 86400000, - "interval": 3600000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", - "min": 0, - "tickDecimals": 0 - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 2400000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "{i18n:api-usage.notifications-hourly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "showLegend": true, - "actions": { - "headerButton": [ - { - "name": "{i18n:api-usage.view-details}", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "notifications", - "setEntityId": false, - "stateEntityParamName": null, - "openInSeparateDialog": null, - "dialogTitle": null, - "dialogHideDashboardToolbar": true, - "dialogWidth": null, - "dialogHeight": null, - "openRightLayout": false, - "id": "49aefac0-ec5e-d6f3-f39c-8744759f4b19" - } - ] - }, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "row": 0, - "col": 0, - "id": "00a02464-9509-911b-3b5e-21fb37629822" - }, - "74199074-7873-6c6a-2a51-3fc614769f03": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "ruleEngineExecutionCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.rule-engine-executions}", - "color": "#ab00ff", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000 - }, - "aggregation": { - "type": "SUM", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", - "min": 0, - "tickDecimals": 0 - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 1800000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "{i18n:api-usage.rule-engine-daily-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "showLegend": true, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "row": 0, - "col": 0, - "id": "74199074-7873-6c6a-2a51-3fc614769f03" - }, - "00006bc3-8a8d-b55e-ed39-e318f1bcd090": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "ruleEngineExecutionCount", - "type": "timeseries", - "label": "{i18n:api-usage.rule-engine-executions}", - "color": "#ab00ff", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 31536000000, - "interval": 1000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", - "min": 0, - "tickDecimals": 0 - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 900000000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "{i18n:api-usage.rule-engine-monthly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "showLegend": true, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "row": 0, - "col": 0, - "id": "00006bc3-8a8d-b55e-ed39-e318f1bcd090" - }, - "5f5ca59c-e507-5301-5910-7ad8cd34df40": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "jsExecutionCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.javascript-executions}", - "color": "#ff9900", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000 - }, - "aggregation": { - "type": "SUM", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 1800000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "{i18n:api-usage.javascript-functions-daily-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "showLegend": true, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "row": 0, - "col": 0, - "id": "5f5ca59c-e507-5301-5910-7ad8cd34df40" - }, - "ada32ee9-44ed-48d1-c368-fd0c94b7607f": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "jsExecutionCount", - "type": "timeseries", - "label": "{i18n:api-usage.javascript-executions}", - "color": "#ff9900", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 31536000000, - "interval": 1000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 900000000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "{i18n:api-usage.javascript-functions-monthly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "showLegend": true, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "row": 0, - "col": 0, - "id": "ada32ee9-44ed-48d1-c368-fd0c94b7607f" - }, - "98bae68b-0f35-72f2-a428-9b06889f1554": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "transportMsgCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.transport-messages}", - "color": "#2196f3", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "transportDataPointsCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.transport-data-points}", - "color": "#4caf50", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.46849996721308895, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000 - }, - "aggregation": { - "type": "SUM", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", - "min": 0, - "tickDecimals": 0 - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": true, - "tooltipIndividual": false, - "defaultBarWidth": 1800000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "tooltipCumulative": false - }, - "title": "{i18n:api-usage.transport-daily-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "showLegend": true, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "row": 0, - "col": 0, - "id": "98bae68b-0f35-72f2-a428-9b06889f1554" - }, - "e61e5477-5a09-cc25-966b-f613d81da833": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "transportMsgCount", - "type": "timeseries", - "label": "{i18n:api-usage.transport-messages}", - "color": "#2196f3", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "transportDataPointsCount", - "type": "timeseries", - "label": "{i18n:api-usage.transport-data-points}", - "color": "#4caf50", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.46849996721308895, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 31536000000, - "interval": 2592000000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", - "min": 0, - "tickDecimals": 0 - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": true, - "tooltipIndividual": false, - "defaultBarWidth": 900000000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "tooltipCumulative": false - }, - "title": "{i18n:api-usage.transport-monthly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "showLegend": true, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "row": 0, - "col": 0, - "id": "e61e5477-5a09-cc25-966b-f613d81da833" - }, - "85fe0738-5326-f069-ab3f-30594bde5fed": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "storageDataPointsCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.data-points-storage-days}", - "color": "#1039ee", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000 - }, - "aggregation": { - "type": "SUM", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 1800000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "{i18n:api-usage.telemetry-persistence-daily-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "showLegend": true, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "row": 0, - "col": 0, - "id": "85fe0738-5326-f069-ab3f-30594bde5fed" - }, - "eaeb381a-437e-f6e9-60c9-6bc8826fdd44": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "storageDataPointsCount", - "type": "timeseries", - "label": "{i18n:api-usage.data-points-storage-days}", - "color": "#1039ee", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 31536000000, - "interval": 1000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 900000000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "{i18n:api-usage.telemetry-persistence-monthly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "showLegend": true, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "row": 0, - "col": 0, - "id": "eaeb381a-437e-f6e9-60c9-6bc8826fdd44" - }, - "4b798823-b97d-9d6a-59dc-fcafd897fc23": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "createdAlarmsCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.alarms-created}", - "color": "#d35a00", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000 - }, - "aggregation": { - "type": "SUM", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 1800000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "{i18n:api-usage.alarms-created-daily-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "showLegend": true, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "row": 0, - "col": 0, - "id": "4b798823-b97d-9d6a-59dc-fcafd897fc23" - }, - "6a981580-7490-19dd-f937-b64cbf67a982": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "createdAlarmsCount", - "type": "timeseries", - "label": "{i18n:api-usage.alarms-created}", - "color": "#d35a00", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 31536000000, - "interval": 1000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 900000000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "{i18n:api-usage.alarms-created-monthly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "showLegend": true, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "row": 0, - "col": 0, - "id": "6a981580-7490-19dd-f937-b64cbf67a982" - }, - "7302df65-1b0c-579e-bbdb-145126ae3392": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "smsCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.sms-messages}", - "color": "#f36021", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000 - }, - "aggregation": { - "type": "SUM", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 1800000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "{i18n:api-usage.sms-messages-daily-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "showLegend": true, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "row": 0, - "col": 0, - "id": "7302df65-1b0c-579e-bbdb-145126ae3392" - }, - "fdb385e7-14fe-fc9f-ebdc-b400f26fc66b": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "smsCount", - "type": "timeseries", - "label": "{i18n:api-usage.sms-messages}", - "color": "#f36021", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 31536000000, - "interval": 1000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 900000000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "{i18n:api-usage.sms-messages-monthly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "showLegend": true, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "row": 0, - "col": 0, - "id": "fdb385e7-14fe-fc9f-ebdc-b400f26fc66b" - }, - "2408ad30-163e-8221-08e1-a82b638be564": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "basic_timeseries", - "type": "timeseries", - "title": "New widget", - "sizeX": 12, - "sizeY": 7, - "config": { - "datasources": [ - { - "type": "entity", - "dataKeys": [ - { - "name": "successfulMsgs", - "type": "timeseries", - "label": "{i18n:api-usage.successful}", - "color": "#4caf50", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.15490750967648736 - }, - { - "name": "failedMsgs", - "type": "timeseries", - "label": "{i18n:api-usage.permanent-failures}", - "color": "#ef5350", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.4186621166514697 - }, - { - "name": "tmpFailed", - "type": "timeseries", - "label": "{i18n:api-usage.processing-failures}", - "color": "#ffc107", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.49891007198715376 - } - ], - "entityAliasId": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f" - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 0, - "realtime": { - "timewindowMs": 3600000, - "interval": 1000 - }, - "aggregation": { - "type": "NONE", - "limit": 10000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "Queue Stats", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "mobileHeight": null, - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "widgetStyle": {}, - "useDashboardTimewindow": false, - "displayTimewindow": true, - "showLegend": true, - "actions": {}, - "legendConfig": { - "direction": "column", - "position": "bottom", - "showMin": true, - "showMax": true, - "showAvg": false, - "showTotal": true - } - }, - "id": "2408ad30-163e-8221-08e1-a82b638be564" - }, - "e43dcfe1-b970-6a11-ce0e-5769f3eb5e88": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "basic_timeseries", - "type": "timeseries", - "title": "New widget", - "sizeX": 12, - "sizeY": 7, - "config": { - "datasources": [ - { - "type": "entity", - "dataKeys": [ - { - "name": "timeoutMsgs", - "type": "timeseries", - "label": "{i18n:api-usage.permanent-timeouts}", - "color": "#4caf50", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.565222981550328 - }, - { - "name": "tmpTimeout", - "type": "timeseries", - "label": "{i18n:api-usage.processing-timeouts}", - "color": "#9c27b0", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.2679547062508352 - } - ], - "entityAliasId": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f" - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 0, - "realtime": { - "timewindowMs": 3600000, - "interval": 1000 - }, - "aggregation": { - "type": "NONE", - "limit": 10000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "Processing Failures and Timeouts", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "mobileHeight": null, - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "widgetStyle": {}, - "useDashboardTimewindow": false, - "displayTimewindow": true, - "showLegend": true, - "actions": {}, - "legendConfig": { - "direction": "column", - "position": "bottom", - "showMin": true, - "showMax": true, - "showAvg": false, - "showTotal": true - } - }, - "id": "e43dcfe1-b970-6a11-ce0e-5769f3eb5e88" - }, - "a669cf86-e715-efa4-dd9a-b839abf499e9": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "timeseries_table", - "type": "timeseries", - "title": "New widget", - "sizeX": 24, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "dataKeys": [ - { - "name": "ruleEngineException", - "type": "timeseries", - "label": "Rule Chain", - "color": "#2196f3", - "settings": { - "useCellStyleFunction": false, - "useCellContentFunction": true, - "cellContentFunction": "return JSON.parse(value).ruleChainName;" - }, - "_hash": 0.9954481282345906 - }, - { - "name": "ruleEngineException", - "type": "timeseries", - "label": "Rule Node", - "color": "#4caf50", - "settings": { - "useCellStyleFunction": false, - "useCellContentFunction": true, - "cellContentFunction": "return JSON.parse(value).ruleNodeName;" - }, - "_hash": 0.18580357036589978 - }, - { - "name": "ruleEngineException", - "type": "timeseries", - "label": "Latest Error", - "color": "#f44336", - "settings": { - "useCellStyleFunction": false, - "useCellContentFunction": true, - "cellContentFunction": "return JSON.parse(value).message;" - }, - "_hash": 0.7255162989552142 - } - ], - "entityAliasId": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f" - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 0, - "realtime": { - "timewindowMs": 2592000000, - "interval": 1000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "rgb(255, 255, 255)", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "showTimestamp": true, - "displayPagination": true, - "defaultPageSize": 10 - }, - "title": "Exceptions", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "useDashboardTimewindow": false, - "showLegend": false, - "widgetStyle": {}, - "actions": {}, - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "displayTimewindow": true - }, - "id": "a669cf86-e715-efa4-dd9a-b839abf499e9" - }, - "292eaded-4775-36f7-c896-98d57bdda936": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "html_value_card", - "type": "latest", - "title": "New widget", - "sizeX": 7.5, - "sizeY": 3, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "emailApiState", - "type": "timeseries", - "label": "apiState", - "color": "#2196f3", - "settings": {}, - "_hash": 0.8830669138660703, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "emailLimit", - "type": "timeseries", - "label": "limit", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5463603803546802, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "emailCount", - "type": "timeseries", - "label": "count", - "color": "#f44336", - "settings": {}, - "_hash": 0.5564241862015964, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "transportApiState", - "type": "timeseries", - "label": "cardId", - "color": "#607d8b", - "settings": {}, - "_hash": 0.051659774305067296, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return (Math.random()*100000).toFixed(0);" - }, - { - "name": "smsApiState", - "type": "timeseries", - "label": "apiStatePoint", - "color": "#e91e63", - "settings": {}, - "_hash": 0.2969682764607864, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "smsLimit", - "type": "timeseries", - "label": "pointsLimit", - "color": "#9c27b0", - "settings": {}, - "_hash": 0.22082255831864894, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "smsCount", - "type": "timeseries", - "label": "pointsCount", - "color": "#8bc34a", - "settings": {}, - "_hash": 0.6340356364819146, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "transportApiState", - "type": "timeseries", - "label": "title", - "color": "#3f51b5", - "settings": {}, - "_hash": 0.6894070537030252, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.notifications}\";" - } - ] - } - ], - "timewindow": { - "realtime": { - "timewindowMs": 60000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "#666666", - "padding": "0", - "settings": { - "cardCss": ".card {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n}\n\n.card > img {\n height: 0;\n}\n\n.card .content {\n flex: 1; \n padding: 13px 13px 0;\n display: flex;\n box-sizing: border-box;\n}\n\n.card .content .column {\n display: flex;\n flex-direction: column; \n justify-content: space-around;\n flex: 1;\n}\n\n.card .content .title-row {\n display: flex;\n flex-direction: row;\n padding-bottom: 10px;\n}\n\n.card .title {\n flex: 1;\n font-size: 20px;\n font-weight: 400;\n color: #666666;\n}\n\n.card .state {\n text-transform: uppercase;\n font-size: 20px;\n font-weight: bold;\n}\n\n.card.enabled .state {\n color: #00B260;\n}\n\n.card.warning .state {\n color: #FFAD6F;\n}\n\n.card.disabled .state {\n color: #F73243;\n}\n\n.card .bars-row {\n flex: 1;\n display: flex;\n flex-direction: row;\n}\n\n.card .bar-column {\n flex: 1;\n display: flex;\n flex-direction: column;\n}\n\n.card .bar-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.card .bar {\n flex: 1;\n max-height: 30px;\n margin-top: 3.5px;\n margin-bottom: 4px;\n background-color: #F0F0F0;\n border: 1px solid #DADCDB;\n border-radius: 2px;\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2);\n}\n\n.card.enabled .bar {\n border-color: #00B260;\n background-color: #F0FBF7;\n}\n\n.card.warning .bar {\n border-color: #FFAD6F;\n background-color: #FFFAF6;\n}\n\n.card.disabled .bar {\n border-color: #F73243;\n background-color: #FFF0F0;\n}\n\n.card .bar .bar-fill {\n background-color: #F0F0F0;\n border-radius: 2px;\n height: 100%;\n width: 0%;\n}\n\n.card.enabled .bar-fill {\n background-color: #00C46C;\n}\n\n.card.warning .bar-fill {\n background-color: #FFD099;\n}\n\n.card.disabled .bar-fill {\n background-color: #FF9494;\n}\n\n.card .bar-labels {\n height: 20px;\n font-size: 16px;\n color: #666;\n display: flex;\n flex-direction: row;\n}\n\n.card .mat-mdc-button-base {\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.card .mdc-button__label {\n pointer-events: none;\n}\n\n.card .action-row {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n padding: 8px 0;\n}\n\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .card .title {\n font-size: 12px;\n }\n .card .state {\n font-size: 12px;\n }\n .card .unit {\n font-size: 8px;\n }\n .card .bar-labels {\n font-size: 6px;\n }\n .card .mat-mdc-button-base {\n font-size: 8px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1599px) {\n .card .title {\n font-size: 14px;\n }\n .card .state {\n font-size: 14px;\n }\n .card .unit {\n font-size: 10px;\n }\n .card .bar-labels {\n font-size: 8px;\n }\n .card .mat-mdc-button-base {\n font-size: 10px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1600px) and (max-width: 1919px) {\n .card .title {\n font-size: 16px;\n }\n .card .state {\n font-size: 16px;\n }\n .card .unit {\n font-size: 12px;\n }\n .card .bar-labels {\n font-size: 12px;\n }\n .card .mat-mdc-button-base {\n font-size: 12px;\n }\n .card .action-row {\n padding: 0;\n }\n} \n\n", - "cardHtml": "
\n \n \n
\n
\n
\n
\n ${title}\n
\n
\n
\n
\n
\n
\n
{i18n:api-usage.email}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
{i18n:api-usage.sms}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n
\n
" - }, - "title": "Notifications (Email/SMS)", - "dropShadow": true, - "enableFullscreen": false, - "widgetStyle": { - "cursor": "default" - }, - "titleStyle": { - "fontSize": "20px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "showLegend": false, - "actions": { - "elementClick": [ - { - "name": "transport_details", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "notifications", - "setEntityId": false, - "stateEntityParamName": null, - "openInSeparateDialog": null, - "dialogTitle": null, - "dialogHideDashboardToolbar": true, - "dialogWidth": null, - "dialogHeight": null, - "openRightLayout": false, - "id": "46b7cefe-e1f2-67c1-4055-3a214520f869" - } - ] - }, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "292eaded-4775-36f7-c896-98d57bdda936" - }, - "b3571a36-2106-1122-7d58-0d38bbb0e9c8": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "emailCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.email-messages}", - "color": "#d35a00", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000 - }, - "aggregation": { - "type": "SUM", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 1800000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "{i18n:api-usage.email-messages-daily-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "showLegend": true, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "row": 0, - "col": 0, - "id": "b3571a36-2106-1122-7d58-0d38bbb0e9c8" - }, - "aa875b7f-e7c8-7529-1ae7-f456211b59cc": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "emailCount", - "type": "timeseries", - "label": "{i18n:api-usage.email-messages}", - "color": "#d35a00", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 31536000000, - "interval": 1000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 900000000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - } - }, - "title": "{i18n:api-usage.email-messages-monthly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "showLegend": true, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "row": 0, - "col": 0, - "id": "aa875b7f-e7c8-7529-1ae7-f456211b59cc" - } - }, - "states": { - "default": { - "name": "{i18n:api-usage.api-usage}", - "root": true, - "layouts": { - "main": { - "widgets": { - "fd6df872-2ddf-0921-3929-2e7f55062fad": { - "sizeX": 4, - "sizeY": 2, - "row": 0, - "col": 8, - "mobileHeight": 3 - }, - "7e235874-461b-e7c2-2fdd-d8762a020773": { - "sizeX": 4, - "sizeY": 2, - "row": 0, - "col": 12, - "mobileHeight": 3 - }, - "08545554-a0e8-05c7-66df-6000cfeff8a4": { - "sizeX": 4, - "sizeY": 2, - "row": 0, - "col": 4, - "mobileHeight": 3 - }, - "a245c67e-53ec-d299-fa89-69fe2062ccb2": { - "sizeX": 4, - "sizeY": 2, - "row": 0, - "col": 0, - "mobileHeight": 3 - }, - "a151ae60-0326-6116-d818-9070dda8e9c7": { - "sizeX": 4, - "sizeY": 2, - "row": 0, - "col": 16, - "mobileHeight": 3 - }, - "292eaded-4775-36f7-c896-98d57bdda936": { - "sizeX": 4, - "sizeY": 2, - "row": 0, - "col": 20, - "mobileHeight": 3 - }, - "68e16e98-0420-f72c-4848-41dedffd3904": { - "sizeX": 8, - "sizeY": 4, - "row": 2, - "col": 8 - }, - "2aa6b499-6e27-b315-6833-89c4d58485ce": { - "sizeX": 8, - "sizeY": 4, - "row": 2, - "col": 0 - }, - "d890cea3-fba0-6474-9a21-fa780230dc62": { - "sizeX": 8, - "sizeY": 4, - "row": 2, - "col": 16 - }, - "84b6cfa5-1449-e0f2-3560-f810d2dd7ead": { - "sizeX": 8, - "sizeY": 4, - "row": 6, - "col": 0 - }, - "d296b566-a000-7402-ae9d-c815381c5435": { - "sizeX": 8, - "sizeY": 4, - "row": 6, - "col": 8 - }, - "00a02464-9509-911b-3b5e-21fb37629822": { - "sizeX": 8, - "sizeY": 4, - "row": 6, - "col": 16 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 5, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70 - } - } - } - }, - "transport": { - "name": "{i18n:api-usage.transport}", - "root": false, - "layouts": { - "main": { - "widgets": { - "98bae68b-0f35-72f2-a428-9b06889f1554": { - "sizeX": 24, - "sizeY": 6, - "row": 0, - "col": 0 - }, - "e61e5477-5a09-cc25-966b-f613d81da833": { - "sizeX": 24, - "sizeY": 6, - "row": 6, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 10, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70 - } - } - } - }, - "rule_engine_execution": { - "name": "{i18n:api-usage.rule-engine-executions}", - "root": false, - "layouts": { - "main": { - "widgets": { - "74199074-7873-6c6a-2a51-3fc614769f03": { - "sizeX": 24, - "sizeY": 6, - "row": 0, - "col": 0 - }, - "00006bc3-8a8d-b55e-ed39-e318f1bcd090": { - "sizeX": 24, - "sizeY": 6, - "row": 6, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 10, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70 - } - } - } - }, - "javascript_functions": { - "name": "{i18n:api-usage.javascript-functions}", - "root": false, - "layouts": { - "main": { - "widgets": { - "5f5ca59c-e507-5301-5910-7ad8cd34df40": { - "sizeX": 24, - "sizeY": 6, - "row": 0, - "col": 0 - }, - "ada32ee9-44ed-48d1-c368-fd0c94b7607f": { - "sizeX": 24, - "sizeY": 6, - "row": 6, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 10, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70 - } - } - } - }, - "telemetry_persistence": { - "name": "{i18n:api-usage.telemetry-persistence}", - "root": false, - "layouts": { - "main": { - "widgets": { - "85fe0738-5326-f069-ab3f-30594bde5fed": { - "sizeX": 24, - "sizeY": 6, - "row": 0, - "col": 0 - }, - "eaeb381a-437e-f6e9-60c9-6bc8826fdd44": { - "sizeX": 24, - "sizeY": 6, - "row": 6, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 10, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70 - } - } - } - }, - "rule_engine_statistics": { - "name": "{i18n:api-usage.rule-engine-statistics}", - "root": false, - "layouts": { - "main": { - "widgets": { - "2408ad30-163e-8221-08e1-a82b638be564": { - "sizeX": 12, - "sizeY": 7, - "mobileHeight": null, - "row": 0, - "col": 0 - }, - "e43dcfe1-b970-6a11-ce0e-5769f3eb5e88": { - "sizeX": 12, - "sizeY": 7, - "mobileHeight": null, - "row": 0, - "col": 12 - }, - "a669cf86-e715-efa4-dd9a-b839abf499e9": { - "sizeX": 24, - "sizeY": 5, - "row": 7, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 10, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70 - } - } - } - }, - "notifications": { - "name": "{i18n:api-usage.notifications-email-sms}", - "root": false, - "layouts": { - "main": { - "widgets": { - "7302df65-1b0c-579e-bbdb-145126ae3392": { - "sizeX": 12, - "sizeY": 6, - "row": 0, - "col": 12 - }, - "fdb385e7-14fe-fc9f-ebdc-b400f26fc66b": { - "sizeX": 12, - "sizeY": 6, - "row": 6, - "col": 12 - }, - "b3571a36-2106-1122-7d58-0d38bbb0e9c8": { - "sizeX": 12, - "sizeY": 6, - "row": 0, - "col": 0 - }, - "aa875b7f-e7c8-7529-1ae7-f456211b59cc": { - "sizeX": 12, - "sizeY": 6, - "row": 6, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 10, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70 - } - } - } - }, - "alarms_created": { - "name": "{i18n:api-usage.alarms-created}", - "root": false, - "layouts": { - "main": { - "widgets": { - "4b798823-b97d-9d6a-59dc-fcafd897fc23": { - "sizeX": 24, - "sizeY": 6, - "row": 0, - "col": 0 - }, - "6a981580-7490-19dd-f937-b64cbf67a982": { - "sizeX": 24, - "sizeY": 6, - "row": 6, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 10, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70 - } - } - } - } - }, - "entityAliases": { - "40193437-33ac-3172-eefd-0b08eb849062": { - "id": "40193437-33ac-3172-eefd-0b08eb849062", - "alias": "Api usage state", - "filter": { - "type": "apiUsageState", - "resolveMultiple": false - } - }, - "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f": { - "id": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f", - "alias": "TbServiceQueues", - "filter": { - "type": "assetType", - "resolveMultiple": true, - "assetType": "TbServiceQueue", - "assetNameFilter": "" - } - } - }, - "filters": {}, - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 0, - "realtime": { - "timewindowMs": 86400000, - "interval": 3600000 - }, - "aggregation": { - "type": "NONE", - "limit": 50000 - } - }, - "settings": { - "stateControllerId": "entity", - "showTitle": false, - "showDashboardsSelect": false, - "showEntitiesSelect": false, - "showDashboardTimewindow": false, - "showDashboardExport": false, - "toolbarAlwaysOpen": true, - "titleColor": "rgba(0,0,0,0.870588)", - "showFilters": false, - "showDashboardLogo": false - } - }, - "name": "Api Usage" -} diff --git a/ui-ngx/src/app/modules/home/pages/asset-profile/asset-profiles-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/asset-profile/asset-profiles-table-config.resolver.ts index f33eccf090..1862a9d5c0 100644 --- a/ui-ngx/src/app/modules/home/pages/asset-profile/asset-profiles-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/asset-profile/asset-profiles-table-config.resolver.ts @@ -29,7 +29,7 @@ import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared import { EntityAction } from '@home/models/entity/entity-component.models'; import { DialogService } from '@core/services/dialog.service'; import { MatDialog } from '@angular/material/dialog'; -import { ImportExportService } from '@home/components/import-export/import-export.service'; +import { ImportExportService } from '@shared/import-export/import-export.service'; import { HomeDialogsService } from '@home/dialogs/home-dialogs.service'; import { AssetProfile, TB_SERVICE_QUEUE } from '@shared/models/asset.models'; import { AssetProfileService } from '@core/http/asset-profile.service'; diff --git a/ui-ngx/src/app/modules/home/pages/asset/asset.component.html b/ui-ngx/src/app/modules/home/pages/asset/asset.component.html index 16229a7af6..b2c4b5a10a 100644 --- a/ui-ngx/src/app/modules/home/pages/asset/asset.component.html +++ b/ui-ngx/src/app/modules/home/pages/asset/asset.component.html @@ -86,13 +86,6 @@ {{ 'asset.name-max-length' | translate }} - - asset.label @@ -100,6 +93,20 @@ {{ 'asset.label-max-length' | translate }} + + + +
asset.description diff --git a/ui-ngx/src/app/modules/home/pages/asset/asset.component.ts b/ui-ngx/src/app/modules/home/pages/asset/asset.component.ts index 8c53cbba38..a96b65e249 100644 --- a/ui-ngx/src/app/modules/home/pages/asset/asset.component.ts +++ b/ui-ngx/src/app/modules/home/pages/asset/asset.component.ts @@ -69,6 +69,7 @@ export class AssetComponent extends EntityComponent { name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]], assetProfileId: [entity ? entity.assetProfileId : null, [Validators.required]], label: [entity ? entity.label : '', Validators.maxLength(255)], + customerId: [entity ? entity.customerId : ''], additionalInfo: this.fb.group( { description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], @@ -82,6 +83,7 @@ export class AssetComponent extends EntityComponent { this.entityForm.patchValue({name: entity.name}); this.entityForm.patchValue({assetProfileId: entity.assetProfileId}); this.entityForm.patchValue({label: entity.label}); + this.entityForm.patchValue({customerId: entity.customerId}); this.entityForm.patchValue({additionalInfo: {description: entity.additionalInfo ? entity.additionalInfo.description : ''}}); } diff --git a/ui-ngx/src/app/modules/home/pages/customer/customer-routing.module.ts b/ui-ngx/src/app/modules/home/pages/customer/customer-routing.module.ts index d2c02955c2..3f94a23a30 100644 --- a/ui-ngx/src/app/modules/home/pages/customer/customer-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/customer/customer-routing.module.ts @@ -251,6 +251,7 @@ const routes: Routes = [ { path: ':dashboardId', component: DashboardPageComponent, + canDeactivate: [ConfirmOnExitGuard], data: { breadcrumb: { labelFunction: dashboardBreadcumbLabelFunction, diff --git a/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.html b/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.html index 3514b0d919..b1bb37470a 100644 --- a/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.html +++ b/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.html @@ -65,16 +65,16 @@ [fxShow]="!hideDelete() && !isEdit"> {{'dashboard.delete' | translate }} -
-
- +
+ +
-
dashboard.mobile-app-settings
- - - - {{ 'dashboard.mobile-hide' | translate }} - - - dashboard.mobile-order - - + + +
+
dashboard.mobile-app-settings
+ + {{ 'dashboard.mobile-hide' | translate }} + + + dashboard.mobile-order + + + + +
diff --git a/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.ts b/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.ts index 053ee9326d..3da09f84c8 100644 --- a/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.ts +++ b/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.ts @@ -30,6 +30,7 @@ import { import { DashboardService } from '@core/http/dashboard.service'; import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; import { isEqual } from '@core/utils'; +import { EntityType } from '@shared/models/entity-type.models'; @Component({ selector: 'tb-dashboard-form', @@ -43,6 +44,7 @@ export class DashboardFormComponent extends EntityComponent { publicLink: string; assignedCustomersText: string; + entityType = EntityType; constructor(protected store: Store, protected translate: TranslateService, @@ -78,7 +80,7 @@ export class DashboardFormComponent extends EntityComponent { buildForm(entity: Dashboard): UntypedFormGroup { this.updateFields(entity); - return this.fb.group( + const form = this.fb.group( { title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]], image: [entity ? entity.image : null], @@ -91,6 +93,11 @@ export class DashboardFormComponent extends EntityComponent { ) } ); + if (this.isAdd) { + form.addControl('assignedCustomerIds', this.fb.control([])); + } + + return form; } updateForm(entity: Dashboard) { diff --git a/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-routing.module.ts b/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-routing.module.ts index 89f66df545..6645dc6bd3 100644 --- a/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/dashboard/dashboard-routing.module.ts @@ -32,6 +32,7 @@ import { UserDashboardAction } from '@shared/models/user-settings.models'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { getCurrentAuthUser } from '@core/auth/auth.selectors'; +import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; @Injectable() export class DashboardResolver implements Resolve { @@ -85,6 +86,7 @@ const routes: Routes = [ { path: ':dashboardId', component: DashboardPageComponent, + canDeactivate: [ConfirmOnExitGuard], data: { breadcrumb: { labelFunction: dashboardBreadcumbLabelFunction, diff --git a/ui-ngx/src/app/modules/home/pages/dashboard/dashboards-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/dashboard/dashboards-table-config.resolver.ts index 097902d087..6a0b987e5b 100644 --- a/ui-ngx/src/app/modules/home/pages/dashboard/dashboards-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/dashboard/dashboards-table-config.resolver.ts @@ -47,6 +47,7 @@ import { import { Dashboard, DashboardInfo, + DashboardSetup, getDashboardAssignedCustomersText, isCurrentPublicDashboardCustomer, isPublicDashboard @@ -63,13 +64,19 @@ import { MakeDashboardPublicDialogData } from '@modules/home/pages/dashboard/make-dashboard-public-dialog.component'; import { DashboardTabsComponent } from '@home/pages/dashboard/dashboard-tabs.component'; -import { ImportExportService } from '@home/components/import-export/import-export.service'; +import { ImportExportService } from '@shared/import-export/import-export.service'; import { EdgeService } from '@core/http/edge.service'; import { AddEntitiesToEdgeDialogComponent, AddEntitiesToEdgeDialogData } from '@home/dialogs/add-entities-to-edge-dialog.component'; import { HomeDialogsService } from '@home/dialogs/home-dialogs.service'; +import { Widget } from '@shared/models/widget.models'; +import { EntityAliases } from '@shared/models/alias.models'; +import { + EntityAliasesDialogComponent, + EntityAliasesDialogData +} from '@home/components/alias/entity-aliases-dialog.component'; @Injectable() export class DashboardsTableConfigResolver implements Resolve> { @@ -103,9 +110,7 @@ export class DashboardsTableConfigResolver implements Resolve this.translate.instant('dashboard.delete-dashboards-text'); this.config.loadEntity = id => this.dashboardService.getDashboard(id.id); - this.config.saveEntity = dashboard => { - return this.dashboardService.saveDashboard(dashboard as Dashboard); - }; + this.config.saveEntity = dashboard => this.saveAndAssignDashboard(dashboard as DashboardSetup); this.config.onEntityAction = action => this.onDashboardAction(action); this.config.detailsReadonly = () => (this.config.componentsData.dashboardScope === 'customer_user' || this.config.componentsData.dashboardScope === 'edge_customer_user'); @@ -118,6 +123,10 @@ export class DashboardsTableConfigResolver implements Resolve { + this.openDashboard(null, dashboard); + }; } resolve(route: ActivatedRouteSnapshot): Observable> { @@ -178,13 +187,9 @@ export class DashboardsTableConfigResolver implements Resolve('customersTitle', 'dashboard.assignedToCustomers', - '50%', entity => { - return getDashboardAssignedCustomersText(entity); - }, () => ({}), false), + '50%', entity => getDashboardAssignedCustomersText(entity), () => ({}), false), new EntityTableColumn('dashboardIsPublic', 'dashboard.public', '60px', - entity => { - return checkBoxCell(isPublicDashboard(entity)); - }, () => ({}), false), + entity => checkBoxCell(isPublicDashboard(entity)), () => ({}), false), ); } return columns; @@ -269,7 +274,7 @@ export class DashboardsTableConfigResolver implements Resolve true, + isEnabled: () => true, onAction: ($event, entity) => this.unassignFromEdge($event, entity) } ); @@ -383,8 +388,8 @@ export class DashboardsTableConfigResolver implements Resolve { if (dashboard) { this.config.updateData(); @@ -393,6 +398,30 @@ export class DashboardsTableConfigResolver implements Resolve, isSingleWidget: boolean, + customTitle: string, missingEntityAliases: EntityAliases): Observable { + return this.dialog.open(EntityAliasesDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + entityAliases: missingEntityAliases, + widgets, + customTitle, + isSingleWidget, + disableAdd: true + } + }).afterClosed().pipe( + map((updatedEntityAliases) => { + if (updatedEntityAliases) { + return updatedEntityAliases; + } else { + throw new Error('Unable to resolve missing entity aliases!'); + } + } + )); + } + exportDashboard($event: Event, dashboard: DashboardInfo) { if ($event) { $event.stopPropagation(); @@ -647,4 +676,17 @@ export class DashboardsTableConfigResolver implements Resolve { + const {assignedCustomerIds, ...dashboardToCreate} = dashboard; + + return this.dashboardService.saveDashboard(dashboardToCreate as Dashboard).pipe( + mergeMap((createdDashboard) => { + if (assignedCustomerIds?.length) { + return this.dashboardService.addDashboardCustomers(createdDashboard.id.id, assignedCustomerIds); + } + return of(createdDashboard); + }) + ); + } + } diff --git a/ui-ngx/src/app/modules/home/pages/device-profile/device-profiles-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/device-profile/device-profiles-table-config.resolver.ts index 9f026d626e..28ceb285ad 100644 --- a/ui-ngx/src/app/modules/home/pages/device-profile/device-profiles-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/device-profile/device-profiles-table-config.resolver.ts @@ -41,7 +41,7 @@ import { AddDeviceProfileDialogComponent, AddDeviceProfileDialogData } from '@home/components/profile/add-device-profile-dialog.component'; -import { ImportExportService } from '@home/components/import-export/import-export.service'; +import { ImportExportService } from '@shared/import-export/import-export.service'; import { HomeDialogsService } from '@home/dialogs/home-dialogs.service'; @Injectable() diff --git a/ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.ts index 01e5584a57..5aa9b0b317 100644 --- a/ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.ts @@ -17,10 +17,10 @@ import { Component, forwardRef, Input, OnInit } from '@angular/core'; import { ControlValueAccessor, - UntypedFormBuilder, - UntypedFormGroup, NG_VALIDATORS, NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormGroup, ValidationErrors, Validator, Validators @@ -104,9 +104,7 @@ export class DeviceTransportConfigurationComponent implements ControlValueAccess if (configuration) { delete configuration.type; } - setTimeout(() => { - this.deviceTransportConfigurationFormGroup.patchValue({configuration}, {emitEvent: false}); - }, 0); + this.deviceTransportConfigurationFormGroup.patchValue({configuration}, {emitEvent: false}); } validate(): ValidationErrors | null { diff --git a/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.ts index 29ac39ee6a..26b70396d9 100644 --- a/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.ts @@ -127,6 +127,9 @@ export class SnmpDeviceTransportConfigurationComponent implements ControlValueAc this.snmpDeviceTransportConfigurationFormGroup.disable({emitEvent: false}); } else { this.snmpDeviceTransportConfigurationFormGroup.enable({emitEvent: false}); + this.updateDisabledFormValue( + this.snmpDeviceTransportConfigurationFormGroup.get('protocolVersion').value || SnmpDeviceProtocolVersion.V2C + ); } } diff --git a/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.html b/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.html new file mode 100644 index 0000000000..70cdd2cdd3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.html @@ -0,0 +1,376 @@ + + +

{{ dialogTitle }}

+ + + +
+
+
+ + + {{ deviceTransportTypeTranslationMap.get(BasicTransportType.HTTP) | translate }} + + + {{ deviceTransportTypeTranslationMap.get(DeviceTransportType.MQTT) | translate }} + + + {{ deviceTransportTypeTranslationMap.get(DeviceTransportType.COAP) | translate }} + + + {{ deviceTransportTypeTranslationMap.get(DeviceTransportType.SNMP) | translate }} + + + {{ deviceTransportTypeTranslationMap.get(DeviceTransportType.LWM2M) | translate }} + + +
+ + +
device.connectivity.use-following-instructions
+ + + + + Windows + + +
+
+
device.connectivity.install-necessary-client-tools
+
device.connectivity.install-curl-windows
+
+ + +
+
+
+ + + + MacOS + + +
+
+
device.connectivity.install-necessary-client-tools
+
device.connectivity.install-curl-macos
+
+ + +
+
+
+ + + + Linux + + +
+
+
device.connectivity.install-necessary-client-tools
+ +
+ + +
+
+
+
+
+ +
+ + +
+ +
device.connectivity.use-following-instructions
+ + + + + Windows + + +
+
+
device.connectivity.install-necessary-client-tools
+
+ + +
+
+ + +
+
+
+ + + + MacOS + + +
+
+
device.connectivity.install-necessary-client-tools
+ +
+ + +
+
+
+ + + + Linux + + +
+
+
device.connectivity.install-necessary-client-tools
+ +
+ + +
+
+
+ + + + Docker + + +
+ + +
+
+
+
+
+
+ +
device.connectivity.use-following-instructions
+ + + + + Linux + + +
+
+
device.connectivity.install-necessary-client-tools
+
+ + +
+
+ + +
+
+
+ + + + Docker + + +
+ + +
+
+
+
+
+ +
+ + +
+
+ +
+ + +
+
+
+
+
+
+
device.state
+
+ {{ (status ? 'device.active' : 'device.inactive') | translate }} +
+
+
attribute.latest-telemetry
+
+
+
device.time
+
attribute.key
+
attribute.value
+
+
+
+
{{ telemetry.lastUpdateTs | date: 'yyyy-MM-dd HH:mm:ss' }}
+
{{ telemetry.key }}
+
{{ telemetry.value }}
+
+
+
+
+
+
+
+ {{ 'action.dont-show-again' | translate}} + + +
+ +
+ + + {{ 'device.connectivity.loading-check-connectivity-command' | translate }} + +
+
+ +
+
+
attribute.no-latest-telemetry
+
+
+ + +
+
+
device.connectivity.execute-following-command
+ + {{ cmd.noSecLabel }} + {{ cmd.secLabel }} + +
+ + + + + +
+ +
+ + + + +
+
+
+
+ + +
+ diff --git a/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.scss new file mode 100644 index 0000000000..348fa01a26 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.scss @@ -0,0 +1,177 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import "../../../../../scss/constants"; + +:host { + height: 100%; + max-height: 100vh; + display: grid; + grid-template-rows: min-content minmax(auto, 1fr) min-content; + + .tb-loader { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 16px; + height: 300px; + max-height: 100%; + + .label { + margin-bottom: 0; + text-align: center; + } + } + + .status { + margin-left: 12px; + border-radius: 12px; + height: 24px; + line-height: 24px; + padding: 0 8px; + width: fit-content; + color: #198038; + background-color: rgba(25, 128, 56, 0.08); + font-size: 14px; + + &.inactive { + color: #d12730; + background-color: rgba(209, 39, 48, 0.08); + } + } + + .tb-hint-instruction { + border-radius: 6px; + background-color: rgba(48, 86, 128, 0.04); + padding: 6px 16px; + + .content { + vertical-align: middle; + } + } + + .tb-font-14 { + font-size: 14px; + } + + .tb-flex-1 { + flex: 1; + } + + .tb-form-table-body { + max-height: 108px; + min-height: 108px; + overflow-y: auto; + scrollbar-gutter: stable; + + .tb-form-table-row { + min-height: 38px; + } + } + + .tb-no-data-available { + .tb-no-data-bg { + min-height: 68px; + } + } + + .tb-install-instruction-text { + min-height: 42px; + } + + @media #{$mat-sm} { + width: 470px; + } + + @media #{$mat-gt-sm} { + width: 720px; + } +} + +:host-context(.mat-mdc-dialog-container) { + .tb-dialog-actions { + display: flex; + gap: 8px; + padding: 8px 16px; + } + + .mat-mdc-dialog-content { + max-height: 80vh; + padding: 16px; + } +} + +:host ::ng-deep { + .tb-markdown-view { + .tb-command-code { + .code-wrapper { + padding: 0; + pre[class*=language-] { + margin: 0; + background: #F3F6FA; + border-color: #305680; + padding-right: 38px; + overflow: scroll; + padding-bottom: 4px; + + &::-webkit-scrollbar { + width: 4px; + height: 4px; + } + } + } + button.clipboard-btn { + right: -2px; + p { + color: #305680; + } + p, div { + background-color: #F3F6FA; + } + div { + img { + display: none; + } + &:after { + content: ""; + position: initial; + display: block; + width: 18px; + height: 18px; + background: #305680; + mask-image: url(/assets/copy-code-icon.svg); + mask-repeat: no-repeat; + } + } + } + } + } + .mdc-button__label > span { + .mat-icon { + vertical-align: text-bottom; + box-sizing: initial; + } + } + + .tabs-icon { + margin-right: 8px; + } + + .tb-form-panel.tb-tab-body { + padding: 16px 0 0; + } +} diff --git a/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.ts new file mode 100644 index 0000000000..f2e4229314 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/device/device-check-connectivity-dialog.component.ts @@ -0,0 +1,220 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, Inject, NgZone, OnDestroy, OnInit } from '@angular/core'; +import { DialogComponent } from '@shared/components/dialog.component'; +import { select, Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { Router } from '@angular/router'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { DeviceService } from '@core/http/device.service'; +import { + AttributeData, + AttributeScope, + AttributesSubscriptionCmd, + LatestTelemetry, + TelemetrySubscriber +} from '@shared/models/telemetry/telemetry.models'; +import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service'; +import { EntityId } from '@shared/models/id/entity-id'; +import { EntityType } from '@shared/models/entity-type.models'; +import { selectPersistDeviceStateToTelemetry } from '@core/auth/auth.selectors'; +import { take } from 'rxjs/operators'; +import { + BasicTransportType, + DeviceTransportType, + deviceTransportTypeTranslationMap, + NetworkTransportType, + PublishTelemetryCommand +} from '@shared/models/device.models'; +import { ActionPreferencesPutUserSettings } from '@core/auth/auth.actions'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; +import { getOS } from '@core/utils'; + +export interface DeviceCheckConnectivityDialogData { + deviceId: EntityId; + afterAdd: boolean; +} + +@Component({ + selector: 'tb-device-check-connectivity-dialog', + templateUrl: './device-check-connectivity-dialog.component.html', + styleUrls: ['./device-check-connectivity-dialog.component.scss'] +}) +export class DeviceCheckConnectivityDialogComponent extends + DialogComponent implements OnInit, OnDestroy { + + loadedCommand = false; + + status: boolean; + + latestTelemetry: Array = []; + + commands: PublishTelemetryCommand; + + allowTransportType = new Set(); + selectTransportType: NetworkTransportType; + + BasicTransportType = BasicTransportType; + DeviceTransportType = DeviceTransportType; + deviceTransportTypeTranslationMap = deviceTransportTypeTranslationMap; + + showDontShowAgain: boolean; + dialogTitle: string; + + notShowAgain = false; + + httpTabIndex = 0; + mqttTabIndex = 0; + coapTabIndex = 0; + + private telemetrySubscriber: TelemetrySubscriber; + + private currentTime = Date.now(); + + private transportTypes = [...Object.keys(BasicTransportType), ...Object.keys(DeviceTransportType)] as Array; + + constructor(protected store: Store, + protected router: Router, + @Inject(MAT_DIALOG_DATA) private data: DeviceCheckConnectivityDialogData, + public dialogRef: MatDialogRef, + private deviceService: DeviceService, + private telemetryWsService: TelemetryWebsocketService, + private zone: NgZone) { + super(store, router, dialogRef); + + if (this.data.afterAdd) { + this.dialogTitle = 'device.connectivity.device-created-check-connectivity'; + this.showDontShowAgain = true; + } else { + this.dialogTitle = 'device.connectivity.check-connectivity'; + this.showDontShowAgain = false; + } + } + + ngOnInit() { + this.loadCommands(); + this.subscribeToLatestTelemetry(); + } + + ngOnDestroy() { + super.ngOnDestroy(); + this.telemetrySubscriber?.complete(); + this.telemetrySubscriber?.unsubscribe(); + } + + close(): void { + if (this.notShowAgain && this.showDontShowAgain) { + this.store.dispatch(new ActionPreferencesPutUserSettings({ notDisplayConnectivityAfterAddDevice: true })); + this.dialogRef.close(null); + } else { + this.dialogRef.close(null); + } + } + + createMarkDownCommand(commands: string | string[]): string { + if (Array.isArray(commands)) { + const formatCommands: Array = []; + commands.forEach(command => formatCommands.push(this.createMarkDownSingleCommand(command))); + return formatCommands.join(`\n
\n\n`); + } else { + return this.createMarkDownSingleCommand(commands); + } + } + + private createMarkDownSingleCommand(command: string): string { + return '```bash\n' + + command + + '{:copy-code}\n' + + '```'; + } + + private loadCommands() { + this.deviceService.getDevicePublishTelemetryCommands(this.data.deviceId.id).subscribe( + commands => { + this.commands = commands; + const commandsProtocols = Object.keys(commands); + this.transportTypes.forEach(transport => { + const findCommand = commandsProtocols.find(item => item.toUpperCase().startsWith(transport)); + if (findCommand) { + this.allowTransportType.add(transport); + } + }); + this.selectTransportType = this.allowTransportType.values().next().value; + this.selectTabIndexForUserOS(); + this.loadedCommand = true; + } + ); + } + + private subscribeToLatestTelemetry() { + this.store.pipe(select(selectPersistDeviceStateToTelemetry)).pipe( + take(1) + ).subscribe(persistToTelemetry => { + this.telemetrySubscriber = TelemetrySubscriber.createEntityAttributesSubscription( + this.telemetryWsService, this.data.deviceId, LatestTelemetry.LATEST_TELEMETRY, this.zone); + if (!persistToTelemetry) { + const subscriptionCommand = new AttributesSubscriptionCmd(); + subscriptionCommand.entityType = this.data.deviceId.entityType as EntityType; + subscriptionCommand.entityId = this.data.deviceId.id; + subscriptionCommand.scope = AttributeScope.SERVER_SCOPE; + subscriptionCommand.keys = 'active'; + this.telemetrySubscriber.subscriptionCommands.push(subscriptionCommand); + } + + this.telemetrySubscriber.subscribe(); + this.telemetrySubscriber.attributeData$().subscribe( + (data) => { + const telemetry = data.reduce>((accumulator, item) => { + if (item.key === 'active') { + this.status = coerceBooleanProperty(item.value); + } else if (item.lastUpdateTs > this.currentTime) { + accumulator.push(item); + } + return accumulator; + }, []); + this.latestTelemetry = telemetry.sort((a, b) => b.lastUpdateTs - a.lastUpdateTs); + } + ); + }); + } + + private selectTabIndexForUserOS() { + const currentOS = getOS(); + switch (currentOS) { + case 'linux': + case 'android': + this.httpTabIndex = 2; + this.mqttTabIndex = 2; + break; + case 'macos': + case 'ios': + this.httpTabIndex = 1; + this.mqttTabIndex = 1; + this.coapTabIndex = 1; + break; + case 'windows': + this.httpTabIndex = 0; + this.mqttTabIndex = 0; + this.coapTabIndex = 1; + break; + default: + this.mqttTabIndex = this.commands.mqtt?.docker ? 3 : 0; + this.coapTabIndex = this.commands.coap?.docker ? 1 : 0; + } + } + +} diff --git a/ui-ngx/src/app/modules/home/pages/device/device-credentials-dialog.component.html b/ui-ngx/src/app/modules/home/pages/device/device-credentials-dialog.component.html index ff32bf68df..d7f964542e 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device-credentials-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/device/device-credentials-dialog.component.html @@ -15,49 +15,47 @@ limitations under the License. --> -
- -

{{ 'device.device-credentials' | translate }}

- - -
- - -
-
-
-
- - -
-
- -
- - - {{ 'device.loading-device-credentials' | translate }} - -
-
-
-
- - -
-
+ +

{{ 'device.device-credentials' | translate }}

+ + +
+ + +
+
+
+ + +
+
+ +
+ + + {{ 'device.loading-device-credentials' | translate }} + +
+
+
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/pages/device/device-credentials-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/device/device-credentials-dialog.component.scss new file mode 100644 index 0000000000..f2c168e150 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/device/device-credentials-dialog.component.scss @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import "../../../../../scss/constants"; + +:host { + height: 100%; + display: grid; + grid-template-rows: min-content 4px auto min-content; + + @media #{$mat-gt-xs} { + min-width: 420px; + } +} + +:host-context(.mat-mdc-dialog-container) { + .tb-dialog-actions { + grid-row: 4; + display: flex; + gap: 8px; + justify-content: flex-end; + flex: 1; + } + + .mat-mdc-dialog-content { + grid-row: 3; + padding: 24px 24px 4px; + } +} diff --git a/ui-ngx/src/app/modules/home/pages/device/device-credentials-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/device/device-credentials-dialog.component.ts index 41986b2b03..cb4a795c9e 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device-credentials-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/device-credentials-dialog.component.ts @@ -37,7 +37,7 @@ export interface DeviceCredentialsDialogData { selector: 'tb-device-credentials-dialog', templateUrl: './device-credentials-dialog.component.html', providers: [{provide: ErrorStateMatcher, useExisting: DeviceCredentialsDialogComponent}], - styleUrls: [] + styleUrls: ['./device-credentials-dialog.component.scss'] }) export class DeviceCredentialsDialogComponent extends DialogComponent implements OnInit, ErrorStateMatcher { diff --git a/ui-ngx/src/app/modules/home/pages/device/device.component.html b/ui-ngx/src/app/modules/home/pages/device/device.component.html index e619fa9561..0b1ef5225c 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device.component.html +++ b/ui-ngx/src/app/modules/home/pages/device/device.component.html @@ -46,6 +46,12 @@ [fxShow]="!isEdit"> {{ ((deviceScope === 'customer_user' || deviceScope === 'edge_customer_user') ? 'device.view-credentials' : 'device.manage-credentials') | translate }} + - - - -
-
- -
-
- -
+ +

{{ dialogTitle }}

+ + +
+
+
+
+ + + + + Ubuntu + + +
+
+ +
+
+
+
+ + + + CentOS/RHEL + + +
+
+ +
+
+
+
+ + + + Docker + + +
+
+ +
+
+
+
+
+
+
+
+ {{ 'action.dont-show-again' | translate}} + + +
+ +
+ + + {{ 'edge.loading-edge-instructions' | translate }} + +
+
diff --git a/ui-ngx/src/app/modules/home/pages/edge/edge-instructions-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/edge/edge-instructions-dialog.component.scss new file mode 100644 index 0000000000..54dc438011 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/edge/edge-instructions-dialog.component.scss @@ -0,0 +1,137 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import "../../../../../scss/constants"; + +:host { + height: 100%; + max-height: 100vh; + display: grid; + grid-template-rows: min-content minmax(auto, 1fr) min-content; + + .tb-loader { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 16px; + height: 300px; + max-height: 100%; + + .label { + margin-bottom: 0; + text-align: center; + } + } + + .tb-font-14 { + font-size: 14px; + } + + .tb-flex-1 { + flex: 1; + } + + @media #{$mat-sm} { + width: 705px; + } + + @media #{$mat-gt-sm} { + width: 1080px; + } +} + +:host-context(.mat-mdc-dialog-container) { + .tb-dialog-actions { + display: flex; + gap: 8px; + padding: 8px 16px; + } + + .mat-mdc-dialog-content { + max-height: 80vh; + padding: 0 16px 16px 16px; + border-top: 16px solid #fff; + } +} + +:host ::ng-deep { + .tb-markdown-view { + padding: 16px 16px 32px 16px; + div { + background-color: #F3F6FA; + &.code-wrapper button.clipboard-btn { + right: -2px !important; + p { + color: $tb-primary-color !important; + } + p, div { + background-color: unset !important; + } + div { + img { + display: none; + } + &:after { + content: ""; + position: initial; + display: block; + width: 18px; + height: 18px; + background: $tb-primary-color; + mask-image: url(/assets/copy-code-icon.svg); + mask-repeat: no-repeat; + } + } + &.multiline { + right: -2px !important; + } + } + } + & > *:not(ul) { + padding-right: unset !important; + padding-left: unset !important; + } + pre[class*="language-"] { + border: 1px solid $tb-primary-color !important; + } + } + .mdc-button__label > span { + .mat-icon { + vertical-align: text-bottom; + box-sizing: initial; + } + } + + .mat-mdc-tab-header { + position: sticky; + top: 0; + z-index: 10; + background-color: #fff; + } + + .tabs-icon { + margin-right: 8px; + } + + .tb-form-panel.tb-tab-body { + padding: 16px 0 0; + } + + .mat-mdc-tab-body { + padding: 16px 0; + } +} diff --git a/ui-ngx/src/app/modules/home/pages/edge/edge-instructions-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/edge/edge-instructions-dialog.component.ts index a4dfe4345b..6c9511d709 100644 --- a/ui-ngx/src/app/modules/home/pages/edge/edge-instructions-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/edge/edge-instructions-dialog.component.ts @@ -14,33 +14,106 @@ /// limitations under the License. /// -import { Component, Inject } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; -import { DialogComponent } from "@shared/components/dialog.component"; -import { Store } from "@ngrx/store"; -import { AppState } from "@core/core.state"; -import { Router } from "@angular/router"; - -export interface EdgeInstructionsData { - instructions: string; +import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; +import { DialogComponent } from '@shared/components/dialog.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { Router } from '@angular/router'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { ActionPreferencesPutUserSettings } from '@core/auth/auth.actions'; +import { + EdgeInfo, + EdgeInstructions, + EdgeInstructionsMethod, + edgeVersionAttributeKey +} from '@shared/models/edge.models'; +import { EdgeService } from '@core/http/edge.service'; +import { AttributeService } from '@core/http/attribute.service'; +import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; +import { mergeMap, Observable } from 'rxjs'; + +export interface EdgeInstructionsDialogData { + edge: EdgeInfo; + afterAdd: boolean; + upgradeAvailable: boolean; } @Component({ - selector: 'tb-edge-instructions', - templateUrl: './edge-instructions-dialog.component.html' + selector: 'tb-edge-installation-dialog', + templateUrl: './edge-instructions-dialog.component.html', + styleUrls: ['./edge-instructions-dialog.component.scss'] }) -export class EdgeInstructionsDialogComponent extends DialogComponent { +export class EdgeInstructionsDialogComponent extends DialogComponent implements OnInit, OnDestroy { + + dialogTitle: string; + showDontShowAgain: boolean; - instructions: string = this.data.instructions; + loadedInstructions = false; + notShowAgain = false; + tabIndex = 0; + instructionsMethod = EdgeInstructionsMethod; + contentData: any = {}; constructor(protected store: Store, protected router: Router, - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: EdgeInstructionsData) { + @Inject(MAT_DIALOG_DATA) private data: EdgeInstructionsDialogData, + public dialogRef: MatDialogRef, + private attributeService: AttributeService, + private edgeService: EdgeService) { super(store, router, dialogRef); + + if (this.data.afterAdd) { + this.dialogTitle = 'edge.install-connect-instructions-edge-created'; + this.showDontShowAgain = true; + } else if (this.data.upgradeAvailable) { + this.dialogTitle = 'edge.upgrade-instructions'; + this.showDontShowAgain = false; + } else { + this.dialogTitle = 'edge.install-connect-instructions'; + this.showDontShowAgain = false; + } + } + + ngOnInit() { + this.getInstructions(this.instructionsMethod[this.tabIndex]); + } + + ngOnDestroy() { + super.ngOnDestroy(); + } + + close(): void { + if (this.notShowAgain && this.showDontShowAgain) { + this.store.dispatch(new ActionPreferencesPutUserSettings({notDisplayInstructionsAfterAddEdge: true})); + this.dialogRef.close(null); + } else { + this.dialogRef.close(null); + } + } + + selectedTabChange(index: number) { + this.getInstructions(this.instructionsMethod[index]); } - cancel(): void { - this.dialogRef.close(null); + getInstructions(method: string) { + if (!this.contentData[method]) { + this.loadedInstructions = false; + let edgeInstructions$: Observable; + if (this.data.upgradeAvailable) { + edgeInstructions$ = this.attributeService.getEntityAttributes(this.data.edge.id, AttributeScope.SERVER_SCOPE, [edgeVersionAttributeKey]) + .pipe(mergeMap(attributes => { + if (attributes.length) { + const edgeVersion = attributes[0].value; + return this.edgeService.getEdgeUpgradeInstructions(edgeVersion, method); + } + })); + } else { + edgeInstructions$ = this.edgeService.getEdgeInstallInstructions(this.data.edge.id.id, method); + } + edgeInstructions$.subscribe(res => { + this.contentData[method] = res.instructions; + this.loadedInstructions = true; + }); + } } } diff --git a/ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts b/ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts index 9ec7d15aa0..d454e7bc83 100644 --- a/ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts @@ -241,6 +241,7 @@ const routes: Routes = [ { path: ':dashboardId', component: DashboardPageComponent, + canDeactivate: [ConfirmOnExitGuard], data: { breadcrumb: { labelFunction: dashboardBreadcumbLabelFunction, diff --git a/ui-ngx/src/app/modules/home/pages/edge/edge.component.html b/ui-ngx/src/app/modules/home/pages/edge/edge.component.html index 9c9a78f91a..3d0baad650 100644 --- a/ui-ngx/src/app/modules/home/pages/edge/edge.component.html +++ b/ui-ngx/src/app/modules/home/pages/edge/edge.component.html @@ -114,13 +114,26 @@
- +
+ + + + + + +
diff --git a/ui-ngx/src/app/modules/home/pages/edge/edge.component.ts b/ui-ngx/src/app/modules/home/pages/edge/edge.component.ts index 78c8df6c08..cdf7f1b6b6 100644 --- a/ui-ngx/src/app/modules/home/pages/edge/edge.component.ts +++ b/ui-ngx/src/app/modules/home/pages/edge/edge.component.ts @@ -26,6 +26,7 @@ import { NULL_UUID } from '@shared/models/id/has-uuid'; import { ActionNotificationShow } from '@core/notification/notification.actions'; import { generateSecret, guid } from '@core/utils'; import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; +import {EdgeService} from "@core/http/edge.service"; @Component({ selector: 'tb-edge', @@ -37,9 +38,11 @@ export class EdgeComponent extends EntityComponent { entityType = EntityType; edgeScope: 'tenant' | 'customer' | 'customer_user'; + upgradeAvailable: boolean = false; constructor(protected store: Store, protected translate: TranslateService, + private edgeService: EdgeService, @Inject('entity') protected entityValue: EdgeInfo, @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig, public fb: UntypedFormBuilder, @@ -95,6 +98,10 @@ export class EdgeComponent extends EntityComponent { } }); this.generateRoutingKeyAndSecret(entity, this.entityForm); + this.edgeService.isEdgeUpgradeAvailable(this.entity.id.id) + .subscribe(isUpgradeAvailable => { + this.upgradeAvailable = isUpgradeAvailable; + }); } updateFormState() { diff --git a/ui-ngx/src/app/modules/home/pages/edge/edges-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/edge/edges-table-config.resolver.ts index 1acaff3841..4429515705 100644 --- a/ui-ngx/src/app/modules/home/pages/edge/edges-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/edge/edges-table-config.resolver.ts @@ -29,10 +29,10 @@ import { import { TranslateService } from '@ngx-translate/core'; import { DatePipe } from '@angular/common'; import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; -import { EntityAction } from '@home/models/entity/entity-component.models'; +import { AddEntityDialogData, EntityAction } from '@home/models/entity/entity-component.models'; import { forkJoin, Observable, of } from 'rxjs'; import { select, Store } from '@ngrx/store'; -import { selectAuthUser } from '@core/auth/auth.selectors'; +import { selectAuthUser, selectUserSettingsProperty } from '@core/auth/auth.selectors'; import { map, mergeMap, take, tap } from 'rxjs/operators'; import { AppState } from '@core/core.state'; import { Authority } from '@app/shared/models/authority.enum'; @@ -51,7 +51,7 @@ import { AddEntitiesToCustomerDialogData } from '../../dialogs/add-entities-to-customer-dialog.component'; import { HomeDialogsService } from '@home/dialogs/home-dialogs.service'; -import { Edge, EdgeInfo, EdgeInstallInstructions } from '@shared/models/edge.models'; +import { Edge, EdgeInfo } from '@shared/models/edge.models'; import { EdgeService } from '@core/http/edge.service'; import { EdgeComponent } from '@home/pages/edge/edge.component'; import { EdgeTableHeaderComponent } from '@home/pages/edge/edge-table-header.component'; @@ -59,9 +59,10 @@ import { EdgeId } from '@shared/models/id/edge-id'; import { EdgeTabsComponent } from '@home/pages/edge/edge-tabs.component'; import { ActionNotificationShow } from '@core/notification/notification.actions'; import { - EdgeInstructionsData, - EdgeInstructionsDialogComponent -} from "@home/pages/edge/edge-instructions-dialog.component"; + EdgeInstructionsDialogComponent, + EdgeInstructionsDialogData +} from '@home/pages/edge/edge-instructions-dialog.component'; +import { AddEntityDialogComponent } from '@home/components/entity/add-entity-dialog.component'; @Injectable() export class EdgesTableConfigResolver implements Resolve> { @@ -140,6 +141,7 @@ export class EdgesTableConfigResolver implements Resolve this.config.componentsData.edgeScope === 'tenant'; + this.config.addEntity = () => { this.addEdge(); return of(null); }; return this.config; }) ); @@ -530,21 +532,51 @@ export class EdgesTableConfigResolver implements Resolve, + EdgeInfo>(AddEntityDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + entitiesTableConfig: this.config + } + }).afterClosed().subscribe( + (entity) => { + if (entity) { + this.store.pipe(select(selectUserSettingsProperty('notDisplayInstructionsAfterAddEdge'))).pipe( + take(1) + ).subscribe((settings: boolean) => { + if (!settings) { + this.openInstructions(null, entity, true); + } else { + this.config.updateData(); + this.config.entityAdded(entity); + } + }); + } + } + ); + } + + openInstructions($event: Event, edge: EdgeInfo, afterAdd = false, upgradeAvailable = false) { if ($event) { $event.stopPropagation(); } - this.edgeService.getEdgeDockerInstallInstructions(edge.id.id).subscribe( - (edgeInstructionsTemplate: EdgeInstallInstructions) => { - this.dialog.open(EdgeInstructionsDialogComponent, { - disableClose: false, - panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], - data: { - instructions: edgeInstructionsTemplate.dockerInstallInstructions - } - }); + this.dialog.open + (EdgeInstructionsDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + edge, + afterAdd, + upgradeAvailable } - ) + }).afterClosed().subscribe(() => { + if (afterAdd) { + this.config.updateData(); + } + } + ); } onEdgeAction(action: EntityAction, config: EntityTableConfig): boolean { @@ -579,9 +611,12 @@ export class EdgesTableConfigResolver implements Resolve\n \n
", - "applyDefaultMarkdownStyle": false, - "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n}\n\n.tb-content-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n gap: 12px;\n}\n\n.tb-card-header {\n height: 36px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-item-cards {\n flex: 1;\n display: flex;\n flex-direction: row;\n gap: 12px;\n}\n\na.tb-item-card {\n flex: 1;\n display: flex;\n flex-direction: column;\n padding: 8px 12px;\n border: 1px solid;\n border-radius: 10px;\n margin-bottom: 12px;\n}\n\na.tb-item-card.tb-inactive {\n background: rgba(209, 39, 48, 0.04);\n border-color: rgba(209, 39, 48, 0.06);\n}\n\na.tb-item-card.tb-active {\n background: rgba(48, 86, 128, 0.04);\n border-color: rgba(48, 86, 128, 0.12);\n}\n\na.tb-item-card.tb-total {\n background: rgba(0, 0, 0, 0.01);\n border-color: rgba(0, 0, 0, 0.05);\n}\n\n.tb-item-title-container {\n display: grid;\n}\n\n.tb-item-title {\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis; \n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-item-title.tb-home-widget-link:after {\n position: absolute;\n right: 0;\n}\n\na.tb-item-card:hover .tb-item-title.tb-home-widget-link:after { \n color: rgba(0, 0, 0, 0.38);\n}\n\na.tb-item-card:hover {\n box-shadow: 0px 4px 10px rgba(23, 33, 90, 0.08);\n}\n\n.tb-count-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 24px;\n line-height: 36px;\n white-space: nowrap;\n color: rgba(0, 0, 0, 0.87);\n}\n\n@media screen and (max-width: 959px) {\n .tb-item-cards {\n flex-direction: column;\n }\n a.tb-item-card {\n margin-bottom: 0;\n }\n}\n\n@media screen and (max-width: 1279px) {\n a.tb-item-card {\n flex-direction: row;\n align-items: center;\n }\n .tb-item-title.tb-home-widget-link:after {\n position: relative;\n }\n .tb-count-container {\n align-items: flex-end;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-item-title {\n font-size: 11px;\n line-height: 16px;\n }\n .tb-count {\n font-size: 16px;\n line-height: 24px;\n }\n a.tb-item-card {\n padding: 4px 8px;\n margin-bottom: 6px;\n }\n a.tb-item-card:hover {\n box-shadow: 0px 2px 5px rgba(23, 33, 90, 0.08);\n }\n}\n" - }, - "title": "Devices", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "35d3ac3b-f1af-dc3d-a1d1-11f351d16fc6" - }, - "7ac20b6a-dc40-b18e-9f5f-bca20bc693bb": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [ - { - "type": "alarmCount", - "name": null, - "entityAliasId": null, - "filterId": null, - "dataKeys": [ - { - "name": "count", - "type": "count", - "label": "totalAlarms", - "color": "#ffc107", - "settings": {}, - "_hash": 0.8743321483507485, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ], - "alarmFilterConfig": { - "statusList": [ - "ACTIVE" - ] - } - }, - { - "type": "alarmCount", - "name": null, - "entityAliasId": null, - "filterId": null, - "dataKeys": [ - { - "name": "count", - "type": "count", - "label": "assignedToMeAlarms", - "color": "#ffc107", - "settings": {}, - "_hash": 0.6392390736755882, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ], - "alarmFilterConfig": { - "statusList": [ - "ACTIVE" - ], - "assignedToCurrentUser": true - } - }, - { - "type": "alarmCount", - "name": null, - "entityAliasId": null, - "filterId": null, - "dataKeys": [ - { - "name": "count", - "type": "count", - "label": "criticalAlarms", - "color": "#ffc107", - "settings": {}, - "_hash": 0.8329478098552843, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ], - "alarmFilterConfig": { - "statusList": [ - "ACTIVE" - ], - "severityList": [ - "CRITICAL" - ] - } - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "useMarkdownTextFunction": false, - "markdownTextPattern": "", - "applyDefaultMarkdownStyle": false, - "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n}\n\n.tb-content-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n gap: 12px;\n}\n\n.tb-card-header {\n height: 36px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-item-cards {\n flex: 1;\n display: flex;\n flex-direction: row;\n gap: 12px;\n}\n\na.tb-item-card {\n flex: 1;\n display: flex;\n flex-direction: column;\n padding: 8px 12px;\n border: 1px solid;\n border-radius: 10px;\n margin-bottom: 12px;\n}\n\na.tb-item-card.tb-critical {\n background: rgba(209, 39, 48, 0.04);\n border-color: rgba(209, 39, 48, 0.06);\n}\n\na.tb-item-card.tb-assigned {\n background: rgba(48, 86, 128, 0.04);\n border-color: rgba(48, 86, 128, 0.12);\n}\n\na.tb-item-card.tb-total {\n background: rgba(0, 0, 0, 0.01);\n border-color: rgba(0, 0, 0, 0.05);\n}\n\n.tb-item-title-container {\n display: grid;\n}\n\n.tb-item-title {\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis; \n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-item-title.tb-home-widget-link:after {\n position: absolute;\n right: 0;\n}\n\na.tb-item-card:hover .tb-item-title.tb-home-widget-link:after { \n color: rgba(0, 0, 0, 0.38);\n}\n\na.tb-item-card:hover {\n box-shadow: 0px 4px 10px rgba(23, 33, 90, 0.08);\n}\n\n.tb-count-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 24px;\n line-height: 36px;\n white-space: nowrap;\n color: rgba(0, 0, 0, 0.87);\n}\n\na.tb-item-card.tb-critical .tb-count:after {\n content: \"warning\";\n display: inline-block;\n position: relative;\n font-family: 'Material Icons Round';\n color: #D12730;\n vertical-align: bottom;\n margin-left: 6px;\n}\n\n@media screen and (max-width: 959px) {\n .tb-item-cards {\n flex-direction: column;\n }\n a.tb-item-card {\n margin-bottom: 0;\n }\n}\n\n@media screen and (max-width: 1279px) {\n a.tb-item-card {\n flex-direction: row;\n align-items: center;\n }\n .tb-item-title.tb-home-widget-link:after {\n position: relative;\n }\n .tb-count-container {\n align-items: flex-end;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-item-title {\n font-size: 11px;\n line-height: 16px;\n }\n .tb-count {\n font-size: 16px;\n line-height: 24px;\n }\n a.tb-item-card {\n padding: 4px 8px;\n margin-bottom: 6px;\n }\n a.tb-item-card:hover {\n box-shadow: 0px 2px 5px rgba(23, 33, 90, 0.08);\n }\n a.tb-item-card.tb-critical .tb-count:after {\n margin-left: 2px;\n }\n}\n" - }, - "title": "Alarms", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "7ac20b6a-dc40-b18e-9f5f-bca20bc693bb" - } - }, - "states": { - "default": { - "name": "Customer User Home Page", - "root": true, - "layouts": { - "main": { - "widgets": { - "867f82cf-ecf2-2d5c-35cb-08c6f2edc3a4": { - "sizeX": 42, - "sizeY": 16, - "row": 42, - "col": 78, - "mobileHide": false, - "mobileOrder": 4, - "mobileHeight": 7 - }, - "9e3ef045-d8bc-1640-a3f4-2dd10b19d50e": { - "sizeX": 42, - "sizeY": 16, - "row": 26, - "col": 78, - "mobileHide": false, - "mobileOrder": 3, - "mobileHeight": 7 - }, - "6d6e2b1d-6ce7-4678-3745-c6b0897b2674": { - "sizeX": 78, - "sizeY": 58, - "row": 0, - "col": 0, - "mobileOrder": 2, - "mobileHeight": 10 - }, - "35d3ac3b-f1af-dc3d-a1d1-11f351d16fc6": { - "sizeX": 42, - "sizeY": 13, - "row": 13, - "col": 78, - "mobileHeight": 9, - "mobileOrder": 1 - }, - "7ac20b6a-dc40-b18e-9f5f-bca20bc693bb": { - "sizeX": 42, - "sizeY": 13, - "mobileHeight": 9, - "row": 0, - "col": 78, - "mobileOrder": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "columns": 120, - "margin": 12, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 20, - "outerMargin": true - } - } - } - } - }, - "entityAliases": { - "a1ddb8fa-90ff-5598-e7f2-e254194d055d": { - "id": "a1ddb8fa-90ff-5598-e7f2-e254194d055d", - "alias": "Devices", - "filter": { - "type": "entityType", - "resolveMultiple": true, - "entityType": "DEVICE" - } - }, - "d9229b29-3f46-de8d-7fe8-eb0c43c75079": { - "id": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", - "alias": "Api Usage State", - "filter": { - "type": "apiUsageState", - "resolveMultiple": true - } - } - }, - "filters": { - "42f42812-92fe-a13a-b0ba-c6e0dfab3584": { - "id": "42f42812-92fe-a13a-b0ba-c6e0dfab3584", - "filter": "Active Devices", - "keyFilters": [ - { - "key": { - "type": "ATTRIBUTE", - "key": "active" - }, - "valueType": "BOOLEAN", - "predicates": [ - { - "keyFilterPredicate": { - "operation": "EQUAL", - "value": { - "defaultValue": true, - "dynamicValue": null - }, - "type": "BOOLEAN" - }, - "userInfo": { - "editable": true, - "label": "", - "autogeneratedLabel": true, - "order": 0 - } - } - ] - } - ], - "editable": false - }, - "39d92c21-2426-7346-4484-452829146c33": { - "id": "39d92c21-2426-7346-4484-452829146c33", - "filter": "Inactive Devices", - "keyFilters": [ - { - "key": { - "type": "ATTRIBUTE", - "key": "active" - }, - "valueType": "BOOLEAN", - "predicates": [ - { - "keyFilterPredicate": { - "operation": "EQUAL", - "value": { - "defaultValue": false, - "dynamicValue": null - }, - "type": "BOOLEAN" - }, - "userInfo": { - "editable": true, - "label": "", - "autogeneratedLabel": true, - "order": 0 - } - } - ] - } - ], - "editable": false - } - }, - "timewindow": { - "displayValue": "", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "hideTimezone": false, - "selectedTab": 0, - "realtime": { - "realtimeType": 0, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168326072, - "endTimeMs": 1680254726072 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "settings": { - "stateControllerId": "entity", - "showTitle": false, - "showDashboardsSelect": true, - "showEntitiesSelect": true, - "showDashboardTimewindow": true, - "showDashboardExport": true, - "toolbarAlwaysOpen": true, - "titleColor": "rgba(0,0,0,0.870588)", - "showDashboardLogo": false, - "dashboardLogoUrl": null, - "hideToolbar": true, - "showFilters": true, - "showUpdateDashboardImage": true, - "dashboardCss": ".tb-widget-container > .tb-widget {\n border: 1px solid rgba(0, 0, 0, 0.05);\n box-shadow: 0px 5px 16px rgba(0, 0, 0, 0.04);\n border-radius: 12px;\n}\n\n.tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 16px !important;\n}\n\n.tb-card-title {\n display: grid;\n}\n\n.tb-home-widget-title {\n font-style: normal;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n color: rgba(0, 0, 0, 0.54);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.tb-home-widget-link {\n position: relative;\n border-bottom: none;\n}\n\n.tb-home-widget-link:hover {\n border-bottom: none;\n}\n\n.tb-home-widget-link:focus {\n border-bottom: none;\n}\n\n.tb-home-widget-link::after {\n content: 'arrow_forward';\n display: inline-block;\n transform: rotate(315deg);\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n font-size: 18px;\n color: rgba(0, 0, 0, 0.12);\n vertical-align: bottom;\n margin-left: 6px; \n}\n\n.tb-home-widget-link:hover::after {\n color: inherit;\n}\n\n.tb-home-widget-info-icon {\n color: rgba(0, 0, 0, 0.12);\n font-size: 16px;\n width: 16px;\n height: 16px;\n line-height: 15px;\n vertical-align: middle;\n}\n\n.tb-widget-container > .tb-widget .tb-timewindow {\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n color: rgba(0, 0, 0, 0.54);\n padding: 0;\n}\n\n.tb-widget-container > .tb-widget .tb-legend-keys .tb-legend-label {\n cursor: pointer;\n user-select: none;\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n color: rgba(0, 0, 0, 0.54);\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .tb-widget-container > .tb-widget {\n border-radius: 4px;\n }\n .tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 2px !important;\n }\n .tb-hide-md {\n display: none;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1819px) {\n .tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 8px !important;\n }\n .tb-hide-lg {\n display: none;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-hide-md-lg {\n display: none;\n }\n\n .tb-home-widget-title {\n font-size: 12px;\n line-height: 16px;\n }\n \n .tb-widget-container > .tb-widget .tb-widget-title {\n padding: 0;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow {\n font-size: 12px;\n line-height: 16px;\n min-height: 24px;\n padding: 0;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow .mat-mdc-icon-button.tb-mat-32 {\n width: 24px;\n height: 24px;\n line-height: 24px;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow .mat-mdc-icon-button.tb-mat-32 .mat-icon {\n width: 18px;\n height: 18px;\n font-size: 18px;\n }\n \n .tb-widget-container > .tb-widget tb-legend {\n padding-bottom: 0 !important;\n }\n \n .tb-widget-container > .tb-widget .tb-legend-keys .tb-legend-label {\n font-size: 11px;\n line-height: 16px;\n letter-spacing: 0.25px;\n }\n}\n\n@media screen and (max-width: 959px), screen and (min-width: 1820px) {\n .tb-hide-not-md-lg {\n display: none;\n }\n}\n" - } - }, - "externalId": null, - "name": "Customer User Home Page" -} \ No newline at end of file diff --git a/ui-ngx/src/app/modules/home/pages/home-links/home-links-routing.module.ts b/ui-ngx/src/app/modules/home/pages/home-links/home-links-routing.module.ts index 5edc9cb7fc..a11ae14231 100644 --- a/ui-ngx/src/app/modules/home/pages/home-links/home-links-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/home-links/home-links-routing.module.ts @@ -14,8 +14,8 @@ /// limitations under the License. /// -import { Injectable, NgModule } from '@angular/core'; -import { Resolve, RouterModule, Routes } from '@angular/router'; +import { inject, NgModule } from '@angular/core'; +import { ActivatedRouteSnapshot, ResolveFn, RouterModule, RouterStateSnapshot, Routes } from '@angular/router'; import { HomeLinksComponent } from './home-links.component'; import { Authority } from '@shared/models/authority.enum'; @@ -25,57 +25,19 @@ import { DashboardService } from '@core/http/dashboard.service'; import { select, Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { map } from 'rxjs/operators'; -import { - getCurrentAuthUser, - selectHasRepository, - selectPersistDeviceStateToTelemetry -} from '@core/auth/auth.selectors'; -import sysAdminHomePageDashboardJson from '!raw-loader!./sys_admin_home_page.raw'; -import tenantAdminHomePageDashboardJson from '!raw-loader!./tenant_admin_home_page.raw'; -import customerUserHomePageDashboardJson from '!raw-loader!./customer_user_home_page.raw'; +import { getCurrentAuthUser, selectPersistDeviceStateToTelemetry } from '@core/auth/auth.selectors'; import { EntityKeyType } from '@shared/models/query/query.models'; +import { ResourcesService } from '@core/services/resources.service'; -@Injectable() -export class HomeDashboardResolver implements Resolve { +const sysAdminHomePageJson = '/assets/dashboard/sys_admin_home_page.json'; +const tenantAdminHomePageJson = '/assets/dashboard/tenant_admin_home_page.json'; +const customerUserHomePageJson = '/assets/dashboard/customer_user_home_page.json'; - constructor(private dashboardService: DashboardService, - private store: Store) { - } - - resolve(): Observable { - return this.dashboardService.getHomeDashboard().pipe( - mergeMap((dashboard) => { - if (!dashboard) { - let dashboard$: Observable; - const authority = getCurrentAuthUser(this.store).authority; - switch (authority) { - case Authority.SYS_ADMIN: - dashboard$ = of(JSON.parse(sysAdminHomePageDashboardJson)); - break; - case Authority.TENANT_ADMIN: - dashboard$ = this.updateDeviceActivityKeyFilterIfNeeded(JSON.parse(tenantAdminHomePageDashboardJson)); - break; - case Authority.CUSTOMER_USER: - dashboard$ = this.updateDeviceActivityKeyFilterIfNeeded(JSON.parse(customerUserHomePageDashboardJson)); - break; - } - if (dashboard$) { - return dashboard$.pipe( - map((homeDashboard) => { - homeDashboard.hideDashboardToolbar = true; - return homeDashboard; - }) - ); - } - } - return of(dashboard); - }) - ); - } - - private updateDeviceActivityKeyFilterIfNeeded(dashboard: HomeDashboard): Observable { - return this.store.pipe(select(selectPersistDeviceStateToTelemetry)).pipe( - map((persistToTelemetry) => { +const updateDeviceActivityKeyFilterIfNeeded = (store: Store, + dashboard$: Observable): Observable => + store.pipe(select(selectPersistDeviceStateToTelemetry)).pipe( + mergeMap((persistToTelemetry) => dashboard$.pipe( + map((dashboard) => { if (persistToTelemetry) { for (const filterId of Object.keys(dashboard.configuration.filters)) { if (['Active Devices', 'Inactive Devices'].includes(dashboard.configuration.filters[filterId].filter)) { @@ -85,9 +47,44 @@ export class HomeDashboardResolver implements Resolve { } return dashboard; }) - ); - } -} + )) + ); + +export const homeDashboardResolver: ResolveFn = ( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot, + dashboardService = inject(DashboardService), + resourcesService = inject(ResourcesService), + store: Store = inject(Store) +): Observable => + dashboardService.getHomeDashboard().pipe( + mergeMap((dashboard) => { + if (!dashboard) { + let dashboard$: Observable; + const authority = getCurrentAuthUser(store).authority; + switch (authority) { + case Authority.SYS_ADMIN: + dashboard$ = resourcesService.loadJsonResource(sysAdminHomePageJson); + break; + case Authority.TENANT_ADMIN: + dashboard$ = updateDeviceActivityKeyFilterIfNeeded(store, resourcesService.loadJsonResource(tenantAdminHomePageJson)); + break; + case Authority.CUSTOMER_USER: + dashboard$ = updateDeviceActivityKeyFilterIfNeeded(store, resourcesService.loadJsonResource(customerUserHomePageJson)); + break; + } + if (dashboard$) { + return dashboard$.pipe( + map((homeDashboard) => { + homeDashboard.hideDashboardToolbar = true; + return homeDashboard; + }) + ); + } + } + return of(dashboard); + }) + ); const routes: Routes = [ { @@ -102,16 +99,13 @@ const routes: Routes = [ } }, resolve: { - homeDashboard: HomeDashboardResolver + homeDashboard: homeDashboardResolver } } ]; @NgModule({ imports: [RouterModule.forChild(routes)], - exports: [RouterModule], - providers: [ - HomeDashboardResolver - ] + exports: [RouterModule] }) export class HomeLinksRoutingModule { } diff --git a/ui-ngx/src/app/modules/home/pages/home-links/home-links.component.html b/ui-ngx/src/app/modules/home/pages/home-links/home-links.component.html index 23619e2632..521f54f53d 100644 --- a/ui-ngx/src/app/modules/home/pages/home-links/home-links.component.html +++ b/ui-ngx/src/app/modules/home/pages/home-links/home-links.component.html @@ -27,8 +27,7 @@ - {{place.icon}} - + {{place.icon}} {{place.name}} diff --git a/ui-ngx/src/app/modules/home/pages/home-links/home-links.component.scss b/ui-ngx/src/app/modules/home/pages/home-links/home-links.component.scss index a30605652b..9acc1490ba 100644 --- a/ui-ngx/src/app/modules/home/pages/home-links/home-links.component.scss +++ b/ui-ngx/src/app/modules/home/pages/home-links/home-links.component.scss @@ -55,7 +55,7 @@ display: flex; flex-direction: column; align-items: center; - mat-icon { + .mat-icon { margin: auto; } span.mdc-button__label { diff --git a/ui-ngx/src/app/modules/home/pages/home-links/sys_admin_home_page.raw b/ui-ngx/src/app/modules/home/pages/home-links/sys_admin_home_page.raw deleted file mode 100644 index b1dfe94f86..0000000000 --- a/ui-ngx/src/app/modules/home/pages/home-links/sys_admin_home_page.raw +++ /dev/null @@ -1,2724 +0,0 @@ -{ - "title": "System Administrator Home Page", - "image": null, - "mobileHide": false, - "mobileOrder": null, - "configuration": { - "description": "", - "widgets": { - "d70cc256-4c7b-ee06-9905-b8c5e546605f": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "useMarkdownTextFunction": false, - "markdownTextPattern": "
\n
\n
{{ 'widgets.transport-messages.title' | translate }}\n info\n
\n
\n \n
", - "applyDefaultMarkdownStyle": false, - "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n}\n" - }, - "title": "Transport messages", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "d70cc256-4c7b-ee06-9905-b8c5e546605f" - }, - "8ee72d43-678c-4e25-e9a8-7d4cfd7a5f8e": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", - "filterId": null, - "dataKeys": [ - { - "name": "transportMsgCountHourly", - "type": "timeseries", - "label": "{i18n:widgets.transport-messages.title}", - "color": "#305680", - "settings": {}, - "_hash": 0.2880464219129071, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ], - "latestDataKeys": [] - } - ], - "timewindow": { - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false, - "hideAggregation": true, - "hideAggInterval": false, - "hideTimezone": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000, - "fixedTimewindow": { - "startTimeMs": 1680443065451, - "endTimeMs": 1680529465451 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "SUM", - "limit": 50000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "0px", - "settings": { - "stack": true, - "fontSize": 10, - "fontColor": "#545454", - "showTooltip": true, - "tooltipIndividual": false, - "tooltipCumulative": false, - "hideZeros": false, - "grid": { - "verticalLines": false, - "horizontalLines": false, - "outlineWidth": 0, - "color": "#545454", - "backgroundColor": null, - "tickColor": "#DDDDDD" - }, - "xaxis": { - "title": null, - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "min": 0, - "max": null, - "title": null, - "showLabels": true, - "color": "#545454", - "tickSize": null, - "tickDecimals": 0, - "ticksFormatter": "return value % 1000 === 0 ? ((value / 1000) + 'k') : '';" - }, - "defaultBarWidth": 1800000, - "barAlignment": "left", - "comparisonEnabled": false, - "timeForComparison": "previousInterval", - "comparisonCustomIntervalValue": 7200000, - "xaxisSecond": { - "axisPosition": "top", - "title": null, - "showLabels": true - }, - "customLegendEnabled": false, - "dataKeysListForLabels": [] - }, - "title": "Transport messages", - "dropShadow": false, - "enableFullscreen": false, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": { - "padding": "0" - }, - "useDashboardTimewindow": false, - "showLegend": false, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "titleTooltip": "", - "widgetCss": ".tb-widget-container > .tb-widget {\n border: none !important;\n border-radius: 0 !important;\n box-shadow: none !important;\n}\n\n.tb-widget-container > .tb-widget .flot-base {\n opacity: 0.48;\n}\n", - "pageSize": 1024, - "noDataDisplayMessage": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": true, - "showTotal": false, - "showLatest": false - } - }, - "row": 0, - "col": 0, - "id": "8ee72d43-678c-4e25-e9a8-7d4cfd7a5f8e" - }, - "4b5e47ed-c197-a937-d727-041ba8decec2": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", - "filterId": null, - "dataKeys": [ - { - "name": "clusterMode", - "type": "timeseries", - "label": "clusterMode", - "color": "#2196f3", - "settings": {}, - "_hash": 0.7272123990942316, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": false, - "postFuncBody": "" - } - ] - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#ffffff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "0px", - "settings": { - "useMarkdownTextFunction": true, - "markdownTextFunction": "let html = '
';\nlet clusterMode = data[0]['clusterMode'];\nif (clusterMode === 'true') {\n html += '';\n} else {\n html += '';\n}\nhtml += '
';\nreturn html;", - "applyDefaultMarkdownStyle": false - }, - "title": "System info", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": { - "border": "none", - "boxShadow": "none", - "padding": "0" - }, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "4b5e47ed-c197-a937-d727-041ba8decec2" - }, - "eace9148-b02a-48fe-1a95-acd9928aa8c5": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "basic_timeseries", - "type": "timeseries", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", - "filterId": null, - "dataKeys": [ - { - "name": "cpuUsage", - "type": "timeseries", - "label": "{i18n:widgets.system-info.cpu}", - "color": "#305680", - "settings": { - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "excludeFromStacking": false, - "showLines": true, - "lineWidth": 3, - "fillLines": false, - "showPoints": false, - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showSeparateAxis": false, - "axisPosition": "left", - "comparisonSettings": { - "showValuesForComparison": true, - "comparisonValuesLabel": "", - "color": "" - }, - "thresholds": [] - }, - "_hash": 0.9347575372081658, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "memoryUsage", - "type": "timeseries", - "label": "{i18n:widgets.system-info.ram}", - "color": "#ac3bc9", - "settings": { - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "excludeFromStacking": false, - "showLines": true, - "lineWidth": 3, - "fillLines": false, - "showPoints": false, - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showSeparateAxis": false, - "axisPosition": "left", - "comparisonSettings": { - "showValuesForComparison": true, - "comparisonValuesLabel": "", - "color": "" - }, - "thresholds": [] - }, - "_hash": 0.31887216598848855, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "discUsage", - "type": "timeseries", - "label": "{i18n:widgets.system-info.disk}", - "color": "#40d0ae", - "settings": { - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "excludeFromStacking": false, - "showLines": true, - "lineWidth": 3, - "fillLines": false, - "showPoints": false, - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showSeparateAxis": false, - "axisPosition": "left", - "comparisonSettings": { - "showValuesForComparison": true, - "comparisonValuesLabel": "", - "color": "" - }, - "thresholds": [] - }, - "_hash": 0.26499182606431004, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ], - "latestDataKeys": null - } - ], - "timewindow": { - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "hideTimezone": false, - "selectedTab": 0, - "realtime": { - "realtimeType": 0, - "timewindowMs": 3600000, - "quickInterval": "CURRENT_DAY", - "interval": 10000 - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "stack": false, - "fontSize": 10, - "fontColor": "#545454", - "showTooltip": true, - "tooltipIndividual": false, - "tooltipCumulative": false, - "hideZeros": false, - "grid": { - "verticalLines": false, - "horizontalLines": false, - "outlineWidth": 0, - "color": "#545454", - "backgroundColor": null, - "tickColor": "#DDDDDD" - }, - "xaxis": { - "title": null, - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "min": 0, - "max": 100, - "title": null, - "showLabels": true, - "color": "#545454", - "tickSize": null, - "tickDecimals": 0, - "ticksFormatter": "" - }, - "shadowSize": 0, - "smoothLines": true, - "comparisonEnabled": false, - "timeForComparison": "previousInterval", - "comparisonCustomIntervalValue": 7200000, - "xaxisSecond": { - "axisPosition": "top", - "title": null, - "showLabels": true - }, - "customLegendEnabled": false, - "dataKeysListForLabels": [] - }, - "title": "System Info Chart", - "dropShadow": false, - "enableFullscreen": false, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "useDashboardTimewindow": false, - "showTitleIcon": false, - "titleTooltip": "", - "widgetStyle": {}, - "widgetCss": "\n.tb-legend-keys td .tb-legend-line {\n height: 5px !important;\n vertical-align: middle;\n}\n", - "pageSize": 1024, - "units": "%", - "noDataDisplayMessage": "", - "showLegend": true, - "legendConfig": { - "direction": "row", - "position": "top", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": false, - "showLatest": false - }, - "displayTimewindow": true - }, - "row": 0, - "col": 0, - "id": "eace9148-b02a-48fe-1a95-acd9928aa8c5" - }, - "8acbf5df-f9fc-114d-216f-86f081aa4779": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "useMarkdownTextFunction": false, - "markdownTextPattern": "
\n
\n
\n
widgets.functions.title
\n
\n {{ 'widgets.functions.switch-to-pe' | translate }}\n
\n
\n
\n
widgets.functions.alarms
\n
widgets.functions.dashboards
\n
widgets.functions.entities-and-relations
\n
widgets.functions.profiles
\n
widgets.functions.advanced-features
\n
\n
\n
widgets.functions.notification-center
\n
widgets.functions.api-usage
\n
widgets.functions.customers
\n
widgets.functions.customers-hierarchy
\n
widgets.functions.roles-and-permissions
\n
\n
\n
widgets.functions.groups
\n
widgets.functions.integrations
\n
widgets.functions.solution-templates
\n
widgets.functions.scheduler
\n
widgets.functions.white-labeling
\n
\n
\n
", - "applyDefaultMarkdownStyle": false, - "markdownCss": ".tb-card-header {\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n}\n\n.tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n margin-top: 4px;\n overflow: auto;\n}\n\n.tb-card-column {\n min-width: 0px;\n flex: 1;\n display: flex;\n flex-direction: column;\n}\n\n.feature {\n position: relative;\n cursor: default;\n font-weight: 400;\n font-size: 14px;\n line-height: 28px;\n letter-spacing: 0.2px;\n color: rgba(0, 0, 0, 0.76);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.feature:before {\n content: \"check\";\n font-family: 'Material Icons Round';\n display: inline-block;\n font-size: 20px;\n line-height: 20px;\n color: #198038;\n font-weight: 600;\n position: relative;\n background: #F3F6FA;\n border-radius: 4px;\n width: 20px;\n height: 20px;\n padding: 2px;\n vertical-align: sub;\n margin-right: 8px;\n}\n\n.feature.pe:before {\n content: \"close\";\n background: #F4F4F4;\n color: rgba(0, 0, 0, 0.12);\n}\n\n.feature.pe:after {\n position: relative;\n display: inline-block;\n margin-left: 4px;\n vertical-align: baseline;\n top: -0.5em;\n content: \"PE\";\n color: #305680;\n font-weight: 700;\n font-size: 12px;\n line-height: 12px;\n letter-spacing: 0.0015em;\n}\n\n.feature+.feature {\n margin-top: 4px;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-switch-to-pe {\n height: 24px;\n font-size: 12px;\n padding: 0 4px;\n }\n \n .feature+.feature {\n margin-top: 2px;\n }\n \n .feature {\n font-size: 11px;\n line-height: 18px;\n }\n\n .feature:before {\n font-size: 14px;\n line-height: 14px;\n width: 14px;\n height: 14px;\n margin-right: 4px;\n }\n .feature.pe:after {\n margin-left: 2px;\n top: -0.5em;\n content: \"PE\";\n font-size: 8px;\n line-height: 8px;\n }\n \n}\n\n" - }, - "title": "Functions", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "8acbf5df-f9fc-114d-216f-86f081aa4779" - }, - "d2784f6c-0518-fd95-1d28-b21f70bdcb10": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "useMarkdownTextFunction": false, - "markdownTextPattern": "
\n \n
", - "applyDefaultMarkdownStyle": false - }, - "title": "Configured features", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "d2784f6c-0518-fd95-1d28-b21f70bdcb10" - }, - "66dcf1a2-9d83-6873-c693-d6a5d989b3f8": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "useMarkdownTextFunction": false, - "markdownTextPattern": "
\n \n
", - "applyDefaultMarkdownStyle": false - }, - "title": "Version", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "66dcf1a2-9d83-6873-c693-d6a5d989b3f8" - }, - "867f82cf-ecf2-2d5c-35cb-08c6f2edc3a4": { - "isSystemType": true, - "bundleAlias": "home_page_widgets", - "typeAlias": "documentation_links", - "type": "static", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 7.5, - "sizeY": 3, - "config": { - "datasources": [], - "timewindow": { - "realtime": { - "timewindowMs": 60000 - } - }, - "showTitle": false, - "backgroundColor": "rgb(255, 255, 255)", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "columns": 3 - }, - "title": "Documentation", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "", - "showLegend": false - }, - "row": 0, - "col": 0, - "id": "867f82cf-ecf2-2d5c-35cb-08c6f2edc3a4" - }, - "a23185ad-dc46-806c-0e50-5b21fb080ace": { - "isSystemType": true, - "bundleAlias": "home_page_widgets", - "typeAlias": "getting_started", - "type": "static", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 7.5, - "sizeY": 6.5, - "config": { - "datasources": [], - "timewindow": { - "realtime": { - "timewindowMs": 60000 - } - }, - "showTitle": false, - "backgroundColor": "rgb(255, 255, 255)", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "columns": 3 - }, - "title": "Getting started", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "", - "showLegend": false - }, - "row": 0, - "col": 0, - "id": "a23185ad-dc46-806c-0e50-5b21fb080ace" - }, - "b35fa1b0-7f95-0b23-fade-d066653a40e1": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", - "filterId": null, - "dataKeys": [ - { - "name": "cpuUsage", - "type": "timeseries", - "label": "cpuUsage", - "color": "#2196f3", - "settings": {}, - "_hash": 0.659021918423117 - }, - { - "name": "cpuCount", - "type": "timeseries", - "label": "cpuCount", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5332753234169949 - }, - { - "name": "cpuUsage", - "type": "timeseries", - "label": "status", - "color": "#f44336", - "settings": {}, - "_hash": 0.025084892962804473, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "var status = 'status-ok';\nif (value > 85) {\n status = 'status-critical';\n} else if (value > 75) {\n status = 'status-warn';\n}\nreturn status;" - }, - { - "name": "cpuUsage", - "type": "timeseries", - "label": "statusTooltip", - "color": "#ffc107", - "settings": {}, - "_hash": 0.8542003006820891, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "var text = '';\nif (value > 85) {\n text = '{{ \\'widgets.system-info.cpu-critical-text\\' | translate }}';\n} else if (value > 75) {\n text = '{{ \\'widgets.system-info.cpu-warning-text\\' | translate }}';\n}\nreturn text;\n" - } - ] - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "useMarkdownTextFunction": false, - "markdownTextPattern": "
\n
\n
widgets.system-info.cpu
\n
\n
\n
\n ${cpuUsage:0}%\n | ${cpuCount:0} cores\n
\n
", - "applyDefaultMarkdownStyle": false, - "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: flex-start;\n}\n\n.tb-card-title {\n width: 100%;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-status {\n width: 24px;\n height: 24px;\n position: relative;\n}\n\n.tb-status.status-ok {\n background: #F3F6FA;\n border-radius: 4px;\n}\n\n.tb-status:after {\n position: absolute;\n top: 2px;\n left: 2px;\n font-family: 'Material Icons Round';\n font-size: 20px;\n line-height: 1;\n}\n\n.tb-status.status-ok:after {\n content: \"check\";\n color: #198038;\n font-weight: 600;\n}\n\n.tb-status.status-warn:after {\n content: \"warning\";\n color: #FAA405;\n}\n\n.tb-status.status-critical:after {\n content: \"warning\";\n color: #D12730;\n}\n\n.tb-value-container {\n user-select: none;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 26px;\n line-height: 36px;\n}\n\n.tb-total {\n padding-left: 4px;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n color: rgba(0, 0, 0, 0.54);\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-status {\n width: 18px;\n height: 18px;\n }\n .tb-status:after {\n font-size: 14px;\n }\n .tb-count {\n font-size: 16px;\n line-height: 24px;\n }\n .tb-total {\n font-size: 11px;\n line-height: 16px;\n }\n}" - }, - "title": "CPU", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "b35fa1b0-7f95-0b23-fade-d066653a40e1" - }, - "5eabde4c-095f-defc-dd09-6c0afb9b9f7b": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", - "filterId": null, - "dataKeys": [ - { - "name": "memoryUsage", - "type": "timeseries", - "label": "memoryUsage", - "color": "#2196f3", - "settings": {}, - "_hash": 0.659021918423117, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "totalMemory", - "type": "timeseries", - "label": "totalMemory", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5332753234169949, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value / 1073741824;" - }, - { - "name": "memoryUsage", - "type": "timeseries", - "label": "status", - "color": "#f44336", - "settings": {}, - "_hash": 0.025084892962804473, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "var status = 'status-ok';\nif (value > 85) {\n status = 'status-critical';\n} else if (value > 75) {\n status = 'status-warn';\n}\nreturn status;" - }, - { - "name": "memoryUsage", - "type": "timeseries", - "label": "statusTooltip", - "color": "#ffc107", - "settings": {}, - "_hash": 0.8542003006820891, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "var text = '';\nif (value > 85) {\n text = '{{ \\'widgets.system-info.ram-critical-text\\' | translate }}';\n} else if (value > 75) {\n text = '{{ \\'widgets.system-info.ram-warning-text\\' | translate }}';\n}\nreturn text;\n" - } - ] - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "useMarkdownTextFunction": true, - "markdownTextFunction": "const memoryUsage = data[0]['memoryUsage'];\nconst totalMemory = data[0]['totalMemory'];\nconst usedMemory = (totalMemory * (memoryUsage / 100)).toFixed(0);\n\n\nreturn '
' +\n '
' +\n '
widgets.system-info.ram
' +\n '
' +\n '
' +\n '
' +\n '${memoryUsage:0}%' +\n '| ${totalMemory:0} Gb' +\n '
' +\n '
';", - "applyDefaultMarkdownStyle": false, - "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: flex-start;\n}\n\n.tb-card-title {\n width: 100%;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-status {\n width: 24px;\n height: 24px;\n position: relative;\n}\n\n.tb-status.status-ok {\n background: #F3F6FA;\n border-radius: 4px;\n}\n\n.tb-status:after {\n position: absolute;\n top: 2px;\n left: 2px;\n font-family: 'Material Icons Round';\n font-size: 20px;\n line-height: 1;\n}\n\n.tb-status.status-ok:after {\n content: \"check\";\n color: #198038;\n font-weight: 600;\n}\n\n.tb-status.status-warn:after {\n content: \"warning\";\n color: #FAA405;\n}\n\n.tb-status.status-critical:after {\n content: \"warning\";\n color: #D12730;\n}\n\n.tb-value-container {\n user-select: none;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 26px;\n line-height: 36px;\n}\n\n.tb-total {\n padding-left: 4px;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n color: rgba(0, 0, 0, 0.54);\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-status {\n width: 18px;\n height: 18px;\n }\n .tb-status:after {\n font-size: 14px;\n }\n .tb-count {\n font-size: 16px;\n line-height: 24px;\n }\n .tb-total {\n font-size: 11px;\n line-height: 16px;\n }\n}" - }, - "title": "RAM", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "5eabde4c-095f-defc-dd09-6c0afb9b9f7b" - }, - "451e2588-80b8-cd08-2199-1263ed4dc12c": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", - "filterId": null, - "dataKeys": [ - { - "name": "discUsage", - "type": "timeseries", - "label": "discUsage", - "color": "#2196f3", - "settings": {}, - "_hash": 0.659021918423117, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "totalDiscSpace", - "type": "timeseries", - "label": "totalDiscSpace", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5332753234169949, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value / 1073741824;" - }, - { - "name": "discUsage", - "type": "timeseries", - "label": "status", - "color": "#f44336", - "settings": {}, - "_hash": 0.025084892962804473, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "var status = 'status-ok';\nif (value > 85) {\n status = 'status-critical';\n} else if (value > 75) {\n status = 'status-warn';\n}\nreturn status;" - }, - { - "name": "discUsage", - "type": "timeseries", - "label": "statusTooltip", - "color": "#ffc107", - "settings": {}, - "_hash": 0.8542003006820891, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "var text = '';\nif (value > 85) {\n text = '{{ \\'widgets.system-info.disk-critical-text\\' | translate }}';\n} else if (value > 75) {\n text = '{{ \\'widgets.system-info.disk-warning-text\\' | translate }}';\n}\nreturn text;" - } - ] - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "useMarkdownTextFunction": true, - "markdownTextFunction": "const discUsage = data[0]['discUsage'];\nconst totalDiscSpace = data[0]['totalDiscSpace'];\nconst usedDisc = (totalDiscSpace * (discUsage / 100)).toFixed(0);\n\n\nreturn '
' +\n '
' +\n '
widgets.system-info.disk
' +\n '
' +\n '
' +\n '
' +\n '${discUsage:0}%' +\n '| ${totalDiscSpace:0} Gb' +\n '
' +\n '
';", - "applyDefaultMarkdownStyle": false, - "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: flex-start;\n}\n\n.tb-card-title {\n width: 100%;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-status {\n width: 24px;\n height: 24px;\n position: relative;\n}\n\n.tb-status.status-ok {\n background: #F3F6FA;\n border-radius: 4px;\n}\n\n.tb-status:after {\n position: absolute;\n top: 2px;\n left: 2px;\n font-family: 'Material Icons Round';\n font-size: 20px;\n line-height: 1;\n}\n\n.tb-status.status-ok:after {\n content: \"check\";\n color: #198038;\n font-weight: 600;\n}\n\n.tb-status.status-warn:after {\n content: \"warning\";\n color: #FAA405;\n}\n\n.tb-status.status-critical:after {\n content: \"warning\";\n color: #D12730;\n}\n\n.tb-value-container {\n user-select: none;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 26px;\n line-height: 36px;\n}\n\n.tb-total {\n padding-left: 4px;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n color: rgba(0, 0, 0, 0.54);\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-status {\n width: 18px;\n height: 18px;\n }\n .tb-status:after {\n font-size: 14px;\n }\n .tb-count {\n font-size: 16px;\n line-height: 24px;\n }\n .tb-total {\n font-size: 11px;\n line-height: 16px;\n }\n}" - }, - "title": "Disk", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "451e2588-80b8-cd08-2199-1263ed4dc12c" - }, - "01b94c17-84eb-4e49-6a92-c3f108ecff8e": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", - "filterId": null, - "dataKeys": [ - { - "name": "cpuUsage", - "type": "timeseries", - "label": "cpuUsage", - "color": "#2196f3", - "settings": {}, - "_hash": 0.659021918423117 - }, - { - "name": "cpuCount", - "type": "timeseries", - "label": "cpuCount", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5332753234169949 - }, - { - "name": "cpuUsage", - "type": "timeseries", - "label": "cpuStatus", - "color": "#f44336", - "settings": {}, - "_hash": 0.025084892962804473, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "var status = 'status-ok';\nif (value > 85) {\n status = 'status-critical';\n} else if (value > 75) {\n status = 'status-warn';\n}\nreturn status;" - }, - { - "name": "cpuUsage", - "type": "timeseries", - "label": "cpuStatusTooltip", - "color": "#ffc107", - "settings": {}, - "_hash": 0.8542003006820891, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "var text = '';\nif (value > 85) {\n text = '{{ \\'widgets.system-info.cpu-critical-text\\' | translate }}';\n} else if (value > 75) {\n text = '{{ \\'widgets.system-info.cpu-warning-text\\' | translate }}';\n}\nreturn text;\n" - }, - { - "name": "memoryUsage", - "type": "timeseries", - "label": "memoryUsage", - "color": "#607d8b", - "settings": {}, - "_hash": 0.9761283269385537 - }, - { - "name": "totalMemory", - "type": "timeseries", - "label": "totalMemory", - "color": "#9c27b0", - "settings": {}, - "_hash": 0.6363130990513808, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value / 1073741824;" - }, - { - "name": "memoryUsage", - "type": "timeseries", - "label": "memoryStatus", - "color": "#8bc34a", - "settings": {}, - "_hash": 0.15388812616419556, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "var status = 'status-ok';\nif (value > 85) {\n status = 'status-critical';\n} else if (value > 75) {\n status = 'status-warn';\n}\nreturn status;" - }, - { - "name": "memoryUsage", - "type": "timeseries", - "label": "memoryStatusTooltip", - "color": "#3f51b5", - "settings": {}, - "_hash": 0.8086245517967001, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "var text = '';\nif (value > 85) {\n text = '{{ \\'widgets.system-info.ram-critical-text\\' | translate }}';\n} else if (value > 75) {\n text = '{{ \\'widgets.system-info.ram-warning-text\\' | translate }}';\n}\nreturn text;" - }, - { - "name": "discUsage", - "type": "timeseries", - "label": "discUsage", - "color": "#e91e63", - "settings": {}, - "_hash": 0.4278686534236851 - }, - { - "name": "totalDiscSpace", - "type": "timeseries", - "label": "totalDiscSpace", - "color": "#ffeb3b", - "settings": {}, - "_hash": 0.543030429255416, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value / 1073741824;" - }, - { - "name": "discUsage", - "type": "timeseries", - "label": "discStatus", - "color": "#03a9f4", - "settings": {}, - "_hash": 0.29138586081227835, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "var status = 'status-ok';\nif (value > 85) {\n status = 'status-critical';\n} else if (value > 75) {\n status = 'status-warn';\n}\nreturn status;" - }, - { - "name": "discUsage", - "type": "timeseries", - "label": "discStatusTooltip", - "color": "#ff9800", - "settings": {}, - "_hash": 0.08995267596665912, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "var text = '';\nif (value > 85) {\n text = '{{ \\'widgets.system-info.disk-critical-text\\' | translate }}';\n} else if (value > 75) {\n text = '{{ \\'widgets.system-info.disk-warning-text\\' | translate }}';\n}\nreturn text;" - } - ] - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "useMarkdownTextFunction": true, - "markdownTextFunction": "const memoryUsage = data[0]['memoryUsage'];\nconst totalMemory = data[0]['totalMemory'];\nconst usedMemory = (totalMemory * (memoryUsage / 100)).toFixed(0);\n\nconst discUsage = data[0]['discUsage'];\nconst totalDiscSpace = data[0]['totalDiscSpace'];\nconst usedDisc = (totalDiscSpace * (discUsage / 100)).toFixed(0);\n\n\nreturn '
' +\n '
' +\n '
' +\n '
widgets.system-info.cpu
' +\n '
' +\n '
' +\n '
' +\n '${cpuUsage:0}%' +\n '| ${cpuCount:0} cores' +\n '
' +\n '
' +\n '
' +\n '
' +\n '
widgets.system-info.ram
' +\n '
' +\n '
' +\n '
' +\n '${memoryUsage:0}%' +\n '| ${totalMemory:0} Gb' +\n '
' +\n '
' +\n '
' +\n '
' +\n '
widgets.system-info.disk
' +\n '
' +\n '
' +\n '
' +\n '${discUsage:0}%' +\n '| ${totalDiscSpace:0} Gb' +\n '
' +\n '
' +\n '
';", - "applyDefaultMarkdownStyle": false, - "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n align-items: stretch;\n}\n\n.tb-card-section {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: flex-start;\n}\n\n.tb-card-section.left {\n padding-right: 12px;\n border-right: solid 1px rgba(0, 0, 0, 0.12);\n}\n\n.tb-card-section.center {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n.tb-card-section.right {\n padding-left: 12px;\n border-left: solid 1px rgba(0, 0, 0, 0.12);\n}\n\n.tb-card-title {\n width: 100%;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-status {\n width: 24px;\n height: 24px;\n position: relative;\n}\n\n.tb-status.status-ok {\n background: #F3F6FA;\n border-radius: 4px;\n}\n\n.tb-status:after {\n position: absolute;\n top: 2px;\n left: 2px;\n font-family: 'Material Icons Round';\n font-size: 20px;\n line-height: 1;\n}\n\n.tb-status.status-ok:after {\n content: \"check\";\n color: #198038;\n font-weight: 600;\n}\n\n.tb-status.status-warn:after {\n content: \"warning\";\n color: #FAA405;\n}\n\n.tb-status.status-critical:after {\n content: \"warning\";\n color: #D12730;\n}\n\n.tb-value-container {\n user-select: none;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 600;\n font-size: 20px;\n line-height: 24px;\n}\n\n.tb-total {\n padding-left: 4px;\n font-weight: 400;\n font-size: 12px;\n line-height: 16px;\n color: rgba(0, 0, 0, 0.54);\n}\n" - }, - "title": "System info mobile card", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "01b94c17-84eb-4e49-6a92-c3f108ecff8e" - }, - "f0479b00-ed47-0a30-0c36-bde7847aae00": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "rgba(0,0,0,0)", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "0px", - "settings": { - "useMarkdownTextFunction": false, - "markdownTextPattern": "
\n \n \n \n \n
", - "applyDefaultMarkdownStyle": false - }, - "title": "System info cards", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": { - "border": "none", - "boxShadow": "none", - "padding": "0" - }, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "f0479b00-ed47-0a30-0c36-bde7847aae00" - }, - "57ed0ad9-8fcf-4454-1fa7-d39e22f84570": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [ - { - "type": "entityCount", - "name": null, - "entityAliasId": "ae870700-071f-b3bc-406c-16ba554c5a55", - "filterId": null, - "dataKeys": [ - { - "name": "count", - "type": "count", - "label": "tenantsCount", - "color": "#2196f3", - "settings": {}, - "_hash": 0.8491768696709192, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "useMarkdownTextFunction": false, - "markdownTextPattern": "
\n \n
\n \n add\n \n
\n
", - "applyDefaultMarkdownStyle": false, - "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n}\n\n.tb-content-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n}\n\n.tb-small-button-container {\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.tb-card-header {\n height: 36px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 28px;\n line-height: 36px;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-count {\n font-size: 18px;\n line-height: 24px;\n }\n}\n" - }, - "title": "Tenants", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "57ed0ad9-8fcf-4454-1fa7-d39e22f84570" - }, - "0464b6bc-7863-ba75-4b84-64b8acae2af9": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [ - { - "type": "entityCount", - "name": null, - "entityAliasId": "ca4d90e4-f9ae-6fca-6b09-85815a48d52b", - "filterId": null, - "dataKeys": [ - { - "name": "count", - "type": "count", - "label": "tenantProfilesCount", - "color": "#2196f3", - "settings": {}, - "_hash": 0.8491768696709192, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "useMarkdownTextFunction": false, - "markdownTextPattern": "\n", - "applyDefaultMarkdownStyle": false, - "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n}\n\n.tb-content-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n}\n\n.tb-small-button-container {\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.tb-card-header {\n height: 36px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 28px;\n line-height: 36px;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-count {\n font-size: 18px;\n line-height: 24px;\n }\n}\n" - }, - "title": "Tenant Profiles", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "0464b6bc-7863-ba75-4b84-64b8acae2af9" - }, - "4c043bba-6ac1-dd23-0796-fe6ae4c3c0a3": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [ - { - "type": "entityCount", - "name": null, - "entityAliasId": "a1ddb8fa-90ff-5598-e7f2-e254194d055d", - "filterId": null, - "dataKeys": [ - { - "name": "count", - "type": "count", - "label": "devicesCount", - "color": "#2196f3", - "settings": {}, - "_hash": 0.8491768696709192, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "useMarkdownTextFunction": false, - "markdownTextPattern": "
\n
\n
{{ 'device.devices' | translate }}
\n
\n
${devicesCount:0}
\n
", - "applyDefaultMarkdownStyle": false, - "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 26px;\n line-height: 36px;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-count {\n font-size: 18px;\n line-height: 24px;\n }\n}" - }, - "title": "Devices", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "4c043bba-6ac1-dd23-0796-fe6ae4c3c0a3" - }, - "8b8d5d4f-2c5f-c768-d4fb-68d6e0b716f4": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [ - { - "type": "entityCount", - "name": null, - "entityAliasId": "619cdf00-a042-3b55-124e-194c1b28c236", - "filterId": null, - "dataKeys": [ - { - "name": "count", - "type": "count", - "label": "assetsCount", - "color": "#2196f3", - "settings": {}, - "_hash": 0.8491768696709192, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "useMarkdownTextFunction": false, - "markdownTextPattern": "
\n
\n
{{ 'asset.assets' | translate }}
\n
\n
${assetsCount:0}
\n
", - "applyDefaultMarkdownStyle": false, - "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 26px;\n line-height: 36px;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-count {\n font-size: 18px;\n line-height: 24px;\n }\n}" - }, - "title": "Assets", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "8b8d5d4f-2c5f-c768-d4fb-68d6e0b716f4" - }, - "d58bfdd4-18b0-a70c-5d5f-73d12c2ea468": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [ - { - "type": "entityCount", - "name": null, - "entityAliasId": "1d97ff7f-8b42-5882-f87b-16f3d0dee4f2", - "filterId": null, - "dataKeys": [ - { - "name": "count", - "type": "count", - "label": "usersCount", - "color": "#2196f3", - "settings": {}, - "_hash": 0.8491768696709192, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "useMarkdownTextFunction": false, - "markdownTextPattern": "
\n
\n
{{ 'user.users' | translate }}
\n
\n
${usersCount:0}
\n
", - "applyDefaultMarkdownStyle": false, - "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 26px;\n line-height: 36px;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-count {\n font-size: 18px;\n line-height: 24px;\n }\n}\n" - }, - "title": "Users", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "d58bfdd4-18b0-a70c-5d5f-73d12c2ea468" - }, - "a3d83738-a4f2-ea92-17d7-1a3c52aa1770": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [ - { - "type": "entityCount", - "name": null, - "entityAliasId": "0dd2b154-59f4-0f97-da1a-d85f5b5cfe31", - "filterId": null, - "dataKeys": [ - { - "name": "count", - "type": "count", - "label": "customersCount", - "color": "#2196f3", - "settings": {}, - "_hash": 0.8491768696709192, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "useMarkdownTextFunction": false, - "markdownTextPattern": "
\n
\n
{{ 'customer.customers' | translate }}
\n
\n
${customersCount:0}
\n
", - "applyDefaultMarkdownStyle": false, - "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 26px;\n line-height: 36px;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-count {\n font-size: 18px;\n line-height: 24px;\n }\n}" - }, - "title": "Customers", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "a3d83738-a4f2-ea92-17d7-1a3c52aa1770" - }, - "ff4f04a2-f739-2f3e-17dc-9ede9f2df75d": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [ - { - "type": "entityCount", - "name": null, - "entityAliasId": "ae870700-071f-b3bc-406c-16ba554c5a55", - "filterId": null, - "dataKeys": [ - { - "name": "count", - "type": "count", - "label": "tenantsCount", - "color": "#2196f3", - "settings": {}, - "_hash": 0.8491768696709192, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - }, - { - "type": "entityCount", - "name": null, - "entityAliasId": "ca4d90e4-f9ae-6fca-6b09-85815a48d52b", - "filterId": null, - "dataKeys": [ - { - "name": "count", - "type": "count", - "label": "tenantProfilesCount", - "color": "#4caf50", - "settings": {}, - "_hash": 0.04291827117688696, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - }, - { - "type": "entityCount", - "name": null, - "entityAliasId": "a1ddb8fa-90ff-5598-e7f2-e254194d055d", - "filterId": null, - "dataKeys": [ - { - "name": "count", - "type": "count", - "label": "devicesCount", - "color": "#f44336", - "settings": {}, - "_hash": 0.45105711028955886, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - }, - { - "type": "entityCount", - "name": null, - "entityAliasId": "619cdf00-a042-3b55-124e-194c1b28c236", - "filterId": null, - "dataKeys": [ - { - "name": "count", - "type": "count", - "label": "assetsCount", - "color": "#ffc107", - "settings": {}, - "_hash": 0.17028494648519876, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - }, - { - "type": "entityCount", - "name": null, - "entityAliasId": "1d97ff7f-8b42-5882-f87b-16f3d0dee4f2", - "filterId": null, - "dataKeys": [ - { - "name": "count", - "type": "count", - "label": "usersCount", - "color": "#607d8b", - "settings": {}, - "_hash": 0.0362458342595664, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - }, - { - "type": "entityCount", - "name": null, - "entityAliasId": "0dd2b154-59f4-0f97-da1a-d85f5b5cfe31", - "filterId": null, - "dataKeys": [ - { - "name": "count", - "type": "count", - "label": "customersCount", - "color": "#9c27b0", - "settings": {}, - "_hash": 0.7596585662918276, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "useMarkdownTextFunction": false, - "markdownTextPattern": "
\n \n
\n
\n
${devicesCount:0}
\n
{{ 'device.devices' | translate }}
\n
\n
\n
${assetsCount:0}
\n
{{ 'asset.assets' | translate }}
\n
\n
\n
${usersCount:0}
\n
{{ 'user.users' | translate }}
\n
\n
\n
${customersCount:0}
\n
{{ 'customer.customers' | translate }}
\n
\n
\n
\n", - "applyDefaultMarkdownStyle": false, - "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n align-items: stretch;\n}\n\n.tb-card-section {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: space-evenly;\n align-items: stretch;\n}\n\n.tb-card-section.left {\n border-right: solid 1px rgba(0, 0, 0, 0.12);\n}\n\n.tb-card-section.right .tb-entity-section {\n padding-left: 40px;\n}\n\n.tb-card-section.right .tb-count {\n min-width: 24px;\n text-align: right;\n}\n\n.tb-button-section {\n display: flex;\n padding-right: 40px;\n flex-direction: row;\n justify-content: flex-end;\n align-items: center;\n}\n\n.tb-entity-section {\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n align-items: center;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 600;\n font-size: 20px;\n line-height: 24px;\n margin-right: 12px;\n}\n\n.tb-entity-title {\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n color: rgba(0, 0, 0, 0.54);\n}\n\n" - }, - "title": "Entities info mobile card", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "ff4f04a2-f739-2f3e-17dc-9ede9f2df75d" - }, - "163025d8-3ca4-dd1e-c17b-e40d8e03e8a8": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "rgba(0,0,0,0)", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "0px", - "settings": { - "useMarkdownTextFunction": false, - "markdownTextPattern": "
\n \n \n \n \n
", - "applyDefaultMarkdownStyle": false - }, - "title": "Entities info cards", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": { - "border": "none", - "boxShadow": "none", - "padding": "0" - }, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "163025d8-3ca4-dd1e-c17b-e40d8e03e8a8" - } - }, - "states": { - "default": { - "name": "System Administrator Home Page", - "root": true, - "layouts": { - "main": { - "widgets": { - "d70cc256-4c7b-ee06-9905-b8c5e546605f": { - "sizeX": 41, - "sizeY": 16, - "row": 26, - "col": 44, - "mobileOrder": 7, - "mobileHeight": 8 - }, - "4b5e47ed-c197-a937-d727-041ba8decec2": { - "sizeX": 41, - "sizeY": 26, - "row": 0, - "col": 44, - "mobileOrder": 6, - "mobileHeight": 11 - }, - "8acbf5df-f9fc-114d-216f-86f081aa4779": { - "sizeX": 52, - "sizeY": 16, - "row": 42, - "col": 33, - "mobileHide": true - }, - "d2784f6c-0518-fd95-1d28-b21f70bdcb10": { - "sizeX": 44, - "sizeY": 13, - "row": 29, - "col": 0, - "mobileOrder": 8, - "mobileHeight": 8 - }, - "66dcf1a2-9d83-6873-c693-d6a5d989b3f8": { - "sizeX": 33, - "sizeY": 16, - "row": 42, - "col": 0, - "mobileOrder": 9, - "mobileHeight": 7 - }, - "867f82cf-ecf2-2d5c-35cb-08c6f2edc3a4": { - "sizeX": 44, - "sizeY": 14, - "row": 15, - "col": 0, - "mobileHide": true - }, - "a23185ad-dc46-806c-0e50-5b21fb080ace": { - "sizeX": 35, - "sizeY": 58, - "row": 0, - "col": 85, - "mobileHide": true - }, - "163025d8-3ca4-dd1e-c17b-e40d8e03e8a8": { - "sizeX": 44, - "sizeY": 15, - "mobileOrder": 0, - "mobileHeight": 7, - "row": 0, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "columns": 120, - "margin": 12, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 20, - "outerMargin": true - } - } - } - }, - "transport_messages": { - "name": "Transport messages", - "root": false, - "layouts": { - "main": { - "widgets": { - "8ee72d43-678c-4e25-e9a8-7d4cfd7a5f8e": { - "sizeX": 24, - "sizeY": 11, - "row": 0, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#ffffff", - "columns": 24, - "margin": 0, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": true, - "mobileRowHeight": 70, - "outerMargin": true - } - } - } - }, - "monolith_system_info": { - "name": "Monolith System Info", - "root": false, - "layouts": { - "main": { - "widgets": { - "eace9148-b02a-48fe-1a95-acd9928aa8c5": { - "sizeX": 48, - "sizeY": 20, - "row": 8, - "col": 0, - "mobileOrder": 1, - "mobileHeight": 10 - }, - "f0479b00-ed47-0a30-0c36-bde7847aae00": { - "sizeX": 48, - "sizeY": 8, - "mobileOrder": 0, - "mobileHeight": 4, - "row": 0, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "rgba(0,0,0,0)", - "columns": 48, - "margin": 12, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": true, - "mobileRowHeight": 70, - "outerMargin": false - } - } - } - }, - "system_info_desktop": { - "name": "System Info Desktop", - "root": false, - "layouts": { - "main": { - "widgets": { - "b35fa1b0-7f95-0b23-fade-d066653a40e1": { - "sizeX": 8, - "sizeY": 3, - "row": 0, - "col": 0 - }, - "5eabde4c-095f-defc-dd09-6c0afb9b9f7b": { - "sizeX": 8, - "sizeY": 3, - "row": 0, - "col": 8 - }, - "451e2588-80b8-cd08-2199-1263ed4dc12c": { - "sizeX": 8, - "sizeY": 3, - "row": 0, - "col": 16 - } - }, - "gridSettings": { - "backgroundColor": "rgba(0,0,0,0)", - "columns": 24, - "margin": 12, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": true, - "mobileRowHeight": 70, - "outerMargin": false - } - } - } - }, - "system_info_mobile": { - "name": "System Info Mobile", - "root": false, - "layouts": { - "main": { - "widgets": { - "01b94c17-84eb-4e49-6a92-c3f108ecff8e": { - "sizeX": 24, - "sizeY": 3, - "row": 0, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "rgba(0,0,0,0)", - "columns": 24, - "margin": 12, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": true, - "mobileRowHeight": 70, - "outerMargin": false - } - } - } - }, - "entities_info_desktop": { - "name": "Entities Info Desktop", - "root": false, - "layouts": { - "main": { - "widgets": { - "57ed0ad9-8fcf-4454-1fa7-d39e22f84570": { - "sizeX": 60, - "sizeY": 29, - "row": 0, - "col": 0 - }, - "0464b6bc-7863-ba75-4b84-64b8acae2af9": { - "sizeX": 60, - "sizeY": 29, - "row": 0, - "col": 60 - }, - "4c043bba-6ac1-dd23-0796-fe6ae4c3c0a3": { - "sizeX": 30, - "sizeY": 26, - "row": 29, - "col": 0 - }, - "8b8d5d4f-2c5f-c768-d4fb-68d6e0b716f4": { - "sizeX": 30, - "sizeY": 26, - "row": 29, - "col": 30 - }, - "d58bfdd4-18b0-a70c-5d5f-73d12c2ea468": { - "sizeX": 30, - "sizeY": 26, - "row": 29, - "col": 60 - }, - "a3d83738-a4f2-ea92-17d7-1a3c52aa1770": { - "sizeX": 30, - "sizeY": 26, - "row": 29, - "col": 90 - } - }, - "gridSettings": { - "backgroundColor": "rgba(0,0,0,0)", - "columns": 120, - "margin": 12, - "outerMargin": false, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70 - } - } - } - }, - "entities_info_mobile": { - "name": "Entities Info Mobile", - "root": false, - "layouts": { - "main": { - "widgets": { - "ff4f04a2-f739-2f3e-17dc-9ede9f2df75d": { - "sizeX": 24, - "sizeY": 11, - "row": 0, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "rgba(0,0,0,0)", - "columns": 24, - "margin": 12, - "outerMargin": false, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": true, - "mobileRowHeight": 70 - } - } - } - } - }, - "entityAliases": { - "ae870700-071f-b3bc-406c-16ba554c5a55": { - "id": "ae870700-071f-b3bc-406c-16ba554c5a55", - "alias": "Tenants", - "filter": { - "type": "entityType", - "resolveMultiple": true, - "entityType": "TENANT" - } - }, - "ca4d90e4-f9ae-6fca-6b09-85815a48d52b": { - "id": "ca4d90e4-f9ae-6fca-6b09-85815a48d52b", - "alias": "TenantProfiles", - "filter": { - "type": "entityType", - "resolveMultiple": true, - "entityType": "TENANT_PROFILE" - } - }, - "a1ddb8fa-90ff-5598-e7f2-e254194d055d": { - "id": "a1ddb8fa-90ff-5598-e7f2-e254194d055d", - "alias": "Devices", - "filter": { - "type": "entityType", - "resolveMultiple": true, - "entityType": "DEVICE" - } - }, - "619cdf00-a042-3b55-124e-194c1b28c236": { - "id": "619cdf00-a042-3b55-124e-194c1b28c236", - "alias": "Assets", - "filter": { - "type": "entityType", - "resolveMultiple": true, - "entityType": "ASSET" - } - }, - "1d97ff7f-8b42-5882-f87b-16f3d0dee4f2": { - "id": "1d97ff7f-8b42-5882-f87b-16f3d0dee4f2", - "alias": "Users", - "filter": { - "type": "entityType", - "resolveMultiple": true, - "entityType": "USER" - } - }, - "0dd2b154-59f4-0f97-da1a-d85f5b5cfe31": { - "id": "0dd2b154-59f4-0f97-da1a-d85f5b5cfe31", - "alias": "Customers", - "filter": { - "type": "entityType", - "resolveMultiple": true, - "entityType": "CUSTOMER" - } - }, - "d9229b29-3f46-de8d-7fe8-eb0c43c75079": { - "id": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", - "alias": "Api Usage State", - "filter": { - "type": "apiUsageState", - "resolveMultiple": true - } - } - }, - "filters": {}, - "timewindow": { - "displayValue": "", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "hideTimezone": false, - "selectedTab": 0, - "realtime": { - "realtimeType": 0, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168326072, - "endTimeMs": 1680254726072 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "settings": { - "stateControllerId": "entity", - "showTitle": false, - "showDashboardsSelect": true, - "showEntitiesSelect": true, - "showDashboardTimewindow": true, - "showDashboardExport": true, - "toolbarAlwaysOpen": true, - "titleColor": "rgba(0,0,0,0.870588)", - "showDashboardLogo": false, - "dashboardLogoUrl": null, - "hideToolbar": true, - "showFilters": true, - "showUpdateDashboardImage": true, - "dashboardCss": ".tb-widget-container > .tb-widget {\n border: 1px solid rgba(0, 0, 0, 0.05);\n box-shadow: 0px 5px 16px rgba(0, 0, 0, 0.04);\n border-radius: 12px;\n}\n\n.tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 16px !important;\n}\n\n.tb-card-title {\n display: grid;\n}\n\n.tb-home-widget-title {\n font-style: normal;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n color: rgba(0, 0, 0, 0.54);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.tb-home-widget-link {\n position: relative;\n border-bottom: none;\n}\n\n.tb-home-widget-link:hover {\n border-bottom: none;\n}\n\n.tb-home-widget-link:focus {\n border-bottom: none;\n}\n\n.tb-home-widget-link::after {\n content: 'arrow_forward';\n display: inline-block;\n transform: rotate(315deg);\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n font-size: 18px;\n color: rgba(0, 0, 0, 0.12);\n vertical-align: bottom;\n margin-left: 6px; \n}\n\n.tb-home-widget-link:hover::after {\n color: inherit;\n}\n\n.tb-home-widget-info-icon {\n color: rgba(0, 0, 0, 0.12);\n font-size: 16px;\n width: 16px;\n height: 16px;\n line-height: 15px;\n vertical-align: middle;\n}\n\n.tb-widget-container > .tb-widget .tb-timewindow {\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n color: rgba(0, 0, 0, 0.54);\n padding: 0;\n}\n\n.tb-widget-container > .tb-widget .tb-legend-keys .tb-legend-label {\n cursor: pointer;\n user-select: none;\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n color: rgba(0, 0, 0, 0.54);\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .tb-widget-container > .tb-widget {\n border-radius: 4px;\n }\n .tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 2px !important;\n }\n .tb-hide-md {\n display: none;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1819px) {\n .tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 8px !important;\n }\n .tb-hide-lg {\n display: none;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-hide-md-lg {\n display: none;\n }\n\n .tb-home-widget-title {\n font-size: 12px;\n line-height: 16px;\n }\n \n .tb-widget-container > .tb-widget .tb-widget-title {\n padding: 0;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow {\n font-size: 12px;\n line-height: 16px;\n min-height: 24px;\n padding: 0;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow .mat-mdc-icon-button.tb-mat-32 {\n width: 24px;\n height: 24px;\n line-height: 24px;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow .mat-mdc-icon-button.tb-mat-32 .mat-icon {\n width: 18px;\n height: 18px;\n font-size: 18px;\n }\n \n .tb-widget-container > .tb-widget tb-legend {\n padding-bottom: 0 !important;\n }\n \n .tb-widget-container > .tb-widget .tb-legend-keys .tb-legend-label {\n font-size: 11px;\n line-height: 16px;\n letter-spacing: 0.25px;\n }\n}\n\n@media screen and (max-width: 959px), screen and (min-width: 1820px) {\n .tb-hide-not-md-lg {\n display: none;\n }\n}\n" - } - }, - "externalId": null, - "name": "System Administrator Home Page" -} diff --git a/ui-ngx/src/app/modules/home/pages/home-links/tenant_admin_home_page.raw b/ui-ngx/src/app/modules/home/pages/home-links/tenant_admin_home_page.raw deleted file mode 100644 index 9205fddf71..0000000000 --- a/ui-ngx/src/app/modules/home/pages/home-links/tenant_admin_home_page.raw +++ /dev/null @@ -1,1104 +0,0 @@ -{ - "title": "Tenant Administrator Home Page", - "image": null, - "mobileHide": false, - "mobileOrder": null, - "configuration": { - "description": "", - "widgets": { - "d70cc256-4c7b-ee06-9905-b8c5e546605f": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "useMarkdownTextFunction": false, - "markdownTextPattern": "
\n
\n
{{ 'widgets.activity.title' | translate }}
\n \n \n
\n \n \n \n \n \n \n \n \n
", - "applyDefaultMarkdownStyle": false, - "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n}\n" - }, - "title": "Transport messages", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "d70cc256-4c7b-ee06-9905-b8c5e546605f" - }, - "8ee72d43-678c-4e25-e9a8-7d4cfd7a5f8e": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "timeseries_bars_flot", - "type": "timeseries", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", - "filterId": null, - "dataKeys": [ - { - "name": "transportMsgCountHourly", - "type": "timeseries", - "label": "{i18n:widgets.transport-messages.title}", - "color": "#305680", - "settings": {}, - "_hash": 0.2880464219129071, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ], - "latestDataKeys": [] - } - ], - "timewindow": { - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false, - "hideAggregation": true, - "hideAggInterval": false, - "hideTimezone": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000, - "fixedTimewindow": { - "startTimeMs": 1680443065451, - "endTimeMs": 1680529465451 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "SUM", - "limit": 50000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "0px", - "settings": { - "stack": true, - "fontSize": 10, - "fontColor": "#545454", - "showTooltip": true, - "tooltipIndividual": false, - "tooltipCumulative": false, - "hideZeros": false, - "grid": { - "verticalLines": false, - "horizontalLines": false, - "outlineWidth": 0, - "color": "#545454", - "backgroundColor": null, - "tickColor": "#DDDDDD" - }, - "xaxis": { - "title": null, - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "min": 0, - "max": null, - "title": null, - "showLabels": true, - "color": "#545454", - "tickSize": null, - "tickDecimals": 0, - "ticksFormatter": "return value % 1000 === 0 ? ((value / 1000) + 'k') : '';" - }, - "defaultBarWidth": 1800000, - "barAlignment": "left", - "comparisonEnabled": false, - "timeForComparison": "previousInterval", - "comparisonCustomIntervalValue": 7200000, - "xaxisSecond": { - "axisPosition": "top", - "title": null, - "showLabels": true - }, - "customLegendEnabled": false, - "dataKeysListForLabels": [] - }, - "title": "Transport messages", - "dropShadow": false, - "enableFullscreen": false, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": { - "padding": "0" - }, - "useDashboardTimewindow": false, - "showLegend": false, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "titleTooltip": "", - "widgetCss": ".tb-widget-container > .tb-widget {\n border: none !important;\n border-radius: 0 !important;\n box-shadow: none !important;\n}\n\n.tb-widget-container > .tb-widget .flot-base {\n opacity: 0.48;\n}\n", - "pageSize": 1024, - "noDataDisplayMessage": "", - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": true, - "showTotal": false, - "showLatest": false - } - }, - "row": 0, - "col": 0, - "id": "8ee72d43-678c-4e25-e9a8-7d4cfd7a5f8e" - }, - "867f82cf-ecf2-2d5c-35cb-08c6f2edc3a4": { - "isSystemType": true, - "bundleAlias": "home_page_widgets", - "typeAlias": "documentation_links", - "type": "static", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 7.5, - "sizeY": 3, - "config": { - "datasources": [], - "timewindow": { - "realtime": { - "timewindowMs": 60000 - } - }, - "showTitle": false, - "backgroundColor": "rgb(255, 255, 255)", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "columns": 2 - }, - "title": "Documentation", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "", - "showLegend": false - }, - "row": 0, - "col": 0, - "id": "867f82cf-ecf2-2d5c-35cb-08c6f2edc3a4" - }, - "a23185ad-dc46-806c-0e50-5b21fb080ace": { - "isSystemType": true, - "bundleAlias": "home_page_widgets", - "typeAlias": "getting_started", - "type": "static", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 7.5, - "sizeY": 6.5, - "config": { - "datasources": [], - "timewindow": { - "realtime": { - "timewindowMs": 60000 - } - }, - "showTitle": false, - "backgroundColor": "rgb(255, 255, 255)", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "columns": 3 - }, - "title": "Getting started", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "", - "showLegend": false - }, - "row": 0, - "col": 0, - "id": "a23185ad-dc46-806c-0e50-5b21fb080ace" - }, - "d26e5cd7-75ef-d475-00c7-1a2d1114efe8": { - "isSystemType": true, - "bundleAlias": "charts", - "typeAlias": "basic_timeseries", - "type": "timeseries", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", - "filterId": null, - "dataKeys": [ - { - "name": "activeDevicesCountHourly", - "type": "timeseries", - "label": "{i18n:device.devices}", - "color": "#305680", - "settings": { - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "excludeFromStacking": false, - "showLines": true, - "lineWidth": 3, - "fillLines": true, - "fillLinesOpacity": 0.04, - "showPoints": false, - "showSeparateAxis": false, - "axisPosition": "left", - "comparisonSettings": { - "showValuesForComparison": true, - "comparisonValuesLabel": "", - "color": "" - }, - "thresholds": [] - }, - "_hash": 0.9688095820365725, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ], - "latestDataKeys": [] - } - ], - "timewindow": { - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false, - "hideAggregation": true, - "hideAggInterval": true, - "hideTimezone": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 7200000, - "fixedTimewindow": { - "startTimeMs": 1681400576338, - "endTimeMs": 1681486976338 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "NONE", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "0px", - "settings": { - "stack": false, - "fontSize": 10, - "fontColor": "#545454", - "showTooltip": true, - "tooltipIndividual": false, - "tooltipCumulative": false, - "hideZeros": false, - "grid": { - "verticalLines": false, - "horizontalLines": false, - "outlineWidth": 0, - "color": "#545454", - "backgroundColor": null, - "tickColor": "#DDDDDD" - }, - "xaxis": { - "title": null, - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "min": null, - "max": null, - "title": null, - "showLabels": true, - "color": "#545454", - "tickSize": null, - "tickDecimals": 0, - "ticksFormatter": "" - }, - "shadowSize": 0, - "smoothLines": true, - "comparisonEnabled": false, - "timeForComparison": "previousInterval", - "comparisonCustomIntervalValue": 7200000, - "xaxisSecond": { - "axisPosition": "top", - "title": null, - "showLabels": true - }, - "customLegendEnabled": false, - "dataKeysListForLabels": [] - }, - "title": "Devices activity", - "dropShadow": false, - "enableFullscreen": false, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "useDashboardTimewindow": false, - "displayTimewindow": true, - "showTitleIcon": false, - "titleTooltip": "", - "widgetStyle": { - "padding": "0" - }, - "widgetCss": ".tb-widget-container > .tb-widget {\n border: none !important;\n border-radius: 0 !important;\n box-shadow: none !important;\n}\n", - "pageSize": 1024, - "noDataDisplayMessage": "", - "showLegend": false, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": true, - "showTotal": false, - "showLatest": false - } - }, - "row": 0, - "col": 0, - "id": "d26e5cd7-75ef-d475-00c7-1a2d1114efe8" - }, - "ebbd0a6e-8a47-e770-5086-7f4974250f2d": { - "isSystemType": true, - "bundleAlias": "home_page_widgets", - "typeAlias": "usage_info", - "type": "static", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 7.5, - "sizeY": 6.5, - "config": { - "showTitle": false, - "backgroundColor": "rgb(255, 255, 255)", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": {}, - "title": "New Usage info", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "", - "showLegend": false, - "datasources": [] - }, - "row": 0, - "col": 0, - "id": "ebbd0a6e-8a47-e770-5086-7f4974250f2d" - }, - "9e3ef045-d8bc-1640-a3f4-2dd10b19d50e": { - "isSystemType": true, - "bundleAlias": "home_page_widgets", - "typeAlias": "quick_links", - "type": "static", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 7.5, - "sizeY": 3, - "config": { - "showTitle": false, - "backgroundColor": "rgb(255, 255, 255)", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "columns": 2 - }, - "title": "Quick links", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "", - "showLegend": false, - "datasources": [] - }, - "row": 0, - "col": 0, - "id": "9e3ef045-d8bc-1640-a3f4-2dd10b19d50e" - }, - "6d6e2b1d-6ce7-4678-3745-c6b0897b2674": { - "isSystemType": true, - "bundleAlias": "home_page_widgets", - "typeAlias": "dashboards", - "type": "static", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 7.5, - "sizeY": 6.5, - "config": { - "showTitle": false, - "backgroundColor": "rgb(255, 255, 255)", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": {}, - "title": "Dashboards", - "dropShadow": true, - "enableFullscreen": false, - "widgetStyle": {}, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "", - "showLegend": false, - "datasources": [] - }, - "row": 0, - "col": 0, - "id": "6d6e2b1d-6ce7-4678-3745-c6b0897b2674" - }, - "35d3ac3b-f1af-dc3d-a1d1-11f351d16fc6": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [ - { - "type": "entityCount", - "name": null, - "entityAliasId": "a1ddb8fa-90ff-5598-e7f2-e254194d055d", - "filterId": null, - "dataKeys": [ - { - "name": "count", - "type": "count", - "label": "totalDevices", - "color": "#2196f3", - "settings": {}, - "_hash": 0.8491768696709192, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - }, - { - "type": "entityCount", - "name": null, - "entityAliasId": "a1ddb8fa-90ff-5598-e7f2-e254194d055d", - "filterId": "42f42812-92fe-a13a-b0ba-c6e0dfab3584", - "dataKeys": [ - { - "name": "count", - "type": "count", - "label": "activeDevices", - "color": "#4caf50", - "settings": {}, - "_hash": 0.1262449138010293, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - }, - { - "type": "entityCount", - "name": null, - "entityAliasId": "a1ddb8fa-90ff-5598-e7f2-e254194d055d", - "filterId": "39d92c21-2426-7346-4484-452829146c33", - "dataKeys": [ - { - "name": "count", - "type": "count", - "label": "inactiveDevices", - "color": "#f44336", - "settings": {}, - "_hash": 0.39119172615806797, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "useMarkdownTextFunction": false, - "markdownTextPattern": "", - "applyDefaultMarkdownStyle": false, - "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n}\n\n.tb-content-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n gap: 12px;\n}\n\n.tb-card-header {\n height: 36px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-item-cards {\n flex: 1;\n display: flex;\n flex-direction: row;\n gap: 12px;\n overflow: hidden;\n}\n\na.tb-item-card {\n flex: 1;\n display: flex;\n flex-direction: column;\n padding: 8px 12px;\n border: 1px solid;\n border-radius: 10px;\n margin-bottom: 12px;\n overflow: hidden;\n justify-content: space-evenly;\n}\n\na.tb-item-card.tb-inactive {\n background: rgba(209, 39, 48, 0.04);\n border-color: rgba(209, 39, 48, 0.06);\n}\n\na.tb-item-card.tb-active {\n background: rgba(48, 86, 128, 0.04);\n border-color: rgba(48, 86, 128, 0.12);\n}\n\na.tb-item-card.tb-total {\n background: rgba(0, 0, 0, 0.01);\n border-color: rgba(0, 0, 0, 0.05);\n}\n\n.tb-item-title-container {\n display: grid;\n}\n\n.tb-item-title {\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis; \n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-item-title.tb-home-widget-link:after {\n position: absolute;\n right: 0;\n}\n\na.tb-item-card:hover .tb-item-title.tb-home-widget-link:after { \n color: rgba(0, 0, 0, 0.38);\n}\n\na.tb-item-card:hover {\n box-shadow: 0px 4px 10px rgba(23, 33, 90, 0.08);\n}\n\n.tb-count-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 24px;\n line-height: 36px;\n white-space: nowrap;\n color: rgba(0, 0, 0, 0.87);\n}\n\n@media screen and (max-width: 959px) {\n .tb-item-cards {\n flex-direction: column;\n }\n a.tb-item-card {\n margin-bottom: 0;\n }\n}\n\n@media screen and (max-width: 1279px) {\n a.tb-item-card {\n flex-direction: row;\n align-items: center;\n }\n .tb-item-title.tb-home-widget-link:after {\n position: relative;\n }\n .tb-count-container {\n align-items: flex-end;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-item-title {\n font-size: 11px;\n line-height: 16px;\n }\n .tb-count {\n font-size: 16px;\n line-height: 24px;\n }\n a.tb-item-card {\n padding: 4px 8px;\n margin-bottom: 6px;\n }\n a.tb-item-card:hover {\n box-shadow: 0px 2px 5px rgba(23, 33, 90, 0.08);\n }\n}\n" - }, - "title": "Devices", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "35d3ac3b-f1af-dc3d-a1d1-11f351d16fc6" - }, - "7ac20b6a-dc40-b18e-9f5f-bca20bc693bb": { - "isSystemType": true, - "bundleAlias": "cards", - "typeAlias": "markdown_card", - "type": "latest", - "title": "New widget", - "image": null, - "description": null, - "sizeX": 5, - "sizeY": 3.5, - "config": { - "datasources": [ - { - "type": "alarmCount", - "name": null, - "entityAliasId": null, - "filterId": null, - "dataKeys": [ - { - "name": "count", - "type": "count", - "label": "totalAlarms", - "color": "#ffc107", - "settings": {}, - "_hash": 0.8743321483507485, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ], - "alarmFilterConfig": { - "statusList": [ - "ACTIVE" - ] - } - }, - { - "type": "alarmCount", - "name": null, - "entityAliasId": null, - "filterId": null, - "dataKeys": [ - { - "name": "count", - "type": "count", - "label": "assignedToMeAlarms", - "color": "#ffc107", - "settings": {}, - "_hash": 0.6392390736755882, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ], - "alarmFilterConfig": { - "statusList": [ - "ACTIVE" - ], - "assignedToCurrentUser": true - } - }, - { - "type": "alarmCount", - "name": null, - "entityAliasId": null, - "filterId": null, - "dataKeys": [ - { - "name": "count", - "type": "count", - "label": "criticalAlarms", - "color": "#ffc107", - "settings": {}, - "_hash": 0.8329478098552843, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ], - "alarmFilterConfig": { - "statusList": [ - "ACTIVE" - ], - "severityList": [ - "CRITICAL" - ] - } - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168340431, - "endTimeMs": 1680254740431 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "16px", - "settings": { - "useMarkdownTextFunction": false, - "markdownTextPattern": "", - "applyDefaultMarkdownStyle": false, - "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n}\n\n.tb-content-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n gap: 12px;\n}\n\n.tb-card-header {\n height: 36px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-item-cards {\n flex: 1;\n display: flex;\n flex-direction: row;\n gap: 12px;\n overflow: hidden;\n}\n\na.tb-item-card {\n flex: 1;\n display: flex;\n flex-direction: column;\n padding: 8px 12px;\n border: 1px solid;\n border-radius: 10px;\n margin-bottom: 12px;\n overflow: hidden;\n justify-content: space-evenly;\n}\n\na.tb-item-card.tb-critical {\n background: rgba(209, 39, 48, 0.04);\n border-color: rgba(209, 39, 48, 0.06);\n}\n\na.tb-item-card.tb-assigned {\n background: rgba(48, 86, 128, 0.04);\n border-color: rgba(48, 86, 128, 0.12);\n}\n\na.tb-item-card.tb-total {\n background: rgba(0, 0, 0, 0.01);\n border-color: rgba(0, 0, 0, 0.05);\n}\n\n.tb-item-title-container {\n display: grid;\n}\n\n.tb-item-title {\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis; \n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-item-title.tb-home-widget-link:after {\n position: absolute;\n right: 0;\n}\n\na.tb-item-card:hover .tb-item-title.tb-home-widget-link:after { \n color: rgba(0, 0, 0, 0.38);\n}\n\na.tb-item-card:hover {\n box-shadow: 0px 4px 10px rgba(23, 33, 90, 0.08);\n}\n\n.tb-count-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 24px;\n line-height: 36px;\n white-space: nowrap;\n color: rgba(0, 0, 0, 0.87);\n}\n\na.tb-item-card.tb-critical .tb-count:after {\n content: \"warning\";\n display: inline-block;\n position: relative;\n font-family: 'Material Icons Round';\n color: #D12730;\n vertical-align: bottom;\n margin-left: 6px;\n}\n\n@media screen and (max-width: 959px) {\n .tb-item-cards {\n flex-direction: column;\n }\n a.tb-item-card {\n margin-bottom: 0;\n }\n}\n\n@media screen and (max-width: 1279px) {\n a.tb-item-card {\n flex-direction: row;\n align-items: center;\n }\n .tb-item-title.tb-home-widget-link:after {\n position: relative;\n }\n .tb-count-container {\n align-items: flex-end;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-item-title {\n font-size: 11px;\n line-height: 16px;\n }\n .tb-count {\n font-size: 16px;\n line-height: 24px;\n }\n a.tb-item-card {\n padding: 4px 8px;\n margin-bottom: 6px;\n }\n a.tb-item-card:hover {\n box-shadow: 0px 2px 5px rgba(23, 33, 90, 0.08);\n }\n a.tb-item-card.tb-critical .tb-count:after {\n margin-left: 2px;\n }\n}\n" - }, - "title": "Alarms", - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "dropShadow": false, - "enableFullscreen": false, - "widgetStyle": {}, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "showLegend": false, - "useDashboardTimewindow": true, - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "7ac20b6a-dc40-b18e-9f5f-bca20bc693bb" - } - }, - "states": { - "default": { - "name": "Tenant Administrator Home Page", - "root": true, - "layouts": { - "main": { - "widgets": { - "d70cc256-4c7b-ee06-9905-b8c5e546605f": { - "sizeX": 42, - "sizeY": 29, - "row": 13, - "col": 43, - "mobileOrder": 4, - "mobileHeight": 10 - }, - "867f82cf-ecf2-2d5c-35cb-08c6f2edc3a4": { - "sizeX": 29, - "sizeY": 16, - "row": 42, - "col": 28, - "mobileHide": true - }, - "a23185ad-dc46-806c-0e50-5b21fb080ace": { - "sizeX": 35, - "sizeY": 58, - "row": 0, - "col": 85, - "mobileHide": true - }, - "ebbd0a6e-8a47-e770-5086-7f4974250f2d": { - "sizeX": 28, - "sizeY": 16, - "row": 42, - "col": 57, - "mobileOrder": 3, - "mobileHeight": 10 - }, - "9e3ef045-d8bc-1640-a3f4-2dd10b19d50e": { - "sizeX": 28, - "sizeY": 16, - "row": 42, - "col": 0, - "mobileHide": true - }, - "6d6e2b1d-6ce7-4678-3745-c6b0897b2674": { - "sizeX": 43, - "sizeY": 29, - "row": 13, - "col": 0, - "mobileOrder": 2, - "mobileHeight": 10 - }, - "35d3ac3b-f1af-dc3d-a1d1-11f351d16fc6": { - "sizeX": 43, - "sizeY": 13, - "row": 0, - "col": 0, - "mobileHeight": 9, - "mobileOrder": 1 - }, - "7ac20b6a-dc40-b18e-9f5f-bca20bc693bb": { - "sizeX": 42, - "sizeY": 13, - "mobileHeight": 9, - "row": 0, - "col": 43, - "mobileOrder": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "columns": 120, - "margin": 12, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 20, - "outerMargin": true - } - } - } - }, - "transport_messages": { - "name": "Transport messages", - "root": false, - "layouts": { - "main": { - "widgets": { - "8ee72d43-678c-4e25-e9a8-7d4cfd7a5f8e": { - "sizeX": 24, - "sizeY": 11, - "row": 0, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#ffffff", - "columns": 24, - "margin": 0, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": true, - "mobileRowHeight": 70, - "outerMargin": true - } - } - } - }, - "devices_activity": { - "name": "Devices activity", - "root": false, - "layouts": { - "main": { - "widgets": { - "d26e5cd7-75ef-d475-00c7-1a2d1114efe8": { - "sizeX": 24, - "sizeY": 11, - "row": 0, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#ffffff", - "columns": 24, - "margin": 0, - "outerMargin": true, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": true, - "mobileRowHeight": 70 - } - } - } - } - }, - "entityAliases": { - "a1ddb8fa-90ff-5598-e7f2-e254194d055d": { - "id": "a1ddb8fa-90ff-5598-e7f2-e254194d055d", - "alias": "Devices", - "filter": { - "type": "entityType", - "resolveMultiple": true, - "entityType": "DEVICE" - } - }, - "d9229b29-3f46-de8d-7fe8-eb0c43c75079": { - "id": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", - "alias": "Api Usage State", - "filter": { - "type": "apiUsageState", - "resolveMultiple": true - } - } - }, - "filters": { - "42f42812-92fe-a13a-b0ba-c6e0dfab3584": { - "id": "42f42812-92fe-a13a-b0ba-c6e0dfab3584", - "filter": "Active Devices", - "keyFilters": [ - { - "key": { - "type": "ATTRIBUTE", - "key": "active" - }, - "valueType": "BOOLEAN", - "predicates": [ - { - "keyFilterPredicate": { - "operation": "EQUAL", - "value": { - "defaultValue": true, - "dynamicValue": null - }, - "type": "BOOLEAN" - }, - "userInfo": { - "editable": true, - "label": "", - "autogeneratedLabel": true, - "order": 0 - } - } - ] - } - ], - "editable": false - }, - "39d92c21-2426-7346-4484-452829146c33": { - "id": "39d92c21-2426-7346-4484-452829146c33", - "filter": "Inactive Devices", - "keyFilters": [ - { - "key": { - "type": "ATTRIBUTE", - "key": "active" - }, - "valueType": "BOOLEAN", - "predicates": [ - { - "keyFilterPredicate": { - "operation": "EQUAL", - "value": { - "defaultValue": false, - "dynamicValue": null - }, - "type": "BOOLEAN" - }, - "userInfo": { - "editable": true, - "label": "", - "autogeneratedLabel": true, - "order": 0 - } - } - ] - } - ], - "editable": false - } - }, - "timewindow": { - "displayValue": "", - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "hideTimezone": false, - "selectedTab": 0, - "realtime": { - "realtimeType": 0, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1680168326072, - "endTimeMs": 1680254726072 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "settings": { - "stateControllerId": "entity", - "showTitle": false, - "showDashboardsSelect": true, - "showEntitiesSelect": true, - "showDashboardTimewindow": true, - "showDashboardExport": true, - "toolbarAlwaysOpen": true, - "titleColor": "rgba(0,0,0,0.870588)", - "showDashboardLogo": false, - "dashboardLogoUrl": null, - "hideToolbar": true, - "showFilters": true, - "showUpdateDashboardImage": true, - "dashboardCss": ".tb-widget-container > .tb-widget {\n border: 1px solid rgba(0, 0, 0, 0.05);\n box-shadow: 0px 5px 16px rgba(0, 0, 0, 0.04);\n border-radius: 12px;\n}\n\n.tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 16px !important;\n}\n\n.tb-card-title {\n display: grid;\n}\n\n.tb-home-widget-title {\n font-style: normal;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n color: rgba(0, 0, 0, 0.54);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.tb-home-widget-link {\n position: relative;\n border-bottom: none;\n}\n\n.tb-home-widget-link:hover {\n border-bottom: none;\n}\n\n.tb-home-widget-link:focus {\n border-bottom: none;\n}\n\n.tb-home-widget-link::after {\n content: 'arrow_forward';\n display: inline-block;\n transform: rotate(315deg);\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n font-size: 18px;\n color: rgba(0, 0, 0, 0.12);\n vertical-align: bottom;\n margin-left: 6px; \n}\n\n.tb-home-widget-link:hover::after {\n color: inherit;\n}\n\n.tb-home-widget-info-icon {\n color: rgba(0, 0, 0, 0.12);\n font-size: 16px;\n width: 16px;\n height: 16px;\n line-height: 15px;\n vertical-align: middle;\n}\n\n.tb-widget-container > .tb-widget .tb-timewindow {\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n color: rgba(0, 0, 0, 0.54);\n padding: 0;\n}\n\n.tb-widget-container > .tb-widget .tb-legend-keys .tb-legend-label {\n cursor: pointer;\n user-select: none;\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n color: rgba(0, 0, 0, 0.54);\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .tb-widget-container > .tb-widget {\n border-radius: 4px;\n }\n .tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 2px !important;\n }\n .tb-hide-md {\n display: none;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1819px) {\n .tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 8px !important;\n }\n .tb-hide-lg {\n display: none;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-hide-md-lg {\n display: none;\n }\n\n .tb-home-widget-title {\n font-size: 12px;\n line-height: 16px;\n }\n \n .tb-widget-container > .tb-widget .tb-widget-title {\n padding: 0;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow {\n font-size: 12px;\n line-height: 16px;\n min-height: 24px;\n padding: 0;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow .mat-mdc-icon-button.tb-mat-32 {\n width: 24px;\n height: 24px;\n line-height: 24px;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow .mat-mdc-icon-button.tb-mat-32 .mat-icon {\n width: 18px;\n height: 18px;\n font-size: 18px;\n }\n \n .tb-widget-container > .tb-widget tb-legend {\n padding-bottom: 0 !important;\n }\n \n .tb-widget-container > .tb-widget .tb-legend-keys .tb-legend-label {\n font-size: 11px;\n line-height: 16px;\n letter-spacing: 0.25px;\n }\n}\n\n@media screen and (max-width: 959px), screen and (min-width: 1820px) {\n .tb-hide-not-md-lg {\n display: none;\n }\n}\n" - } - }, - "externalId": null, - "name": "Tenant Administrator Home Page" -} \ No newline at end of file diff --git a/ui-ngx/src/app/modules/home/pages/home-pages.models.ts b/ui-ngx/src/app/modules/home/pages/home-pages.models.ts index cd6190bb00..3979730f0c 100644 --- a/ui-ngx/src/app/modules/home/pages/home-pages.models.ts +++ b/ui-ngx/src/app/modules/home/pages/home-pages.models.ts @@ -16,9 +16,18 @@ import { BreadCrumbLabelFunction } from '@shared/components/breadcrumb'; import { EntityDetailsPageComponent } from '@home/components/entity/entity-details-page.component'; +import { EntityType } from '@shared/models/entity-type.models'; +import { ResourceInfo } from '@shared/models/resource.models'; +import { OtaPackage } from '@shared/models/ota-package.models'; export const entityDetailsPageBreadcrumbLabelFunction: BreadCrumbLabelFunction = ((route, translate, component) => { - return component.entity?.name; + switch (component.entitiesTableConfig.entityType) { + case EntityType.TB_RESOURCE: + case EntityType.OTA_PACKAGE: + return (component.entity as ResourceInfo | OtaPackage)?.title; + default: + return component.entity?.name; + } }); diff --git a/ui-ngx/src/app/modules/home/pages/home-pages.module.ts b/ui-ngx/src/app/modules/home/pages/home-pages.module.ts index b1f4715c33..005c47f787 100644 --- a/ui-ngx/src/app/modules/home/pages/home-pages.module.ts +++ b/ui-ngx/src/app/modules/home/pages/home-pages.module.ts @@ -42,6 +42,7 @@ import { AlarmModule } from '@home/pages/alarm/alarm.module'; import { EntitiesModule } from '@home/pages/entities/entities.module'; import { FeaturesModule } from '@home/pages/features/features.module'; import { NotificationModule } from '@home/pages/notification/notification.module'; +import { AccountModule } from '@home/pages/account/account.module'; @NgModule({ exports: [ @@ -70,7 +71,8 @@ import { NotificationModule } from '@home/pages/notification/notification.module ApiUsageModule, OtaUpdateModule, UserModule, - VcModule + VcModule, + AccountModule ] }) export class HomePagesModule { } diff --git a/ui-ngx/src/app/modules/home/pages/notification/inbox/inbox-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/notification/inbox/inbox-table-config.resolver.ts index ef82d0d9e4..a5e6651dd2 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/inbox/inbox-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/inbox/inbox-table-config.resolver.ts @@ -39,6 +39,7 @@ import { } from '@home/pages/notification/inbox/inbox-notification-dialog.component'; import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; +import { UtilsService } from '@core/services/utils.service'; @Injectable() export class InboxTableConfigResolver implements Resolve> { @@ -48,7 +49,8 @@ export class InboxTableConfigResolver implements Resolve('createdTime', 'common.created-time', this.datePipe, '170px'), new EntityTableColumn('type', 'notification.type', '10%', (notification) => this.translate.instant(NotificationTemplateTypeTranslateMap.get(notification.type).name)), - new EntityTableColumn('subject', 'notification.subject', '30%'), - new EntityTableColumn('text', 'notification.message', '60%') + new EntityTableColumn('subject', 'notification.subject', '30%', + (entity) => this.utilsService.customTranslation(entity.subject, entity.subject)), + new EntityTableColumn('text', 'notification.message', '60%', + (entity) => this.utilsService.customTranslation(entity.text, entity.text)) ); } diff --git a/ui-ngx/src/app/modules/home/pages/notification/inbox/inbox-table-header.component.scss b/ui-ngx/src/app/modules/home/pages/notification/inbox/inbox-table-header.component.scss index 01519235aa..628a964d4d 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/inbox/inbox-table-header.component.scss +++ b/ui-ngx/src/app/modules/home/pages/notification/inbox/inbox-table-header.component.scss @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@import "../../../../../../theme"; +@import "../../../../../../scss/constants"; :host ::ng-deep { .mat-button-toggle-group.tb-notification-unread-toggle-group { diff --git a/ui-ngx/src/app/modules/home/pages/notification/notification.module.ts b/ui-ngx/src/app/modules/home/pages/notification/notification.module.ts index 5ab7bfa957..389bf358d6 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/notification.module.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/notification.module.ts @@ -35,6 +35,10 @@ import { EscalationFormComponent } from '@home/pages/notification/rule/escalatio import { EscalationsComponent } from '@home/pages/notification/rule/escalations.component'; import { RuleNotificationDialogComponent } from '@home/pages/notification/rule/rule-notification-dialog.component'; import { RuleTableHeaderComponent } from '@home/pages/notification/rule/rule-table-header.component'; +import { NotificationSettingsComponent } from '@home/pages/notification/settings/notification-settings.component'; +import { + NotificationSettingFormComponent +} from '@home/pages/notification/settings/notification-setting-form.component'; @NgModule({ declarations: [ @@ -49,7 +53,9 @@ import { RuleTableHeaderComponent } from '@home/pages/notification/rule/rule-tab EscalationFormComponent, EscalationsComponent, RuleNotificationDialogComponent, - RuleTableHeaderComponent + RuleTableHeaderComponent, + NotificationSettingsComponent, + NotificationSettingFormComponent ], imports: [ CommonModule, diff --git a/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.component.html b/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.component.html index 378407d862..9988adcc92 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.component.html @@ -61,12 +61,11 @@
- - {{ 'tenant.tenant' | translate }} - {{ 'tenant-profile.tenant-profile' | translate }} - + + {{ 'tenant.tenant' | translate }} + {{ 'tenant-profile.tenant-profile' | translate }} +
+
+ + notification.webhook-url + + + {{ 'notification.webhook-url-required' | translate }} + + + + notification.channel-name + + + {{ 'notification.channel-name-required' | translate }} + + +
notification.description diff --git a/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.component.scss index 8406cae3fe..56f1fbf5d2 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.component.scss @@ -14,12 +14,12 @@ * limitations under the License. */ @import "../../../../../../scss/constants"; -@import "../../../../../../theme"; :host { form.tb-dialog-container { min-width: 600px; max-height: 100vh; + color: rgba(0, 0, 0, 0.87); } .mat-dialog-content { @@ -30,6 +30,11 @@ display: block; padding-bottom: 6px; } + + .tb-notification-tenant-group { + width: 280px; + margin-bottom: 16px; + } } :host ::ng-deep { @@ -52,58 +57,4 @@ flex-direction: column; } } - - .mat-button-toggle-group.tb-notification-tenant-group { - &.mat-button-toggle-group-appearance-standard { - border: none; - border-radius: 18px; - margin-bottom: 14px; - - .mat-button-toggle + .mat-button-toggle { - border-left: none; - } - } - - .mat-button-toggle { - background: rgba(0, 0, 0, 0.06); - height: 36px; - align-items: center; - display: flex; - - .mat-button-toggle-ripple { - top: 2px; - left: 2px; - right: 2px; - bottom: 2px; - border-radius: 18px; - } - } - - .mat-button-toggle-button { - color: #959595; - } - - .mat-button-toggle-focus-overlay { - border-radius: 18px; - margin: 2px; - } - - .mat-button-toggle-checked .mat-button-toggle-button { - background-color: $tb-primary-color; - color: #fff; - border-radius: 18px; - margin-left: 2px; - margin-right: 2px; - } - - .mat-button-toggle-appearance-standard .mat-button-toggle-label-content { - line-height: 34px; - font-size: 16px; - font-weight: 500; - } - - .mat-button-toggle-checked.mat-button-toggle-appearance-standard:not(.mat-button-toggle-disabled):hover .mat-button-toggle-focus-overlay { - opacity: .01; - } - } } diff --git a/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.component.ts index c07d8beb75..16ef8d12ae 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-notification-dialog.component.ts @@ -39,7 +39,6 @@ import { Authority } from '@shared/models/authority.enum'; import { AuthState } from '@core/auth/auth.models'; import { getCurrentAuthState } from '@core/auth/auth.selectors'; import { AuthUser } from '@shared/models/user.model'; -import { control } from 'leaflet'; export interface RecipientNotificationDialogData { target?: NotificationTarget; @@ -99,6 +98,8 @@ export class RecipientNotificationDialogComponent extends }), conversationType: [{value: SlackChanelType.PUBLIC_CHANNEL, disabled: true}], conversation: [{value: '', disabled: true}, Validators.required], + webhookUrl: [{value: '', disabled: true}, Validators.required], + channelName: [{value: '', disabled: true}, Validators.required], description: [null] }) }); @@ -116,6 +117,10 @@ export class RecipientNotificationDialogComponent extends this.targetNotificationForm.get('configuration.conversationType').enable({emitEvent: false}); this.targetNotificationForm.get('configuration.conversation').enable({emitEvent: false}); break; + case NotificationTargetType.MICROSOFT_TEAMS: + this.targetNotificationForm.get('configuration.webhookUrl').enable({emitEvent: false}); + this.targetNotificationForm.get('configuration.channelName').enable({emitEvent: false}); + break; } this.targetNotificationForm.get('configuration.type').enable({emitEvent: false}); this.targetNotificationForm.get('configuration.description').enable({emitEvent: false}); @@ -160,7 +165,7 @@ export class RecipientNotificationDialogComponent extends if (isDefinedAndNotNull(data.target)) { this.targetNotificationForm.patchValue(data.target, {emitEvent: false}); this.targetNotificationForm.get('configuration.type').updateValueAndValidity({onlySelf: true}); - if (this.isSysAdmin() && data.target.configuration.usersFilter.type === NotificationTargetConfigType.TENANT_ADMINISTRATORS) { + if (this.isSysAdmin() && data.target.configuration.usersFilter?.type === NotificationTargetConfigType.TENANT_ADMINISTRATORS) { this.targetNotificationForm.get('configuration.usersFilter.filterByTenants') .patchValue(!Array.isArray(this.data.target.configuration.usersFilter.tenantProfilesIds), {onlySelf: true}); } diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html index 4e7a133009..ef4b8f3f81 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html @@ -495,6 +495,28 @@ + + {{ 'notification.rate-limits-trigger-settings' | translate }} +
+
+ + +
+
+
+
+ + notification.description + + +
+
+
diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.scss index 7e582d2b7d..3c2a3b70d5 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.scss @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@import "../../../../../../theme"; @import "../../../../../../scss/constants"; :host { @@ -97,6 +96,7 @@ flex-direction: column; height: 100%; padding: 0 !important; + color: rgba(0, 0, 0, 0.87); .mat-stepper-horizontal { display: flex; diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts index 93b22911f3..eb156cd071 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts @@ -66,6 +66,8 @@ import { ApiUsageStateValue, ApiUsageStateValueTranslationMap } from '@shared/models/api-usage.models'; +import { LimitedApi, LimitedApiTranslationMap } from '@shared/models/limited-api.models'; +import { StringItemsOption } from '@shared/components/string-items-list.component'; export interface RuleNotificationDialogData { rule?: NotificationRule; @@ -95,6 +97,7 @@ export class RuleNotificationDialogComponent extends entitiesLimitTemplateForm: FormGroup; apiUsageLimitTemplateForm: FormGroup; newPlatformVersionTemplateForm: FormGroup; + rateLimitsTemplateForm: FormGroup; triggerType = TriggerType; triggerTypes: TriggerType[]; @@ -129,6 +132,8 @@ export class RuleNotificationDialogComponent extends apiFeatures: ApiFeature[] = Object.values(ApiFeature); apiFeatureTranslationMap = ApiFeatureTranslationMap; + limitedApis: StringItemsOption[]; + entityType = EntityType; isAdd = true; @@ -171,6 +176,11 @@ export class RuleNotificationDialogComponent extends this.isAdd = data.isAdd; } + this.limitedApis = Object.values(LimitedApi).map(value => ({ + name: this.translate.instant(LimitedApiTranslationMap.get(value)), + value + })); + this.stepperOrientation = this.breakpointObserver.observe(MediaBreakpoints['gt-xs']) .pipe(map(({matches}) => matches ? 'horizontal' : 'vertical')); @@ -302,6 +312,12 @@ export class RuleNotificationDialogComponent extends }) }); + this.rateLimitsTemplateForm = this.fb.group({ + triggerConfig: this.fb.group({ + apis: [] + }) + }); + this.triggerTypeFormsMap = new Map([ [TriggerType.ALARM, this.alarmTemplateForm], [TriggerType.ALARM_COMMENT, this.alarmCommentTemplateForm], @@ -311,7 +327,8 @@ export class RuleNotificationDialogComponent extends [TriggerType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, this.ruleEngineEventsTemplateForm], [TriggerType.ENTITIES_LIMIT, this.entitiesLimitTemplateForm], [TriggerType.API_USAGE_LIMIT, this.apiUsageLimitTemplateForm], - [TriggerType.NEW_PLATFORM_VERSION, this.newPlatformVersionTemplateForm] + [TriggerType.NEW_PLATFORM_VERSION, this.newPlatformVersionTemplateForm], + [TriggerType.RATE_LIMITS, this.rateLimitsTemplateForm] ]); if (data.isAdd || data.isCopy) { @@ -447,6 +464,7 @@ export class RuleNotificationDialogComponent extends TriggerType.ENTITIES_LIMIT, TriggerType.API_USAGE_LIMIT, TriggerType.NEW_PLATFORM_VERSION, + TriggerType.RATE_LIMITS ]); if (this.isSysAdmin()) { diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts index 86f8120ae0..94f363e878 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts @@ -81,7 +81,7 @@ export class RuleTableConfigResolver implements Resolve this.translate.instant(TriggerTypeTranslationMap.get(rule.triggerType)) || '', () => ({}), true), new EntityTableColumn('additionalConfig.description', 'notification.description', '30%', - (target) => target.additionalConfig.description || '', + (target) => target.additionalConfig?.description || '', () => ({}), false) ); } @@ -95,9 +95,9 @@ export class RuleTableConfigResolver implements Resolve this.translate.instant(entity.enabled ? 'notification.rule-disable' : 'notification.rule-enable'), - mdiIcon: 'mdi:toggle-switch', + icon: 'mdi:toggle-switch', isEnabled: () => true, - mdiIconFunction: (entity) => entity.enabled ? 'mdi:toggle-switch' : 'mdi:toggle-switch-off-outline', + iconFunction: (entity) => entity.enabled ? 'mdi:toggle-switch' : 'mdi:toggle-switch-off-outline', onAction: ($event, entity) => this.toggleEnableMode($event, entity) }, { diff --git a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-error-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/notification/sent/sent-error-dialog.component.scss index 95301b4daa..52a40155e4 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-error-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/pages/notification/sent/sent-error-dialog.component.scss @@ -17,6 +17,7 @@ display: block; width: 600px; max-width: 100%; + color: rgba(0, 0, 0, 0.87); h6 { font-weight: 500; diff --git a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.html b/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.html index 5be232104b..a7cc0cea42 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.html @@ -27,10 +27,9 @@ -
@@ -178,73 +177,82 @@ {{ 'notification.message-required' | translate }} -
-
- +
+
+ {{ 'icon.icon' | translate }} -
- +
+ - +
-
-
- - {{ 'notification.action-button' | translate }} - -
-
- - notification.button-text - - - {{ 'notification.button-text-required' | translate }} - - - {{ 'notification.button-text-max-length' | translate : - {length: webTemplateForm.get('additionalConfig.actionButtonConfig.text').getError('maxlength').requiredLength} - }} - - -
-
- - notification.action-type - - - {{ actionButtonLinkTypeTranslateMap.get(actionButtonLinkType) | translate }} - - - - - notification.link - - - {{ 'notification.link-required' | translate }} - - - - - - - - -
- - {{ 'notification.set-entity-from-notification' | translate }} - -
-
+
+
+ + + + + {{ 'notification.action-button' | translate }} + + + + +
+ + notification.button-text + + + {{ 'notification.button-text-required' | translate }} + + + {{ 'notification.button-text-max-length' | translate : + {length: webTemplateForm.get('additionalConfig.actionButtonConfig.text').getError('maxlength').requiredLength} + }} + + +
+
+ + notification.action-type + + + {{ actionButtonLinkTypeTranslateMap.get(actionButtonLinkType) | translate }} + + + + + notification.link + + + {{ 'notification.link-required' | translate }} + + + + + + + + +
+ + {{ 'notification.set-entity-from-notification' | translate }} + +
+
+
@@ -339,6 +347,104 @@ + + {{ 'notification.delivery-method.microsoft-teams' | translate }} +
+ {{ 'notification.input-fields-support-templatization' | translate}} + +
+
+ + notification.subject + + + + notification.message + + + {{ 'notification.message-required' | translate }} + + +
+
+
notification.theme-color
+ +
+
+ + + + + {{ 'notification.action-button' | translate }} + + + + +
+ + notification.button-text + + + {{ 'notification.button-text-required' | translate }} + + + {{ 'notification.button-text-max-length' | translate : + {length: microsoftTeamsTemplateForm.get('button.text').getError('maxlength').requiredLength} + }} + + +
+
+ + notification.action-type + + + {{ actionButtonLinkTypeTranslateMap.get(actionButtonLinkType) | translate }} + + + + + notification.link + + + {{ 'notification.link-required' | translate }} + + + + + + + + +
+ + {{ 'notification.set-entity-from-notification' | translate }} + +
+
+
+
+
+
@@ -427,6 +533,16 @@
{{ preview.processedTemplates.MOBILE.body }}
+
+
+ +
notification.delivery-method.microsoft-teams-preview
+
+
+
{{ preview.processedTemplates.MICROSOFT_TEAMS.subject }}
+ {{ preview.processedTemplates.MICROSOFT_TEAMS.body }} +
+
supervisor_account @@ -449,7 +565,7 @@
-
+
diff --git a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.scss index 4623ab1637..df80b7e7fe 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.component.scss @@ -14,15 +14,34 @@ * limitations under the License. */ @import "../../../../../../scss/constants"; -@import "../../../../../../theme"; -:host-context(.tb-fullscreen-dialog .mat-mdc-dialog-container) { +:host { width: 930px; height: 100%; max-width: 100%; max-height: 100vh; - display: flex; - flex-direction: column; + display: grid; + grid-template-rows: min-content 4px minmax(auto, 1fr) min-content min-content; +} + +:host-context(.tb-fullscreen-dialog .mat-mdc-dialog-container) { + .mat-mdc-dialog-content { + grid-row: 3; + display: flex; + flex-direction: column; + height: 100%; + padding: 0; + color: rgba(0, 0, 0, 0.87); + } + + .tb-dialog-actions { + grid-row: 5; + display: flex; + } + + .mat-divider { + grid-row: 4; + } .tb-title { font-size: 16px; @@ -179,17 +198,28 @@ line-height: 20px; overflow-x: auto; } + + &.mini { + font-size: 12px; + line-height: 1.25; + + .subject { + font-size: 14px; + line-height: 1.5; + padding-bottom: 4px; + } + } } } + + .tb-notification-use-template-toggle-group { + margin-bottom: 24px; + width: 320px; + } } :host ::ng-deep { .mat-mdc-dialog-content { - display: flex; - flex-direction: column; - height: 100%; - padding: 0 !important; - .mat-stepper-horizontal { display: flex; height: 100%; @@ -210,6 +240,14 @@ } } } + + .tb-form-panel .mat-expansion-panel.tb-settings { + padding: 11px 16px; + + & > .mat-expansion-panel-content > .mat-expansion-panel-body { + gap: 0; + } + } } .tb-notification-use-template-toggle-group { @@ -224,9 +262,7 @@ } } } -} -:host ::ng-deep { .delivery-methods-container { .delivery-method-container { &.interact * { diff --git a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.componet.ts b/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.componet.ts index 674da8c5e8..1ac53c87ce 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.componet.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/sent/sent-notification-dialog.componet.ts @@ -63,7 +63,6 @@ export class SentNotificationDialogComponent extends @ViewChild('createNotification', {static: true}) createNotification: MatStepper; stepperOrientation: Observable; - stepperLabelPosition: Observable<'bottom' | 'end'>; isAdd = true; entityType = EntityType; @@ -102,9 +101,6 @@ export class SentNotificationDialogComponent extends this.stepperOrientation = this.breakpointObserver.observe(MediaBreakpoints['gt-sm']) .pipe(map(({matches}) => matches ? 'horizontal' : 'vertical')); - this.stepperLabelPosition = this.breakpointObserver.observe(MediaBreakpoints['gt-md']) - .pipe(map(({matches}) => matches ? 'end' : 'bottom')); - this.notificationRequestForm = this.fb.group({ useTemplate: [false], templateId: [{value: null, disabled: true}, Validators.required], @@ -162,7 +158,6 @@ export class SentNotificationDialogComponent extends } this.notificationRequestForm.get('useTemplate').setValue(useTemplate, {onlySelf : true}); } - this.refreshAllowDeliveryMethod(); } @@ -342,19 +337,23 @@ export class SentNotificationDialogComponent extends refreshAllowDeliveryMethod() { this.notificationService.getAvailableDeliveryMethods({ignoreLoading: true}).subscribe(allowMethods => { this.allowNotificationDeliveryMethods = allowMethods; - this.updateDeliveryMethodsDisableState(); - this.showRefresh = (this.notificationDeliveryMethods.length !== allowMethods.length); + if (!this.notificationRequestForm.get('useTemplate').value) { + this.updateDeliveryMethodsDisableState(); + this.showRefresh = (this.notificationDeliveryMethods.length !== allowMethods.length); + } }); } private updateDeliveryMethodsDisableState() { - this.notificationDeliveryMethods.forEach(method => { - if (this.allowNotificationDeliveryMethods.includes(method)) { - this.getDeliveryMethodsTemplatesControl(method).enable({emitEvent: true}); - } else { - this.getDeliveryMethodsTemplatesControl(method).disable({emitEvent: true}); - this.getDeliveryMethodsTemplatesControl(method).setValue(false, {emitEvent: true}); //used for notify again - } - }); + if (this.allowNotificationDeliveryMethods) { + this.notificationDeliveryMethods.forEach(method => { + if (this.allowNotificationDeliveryMethods.includes(method)) { + this.getDeliveryMethodsTemplatesControl(method).enable({emitEvent: true}); + } else { + this.getDeliveryMethodsTemplatesControl(method).disable({emitEvent: true}); + this.getDeliveryMethodsTemplatesControl(method).setValue(false, {emitEvent: true}); //used for notify again + } + }); + } } } diff --git a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/notification/sent/sent-table-config.resolver.ts index 290774b6e9..cc7a5db2ff 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/sent/sent-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/sent/sent-table-config.resolver.ts @@ -107,7 +107,7 @@ export class SentTableConfigResolver implements Resolve> { return [{ name: this.translate.instant('notification.notify-again'), - mdiIcon: 'mdi:repeat-variant', + icon: 'mdi:repeat-variant', isEnabled: (request) => request.status !== NotificationRequestStatus.SCHEDULED, onAction: ($event, entity) => this.createRequest($event, entity) }]; diff --git a/ui-ngx/src/app/modules/home/pages/notification/settings/notification-setting-form.component.html b/ui-ngx/src/app/modules/home/pages/notification/settings/notification-setting-form.component.html new file mode 100644 index 0000000000..fe9f3a5343 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/notification/settings/notification-setting-form.component.html @@ -0,0 +1,41 @@ + +
+
+
+ + + {{notificationTemplateTypeTranslateMap.get(notificationSettingsFormGroup.get('name').value)?.name | translate}} + + +
+
+
+ +
+
+
+
diff --git a/ui-ngx/src/app/modules/home/pages/notification/settings/notification-setting-form.component.scss b/ui-ngx/src/app/modules/home/pages/notification/settings/notification-setting-form.component.scss new file mode 100644 index 0000000000..ee37181a09 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/notification/settings/notification-setting-form.component.scss @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + .notification-type { + font-size: 14px; + + &-disabled { + color: rgba(0, 0, 0, 0.38) + } + } +} diff --git a/ui-ngx/src/app/modules/home/pages/notification/settings/notification-setting-form.component.ts b/ui-ngx/src/app/modules/home/pages/notification/settings/notification-setting-form.component.ts new file mode 100644 index 0000000000..be9ea3988f --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/notification/settings/notification-setting-form.component.ts @@ -0,0 +1,145 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { deepClone, isDefinedAndNotNull } from '@core/utils'; +import { Subscription } from 'rxjs'; +import { + NotificationDeliveryMethod, + NotificationTemplateTypeTranslateMap, + NotificationUserSetting +} from '@shared/models/notification.models'; + +@Component({ + selector: 'tb-notification-setting-form', + templateUrl: './notification-setting-form.component.html', + styleUrls: ['./notification-setting-form.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => NotificationSettingFormComponent), + multi: true + } + ] +}) +export class NotificationSettingFormComponent implements ControlValueAccessor, OnInit, OnDestroy { + + @Input() + disabled: boolean; + + @Input() + deliveryMethods: NotificationDeliveryMethod[] = []; + + notificationSettingsFormGroup: UntypedFormGroup; + + notificationTemplateTypeTranslateMap = NotificationTemplateTypeTranslateMap; + + private propagateChange = null; + + private valueChange$: Subscription = null; + + constructor(private fb: UntypedFormBuilder) { + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + ngOnInit() { + const deliveryMethod = {}; + this.deliveryMethods.forEach(value => { + deliveryMethod[value] = true; + }); + this.notificationSettingsFormGroup = this.fb.group( + { + name: [''], + enabled: [true], + enabledDeliveryMethods: this.fb.group({ + ...deliveryMethod + }) + }); + this.valueChange$ = this.notificationSettingsFormGroup.valueChanges.subscribe(() => { + this.updateModel(); + }); + } + + ngOnDestroy() { + if (this.valueChange$) { + this.valueChange$.unsubscribe(); + this.valueChange$ = null; + } + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.notificationSettingsFormGroup.disable({emitEvent: false}); + } else { + this.notificationSettingsFormGroup.enable({emitEvent: false}); + } + } + + getChecked(deliveryMethod?: NotificationDeliveryMethod): boolean { + if (deliveryMethod) { + return this.notificationSettingsFormGroup.get('enabledDeliveryMethods').get(deliveryMethod).value; + } else { + const enabledDeliveryMethod = Object.values(this.notificationSettingsFormGroup.get('enabledDeliveryMethods').value); + const checked = enabledDeliveryMethod.some(value => value === true); + if (checked !== this.notificationSettingsFormGroup.get('enabled').value) { + setTimeout(() => this.notificationSettingsFormGroup.get('enabled').patchValue(checked), 0); + } + return enabledDeliveryMethod.every(value => value); + } + } + + toggleDeliviryMethod(deliveryMethod: NotificationDeliveryMethod) { + this.notificationSettingsFormGroup.get('enabledDeliveryMethods').get(deliveryMethod) + .patchValue(!this.notificationSettingsFormGroup.get('enabledDeliveryMethods').get(deliveryMethod).value); + } + + changeInstanceTypeCheckBox(value: any) { + const enabledDeliveryMethod = deepClone(this.notificationSettingsFormGroup.get('enabledDeliveryMethods').value); + Object.keys(enabledDeliveryMethod).forEach(key => { + enabledDeliveryMethod[key] = value; + }); + this.notificationSettingsFormGroup.get('enabled').patchValue(value, {emitEvent: false}); + this.notificationSettingsFormGroup.get('enabledDeliveryMethods').patchValue(enabledDeliveryMethod); + this.notificationSettingsFormGroup.markAsDirty(); + } + + getIndeterminate() { + if (!this.notificationSettingsFormGroup.get('enabled').value) { + return false; + } + const enabledDeliveryMethod: Array = Object.values(this.notificationSettingsFormGroup.get('enabledDeliveryMethods').value); + const checkedResource = enabledDeliveryMethod.filter(value => value); + return checkedResource.length !== 0 && checkedResource.length !== enabledDeliveryMethod.length; + } + + writeValue(value: NotificationUserSetting): void { + if (isDefinedAndNotNull(value)) { + this.notificationSettingsFormGroup.patchValue(value, {emitEvent: false}); + } + } + + private updateModel() { + this.propagateChange(this.notificationSettingsFormGroup.value); + } +} diff --git a/ui-ngx/src/app/modules/home/pages/notification/settings/notification-settings-routing.modules.ts b/ui-ngx/src/app/modules/home/pages/notification/settings/notification-settings-routing.modules.ts new file mode 100644 index 0000000000..6a65e554bf --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/notification/settings/notification-settings-routing.modules.ts @@ -0,0 +1,41 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Routes } from '@angular/router'; +import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; +import { Authority } from '@shared/models/authority.enum'; +import { inject, NgModule } from '@angular/core'; +import { NotificationSettingsComponent } from '@home/pages/notification/settings/notification-settings.component'; +import { NotificationService } from '@core/http/notification.service'; + +export const notificationUserSettingsRoutes: Routes = [ + { + path: 'notificationSettings', + component: NotificationSettingsComponent, + canDeactivate: [ConfirmOnExitGuard], + data: { + auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN, Authority.CUSTOMER_USER], + title: 'account.notification-settings', + breadcrumb: { + label: 'account.notification-settings', + icon: 'settings' + } + }, + resolve: { + userSettings: () => inject(NotificationService).getNotificationUserSettings() + } + } +]; diff --git a/ui-ngx/src/app/modules/home/pages/notification/settings/notification-settings.component.html b/ui-ngx/src/app/modules/home/pages/notification/settings/notification-settings.component.html new file mode 100644 index 0000000000..33c446e32d --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/notification/settings/notification-settings.component.html @@ -0,0 +1,82 @@ + +
+ + +
+
+ notification.settings.notification-settings +
+
+ +
+
+
+ + +
+ +
+
+
+
+
+ + notification.settings.type + +
+
+
+ + {{ notificationDeliveryMethodTranslateMap.get(deliveryMethods) | translate }} + +
+
+
+ +
+ + + +
+
+
+
+
+
+ +
+
+
diff --git a/ui-ngx/src/app/modules/home/pages/notification/settings/notification-settings.component.scss b/ui-ngx/src/app/modules/home/pages/notification/settings/notification-settings.component.scss new file mode 100644 index 0000000000..a91c57ed65 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/notification/settings/notification-settings.component.scss @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import "../../../../../../scss/constants"; + +:host { + .mat-mdc-card.settings-card { + margin: 8px; + @media #{$mat-gt-sm} { + width: 60%; + } + .mat-headline-5 { + margin: 0; + } + .notification-form { + height: 100%; + min-height: min-content; + max-height: min-content; + } + .notification-section { + height: 100%; + border: 1px solid rgba(0, 0, 0, 0.12); + overflow: scroll; + &-block { + min-width: 470px; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/pages/notification/settings/notification-settings.component.ts b/ui-ngx/src/app/modules/home/pages/notification/settings/notification-settings.component.ts new file mode 100644 index 0000000000..52a09ad3b5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/notification/settings/notification-settings.component.ts @@ -0,0 +1,191 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, OnInit } from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { AbstractControl, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard'; +import { TranslateService } from '@ngx-translate/core'; +import { ActivatedRoute } from '@angular/router'; +import { deepClone, isDefinedAndNotNull } from '@core/utils'; +import { + NotificationDeliveryMethod, + NotificationDeliveryMethodTranslateMap, + NotificationUserSettings +} from '@shared/models/notification.models'; +import { NotificationService } from '@core/http/notification.service'; +import { DialogService } from '@core/services/dialog.service'; + +@Component({ + selector: 'tb-notification-settings', + templateUrl: './notification-settings.component.html', + styleUrls: ['./notification-settings.component.scss'] +}) +export class NotificationSettingsComponent extends PageComponent implements OnInit, HasConfirmForm { + + notificationSettings: UntypedFormGroup; + + notificationDeliveryMethods: NotificationDeliveryMethod[]; + notificationDeliveryMethodTranslateMap = NotificationDeliveryMethodTranslateMap; + + private deliveryMethods = new Set([ + NotificationDeliveryMethod.SLACK, + NotificationDeliveryMethod.MICROSOFT_TEAMS + ]); + + constructor(protected store: Store, + private route: ActivatedRoute, + private translate: TranslateService, + private dialogService: DialogService, + private notificationService: NotificationService, + private fb: UntypedFormBuilder,) { + super(store); + this.notificationService.getAvailableDeliveryMethods({ignoreLoading: true}).subscribe( + allowMethods => { + this.notificationDeliveryMethods = allowMethods.filter(value => !this.deliveryMethods.has(value)); + this.patchNotificationSettings(this.route.snapshot.data.userSettings); + }); + } + + ngOnInit() { + this.buildNotificationSettingsForm(); + } + + private buildNotificationSettingsForm() { + this.notificationSettings = this.fb.group({ + prefs: this.fb.array([]) + }); + } + + private patchNotificationSettings(settings: NotificationUserSettings) { + const notificationSettingsControls: Array = []; + if (settings.prefs) { + this.prepareNotificationSettings(settings.prefs).forEach(setting => + notificationSettingsControls.push(this.fb.control(setting, [Validators.required])) + ); + } + this.notificationSettings.setControl('prefs', this.fb.array(notificationSettingsControls), {emitEvent: false}); + } + + private prepareNotificationSettings(prefs: any) { + return Object.entries(prefs).map((value: any) => { + value[1].name = value[0]; + if (!value[1].enabled && Object.values(value[1].enabledDeliveryMethods).some(deliveryMethod => deliveryMethod === true)) { + const enabledDeliveryMethod = deepClone(value[1].enabledDeliveryMethods); + Object.keys(enabledDeliveryMethod).forEach(key => { + enabledDeliveryMethod[key] = false; + }); + value[1].enabledDeliveryMethods = enabledDeliveryMethod; + } + value[1].enabledDeliveryMethods = Object.assign( + this.notificationDeliveryMethods.reduce((a, v) => ({ ...a, [v]: true}), {}), + value[1].enabledDeliveryMethods + ); + return value[1]; + }); + } + + resetSettings() { + this.dialogService.confirm( + this.translate.instant('notification.settings.reset-all-title'), + this.translate.instant('notification.settings.reset-all-text'), + this.translate.instant('action.no'), + this.translate.instant('action.yes'), + true + ).subscribe( + result => { + if (result) { + const settings = this.prepareNotificationSettings(this.route.snapshot.data.userSettings.prefs); + const notificationSettingsControls: Array = []; + this.notificationSettings.reset({}); + if (settings) { + settings.forEach((setting) => { + setting.enabled = true; + setting.enabledDeliveryMethods = this.notificationDeliveryMethods.reduce((a, v) => ({ ...a, [v]: true}), {}); + notificationSettingsControls.push(this.fb.control(setting, [Validators.required])); + }); + } + this.notificationSettings.setControl('prefs', this.fb.array(notificationSettingsControls), {emitEvent: false}); + this.save(); + } + } + ); + } + + getChecked = (method: NotificationDeliveryMethod = null): boolean => { + const type = this.notificationSettings.get('prefs').value; + if (isDefinedAndNotNull(method)) { + return isDefinedAndNotNull(type) && type.every(resource => resource.enabledDeliveryMethods[method]); + } + return isDefinedAndNotNull(type) && type.every(resource => resource.enabled); + }; + + getSomeChecked = () => { + const type = this.notificationSettings.get('prefs').value; + return isDefinedAndNotNull(type) && type.some(resource => resource.enabled); + }; + + getIndeterminate = (deliveryMethod: NotificationDeliveryMethod = null): boolean => { + const type = this.notificationSettings.get('prefs').value; + if (isDefinedAndNotNull(type)) { + const checkedResource = isDefinedAndNotNull(deliveryMethod) ? + type.filter(resource => resource.enabledDeliveryMethods[deliveryMethod]) : + type.filter(resource => resource.enabled); + return checkedResource.length !== 0 && checkedResource.length !== type.length; + } + return false; + }; + + changeInstanceTypeCheckBox = (value: boolean, deliveryMethod: NotificationDeliveryMethod = null): void => { + const type = deepClone(this.notificationSettings.get('prefs').value); + if (isDefinedAndNotNull(deliveryMethod)) { + type.forEach(notificationType => notificationType.enabledDeliveryMethods[deliveryMethod] = value); + } else { + type.forEach(notificationType => { + notificationType.enabled = value; + notificationType.enabledDeliveryMethods = + Object.keys(notificationType.enabledDeliveryMethods).reduce((a, v) => ({ ...a, [v]: value}), {}); + }); + } + this.notificationSettings.get('prefs').patchValue(type, {emitEvent: false}); + this.notificationSettings.markAsDirty(); + }; + + get notificationSettingsFormArray(): UntypedFormArray { + return this.notificationSettings.get('prefs') as UntypedFormArray; + } + + save(): void { + const settings = {prefs: {}}; + this.notificationSettings.getRawValue().prefs.forEach(value => { + const key = value.name; + delete value.name; + settings.prefs[key] = value; + }); + this.notificationService.saveNotificationUserSettings(settings).subscribe( + (userSettings) => { + this.notificationSettings.get('prefs').reset({}); + this.patchNotificationSettings(userSettings); + } + ); + } + + confirmForm(): UntypedFormGroup { + return this.notificationSettings; + } +} diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/template-configuration.ts b/ui-ngx/src/app/modules/home/pages/notification/template/template-configuration.ts index 90115738a6..53b5a44a77 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/template/template-configuration.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/template/template-configuration.ts @@ -33,6 +33,7 @@ import { AppState } from '@core/core.state'; import { Router } from '@angular/router'; import { MatDialogRef } from '@angular/material/dialog'; import { deepClone, deepTrim } from '@core/utils'; +import tinymce from 'tinymce'; @Directive() // tslint:disable-next-line:directive-class-suffix @@ -43,6 +44,7 @@ export abstract class TemplateConfiguration extends DialogComponent< emailTemplateForm: FormGroup; smsTemplateForm: FormGroup; slackTemplateForm: FormGroup; + microsoftTeamsTemplateForm: FormGroup; mobileTemplateForm: FormGroup; notificationDeliveryMethods = Object.keys(NotificationDeliveryMethod) as NotificationDeliveryMethod[]; @@ -61,6 +63,7 @@ export abstract class TemplateConfiguration extends DialogComponent< toolbar: 'fontselect fontsizeselect | formatselect | bold italic strikethrough forecolor backcolor ' + '| link | table | image | alignleft aligncenter alignright alignjustify ' + '| numlist bullist outdent indent | removeformat | code | fullscreen', + toolbar_mode: 'sliding', height: 400, autofocus: false, branding: false @@ -96,17 +99,9 @@ export abstract class TemplateConfiguration extends DialogComponent< icon: this.fb.group({ enabled: [false], icon: [{value: 'notifications', disabled: true}, Validators.required], - color: ['#757575'] - }), - actionButtonConfig: this.fb.group({ - enabled: [false], - text: [{value: '', disabled: true}, [Validators.required, Validators.maxLength(50)]], - linkType: [ActionButtonLinkType.LINK], - link: [{value: '', disabled: true}, Validators.required], - dashboardId: [{value: null, disabled: true}, Validators.required], - dashboardState: [{value: null, disabled: true}], - setEntityIdInState: [{value: true, disabled: true}], + color: [{value: '#757575', disabled: true}] }), + actionButtonConfig: this.createButtonConfigForm() }) }); @@ -115,39 +110,10 @@ export abstract class TemplateConfiguration extends DialogComponent< ).subscribe((value) => { if (value) { this.webTemplateForm.get('additionalConfig.icon.icon').enable({emitEvent: false}); + this.webTemplateForm.get('additionalConfig.icon.color').enable({emitEvent: false}); } else { this.webTemplateForm.get('additionalConfig.icon.icon').disable({emitEvent: false}); - } - }); - - this.webTemplateForm.get('additionalConfig.actionButtonConfig.enabled').valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe((value) => { - if (value) { - this.webTemplateForm.get('additionalConfig.actionButtonConfig').enable({emitEvent: false}); - this.webTemplateForm.get('additionalConfig.actionButtonConfig.linkType').updateValueAndValidity({onlySelf: true}); - } else { - this.webTemplateForm.get('additionalConfig.actionButtonConfig').disable({emitEvent: false}); - this.webTemplateForm.get('additionalConfig.actionButtonConfig.enabled').enable({emitEvent: false}); - } - }); - - this.webTemplateForm.get('additionalConfig.actionButtonConfig.linkType').valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe((value) => { - const isEnabled = this.webTemplateForm.get('additionalConfig.actionButtonConfig.enabled').value; - if (isEnabled) { - if (value === ActionButtonLinkType.LINK) { - this.webTemplateForm.get('additionalConfig.actionButtonConfig.link').enable({emitEvent: false}); - this.webTemplateForm.get('additionalConfig.actionButtonConfig.dashboardId').disable({emitEvent: false}); - this.webTemplateForm.get('additionalConfig.actionButtonConfig.dashboardState').disable({emitEvent: false}); - this.webTemplateForm.get('additionalConfig.actionButtonConfig.setEntityIdInState').disable({emitEvent: false}); - } else { - this.webTemplateForm.get('additionalConfig.actionButtonConfig.link').disable({emitEvent: false}); - this.webTemplateForm.get('additionalConfig.actionButtonConfig.dashboardId').enable({emitEvent: false}); - this.webTemplateForm.get('additionalConfig.actionButtonConfig.dashboardState').enable({emitEvent: false}); - this.webTemplateForm.get('additionalConfig.actionButtonConfig.setEntityIdInState').enable({emitEvent: false}); - } + this.webTemplateForm.get('additionalConfig.icon.color').disable({emitEvent: false}); } }); @@ -169,11 +135,19 @@ export abstract class TemplateConfiguration extends DialogComponent< body: ['', Validators.required] }); + this.microsoftTeamsTemplateForm = this.fb.group({ + subject: [''], + body: ['', Validators.required], + themeColor: [''], + button: this.createButtonConfigForm() + }); + this.deliveryMethodFormsMap = new Map([ [NotificationDeliveryMethod.WEB, this.webTemplateForm], [NotificationDeliveryMethod.EMAIL, this.emailTemplateForm], [NotificationDeliveryMethod.SMS, this.smsTemplateForm], [NotificationDeliveryMethod.SLACK, this.slackTemplateForm], + [NotificationDeliveryMethod.MICROSOFT_TEAMS, this.microsoftTeamsTemplateForm], [NotificationDeliveryMethod.MOBILE, this.mobileTemplateForm] ]); } @@ -205,4 +179,48 @@ export abstract class TemplateConfiguration extends DialogComponent< }); return deepTrim(template); } + + private createButtonConfigForm(): FormGroup { + const form = this.fb.group({ + enabled: [false], + text: [{value: '', disabled: true}, [Validators.required, Validators.maxLength(50)]], + linkType: [ActionButtonLinkType.LINK], + link: [{value: '', disabled: true}, Validators.required], + dashboardId: [{value: null, disabled: true}, Validators.required], + dashboardState: [{value: null, disabled: true}], + setEntityIdInState: [{value: true, disabled: true}], + }); + + form.get('enabled').valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe((value) => { + if (value) { + form.enable({emitEvent: false}); + form.get('linkType').updateValueAndValidity({onlySelf: true}); + } else { + form.disable({emitEvent: false}); + form.get('enabled').enable({emitEvent: false}); + } + }); + + form.get('linkType').valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe((value) => { + const isEnabled = form.get('enabled').value; + if (isEnabled) { + if (value === ActionButtonLinkType.LINK) { + form.get('link').enable({emitEvent: false}); + form.get('dashboardId').disable({emitEvent: false}); + form.get('dashboardState').disable({emitEvent: false}); + form.get('setEntityIdInState').disable({emitEvent: false}); + } else { + form.get('link').disable({emitEvent: false}); + form.get('dashboardId').enable({emitEvent: false}); + form.get('dashboardState').enable({emitEvent: false}); + form.get('setEntityIdInState').enable({emitEvent: false}); + } + } + }); + return form; + } } diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.html b/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.html index 784f36157b..c13e33b6e3 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.html @@ -27,10 +27,9 @@ -
@@ -101,73 +100,82 @@ {{ 'notification.message-required' | translate }} -
-
- +
+
+ {{ 'icon.icon' | translate }} -
- +
+ - +
-
-
- - {{ 'notification.action-button' | translate }} - -
-
- - notification.button-text - - - {{ 'notification.button-text-required' | translate }} - - - {{ 'notification.button-text-max-length' | translate : - {length: webTemplateForm.get('additionalConfig.actionButtonConfig.text').getError('maxlength').requiredLength} - }} - - -
-
- - notification.action-type - - - {{ actionButtonLinkTypeTranslateMap.get(actionButtonLinkType) | translate }} - - - - - notification.link - - - {{ 'notification.link-required' | translate }} - - - - - - - - -
- - {{ 'notification.set-entity-from-notification' | translate }} - -
-
+
+
+ + + + + {{ 'notification.action-button' | translate }} + + + + +
+ + notification.button-text + + + {{ 'notification.button-text-required' | translate }} + + + {{ 'notification.button-text-max-length' | translate : + {length: webTemplateForm.get('additionalConfig.actionButtonConfig.text').getError('maxlength').requiredLength} + }} + + +
+
+ + notification.action-type + + + {{ actionButtonLinkTypeTranslateMap.get(actionButtonLinkType) | translate }} + + + + + notification.link + + + {{ 'notification.link-required' | translate }} + + + + + + + + +
+ + {{ 'notification.set-entity-from-notification' | translate }} + +
+
+
@@ -259,6 +267,103 @@ + + {{ 'notification.delivery-method.microsoft-teams' | translate }} +
+ {{ 'notification.input-fields-support-templatization' | translate}} + +
+
+ + notification.subject + + + + notification.message + + + {{ 'notification.message-required' | translate }} + + +
+
+
notification.theme-color
+ +
+
+ + + + + {{ 'notification.action-button' | translate }} + + + + +
+ + notification.button-text + + + {{ 'notification.button-text-required' | translate }} + + + {{ 'notification.button-text-max-length' | translate : + {length: microsoftTeamsTemplateForm.get('button.text').getError('maxlength').requiredLength} + }} + + +
+
+ + notification.action-type + + + {{ actionButtonLinkTypeTranslateMap.get(actionButtonLinkType) | translate }} + + + + + notification.link + + + {{ 'notification.link-required' | translate }} + + + + + + + + +
+ + {{ 'notification.set-entity-from-notification' | translate }} + +
+
+
+
+
+
{{ 'notification.delivery-method.mobile' | translate }} @@ -295,7 +400,7 @@
-
+
diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.scss index 2de13512cd..cf41bae5d0 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.scss @@ -16,13 +16,33 @@ @import "../../../../../../scss/constants"; @import "../../../../../../theme"; -:host-context(.tb-fullscreen-dialog .mat-mdc-dialog-container) { +:host { width: 840px; height: 100%; max-width: 100%; max-height: 100vh; - display: flex; - flex-direction: column; + display: grid; + grid-template-rows: min-content 4px minmax(auto, 1fr) min-content min-content; +} + +:host-context(.tb-fullscreen-dialog .mat-mdc-dialog-container) { + .mat-mdc-dialog-content { + grid-row: 3; + display: flex; + flex-direction: column; + height: 100%; + padding: 0; + color: rgba(0, 0, 0, 0.87); + } + + .tb-dialog-actions { + grid-row: 5; + display: flex; + } + + .mat-divider { + grid-row: 4; + } .tb-title { font-size: 16px; @@ -81,28 +101,10 @@ } } } - - .additional-config-group { - padding: 16px 16px 4px; - margin-bottom: 12px; - border: 1px solid rgba(0, 0, 0, 0.1); - border-radius: 6px; - width: 100%; - height: 100%; - - .toggle { - margin-bottom: 12px; - } - } } :host ::ng-deep { .mat-mdc-dialog-content { - display: flex; - flex-direction: column; - height: 100%; - padding: 0 !important; - .mat-stepper-horizontal { display: flex; height: 100%; @@ -123,5 +125,13 @@ } } } + + .tb-form-panel .mat-expansion-panel.tb-settings { + padding: 11px 16px; + + & > .mat-expansion-panel-content > .mat-expansion-panel-body { + gap: 0; + } + } } } diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.ts index b4ba480732..8ddb41fe37 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.ts @@ -54,7 +54,6 @@ export class TemplateNotificationDialogComponent @ViewChild('notificationTemplateStepper', {static: true}) notificationTemplateStepper: MatStepper; stepperOrientation: Observable; - stepperLabelPosition: Observable<'bottom' | 'end'>; dialogTitle = 'notification.edit-notification-template'; @@ -82,9 +81,6 @@ export class TemplateNotificationDialogComponent this.stepperOrientation = this.breakpointObserver.observe(MediaBreakpoints['gt-sm']) .pipe(map(({matches}) => matches ? 'horizontal' : 'vertical')); - this.stepperLabelPosition = this.breakpointObserver.observe(MediaBreakpoints['gt-md']) - .pipe(map(({matches}) => matches ? 'end' : 'bottom')); - if (isDefinedAndNotNull(this.data?.predefinedType)) { this.hideSelectType = true; this.templateNotificationForm.get('notificationType').setValue(this.data.predefinedType, {emitEvent: false}); @@ -181,6 +177,7 @@ export class TemplateNotificationDialogComponent NotificationType.ENTITIES_LIMIT, NotificationType.API_USAGE_LIMIT, NotificationType.NEW_PLATFORM_VERSION, + NotificationType.RATE_LIMITS ]); if (this.isSysAdmin()) { diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html index 3545a63018..512ea2c6f0 100644 --- a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html @@ -120,6 +120,7 @@ diff --git a/ui-ngx/src/app/modules/home/pages/profile/profile-routing.module.ts b/ui-ngx/src/app/modules/home/pages/profile/profile-routing.module.ts index c14e450745..334174194c 100644 --- a/ui-ngx/src/app/modules/home/pages/profile/profile-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/profile/profile-routing.module.ts @@ -40,7 +40,7 @@ export class UserProfileResolver implements Resolve { } } -const routes: Routes = [ +export const profileRoutes: Routes = [ { path: 'profile', component: ProfileComponent, @@ -59,6 +59,13 @@ const routes: Routes = [ } ]; +const routes: Routes = [ + { + path: 'profile', + redirectTo: 'account/profile' + } +]; + @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule], diff --git a/ui-ngx/src/app/modules/home/pages/profile/profile.component.html b/ui-ngx/src/app/modules/home/pages/profile/profile.component.html index fad97beb68..c560a6f06a 100644 --- a/ui-ngx/src/app/modules/home/pages/profile/profile.component.html +++ b/ui-ngx/src/app/modules/home/pages/profile/profile.component.html @@ -38,7 +38,7 @@
user.email - + {{ 'user.email-required' | translate }} diff --git a/ui-ngx/src/app/modules/home/pages/profile/profile.component.ts b/ui-ngx/src/app/modules/home/pages/profile/profile.component.ts index 0633e696dd..cf6b3d34cb 100644 --- a/ui-ngx/src/app/modules/home/pages/profile/profile.component.ts +++ b/ui-ngx/src/app/modules/home/pages/profile/profile.component.ts @@ -30,6 +30,7 @@ import { ActionSettingsChangeLanguage } from '@core/settings/settings.actions'; import { ActivatedRoute } from '@angular/router'; import { isDefinedAndNotNull } from '@core/utils'; import { getCurrentAuthUser } from '@core/auth/auth.selectors'; +import { AuthService } from '@core/auth/auth.service'; @Component({ selector: 'tb-profile', @@ -47,6 +48,7 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir constructor(protected store: Store, private route: ActivatedRoute, private userService: UserService, + private authService: AuthService, private translate: TranslateService, public fb: UntypedFormBuilder) { super(store); @@ -94,6 +96,7 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir lastName: user.lastName, } })); this.store.dispatch(new ActionSettingsChangeLanguage({ userLang: user.additionalInfo.lang })); + this.authService.refreshJwtToken(false); } ); } diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/add-rule-node-dialog.component.html b/ui-ngx/src/app/modules/home/pages/rulechain/add-rule-node-dialog.component.html index bc72cd90c0..0da62e48bc 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/add-rule-node-dialog.component.html +++ b/ui-ngx/src/app/modules/home/pages/rulechain/add-rule-node-dialog.component.html @@ -29,16 +29,12 @@
-
- - -
+ +
diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-config.component.scss b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-config.component.scss index 8f68623578..464d28116a 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-config.component.scss +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-config.component.scss @@ -15,7 +15,7 @@ */ :host { display: block; - margin-bottom: 16px; + tb-json-object-edit.tb-rule-node-configuration-json { display: block; height: 300px; diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-config.component.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-config.component.ts index 73f0d5e14a..bf6170e4ad 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-config.component.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-config.component.ts @@ -18,14 +18,22 @@ import { AfterViewInit, Component, ComponentRef, + EventEmitter, forwardRef, Input, OnDestroy, OnInit, + Output, ViewChild, ViewContainerRef } from '@angular/core'; -import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormGroup, + Validators +} from '@angular/forms'; import { IRuleNodeConfigurationComponent, RuleNodeConfiguration, @@ -76,6 +84,12 @@ export class RuleNodeConfigComponent implements ControlValueAccessor, OnInit, On @Input() ruleChainType: RuleChainType; + @Output() + initRuleNode = new EventEmitter(); + + @Output() + changeScript = new EventEmitter(); + nodeDefinitionValue: RuleNodeDefinition; @Input() @@ -85,6 +99,7 @@ export class RuleNodeConfigComponent implements ControlValueAccessor, OnInit, On if (this.nodeDefinitionValue) { this.validateDefinedDirective(); } + setTimeout(() => this.initRuleNode.emit()); } } @@ -98,8 +113,11 @@ export class RuleNodeConfigComponent implements ControlValueAccessor, OnInit, On changeSubscription: Subscription; + changeScriptSubscription: Subscription; + + definedConfigComponent: IRuleNodeConfigurationComponent; + private definedConfigComponentRef: ComponentRef; - private definedConfigComponent: IRuleNodeConfigurationComponent; private configuration: RuleNodeConfiguration; @@ -127,6 +145,14 @@ export class RuleNodeConfigComponent implements ControlValueAccessor, OnInit, On if (this.definedConfigComponentRef) { this.definedConfigComponentRef.destroy(); } + if (this.changeSubscription) { + this.changeSubscription.unsubscribe(); + this.changeSubscription = null; + } + if (this.changeScriptSubscription) { + this.changeScriptSubscription.unsubscribe(); + this.changeScriptSubscription = null; + } } ngAfterViewInit(): void { @@ -139,6 +165,9 @@ export class RuleNodeConfigComponent implements ControlValueAccessor, OnInit, On } else { this.ruleNodeConfigFormGroup.enable({emitEvent: false}); } + if (this.definedConfigComponent) { + this.definedConfigComponent.disabled = this.disabled; + } } writeValue(value: RuleNodeConfiguration): void { @@ -199,6 +228,9 @@ export class RuleNodeConfigComponent implements ControlValueAccessor, OnInit, On this.changeSubscription = this.definedConfigComponent.configurationChanged.subscribe((configuration) => { this.updateModel(configuration); }); + if (this.definedConfigComponent?.changeScript) { + this.changeScriptSubscription = this.definedConfigComponent.changeScript.subscribe(() => this.changeScript.emit()); + } } } diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html index 35a564182c..cf798bf33b 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html @@ -22,42 +22,40 @@
-
-
-
- - rulenode.name - - + + rulenode.name + + - {{ 'rulenode.name-required' | translate }} - - - {{ 'rulenode.name-max-length' | translate }} - - -
- - {{ 'rulenode.debug-mode' | translate }} - - - {{ 'rulenode.singleton-mode' | translate }} - -
-
- - -
- - rulenode.description - - -
+ {{ 'rulenode.name-required' | translate }} + + + {{ 'rulenode.name-max-length' | translate }} + + +
+ + {{ 'rulenode.debug-mode' | translate }} + + + {{ 'rulenode.singleton-mode' | translate }} +
-
+ + + +
+ + rulenode.rule-node-description + + +
diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.scss b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.scss index 4995d5c06d..827f77b865 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.scss +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.scss @@ -27,16 +27,21 @@ display: flex; flex-direction: column; gap: 8px; - margin-bottom: 5px; + margin-bottom: 22px; } @media #{$mat-gt-sm} { flex-direction: row; gap: 8px; + align-items: center; .node-setting { margin-top: 5px; } } } + + .description-block { + margin-top: 22px; + } } diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.ts index dad1e715ec..5fce7f8e0b 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, Input, OnChanges, OnInit, SimpleChanges, ViewChild } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -22,11 +22,11 @@ import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms import { FcRuleNode, RuleNodeType } from '@shared/models/rule-node.models'; import { EntityType } from '@shared/models/entity-type.models'; import { Subscription } from 'rxjs'; -import { RuleChainService } from '@core/http/rule-chain.service'; import { RuleNodeConfigComponent } from './rule-node-config.component'; import { Router } from '@angular/router'; import { RuleChainType } from '@app/shared/models/rule-chain.models'; import { ComponentClusteringMode } from '@shared/models/component-descriptor.models'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-rule-node', @@ -47,14 +47,19 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O ruleChainType: RuleChainType; @Input() - isEdit: boolean; - - @Input() - isReadOnly: boolean; + @coerceBoolean() + disabled = false; @Input() + @coerceBoolean() isAdd = false; + @Output() + initRuleNode = new EventEmitter(); + + @Output() + changeScript = new EventEmitter(); + ruleNodeType = RuleNodeType; entityType = EntityType; @@ -64,7 +69,6 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O constructor(protected store: Store, private fb: UntypedFormBuilder, - private ruleChainService: RuleChainService, private router: Router) { super(store); this.ruleNodeFormGroup = this.fb.group({}); @@ -93,6 +97,9 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O } else { this.ruleNodeFormGroup = this.fb.group({}); } + if (this.disabled) { + this.ruleNodeFormGroup.disable({emitEvent: false}); + } } private updateRuleNode() { @@ -102,6 +109,9 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O } ngOnInit(): void { + if (this.disabled) { + this.ruleNodeFormGroup.disable({emitEvent: false}); + } } ngOnChanges(changes: SimpleChanges): void { diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html index 4cdaed942d..6911558ad4 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html @@ -46,12 +46,12 @@ matTooltipPosition="above"> search -
diff --git a/ui-ngx/src/app/modules/home/pages/widget/select-widget-type-dialog.component.scss b/ui-ngx/src/app/modules/home/pages/widget/select-widget-type-dialog.component.scss index 7de71586fa..d289574f15 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/select-widget-type-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/pages/widget/select-widget-type-dialog.component.scss @@ -21,7 +21,7 @@ display: flex; flex-direction: column; align-items: center; - mat-icon { + .mat-icon { margin: auto; } span.mdc-button__label { diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html index 2b650cb1ce..2251bd4022 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.html @@ -22,7 +22,7 @@ + placeholder="{{ 'widget.widget-title' | translate }}"/> - save + save_as action.saveAs @@ -240,11 +240,10 @@
- - + widget.description {{descriptionInput.value?.length || 0}}/255 + + + + {{ 'widget.deprecated' | translate }} + widget.settings-form-selector (); constructor(protected store: Store, @@ -181,14 +195,13 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe } private init(data: any) { - this.widgetsBundle = data.widgetsBundle; + this.widgetTypeDetails = data.widgetEditorData.widgetTypeDetails; + this.widget = data.widgetEditorData.widget; if (this.authUser.authority === Authority.TENANT_ADMIN) { - this.isReadOnly = !this.widgetsBundle || this.widgetsBundle.tenantId.id === NULL_UUID; + this.isReadOnly = this.widgetTypeDetails && this.widgetTypeDetails.tenantId.id === NULL_UUID; } else { this.isReadOnly = this.authUser.authority !== Authority.SYS_ADMIN; } - this.widgetTypeDetails = data.widgetEditorData.widgetTypeDetails; - this.widget = data.widgetEditorData.widget; if (this.widgetTypeDetails) { const config = JSON.parse(this.widget.defaultConfig); this.widget.defaultConfig = JSON.stringify(config); @@ -449,6 +462,10 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe break; case 'widgetEditUpdated': this.onWidgetEditUpdated(message.data); + this.onWidgetEditModeToggled(false); + break; + case 'widgetEditModeToggle': + this.onWidgetEditModeToggled(message.data); break; } } @@ -485,6 +502,10 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe this.isDirty = true; } + private onWidgetEditModeToggled(mode: boolean) { + this.isEditModeWidget = mode; + } + private onWidgetException(details: ExceptionData) { if (!this.gotError) { this.gotError = true; @@ -542,17 +563,32 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe private commitSaveWidget() { const id = (this.widgetTypeDetails && this.widgetTypeDetails.id) ? this.widgetTypeDetails.id : undefined; const createdTime = (this.widgetTypeDetails && this.widgetTypeDetails.createdTime) ? this.widgetTypeDetails.createdTime : undefined; - this.widgetService.saveWidgetTypeDetails(this.widget, id, this.widgetsBundle.alias, createdTime).subscribe( - (widgetTypeDetails) => { - this.setWidgetTypeDetails(widgetTypeDetails); + this.widgetService.saveWidgetTypeDetails(this.widget, id, createdTime).pipe( + mergeMap((widgetTypeDetails) => { + const widgetsBundleId = this.route.snapshot.params.widgetsBundleId as string; + if (widgetsBundleId && !id) { + return this.widgetService.addWidgetFqnToWidgetBundle(widgetsBundleId, widgetTypeDetails.fqn).pipe( + map(() => widgetTypeDetails) + ); + } + return of(widgetTypeDetails); + }) + ).subscribe({ + next: (widgetTypeDetails) => { this.saveWidgetPending = false; + if (!this.widgetTypeDetails?.id) { + this.isDirty = false; + this.router.navigate(['..', widgetTypeDetails.id.id], {relativeTo: this.route}); + } else { + this.setWidgetTypeDetails(widgetTypeDetails); + } this.store.dispatch(new ActionNotificationShow( {message: this.translate.instant('widget.widget-saved'), type: 'success', duration: 500})); }, - () => { + error: () => { this.saveWidgetPending = false; } - ); + }); } private commitSaveWidgetAs() { @@ -564,18 +600,38 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe (saveWidgetAsData) => { if (saveWidgetAsData) { this.widget.widgetName = saveWidgetAsData.widgetName; - this.widget.alias = undefined; + this.widget.fullFqn = undefined; const config = JSON.parse(this.widget.defaultConfig); config.title = this.widget.widgetName; this.widget.defaultConfig = JSON.stringify(config); this.isDirty = false; - this.widgetService.saveWidgetTypeDetails(this.widget, undefined, saveWidgetAsData.bundleAlias, undefined).subscribe( - (widgetTypeDetails) => { - this.router.navigateByUrl(`/widgets-bundles/${saveWidgetAsData.bundleId}/widgetTypes/${widgetTypeDetails.id.id}`); - } - ); + this.widgetService.saveWidgetTypeDetails(this.widget, undefined, undefined).pipe( + mergeMap((widget) => { + if (saveWidgetAsData.widgetBundleId) { + return this.widgetService.addWidgetFqnToWidgetBundle(saveWidgetAsData.widgetBundleId, widget.fqn).pipe( + map(() => widget) + ); + } + return of(widget); + }) + ).subscribe( + { + next: (widgetTypeDetails) => { + this.saveWidgetAsPending = false; + if (saveWidgetAsData.widgetBundleId) { + this.router.navigate(['resources', 'widgets-library', 'widgets-bundles', + saveWidgetAsData.widgetBundleId, widgetTypeDetails.id.id]); + } else { + this.router.navigate(['resources', 'widgets-library', 'widget-types', widgetTypeDetails.id.id]); + } + }, + error: () => { + this.saveWidgetAsPending = false; + } + }); + } else { + this.saveWidgetAsPending = false; } - this.saveWidgetAsPending = false; } ); } @@ -587,6 +643,7 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe this.widget.defaultConfig = JSON.stringify(config); this.origWidget = deepClone(this.widget); this.isDirty = false; + this.updateBreadcrumbs.emit(); } applyWidgetScript(): void { @@ -594,7 +651,9 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe this.gotError = false; this.iframeWidgetEditModeInited = false; const config: WidgetConfig = JSON.parse(this.widget.defaultConfig); - config.title = this.widget.widgetName; + if (!config.title) { + config.title = this.widget.widgetName; + } this.widget.defaultConfig = JSON.stringify(config); this.iframe.attr('data-widget', JSON.stringify(this.widget)); // @ts-ignore @@ -624,7 +683,7 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe } undoDisabled(): boolean { - return !this.isDirty + return !this._isDirty || !this.iframeWidgetEditModeInited || this.saveWidgetPending || this.saveWidgetAsPending; @@ -632,7 +691,7 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe saveDisabled(): boolean { return this.isReadOnly - || !this.isDirty + || !this._isDirty || !this.iframeWidgetEditModeInited || this.saveWidgetPending || this.saveWidgetAsPending; @@ -784,4 +843,11 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe this.widget.defaultConfig = JSON.stringify(config); this.isDirty = true; } + + get confirmOnExitMessage(): string { + if (this.isEditModeWidget && !this._isDirty) { + return this.translate.instant('widget.confirm-to-exit-editor-html'); + } + return ''; + } } diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts b/ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts index 368ce8b138..9b14558086 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-library-routing.module.ts @@ -14,95 +14,49 @@ /// limitations under the License. /// -import { Injectable, NgModule } from '@angular/core'; -import { ActivatedRouteSnapshot, Resolve, RouterModule, Routes } from '@angular/router'; +import { inject, NgModule } from '@angular/core'; +import { ActivatedRouteSnapshot, ResolveFn, RouterModule, Routes } from '@angular/router'; import { EntitiesTableComponent } from '../../components/entity/entities-table.component'; import { Authority } from '@shared/models/authority.enum'; import { WidgetsBundlesTableConfigResolver } from '@modules/home/pages/widget/widgets-bundles-table-config.resolver'; -import { WidgetLibraryComponent } from '@home/pages/widget/widget-library.component'; import { BreadCrumbConfig, BreadCrumbLabelFunction } from '@shared/components/breadcrumb'; -import { Observable } from 'rxjs'; import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; import { WidgetService } from '@core/http/widget.service'; import { WidgetEditorComponent } from '@home/pages/widget/widget-editor.component'; import { map } from 'rxjs/operators'; import { detailsToWidgetInfo, WidgetInfo } from '@home/models/widget-component.models'; -import { widgetType, WidgetTypeDetails } from '@app/shared/models/widget.models'; +import { widgetType, WidgetTypeDetails, WidgetTypeInfo } from '@app/shared/models/widget.models'; import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; -import { WidgetsData } from '@home/models/dashboard-component.models'; -import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { RouterTabsComponent } from '@home/components/router-tabs.component'; +import { WidgetTypesTableConfigResolver } from '@home/pages/widget/widget-types-table-config.resolver'; +import { WidgetsBundleWidgetsComponent } from '@home/pages/widget/widgets-bundle-widgets.component'; +import { EntityDetailsPageComponent } from '@home/components/entity/entity-details-page.component'; +import { entityDetailsPageBreadcrumbLabelFunction } from '@home/pages/home-pages.models'; export interface WidgetEditorData { widgetTypeDetails: WidgetTypeDetails; widget: WidgetInfo; } -@Injectable() -export class WidgetsBundleResolver implements Resolve { +const widgetsBundleResolver: ResolveFn = (route: ActivatedRouteSnapshot) => { + const widgetsBundleId = route.params.widgetsBundleId; + return inject(WidgetService).getWidgetsBundle(widgetsBundleId); +}; - constructor(private widgetsService: WidgetService) { - } - - resolve(route: ActivatedRouteSnapshot): Observable { - let widgetsBundleId = route.params.widgetsBundleId; - if (!widgetsBundleId) { - widgetsBundleId = route.parent.params.widgetsBundleId; - } - return this.widgetsService.getWidgetsBundle(widgetsBundleId); - } -} - -@Injectable() -export class WidgetsTypesDataResolver implements Resolve { - - constructor(private widgetsService: WidgetService) { - } +const widgetsBundleWidgetsResolver: ResolveFn> = (route: ActivatedRouteSnapshot) => { + const widgetsBundleId = route.params.widgetsBundleId; + return inject(WidgetService).getBundleWidgetTypeInfosList(widgetsBundleId); +}; - resolve(route: ActivatedRouteSnapshot): Observable { - const widgetsBundle: WidgetsBundle = route.parent.data.widgetsBundle; - const bundleAlias = widgetsBundle.alias; - const isSystem = widgetsBundle.tenantId.id === NULL_UUID; - return this.widgetsService.loadBundleLibraryWidgets(bundleAlias, - isSystem).pipe( - map((widgets) => { - return { widgets }; - } - )); - } -} - -@Injectable() -export class WidgetEditorDataResolver implements Resolve { - - constructor(private widgetsService: WidgetService) { - } - - resolve(route: ActivatedRouteSnapshot): Observable { - const widgetTypeId = route.params.widgetTypeId; - return this.widgetsService.getWidgetTypeById(widgetTypeId).pipe( - map((result) => { - return { - widgetTypeDetails: result, - widget: detailsToWidgetInfo(result) - }; - }) - ); - } -} - -@Injectable() -export class WidgetEditorAddDataResolver implements Resolve { - - constructor(private widgetsService: WidgetService) { - } - - resolve(route: ActivatedRouteSnapshot): Observable { - let widgetTypeParam = route.params.widgetType as widgetType; +const widgetEditorDataResolver: ResolveFn = (route: ActivatedRouteSnapshot) => { + const widgetTypeId = route.params.widgetTypeId; + if (!widgetTypeId || Object.keys(widgetType).includes(widgetTypeId)) { + let widgetTypeParam = widgetTypeId as widgetType; if (!widgetTypeParam) { widgetTypeParam = widgetType.timeseries; } - return this.widgetsService.getWidgetTemplate(widgetTypeParam).pipe( + return inject(WidgetService).getWidgetTemplate(widgetTypeParam).pipe( map((widget) => { widget.widgetName = null; return { @@ -111,21 +65,50 @@ export class WidgetEditorAddDataResolver implements Resolve { }; }) ); + } else { + return inject(WidgetService).getWidgetTypeById(widgetTypeId).pipe( + map((result) => ({ + widgetTypeDetails: result, + widget: detailsToWidgetInfo(result) + })) + ); } -} +}; -export const widgetTypesBreadcumbLabelFunction: BreadCrumbLabelFunction = ((route, translate) => +export const widgetsBundleWidgetsBreadcumbLabelFunction: BreadCrumbLabelFunction = ((route) => route.data.widgetsBundle.title); export const widgetEditorBreadcumbLabelFunction: BreadCrumbLabelFunction = - ((route, translate, component) => component ? component.widget.widgetName : ''); + ((route, translate, component) => + component?.widget?.widgetName ? + (component.widget.widgetName + (component.widget.deprecated ? ` (${translate.instant('widget.deprecated')})` : '')) : ''); -export const widgetsBundlesRoutes: Routes = [ +const widgetEditorRouter: Routes = [ { - path: 'widgets-bundles', + path: ':widgetTypeId', + component: WidgetEditorComponent, + canDeactivate: [ConfirmOnExitGuard], data: { + auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], + title: 'widget.editor', breadcrumb: { - label: 'widgets-bundle.widgets-bundles', + labelFunction: widgetEditorBreadcumbLabelFunction, + icon: 'insert_chart' + } as BreadCrumbConfig, + hideTabs: true + }, + resolve: { + widgetEditorData: widgetEditorDataResolver + } + } +]; + +const widgetTypesRoutes: Routes = [ + { + path: 'widget-types', + data: { + breadcrumb: { + label: 'widget.widgets', icon: 'now_widgets' } }, @@ -135,92 +118,180 @@ export const widgetsBundlesRoutes: Routes = [ component: EntitiesTableComponent, data: { auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], - title: 'widgets-bundle.widgets-bundles' + title: 'widget.widgets' }, resolve: { - entitiesTableConfig: WidgetsBundlesTableConfigResolver + entitiesTableConfig: WidgetTypesTableConfigResolver } }, { - path: ':widgetsBundleId/widgetTypes', - data: { - breadcrumb: { - labelFunction: widgetTypesBreadcumbLabelFunction, - icon: 'now_widgets' - } as BreadCrumbConfig - }, - resolve: { - widgetsBundle: WidgetsBundleResolver - }, + path: 'details', children: [ { - path: '', - component: WidgetLibraryComponent, + path: ':entityId', + component: EntityDetailsPageComponent, + canDeactivate: [ConfirmOnExitGuard], data: { + breadcrumb: { + labelFunction: entityDetailsPageBreadcrumbLabelFunction, + icon: 'now_widgets' + } as BreadCrumbConfig, auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], - title: 'widget.widget-library' + title: 'widget.widgets', + hideTabs: true, + backNavigationCommands: ['../..'] }, resolve: { - widgetsData: WidgetsTypesDataResolver + entitiesTableConfig: WidgetTypesTableConfigResolver } - }, + } + ] + }, + ...widgetEditorRouter + ] + }, +]; + +const widgetsBundlesRoutes: Routes = [ + { + path: 'widgets-bundles', + data: { + breadcrumb: { + label: 'widgets-bundle.widgets-bundles', + icon: 'now_widgets' + } + }, + children: [ + { + path: '', + component: EntitiesTableComponent, + data: { + auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], + title: 'widgets-bundle.widgets-bundles' + }, + resolve: { + entitiesTableConfig: WidgetsBundlesTableConfigResolver + } + }, + { + path: 'details', + children: [ { - path: ':widgetTypeId', - component: WidgetEditorComponent, + path: ':entityId', + component: EntityDetailsPageComponent, canDeactivate: [ConfirmOnExitGuard], data: { - auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], - title: 'widget.editor', breadcrumb: { - labelFunction: widgetEditorBreadcumbLabelFunction, - icon: 'insert_chart' - } as BreadCrumbConfig + labelFunction: entityDetailsPageBreadcrumbLabelFunction, + icon: 'now_widgets' + } as BreadCrumbConfig, + auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], + title: 'widgets-bundle.widgets-bundles', + hideTabs: true, + backNavigationCommands: ['../..'] }, resolve: { - widgetEditorData: WidgetEditorDataResolver + entitiesTableConfig: WidgetsBundlesTableConfigResolver } - }, + } + ] + }, + { + path: ':widgetsBundleId', + data: { + breadcrumb: { + labelFunction: widgetsBundleWidgetsBreadcumbLabelFunction, + icon: 'now_widgets' + } as BreadCrumbConfig, + }, + resolve: { + widgetsBundle: widgetsBundleResolver + }, + children: [ { - path: 'add/:widgetType', - component: WidgetEditorComponent, + path: '', + component: WidgetsBundleWidgetsComponent, canDeactivate: [ConfirmOnExitGuard], data: { auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], - title: 'widget.editor', - breadcrumb: { - labelFunction: widgetEditorBreadcumbLabelFunction, - icon: 'insert_chart' - } as BreadCrumbConfig + title: 'widgets-bundle.widgets-bundle-widgets', + hideTabs: true }, resolve: { - widgetEditorData: WidgetEditorAddDataResolver + widgets: widgetsBundleWidgetsResolver } - } + }, + ...widgetEditorRouter ] } ] }, ]; +export const widgetsLibraryRoutes: Routes = [ + { + path: 'widgets-library', + component: RouterTabsComponent, + data: { + auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], + breadcrumb: { + label: 'widget.widget-library', + icon: 'now_widgets' + } + }, + children: [ + { + path: '', + children: [], + data: { + auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], + redirectTo: '/resources/widgets-library/widget-types' + } + }, + ...widgetTypesRoutes, + ...widgetsBundlesRoutes + ] + } +]; + const routes: Routes = [ { path: 'widgets-bundles', pathMatch: 'full', - redirectTo: '/resources/widgets-bundles' + redirectTo: '/resources/widgets-library/widgets-bundles' + }, + { + path: 'resources/widgets-bundles', + pathMatch: 'full', + redirectTo: '/resources/widgets-library/widgets-bundles' }, { path: 'widgets-bundles/:widgetsBundleId/widgetTypes', pathMatch: 'full', - redirectTo: '/resources/widgets-bundles/:widgetsBundleId/widgetTypes', + redirectTo: '/resources/widgets-library/widgets-bundles/:widgetsBundleId' + }, + { + path: 'resources/widgets-bundles/:widgetsBundleId/widgetTypes', + pathMatch: 'full', + redirectTo: '/resources/widgets-library/widgets-bundles/:widgetsBundleId' }, { path: 'widgets-bundles/:widgetsBundleId/widgetTypes/:widgetTypeId', pathMatch: 'full', - redirectTo: '/resources/widgets-bundles/:widgetsBundleId/widgetTypes/:widgetTypeId', + redirectTo: '/resources/widgets-library/widget-types/:widgetTypeId' + }, + { + path: 'resources/widgets-bundles/:widgetsBundleId/widgetTypes/:widgetTypeId', + pathMatch: 'full', + redirectTo: '/resources/widgets-library/widget-types/:widgetTypeId' }, { path: 'widgets-bundles/:widgetsBundleId/widgetTypes/add/:widgetType', - redirectTo: '/resources/widgets-bundles/:widgetsBundleId/widgetTypes/add/:widgetType', + redirectTo: '/resources/widgets-library/widget-types/:widgetType', + }, + { + path: 'resources/widgets-bundles/:widgetsBundleId/widgetTypes/add/:widgetType', + redirectTo: '/resources/widgets-library/widget-types/:widgetType', } ]; @@ -229,11 +300,8 @@ const routes: Routes = [ imports: [RouterModule.forChild(routes)], exports: [RouterModule], providers: [ - WidgetsBundlesTableConfigResolver, - WidgetsBundleResolver, - WidgetsTypesDataResolver, - WidgetEditorDataResolver, - WidgetEditorAddDataResolver + WidgetTypesTableConfigResolver, + WidgetsBundlesTableConfigResolver ] }) export class WidgetLibraryRoutingModule { } diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-library.component.html b/ui-ngx/src/app/modules/home/pages/widget/widget-library.component.html deleted file mode 100644 index d6d7fb4869..0000000000 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-library.component.html +++ /dev/null @@ -1,42 +0,0 @@ - -
- - widgets-bundle.empty -
- - - diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-library.component.scss b/ui-ngx/src/app/modules/home/pages/widget/widget-library.component.scss deleted file mode 100644 index 8fcd595570..0000000000 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-library.component.scss +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright © 2016-2023 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -:host { - button.tb-add-new-widget { - height: auto; - padding-right: 12px; - font-size: 24px; - border-style: dashed; - border-width: 2px; - } -} - -:host ::ng-deep { - .tb-widget-library { - .tb-widget-container { - cursor: pointer; - } - } -} diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-library.component.ts b/ui-ngx/src/app/modules/home/pages/widget/widget-library.component.ts deleted file mode 100644 index 970fa8ebe6..0000000000 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-library.component.ts +++ /dev/null @@ -1,208 +0,0 @@ -/// -/// Copyright © 2016-2023 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -import { Component, OnInit, ViewChild } from '@angular/core'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { PageComponent } from '@shared/components/page.component'; -import { AuthUser } from '@shared/models/user.model'; -import { getCurrentAuthUser } from '@core/auth/auth.selectors'; -import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; -import { ActivatedRoute, Router } from '@angular/router'; -import { Authority } from '@shared/models/authority.enum'; -import { NULL_UUID } from '@shared/models/id/has-uuid'; -import { of } from 'rxjs'; -import { Widget, widgetType } from '@app/shared/models/widget.models'; -import { WidgetService } from '@core/http/widget.service'; -import { map, mergeMap } from 'rxjs/operators'; -import { DialogService } from '@core/services/dialog.service'; -import { FooterFabButtons } from '@app/shared/components/footer-fab-buttons.component'; -import { DashboardCallbacks, IDashboardComponent, WidgetsData } from '@home/models/dashboard-component.models'; -import { IAliasController, IStateController, StateParams } from '@app/core/api/widget-api.models'; -import { AliasController } from '@core/api/alias-controller'; -import { MatDialog } from '@angular/material/dialog'; -import { SelectWidgetTypeDialogComponent } from '@home/pages/widget/select-widget-type-dialog.component'; -import { TranslateService } from '@ngx-translate/core'; -import { ImportExportService } from '@home/components/import-export/import-export.service'; -import { UtilsService } from '@core/services/utils.service'; -import { EntityService } from '@core/http/entity.service'; - -@Component({ - selector: 'tb-widget-library', - templateUrl: './widget-library.component.html', - styleUrls: ['./widget-library.component.scss'] -}) -export class WidgetLibraryComponent extends PageComponent implements OnInit { - - authUser: AuthUser; - - isReadOnly: boolean; - - widgetsBundle: WidgetsBundle; - - widgetsData: WidgetsData; - - footerFabButtons: FooterFabButtons = { - fabTogglerName: 'widget.add-widget-type', - fabTogglerIcon: 'add', - buttons: [ - { - name: 'widget-type.create-new-widget-type', - icon: 'insert_drive_file', - onAction: ($event) => { - this.addWidgetType($event); - } - }, - { - name: 'widget-type.import', - icon: 'file_upload', - onAction: ($event) => { - this.importWidgetType($event); - } - } - ] - }; - - dashboardCallbacks: DashboardCallbacks = { - onEditWidget: this.openWidgetType.bind(this), - onWidgetClicked: this.openWidgetType.bind(this), - onExportWidget: this.exportWidgetType.bind(this), - onRemoveWidget: this.removeWidgetType.bind(this) - }; - - aliasController: IAliasController = new AliasController(this.utils, - this.entityService, - this.translate, - () => { return { - getStateParams(): StateParams { - return {}; - } - } as IStateController; - }, - {}, - {}); - - @ViewChild('dashboard', {static: true}) dashboard: IDashboardComponent; - - constructor(protected store: Store, - private route: ActivatedRoute, - private router: Router, - private widgetService: WidgetService, - private dialogService: DialogService, - private importExport: ImportExportService, - private dialog: MatDialog, - private translate: TranslateService, - private utils: UtilsService, - private entityService: EntityService) { - super(store); - - this.authUser = getCurrentAuthUser(this.store); - this.widgetsBundle = this.route.snapshot.data.widgetsBundle; - this.widgetsData = this.route.snapshot.data.widgetsData; - if (this.authUser.authority === Authority.TENANT_ADMIN) { - this.isReadOnly = !this.widgetsBundle || this.widgetsBundle.tenantId.id === NULL_UUID; - } else { - this.isReadOnly = this.authUser.authority !== Authority.SYS_ADMIN; - } - } - - ngOnInit(): void { - } - - addWidgetType($event: Event): void { - this.openWidgetType($event); - } - - importWidgetType($event: Event): void { - if ($event) { - $event.stopPropagation(); - } - this.importExport.importWidgetType(this.widgetsBundle.alias).subscribe( - (widgetTypeInstance) => { - if (widgetTypeInstance) { - this.reload(); - } - } - ); - } - - private reload() { - const bundleAlias = this.widgetsBundle.alias; - const isSystem = this.widgetsBundle.tenantId.id === NULL_UUID; - this.widgetService.loadBundleLibraryWidgets(bundleAlias, isSystem).subscribe( - (widgets) => { - this.widgetsData = {widgets}; - } - ); - } - - openWidgetType($event: Event, widget?: Widget): void { - if ($event) { - $event.stopPropagation(); - } - if (widget) { - this.router.navigate([widget.typeId.id], {relativeTo: this.route}); - } else { - this.dialog.open(SelectWidgetTypeDialogComponent, { - disableClose: true, - panelClass: ['tb-dialog', 'tb-fullscreen-dialog'] - }).afterClosed().subscribe( - (type) => { - if (type) { - this.router.navigate(['add', type], {relativeTo: this.route}); - } - } - ); - } - } - - exportWidgetType($event: Event, widget: Widget): void { - if ($event) { - $event.stopPropagation(); - } - this.importExport.exportWidgetType(widget.typeId.id); - } - - removeWidgetType($event: Event, widget: Widget): void { - if ($event) { - $event.stopPropagation(); - } - this.dialogService.confirm( - this.translate.instant('widget.remove-widget-type-title', {widgetName: widget.config.title}), - this.translate.instant('widget.remove-widget-type-text'), - this.translate.instant('action.no'), - this.translate.instant('action.yes'), - ).pipe( - mergeMap((result) => { - if (result) { - return this.widgetService.deleteWidgetType(widget.bundleAlias, widget.typeAlias, widget.isSystemType); - } else { - return of(false); - } - }), - map((result) => { - if (result !== false) { - this.reload(); - return true; - } else { - return false; - } - } - )).subscribe(); - } - -} diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-library.module.ts b/ui-ngx/src/app/modules/home/pages/widget/widget-library.module.ts index 6a6e4a47e0..32178c1be3 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-library.module.ts +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-library.module.ts @@ -20,19 +20,25 @@ import { SharedModule } from '@shared/shared.module'; import { WidgetsBundleComponent } from '@modules/home/pages/widget/widgets-bundle.component'; import { WidgetLibraryRoutingModule } from '@modules/home/pages/widget/widget-library-routing.module'; import { HomeComponentsModule } from '@modules/home/components/home-components.module'; -import { WidgetLibraryComponent } from './widget-library.component'; import { WidgetEditorComponent } from '@home/pages/widget/widget-editor.component'; import { SelectWidgetTypeDialogComponent } from '@home/pages/widget/select-widget-type-dialog.component'; import { SaveWidgetTypeAsDialogComponent } from './save-widget-type-as-dialog.component'; import { WidgetsBundleTabsComponent } from '@home/pages/widget/widgets-bundle-tabs.component'; +import { WidgetTypeComponent } from '@home/pages/widget/widget-type.component'; +import { WidgetTypeTabsComponent } from '@home/pages/widget/widget-type-tabs.component'; +import { WidgetsBundleWidgetsComponent } from '@home/pages/widget/widgets-bundle-widgets.component'; +import { WidgetTypeAutocompleteComponent } from '@home/pages/widget/widget-type-autocomplete.component'; @NgModule({ declarations: [ + WidgetTypeComponent, WidgetsBundleComponent, - WidgetLibraryComponent, + WidgetTypeAutocompleteComponent, + WidgetsBundleWidgetsComponent, WidgetEditorComponent, SelectWidgetTypeDialogComponent, SaveWidgetTypeAsDialogComponent, + WidgetTypeTabsComponent, WidgetsBundleTabsComponent ], imports: [ diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-type-autocomplete.component.html b/ui-ngx/src/app/modules/home/pages/widget/widget-type-autocomplete.component.html new file mode 100644 index 0000000000..4c8087f39b --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-type-autocomplete.component.html @@ -0,0 +1,59 @@ + + + {{ label }} + + + + +
+ {{ widgetType.name }} +
+
+
{{ 'widget.deprecated' | translate }}
+
+
+
+ + + {{ translate.get('widget.no-widgets-matching', {entity: searchText}) | async }} + + +
+ + + + + + +
diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-type-autocomplete.component.scss b/ui-ngx/src/app/modules/home/pages/widget/widget-type-autocomplete.component.scss new file mode 100644 index 0000000000..79a5bc5c80 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-type-autocomplete.component.scss @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.tb-widget-type-autocomplete { + .mat-mdc-option { + .mdc-list-item__primary-text { + width: 100%; + .tb-widget-type-option-container { + display: flex; + gap: 8px; + align-items: center; + } + .tb-widget-type-option-image-preview { + width: 36px; + max-height: 100%; + object-fit: contain; + border-radius: 6px; + } + .tb-widget-type-option-details { + flex: 1; + display: flex; + align-items: center; + justify-content: space-between; + .tb-widget-type-option-text { + font-size: 14px; + font-weight: 400; + letter-spacing: 0.2px; + } + .tb-widget-type-option-deprecated { + font-size: 13px; + font-weight: 400; + color: rgba(0, 0, 0, 0.54); + } + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-type-autocomplete.component.ts b/ui-ngx/src/app/modules/home/pages/widget/widget-type-autocomplete.component.ts new file mode 100644 index 0000000000..1bb5fa0d17 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-type-autocomplete.component.ts @@ -0,0 +1,227 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + AfterViewInit, + Component, + ElementRef, + forwardRef, + Input, + OnInit, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Observable, of } from 'rxjs'; +import { PageLink } from '@shared/models/page/page-link'; +import { Direction } from '@shared/models/page/sort-order'; +import { catchError, debounceTime, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators'; +import { emptyPageData } from '@shared/models/page/page-data'; +import { Store } from '@ngrx/store'; +import { AppState } from '@app/core/core.state'; +import { TranslateService } from '@ngx-translate/core'; +import { FloatLabelType, MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field'; +import { WidgetTypeInfo } from '@shared/models/widget.models'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { WidgetService } from '@core/http/widget.service'; +import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; +import { isDefinedAndNotNull } from '@core/utils'; + +@Component({ + selector: 'tb-widget-type-autocomplete', + templateUrl: './widget-type-autocomplete.component.html', + styleUrls: ['./widget-type-autocomplete.component.scss'], + providers: [{ + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => WidgetTypeAutocompleteComponent), + multi: true + }], + encapsulation: ViewEncapsulation.None +}) +export class WidgetTypeAutocompleteComponent implements ControlValueAccessor, OnInit, AfterViewInit { + + private dirty = false; + + selectWidgetTypeFormGroup: UntypedFormGroup; + + modelValue: WidgetTypeInfo | null; + + @Input() + label = this.translate.instant('widget.widget'); + + @Input() + placeholder: string; + + @Input() + floatLabel: FloatLabelType = 'auto'; + + @Input() + appearance: MatFormFieldAppearance = 'fill'; + + @Input() + subscriptSizing: SubscriptSizing = 'fixed'; + + @Input() + @coerceBoolean() + required: boolean; + + @Input() + disabled: boolean; + + @Input() + excludeWidgetTypeIds: Array; + + @ViewChild('widgetTypeInput', {static: true}) widgetTypeInput: ElementRef; + + filteredWidgetTypes: Observable>; + + searchText = ''; + + private propagateChange = (v: any) => { }; + + constructor(private store: Store, + public translate: TranslateService, + private widgetService: WidgetService, + private sanitizer: DomSanitizer, + private fb: UntypedFormBuilder) { + this.selectWidgetTypeFormGroup = this.fb.group({ + widgetType: [null] + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + ngOnInit() { + this.filteredWidgetTypes = this.selectWidgetTypeFormGroup.get('widgetType').valueChanges + .pipe( + debounceTime(150), + tap(value => { + let modelValue; + if (typeof value === 'string' || !value) { + modelValue = null; + } else { + modelValue = value; + } + this.updateView(modelValue); + }), + map(value => value ? (typeof value === 'string' ? value : value.name) : ''), + distinctUntilChanged(), + switchMap(name => this.fetchWidgetTypes(name) ), + share() + ); + } + + ngAfterViewInit(): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.selectWidgetTypeFormGroup.disable({emitEvent: false}); + } else { + this.selectWidgetTypeFormGroup.enable({emitEvent: false}); + } + } + + writeValue(value: WidgetTypeInfo | string | null): void { + this.searchText = ''; + if (value != null) { + if (typeof value === 'string') { + this.widgetService.getWidgetTypeInfoById(value).subscribe( + (widgetType) => { + this.modelValue = widgetType; + this.selectWidgetTypeFormGroup.get('widgetType').patchValue(widgetType, {emitEvent: false}); + } + ); + } else { + this.modelValue = value; + this.selectWidgetTypeFormGroup.get('widgetType').patchValue(value, {emitEvent: false}); + } + } else { + this.modelValue = null; + this.selectWidgetTypeFormGroup.get('widgetType').patchValue('', {emitEvent: false}); + } + this.dirty = true; + } + + updateView(value: WidgetTypeInfo | null) { + if (this.modelValue !== value) { + this.modelValue = value; + this.propagateChange(this.modelValue); + } + } + + displayWidgetTypeFn(widgetType?: WidgetTypeInfo): string | undefined { + return widgetType ? widgetType.name : undefined; + } + + getPreviewImage(imageUrl: string | null): SafeUrl | string { + if (isDefinedAndNotNull(imageUrl)) { + return this.sanitizer.bypassSecurityTrustUrl(imageUrl); + } + return '/assets/widget-preview-empty.svg'; + } + + fetchWidgetTypes(searchText?: string): Observable> { + this.searchText = searchText; + const pageLink = new PageLink(10, 0, searchText, { + property: 'name', + direction: Direction.ASC + }); + return this.getWidgetTypes(pageLink); + } + + getWidgetTypes(pageLink: PageLink, + result: Array = []): Observable> { + return this.widgetService.getWidgetTypes(pageLink, true).pipe( + catchError(() => of(emptyPageData())), + switchMap((data) => { + if (this.excludeWidgetTypeIds?.length) { + const filtered = data.data.filter(w => !this.excludeWidgetTypeIds.includes(w.id.id)); + result = result.concat(filtered); + } else { + result = data.data; + } + if (result.length >= pageLink.pageSize || !this.excludeWidgetTypeIds?.length || !data.hasNext) { + return of(result); + } else { + return this.getWidgetTypes(pageLink.nextPageLink(), result); + } + }) + ); + } + + onFocus() { + if (this.dirty) { + this.selectWidgetTypeFormGroup.get('widgetType').updateValueAndValidity({onlySelf: true}); + this.dirty = false; + } + } + + clear() { + this.selectWidgetTypeFormGroup.get('widgetType').patchValue(''); + setTimeout(() => { + this.widgetTypeInput.nativeElement.blur(); + this.widgetTypeInput.nativeElement.focus(); + }, 0); + } + +} diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-type-tabs.component.html b/ui-ngx/src/app/modules/home/pages/widget/widget-type-tabs.component.html new file mode 100644 index 0000000000..b6a6ecc32b --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-type-tabs.component.html @@ -0,0 +1,23 @@ + + + + diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-type-tabs.component.ts b/ui-ngx/src/app/modules/home/pages/widget/widget-type-tabs.component.ts new file mode 100644 index 0000000000..6543777295 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-type-tabs.component.ts @@ -0,0 +1,43 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { EntityTabsComponent } from '../../components/entity/entity-tabs.component'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { WidgetTypeDetails } from '@shared/models/widget.models'; + +@Component({ + selector: 'tb-widget-type-tabs', + templateUrl: './widget-type-tabs.component.html', + styleUrls: [] +}) +export class WidgetTypeTabsComponent extends EntityTabsComponent { + + constructor(protected store: Store) { + super(store); + } + + isTenantWidgetType() { + return this.entity && this.entity.tenantId.id !== NULL_UUID; + } + + ngOnInit() { + super.ngOnInit(); + } + +} diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-type.component.html b/ui-ngx/src/app/modules/home/pages/widget/widget-type.component.html new file mode 100644 index 0000000000..e3ce596c67 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-type.component.html @@ -0,0 +1,75 @@ + +
+ + + + +
+
+ +
+ + widget.title + + + {{ 'widget.title-required' | translate }} + + + {{ 'widget.title-max-length' | translate }} + + + + + + widget.description + + {{descriptionInput.value?.length || 0}}/1024 + + + + + {{ 'widget.deprecated' | translate }} + +
+ +
diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-type.component.ts b/ui-ngx/src/app/modules/home/pages/widget/widget-type.component.ts new file mode 100644 index 0000000000..e6354945d7 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-type.component.ts @@ -0,0 +1,70 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ChangeDetectorRef, Component, Inject } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { EntityComponent } from '../../components/entity/entity.component'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; +import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; +import { WidgetTypeDetails } from '@shared/models/widget.models'; + +@Component({ + selector: 'tb-widget-type', + templateUrl: './widget-type.component.html', + styleUrls: [] +}) +export class WidgetTypeComponent extends EntityComponent { + + constructor(protected store: Store, + @Inject('entity') protected entityValue: WidgetTypeDetails, + @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig, + public fb: UntypedFormBuilder, + protected cd: ChangeDetectorRef) { + super(store, fb, entityValue, entitiesTableConfigValue, cd); + } + + hideDelete() { + if (this.entitiesTableConfig) { + return !this.entitiesTableConfig.deleteEnabled(this.entity); + } else { + return false; + } + } + + buildForm(entity: WidgetTypeDetails): UntypedFormGroup { + return this.fb.group( + { + name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]], + image: [entity ? entity.image : ''], + description: [entity ? entity.description : '', Validators.maxLength(1024)], + tags: [entity ? entity.tags : []], + deprecated: [entity ? entity.deprecated : false] + } + ); + } + + updateForm(entity: WidgetTypeDetails) { + this.entityForm.patchValue({ + name: entity.name, + image: entity.image, + description: entity.description, + tags: entity.tags, + deprecated: entity.deprecated + }); + } +} diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-types-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/widget/widget-types-table-config.resolver.ts new file mode 100644 index 0000000000..8086e7f646 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-types-table-config.resolver.ts @@ -0,0 +1,241 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Injectable } from '@angular/core'; + +import { Resolve, Router } from '@angular/router'; +import { + checkBoxCell, + DateEntityTableColumn, + EntityTableColumn, + EntityTableConfig +} from '@home/models/entity/entities-table-config.models'; +import { TranslateService } from '@ngx-translate/core'; +import { DatePipe } from '@angular/common'; +import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; +import { EntityAction } from '@home/models/entity/entity-component.models'; +import { WidgetService } from '@app/core/http/widget.service'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { getCurrentAuthUser } from '@app/core/auth/auth.selectors'; +import { Authority } from '@shared/models/authority.enum'; +import { ImportExportService } from '@shared/import-export/import-export.service'; +import { Direction } from '@shared/models/page/sort-order'; +import { + BaseWidgetType, + widgetType as WidgetDataType, + WidgetTypeDetails, + WidgetTypeInfo, + widgetTypesData +} from '@shared/models/widget.models'; +import { WidgetTypeComponent } from '@home/pages/widget/widget-type.component'; +import { WidgetTypeTabsComponent } from '@home/pages/widget/widget-type-tabs.component'; +import { SelectWidgetTypeDialogComponent } from '@home/pages/widget/select-widget-type-dialog.component'; +import { MatDialog } from '@angular/material/dialog'; + +@Injectable() +export class WidgetTypesTableConfigResolver implements Resolve> { + + private readonly config: EntityTableConfig = + new EntityTableConfig(); + + constructor(private store: Store, + private dialog: MatDialog, + private widgetsService: WidgetService, + private translate: TranslateService, + private importExport: ImportExportService, + private datePipe: DatePipe, + private router: Router) { + + this.config.entityType = EntityType.WIDGETS_BUNDLE; + this.config.entityComponent = WidgetTypeComponent; + this.config.entityTabsComponent = WidgetTypeTabsComponent; + this.config.entityTranslations = entityTypeTranslations.get(EntityType.WIDGET_TYPE); + this.config.entityResources = entityTypeResources.get(EntityType.WIDGET_TYPE); + this.config.defaultSortOrder = {property: 'name', direction: Direction.ASC}; + + this.config.rowPointer = true; + + this.config.entityTitle = (widgetType) => widgetType ? + widgetType.name : ''; + + this.config.columns.push( + new DateEntityTableColumn('createdTime', 'common.created-time', this.datePipe, '150px'), + new EntityTableColumn('name', 'widget.title', '100%'), + new EntityTableColumn('widgetType', 'widget.type', '150px', entity => + entity?.widgetType ? this.translate.instant(widgetTypesData.get(entity.widgetType).name) : '', undefined, false), + new EntityTableColumn('tenantId', 'widget.system', '60px', + entity => checkBoxCell(entity.tenantId.id === NULL_UUID)), + new EntityTableColumn('deprecated', 'widget.deprecated', '60px', + entity => checkBoxCell(entity.deprecated)) + ); + + this.config.addActionDescriptors.push( + { + name: this.translate.instant('dashboard.create-new-widget'), + icon: 'insert_drive_file', + isEnabled: () => true, + onAction: ($event) => this.addWidgetType($event) + }, + { + name: this.translate.instant('widget.import'), + icon: 'file_upload', + isEnabled: () => true, + onAction: ($event) => this.importWidgetType($event) + } + ); + + this.config.cellActionDescriptors.push( + { + name: this.translate.instant('widget.export'), + icon: 'file_download', + isEnabled: () => true, + onAction: ($event, entity) => this.exportWidgetType($event, entity) + }, + { + name: this.translate.instant('widget.widget-details'), + icon: 'edit', + isEnabled: () => true, + onAction: ($event, entity) => this.config.toggleEntityDetails($event, entity) + } + ); + + this.config.groupActionDescriptors.push( + { + name: this.translate.instant('widget.export-widgets'), + icon: 'file_download', + isEnabled: true, + onAction: ($event, entities) => this.exportWidgetTypes($event, entities) + } + ); + + this.config.deleteEntityTitle = widgetType => this.translate.instant('widget.delete-widget-title', + { widgetName: widgetType.name }); + this.config.deleteEntityContent = () => this.translate.instant('widget.delete-widget-text'); + this.config.deleteEntitiesTitle = count => this.translate.instant('widget.delete-widgets-title', {count}); + this.config.deleteEntitiesContent = () => this.translate.instant('widget.delete-widgets-text'); + + + this.config.loadEntity = id => this.widgetsService.getWidgetTypeById(id.id); + this.config.saveEntity = widgetType => this.widgetsService.saveWidgetType(widgetType as WidgetTypeDetails); + this.config.deleteEntity = id => this.widgetsService.deleteWidgetType(id.id); + this.config.onEntityAction = action => this.onWidgetTypeAction(action, this.config); + + this.config.handleRowClick = ($event, widgetType) => { + if (this.config.isDetailsOpen()) { + this.config.toggleEntityDetails($event, widgetType); + } else { + this.openWidgetEditor($event, widgetType); + } + return true; + }; + } + + resolve(): EntityTableConfig { + this.config.tableTitle = this.translate.instant('widget.widgets'); + const authUser = getCurrentAuthUser(this.store); + this.config.deleteEnabled = (widgetType) => this.isWidgetTypeEditable(widgetType, authUser.authority); + this.config.entitySelectionEnabled = (widgetType) => this.isWidgetTypeEditable(widgetType, authUser.authority); + this.config.detailsReadonly = (widgetType) => !this.isWidgetTypeEditable(widgetType, authUser.authority); + this.config.entitiesFetchFunction = pageLink => this.widgetsService.getWidgetTypes(pageLink); + return this.config; + } + + isWidgetTypeEditable(widgetType: BaseWidgetType, authority: Authority): boolean { + if (authority === Authority.TENANT_ADMIN) { + return widgetType && widgetType.tenantId && widgetType.tenantId.id !== NULL_UUID; + } else { + return authority === Authority.SYS_ADMIN; + } + } + + addWidgetType($event: Event): void { + if ($event) { + $event.stopPropagation(); + } + this.dialog.open(SelectWidgetTypeDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'] + }).afterClosed().subscribe( + (type) => { + if (type) { + this.router.navigateByUrl(`resources/widgets-library/widget-types/${type}`); + } + } + ); + } + + importWidgetType($event: Event) { + this.importExport.importWidgetType().subscribe( + (widgetType) => { + if (widgetType) { + this.config.updateData(); + } + } + ); + } + + openWidgetEditor($event: Event, widgetType: BaseWidgetType) { + if ($event) { + $event.stopPropagation(); + } + this.router.navigateByUrl(`resources/widgets-library/widget-types/${widgetType.id.id}`); + } + + private openWidgetTypeDetails($event: Event, widgetType: BaseWidgetType, config: EntityTableConfig) { + if ($event) { + $event.stopPropagation(); + } + const url = this.router.createUrlTree(['details', widgetType.id.id], {relativeTo: config.getActivatedRoute()}); + this.router.navigateByUrl(url); + } + + exportWidgetType($event: Event, widgetType: BaseWidgetType) { + if ($event) { + $event.stopPropagation(); + } + this.importExport.exportWidgetType(widgetType.id.id); + } + + exportWidgetTypes($event: Event, widgetTypes: BaseWidgetType[]) { + if ($event) { + $event.stopPropagation(); + } + this.importExport.exportWidgetTypes(widgetTypes.map(w => w.id.id)).subscribe( + () => { + this.config.getTable().clearSelection(); + } + ); + } + + onWidgetTypeAction(action: EntityAction, config: EntityTableConfig): boolean { + switch (action.action) { + case 'edit': + this.openWidgetEditor(action.event, action.entity); + return true; + case 'openDetails': + this.openWidgetTypeDetails(action.event, action.entity, config); + return true; + case 'export': + this.exportWidgetType(action.event, action.entity); + return true; + } + return false; + } + +} diff --git a/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.html b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.html new file mode 100644 index 0000000000..f9f4990d21 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.html @@ -0,0 +1,148 @@ + + + + +
+ + {{ widgetsBundle.title }}: {{ 'widget.widgets' | translate }} + + + + + + + +
+
+
+ + +
+ +
+
+
+ {{ widget.name }} +
+
{{ widget.name }}
+
{{ 'widget.deprecated' | translate }}
+
+
+ + + + +
+
+
+ add +
+
+
+ add +
widget.add-existing-widget
+
+
+
+
+
widget.no-widgets
+
+
+ + +
+ +
+
+
+ + + + + +
+ diff --git a/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.scss b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.scss new file mode 100644 index 0000000000..11c611c739 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.scss @@ -0,0 +1,185 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import "../../../../../scss/constants"; + +:host { + width: 100%; + height: 100%; + padding: 8px; +} + +:host ::ng-deep { + .tb-bundle-widgets-card { + .mat-mdc-card-header-text { + flex: 1; + } + } +} + +.tb-bundle-widgets-card { + padding: 0; + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + + @media #{$mat-md} { + width: 80%; + } + + @media #{$mat-gt-md} { + width: 60%; + } + + .title-container { + display: flex; + flex-direction: row; + align-items: center; + gap: 8px; + .mat-headline-5 { + margin: 0; + } + } + + .mat-mdc-card-content { + flex: 1; + padding: 16px; + min-height: 0; + overflow: hidden; + display: flex; + flex-direction: column; + gap: 8px; + } + + .tb-add-widget-button { + min-height: 56px; + cursor: pointer; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background: #FFFFFF; + padding: 8px; + border: 2px dashed rgba(0, 0, 0, 0.08); + border-radius: 10px; + } + + .tb-add-widget-button-panel { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + .tb-add-widget-button-with-text { + cursor: pointer; + display: flex; + padding: 24px 40px 24px 24px; + justify-content: center; + align-items: center; + gap: 8px; + border: 2px dashed rgba(0, 0, 0, 0.12); + border-radius: 10px; + .tb-add-widget-button-text { + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: 24px; + letter-spacing: 0.25px; + color: $tb-primary-color; + } + } + } + + .tb-add-bundle-widget-form { + display: flex; + padding: 16px; + flex-direction: column; + gap: 12px; + border-radius: 10px; + border: 1px solid rgba(0, 0, 0, 0.05); + box-shadow: 0 5px 16px 0 rgba(0, 0, 0, 0.04); + } + + .tb-add-bundle-widget-actions { + display: flex; + justify-content: flex-end; + align-items: flex-start; + gap: 16px; + } + + .mat-mdc-card-actions { + justify-content: end; + gap: 8px; + } +} + +.tb-bundle-widgets-container { + display: flex; + flex-direction: column; + gap: 8px; + min-height: 0; + overflow: auto; + padding-top: 8px; + padding-bottom: 8px; +} + +.tb-bundle-widget-row { + display: flex; + flex-direction: row; + align-items: center; + padding: 6px 12px 6px 6px; + background: #fff; + gap: 12px; + border-radius: 10px; + border: 1px solid rgba(0, 0, 0, 0.05); + box-shadow: 0 5px 16px 0 rgba(0, 0, 0, 0.04); + .tb-bundle-widget-row-details { + flex: 1; + display: flex; + flex-direction: row; + align-items: center; + gap: 12px; + .tb-bundle-widget-image-preview { + width: 64px; + max-height: 52px; + height: 100%; + object-fit: contain; + border-radius: 6px; + } + .tb-bundle-widget-details { + padding: 18px 0; + flex: 1; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + .tb-bundle-widget-title { + font-size: 14px; + font-weight: 400; + letter-spacing: 0.2px; + color: rgba(0, 0, 0, 0.76); + } + .tb-bundle-widget-deprecated { + font-size: 13px; + font-weight: 400; + color: rgba(0, 0, 0, 0.54); + } + } + } + .mat-icon { + color: rgba(0, 0, 0, 0.54); + } +} diff --git a/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.ts b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.ts new file mode 100644 index 0000000000..105c18c123 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle-widgets.component.ts @@ -0,0 +1,214 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { AuthUser } from '@shared/models/user.model'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { ActivatedRoute, Router } from '@angular/router'; +import { getCurrentAuthUser } from '@core/auth/auth.selectors'; +import { Authority } from '@shared/models/authority.enum'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; +import { widgetType as WidgetDataType, WidgetTypeInfo } from '@shared/models/widget.models'; +import { CdkDragDrop } from '@angular/cdk/drag-drop'; +import { ImportExportService } from '@shared/import-export/import-export.service'; +import { WidgetService } from '@core/http/widget.service'; +import { FormControl, Validators } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { SelectWidgetTypeDialogComponent } from '@home/pages/widget/select-widget-type-dialog.component'; + +type WidgetTypeBundle = WithOptional; + +@Component({ + selector: 'tb-widgets-bundle-widget', + templateUrl: './widgets-bundle-widgets.component.html', + styleUrls: ['./widgets-bundle-widgets.component.scss'] +}) +export class WidgetsBundleWidgetsComponent extends PageComponent implements OnInit { + + authUser: AuthUser; + + isReadOnly: boolean; + editMode = false; + addMode = false; + isDirty = false; + + widgetsBundle: WidgetsBundle; + widgets: Array; + excludeWidgetTypeIds: Array; + + addWidgetFormControl = new FormControl(null, [Validators.required]); + + constructor(protected store: Store, + private router: Router, + private route: ActivatedRoute, + private widgetsService: WidgetService, + private importExport: ImportExportService, + private cd: ChangeDetectorRef, + private dialog: MatDialog) { + super(store); + this.authUser = getCurrentAuthUser(this.store); + this.widgetsBundle = this.route.snapshot.data.widgetsBundle; + this.widgets = [...this.route.snapshot.data.widgets]; + if (this.authUser.authority === Authority.TENANT_ADMIN) { + this.isReadOnly = !this.widgetsBundle || this.widgetsBundle.tenantId.id === NULL_UUID; + } else { + this.isReadOnly = this.authUser.authority !== Authority.SYS_ADMIN; + } + if (!this.isReadOnly && !this.widgets.length) { + this.editMode = true; + } + this.addWidgetFormControl.valueChanges.subscribe((newWidget) => { + if (newWidget) { + this.addWidget(newWidget); + } + }); + } + + ngOnInit(): void { + } + + trackByWidget(index: number, widget: WidgetTypeBundle): any { + return widget; + } + + widgetDrop(event: CdkDragDrop) { + const widget = this.widgets[event.previousIndex]; + this.widgets.splice(event.previousIndex, 1); + this.widgets.splice(event.currentIndex, 0, widget); + this.isDirty = true; + } + + addWidgetMode() { + this.addWidgetFormControl.patchValue(null, {emitEvent: false}); + this.excludeWidgetTypeIds = this.widgets.map(w => w.id.id); + this.addMode = true; + } + + cancelAddWidgetMode() { + this.addMode = false; + } + + private addWidget(newWidget: WidgetTypeBundle) { + this.widgets.push(newWidget); + this.isDirty = true; + this.addMode = false; + } + + openWidgetEditor($event: Event, widgetType: WidgetTypeBundle) { + if ($event) { + $event.stopPropagation(); + } + this.router.navigate([widgetType.id.id], {relativeTo: this.route}).then(()=> {}); + } + + exportWidgetType($event: Event, widgetType: WidgetTypeBundle) { + if ($event) { + $event.stopPropagation(); + } + this.importExport.exportWidgetType(widgetType.id.id); + } + + removeWidgetType($event: Event, widgetType: WidgetTypeBundle) { + if ($event) { + $event.stopPropagation(); + } + const index = this.widgets.indexOf(widgetType); + this.widgets.splice(index, 1); + this.isDirty = true; + } + + goBack() { + this.router.navigate(['..'], { relativeTo: this.route }); + } + + exportWidgetsBundle() { + this.importExport.exportWidgetsBundle(this.widgetsBundle.id.id); + } + + edit() { + this.editMode = true; + } + + cancel() { + if (this.isDirty) { + this.widgetsService.getBundleWidgetTypeInfosList(this.widgetsBundle.id.id).subscribe( + (widgets) => { + this.widgets = [...widgets]; + this.isDirty = false; + this.addMode = false; + this.editMode = !this.widgets.length; + this.cd.markForCheck(); + } + ); + } else { + this.addMode = false; + this.editMode = !this.widgets.length; + } + } + + save() { + const widgetTypeIds = this.widgets.map(w => w.id.id); + this.widgetsService.updateWidgetsBundleWidgetTypes(this.widgetsBundle.id.id, widgetTypeIds).subscribe(() => { + this.isDirty = false; + this.editMode = false; + this.addMode = false; + }); + } + + addWidgetType($event: Event): void { + if ($event) { + $event.stopPropagation(); + } + this.dialog.open(SelectWidgetTypeDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'] + }).afterClosed().subscribe( + (type) => { + if (type) { + this.router.navigate([type], {relativeTo: this.route}).then(() => {}); + } + } + ); + } + + importWidgetType() { + this.importExport.importWidgetType().subscribe( + (widgetType) => { + if (widgetType) { + const isExistWidget = this.widgets.some(widget => widget.id.id === widgetType.id.id); + if (isExistWidget) { + this.widgets = this.widgets.map(widget => widget.id.id !== widgetType.id.id ? widget : widgetType); + } + if (this.editMode) { + this.widgets.push(widgetType); + this.isDirty = true; + this.cd.markForCheck(); + } else if (!isExistWidget) { + this.widgetsService.addWidgetFqnToWidgetBundle(this.widgetsBundle.id.id, widgetType.fqn).subscribe(() => { + this.widgets.push(widgetType); + this.cd.markForCheck(); + }); + } + } + } + ); + } + +} diff --git a/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.html b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.html index 05847aa6b6..978a79ad63 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.html +++ b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.html @@ -22,6 +22,12 @@ [fxShow]="!isEdit"> {{'widgets-bundle.open-widgets-bundle' | translate }} +
diff --git a/ui-ngx/src/app/modules/login/pages/login/login.component.ts b/ui-ngx/src/app/modules/login/pages/login/login.component.ts index 2fc1c7c50d..e61322b653 100644 --- a/ui-ngx/src/app/modules/login/pages/login/login.component.ts +++ b/ui-ngx/src/app/modules/login/pages/login/login.component.ts @@ -32,6 +32,8 @@ import { OAuth2ClientInfo } from '@shared/models/oauth2.models'; }) export class LoginComponent extends PageComponent implements OnInit { + passwordViolation = false; + loginFormGroup = this.fb.group({ username: '', password: '' @@ -57,6 +59,8 @@ export class LoginComponent extends PageComponent implements OnInit { if (error && error.error && error.error.errorCode) { if (error.error.errorCode === Constants.serverErrorCode.credentialsExpired) { this.router.navigateByUrl(`login/resetExpiredPassword?resetToken=${error.error.resetToken}`); + } else if (error.error.errorCode === Constants.serverErrorCode.passwordViolation) { + this.passwordViolation = true; } } } diff --git a/ui-ngx/src/app/shared/components/breadcrumb.component.html b/ui-ngx/src/app/shared/components/breadcrumb.component.html index be7f1e6b16..594578465f 100644 --- a/ui-ngx/src/app/shared/components/breadcrumb.component.html +++ b/ui-ngx/src/app/shared/components/breadcrumb.component.html @@ -37,11 +37,9 @@
- - - + {{ breadcrumb.icon }} - + {{ breadcrumb.ignoreTranslate ? (breadcrumb.labelFunction ? breadcrumb.labelFunction() : utils.customTranslation(breadcrumb.label, breadcrumb.label)) : (breadcrumb.label | translate) }} diff --git a/ui-ngx/src/app/shared/components/breadcrumb.component.ts b/ui-ngx/src/app/shared/components/breadcrumb.component.ts index 2542f58871..380ddc59ce 100644 --- a/ui-ngx/src/app/shared/components/breadcrumb.component.ts +++ b/ui-ngx/src/app/shared/components/breadcrumb.component.ts @@ -117,7 +117,6 @@ export class BreadcrumbComponent implements OnInit, OnDestroy { ignoreTranslate = false; } const icon = breadcrumbConfig.icon || 'home'; - const isMdiIcon = icon.startsWith('mdi:'); const link = [ route.pathFromRoot.map(v => v.url.map(segment => segment.toString()).join('/')).join('/') ]; const breadcrumb = { id: guid(), @@ -125,7 +124,6 @@ export class BreadcrumbComponent implements OnInit, OnDestroy { labelFunction, ignoreTranslate, icon, - isMdiIcon, link, queryParams: null }; diff --git a/ui-ngx/src/app/shared/components/breadcrumb.ts b/ui-ngx/src/app/shared/components/breadcrumb.ts index 599f8b85e8..77a832dbed 100644 --- a/ui-ngx/src/app/shared/components/breadcrumb.ts +++ b/ui-ngx/src/app/shared/components/breadcrumb.ts @@ -23,7 +23,6 @@ export interface BreadCrumb extends HasUUID{ labelFunction?: () => string; ignoreTranslate: boolean; icon: string; - isMdiIcon: boolean; link: any[]; queryParams: Params; } diff --git a/ui-ngx/src/app/shared/components/button/copy-button.component.html b/ui-ngx/src/app/shared/components/button/copy-button.component.html index 640d467998..285fd85491 100644 --- a/ui-ngx/src/app/shared/components/button/copy-button.component.html +++ b/ui-ngx/src/app/shared/components/button/copy-button.component.html @@ -23,9 +23,9 @@ [matTooltip]="matTooltipText" [matTooltipPosition]="matTooltipPosition" (click)="copy($event)"> - - {{ icon }} - + + {{ icon || mdiIcon }} + done diff --git a/ui-ngx/src/app/shared/components/button/copy-button.component.scss b/ui-ngx/src/app/shared/components/button/copy-button.component.scss index 0818466a38..e26f6978cb 100644 --- a/ui-ngx/src/app/shared/components/button/copy-button.component.scss +++ b/ui-ngx/src/app/shared/components/button/copy-button.component.scss @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@import "../../../../theme"; +@import "../../../../scss/constants"; :host { .mini-button { diff --git a/ui-ngx/src/app/shared/components/button/copy-button.component.ts b/ui-ngx/src/app/shared/components/button/copy-button.component.ts index b22cf575c0..0297faa0d0 100644 --- a/ui-ngx/src/app/shared/components/button/copy-button.component.ts +++ b/ui-ngx/src/app/shared/components/button/copy-button.component.ts @@ -39,6 +39,7 @@ export class CopyButtonComponent { @coerceBoolean() disabled = false; + // @deprecated need to use icon input @Input() mdiIcon: string; diff --git a/ui-ngx/src/app/shared/components/color-input.component.html b/ui-ngx/src/app/shared/components/color-input.component.html index 492a2419eb..cd40dde783 100644 --- a/ui-ngx/src/app/shared/components/color-input.component.html +++ b/ui-ngx/src/app/shared/components/color-input.component.html @@ -20,10 +20,10 @@ {{icon}} {{label}} -
+
- + diff --git a/ui-ngx/src/app/shared/components/color-input.component.scss b/ui-ngx/src/app/shared/components/color-input.component.scss index ca8ffedc72..2660c16dc8 100644 --- a/ui-ngx/src/app/shared/components/color-input.component.scss +++ b/ui-ngx/src/app/shared/components/color-input.component.scss @@ -13,6 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +@import './../../../scss/mixins'; + :host { .mat-mdc-form-field { width: 100%; diff --git a/ui-ngx/src/app/shared/components/color-input.component.ts b/ui-ngx/src/app/shared/components/color-input.component.ts index 08432d6967..6d7c7cf8b9 100644 --- a/ui-ngx/src/app/shared/components/color-input.component.ts +++ b/ui-ngx/src/app/shared/components/color-input.component.ts @@ -14,15 +14,33 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, forwardRef, Input, OnInit } from '@angular/core'; +import { + ChangeDetectorRef, + Component, + ElementRef, + forwardRef, + Input, + OnInit, + Renderer2, + ViewContainerRef +} from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormGroup, + Validators +} from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { DialogService } from '@core/services/dialog.service'; import { coerceBoolean } from '@shared/decorators/coercion'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { ColorPickerPanelComponent } from '@shared/components/color-picker/color-picker-panel.component'; +import { MatButton } from '@angular/material/button'; @Component({ selector: 'tb-color-input', @@ -51,29 +69,13 @@ export class ColorInputComponent extends PageComponent implements OnInit, Contro @Input() requiredText: string; - private colorClearButtonValue: boolean; - get colorClearButton(): boolean { - return this.colorClearButtonValue; - } @Input() - set colorClearButton(value: boolean) { - const newVal = coerceBooleanProperty(value); - if (this.colorClearButtonValue !== newVal) { - this.colorClearButtonValue = newVal; - } - } + @coerceBoolean() + colorClearButton = false; - private openOnInputValue: boolean; - get openOnInput(): boolean { - return this.openOnInputValue; - } @Input() - set openOnInput(value: boolean) { - const newVal = coerceBooleanProperty(value); - if (this.openOnInputValue !== newVal) { - this.openOnInputValue = newVal; - } - } + @coerceBoolean() + openOnInput = false; private requiredValue: boolean; get required(): boolean { @@ -91,6 +93,10 @@ export class ColorInputComponent extends PageComponent implements OnInit, Contro @Input() disabled: boolean; + @Input() + @coerceBoolean() + readonly = false; + private modelValue: string; private propagateChange = null; @@ -100,6 +106,9 @@ export class ColorInputComponent extends PageComponent implements OnInit, Contro constructor(protected store: Store, private dialogs: DialogService, private translate: TranslateService, + private popoverService: TbPopoverService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef, private fb: UntypedFormBuilder, private cd: ChangeDetectorRef) { super(store); @@ -153,17 +162,50 @@ export class ColorInputComponent extends PageComponent implements OnInit, Contro } } - showColorPicker() { - this.dialogs.colorPicker(this.colorFormGroup.get('color').value).subscribe( - (color) => { - if (color) { + showColorPicker($event: MouseEvent) { + $event.stopPropagation(); + if (!this.disabled && !this.readonly) { + this.dialogs.colorPicker(this.colorFormGroup.get('color').value, + this.colorClearButton).subscribe( + (result) => { + if (!result?.canceled) { + this.colorFormGroup.patchValue( + {color: result?.color}, {emitEvent: true} + ); + this.cd.markForCheck(); + } + } + ); + } + } + + openColorPickerPopup($event: Event, element?: ElementRef) { + if ($event) { + $event.stopPropagation(); + } + if (!this.disabled && !this.readonly) { + const trigger = element ? element.nativeElement : $event.target; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const colorPickerPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, ColorPickerPanelComponent, 'left', true, null, + { + color: this.colorFormGroup.get('color').value, + colorClearButton: this.colorClearButton + }, + {}, + {}, {}, true); + colorPickerPopover.tbComponentRef.instance.popover = colorPickerPopover; + colorPickerPopover.tbComponentRef.instance.colorSelected.subscribe((color) => { + colorPickerPopover.hide(); this.colorFormGroup.patchValue( - {color}, {emitEvent: true} + {color}, {emitEvent: true} ); this.cd.markForCheck(); - } + }); } - ); + } } clear() { diff --git a/ui-ngx/src/app/shared/components/color-picker/color-picker-panel.component.html b/ui-ngx/src/app/shared/components/color-picker/color-picker-panel.component.html new file mode 100644 index 0000000000..3ed6d4d14b --- /dev/null +++ b/ui-ngx/src/app/shared/components/color-picker/color-picker-panel.component.html @@ -0,0 +1,38 @@ + +
+
color.color
+ +
+ + +
+
diff --git a/ui-ngx/src/app/shared/components/color-picker/color-picker-panel.component.scss b/ui-ngx/src/app/shared/components/color-picker/color-picker-panel.component.scss new file mode 100644 index 0000000000..e7d78b4018 --- /dev/null +++ b/ui-ngx/src/app/shared/components/color-picker/color-picker-panel.component.scss @@ -0,0 +1,36 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.tb-color-picker-panel { + width: 328px; + display: flex; + flex-direction: column; + gap: 16px; + .tb-color-picker-title { + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.87); + } + .tb-color-picker-panel-buttons { + height: 60px; + display: flex; + flex-direction: row; + gap: 16px; + justify-content: flex-end; + align-items: flex-end; + } +} diff --git a/ui-ngx/src/app/shared/components/color-picker/color-picker-panel.component.ts b/ui-ngx/src/app/shared/components/color-picker/color-picker-panel.component.ts new file mode 100644 index 0000000000..6e20aabf87 --- /dev/null +++ b/ui-ngx/src/app/shared/components/color-picker/color-picker-panel.component.ts @@ -0,0 +1,64 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { PageComponent } from '@shared/components/page.component'; +import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { UntypedFormControl } from '@angular/forms'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { coerceBoolean } from '@shared/decorators/coercion'; + +@Component({ + selector: 'tb-color-picker-panel', + templateUrl: './color-picker-panel.component.html', + providers: [], + styleUrls: ['./color-picker-panel.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class ColorPickerPanelComponent extends PageComponent implements OnInit { + + @Input() + color: string; + + @Input() + @coerceBoolean() + colorClearButton = false; + + @Input() + popover: TbPopoverComponent; + + @Output() + colorSelected = new EventEmitter(); + + colorPickerControl: UntypedFormControl; + + constructor(protected store: Store) { + super(store); + } + + ngOnInit(): void { + this.colorPickerControl = new UntypedFormControl(this.color); + } + + selectColor() { + this.colorSelected.emit(this.colorPickerControl.value); + } + + clearColor() { + this.colorSelected.emit(null); + } +} diff --git a/ui-ngx/src/app/shared/components/color-picker/color-picker.component.html b/ui-ngx/src/app/shared/components/color-picker/color-picker.component.html index d1432d27f5..5806037c89 100644 --- a/ui-ngx/src/app/shared/components/color-picker/color-picker.component.html +++ b/ui-ngx/src/app/shared/components/color-picker/color-picker.component.html @@ -19,7 +19,7 @@
@@ -29,7 +29,12 @@
-
+ + HEX + RGBA + HSLA + +
-
+
+ +
+
diff --git a/ui-ngx/src/app/shared/components/color-picker/color-picker.component.scss b/ui-ngx/src/app/shared/components/color-picker/color-picker.component.scss index ea2bfa94ba..3e43c87ba7 100644 --- a/ui-ngx/src/app/shared/components/color-picker/color-picker.component.scss +++ b/ui-ngx/src/app/shared/components/color-picker/color-picker.component.scss @@ -17,12 +17,10 @@ width: 100%; display: flex; flex-direction: column; - gap: 16px; + gap: 32px; .saturation-component { - height: 100%; - min-height: 200px; - max-height: 300px; + height: 238px; border-radius: 8px; } @@ -55,6 +53,12 @@ .color-input-block { display: flex; + gap: 20px; + + .presentation-select { + font-size: 14px; + width: 56px; + } .color-input { flex: 1; @@ -63,16 +67,13 @@ color: initial; } } + } - .type-btn { - height: 26px; - width: 20px; - background: transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAgCAMAAAAootjDAAAAM1BMVEUAAAAzMzMzMzMzMzMzMzM0NDQzMzMzMzM0NDQzMzMzMzM0NDQzMzMzMzMyMjIrKyszMzPF8UZlAAAAEHRSTlMA1fHr4ZxxSRP45sG+sCkGH2+Z6QAAAHJJREFUKM+9kkkSgCAQA0FEVLb5/2tViqgQvNrHviSzKGCt6nDGuNass8i8NsrLiX+bZbrUtDwm7VLYE0zWUtEZ+RvUZpEvN8YhH9QmQRoC8kFpEnVHVP/DJUZVeSAem5fDKxwtms/BR+PT8gN8vwk/0wE1gQzNVYryIwAAAABJRU5ErkJggg==') no-repeat center; - background-size: 6px 12px; - - &:hover { - background-color: #eee; - } + .color-presets-block { + .color-presets-component { + display: flex; + flex-direction: column; + gap: 12px; } } } @@ -88,7 +89,7 @@ indicator-component { svg { - vertical-align: -70% !important; + vertical-align: -100% !important; } } @@ -105,4 +106,16 @@ } } } + + .color-presets-component { + .presets-row { + gap: 10px; + justify-content: space-between; + } + color-preset { + height: 20px; + width: 20px; + border-radius: 4px; + } + } } diff --git a/ui-ngx/src/app/shared/components/color-picker/color-picker.component.ts b/ui-ngx/src/app/shared/components/color-picker/color-picker.component.ts index ce49310b03..5990caa6ed 100644 --- a/ui-ngx/src/app/shared/components/color-picker/color-picker.component.ts +++ b/ui-ngx/src/app/shared/components/color-picker/color-picker.component.ts @@ -17,7 +17,8 @@ import { Component, forwardRef, OnDestroy } from '@angular/core'; import { Color, ColorPickerControl } from '@iplab/ngx-color-picker'; import { Subscription } from 'rxjs'; -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; +import { isString } from '@core/utils'; export enum ColorType { hex = 'hex', @@ -29,6 +30,10 @@ export enum ColorType { cmyk = 'cmyk' } +const colorPresetsHex = + ['#435B63', '#F44336', '#E89623', '#F5DD00', '#8BC34A', '#4CAF50', '#009688', '#048AD3', '#673AB7', '#9C27B0', '#E91E63', + '#A1ADB1', '#F9A19B', '#FFD190', '#FFF59D', '#C5E1A4', '#A5D7A7', '#80CBC3', '#81C4E9', '#B39CDB', '#CD93D7', '#F48FB1']; + @Component({ selector: `tb-color-picker`, templateUrl: `./color-picker.component.html`, @@ -43,10 +48,13 @@ export enum ColorType { }) export class ColorPickerComponent implements ControlValueAccessor, OnDestroy { - selectedPresentation = 0; presentations = [ColorType.hex, ColorType.rgba, ColorType.hsla]; control = new ColorPickerControl(); + presentationControl = new UntypedFormControl(0); + + colorPresets: Color[] = colorPresetsHex.map(c => Color.from(c)); + private modelValue: string; private subscriptions: Array = []; @@ -65,6 +73,11 @@ export class ColorPickerComponent implements ControlValueAccessor, OnDestroy { } }) ); + this.subscriptions.push( + this.presentationControl.valueChanges.subscribe(() => { + this.updateModel(); + }) + ); } registerOnChange(fn: any): void { @@ -75,8 +88,9 @@ export class ColorPickerComponent implements ControlValueAccessor, OnDestroy { } writeValue(value: string): void { - this.setValue = !!value; - this.control.setValueFrom(value || '#fff'); + const valid = this.isValidColorValue(value); + this.setValue = valid; + this.control.setValueFrom(valid ? value : '#fff'); this.modelValue = value; if (this.control.initType === ColorType.hexa) { @@ -86,12 +100,15 @@ export class ColorPickerComponent implements ControlValueAccessor, OnDestroy { } else if (this.control.initType === ColorType.hsl) { this.control.initType = ColorType.hsla; } + this.presentationControl.patchValue(this.presentations.indexOf(this.control.initType), {emitEvent: false}); + } - this.selectedPresentation = this.presentations.indexOf(this.control.initType); + private isValidColorValue(value: string): boolean { + return value && isString(value) && value.trim().length > 0; } private updateModel() { - const color: string = this.getValueByType(this.control.value, this.presentations[this.selectedPresentation]); + const color: string = this.getValueByType(this.control.value, this.presentations[this.presentationControl.value]); if (this.modelValue !== color) { this.modelValue = color; this.propagateChange(color); @@ -103,12 +120,6 @@ export class ColorPickerComponent implements ControlValueAccessor, OnDestroy { this.subscriptions.length = 0; } - public changePresentation(): void { - this.selectedPresentation = - this.selectedPresentation === this.presentations.length - 1 ? 0 : this.selectedPresentation + 1; - this.updateModel(); - } - getValueByType(color: Color, type: ColorType): string { switch (type) { case ColorType.hex: diff --git a/ui-ngx/src/app/shared/components/contact.component.html b/ui-ngx/src/app/shared/components/contact.component.html index 012ed7d3b6..8d7a4fed11 100644 --- a/ui-ngx/src/app/shared/components/contact.component.html +++ b/ui-ngx/src/app/shared/components/contact.component.html @@ -62,7 +62,7 @@ contact.email - + {{ 'user.invalid-email-format' | translate }} diff --git a/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.html b/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.html index b8783470db..c9991d6ff7 100644 --- a/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/dashboard-autocomplete.component.html @@ -24,7 +24,11 @@ formControlName="dashboard" (focusin)="onFocus()" [required]="required" - [matAutocomplete]="dashboardAutocomplete"> + [matAutocomplete]="dashboardAutocomplete" + [fxShow]="!useDashboardLink || !disabled || !selectDashboardFormGroup.get('dashboard').value"> + + {{ displayDashboardFn(selectDashboardFormGroup.get('dashboard').value) }} +
diff --git a/ui-ngx/src/app/shared/components/dialog/alert-dialog.component.ts b/ui-ngx/src/app/shared/components/dialog/alert-dialog.component.ts index c29d66b1cc..c7c26bc7a2 100644 --- a/ui-ngx/src/app/shared/components/dialog/alert-dialog.component.ts +++ b/ui-ngx/src/app/shared/components/dialog/alert-dialog.component.ts @@ -21,6 +21,7 @@ export interface AlertDialogData { title: string; message: string; ok: string; + textMode?: boolean; } @Component({ diff --git a/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.html b/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.html index eaec4c0b5e..7cb184ebeb 100644 --- a/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.html +++ b/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.html @@ -15,22 +15,15 @@ limitations under the License. --> -
-
- -
-
- - - -
-
+
+ + + +
diff --git a/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.scss b/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.scss new file mode 100644 index 0000000000..afd8d1cfcb --- /dev/null +++ b/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.scss @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + .tb-close-button { + position: absolute; + top: 6px; + right: 6px; + } +} diff --git a/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts b/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts index 44959023f4..38fcedf406 100644 --- a/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts +++ b/ui-ngx/src/app/shared/components/dialog/color-picker-dialog.component.ts @@ -14,48 +14,48 @@ /// limitations under the License. /// -import { Component, Inject, OnInit } from '@angular/core'; +import { Component, Inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Router } from '@angular/router'; import { DialogComponent } from '@shared/components/dialog.component'; export interface ColorPickerDialogData { color: string; + colorClearButton: boolean; +} + +export interface ColorPickerDialogResult { + color?: string; + canceled?: boolean; } @Component({ selector: 'tb-color-picker-dialog', templateUrl: './color-picker-dialog.component.html', - styleUrls: [] + styleUrls: ['./color-picker-dialog.component.scss'] }) -export class ColorPickerDialogComponent extends DialogComponent - implements OnInit { +export class ColorPickerDialogComponent extends DialogComponent { - colorPickerFormGroup: FormGroup; + color: string; + colorClearButton: boolean; constructor(protected store: Store, protected router: Router, @Inject(MAT_DIALOG_DATA) public data: ColorPickerDialogData, - public dialogRef: MatDialogRef, - public fb: FormBuilder) { + public dialogRef: MatDialogRef) { super(store, router, dialogRef); + this.color = data.color; + this.colorClearButton = data.colorClearButton; } - ngOnInit(): void { - this.colorPickerFormGroup = this.fb.group({ - color: [this.data.color, [Validators.required]] - }); + selectColor(color: string) { + this.dialogRef.close({color}); } cancel(): void { - this.dialogRef.close(null); + this.dialogRef.close({canceled: true}); } - select(): void { - const color: string = this.colorPickerFormGroup.get('color').value; - this.dialogRef.close(color); - } } diff --git a/ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.html b/ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.html index 141979d8ff..3b216bda71 100644 --- a/ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.html +++ b/ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.html @@ -33,8 +33,7 @@ diff --git a/ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.ts b/ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.ts index e55431a327..4aeed2a360 100644 --- a/ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.ts +++ b/ui-ngx/src/app/shared/components/dialog/json-object-edit-dialog.component.ts @@ -26,6 +26,7 @@ import { isNotEmptyStr } from '@core/utils'; export interface JsonObjectEditDialogData { jsonValue: object; + required?: boolean; title?: string; saveLabel?: string; cancelLabel?: string; @@ -43,6 +44,8 @@ export class JsonObjectEditDialogComponent extends DialogComponent, protected router: Router, @Inject(MAT_DIALOG_DATA) public data: JsonObjectEditDialogData, diff --git a/ui-ngx/src/app/shared/components/dialog/material-icons-dialog.component.html b/ui-ngx/src/app/shared/components/dialog/material-icons-dialog.component.html index 94d256fd61..632e9216be 100644 --- a/ui-ngx/src/app/shared/components/dialog/material-icons-dialog.component.html +++ b/ui-ngx/src/app/shared/components/dialog/material-icons-dialog.component.html @@ -15,63 +15,15 @@ limitations under the License. --> -
- -

{{ 'icon.select-icon' | translate }}

- -
- - - -
- -
- - -
-
- -
-
-
-
- - - - - - -
-
-
-
- - -
-
+
+ + + +
diff --git a/ui-ngx/src/app/shared/components/dialog/material-icons-dialog.component.scss b/ui-ngx/src/app/shared/components/dialog/material-icons-dialog.component.scss index 849b646234..afd8d1cfcb 100644 --- a/ui-ngx/src/app/shared/components/dialog/material-icons-dialog.component.scss +++ b/ui-ngx/src/app/shared/components/dialog/material-icons-dialog.component.scss @@ -14,36 +14,9 @@ * limitations under the License. */ :host { - .tb-material-icons-dialog { - position: relative; - } - .tb-icons-load { - top: 64px; - z-index: 3; - background: rgba(255, 255, 255, .75); - } -} - -:host ::ng-deep { - .tb-material-icons-dialog { - button.mat-mdc-button-base.tb-select-icon-button { - width: 56px; - min-width: 56px; - height: 56px; - padding: 16px; - margin: 10px; - border: solid 1px #ffa500; - border-radius: 0; - line-height: 0; - display: inline-block; - vertical-align: baseline; - .mat-icon { - width: 24px; - margin: 0; - height: 24px; - vertical-align: initial; - font-size: 24px; - } - } + .tb-close-button { + position: absolute; + top: 6px; + right: 6px; } } diff --git a/ui-ngx/src/app/shared/components/dialog/material-icons-dialog.component.ts b/ui-ngx/src/app/shared/components/dialog/material-icons-dialog.component.ts index cfa4c3d44f..53aec6d74f 100644 --- a/ui-ngx/src/app/shared/components/dialog/material-icons-dialog.component.ts +++ b/ui-ngx/src/app/shared/components/dialog/material-icons-dialog.component.ts @@ -14,19 +14,21 @@ /// limitations under the License. /// -import { AfterViewInit, Component, Inject, OnInit, QueryList, ViewChildren } from '@angular/core'; +import { Component, Inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { Router } from '@angular/router'; import { DialogComponent } from '@shared/components/dialog.component'; -import { UtilsService } from '@core/services/utils.service'; -import { UntypedFormControl } from '@angular/forms'; -import { merge, Observable, of } from 'rxjs'; -import { delay, map, mapTo, mergeMap, share, startWith, tap } from 'rxjs/operators'; export interface MaterialIconsDialogData { icon: string; + iconClearButton: boolean; +} + +export interface MaterialIconsDialogResult { + icon?: string; + canceled?: boolean; } @Component({ @@ -35,72 +37,26 @@ export interface MaterialIconsDialogData { providers: [], styleUrls: ['./material-icons-dialog.component.scss'] }) -export class MaterialIconsDialogComponent extends DialogComponent - implements OnInit, AfterViewInit { - - @ViewChildren('iconButtons') iconButtons: QueryList; +export class MaterialIconsDialogComponent extends DialogComponent { selectedIcon: string; - icons$: Observable>; - loadingIcons$: Observable; - - showAllControl: UntypedFormControl; + iconClearButton: boolean; constructor(protected store: Store, protected router: Router, @Inject(MAT_DIALOG_DATA) public data: MaterialIconsDialogData, - private utils: UtilsService, - public dialogRef: MatDialogRef) { + public dialogRef: MatDialogRef) { super(store, router, dialogRef); this.selectedIcon = data.icon; - this.showAllControl = new UntypedFormControl(false); - } - - ngOnInit(): void { - this.icons$ = this.showAllControl.valueChanges.pipe( - map((showAll) => { - return {firstTime: false, showAll}; - }), - startWith<{firstTime: boolean, showAll: boolean}>({firstTime: true, showAll: false}), - mergeMap((data) => { - if (data.showAll) { - return this.utils.getMaterialIcons().pipe(delay(100)); - } else { - const res = of(this.utils.getCommonMaterialIcons()); - return data.firstTime ? res : res.pipe(delay(50)); - } - }), - share() - ); - } - - ngAfterViewInit(): void { - this.loadingIcons$ = merge( - this.showAllControl.valueChanges.pipe( - mapTo(true), - ), - this.iconButtons.changes.pipe( - delay(100), - mapTo( false), - ) - ).pipe( - tap((loadingIcons) => { - if (loadingIcons) { - this.showAllControl.disable({emitEvent: false}); - } else { - this.showAllControl.enable({emitEvent: false}); - } - }), - share() - ); + this.iconClearButton = data.iconClearButton; } selectIcon(icon: string) { - this.dialogRef.close(icon); + this.dialogRef.close({icon}); } cancel(): void { - this.dialogRef.close(null); + this.dialogRef.close({canceled: true}); } } diff --git a/ui-ngx/src/app/shared/components/directives/tb-json-to-string.directive.ts b/ui-ngx/src/app/shared/components/directives/tb-json-to-string.directive.ts index 8da5fb1396..c70de1ab48 100644 --- a/ui-ngx/src/app/shared/components/directives/tb-json-to-string.directive.ts +++ b/ui-ngx/src/app/shared/components/directives/tb-json-to-string.directive.ts @@ -17,19 +17,23 @@ import { Directive, ElementRef, forwardRef, HostListener, Renderer2, SkipSelf } from '@angular/core'; import { ControlValueAccessor, - UntypedFormControl, FormGroupDirective, NG_VALIDATORS, NG_VALUE_ACCESSOR, NgForm, + UntypedFormControl, ValidationErrors, Validator } from '@angular/forms'; import { ErrorStateMatcher } from '@angular/material/core'; -import { isObject } from "@core/utils"; +import { isObject } from '@core/utils'; @Directive({ selector: '[tb-json-to-string]', + // eslint-disable-next-line @angular-eslint/no-host-metadata-property + host: { + '(blur)': 'onTouched()' + }, providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TbJsonToStringDirective), @@ -48,18 +52,26 @@ import { isObject } from "@core/utils"; export class TbJsonToStringDirective implements ControlValueAccessor, Validator, ErrorStateMatcher { private propagateChange = null; + public onTouched = () => {}; private parseError: boolean; private data: any; @HostListener('input', ['$event.target.value']) input(newValue: any): void { try { - this.data = JSON.parse(newValue); - if (isObject(this.data)) { - this.parseError = false; + if (newValue) { + this.data = JSON.parse(newValue); + if (isObject(this.data)) { + this.parseError = false; + } else { + this.data = null; + this.parseError = true; + } } else { - this.parseError = true; + this.data = null; + this.parseError = false; } } catch (e) { + this.data = null; this.parseError = true; } @@ -73,9 +85,7 @@ export class TbJsonToStringDirective implements ControlValueAccessor, Validator, } isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean { - const originalErrorState = this.errorStateMatcher.isErrorState(control, form); - const customErrorState = !!(control && control.invalid && this.parseError); - return originalErrorState || customErrorState; + return !!(control && control.invalid && !Array.isArray(control.value) && control.touched); } validate(c: UntypedFormControl): ValidationErrors { @@ -87,11 +97,9 @@ export class TbJsonToStringDirective implements ControlValueAccessor, Validator, } writeValue(obj: any): void { - if (obj) { - this.data = obj; - this.parseError = false; - this.render.setProperty(this.element.nativeElement, 'value', JSON.stringify(obj)); - } + this.data = obj; + this.parseError = false; + this.render.setProperty(this.element.nativeElement, 'value', obj ? JSON.stringify(obj) : ''); } registerOnChange(fn: any): void { @@ -99,5 +107,6 @@ export class TbJsonToStringDirective implements ControlValueAccessor, Validator, } registerOnTouched(fn: any): void { + this.onTouched = fn; } } diff --git a/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.html b/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.html index 6d8a424352..680d8c268f 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/entity/entity-autocomplete.component.html @@ -22,7 +22,11 @@ formControlName="entity" (focusin)="onFocus()" [required]="required" - [matAutocomplete]="entityAutocomplete"> + [matAutocomplete]="entityAutocomplete" + [fxShow]="!disabled || !selectEntityFormGroup.get('entity').value"> + + {{ displayEntityFn(selectEntityFormGroup.get('entity').value) }} + - +
-
-
- -
{{ noFileText }}
-
{{ fileName }}
+
+ +
{{ noFileText }}
+
{{ fileName }}
+
dashboard.maximum-upload-file-size
+
diff --git a/ui-ngx/src/app/shared/components/file-input.component.scss b/ui-ngx/src/app/shared/components/file-input.component.scss index 02924111b4..8a3cc015a3 100644 --- a/ui-ngx/src/app/shared/components/file-input.component.scss +++ b/ui-ngx/src/app/shared/components/file-input.component.scss @@ -21,9 +21,13 @@ $previewSize: 100px !default; .tb-container { margin-top: 0; + padding: 0 0 16px; + display: flex; + flex-direction: column; + gap: 8px; label.tb-title { - display: block; - padding-bottom: 8px; + display: flex; + padding-bottom: 0; } } @@ -78,19 +82,50 @@ $previewSize: 100px !default; text-align: center; .mat-icon { margin-right: 17px; + color: rgba(0,0,0,0.12); } } } + + .tb-file-info-container { + display: flex; + flex-direction: column; + gap: 8px; + font-size: 13px; + font-style: normal; + line-height: 16px; + letter-spacing: normal; + } + + .tb-file-name { + color: rgba(0, 0, 0, 0.54); + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + } + + .tb-file-hint { + color: rgba(0, 0, 0, 0.38); + font-weight: 400; + overflow: hidden; + text-overflow: ellipsis; + } } :host ::ng-deep { - button.browse-file { + button.mat-mdc-button.mat-mdc-button-base.browse-file { padding: 0; + min-width: 0; + height: 24px; font-size: 16px; label { display: block; cursor: pointer; - padding: 0 16px; + padding: 0 8px; } } + + .pointer-event { + pointer-events: auto; + } } diff --git a/ui-ngx/src/app/shared/components/file-input.component.ts b/ui-ngx/src/app/shared/components/file-input.component.ts index 51933d749c..3215baf10f 100644 --- a/ui-ngx/src/app/shared/components/file-input.component.ts +++ b/ui-ngx/src/app/shared/components/file-input.component.ts @@ -32,10 +32,12 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { Subscription } from 'rxjs'; -import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { FlowDirective } from '@flowjs/ngx-flow'; import { TranslateService } from '@ngx-translate/core'; import { UtilsService } from '@core/services/utils.service'; +import { DialogService } from '@core/services/dialog.service'; +import { FileSizePipe } from '@shared/pipe/file-size.pipe'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-file-input', @@ -54,6 +56,9 @@ export class FileInputComponent extends PageComponent implements AfterViewInit, @Input() label: string; + @Input() + hint: string; + @Input() accept = '*/*'; @@ -70,43 +75,28 @@ export class FileInputComponent extends PageComponent implements AfterViewInit, dropLabel: string; @Input() - contentConvertFunction: (content: string) => any; - - private requiredValue: boolean; - - get required(): boolean { - return this.requiredValue; - } + maxSizeByte: number; @Input() - set required(value: boolean) { - const newVal = coerceBooleanProperty(value); - if (this.requiredValue !== newVal) { - this.requiredValue = newVal; - } - } - - private requiredAsErrorValue: boolean; + contentConvertFunction: (content: string) => any; - get requiredAsError(): boolean { - return this.requiredAsErrorValue; - } + @Input() + @coerceBoolean() + required: boolean; @Input() - set requiredAsError(value: boolean) { - const newVal = coerceBooleanProperty(value); - if (this.requiredAsErrorValue !== newVal) { - this.requiredAsErrorValue = newVal; - } - } + @coerceBoolean() + requiredAsError: boolean; @Input() + @coerceBoolean() disabled: boolean; @Input() existingFileName: string; @Input() + @coerceBoolean() readAsBinary = false; @Input() @@ -145,7 +135,9 @@ export class FileInputComponent extends PageComponent implements AfterViewInit, constructor(protected store: Store, private utils: UtilsService, - public translate: TranslateService) { + private translate: TranslateService, + private dialog: DialogService, + private fileSize: FileSizePipe) { super(store); } @@ -153,11 +145,24 @@ export class FileInputComponent extends PageComponent implements AfterViewInit, this.autoUploadSubscription = this.flow.events$.subscribe(event => { if (event.type === 'filesAdded') { const readers = []; + let showMaxSizeAlert = false; (event.event[0] as flowjs.FlowFile[]).forEach(file => { if (this.filterFile(file)) { - readers.push(this.readerAsFile(file)); + if (this.checkMaxSize(file)) { + readers.push(this.readerAsFile(file)); + } else { + showMaxSizeAlert = true; + } } }); + + if (showMaxSizeAlert) { + this.dialog.alert( + this.translate.instant('dashboard.cannot-upload-file'), + this.translate.instant('dashboard.maximum-upload-file-size', {size: this.fileSize.transform(this.maxSizeByte)}) + ).subscribe(() => { }); + } + if (readers.length) { Promise.all(readers).then((files) => { files = files.filter(file => file.fileContent != null || file.files != null); @@ -215,6 +220,10 @@ export class FileInputComponent extends PageComponent implements AfterViewInit, }); } + private checkMaxSize(file: flowjs.FlowFile): boolean { + return !this.maxSizeByte || file.size <= this.maxSizeByte; + } + private filterFile(file: flowjs.FlowFile): boolean { if (this.allowedExtensions) { return this.allowedExtensions.split(',').indexOf(file.getExtension()) > -1; diff --git a/ui-ngx/src/app/shared/components/grid/scroll-grid-datasource.ts b/ui-ngx/src/app/shared/components/grid/scroll-grid-datasource.ts new file mode 100644 index 0000000000..1298ec4f8f --- /dev/null +++ b/ui-ngx/src/app/shared/components/grid/scroll-grid-datasource.ts @@ -0,0 +1,282 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { DataSource, ListRange } from '@angular/cdk/collections'; +import { CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; +import { BehaviorSubject, Observable, of, Subscription } from 'rxjs'; +import { catchError } from 'rxjs/operators'; +import { emptyPageData, PageData } from '@shared/models/page/page-data'; +import { BreakpointObserver } from '@angular/cdk/layout'; +import { resolveBreakpoint } from '@shared/models/constants'; + +export type GridEntitiesFetchFunction = (pageSize: number, page: number, filter: F) => Observable>; + +export type GridCellType = 'emptyCell' | 'loadingCell'; + +export interface ScrollGridColumns { + columns: number; + breakpoints?: {[breakpoint: string]: number}; +} + +export class ScrollGridDatasource extends DataSource<(T | GridCellType)[]> { + + public initialDataLoading = true; + + private _data: T[] = []; + private _rows: (T | GridCellType)[][] = Array.from({length: 100000}); + private _hasNext = true; + private _columns: number; + private _viewport: CdkVirtualScrollViewport; + private _pendingRange: ListRange = null; + private _fetchingData = false; + private _fetchSubscription: Subscription; + private _totalElements = 0; + + private _dataStream: BehaviorSubject<(T | GridCellType)[][]>; + private _subscription: Subscription; + + constructor(private breakpointObserver: BreakpointObserver, + private columns: ScrollGridColumns | number, + private fetchFunction: GridEntitiesFetchFunction, + private filter: F) { + super(); + } + + connect(collectionViewer: CdkVirtualForOf<(T | GridCellType)[]>): Observable<(T | GridCellType)[][]> { + this._viewport = (collectionViewer as any)._viewport; + this._init(); + + if (typeof this.columns === 'object' && this.columns.breakpoints) { + const breakpoints = Object.keys(this.columns.breakpoints); + this._subscription.add(this.breakpointObserver.observe(breakpoints.map(breakpoint => resolveBreakpoint(breakpoint))).subscribe( + () => { + this._columnsChanged(this._detectColumns()); + } + )); + } + + this._subscription.add( + collectionViewer.viewChange.subscribe(range => this._fetchDataFromRange(range)) + ); + return this._dataStream; + } + + disconnect(): void { + this._reset(); + this._subscription.unsubscribe(); + } + + + get isEmpty(): boolean { + return !this._data.length; + } + + get active(): boolean { + return !!this._subscription && !this._subscription.closed; + } + + get currentColumns(): number { + return this._columns; + } + + public updateFilter(filter: F) { + this.filter = filter; + this.update(); + } + + public update() { + if (this.active) { + const prevLength = this._rows.length; + this._reset(); + const dataLengthChanged = prevLength !== this._rows.length; + + const range = this._viewport.getRenderedRange(); + + if (dataLengthChanged) { + // Force recalculate new range + if (range.start === 0) { + range.start = 1; + } + this._viewport.appendOnly = false; + } + + const scrollOffset = this._viewport.measureScrollOffset(); + if (scrollOffset > 0) { + this._viewport.scrollToOffset(0); + } + + this._dataUpdated(); + this._viewport.appendOnly = true; + + if (!dataLengthChanged) { + this._fetchDataFromRange(range); + } + } + } + + public updateItem(index: number, item: T) { + this._data[index] = item; + this._dataUpdated(); + } + + public deleteItem(index: number) { + if (index < this._data.length) { + this._data.splice(index, 1); + this._totalElements--; + const rowsLength = this._totalElements ? Math.ceil(this._totalElements / this._columns) : 100000; + this._rows = Array.from({length: rowsLength}); + this._dataUpdated(); + if (this._hasNext) { + this._fetchDataFromRange(this._viewport.getRenderedRange()); + } + } + } + + private _detectColumns(): number { + if (typeof this.columns !== 'object') { + return this.columns; + } else { + let columns = this.columns.columns; + if (this.columns.breakpoints) { + for (const breakpoint of Object.keys(this.columns.breakpoints)) { + const breakpointValue = resolveBreakpoint(breakpoint); + if (this.breakpointObserver.isMatched(breakpointValue)) { + columns = this.columns.breakpoints[breakpoint]; + break; + } + } + } + return columns; + } + } + + private _init() { + this._subscription = new Subscription(); + this._columns = this._detectColumns(); + if (this._dataStream) { + this._dataStream.complete(); + } + this._dataStream = new BehaviorSubject(this._rows); + } + + private _reset() { + this._data = []; + this._totalElements = 0; + this.initialDataLoading = true; + this._rows = Array.from({length: 100000}); + this._hasNext = true; + this._pendingRange = null; + this._fetchingData = false; + if (this._fetchSubscription) { + this._fetchSubscription.unsubscribe(); + } + } + + private _columnsChanged(columns: number) { + if (this._columns !== columns) { + const fetchData = columns > this._columns; + this._columns = columns; + const rowsLength = this._totalElements ? Math.ceil(this._totalElements / this._columns) : 100000; + this._rows = Array.from({length: rowsLength}); + this._dataUpdated(); + if (fetchData && this._hasNext) { + this._fetchDataFromRange(this._viewport.getRenderedRange()); + } + } + } + + private _fetchDataFromRange(range: ListRange) { + if (this._hasNext) { + if (this._fetchingData) { + this._pendingRange = range; + } else { + const endIndex = (range.end + 1) * this._columns; + if (endIndex > this._data.length) { + const startIndex = this._data.length; + const minPageSize = endIndex - startIndex; + const maxPageSize = minPageSize * 2; + let pageSize = minPageSize; + let page = Math.floor(startIndex / pageSize); + while (startIndex % pageSize !== 0 && pageSize <= maxPageSize) { + if (((page + 1) * pageSize) > endIndex) { + break; + } + pageSize++; + page = Math.floor(startIndex / pageSize); + } + const offset = startIndex % pageSize; + this._fetchData(offset, pageSize, page); + } + } + } + } + + private _fetchData(offset: number, pageSize: number, page: number) { + this._fetchingData = true; + this._fetchSubscription = this.fetchFunction(pageSize, page, this.filter).pipe( + catchError(() => of(emptyPageData())) + ).subscribe( + (data) => { + this._hasNext = data.hasNext; + if (data.data.length > offset) { + for (let i = offset; i < data.data.length; i++) { + this._data.push(data.data[i]); + } + } + this._totalElements = data.totalElements; + const rowsLength = this._totalElements ? Math.ceil(this._totalElements / this._columns) : 100000; + this._rows = Array.from({length: rowsLength}); + this._dataUpdated(); + this.initialDataLoading = false; + this._fetchingData = false; + if (this._pendingRange) { + const range = this._pendingRange; + this._pendingRange = null; + this._fetchDataFromRange(range); + } + } + ); + } + + private _dataUpdated() { + for (let index = 0; index < this._data.length; index++) { + const row = Math.floor(index / this._columns); + const col = index % this._columns; + if (!this._rows[row]) { + this._rows[row] = []; + } + this._rows[row][col] = this._data[index]; + } + this._fillGridCells(); + this._dataStream.next(this._rows); + } + + private _fillGridCells() { + if (this._totalElements) { + const startIndex = this._data.length; + const endIndex = this._rows.length * this._columns; + for (let index = startIndex; index < endIndex; index++) { + const row = Math.floor(index / this._columns); + const col = index % this._columns; + const cellType: GridCellType = index < this._totalElements ? 'loadingCell' : 'emptyCell'; + if (!this._rows[row]) { + this._rows[row] = []; + } + this._rows[row][col] = cellType; + } + } + } +} diff --git a/ui-ngx/src/app/shared/components/grid/scroll-grid.component.html b/ui-ngx/src/app/shared/components/grid/scroll-grid.component.html new file mode 100644 index 0000000000..43b877d295 --- /dev/null +++ b/ui-ngx/src/app/shared/components/grid/scroll-grid.component.html @@ -0,0 +1,54 @@ + + + +
+
+ + + + + + +
+
+
+
+ + + + + + + + + + + + +
+ +
+
+ +
+ +
+
diff --git a/ui-ngx/src/app/shared/components/grid/scroll-grid.component.scss b/ui-ngx/src/app/shared/components/grid/scroll-grid.component.scss new file mode 100644 index 0000000000..0dec0ff8e5 --- /dev/null +++ b/ui-ngx/src/app/shared/components/grid/scroll-grid.component.scss @@ -0,0 +1,34 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.tb-scroll-grid-viewport { + height: 100%; + .cdk-virtual-scroll-content-wrapper { + display: flex; + flex-direction: column; + width: 100%; + } + .cdk-virtual-scroll-spacer { + height: auto !important; + } + .tb-scroll-grid-items-row { + display: flex; + flex-direction: row; + } + .tb-scroll-grid-item-container { + flex: 1; + min-width: 0; + } +} diff --git a/ui-ngx/src/app/shared/components/grid/scroll-grid.component.ts b/ui-ngx/src/app/shared/components/grid/scroll-grid.component.ts new file mode 100644 index 0000000000..9925fcbf5d --- /dev/null +++ b/ui-ngx/src/app/shared/components/grid/scroll-grid.component.ts @@ -0,0 +1,167 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + AfterViewInit, ChangeDetectorRef, + Component, + Input, + OnChanges, OnDestroy, + OnInit, + Renderer2, + SimpleChanges, + TemplateRef, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { + GridEntitiesFetchFunction, + ScrollGridColumns, + ScrollGridDatasource +} from '@shared/components/grid/scroll-grid-datasource'; +import { BreakpointObserver } from '@angular/cdk/layout'; +import { isObject } from '@app/core/utils'; +import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; +import { ResizeObserver } from '@juggle/resize-observer'; + +export type ItemSizeFunction = (itemWidth: number) => number; + +export interface ItemSizeStrategy { + defaultItemSize: number; + itemSizeFunction: ItemSizeFunction; +} + +@Component({ + selector: 'tb-scroll-grid', + templateUrl: './scroll-grid.component.html', + styleUrls: ['./scroll-grid.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class ScrollGridComponent implements OnInit, AfterViewInit, OnChanges, OnDestroy { + + @ViewChild('viewport') + viewport: CdkVirtualScrollViewport; + + @Input() + columns: ScrollGridColumns | number = 1; + + @Input() + fetchFunction: GridEntitiesFetchFunction; + + @Input() + filter: F; + + @Input() + itemSize: number | ItemSizeStrategy = 200; + + @Input() + gap = 12; + + @Input() + itemCard: TemplateRef<{item: T}>; + + @Input() + loadingCell: TemplateRef; + + @Input() + dataLoading: TemplateRef; + + @Input() + noData: TemplateRef; + + dataSource: ScrollGridDatasource; + + calculatedItemSize: number; + minBuffer: number; + maxBuffer: number; + + private contentResize$: ResizeObserver; + + constructor(private breakpointObserver: BreakpointObserver, + private cd: ChangeDetectorRef, + private renderer: Renderer2) { + } + + ngOnInit(): void { + if (typeof this.itemSize === 'number') { + this.calculatedItemSize = this.itemSize; + } else { + this.calculatedItemSize = this.itemSize.defaultItemSize; + } + this.minBuffer = this.calculatedItemSize; + this.maxBuffer = this.calculatedItemSize * 2; + this.dataSource = new ScrollGridDatasource(this.breakpointObserver, this.columns, this.fetchFunction, this.filter); + } + + ngAfterViewInit() { + this.renderer.setStyle(this.viewport._contentWrapper.nativeElement, 'gap', this.gap + 'px'); + this.renderer.setStyle(this.viewport._contentWrapper.nativeElement, 'padding', this.gap + 'px'); + if (!(typeof this.itemSize === 'number')) { + this.contentResize$ = new ResizeObserver(() => { + this.onContentResize(); + }); + this.contentResize$.observe(this.viewport._contentWrapper.nativeElement); + } + } + + ngOnChanges(changes: SimpleChanges): void { + for (const propName of Object.keys(changes)) { + const change = changes[propName]; + if (!change.firstChange && change.currentValue !== change.previousValue && propName === 'filter') { + this.dataSource.updateFilter(this.filter); + } + } + } + + ngOnDestroy() { + if (this.contentResize$) { + this.contentResize$.disconnect(); + } + } + + isObject(value: any): boolean { + return isObject(value); + } + + trackByItemsRow(index: number, itemsRow: T[]): number { + return index; + } + + trackByItem(index: number, item: T): T { + return item; + } + + public update() { + this.dataSource.update(); + } + + public updateItem(index: number, item: T) { + this.dataSource.updateItem(index, item); + } + + public deleteItem(index: number) { + this.dataSource.deleteItem(index); + } + + private onContentResize() { + const contentWidth = this.viewport._contentWrapper.nativeElement.getBoundingClientRect().width; + const columns = this.dataSource.currentColumns; + const itemWidth = (contentWidth - this.gap * (columns + 1)) / columns; + this.calculatedItemSize = (this.itemSize as ItemSizeStrategy).itemSizeFunction(itemWidth); + this.minBuffer = this.calculatedItemSize; + this.maxBuffer = this.calculatedItemSize * 2; + this.cd.markForCheck(); + } +} diff --git a/ui-ngx/src/app/shared/components/help-markdown.component.ts b/ui-ngx/src/app/shared/components/help-markdown.component.ts index 97bd326b46..90ac325c55 100644 --- a/ui-ngx/src/app/shared/components/help-markdown.component.ts +++ b/ui-ngx/src/app/shared/components/help-markdown.component.ts @@ -24,6 +24,7 @@ import { import { BehaviorSubject } from 'rxjs'; import { share } from 'rxjs/operators'; import { HelpService } from '@core/services/help.service'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-help-markdown', @@ -36,7 +37,9 @@ export class HelpMarkdownComponent implements OnDestroy, OnInit, OnChanges { @Input() helpContent: string; - @Input() visible: boolean; + @Input() + @coerceBoolean() + visible: boolean; @Input() style: { [klass: string]: any } = {}; diff --git a/ui-ngx/src/app/shared/components/help-popup.component.html b/ui-ngx/src/app/shared/components/help-popup.component.html index 1a9fe5541a..6381ecdf73 100644 --- a/ui-ngx/src/app/shared/components/help-popup.component.html +++ b/ui-ngx/src/app/shared/components/help-popup.component.html @@ -30,19 +30,22 @@
-
+
diff --git a/ui-ngx/src/app/shared/components/help-popup.component.scss b/ui-ngx/src/app/shared/components/help-popup.component.scss index 2ec26a3c0f..6be4d9b71c 100644 --- a/ui-ngx/src/app/shared/components/help-popup.component.scss +++ b/ui-ngx/src/app/shared/components/help-popup.component.scss @@ -17,6 +17,9 @@ width: initial; display: inline-block; vertical-align: middle; + &.hint-button { + line-height: 1; + } } .tb-help-popup-button { @@ -65,4 +68,19 @@ vertical-align: middle; } } + &.hint-button { + padding: 2px 3px; + line-height: 1; + &.mat-mdc-outlined-button { + padding: 1px 2px; + } + .mdc-button__label > span { + .mat-icon { + margin-right: 0; + } + .mat-mdc-progress-spinner { + margin-right: 0; + } + } + } } diff --git a/ui-ngx/src/app/shared/components/help-popup.component.ts b/ui-ngx/src/app/shared/components/help-popup.component.ts index 08be61f728..7c698f52a5 100644 --- a/ui-ngx/src/app/shared/components/help-popup.component.ts +++ b/ui-ngx/src/app/shared/components/help-popup.component.ts @@ -28,9 +28,9 @@ import { TbPopoverService } from '@shared/components/popover.service'; import { PopoverPlacement } from '@shared/components/popover.models'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { isDefinedAndNotNull } from '@core/utils'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ - // eslint-disable-next-line @angular-eslint/component-selector selector: '[tb-help-popup], [tb-help-popup-content]', templateUrl: './help-popup.component.html', styleUrls: ['./help-popup.component.scss'], @@ -41,10 +41,8 @@ export class HelpPopupComponent implements OnChanges, OnDestroy { @ViewChild('toggleHelpButton', {read: ElementRef, static: false}) toggleHelpButton: ElementRef; @ViewChild('toggleHelpTextButton', {read: ElementRef, static: false}) toggleHelpTextButton: ElementRef; - // eslint-disable-next-line @angular-eslint/no-input-rename @Input('tb-help-popup') helpId: string; - // eslint-disable-next-line @angular-eslint/no-input-rename @Input('tb-help-popup-content') helpContent: string; // eslint-disable-next-line @angular-eslint/no-input-rename @@ -62,6 +60,11 @@ export class HelpPopupComponent implements OnChanges, OnDestroy { popoverVisible = false; popoverReady = true; + + @Input() + @coerceBoolean() + hintMode = false; + triggerSafeHtml: SafeHtml = null; textMode = false; diff --git a/ui-ngx/src/app/shared/components/help.component.ts b/ui-ngx/src/app/shared/components/help.component.ts index d7be6942dc..3ea2653658 100644 --- a/ui-ngx/src/app/shared/components/help.component.ts +++ b/ui-ngx/src/app/shared/components/help.component.ts @@ -18,13 +18,11 @@ import { Component, Input } from '@angular/core'; import { HelpLinks } from '@shared/models/constants'; @Component({ - // eslint-disable-next-line @angular-eslint/component-selector selector: '[tb-help]', templateUrl: './help.component.html' }) export class HelpComponent { - // eslint-disable-next-line @angular-eslint/no-input-rename @Input('tb-help') helpLinkId: string; gotoHelpPage(): void { diff --git a/ui-ngx/src/app/shared/components/hint-tooltip-icon.component.html b/ui-ngx/src/app/shared/components/hint-tooltip-icon.component.html new file mode 100644 index 0000000000..63feb714d6 --- /dev/null +++ b/ui-ngx/src/app/shared/components/hint-tooltip-icon.component.html @@ -0,0 +1,22 @@ + + +{{ hintIcon }} diff --git a/ui-ngx/src/app/shared/components/hint-tooltip-icon.component.scss b/ui-ngx/src/app/shared/components/hint-tooltip-icon.component.scss new file mode 100644 index 0000000000..80e36d117d --- /dev/null +++ b/ui-ngx/src/app/shared/components/hint-tooltip-icon.component.scss @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host(.tb-hint-tooltip) { + display: flex; + flex-direction: row; + align-items: center; + gap: 8px; +} + +:host { + .tb-hint-tooltip-icon { + color: #E0E0E0; + overflow: visible; + &:hover { + color: #9E9E9E; + } + } +} diff --git a/ui-ngx/src/app/shared/components/hint-tooltip-icon.component.ts b/ui-ngx/src/app/shared/components/hint-tooltip-icon.component.ts new file mode 100644 index 0000000000..dde6dc98e8 --- /dev/null +++ b/ui-ngx/src/app/shared/components/hint-tooltip-icon.component.ts @@ -0,0 +1,36 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, HostBinding, Input } from '@angular/core'; +import { TooltipPosition } from '@angular/material/tooltip'; + +@Component({ + selector: '[tb-hint-tooltip-icon]', + templateUrl: './hint-tooltip-icon.component.html', + styleUrls: ['./hint-tooltip-icon.component.scss'] +}) +export class HintTooltipIconComponent { + + @HostBinding('class.tb-hint-tooltip') + @Input('tb-hint-tooltip-icon') tooltipText: string; + + @Input() + tooltipPosition: TooltipPosition = 'right'; + + @Input() + hintIcon = 'info'; + +} diff --git a/ui-ngx/src/app/shared/components/icon.component.ts b/ui-ngx/src/app/shared/components/icon.component.ts new file mode 100644 index 0000000000..cf4359c369 --- /dev/null +++ b/ui-ngx/src/app/shared/components/icon.component.ts @@ -0,0 +1,281 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { CanColor, mixinColor } from '@angular/material/core'; +import { + AfterContentInit, + AfterViewChecked, + ChangeDetectionStrategy, + Component, + ElementRef, + ErrorHandler, + Inject, + OnDestroy, + Renderer2, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { MAT_ICON_LOCATION, MatIconLocation, MatIconRegistry } from '@angular/material/icon'; +import { Subscription } from 'rxjs'; +import { take } from 'rxjs/operators'; +import { isSvgIcon, splitIconName } from '@shared/models/icon.models'; +import { ContentObserver } from '@angular/cdk/observers'; + +const _TbIconBase = mixinColor( + class { + constructor(public _elementRef: ElementRef) {} + }, +); + +const funcIriAttributes = [ + 'clip-path', + 'color-profile', + 'src', + 'cursor', + 'fill', + 'filter', + 'marker', + 'marker-start', + 'marker-mid', + 'marker-end', + 'mask', + 'stroke', +]; + +const funcIriAttributeSelector = funcIriAttributes.map(attr => `[${attr}]`).join(', '); + +const funcIriPattern = /^url\(['"]?#(.*?)['"]?\)$/; + +@Component({ + template: '', + selector: 'tb-icon', + exportAs: 'tbIcon', + styleUrls: [], + // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property + inputs: ['color'], + // eslint-disable-next-line @angular-eslint/no-host-metadata-property + host: { + role: 'img', + class: 'mat-icon notranslate', + '[attr.data-mat-icon-type]': '!_useSvgIcon ? "font" : "svg"', + '[attr.data-mat-icon-name]': '_svgName', + '[attr.data-mat-icon-namespace]': '_svgNamespace', + '[class.mat-icon-no-color]': 'color !== "primary" && color !== "accent" && color !== "warn"', + }, + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class TbIconComponent extends _TbIconBase + implements AfterContentInit, AfterViewChecked, CanColor, OnDestroy { + + @ViewChild('iconNameContent', {static: true}) + _iconNameContent: ElementRef; + + private icon: string; + + get viewValue(): string { + return (this._iconNameContent?.nativeElement.textContent || '').trim(); + } + + private _contentChanges = Subscription.EMPTY; + private _previousFontSetClass: string[] = []; + + _useSvgIcon = false; + _svgName: string | null; + _svgNamespace: string | null; + + private _textElement = null; + + private _previousPath?: string; + + private _elementsWithExternalReferences?: Map; + + private _currentIconFetch = Subscription.EMPTY; + + constructor(elementRef: ElementRef, + private contentObserver: ContentObserver, + private renderer: Renderer2, + private _iconRegistry: MatIconRegistry, + @Inject(MAT_ICON_LOCATION) private _location: MatIconLocation, + private readonly _errorHandler: ErrorHandler) { + super(elementRef); + } + + ngAfterContentInit(): void { + this.icon = this.viewValue; + this._updateIcon(); + this._contentChanges = this.contentObserver.observe(this._iconNameContent.nativeElement) + .subscribe(() => { + const content = this.viewValue; + if (this.icon !== content) { + this.icon = content; + this._updateIcon(); + } + }); + } + + ngAfterViewChecked() { + const cachedElements = this._elementsWithExternalReferences; + if (cachedElements && cachedElements.size) { + const newPath = this._location.getPathname(); + if (newPath !== this._previousPath) { + this._previousPath = newPath; + this._prependPathToReferences(newPath); + } + } + } + + ngOnDestroy() { + this._contentChanges.unsubscribe(); + this._currentIconFetch.unsubscribe(); + if (this._elementsWithExternalReferences) { + this._elementsWithExternalReferences.clear(); + } + } + + private _updateIcon() { + const useSvgIcon = isSvgIcon(this.icon); + if (this._useSvgIcon !== useSvgIcon) { + this._useSvgIcon = useSvgIcon; + if (!this._useSvgIcon) { + this._updateSvgIcon(undefined); + } else { + this._updateFontIcon(undefined); + } + } + if (this._useSvgIcon) { + this._updateSvgIcon(this.icon); + } else { + this._updateFontIcon(this.icon); + } + } + + private _updateFontIcon(rawName: string | undefined) { + if (rawName) { + this._clearFontIcon(); + const iconName = splitIconName(rawName)[1]; + this._textElement = this.renderer.createText(iconName); + const elem: HTMLElement = this._elementRef.nativeElement; + this.renderer.insertBefore(elem, this._textElement, this._iconNameContent.nativeElement); + const fontSetClasses = ( + this._iconRegistry.getDefaultFontSetClass() + ).filter(className => className.length > 0); + fontSetClasses.forEach(className => elem.classList.add(className)); + this._previousFontSetClass = fontSetClasses; + } else { + this._clearFontIcon(); + } + } + + private _clearFontIcon() { + const elem: HTMLElement = this._elementRef.nativeElement; + if (this._textElement !== null) { + this.renderer.removeChild(elem, this._textElement); + this._textElement = null; + } + this._previousFontSetClass.forEach(className => elem.classList.remove(className)); + this._previousFontSetClass = []; + } + + private _updateSvgIcon(rawName: string | undefined) { + this._svgNamespace = null; + this._svgName = null; + this._currentIconFetch.unsubscribe(); + + if (rawName) { + const [namespace, iconName] = splitIconName(rawName); + if (namespace) { + this._svgNamespace = namespace; + } + if (iconName) { + this._svgName = iconName; + } + this._iconRegistry.getDefaultFontSetClass(); + this._currentIconFetch = this._iconRegistry + .getNamedSvgIcon(iconName, namespace) + .pipe(take(1)) + .subscribe({ + next: (svg) => this._setSvgElement(svg), + error: (err: Error) => { + const errorMessage = `Error retrieving icon ${namespace}:${iconName}! ${err.message}`; + this._errorHandler.handleError(new Error(errorMessage)); + } + }); + } else { + this._clearSvgElement(); + } + } + + private _setSvgElement(svg: SVGElement) { + this._clearSvgElement(); + const path = this._location.getPathname(); + this._previousPath = path; + this._cacheChildrenWithExternalReferences(svg); + this._prependPathToReferences(path); + this.renderer.insertBefore(this._elementRef.nativeElement, svg, this._iconNameContent.nativeElement); + } + + private _clearSvgElement() { + const layoutElement: HTMLElement = this._elementRef.nativeElement; + let childCount = layoutElement.childNodes.length; + if (this._elementsWithExternalReferences) { + this._elementsWithExternalReferences.clear(); + } + while (childCount--) { + const child = layoutElement.childNodes[childCount]; + if (child.nodeType !== 1 || child.nodeName.toLowerCase() === 'svg') { + child.remove(); + } + } + } + + private _cacheChildrenWithExternalReferences(element: SVGElement) { + const elementsWithFuncIri = element.querySelectorAll(funcIriAttributeSelector); + const elements = (this._elementsWithExternalReferences = this._elementsWithExternalReferences || new Map()); + elementsWithFuncIri.forEach( + (elementWithFuncIri) => { + funcIriAttributes.forEach(attr => { + const elementWithReference = elementWithFuncIri; + const value = elementWithReference.getAttribute(attr); + const match = value ? value.match(funcIriPattern) : null; + + if (match) { + let attributes = elements.get(elementWithReference); + + if (!attributes) { + attributes = []; + elements.set(elementWithReference, attributes); + } + + attributes.push({name: attr, value: match[1]}); + } + }); + } + ); + } + + private _prependPathToReferences(path: string) { + const elements = this._elementsWithExternalReferences; + if (elements) { + elements.forEach((attrs, element) => { + attrs.forEach(attr => { + element.setAttribute(attr.name, `url('${path}#${attr.value}')`); + }); + }); + } + } + +} diff --git a/ui-ngx/src/app/shared/components/image-input.component.html b/ui-ngx/src/app/shared/components/image-input.component.html index d2512a8148..96f674a745 100644 --- a/ui-ngx/src/app/shared/components/image-input.component.html +++ b/ui-ngx/src/app/shared/components/image-input.component.html @@ -20,38 +20,14 @@
-
-
-
-
{{ (disabled ? 'dashboard.empty-image' : 'dashboard.no-image') | translate }}
- -
-
- -
-
-
-
-
- cloud_upload - image-input.drop-image-or - - +
+
+
{{ 'dashboard.empty-image' | translate }}
+
-
-
-
-
+
+
+ cloud_upload +
+ image-input.drag-and-drop +
+ image-input.or + + +
+
+
+
+ +
-
dashboard.maximum-upload-file-size
+
+
{{ fileName }}
+
dashboard.maximum-upload-file-size
+
diff --git a/ui-ngx/src/app/shared/components/image-input.component.scss b/ui-ngx/src/app/shared/components/image-input.component.scss index a776bc8cd7..36f4c59557 100644 --- a/ui-ngx/src/app/shared/components/image-input.component.scss +++ b/ui-ngx/src/app/shared/components/image-input.component.scss @@ -15,46 +15,57 @@ */ @import "../../../scss/constants"; -$containerHeight: 120px !default; -$previewContainerWidth: 168px !default; -$previewSize: 96px !default; +$containerHeight: 96px !default; +$previewSize: 78px !default; :host { .tb-container { margin-top: 0; - padding: 0; + padding: 0 0 16px; + display: flex; + flex-direction: column; + gap: 8px; label.tb-title { display: block; - padding-bottom: 8px; + padding-bottom: 0px; } } .tb-image-select-container { - position: relative; width: 100%; height: $containerHeight; + display: flex; + gap: 12px; + align-items: center; } .image-container { - position: relative; - float: left; - height: $containerHeight; - padding: 12px; - margin-right: 8px; - background: rgba(0, 0, 0, 0.03); + background: #F3F6FA; border-radius: 4px; + border: 1px solid rgba(0, 0, 0, 0.05); + padding: 8px 12px 8px 8px; + display: flex; + align-items: center; + gap: 12px; + &.disabled { + padding: 8px; + } } - .image-content-container { - background: #FFFFFF; - border: 1px solid rgba(0, 0, 0, 0.2); - border-radius: 4px; - padding-left: 8px; + .tb-image-preview-container { + width: $previewSize; height: $previewSize; - &.no-padding { - padding-left: 0px; - } + border: 1px solid rgba(0, 0, 0, 0.12); + background: #fff; + display: flex; + align-items: center; + justify-content: center; + } + + .tb-image-preview-text { + font-size: 14px; + text-align: center; } .tb-image-preview { @@ -64,39 +75,28 @@ $previewSize: 96px !default; max-height: $previewSize - 2px; } - .tb-image-preview-container { - position: relative; - float: left; - width: $previewSize; - height: $previewSize; - margin-top: -1px; - margin-bottom: -1px; - border: 1px solid rgba(0, 0, 0, 0.54); - - div { - width: 100%; - font-size: 18px; - text-align: center; - } + .tb-image-info-container { + display: flex; + gap: 12px; + font-size: 13px; + font-style: normal; + line-height: 16px; + letter-spacing: normal; + } - div, - .tb-image-preview { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - } + .tb-image-file-name { + width: 136px; + color: rgba(0, 0, 0, 0.54); + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; } - .tb-image-clear-container { - position: relative; - float: right; - height: $previewSize; - display: flex; - align-items: center; - &.full-height { - height: $containerHeight; - } + .tb-image-file-hint { + color: rgba(0, 0, 0, 0.38); + font-weight: 400; + overflow: hidden; + text-overflow: ellipsis; } .file-input { @@ -104,17 +104,13 @@ $previewSize: 96px !default; } .tb-flow-drop { - position: relative; - height: $containerHeight; + height: 100%; + flex: 1; overflow: hidden; border: 2px dashed rgba(0, 0, 0, 0.2); border-radius: 4px; box-sizing: border-box; - &.float-left { - float: left; - } - .upload-label { width: 100%; height: 100%; @@ -123,28 +119,48 @@ $previewSize: 96px !default; flex-direction: row; justify-content: center; align-items: center; - font-size: 16px; - color: rgba(0, 0, 0, 0.54); - text-align: center; + gap: 8px; .mat-icon { - margin-right: 17px; + color: rgba(0,0,0,0.12); + } + .upload-text-area { + display: flex; + flex-direction: column; + align-items: center; + font-size: 16px; + line-height: 24px; + color: rgba(0, 0, 0, 0.54); + text-align: center; + .hide-xs { + @media #{$mat-xs} { + display: none; + } + } + } + .upload-button-area { + display: flex; + justify-content: center; + align-items: flex-start; + gap: 6px; } } } .tb-hint{ margin-top: 8px; + padding-bottom: 0; } } :host ::ng-deep { - button.browse-file { + button.mat-mdc-button.mat-mdc-button-base.browse-file { padding: 0; + min-width: 0; + height: 24px; font-size: 16px; label { display: block; cursor: pointer; - padding: 0 16px; } } } diff --git a/ui-ngx/src/app/shared/components/image-input.component.ts b/ui-ngx/src/app/shared/components/image-input.component.ts index 026b64a413..21722159d4 100644 --- a/ui-ngx/src/app/shared/components/image-input.component.ts +++ b/ui-ngx/src/app/shared/components/image-input.component.ts @@ -14,7 +14,16 @@ /// limitations under the License. /// -import { AfterViewInit, ChangeDetectorRef, Component, forwardRef, Input, OnDestroy, ViewChild } from '@angular/core'; +import { + AfterViewInit, + ChangeDetectorRef, + Component, EventEmitter, + forwardRef, + Input, + OnDestroy, + Output, + ViewChild +} from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -27,6 +36,8 @@ import { UtilsService } from '@core/services/utils.service'; import { DialogService } from '@core/services/dialog.service'; import { TranslateService } from '@ngx-translate/core'; import { FileSizePipe } from '@shared/pipe/file-size.pipe'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { ImagePipe } from '@shared/pipe/image.pipe'; @Component({ selector: 'tb-image-input', @@ -74,7 +85,27 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit, @Input() inputId = this.utils.guid(); + @Input() + @coerceBoolean() + processImageApiLink = false; + + @Input() + @coerceBoolean() + resultAsFile = false; + + @Input() + @coerceBoolean() + showFileName = false; + + @Input() + fileName: string; + + @Output() + fileNameChanged = new EventEmitter(); + imageUrl: string; + file: File; + safeImageUrl: SafeUrl; @ViewChild('flow', {static: true}) @@ -87,6 +118,7 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit, constructor(protected store: Store, private utils: UtilsService, private sanitizer: DomSanitizer, + private imagePipe: ImagePipe, private dialog: DialogService, private translate: TranslateService, private fileSize: FileSizePipe, @@ -97,7 +129,9 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit, ngAfterViewInit() { this.autoUploadSubscription = this.flow.events$.subscribe(event => { if (event.type === 'fileAdded') { - const file = (event.event[0] as flowjs.FlowFile).file; + const flowFile = event.event[0] as flowjs.FlowFile; + const file = flowFile.file; + const fileName = flowFile.name; if (this.maxSizeByte && this.maxSizeByte < file.size) { this.dialog.alert( this.translate.instant('dashboard.cannot-upload-file'), @@ -108,10 +142,12 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit, return false; } const reader = new FileReader(); - reader.onload = (loadEvent) => { + reader.onload = (_loadEvent) => { if (typeof reader.result === 'string' && reader.result.startsWith('data:image/')) { this.imageUrl = reader.result; this.safeImageUrl = this.sanitizer.bypassSecurityTrustUrl(this.imageUrl); + this.file = file; + this.fileName = fileName; this.updateModel(); } }; @@ -138,7 +174,15 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit, writeValue(value: string): void { this.imageUrl = value; if (this.imageUrl) { - this.safeImageUrl = this.sanitizer.bypassSecurityTrustUrl(this.imageUrl); + if (this.processImageApiLink) { + this.imagePipe.transform(this.imageUrl, {preview: true, ignoreLoadingImage: true}).subscribe( + (res) => { + this.safeImageUrl = res; + } + ); + } else { + this.safeImageUrl = this.sanitizer.bypassSecurityTrustUrl(this.imageUrl); + } } else { this.safeImageUrl = null; } @@ -146,12 +190,19 @@ export class ImageInputComponent extends PageComponent implements AfterViewInit, private updateModel() { this.cd.markForCheck(); - this.propagateChange(this.imageUrl); + if (this.resultAsFile) { + this.propagateChange(this.file); + } else { + this.propagateChange(this.imageUrl); + } + this.fileNameChanged.emit(this.fileName); } clearImage() { this.imageUrl = null; this.safeImageUrl = null; + this.file = null; + this.fileName = null; this.updateModel(); } } diff --git a/ui-ngx/src/app/shared/components/image/embed-image-dialog.component.html b/ui-ngx/src/app/shared/components/image/embed-image-dialog.component.html new file mode 100644 index 0000000000..bd12430e98 --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/embed-image-dialog.component.html @@ -0,0 +1,60 @@ + + +

{{ 'image.embed-image' | translate }}

+ + +
+ + +
+
+
+ + + +
+ +
{{ 'image.embed-to-html' | translate }}
+
+
+
image.embed-to-html
+
+
+ +
+ +
+
+
+
+
image.embed-to-angular-template
+
+ +
+
diff --git a/ui-ngx/src/app/shared/components/image/embed-image-dialog.component.scss b/ui-ngx/src/app/shared/components/image/embed-image-dialog.component.scss new file mode 100644 index 0000000000..b769de7840 --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/embed-image-dialog.component.scss @@ -0,0 +1,71 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + .mat-mdc-dialog-content { + display: flex; + flex-direction: column; + gap: 16px; + .tb-embed-image-text { + font-size: 14px; + font-weight: 400; + line-height: 20px; + color: rgba(0,0,0,0.54); + letter-spacing: 0.2px; + } + .tb-form-panel-title { + color: rgba(0, 0, 0, 0.87); + } + } +} + +:host ::ng-deep { + .tb-markdown-view { + max-width: 700px; + .tb-embed-image-code { + .code-wrapper { + padding: 0; + pre[class*=language-] { + margin: 0; + padding: 9px 38px 9px 16px; + } + code[class*="language-"], pre[class*="language-"] { + font-size: 12px; + overflow: hidden; + white-space: normal; + word-break: break-word; + } + button.clipboard-btn { + right: 0; + height: 36px; + p, div { + background: transparent; + } + p { + margin: 0; + padding: 6px; + font-size: 14px; + } + div { + top: 0; + padding: 8px; + height: 38px; + width: 38px; + } + } + } + } + } +} diff --git a/ui-ngx/src/app/shared/components/image/embed-image-dialog.component.ts b/ui-ngx/src/app/shared/components/image/embed-image-dialog.component.ts new file mode 100644 index 0000000000..b2c1d19e1f --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/embed-image-dialog.component.ts @@ -0,0 +1,94 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ImageResourceInfo } from '@shared/models/resource.models'; +import { Component, Inject, OnInit } from '@angular/core'; +import { DialogComponent } from '@shared/components/dialog.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { Router } from '@angular/router'; +import { ImageService } from '@core/http/image.service'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { FormControl, UntypedFormBuilder } from '@angular/forms'; + +export interface EmbedImageDialogData { + readonly: boolean; + image: ImageResourceInfo; +} + +@Component({ + selector: 'tb-embed-image-dialog', + templateUrl: './embed-image-dialog.component.html', + styleUrls: ['./embed-image-dialog.component.scss'] +}) +export class EmbedImageDialogComponent extends + DialogComponent implements OnInit { + + image = this.data.image; + + readonly = this.data.readonly; + + imageChanged = false; + + publicStatusControl = new FormControl(this.image.public); + + constructor(protected store: Store, + protected router: Router, + private imageService: ImageService, + @Inject(MAT_DIALOG_DATA) private data: EmbedImageDialogData, + public dialogRef: MatDialogRef, + public fb: UntypedFormBuilder) { + super(store, router, dialogRef); + } + + ngOnInit(): void { + if (!this.readonly) { + this.publicStatusControl.valueChanges.subscribe( + (isPublic) => { + this.updateImagePublicStatus(isPublic); + } + ); + } + } + + cancel(): void { + this.dialogRef.close(this.imageChanged ? this.image : null); + } + + embedToHtmlCode(): string { + return '```html\n' + + ''+this.image.title.replace(/' + + '{:copy-code}\n' + + '```'; + } + + embedToAngularTemplateCode(): string { + return '```html\n' + + '' + + '{:copy-code}\n' + + '```'; + } + + private updateImagePublicStatus(isPublic: boolean): void { + this.imageService.updateImagePublicStatus(this.image, isPublic).subscribe( + (image) => { + this.image = image; + this.imageChanged = true; + } + ); + } + +} diff --git a/ui-ngx/src/app/shared/components/image/gallery-image-input.component.html b/ui-ngx/src/app/shared/components/image/gallery-image-input.component.html new file mode 100644 index 0000000000..0ef4aa4bb0 --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/gallery-image-input.component.html @@ -0,0 +1,95 @@ + +
+ +
+
+ +
+
+
+
+
+ +
+ {{ imageResource.title }} +
+
+
{{ imageResource.descriptor.width }}x{{ imageResource.descriptor.height }}
+ +
{{ imageResource.descriptor.size | fileSize }}
+
+
+
+
+ + +
+ +
+
+ + +
+
+
+ + +
{{ (disabled ? 'image.no-image' : 'image.no-image-selected') | translate }}
+
+ + + diff --git a/ui-ngx/src/app/shared/components/image/gallery-image-input.component.scss b/ui-ngx/src/app/shared/components/image/gallery-image-input.component.scss new file mode 100644 index 0000000000..47ae1e96a9 --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/gallery-image-input.component.scss @@ -0,0 +1,191 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import "../../../../scss/constants"; + +$containerHeight: 96px !default; + +:host { + .tb-container { + margin-top: 0; + padding: 0 0 16px; + display: flex; + flex-direction: column; + gap: 8px; + label.tb-title { + display: block; + padding-bottom: 0; + } + } + + .tb-image-select-container { + width: 100%; + height: $containerHeight; + border-radius: 4px; + border: 1px solid rgba(0, 0, 0, 0.12); + display: flex; + align-items: center; + &.disabled { + width: $containerHeight; + .tb-image-container { + width: $containerHeight - 2px; + border-right: none; + } + } + } + + .tb-image-container { + width: $containerHeight - 1px; + height: $containerHeight - 2px; + display: flex; + justify-content: center; + align-items: center; + border-radius: 4px; + border-right: 1px solid rgba(0, 0, 0, 0.12); + background: #fff; + overflow: hidden; + } + + .tb-image-preview { + width: auto; + max-width: $containerHeight - 2px; + height: auto; + max-height: $containerHeight - 2px; + } + + .tb-no-image { + text-align: center; + color: rgba(0, 0, 0, 0.38); + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 16px; + letter-spacing: 0.4px; + } + + .tb-image-info-container { + display: flex; + flex: 1; + align-self: stretch; + padding: 0 8px; + justify-content: flex-end; + align-items: center; + gap: 4px; + .tb-base64-image-container, .tb-resource-image-container, .tb-external-image-container { + display: flex; + flex: 1; + align-self: stretch; + } + .tb-resource-image-container { + padding: 8px; + justify-content: center; + align-items: flex-start; + flex-direction: column; + gap: 4px; + .tb-resource-image-name { + color: rgba(0, 0, 0, 0.54); + overflow: hidden; + text-overflow: ellipsis; + -webkit-line-clamp: 2; + display: -webkit-box; + -webkit-box-orient: vertical; + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: 16px; + letter-spacing: 0.25px; + } + .tb-resource-image-details { + display: flex; + align-items: center; + gap: 8px; + color: rgba(0, 0, 0, 0.38); + font-size: 11px; + font-style: normal; + font-weight: 400; + line-height: 16px; + letter-spacing: 0.017px; + .mat-divider.mat-divider-vertical { + height: 16px; + } + } + &.loading { + align-items: center; + } + } + + .tb-external-image-container { + padding: 16px 8px 0 16px; + flex-direction: column; + align-items: flex-start; + gap: 4px; + .tb-external-link-label { + color: rgba(0, 0, 0, 0.54); + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 16px; + letter-spacing: 0.4px; + } + .tb-external-link-input-container { + display: flex; + justify-content: flex-end; + align-items: center; + gap: 4px; + align-self: stretch; + .tb-inline-field { + width: 100%; + } + } + } + + .tb-image-clear-btn { + color: rgba(0,0,0,0.38); + } + } + + .tb-image-select-buttons-container { + display: flex; + flex: 1; + align-self: stretch; + padding: 8px; + gap: 8px; + justify-content: center; + align-items: flex-start; + .tb-image-select-button { + width: 100%; + height: 100%; + align-self: stretch; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 4px; + padding: 8px; + line-height: normal; + font-size: 12px; + @media #{$mat-gt-xs} { + padding: 16px; + } + .mat-icon { + width: 24px; + height: 24px; + font-size: 24px; + margin: 0; + } + } + } +} diff --git a/ui-ngx/src/app/shared/components/image/gallery-image-input.component.ts b/ui-ngx/src/app/shared/components/image/gallery-image-input.component.ts new file mode 100644 index 0000000000..2a82ff1dc0 --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/gallery-image-input.component.ts @@ -0,0 +1,225 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + ChangeDetectorRef, + Component, + forwardRef, + Input, + OnDestroy, + OnInit, + Renderer2, + ViewContainerRef +} from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR, } from '@angular/forms'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { + extractParamsFromImageResourceUrl, + ImageResourceInfo, + isBase64DataImageUrl, + isImageResourceUrl, prependTbImagePrefix, removeTbImagePrefix +} from '@shared/models/resource.models'; +import { ImageService } from '@core/http/image.service'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { ImageGalleryComponent } from '@shared/components/image/image-gallery.component'; + +export enum ImageLinkType { + none = 'none', + base64 = 'base64', + external = 'external', + resource = 'resource' +} + +@Component({ + selector: 'tb-gallery-image-input', + templateUrl: './gallery-image-input.component.html', + styleUrls: ['./gallery-image-input.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => GalleryImageInputComponent), + multi: true + } + ] +}) +export class GalleryImageInputComponent extends PageComponent implements OnInit, OnDestroy, ControlValueAccessor { + + @Input() + label: string; + + @Input() + @coerceBoolean() + required = false; + + @Input() + disabled: boolean; + + imageUrl: string; + + imageResource: ImageResourceInfo; + + loadingImageResource = false; + + ImageLinkType = ImageLinkType; + + linkType: ImageLinkType = ImageLinkType.none; + + externalLinkControl = new FormControl(null); + + private propagateChange = null; + + constructor(protected store: Store, + private imageService: ImageService, + private cd: ChangeDetectorRef, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef, + private popoverService: TbPopoverService) { + super(store); + } + + ngOnInit() { + this.externalLinkControl.valueChanges.subscribe((value) => { + if (this.linkType === ImageLinkType.external) { + this.updateModel(value); + } + }); + } + + ngOnDestroy() { + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.detectLinkType(); + this.externalLinkControl.disable({emitEvent: false}); + } else { + this.externalLinkControl.enable({emitEvent: false}); + } + } + + writeValue(value: string): void { + value = removeTbImagePrefix(value); + if (this.imageUrl !== value) { + this.reset(); + this.imageUrl = value; + this.detectLinkType(); + if (this.linkType === ImageLinkType.resource) { + const params = extractParamsFromImageResourceUrl(this.imageUrl); + this.loadingImageResource = true; + this.imageService.getImageInfo(params.type, params.key, {ignoreLoading: true, ignoreErrors: true}).subscribe( + { + next: (res) => { + this.imageResource = res; + this.loadingImageResource = false; + this.cd.markForCheck(); + }, + error: () => { + this.reset(); + this.loadingImageResource = false; + this.cd.markForCheck(); + } + } + ); + } else if (this.linkType === ImageLinkType.base64) { + this.cd.markForCheck(); + } else if (this.linkType === ImageLinkType.external) { + this.externalLinkControl.setValue(this.imageUrl, {emitEvent: false}); + this.cd.markForCheck(); + } + } + } + + private detectLinkType() { + if (this.imageUrl) { + if (isImageResourceUrl(this.imageUrl)) { + this.linkType = ImageLinkType.resource; + } else if (isBase64DataImageUrl(this.imageUrl)) { + this.linkType = ImageLinkType.base64; + } else { + this.linkType = ImageLinkType.external; + } + } else { + this.linkType = ImageLinkType.none; + } + } + + private updateModel(value: string) { + this.cd.markForCheck(); + if (this.imageUrl !== value) { + this.imageUrl = value; + this.propagateChange(prependTbImagePrefix(this.imageUrl)); + } + } + + private reset() { + this.linkType = ImageLinkType.none; + this.imageResource = null; + this.externalLinkControl.setValue(null, {emitEvent: false}); + } + + clearImage() { + this.reset(); + this.updateModel(null); + } + + setLink($event: Event) { + if ($event) { + $event.stopPropagation(); + } + this.linkType = ImageLinkType.external; + } + + toggleGallery($event: Event, browseGalleryButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = browseGalleryButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const ctx: any = { + pageMode: false, + popoverMode: true, + mode: 'grid', + selectionMode: true + }; + const imageGalleryPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, ImageGalleryComponent, 'top', true, null, + ctx, + {}, + {}, {}, true); + imageGalleryPopover.tbComponentRef.instance.imageSelected.subscribe((image) => { + imageGalleryPopover.hide(); + this.linkType = ImageLinkType.resource; + this.imageResource = image; + this.updateModel(image.link); + }); + } + } + +} diff --git a/ui-ngx/src/app/shared/components/image/image-dialog.component.html b/ui-ngx/src/app/shared/components/image/image-dialog.component.html new file mode 100644 index 0000000000..5341c94ad5 --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/image-dialog.component.html @@ -0,0 +1,107 @@ + +
+ +

{{ (readonly ? 'image.image-details' : 'image.edit-image') | translate }}

+ + +
+ + +
+
+
+ + image.name + + + {{ 'image.name-required' | translate }} + + + + +
+
+
+
+ + + +
+ +
+
+
+ +
+
+
{{ image.descriptor.width }}x{{ image.descriptor.height }}
+ +
{{ image.descriptor.size | fileSize }}
+
+
+
+
+
+
diff --git a/ui-ngx/src/app/shared/components/image/image-dialog.component.scss b/ui-ngx/src/app/shared/components/image/image-dialog.component.scss new file mode 100644 index 0000000000..497714b6dd --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/image-dialog.component.scss @@ -0,0 +1,97 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import '../../../../scss/constants'; + +:host { + .tb-image-dialog { + @media #{$mat-gt-xs} { + width: 50vh; + } + .mat-mdc-dialog-content { + max-height: 100%; + } + fieldset { + height: 100%; + display: flex; + flex-direction: column; + } + } + .tb-image-container { + height: 100%; + border-radius: 4px; + border: 1px solid rgba(0, 0, 0, 0.05); + padding: 12px; + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 12px; + + .tb-image-content { + height: 100%; + width: 100%; + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; + .tb-image-preview-container { + position: relative; + width: 100%; + height: 100%; + border: 1px solid rgba(0, 0, 0, 0.12); + border-radius: 4px; + background: #fff; + display: flex; + align-items: center; + justify-content: center; + z-index: 1; + .tb-image-preview-spacer { + @media #{$mat-gt-xs} { + margin-top: 100%; + } + } + .tb-image-preview { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: contain; + } + } + .tb-image-preview-details { + display: flex; + align-items: center; + gap: 8px; + color: rgba(0, 0, 0, 0.38); + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; + .mat-divider.mat-divider-vertical { + height: 20px; + } + } + .tb-image-actions { + display: flex; + align-items: center; + align-self: stretch; + justify-content: space-between; + gap: 8px; + color: rgba(0,0,0,0.54); + } + } + } +} diff --git a/ui-ngx/src/app/shared/components/image/image-dialog.component.ts b/ui-ngx/src/app/shared/components/image/image-dialog.component.ts new file mode 100644 index 0000000000..d2783d59a0 --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/image-dialog.component.ts @@ -0,0 +1,163 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; +import { ErrorStateMatcher } from '@angular/material/core'; +import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { DialogComponent } from '@shared/components/dialog.component'; +import { Router } from '@angular/router'; +import { ImageService } from '@core/http/image.service'; +import { ImageResourceInfo, imageResourceType } from '@shared/models/resource.models'; +import { + UploadImageDialogComponent, + UploadImageDialogData +} from '@shared/components/image/upload-image-dialog.component'; +import { UrlHolder } from '@shared/pipe/image.pipe'; +import { ImportExportService } from '@shared/import-export/import-export.service'; +import { EmbedImageDialogComponent, EmbedImageDialogData } from '@shared/components/image/embed-image-dialog.component'; + +export interface ImageDialogData { + readonly: boolean; + image: ImageResourceInfo; +} + +@Component({ + selector: 'tb-image-dialog', + templateUrl: './image-dialog.component.html', + styleUrls: ['./image-dialog.component.scss'] +}) +export class ImageDialogComponent extends + DialogComponent implements OnInit { + + image: ImageResourceInfo; + + readonly: boolean; + + imageFormGroup: UntypedFormGroup; + + imageChanged = false; + + imagePreviewData: UrlHolder; + + constructor(protected store: Store, + protected router: Router, + private imageService: ImageService, + private dialog: MatDialog, + private importExportService: ImportExportService, + @Inject(MAT_DIALOG_DATA) private data: ImageDialogData, + public dialogRef: MatDialogRef, + public fb: UntypedFormBuilder) { + super(store, router, dialogRef); + this.image = data.image; + this.readonly = data.readonly; + this.imagePreviewData = { + url: this.image.public ? this.image.publicLink : this.image.link + }; + } + + ngOnInit(): void { + this.imageFormGroup = this.fb.group({ + title: [this.image.title, [Validators.required]] + }); + if (this.data.readonly) { + this.imageFormGroup.disable(); + } + } + + cancel(): void { + this.dialogRef.close(this.imageChanged ? this.image : null); + } + + revertInfo(): void { + this.imageFormGroup.get('title').setValue(this.image.title); + this.imageFormGroup.markAsPristine(); + } + + saveInfo(): void { + const title: string = this.imageFormGroup.get('title').value; + const image = {...this.image, ...{title}}; + this.imageService.updateImageInfo(image).subscribe( + (saved) => { + this.image = saved; + this.imageChanged = true; + this.imageFormGroup.markAsPristine(); + } + ); + } + + downloadImage($event) { + if ($event) { + $event.stopPropagation(); + } + this.imageService.downloadImage(imageResourceType(this.image), this.image.resourceKey).subscribe(); + } + + exportImage($event) { + if ($event) { + $event.stopPropagation(); + } + this.importExportService.exportImage(imageResourceType(this.image), this.image.resourceKey); + } + + embedImage($event: Event) { + if ($event) { + $event.stopPropagation(); + } + this.dialog.open(EmbedImageDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + image: this.image, + readonly: this.readonly + } + }).afterClosed().subscribe((result) => { + if (result) { + this.imageChanged = true; + this.image = result; + this.imagePreviewData = { + url: this.image.public ? this.image.publicLink : this.image.link + }; + } + }); + } + + updateImage($event): void { + if ($event) { + $event.stopPropagation(); + } + this.dialog.open(UploadImageDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + image: this.image + } + }).afterClosed().subscribe((result) => { + if (result) { + this.imageChanged = true; + this.image = result; + this.imagePreviewData = { + url: this.image.public ? this.image.publicLink : this.image.link + }; + } + }); + } + +} diff --git a/ui-ngx/src/app/shared/components/image/image-gallery.component.html b/ui-ngx/src/app/shared/components/image/image-gallery.component.html new file mode 100644 index 0000000000..78d2e4b788 --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/image-gallery.component.html @@ -0,0 +1,397 @@ + +
+
+ +
+
+ image.gallery +
+
+ +
+
+ +
+
+ {{ 'image.include-system-images' | translate }} +
+
+ +
+ + + + +
+ +
+
+ {{ 'image.include-system-images' | translate }} +
+ +
+ + +   + + + +
+
+ +
+ + {{ translate.get('image.selected-images', {count: dataSource?.selection.selected.length}) | async }} + + + +
+
+
+
+ + + + + + + + + + + + + + +
+ {{ image.title }} +
+
+
+ + {{ 'image.name' | translate }} + + {{ image.title }} + + + + {{ 'image.created-time' | translate }} + + {{ image.createdTime | date:'yyyy-MM-dd HH:mm:ss' }} + + + + {{ 'image.resolution' | translate }} + + {{ image.descriptor.width }}x{{ image.descriptor.height }} + + + + {{ 'image.size' | translate }} + + {{ image.descriptor.size | fileSize }} + + + + {{ 'image.system' | translate }} + + {{isSystem(image) ? 'check_box' : 'check_box_outline_blank'}} + + + + + + +
+ + + + + +
+
+ + + + + + + + +
+
+
+ + + + + + + + +
+ + + + {{ 'common.loading' | translate }} +
+ + +
+
+ + +
+
+
+ +
+ + {{ 'common.loading' | translate }} + +
+
+ +
+
+
image.no-images
+
+
+ +
+
+
+ + + + +
+
+
+
+ + +
+
+ {{ item.title }} +
+
+
+
+ {{ item.title }} +
+
sys
+
+
+
{{ item.descriptor.width }}x{{ item.descriptor.height }}
+ +
{{ item.descriptor.size | fileSize }}
+
+
+
+
+ +
+
+
+
+
+
+
+
diff --git a/ui-ngx/src/app/shared/components/image/image-gallery.component.scss b/ui-ngx/src/app/shared/components/image/image-gallery.component.scss new file mode 100644 index 0000000000..6272b0d42d --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/image-gallery.component.scss @@ -0,0 +1,299 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import "../../../../scss/constants"; + +$tb-button-selected-color: rgb(255, 110, 64) !default; + +.tb-images { + + &.tb-popover-mode { + padding-top: 8px; + position: relative; + width: 300px; + height: 500px; + max-width: calc(100vw - 50px); + max-height: calc(100vh - 100px); + @media #{$mat-gt-xs} { + width: 500px; + height: 600px; + } + @media #{$mat-gt-sm} { + width: 700px; + height: 600px; + } + @media #{$mat-gt-md} { + width: 1000px; + height: 700px; + } + @media #{$mat-gt-xmd} { + width: 1300px; + height: 700px; + } + @media #{$mat-gt-xl} { + width: 1600px; + height: 800px; + } + @media screen and (min-width: 2320px) { + width: 2000px; + height: 800px; + } + } + + .tb-images-content { + width: 100%; + height: 100%; + background: #fff; + overflow: hidden; + + &.tb-outlined-border { + box-shadow: 0 0 0 0 rgb(0 0 0 / 20%), 0 0 0 0 rgb(0 0 0 / 14%), 0 0 0 0 rgb(0 0 0 / 12%); + border: solid 1px #e0e0e0; + border-radius: 4px; + } + + .mat-mdc-table-toolbar { + &.multi-row { + &.mat-toolbar-single-row { + height: 112px; + } + .mat-mdc-slide-toggle { + display: flex; + min-height: 48px; + } + } + } + + .tb-images-title { + padding-right: 20px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .tb-images-view-type-toolbar { + height: 55px; + min-height: 55px; + padding-right: 16px; + background-color: transparent; + box-shadow: none; + + .tb-toolbar-button { + height: 48px; + button.mat-mdc-icon-button { + margin: 0; + } + &.tb-selected { + background-color: rgba(255, 255, 255, .15); + border-bottom: $tb-button-selected-color solid 4px; + + button.mat-mdc-icon-button { + margin-bottom: -4px; + + .mat-icon { + color: $tb-button-selected-color; + fill: $tb-button-selected-color; + } + } + } + } + } + + .table-container, tb-scroll-grid { + position: relative; + } + + .tb-no-images { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + + .tb-image-card { + position: relative; + height: 100%; + border-radius: 4px; + box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.24); + padding: 8px; + display: flex; + gap: 8px; + flex-direction: column; + cursor: pointer; + + .tb-image-card-overlay { + position: absolute; + pointer-events: none; + inset: 0; + border-radius: 4px; + z-index: 2; + display: flex; + flex-direction: column; + .tb-image-card-overlay-buttons { + width: 100%; + display: flex; + justify-content: flex-end; + align-items: center; + color: rgba(0,0,0,0.78); + opacity: 0; + transition: opacity 0.5s; + } + } + + &:hover { + .tb-image-card-overlay { + .tb-image-card-overlay-buttons { + opacity: 1; + } + } + .tb-image-preview-container { + .tb-image-preview-overlay { + background: rgba(245,245,245,0.6); + backdrop-filter: blur(4px); + .mdc-button { + opacity: 1; + } + } + } + } + + .tb-image-preview-container { + position: relative; + .tb-image-preview-overlay { + position: absolute; + inset: 0; + z-index: 1; + background: rgba(245,245,245,0); + backdrop-filter: none; + transition: all 0.5s; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + .mdc-button { + opacity: 0; + transition: opacity 0.5s; + } + } + .tb-image-preview-spacer { + margin-top: 100%; + } + .tb-image-preview { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + } + } + + .tb-image-details { + min-height: 52px; + flex: 1; + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: space-between; + gap: 4px; + align-self: stretch; + .tb-image-title-container { + display: flex; + align-items: center; + gap: 8px; + align-self: stretch; + .tb-image-title { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + -webkit-line-clamp: 2; + display: -webkit-box; + -webkit-box-orient: vertical; + color: rgba(0, 0, 0, 0.76); + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: 16px; + } + .tb-image-sys { + padding: 1px 4px; + border-radius: 4px; + background: rgba(236, 236, 236, 0.64); + color: rgba(0, 0, 0, 0.54); + font-size: 11px; + font-style: normal; + font-weight: 500; + line-height: 16px; + letter-spacing: 0.017px; + } + } + .tb-image-info-container { + display: flex; + align-items: center; + gap: 8px; + align-self: stretch; + color: rgba(0, 0, 0, 0.38); + font-size: 10px; + font-style: normal; + font-weight: 400; + line-height: 16px; + .mat-divider.mat-divider-vertical { + height: 16px; + } + } + } + &.loading-cell { + .tb-image-preview-container, .tb-image-details { + background: linear-gradient(110deg, #ececec 8%, #f5f5f5 18%, #ececec 33%); + border-radius: 5px; + background-size: 200% 100%; + animation: 1s shine linear infinite; + } + } + } + + .table-container { + overflow: auto; + + .mat-sort-header-sorted .mat-sort-header-arrow { + opacity: 1 !important; + } + .mat-mdc-cell.mat-column-preview { + width: 50px; + height: 50px; + padding: 2px 12px; + } + } + + .tb-image-preview-cell { + width: 50px; + height: 50px; + } + + .tb-image-preview { + width: 50px; + height: 50px; + object-fit: contain; + border-radius: 4px; + } + } +} + +@keyframes shine { + to { + background-position-x: -200%; + } +} diff --git a/ui-ngx/src/app/shared/components/image/image-gallery.component.ts b/ui-ngx/src/app/shared/components/image/image-gallery.component.ts new file mode 100644 index 0000000000..05b1d2e638 --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/image-gallery.component.ts @@ -0,0 +1,699 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + ImageResourceInfo, + ImageResourceInfoWithReferences, + imageResourceType, + toImageDeleteResult +} from '@shared/models/resource.models'; +import { forkJoin, merge, Observable, of, Subject, Subscription } from 'rxjs'; +import { ImageService } from '@core/http/image.service'; +import { TranslateService } from '@ngx-translate/core'; +import { PageLink, PageQueryParam } from '@shared/models/page/page-link'; +import { catchError, debounceTime, distinctUntilChanged, map, skip, takeUntil } from 'rxjs/operators'; +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, EventEmitter, HostBinding, + Input, + OnDestroy, + OnInit, Output, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort, SortDirection } from '@angular/material/sort'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { DialogService } from '@core/services/dialog.service'; +import { FormBuilder } from '@angular/forms'; +import { Direction, SortOrder } from '@shared/models/page/sort-order'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { hidePageSizePixelValue } from '@shared/models/constants'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { ActivatedRoute, QueryParamsHandling, Router } from '@angular/router'; +import { isEqual, isNotEmptyStr, parseHttpErrorMessage } from '@core/utils'; +import { BaseData, HasId } from '@shared/models/base-data'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { getCurrentAuthUser } from '@core/auth/auth.selectors'; +import { Authority } from '@shared/models/authority.enum'; +import { GridEntitiesFetchFunction, ScrollGridColumns } from '@shared/components/grid/scroll-grid-datasource'; +import { ItemSizeStrategy, ScrollGridComponent } from '@shared/components/grid/scroll-grid.component'; +import { MatDialog } from '@angular/material/dialog'; +import { + UploadImageDialogComponent, + UploadImageDialogData +} from '@shared/components/image/upload-image-dialog.component'; +import { ImageDialogComponent, ImageDialogData } from '@shared/components/image/image-dialog.component'; +import { ImportExportService } from '@shared/import-export/import-export.service'; +import { ActionNotificationShow } from '@core/notification/notification.actions'; +import { + ImagesInUseDialogComponent, + ImagesInUseDialogData +} from '@shared/components/image/images-in-use-dialog.component'; +import { ImagesDatasource } from '@shared/components/image/images-datasource'; +import { EmbedImageDialogComponent, EmbedImageDialogData } from '@shared/components/image/embed-image-dialog.component'; + +interface GridImagesFilter { + search: string; + includeSystemImages: boolean; +} + +const pageGridColumns: ScrollGridColumns = { + columns: 2, + breakpoints: { + 'screen and (min-width: 2320px)': 10, + 'screen and (min-width: 2000px)': 8, + 'gt-lg': 7, + 'screen and (min-width: 1600px)': 6, + 'gt-md': 5, + 'screen and (min-width: 1120px)': 4, + 'gt-xs': 3 + } +}; + +const popoverGridColumns: ScrollGridColumns = { + columns: 1, + breakpoints: { + 'screen and (min-width: 2320px)': 8, + 'gt-lg': 6, + 'screen and (min-width: 1600px)': 5, + 'gt-md': 4, + 'gt-sm': 3, + 'gt-xs': 2 + } +}; + +@Component({ + selector: 'tb-image-gallery', + templateUrl: './image-gallery.component.html', + styleUrls: ['./image-gallery.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class ImageGalleryComponent extends PageComponent implements OnInit, OnDestroy, AfterViewInit { + + @HostBinding('style.display') + private display = 'block'; + + @Input() + @coerceBoolean() + pageMode = true; + + @Input() + @coerceBoolean() + popoverMode = false; + + @Input() + mode: 'list' | 'grid' = 'list'; + + @Input() + selectionMode = false; + + @Output() + imageSelected = new EventEmitter(); + + @ViewChild('searchInput') searchInputField: ElementRef; + + @ViewChild(MatPaginator) paginator: MatPaginator; + @ViewChild(MatSort) sort: MatSort; + + @ViewChild(ScrollGridComponent) gridComponent: ScrollGridComponent; + + defaultPageSize = 10; + defaultSortOrder: SortOrder = { property: 'createdTime', direction: Direction.DESC }; + hidePageSize = false; + + displayedColumns: string[]; + pageSizeOptions: number[]; + pageLink: PageLink; + + textSearchMode = false; + + dataSource: ImagesDatasource; + + textSearch = this.fb.control('', {nonNullable: true}); + includeSystemImages = false; + + gridColumns: ScrollGridColumns; + + gridImagesFetchFunction: GridEntitiesFetchFunction; + gridImagesFilter: GridImagesFilter = { + search: '', + includeSystemImages: false + }; + + gridImagesItemSizeStrategy: ItemSizeStrategy = { + defaultItemSize: 200, + itemSizeFunction: itemWidth => itemWidth + 72 + }; + + authUser = getCurrentAuthUser(this.store); + + private updateDataSubscription: Subscription; + + private widgetResize$: ResizeObserver; + private destroy$ = new Subject(); + private destroyListMode$: Subject; + + constructor(protected store: Store, + private route: ActivatedRoute, + private router: Router, + private dialog: MatDialog, + public translate: TranslateService, + private imageService: ImageService, + private dialogService: DialogService, + private importExportService: ImportExportService, + private elementRef: ElementRef, + private cd: ChangeDetectorRef, + private fb: FormBuilder) { + super(store); + + this.gridImagesFetchFunction = (pageSize, page, filter) => { + const pageLink = new PageLink(pageSize, page, filter.search, { + property: 'createdTime', + direction: Direction.DESC + }); + return this.imageService.getImages(pageLink, filter.includeSystemImages); + }; + } + + ngOnInit(): void { + this.gridColumns = this.popoverMode ? popoverGridColumns : pageGridColumns; + this.displayedColumns = this.computeDisplayedColumns(); + let sortOrder: SortOrder = this.defaultSortOrder; + this.pageSizeOptions = [this.defaultPageSize, this.defaultPageSize * 2, this.defaultPageSize * 3]; + const routerQueryParams: PageQueryParam = this.route.snapshot.queryParams; + if (this.pageMode) { + if (routerQueryParams.hasOwnProperty('direction') + || routerQueryParams.hasOwnProperty('property')) { + sortOrder = { + property: routerQueryParams?.property || this.defaultSortOrder.property, + direction: routerQueryParams?.direction || this.defaultSortOrder.direction + }; + } + } + this.pageLink = new PageLink(this.defaultPageSize, 0, null, sortOrder); + if (this.pageMode) { + if (routerQueryParams.hasOwnProperty('page')) { + this.pageLink.page = Number(routerQueryParams.page); + } + if (routerQueryParams.hasOwnProperty('pageSize')) { + this.pageLink.pageSize = Number(routerQueryParams.pageSize); + } + const textSearchParam = routerQueryParams.textSearch; + if (isNotEmptyStr(textSearchParam)) { + const decodedTextSearch = decodeURI(textSearchParam); + this.textSearchMode = true; + this.pageLink.textSearch = decodedTextSearch.trim(); + this.textSearch.setValue(decodedTextSearch, {emitEvent: false}); + } + } + if (this.mode === 'list') { + this.dataSource = new ImagesDatasource(this.imageService, null, + entity => this.deleteEnabled(entity)); + } + } + + ngOnDestroy(): void { + if (this.widgetResize$) { + this.widgetResize$.disconnect(); + } + if (this.destroyListMode$) { + this.destroyListMode$.next(); + this.destroyListMode$.complete(); + } + this.destroy$.next(); + this.destroy$.complete(); + } + + ngAfterViewInit() { + this.textSearch.valueChanges.pipe( + debounceTime(150), + distinctUntilChanged((prev, current) => + ((this.mode === 'list' ? this.pageLink.textSearch : this.gridImagesFilter.search) ?? '') === current.trim()), + takeUntil(this.destroy$) + ).subscribe(value => { + if (this.mode === 'list') { + if (this.pageMode) { + const queryParams: PageQueryParam = { + textSearch: isNotEmptyStr(value) ? encodeURI(value) : null, + page: null + }; + this.updatedRouterParamsAndData(queryParams); + } else { + this.pageLink.textSearch = isNotEmptyStr(value) ? value.trim() : null; + this.paginator.pageIndex = 0; + this.updateData(); + } + } else { + this.gridImagesFilter = { + search: isNotEmptyStr(value) ? value.trim() : null, + includeSystemImages: this.includeSystemImages + }; + this.cd.markForCheck(); + } + }); + this.updateMode(); + } + + public includeSystemImagesChanged(value: boolean) { + this.includeSystemImages = value; + this.displayedColumns = this.computeDisplayedColumns(); + this.gridImagesFilter = { + search: this.gridImagesFilter.search, + includeSystemImages: this.includeSystemImages + }; + if (this.mode === 'list') { + this.paginator.pageIndex = 0; + this.updateData(); + } else { + this.cd.markForCheck(); + } + } + + public setMode(targetMode: 'list' | 'grid') { + if (this.mode !== targetMode) { + if (this.widgetResize$) { + this.widgetResize$.disconnect(); + this.widgetResize$ = null; + } + if (this.destroyListMode$) { + this.destroyListMode$.next(); + this.destroyListMode$.complete(); + this.destroyListMode$ = null; + } + this.mode = targetMode; + if (this.mode === 'list') { + this.dataSource = new ImagesDatasource(this.imageService, null, + entity => this.deleteEnabled(entity)); + } + setTimeout(() => { + this.updateMode(); + }); + } + } + + public get isSysAdmin(): boolean { + return this.authUser.authority === Authority.SYS_ADMIN; + } + + private computeDisplayedColumns(): string[] { + let columns: string[]; + if (this.selectionMode) { + columns = ['preview', 'title']; + if (!this.isSysAdmin && this.includeSystemImages) { + columns.push('system'); + } + columns.push('imageSelect'); + } else { + columns = ['select', 'preview', 'title', 'createdTime', 'resolution', 'size']; + if (!this.isSysAdmin && this.includeSystemImages) { + columns.push('system'); + } + columns.push('actions'); + } + return columns; + } + + private updateMode() { + if (this.mode === 'list') { + this.initListMode(); + } else { + this.initGridMode(); + } + } + + private initListMode() { + this.destroyListMode$ = new Subject(); + this.widgetResize$ = new ResizeObserver(() => { + const showHidePageSize = this.elementRef.nativeElement.offsetWidth < hidePageSizePixelValue; + if (showHidePageSize !== this.hidePageSize) { + this.hidePageSize = showHidePageSize; + this.cd.markForCheck(); + } + }); + this.widgetResize$.observe(this.elementRef.nativeElement); + if (this.pageMode) { + this.route.queryParams.pipe( + skip(1), + takeUntil(this.destroyListMode$) + ).subscribe((params: PageQueryParam) => { + this.paginator.pageIndex = Number(params.page) || 0; + this.paginator.pageSize = Number(params.pageSize) || this.defaultPageSize; + this.sort.active = params.property || this.defaultSortOrder.property; + this.sort.direction = (params.direction || this.defaultSortOrder.direction).toLowerCase() as SortDirection; + const textSearchParam = params.textSearch; + if (isNotEmptyStr(textSearchParam)) { + const decodedTextSearch = decodeURI(textSearchParam); + this.textSearchMode = true; + this.pageLink.textSearch = decodedTextSearch.trim(); + this.textSearch.setValue(decodedTextSearch, {emitEvent: false}); + } else { + this.pageLink.textSearch = null; + this.textSearch.reset('', {emitEvent: false}); + } + this.updateData(); + }); + } + this.updatePaginationSubscriptions(); + this.updateData(); + } + + private initGridMode() { + + } + + private updatePaginationSubscriptions() { + if (this.updateDataSubscription) { + this.updateDataSubscription.unsubscribe(); + this.updateDataSubscription = null; + } + const sortSubscription$: Observable = this.sort.sortChange.asObservable().pipe( + map((data) => { + const direction = data.direction.toUpperCase(); + const queryParams: PageQueryParam = { + direction: (this.defaultSortOrder.direction === direction ? null : direction) as Direction, + property: this.defaultSortOrder.property === data.active ? null : data.active + }; + queryParams.page = null; + this.paginator.pageIndex = 0; + return queryParams; + }) + ); + const paginatorSubscription$ = this.paginator.page.asObservable().pipe( + map((data) => ({ + page: data.pageIndex === 0 ? null : data.pageIndex, + pageSize: data.pageSize === this.defaultPageSize ? null : data.pageSize + })) + ); + this.updateDataSubscription = (merge(sortSubscription$, paginatorSubscription$) as Observable).pipe( + takeUntil(this.destroyListMode$) + ).subscribe(queryParams => this.updatedRouterParamsAndData(queryParams)); + } + + clearSelection() { + this.dataSource.selection.clear(); + this.cd.detectChanges(); + } + + updateData() { + if (this.mode === 'list') { + this.pageLink.page = this.paginator.pageIndex; + this.pageLink.pageSize = this.paginator.pageSize; + if (this.sort.active) { + this.pageLink.sortOrder = { + property: this.sort.active, + direction: Direction[this.sort.direction.toUpperCase()] + }; + } else { + this.pageLink.sortOrder = null; + } + this.dataSource.loadEntities(this.pageLink, this.includeSystemImages); + } else { + this.gridComponent.update(); + } + } + + private imageUpdated(image: ImageResourceInfo, index = -1) { + if (this.mode === 'list') { + this.updateData(); + } else { + this.gridComponent.updateItem(index, image); + } + } + + private imageDeleted(index = -1) { + if (this.mode === 'list') { + this.updateData(); + } else { + this.gridComponent.deleteItem(index); + } + } + + enterFilterMode() { + this.textSearchMode = true; + setTimeout(() => { + this.searchInputField.nativeElement.focus(); + this.searchInputField.nativeElement.setSelectionRange(0, 0); + }, 10); + } + + exitFilterMode() { + this.textSearchMode = false; + this.textSearch.reset(); + } + + trackByEntity(index: number, entity: BaseData) { + return entity; + } + + isSystem(image?: ImageResourceInfo): boolean { + return !this.isSysAdmin && image?.tenantId?.id === NULL_UUID; + } + + readonly(image?: ImageResourceInfo): boolean { + return this.authUser.authority !== Authority.SYS_ADMIN && this.isSystem(image); + } + + deleteEnabled(image?: ImageResourceInfo): boolean { + return this.authUser.authority === Authority.SYS_ADMIN || !this.isSystem(image); + } + + deleteImage($event: Event, image: ImageResourceInfo, itemIndex = -1) { + if ($event) { + $event.stopPropagation(); + } + const title = this.translate.instant('image.delete-image-title', {imageTitle: image.title}); + const content = this.translate.instant('image.delete-image-text'); + this.dialogService.confirm(title, content, + this.translate.instant('action.no'), + this.translate.instant('action.yes')).subscribe((result) => { + if (result) { + this.imageService.deleteImage(imageResourceType(image), image.resourceKey, false, {ignoreErrors: true}).pipe( + map(() => toImageDeleteResult(image)), + catchError((err) => of(toImageDeleteResult(image, err))) + ).subscribe( + (deleteResult) => { + if (deleteResult.success) { + this.imageDeleted(itemIndex); + } else if (deleteResult.imageIsReferencedError) { + this.dialog.open(ImagesInUseDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + multiple: false, + images: [{...image, ...{references: deleteResult.references}}] + } + }).afterClosed().subscribe((images) => { + if (images) { + this.imageService.deleteImage(imageResourceType(image), image.resourceKey, true).subscribe( + () => { + this.imageDeleted(itemIndex); + } + ); + } + }); + } else { + const errorMessageWithTimeout = parseHttpErrorMessage(deleteResult.error, this.translate); + setTimeout(() => { + this.store.dispatch(new ActionNotificationShow({message: errorMessageWithTimeout.message, type: 'error'})); + }, errorMessageWithTimeout.timeout); + } + }); + } + }); + } + + deleteImages($event: Event) { + if ($event) { + $event.stopPropagation(); + } + const selectedImages = this.dataSource.selection.selected; + if (selectedImages && selectedImages.length) { + const title = this.translate.instant('image.delete-images-title', {count: selectedImages.length}); + const content = this.translate.instant('image.delete-images-text'); + this.dialogService.confirm(title, content, + this.translate.instant('action.no'), + this.translate.instant('action.yes')).subscribe((result) => { + if (result) { + const tasks = selectedImages.map((image) => + this.imageService.deleteImage(imageResourceType(image), image.resourceKey, false, {ignoreErrors: true}).pipe( + map(() => toImageDeleteResult(image)), + catchError((err) => of(toImageDeleteResult(image, err))) + ) + ); + forkJoin(tasks).subscribe( + (deleteResults) => { + const anySuccess = deleteResults.some(res => res.success); + const referenceErrors = deleteResults.filter(res => res.imageIsReferencedError); + const otherError = deleteResults.find(res => !res.success); + if (anySuccess) { + this.updateData(); + } + if (referenceErrors?.length) { + const imagesWithReferences: ImageResourceInfoWithReferences[] = + referenceErrors.map(ref => ({...ref.image, ...{references: ref.references}})); + this.dialog.open(ImagesInUseDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + multiple: true, + images: imagesWithReferences + } + }).afterClosed().subscribe((forceDeleteImages) => { + if (forceDeleteImages && forceDeleteImages.length) { + const forceDeleteTasks = forceDeleteImages.map((image) => + this.imageService.deleteImage(imageResourceType(image), image.resourceKey, true) + ); + forkJoin(forceDeleteTasks).subscribe( + () => { + this.updateData(); + } + ); + } + }); + } else if (otherError) { + const errorMessageWithTimeout = parseHttpErrorMessage(otherError.error, this.translate); + setTimeout(() => { + this.store.dispatch(new ActionNotificationShow({message: errorMessageWithTimeout.message, type: 'error'})); + }, errorMessageWithTimeout.timeout); + } + } + ); + } + }); + } + } + + downloadImage($event, image: ImageResourceInfo) { + if ($event) { + $event.stopPropagation(); + } + this.imageService.downloadImage(imageResourceType(image), image.resourceKey).subscribe(); + } + + exportImage($event, image: ImageResourceInfo) { + if ($event) { + $event.stopPropagation(); + } + this.importExportService.exportImage(imageResourceType(image), image.resourceKey); + } + + importImage(): void { + this.importExportService.importImage().subscribe((image) => { + if (image) { + if (this.selectionMode) { + this.imageSelected.next(image); + } else { + this.updateData(); + } + } + }); + } + + selectImage($event, image: ImageResourceInfo) { + if ($event) { + $event.stopPropagation(); + } + this.imageSelected.next(image); + } + + rowClick($event, image: ImageResourceInfo) { + if (this.selectionMode) { + this.selectImage($event, image); + } else { + if (this.deleteEnabled(image)) { + this.dataSource.selection.toggle(image); + } + } + } + + uploadImage(): void { + this.dialog.open(UploadImageDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: {} + }).afterClosed().subscribe((result) => { + if (result) { + if (this.selectionMode) { + this.imageSelected.next(result); + } else { + this.updateData(); + } + } + }); + } + + editImage($event: Event, image: ImageResourceInfo, itemIndex = -1) { + if ($event) { + $event.stopPropagation(); + } + this.dialog.open(ImageDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + image, + readonly: this.readonly(image) + } + }).afterClosed().subscribe((result) => { + if (result) { + this.imageUpdated(result, itemIndex); + } + }); + } + + embedImage($event: Event, image: ImageResourceInfo, itemIndex = -1) { + if ($event) { + $event.stopPropagation(); + } + this.dialog.open(EmbedImageDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + image, + readonly: this.readonly(image) + } + }).afterClosed().subscribe((result) => { + if (result) { + this.imageUpdated(result, itemIndex); + } + }); + } + + protected updatedRouterParamsAndData(queryParams: object, queryParamsHandling: QueryParamsHandling = 'merge') { + if (this.pageMode) { + this.router.navigate([], { + relativeTo: this.route, + queryParams, + queryParamsHandling + }); + if (queryParamsHandling === '' && isEqual(this.route.snapshot.queryParams, queryParams)) { + this.updateData(); + } + } else { + this.updateData(); + } + } + +} diff --git a/ui-ngx/src/app/shared/components/image/image-references.component.html b/ui-ngx/src/app/shared/components/image/image-references.component.html new file mode 100644 index 0000000000..611d744cfa --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/image-references.component.html @@ -0,0 +1,54 @@ + + + + + + +
    + +
  • +
    {{ 'image.system-entities' | translate }}
    + +
  • + +
  • +
    + {{ 'tenant.tenant' | translate }} {{ entry[1].tenantName }} {{ 'image.entities' | translate }} +
    + +
  • +
    +
    +
+
+
+ + + + + + +
{{ referencedEntity.typeName }} + {{ referencedEntity.entity.name }} + {{ referencedEntity.entity.name }} +
+
+ + + diff --git a/ui-ngx/src/app/shared/components/image/image-references.component.scss b/ui-ngx/src/app/shared/components/image/image-references.component.scss new file mode 100644 index 0000000000..4f69db0add --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/image-references.component.scss @@ -0,0 +1,92 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import "../../../../scss/constants"; + +:host { + ul.tb-references { + padding: 0; + margin: 0; + display: flex; + flex-direction: column; + gap: 12px; + } + li.tb-entities-container { + padding: 8px 12px 12px 12px; + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; + border-radius: 8px; + } + a.tb-reference { + color: $tb-primary-color; + font-weight: inherit; + } + .tb-entities-title { + display: flex; + gap: 8px; + color: rgba(0, 0, 0, 0.87); + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: 24px; + letter-spacing: 0.25px; + } + table.tb-entities-list-table { + position: relative; + overflow: hidden; + padding: 8px 10px 8px 4px; + border-radius: 4px; + background: #fff; + align-self: stretch; + z-index: 1; + color: rgba(0, 0, 0, 0.76); + &:before { + display: block; + height: auto; + content: ""; + position: absolute; + inset: 0; + border-radius: 4px; + border: 1px solid $tb-primary-color; + background: transparent; + opacity: 0.4; + pointer-events: none; + } + td.tb-entity-type { + white-space: nowrap; + padding-right: 20px; + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: 20px; + letter-spacing: 0.25px; + &:before { + content: "•"; + padding-left: 8px; + padding-right: 8px; + } + } + td.tb-entity-name { + width: 100%; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 20px; + letter-spacing: 0.2px; + } + } +} diff --git a/ui-ngx/src/app/shared/components/image/image-references.component.ts b/ui-ngx/src/app/shared/components/image/image-references.component.ts new file mode 100644 index 0000000000..b236c6d47c --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/image-references.component.ts @@ -0,0 +1,171 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { ImageReferences } from '@shared/models/resource.models'; +import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; +import { TranslateService } from '@ngx-translate/core'; +import { getEntityDetailsPageURL } from '@core/utils'; +import { getCurrentAuthUser } from '@core/auth/auth.selectors'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { Authority } from '@shared/models/authority.enum'; +import { Observable } from 'rxjs'; +import { EntityService } from '@core/http/entity.service'; +import { BaseData, HasId } from '@shared/models/base-data'; +import { HasTenantId } from '@shared/models/entity.models'; +import { map } from 'rxjs/operators'; +import { TbPopoverComponent } from '@shared/components/popover.component'; + +interface ReferencedEntityInfo { + entity: BaseData & HasTenantId; + typeName: string; + detailsUrl: string; +} + +interface TenantReferencedEntities { + tenantName?: string; + tenantDetailsUrl?: string; + entities: ReferencedEntityInfo[]; +} + +type ReferencedEntities = {[tenantId: string]: TenantReferencedEntities}; +type ReferencedEntitiesEntry = [string, TenantReferencedEntities]; + +@Component({ + selector: 'tb-image-references', + templateUrl: './image-references.component.html', + styleUrls: ['./image-references.component.scss'] +}) +export class ImageReferencesComponent implements OnInit { + + @Input() + references: ImageReferences; + + popoverComponent: TbPopoverComponent; + + contentReady = false; + + authUser = getCurrentAuthUser(this.store); + + simpleList = true; + + referencedEntitiesList: ReferencedEntityInfo[]; + + referencedEntitiesEntries: ReferencedEntitiesEntry[]; + + constructor(protected store: Store, + private entityService: EntityService, + private cd: ChangeDetectorRef, + private translate: TranslateService) { + } + + ngOnInit(): void { + if (this.authUser.authority === Authority.SYS_ADMIN && this.hasNonSystemEntities(this.references)) { + this.simpleList = false; + this.toReferencedEntitiesEntries(this.references).subscribe( + (entries) => { + this.referencedEntitiesEntries = entries; + this.contentReady = true; + this.cd.detectChanges(); + if (this.popoverComponent) { + Promise.resolve().then(() => { + this.popoverComponent.updatePosition(); + }); + } + } + ); + } else { + this.referencedEntitiesList = this.toReferencedEntitiesList(this.references); + this.contentReady = true; + } + } + + isSystem(tenantId: string): boolean { + return tenantId === NULL_UUID; + } + + private hasNonSystemEntities(references: ImageReferences): boolean { + for (const entityTypeStr of Object.keys(references)) { + const entities = this.references[entityTypeStr]; + if (entities.some(e => e.tenantId && e.tenantId.id && e.tenantId.id !== NULL_UUID)) { + return true; + } + } + return false; + } + + private toReferencedEntitiesList(references: ImageReferences): ReferencedEntityInfo[] { + const result: ReferencedEntityInfo[] = []; + for (const entityTypeStr of Object.keys(references)) { + const entityType = entityTypeStr as EntityType; + const entityTypeName = this.translate.instant(entityTypeTranslations.get(entityType).type); + const entities = references[entityTypeStr]; + for (const entity of entities) { + const detailsUrl = getEntityDetailsPageURL(entity.id.id, entityType); + result.push({ + entity, + typeName: entityTypeName, + detailsUrl + }); + } + } + return result; + } + + private toReferencedEntitiesEntries(references: ImageReferences): Observable { + let referencedEntities: ReferencedEntities = {}; + const referencedEntitiesList = this.toReferencedEntitiesList(references); + for (const referencedEntityInfo of referencedEntitiesList) { + const tenantId = referencedEntityInfo.entity.tenantId?.id || NULL_UUID; + let tenantEntitiesInfo = referencedEntities[tenantId]; + if (!tenantEntitiesInfo) { + tenantEntitiesInfo = { + entities: [] + }; + referencedEntities[tenantId] = tenantEntitiesInfo; + } + tenantEntitiesInfo.entities.push(referencedEntityInfo); + } + referencedEntities = Object.keys(referencedEntities).sort((tenantId1, tenantId2) => { + if (tenantId1 === NULL_UUID) { + return -1; + } else if (tenantId2 === NULL_UUID) { + return 1; + } + return 0; + }).reduce( + (obj, key) => { + obj[key] = referencedEntities[key]; + return obj; + }, + {} + ); + const tenantIds = Object.keys(referencedEntities).filter(id => id !== NULL_UUID); + return this.entityService.getEntities(EntityType.TENANT, tenantIds).pipe( + map((tenants) => { + for (const tenant of tenants) { + const tenantEntitiesInfo = referencedEntities[tenant.id.id]; + tenantEntitiesInfo.tenantName = tenant.name; + tenantEntitiesInfo.tenantDetailsUrl = getEntityDetailsPageURL(tenant.id.id, EntityType.TENANT); + } + return Object.entries(referencedEntities); + }) + ); + } + +} diff --git a/ui-ngx/src/app/shared/components/image/images-datasource.ts b/ui-ngx/src/app/shared/components/image/images-datasource.ts new file mode 100644 index 0000000000..8be2f30cc0 --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/images-datasource.ts @@ -0,0 +1,130 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { CollectionViewer, DataSource, SelectionModel } from '@angular/cdk/collections'; +import { ImageResourceInfo } from '@shared/models/resource.models'; +import { BehaviorSubject, Observable, of, ReplaySubject, Subject } from 'rxjs'; +import { emptyPageData, PageData } from '@shared/models/page/page-data'; +import { ImageService } from '@core/http/image.service'; +import { EntityBooleanFunction } from '@home/models/entity/entities-table-config.models'; +import { PageLink } from '@shared/models/page/page-link'; +import { catchError, map, take, tap } from 'rxjs/operators'; + +export class ImagesDatasource implements DataSource { + private entitiesSubject: Subject; + private readonly pageDataSubject: Subject>; + + public pageData$: Observable>; + + public selection = new SelectionModel(true, []); + + public dataLoading = true; + + constructor(private imageService: ImageService, + private images: ImageResourceInfo[], + private selectionEnabledFunction: EntityBooleanFunction) { + if (this.images && this.images.length) { + this.entitiesSubject = new BehaviorSubject(this.images); + } else { + this.entitiesSubject = new BehaviorSubject([]); + this.pageDataSubject = new BehaviorSubject>(emptyPageData()); + this.pageData$ = this.pageDataSubject.asObservable(); + } + } + + connect(collectionViewer: CollectionViewer): + Observable> { + return this.entitiesSubject.asObservable(); + } + + disconnect(collectionViewer: CollectionViewer): void { + this.entitiesSubject.complete(); + if (this.pageDataSubject) { + this.pageDataSubject.complete(); + } + } + + reset() { + this.entitiesSubject.next([]); + if (this.pageDataSubject) { + this.pageDataSubject.next(emptyPageData()); + } + } + + loadEntities(pageLink: PageLink, includeSystemImages = false): Observable> { + this.dataLoading = true; + const result = new ReplaySubject>(); + this.fetchEntities(pageLink, includeSystemImages).pipe( + tap(() => { + this.selection.clear(); + }), + catchError(() => of(emptyPageData())), + ).subscribe( + (pageData) => { + this.entitiesSubject.next(pageData.data); + this.pageDataSubject.next(pageData); + result.next(pageData); + this.dataLoading = false; + } + ); + return result; + } + + fetchEntities(pageLink: PageLink, includeSystemImages = false): Observable> { + return this.imageService.getImages(pageLink, includeSystemImages); + } + + isAllSelected(): Observable { + const numSelected = this.selection.selected.length; + return this.entitiesSubject.pipe( + map((entities) => numSelected === entities.length) + ); + } + + isEmpty(): Observable { + return this.entitiesSubject.pipe( + map((entities) => !entities.length) + ); + } + + total(): Observable { + return this.pageDataSubject.pipe( + map((pageData) => pageData.totalElements) + ); + } + + masterToggle() { + this.entitiesSubject.pipe( + tap((entities) => { + const numSelected = this.selection.selected.length; + if (numSelected === this.selectableEntitiesCount(entities)) { + this.selection.clear(); + } else { + entities.forEach(row => { + if (this.selectionEnabledFunction(row)) { + this.selection.select(row); + } + }); + } + }), + take(1) + ).subscribe(); + } + + private selectableEntitiesCount(entities: Array): number { + return entities.filter((entity) => this.selectionEnabledFunction(entity)).length; + } +} diff --git a/ui-ngx/src/app/shared/components/image/images-in-use-dialog.component.html b/ui-ngx/src/app/shared/components/image/images-in-use-dialog.component.html new file mode 100644 index 0000000000..8739f7c9b2 --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/images-in-use-dialog.component.html @@ -0,0 +1,82 @@ + +

{{title}}

+
+
+ +
+ + + + + + + + + + + + + + + {{ image.title }} + + + + + + {{ 'image.name' | translate }} + + + {{ translate.get('image.selected-images', {count: dataSource.selection.selected.length}) | async }} + + + + {{ image.title }} + + + + + + + + + + +
+
+
+
+
+ + +
+ + +
+
diff --git a/ui-ngx/src/app/shared/components/image/images-in-use-dialog.component.scss b/ui-ngx/src/app/shared/components/image/images-in-use-dialog.component.scss new file mode 100644 index 0000000000..ba8531802e --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/images-in-use-dialog.component.scss @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + .tb-images-in-use-content { + display: flex; + flex-direction: column; + gap: 24px; + &.multiple { + gap: 16px; + } + .table-container { + overflow: auto; + .mat-mdc-cell.mat-column-preview { + width: 50px; + height: 50px; + padding: 2px 12px; + display: block; + } + } + .tb-image-preview { + width: 50px; + height: 50px; + object-fit: contain; + border-radius: 4px; + } + } +} diff --git a/ui-ngx/src/app/shared/components/image/images-in-use-dialog.component.ts b/ui-ngx/src/app/shared/components/image/images-in-use-dialog.component.ts new file mode 100644 index 0000000000..ccb530b642 --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/images-in-use-dialog.component.ts @@ -0,0 +1,114 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, Inject, OnInit, Renderer2, ViewContainerRef } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { DialogComponent } from '@shared/components/dialog.component'; +import { Router } from '@angular/router'; +import { ImageReferences, ImageResourceInfo, ImageResourceInfoWithReferences } from '@shared/models/resource.models'; +import { ImagesDatasource } from '@shared/components/image/images-datasource'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { ImageReferencesComponent } from '@shared/components/image/image-references.component'; +import { TranslateService } from '@ngx-translate/core'; + +export interface ImagesInUseDialogData { + multiple: boolean; + images: ImageResourceInfoWithReferences[]; +} + +@Component({ + selector: 'tb-images-in-use-dialog', + templateUrl: './images-in-use-dialog.component.html', + styleUrls: ['./images-in-use-dialog.component.scss'] +}) +export class ImagesInUseDialogComponent extends + DialogComponent implements OnInit { + + title: string; + message: string; + + references: ImageReferences; + + dataSource: ImagesDatasource; + + constructor(protected store: Store, + protected router: Router, + @Inject(MAT_DIALOG_DATA) public data: ImagesInUseDialogData, + public dialogRef: MatDialogRef, + public translate: TranslateService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef, + private popoverService: TbPopoverService) { + super(store, router, dialogRef); + } + + ngOnInit(): void { + if (this.data.multiple) { + this.title = this.translate.instant('image.images-are-in-use'); + this.message = this.translate.instant('image.images-are-in-use-text'); + this.dataSource = new ImagesDatasource(null, this.data.images, entity => true); + } else { + this.title = this.translate.instant('image.image-is-in-use'); + this.message = this.translate.instant('image.image-is-in-use-text', {title: this.data.images[0].title}); + this.references = this.data.images[0].references; + } + } + + cancel() { + this.dialogRef.close(null); + } + + delete() { + if (this.data.multiple) { + this.dialogRef.close(this.dataSource.selection.selected); + } else { + this.dialogRef.close(this.data.images); + } + } + + toggleShowReferences($event: Event, image: ImageResourceInfoWithReferences, referencesButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = referencesButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const referencesPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, ImageReferencesComponent, 'top', true, null, + { + references: image.references + }, {}, {}, {}, + false, + visible => { + const addClasses = + visible ? 'mdc-button--unelevated mat-mdc-unelevated-button' : 'mdc-button--outlined mat-mdc-outlined-button'; + const removeClasses = + visible ? 'mdc-button--outlined mat-mdc-outlined-button' : 'mdc-button--unelevated mat-mdc-unelevated-button'; + addClasses.split(' ').forEach(clazz => { + this.renderer.addClass(trigger, clazz); + }); + removeClasses.split(' ').forEach(clazz => { + this.renderer.removeClass(trigger, clazz); + }); + }); + referencesPopover.tbComponentRef.instance.popoverComponent = referencesPopover; + } + } +} diff --git a/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.html b/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.html new file mode 100644 index 0000000000..aeae4d9a1f --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.html @@ -0,0 +1,111 @@ + +
+ +
+
+
+ {{ 'image-input.images' | translate }} [{{ $index }}] +
+
+ drag_indicator +
+
+ +
+
+ +
+
+
+
{{ 'image-input.no-images' | translate }}
+
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
{{ 'image.no-image-selected' | translate }}
+
diff --git a/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.scss b/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.scss new file mode 100644 index 0000000000..a0cf328630 --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.scss @@ -0,0 +1,225 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import "../../../../scss/constants"; + +$imagesContainerHeight: 106px !default; +$selectContainerHeight: 96px !default; +$previewSize: 64px !default; + +.image-card { + margin-bottom: 8px; + &.image-dnd-placeholder { + height: 82px; + width: 146px; + border: 2px dashed rgba(0, 0, 0, 0.2); + background: rgba(0, 0, 0, 0.1); + border-radius: 4px; + } + &.image-dragging { + display: none !important; + } + .image-title { + font-size: 11px; + font-weight: 400; + line-height: 14px; + color: rgba(0, 0, 0, 0.6); + padding-bottom: 4px; + } + + .image-content-container { + background: #FFFFFF; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 4px; + height: $previewSize; + } + + .tb-image-preview { + width: auto; + max-width: $previewSize - 2px; + height: auto; + max-height: $previewSize - 2px; + } + + .tb-image-preview-container { + position: relative; + width: $previewSize; + height: $previewSize; + margin-top: -1px; + margin-bottom: -1px; + border: 1px solid rgba(0, 0, 0, 0.54); + + .tb-image-preview { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + } + + .tb-image-action-container { + position: relative; + height: $previewSize - 2px; + display: flex; + align-items: center; + justify-content: center; + min-width: 40px; + } +} + +:host { + .tb-container { + margin-top: 0; + padding: 0 0 16px; + display: flex; + flex-direction: column; + gap: 8px; + label.tb-title { + display: block; + padding-bottom: 0; + } + } + + .images-container { + padding: 12px 12px 4px; + background: rgba(0, 0, 0, 0.03); + border-radius: 4px; + flex-wrap: wrap; + margin-bottom: 8px; + &.no-images { + height: $imagesContainerHeight; + padding-bottom: 12px; + align-items: center; + justify-content: center; + } + } + + .no-images-prompt { + font-size: 18px; + color: rgba(0, 0, 0, 0.54); + } + + .tb-image-select-container { + width: 100%; + height: $selectContainerHeight; + border-radius: 4px; + border: 1px solid rgba(0, 0, 0, 0.12); + display: flex; + align-items: center; + + .tb-image-container { + width: $selectContainerHeight - 1px; + height: $selectContainerHeight - 2px; + display: flex; + justify-content: center; + align-items: center; + border-radius: 4px; + border-right: 1px solid rgba(0, 0, 0, 0.12); + background: #fff; + overflow: hidden; + + .tb-image-preview { + width: auto; + max-width: $selectContainerHeight - 2px; + height: auto; + max-height: $selectContainerHeight - 2px; + } + + .tb-no-image { + text-align: center; + color: rgba(0, 0, 0, 0.38); + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 16px; + letter-spacing: 0.4px; + } + } + + .tb-image-info-container { + display: flex; + flex: 1; + align-self: stretch; + padding: 0 8px; + justify-content: flex-end; + align-items: center; + gap: 4px; + + .tb-external-image-container { + display: flex; + flex: 1; + align-self: stretch; + padding: 16px 8px 0 16px; + flex-direction: column; + align-items: flex-start; + gap: 4px; + .tb-external-link-label { + color: rgba(0, 0, 0, 0.54); + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 16px; + letter-spacing: 0.4px; + } + .tb-external-link-input-container { + display: flex; + justify-content: flex-end; + align-items: center; + gap: 4px; + align-self: stretch; + .tb-inline-field { + width: 100%; + } + .tb-image-decline-btn { + color: rgba(0,0,0,0.38); + } + } + } + + } + + .tb-image-select-buttons-container { + display: flex; + flex: 1; + align-self: stretch; + padding: 8px; + gap: 8px; + justify-content: center; + align-items: flex-start; + .tb-image-select-button { + width: 100%; + height: 100%; + align-self: stretch; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 4px; + padding: 8px; + line-height: normal; + font-size: 12px; + @media #{$mat-gt-xs} { + padding: 16px; + } + .mat-icon { + width: 24px; + height: 24px; + font-size: 24px; + margin: 0; + } + } + } + } +} diff --git a/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.ts b/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.ts new file mode 100644 index 0000000000..5a6a4e7d37 --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/multiple-gallery-image-input.component.ts @@ -0,0 +1,181 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ChangeDetectorRef, Component, forwardRef, Input, OnDestroy, Renderer2, ViewContainerRef } from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR, } from '@angular/forms'; +import { moveItemInArray } from '@angular/cdk/drag-drop'; +import { DndDropEvent } from 'ngx-drag-drop'; +import { isUndefined } from '@core/utils'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { ImageLinkType } from '@shared/components/image/gallery-image-input.component'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { MatButton } from '@angular/material/button'; +import { ImageGalleryComponent } from '@shared/components/image/image-gallery.component'; +import { prependTbImagePrefixToUrls, removeTbImagePrefixFromUrls } from '@shared/models/resource.models'; + +@Component({ + selector: 'tb-multiple-gallery-image-input', + templateUrl: './multiple-gallery-image-input.component.html', + styleUrls: ['./multiple-gallery-image-input.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MultipleGalleryImageInputComponent), + multi: true + } + ] +}) +export class MultipleGalleryImageInputComponent extends PageComponent implements OnDestroy, ControlValueAccessor { + + @Input() + label: string; + + @Input() + @coerceBoolean() + required = false; + + @Input() + disabled: boolean; + + imageUrls: string[]; + + ImageLinkType = ImageLinkType; + + linkType: ImageLinkType = ImageLinkType.none; + + externalLinkControl = new FormControl(null); + + dragIndex: number; + + private propagateChange = null; + + constructor(protected store: Store, + private cd: ChangeDetectorRef, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef, + private popoverService: TbPopoverService) { + super(store); + } + + ngOnDestroy() { + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + } + + writeValue(value: string[]): void { + this.reset(); + this.imageUrls = removeTbImagePrefixFromUrls(value); + } + + private updateModel() { + this.cd.markForCheck(); + this.propagateChange(prependTbImagePrefixToUrls(this.imageUrls)); + } + + private reset() { + this.linkType = ImageLinkType.none; + this.externalLinkControl.setValue(null, {emitEvent: false}); + } + + clearImage(index: number) { + this.imageUrls.splice(index, 1); + this.updateModel(); + } + + setLink($event: Event) { + if ($event) { + $event.stopPropagation(); + } + this.linkType = ImageLinkType.external; + } + + declineLink($event: Event) { + if ($event) { + $event.stopPropagation(); + } + this.reset(); + } + + applyLink($event: Event) { + if ($event) { + $event.stopPropagation(); + } + this.imageUrls.push(this.externalLinkControl.value); + this.reset(); + this.updateModel(); + } + + toggleGallery($event: Event, browseGalleryButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = browseGalleryButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const ctx: any = { + pageMode: false, + popoverMode: true, + mode: 'grid', + selectionMode: true + }; + const imageGalleryPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, ImageGalleryComponent, 'top', true, null, + ctx, + {}, + {}, {}, true); + imageGalleryPopover.tbComponentRef.instance.imageSelected.subscribe((image) => { + imageGalleryPopover.hide(); + this.imageUrls.push(image.link); + this.updateModel(); + }); + } + } + + imageDragStart(index: number) { + setTimeout(() => { + this.dragIndex = index; + this.cd.markForCheck(); + }); + } + + imageDragEnd() { + this.dragIndex = -1; + this.cd.markForCheck(); + } + + imageDrop(event: DndDropEvent) { + let index = event.index; + if (isUndefined(index)) { + index = this.imageUrls.length; + } + moveItemInArray(this.imageUrls, this.dragIndex, index); + this.dragIndex = -1; + this.updateModel(); + } +} diff --git a/ui-ngx/src/app/shared/components/image/upload-image-dialog.component.html b/ui-ngx/src/app/shared/components/image/upload-image-dialog.component.html new file mode 100644 index 0000000000..59b35e628c --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/upload-image-dialog.component.html @@ -0,0 +1,65 @@ + +
+ +

{{ ( uploadImage ? 'image.upload-image' : 'image.update-image' ) | translate }}

+ + +
+ + +
+
+
+ + + + image.name + + + {{ 'image.name-required' | translate }} + + +
+
+
+ + +
+
diff --git a/ui-ngx/src/app/shared/components/image/upload-image-dialog.component.ts b/ui-ngx/src/app/shared/components/image/upload-image-dialog.component.ts new file mode 100644 index 0000000000..75e53d38f2 --- /dev/null +++ b/ui-ngx/src/app/shared/components/image/upload-image-dialog.component.ts @@ -0,0 +1,115 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, Inject, OnInit, SkipSelf } from '@angular/core'; +import { ErrorStateMatcher } from '@angular/material/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { + FormGroupDirective, + NgForm, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validators +} from '@angular/forms'; +import { DialogComponent } from '@shared/components/dialog.component'; +import { Router } from '@angular/router'; +import { ImageService } from '@core/http/image.service'; +import { ImageResourceInfo, imageResourceType } from '@shared/models/resource.models'; +import { getCurrentAuthState } from '@core/auth/auth.selectors'; + +export interface UploadImageDialogData { + image?: ImageResourceInfo; +} + +@Component({ + selector: 'tb-upload-image-dialog', + templateUrl: './upload-image-dialog.component.html', + providers: [{provide: ErrorStateMatcher, useExisting: UploadImageDialogComponent}], + styleUrls: [] +}) +export class UploadImageDialogComponent extends + DialogComponent implements OnInit, ErrorStateMatcher { + + uploadImageFormGroup: UntypedFormGroup; + + uploadImage = true; + + submitted = false; + + maxResourceSize = getCurrentAuthState(this.store).maxResourceSize; + + constructor(protected store: Store, + protected router: Router, + private imageService: ImageService, + @Inject(MAT_DIALOG_DATA) public data: UploadImageDialogData, + @SkipSelf() private errorStateMatcher: ErrorStateMatcher, + public dialogRef: MatDialogRef, + public fb: UntypedFormBuilder) { + super(store, router, dialogRef); + } + + ngOnInit(): void { + this.uploadImage = !this.data?.image; + this.uploadImageFormGroup = this.fb.group({ + file: [this.data?.image?.link, [Validators.required]] + }); + if (this.uploadImage) { + this.uploadImageFormGroup.addControl('title', this.fb.control(null, [Validators.required])); + } + } + + imageFileNameChanged(fileName: string) { + if (this.uploadImage) { + const titleControl = this.uploadImageFormGroup.get('title'); + if (!titleControl.value || !titleControl.touched) { + titleControl.setValue(fileName); + } + } + } + + isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean { + const originalErrorState = this.errorStateMatcher.isErrorState(control, form); + const customErrorState = !!(control && control.invalid && this.submitted); + return originalErrorState || customErrorState; + } + + cancel(): void { + this.dialogRef.close(null); + } + + upload(): void { + this.submitted = true; + const file: File = this.uploadImageFormGroup.get('file').value; + if (this.uploadImage) { + const title: string = this.uploadImageFormGroup.get('title').value; + this.imageService.uploadImage(file, title).subscribe( + (res) => { + this.dialogRef.close(res); + } + ); + } else { + const image = this.data.image; + this.imageService.updateImage(imageResourceType(image), image.resourceKey, file).subscribe( + (res) => { + this.dialogRef.close(res); + } + ); + } + } +} diff --git a/ui-ngx/src/app/shared/components/js-func.component.html b/ui-ngx/src/app/shared/components/js-func.component.html index 23ebd76918..41e834668e 100644 --- a/ui-ngx/src/app/shared/components/js-func.component.html +++ b/ui-ngx/src/app/shared/components/js-func.component.html @@ -27,6 +27,7 @@ +
void) { - this.dialogs.colorPicker(tinycolor(val).toRgbString()).subscribe((color) => { - if (color && colorSelectedFn) { - colorSelectedFn(tinycolor(color).toRgb()); + this.dialogs.colorPicker(tinycolor(val).toRgbString()).subscribe((result) => { + if (!result?.canceled && colorSelectedFn) { + colorSelectedFn(tinycolor(result?.color).toRgb()); } }); } @@ -226,9 +226,9 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato private onIconClick(key: (string | number)[], val: string, iconSelectedFn: (icon: string) => void) { - this.dialogs.materialIconPicker(val).subscribe((icon) => { - if (icon && iconSelectedFn) { - iconSelectedFn(icon); + this.dialogs.materialIconPicker(val).subscribe((result) => { + if (!result?.canceled && iconSelectedFn) { + iconSelectedFn(result?.icon); } }); } diff --git a/ui-ngx/src/app/shared/components/json-object-edit.component.html b/ui-ngx/src/app/shared/components/json-object-edit.component.html index c0024da9dd..3ed828e6cf 100644 --- a/ui-ngx/src/app/shared/components/json-object-edit.component.html +++ b/ui-ngx/src/app/shared/components/json-object-edit.component.html @@ -20,7 +20,7 @@ [fullscreen]="fullscreen" (fullscreenChanged)="onFullscreen()" fxLayout="column">
diff --git a/ui-ngx/src/app/shared/components/json-object-edit.component.ts b/ui-ngx/src/app/shared/components/json-object-edit.component.ts index ad2e3abd2f..f89430f98b 100644 --- a/ui-ngx/src/app/shared/components/json-object-edit.component.ts +++ b/ui-ngx/src/app/shared/components/json-object-edit.component.ts @@ -14,8 +14,24 @@ /// limitations under the License. /// -import { Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { ControlValueAccessor, UntypedFormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, Validator } from '@angular/forms'; +import { + ChangeDetectorRef, + Component, + ElementRef, + forwardRef, + Input, + OnDestroy, + OnInit, + ViewChild +} from '@angular/core'; +import { + ControlValueAccessor, + UntypedFormControl, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + Validator, + AbstractControl, ValidationErrors +} from '@angular/forms'; import { Ace } from 'ace-builds'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { ActionNotificationHide, ActionNotificationShow } from '@core/notification/notification.actions'; @@ -25,6 +41,9 @@ import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; import { guid, isDefinedAndNotNull, isObject, isUndefined } from '@core/utils'; import { ResizeObserver } from '@juggle/resize-observer'; import { getAce } from '@shared/models/ace/ace.models'; +import { coerceBoolean } from '@shared/decorators/coercion'; + +export const jsonRequired = (control: AbstractControl): ValidationErrors | null => !control.value ? {required: true} : null; @Component({ selector: 'tb-json-object-edit', @@ -64,27 +83,13 @@ export class JsonObjectEditComponent implements OnInit, ControlValueAccessor, Va @Input() sort: (key: string, value: any) => any; - private requiredValue: boolean; - - get required(): boolean { - return this.requiredValue; - } - + @coerceBoolean() @Input() - set required(value: boolean) { - this.requiredValue = coerceBooleanProperty(value); - } - - private readonlyValue: boolean; - - get readonly(): boolean { - return this.readonlyValue; - } + jsonRequired: boolean; + @coerceBoolean() @Input() - set readonly(value: boolean) { - this.readonlyValue = coerceBooleanProperty(value); - } + readonly: boolean; fullscreen = false; @@ -104,7 +109,8 @@ export class JsonObjectEditComponent implements OnInit, ControlValueAccessor, Va constructor(public elementRef: ElementRef, protected store: Store, - private raf: RafService) { + private raf: RafService, + private cd: ChangeDetectorRef) { } ngOnInit(): void { @@ -235,12 +241,10 @@ export class JsonObjectEditComponent implements OnInit, ControlValueAccessor, Va try { if (isDefinedAndNotNull(this.modelValue)) { this.contentValue = JSON.stringify(this.modelValue, isUndefined(this.sort) ? undefined : - (key, objectValue) => { - return this.sort(key, objectValue); - }, 2); + (key, objectValue) => this.sort(key, objectValue), 2); this.objectValid = true; } else { - this.objectValid = !this.required; + this.objectValid = !this.jsonRequired; this.validationError = 'Json object is required.'; } } catch (e) { @@ -278,11 +282,12 @@ export class JsonObjectEditComponent implements OnInit, ControlValueAccessor, Va this.validationError = errorInfo; } } else { - this.objectValid = !this.required; - this.validationError = this.required ? 'Json object is required.' : ''; + this.objectValid = !this.jsonRequired; + this.validationError = this.jsonRequired ? 'Json object is required.' : ''; } this.modelValue = data; this.propagateChange(data); + this.cd.markForCheck(); } } diff --git a/ui-ngx/src/app/shared/components/markdown-editor.component.ts b/ui-ngx/src/app/shared/components/markdown-editor.component.ts index 4747254011..9a775844b8 100644 --- a/ui-ngx/src/app/shared/components/markdown-editor.component.ts +++ b/ui-ngx/src/app/shared/components/markdown-editor.component.ts @@ -14,11 +14,23 @@ /// limitations under the License. /// -import { Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { + ChangeDetectorRef, + Component, + ElementRef, + forwardRef, + Input, + OnDestroy, + OnInit, + ViewChild, + ViewEncapsulation +} from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { Ace } from 'ace-builds'; -import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { getAce } from '@shared/models/ace/ace.models'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; @Component({ selector: 'tb-markdown-editor', @@ -30,7 +42,8 @@ import { getAce } from '@shared/models/ace/ace.models'; useExisting: forwardRef(() => MarkdownEditorComponent), multi: true } - ] + ], + encapsulation: ViewEncapsulation.None }) export class MarkdownEditorComponent implements OnInit, ControlValueAccessor, OnDestroy { @@ -42,11 +55,13 @@ export class MarkdownEditorComponent implements OnInit, ControlValueAccessor, On @Input() helpId: string; + @Input() + @coerceBoolean() + required: boolean; + @ViewChild('markdownEditor', {static: true}) markdownEditorElmRef: ElementRef; - private markdownEditor: Ace.Editor; - editorMode = true; fullscreen = false; @@ -54,22 +69,15 @@ export class MarkdownEditorComponent implements OnInit, ControlValueAccessor, On markdownValue: string; renderValue: string; - ignoreChange = false; - - private propagateChange = null; - - private requiredValue: boolean; + private markdownEditor: Ace.Editor; + private ignoreChange = false; - get required(): boolean { - return this.requiredValue; - } + private editorResize$: ResizeObserver; + private editorsResizeCaf: CancelAnimationFrame; + private propagateChange: (value: any) => void = () => {}; - @Input() - set required(value: boolean) { - this.requiredValue = coerceBooleanProperty(value); - } - - constructor() { + constructor(private cd: ChangeDetectorRef, + private raf: RafService) { } ngOnInit(): void { @@ -100,6 +108,10 @@ export class MarkdownEditorComponent implements OnInit, ControlValueAccessor, On this.updateView(); } }); + this.editorResize$ = new ResizeObserver(() => { + this.onAceEditorResize(); + }); + this.editorResize$.observe(editorElement); } ); @@ -107,6 +119,13 @@ export class MarkdownEditorComponent implements OnInit, ControlValueAccessor, On } ngOnDestroy(): void { + if (this.editorResize$) { + this.editorResize$.disconnect(); + } + if (this.editorsResizeCaf) { + this.editorsResizeCaf(); + this.editorsResizeCaf = null; + } if (this.markdownEditor) { this.markdownEditor.destroy(); } @@ -134,15 +153,6 @@ export class MarkdownEditorComponent implements OnInit, ControlValueAccessor, On } } - updateView() { - const editorValue = this.markdownEditor.getValue(); - if (this.markdownValue !== editorValue) { - this.markdownValue = editorValue; - this.renderValue = this.markdownValue ? this.markdownValue : ' '; - this.propagateChange(this.markdownValue); - } - } - onFullscreen() { if (this.markdownEditor) { setTimeout(() => { @@ -159,4 +169,25 @@ export class MarkdownEditorComponent implements OnInit, ControlValueAccessor, On }, 0); } } + + private updateView() { + const editorValue = this.markdownEditor.getValue(); + if (this.markdownValue !== editorValue) { + this.markdownValue = editorValue; + this.renderValue = this.markdownValue ? this.markdownValue : ' '; + this.propagateChange(this.markdownValue); + this.cd.markForCheck(); + } + } + + private onAceEditorResize() { + if (this.editorsResizeCaf) { + this.editorsResizeCaf(); + this.editorsResizeCaf = null; + } + this.editorsResizeCaf = this.raf.raf(() => { + this.markdownEditor.resize(); + this.markdownEditor.renderer.updateFull(); + }); + } } diff --git a/ui-ngx/src/app/shared/components/markdown.component.ts b/ui-ngx/src/app/shared/components/markdown.component.ts index cd03959fad..168242da0f 100644 --- a/ui-ngx/src/app/shared/components/markdown.component.ts +++ b/ui-ngx/src/app/shared/components/markdown.component.ts @@ -17,7 +17,6 @@ import { ChangeDetectorRef, Component, - ComponentFactory, ComponentRef, ElementRef, EventEmitter, @@ -91,7 +90,7 @@ export class TbMarkdownComponent implements OnChanges { error = null; private tbMarkdownInstanceComponentRef: ComponentRef; - private tbMarkdownInstanceComponentFactory: ComponentFactory; + private tbMarkdownInstanceComponentType: Type; constructor(private help: HelpService, private cd: ChangeDetectorRef, @@ -153,7 +152,7 @@ export class TbMarkdownComponent implements OnChanges { if (this.additionalCompileModules) { compileModules = compileModules.concat(this.additionalCompileModules); } - this.dynamicComponentFactoryService.createDynamicComponentFactory( + this.dynamicComponentFactoryService.createDynamicComponent( class TbMarkdownInstance { ngOnDestroy(): void { parent.destroyMarkdownInstanceResources(); @@ -162,12 +161,13 @@ export class TbMarkdownComponent implements OnChanges { template, compileModules, true, 1, styles - ).subscribe((factory) => { - this.tbMarkdownInstanceComponentFactory = factory; + ).subscribe((componentData) => { + this.tbMarkdownInstanceComponentType = componentData.componentType; const injector: Injector = Injector.create({providers: [], parent: this.markdownContainer.injector}); try { this.tbMarkdownInstanceComponentRef = - this.markdownContainer.createComponent(this.tbMarkdownInstanceComponentFactory, 0, injector); + this.markdownContainer.createComponent(this.tbMarkdownInstanceComponentType, + {index: 0, injector, ngModuleRef: componentData.componentModuleRef}); if (this.context) { for (const propName of Object.keys(this.context)) { this.tbMarkdownInstanceComponentRef.instance[propName] = this.context[propName]; @@ -179,6 +179,7 @@ export class TbMarkdownComponent implements OnChanges { this.error = null; } catch (error) { readyObservable = this.handleError(template, error, styles); + this.cd.detectChanges(); } readyObservable.subscribe(() => { this.ready.emit(); @@ -261,9 +262,9 @@ export class TbMarkdownComponent implements OnChanges { } private destroyMarkdownInstanceResources() { - if (this.tbMarkdownInstanceComponentFactory) { - this.dynamicComponentFactoryService.destroyDynamicComponentFactory(this.tbMarkdownInstanceComponentFactory); - this.tbMarkdownInstanceComponentFactory = null; + if (this.tbMarkdownInstanceComponentType) { + this.dynamicComponentFactoryService.destroyDynamicComponent(this.tbMarkdownInstanceComponentType); + this.tbMarkdownInstanceComponentType = null; } this.tbMarkdownInstanceComponentRef = null; } diff --git a/ui-ngx/src/app/shared/components/material-icon-select.component.html b/ui-ngx/src/app/shared/components/material-icon-select.component.html index 5cf7cb6de7..5e599e188d 100644 --- a/ui-ngx/src/app/shared/components/material-icon-select.component.html +++ b/ui-ngx/src/app/shared/components/material-icon-select.component.html @@ -16,7 +16,7 @@ -->
- {{materialIconFormGroup.get('icon').value}} + {{materialIconFormGroup.get('icon').value}} {{ label }} @@ -24,12 +24,19 @@ type="button" matSuffix mat-icon-button aria-label="Clear" (click)="clear()"> - close + close
- {{materialIconFormGroup.get('icon').value}} + diff --git a/ui-ngx/src/app/shared/components/material-icon-select.component.scss b/ui-ngx/src/app/shared/components/material-icon-select.component.scss index 6bfd308ae5..78e4d235cf 100644 --- a/ui-ngx/src/app/shared/components/material-icon-select.component.scss +++ b/ui-ngx/src/app/shared/components/material-icon-select.component.scss @@ -22,20 +22,5 @@ border: solid 1px rgba(0, 0, 0, .27); box-sizing: initial; } - &.icon-box { - border: 1px solid rgba(0, 0, 0, 0.12); - border-radius: 4px; - cursor: pointer; - box-sizing: border-box; - padding: 8px; - height: 40px; - width: 40px; - font-size: 22px; - vertical-align: middle; - &.disabled { - cursor: initial; - color: rgba(0, 0, 0, 0.38); - } - } } } diff --git a/ui-ngx/src/app/shared/components/material-icon-select.component.ts b/ui-ngx/src/app/shared/components/material-icon-select.component.ts index b8bb7ed25b..a7af6eaa70 100644 --- a/ui-ngx/src/app/shared/components/material-icon-select.component.ts +++ b/ui-ngx/src/app/shared/components/material-icon-select.component.ts @@ -14,15 +14,18 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, forwardRef, Input, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Component, forwardRef, Input, OnInit, Renderer2, ViewContainerRef } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; import { DialogService } from '@core/services/dialog.service'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { TranslateService } from '@ngx-translate/core'; import { coerceBoolean } from '@shared/decorators/coercion'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { MaterialIconsComponent } from '@shared/components/material-icons.component'; +import { MatButton } from '@angular/material/button'; @Component({ selector: 'tb-material-icon-select', @@ -48,20 +51,15 @@ export class MaterialIconSelectComponent extends PageComponent implements OnInit @Input() color: string; + @Input() + backgroundColor: string; + @Input() disabled: boolean; - private iconClearButtonValue: boolean; - get iconClearButton(): boolean { - return this.iconClearButtonValue; - } @Input() - set iconClearButton(value: boolean) { - const newVal = coerceBooleanProperty(value); - if (this.iconClearButtonValue !== newVal) { - this.iconClearButtonValue = newVal; - } - } + @coerceBoolean() + iconClearButton = false; private requiredValue: boolean; get required(): boolean { @@ -81,6 +79,9 @@ export class MaterialIconSelectComponent extends PageComponent implements OnInit constructor(protected store: Store, private dialogs: DialogService, private translate: TranslateService, + private popoverService: TbPopoverService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef, private fb: UntypedFormBuilder, private cd: ChangeDetectorRef) { super(store); @@ -129,11 +130,12 @@ export class MaterialIconSelectComponent extends PageComponent implements OnInit openIconDialog() { if (!this.disabled) { - this.dialogs.materialIconPicker(this.materialIconFormGroup.get('icon').value).subscribe( - (icon) => { - if (icon) { + this.dialogs.materialIconPicker(this.materialIconFormGroup.get('icon').value, + this.iconClearButton).subscribe( + (result) => { + if (!result?.canceled) { this.materialIconFormGroup.patchValue( - {icon}, {emitEvent: true} + {icon: result?.icon}, {emitEvent: true} ); this.cd.markForCheck(); } @@ -142,6 +144,33 @@ export class MaterialIconSelectComponent extends PageComponent implements OnInit } } + openIconPopup($event: Event, matButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = matButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const materialIconsPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, MaterialIconsComponent, 'left', true, null, + { + selectedIcon: this.materialIconFormGroup.get('icon').value, + iconClearButton: this.iconClearButton + }, + {}, + {}, {}, true); + materialIconsPopover.tbComponentRef.instance.popover = materialIconsPopover; + materialIconsPopover.tbComponentRef.instance.iconSelected.subscribe((icon) => { + materialIconsPopover.hide(); + this.materialIconFormGroup.patchValue( + {icon}, {emitEvent: true} + ); + this.cd.markForCheck(); + }); + } + } + clear() { this.materialIconFormGroup.get('icon').patchValue(null, {emitEvent: true}); this.cd.markForCheck(); diff --git a/ui-ngx/src/app/shared/components/material-icons.component.html b/ui-ngx/src/app/shared/components/material-icons.component.html new file mode 100644 index 0000000000..bbc50a9292 --- /dev/null +++ b/ui-ngx/src/app/shared/components/material-icons.component.html @@ -0,0 +1,76 @@ + +
+
icon.icons
+ + search + + + + +
+ + + + +
+
+ +
+
+
{{ 'icon.no-icons-found' | translate:{iconSearch: searchIconControl.value} }}
+
+
+
+ + + +
+
diff --git a/ui-ngx/src/app/shared/components/material-icons.component.scss b/ui-ngx/src/app/shared/components/material-icons.component.scss new file mode 100644 index 0000000000..b9c7a74c9a --- /dev/null +++ b/ui-ngx/src/app/shared/components/material-icons.component.scss @@ -0,0 +1,69 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.tb-material-icons-panel { + width: 100%; + display: flex; + flex-direction: column; + gap: 16px; + align-items: center; + .tb-material-icons-title { + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.87); + } + .tb-material-icons-title, .tb-material-icons-search { + width: 100%; + } + .tb-material-icons-viewport { + min-height: 144px; + } + .tb-material-icons-row { + display: flex; + flex-direction: row; + gap: 12px; + } + .tb-material-icons-row + .tb-material-icons-row { + margin-top: 12px; + } + .tb-no-data-available { + min-height: 144px; + } + button.mat-mdc-button-base.tb-select-icon-button { + width: 36px; + min-width: 36px; + height: 36px; + padding: 6px; + &:not(.mat-primary) { + color: rgba(0, 0, 0, 0.54); + } + > .mat-icon { + width: 24px; + height: 24px; + font-size: 24px; + margin: 0; + } + } + .tb-material-icons-panel-buttons { + height: 40px; + display: flex; + flex-direction: row; + gap: 16px; + align-items: flex-start; + align-self: stretch; + } +} diff --git a/ui-ngx/src/app/shared/components/material-icons.component.ts b/ui-ngx/src/app/shared/components/material-icons.component.ts new file mode 100644 index 0000000000..1d826816b7 --- /dev/null +++ b/ui-ngx/src/app/shared/components/material-icons.component.ts @@ -0,0 +1,144 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { PageComponent } from '@shared/components/page.component'; +import { + ChangeDetectorRef, + Component, + EventEmitter, + Input, + OnInit, + Output, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { UntypedFormControl } from '@angular/forms'; +import { BehaviorSubject, combineLatest, debounce, Observable, of, timer } from 'rxjs'; +import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; +import { getMaterialIcons, MaterialIcon } from '@shared/models/icon.models'; +import { distinctUntilChanged, map, mergeMap, share, startWith, tap } from 'rxjs/operators'; +import { ResourcesService } from '@core/services/resources.service'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { BreakpointObserver } from '@angular/cdk/layout'; +import { MediaBreakpoints } from '@shared/models/constants'; +import { coerceBoolean } from '@shared/decorators/coercion'; + +@Component({ + selector: 'tb-material-icons', + templateUrl: './material-icons.component.html', + providers: [], + styleUrls: ['./material-icons.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class MaterialIconsComponent extends PageComponent implements OnInit { + + @ViewChild('iconsPanel') + iconsPanel: CdkVirtualScrollViewport; + + @Input() + selectedIcon: string; + + @Input() + @coerceBoolean() + iconClearButton = false; + + @Input() + popover: TbPopoverComponent; + + @Output() + iconSelected = new EventEmitter(); + + iconRows$: Observable; + showAllSubject = new BehaviorSubject(false); + searchIconControl: UntypedFormControl; + + iconsRowHeight = 48; + + iconsPanelHeight: string; + iconsPanelWidth: string; + + notFound = false; + + constructor(protected store: Store, + private resourcesService: ResourcesService, + private breakpointObserver: BreakpointObserver, + private cd: ChangeDetectorRef) { + super(store); + this.searchIconControl = new UntypedFormControl(''); + } + + ngOnInit(): void { + const iconsRowSize = this.breakpointObserver.isMatched(MediaBreakpoints['lt-md']) ? 8 : 11; + this.calculatePanelSize(iconsRowSize); + const iconsRowSizeObservable = this.breakpointObserver + .observe(MediaBreakpoints['lt-md']).pipe( + map((state) => state.matches ? 8 : 11), + startWith(iconsRowSize), + ); + this.iconRows$ = combineLatest({showAll: this.showAllSubject.asObservable(), + rowSize: iconsRowSizeObservable, + searchText: this.searchIconControl.valueChanges.pipe( + startWith(''), + debounce((searchText) => searchText ? timer(150) : of({})), + )}).pipe( + map((data) => { + if (data.searchText && !data.showAll) { + data.showAll = true; + this.showAllSubject.next(true); + } + return data; + }), + distinctUntilChanged((p, c) => c.showAll === p.showAll && c.searchText === p.searchText && c.rowSize === p.rowSize), + mergeMap((data) => getMaterialIcons(this.resourcesService, data.rowSize, data.showAll, data.searchText).pipe( + map(iconRows => ({iconRows, iconsRowSize: data.rowSize})) + )), + tap((data) => { + this.notFound = !data.iconRows.length; + this.calculatePanelSize(data.iconsRowSize, data.iconRows.length); + this.cd.markForCheck(); + setTimeout(() => { + this.checkSize(); + }, 0); + }), + map((data) => data.iconRows), + share() + ); + } + + clearSearch() { + this.searchIconControl.patchValue('', {emitEvent: true}); + } + + selectIcon(icon: MaterialIcon) { + this.iconSelected.emit(icon.name); + } + + clearIcon() { + this.iconSelected.emit(null); + } + + private calculatePanelSize(iconsRowSize: number, iconRows = 4) { + this.iconsPanelHeight = Math.min(iconRows * this.iconsRowHeight, 10 * this.iconsRowHeight) + 'px'; + this.iconsPanelWidth = (iconsRowSize * 36 + (iconsRowSize - 1) * 12 + 6) + 'px'; + } + + private checkSize() { + this.iconsPanel?.checkViewportSize(); + this.popover?.updatePosition(); + } +} diff --git a/ui-ngx/src/app/shared/components/notification/notification.component.html b/ui-ngx/src/app/shared/components/notification/notification.component.html index 361d3589fb..ba6091eebc 100644 --- a/ui-ngx/src/app/shared/components/notification/notification.component.html +++ b/ui-ngx/src/app/shared/components/notification/notification.component.html @@ -18,14 +18,14 @@
- + {{ notification.additionalConfig.icon.icon }} - +
- + {{ notificationTypeIcons.get(notification.type) }} - +
@@ -41,7 +41,7 @@ matTooltip="{{ 'notification.mark-as-read' | translate }}" matTooltipPosition="above"> check_circle_outline -
{{alarmSeverityTranslations.get(notification.info.alarmSeverity) | translate}} diff --git a/ui-ngx/src/app/shared/components/notification/notification.component.ts b/ui-ngx/src/app/shared/components/notification/notification.component.ts index ad543c43c1..6e1ff508a7 100644 --- a/ui-ngx/src/app/shared/components/notification/notification.component.ts +++ b/ui-ngx/src/app/shared/components/notification/notification.component.ts @@ -139,7 +139,7 @@ export class NotificationComponent implements OnInit { } notificationColor(): string { - if (this.notification.type === NotificationType.ALARM) { + if (this.notification.type === NotificationType.ALARM && !this.notification.info.cleared) { return AlarmSeverityNotificationColors.get(this.notification.info.alarmSeverity); } return 'transparent'; diff --git a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.html b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.html index fb5c33d785..374f81bfdb 100644 --- a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.html @@ -24,8 +24,9 @@ [required]="required" [matAutocomplete]="packageAutocomplete" [matAutocompleteDisabled]="disabled" - [fxShow]="!showDetailsPageLink || !disabled || !otaPackageFormGroup.get('packageId').value"> - + [fxShow]="!showDetailsPageLink || !usePackageLink || !disabled || !otaPackageFormGroup.get('packageId').value"> + {{ displayPackageFn(otaPackageFormGroup.get('packageId').value) }} + + warning + + + {{errorText}} + + + + + + + diff --git a/ui-ngx/src/app/shared/components/string-autocomplete.component.scss b/ui-ngx/src/app/shared/components/string-autocomplete.component.scss new file mode 100644 index 0000000000..2d96ee0d5e --- /dev/null +++ b/ui-ngx/src/app/shared/components/string-autocomplete.component.scss @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + mat-form-field { + display: flex; + flex: 1; + max-width: 100%; + box-sizing: border-box; + + .tb-autocomplete.tb-option-input-autocomplete { + .mat-mdc-option { + border-bottom: none; + + .mdc-list-item__primary-text { + flex: 1; + display: flex; + flex-direction: row; + gap: 8px; + + .tb-option { + font-size: 14px; + font-weight: 400; + line-height: 20px; + letter-spacing: 0.2px; + } + } + } + } + } +} diff --git a/ui-ngx/src/app/shared/components/string-autocomplete.component.ts b/ui-ngx/src/app/shared/components/string-autocomplete.component.ts new file mode 100644 index 0000000000..df17ace6c7 --- /dev/null +++ b/ui-ngx/src/app/shared/components/string-autocomplete.component.ts @@ -0,0 +1,180 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + Component, + Input, + forwardRef, + OnInit, + ViewChild, + ElementRef +} from '@angular/core'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + FormControl, + Validators, + FormBuilder +} from '@angular/forms'; +import { Observable, of } from 'rxjs'; +import { tap, map, switchMap, take } from 'rxjs/operators'; +import { TranslateService } from '@ngx-translate/core'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field'; + +@Component({ + selector: 'tb-string-autocomplete', + templateUrl: './string-autocomplete.component.html', + styleUrls: ['./string-autocomplete.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => StringAutocompleteComponent), + multi: true + } + ] +}) +export class StringAutocompleteComponent implements ControlValueAccessor, OnInit { + + @ViewChild('nameInput', {static: true}) nameInput: ElementRef; + + @Input() + disabled: boolean; + + @Input() + @coerceBoolean() + required = false; + + @Input() + fetchOptionsFn: (searchText?: string) => Observable>; + + @Input() + placeholderText: string = this.translate.instant('widget-config.set'); + + @Input() + subscriptSizing: SubscriptSizing = 'dynamic'; + + @Input() + additionalClass: string | string[] | Record = 'tb-inline-field tb-suffix-show-on-hover'; + + @Input() + appearance: MatFormFieldAppearance = 'outline'; + + @Input() + label: string; + + @Input() + tooltipClass = 'tb-error-tooltip'; + + @Input() + errorText: string; + + @Input() + @coerceBoolean() + showInlineError = false; + + selectionFormControl: FormControl; + + modelValue: string | null; + + filteredOptions$: Observable>; + + searchText = ''; + + private dirty = false; + + private propagateChange = (_val: any) => {}; + + constructor(private fb: FormBuilder, + private translate: TranslateService) { + } + + ngOnInit() { + this.selectionFormControl = this.fb.control('', this.required ? [Validators.required] : []); + this.filteredOptions$ = this.selectionFormControl.valueChanges + .pipe( + tap(value => this.updateView(value)), + map(value => value ? value : ''), + switchMap(value => this.fetchOptionsFn ? this.fetchOptionsFn(value) : of([])) + ); + } + + writeValue(option?: string): void { + this.searchText = ''; + this.modelValue = option ? option : null; + + if (this.fetchOptionsFn) { + this.fetchOptionsFn(option) + .pipe( + map(options => { + if (options) { + const foundOption = options.find(opt => opt === option); + return foundOption ? foundOption : option; + } + + return option; + }), + take(1) + ) + .subscribe(result => { + this.selectionFormControl.patchValue(result, { emitEvent: false }); + this.dirty = true; + }); + } else { + this.selectionFormControl.patchValue(null, { emitEvent: false }); + this.dirty = true; + } + } + + onFocus() { + if (this.dirty) { + this.selectionFormControl.updateValueAndValidity({onlySelf: true, emitEvent: true}); + this.dirty = false; + } + } + + updateView(value: string) { + this.searchText = value ? value : ''; + if (this.modelValue !== value) { + this.modelValue = value; + this.propagateChange(this.modelValue); + } + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.selectionFormControl.disable({emitEvent: false}); + } else { + this.selectionFormControl.enable({emitEvent: false}); + } + } + + clear() { + this.selectionFormControl.patchValue(null, {emitEvent: true}); + setTimeout(() => { + this.nameInput.nativeElement.blur(); + this.nameInput.nativeElement.focus(); + }, 0); + } +} diff --git a/ui-ngx/src/app/shared/components/string-items-list.component.html b/ui-ngx/src/app/shared/components/string-items-list.component.html index f1f9f0d118..41723ce3d7 100644 --- a/ui-ngx/src/app/shared/components/string-items-list.component.html +++ b/ui-ngx/src/app/shared/components/string-items-list.component.html @@ -15,24 +15,56 @@ limitations under the License. --> -
- - {{ label }} - - - {{item}} - close - - - - {{ hint }} - {{ requiredText }} - -
+ + {{ label }} + + + {{ item.name }} + close + + + + + + + + + {{ 'common.not-found' | translate }} + + + + {{ hint }} + + + + +
+ +
+ + {{ requiredText }} + +
diff --git a/ui-ngx/src/app/shared/components/string-items-list.component.ts b/ui-ngx/src/app/shared/components/string-items-list.component.ts index 0febd3cad4..48a7e740cb 100644 --- a/ui-ngx/src/app/shared/components/string-items-list.component.ts +++ b/ui-ngx/src/app/shared/components/string-items-list.component.ts @@ -14,13 +14,27 @@ /// limitations under the License. /// -import { Component, forwardRef, Input } from '@angular/core'; -import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + FormBuilder, + FormGroup, + NG_VALUE_ACCESSOR, + Validators +} from '@angular/forms'; import { MatChipInputEvent } from '@angular/material/chips'; import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; import { FloatLabelType, MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field'; -import { coerceBoolean } from '@shared/decorators/coercion'; +import { coerceArray, coerceBoolean } from '@shared/decorators/coercion'; +import { Observable, of } from 'rxjs'; +import { filter, mergeMap, share, tap } from 'rxjs/operators'; +import { MatAutocompleteTrigger } from '@angular/material/autocomplete'; +export interface StringItemsOption { + name: string; + value: any; +} @Component({ selector: 'tb-string-items-list', templateUrl: './string-items-list.component.html', @@ -33,17 +47,29 @@ import { coerceBoolean } from '@shared/decorators/coercion'; } ] }) -export class StringItemsListComponent implements ControlValueAccessor{ +export class StringItemsListComponent implements ControlValueAccessor, OnInit { stringItemsForm: FormGroup; + + filteredValues: Observable>; + + searchText = ''; + + itemList: StringItemsOption[] = []; + private modelValue: Array | null; readonly separatorKeysCodes: number[] = [ENTER, COMMA, SEMICOLON]; + @ViewChild('stringItemInput', {static: true}) stringItemInput: ElementRef; + @ViewChild(MatAutocompleteTrigger) autocomplete: MatAutocompleteTrigger; + private requiredValue: boolean; + get required(): boolean { return this.requiredValue; } + @Input() @coerceBoolean() set required(value: boolean) { @@ -80,19 +106,52 @@ export class StringItemsListComponent implements ControlValueAccessor{ editable = false; @Input() - subscriptSizing: SubscriptSizing = 'fixed' + subscriptSizing: SubscriptSizing = 'fixed'; - private propagateChange = (v: any) => { }; + @Input() + @coerceArray() + predefinedValues: StringItemsOption[]; + + get itemsControl(): AbstractControl { + return this.stringItemsForm.get('items'); + } + + get itemControl(): AbstractControl { + return this.stringItemsForm.get('item'); + } + + onTouched = () => {}; + private propagateChange: (value: any) => void = () => {}; + private dirty = false; constructor(private fb: FormBuilder) { this.stringItemsForm = this.fb.group({ - items: [null, this.required ? [Validators.required] : []] + item: [null], + items: [null] }); } + ngOnInit() { + if (this.predefinedValues) { + this.filteredValues = this.itemControl.valueChanges + .pipe( + tap((value) => { + if (value && typeof value !== 'string') { + this.add(value); + } else if (value === null) { + this.clear(); + } + }), + filter((value) => typeof value === 'string'), + mergeMap(name => this.fetchValues(name)), + share() + ); + } + } + updateValidators() { - this.stringItemsForm.get('items').setValidators(this.required ? [Validators.required] : []); - this.stringItemsForm.get('items').updateValueAndValidity(); + this.itemsControl.setValidators(this.required ? [Validators.required] : []); + this.itemsControl.updateValueAndValidity(); } registerOnChange(fn: any): void { @@ -100,6 +159,7 @@ export class StringItemsListComponent implements ControlValueAccessor{ } registerOnTouched(fn: any): void { + this.onTouched = fn; } setDisabledState(isDisabled: boolean): void { @@ -112,48 +172,100 @@ export class StringItemsListComponent implements ControlValueAccessor{ } writeValue(value: Array | null): void { + this.searchText = ''; if (value != null && value.length > 0) { this.modelValue = [...value]; - this.stringItemsForm.get('items').setValue(value); + this.itemList = []; + if (this.predefinedValues) { + value.forEach(item => { + const findItem = this.predefinedValues.find(option => option.value === item); + if (findItem) { + this.itemList.push(findItem); + } + }); + } else { + value.forEach(item => this.itemList.push({value: item, name: item})); + } + this.itemsControl.setValue(this.itemList, {emitEvents: false}); } else { - this.stringItemsForm.get('items').setValue(null); + this.itemsControl.setValue(null, {emitEvents: false}); this.modelValue = null; + this.itemList = []; } + this.dirty = true; } addItem(event: MatChipInputEvent): void { - let item = event.value || ''; - const input = event.chipInput.inputElement; - item = item.trim(); + const item = event.value?.trim() ?? ''; if (item) { - if (!this.modelValue || this.modelValue.indexOf(item) === -1) { - if (!this.modelValue) { - this.modelValue = []; + if (this.predefinedValues) { + const findItems = this.predefinedValues + .filter(value => value.name.toLowerCase().includes(item.toLowerCase())); + if (findItems.length === 1) { + this.add(findItems[0]); } - this.modelValue.push(item); - this.stringItemsForm.get('items').setValue(this.modelValue); - } - this.propagateChange(this.modelValue); - if (input) { - input.value = ''; + } else { + this.add({value: item, name: item}); } } } - removeItems(item: string) { - const index = this.modelValue.indexOf(item); + removeItems(item: StringItemsOption) { + const index = this.modelValue.indexOf(item.value); if (index >= 0) { this.modelValue.splice(index, 1); + this.itemList.splice(index, 1); if (!this.modelValue.length) { this.modelValue = null; } - this.stringItemsForm.get('items').setValue(this.modelValue); + this.itemsControl.setValue(this.itemList); this.propagateChange(this.modelValue); + this.autocomplete?.closePanel(); + } + } + + onFocus() { + if (this.dirty) { + this.itemControl.updateValueAndValidity({onlySelf: true, emitEvent: true}); + this.dirty = false; + } + } + + displayValueFn(values?: StringItemsOption): string | undefined { + return values ? values.name : undefined; + } + + private add(item: StringItemsOption) { + if (!this.modelValue || this.modelValue.indexOf(item.value) === -1) { + if (!this.modelValue) { + this.modelValue = []; + } + this.modelValue.push(item.value); + this.itemList.push(item); + this.itemsControl.setValue(this.itemList); } + this.propagateChange(this.modelValue); + this.clear(); } - get stringItemsList(): string[] { - return this.stringItemsForm.get('items').value; + private fetchValues(searchText?: string): Observable> { + if (!this.predefinedValues?.length) { + return of([]); + } + this.searchText = searchText; + let result = this.predefinedValues; + if (searchText && searchText.length) { + result = this.predefinedValues.filter(option => option.name.toLowerCase().includes(searchText.toLowerCase())); + } + return of(result); } + private clear(value: string = '') { + this.stringItemInput.nativeElement.value = value; + this.itemControl.patchValue(value, {emitEvent: true}); + setTimeout(() => { + this.stringItemInput.nativeElement.blur(); + this.stringItemInput.nativeElement.focus(); + }, 0); + } } diff --git a/ui-ngx/src/app/shared/components/tb-error.component.ts b/ui-ngx/src/app/shared/components/tb-error.component.ts index 08959e2949..c0055df1f1 100644 --- a/ui-ngx/src/app/shared/components/tb-error.component.ts +++ b/ui-ngx/src/app/shared/components/tb-error.component.ts @@ -14,16 +14,17 @@ /// limitations under the License. /// -import { Component, Input } from '@angular/core'; +import { ChangeDetectorRef, Component, Input } from '@angular/core'; import { animate, state, style, transition, trigger } from '@angular/animations'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-error', template: ` -
- - {{message}} - +
+ + {{message}} +
`, styles: [` @@ -35,33 +36,37 @@ import { animate, state, style, transition, trigger } from '@angular/animations' trigger('animation', [ state('show', style({ opacity: 1, + transform: 'translateY(0)' })), state('hide', style({ opacity: 0, transform: 'translateY(-1rem)' })), - transition('show => hide', animate('200ms ease-out')), - transition('* => show', animate('200ms ease-in')) - + transition('* <=> *', animate('200ms ease-out')) ]), ] }) export class TbErrorComponent { - errorValue: any; - state: any; - message; + errorValue: string; + state = 'hide'; + message: string; + + constructor(private cd: ChangeDetectorRef) { + } + + @Input() + @coerceBoolean() + noMargin = false; @Input() set error(value) { - if (value && !this.message) { - this.message = value; - this.state = 'hide'; - setTimeout(() => { - this.state = 'show'; - }); - } else { + if (this.errorValue !== value) { this.errorValue = value; + if (value) { + this.message = value; + } this.state = value ? 'show' : 'hide'; + this.cd.markForCheck(); } } } diff --git a/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.scss b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.scss index 6f38e6a6a5..f6f24e2608 100644 --- a/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.scss +++ b/ui-ngx/src/app/shared/components/time/history-selector/history-selector.component.scss @@ -51,7 +51,7 @@ margin: 2px; line-height: 24px; - mat-icon { + .mat-icon { width: 24px; height: 24px; @@ -93,7 +93,7 @@ margin: 0; line-height: 28px; - mat-icon { + .mat-icon { width: 24px; height: 24px; font-size: 24px; diff --git a/ui-ngx/src/app/shared/components/time/quick-time-interval.component.scss b/ui-ngx/src/app/shared/components/time/quick-time-interval.component.scss index ff3a9c5438..74af632802 100644 --- a/ui-ngx/src/app/shared/components/time/quick-time-interval.component.scss +++ b/ui-ngx/src/app/shared/components/time/quick-time-interval.component.scss @@ -17,6 +17,7 @@ :host { min-width: 355px; + display: block; @media #{$mat-xs} { min-width: 0; diff --git a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.html b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.html index 0524f7e989..8e1a05d031 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow-panel.component.html +++ b/ui-ngx/src/app/shared/components/time/timewindow-panel.component.html @@ -17,7 +17,7 @@ -->
+ (selectedTabChange)="onTimewindowTypeChange()" [(selectedIndex)]="timewindow.selectedTab">
this.timewindowForm.get('realtime.interval').patchValue(timewindowFormValue.history.interval)); + } + } else { + this.timewindowForm.get('history').patchValue({ + historyType: HistoryWindowType[RealtimeWindowType[timewindowFormValue.realtime.realtimeType]], + timewindowMs: timewindowFormValue.realtime.timewindowMs, + quickInterval: timewindowFormValue.realtime.quickInterval + }); + setTimeout(() => this.timewindowForm.get('history.interval').patchValue(timewindowFormValue.realtime.interval)); + } + this.timewindowForm.patchValue({ + aggregation: { + type: timewindowFormValue.aggregation.type, + limit: timewindowFormValue.aggregation.limit + }, + timezone: timewindowFormValue.timezone + }); + } + update() { const timewindowFormValue = this.timewindowForm.getRawValue(); this.timewindow.realtime = { diff --git a/ui-ngx/src/app/shared/components/time/timewindow.component.html b/ui-ngx/src/app/shared/components/time/timewindow.component.html index e445e99c98..a53ea656fc 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow.component.html +++ b/ui-ngx/src/app/shared/components/time/timewindow.component.html @@ -41,26 +41,16 @@
- - + [class]="{'no-padding': noPadding}" + matTooltip="{{ 'timewindow.edit' | translate }}" + [matTooltipPosition]="tooltipPosition" + [style]="timewindowComponentStyle" + (click)="toggleTimewindow($event)"> + {{ computedTimewindowStyle.icon }} +
{{innerValue?.displayValue}} | {{innerValue?.displayTimezoneAbbr}} - - +
+ {{ computedTimewindowStyle.icon }}
diff --git a/ui-ngx/src/app/shared/components/time/timewindow.component.scss b/ui-ngx/src/app/shared/components/time/timewindow.component.scss index 695362197d..a00fe5bdc1 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow.component.scss +++ b/ui-ngx/src/app/shared/components/time/timewindow.component.scss @@ -17,19 +17,31 @@ min-width: 48px; margin: 8px 0; max-width: 100%; + &.no-margin { + margin: 0; + } .mdc-button { max-width: 100%; } section.tb-timewindow { - min-height: 32px; padding: 0 8px; + &.no-padding { + padding: 0; + } + line-height: 32px; + pointer-events: all; + cursor: pointer; + display: flex; + flex-direction: row; + place-content: center flex-start; + align-items: center; + gap: 4px; + width: 100%; - span { + .tb-timewindow-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - pointer-events: all; - cursor: pointer; } .timezone-abbr { diff --git a/ui-ngx/src/app/shared/components/time/timewindow.component.ts b/ui-ngx/src/app/shared/components/time/timewindow.component.ts index f9b40daac1..bd19cf0c2a 100644 --- a/ui-ngx/src/app/shared/components/time/timewindow.component.ts +++ b/ui-ngx/src/app/shared/components/time/timewindow.component.ts @@ -19,8 +19,12 @@ import { Component, ElementRef, forwardRef, + HostBinding, Injector, Input, + OnChanges, + OnInit, + SimpleChanges, StaticProvider, ViewContainerRef } from '@angular/core'; @@ -47,9 +51,18 @@ import { TimeService } from '@core/services/time.service'; import { TooltipPosition } from '@angular/material/tooltip'; import { deepClone, isDefinedAndNotNull } from '@core/utils'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; -import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; +import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; import { ComponentPortal } from '@angular/cdk/portal'; import { coerceBoolean } from '@shared/decorators/coercion'; +import { + ComponentStyle, + defaultTimewindowStyle, + iconStyle, + textStyle, + TimewindowStyle +} from '@shared/models/widget-settings.models'; +import { DEFAULT_OVERLAY_POSITIONS } from '@shared/models/overlay.models'; +import { fromEvent } from 'rxjs'; // @dynamic @Component({ @@ -64,7 +77,7 @@ import { coerceBoolean } from '@shared/decorators/coercion'; } ] }) -export class TimewindowComponent implements ControlValueAccessor { +export class TimewindowComponent implements ControlValueAccessor, OnInit, OnChanges { historyOnlyValue = false; @@ -83,6 +96,24 @@ export class TimewindowComponent implements ControlValueAccessor { return this.historyOnlyValue; } + get displayTypePrefix(): boolean { + return isDefinedAndNotNull(this.computedTimewindowStyle?.displayTypePrefix) + ? this.computedTimewindowStyle?.displayTypePrefix : true; + } + + @HostBinding('class.no-margin') + @Input() + @coerceBoolean() + noMargin = false; + + @Input() + @coerceBoolean() + noPadding = false; + + @Input() + @coerceBoolean() + disablePanel = false; + @Input() @coerceBoolean() forAllTimeEnabled = false; @@ -140,10 +171,10 @@ export class TimewindowComponent implements ControlValueAccessor { } @Input() - direction: 'left' | 'right' = 'left'; + tooltipPosition: TooltipPosition = 'above'; @Input() - tooltipPosition: TooltipPosition = 'above'; + timewindowStyle: TimewindowStyle; @Input() @coerceBoolean() @@ -153,6 +184,10 @@ export class TimewindowComponent implements ControlValueAccessor { timewindowDisabled: boolean; + computedTimewindowStyle: TimewindowStyle; + timewindowComponentStyle: ComponentStyle; + timewindowIconStyle: ComponentStyle; + private propagateChange = (_: any) => {}; constructor(private overlay: Overlay, @@ -165,10 +200,29 @@ export class TimewindowComponent implements ControlValueAccessor { public viewContainerRef: ViewContainerRef) { } + ngOnInit() { + this.updateTimewindowStyle(); + } + + ngOnChanges(changes: SimpleChanges): void { + for (const propName of Object.keys(changes)) { + const change = changes[propName]; + if (!change.firstChange && change.currentValue !== change.previousValue) { + if (propName === 'timewindowStyle') { + this.updateTimewindowStyle(); + this.updateDisplayValue(); + } + } + } + } + toggleTimewindow($event: Event) { if ($event) { $event.stopPropagation(); } + if (this.disablePanel) { + return; + } const config = new OverlayConfig({ panelClass: 'tb-timewindow-panel', backdropClass: 'cdk-overlay-transparent-backdrop', @@ -176,14 +230,10 @@ export class TimewindowComponent implements ControlValueAccessor { maxHeight: '80vh', height: 'min-content' }); - const connectedPosition: ConnectedPosition = { - originX: 'start', - originY: 'bottom', - overlayX: 'start', - overlayY: 'top' - }; - config.positionStrategy = this.overlay.position().flexibleConnectedTo(this.nativeElement) - .withPositions([connectedPosition]); + + config.positionStrategy = this.overlay.position() + .flexibleConnectedTo(this.nativeElement) + .withPositions(DEFAULT_OVERLAY_POSITIONS); const overlayRef = this.overlay.create(config); overlayRef.backdropClick().subscribe(() => { @@ -210,7 +260,11 @@ export class TimewindowComponent implements ControlValueAccessor { const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); const componentRef = overlayRef.attach(new ComponentPortal(TimewindowPanelComponent, this.viewContainerRef, injector)); + const resizeWindows$ = fromEvent(window, 'resize').subscribe(() => { + overlayRef.updatePosition(); + }); componentRef.onDestroy(() => { + resizeWindows$.unsubscribe(); if (componentRef.instance.result) { this.innerValue = componentRef.instance.result; this.timewindowDisabled = this.isTimewindowDisabled(); @@ -221,6 +275,17 @@ export class TimewindowComponent implements ControlValueAccessor { this.cd.detectChanges(); } + private updateTimewindowStyle() { + if (!this.asButton) { + this.computedTimewindowStyle = {...defaultTimewindowStyle, ...(this.timewindowStyle || {})}; + this.timewindowComponentStyle = textStyle(this.computedTimewindowStyle.font); + if (this.computedTimewindowStyle.color) { + this.timewindowComponentStyle.color = this.computedTimewindowStyle.color; + } + this.timewindowIconStyle = this.computedTimewindowStyle.iconSize ? iconStyle(this.computedTimewindowStyle.iconSize) : {}; + } + } + private onHistoryOnlyChanged(): boolean { if (this.historyOnlyValue && this.innerValue && this.innerValue.selectedTab !== TimewindowType.HISTORY) { this.innerValue.selectedTab = TimewindowType.HISTORY; @@ -264,7 +329,7 @@ export class TimewindowComponent implements ControlValueAccessor { updateDisplayValue() { if (this.innerValue.selectedTab === TimewindowType.REALTIME && !this.historyOnly) { - this.innerValue.displayValue = this.translate.instant('timewindow.realtime') + ' - '; + this.innerValue.displayValue = this.displayTypePrefix ? (this.translate.instant('timewindow.realtime') + ' - ') : ''; if (this.innerValue.realtime.realtimeType === RealtimeWindowType.INTERVAL) { this.innerValue.displayValue += this.translate.instant(QuickTimeIntervalTranslationMap.get(this.innerValue.realtime.quickInterval)); } else { @@ -272,7 +337,7 @@ export class TimewindowComponent implements ControlValueAccessor { this.millisecondsToTimeStringPipe.transform(this.innerValue.realtime.timewindowMs); } } else { - this.innerValue.displayValue = (!this.historyOnly || this.alwaysDisplayTypePrefix) ? + this.innerValue.displayValue = this.displayTypePrefix && (!this.historyOnly || this.alwaysDisplayTypePrefix) ? (this.translate.instant('timewindow.history') + ' - ') : ''; if (this.innerValue.history.historyType === HistoryWindowType.LAST_INTERVAL) { this.innerValue.displayValue += this.translate.instant('timewindow.last-prefix') + ' ' + diff --git a/ui-ngx/src/app/shared/components/toggle-header.component.html b/ui-ngx/src/app/shared/components/toggle-header.component.html index e36cdbd592..7aa391b3b4 100644 --- a/ui-ngx/src/app/shared/components/toggle-header.component.html +++ b/ui-ngx/src/app/shared/components/toggle-header.component.html @@ -15,16 +15,36 @@ limitations under the License. --> - - {{ option.name }} - + +
+ + {{ option.name }} + +
+ - + {{ option.name }} diff --git a/ui-ngx/src/app/shared/components/toggle-header.component.scss b/ui-ngx/src/app/shared/components/toggle-header.component.scss index a9542012d4..7091c68c93 100644 --- a/ui-ngx/src/app/shared/components/toggle-header.component.scss +++ b/ui-ngx/src/app/shared/components/toggle-header.component.scss @@ -14,11 +14,38 @@ * limitations under the License. */ -@import "../../../theme"; @import "../../../scss/constants"; +:host { + max-width: 100%; + display: grid; + grid-template-columns: min-content minmax(auto, 1fr) min-content; + .tb-toggle-header-pagination-button { + display: none; + } + &.tb-toggle-header-pagination-controls-enabled { + .tb-toggle-header-pagination-button { + display: block; + } + } + .tb-toggle-container, .tb-toggle-header-select { + display: inline-grid; + grid-column: 2; + overflow: hidden; + } + .tb-toggle-container { + &.tb-disable-pagination { + overflow: visible; + } + } + .tb-toggle-header { + transition: transform 500ms cubic-bezier(0.35, 0, 0.25, 1); + } +} + :host ::ng-deep { .mat-button-toggle-group.mat-button-toggle-group-appearance-standard.tb-toggle-header { + overflow: visible; width: 100%; border-radius: 100px; height: 32px; @@ -80,6 +107,33 @@ } } } + &.tb-disabled { + pointer-events: none; + background: rgba(0, 0, 0, 0.03); + + .mat-button-toggle.mat-button-toggle-appearance-standard { + color: rgba(0, 0, 0, 0.28); + + &.mat-button-toggle-checked { + .mat-button-toggle-button { + background: transparent; + color: rgba(0, 0, 0, 0.38); + border-color: rgba(0, 0, 0, 0.38); + } + } + } + &.tb-fill { + .mat-button-toggle.mat-button-toggle-appearance-standard { + &.mat-button-toggle-checked { + .mat-button-toggle-button { + background: rgba(0, 0, 0, 0.12); + color: rgba(0, 0, 0, 0.38); + border: transparent; + } + } + } + } + } } @media #{$mat-md-lg} { .mat-button-toggle-group.mat-button-toggle-group-appearance-standard.tb-toggle-header:not(.tb-ignore-md-lg) { @@ -125,9 +179,6 @@ line-height: 16px; letter-spacing: 0.25px; } - .mat-mdc-select-value { - color: rgba(0, 0, 0, 0.38); - } .mat-mdc-select-arrow-wrapper { height: 12px; padding-left: 6px; diff --git a/ui-ngx/src/app/shared/components/toggle-header.component.ts b/ui-ngx/src/app/shared/components/toggle-header.component.ts index a7f37f53a4..5781f9fac9 100644 --- a/ui-ngx/src/app/shared/components/toggle-header.component.ts +++ b/ui-ngx/src/app/shared/components/toggle-header.component.ts @@ -15,30 +15,33 @@ /// import { - AfterContentInit, + AfterContentChecked, + AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectorRef, Component, - ContentChildren, EventEmitter, + ContentChildren, + Directive, + ElementRef, + EventEmitter, + HostBinding, Input, - OnInit, Output, + OnDestroy, + OnInit, + Output, QueryList, ViewChild } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { AdminService } from '@core/http/admin.service'; -import { UpdateMessage } from '@shared/models/settings.models'; -import { getCurrentAuthUser } from '@core/auth/auth.selectors'; -import { Authority } from '@shared/models/authority.enum'; -import { of, Subscription } from 'rxjs'; -import { MatStepper } from '@angular/material/stepper'; -import { MatButtonToggle, MatButtonToggleGroup } from '@angular/material/button-toggle'; +import { BehaviorSubject, Subject, Subscription } from 'rxjs'; import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout'; import { MediaBreakpoints } from '@shared/models/constants'; import { coerceBoolean } from '@shared/decorators/coercion'; -import { BreadCrumb } from '@shared/components/breadcrumb'; +import { startWith, takeUntil } from 'rxjs/operators'; +import { Platform } from '@angular/cdk/platform'; +import { MatButtonToggle, MatButtonToggleGroup } from '@angular/material/button-toggle'; export interface ToggleHeaderOption { name: string; @@ -47,12 +50,103 @@ export interface ToggleHeaderOption { export type ToggleHeaderAppearance = 'fill' | 'fill-invert' | 'stroked'; +export type ScrollDirection = 'after' | 'before'; + +@Directive( + { + // eslint-disable-next-line @angular-eslint/directive-selector + selector: 'tb-toggle-option', + } +) +// eslint-disable-next-line @angular-eslint/directive-class-suffix +export class ToggleOption { + + @Input() value: any; + + get viewValue(): string { + return (this._element?.nativeElement.textContent || '').trim(); + } + + constructor( + private _element: ElementRef + ) {} +} + +@Directive() +export abstract class _ToggleBase extends PageComponent implements AfterContentInit, OnDestroy { + + @ContentChildren(ToggleOption) toggleOptions: QueryList; + + @Input() + options: ToggleHeaderOption[] = []; + + protected _destroyed = new Subject(); + + protected constructor(protected store: Store) { + super(store); + } + + ngAfterContentInit(): void { + this.toggleOptions.changes.pipe(startWith(null), takeUntil(this._destroyed)).subscribe(() => { + this.syncToggleHeaderOptions(); + }); + } + + ngOnDestroy() { + this._destroyed.next(); + this._destroyed.complete(); + } + + private syncToggleHeaderOptions() { + if (this.toggleOptions?.length) { + this.options.length = 0; + this.toggleOptions.forEach(option => { + this.options.push( + { name: option.viewValue, + value: option.value + } + ); + }); + } + } + +} + @Component({ selector: 'tb-toggle-header', templateUrl: './toggle-header.component.html', styleUrls: ['./toggle-header.component.scss'] }) -export class ToggleHeaderComponent extends PageComponent implements OnInit { +export class ToggleHeaderComponent extends _ToggleBase implements OnInit, AfterViewInit, AfterContentInit, + AfterContentChecked, AfterViewChecked, OnDestroy { + + @ViewChild('toggleGroup', {static: false}) + toggleGroup: ElementRef; + + @ViewChild(MatButtonToggleGroup, {static: false}) + buttonToggleGroup: MatButtonToggleGroup; + + @ViewChild('toggleGroupContainer', {static: false}) + toggleGroupContainer: ElementRef; + + @HostBinding('class.tb-toggle-header-pagination-controls-enabled') + private showPaginationControls = false; + private _showPaginationControlsChanged = false; + + private toggleGroupResize$: ResizeObserver; + + leftPaginationEnabled = false; + rightPaginationEnabled = false; + + private _scrollDistance = 0; + private _scrollDistanceChanged: boolean; + + get scrollDistance(): number { + return this._scrollDistance; + } + set scrollDistance(value: number) { + this._scrollTo(value); + } @Input() value: any; @@ -61,14 +155,26 @@ export class ToggleHeaderComponent extends PageComponent implements OnInit { valueChange = new EventEmitter(); @Input() - options: ToggleHeaderOption[]; + name: string; + + @Input() + @coerceBoolean() + disablePagination = false; @Input() - name: string; + selectMediaBreakpoint = 'md-lg'; @Input() @coerceBoolean() - useSelectOnMdLg = true; + set useSelectOnMdLg(value: boolean) { + if (value) { + this.selectMediaBreakpoint = 'md-lg'; + } else { + if (this.selectMediaBreakpoint === 'md-lg') { + this.selectMediaBreakpoint = ''; + } + } + } @Input() @coerceBoolean() @@ -77,28 +183,214 @@ export class ToggleHeaderComponent extends PageComponent implements OnInit { @Input() appearance: ToggleHeaderAppearance = 'stroked'; - isMdLg: boolean; + @Input() + @coerceBoolean() + disabled = false; + + get isMdLg(): boolean { + return !this.ignoreMdLgSize && this.isMdLgValue; + } + + private isMdLgValue: boolean; + private useSelectSubject = new BehaviorSubject(false); + + useSelect$ = this.useSelectSubject.asObservable(); private observeBreakpointSubscription: Subscription; constructor(protected store: Store, private cd: ChangeDetectorRef, + private platform: Platform, private breakpointObserver: BreakpointObserver) { super(store); } ngOnInit() { - this.isMdLg = this.breakpointObserver.isMatched(MediaBreakpoints['md-lg']); + const mediaBreakpoints = [MediaBreakpoints['md-lg']]; + if (this.selectMediaBreakpoint && this.selectMediaBreakpoint !== 'md-lg') { + mediaBreakpoints.push(MediaBreakpoints[this.selectMediaBreakpoint]); + } this.observeBreakpointSubscription = this.breakpointObserver - .observe(MediaBreakpoints['md-lg']) + .observe(mediaBreakpoints) .subscribe((state: BreakpointState) => { - this.isMdLg = state.matches; + this.isMdLgValue = state.breakpoints[MediaBreakpoints['md-lg']]; + if (this.selectMediaBreakpoint) { + this.useSelectSubject.next(state.breakpoints[MediaBreakpoints[this.selectMediaBreakpoint]]); + } else { + this.useSelectSubject.next(false); + } this.cd.markForCheck(); } ); + if (!this.disablePagination) { + this.valueChange.pipe(takeUntil(this._destroyed)).subscribe(() => { + this.scrollToToggleOptionValue(); + }); + } + } + + ngOnDestroy() { + this.stopObservePagination(); + super.ngOnDestroy(); + } + + ngAfterViewInit() { + if (!this.disablePagination) { + this.useSelect$.pipe(takeUntil(this._destroyed)).subscribe((useSelect) => { + if (useSelect) { + this.removePagination(); + } else { + setTimeout(() => { + this.startObservePagination(); + }, 0); + } + }); + } + } + + ngAfterContentChecked() { + if (this._scrollDistanceChanged) { + this.updateToggleHeaderScrollPosition(); + this._scrollDistanceChanged = false; + this.cd.markForCheck(); + } + } + + ngAfterViewChecked() { + if (this._showPaginationControlsChanged) { + this.scrollToToggleOptionValue(); + this._showPaginationControlsChanged = false; + this.cd.markForCheck(); + } } trackByHeaderOption(index: number, option: ToggleHeaderOption){ return option.value; } + + handlePaginatorClick(direction: ScrollDirection, $event: Event) { + if ($event) { + $event.stopPropagation(); + } + this.scrollHeader(direction); + } + + handlePaginatorTouchStart(direction: ScrollDirection, $event: Event) { + if (direction === 'before' && !this.leftPaginationEnabled || + direction === 'after' && !this.rightPaginationEnabled) { + $event.preventDefault(); + } + } + + private startObservePagination() { + this.toggleGroupResize$ = new ResizeObserver(() => { + this.updatePagination(); + }); + this.toggleGroupResize$.observe(this.toggleGroupContainer.nativeElement); + } + + private removePagination() { + this.stopObservePagination(); + this.showPaginationControls = false; + } + + private stopObservePagination() { + if (this.toggleGroupResize$) { + this.toggleGroupResize$.disconnect(); + this.toggleGroupResize$ = null; + } + } + + private scrollHeader(direction: ScrollDirection) { + const viewLength = this.toggleGroup.nativeElement.offsetWidth; + // Move the scroll distance one-third the length of the tab list's viewport. + const scrollAmount = ((direction === 'before' ? -1 : 1) * viewLength) / 3; + return this._scrollTo(this._scrollDistance + scrollAmount); + } + + private scrollToToggleOptionValue() { + if (this.buttonToggleGroup && this.buttonToggleGroup.selected) { + const selectedToggleButton = this.buttonToggleGroup.selected as MatButtonToggle; + const index = this.options.findIndex(o => o.value === selectedToggleButton.value); + const isLast = index === this.options.length - 1; + const isFirst = index === 0; + const viewLength = this.toggleGroupContainer.nativeElement.offsetWidth; + const {offsetLeft, offsetWidth} = (selectedToggleButton._buttonElement.nativeElement.offsetParent as HTMLElement); + const labelBeforePos = isFirst ? 0 : offsetLeft; + const labelAfterPos = isLast ? this.toggleGroup.nativeElement.scrollWidth : labelBeforePos + offsetWidth; + const beforeVisiblePos = this.scrollDistance; + const afterVisiblePos = this.scrollDistance + viewLength; + if (labelBeforePos < beforeVisiblePos) { + this.scrollDistance -= beforeVisiblePos - labelBeforePos; + } else if (labelAfterPos > afterVisiblePos) { + this.scrollDistance += Math.min( + labelAfterPos - afterVisiblePos, + labelBeforePos - beforeVisiblePos, + ); + } + } + } + + private updatePagination() { + this.checkPaginationEnabled(); + this.checkPaginationControls(); + this.updateToggleHeaderScrollPosition(); + } + + private checkPaginationEnabled() { + if (this.toggleGroupContainer) { + const isEnabled = this.toggleGroup.nativeElement.scrollWidth > this.toggleGroupContainer.nativeElement.offsetWidth; + if (isEnabled !== this.showPaginationControls) { + if (!isEnabled) { + this.scrollDistance = 0; + } else { + this._showPaginationControlsChanged = true; + } + this.cd.markForCheck(); + this.showPaginationControls = isEnabled; + } + } else { + this.showPaginationControls = false; + } + } + + private checkPaginationControls() { + if (!this.showPaginationControls) { + this.leftPaginationEnabled = this.rightPaginationEnabled = false; + } else { + // Check if the pagination arrows should be activated. + this.leftPaginationEnabled = this.scrollDistance > 0; + this.rightPaginationEnabled = this.scrollDistance < this.getMaxScrollDistance(); + this.cd.markForCheck(); + } + } + + private getMaxScrollDistance(): number { + const lengthOfToggleGroup = this.toggleGroup.nativeElement.scrollWidth; + const viewLength = this.toggleGroupContainer.nativeElement.offsetWidth; + return lengthOfToggleGroup - viewLength || 0; + } + + private _scrollTo(position: number) { + if (!this.showPaginationControls) { + return {maxScrollDistance: 0, distance: 0}; + } else { + const maxScrollDistance = this.getMaxScrollDistance(); + this._scrollDistance = Math.max(0, Math.min(maxScrollDistance, position)); + this._scrollDistanceChanged = true; + this.checkPaginationControls(); + return {maxScrollDistance, distance: this._scrollDistance}; + } + } + + private updateToggleHeaderScrollPosition() { + if (this.toggleGroupContainer) { + const scrollDistance = this.scrollDistance; + const translateX = -scrollDistance; + this.toggleGroup.nativeElement.style.transform = `translateX(${Math.round(translateX)}px)`; + if (this.platform.TRIDENT || this.platform.EDGE) { + this.toggleGroupContainer.nativeElement.scrollLeft = 0; + } + } + } } diff --git a/ui-ngx/src/app/shared/components/toggle-select.component.html b/ui-ngx/src/app/shared/components/toggle-select.component.html new file mode 100644 index 0000000000..c03e0cfcbf --- /dev/null +++ b/ui-ngx/src/app/shared/components/toggle-select.component.html @@ -0,0 +1,28 @@ + + + diff --git a/ui-ngx/src/app/shared/components/toggle-select.component.ts b/ui-ngx/src/app/shared/components/toggle-select.component.ts new file mode 100644 index 0000000000..c1541d1128 --- /dev/null +++ b/ui-ngx/src/app/shared/components/toggle-select.component.ts @@ -0,0 +1,82 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, forwardRef, HostBinding, Input } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { _ToggleBase, ToggleHeaderAppearance } from '@shared/components/toggle-header.component'; +import { coerceBoolean } from '@shared/decorators/coercion'; + +@Component({ + selector: 'tb-toggle-select', + templateUrl: './toggle-select.component.html', + styleUrls: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ToggleSelectComponent), + multi: true + } + ] +}) +export class ToggleSelectComponent extends _ToggleBase implements ControlValueAccessor { + + @HostBinding('style.maxWidth') + get maxWidth() { return '100%'; } + + @Input() + @coerceBoolean() + disabled: boolean; + + @Input() + selectMediaBreakpoint; + + @Input() + appearance: ToggleHeaderAppearance = 'stroked'; + + @Input() + @coerceBoolean() + disablePagination = false; + + modelValue: any; + + private propagateChange = null; + + constructor(protected store: Store) { + super(store); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + } + + writeValue(value: any): void { + this.modelValue = value; + } + + updateModel(value: any) { + this.modelValue = value; + this.propagateChange(this.modelValue); + } +} diff --git a/ui-ngx/src/app/shared/components/unit-input.component.html b/ui-ngx/src/app/shared/components/unit-input.component.html new file mode 100644 index 0000000000..b0f3625eb0 --- /dev/null +++ b/ui-ngx/src/app/shared/components/unit-input.component.html @@ -0,0 +1,47 @@ + + + + + + warning + + + + + + + + diff --git a/ui-ngx/src/app/shared/components/unit-input.component.scss b/ui-ngx/src/app/shared/components/unit-input.component.scss new file mode 100644 index 0000000000..25280e51ec --- /dev/null +++ b/ui-ngx/src/app/shared/components/unit-input.component.scss @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +.tb-autocomplete.tb-unit-input-autocomplete { + .mat-mdc-option { + border-bottom: none; + .mdc-list-item__primary-text { + flex: 1; + display: flex; + flex-direction: row; + gap: 8px; + .tb-unit-name, .tb-unit-symbol { + font-size: 14px; + font-weight: 400; + line-height: 20px; + letter-spacing: 0.2px; + } + .tb-unit-symbol { + color: rgba(0, 0, 0, 0.38); + min-width: 22px; + text-align: end; + b { + color: rgba(0, 0, 0, 0.87); + } + } + } + } +} diff --git a/ui-ngx/src/app/shared/components/unit-input.component.ts b/ui-ngx/src/app/shared/components/unit-input.component.ts new file mode 100644 index 0000000000..ce84aede7f --- /dev/null +++ b/ui-ngx/src/app/shared/components/unit-input.component.ts @@ -0,0 +1,189 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + Component, + ElementRef, + forwardRef, + HostBinding, + Input, + OnInit, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + FormControl, + NG_VALUE_ACCESSOR, + Validators +} from '@angular/forms'; +import { Observable, of, shareReplay, switchMap } from 'rxjs'; +import { getUnits, searchUnits, Unit, unitBySymbol, UnitsType } from '@shared/models/unit.models'; +import { map, mergeMap, tap } from 'rxjs/operators'; +import { TranslateService } from '@ngx-translate/core'; +import { ResourcesService } from '@core/services/resources.service'; +import { coerceBoolean } from '@shared/decorators/coercion'; + +@Component({ + selector: 'tb-unit-input', + templateUrl: './unit-input.component.html', + styleUrls: ['./unit-input.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => UnitInputComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class UnitInputComponent implements ControlValueAccessor, OnInit { + + @HostBinding('style.display') get hostDisplay() {return 'flex';}; + + unitsFormControl: FormControl; + + modelValue: string | null; + + @Input() + disabled: boolean; + + @Input() + @coerceBoolean() + required = false; + + @Input() + tagFilter: UnitsType; + + @ViewChild('unitInput', {static: true}) unitInput: ElementRef; + + filteredUnits: Observable>; + + searchText = ''; + + private dirty = false; + + private fetchUnits$: Observable> = null; + + private propagateChange = (_val: any) => {}; + + constructor(private fb: FormBuilder, + private resourcesService: ResourcesService, + private translate: TranslateService) { + } + + ngOnInit() { + this.unitsFormControl = this.fb.control('', this.required ? [Validators.required] : []); + this.filteredUnits = this.unitsFormControl.valueChanges + .pipe( + tap(value => { + this.updateView(value); + }), + map(value => (value as Unit)?.symbol ? (value as Unit).symbol : (value ? value as string : '')), + mergeMap(symbol => this.fetchUnits(symbol)) + ); + } + + writeValue(symbol?: string): void { + this.searchText = ''; + this.modelValue = symbol; + of(symbol).pipe( + switchMap(value => value + ? this.unitsConstant().pipe(map(units => unitBySymbol(units, value) ?? value)) + : of(null)) + ).subscribe(result => { + this.unitsFormControl.patchValue(result, {emitEvent: false}); + this.dirty = true; + }); + } + + onFocus() { + if (this.dirty) { + this.unitsFormControl.updateValueAndValidity({onlySelf: true, emitEvent: true}); + this.dirty = false; + } + } + + updateView(value: Unit | string | null) { + const res: string = (value as Unit)?.symbol ? (value as Unit)?.symbol : (value as string); + if (this.modelValue !== res) { + this.modelValue = res; + this.propagateChange(this.modelValue); + } + } + + displayUnitFn(unit?: Unit | string): string | undefined { + if (unit) { + if ((unit as Unit).symbol) { + return (unit as Unit).symbol; + } else { + return unit as string; + } + } + return undefined; + } + + fetchUnits(searchText?: string): Observable> { + this.searchText = searchText; + return this.unitsConstant().pipe( + map(unit => searchUnits(unit, searchText)) + ); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.unitsFormControl.disable({emitEvent: false}); + } else { + this.unitsFormControl.enable({emitEvent: false}); + } + } + + clear() { + this.unitsFormControl.patchValue(null, {emitEvent: true}); + setTimeout(() => { + this.unitInput.nativeElement.blur(); + this.unitInput.nativeElement.focus(); + }, 0); + } + + private unitsConstant(): Observable> { + if (this.fetchUnits$ === null) { + this.fetchUnits$ = getUnits(this.resourcesService).pipe( + map((units) => { + if (this.tagFilter) { + units = units.filter(u => u.tags.includes(this.tagFilter)); + } + return units.map(u => ({ + symbol: u.symbol, + name: this.translate.instant(u.name), + tags: u.tags + })); + }), + shareReplay(1) + ); + } + return this.fetchUnits$; + } +} diff --git a/ui-ngx/src/app/shared/components/user-menu.component.html b/ui-ngx/src/app/shared/components/user-menu.component.html index a25a0fec25..afb6e66437 100644 --- a/ui-ngx/src/app/shared/components/user-menu.component.html +++ b/ui-ngx/src/app/shared/components/user-menu.component.html @@ -28,13 +28,9 @@
- - - + {{ (requiredText ? requiredText : 'value.json-value-required') | translate }} diff --git a/ui-ngx/src/app/shared/components/value-input.component.ts b/ui-ngx/src/app/shared/components/value-input.component.ts index 4ba7abf157..6d57b1ddd2 100644 --- a/ui-ngx/src/app/shared/components/value-input.component.ts +++ b/ui-ngx/src/app/shared/components/value-input.component.ts @@ -73,7 +73,8 @@ export class ValueInputComponent implements OnInit, ControlValueAccessor { disableClose: true, panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], data: { - jsonValue: this.modelValue + jsonValue: this.modelValue, + required: true } }).afterClosed().subscribe( (res) => { @@ -115,7 +116,8 @@ export class ValueInputComponent implements OnInit, ControlValueAccessor { } updateView() { - if (this.inputForm.valid || this.valueType === ValueType.BOOLEAN) { + if (this.inputForm.valid || this.valueType === ValueType.BOOLEAN || + (this.valueType === ValueType.JSON && Array.isArray(this.modelValue))) { this.propagateChange(this.modelValue); } else { this.propagateChange(null); diff --git a/ui-ngx/src/app/shared/components/widgets-bundle-select.component.html b/ui-ngx/src/app/shared/components/widgets-bundle-select.component.html index bffcbc9c37..7642debf37 100644 --- a/ui-ngx/src/app/shared/components/widgets-bundle-select.component.html +++ b/ui-ngx/src/app/shared/components/widgets-bundle-select.component.html @@ -19,9 +19,9 @@
@@ -29,6 +29,8 @@ widgets-bundle.system
+ +
{{widgetsBundle.title}} diff --git a/ui-ngx/src/app/shared/components/widgets-bundle-select.component.ts b/ui-ngx/src/app/shared/components/widgets-bundle-select.component.ts index e1b403e89b..271db6d26a 100644 --- a/ui-ngx/src/app/shared/components/widgets-bundle-select.component.ts +++ b/ui-ngx/src/app/shared/components/widgets-bundle-select.component.ts @@ -20,12 +20,12 @@ import { Observable } from 'rxjs'; import { map, share, tap } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; -import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; import { WidgetService } from '@core/http/widget.service'; import { isDefined } from '@core/utils'; import { NULL_UUID } from '@shared/models/id/has-uuid'; import { getCurrentAuthState } from '@core/auth/auth.selectors'; +import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ selector: 'tb-widgets-bundle-select', @@ -44,30 +44,30 @@ export class WidgetsBundleSelectComponent implements ControlValueAccessor, OnIni bundlesScope: 'system' | 'tenant'; @Input() + @coerceBoolean() selectFirstBundle: boolean; @Input() selectBundleAlias: string; - private requiredValue: boolean; - get required(): boolean { - return this.requiredValue; - } @Input() - set required(value: boolean) { - this.requiredValue = coerceBooleanProperty(value); - } + @coerceBoolean() + required: boolean; @Input() disabled: boolean; + @Input() + excludeBundleIds: Array; + widgetsBundles$: Observable>; widgetsBundles: Array; widgetsBundle: WidgetsBundle | null; - private propagateChange = (v: any) => { }; + onTouched = () => {}; + private propagateChange: (value: any) => void = () => {}; constructor(private store: Store, private widgetService: WidgetService) { @@ -78,6 +78,7 @@ export class WidgetsBundleSelectComponent implements ControlValueAccessor, OnIni } registerOnTouched(fn: any): void { + this.onTouched = fn; } ngOnInit() { @@ -161,6 +162,12 @@ export class WidgetsBundleSelectComponent implements ControlValueAccessor, OnIni } else { widgetsBundlesObservable = this.widgetService.getAllWidgetsBundles(); } + if (this.excludeBundleIds && this.excludeBundleIds.length) { + widgetsBundlesObservable = widgetsBundlesObservable.pipe( + map((widgetBundles) => + widgetBundles.filter(w => !this.excludeBundleIds.includes(w.id.id))) + ); + } return widgetsBundlesObservable; } diff --git a/ui-ngx/src/app/shared/decorators/coercion.ts b/ui-ngx/src/app/shared/decorators/coercion.ts index c60cda730d..d1d00828f1 100644 --- a/ui-ngx/src/app/shared/decorators/coercion.ts +++ b/ui-ngx/src/app/shared/decorators/coercion.ts @@ -22,87 +22,128 @@ import { coerceStringArray as coerceStringArrayAngular } from '@angular/cdk/coercion'; -export const coerceBoolean = () => (target: any, key: string): void => { - const getter = function() { - return this['__' + key]; - }; - - const setter = function(next: any) { - this['__' + key] = coerceBooleanProperty(next); - }; - - Object.defineProperty(target, key, { - get: getter, - set: setter, - enumerable: true, - configurable: true, - }); +export const coerceBoolean = () => (target: any, key: string, propertyDescriptor?: PropertyDescriptor): void => { + if (!!propertyDescriptor && !!propertyDescriptor.set) { + const original = propertyDescriptor.set; + + propertyDescriptor.set = function(next) { + original.apply(this, [coerceBooleanProperty(next)]); + }; + } else { + const getter = function() { + return this['__' + key]; + }; + + const setter = function(next: any) { + this['__' + key] = coerceBooleanProperty(next); + }; + + Object.defineProperty(target, key, { + get: getter, + set: setter, + enumerable: true, + configurable: true, + }); + } }; -export const coerceNumber = () => (target: any, key: string): void => { - const getter = function(): number { - return this['__' + key]; - }; - - const setter = function(next: any) { - this['__' + key] = coerceNumberProperty(next); - }; - - Object.defineProperty(target, key, { - get: getter, - set: setter, - enumerable: true, - configurable: true, - }); +export const coerceNumber = () => (target: any, key: string, propertyDescriptor?: PropertyDescriptor): void => { + if (!!propertyDescriptor && !!propertyDescriptor.set) { + const original = propertyDescriptor.set; + + propertyDescriptor.set = function(next) { + original.apply(this, [coerceNumberProperty(next)]); + }; + } else { + const getter = function() { + return this['__' + key]; + }; + + const setter = function(next: any) { + this['__' + key] = coerceNumberProperty(next); + }; + + Object.defineProperty(target, key, { + get: getter, + set: setter, + enumerable: true, + configurable: true, + }); + } }; -export const coerceCssPixelValue = () => (target: any, key: string): void => { - const getter = function(): string { - return this['__' + key]; - }; - - const setter = function(next: any) { - this['__' + key] = coerceCssPixelValueAngular(next); - }; - - Object.defineProperty(target, key, { - get: getter, - set: setter, - enumerable: true, - configurable: true, - }); +export const coerceCssPixelValue = () => (target: any, key: string, propertyDescriptor?: PropertyDescriptor): void => { + if (!!propertyDescriptor && !!propertyDescriptor.set) { + const original = propertyDescriptor.set; + + propertyDescriptor.set = function(next) { + original.apply(this, [coerceCssPixelValueAngular(next)]); + }; + } else { + const getter = function() { + return this['__' + key]; + }; + + const setter = function(next: any) { + this['__' + key] = coerceCssPixelValueAngular(next); + }; + + Object.defineProperty(target, key, { + get: getter, + set: setter, + enumerable: true, + configurable: true, + }); + } }; -export const coerceArray = () => (target: any, key: string): void => { - const getter = function(): any[] { - return this['__' + key]; - }; - - const setter = function(next: any) { - this['__' + key] = coerceArrayAngular(next); - }; - - Object.defineProperty(target, key, { - get: getter, - set: setter, - enumerable: true, - configurable: true, - }); +export const coerceArray = () => (target: any, key: string, propertyDescriptor?: PropertyDescriptor): void => { + if (!!propertyDescriptor && !!propertyDescriptor.set) { + const original = propertyDescriptor.set; + + propertyDescriptor.set = function(next) { + original.apply(this, [coerceArrayAngular(next)]); + }; + } else { + const getter = function() { + return this['__' + key]; + }; + + const setter = function(next: any) { + this['__' + key] = coerceArrayAngular(next); + }; + + Object.defineProperty(target, key, { + get: getter, + set: setter, + enumerable: true, + configurable: true, + }); + } }; -export const coerceStringArray = (separator?: string | RegExp) => (target: any, key: string): void => { - const getter = function(): string[] { - return this['__' + key]; - }; - - const setter = function(next: any) { - this['__' + key] = coerceStringArrayAngular(next, separator); - }; - - Object.defineProperty(target, key, { - get: getter, - set: setter, - enumerable: true, - configurable: true, - }); +export const coerceStringArray = (separator?: string | RegExp) => + (target: any, key: string, propertyDescriptor?: PropertyDescriptor): void => { + if (!!propertyDescriptor && !!propertyDescriptor.set) { + const original = propertyDescriptor.set; + + propertyDescriptor.set = function(next) { + original.apply(this, [coerceStringArrayAngular(next, separator)]); + }; + } else { + const getter = function() { + return this['__' + key]; + }; + + const setter = function(next: any) { + this['__' + key] = coerceStringArrayAngular(next, separator); + }; + + Object.defineProperty(target, key, { + get: getter, + set: setter, + enumerable: true, + configurable: true, + }); + } }; diff --git a/ui-ngx/src/app/shared/import-export/export-widgets-bundle-dialog.component.html b/ui-ngx/src/app/shared/import-export/export-widgets-bundle-dialog.component.html new file mode 100644 index 0000000000..a23a55fecf --- /dev/null +++ b/ui-ngx/src/app/shared/import-export/export-widgets-bundle-dialog.component.html @@ -0,0 +1,46 @@ + + +

widgets-bundle.export

+ + +
+ + +
+
+ {{ 'widgets-bundle.export-widgets-bundle-widgets-prompt' | translate }} +
+
+
+ + +
diff --git a/ui-ngx/src/app/shared/import-export/export-widgets-bundle-dialog.component.ts b/ui-ngx/src/app/shared/import-export/export-widgets-bundle-dialog.component.ts new file mode 100644 index 0000000000..b6a4664aed --- /dev/null +++ b/ui-ngx/src/app/shared/import-export/export-widgets-bundle-dialog.component.ts @@ -0,0 +1,71 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, Inject, OnInit } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { FormControl } from '@angular/forms'; +import { Router } from '@angular/router'; +import { DialogComponent } from '@app/shared/components/dialog.component'; +import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; + +export interface ExportWidgetsBundleDialogData { + widgetsBundle: WidgetsBundle; + includeBundleWidgetsInExport: boolean; +} + +export interface ExportWidgetsBundleDialogResult { + exportWidgets: boolean; +} + +@Component({ + selector: 'tb-export-widgets-bundle-dialog', + templateUrl: './export-widgets-bundle-dialog.component.html', + providers: [], + styleUrls: [] +}) +export class ExportWidgetsBundleDialogComponent extends DialogComponent + implements OnInit { + + widgetsBundle: WidgetsBundle; + + exportWidgetsFormControl = new FormControl(false); + + constructor(protected store: Store, + protected router: Router, + @Inject(MAT_DIALOG_DATA) public data: ExportWidgetsBundleDialogData, + public dialogRef: MatDialogRef) { + super(store, router, dialogRef); + this.widgetsBundle = data.widgetsBundle; + if (data.includeBundleWidgetsInExport) { + this.exportWidgetsFormControl.patchValue(data.includeBundleWidgetsInExport, {emitEvent: false}); + } + } + + ngOnInit(): void { + } + + cancel(): void { + this.dialogRef.close(null); + } + + export(): void { + this.dialogRef.close({ + exportWidgets: this.exportWidgetsFormControl.value + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/import-export/import-dialog-csv.component.html b/ui-ngx/src/app/shared/import-export/import-dialog-csv.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/import-export/import-dialog-csv.component.html rename to ui-ngx/src/app/shared/import-export/import-dialog-csv.component.html diff --git a/ui-ngx/src/app/modules/home/components/import-export/import-dialog-csv.component.scss b/ui-ngx/src/app/shared/import-export/import-dialog-csv.component.scss similarity index 100% rename from ui-ngx/src/app/modules/home/components/import-export/import-dialog-csv.component.scss rename to ui-ngx/src/app/shared/import-export/import-dialog-csv.component.scss diff --git a/ui-ngx/src/app/shared/import-export/import-dialog-csv.component.ts b/ui-ngx/src/app/shared/import-export/import-dialog-csv.component.ts new file mode 100644 index 0000000000..eb29f17017 --- /dev/null +++ b/ui-ngx/src/app/shared/import-export/import-dialog-csv.component.ts @@ -0,0 +1,303 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { AfterViewInit, Component, ElementRef, Inject, OnDestroy, Renderer2, ViewChild } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; +import { DialogComponent } from '@app/shared/components/dialog.component'; +import { EntityType } from '@shared/models/entity-type.models'; +import { TranslateService } from '@ngx-translate/core'; +import { ActionNotificationShow } from '@core/notification/notification.actions'; +import { MatStepper } from '@angular/material/stepper'; +import { + BulkImportRequest, + BulkImportResult, + ColumnMapping, + convertCSVToJson, + CsvColumnParam, + CSVDelimiter, + CsvToJsonConfig, + CsvToJsonResult, + ImportEntityColumnType +} from '@shared/import-export/import-export.models'; +import { ImportExportService } from '@shared/import-export/import-export.service'; +import { TableColumnsAssignmentComponent } from '@shared/import-export/table-columns-assignment.component'; +import { Ace } from 'ace-builds'; +import { getAce } from '@shared/models/ace/ace.models'; + +export interface ImportDialogCsvData { + entityType: EntityType; + importTitle: string; + importFileLabel: string; +} + +@Component({ + selector: 'tb-import-csv-dialog', + templateUrl: './import-dialog-csv.component.html', + providers: [], + styleUrls: ['./import-dialog-csv.component.scss'] +}) +export class ImportDialogCsvComponent extends DialogComponent + implements AfterViewInit, OnDestroy { + + @ViewChild('importStepper', {static: true}) importStepper: MatStepper; + + @ViewChild('columnsAssignmentComponent', {static: true}) + columnsAssignmentComponent: TableColumnsAssignmentComponent; + + @ViewChild('failureDetailsEditor') + failureDetailsEditorElmRef: ElementRef; + + entityType: EntityType; + importTitle: string; + importFileLabel: string; + + delimiters: { key: CSVDelimiter; value: string }[] = [{ + key: ',', + value: ',' + }, { + key: ';', + value: ';' + }, { + key: '|', + value: '|' + }, { + key: '\t', + value: 'Tab' + }]; + + selectedIndex = 0; + + selectFileFormGroup: UntypedFormGroup; + importParametersFormGroup: UntypedFormGroup; + columnTypesFormGroup: UntypedFormGroup; + + isImportData = false; + statistical: BulkImportResult; + + aceEditor: Ace.Editor; + + private allowAssignColumn: ImportEntityColumnType[]; + private initEditorComponent = false; + private parseData: CsvToJsonResult; + + constructor(protected store: Store, + protected router: Router, + @Inject(MAT_DIALOG_DATA) public data: ImportDialogCsvData, + public dialogRef: MatDialogRef, + public translate: TranslateService, + private importExport: ImportExportService, + private fb: UntypedFormBuilder, + private renderer: Renderer2) { + super(store, router, dialogRef); + this.entityType = data.entityType; + this.importTitle = data.importTitle; + this.importFileLabel = data.importFileLabel; + + this.selectFileFormGroup = this.fb.group( + { + importData: [null, [Validators.required]] + } + ); + this.importParametersFormGroup = this.fb.group({ + delim: [',', [Validators.required]], + isHeader: [true, []], + isUpdate: [true, []], + }); + this.columnTypesFormGroup = this.fb.group({ + columnsParam: [[], []] + }); + } + + ngAfterViewInit() { + let columns = this.columnsAssignmentComponent.columnTypes; + if (this.entityType === EntityType.DEVICE) { + columns = columns.concat(this.columnsAssignmentComponent.columnDeviceCredentials); + } + this.allowAssignColumn = columns.map(column => column.value); + } + + ngOnDestroy(): void { + if (this.aceEditor) { + this.aceEditor.destroy(); + } + super.ngOnDestroy(); + } + + cancel(): void { + this.dialogRef.close(false); + } + + previousStep() { + this.importStepper.previous(); + } + + nextStep(step: number) { + switch (step) { + case 2: + this.importStepper.next(); + break; + case 3: + const importData: string = this.selectFileFormGroup.get('importData').value; + const parseData = this.parseCSV(importData); + if (parseData === -1) { + this.importStepper.previous(); + this.importStepper.selected.reset(); + } else { + this.parseData = parseData as CsvToJsonResult; + const columnsParam = this.createColumnsData(); + this.columnTypesFormGroup.patchValue({columnsParam}, {emitEvent: true}); + this.importStepper.next(); + } + break; + case 4: + this.importStepper.next(); + this.isImportData = true; + this.addEntities(); + break; + case 6: + this.dialogRef.close(true); + break; + } + } + + private parseCSV(importData: string): CsvToJsonResult | number { + const config: CsvToJsonConfig = { + delim: this.importParametersFormGroup.get('delim').value, + header: this.importParametersFormGroup.get('isHeader').value + }; + return convertCSVToJson(importData, config, + (messageId, params) => { + this.store.dispatch(new ActionNotificationShow( + { + message: this.translate.instant(messageId, params), + type: 'error' + })); + } + ); + } + + private createColumnsData(): CsvColumnParam[] { + const columnsParam: CsvColumnParam[] = []; + const isHeader: boolean = this.importParametersFormGroup.get('isHeader').value; + for (let i = 0; i < this.parseData.headers.length; i++) { + let columnParam: CsvColumnParam; + let findEntityColumnType: ImportEntityColumnType; + if (isHeader) { + const headerColumnName = this.parseData.headers[i].toUpperCase(); + findEntityColumnType = this.allowAssignColumn.find(column => column === headerColumnName); + } + if (isHeader && findEntityColumnType) { + columnParam = { + type: findEntityColumnType, + key: this.parseData.headers[i].toLowerCase(), + sampleData: this.parseData.rows[0][i] + }; + } else { + columnParam = { + type: ImportEntityColumnType.serverAttribute, + key: isHeader ? this.parseData.headers[i] : '', + sampleData: this.parseData.rows[0][i] + }; + } + columnsParam.push(columnParam); + } + return columnsParam; + } + + + private addEntities() { + const entitiesData: BulkImportRequest = { + file: this.selectFileFormGroup.get('importData').value, + mapping: { + columns: this.processingColumnsParams(), + delimiter: this.importParametersFormGroup.get('delim').value, + header: this.importParametersFormGroup.get('isHeader').value, + update: this.importParametersFormGroup.get('isUpdate').value + } + }; + this.importExport.bulkImportEntities(entitiesData, this.entityType, {ignoreErrors: true}).subscribe( + (result) => { + this.statistical = result; + this.isImportData = false; + this.importStepper.next(); + } + ); + } + + private processingColumnsParams(): Array { + const parameterColumns: CsvColumnParam[] = this.columnTypesFormGroup.get('columnsParam').value; + const allowKeyForTypeColumns: ImportEntityColumnType[] = [ + ImportEntityColumnType.serverAttribute, + ImportEntityColumnType.timeseries, + ImportEntityColumnType.sharedAttribute + ]; + return parameterColumns.map(column => ({ + type: column.type, + key: allowKeyForTypeColumns.some(type => type === column.type) ? column.key : undefined + })); + } + + initEditor() { + if (!this.initEditorComponent) { + this.createEditor(this.failureDetailsEditorElmRef, this.statistical.errorsList); + } + } + + private createEditor(editorElementRef: ElementRef, contents: string[]): void { + const editorElement = editorElementRef.nativeElement; + let editorOptions: Partial = { + mode: 'ace/mode/java', + theme: 'ace/theme/github', + showGutter: false, + showPrintMargin: false, + readOnly: true + }; + + const advancedOptions = { + enableSnippets: false, + enableBasicAutocompletion: false, + enableLiveAutocompletion: false + }; + + editorOptions = {...editorOptions, ...advancedOptions}; + const content = contents.map(error => error.replace('\n', '')).join('\n'); + getAce().subscribe( + (ace) => { + this.aceEditor = ace.edit(editorElement, editorOptions); + this.aceEditor.session.setUseWrapMode(false); + this.aceEditor.setValue(content, -1); + this.updateEditorSize(editorElement, content, this.aceEditor); + } + ); + } + + private updateEditorSize(editorElement: any, content: string, editor: Ace.Editor) { + let newHeight = 200; + if (content && content.length > 0) { + const lines = content.split('\n'); + newHeight = 16 * lines.length + 24; + } + const minHeight = Math.min(200, newHeight); + this.renderer.setStyle(editorElement, 'minHeight', minHeight.toString() + 'px'); + this.renderer.setStyle(editorElement, 'height', newHeight.toString() + 'px'); + editor.resize(); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/import-export/import-dialog.component.html b/ui-ngx/src/app/shared/import-export/import-dialog.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/import-export/import-dialog.component.html rename to ui-ngx/src/app/shared/import-export/import-dialog.component.html diff --git a/ui-ngx/src/app/modules/home/components/import-export/import-dialog.component.ts b/ui-ngx/src/app/shared/import-export/import-dialog.component.ts similarity index 100% rename from ui-ngx/src/app/modules/home/components/import-export/import-dialog.component.ts rename to ui-ngx/src/app/shared/import-export/import-dialog.component.ts diff --git a/ui-ngx/src/app/shared/import-export/import-export.models.ts b/ui-ngx/src/app/shared/import-export/import-export.models.ts new file mode 100644 index 0000000000..91d4cd0f05 --- /dev/null +++ b/ui-ngx/src/app/shared/import-export/import-export.models.ts @@ -0,0 +1,235 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Widget, WidgetTypeDetails } from '@app/shared/models/widget.models'; +import { DashboardLayoutId } from '@shared/models/dashboard.models'; +import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; + +export interface ImportWidgetResult { + widget: Widget; + layoutId: DashboardLayoutId; +} + +export interface WidgetsBundleItem { + widgetsBundle: WidgetsBundle; + widgetTypes?: WidgetTypeDetails[]; + widgetTypeFqns?: string[]; +} + +export interface CsvToJsonConfig { + delim?: string; + header?: boolean; +} + +export interface CsvToJsonResult { + headers?: string[]; + rows?: any[][]; +} + +export type CSVDelimiter = ',' | ';' | '|' | '\t'; + +export enum ImportEntityColumnType { + name = 'NAME', + type = 'TYPE', + label = 'LABEL', + clientAttribute = 'CLIENT_ATTRIBUTE', + sharedAttribute = 'SHARED_ATTRIBUTE', + serverAttribute = 'SERVER_ATTRIBUTE', + timeseries = 'TIMESERIES', + accessToken = 'ACCESS_TOKEN', + x509 = 'X509', + mqttClientId = 'MQTT_CLIENT_ID', + mqttUserName = 'MQTT_USER_NAME', + mqttPassword = 'MQTT_PASSWORD', + lwm2mClientEndpoint = 'LWM2M_CLIENT_ENDPOINT', + lwm2mClientSecurityConfigMode = 'LWM2M_CLIENT_SECURITY_CONFIG_MODE', + lwm2mClientIdentity = 'LWM2M_CLIENT_IDENTITY', + lwm2mClientKey = 'LWM2M_CLIENT_KEY', + lwm2mClientCert = 'LWM2M_CLIENT_CERT', + lwm2mBootstrapServerSecurityMode = 'LWM2M_BOOTSTRAP_SERVER_SECURITY_MODE', + lwm2mBootstrapServerClientPublicKeyOrId = 'LWM2M_BOOTSTRAP_SERVER_PUBLIC_KEY_OR_ID', + lwm2mBootstrapServerClientSecretKey = 'LWM2M_BOOTSTRAP_SERVER_SECRET_KEY', + lwm2mServerSecurityMode = 'LWM2M_SERVER_SECURITY_MODE', + lwm2mServerClientPublicKeyOrId = 'LWM2M_SERVER_CLIENT_PUBLIC_KEY_OR_ID', + lwm2mServerClientSecretKey = 'LWM2M_SERVER_CLIENT_SECRET_KEY', + snmpHost = 'SNMP_HOST', + snmpPort = 'SNMP_PORT', + snmpVersion = 'SNMP_VERSION', + snmpCommunityString = 'SNMP_COMMUNITY_STRING', + isGateway = 'IS_GATEWAY', + description = 'DESCRIPTION', + routingKey = 'ROUTING_KEY', + secret = 'SECRET' +} + +export const importEntityColumnTypeTranslations = new Map( + [ + [ImportEntityColumnType.name, 'import.column-type.name'], + [ImportEntityColumnType.type, 'import.column-type.type'], + [ImportEntityColumnType.label, 'import.column-type.label'], + [ImportEntityColumnType.clientAttribute, 'import.column-type.client-attribute'], + [ImportEntityColumnType.sharedAttribute, 'import.column-type.shared-attribute'], + [ImportEntityColumnType.serverAttribute, 'import.column-type.server-attribute'], + [ImportEntityColumnType.timeseries, 'import.column-type.timeseries'], + [ImportEntityColumnType.accessToken, 'import.column-type.access-token'], + [ImportEntityColumnType.x509, 'import.column-type.x509'], + [ImportEntityColumnType.mqttClientId, 'import.column-type.mqtt.client-id'], + [ImportEntityColumnType.mqttUserName, 'import.column-type.mqtt.user-name'], + [ImportEntityColumnType.mqttPassword, 'import.column-type.mqtt.password'], + [ImportEntityColumnType.lwm2mClientEndpoint, 'import.column-type.lwm2m.client-endpoint'], + [ImportEntityColumnType.lwm2mClientSecurityConfigMode, 'import.column-type.lwm2m.security-config-mode'], + [ImportEntityColumnType.lwm2mClientIdentity, 'import.column-type.lwm2m.client-identity'], + [ImportEntityColumnType.lwm2mClientKey, 'import.column-type.lwm2m.client-key'], + [ImportEntityColumnType.lwm2mClientCert, 'import.column-type.lwm2m.client-cert'], + [ImportEntityColumnType.lwm2mBootstrapServerSecurityMode, 'import.column-type.lwm2m.bootstrap-server-security-mode'], + [ImportEntityColumnType.lwm2mBootstrapServerClientPublicKeyOrId, 'import.column-type.lwm2m.bootstrap-server-public-key-id'], + [ImportEntityColumnType.lwm2mBootstrapServerClientSecretKey, 'import.column-type.lwm2m.bootstrap-server-secret-key'], + [ImportEntityColumnType.lwm2mServerSecurityMode, 'import.column-type.lwm2m.lwm2m-server-security-mode'], + [ImportEntityColumnType.lwm2mServerClientPublicKeyOrId, 'import.column-type.lwm2m.lwm2m-server-public-key-id'], + [ImportEntityColumnType.lwm2mServerClientSecretKey, 'import.column-type.lwm2m.lwm2m-server-secret-key'], + [ImportEntityColumnType.snmpHost, 'import.column-type.snmp.host'], + [ImportEntityColumnType.snmpPort, 'import.column-type.snmp.port'], + [ImportEntityColumnType.snmpVersion, 'import.column-type.snmp.version'], + [ImportEntityColumnType.snmpCommunityString, 'import.column-type.snmp.community-string'], + [ImportEntityColumnType.isGateway, 'import.column-type.isgateway'], + [ImportEntityColumnType.description, 'import.column-type.description'], + [ImportEntityColumnType.routingKey, 'import.column-type.routing-key'], + [ImportEntityColumnType.secret, 'import.column-type.secret'] + ] +); + +export interface CsvColumnParam { + type: ImportEntityColumnType; + key: string; + sampleData: any; +} + +export interface ColumnMapping { + type: ImportEntityColumnType; + key?: string; +} + +export interface BulkImportRequest { + file: string; + mapping: { + columns: Array; + delimiter: CSVDelimiter; + header: boolean; + update: boolean; + }; +} + +export interface BulkImportResult { + created: number; + updated: number; + errors: number; + errorsList: Array; +} + +export interface FileType { + mimeType: string; + extension: string; +} + +export const TEXT_TYPE: FileType = { + mimeType: 'text/plain', + extension: 'txt' +}; + +export const JSON_TYPE: FileType = { + mimeType: 'text/json', + extension: 'json' +}; + +export const ZIP_TYPE: FileType = { + mimeType: 'application/zip', + extension: 'zip' +}; + +export const CSV_TYPE: FileType = { + mimeType: 'attachament/csv', + extension: 'csv' +}; + +export function convertCSVToJson(csvdata: string, config: CsvToJsonConfig, + onError: (messageId: string, params?: any) => void): CsvToJsonResult | number { + config = config || {}; + const delim = config.delim || ','; + const header = config.header || false; + const result: CsvToJsonResult = {}; + const csvlines = csvdata.split(/[\r\n]+/); + const csvheaders = splitCSV(csvlines[0], delim); + if (csvheaders.length < 2) { + onError('import.import-csv-number-columns-error'); + return -1; + } + const csvrows = header ? csvlines.slice(1, csvlines.length) : csvlines; + result.headers = csvheaders; + result.rows = []; + for (const row of csvrows) { + if (row.length === 0) { + break; + } + const rowitems: any[] = splitCSV(row, delim); + if (rowitems.length !== result.headers.length) { + onError('import.import-csv-invalid-format-error', {line: (header ? result.rows.length + 2 : result.rows.length + 1)}); + return -1; + } + for (let i = 0; i < rowitems.length; i++) { + rowitems[i] = convertStringToJSType(rowitems[i]); + } + result.rows.push(rowitems); + } + return result; +} + +function splitCSV(str: string, sep: string): string[] { + let foo: string[]; + let x: number; + let tl: string; + for (foo = str.split(sep = sep || ','), x = foo.length - 1, tl; x >= 0; x--) { + if (foo[x].replace(/"\s+$/, '"').charAt(foo[x].length - 1) === '"') { + if ((tl = foo[x].replace(/^\s+"/, '"')).length > 1 && tl.charAt(0) === '"') { + foo[x] = foo[x].replace(/^\s*"|"\s*$/g, '').replace(/""/g, '"'); + } else if (x) { + foo.splice(x - 1, 2, [foo[x - 1], foo[x]].join(sep)); + } else { + foo = foo.shift().split(sep).concat(foo); + } + } else { + foo[x].replace(/""/g, '"'); + } + } + return foo; +} + +function isNumeric(str: any): boolean { + str = str.replace(',', '.'); + return (str - parseFloat(str) + 1) >= 0 && Number(str).toString() === str; +} + +function convertStringToJSType(str: string): any { + if (isNumeric(str.replace(',', '.'))) { + return parseFloat(str.replace(',', '.')); + } + if (str.search(/^(true|false)$/im) === 0) { + return str.toLowerCase() === 'true'; + } + if (str === '') { + return null; + } + return str; +} diff --git a/ui-ngx/src/app/shared/import-export/import-export.service.ts b/ui-ngx/src/app/shared/import-export/import-export.service.ts new file mode 100644 index 0000000000..e7b5522d56 --- /dev/null +++ b/ui-ngx/src/app/shared/import-export/import-export.service.ts @@ -0,0 +1,1130 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Inject, Injectable } from '@angular/core'; +import { DashboardService } from '@core/http/dashboard.service'; +import { TranslateService } from '@ngx-translate/core'; +import { select, Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { ActionNotificationShow } from '@core/notification/notification.actions'; +import { Dashboard, DashboardLayoutId } from '@shared/models/dashboard.models'; +import { deepClone, guid, isDefined, isNotEmptyStr, isObject, isString, isUndefined } from '@core/utils'; +import { WINDOW } from '@core/services/window.service'; +import { DOCUMENT } from '@angular/common'; +import { + AliasesInfo, + AliasFilterType, + EntityAlias, + EntityAliases, + EntityAliasFilter, + EntityAliasInfo +} from '@shared/models/alias.models'; +import { MatDialog } from '@angular/material/dialog'; +import { ImportDialogComponent, ImportDialogData } from '@shared/import-export/import-dialog.component'; +import { forkJoin, Observable, of, Subject } from 'rxjs'; +import { catchError, map, mergeMap, switchMap, take, tap } from 'rxjs/operators'; +import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; +import { EntityService } from '@core/http/entity.service'; +import { Widget, WidgetSize, WidgetTypeDetails } from '@shared/models/widget.models'; +import { ItemBufferService, WidgetItem } from '@core/services/item-buffer.service'; +import { + BulkImportRequest, + BulkImportResult, + CSV_TYPE, + FileType, + ImportWidgetResult, + JSON_TYPE, + TEXT_TYPE, + WidgetsBundleItem, + ZIP_TYPE +} from './import-export.models'; +import { EntityType } from '@shared/models/entity-type.models'; +import { UtilsService } from '@core/services/utils.service'; +import { WidgetService } from '@core/http/widget.service'; +import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; +import { ImportEntitiesResultInfo, ImportEntityData } from '@shared/models/entity.models'; +import { RequestConfig } from '@core/http/http-utils'; +import { RuleChain, RuleChainImport, RuleChainMetaData, RuleChainType } from '@shared/models/rule-chain.models'; +import { RuleChainService } from '@core/http/rule-chain.service'; +import { FiltersInfo } from '@shared/models/query/query.models'; +import { DeviceProfileService } from '@core/http/device-profile.service'; +import { DeviceProfile } from '@shared/models/device.models'; +import { TenantProfile } from '@shared/models/tenant.model'; +import { TenantProfileService } from '@core/http/tenant-profile.service'; +import { DeviceService } from '@core/http/device.service'; +import { AssetService } from '@core/http/asset.service'; +import { EdgeService } from '@core/http/edge.service'; +import { RuleNode } from '@shared/models/rule-node.models'; +import { AssetProfileService } from '@core/http/asset-profile.service'; +import { AssetProfile } from '@shared/models/asset.models'; +import { + ExportWidgetsBundleDialogComponent, + ExportWidgetsBundleDialogData, + ExportWidgetsBundleDialogResult +} from '@shared/import-export/export-widgets-bundle-dialog.component'; +import { ImageService } from '@core/http/image.service'; +import { ImageExportData, ImageResourceInfo, ImageResourceType } from '@shared/models/resource.models'; +import { selectUserSettingsProperty } from '@core/auth/auth.selectors'; +import { ActionPreferencesPutUserSettings } from '@core/auth/auth.actions'; +import { ExportableEntity } from '@shared/models/base-data'; +import { EntityId } from '@shared/models/id/entity-id'; + +export type editMissingAliasesFunction = (widgets: Array, isSingleWidget: boolean, + customTitle: string, missingEntityAliases: EntityAliases) => Observable; + +// @dynamic +@Injectable() +export class ImportExportService { + + constructor(@Inject(WINDOW) private window: Window, + @Inject(DOCUMENT) private document: Document, + private store: Store, + private translate: TranslateService, + private dashboardService: DashboardService, + private dashboardUtils: DashboardUtilsService, + private widgetService: WidgetService, + private deviceProfileService: DeviceProfileService, + private assetProfileService: AssetProfileService, + private tenantProfileService: TenantProfileService, + private entityService: EntityService, + private ruleChainService: RuleChainService, + private deviceService: DeviceService, + private assetService: AssetService, + private edgeService: EdgeService, + private imageService: ImageService, + private utils: UtilsService, + private itembuffer: ItemBufferService, + private dialog: MatDialog) { + + } + + public exportImage(type: ImageResourceType, key: string) { + this.imageService.exportImage(type, key).subscribe( + { + next: (imageData) => { + const name = imageData.fileName.split('.')[0]; + this.exportToPc(imageData, name); + }, + error: (e) => { + this.handleExportError(e, 'image.export-failed-error'); + } + } + ); + } + + public importImage(): Observable { + return this.openImportDialog('image.import-image', 'image.image-json-file').pipe( + mergeMap((imageData: ImageExportData) => { + if (!this.validateImportedImage(imageData)) { + this.store.dispatch(new ActionNotificationShow( + {message: this.translate.instant('image.invalid-image-json-file-error'), + type: 'error'})); + throw new Error('Invalid image JSON file'); + } else { + return this.imageService.importImage(imageData); + } + }), + catchError(() => of(null)) + ); + } + + public exportDashboard(dashboardId: string) { + this.dashboardService.exportDashboard(dashboardId).subscribe({ + next: (dashboard) => { + let name = dashboard.title; + name = name.toLowerCase().replace(/\W/g, '_'); + this.exportToPc(this.prepareDashboardExport(dashboard), name); + }, + error: (e) => { + this.handleExportError(e, 'dashboard.export-failed-error'); + } + }); + } + + public importDashboard(onEditMissingAliases: editMissingAliasesFunction): Observable { + return this.openImportDialog('dashboard.import', 'dashboard.dashboard-file').pipe( + mergeMap((dashboard: Dashboard) => { + if (!this.validateImportedDashboard(dashboard)) { + this.store.dispatch(new ActionNotificationShow( + {message: this.translate.instant('dashboard.invalid-dashboard-file-error'), + type: 'error'})); + throw new Error('Invalid dashboard file'); + } else { + dashboard = this.prepareImport(dashboard); + dashboard = this.dashboardUtils.validateAndUpdateDashboard(dashboard); + let aliasIds = null; + const entityAliases = dashboard.configuration.entityAliases; + if (entityAliases) { + aliasIds = Object.keys(entityAliases); + } + if (aliasIds && aliasIds.length > 0) { + return this.processEntityAliases(entityAliases, aliasIds).pipe( + mergeMap((missingEntityAliases) => { + if (Object.keys(missingEntityAliases).length > 0) { + return onEditMissingAliases(this.dashboardUtils.getWidgetsArray(dashboard), + false, 'dashboard.dashboard-import-missing-aliases-title', + missingEntityAliases).pipe( + mergeMap((updatedEntityAliases) => { + for (const aliasId of Object.keys(updatedEntityAliases)) { + entityAliases[aliasId] = updatedEntityAliases[aliasId]; + } + return this.saveImportedDashboard(dashboard); + }) + ); + } else { + return this.saveImportedDashboard(dashboard); + } + } + )); + } else { + return this.saveImportedDashboard(dashboard); + } + } + }), + catchError(() => of(null)) + ); + } + + public exportWidget(dashboard: Dashboard, sourceState: string, sourceLayout: DashboardLayoutId, widget: Widget) { + const widgetItem = this.itembuffer.prepareWidgetItem(dashboard, sourceState, sourceLayout, widget); + let name = widgetItem.widget.config.title; + name = name.toLowerCase().replace(/\W/g, '_'); + this.exportToPc(this.prepareExport(widgetItem), name); + } + + public importWidget(dashboard: Dashboard, targetState: string, + onEditMissingAliases: editMissingAliasesFunction, + targetLayoutFunction: () => Observable, + onAliasesUpdateFunction: () => void, + onFiltersUpdateFunction: () => void): Observable { + return this.openImportDialog('dashboard.import-widget', 'dashboard.widget-file').pipe( + mergeMap((widgetItem: WidgetItem) => { + if (!this.validateImportedWidget(widgetItem)) { + this.store.dispatch(new ActionNotificationShow( + {message: this.translate.instant('dashboard.invalid-widget-file-error'), + type: 'error'})); + throw new Error('Invalid widget file'); + } else { + let widget = this.prepareImport(widgetItem.widget); + widget = this.dashboardUtils.validateAndUpdateWidget(widget); + widget.id = guid(); + const aliasesInfo = this.prepareAliasesInfo(widgetItem.aliasesInfo); + const filtersInfo: FiltersInfo = widgetItem.filtersInfo || { + datasourceFilters: {} + }; + const originalColumns = widgetItem.originalColumns; + const originalSize = widgetItem.originalSize; + + const datasourceAliases = aliasesInfo.datasourceAliases; + const targetDeviceAliases = aliasesInfo.targetDeviceAliases; + if (datasourceAliases || targetDeviceAliases) { + const entityAliases: EntityAliases = {}; + const datasourceAliasesMap: {[aliasId: string]: number} = {}; + const targetDeviceAliasesMap: {[aliasId: string]: number} = {}; + let aliasId: string; + let datasourceIndex: number; + if (datasourceAliases) { + for (const strIndex of Object.keys(datasourceAliases)) { + datasourceIndex = Number(strIndex); + aliasId = this.utils.guid(); + datasourceAliasesMap[aliasId] = datasourceIndex; + entityAliases[aliasId] = {id: aliasId, ...datasourceAliases[datasourceIndex]}; + } + } + if (targetDeviceAliases) { + for (const strIndex of Object.keys(targetDeviceAliases)) { + datasourceIndex = Number(strIndex); + aliasId = this.utils.guid(); + targetDeviceAliasesMap[aliasId] = datasourceIndex; + entityAliases[aliasId] = {id: aliasId, ...targetDeviceAliases[datasourceIndex]}; + } + } + const aliasIds = Object.keys(entityAliases); + if (aliasIds.length > 0) { + return this.processEntityAliases(entityAliases, aliasIds).pipe( + mergeMap((missingEntityAliases) => { + if (Object.keys(missingEntityAliases).length > 0) { + onEditMissingAliases([widget], + false, 'dashboard.widget-import-missing-aliases-title', + missingEntityAliases).pipe( + mergeMap((updatedEntityAliases) => { + for (const id of Object.keys(updatedEntityAliases)) { + const entityAlias = updatedEntityAliases[id]; + let index: number; + if (isDefined(datasourceAliasesMap[id])) { + index = datasourceAliasesMap[id]; + datasourceAliases[index] = entityAlias; + } else if (isDefined(targetDeviceAliasesMap[id])) { + index = targetDeviceAliasesMap[id]; + targetDeviceAliases[index] = entityAlias; + } + } + return this.addImportedWidget(dashboard, targetState, targetLayoutFunction, widget, + aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize); + } + )); + } else { + return this.addImportedWidget(dashboard, targetState, targetLayoutFunction, widget, + aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize); + } + } + ) + ); + } else { + return this.addImportedWidget(dashboard, targetState, targetLayoutFunction, widget, + aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize); + } + } else { + return this.addImportedWidget(dashboard, targetState, targetLayoutFunction, widget, + aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, originalColumns, originalSize); + } + } + }), + catchError(() => of(null)) + ); + } + + public exportWidgetType(widgetTypeId: string) { + this.widgetService.exportWidgetType(widgetTypeId).subscribe({ + next: (widgetTypeDetails) => { + let name = widgetTypeDetails.name; + name = name.toLowerCase().replace(/\W/g, '_'); + this.exportToPc(this.prepareExport(widgetTypeDetails), name); + }, + error: (e) => { + this.handleExportError(e, 'widget-type.export-failed-error'); + } + }); + } + + public exportWidgetTypes(widgetTypeIds: string[]): Observable { + const widgetTypesObservables: Array> = []; + for (const id of widgetTypeIds) { + widgetTypesObservables.push(this.widgetService.exportWidgetType(id)); + } + return forkJoin(widgetTypesObservables).pipe( + map((widgetTypes) => + Object.fromEntries(widgetTypes.map(wt=> { + let name = wt.name; + name = name.toLowerCase().replace(/\W/g, '_') + `.${JSON_TYPE.extension}`; + const data = JSON.stringify(this.prepareExport(wt), null, 2); + return [name, data]; + }))), + mergeMap(widgetTypeFiles => this.exportJSZip(widgetTypeFiles, 'widget_types')), + catchError(e => { + this.handleExportError(e, 'widget-type.export-failed-error'); + throw e; + }) + ); + } + + public importWidgetType(): Observable { + return this.openImportDialog('widget.import', 'widget-type.widget-file').pipe( + mergeMap((widgetTypeDetails: WidgetTypeDetails) => { + if (!this.validateImportedWidgetTypeDetails(widgetTypeDetails)) { + this.store.dispatch(new ActionNotificationShow( + {message: this.translate.instant('widget-type.invalid-widget-file-error'), + type: 'error'})); + throw new Error('Invalid widget file'); + } else { + return this.widgetService.saveImportedWidgetTypeDetails(this.prepareImport(widgetTypeDetails)); + } + }), + catchError(() => of(null)) + ); + } + + public exportWidgetsBundle(widgetsBundleId: string) { + const tasks = { + includeBundleWidgetsInExport: this.store.pipe(select(selectUserSettingsProperty( 'includeBundleWidgetsInExport'))).pipe(take(1)), + widgetsBundle: this.widgetService.exportWidgetsBundle(widgetsBundleId) + }; + + forkJoin(tasks).subscribe({ + next: ({includeBundleWidgetsInExport, widgetsBundle}) => { + this.dialog.open(ExportWidgetsBundleDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + widgetsBundle, + includeBundleWidgetsInExport + } + }).afterClosed().subscribe( + (result) => { + if (result) { + if (includeBundleWidgetsInExport !== result.exportWidgets) { + this.store.dispatch(new ActionPreferencesPutUserSettings({includeBundleWidgetsInExport: result.exportWidgets})); + } + if (result.exportWidgets) { + this.exportWidgetsBundleWithWidgetTypes(widgetsBundle); + } else { + this.exportWidgetsBundleWithWidgetTypeFqns(widgetsBundle); + } + } + } + ); + }, + error: (e) => { + this.handleExportError(e, 'widgets-bundle.export-failed-error'); + } + }); + } + + private exportWidgetsBundleWithWidgetTypes(widgetsBundle: WidgetsBundle) { + this.widgetService.exportBundleWidgetTypesDetails(widgetsBundle.id.id).subscribe({ + next: (widgetTypesDetails) => { + const widgetsBundleItem: WidgetsBundleItem = { + widgetsBundle: this.prepareExport(widgetsBundle), + widgetTypes: [] + }; + for (const widgetTypeDetails of widgetTypesDetails) { + widgetsBundleItem.widgetTypes.push(this.prepareExport(widgetTypeDetails)); + } + let name = widgetsBundle.title; + name = name.toLowerCase().replace(/\W/g, '_'); + this.exportToPc(widgetsBundleItem, name); + }, + error: (e) => { + this.handleExportError(e, 'widgets-bundle.export-failed-error'); + } + }); + } + + private exportWidgetsBundleWithWidgetTypeFqns(widgetsBundle: WidgetsBundle) { + this.widgetService.getBundleWidgetTypeFqns(widgetsBundle.id.id).subscribe({ + next: (widgetTypeFqns) => { + const widgetsBundleItem: WidgetsBundleItem = { + widgetsBundle: this.prepareExport(widgetsBundle), + widgetTypeFqns + }; + let name = widgetsBundle.title; + name = name.toLowerCase().replace(/\W/g, '_'); + this.exportToPc(widgetsBundleItem, name); + }, + error: (e) => { + this.handleExportError(e, 'widgets-bundle.export-failed-error'); + } + }); + } + + public importWidgetsBundle(): Observable { + return this.openImportDialog('widgets-bundle.import', 'widgets-bundle.widgets-bundle-file').pipe( + mergeMap((widgetsBundleItem: WidgetsBundleItem) => { + if (!this.validateImportedWidgetsBundle(widgetsBundleItem)) { + this.store.dispatch(new ActionNotificationShow( + {message: this.translate.instant('widgets-bundle.invalid-widgets-bundle-file-error'), + type: 'error'})); + throw new Error('Invalid widgets bundle file'); + } else { + const widgetsBundle = this.prepareImport(widgetsBundleItem.widgetsBundle); + return this.widgetService.saveWidgetsBundle(widgetsBundle).pipe( + mergeMap((savedWidgetsBundle) => { + if (widgetsBundleItem.widgetTypes?.length || widgetsBundleItem.widgetTypeFqns?.length) { + let widgetTypesObservable: Observable>; + if (widgetsBundleItem.widgetTypes?.length) { + const widgetTypesDetails = widgetsBundleItem.widgetTypes; + const saveWidgetTypesObservables: Array> = []; + for (const widgetTypeDetails of widgetTypesDetails) { + saveWidgetTypesObservables.push( + this.widgetService.saveImportedWidgetTypeDetails(this.prepareWidgetType(widgetTypeDetails, savedWidgetsBundle)) + ); + } + widgetTypesObservable = forkJoin(saveWidgetTypesObservables); + } else { + widgetTypesObservable = of([]); + } + return widgetTypesObservable.pipe( + switchMap((widgetTypes) => { + let widgetTypeFqns = widgetTypes.map(w => w.fqn); + if (widgetsBundleItem.widgetTypeFqns?.length) { + widgetTypeFqns = widgetTypeFqns.concat(widgetsBundleItem.widgetTypeFqns); + } + if (widgetTypeFqns.length) { + return this.widgetService.updateWidgetsBundleWidgetFqns(savedWidgetsBundle.id.id, widgetTypeFqns).pipe( + map(() => savedWidgetsBundle) + ); + } else { + return of(savedWidgetsBundle); + } + }) + ); + } else { + return of(savedWidgetsBundle); + } + } + )); + } + }), + catchError(() => of(null)) + ); + } + + private prepareWidgetType(widgetType: WidgetTypeDetails & {alias?: string}, widgetsBundle: WidgetsBundle): WidgetTypeDetails { + if (!widgetType.fqn) { + widgetType.fqn = `${widgetsBundle.alias}.${widgetType.alias + ? widgetType.alias + : widgetType.name.toLowerCase().replace(/\W/g, '_')}`; + } + return widgetType; + } + + public bulkImportEntities(entitiesData: BulkImportRequest, entityType: EntityType, config?: RequestConfig): Observable { + switch (entityType) { + case EntityType.DEVICE: + return this.deviceService.bulkImportDevices(entitiesData, config); + case EntityType.ASSET: + return this.assetService.bulkImportAssets(entitiesData, config); + case EntityType.EDGE: + return this.edgeService.bulkImportEdges(entitiesData, config); + } + } + + public importEntities(entitiesData: ImportEntityData[], entityType: EntityType, updateData: boolean, + importEntityCompleted?: () => void, config?: RequestConfig): Observable { + let partSize = 100; + partSize = entitiesData.length > partSize ? partSize : entitiesData.length; + + let statisticalInfo: ImportEntitiesResultInfo = {}; + const importEntitiesObservables: Observable[] = []; + for (let i = 0; i < partSize; i++) { + const saveEntityPromise = this.entityService.saveEntityParameters(entityType, entitiesData[i], updateData, config); + const importEntityPromise = saveEntityPromise.pipe( + tap(() => { + if (importEntityCompleted) { + importEntityCompleted(); + } + }) + ); + importEntitiesObservables.push(importEntityPromise); + } + return forkJoin(importEntitiesObservables).pipe( + mergeMap((responses) => { + for (const response of responses) { + statisticalInfo = this.sumObject(statisticalInfo, response); + } + entitiesData.splice(0, partSize); + if (entitiesData.length) { + return this.importEntities(entitiesData, entityType, updateData, importEntityCompleted, config).pipe( + map((response) => this.sumObject(statisticalInfo, response)) + ); + } else { + return of(statisticalInfo); + } + }) + ); + } + + public exportRuleChain(ruleChainId: string) { + this.ruleChainService.getRuleChain(ruleChainId).pipe( + mergeMap(ruleChain => this.ruleChainService.getRuleChainMetadata(ruleChainId).pipe( + map((ruleChainMetaData) => { + const ruleChainExport: RuleChainImport = { + ruleChain: this.prepareRuleChain(ruleChain), + metadata: this.prepareRuleChainMetaData(ruleChainMetaData) + }; + return ruleChainExport; + }) + )) + ).subscribe({ + next: (ruleChainExport) => { + let name = ruleChainExport.ruleChain.name; + name = name.toLowerCase().replace(/\W/g, '_'); + this.exportToPc(ruleChainExport, name); + }, + error: (e) => { + this.handleExportError(e, 'rulechain.export-failed-error'); + } + }); + } + + public importRuleChain(expectedRuleChainType: RuleChainType): Observable { + return this.openImportDialog('rulechain.import', 'rulechain.rulechain-file').pipe( + mergeMap((ruleChainImport: RuleChainImport) => { + if (!this.validateImportedRuleChain(ruleChainImport)) { + this.store.dispatch(new ActionNotificationShow( + {message: this.translate.instant('rulechain.invalid-rulechain-file-error'), + type: 'error'})); + throw new Error('Invalid rule chain file'); + } else if (ruleChainImport.ruleChain.type !== expectedRuleChainType) { + this.store.dispatch(new ActionNotificationShow( + {message: this.translate.instant('rulechain.invalid-rulechain-type-error', {expectedRuleChainType}), + type: 'error'})); + throw new Error('Invalid rule chain type'); + } else { + return this.processOldRuleChainConnections(ruleChainImport); + } + }), + catchError(() => of(null)) + ); + } + + private processOldRuleChainConnections(ruleChainImport: RuleChainImport): Observable { + ruleChainImport.ruleChain = this.prepareImport(ruleChainImport.ruleChain); + const metadata = ruleChainImport.metadata; + if ((metadata as any).ruleChainConnections) { + const ruleChainNameResolveObservables: Observable[] = []; + for (const ruleChainConnection of (metadata as any).ruleChainConnections) { + if (ruleChainConnection.targetRuleChainId && ruleChainConnection.targetRuleChainId.id) { + const ruleChainNode: RuleNode = { + name: '', + debugMode: false, + singletonMode: false, + type: 'org.thingsboard.rule.engine.flow.TbRuleChainInputNode', + configuration: { + ruleChainId: ruleChainConnection.targetRuleChainId.id + }, + additionalInfo: ruleChainConnection.additionalInfo + }; + ruleChainNameResolveObservables.push(this.ruleChainService.getRuleChain(ruleChainNode.configuration.ruleChainId, + {ignoreErrors: true, ignoreLoading: true}).pipe( + catchError(() => of({name: 'Rule Chain Input'} as RuleChain)), + map((ruleChain => { + ruleChainNode.name = ruleChain.name; + return null; + }) + ) + )); + const toIndex = metadata.nodes.length; + metadata.nodes.push(ruleChainNode); + metadata.connections.push({ + toIndex, + fromIndex: ruleChainConnection.fromIndex, + type: ruleChainConnection.type + }); + } + } + if (ruleChainNameResolveObservables.length) { + return forkJoin(ruleChainNameResolveObservables).pipe( + map(() => ruleChainImport) + ); + } else { + return of(ruleChainImport); + } + } else { + return of(ruleChainImport); + } + } + + public exportDeviceProfile(deviceProfileId: string) { + this.deviceProfileService.exportDeviceProfile(deviceProfileId).subscribe({ + next: (deviceProfile) => { + let name = deviceProfile.name; + name = name.toLowerCase().replace(/\W/g, '_'); + this.exportToPc(this.prepareProfileExport(deviceProfile), name); + }, + error: (e) => { + this.handleExportError(e, 'device-profile.export-failed-error'); + } + }); + } + + public importDeviceProfile(): Observable { + return this.openImportDialog('device-profile.import', 'device-profile.device-profile-file').pipe( + mergeMap((deviceProfile: DeviceProfile) => { + if (!this.validateImportedDeviceProfile(deviceProfile)) { + this.store.dispatch(new ActionNotificationShow( + {message: this.translate.instant('device-profile.invalid-device-profile-file-error'), + type: 'error'})); + throw new Error('Invalid device profile file'); + } else { + return this.deviceProfileService.saveDeviceProfile(this.prepareImport(deviceProfile)); + } + }), + catchError(() => of(null)) + ); + } + + public exportAssetProfile(assetProfileId: string) { + this.assetProfileService.exportAssetProfile(assetProfileId).subscribe({ + next: (assetProfile) => { + let name = assetProfile.name; + name = name.toLowerCase().replace(/\W/g, '_'); + this.exportToPc(this.prepareProfileExport(assetProfile), name); + }, + error: (e) => { + this.handleExportError(e, 'asset-profile.export-failed-error'); + } + }); + } + + public importAssetProfile(): Observable { + return this.openImportDialog('asset-profile.import', 'asset-profile.asset-profile-file').pipe( + mergeMap((assetProfile: AssetProfile) => { + if (!this.validateImportedAssetProfile(assetProfile)) { + this.store.dispatch(new ActionNotificationShow( + {message: this.translate.instant('asset-profile.invalid-asset-profile-file-error'), + type: 'error'})); + throw new Error('Invalid asset profile file'); + } else { + return this.assetProfileService.saveAssetProfile(this.prepareImport(assetProfile)); + } + }), + catchError(() => of(null)) + ); + } + + public exportTenantProfile(tenantProfileId: string) { + this.tenantProfileService.getTenantProfile(tenantProfileId).subscribe({ + next: (tenantProfile) => { + let name = tenantProfile.name; + name = name.toLowerCase().replace(/\W/g, '_'); + this.exportToPc(this.prepareProfileExport(tenantProfile), name); + }, + error: (e) => { + this.handleExportError(e, 'tenant-profile.export-failed-error'); + } + }); + } + + public importTenantProfile(): Observable { + return this.openImportDialog('tenant-profile.import', 'tenant-profile.tenant-profile-file').pipe( + mergeMap((tenantProfile: TenantProfile) => { + if (!this.validateImportedTenantProfile(tenantProfile)) { + this.store.dispatch(new ActionNotificationShow( + {message: this.translate.instant('tenant-profile.invalid-tenant-profile-file-error'), + type: 'error'})); + throw new Error('Invalid tenant profile file'); + } else { + return this.tenantProfileService.saveTenantProfile(this.prepareImport(tenantProfile)); + } + }), + catchError(() => of(null)) + ); + } + + private processCSVCell(cellData: any): any { + if (isString(cellData)) { + let result = cellData.replace(/"/g, '""'); + if (result.search(/([",\n])/g) >= 0) { + result = `"${result}"`; + } + return result; + } + return cellData; + } + + public exportCsv(data: {[key: string]: any}[], filename: string) { + let colsHead: string; + let colsData: string; + if (data && data.length) { + colsHead = Object.keys(data[0]).map(key => [this.processCSVCell(key)]).join(';'); + colsData = data.map(obj => [ + Object.keys(obj).map(col => [ + this.processCSVCell(obj[col]) + ]).join(';') + ]).join('\n'); + } else { + colsHead = ''; + colsData = ''; + } + const csvData = `${colsHead}\n${colsData}`; + this.downloadFile(csvData, filename, CSV_TYPE); + } + + public exportText(data: string | Array, filename: string) { + let content = data; + if (Array.isArray(data)) { + content = data.join('\n'); + } + this.downloadFile(content, filename, TEXT_TYPE); + } + + public exportJSZip(data: object, filename: string): Observable { + const exportJsSubjectSubject = new Subject(); + import('jszip').then((JSZip) => { + try { + const jsZip = new JSZip.default(); + for (const keyName in data) { + if (data.hasOwnProperty(keyName)) { + const valueData = data[keyName]; + jsZip.file(keyName, valueData); + } + } + jsZip.generateAsync({type: 'blob'}).then(content => { + this.downloadFile(content, filename, ZIP_TYPE); + exportJsSubjectSubject.next(null); + }).catch(e => { + exportJsSubjectSubject.error(e); + }); + } catch (e) { + exportJsSubjectSubject.error(e); + } + }); + return exportJsSubjectSubject.asObservable(); + } + + private prepareRuleChain(ruleChain: RuleChain): RuleChain { + ruleChain = this.prepareExport(ruleChain); + if (ruleChain.firstRuleNodeId) { + ruleChain.firstRuleNodeId = null; + } + ruleChain.root = false; + return ruleChain; + } + + private prepareRuleChainMetaData(ruleChainMetaData: RuleChainMetaData) { + delete ruleChainMetaData.ruleChainId; + for (let i = 0; i < ruleChainMetaData.nodes.length; i++) { + const node = ruleChainMetaData.nodes[i]; + delete node.ruleChainId; + ruleChainMetaData.nodes[i] = this.prepareExport(node); + } + return ruleChainMetaData; + } + + private validateImportedRuleChain(ruleChainImport: RuleChainImport): boolean { + if (isUndefined(ruleChainImport.ruleChain) + || isUndefined(ruleChainImport.metadata) + || isUndefined(ruleChainImport.ruleChain.name)) { + return false; + } + if (isUndefined(ruleChainImport.ruleChain.type)) { + ruleChainImport.ruleChain.type = RuleChainType.CORE; + } + return true; + } + + private validateImportedDeviceProfile(deviceProfile: DeviceProfile): boolean { + if (isUndefined(deviceProfile.name) + || isUndefined(deviceProfile.type) + || isUndefined(deviceProfile.transportType) + || isUndefined(deviceProfile.provisionType) + || isUndefined(deviceProfile.profileData)) { + return false; + } + return true; + } + + private validateImportedAssetProfile(assetProfile: AssetProfile): boolean { + if (isUndefined(assetProfile.name)) { + return false; + } + return true; + } + + private validateImportedTenantProfile(tenantProfile: TenantProfile): boolean { + return isDefined(tenantProfile.name) + && isDefined(tenantProfile.profileData) + && isDefined(tenantProfile.isolatedTbRuleEngine); + } + + private sumObject(obj1: T, obj2: T): T { + Object.keys(obj2).map((key) => { + if (isObject(obj2[key])) { + obj1[key] = obj1[key] || {}; + obj1[key] = {...obj1[key], ...this.sumObject(obj1[key], obj2[key])}; + } else if (isString(obj2[key])) { + obj1[key] = (obj1[key] || '') + `${obj2[key]}\n`; + } else { + obj1[key] = (obj1[key] || 0) + obj2[key]; + } + }); + return obj1; + } + + private handleExportError(e: any, errorDetailsMessageId: string) { + let message = e; + if (!message) { + message = this.translate.instant('error.unknown-error'); + } + this.store.dispatch(new ActionNotificationShow( + {message: this.translate.instant(errorDetailsMessageId, {error: message}), + type: 'error'})); + } + + private validateImportedImage(image: ImageExportData): boolean { + return !(!isNotEmptyStr(image.data) + || !isNotEmptyStr(image.title) + || !isNotEmptyStr(image.fileName) + || !isNotEmptyStr(image.mediaType) + || !isNotEmptyStr(image.resourceKey)); + } + + private validateImportedDashboard(dashboard: Dashboard): boolean { + if (isUndefined(dashboard.title) || isUndefined(dashboard.configuration)) { + return false; + } + return true; + } + + private validateImportedWidget(widgetItem: WidgetItem): boolean { + if (isUndefined(widgetItem.widget) + || isUndefined(widgetItem.aliasesInfo) + || isUndefined(widgetItem.originalColumns)) { + return false; + } + const widget = widgetItem.widget; + if ((isUndefined(widget.typeFullFqn) && (isUndefined((widget as any).isSystemType) || + isUndefined((widget as any).bundleAlias) || + isUndefined((widget as any).typeAlias))) || + isUndefined(widget.type)) { + return false; + } + return true; + } + + private validateImportedWidgetTypeDetails(widgetTypeDetails: WidgetTypeDetails): boolean { + if (isUndefined(widgetTypeDetails.name) + || isUndefined(widgetTypeDetails.descriptor)) { + return false; + } + return true; + } + + private validateImportedWidgetsBundle(widgetsBundleItem: WidgetsBundleItem): boolean { + if (isUndefined(widgetsBundleItem.widgetsBundle)) { + return false; + } + if (isUndefined(widgetsBundleItem.widgetTypes) && isUndefined(widgetsBundleItem.widgetTypeFqns)) { + return false; + } + const widgetsBundle = widgetsBundleItem.widgetsBundle; + if (isUndefined(widgetsBundle.title)) { + return false; + } + if (isDefined(widgetsBundleItem.widgetTypes)) { + const widgetTypesDetails = widgetsBundleItem.widgetTypes; + for (const widgetTypeDetails of widgetTypesDetails) { + if (!this.validateImportedWidgetTypeDetails(widgetTypeDetails)) { + return false; + } + } + } + if (isDefined(widgetsBundleItem.widgetTypeFqns)) { + if (!Array.isArray(widgetsBundleItem.widgetTypeFqns)) { + return false; + } + } + return true; + } + + private saveImportedDashboard(dashboard: Dashboard): Observable { + return this.dashboardService.saveDashboard(dashboard); + } + + private addImportedWidget(dashboard: Dashboard, targetState: string, + targetLayoutFunction: () => Observable, + widget: Widget, aliasesInfo: AliasesInfo, + filtersInfo: FiltersInfo, + onAliasesUpdateFunction: () => void, + onFiltersUpdateFunction: () => void, + originalColumns: number, originalSize: WidgetSize): Observable { + return targetLayoutFunction().pipe( + mergeMap((targetLayout) => this.itembuffer.addWidgetToDashboard(dashboard, targetState, targetLayout, + widget, aliasesInfo, filtersInfo, onAliasesUpdateFunction, onFiltersUpdateFunction, + originalColumns, originalSize, -1, -1).pipe( + map(() => ({widget, layoutId: targetLayout} as ImportWidgetResult)) + ) + )); + } + + private processEntityAliases(entityAliases: EntityAliases, aliasIds: string[]): Observable { + const tasks: Observable[] = []; + for (const aliasId of aliasIds) { + const entityAlias = entityAliases[aliasId]; + tasks.push( + this.entityService.checkEntityAlias(entityAlias).pipe( + map((result) => { + if (!result) { + const missingEntityAlias = deepClone(entityAlias); + missingEntityAlias.filter = null; + return missingEntityAlias; + } + return null; + } + ) + ) + ); + } + return forkJoin(tasks).pipe( + map((missingAliasesArray) => { + missingAliasesArray = missingAliasesArray.filter(alias => alias !== null); + const missingEntityAliases: EntityAliases = {}; + for (const missingAlias of missingAliasesArray) { + missingEntityAliases[missingAlias.id] = missingAlias; + } + return missingEntityAliases; + } + ) + ); + } + + private prepareAliasesInfo(aliasesInfo: AliasesInfo): AliasesInfo { + const datasourceAliases = aliasesInfo.datasourceAliases; + const targetDeviceAliases = aliasesInfo.targetDeviceAliases; + if (datasourceAliases || targetDeviceAliases) { + if (datasourceAliases) { + for (const strIndex of Object.keys(datasourceAliases)) { + const datasourceIndex = Number(strIndex); + datasourceAliases[datasourceIndex] = this.prepareEntityAlias(datasourceAliases[datasourceIndex]); + } + } + if (targetDeviceAliases) { + for (const strIndex of Object.keys(targetDeviceAliases)) { + const datasourceIndex = Number(strIndex); + targetDeviceAliases[datasourceIndex] = this.prepareEntityAlias(targetDeviceAliases[datasourceIndex]); + } + } + } + return aliasesInfo; + } + + private prepareEntityAlias(aliasInfo: EntityAliasInfo): EntityAliasInfo { + let alias: string; + let filter: EntityAliasFilter; + if (aliasInfo.deviceId) { + alias = aliasInfo.aliasName; + filter = { + type: AliasFilterType.entityList, + entityType: EntityType.DEVICE, + entityList: [aliasInfo.deviceId], + resolveMultiple: false + }; + } else if (aliasInfo.deviceFilter) { + alias = aliasInfo.aliasName; + filter = { + type: aliasInfo.deviceFilter.useFilter ? AliasFilterType.entityName : AliasFilterType.entityList, + entityType: EntityType.DEVICE, + resolveMultiple: false + }; + if (filter.type === AliasFilterType.entityList) { + filter.entityList = aliasInfo.deviceFilter.deviceList; + } else { + filter.entityNameFilter = aliasInfo.deviceFilter.deviceNameFilter; + } + } else if (aliasInfo.entityFilter) { + alias = aliasInfo.aliasName; + filter = { + type: aliasInfo.entityFilter.useFilter ? AliasFilterType.entityName : AliasFilterType.entityList, + entityType: aliasInfo.entityType, + resolveMultiple: false + }; + if (filter.type === AliasFilterType.entityList) { + filter.entityList = aliasInfo.entityFilter.entityList; + } else { + filter.entityNameFilter = aliasInfo.entityFilter.entityNameFilter; + } + } else { + alias = aliasInfo.alias; + filter = aliasInfo.filter; + } + return { + alias, + filter + }; + } + + private openImportDialog(importTitle: string, importFileLabel: string): Observable { + return this.dialog.open(ImportDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + importTitle, + importFileLabel + } + }).afterClosed().pipe( + map((importedData) => { + if (importedData) { + return importedData; + } else { + throw new Error('No file selected!'); + } + } + )); + } + + private exportToPc(data: any, filename: string) { + if (!data) { + console.error('No data'); + return; + } + this.exportJson(data, filename); + } + + private exportJson(data: any, filename: string) { + if (isObject(data)) { + data = JSON.stringify(data, null, 2); + } + this.downloadFile(data, filename, JSON_TYPE); + } + + private downloadFile(data: any, filename: string, fileType: FileType) { + if (!filename) { + filename = 'download'; + } + filename += '.' + fileType.extension; + const blob = new Blob([data], {type: fileType.mimeType}); + // @ts-ignore + if (this.window.navigator && this.window.navigator.msSaveOrOpenBlob) { + // @ts-ignore + this.window.navigator.msSaveOrOpenBlob(blob, filename); + } else { + const e = this.document.createEvent('MouseEvents'); + const a = this.document.createElement('a'); + a.download = filename; + a.href = URL.createObjectURL(blob); + a.dataset.downloadurl = [fileType.mimeType, a.download, a.href].join(':'); + // @ts-ignore + e.initEvent('click', true, false, this.window, + 0, 0, 0, 0, 0, false, false, false, false, 0, null); + a.dispatchEvent(e); + } + } + + private prepareDashboardExport(dashboard: Dashboard): Dashboard { + dashboard = this.dashboardUtils.validateAndUpdateDashboard(dashboard); + dashboard = this.prepareExport(dashboard); + delete dashboard.assignedCustomers; + return dashboard; + } + + private prepareProfileExport(profile: T): T { + profile = this.prepareExport(profile); + profile.default = false; + return profile; + } + + private prepareExport(data: any): any { + const exportedData = deepClone(data); + if (isDefined(exportedData.id)) { + delete exportedData.id; + } + if (isDefined(exportedData.createdTime)) { + delete exportedData.createdTime; + } + if (isDefined(exportedData.tenantId)) { + delete exportedData.tenantId; + } + if (isDefined(exportedData.customerId)) { + delete exportedData.customerId; + } + if (isDefined(exportedData.externalId)) { + delete exportedData.externalId; + } + return exportedData; + } + + private prepareImport>(data: T): T{ + const importedData = deepClone(data); + if (isDefined(importedData.externalId)) { + delete importedData.externalId; + } + return importedData; + } + +} diff --git a/ui-ngx/src/app/modules/home/components/import-export/table-columns-assignment.component.html b/ui-ngx/src/app/shared/import-export/table-columns-assignment.component.html similarity index 100% rename from ui-ngx/src/app/modules/home/components/import-export/table-columns-assignment.component.html rename to ui-ngx/src/app/shared/import-export/table-columns-assignment.component.html diff --git a/ui-ngx/src/app/modules/home/components/import-export/table-columns-assignment.component.scss b/ui-ngx/src/app/shared/import-export/table-columns-assignment.component.scss similarity index 100% rename from ui-ngx/src/app/modules/home/components/import-export/table-columns-assignment.component.scss rename to ui-ngx/src/app/shared/import-export/table-columns-assignment.component.scss diff --git a/ui-ngx/src/app/shared/import-export/table-columns-assignment.component.ts b/ui-ngx/src/app/shared/import-export/table-columns-assignment.component.ts new file mode 100644 index 0000000000..57f3607a2b --- /dev/null +++ b/ui-ngx/src/app/shared/import-export/table-columns-assignment.component.ts @@ -0,0 +1,241 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, ElementRef, forwardRef, Input, OnInit } from '@angular/core'; +import { ControlValueAccessor, UntypedFormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, Validator } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { EntityType } from '@shared/models/entity-type.models'; +import { + CsvColumnParam, + ImportEntityColumnType, + importEntityColumnTypeTranslations +} from '@shared/import-export/import-export.models'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { CollectionViewer, DataSource } from '@angular/cdk/collections'; + +@Component({ + selector: 'tb-table-columns-assignment', + templateUrl: './table-columns-assignment.component.html', + styleUrls: ['./table-columns-assignment.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => TableColumnsAssignmentComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => TableColumnsAssignmentComponent), + multi: true, + } + ] +}) +export class TableColumnsAssignmentComponent implements OnInit, ControlValueAccessor, Validator { + + @Input() entityType: EntityType; + + @Input() disabled: boolean; + + dataSource = new CsvColumnsDatasource(); + + displayedColumns = ['order', 'sampleData', 'type', 'key']; + + columnTypes: AssignmentColumnType[] = []; + + columnDeviceCredentials: AssignmentColumnType[] = []; + + columnTypesTranslations = importEntityColumnTypeTranslations; + + readonly entityTypeDevice = EntityType.DEVICE; + + private columns: CsvColumnParam[]; + + private valid = true; + + private propagateChangePending = false; + private propagateChange = null; + + constructor(public elementRef: ElementRef, + protected store: Store) { + } + + ngOnInit(): void { + this.columnTypes.push( + { value: ImportEntityColumnType.name }, + { value: ImportEntityColumnType.type }, + { value: ImportEntityColumnType.label }, + { value: ImportEntityColumnType.description }, + ); + switch (this.entityType) { + case EntityType.DEVICE: + this.columnTypes.push( + { value: ImportEntityColumnType.sharedAttribute }, + { value: ImportEntityColumnType.serverAttribute }, + { value: ImportEntityColumnType.timeseries }, + { value: ImportEntityColumnType.isGateway } + ); + this.columnDeviceCredentials.push( + { value: ImportEntityColumnType.accessToken }, + { value: ImportEntityColumnType.x509 }, + { value: ImportEntityColumnType.mqttClientId }, + { value: ImportEntityColumnType.mqttUserName }, + { value: ImportEntityColumnType.mqttPassword }, + { value: ImportEntityColumnType.lwm2mClientEndpoint }, + { value: ImportEntityColumnType.lwm2mClientSecurityConfigMode }, + { value: ImportEntityColumnType.lwm2mClientIdentity }, + { value: ImportEntityColumnType.lwm2mClientKey }, + { value: ImportEntityColumnType.lwm2mClientCert }, + { value: ImportEntityColumnType.lwm2mBootstrapServerSecurityMode }, + { value: ImportEntityColumnType.lwm2mBootstrapServerClientPublicKeyOrId }, + { value: ImportEntityColumnType.lwm2mBootstrapServerClientSecretKey }, + { value: ImportEntityColumnType.lwm2mServerSecurityMode }, + { value: ImportEntityColumnType.lwm2mServerClientPublicKeyOrId }, + { value: ImportEntityColumnType.lwm2mServerClientSecretKey }, + { value: ImportEntityColumnType.snmpHost }, + { value: ImportEntityColumnType.snmpPort }, + { value: ImportEntityColumnType.snmpVersion }, + { value: ImportEntityColumnType.snmpCommunityString }, + ); + break; + case EntityType.ASSET: + this.columnTypes.push( + { value: ImportEntityColumnType.serverAttribute }, + { value: ImportEntityColumnType.timeseries } + ); + break; + case EntityType.EDGE: + this.columnTypes.push( + { value: ImportEntityColumnType.routingKey }, + { value: ImportEntityColumnType.secret }, + { value: ImportEntityColumnType.serverAttribute }, + { value: ImportEntityColumnType.timeseries } + ); + break; + } + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + if (this.propagateChangePending) { + this.propagateChange(this.columns); + this.propagateChangePending = false; + } + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + } + + columnsUpdated() { + const isSelectName = this.columns.findIndex((column) => column.type === ImportEntityColumnType.name) > -1; + const isSelectType = this.columns.findIndex((column) => column.type === ImportEntityColumnType.type) > -1; + const isSelectLabel = this.columns.findIndex((column) => column.type === ImportEntityColumnType.label) > -1; + const isSelectDescription = this.columns.findIndex((column) => column.type === ImportEntityColumnType.description) > -1; + const hasInvalidColumn = this.columns.findIndex((column) => !this.columnValid(column)) > -1; + + this.valid = isSelectName && isSelectType && !hasInvalidColumn; + + this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.name).disabled = isSelectName; + this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.type).disabled = isSelectType; + this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.label).disabled = isSelectLabel; + this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.description).disabled = isSelectDescription; + + if (this.entityType === EntityType.DEVICE) { + const isSelectGateway = this.columns.findIndex((column) => column.type === ImportEntityColumnType.isGateway) > -1; + + const isGatewayColumnType = this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.isGateway); + if (isGatewayColumnType) { + isGatewayColumnType.disabled = isSelectGateway; + } + + this.columnDeviceCredentials.forEach((columnCredential) => { + columnCredential.disabled = this.columns.findIndex(column => column.type === columnCredential.value) > -1; + }); + } + + if (this.entityType === EntityType.EDGE) { + const isSelectRoutingKey = this.columns.findIndex((column) => column.type === ImportEntityColumnType.routingKey) > -1; + const isSelectSecret = this.columns.findIndex((column) => column.type === ImportEntityColumnType.secret) > -1; + + this.valid = this.valid && isSelectSecret && isSelectRoutingKey; + + this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.routingKey).disabled = isSelectRoutingKey; + this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.secret).disabled = isSelectSecret; + } + + if (this.propagateChange) { + this.propagateChange(this.columns); + } else { + this.propagateChangePending = true; + } + } + + public isColumnTypeDiffers(columnType: ImportEntityColumnType): boolean { + return columnType === ImportEntityColumnType.clientAttribute || + columnType === ImportEntityColumnType.sharedAttribute || + columnType === ImportEntityColumnType.serverAttribute || + columnType === ImportEntityColumnType.timeseries; + } + + private columnValid(column: CsvColumnParam): boolean { + if (this.isColumnTypeDiffers(column.type)) { + return column.key && column.key.trim().length > 0; + } else { + return true; + } + } + + public validate(c: UntypedFormControl) { + return (this.valid) ? null : { + columnsInvalid: true + }; + } + + writeValue(value: CsvColumnParam[]): void { + this.columns = value; + this.dataSource.setColumns(this.columns); + this.columnsUpdated(); + } +} + +interface AssignmentColumnType { + value: ImportEntityColumnType; + disabled?: boolean; +} + +class CsvColumnsDatasource implements DataSource { + + private columnsSubject = new BehaviorSubject([]); + + constructor() {} + + connect(collectionViewer: CollectionViewer): Observable> { + return this.columnsSubject.asObservable(); + } + + disconnect(collectionViewer: CollectionViewer): void { + this.columnsSubject.complete(); + } + + setColumns(columns: CsvColumnParam[]) { + this.columnsSubject.next(columns); + } + +} diff --git a/ui-ngx/src/app/shared/models/ace/tbel/worker-tbel.js b/ui-ngx/src/app/shared/models/ace/tbel/worker-tbel.js index bc22e806c3..ec4133da2a 100644 --- a/ui-ngx/src/app/shared/models/ace/tbel/worker-tbel.js +++ b/ui-ngx/src/app/shared/models/ace/tbel/worker-tbel.js @@ -4656,10 +4656,12 @@ var JSHINT = (function() { case "else": case "finally": case "for": + case "foreach": case "if": case "in": case "instanceof": case "return": + case "until": case "switch": case "throw": case "try": @@ -5387,6 +5389,7 @@ var JSHINT = (function() { } switch (state.funct["(verb)"]) { case "break": + case "until": case "continue": case "return": case "throw": @@ -7691,6 +7694,20 @@ var JSHINT = (function() { return this; }).labelled = true; + blockstmt("until", function(context) { + var t = state.tokens.next; + state.funct["(breakage)"] += 1; + state.funct["(loopage)"] += 1; + increaseComplexityCount(); + advance("("); + checkCondAssignment(expression(context, 0)); + advance(")", t); + block(context, true, true); + state.funct["(breakage)"] -= 1; + state.funct["(loopage)"] -= 1; + return this; + }).labelled = true; + blockstmt("with", function(context) { var t = state.tokens.next; if (state.isStrict()) { @@ -7840,7 +7857,12 @@ var JSHINT = (function() { increaseComplexityCount(); this.first = block(context, true, true); - advance("while"); + var s = state.tokens.next; + if (s.value !== "while") { + advance("until"); + } else { + advance("while"); + } var t = state.tokens.next; advance("("); checkCondAssignment(expression(context, 0)); @@ -8135,7 +8157,7 @@ var JSHINT = (function() { state.funct["(breakage)"] += 1; state.funct["(loopage)"] += 1; - state.funct["(breakage)"] -= 1; + // state.funct["(breakage)"] -= 1; state.funct["(loopage)"] -= 1; } else { nolinebreak(state.tokens.curr); @@ -9755,9 +9777,9 @@ Lexer.prototype = { scanKeyword: function() { var result = /^[a-zA-Z_$][a-zA-Z0-9_$]*/.exec(this.input); var keywords = [ - "if", "in", "do", "var", "for", "new", + "if", "in", "do", "var", "for", "foreach", "new", "try", "let", "this", "else", "case", - "void", "with", "enum", "while", "break", + "void", "with", "enum", "while", "until", "break", "catch", "throw", "const", "yield", "class", "super", "return", "typeof", "delete", "switch", "export", "import", "default", @@ -11364,7 +11386,6 @@ var warnings = { W086: "Expected a 'break' statement before '{a}'.", W087: "Forgotten 'debugger' statement?", W088: "Creating global 'for' variable. Should be 'for (var {a} ...'.", - // W288: "The syntax of function '{a}' is specific to TBEL, and is not supported by JS executor.", W089: "The body of a for in should be wrapped in an if statement to filter " + "unwanted properties from the prototype.", W090: "'{a}' is not a statement label.", diff --git a/ui-ngx/src/app/shared/models/alarm.models.ts b/ui-ngx/src/app/shared/models/alarm.models.ts index 202b25d539..2cfe0531ff 100644 --- a/ui-ngx/src/app/shared/models/alarm.models.ts +++ b/ui-ngx/src/app/shared/models/alarm.models.ts @@ -26,6 +26,7 @@ import { TableCellButtonActionDescriptor } from '@home/components/widget/lib/tab import { AlarmCommentId } from '@shared/models/id/alarm-comment-id'; import { UserId } from '@shared/models/id/user-id'; import { AlarmFilter } from '@shared/models/query/query.models'; +import { HasTenantId } from '@shared/models/entity.models'; export enum AlarmsMode { ALL, @@ -94,7 +95,7 @@ export const alarmSeverityColors = new Map( ] ); -export interface Alarm extends BaseData { +export interface Alarm extends BaseData, HasTenantId { tenantId: TenantId; customerId: CustomerId; assigneeId: UserId; @@ -102,6 +103,8 @@ export interface Alarm extends BaseData { originator: EntityId; severity: AlarmSeverity; status: AlarmStatus; + acknowledged: boolean; + cleared: boolean; startTs: number; endTs: number; ackTs: number; @@ -145,6 +148,11 @@ export interface AlarmAssignee { email: string; } +export enum AlarmAssigneeOption { + noAssignee = 'noAssignee', + currentUser = 'currentUser' +} + export interface AlarmDataInfo extends AlarmInfo { actionCellButtons?: TableCellButtonActionDescriptor[]; hasActions?: boolean; @@ -176,6 +184,8 @@ export const simulatedAlarm: AlarmInfo = { type: 'TEMPERATURE', severity: AlarmSeverity.MAJOR, status: AlarmStatus.ACTIVE_UNACK, + acknowledged: false, + cleared: false, details: { message: 'Temperature is high!' }, diff --git a/ui-ngx/src/app/shared/models/api-usage.models.ts b/ui-ngx/src/app/shared/models/api-usage.models.ts index bbc0ff5597..e6b1d86207 100644 --- a/ui-ngx/src/app/shared/models/api-usage.models.ts +++ b/ui-ngx/src/app/shared/models/api-usage.models.ts @@ -31,6 +31,7 @@ export enum ApiFeature { DB = 'DB', RE = 'RE', JS = 'JS', + TBEL = 'TBEL', EMAIL = 'EMAIL', SMS = 'SMS', ALARM = 'ALARM' @@ -41,6 +42,7 @@ export const ApiFeatureTranslationMap = new Map([ [ApiFeature.DB, 'api-usage.telemetry-persistence'], [ApiFeature.RE, 'api-usage.rule-engine-executions'], [ApiFeature.JS, 'api-usage.javascript-executions'], + [ApiFeature.TBEL, 'api-usage.tbel-executions'], [ApiFeature.EMAIL, 'api-usage.email-messages'], [ApiFeature.SMS, 'api-usage.sms-messages'], [ApiFeature.ALARM, 'api-usage.alarm'], diff --git a/ui-ngx/src/app/shared/models/asset.models.ts b/ui-ngx/src/app/shared/models/asset.models.ts index a1ab16fdc8..2e9b55f611 100644 --- a/ui-ngx/src/app/shared/models/asset.models.ts +++ b/ui-ngx/src/app/shared/models/asset.models.ts @@ -22,11 +22,11 @@ import { EntitySearchQuery } from '@shared/models/relation.models'; import { AssetProfileId } from '@shared/models/id/asset-profile-id'; import { RuleChainId } from '@shared/models/id/rule-chain-id'; import { DashboardId } from '@shared/models/id/dashboard-id'; -import { EntityInfoData } from '@shared/models/entity.models'; +import { EntityInfoData, HasTenantId } from '@shared/models/entity.models'; export const TB_SERVICE_QUEUE = 'TbServiceQueue'; -export interface AssetProfile extends BaseData, ExportableEntity { +export interface AssetProfile extends BaseData, HasTenantId, ExportableEntity { tenantId?: TenantId; name: string; description?: string; @@ -44,7 +44,7 @@ export interface AssetProfileInfo extends EntityInfoData { defaultDashboardId?: DashboardId; } -export interface Asset extends BaseData, ExportableEntity { +export interface Asset extends BaseData, HasTenantId, ExportableEntity { tenantId?: TenantId; customerId?: CustomerId; name: string; diff --git a/ui-ngx/src/app/shared/models/base-data.ts b/ui-ngx/src/app/shared/models/base-data.ts index 16022641aa..437780fcbb 100644 --- a/ui-ngx/src/app/shared/models/base-data.ts +++ b/ui-ngx/src/app/shared/models/base-data.ts @@ -39,8 +39,6 @@ export function sortEntitiesByIds>(entiti } export interface ExportableEntity { - createdTime?: number; - id?: T; externalId?: T; } diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index 8648973c72..b566c15280 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -30,7 +30,8 @@ export const Constants = { badRequestParams: 31, itemNotFound: 32, tooManyRequests: 33, - tooManyUpdates: 34 + tooManyUpdates: 34, + passwordViolation: 45 }, entryPoints: { login: '/api/auth/login', @@ -71,6 +72,13 @@ export const MediaBreakpoints = { 'md-lg': 'screen and (min-width: 960px) and (max-width: 1819px)' }; +export const resolveBreakpoint = (breakpoint: string): string => { + if (MediaBreakpoints[breakpoint]) { + return MediaBreakpoints[breakpoint]; + } + return breakpoint; +}; + export const helpBaseUrl = 'https://thingsboard.io'; export const HelpLinks = { @@ -149,7 +157,8 @@ export const HelpLinks = { lwm2mResourceLibrary: helpBaseUrl + '/docs/reference/lwm2m-api', dashboards: helpBaseUrl + '/docs/user-guide/ui/dashboards', otaUpdates: helpBaseUrl + '/docs/user-guide/ota-updates', - widgetsBundles: helpBaseUrl + '/docs/user-guide/ui/widget-library#bundles', + widgetTypes: helpBaseUrl + '/docs/user-guide/ui/widget-library/#widget-types', + widgetsBundles: helpBaseUrl + '/docs/user-guide/ui/widget-library/#widgets-library-bundles', widgetsConfig: helpBaseUrl + '/docs/user-guide/ui/dashboards#widget-configuration', widgetsConfigTimeseries: helpBaseUrl + '/docs/user-guide/ui/dashboards#timeseries', widgetsConfigLatest: helpBaseUrl + '/docs/user-guide/ui/dashboards#latest', @@ -167,6 +176,7 @@ export const HelpLinks = { recipientNotifications: helpBaseUrl + '/docs/user-guide/notifications/#recipients', ruleNotifications: helpBaseUrl + '/docs/user-guide/notifications/#rules', jwtSecuritySettings: helpBaseUrl + '/docs/user-guide/ui/jwt-security-settings/', + gatewayInstall: helpBaseUrl + '/docs/iot-gateway/install/docker-installation/', } }; diff --git a/ui-ngx/src/app/shared/models/customer.model.ts b/ui-ngx/src/app/shared/models/customer.model.ts index d26725814f..d770ba2420 100644 --- a/ui-ngx/src/app/shared/models/customer.model.ts +++ b/ui-ngx/src/app/shared/models/customer.model.ts @@ -18,8 +18,9 @@ import { CustomerId } from '@shared/models/id/customer-id'; import { ContactBased } from '@shared/models/contact-based.model'; import { TenantId } from './id/tenant-id'; import { ExportableEntity } from '@shared/models/base-data'; +import { HasTenantId } from '@shared/models/entity.models'; -export interface Customer extends ContactBased, ExportableEntity { +export interface Customer extends ContactBased, HasTenantId, ExportableEntity { tenantId: TenantId; title: string; additionalInfo?: any; diff --git a/ui-ngx/src/app/shared/models/dashboard.models.ts b/ui-ngx/src/app/shared/models/dashboard.models.ts index f3f2038fea..14e8f6b1a8 100644 --- a/ui-ngx/src/app/shared/models/dashboard.models.ts +++ b/ui-ngx/src/app/shared/models/dashboard.models.ts @@ -23,8 +23,9 @@ import { Timewindow } from '@shared/models/time/time.models'; import { EntityAliases } from './alias.models'; import { Filters } from '@shared/models/query/query.models'; import { MatDialogRef } from '@angular/material/dialog'; +import { HasTenantId } from '@shared/models/entity.models'; -export interface DashboardInfo extends BaseData, ExportableEntity { +export interface DashboardInfo extends BaseData, HasTenantId, ExportableEntity { tenantId?: TenantId; title?: string; image?: string; @@ -74,10 +75,10 @@ export interface DashboardLayoutInfo { } export interface LayoutDimension { - type?: LayoutType, - fixedWidth?: number, - fixedLayout?: DashboardLayoutId, - leftWidthPercentage?: number + type?: LayoutType; + fixedWidth?: number; + fixedLayout?: DashboardLayoutId; + leftWidthPercentage?: number; } export declare type DashboardLayoutId = 'main' | 'right'; @@ -137,16 +138,20 @@ export interface HomeDashboardInfo { hideDashboardToolbar: boolean; } -export function isPublicDashboard(dashboard: DashboardInfo): boolean { +export interface DashboardSetup extends Dashboard { + assignedCustomerIds?: Array; +} + +export const isPublicDashboard = (dashboard: DashboardInfo): boolean => { if (dashboard && dashboard.assignedCustomers) { return dashboard.assignedCustomers .filter(customerInfo => customerInfo.public).length > 0; } else { return false; } -} +}; -export function getDashboardAssignedCustomersText(dashboard: DashboardInfo): string { +export const getDashboardAssignedCustomersText = (dashboard: DashboardInfo): string => { if (dashboard && dashboard.assignedCustomers && dashboard.assignedCustomers.length > 0) { return dashboard.assignedCustomers .filter(customerInfo => !customerInfo.public) @@ -155,14 +160,13 @@ export function getDashboardAssignedCustomersText(dashboard: DashboardInfo): str } else { return ''; } -} +}; -export function isCurrentPublicDashboardCustomer(dashboard: DashboardInfo, customerId: string): boolean { +export const isCurrentPublicDashboardCustomer = (dashboard: DashboardInfo, customerId: string): boolean => { if (customerId && dashboard && dashboard.assignedCustomers) { - return dashboard.assignedCustomers.filter(customerInfo => { - return customerInfo.public && customerId === customerInfo.customerId.id; - }).length > 0; + return dashboard.assignedCustomers.filter(customerInfo => + customerInfo.public && customerId === customerInfo.customerId.id).length > 0; } else { return false; } -} +}; diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts index f7802b3a8b..a82079ce67 100644 --- a/ui-ngx/src/app/shared/models/device.models.ts +++ b/ui-ngx/src/app/shared/models/device.models.ts @@ -22,7 +22,7 @@ import { DeviceCredentialsId } from '@shared/models/id/device-credentials-id'; import { EntitySearchQuery } from '@shared/models/relation.models'; import { DeviceProfileId } from '@shared/models/id/device-profile-id'; import { RuleChainId } from '@shared/models/id/rule-chain-id'; -import { EntityInfoData } from '@shared/models/entity.models'; +import { EntityInfoData, HasTenantId } from '@shared/models/entity.models'; import { FilterPredicateValue, KeyFilter } from '@shared/models/query/query.models'; import { TimeUnit } from '@shared/models/time/time.models'; import * as _moment from 'moment'; @@ -52,6 +52,12 @@ export enum DeviceTransportType { SNMP = 'SNMP' } +export enum BasicTransportType { + HTTP = 'HTTP' +} +export type TransportType = BasicTransportType | DeviceTransportType; +export type NetworkTransportType = BasicTransportType | Exclude; + export enum TransportPayloadType { JSON = 'JSON', PROTOBUF = 'PROTOBUF' @@ -99,13 +105,14 @@ export const deviceProfileTypeConfigurationInfoMap = new Map( +export const deviceTransportTypeTranslationMap = new Map( [ [DeviceTransportType.DEFAULT, 'device-profile.transport-type-default'], [DeviceTransportType.MQTT, 'device-profile.transport-type-mqtt'], [DeviceTransportType.COAP, 'device-profile.transport-type-coap'], [DeviceTransportType.LWM2M, 'device-profile.transport-type-lwm2m'], - [DeviceTransportType.SNMP, 'device-profile.transport-type-snmp'] + [DeviceTransportType.SNMP, 'device-profile.transport-type-snmp'], + [BasicTransportType.HTTP, 'device-profile.transport-type-http'] ] ); @@ -119,13 +126,14 @@ export const deviceProvisionTypeTranslationMap = new Map( +export const deviceTransportTypeHintMap = new Map( [ [DeviceTransportType.DEFAULT, 'device-profile.transport-type-default-hint'], [DeviceTransportType.MQTT, 'device-profile.transport-type-mqtt-hint'], [DeviceTransportType.COAP, 'device-profile.transport-type-coap-hint'], [DeviceTransportType.LWM2M, 'device-profile.transport-type-lwm2m-hint'], - [DeviceTransportType.SNMP, 'device-profile.transport-type-snmp-hint'] + [DeviceTransportType.SNMP, 'device-profile.transport-type-snmp-hint'], + [BasicTransportType.HTTP, ''] ] ); @@ -290,14 +298,16 @@ export enum SnmpSpecType { TELEMETRY_QUERYING = 'TELEMETRY_QUERYING', CLIENT_ATTRIBUTES_QUERYING = 'CLIENT_ATTRIBUTES_QUERYING', SHARED_ATTRIBUTES_SETTING = 'SHARED_ATTRIBUTES_SETTING', - TO_DEVICE_RPC_REQUEST = 'TO_DEVICE_RPC_REQUEST' + TO_DEVICE_RPC_REQUEST = 'TO_DEVICE_RPC_REQUEST', + TO_SERVER_RPC_REQUEST = 'TO_SERVER_RPC_REQUEST' } export const SnmpSpecTypeTranslationMap = new Map([ - [SnmpSpecType.TELEMETRY_QUERYING, ' Telemetry'], - [SnmpSpecType.CLIENT_ATTRIBUTES_QUERYING, 'Client attributes'], - [SnmpSpecType.SHARED_ATTRIBUTES_SETTING, 'Shared attributes'], - [SnmpSpecType.TO_DEVICE_RPC_REQUEST, 'RPC request'] + [SnmpSpecType.TELEMETRY_QUERYING, ' Telemetry (SNMP GET)'], + [SnmpSpecType.CLIENT_ATTRIBUTES_QUERYING, 'Client attributes (SNMP GET)'], + [SnmpSpecType.SHARED_ATTRIBUTES_SETTING, 'Shared attributes (SNMP SET)'], + [SnmpSpecType.TO_DEVICE_RPC_REQUEST, 'To-device RPC request (SNMP GET/SET)'], + [SnmpSpecType.TO_SERVER_RPC_REQUEST, 'From-device RPC request (SNMP TRAP)'] ]); export interface SnmpCommunicationConfig { @@ -574,7 +584,7 @@ export interface DeviceProfileData { provisionConfiguration?: DeviceProvisionConfiguration; } -export interface DeviceProfile extends BaseData, ExportableEntity { +export interface DeviceProfile extends BaseData, HasTenantId, ExportableEntity { tenantId?: TenantId; name: string; description?: string; @@ -593,7 +603,7 @@ export interface DeviceProfile extends BaseData, ExportableEnti defaultEdgeRuleChainId?: RuleChainId; } -export interface DeviceProfileInfo extends EntityInfoData { +export interface DeviceProfileInfo extends EntityInfoData, HasTenantId { tenantId?: TenantId; type: DeviceProfileType; transportType: DeviceTransportType; @@ -701,11 +711,11 @@ export interface DeviceData { transportConfiguration: DeviceTransportConfiguration; } -export interface Device extends BaseData, ExportableEntity { +export interface Device extends BaseData, HasTenantId, ExportableEntity { tenantId?: TenantId; customerId?: CustomerId; name: string; - type: string; + type?: string; label: string; firmwareId?: OtaPackageId; softwareId?: OtaPackageId; @@ -829,6 +839,32 @@ export interface ClaimResult { response: ClaimResponse; } +export interface PublishTelemetryCommand { + http?: { + http?: string; + https?: string; + }; + mqtt: { + mqtt?: string; + mqtts?: string | Array; + docker?: { + mqtt?: string; + mqtts?: string | Array; + }; + sparkplug?: string; + }; + coap: { + coap?: string; + coaps?: string | Array; + docker?: { + coap?: string; + coaps?: string | Array; + }; + }; + lwm2m?: string; + snmp?: string; +} + export const dayOfWeekTranslations = new Array( 'device-profile.schedule-day.monday', 'device-profile.schedule-day.tuesday', diff --git a/ui-ngx/src/app/shared/models/edge.models.ts b/ui-ngx/src/app/shared/models/edge.models.ts index 372a910402..26337b5e35 100644 --- a/ui-ngx/src/app/shared/models/edge.models.ts +++ b/ui-ngx/src/app/shared/models/edge.models.ts @@ -22,8 +22,9 @@ import { EntitySearchQuery } from '@shared/models/relation.models'; import { RuleChainId } from '@shared/models/id/rule-chain-id'; import { BaseEventBody } from '@shared/models/event.models'; import { EventId } from '@shared/models/id/event-id'; +import { HasTenantId } from '@shared/models/entity.models'; -export interface Edge extends BaseData { +export interface Edge extends BaseData, HasTenantId { tenantId?: TenantId; customerId?: CustomerId; name: string; @@ -59,9 +60,12 @@ export enum EdgeEventType { CUSTOMER = 'CUSTOMER', RELATION = 'RELATION', TENANT = 'TENANT', + TENANT_PROFILE = 'TENANT_PROFILE', WIDGETS_BUNDLE = 'WIDGETS_BUNDLE', WIDGET_TYPE = 'WIDGET_TYPE', - ADMIN_SETTINGS = 'ADMIN_SETTINGS' + ADMIN_SETTINGS = 'ADMIN_SETTINGS', + OTA_PACKAGE = 'OTA_PACKAGE', + QUEUE = 'QUEUE' } export enum EdgeEventActionType { @@ -80,6 +84,8 @@ export enum EdgeEventActionType { RPC_CALL = 'RPC_CALL', ALARM_ACK = 'ALARM_ACK', ALARM_CLEAR = 'ALARM_CLEAR', + ALARM_ASSIGNED = 'ALARM_ASSIGNED', + ALARM_UNASSIGNED = 'ALARM_UNASSIGNED', ASSIGNED_TO_EDGE = 'ASSIGNED_TO_EDGE', UNASSIGNED_FROM_EDGE = 'UNASSIGNED_FROM_EDGE', CREDENTIALS_REQUEST = 'CREDENTIALS_REQUEST', @@ -107,9 +113,12 @@ export const edgeEventTypeTranslations = new Map( [EdgeEventType.CUSTOMER, 'edge-event.type-customer'], [EdgeEventType.RELATION, 'edge-event.type-relation'], [EdgeEventType.TENANT, 'edge-event.type-tenant'], + [EdgeEventType.TENANT_PROFILE, 'edge-event.type-tenant-profile'], [EdgeEventType.WIDGETS_BUNDLE, 'edge-event.type-widgets-bundle'], [EdgeEventType.WIDGET_TYPE, 'edge-event.type-widgets-type'], - [EdgeEventType.ADMIN_SETTINGS, 'edge-event.type-admin-settings'] + [EdgeEventType.ADMIN_SETTINGS, 'edge-event.type-admin-settings'], + [EdgeEventType.OTA_PACKAGE, 'edge-event.type-ota-package'], + [EdgeEventType.QUEUE, 'edge-event.type-queue'] ] ); @@ -130,6 +139,8 @@ export const edgeEventActionTypeTranslations = new Map { body: string; } -export interface EdgeInstallInstructions { - dockerInstallInstructions: string; +export interface EdgeInstructions { + instructions: string; } + +export enum EdgeInstructionsMethod { + ubuntu, + centos, + docker +} + +export const edgeVersionAttributeKey = 'edgeVersion'; diff --git a/ui-ngx/src/app/shared/models/entity-type.models.ts b/ui-ngx/src/app/shared/models/entity-type.models.ts index 1a5f187459..adc750b52b 100644 --- a/ui-ngx/src/app/shared/models/entity-type.models.ts +++ b/ui-ngx/src/app/shared/models/entity-type.models.ts @@ -268,6 +268,19 @@ export const entityTypeTranslations = new Map([ [EntityType.ENTITY_VIEW, '/entities/entityViews'], [EntityType.TB_RESOURCE, '/resources/resources-library'], [EntityType.OTA_PACKAGE, '/features/otaUpdates'], - [EntityType.QUEUE, '/settings/queues'] + [EntityType.QUEUE, '/settings/queues'], + [EntityType.WIDGETS_BUNDLE, '/resources/widgets-library/widgets-bundles/details'], + [EntityType.WIDGET_TYPE, '/resources/widgets-library/widget-types/details'] ]); export interface EntitySubtype { diff --git a/ui-ngx/src/app/shared/models/entity-view.models.ts b/ui-ngx/src/app/shared/models/entity-view.models.ts index dd16914bc1..5c34e932c3 100644 --- a/ui-ngx/src/app/shared/models/entity-view.models.ts +++ b/ui-ngx/src/app/shared/models/entity-view.models.ts @@ -20,6 +20,7 @@ import { CustomerId } from '@shared/models/id/customer-id'; import { EntityViewId } from '@shared/models/id/entity-view-id'; import { EntityId } from '@shared/models/id/entity-id'; import { EntitySearchQuery } from '@shared/models/relation.models'; +import { HasTenantId } from '@shared/models/entity.models'; export interface AttributesEntityView { cs: Array; @@ -32,7 +33,7 @@ export interface TelemetryEntityView { attributes: AttributesEntityView; } -export interface EntityView extends BaseData, ExportableEntity { +export interface EntityView extends BaseData, HasTenantId, ExportableEntity { tenantId: TenantId; customerId: CustomerId; entityId: EntityId; diff --git a/ui-ngx/src/app/shared/models/entity.models.ts b/ui-ngx/src/app/shared/models/entity.models.ts index 548b912b04..7e48eb2d43 100644 --- a/ui-ngx/src/app/shared/models/entity.models.ts +++ b/ui-ngx/src/app/shared/models/entity.models.ts @@ -19,6 +19,7 @@ import { AttributeData } from './telemetry/telemetry.models'; import { EntityId } from '@shared/models/id/entity-id'; import { DeviceCredentialMQTTBasic } from '@shared/models/device.models'; import { Lwm2mSecurityConfigModels } from '@shared/models/lwm2m-security-config.models'; +import { TenantId } from '@shared/models/id/tenant-id'; export interface EntityInfo { name?: string; @@ -162,3 +163,7 @@ export const entityFields: {[fieldName: string]: EntityField} = { value: 'label' } }; + +export interface HasTenantId { + tenantId?: TenantId; +} diff --git a/ui-ngx/src/app/shared/models/icon.models.ts b/ui-ngx/src/app/shared/models/icon.models.ts new file mode 100644 index 0000000000..85c6617aa1 --- /dev/null +++ b/ui-ngx/src/app/shared/models/icon.models.ts @@ -0,0 +1,140 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ResourcesService } from '@core/services/resources.service'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { isNotEmptyStr } from '@core/utils'; + +export const svgIcons: {[key: string]: string} = { + 'google-logo': '', + 'github-logo': '', + 'facebook-logo': '', + 'apple-logo': '', + 'queues-list': '' + + '' + + '' + + '' + + '' +}; + +export const svgIconsUrl: { [key: string]: string } = { + windows: '/assets/windows.svg', + macos: '/assets/macos.svg', + linux: '/assets/linux.svg', + docker: '/assets/docker.svg' +}; + +const svgIconNamespaces: string[] = ['mdi']; +const svgIconNames = [...Object.keys(svgIcons), ...Object.keys(svgIconsUrl)]; + +export const splitIconName = (iconName: string): [string, string] => { + if (!iconName) { + return ['', '']; + } + const parts = iconName.split(':'); + switch (parts.length) { + case 1: + return ['', parts[0]]; + case 2: + return parts as [string, string]; + default: + throw Error(`Invalid icon name: "${iconName}"`); + } +}; + +export const isSvgIcon = (icon: string): boolean => { + const [namespace, iconName] = splitIconName(icon); + return svgIconNamespaces.includes(namespace) || svgIconNames.includes(iconName); +}; + +export interface MaterialIcon { + name: string; + displayName?: string; + tags: string[]; +} + +export const iconByName = (icons: Array, name: string): MaterialIcon => icons.find(i => i.name === name); + +const searchIconTags = (icon: MaterialIcon, searchText: string): boolean => + !!icon.tags.find(t => t.toUpperCase().includes(searchText.toUpperCase())); + +const searchIcons = (_icons: Array, searchText: string): Array => _icons.filter( + i => i.name.toUpperCase().includes(searchText.toUpperCase()) || + i.displayName.toUpperCase().includes(searchText.toUpperCase()) || + searchIconTags(i, searchText) +); + +const getCommonMaterialIcons = (icons: Array, chunkSize: number): Array => icons.slice(0, chunkSize * 4); + +export const getMaterialIcons = (resourcesService: ResourcesService, chunkSize = 11, + all = false, searchText: string): Observable => + resourcesService.loadJsonResource>('/assets/metadata/material-icons.json', + (icons) => { + for (const icon of icons) { + const iconName = splitIconName(icon.name)[1]; + const words = iconName.replace(/[_\-]/g, ' ').split(' '); + for (let i = 0; i < words.length; i++) { + words[i] = words[i].charAt(0).toUpperCase() + words[i].slice(1); + } + icon.displayName = words.join(' '); + } + return icons; + } + ).pipe( + map((icons) => { + if (isNotEmptyStr(searchText)) { + return searchIcons(icons, searchText); + } else if (!all) { + return getCommonMaterialIcons(icons, chunkSize); + } else { + return icons; + } + }), + map((icons) => { + const iconChunks: MaterialIcon[][] = []; + for (let i = 0; i < icons.length; i += chunkSize) { + const chunk = icons.slice(i, i + chunkSize); + iconChunks.push(chunk); + } + return iconChunks; + }) + ); diff --git a/ui-ngx/src/app/shared/models/limited-api.models.ts b/ui-ngx/src/app/shared/models/limited-api.models.ts new file mode 100644 index 0000000000..3b68800f24 --- /dev/null +++ b/ui-ngx/src/app/shared/models/limited-api.models.ts @@ -0,0 +1,43 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +export enum LimitedApi { + ENTITY_EXPORT = 'ENTITY_EXPORT', + ENTITY_IMPORT = 'ENTITY_IMPORT', + NOTIFICATION_REQUESTS = 'NOTIFICATION_REQUESTS', + NOTIFICATION_REQUESTS_PER_RULE = 'NOTIFICATION_REQUESTS_PER_RULE', + REST_REQUESTS_PER_TENANT = 'REST_REQUESTS_PER_TENANT', + REST_REQUESTS_PER_CUSTOMER = 'REST_REQUESTS_PER_CUSTOMER', + WS_UPDATES_PER_SESSION = 'WS_UPDATES_PER_SESSION', + CASSANDRA_QUERIES = 'CASSANDRA_QUERIES', + TRANSPORT_MESSAGES_PER_TENANT = 'TRANSPORT_MESSAGES_PER_TENANT', + TRANSPORT_MESSAGES_PER_DEVICE = 'TRANSPORT_MESSAGES_PER_DEVICE' +} + +export const LimitedApiTranslationMap = new Map( + [ + [LimitedApi.ENTITY_EXPORT, 'api-limit.entity-version-creation'], + [LimitedApi.ENTITY_IMPORT, 'api-limit.entity-version-load'], + [LimitedApi.NOTIFICATION_REQUESTS, 'api-limit.notification-requests'], + [LimitedApi.NOTIFICATION_REQUESTS_PER_RULE, 'api-limit.notification-requests-per-rule'], + [LimitedApi.REST_REQUESTS_PER_TENANT, 'api-limit.rest-api-requests'], + [LimitedApi.REST_REQUESTS_PER_CUSTOMER, 'api-limit.rest-api-requests-per-customer'], + [LimitedApi.WS_UPDATES_PER_SESSION, 'api-limit.ws-updates-per-session'], + [LimitedApi.CASSANDRA_QUERIES, 'api-limit.cassandra-queries'], + [LimitedApi.TRANSPORT_MESSAGES_PER_TENANT, 'api-limit.transport-messages'], + [LimitedApi.TRANSPORT_MESSAGES_PER_DEVICE, 'api-limit.transport-messages-per-device'] + ] +); diff --git a/ui-ngx/src/app/shared/models/notification.models.ts b/ui-ngx/src/app/shared/models/notification.models.ts index 23c28ce440..cddd7f63f3 100644 --- a/ui-ngx/src/app/shared/models/notification.models.ts +++ b/ui-ngx/src/app/shared/models/notification.models.ts @@ -26,6 +26,8 @@ import { NotificationRuleId } from '@shared/models/id/notification-rule-id'; import { AlarmSearchStatus, AlarmSeverity, AlarmStatus } from '@shared/models/alarm.models'; import { EntityType } from '@shared/models/entity-type.models'; import { ApiFeature, ApiUsageStateValue } from '@shared/models/api-usage.models'; +import { LimitedApi } from '@shared/models/limited-api.models'; +import { HasTenantId } from '@shared/models/entity.models'; export interface Notification { readonly id: NotificationId; @@ -47,6 +49,8 @@ export interface NotificationInfo { alarmStatus?: AlarmStatus; alarmType?: string; stateEntityId?: EntityId; + acknowledged?: boolean; + cleared?: boolean; } export interface NotificationRequest extends Omit, 'label'> { @@ -112,20 +116,20 @@ export interface SlackConversation { type: string; } -export interface NotificationRule extends Omit, 'label'>, ExportableEntity { +export interface NotificationRule extends Omit, 'label'>, HasTenantId, ExportableEntity { tenantId: TenantId; enabled: boolean; templateId: NotificationTemplateId; triggerType: TriggerType; triggerConfig: NotificationRuleTriggerConfig; recipientsConfig: NotificationRuleRecipientConfig; - additionalConfig: {description: string}; + additionalConfig?: {description: string}; } export type NotificationRuleTriggerConfig = Partial; + ApiUsageLimitNotificationRuleTriggerConfig & RateLimitsNotificationRuleTriggerConfig>; export interface AlarmNotificationRuleTriggerConfig { alarmTypes?: Array; @@ -184,6 +188,10 @@ export interface ApiUsageLimitNotificationRuleTriggerConfig { notifyOn: ApiUsageStateValue[]; } +export interface RateLimitsNotificationRuleTriggerConfig { + apis: LimitedApi[]; +} + export enum ComponentLifecycleEvent { STARTED = 'STARTED', UPDATED = 'UPDATED', @@ -240,12 +248,15 @@ export interface NonConfirmedNotificationEscalation { targets: Array; } -export interface NotificationTarget extends Omit, 'label'>, ExportableEntity { +export interface NotificationTarget extends Omit, 'label'>, HasTenantId, + ExportableEntity { tenantId: TenantId; configuration: NotificationTargetConfig; } -export interface NotificationTargetConfig extends Partial { +export interface NotificationTargetConfig extends Partial { description?: string; type: NotificationTargetType; } @@ -275,17 +286,25 @@ export interface SlackNotificationTargetConfig { conversationType: SlackChanelType; conversation: SlackConversation; } + +export interface MicrosoftTeamsNotificationTargetConfig { + webhookUrl: string; + channelName: string; +} export enum NotificationTargetType { PLATFORM_USERS = 'PLATFORM_USERS', - SLACK = 'SLACK' + SLACK = 'SLACK', + MICROSOFT_TEAMS = 'MICROSOFT_TEAMS' } export const NotificationTargetTypeTranslationMap = new Map([ [NotificationTargetType.PLATFORM_USERS, 'notification.platform-users'], - [NotificationTargetType.SLACK, 'notification.delivery-method.slack'] + [NotificationTargetType.SLACK, 'notification.delivery-method.slack'], + [NotificationTargetType.MICROSOFT_TEAMS, 'notification.delivery-method.microsoft-teams'], ]); -export interface NotificationTemplate extends Omit, 'label'>, ExportableEntity { +export interface NotificationTemplate extends Omit, 'label'>, + HasTenantId, ExportableEntity { tenantId: TenantId; notificationType: NotificationType; configuration: NotificationTemplateConfig; @@ -298,15 +317,18 @@ interface NotificationTemplateConfig { } export interface DeliveryMethodNotificationTemplate extends - Partial { - body?: string; + Partial{ + body: string; enabled: boolean; method: NotificationDeliveryMethod; } interface WebDeliveryMethodNotificationTemplate { - subject?: string; + subject: string; additionalConfig: WebDeliveryMethodAdditionalConfig; } @@ -316,15 +338,17 @@ interface WebDeliveryMethodAdditionalConfig { icon: string; color: string; }; - actionButtonConfig: { - enabled: boolean; - text: string; - linkType: ActionButtonLinkType; - link?: string; - dashboardId?: string; - dashboardState?: string; - setEntityIdInState?: boolean; - }; + actionButtonConfig: NotificationButtonConfig; +} + +interface NotificationButtonConfig { + enabled: boolean; + text: string; + linkType: ActionButtonLinkType; + link?: string; + dashboardId?: string; + dashboardState?: string; + setEntityIdInState?: boolean; } interface EmailDeliveryMethodNotificationTemplate { @@ -336,6 +360,11 @@ interface SlackDeliveryMethodNotificationTemplate { conversationId: string; } +interface MicrosoftTeamsDeliveryMethodNotificationTemplate { + subject?: string; + button: NotificationButtonConfig; +} + interface MobileDeliveryMethodNotificationTemplate { subject: string; } @@ -350,6 +379,7 @@ export enum NotificationDeliveryMethod { SMS = 'SMS', EMAIL = 'EMAIL', SLACK = 'SLACK', + MICROSOFT_TEAMS = 'MICROSOFT_TEAMS', MOBILE = 'MOBILE' } @@ -358,6 +388,7 @@ export const NotificationDeliveryMethodTranslateMap = new Map([ @@ -562,6 +592,12 @@ export const NotificationTemplateTypeTranslateMap = new Map([ @@ -587,4 +624,14 @@ export const TriggerTypeTranslationMap = new Map([ [TriggerType.ENTITIES_LIMIT, 'notification.trigger.entities-limit'], [TriggerType.API_USAGE_LIMIT, 'notification.trigger.api-usage-limit'], [TriggerType.NEW_PLATFORM_VERSION, 'notification.trigger.new-platform-version'], + [TriggerType.RATE_LIMITS, 'notification.trigger.rate-limits'], ]); + +export interface NotificationUserSettings { + prefs: {[key: string]: NotificationUserSetting}; +} + +export interface NotificationUserSetting { + enabled: boolean; + enabledDeliveryMethods: {[key: string]: boolean}; +} diff --git a/ui-ngx/src/app/shared/models/ota-package.models.ts b/ui-ngx/src/app/shared/models/ota-package.models.ts index 3af7b9ae30..a53fff7b8e 100644 --- a/ui-ngx/src/app/shared/models/ota-package.models.ts +++ b/ui-ngx/src/app/shared/models/ota-package.models.ts @@ -18,6 +18,7 @@ import { BaseData } from '@shared/models/base-data'; import { TenantId } from '@shared/models/id/tenant-id'; import { OtaPackageId } from '@shared/models/id/ota-package-id'; import { DeviceProfileId } from '@shared/models/id/device-profile-id'; +import { HasTenantId } from '@shared/models/entity.models'; export enum ChecksumAlgorithm { MD5 = 'MD5', @@ -85,7 +86,7 @@ export interface OtaPagesIds { softwareId?: OtaPackageId; } -export interface OtaPackageInfo extends BaseData { +export interface OtaPackageInfo extends BaseData, HasTenantId { tenantId?: TenantId; type: OtaUpdateType; deviceProfileId?: DeviceProfileId; diff --git a/ui-ngx/src/app/shared/models/overlay.models.ts b/ui-ngx/src/app/shared/models/overlay.models.ts new file mode 100644 index 0000000000..8316a0841b --- /dev/null +++ b/ui-ngx/src/app/shared/models/overlay.models.ts @@ -0,0 +1,44 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ConnectionPositionPair } from '@angular/cdk/overlay'; + +export const POSITION_MAP: { [key: string]: ConnectionPositionPair } = { + top: new ConnectionPositionPair({ originX: 'center', originY: 'top' }, { overlayX: 'center', overlayY: 'bottom' }), + topLeft: new ConnectionPositionPair({ originX: 'start', originY: 'top' }, { overlayX: 'start', overlayY: 'bottom' }), + topRight: new ConnectionPositionPair({ originX: 'end', originY: 'top' }, { overlayX: 'end', overlayY: 'bottom' }), + right: new ConnectionPositionPair({ originX: 'end', originY: 'center' }, { overlayX: 'start', overlayY: 'center' }), + rightTop: new ConnectionPositionPair({ originX: 'end', originY: 'top' }, { overlayX: 'start', overlayY: 'top' }), + rightBottom: new ConnectionPositionPair( + { originX: 'end', originY: 'bottom' }, + { overlayX: 'start', overlayY: 'bottom' } + ), + bottom: new ConnectionPositionPair({ originX: 'center', originY: 'bottom' }, { overlayX: 'center', overlayY: 'top' }), + bottomLeft: new ConnectionPositionPair( + { originX: 'start', originY: 'bottom' }, + { overlayX: 'start', overlayY: 'top' } + ), + bottomRight: new ConnectionPositionPair({ originX: 'end', originY: 'bottom' }, { overlayX: 'end', overlayY: 'top' }), + left: new ConnectionPositionPair({ originX: 'start', originY: 'center' }, { overlayX: 'end', overlayY: 'center' }), + leftTop: new ConnectionPositionPair({ originX: 'start', originY: 'top' }, { overlayX: 'end', overlayY: 'top' }), + leftBottom: new ConnectionPositionPair( + { originX: 'start', originY: 'bottom' }, + { overlayX: 'end', overlayY: 'bottom' } + ) +}; + +export const DEFAULT_OVERLAY_POSITIONS = [POSITION_MAP.bottomLeft, POSITION_MAP.bottomRight, POSITION_MAP.topLeft, + POSITION_MAP.topRight, POSITION_MAP.left, POSITION_MAP.right]; diff --git a/ui-ngx/src/app/shared/models/page/page-link.ts b/ui-ngx/src/app/shared/models/page/page-link.ts index 586b246f1e..4d4edcac97 100644 --- a/ui-ngx/src/app/shared/models/page/page-link.ts +++ b/ui-ngx/src/app/shared/models/page/page-link.ts @@ -115,8 +115,9 @@ export class PageLink { public toQuery(): string { let query = `?pageSize=${this.pageSize}&page=${this.page}`; - if (this.textSearch && this.textSearch.length) { - const textSearch = encodeURIComponent(this.textSearch); + const textSearchParams = this.textSearch?.trim(); + if (textSearchParams?.length) { + const textSearch = encodeURIComponent(textSearchParams); query += `&textSearch=${textSearch}`; } if (this.sortOrder) { diff --git a/ui-ngx/src/app/shared/models/public-api.ts b/ui-ngx/src/app/shared/models/public-api.ts index dbc5e11213..4ff3ce5778 100644 --- a/ui-ngx/src/app/shared/models/public-api.ts +++ b/ui-ngx/src/app/shared/models/public-api.ts @@ -37,12 +37,14 @@ export * from './entity-type.models'; export * from './entity-view.models'; export * from './error.models'; export * from './event.models'; +export * from './limited-api.models'; export * from './login.models'; export * from './material.models'; export * from './notification.models'; -export * from './websocket/notification-ws.models'; export * from './websocket/websocket.models'; export * from './oauth2.models'; +export * from './ota-package.models'; +export * from './overlay.models'; export * from './queue.models'; export * from './relation.models'; export * from './resource.models'; @@ -53,7 +55,9 @@ export * from './settings.models'; export * from './tenant.model'; export * from './user.model'; export * from './user-settings.models'; +export * from './widget-settings.models'; export * from './widget.models'; export * from './widgets-bundle.model'; export * from './window-message.model'; export * from './usage.models'; +export * from './query/query.models'; diff --git a/ui-ngx/src/app/shared/models/query/query.models.ts b/ui-ngx/src/app/shared/models/query/query.models.ts index 7c978fb4c9..45081908e3 100644 --- a/ui-ngx/src/app/shared/models/query/query.models.ts +++ b/ui-ngx/src/app/shared/models/query/query.models.ts @@ -22,12 +22,21 @@ import { EntityInfo } from '@shared/models/entity.models'; import { EntityType } from '@shared/models/entity-type.models'; import { DataKey, Datasource, DatasourceType } from '@shared/models/widget.models'; import { PageData } from '@shared/models/page/page-data'; -import { isDefined, isEqual } from '@core/utils'; +import { + isArraysEqualIgnoreUndefined, + isDefined, + isDefinedAndNotNull, + isEmpty, + isEqual, + isEqualIgnoreUndefined, + isUndefinedOrNull +} from '@core/utils'; import { TranslateService } from '@ngx-translate/core'; import { AlarmInfo, AlarmSearchStatus, AlarmSeverity } from '../alarm.models'; import { Filter } from '@material-ui/icons'; import { DatePipe } from '@angular/common'; import { UserId } from '../id/user-id'; +import { Direction } from '@shared/models/page/sort-order'; export enum EntityKeyType { ATTRIBUTE = 'ATTRIBUTE', @@ -46,7 +55,10 @@ export const entityKeyTypeTranslationMap = new Map( [EntityKeyType.ATTRIBUTE, 'filter.key-type.attribute'], [EntityKeyType.TIME_SERIES, 'filter.key-type.timeseries'], [EntityKeyType.ENTITY_FIELD, 'filter.key-type.entity-field'], - [EntityKeyType.CONSTANT, 'filter.key-type.constant'] + [EntityKeyType.CONSTANT, 'filter.key-type.constant'], + [EntityKeyType.CLIENT_ATTRIBUTE, 'filter.key-type.client-attribute'], + [EntityKeyType.SERVER_ATTRIBUTE, 'filter.key-type.server-attribute'], + [EntityKeyType.SHARED_ATTRIBUTE, 'filter.key-type.shared-attribute'] ] ); @@ -688,11 +700,6 @@ export interface EntityFilter extends EntityFilters { type?: AliasFilterType; } -export enum Direction { - ASC = 'ASC', - DESC = 'DESC' -} - export interface EntityDataSortOrder { key: EntityKey; direction: Direction; @@ -721,6 +728,36 @@ export interface AlarmFilterConfig extends AlarmFilter { assignedToCurrentUser?: boolean; } +export const alarmFilterConfigEquals = (filter1?: AlarmFilterConfig, filter2?: AlarmFilterConfig): boolean => { + if (filter1 === filter2) { + return true; + } + if ((isUndefinedOrNull(filter1) || isEmpty(filter1)) && (isUndefinedOrNull(filter2) || isEmpty(filter2))) { + return true; + } else if (isDefinedAndNotNull(filter1) && isDefinedAndNotNull(filter2)) { + if (!isArraysEqualIgnoreUndefined(filter1.typeList, filter2.typeList)) { + return false; + } + if (!isArraysEqualIgnoreUndefined(filter1.statusList, filter2.statusList)) { + return false; + } + if (!isArraysEqualIgnoreUndefined(filter1.severityList, filter2.severityList)) { + return false; + } + if (!isEqualIgnoreUndefined(filter1.assigneeId, filter2.assigneeId)) { + return false; + } + if (!isEqualIgnoreUndefined(filter1.searchPropagatedAlarms, filter2.searchPropagatedAlarms)) { + return false; + } + if (!isEqualIgnoreUndefined(filter1.assignedToCurrentUser, filter2.assignedToCurrentUser)) { + return false; + } + return true; + } + return false; +}; + export type AlarmCountQuery = EntityCountQuery & AlarmFilter; export type AlarmDataPageLink = EntityDataPageLink & AlarmFilter; diff --git a/ui-ngx/src/app/shared/models/queue.models.ts b/ui-ngx/src/app/shared/models/queue.models.ts index 76ee0bf022..22ebac12f1 100644 --- a/ui-ngx/src/app/shared/models/queue.models.ts +++ b/ui-ngx/src/app/shared/models/queue.models.ts @@ -17,6 +17,7 @@ import { BaseData } from '@shared/models/base-data'; import { TenantId } from '@shared/models/id/tenant-id'; import { QueueId } from '@shared/models/id/queue-id'; +import { HasTenantId } from '@shared/models/entity.models'; export enum ServiceType { TB_CORE = 'TB_CORE', @@ -99,7 +100,7 @@ export const QueueProcessingStrategyTypesMap = new Map { +export interface QueueInfo extends BaseData, HasTenantId { generatedId?: string; name: string; packProcessingTimeout: number; @@ -121,5 +122,6 @@ export interface QueueInfo extends BaseData { topic: string; additionalInfo: { description?: string; + customProperties?: string; }; } diff --git a/ui-ngx/src/app/shared/models/resource.models.ts b/ui-ngx/src/app/shared/models/resource.models.ts index 6621f10c42..1236e19abf 100644 --- a/ui-ngx/src/app/shared/models/resource.models.ts +++ b/ui-ngx/src/app/shared/models/resource.models.ts @@ -14,9 +14,11 @@ /// limitations under the License. /// -import { BaseData } from '@shared/models/base-data'; +import { BaseData, ExportableEntity, HasId } from '@shared/models/base-data'; import { TenantId } from '@shared/models/id/tenant-id'; import { TbResourceId } from '@shared/models/id/tb-resource-id'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { HasTenantId } from '@shared/models/entity.models'; export enum ResourceType { LWM2M_MODEL = 'LWM2M_MODEL', @@ -52,20 +54,111 @@ export const ResourceTypeTranslationMap = new Map( ] ); -export interface ResourceInfo extends Omit, 'name' | 'label'> { +export interface TbResourceInfo extends Omit, 'name' | 'label'>, HasTenantId, ExportableEntity { tenantId?: TenantId; resourceKey?: string; title?: string; resourceType: ResourceType; + fileName: string; + public: boolean; + publicResourceKey?: string; + descriptor?: D; } +export type ResourceInfo = TbResourceInfo; + export interface Resource extends ResourceInfo { data: string; - fileName: string; name?: string; } -export interface Resources extends ResourceInfo { - data: Array; - fileName: Array; +export interface ImageDescriptor { + mediaType: string; + width: number; + height: number; + size: number; + etag: string; + previewDescriptor: ImageDescriptor; +} + +export interface ImageResourceInfo extends TbResourceInfo { + link?: string; + publicLink?: string; +} + +export interface ImageExportData { + mediaType: string; + fileName: string; + title: string; + resourceKey: string; + public: boolean; + publicResourceKey: string; + data: string; +} + +export type ImageResourceType = 'tenant' | 'system'; + +export type ImageReferences = {[entityType: string]: Array & HasTenantId>}; + +export interface ImageResourceInfoWithReferences extends ImageResourceInfo { + references: ImageReferences; +} + +export interface ImageDeleteResult { + image: ImageResourceInfo; + success: boolean; + imageIsReferencedError?: boolean; + error?: any; + references?: ImageReferences; } + +export const toImageDeleteResult = (image: ImageResourceInfo, e?: any): ImageDeleteResult => { + if (!e) { + return {image, success: true}; + } else { + const result: ImageDeleteResult = {image, success: false, error: e}; + if (e?.status === 400 && e?.error?.success === false && e?.error?.references) { + const references: ImageReferences = e?.error?.references; + result.imageIsReferencedError = true; + result.references = references; + } + return result; + } +}; + +export const imageResourceType = (imageInfo: ImageResourceInfo): ImageResourceType => + (!imageInfo.tenantId || imageInfo.tenantId?.id === NULL_UUID) ? 'system' : 'tenant'; + +export const TB_IMAGE_PREFIX = 'tb-image;'; + +export const IMAGES_URL_REGEXP = /\/api\/images\/(tenant|system)\/(.*)/; +export const IMAGES_URL_PREFIX = '/api/images'; + +export const PUBLIC_IMAGES_URL_PREFIX = '/api/images/public'; + +export const IMAGE_BASE64_URL_PREFIX = 'data:image/'; + +export const removeTbImagePrefix = (url: string): string => url ? url.replace(TB_IMAGE_PREFIX, '') : url; + +export const removeTbImagePrefixFromUrls = (urls: string[]): string[] => urls ? urls.map(url => removeTbImagePrefix(url)) : []; + +export const prependTbImagePrefix = (url: string): string => { + if (url && !url.startsWith(TB_IMAGE_PREFIX)) { + url = TB_IMAGE_PREFIX + url; + } + return url; +}; + +export const prependTbImagePrefixToUrls = (urls: string[]): string[] => urls ? urls.map(url => prependTbImagePrefix(url)) : []; + + +export const isImageResourceUrl = (url: string): boolean => url && IMAGES_URL_REGEXP.test(url); + +export const extractParamsFromImageResourceUrl = (url: string): {type: ImageResourceType; key: string} => { + const res = url.match(IMAGES_URL_REGEXP); + return {type: res[1] as ImageResourceType, key: res[2]}; +}; + +export const isBase64DataImageUrl = (url: string): boolean => url && url.startsWith(IMAGE_BASE64_URL_PREFIX); + +export const NO_IMAGE_DATA_URI = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='; diff --git a/ui-ngx/src/app/shared/models/rule-chain.models.ts b/ui-ngx/src/app/shared/models/rule-chain.models.ts index fbf6f57268..96fa1034c2 100644 --- a/ui-ngx/src/app/shared/models/rule-chain.models.ts +++ b/ui-ngx/src/app/shared/models/rule-chain.models.ts @@ -20,8 +20,9 @@ import { RuleChainId } from '@shared/models/id/rule-chain-id'; import { RuleNodeId } from '@shared/models/id/rule-node-id'; import { RuleNode, RuleNodeComponentDescriptor, RuleNodeType } from '@shared/models/rule-node.models'; import { ComponentClusteringMode, ComponentType } from '@shared/models/component-descriptor.models'; +import { HasTenantId } from '@shared/models/entity.models'; -export interface RuleChain extends BaseData, ExportableEntity { +export interface RuleChain extends BaseData, HasTenantId, ExportableEntity { tenantId: TenantId; name: string; firstRuleNodeId: RuleNodeId; @@ -65,6 +66,7 @@ export const unknownNodeComponent: RuleNodeComponentDescriptor = { type: RuleNodeType.UNKNOWN, name: 'unknown', clusteringMode: ComponentClusteringMode.ENABLED, + configurationVersion: 0, clazz: 'tb.internal.Unknown', configurationDescriptor: { nodeDefinition: { @@ -81,6 +83,7 @@ export const unknownNodeComponent: RuleNodeComponentDescriptor = { export const inputNodeComponent: RuleNodeComponentDescriptor = { type: RuleNodeType.INPUT, + configurationVersion: 0, clusteringMode: ComponentClusteringMode.ENABLED, name: 'Input', clazz: 'tb.internal.Input' diff --git a/ui-ngx/src/app/shared/models/rule-node.models.ts b/ui-ngx/src/app/shared/models/rule-node.models.ts index dbb065f7f2..4a40944c67 100644 --- a/ui-ngx/src/app/shared/models/rule-node.models.ts +++ b/ui-ngx/src/app/shared/models/rule-node.models.ts @@ -26,6 +26,8 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { AbstractControl, UntypedFormGroup } from '@angular/forms'; import { RuleChainType } from '@shared/models/rule-chain.models'; +import { DebugRuleNodeEventBody } from '@shared/models/event.models'; +import { TranslateService } from '@ngx-translate/core'; export interface RuleNodeConfiguration { [key: string]: any; @@ -37,6 +39,7 @@ export interface RuleNode extends BaseData { name: string; debugMode: boolean; singletonMode: boolean; + configurationVersion?: number; configuration: RuleNodeConfiguration; additionalInfo?: any; } @@ -70,10 +73,15 @@ export interface RuleNodeConfigurationDescriptor { export interface IRuleNodeConfigurationComponent { ruleNodeId: string; ruleChainId: string; + hasScript: boolean; + disabled: boolean; + testScriptLabel?: string; + changeScript?: EventEmitter; ruleChainType: RuleChainType; configuration: RuleNodeConfiguration; configurationChanged: Observable; validate(); + testScript? (debugEventBody?: DebugRuleNodeEventBody); [key: string]: any; } @@ -86,11 +94,26 @@ export abstract class RuleNodeConfigurationComponent extends PageComponent imple ruleChainId: string; + hasScript: boolean = false; + ruleChainType: RuleChainType; configurationValue: RuleNodeConfiguration; private configurationSet = false; + private disabledValue = false; + + set disabled(value: boolean) { + if (this.disabledValue !== value) { + this.disabledValue = value; + if (value) { + this.configForm().disable({emitEvent: false}); + } else { + this.configForm().enable({emitEvent: false}); + this.updateValidators(false); + } + } + }; set configuration(value: RuleNodeConfiguration) { this.configurationValue = value; @@ -304,6 +327,7 @@ export const ruleNodeTypeDescriptors = new Map; + export interface UserPasswordPolicy { minimumLength: number; + maximumLength: number; minimumUppercaseLetters: number; minimumLowercaseLetters: number; minimumDigits: number; minimumSpecialCharacters: number; passwordExpirationPeriodDays: number; allowWhitespaces: boolean; + forceUserToResetPasswordIfNotValid: boolean; } export interface SecuritySettings { diff --git a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts b/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts index 50cbef2f8b..e56941f688 100644 --- a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts +++ b/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts @@ -17,25 +17,31 @@ import { EntityType } from '@shared/models/entity-type.models'; import { AggregationType } from '../time/time.models'; -import { Observable, ReplaySubject } from 'rxjs'; +import { BehaviorSubject, Observable, ReplaySubject } from 'rxjs'; import { EntityId } from '@shared/models/id/entity-id'; import { map } from 'rxjs/operators'; import { NgZone } from '@angular/core'; import { AlarmCountQuery, AlarmData, - AlarmDataQuery, EntityCountQuery, + AlarmDataQuery, + EntityCountQuery, EntityData, - EntityDataQuery, EntityFilter, + EntityDataQuery, + EntityFilter, EntityKey, TsValue } from '@shared/models/query/query.models'; import { PageData } from '@shared/models/page/page-data'; import { alarmFields } from '@shared/models/alarm.models'; import { entityFields } from '@shared/models/entity.models'; -import { isUndefined } from '@core/utils'; -import { CmdWrapper, WsSubscriber } from '@shared/models/websocket/websocket.models'; +import { isDefinedAndNotNull, isUndefined } from '@core/utils'; +import { CmdWrapper, WsService, WsSubscriber } from '@shared/models/websocket/websocket.models'; import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service'; +import { Notification } from '@shared/models/notification.models'; +import { WebsocketService } from '@core/ws/websocket.service'; + +export const NOT_SUPPORTED = 'Not supported!'; export enum DataKeyType { timeseries = 'timeseries', @@ -56,9 +62,11 @@ export enum AttributeScope { SHARED_SCOPE = 'SHARED_SCOPE' } -export enum TelemetryFeature { - ATTRIBUTES = 'ATTRIBUTES', - TIMESERIES = 'TIMESERIES' +export enum TimeseriesDeleteStrategy { + DELETE_ALL_DATA = 'DELETE_ALL_DATA', + DELETE_ALL_DATA_EXCEPT_LATEST_VALUE = 'DELETE_ALL_DATA_EXCEPT_LATEST_VALUE', + DELETE_LATEST_VALUE = 'DELETE_LATEST_VALUE', + DELETE_ALL_DATA_FOR_TIME_PERIOD = 'DELETE_ALL_DATA_FOR_TIME_PERIOD' } export type TelemetryType = LatestTelemetry | AttributeScope; @@ -73,7 +81,7 @@ export const toTelemetryType = (val: string): TelemetryType => { export const telemetryTypeTranslations = new Map( [ - [LatestTelemetry.LATEST_TELEMETRY, 'attribute.scope-latest-telemetry'], + [LatestTelemetry.LATEST_TELEMETRY, 'attribute.scope-telemetry'], [AttributeScope.CLIENT_SCOPE, 'attribute.scope-client'], [AttributeScope.SERVER_SCOPE, 'attribute.scope-server'], [AttributeScope.SHARED_SCOPE, 'attribute.scope-shared'] @@ -89,6 +97,15 @@ export const isClientSideTelemetryType = new Map( ] ); +export const timeseriesDeleteStrategyTranslations = new Map( + [ + [TimeseriesDeleteStrategy.DELETE_ALL_DATA, 'attribute.delete-timeseries.all-data'], + [TimeseriesDeleteStrategy.DELETE_ALL_DATA_EXCEPT_LATEST_VALUE, 'attribute.delete-timeseries.all-data-except-latest-value'], + [TimeseriesDeleteStrategy.DELETE_LATEST_VALUE, 'attribute.delete-timeseries.latest-value'], + [TimeseriesDeleteStrategy.DELETE_ALL_DATA_FOR_TIME_PERIOD, 'attribute.delete-timeseries.all-data-for-time-period'] + ] +); + export interface AttributeData { lastUpdateTs?: number; key: string; @@ -104,8 +121,36 @@ export enum DataSortOrder { DESC = 'DESC' } +export enum WsCmdType { + AUTH = 'AUTH', + + ATTRIBUTES = 'ATTRIBUTES', + TIMESERIES = 'TIMESERIES', + TIMESERIES_HISTORY = 'TIMESERIES_HISTORY', + ENTITY_DATA = 'ENTITY_DATA', + ENTITY_COUNT = 'ENTITY_COUNT', + ALARM_DATA = 'ALARM_DATA', + ALARM_COUNT = 'ALARM_COUNT', + + NOTIFICATIONS = 'NOTIFICATIONS', + NOTIFICATIONS_COUNT = 'NOTIFICATIONS_COUNT', + MARK_NOTIFICATIONS_AS_READ = 'MARK_NOTIFICATIONS_AS_READ', + MARK_ALL_NOTIFICATIONS_AS_READ = 'MARK_ALL_NOTIFICATIONS_AS_READ', + + ALARM_DATA_UNSUBSCRIBE = 'ALARM_DATA_UNSUBSCRIBE', + ALARM_COUNT_UNSUBSCRIBE = 'ALARM_COUNT_UNSUBSCRIBE', + ENTITY_DATA_UNSUBSCRIBE = 'ENTITY_DATA_UNSUBSCRIBE', + ENTITY_COUNT_UNSUBSCRIBE = 'ENTITY_COUNT_UNSUBSCRIBE', + NOTIFICATIONS_UNSUBSCRIBE = 'NOTIFICATIONS_UNSUBSCRIBE' +} + export interface WebsocketCmd { cmdId: number; + type: WsCmdType; +} + +export interface AuthWsCmd { + authCmd: AuthCmd; } export interface TelemetryPluginCmd extends WebsocketCmd { @@ -119,13 +164,11 @@ export abstract class SubscriptionCmd implements TelemetryPluginCmd { entityId: string; scope?: AttributeScope; unsubscribe: boolean; - abstract getType(): TelemetryFeature; + abstract type: WsCmdType; } export class AttributesSubscriptionCmd extends SubscriptionCmd { - getType() { - return TelemetryFeature.ATTRIBUTES; - } + type = WsCmdType.ATTRIBUTES; } export class TimeseriesSubscriptionCmd extends SubscriptionCmd { @@ -134,10 +177,7 @@ export class TimeseriesSubscriptionCmd extends SubscriptionCmd { interval: number; limit: number; agg: AggregationType; - - getType() { - return TelemetryFeature.TIMESERIES; - } + type = WsCmdType.TIMESERIES; } export class GetHistoryCmd implements TelemetryPluginCmd { @@ -150,6 +190,7 @@ export class GetHistoryCmd implements TelemetryPluginCmd { interval: number; limit: number; agg: AggregationType; + type = WsCmdType.TIMESERIES_HISTORY; } export interface EntityHistoryCmd { @@ -205,6 +246,7 @@ export class EntityDataCmd implements WebsocketCmd { tsCmd?: TimeSeriesCmd; aggHistoryCmd?: AggEntityHistoryCmd; aggTsCmd?: AggTimeSeriesCmd; + type = WsCmdType.ENTITY_DATA; public isEmpty(): boolean { return !this.query && !this.historyCmd && !this.latestCmd && !this.tsCmd && !this.aggTsCmd && !this.aggHistoryCmd; @@ -214,11 +256,13 @@ export class EntityDataCmd implements WebsocketCmd { export class EntityCountCmd implements WebsocketCmd { cmdId: number; query?: EntityCountQuery; + type = WsCmdType.ENTITY_COUNT; } export class AlarmDataCmd implements WebsocketCmd { cmdId: number; query?: AlarmDataQuery; + type = WsCmdType.ALARM_DATA; public isEmpty(): boolean { return !this.query; @@ -228,50 +272,83 @@ export class AlarmDataCmd implements WebsocketCmd { export class AlarmCountCmd implements WebsocketCmd { cmdId: number; query?: AlarmCountQuery; + type = WsCmdType.ALARM_COUNT; +} + +export class UnreadCountSubCmd implements WebsocketCmd { + cmdId: number; + type = WsCmdType.NOTIFICATIONS_COUNT; +} + +export class UnreadSubCmd implements WebsocketCmd { + limit: number; + cmdId: number; + type = WsCmdType.NOTIFICATIONS; + + constructor(limit = 10) { + this.limit = limit; + } +} + +export class MarkAsReadCmd implements WebsocketCmd { + + cmdId: number; + notifications: string[]; + type = WsCmdType.MARK_NOTIFICATIONS_AS_READ; + + constructor(ids: string[]) { + this.notifications = ids; + } +} + +export class MarkAllAsReadCmd implements WebsocketCmd { + cmdId: number; + type = WsCmdType.MARK_ALL_NOTIFICATIONS_AS_READ; } export class EntityDataUnsubscribeCmd implements WebsocketCmd { cmdId: number; + type = WsCmdType.ENTITY_DATA_UNSUBSCRIBE; } export class EntityCountUnsubscribeCmd implements WebsocketCmd { cmdId: number; + type = WsCmdType.ENTITY_COUNT_UNSUBSCRIBE; } export class AlarmDataUnsubscribeCmd implements WebsocketCmd { cmdId: number; + type = WsCmdType.ALARM_DATA_UNSUBSCRIBE; } export class AlarmCountUnsubscribeCmd implements WebsocketCmd { cmdId: number; + type = WsCmdType.ALARM_COUNT_UNSUBSCRIBE; +} + +export class UnsubscribeCmd implements WebsocketCmd { + cmdId: number; + type = WsCmdType.NOTIFICATIONS_UNSUBSCRIBE; +} + +export class AuthCmd implements WebsocketCmd { + cmdId = 0; + type: WsCmdType.AUTH; + token: string; + + constructor(token: string) { + this.token = token; + } } export class TelemetryPluginCmdsWrapper implements CmdWrapper { constructor() { - this.attrSubCmds = []; - this.tsSubCmds = []; - this.historyCmds = []; - this.entityDataCmds = []; - this.entityDataUnsubscribeCmds = []; - this.alarmDataCmds = []; - this.alarmDataUnsubscribeCmds = []; - this.entityCountCmds = []; - this.entityCountUnsubscribeCmds = []; - this.alarmCountCmds = []; - this.alarmCountUnsubscribeCmds = []; - } - attrSubCmds: Array; - tsSubCmds: Array; - historyCmds: Array; - entityDataCmds: Array; - entityDataUnsubscribeCmds: Array; - alarmDataCmds: Array; - alarmDataUnsubscribeCmds: Array; - entityCountCmds: Array; - entityCountUnsubscribeCmds: Array; - alarmCountCmds: Array; - alarmCountUnsubscribeCmds: Array; + this.cmds = []; + } + + cmds: Array; + authCmd: AuthCmd; private static popCmds(cmds: Array, leftCount: number): Array { const toPublish = Math.min(cmds.length, leftCount); @@ -282,58 +359,25 @@ export class TelemetryPluginCmdsWrapper implements CmdWrapper { } } + public setAuth(token: string) { + this.authCmd = new AuthCmd(token); + } + public hasCommands(): boolean { - return this.tsSubCmds.length > 0 || - this.historyCmds.length > 0 || - this.attrSubCmds.length > 0 || - this.entityDataCmds.length > 0 || - this.entityDataUnsubscribeCmds.length > 0 || - this.alarmDataCmds.length > 0 || - this.alarmDataUnsubscribeCmds.length > 0 || - this.entityCountCmds.length > 0 || - this.entityCountUnsubscribeCmds.length > 0 || - this.alarmCountCmds.length > 0 || - this.alarmCountUnsubscribeCmds.length > 0; + return this.cmds.length > 0; } public clear() { - this.attrSubCmds.length = 0; - this.tsSubCmds.length = 0; - this.historyCmds.length = 0; - this.entityDataCmds.length = 0; - this.entityDataUnsubscribeCmds.length = 0; - this.alarmDataCmds.length = 0; - this.alarmDataUnsubscribeCmds.length = 0; - this.entityCountCmds.length = 0; - this.entityCountUnsubscribeCmds.length = 0; - this.alarmCountCmds.length = 0; - this.alarmCountUnsubscribeCmds.length = 0; + this.cmds.length = 0; } public preparePublishCommands(maxCommands: number): TelemetryPluginCmdsWrapper { const preparedWrapper = new TelemetryPluginCmdsWrapper(); - let leftCount = maxCommands; - preparedWrapper.tsSubCmds = TelemetryPluginCmdsWrapper.popCmds(this.tsSubCmds, leftCount); - leftCount -= preparedWrapper.tsSubCmds.length; - preparedWrapper.historyCmds = TelemetryPluginCmdsWrapper.popCmds(this.historyCmds, leftCount); - leftCount -= preparedWrapper.historyCmds.length; - preparedWrapper.attrSubCmds = TelemetryPluginCmdsWrapper.popCmds(this.attrSubCmds, leftCount); - leftCount -= preparedWrapper.attrSubCmds.length; - preparedWrapper.entityDataCmds = TelemetryPluginCmdsWrapper.popCmds(this.entityDataCmds, leftCount); - leftCount -= preparedWrapper.entityDataCmds.length; - preparedWrapper.entityDataUnsubscribeCmds = TelemetryPluginCmdsWrapper.popCmds(this.entityDataUnsubscribeCmds, leftCount); - leftCount -= preparedWrapper.entityDataUnsubscribeCmds.length; - preparedWrapper.alarmDataCmds = TelemetryPluginCmdsWrapper.popCmds(this.alarmDataCmds, leftCount); - leftCount -= preparedWrapper.alarmDataCmds.length; - preparedWrapper.alarmDataUnsubscribeCmds = TelemetryPluginCmdsWrapper.popCmds(this.alarmDataUnsubscribeCmds, leftCount); - leftCount -= preparedWrapper.alarmDataUnsubscribeCmds.length; - preparedWrapper.entityCountCmds = TelemetryPluginCmdsWrapper.popCmds(this.entityCountCmds, leftCount); - leftCount -= preparedWrapper.entityCountCmds.length; - preparedWrapper.entityCountUnsubscribeCmds = TelemetryPluginCmdsWrapper.popCmds(this.entityCountUnsubscribeCmds, leftCount); - leftCount -= preparedWrapper.entityCountUnsubscribeCmds.length; - preparedWrapper.alarmCountCmds = TelemetryPluginCmdsWrapper.popCmds(this.alarmCountCmds, leftCount); - leftCount -= preparedWrapper.alarmCountCmds.length; - preparedWrapper.alarmCountUnsubscribeCmds = TelemetryPluginCmdsWrapper.popCmds(this.alarmCountUnsubscribeCmds, leftCount); + if (this.authCmd) { + preparedWrapper.authCmd = this.authCmd; + this.authCmd = null; + } + preparedWrapper.cmds = TelemetryPluginCmdsWrapper.popCmds(this.cmds, maxCommands); return preparedWrapper; } } @@ -397,8 +441,22 @@ export interface AlarmCountUpdateMsg extends CmdUpdateMsg { count: number; } +export interface NotificationCountUpdateMsg extends CmdUpdateMsg { + cmdUpdateType: CmdUpdateType.NOTIFICATIONS_COUNT; + totalUnreadCount: number; + sequenceNumber: number; +} + +export interface NotificationsUpdateMsg extends CmdUpdateMsg { + cmdUpdateType: CmdUpdateType.NOTIFICATIONS; + update?: Notification; + notifications?: Notification[]; + totalUnreadCount: number; + sequenceNumber: number; +} + export type WebsocketDataMsg = AlarmDataUpdateMsg | AlarmCountUpdateMsg | - EntityDataUpdateMsg | EntityCountUpdateMsg | SubscriptionUpdateMsg; + EntityDataUpdateMsg | EntityCountUpdateMsg | SubscriptionUpdateMsg | NotificationCountUpdateMsg | NotificationsUpdateMsg; export const isEntityDataUpdateMsg = (message: WebsocketDataMsg): message is EntityDataUpdateMsg => { const updateMsg = (message as CmdUpdateMsg); @@ -420,6 +478,16 @@ export const isAlarmCountUpdateMsg = (message: WebsocketDataMsg): message is Ala return updateMsg.cmdId !== undefined && updateMsg.cmdUpdateType === CmdUpdateType.ALARM_COUNT_DATA; }; +export const isNotificationCountUpdateMsg = (message: WebsocketDataMsg): message is NotificationCountUpdateMsg => { + const updateMsg = (message as CmdUpdateMsg); + return updateMsg.cmdId !== undefined && updateMsg.cmdUpdateType === CmdUpdateType.NOTIFICATIONS_COUNT; +}; + +export const isNotificationsUpdateMsg = (message: WebsocketDataMsg): message is NotificationsUpdateMsg => { + const updateMsg = (message as CmdUpdateMsg); + return updateMsg.cmdId !== undefined && updateMsg.cmdUpdateType === CmdUpdateType.NOTIFICATIONS; +}; + export class SubscriptionUpdate implements SubscriptionUpdateMsg { subscriptionId: number; errorCode: number; @@ -609,6 +677,32 @@ export class AlarmCountUpdate extends CmdUpdate { } } +export class NotificationCountUpdate extends CmdUpdate { + totalUnreadCount: number; + sequenceNumber: number; + + constructor(msg: NotificationCountUpdateMsg) { + super(msg); + this.totalUnreadCount = msg.totalUnreadCount; + this.sequenceNumber = msg.sequenceNumber; + } +} + +export class NotificationsUpdate extends CmdUpdate { + totalUnreadCount: number; + sequenceNumber: number; + update?: Notification; + notifications?: Notification[]; + + constructor(msg: NotificationsUpdateMsg) { + super(msg); + this.totalUnreadCount = msg.totalUnreadCount; + this.sequenceNumber = msg.sequenceNumber; + this.update = msg.update; + this.notifications = msg.notifications; + } +} + export class TelemetrySubscriber extends WsSubscriber { private dataSubject = new ReplaySubject(1); @@ -771,3 +865,113 @@ export class TelemetrySubscriber extends WsSubscriber { ); } } + +export class NotificationSubscriber extends WsSubscriber { + private notificationCountSubject = new BehaviorSubject({ + cmdId: 0, + cmdUpdateType: undefined, + errorCode: 0, + errorMsg: '', + totalUnreadCount: 0, + sequenceNumber: 0 + }); + private notificationsSubject = new BehaviorSubject({ + cmdId: 0, + cmdUpdateType: undefined, + errorCode: 0, + errorMsg: '', + notifications: null, + totalUnreadCount: 0, + sequenceNumber: 0 + }); + + public messageLimit = 10; + + public notificationCount$ = this.notificationCountSubject.asObservable().pipe(map(msg => msg.totalUnreadCount)); + public notifications$ = this.notificationsSubject.asObservable().pipe(map(msg => msg.notifications )); + + public static createNotificationCountSubscription(websocketService: WebsocketService, + zone: NgZone): NotificationSubscriber { + const subscriptionCommand = new UnreadCountSubCmd(); + const subscriber = new NotificationSubscriber(websocketService, zone); + subscriber.subscriptionCommands.push(subscriptionCommand); + return subscriber; + } + + public static createNotificationsSubscription(websocketService: WebsocketService, + zone: NgZone, limit = 10): NotificationSubscriber { + const subscriptionCommand = new UnreadSubCmd(limit); + const subscriber = new NotificationSubscriber(websocketService, zone); + subscriber.messageLimit = limit; + subscriber.subscriptionCommands.push(subscriptionCommand); + return subscriber; + } + + public static createMarkAsReadCommand(websocketService: WebsocketService, + ids: string[]): NotificationSubscriber { + const subscriptionCommand = new MarkAsReadCmd(ids); + const subscriber = new NotificationSubscriber(websocketService); + subscriber.subscriptionCommands.push(subscriptionCommand); + return subscriber; + } + + public static createMarkAllAsReadCommand(websocketService: WebsocketService): NotificationSubscriber { + const subscriptionCommand = new MarkAllAsReadCmd(); + const subscriber = new NotificationSubscriber(websocketService); + subscriber.subscriptionCommands.push(subscriptionCommand); + return subscriber; + } + + constructor(private websocketService: WsService, protected zone?: NgZone) { + super(websocketService, zone); + } + + onNotificationCountUpdate(message: NotificationCountUpdate) { + const currentNotificationCount = this.notificationCountSubject.value; + if (message.sequenceNumber <= currentNotificationCount.sequenceNumber) { + return; + } + if (this.zone) { + this.zone.run( + () => { + this.notificationCountSubject.next(message); + } + ); + } else { + this.notificationCountSubject.next(message); + } + } + + public complete() { + this.notificationCountSubject.complete(); + this.notificationsSubject.complete(); + super.complete(); + } + + onNotificationsUpdate(message: NotificationsUpdate) { + const currentNotifications = this.notificationsSubject.value; + if (message.sequenceNumber <= currentNotifications.sequenceNumber) { + message.totalUnreadCount = currentNotifications.totalUnreadCount; + } + let processMessage = message; + if (isDefinedAndNotNull(currentNotifications) && message.update) { + currentNotifications.notifications.unshift(message.update); + if (currentNotifications.notifications.length > this.messageLimit) { + currentNotifications.notifications.pop(); + } + processMessage = currentNotifications; + processMessage.totalUnreadCount = message.totalUnreadCount; + } + if (this.zone) { + this.zone.run( + () => { + this.notificationsSubject.next(processMessage); + this.notificationCountSubject.next(processMessage); + } + ); + } else { + this.notificationsSubject.next(processMessage); + this.notificationCountSubject.next(processMessage); + } + } +} diff --git a/ui-ngx/src/app/shared/models/tenant.model.ts b/ui-ngx/src/app/shared/models/tenant.model.ts index 9074d599f5..b28eccce18 100644 --- a/ui-ngx/src/app/shared/models/tenant.model.ts +++ b/ui-ngx/src/app/shared/models/tenant.model.ts @@ -17,7 +17,7 @@ import { ContactBased } from '@shared/models/contact-based.model'; import { TenantId } from './id/tenant-id'; import { TenantProfileId } from '@shared/models/id/tenant-profile-id'; -import { BaseData } from '@shared/models/base-data'; +import { BaseData, ExportableEntity } from '@shared/models/base-data'; import { QueueInfo } from '@shared/models/queue.models'; export enum TenantProfileType { @@ -33,6 +33,7 @@ export interface DefaultTenantProfileConfiguration { maxRuleChains: number; maxResourcesInBytes: number; maxOtaPackagesInBytes: number; + maxResourceSize: number; transportTenantMsgRateLimit?: string; transportTenantTelemetryMsgRateLimit?: string; @@ -50,6 +51,7 @@ export interface DefaultTenantProfileConfiguration { maxTransportDataPoints: number; maxREExecutions: number; maxJSExecutions: number; + maxTbelExecutions: number; maxDPStorageDays: number; maxRuleNodeExecutionsPerMessage: number; maxEmails: number; @@ -76,6 +78,8 @@ export interface DefaultTenantProfileConfiguration { defaultStorageTtlDays: number; alarmsTtlDays: number; rpcTtlDays: number; + queueStatsTtlDays: number; + ruleEngineExceptionsTtlDays: number; } export type TenantProfileConfigurations = DefaultTenantProfileConfiguration; @@ -98,10 +102,12 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan maxRuleChains: 0, maxResourcesInBytes: 0, maxOtaPackagesInBytes: 0, + maxResourceSize: 0, maxTransportMessages: 0, maxTransportDataPoints: 0, maxREExecutions: 0, maxJSExecutions: 0, + maxTbelExecutions: 0, maxDPStorageDays: 0, maxRuleNodeExecutionsPerMessage: 0, maxEmails: 0, @@ -124,6 +130,8 @@ export function createTenantProfileConfiguration(type: TenantProfileType): Tenan defaultStorageTtlDays: 0, alarmsTtlDays: 0, rpcTtlDays: 0, + queueStatsTtlDays: 0, + ruleEngineExceptionsTtlDays: 0 }; configuration = {...defaultConfiguration, type: TenantProfileType.DEFAULT}; break; @@ -137,7 +145,7 @@ export interface TenantProfileData { queueConfiguration?: Array; } -export interface TenantProfile extends BaseData { +export interface TenantProfile extends BaseData, ExportableEntity { name: string; description?: string; default?: boolean; diff --git a/ui-ngx/src/app/shared/models/time/time.models.ts b/ui-ngx/src/app/shared/models/time/time.models.ts index fd2b4f52fa..235df14bb0 100644 --- a/ui-ngx/src/app/shared/models/time/time.models.ts +++ b/ui-ngx/src/app/shared/models/time/time.models.ts @@ -452,6 +452,15 @@ export const calculateIntervalStartTime = (interval: QuickTimeInterval, currentD case QuickTimeInterval.PREVIOUS_MONTH: currentDate.subtract(1, 'months'); return currentDate.startOf('month'); + case QuickTimeInterval.PREVIOUS_QUARTER: + currentDate.subtract(1, 'quarter'); + return currentDate.startOf('quarter'); + case QuickTimeInterval.PREVIOUS_HALF_YEAR: + if (currentDate.get('quarter') < 3) { + return currentDate.startOf('year').subtract(2, 'quarters'); + } else { + return currentDate.startOf('year'); + } case QuickTimeInterval.PREVIOUS_YEAR: currentDate.subtract(1, 'years'); return currentDate.startOf('year'); diff --git a/ui-ngx/src/app/shared/models/unit.models.ts b/ui-ngx/src/app/shared/models/unit.models.ts new file mode 100644 index 0000000000..408c0b23b9 --- /dev/null +++ b/ui-ngx/src/app/shared/models/unit.models.ts @@ -0,0 +1,47 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ResourcesService } from '@core/services/resources.service'; +import { Observable } from 'rxjs'; + +export interface Unit { + name: string; + symbol: string; + tags: string[]; +} + +export enum UnitsType { + capacity = 'capacity' +} + +export enum Units { + percent = '%', + liters = 'L' +} + +export const unitBySymbol = (_units: Array, symbol: string): Unit => _units.find(u => u.symbol === symbol); + +const searchUnitTags = (unit: Unit, searchText: string): boolean => + !!unit.tags.find(t => t.toUpperCase().includes(searchText.toUpperCase())); + +export const searchUnits = (_units: Array, searchText: string): Array => _units.filter( + u => u.symbol.toUpperCase().includes(searchText.toUpperCase()) || + u.name.toUpperCase().includes(searchText.toUpperCase()) || + searchUnitTags(u, searchText) +); + +export const getUnits = (resourcesService: ResourcesService): Observable> => + resourcesService.loadJsonResource('/assets/metadata/units.json'); diff --git a/ui-ngx/src/app/shared/models/user-settings.models.ts b/ui-ngx/src/app/shared/models/user-settings.models.ts index e39600f950..28aedc7c14 100644 --- a/ui-ngx/src/app/shared/models/user-settings.models.ts +++ b/ui-ngx/src/app/shared/models/user-settings.models.ts @@ -16,6 +16,9 @@ export interface UserSettings { openedMenuSections?: string[]; + notDisplayConnectivityAfterAddDevice?: boolean; + notDisplayInstructionsAfterAddEdge?: boolean; + includeBundleWidgetsInExport?: boolean; } export const initialUserSettings: UserSettings = { diff --git a/ui-ngx/src/app/shared/models/user.model.ts b/ui-ngx/src/app/shared/models/user.model.ts index 02a8c657f9..21c6a7506a 100644 --- a/ui-ngx/src/app/shared/models/user.model.ts +++ b/ui-ngx/src/app/shared/models/user.model.ts @@ -19,8 +19,9 @@ import { UserId } from './id/user-id'; import { CustomerId } from './id/customer-id'; import { Authority } from './authority.enum'; import { TenantId } from './id/tenant-id'; +import { HasTenantId } from '@shared/models/entity.models'; -export interface User extends BaseData { +export interface User extends BaseData, HasTenantId { tenantId: TenantId; customerId: CustomerId; email: string; diff --git a/ui-ngx/src/app/shared/models/vc.models.ts b/ui-ngx/src/app/shared/models/vc.models.ts index d1886fb1aa..b682b79600 100644 --- a/ui-ngx/src/app/shared/models/vc.models.ts +++ b/ui-ngx/src/app/shared/models/vc.models.ts @@ -30,7 +30,9 @@ export const exportableEntityTypes: Array = [ EntityType.DEVICE_PROFILE, EntityType.ASSET_PROFILE, EntityType.RULE_CHAIN, + EntityType.WIDGET_TYPE, EntityType.WIDGETS_BUNDLE, + EntityType.TB_RESOURCE, EntityType.NOTIFICATION_TEMPLATE, EntityType.NOTIFICATION_TARGET, EntityType.NOTIFICATION_RULE @@ -39,7 +41,8 @@ export const exportableEntityTypes: Array = [ export const entityTypesWithoutRelatedData: Set = new Set([ EntityType.NOTIFICATION_TEMPLATE, EntityType.NOTIFICATION_TARGET, - EntityType.NOTIFICATION_RULE + EntityType.NOTIFICATION_RULE, + EntityType.TB_RESOURCE ]); export interface VersionCreateConfig { diff --git a/ui-ngx/src/app/shared/models/websocket/notification-ws.models.ts b/ui-ngx/src/app/shared/models/websocket/notification-ws.models.ts deleted file mode 100644 index 0acbba1139..0000000000 --- a/ui-ngx/src/app/shared/models/websocket/notification-ws.models.ts +++ /dev/null @@ -1,240 +0,0 @@ -/// -/// Copyright © 2016-2023 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -import { BehaviorSubject, ReplaySubject } from 'rxjs'; -import { CmdUpdate, CmdUpdateMsg, CmdUpdateType, WebsocketCmd } from '@shared/models/telemetry/telemetry.models'; -import { map } from 'rxjs/operators'; -import { NgZone } from '@angular/core'; -import { isDefinedAndNotNull } from '@core/utils'; -import { Notification } from '@shared/models/notification.models'; -import { CmdWrapper, WsSubscriber } from '@shared/models/websocket/websocket.models'; -import { NotificationWebsocketService } from '@core/ws/notification-websocket.service'; - -export class NotificationCountUpdate extends CmdUpdate { - totalUnreadCount: number; - - constructor(msg: NotificationCountUpdateMsg) { - super(msg); - this.totalUnreadCount = msg.totalUnreadCount; - } -} - -export class NotificationsUpdate extends CmdUpdate { - totalUnreadCount: number; - update?: Notification; - notifications?: Notification[]; - - constructor(msg: NotificationsUpdateMsg) { - super(msg); - this.totalUnreadCount = msg.totalUnreadCount; - this.update = msg.update; - this.notifications = msg.notifications; - } -} - -export class NotificationSubscriber extends WsSubscriber { - private notificationCountSubject = new ReplaySubject(1); - private notificationsSubject = new BehaviorSubject({ - cmdId: 0, - cmdUpdateType: undefined, - errorCode: 0, - errorMsg: '', - notifications: [], - totalUnreadCount: 0 - }); - - public messageLimit = 10; - - public notificationCount$ = this.notificationCountSubject.asObservable().pipe(map(msg => msg.totalUnreadCount)); - public notifications$ = this.notificationsSubject.asObservable().pipe(map(msg => msg.notifications )); - - public static createNotificationCountSubscription(notificationWsService: NotificationWebsocketService, - zone: NgZone): NotificationSubscriber { - const subscriptionCommand = new UnreadCountSubCmd(); - const subscriber = new NotificationSubscriber(notificationWsService, zone); - subscriber.subscriptionCommands.push(subscriptionCommand); - return subscriber; - } - - public static createNotificationsSubscription(notificationWsService: NotificationWebsocketService, - zone: NgZone, limit = 10): NotificationSubscriber { - const subscriptionCommand = new UnreadSubCmd(limit); - const subscriber = new NotificationSubscriber(notificationWsService, zone); - subscriber.messageLimit = limit; - subscriber.subscriptionCommands.push(subscriptionCommand); - return subscriber; - } - - public static createMarkAsReadCommand(notificationWsService: NotificationWebsocketService, - ids: string[]): NotificationSubscriber { - const subscriptionCommand = new MarkAsReadCmd(ids); - const subscriber = new NotificationSubscriber(notificationWsService); - subscriber.subscriptionCommands.push(subscriptionCommand); - return subscriber; - } - - public static createMarkAllAsReadCommand(notificationWsService: NotificationWebsocketService): NotificationSubscriber { - const subscriptionCommand = new MarkAllAsReadCmd(); - const subscriber = new NotificationSubscriber(notificationWsService); - subscriber.subscriptionCommands.push(subscriptionCommand); - return subscriber; - } - - constructor(private notificationWsService: NotificationWebsocketService, protected zone?: NgZone) { - super(notificationWsService, zone); - } - - onNotificationCountUpdate(message: NotificationCountUpdate) { - if (this.zone) { - this.zone.run( - () => { - this.notificationCountSubject.next(message); - } - ); - } else { - this.notificationCountSubject.next(message); - } - } - - public complete() { - this.notificationCountSubject.complete(); - this.notificationsSubject.complete(); - super.complete(); - } - - onNotificationsUpdate(message: NotificationsUpdate) { - const currentNotifications = this.notificationsSubject.value; - let processMessage = message; - if (isDefinedAndNotNull(currentNotifications) && message.update) { - currentNotifications.notifications.unshift(message.update); - if (currentNotifications.notifications.length > this.messageLimit) { - currentNotifications.notifications.pop(); - } - processMessage = currentNotifications; - processMessage.totalUnreadCount = message.totalUnreadCount; - } - if (this.zone) { - this.zone.run( - () => { - this.notificationsSubject.next(processMessage); - this.notificationCountSubject.next(processMessage); - } - ); - } else { - this.notificationsSubject.next(processMessage); - this.notificationCountSubject.next(processMessage); - } - } -} - -export class UnreadCountSubCmd implements WebsocketCmd { - cmdId: number; -} - -export class UnreadSubCmd implements WebsocketCmd { - limit: number; - cmdId: number; - - constructor(limit = 10) { - this.limit = limit; - } -} - -export class UnsubscribeCmd implements WebsocketCmd { - cmdId: number; -} - -export class MarkAsReadCmd implements WebsocketCmd { - - cmdId: number; - notifications: string[]; - - constructor(ids: string[]) { - this.notifications = ids; - } -} - -export class MarkAllAsReadCmd implements WebsocketCmd { - cmdId: number; -} - -export interface NotificationCountUpdateMsg extends CmdUpdateMsg { - cmdUpdateType: CmdUpdateType.NOTIFICATIONS_COUNT; - totalUnreadCount: number; -} - -export interface NotificationsUpdateMsg extends CmdUpdateMsg { - cmdUpdateType: CmdUpdateType.NOTIFICATIONS; - totalUnreadCount: number; - update?: Notification; - notifications?: Notification[]; -} - -export type WebsocketNotificationMsg = NotificationCountUpdateMsg | NotificationsUpdateMsg; - -export const isNotificationCountUpdateMsg = (message: WebsocketNotificationMsg): message is NotificationCountUpdateMsg => { - const updateMsg = (message as CmdUpdateMsg); - return updateMsg.cmdId !== undefined && updateMsg.cmdUpdateType === CmdUpdateType.NOTIFICATIONS_COUNT; -}; - -export const isNotificationsUpdateMsg = (message: WebsocketNotificationMsg): message is NotificationsUpdateMsg => { - const updateMsg = (message as CmdUpdateMsg); - return updateMsg.cmdId !== undefined && updateMsg.cmdUpdateType === CmdUpdateType.NOTIFICATIONS; -}; - -export class NotificationPluginCmdWrapper implements CmdWrapper { - - constructor() { - this.unreadCountSubCmd = null; - this.unreadSubCmd = null; - this.unsubCmd = null; - this.markAsReadCmd = null; - this.markAllAsReadCmd = null; - } - - unreadCountSubCmd: UnreadCountSubCmd; - unreadSubCmd: UnreadSubCmd; - unsubCmd: UnsubscribeCmd; - markAsReadCmd: MarkAsReadCmd; - markAllAsReadCmd: MarkAllAsReadCmd; - - public hasCommands(): boolean { - return isDefinedAndNotNull(this.unreadCountSubCmd) || - isDefinedAndNotNull(this.unreadSubCmd) || - isDefinedAndNotNull(this.unsubCmd) || - isDefinedAndNotNull(this.markAsReadCmd) || - isDefinedAndNotNull(this.markAllAsReadCmd); - } - - public clear() { - this.unreadCountSubCmd = null; - this.unreadSubCmd = null; - this.unsubCmd = null; - this.markAsReadCmd = null; - this.markAllAsReadCmd = null; - } - - public preparePublishCommands(): NotificationPluginCmdWrapper { - const preparedWrapper = new NotificationPluginCmdWrapper(); - preparedWrapper.unreadCountSubCmd = this.unreadCountSubCmd || undefined; - preparedWrapper.unreadSubCmd = this.unreadSubCmd || undefined; - preparedWrapper.unsubCmd = this.unsubCmd || undefined; - preparedWrapper.markAsReadCmd = this.markAsReadCmd || undefined; - preparedWrapper.markAllAsReadCmd = this.markAllAsReadCmd || undefined; - this.clear(); - return preparedWrapper; - } -} diff --git a/ui-ngx/src/app/shared/models/websocket/websocket.models.ts b/ui-ngx/src/app/shared/models/websocket/websocket.models.ts index fc7fc3d15c..9b88692b51 100644 --- a/ui-ngx/src/app/shared/models/websocket/websocket.models.ts +++ b/ui-ngx/src/app/shared/models/websocket/websocket.models.ts @@ -25,6 +25,7 @@ export interface WsService { } export abstract class CmdWrapper { + abstract setAuth(token: string); abstract hasCommands(): boolean; abstract clear(): void; abstract preparePublishCommands(maxCommands: number): CmdWrapper; diff --git a/ui-ngx/src/app/shared/models/widget-settings.models.ts b/ui-ngx/src/app/shared/models/widget-settings.models.ts new file mode 100644 index 0000000000..5e1aa6fe9e --- /dev/null +++ b/ui-ngx/src/app/shared/models/widget-settings.models.ts @@ -0,0 +1,660 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { isDefinedAndNotNull, isNumber, isNumeric, isUndefinedOrNull, parseFunction } from '@core/utils'; +import { DataKey, Datasource, DatasourceData } from '@shared/models/widget.models'; +import { Injector } from '@angular/core'; +import { DatePipe } from '@angular/common'; +import { DateAgoPipe } from '@shared/pipe/date-ago.pipe'; +import { TranslateService } from '@ngx-translate/core'; +import { AlarmFilterConfig } from '@shared/models/query/query.models'; +import { AlarmSearchStatus } from '@shared/models/alarm.models'; +import { Observable, of } from 'rxjs'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { map } from 'rxjs/operators'; +import { DomSanitizer } from '@angular/platform-browser'; + +export type ComponentStyle = {[klass: string]: any}; + +export const cssUnits = ['px', 'em', '%', 'rem', 'pt', 'pc', 'in', 'cm', 'mm', 'ex', 'ch', 'vw', 'vh', 'vmin', 'vmax'] as const; +type cssUnitTuple = typeof cssUnits; +export type cssUnit = cssUnitTuple[number]; + +export const fontWeights = ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900'] as const; +type fontWeightTuple = typeof fontWeights; +export type fontWeight = fontWeightTuple[number]; + +export const fontWeightTranslations = new Map( + [ + ['normal', 'widgets.widget-font.font-weight-normal'], + ['bold', 'widgets.widget-font.font-weight-bold'], + ['bolder', 'widgets.widget-font.font-weight-bolder'], + ['lighter', 'widgets.widget-font.font-weight-lighter'] + ] +); + +export const fontStyles = ['normal', 'italic', 'oblique'] as const; +type fontStyleTuple = typeof fontStyles; +export type fontStyle = fontStyleTuple[number]; + +export const fontStyleTranslations = new Map( + [ + ['normal', 'widgets.widget-font.font-style-normal'], + ['italic', 'widgets.widget-font.font-style-italic'], + ['oblique', 'widgets.widget-font.font-style-oblique'] + ] +); + +export const commonFonts = ['Roboto', 'monospace', 'sans-serif', 'serif']; + +export interface Font { + size: number; + sizeUnit: cssUnit; + family: string; + weight: fontWeight; + style: fontStyle; + lineHeight: string; +} + +export enum ColorType { + constant = 'constant', + range = 'range', + function = 'function' +} + +export const colorTypeTranslations = new Map( + [ + [ColorType.constant, 'widgets.color.color-type-constant'], + [ColorType.range, 'widgets.color.color-type-range'], + [ColorType.function, 'widgets.color.color-type-function'] + ] +); + +export interface ColorRange { + from?: number; + to?: number; + color: string; +} + +export const colorRangeIncludes = (range: ColorRange, toCheck: ColorRange): boolean => { + if (isNumber(range.from) && isNumber(range.to)) { + if (isNumber(toCheck.from) && isNumber(toCheck.to)) { + return toCheck.from >= range.from && toCheck.to < range.to; + } else { + return false; + } + } else if (isNumber(range.from)) { + if (isNumber(toCheck.from)) { + return toCheck.from >= range.from; + } else { + return false; + } + } else if (isNumber(range.to)) { + if (isNumber(toCheck.to)) { + return toCheck.to < range.to; + } else { + return false; + } + } else { + return false; + } +}; + +export const filterIncludingColorRanges = (ranges: Array): Array => { + const result = [...ranges]; + let includes = true; + while (includes) { + let index = -1; + for (let i = 0; i < result.length; i++) { + const range = result[i]; + if (result.some((value, i1) => i1 !== i && colorRangeIncludes(value, range))) { + index = i; + break; + } + } + if (index > -1) { + result.splice(index, 1); + } else { + includes = false; + } + } + return result; +}; + +export const sortedColorRange = (ranges: Array): Array => ranges ? [...ranges].sort( + (a, b) => { + if (isNumber(a.from) && isNumber(a.to) && isNumber(b.from) && isNumber(b.to)) { + if (b.from >= a.from && b.to < a.to) { + return 1; + } else if (a.from >= b.from && a.to < b.to) { + return -1; + } else { + return a.from - b.from; + } + } else if (isNumber(a.from) && isNumber(b.from)) { + return a.from - b.from; + } else if (isNumber(a.to) && isNumber(b.to)) { + return a.to - b.to; + } else if (isNumber(a.from) && isUndefinedOrNull(b.from)) { + return 1; + } else if (isUndefinedOrNull(a.from) && isNumber(b.from)) { + return -1; + } else if (isNumber(a.to) && isUndefinedOrNull(b.to)) { + return 1; + } else if (isUndefinedOrNull(a.to) && isNumber(b.to)) { + return -1; + } else { + return 0; + } + } + ) : []; + +export interface ColorSettings { + type: ColorType; + color: string; + rangeList?: ColorRange[]; + colorFunction?: string; +} + +export interface TimewindowStyle { + showIcon: boolean; + icon: string; + iconSize: string; + iconPosition: 'left' | 'right'; + font?: Font; + color?: string; + displayTypePrefix?: boolean; +} + +export const defaultTimewindowStyle: TimewindowStyle = { + showIcon: true, + icon: 'query_builder', + iconSize: '24px', + iconPosition: 'left', + displayTypePrefix: true +}; + +export const constantColor = (color: string): ColorSettings => ({ + type: ColorType.constant, + color, + colorFunction: defaultColorFunction +}); + +export const defaultColorFunction = 'var temperature = value;\n' + + 'if (typeof temperature !== undefined) {\n' + + ' var percent = (temperature + 60)/120 * 100;\n' + + ' return tinycolor.mix(\'blue\', \'red\', percent).toHexString();\n' + + '}\n' + + 'return \'blue\';'; + +export const cssSizeToStrSize = (size?: number, unit?: cssUnit): string => (isDefinedAndNotNull(size) ? size + '' : '0') + (unit || 'px'); + +export const resolveCssSize = (strSize?: string): [number, cssUnit] => { + if (!strSize || !strSize.trim().length) { + return [0, 'px']; + } + let resolvedUnit: cssUnit; + let resolvedSize = strSize; + for (const unit of cssUnits) { + if (strSize.endsWith(unit)) { + resolvedUnit = unit; + break; + } + } + if (resolvedUnit) { + resolvedSize = strSize.substring(0, strSize.length - resolvedUnit.length); + } + resolvedUnit = resolvedUnit || 'px'; + let numericSize = 0; + if (isNumeric(resolvedSize)) { + numericSize = Number(resolvedSize); + } + return [numericSize, resolvedUnit]; +}; + +type ValueColorFunction = (value: any) => string; + +export abstract class ColorProcessor { + + static fromSettings(color: ColorSettings): ColorProcessor { + const settings = color || constantColor(null); + switch (settings.type) { + case ColorType.constant: + return new ConstantColorProcessor(settings); + case ColorType.range: + return new RangeColorProcessor(settings); + case ColorType.function: + return new FunctionColorProcessor(settings); + default: + return new ConstantColorProcessor(settings); + } + } + + color: string; + + protected constructor(protected settings: ColorSettings) { + this.color = settings.color; + } + + abstract update(value: any): void; + +} + +class ConstantColorProcessor extends ColorProcessor { + constructor(protected settings: ColorSettings) { + super(settings); + } + + update(value: any): void {} +} + +class RangeColorProcessor extends ColorProcessor { + + constructor(protected settings: ColorSettings) { + super(settings); + } + + update(value: any): void { + this.color = this.computeFromRange(value); + } + + private computeFromRange(value: any): string { + if (this.settings.rangeList?.length && isDefinedAndNotNull(value) && isNumeric(value)) { + const num = Number(value); + for (const range of this.settings.rangeList) { + if (RangeColorProcessor.constantRange(range) && range.from === num) { + return range.color; + } else if ((!isNumber(range.from) || num >= range.from) && (!isNumber(range.to) || num < range.to)) { + return range.color; + } + } + } + return this.settings.color; + } + + public static constantRange(range: ColorRange): boolean { + return isNumber(range.from) && isNumber(range.to) && range.from === range.to; + } +} + +class FunctionColorProcessor extends ColorProcessor { + + private readonly colorFunction: ValueColorFunction; + + constructor(protected settings: ColorSettings) { + super(settings); + this.colorFunction = parseFunction(settings.colorFunction, ['value']); + } + + update(value: any): void { + if (this.colorFunction) { + this.color = this.colorFunction(value) || this.settings.color; + } + } +} + +export interface DateFormatSettings { + format?: string; + lastUpdateAgo?: boolean; + custom?: boolean; + hideLastUpdatePrefix?: boolean; +} + +export const simpleDateFormat = (format: string): DateFormatSettings => ({ + format, + lastUpdateAgo: false, + custom: false +}); + +export const lastUpdateAgoDateFormat = (): DateFormatSettings => ({ + format: null, + lastUpdateAgo: true, + custom: false +}); + +export const customDateFormat = (format: string): DateFormatSettings => ({ + format, + lastUpdateAgo: false, + custom: true +}); + +export const dateFormats = ['MMM dd yyyy HH:mm', 'dd MMM yyyy HH:mm', 'yyyy MMM dd HH:mm', + 'MM/dd/yyyy HH:mm', 'dd/MM/yyyy HH:mm', 'yyyy/MM/dd HH:mm:ss', 'yyyy-MM-dd HH:mm:ss', 'yyyy-MM-dd HH:mm:ss.SSS'] + .map(f => simpleDateFormat(f)).concat([lastUpdateAgoDateFormat(), customDateFormat('EEE, MMMM dd, yyyy')]); + +export const compareDateFormats = (df1: DateFormatSettings, df2: DateFormatSettings): boolean => { + if (df1 === df2) { + return true; + } else if (df1 && df2) { + if (df1.lastUpdateAgo && df2.lastUpdateAgo) { + return true; + } else if (df1.custom && df2.custom) { + return true; + } else if (!df1.lastUpdateAgo && !df2.lastUpdateAgo && !df1.custom && !df2.custom) { + return df1.format === df2.format; + } + } + return false; +}; + +export abstract class DateFormatProcessor { + + static fromSettings($injector: Injector, settings: DateFormatSettings): DateFormatProcessor { + if (settings.lastUpdateAgo) { + return new LastUpdateAgoDateFormatProcessor($injector, settings); + } else { + return new SimpleDateFormatProcessor($injector, settings); + } + } + + formatted = ' '; + + protected constructor(protected $injector: Injector, + protected settings: DateFormatSettings) { + } + + abstract update(ts: string | number | Date): void; + +} + +export class SimpleDateFormatProcessor extends DateFormatProcessor { + + private datePipe: DatePipe; + + constructor(protected $injector: Injector, + protected settings: DateFormatSettings) { + super($injector, settings); + this.datePipe = $injector.get(DatePipe); + } + + update(ts: string| number | Date): void { + if (ts) { + this.formatted = this.datePipe.transform(ts, this.settings.format); + } else { + this.formatted = ' '; + } + } + +} + +export class LastUpdateAgoDateFormatProcessor extends DateFormatProcessor { + + private dateAgoPipe: DateAgoPipe; + private translate: TranslateService; + + constructor(protected $injector: Injector, + protected settings: DateFormatSettings) { + super($injector, settings); + this.dateAgoPipe = $injector.get(DateAgoPipe); + this.translate = $injector.get(TranslateService); + } + + update(ts: string| number | Date): void { + if (ts) { + const agoText = this.dateAgoPipe.transform(ts, {applyAgo: true, short: true, textPart: true}); + if (this.settings.hideLastUpdatePrefix) { + this.formatted = agoText; + } else { + this.formatted = this.translate.instant('date.last-update-n-ago-text', + {agoText}); + } + } else { + this.formatted = ' '; + } + } + +} + +export enum BackgroundType { + image = 'image', + color = 'color' +} + +export const backgroundTypeTranslations = new Map( + [ + [BackgroundType.image, 'widgets.background.background-type-image'], + [BackgroundType.color, 'widgets.background.background-type-color'] + ] +); + +export interface OverlaySettings { + enabled: boolean; + color: string; + blur: number; +} + +export interface BackgroundSettings { + type: BackgroundType; + imageUrl?: string; + color?: string; + overlay: OverlaySettings; +} + +export const iconStyle = (size: number | string, sizeUnit: cssUnit = 'px'): ComponentStyle => { + const iconSize = typeof size === 'number' ? size + sizeUnit : size; + return { + width: iconSize, + minWidth: iconSize, + height: iconSize, + fontSize: iconSize, + lineHeight: iconSize + }; +}; + +export const textStyle = (font?: Font, letterSpacing = 'normal'): ComponentStyle => { + const style: ComponentStyle = { + letterSpacing + }; + if (font?.style) { + style.fontStyle = font.style; + } + if (font?.weight) { + style.fontWeight = font.weight; + } + if (font?.lineHeight) { + style.lineHeight = font.lineHeight; + } + if (font?.size) { + style.fontSize = (font.size + (font.sizeUnit || 'px')); + } + if (font?.family) { + style.fontFamily = font.family + + (font.family !== 'Roboto' ? ', Roboto' : ''); + } + return style; +}; + +export const inlineTextStyle = (font?: Font, letterSpacing = 'normal'): ComponentStyle => { + const style: ComponentStyle = { + letterSpacing + }; + if (font?.style) { + style['font-style'] = font.style; + } + if (font?.weight) { + style['font-weight'] = font.weight; + } + if (font?.lineHeight) { + style['line-height'] = font.lineHeight; + } + if (font?.size) { + style['font-size'] = (font.size + (font.sizeUnit || 'px')); + } + if (font?.family) { + style['font-family'] = font.family + + (font.family !== 'Roboto' ? ', Roboto' : ''); + } + return style; +}; + +export const cssTextFromInlineStyle = (styleObj: { [key: string]: string | number }): string => Object.entries(styleObj) + .map(([key, value]) => `${key}: ${value}`) + .join('; '); + +export const isFontSet = (font: Font): boolean => (!!font && !!font.style && !!font.weight && !!font.size && !!font.family); + +export const isFontPartiallySet = (font: Font): boolean => (!!font && (!!font.style || !!font.weight || !!font.size || !!font.family)); + +export const validateAndUpdateBackgroundSettings = (background: BackgroundSettings): BackgroundSettings => { + if (background) { + if (background.type === BackgroundType.image && (background as any).imageBase64) { + background.imageUrl = (background as any).imageBase64; + } + if (background.type === 'imageUrl' as any) { + background.type = BackgroundType.image; + } + delete (background as any).imageBase64; + } + return background; +}; + +export const backgroundStyle = (background: BackgroundSettings, imagePipe: ImagePipe, + sanitizer: DomSanitizer, preview = false): Observable => { + background = validateAndUpdateBackgroundSettings(background); + if (background.type === BackgroundType.color) { + return of({ + background: background.color + }); + } else { + const imageUrl = background.imageUrl; + if (imageUrl) { + return imagePipe.transform(imageUrl, {asString: true, ignoreLoadingImage: true, preview}).pipe( + map((transformedUrl) => ({ + background: sanitizer.bypassSecurityTrustStyle(`url(${transformedUrl}) no-repeat 50% 50% / cover`) + })) + ); + } else { + return of({}); + } + } +}; + +export const overlayStyle = (overlay: OverlaySettings): ComponentStyle => ( + { + display: overlay.enabled ? 'block' : 'none', + background: overlay.color, + backdropFilter: `blur(${overlay.blur}px)` + } +); + +export const getDataKey = (datasources?: Datasource[], index = 0): DataKey => { + if (datasources && datasources.length) { + const dataKeys = datasources[0].dataKeys; + if (dataKeys && dataKeys.length > index) { + return dataKeys[index]; + } + } + return null; +}; + +export const updateDataKeys = (datasources: Datasource[], dataKeys: DataKey[]): void => { + if (datasources && datasources.length) { + datasources[0].dataKeys = dataKeys; + } +}; + + +export const getDataKeyByLabel = (datasources: Datasource[], label: string): DataKey => { + if (datasources && datasources.length) { + const dataKeys = datasources[0].dataKeys; + if (dataKeys && dataKeys.length) { + return dataKeys.find(k => k.label === label); + } + } + return null; +}; + +export const updateDataKeyByLabel = (datasources: Datasource[], dataKey: DataKey, label: string): void => { + if (datasources && datasources.length) { + let dataKeys = datasources[0].dataKeys; + if (!dataKeys) { + dataKeys = []; + datasources[0].dataKeys = dataKeys; + } + const existingIndex = dataKeys.findIndex(k => k.label === label || k === dataKey); + if (dataKey) { + dataKey.label = label; + if (existingIndex > -1) { + dataKeys[existingIndex] = dataKey; + } else { + dataKeys.push(dataKey); + } + } else if (existingIndex > -1) { + dataKeys.splice(existingIndex, 1); + } + } +}; + +export const getAlarmFilterConfig = (datasources?: Datasource[]): AlarmFilterConfig => { + if (datasources && datasources.length) { + const config = datasources[0].alarmFilterConfig; + if (config) { + return config; + } + } + return { statusList: [ AlarmSearchStatus.ACTIVE ] }; +}; + +export const setAlarmFilterConfig = (config: AlarmFilterConfig, datasources?: Datasource[]): void => { + if (datasources && datasources.length) { + datasources[0].alarmFilterConfig = config; + } +}; + +export const getLabel = (datasources?: Datasource[]): string => { + const dataKey = getDataKey(datasources); + if (dataKey) { + return dataKey.label; + } + return ''; +}; + +export const setLabel = (label: string, datasources?: Datasource[]): void => { + const dataKey = getDataKey(datasources); + if (dataKey) { + dataKey.label = label; + } +}; + +export const getSingleTsValue = (data: Array): [number, any] => { + if (data.length) { + const dsData = data[0]; + if (dsData.data.length) { + return dsData.data[0]; + } + } + return null; +}; + +export const getSingleTsValueByDataKey = (data: Array, dataKey: DataKey): [number, any] => { + if (data.length) { + const dsData = data.find(d => d.dataKey === dataKey); + if (dsData?.data?.length) { + return dsData.data[0]; + } + } + return null; +}; + +export const getLatestSingleTsValue = (data: Array): [number, any] => { + if (data.length) { + const dsData = data[0]; + if (dsData.data.length) { + return dsData.data[dsData.data.length - 1]; + } + } + return null; +}; diff --git a/ui-ngx/src/app/shared/models/widget.models.ts b/ui-ngx/src/app/shared/models/widget.models.ts index bfe9021926..6a6a20819e 100644 --- a/ui-ngx/src/app/shared/models/widget.models.ts +++ b/ui-ngx/src/app/shared/models/widget.models.ts @@ -14,12 +14,11 @@ /// limitations under the License. /// -import { BaseData } from '@shared/models/base-data'; +import { BaseData, ExportableEntity } from '@shared/models/base-data'; import { TenantId } from '@shared/models/id/tenant-id'; import { WidgetTypeId } from '@shared/models/id/widget-type-id'; import { AggregationType, ComparisonDuration, Timewindow } from '@shared/models/time/time.models'; import { EntityType } from '@shared/models/entity-type.models'; -import { AlarmSearchStatus, AlarmSeverity } from '@shared/models/alarm.models'; import { DataKeyType } from './telemetry/telemetry.models'; import { EntityId } from '@shared/models/id/entity-id'; import * as moment_ from 'moment'; @@ -39,7 +38,11 @@ import { AbstractControl, UntypedFormGroup } from '@angular/forms'; import { Observable } from 'rxjs'; import { Dashboard } from '@shared/models/dashboard.models'; import { IAliasController } from '@core/api/widget-api.models'; -import { isEmptyStr } from '@core/utils'; +import { isNotEmptyStr } from '@core/utils'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { ComponentStyle, Font, TimewindowStyle } from '@shared/models/widget-settings.models'; +import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { HasTenantId } from '@shared/models/entity.models'; export enum widgetType { timeseries = 'timeseries', @@ -50,14 +53,12 @@ export enum widgetType { } export interface WidgetTypeTemplate { - bundleAlias: string; - alias: string; + fullFqn: string; } export interface WidgetTypeData { name: string; icon: string; - isMdiIcon?: boolean; configHelpLinkId: string; template: WidgetTypeTemplate; } @@ -71,8 +72,7 @@ export const widgetTypesData = new Map( icon: 'timeline', configHelpLinkId: 'widgetsConfigTimeseries', template: { - bundleAlias: 'charts', - alias: 'basic_timeseries' + fullFqn: 'system.charts.basic_timeseries' } } ], @@ -83,8 +83,7 @@ export const widgetTypesData = new Map( icon: 'track_changes', configHelpLinkId: 'widgetsConfigLatest', template: { - bundleAlias: 'cards', - alias: 'attributes_card' + fullFqn: 'system.cards.attributes_card' } } ], @@ -94,10 +93,8 @@ export const widgetTypesData = new Map( name: 'widget.rpc', icon: 'mdi:developer-board', configHelpLinkId: 'widgetsConfigRpc', - isMdiIcon: true, template: { - bundleAlias: 'gpio_widgets', - alias: 'basic_gpio_control' + fullFqn: 'system.gpio_widgets.basic_gpio_control' } } ], @@ -108,8 +105,7 @@ export const widgetTypesData = new Map( icon: 'error', configHelpLinkId: 'widgetsConfigAlarm', template: { - bundleAlias: 'alarm_widgets', - alias: 'alarms_table' + fullFqn: 'system.alarm_widgets.alarms_table' } } ], @@ -120,8 +116,7 @@ export const widgetTypesData = new Map( icon: 'font_download', configHelpLinkId: 'widgetsConfigStatic', template: { - bundleAlias: 'cards', - alias: 'html_card' + fullFqn: 'system.cards.html_card' } } ] @@ -182,6 +177,12 @@ export interface WidgetTypeParameters { warnOnPageDataOverflow?: boolean; ignoreDataUpdateOnIntervalTick?: boolean; processNoDataByWidget?: boolean; + previewWidth?: string; + previewHeight?: string; + embedTitlePanel?: boolean; + hideDataSettings?: boolean; + defaultDataKeysFunction?: (configComponent: any, configData: any) => DataKey[]; + defaultLatestDataKeysFunction?: (configComponent: any, configData: any) => DataKey[]; } export interface WidgetControllerDescriptor { @@ -193,13 +194,40 @@ export interface WidgetControllerDescriptor { actionSources?: {[actionSourceId: string]: WidgetActionSource}; } -export interface BaseWidgetType extends BaseData { +export interface BaseWidgetType extends BaseData, HasTenantId { tenantId: TenantId; - bundleAlias: string; - alias: string; + fqn: string; name: string; + deprecated: boolean; } +export const fullWidgetTypeFqn = (type: BaseWidgetType): string => + ((!type.tenantId || type.tenantId?.id === NULL_UUID) ? 'system' : 'tenant') + '.' + type.fqn; + +export const widgetTypeFqn = (fullFqn: string): string => { + if (isNotEmptyStr(fullFqn)) { + const parts = fullFqn.split('.'); + if (parts.length > 1) { + const scopeQualifier = parts[0]; + if (['system', 'tenant'].includes(scopeQualifier)) { + return fullFqn.substring(scopeQualifier.length + 1); + } + } + } + return fullFqn; +}; + +export const isValidWidgetFullFqn = (fullFqn: string): boolean => { + if (isNotEmptyStr(fullFqn)) { + const parts = fullFqn.split('.'); + if (parts.length > 1) { + const scopeQualifier = parts[0]; + return ['system', 'tenant'].includes(scopeQualifier); + } + } + return false; +}; + export interface WidgetType extends BaseWidgetType { descriptor: WidgetTypeDescriptor; } @@ -207,12 +235,20 @@ export interface WidgetType extends BaseWidgetType { export interface WidgetTypeInfo extends BaseWidgetType { image: string; description: string; + tags: string[]; widgetType: widgetType; } -export interface WidgetTypeDetails extends WidgetType { +export interface WidgetTypeDetails extends WidgetType, ExportableEntity { image: string; description: string; + tags: string[]; +} + +export enum DeprecatedFilter { + ALL = 'ALL', + ACTUAL = 'ACTUAL', + DEPRECATED = 'DEPRECATED' } export enum LegendDirection { @@ -234,6 +270,8 @@ export enum LegendPosition { right = 'right' } +export const legendPositions = Object.keys(LegendPosition) as LegendPosition[]; + export const legendPositionTranslationMap = new Map( [ [ LegendPosition.top, 'position.top' ], @@ -318,6 +356,11 @@ export interface DataKey extends KeyInfo { _hash?: number; } +export enum DataKeyConfigMode { + general = 'general', + advanced = 'advanced' +} + export enum DatasourceType { function = 'function', device = 'device', @@ -369,8 +412,8 @@ export interface Datasource { export const datasourcesHasAggregation = (datasources?: Array): boolean => { if (datasources) { const foundDatasource = datasources.find(datasource => { - const found = datasource.dataKeys && datasource.dataKeys.find(key => key.type === DataKeyType.timeseries && - key.aggregationType && key.aggregationType !== AggregationType.NONE); + const found = datasource.dataKeys && datasource.dataKeys.find(key => key?.type === DataKeyType.timeseries && + key?.aggregationType && key.aggregationType !== AggregationType.NONE); return !!found; }); if (foundDatasource) { @@ -386,8 +429,8 @@ export const datasourcesHasOnlyComparisonAggregation = (datasources?: Array { - const found = datasource.dataKeys && datasource.dataKeys.find(key => key.type === DataKeyType.timeseries && - key.aggregationType && key.aggregationType !== AggregationType.NONE && !key.comparisonEnabled); + const found = datasource.dataKeys && datasource.dataKeys.find(key => key?.type === DataKeyType.timeseries && + key?.aggregationType && key.aggregationType !== AggregationType.NONE && !key.comparisonEnabled); return !!found; }); if (foundDatasource) { @@ -613,6 +656,8 @@ export enum WidgetConfigMode { export interface WidgetConfig { configMode?: WidgetConfigMode; title?: string; + titleFont?: Font; + titleColor?: string; titleIcon?: string; showTitle?: boolean; showTitleIcon?: boolean; @@ -624,6 +669,7 @@ export interface WidgetConfig { useDashboardTimewindow?: boolean; displayTimewindow?: boolean; timewindow?: Timewindow; + timewindowStyle?: TimewindowStyle; desktopHide?: boolean; mobileHide?: boolean; mobileHeight?: number; @@ -632,9 +678,10 @@ export interface WidgetConfig { backgroundColor?: string; padding?: string; margin?: string; - widgetStyle?: {[klass: string]: any}; + borderRadius?: string; + widgetStyle?: ComponentStyle; widgetCss?: string; - titleStyle?: {[klass: string]: any}; + titleStyle?: ComponentStyle; units?: string; decimals?: number; noDataDisplayMessage?: string; @@ -648,7 +695,13 @@ export interface WidgetConfig { [key: string]: any; } -export interface Widget extends WidgetInfo{ +export interface BaseWidgetInfo { + id?: string; + typeFullFqn: string; + type: widgetType; +} + +export interface Widget extends BaseWidgetInfo, ExportableEntity { typeId?: WidgetTypeId; sizeX: number; sizeY: number; @@ -657,15 +710,11 @@ export interface Widget extends WidgetInfo{ config: WidgetConfig; } -export interface WidgetInfo { - id?: string; - isSystemType: boolean; - bundleAlias: string; - typeAlias: string; - type: widgetType; +export interface WidgetInfo extends BaseWidgetInfo { title: string; image?: string; description?: string; + deprecated?: boolean; } export interface GroupInfo { @@ -700,26 +749,14 @@ export interface IWidgetSettingsComponent { aliasController: IAliasController; dashboard: Dashboard; widget: Widget; + widgetConfig: WidgetConfigComponentData; functionScopeVariables: string[]; settings: WidgetSettings; settingsChanged: Observable; - validate(); + validateSettings(): boolean; [key: string]: any; } -const removeEmptyWidgetSettings = (settings: WidgetSettings): WidgetSettings => { - if (settings) { - const keys = Object.keys(settings); - for (const key of keys) { - const val = settings[key]; - if (val === null || isEmptyStr(val)) { - delete settings[key]; - } - } - } - return settings; -}; - @Directive() // eslint-disable-next-line @angular-eslint/directive-class-suffix export abstract class WidgetSettingsComponent extends PageComponent implements @@ -731,6 +768,17 @@ export abstract class WidgetSettingsComponent extends PageComponent implements widget: Widget; + widgetConfigValue: WidgetConfigComponentData; + + set widgetConfig(value: WidgetConfigComponentData) { + this.widgetConfigValue = value; + this.onWidgetConfigSet(value); + } + + get widgetConfig(): WidgetConfigComponentData { + return this.widgetConfigValue; + } + functionScopeVariables: string[]; settingsValue: WidgetSettings; @@ -765,15 +813,15 @@ export abstract class WidgetSettingsComponent extends PageComponent implements ngOnInit() {} ngAfterViewInit(): void { - setTimeout(() => { - if (!this.validateSettings()) { - this.settingsChangedEmitter.emit(null); - } - }, 0); + if (!this.validateSettings()) { + setTimeout(() => { + this.onSettingsChanged(this.prepareOutputSettings(this.settingsForm().getRawValue())); + }, 0); + } } - validate() { - this.onValidate(); + public validateSettings(): boolean { + return this.settingsForm().valid; } protected setupSettings(settings: WidgetSettings) { @@ -789,8 +837,8 @@ export abstract class WidgetSettingsComponent extends PageComponent implements this.updateValidators(true, trigger); }); } - this.settingsForm().valueChanges.subscribe((updated: any) => { - this.onSettingsChanged(this.prepareOutputSettings(updated)); + this.settingsForm().valueChanges.subscribe(() => { + this.onSettingsChanged(this.prepareOutputSettings(this.settingsForm().getRawValue())); }); } @@ -809,12 +857,8 @@ export abstract class WidgetSettingsComponent extends PageComponent implements } protected onSettingsChanged(updated: WidgetSettings) { - this.settingsValue = removeEmptyWidgetSettings(updated); - if (this.validateSettings()) { - this.settingsChangedEmitter.emit(this.settingsValue); - } else { - this.settingsChangedEmitter.emit(null); - } + this.settingsValue = updated; + this.settingsChangedEmitter.emit(this.settingsValue); } protected doUpdateSettings(settingsForm: UntypedFormGroup, settings: WidgetSettings) { @@ -828,12 +872,6 @@ export abstract class WidgetSettingsComponent extends PageComponent implements return settings; } - protected validateSettings(): boolean { - return this.settingsForm().valid; - } - - protected onValidate() {} - protected abstract settingsForm(): UntypedFormGroup; protected abstract onSettingsSet(settings: WidgetSettings); @@ -842,4 +880,7 @@ export abstract class WidgetSettingsComponent extends PageComponent implements return {}; } + protected onWidgetConfigSet(widgetConfig: WidgetConfigComponentData) { + } + } diff --git a/ui-ngx/src/app/shared/models/widgets-bundle.model.ts b/ui-ngx/src/app/shared/models/widgets-bundle.model.ts index c7eb999221..a6418157b4 100644 --- a/ui-ngx/src/app/shared/models/widgets-bundle.model.ts +++ b/ui-ngx/src/app/shared/models/widgets-bundle.model.ts @@ -17,11 +17,13 @@ import { BaseData, ExportableEntity } from '@shared/models/base-data'; import { TenantId } from '@shared/models/id/tenant-id'; import { WidgetsBundleId } from '@shared/models/id/widgets-bundle-id'; +import { HasTenantId } from '@shared/models/entity.models'; -export interface WidgetsBundle extends BaseData, ExportableEntity { +export interface WidgetsBundle extends BaseData, HasTenantId, ExportableEntity { tenantId: TenantId; alias: string; title: string; image: string; description: string; + order: number; } diff --git a/ui-ngx/src/app/shared/models/window-message.model.ts b/ui-ngx/src/app/shared/models/window-message.model.ts index f720f2692f..5e26283b47 100644 --- a/ui-ngx/src/app/shared/models/window-message.model.ts +++ b/ui-ngx/src/app/shared/models/window-message.model.ts @@ -14,7 +14,8 @@ /// limitations under the License. /// -export type WindowMessageType = 'widgetException' | 'widgetEditModeInited' | 'widgetEditUpdated' | 'openDashboardMessage' | 'reloadUserMessage' | 'toggleDashboardLayout'; +export type WindowMessageType = 'widgetException' | 'widgetEditModeInited' | 'widgetEditUpdated' | 'openDashboardMessage' + | 'reloadUserMessage' | 'toggleDashboardLayout' | 'widgetEditModeToggle'; export interface WindowMessage { type: WindowMessageType; diff --git a/ui-ngx/src/app/shared/pipe/date-ago.pipe.ts b/ui-ngx/src/app/shared/pipe/date-ago.pipe.ts index 4fb964eae8..77482dfe4f 100644 --- a/ui-ngx/src/app/shared/pipe/date-ago.pipe.ts +++ b/ui-ngx/src/app/shared/pipe/date-ago.pipe.ts @@ -37,19 +37,21 @@ export class DateAgoPipe implements PipeTransform { } - transform(value: number, args?: any): string { + transform(value: string| number | Date, args?: any): string { if (value) { const applyAgo = !!args?.applyAgo; + const short = !!args?.short; + const textPart = !!args?.textPart; const ms = Math.floor((+new Date() - +new Date(value))); if (ms < 29 * SECOND) { // less than 30 seconds ago will show as 'Just now' - return this.translate.instant('timewindow.just-now'); + return this.translate.instant(textPart ? 'timewindow.just-now-lower' : 'timewindow.just-now'); } let counter; // eslint-disable-next-line guard-for-in for (const i in intervals) { counter = Math.floor(ms / intervals[i]); if (counter > 0) { - let res = this.translate.instant(`timewindow.${i}`, {[i]: counter}); + let res = this.translate.instant(`timewindow.${i+(short ? '-short' : '')}`, {[i]: counter}); if (applyAgo) { res += ' ' + this.translate.instant('timewindow.ago'); } diff --git a/ui-ngx/src/app/shared/pipe/highlight.pipe.ts b/ui-ngx/src/app/shared/pipe/highlight.pipe.ts index 0f8595fc3b..5d712c5b93 100644 --- a/ui-ngx/src/app/shared/pipe/highlight.pipe.ts +++ b/ui-ngx/src/app/shared/pipe/highlight.pipe.ts @@ -18,11 +18,10 @@ import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'highlight' }) export class HighlightPipe implements PipeTransform { - transform(text: string, search): string { + transform(text: string, search: string, includes = false, flags = 'i'): string { const pattern = search .replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); - const regex = new RegExp('^' + pattern, 'i'); - + const regex = new RegExp((!includes ? '^' : '') + pattern, flags); return search ? text.replace(regex, match => `${match}`) : text; } } diff --git a/ui-ngx/src/app/shared/pipe/image.pipe.ts b/ui-ngx/src/app/shared/pipe/image.pipe.ts new file mode 100644 index 0000000000..2559343f56 --- /dev/null +++ b/ui-ngx/src/app/shared/pipe/image.pipe.ts @@ -0,0 +1,70 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { NgZone, Pipe, PipeTransform } from '@angular/core'; +import { ImageService } from '@core/http/image.service'; +import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; +import { AsyncSubject, BehaviorSubject, Observable } from 'rxjs'; +import { isDefinedAndNotNull } from '@core/utils'; +import { NO_IMAGE_DATA_URI } from '@shared/models/resource.models'; + +const LOADING_IMAGE_DATA_URI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRG' + + 'LTgiPz4KPHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAw' + + 'IDIwIDIwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPgo='; + +export interface UrlHolder { + url?: string; +} + +@Pipe({ + name: 'image' +}) +export class ImagePipe implements PipeTransform { + + constructor(private imageService: ImageService, + private sanitizer: DomSanitizer, + private zone: NgZone) { } + + transform(urlData: string | UrlHolder, args?: any): Observable { + const ignoreLoadingImage = !!args?.ignoreLoadingImage; + const asString = !!args?.asString; + const emptyUrl = args?.emptyUrl || NO_IMAGE_DATA_URI; + const image$ = ignoreLoadingImage + ? new AsyncSubject() + : new BehaviorSubject(LOADING_IMAGE_DATA_URI); + const url = (typeof urlData === 'string') ? urlData : urlData?.url; + if (isDefinedAndNotNull(url)) { + const preview = !!args?.preview; + this.imageService.resolveImageUrl(url, preview, asString, emptyUrl).subscribe((imageUrl) => { + Promise.resolve().then(() => { + this.zone.run(() => { + image$.next(imageUrl); + image$.complete(); + }); + }); + }); + } else { + Promise.resolve().then(() => { + this.zone.run(() => { + image$.next(asString ? emptyUrl : this.sanitizer.bypassSecurityTrustUrl(emptyUrl)); + image$.complete(); + }); + }); + } + return image$.asObservable(); + } + +} diff --git a/ui-ngx/src/app/shared/pipe/public-api.ts b/ui-ngx/src/app/shared/pipe/public-api.ts index 9ba3be3610..f03d669345 100644 --- a/ui-ngx/src/app/shared/pipe/public-api.ts +++ b/ui-ngx/src/app/shared/pipe/public-api.ts @@ -23,3 +23,4 @@ export * from './nospace.pipe'; export * from './truncate.pipe'; export * from './file-size.pipe'; export * from './selectable-columns.pipe'; +export * from './image.pipe'; diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index 2a1bcfab87..bc03c47467 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -191,8 +191,30 @@ import { import { ColorPickerComponent } from '@shared/components/color-picker/color-picker.component'; import { ResourceAutocompleteComponent } from '@shared/components/resource/resource-autocomplete.component'; import { ShortNumberPipe } from '@shared/pipe/short-number.pipe'; -import { ToggleHeaderComponent } from '@shared/components/toggle-header.component'; +import { ToggleHeaderComponent, ToggleOption } from '@shared/components/toggle-header.component'; import { RuleChainSelectComponent } from '@shared/components/rule-chain/rule-chain-select.component'; +import { ToggleSelectComponent } from '@shared/components/toggle-select.component'; +import { UnitInputComponent } from '@shared/components/unit-input.component'; +import { MaterialIconsComponent } from '@shared/components/material-icons.component'; +import { ColorPickerPanelComponent } from '@shared/components/color-picker/color-picker-panel.component'; +import { TbIconComponent } from '@shared/components/icon.component'; +import { HintTooltipIconComponent } from '@shared/components/hint-tooltip-icon.component'; +import { StringAutocompleteComponent } from '@shared/components/string-autocomplete.component'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { ImportExportService } from '@shared/import-export/import-export.service'; +import { ImportDialogComponent } from '@shared/import-export/import-dialog.component'; +import { ImportDialogCsvComponent } from '@shared/import-export/import-dialog-csv.component'; +import { ExportWidgetsBundleDialogComponent } from '@shared/import-export/export-widgets-bundle-dialog.component'; +import { TableColumnsAssignmentComponent } from '@shared/import-export/table-columns-assignment.component'; +import { ScrollGridComponent } from '@shared/components/grid/scroll-grid.component'; +import { ImageGalleryComponent } from '@shared/components/image/image-gallery.component'; +import { UploadImageDialogComponent } from '@shared/components/image/upload-image-dialog.component'; +import { ImageDialogComponent } from '@shared/components/image/image-dialog.component'; +import { ImageReferencesComponent } from '@shared/components/image/image-references.component'; +import { ImagesInUseDialogComponent } from '@shared/components/image/images-in-use-dialog.component'; +import { GalleryImageInputComponent } from '@shared/components/image/gallery-image-input.component'; +import { MultipleGalleryImageInputComponent } from '@shared/components/image/multiple-gallery-image-input.component'; +import { EmbedImageDialogComponent } from '@shared/components/image/embed-image-dialog.component'; export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) { return markedOptionsService; @@ -210,6 +232,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) DateAgoPipe, SafePipe, ShortNumberPipe, + ImagePipe, { provide: FlowInjectionToken, useValue: Flow @@ -227,6 +250,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) { provide: SHARED_MODULE_TOKEN, useValue: SharedModule }, { provide: MatPaginatorIntl, useClass: CustomPaginatorIntl }, TbPopoverService, + ImportExportService, { provide: MAT_SELECT_CONFIG, useValue: { @@ -333,6 +357,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) TbJsonPipe, FileSizePipe, DateAgoPipe, + ImagePipe, SafePipe, ShortNumberPipe, SelectableColumnsPipe, @@ -362,9 +387,30 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) GtMdLgLayoutGapDirective, GtMdLgShowHideDirective, ColorPickerComponent, + ColorPickerPanelComponent, ResourceAutocompleteComponent, ToggleHeaderComponent, - RuleChainSelectComponent + ToggleOption, + ToggleSelectComponent, + UnitInputComponent, + StringAutocompleteComponent, + MaterialIconsComponent, + RuleChainSelectComponent, + TbIconComponent, + HintTooltipIconComponent, + ImportDialogComponent, + ImportDialogCsvComponent, + ExportWidgetsBundleDialogComponent, + TableColumnsAssignmentComponent, + ScrollGridComponent, + ImageGalleryComponent, + UploadImageDialogComponent, + ImageDialogComponent, + ImageReferencesComponent, + ImagesInUseDialogComponent, + GalleryImageInputComponent, + MultipleGalleryImageInputComponent, + EmbedImageDialogComponent ], imports: [ CommonModule, @@ -561,6 +607,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) KeyboardShortcutPipe, FileSizePipe, DateAgoPipe, + ImagePipe, SafePipe, ShortNumberPipe, SelectableColumnsPipe, @@ -590,9 +637,30 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) GtMdLgLayoutGapDirective, GtMdLgShowHideDirective, ColorPickerComponent, + ColorPickerPanelComponent, ResourceAutocompleteComponent, ToggleHeaderComponent, - RuleChainSelectComponent + ToggleOption, + ToggleSelectComponent, + UnitInputComponent, + StringAutocompleteComponent, + MaterialIconsComponent, + RuleChainSelectComponent, + TbIconComponent, + HintTooltipIconComponent, + ImportDialogComponent, + ImportDialogCsvComponent, + ExportWidgetsBundleDialogComponent, + TableColumnsAssignmentComponent, + ScrollGridComponent, + ImageGalleryComponent, + UploadImageDialogComponent, + ImageDialogComponent, + ImageReferencesComponent, + ImagesInUseDialogComponent, + GalleryImageInputComponent, + MultipleGalleryImageInputComponent, + EmbedImageDialogComponent ] }) export class SharedModule { } diff --git a/ui-ngx/src/assets/dashboard/api_usage.json b/ui-ngx/src/assets/dashboard/api_usage.json new file mode 100644 index 0000000000..b1cea42249 --- /dev/null +++ b/ui-ngx/src/assets/dashboard/api_usage.json @@ -0,0 +1,4926 @@ +{ + "title": "Api Usage", + "image": null, + "mobileHide": false, + "mobileOrder": null, + "configuration": { + "description": "", + "widgets": { + "fd6df872-2ddf-0921-3929-2e7f55062fad": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "jsExecutionApiState", + "type": "timeseries", + "label": "jsApiState", + "color": "#2196f3", + "settings": {}, + "_hash": 0.8830669138660703, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value ? value : 'ENABLED';", + "aggregationType": "NONE" + }, + { + "name": "jsExecutionLimit", + "type": "timeseries", + "label": "jsLimit", + "color": "#4caf50", + "settings": {}, + "_hash": 0.5463603803546802, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null, + "aggregationType": "NONE" + }, + { + "name": "jsExecutionCount", + "type": "timeseries", + "label": "jsCount", + "color": "#f44336", + "settings": {}, + "_hash": 0.5564241862015964, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null, + "aggregationType": "NONE" + }, + { + "name": "jsExecutionApiState", + "type": "timeseries", + "label": "cardId", + "color": "#607d8b", + "settings": {}, + "_hash": 0.051659774305067296, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return (Math.random()*100000).toFixed(0);" + }, + { + "name": "jsExecutionApiState", + "type": "timeseries", + "label": "title", + "color": "#9c27b0", + "settings": {}, + "_hash": 0.7673280949238444, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.scripts}\";", + "aggregationType": "NONE" + }, + { + "name": "jsExecutionApiState", + "type": "timeseries", + "label": "jsUnit", + "color": "#8bc34a", + "settings": {}, + "_hash": 0.7926918686567068, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.javascript}\";", + "aggregationType": "NONE" + }, + { + "name": "tbelExecutionApiState", + "type": "timeseries", + "label": "tbelApiState", + "color": "#3f51b5", + "settings": {}, + "_hash": 0.2002981454581909, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value ? value : 'ENABLED';" + }, + { + "name": "tbelExecutionLimit", + "type": "timeseries", + "label": "tbelLimit", + "color": "#ffeb3b", + "settings": {}, + "_hash": 0.5039854873031677, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "tbelExecutionCount", + "type": "timeseries", + "label": "tbelCount", + "color": "#e91e63", + "settings": {}, + "_hash": 0.9506731992087107, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "tbelExecutionApiState", + "type": "timeseries", + "label": "tbelUnit", + "color": "#ffeb3b", + "settings": {}, + "_hash": 0.3673530683177082, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.tbel}\";" + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694083375825, + "endTimeMs": 1694169775825 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "#666666", + "padding": "0", + "settings": { + "cardHtml": "
\n \n \n
\n
\n
\n
${title}
\n
\n
\n
\n
\n
\n
${jsUnit}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
${tbelUnit}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n
\n
", + "cardCss": ".card {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n}\n\n.card > img {\n height: 0;\n}\n\n.card .content {\n flex: 1; \n padding: 13px 13px 0;\n display: flex;\n box-sizing: border-box;\n}\n\n.card .content .column {\n display: flex;\n flex-direction: column; \n justify-content: space-around;\n flex: 1;\n}\n\n.card .content .title-row {\n display: flex;\n flex-direction: row;\n padding-bottom: 10px;\n}\n\n.card .title {\n flex: 1;\n font-size: 20px;\n font-weight: 400;\n color: #666666;\n}\n\n.card .state {\n text-transform: uppercase;\n font-size: 20px;\n font-weight: bold;\n}\n\n.card.enabled .state {\n color: #00B260;\n}\n\n.card.warning .state {\n color: #FFAD6F;\n}\n\n.card.disabled .state {\n color: #F73243;\n}\n\n.card .bars-row {\n flex: 1;\n display: flex;\n flex-direction: row;\n}\n\n.card .bar-column {\n flex: 1;\n display: flex;\n flex-direction: column;\n}\n\n.card .bar-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.card .bar {\n flex: 1;\n max-height: 30px;\n margin-top: 3.5px;\n margin-bottom: 4px;\n background-color: #F0F0F0;\n border: 1px solid #DADCDB;\n border-radius: 2px;\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2);\n}\n\n.card.enabled .bar {\n border-color: #00B260;\n background-color: #F0FBF7;\n}\n\n.card.warning .bar {\n border-color: #FFAD6F;\n background-color: #FFFAF6;\n}\n\n.card.disabled .bar {\n border-color: #F73243;\n background-color: #FFF0F0;\n}\n\n.card .bar .bar-fill {\n background-color: #F0F0F0;\n border-radius: 2px;\n height: 100%;\n width: 0%;\n}\n\n.card.enabled .bar-fill {\n background-color: #00C46C;\n}\n\n.card.warning .bar-fill {\n background-color: #FFD099;\n}\n\n.card.disabled .bar-fill {\n background-color: #FF9494;\n}\n\n.card .bar-labels {\n height: 20px;\n font-size: 16px;\n color: #666;\n display: flex;\n flex-direction: row;\n}\n\n.card .mat-mdc-button-base {\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.card .mdc-button__label {\n pointer-events: none;\n}\n\n.card .action-row {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n padding: 8px 0;\n}\n\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .card .title {\n font-size: 12px;\n }\n .card .state {\n font-size: 12px;\n }\n .card .unit {\n font-size: 8px;\n }\n .card .bar-labels {\n font-size: 6px;\n }\n .card .mat-mdc-button-base {\n font-size: 8px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1599px) {\n .card .title {\n font-size: 14px;\n }\n .card .state {\n font-size: 14px;\n }\n .card .unit {\n font-size: 10px;\n }\n .card .bar-labels {\n font-size: 8px;\n }\n .card .mat-mdc-button-base {\n font-size: 10px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1600px) and (max-width: 1919px) {\n .card .title {\n font-size: 16px;\n }\n .card .state {\n font-size: 16px;\n }\n .card .unit {\n font-size: 12px;\n }\n .card .bar-labels {\n font-size: 12px;\n }\n .card .mat-mdc-button-base {\n font-size: 12px;\n }\n .card .action-row {\n padding: 0;\n }\n} " + }, + "title": "JavaScript functions", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": { + "cursor": "default" + }, + "titleStyle": { + "fontSize": "20px", + "fontWeight": 400 + }, + "useDashboardTimewindow": true, + "showLegend": false, + "actions": { + "elementClick": [ + { + "name": "script_functions_details", + "icon": "insert_chart", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "openDashboardState", + "targetDashboardStateId": "script_functions", + "setEntityId": false, + "stateEntityParamName": null, + "openRightLayout": false, + "openInSeparateDialog": false, + "openInPopover": false, + "id": "d4961bea-84de-e1af-e50f-666b98d34cd5" + } + ] + }, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "displayTimewindow": true + }, + "row": 0, + "col": 0, + "id": "fd6df872-2ddf-0921-3929-2e7f55062fad", + "typeFullFqn": "system.cards.html_value_card" + }, + "7e235874-461b-e7c2-2fdd-d8762a020773": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "dbApiState", + "type": "timeseries", + "label": "apiState", + "color": "#2196f3", + "settings": {}, + "_hash": 0.8830669138660703, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value ? value : 'ENABLED';" + }, + { + "name": "storageDataPointsLimit", + "type": "timeseries", + "label": "limit", + "color": "#4caf50", + "settings": {}, + "_hash": 0.5463603803546802, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "storageDataPointsCount", + "type": "timeseries", + "label": "count", + "color": "#f44336", + "settings": {}, + "_hash": 0.5564241862015964, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "dbApiState", + "type": "timeseries", + "label": "apiStateClass", + "color": "#ffc107", + "settings": {}, + "_hash": 0.8737107059960671, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value ? value.toLowerCase() : 'enabled';" + }, + { + "name": "dbApiState", + "type": "timeseries", + "label": "cardId", + "color": "#607d8b", + "settings": {}, + "_hash": 0.051659774305067296, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return (Math.random()*100000).toFixed(0);" + }, + { + "name": "dbApiState", + "type": "timeseries", + "label": "title", + "color": "#9c27b0", + "settings": {}, + "_hash": 0.6301889725474652, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.telemetry}\";" + }, + { + "name": "dbApiState", + "type": "timeseries", + "label": "unit", + "color": "#8bc34a", + "settings": {}, + "_hash": 0.0027742924142306613, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.data-points-storage-days}\";" + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694083375826, + "endTimeMs": 1694169775826 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "#666666", + "padding": "0", + "settings": { + "cardHtml": "
\n \n \n
\n
\n
\n
${title}
\n
${apiState}
\n
\n
\n
${unit}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n
\n
", + "cardCss": ".card {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n}\n\n.card > img {\n height: 0;\n}\n\n.card .content {\n flex: 1; \n padding: 13px 13px 0;\n display: flex;\n box-sizing: border-box;\n}\n\n.card .content .column {\n display: flex;\n flex-direction: column; \n justify-content: space-around;\n flex: 1;\n}\n\n.card .content .title-row {\n display: flex;\n flex-direction: row;\n padding-bottom: 10px;\n}\n\n.card .title {\n flex: 1;\n font-size: 20px;\n font-weight: 400;\n color: #666666;\n}\n\n.card .state {\n text-transform: uppercase;\n font-size: 20px;\n font-weight: bold;\n}\n\n.card.enabled .state {\n color: #00B260;\n}\n\n.card.warning .state {\n color: #FFAD6F;\n}\n\n.card.disabled .state {\n color: #F73243;\n}\n\n.card .bar-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.card .bar {\n flex: 1;\n max-height: 30px;\n margin-top: 3.5px;\n margin-bottom: 4px;\n background-color: #F0F0F0;\n border: 1px solid #DADCDB;\n border-radius: 2px;\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2);\n}\n\n.card.enabled .bar {\n border-color: #00B260;\n background-color: #F0FBF7;\n}\n\n.card.warning .bar {\n border-color: #FFAD6F;\n background-color: #FFFAF6;\n}\n\n.card.disabled .bar {\n border-color: #F73243;\n background-color: #FFF0F0;\n}\n\n.card .bar .bar-fill {\n background-color: #F0F0F0;\n border-radius: 2px;\n height: 100%;\n width: 0%;\n}\n\n.card.enabled .bar-fill {\n background-color: #00C46C;\n}\n\n.card.warning .bar-fill {\n background-color: #FFD099;\n}\n\n.card.disabled .bar-fill {\n background-color: #FF9494;\n}\n\n.card .bar-labels {\n height: 20px;\n font-size: 16px;\n color: #666;\n display: flex;\n flex-direction: row;\n}\n\n\n.card .mat-mdc-button-base {\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.card .mdc-button__label {\n pointer-events: none;\n}\n\n.card .action-row {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n padding: 8px 0;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .card .title {\n font-size: 12px;\n }\n .card .state {\n font-size: 12px;\n }\n .card .unit {\n font-size: 8px;\n }\n .card .bar-labels {\n font-size: 8px;\n }\n .card .mat-mdc-button-base {\n font-size: 8px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1599px) {\n .card .title {\n font-size: 14px;\n }\n .card .state {\n font-size: 14px;\n }\n .card .unit {\n font-size: 10px;\n }\n .card .bar-labels {\n font-size: 10px;\n }\n .card .mat-mdc-button-base {\n font-size: 10px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1600px) and (max-width: 1919px) {\n .card .title {\n font-size: 16px;\n }\n .card .state {\n font-size: 16px;\n }\n .card .unit {\n font-size: 12px;\n }\n .card .bar-labels {\n font-size: 12px;\n }\n .card .mat-mdc-button-base {\n font-size: 12px;\n }\n .card .action-row {\n padding: 0;\n }\n} \n\n\n" + }, + "title": "Telemetry persistence", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": { + "cursor": "default" + }, + "titleStyle": { + "fontSize": "20px", + "fontWeight": 400 + }, + "useDashboardTimewindow": true, + "showLegend": false, + "actions": { + "elementClick": [ + { + "name": "telemetry_persistence_details", + "icon": "insert_chart", + "type": "openDashboardState", + "targetDashboardStateId": "telemetry_persistence", + "setEntityId": false, + "stateEntityParamName": null, + "openRightLayout": false, + "id": "6248831c-5b3f-8879-8548-afcf43f10610" + } + ] + }, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "7e235874-461b-e7c2-2fdd-d8762a020773", + "typeFullFqn": "system.cards.html_value_card" + }, + "08545554-a0e8-05c7-66df-6000cfeff8a4": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "ruleEngineApiState", + "type": "timeseries", + "label": "apiState", + "color": "#2196f3", + "settings": {}, + "_hash": 0.8830669138660703, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value ? value : 'ENABLED';" + }, + { + "name": "ruleEngineExecutionLimit", + "type": "timeseries", + "label": "limit", + "color": "#4caf50", + "settings": {}, + "_hash": 0.5463603803546802, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "ruleEngineExecutionCount", + "type": "timeseries", + "label": "count", + "color": "#f44336", + "settings": {}, + "_hash": 0.5564241862015964, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "ruleEngineApiState", + "type": "timeseries", + "label": "apiStateClass", + "color": "#ffc107", + "settings": {}, + "_hash": 0.8737107059960671, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value ? value.toLowerCase() : 'enabled';" + }, + { + "name": "ruleEngineApiState", + "type": "timeseries", + "label": "cardId", + "color": "#607d8b", + "settings": {}, + "_hash": 0.051659774305067296, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return (Math.random()*100000).toFixed(0);" + }, + { + "name": "ruleEngineApiState", + "type": "timeseries", + "label": "title", + "color": "#9c27b0", + "settings": {}, + "_hash": 0.3551317421302518, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.rule-engine}\";" + }, + { + "name": "ruleEngineApiState", + "type": "timeseries", + "label": "unit", + "color": "#8bc34a", + "settings": {}, + "_hash": 0.5100381746798048, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.executions}\";" + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694083375826, + "endTimeMs": 1694169775826 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "#666666", + "padding": "0", + "settings": { + "cardCss": ".card {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n}\n\n.card > img {\n height: 0;\n}\n\n.card .content {\n flex: 1; \n padding: 13px 13px 0;\n display: flex;\n box-sizing: border-box;\n}\n\n.card .content .column {\n display: flex;\n flex-direction: column; \n justify-content: space-around;\n flex: 1;\n}\n\n.card .content .title-row {\n display: flex;\n flex-direction: row;\n padding-bottom: 10px;\n}\n\n.card .title {\n flex: 1;\n font-size: 20px;\n font-weight: 400;\n color: #666666;\n}\n\n.card .state {\n text-transform: uppercase;\n font-size: 20px;\n font-weight: bold;\n}\n\n.card.enabled .state {\n color: #00B260;\n}\n\n.card.warning .state {\n color: #FFAD6F;\n}\n\n.card.disabled .state {\n color: #F73243;\n}\n\n.card .bar-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.card .bar {\n flex: 1;\n max-height: 30px;\n margin-top: 3.5px;\n margin-bottom: 4px;\n background-color: #F0F0F0;\n border: 1px solid #DADCDB;\n border-radius: 2px;\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2);\n}\n\n.card.enabled .bar {\n border-color: #00B260;\n background-color: #F0FBF7;\n}\n\n.card.warning .bar {\n border-color: #FFAD6F;\n background-color: #FFFAF6;\n}\n\n.card.disabled .bar {\n border-color: #F73243;\n background-color: #FFF0F0;\n}\n\n.card .bar .bar-fill {\n background-color: #F0F0F0;\n border-radius: 2px;\n height: 100%;\n width: 0%;\n}\n\n.card.enabled .bar-fill {\n background-color: #00C46C;\n}\n\n.card.warning .bar-fill {\n background-color: #FFD099;\n}\n\n.card.disabled .bar-fill {\n background-color: #FF9494;\n}\n\n.card .bar-labels {\n height: 20px;\n font-size: 16px;\n color: #666;\n display: flex;\n flex-direction: row;\n}\n\n\n.card .mat-mdc-button-base {\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.card .mdc-button__label {\n pointer-events: none;\n}\n\n.card .action-row {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n padding: 8px 0;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .card .title {\n font-size: 12px;\n }\n .card .state {\n font-size: 12px;\n }\n .card .unit {\n font-size: 8px;\n }\n .card .bar-labels {\n font-size: 8px;\n }\n .card .mat-mdc-button-base {\n font-size: 8px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1599px) {\n .card .title {\n font-size: 14px;\n }\n .card .state {\n font-size: 14px;\n }\n .card .unit {\n font-size: 10px;\n }\n .card .bar-labels {\n font-size: 10px;\n }\n .card .mat-mdc-button-base {\n font-size: 10px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1600px) and (max-width: 1919px) {\n .card .title {\n font-size: 16px;\n }\n .card .state {\n font-size: 16px;\n }\n .card .unit {\n font-size: 12px;\n }\n .card .bar-labels {\n font-size: 12px;\n }\n .card .mat-mdc-button-base {\n font-size: 12px;\n }\n .card .action-row {\n padding: 0;\n }\n} \n\n\n", + "cardHtml": "
\n \n \n
\n
\n
\n
${title}
\n
${apiState}
\n
\n
\n
${unit}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n
\n
" + }, + "title": "Rule Engine execution", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": { + "cursor": "default" + }, + "titleStyle": { + "fontSize": "20px", + "fontWeight": 400 + }, + "useDashboardTimewindow": true, + "showLegend": false, + "actions": { + "elementClick": [ + { + "name": "rule_engine_execution_details", + "icon": "insert_chart", + "type": "openDashboardState", + "targetDashboardStateId": "rule_engine_execution", + "setEntityId": false, + "stateEntityParamName": null, + "openRightLayout": false, + "id": "3c30248f-0cd8-fb97-a917-bc1e09984a79" + }, + { + "name": "rule_engine_statistics_details", + "icon": "show_chart", + "type": "openDashboardState", + "targetDashboardStateId": "rule_engine_statistics", + "setEntityId": false, + "stateEntityParamName": null, + "openRightLayout": false, + "id": "04e4565a-9e24-23df-f376-f2ec70a8165f" + } + ] + }, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "08545554-a0e8-05c7-66df-6000cfeff8a4", + "typeFullFqn": "system.cards.html_value_card" + }, + "a245c67e-53ec-d299-fa89-69fe2062ccb2": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "transportApiState", + "type": "timeseries", + "label": "apiState", + "color": "#2196f3", + "settings": {}, + "_hash": 0.8830669138660703, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value ? value : 'ENABLED';" + }, + { + "name": "transportMsgLimit", + "type": "timeseries", + "label": "limit", + "color": "#4caf50", + "settings": {}, + "_hash": 0.5463603803546802, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "transportMsgCount", + "type": "timeseries", + "label": "count", + "color": "#f44336", + "settings": {}, + "_hash": 0.5564241862015964, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "transportApiState", + "type": "timeseries", + "label": "apiStateClass", + "color": "#ffc107", + "settings": {}, + "_hash": 0.8737107059960671, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value ? value.toLowerCase() : 'enabled';" + }, + { + "name": "transportApiState", + "type": "timeseries", + "label": "cardId", + "color": "#607d8b", + "settings": {}, + "_hash": 0.051659774305067296, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return (Math.random()*100000).toFixed(0);" + }, + { + "name": "transportDataPointsLimit", + "type": "timeseries", + "label": "pointsLimit", + "color": "#9c27b0", + "settings": {}, + "_hash": 0.22082255831864894, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "transportDataPointsCount", + "type": "timeseries", + "label": "pointsCount", + "color": "#8bc34a", + "settings": {}, + "_hash": 0.6340356364819146, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "transportApiState", + "type": "timeseries", + "label": "title", + "color": "#3f51b5", + "settings": {}, + "_hash": 0.6894070537030252, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.transport}\";" + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694083375826, + "endTimeMs": 1694169775826 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "#666666", + "padding": "0", + "settings": { + "cardHtml": "
\n \n \n
\n
\n
\n
\n ${title}\n
\n
${apiState}
\n
\n
\n
\n
\n
{i18n:api-usage.messages}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
{i18n:api-usage.data-points}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n
\n
", + "cardCss": ".card {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n}\n\n.card > img {\n height: 0;\n}\n\n.card .content {\n flex: 1; \n padding: 13px 13px 0;\n display: flex;\n box-sizing: border-box;\n}\n\n.card .content .column {\n display: flex;\n flex-direction: column; \n justify-content: space-around;\n flex: 1;\n}\n\n.card .content .title-row {\n display: flex;\n flex-direction: row;\n padding-bottom: 10px;\n}\n\n.card .title {\n flex: 1;\n font-size: 20px;\n font-weight: 400;\n color: #666666;\n}\n\n.card .state {\n text-transform: uppercase;\n font-size: 20px;\n font-weight: bold;\n}\n\n.card.enabled .state {\n color: #00B260;\n}\n\n.card.warning .state {\n color: #FFAD6F;\n}\n\n.card.disabled .state {\n color: #F73243;\n}\n\n.card .bars-row {\n flex: 1;\n display: flex;\n flex-direction: row;\n}\n\n.card .bar-column {\n flex: 1;\n display: flex;\n flex-direction: column;\n}\n\n.card .bar-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.card .bar {\n flex: 1;\n max-height: 30px;\n margin-top: 3.5px;\n margin-bottom: 4px;\n background-color: #F0F0F0;\n border: 1px solid #DADCDB;\n border-radius: 2px;\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2);\n}\n\n.card.enabled .bar {\n border-color: #00B260;\n background-color: #F0FBF7;\n}\n\n.card.warning .bar {\n border-color: #FFAD6F;\n background-color: #FFFAF6;\n}\n\n.card.disabled .bar {\n border-color: #F73243;\n background-color: #FFF0F0;\n}\n\n.card .bar .bar-fill {\n background-color: #F0F0F0;\n border-radius: 2px;\n height: 100%;\n width: 0%;\n}\n\n.card.enabled .bar-fill {\n background-color: #00C46C;\n}\n\n.card.warning .bar-fill {\n background-color: #FFD099;\n}\n\n.card.disabled .bar-fill {\n background-color: #FF9494;\n}\n\n.card .bar-labels {\n height: 20px;\n font-size: 16px;\n color: #666;\n display: flex;\n flex-direction: row;\n}\n\n.card .mat-mdc-button-base {\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.card .mdc-button__label {\n pointer-events: none;\n}\n\n.card .action-row {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n padding: 8px 0;\n}\n\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .card .title {\n font-size: 12px;\n }\n .card .state {\n font-size: 12px;\n }\n .card .unit {\n font-size: 8px;\n }\n .card .bar-labels {\n font-size: 6px;\n }\n .card .mat-mdc-button-base {\n font-size: 8px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1599px) {\n .card .title {\n font-size: 14px;\n }\n .card .state {\n font-size: 14px;\n }\n .card .unit {\n font-size: 10px;\n }\n .card .bar-labels {\n font-size: 8px;\n }\n .card .mat-mdc-button-base {\n font-size: 10px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1600px) and (max-width: 1919px) {\n .card .title {\n font-size: 16px;\n }\n .card .state {\n font-size: 16px;\n }\n .card .unit {\n font-size: 12px;\n }\n .card .bar-labels {\n font-size: 12px;\n }\n .card .mat-mdc-button-base {\n font-size: 12px;\n }\n .card .action-row {\n padding: 0;\n }\n} \n\n" + }, + "title": "Transport", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": { + "cursor": "default" + }, + "titleStyle": { + "fontSize": "20px", + "fontWeight": 400 + }, + "useDashboardTimewindow": true, + "showLegend": false, + "actions": { + "elementClick": [ + { + "name": "transport_details", + "icon": "insert_chart", + "type": "openDashboardState", + "targetDashboardStateId": "transport", + "setEntityId": false, + "stateEntityParamName": null, + "openRightLayout": false, + "id": "46b7cefe-e1f2-67c1-4055-3a214520f869" + } + ] + }, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "a245c67e-53ec-d299-fa89-69fe2062ccb2", + "typeFullFqn": "system.cards.html_value_card" + }, + "a151ae60-0326-6116-d818-9070dda8e9c7": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "alarmApiState", + "type": "timeseries", + "label": "apiState", + "color": "#2196f3", + "settings": {}, + "_hash": 0.8830669138660703, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value ? value : 'ENABLED';" + }, + { + "name": "createdAlarmsLimit", + "type": "timeseries", + "label": "limit", + "color": "#4caf50", + "settings": {}, + "_hash": 0.5463603803546802, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "createdAlarmsCount", + "type": "timeseries", + "label": "count", + "color": "#f44336", + "settings": {}, + "_hash": 0.5564241862015964, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "alarmApiState", + "type": "timeseries", + "label": "apiStateClass", + "color": "#ffc107", + "settings": {}, + "_hash": 0.8737107059960671, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value ? value.toLowerCase() : 'enabled';" + }, + { + "name": "alarmApiState", + "type": "timeseries", + "label": "cardId", + "color": "#607d8b", + "settings": {}, + "_hash": 0.051659774305067296, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return (Math.random()*100000).toFixed(0);" + }, + { + "name": "alarmApiState", + "type": "timeseries", + "label": "title", + "color": "#9c27b0", + "settings": {}, + "_hash": 0.43439375716502227, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.alarm}\";" + }, + { + "name": "alarmApiState", + "type": "timeseries", + "label": "unit", + "color": "#8bc34a", + "settings": {}, + "_hash": 0.9964061963495883, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.alarms-created}\";" + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694083375826, + "endTimeMs": 1694169775826 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "#666666", + "padding": "0", + "settings": { + "cardHtml": "
\n \n \n
\n
\n
\n
${title}
\n
${apiState}
\n
\n
\n
${unit}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n
\n
", + "cardCss": ".card {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n}\n\n.card > img {\n height: 0;\n}\n\n.card .content {\n flex: 1; \n padding: 13px 13px 0;\n display: flex;\n box-sizing: border-box;\n}\n\n.card .content .column {\n display: flex;\n flex-direction: column; \n justify-content: space-around;\n flex: 1;\n}\n\n.card .content .title-row {\n display: flex;\n flex-direction: row;\n padding-bottom: 10px;\n}\n\n.card .title {\n flex: 1;\n font-size: 20px;\n font-weight: 400;\n color: #666666;\n}\n\n.card .state {\n text-transform: uppercase;\n font-size: 20px;\n font-weight: bold;\n}\n\n.card.enabled .state {\n color: #00B260;\n}\n\n.card.warning .state {\n color: #FFAD6F;\n}\n\n.card.disabled .state {\n color: #F73243;\n}\n\n.card .bar-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.card .bar {\n flex: 1;\n max-height: 30px;\n margin-top: 3.5px;\n margin-bottom: 4px;\n background-color: #F0F0F0;\n border: 1px solid #DADCDB;\n border-radius: 2px;\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2);\n}\n\n.card.enabled .bar {\n border-color: #00B260;\n background-color: #F0FBF7;\n}\n\n.card.warning .bar {\n border-color: #FFAD6F;\n background-color: #FFFAF6;\n}\n\n.card.disabled .bar {\n border-color: #F73243;\n background-color: #FFF0F0;\n}\n\n.card .bar .bar-fill {\n background-color: #F0F0F0;\n border-radius: 2px;\n height: 100%;\n width: 0%;\n}\n\n.card.enabled .bar-fill {\n background-color: #00C46C;\n}\n\n.card.warning .bar-fill {\n background-color: #FFD099;\n}\n\n.card.disabled .bar-fill {\n background-color: #FF9494;\n}\n\n.card .bar-labels {\n height: 20px;\n font-size: 16px;\n color: #666;\n display: flex;\n flex-direction: row;\n}\n\n\n.card .mat-mdc-button-base {\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.card .mdc-button__label {\n pointer-events: none;\n}\n\n.card .action-row {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n padding: 8px 0;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .card .title {\n font-size: 12px;\n }\n .card .state {\n font-size: 12px;\n }\n .card .unit {\n font-size: 8px;\n }\n .card .bar-labels {\n font-size: 8px;\n }\n .card .mat-mdc-button-base {\n font-size: 8px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1599px) {\n .card .title {\n font-size: 14px;\n }\n .card .state {\n font-size: 14px;\n }\n .card .unit {\n font-size: 10px;\n }\n .card .bar-labels {\n font-size: 10px;\n }\n .card .mat-mdc-button-base {\n font-size: 10px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1600px) and (max-width: 1919px) {\n .card .title {\n font-size: 16px;\n }\n .card .state {\n font-size: 16px;\n }\n .card .unit {\n font-size: 12px;\n }\n .card .bar-labels {\n font-size: 12px;\n }\n .card .mat-mdc-button-base {\n font-size: 12px;\n }\n .card .action-row {\n padding: 0;\n }\n} \n\n\n" + }, + "title": "Alarm created", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": { + "cursor": "default" + }, + "titleStyle": { + "fontSize": "20px", + "fontWeight": 400 + }, + "useDashboardTimewindow": true, + "showLegend": false, + "actions": { + "elementClick": [ + { + "name": "email_messages_details", + "icon": "insert_chart", + "type": "openDashboardState", + "targetDashboardStateId": "alarms_created", + "setEntityId": false, + "stateEntityParamName": null, + "openInSeparateDialog": null, + "dialogTitle": null, + "dialogHideDashboardToolbar": true, + "dialogWidth": null, + "dialogHeight": null, + "openRightLayout": false, + "id": "946ba769-84ac-1507-6baa-94701de8967b" + } + ] + }, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "a151ae60-0326-6116-d818-9070dda8e9c7", + "typeFullFqn": "system.cards.html_value_card" + }, + "68e16e98-0420-f72c-4848-41dedffd3904": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "ruleEngineExecutionCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.rule-engine-executions}", + "color": "#ab00ff", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 0, + "realtime": { + "timewindowMs": 86400000, + "interval": 3600000 + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "shadowSize": 4, + "fontColor": "#545454", + "fontSize": 10, + "xaxis": { + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "showLabels": true, + "color": "#545454", + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "min": 0, + "tickDecimals": 0 + }, + "grid": { + "color": "#545454", + "tickColor": "#DDDDDD", + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1 + }, + "stack": false, + "tooltipIndividual": false, + "defaultBarWidth": 2400000, + "barAlignment": "left", + "timeForComparison": "months", + "xaxisSecond": { + "axisPosition": "top", + "showLabels": true + }, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true + } + }, + "title": "{i18n:api-usage.rule-engine-hourly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": {}, + "useDashboardTimewindow": false, + "actions": { + "headerButton": [ + { + "name": "{i18n:api-usage.view-statistics}", + "icon": "show_chart", + "type": "openDashboardState", + "targetDashboardStateId": "rule_engine_statistics", + "setEntityId": false, + "stateEntityParamName": null, + "openRightLayout": false, + "id": "f9f08190-9ed9-d802-5b7a-c57ff84b5648" + }, + { + "name": "{i18n:api-usage.view-details}", + "icon": "insert_chart", + "type": "openDashboardState", + "targetDashboardStateId": "rule_engine_execution", + "setEntityId": false, + "stateEntityParamName": null, + "openRightLayout": false, + "id": "1aec196b-44ba-ddf4-c4dc-c3f60c1eb6fc" + } + ] + }, + "displayTimewindow": true, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "68e16e98-0420-f72c-4848-41dedffd3904", + "typeFullFqn": "system.charts.timeseries_bars_flot" + }, + "2aa6b499-6e27-b315-6833-89c4d58485ce": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "transportMsgCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.transport-messages}", + "color": "#2196f3", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "transportDataPointsCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.transport-data-points}", + "color": "#4caf50", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.46849996721308895, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 0, + "realtime": { + "timewindowMs": 86400000, + "interval": 3600000 + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "shadowSize": 4, + "fontColor": "#545454", + "fontSize": 10, + "xaxis": { + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "showLabels": true, + "color": "#545454", + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "min": 0, + "tickDecimals": 0 + }, + "grid": { + "color": "#545454", + "tickColor": "#DDDDDD", + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1 + }, + "stack": true, + "tooltipIndividual": false, + "defaultBarWidth": 2400000, + "barAlignment": "left", + "timeForComparison": "months", + "xaxisSecond": { + "axisPosition": "top", + "showLabels": true + }, + "tooltipCumulative": false, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true + } + }, + "title": "{i18n:api-usage.transport-hourly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": {}, + "useDashboardTimewindow": false, + "actions": { + "headerButton": [ + { + "name": "{i18n:api-usage.view-details}", + "icon": "insert_chart", + "type": "openDashboardState", + "targetDashboardStateId": "transport", + "setEntityId": false, + "stateEntityParamName": null, + "openRightLayout": false, + "id": "6ef12f6a-0266-25cf-6ca5-5dcb772252c6" + } + ] + }, + "displayTimewindow": true, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "2aa6b499-6e27-b315-6833-89c4d58485ce", + "typeFullFqn": "system.charts.timeseries_bars_flot" + }, + "d890cea3-fba0-6474-9a21-fa780230dc62": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "jsExecutionCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.javascript-executions}", + "color": "#ff9900", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "tbelExecutionCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.tbel-executions}", + "color": "#4caf50", + "settings": {}, + "_hash": 0.6818645685001823, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 0, + "realtime": { + "timewindowMs": 86400000, + "interval": 3600000 + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "stack": false, + "enableSelection": true, + "fontSize": 10, + "fontColor": "#545454", + "showTooltip": true, + "tooltipIndividual": false, + "tooltipCumulative": false, + "hideZeros": false, + "grid": { + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1, + "color": "#545454", + "backgroundColor": null, + "tickColor": "#DDDDDD" + }, + "xaxis": { + "title": null, + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "min": 0, + "max": null, + "title": null, + "showLabels": true, + "color": "#545454", + "tickSize": null, + "tickDecimals": 0, + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" + }, + "defaultBarWidth": 2400000, + "barAlignment": "left", + "comparisonEnabled": false, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true + }, + "customLegendEnabled": false + }, + "title": "{i18n:api-usage.scripts-hourly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": {}, + "useDashboardTimewindow": false, + "actions": { + "headerButton": [ + { + "name": "{i18n:api-usage.view-details}", + "icon": "insert_chart", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "openDashboardState", + "targetDashboardStateId": "script_functions", + "setEntityId": false, + "stateEntityParamName": null, + "openRightLayout": false, + "openInSeparateDialog": false, + "openInPopover": false, + "id": "4687d3f6-8800-a3b6-26e5-0d33f3b828a9" + } + ] + }, + "displayTimewindow": true, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "configMode": "advanced" + }, + "row": 0, + "col": 0, + "id": "d890cea3-fba0-6474-9a21-fa780230dc62", + "typeFullFqn": "system.charts.timeseries_bars_flot" + }, + "84b6cfa5-1449-e0f2-3560-f810d2dd7ead": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "storageDataPointsCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.data-points-storage-days}", + "color": "#1039ee", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 0, + "realtime": { + "timewindowMs": 86400000, + "interval": 3600000 + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "shadowSize": 4, + "fontColor": "#545454", + "fontSize": 10, + "xaxis": { + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "showLabels": true, + "color": "#545454", + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "min": 0, + "tickDecimals": 0 + }, + "grid": { + "color": "#545454", + "tickColor": "#DDDDDD", + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1 + }, + "stack": false, + "tooltipIndividual": false, + "defaultBarWidth": 2400000, + "barAlignment": "left", + "timeForComparison": "months", + "xaxisSecond": { + "axisPosition": "top", + "showLabels": true + }, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true + } + }, + "title": "{i18n:api-usage.telemetry-persistence-hourly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": {}, + "useDashboardTimewindow": false, + "actions": { + "headerButton": [ + { + "name": "{i18n:api-usage.view-details}", + "icon": "insert_chart", + "type": "openDashboardState", + "targetDashboardStateId": "telemetry_persistence", + "setEntityId": false, + "stateEntityParamName": null, + "openRightLayout": false, + "id": "16707efb-e572-bd02-c219-55fc1b0f672a" + } + ] + }, + "displayTimewindow": true, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "84b6cfa5-1449-e0f2-3560-f810d2dd7ead", + "typeFullFqn": "system.charts.timeseries_bars_flot" + }, + "d296b566-a000-7402-ae9d-c815381c5435": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "createdAlarmsCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.alarms-created}", + "color": "#d35a00", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 0, + "realtime": { + "timewindowMs": 86400000, + "interval": 3600000 + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "shadowSize": 4, + "fontColor": "#545454", + "fontSize": 10, + "xaxis": { + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "showLabels": true, + "color": "#545454", + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "min": 0, + "tickDecimals": 0 + }, + "grid": { + "color": "#545454", + "tickColor": "#DDDDDD", + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1 + }, + "stack": false, + "tooltipIndividual": false, + "defaultBarWidth": 2400000, + "barAlignment": "left", + "timeForComparison": "months", + "xaxisSecond": { + "axisPosition": "top", + "showLabels": true + }, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true + } + }, + "title": "{i18n:api-usage.alarms-created-hourly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": {}, + "useDashboardTimewindow": false, + "actions": { + "headerButton": [ + { + "name": "{i18n:api-usage.view-details}", + "icon": "insert_chart", + "type": "openDashboardState", + "targetDashboardStateId": "alarms_created", + "setEntityId": false, + "stateEntityParamName": null, + "openInSeparateDialog": null, + "dialogTitle": null, + "dialogHideDashboardToolbar": true, + "dialogWidth": null, + "dialogHeight": null, + "openRightLayout": false, + "id": "371882f9-ea23-3abc-fca8-9449c5dfdd6b" + } + ] + }, + "displayTimewindow": true, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "d296b566-a000-7402-ae9d-c815381c5435", + "typeFullFqn": "system.charts.timeseries_bars_flot" + }, + "00a02464-9509-911b-3b5e-21fb37629822": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "emailCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.email-messages}", + "color": "#4caf50", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.1348755140779876, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "smsCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.sms-messages}", + "color": "#f36021", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 0, + "realtime": { + "timewindowMs": 86400000, + "interval": 3600000 + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "shadowSize": 4, + "fontColor": "#545454", + "fontSize": 10, + "xaxis": { + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "showLabels": true, + "color": "#545454", + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "min": 0, + "tickDecimals": 0 + }, + "grid": { + "color": "#545454", + "tickColor": "#DDDDDD", + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1 + }, + "stack": false, + "tooltipIndividual": false, + "defaultBarWidth": 2400000, + "barAlignment": "left", + "timeForComparison": "months", + "xaxisSecond": { + "axisPosition": "top", + "showLabels": true + }, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true + } + }, + "title": "{i18n:api-usage.notifications-hourly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": {}, + "useDashboardTimewindow": false, + "actions": { + "headerButton": [ + { + "name": "{i18n:api-usage.view-details}", + "icon": "insert_chart", + "type": "openDashboardState", + "targetDashboardStateId": "notifications", + "setEntityId": false, + "stateEntityParamName": null, + "openInSeparateDialog": null, + "dialogTitle": null, + "dialogHideDashboardToolbar": true, + "dialogWidth": null, + "dialogHeight": null, + "openRightLayout": false, + "id": "49aefac0-ec5e-d6f3-f39c-8744759f4b19" + } + ] + }, + "displayTimewindow": true, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "00a02464-9509-911b-3b5e-21fb37629822", + "typeFullFqn": "system.charts.timeseries_bars_flot" + }, + "74199074-7873-6c6a-2a51-3fc614769f03": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "ruleEngineExecutionCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.rule-engine-executions}", + "color": "#ab00ff", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000 + }, + "aggregation": { + "type": "SUM", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "shadowSize": 4, + "fontColor": "#545454", + "fontSize": 10, + "xaxis": { + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "showLabels": true, + "color": "#545454", + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "min": 0, + "tickDecimals": 0 + }, + "grid": { + "color": "#545454", + "tickColor": "#DDDDDD", + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1 + }, + "stack": false, + "tooltipIndividual": false, + "defaultBarWidth": 1800000, + "barAlignment": "left", + "timeForComparison": "months", + "xaxisSecond": { + "axisPosition": "top", + "showLabels": true + }, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true + } + }, + "title": "{i18n:api-usage.rule-engine-daily-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": {}, + "useDashboardTimewindow": false, + "actions": {}, + "displayTimewindow": true, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "74199074-7873-6c6a-2a51-3fc614769f03", + "typeFullFqn": "system.charts.timeseries_bars_flot" + }, + "00006bc3-8a8d-b55e-ed39-e318f1bcd090": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "ruleEngineExecutionCount", + "type": "timeseries", + "label": "{i18n:api-usage.rule-engine-executions}", + "color": "#ab00ff", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 31536000000, + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "shadowSize": 4, + "fontColor": "#545454", + "fontSize": 10, + "xaxis": { + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "showLabels": true, + "color": "#545454", + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "min": 0, + "tickDecimals": 0 + }, + "grid": { + "color": "#545454", + "tickColor": "#DDDDDD", + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1 + }, + "stack": false, + "tooltipIndividual": false, + "defaultBarWidth": 900000000, + "barAlignment": "left", + "timeForComparison": "months", + "xaxisSecond": { + "axisPosition": "top", + "showLabels": true + }, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true + } + }, + "title": "{i18n:api-usage.rule-engine-monthly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": {}, + "useDashboardTimewindow": false, + "actions": {}, + "displayTimewindow": true, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "00006bc3-8a8d-b55e-ed39-e318f1bcd090", + "typeFullFqn": "system.charts.timeseries_bars_flot" + }, + "5f5ca59c-e507-5301-5910-7ad8cd34df40": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "jsExecutionCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.javascript-executions}", + "color": "#ff9900", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "tbelExecutionCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.tbel-executions}", + "color": "#4caf50", + "settings": {}, + "_hash": 0.5212969314724616, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000 + }, + "aggregation": { + "type": "SUM", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "shadowSize": 4, + "fontColor": "#545454", + "fontSize": 10, + "xaxis": { + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "showLabels": true, + "color": "#545454", + "min": 0, + "tickDecimals": 0, + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" + }, + "grid": { + "color": "#545454", + "tickColor": "#DDDDDD", + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1 + }, + "stack": false, + "tooltipIndividual": false, + "defaultBarWidth": 1800000, + "barAlignment": "left", + "timeForComparison": "months", + "xaxisSecond": { + "axisPosition": "top", + "showLabels": true + }, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true + } + }, + "title": "{i18n:api-usage.scripts-daily-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": {}, + "useDashboardTimewindow": false, + "actions": {}, + "displayTimewindow": true, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "5f5ca59c-e507-5301-5910-7ad8cd34df40", + "typeFullFqn": "system.charts.timeseries_bars_flot" + }, + "ada32ee9-44ed-48d1-c368-fd0c94b7607f": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "jsExecutionCount", + "type": "timeseries", + "label": "{i18n:api-usage.javascript-executions}", + "color": "#ff9900", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "tbelExecutionCount", + "type": "timeseries", + "label": "{i18n:api-usage.tbel-executions}", + "color": "#4caf50", + "settings": {}, + "_hash": 0.49748239768082403, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 31536000000, + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "shadowSize": 4, + "fontColor": "#545454", + "fontSize": 10, + "xaxis": { + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "showLabels": true, + "color": "#545454", + "min": 0, + "tickDecimals": 0, + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" + }, + "grid": { + "color": "#545454", + "tickColor": "#DDDDDD", + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1 + }, + "stack": false, + "tooltipIndividual": false, + "defaultBarWidth": 900000000, + "barAlignment": "left", + "timeForComparison": "months", + "xaxisSecond": { + "axisPosition": "top", + "showLabels": true + }, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true + } + }, + "title": "{i18n:api-usage.scripts-monthly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": {}, + "useDashboardTimewindow": false, + "actions": {}, + "displayTimewindow": true, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "ada32ee9-44ed-48d1-c368-fd0c94b7607f", + "typeFullFqn": "system.charts.timeseries_bars_flot" + }, + "98bae68b-0f35-72f2-a428-9b06889f1554": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "transportMsgCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.transport-messages}", + "color": "#2196f3", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "transportDataPointsCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.transport-data-points}", + "color": "#4caf50", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.46849996721308895, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000 + }, + "aggregation": { + "type": "SUM", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "shadowSize": 4, + "fontColor": "#545454", + "fontSize": 10, + "xaxis": { + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "showLabels": true, + "color": "#545454", + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "min": 0, + "tickDecimals": 0 + }, + "grid": { + "color": "#545454", + "tickColor": "#DDDDDD", + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1 + }, + "stack": true, + "tooltipIndividual": false, + "defaultBarWidth": 1800000, + "barAlignment": "left", + "timeForComparison": "months", + "xaxisSecond": { + "axisPosition": "top", + "showLabels": true + }, + "tooltipCumulative": false, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true + } + }, + "title": "{i18n:api-usage.transport-daily-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": {}, + "useDashboardTimewindow": false, + "actions": {}, + "displayTimewindow": true, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "98bae68b-0f35-72f2-a428-9b06889f1554", + "typeFullFqn": "system.charts.timeseries_bars_flot" + }, + "e61e5477-5a09-cc25-966b-f613d81da833": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "transportMsgCount", + "type": "timeseries", + "label": "{i18n:api-usage.transport-messages}", + "color": "#2196f3", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "transportDataPointsCount", + "type": "timeseries", + "label": "{i18n:api-usage.transport-data-points}", + "color": "#4caf50", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.46849996721308895, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 31536000000, + "interval": 2592000000 + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "shadowSize": 4, + "fontColor": "#545454", + "fontSize": 10, + "xaxis": { + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "showLabels": true, + "color": "#545454", + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "min": 0, + "tickDecimals": 0 + }, + "grid": { + "color": "#545454", + "tickColor": "#DDDDDD", + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1 + }, + "stack": true, + "tooltipIndividual": false, + "defaultBarWidth": 900000000, + "barAlignment": "left", + "timeForComparison": "months", + "xaxisSecond": { + "axisPosition": "top", + "showLabels": true + }, + "tooltipCumulative": false, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true + } + }, + "title": "{i18n:api-usage.transport-monthly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": {}, + "useDashboardTimewindow": false, + "actions": {}, + "displayTimewindow": true, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "e61e5477-5a09-cc25-966b-f613d81da833", + "typeFullFqn": "system.charts.timeseries_bars_flot" + }, + "85fe0738-5326-f069-ab3f-30594bde5fed": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "storageDataPointsCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.data-points-storage-days}", + "color": "#1039ee", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000 + }, + "aggregation": { + "type": "SUM", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "shadowSize": 4, + "fontColor": "#545454", + "fontSize": 10, + "xaxis": { + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "showLabels": true, + "color": "#545454", + "min": 0, + "tickDecimals": 0, + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" + }, + "grid": { + "color": "#545454", + "tickColor": "#DDDDDD", + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1 + }, + "stack": false, + "tooltipIndividual": false, + "defaultBarWidth": 1800000, + "barAlignment": "left", + "timeForComparison": "months", + "xaxisSecond": { + "axisPosition": "top", + "showLabels": true + }, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true + } + }, + "title": "{i18n:api-usage.telemetry-persistence-daily-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": {}, + "useDashboardTimewindow": false, + "actions": {}, + "displayTimewindow": true, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "85fe0738-5326-f069-ab3f-30594bde5fed", + "typeFullFqn": "system.charts.timeseries_bars_flot" + }, + "eaeb381a-437e-f6e9-60c9-6bc8826fdd44": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "storageDataPointsCount", + "type": "timeseries", + "label": "{i18n:api-usage.data-points-storage-days}", + "color": "#1039ee", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 31536000000, + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "shadowSize": 4, + "fontColor": "#545454", + "fontSize": 10, + "xaxis": { + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "showLabels": true, + "color": "#545454", + "min": 0, + "tickDecimals": 0, + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" + }, + "grid": { + "color": "#545454", + "tickColor": "#DDDDDD", + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1 + }, + "stack": false, + "tooltipIndividual": false, + "defaultBarWidth": 900000000, + "barAlignment": "left", + "timeForComparison": "months", + "xaxisSecond": { + "axisPosition": "top", + "showLabels": true + }, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true + } + }, + "title": "{i18n:api-usage.telemetry-persistence-monthly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": {}, + "useDashboardTimewindow": false, + "actions": {}, + "displayTimewindow": true, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "eaeb381a-437e-f6e9-60c9-6bc8826fdd44", + "typeFullFqn": "system.charts.timeseries_bars_flot" + }, + "4b798823-b97d-9d6a-59dc-fcafd897fc23": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "createdAlarmsCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.alarms-created}", + "color": "#d35a00", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000 + }, + "aggregation": { + "type": "SUM", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "shadowSize": 4, + "fontColor": "#545454", + "fontSize": 10, + "xaxis": { + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "showLabels": true, + "color": "#545454", + "min": 0, + "tickDecimals": 0, + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" + }, + "grid": { + "color": "#545454", + "tickColor": "#DDDDDD", + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1 + }, + "stack": false, + "tooltipIndividual": false, + "defaultBarWidth": 1800000, + "barAlignment": "left", + "timeForComparison": "months", + "xaxisSecond": { + "axisPosition": "top", + "showLabels": true + }, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true + } + }, + "title": "{i18n:api-usage.alarms-created-daily-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": {}, + "useDashboardTimewindow": false, + "actions": {}, + "displayTimewindow": true, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "4b798823-b97d-9d6a-59dc-fcafd897fc23", + "typeFullFqn": "system.charts.timeseries_bars_flot" + }, + "6a981580-7490-19dd-f937-b64cbf67a982": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "createdAlarmsCount", + "type": "timeseries", + "label": "{i18n:api-usage.alarms-created}", + "color": "#d35a00", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 31536000000, + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "shadowSize": 4, + "fontColor": "#545454", + "fontSize": 10, + "xaxis": { + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "showLabels": true, + "color": "#545454", + "min": 0, + "tickDecimals": 0, + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" + }, + "grid": { + "color": "#545454", + "tickColor": "#DDDDDD", + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1 + }, + "stack": false, + "tooltipIndividual": false, + "defaultBarWidth": 900000000, + "barAlignment": "left", + "timeForComparison": "months", + "xaxisSecond": { + "axisPosition": "top", + "showLabels": true + }, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true + } + }, + "title": "{i18n:api-usage.alarms-created-monthly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": {}, + "useDashboardTimewindow": false, + "actions": {}, + "displayTimewindow": true, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "6a981580-7490-19dd-f937-b64cbf67a982", + "typeFullFqn": "system.charts.timeseries_bars_flot" + }, + "7302df65-1b0c-579e-bbdb-145126ae3392": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "smsCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.sms-messages}", + "color": "#f36021", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000 + }, + "aggregation": { + "type": "SUM", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "shadowSize": 4, + "fontColor": "#545454", + "fontSize": 10, + "xaxis": { + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "showLabels": true, + "color": "#545454", + "min": 0, + "tickDecimals": 0, + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" + }, + "grid": { + "color": "#545454", + "tickColor": "#DDDDDD", + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1 + }, + "stack": false, + "tooltipIndividual": false, + "defaultBarWidth": 1800000, + "barAlignment": "left", + "timeForComparison": "months", + "xaxisSecond": { + "axisPosition": "top", + "showLabels": true + }, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true + } + }, + "title": "{i18n:api-usage.sms-messages-daily-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": {}, + "useDashboardTimewindow": false, + "actions": {}, + "displayTimewindow": true, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "7302df65-1b0c-579e-bbdb-145126ae3392", + "typeFullFqn": "system.charts.timeseries_bars_flot" + }, + "fdb385e7-14fe-fc9f-ebdc-b400f26fc66b": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "smsCount", + "type": "timeseries", + "label": "{i18n:api-usage.sms-messages}", + "color": "#f36021", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 31536000000, + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "shadowSize": 4, + "fontColor": "#545454", + "fontSize": 10, + "xaxis": { + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "showLabels": true, + "color": "#545454", + "min": 0, + "tickDecimals": 0, + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" + }, + "grid": { + "color": "#545454", + "tickColor": "#DDDDDD", + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1 + }, + "stack": false, + "tooltipIndividual": false, + "defaultBarWidth": 900000000, + "barAlignment": "left", + "timeForComparison": "months", + "xaxisSecond": { + "axisPosition": "top", + "showLabels": true + }, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true + } + }, + "title": "{i18n:api-usage.sms-messages-monthly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": {}, + "useDashboardTimewindow": false, + "actions": {}, + "displayTimewindow": true, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "fdb385e7-14fe-fc9f-ebdc-b400f26fc66b", + "typeFullFqn": "system.charts.timeseries_bars_flot" + }, + "2408ad30-163e-8221-08e1-a82b638be564": { + "type": "timeseries", + "sizeX": 12, + "sizeY": 7, + "config": { + "datasources": [ + { + "type": "entity", + "dataKeys": [ + { + "name": "successfulMsgs", + "type": "timeseries", + "label": "{i18n:api-usage.successful}", + "color": "#4caf50", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": true, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.15490750967648736 + }, + { + "name": "failedMsgs", + "type": "timeseries", + "label": "{i18n:api-usage.permanent-failures}", + "color": "#ef5350", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": true, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.4186621166514697 + }, + { + "name": "tmpFailed", + "type": "timeseries", + "label": "{i18n:api-usage.processing-failures}", + "color": "#ffc107", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": true, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.49891007198715376 + } + ], + "entityAliasId": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f" + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 0, + "realtime": { + "timewindowMs": 3600000, + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 10000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "shadowSize": 4, + "fontColor": "#545454", + "fontSize": 10, + "xaxis": { + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "showLabels": true, + "color": "#545454", + "min": 0, + "tickDecimals": 0, + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" + }, + "grid": { + "color": "#545454", + "tickColor": "#DDDDDD", + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1 + }, + "stack": false, + "tooltipIndividual": false, + "timeForComparison": "months", + "xaxisSecond": { + "axisPosition": "top", + "showLabels": true + }, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "showMin": true, + "showMax": true, + "showAvg": false, + "showTotal": true + } + }, + "title": "Queue Stats", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "mobileHeight": null, + "showTitleIcon": false, + "titleIcon": null, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "widgetStyle": {}, + "useDashboardTimewindow": false, + "displayTimewindow": true, + "actions": {} + }, + "id": "2408ad30-163e-8221-08e1-a82b638be564", + "typeFullFqn": "system.charts.basic_timeseries" + }, + "e43dcfe1-b970-6a11-ce0e-5769f3eb5e88": { + "type": "timeseries", + "sizeX": 12, + "sizeY": 7, + "config": { + "datasources": [ + { + "type": "entity", + "dataKeys": [ + { + "name": "timeoutMsgs", + "type": "timeseries", + "label": "{i18n:api-usage.permanent-timeouts}", + "color": "#4caf50", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": true, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.565222981550328 + }, + { + "name": "tmpTimeout", + "type": "timeseries", + "label": "{i18n:api-usage.processing-timeouts}", + "color": "#9c27b0", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": true, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.2679547062508352 + } + ], + "entityAliasId": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f" + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 0, + "realtime": { + "timewindowMs": 3600000, + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 10000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "shadowSize": 4, + "fontColor": "#545454", + "fontSize": 10, + "xaxis": { + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "showLabels": true, + "color": "#545454", + "min": 0, + "tickDecimals": 0, + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" + }, + "grid": { + "color": "#545454", + "tickColor": "#DDDDDD", + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1 + }, + "stack": false, + "tooltipIndividual": false, + "timeForComparison": "months", + "xaxisSecond": { + "axisPosition": "top", + "showLabels": true + }, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "showMin": true, + "showMax": true, + "showAvg": false, + "showTotal": true + } + }, + "title": "Processing Failures and Timeouts", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "mobileHeight": null, + "showTitleIcon": false, + "titleIcon": null, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "widgetStyle": {}, + "useDashboardTimewindow": false, + "displayTimewindow": true, + "actions": {} + }, + "id": "e43dcfe1-b970-6a11-ce0e-5769f3eb5e88", + "typeFullFqn": "system.charts.basic_timeseries" + }, + "a669cf86-e715-efa4-dd9a-b839abf499e9": { + "type": "timeseries", + "sizeX": 24, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "dataKeys": [ + { + "name": "ruleEngineException", + "type": "timeseries", + "label": "Rule Chain", + "color": "#2196f3", + "settings": { + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "return JSON.parse(value).ruleChainName;" + }, + "_hash": 0.9954481282345906 + }, + { + "name": "ruleEngineException", + "type": "timeseries", + "label": "Rule Node", + "color": "#4caf50", + "settings": { + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "return JSON.parse(value).ruleNodeName;" + }, + "_hash": 0.18580357036589978 + }, + { + "name": "ruleEngineException", + "type": "timeseries", + "label": "Latest Error", + "color": "#f44336", + "settings": { + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "return JSON.parse(value).message;" + }, + "_hash": 0.7255162989552142 + } + ], + "entityAliasId": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f" + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 0, + "realtime": { + "timewindowMs": 2592000000, + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "showTimestamp": true, + "displayPagination": true, + "defaultPageSize": 10 + }, + "title": "Exceptions", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "useDashboardTimewindow": false, + "showLegend": false, + "widgetStyle": {}, + "actions": {}, + "showTitleIcon": false, + "titleIcon": null, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "displayTimewindow": true + }, + "id": "a669cf86-e715-efa4-dd9a-b839abf499e9", + "typeFullFqn": "system.cards.timeseries_table" + }, + "292eaded-4775-36f7-c896-98d57bdda936": { + "type": "latest", + "sizeX": 7.5, + "sizeY": 3, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "emailApiState", + "type": "timeseries", + "label": "apiState", + "color": "#2196f3", + "settings": {}, + "_hash": 0.8830669138660703, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "emailLimit", + "type": "timeseries", + "label": "limit", + "color": "#4caf50", + "settings": {}, + "_hash": 0.5463603803546802, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "emailCount", + "type": "timeseries", + "label": "count", + "color": "#f44336", + "settings": {}, + "_hash": 0.5564241862015964, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "transportApiState", + "type": "timeseries", + "label": "cardId", + "color": "#607d8b", + "settings": {}, + "_hash": 0.051659774305067296, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return (Math.random()*100000).toFixed(0);" + }, + { + "name": "smsApiState", + "type": "timeseries", + "label": "apiStatePoint", + "color": "#e91e63", + "settings": {}, + "_hash": 0.2969682764607864, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "smsLimit", + "type": "timeseries", + "label": "pointsLimit", + "color": "#9c27b0", + "settings": {}, + "_hash": 0.22082255831864894, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "smsCount", + "type": "timeseries", + "label": "pointsCount", + "color": "#8bc34a", + "settings": {}, + "_hash": 0.6340356364819146, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "transportApiState", + "type": "timeseries", + "label": "title", + "color": "#3f51b5", + "settings": {}, + "_hash": 0.6894070537030252, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.notifications}\";" + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1694083375827, + "endTimeMs": 1694169775827 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "#666666", + "padding": "0", + "settings": { + "cardCss": ".card {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n}\n\n.card > img {\n height: 0;\n}\n\n.card .content {\n flex: 1; \n padding: 13px 13px 0;\n display: flex;\n box-sizing: border-box;\n}\n\n.card .content .column {\n display: flex;\n flex-direction: column; \n justify-content: space-around;\n flex: 1;\n}\n\n.card .content .title-row {\n display: flex;\n flex-direction: row;\n padding-bottom: 10px;\n}\n\n.card .title {\n flex: 1;\n font-size: 20px;\n font-weight: 400;\n color: #666666;\n}\n\n.card .state {\n text-transform: uppercase;\n font-size: 20px;\n font-weight: bold;\n}\n\n.card.enabled .state {\n color: #00B260;\n}\n\n.card.warning .state {\n color: #FFAD6F;\n}\n\n.card.disabled .state {\n color: #F73243;\n}\n\n.card .bars-row {\n flex: 1;\n display: flex;\n flex-direction: row;\n}\n\n.card .bar-column {\n flex: 1;\n display: flex;\n flex-direction: column;\n}\n\n.card .bar-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.card .bar {\n flex: 1;\n max-height: 30px;\n margin-top: 3.5px;\n margin-bottom: 4px;\n background-color: #F0F0F0;\n border: 1px solid #DADCDB;\n border-radius: 2px;\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2);\n}\n\n.card.enabled .bar {\n border-color: #00B260;\n background-color: #F0FBF7;\n}\n\n.card.warning .bar {\n border-color: #FFAD6F;\n background-color: #FFFAF6;\n}\n\n.card.disabled .bar {\n border-color: #F73243;\n background-color: #FFF0F0;\n}\n\n.card .bar .bar-fill {\n background-color: #F0F0F0;\n border-radius: 2px;\n height: 100%;\n width: 0%;\n}\n\n.card.enabled .bar-fill {\n background-color: #00C46C;\n}\n\n.card.warning .bar-fill {\n background-color: #FFD099;\n}\n\n.card.disabled .bar-fill {\n background-color: #FF9494;\n}\n\n.card .bar-labels {\n height: 20px;\n font-size: 16px;\n color: #666;\n display: flex;\n flex-direction: row;\n}\n\n.card .mat-mdc-button-base {\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.card .mdc-button__label {\n pointer-events: none;\n}\n\n.card .action-row {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n padding: 8px 0;\n}\n\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .card .title {\n font-size: 12px;\n }\n .card .state {\n font-size: 12px;\n }\n .card .unit {\n font-size: 8px;\n }\n .card .bar-labels {\n font-size: 6px;\n }\n .card .mat-mdc-button-base {\n font-size: 8px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1599px) {\n .card .title {\n font-size: 14px;\n }\n .card .state {\n font-size: 14px;\n }\n .card .unit {\n font-size: 10px;\n }\n .card .bar-labels {\n font-size: 8px;\n }\n .card .mat-mdc-button-base {\n font-size: 10px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1600px) and (max-width: 1919px) {\n .card .title {\n font-size: 16px;\n }\n .card .state {\n font-size: 16px;\n }\n .card .unit {\n font-size: 12px;\n }\n .card .bar-labels {\n font-size: 12px;\n }\n .card .mat-mdc-button-base {\n font-size: 12px;\n }\n .card .action-row {\n padding: 0;\n }\n} \n\n", + "cardHtml": "
\n \n \n
\n
\n
\n
\n ${title}\n
\n
\n
\n
\n
\n
\n
{i18n:api-usage.email}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
{i18n:api-usage.sms}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n
\n
" + }, + "title": "Notifications (Email/SMS)", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": { + "cursor": "default" + }, + "titleStyle": { + "fontSize": "20px", + "fontWeight": 400 + }, + "useDashboardTimewindow": true, + "showLegend": false, + "actions": { + "elementClick": [ + { + "name": "transport_details", + "icon": "insert_chart", + "type": "openDashboardState", + "targetDashboardStateId": "notifications", + "setEntityId": false, + "stateEntityParamName": null, + "openInSeparateDialog": null, + "dialogTitle": null, + "dialogHideDashboardToolbar": true, + "dialogWidth": null, + "dialogHeight": null, + "openRightLayout": false, + "id": "46b7cefe-e1f2-67c1-4055-3a214520f869" + } + ] + }, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "292eaded-4775-36f7-c896-98d57bdda936", + "typeFullFqn": "system.cards.html_value_card" + }, + "b3571a36-2106-1122-7d58-0d38bbb0e9c8": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "emailCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.email-messages}", + "color": "#d35a00", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000 + }, + "aggregation": { + "type": "SUM", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "shadowSize": 4, + "fontColor": "#545454", + "fontSize": 10, + "xaxis": { + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "showLabels": true, + "color": "#545454", + "min": 0, + "tickDecimals": 0, + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" + }, + "grid": { + "color": "#545454", + "tickColor": "#DDDDDD", + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1 + }, + "stack": false, + "tooltipIndividual": false, + "defaultBarWidth": 1800000, + "barAlignment": "left", + "timeForComparison": "months", + "xaxisSecond": { + "axisPosition": "top", + "showLabels": true + }, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true + } + }, + "title": "{i18n:api-usage.email-messages-daily-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": {}, + "useDashboardTimewindow": false, + "actions": {}, + "displayTimewindow": true, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "b3571a36-2106-1122-7d58-0d38bbb0e9c8", + "typeFullFqn": "system.charts.timeseries_bars_flot" + }, + "aa875b7f-e7c8-7529-1ae7-f456211b59cc": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "emailCount", + "type": "timeseries", + "label": "{i18n:api-usage.email-messages}", + "color": "#d35a00", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 31536000000, + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "shadowSize": 4, + "fontColor": "#545454", + "fontSize": 10, + "xaxis": { + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "showLabels": true, + "color": "#545454", + "min": 0, + "tickDecimals": 0, + "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" + }, + "grid": { + "color": "#545454", + "tickColor": "#DDDDDD", + "verticalLines": true, + "horizontalLines": true, + "outlineWidth": 1 + }, + "stack": false, + "tooltipIndividual": false, + "defaultBarWidth": 900000000, + "barAlignment": "left", + "timeForComparison": "months", + "xaxisSecond": { + "axisPosition": "top", + "showLabels": true + }, + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true + } + }, + "title": "{i18n:api-usage.email-messages-monthly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": {}, + "useDashboardTimewindow": false, + "actions": {}, + "displayTimewindow": true, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "aa875b7f-e7c8-7529-1ae7-f456211b59cc", + "typeFullFqn": "system.charts.timeseries_bars_flot" + } + }, + "states": { + "default": { + "name": "{i18n:api-usage.api-usage}", + "root": true, + "layouts": { + "main": { + "widgets": { + "fd6df872-2ddf-0921-3929-2e7f55062fad": { + "sizeX": 4, + "sizeY": 2, + "row": 0, + "col": 8, + "mobileHeight": 3 + }, + "7e235874-461b-e7c2-2fdd-d8762a020773": { + "sizeX": 4, + "sizeY": 2, + "row": 0, + "col": 12, + "mobileHeight": 3 + }, + "08545554-a0e8-05c7-66df-6000cfeff8a4": { + "sizeX": 4, + "sizeY": 2, + "row": 0, + "col": 4, + "mobileHeight": 3 + }, + "a245c67e-53ec-d299-fa89-69fe2062ccb2": { + "sizeX": 4, + "sizeY": 2, + "row": 0, + "col": 0, + "mobileHeight": 3 + }, + "a151ae60-0326-6116-d818-9070dda8e9c7": { + "sizeX": 4, + "sizeY": 2, + "row": 0, + "col": 16, + "mobileHeight": 3 + }, + "292eaded-4775-36f7-c896-98d57bdda936": { + "sizeX": 4, + "sizeY": 2, + "row": 0, + "col": 20, + "mobileHeight": 3 + }, + "68e16e98-0420-f72c-4848-41dedffd3904": { + "sizeX": 8, + "sizeY": 4, + "row": 2, + "col": 8 + }, + "2aa6b499-6e27-b315-6833-89c4d58485ce": { + "sizeX": 8, + "sizeY": 4, + "row": 2, + "col": 0 + }, + "d890cea3-fba0-6474-9a21-fa780230dc62": { + "sizeX": 8, + "sizeY": 4, + "row": 2, + "col": 16 + }, + "84b6cfa5-1449-e0f2-3560-f810d2dd7ead": { + "sizeX": 8, + "sizeY": 4, + "row": 6, + "col": 0 + }, + "d296b566-a000-7402-ae9d-c815381c5435": { + "sizeX": 8, + "sizeY": 4, + "row": 6, + "col": 8 + }, + "00a02464-9509-911b-3b5e-21fb37629822": { + "sizeX": 8, + "sizeY": 4, + "row": 6, + "col": 16 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 5, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } + }, + "transport": { + "name": "{i18n:api-usage.transport}", + "root": false, + "layouts": { + "main": { + "widgets": { + "98bae68b-0f35-72f2-a428-9b06889f1554": { + "sizeX": 24, + "sizeY": 6, + "row": 0, + "col": 0 + }, + "e61e5477-5a09-cc25-966b-f613d81da833": { + "sizeX": 24, + "sizeY": 6, + "row": 6, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 10, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } + }, + "rule_engine_execution": { + "name": "{i18n:api-usage.rule-engine-executions}", + "root": false, + "layouts": { + "main": { + "widgets": { + "74199074-7873-6c6a-2a51-3fc614769f03": { + "sizeX": 24, + "sizeY": 6, + "row": 0, + "col": 0 + }, + "00006bc3-8a8d-b55e-ed39-e318f1bcd090": { + "sizeX": 24, + "sizeY": 6, + "row": 6, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 10, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } + }, + "telemetry_persistence": { + "name": "{i18n:api-usage.telemetry-persistence}", + "root": false, + "layouts": { + "main": { + "widgets": { + "85fe0738-5326-f069-ab3f-30594bde5fed": { + "sizeX": 24, + "sizeY": 6, + "row": 0, + "col": 0 + }, + "eaeb381a-437e-f6e9-60c9-6bc8826fdd44": { + "sizeX": 24, + "sizeY": 6, + "row": 6, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 10, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } + }, + "rule_engine_statistics": { + "name": "{i18n:api-usage.rule-engine-statistics}", + "root": false, + "layouts": { + "main": { + "widgets": { + "2408ad30-163e-8221-08e1-a82b638be564": { + "sizeX": 12, + "sizeY": 7, + "mobileHeight": null, + "row": 0, + "col": 0 + }, + "e43dcfe1-b970-6a11-ce0e-5769f3eb5e88": { + "sizeX": 12, + "sizeY": 7, + "mobileHeight": null, + "row": 0, + "col": 12 + }, + "a669cf86-e715-efa4-dd9a-b839abf499e9": { + "sizeX": 24, + "sizeY": 5, + "row": 7, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 10, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } + }, + "notifications": { + "name": "{i18n:api-usage.notifications-email-sms}", + "root": false, + "layouts": { + "main": { + "widgets": { + "7302df65-1b0c-579e-bbdb-145126ae3392": { + "sizeX": 12, + "sizeY": 6, + "row": 0, + "col": 12 + }, + "fdb385e7-14fe-fc9f-ebdc-b400f26fc66b": { + "sizeX": 12, + "sizeY": 6, + "row": 6, + "col": 12 + }, + "b3571a36-2106-1122-7d58-0d38bbb0e9c8": { + "sizeX": 12, + "sizeY": 6, + "row": 0, + "col": 0 + }, + "aa875b7f-e7c8-7529-1ae7-f456211b59cc": { + "sizeX": 12, + "sizeY": 6, + "row": 6, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 10, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } + }, + "alarms_created": { + "name": "{i18n:api-usage.alarms-created}", + "root": false, + "layouts": { + "main": { + "widgets": { + "4b798823-b97d-9d6a-59dc-fcafd897fc23": { + "sizeX": 24, + "sizeY": 6, + "row": 0, + "col": 0 + }, + "6a981580-7490-19dd-f937-b64cbf67a982": { + "sizeX": 24, + "sizeY": 6, + "row": 6, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 10, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } + }, + "script_functions": { + "name": "{i18n:api-usage.scripts}", + "root": false, + "layouts": { + "main": { + "widgets": { + "5f5ca59c-e507-5301-5910-7ad8cd34df40": { + "sizeX": 24, + "sizeY": 6, + "row": 0, + "col": 0 + }, + "ada32ee9-44ed-48d1-c368-fd0c94b7607f": { + "sizeX": 24, + "sizeY": 6, + "row": 6, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 10, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } + } + }, + "entityAliases": { + "40193437-33ac-3172-eefd-0b08eb849062": { + "id": "40193437-33ac-3172-eefd-0b08eb849062", + "alias": "Api usage state", + "filter": { + "type": "apiUsageState", + "resolveMultiple": false + } + }, + "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f": { + "id": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f", + "alias": "TbServiceQueues", + "filter": { + "type": "assetType", + "resolveMultiple": true, + "assetNameFilter": "", + "assetTypes": [ + "TbServiceQueue" + ] + } + } + }, + "filters": {}, + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 0, + "realtime": { + "timewindowMs": 86400000, + "interval": 3600000 + }, + "aggregation": { + "type": "NONE", + "limit": 50000 + } + }, + "settings": { + "stateControllerId": "entity", + "showTitle": false, + "showDashboardsSelect": false, + "showEntitiesSelect": false, + "showDashboardTimewindow": false, + "showDashboardExport": false, + "toolbarAlwaysOpen": true, + "titleColor": "rgba(0,0,0,0.870588)", + "showFilters": false, + "showDashboardLogo": false + } + }, + "externalId": null, + "name": "Api Usage" +} diff --git a/ui-ngx/src/assets/dashboard/customer_user_home_page.json b/ui-ngx/src/assets/dashboard/customer_user_home_page.json new file mode 100644 index 0000000000..aef3367ff3 --- /dev/null +++ b/ui-ngx/src/assets/dashboard/customer_user_home_page.json @@ -0,0 +1,572 @@ +{ + "title": "Customer User Home Page", + "image": null, + "mobileHide": false, + "mobileOrder": null, + "configuration": { + "description": "", + "widgets": { + "867f82cf-ecf2-2d5c-35cb-08c6f2edc3a4": { + "type": "static", + "sizeX": 7.5, + "sizeY": 3, + "config": { + "datasources": [], + "timewindow": { + "realtime": { + "timewindowMs": 60000 + } + }, + "showTitle": false, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "columns": 2 + }, + "title": "Documentation", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "showLegend": false + }, + "row": 0, + "col": 0, + "id": "867f82cf-ecf2-2d5c-35cb-08c6f2edc3a4", + "typeFullFqn": "system.home_page_widgets.documentation_links" + }, + "9e3ef045-d8bc-1640-a3f4-2dd10b19d50e": { + "type": "static", + "sizeX": 7.5, + "sizeY": 3, + "config": { + "showTitle": false, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "columns": 2 + }, + "title": "Quick links", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "showLegend": false, + "datasources": [] + }, + "row": 0, + "col": 0, + "id": "9e3ef045-d8bc-1640-a3f4-2dd10b19d50e", + "typeFullFqn": "system.home_page_widgets.quick_links" + }, + "6d6e2b1d-6ce7-4678-3745-c6b0897b2674": { + "type": "static", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "showTitle": false, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": {}, + "title": "Dashboards", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "showLegend": false, + "datasources": [] + }, + "row": 0, + "col": 0, + "id": "6d6e2b1d-6ce7-4678-3745-c6b0897b2674", + "typeFullFqn": "system.home_page_widgets.dashboards" + }, + "35d3ac3b-f1af-dc3d-a1d1-11f351d16fc6": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entityCount", + "name": null, + "entityAliasId": "a1ddb8fa-90ff-5598-e7f2-e254194d055d", + "filterId": null, + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "totalDevices", + "color": "#2196f3", + "settings": {}, + "_hash": 0.8491768696709192, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + }, + { + "type": "entityCount", + "name": null, + "entityAliasId": "a1ddb8fa-90ff-5598-e7f2-e254194d055d", + "filterId": "42f42812-92fe-a13a-b0ba-c6e0dfab3584", + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "activeDevices", + "color": "#4caf50", + "settings": {}, + "_hash": 0.1262449138010293, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + }, + { + "type": "entityCount", + "name": null, + "entityAliasId": "a1ddb8fa-90ff-5598-e7f2-e254194d055d", + "filterId": "39d92c21-2426-7346-4484-452829146c33", + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "inactiveDevices", + "color": "#f44336", + "settings": {}, + "_hash": 0.39119172615806797, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n}\n\n.tb-content-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n gap: 12px;\n}\n\n.tb-card-header {\n height: 36px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-item-cards {\n flex: 1;\n display: flex;\n flex-direction: row;\n gap: 12px;\n}\n\na.tb-item-card {\n flex: 1;\n display: flex;\n flex-direction: column;\n padding: 8px 12px;\n border: 1px solid;\n border-radius: 10px;\n margin-bottom: 12px;\n}\n\na.tb-item-card.tb-inactive {\n background: rgba(209, 39, 48, 0.04);\n border-color: rgba(209, 39, 48, 0.06);\n}\n\na.tb-item-card.tb-active {\n background: rgba(48, 86, 128, 0.04);\n border-color: rgba(48, 86, 128, 0.12);\n}\n\na.tb-item-card.tb-total {\n background: rgba(0, 0, 0, 0.01);\n border-color: rgba(0, 0, 0, 0.05);\n}\n\n.tb-item-title-container {\n display: grid;\n}\n\n.tb-item-title {\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis; \n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-item-title.tb-home-widget-link:after {\n position: absolute;\n right: 0;\n}\n\na.tb-item-card:hover .tb-item-title.tb-home-widget-link:after { \n color: rgba(0, 0, 0, 0.38);\n}\n\na.tb-item-card:hover {\n box-shadow: 0px 4px 10px rgba(23, 33, 90, 0.08);\n}\n\n.tb-count-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 24px;\n line-height: 36px;\n white-space: nowrap;\n color: rgba(0, 0, 0, 0.87);\n}\n\n@media screen and (max-width: 959px) {\n .tb-item-cards {\n flex-direction: column;\n }\n a.tb-item-card {\n margin-bottom: 0;\n }\n}\n\n@media screen and (max-width: 1279px) {\n a.tb-item-card {\n flex-direction: row;\n align-items: center;\n }\n .tb-item-title.tb-home-widget-link:after {\n position: relative;\n }\n .tb-count-container {\n align-items: flex-end;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-item-title {\n font-size: 11px;\n line-height: 16px;\n }\n .tb-count {\n font-size: 16px;\n line-height: 24px;\n }\n a.tb-item-card {\n padding: 4px 8px;\n margin-bottom: 6px;\n }\n a.tb-item-card:hover {\n box-shadow: 0px 2px 5px rgba(23, 33, 90, 0.08);\n }\n}\n" + }, + "title": "Devices", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "35d3ac3b-f1af-dc3d-a1d1-11f351d16fc6", + "typeFullFqn": "system.cards.markdown_card" + }, + "7ac20b6a-dc40-b18e-9f5f-bca20bc693bb": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "alarmCount", + "name": null, + "entityAliasId": null, + "filterId": null, + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "totalAlarms", + "color": "#ffc107", + "settings": {}, + "_hash": 0.8743321483507485, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + }, + { + "type": "alarmCount", + "name": null, + "entityAliasId": null, + "filterId": null, + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "assignedToMeAlarms", + "color": "#ffc107", + "settings": {}, + "_hash": 0.6392390736755882, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ], + "assignedToCurrentUser": true + } + }, + { + "type": "alarmCount", + "name": null, + "entityAliasId": null, + "filterId": null, + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "criticalAlarms", + "color": "#ffc107", + "settings": {}, + "_hash": 0.8329478098552843, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ], + "severityList": [ + "CRITICAL" + ] + } + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n}\n\n.tb-content-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n gap: 12px;\n}\n\n.tb-card-header {\n height: 36px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-item-cards {\n flex: 1;\n display: flex;\n flex-direction: row;\n gap: 12px;\n}\n\na.tb-item-card {\n flex: 1;\n display: flex;\n flex-direction: column;\n padding: 8px 12px;\n border: 1px solid;\n border-radius: 10px;\n margin-bottom: 12px;\n}\n\na.tb-item-card.tb-critical {\n background: rgba(209, 39, 48, 0.04);\n border-color: rgba(209, 39, 48, 0.06);\n}\n\na.tb-item-card.tb-assigned {\n background: rgba(48, 86, 128, 0.04);\n border-color: rgba(48, 86, 128, 0.12);\n}\n\na.tb-item-card.tb-total {\n background: rgba(0, 0, 0, 0.01);\n border-color: rgba(0, 0, 0, 0.05);\n}\n\n.tb-item-title-container {\n display: grid;\n}\n\n.tb-item-title {\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis; \n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-item-title.tb-home-widget-link:after {\n position: absolute;\n right: 0;\n}\n\na.tb-item-card:hover .tb-item-title.tb-home-widget-link:after { \n color: rgba(0, 0, 0, 0.38);\n}\n\na.tb-item-card:hover {\n box-shadow: 0px 4px 10px rgba(23, 33, 90, 0.08);\n}\n\n.tb-count-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 24px;\n line-height: 36px;\n white-space: nowrap;\n color: rgba(0, 0, 0, 0.87);\n}\n\na.tb-item-card.tb-critical .tb-count:after {\n content: \"warning\";\n display: inline-block;\n position: relative;\n font-family: 'Material Icons Round';\n color: #D12730;\n vertical-align: bottom;\n margin-left: 6px;\n}\n\n@media screen and (max-width: 959px) {\n .tb-item-cards {\n flex-direction: column;\n }\n a.tb-item-card {\n margin-bottom: 0;\n }\n}\n\n@media screen and (max-width: 1279px) {\n a.tb-item-card {\n flex-direction: row;\n align-items: center;\n }\n .tb-item-title.tb-home-widget-link:after {\n position: relative;\n }\n .tb-count-container {\n align-items: flex-end;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-item-title {\n font-size: 11px;\n line-height: 16px;\n }\n .tb-count {\n font-size: 16px;\n line-height: 24px;\n }\n a.tb-item-card {\n padding: 4px 8px;\n margin-bottom: 6px;\n }\n a.tb-item-card:hover {\n box-shadow: 0px 2px 5px rgba(23, 33, 90, 0.08);\n }\n a.tb-item-card.tb-critical .tb-count:after {\n margin-left: 2px;\n }\n}\n" + }, + "title": "Alarms", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "7ac20b6a-dc40-b18e-9f5f-bca20bc693bb", + "typeFullFqn": "system.cards.markdown_card" + } + }, + "states": { + "default": { + "name": "Customer User Home Page", + "root": true, + "layouts": { + "main": { + "widgets": { + "867f82cf-ecf2-2d5c-35cb-08c6f2edc3a4": { + "sizeX": 42, + "sizeY": 16, + "row": 42, + "col": 78, + "mobileHide": false, + "mobileOrder": 4, + "mobileHeight": 7 + }, + "9e3ef045-d8bc-1640-a3f4-2dd10b19d50e": { + "sizeX": 42, + "sizeY": 16, + "row": 26, + "col": 78, + "mobileHide": false, + "mobileOrder": 3, + "mobileHeight": 7 + }, + "6d6e2b1d-6ce7-4678-3745-c6b0897b2674": { + "sizeX": 78, + "sizeY": 58, + "row": 0, + "col": 0, + "mobileOrder": 2, + "mobileHeight": 10 + }, + "35d3ac3b-f1af-dc3d-a1d1-11f351d16fc6": { + "sizeX": 42, + "sizeY": 13, + "row": 13, + "col": 78, + "mobileHeight": 9, + "mobileOrder": 1 + }, + "7ac20b6a-dc40-b18e-9f5f-bca20bc693bb": { + "sizeX": 42, + "sizeY": 13, + "mobileHeight": 9, + "row": 0, + "col": 78, + "mobileOrder": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 120, + "margin": 12, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 20, + "outerMargin": true + } + } + } + } + }, + "entityAliases": { + "a1ddb8fa-90ff-5598-e7f2-e254194d055d": { + "id": "a1ddb8fa-90ff-5598-e7f2-e254194d055d", + "alias": "Devices", + "filter": { + "type": "entityType", + "resolveMultiple": true, + "entityType": "DEVICE" + } + }, + "d9229b29-3f46-de8d-7fe8-eb0c43c75079": { + "id": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", + "alias": "Api Usage State", + "filter": { + "type": "apiUsageState", + "resolveMultiple": true + } + } + }, + "filters": { + "42f42812-92fe-a13a-b0ba-c6e0dfab3584": { + "id": "42f42812-92fe-a13a-b0ba-c6e0dfab3584", + "filter": "Active Devices", + "keyFilters": [ + { + "key": { + "type": "SERVER_ATTRIBUTE", + "key": "active" + }, + "valueType": "BOOLEAN", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": true, + "dynamicValue": null + }, + "type": "BOOLEAN" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": false + }, + "39d92c21-2426-7346-4484-452829146c33": { + "id": "39d92c21-2426-7346-4484-452829146c33", + "filter": "Inactive Devices", + "keyFilters": [ + { + "key": { + "type": "SERVER_ATTRIBUTE", + "key": "active" + }, + "valueType": "BOOLEAN", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": false, + "dynamicValue": null + }, + "type": "BOOLEAN" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": false + } + }, + "timewindow": { + "displayValue": "", + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168326072, + "endTimeMs": 1680254726072 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "settings": { + "stateControllerId": "entity", + "showTitle": false, + "showDashboardsSelect": true, + "showEntitiesSelect": true, + "showDashboardTimewindow": true, + "showDashboardExport": true, + "toolbarAlwaysOpen": true, + "titleColor": "rgba(0,0,0,0.870588)", + "showDashboardLogo": false, + "dashboardLogoUrl": null, + "hideToolbar": true, + "showFilters": true, + "showUpdateDashboardImage": true, + "dashboardCss": ".tb-widget-container > .tb-widget {\n border: 1px solid rgba(0, 0, 0, 0.05);\n box-shadow: 0px 5px 16px rgba(0, 0, 0, 0.04);\n border-radius: 12px;\n}\n\n.tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 16px !important;\n}\n\n.tb-card-title {\n display: grid;\n}\n\n.tb-home-widget-title {\n font-style: normal;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n color: rgba(0, 0, 0, 0.54);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.tb-home-widget-link {\n position: relative;\n border-bottom: none;\n}\n\n.tb-home-widget-link:hover {\n border-bottom: none;\n}\n\n.tb-home-widget-link:focus {\n border-bottom: none;\n}\n\n.tb-home-widget-link::after {\n content: 'arrow_forward';\n display: inline-block;\n transform: rotate(315deg);\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n font-size: 18px;\n color: rgba(0, 0, 0, 0.12);\n vertical-align: bottom;\n margin-left: 6px; \n}\n\n.tb-home-widget-link:hover::after {\n color: inherit;\n}\n\n.tb-home-widget-info-icon {\n color: rgba(0, 0, 0, 0.12);\n font-size: 16px;\n width: 16px;\n height: 16px;\n line-height: 15px;\n vertical-align: middle;\n}\n\n.tb-widget-container > .tb-widget .tb-timewindow {\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n color: rgba(0, 0, 0, 0.54);\n padding: 0;\n}\n\n.tb-widget-container > .tb-widget .tb-legend-keys .tb-legend-label {\n cursor: pointer;\n user-select: none;\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n color: rgba(0, 0, 0, 0.54);\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .tb-widget-container > .tb-widget {\n border-radius: 4px;\n }\n .tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 2px !important;\n }\n .tb-hide-md {\n display: none;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1819px) {\n .tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 8px !important;\n }\n .tb-hide-lg {\n display: none;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-hide-md-lg {\n display: none;\n }\n\n .tb-home-widget-title {\n font-size: 12px;\n line-height: 16px;\n }\n \n .tb-widget-container > .tb-widget .tb-widget-title {\n padding: 0;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow {\n font-size: 12px;\n line-height: 16px;\n min-height: 24px;\n padding: 0;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow .mat-mdc-icon-button.tb-mat-32 {\n width: 24px;\n height: 24px;\n line-height: 24px;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow .mat-mdc-icon-button.tb-mat-32 .mat-icon {\n width: 18px;\n height: 18px;\n font-size: 18px;\n }\n \n .tb-widget-container > .tb-widget tb-legend {\n padding-bottom: 0 !important;\n }\n \n .tb-widget-container > .tb-widget .tb-legend-keys .tb-legend-label {\n font-size: 11px;\n line-height: 16px;\n letter-spacing: 0.25px;\n }\n}\n\n@media screen and (max-width: 959px), screen and (min-width: 1820px) {\n .tb-hide-not-md-lg {\n display: none;\n }\n}\n" + } + }, + "externalId": null, + "name": "Customer User Home Page" +} diff --git a/ui-ngx/src/assets/dashboard/sys_admin_home_page.json b/ui-ngx/src/assets/dashboard/sys_admin_home_page.json new file mode 100644 index 0000000000..f93de01596 --- /dev/null +++ b/ui-ngx/src/assets/dashboard/sys_admin_home_page.json @@ -0,0 +1,2614 @@ +{ + "title": "System Administrator Home Page", + "image": null, + "mobileHide": false, + "mobileOrder": null, + "configuration": { + "description": "", + "widgets": { + "d70cc256-4c7b-ee06-9905-b8c5e546605f": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "
\n
\n
{{ 'widgets.transport-messages.title' | translate }}\n info\n
\n
\n \n
", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n}\n" + }, + "title": "Transport messages", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "d70cc256-4c7b-ee06-9905-b8c5e546605f", + "typeFullFqn": "system.cards.markdown_card" + }, + "8ee72d43-678c-4e25-e9a8-7d4cfd7a5f8e": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", + "filterId": null, + "dataKeys": [ + { + "name": "transportMsgCountHourly", + "type": "timeseries", + "label": "{i18n:widgets.transport-messages.title}", + "color": "#305680", + "settings": {}, + "_hash": 0.2880464219129071, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "latestDataKeys": [] + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": true, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000, + "fixedTimewindow": { + "startTimeMs": 1680443065451, + "endTimeMs": 1680529465451 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "SUM", + "limit": 50000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "stack": true, + "fontSize": 10, + "fontColor": "#545454", + "showTooltip": true, + "tooltipIndividual": false, + "tooltipCumulative": false, + "hideZeros": false, + "grid": { + "verticalLines": false, + "horizontalLines": false, + "outlineWidth": 0, + "color": "#545454", + "backgroundColor": null, + "tickColor": "#DDDDDD" + }, + "xaxis": { + "title": null, + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "min": 0, + "max": null, + "title": null, + "showLabels": true, + "color": "#545454", + "tickSize": null, + "tickDecimals": 0, + "ticksFormatter": "return value % 1000 === 0 ? ((value / 1000) + 'k') : '';" + }, + "defaultBarWidth": 1800000, + "barAlignment": "left", + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "xaxisSecond": { + "axisPosition": "top", + "title": null, + "showLabels": true + }, + "customLegendEnabled": false, + "dataKeysListForLabels": [], + "showLegend": false, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": true, + "showTotal": false, + "showLatest": false + } + }, + "title": "Transport messages", + "dropShadow": false, + "enableFullscreen": false, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": { + "padding": "0" + }, + "useDashboardTimewindow": false, + "actions": {}, + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetCss": ".tb-widget-container > .tb-widget {\n border: none !important;\n border-radius: 0 !important;\n box-shadow: none !important;\n}\n\n.tb-widget-container > .tb-widget .flot-base {\n opacity: 0.48;\n}\n", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "8ee72d43-678c-4e25-e9a8-7d4cfd7a5f8e", + "typeFullFqn": "system.charts.timeseries_bars_flot" + }, + "4b5e47ed-c197-a937-d727-041ba8decec2": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", + "filterId": null, + "dataKeys": [ + { + "name": "clusterMode", + "type": "timeseries", + "label": "clusterMode", + "color": "#2196f3", + "settings": {}, + "_hash": 0.7272123990942316, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": false, + "postFuncBody": "" + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#ffffff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "useMarkdownTextFunction": true, + "markdownTextFunction": "let html = '
';\nlet clusterMode = data[0]['clusterMode'];\nif (clusterMode === 'true') {\n html += '';\n} else {\n html += '';\n}\nhtml += '
';\nreturn html;", + "applyDefaultMarkdownStyle": false + }, + "title": "System info", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": { + "border": "none", + "boxShadow": "none", + "padding": "0" + }, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "4b5e47ed-c197-a937-d727-041ba8decec2", + "typeFullFqn": "system.cards.markdown_card" + }, + "eace9148-b02a-48fe-1a95-acd9928aa8c5": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", + "filterId": null, + "dataKeys": [ + { + "name": "cpuUsage", + "type": "timeseries", + "label": "{i18n:widgets.system-info.cpu}", + "color": "#305680", + "settings": { + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "excludeFromStacking": false, + "showLines": true, + "lineWidth": 3, + "fillLines": false, + "showPoints": false, + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showPointShape": "circle", + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", + "showSeparateAxis": false, + "axisPosition": "left", + "comparisonSettings": { + "showValuesForComparison": true, + "comparisonValuesLabel": "", + "color": "" + }, + "thresholds": [] + }, + "_hash": 0.9347575372081658, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "memoryUsage", + "type": "timeseries", + "label": "{i18n:widgets.system-info.ram}", + "color": "#ac3bc9", + "settings": { + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "excludeFromStacking": false, + "showLines": true, + "lineWidth": 3, + "fillLines": false, + "showPoints": false, + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showPointShape": "circle", + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", + "showSeparateAxis": false, + "axisPosition": "left", + "comparisonSettings": { + "showValuesForComparison": true, + "comparisonValuesLabel": "", + "color": "" + }, + "thresholds": [] + }, + "_hash": 0.31887216598848855, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "discUsage", + "type": "timeseries", + "label": "{i18n:widgets.system-info.disk}", + "color": "#40d0ae", + "settings": { + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "excludeFromStacking": false, + "showLines": true, + "lineWidth": 3, + "fillLines": false, + "showPoints": false, + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showPointShape": "circle", + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", + "showSeparateAxis": false, + "axisPosition": "left", + "comparisonSettings": { + "showValuesForComparison": true, + "comparisonValuesLabel": "", + "color": "" + }, + "thresholds": [] + }, + "_hash": 0.26499182606431004, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "latestDataKeys": null + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "timewindowMs": 3600000, + "quickInterval": "CURRENT_DAY", + "interval": 10000 + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "stack": false, + "fontSize": 10, + "fontColor": "#545454", + "showTooltip": true, + "tooltipIndividual": false, + "tooltipCumulative": false, + "hideZeros": false, + "grid": { + "verticalLines": false, + "horizontalLines": false, + "outlineWidth": 0, + "color": "#545454", + "backgroundColor": null, + "tickColor": "#DDDDDD" + }, + "xaxis": { + "title": null, + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "min": 0, + "max": 100, + "title": null, + "showLabels": true, + "color": "#545454", + "tickSize": null, + "tickDecimals": 0, + "ticksFormatter": "" + }, + "shadowSize": 0, + "smoothLines": true, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "xaxisSecond": { + "axisPosition": "top", + "title": null, + "showLabels": true + }, + "customLegendEnabled": false, + "dataKeysListForLabels": [], + "showLegend": true, + "legendConfig": { + "direction": "row", + "position": "top", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": false, + "showLatest": false + } + }, + "title": "System Info Chart", + "dropShadow": false, + "enableFullscreen": false, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "useDashboardTimewindow": false, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": {}, + "widgetCss": "\n.tb-legend-keys td .tb-legend-line {\n height: 5px !important;\n vertical-align: middle;\n}\n", + "pageSize": 1024, + "units": "%", + "noDataDisplayMessage": "", + "displayTimewindow": true + }, + "row": 0, + "col": 0, + "id": "eace9148-b02a-48fe-1a95-acd9928aa8c5", + "typeFullFqn": "system.charts.basic_timeseries" + }, + "8acbf5df-f9fc-114d-216f-86f081aa4779": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "
\n
\n
\n
widgets.functions.title
\n
\n {{ 'widgets.functions.switch-to-pe' | translate }}\n
\n
\n
\n
widgets.functions.alarms
\n
widgets.functions.dashboards
\n
widgets.functions.entities-and-relations
\n
widgets.functions.profiles
\n
widgets.functions.advanced-features
\n
\n
\n
widgets.functions.notification-center
\n
widgets.functions.api-usage
\n
widgets.functions.customers
\n
widgets.functions.customers-hierarchy
\n
widgets.functions.roles-and-permissions
\n
\n
\n
widgets.functions.groups
\n
widgets.functions.integrations
\n
widgets.functions.solution-templates
\n
widgets.functions.scheduler
\n
widgets.functions.white-labeling
\n
\n
\n
", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".tb-card-header {\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n}\n\n.tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n margin-top: 4px;\n overflow: auto;\n}\n\n.tb-card-column {\n min-width: 0px;\n flex: 1;\n display: flex;\n flex-direction: column;\n}\n\n.feature {\n position: relative;\n cursor: default;\n font-weight: 400;\n font-size: 14px;\n line-height: 28px;\n letter-spacing: 0.2px;\n color: rgba(0, 0, 0, 0.76);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.feature:before {\n content: \"check\";\n font-family: 'Material Icons Round';\n display: inline-block;\n font-size: 20px;\n line-height: 20px;\n color: #198038;\n font-weight: 600;\n position: relative;\n background: #F3F6FA;\n border-radius: 4px;\n width: 20px;\n height: 20px;\n padding: 2px;\n vertical-align: sub;\n margin-right: 8px;\n}\n\n.feature.pe:before {\n content: \"close\";\n background: #F4F4F4;\n color: rgba(0, 0, 0, 0.12);\n}\n\n.feature.pe:after {\n position: relative;\n display: inline-block;\n margin-left: 4px;\n vertical-align: baseline;\n top: -0.5em;\n content: \"PE\";\n color: #305680;\n font-weight: 700;\n font-size: 12px;\n line-height: 12px;\n letter-spacing: 0.0015em;\n}\n\n.feature+.feature {\n margin-top: 4px;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-switch-to-pe {\n height: 24px;\n font-size: 12px;\n padding: 0 4px;\n }\n \n .feature+.feature {\n margin-top: 2px;\n }\n \n .feature {\n font-size: 11px;\n line-height: 18px;\n }\n\n .feature:before {\n font-size: 14px;\n line-height: 14px;\n width: 14px;\n height: 14px;\n margin-right: 4px;\n }\n .feature.pe:after {\n margin-left: 2px;\n top: -0.5em;\n content: \"PE\";\n font-size: 8px;\n line-height: 8px;\n }\n \n}\n\n" + }, + "title": "Functions", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "8acbf5df-f9fc-114d-216f-86f081aa4779", + "typeFullFqn": "system.cards.markdown_card" + }, + "d2784f6c-0518-fd95-1d28-b21f70bdcb10": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "
\n \n
", + "applyDefaultMarkdownStyle": false + }, + "title": "Configured features", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "d2784f6c-0518-fd95-1d28-b21f70bdcb10", + "typeFullFqn": "system.cards.markdown_card" + }, + "66dcf1a2-9d83-6873-c693-d6a5d989b3f8": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "
\n \n
", + "applyDefaultMarkdownStyle": false + }, + "title": "Version", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "66dcf1a2-9d83-6873-c693-d6a5d989b3f8", + "typeFullFqn": "system.cards.markdown_card" + }, + "867f82cf-ecf2-2d5c-35cb-08c6f2edc3a4": { + "type": "static", + "sizeX": 7.5, + "sizeY": 3, + "config": { + "datasources": [], + "timewindow": { + "realtime": { + "timewindowMs": 60000 + } + }, + "showTitle": false, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "columns": 3 + }, + "title": "Documentation", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "showLegend": false + }, + "row": 0, + "col": 0, + "id": "867f82cf-ecf2-2d5c-35cb-08c6f2edc3a4", + "typeFullFqn": "system.home_page_widgets.documentation_links" + }, + "a23185ad-dc46-806c-0e50-5b21fb080ace": { + "type": "static", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "datasources": [], + "timewindow": { + "realtime": { + "timewindowMs": 60000 + } + }, + "showTitle": false, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "columns": 3 + }, + "title": "Getting started", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "showLegend": false + }, + "row": 0, + "col": 0, + "id": "a23185ad-dc46-806c-0e50-5b21fb080ace", + "typeFullFqn": "system.home_page_widgets.getting_started" + }, + "b35fa1b0-7f95-0b23-fade-d066653a40e1": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", + "filterId": null, + "dataKeys": [ + { + "name": "cpuUsage", + "type": "timeseries", + "label": "cpuUsage", + "color": "#2196f3", + "settings": {}, + "_hash": 0.659021918423117 + }, + { + "name": "cpuCount", + "type": "timeseries", + "label": "cpuCount", + "color": "#4caf50", + "settings": {}, + "_hash": 0.5332753234169949 + }, + { + "name": "cpuUsage", + "type": "timeseries", + "label": "status", + "color": "#f44336", + "settings": {}, + "_hash": 0.025084892962804473, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "var status = 'status-ok';\nif (value > 85) {\n status = 'status-critical';\n} else if (value > 75) {\n status = 'status-warn';\n}\nreturn status;" + }, + { + "name": "cpuUsage", + "type": "timeseries", + "label": "statusTooltip", + "color": "#ffc107", + "settings": {}, + "_hash": 0.8542003006820891, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "var text = '';\nif (value > 85) {\n text = '{{ \\'widgets.system-info.cpu-critical-text\\' | translate }}';\n} else if (value > 75) {\n text = '{{ \\'widgets.system-info.cpu-warning-text\\' | translate }}';\n}\nreturn text;\n" + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "
\n
\n
widgets.system-info.cpu
\n
\n
\n
\n ${cpuUsage:0}%\n | ${cpuCount:0} cores\n
\n
", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: flex-start;\n}\n\n.tb-card-title {\n width: 100%;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-status {\n width: 24px;\n height: 24px;\n position: relative;\n}\n\n.tb-status.status-ok {\n background: #F3F6FA;\n border-radius: 4px;\n}\n\n.tb-status:after {\n position: absolute;\n top: 2px;\n left: 2px;\n font-family: 'Material Icons Round';\n font-size: 20px;\n line-height: 1;\n}\n\n.tb-status.status-ok:after {\n content: \"check\";\n color: #198038;\n font-weight: 600;\n}\n\n.tb-status.status-warn:after {\n content: \"warning\";\n color: #FAA405;\n}\n\n.tb-status.status-critical:after {\n content: \"warning\";\n color: #D12730;\n}\n\n.tb-value-container {\n user-select: none;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 26px;\n line-height: 36px;\n}\n\n.tb-total {\n padding-left: 4px;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n color: rgba(0, 0, 0, 0.54);\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-status {\n width: 18px;\n height: 18px;\n }\n .tb-status:after {\n font-size: 14px;\n }\n .tb-count {\n font-size: 16px;\n line-height: 24px;\n }\n .tb-total {\n font-size: 11px;\n line-height: 16px;\n }\n}" + }, + "title": "CPU", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "b35fa1b0-7f95-0b23-fade-d066653a40e1", + "typeFullFqn": "system.cards.markdown_card" + }, + "5eabde4c-095f-defc-dd09-6c0afb9b9f7b": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", + "filterId": null, + "dataKeys": [ + { + "name": "memoryUsage", + "type": "timeseries", + "label": "memoryUsage", + "color": "#2196f3", + "settings": {}, + "_hash": 0.659021918423117, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "totalMemory", + "type": "timeseries", + "label": "totalMemory", + "color": "#4caf50", + "settings": {}, + "_hash": 0.5332753234169949, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value / 1073741824;" + }, + { + "name": "memoryUsage", + "type": "timeseries", + "label": "status", + "color": "#f44336", + "settings": {}, + "_hash": 0.025084892962804473, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "var status = 'status-ok';\nif (value > 85) {\n status = 'status-critical';\n} else if (value > 75) {\n status = 'status-warn';\n}\nreturn status;" + }, + { + "name": "memoryUsage", + "type": "timeseries", + "label": "statusTooltip", + "color": "#ffc107", + "settings": {}, + "_hash": 0.8542003006820891, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "var text = '';\nif (value > 85) {\n text = '{{ \\'widgets.system-info.ram-critical-text\\' | translate }}';\n} else if (value > 75) {\n text = '{{ \\'widgets.system-info.ram-warning-text\\' | translate }}';\n}\nreturn text;\n" + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "useMarkdownTextFunction": true, + "markdownTextFunction": "const memoryUsage = data[0]['memoryUsage'];\nconst totalMemory = data[0]['totalMemory'];\nconst usedMemory = (totalMemory * (memoryUsage / 100)).toFixed(0);\n\n\nreturn '
' +\n '
' +\n '
widgets.system-info.ram
' +\n '
' +\n '
' +\n '
' +\n '${memoryUsage:0}%' +\n '| ${totalMemory:0} Gb' +\n '
' +\n '
';", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: flex-start;\n}\n\n.tb-card-title {\n width: 100%;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-status {\n width: 24px;\n height: 24px;\n position: relative;\n}\n\n.tb-status.status-ok {\n background: #F3F6FA;\n border-radius: 4px;\n}\n\n.tb-status:after {\n position: absolute;\n top: 2px;\n left: 2px;\n font-family: 'Material Icons Round';\n font-size: 20px;\n line-height: 1;\n}\n\n.tb-status.status-ok:after {\n content: \"check\";\n color: #198038;\n font-weight: 600;\n}\n\n.tb-status.status-warn:after {\n content: \"warning\";\n color: #FAA405;\n}\n\n.tb-status.status-critical:after {\n content: \"warning\";\n color: #D12730;\n}\n\n.tb-value-container {\n user-select: none;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 26px;\n line-height: 36px;\n}\n\n.tb-total {\n padding-left: 4px;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n color: rgba(0, 0, 0, 0.54);\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-status {\n width: 18px;\n height: 18px;\n }\n .tb-status:after {\n font-size: 14px;\n }\n .tb-count {\n font-size: 16px;\n line-height: 24px;\n }\n .tb-total {\n font-size: 11px;\n line-height: 16px;\n }\n}" + }, + "title": "RAM", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "5eabde4c-095f-defc-dd09-6c0afb9b9f7b", + "typeFullFqn": "system.cards.markdown_card" + }, + "451e2588-80b8-cd08-2199-1263ed4dc12c": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", + "filterId": null, + "dataKeys": [ + { + "name": "discUsage", + "type": "timeseries", + "label": "discUsage", + "color": "#2196f3", + "settings": {}, + "_hash": 0.659021918423117, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "totalDiscSpace", + "type": "timeseries", + "label": "totalDiscSpace", + "color": "#4caf50", + "settings": {}, + "_hash": 0.5332753234169949, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value / 1073741824;" + }, + { + "name": "discUsage", + "type": "timeseries", + "label": "status", + "color": "#f44336", + "settings": {}, + "_hash": 0.025084892962804473, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "var status = 'status-ok';\nif (value > 85) {\n status = 'status-critical';\n} else if (value > 75) {\n status = 'status-warn';\n}\nreturn status;" + }, + { + "name": "discUsage", + "type": "timeseries", + "label": "statusTooltip", + "color": "#ffc107", + "settings": {}, + "_hash": 0.8542003006820891, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "var text = '';\nif (value > 85) {\n text = '{{ \\'widgets.system-info.disk-critical-text\\' | translate }}';\n} else if (value > 75) {\n text = '{{ \\'widgets.system-info.disk-warning-text\\' | translate }}';\n}\nreturn text;" + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "useMarkdownTextFunction": true, + "markdownTextFunction": "const discUsage = data[0]['discUsage'];\nconst totalDiscSpace = data[0]['totalDiscSpace'];\nconst usedDisc = (totalDiscSpace * (discUsage / 100)).toFixed(0);\n\n\nreturn '
' +\n '
' +\n '
widgets.system-info.disk
' +\n '
' +\n '
' +\n '
' +\n '${discUsage:0}%' +\n '| ${totalDiscSpace:0} Gb' +\n '
' +\n '
';", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: flex-start;\n}\n\n.tb-card-title {\n width: 100%;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-status {\n width: 24px;\n height: 24px;\n position: relative;\n}\n\n.tb-status.status-ok {\n background: #F3F6FA;\n border-radius: 4px;\n}\n\n.tb-status:after {\n position: absolute;\n top: 2px;\n left: 2px;\n font-family: 'Material Icons Round';\n font-size: 20px;\n line-height: 1;\n}\n\n.tb-status.status-ok:after {\n content: \"check\";\n color: #198038;\n font-weight: 600;\n}\n\n.tb-status.status-warn:after {\n content: \"warning\";\n color: #FAA405;\n}\n\n.tb-status.status-critical:after {\n content: \"warning\";\n color: #D12730;\n}\n\n.tb-value-container {\n user-select: none;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 26px;\n line-height: 36px;\n}\n\n.tb-total {\n padding-left: 4px;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n color: rgba(0, 0, 0, 0.54);\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-status {\n width: 18px;\n height: 18px;\n }\n .tb-status:after {\n font-size: 14px;\n }\n .tb-count {\n font-size: 16px;\n line-height: 24px;\n }\n .tb-total {\n font-size: 11px;\n line-height: 16px;\n }\n}" + }, + "title": "Disk", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "451e2588-80b8-cd08-2199-1263ed4dc12c", + "typeFullFqn": "system.cards.markdown_card" + }, + "01b94c17-84eb-4e49-6a92-c3f108ecff8e": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", + "filterId": null, + "dataKeys": [ + { + "name": "cpuUsage", + "type": "timeseries", + "label": "cpuUsage", + "color": "#2196f3", + "settings": {}, + "_hash": 0.659021918423117 + }, + { + "name": "cpuCount", + "type": "timeseries", + "label": "cpuCount", + "color": "#4caf50", + "settings": {}, + "_hash": 0.5332753234169949 + }, + { + "name": "cpuUsage", + "type": "timeseries", + "label": "cpuStatus", + "color": "#f44336", + "settings": {}, + "_hash": 0.025084892962804473, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "var status = 'status-ok';\nif (value > 85) {\n status = 'status-critical';\n} else if (value > 75) {\n status = 'status-warn';\n}\nreturn status;" + }, + { + "name": "cpuUsage", + "type": "timeseries", + "label": "cpuStatusTooltip", + "color": "#ffc107", + "settings": {}, + "_hash": 0.8542003006820891, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "var text = '';\nif (value > 85) {\n text = '{{ \\'widgets.system-info.cpu-critical-text\\' | translate }}';\n} else if (value > 75) {\n text = '{{ \\'widgets.system-info.cpu-warning-text\\' | translate }}';\n}\nreturn text;\n" + }, + { + "name": "memoryUsage", + "type": "timeseries", + "label": "memoryUsage", + "color": "#607d8b", + "settings": {}, + "_hash": 0.9761283269385537 + }, + { + "name": "totalMemory", + "type": "timeseries", + "label": "totalMemory", + "color": "#9c27b0", + "settings": {}, + "_hash": 0.6363130990513808, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value / 1073741824;" + }, + { + "name": "memoryUsage", + "type": "timeseries", + "label": "memoryStatus", + "color": "#8bc34a", + "settings": {}, + "_hash": 0.15388812616419556, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "var status = 'status-ok';\nif (value > 85) {\n status = 'status-critical';\n} else if (value > 75) {\n status = 'status-warn';\n}\nreturn status;" + }, + { + "name": "memoryUsage", + "type": "timeseries", + "label": "memoryStatusTooltip", + "color": "#3f51b5", + "settings": {}, + "_hash": 0.8086245517967001, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "var text = '';\nif (value > 85) {\n text = '{{ \\'widgets.system-info.ram-critical-text\\' | translate }}';\n} else if (value > 75) {\n text = '{{ \\'widgets.system-info.ram-warning-text\\' | translate }}';\n}\nreturn text;" + }, + { + "name": "discUsage", + "type": "timeseries", + "label": "discUsage", + "color": "#e91e63", + "settings": {}, + "_hash": 0.4278686534236851 + }, + { + "name": "totalDiscSpace", + "type": "timeseries", + "label": "totalDiscSpace", + "color": "#ffeb3b", + "settings": {}, + "_hash": 0.543030429255416, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value / 1073741824;" + }, + { + "name": "discUsage", + "type": "timeseries", + "label": "discStatus", + "color": "#03a9f4", + "settings": {}, + "_hash": 0.29138586081227835, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "var status = 'status-ok';\nif (value > 85) {\n status = 'status-critical';\n} else if (value > 75) {\n status = 'status-warn';\n}\nreturn status;" + }, + { + "name": "discUsage", + "type": "timeseries", + "label": "discStatusTooltip", + "color": "#ff9800", + "settings": {}, + "_hash": 0.08995267596665912, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "var text = '';\nif (value > 85) {\n text = '{{ \\'widgets.system-info.disk-critical-text\\' | translate }}';\n} else if (value > 75) {\n text = '{{ \\'widgets.system-info.disk-warning-text\\' | translate }}';\n}\nreturn text;" + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "useMarkdownTextFunction": true, + "markdownTextFunction": "const memoryUsage = data[0]['memoryUsage'];\nconst totalMemory = data[0]['totalMemory'];\nconst usedMemory = (totalMemory * (memoryUsage / 100)).toFixed(0);\n\nconst discUsage = data[0]['discUsage'];\nconst totalDiscSpace = data[0]['totalDiscSpace'];\nconst usedDisc = (totalDiscSpace * (discUsage / 100)).toFixed(0);\n\n\nreturn '
' +\n '
' +\n '
' +\n '
widgets.system-info.cpu
' +\n '
' +\n '
' +\n '
' +\n '${cpuUsage:0}%' +\n '| ${cpuCount:0} cores' +\n '
' +\n '
' +\n '
' +\n '
' +\n '
widgets.system-info.ram
' +\n '
' +\n '
' +\n '
' +\n '${memoryUsage:0}%' +\n '| ${totalMemory:0} Gb' +\n '
' +\n '
' +\n '
' +\n '
' +\n '
widgets.system-info.disk
' +\n '
' +\n '
' +\n '
' +\n '${discUsage:0}%' +\n '| ${totalDiscSpace:0} Gb' +\n '
' +\n '
' +\n '
';", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n align-items: stretch;\n}\n\n.tb-card-section {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n align-items: flex-start;\n}\n\n.tb-card-section.left {\n padding-right: 12px;\n border-right: solid 1px rgba(0, 0, 0, 0.12);\n}\n\n.tb-card-section.center {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n.tb-card-section.right {\n padding-left: 12px;\n border-left: solid 1px rgba(0, 0, 0, 0.12);\n}\n\n.tb-card-title {\n width: 100%;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-status {\n width: 24px;\n height: 24px;\n position: relative;\n}\n\n.tb-status.status-ok {\n background: #F3F6FA;\n border-radius: 4px;\n}\n\n.tb-status:after {\n position: absolute;\n top: 2px;\n left: 2px;\n font-family: 'Material Icons Round';\n font-size: 20px;\n line-height: 1;\n}\n\n.tb-status.status-ok:after {\n content: \"check\";\n color: #198038;\n font-weight: 600;\n}\n\n.tb-status.status-warn:after {\n content: \"warning\";\n color: #FAA405;\n}\n\n.tb-status.status-critical:after {\n content: \"warning\";\n color: #D12730;\n}\n\n.tb-value-container {\n user-select: none;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 600;\n font-size: 20px;\n line-height: 24px;\n}\n\n.tb-total {\n padding-left: 4px;\n font-weight: 400;\n font-size: 12px;\n line-height: 16px;\n color: rgba(0, 0, 0, 0.54);\n}\n" + }, + "title": "System info mobile card", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "01b94c17-84eb-4e49-6a92-c3f108ecff8e", + "typeFullFqn": "system.cards.markdown_card" + }, + "f0479b00-ed47-0a30-0c36-bde7847aae00": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "rgba(0,0,0,0)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "
\n \n \n \n \n
", + "applyDefaultMarkdownStyle": false + }, + "title": "System info cards", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": { + "border": "none", + "boxShadow": "none", + "padding": "0" + }, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "f0479b00-ed47-0a30-0c36-bde7847aae00", + "typeFullFqn": "system.cards.markdown_card" + }, + "57ed0ad9-8fcf-4454-1fa7-d39e22f84570": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entityCount", + "name": null, + "entityAliasId": "ae870700-071f-b3bc-406c-16ba554c5a55", + "filterId": null, + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "tenantsCount", + "color": "#2196f3", + "settings": {}, + "_hash": 0.8491768696709192, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "
\n \n
\n \n add\n \n
\n
", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n}\n\n.tb-content-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n}\n\n.tb-small-button-container {\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.tb-card-header {\n height: 36px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 28px;\n line-height: 36px;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-count {\n font-size: 18px;\n line-height: 24px;\n }\n}\n" + }, + "title": "Tenants", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "57ed0ad9-8fcf-4454-1fa7-d39e22f84570", + "typeFullFqn": "system.cards.markdown_card" + }, + "0464b6bc-7863-ba75-4b84-64b8acae2af9": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entityCount", + "name": null, + "entityAliasId": "ca4d90e4-f9ae-6fca-6b09-85815a48d52b", + "filterId": null, + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "tenantProfilesCount", + "color": "#2196f3", + "settings": {}, + "_hash": 0.8491768696709192, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "\n", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n}\n\n.tb-content-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n}\n\n.tb-small-button-container {\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.tb-card-header {\n height: 36px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 28px;\n line-height: 36px;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-count {\n font-size: 18px;\n line-height: 24px;\n }\n}\n" + }, + "title": "Tenant Profiles", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "0464b6bc-7863-ba75-4b84-64b8acae2af9", + "typeFullFqn": "system.cards.markdown_card" + }, + "4c043bba-6ac1-dd23-0796-fe6ae4c3c0a3": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entityCount", + "name": null, + "entityAliasId": "a1ddb8fa-90ff-5598-e7f2-e254194d055d", + "filterId": null, + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "devicesCount", + "color": "#2196f3", + "settings": {}, + "_hash": 0.8491768696709192, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "
\n
\n
{{ 'device.devices' | translate }}
\n
\n
${devicesCount:0}
\n
", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 26px;\n line-height: 36px;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-count {\n font-size: 18px;\n line-height: 24px;\n }\n}" + }, + "title": "Devices", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "4c043bba-6ac1-dd23-0796-fe6ae4c3c0a3", + "typeFullFqn": "system.cards.markdown_card" + }, + "8b8d5d4f-2c5f-c768-d4fb-68d6e0b716f4": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entityCount", + "name": null, + "entityAliasId": "619cdf00-a042-3b55-124e-194c1b28c236", + "filterId": null, + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "assetsCount", + "color": "#2196f3", + "settings": {}, + "_hash": 0.8491768696709192, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "
\n
\n
{{ 'asset.assets' | translate }}
\n
\n
${assetsCount:0}
\n
", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 26px;\n line-height: 36px;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-count {\n font-size: 18px;\n line-height: 24px;\n }\n}" + }, + "title": "Assets", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "8b8d5d4f-2c5f-c768-d4fb-68d6e0b716f4", + "typeFullFqn": "system.cards.markdown_card" + }, + "d58bfdd4-18b0-a70c-5d5f-73d12c2ea468": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entityCount", + "name": null, + "entityAliasId": "1d97ff7f-8b42-5882-f87b-16f3d0dee4f2", + "filterId": null, + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "usersCount", + "color": "#2196f3", + "settings": {}, + "_hash": 0.8491768696709192, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "
\n
\n
{{ 'user.users' | translate }}
\n
\n
${usersCount:0}
\n
", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 26px;\n line-height: 36px;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-count {\n font-size: 18px;\n line-height: 24px;\n }\n}\n" + }, + "title": "Users", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "d58bfdd4-18b0-a70c-5d5f-73d12c2ea468", + "typeFullFqn": "system.cards.markdown_card" + }, + "a3d83738-a4f2-ea92-17d7-1a3c52aa1770": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entityCount", + "name": null, + "entityAliasId": "0dd2b154-59f4-0f97-da1a-d85f5b5cfe31", + "filterId": null, + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "customersCount", + "color": "#2196f3", + "settings": {}, + "_hash": 0.8491768696709192, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "
\n
\n
{{ 'customer.customers' | translate }}
\n
\n
${customersCount:0}
\n
", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 26px;\n line-height: 36px;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-count {\n font-size: 18px;\n line-height: 24px;\n }\n}" + }, + "title": "Customers", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "a3d83738-a4f2-ea92-17d7-1a3c52aa1770", + "typeFullFqn": "system.cards.markdown_card" + }, + "ff4f04a2-f739-2f3e-17dc-9ede9f2df75d": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entityCount", + "name": null, + "entityAliasId": "ae870700-071f-b3bc-406c-16ba554c5a55", + "filterId": null, + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "tenantsCount", + "color": "#2196f3", + "settings": {}, + "_hash": 0.8491768696709192, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + }, + { + "type": "entityCount", + "name": null, + "entityAliasId": "ca4d90e4-f9ae-6fca-6b09-85815a48d52b", + "filterId": null, + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "tenantProfilesCount", + "color": "#4caf50", + "settings": {}, + "_hash": 0.04291827117688696, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + }, + { + "type": "entityCount", + "name": null, + "entityAliasId": "a1ddb8fa-90ff-5598-e7f2-e254194d055d", + "filterId": null, + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "devicesCount", + "color": "#f44336", + "settings": {}, + "_hash": 0.45105711028955886, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + }, + { + "type": "entityCount", + "name": null, + "entityAliasId": "619cdf00-a042-3b55-124e-194c1b28c236", + "filterId": null, + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "assetsCount", + "color": "#ffc107", + "settings": {}, + "_hash": 0.17028494648519876, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + }, + { + "type": "entityCount", + "name": null, + "entityAliasId": "1d97ff7f-8b42-5882-f87b-16f3d0dee4f2", + "filterId": null, + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "usersCount", + "color": "#607d8b", + "settings": {}, + "_hash": 0.0362458342595664, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + }, + { + "type": "entityCount", + "name": null, + "entityAliasId": "0dd2b154-59f4-0f97-da1a-d85f5b5cfe31", + "filterId": null, + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "customersCount", + "color": "#9c27b0", + "settings": {}, + "_hash": 0.7596585662918276, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "
\n \n
\n
\n
${devicesCount:0}
\n
{{ 'device.devices' | translate }}
\n
\n
\n
${assetsCount:0}
\n
{{ 'asset.assets' | translate }}
\n
\n
\n
${usersCount:0}
\n
{{ 'user.users' | translate }}
\n
\n
\n
${customersCount:0}
\n
{{ 'customer.customers' | translate }}
\n
\n
\n
\n", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n align-items: stretch;\n}\n\n.tb-card-section {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: space-evenly;\n align-items: stretch;\n}\n\n.tb-card-section.left {\n border-right: solid 1px rgba(0, 0, 0, 0.12);\n}\n\n.tb-card-section.right .tb-entity-section {\n padding-left: 40px;\n}\n\n.tb-card-section.right .tb-count {\n min-width: 24px;\n text-align: right;\n}\n\n.tb-button-section {\n display: flex;\n padding-right: 40px;\n flex-direction: row;\n justify-content: flex-end;\n align-items: center;\n}\n\n.tb-entity-section {\n display: flex;\n flex-direction: row;\n justify-content: flex-start;\n align-items: center;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 600;\n font-size: 20px;\n line-height: 24px;\n margin-right: 12px;\n}\n\n.tb-entity-title {\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n color: rgba(0, 0, 0, 0.54);\n}\n\n" + }, + "title": "Entities info mobile card", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "ff4f04a2-f739-2f3e-17dc-9ede9f2df75d", + "typeFullFqn": "system.cards.markdown_card" + }, + "163025d8-3ca4-dd1e-c17b-e40d8e03e8a8": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "rgba(0,0,0,0)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "
\n \n \n \n \n
", + "applyDefaultMarkdownStyle": false + }, + "title": "Entities info cards", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": { + "border": "none", + "boxShadow": "none", + "padding": "0" + }, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "163025d8-3ca4-dd1e-c17b-e40d8e03e8a8", + "typeFullFqn": "system.cards.markdown_card" + } + }, + "states": { + "default": { + "name": "System Administrator Home Page", + "root": true, + "layouts": { + "main": { + "widgets": { + "d70cc256-4c7b-ee06-9905-b8c5e546605f": { + "sizeX": 41, + "sizeY": 16, + "row": 26, + "col": 44, + "mobileOrder": 7, + "mobileHeight": 8 + }, + "4b5e47ed-c197-a937-d727-041ba8decec2": { + "sizeX": 41, + "sizeY": 26, + "row": 0, + "col": 44, + "mobileOrder": 6, + "mobileHeight": 11 + }, + "8acbf5df-f9fc-114d-216f-86f081aa4779": { + "sizeX": 52, + "sizeY": 16, + "row": 42, + "col": 33, + "mobileHide": true + }, + "d2784f6c-0518-fd95-1d28-b21f70bdcb10": { + "sizeX": 44, + "sizeY": 13, + "row": 29, + "col": 0, + "mobileOrder": 8, + "mobileHeight": 8 + }, + "66dcf1a2-9d83-6873-c693-d6a5d989b3f8": { + "sizeX": 33, + "sizeY": 16, + "row": 42, + "col": 0, + "mobileOrder": 9, + "mobileHeight": 7 + }, + "867f82cf-ecf2-2d5c-35cb-08c6f2edc3a4": { + "sizeX": 44, + "sizeY": 14, + "row": 15, + "col": 0, + "mobileHide": true + }, + "a23185ad-dc46-806c-0e50-5b21fb080ace": { + "sizeX": 35, + "sizeY": 58, + "row": 0, + "col": 85, + "mobileHide": true + }, + "163025d8-3ca4-dd1e-c17b-e40d8e03e8a8": { + "sizeX": 44, + "sizeY": 15, + "mobileOrder": 0, + "mobileHeight": 7, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 120, + "margin": 12, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 20, + "outerMargin": true + } + } + } + }, + "transport_messages": { + "name": "Transport messages", + "root": false, + "layouts": { + "main": { + "widgets": { + "8ee72d43-678c-4e25-e9a8-7d4cfd7a5f8e": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#ffffff", + "columns": 24, + "margin": 0, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } + }, + "monolith_system_info": { + "name": "Monolith System Info", + "root": false, + "layouts": { + "main": { + "widgets": { + "eace9148-b02a-48fe-1a95-acd9928aa8c5": { + "sizeX": 48, + "sizeY": 20, + "row": 8, + "col": 0, + "mobileOrder": 1, + "mobileHeight": 10 + }, + "f0479b00-ed47-0a30-0c36-bde7847aae00": { + "sizeX": 48, + "sizeY": 8, + "mobileOrder": 0, + "mobileHeight": 4, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "rgba(0,0,0,0)", + "columns": 48, + "margin": 12, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70, + "outerMargin": false + } + } + } + }, + "system_info_desktop": { + "name": "System Info Desktop", + "root": false, + "layouts": { + "main": { + "widgets": { + "b35fa1b0-7f95-0b23-fade-d066653a40e1": { + "sizeX": 8, + "sizeY": 3, + "row": 0, + "col": 0 + }, + "5eabde4c-095f-defc-dd09-6c0afb9b9f7b": { + "sizeX": 8, + "sizeY": 3, + "row": 0, + "col": 8 + }, + "451e2588-80b8-cd08-2199-1263ed4dc12c": { + "sizeX": 8, + "sizeY": 3, + "row": 0, + "col": 16 + } + }, + "gridSettings": { + "backgroundColor": "rgba(0,0,0,0)", + "columns": 24, + "margin": 12, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70, + "outerMargin": false + } + } + } + }, + "system_info_mobile": { + "name": "System Info Mobile", + "root": false, + "layouts": { + "main": { + "widgets": { + "01b94c17-84eb-4e49-6a92-c3f108ecff8e": { + "sizeX": 24, + "sizeY": 3, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "rgba(0,0,0,0)", + "columns": 24, + "margin": 12, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70, + "outerMargin": false + } + } + } + }, + "entities_info_desktop": { + "name": "Entities Info Desktop", + "root": false, + "layouts": { + "main": { + "widgets": { + "57ed0ad9-8fcf-4454-1fa7-d39e22f84570": { + "sizeX": 60, + "sizeY": 29, + "row": 0, + "col": 0 + }, + "0464b6bc-7863-ba75-4b84-64b8acae2af9": { + "sizeX": 60, + "sizeY": 29, + "row": 0, + "col": 60 + }, + "4c043bba-6ac1-dd23-0796-fe6ae4c3c0a3": { + "sizeX": 30, + "sizeY": 26, + "row": 29, + "col": 0 + }, + "8b8d5d4f-2c5f-c768-d4fb-68d6e0b716f4": { + "sizeX": 30, + "sizeY": 26, + "row": 29, + "col": 30 + }, + "d58bfdd4-18b0-a70c-5d5f-73d12c2ea468": { + "sizeX": 30, + "sizeY": 26, + "row": 29, + "col": 60 + }, + "a3d83738-a4f2-ea92-17d7-1a3c52aa1770": { + "sizeX": 30, + "sizeY": 26, + "row": 29, + "col": 90 + } + }, + "gridSettings": { + "backgroundColor": "rgba(0,0,0,0)", + "columns": 120, + "margin": 12, + "outerMargin": false, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "entities_info_mobile": { + "name": "Entities Info Mobile", + "root": false, + "layouts": { + "main": { + "widgets": { + "ff4f04a2-f739-2f3e-17dc-9ede9f2df75d": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "rgba(0,0,0,0)", + "columns": 24, + "margin": 12, + "outerMargin": false, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70 + } + } + } + } + }, + "entityAliases": { + "ae870700-071f-b3bc-406c-16ba554c5a55": { + "id": "ae870700-071f-b3bc-406c-16ba554c5a55", + "alias": "Tenants", + "filter": { + "type": "entityType", + "resolveMultiple": true, + "entityType": "TENANT" + } + }, + "ca4d90e4-f9ae-6fca-6b09-85815a48d52b": { + "id": "ca4d90e4-f9ae-6fca-6b09-85815a48d52b", + "alias": "TenantProfiles", + "filter": { + "type": "entityType", + "resolveMultiple": true, + "entityType": "TENANT_PROFILE" + } + }, + "a1ddb8fa-90ff-5598-e7f2-e254194d055d": { + "id": "a1ddb8fa-90ff-5598-e7f2-e254194d055d", + "alias": "Devices", + "filter": { + "type": "entityType", + "resolveMultiple": true, + "entityType": "DEVICE" + } + }, + "619cdf00-a042-3b55-124e-194c1b28c236": { + "id": "619cdf00-a042-3b55-124e-194c1b28c236", + "alias": "Assets", + "filter": { + "type": "entityType", + "resolveMultiple": true, + "entityType": "ASSET" + } + }, + "1d97ff7f-8b42-5882-f87b-16f3d0dee4f2": { + "id": "1d97ff7f-8b42-5882-f87b-16f3d0dee4f2", + "alias": "Users", + "filter": { + "type": "entityType", + "resolveMultiple": true, + "entityType": "USER" + } + }, + "0dd2b154-59f4-0f97-da1a-d85f5b5cfe31": { + "id": "0dd2b154-59f4-0f97-da1a-d85f5b5cfe31", + "alias": "Customers", + "filter": { + "type": "entityType", + "resolveMultiple": true, + "entityType": "CUSTOMER" + } + }, + "d9229b29-3f46-de8d-7fe8-eb0c43c75079": { + "id": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", + "alias": "Api Usage State", + "filter": { + "type": "apiUsageState", + "resolveMultiple": true + } + } + }, + "filters": {}, + "timewindow": { + "displayValue": "", + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168326072, + "endTimeMs": 1680254726072 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "settings": { + "stateControllerId": "entity", + "showTitle": false, + "showDashboardsSelect": true, + "showEntitiesSelect": true, + "showDashboardTimewindow": true, + "showDashboardExport": true, + "toolbarAlwaysOpen": true, + "titleColor": "rgba(0,0,0,0.870588)", + "showDashboardLogo": false, + "dashboardLogoUrl": null, + "hideToolbar": true, + "showFilters": true, + "showUpdateDashboardImage": true, + "dashboardCss": ".tb-widget-container > .tb-widget {\n border: 1px solid rgba(0, 0, 0, 0.05);\n box-shadow: 0px 5px 16px rgba(0, 0, 0, 0.04);\n border-radius: 12px;\n}\n\n.tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 16px !important;\n}\n\n.tb-card-title {\n display: grid;\n}\n\n.tb-home-widget-title {\n font-style: normal;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n color: rgba(0, 0, 0, 0.54);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.tb-home-widget-link {\n position: relative;\n border-bottom: none;\n}\n\n.tb-home-widget-link:hover {\n border-bottom: none;\n}\n\n.tb-home-widget-link:focus {\n border-bottom: none;\n}\n\n.tb-home-widget-link::after {\n content: 'arrow_forward';\n display: inline-block;\n transform: rotate(315deg);\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n font-size: 18px;\n color: rgba(0, 0, 0, 0.12);\n vertical-align: bottom;\n margin-left: 6px; \n}\n\n.tb-home-widget-link:hover::after {\n color: inherit;\n}\n\n.tb-home-widget-info-icon {\n color: rgba(0, 0, 0, 0.12);\n font-size: 16px;\n width: 16px;\n height: 16px;\n line-height: 15px;\n vertical-align: middle;\n}\n\n.tb-widget-container > .tb-widget .tb-timewindow {\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n color: rgba(0, 0, 0, 0.54);\n padding: 0;\n}\n\n.tb-widget-container > .tb-widget .tb-legend-keys .tb-legend-label {\n cursor: pointer;\n user-select: none;\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n color: rgba(0, 0, 0, 0.54);\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .tb-widget-container > .tb-widget {\n border-radius: 4px;\n }\n .tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 2px !important;\n }\n .tb-hide-md {\n display: none;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1819px) {\n .tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 8px !important;\n }\n .tb-hide-lg {\n display: none;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-hide-md-lg {\n display: none;\n }\n\n .tb-home-widget-title {\n font-size: 12px;\n line-height: 16px;\n }\n \n .tb-widget-container > .tb-widget .tb-widget-title {\n padding: 0;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow {\n font-size: 12px;\n line-height: 16px;\n min-height: 24px;\n padding: 0;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow .mat-mdc-icon-button.tb-mat-32 {\n width: 24px;\n height: 24px;\n line-height: 24px;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow .mat-mdc-icon-button.tb-mat-32 .mat-icon {\n width: 18px;\n height: 18px;\n font-size: 18px;\n }\n \n .tb-widget-container > .tb-widget tb-legend {\n padding-bottom: 0 !important;\n }\n \n .tb-widget-container > .tb-widget .tb-legend-keys .tb-legend-label {\n font-size: 11px;\n line-height: 16px;\n letter-spacing: 0.25px;\n }\n}\n\n@media screen and (max-width: 959px), screen and (min-width: 1820px) {\n .tb-hide-not-md-lg {\n display: none;\n }\n}\n" + } + }, + "externalId": null, + "name": "System Administrator Home Page" +} \ No newline at end of file diff --git a/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json b/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json new file mode 100644 index 0000000000..37e8d82e45 --- /dev/null +++ b/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json @@ -0,0 +1,1054 @@ +{ + "title": "Tenant Administrator Home Page", + "image": null, + "mobileHide": false, + "mobileOrder": null, + "configuration": { + "description": "", + "widgets": { + "d70cc256-4c7b-ee06-9905-b8c5e546605f": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "
\n
\n
{{ 'widgets.activity.title' | translate }}
\n \n {{ 'device.devices' | translate }}\n {{ 'widgets.transport-messages.title' | translate }}\n \n
\n \n \n \n \n \n \n \n \n
", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n}\n" + }, + "title": "Transport messages", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "d70cc256-4c7b-ee06-9905-b8c5e546605f", + "typeFullFqn": "system.cards.markdown_card" + }, + "8ee72d43-678c-4e25-e9a8-7d4cfd7a5f8e": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", + "filterId": null, + "dataKeys": [ + { + "name": "transportMsgCountHourly", + "type": "timeseries", + "label": "{i18n:widgets.transport-messages.title}", + "color": "#305680", + "settings": {}, + "_hash": 0.2880464219129071, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "latestDataKeys": [] + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": true, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000, + "fixedTimewindow": { + "startTimeMs": 1680443065451, + "endTimeMs": 1680529465451 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "SUM", + "limit": 50000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "stack": true, + "fontSize": 10, + "fontColor": "#545454", + "showTooltip": true, + "tooltipIndividual": false, + "tooltipCumulative": false, + "hideZeros": false, + "grid": { + "verticalLines": false, + "horizontalLines": false, + "outlineWidth": 0, + "color": "#545454", + "backgroundColor": null, + "tickColor": "#DDDDDD" + }, + "xaxis": { + "title": null, + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "min": 0, + "max": null, + "title": null, + "showLabels": true, + "color": "#545454", + "tickSize": null, + "tickDecimals": 0, + "ticksFormatter": "return value % 1000 === 0 ? ((value / 1000) + 'k') : '';" + }, + "defaultBarWidth": 1800000, + "barAlignment": "left", + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "xaxisSecond": { + "axisPosition": "top", + "title": null, + "showLabels": true + }, + "customLegendEnabled": false, + "dataKeysListForLabels": [], + "showLegend": false, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": true, + "showTotal": false, + "showLatest": false + } + }, + "title": "Transport messages", + "dropShadow": false, + "enableFullscreen": false, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "widgetStyle": { + "padding": "0" + }, + "useDashboardTimewindow": false, + "actions": {}, + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetCss": ".tb-widget-container > .tb-widget {\n border: none !important;\n border-radius: 0 !important;\n box-shadow: none !important;\n}\n\n.tb-widget-container > .tb-widget .flot-base {\n opacity: 0.48;\n}\n", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "8ee72d43-678c-4e25-e9a8-7d4cfd7a5f8e", + "typeFullFqn": "system.charts.timeseries_bars_flot" + }, + "867f82cf-ecf2-2d5c-35cb-08c6f2edc3a4": { + "type": "static", + "sizeX": 7.5, + "sizeY": 3, + "config": { + "datasources": [], + "timewindow": { + "realtime": { + "timewindowMs": 60000 + } + }, + "showTitle": false, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "columns": 2 + }, + "title": "Documentation", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "showLegend": false + }, + "row": 0, + "col": 0, + "id": "867f82cf-ecf2-2d5c-35cb-08c6f2edc3a4", + "typeFullFqn": "system.home_page_widgets.documentation_links" + }, + "a23185ad-dc46-806c-0e50-5b21fb080ace": { + "type": "static", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "datasources": [], + "timewindow": { + "realtime": { + "timewindowMs": 60000 + } + }, + "showTitle": false, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "columns": 3 + }, + "title": "Getting started", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "showLegend": false + }, + "row": 0, + "col": 0, + "id": "a23185ad-dc46-806c-0e50-5b21fb080ace", + "typeFullFqn": "system.home_page_widgets.getting_started" + }, + "d26e5cd7-75ef-d475-00c7-1a2d1114efe8": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", + "filterId": null, + "dataKeys": [ + { + "name": "activeDevicesCountHourly", + "type": "timeseries", + "label": "{i18n:device.devices}", + "color": "#305680", + "settings": { + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "excludeFromStacking": false, + "showLines": true, + "lineWidth": 3, + "fillLines": true, + "fillLinesOpacity": 0.04, + "showPoints": false, + "showSeparateAxis": false, + "axisPosition": "left", + "comparisonSettings": { + "showValuesForComparison": true, + "comparisonValuesLabel": "", + "color": "" + }, + "thresholds": [] + }, + "_hash": 0.9688095820365725, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "latestDataKeys": [] + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": true, + "hideAggInterval": true, + "hideTimezone": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 7200000, + "fixedTimewindow": { + "startTimeMs": 1681400576338, + "endTimeMs": 1681486976338 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "stack": false, + "fontSize": 10, + "fontColor": "#545454", + "showTooltip": true, + "tooltipIndividual": false, + "tooltipCumulative": false, + "hideZeros": false, + "grid": { + "verticalLines": false, + "horizontalLines": false, + "outlineWidth": 0, + "color": "#545454", + "backgroundColor": null, + "tickColor": "#DDDDDD" + }, + "xaxis": { + "title": null, + "showLabels": true, + "color": "#545454" + }, + "yaxis": { + "min": null, + "max": null, + "title": null, + "showLabels": true, + "color": "#545454", + "tickSize": null, + "tickDecimals": 0, + "ticksFormatter": "" + }, + "shadowSize": 0, + "smoothLines": true, + "comparisonEnabled": false, + "timeForComparison": "previousInterval", + "comparisonCustomIntervalValue": 7200000, + "xaxisSecond": { + "axisPosition": "top", + "title": null, + "showLabels": true + }, + "customLegendEnabled": false, + "dataKeysListForLabels": [], + "showLegend": false, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": true, + "showTotal": false, + "showLatest": false + } + }, + "title": "Devices activity", + "dropShadow": false, + "enableFullscreen": false, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "useDashboardTimewindow": false, + "displayTimewindow": true, + "showTitleIcon": false, + "titleTooltip": "", + "widgetStyle": { + "padding": "0" + }, + "widgetCss": ".tb-widget-container > .tb-widget {\n border: none !important;\n border-radius: 0 !important;\n box-shadow: none !important;\n}\n", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "d26e5cd7-75ef-d475-00c7-1a2d1114efe8", + "typeFullFqn": "system.charts.basic_timeseries" + }, + "ebbd0a6e-8a47-e770-5086-7f4974250f2d": { + "type": "static", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "showTitle": false, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": {}, + "title": "New Usage info", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "showLegend": false, + "datasources": [] + }, + "row": 0, + "col": 0, + "id": "ebbd0a6e-8a47-e770-5086-7f4974250f2d", + "typeFullFqn": "system.home_page_widgets.usage_info" + }, + "9e3ef045-d8bc-1640-a3f4-2dd10b19d50e": { + "type": "static", + "sizeX": 7.5, + "sizeY": 3, + "config": { + "showTitle": false, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "columns": 2 + }, + "title": "Quick links", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "showLegend": false, + "datasources": [] + }, + "row": 0, + "col": 0, + "id": "9e3ef045-d8bc-1640-a3f4-2dd10b19d50e", + "typeFullFqn": "system.home_page_widgets.quick_links" + }, + "6d6e2b1d-6ce7-4678-3745-c6b0897b2674": { + "type": "static", + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "showTitle": false, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": {}, + "title": "Dashboards", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": {}, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "showLegend": false, + "datasources": [] + }, + "row": 0, + "col": 0, + "id": "6d6e2b1d-6ce7-4678-3745-c6b0897b2674", + "typeFullFqn": "system.home_page_widgets.dashboards" + }, + "35d3ac3b-f1af-dc3d-a1d1-11f351d16fc6": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entityCount", + "name": null, + "entityAliasId": "a1ddb8fa-90ff-5598-e7f2-e254194d055d", + "filterId": null, + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "totalDevices", + "color": "#2196f3", + "settings": {}, + "_hash": 0.8491768696709192, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + }, + { + "type": "entityCount", + "name": null, + "entityAliasId": "a1ddb8fa-90ff-5598-e7f2-e254194d055d", + "filterId": "42f42812-92fe-a13a-b0ba-c6e0dfab3584", + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "activeDevices", + "color": "#4caf50", + "settings": {}, + "_hash": 0.1262449138010293, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + }, + { + "type": "entityCount", + "name": null, + "entityAliasId": "a1ddb8fa-90ff-5598-e7f2-e254194d055d", + "filterId": "39d92c21-2426-7346-4484-452829146c33", + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "inactiveDevices", + "color": "#f44336", + "settings": {}, + "_hash": 0.39119172615806797, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n}\n\n.tb-content-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n gap: 12px;\n}\n\n.tb-card-header {\n height: 36px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-item-cards {\n flex: 1;\n display: flex;\n flex-direction: row;\n gap: 12px;\n overflow: hidden;\n}\n\na.tb-item-card {\n flex: 1;\n display: flex;\n flex-direction: column;\n padding: 8px 12px;\n border: 1px solid;\n border-radius: 10px;\n margin-bottom: 12px;\n overflow: hidden;\n justify-content: space-evenly;\n}\n\na.tb-item-card.tb-inactive {\n background: rgba(209, 39, 48, 0.04);\n border-color: rgba(209, 39, 48, 0.06);\n}\n\na.tb-item-card.tb-active {\n background: rgba(48, 86, 128, 0.04);\n border-color: rgba(48, 86, 128, 0.12);\n}\n\na.tb-item-card.tb-total {\n background: rgba(0, 0, 0, 0.01);\n border-color: rgba(0, 0, 0, 0.05);\n}\n\n.tb-item-title-container {\n display: grid;\n}\n\n.tb-item-title {\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis; \n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-item-title.tb-home-widget-link:after {\n position: absolute;\n right: 0;\n}\n\na.tb-item-card:hover .tb-item-title.tb-home-widget-link:after { \n color: rgba(0, 0, 0, 0.38);\n}\n\na.tb-item-card:hover {\n box-shadow: 0px 4px 10px rgba(23, 33, 90, 0.08);\n}\n\n.tb-count-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 24px;\n line-height: 36px;\n white-space: nowrap;\n color: rgba(0, 0, 0, 0.87);\n}\n\n@media screen and (max-width: 959px) {\n .tb-item-cards {\n flex-direction: column;\n }\n a.tb-item-card {\n margin-bottom: 0;\n }\n}\n\n@media screen and (max-width: 1279px) {\n a.tb-item-card {\n flex-direction: row;\n align-items: center;\n }\n .tb-item-title.tb-home-widget-link:after {\n position: relative;\n }\n .tb-count-container {\n align-items: flex-end;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-item-title {\n font-size: 11px;\n line-height: 16px;\n }\n .tb-count {\n font-size: 16px;\n line-height: 24px;\n }\n a.tb-item-card {\n padding: 4px 8px;\n margin-bottom: 6px;\n }\n a.tb-item-card:hover {\n box-shadow: 0px 2px 5px rgba(23, 33, 90, 0.08);\n }\n}\n" + }, + "title": "Devices", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "35d3ac3b-f1af-dc3d-a1d1-11f351d16fc6", + "typeFullFqn": "system.cards.markdown_card" + }, + "7ac20b6a-dc40-b18e-9f5f-bca20bc693bb": { + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "alarmCount", + "name": null, + "entityAliasId": null, + "filterId": null, + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "totalAlarms", + "color": "#ffc107", + "settings": {}, + "_hash": 0.8743321483507485, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + }, + { + "type": "alarmCount", + "name": null, + "entityAliasId": null, + "filterId": null, + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "assignedToMeAlarms", + "color": "#ffc107", + "settings": {}, + "_hash": 0.6392390736755882, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ], + "assignedToCurrentUser": true + } + }, + { + "type": "alarmCount", + "name": null, + "entityAliasId": null, + "filterId": null, + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "criticalAlarms", + "color": "#ffc107", + "settings": {}, + "_hash": 0.8329478098552843, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ], + "severityList": [ + "CRITICAL" + ] + } + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168340431, + "endTimeMs": 1680254740431 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "16px", + "settings": { + "useMarkdownTextFunction": false, + "markdownTextPattern": "", + "applyDefaultMarkdownStyle": false, + "markdownCss": ".tb-card-content {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: row;\n}\n\n.tb-content-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n gap: 12px;\n}\n\n.tb-card-header {\n height: 36px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n}\n\n.tb-item-cards {\n flex: 1;\n display: flex;\n flex-direction: row;\n gap: 12px;\n overflow: hidden;\n}\n\na.tb-item-card {\n flex: 1;\n display: flex;\n flex-direction: column;\n padding: 8px 12px;\n border: 1px solid;\n border-radius: 10px;\n margin-bottom: 12px;\n overflow: hidden;\n justify-content: space-evenly;\n}\n\na.tb-item-card.tb-critical {\n background: rgba(209, 39, 48, 0.04);\n border-color: rgba(209, 39, 48, 0.06);\n}\n\na.tb-item-card.tb-assigned {\n background: rgba(48, 86, 128, 0.04);\n border-color: rgba(48, 86, 128, 0.12);\n}\n\na.tb-item-card.tb-total {\n background: rgba(0, 0, 0, 0.01);\n border-color: rgba(0, 0, 0, 0.05);\n}\n\n.tb-item-title-container {\n display: grid;\n}\n\n.tb-item-title {\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis; \n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-item-title.tb-home-widget-link:after {\n position: absolute;\n right: 0;\n}\n\na.tb-item-card:hover .tb-item-title.tb-home-widget-link:after { \n color: rgba(0, 0, 0, 0.38);\n}\n\na.tb-item-card:hover {\n box-shadow: 0px 4px 10px rgba(23, 33, 90, 0.08);\n}\n\n.tb-count-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n}\n\n.tb-count {\n font-style: normal;\n font-weight: 500;\n font-size: 24px;\n line-height: 36px;\n white-space: nowrap;\n color: rgba(0, 0, 0, 0.87);\n}\n\na.tb-item-card.tb-critical .tb-count:after {\n content: \"warning\";\n display: inline-block;\n position: relative;\n font-family: 'Material Icons Round';\n color: #D12730;\n vertical-align: bottom;\n margin-left: 6px;\n}\n\n@media screen and (max-width: 959px) {\n .tb-item-cards {\n flex-direction: column;\n }\n a.tb-item-card {\n margin-bottom: 0;\n }\n}\n\n@media screen and (max-width: 1279px) {\n a.tb-item-card {\n flex-direction: row;\n align-items: center;\n }\n .tb-item-title.tb-home-widget-link:after {\n position: relative;\n }\n .tb-count-container {\n align-items: flex-end;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-item-title {\n font-size: 11px;\n line-height: 16px;\n }\n .tb-count {\n font-size: 16px;\n line-height: 24px;\n }\n a.tb-item-card {\n padding: 4px 8px;\n margin-bottom: 6px;\n }\n a.tb-item-card:hover {\n box-shadow: 0px 2px 5px rgba(23, 33, 90, 0.08);\n }\n a.tb-item-card.tb-critical .tb-count:after {\n margin-left: 2px;\n }\n}\n" + }, + "title": "Alarms", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": false, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "" + }, + "row": 0, + "col": 0, + "id": "7ac20b6a-dc40-b18e-9f5f-bca20bc693bb", + "typeFullFqn": "system.cards.markdown_card" + } + }, + "states": { + "default": { + "name": "Tenant Administrator Home Page", + "root": true, + "layouts": { + "main": { + "widgets": { + "d70cc256-4c7b-ee06-9905-b8c5e546605f": { + "sizeX": 42, + "sizeY": 29, + "row": 13, + "col": 43, + "mobileOrder": 4, + "mobileHeight": 10 + }, + "867f82cf-ecf2-2d5c-35cb-08c6f2edc3a4": { + "sizeX": 29, + "sizeY": 16, + "row": 42, + "col": 28, + "mobileHide": true + }, + "a23185ad-dc46-806c-0e50-5b21fb080ace": { + "sizeX": 35, + "sizeY": 58, + "row": 0, + "col": 85, + "mobileHide": true + }, + "ebbd0a6e-8a47-e770-5086-7f4974250f2d": { + "sizeX": 28, + "sizeY": 16, + "row": 42, + "col": 57, + "mobileOrder": 3, + "mobileHeight": 10 + }, + "9e3ef045-d8bc-1640-a3f4-2dd10b19d50e": { + "sizeX": 28, + "sizeY": 16, + "row": 42, + "col": 0, + "mobileHide": true + }, + "6d6e2b1d-6ce7-4678-3745-c6b0897b2674": { + "sizeX": 43, + "sizeY": 29, + "row": 13, + "col": 0, + "mobileOrder": 2, + "mobileHeight": 10 + }, + "35d3ac3b-f1af-dc3d-a1d1-11f351d16fc6": { + "sizeX": 43, + "sizeY": 13, + "row": 0, + "col": 0, + "mobileHeight": 9, + "mobileOrder": 1 + }, + "7ac20b6a-dc40-b18e-9f5f-bca20bc693bb": { + "sizeX": 42, + "sizeY": 13, + "mobileHeight": 9, + "row": 0, + "col": 43, + "mobileOrder": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "columns": 120, + "margin": 12, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 20, + "outerMargin": true + } + } + } + }, + "transport_messages": { + "name": "Transport messages", + "root": false, + "layouts": { + "main": { + "widgets": { + "8ee72d43-678c-4e25-e9a8-7d4cfd7a5f8e": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#ffffff", + "columns": 24, + "margin": 0, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } + }, + "devices_activity": { + "name": "Devices activity", + "root": false, + "layouts": { + "main": { + "widgets": { + "d26e5cd7-75ef-d475-00c7-1a2d1114efe8": { + "sizeX": 24, + "sizeY": 11, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#ffffff", + "columns": 24, + "margin": 0, + "outerMargin": true, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70 + } + } + } + } + }, + "entityAliases": { + "a1ddb8fa-90ff-5598-e7f2-e254194d055d": { + "id": "a1ddb8fa-90ff-5598-e7f2-e254194d055d", + "alias": "Devices", + "filter": { + "type": "entityType", + "resolveMultiple": true, + "entityType": "DEVICE" + } + }, + "d9229b29-3f46-de8d-7fe8-eb0c43c75079": { + "id": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", + "alias": "Api Usage State", + "filter": { + "type": "apiUsageState", + "resolveMultiple": true + } + } + }, + "filters": { + "42f42812-92fe-a13a-b0ba-c6e0dfab3584": { + "id": "42f42812-92fe-a13a-b0ba-c6e0dfab3584", + "filter": "Active Devices", + "keyFilters": [ + { + "key": { + "type": "SERVER_ATTRIBUTE", + "key": "active" + }, + "valueType": "BOOLEAN", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": true, + "dynamicValue": null + }, + "type": "BOOLEAN" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": false + }, + "39d92c21-2426-7346-4484-452829146c33": { + "id": "39d92c21-2426-7346-4484-452829146c33", + "filter": "Inactive Devices", + "keyFilters": [ + { + "key": { + "type": "SERVER_ATTRIBUTE", + "key": "active" + }, + "valueType": "BOOLEAN", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": false, + "dynamicValue": null + }, + "type": "BOOLEAN" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": false + } + }, + "timewindow": { + "displayValue": "", + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1680168326072, + "endTimeMs": 1680254726072 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "settings": { + "stateControllerId": "entity", + "showTitle": false, + "showDashboardsSelect": true, + "showEntitiesSelect": true, + "showDashboardTimewindow": true, + "showDashboardExport": true, + "toolbarAlwaysOpen": true, + "titleColor": "rgba(0,0,0,0.870588)", + "showDashboardLogo": false, + "dashboardLogoUrl": null, + "hideToolbar": true, + "showFilters": true, + "showUpdateDashboardImage": true, + "dashboardCss": ".tb-widget-container > .tb-widget {\n border: 1px solid rgba(0, 0, 0, 0.05);\n box-shadow: 0px 5px 16px rgba(0, 0, 0, 0.04);\n border-radius: 12px;\n}\n\n.tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 16px !important;\n}\n\n.tb-card-title {\n display: grid;\n}\n\n.tb-home-widget-title {\n font-style: normal;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n color: rgba(0, 0, 0, 0.54);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.tb-home-widget-link {\n position: relative;\n border-bottom: none;\n}\n\n.tb-home-widget-link:hover {\n border-bottom: none;\n}\n\n.tb-home-widget-link:focus {\n border-bottom: none;\n}\n\n.tb-home-widget-link::after {\n content: 'arrow_forward';\n display: inline-block;\n transform: rotate(315deg);\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n font-size: 18px;\n color: rgba(0, 0, 0, 0.12);\n vertical-align: bottom;\n margin-left: 6px; \n}\n\n.tb-home-widget-link:hover::after {\n color: inherit;\n}\n\n.tb-home-widget-info-icon {\n color: rgba(0, 0, 0, 0.12);\n font-size: 16px;\n width: 16px;\n height: 16px;\n line-height: 15px;\n vertical-align: middle;\n}\n\n.tb-widget-container > .tb-widget .tb-timewindow {\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n color: rgba(0, 0, 0, 0.54);\n padding: 0;\n}\n\n.tb-widget-container > .tb-widget .tb-legend-keys .tb-legend-label {\n cursor: pointer;\n user-select: none;\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n color: rgba(0, 0, 0, 0.54);\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .tb-widget-container > .tb-widget {\n border-radius: 4px;\n }\n .tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 2px !important;\n }\n .tb-hide-md {\n display: none;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1819px) {\n .tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 8px !important;\n }\n .tb-hide-lg {\n display: none;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-hide-md-lg {\n display: none;\n }\n\n .tb-home-widget-title {\n font-size: 12px;\n line-height: 16px;\n }\n \n .tb-widget-container > .tb-widget .tb-widget-title {\n padding: 0;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow {\n font-size: 12px;\n line-height: 16px;\n min-height: 24px;\n padding: 0;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow .mat-mdc-icon-button.tb-mat-32 {\n width: 24px;\n height: 24px;\n line-height: 24px;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow .mat-mdc-icon-button.tb-mat-32 .mat-icon {\n width: 18px;\n height: 18px;\n font-size: 18px;\n }\n \n .tb-widget-container > .tb-widget tb-legend {\n padding-bottom: 0 !important;\n }\n \n .tb-widget-container > .tb-widget .tb-legend-keys .tb-legend-label {\n font-size: 11px;\n line-height: 16px;\n letter-spacing: 0.25px;\n }\n}\n\n@media screen and (max-width: 959px), screen and (min-width: 1820px) {\n .tb-hide-not-md-lg {\n display: none;\n }\n}\n" + } + }, + "externalId": null, + "name": "Tenant Administrator Home Page" +} diff --git a/ui-ngx/src/assets/docker.svg b/ui-ngx/src/assets/docker.svg new file mode 100644 index 0000000000..f152739de6 --- /dev/null +++ b/ui-ngx/src/assets/docker.svg @@ -0,0 +1 @@ + diff --git a/ui-ngx/src/assets/fonts/MaterialIcons-Regular.ttf b/ui-ngx/src/assets/fonts/MaterialIcons-Regular.ttf index be4be29c86..9d09b0feb8 100644 Binary files a/ui-ngx/src/assets/fonts/MaterialIcons-Regular.ttf and b/ui-ngx/src/assets/fonts/MaterialIcons-Regular.ttf differ diff --git a/ui-ngx/src/assets/help/en_US/date/date-format.md b/ui-ngx/src/assets/help/en_US/date/date-format.md new file mode 100644 index 0000000000..502ab7d4f7 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/date/date-format.md @@ -0,0 +1,88 @@ +#### Pre-defined format options + +| Option | Equivalent to | Examples (given in `en-US` locale) | +|------------|---------------------------------|-----------------------------------------------| +| short | M/d/yy, h:mm a | 6/15/15, 9:03 AM | +| medium | MMM d, y, h:mm:ss a | Jun 15, 2015, 9:03:01 AM | +| long | MMMM d, y, h:mm:ss a z | June 15, 2015 at 9:03:01 AM GMT+1 | +| full | EEEE, MMMM d, y, h:mm:ss a zzzz | Monday, June 15, 2015 at 9:03:01 AM GMT+01:00 | +| shortDate | M/d/yy | 6/15/15 | +| mediumDate | MMM d, y | Jun 15, 2015 | +| longDate | MMMM d, y | June 15, 2015 | +| fullDate | EEEE, MMMM d, y | Monday, June 15, 2015 | +| shortTime | h:mm a | 9:03 AM | +| mediumTime | h:mm:ss a | 9:03:01 AM | +| longTime | h:mm:ss a z | 9:03:01 AM GMT+1 | +| fullTime | h:mm:ss a zzzz | 9:03:01 AM GMT+01:00 | + +#### Custom format options + +You can construct a format string using symbols to specify the components +of a date-time value, as described in the following table. +Format details depend on the locale. +Fields marked with (*) are only available in the extra data set for the given locale. + +| Field type | Format | Description | Example Value | +|---------------------|-------------|--------------------------------------------------------------|------------------------------------------------------------| +| Era | G, GG & GGG | Abbreviated | AD | +| | GGGG | Wide | Anno Domini | +| | GGGGG | Narrow | A | +| Year | y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 | +| | yy | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 | +| | yyy | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 | +| | yyyy | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 | +| Week-numbering year | Y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 | +| | YY | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 | +| | YYY | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 | +| | YYYY | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 | +| Month | M | Numeric: 1 digit | 9, 12 | +| | MM | Numeric: 2 digits + zero padded | 09, 12 | +| | MMM | Abbreviated | Sep | +| | MMMM | Wide | September | +| | MMMMM | Narrow | S | +| Month standalone | L | Numeric: 1 digit | 9, 12 | +| | LL | Numeric: 2 digits + zero padded | 09, 12 | +| | LLL | Abbreviated | Sep | +| | LLLL | Wide | September | +| | LLLLL | Narrow | S | +| Week of year | w | Numeric: minimum digits | 1... 53 | +| | ww | Numeric: 2 digits + zero padded | 01... 53 | +| Week of month | W | Numeric: 1 digit | 1... 5 | +| Day of month | d | Numeric: minimum digits | 1 | +| | dd | Numeric: 2 digits + zero padded | 01 | +| Week day | E, EE & EEE | Abbreviated | Tue | +| | EEEE | Wide | Tuesday | +| | EEEEE | Narrow | T | +| | EEEEEE | Short | Tu | +| Week day standalone | c, cc | Numeric: 1 digit | 2 | +| | ccc | Abbreviated | Tue | +| | cccc | Wide | Tuesday | +| | ccccc | Narrow | T | +| | cccccc | Short | Tu | +| Period | a, aa & aaa | Abbreviated | am/pm or AM/PM | +| | aaaa | Wide (fallback to `a` when missing) | ante meridiem/post meridiem | +| | aaaaa | Narrow | a/p | +| Period* | B, BB & BBB | Abbreviated | mid. | +| | BBBB | Wide | am, pm, midnight, noon, morning, afternoon, evening, night | +| | BBBBB | Narrow | md | +| Period standalone* | b, bb & bbb | Abbreviated | mid. | +| | bbbb | Wide | am, pm, midnight, noon, morning, afternoon, evening, night | +| | bbbbb | Narrow | md | +| Hour 1-12 | h | Numeric: minimum digits | 1, 12 | +| | hh | Numeric: 2 digits + zero padded | 01, 12 | +| Hour 0-23 | H | Numeric: minimum digits | 0, 23 | +| | HH | Numeric: 2 digits + zero padded | 00, 23 | +| Minute | m | Numeric: minimum digits | 8, 59 | +| | mm | Numeric: 2 digits + zero padded | 08, 59 | +| Second | s | Numeric: minimum digits | 0... 59 | +| | ss | Numeric: 2 digits + zero padded | 00... 59 | +| Fractional seconds | S | Numeric: 1 digit | 0... 9 | +| | SS | Numeric: 2 digits + zero padded | 00... 99 | +| | SSS | Numeric: 3 digits + zero padded (= milliseconds) | 000... 999 | +| Zone | z, zz & zzz | Short specific non location format (fallback to O) | GMT-8 | +| | zzzz | Long specific non location format (fallback to OOOO) | GMT-08:00 | +| | Z, ZZ & ZZZ | ISO8601 basic format | -0800 | +| | ZZZZ | Long localized GMT format | GMT-8:00 | +| | ZZZZZ | ISO8601 extended format + Z indicator for offset 0 (= XXXXX) | -08:00 | +| | O, OO & OOO | Short localized GMT format | GMT-8 | +| | OOOO | Long localized GMT format | GMT-08:00 | diff --git a/ui-ngx/src/assets/help/en_US/notification/rate_limits.md b/ui-ngx/src/assets/help/en_US/notification/rate_limits.md new file mode 100644 index 0000000000..8f6319ad80 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/notification/rate_limits.md @@ -0,0 +1,50 @@ +#### Exceeded rate limits notification templatization + +
+
+ +Notification subject and message fields support templatization. +The list of available templatization parameters depends on the template type. +See the available types and parameters below: + +Available template parameters: + +* `api` - rate-limited API label; one of: 'REST API requests', 'REST API requests per customer', 'transport messages', + 'transport messages per device', 'Cassandra queries', 'WS updates per session', 'notification requests', 'notification requests per rule', + 'entity version creation', 'entity version load'; +* `limitLevelEntityType` - entity type of the limit level entity, e.g. 'Tenant', 'Device', 'Notification rule', 'Customer', etc.; +* `limitLevelEntityId` - id of the limit level entity; +* `limitLevelEntityName` - name of the limit level entity; +* `tenantId` - id of the tenant; +* `tenantName` - name of the tenant; +* `recipientTitle` - title of the recipient (first and last name if specified, email otherwise); +* `recipientEmail` - email of the recipient; +* `recipientFirstName` - first name of the recipient; +* `recipientLastName` - last name of the recipient; + +Parameter names must be wrapped using `${...}`. For example: `${recipientFirstName}`. +You may also modify the value of the parameter with one of the suffixes: + +* `upperCase`, for example - `${recipientFirstName:upperCase}` +* `lowerCase`, for example - `${recipientFirstName:lowerCase}` +* `capitalize`, for example - `${recipientFirstName:capitalize}` + +
+ +##### Examples + +Let's assume customer 'Customer A' exceeded rate limit for per-customer REST API requests. The following template: + +```text +Rate limits for ${api} exceeded for ${limitLevelEntityType:lowerCase} '${limitLevelEntityName}' +{:copy-code} +``` + +will be transformed to: + +```text +Rate limits for REST API requests per customer exceeded for customer 'Customer A' +``` + +
+
diff --git a/ui-ngx/src/assets/help/en_US/notification/rule_node.md b/ui-ngx/src/assets/help/en_US/notification/rule_node.md index 9eb7bd3b71..2d7974d1cc 100644 --- a/ui-ngx/src/assets/help/en_US/notification/rule_node.md +++ b/ui-ngx/src/assets/help/en_US/notification/rule_node.md @@ -13,6 +13,7 @@ Available template parameters: * values from the incoming message data referenced using the data key name; * `originatorType` - type of the originator, e.g. 'Device'; * `originatorId` - id of the originator +* `customerId` - id of the customer if any * `msgType` - type of the message * `recipientTitle` - title of the recipient (first and last name if specified, email otherwise); * `recipientEmail` - email of the recipient; diff --git a/ui-ngx/src/assets/help/en_US/rulenode/change_originator_node_fields_templatization.md b/ui-ngx/src/assets/help/en_US/rulenode/change_originator_node_fields_templatization.md new file mode 100644 index 0000000000..204297957a --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/rulenode/change_originator_node_fields_templatization.md @@ -0,0 +1,81 @@ +#### Fields templatization + +
+
+ +{% include rulenode/common_node_fields_templatization %} + +##### Example + +Let's assume that a tenant manages two assets: + + - `TemperatureManager` asset - responsible for aggregating data from temperature sensors, essential for environmental monitoring and alerts. + - `HumidityManager` asset - collects data from humidity sensors, analyzing relative humidity levels + and correlating them with temperature data for comprehensive atmospheric condition monitoring and automated environmental adjustments. + +Each message received from the device includes `deviceType` property in the message metadata +with either `Temperature` or `Humidity` value according to the sensor type. + +In order to change the originator to the corresponding manager for further message processing, you can define the next node configuration: + +![image](${helpBaseUrl}/help/images/rulenode/examples/change-originator-ft.png) + +Imagine that you receive the message defined below from the `Temperature` sensor +and forwarded it to the **change originator** node with configuration added above. + +- incoming message definition: + +```json +{ + "msg": { + "temperature": 32 + }, + "metadata": { + "deviceType": "Temperature", + "deviceName": "TH-001", + "ts": "1685379440000" + } +} +``` +
+ +The same example for the `humidity` sensor: + +- incoming message definition: + +```json +{ + "msg": { + "humidity": 77 + }, + "metadata": { + "deviceType": "Humidity", + "deviceName": "HM-001", + "ts": "1685379440000" + } +} +``` + +
+ +To demonstrate that the originator was changed based on rule node configuration, the screenshots with debug events captured for the **change originator** rule node will be added below: + + - Debug events for processed message from the `Temperature` sensor: + +![image](${helpBaseUrl}/help/images/rulenode/examples/change-originator-ft-2.png) + + - Debug events for processed message from the `Humidity` sensor: + +![image](${helpBaseUrl}/help/images/rulenode/examples/change-originator-ft-3.png) + +In the debug events displayed, the `IN` message points to the incoming message received by the node, +with the originator type specified as `DEVICE`. This reflects the message from the actual sensor (e.g., a `Temperature` or `Humidity` sensor). +After processing through the `change originator` rule node, the `OUT` message has an originator type of `ASSET`, +indicating that the message originator has been successfully changed to the corresponding managing asset, such as `TemperatureManager` or `HumidityManager`. + +
+ +These examples showcases using the **change originator** node with dynamic configuration based on the substitution of metadata fields. + +
+
diff --git a/ui-ngx/src/assets/help/en_US/rulenode/common_node_fields_templatization.md b/ui-ngx/src/assets/help/en_US/rulenode/common_node_fields_templatization.md new file mode 100644 index 0000000000..0ccbbd9058 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/rulenode/common_node_fields_templatization.md @@ -0,0 +1,4 @@ +Fields templatization feature allows you to process the incoming messages with dynamic configuration +by substitution of templates specified in the configuration fields with values from message or message metadata. +For more detailed information, please refer to the ThingsBoard [documentation](https://thingsboard.io/docs/user-guide/templatization/) + diff --git a/ui-ngx/src/assets/help/en_US/rulenode/customer_attributes_node_fields_templatization.md b/ui-ngx/src/assets/help/en_US/rulenode/customer_attributes_node_fields_templatization.md new file mode 100644 index 0000000000..2092a0cdb5 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/rulenode/customer_attributes_node_fields_templatization.md @@ -0,0 +1,107 @@ +#### Fields templatization + +
+
+ +{% include rulenode/common_node_fields_templatization %} + +##### Examples + +Let's assume that we have a customer-based solution where customer manage two type of devices: `temperature` and `humidity` sensors. +Additionally, let's assume that customer configured the thresholds settings for each device type. +Threshold settings stored as an attributes on a customer level: + +- *temperatureMinThreshold* and *temperatureMaxThreshold* for temperature sensor with values set to *10* and *30* accordingly. +- *humidityMinThreshold* and *humidityMaxThreshold* for humidity sensor with values set to *70* and *85* accordingly. + +Each message received from device includes `deviceType` property in the message metadata +with either `temperature` or `humidity` value according to the sensor type. + +In order to fetch the threshold value for the further message processing you can define next node configuration: + +![image](${helpBaseUrl}/help/images/rulenode/examples/customer-attributes-ft.png) + +Imagine that you receive message defined below from the `temperature` sensor +and forwarded it to the **customer attributes** node with configuration added above. + +- incoming message definition: + +```json +{ + "msg": { + "temperature": 32 + }, + "metadata": { + "deviceType": "temperature", + "deviceName": "TH-001", + "ts": "1685379440000" + } +} +``` + +
+ +The same example for the `humidity` sensor: + +- incoming message definition: + +```json +{ + "msg": { + "humidity": 77 + }, + "metadata": { + "deviceType": "humidity", + "deviceName": "HM-001", + "ts": "1685379440000" + } +} +``` + +
+ +Rule node configuration set to fetch data to the message metadata. In the following way: + +- outgoing message for the `temperature` sensor would be updated to: + +```json +{ + "msg": { + "temperature": 32 + }, + "metadata": { + "deviceType": "temperature", + "deviceName": "TH-001", + "ts": "1685379440000", + "minThreshold": "10", + "maxThreshold": "30" + } +} +``` + +
+ +- outgoing message for the `humidity` sensor would be updated to: + +```json +{ + "msg": { + "humidity": 77 + }, + "metadata": { + "deviceType": "humidity", + "deviceName": "HM-001", + "ts": "1685379440000", + "minThreshold": "70", + "maxThreshold": "85" + } +} +``` + +
+ +These examples showcases using the **customer attributes** node with dynamic configuration based on the substitution of metadata fields. + +
+
+ diff --git a/ui-ngx/src/assets/help/en_US/rulenode/originator_attributes_node_fields_templatization.md b/ui-ngx/src/assets/help/en_US/rulenode/originator_attributes_node_fields_templatization.md new file mode 100644 index 0000000000..bd9bba3fbd --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/rulenode/originator_attributes_node_fields_templatization.md @@ -0,0 +1,132 @@ +#### Fields templatization + +
+
+ +{% include rulenode/common_node_fields_templatization %} + +##### Examples + +Let's assume that we have a moisture meter device(message originator) that publishes a telemetry messages that includes the following data readings: + +- `soilMoisture` +- `windSpeed` +- `windDirection` +- `temperature` +- `humidity` + +Depending on certain conditions, we might need to fetch additional server-side attributes from the moisture meter device. + +Specifically, if the soil moisture reading drops below a certain threshold, let's say 30%, this is considered critical as it directly impacts crop health and growth. +In this case, we need to fetch the `lastIrrigationTime` attribute. +This additional information can help us understand when the field was last watered and take necessary action, such as activating the irrigation system. +However, if the soil moisture is above this critical threshold, then we need to check another condition. +If the wind speed is above a certain level, say 8 m/s, we need to fetch the `lastWindSpeedAlarmTime` attribute. +This additional information can help us to understand when the last significant wind event occurred, +which might be indicative of an approaching storm or damaging winds. + +Consider that you write a script that depending on a conditions written above will add to the message metadata additional key: `keyToFetch` with one of the next values: + +- `lastIrrigationTime` +- `lastWindSpeedAlarmTime` + +In order to fetch value of one of the following keys for the further message processing you can define next node configuration: + +![image](${helpBaseUrl}/help/images/rulenode/examples/originator-attributes-ft.png) + +- message definition that match first condition after processing in the script node: + +```json +{ + "msg": { + "temperature": 26.5, + "humidity": 75.2, + "soilMoisture": 28.9, + "windSpeed": 8.2, + "windDirection": "NNE" + }, + "metadata": { + "deviceType": "default", + "deviceName": "SN-001", + "ts": "1685379440000", + "keyToFetch": "lastIrrigationTime" + } +} +``` + +
+ +- message definition that match second condition after processing in the script node: + +```json +{ + "msg": { + "temperature": 26.5, + "humidity": 75.2, + "soilMoisture": 32.5, + "windSpeed": 10.4, + "windDirection": "NNE" + }, + "metadata": { + "deviceType": "default", + "deviceName": "SN-001", + "ts": "1685379440000", + "keyToFetch": "lastWindSpeedAlarmTime" + } +} +``` + +
+ +Rule node configuration set to fetch data to the message metadata. In the following way: + +- outgoing message for the first condition would be updated to: + +```json +{ + "msg": { + "temperature": 26.5, + "humidity": 75.2, + "soilMoisture": 28.9, + "windSpeed": 8.2, + "windDirection": "NNE" + }, + "metadata": { + "deviceType": "default", + "deviceName": "SN-001", + "ts": "1685379440000", + "keyToFetch": "lastIrrigationTime", + "ss_lastIrrigationTime": "1685369440000" + } +} +``` + +
+ +- outgoing message for the second condition would be updated to: + +```json +{ + "msg": { + "temperature": 26.5, + "humidity": 75.2, + "soilMoisture": 32.5, + "windSpeed": 10.4, + "windDirection": "NNE" + }, + "metadata": { + "deviceType": "default", + "deviceName": "MM-001", + "ts": "1685379440000", + "keyToFetch": "lastWindSpeedAlarmTime", + "ss_lastWindSpeedAlarmTime": "1685359440000" + } +} +``` + +
+ +These examples showcases using the **originator attributes** node with dynamic configuration based on the substitution of metadata fields. + +
+
diff --git a/ui-ngx/src/assets/help/en_US/rulenode/originator_fields_node_fields_templatization.md b/ui-ngx/src/assets/help/en_US/rulenode/originator_fields_node_fields_templatization.md new file mode 100644 index 0000000000..5f22261b73 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/rulenode/originator_fields_node_fields_templatization.md @@ -0,0 +1,106 @@ +#### Fields templatization + +
+
+ +{% include rulenode/common_node_fields_templatization %} + +##### Examples + +Let's assume that we have two device types in our use case: + +- `smartDoorLock` +- `motionDetector` + +Let's assume that device of type `smartDoorLock` and name `SDL-001` publish next type of messages to the system: + +```json +{ + "msg": { + "status": "locked" + }, + "metadata": { + "deviceName": "SDL-001", + "deviceType": "smartDoorLock", + "ts": "1685379440000" + } +} +``` + +
+ +and device of type `motionDetector` and name `MD-001` publish next type of messages to the system: + +```json +{ + "msg": { + "motionDetected": "true" + }, + "metadata": { + "deviceName": "MD-001", + "deviceType": "motionDetector", + "ts": "1685379440000" + } +} +``` + +
+ +Imagine that you send the messages received from the devices to the external systems +and depending on the device type you need add to the message the human-readable label +of the device with field name equal to the `deviceType` value from the message metadata. + +Let's assume that devices have next labels: + +- *Grocery warehouse door* for `SDL-001` device. +- *Grocery Warehouse motion detector* for `MD-001` device. + +In order to fetch labels and add them to the message with the appropriate field name +you can define the next node configuration: + +![image](${helpBaseUrl}/help/images/rulenode/examples/originator-fields-ft.png) + +
+ +Rule node configuration set to fetch data to the message. In the following way: + +- outgoing message for the `SDL-001` device would be updated to: + +```json +{ + "msg": { + "status": "locked", + "smartDoorLock": "Grocery warehouse door" + }, + "metadata": { + "deviceName": "SDL-001", + "deviceType": "smartDoorLock", + "ts": "1685379440000" + } +} +``` + +
+ +- outgoing message for the `MD-001` device would be updated to: + +```json +{ + "msg": { + "motionDetected": "true", + "motionDetector": "Grocery Warehouse motion detector" + }, + "metadata": { + "deviceName": "MD-001", + "deviceType": "motionDetector", + "ts": "1685379440000" + } +} +``` + +
+ +These examples showcases using the **originator fields** node with dynamic configuration based on the substitution of metadata fields. + +
+
diff --git a/ui-ngx/src/assets/help/en_US/rulenode/originator_telemetry_node_fields_templatization.md b/ui-ngx/src/assets/help/en_US/rulenode/originator_telemetry_node_fields_templatization.md new file mode 100644 index 0000000000..5ee59f7161 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/rulenode/originator_telemetry_node_fields_templatization.md @@ -0,0 +1,291 @@ +#### Fields templatization + +
+
+ +{% include rulenode/common_node_fields_templatization %} + +##### Examples + +Originator telemetry node support templatization for multiple configuration fields. Namely, you can specify the template in the +*Timeseries keys* list, and also there is an option to use the templatization for the fetch interval start and end if you are using *dynamic interval*. + +###### Example 1 + +Let's start with an example of using templatization for the *Timeseries keys* list. +Imagine that you have a GPS tracker device(message originator) that publishes a telemetry messages that includes the following data readings: + +- `latitude` - current device latitude value. +- `longitude` - current device longitude value. +- `event` - parameter that specifies the current state of the device. The value might be *parked* or *motion*. + +Additionally let's imagine that devices periodically publishes other telemetry messages that includes additional information such as: + +- `speed` - current speed value. +- `direction` - compass direction in which the device is moving. +- `acceleration` - how quickly the speed of the device is changing. +- `fuelLevel` - current fuel level. +- `batteryLevel` - current battery level. +- `parkedLocation` - precise location where the device is parked. +- `parkedDuration` - current park duration value. +- `parkedTime` - timestamp when the device was parked. + +Let's imagine that we need to make some historical analysis by fetching 3 latest telemetry readings in the range from 1 hour ago to 1 millisecond ago. +If the `event` value is set to *motion* we need to fetch data for keys: + +- `speed` +- `direction` +- `acceleration` +- `fuelLevel` +- `batteryLevel` + +Otherwise, if the `event` value is set to *parked* value we need to fetch data for keys: + +- `parkedLocation` +- `parkedDuration` +- `parkedTime` +- `fuelLevel` +- `batteryLevel` + +Imagine that you created a script node that depending on the `event` value adds to the message metadata appropriate keyToFetch fields. + +- message definition that match condition when `event` is set to *motion* value after processing in the script node: + +```json +{ + "msg": { + "latitude": "40.730610", + "longitude": "-73.935242", + "event": "motion" + }, + "metadata": { + "deviceName": "GPS-001", + "deviceType": "GPS Tracker", + "ts": "1685479440000", + "keyToFetch1": "speed", + "keyToFetch2": "direction", + "keyToFetch3": "acceleration" + } +} +``` + +
+ +- message definition that match condition when `event` is set to *parked* value after processing in the script node: + +```json +{ + "msg": { + "latitude": "40.730610", + "longitude": "-73.935242", + "event": "parked" + }, + "metadata": { + "deviceName": "GPS-001", + "deviceType": "GPS Tracker", + "ts": "1685379440000", + "keyToFetch1": "parkedLocation", + "keyToFetch2": "parkedDuration", + "keyToFetch3": "parkedTime" + } +} +``` + +
+ +In order to fetch the additional telemetry key values to make some historical analysis of the tracker's state you can define the next node configuration: + +![image](${helpBaseUrl}/help/images/rulenode/examples/originator-telemetry-ft.png) + +
+ +Rule node configuration is set to retrieve the telemetry from the fetch interval with configurable query parameters that you can check above. +So let's imagine that 3 latest values for the keys that we are going to fetch are: + +- `speed` - 5.2, 15.7, 30.2 (mph). +- `direction` - N(North), NE(North-East), E(East). +- `acceleration` - 2.2, 2.4, 2.5 (m/s²). +- `fuelLevel` - 61.5, 57.4, 55.6 (%). +- `batteryLevel` - 88.1, 87.8, 87.2 (%). +- `parkedLocation` - dr5rtwceb (geohash). Same value for 3 latest data readings. +- `parkedDuration` - 6300000, 7300000, 8300000 (ms). +- `parkedTime` - 1685339240000 (ms). Same value for 3 latest data readings. + +In the following way: + +- outgoing message for the case when the `event` has value *motion* would be look like this: + +```json +{ + "msg": { + "latitude": "40.730610", + "longitude": "-73.935242", + "event": "motion" + }, + "metadata": { + "deviceName": "GPS-001", + "deviceType": "GPS Tracker", + "ts": "1685479440000", + "keyToFetch1": "speed", + "keyToFetch2": "direction", + "keyToFetch3": "acceleration", + "speed": "[{\"ts\":1685476840000,\"value\":5.2},{\"ts\":1685477840000,\"value\":15.7},{\"ts\":1685478840000,\"value\":30.2}]", + "direction": "[{\"ts\":1685476840000,\"value\":\"N\"},{\"ts\":1685477840000,\"value\":\"NE\"},{\"ts\":1685478840000,\"value\":\"N\"}]", + "acceleration": "[{\"ts\":1685476840000,\"value\":2.2},{\"ts\":1685477840000,\"value\":2.4},{\"ts\":1685478840000,\"value\":2.5}]", + "fuelLevel": "[{\"ts\":1685476840000,\"value\":61.5},{\"ts\":1685477840000,\"value\":57.4},{\"ts\":1685478840000,\"value\":55.6}]", + "batteryLevel": "[{\"ts\":1685476840000,\"value\":88.1},{\"ts\":1685477840000,\"value\":87.8},{\"ts\":1685478840000,\"value\":87.2}]" + } +} +``` + +
+ +- outgoing message for the case when the `event` has value *parked* would be look like this: + +```json +{ + "msg": { + "latitude": "40.730610", + "longitude": "-73.935242", + "event": "parked" + }, + "metadata": { + "deviceName": "GPS-001", + "deviceType": "GPS Tracker", + "ts": "1685379440000", + "keyToFetch1": "parkedLocation", + "keyToFetch2": "parkedDuration", + "keyToFetch3": "parkedTime", + "parkedLocation": "[{\"ts\":1685376840000,\"value\":\"dr5rtwceb\"},{\"ts\":1685377840000,\"value\":\"dr5rtwceb\"},{\"ts\":1685378840000,\"value\":\"dr5rtwceb\"}]", + "parkedDuration": "[{\"ts\":1685376840000,\"value\":6300000},{\"ts\":1685377840000,\"value\":7300000},{\"ts\":1685378840000,\"value\":8300000}]", + "parkedTime": "[{\"ts\":1685376840000,\"value\":1685376840000},{\"ts\":1685377840000,\"value\":1685377840000},{\"ts\":1685378840000,\"value\":1685378840000}]", + "fuelLevel": "[{\"ts\":1685376840000,\"value\":61.5},{\"ts\":1685377840000,\"value\":57.4},{\"ts\":1685378840000,\"value\":55.6}]", + "batteryLevel": "[{\"ts\":1685376840000,\"value\":88.1},{\"ts\":1685377840000,\"value\":87.8},{\"ts\":1685378840000,\"value\":87.2}]" + } +} +``` + +###### Example 2 + +This example will extend the previous example with additional condition: + +Imagine that you need to specify the fetch interval dynamically from the 1 hour ago till the current time. +Additionally let's assume that the current time can be extracted from `ts` field that we have in the message metadata on each message received. +While the value of (1 hour ago) can be calculated in the script node that we use for adding keyToFetch fields into metadata. + +In the following way: + +- message definition that match condition when `event` is set to *motion* value after processing in the script node: + +```json +{ + "msg": { + "latitude": "40.730610", + "longitude": "-73.935242", + "event": "motion" + }, + "metadata": { + "deviceName": "GPS-001", + "deviceType": "GPS Tracker", + "ts": "1685479440000", + "keyToFetch1": "speed", + "keyToFetch2": "direction", + "keyToFetch3": "acceleration", + "dynamicIntervalStart": "1685475840000" + } +} +``` + +- message definition that match condition when `event` is set to *parked* value after processing in the script node: + +```json +{ + "msg": { + "latitude": "40.730610", + "longitude": "-73.935242", + "event": "parked" + }, + "metadata": { + "deviceName": "GPS-001", + "deviceType": "GPS Tracker", + "ts": "1685379440000", + "keyToFetch1": "parkedLocation", + "keyToFetch2": "parkedDuration", + "keyToFetch3": "parkedTime", + "dynamicIntervalStart": "1685375840000" + } +} +``` + +
+ +In order to fetch the data using dynamic interval we need enable *Use dynamic interval* option in the rule node configuration and specify the templates for the *Interval start* and *Interval end*: + + +![image](${helpBaseUrl}/help/images/rulenode/examples/originator-telemetry-ft-2.png) + +
+ +Other configuration wasn't change from our previous example. +In the following way: + +- outgoing message for the case when the `event` has value *motion* would be look like this: + +```json +{ + "msg": { + "latitude": "40.730610", + "longitude": "-73.935242", + "event": "motion" + }, + "metadata": { + "deviceName": "GPS-001", + "deviceType": "GPS Tracker", + "ts": "1685479440000", + "keyToFetch1": "speed", + "keyToFetch2": "direction", + "keyToFetch3": "acceleration", + "dynamicIntervalStart": "1685475840000", + "speed": "[{\"ts\":1685476840000,\"value\":5.2},{\"ts\":1685477840000,\"value\":15.7},{\"ts\":1685478840000,\"value\":30.2}]", + "direction": "[{\"ts\":1685476840000,\"value\":\"N\"},{\"ts\":1685477840000,\"value\":\"NE\"},{\"ts\":1685478840000,\"value\":\"N\"}]", + "acceleration": "[{\"ts\":1685476840000,\"value\":2.2},{\"ts\":1685477840000,\"value\":2.4},{\"ts\":1685478840000,\"value\":2.5}]", + "fuelLevel": "[{\"ts\":1685476840000,\"value\":61.5},{\"ts\":1685477840000,\"value\":57.4},{\"ts\":1685478840000,\"value\":55.6}]", + "batteryLevel": "[{\"ts\":1685476840000,\"value\":88.1},{\"ts\":1685477840000,\"value\":87.8},{\"ts\":1685478840000,\"value\":87.2}]" + } +} +``` + +
+ +- outgoing message for the case when the `event` has value *parked* would be look like this: + +```json +{ + "msg": { + "latitude": "40.730610", + "longitude": "-73.935242", + "event": "parked" + }, + "metadata": { + "deviceName": "GPS-001", + "deviceType": "GPS Tracker", + "ts": "1685379440000", + "keyToFetch1": "parkedLocation", + "keyToFetch2": "parkedDuration", + "keyToFetch3": "parkedTime", + "dynamicIntervalStart": "1685375840000", + "parkedLocation": "[{\"ts\":1685376840000,\"value\":\"dr5rtwceb\"},{\"ts\":1685377840000,\"value\":\"dr5rtwceb\"},{\"ts\":1685378840000,\"value\":\"dr5rtwceb\"}]", + "parkedDuration": "[{\"ts\":1685376840000,\"value\":6300000},{\"ts\":1685377840000,\"value\":7300000},{\"ts\":1685378840000,\"value\":8300000}]", + "parkedTime": "[{\"ts\":1685376840000,\"value\":1685376840000},{\"ts\":1685377840000,\"value\":1685377840000},{\"ts\":1685378840000,\"value\":1685378840000}]", + "fuelLevel": "[{\"ts\":1685376840000,\"value\":61.5},{\"ts\":1685377840000,\"value\":57.4},{\"ts\":1685378840000,\"value\":55.6}]", + "batteryLevel": "[{\"ts\":1685376840000,\"value\":88.1},{\"ts\":1685377840000,\"value\":87.8},{\"ts\":1685378840000,\"value\":87.2}]" + } +} +``` + +
+ +These examples showcases using the **originator telemetry** node with dynamic configuration based on the substitution of metadata fields. + +
+
diff --git a/ui-ngx/src/assets/help/en_US/rulenode/related_device_attributes_node_fields_templatization.md b/ui-ngx/src/assets/help/en_US/rulenode/related_device_attributes_node_fields_templatization.md new file mode 100644 index 0000000000..abfbd777b9 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/rulenode/related_device_attributes_node_fields_templatization.md @@ -0,0 +1,132 @@ +#### Fields templatization + +
+
+ +{% include rulenode/common_node_fields_templatization %} + +##### Examples + +Let's assume that we have a moisture meter device(message originator) that publishes a telemetry messages that includes the following data readings: + +- `soilMoisture` +- `windSpeed` +- `windDirection` +- `temperature` +- `humidity` + +Depending on certain conditions, we might need to fetch additional server-side attributes from the related irrigation controller device. + +Specifically, if the soil moisture reading drops below a certain threshold, let's say 30%, this is considered critical as it directly impacts crop health and growth. +In this case, we need to fetch the `lastIrrigationTime` attribute. +This additional information can help us understand when the field was last watered and take necessary action, such as activating the irrigation system. +However, if the soil moisture is above this critical threshold, then we need to check another condition. +If the wind speed is above a certain level, say 8 m/s, we might need to fetch the `lastIrrigationPauseTime` attribute. +This additional information can help us understand when the last time the irrigation system was paused due to high wind conditions, +which might help to make more informed decisions about when and how to irrigate, considering both current and historical weather conditions. + +Consider that you write a script that depending on a conditions written above will add to the message metadata additional key: `keyToFetch` with one of the next values: + +- `lastIrrigationTime` +- `lastIrrigationPauseTime` + +In order to fetch value of one of the following keys for the further message processing you can define next node configuration: + +![image](${helpBaseUrl}/help/images/rulenode/examples/related-device-attributes-ft.png) + +- message definition that match first condition after processing in the script node: + +```json +{ + "msg": { + "temperature": 26.5, + "humidity": 75.2, + "soilMoisture": 28.9, + "windSpeed": 8.2, + "windDirection": "NNE" + }, + "metadata": { + "deviceType": "default", + "deviceName": "MM-001", + "ts": "1685379440000", + "keyToFetch": "lastIrrigationTime" + } +} +``` + +
+ +- message definition that match second condition after processing in the script node: + +```json +{ + "msg": { + "temperature": 26.5, + "humidity": 75.2, + "soilMoisture": 32.5, + "windSpeed": 10.4, + "windDirection": "NNE" + }, + "metadata": { + "deviceType": "default", + "deviceName": "MM-001", + "ts": "1685379440000", + "keyToFetch": "lastIrrigationPauseTime" + } +} +``` + +
+ +Rule node configuration set to fetch data to the message metadata. In the following way: + +- outgoing message for the first condition would be updated to: + +```json +{ + "msg": { + "temperature": 26.5, + "humidity": 75.2, + "soilMoisture": 28.9, + "windSpeed": 8.2, + "windDirection": "NNE" + }, + "metadata": { + "deviceType": "default", + "deviceName": "MM-001", + "ts": "1685379440000", + "keyToFetch": "lastIrrigationTime", + "ss_lastIrrigationTime": "1685369440000" + } +} +``` + +
+ +- outgoing message the second condition would be updated to: + +```json +{ + "msg": { + "temperature": 26.5, + "humidity": 75.2, + "soilMoisture": 32.5, + "windSpeed": 10.4, + "windDirection": "NNE" + }, + "metadata": { + "deviceType": "default", + "deviceName": "MM-001", + "ts": "1685379440000", + "keyToFetch": "lastIrrigationPauseTime", + "ss_lastIrrigationPauseTime": "1685359440000" + } +} +``` + +
+ +These examples showcases using the **related device attributes** node with dynamic configuration based on the substitution of metadata fields. + +
+
diff --git a/ui-ngx/src/assets/help/en_US/rulenode/related_entity_data_node_fields_templatization.md b/ui-ngx/src/assets/help/en_US/rulenode/related_entity_data_node_fields_templatization.md new file mode 100644 index 0000000000..dad46a6495 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/rulenode/related_entity_data_node_fields_templatization.md @@ -0,0 +1,110 @@ +#### Fields templatization + +
+
+ +{% include rulenode/common_node_fields_templatization %} + +##### Examples + +Let's consider a scenario where we possess an asset that serves as a warehouse +and is responsible for overseeing two categories of devices: + +- sensors measuring `temperature`. +- sensors measuring `humidity`. + +Additionally, let's assume that this asset has configured thresholds set as attributes for each device type: + +- *temperatureMinThreshold* and *temperatureMaxThreshold* for temperature sensor with values set to *10* and *30* accordingly. +- *humidityMinThreshold* and *humidityMaxThreshold* for humidity sensor with values set to *70* and *85* accordingly. + +Each message received from device includes `deviceType` property in the message metadata +with either `temperature` or `humidity` value according to the sensor type. + +In order to fetch the threshold value for the further message processing you can define next node configuration: + +![image](${helpBaseUrl}/help/images/rulenode/examples/related-entity-data-ft.png) + +Imagine that you receive message defined below from the `temperature` sensor +and forwarded it to the **related entity data** node with configuration added above. + +- incoming message definition: + +```json +{ + "msg": { + "temperature": 32 + }, + "metadata": { + "deviceType": "temperature", + "deviceName": "TH-001", + "ts": "1685379440000" + } +} +``` + +
+ +The same example for the `humidity` sensor: + +- incoming message definition: + +```json +{ + "msg": { + "humidity": 77 + }, + "metadata": { + "deviceType": "humidity", + "deviceName": "HM-001", + "ts": "1685379440000" + } +} +``` + +
+ +Rule node configuration set to fetch data to the message metadata. In the following way: + +- outgoing message for the `temperature` sensor would be updated to: + +```json +{ + "msg": { + "temperature": 32 + }, + "metadata": { + "deviceType": "temperature", + "deviceName": "TH-001", + "ts": "1685379440000", + "minThreshold": "10", + "maxThreshold": "30" + } +} +``` + +
+ +- outgoing message for the `humidity` sensor would be updated to: + +```json +{ + "msg": { + "humidity": 77 + }, + "metadata": { + "deviceType": "humidity", + "deviceName": "HM-001", + "ts": "1685379440000", + "minThreshold": "70", + "maxThreshold": "85" + } +} +``` + +
+ +These examples showcases using the **related entity data** node with dynamic configuration based on the substitution of metadata fields. + +
+
diff --git a/ui-ngx/src/assets/help/en_US/rulenode/tenant_attributes_node_fields_templatization.md b/ui-ngx/src/assets/help/en_US/rulenode/tenant_attributes_node_fields_templatization.md new file mode 100644 index 0000000000..71eb28d086 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/rulenode/tenant_attributes_node_fields_templatization.md @@ -0,0 +1,107 @@ +#### Fields templatization + +
+
+ +{% include rulenode/common_node_fields_templatization %} + +##### Examples + +Let's assume that tenant manage two type of devices: `temperature` and `humidity` sensors. +Additionally, let's assume that tenant configured the thresholds settings for each device type. +Threshold settings stored as an attributes on a tenant level: + +- *temperatureMinThreshold* and *temperatureMaxThreshold* for temperature sensor with values set to *10* and *30* accordingly. +- *humidityMinThreshold* and *humidityMaxThreshold* for humidity sensor with values set to *70* and *85* accordingly. + +Each message received from device includes `deviceType` property in the message metadata +with either `temperature` or `humidity` value according to the sensor type. + +In order to fetch the threshold value for the further message processing you can define next node configuration: + +![image](${helpBaseUrl}/help/images/rulenode/examples/tenant-attributes-ft.png) + +Imagine that you receive message defined below from the `temperature` sensor +and forwarded it to the **tenant attributes** node with configuration added above. + +- incoming message definition: + +```json +{ + "msg": { + "temperature": 32 + }, + "metadata": { + "deviceType": "temperature", + "deviceName": "TH-001", + "ts": "1685379440000" + } +} +``` + +
+ +The same example for the `humidity` sensor: + +- incoming message definition: + +```json +{ + "msg": { + "humidity": 77 + }, + "metadata": { + "deviceType": "humidity", + "deviceName": "HM-001", + "ts": "1685379440000" + } +} +``` + +
+ +Rule node configuration set to fetch data to the message metadata. In the following way: + +- outgoing message for the `temperature` sensor would be updated to: + +```json +{ + "msg": { + "temperature": 32 + }, + "metadata": { + "deviceType": "temperature", + "deviceName": "TH-001", + "ts": "1685379440000", + "minThreshold": "10", + "maxThreshold": "30" + } +} +``` + +
+ +- outgoing message for the `humidity` sensor would be updated to: + +```json +{ + "msg": { + "humidity": 77 + }, + "metadata": { + "deviceType": "humidity", + "deviceName": "HM-001", + "ts": "1685379440000", + "minThreshold": "70", + "maxThreshold": "85" + } +} +``` + +
+ +These examples showcases using the **tenant attributes** node with dynamic configuration based on the substitution of metadata fields. + +
+
+ diff --git a/ui-ngx/src/assets/help/en_US/rulenode/to_email_node_fields_templatization.md b/ui-ngx/src/assets/help/en_US/rulenode/to_email_node_fields_templatization.md new file mode 100644 index 0000000000..8fbc917327 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/rulenode/to_email_node_fields_templatization.md @@ -0,0 +1,68 @@ +#### Fields templatization + +
+
+ +{% include rulenode/common_node_fields_templatization %} + +##### Example + +Let's assume that a tenant manages temperature sensors. +When a temperature sensor reports a high temperature, the platform creates an alarm. + +In addition, let's assume that each sensor has a group of alarm notification subscribers +associated with it and a primary user that responsible for updating the alarm status. + +Imagine that after alarm creation we fetched information about notification subscribers and +primary users with the help of enrichment rule nodes and after that our message looks like this: + +```json +{ + "msg": { + "temperature": 32 + }, + "metadata": { + "deviceType": "Thermostat", + "deviceName": "TH-001", + "ts": "1685379440000", + "primaryUser": "john.doe@example.com", + "subscribers": "mike.johnson@example.io,sarah.smith@example.org,emily.davis@example.co" + } +} +``` +
+ +Here is a node configuration: + +![image](${helpBaseUrl}/help/images/rulenode/examples/to-email-ft.png) + +After message evaluation by a rule node the outgoing message will be looks like this: + +```json +{ + "msg": { + "from": "info@testmail.org", + "to": "john.doe@example.com", + "cc": "mike.johnson@example.io,sarah.smith@example.org,emily.davis@example.co", + "bcc": null, + "subject": "Device Thermostat temperature high", + "body": "Device TH-001 has high temperature 32", + "images": null, + "html": false + }, + "metadata": { + "deviceType": "Thermostat", + "deviceName": "TH-001", + "ts": "1685379440000", + "primaryUser": "john.doe@example.com", + "subscribers": "mike.johnson@example.io,sarah.smith@example.org,emily.davis@example.co" + } +} +``` + +
+ +These examples showcases using the **to email** node with dynamic configuration based on the substitution of message and message metadata fields. + +
+
diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/card/value_color_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/card/value_color_fn.md new file mode 100644 index 0000000000..639043bfbc --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/card/value_color_fn.md @@ -0,0 +1,40 @@ +#### Color function + +
+
+ +*function (value): string* + +A JavaScript function used to compute a color. + +**Parameters:** + +
    +
  • value: primitive (number/string/boolean) - A value of the current datapoint. +
  • +
+ +**Returns:** + +Should return string value presenting color. + +In case no data is returned, color value from **Color** settings field will be used. + +
+ +##### Examples + +* Calculate color depending on `temperature` telemetry value: + +```javascript +var temperature = value; +if (typeof temperature !== undefined) { + var percent = (temperature + 60)/120 * 100; + return tinycolor.mix('blue', 'red', percent).toHexString(); +} +return 'blue'; +{:copy-code} +``` + +
+
diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/indicator/shape_attribute_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/indicator/shape_attribute_fn.md new file mode 100644 index 0000000000..f2e69877d2 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/widget/lib/indicator/shape_attribute_fn.md @@ -0,0 +1,69 @@ +#### Shape attribute name + +A string expression value that allows you dynamically select shape image depending on attribute name (ex. tankShape). + +**Attribute values that could be used to add the image:** + +Alt text + +``` +Vertical Oval +{:copy-code} +``` +Alt text + +``` +Vertical Cylinder +{:copy-code} +``` +Alt text + +``` +Vertical Capsule +{:copy-code} +``` +Alt text + +``` +Rectangle +{:copy-code} +``` +Alt text + +``` +Horizontal Oval +{:copy-code} +``` +Alt text + +``` +Horizontal Ellipse +{:copy-code} +``` +Alt text + +``` +Horizontal Dish Ends +{:copy-code} +``` +Alt text + +``` +Horizontal Cylinder +{:copy-code} +``` +Alt text + +``` +Horizontal Capsule +{:copy-code} +``` +Alt text + +``` +Horizontal 2:1 Elliptical +{:copy-code} +``` + +
+
diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/color_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/color_fn.md index 64e994fc3e..ede068d68f 100644 --- a/ui-ngx/src/assets/help/en_US/widget/lib/map/color_fn.md +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/color_fn.md @@ -31,7 +31,7 @@ if (type == 'colorpin') { var temperature = data['temperature']; if (typeof temperature !== undefined) { var percent = (temperature + 60)/120 * 100; - return tinycolor.mix('blue', 'red', amount = percent).toHexString(); + return tinycolor.mix('blue', 'red', percent).toHexString(); } return 'blue'; } diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/path_color_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/path_color_fn.md index 056e47d0eb..c4df0a99a9 100644 --- a/ui-ngx/src/assets/help/en_US/widget/lib/map/path_color_fn.md +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/path_color_fn.md @@ -31,7 +31,7 @@ if (type == 'colorpin') { var temperature = data['temperature']; if (typeof temperature !== undefined) { var percent = (temperature + 60)/120 * 100; - return tinycolor.mix('blue', 'red', amount = percent).toHexString(); + return tinycolor.mix('blue', 'red', percent).toHexString(); } return 'blue'; } diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/path_point_color_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/path_point_color_fn.md index 30f5e0f4d5..de092704c3 100644 --- a/ui-ngx/src/assets/help/en_US/widget/lib/map/path_point_color_fn.md +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/path_point_color_fn.md @@ -31,7 +31,7 @@ if (type == 'colorpin') { var temperature = data['temperature']; if (typeof temperature !== undefined) { var percent = (temperature + 60)/120 * 100; - return tinycolor.mix('blue', 'red', amount = percent).toHexString(); + return tinycolor.mix('blue', 'red', percent).toHexString(); } return 'blue'; } diff --git a/ui-ngx/src/assets/help/en_US/widget/lib/map/polygon_color_fn.md b/ui-ngx/src/assets/help/en_US/widget/lib/map/polygon_color_fn.md index 736bd727f0..b4cc0c5223 100644 --- a/ui-ngx/src/assets/help/en_US/widget/lib/map/polygon_color_fn.md +++ b/ui-ngx/src/assets/help/en_US/widget/lib/map/polygon_color_fn.md @@ -31,7 +31,7 @@ if (type == 'thermostat') { var temperature = data['temperature']; if (typeof temperature !== undefined) { var percent = (temperature + 60)/120 * 100; - return tinycolor.mix('blue', 'red', amount = percent).toHexString(); + return tinycolor.mix('blue', 'red', percent).toHexString(); } return 'blue'; } diff --git a/ui-ngx/src/assets/help/images/rulenode/examples/change-originator-ft-2.png b/ui-ngx/src/assets/help/images/rulenode/examples/change-originator-ft-2.png new file mode 100644 index 0000000000..744b29d3da Binary files /dev/null and b/ui-ngx/src/assets/help/images/rulenode/examples/change-originator-ft-2.png differ diff --git a/ui-ngx/src/assets/help/images/rulenode/examples/change-originator-ft-3.png b/ui-ngx/src/assets/help/images/rulenode/examples/change-originator-ft-3.png new file mode 100644 index 0000000000..4fa936482c Binary files /dev/null and b/ui-ngx/src/assets/help/images/rulenode/examples/change-originator-ft-3.png differ diff --git a/ui-ngx/src/assets/help/images/rulenode/examples/change-originator-ft.png b/ui-ngx/src/assets/help/images/rulenode/examples/change-originator-ft.png new file mode 100644 index 0000000000..8b3c555e80 Binary files /dev/null and b/ui-ngx/src/assets/help/images/rulenode/examples/change-originator-ft.png differ diff --git a/ui-ngx/src/assets/help/images/rulenode/examples/customer-attributes-ft.png b/ui-ngx/src/assets/help/images/rulenode/examples/customer-attributes-ft.png new file mode 100644 index 0000000000..e9b3538812 Binary files /dev/null and b/ui-ngx/src/assets/help/images/rulenode/examples/customer-attributes-ft.png differ diff --git a/ui-ngx/src/assets/help/images/rulenode/examples/originator-attributes-ft.png b/ui-ngx/src/assets/help/images/rulenode/examples/originator-attributes-ft.png new file mode 100644 index 0000000000..fceb295f4d Binary files /dev/null and b/ui-ngx/src/assets/help/images/rulenode/examples/originator-attributes-ft.png differ diff --git a/ui-ngx/src/assets/help/images/rulenode/examples/originator-fields-ft.png b/ui-ngx/src/assets/help/images/rulenode/examples/originator-fields-ft.png new file mode 100644 index 0000000000..750c7d900f Binary files /dev/null and b/ui-ngx/src/assets/help/images/rulenode/examples/originator-fields-ft.png differ diff --git a/ui-ngx/src/assets/help/images/rulenode/examples/originator-telemetry-ft-2.png b/ui-ngx/src/assets/help/images/rulenode/examples/originator-telemetry-ft-2.png new file mode 100644 index 0000000000..f581107bc8 Binary files /dev/null and b/ui-ngx/src/assets/help/images/rulenode/examples/originator-telemetry-ft-2.png differ diff --git a/ui-ngx/src/assets/help/images/rulenode/examples/originator-telemetry-ft.png b/ui-ngx/src/assets/help/images/rulenode/examples/originator-telemetry-ft.png new file mode 100644 index 0000000000..8c289fafe9 Binary files /dev/null and b/ui-ngx/src/assets/help/images/rulenode/examples/originator-telemetry-ft.png differ diff --git a/ui-ngx/src/assets/help/images/rulenode/examples/related-device-attributes-ft.png b/ui-ngx/src/assets/help/images/rulenode/examples/related-device-attributes-ft.png new file mode 100644 index 0000000000..83aca746a3 Binary files /dev/null and b/ui-ngx/src/assets/help/images/rulenode/examples/related-device-attributes-ft.png differ diff --git a/ui-ngx/src/assets/help/images/rulenode/examples/related-entity-data-ft.png b/ui-ngx/src/assets/help/images/rulenode/examples/related-entity-data-ft.png new file mode 100644 index 0000000000..bbfcb112f9 Binary files /dev/null and b/ui-ngx/src/assets/help/images/rulenode/examples/related-entity-data-ft.png differ diff --git a/ui-ngx/src/assets/help/images/rulenode/examples/tenant-attributes-ft.png b/ui-ngx/src/assets/help/images/rulenode/examples/tenant-attributes-ft.png new file mode 100644 index 0000000000..f2c96e8297 Binary files /dev/null and b/ui-ngx/src/assets/help/images/rulenode/examples/tenant-attributes-ft.png differ diff --git a/ui-ngx/src/assets/help/images/rulenode/examples/to-email-ft.png b/ui-ngx/src/assets/help/images/rulenode/examples/to-email-ft.png new file mode 100644 index 0000000000..544441de87 Binary files /dev/null and b/ui-ngx/src/assets/help/images/rulenode/examples/to-email-ft.png differ diff --git a/ui-ngx/src/assets/linux.svg b/ui-ngx/src/assets/linux.svg new file mode 100644 index 0000000000..66f505437f --- /dev/null +++ b/ui-ngx/src/assets/linux.svg @@ -0,0 +1 @@ + diff --git a/ui-ngx/src/assets/locale/locale.constant-ca_ES.json b/ui-ngx/src/assets/locale/locale.constant-ca_ES.json index dc66a0e9f5..db7d6b06ce 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ca_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-ca_ES.json @@ -632,6 +632,7 @@ "attributes": "Atributs", "latest-telemetry": "Última telemetria", "attributes-scope": "Abast dels atributs del dispositiu", + "scope-telemetry": "Telemetria", "scope-latest-telemetry": "Última telemetria", "scope-client": "Atributs del Client", "scope-server": "Atributs del Servidor", @@ -676,10 +677,6 @@ "executions": "Execucions", "javascript": "JavaScript", "javascript-executions": "Execucions JavaScript", - "javascript-functions": "Funcions JavaScript", - "javascript-functions-daily-activity": "Activitat diària de funcions JavaScript", - "javascript-functions-hourly-activity": "Activitat horària de funcions JavaScript", - "javascript-functions-monthly-activity": "Activitat mensual de funcions JavaScript", "latest-error": "Últim error", "messages": "Missatges", "notifications": "Notificacions", @@ -1376,13 +1373,7 @@ "device-configuration": "Configuració del dispositiu", "transport-configuration": "Configuració del transport", "wizard": { - "device-wizard": "Assistent de dispositiu", - "device-details": "Detalls del dispositiu", - "new-device-profile": "Crear un nou perfil de dispositiu", - "existing-device-profile": "Seleccionar un perfil existent", - "specific-configuration": "Configuració específica", - "customer-to-assign-device": "Client al que assignar el dispositiu", - "add-credentials": "Afegir credencial" + "device-details": "Detalls del dispositiu" }, "unassign-devices-from-edge-title": "Està segur de que desitja desassignar {count, plural, =1 {1 dispositivo} other {# dispositivos} }?", "unassign-devices-from-edge-text": "Després de la confirmació, tots els dispositius seleccionats quedaran sense assignar i la vora no podrà accedir a ells." @@ -1404,8 +1395,6 @@ "delete": "Esborrar perfil de dispositiu", "copyId": "Copiar ID de perfil", "name-max-length": "El nom ha de ser inferior a 256", - "new-device-profile-name": "Nom del perfil", - "new-device-profile-name-required": "Cal nom de perfil.", "name": "Nom", "name-required": "Cal nom.", "type": "Tipus de perfil", @@ -1514,9 +1503,7 @@ "condition-duration-value-required": "Cal valor de duració.", "condition-duration-time-unit-required": "Cal una unidad del temps.", "advanced-settings": "Configuració avançada", - "alarm-rule-details": "Detalls", - "alarm-rule-details-hint": "Suggeriment: utilitzeu ${keyName} per substituir els valors de les claus d'atribut o de telemetria que s'utilitzen en condicions de regla d'alarma.", - "add-alarm-rule-details": "Afegir detalls", + "alarm-rule-additional-info-hint": "Suggeriment: utilitzeu ${keyName} per substituir els valors de les claus d'atribut o de telemetria que s'utilitzen en condicions de regla d'alarma.", "alarm-rule-mobile-dashboard": "Tauler de control mòbil", "alarm-rule-mobile-dashboard-hint": "Utilitzat per l'aplicació mòbil com a quadre de comandament de detalls d'alarma", "alarm-rule-no-mobile-dashboard": "No s'ha seleccionat cap tauler", @@ -1526,7 +1513,6 @@ "propagate-alarm-to-owner": "Propaga l'alarma al propietari de l'entitat (Customer or Tenant)", "propagate-alarm-to-owner-hierarchy": "Propaga l'alarma a la jerarquia dels propietaris de l'entitat", "propagate-alarm-to-tenant": "Propaga l'alarma al llogater", - "alarm-details": "Detalls d'alarma", "alarm-rule-condition": "Condicions de regla d'alarma", "enter-alarm-rule-condition-prompt": "Per favor, afegir una condició d'alarma", "edit-alarm-rule-condition": "Editar condició d'alarma", @@ -1997,7 +1983,6 @@ "help-text": "Utilitzeu '%' segons les necessitats: '%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", "use-entity-name-filter": "Utilitza filtre", "entity-list-empty": "No hi ha entitats seleccionades.", - "entity-type-list-empty": "No hi ha tipus d'entitat seleccionats.", "entity-name-filter-required": "Cal filtre de nom d'entitat.", "entity-name-filter-no-entity-matched": "No hi ha entitats que comencen amb '{{entity}}' .", "all-subtypes": "Tots", @@ -2810,7 +2795,6 @@ "browse-files": "Navegar fitxers" }, "image-input": { - "drop-image-or": "Arrossegar i deixar anar imatges o", "drop-images-or": "Arrossegar i deixar anar imatges o", "no-images": "No hi ha imatges seleccionades", "images": "imatges" @@ -3662,8 +3646,7 @@ "assign-rulechains": "Assignar cadenes de regles", "delete-rulechains": "Eliminar cadenes de regles", "unassign-rulechain": "Anul·lar assignació de cadena de regles", - "unassign-rulechains-from-edge-action-title": "Anul·lar assignació {count, plural, =1 {1 cadena de regles} other {# cadenes de regles} } des vores", - "assign-new-rulechain": "Assignar nova cadena de regles" + "unassign-rulechains-from-edge-action-title": "Anul·lar assignació {count, plural, =1 {1 cadena de regles} other {# cadenes de regles} } des vores" }, "rulenode": { "details": "Detalls", @@ -3675,7 +3658,6 @@ "name-required": "Cal el nom.", "name-max-length": "El nom ha de ser inferior a 256", "type": "Tipus", - "description": "Descripció", "delete": "Eliminar node de regles", "select-all-objects": "Seleccionar tots els nodes i conexions", "deselect-all-objects": "Desfer selecció de tots els nodes i conexions", @@ -4489,7 +4471,7 @@ "created": "{{created}} creades", "updated": "{{updated}} actualitzades", "deleted": "{{deleted}} esborrades", - "remove-other-entities-confirm-text": "Atenció! Aquesta acció esborrarà permanentment todas les entitats actuals
no presents a la versió a restaurar.

Escriu eliminar altres entitats per confirmar.", + "remove-other-entities-confirm-text": "Atenció! Aquesta acció esborrarà permanentment todas les entitats actuals
no presents a la versió a restaurar.

Escriu \"remove other entities\" per confirmar.", "auto-commit-to-branch": "autopublicar a la branca {{ branch }}", "default-create-entity-version-name": "{{entityName}} actualizació", "sync-strategy-merge-hint": "Crea o actualitza les entitats seleccionades al repositori. Les altres entitats no seran modificades.", @@ -4549,11 +4531,7 @@ "latest-data-key-settings-form-selector": "Selector formulari dels darrers valors", "javascript": "Javascript", "js": "JS", - "remove-widget-type-title": "Eliminar el tipus del widget '{{widgetName}}'?", - "remove-widget-type-text": "Atenció, després de la confirmació el tipus serà eliminat i la informació relacionada serà irrecuperable.", - "remove-widget-type": "Eliminar tipus de widget.", "add-widget-type": "Afegir nou tipus de widget", - "widget-type-load-failed-error": "Error al carregar el tipus de widget!", "widget-template-load-failed-error": "Error al carregar la plantilla del widget!", "add": "Afegir Widget", "undo": "Desfer canvis", @@ -5713,7 +5691,6 @@ "show-empty-space-hidden-action": "Mostrar espai buit en lloc de cel·la oculta", "dont-reserve-space-hidden-action": "No reserveu espai per als botons en cel·la oculta", "display-timestamp": "Mostra columna timestamp", - "display-milliseconds": "Mostrar mil·lisegons", "display-pagination": "Mostrar pàgines", "default-page-size": "Mida de pàgina per defecte", "use-entity-label-tab-name": "Usar etiqueta important al nom de la taula", diff --git a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json index 41f19bd6f7..7402aedc5c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json +++ b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json @@ -445,6 +445,7 @@ "attributes": "Atributy", "latest-telemetry": "Poslední telemetrie", "attributes-scope": "Rozsah atributů entity", + "scope-telemetry": "Telemetrie", "scope-latest-telemetry": "Poslední telemetrie", "scope-client": "Atributy klienta", "scope-server": "Atributy serveru", @@ -487,10 +488,6 @@ "executions": "Zpracování", "javascript": "JavaScript", "javascript-executions": "JavaScript výjimky", - "javascript-functions": "JavaScript funkce", - "javascript-functions-daily-activity": "Denní aktivita JavaScript funkcí", - "javascript-functions-hourly-activity": "Hodinová aktivita JavaScript funkcí", - "javascript-functions-monthly-activity": "Měsíční aktivita JavaScript funkcí", "latest-error": "Poslední chyba", "messages": "Zprávy", "notifications": "Notifikace", @@ -1018,13 +1015,7 @@ "device-configuration": "Konfigurace zařízení", "transport-configuration": "Konfigurace přenosu", "wizard": { - "device-wizard": "Průvodce zařízením", - "device-details": "Detail zařízení", - "new-device-profile": "Vytvořit nový profil zařízení", - "existing-device-profile": "Vybrat existující profil zařízení", - "specific-configuration": "Specifická konfigurace", - "customer-to-assign-device": "Přiřadit zařízení zákazníkovi", - "add-credentials": "Přidat přístupový údaj" + "device-details": "Detail zařízení" }, "unassign-devices-from-edge-title": "Jste se jisti, že chcete odebrat { count, plural, =1 {1 zařízení} other {# zařízení} }?", "unassign-devices-from-edge-text": "Po potvrzení budou všechna vybraná zařízení odebrána a nebudou pro edge dostupná." @@ -1045,8 +1036,6 @@ "set-default": "Učinit profil zařízení defaultním", "delete": "Smazat profil zařízení", "copyId": "Kopírovat Id profilu zařízení", - "new-device-profile-name": "Název profilu zařízení", - "new-device-profile-name-required": "Název profilu zařízení je povinný.", "name": "Název", "name-required": "Název je povinný.", "type": "Typ profilu", @@ -1150,15 +1139,12 @@ "condition-duration-value-required": "Doba trvání je povinná.", "condition-duration-time-unit-required": "Jednotka času je povinná.", "advanced-settings": "Pokročilá nastavení", - "alarm-rule-details": "Detail", - "add-alarm-rule-details": "Přidat detail", "alarm-rule-mobile-dashboard": "Mobilní dashboard", "alarm-rule-mobile-dashboard-hint": "Používán mobilní aplikací jako dashboard detailu alarmu", "alarm-rule-no-mobile-dashboard": "Žádný dashboard nebyl vybrán", "propagate-alarm": "Propagovat alarm", "alarm-rule-relation-types-list": "Typy vztahů ke zpropagování", "alarm-rule-relation-types-list-hint": "Pokud nejsou vybrány žádné typy vztahů, alarmy budou propagovány bez filtru typu vztahu.", - "alarm-details": "Detail alarmu", "alarm-rule-condition": "Podmínka pravidla alarmu", "enter-alarm-rule-condition-prompt": "Přidejte prosím podmínku pravidla alarmu", "edit-alarm-rule-condition": "Editovat podmínku pravidla alarmu", @@ -1530,7 +1516,6 @@ "help-text": "Použijte '%' dle potřeby: '%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", "use-entity-name-filter": "Použít filtr", "entity-list-empty": "Žádné entity nebyly nalezeny.", - "entity-type-list-empty": "Nebyl vybrán žádný typ entity.", "entity-name-filter-required": "Filtr názvu entity je povinný.", "entity-name-filter-no-entity-matched": "Žádné entity začínající '{{entity}}' nebyly nalezeny.", "all-subtypes": "Vše", @@ -2426,7 +2411,6 @@ "search": "Vyhledat řetězy pravidel", "selected-rulechains": "Vybráno { count, plural, =1 {1 řetězů pravidel} other {# řetězů pravidel} }", "open-rulechain": "Otevřít řetěz pravidel", - "assign-new-rulechain": "Přiřadit nový řetěz pravidel", "edge-template-root": "Hlavní šablona", "assign-to-edge": "Přiřadit k edge", "edge-rulechain": "Řetěz pravidel edge", @@ -2457,7 +2441,6 @@ "name": "Název", "name-required": "Název je povinný.", "type": "Typ", - "description": "Popis", "delete": "Smazat uzel pravidla", "select-all-objects": "Vybrat všechny uzly a spojení", "deselect-all-objects": "Zrušit výběr všech uzlů a spojení", @@ -2828,11 +2811,7 @@ "image-preview": "Náhled obrázku", "javascript": "Javascript", "js": "JS", - "remove-widget-type-title": "Jste si jisti, že chcete odebrat typ widgetu '{{widgetName}}'?", - "remove-widget-type-text": "Po potvrzení nebude možné typ widgetu ani žádná související data obnovit.", - "remove-widget-type": "Odebrat typ widgetu", "add-widget-type": "Přidat nový typ widgetu", - "widget-type-load-failed-error": "Nahrání typu widgetu selhalo!", "widget-template-load-failed-error": "Nahrání šablony widgetu selhalo!", "add": "Přidat widget", "undo": "Vrátit změny widgetu", diff --git a/ui-ngx/src/assets/locale/locale.constant-da_DK.json b/ui-ngx/src/assets/locale/locale.constant-da_DK.json index 7662b60a2c..e815ac65c6 100644 --- a/ui-ngx/src/assets/locale/locale.constant-da_DK.json +++ b/ui-ngx/src/assets/locale/locale.constant-da_DK.json @@ -453,6 +453,7 @@ "attributes": "Attributter", "latest-telemetry": "Seneste telemetri", "attributes-scope": "Omfang af entitetsattributter", + "scope-telemetry": "Telemetri", "scope-latest-telemetry": "Seneste telemetri", "scope-client": "Klientattributter", "scope-server": "Serverattributter", @@ -492,10 +493,6 @@ "executions": "Udførelser", "javascript": "JavaScript", "javascript-executions": "JavaScript-udførelser", - "javascript-functions": "JavaScript-funktioner", - "javascript-functions-daily-activity": "JavaScript-funktioners daglige aktivitet", - "javascript-functions-hourly-activity": "JavaScript-funktioners timeaktivitet", - "javascript-functions-monthly-activity": "JavaScript-funktioners månedlige aktivitet", "latest-error": "Seneste fejl", "messages": "Beskeder", "permanent-failures": "${entityName} Permanente fejl", @@ -1095,13 +1092,7 @@ "device-configuration": "Enhedskonfiguration", "transport-configuration": "Transportkonfiguration", "wizard": { - "device-wizard": "Enhedsguide", - "device-details": "Enhedsoplysninger", - "new-device-profile": "Opret ny enhedsprofil", - "existing-device-profile": "Vælg eksisterende enhedsprofil", - "specific-configuration": "Specifik konfiguration", - "customer-to-assign-device": "Kunden skal tildele enheden", - "add-credential": "Tilføj brugeroplysninger" + "device-details": "Enhedsoplysninger" } }, "device-profile": { @@ -1120,8 +1111,6 @@ "set-default": "Gør enhedsprofil standard", "delete": "Slet enhedsprofil", "copyId": "Kopiér enhedsprofil-id", - "new-device-profile-name": "Enhedsprofilnavn", - "new-device-profile-name-required": "Enhedsprofilnavn er påkrævet.", "name": "Navn", "name-required": "Navn er påkrævet.", "type": "Profiltype", @@ -1204,12 +1193,9 @@ "condition-duration-value-required": "Varighedsværdi er påkrævet.", "condition-duration-time-unit-required": "Tidsenhed er påkrævet.", "advanced-settings": "Avancerede indstillinger", - "alarm-rule-details": "Oplysninger", - "add-alarm-rule-details": "Tilføj oplysninger", "propagate-alarm": "Overfør alarm", "alarm-rule-relation-types-list": "Relationstyper, der skal overføres", "alarm-rule-relation-types-list-hint": "Hvis der ikke vælges Overfør relationstyper vil alarmer blive overført uden filtrering på relationstype.", - "alarm-details": "Alarmoplysninger", "alarm-rule-condition": "Alarmregelbetingelse", "enter-alarm-rule-condition-prompt": "Tilføj alarmregelbetingelse", "edit-alarm-rule-condition": "Rediger alarmregelbetingelse", @@ -1305,7 +1291,6 @@ "name-starts-with": "Navnet begynder med", "use-entity-name-filter": "Anvend filter", "entity-list-empty": "Ingen entiteter valgt.", - "entity-type-list-empty": "Ingen entitetstyper valgt.", "entity-name-filter-required": "Entitetsnavnfilter er påkrævet.", "entity-name-filter-no-entity-matched": "", "all-subtypes": "Alle", @@ -2668,7 +2653,6 @@ "name": "Navn", "name-required": "Navn er påkrævet.", "type": "Type", - "description": "Beskrivelse", "delete": "Slet regelknude", "select-all-objects": "Vælg alle knuder og tilslutninger", "deselect-all-objects": "Fravælg alle knuder og tilslutninger", @@ -3343,11 +3327,7 @@ "image-preview": "Forhåndsvisning af billede", "javascript": "Javascript", "js": "JS", - "remove-widget-type-title": "", - "remove-widget-type-text": "Efter bekræftelsen vil widget-typen og alle relaterede data være uoprettelige.", - "remove-widget-type": "Fjern widget-type", "add-widget-type": "Tilføj ny widget-type", - "widget-type-load-failed-error": "Kunne ikke indlæse widget-type!", "widget-template-load-failed-error": "Kunne ikke indlæse widget-skabelon!", "add": "Tilføj widget", "undo": "Fortryd widget-ændringer", diff --git a/ui-ngx/src/assets/locale/locale.constant-de_DE.json b/ui-ngx/src/assets/locale/locale.constant-de_DE.json index 3f37abf513..582e3e2902 100644 --- a/ui-ngx/src/assets/locale/locale.constant-de_DE.json +++ b/ui-ngx/src/assets/locale/locale.constant-de_DE.json @@ -10,11 +10,16 @@ "permission-denied": "Zugriff verweigert", "permission-denied-text": "Sie haben keine Berechtigung, um diesen Vorgang auszuführen!" }, + "account": { + "account": "Benutzerkonto", + "notification-settings": "Benachrichtigungseinstellungen" + }, "action": { "activate": "Aktivieren", "suspend": "Unterbrechen", "save": "Speichern", "saveAs": "Speichern unter", + "move": "Verschieben", "cancel": "Abbrechen", "ok": "OK", "delete": "Löschen", @@ -23,18 +28,20 @@ "no": "Nein", "update": "Aktualisieren", "remove": "Löschen", - "select": "Auswählen", "search": "Suche", "clear-search": "Suchanfrage löschen", "assign": "Zuordnen", "unassign": "Zuordnung aufheben", "share": "Teilen", "make-private": "Privat machen", + "make-public": "Öffentlich machen", "apply": "Anwenden", "apply-changes": "Änderungen übernehmen", "edit-mode": "Bearbeitungsmodus", "enter-edit-mode": "Zum Bearbeitungsmodus wechseln", "decline-changes": "Änderungen nicht übernehmen", + "open": "Öffnen", + "decline": "Verwerfen", "close": "Schließen", "back": "Zurück", "run": "Ausführen", @@ -52,16 +59,27 @@ "import": "Importieren", "export": "Exportieren", "share-via": "Teilen mit {{provider}}", + "select": "Auswählen", "continue": "Fortsetzen", "discard-changes": "Änderungen verwerfen", "download": "Download", + "next": "Nächste", "next-with-label": "Nächste: {{label}}", "read-more": "Mehr dazu", "hide": "Verstecken", "done": "Erledigt", "print": "Drucken", "restore": "Wiederherstellen", - "confirm": "Bestätigen" + "confirm": "Bestätigen", + "more": "Mehr", + "less": "Weniger", + "skip": "Überspringen", + "send": "Senden", + "reset": "Zurücksetzen", + "show-more": "Zeige mehr", + "dont-show-again": "Nicht wieder anzeigen", + "see-documentation": "Siehe Dokumentation", + "clear": "Leeren" }, "aggregation": { "aggregation": "Aggregation", @@ -76,9 +94,11 @@ "none": "kein Wert" }, "admin": { + "settings": "Einstellungen", "general": "Allgemein", "general-settings": "Allgemeine Einstellungen", "home-settings": "Home Einstellungen", + "home": "Home", "outgoing-mail": "E-Mail Versand", "outgoing-mail-settings": "Konfiguration des Postausgangsservers", "system-settings": "Systemeinstellungen", @@ -99,7 +119,7 @@ "timeout-required": "Wartezeit ist erforderlich.", "timeout-invalid": "Das ist keine gültige Wartezeit.", "enable-tls": "TLS aktivieren", - "tls-version" : "TLS-Version", + "tls-version": "TLS-Version", "enable-proxy": "Proxy aktivieren", "proxy-host": "Proxy Host", "proxy-host-required": "Proxy Host ist erforderlich.", @@ -110,8 +130,21 @@ "proxy-password": "Proxy Passwort", "change-password": "Passwort ändern", "send-test-mail": "Test E-Mail senden", + "use-system-mail-settings": "System E-Mail-Server Einstellungen benutzen", + "mail-templates": "E-Mail Vorlagen", + "mail-template-settings": "Einstellungen E-Mail Vorlagen", + "use-system-mail-template-settings": "System E-Mail Vorlagen benutzen", + "mail-template": { + "mail-template": "E-Mail Vorlage", + "test": "E-Mail Nachricht testen", + "activation": "Nachricht Benutzerkontoaktivierung", + "account-activated": "Nachricht Benutzerkonto aktiviert" + }, + "mail-subject": "E-Mail Betreff", + "mail-body": "E-Mail Nachricht", "sms-provider": "SMS Anbieter", "sms-provider-settings": "SMS Anbieter Einstellungen", + "use-system-sms-settings": "System SMS Anbieter benutzen", "sms-provider-type": "SMS Anbieter Typ", "sms-provider-type-required": "SMS Anbieter Typ ist erforderlich.", "sms-provider-type-aws-sns": "Amazon SNS", @@ -134,6 +167,7 @@ "password-expiration-period-days-range": "Die Gültigkeitsdauer des Passworts in Tagen kann nicht negativ sein", "password-reuse-frequency-days": "Häufigkeit der Kennwortwiederverwendung in Tagen", "password-reuse-frequency-days-range": "Die Häufigkeit der Kennwortwiederverwendung in Tagen kann nicht negativ sein", + "allow-whitespace": "Leerzeichen erlauben", "general-policy": "Allgemeine Politik", "max-failed-login-attempts": "Maximale Anzahl fehlgeschlagener Anmeldeversuche, bevor das Konto gesperrt wird", "minimum-max-failed-login-attempts-range": "Die maximale Anzahl fehlgeschlagener Anmeldeversuche kann nicht negativ sein", @@ -142,11 +176,14 @@ "alarm": { "alarm": "Alarm", "alarms": "Alarme", + "all-alarms": "Alle Alarme", "select-alarm": "Alarm auswählen", "no-alarms-matching": "Keine passenden Alarme zu '{{entity}}' wurden gefunden.", "alarm-required": "Alarm ist erforderlich", + "alarm-filter": "Alarmfilter", + "filter": "Filter", "alarm-status": "Alarm Status", - "alarm-status-list": "Alarm Status Liste", + "alarm-status-list": "Alarm Statusliste", "any-status": "Jeder Status", "search-status": { "ANY": "Jeder", @@ -174,6 +211,7 @@ "end-time": "Endzeit", "ack-time": "Bestätigungszeit", "clear-time": "Zeit gelöscht", + "duration": "Dauer", "alarm-severity-list": "Alarm Schwere Liste", "any-severity": "Jede Schwere", "severity-critical": "Kritisch", @@ -183,6 +221,7 @@ "severity-indeterminate": "Unbestimmt", "acknowledge": "Bestätigen", "clear": "Löschen", + "delete": "Löschen", "search": "Alarme suchen", "selected-alarms": "{ count, plural, =1 {1 Alarm} other {# Alarme} } ausgewählt", "no-data": "Keine Daten zum Anzeigen", @@ -193,11 +232,18 @@ "aknowledge-alarms-text": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Alarm} other {# Alarme} } bestätigen möchten?", "aknowledge-alarm-title": "Alarm bestätigen", "aknowledge-alarm-text": "Möchten Sie den Alarm wirklich bestätigen?", + "selected-alarms-are-acknowledged": "Ausgewählte Alarme wurden bereits bestätigt", "clear-alarms-title": "{ count, plural, =1 {1 Alarm} other {# Alarme} } löschen", "clear-alarms-text": "Möchten Sie wirklich { count, plural, =1 {1 Alarm} other {# Alarme} } löschen?", "clear-alarm-title": "Alarm löschen", "clear-alarm-text": "Möchten Sie den Alarm wirklich löschen?", - "alarm-status-filter": "Alarm Status Filter" + "delete-alarms-title": "Lösche { count, plural, =1 {1 Alarm} other {# Alarme} }", + "delete-alarms-text": "Sind Sie sicher { count, plural, =1 {1 Alarm} other {# Alarme} } zu löschen?", + "selected-alarms-are-cleared": "Ausgewählte Alarme wurden bereits gelöscht", + "alarm-status-filter": "Alarm Status Filter", + "alarm-filter-title": "Alarmfilter", + "assigned": "Zugewiesen", + "filter-title": "Filter" }, "alias": { "add": "Alias hinzufügen", @@ -208,7 +254,7 @@ "filter-type-single-entity": "Einzelne Entität", "filter-type-entity-list": "Entitätsliste", "filter-type-entity-name": "Entitätsname", - "filter-type-entity-type": "Entitätstyp", + "filter-type-entity-type": "Entitätstyp", "filter-type-state-entity": "Entität aus dem Dashboard Status", "filter-type-state-entity-description": "Entität aus den Dashboard Status Parametern", "filter-type-asset-type": "Objekttyp", @@ -224,14 +270,14 @@ "filter-type-edge-type-description": "Rand vom Typ '{{edgeTypes}}'", "filter-type-relations-query": "Beziehungsabfrage", "filter-type-relations-query-description": "{{entities}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", + "filter-type-edge-search-query": "Edge-Abfrage", + "filter-type-edge-search-query-description": "Edge vom Typ {{edgeTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", "filter-type-asset-search-query": "Objektabfrage", "filter-type-asset-search-query-description": "Objekte vom Typ {{assetTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", "filter-type-device-search-query": "Geräteabfrage", "filter-type-device-search-query-description": "Geräte vom Typ {{deviceTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", "filter-type-entity-view-search-query": "Entitätsansichtsabfrage", "filter-type-entity-view-search-query-description": "Entitätsansichten vom Typ {{entityViewTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", - "filter-type-edge-search-query": "Randabfrage", - "filter-type-edge-search-query-description": "Rand vom Typ {{edgeTypes}} mit {{relationType}} Beziehung {{direction}} {{rootEntity}}", "entity-filter": "Entitätsfilter", "resolve-multiple": "Als mehrere Entitäten auflösen", "filter-type": "Filtertyp", @@ -250,11 +296,16 @@ "any-relation": "Jede Beziehung" }, "asset": { + "all": "Alle", + "all-assets": "Alle Objekte", + "groups": "Gruppen", + "shared": "Geteilt", "asset": "Objekt", "assets": "Objekte", "management": "Objektverwaltung", "view-assets": "Objekte anzeigen", "add": "Objekt hinzufügen", + "asset-type-max-length": "Objekttyp sollte kürzer als 256 Zeichen sein", "assign-to-customer": "Einem Kunden zuordnen", "assign-asset-to-customer": "Objekte dem Kunden zuordnen", "assign-asset-to-customer-text": "Bitte wählen Sie die Objekte aus, die dem Kunden zugeordnet werden sollen", @@ -277,6 +328,8 @@ "asset-types": "Objekttypen", "name": "Name", "name-required": "Name ist erforderlich.", + "name-max-length": "Name sollte weniger als 256 Zeichen sein", + "label-max-length": "Label sollte weniger als 256 Zeichen sein", "description": "Beschreibung", "type": "Typ", "type-required": "Typ ist erforderlich.", @@ -286,6 +339,7 @@ "asset-details": "Objektdetails", "assign-assets": "Objekte zuordnen", "assign-assets-text": "Kunden { count, plural, =1 {1 Objekt} other {# Objekte} } zuordnen", + "assign-asset-to-edge-text":"Bitte wählen Sie die Objekte aus, die dem Edge zugeordnet werden sollen", "delete-assets": "Objekte löschen", "unassign-assets": "Objektzuordnungen aufheben", "unassign-assets-action-title": "Kunden { count, plural, =1 {1 Objektzuordnung} other {# Objektzuordnungen} } aufheben", @@ -312,7 +366,6 @@ "name-starts-with": "Name des Objekts beginnt mit", "label": "Bezeichnung", "assign-asset-to-edge": "Objekte dem Rand zuordnen", - "assign-asset-to-edge-text":"Bitte wählen Sie die Objekte aus, die dem Rand zugeordnet werden sollen", "unassign-asset-from-edge": "Objekte von Rand entfernen", "unassign-asset-from-edge-title": "Sind Sie sicher, dass Sie die Zuordnung für das Objekt '{{assetName}}' aufheben möchten?", "unassign-asset-from-edge-text": "Nach Bestätigung wird die Zuordnung des Objekts aufgehoben und es ist für den Kunden nicht mehr zugänglich.", @@ -323,17 +376,23 @@ "attribute": { "attributes": "Eigenschaften", "latest-telemetry": "Neueste Telemetrie", + "no-latest-telemetry": "Keine Telemetriedaten", "attributes-scope": "Entitätseigenschaftsbereich", + "scope-telemetry": "Telemetrie", "scope-latest-telemetry": "Neueste Telemetrie", "scope-client": "Client Eigenschaften", "scope-server": "Server Eigenschaften", "scope-shared": "Gemeinsame Eigenschaften", "add": "Eigenschaft hinzufügen", + "add-attribute-prompt": "Bitte Attribut hinzufügen", "key": "Schlüssel", + "key-max-length": "Der Schlüssel sollte weniger als 256 Zeichen haben", "last-update-time": "Datum der letzten Aktualisierung", "key-required": "Eigenschaftsschlüssel ist erforderlich.", "value": "Wert", "value-required": "Eigenschaftswert ist erforderlich.", + "telemetry-key-required": "Telemetrieschlüssel ist erforderlich", + "telemetry-value-required": "Telemetriewert ist erforderlich", "delete-attributes-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Eigenschaft} other {# Eigenschaften} } löschen möchten?", "delete-attributes-text": "Seien Sie vorsichtig, nach der Bestätigung werden alle ausgewählten Eigenschaften entfernt.", "delete-attributes": "Eigenschaften löschen", @@ -345,7 +404,12 @@ "add-to-dashboard": "Zum Dashboard hinzufügen", "add-widget-to-dashboard": "Widget zum Dashboard hinzufügen", "selected-attributes": "{ count, plural, =1 {1 Eigenschaft} other {# Eigenschaften} } ausgewählt", - "selected-telemetry": "{ count, plural, =1 {1 Telemetrieeinheit } other {# Telemetrieeinheiten} } ausgewählt" + "selected-telemetry": "{ count, plural, =1 {1 Telemetrieeinheit } other {# Telemetrieeinheiten} } ausgewählt", + "no-attributes-text": "Keine Attribute gefunden", + "no-telemetry-text": "Keine Telemetriedaten gefunden", + "copy-key": "Schlüssel kopieren", + "add-telemetry": "Telemetriedaten hinzufügen", + "copy-value": "Wert kopieren" }, "audit-log": { "audit": "Audit", @@ -366,17 +430,27 @@ "type-credentials-updated": "Anmeldeinformationen wurden aktualisiert", "type-assigned-to-customer": "Kunden Zuordnung", "type-unassigned-from-customer": "Kunden Zuordnung aufgehoben", - "type-assigned-to-edge": "Rand Zuordnung", - "type-unassigned-from-edge": "Rand Zuordnung aufgehoben", + "type-assigned-to-edge": "Zum Edge hinzugefügt", + "type-unassigned-from-edge": "Vom Edge entfernt", "type-activated": "Aktiviert", "type-suspended": "Ausgesetzt", "type-credentials-read": "Anmeldeinformationen gelesen", "type-attributes-read": "Eigenschaften gelesen", + "type-added-to-entity-group": "Zur Gruppe hinzugefügt", + "type-removed-from-entity-group": "Aus der Gruppe entfernt", "type-relation-add-or-update": "Beziehung aktualisiert", "type-relation-delete": "Beziehung gelöscht", "type-relations-delete": "Alle Beziehungen gelöscht", "type-alarm-ack": "Bestätigt", "type-alarm-clear": "Gelöscht", + "type-alarm-assign": "Zugeweisen", + "type-alarm-unassign": "nicht zugewiesen", + "type-added-comment": "Kommentar hinzugefügt", + "type-updated-comment": "Kommentar aktualisiert", + "type-deleted-comment": "Kommentar gelöscht", + "type-rest-api-rule-engine-call": "Regelkette REST API Aufruf", + "type-made-public": "Öffentlich machen", + "type-made-private": "Privat machen", "type-login": "Anmeldung", "type-logout": "Ausloggen", "type-lockout": "Aussperrung", @@ -387,7 +461,15 @@ "action-data": "Aktionsdaten", "failure-details": "Fehlerdetails", "search": "Audit-Protokolle durchsuchen", - "clear-search": "Suche leeren" + "clear-search": "Suche leeren", + "type-assigned-from-tenant": "Vom Tenant zugewiesen", + "type-assigned-to-tenant": "Dem Tenant zugewiesen", + "type-provision-success": "Gerätebereitstellung erfolgreich", + "type-provision-failure": "Gerätebereitstellung fehlgeschlagen", + "type-timeseries-updated": "Telemetriedaten aktualisiert", + "type-timeseries-deleted": "Telemetriedaten gelöscht", + "type-owner-changed": "Besitzer wurde gewechselt", + "type-sms-sent": "SMS gesendet" }, "confirm-on-exit": { "message": "Sie haben nicht gespeicherte Änderungen. Möchten Sie diese Seite wirklich verlassen?", @@ -404,7 +486,10 @@ "address2": "Adresse 2", "phone": "Telefon", "email": "E-Mail", - "no-address": "Keine Adresse" + "no-address": "Keine Adresse", + "state-max-length": "Staat sollte weniger als 256 Zeichen haben", + "phone-max-length": "Telefonnummer sollte weniger als 256 Zeichen haben", + "city-max-length": "Ort sollte weniger als 256 Zeichen haben" }, "common": { "username": "Benutzername", @@ -412,7 +497,12 @@ "enter-username": "Benutzername eingeben", "enter-password": "Passwort eingeben", "enter-search": "Suche eingeben", - "created-time": "Erstellungszeit" + "created-time": "Erstellungszeit", + "loading": "wird geladen...", + "proceed": "Fortfahren", + "open-details-page": "Detailseite öffnen", + "not-found": "Nicht gefunden", + "documentation": "Dokumentation" }, "content-type": { "json": "Json", @@ -420,6 +510,11 @@ "binary": "Binär (Base64)" }, "customer": { + "all": "Alle", + "all-customers": "Alle Kunden", + "groups": "Gruppen", + "shared": "Geteilt", + "hierarchy": "Hierarchie", "customer": "Kunde", "customers": "Kunden", "management": "Kundenverwaltung", @@ -432,7 +527,6 @@ "public-devices": "Öffentliche Geräte", "public-assets": "Öffentliche Objekte", "public-entity-views": "Öffentliche Entitätsansichten", - "public-edges": "Öffentliche Rand", "add": "Kunde hinzufügen", "delete": "Kunde löschen", "manage-customer-users": "Kundenbenutzer verwalten", @@ -441,7 +535,6 @@ "manage-public-devices": "Öffentliche Geräte verwalten", "manage-public-dashboards": "Öffentliche Dashboards verwalten", "manage-customer-assets": "Kundenobjekte verwalten", - "manage-customer-edges": "Randobjekte verwalten", "manage-public-assets": "Öffentliche Objekte verwalten", "manage-public-edges": "Öffentliche Rand verwalten", "add-customer-text": "Neuen Kunden hinzufügen", @@ -458,6 +551,7 @@ "manage-dashboards": "Dashboards verwalten", "title": "Titel", "title-required": "Titel ist erforderlich.", + "title-max-length": "Titel sollte weniger asl 256 Zeichen haben", "description": "Beschreibung", "details": "Details", "events": "Ereignisse", @@ -763,17 +857,17 @@ "edge": "Edge", "edge-instances": "Kanteninstanzen", "edge-file": "Edge-Datei", - "management": "Rand verwalten", - "no-edges-matching": "Keine passenden Rand '{{entity}}' gefunden.", - "add": "Rand hinzufügen", - "no-edges-text": "Kein Rand gefunden.", - "edge-details": "Details der Rand", - "add-edge-text": "Neue Rand hinzufügen", - "delete": "Rand löschen", - "delete-edge-title": "Möchten Sie des Rands wirklich löschen '{{edgeName}}'?", - "delete-edge-text": "Seien Sie vorsichtig, nach der Bestätigung werden der Rand und alle zugehörigen Daten nicht wiederhergestellt.", - "delete-edges-title": "Sind Sie sicher, dass Sie die Rand löschen möchten { count, plural, =1 {1 Rand} other {# Rand} }?", - "delete-edges-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Rand entfernt und alle zugehörigen Daten werden nicht wiederhergestellt.", + "management": "Edge verwalten", + "no-edges-matching": "Keine passenden Edge '{{entity}}' gefunden.", + "add": "Edge hinzufügen", + "no-edges-text": "Keine Edge gefunden.", + "edge-details": "Details der Edge", + "add-edge-text": "Neue Edge hinzufügen", + "delete": "Edge löschen", + "delete-edge-title": "Möchten Sie des Edges wirklich löschen '{{edgeName}}'?", + "delete-edge-text": "Seien Sie vorsichtig, nach der Bestätigung werden der Edge und alle zugehörigen Daten nicht wiederhergestellt.", + "delete-edges-title": "Sind Sie sicher, dass Sie die Edge löschen möchten { count, plural, =1 {1 Edge} other {# Edges} }?", + "delete-edges-text": "Vorsicht, nach Bestätigung werden alle ausgewählten Edge entfernt und alle zugehörigen Daten werden nicht wiederhergestellt.", "name": "Name", "name-starts-with": "Der Kantenname beginnt mit", "name-required": "Name ist erforderlich.", @@ -782,62 +876,62 @@ "events": "Ereignisse", "copy-id": "Regelketten-ID kopieren", "id-copied-message": "Regelketten-ID wurde in die Zwischenablage kopiert", - "sync": "Sync Edge", - "edge-required": "Rand ist erforderlich.", - "edge-type": "Randtyp", - "edge-type-required": "Randtyp ist erforderlich.", + "sync": "Edge synchonisieren", + "edge-required": "Edge ist erforderlich.", + "edge-type": "Edgetyp", + "edge-type-required": "Edgetyp ist erforderlich.", "event-action": "Ereignisaktion", "entity-id": "Entität ID", - "select-edge-type": "Randtyp auswählen", + "select-edge-type": "Edgetyp auswählen", "assign-to-customer": "Einem Kunden zuordnen", - "assign-to-customer-text": "Bitte wählen Sie den Kunden aus, dem die Rand zugeordnet werden sollen", - "assign-edge-to-customer": "Rand dem Kunden zuordnen", - "assign-edge-to-customer-text": "Bitte wählen Sie die Rand aus, die dem Kunden zugeordnet werden sollen", + "assign-to-customer-text": "Bitte wählen Sie den Kunden aus, dem die Edge zugeordnet werden sollen", + "assign-edge-to-customer": "Edge dem Kunden zuordnen", + "assign-edge-to-customer-text": "Bitte wählen Sie die Edge aus, die dem Kunden zugeordnet werden sollen", "assignedToCustomer": "Dem Kunden zugewiesen", "edge-public": "Edge ist öffentlich", "assigned-to-customer": "Kunden Zuordnung", "unassign-from-customer": "Kunden Zuordnung aufgehoben", - "unassign-edge-title": "Sind Sie sicher, dass Sie die Zuordnung zum Rand '{{edgeName}}' wirklich aufheben möchten?", - "unassign-edge-text": "Nach der Bestätigung ist der Rand nicht zugeordnet und für den Kunden nicht zugänglich.", - "unassign-edges-title": "Sind Sie sicher, dass Sie die Zuordnung aufheben möchten { count, plural, =1 {1 Rand} other {# Rand} }?", + "unassign-edge-title": "Sind Sie sicher, dass Sie die Zuordnung zum Edge '{{edgeName}}' wirklich aufheben möchten?", + "unassign-edge-text": "Nach der Bestätigung ist der Edge nicht zugeordnet und für den Kunden nicht zugänglich.", + "unassign-edges-title": "Sind Sie sicher, dass Sie die Zuordnung aufheben möchten { count, plural, =1 {1 Edge} other {# Edges} }?", "unassign-edges-text": "Nach der Bestätigung werden alle ausgewählten Kanten nicht zugewiesen und sind für den Kunden nicht zugänglich.", - "make-public": "Rand öffentlich machen", - "make-public-edge-title": "Sind Sie sicher, dass Sie der Rand '{{edgeName}}' öffentlich machen möchten?", + "make-public": "Edge öffentlich machen", + "make-public-edge-title": "Sind Sie sicher, dass Sie der Edge '{{edgeName}}' öffentlich machen möchten?", "make-public-edge-text": "Nach Bestätigung wird der Rabd und alle zugehörigen Daten anderen zugänglich gemacht.", - "make-private": "Rand privat machen", + "make-private": "Edge privat machen", "public": "Öffentlich", - "make-private-edge-title": "Sind Sie sicher, dass Sie der Rand '{{edgeName}}' privat machen möchten?", - "make-private-edge-text": "Nach der Bestätigung werden der Rand und dessen Daten privat und sind für andere nicht mehr zugänglich.", - "import": "Rand importieren", + "make-private-edge-title": "Sind Sie sicher, dass Sie der Edge '{{edgeName}}' privat machen möchten?", + "make-private-edge-text": "Nach der Bestätigung werden der Edge und dessen Daten privat und sind für andere nicht mehr zugänglich.", + "import": "Edge importieren", "label": "Bezeichnung", "load-entity-error": "Entität nicht gefunden. Fehler beim Laden der Informationen", - "assign-new-edge": "Neue Rand zuordnen", - "unassign-from-edge": "Rand zuweisen", - "edge-key": "Rand Schlüssel", - "copy-edge-key": "Rand Schlüssel kopieren", - "edge-key-copied-message": "Rand Schlüssel wurde in die Zwischenablage kopiert", - "edge-secret": "Rand Geheimnis", - "copy-edge-secret": "Rand Geheimnis kopieren", - "edge-secret-copied-message": "Rand Geheimnis wurde in die Zwischenablage kopiert", - "edge-assets": "Rand-Objekte verwalten", - "edge-devices": "Rand-Geräte verwalten", - "edge-entity-views": "Rand-Entitätsansichten verwalten", - "edge-dashboards": "Rand-Dashboards verwalten", - "edge-rulechains": "Kantenregelketten", - "assets": "Rand Objekte", + "assign-new-edge": "Neue Edge zuordnen", + "unassign-from-edge": "Edge zuweisen", + "edge-key": "Edge-Schlüssel", + "copy-edge-key": "Edge-Schlüssel kopieren", + "edge-key-copied-message": "Edge-Schlüssel wurde in die Zwischenablage kopiert", + "edge-secret": "Edge-Geheimnis", + "copy-edge-secret": "Edge-Geheimnis kopieren", + "edge-secret-copied-message": "Edge-Geheimnis wurde in die Zwischenablage kopiert", + "edge-assets": "Edge-Objekte verwalten", + "edge-devices": "Edge-Geräte verwalten", + "edge-entity-views": "Edge-Entitätsansichten verwalten", + "edge-dashboards": "Edge-Dashboards verwalten", + "edge-rulechains": "Edge-Regelketten", + "assets": "Edge Objekte", "devices": "Objekte Geräte", "entity-views": "Objekte Entitätsansichten", - "dashboard": "Kanten-Dashboard", - "dashboards": "Rand Dashboards", + "dashboard": "Edge-Dashboard", + "dashboards": "Edge-Dashboards", "rulechain-templates": "Regelkettenvorlagen", - "rulechains": "Rand Regelketten", - "search": "Kanten durchsuchen", - "selected-edges": "{count, plural, =1 {1 Rand} other {# Rand} } ausgewählt", + "rulechains": "Edge-Regelketten", + "search": "Edges durchsuchen", + "selected-edges": "{count, plural, =1 {1 Edge} other {# Edges} } ausgewählt", "any-edge": "Beliebige Kante", - "no-edge-types-matching": "Es wurden keine Kantentypen gefunden, die mit '{{entitySubtype}}' übereinstimmen.", - "edge-type-list-empty": "Keine Kantentypen ausgewählt.", - "edge-types": "Kantentypen", - "enter-edge-type": "Geben Sie den Kantentyp ein", + "no-edge-types-matching": "Es wurden keine Edgetypen gefunden, die mit '{{entitySubtype}}' übereinstimmen.", + "edge-type-list-empty": "Keine Edgetypen ausgewählt.", + "edge-types": "Edgetypen", + "enter-edge-type": "Geben Sie den Edgetyp ein", "deployed": "Bereitgestellt", "pending": "Steht aus", "downlinks": "Downlinks", @@ -850,38 +944,38 @@ "edge-event": { "type-dashboard": "Dashboard", "type-asset": "Asset", - "type-device": "Device", - "type-device-profile": "Device Profile", - "type-entity-view": "Entity View", + "type-device": "Gerät", + "type-device-profile": "Geräteprofile", + "type-entity-view": "Entitätsansicht", "type-alarm": "Alarm", - "type-rule-chain": "Rule Chain", - "type-rule-chain-metadata": "Rule Chain Metadata", + "type-rule-chain": "Regelkette", + "type-rule-chain-metadata": "Regelkettenmetadaten", "type-edge": "Edge", - "type-user": "User", - "type-customer": "Customer", - "type-relation": "Relation", - "type-widgets-bundle": "Widgets Bundle", - "type-widgets-type": "Widgets Type", - "type-admin-settings": "Admin Settings", - "action-type-added": "Added", - "action-type-deleted": "Deleted", - "action-type-updated": "Updated", - "action-type-post-attributes": "Post Attributes", - "action-type-attributes-updated": "Attributes Updated", - "action-type-attributes-deleted": "Attributes Deleted", - "action-type-timeseries-updated": "Timeseries Updated", - "action-type-credentials-updated": "Credentials Updated", - "action-type-assigned-to-customer": "Assigned to Customer", - "action-type-unassigned-from-customer": "Unassigned from Customer", - "action-type-relation-add-or-update": "Relation Add or Update", - "action-type-relation-deleted": "Relation Deleted", - "action-type-rpc-call": "RPC Call", - "action-type-alarm-ack": "Alarm Ack", - "action-type-alarm-clear": "Alarm Clear", - "action-type-assigned-to-edge": "Assigned to Edge", - "action-type-unassigned-from-edge": "Unassigned from Edge", - "action-type-credentials-request": "Credentials Request", - "action-type-entity-merge-request": "Entity Merge Request" + "type-user": "Benutzer", + "type-customer": "Kunde", + "type-relation": "Beziehung", + "type-widgets-bundle": "Widgetpaket", + "type-widgets-type": "Widgettyp", + "type-admin-settings": "Admineinstellungen", + "action-type-added": "Hinzugefügt", + "action-type-deleted": "Gelöscht", + "action-type-updated": "Aktualisiert", + "action-type-post-attributes": "Attribute posten", + "action-type-attributes-updated": "Attribute Aktualisiert", + "action-type-attributes-deleted": "Attribute Gelöscht", + "action-type-timeseries-updated": "Timeseries Aktualisiert", + "action-type-credentials-updated": "Zugangsdaten Aktualisiert", + "action-type-assigned-to-customer": "Dem Kunden zugewiesen", + "action-type-unassigned-from-customer": "Kundenzuweisung aufgehoben", + "action-type-relation-add-or-update": "Beziehung hinzufügen oder Aktualisieren", + "action-type-relation-deleted": "Beziehung gelöscht", + "action-type-rpc-call": "RPC-Aufruf", + "action-type-alarm-ack": "Alarm zurkenntnisnehmen", + "action-type-alarm-clear": "Alarm löschen", + "action-type-assigned-to-edge": "Der Edge zuweisen", + "action-type-unassigned-from-edge": "Edge-Zuweisung aufheben", + "action-type-credentials-request": "Zugangsdatenabfrage", + "action-type-entity-merge-request": "Entitäts-Merge-Request" }, "error": { "unable-to-connect": "Es konnte keine Verbindung zum Server hergestellt werden! Bitte überprüfen Sie Ihre Internetverbindung.", @@ -913,7 +1007,6 @@ "name-starts-with": "Name beginnt mit", "use-entity-name-filter": "Filter verwenden", "entity-list-empty": "Keine Entitäten ausgewählt.", - "entity-type-list-empty": "Keine Entitättypen ausgewählt.", "entity-name-filter-required": "Entitätsnamenfilter ist erforderlich.", "entity-name-filter-no-entity-matched": "Keine Entitäten beginnend mit '{{entity}}' gefunden.", "all-subtypes": "Alle", @@ -976,10 +1069,10 @@ "type-rulenodes": "Regelknoten", "list-of-rulenodes": "{ count, plural, =1 {Ein Regelknoten} other {Liste von # Regelknoten} }", "rulenode-name-starts-with": "Regelknoten beginnend mit '{{prefix}}'", - "type-edge": "Randtyp", - "type-edges": "Randtyp", - "list-of-edges": "{ count, plural, =1 {1 Rand} other {# Rand} }", - "edge-name-starts-with": "Rand beginnend mit '{{prefix}}'", + "type-edge": "Edgetyp", + "type-edges": "Eandtyp", + "list-of-edges": "{ count, plural, =1 {1 Edge} other {# Edges} }", + "edge-name-starts-with": "Edge beginnend mit '{{prefix}}'", "type-current-customer": "Aktueller Kunde", "search": "Entitäten suchen", "selected-entities": "{ count, plural, =1 {Entität} other {# Entitäten} } ausgewählt", @@ -1121,7 +1214,7 @@ }, "extension": { "extensions": "Erweiterungen", - "selected-extensions": "{ count, plural, =1 {Erweiterung} other {# extensions} } ausgewählt", + "selected-extensions": "{ count, plural, =1 {Erweiterung} other {# Erweiterungen} } ausgewählt", "type": "Typ", "key": "Schlüssel", "value": "Wert", @@ -1135,7 +1228,7 @@ "edit": "Erweiterung bearbeiten", "delete-extension-title": "Möchten Sie die Erweiterung '{{extensionId}}' wirklich löschen?", "delete-extension-text": "Vorsicht, nach Bestätigung werden die Erweiterung und alle zugehörigen Daten nicht wiederhergestellt.", - "delete-extensions-title": "Möchten Sie wirklich löschen? { count, plural, =1 {1 extension} other {# extensions} }?", + "delete-extensions-title": "Möchten Sie wirklich { count, plural, =1 {1 Erweiterung} other {# Erweiterungen} } löschen?", "delete-extensions-text": "Vorsicht, nach der Bestätigung werden alle ausgewählten Erweiterungen entfernt.", "converters": "Konverter", "converter-id": "Konverter-ID", @@ -1162,7 +1255,7 @@ "host": "Host", "port": "Port", "port-range": "Der Port sollte im Bereich von 1 bis 65535 liegen.", - "ssl": "Ssl", + "ssl": "SSL", "credentials": "Zugangsdaten", "username": "Benutzername", "password": "Passwort", @@ -1225,7 +1318,7 @@ "opc-keystore": "Schlüsselspeicher", "opc-type": "Typ", "opc-keystore-type": "Typ", - "opc-keystore-location": "Standort *", + "opc-keystore-location": "Speicherort *", "opc-keystore-password": "Passwort", "opc-keystore-alias": "Alias", "opc-keystore-key-password": "Schlüsselpasswort", @@ -1459,7 +1552,6 @@ "management": "Regelverwaltung", "debug-mode": "Modus zur Fehlersuche", "assign-rulechains": "Regelketten zuweisen", - "assign-new-rulechain": "Neues Regelkette zuweisen", "delete-rulechains": "Regelketten löschen", "unassign-rulechain": "Nicht zugeordnete Regelkette", "unassign-rulechains": "Nicht zugeordnete Regelketten", @@ -1495,7 +1587,6 @@ "name": "Name", "name-required": "Name ist erforderlich.", "type": "Typ", - "description": "Beschreibung", "delete": "Regelknoten löschen", "select-all-objects": "Alle Knoten und Verbindungen auswählen", "deselect-all-objects": "Auswahl aller Knoten und Verbindungen aufheben", @@ -1585,26 +1676,98 @@ "hours": "Stunden", "minutes": "Minuten", "seconds": "Sekunden", - "advanced": "Erweitert" + "advanced": "Erweitert", + "predefined": { + "yesterday": "Gestern", + "day-before-yesterday": "Vorgestern", + "this-day-last-week": "Dieser Tag letzte Woche", + "previous-week": "Letzte Woche (So - Sa)", + "previous-week-iso": "Letzte Woche (Mo - So)", + "previous-month": "Letzter Monat", + "previous-quarter": "Letztes Quartal", + "previous-half-year": "Letztes Halbjahr", + "previous-year": "Letztes Jahr", + "current-hour": "Aktuelle Stunde", + "current-day": "Aktueller Tag", + "current-day-so-far": "Aktueller Tag bisher", + "current-week": "Aktuelle Wiche (So - Sa)", + "current-week-iso": "Aktuelle Woche (Mo - So)", + "current-week-so-far": "Aktuelle Woche bisher (So - Sa)", + "current-week-iso-so-far": "Aktuelle Woche bisher (Mo - So)", + "current-month": "Aktueller Monat", + "current-month-so-far": "Aktueller Monat bisher", + "current-quarter": "Aktuelles Quartal", + "current-quarter-so-far": "Aktuelles Quartal bisher", + "current-half-year": "Aktuelles Halbjahr", + "current-half-year-so-far": "Aktuelles Halbjahr bisher", + "current-year": "Aktuelles Jahr", + "current-year-so-far": "Aktuelles Jahr bisher" + } + }, + "timeunit": { + "milliseconds": "Millisekunden", + "seconds": "Sekunden", + "minutes": "Minuten", + "hours": "Stunden", + "days": "Tage" }, "timewindow": { + "timewindow": "Zeitfenster", + "years": "{ years, plural, =1 { Jahr } other {# Jahre } }", + "years-short": "{{ years }}J", + "months": "{ months, plural, =1 { Monat } other {# Monate } }", + "months-short": "{{ months }}M", + "weeks": "{ weeks, plural, =1 { Woche } other {# Wochen } }", + "weeks-short": "{{ weeks }}W", "days": "{ days, plural, =1 { Tag } other {# Tage } }", + "days-short": "{{ days }}T", "hours": "{ hours, plural, =0 { Stunde } =1 {1 Stunde } other {# Stunden } }", + "hr": "{{ hr }} Std", + "hr-short": "{{ hr }}S", "minutes": "{ minutes, plural, =0 { Minute } =1 {1 Minute } other {# Minuten } }", + "min": "{{ min }} Min", + "min-short": "{{ min }}m", "seconds": "{ seconds, plural, =0 { Sekunde } =1 {1 Sekunde } other {# Sekunden } }", + "sec": "{{ sec }} Sek", + "sec-short": "{{ sec }}s", + "short": { + "days": "{ days, plural, =1 {1 Tag } other {# Tage } }", + "hours": "{ hours, plural, =1 {1 Stunde } other {# Stunden } }", + "minutes": "{{minutes}} Min ", + "seconds": "{{seconds}} Sek " + }, "realtime": "Echtzeit", "history": "Historie", "last-prefix": "letzte", "period": "von {{ startTime }} bis {{ endTime }}", "edit": "Zeitfenster bearbeiten", "date-range": "Datumsbereich", + "for-all-time": "Für immer", "last": "Letzte", - "time-period": "Zeitfenster" + "time-period": "Zeitfenster", + "hide": "Verstecken", + "interval": "Intervall", + "just-now": "Soeben", + "just-now-lower": "soeben", + "ago": "vor", + "style": "Zeitfenster Style", + "icon": "Symbol", + "icon-position": "Position Symbol", + "icon-position-left": "Links", + "icon-position-right": "Rechts", + "font": "Schriftart", + "color": "Farbe", + "displayTypePrefix": "Echtzeit-/Verlaufspräfix anzeigen", + "preview": "Vorschau" }, "user": { - "user": "User", - "users": "Users", - "customer-users": "Kunden Users", + "all": "Alle", + "all-users": "Alle Benutzer", + "groups": "Gruppen", + "user": "Benutzer", + "users": "Benutzer", + "management": "Benutzerverwaltung", + "customer-users": "Kunden-Benutzer", "tenant-admins": "Mandanten-Administratoren", "sys-admin": "System-Administrator", "tenant-admin": "Mandanten-Administrator", @@ -1615,6 +1778,7 @@ "add-user-text": "Neuen Benutzer hinzufügen", "no-users-text": "Keine Benutzer gefunden", "user-details": "Benutzer-Details", + "delete-users": "Benutzer löschen", "delete-user-title": "Möchten Sie den Benutzer '{{userEmail}}' wirklich löschen?", "delete-user-text": "Vorsicht, nach Bestätigung werden der Benutzer und alle zugehörigen Daten gelöscht.", "delete-users-title": "Sind Sie sicher, dass Sie { count, plural, =1 {1 Benutzer} other {# Benutzer} } löschen möchten??", @@ -1646,7 +1810,11 @@ "disable-account": "Benutzerkonto deaktivieren", "enable-account": "Benutzerkonto aktivieren", "enable-account-message": "Benutzerkonto wurde erfolgreich aktiviert!", - "disable-account-message": "Benutzerkonto wurde erfolgreich deaktiviert!" + "disable-account-message": "Benutzerkonto wurde erfolgreich deaktiviert!", + "copyId": "Benutzer-Id kopieren", + "idCopiedMessage": "Benutzer-Id in die Zwischenablage kopiert", + "user-list": "Benutzerliste", + "user-list-required": "Benutzerliste ist erforderlich" }, "value": { "type": "Wertetyp", @@ -1705,11 +1873,7 @@ "settings-schema": "Einstellungsschema", "datakey-settings-schema": "Datenschlüssel-Einstellungsschema", "javascript": "Javascript", - "remove-widget-type-title": "Möchten Sie den Widget-Typ '{{widgetName}}' wirklich entfernen?", - "remove-widget-type-text": "Nach der Bestätigung werden der Widget-Typ und alle zugehörigen Daten nicht wiederhergestellt.", - "remove-widget-type": "Widget-Typ entfernen", "add-widget-type": "Neuen Widget-Typ hinzufügen", - "widget-type-load-failed-error": "Widget-Typ konnte nicht geladen werden!", "widget-template-load-failed-error": "Widget-Vorlage konnte nicht geladen werden!", "add": "Widget hinzufügen", "undo": "Widget-Änderungen widerrufen ", @@ -1814,6 +1978,27 @@ }, "widgets": { "date-range-navigator": { + "date-range-picker-settings": "Einstellungen Datumsbereichsauswahl", + "hide-date-range-picker": "Datumsbereichsauswahl verstecken", + "picker-one-panel": "Datumsbereichsauswahl in einem Bereich", + "picker-auto-confirm": "Automatische Bestätigung der Datumsbereichsauswahl", + "picker-show-template": "Vorlage für die Anzeige der Datumsbereichsauswahl", + "first-day-of-week": "Erster Tag der Woche", + "interval-settings": "Intervalleinstellungen", + "hide-interval": "Intervall verstecken", + "initial-interval": "Anfangsintervall", + "interval-hour": "Stunde", + "interval-day": "Tag", + "interval-week": "Woche", + "interval-two-weeks": "2 Wochen", + "interval-month": "Monat", + "interval-three-months": "3 Monate", + "interval-six-months": "6 Monate", + "step-settings": "Schritteinstellungen", + "hide-step-size": "Schrittweite verstecken", + "initial-step-size": "Anfangsschrittweite", + "hide-labels": "Beschriftung verstecken", + "use-session-storage": "Sessionspeicher verwenden", "localizationMap": { "Sun": "So.", "Mon": "Mo.", @@ -1898,6 +2083,7 @@ }, "icon": { "icon": "Symbol", + "icons": "Symbole", "select-icon": "Symbol auswählen", "material-icons": "Material-Symbole", "show-all": "Alle Symbole anzeigen" diff --git a/ui-ngx/src/assets/locale/locale.constant-el_GR.json b/ui-ngx/src/assets/locale/locale.constant-el_GR.json index 2bc94ebf42..0594dd5853 100644 --- a/ui-ngx/src/assets/locale/locale.constant-el_GR.json +++ b/ui-ngx/src/assets/locale/locale.constant-el_GR.json @@ -291,6 +291,7 @@ "attributes": "Χαρακτηριστικά", "latest-telemetry": "Τελευταία τηλεμετρία", "attributes-scope": "Πεδίο εφαρμογής Χαρακτηριστικών Οντότητας", + "scope-telemetry": "Τηλεμετρία", "scope-latest-telemetry": "Τελευταία τηλεμετρία", "scope-client": "Χαρακτηριστικά Client", "scope-server": "Χαρακτηριστικά Server", @@ -875,7 +876,6 @@ "name-starts-with": "το όνομα αρχίζει από", "use-entity-name-filter": "χρήση φίλτρου", "entity-list-empty": "Δεν έχουν επιλεγεί οντότητες.", - "entity-type-list-empty": "Δεν έχουν επιλεγεί τύποι οντότητας.", "entity-name-filter-required": "Απαιτείται φίλτρο ονόματος οντότητας.", "entity-name-filter-no-entity-matched": "Δεν βρέθηκαν οντότητες οι οποίες αρχίζουν από '{{entity}}'.", "all-subtypes": "Όλοι", @@ -1833,7 +1833,6 @@ "name": "Όνομα", "name-required": "Απαιτείται όνομα.", "type": "Τύπος", - "description": "Περιγραφή", "delete": "Διαγραφή κανόνα κόμβου", "select-all-objects": "Επιλογή όλων των κόμβων και συνδέσεων", "deselect-all-objects": "Αποεπιλογή όλων των κόμβων και συνδέσεων", @@ -2342,11 +2341,7 @@ "settings-schema": "Ρυθμίσεις σχήματος", "datakey-settings-schema": "Πλήκτρο δεδομένων σχήματος ρυθμίσεων", "javascript": "Javascript", - "remove-widget-type-title": "Είστε σίγουροι ότι θέλετε να αφαιρέσετε τον τύπο Widget '{{widgetName}}'?", - "remove-widget-type-text": "Προσοχή, μετά την επιβεβαίωση ο τύπος Widget και όλα τα σχετικά δεδομένα θα διαγραφούν οριστικά.", - "remove-widget-type": "Αφαίρεση τύπου Widget", "add-widget-type": "Προσθήκη νέου τύπου Widget", - "widget-type-load-failed-error": "Αποτυχία φόρτωσης τύπου Widget!", "widget-template-load-failed-error": "Αποτυχία φόρτωσης προτύπου Widget!", "add": "Προσθήκη Widget", "undo": "Αναίρεση αλλαγών Widget", diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 5518e49e6c..f2821c7718 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -10,11 +10,16 @@ "permission-denied": "Permission Denied", "permission-denied-text": "You don't have permission to perform this operation!" }, + "account": { + "account": "Account", + "notification-settings": "Notification settings" + }, "action": { "activate": "Activate", "suspend": "Suspend", "save": "Save", "saveAs": "Save as", + "move": "Move", "cancel": "Cancel", "ok": "OK", "delete": "Delete", @@ -67,7 +72,15 @@ "more": "More", "less": "Less", "skip": "Skip", - "send": "Send" + "send": "Send", + "reset": "Reset", + "show-more": "Show more", + "dont-show-again": "Do not show again", + "see-documentation": "See documentation", + "clear": "Clear", + "upload": "Upload", + "delete-anyway": "Delete anyway", + "delete-selected": "Delete selected" }, "aggregation": { "aggregation": "Aggregation", @@ -87,7 +100,7 @@ "general-settings": "General Settings", "home-settings": "Home Settings", "home": "Home", - "outgoing-mail": "Mail Server", + "outgoing-mail": "Mail server", "outgoing-mail-settings": "Outgoing Mail Server Settings", "system-settings": "System Settings", "test-mail-sent": "Test mail was successfully sent!", @@ -95,6 +108,23 @@ "base-url-required": "Base URL is required.", "prohibit-different-url": "Prohibit to use hostname from the client request headers", "prohibit-different-url-hint": "This setting should be enabled for production environments. May cause security issues when disabled", + "device-connectivity": { + "device-connectivity": "Device connectivity", + "http-s": "HTTP(s)", + "mqtt-s": "MQTT(s)", + "coap-s": "COAP(s)", + "http": "HTTP", + "https": "HTTPs", + "mqtt": "MQTT", + "mqtts": "MQTTs", + "coap": "COAP", + "coaps": "COAPs", + "hint": "If host or port fields are empty, default protocol value will be used.", + "host": "Host", + "port": "Port", + "port-pattern": "Port must be a positive integer.", + "port-range": "Port should be in a range from 1 to 65535." + }, "mail-from": "Mail From", "mail-from-required": "Mail From is required.", "smtp-protocol": "SMTP protocol", @@ -152,7 +182,10 @@ "password-policy": "Password policy", "minimum-password-length": "Minimum password length", "minimum-password-length-required": "Minimum password length is required", - "minimum-password-length-range": "Minimum password length should be in a range from 5 to 50", + "minimum-password-length-range": "Minimum password length should be in a range from 6 to 50", + "maximum-password-length": "Maximum password length", + "maximum-password-length-min": "Maximum password length should be at least 6", + "maximum-password-length-less-min": "Maximum password length should be greater than minimum length", "minimum-uppercase-letters": "Minimum number of uppercase letters", "minimum-uppercase-letters-range": "Minimum number of uppercase letters can't be negative", "minimum-lowercase-letters": "Minimum number of lowercase letters", @@ -166,6 +199,8 @@ "password-reuse-frequency-days": "Password reuse frequency in days", "password-reuse-frequency-days-range": "Password reuse frequency in days can't be negative", "allow-whitespace": "Allow whitespace", + "force-reset-password-if-no-valid": "Force to reset password if not valid", + "force-reset-password-if-no-valid-hint": "Please be careful when enabling this feature: it will require users with not valid password to reset their password via email.", "general-policy": "General policy", "max-failed-login-attempts": "Maximum number of failed login attempts, before account is locked", "minimum-max-failed-login-attempts-range": "Maximum number of failed login attempts can't be negative", @@ -507,6 +542,7 @@ "severity-indeterminate": "Indeterminate", "acknowledge": "Acknowledge", "clear": "Clear", + "delete": "Delete", "search": "Search alarms", "selected-alarms": "{ count, plural, =1 {1 alarm} other {# alarms} } selected", "no-data": "No data to display", @@ -517,10 +553,14 @@ "aknowledge-alarms-text": "Are you sure you want to acknowledge { count, plural, =1 {1 alarm} other {# alarms} }?", "aknowledge-alarm-title": "Acknowledge Alarm", "aknowledge-alarm-text": "Are you sure you want to acknowledge Alarm?", + "selected-alarms-are-acknowledged": "Selected alarms are already acknowledged", "clear-alarms-title": "Clear { count, plural, =1 {1 alarm} other {# alarms} }", "clear-alarms-text": "Are you sure you want to clear { count, plural, =1 {1 alarm} other {# alarms} }?", "clear-alarm-title": "Clear Alarm", "clear-alarm-text": "Are you sure you want to clear Alarm?", + "delete-alarms-title": "Delete { count, plural, =1 {1 alarm} other {# alarms} }", + "delete-alarms-text": "Are you sure you want to delete { count, plural, =1 {1 alarm} other {# alarms} }?", + "selected-alarms-are-cleared": "Selected alarms are already cleared", "alarm-status-filter": "Alarm Status Filter", "alarm-filter-title": "Alarm Filter", "assigned": "Assigned", @@ -538,7 +578,11 @@ "search-propagated-alarms": "Search propagated alarms", "comments": "Alarm comments", "show-more": "Show more", - "additional-info": "Additional info" + "additional-info": "Additional info", + "alarm-type": "Alarm type", + "enter-alarm-type": "Enter alarm type", + "no-alarm-types-matching": "No alarm types matching '{{entitySubtype}}' were found.", + "alarm-type-list-empty": "No alarm types selected." }, "alarm-activity": { "add": "Add a comment...", @@ -588,7 +632,7 @@ "filter-type-device-search-query-description": "Devices with types {{deviceTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}", "filter-type-entity-view-search-query": "Entity view search query", "filter-type-entity-view-search-query-description": "Entity views with types {{entityViewTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}", - "filter-type-apiUsageState": "Api Usage State", + "filter-type-apiUsageState": "API Usage State", "filter-type-edge-search-query": "Edge search query", "filter-type-edge-search-query-description": "Edges with types {{edgeTypes}} that have {{relationType}} relation {{direction}} {{rootEntity}}", "entity-filter": "Entity filter", @@ -614,7 +658,7 @@ "assets": "Assets", "management": "Asset management", "view-assets": "View Assets", - "add": "Add Asset", + "add": "Add asset", "asset-type-max-length": "Asset type should be less than 256", "assign-to-customer": "Assign to customer", "assign-asset-to-customer": "Assign Asset(s) To Customer", @@ -631,7 +675,7 @@ "asset-type": "Asset type", "asset-type-required": "Asset type is required.", "select-asset-type": "Select asset type", - "enter-asset-type": "Enter asset type", + "enter-asset-type": "Enter asset profile", "any-asset": "Any asset", "no-asset-types-matching": "No asset types matching '{{entitySubtype}}' were found.", "asset-type-list-empty": "No asset types selected.", @@ -692,7 +736,9 @@ "attribute": { "attributes": "Attributes", "latest-telemetry": "Latest telemetry", + "no-latest-telemetry": "No latest telemetry", "attributes-scope": "Entity attributes scope", + "scope-telemetry": "Telemetry", "scope-latest-telemetry": "Latest telemetry", "scope-client": "Client attributes", "scope-server": "Server attributes", @@ -704,6 +750,8 @@ "key-required": "Attribute key is required.", "value": "Value", "value-required": "Attribute value is required.", + "telemetry-key-required": "Telemetry key is required", + "telemetry-value-required": "Telemetry value is required", "delete-attributes-title": "Are you sure you want to delete { count, plural, =1 {1 attribute} other {# attributes} }?", "delete-attributes-text": "Be careful, after the confirmation all selected attributes will be removed.", "delete-attributes": "Delete attributes", @@ -719,11 +767,23 @@ "no-attributes-text": "No attributes found", "no-telemetry-text": "No telemetry found", "copy-key": "Copy key", - "copy-value": "Copy value" + "add-telemetry": "Add telemetry", + "copy-value": "Copy value", + "delete-timeseries": { + "start-time": "Start time", + "ends-on": "Ends on", + "strategy": "Strategy", + "delete-strategy": "Delete strategy", + "all-data": "Delete all data", + "all-data-except-latest-value": "Delete all data except latest value", + "latest-value": "Delete latest value", + "all-data-for-time-period": "Delete all data for time period", + "rewrite-latest-value": "Rewrite latest value" + } }, "api-usage": { "api-features": "API features", - "api-usage": "Api Usage", + "api-usage": "API usage", "alarm": "Alarm", "alarms-created": "Alarms created", "alarms-created-daily-activity": "Alarms created daily activity", @@ -738,12 +798,14 @@ "email-messages-monthly-activity": "Email messages monthly activity", "exceptions": "Exceptions", "executions": "Executions", + "scripts": "Scripts", + "scripts-hourly-activity": "Scripts hourly activity", + "scripts-daily-activity": "Scripts daily activity", + "scripts-monthly-activity": "Scripts monthly activity", "javascript": "JavaScript", "javascript-executions": "JavaScript executions", - "javascript-functions": "JavaScript functions", - "javascript-functions-daily-activity": "JavaScript functions daily activity", - "javascript-functions-hourly-activity": "JavaScript functions hourly activity", - "javascript-functions-monthly-activity": "JavaScript functions monthly activity", + "tbel": "TBEL", + "tbel-executions": "TBEL executions", "latest-error": "Latest Error", "messages": "Messages", "notifications": "Notifications", @@ -782,12 +844,24 @@ "view-details": "View details", "view-statistics": "View statistics" }, + "api-limit": { + "cassandra-queries": "Cassandra queries", + "entity-version-creation": "Entity version creation", + "entity-version-load": "Entity version load", + "notification-requests": "Notification requests", + "notification-requests-per-rule": "Notification requests per rule", + "rest-api-requests": "REST API requests", + "rest-api-requests-per-customer": "REST API requests per customer", + "transport-messages": "Transport messages", + "transport-messages-per-device": "Transport messages per device", + "ws-updates-per-session": "WS updates per session" + }, "audit-log": { "audit": "Audit", - "audit-logs": "Audit Logs", + "audit-logs": "Audit logs", "timestamp": "Timestamp", - "entity-type": "Entity Type", - "entity-name": "Entity Name", + "entity-type": "Entity type", + "entity-name": "Entity name", "user": "User", "type": "Type", "status": "Status", @@ -865,7 +939,9 @@ "created-time": "Created time", "loading": "Loading...", "proceed": "Proceed", - "open-details-page": "Open details page" + "open-details-page": "Open details page", + "not-found": "Not found", + "documentation": "Documentation" }, "content-type": { "json": "Json", @@ -886,7 +962,7 @@ "public-assets": "Public Assets", "public-edges": "Public Edges", "public-entity-views": "Public Entity Views", - "add": "Add Customer", + "add": "Add customer", "delete": "Delete customer", "manage-customer-users": "Manage customer users", "manage-customer-devices": "Manage customer devices", @@ -928,6 +1004,13 @@ "edges": "Customer edge instances", "manage-edges": "Manage edges" }, + "date": { + "last-update-n-ago": "Last update N ago", + "last-update-n-ago-text": "Last update {{ agoText }}", + "custom-date": "Custom date", + "format": "Format", + "preview": "Preview" + }, "datetime": { "date-from": "Date from", "time-from": "Time from", @@ -939,7 +1022,7 @@ "dashboards": "Dashboards", "management": "Dashboard management", "view-dashboards": "View Dashboards", - "add": "Add Dashboard", + "add": "Add dashboard", "assign-dashboard-to-customer": "Assign Dashboard(s) To Customer", "assign-dashboard-to-customer-text": "Please select the dashboards to assign to the customer", "assign-dashboard-to-edge-title": "Assign Dashboard(s) To Edge", @@ -1106,6 +1189,7 @@ "hide-details": "Hide details", "select-state": "Select target state", "state-controller": "State controller", + "state-controller-default": "static (deprecated)", "search": "Search dashboards", "selected-dashboards": "{ count, plural, =1 {1 dashboard} other {# dashboards} } selected", "home-dashboard": "Home dashboard", @@ -1116,10 +1200,12 @@ "assign-dashboard-to-edge": "Assign Dashboard(s) To Edge", "assign-dashboard-to-edge-text": "Please select the dashboards to assign to the edge", "non-existent-dashboard-state-error": "Dashboard state with id \"{{ stateId }}\" is not found", - "edit-mode": "Edit mode" + "edit-mode": "Edit mode", + "duplicate-state-action": "Duplicate state" }, "datakey": { "settings": "Settings", + "general": "General", "advanced": "Advanced", "key": "Key", "label": "Label", @@ -1177,7 +1263,13 @@ "delta-calculation-result": "Delta calculation result", "delta-calculation-result-previous-value": "Previous value", "delta-calculation-result-delta-absolute": "Delta (absolute)", - "delta-calculation-result-delta-percent": "Delta (percent)" + "delta-calculation-result-delta-percent": "Delta (percent)", + "source": "Source", + "latest": "Latest", + "latest-value": "Latest value", + "delta": "delta", + "percent": "percent", + "absolute": "absolute" }, "datasource": { "type": "Datasource type", @@ -1220,7 +1312,7 @@ "device-list-empty": "No devices selected.", "device-name-filter-required": "Device name filter is required.", "device-name-filter-no-device-matched": "No devices starting with '{{device}}' were found.", - "add": "Add Device", + "add": "Add device", "assign-to-customer": "Assign to customer", "assign-device-to-customer": "Assign Device(s) To Customer", "assign-device-to-customer-text": "Please select the devices to assign to the customer", @@ -1315,13 +1407,14 @@ "password": "Password", "secret": "Secret", "secret-required": "Secret is required.", - "device-type": "Device type", + "device-type": "Device profile", "device-type-required": "Device type is required.", "select-device-type": "Select device type", - "enter-device-type": "Enter device type", + "enter-device-type": "Enter device profile", "any-device": "Any device", - "no-device-types-matching": "No device types matching '{{entitySubtype}}' were found.", - "device-type-list-empty": "No device types selected.", + "no-device-types-matching": "No device profiles matching '{{entitySubtype}}' were found.", + "device-type-list-empty": "No device profiles selected!", + "device-profile-type-list-empty": "At least one device profile should be selected.", "device-types": "Device types", "name": "Name", "name-required": "Name is required.", @@ -1360,16 +1453,28 @@ "device-configuration": "Device configuration", "transport-configuration": "Transport configuration", "wizard": { - "device-wizard": "Device Wizard", - "device-details": "Device details", - "new-device-profile": "Create new device profile", - "existing-device-profile": "Select existing device profile", - "specific-configuration": "Specific configuration", - "customer-to-assign-device": "Customer to assign the device", - "add-credentials": "Add credentials" + "device-details": "Device details" }, "unassign-devices-from-edge-title": "Are you sure you want to unassign { count, plural, =1 {1 device} other {# devices} }?", - "unassign-devices-from-edge-text": "After the confirmation all selected devices will be unassigned and won't be accessible by the edge." + "unassign-devices-from-edge-text": "After the confirmation all selected devices will be unassigned and won't be accessible by the edge.", + "time": "Time", + "connectivity": { + "check-connectivity": "Check connectivity", + "device-created-check-connectivity": "Device created. Let's check connectivity!", + "loading-check-connectivity-command": "Loading check connectivity commands...", + "use-following-instructions": "Use the following instructions for sending telemetry on behalf of the device using shell", + "execute-following-command": "Execute the following command", + "install-curl-windows": "Starting Windows 10 b17063, cURL is available by default", + "install-curl-macos": "Starting Mac OS X 10.2 6C115 (Jaguar), cURL is available by default", + "install-mqtt-windows": "Use the instructions to download, install, setup and run mosquitto_pub", + "install-coap-client": "Use the instructions to download, install, setup and run coap-client", + "install-necessary-client-tools": "Install necessary client tools", + "mqtts-x509-command": "Use the following documentation to connect the device via MQTT with authorization X509", + "coaps-x509-command": "Use the following documentation to connect the device via CoAP over DTLS with authorization X509", + "snmp-command": "Use the following documentation to connect the device through the SNMP.", + "sparkplug-command": "Use the following documentation to connect the device through the MQTT Sparkplug.", + "lwm2m-command": "Use the following documentation to connect the device through the LWM2M." + } }, "asset-profile": { "asset-profile": "Asset profile", @@ -1433,8 +1538,6 @@ "delete": "Delete device profile", "copyId": "Copy device profile Id", "name-max-length": "Name should be less than 256", - "new-device-profile-name": "Device profile name", - "new-device-profile-name-required": "Device profile name is required.", "name": "Name", "name-required": "Name is required.", "type": "Profile type", @@ -1453,6 +1556,7 @@ "transport-type-lwm2m-hint": "LWM2M transport type", "transport-type-snmp": "SNMP", "transport-type-snmp-hint": "Specify SNMP transport configuration", + "transport-type-http": "HTTP", "description": "Description", "default": "Default", "profile-configuration": "Profile configuration", @@ -1551,9 +1655,10 @@ "condition-duration-value-required": "Duration value is required.", "condition-duration-time-unit-required": "Time unit is required.", "advanced-settings": "Advanced settings", - "alarm-rule-details": "Details", - "alarm-rule-details-hint": "Hint: use ${keyName} to substitute values of the attribute or telemetry keys that are used in alarm rule condition.", - "add-alarm-rule-details": "Add details", + "alarm-rule-additional-info": "Additional info", + "edit-alarm-rule-additional-info": "Edit additional info", + "alarm-rule-additional-info-placeholder": "Please provide your comments and adjustments here to display them within Alarm details under Additional info", + "alarm-rule-additional-info-hint": "Hint: use ${keyName} to substitute values of the attribute or telemetry keys that are used in alarm rule condition.", "alarm-rule-mobile-dashboard": "Mobile dashboard", "alarm-rule-mobile-dashboard-hint": "Used by mobile application as an alarm details dashboard", "alarm-rule-no-mobile-dashboard": "No dashboard selected", @@ -1562,7 +1667,6 @@ "alarm-rule-relation-types-list-hint": "If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.", "propagate-alarm-to-owner": "Propagate alarm to entity owner (Customer or Tenant)", "propagate-alarm-to-tenant": "Propagate alarm to Tenant", - "alarm-details": "Alarm details", "alarm-rule-condition": "Alarm rule condition", "enter-alarm-rule-condition-prompt": "Please add alarm rule condition", "edit-alarm-rule-condition": "Edit alarm rule condition", @@ -1842,7 +1946,7 @@ "type-max-length": "Type should be less than 256", "management": "Edge management", "no-edges-matching": "No edges matching '{{entity}}' were found.", - "add": "Add Edge", + "add": "Add edge", "no-edges-text": "No edges found", "edge-details": "Edge details", "add-edge-text": "Add new edge", @@ -1887,6 +1991,8 @@ "make-private-edge-text": "After the confirmation the edge and all its data will be made private and won't be accessible by others.", "import": "Import edge", "install-connect-instructions": "Install & Connect Instructions", + "install-connect-instructions-edge-created": "Edge created! Check Install & Connect Instructions", + "loading-edge-instructions": "Loading edge instructions...", "label": "Label", "load-entity-error": "Failed to load data. Entity has been deleted.", "assign-new-edge": "Assign new edge", @@ -1924,6 +2030,7 @@ "sync-process-started-successfully": "Sync process started successfully!", "missing-related-rule-chains-title": "Edge has missing related rule chain(s)", "missing-related-rule-chains-text": "Assigned to edge rule chain(s) use rule nodes that forward message(s) to rule chain(s) that are not assigned to this edge.

List of missing rule chain(s):
{{missingRuleChains}}", + "upgrade-instructions": "Upgrade Instructions", "widget-datasource-error": "This widget supports only EDGE entity datasource" }, "edge-event": { @@ -1938,11 +2045,15 @@ "type-rule-chain-metadata": "Rule Chain Metadata", "type-edge": "Edge", "type-user": "User", + "type-tenant": "Tenant", + "type-tenant-profile": "Tenant Profile", "type-customer": "Customer", "type-relation": "Relation", "type-widgets-bundle": "Widgets Bundle", "type-widgets-type": "Widgets Type", "type-admin-settings": "Admin Settings", + "type-ota-package": "Ota Package", + "type-queue": "Queue", "action-type-added": "Added", "action-type-deleted": "Deleted", "action-type-updated": "Updated", @@ -1958,6 +2069,8 @@ "action-type-rpc-call": "RPC Call", "action-type-alarm-ack": "Alarm Ack", "action-type-alarm-clear": "Alarm Clear", + "action-type-alarm-assigned": "Alarm Assigned", + "action-type-alarm-unassigned": "Alarm Unassigned", "action-type-assigned-to-edge": "Assigned to Edge", "action-type-unassigned-from-edge": "Unassigned from Edge", "action-type-credentials-request": "Credentials Request", @@ -1990,6 +2103,7 @@ "entity-types": "Entity types", "entity-type-list": "Entity type list", "any-entity": "Any entity", + "add-entity-type": "Add entity type", "enter-entity-type": "Enter entity type", "no-entities-matching": "No entities matching '{{entity}}' were found.", "no-entity-types-matching": "No entity types matching '{{entityType}}' were found.", @@ -1997,7 +2111,7 @@ "help-text": "Use '%' according to need: '%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", "use-entity-name-filter": "Use filter", "entity-list-empty": "No entities selected.", - "entity-type-list-empty": "No entity types selected.", + "entity-type-list-required": "At least one entity type should be selected.", "entity-name-filter-required": "Entity name filter is required.", "entity-name-filter-no-entity-matched": "No entities starting with '{{entity}}' were found.", "all-subtypes": "All", @@ -2019,6 +2133,7 @@ "device-name-starts-with": "Devices whose names start with '{{prefix}}'", "type-device-profile": "Device profile", "type-device-profiles": "Device profiles", + "clear-selected-profiles": "Clear selected profiles", "list-of-device-profiles": "{ count, plural, =1 {One device profile} other {List of # device profiles} }", "device-profile-name-starts-with": "Device profiles whose names start with '{{prefix}}'", "type-asset-profile": "Asset profile", @@ -2080,6 +2195,9 @@ "type-widgets-bundle": "Widgets bundle", "type-widgets-bundles": "Widgets bundles", "list-of-widgets-bundles": "{ count, plural, =1 {One widgets bundle} other {List of # widget bundles} }", + "type-widget": "Widget", + "type-widgets": "Widgets", + "list-of-widgets": "{ count, plural, =1 {One widget} other {List of # widgets} }", "search": "Search entities", "selected-entities": "{ count, plural, =1 {1 entity} other {# entities} } selected", "entity-name": "Entity name", @@ -2088,12 +2206,14 @@ "no-entities-prompt": "No entities found", "no-data": "No data to display", "columns-to-display": "Columns to Display", - "type-api-usage-state": "Api Usage State", + "type-api-usage-state": "API Usage State", "type-edge": "Edge", "type-edges": "Edges", "list-of-edges": "{ count, plural, =1 {One edge} other {List of # edges} }", "edge-name-starts-with": "Edges whose names start with '{{prefix}}'", "type-tb-resource": "Resource", + "type-tb-resources": "Resources", + "list-of-tb-resources": "{ count, plural, =1 {One resource} other {List of # resources} }", "type-ota-package": "OTA package", "type-rpc": "RPC", "type-queue": "Queue", @@ -2127,9 +2247,9 @@ "label": "Label" }, "entity-view": { - "entity-view": "Entity View", + "entity-view": "Entity view", "entity-view-required": "Entity view is required.", - "entity-views": "Entity Views", + "entity-views": "Entity views", "management": "Entity View management", "view-entity-views": "View Entity Views", "entity-view-alias": "Entity View alias", @@ -2155,7 +2275,7 @@ "entity-view-list-empty": "No entity views selected.", "entity-view-name-filter-required": "Entity view name filter is required.", "entity-view-name-filter-no-entity-view-matched": "No entity views starting with '{{entityView}}' were found.", - "add": "Add Entity View", + "add": "Add entity view", "entity-view-public": "Entity view is public", "assign-to-customer": "Assign to customer", "assign-entity-view-to-customer": "Assign Entity View(s) To Customer", @@ -2183,14 +2303,14 @@ "unassign-entity-view": "Unassign entity view", "unassign-entity-views-title": "Are you sure you want to unassign { count, plural, =1 {1 entity view} other {# entity views} }?", "unassign-entity-views-text": "After the confirmation all selected entity views will be unassigned and won't be accessible by the customer.", - "entity-view-type": "Entity View type", - "entity-view-type-required": "Entity View type is required.", + "entity-view-type": "Entity view type", + "entity-view-type-required": "Entity view type is required.", "select-entity-view-type": "Select entity view type", "enter-entity-view-type": "Enter entity view type", "any-entity-view": "Any entity view", "no-entity-view-types-matching": "No entity view types matching '{{entitySubtype}}' were found.", "entity-view-type-list-empty": "No entity view types selected.", - "entity-view-types": "Entity View types", + "entity-view-types": "Entity view types", "created-time": "Created time", "name": "Name", "name-required": "Name is required.", @@ -2511,7 +2631,10 @@ "attribute": "Attribute", "timeseries": "Timeseries", "entity-field": "Entity field", - "constant": "Constant" + "constant": "Constant", + "client-attribute": "Client attribute", + "server-attribute": "Server attribute", + "shared-attribute": "Shared attribute" }, "value-type": { "value-type": "Value type", @@ -2554,38 +2677,158 @@ }, "gateway": { "add-entry": "Add configuration", + "advanced": "Advanced", + "checking-device-activity": "Checking device activity", + "command": "Docker commands", + "command-copied-message": "Docker command has been copied to clipboard", + "configuration": "Configuration", "connector-add": "Add new connector", "connector-enabled": "Enable connector", "connector-name": "Connector name", "connector-name-required": "Connector name is required.", "connector-type": "Connector type", "connector-type-required": "Connector type is required.", - "connectors": "Connectors configuration", + "connectors": "Connectors", + "connectors-config": "Connectors configuration", + "connectors-table-enabled": "Enabled", + "connectors-table-name": "Name", + "connectors-table-type": "Type", + "connectors-table-status": "Status", + "connectors-table-actions": "Actions", + "connectors-table-key": "Key", + "connectors-table-class": "Class", + "rpc-command-send": "Send", + "rpc-command-result": "Response", + "rpc-command-edit-params": "Edit parameters", + "gateway-configuration": "General Configuration", + "docker-label": "Use the following instruction to run IoT Gateway in in Docker compose with credentials for selected device", + "install-docker-compose": "Use the instructions to download, install and setup docker compose", + "download-configuration-file": "Download configuration file", + "download-docker-compose": "Download docker-compose.yml for your gateway", + "launch-gateway": "Launch gateway", + "launch-docker-compose": "Start the gateway using the following command in the terminal from folder with docker-compose.yml file", "create-new-gateway": "Create a new gateway", "create-new-gateway-text": "Are you sure you want create a new gateway with name: '{{gatewayName}}'?", + "created-time": "Created time", + "configuration-delete-dialog-header": "Configurations will be deleted", + "configuration-delete-dialog-body": "Turning off Remote Configuration is possible only if there is physical access to the Gateway. All previous configurations will be deleted.

\nTo turn off configuration, enter gateway name below", + "configuration-delete-dialog-input": "Gateway name", + "configuration-delete-dialog-input-required": "Gateway name is mandatory", + "configuration-delete-dialog-confirm": "Turn Off", "delete": "Delete configuration", "download-tip": "Download configuration file", + "drop-file": "Drop file here or", "gateway": "Gateway", "gateway-exists": "Device with same name is already exists.", "gateway-name": "Gateway name", "gateway-name-required": "Gateway name is required.", "gateway-saved": "Gateway configuration successfully saved.", + "grpc": "GRPC", + "grpc-keep-alive-timeout": "Keep alive timeout (in ms)", + "grpc-keep-alive-timeout-required": "Keep alive timeout is required", + "grpc-keep-alive-timeout-min": "Keep alive timeout can not be less then 1", + "grpc-keep-alive-timeout-pattern": "Keep alive timeout is not valid", + "grpc-keep-alive": "Keep alive (in ms)", + "grpc-keep-alive-required": "Keep alive is required", + "grpc-keep-alive-min": "Keep alive can not be less then 1", + "grpc-keep-alive-pattern": "Keep alive is not valid", + "grpc-min-time-between-pings": "Min time between pings (in ms)", + "grpc-min-time-between-pings-required": "Min time between pings is required", + "grpc-min-time-between-pings-min": "Min time between pings can not be less then 1", + "grpc-min-time-between-pings-pattern": "Min time between pings is not valid", + "grpc-min-ping-interval-without-data": "Min ping interval without data (in ms)", + "grpc-min-ping-interval-without-data-required": "Min ping interval without data is required", + "grpc-min-ping-interval-without-data-min": "Min ping interval without data can not be less then 1", + "grpc-min-ping-interval-without-data-pattern": "Min ping interval without data is not valid", + "grpc-max-pings-without-data": "Max pings without data", + "grpc-max-pings-without-data-required": "Max pings without data is required", + "grpc-max-pings-without-data-min": "Max pings without data can not be less then 1", + "grpc-max-pings-without-data-pattern": "Max pings without data is not valid", + "inactivity-check-period-seconds": "Inactivity check period (in sec)", + "inactivity-check-period-seconds-required": "Inactivity check period is required", + "inactivity-check-period-seconds-min": "Inactivity check period can not be less then 1", + "inactivity-check-period-seconds-pattern": "Inactivity check period is not valid", + "inactivity-timeout-seconds": "Inactivity timeout (in sec)", + "inactivity-timeout-seconds-required": "Inactivity timeout is required", + "inactivity-timeout-seconds-min": "Inactivity timeout can not be less then 1", + "inactivity-timeout-seconds-pattern": "Inactivity timeout is not valid", "json-parse": "Not valid JSON.", "json-required": "Field cannot be empty.", + "logs": { + "logs": "Logs", + "days": "days", + "hours": "hours", + "minutes": "minutes", + "seconds": "seconds", + "date-format": "Date format", + "date-format-required": "Date format required", + "log-format": "Log format", + "log-type": "Log type", + "log-format-required": "Log format required", + "remote": "Remote logging", + "remote-logs": "Remote logs", + "local": "Local logging", + "level": "Log level", + "file-path": "File path", + "file-path-required": "File path required", + "saving-period": "Log saving period", + "saving-period-min": "Log saving period can not be less then 1", + "saving-period-required": "Log saving period required", + "backup-count": "Backup count", + "backup-count-min": "Backup count can not be less then 1", + "backup-count-required": "Backup count required" + }, + "min-pack-send-delay": "Min pack send delay (in ms)", + "min-pack-send-delay-required": "Min pack send delay is required", + "min-pack-send-delay-min": "Min pack send delay can not be less then 0", "no-connectors": "No connectors", "no-data": "No configurations", "no-gateway-found": "No gateway found.", "no-gateway-matching": " '{{item}}' not found.", "path-logs": "Path to log files", "path-logs-required": "Path is required.", + "permit-without-calls": "Keep alive permit without calls", "remote": "Remote configuration", "remote-logging-level": "Logging level", "remove-entry": "Remove configuration", + "remote-shell": "Remote shell", + "remote-configuration": "Remote Configuration", + "other": "Other", "save-tip": "Save configuration file", "security-type": "Security type", "security-types": { "access-token": "Access Token", - "tls": "TLS" + "username-password": "Username and Password", + "tls": "TLS", + "tls-access-token": "TLS + Access Token", + "tls-private-key": "TLS + Private Key" + }, + "server-port": "Server port", + "statistics": { + "statistic": "Statistic", + "statistics": "Statistics", + "statistic-commands-empty": "No statistics available", + "commands": "Commands", + "send-period": "Statistic send period (in sec)", + "send-period-required": "Statistic send period is required", + "send-period-min": "Statistic send period can not be less then 60", + "send-period-pattern": "Statistic send period is not valid", + "check-connectors-configuration": "Check connectors configuration (in sec)", + "check-connectors-configuration-required": "Check connectors configuration is required", + "check-connectors-configuration-min": "Check connectors configuration can not be less then 1", + "check-connectors-configuration-pattern": "Check connectors configuration is not valid", + "add": "Add command", + "timeout": "Timeout", + "timeout-ms": "Timeout (in ms)", + "timeout-required": "Timeout is required", + "timeout-min": "Timeout can not be less then 1", + "timeout-pattern": "Timeout is not valid", + "attribute-name": "Attribute name", + "attribute-name-required": "Attribute name is required", + "command": "Command", + "command-required": "Command is required", + "command-pattern": "Command is not valid", + "remove": "Remove command" }, "storage": "Storage", "storage-max-file-records": "Maximum records in file", @@ -2597,6 +2840,16 @@ "storage-max-records-min": "Minimum number of records is 1.", "storage-max-records-pattern": "Number is not valid.", "storage-max-records-required": "Maximum records is required.", + "storage-read-record-count": "Read record count in storage", + "storage-read-record-count-min": "Minimum number of records is 1.", + "storage-read-record-count-pattern": "Number is not valid.", + "storage-read-record-count-required": "Read record count is required.", + "storage-max-read-record-count": "Max read record count in storage", + "storage-max-read-record-count-min": "Minimum number of records is 1.", + "storage-max-read-record-count-pattern": "Number is not valid.", + "storage-max-read-record-count-required": "Max Read record count is required.", + "storage-data-folder-path": "Data folder path", + "storage-data-folder-path-required": "Data folder path is required.", "storage-pack-size": "Maximum event pack size", "storage-pack-size-min": "Minimum number is 1.", "storage-pack-size-pattern": "Number is not valid.", @@ -2606,9 +2859,11 @@ "storage-type": "Storage type", "storage-types": { "file-storage": "File storage", - "memory-storage": "Memory storage" + "memory-storage": "Memory storage", + "sqlite": "SQLITE" }, "thingsboard": "ThingsBoard", + "general": "General", "thingsboard-host": "ThingsBoard host", "thingsboard-host-required": "Host is required.", "thingsboard-port": "ThingsBoard port", @@ -2621,10 +2876,61 @@ "title-connectors-json": "Connector {{typeName}} configuration", "tls-path-ca-certificate": "Path to CA certificate on gateway", "tls-path-client-certificate": "Path to client certificate on gateway", + "messages-ttl-check-in-hours": "Messages TTL check in hours", + "messages-ttl-check-in-hours-required": "Messages TTL check in hours is required.", + "messages-ttl-check-in-hours-min": "Min number is 1.", + "messages-ttl-check-in-hours-pattern": "Number is not valid.", + "messages-ttl-in-days": "Messages TTL in days", + "messages-ttl-in-days-required": "Messages TTL in days is required.", + "messages-ttl-in-days-min": "Min number is 1.", + "messages-ttl-in-days-pattern": "Number is not valid.", + "mqtt-qos": "QoS", + "mqtt-qos-required": "QoS is required", + "mqtt-qos-range": "QoS values range is from 0 to 1", "tls-path-private-key": "Path to private key on gateway", "toggle-fullscreen": "Toggle fullscreen", "transformer-json-config": "Configuration JSON*", - "update-config": "Add/update configuration JSON" + "update-config": "Add/update configuration JSON", + "hints": { + "remote-configuration": "Enables remote configuration and management of the gateway", + "remote-shell": "Enables remote control of the operating system with the gateway from the Remote Shell widget", + "host": "Hostname or IP address of ThingsBoard server", + "port": "Port of MQTT service on ThingsBoard server", + "token": "Access token for the gateway from ThingsBoard server", + "client-id": "MQTT client id for the gateway form ThingsBoard server", + "username": "MQTT username for the gateway form ThingsBoard server", + "password": "MQTT password for the gateway form ThingsBoard server", + "ca-cert": "Path to CA certificate file", + "date-form": "Date format in log message", + "data-folder": "Path to folder, that will contains data (Relative or Absolute)", + "log-format": "Log message format", + "remote-log": "Enables remote logging and logs reading from the gateway", + "backup-count": "If backup count is > 0, when a rollover is done, no more than backup count files are kept - the oldest ones are deleted", + "storage": "Provides configuration for saving incoming data before it is sent to the platform", + "max-file-count": "Maximum count of file that will be created", + "max-read-count": "Count of messages to get from storage and send to ThingsBoard", + "max-records": "Maximum count of records that will be stored in one file", + "read-record-count": "Count of messages to get from storage and send to ThingsBoard", + "max-records-count": "Maximum count of data in storage before send to ThingsBoard", + "ttl-check-hour": "How often will Gateway check data for obsolescence", + "ttl-messages-day": "Maximum days that storage will save data", + "commands": "Commands for collecting additional statistic", + "attribute": "Statistic telemetry key", + "timeout": "Timeout for command executing", + "command": "The result of the command execution, will be used as the value for telemetry", + "check-device-activity": "Enables monitor the activity of each connected device", + "inactivity-timeout": "Time after whose the gateway will disconnect device", + "inactivity-period": "Periodicity of device activity check", + "minimal-pack-delay": "Delay between sending packs of messages (Decreasing this setting results in increased CPU usage)", + "qos": "Quality of Service in MQTT messaging (0 - at most once, 1 - at least once)", + "server-port": "Network port on which GRPC server will listen for incoming connections.", + "grpc-keep-alive-timeout": "Maximum time the server should wait for a keepalive ping response before considering the connection dead.", + "grpc-keep-alive": "Duration between two successive keepalive ping messages when there is no active RPC call.", + "grpc-min-time-between-pings": "Minimum amount of time the server should wait between sending keepalive ping messages", + "grpc-max-pings-without-data": "Maximum number of keepalive ping messages that the server can send without receiving any data before it considers the connection dead.", + "grpc-min-ping-interval-without-data": "Minimum amount of time the server should wait between sending keepalive ping messages when there is no data being sent or received.", + "permit-without-calls": "Allow server to keep the GRPC connection alive even when there are no active RPC calls." + } }, "grid": { "delete-item-title": "Are you sure you want to delete this item?", @@ -2655,9 +2961,64 @@ "browse-file": "Browse file", "browse-files": "Browse files" }, + "image": { + "gallery": "Image gallery", + "search": "Search image", + "selected-images": "{ count, plural, =1 {1 image} other {# images} } selected", + "created-time": "Created time", + "name": "Name", + "name-required": "Name is required.", + "resolution": "Resolution", + "size": "Size", + "system": "System", + "download-image": "Download image", + "export-image": "Export image to JSON", + "import-image": "Import image from JSON", + "upload-image": "Upload image", + "edit-image": "Edit image", + "image-details": "Image details", + "no-images": "No images found", + "delete-image": "Delete image", + "delete-image-title": "Are you sure you want to delete image '{{imageTitle}}'?", + "delete-image-text": "Be careful, after the confirmation image will become unrecoverable.", + "delete-images-title": "Are you sure you want to delete { count, plural, =1 {1 image} other {# images} }?", + "delete-images-text": "Be careful, after the confirmation all selected images will be removed and all related data will become unrecoverable.", + "list-mode": "List view", + "grid-mode": "Grid view", + "image-preview": "Image preview", + "update-image": "Update image", + "export-failed-error": "Unable to export image: {{error}}", + "image-json-file": "Image JSON file", + "invalid-image-json-file-error": "Unable to import image from JSON: Invalid image JSON data structure.", + "image-is-in-use": "Image is used by other entities", + "images-are-in-use": "Images are used by other entities", + "image-is-in-use-text": "The image '{{title}}' was not deleted because it is used by the following entities:", + "images-are-in-use-text": "Not all images have been deleted because they are used by other entities.
You can view referenced entities by clicking the References button in the corresponding image row.
If you still want to delete these images, select them in the table below and click the Delete selected button.", + "delete-image-in-use-text": "If you still want to delete the image, click the Delete anyway button.", + "system-entities": "System entities:", + "entities": "entities:", + "references": "References", + "include-system-images": "Include system images", + "clear-image": "Clear image", + "no-image": "No image", + "no-image-selected": "No image selected", + "browse-from-gallery": "Browse from gallery", + "set-link": "Set link", + "image-link": "Image link", + "link": "Link", + "copy-image-link": "Copy image link", + "embed-image": "Embed image", + "embed-to-html": "Embed to HTML", + "embed-to-html-hint": "This feature will make link available to any unauthorized user.", + "embed-to-html-text": "Using the following code snippet, you may embed an image into the components based on the plain HTML.
Such components include HTML card widgets, cell content functions, etc.", + "embed-to-angular-template": "Embed to Angular HTML template", + "embed-to-angular-template-text": "Using the following code snippet, you may embed an image into the Angular HTML template.
Such components include the Markdown widget, HTML section in the widget editor, custom actions, etc." + }, "image-input": { - "drop-image-or": "Drag and drop an image or", "drop-images-or": "Drag and drop an images or", + "drag-and-drop": "Drag & Drop", + "or": "or", + "browse": "Browse", "no-images": "No images selected", "images": "images" }, @@ -2708,6 +3069,12 @@ "lwm2m-server-secret-key": "LwM2M server secret key", "lwm2m-server-public-key-id": "LwM2M server public key or id" }, + "snmp": { + "host": "SNMP host", + "port": "SNMP port", + "version": "SNMP version (v1, v2c or v3)", + "community-string": "SNMP community string" + }, "isgateway": "Is Gateway", "activity-time-from-gateway-device": "Activity time from gateway device", "description": "Description", @@ -2768,8 +3135,14 @@ "left-side": "Left side layout" }, "legend": { - "direction": "Legend direction", - "position": "Legend position", + "direction": "Direction", + "position": "Position", + "show-values": "Show values", + "min-option": "Min", + "max-option": "Max", + "average-option": "Average", + "total-option": "Total", + "latest-option": "Latest", "sort-legend": "Sort datakeys in legend", "show-max": "Show max value", "show-min": "Show min value", @@ -2851,6 +3224,7 @@ "api-feature-hint": "If the field is empty, the trigger will be applied to all api features", "api-usage-trigger-settings": "API usage trigger settings", "new-platform-version-trigger-settings": "New platform version trigger settings", + "rate-limits-trigger-settings": "Exceeded rate limits trigger settings", "at-least-one-should-be-selected": "At least one should be selected", "basic-settings": "Basic settings", "button-text": "Button text", @@ -2891,6 +3265,8 @@ "email-preview": "Email notification preview", "slack": "Slack", "slack-preview": "Slack notification preview", + "microsoft-teams": "Microsoft Teams", + "microsoft-teams-preview": "Microsoft Teams notification preview", "sms": "SMS", "sms-preview": "SMS notification preview", "web": "Web", @@ -2930,6 +3306,7 @@ "dashboard": "Open dashboard", "link": "Open URL link" }, + "loading-notifications": "Loading notifications...", "management": "Notification management", "mark-all-as-read": "Mark all as read", "mark-as-read": "Mark as read", @@ -2970,20 +3347,20 @@ "only-rule-chain-lifecycle-failures": "Only rule chain lifecycle failures", "only-rule-node-lifecycle-failures": "Only rule node lifecycle failures", "platform-users": "Platform users", + "rate-limits": "Rate limits", + "rate-limits-hint": "If the field is empty, the trigger will be applied to all rate limits", "recipient": "Recipient", "recipient-group": "Recipient group", "recipient-type": { "affected-tenant-administrators": "Affected tenant administrators", "affected-user": "Affected user", - "affected-user-hint": "Affected user hint", "all-users": "All users", "customer-users": "Customer users", "system-administrators": "System administrators", "tenant-administrators": "Tenant administrators", "user-filters": "User filter", "user-list": "User list", - "users-entity-owner": "Users of the entity owner", - "users-entity-owner-hint": "Users of the entity owner hint" + "users-entity-owner": "Users of the entity owner" }, "recipients": "Recipients", "notification-recipients": "Notifications / Recipients", @@ -3048,13 +3425,15 @@ "general": "General", "rule-engine-lifecycle-event": "Rule engine lifecycle event", "rule-node": "Rule node", - "new-platform-version": "New platform version" + "new-platform-version": "New platform version", + "rate-limits": "Exceeded rate limits" }, "templates": "Templates", "notification-templates": "Notifications / Templates", "tenant-profiles-list-rule-hint": "If the field is empty, the trigger will be applied to all tenant profiles", "tenants-list-rule-hint": "If the field is empty, the trigger will be applied to all tenants", "threshold": "Threshold", + "theme-color": "Theme color", "time": "Time", "track-rule-node-events": "Track rule node events", "trigger": { @@ -3067,6 +3446,7 @@ "entity-action": "Entity action", "rule-engine-lifecycle-event": "Rule engine lifecycle event", "new-platform-version": "New platform version", + "rate-limits": "Exceeded rate limits", "trigger": "Trigger", "trigger-required": "Trigger is required" }, @@ -3075,7 +3455,21 @@ "updated": "Updated", "use-template": "Use template", "view-all": "View all", - "warning": "Warning" + "warning": "Warning", + "webhook-url": "Webhook URL", + "webhook-url-required": "Webhook URL is required", + "channel-name": "Channel name", + "channel-name-required": "Channel name is required", + "settings": { + "notification-settings": "Notification settings", + "reset-all": "Reset all settings", + "reset-all-title": "Are you sure you want to reset form?", + "reset-all-text": "After the confirmation, the settings form will reset to the default value and save.", + "type": "Type", + "enable-all": "Enable all", + "disable-all": "Disable all", + "delivery-not-configured": "Delivery method is not configured" + } }, "ota-update": { "add": "Add package", @@ -3122,6 +3516,7 @@ "ota-update": "OTA update", "ota-update-details": "OTA update details", "ota-updates": "OTA updates", + "package-file": "Package file", "package-type": "Package type", "packages-repository": "Packages repository", "search": "Search packages", @@ -3137,7 +3532,7 @@ "use-external-url": "Use external URL", "version": "Version", "version-required": "Version is required.", - "version-tag": "Version Tag", + "version-tag": "Version tag", "version-tag-hint": "Custom tag should match the package version reported by your device.", "version-max-length": "Version should be less than 256", "warning-after-save-no-edit": "Once the package is uploaded, you will not be able to modify title, version, device profile and package type." @@ -3165,6 +3560,13 @@ "security": { "security": "Security", "general-settings": "General security settings", + "access-token": "Access token", + "access-token-required": "Access token is required", + "clientId": "Client ID", + "clientId-required": "Client ID is required", + "username": "Username", + "username-required": "Username is required", + "ca-cert": "CA certificate", "2fa": { "2fa": "Two-factor authentication", "2fa-description": "Two-factor authentication protects your account from unauthorized access. All you have to do is enter a security code when you log in.", @@ -3231,12 +3633,14 @@ "password-requirements": "Password requirements", "password-should-difference": "New password should be different from current", "special-character": "{ count, plural, =1 {1 special character} other {# special characters} }", - "uppercase-letter": "{ count, plural, =1 {1 uppercase letter} other {# uppercase letters} }" + "uppercase-letter": "{ count, plural, =1 {1 uppercase letter} other {# uppercase letters} }", + "at-most": "At most:" } }, "relation": { "relations": "Relations", "direction": "Direction", + "clear-relation-type": "Clear relation type", "search-direction": { "FROM": "From", "TO": "To" @@ -3271,6 +3675,7 @@ "delete-from-relations-title": "Are you sure you want to delete { count, plural, =1 {1 relation} other {# relations} }?", "delete-from-relations-text": "Be careful, after the confirmation all selected relations will be removed and current entity will be unrelated from the corresponding entities.", "remove-relation-filter": "Remove relation filter", + "remove-filter": "Remove filter", "add-relation-filter": "Add relation filter", "any-relation": "Any relation", "relation-filters": "Relation filters", @@ -3279,7 +3684,7 @@ "no-relations-text": "No relations found" }, "resource": { - "add": "Add Resource", + "add": "Add resource", "all-types": "All", "copyId": "Copy resource Id", "delete": "Delete resource", @@ -3298,6 +3703,8 @@ "no-resource-text": "No resources found", "open-widgets-bundle": "Open widgets bundle", "resource": "Resource", + "resource-file": "Resource file", + "resource-files": "Resource files", "resource-library-details": "Resource details", "resource-type": "Resource type", "resources-library": "Resources library", @@ -3324,7 +3731,7 @@ "name-required": "Name is required.", "name-max-length": "Name should be less than 256", "description": "Description", - "add": "Add Rule Chain", + "add": "Add rule chain", "set-root": "Make rule chain root", "set-root-rulechain-title": "Are you sure you want to make the rule chain '{{ruleChainName}}' root?", "set-root-rulechain-text": "After the confirmation the rule chain will become root and will handle all incoming transport messages.", @@ -3355,9 +3762,8 @@ "search": "Search rule chains", "selected-rulechains": "{ count, plural, =1 {1 rule chain} other {# rule chains} } selected", "open-rulechain": "Open rule chain", - "assign-new-rulechain": "Assign new rulechain", - "edge-template-root": "Template Root", - "assign-to-edge": "Assign to Edge", + "edge-template-root": "Template root", + "assign-to-edge": "Assign to edge", "edge-rulechain": "Edge rule chain", "unassign-rulechain-from-edge-text": "After the confirmation the rulechain will be unassigned and won't be accessible by the edge.", "unassign-rulechains-from-edge-title": "Are you sure you want to unassign { count, plural, =1 {1 rulechain} other {# rulechains} }?", @@ -3389,7 +3795,7 @@ "name-required": "Name is required.", "name-max-length": "Name should be less than 256", "type": "Type", - "description": "Description", + "rule-node-description": "Rule node description", "delete": "Delete rule node", "select-all-objects": "Select all nodes and connections", "deselect-all-objects": "Deselect all nodes and connections", @@ -3437,7 +3843,7 @@ "ui-resources-load-error": "Failed to load configuration ui resources.", "invalid-target-rulechain": "Unable to resolve target rule chain!", "test-script-function": "Test script function", - "script-lang-java-script": "Java Script", + "script-lang-java-script": "JavaScript", "script-lang-tbel": "TBEL", "message": "Message", "message-type": "Message type", @@ -3448,7 +3854,8 @@ "output": "Output", "test": "Test", "help": "Help", - "reset-debug-mode": "Reset debug mode in all nodes" + "reset-debug-mode": "Reset debug mode in all nodes", + "test-with-this-message": "{{test}} with this message" }, "timezone": { "timezone": "Timezone", @@ -3522,6 +3929,8 @@ "description": "Description", "description-hint": "This text will be displayed in the Queue description instead of the selected strategy", "alt-description": "Submit Strategy: {{submitStrategy}}, Processing Strategy: {{processingStrategy}}", + "custom-properties": "Custom properties", + "custom-properties-hint": "Custom queue (topic) creation properties, e.g. 'retention.ms:604800000;retention.bytes:1048576000'", "strategies": { "sequential-by-originator-label": "Sequential by originator", "sequential-by-originator-hint": "New message for e.g. device A is not submitted until previous message for device A is acknowledged", @@ -3564,7 +3973,7 @@ "tenant": "Tenant", "tenants": "Tenants", "management": "Tenant management", - "add": "Add Tenant", + "add": "Add tenant", "admins": "Admins", "manage-tenant-admins": "Manage tenant admins", "delete": "Delete tenant", @@ -3589,8 +3998,8 @@ "tenant-required": "Tenant is required", "search": "Search tenants", "selected-tenants": "{ count, plural, =1 {1 tenant} other {# tenants} } selected", - "isolated-tb-rule-engine": "Processing in isolated ThingsBoard Rule Engine container", - "isolated-tb-rule-engine-details": "Requires separate microservice(s) per isolated Tenant" + "isolated-tb-rule-engine": "Use isolated ThingsBoard Rule Engine queues", + "isolated-tb-rule-engine-details": "Each tenant will have dedicated Rule Engine queues" }, "tenant-profile": { "tenant-profile": "Tenant profile", @@ -3634,7 +4043,7 @@ "rule-engine": "Rule Engine", "time-to-live": "Time-to-live", "alarms-and-notifications": "Alarms and notifications", - "ota-files-in-bytes": "OTA files in bytes", + "ota-files-in-bytes": "Files", "ws-title": "WS", "unlimited": "(0 - unlimited)", "maximum-devices": "Devices maximum number", @@ -3655,12 +4064,15 @@ "maximum-rule-chains": "Rule chains maximum number", "maximum-rule-chains-required": "Rule chains maximum number is required.", "maximum-rule-chains-range": "Rule chains maximum number can't be negative", - "maximum-resources-sum-data-size": "Resource files sum size", - "maximum-resources-sum-data-size-required": "Resource files sum size is required.", - "maximum-resources-sum-data-size-range": "Resource files sum size can`t be negative", - "maximum-ota-packages-sum-data-size": "OTA package files sum size", - "maximum-ota-package-sum-data-size-required": "OTA package files sum size is required.", - "maximum-ota-package-sum-data-size-range": "OTA package files sum size can`t be negative", + "maximum-resources-sum-data-size": "Maximum total size of resources files (bytes)", + "maximum-resources-sum-data-size-required": "Maximum total size of resources files is required.", + "maximum-resources-sum-data-size-range": "Maximum total size of resources files can't be negative", + "maximum-resource-size": "Maximum resource file size (bytes)", + "maximum-resource-size-required": "Maximum resource file size is required", + "maximum-resource-size-range": "Maximum resource file size can't be negative", + "maximum-ota-packages-sum-data-size": "Maximum total size of OTA package files (bytes)", + "maximum-ota-package-sum-data-size-required": "Maximum total size of OTA package files is required.", + "maximum-ota-package-sum-data-size-range": "Maximum total size of OTA package files can't be negative", "rest-requests-for-tenant": "REST requests for tenant", "transport-tenant-telemetry-msg-rate-limit": "Transport tenant telemetry messages", "transport-tenant-telemetry-data-points-rate-limit": "Transport tenant telemetry data points", @@ -3683,6 +4095,9 @@ "max-j-s-executions": "JavaScript executions maximum number ", "max-j-s-executions-required": "JavaScript executions maximum number is required.", "max-j-s-executions-range": "JavaScript executions maximum number can't be negative", + "max-tbel-executions": "TBEL executions maximum number ", + "max-tbel-executions-required": "TBEL executions maximum number is required.", + "max-tbel-executions-range": "TBEL executions maximum number can't be negative", "max-d-p-storage-days": "Data points storage days maximum number", "max-d-p-storage-days-required": "Data points storage days maximum number is required.", "max-d-p-storage-days-range": "Data points storage days maximum number can't be negative", @@ -3695,12 +4110,19 @@ "rpc-ttl-days": "RPC TTL days", "rpc-ttl-days-required": "RPC TTL days required", "rpc-ttl-days-days-range": "RPC TTL days can't be negative", + "queue-stats-ttl-days": "Queue stats TTL days", + "queue-stats-ttl-days-required": "Queue stats TTL days required", + "queue-stats-ttl-days-range": "Queue stats TTL days can't be negative", + "rule-engine-exceptions-ttl-days": "Rule Engine exceptions TTL days", + "rule-engine-exceptions-ttl-days-required": "Rule Engine exceptions TTL days required", + "rule-engine-exceptions-ttl-days-range": "Rule Engine exceptions TTL days can't be negative", "max-rule-node-executions-per-message": "Rule node per message executions maximum number", "max-rule-node-executions-per-message-required": "MRule node per message executions maximum number is required.", "max-rule-node-executions-per-message-range": "Rule node per message executions maximum number can't be negative", "max-emails": "Emails sent maximum number", "max-emails-required": "Emails sent maximum number is required.", "max-emails-range": "Emails sent maximum number can't be negative", + "sms-enabled": "SMS enabled", "max-sms": "SMS sent maximum number", "max-sms-required": "SMS sent maximum number is required.", "max-sms-range": "SMS sent maximum number can't be negative", @@ -3813,15 +4235,22 @@ "timewindow": { "timewindow": "Timewindow", "years": "{ years, plural, =1 { year } other {# years } }", + "years-short": "{{ years }}y", "months": "{ months, plural, =1 { month } other {# months } }", + "months-short": "{{ months }}M", "weeks": "{ weeks, plural, =1 { week } other {# weeks } }", + "weeks-short": "{{ weeks }}w", "days": "{ days, plural, =1 { day } other {# days } }", + "days-short": "{{ days }}d", "hours": "{ hours, plural, =0 { hour } =1 {1 hour } other {# hours } }", "hr": "{{ hr }} hr", + "hr-short": "{{ hr }}h", "minutes": "{ minutes, plural, =0 { minute } =1 {1 minute } other {# minutes } }", "min": "{{ min }} min", + "min-short": "{{ min }}m", "seconds": "{ seconds, plural, =0 { second } =1 {1 second } other {# seconds } }", "sec": "{{ sec }} sec", + "sec-short": "{{ sec }}s", "short": { "days": "{ days, plural, =1 {1 day } other {# days } }", "hours": "{ hours, plural, =1 {1 hour } other {# hours } }", @@ -3840,18 +4269,448 @@ "hide": "Hide", "interval": "Interval", "just-now": "Just now", - "ago": "ago" + "just-now-lower": "just now", + "ago": "ago", + "style": "Timewindow style", + "icon": "Icon", + "icon-position": "Icon position", + "icon-position-left": "Left", + "icon-position-right": "Right", + "font": "Font", + "color": "Color", + "displayTypePrefix": "Display Realtime/History prefix", + "preview": "Preview" + }, + "unit": { + "millimeter": "Millimeter", + "centimeter": "Centimeter", + "angstrom": "Angstrom", + "nanometer": "Nanometer", + "micrometer": "Micrometer", + "meter": "Meter", + "kilometer": "Kilometer", + "inch": "Inch", + "foot": "Foot", + "yard": "Yard", + "mile": "Mile", + "nautical-mile": "Nautical Mile", + "astronomical-unit": "Astronomical Unit", + "reciprocal-metre": "Reciprocal Metre", + "meter-per-meter": "Meter per meter", + "steradian": "Steradian", + "thou": "Thou", + "barleycorn": "Barleycorn", + "hand": "Hand", + "chain": "Chain", + "furlong": "Furlong", + "league": "League", + "fathom": "Fathom", + "cable": "Cable", + "link": "Link", + "rod": "Rod", + "nanogram": "Nanogram", + "microgram": "Microgram", + "milligram": "Milligram", + "gram": "Gram", + "kilogram": "Kilogram", + "tonne": "Tonne", + "ounce": "Ounce", + "pound": "Pound", + "stone": "Stone", + "hundredweight-count": "Hundredweight count", + "short-tons": "Short tons", + "dalton": "Dalton", + "grain": "Grain", + "drachm": "Drachm", + "quarter": "Quarter", + "slug": "Slug", + "carat": "Carat", + "cubic-millimeter": "Cubic Millimeter", + "cubic-centimeter": "Cubic Centimeter", + "cubic-meter": "Cubic Meter", + "cubic-kilometer": "Cubic Kilometer", + "microliter": "Microliter", + "milliliter": "Milliliter", + "liter": "Liter", + "hectoliter": "Hectolitre", + "cubic-inch": "Cubic Inch", + "cubic-foot": "Cubic Foot", + "cubic-yard": "Cubic Yard", + "fluid-ounce": "Fluid Ounce", + "pint": "Pint", + "quart": "Quart", + "gallon": "Gallon", + "oil-barrels": "Oil Barrel", + "cubic-meter-per-kilogram": "Cubic Meter per Kilogram", + "gill": "Gill", + "hogshead": "Hogshead", + "teaspoon": "Teaspoon", + "tablespoon": "Tablespoon", + "cup": "Cup", + "celsius": "Celsius", + "kelvin": "Kelvin", + "rankine": "Rankine", + "fahrenheit": "Fahrenheit", + "percent": "Percent", + "meter-per-second": "Meter per Second", + "kilometer-per-hour": "Kilometer per Hour", + "foot-per-second": "Foot per Second", + "mile-per-hour": "Mile per Hour", + "knot": "Knot", + "millimeters-per-minute": "Millimeters per minute", + "kilometer-per-hour-squared": "Kilometer per hour squared", + "foot-per-second-squared": "Foot per second squared", + "pascal": "Pascal", + "kilopascal": "Kilopascal", + "megapascal": "Megapascal", + "gigapascal": "Gigapascal", + "millibar": "Millibar", + "bar": "Bar", + "kilobar": "Kilobar", + "newton": "Newton", + "newton-meter": "Newton meter", + "foot-pounds": "Foot-pounds", + "inch-pounds": "Inch-pounds", + "newton-per-meter": "Newton per meter", + "atmospheres": "Atmospheres", + "pounds-per-square-inch": "Pounds per Square Inch", + "torr": "Torr", + "inches-of-mercury": "Inches of Mercury", + "pascal-per-square-meter": "Pascal per Square Meter", + "pound-per-square-inch": "Pound per Square Inch", + "newton-per-square-meter": "Newton per Square Meter", + "kilogram-force-per-square-meter": "Kilogram-force per Square Meter", + "pascal-per-square-centimeter": "Pascal per Square Centimeter", + "ton-force-per-square-inch": "Ton-force per Square Inch", + "kilonewton-per-square-meter": "Kilonewton per Square Meter", + "newton-per-square-millimeter": "Newton per Square Millimeter", + "microjoule": "Microjoule", + "millijoule": "Millijoule", + "joule": "Joule", + "kilojoule": "Kilojoule", + "megajoule": "Megajoule", + "gigajoule": "Gigajoule", + "watt-hour": "Watt-hour", + "kilowatt-hour": "Kilowatt-hour", + "electron-volts": "Electron volts", + "joules-per-coulomb": "Joules per Coulomb", + "british-thermal-unit": "British Thermal Units", + "foot-pound": "Foot-pound", + "calorie": "Calorie", + "small-calorie": "Small Calorie", + "kilocalorie": "Kilocalorie", + "joule-per-kelvin": "Joule per Kelvin", + "joule-per-kilogram-kelvin": "Joule per Kilogram-Kelvin", + "joule-per-kilogram": "Joule per Kilogram", + "watt-per-meter-kelvin": "Watt per Meter-Kelvin", + "joule-per-cubic-meter": "Joule per Cubic Meter", + "therm": "Therm", + "electric-dipole-moment": "Electric Dipole Moment", + "magnetic-dipole-moment": "Magnetic Dipole Moment", + "debye": "Debye", + "coulomb-per-square-meter-per-volt": "Coulomb per Square Meter per Volt", + "milliwatt": "Milliwatt", + "microwatt": "Microwatt", + "watt": "Watt", + "kilowatt": "Kilowatt", + "megawatt": "Megawatt", + "gigawatt": "Gigawatt", + "metric-horsepower": "Metric Horsepower", + "milliwatt-per-square-centimeter": "Milliwatts per square centimeter", + "watt-per-square-centimeter": "Watts per square centimeter", + "kilowatt-per-square-centimeter": "Kilowatts per square centimeter", + "milliwatt-per-square-meter": "Milliwatts per square meter", + "watt-per-square-meter": "Watts per square meter", + "kilowatt-per-square-meter": "Kilowatts per square meter", + "watt-per-square-inch": "Watts per square inch", + "kilowatt-per-square-inch": "Kilowatts per square inch", + "horsepower": "Horsepower", + "btu-per-hour": "British thermal units/hour", + "coulomb": "Coulomb", + "millicoulomb": "Millicoulombs", + "microcoulomb": "Microcoulomb", + "picocoulomb": "Picocoulomb", + "coulomb-per-meter": "Coulomb per meter", + "coulomb-per-cubic-meter": "Coulomb per Cubic Meter", + "coulomb-per-square-meter": "Coulomb per Square Meter", + "square-millimeter": "Square Millimeter", + "square-centimeter": "Square Centimeter", + "square-meter": "Square Meter", + "hectare": "Hectare", + "square-kilometer": "Square Kilometer", + "square-inch": "Square Inch", + "square-foot": "Square Foot", + "square-yard": "Square Yard", + "acre": "Acre", + "square-mile": "Square Mile", + "are": "Are", + "barn": "Barn", + "circular-inch": "Circular Inch", + "milliampere-hour": "Milliampere-hour", + "milliampere-hour-tags": "electric current, current flow, electric charge, current capacity, flow of electricity, electrical flow, milliampere-hour, milliampere-hours, mAh", + "ampere-hours": "Ampere-hours", + "ampere-hours-tags": "electric current, current flow, electric charge, current capacity, flow of electricity, electrical flow, ampere, ampere-hours, Ah", + "kiloampere-hours": "Kiloampere-hours", + "kiloampere-hours-tags": "electric current, current flow, electric charge, current capacity, flow of electricity, electrical flow, kiloampere-hours, kiloampere-hour, kAh", + "nanoampere": "Nanoampere", + "nanoampere-tags": "current, amperes, nanoampere, nA", + "picoampere": "Picoampere", + "picoampere-tags": "current, amperes, picoampere, pA", + "microampere": "Microampere", + "microampere-tags": "electric current, microampere, microamperes, μA", + "milliampere": "Milliampere", + "milliampere-tags": "electric current, milliampere, milliamperes, mA", + "ampere": "Ampere", + "ampere-tags": "electric current, current flow, flow of electricity, electrical flow, ampere, amperes, amperage, A", + "kiloamperes": "Kiloamperes", + "kiloamperes-tags": "electric current, current flow, kiloamperes, kA", + "microampere-per-square-centimeter": "Microampere per square centimeter", + "microampere-per-square-centimeter-tags": "Current density, microampere per square centimeter, µA/cm²", + "ampere-per-square-meter": "Ampere per Square Meter", + "ampere-per-square-meter-tags": "current density, current per unit area, ampere per square meter, A/m²", + "ampere-per-meter": "Ampere per Meter", + "ampere-per-meter-tags": "magnetic field strength, magnetic field intensity, ampere per meter, A/m", + "oersted": "Oersted", + "oersted-tags": "magnetic field, oersted, Oe", + "bohr-magneton": "Bohr Magneton", + "bohr-magneton-tags": "atomic physics, magnetic moment, bohr magneton, μB", + "ampere-meter-squared": "Ampere-Meter Squared", + "ampere-meter-squared-tags": "magnetic moment, dipole moment, ampere-meter squared, A·m²", + "ampere-meter": "Ampere-Meter", + "ampere-meter-tags": "magnetic field, current loop, ampere-meter, A·m", + "nanovolt": "Nanovolt", + "picovolt": "Picovolt", + "millivolts": "Millivolts", + "microvolts": "Microvolts", + "volt": "Volt", + "kilovolts": "Kilovolts", + "dbmV": "dBmV", + "dbm": "dBm", + "volt-meter": "Volt-Meter", + "kilovolt-meter": "Kilovolt-Meter", + "megavolt-meter": "Megavolt-Meter", + "microvolt-meter": "Microvolt-Meter", + "millivolt-meter": "Millivolt-Meter", + "nanovolt-meter": "Nanovolt-Meter", + "ohm": "Ohm", + "microohm": "Microohm", + "milliohm": "Milliohm", + "kilohm": "Kilohm", + "megohm": "Megohm", + "gigohm": "Gigohm", + "hertz": "Hertz", + "kilohertz": "Kilohertz", + "megahertz": "Megahertz", + "gigahertz": "Gigahertz", + "rpm": "Revolutions Per Minute", + "candela-per-square-meter": "Candela per square meter", + "candela": "Candela", + "lumen": "Lumen", + "lux": "Lux", + "foot-candle": "Foot-candle", + "lumen-per-square-meter": "Lumen per square meter", + "lux-second": "Lux second", + "lumen-second": "Lumen second", + "lumens-per-watt": "Lumens per watt", + "absorbance": "Absorbance", + "mole": "Mole", + "nanomole": "Nanomole", + "micromole": "MicroMole", + "millimole": "Millimole", + "kilomole": "Kilomole", + "mole-per-cubic-meter": "Mole per Cubic Meter", + "rssi": "RSSI", + "ppm": "Parts Per Million", + "ppb": "Parts Per Billion", + "micrograms-per-cubic-meter": "Micrograms per Cubic Meter", + "aqi": "AQI", + "gram-per-cubic-meter": "Gram per cubic meter", + "gram-per-kilogram": "Specific Humidity", + "millimeters-per-second": "Millimeters per second", + "neper": "Neper", + "bel": "Bel", + "decibel": "Decibel", + "meters-per-second-squared": "Meters per second squared", + "becquerel": "Becquerel", + "curie": "Curie", + "gray": "Gray", + "sievert": "Sievert", + "roentgen": "Roentgen", + "cps": "Counts per Second", + "rad": "Rad", + "rem": "Rem", + "dps": "Disintegrations per second", + "rutherford": "Rutherford", + "coulombs-per-kilogram": "Coulombs per kilogram", + "becquerels-per-cubic-meter": "Becquerels per cubic meter", + "curies-per-liter": "Curies per liter", + "becquerels-per-second": "Becquerels per second", + "curies-per-second": "Curies per second", + "gy-per-second": "Gray per Second", + "watt-per-steradian": "Watt per Steradian", + "watt-per-square-metre-steradian": "Watt per Square Metre-Steradian", + "ph-level": "pH Level", + "turbidity": "Turbidity", + "mg-per-liter": "Milligrams per liter", + "microsiemens-per-centimeter": "Microsiemens per centimeter", + "millisiemens-per-meter": "Millisiemens per meter", + "siemens-per-meter": "Siemens per meter", + "kilogram-per-cubic-meter": "Kilogram per cubic meter", + "gram-per-cubic-centimeter": "Gram per cubic centimeter", + "kilogram-per-square-meter": "Kilogram per square metre", + "milligram-per-milliliter": "Milligram per milliliter", + "milligram-per-cubic-meter": "Milligram per cubic meter", + "pound-per-cubic-foot": "Pound per cubic foot", + "ounces-per-cubic-inch": "Ounces per cubic inch", + "tons-per-cubic-yard": "Tons per cubic yard", + "particle-density": "Particle density", + "kilometers-per-liter": "Kilometers per liter", + "miles-per-gallon": "Miles per gallon", + "liters-per-100-km": "Liters per 100 km", + "gallons-per-mile": "Gallons per mile", + "liters-per-hour": "Liters per hour", + "gallons-per-hour": "Gallons per hour", + "beats-per-minute": "Beats per minute", + "millimeters-of-mercury": "Millimeters of mercury", + "milligrams-per-deciliter": "Milligrams per deciliter", + "g-force": "G-force", + "kilonewton": "Kilonewton", + "kilogram-force": "Kilogram-Force", + "pound-force": "Pound-Force", + "kilopound-force": "Kilopound-Force", + "dyne": "Dyne", + "poundal": "Poundal", + "kip": "Kip", + "gal": "Gal", + "gravity": "Gravity", + "hectopascal": "Hectopascal", + "atmosphere": "Atmosphere", + "millibars": "Millibars", + "inch-of-mercury": "One inch of mercury", + "richter-scale": "Richter Scale", + "second": "Second", + "minute": "Minute", + "hour": "Hour", + "day": "Day", + "week": "Week", + "month": "Month", + "year": "Year", + "cubic-foot-per-minute": "Cubic Foot Per Minute", + "cubic-meters-per-hour": "Cubic Meters Per Hour", + "cubic-meters-per-second": "Cubic Meters Per Second", + "liter-per-second": "Liter Per Second", + "liter-per-minute": "Liter Per Minute", + "gallons-per-minute": "Gallons Per Minute", + "cubic-foot-per-second": "Cubic foot per second", + "milliliters-per-minute": "Milliliters per minute", + "bit": "Bit", + "byte": "Byte", + "kilobyte": "Kilobyte", + "megabyte": "Megabyte", + "gigabyte": "Gigabyte", + "terabyte": "Terabyte", + "petabyte": "Petabyte", + "exabyte": "Exabyte", + "zettabyte": "Zettabyte", + "yottabyte": "Yottabyte", + "bit-per-second": "Bit per second", + "kilobit-per-second": "Kilobit per second", + "megabit-per-second": "Megabit per second", + "gigabit-per-second": "Gigabit per second", + "terabit-per-second": "Terabit per second", + "byte-per-second": "Byte per second", + "kilobyte-per-second": "Kilobyte per second", + "megabyte-per-second": "Megabyte per second", + "gigabyte-per-second": "Gigabyte per second", + "degree": "Degree", + "radian": "Radian", + "gradian": "Gradian", + "mil": "Mil", + "revolution": "Revolution", + "siemens": "Siemens", + "millisiemens": "Millisiemens", + "microsiemens": "Microsiemens", + "kilosiemens": "Kilosiemens", + "megasiemens": "Megasiemens", + "gigasiemens": "Gigasiemens", + "farad": "Farad", + "millifarad": "Millifarad", + "microfarad": "Microfarad", + "nanofarad": "Nanofarad", + "picofarad": "Picofarad", + "kilofarad": "Kilofarad", + "megafarad": "Megafarad", + "gigafarad": "Gigafarad", + "terfarad": "Terfarad", + "farad-per-meter": "Farad per Meter", + "tesla": "Tesla", + "gauss": "Gauss", + "kilogauss": "Kilogauss", + "millitesla": "Millitesla", + "microtesla": "Microtesla", + "nanotesla": "Nanotesla", + "kilotesla": "Kilotesla", + "megatesla": "Megatesla", + "millitesla-square-meters": "millitesla square meters", + "gamma": "Gamma", + "lambda": "Lambda", + "square-meter-per-second": "Square meter per second", + "square-centimeter-per-second": "Square centimeter per second", + "stoke": "Stoke", + "centistokes": "Centistokes", + "square-foot-per-second": "Square foot per second", + "square-inch-per-second": "Square inch per second", + "pascal-second": "Pascal-second", + "centipoise": "Centipoise", + "poise": "Poise", + "reynolds": "Reynolds", + "pound-per-foot-hour": "Pound per foot-hour", + "newton-second-per-square-meter": "Newton second per square meter", + "dyne-second-per-square-centimeter": "Dyne second per square centimeter", + "kilogram-per-meter-second": "Kilogram per meter-second", + "tesla-square-meters": "Tesla square meters", + "maxwell": "Maxwell", + "tesla-per-meter": "Tesla per Meter", + "gauss-per-centimeter": "Gauss per Centimeter", + "weber": "Weber", + "microweber": "Microweber", + "milliweber": "Milliweber", + "gauss-square-centimeter": "Gauss-Square Centimeter", + "kilogauss-square-centimeter": "Kilogauss-Square Centimeter", + "henry": "Henry", + "millihenry": "Millihenry", + "microhenry": "Microhenry", + "nanohenry": "Nanohenry", + "henry-per-meter": "Henry per Meter", + "tesla-meter-per-ampere": "Tesla Meter per Ampere", + "gauss-per-oersted": "Gauss per Oersted", + "kilogram-per-mole": "Kilogram per mole", + "gram-per-mole": "Gram per mole", + "milligram-per-mole": "Milligram per mole", + "joule-per-mole": "Joule per Mole", + "joule-per-mole-kelvin": "Joule per Mole-Kelvin", + "millivolts-per-meter": "Millivolts per meter", + "volts-per-meter": "Volts per meter", + "kilovolts-per-meter": "Kilovolts per meter", + "radian-per-second": "Radian per second", + "radian-per-second-squared": "Radian per second squared", + "revolutions-per-minute-per-second": "Angular acceleration", + "revolutions-per-minute-per-second-squared": "Angular Acceleration", + "deg-per-second": "deg/s", + "degrees-brix": "Degrees Brix", + "katal": "Katal", + "katal-per-cubic-metre": "Katal per Cubic Metre" }, "user": { "user": "User", "users": "Users", - "customer-users": "Customer Users", - "tenant-admins": "Tenant Admins", + "customer-users": "Customer users", + "tenant-admins": "Tenant admins", "sys-admin": "System administrator", "tenant-admin": "Tenant administrator", "customer": "Customer", "anonymous": "Anonymous", - "add": "Add User", + "add": "Add user", "delete": "Delete user", "add-user-text": "Add new user", "no-users-text": "No users found", @@ -3866,8 +4725,8 @@ "email": "Email", "email-required": "Email is required.", "invalid-email-format": "Invalid email format.", - "first-name": "First Name", - "last-name": "Last Name", + "first-name": "First name", + "last-name": "Last name", "description": "Description", "default-dashboard": "Default dashboard", "always-fullscreen": "Always fullscreen", @@ -3975,7 +4834,7 @@ "created": "{{created}} created", "updated": "{{updated}} updated", "deleted": "{{deleted}} deleted", - "remove-other-entities-confirm-text": "Be careful! This will permanently delete all current entities
not present in the version you want to restore.

Please type remove other entities to confirm.", + "remove-other-entities-confirm-text": "Be careful! This will permanently delete all current entities
not present in the version you want to restore.

Please type \"remove other entities\" to confirm.", "auto-commit-to-branch": "auto-commit to {{ branch }} branch", "default-create-entity-version-name": "{{entityName}} update", "sync-strategy-merge-hint": "Creates or updates selected entities in the repository. All other repository entities are not modified.", @@ -3986,15 +4845,24 @@ "auto-commit-settings-read-only-hint": "Auto-commit feature doesn't work with enabled read-only option in Repository settings." }, "widget": { - "widget-library": "Widgets Library", + "widget-library": "Widgets library", "widget-bundle": "Widgets Bundle", "all-bundles": "All bundles", "select-widgets-bundle": "Select widgets bundle", + "widgets": "Widgets", + "all-widgets": "All widgets", + "widget": "Widget", + "select-widget": "Select widget", + "no-widgets-matching": "No widgets matching '{{entity}}' were found.", + "no-widgets": "No widgets yet", + "no-widgets-text": "No widgets found", "management": "Widget management", "editor": "Widget Editor", + "confirm-to-exit-editor-html": "You have unsaved widget settings.
Are you sure you want to leave this page?", "widget-type-not-found": "Problem loading widget configuration.
Probably associated\n widget type was removed.", "widget-type-load-error": "Widget wasn't loaded due to the following errors:", "remove": "Remove widget", + "delete": "Delete widget", "edit": "Edit widget", "remove-widget-title": "Are you sure you want to remove the widget '{{widgetTitle}}'?", "remove-widget-text": "After the confirmation the widget and all related data will become unrecoverable.", @@ -4005,18 +4873,27 @@ "rpc": "Control widget", "alarm": "Alarm widget", "static": "Static widget", + "timeseries-short": "series", + "latest-short": "latest", + "rpc-short": "control", + "alarm-short": "alarm", + "static-short": "static", "select-widget-type": "Select widget type", "missing-widget-title-error": "Widget title must be specified!", "widget-saved": "Widget saved", "unable-to-save-widget-error": "Unable to save widget! Widget has errors!", "save": "Save widget", "saveAs": "Save widget as", - "save-widget-type-as": "Save widget type as", - "save-widget-type-as-text": "Please enter new widget title and/or select target widgets bundle", + "move": "Move widget", + "save-widget-as": "Save widget as", + "save-widget-as-text": "Please enter new widget title", "toggle-fullscreen": "Toggle fullscreen", "run": "Run widget", - "title": "Widget title", + "widget-title": "Widget title", + "title": "Title", "title-required": "Widget title is required.", + "title-max-length": "Title should be less than 256", + "system": "System", "type": "Widget type", "resources": "Resources", "resource-url": "JavaScript/CSS URL", @@ -4031,25 +4908,37 @@ "latest-datakey-settings-schema": "Latest data key settings schema", "widget-settings": "Widget settings", "description": "Description", + "tags": "Tags", "image-preview": "Image preview", "settings-form-selector": "Settings form selector", "data-key-settings-form-selector": "Data key settings form selector", "latest-data-key-settings-form-selector": "Latest data key settings form selector", + "all": "All", + "actual": "Actual", + "deprecated": "Deprecated", "has-basic-mode": "Has basic mode", "basic-mode-form-selector": "Basic mode form selector", "basic-mode": "Basic", "advanced-mode": "Advanced", "javascript": "Javascript", "js": "JS", - "remove-widget-type-title": "Are you sure you want to remove the widget type '{{widgetName}}'?", - "remove-widget-type-text": "After the confirmation the widget type and all related data will become unrecoverable.", - "remove-widget-type": "Remove widget type", - "add-widget-type": "Add new widget type", - "widget-type-load-failed-error": "Failed to load widget type!", + "delete-widget-title": "Are you sure you want to delete the widget '{{widgetName}}'?", + "delete-widget-text": "After the confirmation the widget and all related data will become unrecoverable.", + "delete-widgets-title": "Are you sure you want to delete { count, plural, =1 {1 widget} other {# widgets} }?", + "delete-widgets-text": "Be careful, after the confirmation all selected widgets will be removed and all related data will become unrecoverable.", + "delete-widget": "Delete widget", "widget-template-load-failed-error": "Failed to load widget template!", - "add": "Add Widget", + "details": "Details", + "widget-details": "Widget details", + "add": "Add widget", + "add-existing-widget": "Add existing widget", + "add-new-widget": "Add new widget", + "search-widgets": "Search widgets", + "selected-widgets": "{ count, plural, =1 {1 widget} other {# widgets} } selected", "undo": "Undo widget changes", "export": "Export widget", + "export-widgets": "Export widgets", + "import": "Import widget", "no-data": "No data to display on widget", "data-overflow": "Widget displays {{count}} out of {{total}} entities", "alarm-data-overflow": "Widget displays alarms for {{allowedEntities}} (maximum allowed) entities out of {{totalEntities}} entities", @@ -4117,14 +5006,16 @@ }, "widgets-bundle": { "current": "Current bundle", - "widgets-bundles": "Widgets Bundles", - "add": "Add Widgets Bundle", + "widgets-bundles": "Widgets bundles", + "widgets-bundle-widgets": "Widgets Bundle Widgets", + "add": "Add widgets bundle", "delete": "Delete widgets bundle", "title": "Title", "title-required": "Title is required.", "title-max-length": "Title should be less than 256", "description": "Description", "image-preview": "Image preview", + "order": "Order", "add-widgets-bundle-text": "Add new widgets bundle", "no-widgets-bundles-text": "No widgets bundles found", "empty": "Widgets bundle is empty", @@ -4140,6 +5031,7 @@ "system": "System", "import": "Import widgets bundle", "export": "Export widgets bundle", + "export-widgets-bundle-widgets-prompt": "Include bundle widgets in exported data (otherwise only referenced widget FQNs will be exported)", "export-failed-error": "Unable to export widgets bundle: {{error}}", "create-new-widgets-bundle": "Create new widgets bundle", "widgets-bundle-file": "Widgets bundle file", @@ -4165,6 +5057,7 @@ "enable-fullscreen": "Enable fullscreen", "background-color": "Background color", "text-color": "Text color", + "border-radius": "Border radius", "padding": "Padding", "margin": "Margin", "widget-style": "Widget style", @@ -4176,9 +5069,13 @@ "mobile-hide": "Hide widget in mobile mode", "desktop-hide": "Hide widget in desktop mode", "units": "Special symbol to show next to value", + "units-by-default": "Units by default", "decimals": "Number of digits after floating point", + "decimals-by-default": "Decimals by default", + "default-data-key-parameter-hint": "This parameter applies to all widget values unless overridden by data key configuration", "units-short": "Units", "decimals-short": "Decimals", + "decimals-suffix": "decimals", "timewindow": "Timewindow", "use-dashboard-timewindow": "Use dashboard timewindow", "use-widget-timewindow": "Use widget timewindow", @@ -4216,6 +5113,7 @@ "title-icon": "Title icon", "display-icon": "Display title icon", "card-icon": "Card icon", + "icon": "Icon", "icon-color": "Icon color", "icon-size": "Icon size", "advanced-settings": "Advanced settings", @@ -4227,26 +5125,73 @@ "preview": "Preview", "set": "Set", "set-message": "Set message", - "card-title": "Card title", "advanced-title-style": "Advanced title style", "card-style": "Card style", "text": "Text", "background": "Background", "advanced-widget-style": "Advanced widget style", - "card-buttons": "Card buttons" + "card-buttons": "Card buttons", + "show-card-buttons": "Show card buttons", + "card-border-radius": "Card border radius", + "card-appearance": "Card appearance", + "color": "Color", + "tooltip": "Tooltip", + "units-required": "Unit is required." }, "widget-type": { "import": "Import widget type", "export": "Export widget type", - "export-failed-error": "Unable to export widget type: {{error}}", - "create-new-widget-type": "Create new widget type", - "widget-type-file": "Widget type file", - "invalid-widget-type-file-error": "Unable to import widget type: Invalid widget type data structure." + "export-failed-error": "Unable to export widget: {{error}}", + "widget-file": "Widget file", + "invalid-widget-file-error": "Unable to import widget: Invalid widget data structure." }, "widgets": { + "background": { + "background": "Background", + "background-settings": "Background settings", + "background-type-image": "Image", + "background-type-color": "Color", + "image-url": "Image URL", + "overlay": "Overlay", + "enable-overlay": "Enable overlay", + "blur": "Blur", + "preview": "Preview" + }, + "battery-level": { + "layout": "Layout", + "layout-vertical-solid": "Vertical. Solid", + "layout-horizontal-solid": "Horizontal. Solid", + "layout-vertical-divided": "Vertical. Divided", + "layout-horizontal-divided": "Horizontal. Divided", + "icon": "Icon", + "value": "Value", + "auto-scale": "Auto scale", + "battery-level-color": "Battery level color", + "battery-shape-color": "Battery shape color", + "battery-level-card-style": "Battery level card style", + "sections-count": "Sections count" + }, + "signal-strength": { + "value": "Value", + "last-update": "Last update", + "no-signal": "No signal", + "layout": "Layout", + "layout-wifi": "Wi-Fi", + "layout-cellular-bar": "Cellular bar", + "icon": "Icon", + "date": "Date", + "active-bars-color": "Active signal bars color", + "inactive-bars-color": "Inactive signal bars color", + "tooltip": "Tooltip", + "background-color": "Background color", + "background-blur": "Background blur", + "signal-strength-card-style": "Signal strength card style" + }, "chart": { "common-settings": "Common settings", "enable-stacking-mode": "Enable stacking mode", + "selection": "Time range selection", + "enable-selection-mode": "Enable selection mode", "line-shadow-size": "Line shadow size", "display-smooth-lines": "Display smooth (curved) lines", "default-bar-width": "Default bar width for non-aggregated data (milliseconds)", @@ -4254,10 +5199,12 @@ "bar-alignment-left": "Left", "bar-alignment-right": "Right", "bar-alignment-center": "Center", + "default-font": "Default font", "default-font-size": "Default font size", "default-font-color": "Default font color", "thresholds-line-width": "Default line width for all thresholds", "tooltip-settings": "Tooltip settings", + "tooltip": "Tooltip", "show-tooltip": "Show tooltip", "hover-individual-points": "Hover individual points", "show-cumulative-values": "Show cumulative values in stacking mode", @@ -4332,9 +5279,10 @@ "axis-position-right": "Right", "thresholds": "Thresholds", "no-thresholds": "No thresholds configured", - "add-threshold": "Add new threshold", + "add-threshold": "Add threshold", "show-values-for-comparison": "Show historical values for comparison", "comparison-values-label": "Historical values label", + "comparison-line-color": "Comparison line color", "threshold-settings": "Threshold settings", "use-as-threshold": "Use key value as threshold", "threshold-line-width": "Threshold line width", @@ -4343,6 +5291,7 @@ "radius": "Radius", "inner-radius": "Inner radius", "tilt": "Tilt", + "common-pie-settings-range-error": "Value should be in range from 0 to 1", "stroke-settings": "Stroke settings", "width-pixels": "Width (pixels)", "show-labels": "Show labels", @@ -4353,7 +5302,35 @@ "border-color": "Border color", "legend-settings": "Legend settings", "display-legend": "Display legend", - "labels-font-color": "Labels font color" + "labels-font-color": "Labels font color", + "series": "Series", + "add-series": "Add series", + "series-settings": "Series settings", + "remove-series": "Remove series", + "no-series": "No series configured", + "no-series-error": "At least one series should be specified", + "chart-appearance": "Chart appearance", + "vertical-grid-lines": "Vertical grid lines", + "horizontal-grid-lines": "Horizontal grid lines", + "chart-background": "Chart background", + "grid-lines-color": "Grid lines color", + "border": "Border", + "axis": "Axis", + "vertical-axis": "Vertical axis", + "ticks": "Ticks", + "horizontal-axis": "Horizontal axis" + }, + "color": { + "color-settings": "Color settings", + "color-type-constant": "Constant", + "color-type-range": "Range", + "color-type-function": "Function", + "color": "Color", + "value-range": "Value range", + "from": "From", + "to": "To", + "color-function": "Color function", + "copy-color-settings-from": "Copy color settings from" }, "dashboard-state": { "dashboard-state-settings": "Dashboard state settings", @@ -4441,6 +5418,29 @@ "Ok": "Ok" } }, + "doughnut": { + "total": "Total", + "layout": "Layout", + "layout-default": "Default", + "layout-with-total": "With total", + "auto-scale": "Auto scale", + "clockwise-layout": "Clockwise layout", + "sort-series": "Sort series by label", + "central-total-value": "Central total value", + "legend-position-top": "Top", + "legend-position-bottom": "Bottom", + "legend-position-left": "Left", + "legend-position-right": "Right", + "legend-label": "Label", + "legend-value": "Value", + "tooltip": "Tooltip", + "tooltip-value": "Value", + "tooltip-value-type-absolute": "Absolute", + "tooltip-value-type-percentage": "Percentage", + "tooltip-background-color": "Background color", + "tooltip-background-blur": "Background blur", + "doughnut-card-style": "Doughnut card style" + }, "entities-hierarchy": { "hierarchy-data-settings": "Hierarchy data settings", "relations-query-function": "Node relations query function", @@ -4469,7 +5469,12 @@ "read-only": "Read only", "events-title": "Gateway events form title", "events-filter": "Events filter", - "event-key-contains": "Event key contains..." + "event-key-contains": "Event key contains...", + "show-connector": "Show for the connector", + "connector-state-param-key": "Connector state parameter key", + "status": "Status", + "message": "Message", + "created-time": "Created time" }, "gauge": { "default-color": "Default color", @@ -4477,10 +5482,14 @@ "ticks-settings": "Ticks settings", "min-value": "Minimum value", "max-value": "Maximum value", + "min-value-short": "min", + "max-value-short": "max", "start-ticks-angle": "Start ticks angle", "ticks-angle": "Ticks angle", + "major-ticks": "Major ticks", "major-ticks-count": "Major ticks count", "major-ticks-color": "Major ticks color", + "minor-ticks": "Minor ticks", "minor-ticks-count": "Minor ticks count", "minor-ticks-color": "Minor ticks color", "tick-numbers-font": "Tick numbers font", @@ -4492,21 +5501,29 @@ "units-font": "Units text font", "value-box-settings": "Value box settings", "show-value-box": "Show value box", + "value-box": "Value box", "value-int": "Digits count for integer part of value", + "value-text": "Value text", + "value-text-shadow": "Value text shadow", "value-font": "Value text font", + "rect-stroke-color-start": "Rectangle stroke color - start gradient", + "rect-stroke-color-end": "Rectangle stroke color - end gradient", + "background-color": "Background color", + "shadow-color": "Shadow color", "value-box-rect-stroke-color": "Value box rectangle stroke color", "value-box-rect-stroke-color-end": "Value box rectangle stroke color - end gradient", "value-box-background-color": "Value box background color", "value-box-shadow-color": "Value box shadow color", "plate-settings": "Plate settings", - "show-plate-border": "Show plate border", + "show-plate-border": "Plate border", "plate-color": "Plate color", "needle-settings": "Needle settings", "needle-circle-size": "Needle circle size", "needle-color": "Needle color", + "needle-color-start": "Needle color - start gradient", "needle-color-end": "Needle color - end gradient", "needle-color-shadow-up": "Upper half of the needle shadow color", - "needle-color-shadow-down": "Drop shadow needle color", + "needle-color-shadow-down": "Drop shadow", "highlights-settings": "Highlights settings", "highlights-width": "Highlights width", "highlights": "Highlights", @@ -4516,7 +5533,8 @@ "no-highlights": "No highlights configured", "add-highlight": "Add highlight", "animation-settings": "Animation settings", - "enable-animation": "Enable animation", + "enable-animation": "Animation", + "animation-duration-rule": "Animation duration and rule", "animation-duration": "Animation duration", "animation-rule": "Animation rule", "animation-linear": "Linear", @@ -4531,17 +5549,21 @@ "animation-debounce": "Debounce", "animation-delastic": "Delastic", "linear-gauge-settings": "Linear gauge settings", + "bar-stroke": "Bar stroke", "bar-stroke-width": "Bar stroke width", "bar-stroke-color": "Bar stroke color", - "bar-background-color": "Gauge bar background color", + "bar-background-color": "Bar background color - start gradient", "bar-background-color-end": "Bar background color - end gradient", "progress-bar-color": "Progress bar color", + "progress-bar": "Progress bar", + "progress-bar-color-start": "Progress bar color - start gradient", "progress-bar-color-end": "Progress bar color - end gradient", "major-ticks-names": "Major ticks names", "show-stroke-ticks": "Show ticks stroke", "major-ticks-font": "Major ticks font", "border-color": "Border color", "border-width": "Border width", + "needle-circle": "Needle circle", "needle-circle-color": "Needle circle color", "animation-target": "Animation target", "animation-target-needle": "Needle", @@ -4585,7 +5607,18 @@ "tick-color": "Tick color", "tick-values": "Tick values", "no-tick-values": "No tick values configured", - "add-tick-value": "Add tick value" + "add-tick-value": "Add tick value", + "gauge-appearance": "Gauge appearance", + "units-title": "Units title", + "value": "Value", + "ticks": "Ticks", + "arrow-and-scale-color": "Arrow and scale default color", + "scale-settings": "Scale settings", + "scale": "Scale", + "scale-color": "Scale colors", + "compass-appearance": "Compass appearance", + "labels": "Labels", + "label-style": "Label style" }, "gpio": { "pin": "Pin", @@ -4702,6 +5735,9 @@ "input-fields-alignment": "Input fields alignment", "input-fields-alignment-column": "Column (default)", "input-fields-alignment-row": "Row", + "layout": "Layout", + "row-gap": "Gap between rows in pixels", + "column-gap": "Gap between columns in pixels", "latitude-field-required": "Latitude field required", "longitude-field-required": "Longitude field required", "attribute-settings": "Attribute settings", @@ -4750,12 +5786,19 @@ "datakey-value-type-date": "Date", "datakey-value-type-time": "Time", "datakey-value-type-select": "Select", + "datakey-value-type-color": "Color", "value-is-required": "Value is required", "ability-to-edit-attribute": "Ability to edit attribute", "ability-to-edit-attribute-editable": "Editable (default)", "ability-to-edit-attribute-disabled": "Disabled", "ability-to-edit-attribute-readonly": "Read-only", "disable-on-datakey-name": "Disable on false value of another datakey (specify datakey name)", + "field-appearance": "Field appearance", + "appearance-fill": "Fill", + "appearance-outline": "Outline", + "subscript-sizing": "Subscript sizing", + "subscript-sizing-fixed": "Fixed", + "subscript-sizing-dynamic": "Dynamic", "slide-toggle-settings": "Slide toggle settings", "slide-toggle-label-position": "Slide toggle label position", "slide-toggle-label-position-after": "After", @@ -4871,6 +5914,20 @@ "no-columns-found": "No columns found", "no-columns-matching": "'{{column}}' not found." }, + "range-chart": { + "chart": "Chart", + "data-zoom": "Data zoom", + "range-colors": "Range colors", + "out-of-range-color": "Out of range color", + "fill-area": "Fill area", + "legend-label": "Label", + "tooltip": "Tooltip", + "tooltip-value": "Value", + "tooltip-date": "Date", + "tooltip-background-color": "Background color", + "tooltip-background-blur": "Background blur", + "range-chart-card-style": "Range chart card style" + }, "rpc": { "value-settings": "Value settings", "initial-value": "Initial value", @@ -5164,6 +6221,132 @@ "label-position-left": "Left", "label-position-top": "Top" }, + "value-card": { + "layout": "Layout", + "layout-square": "Square", + "layout-vertical": "Vertical", + "layout-centered": "Centered", + "layout-simplified": "Simplified", + "layout-horizontal": "Horizontal", + "layout-horizontal-reversed": "Horizontal reversed", + "label": "Label", + "icon": "Icon", + "value": "Value", + "date": "Date", + "value-card-style": "Value card style", + "auto-scale": "Auto scale" + }, + "liquid-level-card": { + "layout-simple": "Simple", + "layout-percentage": "Percentage", + "layout-absolute": "Absolute", + "layout": "Layout", + "background-overlay": "Value background overlay", + "total-volume": "Total volume", + "tank": "Tank", + "shape": "Shape", + "datasource-units": "Source units", + "widget-units": "Widget units", + "decimals": "Decimals", + "liquid": "Liquid", + "liquid-color": "Liquid color", + "value": "Value", + "value-font": "Value font", + "level": "Level", + "last-update": "Last update", + "shape-by-attribute": "Set tank shape by attribute name", + "tooltip-background": "Background color", + "background-blur": "Background blur", + "tank-color": "Tank color", + "static": "Static", + "see-examples": "See examples", + "attribute": "Attribute", + "shape-type": "Type", + "v-oval": "Vertical Oval", + "v-cylinder": "Vertical Cylinder", + "v-capsule": "Vertical Capsule", + "rectangle": "Rectangle", + "h-oval": "Horizontal Oval", + "h-ellipse": "Horizontal Ellipse", + "h-dish-ends": "Horizontal Dish Ends", + "h-cylinder": "Horizontal Cylinder", + "h-capsule": "Horizontal Capsule", + "h-elliptical_2_1": "Horizontal 2:1 Elliptical", + "icon": "Card icon", + "title": "Card title", + "units": "Units", + "color-and-font": "Color and font", + "shape-attribute-name": "Attribute name", + "total-volume-required": "Total volume is required.", + "attribute-name-required": "Attribute name is required.", + "attribute-key-not-set": "Attribute '{{attributeName}}' key not set", + "attribute-key-invalid": "Attribute '{{attributeName}}' key is invalid" + }, + "aggregated-value-card": { + "subtitle": "Subtitle", + "chart": "Chart", + "values": "Values", + "value-appearance": "Value appearance", + "position": "Position", + "position-center": "Center", + "position-right-top": "Right top", + "position-right-bottom": "Right bottom", + "position-left-top": "Left top", + "position-left-bottom": "Left bottom", + "font": "Font", + "color": "Color", + "arrow": "Arrow", + "display-up-down-arrow": "Display Up/Down arrow", + "add-value": "Add value", + "remove-value": "Remove value", + "no-values": "No values configured", + "aggregation": "Aggregation", + "aggregated-value-card-style": "Aggregated value card style", + "auto-scale": "Auto scale" + }, + "value-chart-card": { + "layout": "Layout", + "layout-left": "Left", + "layout-right": "Right", + "auto-scale": "Auto scale", + "icon": "Icon", + "value": "Value", + "chart": "Chart", + "value-chart-card-style": "Value chart card style" + }, + "progress-bar": { + "layout": "Layout", + "layout-default": "Default", + "layout-simplified": "Simplified", + "auto-scale": "Auto scale", + "icon": "Icon", + "value": "Value", + "range": "Range", + "min": "min", + "max": "max", + "range-ticks": "Range ticks", + "bar": "Bar", + "bar-color": "Bar color", + "bar-background": "Bar background", + "progress-bar-card-style": "Progress bar card style" + }, + "alarm-count": { + "alarm-count-card-style": "Alarm count card style" + }, + "entity-count": { + "entity-count-card-style": "Entity count card style" + }, + "count": { + "layout": "Layout", + "layout-column": "Column", + "layout-row": "Row", + "label": "Label", + "icon": "Icon", + "icon-background": "Icon background", + "value": "Value", + "chevron": "Chevron", + "auto-scale": "Auto scale" + }, "table": { "common-table-settings": "Common Table Settings", "enable-search": "Enable search", @@ -5172,8 +6355,7 @@ "hidden-cell-button-display-mode": "Hidden cell button actions display mode", "show-empty-space-hidden-action": "Show empty space instead of hidden cell button action", "dont-reserve-space-hidden-action": "Don't reserve space for hidden action buttons", - "display-timestamp": "Display timestamp column", - "display-milliseconds": "Display timestamp milliseconds", + "display-timestamp": "Timestamp", "display-pagination": "Display pagination", "default-page-size": "Default page size", "use-entity-label-tab-name": "Use entity label in tab name", @@ -5216,19 +6398,52 @@ "display-alarm-activity": "Display alarm activity", "allow-alarms-assign": "Allow alarms assignment", "columns": "Columns", + "column-settings": "Column settings", "remove-column": "Remove column", "add-column": "Add column", - "no-columns": "No columns configured" + "no-columns": "No columns configured", + "columns-to-display": "Columns to display", + "table-header": "Table header", + "header-buttons": "Header buttons", + "table-buttons": "Table buttons", + "pagination": "Pagination", + "rows": "Rows", + "timeseries-column-error": "At least one timeseries column should be specified", + "alarm-column-error": "At least one alarm column should be specified", + "table-tabs": "Table tabs", + "show-cell-actions-menu-mobile": "Show cell actions dropdown menu in mobile mode" + }, + "wind-speed-direction": { + "layout": "Layout", + "layout-default": "Default", + "layout-advanced": "Advanced", + "layout-simplified": "Simplified", + "values": "Values", + "wind-direction": "Wind direction", + "center-value": "Center value", + "icon": "Icon", + "arrow": "Arrow", + "ticks": "Ticks", + "labels-type": "Labels type", + "directional-names": "Directional names", + "degrees": "Degrees", + "major-ticks": "Major ticks", + "minor-ticks": "Minor ticks", + "wind-speed-direction-card-style": "Wind speed and direction card style", + "ticks-color": "Ticks color", + "ticks-labels-type": "Ticks labels type", + "arrow-color": "Arrow color" }, "value-source": { "value-source": "Value source", "predefined-value": "Predefined value", - "entity-attribute": "Value taken from entity attribute", + "entity-attribute": "Entity attribute", "value": "Value", "source-entity-alias": "Source entity alias", "source-entity-attribute": "Source entity attribute" }, "widget-font": { + "font-settings": "Font settings", "font-family": "Font family", "size": "Size", "relative-font-size": "Relative font size (percents)", @@ -5242,7 +6457,10 @@ "font-weight-bolder": "Bolder", "font-weight-lighter": "Lighter", "color": "Color", - "shadow-color": "Shadow color" + "shadow-color": "Shadow color", + "preview": "Preview", + "line-height": "Line height", + "auto": "Auto" }, "home": { "no-data-available": "No data available" @@ -5439,11 +6657,17 @@ } } }, + "color": { + "color": "Color" + }, "icon": { "icon": "Icon", + "icons": "Icons", "select-icon": "Select icon", "material-icons": "Material icons", - "show-all": "Show all icons" + "show-all": "Show all icons", + "search-icon": "Search icon", + "no-icons-found": "No icons found for '{{iconSearch}}'" }, "phone-input": { "phone-input-label": "Phone number", @@ -5463,7 +6687,8 @@ "node-selected": "On node selected", "element-click": "On HTML element click", "pie-slice-click": "On slice click", - "row-double-click": "On row double click" + "row-double-click": "On row double click", + "card-click": "On card click" } }, "paginator" : { @@ -5491,9 +6716,9 @@ "ka_GE": "ქართული", "ko_KR": "한국어", "lv_LV": "Latviešu", + "nl_BE": "Koninkrijk België", "pt_BR": "Português do Brasil", "ro_RO": "Română", - "ru_RU": "Русский", "sl_SI": "Slovenščina", "tr_TR": "Türkçe", "uk_UA": "Українська", diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index d7925248ed..8727339851 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -10,11 +10,16 @@ "permission-denied": "Permiso Denegado", "permission-denied-text": "No tienes suficientes derechos para realizar esta operación!" }, + "account": { + "account": "Cuenta", + "notification-settings": "Ajustes de notificaciones" + }, "action": { "activate": "Activar", "suspend": "Suspender", "save": "Guardar", "saveAs": "Guardar como", + "move": "Mover", "cancel": "Cancelar", "ok": "OK", "delete": "Borrar", @@ -35,6 +40,7 @@ "edit-mode": "Modo Edición", "enter-edit-mode": "Modo Edición", "decline-changes": "Descartar cambios", + "decline": "Descartar", "close": "Cerrar", "back": "Atrás", "run": "Ejecutar", @@ -66,7 +72,12 @@ "more": "Más", "less": "Menos", "skip": "Saltar", - "send": "Enviar" + "send": "Enviar", + "reset": "Reset", + "show-more": "Mostrar más", + "dont-show-again": "No volver a mostrar", + "see-documentation": "Ver documentación", + "clear": "Borrar" }, "aggregation": { "aggregation": "Agrupación", @@ -173,6 +184,7 @@ "domain-name-unique": "El nombre de dominio y protocolo debe ser único.", "domain-name-max-length": "El nombre de dominio debe ser menor que 256", "error-verification-url": "Un nombre de dominio no debe contener símbolos '/' y ':'. Ejemplo: thingsboard.io", + "connection-settings": "Ajustes de conexión", "oauth2": { "access-token-uri": "URI Access token", "access-token-uri-required": "Se requere URI Access token.", @@ -263,7 +275,28 @@ "platform-android": "Android", "platform-ios": "iOS", "all-platforms": "Todas las plataformas", - "allowed-platforms": "Plataformas permitidas" + "smtp-provider": "Proveedor SMTP", + "allowed-platforms": "Plataformas permitidas", + "authentication": "Autenticación", + "basic": "Básica", + "provider": "Proveedor", + "redirect-url": "URL Redirección", + "domain-name": "Nombre de dominio", + "redirect-url-template": "Plantilla de redirección (URI)", + "microsoft-tenant-id": "Microsoft tenant ID", + "microsoft-tenant-id-required": "Se requiere tenant ID", + "token-uri": "URI Token", + "token-uri-required": "Se requiere URI token", + "redirect-uri": "URI Redirección", + "google-provider": "Google", + "microsoft-provider": "Office 365", + "sendgrid-provider": "Sendgrid", + "custom-provider": "Personalizado", + "generate-access-token": "Generar token de acceso", + "update-access-token": "Actualizar token de acceso", + "access-token-status": "Estado token:", + "token-status-generated": "Generado", + "token-status-not-generated": "No generado" }, "smpp-provider": { "smpp-version": "Versión SMPP", @@ -481,6 +514,7 @@ "severity-indeterminate": "Indeterminada", "acknowledge": "Reconocer", "clear": "Normalizar", + "delete": "Borrar", "search": "Buscar alarmas", "selected-alarms": "{ count, plural, =1 {1 alarma} other {# alarmas} } seleccionadas", "no-data": "Sin datos que mostrar", @@ -491,10 +525,14 @@ "aknowledge-alarms-text": "Estas seguro de reconocer { count, plural, =1 {1 alarma} other {# alarmas} }?", "aknowledge-alarm-title": "Recononcer Alarma", "aknowledge-alarm-text": "Estas seguro de reconocer Alarma?", + "selected-alarms-are-acknowledged": "Las alarmas seleccionadas ya están reconocidas", "clear-alarms-title": "Normalizar { count, plural, =1 {1 alarma} other {# alarmas} }", "clear-alarms-text": "Limpiar { count, plural, =1 {1 alarma} other {# alarmas} }?", "clear-alarm-title": "Limpiar Alarma", "clear-alarm-text": "Limpiar Alarma?", + "delete-alarms-title": "Delete { count, plural, =1 {1 alarm} other {# alarms} }", + "delete-alarms-text": "Are you sure you want to delete { count, plural, =1 {1 alarm} other {# alarms} }?", + "selected-alarms-are-cleared": "Selected alarms are already cleared", "alarm-status-filter": "Filtro de estados de Alarmas", "alarm-filter-title": "Filtros de alarmas", "assigned": "Asignadas", @@ -512,7 +550,11 @@ "search-propagated-alarms": "Buscar alarmas propagadas", "comments": "Comentarios de alarma", "show-more": "Mostrar más", - "additional-info": "Información adicional" + "additional-info": "Información adicional", + "alarm-type": "Tipo de alarma", + "enter-alarm-type": "Introduce tipo de alarma", + "no-alarm-types-matching": "No se han encontrado tipos de alarma que coincidan con '{{entitySubtype}}'.", + "alarm-type-list-empty": "No se han seleccionado tipos de alarma." }, "alarm-activity": { "add": "Añadir comentario...", @@ -666,7 +708,9 @@ "attribute": { "attributes": "Atributos", "latest-telemetry": "Última telemetría", + "no-latest-telemetry": "No hay última telemetría", "attributes-scope": "Alcance de los atributos del dispositivo", + "scope-telemetry": "Telemetría", "scope-latest-telemetry": "Última telemetría", "scope-client": "Atributos de Cliente", "scope-server": "Atributos de Servidor", @@ -678,6 +722,8 @@ "key-required": "Clave del atributo requerida.", "value": "Valor", "value-required": "Valor del atributo requerido.", + "telemetry-key-required": "Se requiere clave de telemetría", + "telemetry-value-required": "Se requiere valor Telemetry value is required", "delete-attributes-title": "¿Eliminar { count, plural, =1 {1 atributo} other {# atributos} }?", "delete-attributes-text": "Atención, tras la confirmación el atributo será eliminado, y la información relacionada será irrecuperable.", "delete-attributes": "Borrar atributo", @@ -693,7 +739,19 @@ "no-attributes-text": "No se encontró ningún atributo", "no-telemetry-text": "No se encontró ninguna telemetría", "copy-key": "Copiar clave", - "copy-value": "Copiar valor" + "add-telemetry": "Añadir telemetría", + "copy-value": "Copiar valor", + "delete-timeseries": { + "start-time": "Hora de inicio", + "ends-on": "Fin", + "strategy": "Estrategia", + "delete-strategy": "Estrategia de borrado", + "all-data": "Borrar todos los datos", + "all-data-except-latest-value": "Borrar todos los datos salvo los últimos valores", + "latest-value": "Borrar último valor", + "all-data-for-time-period": "Borrar todos los datos del periodo seleccionado", + "rewrite-latest-value": "Reescribir último valor" + } }, "api-usage": { "api-features": "Características API", @@ -714,10 +772,6 @@ "executions": "Ejecuciones", "javascript": "JavaScript", "javascript-executions": "Ejecuciones JavaScript", - "javascript-functions": "Funciones JavaScript", - "javascript-functions-daily-activity": "Actividad diaria de funciones JavaScript", - "javascript-functions-hourly-activity": "Actividad horaria de funciones JavaScript", - "javascript-functions-monthly-activity": "Actividad mensual de funciones JavaScript", "latest-error": "Último error", "messages": "Mensajes", "notifications": "Notificaciones", @@ -756,6 +810,18 @@ "view-details": "Ver detalles", "view-statistics": "Ver estadísticas" }, + "api-limit": { + "cassandra-queries": "Consultas Cassandra", + "entity-version-creation": "Creación de versiones de entidad", + "entity-version-load": "Carga de versiones de entidad", + "notification-requests": "Solicitudes de notificación", + "notification-requests-per-rule": "Solicitudes de notificación por regla", + "rest-api-requests": "Solicitudes REST API", + "rest-api-requests-per-customer": "Solicitudes REST API por cliente", + "transport-messages": "Mensajes de transporte", + "transport-messages-per-device": "Mensajes de transporte por dispositivo", + "ws-updates-per-session": "Actualizaciones WS por sesión" + }, "audit-log": { "audit": "Auditoría", "audit-logs": "Registro Auditoría", @@ -807,7 +873,8 @@ "type-provision-success": "Dispositivo aprovisionado", "type-provision-failure": "Aprovisionamiento fallido", "type-timeseries-updated": "Telemetría actualizada", - "type-timeseries-deleted": "Telemetría borrada" + "type-timeseries-deleted": "Telemetría borrada", + "type-sms-sent": "SMS enviado" }, "confirm-on-exit": { "message": "Tienes cambios sin guardar. ¿Abandonar la página?", @@ -838,7 +905,9 @@ "created-time": "Fecha de creación", "loading": "Cargando...", "proceed": "Proceder", - "open-details-page": "Abrir detalles" + "open-details-page": "Abrir detalles", + "not-found": "No encontrado", + "documentation": "Documentación" }, "content-type": { "json": "Json", @@ -867,9 +936,9 @@ "manage-public-devices": "Gestionar dispositivos públicos", "manage-public-dashboards": "Gestionar paneles públicos", "manage-customer-assets": "Gestionar activos del cliente", + "manage-public-assets": "Gestionar activos públicos", "manage-customer-edges": "Administrar Edges de clientes", "manage-public-edges": "Administrar Edges públicos", - "manage-public-assets": "Gestionar activos públicos", "add-customer-text": "Agregar nuevo cliente", "no-customers-text": "No se encontraron clientes", "customer-details": "Detalles del cliente", @@ -901,6 +970,13 @@ "edges": "Instancias de Edge del cliente", "manage-edges": "Administrar Edges" }, + "date": { + "last-update-n-ago": "Última actualización hace N", + "last-update-n-ago-text": "Última actualización {{ agoText }}", + "custom-date": "Fecha personalizada", + "format": "Formato", + "preview": "Previsualizar" + }, "datetime": { "date-from": "Fecha desde", "time-from": "Hora desde", @@ -930,6 +1006,7 @@ "no-dashboards-text": "Ningún panel encontrado", "no-widgets": "Ningún widget configurado", "add-widget": "Agregar nuevo widget", + "add-widget-button-text": "Agregar widget", "title": "Título", "image": "Imagen de panel", "mobile-app-settings": "Ajustes de aplicación móvil", @@ -1058,6 +1135,7 @@ "public-link-copied-message": "El link público del panel se ha copiado al portapapeles", "manage-states": "Administrar estados de paneles", "states": "Estados de paneles", + "states-short": "Estados", "search-states": "Buscar estados de paneles", "selected-states": "{ count, plural, =1 {1 estado panel} other {# estado paneles} } seleccionados", "edit-state": "Editar estado panel", @@ -1086,11 +1164,14 @@ "unassign-dashboards-from-edge-text": "Después de la confirmación, se anulará la asignación de todos los paneles seleccionados y no serán accesibles por de Edge", "assign-dashboard-to-edge": "Asignar panel(es) al Edge", "assign-dashboard-to-edge-text": "Por favor selecciona los paneles para asignar al Edge", - "non-existent-dashboard-state-error": "El panel con id de estado \"{{ stateId }}\" no se ha encontrado" + "non-existent-dashboard-state-error": "El panel con id de estado \"{{ stateId }}\" no se ha encontrado", + "edit-mode": "Modo Edición" }, "datakey": { "settings": "Ajustes", + "general": "General", "advanced": "Avanzado", + "key": "Clave", "label": "Etiqueta", "color": "Color", "units": "Símbolo especial para mostrar junto con el valor", @@ -1110,6 +1191,10 @@ "function-types": "Tipos de funciones", "function-type": "Tipos de función", "function-types-required": "Tipos de funciones requerido.", + "data-keys": "Claves de datos", + "data-key": "Clave de datos", + "data-keys-required": "Se requieren claves de datos.", + "data-key-required": "Se requiere clave de datos.", "alarm-keys": "Claves de Alarmas", "alarm-key": "Clave de Slarma", "alarm-key-functions": "Funciones de claves de Alarmas", @@ -1142,7 +1227,13 @@ "delta-calculation-result": "Resultado de cálculo delta", "delta-calculation-result-previous-value": "Valor anterior", "delta-calculation-result-delta-absolute": "Delta (absoluto)", - "delta-calculation-result-delta-percent": "Delta (percentil)" + "delta-calculation-result-delta-percent": "Delta (percentil)", + "source": "Origen", + "latest": "Último", + "latest-value": "Último valor", + "delta": "delta", + "percent": "porcentaje", + "absolute": "absoluto" }, "datasource": { "type": "Típo de fuente de datos", @@ -1325,16 +1416,28 @@ "device-configuration": "Configuración del dispositivo", "transport-configuration": "Configuración del transporte", "wizard": { - "device-wizard": "Asistente de dispositivo", - "device-details": "Detalles del dispositivo", - "new-device-profile": "Crear un nuevo perfil de dispositivo", - "existing-device-profile": "Seleccionar un perfil existente", - "specific-configuration": "Configuración específica", - "customer-to-assign-device": "Cliente al que asignar el dispositivo", - "add-credentials": "Añadir credencial" + "device-details": "Detalles del dispositivo" }, "unassign-devices-from-edge-title": "¿Está seguro de que desea desasignar {count, plural, =1 {1 dispositivo} other {# dispositivos} }?", - "unassign-devices-from-edge-text": "Después de la confirmación, todos los dispositivos seleccionados quedarán sin asignar y el Edge no podrá acceder a ellos." + "unassign-devices-from-edge-text": "Después de la confirmación, todos los dispositivos seleccionados quedarán sin asignar y el Edge no podrá acceder a ellos.", + "time": "Hora", + "connectivity": { + "check-connectivity": "Probar conectividad", + "device-created-check-connectivity": "Dispositivo creado. Prueba de conectividad.", + "loading-check-connectivity-command": "Cargando comandos...", + "use-following-instructions": "Usa las siguientes instrucciones para enviar telemetrías usando comandos shell", + "execute-following-command": "Ejecuta el comando siguietne", + "install-curl-windows": "Desde Windows 10 b17063, el comando cURL está disponible por defecto", + "install-curl-macos": "Desde Mac OS X 10.2 6C115 (Jaguar), el comando cURL está disponible por defecto", + "install-mqtt-windows": "Usa las instrucciones para descargar, instalar y configurar mosquitto_pub", + "install-coap-client": "Usa las instrucciones para descargar, instalar y configurar coap-client", + "install-necessary-client-tools": "Instala las herramientas de cliente", + "mqtts-x509-command": "Usa la documentación para conectar el dispositivo vía MQTT con autorización X509", + "coaps-x509-command": "Usa la documentación para conectar el dispositivo vía CoAP sobre DTLS con autorización X509", + "snmp-command": "Usa la siguiente documentación para conectar el dispositivo vía SNMP.", + "sparkplug-command": "Usa la siguiente documentación para conectar el dispositivo vía MQTT Sparkplug.", + "lwm2m-command": "Usa la siguiente documentación para conectar el dispositivo vía LWM2M." + } }, "asset-profile": { "asset-profile": "Perfil de activo", @@ -1398,8 +1501,6 @@ "delete": "Borrar perfil de dispositivo", "copyId": "Copiar ID de perfil", "name-max-length": "El nombre debe ser menor de 256", - "new-device-profile-name": "Nombre de perfil", - "new-device-profile-name-required": "Se requiere nombre de perfil.", "name": "Nombre", "name-required": "Se requiere nombre.", "type": "Tipo de perfil", @@ -1418,6 +1519,7 @@ "transport-type-lwm2m-hint": "Transporte LWM2M", "transport-type-snmp": "SNMP", "transport-type-snmp-hint": "Configuración transporte SNMP", + "transport-type-http": "HTTP", "description": "Descripción", "default": "Defecto", "profile-configuration": "Configuración de perfil", @@ -1516,9 +1618,7 @@ "condition-duration-value-required": "Se requiere valor de duración.", "condition-duration-time-unit-required": "Se requiere una unidad de tiempo.", "advanced-settings": "Ajustes avanzados", - "alarm-rule-details": "Detalles", - "alarm-rule-details-hint": "Ayuda: usa ${nombredeClave} para sustituir los valores de atributos o telemetrías usadas en la condición de la regla de alarma.", - "add-alarm-rule-details": "Añadir detalles", + "alarm-rule-additional-info-hint": "Ayuda: usa ${nombredeClave} para sustituir los valores de atributos o telemetrías usadas en la condición de la regla de alarma.", "alarm-rule-mobile-dashboard": "Panel Móvil", "alarm-rule-mobile-dashboard-hint": "Usado por la aplicación móvil como panel de detalle de alarmas", "alarm-rule-no-mobile-dashboard": "No hay panel seleccionado", @@ -1527,7 +1627,6 @@ "alarm-rule-relation-types-list-hint": "Si no está seleccionado 'propagar relaciones', las alarmas serán propagadas sin filtrar por relación.", "propagate-alarm-to-owner": "Propagar alarma al propietario de la entidad (Cliente o Administrador)", "propagate-alarm-to-tenant": "Propagar alarma a Administrador", - "alarm-details": "Detalles de alarma", "alarm-rule-condition": "Condiciones de regla de alarma", "enter-alarm-rule-condition-prompt": "Por favor, añade una condición de alarma", "edit-alarm-rule-condition": "Editar condición de alarma", @@ -1789,7 +1888,9 @@ } }, "dialog": { - "close": "Cerrar diálogo" + "close": "Cerrar diálogo", + "error-message-title": "Mensaje de error:", + "error-details-title": "Detalles de error" }, "direction": { "column": "Columna", @@ -1850,6 +1951,8 @@ "make-private-edge-text": "Después de la confirmación, el Edge y todos sus datos se harán privados y otros no podrán acceder a ellos", "import": "Importar Edge", "install-connect-instructions": "Instrucciones de instalación y conexión", + "install-connect-instructions-edge-created": "Edge creado! Revisa las instrucciones de instalación y conexión", + "loading-edge-instructions": "Cargando instrucciones edge...", "label": "Etiqueta", "load-entity-error": "Entidad no encontrada. No se pudo cargar la información", "assign-new-edge": "Asignar nuevo Edge", @@ -1860,11 +1963,11 @@ "edge-secret": "Secreto del Edge", "copy-edge-secret": "Copiar secreto del Edge", "edge-secret-copied-message": "El secreto de Edge se ha copiado al portapapeles", - "edge-assets": "Gestionar activos de Edges", - "edge-devices": "Gestionar dispositivos de Edge", - "edge-entity-views": "Gestionar vistas de entidad de Edge", - "edge-dashboards": "Administrar paneles de Edge", - "edge-rulechains": "Cadenas de reglas de Edge", + "manage-assets": "Gestionar activos", + "manage-devices": "Gestionar dispositivos", + "manage-entity-views": "Gestionar vistas de entidad", + "manage-dashboards": "Administrar paneles", + "manage-rulechains": "Administrar cadenas de reglas", "assets": "Activos de Edge", "devices": "Dispositivos de Edge", "entity-views": "Vistas de entidad de Edge", @@ -1901,11 +2004,15 @@ "type-rule-chain-metadata": "Metadatos de Cadena de Reglas", "type-edge": "Edge", "type-user": "Usuario", + "type-tenant": "Propietario", + "type-tenant-profile": "Perfil de Propietario", "type-customer": "Cliente", "type-relation": "Relación", "type-widgets-bundle": "Paquete de Widgets", "type-widgets-type": "Tipos de Widgets", "type-admin-settings": "Ajustes de Administración", + "type-ota-package": "Paquete OTA", + "type-queue": "Cola", "action-type-added": "Añadido", "action-type-deleted": "Borrado", "action-type-updated": "Actualizado", @@ -1921,6 +2028,8 @@ "action-type-rpc-call": "Llamada RPC", "action-type-alarm-ack": "ACK Alarma", "action-type-alarm-clear": "Borrado de Alarma", + "action-type-alarm-assigned": "Alarma Asignada", + "action-type-alarm-unassigned": "Alarma Desasignada", "action-type-assigned-to-edge": "Asignado a Edge", "action-type-unassigned-from-edge": "Desasignado de Edge", "action-type-credentials-request": "Obtención de Credenciales", @@ -1937,6 +2046,7 @@ "entities-count": "Nº de entidades", "alarms-count": "Nº de alarmas", "aliases": "Alias de entidad", + "aliases-short": "Alias", "entity-alias": "Alias de entidad", "unable-delete-entity-alias-title": "No ha sido posible eliminar el alias de entidad", "unable-delete-entity-alias-text": "El alias de entidad '{{entityAlias}}' no puede ser eliminado ya que se esta usando por los siguientes widgets:
{{widgetsList}}", @@ -1952,6 +2062,7 @@ "entity-types": "Tipos de entidades", "entity-type-list": "Lista de tipos de entidad", "any-entity": "Cualquier entdad", + "add-entity-type": "Añadir tipo de entidad", "enter-entity-type": "Introducir tipo de entidad", "no-entities-matching": "No se han encontrado entidades que coincidan con '{{entity}}' .", "no-entity-types-matching": "No se han encontrado tipos de entidad que coincidan con '{{entityType}}' .", @@ -1959,7 +2070,7 @@ "help-text": "Usar el símbolo '%' de acuerdo a las necesidades: '%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", "use-entity-name-filter": "Usar filtro", "entity-list-empty": "No hay entidades seleccionadas.", - "entity-type-list-empty": "No hay tipos de entidad seleccionados.", + "entity-type-list-required": "Al menos debe seleccionar un tipo de entidad", "entity-name-filter-required": "Filtro de nombre de entidad requerido.", "entity-name-filter-no-entity-matched": "No hay entidades que comiencen con '{{entity}}' .", "all-subtypes": "Todos", @@ -1967,6 +2078,7 @@ "no-aliases-found": "No se han encontrado alias.", "no-alias-matching": "'{{alias}}' no encontrado.", "create-new-alias": "Crear nuevo alias!", + "create-new": "Crear nuevo", "key": "Clave", "key-name": "Nombre de clave", "no-keys-found": "No se han encontrado claves.", @@ -1980,6 +2092,7 @@ "device-name-starts-with": "Dispositivos cuyos nombres comiencen por '{{prefix}}'", "type-device-profile": "Perfil de dispositivo", "type-device-profiles": "Perfiles de dispositivo", + "clear-selected-profiles": "Borrar perfiles seleccionados", "list-of-device-profiles": "{ count, plural, =1 {un perfil} other {Lista de # perfiles} }", "device-profile-name-starts-with": "Perfiles cuyo nombre empiece por '{{prefix}}'", "type-asset-profile": "Perfil de activo", @@ -2041,6 +2154,9 @@ "type-widgets-bundle": "Paquete de widgets", "type-widgets-bundles": "Paquetes de widgets", "list-of-widgets-bundles": "{ count, plural, =1 {Un paquete de widget} other {Lista de # paquetes de widgets} }", + "type-widget-type": "Tipo de widget", + "type-widget-types": "Tipos de widget", + "list-of-widget-types": "{ count, plural, =1 {Un tipo de widget} other {Lista de # tipos de widgets} }", "search": "Buscar entidades", "selected-entities": "{ count, plural, =1 {1 entidad} other {# entidades} } seleccionadas", "entity-label": "Etiqueta de entidad", @@ -2059,10 +2175,16 @@ "type-rpc": "RPC", "type-queue": "Cola", "type-notification": "Notificación", - "type-notification-rule": "Regla de notificaciones", - "type-notification-target": "Destinatario de notificaciones", + "type-notification-rule": "Regla de notificación", + "type-notification-rules": "Reglas de notificación", + "list-of-notification-rules": "{ count, plural, =1 {Una regla de notificación} other {Lista de # reglas de notificación} }", + "type-notification-target": "Destinatario de notificación", + "type-notification-targets": "Destinatarios de notificación", + "list-of-notification-targets": "{ count, plural, =1 {Un destinatario} other {Lista de # destinatarios} }", "type-notification-request": "Solicitud de notificaciones", - "type-notification-template": "Plantilla de notificaciones" + "type-notification-template": "Plantilla de notificaciones", + "type-notification-templates": "Plantillas de notificación", + "list-of-notification-templates": "{ count, plural, =1 {Una plantilla de notificación} other {Lista de # plantillas de notificación} }" }, "entity-field": { "created-time": "Hora de creación", @@ -2421,6 +2543,7 @@ "add-filter-prompt": "Por favos, añadir filtro", "no-filter-matching": "'{{filter}}' no encontrado.", "create-new-filter": "Crear un filtro nuevo!", + "create-new": "Crear nuevo", "filter-required": "Se requiere filtro.", "operation": { "operation": "Operación", @@ -2508,38 +2631,151 @@ }, "gateway": { "add-entry": "Añadir configuración", + "advanced": "Avanzado", + "checking-device-activity": "Probando actividad de dispositivo", + "command": "Comandos Docker", + "command-copied-message": "Se han copiado los comandos al portapapeles", + "configuration": "Configuración", "connector-add": "Añadir conector", "connector-enabled": "Activar conector", "connector-name": "Nombre conector", "connector-name-required": "Se requiere nombre conector.", "connector-type": "Tipo conector", "connector-type-required": "Se requiere tipo conector.", - "connectors": "Configuración de conectores", + "connectors": "Conectores", + "connectors-config": "Configuración de conectores", + "connectors-table-enabled": "Enabled", + "connectors-table-name": "Nombre", + "connectors-table-type": "Tipo", + "connectors-table-status": "Estado", + "connectors-table-actions": "Acciones", + "connectors-table-key": "Clave", + "connectors-table-class": "Clase", + "rpc-command-send": "Enviar", + "rpc-command-result": "Resultado", + "rpc-command-edit-params": "Editar parametros", + "gateway-configuration": "Configuración General", "create-new-gateway": "Crear un gateway nuevo", "create-new-gateway-text": "Crear un nuevo gateway con el nombre: '{{gatewayName}}'?", + "created-time": "Hora de creación", + "configuration-delete-dialog-header": "Las configuraciones se borrarán", + "configuration-delete-dialog-body": "Sólo es posible desactivar la configuración remota, si hay acceso físico al gateway. Se borrarán todas las configuraciones previas.

\nPara desactivar la configuración, introduce el nombre del gateway aquí", + "configuration-delete-dialog-input": "Nombre Gateway", + "configuration-delete-dialog-input-required": "Se requiere nombre de gateway", + "configuration-delete-dialog-confirm": "Desactivar", "delete": "Borrar configuración", "download-tip": "Descargar fichero de configuración", + "drop-file": "Arrastra un fichero o", "gateway": "Gateway", "gateway-exists": "Ya existe un dispositivo con el mismo nombre.", "gateway-name": "Nombre de Gateway", "gateway-name-required": "Se requiere un nombre de gateway.", "gateway-saved": "Configuración de gateway grabada satisfactoriamente.", + "grpc": "GRPC", + "grpc-keep-alive-timeout": "Timeout Keep alive (en ms)", + "grpc-keep-alive-timeout-required": "Se requiere Timeout Keep alive", + "grpc-keep-alive-timeout-min": "El valor no puede ser menor de 1", + "grpc-keep-alive-timeout-pattern": "El valor no es válido", + "grpc-keep-alive": "Keep alive (en ms)", + "grpc-keep-alive-required": "Se requiere keep alive", + "grpc-keep-alive-min": "El valor no puede ser menor de 1", + "grpc-keep-alive-pattern": "El valor keep alive no es válido", + "grpc-min-time-between-pings": "Tiempo mínimo entre pings (en ms)", + "grpc-min-time-between-pings-required": "Se requiere tiempo mínimo entre pings", + "grpc-min-time-between-pings-min": "El valor no puede ser menor de 1", + "grpc-min-time-between-pings-pattern": "El valor de tiempo mínimo entre pings no es válido", + "grpc-min-ping-interval-without-data": "Intervalo mínimo sin datos (en ms)", + "grpc-min-ping-interval-without-data-required": "Se requiere intervalo", + "grpc-min-ping-interval-without-data-min": "El valor no puede ser menor de 1", + "grpc-min-ping-interval-without-data-pattern": "El valor de intervalo no es válido", + "grpc-max-pings-without-data": "Intervalo máximo sin datos", + "grpc-max-pings-without-data-required": "Se requiere intervalo", + "grpc-max-pings-without-data-min": "El valor no puede ser menor de 1", + "grpc-max-pings-without-data-pattern": "El valor de intervalo no es válido", + "inactivity-check-period-seconds": "Periodo de control de inactividad (en segundos)", + "inactivity-check-period-seconds-required": "Se requiere periodo", + "inactivity-check-period-seconds-min": "El valor no puede ser menor de 1", + "inactivity-check-period-seconds-pattern": "El valor del periodo no es válido", + "inactivity-timeout-seconds": "Timeout de inactividad (en segundos)", + "inactivity-timeout-seconds-required": "Se requiere timeout de inactividad", + "inactivity-timeout-seconds-min": "El valor no puede ser menor de 1", + "inactivity-timeout-seconds-pattern": "El valor de inactividad no es válido", "json-parse": "JSON no válido.", "json-required": "El campo no puede estar vacío.", + "logs": { + "logs": "Registros", + "days": "días", + "hours": "horas", + "minutes": "minutos", + "seconds": "segundos", + "date-format": "Formato de fecha", + "date-format-required": "Se requiere formato de fecha", + "log-format": "Formato de registro", + "log-type": "Tipo de registro", + "log-format-required": "Se requiere tipo de registro", + "remote": "Registro remoto", + "remote-logs": "Registro remoto", + "local": "Registro local", + "level": "Nivel de registro", + "file-path": "Ruta de fichero", + "file-path-required": "Se requiere ruta de fichero", + "saving-period": "Periodo de guardado de registros", + "saving-period-min": "El periodo no puede ser menor que 1", + "saving-period-required": "Se requiere periodo de guardado", + "backup-count": "Número de backups", + "backup-count-min": "El número de backups no puede ser menor que 1", + "backup-count-required": "Se requiere número de backups" + }, + "min-pack-send-delay": "Tiempo de espera, envío de paquetes (en ms)", + "min-pack-send-delay-required": "Se requiere tiempo de espera", + "min-pack-send-delay-min": "El tiempo de espera no puede ser menor que 0", "no-connectors": "No hay conectores", "no-data": "No hay configuraciones", "no-gateway-found": "No se ha encontrado ningún gateway.", "no-gateway-matching": " '{{item}}' no encontrado.", "path-logs": "Ruta a los archivos de log", "path-logs-required": "Ruta requerida.", + "permit-without-calls": "Permitir Keep alive si llamadas", "remote": "Configuración remota", "remote-logging-level": "Nivel de logging", "remove-entry": "Borrar configuración", + "remote-shell": "Consola remota", + "remote-configuration": "Configuración remota", + "other": "otros", "save-tip": "Grabar fichero de configuración", "security-type": "Tipo de seguridad", "security-types": { - "access-token": "Token de acceso", - "tls": "TLS" + "access-token": "Tóken de acceso", + "username-password": "Usuario y contraseña", + "tls": "TLS", + "tls-access-token": "TLS + Tóken de acceso", + "tls-private-key": "TLS + Clave privada" + }, + "server-port": "Puerto del servidor", + "statistics": { + "statistic": "Estadística", + "statistics": "Estadísticas", + "statistic-commands-empty": "No hay estadísticas", + "commands": "Comandos", + "send-period": "Periodo de envío de estadísticas (en segundos)", + "send-period-required": "Se requiere periodo de envío", + "send-period-min": "El periodo de envío no puede ser menor de 60", + "send-period-pattern": "El periodo de envío no es válido", + "check-connectors-configuration": "Revisar configuración de conectores (en segundos)", + "check-connectors-configuration-required": "Se requiere un valor", + "check-connectors-configuration-min": "El valor no puede ser menor de 1", + "check-connectors-configuration-pattern": "La configuración no es válida", + "add": "Añadir comando", + "timeout": "Timeout", + "timeout-ms": "Timeout (en ms)", + "timeout-required": "Se requiere timeout", + "timeout-min": "El timeout no puede ser menor de 1", + "timeout-pattern": "El timeout no es válido", + "attribute-name": "Nombre de atributo", + "attribute-name-required": "Se requiere nombre de atributo", + "command": "Comando", + "command-required": "Se requiere comando", + "remove": "Borrar comando" }, "storage": "Grabación", "storage-max-file-records": "Número máximo de registros en fichero", @@ -2551,6 +2787,16 @@ "storage-max-records-min": "El número mínimo es 1.", "storage-max-records-pattern": "Número no válido.", "storage-max-records-required": "Se requiere número.", + "storage-read-record-count": "Leer número de entradas en almacén", + "storage-read-record-count-min": "El número mínimo de entradas es 1.", + "storage-read-record-count-pattern": "El número no es válido.", + "storage-read-record-count-required": "Se requiere número de entradas.", + "storage-max-read-record-count": "Número máximo de entradas en el almacén", + "storage-max-read-record-count-min": "El número mínimo es 1.", + "storage-max-read-record-count-pattern": "El número no es válido", + "storage-max-read-record-count-required": "Se requiere número máximo de entradas.", + "storage-data-folder-path": "Ruta de carpeta de datos", + "storage-data-folder-path-required": "Se requiere ruta.", "storage-pack-size": "Tamaño máximo de eventos", "storage-pack-size-min": "El número mínimo es 1.", "storage-pack-size-pattern": "Número no válido.", @@ -2559,10 +2805,12 @@ "storage-path-required": "Se requiere ruta de almacén.", "storage-type": "Tipo de almacén", "storage-types": { - "file-storage": "Almacén fichero", - "memory-storage": "Almacén en memoria" + "file-storage": "Almacén en fichero", + "memory-storage": "Almacén en memoria", + "sqlite": "SQLITE" }, "thingsboard": "ThingsBoard", + "general": "General", "thingsboard-host": "Host ThingsBoard", "thingsboard-host-required": "Se requiere Host.", "thingsboard-port": "Puerto ThingsBoard", @@ -2575,10 +2823,61 @@ "title-connectors-json": "Configuración conector {{typeName}}", "tls-path-ca-certificate": "Ruta al certificado CA en el gateway", "tls-path-client-certificate": "Ruta al certificado cliente en el gateway", + "messages-ttl-check-in-hours": "Comprobación de TTL de mensajes en horas", + "messages-ttl-check-in-hours-required": "Campo requerido.", + "messages-ttl-check-in-hours-min": "El mínimo es 1.", + "messages-ttl-check-in-hours-pattern": "El número no es válido.", + "messages-ttl-in-days": "TTL (Time to live) de mensages en días", + "messages-ttl-in-days-required": "Se requiere TTL de mensajes.", + "messages-ttl-in-days-min": "El número mínimo es 1.", + "messages-ttl-in-days-pattern": "El número no es válido.", + "mqtt-qos": "QoS", + "mqtt-qos-required": "Se requiere QoS", + "mqtt-qos-range": "El rango de valores es desde 0 a 1", "tls-path-private-key": "Ruta a la clave privada en el gateway", "toggle-fullscreen": "Pantalla completa fullscreen", "transformer-json-config": "Configuración JSON*", - "update-config": "Añadir/actualizar configuración JSON" + "update-config": "Añadir/actualizar configuración JSON", + "hints": { + "remote-configuration": "Habilita la administración y configuración remota del gateway", + "remote-shell": "Habilita el control remoto del sistema operativo del gateway desde el widget terminal remoto", + "host": "Hostname o dirección IP del servidor Thingsboard", + "port": "Puerto del servicio MQTT en el servidor Thingsboard", + "token": "Access token para el gateway", + "client-id": "ID de cliente MQTT para el gateway", + "username": "Usuario MQTT para el gateway", + "password": "Contraseña MQTT para el gateway", + "ca-cert": "Ruta al fichero del certificado CA", + "date-form": "Formato de fecha en los mensajes de registro", + "data-folder": "Ruta a la carpeta que contendrá los datos (Relativa o absoluta)", + "log-format": "Formato de mensajes en registro", + "remote-log": "Habilita el registro remoto y la posterior lectura desde el gateway", + "backup-count": "Si el contaje de copias de seguridad es mayor que 0, cuando se realice una renovación, no se conservan más que los archivos de recuento de copias de seguridad, los más antíguos se eliminarán", + "storage": "Provee la configuración para el grabado de datos entrantes antes de que se envíen a la plataforma", + "max-file-count": "Número máximo de ficheros que se crearán", + "max-read-count": "Númeo máximo de mensajes a obtener desde el disco y enviados a la plataforma", + "max-records": "Número máximo de registros que se guardarán en un solo fichero", + "read-record-count": "Número de mensages a obtener desde el almacenamiento y enviados a la plataforma", + "max-records-count": "Número máximo de datos en almacenamiento antes de enviar a la plataforma", + "ttl-check-hour": "Con qué frecuencia el gateway comprobará si los datos están obsoletos", + "ttl-messages-day": "Número máximo de días para la retención de datos en el almacén", + "commands": "Comandos para recoger estadísticas adicionales", + "attribute": "Clave de telemetría para estadísticas", + "timeout": "Timeout para la ejecución de comandos", + "command": "El resultado de la ejecución del comando, se usará como valor para la telemetría", + "check-device-activity": "Habilita la monitorización de cada uno de los dispositivos conectados", + "inactivity-timeout": "Tiempo tras que el gateway desconectará el dispositivo", + "inactivity-period": "Periodo de monitorización de actividad en el dispositivo", + "minimal-pack-delay": "Tiempo de espera entre envío de paquetes de mensajes (Un valor muy bajo, resultará en un aumento de uso de la CPU en el gateway)", + "qos": "Quality of Service en los mensajes MQTT (0 - at most once, 1 - at least once)", + "server-port": "Puerto de red en el cual el servidor GRPC escuchará conexiones entrantes.", + "grpc-keep-alive-timeout": "Tiempo máximo, el cual el servidor esperara un ping keepalive antes de considerar la conexión terminada.", + "grpc-keep-alive": "Duración entre dos pings keepalive cuando no haya llamada RPC activa.", + "grpc-min-time-between-pings": "Mínimo tiempo que el servidor debe esperar entre envíos de mensajes de ping", + "grpc-max-pings-without-data": "Número máximo de pings keepalive que el servidor puede enviar sin recibir ningún dato antes de considerar la conexión terminada.", + "grpc-min-ping-interval-without-data": "Mínimo tiempo que el servidor debe esperar entre envíos de ping keepalive cuando no haya ningún dato en envío o recepción.", + "permit-without-calls": "Permitir al servidor mantener la conexión GRPC abierta, cuando no haya llamadas RPC activas." + } }, "grid": { "delete-item-title": "¿Quieres eliminar este item?", @@ -2610,8 +2909,10 @@ "browse-files": "Navegar ficheros" }, "image-input": { - "drop-image-or": "Arrastrar y soltar una imagen o", "drop-images-or": "Arrastrar y soltar imagenes o", + "drag-and-drop": "Arrastrar y soltar", + "or": "O", + "browse": "Navegar", "no-images": "No hay imágenes seleccionadas", "images": "imágenes" }, @@ -2662,6 +2963,12 @@ "lwm2m-server-secret-key": "Clave secreta servidor LwM2M", "lwm2m-server-public-key-id": "Clave pública servidor LwM2M" }, + "snmp": { + "host": "Host SNMP", + "port": "Puerto SNMP", + "version": "Versión SNMP (v1, v2c or v3)", + "community-string": "String de SNMP community" + }, "isgateway": "Es Gateway", "activity-time-from-gateway-device": "Fecha de actividad desde el dispositivo gateway", "description": "Descripción", @@ -2698,6 +3005,7 @@ }, "layout": { "layout": "Diseño", + "layouts": "Diseños", "manage": "Administrar diseños", "settings": "Ajustes de diseño", "color": "Color", @@ -2723,6 +3031,12 @@ "legend": { "direction": "Dirección de la leyenda", "position": "Posición de la leyenda", + "show-values": "Mostrar valores", + "min-option": "Min", + "max-option": "Max", + "average-option": "Media", + "total-option": "Total", + "latest-option": "Últimos", "sort-legend": "Ordenar claves en leyenda", "show-max": "Mostrar valor máximo", "show-min": "Mostrar valor mínimo", @@ -2779,6 +3093,35 @@ "backup-code-auth-description": "Por favor, introduce el código de backup.", "backup-code-auth-placeholder": "Código de Backup" }, + "signup": { + "firstname": "Nombre", + "lastname": "Apellido", + "email": "Correo electrónico", + "signup": "Registrarse", + "create-password": "Crear una contraseña", + "repeat-password": "Repite tu contraseña", + "have-account": "¿Ya tienes una cuenta?", + "signin": "Iniciar sesión", + "no-captcha-message": "Debes confirmar que no eres un robot", + "password-length-message": "Tu contraseña debe tener al menos 6 caracteres", + "email-verification": "Verificación de correo electrónico", + "email-verification-message": "Se envió un correo electrónico con detalles de verificación a la dirección de correo electrónico especificada. Por favor, sigue las instrucciones proporcionadas en el correo electrónico para completar tu proceso de registro. Nota: si no has visto el correo electrónico durante un tiempo, por favor revisa tu carpeta de 'spam' o intenta reenviar el correo electrónico haciendo clic en el botón 'Reenviar'.", + "account-activation-title": "Activación de cuenta", + "account-activated": "¡Cuenta activada con éxito!", + "account-activated-text": "¡Felicidades! Tu cuenta ha sido activada.", + "resend": "Reenviar", + "inactive-user-exists-title": "Ya existe un usuario inactivo", + "inactive-user-exists-text": "Ya hay un usuario registrado con dirección de correo electrónico no verificada. Haz clic en el botón 'Reenviar' si deseas reenviar el correo electrónico de verificación.", + "activating-account": "Activando cuenta...", + "activating-account-text": "Tu cuenta se está activando actualmente. Por favor espera...", + "accept-privacy-policy": "Aceptar la Política de Privacidad", + "accept": "Aceptar", + "privacy-policy": "Política de Privacidad", + "accept-privacy-policy-message": "Debes aceptar nuestra Política de Privacidad", + "recaptcha-title": "reCAPTCHA", + "terms-of-use": "Términos de Uso", + "accept-terms-of-use-message": "Debes aceptar nuestros Términos de Uso" + }, "markdown": { "edit": "Editar", "preview": "Previsualizar", @@ -2804,6 +3147,7 @@ "api-feature-hint": "Si el campo está vacío, la activación se aplicará a todas las características API", "api-usage-trigger-settings": "Ajustes de activación de uso API", "new-platform-version-trigger-settings": "Ajustes de activación de nueva versión de plataforma", + "rate-limits-trigger-settings": "Ajustes de límites excedidos", "at-least-one-should-be-selected": "Se debe seleccionar por lo menos uno", "basic-settings": "Ajustes básicos", "button-text": "Texto del botón", @@ -2844,6 +3188,8 @@ "email-preview": "Vista previa de notificación eMail", "slack": "Slack", "slack-preview": "Vista previa de notificación Slack", + "microsoft-teams": "Microsoft Teams", + "microsoft-teams-preview": "Vista previa de notificación por Microsoft Teams", "sms": "SMS", "sms-preview": "Vista previa de notificación SMS", "web": "Web", @@ -2881,6 +3227,7 @@ "dashboard": "Abrir panel", "link": "Abrir enlace URL" }, + "loading-notifications": "Cargando notificaciones...", "management": "Administración de notificaciones", "mark-all-as-read": "Marcar todas como leídas", "mark-as-read": "Marcar como leída", @@ -2921,20 +3268,20 @@ "only-rule-chain-lifecycle-failures": "Sólo fallos de ciclo de vida en cadena de reglas", "only-rule-node-lifecycle-failures": "Sólo fallos de ciclo de vida en nodos de reglas", "platform-users": "Usuarios de la plataforma", + "rate-limits": "Límites de tasa", + "rate-limits-hint": "Si el campo está vacío, el activador se aplicará a todos los límites de tasa", "recipient": "Destinatario", "recipient-group": "Grupo de destinatarios", "recipient-type": { "affected-tenant-administrators": "Administradores afectados", "affected-user": "Usuario afectado", - "affected-user-hint": "Sugerencia en usuario afectado", "all-users": "Todos los usuarios", "customer-users": "Usuarios del cliente", "system-administrators": "Administradores del sistema", "tenant-administrators": "Administradores de propietarios", "user-filters": "Filtro de usuarios", "user-list": "Lista de usuarios", - "users-entity-owner": "Usuarios que sean propietarios de la entidad", - "users-entity-owner-hint": "Sugerencia en usuarios propietarios de la entidad" + "users-entity-owner": "Usuarios que sean propietarios de la entidad" }, "recipients": "Destinatarios", "notification-recipients": "Notificaciones / Destinatarios", @@ -2954,6 +3301,8 @@ "rule-engine-filter": "Filtro de motor de reglas", "rule-name": "Nombre de regla", "rule-name-required": "Se requiere nombre", + "rule-disable": "Desactivar regla de notificación", + "rule-enable": "Activar regla de notificación", "rule-node-filter": "Filtro de regla", "rules": "Reglas", "notification-rules": "Notificaciones / Reglas", @@ -2997,13 +3346,15 @@ "general": "General", "rule-engine-lifecycle-event": "Evento de ciclo de vida en motor de reglas", "rule-node": "Nodo de regla", - "new-platform-version": "Nueva versión de plataforma" + "new-platform-version": "Nueva versión de plataforma", + "rate-limits": "Se excedieron los límites de tasa" }, "templates": "Plantillas", "notification-templates": "Notificaciones / Plantillas", "tenant-profiles-list-rule-hint": "Si el campo está vacío, el disparo se aplicará a todos los perfiles de administradores propietarios", "tenants-list-rule-hint": "Si el campo está vacío, el disparo se aplicará a todos los administradores propietarios", "threshold": "Umbral", + "theme-color": "Color del tema", "time": "Hora", "track-rule-node-events": "Hacer seguimiento de eventos en nodos de reglas", "trigger": { @@ -3016,6 +3367,7 @@ "entity-action": "Actiones de entidades", "rule-engine-lifecycle-event": "Evento de ciclo de vida en Motor de reglas", "new-platform-version": "Nueva versión de plataforma", + "rate-limits": "Se excedieron los límites de tasa", "trigger": "Disparo", "trigger-required": "Se requiere disparo" }, @@ -3024,7 +3376,21 @@ "updated": "Actualizado", "use-template": "Usar plantilla", "view-all": "Ver todos", - "warning": "Alerta" + "warning": "Alerta", + "webhook-url": "URL Webhook", + "webhook-url-required": "Se requiere URL Webhook", + "channel-name": "Nombre del canal", + "channel-name-required": "Se requiere nombre del canal", + "settings": { + "notification-settings": "Ajustes de notificaciones", + "reset-all": "Restablecer ajustes", + "reset-all-title": "Estás seguro de borrar el formulario?", + "reset-all-text": "Atención, tras la confirmación, el formulario de ajustes se restablecerá a los valores por defecto.", + "type": "Tipo", + "enable-all": "Activar todos", + "disable-all": "Desactivar todos", + "delivery-not-configured": "No hay configurado un método de entrega" + } }, "ota-update": { "add": "Añadir paquete", @@ -3113,7 +3479,14 @@ }, "security": { "security": "Seguridad", - "general-settings": "Ajusters generales de seguridad", + "general-settings": "Ajustes generales de seguridad", + "access-token": "Tóken de acceso", + "access-token-required": "Tóken de acceso requerido.", + "clientId": "ID cliente", + "clientId-required": "Se requiere ID cliente", + "username": "Usuario", + "username-required": "Se requiere usuario", + "ca-cert": "Certificado CA", "2fa": { "2fa": "Autenticación de doble factor (2FA)", "2fa-description": "La autenticación de doble factor proteje tu cuenta de accesos no autorizados. Lo único que tienes que hacer es entrar un código de seguridad al hacer login.", @@ -3186,6 +3559,7 @@ "relation": { "relations": "Relaciones", "direction": "Dirección", + "clear-relation-type": "Borrar tipo de relación", "search-direction": { "FROM": "Desde", "TO": "Hacia" @@ -3219,7 +3593,8 @@ "delete-from-relation-text": "Atención, tras la confirmación la entidad actual no estará relacionada con la entidad '{{entityName}}'.", "delete-from-relations-title": "¿Quieres eliminar { count, plural, =1 {1 relación} other {# relaciones} }?", "delete-from-relations-text": "Atención, tras la confirmación todas las relaciones seleccionadas se eliminarán y sus entidades correspondientes no estarán relacionadas con sus entidades correspondientes.", - "remove-relation-filter": "Quitar filtro de relación", + "remove-relation-filter": "Borrar filtro de relación", + "remove-filter": "Borrar filtro", "add-relation-filter": "Añadir filtro de relación", "any-relation": "Cualquier relación", "relation-filters": "Filtro de relación", @@ -3229,6 +3604,7 @@ }, "resource": { "add": "Añadir Recurso", + "all-types": "Todos", "copyId": "Copiar Id de recurso", "delete": "Borrar recurso", "delete-resource-text": "Atención, tras la confirmación el recurso será irrecuperable.", @@ -3254,7 +3630,13 @@ "system": "Sistema", "title": "Título", "title-required": "Título requerido.", - "title-max-length": "El título debe ser menor de 256" + "title-max-length": "El título debe ser menor de 256", + "type": { + "jks": "JKS", + "js-module": "Módulo JS", + "lwm2m-model": "Modelo LWM2M", + "pkcs-12": "PKCS #12" + } }, "rulechain": { "rulechain": "Cadena de Regla", @@ -3297,7 +3679,6 @@ "search": "Buscar cadenas de reglas", "selected-rulechains": "{ count, plural, =1 {1 cadena de reglas} other {# cadenas de reglas} } seleccionadas", "open-rulechain": "Abrir cadena de reglas", - "assign-new-rulechain": "Asignar nueva cadena de reglas", "edge-template-root": "Raíz de plantilla", "assign-to-edge": "Asignar a Edge", "edge-rulechain": "Cadena de reglas de Edge", @@ -3325,12 +3706,13 @@ "events": "Eventos", "search": "Buscar nodos", "open-node-library": "Abrir librería de nodos", + "close-node-library": "Cerrar librería de nodos", "add": "Añadir nodo de reglas", "name": "Nombre", "name-required": "El nombre es requerido.", "name-max-length": "Nombre debe ser menor de 256", "type": "Tipo", - "description": "Descripción", + "rule-node-description": "Descripción de nodo de reglas", "delete": "Eliminar nodo de reglas", "select-all-objects": "Seleccionar todos los nodos y conexiones", "deselect-all-objects": "Deshacer selección de todos los nodos y conexiones", @@ -3378,7 +3760,7 @@ "ui-resources-load-error": "Error al cargar los recursos de configuración UI.", "invalid-target-rulechain": "No se puede resolver la cadena de reglas objetivo!", "test-script-function": "Probar Script de función", - "script-lang-java-script": "Java Script", + "script-lang-java-script": "JavaScript", "script-lang-tbel": "TBEL", "message": "Mensaje", "message-type": "Tipo de mensaje", @@ -3389,7 +3771,8 @@ "output": "Salida", "test": "Test", "help": "Ayuda", - "reset-debug-mode": "Restablecer el modo de depuración en todos los nodos" + "reset-debug-mode": "Restablecer el modo de depuración en todos los nodos", + "test-with-this-message": "{{test}} con este mensaje" }, "timezone": { "timezone": "Zona Horaria", @@ -3402,7 +3785,7 @@ "queue-name": "Cola", "no-queues-found": "No se encontraron colas.", "no-queues-matching": "No se encontraron colas que coincidan con '{{queue}}'.", - "select_name": "Selecciona el nombre de la cola", + "select-name": "Selecciona el nombre de la cola", "name": "Nombre Cola", "name-required": "Necesario especificar el nombre de cola", "name-unique": "El nombre de cola ya existe!", @@ -3463,6 +3846,8 @@ "description": "Descripción", "description-hint": "Este texto se mostrará en la descripción de la cola, en lugar de la estrategia seleccionada", "alt-description": "Estrategia de envío: {{submitStrategy}}, Estrategia de procesamiento: {{processingStrategy}}", + "custom-properties": "Propiedades personalizadas", + "custom-properties-hint": "Propiedades de creación de tema en colas personalizadas, p.e. 'retention.ms:604800000;retention.bytes:1048576000'", "strategies": { "sequential-by-originator-label": "Secuencial por iniciador", "sequential-by-originator-hint": "El nuevo mensaje por ejemplo dispositivo A, no se enviará hasta que el mensaje anterior del dispositivo A sea admitido/procesado", @@ -3642,6 +4027,7 @@ "max-emails": "Nº Máximo de emails (0 - sin límite)", "max-emails-required": "Nº Máximo de emails requerido.", "max-emails-range": "Nº Máximo de emails no puede ser negativo", + "sms-enabled": "SMS Activados", "max-sms": "Nº Máximo de mensajes SMS (0 - sin límite)", "max-sms-required": "Nº Máximo de mensajes SMS requerido.", "max-sms-range": "Nº Máximo de mensajes SMS no puede ser negativo", @@ -3752,16 +4138,24 @@ "days": "Días" }, "timewindow": { + "timewindow": "Ventana de tiempo", "years": "{ years, plural, =1 { año } other {# años } }", + "years-short": "{{ years }}a", "months": "{ months, plural, =1 { mes } other {# meses } }", + "months-short": "{{ months }}M", "weeks": "{ weeks, plural, =1 { semana } other {# semanas } }", + "weeks-short": "{{ weeks }}sem", "days": "{ days, plural, =1 { día } other {# días } }", + "days-short": "{{ days }}d", "hours": "{ hours, plural, =0 { horas } =1 {1 hora } other {# horas } }", "hr": "{{ hr }} hr", + "hr-short": "{{ hr }}h", "minutes": "{ minutes, plural, =0 { minutos } =1 {1 minuto } other {# minutos } }", "min": "{{ min }} min", + "min-short": "{{ min }}m", "seconds": "{ seconds, plural, =0 { segundos } =1 {1 segundo } other {# segundos } }", "sec": "{{ sec }} sec", + "sec-short": "{{ sec }}s", "short": { "days": "{ days, plural, =1 {1 día } other {# días } }", "hours": "{ hours, plural, =1 {1 hora } other {# horas } }", @@ -3780,7 +4174,436 @@ "hide": "Ocultar", "interval": "Intervalo", "just-now": "Ahora mismo", - "ago": "hace" + "just-now-lower": "ahora mismo", + "ago": "hace", + "style": "Estilo de ventana de tiempo", + "icon": "Icono", + "icon-position": "Posición de icono", + "icon-position-left": "Izquierda", + "icon-position-right": "Derecha", + "font": "Fuente", + "color": "Color", + "displayTypePrefix": "Mostrar prefijo en tiempo real/histórico", + "preview": "Previsualizar" + }, + "unit": { + "millimeter": "Milímetro", + "centimeter": "Centímetro", + "angstrom": "Angstrom", + "nanometer": "Nanómetro", + "micrometer": "Micrómetro", + "meter": "Metro", + "kilometer": "Kilómetro", + "inch": "Pulgada", + "foot": "Pie", + "yard": "Yarda", + "mile": "Milla", + "nautical-mile": "Milla Náutica", + "astronomical-unit": "Unidad Astronómica", + "reciprocal-metre": "Metro recíproco", + "meter-per-meter": "Metro por metro", + "steradian": "Estereorradián", + "thou": "Tú", + "barleycorn": "Grano de cebada", + "hand": "Hand", + "chain": "Cadena", + "furlong": "Furlong", + "league": "League", + "fathom": "Fathom", + "cable": "Cable", + "link": "Enlace", + "rod": "Barra", + "nanogram": "Nanogramo", + "microgram": "Microgramo", + "milligram": "Miligramo", + "gram": "Gramo", + "kilogram": "Kilogramo", + "tonne": "Tonelada", + "ounce": "Onza", + "pound": "Libra", + "stone": "Piedra", + "hundredweight-count": "Hundredweight", + "short-tons": "Tonelada corta", + "dalton": "Dalton", + "grain": "Grano", + "drachm": "Dracma", + "quarter": "Cuarto", + "slug": "Lingote", + "carat": "Quilate", + "cubic-millimeter": "Milímetro Cúbico", + "cubic-centimeter": "Centímetro Cúbico", + "cubic-meter": "Metro Cúbico/s", + "cubic-kilometer": "Kilómetro Cúbico", + "microliter": "Microlitro", + "milliliter": "Mililitro", + "liter": "Litro", + "hectoliter": "Hectolitro", + "cubic-inch": "Pulgada Cúbica", + "cubic-foot": "Pié Cúbico", + "cubic-yard": "Yarda Cúbica", + "fluid-ounce": "Onza Fluida", + "pint": "Pinta", + "quart": "Cuarta", + "gallon": "Galón", + "oil-barrels": "Barriles de Aceite", + "cubic-meter-per-kilogram": "Metro Cúbico por Kilogramo", + "gill": "Cuarta parte de una pinta", + "hogshead": "Tonel", + "teaspoon": "Cucharilla", + "tablespoon": "Cucharada", + "cup": "Taza", + "celsius": "Celsius", + "kelvin": "Kelvin", + "rankine": "Rankine", + "fahrenheit": "Fahrenheit", + "percent": "%", + "meter-per-second": "Metros por Segundo", + "kilometer-per-hour": "Kilómetros por Hora", + "foot-per-second": "Pies por Segundo", + "mile-per-hour": "Millas por Hora", + "knot": "Nudo", + "millimeters-per-minute": "Milímetros por minuto", + "kilometer-per-hour-squared": "Kilómetros al cuadrado por hora", + "foot-per-second-squared": "Pie al cuadrado por segundo", + "pascal": "Pascal", + "kilopascal": "Kilopascale", + "megapascal": "Megapascal", + "gigapascal": "Gigapascal", + "millibar": "Milibar", + "bar": "Bar", + "kilobar": "Kilobar", + "newton": "Newton", + "newton-meter": "Newton metro", + "foot-pounds": "Libras-pie", + "inch-pounds": "Pulgadas-pie", + "newton-per-meter": "Newton por metro", + "atmospheres": "Atmósferas", + "pounds-per-square-inch": "Libras por pulgada cuadrada", + "torr": "Torr", + "inches-of-mercury": "Pulgadas de mercurio", + "pascal-per-square-meter": "Pascales por metro cuadrado", + "pound-per-square-inch": "Pie por pulgada cuadrada", + "newton-per-square-meter": "Newtons por metro cuadrado", + "kilogram-force-per-square-meter": "Kilogramo de fuerza por metro cuadrado", + "pascal-per-square-centimeter": "Pascales por centímetro cuadrado", + "ton-force-per-square-inch": "Tonelada de fuerza por pulgada cuadrada", + "kilonewton-per-square-meter": "Kilonewtons por metro cuadrado", + "newton-per-square-millimeter": "Newton por milímetro cuadrado", + + "microjoule": "Microjulio", + "millijoule": "Milijulio", + "joule": "Julio", + "kilojoule": "Kilojulio", + "megajoule": "Megajulio", + "gigajoule": "Gigajulio", + "watt-hour": "Watios-hora", + "kilowatt-hour": "Kilowatios-hora", + "electron-volts": "Electronvoltios", + "joules-per-coulomb": "Julios por Coulomb", + "british-thermal-unit": "British Thermal Units (BTU)", + "foot-pound": "Foot-pound", + "calorie": "Caloría", + "small-calorie": "Caloría-pequeña", + "kilocalorie": "Kilocaloría", + "joule-per-kelvin": "Julios por Kelvin", + "joule-per-kilogram-kelvin": "Julio por Kilogramo-Kelvin", + "joule-per-kilogram": "Julio por Kilogramo", + "watt-per-meter-kelvin": "Watios por metro Kelvin", + "joule-per-cubic-meter": "Julios por metro cúbico", + "therm": "Termia", + "electric-dipole-moment": "Momento dipolar eléctrico", + "magnetic-dipole-moment": "Momento dipolar magnético", + "debye": "Debyé", + "coulomb-per-square-meter-per-volt": "Culombio por metro cuadrado por Voltio", + "milliwatt": "Miliwatio", + "microwatt": "Microwatio", + "watt": "Watio", + "kilowatt": "Kilowatio", + "megawatt": "Megawatio", + "gigawatt": "Gigawatio", + "metric-horsepower": "Caballos métricos", + "milliwatt-per-square-centimeter": "Miliwatios por centímetro cuadrado", + "watt-per-square-centimeter": "Watios por centímetro cuadrado", + "kilowatt-per-square-centimeter": "Kilowatios por centímetro cuadrado", + "milliwatt-per-square-meter": "Miliwatios por metro cuadrado", + "watt-per-square-meter": "Watios por metro cuadrado", + "kilowatt-per-square-meter": "Kilowatios por metro cuadrado", + "watt-per-square-inch": "Watios por pulgada cuadrada", + "kilowatt-per-square-inch": "Kilowatios por pulgada cuadrada", + "horsepower": "Caballos", + "btu-per-hour": "BTU/h", + "coulomb": "Culombio", + "millicoulomb": "Miliculombios", + "microcoulomb": "Microculombios", + "picocoulomb": "Picoculombios", + "coulomb-per-meter": "Culombio por metro", + "coulomb-per-cubic-meter": "Culombio por metro cúbico", + "coulomb-per-square-meter": "Culombio por metro cuadrado", + "square-millimeter": "Milímetro cuadrado", + "square-centimeter": "Centímetro cuadrado", + "square-meter": "Metro Cuadrado", + "hectare": "Hectárea", + "square-kilometer": "Kilómetro Cuadrado", + "square-inch": "Pulgada Cuadrada", + "square-foot": "Pie Cuadrada", + "square-yard": "Yarda Cuadrada", + "acre": "Hectárea", + "square-mile": "Milla Cuadrada", + "are": "Área", + "barn": "Barnio", + "circular-inch": "Pulgada circular", + "milliampere-hour": "Miliamperios hora", + "milliampere-hour-tags": "intensidad eléctrica, flujo de corriente, carga eléctrica, capacidad actual, flujo de electricidad, flujo eléctrico, miliamperio hora, miliamperios hora, mAh", + "ampere-hours": "Amperios hora", + "ampere-hours-tags": "intensidad eléctrica, flujo de corriente, carga eléctrica, capacidad actual, flujo de electricidad, flujo eléctrico, ampere, amperio hora, Ah", + "kiloampere-hours": "Kiloamperios hora", + "kiloampere-hours-tags": "intensidad eléctrica, flujo de corriente, carga eléctrica, capacidad actual, flujo de electricidad, flujo eléctrico, kiloamperio hora, kiloampreios hora, kAh", + "nanoampere": "Nanoamperio", + "nanoampere-tags": "intensidad, amperios, nanoamperio, nA", + "picoampere": "Picoamperio", + "picoampere-tags": "intensidad, amperios, picoamperio, pA", + "microampere": "Microamperio", + "microampere-tags": "intensidad eléctrica, microamperio, microamperios, μA", + "milliampere": "Miliamperio", + "milliampere-tags": "intensidad eléctrica, miliamperio, miliamperios, mA", + "ampere": "Amperio", + "ampere-tags": "intensidad eléctrica, flujo de corriente, flujo de electricidad, flujo eléctrico, amperio, amperios, amperaje, A", + "kiloamperes": "Kiloamperios", + "kiloamperes-tags": "intensidad eléctrica, flujo de corriente, kiloamperios, kA", + "microampere-per-square-centimeter": "Microamperio por centímetro cuadrado", + "microampere-per-square-centimeter-tags": "Densidad de intensidad, microamperio por centímetro cuadrado, µA/cm²", + "ampere-per-square-meter": "Amperio por metro cuadrado", + "ampere-per-square-meter-tags": "Densidad de intensidad, intensidad por área de unidad, amperio por metro cuadrado, A/m²", + "ampere-per-meter": "Amperio por Metro", + "ampere-per-meter-tags": "fuerza del campo magnético, intensidad del campo magnético, amperios por metro, A/m", + "oersted": "Oersted", + "oersted-tags": "campo magnético, oersted, Oe", + "bohr-magneton": "Magneton de Bohr", + "bohr-magneton-tags": "física atómica, momento magnético, magnetón de bohr, μB", + "ampere-meter-squared": "Amperios metro al cuadrado", + "ampere-meter-squared-tags": "momento magnético, momento dipolar, ampero metro cuadrado, A·m²", + "ampere-meter": "Amperio Metro", + "ampere-meter-tags": "campo magnético, bucle de corriente, ampero metro, A·m", + "nanovolt": "Nanovoltio", + "picovolt": "Picovoltio", + "millivolts": "Milivoltios", + "microvolts": "Microvoltios", + "volt": "Voltio", + "kilovolts": "Kilovoltios", + "dbmV": "dBmV", + "volt-meter": "Voltio-Metro", + "kilovolt-meter": "Kilovoltio-Metro", + "megavolt-meter": "Megavoltio-Metro", + "microvolt-meter": "Microvoltio-Metro", + "millivolt-meter": "Milivoltio-Metro", + "nanovolt-meter": "Nanovoltio-Metro", + "ohm": "Ohmio", + "microohm": "Microohmio", + "milliohm": "Miliohmio", + "kilohm": "Kiloohmio", + "megohm": "Megaohmio", + "gigohm": "Gigaohmio", + "hertz": "Hertzio", + "kilohertz": "Kilohertzio", + "megahertz": "Megahertzio", + "gigahertz": "Gigahertzio", + "rpm": "Revoluciones por minuto", + "candela-per-square-meter": "Candela por metro cuadrado", + "candela": "Candela", + "lumen": "Lúmenes", + "lux": "Lux", + "foot-candle": "Candela", + "lumen-per-square-meter": "Lúmenes por metro cuadrado", + "lux-second": "Lux segundo", + "lumen-second": "Lumen segundo", + "lumens-per-watt": "Lumens por watio", + "absorbance": "Absorbancia", + "mole": "Mole", + "nanomole": "Nanomole", + "micromole": "MicroMole", + "millimole": "Milimole", + "kilomole": "Kilomole", + "mole-per-cubic-meter": "Mole por metro cúbico", + "rssi": "RSSI", + "ppm": "Partes Por Millón", + "ppb": "Partes Por Billión", + "micrograms-per-cubic-meter": "Microgramos por metro cúbico", + "aqi": "AQI", + "gram-per-cubic-meter": "Gramos por metro cúbico", + "gram-per-kilogram": "Humedad Específica", + "millimeters-per-second": "Milimetros por segundo", + "neper": "Neper", + "bel": "Belio", + "decibel": "Decibelio", + "meters-per-second-squared": "Metros por segundo al cuadrado", + "becquerel": "Becquerel", + "curie": "Curie", + "gray": "Gray", + "sievert": "Sievert", + "roentgen": "Roentgen", + "cps": "Cuentas por segundo", + "rad": "Rad", + "rem": "Rem", + "dps": "Desintegraciones por segundo", + "rutherford": "Rutherford", + "coulombs-per-kilogram": "Culombios por kilogramo", + "becquerels-per-cubic-meter": "Becquerels por metro cúbico", + "curies-per-liter": "Curies por litro", + "becquerels-per-second": "Becquerels por segundo", + "curies-per-second": "Curies por segundo", + "gy-per-second": "Gray por segundo", + "watt-per-steradian": "Watio por estereorradián", + "watt-per-square-metre-steradian": "Watios por metro cuadrado de estereorradián", + "ph-level": "Nivel de pH", + "turbidity": "Turbidez", + "mg-per-liter": "Miligramos por litro", + "microsiemens-per-centimeter": "Microsiemens por centímetro", + "millisiemens-per-meter": "Milisiemens por metro", + "siemens-per-meter": "Siemens por metro", + "kilogram-per-cubic-meter": "Kilogramos por metro cúbico", + "gram-per-cubic-centimeter": "Gramos por centímetro cúbico", + "kilogram-per-square-meter": "Kilogramo por metro cuadrado", + "milligram-per-milliliter": "Miligramo por mililitro", + "pound-per-cubic-foot": "Libra por pie cúbico", + "ounces-per-cubic-inch": "Onzas por pulgada cúbica", + "tons-per-cubic-yard": "Toneladas por yarda cúbica", + "particle-density": "Densidad de partícula", + "kilometers-per-liter": "Kilometros por litro", + "miles-per-gallon": "Millas por galón", + "liters-per-100-km": "Litros por 100 km", + "gallons-per-mile": "Galones por milla", + "liters-per-hour": "Litros por hora", + "gallons-per-hour": "Galones por hora", + "beats-per-minute": "Pulsaciones por minuto", + "millimeters-of-mercury": "Milímetros de mercurio", + "milligrams-per-deciliter": "Miligramos por decilitro", + "g-force": "Fuerza-G", + "kilonewton": "Kilonewton", + "kilogram-force": "Kilogramo-Fuerza", + "pound-force": "Pondio-Fuerza", + "kilopound-force": "Kilopondio-Fuerza", + "dyne": "Dina", + "poundal": "Poundal", + "kip": "Kip", + "gal": "Gal", + "gravity": "Gravedad", + "hectopascal": "Hectopascal", + "atmosphere": "Atmósfera", + "millibars": "Milibares", + "inch-of-mercury": "Pulgada de mercurio", + "richter-scale": "Escala de Richter", + "second": "Segundo", + "minute": "Minuto", + "hour": "Hora", + "day": "Día", + "week": "Semana", + "month": "Mes", + "year": "Año", + "cubic-foot-per-minute": "Pie cúbico por minuto", + "cubic-meters-per-hour": "Metros cúbicos por hora", + "cubic-meters-per-second": "Metros cúbicos por segundo", + "liter-per-second": "Litros por segundo", + "liter-per-minute": "Litros por minuto", + "gallons-per-minute": "Galones por minuto", + "cubic-foot-per-second": "Pie cúbico por segundo", + "milliliters-per-minute": "Mililitros por minuto", + "bit": "Bit", + "byte": "Byte", + "kilobyte": "Kilobyte", + "megabyte": "Megabyte", + "gigabyte": "Gigabyte", + "terabyte": "Terabyte", + "petabyte": "Petabyte", + "exabyte": "Exabyte", + "zettabyte": "Zettabyte", + "yottabyte": "Yottabyte", + "bit-per-second": "Bit por segundo", + "kilobit-per-second": "Kilobit por segundo", + "megabit-per-second": "Megabit por segundo", + "gigabit-per-second": "Gigabit por segundo", + "terabit-per-second": "Terabit por segundo", + "byte-per-second": "Byte por segundo", + "kilobyte-per-second": "Kilobyte por segundo", + "megabyte-per-second": "Megabyte por segundo", + "gigabyte-per-second": "Gigabyte por segundo", + "degree": "Grado", + "radian": "Radian", + "gradian": "Gradian", + "mil": "Mil", + "revolution": "Revolución", + "siemens": "Siemens", + "millisiemens": "Milisiemens", + "microsiemens": "Microsiemens", + "kilosiemens": "Kilosiemens", + "megasiemens": "Megasiemens", + "gigasiemens": "Gigasiemens", + "farad": "Faradio", + "millifarad": "Milifaradio", + "microfarad": "Microfaradio", + "nanofarad": "Nanofaradio", + "picofarad": "Picofaradio", + "kilofarad": "Kilofaradio", + "megafarad": "Megafaradio", + "gigafarad": "Gigafaradio", + "terfarad": "Terfaradio", + "farad-per-meter": "Faradio por metro", + "tesla": "Tesla", + "gauss": "Gauss", + "kilogauss": "Kilogauss", + "millitesla": "Militesla", + "microtesla": "Microtesla", + "nanotesla": "Nanotesla", + "kilotesla": "Kilotesla", + "megatesla": "Megatesla", + "millitesla-square-meters": "militesla por metro cuadrado", + "gamma": "Gamma", + "lambda": "Lambda", + "square-meter-per-second": "Metros cuadrados por segundo", + "square-centimeter-per-second": "Centímetros cuadrados por segundo", + "stoke": "Stoke", + "centistokes": "Centistokes", + "square-foot-per-second": "Pie cuadrado por segundo", + "square-inch-per-second": "Pulgada cuadrada por segundo", + "pascal-second": "Pascal-segundo", + "centipoise": "Centipoise", + "poise": "Poise", + "reynolds": "Reynolds", + "pound-per-foot-hour": "Libra por pie-hora", + "newton-second-per-square-meter": "Newton segundo por metro cuadrado", + "dyne-second-per-square-centimeter": "Dyne segundo por centímetro cuadrado", + "kilogram-per-meter-second": "Kilogramo por metro-segundo", + "tesla-square-meters": "Tesla metros cuadrados", + "maxwell": "Maxwell", + "tesla-per-meter": "Tesla por metro", + "gauss-per-centimeter": "Gauss por centímetro", + "weber": "Weber", + "microweber": "Microweber", + "milliweber": "Miliweber", + "gauss-square-centimeter": "Gauss-centímetro cuadrado", + "kilogauss-square-centimeter": "Kilogauss-centímetro cuadrado", + "henry": "Henrio", + "millihenry": "Milihenrio", + "microhenry": "Microhenrio", + "nanohenry": "Nanohenrio", + "henry-per-meter": "Henrio por metro", + "tesla-meter-per-ampere": "Tesla metro por amperio", + "gauss-per-oersted": "Gauss por Oersted", + "kilogram-per-mole": "Kilogramo por mole", + "gram-per-mole": "Gramo por mole", + "milligram-per-mole": "Miligramo por mole", + "joule-per-mole": "Julios por mole", + "joule-per-mole-kelvin": "Julios por mole-Kelvin", + "millivolts-per-meter": "Milivoltios por metro", + "volts-per-meter": "Voltios por metro", + "kilovolts-per-meter": "Kilovoltios por metro", + "radian-per-second": "Radianes por segundo", + "radian-per-second-squared": "Radianes por segundo al cuadrado", + "revolutions-per-minute-per-second": "Aceleración angular", + "revolutions-per-minute-per-second-squared": "Aceleración angular", + "deg-per-second": "Grados/s", + "degrees-brix": "Grados Brix", + "katal": "Katal", + "katal-per-cubic-metre": "Katal por metro cúbico" }, "user": { "user": "Usuario", @@ -3915,7 +4738,7 @@ "created": "{{created}} creadas", "updated": "{{updated}} actualizadas", "deleted": "{{deleted}} borradas", - "remove-other-entities-confirm-text": "Atención! Esta acción borrará permanentemente todas las entidades actuales
no presentes en la versión a restaurar.

Escribe remove other entities para confirmar.", + "remove-other-entities-confirm-text": "Atención! Esta acción borrará permanentemente todas las entidades actuales
no presentes en la versión a restaurar.

Escribe \"remove other entities\" para confirmar.", "auto-commit-to-branch": "auto-publicar a la rama {{ branch }}", "default-create-entity-version-name": "{{entityName}} actualización", "sync-strategy-merge-hint": "Crea o actualiza las entidades seleccionadas en el repositorio. Las demás entidades no serán modificadas.", @@ -3930,12 +4753,21 @@ "widget-bundle": "Paquetes de Widgets", "all-bundles": "Todos los paquetes", "select-widgets-bundle": "Seleccionar paquete de widgets", + "widgets": "Widgets", + "all-widgets": "Todos los widgets", + "widget": "Widget", + "select-widget": "Seleccionar widget", + "no-widgets-matching": "No se han encontrado widgets que coincidan con '{{entity}}'.", + "no-widgets": "No hay widgets todavía", + "no-widgets-text": "No se han encontrado widgets", "management": "Gestión de Widgets", "editor": "Editor de widgets", "widget-type-not-found": "Problema al cargar la configuración del widget.
Probablemente asociado\n El tipo de widget fue eliminado.", "widget-type-load-error": "El widget no pudo ser cargado debido a estos errores:", "remove": "Eliminar widget", + "delete": "Borrar widget", "edit": "Editar widget", + "edit-widget-type": "Editar tipo de widget", "remove-widget-title": "¿Eliminar el widget '{{widgetTitle}}'?", "remove-widget-text": "Atención, tras la confirmación el widget será eliminado y toda la información relacionada será irrecuperable..", "timeseries": "Series de tiempo", @@ -3951,13 +4783,17 @@ "unable-to-save-widget-error": "Imposible guardar widget! Tiene errores!", "save": "Guardar widget", "saveAs": "Guardar widget como", + "move": "Mover widget", "save-widget-type-as": "Guardar tipo de widget como", "save-widget-type-as-text": "Por favor, ingrese un nuevo titulo y/o seleccione un paquete de destino.", "toggle-fullscreen": "Cambiar a pantalla completa", "run": "Ejecutar widget", + "widget-title": "Título del widget", "title": "Título", "title-required": "Título requerido.", - "type": "Tipo", + "title-max-length": "El título debe ser menor de 256", + "system": "Sistema", + "type": "Tipo de widget", "resources": "Recursos", "resource-url": "URL JavaScript/CSS", "resource-is-module": "Es módulo", @@ -3971,19 +4807,32 @@ "latest-datakey-settings-schema": "Esquema de últimos valores", "widget-settings": "Ajustes de widget", "description": "Descripción", + "tags": "Etiquetas", "image-preview": "Imagen previsualización", "settings-form-selector": "Selector formulario de ajustes", "data-key-settings-form-selector": "Selector formulario de ajustes claves de datos", + "all": "Todos", + "actual": "Actuales", + "deprecated": "Obsoletos", + "has-basic-mode": "Tienen modo básico", + "basic-mode-form-selector": "Selector formulario en modo básico", + "basic-mode": "Básico", + "advanced-mode": "Avanzado", "latest-data-key-settings-form-selector": "Selector formulario de últimos valores", "javascript": "Javascript", "js": "JS", - "remove-widget-type-title": "¿Eliminar el tipo del widget '{{widgetName}}'?", - "remove-widget-type-text": "Atención, tras la confirmación el tipo será eliminado y la información relacionada será irrecuperable.", - "remove-widget-type": "Eliminar tipo de widget.", - "add-widget-type": "Agregar nuevo tipo de widget", - "widget-type-load-failed-error": "Error al cargar el tipo de widget!", + "widget-types": "Tipos de Widget", + "delete-widget-title": "¿Eliminar de widget '{{widgetName}}'?", + "delete-widget-text": "Atención, tras la confirmación será eliminado y la información relacionada será irrecuperable.", + "delete-widgets-title": "¿Eliminar { count, plural, =1 {1 de widget} other {# de widgets} }?", + "delete-widgets-text": "Atención, tras la confirmación los de widget seleccionados serán eliminados y la información relacionada será irrecuperable.", + "delete-widget": "Borrar de widget", "widget-template-load-failed-error": "Error al cargar la plantilla del widget!", + "details": "Detalles", + "widget-details": "Detalles de widget", "add": "Agregar Widget", + "search-widget": "Buscar de widget", + "selected-widgets": "{ count, plural, =1 {1 de widget} other {# de widgets} } seleccionados", "undo": "Deshacer cambios", "export": "Exportar widget", "no-data": "No hay datos para mostrar en widget", @@ -3991,7 +4840,8 @@ "alarm-data-overflow": "El widget muestra alarmas para {{allowedEntities}} entidades (máximo permitido) de {{totalEntities}} entidades", "search": "Buscar widget", "filter": "Filtro tipo de widget", - "loading-widgets": "Cargando widgets..." + "loading-widgets": "Cargando widgets...", + "widget-template-error": "Plantilla HTML inválida." }, "widget-action": { "header-button": "Botón de encabezado widget", @@ -4000,6 +4850,9 @@ "open-dashboard": "Navegar hacia otro panel", "custom": "Acción personalizada", "custom-pretty": "Acción personalizada (con plantilla HTML)", + "custom-pretty-error-title": "Diálogo de error personalizado", + "custom-pretty-template-error": "Plantilla de diálogo de error inválida.", + "custom-pretty-controller-error": "Ha ocurrido un error mientras se evaluaba la función.", "mobile-action": "Acción en dispositivo móvil", "target-dashboard-state": "Estado de panel de destino", "target-dashboard-state-required": "Se requiere estado de panel de destino", @@ -4050,6 +4903,7 @@ "widgets-bundle": { "current": "Paquete actual", "widgets-bundles": "Paquete de Widgets", + "widgets-bundle-widgets": "Widgets del paquete de widgets", "add": "Agregar paquete de widgets", "delete": "Eliminar paquete de widgets", "title": "Título", @@ -4072,6 +4926,7 @@ "system": "Widget de Sistema", "import": "Importar paquete de widgets", "export": "Exportar paquete de widgets", + "export-widgets-bundle-widgets-prompt": "Incluir de widget en los datos exportados (de lo contrario, solo se exportarán los FQN de los tipos de widget a los que hace referencia)", "export-failed-error": "Imposible exportar paquete de widgets: {{error}}", "create-new-widgets-bundle": "Crear nuevo paquete de widgets", "widgets-bundle-file": "Archivo de paquete de widgets", @@ -4085,14 +4940,19 @@ "data": "Datos", "settings": "Ajustes", "advanced": "Avanzado", - "title": "Titulo", + "appearance": "Apariencia", + "widget-card": "Tarjeta de widget", + "mobile": "Móvil", + "title": "Título", "title-tooltip": "Tooltip Título", "general-settings": "Ajustes generales", "display-title": "Mostrar titulo", + "card-title": "Título de la tarjeta", "drop-shadow": "Sombra", "enable-fullscreen": "Habilitar pantalla completa", "background-color": "Color de fondo", "text-color": "Color del texto", + "border-radius": "Radio del borde", "padding": "Relleno", "margin": "Margen", "widget-style": "Estilo de widget", @@ -4104,13 +4964,21 @@ "mobile-hide": "Ocultar widget en modo móvil", "desktop-hide": "Ocultar widget en modo escritorio", "units": "Caracter especial a mostrar en el siguiente valor", - "decimals": "Números de dígitos después de la coma", + "units-by-default": "Unidades por defecto", + "decimals": "Decimales", + "decimals-by-default": "Decimales por defecto", + "default-data-key-parameter-hint": "Este parámetro aplica a todos los valores del widget a menos que la configuración de la clave de datos lo anule", + "units-short": "Unidades", + "decimals-short": "Decimales", + "decimals-suffix": "decimales", "timewindow": "Ventana de tiempo", "use-dashboard-timewindow": "Usar ventana de tiempo del Panel", + "use-widget-timewindow": "Usar ventana de tiempo del widget", "display-timewindow": "Mostrar ventana de tiempo", "legend": "Leyenda", "display-legend": "Mostrar leyenda", - "datasources": "Set de datos", + "datasources": "Origen de datos", + "datasource": "Origen de dato", "maximum-datasources": "Un máximo de { count, plural, =1 {1 set de datos es permitido.} other {# set de datos son permitidos} }", "timeseries-key-error": "Se debe especificar al menos un datakey tipo timeseries", "datasource-type": "Tipo", @@ -4139,26 +5007,67 @@ "delete-action-text": "Eliminar la acción de widget con el nombre '{{actionName}}'?", "title-icon": "Título de icono", "display-icon": "Mostrar título de icono", + "card-icon": "Card icon", "icon-color": "Color del icono", "icon-size": "Tamaño del icono", "advanced-settings": "Ajustes avanzados", "data-settings": "Ajustes de datos", + "limits": "Limits", "no-data-display-message": "\"No hay datos que mostrar\" mensaje alternativo", "data-page-size": "Nº Máximo de entidades por origen de datos", - "settings-component-not-found": "Componente de ajustes no encontrado para el selector '{{selector}}'" + "settings-component-not-found": "Componente de ajustes no encontrado para el selector '{{selector}}'", + "preview": "Previsualizar", + "set": "Establecer", + "set-message": "Establecer mensaje", + "advanced-title-style": "Estilo de título avanzado", + "card-style": "Estilo de tarjeta", + "text": "Texto", + "background": "Fondo", + "advanced-widget-style": "Estilo avanzado de widget", + "card-buttons": "Botones de tarjeta", + "show-card-buttons": "Mostrar botones", + "card-border-radius": "Radio del borde", + "card-appearance": "Apariencia", + "color": "Color" }, "widget-type": { "import": "Importar tipo de widget", "export": "Exportar tipo de widget", - "export-failed-error": "Imposible exportar tipo de widget: {{error}}", - "create-new-widget-type": "Crear nuevo tipo de widget", - "widget-type-file": "Archivo de tipo de widget", + "export-failed-error": "Imposible exportar de widget: {{error}}", + "widget-file": "Archivo de widget", "invalid-widget-type-file-error": "No se puede importar tipo de widget: Estructura de datos del tipo de widget es inválida." }, "widgets": { + "background": { + "background": "Fondo", + "background-settings": "Ajustes de fondo", + "background-type-image": "Subir imagen", + "background-type-image-url": "URL imagen", + "background-type-color": "Color sólido", + "image-url": "URL imagen", + "overlay": "Capa superpuesta", + "enable-overlay": "Activar capa", + "blur": "Difuminar", + "preview": "Previsualizar" + }, + "battery-level": { + "layout": "Diseño", + "layout-vertical-solid": "Vertical. Sólido", + "layout-horizontal-solid": "Horizontal. Sólido", + "layout-vertical-divided": "Vertical. Dividido", + "layout-horizontal-divided": "Horizontal. Dividido", + "icon": "Icono", + "value": "Valor", + "auto-scale": "Auto escalar", + "battery-level-color": "Color del nivel de batería", + "battery-shape-color": "Color de la forma", + "battery-level-card-style": "Estilo de tarjeta batería" + }, "chart": { "common-settings": "Ajustes comunes", "enable-stacking-mode": "Activar modo de apilamiento (stacking)", + "selection": "Selección de rango de tiempo", + "enable-selection-mode": "Habilitar modo de selección", "line-shadow-size": "Tamaño de sombra (línea)", "display-smooth-lines": "Mostrar líneas suaves (curvas)", "default-bar-width": "Ancho de barra por defecto para datos no agregados (millisegundos)", @@ -4166,10 +5075,12 @@ "bar-alignment-left": "Izquierda", "bar-alignment-right": "Derecha", "bar-alignment-center": "Centro", + "default-font": "Fuente por defecto", "default-font-size": "Tamaño de fuente por defecto", "default-font-color": "Color de fuente por defecto", "thresholds-line-width": "Ancho de línea por defecto para todos los umbrales", "tooltip-settings": "Ajustes de sugerencias (tooltip)", + "tooltip": "Tooltip (sugerencias)", "show-tooltip": "Mostrar sugerencias", "hover-individual-points": "Hover sobre puntos individuales", "show-cumulative-values": "Mostrar valores acumulados en modo apilado", @@ -4247,6 +5158,7 @@ "add-threshold": "Añadir nuevo umbral", "show-values-for-comparison": "Mostrar valores históricos para su comparación", "comparison-values-label": "Etiqueta de valores históricos", + "comparison-line-color": "Color de línea en comparación", "threshold-settings": "Ajustes de umbrales", "use-as-threshold": "Usar valor de clave como umbral", "threshold-line-width": "Ancho de línea (para umbral)", @@ -4255,6 +5167,7 @@ "radius": "Radio", "inner-radius": "Radio interior", "tilt": "Inclinación", + "common-pie-settings-range-error": "El valor debe estar en el rango de 0 a 1", "stroke-settings": "Ajustes de trazo", "width-pixels": "Ancho (pixels)", "show-labels": "Mostrar etiquetas", @@ -4265,7 +5178,34 @@ "border-color": "Color de borde", "legend-settings": "Ajustes de leyenda", "display-legend": "Mostrar leyenda", - "labels-font-color": "Color de texto en leyenda" + "labels-font-color": "Color de texto en leyenda", + "series": "Series", + "add-series": "Añadir series", + "series-settings": "Ajustes de series", + "remove-series": "Eliminar serie", + "no-series": "No hay series configuradas", + "no-series-error": "Al menos se debe especificar una serie", + "chart-appearance": "Apariencia del gráfico", + "vertical-grid-lines": "Líneas de cuadrícula verticales", + "horizontal-grid-lines": "Líneas de cuadrícula horizontales", + "chart-background": "Fondo del gráfico", + "grid-lines-color": "Color de cuadrícula", + "border": "Borde", + "axis": "Eje", + "vertical-axis": "Eje vertical", + "ticks": "Ticks", + "horizontal-axis": "Eje horizontal" + }, + "color": { + "color-settings": "Ajustes de color", + "color-type-constant": "Constante", + "color-type-range": "Rango", + "color-type-function": "Función", + "color": "Color", + "value-range": "Rango de valor", + "from": "Desde", + "to": "A", + "color-function": "Función de color" }, "dashboard-state": { "dashboard-state-settings": "Ajustes estado de panel", @@ -4381,7 +5321,12 @@ "read-only": "Solo lectura", "events-title": "Título del formulario de eventos", "events-filter": "Filtro de eventos", - "event-key-contains": "La clave de evento contiene..." + "event-key-contains": "La clave de evento contiene...", + "show-connector": "Mostrar para el conector", + "connector-state-param-key": "Clave del parámetro de estado del conector", + "status": "Estado", + "message": "Mensaje", + "created-time": "Hora de creación" }, "gauge": { "default-color": "Color por defecto", @@ -4614,6 +5559,9 @@ "input-fields-alignment": "Alineado de campos de entrada", "input-fields-alignment-column": "Columna (por defecto)", "input-fields-alignment-row": "Fila", + "layout": "Diseño", + "row-gap": "Espacio entre filas en píxeles", + "column-gap": "Espacio entre columnas en píxeles", "latitude-field-required": "Campo latitud requerido", "longitude-field-required": "Campo longitud requerido", "attribute-settings": "Ajustes de atributos", @@ -4662,12 +5610,19 @@ "datakey-value-type-date": "Fecha", "datakey-value-type-time": "Hora", "datakey-value-type-select": "Selección", + "datakey-value-type-color": "Color", "value-is-required": "Valor requerido", "ability-to-edit-attribute": "Posibilidad de editar atributo", "ability-to-edit-attribute-editable": "Editable (por defecto)", "ability-to-edit-attribute-disabled": "Desactivado", "ability-to-edit-attribute-readonly": "Sólo lectura", "disable-on-datakey-name": "Desactivar cuando otra clave de datos sea false (especificar nombre de clave de datos)", + "field-appearance": "Apariencia de los campos", + "appearance-fill": "Relleno", + "appearance-outline": "Contorno", + "subscript-sizing": "Tamaño subscript", + "subscript-sizing-fixed": "Fijo", + "subscript-sizing-dynamic": "Dinámico", "slide-toggle-settings": "Ajustes de deslizador", "slide-toggle-label-position": "Posición de etiqueta", "slide-toggle-label-position-after": "Después", @@ -5071,10 +6026,62 @@ "markdown-css": "Markdown/HTML CSS" }, "simple-card": { + "label": "Etiqueta", "label-position": "Posición etiqueta", "label-position-left": "Izquierda", "label-position-top": "Superior" }, + "value-card": { + "layout": "Diseño", + "layout-square": "Cuadrado", + "layout-vertical": "Vertical", + "layout-centered": "Centrado", + "layout-simplified": "Simplificado", + "layout-horizontal": "Horizontal", + "layout-horizontal-reversed": "Horizontal inverso", + "label": "Etiqueta", + "icon": "Icono", + "value": "Valor", + "date": "Fecha", + "value-card-style": "Estilo de tarjeta valores" + }, + "aggregated-value-card": { + "subtitle": "Subtítulo", + "chart": "Gráfico", + "values": "Valores", + "value-appearance": "Apariencia de valores", + "position": "Posición", + "position-center": "Centro", + "position-right-top": "Arriba a la derecha", + "position-right-bottom": "Abajo a la derecha", + "position-left-top": "Arriba a la izquierda", + "position-left-bottom": "Abajo a la izquierda", + "font": "Fuente", + "color": "Color", + "arrow": "Flecha", + "display-up-down-arrow": "Mostrar flecha Arriba/Abajo", + "add-value": "Añadir valor", + "remove-value": "Quitar valor", + "no-values": "No hay valores configurados", + "aggregation": "Agregación", + "aggregated-value-card-style": "Estilo de tarjeta valores agregados" + }, + "alarm-count": { + "alarm-count-card-style": "Estilo de tarjeta contaje de alarmas" + }, + "entity-count": { + "entity-count-card-style": "Estilo de tarjeta contaje de entidades" + }, + "count": { + "layout": "Diseño", + "layout-column": "Columna", + "layout-row": "Fila", + "label": "Etiqueta", + "icon": "Icono", + "icon-background": "Fondo de icono", + "value": "Valor", + "chevron": "Chevron" + }, "table": { "common-table-settings": "Ajustes comunes en tablas", "enable-search": "Activar búsqueda", @@ -5084,7 +6091,6 @@ "show-empty-space-hidden-action": "Mostrar espacio vacío en lugar de celda oculta", "dont-reserve-space-hidden-action": "No reservar espacio para los botones en celda oculta", "display-timestamp": "Mostrar columna timestamp", - "display-milliseconds": "Mostrar milisegundos", "display-pagination": "Mostrar páginas", "default-page-size": "Tamaño de página por defecto", "use-entity-label-tab-name": "Usar etiqueta de entidad en el nombre de la tabla", @@ -5125,7 +6131,21 @@ "allow-alarms-ack": "Permitir reconocimiento de alarmas", "allow-alarms-clear": "Permitir borrado de alarmas", "display-alarm-activity": "Mostrar actividad de alarmas", - "allow-alarms-assign": "Permitir asignar alarmas" + "allow-alarms-assign": "Permitir asignar alarmas", + "columns": "Columnas", + "column-settings": "Ajustes de columnas", + "remove-column": "Quitar columna", + "add-column": "Añadir columna", + "no-columns": "No hay columnas configuradas", + "columns-to-display": "Columnas a Mostrar", + "table-header": "Encabezado de tabla", + "header-buttons": "Botones encabezado", + "table-buttons": "Botones de tabla", + "pagination": "Paginación", + "rows": "Filas", + "timeseries-column-error": "Se debe especificar al menos una columna tipo timeseries", + "table-tabs": "Pestañas de la tabla", + "show-cell-actions-menu-mobile": "Mostrar menú desplegable de acciones de celda en modo móvil" }, "value-source": { "value-source": "Origen valor", @@ -5136,6 +6156,7 @@ "source-entity-attribute": "Atributo entidad de origen" }, "widget-font": { + "font-settings": "Ajustes de fuente", "font-family": "Familia (font family)", "size": "Tamaño", "relative-font-size": "Tamaño fuente relativo (porcentaje)", @@ -5149,7 +6170,10 @@ "font-weight-bolder": "Negrita+", "font-weight-lighter": "Lighter", "color": "Color", - "shadow-color": "Color de sombra" + "shadow-color": "Color de sombra", + "preview": "Previsualizar", + "line-height": "Altura de línea", + "auto": "Auto" }, "home": { "no-data-available": "No hay datos disponibles" @@ -5346,11 +6370,17 @@ } } }, + "color": { + "color": "Color" + }, "icon": { "icon": "Icono", - "select-icon": "Seleccionar iconos", + "icons": "Iconos", + "select-icon": "Seleccionar icono", "material-icons": "Iconos material-design", - "show-all": "Mostrar todos los iconos" + "show-all": "Mostrar todos los iconos", + "search-icon": "Buscar icono", + "no-icons-found": "No se han encontrado iconos que coincidan con '{{iconSearch}}'" }, "phone-input": { "phone-input-label": "Número de teléfono", @@ -5370,7 +6400,8 @@ "node-selected": "Clic en el nodo seleccionado", "element-click": "Clic en el elemento HTML", "pie-slice-click": "Clic en la rebanada", - "row-double-click": "Doble clic en la fila" + "row-double-click": "Doble clic en la fila", + "card-click": "En click de tarjeta" } }, "paginator" : { diff --git a/ui-ngx/src/assets/locale/locale.constant-fa_IR.json b/ui-ngx/src/assets/locale/locale.constant-fa_IR.json index 705fc6824c..222b40042a 100644 --- a/ui-ngx/src/assets/locale/locale.constant-fa_IR.json +++ b/ui-ngx/src/assets/locale/locale.constant-fa_IR.json @@ -254,6 +254,7 @@ "attributes": "ويژگي ها", "latest-telemetry": "آخرين سنجش", "attributes-scope": "حوزه ويژگي هاي موجودي", + "scope-telemetry": "تله متری", "scope-latest-telemetry": "آخرين سنجش", "scope-client": "ويژگي هاي مشتري", "scope-server": "ويژگي هاي سِروِر", @@ -701,7 +702,6 @@ "name-starts-with": "نام شروع مي شود با", "use-entity-name-filter": "استفاده از فيلتر", "entity-list-empty": ".هيچ موجودي اي انتخاب نشده است", - "entity-type-list-empty": ".هيچ نوع موجودي انتخاب نشده است", "entity-name-filter-required": ".فيلتر نام موجودي مورد نياز است", "entity-name-filter-no-entity-matched": ".شروع شود يافت نشد '{{entity}}' هيچ موجودي که با", "all-subtypes": "همه", @@ -1240,7 +1240,6 @@ "name": "نام", "name-required": ".نام مورد نياز است", "type": "نوع", - "description": "توصيف", "delete": "حذف گره قواعد", "select-all-objects": "انتخاب تمام گره ها و اتصالات", "deselect-all-objects": "لغو انتخاب تمام گره ها و اتصالات", @@ -1446,11 +1445,7 @@ "settings-schema": "طرح تنظيمات", "datakey-settings-schema": "طرح تنظيمات کليد داده", "javascript": "Javascript", - "remove-widget-type-title": "مطمئنيد؟ '{{widgetName}}' از حذف ويجت نوع", - "remove-widget-type-text": ".پس از تأييد، نوع ويجت و تمامي داده هاي مربوطه غير قابل بازيابي مي شوند", - "remove-widget-type": "حذف نوع ويجت", "add-widget-type": "افزودن نوع ويجت جديد", - "widget-type-load-failed-error": "!عدم موفقيت در بارگذاري نوع ويجت", "widget-template-load-failed-error": "!عدم موفقيت در بارگذاري قالب ويجت", "add": "افزودن ويجت", "undo": "برگرداندن تغييرات ويجت", diff --git a/ui-ngx/src/assets/locale/locale.constant-fr_FR.json b/ui-ngx/src/assets/locale/locale.constant-fr_FR.json index edaaabdd83..7e678d9ed9 100644 --- a/ui-ngx/src/assets/locale/locale.constant-fr_FR.json +++ b/ui-ngx/src/assets/locale/locale.constant-fr_FR.json @@ -459,6 +459,7 @@ "next-widget": "Widget suivant", "prev-widget": "Widget précédent", "scope-client": "Attributs du client", + "scope-telemetry": "Télémétrie", "scope-latest-telemetry": "Dernière télémétrie", "scope-server": "Attributs du serveur", "scope-shared": "Attributs partagés", @@ -487,10 +488,6 @@ "email-messages-monthly-activity": "Activité menuselle de courriels", "executions": "Exécutions", "javascript-executions": "Exécutions JavaScript", - "javascript-functions": "Fonctions JavaScript", - "javascript-functions-daily-activity": "Activité hebdomadaire de fonctions JavaScript", - "javascript-functions-hourly-activity": "Activité horaire de fonctions JavaScript", - "javascript-functions-monthly-activity": "Activité mensuelle de fonctions JavaScript", "latest-error": "Dernière erreur", "notifications-email-sms": "Notifications (Coourriel/SMS)", "notifications-hourly-activity": "Activité horaire de notifications", @@ -1053,13 +1050,7 @@ "device-configuration": "Configuration du dipositif", "transport-configuration": "Configuration du transport", "wizard": { - "device-wizard": "Wizard du dispositif", - "device-details": "Détails du dispositif", - "new-device-profile": "Créer un nouveau profil de dispositif", - "existing-device-profile": "Choisissez un profile de dispositif existant", - "specific-configuration": "Configuration spécifique", - "customer-to-assign-device": "Client auquel assigner le dispositif", - "add-credentials": "Ajouter identifiants" + "device-details": "Détails du dispositif" } }, "device-profile": { @@ -1079,8 +1070,6 @@ "delete": "Supprimer le profil de dispositif", "copyId": "Copier l'Identifiant du profil de dispositif", "name-max-length": "La longueur du nom devrait être moins de 256", - "new-device-profile-name": "Nom du profil de dispositif", - "new-device-profile-name-required": "Nom du profil de dispositif est requis.", "name": "Nom", "name-required": "Nom est requis.", "type": "Type de profile", @@ -1301,7 +1290,6 @@ "entity-name-filter-required": "Le filtre de nom d'entité est requis.", "entity-type": "Type d'entité", "entity-type-list": "Liste de types d'entités", - "entity-type-list-empty": "Aucun type d'entité sélectionné.", "entity-types": "Types d'entité", "entity-view-name-starts-with": "Les vues d'entité dont le nom commence par '{{prefix}}'", "key": "Clé", @@ -1878,7 +1866,6 @@ "set-root-rulechain-title": "Voulez-vous vraiment que la chaîne de règles '{{ruleChainName}} soit racine (root) ?", "system": "Système", "assign-rulechains": "Attribuer aux chaînes de règles", - "assign-new-rulechain": "Attribuer une nouvele chaînes de règles", "delete-rulechains": "Supprimer une chaînes de règles", "unassign-rulechain": "Retirer chaîne de règles", "unassign-rulechains": "Retirer chaînes de règles", @@ -1918,7 +1905,6 @@ "delete": "Supprimer le noeud de règle", "delete-selected": "Supprimer les éléments sélectionnés", "delete-selected-objects": "Supprimer les nœuds et les connexions sélectionnés", - "description": "Description", "deselect-all": "Désélectionner tout", "deselect-all-objects": "Désélectionnez tous les nœuds et toutes les connexions", "details": "Détails", @@ -2114,9 +2100,6 @@ "remove-resource": "Supprimer une ressource", "remove-widget-text": "Après la confirmation, le widget et toutes les données associées deviendront irrécupérables.", "remove-widget-title": "Êtes-vous sûr de vouloir supprimer le widget '{{widgetTitle}}'?", - "remove-widget-type": "Supprimer le type de widget", - "remove-widget-type-text": "Après la confirmation, le type de widget et toutes les données associées deviendront irrécupérables.", - "remove-widget-type-title": "Êtes-vous sûr de vouloir supprimer le type de widget '{{widgetName}}'?", "resource-url": "URL JavaScript / CSS", "resources": "Ressources", "rpc": "Widget de contrôle", @@ -2143,7 +2126,6 @@ "widget-saved": "Widget enregistré", "widget-template-load-failed-error": "Impossible de charger le modéle de widget!", "widget-type-load-error": "Le widget n'a pas été chargé à cause des erreurs suivantes:", - "widget-type-load-failed-error": "Impossible de charger le type de widget!", "widget-type-not-found": "Problème de chargement de la configuration du widget.
Le type de widget associé a probablement été supprimé.", "no-data": "Aucune donnée à afficher sur le widget" }, diff --git a/ui-ngx/src/assets/locale/locale.constant-it_IT.json b/ui-ngx/src/assets/locale/locale.constant-it_IT.json index 6d0eae5ee5..f2e1ffa240 100644 --- a/ui-ngx/src/assets/locale/locale.constant-it_IT.json +++ b/ui-ngx/src/assets/locale/locale.constant-it_IT.json @@ -276,6 +276,7 @@ "attributes": "Attributi", "latest-telemetry": "Ultima telemetria", "attributes-scope": "Visibilità attributi entità", + "scope-telemetry": "Telemetria", "scope-latest-telemetry": "Ultima telemetria", "scope-client": "Attributi client", "scope-server": "Attributi server", @@ -730,7 +731,6 @@ "name-starts-with": "Nome inizia per", "use-entity-name-filter": "Usa filtro", "entity-list-empty": "Nessuna entità selezionata.", - "entity-type-list-empty": "Nessun tipo di entità selezionato.", "entity-name-filter-required": "Filtro nome entità obbligatorio.", "entity-name-filter-no-entity-matched": "Nessuna entità che inizia per '{{entity}}' è stata trovata.", "all-subtypes": "Tutte", @@ -1278,7 +1278,6 @@ "name": "Nome", "name-required": "Nome obbligatorio.", "type": "Tipo", - "description": "Descrizione", "delete": "Elimina nodo regola", "select-all-objects": "Seleziona tutti i nodi e le connessioni", "deselect-all-objects": "Deseleziona tutti i nodi e le connessioni", @@ -1489,11 +1488,7 @@ "settings-schema": "Impostazioni schema", "datakey-settings-schema": "Impostazioni Data key schema", "javascript": "Javascript", - "remove-widget-type-title": "Sei sicuro di voler rimuovere il tipo di widget '{{widgetName}}'?", - "remove-widget-type-text": "Dopo la conferma il tipo di widget e tutti i suoi dati non saranno più recuperabili.", - "remove-widget-type": "Rimuovi tipo widget", "add-widget-type": "Aggiungi nuovo tipo widget", - "widget-type-load-failed-error": "Caricamento tipo widget fallito!", "widget-template-load-failed-error": "Caricamento template widget fallito!", "add": "Aggiungi Widget", "undo": "Annulla modifiche widget", diff --git a/ui-ngx/src/assets/locale/locale.constant-ja_JP.json b/ui-ngx/src/assets/locale/locale.constant-ja_JP.json index f63a6681a2..c65928e452 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ja_JP.json +++ b/ui-ngx/src/assets/locale/locale.constant-ja_JP.json @@ -244,6 +244,7 @@ "attributes": "属性", "latest-telemetry": "最新テレメトリ", "attributes-scope": "エンティティ属性のスコープ", + "scope-telemetry": "テレメトリー", "scope-latest-telemetry": "最新テレメトリ", "scope-client": "クライアントの属性", "scope-server": "サーバーの属性", @@ -684,7 +685,6 @@ "name-starts-with": "名前はで始まる", "use-entity-name-filter": "フィルタを使用する", "entity-list-empty": "選択されたエンティティはありません", - "entity-type-list-empty": "エンティティタイプは選択されていません。", "entity-name-filter-required": "エンティティ名フィルタが必要です。", "entity-name-filter-no-entity-matched": "'{{entity}}'発見されました。", "all-subtypes": "すべて", @@ -1331,11 +1331,7 @@ "settings-schema": "設定スキーマ", "datakey-settings-schema": "データキー設定のスキーマ", "javascript": "Javascript", - "remove-widget-type-title": "'{{widgetName}}'?", - "remove-widget-type-text": "確認後、ウィジェットのタイプと関連するすべてのデータは回復不能になります。", - "remove-widget-type": "ウィジェットタイプを削除", "add-widget-type": "新しいウィジェットタイプを追加する", - "widget-type-load-failed-error": "ウィジェットタイプの読み込みに失敗しました!", "widget-template-load-failed-error": "ウィジェットテンプレートを読み込めませんでした!", "add": "ウィジェットを追加", "undo": "ウィジェットの変更を元に戻す", diff --git a/ui-ngx/src/assets/locale/locale.constant-ka_GE.json b/ui-ngx/src/assets/locale/locale.constant-ka_GE.json index a6c5a6576d..28458f0a70 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ka_GE.json +++ b/ui-ngx/src/assets/locale/locale.constant-ka_GE.json @@ -290,6 +290,7 @@ "attributes": "ატრიბუტები", "latest-telemetry": "უახლესი ტელემეტრია", "attributes-scope": "ობიექტის ატრიბუტების ფარგლები", + "scope-telemetry": "ტელემეტრია", "scope-latest-telemetry": "უახლესი ტელემეტრია", "scope-client": "კლიენტის ატრიბუტები", "scope-server": "სერვერის ატრიბუტები", @@ -754,7 +755,6 @@ "name-starts-with": "სახელი იწყება", "use-entity-name-filter": "გამოიყენე ფილტრი", "entity-list-empty": "ობიექტი არ არის არჩეული", - "entity-type-list-empty": "ობიექტის ტიპი არ არის არჩეული", "entity-name-filter-required": "ობიექტის სახელის ფილტრი სავალდებულოა", "entity-name-filter-no-entity-matched": "ობიექტები რომბლებიც იწყება '{{entity}}' -ით არ იქნა ნაპოვნი", "all-subtypes": "ყველა", @@ -1578,11 +1578,7 @@ "datakey-settings-schema": "მონაცემტა გასაღები პარამეტრების სქემა", "javascript": "javascript", "js": "JS", - "remove-widget-type-title": "დარწმუნებული ხართ რომ გსურთ ვიჯეტის ტიპი '{{widgetName}}'?", - "remove-widget-type-text": "დასტურის შემთხვევაში ვიჯეტი და მასთან ასოცირებული მონაცემები დაიკარგება.", - "remove-widget-type": "ვიჯეტის ტიპის ამოღება", "add-widget-type": "ახალი ვიჯეტის ტიპი", - "widget-type-load-failed-error": "ვიჯეტის ტიპის ჩატვირთვა ვერ მოხერხდა!", "widget-template-load-failed-error": "ვიჯეტის შაბლონი ჩატვირთვა ვერ მოხერხდა!", "add": "ვიჯეტის დამატება", "undo": "ვიჯეტის ცვლილების გაუქმება", diff --git a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json index 28c02a410d..09cea16190 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json +++ b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json @@ -408,6 +408,7 @@ "attributes": "속성", "latest-telemetry": "최근 데이터", "attributes-scope": "장치 속성 범위", + "scope-telemetry": "원격 측정", "scope-latest-telemetry": "최근 데이터", "scope-client": "클라이언트 속성", "scope-server": "서버 속성", @@ -446,10 +447,6 @@ "executions": "Executions", "javascript": "JavaScript", "javascript-executions": "JavaScript executions", - "javascript-functions": "JavaScript functions", - "javascript-functions-daily-activity": "JavaScript functions daily activity", - "javascript-functions-hourly-activity": "JavaScript functions hourly activity", - "javascript-functions-monthly-activity": "JavaScript functions monthly activity", "latest-error": "최근 오류", "messages": "메시지", "permanent-failures": "${entityName} Permanent Failures", @@ -913,13 +910,7 @@ "device-configuration": "장치 설정", "transport-configuration": "전송 설정", "wizard": { - "device-wizard": "장치 마법사", - "device-details": "장치 상세 정보", - "new-device-profile": "새로운 장치 프로파일 생성", - "existing-device-profile": "기존 장치 프로파일 선택", - "specific-configuration": "특수 설정", - "customer-to-assign-device": "장치에 할당할 커스터머", - "add-credentials": "크리덴셜 추가" + "device-details": "장치 상세 정보" } }, "device-profile": { @@ -938,8 +929,6 @@ "set-default": "Make device profile default", "delete": "Delete device profile", "copyId": "Copy device profile Id", - "new-device-profile-name": "장치 프로파일 이름", - "new-device-profile-name-required": "Device profile name is required.", "name": "이름", "name-required": "이름을 입력하세요.", "type": "프로파일 유형", @@ -1017,12 +1006,9 @@ "condition-duration-value-required": "Duration value is required.", "condition-duration-time-unit-required": "Time unit is required.", "advanced-settings": "Advanced settings", - "alarm-rule-details": "Details", - "add-alarm-rule-details": "Add details", "propagate-alarm": "Propagate alarm", "alarm-rule-relation-types-list": "Relation types to propagate", "alarm-rule-relation-types-list-hint": "If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.", - "alarm-details": "Alarm details", "alarm-rule-condition": "Alarm rule condition", "enter-alarm-rule-condition-prompt": "Please add alarm rule condition", "edit-alarm-rule-condition": "Edit alarm rule condition", @@ -1111,7 +1097,6 @@ "name-starts-with": "다음으로 시작하는 이름", "use-entity-name-filter": "필터 사용", "entity-list-empty": "아무 개체도 선택되지 않았습니다.", - "entity-type-list-empty": "개체 유형이 선택되지 않았습니다.", "entity-name-filter-required": "개체 이름 필터를 입력하세요.", "entity-name-filter-no-entity-matched": "'{{entity}}'로 시작되는 개체를 찾을 수 없습니다.", "all-subtypes": "모두", @@ -1905,7 +1890,6 @@ "name": "이름", "name-required": "이름을 입력하세요.", "type": "유형", - "description": "설명", "delete": "규칙 노드 삭제", "select-all-objects": "모든 노드와 연결을 선택", "deselect-all-objects": "모든 노드와 연결을 선택 해제", @@ -2229,11 +2213,7 @@ "datakey-settings-schema": "데이터 키 설정 스키마", "javascript": "Javascript", "js": "JS", - "remove-widget-type-title": "'{{widgetName}}' 위젯 타입을 삭제하시겠습니까?", - "remove-widget-type-text": "위젯 타입 및 관련된 모든 데이터를 복구할 수 없으므로 주의하십시오.", - "remove-widget-type": "위젯 타입 삭제", "add-widget-type": "새로운 위젯 타입 추가", - "widget-type-load-failed-error": "위젯 타입을 로드하지 못했습니다!", "widget-template-load-failed-error": "위젯 템플릿을 로드하지 못했습니다!", "add": "위젯 추가", "undo": "위젯 변경사항 취소", diff --git a/ui-ngx/src/assets/locale/locale.constant-lv_LV.json b/ui-ngx/src/assets/locale/locale.constant-lv_LV.json index 645fe007d3..63e7fb6db2 100644 --- a/ui-ngx/src/assets/locale/locale.constant-lv_LV.json +++ b/ui-ngx/src/assets/locale/locale.constant-lv_LV.json @@ -256,6 +256,7 @@ "attributes": "Attribūti", "latest-telemetry": "Jaunākā telemetrija", "attributes-scope": "Vienības atribūtu darbības joma", + "scope-telemetry": "Telemetrija", "scope-latest-telemetry": "Jaunākā telemetrija", "scope-client": "Klientu atribūti", "scope-server": "Servera atribūti", @@ -710,7 +711,6 @@ "name-starts-with": "Nosaukums sākas ar", "use-entity-name-filter": "Lietot filtru", "entity-list-empty": "Nav vienības atlasītas.", - "entity-type-list-empty": "Nav vienības tipi atlasīti.", "entity-name-filter-required": "Vienību nosaukuma filtri ir vajadzīgi.", "entity-name-filter-no-entity-matched": "Nav vienības kas sākas ar '{{entity}}' atrastas.", "all-subtypes": "Visi", @@ -1287,7 +1287,6 @@ "name": "Nosaukums", "name-required": "Nosaukums ir nepieciešams.", "type": "Tips", - "description": "Apraksts", "delete": "Dzēst noteikumu nodi", "select-all-objects": "Atlasīt visas nodes un savienojumus", "deselect-all-objects": "Noņemt visas nodes un savienojumus", @@ -1494,11 +1493,7 @@ "settings-schema": "Iestatījumu shēma", "datakey-settings-schema": "Datu atslēgas iestatījumu shēma", "javascript": "Javascript", - "remove-widget-type-title": "Vai esat pārliecināts, ka vēlaties noņemt logrīka tipu '{{widgetName}}'?", - "remove-widget-type-text": "Pēc apstiprinājuma logrīka tips un tā saistītie dati nebūs atjaunojami.", - "remove-widget-type": "Noņemt logrīka tipu", "add-widget-type": "Pievienot jaunu logrīka tipu", - "widget-type-load-failed-error": "Neveiksme ielādēt logrīka tipu!", "widget-template-load-failed-error": "Neveiksme ielādēt logrīka paraugu!", "add": "Pievienot logrīku", "undo": "Atcelt logrīka izmaiņas", diff --git a/ui-ngx/src/assets/locale/locale.constant-nl_BE.json b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json new file mode 100644 index 0000000000..df3820d497 --- /dev/null +++ b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json @@ -0,0 +1,6951 @@ +{ + "access": { + "unauthorized": "Geen toegang", + "unauthorized-access": "Geen toegang", + "unauthorized-access-text": "Gelieve u zich opnieuw in te loggen.", + "access-forbidden": "Toegang verboden", + "access-forbidden-text": "Je hebt geen toegangsrechten tot deze locatie!
Probeer in te loggen met een andere gebruiker als u toch toegang wilt verkrijgen.", + "refresh-token-expired": "Sessie is verlopen", + "refresh-token-failed": "Kan sessie niet vernieuwen", + "permission-denied": "Toestemming geweigerd", + "permission-denied-text": "U hebt geen toestemming om deze bewerking uit te voeren!" + }, + "action": { + "activate": "Activeren", + "suspend": "Opschorten", + "save": "Opslaan", + "saveAs": "Opslaan als", + "cancel": "Annuleren", + "ok": "OK", + "delete": "Verwijderen", + "add": "Toevoegen", + "yes": "Ja", + "no": "Nee", + "update": "Update", + "remove": "Verwijderen", + "search": "Zoeken", + "clear-search": "Zoekopdracht wissen", + "assign": "Toewijzen", + "unassign": "Toewijzing ongedaan maken", + "share": "Delen", + "make-private": "Privé maken", + "make-public": "Openbaar maken", + "apply": "Toepassen", + "apply-changes": "Wijzigingen toepassen", + "edit-mode": "Bewerk modus", + "enter-edit-mode": "Ga naar de bewerkingsmodus", + "decline-changes": "Wijzigingen weigeren", + "open": "Open", + "close": "Sluiten", + "back": "Terug", + "run": "Uitvoeren", + "sign-in": "Aanmelden", + "edit": "Bewerken", + "view": "Bekijken", + "create": "Aanmaken", + "drag": "Slepen", + "refresh": "Vernieuwen", + "undo": "Ongedaan maken", + "copy": "Kopiëren", + "paste": "Plakken", + "copy-reference": "Referentie kopiëren", + "paste-reference": "Referentie plakken", + "import": "Importeren", + "export": "Exporteren", + "share-via": "Delen via {{provider}}", + "move": "Verplaatsen", + "select": "Selecteren", + "continue": "Voortzetten", + "discard-changes": "Wijzigingen negeren", + "download": "Downloaden", + "next": "Volgend", + "next-with-label": "Volgende: {{label}}", + "read-more": "Lees meer", + "hide": "Verbergen", + "done": "Klaar", + "print": "Afdrukken", + "restore": "Herstellen", + "confirm": "Bevestigen", + "more": "Meer", + "less": "Minder", + "skip": "Overslaan", + "send": "Verzenden" + }, + "aggregation": { + "aggregation": "Aggregatie", + "function": "Functie voor gegevensaggregatie", + "limit": "Maximale waarden", + "group-interval": "Groeperingsinterval", + "min": "Min", + "max": "Max", + "avg": "Gemiddeld", + "sum": "Som", + "count": "Tellen", + "none": "Geen" + }, + "admin": { + "settings": "Instellingen", + "general": "Algemeen", + "general-settings": "Algemene instellingen", + "home-settings": "Home-instellingen", + "home": "Thuis", + "outgoing-mail": "E-mailserver", + "outgoing-mail-settings": "Serverinstellingen voor uitgaande e-mail", + "system-settings": "Systeeminstellingen", + "test-mail-sent": "Testmail is succesvol verzonden!", + "base-url": "Basis-URL", + "base-url-required": "Basis-URL is vereist.", + "prohibit-different-url": "Verbieden om de hostnaam te gebruiken in de headers van de clientaanvraag", + "prohibit-different-url-hint": "Deze instelling moet zijn ingeschakeld voor productieomgevingen. Kan beveiligingsproblemen veroorzaken wanneer deze is uitgeschakeld", + "mail-from": "E-mail van", + "mail-from-required": "Mail Van is vereist.", + "smtp-protocol": "SMTP-protocol", + "smtp-host": "SMTP-host", + "smtp-host-required": "SMTP-host is vereist.", + "smtp-port": "SMTP-poort", + "smtp-port-required": "U moet een smtp-poort opgeven.", + "smtp-port-invalid": "Dat ziet er niet uit als een geldige smtp-poort.", + "timeout-msec": "Time-out (msec)", + "timeout-required": "Er is een time-out vereist.", + "timeout-invalid": "Dat ziet er niet uit als een geldige time-out.", + "enable-tls": "TLS inschakelen", + "tls-version": "TLS-versie", + "enable-proxy": "Proxy inschakelen", + "proxy-host": "Proxy-host", + "proxy-host-required": "Proxyhost is vereist.", + "proxy-port": "Proxy-poort", + "proxy-port-required": "Proxypoort is vereist.", + "proxy-port-range": "De proxypoort moet tussen 1 en 65535 liggen.", + "proxy-user": "Proxy-gebruiker", + "proxy-password": "Proxy wachtwoord", + "change-password": "Wachtwoord wijzigen", + "send-test-mail": "Testmail versturen", + "use-system-mail-settings": "Systeeminstellingen voor e-mailserver gebruiken", + "mail-templates": "E-mail sjablonen", + "mail-template-settings": "Instellingen voor e-mailsjablonen", + "use-system-mail-template-settings": "Systeem-e-mailsjablonen gebruiken", + "mail-template": { + "mail-template": "E-mail sjabloon", + "test": "E-mailbericht testen", + "activation": "Bericht over accountactivering", + "account-activated": "Bericht over account geactiveerd", + "account-lockout": "Bericht over accountvergrendeling", + "reset-password": "Bericht over wachtwoord opnieuw instellen", + "password-was-reset": "Bericht Wachtwoord is opnieuw ingesteld", + "user-activated": "Door de gebruiker geactiveerd bericht", + "user-registered": "Geregistreerd bericht van de gebruiker", + "api-usage-state-enabled": "Api-gebruiksstatus ingeschakeld", + "api-usage-state-warning": "Waarschuwing voor API-gebruiksstatus", + "api-usage-state-disabled": "Api-gebruiksstatus uitgeschakeld", + "two-fa-verification": "2FA-verificatiebericht" + }, + "mail-subject": "Onderwerp van de e-mail", + "mail-body": "Hoofdtekst van e-mail", + "sms-provider": "SMS-provider", + "sms-provider-settings": "Instellingen sms-provider", + "use-system-sms-settings": "De instellingen van de systeem-sms-provider gebruiken", + "sms-provider-type": "Type sms-provider", + "sms-provider-type-required": "Het type sms-provider is vereist.", + "sms-provider-type-aws-sns": "Amazone SNS", + "sms-provider-type-twilio": "Twilio", + "sms-provider-type-smpp": "SMPP", + "aws-access-key-id": "AWS-toegangssleutel-ID", + "aws-access-key-id-required": "AWS Access Key ID is vereist", + "aws-secret-access-key": "AWS geheime toegangssleutel", + "aws-secret-access-key-required": "AWS geheime toegangssleutel is vereist", + "aws-region": "AWS-regio", + "aws-region-required": "AWS-regio is vereist", + "number-from": "Telefoonnummer van", + "number-from-required": "Telefoonnummer van is vereist.", + "number-to": "Telefoonnummer naar", + "number-to-required": "Telefoonnummer Naar is vereist.", + "phone-number-hint": "Telefoonnummer in E.164-formaat, bijv. +19995550123", + "phone-number-hint-twilio": "Telefoonnummer in E.164-formaat/SID van telefoonnummer/SID-berichtenservice, bijv. +19995550123/PNXXX/MGXXX", + "phone-number-pattern": "Ongeldig telefoonnummer. Moet in E.164-formaat zijn, bijv. +19995550123.", + "phone-number-pattern-twilio": "Ongeldig telefoonnummer. Moet in E.164-indeling/SID/Berichtenservice SID van het telefoonnummer/telefoonnummer zijn, bijv. +19995550123/PNXXX/MGXXX.", + "sms-message": "SMS-bericht", + "sms-message-required": "SMS-bericht is vereist.", + "sms-message-max-length": "SMS-bericht mag niet langer zijn dan 1600 tekens", + "twilio-account-sid": "TSID voor Twilio-account", + "twilio-account-sid-required": "TSID voor Twilio-account is vereist", + "twilio-account-token": "Twilio-accounttoken", + "twilio-account-token-required": "Twilio-accounttoken is vereist", + "send-test-sms": "Test-sms verzenden", + "test-sms-sent": "Test-sms is succesvol verzonden!", + "security-settings": "Beveiligingsinstellingen", + "password-policy": "Wachtwoord beleid", + "minimum-password-length": "Minimale wachtwoordlengte", + "minimum-password-length-required": "Minimale wachtwoordlengte is vereist", + "minimum-password-length-range": "De minimale wachtwoordlengte moet tussen 5 en 50 liggen", + "minimum-uppercase-letters": "Minimum aantal hoofdletters", + "minimum-uppercase-letters-range": "Minimum aantal hoofdletters mag niet negatief zijn", + "minimum-lowercase-letters": "Minimum aantal kleine letters", + "minimum-lowercase-letters-range": "Minimum aantal kleine letters mag niet negatief zijn", + "minimum-digits": "Minimum aantal cijfers", + "minimum-digits-range": "Minimum aantal cijfers mag niet negatief zijn", + "minimum-special-characters": "Minimum aantal speciale tekens", + "minimum-special-characters-range": "Minimum aantal speciale tekens mag niet negatief zijn", + "password-expiration-period-days": "Vervalperiode van wachtwoord in dagen", + "password-expiration-period-days-range": "De vervalperiode van het wachtwoord in dagen mag niet negatief zijn", + "password-reuse-frequency-days": "Frequentie van hergebruik van wachtwoorden in dagen", + "password-reuse-frequency-days-range": "De frequentie van hergebruik van wachtwoorden in dagen mag niet negatief zijn", + "allow-whitespace": "Witruimte toestaan", + "general-policy": "Algemeen beleid", + "max-failed-login-attempts": "Maximum aantal mislukte inlogpogingen voordat het account wordt vergrendeld", + "minimum-max-failed-login-attempts-range": "Het maximale aantal mislukte aanmeldingspogingen mag niet negatief zijn", + "user-lockout-notification-email": "Als het gebruikersaccount is vergrendeld, stuurt u een melding naar e-mail", + "domain-name": "Domeinnaam", + "domain-name-unique": "Domeinnaam en protocol moeten uniek zijn.", + "domain-name-max-length": "Domeinnaam moet kleiner zijn dan 256 tekens tekens", + "error-verification-url": "Een domeinnaam mag geen symbolen '/' en ':' bevatten. Voorbeeld: thingsboard.io", + "oauth2": { + "access-token-uri": "Toegangstoken URI", + "access-token-uri-required": "De URI van het toegangstoken is vereist.", + "activate-user": "Gebruiker activeren", + "add-domain": "Domein toevoegen", + "delete-domain": "Domein verwijderen", + "add-provider": "Provider toevoegen", + "delete-provider": "Provider verwijderen", + "allow-user-creation": "Aanmaken van gebruikers toestaan", + "always-fullscreen": "Altijd volledig scherm", + "authorization-uri": "Autorisatie-URI", + "authorization-uri-required": "Autorisatie-URI is vereist.", + "client-authentication-method": "Verificatiemethode voor clients", + "client-id": "Client-ID", + "client-id-required": "Client-ID is vereist.", + "client-id-max-length": "Client-ID lengte moet kleiner zijn dan 256 tekens tekens", + "client-secret": "Geheim van de klant", + "client-secret-required": "Clientgeheim is vereist.", + "client-secret-max-length": "Clientgeheim lengte moet kleiner zijn dan 2049 tekens", + "custom-setting": "Aangepaste instellingen", + "customer-name-pattern": "Patroon klantnaam", + "customer-name-pattern-max-length": "Het patroon van de klantnaam moet kleiner zijn dan 256 tekens tekens", + "parent-customer-name-pattern": "Patroon voor bovenliggende klantnaam", + "user-groups-name-pattern": "Naampatroon van gebruikersgroepen", + "default-dashboard-name": "Standaard dashboardnaam", + "default-dashboard-name-max-length": "De standaardnaam van het dashboard moet kleiner zijn dan 256 tekens tekens", + "delete-domain-text": "Let op, na de bevestiging zijn dit domein en alle providergegevens niet meer beschikbaar.", + "delete-domain-title": "Weet u zeker dat u instellingen wilt verwijderen van dit domein '{{domainName}}'?", + "delete-registration-text": "Let op, na de bevestiging zijn de gegevens van deze provider niet meer beschikbaar.", + "delete-registration-title": "Weet u zeker dat u de provider '{{name}}' wilt verwijderen?", + "email-attribute-key": "E-mail attribuut sleutel", + "email-attribute-key-required": "E-mailkenmerksleutel is vereist.", + "email-attribute-key-max-length": "De e-mailkenmerksleutel moet kleiner zijn dan 32 tekens", + "first-name-attribute-key": "Voornaam attribuutsleutel", + "first-name-attribute-key-max-length": "De kenmerksleutel voor de voornaam moet kleiner zijn dan 32 tekens", + "general": "Algemeen", + "jwk-set-uri": "JSON-websleutel-URI", + "last-name-attribute-key": "Sleutel voor het kenmerk achternaam", + "last-name-attribute-key-max-length": "De kenmerksleutel voor de achternaam moet kleiner zijn dan 32 tekens", + "login-button-icon": "Pictogram van de inlogknop", + "login-button-label": "Label van de aanbieder", + "login-button-label-placeholder": "Log in met $(Providerlabel)", + "login-button-label-required": "Label is vereist.", + "login-provider": "Login provider", + "mapper": "Mapper", + "new-domain": "Nieuw domein", + "oauth2": "OAuth2", + "password-max-length": "Wachtwoord moet kleiner zijn dan 256 tekens tekens", + "redirect-uri-template": "Sjabloon voor omleidings-URI", + "copy-redirect-uri": "Omleidings-URI kopiëren", + "registration-id": "Registratie-ID", + "registration-id-required": "Registratie-ID is vereist.", + "registration-id-unique": "Registratie-ID moet uniek zijn voor het systeem.", + "scope": "Draagwijdte", + "scope-required": "Scope is vereist.", + "tenant-name-pattern": "Patroon van tenantnaam", + "tenant-name-pattern-required": "Het patroon van de naam van de tenant is vereist.", + "tenant-name-pattern-max-length": "Patroon van tenantnaam imoet kleiner zijn dan 256 tekens tekens", + "tenant-name-strategy": "Strategie voor naam tenant", + "type": "Type mapper", + "uri-pattern-error": "Ongeldige URI-indeling.", + "url": "URL", + "url-pattern": "Ongeldige URL-indeling.", + "url-required": "URL is vereist.", + "url-max-length": "URL moet kleiner zijn dan 256 tekens tekens", + "user-info-uri": "Gebruikersinfo URI", + "user-info-uri-required": "URI voor gebruikersinformatie is vereist.", + "username-max-length": "Gebruikersnaam moet kleiner zijn dan 256 tekens tekens", + "user-name-attribute-name": "Sleutel voor gebruikersnaamattribuut", + "user-name-attribute-name-required": "De kenmerksleutel van de gebruikersnaam is vereist", + "protocol": "Protocol", + "domain-schema-http": "HTTP (HTTP)", + "domain-schema-https": "HTTPS", + "domain-schema-mixed": "HTTP+HTTPS", + "enable": "OAuth2-instellingen inschakelen", + "domains": "Domeinen", + "mobile-apps": "Mobiele applicaties", + "no-mobile-apps": "Geen applicaties geconfigureerd", + "mobile-package": "Applicatie pakket", + "mobile-package-placeholder": "Vb.: my.example.app", + "mobile-package-hint": "Voor Android: uw eigen unieke applicatie-ID. Voor iOS: ID van productbundel.", + "mobile-package-unique": "Het toepassingspakket moet uniek zijn.", + "mobile-app-secret": "Geheim van de toepassing", + "invalid-mobile-app-secret": "Het geheim van de toepassing mag alleen alfanumerieke tekens bevatten en moet tussen de 16 en 2048 tekens lang zijn.", + "copy-mobile-app-secret": "Toepassingsgeheim kopiëren", + "add-mobile-app": "Applicatie toevoegen", + "delete-mobile-app": "Toepassingsgegevens verwijderen", + "providers": "Providers", + "platform-web": "Web", + "platform-android": "Android", + "platform-ios": "iOS", + "all-platforms": "Alle platformen", + "allowed-platforms": "Toegestane platforms" + }, + "smpp-provider": { + "smpp-version": "SMPP-versie", + "smpp-host": "SMPP-host", + "smpp-host-required": "SMPP-host is vereist", + "smpp-port": "SMPP-poort", + "smpp-port-required": "SMPP-poort is vereist", + "system-id": "Systeem-ID", + "system-id-required": "Systeem-ID is vereist", + "password": "Wachtwoord", + "password-required": "Wachtwoord is vereist", + "type-settings": "Typ instellingen", + "source-settings": "Bron-instellingen", + "destination-settings": "Bestemming instellingen", + "additional-settings": "Aanvullende instellingen", + "system-type": "Type systeem", + "bind-type": "Type binden", + "service-type": "Soort dienst", + "source-address": "Bronadres", + "source-ton": "Bron: TON", + "source-npi": "Bron: NPI", + "destination-ton": "Bestemming TON (type nummer)", + "destination-npi": "Bestemming NPI (Nummerplan Identificatie)", + "address-range": "Adres bereik", + "coding-scheme": "Coderingsschema", + "bind-type-tx": "Zender", + "bind-type-rx": "Ontvanger", + "bind-type-trx": "Transciever", + "ton-unknown": "Onbekend", + "ton-international": "Internationaal", + "ton-national": "Nationaal", + "ton-network-specific": "Netwerk Specifiek", + "ton-subscriber-number": "Abonneenummer", + "ton-alphanumeric": "Alfanumeriek", + "ton-abbreviated": "Afgekort", + "npi-unknown": "0 - Onbekend", + "npi-isdn": "1 - ISDN/telefoonnummerplan (E163/E164)", + "npi-data-numbering-plan": "3 - Nummeringsplan voor gegevens (X.121)", + "npi-telex-numbering-plan": "4 - Telexnummerplan (F.69)", + "npi-land-mobile": "6 - Landmobiel (E.212)", + "npi-national-numbering-plan": "8 - Nationaal nummerplan", + "npi-private-numbering-plan": "9 - Privé nummerplan", + "npi-ermes-numbering-plan": "10 - ERMES-nummerplan (ETSI DE/PS 3 01-3)", + "npi-internet": "13 - Internet (IP)", + "npi-wap-client-id": "18 - WAP-client-ID (te definiëren door WAP-forum)", + "scheme-smsc": "0 - SMSC Default Alphabet (ASCII voor korte en lange code en naar GSM voor gratis)", + "scheme-ia5": "1 - IA5 (ASCII voor korte en lange code, Latijnse 9 voor gratis code (ISO-8859-9))", + "scheme-octet-unspecified-2": "2 - Octet Niet gespecificeerd (8-bits binair)", + "scheme-latin-1": "3 - Latijn 1 (ISO-8859-1)", + "scheme-octet-unspecified-4": "4 - Octet Niet gespecificeerd (8-bits binair)", + "scheme-jis": "5 - JIS (X 0208-1990)", + "scheme-cyrillic": "6 - Cyrillisch (ISO-8859-5)", + "scheme-latin-hebrew": "7 - Latijn/Hebreeuws (ISO-8859-8)", + "scheme-ucs-utf": "8 - UCS2/UTF-16 (ISO/IEC-10646)", + "scheme-pictogram-encoding": "9 - Codering van pictogrammen", + "scheme-music-codes": "10 - Muziek Codes (ISO-2022-JP)", + "scheme-extended-kanji-jis": "13 - Uitgebreide Kanji JIS (X 0212-1990)", + "scheme-korean-graphic-character-set": "14 - Koreaanse grafische tekenset (KS C 5601/KS X 1001)" + }, + "queue-select-name": "Selecteer de naam van de queue", + "queue-name": "Naam", + "queue-name-required": "Queue naam is verplicht!", + "queues": "Queues", + "queue-partitions": "Partities", + "queue-submit-strategy": "Strategie indienen", + "queue-processing-strategy": "Verwerkingsstrategie", + "queue-configuration": "Configuratie van queues", + "repository-settings": "Repository-instellingen", + "repository": "Opslagplaats", + "repository-url": "URL van opslagplaats", + "repository-url-required": "De URL van de opslagplaats is vereist.", + "default-branch": "Standaard filiaalnaam", + "repository-read-only": "Alleen-lezen", + "show-merge-commits": "Samenvoegcommits weergeven", + "authentication-settings": "Authenticatie-instellingen", + "auth-method": "Verificatiemethode", + "auth-method-username-password": "Wachtwoord / toegangstoken", + "auth-method-username-password-hint": "GitHub-gebruikers moeten toegang tokens gebruiken met schrijfmachtigingen voor de opslagplaats.", + "auth-method-private-key": "Privé-sleutel", + "password-access-token": "Wachtwoord / toegangstoken", + "change-password-access-token": "Wachtwoord / toegangstoken wijzigen", + "private-key": "Privé-sleutel", + "drop-private-key-file-or": "Sleep een bestand met een persoonlijke sleutel of", + "passphrase": "Wachtwoordzin", + "enter-passphrase": "Voer wachtwoordzin in", + "change-passphrase": "Wachtwoordzin wijzigen", + "check-access": "Toegang controleren", + "check-repository-access-success": "Toegang tot repository succesvol geverifieerd!", + "delete-repository-settings-title": "Weet je zeker dat je repository-instellingen wilt verwijderen?", + "delete-repository-settings-text": "Opgelet, na de bevestiging worden de instellingen van het archief verwijderd en is de versiebeheerfunctie niet meer beschikbaar.", + "auto-commit-settings": "Instellingen voor automatisch vastleggen", + "auto-commit": "Automatisch vastleggen", + "auto-commit-entities": "Entiteiten automatisch vastleggen", + "no-auto-commit-entities-prompt": "Geen entiteiten geconfigureerd voor automatisch vastleggen", + "delete-auto-commit-settings-title": "Weet je zeker dat je instellingen voor automatisch vastleggen wilt verwijderen?", + "delete-auto-commit-settings-text": "Opgelet, na de bevestiging worden de instellingen voor automatisch vastleggen verwijderd en wordt automatisch vastleggen uitgeschakeld voor alle entiteiten.", + "2fa": { + "2fa": "Twee-factor-authenticatie", + "available-providers": "Beschikbare providers", + "issuer-name": "Naam van de uitgever", + "issuer-name-required": "De naam van de uitgever is vereist.", + "max-verification-failures-before-user-lockout": "Max. aantal verificatiefouten vóór gebruikersvergrendeling", + "max-verification-failures-before-user-lockout-pattern": "Het maximum aantal mislukte verificaties moet een positief geheel getal zijn.", + "number-of-checking-attempts": "Aantal controlepogingen", + "number-of-checking-attempts-pattern": "Het aantal controlepogingen moet een positief geheel getal zijn.", + "number-of-checking-attempts-required": "Het aantal controlepogingen is vereist.", + "number-of-codes": "Aantal codes", + "number-of-codes-pattern": "Het aantal codes moet een positief geheel getal zijn.", + "number-of-codes-required": "Aantal codes is vereist.", + "provider": "Aanbieder", + "retry-verification-code-period": "Verificatiecodeperiode opnieuw proberen (sec)", + "retry-verification-code-period-pattern": "Minimale tijdsduur is 5 sec", + "retry-verification-code-period-required": "De periode van de verificatiecode voor opnieuw proberen is vereist.", + "total-allowed-time-for-verification": "Totale toegestane tijd voor verificatie (sec)", + "total-allowed-time-for-verification-pattern": "Minimaal toegestane tijd is 60 sec", + "total-allowed-time-for-verification-required": "De totale toegestane tijd is vereist.", + "use-system-two-factor-auth-settings": "Tweefactorauthenticatie-instellingen van het systeem gebruiken", + "verification-code-check-rate-limit": "Snelheidslimiet voor verificatiecodecontrole", + "verification-code-lifetime": "Levensduur verificatiecode (sec)", + "verification-code-lifetime-pattern": "De levensduur van de verificatiecode moet een positief geheel getal zijn.", + "verification-code-lifetime-required": "De levensduur van de verificatiecode is vereist.", + "verification-message-template": "Sjabloon voor verificatiebericht", + "verification-limitations": "Beperkingen voor verificatie", + "verification-message-template-pattern": "Verificatiebericht moet patroon bevatten: ${code}", + "verification-message-template-required": "Verificatieberichtsjabloon is vereist.", + "within-time": "Binnen de tijd (sec)", + "within-time-pattern": "Tijd moet een positief geheel getal zijn.", + "within-time-required": "Er is tijd nodig." + }, + "jwt": { + "security-settings": "JWT-beveiligingsinstellingen", + "issuer-name": "Naam van de uitgever", + "issuer-name-required": "De naam van de uitgever is vereist.", + "signings-key": "Sleutel ondertekenen", + "signings-key-hint": "Base64-gecodeerde tekenreeks die ten minste 256 bits aan gegevens vertegenwoordigt.", + "signings-key-required": "Ondertekeningssleutel is vereist.", + "signings-key-min-length": "De ondertekeningssleutel moet ten minste 256 bits aan gegevens bevatten.", + "signings-key-base64": "De ondertekeningssleutel moet de base64-indeling hebben.", + "expiration-time": "Vervaltijd token (sec)", + "expiration-time-required": "De vervaltijd van het token is vereist.", + "expiration-time-pattern": "De vervaltijd van het token is een positief geheel getal.", + "expiration-time-min": "De minimale tijd is 60 seconden (1 minuut).", + "refresh-expiration-time": "Vervaltijd token vernieuwen (sec)", + "refresh-expiration-time-required": "De vervaltijd van het vernieuwingstoken is vereist.", + "refresh-expiration-time-pattern": "De vervaltijd van het token vernieuwen is een positief geheel getal.", + "refresh-expiration-time-min": "De minimale tijd is 900 seconden (15 minuten).", + "refresh-expiration-time-less-token": "De tokentijd voor vernieuwen moet een grotere tokentijd zijn.", + "generate-key": "Sleutel genereren", + "info-header": "Alle gebruikers moeten opnieuw inloggen", + "info-message": "Wijziging van de JWT-ondertekeningssleutel zorgt ervoor dat alle uitgegeven tokens ongeldig zijn. Alle gebruikers moeten opnieuw inloggen. Dit heeft ook gevolgen voor scripts die gebruikmaken van Rest API/Websockets." + }, + "resources": "Weg", + "notifications": "Meldingen", + "notifications-settings": "Instellingen voor meldingen", + "slack-api-token": "Slack API-token", + "slack": "Los", + "slack-settings": "Slack-instellingen" + }, + "alarm": { + "alarm": "Alarm", + "alarms": "Alarmen", + "all-alarms": "Alle alarmen", + "select-alarm": "Selecteer alarm", + "no-alarms-matching": "Er zijn geen alarmen gevonden die overeenkomen met '{{entity}}'.", + "alarm-required": "Alarm is vereist", + "alarm-filter": "Alarm filter", + "filter": "Filter", + "alarm-status": "Alarmstatus", + "alarm-status-list": "Lijst met alarmstatussen", + "any-status": "Elke status", + "search-status": { + "ANY": "Enig", + "ACTIVE": "Actief", + "CLEARED": "Uitgeschakeld", + "ACK": "Erkend", + "UNACK": "Niet erkend" + }, + "display-status": { + "ACTIVE_UNACK": "Actief Niet erkend", + "ACTIVE_ACK": "Actief Erkend", + "CLEARED_UNACK": "Gewist Niet bevestigd", + "CLEARED_ACK": "Gewist Erkend" + }, + "no-alarms-prompt": "Geen alarmen gevonden", + "created-time": "Gecreëerde tijd", + "type": "Type", + "severity": "Strengheid", + "originator": "Opdrachtgever", + "originator-type": "Type opsteller", + "details": "Details", + "originator-label": "Etiket van de afzender", + "assign": "Toewijzen", + "assignments": "Toewijzingen", + "assignee": "Gevolmachtigde", + "assignee-id": "Verantwoordelijke ID", + "assignee-first-name": "Voornaam van de gevolmachtigde", + "assignee-last-name": "Achternaam van de cessionaris", + "assignee-email": "E-mailadres van de verantwoordelijke", + "unassigned": "Toegewezen", + "assignee-not-set": "Alle", + "status": "Status", + "alarm-details": "Details van het alarm", + "start-time": "Begintijd", + "assign-time": "Tijd toewijzen", + "end-time": "Eindtijd", + "ack-time": "Erkende tijd", + "clear-time": "Vrijgemaakte tijd", + "duration": "Duur", + "alarm-severity-list": "Lijst met alarmernst", + "any-severity": "Elke ernst", + "severity-critical": "Kritisch", + "severity-major": "Majoor", + "severity-minor": "Minderjarige", + "severity-warning": "Waarschuwing", + "severity-indeterminate": "Onbepaalde", + "acknowledge": "Erkennen", + "clear": "Duidelijk", + "search": "Alarmen zoeken", + "selected-alarms": "{ count, plural, =1 {1 alarm} andere {# alarms} } geselecteerd", + "no-data": "Geen gegevens om weer te geven", + "polling-interval": "Polling-interval alarmen (sec)", + "polling-interval-required": "Alarmen polling-interval is vereist.", + "min-polling-interval-message": "Minimaal 1 sec polling-interval is toegestaan.", + "aknowledge-alarms-title": "Erken { count, plural, =1 {1 alarm} andere {# alarms} }", + "aknowledge-alarms-text": "Weet je zeker dat je { count, plural, =1 {1 alarm} andere {# alarms} } wilt erkennen?", + "aknowledge-alarm-title": "Alarm bevestigen", + "aknowledge-alarm-text": "Weet u zeker dat u Alarm wilt erkennen?", + "clear-alarms-title": "Wis { count, plural, =1 {1 alarm} andere {# alarms} }", + "clear-alarms-text": "Weet u zeker dat u { count, plural, =1 {1 alarm} andere {# alarms} } wilt wissen?", + "clear-alarm-title": "Alarm wissen", + "clear-alarm-text": "Weet u zeker dat u Alarm wilt wissen?", + "alarm-status-filter": "Alarm Status Filter", + "alarm-filter-title": "Alarm Filter", + "assigned": "Toegewezen", + "filter-title": "Filter", + "max-count-load": "Maximaal aantal te laden alarmen (0 - onbeperkt)", + "max-count-load-required": "Er is een maximaal aantal alarmen vereist om te laden.", + "max-count-load-error-min": "Minimale waarde is 0.", + "fetch-size": "Ophaal grootte", + "fetch-size-required": "Ophaal grootte is vereist.", + "fetch-size-error-min": "Minimale waarde is 10.", + "alarm-type-list": "Lijst met alarmtypen", + "any-type": "Elk type", + "assigned-to-current-user": "Toegewezen aan huidige gebruiker", + "assigned-to-me": "Aan mij toegewezen", + "search-propagated-alarms": "Doorgegeven alarmen zoeken", + "comments": "Alarm opmerkingen", + "show-more": "Meer tonen", + "additional-info": "Aanvullende informatie" + }, + "alarm-activity": { + "add": "Voeg een reactie...", + "alarm-comment": "Alarm commentaar", + "comments": "Opmerkingen", + "delete-alarm-comment": "Wil je deze reactie verwijderen?", + "refresh": "Vernieuwen", + "oldest-first": "Oudste eerst", + "newest-first": "Nieuwste eerst", + "activity": "Activiteit", + "export": "Exporteren naar CSV", + "author": "Auteur", + "created-date": "Datum van aanmaak", + "edited-date": "Bewerkte datum", + "text": "Sms", + "system": "Systeem" + }, + "alias": { + "add": "Alias toevoegen", + "edit": "Alias bewerken", + "name": "Alias naam", + "name-required": "Aliasnaam is vereist", + "duplicate-alias": "Alias met dezelfde naam bestaat al.", + "filter-type-single-entity": "Eén entiteit", + "filter-type-entity-group": "Entiteiten van de groep", + "filter-type-entity-list": "Lijst met entiteiten", + "filter-type-entity-name": "Naam van de entiteit", + "filter-type-entity-type": "Type entiteit", + "filter-type-entity-group-list": "Lijst met entiteitsgroepen", + "filter-type-entity-group-name": "Naam van entiteitsgroep", + "filter-type-entities-by-group-name": "Entiteiten op groepsnaam", + "filter-type-state-entity": "Entiteit vanuit dashboardstatus", + "filter-type-state-entity-description": "Entiteit die is overgenomen uit de statusparameters van het dashboard", + "filter-type-state-entity-owner": "Eigenaar van entiteit vanuit dashboardstatus", + "filter-type-state-entity-owner-description": "Eigenaar van entiteit die is overgenomen uit de parameters van de dashboardstatus", + "filter-type-asset-type": "Type asset", + "filter-type-asset-type-description": "Asset van het type \"{{assetTypes}}\"", + "filter-type-asset-type-and-name-description": "Asset van het type '{{assetTypes}}' en waarvan de naam begint met '{{prefix}}'", + "filter-type-device-type": "Type device", + "filter-type-device-type-description": "Devices van het type \"{{deviceTypes}}\"", + "filter-type-device-type-and-name-description": "Devices van het type '{{deviceTypes}}' en waarvan de naam begint met '{{prefix}}'", + "filter-type-entity-view-type": "Type entiteitsweergave", + "filter-type-entity-view-type-description": "Entiteitsweergaven van het type '{{entityViewTypes}}'", + "filter-type-entity-view-type-and-name-description": "Entiteitsweergaven van het type '{{entityViewTypes}}' en met een naam die begint met '{{prefix}}'", + "filter-type-edge-type": "Type edge", + "filter-type-edge-type-description": "Edge van het type \"{{edgeTypes}}\"", + "filter-type-edge-type-and-name-description": "Edge van het type '{{edgeTypes}}' en met de naam beginnend met '{{prefix}}'", + "filter-type-relations-query": "Query op relaties", + "filter-type-relations-query-description": "{{entities}} die {{relationType}} relatie hebben {{direction}} {{rootEntity}}", + "filter-type-edge-search-query": "Edge-zoekopdracht", + "filter-type-edge-search-query-description": "Edge met types {{edgeTypes}} die {{relationType}} relatie hebben {{direction}} {{rootEntity}}", + "filter-type-scheduler-event": "Scheduler-events", + "filter-type-scheduler-event-type-description": "Scheduler-events met het type '{{eventType}}'", + "filter-type-scheduler-event-originator-description": "Scheduler-gebeurtenissen van een specifieke afzender", + "filter-type-scheduler-event-type-originator-description": "Scheduler-gebeurtenissen van een specifieke afzender met '{{eventType}}'", + "filter-type-asset-search-query": "Zoekquery voor asset", + "filter-type-asset-search-query-description": "Asset met types {{assetTypes}} die {{relationType}} relatie hebben {{direction}} {{rootEntity}}", + "filter-type-device-search-query": "Zoekquery voor device", + "filter-type-device-search-query-description": "Devices met types {{deviceTypes}} die {{relationType}} relatie hebben {{direction}} {{rootEntity}}", + "filter-type-entity-view-search-query": "Zoekquery voor entiteitsweergave", + "filter-type-entity-view-search-query-description": "Entiteitsweergaven met types {{entityViewTypes}} die {{relationType}} relatie hebben {{direction}} {{rootEntity}}", + "filter-type-apiUsageState": "Api-gebruiksstatus", + "entity-filter": "Entiteit filteren", + "resolve-multiple": "Oplossen als meerdere entiteiten", + "filter-type": "Filter type", + "filter-type-required": "Filtertype is vereist.", + "entity-filter-no-entity-matched": "Er zijn geen entiteiten gevonden die overeenkomen met het opgegeven filter.", + "no-entity-filter-specified": "Geen entiteitsfilter opgegeven", + "root-state-entity": "Dashboardstatusentiteit gebruiken als hoofdmap", + "group-state-entity": "Dashboardstatusentiteit gebruiken als entiteitsgroep", + "group-state-entity-owner": "Dashboardstatusentiteit gebruiken als eigenaar van entiteitsgroep", + "last-level-relation": "Alleen relatie op het laatste niveau ophalen", + "root-entity": "Hoofdentiteit", + "state-entity-parameter-name": "Naam van de parameter van de entiteit van de staat", + "default-state-entity": "Entiteit met standaardstatus", + "default-state-entity-group": "Entiteitsgroep standaardstatus", + "default-entity-parameter-name": "Standaard", + "max-relation-level": "Max. relatieniveau", + "unlimited-level": "Onbeperkt niveau", + "state-entity": "Entiteit voor dashboardstatus", + "entities-of-group-state-entity": "Entiteiten uit de entiteitsgroep van de dashboardstatus", + "all-entities": "Alle entiteiten", + "any-relation": "enig", + "originator": "Opdrachtgever", + "originator-state-entity": "Dashboardstatusentiteit gebruiken als afzender", + "scheduler-event-type": "Gebeurtenistype planner" + }, + "asset": { + "all": "Alle", + "all-assets": "Alle assets", + "groups": "Groepen", + "shared": "Gedeeld", + "asset": "Asset", + "assets": "Assets", + "management": "Vermogensbeheer", + "view-assets": "Assets bekijken", + "add": "Asset toevoegen", + "asset-type-max-length": "Het type asset moet kleiner zijn dan 256 tekens tekens", + "assign-to-customer": "Toewijzen aan klant", + "assign-asset-to-customer": "Asset(s) toewijzen aan klant", + "assign-asset-to-customer-text": "Selecteer de asset die u aan de klant wilt toewijzen", + "no-assets-text": "Geen asset gevonden", + "assign-to-customer-text": "Selecteer de klant om de asset toe te wijzen", + "public": "Publiek", + "assignedToCustomer": "Toegewezen aan klant", + "make-public": "Assets openbaar maken", + "make-private": "Assets privé maken", + "unassign-from-customer": "Toewijzing van klant ongedaan maken", + "delete": "Asset verwijderen", + "asset-public": "Asset is openbaar", + "asset-type": "Type asset", + "asset-type-required": "Het type asset is vereist.", + "select-asset-type": "Selecteer asset type", + "enter-asset-type": "Asset type invoeren", + "any-asset": "Elke asset", + "no-asset-types-matching": "Er zijn geen asset types gevonden die overeenkomen met '{{entitySubtype}}'.", + "asset-type-list-empty": "Er zijn geen asset types geselecteerd.", + "asset-types": "Soorten asset", + "name": "Naam", + "name-required": "Naam is verplicht.", + "name-max-length": "Naam moet kleiner zijn dan 256 tekens tekens", + "label-max-length": "Etiket moet kleiner zijn dan 256 tekens tekens", + "description": "Omschrijving: __________", + "type": "Type", + "type-required": "Type is vereist.", + "details": "Detail", + "events": "Events", + "add-asset-text": "Nieuw asset toevoegen", + "asset-details": "Details van het bedrijfsmiddel", + "assign-assets": "Middelen toewijzen", + "assign-assets-text": "Wijs { count, plural, =1 {1 asset} andere {# assets} } toe aan de klant", + "assign-asset-to-edge-title": "Asset(s) toewijzen aan Edge", + "assign-asset-to-edge-text": "Selecteer de assets die u aan de edge wilt toewijzen", + "delete-assets": "Assets verwijderen", + "unassign-assets": "Toewijzing van assets ongedaan maken", + "unassign-assets-action-title": "Toewijzing { count, plural, =1 {1 asset} andere {# assets} } van klant ongedaan maken", + "assign-new-asset": "Nieuw asset toewijzen", + "delete-asset-title": "Weet je zeker dat je de asset '{{assetName}}' wilt verwijderen?", + "delete-asset-text": "Opgelet, na de bevestiging worden de asset en alle gerelateerde gegevens onherstelbaar.", + "delete-assets-title": "Weet u zeker dat u { count, plural, =1 {1 asset} andere {# assets} } wilt verwijderen?", + "delete-assets-action-title": "Verwijder { count, plural, =1 {1 asset} andere {# assets} }", + "delete-assets-text": "Opgelet, na de bevestiging worden alle geselecteerde assets verwijderd en kunnen alle gerelateerde gegevens niet meer worden hersteld.", + "make-public-asset-title": "Weet je zeker dat je de asset '{{assetName}}' openbaar wilt maken?", + "make-public-asset-text": "Na de bevestiging worden de asset en alle bijbehorende gegevens openbaar en toegankelijk gemaakt voor anderen.", + "make-private-asset-title": "Weet je zeker dat je de asset '{{assetName}}' privé wilt maken?", + "make-private-asset-text": "Na de bevestiging worden de asset en al zijn gegevens privé gemaakt en zijn ze niet toegankelijk voor anderen.", + "unassign-asset-title": "Weet je zeker dat je de toewijzing van de asset '{{assetName}}' ongedaan wilt maken?", + "unassign-asset-text": "Na de bevestiging wordt de toewijzing van de asset ongedaan gemaakt en is het niet toegankelijk voor de klant.", + "unassign-asset": "Toewijzing van asset ongedaan maken", + "unassign-assets-title": "Weet u zeker dat u de toewijzing van { count, plural, =1 {1 asset} andere {# assets} } wilt opheffen?", + "unassign-assets-text": "Na de bevestiging worden alle geselecteerde assets ongedaan gemaakt en zijn ze niet toegankelijk voor de klant.", + "copyId": "Item-ID kopiëren", + "idCopiedMessage": "Item-ID is gekopieerd naar het klembord", + "select-asset": "Selecteer asset", + "no-assets-matching": "Er zijn geen assets gevonden die overeenkomen met '{{entity}}'.", + "asset-required": "Asset is vereist", + "name-starts-with": "Expressie van itemnaam", + "help-text": "Gebruik '%' naar behoefte: '%asset_name_contains%', '%asset_name_ends', 'asset_starts_with'.", + "search": "Assets zoeken", + "select-group-to-add": "Selecteer doelgroep om geselecteerde assets toe te voegen", + "select-group-to-move": "Selecteer de doelgroep om geselecteerde assets te verplaatsen", + "remove-assets-from-group": "Weet je zeker dat je { count, plural, =1 {1 asset} andere {# assets} } uit groep '{{entityGroup}}' wilt verwijderen?", + "group": "Groep van assets", + "list-of-groups": "{ count, plural, =1 {One asset group} andere {List of # asset groups} }", + "group-name-starts-with": "Asset groepen waarvan de naam begint met '{{prefix}}'", + "import": "Asset importeren", + "asset-file": "Asset bestand", + "label": "Etiket", + "assign-asset-to-edge": "Asset(s) toewijzen aan Edge", + "unassign-asset-from-edge": "Toewijzing van asset ongedaan maken", + "unassign-asset-from-edge-title": "Weet je zeker dat je de toewijzing van de asset '{{assetName}}' ongedaan wilt maken?", + "unassign-asset-from-edge-text": "Na de bevestiging wordt de toewijzing van de asset ongedaan gemaakt en is het niet toegankelijk via de edge.", + "unassign-assets-from-edge-title": "Weet u zeker dat u de toewijzing van { count, plural, =1 {1 asset} andere {# assets} } wilt opheffen?", + "unassign-assets-from-edge-text": "Na de bevestiging worden alle geselecteerde assets ongedaan gemaakt en zijn ze niet toegankelijk via de edge.", + "selected-assets": "{ count, plural, =1 {1 asset} andere {# assets} } geselecteerd" + }, + "attribute": { + "attributes": "Attributen", + "latest-telemetry": "Nieuwste telemetrie", + "attributes-scope": "Bereik van entiteits attributen", + "scope-latest-telemetry": "Nieuwste telemetrie", + "scope-client": "Attributen van de klant", + "scope-server": "Server-attributen", + "scope-shared": "Gedeelde attributen", + "add": "Attribuut toevoegen", + "add-attribute-prompt": "Voeg attribuut toe", + "key": "Sleutel", + "key-max-length": "Sleutel moet kleiner zijn dan 256 tekens tekens", + "last-update-time": "Laatste update tijd", + "key-required": "Attribuutsleutel is vereist.", + "value": "Waarde", + "value-required": "Kenmerkwaarde is vereist.", + "delete-attributes-title": "Weet u zeker dat u { count, plural, =1 {1 attribute} andere {# attributes} } wilt verwijderen?", + "delete-attributes-text": "Opgelet, na de bevestiging worden alle geselecteerde attributen verwijderd.", + "delete-attributes": "Attributen verwijderen", + "enter-attribute-value": "Kenmerkwaarde invoeren", + "show-on-widget": "Toon op widget", + "widget-mode": "Widget-modus", + "next-widget": "Volgende widget", + "prev-widget": "Vorige widget", + "add-to-dashboard": "Toevoegen aan dashboard", + "add-widget-to-dashboard": "Widget toevoegen aan dashboard", + "selected-attributes": "{ count, plural, =1 {1 attribute} andere {# attributes} } geselecteerd", + "selected-telemetry": "{ count, plural, =1 {1 telemetry unit} andere {# telemetry units} } geselecteerd", + "no-attributes-text": "Geen attributen gevonden", + "no-telemetry-text": "Geen telemetrie gevonden", + "copy-key": "Kopieer sleutel", + "copy-value": "Waarde kopiëren" + }, + "api-usage": { + "api-features": "API-functies", + "api-usage": "API-gebruik", + "alarm": "Alarm", + "alarms-created": "Alarmen gemaakt", + "alarms-created-daily-activity": "Alarmen creëerden dagelijkse activiteit", + "alarms-created-hourly-activity": "Alarmen creëerden activiteit per uur", + "alarms-created-monthly-activity": "Alarmen creëerden maandelijkse activiteit", + "data-points": "Datapunten", + "data-points-storage-days": "Opslagdagen voor datapunten", + "device-api": "Device-API", + "email": "E-mail", + "email-messages": "E-mailberichten", + "email-messages-daily-activity": "Dagelijkse activiteit e-mailberichten", + "email-messages-monthly-activity": "Maandelijkse activiteit e-mailberichten", + "exceptions": "Uitzonderingen", + "executions": "Executies", + "javascript": "JavaScript", + "javascript-executions": "JavaScript-uitvoeringen", + "javascript-functions": "JavaScript-functies", + "javascript-functions-daily-activity": "JavaScript-functies dagelijkse activiteit", + "javascript-functions-hourly-activity": "JavaScript-functies activiteit per uur", + "javascript-functions-monthly-activity": "JavaScript-functies maandelijkse activiteit", + "latest-error": "Laatste error", + "messages": "Berichten", + "notifications": "Meldingen", + "notifications-email-sms": "Meldingen (e-mail/sms)", + "notifications-hourly-activity": "Meldingen activiteit per uur", + "permanent-failures": "${entityName} Permanente storingen", + "permanent-timeouts": "Permanente time-outs van $ {entityName}", + "processing-failures": "${entityName} Verwerkingsfouten", + "processing-failures-and-timeouts": "Verwerkingsfouten en time-outs", + "processing-timeouts": "${entityName} time-outs voor verwerking", + "queue-stats": "Queue Statistieken", + "rule-chain": "Rule chain", + "rule-engine": "Rule engine", + "rule-engine-daily-activity": "Dagelijkse activiteit van de rule engine", + "rule-engine-executions": "Uitvoeringen van rule engine", + "rule-engine-hourly-activity": "Rule engine uurlijkse activiteit", + "rule-engine-monthly-activity": "Maandelijkse activiteit van Rule Engine", + "rule-engine-statistics": "Rule Engine-statistieken", + "rule-node": "Regel Knooppunt", + "sms": "SMS", + "sms-messages": "SMS-berichten", + "sms-messages-daily-activity": "Dagelijkse activiteit SMS-berichten", + "sms-messages-monthly-activity": "SMS-berichten maandelijkse activiteit", + "successful": "${entityName} Succesvol", + "telemetry": "Telemetrie", + "telemetry-persistence": "Persistentie van telemetrie", + "telemetry-persistence-daily-activity": "Dagelijkse activiteit met behoud van telemetrie", + "telemetry-persistence-hourly-activity": "Telemetriepersistentie activiteit per uur", + "telemetry-persistence-monthly-activity": "Maandelijkse activiteit voor telemetriepersistentie", + "transport": "Vervoer", + "transport-daily-activity": "Vervoer dagelijkse activiteit", + "transport-data-points": "Datapunten voor transport", + "transport-hourly-activity": "Vervoer per uur", + "transport-messages": "Berichten vervoeren", + "transport-monthly-activity": "Maandelijkse activiteit Transport", + "view-details": "Bekijk details", + "view-statistics": "Statistieken bekijken" + }, + "audit-log": { + "audit": "Audit", + "audit-logs": "Audit Logboeken", + "timestamp": "Tijdstempel", + "entity-type": "Type entiteit", + "entity-name": "Naam van de entiteit", + "user": "Gebruiker", + "type": "Type", + "status": "Status", + "details": "Details", + "type-added": "Toegevoegd", + "type-deleted": "Verwijderd", + "type-updated": "Bijgewerkt", + "type-attributes-updated": "Attributen geüpdatet", + "type-attributes-deleted": "Attributen verwijderd", + "type-rpc-call": "RPC-oproep", + "type-credentials-updated": "Inloggegevens bijgewerkt", + "type-assigned-to-customer": "Toegewezen aan klant", + "type-unassigned-from-customer": "Toewijzing van klant ongedaan gemaakt", + "type-assigned-to-edge": "Toegewezen aan Edge", + "type-unassigned-from-edge": "Toewijzing van Edge ongedaan gemaakt", + "type-activated": "Geactiveerd", + "type-suspended": "Latent", + "type-credentials-read": "Referenties gelezen", + "type-attributes-read": "Gelezen attributen", + "type-added-to-entity-group": "Toegevoegd aan groep", + "type-removed-from-entity-group": "Verwijderd uit groep", + "type-relation-add-or-update": "Relatie geüpdatet", + "type-relation-delete": "Relatie verwijderd", + "type-relations-delete": "Alle relaties verwijderd", + "type-alarm-ack": "Erkend", + "type-alarm-clear": "Uitgeschakeld", + "type-alarm-assign": "Toegewezen", + "type-alarm-unassign": "Toegewezen", + "type-added-comment": "Reactie toegevoegd", + "type-updated-comment": "Bijgewerkte reactie", + "type-deleted-comment": "Verwijderde reactie", + "type-rest-api-rule-engine-call": "REST API-aanroep van rule engine", + "type-made-public": "Openbaar gemaakt", + "type-made-private": "Privé gemaakt", + "type-login": "Inloggen", + "type-logout": "Logout", + "type-lockout": "Lockout", + "status-success": "Succes", + "status-failure": "Fout", + "audit-log-details": "Details van het auditlogboek", + "no-audit-logs-prompt": "Geen logboeken gevonden", + "action-data": "Actie gegevens", + "failure-details": "Details van de fout", + "search": "Auditlogboeken doorzoeken", + "clear-search": "Zoekopdracht wissen", + "type-assigned-from-tenant": "Toegewezen door tenant", + "type-assigned-to-tenant": "Toegewezen aan tenant", + "type-provision-success": "Device ingericht", + "type-provision-failure": "Het inrichten van devices is mislukt", + "type-timeseries-updated": "Telemetrie bijgewerkt", + "type-timeseries-deleted": "Telemetrie verwijderd", + "type-owner-changed": "Eigenaar gewijzigd" + }, + "confirm-on-exit": { + "message": "U hebt niet-opgeslagen wijzigingen. Weet je zeker dat je deze pagina wilt verlaten?", + "html-message": "U hebt niet-opgeslagen wijzigingen.
Weet je zeker dat je deze pagina wilt verlaten?", + "title": "Niet-opgeslagen wijzigingen" + }, + "contact": { + "country": "Land", + "city": "Stad", + "state": "Staat / Provincie", + "postal-code": "Postcode", + "postal-code-invalid": "Ongeldige postcode notatie.", + "address": "Adres", + "address2": "Adres 2", + "phone": "Telefoon", + "email": "E-mail", + "no-address": "Geen adres", + "state-max-length": "De lengte van de staat moet kleiner zijn dan 256 tekens tekens", + "phone-max-length": "Telefoonnummer moet minder zijn dan 256 tekens tekens", + "city-max-length": "De opgegeven stad moet kleiner zijn dan 256 tekens tekens" + }, + "common": { + "username": "Gebruikersnaam", + "password": "Wachtwoord", + "enter-username": "Gebruikersnaam invoeren", + "enter-password": "Wachtwoord invoeren", + "enter-search": "Zoekopdracht invoeren", + "created-time": "Gecreëerde tijd", + "loading": "Laden...", + "proceed": "Doorgaan", + "open-details-page": "Detailpagina openen" + }, + "converter": { + "converter": "Gegevens converteren", + "converters": "Data-converters", + "select-converter": "Selecteer gegevensomzetter", + "no-converters-matching": "Er zijn geen gegevensconverters gevonden die overeenkomen met '{{entity}}'.", + "no-converters-found": "Geen dataconverters gevonden.", + "converter-required": "Dataconverter is vereist", + "delete": "Converter verwijderen", + "management": "Beheer van dataconverters", + "add-converter-text": "Nieuwe gegevensconverter toevoegen", + "no-converters-text": "Geen dataconverters gevonden", + "selected-converters": "{ count, plural, =1 {1 data converter} andere {# data converters} } geselecteerd", + "delete-converter-title": "Weet u zeker dat u de dataconverter '{{converterName}}' wilt verwijderen?", + "delete-converter-text": "Opgelet, na de bevestiging worden de gegevensomzetter en alle gerelateerde gegevens onherstelbaar.", + "delete-converters-title": "Weet u zeker dat u { count, plural, =1 {1 data converter} andere {# data converters} } wilt verwijderen?", + "delete-converters-action-title": "Verwijder { count, plural, =1 {1 data converter} andere {# data converters} }", + "delete-converters-text": "Opgelet, na de bevestiging worden alle geselecteerde gegevensconverters verwijderd en kunnen alle gerelateerde gegevens niet meer worden hersteld.", + "events": "Events", + "add": "Gegevensconverter toevoegen", + "search": "Gegevensconverters zoeken", + "converter-details": "Details van de gegevensconverter", + "details": "Details", + "copyId": "Kopieer converter-ID", + "idCopiedMessage": "Converter-ID is gekopieerd naar klembord", + "debug-mode": "Foutopsporingsmodus", + "created-time": "Gecreëerde tijd", + "name": "Naam", + "name-required": "Naam is verplicht.", + "name-max-length": "Naam moet kleiner zijn dan 256 tekens", + "description": "Omschrijving: __________", + "decoder": "Decoder", + "encoder": "Coderingsprogramma", + "test-decoder-fuction": "Test decoder functie", + "test-encoder-fuction": "Encoderfunctie testen", + "decoder-input-params": "Ingangsparameters decoder", + "encoder-input-params": "Ingangsparameters van de encoder", + "payload": "Nettolading", + "payload-content-type": "Inhoudstype payload", + "payload-content": "Inhoud van payload", + "message": "Bericht", + "message-type": "Soort bericht", + "message-type-required": "Berichttype is vereist", + "test": "Test", + "metadata": "Metagegevens", + "metadata-required": "Metagegevensvermeldingen mogen niet leeg zijn.", + "integration-metadata": "Integratie metadata", + "integration-metadata-required": "Vermeldingen in integratiemetagegevens mogen niet leeg zijn.", + "output": "Uitvoer", + "import": "Converter importeren", + "export": "Converter exporteren", + "export-failed-error": "Kan converter niet exporteren: {{error}}", + "create-new-converter": "Nieuwe converter maken", + "converter-file": "Converter bestand", + "invalid-converter-file-error": "Kan converter niet importeren: Ongeldige gegevensstructuur van converter.", + "type": "Type", + "type-required": "Type is vereist.", + "type-uplink": "Uplink", + "type-downlink": "Downlink" + }, + "content-type": { + "json": "Json", + "text": "Sms", + "binary": "Binair (Base64)" + }, + "color": { + "color-picker": "Kleurkiezer", + "primary-colors": "Primaire kleuren", + "accent-colors": "Accent kleuren", + "no-color-selected": "Geen kleur geselecteerd" + }, + "customer": { + "all": "Alle", + "all-customers": "Alle klanten", + "groups": "Groepen", + "shared": "Gedeeld", + "hierarchy": "Hiërarchie", + "customer": "Klant", + "customers": "Klanten", + "management": "Klantenbeheer", + "dashboard": "Klantendashboard", + "dashboards": "Dashboards voor klanten", + "devices": "Devices van klanten", + "entity-views": "Weergaven van klantentiteiten", + "assets": "Assets van klanten", + "public-dashboards": "Openbare dashboards", + "public-devices": "Openbare devices", + "public-assets": "Publieke assets", + "public-entity-views": "Weergaven van openbare entiteit-views", + "add": "Klant toevoegen", + "delete": "Klant verwijderen", + "manage-customer-user-groups": "Gebruikersgroepen van klanten beheren", + "manage-customer-groups": "Klantgroepen beheren", + "manage-customer-device-groups": "Asset groepen van klanten beheren", + "manage-customer-asset-groups": "Asset groepen van klanten beheren", + "manage-customer-entity-view-groups": "Weergavegroepen voor klantentiteiten beheren", + "manage-customer-edge-groups": "Edge-groepen voor klanten beheren", + "manage-customer-dashboard-groups": "Groepen klantendashboards beheren", + "manage-customer-users": "Klantgebruikers beheren", + "manage-customers": "Klanten beheren", + "manage-customer-devices": "Devices van klanten beheren", + "manage-customer-entity-views": "Weergaven van klantentiteiten beheren", + "manage-customer-dashboards": "Klantdashboards beheren", + "manage-public-devices": "Openbare devices beheren", + "manage-public-dashboards": "Openbare dashboards beheren", + "manage-customer-assets": "Klant devices beheren", + "manage-customer-edges": "Beheer klant edges", + "manage-public-assets": "Openbare asset beheren", + "add-customer-text": "Nieuwe klant toevoegen", + "no-customers-text": "Geen klanten gevonden", + "customer-details": "Klantgegevens", + "delete-customer-title": "Weet je zeker dat je de '{{customerTitle}}' van de klant wilt verwijderen?", + "delete-customer-text": "Let op, na de bevestiging worden de klant en alle gerelateerde gegevens onherstelbaar.", + "delete-customers-title": "Weet u zeker dat u { count, plural, =1 {1 customer} andere {# customers} } wilt verwijderen?", + "delete-customers-action-title": "Verwijder { count, plural, =1 {1 customer} andere {# customers} }", + "delete-customers-text": "Opgelet, na de bevestiging worden alle geselecteerde klanten verwijderd en kunnen alle gerelateerde gegevens niet meer worden hersteld.", + "manage-user-groups": "Gebruikersgroepen beheren", + "manage-asset-groups": "Asset groepen beheren", + "manage-device-groups": "Asset groepen beheren", + "manage-dashboard-groups": "Dashboardgroepen beheren", + "manage-entity-view-groups": "Entiteitsweergavegroepen beheren", + "manage-edge-groups": "Edge-groepen beheren", + "manage-users": "Gebruikers beheren", + "manage-assets": "Assets beheren", + "manage-devices": "Devices beheren", + "manage-dashboards": "Dashboards beheren", + "manage-entity-views": "Entiteitsweergaven beheren", + "title": "Titel", + "title-required": "Titel is vereist.", + "title-max-length": "Titel moet kleiner zijn dan 256 tekens", + "description": "Omschrijving: __________", + "details": "Details", + "events": "Events", + "copyId": "Kopieer klant-ID", + "idCopiedMessage": "Klant-ID is gekopieerd naar klembord", + "select-customer": "Selecteer klant", + "no-customers-matching": "Er zijn geen klanten gevonden die overeenkomen met '{{entity}}'.", + "customer-required": "Klant is verplicht", + "select-group-to-add": "Selecteer doelgroep om geselecteerde klanten toe te voegen", + "select-group-to-move": "Selecteer doelgroep om geselecteerde klanten te verhuizen", + "remove-customers-from-group": "Weet je zeker dat je { count, plural, =1 {1 customer} andere {# customers} } uit groep '{{entityGroup}}' wilt verwijderen?", + "group": "Groep klanten", + "list-of-groups": "{ count, plural, =1 {One customer group} andere {List of # customer groups} }", + "group-name-starts-with": "Klantgroepen waarvan de naam begint met '{{prefix}}'", + "select-default-customer": "Selecteer standaardklant", + "default-customer": "Standaard klant", + "default-customer-required": "Standaardklant is vereist om fouten op te sporen in het dashboard op tenantniveau", + "allow-white-labeling": "White Labeling toestaan", + "search": "Klanten zoeken", + "selected-customers": "{ count, plural, =1 {1 customer} andere {# customers} } geselecteerd", + "edges": "Edge-instanties van de klant", + "manage-edges": "Edge beheren" + }, + "customers-hierarchy": { + "customers-hierarchy": "Hiërarchie van klanten", + "open-nav-tree": "Navigatiestructuur openen", + "return-to-top-level": "Terug naar het hoogste niveau" + }, + "custom-menu": { + "custom-menu": "Aangepast menu", + "custom-menu-hint": "Definieer hieronder de JSON van het aangepaste menu. Deze JSON bevat een lijst met aangepaste menu-items." + }, + "custom-translation": { + "custom-translation": "Aangepaste vertaling", + "translation-map": "Vertaling kaart", + "key": "Vertaling sleutel", + "import": "Vertaling importeren", + "export": "Vertaling exporteren", + "export-data": "Vertaalgegevens exporteren", + "import-data": "Vertaalgegevens importeren", + "translation-file": "Vertaal bestand", + "invalid-translation-file-error": "Kan vertaalbestand niet importeren: Ongeldige gegevensstructuur voor vertalingen.", + "custom-translation-hint": "Definieer hieronder de JSON voor aangepaste vertalingen. Deze JSON overschrijft de standaardvertaling. Klik op 'Download locale file' om een bestaande vertaling te krijgen. U kunt het gedownloade bestand ook gebruiken als referentie voor beschikbare sleutel-waardeparen voor vertalingen.", + "download-locale-file": "Landinstellingsbestand downloaden" + }, + "datetime": { + "date-from": "Datum van", + "time-from": "Tijd van", + "date-to": "Datum tot", + "time-to": "Tijd om" + }, + "dashboard": { + "all": "Alle", + "all-dashboards": "Alle dashboards", + "groups": "Groepen", + "shared": "Gedeeld", + "dashboard": "Dashboard", + "dashboards": "Dashboards", + "management": "Beheer van dashboards", + "view-dashboards": "Dashboards bekijken", + "add": "Dashboard toevoegen", + "assign-dashboard-to-customer": "Dashboard(s) toewijzen aan klant", + "assign-dashboard-to-customer-text": "Selecteer de dashboards die u aan de klant wilt toewijzen", + "assign-to-customer-text": "Selecteer de klant om de dashboard(s) toe te wijzen", + "assign-to-customer": "Toewijzen aan klant", + "unassign-from-customer": "Toewijzing van klant ongedaan maken", + "make-public": "Dashboard openbaar maken", + "make-private": "Dashboard privé maken", + "manage-assigned-customers": "Toegewezen klanten beheren", + "assigned-customers": "Toegewezen klanten", + "assign-to-customers": "Dashboard(s) toewijzen aan klanten", + "assign-to-customers-text": "Selecteer de klanten om de dashboard(s) toe te wijzen", + "unassign-from-customers": "Toewijzing van dashboard(s) van klanten ongedaan maken", + "unassign-from-customers-text": "Selecteer de klanten die u wilt opheffen in de dashboard(s)", + "no-dashboards-text": "Geen dashboards gevonden", + "no-widgets": "Geen widgets geconfigureerd", + "add-widget": "Nieuwe widget toevoegen", + "title": "Titel", + "image": "Dashboard afbeelding", + "mobile-app-settings": "Instellingen voor mobiele applicaties", + "mobile-order": "Dashboardbestelling in mobiele applicatie", + "mobile-hide": "Dashboard verbergen in mobiele applicatie", + "update-image": "Dashboardafbeelding bijwerken", + "take-screenshot": "Screenshot maken", + "select-widget-title": "Selecteer widget", + "select-widget-value": "{{title}}: Widget selecteren", + "select-widget-subtitle": "Lijst met beschikbare widgettypen", + "delete": "Dashboard verwijderen", + "title-required": "Titel is vereist.", + "title-max-length": "Titel moet kleiner zijn dan 256 tekens tekens", + "description": "Omschrijving: __________", + "details": "Details", + "dashboard-details": "Details van het dashboard", + "add-dashboard-text": "Nieuw dashboard toevoegen", + "assign-dashboards": "Dashboards toewijzen", + "assign-new-dashboard": "Nieuw dashboard toewijzen", + "assign-dashboards-text": "Wijs { count, plural, =1 {1 dashboard} andere {# dashboards} } toe aan klanten", + "unassign-dashboards-action-text": "Toewijzing { count, plural, =1 {1 dashboard} andere {# dashboards} } van klanten ongedaan maken", + "delete-dashboards": "Dashboards verwijderen", + "unassign-dashboards": "Toewijzing van dashboards ongedaan maken", + "unassign-dashboards-action-title": "Toewijzing { count, plural, =1 {1 dashboard} andere {# dashboards} } van klant ongedaan maken", + "delete-dashboard-title": "Weet je zeker dat je de '{{dashboardTitle}}' van het dashboard wilt verwijderen?", + "delete-dashboard-text": "Opgelet, na de bevestiging worden het dashboard en alle gerelateerde gegevens onherstelbaar.", + "delete-dashboards-title": "Weet u zeker dat u { count, plural, =1 {1 dashboard} andere {# dashboards} } wilt verwijderen?", + "delete-dashboards-action-title": "Verwijder { count, plural, =1 {1 dashboard} andere {# dashboards} }", + "delete-dashboards-text": "Opgelet, na de bevestiging worden alle geselecteerde dashboards verwijderd en worden alle gerelateerde gegevens onherstelbaar.", + "unassign-dashboard-title": "Weet u zeker dat u de toewijzing van het dashboard '{{dashboardTitle}}' wilt opheffen?", + "unassign-dashboard-text": "Na de bevestiging wordt de toewijzing van het dashboard ongedaan gemaakt en is het niet toegankelijk voor de klant.", + "unassign-dashboard": "Toewijzing dashboard ongedaan maken", + "unassign-dashboards-title": "Weet u zeker dat u de toewijzing van { count, plural, =1 {1 dashboard} andere {# dashboards} } wilt opheffen?", + "unassign-dashboards-text": "Na de bevestiging worden alle geselecteerde dashboards ongedaan gemaakt en zijn ze niet toegankelijk voor de klant.", + "public-dashboard-title": "Dashboard is nu openbaar", + "public-dashboard-text": "De {{dashboardTitle}} van uw dashboard is nu openbaar en toegankelijk via de volgende openbare link:", + "public-dashboard-notice": "notitie: Vergeet niet om gerelateerde devices openbaar te maken om toegang te krijgen tot hun gegevens.", + "public-dashboard-link": "Koppeling naar openbaar dashboard", + "public-dashboard-link-text": "Uw openbare dashboard {{dashboardTitle}} is toegankelijk via de volgende openbare link:", + "public-dashboard-link-notice": "notitie: Vergeet niet om gerelateerde Devices, asset en entiteitsweergaven openbaar te maken om toegang te krijgen tot hun gegevens.", + "make-private-dashboard-title": "Weet je zeker dat je het dashboard '{{dashboardTitle}}' privé wilt maken?", + "make-private-dashboard-text": "Na de bevestiging wordt het dashboard privé gemaakt en is het niet toegankelijk voor anderen.", + "make-private-dashboard": "Dashboard privé maken", + "socialshare-text": "'{{dashboardTitle}}' mogelijk gemaakt door ThingsBoard", + "socialshare-title": "'{{dashboardTitle}}' mogelijk gemaakt door ThingsBoard", + "select-dashboard": "Selecteer dashboard", + "no-dashboards-matching": "Er zijn geen dashboards gevonden die overeenkomen met '{{entity}}'.", + "dashboard-required": "Dashboard is vereist.", + "select-existing": "Bestaand dashboard selecteren", + "create-new": "Nieuw dashboard maken", + "new-dashboard-title": "Nieuwe dashboardtitel", + "open-dashboard": "Dashboard openen", + "set-background": "Achtergrond instellen", + "background-color": "Achtergrondkleur", + "background-image": "Achtergrondafbeelding", + "background-size-mode": "Modus voor achtergrondgrootte", + "no-image": "Geen afbeelding geselecteerd", + "empty-image": "Geen afbeelding", + "drop-image": "Zet een afbeelding neer of klik om een bestand te selecteren om te uploaden.", + "maximum-upload-file-size": "Maximale bestandsgrootte uploaden: {{ size }}", + "cannot-upload-file": "Kan bestand niet uploaden", + "settings": "Instellingen", + "layout-settings": "Lay-out instellingen", + "columns-count": "Aantal kolommen", + "columns-count-required": "Het aantal kolommen is vereist.", + "min-columns-count-message": "Er is slechts een minimum van 10 kolommen toegestaan.", + "max-columns-count-message": "Er is slechts een maximum van 1000 kolommen toegestaan.", + "widgets-margins": "Marge tussen widgets", + "margin-required": "Margewaarde is vereist.", + "min-margin-message": "Alleen 0 is toegestaan als minimale margewaarde.", + "max-margin-message": "Slechts 50 is toegestaan als maximale margewaarde.", + "horizontal-margin": "Horizontale marge", + "horizontal-margin-required": "Horizontale margewaarde is vereist.", + "min-horizontal-margin-message": "Alleen 0 is toegestaan als minimale horizontale margewaarde.", + "max-horizontal-margin-message": "Slechts 50 is toegestaan als maximale horizontale margewaarde.", + "vertical-margin": "Verticale marge", + "vertical-margin-required": "Verticale margewaarde is vereist.", + "min-vertical-margin-message": "Alleen 0 is toegestaan als minimale verticale margewaarde.", + "max-vertical-margin-message": "Slechts 50 is toegestaan als maximale verticale margewaarde.", + "apply-outer-margin": "Marges toepassen op de zijkanten van de lay-out", + "autofill-height": "Lay-outhoogte automatisch vullen", + "mobile-layout": "Mobiele lay-outinstellingen", + "mobile-row-height": "Mobiele rijhoogte, px", + "mobile-row-height-required": "De waarde van de rijhoogte van de mobiele rij is vereist.", + "min-mobile-row-height-message": "Er zijn slechts 5 pixels toegestaan als minimale waarde voor de rijhoogte van mobiele rijen.", + "max-mobile-row-height-message": "Er zijn slechts 200 pixels toegestaan als maximale waarde voor de rijhoogte van mobiele Devices.", + "title-settings": "Titelinstellingen", + "display-title": "Dashboardtitel weergeven", + "title-color": "Titel kleur", + "toolbar-settings": "Werkbalk-instellingen", + "hide-toolbar": "Werkbalk verbergen", + "toolbar-always-open": "Werkbalk open houden", + "display-dashboards-selection": "Dashboardselectie weergeven", + "display-entities-selection": "Selectie van weergave-entiteiten", + "display-filters": "Filters weergeven", + "display-dashboard-timewindow": "Tijdvenster weergeven", + "display-dashboard-export": "Export weergeven", + "display-update-dashboard-image": "Afbeelding van updatedashboard weergeven", + "dashboard-logo-settings": "Instellingen voor dashboardlogo's", + "display-dashboard-logo": "Logo weergeven in volledig scherm dashboard", + "dashboard-logo-image": "Afbeelding van het dashboardlogo", + "advanced-settings": "Geavanceerde instellingen", + "dashboard-css": "Dashboard CSS", + "import": "Dashboard importeren", + "export": "Dashboard exporteren", + "export-failed-error": "Dashboard kan niet worden geëxporteerd: {{error}}", + "export-pdf": "Exporteren als PDF", + "export-png": "Exporteren als PNG", + "export-jpg": "Exporteren als JPEG", + "export-json-config": "JSON-configuratie exporteren", + "download-dashboard-progress": "Dashboard genereren {{reportType}} ...", + "create-new-dashboard": "Nieuw dashboard maken", + "dashboard-file": "Dashboard bestand", + "invalid-dashboard-file-error": "Dashboard kan niet worden geïmporteerd: Ongeldige gegevensstructuur van het dashboard.", + "dashboard-import-missing-aliases-title": "Aliassen configureren die worden gebruikt door geïmporteerd dashboard", + "create-new-widget": "Nieuwe widget maken", + "import-widget": "Widget importeren", + "widget-file": "Widget bestand", + "invalid-widget-file-error": "Widget kan niet worden geïmporteerd: Ongeldige gegevensstructuur van de widget.", + "widget-import-missing-aliases-title": "Aliassen configureren die worden gebruikt door geïmporteerde widgets", + "open-toolbar": "Dashboardwerkbalk openen", + "close-toolbar": "Werkbalk sluiten", + "configuration-error": "Configuratie fout", + "alias-resolution-error-title": "Configuratiefout voor dashboardaliassen", + "invalid-aliases-config": "Kan geen devices vinden die overeenkomen met sommige aliassenfilter.
Neem contact op met uw beheerder om dit probleem op te lossen.", + "select-devices": "Devices selecteren", + "assignedToCustomer": "Toegewezen aan klant", + "assignedToCustomers": "Toegewezen aan klanten", + "public": "Publiek", + "copyId": "Dashboard-id kopiëren", + "idCopiedMessage": "Dashboard-id is gekopieerd naar klembord", + "public-link": "Openbare link", + "copy-public-link": "Kopieer openbare link", + "public-link-copied-message": "De openbare koppeling naar het dashboard is gekopieerd naar het klembord", + "manage-states": "Dashboardstatussen beheren", + "states": "Statussen van dashboards", + "search-states": "Statussen van zoekdashboard", + "selected-states": "{ count, plural, =1 {1 dashboard state} andere {# dashboard states} } geselecteerd", + "edit-state": "Dashboardstatus bewerken", + "delete-state": "Dashboardstatus verwijderen", + "add-state": "Dashboardstatus toevoegen", + "no-states-text": "Geen staten gevonden", + "state": "Status van het dashboard", + "state-name": "Naam", + "state-name-required": "De naam van de dashboardstatus is vereist.", + "state-id": "Staats-ID", + "state-id-required": "Status-id van het dashboard is vereist.", + "state-id-exists": "De status van het dashboard met dezelfde id bestaat al.", + "is-root-state": "Wortel staat", + "delete-state-title": "Dashboardstatus verwijderen", + "delete-state-text": "Weet u zeker dat u de status van het dashboard met de naam '{{stateName}}' wilt verwijderen?", + "show-details": "Details weergeven", + "hide-details": "Verberg details", + "select-state": "Selecteer de doelstatus", + "state-controller": "Controle van de staat", + "search": "Dashboards doorzoeken", + "selected-dashboards": "{ count, plural, =1 {1 dashboard} andere {# dashboards} } geselecteerd", + "home-dashboard": "Startpagina dashboard", + "home-dashboard-hide-toolbar": "Verberg de werkbalk van het startdashboard", + "select-group-to-add": "Selecteer doelgroep om geselecteerde dashboards toe te voegen", + "select-group-to-move": "Selecteer de doelgroep om geselecteerde dashboards te verplaatsen", + "remove-dashboards-from-group": "Weet je zeker dat je { count, plural, =1 {1 dashboard} andere {# dashboards} } uit groep '{{entityGroup}}' wilt verwijderen?", + "group": "Groep dashboards", + "list-of-groups": "{ count, plural, =1 {One dashboard group} andere {List of # dashboard groups} }", + "group-name-starts-with": "Dashboardgroepen waarvan de naam begint met '{{prefix}}'", + "unassign-dashboard-from-edge-text": "Na de bevestiging wordt de toewijzing van het dashboard ongedaan gemaakt en is het niet toegankelijk voor de edge.", + "unassign-dashboards-from-edge-title": "Weet u zeker dat u de toewijzing van { count, plural, =1 {1 dashboard} andere {# dashboards} } wilt opheffen?", + "unassign-dashboards-from-edge-text": "Na de bevestiging worden alle geselecteerde dashboards ongedaan gemaakt en zijn ze niet toegankelijk voor de edge.", + "assign-dashboard-to-edge": "Dashboard(s) toewijzen aan Edge", + "assign-dashboard-to-edge-text": "Selecteer de dashboards die u aan de edge wilt toewijzen", + "non-existent-dashboard-state-error": "Dashboardstatus met id '{{ stateId }}' is niet gevonden" + }, + "datakey": { + "settings": "Instellingen", + "advanced": "Geavanceerd", + "label": "Etiket", + "color": "Kleur", + "units": "Speciaal symbool om naast waarde weer te geven", + "decimals": "Aantal cijfers na drijvende komma", + "data-generation-func": "Functie voor het genereren van gegevens", + "use-data-post-processing-func": "Gebruik de functie voor het nabewerken van gegevens", + "configuration": "Configuratie van gegevenssleutels", + "timeseries": "Timeseries", + "attributes": "Attributen", + "entity-field": "Entiteit veld", + "alarm": "Alarmvelden", + "timeseries-required": "Timeseries van entiteiten zijn vereist.", + "timeseries-or-attributes-required": "Timeseries/attributen van entiteiten zijn vereist.", + "alarm-fields-timeseries-or-attributes-required": "Alarmvelden of tijdreeksen/attributen van entiteiten zijn vereist.", + "maximum-timeseries-or-attributes": "Maximaal { count, plural, =1 {1 timeseries/attribute is allowed.} andere {# timeseries/attributes are allowed} }", + "alarm-fields-required": "Alarmvelden zijn verplicht.", + "function-types": "Soorten functies", + "function-type": "Soort functie", + "function-types-required": "Functietypes zijn vereist.", + "alarm-keys": "Alarmgegevens toetsen", + "alarm-key": "Sleutel voor alarmgegevens", + "alarm-key-functions": "Functies van de alarmtoets", + "alarm-key-function": "Alarmtoets functie", + "latest-keys": "Nieuwste gegevenssleutels", + "latest-key": "Meest recente gegevenssleutel", + "latest-key-functions": "Nieuwste sleutelfuncties", + "latest-key-function": "Nieuwste toetsfunctie", + "timeseries-keys": "Gegevenssleutels voor tijdreeksen", + "timeseries-key": "Gegevenssleutel tijdreeksen", + "timeseries-key-functions": "Timeseries belangrijkste functies", + "timeseries-key-function": "Tijdreeks-sleutelfunctie", + "maximum-function-types": "Maximaal { count, plural, =1 {1 function type is allowed.} andere {# function types are allowed} }", + "time-description": "tijdstempel van de huidige waarde;", + "value-description": "de huidige waarde;", + "prev-value-description": "resultaat van de vorige functieaanroep;", + "time-prev-description": "tijdstempel van de vorige waarde;", + "prev-orig-value-description": "oorspronkelijke vorige waarde;", + "aggregation": "Aggregatie", + "aggregation-type-hint-common": "Om prestatieredenen is de berekening van de geaggregeerde waarden alleen beschikbaar voor vaste tijdsintervallen zoals 'huidige dag', 'huidige maand', enz., en niet voor schuifvensterintervallen zoals 'laatste 30 minuten' of 'laatste 24 uur'.", + "aggregation-type-none-hint": "Neem de laatste waarde.", + "aggregation-type-min-hint": "Zoek de minimumwaarde tussen gegevenspunten binnen een geselecteerd tijdvenster.", + "aggregation-type-max-hint": "Zoek de maximale waarde tussen gegevenspunten binnen een geselecteerd tijdvenster.", + "aggregation-type-avg-hint": "Bereken een gemiddelde waarde tussen gegevenspunten binnen een geselecteerd tijdvenster.", + "aggregation-type-sum-hint": "Tel alle waarden van de gegevenspunten binnen een geselecteerd tijdvenster bij elkaar op.", + "aggregation-type-count-hint": "Het totale aantal gegevenspunten binnen een geselecteerd tijdvenster.", + "delta-calculation": "Delta-berekening", + "enable-delta-calculation": "Deltaberekening inschakelen", + "enable-delta-calculation-hint": "Als deze optie is ingeschakeld, wordt de waarde van de gegevenssleutel berekend op basis van de geaggregeerde waarden voor een geselecteerd tijdvenster en een opgegeven vergelijkingsperiode. Om prestatieredenen is de deltaberekening alleen beschikbaar voor historische tijdvensters en niet voor realtimewaarden. U kunt bijvoorbeeld de delta berekenen tussen het energieverbruik van gisteren ten opzichte van het energieverbruik van eergisteren.", + "delta-calculation-result": "Resultaat van de deltaberekening", + "delta-calculation-result-previous-value": "Vorige waarde", + "delta-calculation-result-delta-absolute": "Delta (absoluut)", + "delta-calculation-result-delta-percent": "Delta (procent)" + }, + "datasource": { + "type": "Type gegevensbron", + "name": "Naam", + "label": "Etiket", + "add-datasource-prompt": "Voeg gegevensbron toe" + }, + "details": { + "details": "Details", + "edit-mode": "Modus bewerken", + "edit-json": "JSON bewerken", + "toggle-edit-mode": "Bewerkingsmodus in-/uitschakelen" + }, + "device": { + "all": "Alle", + "all-devices": "Alle Devices", + "groups": "Groepen", + "shared": "Gedeeld", + "device": "Device", + "device-required": "Device is vereist.", + "devices": "Devices", + "management": "Device beheer", + "view-devices": "Devices bekijken", + "device-alias": "Alias van het device", + "device-type-max-length": "Device type moet kleiner zijn dan 256 tekens", + "aliases": "Aliassen van Devices", + "no-alias-matching": "'{{alias}}' niet gevonden.", + "no-aliases-found": "Geen aliassen gevonden.", + "no-key-matching": "'{{key}}' niet gevonden.", + "no-keys-found": "Geen sleutels gevonden.", + "create-new-alias": "Maak een nieuwe aan!", + "create-new-key": "Maak een nieuwe aan!", + "duplicate-alias-error": "Dubbele alias gevonden '{{alias}}'.
Apparaataliassen moeten uniek zijn binnen het dashboard.", + "configure-alias": "Configureer de alias '{{alias}}'", + "no-devices-matching": "Er zijn geen devices gevonden die overeenkomen met '{{entity}}'.", + "alias": "Alias", + "alias-required": "Device alias is vereist.", + "remove-alias": "Device alias verwijderen", + "add-alias": "Device alias toevoegen", + "name-starts-with": "Expressie van device naam", + "help-text": "Gebruik '%' naar behoefte: '%device_name_contains%', '%device_name_ends', 'device_starts_with'.", + "device-list": "Lijst met Devices", + "use-device-name-filter": "Filter gebruiken", + "device-list-empty": "Geen devices geselecteerd.", + "device-name-filter-required": "Apparaatnaamfilter is vereist.", + "device-name-filter-no-device-matched": "Er zijn geen devices gevonden die beginnen met '{{device}}'.", + "add": "Device toevoegen", + "assign-to-customer": "Toewijzen aan klant", + "assign-device-to-customer": "Device(en) toewijzen aan klant", + "assign-device-to-customer-text": "Selecteer de devices die u aan de klant wilt toewijzen", + "make-public": "Device openbaar maken", + "make-private": "Device privé maken", + "no-devices-text": "Geen devices gevonden", + "assign-to-customer-text": "Selecteer de klant om het/het device(en) toe te wijzen", + "device-details": "Gegevens van het device", + "add-device-text": "Nieuw device toevoegen", + "credentials": "Geloofsbrief", + "manage-credentials": "Inloggegevens beheren", + "delete": "Device verwijderen", + "assign-devices": "Devices toewijzen", + "assign-devices-text": "Wijs { count, plural, =1 {1 device} andere {# devices} } toe aan de klant", + "delete-devices": "Devices verwijderen", + "unassign-from-customer": "Toewijzing van klant ongedaan maken", + "unassign-devices": "Toewijzing van devices ongedaan maken", + "unassign-devices-action-title": "Toewijzing { count, plural, =1 {1 device} andere {# devices} } van klant ongedaan maken", + "unassign-device-from-edge-title": "Weet u zeker dat u de toewijzing van het device '{{deviceName}}' wilt opheffen?", + "unassign-device-from-edge-text": "Na de bevestiging wordt de toewijzing van het device ongedaan gemaakt en is het niet toegankelijk via de edge.", + "unassign-devices-from-edge": "Toewijzing van devices aan de edge ongedaan maken", + "assign-new-device": "Nieuw device toewijzen", + "make-public-device-title": "Weet je zeker dat je het device '{{deviceName}}' openbaar wilt maken?", + "make-public-device-text": "Na de bevestiging worden het device en al zijn gegevens openbaar gemaakt en toegankelijk gemaakt voor anderen.", + "make-private-device-title": "Weet je zeker dat je het device '{{deviceName}}' privé wilt maken?", + "make-private-device-text": "Na de bevestiging worden het device en al zijn gegevens privé gemaakt en zijn ze niet toegankelijk voor anderen.", + "view-credentials": "Inloggegevens bekijken", + "delete-device-title": "Weet u zeker dat u de '{{deviceName}}' van het device wilt verwijderen?", + "delete-device-text": "Opgelet, na de bevestiging worden het device en alle gerelateerde gegevens onherstelbaar.", + "delete-devices-title": "Weet u zeker dat u { count, plural, =1 {1 device} andere {# devices} } wilt verwijderen?", + "delete-devices-action-title": "Verwijder { count, plural, =1 {1 device} andere {# devices} }", + "delete-devices-text": "Opgelet, na de bevestiging worden alle geselecteerde devices verwijderd en kunnen alle gerelateerde gegevens niet meer worden hersteld.", + "unassign-device-title": "Weet u zeker dat u de toewijzing van het device '{{deviceName}}' wilt opheffen?", + "unassign-device-text": "Na de bevestiging wordt de toewijzing van het device ongedaan gemaakt en is het niet toegankelijk voor de klant.", + "unassign-device": "Toewijzing van device ongedaan maken", + "unassign-devices-title": "Weet u zeker dat u de toewijzing van { count, plural, =1 {1 device} andere {# devices} } wilt opheffen?", + "unassign-devices-text": "Na de bevestiging worden alle geselecteerde devices ongedaan gemaakt en zijn ze niet toegankelijk voor de klant.", + "device-credentials": "Inloggegevens van het device", + "loading-device-credentials": "Devicereferenties worden geladen...", + "credentials-type": "Type referenties", + "access-token": "Toegang token", + "access-token-required": "Toegangstoken is vereist.", + "access-token-invalid": "De lengte van het toegangstoken moet tussen de 1 en 32 tekens zijn.", + "certificate-pem-format": "Certificaat in PEM-formaat", + "certificate-pem-format-required": "Certificaat is vereist.", + "copy-access-token": "Toegangstoken kopiëren", + "copy-certificate": "Kopie certificaat", + "copy-client-id": "Client-ID kopiëren", + "copy-user-name": "Gebruikersnaam kopiëren", + "copy-password": "Kopieer wachtwoord", + "generate-client-id": "Client-ID genereren", + "generate-user-name": "Gebruikersnaam genereren", + "generate-password": "Wachtwoord genereren", + "generate-access-token": "Toegangstoken genereren", + "lwm2m-security-config": { + "identity": "Identiteit van de klant", + "identity-required": "Clientidentiteit is vereist.", + "identity-tooltip": "De PSK-identifier is een willekeurige PSK-identifier van maximaal 128 bytes, zoals beschreven in de standaard [RFC7925].\nDe PSK-id MOET eerst worden geconverteerd naar een tekenreeks en vervolgens worden gecodeerd in octetten met behulp van UTF-8.", + "client-key": "Client-sleutel", + "client-key-required": "Clientsleutel is vereist.", + "client-key-tooltip-prk": "RPK openbare sleutel of id moet in de standaard [RFC7250] en gecodeerd zijn naar Base64-indeling!", + "client-key-tooltip-psk": "PSK-sleutel moet in het standaard [RFC4279] en HexDec-formaat zijn: 32, 64, 128 tekens!", + "endpoint": "Naam van endpoint", + "endpoint-required": "De naam van de endpoint is vereist.", + "client-public-key": "Openbare sleutel van client", + "client-public-key-hint": "Als de openbare sleutel van de client leeg is, wordt het vertrouwde certificaat gebruikt", + "client-public-key-tooltip": "De openbare sleutel X509 moet in DER-gecodeerd X509v3-formaat zijn en uitsluitend het EC-algoritme ondersteunen en vervolgens worden gecodeerd naar het Base64-formaat!", + "mode": "Beveiligingsconfiguratiemodus", + "client-tab": "Configuratie voor clientbeveiliging", + "client-certificate": "Certificaat van de klant", + "bootstrap-tab": "Bootstrap-client", + "bootstrap-server": "Bootstrap-server", + "lwm2m-server": "LwM2M-server", + "client-publicKey-or-id": "Openbare sleutel of id van de client", + "client-publicKey-or-id-required": "De openbare sleutel of id van de client is vereist.", + "client-publicKey-or-id-tooltip-psk": "De PSK-identifier is een willekeurige PSK-identifier van maximaal 128 bytes, zoals beschreven in de standaard [RFC7925].\nDe PSK-id MOET eerst worden geconverteerd naar een tekenreeks en vervolgens worden gecodeerd in octetten met behulp van UTF-8.", + "client-publicKey-or-id-tooltip-rpk": "RPK openbare sleutel of id moet in de standaard [RFC7250] en gecodeerd zijn naar Base64-indeling!", + "client-publicKey-or-id-tooltip-x509": "De openbare X509-sleutel moet in DER-gecodeerd X509v3-formaat zijn en uitsluitend het EC-algoritme ondersteunen en vervolgens worden gecodeerd in Base64-indeling", + "client-secret-key": "Geheime sleutel van de client", + "client-secret-key-required": "Client Secret Key is vereist.", + "client-secret-key-tooltip-psk": "PSK-sleutel moet in het standaard [RFC4279] en HexDec-formaat zijn: 32, 64, 128 tekens!", + "client-secret-key-tooltip-prk": "De geheime RPK-sleutel moet in PKCS_8 formaat zijn (DER-codering, standaard [RFC5958]) en vervolgens worden gecodeerd in Base64-formaat!", + "client-secret-key-tooltip-x509": "De geheime sleutel X509 moet in PKCS_8 formaat zijn (DER-codering, standaard [RFC5958]) en vervolgens worden gecodeerd in Base64-formaat!" + }, + "client-id": "Client-ID", + "client-id-pattern": "Bevat een ongeldig teken.", + "user-name": "Gebruikersnaam", + "user-name-required": "Gebruikersnaam is vereist.", + "client-id-or-user-name-necessary": "Client-ID en/of gebruikersnaam zijn noodzakelijk", + "password": "Wachtwoord", + "secret": "Geheim", + "secret-required": "Geheim is vereist.", + "device-type": "Device type", + "device-type-required": "Device type is vereist.", + "select-device-type": "Selecteer device type", + "enter-device-type": "Voer het device type in", + "any-device": "Elk device", + "no-device-types-matching": "Er zijn geen device typen gevonden die overeenkomen met '{{entitySubtype}}'.", + "device-type-list-empty": "Geen device typen geselecteerd.", + "device-types": "Device sorrten", + "name": "Naam", + "name-required": "Naam is verplicht.", + "name-max-length": "Naam moet kleiner zijn dan 256 tekens tekens", + "label-max-length": "Etiket moet kleiner zijn dan 256 tekens tekens", + "description": "Omschrijving: __________", + "label": "Etiket", + "events": "Events", + "details": "Details", + "copyId": "Device-ID kopiëren", + "copyAccessToken": "Toegangstoken kopiëren", + "copy-mqtt-authentication": "MQTT-inloggegevens kopiëren", + "idCopiedMessage": "Device-ID is gekopieerd naar klembord", + "accessTokenCopiedMessage": "Token voor apparaattoegang is gekopieerd naar het klembord", + "mqtt-authentication-copied-message": "MQTT-verificatie van device is gekopieerd naar klembord", + "assignedToCustomer": "Toegewezen aan klant", + "unable-delete-device-alias-title": "Kan apparaatalias niet verwijderen", + "unable-delete-device-alias-text": "Device alias '{{deviceAlias}}' kan niet worden verwijderd omdat deze wordt gebruikt door de volgende widget(s):
{{widgetsList}}", + "is-gateway": "Is gateway", + "overwrite-activity-time": "Activiteitstijd overschrijven voor verbonden device", + "device-filter": "Device filter", + "device-filter-title": "Device filter", + "filter-title": "Filter", + "device-state": "Status van het device", + "state": "Staat", + "any": "Enig", + "active": "Actief", + "inactive": "Inactief", + "public": "Publiek", + "device-public": "Device is openbaar", + "select-device": "Selecteer device", + "select-group-to-add": "Selecteer de doelgroep om geselecteerde devices toe te voegen", + "select-group-to-move": "Selecteer de doelgroep om geselecteerde devices te verplaatsen", + "remove-devices-from-group": "Weet je zeker dat je { count, plural, =1 {1 device} andere {# devices} } uit groep '{{entityGroup}}' wilt verwijderen?", + "group": "Device groep", + "list-of-groups": "{ count, plural, =1 {One device group} andere {List of # device groups} }", + "group-name-starts-with": "Asset groepen waarvan de naam begint met '{{prefix}}'", + "import": "Device importeren", + "device-file": "Device bestand", + "search": "Devices zoeken", + "selected-devices": "{ count, plural, =1 {1 device} andere {# devices} } geselecteerd", + "device-configuration": "Configuratie van het device", + "transport-configuration": "Configuratie van het transport", + "wizard": { + "device-wizard": "Device Wizard", + "device-details": "Gegevens van het device", + "new-device-profile": "Nieuw device profiel maken", + "existing-device-profile": "Bestaand device profiel selecteren", + "specific-configuration": "Specifieke configuratie", + "customer-to-assign-device": "Klant om het device toe te wijzen", + "add-credentials": "Inloggegevens toevoegen" + }, + "unassign-devices-from-edge-title": "Weet u zeker dat u de toewijzing van { count, plural, =1 {1 device} andere {# devices} } wilt opheffen?", + "unassign-devices-from-edge-text": "Na de bevestiging worden alle geselecteerde devices ongedaan gemaakt en zijn ze niet toegankelijk via de edge." + }, + "asset-profile": { + "asset-profile": "Asset profiel", + "asset-profiles": "Asset profielen", + "all-asset-profiles": "Alle", + "add": "Asset profiel toevoegen", + "edit": "Asset profiel bewerken", + "asset-profile-details": "Details van de asset profiel", + "no-asset-profiles-text": "Geen assetprofielen gevonden", + "search": "Assetprofielen zoeken", + "selected-asset-profiles": "{ count, plural, =1 {1 asset profile} andere {# asset profiles} } geselecteerd", + "no-asset-profiles-matching": "Er zijn geen asset profielen gevonden die overeenkomen met '{{entity}}'.", + "asset-profile-required": "Asset profiel is vereist", + "idCopiedMessage": "De ID van het itemprofiel is gekopieerd naar het klembord", + "set-default": "Itemprofiel instellen als standaard", + "delete": "Asset profiel verwijderen", + "copyId": "Itemprofiel-ID kopiëren", + "name-max-length": "Naam moet kleiner zijn dan 256 tekens", + "new-device-profile-name": "Naam van itemprofiel", + "new-device-profile-name-required": "De naam van de asset profiel is vereist.", + "name": "Naam", + "name-required": "Naam is verplicht.", + "image": "Afbeelding van assetprofiel", + "description": "Omschrijving: __________", + "default": "Verstek", + "default-rule-chain": "Keten van standaardregels", + "default-edge-rule-chain": "Standaard rule chain", + "default-edge-rule-chain-hint": "Wordt aan de edge gebruikt als rule chain om inkomende gegevens voor asset van dit asset profiel te verwerken", + "mobile-dashboard": "Mobiel dashboard", + "mobile-dashboard-hint": "Gebruikt door mobiele applicatie als dashboard met activagegevens", + "select-queue-hint": "Maak een keuze uit een vervolgkeuzelijst.", + "delete-asset-profile-title": "Weet je zeker dat je de asset profiel '{{assetProfileName}}' wilt verwijderen?", + "delete-asset-profile-text": "Let op, na de bevestiging worden de asset profiel en alle gerelateerde gegevens onherstelbaar.", + "delete-asset-profiles-title": "Weet u zeker dat u { count, plural, =1 {1 asset profile} andere {# asset profiles} } wilt verwijderen?", + "delete-asset-profiles-text": "Opgelet, na de bevestiging worden alle geselecteerde asset profielen verwijderd en worden alle gerelateerde gegevens onherstelbaar.", + "set-default-asset-profile-title": "Weet u zeker dat u de asset profiel standaard op '{{assetProfileName}}' wilt zetten?", + "set-default-asset-profile-text": "Na de bevestiging wordt de asset profiel gemarkeerd als standaard en wordt het gebruikt voor nieuwe asset waarvoor geen profiel is opgegeven.", + "no-asset-profiles-found": "Geen assetprofielen gevonden.", + "create-new-asset-profile": "Maak een nieuwe aan!", + "create-asset-profile": "Nieuw asset profiel maken", + "import": "Asset profiel importeren", + "export": "Asset profiel exporteren", + "export-failed-error": "Kan itemprofiel niet exporteren: {{error}}", + "asset-profile-file": "Asset profiel bestand", + "invalid-asset-profile-file-error": "Kan itemprofiel niet importeren: Ongeldige gegevensstructuur van asset profiel." + }, + "device-profile": { + "device-profile": "Device profiel", + "device-profiles": "Device profielen", + "all-device-profiles": "Alle", + "add": "Device profiel toevoegen", + "edit": "Device profiel bewerken", + "device-profile-details": "Details van device profiel", + "no-device-profiles-text": "Geen device profielen gevonden", + "search": "Device profielen zoeken", + "selected-device-profiles": "{ count, plural, =1 {1 device profile} andere {# device profiles} } geselecteerd", + "no-device-profiles-matching": "Er zijn geen device profielen gevonden die overeenkomen met '{{entity}}'.", + "device-profile-required": "Device profiel is vereist", + "idCopiedMessage": "Device profiel-ID is gekopieerd naar klembord", + "set-default": "Device profiel standaard instellen", + "delete": "Device profiel verwijderen", + "copyId": "Device profiel-ID kopiëren", + "name-max-length": "Naam moet kleiner zijn dan 256 tekens tekens", + "new-device-profile-name": "Profielnaam device", + "new-device-profile-name-required": "De naam van het device profiel is vereist.", + "name": "Naam", + "name-required": "Naam is verplicht.", + "type": "Type profiel", + "type-required": "Profieltype is vereist.", + "type-default": "Verstek", + "image": "Profielafbeelding van device", + "transport-type": "Soort vervoer", + "transport-type-required": "Transporttype is vereist.", + "transport-type-default": "Verstek", + "transport-type-default-hint": "Ondersteunt standaard MQTT-, HTTP- en CoAP-transport", + "transport-type-mqtt": "MQTT", + "transport-type-mqtt-hint": "Maakt geavanceerde MQTT-transportinstellingen mogelijk", + "transport-type-coap": "CoAP", + "transport-type-coap-hint": "Maakt geavanceerde CoAP-transportinstellingen mogelijk", + "transport-type-lwm2m": "LWM2M", + "transport-type-lwm2m-hint": "LWM2M transporttype", + "transport-type-snmp": "SNMP", + "transport-type-snmp-hint": "SNMP-transportconfiguratie opgeven", + "description": "Omschrijving: __________", + "default": "Verstek", + "profile-configuration": "Profiel configuratie", + "transport-configuration": "Configuratie van het transport", + "default-rule-chain": "Standaard rule chain", + "default-edge-rule-chain": "Standaard edge rule chain", + "default-edge-rule-chain-hint": "Wordt aan de edge rule chain om inkomende gegevens te verwerken voor devices met dit device profiel", + "mobile-dashboard": "Mobiel dashboard", + "mobile-dashboard-hint": "Gebruikt door mobiele applicatie als dashboard met device details", + "select-queue-hint": "Maak een keuze uit een vervolgkeuzelijst.", + "delete-device-profile-title": "Weet u zeker dat u het device profiel '{{deviceProfileName}}' wilt verwijderen?", + "delete-device-profile-text": "Opgelet, na de bevestiging worden het device profiel en alle gerelateerde gegevens, inclusief bijbehorende OTA-updates, onherstelbaar.", + "delete-device-profiles-title": "Weet u zeker dat u { count, plural, =1 {1 device profile} andere {# device profiles} } wilt verwijderen?", + "delete-device-profiles-text": "Opgelet, na de bevestiging worden alle geselecteerde device profielen verwijderd en kunnen alle gerelateerde gegevens, inclusief bijbehorende OTA-updates, niet meer worden hersteld.", + "set-default-device-profile-title": "Weet u zeker dat u het device profiel standaard '{{deviceProfileName}}' wilt maken?", + "set-default-device-profile-text": "Na de bevestiging wordt het device profiel als standaard gemarkeerd en wordt het gebruikt voor nieuwe devices waarvoor geen profiel is opgegeven.", + "no-device-profiles-found": "Geen device profielen gevonden.", + "create-new-device-profile": "Maak een nieuwe aan!", + "mqtt-device-topic-filters": "Onderwerpfilters voor MQTT-Devices", + "mqtt-device-topic-filters-unique": "Onderwerpfilters voor MQTT-Devices moeten uniek zijn.", + "mqtt-device-topic-filters-spark-plug": "MQTT Bougie B Edge van het netwerk (EoN) knooppunt.", + "mqtt-device-topic-filters-spark-plug-hint": "Sta verbindingen toe vanaf EoN-rule nodes met Sparkplug B-payload en onderwerpindeling.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names": "SparkPlug-statistieken om op te slaan als attributen.", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names-hint": "Namen van SparkPlug-statistieken die worden opgeslagen als device attributen. Alle andere metrische gegevens worden opgeslagen als device telemetrie.", + "mqtt-device-payload-type": "Nuttige waarde van het MQTT-device", + "mqtt-device-payload-type-json": "JSON", + "mqtt-device-payload-type-proto": "Protobuf", + "mqtt-enable-compatibility-with-json-payload-format": "Schakel compatibiliteit met andere payload-indelingen in.", + "mqtt-enable-compatibility-with-json-payload-format-hint": "Indien ingeschakeld, gebruikt het platform standaard een Protobuf payload-indeling. Als parsen mislukt, probeert het platform de JSON-payloadindeling te gebruiken. Handig voor achterwaartse compatibiliteit tijdens firmware-updates. De eerste release van de firmware gebruikt bijvoorbeeld Json, terwijl de nieuwe release Protobuf gebruikt. Tijdens het proces van firmware-update voor de vloot van devices, is het vereist om zowel Protobuf als JSON tegelijkertijd te ondersteunen. De compatibiliteitsmodus introduceert een lichte prestatievermindering, dus het wordt aanbevolen om deze modus uit te schakelen zodra alle devices zijn bijgewerkt.", + "mqtt-use-json-format-for-default-downlink-topics": "Json-indeling gebruiken voor standaard downlink-onderwerpen", + "mqtt-use-json-format-for-default-downlink-topics-hint": "Indien ingeschakeld, gebruikt het platform de Json-payloadindeling om attributen en RPC te pushen via de volgende onderwerpen: v1/devices/me/attributes/response/$request_id, v1/devices/me/attributes, v1/devices/me/rpc/request/$request_id, v1/devices/me/rpc/response/$request_id. Deze instelling heeft geen invloed op kenmerk- en rpc-abonnementen die worden verzonden met behulp van nieuwe (v2) onderwerpen: v2/a/res/$request_id, v2/a, v2/r/req/$request_id, v2/r/res/$request_id. Waarbij $request_id een id van een geheel getal is.", + "mqtt-send-ack-on-validation-exception": "PUBACK verzenden bij mislukte validatie van PUBLISH-bericht", + "mqtt-send-ack-on-validation-exception-hint": "Standaard sluit het platform de MQTT-sessie bij mislukte berichtvalidatie. Indien ingeschakeld, stuurt het platform een publicatiebevestiging in plaats van de sessie te sluiten.", + "snmp-add-mapping": "SNMP-toewijzing toevoegen", + "snmp-mapping-not-configured": "Geen toewijzing voor OID aan tijdreeks/telemetrie geconfigureerd", + "snmp-timseries-or-attribute-name": "Tijdreeks-/kenmerknaam voor toewijzing", + "snmp-timseries-or-attribute-type": "Tijdreeks/kenmerktype voor toewijzing", + "snmp-method-pdu-type-get-request": "GetRequest (GetAanvraag)", + "snmp-method-pdu-type-get-next-request": "VolgendeAanvraag", + "snmp-oid": "OID", + "transport-device-payload-type-json": "JSON", + "transport-device-payload-type-proto": "Protobuf", + "mqtt-payload-type-required": "Payload-type is vereist.", + "coap-device-type": "Type CoAP-device", + "coap-device-payload-type": "Nuttige lading van CoAP-device", + "coap-device-type-required": "Het type CoAP-device is vereist.", + "coap-device-type-default": "Verstek", + "coap-device-type-efento": "Efento NB-IoT", + "support-level-wildcards": "Enkele [+] en multi-level [#] jokertekens worden ondersteund.", + "telemetry-topic-filter": "Onderwerpfilter voor telemetrie", + "telemetry-topic-filter-required": "Telemetrie-onderwerpfilter is vereist.", + "attributes-topic-filter": "Attributen topic filter", + "attributes-subscribe-topic-filter": "Attributen topic subscribe filter", + "attributes-topic-filter-required": "Attributen topic filter is vereist.", + "attributes-subscribe-topic-filter-required": "Attributen topic subscribe is vereist", + "telemetry-proto-schema": "Telemetrie proto-schema", + "telemetry-proto-schema-required": "Telemetrie proto-schema is vereist.", + "attributes-proto-schema": "Attributen proto-schema", + "attributes-proto-schema-required": "Attributen proto-schema is vereist.", + "rpc-response-proto-schema": "RPC-respons proto-schema", + "rpc-response-proto-schema-required": "RPC-respons proto-schema is vereist.", + "rpc-response-topic-filter": "Onderwerpfilter voor RPC-reacties", + "rpc-response-topic-filter-required": "RPC-responsonderwerpfilter is vereist.", + "rpc-request-proto-schema": "RPC-aanvraag proto-schema", + "rpc-request-proto-schema-required": "RPC-aanvraag proto-schema is vereist.", + "rpc-request-proto-schema-hint": "RPC-aanvraagbericht moet altijd velden bevatten: tekenreeksmethode = 1; int32 requestId = 2; en params = 3 van elk gegevenstype.", + "not-valid-pattern-topic-filter": "Ongeldig topic pattern filter", + "not-valid-single-character": "Ongeldig gebruik van een jokerteken op één niveau", + "not-valid-multi-character": "Ongeldig gebruik van een jokerteken met meerdere niveaus", + "single-level-wildcards-hint": "[+] is geschikt voor elk topic filter niveau. Bijv.: v1/devices/+/telemetry of +/devices/+/attributen.", + "multi-level-wildcards-hint": "[#] kan de topic filter zelf vervangen en moet het laatste symbool van de topic zijn. Bijv.: # of v1/Devices/ik/#.", + "alarm-rules": "Alarm regels", + "alarm-rules-with-count": "Alarmregels ({{count}})", + "no-alarm-rules": "Geen alarmregels geconfigureerd", + "add-alarm-rule": "Alarmregel toevoegen", + "edit-alarm-rule": "Alarmregel bewerken", + "alarm-type": "Soort alarm", + "alarm-type-required": "Alarmtype is vereist.", + "alarm-type-unique": "Het alarmtype moet uniek zijn binnen de alarmregels van het device profiel.", + "alarm-type-max-length": "Alarmtype moet kleiner zijn dan 256 tekens tekens", + "create-alarm-pattern": "Creëer {{alarmType}} alarm", + "create-alarm-rules": "Alarmregels maken", + "no-create-alarm-rules": "Geen voorwaarden voor het creëren van alarmen+ geconfigureerd", + "add-create-alarm-rule-prompt": "Voeg een alarmregel toe", + "clear-alarm-rule": "Duidelijke alarmregel", + "no-clear-alarm-rule": "Geen duidelijke voorwaarde geconfigureerd", + "add-create-alarm-rule": "Voorwaarde voor maken toevoegen", + "add-clear-alarm-rule": "Duidelijke voorwaarde toevoegen", + "select-alarm-severity": "Selecteer de ernst van het alarm", + "alarm-severity-required": "De ernst van het alarm is vereist.", + "condition-duration": "Duur van de conditie", + "condition-duration-value": "Duur waarde", + "condition-duration-time-unit": "Tijdseenheid", + "condition-duration-value-range": "De duurwaarde moet tussen 1 en 2147483647 liggen.", + "condition-duration-value-pattern": "De waarde van de duur moet gehele getallen zijn.", + "condition-duration-value-required": "Duurwaarde is vereist.", + "condition-duration-time-unit-required": "Tijdseenheid is vereist.", + "advanced-settings": "Geavanceerde instellingen", + "alarm-rule-details": "Details", + "alarm-rule-details-hint": "Tip: gebruik ${keyName} om waarden van de attribuut- of telemetriesleutels te vervangen die worden gebruikt in de alarmregelvoorwaarde.", + "add-alarm-rule-details": "Voeg details toe", + "alarm-rule-mobile-dashboard": "Mobiel dashboard", + "alarm-rule-mobile-dashboard-hint": "Gebruikt door mobiele applicatie als dashboard met alarmdetails", + "alarm-rule-no-mobile-dashboard": "Geen dashboard geselecteerd", + "propagate-alarm": "Alarm doorgeven aan verwante entiteiten", + "alarm-rule-relation-types-list": "Relatietypen die moeten worden gepropageerd", + "alarm-rule-relation-types-list-hint": "Als Relatietypen doorgeven niet is geselecteerd, worden alarmen doorgegeven zonder te filteren op relatietype.", + "propagate-alarm-to-owner": "Alarm doorgeven aan entiteitseigenaar (klant of tenant)", + "propagate-alarm-to-owner-hierarchy": "Alarm doorgeven aan de hiërarchie van entiteitseigenaren", + "propagate-alarm-to-tenant": "Alarm doorgeven aan tenant", + "alarm-details": "Details van het alarm", + "alarm-rule-condition": "Voorwaarde van de alarmregel", + "enter-alarm-rule-condition-prompt": "Voeg de voorwaarde van de alarmregel toe", + "edit-alarm-rule-condition": "Voorwaarde van de alarmregel bewerken", + "device-provisioning": "Inrichting van Devices", + "provision-strategy": "Provisioning-strategie", + "provision-strategy-required": "Provisioning-strategie is vereist.", + "provision-strategy-disabled": "Provisioning-strategie uitgeschakeld ", + "provision-strategy-created-new": "Toestaan om nieuwe provisioning-strategie aan te maken", + "provision-strategy-check-pre-provisioned": "Controleren op vooraf geprovisioneerde devices", + "provision-device-key": "Provision device sleutel", + "provision-device-key-required": "Provision device sleutel is vereist.", + "copy-provision-key": "Provisioningsleutel kopiëren", + "provision-key-copied-message": "Provisioningsleutel is gekopieerd naar klembord", + "provision-device-secret": "Provion device geheim", + "provision-device-secret-required": "Provision device geheim is vereist.", + "copy-provision-secret": "Provisioning geheim kopiëren", + "provision-secret-copied-message": "Provisioningsgeheim is gekopieerd naar klembord", + "provision-strategy-x509": { + "certificate-chain": "X509 Certificaten Keten", + "certificate-chain-hint": "De X.509-certificatenstrategie wordt gebruikt om devices in te richten op clientcertificaten in tweerichtings-TLS-communicatie.", + "allow-create-new-devices": "Nieuwe devices maken", + "allow-create-new-devices-hint": "Indien geselecteerd, worden nieuwe devices gemaakt en wordt het clientcertificaat gebruikt als apparaatreferenties.", + "certificate-value": "Certificaat in PEM-formaat", + "certificate-value-required": "Certificaat in PEM-formaat is vereist", + "cn-regex-variable": "CN Reguliere expressie variabele", + "cn-regex-variable-required": "CN Regular Expression variabele is vereist", + "cn-regex-variable-hint": "Vereist om de device naam op te halen uit de algemene naam van het X509-certificaat van het device." + }, + "condition": "Conditie", + "condition-type": "Conditie type", + "condition-type-simple": "Eenvoudig", + "condition-type-duration": "Duur", + "condition-during": "Tijdens {{during}}", + "condition-during-dynamic": "Tijdens \"{{ attribute }}\" ({{during}})", + "condition-type-repeating": "Herhalende", + "condition-type-required": "Conditietype is vereist.", + "condition-repeating-value": "Aantal gebeurtenissen", + "condition-repeating-value-range": "Het aantal gebeurtenissen moet tussen 1 en 2147483647 liggen.", + "condition-repeating-value-pattern": "Het aantal gebeurtenissen moet gehele getallen zijn.", + "condition-repeating-value-required": "Het aantal gebeurtenissen is vereist.", + "condition-repeat-times": "Herhalingen { count, plural, =1 {1 time} andere {# times} }", + "condition-repeat-times-dynamic": "Herhaalt \"{ attribute }\" ({ count, plural, =1 {1 time} andere {# times} })", + "schedule-type": "Type planner", + "schedule-type-required": "Het type planner is vereist.", + "schedule": "Rooster", + "edit-schedule": "Alarmschema bewerken", + "schedule-any-time": "Altijd actief", + "schedule-specific-time": "Actief op een bepaald tijdstip", + "schedule-custom": "Gewoonte", + "schedule-day": { + "monday": "Maandag", + "tuesday": "Dinsdag", + "wednesday": "Woensdag", + "thursday": "Donderdag", + "friday": "Vrijdag", + "saturday": "Zaterdag", + "sunday": "Zondag" + }, + "schedule-days": "Dagen", + "schedule-time": "Tijd", + "schedule-time-from": "Van", + "schedule-time-to": "Aan", + "schedule-days-of-week-required": "Er moet ten minste één dag van de week worden geselecteerd.", + "create-device-profile": "Nieuw device profiel maken", + "import": "Device profiel importeren", + "export": "Device profiel exporteren", + "export-failed-error": "Device profiel kan niet worden geëxporteerd: {{error}}", + "device-profile-file": "Bestand met device profiel", + "invalid-device-profile-file-error": "Kan device profiel niet importeren: Ongeldige gegevensstructuur van het device profiel.", + "power-saving-mode": "Energiebesparende modus", + "power-saving-mode-type": { + "default": "Energiebesparende modus voor device profiel gebruiken", + "psm": "Energiebesparende modus (PSM)", + "drx": "Discontinue ontvangst (DRX)", + "edrx": "Uitgebreide discontinue ontvangst (eDRX)" + }, + "edrx-cycle": "eDRX-cyclus", + "edrx-cycle-required": "eDRX-cyclus is vereist.", + "edrx-cycle-pattern": "De eDRX-cyclus moet een positief geheel getal zijn.", + "edrx-cycle-min": "Het minimale aantal eDRX-cycli is {{ min }} seconden.", + "paging-transmission-window": "Transmissievenster voor oproepen", + "paging-transmission-window-required": "Oproeptransmissievenster is vereist.", + "paging-transmission-window-pattern": "Het zendvenster van de paging-transmissie moet een positief geheel getal zijn.", + "paging-transmission-window-min": "Het minimale aantal paging-transmissievensters is {{ min }} seconden.", + "psm-activity-timer": "PSM-activiteitstimer", + "psm-activity-timer-required": "PSM-activiteitstimer is vereist.", + "psm-activity-timer-pattern": "PSM-activiteitstimer moet een positief geheel getal zijn.", + "psm-activity-timer-min": "Het minimale aantal PSM-activiteitstimers is {{ min }} seconden.", + "lwm2m": { + "object-list": "Lijst met objecten", + "object-list-empty": "Geen objecten geselecteerd.", + "no-objects-found": "Geen objecten gevonden.", + "no-objects-matching": "Er zijn geen objecten gevonden die overeenkomen met '{{object}}'.", + "model-tab": "LWM2M Model", + "add-new-instances": "Nieuwe instanties toevoegen", + "instances-list": "Lijst met instanties", + "instances-list-required": "Een lijst met instanties is vereist.", + "instance-id-pattern": "Instantie-id moet een positief geheel getal zijn.", + "instance-id-max": "Maximale exemplaar-id-waarde {{max}}.", + "instance": "Voorbeeld", + "resource-label": "#ID Naam van de bron", + "observe-label": "Waarnemen", + "attribute-label": "Attribuut", + "telemetry-label": "Telemetrie", + "edit-observe-select": "Telemetrie of attribuut bewerken om te bewerken, te observeren", + "edit-attributes-select": "Als u attributen wilt bewerken, selecteert u telemetrie of kenmerk", + "no-attributes-set": "Geen attributen ingesteld", + "key-name": "Naam van de sleutel", + "key-name-required": "Sleutelnaam is vereist", + "attribute-name": "Naam attribuut", + "attribute-name-required": "Naamattribuut is vereist.", + "attribute-value": "Attribuutwaarde", + "attribute-value-required": "Kenmerkwaarde is vereist.", + "attribute-value-pattern": "De kenmerkwaarde moet een positief geheel getal zijn.", + "edit-attributes": "Attributen bewerken: {{ name }}", + "view-attributes": "Bekijk attributen: {{ name }}", + "add-attribute": "Attribuut toevoegen", + "edit-attribute": "Attribuut bewerken", + "view-attribute": "Bekijk attribuut", + "remove-attribute": "Attribuut verwijderen", + "delete-server-text": "Opgelet, na de bevestiging wordt de serverconfiguratie onherstelbaar.", + "delete-server-title": "Weet je zeker dat je de server wilt verwijderen?", + "mode": "Beveiligingsconfiguratiemodus", + "bootstrap-tab": "Opstarten", + "bootstrap-server-legend": "Bootstrap-server (ShortId...)", + "lwm2m-server-legend": "LwM2M-server (ShortId...)", + "server": "Server", + "short-id": "Korte server-ID", + "short-id-tooltip": "Server-id kort Id. Wordt gebruikt als koppeling naar het Object-exemplaar van de koppeling.\nDeze id identificeert elke LwM2M-server die is geconfigureerd voor de LwM2M-client op unieke wijze.\nResource MOET worden ingesteld wanneer de Bootstrap-Server Resource een waarde van 'false' heeft.\nDe waarden ID:0 en ID:65535 mogen NIET worden gebruikt voor het identificeren van de LwM2M-server.", + "short-id-required": "Een korte server-ID is vereist.", + "short-id-range": "Korte server-ID moet tussen 1 en 65534 liggen.", + "short-id-pattern": "Korte server-ID moet een positief geheel getal zijn.", + "lifetime": "Levensduur klantregistratie", + "lifetime-required": "Levenslange registratie van klanten is vereist.", + "lifetime-pattern": "De levensduur van clientregistratie moet een positief geheel getal zijn.", + "default-min-period": "Min. periode tussen twee kennisgeving(en)", + "default-min-period-tooltip": "De standaardwaarde die de LwM2M-client moet gebruiken voor de minimale periode van een waarneming als deze parameter niet in een waarneming is opgenomen.", + "default-min-period-required": "Minimumperiode is vereist.", + "default-min-period-pattern": "De minimumperiode moet een positief geheel getal zijn.", + "notification-storing": "Melding opslaan wanneer uitgeschakeld of offline", + "binding": "Bindend", + "binding-type": { + "u": "U: Cliënt is te allen tijde bereikbaar via de UDP-binding.", + "m": "M: Opdrachtgever is te allen tijde bereikbaar via de MQTT-binding.", + "h": "H: Client is op elk moment bereikbaar via de HTTP-binding.", + "t": "T: Cliënt is te allen tijde bereikbaar via de TCP-binding.", + "s": "S: Cliënt is te allen tijde bereikbaar via de SMS-binding.", + "n": "N: Client MOET het antwoord op een dergelijk verzoek verzenden via de niet-IP-binding (wordt ondersteund sinds LWM2M 1.1).", + "uq": "UQ: UDP-verbinding in wachtrijmodus (wordt niet meer ondersteund sinds LWM2M 1.1)", + "uqs": "UQS: zowel UDP- als SMS-verbindingen actief; UDP in wachtrijmodus, SMS in standaardmodus (wordt niet ondersteund sinds LWM2M 1.1)", + "tq": "TQ: TCP-verbinding in wachtrijmodus (wordt niet meer ondersteund sinds LWM2M 1.1)", + "tqs": "TQS: zowel TCP- als SMS-verbindingen actief; TCP in wachtrijmodus, SMS in standaardmodus (wordt niet ondersteund sinds LWM2M 1.1)", + "sq": "SQ: SMS-verbinding in wachtrijmodus (wordt niet meer ondersteund sinds LWM2M 1.1)" + }, + "binding-tooltip": "Dit is de lijst in de \"bindende\" bron van het LwM2M-serverobject - /1/x/7.\nGeeft de ondersteunde bindingsmodi in de LwM2M-client aan.\nDeze waarde MOET hetzelfde zijn als de waarde in de bron \"Ondersteunde bindingen en modi\" in het apparaatobject (/3/0/16).\nHoewel meerdere transporten worden ondersteund, kan er slechts één transportbinding worden gebruikt tijdens de gehele transportsessie.\nWanneer bijvoorbeeld UDP en SMS beide worden ondersteund, kunnen de LwM2M-client en de LwM2M-server ervoor kiezen om tijdens de gehele transportsessie via UDP of sms te communiceren.", + "bootstrap-server": "Bootstrap-server", + "lwm2m-server": "LwM2M-server", + "include-bootstrap-server": "Bootstrap-serverupdates opnemen", + "bootstrap-update-title": "U hebt Bootstrap Server al geconfigureerd. Weet je zeker dat je de updates wilt uitsluiten?", + "bootstrap-update-text": "Opgelet, na de bevestiging worden de configuratiegegevens van de Bootstrap-server onherstelbaar.", + "server-host": "Server host", + "server-host-required": "Server host is vereist.", + "server-port": "Haven", + "server-port-required": "Poort is vereist.", + "server-port-pattern": "Poort moet een positief geheel getal zijn.", + "server-port-range": "De poort moet tussen 1 en 65535 liggen.", + "server-public-key": "Openbare sleutel van de server", + "server-public-key-required": "De openbare sleutel van de server is vereist.", + "client-hold-off-time": "Wacht met de tijd", + "client-hold-off-time-required": "Wachttijd is vereist.", + "client-hold-off-time-pattern": "Hold Off Time moet een positief geheel getal zijn.", + "client-hold-off-time-tooltip": "Client Hold Off Time alleen voor gebruik met een Bootstrap-Server", + "account-after-timeout": "Account na de time-out", + "account-after-timeout-required": "Account na de time-out is vereist.", + "account-after-timeout-pattern": "Account na de time-out moet een positief geheel getal zijn.", + "account-after-timeout-tooltip": "Bootstrap-Server-account na de time-outwaarde die door deze resource wordt opgegeven.", + "server-type": "Type server", + "add-new-server-title": "Nieuwe serverconfiguratie toevoegen", + "add-server-config": "Serverconfiguratie toevoegen", + "add-lwm2m-server-config": "LwM2M-server toevoegen", + "no-config-servers": "Geen servers geconfigureerd", + "others-tab": "Overige instellingen", + "client-strategy": "Klantstrategie bij het verbinden", + "client-strategy-label": "Strategie", + "client-strategy-only-observe": "Observeer het verzoek aan de client pas na de eerste verbinding", + "client-strategy-read-all": "Lees alle bronnen en observeer het verzoek aan de klant na registratie", + "fw-update": "Firmware-update", + "fw-update-strategy": "Strategie voor firmware-updates", + "fw-update-strategy-data": "Firmware-update pushen als binair bestand met behulp van Object 19 en Resource 0 (gegevens)", + "fw-update-strategy-package": "Firmware-update pushen als binair bestand met behulp van Object 5 en Resource 0 (pakket)", + "fw-update-strategy-package-uri": "Genereer automatisch een unieke CoAP-URL om het pakket te downloaden en de firmware-update te pushen als Object 5 en Resource 1 (pakket-URI)", + "sw-update": "Bijwerken van de software", + "sw-update-strategy": "Strategie voor software-updates", + "sw-update-strategy-package": "Binair bestand pushen met Object 9 en Resource 2 (pakket)", + "sw-update-strategy-package-uri": "Genereer automatisch een unieke CoAP-URL om het pakket te downloaden en software-update te pushen met behulp van Object 9 en Resource 3 (pakket-URI)", + "fw-update-resource": "CoAP-bron voor firmware-update", + "fw-update-resource-required": "CoAP-bron voor firmware-update is vereist.", + "sw-update-resource": "CoAP-bron voor software-update", + "sw-update-resource-required": "Software-update CoAP-bron is vereist.", + "config-json-tab": "Json Config Profile-device", + "attributes-name": { + "min-period": "Minimale periode", + "max-period": "Maximale periode", + "greater-than": "Groter dan", + "less-than": "Minder dan", + "step": "Stap", + "min-evaluation-period": "Minimale evaluatieperiode", + "max-evaluation-period": "Maximale evaluatieperiode" + }, + "composite-operations-support": "Ondersteunt samengestelde lees-/schrijf-/observatiebewerkingen" + }, + "snmp": { + "add-communication-config": "Communicatieconfiguratie toevoegen", + "add-mapping": "Toewijzing toevoegen", + "authentication-passphrase": "Wachtwoordzin voor verificatie", + "authentication-passphrase-required": "Wachtwoordzin voor verificatie is vereist.", + "authentication-protocol": "Authenticatie protocol", + "authentication-protocol-required": "Verificatieprotocol is vereist.", + "communication-configs": "Communicatie configs", + "community": "Tekenreeks van de gemeenschap", + "community-required": "Community-tekenreeks is vereist.", + "context-name": "Naam van de context", + "data-key": "Gegevenssleutel", + "data-key-required": "Gegevenssleutel is vereist.", + "data-type": "Soort gegevens", + "data-type-required": "Gegevenstype is vereist.", + "engine-id": "Motor-ID", + "host": "Server host", + "host-required": "Server host is vereist.", + "oid": "OID", + "oid-pattern": "Ongeldige OID-indeling.", + "oid-required": "OID is vereist.", + "please-add-communication-config": "Gelieve communicatieconfiguratie toe te voegen", + "please-add-mapping-config": "Voeg a.u.b. toewijzingsconfiguratie toe", + "port": "Haven", + "port-format": "Ongeldige poortindeling.", + "port-required": "Poort is vereist.", + "privacy-passphrase": "Wachtwoordzin voor privacy", + "privacy-passphrase-required": "Privacywachtwoordzin is vereist.", + "privacy-protocol": "Privacybeleid protocol", + "privacy-protocol-required": "Privacyprotocol is vereist.", + "protocol-version": "Protocolversie", + "protocol-version-required": "Protocolversie is vereist.", + "querying-frequency": "Frequentie van vragen, ms", + "querying-frequency-invalid-format": "De frequentie van query's moet een positief geheel getal zijn.", + "querying-frequency-required": "Queryfrequentie is vereist.", + "retries": "Pogingen", + "retries-invalid-format": "Pogingen voor nieuwe pogingen moeten een positief geheel getal zijn.", + "retries-required": "Opnieuw proberen is vereist.", + "scope": "Draagwijdte", + "scope-required": "Scope is vereist.", + "security-name": "Naam van de beveiliging", + "security-name-required": "Beveiligingsnaam is vereist.", + "timeout-ms": "Time-out, ms", + "timeout-ms-invalid-format": "Time-out moet een positief geheel getal zijn.", + "timeout-ms-required": "Er is een time-out vereist.", + "user-name": "Gebruikersnaam", + "user-name-required": "Gebruikersnaam is vereist." + } + }, + "dialog": { + "close": "Dialoogvenster sluiten", + "error-message-title": "Foutmelding:", + "error-details-title": "Details van de fout" + }, + "direction": { + "column": "Kolom", + "row": "Rijen" + }, + "edge": { + "all": "Alle", + "all-edges": "Alle edges", + "groups": "Groepen", + "shared": "Gedeeld", + "edge": "Edge", + "edge-instances": "Edge-instanties", + "instances": "Exemplaren", + "edge-file": "Edge-bestand", + "name-max-length": "Naam moet kleiner zijn dan 256 tekens", + "label-max-length": "Etiket moet kleiner zijn dan 256 tekens", + "type-max-length": "Type moet kleiner zijn dan 256 tekens", + "management": "Edge-beheer", + "no-edges-matching": "Er zijn geen edges gevonden die overeenkomen met '{{entity}}'.", + "add": "Edge toevoegen", + "no-edges-text": "Geen edges gevonden", + "edge-details": "Details van de edge", + "add-edge-text": "Nieuwe edge toevoegen", + "delete": "Edge verwijderen", + "delete-edge-title": "Weet je zeker dat je de edge '{{edgeName}}' wilt verwijderen?", + "delete-edge-text": "Opgelet, na de bevestiging worden de edge en alle gerelateerde gegevens onherstelbaar.", + "delete-edges-title": "Weet je zeker dat je { count, plural, =1 {1 edge} andere {# edges} } wilt bevoordelen?", + "delete-edges-text": "Opgelet, na de bevestiging worden alle geselecteerde edges verwijderd en kunnen alle gerelateerde gegevens niet meer worden hersteld.", + "name": "Naam", + "name-starts-with": "Edge naam begint met", + "name-required": "Naam is verplicht.", + "edge-license-key": "Edge-licentiesleutel", + "edge-license-key-required": "Edge-licentiecode is vereist.", + "edge-license-key-max-length": "Edge-licentiesleutel moet kleiner zijn dan 31", + "edge-license-key-hint": "BELANGRIJKE OPMERKING: standaard edge-licentiesleutel wordt alleen verstrekt voor evaluatiedoeleinden.
Deze sleutel is slechts 30 dagen na activering actief. De whitelabeling-functie is uitgeschakeld.
Om uw licentiesleutel te verkrijgen, navigeert u naar de pagina pricing en selecteert u de beste licentieoptie voor uw geval.", + "cloud-endpoint": "Cloud-eindpunt", + "cloud-endpoint-required": "Cloud Endpoint is vereist.", + "cloud-endpoint-max-length": "Cloud-eindpunt moet kleiner zijn dan 256 tekens", + "cloud-endpoint-hint": "Edge vereist HTTP(s)-toegang tot Cloud (ThingsBoard PE) om de licentiesleutel te verifiëren. Geef de cloud-URL op waarmee Edge verbinding kan maken.", + "description": "Omschrijving: __________", + "details": "Details", + "events": "Events", + "copy-id": "Edge-ID kopiëren", + "id-copied-message": "Edge Id is gekopieerd naar klembord", + "sync": "Edge synchroniseren", + "edge-required": "Edge vereist", + "edge-type": "Type edge", + "edge-type-required": "Edge type is vereist.", + "event-action": "Actie voor event", + "entity-id": "Entiteit-ID", + "select-edge-type": "Edge type selecteren", + "assign-to-customer": "Toewijzen aan klant", + "assign-to-customer-text": "Selecteer de klant om de edge(en) toe te wijzen", + "assign-edge-to-customer": "Edge(s) toewijzen aan klant", + "assign-edge-to-customer-text": "Selecteer de edges die u aan de klant wilt toewijzen", + "assignedToCustomer": "Toegewezen aan klant", + "edge-public": "Edge is openbaar", + "assigned-to-customer": "Toegewezen aan: {{customerTitle}}", + "unassign-from-customer": "Toewijzing van klant ongedaan maken", + "unassign-edge-title": "Weet u zeker dat u de toewijzing van de edge '{{edgeName}}' wilt opheffen?", + "unassign-edge-text": "Na de bevestiging wordt de toewijzing van de edge ongedaan gemaakt en is deze niet toegankelijk voor de klant.", + "unassign-edges-title": "Weet u zeker dat u de toewijzing van { count, plural, =1 {1 edge} andere {# edges} } wilt opheffen?", + "unassign-edges-text": "Na de bevestiging worden alle geselecteerde edges ongedaan gemaakt en zijn ze niet toegankelijk voor de klant.", + "make-public": "Edge openbaar maken", + "make-public-edge-title": "Weet je zeker dat je de edge '{{edgeName}}' openbaar wilt maken?", + "make-public-edge-text": "Na de bevestiging worden de edge en al zijn gegevens openbaar gemaakt en toegankelijk gemaakt voor anderen.", + "make-private": "Maak edge privé", + "public": "Publiek", + "make-private-edge-title": "Weet je zeker dat je de edge '{{edgeName}}' privé wilt maken?", + "make-private-edge-text": "Na de bevestiging worden de edge en al zijn gegevens privé gemaakt en zijn ze niet toegankelijk voor anderen.", + "import": "Edge importeren", + "install-connect-instructions": "Installatie- en verbindingsinstructies", + "label": "Etiket", + "load-entity-error": "Gegevens kunnen niet worden geladen. Entiteit is verwijderd.", + "assign-new-edge": "Nieuwe edge toewijzen", + "unassign-from-edge": "Toewijzing van edge ongedaan maken", + "edge-key": "Edge sleutel", + "copy-edge-key": "Edge sleutel kopiëren", + "edge-key-copied-message": "Edge sleutel is gekopieerd naar klembord", + "edge-secret": "Het geheim van de edge", + "copy-edge-secret": "Kopieer Edge geheim", + "edge-secret-copied-message": "Edge-geheim is gekopieerd naar klembord", + "manage-assets": "Assets beheren", + "manage-devices": "Devices beheren", + "manage-entity-views": "Entiteitsweergaven beheren", + "manage-dashboards": "Dashboards beheren", + "manage-rulechains": "Rule chains beheren", + "assets": "Edge-asset", + "devices": "Edge-Devices", + "entity-views": "Edge-entiteitsweergaven", + "dashboard": "Edge-dashboard", + "dashboards": "Edge-dashboards", + "rulechain-templates": "Sjablonen voor rule chains", + "edge-rulechain-templates": "Sjablonen voor edge rule chains", + "converter-templates": "Converter sjablonen", + "edge-converter-templates": "Sjablonen voor edge-converters", + "integration-templates": "Integratie sjablonen", + "edge-integration-templates": "Sjablonen voor edge-integratie", + "rulechains": "Edge rule chains", + "integrations": "Integraties", + "search": "Edge zoeken", + "selected-edges": "{ count, plural, =1 {1 edge} andere {# edges} } geselecteerd", + "any-edge": "Elke edge", + "no-edge-types-matching": "Er zijn geen edge typen gevonden die overeenkomen met '{{entitySubtype}}'.", + "edge-type-list-empty": "Geen edge typen geselecteerd.", + "edge-types": "Soorten edges", + "enter-edge-type": "Voer edge type in", + "deployed": "Ingezet", + "pending": "In behandeling", + "downlinks": "Downlinks", + "no-downlinks-prompt": "Geen downlinks gevonden", + "sync-process-started-successfully": "Het synchronisatieproces is succesvol gestart!", + "missing-related-rule-chains-title": "Edge heeft ontbrekende gerelateerde rule chain(s)", + "missing-related-rule-chains-text": "Toegewezen aan rule chain(s) Gebruik rule nodes die bericht(en) doorsturen naar rule chain(s) die niet aan deze edge zijn toegewezen.

Lijst met ontbrekende rule chain(s):
{{missingRuleChains}}", + "widget-datasource-error": "Deze widget ondersteunt alleen de gegevensbron van de EDGE-entiteit", + "assign-to-edge": "Toewijzen aan edge", + "assign-to-edge-title": "Entiteitsgroep(en) toewijzen aan Edge", + "manage-edge-user-groups": "Edge-gebruikersgroepen beheren", + "manage-edge-asset-groups": "Edge-activagroepen beheren", + "manage-edge-device-groups": "Edge-apparaatgroepen beheren", + "manage-edge-entity-view-groups": "Edge-entiteitsweergavegroepen beheren", + "manage-edge-dashboard-groups": "Edge-dashboardgroepen beheren", + "manage-edge-rule-chains": "Ketens van randregels beheren", + "manage-edge-integrations": "Edge-integraties beheren", + "manage-edge-scheduler-events": "Edge-scheduler events beheren", + "select-group-to-add": "Selecteer de doelgroep om geselecteerde edges toe te voegen", + "select-group-to-move": "Selecteer de doelgroep om geselecteerde edges te verplaatsen", + "remove-edges-from-group": "Weet je zeker dat je { count, plural, =1 {1 edge} andere {# edges} } uit groep '{entityGroup}' wilt verwijderen?", + "group": "Groep edges", + "list-of-groups": "{ count, plural, =1 {One edge group} andere {List of # edge groups} }", + "group-name-starts-with": "Edge-groepen waarvan de naam begint met '{{prefix}}'", + "unassign-entity-group-from-edge-title": "Weet u zeker dat u de toewijzing van de entiteitsgroep '{{ entityGroupName }}' wilt opheffen?", + "unassign-entity-group-from-edge-text": "Na de bevestiging wordt de toewijzing van de entiteitsgroep ongedaan gemaakt en is deze niet toegankelijk via de edge.", + "unassign-entity-group-from-edge": "Toewijzing van entiteitsgroep aan edge ongedaan maken", + "unassign-entity-groups-from-edge-title": "Weet u zeker dat u de toewijzing van {{count}} entiteitsgroepen wilt opheffen?", + "unassign-entity-groups-from-edge-text": "Na de bevestiging wordt de toewijzing van entiteitsgroepen ongedaan gemaakt en zijn ze niet toegankelijk via de edge.", + "unassign-entity-groups-from-edge": "Toewijzing van entiteitsgroepen aan de edge ongedaan maken", + "unassign-scheduler-events-from-edge": "Toewijzing van scheduler events van edge ongedaan maken", + "unassign-scheduler-event-from-edge-title": "Weet u zeker dat u de toewijzing van de '{{schedulerEventName}}' van de planningsgebeurtenis wilt intrekken?", + "unassign-scheduler-event-from-edge-text": "Na de bevestiging wordt de toewijzing van de planningsgebeurtenis ongedaan gemaakt en is deze niet toegankelijk via de edge.", + "unassign-scheduler-events-from-edge-title": "Weet u zeker dat u de toewijzing van { count, plural, =1 {1 scheduler event} andere {# scheduler events} } wilt opheffen?", + "unassign-scheduler-events-from-edge-text": "Na de bevestiging worden alle geselecteerde scheduler events ongedaan gemaakt en zijn ze niet toegankelijk voor de edge.", + "manage-user-groups": "Gebruikersgroepen beheren", + "manage-asset-groups": "Asset groepen beheren", + "manage-device-groups": "Asset groepen beheren", + "manage-dashboard-groups": "Dashboardgroepen beheren", + "manage-entity-view-groups": "Entiteitsweergavegroepen beheren", + "manage-scheduler-events": "Scheduler-events beheren", + "manage-integrations": "Integraties beheren", + "assign-scheduler-event-to-edge-title": "Scheduler-gebeurtenissen toewijzen aan Edge", + "assign-scheduler-event-to-edge-text": "Selecteer de scheduler events die u aan de edge wilt toewijzen", + "assign-entity-groups-to-edge-text": "Selecteer de entiteitsgroepen die u aan de edge wilt toewijzen", + "assign-integration-to-edge-title": "Integraties toewijzen aan Edge", + "assign-integration-to-edge-text": "Selecteer de integraties die u aan de edge wilt toewijzen", + "missing-attributes-title": "Edge heeft ontbrekende attribuut(en)", + "missing-attributes-text": "Integratie(s) die aan de edge zijn toegewezen, hebben tijdelijke aanduidingen voor attributen die niet beschikbaar zijn aan deze edge.

Lijst met ontbrekende edge kenmerken:
{{missingEdgeAttributes}}", + "missing-all-related-attributes-title": "Edge(en) hebben ontbrekende attribuut(en)", + "missing-all-related-attributes-text": "Edge(s), gerelateerd aan integratie, hebben geen attributen die aanwezig zijn in de integratieconfiguratie als tijdelijke aanduidingen voor attributen.

Lijst met ontbrekende edge kenmerken:
{{missingEdgeAttributes}}", + "quick-overview-widget-header": "{{edgeName}} Snel overzicht" + }, + "edge-event": { + "type-dashboard": "Dashboard", + "type-asset": "Asset", + "type-device": "Device", + "type-device-profile": "Device Profiel", + "type-asset-profile": "Asset Profiel", + "type-entity-view": "Entiteit Weergave", + "type-alarm": "Alarm", + "type-entity-group": "Entiteitsgroep", + "type-rule-chain": "Rule chain", + "type-rule-chain-metadata": "Metagegevens van rule chain", + "type-edge": "Edge", + "type-user": "Gebruiker", + "type-customer": "Klant", + "type-relation": "Relatie", + "type-widgets-bundle": "Widgets Bundel", + "type-widgets-type": "Widgets Type", + "type-admin-settings": "Admin-instellingen", + "type-scheduler-event": "Scheduler Evenement", + "type-white-labeling": "White labelling", + "type-login-white-labeling": "Inloggen White Labeling", + "type-custom-translation": "Aangepaste vertaling", + "type-role": "Rol", + "type-group-permission": "Toestemming voor groepen", + "type-integration": "Integratie", + "type-converter": "Conversie type", + "action-type-added": "Toegevoegd", + "action-type-deleted": "Verwijderd", + "action-type-updated": "Bijgewerkt", + "action-type-post-attributes": "Attributen posten", + "action-type-attributes-updated": "Attributen bijgewerkt", + "action-type-attributes-deleted": "Attributen verwijderd", + "action-type-timeseries-updated": "Tijdreeks bijgewerkt", + "action-type-credentials-updated": "Inloggegevens bijgewerkt", + "action-type-assigned-to-customer": "Toegewezen aan klant", + "action-type-unassigned-from-customer": "Toewijzing van klant ongedaan gemaakt", + "action-type-relation-add-or-update": "Relatie toevoegen of bijwerken", + "action-type-relation-deleted": "Relatie verwijderd", + "action-type-rpc-call": "RPC-oproep", + "action-type-alarm-ack": "Alarm Ack", + "action-type-alarm-clear": "Alarm wissen", + "action-type-assigned-to-edge": "Toegewezen aan Edge", + "action-type-unassigned-from-edge": "Toewijzing van Edge ongedaan gemaakt", + "action-type-credentials-request": "Inloggegevens aanvragen", + "action-type-entity-merge-request": "Aanvraag voor samenvoeging van entiteiten", + "action-type-added-to-entity-group": "Toegevoegd aan entiteitsgroep", + "action-type-removed-from-entity-group": "Verwijderd uit entiteitsgroep", + "action-type-change-owner": "Eigenaar wijzigen", + "action-type-relations-deleted": "Relaties verwijderd" + }, + "error": { + "unable-to-connect": "Kan geen verbinding maken met de server! Controleer uw internetverbinding.", + "unhandled-error-code": "Onverwerkte foutcode: {{errorCode}}", + "unknown-error": "Onbekende fout" + }, + "entity": { + "entity": "Entiteit", + "entities": "Entiteiten", + "entities-count": "Aantal entiteiten", + "alarms-count": "Aantal alarmen", + "aliases": "Aliassen van entiteiten", + "entity-alias": "Alias van entiteit", + "unable-delete-entity-alias-title": "Entiteitsalias kan niet worden verwijderd", + "unable-delete-entity-alias-text": "Entiteitsalias '{{entityAlias}}' kan niet worden verwijderd omdat deze wordt gebruikt door de volgende widget(s):
{{widgetsList}}", + "duplicate-alias-error": "Dubbele alias gevonden '{{alias}}'.
Aliassen van entiteiten moeten uniek zijn binnen het dashboard.", + "missing-entity-filter-error": "Filter ontbreekt voor alias '{{alias}}'.", + "configure-alias": "Configureer de alias '{{alias}}'", + "alias": "Alias", + "alias-required": "Entiteitsalias is vereist.", + "remove-alias": "Entiteitsalias verwijderen", + "add-alias": "Entiteitsalias toevoegen", + "entity-list": "Lijst met entiteiten", + "entity-type": "Type entiteit", + "entity-types": "Soorten entiteiten", + "entity-type-list": "Lijst met entiteitstypen", + "any-entity": "Elke entiteit", + "enter-entity-type": "Entiteitstype invoeren", + "no-entities-matching": "Er zijn geen entiteiten gevonden die overeenkomen met '{{entity}}'.", + "no-entity-types-matching": "Er zijn geen entiteitstypen gevonden die overeenkomen met '{{entityType}}'.", + "name-starts-with": "Naam expressie", + "help-text": "Gebruik '%' naar behoefte: '%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", + "use-entity-name-filter": "Filter gebruiken", + "entity-list-empty": "Geen entiteiten geselecteerd.", + "entity-type-list-empty": "Er zijn geen entiteitstypen geselecteerd.", + "entity-name-filter-required": "Filter voor entiteitsnamen is vereist.", + "entity-name-filter-no-entity-matched": "Er zijn geen entiteiten gevonden die beginnen met '{{entity}}'.", + "all-subtypes": "Alle", + "select-entities": "Entiteiten selecteren", + "no-aliases-found": "Geen aliassen gevonden.", + "no-alias-matching": "'{{alias}}' niet gevonden.", + "create-new-alias": "Maak een nieuwe aan!", + "key": "Sleutel", + "key-name": "Naam van de sleutel", + "no-keys-found": "Geen sleutels gevonden.", + "no-key-matching": "'{{key}}' niet gevonden.", + "create-new-key": "Maak een nieuwe aan!", + "type": "Type", + "type-required": "Entiteitstype is vereist.", + "type-device": "Device", + "type-devices": "Devices", + "list-of-devices": "{ count, plural, =1 {One device} andere {List of # devices} }", + "device-name-starts-with": "Devices waarvan de naam begint met '{{prefix}}'", + "type-device-profile": "Device profiel", + "type-device-profiles": "Device profielen", + "list-of-device-profiles": "{ count, plural, =1 {One device profile} andere {List of # device profiles} }", + "device-profile-name-starts-with": "Device profielen waarvan de naam begint met '{{prefix}}'", + "type-asset-profile": "Asset profiel", + "type-asset-profiles": "Asset profielen", + "list-of-asset-profiles": "{ count, plural, =1 {One asset profile} andere {List of # asset profiles} }", + "asset-profile-name-starts-with": "Assetprofielen waarvan de naam begint met '{{prefix}}'", + "type-asset": "Asset", + "type-assets": "Assets", + "list-of-assets": "{ count, plural, =1 {One asset} andere {List of # assets} }", + "asset-name-starts-with": "Assets waarvan de naam begint met '{{prefix}}'", + "type-entity-view": "Entiteit Weergave", + "type-entity-views": "Entiteitsweergaven", + "list-of-entity-views": "{ count, plural, =1 {One entity view} andere {List of # entity views} }", + "entity-view-name-starts-with": "Entiteitsweergaven waarvan de naam begint met '{{prefix}}'", + "type-rule": "Regel", + "type-rules": "Reglement", + "list-of-rules": "{ count, plural, =1 {One rule} andere {List of # rules} }", + "rule-name-starts-with": "Regels waarvan de naam begint met '{{prefix}}'", + "type-plugin": "Plug-in", + "type-plugins": "Insteekplaatsen", + "list-of-plugins": "{ count, plural, =1 {One plugin} andere {List of # plugins} }", + "plugin-name-starts-with": "Plug-ins waarvan de naam begint met '{{prefix}}'", + "type-tenant": "Tenant", + "type-tenants": "Tenants", + "list-of-tenants": "{ count, plural, =1 {One tenant} andere {List of # tenants} }", + "tenant-name-starts-with": "Tenants van wie de naam begint met '{{prefix}}'", + "type-tenant-profile": "Profiel van de tenant", + "type-tenant-profiles": "Profielen van tenants", + "list-of-tenant-profiles": "{ count, plural, =1 {One tenant profile} andere {List of # tenant profiles} }", + "tenant-profile-name-starts-with": "Tentant profielen waarvan de naam begint met '{{prefix}}'", + "type-customer": "Klant", + "type-customers": "Klanten", + "list-of-customers": "{ count, plural, =1 {One customer} andere {List of # customers} }", + "customer-name-starts-with": "Klanten van wie de naam begint met '{{prefix}}'", + "type-user": "Gebruiker", + "type-users": "Gebruikers", + "list-of-users": "{ count, plural, =1 {One user} andere {List of # users} }", + "user-name-starts-with": "Gebruikers van wie de naam begint met '{{prefix}}'", + "type-dashboard": "Dashboard", + "type-dashboards": "Dashboards", + "list-of-dashboards": "{ count, plural, =1 {One dashboard} andere {List of # dashboards} }", + "dashboard-name-starts-with": "Dashboards waarvan de naam begint met '{{prefix}}'", + "type-alarm": "Alarm", + "type-alarms": "Alarmen", + "list-of-alarms": "{ count, plural, =1 {One alarm} andere {List of # alarms} }", + "alarm-name-starts-with": "Alarmen waarvan de naam begint met '{{prefix}}'", + "type-rulechain": "Rule chains", + "type-rulechains": "Rule chains", + "list-of-rulechains": "{ count, plural, =1 {One rule chain} andere {List of # rule chains} }", + "rulechain-name-starts-with": "Rule chains waarvan de naam begint met '{{prefix}}'", + "type-scheduler-event": "Scheduler-event", + "type-scheduler-events": "Scheduler-events", + "list-of-scheduler-events": "{ count, plural, =1 {One scheduler event} andere {List of # scheduler events} }", + "scheduler-event-name-starts-with": "Scheduler-events waarvan de naam begint met '{{prefix}}'", + "type-blob-entity": "Blob-entiteit", + "type-blob-entities": "Blob-entiteiten", + "list-of-blob-entities": "{ count, plural, =1 {One blob entity} andere {List of # blob entities} }", + "blob-entity-name-starts-with": "Blob-entiteiten waarvan de naam begint met '{{prefix}}'", + "type-rulenode": "Rule node", + "type-rulenodes": "Rule nodes", + "list-of-rulenodes": "{ count, plural, =1 {One rule node} andere {List of # rule nodes} }", + "rulenode-name-starts-with": "Rule node waarvan de naam begint met '{{prefix}}'", + "type-current-customer": "Huidige klant", + "type-current-tenant": "Huidige tenant", + "type-current-user": "Huidige gebruiker", + "type-current-user-owner": "Huidige gebruikerseigenaar", + "type-widgets-bundle": "Widgets bundel", + "type-widgets-bundles": "Widgets bundels", + "list-of-widgets-bundles": "{ count, plural, =1 {One widgets bundle} andere {List of # widget bundles} }", + "search": "Entiteiten zoeken", + "selected-entities": "{ count, plural, =1 {1 entity} andere {# entities} } geselecteerd", + "entity-name": "Naam van de entiteit", + "entity-label": "Entiteitslabel", + "details": "Gegevens van de entiteit", + "no-entities-prompt": "Geen entiteiten gevonden", + "no-data": "Geen gegevens om weer te geven", + "columns-to-display": "Weer te geven kolommen", + "type-api-usage-state": "Api-gebruiksstatus", + "type-entity-group": "Entiteitsgroep", + "type-converter": "Gegevens converteren", + "type-converters": "Data-converters", + "list-of-converters": "{ count, plural, =1 {One data converter} andere {List of # data converters} }", + "converter-name-starts-with": "Dataconverters waarvan de naam begint met \"{{prefix}}\"", + "type-integration": "Integratie", + "type-integrations": "Integraties", + "list-of-integrations": "{ count, plural, =1 {One integration} andere {List of # integrations} }", + "integration-name-starts-with": "Integraties waarvan de naam begint met '{{prefix}}'", + "type-role": "Rol", + "type-roles": "Rollen", + "list-of-roles": "{ count, plural, =1 {One role} andere {List of # roles} }", + "role-name-starts-with": "Rollen waarvan de naam begint met '{{prefix}}'", + "type-group-permission": "Toestemming voor groepen", + "type-edge": "Edge", + "type-edges": "Edge", + "list-of-edges": "{ count, plural, =1 {One edge} andere {List of # edges} }", + "edge-name-starts-with": "Edge waarvan de naam begint met '{{prefix}}'", + "type-tb-resource": "Hulpbron", + "type-ota-package": "OTA-pakket", + "type-rpc": "RPC", + "type-queue": "Rij", + "type-notification": "Bekendmaking", + "type-notification-rule": "Meldingsregel", + "type-notification-target": "Ontvanger van de melding", + "type-notification-request": "Verzoek om kennisgeving", + "type-notification-template": "Kennisgeving sjabloon", + "customer-name": "Naam van de klant", + "sub-customer-name": "Naam van de subklant", + "parent-customer-name": "Naam van bovenliggende klant", + "parent-sub-customer-name": "Naam van bovenliggende subklant", + "include-customer-entities": "Klantentiteiten opnemen", + "include-sub-customer-entities": "Entiteiten van subklanten opnemen", + "groups": "Groepen" + }, + "entity-group": { + "entity-group": "Entiteitsgroep", + "details": "Details", + "columns": "Kolommen", + "add-column": "Kolom toevoegen", + "column-value": "Waarde", + "column-value-required": "Kolomwaarde is vereist.", + "column-title": "Titel", + "default-sort-order": "Standaard sorteervolgorde", + "default-sort-order-required": "De standaard sorteervolgorde is vereist.", + "hide-in-mobile-view": "Mobiel verborgen", + "use-cell-style-function": "Celstijlfunctie gebruiken", + "use-cell-content-function": "De functie voor celinhoud gebruiken", + "edit-column": "Kolom bewerken", + "column-details": "Kolom details", + "actions": "Acties", + "settings": "Instellingen", + "search": "Entiteitsgroepen zoeken", + "delete": "Entiteitsgroep verwijderen", + "name": "Naam", + "name-required": "Naam is verplicht.", + "name-max-length": "Naam moet kleiner zijn dan 256 tekens", + "description": "Omschrijving: __________", + "add": "Entiteitsgroep toevoegen", + "open-entity-group": "Entiteitsgroep openen", + "add-entity-group-text": "Nieuwe entiteitsgroep toevoegen", + "no-entity-groups-text": "Geen entiteitsgroepen gevonden", + "entity-group-details": "Details van entiteitsgroep", + "selected-entity-groups": "{ count, plural, =1 {1 entity group} andere {# entity groups} } geselecteerd", + "delete-entity-groups": "Entiteitsgroepen verwijderen", + "delete-entity-group-title": "Weet u zeker dat u de entiteitsgroep '{{entityGroupName}}' wilt verwijderen?", + "delete-entity-group-text": "Let op, na de bevestiging worden de entiteitsgroep en alle gerelateerde gegevens onherstelbaar.", + "delete-entity-groups-title": "Weet u zeker dat u { count, plural, =1 {1 entity group} andere {# entity groups} } wilt verwijderen?", + "delete-entity-groups-action-title": "Verwijder { count, plural, =1 {1 entity group} andere {# entity groups} }", + "delete-entity-groups-text": "Opgelet, na de bevestiging worden alle geselecteerde entiteitsgroepen verwijderd en kunnen alle gerelateerde gegevens niet meer worden hersteld.", + "device-groups": "Asset groepen", + "shared-device-groups": "Groepen met gedeelde devices", + "asset-groups": "Asset groepen", + "shared-asset-groups": "Gedeelde activagroepen", + "customer-groups": "Klantengroepen", + "shared-customer-groups": "Gedeelde klantgroepen", + "device-group": "Apparaatgroep", + "asset-group": "Activagroep", + "user-group": "Gebruikersgroep", + "user-groups": "Gebruikersgroepen", + "customer-group": "Klantengroep", + "entity-view-groups": "Groepen voor entiteitsweergaven", + "shared-entity-view-groups": "Groepen met gedeelde entiteitsweergave", + "entity-view-group": "Groep voor entiteitsweergave", + "edge-groups": "Randgroepen", + "shared-edge-groups": "Gedeelde randgroepen", + "edge-group": "Edge groep", + "dashboard-groups": "Dashboard groepen", + "shared-dashboard-groups": "Gedeelde dashboardgroepen", + "dashboard-group": "Dashboard groep", + "fetch-more": "Haal meer op", + "column-type": { + "column-type": "Type kolom", + "client-attribute": "Cliënt attribuut", + "shared-attribute": "Gedeeld attribuut", + "server-attribute": "Server-attribuut", + "timeseries": "Timeseries", + "entity-field": "Entiteit veld" + }, + "column-type-required": "Kolomtype is vereist.", + "entity-field": { + "created-time": "Gecreëerde tijd", + "name": "Naam", + "type": "Type", + "device_profile": "Device profiel", + "asset_profile": "Asset profiel", + "assigned_customer": "Toegewezen klant", + "authority": "Autoriteit", + "first_name": "Voornaam", + "last_name": "Achternaam", + "email": "E-mail", + "title": "Titel", + "country": "Land", + "state": "Staat", + "city": "Stad", + "address": "Adres", + "address2": "Adres 2", + "zip": "Ritssluiting", + "phone": "Telefoon", + "label": "Etiket" + }, + "sort-order": { + "asc": "Oplopend", + "desc": "Aflopende", + "none": "Geen" + }, + "details-mode": { + "on-row-click": "Klik op rij", + "on-action-button-click": "Klik op de knop Details", + "disabled": "Invalide" + }, + "change-owner": "Eigenaar wijzigen", + "select-target-owner": "Selecteer doeleigenaar", + "no-owners-matching": "Er zijn geen eigenaren gevonden die overeenkomen met '{{owner}}'.", + "target-owner-required": "Doeleigenaar is vereist.", + "confirm-change-owner-title": "Weet u zeker dat u van eigenaar wilt veranderen voor { count, plural, =1 {1 selected entity} andere {# selected entities} }?", + "confirm-change-owner-text": "Opgelet, na de bevestiging worden alle geselecteerde entiteiten verwijderd van de huidige eigenaar en worden ze geplaatst in de groep 'Alle' van de doeleigenaar.", + "add-to-group": "Toevoegen aan groep", + "move-to-group": "Verplaatsen naar groep", + "select-entity-group": "Entiteitsgroep selecteren", + "no-entity-groups-matching": "Er zijn geen entiteitsgroepen gevonden die overeenkomen met '{{entityGroup}}'.", + "create-new-entity-group": "Maak een nieuwe aan!", + "target-entity-group-required": "Doelgroep is vereist.", + "select-user-group": "Selecteer gebruikersgroep", + "no-user-groups-matching": "Er zijn geen gebruikersgroepen gevonden die overeenkomen met '{{entityGroup}}'.", + "target-user-group-required": "Doelgroep is vereist.", + "remove-from-group": "Verwijderen uit groep", + "group-table-title": "Titel van groepstabel", + "enable-search": "Zoeken naar entiteiten inschakelen", + "enable-add": "Entiteiten toevoegen inschakelen", + "enable-delete": "Entiteiten verwijderen inschakelen", + "enable-selection": "Selectie van entiteiten inschakelen", + "enable-group-transfer": "Acties voor groepsoverdracht inschakelen", + "display-pagination": "Paginering weergeven", + "default-page-size": "Standaard paginaformaat", + "enable-assignment-actions": "Toewijzingsacties inschakelen", + "enable-credentials-management": "Beheer van referenties inschakelen", + "enable-login-as-user": "Inloggen als gebruikersactie inschakelen", + "enable-users-management": "Gebruikersbeheer inschakelen", + "enable-customers-management": "Klantenbeheer inschakelen", + "enable-assets-management": "Asset beheer inschakelen", + "enable-devices-management": "Device beheer inschakelen", + "enable-entity-views-management": "Beheer van entiteitsweergaven inschakelen", + "enable-edges-management": "Edge beheer inschakelen", + "enable-dashboards-management": "Dashboardbeheer inschakelen", + "enable-scheduler-events-management": "Beheer van scheduler events inschakelen", + "open-details-on": "Entiteitsdetails openen op", + "select-existing": "Bestaande entiteitsgroep selecteren", + "create-new": "Nieuwe entiteitsgroep maken", + "new-entity-group-name": "Naam van nieuwe entiteitsgroep", + "entity-group-list": "Lijst met entiteitsgroepen", + "entity-group-list-empty": "Geen entiteitsgroepen geselecteerd.", + "name-starts-with": "De naam van de entiteitsgroep begint met", + "entity-group-name-filter-required": "Filter voor de naam van de entiteitsgroep is vereist.", + "roles": "Rollen", + "permissions": "Machtigingen", + "public": "Publiek", + "entity-group-public": "Entiteitsgroep is openbaar", + "make-public": "Entiteitsgroep openbaar maken", + "make-private": "Entiteitsgroep privé maken", + "make-public-entity-group-title": "Weet u zeker dat u de entiteitsgroep '{{entityGroupName}}' openbaar wilt maken?", + "make-public-entity-group-text": "Na de bevestiging worden de entiteitengroep en al haar entiteiten openbaar gemaakt en toegankelijk gemaakt voor anderen.", + "make-private-entity-group-title": "Weet u zeker dat u de entiteitsgroep '{{entityGroupName}}' privé wilt maken?", + "make-private-entity-group-text": "Na de bevestiging worden de entiteitengroep en al zijn entiteiten privé gemaakt en zijn ze niet toegankelijk voor anderen.", + "share": "Entiteitsgroep delen", + "copyId": "Entiteitsgroep-id kopiëren", + "idCopiedMessage": "Entiteitsgroep-id is gekopieerd naar klembord", + "entity-group-name": "Naam van entiteitsgroep", + "all-users": "Alle gebruikers", + "owner": "Eigenaar", + "owner-required": "Eigenaar is verplicht.", + "manage-owner-and-groups": "Eigenaar en groepen beheren", + "owner-and-groups": "Eigenaar en groepen" + }, + "entity-field": { + "created-time": "Gecreëerde tijd", + "name": "Naam", + "type": "Type", + "first-name": "Voornaam", + "last-name": "Achternaam", + "email": "E-mail", + "title": "Titel", + "country": "Land", + "state": "Staat", + "city": "Stad", + "address": "Adres", + "address2": "Adres 2", + "zip": "Ritssluiting", + "phone": "Telefoon", + "label": "Etiket", + "configuration": "Configuratie", + "schedule": "Rooster", + "originatorId": "Initiator-ID", + "originatorType": "Type opsteller" + }, + "entity-view": { + "all": "Alle", + "all-entity-views": "Alle entiteitsweergaven", + "groups": "Groepen", + "shared": "Gedeeld", + "entity-view": "Entiteit Weergave", + "entity-view-required": "Entiteitsweergave is vereist.", + "entity-views": "Entiteitsweergaven", + "management": "Beheer van entiteitsweergaven", + "view-entity-views": "Entiteitsweergaven weergeven", + "entity-view-alias": "Alias voor entiteitsweergave", + "aliases": "Aliassen voor entiteitsweergave", + "no-alias-matching": "'{{alias}}' niet gevonden.", + "no-aliases-found": "Geen aliassen gevonden.", + "no-key-matching": "'{{key}}' niet gevonden.", + "no-keys-found": "Geen sleutels gevonden.", + "create-new-alias": "Maak een nieuwe aan!", + "create-new-key": "Maak een nieuwe aan!", + "duplicate-alias-error": "Dubbele alias gevonden '{{alias}}'.
Aliassen voor entiteitsweergave moeten uniek zijn binnen het dashboard.", + "configure-alias": "Configureer de alias '{{alias}}'", + "no-entity-views-matching": "Er zijn geen entiteitsweergaven gevonden die overeenkomen met '{{entity}}'.", + "public": "Publiek", + "alias": "Alias", + "alias-required": "Alias voor entiteitsweergave is vereist.", + "remove-alias": "Alias voor entiteitsweergave verwijderen", + "add-alias": "Alias voor entiteitsweergave toevoegen", + "name-starts-with": "Naamexpressie entiteitsweergave", + "help-text": "Gebruik '%' naar behoefte: '%entity-view_name_contains%', '%entity-view_name_ends', 'entity-view_starts_with'.", + "entity-view-list": "Lijst met entiteitsweergaven", + "use-entity-view-name-filter": "Filter gebruiken", + "entity-view-list-empty": "Geen entiteitsweergaven geselecteerd.", + "entity-view-name-filter-required": "Het naamfilter voor de entiteitsweergave is vereist.", + "entity-view-name-filter-no-entity-view-matched": "Er zijn geen entiteitsweergaven gevonden die beginnen met '{{entityView}}'.", + "add": "Entiteitsweergave toevoegen", + "entity-view-public": "Entiteitsweergave is openbaar", + "assign-to-customer": "Toewijzen aan klant", + "assign-entity-view-to-customer": "Entiteitsweergave(n) toewijzen aan klant", + "assign-entity-view-to-customer-text": "Selecteer de entiteitsweergaven die u aan de klant wilt toewijzen", + "no-entity-views-text": "Geen entiteitsweergaven gevonden", + "assign-to-customer-text": "Selecteer de klant om de entiteitsweergave(n) toe te wijzen", + "entity-view-details": "Details van entiteitsweergave", + "add-entity-view-text": "Nieuwe entiteitsweergave toevoegen", + "delete": "Entiteitsweergave verwijderen", + "assign-entity-views": "Entiteitsweergaven toewijzen", + "assign-entity-views-text": "Wijs { count, plural, =1 {1 entity view} andere {# entity views} } toe aan de klant", + "delete-entity-views": "Entiteitsweergaven verwijderen", + "make-public": "Entiteitsweergave openbaar maken", + "make-private": "Entiteitsweergave privé maken", + "unassign-from-customer": "Toewijzing van klant ongedaan maken", + "unassign-entity-views": "Toewijzing van entiteitsweergaven intrekken", + "unassign-entity-views-action-title": "Toewijzing { count, plural, =1 {1 entity view} andere {# entity views} } van klant ongedaan maken", + "assign-new-entity-view": "Nieuwe entiteitsweergave toewijzen", + "delete-entity-view-title": "Weet u zeker dat u de entiteitsweergave '{{entityViewName}}' wilt verwijderen?", + "delete-entity-view-text": "Opgelet, na de bevestiging worden de entiteitsweergave en alle gerelateerde gegevens onherstelbaar.", + "delete-entity-views-title": "Weet u zeker dat u { count, plural, =1 {1 entity view} andere {# entity views} } wilt verwijderen?", + "delete-entity-views-action-title": "Verwijder { count, plural, =1 {1 entity view} andere {# entity views} }", + "delete-entity-views-text": "Opgelet, na de bevestiging worden alle geselecteerde entiteitsweergaven verwijderd en kunnen alle gerelateerde gegevens niet meer worden hersteld.", + "make-public-entity-view-title": "Weet u zeker dat u de entiteitsweergave '{{entityViewName}}' openbaar wilt maken?", + "make-public-entity-view-text": "Na de bevestiging worden de entiteitsweergave en al haar gegevens openbaar en toegankelijk gemaakt voor anderen.", + "make-private-entity-view-title": "Weet u zeker dat u de entiteitsweergave '{{entityViewName}}' privé wilt maken?", + "make-private-entity-view-text": "Na de bevestiging worden de entiteitsweergave en al zijn gegevens privé gemaakt en zijn ze niet toegankelijk voor anderen.", + "unassign-entity-view-title": "Weet u zeker dat u de toewijzing van de entiteitsweergave '{{entityViewName}}' wilt opheffen?", + "unassign-entity-view-text": "Na de bevestiging wordt de toewijzing van de entiteitsweergave ongedaan gemaakt en is deze niet toegankelijk voor de klant.", + "unassign-entity-view": "Toewijzing van entiteitsweergave ongedaan maken", + "unassign-entity-views-title": "Weet u zeker dat u de toewijzing van { count, plural, =1 {1 entity view} andere {# entity views} } wilt opheffen?", + "unassign-entity-views-text": "Na de bevestiging worden alle geselecteerde entiteitsweergaven ongedaan gemaakt en zijn ze niet toegankelijk voor de klant.", + "entity-view-type": "Type entiteitsweergave", + "entity-view-type-required": "Het type Entiteitsweergave is vereist.", + "select-entity-view-type": "Weergavetype entiteit selecteren", + "enter-entity-view-type": "Entiteitsweergavetype invoeren", + "any-entity-view": "Elke entiteitsweergave", + "no-entity-view-types-matching": "Er zijn geen entiteitsweergavetypen gevonden die overeenkomen met '{{entitySubtype}}'.", + "entity-view-type-list-empty": "Er zijn geen types entiteitsweergaven geselecteerd.", + "entity-view-types": "Typen entiteitsweergaven", + "created-time": "Gecreëerde tijd", + "name": "Naam", + "name-required": "Naam is verplicht.", + "name-max-length": "Naam moet kleiner zijn dan 256 tekens", + "type-max-length": "Het type entiteitsweergave moet kleiner zijn dan 256 tekens", + "description": "Omschrijving: __________", + "events": "Events", + "details": "Details", + "copyId": "Entiteitsweergave-id kopiëren", + "idCopiedMessage": "Entiteitsweergave-id is gekopieerd naar klembord", + "assignedToCustomer": "Toegewezen aan klant", + "unable-entity-view-device-alias-title": "Kan alias voor entiteitsweergave niet verwijderen", + "unable-entity-view-device-alias-text": "Entiteitsweergave device alias '{{entityViewAlias}}' kan niet worden verwijderd omdat deze wordt gebruikt door de volgende widget(s):
{{widgetsList}}", + "select-entity-view": "Entiteitsweergave selecteren", + "start-date": "Begindatum", + "start-ts": "Begintijd", + "end-date": "Einddatum", + "end-ts": "Eindtijd", + "date-limits": "Datumlimieten", + "client-attributes": "Attributen van de klant", + "shared-attributes": "Gedeelde attributen", + "server-attributes": "Server-attributen", + "timeseries": "Timeseries", + "client-attributes-placeholder": "Attributen van de klant", + "shared-attributes-placeholder": "Gedeelde attributen", + "server-attributes-placeholder": "Server-attributen", + "timeseries-placeholder": "Timeseries", + "target-entity": "Doelentiteit", + "attributes-propagation": "Doorgifte van attributen", + "attributes-propagation-hint": "Entiteitsweergave kopieert automatisch gespecificeerde attributen van doelentiteit telkens wanneer u deze entiteitsweergave opslaat of bijwerkt. Om prestatieredenen worden de attributen van de doelentiteit niet doorgegeven aan de entiteitsweergave bij elke kenmerkwijziging. U kunt automatische doorgifte inschakelen door het rulenode 'kopiëren naar weergave' in uw rule chain te configureren en berichten 'Attributen posten' en 'Attributen bijgewerkt' te koppelen aan het nieuwe rule node.", + "timeseries-data": "Tijdreeksgegevens", + "timeseries-data-hint": "Configureer timeseries sleutels van de doelentiteit die toegankelijk zijn voor de entiteitsweergave. Deze timeseries gegevens zijn niet editeerbaar.", + "select-group-to-add": "Selecteer de doelgroep om geselecteerde entiteitsweergaven toe te voegen", + "select-group-to-move": "Selecteer de doelgroep om geselecteerde entiteitsweergaven te verplaatsen", + "remove-entity-views-from-group": "Weet je zeker dat je { count, plural, =1 {1 entity view} andere {# entity views} } uit groep '{{entityGroup}}' wilt verwijderen?", + "group": "Groep entiteitsweergaven", + "list-of-groups": "{ count, plural, =1 {One entity view group} andere {List of # entity view groups} }", + "group-name-starts-with": "Entiteitsweergavegroepen waarvan de naam begint met '{{prefix}}'", + "search": "Entiteitsweergaven zoeken", + "selected-entity-views": "{ count, plural, =1 {1 entity view} andere {# entity views} } geselecteerd", + "assign-entity-view-to-edge": "Entiteitsweergave(n) toewijzen aan edge", + "assign-entity-view-to-edge-text": "Selecteer de entiteitsweergaven die u aan de edge wilt toewijzen", + "unassign-entity-view-from-edge-title": "Weet u zeker dat u de toewijzing van de entiteitsweergave '{{entityViewName}}' wilt opheffen?", + "unassign-entity-view-from-edge-text": "Na de bevestiging wordt de toewijzing van de entiteitsweergave ongedaan gemaakt en is deze niet toegankelijk via de edge.", + "unassign-entity-views-from-edge-action-title": "Toewijzing { count, plural, =1 {1 entity view} andere {# entity views} } van edge ongedaan maken", + "unassign-entity-view-from-edge": "Toewijzing van entiteitsweergave ongedaan maken", + "unassign-entity-views-from-edge-title": "Weet u zeker dat u de toewijzing van { count, plural, =1 {1 entity view} andere {# entity views} } wilt opheffen?", + "unassign-entity-views-from-edge-text": "Na de bevestiging worden alle geselecteerde entiteitsweergaven ongedaan gemaakt en zijn ze niet toegankelijk via de edge." + }, + "event": { + "events": "Events", + "event-type": "Type event", + "events-filter": "Events filter", + "clean-events": "Events wissen", + "type-error": "Fout", + "type-lc-event": "Levens cyclus event", + "type-stats": "Statistiek", + "type-debug-converter": "Debuggen", + "type-debug-integration": "Debuggen", + "type-debug-rule-node": "Debuggen", + "type-debug-rule-chain": "Debuggen", + "no-events-prompt": "Geen events gevonden", + "error": "Fout", + "alarm": "Alarm", + "event-time": "Tijd voor de event", + "server": "Server", + "body": "Body", + "method": "Methode", + "type": "Type", + "in": "In", + "out": "Buiten", + "metadata": "Metagegevens", + "message": "Bericht", + "entity": "Entiteit", + "message-id": "Bericht-ID", + "copy-message-id": "Kopieer bericht-ID", + "message-type": "Type bericht", + "data-type": "Soort gegevens", + "relation-type": "Relatie Type", + "data": "Data", + "event": "Event", + "status": "Status", + "success": "Succes", + "failed": "Mislukt", + "messages-processed": "Verwerkte berichten", + "max-messages-processed": "Maximum aantal verwerkte berichten", + "min-messages-processed": "Minimaal aantal verwerkte berichten", + "errors-occurred": "Er zijn fouten opgetreden", + "max-errors-occurred": "Maximale fouten opgetreden", + "min-errors-occurred": "Er zijn minimale fouten opgetreden", + "min-value": "Minimale waarde is 0.", + "all-events": "Alle", + "has-error": "Heeft fout", + "entity-id": "Entiteit-ID", + "copy-entity-id": "Entiteits-ID kopiëren", + "entity-type": "Type entiteit", + "clear-filter": "Filter wissen", + "clear-request-title": "Wis alle events", + "clear-request-text": "Weet je zeker dat je alle events wilt wissen?", + "started": "Begon", + "updated": "Bijgewerkt", + "stopped": "Gestopt" + }, + "extension": { + "extensions": "Extensies", + "selected-extensions": "{ count, plural, =1 {1 extension} andere {# extensions} } geselecteerd", + "type": "Type", + "key": "Sleutel", + "value": "Waarde", + "id": "Legitimatiebewijs", + "extension-id": "Extensie-id", + "extension-type": "Type extensie", + "transformer-json": "JSON *", + "unique-id-required": "De huidige extensie-id bestaat al.", + "delete": "Extensie verwijderen", + "add": "Extensie toevoegen", + "edit": "Extensie bewerken", + "view": "Bekijk extensie", + "delete-extension-title": "Weet je zeker dat je de extensie '{{extensionId}}' wilt verwijderen?", + "delete-extension-text": "Opgelet, na de bevestiging worden de extensie en alle gerelateerde gegevens onherstelbaar.", + "delete-extensions-title": "Weet u zeker dat u { count, plural, =1 {1 extension} andere {# extensions} } wilt verwijderen?", + "delete-extensions-text": "Opgelet, na de bevestiging worden alle geselecteerde extensies verwijderd.", + "converters": "Converters", + "converter-id": "Converter-id", + "configuration": "Configuratie", + "converter-configurations": "Converter configuraties", + "token": "Beveiligingstoken", + "add-converter": "Converter toevoegen", + "add-config": "Converterconfiguratie toevoegen", + "device-name-expression": "Expressie van device naam", + "device-type-expression": "Expressie van device type", + "custom": "Gewoonte", + "to-double": "Verdubbelen", + "transformer": "Transformator", + "json-required": "Transformator json is vereist.", + "json-parse": "Kan transformator json niet parseren.", + "attributes": "Attributen", + "add-attribute": "Attribuut toevoegen", + "add-map": "Toewijzingselement toevoegen", + "timeseries": "Timeseries", + "add-timeseries": "Timeseries toevoegen", + "field-required": "Veld is verplicht", + "brokers": "Brokers", + "add-broker": "Broker toevoegen", + "host": "Server host", + "port": "Haven", + "port-range": "De poort moet tussen 1 en 65535 liggen.", + "ssl": "Ssl", + "credentials": "Inloggegevens", + "username": "Gebruikersnaam", + "password": "Wachtwoord", + "retry-interval": "Interval voor opnieuw proberen in milliseconden", + "sas": "Handtekening voor gedeelde toegang", + "anonymous": "Anoniem", + "basic": "Basis", + "pem": "PEM", + "ca-cert": "CA-certificaatbestand *", + "private-key": "Bestand met privésleutel *", + "cert": "Certificaat bestand *", + "no-file": "Geen bestand geselecteerd.", + "drop-file": "Zet een bestand neer of klik om een bestand te selecteren om te uploaden.", + "mapping": "Kartering", + "topic-filter": "Onderwerp filter", + "converter-type": "Type converter", + "converter-json": "Json", + "json-name-expression": "JSON-expressie voor device naam", + "topic-name-expression": "Topic expressie device naam", + "json-type-expression": "json-expressie van device type", + "topic-type-expression": "Topic expressie van device type", + "attribute-key-expression": "Expressie van kenmerksleutel", + "attr-json-key-expression": "Json-expressie van kenmerksleutel", + "attr-topic-key-expression": "Topic expressie van kenmerk", + "request-id-expression": "ID-expressie aanvragen", + "request-id-json-expression": "Aanvraag-id json-expressie", + "request-id-topic-expression": "Topic expressie voor aanvraag-id", + "response-topic-expression": "Expressie van topic response", + "value-expression": "Waarde expressie", + "topic": "Onderwerp", + "timeout": "Time-out in milliseconden", + "converter-json-required": "Converter json is vereist.", + "converter-json-parse": "Kan converter json niet parseren.", + "filter-expression": "Expressie filteren", + "connect-requests": "Verbind aanvragen", + "add-connect-request": "Verbindingsverzoek toevoegen", + "disconnect-requests": "Verzoeken om de verbinding te verbreken", + "add-disconnect-request": "Verzoek tot verbroken verbinding toevoegen", + "attribute-requests": "Attribuut aanvragen", + "add-attribute-request": "Attribuutaanvraag toevoegen", + "attribute-updates": "Updates van attributen", + "add-attribute-update": "Attribuutupdate toevoegen", + "server-side-rpc": "RPC aan de serverzijde", + "add-server-side-rpc-request": "RPC-verzoek aan serverzijde toevoegen", + "device-name-filter": "Filter voor device naam", + "attribute-filter": "Attribuut filter", + "method-filter": "Methode filter", + "request-topic-expression": "Topic expressie aanvragen", + "response-timeout": "Time-out voor respons in milliseconden", + "topic-expression": "Onderwerp expressie", + "client-scope": "Toepassingsgebied van de klant", + "add-device": "Device toevoegen", + "opc-server": "Servers", + "opc-add-server": "Server toevoegen", + "opc-add-server-prompt": "Gelieve server toe te voegen", + "opc-application-name": "Naam van de toepassing", + "opc-application-uri": "Applicatie uri", + "opc-scan-period-in-seconds": "Scanperiode in seconden", + "opc-security": "Veiligheid", + "opc-identity": "Identiteit", + "opc-keystore": "Sleutel opslag", + "opc-type": "Type", + "opc-keystore-type": "Type", + "opc-keystore-location": "Plaats*", + "opc-keystore-password": "Wachtwoord", + "opc-keystore-alias": "Alias", + "opc-keystore-key-password": "Sleutel wachtwoord", + "opc-device-node-pattern": "Knooppuntpatroon van het device", + "opc-device-name-pattern": "Patroon van device naam", + "modbus-server": "Servers", + "modbus-add-server": "Server toevoegen", + "modbus-add-server-prompt": "Gelieve server toe te voegen", + "modbus-transport": "Vervoer", + "modbus-tcp-reconnect": "Automatisch opnieuw verbinding maken", + "modbus-rtu-over-tcp": "RTU via TCP", + "modbus-port-name": "Naam seriële poort", + "modbus-encoding": "Codering", + "modbus-parity": "Pariteit", + "modbus-baudrate": "Baudrate", + "modbus-databits": "Data bits gegevens", + "modbus-stopbits": "Data bits stop", + "modbus-databits-range": "Data bits moeten tussen 7 en 8 liggen.", + "modbus-stopbits-range": "Stop bits moeten in een bereik van 1 tot 2 liggen.", + "modbus-unit-id": "Eenheid-ID", + "modbus-unit-id-range": "De eenheids-ID moet tussen 1 en 247 liggen.", + "modbus-device-name": "Naam van het device", + "modbus-poll-period": "Enquête periode (ms)", + "modbus-attributes-poll-period": "Attributen enquêteperiode (ms)", + "modbus-timeseries-poll-period": "Tijdreeks-enquêteperiode (ms)", + "modbus-poll-period-range": "De enquêteperiode moet een positieve waarde hebben.", + "modbus-tag": "Label", + "modbus-function": "Functie", + "modbus-register-address": "Adres registreren", + "modbus-register-address-range": "Het registratieadres moet tussen 0 en 65535 liggen.", + "modbus-register-bit-index": "De index van het bit", + "modbus-register-bit-index-range": "De bitindex moet tussen 0 en 15 liggen.", + "modbus-register-count": "Aantal registreren", + "modbus-register-count-range": "Het aantal registers moet een positieve waarde zijn.", + "modbus-byte-order": "Byte volgorde", + "sync": { + "status": "Status", + "sync": "Sync", + "not-sync": "Niet synchroniseren", + "last-sync-time": "Laatste synchronisatietijd", + "not-available": "Niet beschikbaar" + }, + "export-extensions-configuration": "Configuratie van extensies exporteren", + "import-extensions-configuration": "Configuratie van extensies importeren", + "import-extensions": "Extensies importeren", + "import-extension": "Extensie importeren", + "export-extension": "Export extensie", + "file": "Extensies bestand", + "invalid-file-error": "Ongeldig extensiebestand", + "text": "SMS", + "json": "JSON", + "binary": "BINAIR", + "hex": "HEX" + }, + "feature": { + "advanced-features": "Geavanceerde functies" + }, + "filter": { + "add": "Filter toevoegen", + "edit": "Filter bewerken", + "name": "Naam van filter", + "name-required": "Filternaam is vereist.", + "duplicate-filter": "Filter met dezelfde naam bestaat al.", + "filters": "Filters", + "unable-delete-filter-title": "Filter kan niet worden verwijderd", + "unable-delete-filter-text": "Filter '{{filter}}' kan niet worden verwijderd omdat het wordt gebruikt door de volgende widget(s):
{{widgetsList}}", + "duplicate-filter-error": "Duplicaatfilter gevonden '{{filter}}'.
Filters moeten uniek zijn binnen het dashboard.", + "missing-key-filters-error": "Sleutelfilters ontbreken voor filter '{{filter}}'.", + "filter": "Filter", + "editable": "Bewerkbaar", + "no-filters-found": "Geen filters gevonden.", + "no-filter-text": "Geen filter gespecificeerd", + "add-filter-prompt": "Filter toevoegen", + "no-filter-matching": "'{{filter}}' niet gevonden.", + "create-new-filter": "Maak een nieuwe aan!", + "filter-required": "Filter is vereist.", + "operation": { + "operation": "Operatie", + "equal": "gelijk", + "not-equal": "Niet gelijk", + "starts-with": "begint met", + "ends-with": "eindigt met", + "contains": "Bevat", + "not-contains": "Bevat niet", + "greater": "groter dan", + "less": "minder dan", + "greater-or-equal": "groter of gelijk", + "less-or-equal": "minder of gelijk aan", + "and": "en", + "or": "of", + "in": "in", + "not-in": "Niet in" + }, + "ignore-case": "Hoofdlettergebruik negeren", + "value": "Waarde", + "remove-filter": "Filter verwijderen", + "duplicate-filter-action": "Filter dupliceren", + "preview": "Voorbeeld van filter", + "no-filters": "Geen filters geconfigureerd", + "add-filter": "Filter toevoegen", + "add-complex-filter": "Complex filter toevoegen", + "add-complex": "Complex toevoegen", + "complex-filter": "Complex filter", + "edit-complex-filter": "Complex filter bewerken", + "edit-filter-user-params": "Gebruikersparameters voor filter predicate bewerken", + "filter-user-params": "Gebruikersparameters voor predicaten filteren", + "user-parameters": "Parameters van de gebruiker", + "display-label": "Label om weer te geven", + "autogenerated-label": "Automatisch label genereren", + "order-priority": "Prioriteit voor veldorders", + "key-filter": "Toets filter", + "key-filters": "Belangrijkste filters", + "key-name": "Naam van de sleutel", + "key-name-required": "De naam van de sleutel is vereist.", + "key-type": { + "key-type": "Type sleutel", + "attribute": "Attribuut", + "timeseries": "Timeseries", + "entity-field": "Entiteit veld", + "constant": "Constant" + }, + "value-type": { + "value-type": "Soort waarde", + "string": "Snaar", + "numeric": "Numeriek", + "boolean": "Booleaans", + "date-time": "Datum/tijd" + }, + "value-type-required": "Het type sleutelwaarde is vereist.", + "key-value-type-change-title": "Weet u zeker dat u het type sleutelwaarde wilt wijzigen?", + "key-value-type-change-message": "Als u het nieuwe waardetype bevestigt, worden alle ingevoerde toetsfilters verwijderd.", + "no-key-filters": "Geen toetsfilters geconfigureerd", + "add-key-filter": "Sleutelfilter toevoegen", + "remove-key-filter": "Sleutelfilter verwijderen", + "edit-key-filter": "Sleutelfilter bewerken", + "date": "Datum", + "time": "Tijd", + "current-tenant": "Huidige tenant", + "current-customer": "Huidige klant", + "current-user": "Huidige gebruiker", + "current-device": "Huidig device", + "default-value": "Standaardwaarde", + "dynamic-source-type": "Dynamisch brontype", + "dynamic-value": "Dynamische waarde", + "no-dynamic-value": "Geen dynamische waarde", + "source-attribute": "Bron attribuut", + "switch-to-dynamic-value": "Overschakelen naar dynamische waarde", + "switch-to-default-value": "Overschakelen naar standaardwaarde", + "inherit-owner": "Erven van eigenaar", + "source-attribute-not-set": "Als het bronkenmerk niet is ingesteld" + }, + "fullscreen": { + "expand": "Uitvouwen naar volledig scherm", + "exit": "Volledig scherm afsluiten", + "toggle": "Modus voor volledig scherm in- en uitschakelen", + "fullscreen": "Volledig scherm" + }, + "function": { + "function": "Functie" + }, + "gateway": { + "add-entry": "Configuratie toevoegen", + "connector-add": "Nieuwe connector toevoegen", + "connector-enabled": "Connector inschakelen", + "connector-name": "Naam van de connector", + "connector-name-required": "De naam van de connector is vereist.", + "connector-type": "Type aansluiting", + "connector-type-required": "Het type connector is vereist.", + "connectors": "Configuratie van connectoren", + "create-new-gateway": "Een nieuwe gateway maken", + "create-new-gateway-text": "Weet u zeker dat u een nieuwe gateway wilt maken met de naam: '{{gatewayName}}'?", + "delete": "Configuratie verwijderen", + "download-tip": "Configuratiebestand downloaden", + "gateway": "Gateway", + "gateway-exists": "Device met dezelfde naam bestaat al.", + "gateway-name": "Naam van de gateway", + "gateway-name-required": "De naam van de gateway is vereist.", + "gateway-saved": "Gatewayconfiguratie succesvol opgeslagen.", + "json-parse": "Ongeldige JSON.", + "json-required": "Het veld mag niet leeg zijn.", + "no-connectors": "Geen connectoren", + "no-data": "Geen configuraties", + "no-gateway-found": "Geen gateway gevonden.", + "no-gateway-matching": "'{{item}}' niet gevonden.", + "path-logs": "Pad naar logbestanden", + "path-logs-required": "Pad is vereist.", + "remote": "Configuratie op afstand", + "remote-logging-level": "Registratie niveau", + "remove-entry": "Configuratie verwijderen", + "save-tip": "Configuratiebestand opslaan", + "security-type": "Soort beveiliging", + "security-types": { + "access-token": "Toegang tot token", + "tls": "TLS (TLS)" + }, + "storage": "Opslag", + "storage-max-file-records": "Maximum aantal records in bestand", + "storage-max-files": "Maximaal aantal bestanden", + "storage-max-files-min": "Minimum aantal is 1.", + "storage-max-files-pattern": "Nummer is niet geldig.", + "storage-max-files-required": "Nummer is vereist.", + "storage-max-records": "Maximum aantal records in opslag", + "storage-max-records-min": "Minimum aantal records is 1.", + "storage-max-records-pattern": "Nummer is niet geldig.", + "storage-max-records-required": "Maximale records zijn vereist.", + "storage-pack-size": "Maximale pakketgrootte voor events", + "storage-pack-size-min": "Minimum aantal is 1.", + "storage-pack-size-pattern": "Nummer is niet geldig.", + "storage-pack-size-required": "De maximale pakketgrootte van het event is vereist.", + "storage-path": "Opslag pad", + "storage-path-required": "Opslagpad is vereist.", + "storage-type": "Type opslag", + "storage-types": { + "file-storage": "Opslag van bestanden", + "memory-storage": "Geheugen opslag" + }, + "thingsboard": "Dingen Bord", + "thingsboard-host": "ThingsBoard-gastheer", + "thingsboard-host-required": "Server host is vereist.", + "thingsboard-port": "ThingsBoard-poort", + "thingsboard-port-max": "Het maximale poortnummer is 65535.", + "thingsboard-port-min": "Het minimale poortnummer is 1.", + "thingsboard-port-pattern": "Poort is niet geldig.", + "thingsboard-port-required": "Poort is vereist.", + "tidy": "Ordelijk", + "tidy-tip": "Opgeruimde configuratie JSON", + "title-connectors-json": "Configuratie van connector {{typeName}}", + "tls-path-ca-certificate": "Pad naar CA-certificaat op gateway", + "tls-path-client-certificate": "Pad naar clientcertificaat op gateway", + "tls-path-private-key": "Pad naar privésleutel op gateway", + "toggle-fullscreen": "Volledig scherm in- en uitschakelen", + "transformer-json-config": "Configuratie JSON*", + "update-config": "Configuratie JSON toevoegen/bijwerken" + }, + "grid": { + "delete-item-title": "Weet u zeker dat u dit item wilt verwijderen?", + "delete-item-text": "Opgelet, na de bevestiging zullen dit item en alle gerelateerde gegevens onherstelbaar worden.", + "delete-items-title": "Weet u zeker dat u { count, plural, =1 {1 item} andere {# items} } wilt verwijderen?", + "delete-items-action-title": "Verwijder { count, plural, =1 {1 item} andere {# items} }", + "delete-items-text": "Opgelet, na de bevestiging worden alle geselecteerde items verwijderd en kunnen alle gerelateerde gegevens niet meer worden hersteld.", + "add-item-text": "Nieuw item toevoegen", + "no-items-text": "Geen items gevonden", + "item-details": "Details van het artikel", + "delete-item": "Item verwijderen", + "delete-items": "Items verwijderen", + "scroll-to-top": "Scroll naar bovenzijde" + }, + "help": { + "goto-help-page": "Ga naar de helppagina", + "show-help": "Hulp tonen" + }, + "home": { + "home": "Thuis", + "profile": "Profiel", + "logout": "Logout", + "menu": "Menu", + "avatar": "Avatar", + "open-user-menu": "Gebruikersmenu openen" + }, + "file-input": { + "browse-file": "Blader door het bestand", + "browse-files": "Blader door bestanden" + }, + "image-input": { + "drop-image-or": "Een afbeelding slepen en neerzetten of", + "drop-images-or": "Sleep een afbeelding of", + "no-images": "Geen afbeeldingen geselecteerd", + "images": "beelden" + }, + "import": { + "no-file": "Geen bestand geselecteerd", + "drop-file": "Zet een JSON-bestand neer of klik om een bestand te selecteren om te uploaden.", + "drop-csv-file": "Zet een CSV-bestand neer of klik om een bestand te selecteren om te uploaden.", + "drop-json-file-or": "Een JSON-bestand slepen en neerzetten of", + "drop-file-csv": "Zet een CSV-bestand neer of klik om een bestand te selecteren om te uploaden.", + "drop-file-csv-or": "Een CSV-bestand slepen en neerzetten of", + "column-value": "Waarde", + "column-title": "Titel", + "column-example": "Voorbeeld van waardegegevens", + "column-key": "Kenmerk-/telemetriesleutel", + "credentials": "Geloofsbrief", + "csv-delimiter": "CSV-scheidingsteken", + "csv-first-line-header": "De eerste rule bevat kolomnamen", + "csv-update-data": "Attributen/telemetrie bijwerken", + "details": "Details", + "import-csv-number-columns-error": "Een bestand moet ten minste twee kolommen bevatten", + "import-csv-invalid-format-error": "Ongeldige bestandsindeling. Regel: '{{line}}'", + "column-type": { + "name": "Naam", + "type": "Type", + "label": "Etiket", + "column-type": "Type kolom", + "client-attribute": "Cliënt attribuut", + "shared-attribute": "Gedeeld attribuut", + "server-attribute": "Server-attribuut", + "timeseries": "Timeseries", + "entity-field": "Entiteit veld", + "access-token": "Toegang token", + "x509": "X.509", + "mqtt": { + "client-id": "MQTT-client-id", + "user-name": "MQTT-gebruikersnaam", + "password": "MQTT-wachtwoord" + }, + "lwm2m": { + "client-endpoint": "Naam van de LwM2M-endpoint", + "security-config-mode": "LwM2M-modus voor beveiligingsconfiguratie", + "client-identity": "Identiteit van LwM2M-client", + "client-key": "LwM2M-clientsleutel", + "client-cert": "Openbare sleutel van LwM2M-client", + "bootstrap-server-security-mode": "LwM2M bootstrap-serverbeveiligingsmodus", + "bootstrap-server-secret-key": "LwM2M bootstrap server geheime sleutel", + "bootstrap-server-public-key-id": "LwM2M opstarttrap, server openbare sleutel of id", + "lwm2m-server-security-mode": "LwM2M-server beveiligingsmodus", + "lwm2m-server-secret-key": "Geheime sleutel van de LwM2M-server", + "lwm2m-server-public-key-id": "Openbare sleutel of id van de LwM2M-server" + }, + "isgateway": "Is Gateway", + "activity-time-from-gateway-device": "Activiteitstijd vanaf gateway-device", + "description": "Omschrijving: __________", + "edge-license-key": "Licentiesleutel", + "cloud-endpoint": "Cloud-eindpunt", + "routing-key": "Edge routing sleutel", + "secret": "Edge geheim" + }, + "stepper-text": { + "select-file": "Selecteer een bestand", + "configuration": "Configuratie importeren", + "column-type": "Kolomtype selecteren", + "creat-entities": "Nieuwe entiteiten maken" + }, + "message": { + "create-entities": "{{count}} zijn met succes nieuwe entiteiten gecreëerd.", + "update-entities": "{{count}} entiteiten zijn bijgewerkt.", + "error-entities": "Er is een fout opgetreden bij het maken van {{count}} entiteiten." + } + }, + "integration": { + "integration": "Integratie", + "integrations": "Integraties", + "integrations-center": "Integratiecentrum", + "select-integration": "Selecteer integratie", + "no-integrations-matching": "Er zijn geen integraties gevonden die overeenkomen met '{{entity}}'.", + "integration-required": "Integratie is vereist", + "delete": "Integratie verwijderen", + "management": "Beheer van integraties", + "add-integration-text": "Nieuwe integratie toevoegen", + "no-integrations-text": "Geen integraties gevonden", + "selected-integrations": "{ count, plural, =1 {1 integration} andere {# integrations} } geselecteerd", + "delete-integration-title": "Weet je zeker dat je de integratie '{{integrationName}}' wilt verwijderen?", + "delete-integration-text": "Opgelet, na de bevestiging worden de integratie en alle gerelateerde gegevens onherstelbaar.", + "delete-integrations-title": "Weet u zeker dat u { count, plural, =1 {1 integration} andere {# integrations} } wilt verwijderen?", + "delete-integrations-action-title": "Verwijder { count, plural, =1 {1 integration} andere {# integrations} }", + "delete-integrations-text": "Opgelet, na de bevestiging worden alle geselecteerde integraties verwijderd en worden alle gerelateerde gegevens onherstelbaar.", + "events": "Events", + "enabled": "Integratie inschakelen", + "allow-create-devices-or-assets": "Devices of assets maken toestaan", + "all-types": "Alle integratietypes", + "add": "Integratie toevoegen", + "search": "Integraties zoeken", + "integration-details": "Details van de integratie", + "details": "Details", + "copyId": "Integratie-ID kopiëren", + "idCopiedMessage": "Integratie-id is gekopieerd naar klembord", + "debug-mode": "Foutopsporingsmodus", + "enable-security": "Beveiliging inschakelen", + "enable-security-new": "Beveiliging inschakelen voor automatische tokenupdates", + "headers-filter": "Filter voor kopteksten", + "header": "Rubriek", + "no-headers-filter": "Geen headers filter", + "downlink-url": "Downlink URL", + "downlink-url-required": "Downlink-URL is vereist", + "create-loriot-output": "Uitvoer van Loriot-toepassingen maken", + "send-downlink": "Downlink verzenden", + "server": "Server", + "server-required": "Server is vereist", + "domain": "Domein", + "app-id": "Toepassings-ID", + "app-id-required": "Toepassings-ID is vereist", + "app-token": "Token voor toegang tot toepassing", + "app-token-required": "Application Access Token is vereist", + "email": "E-mail", + "email-required": "E-mail is verplicht", + "application-uri": "Toepassings-URI", + "as-id": "AS ID", + "as-id-required": "AS-ID is vereist.", + "as-key": "AS-toets", + "as-key-required": "AS-sleutel is vereist.", + "client-id-new": "Client-ID", + "client-id-new-required": "Client-ID (login) is vereist (login).", + "client-secret": "Geheim van de klant", + "client-secret-required": "Client Secret (wachtwoord) is vereist (wachtwoord).", + "max-time-diff-in-seconds": "Maximaal tijdsverschil (seconden)", + "max-time-diff-in-seconds-required": "Maximaal tijdsverschil is vereist.", + "created-time": "Gecreëerde tijd", + "name": "Naam", + "name-required": "Naam is verplicht.", + "name-max-length": "Naam moet kleiner zijn dan 256 tekens", + "description": "Omschrijving: __________", + "base-url": "Basis-URL", + "base-url-required": "Basis-URL is vereist", + "security-key": "Beveiligingssleutel", + "http-endpoint": "URL van HTTP-eindpunt", + "replace-no-content-to-ok": "Vervang de reactiestatus van 'Geen inhoud' naar 'OK'", + "copy-http-endpoint-url": "Kopieer de URL van het HTTP-eindpunt", + "http-endpoint-url-copied-message": "De URL van het HTTP-eindpunt is gekopieerd naar het klembord", + "host": "Server host", + "host-required": "Server host is vereist.", + "host-private": "De host moet zich in het openbare netwerk bevinden.", + "url-private": "De URL moet zich in het openbare netwerk bevinden.", + "host-type": "Type host", + "host-type-required": "Hosttype is vereist.", + "api-version": "API v3 gebruiken", + "custom-host": "Aangepaste host", + "custom-host-required": "Aangepaste host is vereist.", + "port": "Poort", + "port-required": "Poort is vereist.", + "port-range": "De poort moet tussen 1 en 65535 liggen.", + "connect-timeout": "Time-out verbinding (sec)", + "connect-timeout-required": "Er is een time-out voor de verbinding vereist.", + "connect-timeout-range": "De time-out van de verbinding moet tussen 1 en 200 liggen.", + "client-id": "Client-ID", + "client-id-required": "Client-ID is vereist.", + "client-id-range": "Client-ID moet tussen de 1 en 23 tekens lang zijn. [MQTT-3.1.3-5]", + "client-id-pattern": "Client-ID moet bestaan uit cijfers, hoofdletters en kleine letters. [MQTT-3.1.3-5]", + "client-id-hint": "Tip: Optioneel. Laat dit veld leeg voor automatisch gegenereerde client-ID. Opgelet bij het opgeven van de Client ID. De meerderheid van de MQTT-brokers staat niet toe dat meerdere verbindingen met dezelfde client-ID worden gebruikt. Om verbinding te maken met dergelijke makelaars, moet uw MQTT-klant-ID uniek zijn.", + "max-bytes-in-message": "Max. aantal bytes in bericht", + "max-bytes-in-message-range": "Het maximale aantal bytes in een bericht moet tussen 1 en 256000000 liggen.", + "device-id": "Device-ID", + "device-id-required": "Device-ID is vereist.", + "device-id-range": "Device-ID moet tussen 1 en 65535 tekens bestaan.", + "device-id-pattern": "Device-ID moet bestaan uit cijfers, hoofdletters en kleine letters. [MQTT-3.1.3-5]", + "group-id": "Groeps-ID", + "group-id-required": "Groeps-ID is vereist.", + "topics": "Onderwerpen", + "topics-required": "Onderwerp is vereist.", + "routing-keys": "Routing sleutels", + "routing-keys-required": "Routingsleutel is vereist.", + "queues": "Queues", + "queues-required": "De naam van de queue is vereist.", + "durable": "Duurzaam", + "exclusive": "Exclusief", + "autoDelete": "Automatisch verwijderen", + "exchange-name": "Naam van de uitwisseling", + "exchange-name-required": "De naam van de uitwisseling is vereist.", + "downlink-topic": "Downlink-onderwerp", + "connection-timeout": "Time-out van de verbinding, ms", + "connection-timeout-min": "Ongeldige time-outwaarde voor verbinding.", + "handshake-timeout": "Time-out voor handdruk, ms", + "handshake-timeout-min": "Ongeldige time-outwaarde voor handdruk.", + "virtual-host": "Virtuele gastheer", + "rabbit-mq-poll-interval": "Polling-interval, ms", + "rabbit-mq-poll-interval-min": "Ongeldige waarde voor de polling-interval.", + "application-server-url": "URL van applicatieserver", + "application-server-url-required": "De URL van de toepassingsserver is vereist.", + "application-server-api-token": "API-token voor toepassingsserver", + "application-server-api-token-required": "API-token van toepassingsserver is vereist.", + "bootstrap-servers": "Bootstrap-servers", + "bootstrap-servers-required": "Bootstrap-servers zijn vereist.", + "poll-interval": "Poll-interval", + "poll-interval-required": "Poll-interval is vereist.", + "poll-interval-min-value": "De waarde van het pollinterval mag niet kleiner zijn dan 1", + "auto-create-topics": "Onderwerpen automatisch maken", + "clean-session": "Vernieuwde sessie", + "enable-ssl": "SSL inschakelen", + "credentials": "Inloggegevens", + "credentials-type": "Type referenties", + "credentials-type-required": "Het type referentie is vereist.", + "username": "Gebruikersnaam", + "username-required": "Gebruikersnaam is vereist.", + "password": "Wachtwoord", + "password-required": "Wachtwoord is vereist.", + "azure-ca-cert": "CA-certificaatbestand", + "ca-cert": "CA-certificaatbestand", + "private-key": "Bestand met privésleutel", + "private-key-password": "Wachtwoord voor privésleutel", + "cert": "Certificaat bestand", + "no-file": "Geen bestand geselecteerd.", + "drop-file": "Zet een bestand neer of klik om een bestand te selecteren om te uploaden.", + "drop-file-or": "Een bestand slepen en neerzetten of", + "check-connection": "Verbinding controleren", + "check-success": "Verbinding tot stand gebracht.", + "topic-filters": "Onderwerp filters", + "remove-topic-filter": "Onderwerpfilter verwijderen", + "add-topic-filter": "Onderwerpfilter toevoegen", + "add-topic-filter-prompt": "Voeg een onderwerpfilter toe", + "topic": "Onderwerp", + "mqtt-qos": "QoS", + "mqtt-qos-at-most-once": "Hoogstens één keer", + "mqtt-qos-at-least-once": "Minstens één keer", + "mqtt-qos-exactly-once": "Precies één keer", + "downlink-topic-pattern": "Patroon voor downlinkonderwerpen", + "downlink-topic-pattern-required": "Downlink-onderwerppatroon is vereist.", + "retained-message": "Behouden", + "aws-access-key-id": "AWS-toegangssleutel-ID", + "aws-secret-access-key": "Geheime AWS-toegangssleutel", + "aws-region": "Regio", + "aws-iot-endpoint": "AWS IoT-eindpunt", + "aws-iot-endpoint-required": "AWS IoT Endpoint is vereist.", + "aws-iot-credentials": "AWS IoT-referenties", + "aws-sqs-polling-period-in-seconds": "Pollingperiode in seconden", + "aws-sqs-queue-url": "URL van SQS-queue", + "aws-sqs-queue-url-required": "SQS Queue URL is vereist", + "aws-sqs-access-key-id-required": "Toegangssleutel-ID is vereist", + "aws-sqs-secret-access-key-required": "Geheime toegangssleutel is vereist", + "application-credentials": "Aanmeldingsgegevens van de toepassing", + "api-key": "API-sleutel", + "api-key-required": "API-sleutel is vereist.", + "api-key-format": "Ongeldige API-sleutelindeling.", + "auth-token": "Authenticatie token", + "auth-token-required": "Verificatietoken is vereist.", + "region": "Regio", + "region-required": "Regio is vereist.", + "application-id": "Toepassings-ID", + "application-id-required": "Toepassings-ID is vereist.", + "access-id": "Toegangs-ID", + "access-id-required": "Toegangs-ID is vereist.", + "access-key": "Toegangssleutel", + "access-key-required": "Toegangssleutel is vereist.", + "access-key-min-length": "Toegangssleutel is te kort.", + "access-key-max-length": "Toegangssleutel is te lang.", + "connection-parameters": "Verbinding parameters", + "service-bus-namespace-name": "Naam van Service Bus-namespace", + "service-bus-namespace-name-required": "Service Bus-namespace naam is vereist.", + "connection-string": "Connectionstring", + "consumer-group": "Consumentengroep", + "connection-string-required": "Aansluitreeks vereist!", + "event-hub-name": "Naam Event Hub", + "event-hub-name-required": "Event Hub-naam is vereist.", + "event-iot-hub-name-required": "Iot Hub-naam is vereist voor downlink", + "sas-key-name": "SAS-sleutelnaam", + "sas-key-name-required": "SAS-sleutelnaam is vereist.", + "sas-key": "SAS-toets", + "sas-key-required": "SAS-sleutel is vereist.", + "iot-hub-name": "IoT Hub-naam (vereist voor downlink)", + "hostname": "Hostnaam", + "hostname-required": "Hostnaam is vereist", + "integration-clazz": "Integratie klasse", + "integration-clazz-required": "Integratie klasse is vereist", + "integration-configuration": "Integratie JSON-configuratie", + "metadata": "Metagegevens", + "type": "Type integratie", + "select-integration-type": "Selecteer integratietype", + "type-required": "Integratietype is vereist.", + "type-not-found": "Integratietype niet gevonden", + "uplink-converter": "Uplink data converter", + "uplink-converter-required": "Uplink-gegevensconverter is vereist.", + "downlink-converter": "Downlink data converter", + "type-http": "HTTP (HTTP)", + "type-http-description": "Protocol voor het overbrengen van tekst- of multimediabestanden.", + "type-ocean-connect": "Oceaan verbinden", + "type-ocean-connect-description": "Integratie van devices met verschillende mogelijkheden met het IoT-platform.", + "type-sigfox": "SigFox", + "type-sigfox-description": "Lichtgewicht protocol voor het verwerken van kleine gegevensoverdrachten.", + "type-thingpark": "ThingPark", + "type-thingpark-description": "Berichtgeoriënteerd protocol voor transportlagen.", + "type-loriot": "Loriot", + "type-loriot-description": "Gedistribueerde LoRaWAN-infrastructuur.", + "type-thingpark-enterprise": "ThingParkEnterprise", + "type-thingpark-enterprise-description": "Berichtenplatform voor het LPWA-netwerk.", + "type-tmobile-iot-cdp": "iotcreators.com (T-Mobile – IoT CDP)", + "type-tmobile-iot-cdp-description": "Mobiele IoT-connectiviteitsservice.", + "type-mqtt": "MQTT", + "type-mqtt-description": "Netwerkprotocol van machine naar machine.", + "type-aws-iot": "AWS IoT", + "type-aws-iot-description": "Beheer-, opslag- en analyseservice.", + "type-aws-sqs": "AWS SQS", + "type-aws-sqs-description": "Volledig beheerde message queuing-service.", + "type-aws-kinesis": "AWS-kinesis", + "type-aws-kinesis-description": "Service voor real-time video- en datastreams.", + "type-ibm-watson-iot": "IBM Watson IoT", + "type-ibm-watson-iot-description": "Volledig beheerde, in de cloud gehoste service.", + "type-ttn": "De Things Stack-gemeenschap", + "type-ttn-description": "Open en gedecentraliseerd LoRaWAN-netwerk.", + "type-tti": "De dingen stapelen industrieën", + "type-tti-description": "Software om private LoRaWAN-netwerken op grote schaal te exploiteren.", + "type-chirpstack": "ChirpStack", + "type-chirpstack-description": "LoRaWAN-netwerkconfiguratieserver.", + "type-azure-event-hub": "Azure Event Hub", + "type-azure-event-hub-description": "Big data-streamingplatform en service voor het opnemen van events.", + "type-azure-iot-hub": "Azure IoT Hub", + "type-azure-iot-hub-description": "In de cloud gehoste berichtenservice.", + "type-opc-ua": "OPC-UA", + "type-opc-ua-description": "Cross-platform voor gegevensuitwisseling van sensoren naar cloud-apps.", + "type-custom": "Gewoonte", + "type-coap": "CoAP", + "type-coap-description": "Netwerkprotocol van machine naar machine voor beperkte Devices.", + "type-udp": "UDP", + "type-udp-description": "Berichtgeoriënteerd protocol voor transportlagen.", + "type-tcp": "TCP", + "type-tcp-description": "Verbindingsgericht protocol.", + "type-kafka": "Kafka", + "type-kafka-description": "Streaming analytics, data-integratie streaming platform.", + "type-rabbitmq": "KonijnMQ", + "type-rabbitmq-description": "Berichtenbroker die ondersteuning biedt voor meerdere berichtenprotocollen.", + "type-pubsub": "Kroeg/Sub", + "type-pubsub-description": "Service voor asynchrone berichten tussen apps.", + "type-apache-pulsar": "Apache Pulsar", + "type-apache-pulsar-description": "Gedistribueerd pub-sub berichten- en streamingplatform.", + "type-tuya": "Tuya", + "type-tuya-description": "In de cloud gehoste berichten. platform van het device.", + "type-azure-service-bus": "Azure Service Bus", + "type-azure-service-bus-description": "Volledig beheerde berichtenmakelaar voor ondernemingen met berichtwachtrijen en onderwerpen voor publiceren en abonneren.", + "opc-ua-application-name": "Naam van de toepassing", + "opc-ua-application-uri": "Applicatie uri", + "opc-ua-scan-period-in-seconds": "Scanperiode in seconden", + "opc-ua-scan-period-in-seconds-required": "Scanperiode is vereist", + "opc-ua-timeout": "Time-out in milliseconden", + "opc-ua-timeout-required": "Time-out is vereist", + "opc-ua-security": "Veiligheid", + "opc-ua-security-required": "Beveiliging is vereist", + "opc-ua-identity": "Identiteit", + "opc-ua-identity-required": "Legitimatie is vereist", + "opc-ua-keystore": "Sleutel opslag", + "add-opc-ua-keystore-prompt": "Gelieve keystore-bestand toe te voegen", + "opc-ua-keystore-required": "Keystore is vereist", + "opc-ua-type": "Type", + "opc-ua-keystore-type": "Type", + "opc-ua-keystore-type-required": "Type is vereist", + "opc-ua-keystore-location": "Plaats*", + "opc-ua-keystore-password": "Wachtwoord", + "opc-ua-keystore-password-required": "Wachtwoord is vereist", + "opc-ua-keystore-alias": "Alias", + "opc-ua-keystore-alias-required": "Alias is vereist", + "opc-ua-keystore-key-password": "Sleutel wachtwoord", + "opc-ua-keystore-key-password-required": "Sleutelwachtwoord is vereist", + "opc-ua-mapping": "Kartering", + "add-opc-ua-mapping-prompt": "Gelieve kaarten toe te voegen", + "opc-ua-mapping-type": "Type toewijzing", + "opc-ua-mapping-type-required": "Toewijzingstype is vereist", + "opc-ua-device-node-pattern": "Patroon van apparaatknooppunten", + "opc-ua-device-node-pattern-required": "Apparaatknooppuntpatroon is vereist", + "opc-ua-namespace": "Naamruimte", + "opc-ua-add-map": "Toewijzingselement toevoegen", + "kinesis-stream-name": "Naam van de stream", + "kinesis-stream-name-required": "Streamnaam is vereist", + "kinesis-region": "Regio", + "kinesis-region-required": "Regio is vereist", + "kinesis-access-key-id": "Toegangssleutel-ID", + "kinesis-access-key-id-required": "Toegangssleutel-ID is vereist", + "kinesis-secret-access-key": "Geheime toegangssleutel", + "kinesis-secret-access-key-required": "Geheime toegangssleutel is vereist", + "kinesis-use-consumers-with-enhanced-fan-out": "Gebruik consumenten met verbeterde fan-out", + "kinesis-use-credentials-from-instance-metadata": "Referenties van de Amazon EC2 Instance Metadata Service gebruiken", + "kinesis-application-name": "Toepassingsnaam (standaard gelijk aan Streamnaam)", + "kinesis-initial-position-in-stream": "Beginpositie in stream", + "kinesis-initial-position-in-stream-required": "Beginpositie in stream vereist", + "kinesis-max-records": "Max. records", + "kinesis-max-records-required": "Max. records is vereist", + "kinesis-max-records-length-range": "De maximale recordlengte moet tussen 1 en 10000 liggen", + "kinesis-request-timeout": "Time-out aanvragen in enkele seconden", + "kinesis-request-timeout-required": "Time-out voor aanvraag is vereist", + "other-properties": "Overige eigenschappen", + "subscription-tags": "Tags voor abonnementen", + "remove-subscription-tag": "Abonnementstag verwijderen", + "add-subscription-tag": "Abonnementstag toevoegen", + "add-subscription-tag-prompt": "Voeg een abonnementstag toe", + "key": "Sleutel", + "path": "Pad", + "required": "Vereist", + "integration-key": "Integratiesleutel", + "copy-integration-key": "Integratiesleutel kopiëren", + "integration-key-copied-message": "Integratiesleutel is gekopieerd naar klembord", + "integration-secret": "Integratie geheim", + "copy-integration-secret": "Integratiegeheim kopiëren", + "integration-secret-copied-message": "Integratiegeheim is gekopieerd naar klembord", + "execute-remotely": "Op afstand uitvoeren", + "remote": "Afgelegen", + "handler-configuration": "Configuratie van de handler", + "handler-configuration-type": "Handler Type", + "so-broadcast": "Uitzending inschakelen - integratie accepteert uitzendadrespakketten", + "so-keepalive-option": "Verzenden van keep-alive-berichten op verbindingsgeoriënteerde stopcontacten mogelijk maken", + "so-reuse-addr": "Bind het proces aan een poort", + "tcp-no-delay": "Dwingt een socket om de gegevens te verzenden zonder buffering (schakel het bufferalgoritme van Nagle uit)", + "fail-fast": "Gegooide uitzondering zodra de decoder merkt dat de lengte van het frame de maximale grootte zal overschrijden", + "strip-delimiter": "Scheidingsteken voor stroken", + "length-field-offset": "Lengte veld offset", + "length-field-offset-required": "Lengte veldverschuiving is vereist.", + "length-field-offset-range": "De lengteveldverschuiving moet tussen 0 en 8 liggen.", + "length-field-length": "Lengte Veldlengte", + "length-field-length-required": "Lengte Veldlengte is vereist.", + "length-field-length-range": "Lengte Veldlengte moet tussen 0 en 8 liggen.", + "length-adjustment": "Lengteaanpassing (de compensatiewaarde die moet worden toegevoegd aan de waarde van het lengteveld)", + "length-adjustment-required": "Lengteaanpassing is vereist.", + "length-adjustment-range": "De lengte-aanpassing moet tussen 0 en 8 liggen.", + "byte-order": "Byte Volgorde van het lengteveld", + "initial-bytes-to-strip": "Aantal eerste bytes dat uit het gedecodeerde frame moet worden verwijderd", + "initial-bytes-to-strip-required": "Het aantal eerste bytes dat uit het gedecodeerde frame moet worden verwijderd, is vereist.", + "initial-bytes-to-strip-range": "Het aantal eerste bytes dat uit het gedecodeerde frame moet worden verwijderd, moet tussen 0 en 8 liggen.", + "so-backlog-option": "Max. aantal in behandeling zijnde verbindingen op het stopcontact", + "so-backlog-option-required": "Het maximale aantal in behandeling zijnde verbindingen op het stopcontact is vereist.", + "so-backlog-option-range": "Het maximale aantal in behandeling zijnde verbindingen op het stopcontact moet tussen 1 en 65535 liggen.", + "so-rcv-buf": "Grootte van de buffer voor inkomende socket (in KB)", + "so-rcv-buf-required": "De grootte van de buffer voor de inkomende socket (in KB) is vereist.", + "so-rcv-buf-range": "De grootte van de buffer voor inkomende socket (in KB) moet tussen 1 en 65535 liggen.", + "so-snd-buf": "Grootte van de buffer voor uitgaande socket (in KB)", + "so-snd-buf-required": "De grootte van de buffer voor uitgaande socket (in KB) is vereist.", + "so-snd-buf-range": "De grootte van de buffer voor uitgaande socket (in KB) moet tussen 1 en 65535 liggen.", + "charset-name": "Naam van de set", + "charset-name-required": "Charset-naam is vereist.", + "message-separator": "Scheidingsteken voor berichten", + "message-separator-required": "Berichtenscheidingsteken is vereist.", + "character-sequence": "Karakter Volgorde", + "character-sequence-required": "Tekenvolgorde is vereist.", + "max-frame-length": "Maximale framelengte (in bytes)", + "max-frame-length-required": "Maximale framelengte (in bytes) is vereist.", + "max-frame-length-range": "De maximale framelengte (in bytes) moet tussen 1 en 65535 liggen.", + "handler-type": "Handler Type", + "message-size": "Grootte van het bericht", + "message-size-required": "Berichtgrootte is vereist.", + "service-url": "URL van de dienst", + "service-url-required": "Service-URL is vereist.", + "subscription-name": "Naam van het abonnement", + "subscription-name-required": "De naam van het abonnement is vereist.", + "max-num-messages": "Max. aantal berichten", + "max-num-messages-required": "Het maximale aantal berichten is vereist.", + "max-num-bytes": "Max. aantal bytes", + "max-num-bytes-required": "Maximaal aantal bytes vereist.", + "timeout-in-ms": "Time-out in milliseconden", + "timeout-in-ms-required": "Time-out in milliseconden is vereist.", + "user-id": "Gebruikersnaam", + "user-id-required": "Gebruikersnaam is vereist.", + "token": "Teken", + "token-required": "Token is vereist.", + "project-id": "Project-ID", + "project-id-required": "Project-ID is vereist.", + "subscription-id": "Abonnements-ID", + "subscription-id-required": "Abonnements-ID is vereist.", + "service-account-key": "Sleutelbestand voor serviceaccount", + "service-account-key-required": "Het sleutelbestand van het serviceaccount is vereist.", + "tcp": { + "system-line-separator": "Systeem Lijnscheider", + "nul-delimiter": "Nul-scheidingsteken", + "byte-order-little-endian": "Kleine Endian", + "byte-order-big-endian": "Grote Endian" + }, + "cache-size": "Cache-grootte", + "cache-time-to-live": "Cachetijd om in minuten te leven", + "min-cache-size": "Cachegrootte kan niet lager zijn 0", + "min-cache-time-to-live": "Cache tijd om te leven kan niet lager zijn 0", + "max-cache-time-to-live": "Ongeldige tijd van cache live, kies tussen 0 en 525600", + "coap-security-mode": "Beveiligingsmodus", + "coap-security-mode-required": "CoAP-beveiligingsmodus is vereist", + "coap-security-mode-no-secure": "NIET VEILIG", + "coap-security-mode-dtls": "DTLS", + "coap-security-mode-mixed": "GEMENGD", + "coap-endpoint": "URL van CoAP-eindpunt", + "coap-endpoint-url-copied-message": "De URL van het CoAP-eindpunt is gekopieerd naar het klembord", + "copy-coap-endpoint-url": "Kopieer de URL van het CoAP-eindpunt", + "copy-coap-dtls-endpoint-url": "Kopieer de URL van het CoAP DTLS-eindpunt", + "coap-dtls-base-url": "DTLS-basis-URL", + "coap-dtls-base-url-required": "DTLS-basis-URL is vereist", + "coap-dtls-endpoint": "URL van CoAP DTLS-eindpunt", + "coap-dtls-endpoint-url-copied-message": "De URL van het CoAP DTLS-eindpunt is gekopieerd naar het klembord", + "unassign-integration-title": "Weet je zeker dat je de toewijzing van de integratie '{{integrationName}}' ongedaan wilt maken?", + "unassign-integration-from-edge-text": "Na de bevestiging wordt de toewijzing van de integratie ongedaan gemaakt en is deze niet toegankelijk voor de edge.", + "unassign-integrations-from-edge-title": "Weet u zeker dat u de toewijzing van { count, plural, =1 {1 integration} andere {# integrations} } wilt opheffen?", + "unassign-integrations-from-edge-text": "Na de bevestiging worden alle geselecteerde integraties ongedaan gemaakt en zijn ze niet toegankelijk voor de edge.", + "unassign-integrations": "Toewijzing van integraties ongedaan maken", + "edge-placeholder-hint": "U kunt tijdelijke aanduiding ${{ATTRIBUTE_KEY}} gebruiken om het integratieveld te vervangen door de kenmerkwaarde van een specifieke Edge-entiteit. 'Edge A' heeft bijvoorbeeld het kenmerk 'baseUrl' dat gelijk is aan 'http://localhost:9999'. U kunt $ {{baseUrl}} instellen als een van de integratievelden, en het wordt vervangen door 'http://localhost:9999' tijdens de toewijzing van deze integratie aan 'Edge A'. Bovendien, als 'Edge A' attribuut 'baseUrl' wordt bijgewerkt, wordt de integratie met de nieuwe bijgewerkte waarde automatisch ingericht voor 'Edge A'.", + "status": { + "status": "Status", + "active": "Actief", + "disabled": "Invalide", + "failed": "Mislukt", + "pending": "In behandeling" + }, + "daily-activity": "Dagelijkse activiteit", + "enable-debug-mode": "Foutopsporingsmodus inschakelen", + "disable-debug-mode": "Schakel de foutopsporingsmodus uit", + "advanced-settings": "Geavanceerde instellingen", + "basic-settings": "Basisinstellingen", + "existing-uplink-converter": "Selecteer een bestaande uplink-gegevensconverter", + "new-uplink-converter": "Maak een nieuwe uplink-gegevensconverter", + "existing-downlink-converter": "Selecteer een bestaande downlink-gegevensconverter", + "new-downlink-converter": "Maak een nieuwe downlink-gegevensconverter", + "connection": "Verbinding", + "connected": "Verbonden", + "not-connected": "Niet verbonden", + "environment": "Milieu", + "environment-required": "Omgeving is vereist.", + "region-cn": "China", + "region-us": "Verenigde Staten", + "region-eu": "Europa", + "region-in": "India", + "topic-name": "Naam van het onderwerp", + "topic-name-required": "Onderwerpnaam is verplicht", + "sub-name": "Naam van het abonnement", + "sub-name-required": "De naam van het abonnement is vereist", + "downlink-connection-string": "Downlink-verbindingsreeks", + "downlink-connection-string-required": "Downlink-verbindingsreeks is vereist", + "downlink-topic-name": "Naam van downlink-onderwerp", + "downlink-topic-name-required": "De naam van het downlink-onderwerp is vereist" + }, + "item": { + "selected": "Geselecteerd" + }, + "js-func": { + "no-return-error": "Functie moet waarde teruggeven.", + "return-type-mismatch": "Functie moet waarde van het type '{{type}}' teruggeven.", + "tidy": "Ordelijk", + "mini": "Mini" + }, + "key-val": { + "key": "Sleutel", + "value": "Waarde", + "remove-entry": "Invoer verwijderen", + "add-entry": "Invoer toevoegen", + "no-data": "Geen inzendingen" + }, + "layout": { + "layout": "Indeling", + "manage": "Lay-outs beheren", + "settings": "Lay-out instellingen", + "color": "Kleur", + "main": "Voornaamste", + "right": "Rechts", + "left": "Links", + "select": "Doellay-out selecteren", + "percentage-width": "Percentage breedte (%)", + "fixed-width": "Vaste breedte (px)", + "left-width": "Linkerkolom (%)", + "right-width": "Rechterkolom (%)", + "pick-fixed-side": "Vaste zijde:", + "layout-fixed-width": "Vaste breedte (px)", + "value-min-error": "Waarde moet meer zijn dan {{min}}{{unit}}", + "value-max-error": "De waarde moet lager zijn dan {{max}}{{unit}}", + "layout-fixed-width-required": "Vaste breedte is vereist", + "right-width-percentage-required": "Het juiste percentage is vereist", + "left-width-percentage-required": "Linker percentage is vereist", + "divider": "Scheidingslijn", + "right-side": "Indeling rechterkant", + "left-side": "Lay-out aan de linkerkant" + }, + "legend": { + "direction": "Richting van de legende", + "position": "Legende positie", + "sort-legend": "Datakeys sorteren in legende", + "show-max": "Maximale waarde weergeven", + "show-min": "Minimale waarde weergeven", + "show-avg": "Gemiddelde waarde weergeven", + "show-total": "Totale waarde weergeven", + "show-latest": "Toon laatste waarde", + "settings": "Legende-instellingen", + "min": "Min", + "max": ".max", + "avg": "Avg", + "total": "totaal", + "latest": "laatst", + "comparison-time-ago": { + "previousInterval": "(vorig interval)", + "customInterval": "(aangepast interval)", + "days": "(dag geleden)", + "weeks": "(week geleden)", + "months": "(maand geleden)", + "years": "(jaar geleden)" + } + }, + "login": { + "login": "Inloggen", + "request-password-reset": "Verzoek om wachtwoordreset", + "reset-password": "Wachtwoord opnieuw instellen", + "create-password": "Wachtwoord maken", + "two-factor-authentication": "Twee-factor-authenticatie", + "passwords-mismatch-error": "Ingevoerde wachtwoorden moeten hetzelfde zijn.", + "password-again": "Wachtwoord opnieuw ingeven", + "sign-in": "Gelieve in te loggen", + "username": "Gebruikersnaam (e-mail)", + "remember-me": "Onthoud mij", + "forgot-password": "Wachtwoord vergeten?", + "password-reset": "Wachtwoord opnieuw instellen", + "expired-password-reset-message": "Uw inloggegevens zijn verlopen. Gelieve een nieuwe wachtwoord aan te maken.", + "new-password": "Nieuw wachtwoord", + "new-password-again": "Bevestig nieuw wachtwoord", + "password-link-sent-message": "Reset-link is verzonden", + "email": "E-mail", + "no-account": "Heb je nog geen account?", + "create-account": "Maak een account aan", + "login-with": "Log in met {{name}}", + "or": "of", + "error": "Fout bij inloggen", + "verify-your-identity": "Verifieer uw identiteit", + "select-way-to-verify": "Selecteer een manier om te verifiëren", + "resend-code": "Code opnieuw verzenden", + "resend-code-wait": "Code opnieuw verzenden in { time, plural, =1 {1 second} andere {# seconds} }", + "try-another-way": "Probeer een andere manier", + "totp-auth-description": "Voer de beveiligingscode van je authenticator-app in.", + "totp-auth-placeholder": "Code", + "sms-auth-description": "Er is een beveiligingscode naar je telefoon gestuurd om {{contact}}.", + "sms-auth-placeholder": "SMS-code", + "email-auth-description": "Er is een beveiligingscode verzonden naar uw e-mailadres op {{contact}}.", + "email-auth-placeholder": "E-mail code", + "backup-code-auth-description": "Voer een van uw back-upcodes in.", + "backup-code-auth-placeholder": "Back-up code" + }, + "signup": { + "firstname": "Voornaam", + "lastname": "Achternaam", + "email": "E-mail", + "signup": "Inschrijven", + "create-password": "Maak een wachtwoord aan", + "repeat-password": "Herhaal je wachtwoord", + "have-account": "Heb je al een account?", + "signin": "Aanmelden", + "no-captcha-message": "U moet bevestigen dat u geen robot bent", + "password-length-message": "Uw wachtwoord moet minimaal 6 tekens lang zijn", + "email-verification": "E-mail verificatie", + "email-verification-message": "Er is een e-mail met verificatiegegevens verzonden naar het opgegeven e-mailadres.
Volg de instructies in de e-mail om uw aanmeldingsprocedure te voltooien.
Let op: als je de e-mail al een tijdje niet hebt gezien, controleer dan je map 'spam' of probeer de e-mail opnieuw te verzenden door op de knop 'Opnieuw verzenden' te klikken.", + "account-activation-title": "Account activeren", + "account-activated": "Account succesvol geactiveerd!", + "account-activated-text": "Gefeliciteerd!
Uw account is geactiveerd.", + "resend": "Verzenden", + "inactive-user-exists-title": "Inactieve gebruiker bestaat al", + "inactive-user-exists-text": "Er is al een geregistreerde gebruiker met een niet-geverifieerd e-mailadres.
Klik op de knop 'Opnieuw verzenden' als u de verificatie-e-mail opnieuw wilt verzenden.", + "activating-account": "Account activeren...", + "activating-account-text": "Uw account wordt momenteel geactiveerd. Een ogenblik geduld...", + "accept-privacy-policy": "Accepteer het privacybeleid", + "accept": "Accepteren", + "privacy-policy": "Privacybeleid", + "accept-privacy-policy-message": "U moet ons privacybeleid accepteren", + "recaptcha-title": "reCAPTCHA", + "terms-of-use": "Gebruiksvoorwaarden", + "accept-terms-of-use-message": "U moet onze Gebruiksvoorwaarden accepteren" + }, + "notification": { + "action-button": "Actieknop", + "action-type": "Soort actie", + "active": "Actief", + "add-notification-recipients-group": "Groep voor ontvangers van meldingen toevoegen", + "add-notification-template": "Meldingssjabloon toevoegen", + "add-recipient": "Ontvanger toevoegen", + "add-recipients": "Geadresseerden toevoegen", + "add-rule": "Regel toevoegen", + "add-stage": "Fase toevoegen", + "add-template": "Sjabloon toevoegen", + "after": "Na", + "alarm-assignment-trigger-settings": "Triggerinstellingen voor alarmtoewijzing", + "alarm-comment-trigger-settings": "Instellingen voor triggers voor alarmreacties", + "alarm-trigger-settings": "Instellingen alarmtrigger", + "all": "Alle", + "api-feature-hint": "Als het veld leeg is, wordt de trigger toegepast op alle api-functies", + "api-usage-trigger-settings": "Instellingen voor API-gebruikstrigger", + "new-platform-version-trigger-settings": "Triggerinstellingen voor nieuwe platformversie", + "at-least-one-should-be-selected": "Er moet er ten minste één worden geselecteerd", + "basic-settings": "Basisinstellingen", + "button-text": "Tekst van de knop", + "button-text-required": "Knoptekst is vereist", + "button-text-max-length": "De tekst van de knop moet kleiner zijn dan of gelijk zijn aan {{ length }} tekens", + "compose": "Samenstellen", + "conversation": "Gesprek", + "conversation-required": "Gesprek is vereist", + "copy-notification-template": "Meldingssjabloon kopiëren", + "copy-rule": "Regel kopiëren", + "copy-template": "Kopieer sjabloon", + "create-new": "Nieuwe maken", + "created": "Geschapen", + "delete-notification-text": "Opgelet, na de bevestiging wordt de melding onherstelbaar.", + "delete-notification-title": "Weet je zeker dat je de melding wilt verwijderen?", + "delete-notifications-text": "Opgelet, na de bevestiging worden meldingen onherstelbaar.", + "delete-notifications-title": "Weet u zeker dat u { count, plural, =1 {1 notification} andere {# notifications} } wilt verwijderen?", + "delete-recipient-text": "Let op, na de bevestiging wordt de ontvanger onherstelbaar.", + "delete-recipient-title": "Weet u zeker dat u de '{{recipientName}}' van de ontvanger wilt verwijderen?", + "delete-recipients-text": "Opgelet, na de bevestiging worden ontvangers onherstelbaar.", + "delete-recipients-title": "Weet u zeker dat u { count, plural, =1 {1 recipient} andere {# recipients} } wilt verwijderen?", + "delete-request-text": "Opgelet, na het bevestigingsverzoek wordt het onherstelbaar.", + "delete-request-title": "Weet je zeker dat je een verzoek wilt verwijderen?", + "delete-requests-text": "Opgelet, na de bevestiging worden verzoeken onherstelbaar.", + "delete-requests-title": "Weet u zeker dat u { count, plural, =1 {1 request} andere {# requests} } wilt verwijderen?", + "delete-rule-text": "Opgelet, nadat de bevestigingsregel onherstelbaar wordt.", + "delete-rule-title": "Weet u zeker dat u rule '{{ruleName}}' wilt verwijderen?", + "delete-rules-text": "Opgelet, na de bevestiging worden de regels onherstelbaar.", + "delete-rules-title": "Weet u zeker dat u { count, plural, =1 {1 rule} andere {# rules} } wilt verwijderen?", + "delete-template-text": "Opgelet, nadat de bevestigingssjabloon onherstelbaar wordt.", + "delete-template-title": "Weet u zeker dat u sjabloon '{{templateName}}' wilt verwijderen?", + "delete-templates-text": "Opgelet, nadat de bevestigingssjablonen onherstelbaar zijn geworden.", + "delete-templates-title": "Weet u zeker dat u { count, plural, =1 {1 template} andere {# templates} } wilt verwijderen?", + "deleted": "Verwijderd", + "delivery-method": { + "delivery-method": "Wijze van levering", + "email": "E-mail", + "email-preview": "Voorbeeld van e-mailmelding", + "slack": "Los", + "slack-preview": "Voorbeeld van Slack-melding", + "sms": "SMS", + "sms-preview": "Voorbeeld van sms-melding", + "web": "Web", + "web-preview": "Voorbeeld van webmelding" + }, + "delivery-method-not-configure-click": "De leveringsmethode is niet geconfigureerd. Klik om in te stellen.", + "delivery-method-not-configure-contact": "De leveringsmethode is niet geconfigureerd. Neem contact op met uw systeembeheerder.", + "delivery-methods": "Levering methodes", + "description": "Omschrijving: __________", + "device-activity-trigger-settings": "Triggerinstellingen voor actieve devices", + "device-list-rule-hint": "Als het veld leeg is, wordt de trigger toegepast op alle devices", + "device-profiles-list-rule-hint": "Als het veld leeg is, wordt de trigger toegepast op alle device profielen", + "disabled": "Uitgeschakeld", + "edit-notification-recipients-group": "Groep geadresseerden van meldingen bewerken", + "edit-notification-template": "Meldingssjabloon bewerken", + "edit-rule": "Regel bewerken", + "edit-template": "Sjabloon bewerken", + "enabled": "Ingeschakeld", + "entities-limit-trigger-settings": "Entiteiten beperken triggerinstellingen", + "entity-action-trigger-settings": "Instellingen voor triggers voor entiteitsacties", + "entity-type": "Type entiteit", + "escalation-chain": "Escalatie keten", + "failed-send": "Fouten verzenden", + "fails": "{ count, plural, =1 {1 fail} andere {# fails} }", + "filter": "Filter", + "first-recipient": "Eerste ontvanger", + "inactive": "Inactief", + "inbox": "Postvak IN", + "notification-inbox": "Meldingen / Postvak IN", + "input-field-support-templatization": "Ondersteuning voor invoervelden sjabloonisatie.", + "input-fields-support-templatization": "Invoervelden ondersteunen sjabloonisatie.", + "integration-action-trigger-settings": "Activeringsinstellingen voor integratiegebeurtenissen", + "integration-list-rule-hint": "Als het veld leeg is, wordt de trigger toegepast op alle integraties", + "link": "Verbinden", + "link-required": "Link is verplicht", + "link-type": { + "dashboard": "Dashboard openen", + "link": "URL-link openen" + }, + "management": "Beheer van meldingen", + "mark-all-as-read": "Alles markeren als gelezen", + "mark-as-read": "Markeren als gelezen", + "message": "Bericht", + "message-required": "Bericht is vereist", + "message-max-length": "Het bericht moet kleiner zijn dan of gelijk zijn aan {{ length }} tekens", + "name": "Naam", + "name-required": "Naam is verplicht", + "new-notification": "Nieuwe melding", + "no-inbox-notification": "Geen melding gevonden", + "no-notification-request": "Geen meldingsverzoek", + "no-notification-templates": "Geen meldingssjablonen gevonden", + "no-notifications-yet": "Nog geen meldingen", + "no-recipients-notification": "Geen melding van ontvangers", + "no-rule": "Geen rule geconfigureerd", + "no-rules-notification": "Geen melding van regels", + "no-severity-found": "Geen ernst gevonden", + "no-severity-matching": "'{{severity}}' niet gevonden.", + "no-template-matching": "Er zijn geen '{{template}}' gevonden die overeenkomen met de bron.", + "not-found-slack-recipient": "Slack-ontvanger niet gevonden", + "notification": "Bekendmaking", + "notification-center": "Meldingscentrum", + "notify": "aankondigen", + "notify-again": "Opnieuw melden", + "notify-alarm-action": { + "acknowledged": "Alarm bevestigd", + "assigned": "Alarm toegewezen", + "cleared": "Alarm gewist", + "created": "Alarm gemaakt", + "severity-changed": "Alarmernst gewijzigd", + "unassigned": "Alarm niet toegewezen" + }, + "notify-on": "Melden op", + "notify-on-comment-update": "Bericht over commentaarupdate", + "notify-on-required": "Melden op is vereist", + "notify-on-unassign": "Melden bij toewijzing ongedaan maken", + "notify-only-integrations-errors": "Alleen melden bij fout", + "notify-only-user-comments": "Alleen opmerkingen van gebruikers op de hoogte stellen", + "only-rule-chain-lifecycle-failures": "Alleen fouten in de levenscyclus van de rule chain", + "only-rule-node-lifecycle-failures": "Alleen fouten in de levenscyclus van rule nodes", + "platform-users": "Gebruikers van het platform", + "recipient": "Ontvanger", + "recipient-group": "Groep ontvangers", + "recipient-type": { + "affected-tenant-administrators": "Betrokken tenantbeheerders", + "affected-user": "Betrokken gebruiker", + "all-users": "Alle gebruikers", + "customer-users": "Klant-gebruikers", + "system-administrators": "Systeembeheerders", + "tenant-administrators": "Tenant beheerders", + "user-filters": "Gebruiker filter", + "user-group-list": "Lijst met gebruikersgroepen", + "user-list": "Lijst met gebruikers", + "user-role": "Rol van de gebruiker", + "users-entity-owner": "Gebruikers van de entiteitseigenaar" + }, + "recipients": "Ontvangers", + "notification-recipients": "Meldingen / Ontvangers", + "recipients-count": "{ count, plural, =1 {1 recipient} andere {# recipients} }", + "recipients-required": "Ontvangers zijn verplicht", + "refresh-allow-delivery-method": "Vernieuwen Bezorgmethode toestaan", + "request-search": "Zoekopdracht aanvragen", + "request-status": { + "processing": "Verwerking", + "scheduled": "Gepland", + "sent": "Verzonden" + }, + "review": "Recensie", + "rule": "Regel", + "rule-chain-list-rule-hint": "Als het veld leeg is, wordt de trigger toegepast op alle rule chains", + "rule-engine-events-trigger-settings": "Events in de rule engine activeren instellingen", + "rule-engine-filter": "Rule engine filter", + "rule-name": "Naam van rule", + "rule-name-required": "Naam is verplicht", + "rule-node-filter": "Filter voor rule nodes", + "rules": "Reglement", + "notification-rules": "Meldingen / Regels", + "scheduler-later": "Plannen voor later", + "search-notification": "Meldingen zoeken", + "search-recipients": "Ontvangers zoeken", + "search-rules": "Zoekregels", + "search-templates": "Sjablonen zoeken", + "see-documentation": "Zie documentatie", + "selected-notifications": "{ count, plural, =1 {1 notification} andere {# notifications} } geselecteerd", + "selected-recipients": "{ count, plural, =1 {1 recipient} andere {# recipients} } geselecteerd", + "selected-requests": "{ count, plural, =1 {1 request} andere {# requests} } geselecteerd", + "selected-rules": "{ count, plural, =1 {1 rule} andere {# rules} } geselecteerd", + "selected-template": "{ count, plural, =1 {1 template} andere {# templates} } geselecteerd", + "send-notification": "Notificatie versturen", + "sent": "Verzonden", + "notification-sent": "Notificaties / Verzonden", + "set-entity-from-notification": "Entiteit instellen van melding naar dashboardstatus", + "slack-chanel-type": "Type Slack-kanaal", + "slack-chanel-types": { + "direct": "Direct bericht", + "private-channel": "Privé-kanaal", + "public-channel": "Publieke zender" + }, + "start-from-scratch": "Begin vanaf nul", + "status": "Status", + "stop-escalation-alarm-status-become": "Stop de escalatie van de alarmstatus wordt:", + "subject": "Onderwerp", + "subject-required": "Onderwerp is verplicht", + "template": "Sjabloon", + "template-name": "Naam van sjabloon", + "template-required": "Sjabloon is vereist", + "template-type": { + "alarm": "Alarm", + "alarm-assignment": "Alarm toewijzing", + "alarm-comment": "Alarm commentaar", + "api-usage-limit": "Limiet voor API-gebruik", + "device-activity": "Activiteit van het device", + "entities-limit": "Limieten voor entiteiten", + "entity-action": "Actie van entiteit", + "general": "Algemeen", + "integration-lifecycle-event": "Levens cyclus event van integratie", + "rule-engine-lifecycle-event": "Levens cyclus event van rule engine", + "rule-node": "Regel knooppunt", + "new-platform-version": "Nieuwe platformversie" + }, + "templates": "Sjablonen", + "notification-templates": "Meldingen / Sjablonen", + "tenant-profiles-list-rule-hint": "Als het veld leeg is, wordt de trigger toegepast op alle tenantprofielen", + "tenants-list-rule-hint": "Als het veld leeg is, wordt de trigger toegepast op alle tenants", + "threshold": "Drempel", + "time": "Tijd", + "track-rule-node-events": "Regelknooppuntgebeurtenissen bijhouden", + "trigger": { + "alarm": "Alarm", + "alarm-assignment": "Alarm toewijzing", + "alarm-comment": "Alarm commentaar", + "api-usage-limit": "Limiet voor API-gebruik", + "device-activity": "Activiteit van het device", + "entities-limit": "Limieten voor entiteiten", + "entity-action": "Actie van entiteit", + "integration-lifecycle-event": "Levens cyclus event van integratie", + "rule-engine-lifecycle-event": "Levens cyclus event van rule engine", + "new-platform-version": "Nieuwe platformversie", + "trigger": "Trigger", + "trigger-required": "Trigger is vereist" + }, + "type": "Type", + "unread": "Ongelezen", + "updated": "Bijgewerkt", + "use-template": "Sjabloon gebruiken", + "view-all": "Alles weergeven", + "view-notification-recipients-group": "Groep geadresseerden van meldingen weergeven", + "view-notification-template": "Meldingssjabloon weergeven", + "view-rule": "Bekijk rule", + "warning": "Waarschuwing" + }, + "ota-update": { + "add": "Pakket toevoegen", + "assign-firmware": "Toegewezen firmware", + "assign-firmware-required": "Toegewezen firmware is vereist", + "assign-software": "Toegewezen software", + "assign-software-required": "Toegewezen software is vereist", + "auto-generate-checksum": "Automatisch controlesom genereren", + "cant-applied-group-all": "Kan niet worden aangevraagd voor groep Alle", + "checksum": "Checksum", + "checksum-hint": "Als de checksum leeg is, wordt deze automatisch gegenereerd", + "checksum-algorithm": "Checksum-algoritme", + "checksum-copied-message": "De controlesom van het pakket is gekopieerd naar het klembord", + "change-firmware": "Wijziging van de firmware kan leiden tot een update van { count, plural, =1 {1 device} andere {# devices} }.", + "change-software": "Wijziging van de software kan leiden tot een update van { count, plural, =1 {1 device} andere {# devices} }.", + "chose-compatible-device-profile": "Het geüploade pakket is alleen beschikbaar voor devices met het gekozen profiel.", + "chose-firmware-distributed-device": "Kies firmware die naar de devices wordt gedistribueerd", + "chose-software-distributed-device": "Kies software die naar de devices wordt gedistribueerd", + "content-type": "Inhoudstype", + "copy-checksum": "Controlesom kopiëren", + "copy-direct-url": "Directe URL kopiëren", + "copyId": "Kopieer pakket-ID", + "copied": "Gekopieerd!", + "delete": "Pakket verwijderen", + "delete-ota-update-text": "Opgelet, na de bevestiging wordt de OTA-update onherstelbaar.", + "delete-ota-update-title": "Weet je zeker dat je de OTA-update '{{title}}' wilt verwijderen?", + "delete-ota-updates-text": "Opgelet, na de bevestiging worden alle geselecteerde OTA-updates verwijderd.", + "delete-ota-updates-title": "Weet u zeker dat u { count, plural, =1 {1 OTA update} andere {# OTA updates} } wilt verwijderen?", + "description": "Omschrijving: __________", + "direct-url": "Directe URL", + "direct-url-copied-message": "De directe URL van het pakket is gekopieerd naar het klembord", + "direct-url-required": "Directe URL is vereist", + "download": "Pakket downloaden", + "drop-file": "Zet een pakketbestand neer of klik om een bestand te selecteren om te uploaden.", + "drop-package-file-or": "Sleep een pakketbestand of", + "file-name": "Bestandsnaam", + "file-size": "Bestandsgrootte", + "file-size-bytes": "Bestandsgrootte in bytes", + "idCopiedMessage": "Pakket-ID is gekopieerd naar klembord", + "no-firmware-matching": "Er zijn geen compatibele OTA-updatepakketten gevonden die overeenkomen met '{{entity}}'.", + "no-firmware-text": "Er zijn geen compatibele OTA-updatepakketten voor firmware ingericht.", + "no-packages-text": "Geen pakketten gevonden", + "no-software-matching": "Er zijn geen compatibele Software OTA Update-pakketten gevonden die overeenkomen met '{{entity}}'.", + "no-software-text": "Er zijn geen compatibele OTA-updatepakketten voor software geleverd.", + "ota-update": "OTA-update", + "ota-update-details": "Details van OTA-update", + "ota-updates": "OTA-updates", + "package-type": "Soort verpakking", + "packages-repository": "Pakketten repository", + "search": "Pakketten zoeken", + "selected-package": "{ count, plural, =1 {1 package} andere {# packages} } geselecteerd", + "title": "Titel", + "title-required": "Titel is vereist.", + "title-max-length": "Titel moet kleiner zijn dan 256 tekens", + "types": { + "firmware": "Firmware", + "software": "Software" + }, + "upload-binary-file": "Binair bestand uploaden", + "use-external-url": "Externe URL gebruiken", + "version": "Versie", + "version-required": "Versie is vereist.", + "version-tag": "Versie Tag", + "version-tag-hint": "De aangepaste tag moet overeenkomen met de pakketversie die door uw device wordt gerapporteerd.", + "version-max-length": "Versie moet kleiner zijn dan 256 tekens", + "warning-after-save-no-edit": "Zodra het pakket is geüpload, kunt u de titel, versie, device profiel en pakkettype niet meer wijzigen." + }, + "position": { + "top": "Boven", + "bottom": "Bodem", + "left": "Links", + "right": "Rechts" + }, + "profile": { + "profile": "Profiel", + "last-login-time": "Laatste keer inloggen", + "change-password": "Wachtwoord wijzigen", + "current-password": "Huidig wachtwoord", + "copy-jwt-token": "JWT-token kopiëren", + "jwt-token": "JWT-token", + "token-valid-till": "Token is geldig tot", + "tokenCopiedSuccessMessage": "JWT-token is gekopieerd naar klembord", + "tokenCopiedWarnMessage": "JWT-token is verlopen! Vernieuw de pagina." + }, + "profiles": { + "profiles": "Profielen" + }, + "security": { + "security": "Veiligheid", + "general-settings": "Algemene beveiligingsinstellingen", + "2fa": { + "2fa": "Twee-factor-authenticatie", + "2fa-description": "Tweefactorauthenticatie beschermt uw account tegen ongeoorloofde toegang. Het enige wat je hoeft te doen is een beveiligingscode in te voeren wanneer je inlogt.", + "authenticate-with": "U kunt zich authenticeren met:", + "disable-2fa-provider-text": "Als u {{name}} uitschakelt, wordt uw account minder veilig", + "disable-2fa-provider-title": "Weet u zeker dat u {{name}} wilt uitschakelen?", + "get-new-code": "Nieuwe code ophalen", + "main-2fa-method": "Gebruik als belangrijkste tweefactorauthenticatiemethode", + "dialog": { + "activation-step-description-email": "De volgende keer dat u inlogt, wordt u gevraagd de beveiligingscode in te voeren die naar uw e-mailadres wordt verzonden.", + "activation-step-description-sms": "De volgende keer dat u inlogt, wordt u gevraagd de beveiligingscode in te voeren die naar het telefoonnummer wordt verzonden.", + "activation-step-description-totp": "De volgende keer dat u inlogt, moet u een tweefactorauthenticatiecode opgeven.", + "activation-step-label": "Activering", + "backup-code-description": "Print de codes uit, zodat je ze bij de hand hebt wanneer je ze moet gebruiken om in te loggen op je account. U kunt elke back-upcode één keer gebruiken.", + "backup-code-warn": "Zodra u deze pagina verlaat, kunnen deze codes niet meer worden weergegeven. Bewaar ze veilig met behulp van de onderstaande opties.", + "download-txt": "Downloaden (txt)", + "email-step-description": "Voer een e-mailadres in dat u als authenticator wilt gebruiken.", + "email-step-label": "E-mail", + "enable-email-title": "E-mailauthenticator inschakelen", + "enable-sms-title": "SMS-authenticator inschakelen", + "enable-totp-title": "Authenticator-app inschakelen", + "enter-verification-code": "Vul hier de 6-cijferige code in", + "get-backup-code-title": "Back-upcode ophalen", + "next": "Volgend", + "scan-qr-code": "Scan deze QR-code met je verificatie-app", + "send-code": "Verstuur code", + "sms-step-description": "Voer een telefoonnummer in dat u als authenticator wilt gebruiken.", + "sms-step-label": "Telefoonnummer", + "success": "Succes!", + "totp-step-description-install": "U kunt apps zoals Google Authenticator, Authy of Duo installeren.", + "totp-step-description-open": "Open de authenticator-app op je mobiele telefoon.", + "totp-step-label": "Download de app", + "verification-code": "6-cijferige code", + "verification-code-invalid": "Ongeldige indeling van de verificatiecode", + "verification-code-incorrect": "Verificatiecode is onjuist", + "verification-code-many-request": "Te veel verzoeken om verificatiecode te controleren", + "verification-step-description": "Voer een 6-cijferige code in die we zojuist naar {{address}} hebben gestuurd", + "verification-step-label": "Verificatie" + }, + "provider": { + "email": "E-mail", + "email-description": "Gebruik een beveiligingscode die naar uw e-mailadres is verzonden om u te verifiëren.", + "email-hint": "Authenticatiecodes worden per e-mail verzonden naar {{ info }}", + "sms": "SMS", + "sms-description": "Gebruik je telefoon om je te authenticeren. We sturen je een beveiligingscode via sms wanneer je inlogt.", + "sms-hint": "Authenticatiecodes worden per sms verzonden naar {{ info }}", + "totp": "Authenticator-app", + "totp-description": "Gebruik apps zoals Google Authenticator, Authy of Duo op je telefoon om te verifiëren. Het genereert een beveiligingscode om in te loggen.", + "totp-hint": "De Authenticator-app is ingesteld voor uw account", + "backup_code": "Back-up code", + "backup-code-description": "Met deze afdrukbare eenmalige toegangscodes kunt u zich aanmelden wanneer u uw telefoon niet bij de hand hebt, bijvoorbeeld wanneer u op reis bent.", + "backup-code-hint": "{{ info }} codes voor eenmalig gebruik zijn op dit moment actief" + } + }, + "password-requirement": { + "at-least": "Minstens:", + "character": "{ count, plural, =1 {1 character} andere {# characters} }", + "digit": "{ count, plural, =1 {1 digit} andere {# digits} }", + "incorrect-password-try-again": "Onjuist wachtwoord. Probeer het opnieuw", + "lowercase-letter": "{ count, plural, =1 {1 lowercase letter} andere {# lowercase letters} }", + "new-passwords-not-match": "Nieuw wachtwoord komt niet overeen", + "password-should-not-contain-spaces": "Uw wachtwoord mag geen spaties bevatten", + "password-not-meet-requirements": "Wachtwoord voldeed niet aan de vereisten", + "password-requirements": "Vereisten voor wachtwoorden", + "password-should-difference": "Het nieuwe wachtwoord moet anders zijn dan het huidige", + "special-character": "{ count, plural, =1 {1 special character} andere {# special characters} }", + "uppercase-letter": "{ count, plural, =1 {1 uppercase letter} andere {# uppercase letters} }" + } + }, + "relation": { + "relations": "Betrekkingen", + "direction": "Richting", + "search-direction": { + "FROM": "Van", + "TO": "Aan" + }, + "direction-type": { + "FROM": "Van", + "TO": "Aan" + }, + "from-relations": "Uitgaande relaties", + "to-relations": "Inkomende relaties", + "selected-relations": "{ count, plural, =1 {1 relation} andere {# relations} } geselecteerd", + "type": "Type", + "to-entity-type": "Naar entiteitstype", + "to-entity-name": "Naar entiteitsnaam", + "from-entity-type": "Van entiteitstype", + "from-entity-name": "Van entiteitsnaam", + "to-entity": "Naar entiteit", + "from-entity": "Van entiteit", + "delete": "Relatie verwijderen", + "relation-type": "Relatie type", + "relation-type-required": "Relatietype is vereist.", + "relation-type-max-length": "Relatietype moet kleiner zijn dan 256 tekens", + "any-relation-type": "Elk type", + "add": "Relatie toevoegen", + "edit": "Relatie bewerken", + "view": "Bekijk relatie", + "delete-to-relation-title": "Weet u zeker dat u de relatie met de entiteit '{{entityName}}' wilt verwijderen?", + "delete-to-relation-text": "Let op, na de bevestiging is de entiteit '{{entityName}}' niet meer gerelateerd aan de huidige entiteit.", + "delete-to-relations-title": "Weet u zeker dat u { count, plural, =1 {1 relation} andere {# relations} } wilt verwijderen?", + "delete-to-relations-text": "Opgelet, na de bevestiging worden alle geselecteerde relaties verwijderd en worden de bijbehorende entiteiten losgekoppeld van de huidige entiteit.", + "delete-from-relation-title": "Weet u zeker dat u de relatie uit de entiteit '{{entityName}}' wilt verwijderen?", + "delete-from-relation-text": "Opgelet, na de bevestiging zal de huidige entiteit niet meer gerelateerd zijn aan de entiteit '{{entityName}}'.", + "delete-from-relations-title": "Weet u zeker dat u { count, plural, =1 {1 relation} andere {# relations} } wilt verwijderen?", + "delete-from-relations-text": "Opgelet, na de bevestiging worden alle geselecteerde relaties verwijderd en wordt de huidige entiteit losgekoppeld van de corresponderende entiteiten.", + "remove-relation-filter": "Relatiefilter verwijderen", + "add-relation-filter": "Relatiefilter toevoegen", + "any-relation": "Elke relatie", + "relation-filters": "Relatie filters", + "additional-info": "Aanvullende informatie (JSON)", + "invalid-additional-info": "Kan aanvullende informatie json niet parseren.", + "no-relations-text": "Geen relaties gevonden" + }, + "resource": { + "add": "Bron toevoegen", + "copyId": "Resource-ID kopiëren", + "delete": "Bron verwijderen", + "delete-resource-text": "Opgelet, na de bevestiging wordt de bron onherstelbaar.", + "delete-resource-title": "Weet u zeker dat u de bron '{{resourceTitle}}' wilt verwijderen?", + "delete-resources-action-title": "Verwijder { count, plural, =1 {1 resource} andere {# resources} }", + "delete-resources-text": "Houd er rekening mee dat de geselecteerde bronnen, zelfs als ze in device profielen worden gebruikt, worden verwijderd.", + "delete-resources-title": "Weet u zeker dat u { count, plural, =1 {1 resource} andere {# resources} } wilt verwijderen?", + "download": "Bron downloaden", + "drop-file": "Zet een bronbestand neer of klik om een bestand te selecteren om te uploaden.", + "drop-resource-file-or": "Sleep een bronbestand of", + "empty": "Bron is leeg", + "file-name": "Bestandsnaam", + "idCopiedMessage": "Bron-id is gekopieerd naar klembord", + "no-resource-matching": "Er zijn geen '{{widgetsBundle}}' gevonden die overeenkomen met de bron.", + "no-resource-text": "Geen bronnen gevonden", + "open-widgets-bundle": "Widgets-bundel openen", + "resource": "Hulpbron", + "resource-library-details": "Details van de bron", + "resource-type": "Type bron", + "resources-library": "Bibliotheek met bronnen", + "search": "Bronnen zoeken", + "selected-resources": "{ count, plural, =1 {1 resource} andere {# resources} } geselecteerd", + "system": "Systeem", + "title": "Titel", + "title-required": "Titel is vereist.", + "title-max-length": "Titel moet kleiner zijn dan 256 tekens" + }, + "rulechain": { + "rulechain": "Rule chains", + "rulechain-events": "Events in de rule chain", + "rulechains": "Rule chains", + "root": "Bron", + "delete": "Rule chain verwijderen", + "name": "Naam", + "name-required": "Naam is verplicht.", + "name-max-length": "Naam moet kleiner zijn dan 256 tekens", + "description": "Omschrijving: __________", + "add": "Rule chain toevoegen", + "set-root": "Bron rule chain aanmaken", + "set-root-rulechain-title": "Weet u zeker dat u de rule chain '{{ruleChainName}}' als bron wilt maken?", + "set-root-rulechain-text": "Na de bevestiging wordt de rule chain de bron en worden alle inkomende transportberichten afgehandeld.", + "delete-rulechain-title": "Weet u zeker dat u de rule chain '{{ruleChainName}}' wilt verwijderen?", + "delete-rulechain-text": "Opgelet, na de bevestiging worden de rule chain en alle gerelateerde gegevens onherstelbaar.", + "delete-rulechains-title": "Weet u zeker dat u { count, plural, =1 {1 rule chain} andere {# rule chains} } wilt verwijderen?", + "delete-rulechains-action-title": "Verwijder { count, plural, =1 {1 rule chain} andere {# rule chains} }", + "delete-rulechains-text": "Opgelet, na de bevestiging worden alle geselecteerde rule chains verwijderd en worden alle gerelateerde gegevens onherstelbaar.", + "add-rulechain-text": "Nieuwe rule chain toevoegen", + "no-rulechains-text": "Geen rule chains gevonden", + "rulechain-details": "Details van de rule chain", + "details": "Details", + "events": "Events", + "system": "Systeem", + "import": "Rule chain importeren", + "export": "Rule chain exporteren", + "export-failed-error": "Rule chain kan niet worden geëxporteerd: {{error}}", + "create-new-rulechain": "Nieuwe rule chain maken", + "rulechain-file": "Rule chain bestand", + "invalid-rulechain-file-error": "Rule chain kan niet worden geïmporteerd: Ongeldige gegevensstructuur van de rule chain.", + "copyId": "Rule chain-id kopiëren", + "idCopiedMessage": "Rule chain-id is gekopieerd naar klembord", + "select-rulechain": "Rule chain selecteren", + "no-rulechains-matching": "Er zijn geen rule chains gevonden die overeenkomen met '{{entity}}'.", + "rulechain-required": "Rule chain is vereist", + "management": "Beheer van rule chains", + "debug-mode": "Foutopsporingsmodus", + "search": "Rule chains zoeken", + "selected-rulechains": "{ count, plural, =1 {1 rule chain} andere {# rule chains} } geselecteerd", + "open-rulechain": "Rule chain openen", + "edge-template-root": "Sjabloon Wortel", + "assign-to-edge": "Toewijzen aan Edge", + "edge-rulechain": "Edge rule chain", + "unassign-rulechain-from-edge-text": "Na de bevestiging wordt de toewijzing van de rule chain ongedaan gemaakt en is deze niet toegankelijk voor de edge.", + "unassign-rulechains-from-edge-title": "Weet u zeker dat u de toewijzing van { count, plural, =1 {1 rulechain} andere {# rulechains} } wilt opheffen?", + "unassign-rulechains-from-edge-text": "Na de bevestiging worden alle geselecteerde rule chains ongedaan gemaakt en zijn ze niet toegankelijk via de edge.", + "assign-rulechain-to-edge-title": "Regel Chain(s) toewijzen aan Edge", + "assign-rulechain-to-edge-text": "Selecteer de rule chains die u aan de edge wilt toewijzen", + "set-edge-template-root-rulechain": "Rule chain maken als hoofdmap van de randsjabloon", + "set-edge-tscheduler-eventemplate-root-rulechain": "Rule chain maken als hoofdmap van de randsjabloon", + "set-edge-template-root-rulechain-title": "Weet u zeker dat u de rule chain '{{ruleChainName}}' randsjabloon root wilt maken?", + "set-edge-template-root-rulechain-text": "Na de bevestiging wordt de rule chain de hoofdstructuur van de randsjabloon en wordt deze de hoofdregelketen voor een nieuw aangemaakte edges.", + "invalid-rulechain-type-error": "Kan rule chain niet importeren: Ongeldig type rule chain. Verwacht type is {{expectedRuleChainType}}.", + "set-auto-assign-to-edge": "Rule chain toewijzen aan edge(en) bij het maken", + "set-auto-assign-to-edge-title": "Weet u zeker dat u de rule chain '{{ruleChainName}}' wilt toewijzen aan edge(en) bij het maken?", + "set-auto-assign-to-edge-text": "Na de bevestiging wordt de rule chain automatisch toegewezen aan edge(en) bij het aanmaken.", + "unset-auto-assign-to-edge": "Wijs geen rule chain toe aan edge(en) bij het maken", + "unset-auto-assign-to-edge-title": "Weet u zeker dat u de rule chain '{{ruleChainName}}' niet wilt toewijzen aan edge(en) bij het maken?", + "unset-auto-assign-to-edge-text": "Na de bevestiging wordt de rule chain niet langer automatisch toegewezen aan edge(en) bij het aanmaken.", + "unassign-rulechain-title": "Weet u zeker dat u de toewijzing van de rule chain '{{ruleChainName}}' wilt opheffen?", + "unassign-rulechains": "Toewijzing van rule chains ongedaan maken" + }, + "rulenode": { + "rule-node-events": "Events met rule nodes", + "details": "Details", + "events": "Events", + "search": "Rule nodes zoeken", + "open-node-library": "Knooppuntbibliotheek openen", + "add": "Rule node toevoegen", + "name": "Naam", + "name-required": "Naam is verplicht.", + "name-max-length": "Naam moet kleiner zijn dan 256 tekens", + "type": "Type", + "description": "Omschrijving: __________", + "delete": "Rule node verwijderen", + "select-all-objects": "Selecteer alle rule nodes en verbindingen", + "deselect-all-objects": "Deselecteer alle rule nodes en verbindingen", + "delete-selected-objects": "Geselecteerde rule nodes en verbindingen verwijderen", + "delete-selected": "Selectie verwijderen", + "create-nested-rulechain": "Geneste rule chain maken", + "select-all": "Alles selecteren", + "copy-selected": "Geselecteerde tekst kopiëren", + "deselect-all": "Alles deselecteren", + "rulenode-details": "Details van rule nodes", + "debug-mode": "Foutopsporingsmodus", + "singleton-mode": "Singleton-modus", + "configuration": "Configuratie", + "link": "Verbinden", + "link-details": "Details van rule nodes links", + "add-link": "Link toevoegen", + "link-label": "Label koppelen", + "link-label-required": "Linklabel is vereist.", + "custom-link-label": "Aangepast linklabel", + "custom-link-label-required": "Aangepast linklabel is vereist.", + "link-labels": "Labels koppelen", + "link-labels-required": "Labels koppelen is vereist.", + "no-link-labels-found": "Geen linklabels gevonden", + "no-link-label-matching": "'{{label}}' niet gevonden.", + "create-new-link-label": "Maak een nieuwe aan!", + "type-filter": "Filter", + "type-filter-details": "Inkomende berichten filteren met geconfigureerde voorwaarden", + "type-enrichment": "Verrijking", + "type-enrichment-details": "Aanvullende informatie toevoegen aan de metagegevens van berichten", + "type-transformation": "Transformatie", + "type-transformation-details": "De payload en metagegevens van berichten wijzigen", + "type-action": "Actie", + "type-action-details": "Speciale actie uitvoeren", + "type-analytics": "Analytics", + "type-analytics-details": "Analyse uitvoeren van gestreamde of persistente gegevens", + "type-external": "Extern", + "type-external-details": "Werkt samen met extern systeem", + "type-rule-chain": "Rule chain", + "type-rule-chain-details": "Inkomende berichten worden doorgestuurd naar een opgegeven rule chain", + "type-flow": "Vloeien", + "type-flow-details": "Organiseert de berichtenstroom", + "type-input": "Invoer", + "type-input-details": "Logische invoer van rule chain, stuurt inkomende berichten door naar het volgende gerelateerde rule node", + "type-unknown": "Onbekend", + "type-unknown-details": "Onopgelost rule node", + "directive-is-not-loaded": "Gedefinieerde configuratierichtlijn '{{directiveName}}' is niet beschikbaar.", + "ui-resources-load-error": "Het laden van de bronnen van de configuratie-gebruikersinterface is mislukt.", + "invalid-target-rulechain": "Kan doelregelketen niet oplossen!", + "test-script-function": "Test script functie", + "script-lang-java-script": "Java-schrift", + "script-lang-tbel": "TBEL", + "message": "Bericht", + "message-type": "Soort bericht", + "select-message-type": "Selecteer berichttype", + "message-type-required": "Berichttype is vereist", + "metadata": "Metagegevens", + "metadata-required": "Metagegevensvermeldingen mogen niet leeg zijn.", + "output": "Uitvoer", + "test": "Test", + "help": "Help", + "reset-debug-mode": "Foutopsporingsmodus resetten in alle rule nodes" + }, + "role": { + "role": "Rol", + "roles": "Rollen", + "management": "Rollen beheer", + "view-roles": "Rollen weergeven", + "no-roles-matching": "Er zijn geen rollen gevonden die overeenkomen met '{{entity}}'.", + "role-list": "Lijst met rollen", + "role-list-required": "Rollenlijst is verplicht", + "add": "Rol toevoegen", + "view": "Bekijk rol", + "search": "Rollen zoeken", + "selected-roles": "{ count, plural, =1 {1 role} andere {# roles} } geselecteerd", + "no-roles-text": "Geen rollen gevonden", + "role-details": "Details van de rol", + "add-role-text": "Nieuwe rol toevoegen", + "delete": "Rol verwijderen", + "delete-roles": "Rollen verwijderen", + "delete-role-title": "Weet je zeker dat je de rol '{{roleName}}' wilt verwijderen?", + "delete-role-text": "Opgelet, na de bevestiging worden de rol en alle gerelateerde gegevens onherstelbaar.", + "delete-roles-title": "Weet u zeker dat u { count, plural, =1 {1 role} andere {# roles} } wilt verwijderen?", + "delete-roles-action-title": "Verwijder { count, plural, =1 {1 role} andere {# roles} }", + "delete-roles-text": "Opgelet, na de bevestiging worden alle geselecteerde rollen verwijderd en worden alle gerelateerde gegevens onherstelbaar.", + "role-type": "Soort rol", + "role-type-required": "Roltype is vereist.", + "select-role-type": "Roltype selecteren", + "enter-role-type": "Roltype invoeren", + "any-role": "Elke rol", + "no-role-types-matching": "Er zijn geen roltypen gevonden die overeenkomen met '{{entitySubtype}}'.", + "role-type-list-empty": "Geen roltypen geselecteerd.", + "role-types": "Soorten rollen", + "created-time": "Gecreëerde tijd", + "name": "Naam", + "name-required": "Naam is verplicht.", + "name-max-length": "Naam moet kleiner zijn dan 256 tekens", + "description": "Omschrijving: __________", + "events": "Events", + "details": "Details", + "copyId": "Rol-ID kopiëren", + "idCopiedMessage": "Rol-ID is gekopieerd naar het klembord", + "permissions": "Machtigingen", + "role-required": "Vereiste rol", + "roles-required": "Vereiste rollen", + "display-type": { + "GENERIC": "Generiek", + "GROUP": "Groep" + } + }, + "group-permission": { + "user-group-roles": "Rollen voor gebruikersgroepen", + "entity-group-permissions": "Machtigingen voor entiteitsgroepen", + "role-type": "Soort rol", + "role-name": "Naam van de rol", + "group-type": "Soort groep", + "group-name": "Naam van de groep", + "group-owner": "Eigenaar van de groep", + "user-group-name": "Naam van de gebruikersgroep", + "user-group-owner": "Eigenaar van gebruikersgroep", + "edit": "Machtigingen bewerken", + "delete": "Machtigingen verwijderen", + "selected-group-permissions": "{ count, plural, =1 {1 group permission} andere {# group permissions} } geselecteerd", + "delete-group-permission-title": "Weet je zeker dat je de groepsrechten '{{roleName}}' wilt verwijderen?", + "delete-group-permission-text": "Opgelet, na de bevestiging worden de groepstoestemming en alle gerelateerde gegevens onherstelbaar.", + "delete-group-permission": "Groepsmachtiging verwijderen", + "delete-group-permissions-title": "Weet u zeker dat u { count, plural, =1 {1 group permission} andere {# group permission} } wilt verwijderen?", + "delete-group-permissions-text": "Opgelet, na de bevestiging worden alle geselecteerde groepsmachtigingen verwijderd en verliezen de bijbehorende gebruikers de toegang tot gespecificeerde bronnen.", + "delete-group-permissions": "Groepsmachtigingen verwijderen", + "add-group-permission": "Groepsmachtiging toevoegen", + "edit-group-permission": "Groepsmachtiging bewerken", + "entity-group": "Entiteitsgroep", + "user-group": "Gebruikersgroep", + "no-owners-matching": "Er zijn geen eigenaren gevonden die overeenkomen met '{{owner}}'.", + "target-owner-required": "Eigenaar van entiteitsgroep is vereist.", + "target-user-group-owner-required": "Eigenaar van gebruikersgroep is vereist.", + "no-group-permissions-text": "Geen groepsmachtigingen gevonden" + }, + "permission": { + "permissions-required": "Er moet ten minste één machtigingsvermelding worden opgegeven.", + "remove-permission": "Machtigingsinvoer verwijderen", + "add-permission": "Machtigingsinvoer toevoegen", + "other": "Anders __________", + "resource": { + "resource": "Hulpbron", + "select-resource": "Bron selecteren", + "resource-required": "Resource is vereist", + "no-resources-matching": "Er zijn geen bronnen gevonden die overeenkomen met '{{resource}}'.", + "display-type": { + "ALL": "Alle", + "PROFILE": "Profiel", + "ADMIN_SETTINGS": "Admin-instellingen", + "ALARM": "Alarm", + "DEVICE": "Device", + "DEVICE_PROFILE": "Device profiel", + "ASSET": "Asset", + "CUSTOMER": "Klant", + "DASHBOARD": "Dashboard", + "ENTITY_VIEW": "Entiteit Weergave", + "EDGE": "Edge", + "TENANT": "Tenant", + "TENANT_PROFILE": "Profiel van de tenant", + "RULE_CHAIN": "Rule chain", + "USER": "Gebruiker", + "WIDGETS_BUNDLE": "Widgets Bundel", + "WIDGET_TYPE": "Widget Type", + "CONVERTER": "Conversieprogramma", + "INTEGRATION": "Integratie", + "SCHEDULER_EVENT": "Scheduler Evenement", + "BLOB_ENTITY": "Blob-entiteit", + "CUSTOMER_GROUP": "Klantengroep", + "DEVICE_GROUP": "Device Groep", + "ASSET_GROUP": "Activagroep", + "USER_GROUP": "Gebruikersgroep", + "ENTITY_VIEW_GROUP": "Groep Entiteitsweergave", + "DASHBOARD_GROUP": "Dashboard Groep", + "ROLE": "Rol", + "GROUP_PERMISSION": "Toestemming voor groepen", + "WHITE_LABELING": "White labelling", + "AUDIT_LOG": "Controle logboek", + "API_USAGE_STATE": "Status API-gebruik", + "TB_RESOURCE": "Hulpbron", + "EDGE_GROUP": "Edge-groep", + "OTA_PACKAGE": "Ota-pakket", + "QUEUE": "Rij", + "VERSION_CONTROL": "Versiebeheer", + "ASSET_PROFILE": "Asset Profiel", + "NOTIFICATION": "Bekendmaking" + } + }, + "operation": { + "operation": "Operatie", + "operations": "Operaties", + "operations-required": "Er moet ten minste één bewerking worden opgegeven.", + "enter-operation": "Operatie invoeren", + "no-operations-matching": "Er zijn geen bewerkingen gevonden die overeenkomen met '{{operation}}'.", + "display-type": { + "ALL": "Alle", + "CREATE": "Nieuw", + "READ": "Lezen", + "WRITE": "Schrijven", + "DELETE": "Verwijderen", + "ASSIGN_TO_CUSTOMER": "Toewijzen aan klant", + "UNASSIGN_FROM_CUSTOMER": "Toewijzing van klant ongedaan maken", + "RPC_CALL": "RPC-oproep", + "READ_CREDENTIALS": "Inloggegevens lezen", + "WRITE_CREDENTIALS": "Referenties schrijven", + "READ_ATTRIBUTES": "Attributen lezen", + "WRITE_ATTRIBUTES": "Attributen schrijven", + "READ_TELEMETRY": "Telemetrie lezen", + "WRITE_TELEMETRY": "Telemetrie schrijven", + "CLAIM_DEVICES": "Devices claimen", + "IMPERSONATE": "Imiteren", + "CHANGE_OWNER": "Eigenaar wijzigen", + "ADD_TO_GROUP": "Voeg toe aan groep", + "REMOVE_FROM_GROUP": "Verwijderen uit groep", + "SHARE_GROUP": "Groep delen", + "ASSIGN_TO_TENANT": "Toewijzen aan tenant" + } + } + }, + "scheduler": { + "scheduler": "Scheduler", + "scheduler-event": "Scheduler-event", + "select-scheduler-event": "Scheduler event selecteren", + "no-scheduler-events-matching": "Er zijn geen scheduler events gevonden die overeenkomen met '{{entity}}'.", + "scheduler-event-required": "Scheduler-event is vereist", + "management": "Beheer van de planning", + "scheduler-events": "Scheduler-events", + "add-scheduler-event": "Scheduler-event toevoegen", + "search-scheduler-events": "Scheduler-events zoeken", + "created-time": "Gecreëerde tijd", + "name": "Naam", + "name-required": "Naam is verplicht", + "name-max-length": "Naam moet kleiner zijn dan 256 tekens", + "type": "Type", + "created_customer": "Gemaakt door de klant", + "edit-scheduler-event": "Scheduler-event bewerken", + "view-scheduler-event": "Scheduler event weergeven", + "delete-scheduler-event": "Scheduler-event verwijderen", + "no-scheduler-events": "Geen planner-gebeurtenissen gevonden", + "selected-scheduler-events": "{ count, plural, =1 {1 scheduler event} andere {# scheduler events} } geselecteerd", + "delete-scheduler-event-title": "Weet u zeker dat u de '{{schedulerEventName}}' van de planningsgebeurtenis wilt verwijderen?", + "delete-scheduler-event-text": "Opgelet, na de bevestiging worden de event van de planner en alle gerelateerde gegevens onherstelbaar.", + "delete-scheduler-events-title": "Weet u zeker dat u { count, plural, =1 {1 scheduler event} andere {# scheduler events} } wilt verwijderen?", + "delete-scheduler-events-text": "Opgelet, na de bevestiging worden alle geselecteerde scheduler events verwijderd en kunnen alle gerelateerde gegevens niet meer worden hersteld.", + "create": "Scheduler-event maken", + "edit": "Scheduler-event bewerken", + "view": "Scheduler event weergeven", + "configuration": "Configuratie", + "schedule": "Rooster", + "start-time": "Begintijd", + "repeat": "Herhalen", + "repeats": "Herhaalt", + "daily": "Dagelijks", + "every-n-days": "Elke N dagen", + "every-n-days-text": "Elke { days, plural, =1 {day} andere {# days} }", + "weekly": "Wekelijks", + "every-n-weeks": "Elke N weken", + "every-n-weeks-text": "Elke { weeks, plural, =1 {week} andere {# weeks} }", + "monthly": "Maandelijks", + "yearly": "Jaarlijks", + "timer": "Op basis van een timer", + "repeats-required": "Herhalingen zijn vereist.", + "repeat-on": "Herhaal op", + "repeat-every": "Herhaal elke", + "repeat-every-n-days": "Herhaal dit elke N dagen", + "repeat-days-required": "Het aantal dagen om te herhalen is vereist.", + "invalid-repeat-days-value": "Het aantal te herhalen dagen moet een positief geheel getal zijn.", + "repeat-every-n-weeks": "Herhaal elke N weken", + "repeat-weeks-required": "Aantal weken om te herhalen is vereist.", + "invalid-repeat-weeks-value": "Het aantal te herhalen weken moet een positief geheel getal zijn.", + "ends-on": "Eindigt op", + "sunday-label": "S", + "monday-label": "M", + "tuesday-label": "T", + "wednesday-label": "W", + "thursday-label": "T", + "friday-label": "F", + "saturday-label": "S", + "repeat-on-sunday": "Herhaling op zondag", + "repeat-on-monday": "Herhaal op maandag", + "repeat-on-tuesday": "Herhaal op dinsdag", + "repeat-on-wednesday": "Herhaal op woensdag", + "repeat-on-thursday": "Herhaal op donderdag", + "repeat-on-friday": "Herhaal op vrijdag", + "repeat-on-saturday": "Herhaling op zaterdag", + "event-type": "Type event", + "select-event-type": "Selecteer evenementtype", + "event-type-required": "Gebeurtenistype is vereist.", + "event-type-max-length": "Het gebeurtenistype moet kleiner zijn dan 256 tekens.", + "list-mode": "Lijst weergave", + "calendar-mode": "Kalenderweergave", + "calendar-view-type": "Type kalenderweergave", + "month": "Maand", + "week": "Week", + "day": "Dag", + "agenda-week": "Agenda Week", + "agenda-day": "Dag van de agenda", + "list-year": "Lijst Jaar", + "list-month": "Lijst Maand", + "list-week": "Lijst Week", + "list-day": "Lijst Dag", + "today": "Vandaag", + "navigate-before": "Navigeer voor", + "navigate-next": "Navigeer volgende", + "starting-from": "Vanaf", + "until": "totdat", + "on": "op", + "sunday": "Zondag", + "monday": "Maandag", + "tuesday": "Dinsdag", + "wednesday": "Woensdag", + "thursday": "Donderdag", + "friday": "Vrijdag", + "saturday": "Zaterdag", + "originator": "Opdrachtgever", + "single-entity": "Eén entiteit", + "group-of-entities": "Groep van entiteiten", + "entities-group-owner": "Eigenaar van entiteitengroep", + "single-device": "Eén device", + "group-of-devices": "Groep Devices", + "devices-group-owner": "Eigenaar van de Devicesgroep", + "message-body": "Hoofdtekst van bericht", + "target": "Doel", + "rpc-method": "Methode", + "rpc-method-required": "Methode is vereist", + "rpc-method-white-space": "Witruimte is niet toegestaan.", + "rpc-params": "Params", + "select-dashboard-state": "Dashboardstatus selecteren", + "hours": "Uren", + "minutes": "Notulen", + "seconds": "Seconden", + "time-interval-required": "Tijdsinterval is vereist", + "time-unit-required": "Tijdseenheid is vereist", + "every-hour": "elke { count, plural, =1 {hour} andere {# hours} }", + "every-minute": "elke { count, plural, =1 {minute} andere {# minutes} }", + "every-second": "elke { count, plural, =1 {second} andere {# seconds} }", + "invalid-time": "Ongeldige tijd" + }, + "report": { + "report-config": "Configuratie van rapporten", + "email-config": "E-mail configuratie", + "dashboard-state-param": "Parameterwaarde dashboardstatus", + "base-url": "Basis-URL", + "base-url-required": "Basis-URL is vereist.", + "use-dashboard-timewindow": "Dashboard tijdvenster gebruiken", + "timewindow": "Tijdvenster", + "name-pattern": "Patroon van rapportnaam", + "name-pattern-required": "Rapportnaam patroon is vereist", + "type": "Soort rapport", + "use-current-user-credentials": "Huidige gebruikersreferenties gebruiken", + "customer-user-credentials": "Gebruikersgegevens van de klant", + "customer-user-credentials-required": "Gebruikersgegevens van de klant zijn vereist", + "generate-test-report": "Testrapport genereren", + "send-email": "Stuur een e-mail", + "from": "Van", + "from-required": "Van is vereist.", + "to": "Aan", + "to-required": "Naar is vereist.", + "cc": "CC", + "bcc": "Bcc", + "subject": "Onderwerp", + "subject-required": "Onderwerp is verplicht.", + "body": "Body", + "body-required": "Body is vereist." + }, + "blob-entity": { + "blob-entity": "Blob-entiteit", + "select-blob-entity": "Blob-entiteit selecteren", + "no-blob-entities-matching": "Er zijn geen blob-entiteiten gevonden die overeenkomen met '{{entity}}'.", + "blob-entity-required": "Blob-entiteit is vereist", + "files": "Archief", + "search": "Bestanden zoeken", + "clear-search": "Zoekopdracht wissen", + "no-blob-entities-prompt": "Geen bestanden gevonden", + "report": "Rapport", + "created-time": "Gecreëerde tijd", + "name": "Naam", + "type": "Type", + "created_customer": "Gemaakt door de klant", + "selected-blob-entities": "{ count, plural, =1 {1 file} andere {# files} } geselecteerd", + "download-blob-entity": "Bestand downloaden", + "delete-blob-entity": "Bestand verwijderen", + "delete-blob-entity-title": "Weet u zeker dat u bestand '{{blobEntityName}}' wilt verwijderen?", + "delete-blob-entity-text": "Opgelet, na de bevestiging worden de gegevens van het bestand onherstelbaar.", + "delete-blob-entities-title": "Weet u zeker dat u { count, plural, =1 {1 file} andere {# files} } wilt verwijderen?", + "delete-blob-entities-text": "Opgelet, na de bevestiging worden alle geselecteerde bestanden verwijderd en kunnen alle gerelateerde gegevens niet meer worden hersteld." + }, + "timezone": { + "timezone": "Tijdzone", + "select-timezone": "Selecteer tijdzone", + "no-timezones-matching": "Er zijn geen tijdzones gevonden die overeenkomen met '{{timezone}}'.", + "timezone-required": "Tijdzone is vereist.", + "browser-time": "Browser Tijd" + }, + "queue": { + "queue-name": "Rij", + "no-queues-found": "Geen queues gevonden.", + "no-queues-matching": "Er zijn geen queues gevonden die overeenkomen met '{{queue}}'.", + "select-name": "Selecteer de naam van de queue", + "name": "Naam", + "name-required": "Queue naam is verplicht!", + "name-unique": "De naam van de queue is niet uniek!", + "name-pattern": "De naam van de queue bevat een ander teken dan ASCII-alfanumerieke tekens, '.', '_' en '-'!", + "queue-required": "Queue is verplicht!", + "topic-required": "Wachtrijonderwerp is vereist!", + "poll-interval-required": "Poll-interval is vereist!", + "poll-interval-min-value": "De waarde van het pollinterval mag niet kleiner zijn dan 1", + "partitions-required": "Partities is vereist!", + "partitions-min-value": "De waarde van partities mag niet kleiner zijn dan 1", + "pack-processing-timeout-required": "Time-out voor verwerking is vereist", + "pack-processing-timeout-min-value": "De time-outwaarde voor verwerking mag niet kleiner zijn dan 1", + "batch-size-required": "Batchgrootte is vereist!", + "batch-size-min-value": "De waarde van de batchgrootte mag niet kleiner zijn dan 1", + "retries-required": "Opnieuw proberen is vereist!", + "retries-min-value": "De waarde voor nieuwe pogingen mag niet negatief zijn", + "failure-percentage-required": "Error percentage is vereist!", + "failure-percentage-min-value": "De waarde van het error percentage mag niet kleiner zijn dan 0", + "failure-percentage-max-value": "De waarde van het error percentage mag niet hoger zijn dan 100", + "pause-between-retries-required": "Pauze tussen nieuwe pogingen is vereist!", + "pause-between-retries-min-value": "De waarde van de pauze tussen nieuwe pogingen mag niet kleiner zijn dan 1", + "max-pause-between-retries-required": "Maximale pauze tussen nieuwe pogingen is vereist!", + "max-pause-between-retries-min-value": "De maximale pauze tussen de waarde voor nieuwe pogingen mag niet minder zijn dan 1", + "submit-strategy-type-required": "Strategietype indienen is vereist!", + "processing-strategy-type-required": "Het type verwerkingsstrategie is vereist!", + "queues": "Queues", + "selected-queues": "{ count, plural, =1 {1 queue} andere {# queues} } geselecteerd", + "delete-queue-title": "Weet je zeker dat je de queue '{{queueName}}' wilt verwijderen?", + "delete-queues-title": "Weet u zeker dat u { count, plural, =1 {1 queue} andere {# queues} } wilt verwijderen?", + "delete-queue-text": "Opgelet, na de bevestiging worden de queue en alle gerelateerde gegevens onherstelbaar.", + "delete-queues-text": "Na de bevestiging worden alle geselecteerde queues verwijderd en zijn ze niet meer toegankelijk.", + "search": "Queue zoeken", + "add": "Queue toevoegen", + "details": "Details van de queue", + "topic": "Onderwerp", + "submit-settings": "Instellingen verzenden", + "submit-strategy": "Soort strategie *", + "grouping-parameter": "Parameter groeperen", + "processing-settings": "Verwerkingsinstellingen opnieuw proberen", + "processing-strategy": "Soort verwerking *", + "retries-settings": "Instellingen voor nieuwe pogingen", + "polling-settings": "Polling-instellingen", + "batch-processing": "Batchverwerking", + "poll-interval": "Poll-interval", + "partitions": "Partities", + "immediate-processing": "Onmiddellijke verwerking", + "consumer-per-partition": "Stuur een berichtenquête voor elke consument", + "consumer-per-partition-hint": "Schakel afzonderlijke verbruiker(s) in voor elke partitie", + "processing-timeout": "Verwerking binnen, ms", + "batch-size": "Grootte van de partij", + "retries": "Aantal nieuwe pogingen (0 – onbeperkt)", + "failure-percentage": "Percentage mislukte berichten voor het overslaan van nieuwe pogingen", + "pause-between-retries": "Probeer het opnieuw binnen, sec", + "max-pause-between-retries": "Extra nieuwe poging binnen, sec", + "delete": "Queue verwijderen", + "copyId": "Queue-ID kopiëren", + "idCopiedMessage": "Queue-ID is gekopieerd naar klembord", + "description": "Omschrijving: __________", + "description-hint": "Deze tekst wordt weergegeven in de wachtrijbeschrijving in plaats van de geselecteerde strategie", + "alt-description": "Strategie indienen: {{submitStrategy}}, verwerkingsstrategie: {{processingStrategy}}", + "strategies": { + "sequential-by-originator-label": "Opeenvolgend door opsteller", + "sequential-by-originator-hint": "Het nieuwe bericht voor bijvoorbeeld device A wordt pas verzonden als het vorige bericht voor device A is bevestigd", + "sequential-by-tenant-label": "Sequentieel per tenant", + "sequential-by-tenant-hint": "Nieuw bericht voor bijvoorbeeld tenant A wordt pas verzonden als het vorige bericht voor tenant A is bevestigd", + "sequential-label": "Sequentieel", + "sequential-hint": "Nieuw bericht wordt pas verzonden als het vorige bericht is bevestigd", + "burst-label": "Barsten", + "burst-hint": "Alle berichten worden verzonden naar de rule chains in de volgorde waarin ze binnenkomen", + "batch-label": "Batch", + "batch-hint": "De nieuwe batch wordt pas ingediend als de vorige batch is bevestigd", + "skip-all-failures-label": "Sla alle storingen over", + "skip-all-failures-hint": "Negeer alle fouten", + "skip-all-failures-and-timeouts-label": "Sla alle storingen en time-outs over", + "skip-all-failures-and-timeouts-hint": "Negeer alle storingen en time-outs", + "retry-all-label": "Probeer alles opnieuw", + "retry-all-hint": "Probeer alle berichten uit het verwerkingspakket opnieuw uit te voeren", + "retry-failed-label": "Opnieuw proberen mislukt", + "retry-failed-hint": "Probeer alle mislukte berichten opnieuw uit het verwerkingspakket", + "retry-timeout-label": "Time-out opnieuw proberen", + "retry-timeout-hint": "Probeer alle time-outberichten opnieuw uit het verwerkingspakket", + "retry-failed-and-timeout-label": "Opnieuw proberen mislukt en time-out", + "retry-failed-and-timeout-hint": "Probeer alle mislukte en getimede berichten opnieuw uit het verwerkingspakket" + } + }, + "server-error": { + "general": "Algemene serverfout", + "authentication": "Authenticatie fout", + "jwt-token-expired": "JWT-token is verlopen", + "tenant-trial-expired": "Proefperiode tenant verlopen", + "credentials-expired": "Inloggegevens verlopen", + "permission-denied": "Toestemming geweigerd", + "invalid-arguments": "Ongeldige argumenten", + "bad-request-params": "Ongeldige aanvraagparameters", + "item-not-found": "Object niet gevonden", + "too-many-requests": "Te veel aanvragen", + "too-many-updates": "Te veel updates" + }, + "tenant": { + "tenant": "Tenant", + "tenants": "Tenants", + "management": "Beheer van tenants", + "add": "Tenant toevoegen", + "admins": "Beheerders", + "manage-tenant-admins": "Tenant beheerders beheren", + "delete": "Tenant verwijderen", + "add-tenant-text": "Nieuwe tenant toevoegen", + "no-tenants-text": "Geen tenants gevonden", + "tenant-details": "Gegevens tenant", + "title-max-length": "Titel moet kleiner zijn dan 256 tekens", + "delete-tenant-title": "Weet u zeker dat u de tenant '{{tenantTitle}}' wilt verwijderen?", + "delete-tenant-text": "Let op, na de bevestiging worden de tenant en alle gerelateerde gegevens onherstelbaar.", + "delete-tenants-title": "Weet u zeker dat u { count, plural, =1 {1 tenant} andere {# tenants} } wilt verwijderen?", + "delete-tenants-action-title": "Verwijder { count, plural, =1 {1 tenant} andere {# tenants} }", + "delete-tenants-text": "Let op, na de bevestiging worden alle geselecteerde tenants verwijderd en worden alle gerelateerde gegevens onherstelbaar.", + "title": "Titel", + "title-required": "Titel is vereist.", + "description": "Omschrijving: __________", + "details": "Details", + "events": "Events", + "copyId": "Tenant-id kopiëren", + "idCopiedMessage": "Tenant-id is gekopieerd naar klembord", + "select-tenant": "Selecteer tenant", + "no-tenants-matching": "Er zijn geen tenants gevonden die overeenkomen met '{{entity}}'.", + "tenant-required": "Tenant is verplicht", + "allow-white-labeling": "White Labeling toestaan", + "allow-customer-white-labeling": "White labeling van klanten toestaan", + "search": "Tenants zoeken", + "selected-tenants": "{ count, plural, =1 {1 tenant} andere {# tenants} } geselecteerd", + "isolated-tb-rule-engine": "Verwerking in geïsoleerde ThingsBoard Rule Engine-container", + "isolated-tb-rule-engine-details": "Vereist afzonderlijke microservice(s) per geïsoleerde tenant" + }, + "tenant-profile": { + "tenant-profile": "Profiel van de tenant", + "tenant-profiles": "Profielen van tenants", + "add": "Huurdersprofiel toevoegen", + "add-profile": "Profiel toevoegen", + "edit": "Huurdersprofiel bewerken", + "tenant-profile-details": "Details van tenantsprofiel", + "no-tenant-profiles-text": "Geen tenantsprofielen gevonden", + "name-max-length": "Naam moet kleiner zijn dan 256 tekens", + "search": "Tentant profielen zoeken", + "selected-tenant-profiles": "{ count, plural, =1 {1 tenant profile} andere {# tenant profiles} } geselecteerd", + "no-tenant-profiles-matching": "Er zijn geen tenantprofielen gevonden die overeenkomen met '{{entity}}'.", + "tenant-profile-required": "Huurdersprofiel is vereist", + "idCopiedMessage": "De profiel-id van de tenant is gekopieerd naar het klembord", + "set-default": "Tenantprofiel standaard instellen", + "delete": "Tenantprofiel verwijderen", + "copyId": "Tenantprofiel-id kopiëren", + "name": "Naam", + "name-required": "Naam is verplicht.", + "data": "Profiel gegevens", + "profile-configuration": "Profiel configuratie", + "description": "Omschrijving: __________", + "default": "Verstek", + "delete-tenant-profile-title": "Weet u zeker dat u het tenantsprofiel '{{tenantProfileName}}' wilt verwijderen?", + "delete-tenant-profile-text": "Let op, na de bevestiging wordt het tenantsprofiel en alle gerelateerde gegevens onherstelbaar.", + "delete-tenant-profiles-title": "Weet u zeker dat u { count, plural, =1 {1 tenant profile} andere {# tenant profiles} } wilt verwijderen?", + "delete-tenant-profiles-text": "Opgelet, na de bevestiging worden alle geselecteerde tenantsprofielen verwijderd en kunnen alle gerelateerde gegevens niet meer worden hersteld.", + "set-default-tenant-profile-title": "Weet u zeker dat u het tenantsprofiel standaard '{{tenantProfileName}}' wilt maken?", + "set-default-tenant-profile-text": "Na de bevestiging wordt het tenantsprofiel als standaard gemarkeerd en wordt het gebruikt voor nieuwe tenants waarvoor geen profiel is opgegeven.", + "no-tenant-profiles-found": "Er zijn geen tenantsprofielen gevonden.", + "create-new-tenant-profile": "Maak een nieuwe aan!", + "create-tenant-profile": "Nieuw tenantsprofiel maken", + "import": "Tenantprofiel importeren", + "export": "Tenantprofiel exporteren", + "export-failed-error": "Kan tenantprofiel niet exporteren: {{error}}", + "tenant-profile-file": "Bestand met tenantsprofiel", + "invalid-tenant-profile-file-error": "Kan tenantprofiel niet importeren: Ongeldige gegevensstructuur van tenantprofiel.", + "advanced-settings": "Geavanceerde instellingen", + "entities": "Entiteiten", + "rule-engine": "Rule engine", + "time-to-live": "Tijd om te leven", + "alarms-and-notifications": "Alarmen en meldingen", + "ota-files-in-bytes": "OTA-bestanden in bytes", + "ws-title": "WS", + "unlimited": "(0 - onbeperkt)", + "maximum-devices": "Maximaal aantal Devices", + "maximum-devices-required": "Het maximale aantal devices is vereist.", + "maximum-devices-range": "Het maximale aantal devices mag niet negatief zijn", + "maximum-assets": "Maximaal aantal asset", + "maximum-assets-required": "Het maximale aantal asset is vereist.", + "maximum-assets-range": "Het maximumaantal items mag niet negatief zijn", + "maximum-customers": "Maximaal aantal klanten", + "maximum-customers-required": "Het maximale aantal klanten is vereist.", + "maximum-customers-range": "Het maximale aantal klanten mag niet negatief zijn", + "maximum-users": "Maximaal aantal gebruikers", + "maximum-users-required": "Het maximale aantal gebruikers is vereist.", + "maximum-users-range": "Het maximumaantal gebruikers mag niet negatief zijn", + "maximum-dashboards": "Dashboards maximaal aantal", + "maximum-dashboards-required": "Het maximale aantal dashboards is vereist.", + "maximum-dashboards-range": "Het maximumaantal dashboards mag niet negatief zijn", + "maximum-rule-chains": "Maximaal aantal rule chains", + "maximum-rule-chains-required": "Het maximale aantal rule chains is vereist.", + "maximum-rule-chains-range": "Het maximale aantal rule chains mag niet negatief zijn", + "maximum-integrations": "Integraties maximaal aantal", + "maximum-integrations-required": "Integraties maximaal aantal is vereist.", + "maximum-integrations-range": "Het maximale aantal integraties mag niet negatief zijn", + "maximum-converters": "Converters maximaal aantal", + "maximum-converters-required": "Het maximale aantal converters is vereist.", + "maximum-converters-range": "Het maximale aantal converters mag niet negatief zijn", + "maximum-scheduler-events": "Maximaal aantal scheduler events", + "maximum-scheduler-events-required": "Het maximale aantal scheduler events is vereist.", + "maximum-scheduler-events-range": "Het maximale aantal scheduler events mag niet negatief zijn", + "maximum-resources-sum-data-size": "Totale grootte van bronbestanden", + "maximum-resources-sum-data-size-required": "De somgrootte van bronbestanden is vereist.", + "maximum-resources-sum-data-size-range": "De somgrootte van resourcebestanden mag niet negatief zijn", + "maximum-ota-packages-sum-data-size": "Totale grootte van OTA-pakketbestanden", + "maximum-ota-package-sum-data-size-required": "De somgrootte van OTA-pakketbestanden is vereist.", + "maximum-ota-package-sum-data-size-range": "De somgrootte van OTA-pakketbestanden mag niet negatief zijn", + "rest-requests-for-tenant": "REST-aanvragen voor tenant", + "transport-tenant-telemetry-msg-rate-limit": "Telemetrieberichten van tenants vervoeren", + "transport-tenant-telemetry-data-points-rate-limit": "Telemetrie gegevenspunten voor transporttenants", + "transport-device-msg-rate-limit": "Berichten over transportmiddelen", + "transport-device-telemetry-msg-rate-limit": "Telemetrieberichten van transport devices", + "transport-device-telemetry-data-points-rate-limit": "Telemetrie gegevenspunten voor transport devices", + "tenant-entity-export-rate-limit": "Aanmaken van entiteitsversies", + "tenant-entity-import-rate-limit": "Laden van entiteitsversie", + "tenant-notification-request-rate-limit": "Verzoeken om kennisgeving", + "tenant-notification-requests-per-rule-rate-limit": "Notificatieverzoeken per notificatieregel", + "max-transport-messages": "Maximaal aantal transportberichten", + "max-transport-messages-required": "Transportberichten maximaal aantal is vereist.", + "max-transport-messages-range": "Het maximale aantal transportberichten mag niet negatief zijn", + "max-transport-data-points": "Maximaal aantal transportdatapunten", + "max-transport-data-points-required": "Er is een maximaal aantal transportdatapunten vereist.", + "max-transport-data-points-range": "Het maximale aantal transport gegevenspunten mag niet negatief zijn", + "max-r-e-executions": "Maximaal aantal uitvoeringen van rule engine", + "max-r-e-executions-required": "Het maximale aantal uitvoeringen van de rule engine is vereist.", + "max-r-e-executions-range": "Het maximale aantal uitvoeringen van de rule engine mag niet negatief zijn", + "max-j-s-executions": "Maximaal aantal JavaScript-uitvoeringen", + "max-j-s-executions-required": "Het maximale aantal JavaScript-uitvoeringen is vereist.", + "max-j-s-executions-range": "Het maximale aantal JavaScript-uitvoeringen mag niet negatief zijn", + "max-d-p-storage-days": "Maximaal aantal bewaardagen datapunten", + "max-d-p-storage-days-required": "Het maximale aantal opslagdagen voor datapunten is vereist.", + "max-d-p-storage-days-range": "Het maximale aantal opslagdagen voor gegevenspunten kan niet negatief zijn", + "default-storage-ttl-days": "Opslag TTL-dagen standaard", + "default-storage-ttl-days-required": "Standaard zijn TTL-dagen voor opslag vereist.", + "default-storage-ttl-days-range": "Opslag TTL-dagen kunnen standaard niet negatief zijn", + "alarms-ttl-days": "Alarmen TTL-dagen", + "alarms-ttl-days-required": "Alarmen TTL-dagen vereist", + "alarms-ttl-days-days-range": "Alarmen TTL-dagen kunnen niet negatief zijn", + "rpc-ttl-days": "RPC TTL-dagen", + "rpc-ttl-days-required": "RPC TTL-dagen vereist", + "rpc-ttl-days-days-range": "RPC TTL-dagen kunnen niet negatief zijn", + "max-rule-node-executions-per-message": "Maximaal aantal uitvoeringen van rule node per bericht", + "max-rule-node-executions-per-message-required": "MRule node per bericht voert het maximale aantal uit dat wordt uitgevoerd.", + "max-rule-node-executions-per-message-range": "Rule node per bericht dat wordt uitgevoerd, het maximale aantal kan niet negatief zijn", + "max-emails": "Maximaal aantal verzonden e-mails", + "max-emails-required": "Het maximale aantal verzonden e-mails is vereist.", + "max-emails-range": "Het maximale aantal verzonden e-mails mag niet negatief zijn", + "max-sms": "Maximaal aantal sms-berichten", + "max-sms-required": "Het maximale aantal sms-berichten is vereist.", + "max-sms-range": "Het maximale aantal verzonden sms'jes mag niet negatief zijn", + "max-created-alarms": "Maximaal aantal alarmen gemaakt", + "max-created-alarms-required": "Het maximale aantal alarmen is vereist.", + "max-created-alarms-range": "Alarmen gemaakt maximaal aantal negatief zijn", + "no-queue": "Geen queue geconfigureerd", + "add-queue": "Queue toevoegen", + "queues-with-count": "Queues ({{count}})", + "tenant-rest-limits": "REST-aanvragen voor tenant", + "customer-rest-limits": "REST-aanvragen voor klant", + "incorrect-pattern-for-rate-limits": "Het formaat bestaat uit door komma's gescheiden paren van capaciteit en punt (in seconden) met een dubbele punt ertussen, bijvoorbeeld 100:1,2000:60", + "too-small-value-zero": "De waarde moet groter zijn dan 0", + "too-small-value-one": "De waarde moet groter zijn dan 1", + "queue-size-is-limited-by-system-configuration": "De grootte van de queue wordt ook beperkt door de systeemconfiguratie.", + "cassandra-tenant-limits-configuration": "Cassandra-query voor tenant", + "ws-limit-max-sessions-per-tenant": "Sessies per tenant maximaal aantal", + "ws-limit-max-sessions-per-customer": "Maximaal aantal sessies per klant", + "ws-limit-max-sessions-per-regular-user": "Sessies per reguliere gebruiker maximaal aantal", + "ws-limit-max-sessions-per-public-user": "Maximaal aantal sessies per openbare gebruiker", + "ws-limit-queue-per-session": "Maximale grootte van berichtenwachtrij per sessie", + "ws-limit-max-subscriptions-per-tenant": "Maximaal aantal abonnementen per tenant", + "ws-limit-max-subscriptions-per-customer": "Abonnementen per klant maximum aantal", + "ws-limit-max-subscriptions-per-regular-user": "Abonnementen per gewone gebruiker maximum aantal", + "ws-limit-max-subscriptions-per-public-user": "Abonnementen per openbare gebruiker maximum aantal", + "ws-limit-updates-per-session": "WS-updates per sessie", + "rate-limits": { + "add-limit": "Limiet toevoegen", + "advanced-settings": "Geavanceerde instellingen", + "edit-limit": "Limiet bewerken", + "but-less-than": "maar minder dan", + "edit-transport-tenant-msg-title": "Snelheidslimieten voor berichten van transporttenants bewerken", + "edit-transport-tenant-telemetry-msg-title": "Snelheidslimieten voor telemetrie berichten van transporttenants bewerken", + "edit-transport-tenant-telemetry-data-points-title": "Snelheidslimieten voor telemetrie gegevenspunten van transporttenants bewerken", + "edit-transport-device-msg-title": "Snelheidslimieten voor berichten van transport devices bewerken", + "edit-transport-device-telemetry-msg-title": "Snelheidslimieten voor telemetrie berichten van transport devices bewerken", + "edit-transport-device-telemetry-data-points-title": "Snelheidslimieten voor telemetriegegevenspunten van transport devices bewerken", + "edit-tenant-rest-limits-title": "REST-aanvragen voor tarieflimieten voor tenants bewerken", + "edit-customer-rest-limits-title": "REST-aanvragen voor snelheidslimieten voor klanten bewerken", + "edit-ws-limit-updates-per-session-title": "Snelheidslimieten voor WS-updates per sessie bewerken", + "edit-cassandra-tenant-limits-configuration-title": "Cassandra-query bewerken voor tarieflimieten voor tenants", + "edit-tenant-entity-export-rate-limit-title": "Limieten voor het maken van entiteitsversies bewerken", + "edit-tenant-entity-import-rate-limit-title": "Limieten voor de laadsnelheid van entiteitsversies bewerken", + "edit-tenant-notification-request-rate-limit-title": "Snelheidslimieten voor meldingsverzoeken bewerken", + "edit-tenant-notification-requests-per-rule-rate-limit-title": "Snelheidslimieten voor meldingsverzoeken per meldingsregel bewerken", + "messages-per": "berichten per", + "not-set": "Niet ingesteld", + "number-of-messages": "Aantal berichten", + "number-of-messages-required": "Het aantal berichten is vereist.", + "number-of-messages-min": "Minimale waarde is 1.", + "preview": "Voorbeeld", + "per-seconds": "Per seconde", + "per-seconds-required": "Tijdtarief is vereist.", + "per-seconds-min": "Minimale waarde is 1.", + "rate-limits": "Limieten voor tarieven", + "remove-limit": "Limiet verwijderen", + "transport-tenant-msg": "Berichten van tenants vervoeren", + "transport-tenant-telemetry-msg": "Telemetrieberichten van tenants vervoeren", + "transport-tenant-telemetry-data-points": "Telemetriege gevenspunten voor transport tenants", + "transport-device-msg": "Berichten over transportmiddelen", + "transport-device-telemetry-msg": "Telemetrieberichten van transport devices", + "transport-device-telemetry-data-points": "Telemetriegegevenspunten voor transport devices", + "sec": "seconde" + } + }, + "timeinterval": { + "seconds-interval": "{ seconds, plural, =1 {1 second} andere {# seconds} }", + "minutes-interval": "{ minutes, plural, =1 {1 minute} andere {# minutes} }", + "hours-interval": "{ hours, plural, =1 {1 hour} andere {# hours} }", + "days-interval": "{ days, plural, =1 {1 day} andere {# days} }", + "days": "Dagen", + "hours": "Uren", + "minutes": "Notulen", + "seconds": "Seconden", + "advanced": "Geavanceerd", + "predefined": { + "yesterday": "Gisteren", + "day-before-yesterday": "Eergisteren", + "this-day-last-week": "Deze dag vorige week", + "previous-week": "Vorige week (zo - za)", + "previous-week-iso": "Vorige week (ma - zo)", + "previous-month": "Vorige maand", + "previous-quarter": "Vorig kwartaal", + "previous-half-year": "Vorig halfjaar", + "previous-year": "Vorig jaar", + "current-hour": "Huidig uur", + "current-day": "Huidige dag", + "current-day-so-far": "Huidige dag tot nu toe", + "current-week": "Huidige week (zo - za)", + "current-week-iso": "Huidige week (ma - zo)", + "current-week-so-far": "Huidige week tot nu toe (zo - za)", + "current-week-iso-so-far": "Huidige week tot nu toe (ma - zo)", + "current-month": "Huidige maand", + "current-month-so-far": "Huidige maand tot nu toe", + "current-quarter": "Huidig kwartaal", + "current-quarter-so-far": "Huidig kwartaal tot nu toe", + "current-half-year": "Huidig halfjaar", + "current-half-year-so-far": "Huidig halfjaar tot nu toe", + "current-year": "Huidig jaar", + "current-year-so-far": "Huidig jaar tot nu toe" + } + }, + "timeunit": { + "milliseconds": "Milliseconden", + "seconds": "Seconden", + "minutes": "Notulen", + "hours": "Uren", + "days": "Dagen" + }, + "timewindow": { + "years": "{ years, plural, =1 { year } andere {# years } }", + "months": "{ months, plural, =1 { month } andere {# months } }", + "weeks": "{ weeks, plural, =1 { week } andere {# weeks } }", + "days": "{ days, plural, =1 { day } andere {# days } }", + "hours": "{ hours, plural, =0 { hour } =1 {1 hour } andere {# hours } }", + "hr": "{{ hr }} uur", + "minutes": "{ minutes, plural, =0 { minute } =1 {1 minute } andere {# minutes } }", + "min": "{{ min }} min", + "seconds": "{ seconds, plural, =0 { second } =1 {1 second } andere {# seconds } }", + "sec": "{{ sec }} sec", + "short": { + "days": "{ days, plural, =1 {1 day } andere {# days } }", + "hours": "{ hours, plural, =1 {1 hour } andere {# hours } }", + "minutes": "{{minutes}} min", + "seconds": "{{seconds}} sec" + }, + "realtime": "Realtime", + "history": "Geschiedenis", + "last-prefix": "laatste", + "period": "van {{ startTime }} tot {{ endTime }}", + "edit": "Tijdvenster bewerken", + "date-range": "Periode", + "for-all-time": "Permanent", + "last": "Laatste", + "time-period": "Periode", + "hide": "Verbergen", + "interval": "Interval", + "just-now": "Daarnet", + "ago": "geleden" + }, + "user": { + "all": "Alle", + "all-users": "Alle gebruikers", + "groups": "Groepen", + "user": "Gebruiker", + "users": "Gebruikers", + "management": "Gebruikersbeheer", + "customer-users": "Gebruikers van klanten", + "tenant-admins": "Tenant Admins", + "sys-admin": "Systeembeheerder", + "tenant-admin": "Tenant beheerder", + "customer": "Klant", + "anonymous": "Anoniem", + "add": "Gebruiker toevoegen", + "delete": "Gebruiker verwijderen", + "add-user-text": "Nieuwe gebruiker toevoegen", + "no-users-text": "Geen gebruikers gevonden", + "user-details": "Gegevens van de gebruiker", + "delete-users": "Gebruikers verwijderen", + "delete-user-title": "Weet u zeker dat u de '{{userEmail}}' van de gebruiker wilt verwijderen?", + "delete-user-text": "Opgelet, na de bevestiging worden de gebruiker en alle gerelateerde gegevens onherstelbaar.", + "delete-users-title": "Weet u zeker dat u { count, plural, =1 {1 user} andere {# users} } wilt verwijderen?", + "delete-users-action-title": "Verwijder { count, plural, =1 {1 user} andere {# users} }", + "delete-users-text": "Opgelet, na de bevestiging worden alle geselecteerde gebruikers verwijderd en kunnen alle gerelateerde gegevens niet meer worden hersteld.", + "activation-email-sent-message": "Activeringsmail is succesvol verzonden!", + "resend-activation": "Activering opnieuw verzenden", + "email": "E-mail", + "email-required": "E-mail is vereist.", + "invalid-email-format": "Ongeldig e-mailformaat.", + "first-name": "Voornaam", + "last-name": "Achternaam", + "description": "Omschrijving: __________", + "default-dashboard": "Standaard dashboard", + "always-fullscreen": "Altijd volledig scherm", + "select-user": "Selecteer gebruiker", + "no-users-matching": "Er zijn geen gebruikers gevonden die overeenkomen met '{{entity}}'.", + "user-required": "Gebruiker is vereist", + "activation-method": "Activeringsmethode", + "display-activation-link": "Activeringslink weergeven", + "send-activation-mail": "Activeringsmail versturen", + "activation-link": "Activeringslink voor gebruiker", + "activation-link-text": "Om de gebruiker te activeren gebruikt u de volgende activation link:", + "copy-activation-link": "Activeringslink kopiëren", + "activation-link-copied-message": "De activeringslink van de gebruiker is gekopieerd naar het klembord", + "details": "Details", + "login-as-tenant-admin": "Aanmelden als tenantbeheerder", + "login-as-customer-user": "Inloggen als klantgebruiker", + "select-group-to-add": "Selecteer doelgroep om geselecteerde gebruikers toe te voegen", + "select-group-to-move": "Selecteer de doelgroep om geselecteerde gebruikers te verplaatsen", + "remove-users-from-group": "Weet je zeker dat je { count, plural, =1 {1 user} andere {# users} } uit groep '{{entityGroup}}' wilt verwijderen?", + "group": "Groep gebruikers", + "list-of-groups": "{ count, plural, =1 {One user group} andere {List of # user groups} }", + "group-name-starts-with": "Gebruikersgroepen waarvan de naam begint met '{{prefix}}'", + "search": "Gebruikers zoeken", + "selected-users": "{ count, plural, =1 {1 user} andere {# users} } geselecteerd", + "disable-account": "Gebruikersaccount uitschakelen", + "enable-account": "Gebruikersaccount inschakelen", + "enable-account-message": "Gebruikersaccount is succesvol ingeschakeld!", + "disable-account-message": "Gebruikersaccount is met succes uitgeschakeld!", + "copyId": "Kopieer gebruikers-ID", + "idCopiedMessage": "Gebruikers-ID is gekopieerd naar klembord", + "user-list": "Lijst met gebruikers", + "user-list-required": "Gebruikerslijst is vereist" + }, + "value": { + "type": "Soort waarde", + "string": "Snaar", + "string-value": "Tekenreeks waarde", + "string-value-required": "Tekenreekswaarde is vereist", + "integer": "Geheel getal", + "integer-value": "Gehele waarde", + "integer-value-required": "Geheel getal is vereist", + "invalid-integer-value": "Ongeldige waarde van een geheel getal", + "double": "Dubbel", + "double-value": "Dubbele waarde", + "double-value-required": "Dubbele waarde is vereist", + "boolean": "Booleaans", + "boolean-value": "Booleaanse waarde", + "false": "Vals", + "true": "Waar", + "long": "Lang", + "json": "JSON", + "json-value": "JSON-waarde", + "json-value-invalid": "JSON-waarde heeft een ongeldige indeling", + "json-value-required": "JSON-waarde is vereist." + }, + "version-control": { + "version-control": "Versiebeheer", + "management": "Beheer van versiebeheer", + "search": "Versies zoeken", + "branch": "Tak", + "default": "Verstek", + "select-branch": "Selecteer filiaal", + "branch-required": "Filiaal is vereist", + "create-entity-version": "Entiteitsversie maken", + "version-name": "Versienaam", + "version-name-required": "Versienaam is vereist", + "author": "Auteur", + "export-relations": "Exportrelaties", + "export-attributes": "Attributen exporteren", + "export-credentials": "Inloggegevens exporteren", + "export-group-entities": "Groepsentiteiten exporteren", + "export-roles": "Rollen exporteren", + "entity-versions": "Versies van entiteiten", + "versions": "Versies", + "created-time": "Gecreëerde tijd", + "version-id": "Versie-ID", + "no-entity-versions-text": "Geen entiteitsversies gevonden", + "no-versions-text": "Geen versies gevonden", + "copy-full-version-id": "Kopieer de volledige versie-id", + "create-version": "Versie maken", + "creating-version": "Versie maken... Een ogenblik geduld", + "nothing-to-commit": "Geen wijzigingen om vast te leggen", + "restore-version": "Versie herstellen", + "restore-entity-from-version": "Entiteit herstellen vanaf versie '{{versionName}}'", + "restoring-entity-version": "Entiteitsversie herstellen... Een ogenblik geduld", + "load-relations": "Relaties met belasting", + "load-attributes": "Attributen laden", + "load-credentials": "Inloggegevens laden", + "load-group-entities": "Groepsentiteiten laden", + "load-roles": "Rollen laden", + "compare-with-current": "Vergelijk met de huidige", + "diff-entity-with-version": "Verschil met entiteitsversie '{{versionName}}'", + "previous-difference": "Vorig verschil", + "next-difference": "Volgende Verschil", + "current": "Actueel", + "differences": "{ count, plural, =1 {1 difference} andere {# differences} }", + "create-entities-version": "Versie van entiteiten maken", + "default-sync-strategy": "Standaard synchronisatiestrategie", + "sync-strategy-merge": "Fuseren", + "sync-strategy-overwrite": "Overschrijven", + "entities-to-export": "Te exporteren entiteiten", + "entities-to-restore": "Entiteiten die moeten worden hersteld", + "sync-strategy": "Synchronisatie strategie", + "all-entities": "Alle entiteiten", + "no-entities-to-export-prompt": "Geef entiteiten op die u wilt exporteren", + "no-entities-to-restore-prompt": "Geef entiteiten op die u wilt herstellen", + "add-entity-type": "Entiteitstype toevoegen", + "remove-all": "Alles verwijderen", + "version-create-result": "{ added, plural, =0 {No entities} =1 {1 entity} andere {# entities} } toegevoegd.
{ modified, plural, =0 {No entities} =1 {1 entity} andere {# entities} } gewijzigd.
{ removed, plural, =0 {No entities} =1 {1 entity} andere {# entities} } verwijderd.", + "remove-other-entities": "Andere entiteiten verwijderen", + "find-existing-entity-by-name": "Bestaande entiteit zoeken op naam", + "restore-entities-from-version": "Entiteiten herstellen vanaf versie '{{versionName}}'", + "restoring-entities-from-version": "Entiteiten herstellen... Een ogenblik geduld", + "no-entities-restored": "Geen entiteiten hersteld", + "created": "{{created}} gemaakt", + "updated": "{{updated}} geüpdatet", + "deleted": "{{deleted}} geschrapt", + "groups-created": "{ created, plural, =1 {1 group} andere {# groups} } gemaakt", + "groups-updated": "{ updated, plural, =1 {1 group} andere {# groups} } bijgewerkt", + "groups-deleted": "{ deleted, plural, =1 {1 group} andere {# groups} } verwijderd", + "remove-other-entities-confirm-text": "Opgelet! Hiermee worden alle huidige entiteiten
die niet aanwezig zijn in de versie die u wilt herstellen, permanent verwijderd.

Typ andere entiteiten verwijderen om te bevestigen.", + "auto-commit-to-branch": "Automatisch vastleggen op {{ branch }} filiaal", + "default-create-entity-version-name": "{{entityName}} update", + "sync-strategy-merge-hint": "Hiermee maakt of actualiseert u geselecteerde entiteiten in de opslagplaats. Alle andere opslagplaatsentiteiten worden niet gewijzigd.", + "sync-strategy-overwrite-hint": "Hiermee maakt of actualiseert u geselecteerde entiteiten in de opslagplaats. Alle andere opslagplaatsentiteiten worden verwijderd.", + "device-credentials-conflict": "Kan het device niet laden met externe id {{entityId}}
omdat dezelfde referenties al aanwezig zijn in de database voor een ander device.
Overweeg om de instelling voor laadreferenties in het herstelformulier uit te schakelen.", + "missing-referenced-entity": "Het laden van de {{sourceEntityTypeName}} met externe id {{sourceEntityId}}
omdat het verwijst naar ontbrekende {{targetEntityTypeName}} met id {{targetEntityId}}.", + "add-entity-groups": "Entiteitsgroepen toevoegen", + "entity-groups": "Entiteitsgroepen", + "integration-routing-key-conflict": "Kan de integratie met externe id niet laden {{entityId}}
vanwege dezelfde integratiesleutel is al aanwezig in de database voor een andere integratie.
Overweeg om de instelling voor het automatisch genereren van integratiesleutels in het herstelformulier in te schakelen.", + "auto-generate-integration-key": "Integratiesleutel automatisch genereren", + "runtime-failed": "Mislukt: {{message}}", + "auto-commit-settings-read-only-hint": "De functie voor automatisch vastleggen werkt niet met de ingeschakelde niet editeerbaar optie in de instellingen van de opslagplaats." + }, + "widget": { + "widget-library": "Widgets Bibliotheek", + "widget-bundle": "Widgets Bundel", + "all-bundles": "Alle bundels", + "select-widgets-bundle": "Selecteer widgetsbundel", + "management": "Beheer van widgets", + "editor": "Widget-editor", + "widget-type-not-found": "Probleem met het laden van de configuratie van de widget.
Waarschijnlijk geassocieerd\n Widgettype is verwijderd.", + "widget-type-load-error": "Widget is niet geladen vanwege de volgende fouten:", + "remove": "Widget verwijderen", + "edit": "Widget bewerken", + "remove-widget-title": "Weet je zeker dat je de widget '{{widgetTitle}}' wilt verwijderen?", + "remove-widget-text": "Na de bevestiging worden de widget en alle gerelateerde gegevens onherstelbaar.", + "timeseries": "Timeseries", + "search-data": "Zoekgegevens", + "no-data-found": "Geen gegevens gevonden", + "latest": "Meest recente waarden", + "rpc": "Controle widget", + "alarm": "Alarm widget", + "static": "Statische widget", + "select-widget-type": "Selecteer widgettype", + "missing-widget-title-error": "De titel van de widget moet worden opgegeven!", + "widget-saved": "Widget opgeslagen", + "unable-to-save-widget-error": "Kan widget niet opslaan! Widget heeft fouten!", + "save": "Widget opslaan", + "saveAs": "Widget opslaan als", + "save-widget-type-as": "Widgettype opslaan als", + "save-widget-type-as-text": "Voer een nieuwe widgettitel in en/of selecteer de bundel met doelwidgets", + "toggle-fullscreen": "Volledig scherm in- en uitschakelen", + "run": "Widget uitvoeren", + "title": "Titel van de widget", + "title-required": "De titel van de widget is vereist.", + "type": "Type widget", + "resources": "Weg", + "resource-url": "JavaScript/CSS-URL", + "resource-is-module": "Is-module", + "remove-resource": "Bron verwijderen", + "add-resource": "Bron toevoegen", + "html": ".HTML", + "tidy": "Ordelijk", + "css": ".CSS", + "settings-schema": "Schema Instellingen", + "datakey-settings-schema": "Schema voor instellingen voor gegevenssleutels", + "latest-datakey-settings-schema": "Schema met meest recente instellingen voor gegevenssleutels", + "widget-settings": "Widget instellingen", + "description": "Omschrijving: __________", + "image-preview": "Voorbeeld van afbeelding", + "settings-form-selector": "Formulierkiezer voor instellingen", + "data-key-settings-form-selector": "Formulierkiezer voor instellingen voor gegevenssleutels", + "latest-data-key-settings-form-selector": "Formulierkiezer voor de meest recente gegevenssleutelinstellingen", + "javascript": "Javascript", + "js": ".JS", + "remove-widget-type-title": "Weet je zeker dat je het widgettype '{{widgetName}}' wilt verwijderen?", + "remove-widget-type-text": "Na de bevestiging worden het type widget en alle gerelateerde gegevens onherstelbaar.", + "remove-widget-type": "Widgettype verwijderen", + "add-widget-type": "Nieuw widgettype toevoegen", + "widget-type-load-failed-error": "Kan widgettype niet laden!", + "widget-template-load-failed-error": "Kan widgetsjabloon niet laden!", + "add": "Widget toevoegen", + "undo": "Wijzigingen in widgets ongedaan maken", + "export": "Widget exporteren", + "export-data": "Widgetgegevens exporteren", + "export-to-csv": "Exporteer gegevens naar CSV...", + "export-to-excel": "Gegevens exporteren naar XLS...", + "export-to-excel-xlsx": "Gegevens exporteren naar XLSX...", + "no-data": "Geen gegevens om weer te geven op widget", + "data-overflow": "Widget geeft {{count}} van {{total}} entiteiten weer", + "alarm-data-overflow": "Widget geeft alarmen weer voor {{allowedEntities}} (maximaal toegestane) entiteiten van {{totalEntities}} entiteiten", + "search": "Widget zoeken", + "filter": "Type widgetfilter", + "loading-widgets": "Widgets worden geladen...", + "widget-template-error": "Ongeldige HTML-sjabloon voor widgets." + }, + "widget-action": { + "header-button": "Knop koptekst widget", + "open-dashboard-state": "Navigeer naar de nieuwe dashboardstatus", + "update-dashboard-state": "Huidige dashboardstatus bijwerken", + "open-dashboard": "Navigeer naar ander dashboard", + "custom": "Aangepaste actie", + "custom-pretty": "Aangepaste actie (met HTML-sjabloon)", + "custom-pretty-error-title": "Fout in aangepast dialoogvenster", + "custom-pretty-template-error": "Ongeldige sjabloon voor een aangepast dialoogvenster.", + "custom-pretty-controller-error": "Er is een fout opgetreden bij het evalueren van de aangepaste dialoogvensterfunctie.", + "mobile-action": "Mobiele actie", + "target-dashboard-state": "Status van doeldashboard", + "target-dashboard-state-required": "De status van het doeldashboard is vereist", + "set-entity-from-widget": "Entiteit instellen vanuit widget", + "target-dashboard": "Dashboard voor doelen", + "open-right-layout": "Dashboardlay-out rechts openen (mobiele weergave)", + "state-display-type": "Optie voor weergave van de status van het dashboard", + "open-normal": "Normaal", + "open-in-separate-dialog": "Openen in apart dialoogvenster", + "open-in-popover": "Openen in popover", + "dialog-title": "Titel van het dialoogvenster", + "dialog-hide-dashboard-toolbar": "Dashboardwerkbalk verbergen in dialoogvenster", + "dialog-width": "Dialoogvensterbreedte in procenten ten opzichte van viewportbreedte", + "dialog-height": "Dialoogvensterhoogte in procenten ten opzichte van viewporthoogte", + "dialog-size-range-error": "De procentuele waarde van de dialooggrootte moet tussen 1 en 100 liggen.", + "popover-preferred-placement": "Voorkeursplaatsing van de popover", + "popover-placement-top": "Boven", + "popover-placement-topLeft": "Linksboven", + "popover-placement-topRight": "Rechtsboven", + "popover-placement-right": "Rechts", + "popover-placement-rightTop": "Rechtsboven", + "popover-placement-rightBottom": "Rechtsonder", + "popover-placement-bottom": "Bodem", + "popover-placement-bottomLeft": "Linksonder", + "popover-placement-bottomRight": "Rechtsonder", + "popover-placement-left": "Links", + "popover-placement-leftTop": "Links boven", + "popover-placement-leftBottom": "Links onder", + "popover-hide-on-click-outside": "Popover verbergen bij klikken aan de buitenkant", + "popover-hide-dashboard-toolbar": "Dashboardwerkbalk verbergen in pop-over", + "popover-width": "Popover-breedte in browsereenheden (bijv. 100px, 25vw)", + "popover-height": "Popover-hoogte in browsereenheden (bijv. 100px, 25vh)", + "popover-style": "Popover-stijl", + "open-new-browser-tab": "Openen in een nieuw browsertabblad", + "mobile": { + "action-type": "Mobiel actietype", + "action-type-required": "Mobiel actietype is vereist", + "take-picture-from-gallery": "Maak een foto uit de galerij", + "take-photo": "Maak een foto", + "map-direction": "Routebeschrijving openen", + "map-location": "Locatie op de kaart openen", + "scan-qr-code": "QR-code scannen", + "make-phone-call": "Telefoneren", + "get-location": "Telefoonlocatie ophalen", + "take-screenshot": "Screenshot maken" + } + }, + "widgets-bundle": { + "current": "Huidige bundel", + "widgets-bundles": "Widgets Bundels", + "add": "Widgets-bundel toevoegen", + "delete": "Widgetsbundel verwijderen", + "title": "Titel", + "title-required": "Titel is vereist.", + "title-max-length": "Titel moet kleiner zijn dan 256 tekens", + "description": "Omschrijving: __________", + "image-preview": "Voorbeeld van afbeelding", + "add-widgets-bundle-text": "Nieuwe widgetsbundel toevoegen", + "no-widgets-bundles-text": "Geen widgetbundels gevonden", + "empty": "Widgets-bundel is leeg", + "details": "Details", + "widgets-bundle-details": "Details van de widgetsbundel", + "delete-widgets-bundle-title": "Weet je zeker dat je de widgets bundel '{{widgetsBundleTitle}}' wilt verwijderen?", + "delete-widgets-bundle-text": "Opgelet, na de bevestiging worden de widgetbundel en alle gerelateerde gegevens onherstelbaar.", + "delete-widgets-bundles-title": "Weet u zeker dat u { count, plural, =1 {1 widgets bundle} andere {# widgets bundles} } wilt verwijderen?", + "delete-widgets-bundles-action-title": "Verwijder { count, plural, =1 {1 widgets bundle} andere {# widgets bundles} }", + "delete-widgets-bundles-text": "Opgelet, na de bevestiging worden alle geselecteerde widgetbundels verwijderd en worden alle gerelateerde gegevens onherstelbaar.", + "no-widgets-bundles-matching": "Er zijn geen widgetbundels gevonden die overeenkomen met '{{widgetsBundle}}'.", + "widgets-bundle-required": "Widgets-bundel is vereist.", + "system": "Systeem", + "import": "Widgets importeren bundel", + "export": "Widgets exporteren bundel", + "export-failed-error": "Kan widgetbundel niet exporteren: {{error}}", + "create-new-widgets-bundle": "Nieuwe widgetsbundel maken", + "widgets-bundle-file": "Widgets bundel bestand", + "invalid-widgets-bundle-file-error": "Kan widgetbundel niet importeren: Ongeldige widgetbundel gegevensstructuur.", + "search": "Widgetbundels zoeken", + "selected-widgets-bundles": "{ count, plural, =1 {1 widgets bundle} andere {# widgets bundles} } geselecteerd", + "open-widgets-bundle": "Widgets-bundel openen", + "loading-widgets-bundles": "Widgets bundels worden geladen..." + }, + "widget-config": { + "data": "Gegevens", + "settings": "Instellingen", + "advanced": "Geavanceerd", + "title": "Titel", + "title-tooltip": "Titel Tooltip", + "general-settings": "Algemene instellingen", + "display-title": "Titel van widget weergeven", + "drop-shadow": "Schaduw verwijderen", + "enable-fullscreen": "Volledig scherm inschakelen", + "enable-data-export": "Gegevensexport inschakelen", + "background-color": "Achtergrondkleur", + "text-color": "Tekstkleur", + "padding": "Spacties", + "margin": "Marge", + "widget-style": "Widget stijl", + "widget-css": "Widget CSS", + "title-style": "Titel stijl", + "mobile-mode-settings": "Mobiele modus", + "order": "Bevelen", + "height": "Hoogte", + "mobile-hide": "Widget verbergen in mobiele modus", + "desktop-hide": "Widget verbergen in desktopmodus", + "units": "Speciaal symbool om naast waarde weer te geven", + "decimals": "Aantal cijfers na drijvende komma", + "timewindow": "Tijdvenster", + "use-dashboard-timewindow": "Dashboardtijdvenster gebruiken", + "display-timewindow": "Tijdvenster weergeven", + "legend": "Legende", + "display-legend": "Legende weergeven", + "datasources": "Gegevensbronnen", + "maximum-datasources": "Maximaal { count, plural, =1 {1 datasource is allowed.} andere {# datasources are allowed} }", + "timeseries-key-error": "Er moet ten minste één tijdreeksgegevenssleutel worden opgegeven", + "datasource-type": "Type", + "datasource-parameters": "Parameters", + "remove-datasource": "Gegevensbron verwijderen", + "add-datasource": "Gegevensbron toevoegen", + "target-device": "Doelapparaat", + "alarm-source": "Alarmbron", + "actions": "Acties", + "action": "Actie", + "add-action": "Actie toevoegen", + "search-actions": "Zoekacties", + "no-actions-text": "Geen acties gevonden", + "action-source": "Bron van actie", + "action-source-required": "Actiebron is vereist.", + "action-name": "Naam", + "action-name-required": "De naam van de actie is vereist.", + "action-name-not-unique": "Er bestaat al een andere actie met dezelfde naam.
De naam van de actie moet uniek zijn binnen dezelfde actiebron.", + "action-icon": "Pictogram", + "show-hide-action-using-function": "Actie tonen/verbergen met behulp van functie", + "action-type": "Type", + "action-type-required": "Actietype is vereist.", + "edit-action": "Actie bewerken", + "delete-action": "Actie verwijderen", + "delete-action-title": "Widgetactie verwijderen", + "delete-action-text": "Weet je zeker dat je de actie voor het verwijderen van widgets met de naam '{{actionName}}' wilt?", + "title-icon": "Titel icoon", + "display-icon": "Titelpictogram weergeven", + "icon-color": "Kleur van het pictogram", + "icon-size": "Grootte van het pictogram", + "advanced-settings": "Geavanceerde instellingen", + "data-settings": "Gegevensinstellingen", + "no-data-display-message": "Alternatief bericht 'Geen gegevens om weer te geven'", + "data-page-size": "Maximum aantal entiteiten per gegevensbron", + "settings-component-not-found": "Instellingen formuliercomponent niet gevonden voor selector '{{selector}}'" + }, + "widget-type": { + "import": "Type widget importeren", + "export": "Widgettype exporteren", + "export-failed-error": "Kan widgettype niet exporteren: {{error}}", + "create-new-widget-type": "Nieuw widgettype maken", + "widget-type-file": "Bestand van het type widget", + "invalid-widget-type-file-error": "Kan widgettype niet importeren: Ongeldige gegevensstructuur van het widgettype." + }, + "self-registration": { + "self-registration": "Zelf registratie", + "self-registration-url": "URL voor zelfregistratie", + "captcha-version": "reCAPTCHA-versie", + "captcha-action": "naam van de actie van het reCAPTCHA-logboek", + "captcha-site-key": "reCAPTCHA-sitesleutel", + "captcha-secret-key": "reCAPTCHA geheime sleutel", + "notification-email": "E-mail met melding", + "privacy-policy-text": "Tekst van het privacybeleid", + "terms-of-use-text": "Tekst van de gebruiksvoorwaarden", + "text-message-page": "Sms voor aanmeldingspagina", + "enable-mobile-self-registration": "Zelfregistratie inschakelen vanuit de mobiele applicatie", + "mobile-self-registration-title": "Instellingen voor zelfregistratie van mobiele applicaties", + "mobile-package": "Applicatie pakket", + "mobile-package-placeholder": "Vb.: my.example.app", + "mobile-package-hint": "Voor Android: uw eigen unieke applicatie-ID. Voor iOS: ID van productbundel.", + "mobile-package-required": "Aanvraagpakket is vereist", + "mobile-app-secret": "Geheim van de toepassing", + "invalid-mobile-app-secret": "Het geheim van de toepassing mag alleen alfanumerieke tekens bevatten en moet tussen de 16 en 2048 tekens lang zijn.", + "copy-mobile-app-secret": "Toepassingsgeheim kopiëren", + "mobile-app-scheme": "URL-schema van toepassing", + "mobile-app-scheme-placeholder": "Vb.: maatwerk", + "mobile-app-scheme-required": "Het URL-schema van de aanvraag is vereist", + "mobile-app-host": "Hostnaam van applicatie-URL", + "mobile-app-host-placeholder": "Bijv.: mijn.app.host", + "mobile-app-host-required": "Hostnaam van toepassings-URL is vereist", + "show-privacy-policy": "Toon Privacybeleid", + "show-terms-of-use": "Toon Gebruiksvoorwaarden", + "domain-settings": "Domein instellingen", + "general-settings": "Algemene instellingen" + }, + "solution-template": { + "solution-template": "Oplossing sjabloon", + "solution-templates": "Sjablonen voor oplossingen", + "management": "Oplossingssjablonen beheren", + "details": "Details", + "install": "Installeren", + "level": "Niveau", + "install-title": "Oplossingssjabloon succesvol geïnstalleerd", + "install-failed-title": "Installatie van oplossingssjabloon mislukt", + "instructions": "Aanwijzingen", + "goto-main-dashboard": "Ga naar het hoofddashboard", + "delete": "Verwijderen", + "delete-solution-title": "Weet u zeker dat u de oplossing '{{solutionTitle}}' wilt verwijderen?", + "delete-solution-text": "Opgelet, na de bevestiging worden de oplossing en alle gerelateerde gegevens onherstelbaar.", + "installing": "Oplossingssjabloon installeren..." + }, + "markdown": { + "edit": "Bewerken", + "preview": "Voorbeeld", + "copy-code": "Klik om te kopiëren", + "copied": "Gekopieerd!" + }, + "white-labeling": { + "white-labeling": "White labelling", + "white-labeling-general": "Algemene White Labeling", + "login-white-labeling": "Inloggen White Labeling", + "general": "Algemeen", + "login": "Inloggen", + "preview": "Voorbeeld", + "app-title": "Titel van de toepassing", + "favicon": "Website icoon", + "favicon-description": "*.ico, *.gif of *.png afbeelding met een maximale grootte {{kbSize}} KBytes.", + "favicon-size-error": "De afbeelding van de website is te groot. De maximaal toegestane afbeeldingsgrootte van de website {{kbSize}} KBytes.", + "favicon-type-error": "Ongeldige bestandsindeling voor websiteafbeeldingen. Alleen ICO-, GIF- of PNG-afbeeldingen worden geaccepteerd.", + "drop-favicon-image": "Zet een afbeelding van een websitepictogram neer of klik om een bestand te selecteren om te uploaden.", + "drop-favicon-image-or": "Sleep een afbeelding van een websitepictogram of", + "no-favicon-image": "Geen pictogram geselecteerd", + "logo": "Logo", + "logo-description": "Elke afbeelding met een maximale grootte {{kbSize}} KBytes.", + "logo-size-error": "De afbeelding van het logo is te groot. De maximaal toegestane afbeeldingsgrootte van het logo {{kbSize}} KBytes.", + "logo-type-error": "Ongeldig bestandsformaat voor logo's. Alleen afbeeldingen worden geaccepteerd.", + "drop-logo-image": "Zet een logo-afbeelding neer of klik om een bestand te selecteren om te uploaden.", + "drop-logo-image-or": "Een logo-afbeelding slepen en neerzetten of", + "no-logo-image": "Geen logo geselecteerd", + "logo-height": "Hoogte logo, px", + "primary-palette": "Primair palet", + "accent-palette": "Accent palet", + "customize-palette": "Aanpassen", + "advanced-css": "Geavanceerde CSS", + "edit-palette": "Palet bewerken", + "save-palette": "Palet opslaan", + "primary-background": "Primaire achtergrond", + "secondary-background": "Secundaire achtergrond", + "hue1": "TINT 1", + "hue2": "HUE 2", + "hue3": "HUE 3", + "page-background-color": "Achtergrondkleur van de pagina", + "dark-foreground": "Donkere voorgrond", + "domain-name": "Domeinnaam", + "base-url": "Basis-URL", + "base-url-required": "Basis-URL is vereist.", + "prohibit-different-url": "Verbieden om de hostnaam te gebruiken in de headers van de clientaanvraag", + "prohibit-different-url-hint": "Deze instelling moet zijn ingeschakeld voor productieomgevingen. Kan beveiligingsproblemen veroorzaken wanneer deze is uitgeschakeld", + "help-link-base-url": "Help-links basis-url", + "ui-help-base-url": "Help-basis-URL van de gebruikersinterface", + "enable-help-links": "Helpkoppelingen inschakelen", + "error-verification-url": "Een domeinnaam mag geen symbolen '/' en ':' bevatten. Voorbeeld: thingsboard.io", + "show-platform-name-version": "Platformnaam en -versie weergeven", + "platform-name": "Naam van het platform", + "platform-version": "Platform versie", + "version-mask": "{{name}} v.{{version}}", + "position": { + "label": "Platformnaam en versiepositie", + "under-logo": "Onder het logo", + "bottom": "Onderaan het inlogformulier" + } + }, + "widgets": { + "chart": { + "common-settings": "Algemene instellingen", + "enable-stacking-mode": "Stapelmodus inschakelen", + "line-shadow-size": "Grootte van de lijnschaduw", + "display-smooth-lines": "Vloeiende (gebogen) lijnen weergeven", + "default-bar-width": "Standaardbalkbreedte voor niet-geaggregeerde gegevens (milliseconden)", + "bar-alignment": "Staaf uitlijning", + "bar-alignment-left": "Links", + "bar-alignment-right": "Rechts", + "bar-alignment-center": "Middelpunt", + "default-font-size": "Standaard lettergrootte", + "default-font-color": "Standaard tekstkleur", + "thresholds-line-width": "Standaardregeldikte voor alle drempels", + "tooltip-settings": "Tooltip-instellingen", + "show-tooltip": "Tooltip weergeven", + "hover-individual-points": "Beweeg de muisaanwijzer over individuele punten", + "show-cumulative-values": "Cumulatieve waarden weergeven in stapelmodus", + "hide-zero-false-values": "Nul/onwaar-waarden verbergen in knopinfo", + "tooltip-value-format-function": "Functie voor het opmaken van knoppuntwaarden", + "grid-settings": "Raster instellingen", + "show-vertical-lines": "Verticale lijnen weergeven", + "show-horizontal-lines": "Horizontale lijnen weergeven", + "grid-outline-border-width": "Rasteromtrek/randbreedte (px)", + "primary-color": "Primaire kleur", + "background-color": "Achtergrondkleur", + "ticks-color": "De kleur van de teken", + "xaxis-settings": "Instellingen van de X-as", + "axis-title": "Titel van de as", + "xaxis-tick-labels-settings": "Instellingen voor labels voor tikken op de X-as", + "show-tick-labels": "Labels voor asvinkjes weergeven", + "yaxis-settings": "Y-as instellingen", + "min-scale-value": "Minimumwaarde op de weegschaal", + "max-scale-value": "Maximale waarde op de weegschaal", + "yaxis-tick-labels-settings": "Instellingen voor labels voor Y-as tikken", + "tick-step-size": "Stapgrootte tussen teken", + "number-of-decimals": "Het aantal decimalen dat moet worden weergegeven", + "ticks-formatter-function": "Teken formatter-functie", + "comparison-settings": "Vergelijkingsinstellingen", + "enable-comparison": "Vergelijking inschakelen", + "time-for-comparison": "Vergelijkingsperiode", + "time-for-comparison-previous-interval": "Vorig interval (standaard)", + "time-for-comparison-days": "Dag geleden", + "time-for-comparison-weeks": "Week geleden", + "time-for-comparison-months": "Maand geleden", + "time-for-comparison-years": "Jaar geleden", + "time-for-comparison-custom-interval": "Aangepast interval", + "custom-interval-value": "Aangepaste intervalwaarde (ms)", + "comparison-x-axis-settings": "Vergelijking X-asinstellingen", + "axis-position": "Positie van de as", + "axis-position-top": "Top (standaard)", + "axis-position-bottom": "Bodem", + "custom-legend-settings": "Aangepaste legende-instellingen", + "enable-custom-legend": "Aangepaste legende inschakelen (hiermee kunt u attribuut-/tijdreekswaarden gebruiken in sleutellabels)", + "key-name": "Naam van de sleutel", + "key-name-required": "Sleutelnaam is vereist", + "key-type": "Type sleutel", + "key-type-attribute": "Attribuut", + "key-type-timeseries": "Timeseries", + "label-keys-list": "Toetsenlijst voor labels", + "no-label-keys": "Geen sleutels geconfigureerd", + "add-label-key": "Nieuwe sleutel toevoegen", + "line-width": "Lijndikte", + "color": "Kleur", + "data-is-hidden-by-default": "Gegevens zijn standaard verborgen", + "disable-data-hiding": "Schakel het verbergen van gegevens uit", + "remove-from-legend": "Datakey verwijderen uit legende", + "exclude-from-stacking": "Uitsluiten van stapelen (beschikbaar in de modus \"Stapelen\")", + "line-settings": "Lijn instellingen", + "show-line": "Toon lijn", + "fill-line": "Lijn vullen", + "fill-line-opacity": "Dekking vullen", + "points-settings": "Punten instellingen", + "show-points": "Toon punten", + "points-line-width": "Lijndikte van punten", + "points-radius": "Straal van punten", + "point-shape": "Puntvorm", + "point-shape-circle": "Cirkel", + "point-shape-cross": "Kruis", + "point-shape-diamond": "Diamant", + "point-shape-square": "Vierkant", + "point-shape-triangle": "Driehoek", + "point-shape-custom": "Aangepaste functie", + "point-shape-draw-function": "Tekenfunctie voor puntvorm", + "show-separate-axis": "Afzonderlijke as weergeven", + "axis-position-left": "Links a-x positie", + "axis-position-right": "Rechts a-x positie", + "thresholds": "Drempels", + "no-thresholds": "Geen drempels geconfigureerd", + "add-threshold": "Nieuwe drempel toevoegen", + "show-values-for-comparison": "Historische waarden weergeven ter vergelijking", + "comparison-values-label": "Label met historische waarden", + "threshold-settings": "Drempelwaarde instellingen", + "use-as-threshold": "Sleutelwaarde gebruiken als drempelwaarde", + "threshold-line-width": "Lijnbreedte drempel", + "threshold-color": "Drempel kleur", + "common-pie-settings": "Algemene taartinstellingen", + "radius": "Radius", + "inner-radius": "Binnen radius", + "tilt": "Kantelen", + "stroke-settings": "Lijn instellingen", + "width-pixels": "Breedte (pixels)", + "show-labels": "Labels weergeven", + "animation-settings": "Animatie-instellingen", + "animated-pie": "Pie animatie inschakelen (experimenteel)", + "border-settings": "Randinstellingen", + "border-width": "Breedte van de edge", + "border-color": "Kleur van de edge", + "legend-settings": "Legende-instellingen", + "display-legend": "Legende weergeven", + "labels-font-color": "Letterkleur van labels" + }, + "dashboard-state": { + "dashboard-state-settings": "Instellingen voor de status van het dashboard", + "dashboard-state": "Status-id van dashboard", + "autofill-state-layout": "Standaard lay-outhoogte voor automatisch aanvullen", + "default-margin": "Standaard widgets marge", + "default-background-color": "Standaard achtergrondkleur", + "sync-parent-state-params": "Statusparameters synchroniseren met het bovenliggende dashboard" + }, + "date-range-navigator": { + "date-range-picker-settings": "Instellingen voor het kiezen van het datumbereik", + "hide-date-range-picker": "Datumbereik kiezer verbergen", + "picker-one-panel": "Datumbereik kiezer één paneel", + "picker-auto-confirm": "Datumbereik kiezer automatisch bevestigen", + "picker-show-template": "Datumbereik kiezer toon sjabloon", + "first-day-of-week": "Eerste dag van de week", + "interval-settings": "Interval-instellingen", + "hide-interval": "Interval verbergen", + "initial-interval": "Initieel interval", + "interval-hour": "Uur", + "interval-day": "Dag", + "interval-week": "Week", + "interval-two-weeks": "2 weken", + "interval-month": "Maand", + "interval-three-months": "3 maanden", + "interval-six-months": "6 maanden", + "step-settings": "Stap instellingen", + "hide-step-size": "Stapgrootte verbergen", + "initial-step-size": "Grootte van de eerste stap", + "hide-labels": "Labels verbergen", + "use-session-storage": "Sessieopslag gebruiken", + "localizationMap": { + "Sun": "Zon", + "Mon": "Ma", + "Tue": "Di", + "Wed": "Woe", + "Thu": "Do", + "Fri": "Vr", + "Sat": "Zaterdag", + "Jan": "Jan", + "Feb": "Februari", + "Mar": "Mar", + "Apr": "Apr", + "May": "Mei", + "Jun": "Jun", + "Jul": "Jul", + "Aug": "Aug", + "Sep": "Sep", + "Oct": "Oct", + "Nov": "Nov", + "Dec": "Dec", + "January": "Januari", + "February": "Februari", + "March": "Maart", + "April": "April", + "June": "Juni", + "July": "Juli", + "August": "Augustus", + "September": "September", + "October": "Oktober", + "November": "November", + "December": "December", + "Custom Date Range": "Aangepast datumbereik", + "Date Range Template": "Datumbereik sjabloon", + "Today": "Vandaag", + "Yesterday": "Gisteren", + "This Week": "Deze week", + "Last Week": "Vorige week", + "This Month": "Deze maand", + "Last Month": "Afgelopen maand", + "Year": "Jaar", + "This Year": "Dit jaar", + "Last Year": "Vorig jaar", + "Date picker": "Datumkiezer", + "Hour": "Uur", + "Day": "Dag", + "Week": "Week", + "2 weeks": "2 weken", + "Month": "Maand", + "3 months": "3 maanden", + "6 months": "6 maanden", + "Custom interval": "Aangepast interval", + "Interval": "Interval", + "Step size": "Stap grootte", + "Ok": "OK" + } + }, + "entities-hierarchy": { + "hierarchy-data-settings": "Instellingen voor hiërarchische gegevens", + "relations-query-function": "Queryfunctie voor knooppuntrelaties", + "has-children-function": "Knoop heeft een kinderfunctie", + "node-state-settings": "Instellingen voor knooppuntstatus", + "node-opened-function": "Standaard functie voor het openen van rule nodes", + "node-disabled-function": "Knooppuntuitgeschakelde functie", + "display-settings": "Weergave-instellingen", + "node-icon-function": "Knooppuntpictogram functie", + "node-text-function": "Knooppunttekstfunctie", + "sort-settings": "Sorteer instellingen", + "nodes-sort-function": "Sorteerfunctie voor rule nodes" + }, + "edge": { + "display-default-title": "Standaardtitel weergeven" + }, + "gateway": { + "general-settings": "Algemene instellingen", + "widget-title": "Titel van de widget", + "default-archive-file-name": "Standaard bestandsnaam voor archief", + "device-type-for-new-gateway": "Device type voor nieuwe gateway", + "messages-settings": "Instellingen voor Berichten", + "save-config-success-message": "Sms-bericht over succesvol opgeslagen gatewayconfiguratie", + "device-name-exists-message": "Sms-bericht wanneer het device met de ingevoerde naam al bestaat", + "gateway-title": "Gateway-formulier", + "read-only": "Alleen-lezen", + "events-title": "Titel van het formulier voor gateway events", + "events-filter": "Events filter", + "event-key-contains": "Events sleutel bevat..." + }, + "gauge": { + "default-color": "Standaard kleur", + "radial-gauge-settings": "Instellingen radiale meter", + "ticks-settings": "Vink instellingen aan", + "min-value": "Minimumwaarde", + "max-value": "Maximale waarde", + "start-ticks-angle": "Start tikt hoek", + "ticks-angle": "De hoek van teken", + "major-ticks-count": "Aantal belangrijke teken", + "major-ticks-color": "De belangrijkste kleur van teken", + "minor-ticks-count": "Kleine vinkjes tellen", + "minor-ticks-color": "Kleine vinkjes kleur", + "tick-numbers-font": "Tick nummers lettertype", + "unit-title-settings": "Instellingen voor eenheidstitels", + "show-unit-title": "Titel van eenheid weergeven", + "unit-title": "Titel van de eenheid", + "title-font": "Lettertype van titeltekst", + "units-settings": "Instellingen van de eenheden", + "units-font": "Eenheden tekstlettertype", + "value-box-settings": "Instellingen voor het vak Waarde", + "show-value-box": "Vak Waarde weergeven", + "value-int": "Cijfers tellen mee voor het gehele deel van de waarde", + "value-font": "Waarde tekstlettertype", + "value-box-rect-stroke-color": "Voordeelvak rechthoekige lijnkleur", + "value-box-rect-stroke-color-end": "Voordeelvak rechthoekige lijnkleur - eindverloop", + "value-box-background-color": "Achtergrondkleur van het waardevak", + "value-box-shadow-color": "Waarde vak schaduw kleur", + "plate-settings": "Plaat instellingen", + "show-plate-border": "Plaatrand weergeven", + "plate-color": "Kleur van de plaat", + "needle-settings": "Naald instellingen", + "needle-circle-size": "De grootte van de naaldcirkel", + "needle-color": "Kleur van de naald", + "needle-color-end": "Naaldkleur - eindverloop", + "needle-color-shadow-up": "Bovenste helft van de kleur van de naaldschaduw", + "needle-color-shadow-down": "Kleur slagschaduw naald", + "highlights-settings": "Instellingen voor markeringen", + "highlights-width": "Breedte van hoogtepunten", + "highlights": "Hoogtepunten", + "highlight-from": "Van", + "highlight-to": "Aan", + "highlight-color": "Kleur", + "no-highlights": "Geen markeringen geconfigureerd", + "add-highlight": "Markering toevoegen", + "animation-settings": "Animatie-instellingen", + "enable-animation": "Animatie inschakelen", + "animation-duration": "Duur van de animatie", + "animation-rule": "Animatie regel", + "animation-linear": "Lineair", + "animation-quad": "Quad", + "animation-quint": "Quint", + "animation-cycle": "Cyclus", + "animation-bounce": "Stuiteren", + "animation-elastic": "Elastisch", + "animation-dequad": "Splitsen", + "animation-dequint": "Afscheid nemen", + "animation-decycle": "Uitzetten", + "animation-debounce": "Debouncen", + "animation-delastic": "Delastisch", + "linear-gauge-settings": "Lineaire meetinstellingen", + "bar-stroke-width": "Lijnbreedte van de staaf", + "bar-stroke-color": "Kleur balklijn", + "bar-background-color": "Achtergrondkleur van de meterbalk", + "bar-background-color-end": "Achtergrondkleur van de balk - eindverloop", + "progress-bar-color": "Kleur voortgangsbalk", + "progress-bar-color-end": "Kleur voortgangsbalk - eindverloop", + "major-ticks-names": "Belangrijke namen van teken", + "show-stroke-ticks": "Toon teken beroerte", + "major-ticks-font": "Het belangrijkste teken lettertype", + "border-color": "Kleur van de edge", + "border-width": "Breedte van de edge", + "needle-circle-color": "De cirkelkleur van de naald", + "animation-target": "Animatie doel", + "animation-target-needle": "Naald", + "animation-target-plate": "Bord", + "common-settings": "Gemeenschappelijke meterinstellingen", + "gauge-type": "Type meter", + "gauge-type-arc": "Boog", + "gauge-type-donut": "Donut", + "gauge-type-horizontal-bar": "Rekstok", + "gauge-type-vertical-bar": "Verticale balk", + "donut-start-angle": "Hoek om mee te beginnen", + "bar-settings": "Meetbalk-instellingen", + "relative-bar-width": "Relatieve staafbreedte", + "neon-glow-brightness": "Neon glow effect helderheid, (0-100), 0 - effect uitschakelen", + "stripes-thickness": "Dikte van de strepen, 0 - geen strepen", + "rounded-line-cap": "Afgeronde lijndop weergeven", + "bar-color-settings": "Kleurinstellingen voor de balk", + "use-precise-level-color-values": "Gebruik nauwkeurige kleurniveaus", + "bar-colors": "Balkkleuren, van onder naar boven", + "color": "Kleur", + "no-bar-colors": "Geen balkkleuren geconfigureerd", + "add-bar-color": "Staafkleur toevoegen", + "from": "Van", + "to": "Aan", + "fixed-level-colors": "Staafkleuren met behulp van grenswaarden", + "gauge-title-settings": "Titelinstellingen meten", + "show-gauge-title": "Metertitel weergeven", + "gauge-title": "Titel van de meter", + "gauge-title-font": "Lettertype van de metertitel", + "unit-title-and-timestamp-settings": "Instellingen voor titel en tijdstempel van de eenheid", + "show-timestamp": "Tijdstempel van de waarde weergeven", + "timestamp-format": "Indeling tijdstempel", + "label-font": "Lettertype van label dat onder waarde wordt weergegeven", + "value-settings": "Waarde-instellingen", + "show-value": "Waardetekst weergeven", + "min-max-settings": "Instellingen voor minimale/maximale labels", + "show-min-max": "Min- en max-waarden weergeven", + "min-max-font": "Lettertype van minimum- en maximumlabels", + "show-ticks": "Toon teken", + "tick-width": "Tikbreedte", + "tick-color": "Vink kleur aan", + "tick-values": "Vink waarden aan", + "no-tick-values": "Geen vinkjes geconfigureerd", + "add-tick-value": "Tick waarde toevoegen" + }, + "gpio": { + "pin": "Speld", + "label": "Etiket", + "row": "Rijen", + "column": "Kolom", + "color": "Kleur", + "panel-settings": "Paneel instellingen", + "background-color": "Achtergrondkleur", + "gpio-switches": "GPIO-schakelaars", + "no-gpio-switches": "Geen GPIO-schakelaars geconfigureerd", + "add-gpio-switch": "GPIO-schakelaar toevoegen", + "gpio-status-request": "GPIO-statusaanvraag", + "method-name": "Naam van de methode", + "method-body": "Methode lichaam", + "gpio-status-change-request": "Aanvraag voor wijziging van GPIO-status", + "parse-gpio-status-function": "GPIO-statusfunctie parseren", + "gpio-leds": "GPIO-leds", + "no-gpio-leds": "Geen GPIO-leds geconfigureerd", + "add-gpio-led": "GPIO-led toevoegen" + }, + "html-card": { + "html": ".HTML", + "css": ".CSS" + }, + "input-widgets": { + "attribute-not-allowed": "Attribuutparameter kan niet worden gebruikt in deze widget", + "blocked-location": "Geolocatie is geblokkeerd in uw browser", + "claim-device": "Device claimen", + "claim-failed": "Kan het device niet claimen!", + "claim-not-found": "Device niet gevonden!", + "claim-successful": "Device is succesvol geclaimd!", + "date": "Datum", + "device-name": "Naam van het device", + "device-name-required": "Device naam is vereist", + "discard-changes": "Wijzigingen negeren", + "entity-attribute-required": "Entiteitskenmerk is vereist", + "entity-coordinate-required": "Beide velden, lengte- en breedtegraad, zijn verplicht", + "entity-timeseries-required": "Timeseries van entiteiten zijn vereist", + "get-location": "Huidige locatie ophalen", + "invalid-date": "Ongeldige datum", + "latitude": "Breedtegraad", + "longitude": "Lengtegraad", + "min-value-error": "Minimale waarde is {{value}}", + "max-value-error": "Maximale waarde is {{value}}", + "not-allowed-entity": "Geselecteerde entiteit kan geen gedeelde attributen hebben", + "no-attribute-selected": "Er is geen attribuut geselecteerd", + "no-datakey-selected": "Er is geen datakey geselecteerd", + "no-coordinate-specified": "Datakey voor lengte- en breedtegraad is niet opgegeven", + "no-entity-selected": "Geen entiteit geselecteerd", + "no-image": "Geen afbeelding", + "no-support-geolocation": "Uw browser ondersteunt geen geolocatie", + "no-support-web-camera": "Uw browser ondersteunt geen camera's", + "enable-https-use-widget": "Schakel HTTPS in om deze widget te gebruiken", + "no-found-your-camera": "Kan je camera niet vinden", + "no-permission-camera": "Toestemming is geweigerd door de gebruiker / Deze site heeft geen toestemming om de camera te gebruiken", + "no-timeseries-selected": "Geen tijdreeks geselecteerd", + "secret-key": "Geheime sleutel", + "secret-key-required": "Geheime sleutel is vereist", + "switch-attribute-value": "Entiteitskenmerkwaarde wijzigen", + "switch-camera": "Wissel van camera", + "switch-timeseries-value": "Tijdreekswaarde van entiteit wijzigen", + "take-photo": "Maak een foto", + "time": "Tijd", + "timeseries-not-allowed": "Timeseries parameter kan niet worden gebruikt in deze widget", + "update-failed": "Update mislukt", + "update-successful": "Update geslaagd", + "update-attribute": "Attribuut bijwerken", + "update-timeseries": "Timeseries bijwerken", + "value": "Waarde", + "general-settings": "Algemene instellingen", + "widget-title": "Titel van de widget", + "claim-button-label": "Knoplabel claimen", + "show-secret-key-field": "Toon 'Geheime sleutel' invoerveld", + "labels-settings": "Labels instellingen", + "show-labels": "Labels weergeven", + "device-name-label": "Label voor invoerveld device naam", + "secret-key-label": "Label voor het invoerveld van de geheime sleutel", + "messages-settings": "Instellingen voor berichten", + "claim-device-success-message": "Sms-bericht van succesvolle claiming van device", + "claim-device-not-found-message": "Sms wanneer device niet gevonden", + "claim-device-failed-message": "Sms-bericht van geclaimd device", + "claim-device-name-required-message": "Foutbericht 'Device naam vereist'", + "claim-device-secret-key-required-message": "Foutmelding 'Geheime sleutel vereist'", + "relations-settings": "Instellingen voor relaties", + "relate-device-to-state-entity": "Device relateren aan de entiteit met de huidige status", + "relate-direction": "Richting relateren", + "relate-direction-from": "Van", + "relate-direction-to": "Aan", + "relation-type": "Relatie type", + "show-label": "Etiket weergeven", + "label": "Etiket", + "required": "Vereist", + "required-error-message": "Foutmelding 'Vereist'", + "show-result-message": "Resultaatbericht weergeven", + "integer-field-settings": "Instellingen voor velden met gehele getallen", + "min-value": "Min waarde", + "max-value": "Maximale waarde", + "double-field-settings": "Dubbele veldinstellingen", + "text-field-settings": "Instellingen voor tekstvelden", + "min-length": "Min lengte", + "max-length": "Maximale lengte", + "checkbox-settings": "Selectievakjes instellingen", + "true-label": "Gecontroleerd etiket", + "false-label": "Onaangevinkt etiket", + "image-input-settings": "Instellingen voor beeldinvoer", + "display-preview": "Voorbeeld weergeven", + "display-clear-button": "Knop Wissen weergeven", + "display-apply-button": "Knop Toepassen weergeven", + "display-discard-button": "Knop voor weggooien weergeven", + "datetime-field-settings": "Instellingen voor datum-/tijdvelden", + "display-time-input": "Tijdinvoer weergeven", + "latitude-key-name": "Naam van breedtegraad-sleutel", + "longitude-key-name": "Lengtegraad sleutelnaam", + "show-get-location-button": "Toon knop 'Huidige locatie ophalen'", + "use-high-accuracy": "Gebruik hoge nauwkeurigheid", + "location-fields-settings": "Instellingen voor locatievelden", + "latitude-label": "Label voor breedtegraad", + "longitude-label": "Label voor lengtegraad", + "input-fields-alignment": "Uitlijning van invoervelden", + "input-fields-alignment-column": "Kolom (standaard)", + "input-fields-alignment-row": "Rijen", + "latitude-field-required": "Breedtegraadveld vereist", + "longitude-field-required": "Lengtegraadveld vereist", + "attribute-settings": "Attribuut instellingen", + "widget-mode": "Widget-modus", + "widget-mode-update-attribute": "Attribuut bijwerken", + "widget-mode-update-timeseries": "Timeseries bijwerken", + "attribute-scope": "Bereik van attribuut", + "attribute-scope-server": "Server-attribuut", + "attribute-scope-shared": "Gedeeld attribuut", + "value-required": "Vereiste waarde", + "image-settings": "Beeldinstellingen", + "image-format": "Beeldformaat", + "image-format-jpeg": ".JPEG", + "image-format-png": ".PNG", + "image-format-webp": ".WEBP", + "image-quality": "Beeldkwaliteit die compressie met verlies gebruikt, zoals jpeg en webp", + "max-image-width": "Maximale afbeeldingsbreedte", + "max-image-height": "Maximale afbeeldingshoogte", + "action-buttons": "Actieknoppen", + "show-action-buttons": "Actieknoppen weergeven", + "update-all-values": "Werk alle waarden bij, niet alleen gewijzigd", + "save-button-label": "Label met de knop 'OPSLAAN'", + "reset-button-label": "Knoplabel 'ONGEDAAN MAKEN'", + "group-settings": "Groepsinstellingen", + "show-group-title": "Titel weergeven voor groep velden, gerelateerd aan verschillende entiteiten", + "group-title": "Titel van de groep", + "fields-alignment": "Uitlijning van velden", + "fields-alignment-row": "Rij (standaard)", + "fields-alignment-column": "Kolom", + "fields-in-row": "Aantal velden in de rij", + "option-value": "Waarde (schrijf 'null' voor de optie leeg maken)", + "option-label": "Etiket", + "hide-input-field": "Invoerveld verbergen", + "datakey-type": "Type datakey", + "datakey-type-server": "Serverkenmerk (standaard)", + "datakey-type-shared": "Gedeeld attribuut", + "datakey-type-timeseries": "Timeseries", + "datakey-value-type": "Waardetype Datakey", + "datakey-value-type-string": "Snaar", + "datakey-value-type-double": "Dubbel", + "datakey-value-type-integer": "Geheel getal", + "datakey-value-type-json": "JSON", + "datakey-value-type-boolean-checkbox": "Booleaans (selectievakje)", + "datakey-value-type-boolean-switch": "Booleaans (schakelaar)", + "datakey-value-type-date-time": "Datum en tijd", + "datakey-value-type-date": "Datum", + "datakey-value-type-time": "Tijd", + "datakey-value-type-select": "Selecteren", + "value-is-required": "Waarde is vereist", + "ability-to-edit-attribute": "Mogelijkheid om attribuut te bewerken", + "ability-to-edit-attribute-editable": "Bewerkbaar (standaard)", + "ability-to-edit-attribute-disabled": "Invalide", + "ability-to-edit-attribute-readonly": "Alleen-lezen", + "disable-on-datakey-name": "Uitschakelen bij valse waarde van een andere datakey (geef de naam van de datakey op)", + "slide-toggle-settings": "Instellingen voor schuifschakelaar", + "slide-toggle-label-position": "Schuif de positie van het label omschakelen", + "slide-toggle-label-position-after": "Na", + "slide-toggle-label-position-before": "Voor", + "select-options": "Opties selecteren", + "no-select-options": "Geen geselecteerde opties geconfigureerd", + "add-select-option": "Selectieoptie toevoegen", + "numeric-field-settings": "Instellingen voor numerieke velden", + "step-interval": "Stapinterval tussen waarden", + "error-messages": "Foutberichten", + "min-value-error-message": "Foutmelding 'Min. waarde'", + "max-value-error-message": "Foutmelding 'Maximale waarde'", + "invalid-date-error-message": "Foutmelding 'Ongeldige datum'", + "invalid-JSON-error-message": "Foutbericht 'Ongeldige JSON'", + "icon-settings": "Icoon instellingen", + "dialog-editor-settings": "Instellingen van de dialoogeditor", + "use-custom-icon": "Aangepast pictogram gebruiken", + "input-cell-icon": "Pictogram om weer te geven vóór invoercel", + "value-conversion-settings": "Instellingen voor waardeconversie", + "get-value-settings": "Waarde-instellingen ophalen", + "use-get-value-function": "De getValue-functie gebruiken", + "get-value-function": "getValue, functie", + "set-value-settings": "Waarde-instellingen instellen", + "use-set-value-function": "Gebruik de setValue-functie", + "set-value-function": "setValue-functie", + "json-invalid": "JSON-waarde heeft een ongeldige indeling", + "title": "Titel", + "cancel-button-label": "Label met de knop 'Annuleren'" + }, + "invalid-qr-code-text": "Ongeldige invoertekst voor QR-code. Invoer moet een tekenreekstype hebben", + "qr-code": { + "use-qr-code-text-function": "Gebruik de tekstfunctie van de QR-code", + "qr-code-text-pattern": "Tekstpatroon QR-code (bijv. '${entityName} | ${keyName} - wat tekst.')", + "qr-code-text-pattern-hint": "Het tekstpatroon van de QR-code gebruikt de waarde van de eerst gevonden sleutel in de entiteiten in de entiteitsalias.", + "qr-code-text-pattern-required": "Tekstpatroon QR-code is vereist.", + "qr-code-text-function": "Tekstfunctie QR-code" + }, + "label-widget": { + "label-pattern": "Patroon", + "label-pattern-hint": "Tip: bijv. 'Text <span style=\"color: #000;\">${keyName<span style=\"color: #000;\">} eenheden.' of <span style=\"color: #000;\">${#<key index><span style=\"color: #000;\">} eenheden'", + "label-pattern-required": "Patroon is vereist", + "label-position": "Positie (percentage ten opzichte van achtergrond)", + "x-pos": "X", + "y-pos": "Y", + "background-color": "Achtergrondkleur", + "font-settings": "Lettertype-instellingen", + "background-image": "Achtergrondafbeelding", + "labels": "Etiketten", + "no-labels": "Geen labels geconfigureerd", + "add-label": "Label toevoegen" + }, + "navigation": { + "title": "Titel", + "navigation-path": "Navigatie pad", + "filter-type": "Filter type", + "filter-type-all": "Alle artikelen", + "filter-type-include": "Items opnemen", + "filter-type-exclude": "Items uitsluiten", + "items": "Items", + "enter-urls-to-filter": "Voer url's in om te filteren..." + }, + "persistent-table": { + "rpc-id": "RPC-ID", + "message-type": "Soort bericht", + "method": "Methode", + "params": "Params", + "created-time": "Gecreëerde tijd", + "expiration-time": "Vervaltijd", + "retries": "Pogingen", + "status": "Status", + "filter": "Filter", + "refresh": "Opfrissen", + "add": "RPC-aanvraag toevoegen", + "details": "Details", + "delete": "Verwijderen", + "delete-request-title": "Persistent RPC-verzoek verwijderen", + "delete-request-text": "Weet je zeker dat je een verzoek wilt verwijderen?", + "details-title": "Details RPC-ID:", + "additional-info": "Aanvullende informatie", + "response": "Antwoord", + "any-status": "Elke status", + "rpc-status-list": "RPC-statuslijst", + "no-request-prompt": "Geen verzoek om weer te geven", + "send-request": "Aanvraag versturen", + "add-title": "Permanente RPC-aanvraag maken", + "method-error": "Methode is vereist.", + "timeout-error": "De minimale time-outwaarde is 5000 (5 seconden).", + "white-space-error": "Witruimte is niet toegestaan.", + "rpc-status": { + "QUEUED": "WACHTRIJ", + "SENT": "VERZONDEN", + "DELIVERED": "GELEVERD", + "SUCCESSFUL": "SUCCESVOL", + "TIMEOUT": "TIMEOUT", + "EXPIRED": "VERVALLEN", + "FAILED": "MISLUKT" + }, + "rpc-search-status-all": "ALLE", + "message-types": { + "false": "Twee richtingen", + "true": "One-way" + }, + "general-settings": "Algemene instellingen", + "enable-filter": "Filter inschakelen", + "enable-sticky-header": "Koptekst weergeven tijdens scrollen", + "enable-sticky-action": "Kolom Acties weergeven tijdens het scrollen", + "display-request-details": "Aanvraaggegevens weergeven", + "allow-send-request": "Sta het verzenden van RPC-verzoek toe", + "allow-delete-request": "Verwijderingsverzoek toestaan", + "columns-settings": "Instellingen voor kolommen", + "display-columns": "Weer te geven kolommen", + "column": "Kolom", + "no-columns-found": "Geen kolommen gevonden", + "no-columns-matching": "'{{column}}' niet gevonden." + }, + "rpc": { + "value-settings": "Waarde-instellingen", + "initial-value": "Beginwaarde", + "retrieve-value-settings": "Aan/uit-waarde-instellingen ophalen", + "retrieve-value-method": "Waarde ophalen met behulp van methode", + "retrieve-value-method-none": "Niet ophalen", + "retrieve-value-method-rpc": "RPC aanroepen om waardemethode op te halen", + "retrieve-value-method-attribute": "Abonneren op attribuut", + "retrieve-value-method-timeseries": "Schrijf je in voor tijdseries", + "attribute-value-key": "Attribuut sleutel", + "timeseries-value-key": "Tijdreeks-toets", + "get-value-method": "RPC get value methode", + "parse-value-function": "Functie voor het parseren van waarden", + "update-value-settings": "Waarde-instellingen bijwerken", + "set-value-method": "RPC-methode voor ingestelde waarden", + "convert-value-function": "Functie Waarde converteren", + "rpc-settings": "RPC-instellingen", + "request-timeout": "Time-out voor RPC-aanvraag (ms)", + "persistent-rpc-settings": "Persistente RPC-instellingen", + "request-persistent": "RPC-aanvraag persistent", + "persistent-polling-interval": "Pollinginterval (ms) om persistente RPC-opdrachtrespons te krijgen", + "common-settings": "Algemene instellingen", + "switch-title": "Titel wijzigen", + "show-on-off-labels": "Aan/uit-labels weergeven", + "slide-toggle-label": "Schuif toggle label", + "label-position": "Positie van het etiket", + "label-position-before": "Voor", + "label-position-after": "Na", + "slider-color": "Kleur schuifregelaar", + "slider-color-primary": "Primair", + "slider-color-accent": "Accent", + "slider-color-warn": "Waarschuwen", + "button-style": "Knop stijl", + "button-raised": "Verhoogde knop", + "button-primary": "Primaire kleur", + "button-background-color": "Achtergrondkleur knop", + "button-text-color": "Tekstkleur knop", + "widget-title": "Titel van de widget", + "button-label": "Knop label", + "device-attribute-scope": "Bereik van device attributen", + "server-attribute": "Server-attribuut", + "shared-attribute": "Gedeeld attribuut", + "device-attribute-parameters": "Parameters voor device attributen", + "is-one-way-command": "Is eenrichtingscommando", + "rpc-method": "RPC-methode", + "rpc-method-params": "Params van de RPC-methode", + "show-rpc-error": "Fout bij het uitvoeren van RPC-opdrachten weergeven", + "led-title": "LED-titel", + "led-color": "LED-kleur", + "check-status-settings": "Statusinstellingen controleren", + "perform-rpc-status-check": "Voer de statuscontrole van het RPC-device uit", + "retrieve-led-status-value-method": "Leidsstatuswaarde ophalen met behulp van methode", + "led-status-value-attribute": "Apparaatkenmerk met led-statuswaarde", + "led-status-value-timeseries": "Tijdreeks van het device met led-statuswaarde", + "check-status-method": "RPC-methode voor het controleren van de apparaatstatus", + "parse-led-status-value-function": "Led-statuswaardefunctie parseren", + "knob-title": "Titel van de knop", + "min-value": "Minimumwaarde", + "max-value": "Maximale waarde" + }, + "maps": { + "select-entity": "Entiteit selecteren", + "select-entity-hint": "Tip: klik na selectie op de kaart om de positie in te stellen", + "tooltips": { + "placeMarker": "Klik om de entiteit '{{entityName}}' te plaatsen", + "firstVertex": "Polygoon voor '{{entityName}}': klik om eerste punt te plaatsen", + "firstVertex-cut": "Klik om het eerste punt te plaatsen", + "continueLine": "Polygoon voor '{{entityName}}': klik om verder te tekenen", + "continueLine-cut": "Klik om door te gaan met tekenen", + "finishLine": "Klik op een bestaande markering om te voltooien", + "finishPoly": "Polygoon voor '{{entityName}}': klik op de eerste markering om te voltooien en op te slaan", + "finishPoly-cut": "Klik op de eerste markering om te voltooien en op te slaan", + "finishRect": "Polygoon voor '{{entityName}}': klik om te voltooien en op te slaan", + "startCircle": "Cirkel voor '{{entityName}}': klik om het midden van de cirkel te plaatsen", + "finishCircle": "Cirkel voor '{{entityName}}': klik om cirkel te voltooien", + "placeCircleMarker": "Klik om de cirkelmarkering te plaatsen" + }, + "actions": { + "finish": "Fins", + "cancel": "Annuleren", + "removeLastVertex": "Laatste punt verwijderen" + }, + "buttonTitles": { + "drawMarkerButton": "Entiteit plaatsen", + "drawPolyButton": "Polygoon maken", + "drawLineButton": "Polylijn maken", + "drawCircleButton": "Cirkel maken", + "drawRectButton": "Rechthoek maken", + "editButton": "Modus bewerken", + "dragButton": "Drag-drop-modus", + "cutButton": "Snijd polygoongebied", + "deleteButton": "Verwijderen", + "drawCircleMarkerButton": "Cirkelmarkering maken", + "rotateButton": "Polygoon roteren" + }, + "map-provider-settings": "Instellingen van kaartprovider", + "map-provider": "Kaart provider", + "map-provider-google": "Google kaarten", + "map-provider-openstreet": "OpenStreet-kaarten", + "map-provider-here": "HERE kaarten", + "map-provider-image": "Afbeelding kaart", + "map-provider-tencent": "Tencent kaarten", + "openstreet-provider": "OpenStreet-kaartprovider", + "openstreet-provider-mapnik": "OpenStreetMap.Mapnik (standaard)", + "openstreet-provider-hot": "OpenStreetMap.HOT", + "openstreet-provider-esri-street": "Esri.WorldStreetMap", + "openstreet-provider-esri-topo": "Esri.WorldTopoMap", + "openstreet-provider-esri-imagery": "Esri.WorldImagery", + "openstreet-provider-cartodb-positron": "CartoDB.Positron", + "openstreet-provider-cartodb-dark-matter": "CartoDB.DarkMatter", + "use-custom-provider": "Aangepaste provider gebruiken", + "custom-provider-tile-url": "Aangepaste tegel-URL van provider", + "google-maps-api-key": "API-sleutel voor Google Maps", + "default-map-type": "Standaard kaarttype", + "google-map-type-roadmap": "Stappenplan", + "google-map-type-satelite": "Satelliet", + "google-map-type-hybrid": "Hybride", + "google-map-type-terrain": "Terrein", + "map-layer": "Kaart laag", + "here-map-normal-day": "HERE.normalDay (standaard)", + "here-map-normal-night": "HIER.normalNight", + "here-map-hybrid-day": "HIER.hybridDay", + "here-map-terrain-day": "HIER.terreinDag", + "credentials": "Geloofsbrief", + "here-app-id": "HERE-app-id", + "here-app-code": "HERE-app-code", + "here-api-key": "HERE API-sleutel", + "here-use-new-version-api-3": "API-versie 3 gebruiken", + "tencent-maps-api-key": "API-sleutel voor Tencent Maps", + "tencent-map-type-roadmap": "Stappenplan", + "tencent-map-type-satelite": "Satelliet", + "tencent-map-type-hybrid": "Hybride", + "image-map-background": "De achtergrond van de afbeeldingskaart", + "image-map-background-from-entity-attribute": "Neem de achtergrond van de afbeeldingstoewijzing van het entiteitskenmerk", + "image-url-source-entity-alias": "Alias van de bronentiteit van de afbeeldings-URL", + "image-url-source-entity-attribute": "Entiteitskenmerk bronentiteit afbeeldings-URL", + "common-map-settings": "Algemene kaartinstellingen", + "x-pos-key-name": "Naam van de X-positiesleutel", + "y-pos-key-name": "Y-positie sleutelnaam", + "latitude-key-name": "Naam van breedtegraad-sleutel", + "longitude-key-name": "Lengtegraad sleutelnaam", + "default-map-zoom-level": "Standaard zoomniveau voor kaart (0 - 20)", + "default-map-center-position": "Standaard middenpositie op de kaart (0,0)", + "disable-scroll-zooming": "Scrollen uitschakelen", + "disable-double-click-zooming": "Dubbelklikken uitschakelen", + "disable-zoom-control-buttons": "Zoombedieningsknoppen uitschakelen", + "fit-map-bounds": "Pas kaartgrenzen aan om alle markeringen te bedekken", + "use-default-map-center-position": "Gebruik de standaardpositie in het midden van de kaart", + "entities-limit": "Limiet van te laden entiteiten", + "markers-settings": "Instellingen voor markeringen", + "marker-offset-x": "Markering X verschuiving ten opzichte van positie vermenigvuldigd met markeringsbreedte", + "marker-offset-y": "Markering Y-verschuiving ten opzichte van positie vermenigvuldigd met markeringshoogte", + "position-function": "Positieconversiefunctie, moet x,y-coördinaten retourneren als dubbel van 0 tot 1 elk", + "draggable-marker": "Versleepbare markering", + "label": "Etiket", + "show-label": "Etiket weergeven", + "use-label-function": "Labelfunctie gebruiken", + "label-pattern": "Label (patroonvoorbeelden: '${entityName}', '${entityName}: (Tekst ${keyName} eenheden.)' )", + "label-function": "Label functie", + "tooltip": "Tooltip", + "show-tooltip": "Tooltip weergeven", + "show-tooltip-action": "Actie voor het weergeven van de knopinfo", + "show-tooltip-action-click": "Knopinfo weergeven bij klikken (Standaard)", + "show-tooltip-action-hover": "Knopinfo weergeven bij aanwijzen", + "auto-close-tooltips": "Tooltips voor automatisch sluiten", + "use-tooltip-function": "Tooltip-functie gebruiken", + "tooltip-pattern": "Tooltip (bijv. 'Tekst ${keyName} eenheden.' of Linktekst')", + "tooltip-function": "Tooltip-functie", + "tooltip-offset-x": "Tooltip X-verschuiving ten opzichte van markeringsanker vermenigvuldigd met markeringsbreedte", + "tooltip-offset-y": "Tooltip Y-verschuiving ten opzichte van markeringsanker vermenigvuldigd met markeringshoogte", + "color": "Kleur", + "use-color-function": "Kleurfunctie gebruiken", + "color-function": "Kleur functie", + "marker-image": "Afbeelding markeren", + "use-marker-image-function": "Gebruik de functie voor markeringsafbeeldingen", + "custom-marker-image": "Aangepaste markeringsafbeelding", + "custom-marker-image-size": "Afbeeldingsgrootte aangepaste markering (px)", + "marker-image-function": "Marker image functie", + "marker-images": "Marker afbeeldingen", + "polygon-settings": "Polygoon-instellingen", + "show-polygon": "Polygoon weergeven", + "polygon-key-name": "Naam van de Polygoonsleutel", + "enable-polygon-edit": "Polygoonbewerking inschakelen", + "polygon-label": "Polygoon etiket", + "show-polygon-label": "Polygoonlabel weergeven", + "use-polygon-label-function": "Polygoonlabelfunctie gebruiken", + "polygon-label-pattern": "Polygoonlabel (patroonvoorbeelden: '${entityName}', '${entityName}: (Tekst ${keyName} eenheden.)' )", + "polygon-label-function": "Polygoon label functie", + "polygon-tooltip": "Tooltip voor polygoon", + "show-polygon-tooltip": "Polygoon knopinfo weergeven", + "auto-close-polygon-tooltips": "Tooltips voor polygoon automatisch sluiten", + "use-polygon-tooltip-function": "Polygoon-tooltipfunctie gebruiken", + "polygon-tooltip-pattern": "Tooltip (bijv. 'Tekst ${keyName} eenheden.' of Linktekst')", + "polygon-tooltip-function": "Polygoon tooltip functie", + "polygon-color": "Polygoon kleur", + "polygon-opacity": "Dekking van polygoon", + "use-polygon-color-function": "Polygoonkleurfunctie gebruiken", + "polygon-color-function": "Polygoon kleur functie", + "polygon-stroke": "Polygoon lijn", + "stroke-color": "Kleur van de lijn", + "stroke-opacity": "Dekking van de beroerte", + "stroke-weight": "Het gewicht van de slag", + "use-polygon-stroke-color-function": "Gebruik de kleurfunctie van de polygoonlijn", + "polygon-stroke-color-function": "Kleurfunctie voor polygoonlijnen", + "circle-settings": "Cirkel-instellingen", + "show-circle": "Toon cirkel", + "circle-key-name": "Naam van de cirkelsleutel", + "enable-circle-edit": "Cirkelbewerking inschakelen", + "circle-label": "Het etiket van de cirkel", + "show-circle-label": "Cirkellabel weergeven", + "use-circle-label-function": "Gebruik de cirkellabelfunctie", + "circle-label-pattern": "Cirkellabel (patroonvoorbeelden: '${entityName}', '${entityName}: (Tekst ${keyName} eenheden.)' )", + "circle-label-function": "Cirkellabel-functie", + "circle-tooltip": "Tooltip voor cirkelen", + "show-circle-tooltip": "Knopinfo cirkel weergeven", + "auto-close-circle-tooltips": "Tooltips voor automatisch sluiten van cirkels", + "use-circle-tooltip-function": "Gebruik de cirkel-tooltip-functie", + "circle-tooltip-pattern": "Tooltip (bijv. 'Tekst ${keyName} eenheden.' of Linktekst')", + "circle-tooltip-function": "Cirkel tooltip functie", + "circle-fill-color": "Vulkleur cirkel", + "circle-fill-color-opacity": "Dekking van de vulkleur van de cirkel", + "use-circle-fill-color-function": "Gebruik de functie voor het vullen van cirkels", + "circle-fill-color-function": "Cirkelvulkleurfunctie", + "circle-stroke": "De slag van de cirkel", + "use-circle-stroke-color-function": "Gebruik de kleurfunctie van de cirkelstreek", + "circle-stroke-color-function": "Cirkellijn kleurfunctie", + "markers-clustering-settings": "Instellingen voor het clusteren van markeringen", + "use-map-markers-clustering": "Clustering van kaartmarkeringen gebruiken", + "zoom-on-cluster-click": "Inzoomen bij het klikken op een cluster", + "max-cluster-zoom": "Het maximale zoomniveau wanneer een markering deel kan uitmaken van een cluster (0 - 18)", + "max-cluster-radius-pixels": "Maximale straal die een cluster in pixels bestrijkt", + "cluster-zoom-animation": "Animatie weergeven op markeringen bij het zoomen", + "show-markers-bounds-on-cluster-mouse-over": "De grenzen van markeringen weergeven wanneer u de muisaanwijzer op een cluster plaatst", + "spiderfy-max-zoom-level": "Spiderfy op het maximale zoomniveau (om alle clustermarkeringen te zien)", + "load-optimization": "Optimalisatie van de belasting", + "cluster-chunked-loading": "Gebruik chunks om markeringen toe te voegen, zodat de pagina niet vastloopt", + "cluster-markers-lazy-load": "Gebruik lazy load voor het toevoegen van markeringen", + "editor-settings": "Editor-instellingen", + "enable-snapping": "Uitlijnen op andere punten inschakelen voor nauwkeurig tekenen", + "init-draggable-mode": "Kaart initialiseren in versleepbare modus", + "hide-all-edit-buttons": "Alle bedieningsknoppen voor bewerken verbergen", + "hide-draw-buttons": "Tekenknoppen verbergen", + "hide-edit-buttons": "Bewerkingsknoppen verbergen", + "hide-remove-button": "Verwijderknop verbergen", + "route-map-settings": "Routekaartinstellingen", + "trip-animation-settings": "Instellingen voor reisanimatie", + "normalization-step": "Normalisatie gegevensstap (ms)", + "tooltip-background-color": "Achtergrondkleur tooltip", + "tooltip-font-color": "Tekstkleur van knopinfo", + "tooltip-opacity": "Tooltip dekking (0-1)", + "auto-close-tooltip": "Tooltip voor automatisch sluiten", + "rotation-angle": "Stel extra draaihoek in voor marker (graden)", + "path-settings": "Pad-instellingen", + "path-color": "De kleur van het pad", + "use-path-color-function": "Padkleurfunctie gebruiken", + "path-color-function": "Padkleurfunctie", + "path-decorator": "Pad decorateur", + "use-path-decorator": "Gebruik paddecorateur", + "decorator-symbol": "Het symbool van de decorateur", + "decorator-symbol-arrow-head": "Pijl", + "decorator-symbol-dash": "Scheutje", + "decorator-symbol-size": "Grootte decorateur symbool (px)", + "use-path-decorator-custom-color": "Gebruik de aangepaste kleur van de paddecorateur", + "decorator-custom-color": "Decorateur aangepaste kleur", + "decorator-offset": "Decorateur offset", + "end-decorator-offset": "Einddecorateur offset", + "decorator-repeat": "Decorateur herhalen", + "points-settings": "Punten instellingen", + "show-points": "Toon punten", + "point-color": "Kleur van de punt", + "point-size": "Punt grootte (px)", + "use-point-color-function": "Puntkleurfunctie gebruiken", + "point-color-function": "Puntkleurfunctie", + "use-point-as-anchor": "Punt gebruiken als anker", + "point-as-anchor-function": "Aanwijzen als ankerfunctie", + "independent-point-tooltip": "Tooltip voor onafhankelijk punt", + "clustering-markers": "Markeringen clusteren", + "use-icon-create-function": "Gebruik de kleurfunctie van markeringen", + "marker-color-function": "Marker kleur functie" + }, + "markdown": { + "use-markdown-text-function": "Markdown/HTML-waardefunctie gebruiken", + "markdown-text-function": "Markdown/HTML-waarde functie", + "markdown-text-pattern": "Markdown/HTML-patroon (markdown of HTML met variabelen, bijvoorbeeld '${entityName} of ${keyName} - wat tekst.')", + "apply-default-markdown-style": "Standaardstijl voor afprijzing toepassen", + "markdown-css": "Afprijzing/HTML CSS" + }, + "simple-card": { + "label-position": "Positie van het etiket", + "label-position-left": "Links", + "label-position-top": "Boven" + }, + "table": { + "common-table-settings": "Algemene tabelinstellingen", + "enable-search": "Zoeken inschakelen", + "enable-sticky-header": "Koptekst altijd weergeven", + "enable-sticky-action": "Kolom Acties altijd weergeven", + "hidden-cell-button-display-mode": "Weergavemodus voor verborgen celknopacties", + "show-empty-space-hidden-action": "Lege ruimte weergeven in plaats van verborgen celknopactie", + "dont-reserve-space-hidden-action": "Reserveer geen ruimte voor verborgen actieknoppen", + "display-timestamp": "Tijdstempelkolom weergeven", + "display-milliseconds": "Tijdstempel milliseconden weergeven", + "display-pagination": "Paginering weergeven", + "default-page-size": "Standaard paginaformaat", + "use-entity-label-tab-name": "Entiteitslabel gebruiken in de naam van het tabblad", + "hide-empty-lines": "Lege regels verbergen", + "row-style": "Rij stijl", + "use-row-style-function": "Rijstijlfunctie gebruiken", + "row-style-function": "Rijstijl functie", + "cell-style": "Cel stijl", + "use-cell-style-function": "Celstijlfunctie gebruiken", + "cell-style-function": "Functie voor celstijl", + "cell-content": "Celinhoud", + "use-cell-content-function": "De functie voor celinhoud gebruiken", + "cell-content-function": "Functie voor celinhoud", + "show-latest-data-column": "Kolom met meest recente gegevens weergeven", + "latest-data-column-order": "Meest recente volgorde van gegevenskolommen", + "entities-table-title": "Titel van de tabel Entiteiten", + "enable-select-column-display": "Schakel geselecteerde kolommen in om weer te geven", + "display-entity-name": "Kolom met entiteitsnaam weergeven", + "entity-name-column-title": "Kolomtitel van entiteitsnaam", + "display-entity-label": "Kolom met entiteitslabel weergeven", + "entity-label-column-title": "Kolomtitel van entiteitslabel", + "display-entity-type": "Kolom met entiteitstype weergeven", + "default-sort-order": "Standaard sorteervolgorde", + "custom-title": "Aangepaste kopteksttitel", + "column-width": "Kolombreedte (px of %)", + "default-column-visibility": "Standaard zichtbaarheid van kolommen", + "column-visibility-visible": "Zichtbaar", + "column-visibility-hidden": "Verborgen", + "column-visibility-hidden-mobile": "Verborgen in mobiele modus", + "column-selection-to-display": "Kolomselectie in 'Weer te geven kolommen'", + "column-selection-to-display-enabled": "Ingeschakeld", + "column-selection-to-display-disabled": "Invalide", + "column-export-option": "Kolom opnemen in export", + "column-export-option-always": "Altijd", + "column-export-option-only-visible": "Alleen als de kolom zichtbaar is", + "column-export-option-never": "Nooit", + "alarms-table-title": "Titel van de tabel Alarmen", + "enable-alarms-selection": "Selectie van alarmen inschakelen", + "enable-alarms-search": "Zoeken naar alarmen inschakelen", + "enable-alarm-filter": "Alarmfilter inschakelen", + "display-alarm-details": "Alarmdetails weergeven", + "allow-alarms-ack": "Alarmbevestiging toestaan", + "allow-alarms-clear": "Laat alarmen wissen", + "display-alarm-activity": "Alarmactiviteit weergeven", + "allow-alarms-assign": "Toewijzing van alarmen toestaan", + "blob-entities-table-title": "Titel van de tabel met blobentiteiten", + "display-created-time": "Aangemaakte tijdkolom weergeven", + "display-type": "Kolom Weergavetype", + "display-customer": "Klantkolom weergeven", + "no-data-display-message": "Alternatief bericht 'Geen gegevens om weer te geven'", + "force-default-blob-entity-type": "Standaard blob-entiteitstype afdwingen", + "scheduler-events-table-title": "Titel van de tabel met scheduler events", + "force-default-event-type": "Standaardgebeurtenistype afdwingen" + }, + "scheduler": { + "enabled-scheduler-event-views": "Weergaven van scheduler events ingeschakeld", + "enabled-scheduler-event-views-both": "Beide", + "enabled-scheduler-event-views-list": "Alleen lijstweergave", + "enabled-scheduler-event-views-calendar": "Alleen agendaweergave", + "display-name": "Weergavenaam", + "type-name": "Typ naam", + "display-originator-select": "Aansteller van entiteit weergeven", + "display-message-type-select": "Berichttype weergeven selecteren", + "display-metadata-table": "Tabel met metagegevens van berichten weergeven", + "configuration-html-template": "Configuratie HTML-sjabloon", + "custom-event-types": "Aangepaste gebeurtenistypen", + "no-custom-event-types": "Geen aangepaste gebeurtenistypen geconfigureerd", + "add-custom-event-type": "Aangepast gebeurtenistype toevoegen" + }, + "value-source": { + "value-source": "Waardebron", + "predefined-value": "Vooraf gedefinieerde waarde", + "entity-attribute": "Waarde ontleend aan entiteitskenmerk", + "value": "Waarde", + "source-entity-alias": "Alias van bronentiteit", + "source-entity-attribute": "Kenmerk van bronentiteit" + }, + "widget-font": { + "font-family": "Lettertype familie", + "size": "Grootte", + "relative-font-size": "Relatieve lettergrootte (procenten)", + "font-style": "Stijl", + "font-style-normal": "Normaal", + "font-style-italic": "Cursief", + "font-style-oblique": "Schuin", + "font-weight": "Gewicht", + "font-weight-normal": "Normaal", + "font-weight-bold": "Bold", + "font-weight-bolder": "Bolder", + "font-weight-lighter": "Lichter", + "color": "Kleur", + "shadow-color": "De kleur van de schaduw" + }, + "home": { + "no-data-available": "Geen gegevens beschikbaar" + }, + "system-info": { + "cpu": "CPU", + "ram": "RAM", + "disk": "Schijf", + "cpu-warning-text": "Hoog CPU-gebruik. Optimaliseer de systeemprestaties om systeemstoringen te voorkomen.", + "cpu-critical-text": "Kritisch hoog CPU-gebruik. Optimaliseer de systeemprestaties om systeemstoringen te voorkomen.", + "ram-warning-text": "Heeft bijna geen RAM-geheugen meer. Om systeemstoringen te voorkomen, optimaliseert u de systeemprestaties of vergroot u de grootte van het RAM-geheugen.", + "ram-critical-text": "Kritisch lage RAM-reserve. Om systeemstoringen te voorkomen, optimaliseert u de systeemprestaties of vergroot u de grootte van het RAM-geheugen.", + "disk-warning-text": "Weinig schijfruimte. Om gegevensverlies te voorkomen, maakt u schijfruimte vrij of breidt u deze uit.", + "disk-critical-text": "Kritisch weinig schijfruimte. Om gegevensverlies te voorkomen, maakt u schijfruimte vrij of breidt u deze uit." + }, + "cluster-info": { + "service-id": "Service-id", + "service-type": "Soort dienst", + "no-data": "Geen gegevens" + }, + "transport-messages": { + "title": "Berichten vervoeren", + "info": "Alle berichten die afkomstig zijn van Devices" + }, + "activity": { + "title": "Activiteit" + }, + "documentation": { + "title": "Documentatie", + "add-link": "Link toevoegen", + "add-link-title": "Documentatiekoppeling toevoegen", + "name": "Naam", + "name-required": "Naam is verplicht.", + "link": "Verbinden", + "link-required": "Link is vereist.", + "columns": "Kolommen" + }, + "quick-links": { + "title": "Snelkoppelingen", + "add-link": "Link toevoegen", + "add-link-title": "Snelkoppeling toevoegen", + "quick-link": "Snelkoppeling", + "quick-link-required": "Snelle koppeling is vereist.", + "no-links-matching": "Er zijn geen links gevonden die overeenkomen met '{{name}}'.", + "columns": "Kolommen" + }, + "recent-dashboards": { + "title": "Dashboards", + "last": "Laatst bekeken", + "starred": "Speelde", + "name": "Naam", + "last-viewed": "Laatst bekeken", + "no-last-viewed-dashboards": "Nog geen laatst bekeken dashboards" + }, + "configured-features": { + "title": "Geconfigureerde functies", + "info": "Status van functies waarvoor configuratie vereist is", + "white-labeling-feature": "White labelling", + "email-feature": "E-mail", + "sms-feature": "SMS", + "slack-feature": "Los", + "oauth2-feature": "OAuth 2", + "2fa-feature": "2FA", + "feature-configured": "Functie is geconfigureerd.\nKlik om in te stellen", + "feature-not-configured": "Functie is niet geconfigureerd.\nKlik om in te stellen" + }, + "version-info": { + "title": "Versie", + "contact-us": "Neem contact met ons op", + "current-version": "Huidige versie", + "current": "Actueel", + "available-version": "Beschikbare versie", + "available": "Beschikbaar", + "upgrade": "Opwaarderen", + "version-is-up-to-date": "Versie is up-to-date" + }, + "license-info": { + "title": "Licentie info", + "view-all": "Alles weergeven", + "license-portal": "Licentie portaal", + "devices-info": "{count} / { max, plural, =0 {∞} andere {#} } Devices", + "assets-info": "{count} / { max, plural, =0 {∞} andere {#} } Assets", + "dashboards-info": "{count} / { max, plural, =0 {∞} andere {#} } Dashboards", + "integrations-info": "{count} / { max, plural, =0 {∞} andere {#} } Integraties", + "community-support": "Ondersteuning van de gemeenschap", + "white-labeling": "White labelling", + "unlimited-datapoints-and-messages": "Onbeperkt aantal datapunten en berichten", + "unlimited-api-calls": "Onbeperkt aantal API-aanroepen" + }, + "usage-info": { + "title": "Gebruik", + "entities": "Entiteiten", + "api-calls": "API-aanroepen" + }, + "functions": { + "title": "Functies", + "pe-feature-tooltip": "Alleen op ThingsBoard\nProfessionele editie", + "switch-to-pe": "Overschakelen naar PE", + "alarms": "Alarmen", + "dashboards": "Dashboards", + "entities-and-relations": "Entiteiten en relaties", + "profiles": "Profielen", + "advanced-features": "Geavanceerde functies", + "notification-center": "Meldingscentrum", + "api-usage": "API-gebruik", + "customers": "Klanten", + "customers-hierarchy": "Hiërarchie van klanten", + "roles-and-permissions": "Rollen en machtigingen", + "groups": "Groepen", + "integrations": "Integraties", + "solution-templates": "Sjablonen voor oplossingen", + "scheduler": "Scheduler", + "white-labeling": "White labelling" + }, + "devices": { + "view-docs": "Documenten bekijken", + "inactive": "Inactief", + "active": "Actief", + "total": "Totaal" + }, + "alarms": { + "critical": "Kritisch", + "assigned-to-me": "Aan mij toegewezen", + "total": "Totaal" + }, + "solution-templates": { + "title": "Sjablonen voor oplossingen", + "prototype-plan": "Prototype plan", + "startup-plan": "Opstart plan" + }, + "getting-started": { + "get-started": "Start", + "finish": "Einde", + "done-welcome-title": "Welkom", + "done-welcome-text": "Je hebt het geweldig gedaan!", + "sys-admin": { + "step1": { + "title": "Tenant en tenantbeheerder maken", + "content": "

Een tenant is een persoon of een organisatie die devices en bedrijfsmiddelen bezit of produceert. De tenant kan meerdere tenantbeheerders, gebruikers, klanten, devices en assets hebben.

De tenantbeheerder kan devices, assets, klanten en dashboards maken en beheren binnen het tenantaccount.

Volg de documentatie over hoe u dit moet doen:

", + "how-to-create-tenant": "Tenant en tenantbeheerder aanmaken" + }, + "step2": { + "title": "Functie configureren: E-mailserver", + "content": "

De configuratie van de e-mailserver is essentieel voor gebruikersactivering, wachtwoordherstel en bezorging van alarmmeldingen.

Volg de documentatie over hoe u dit moet doen:

", + "how-to-configure-mail-server": "Hoe de e-mailserver te configureren" + }, + "step3": { + "title": "Configureer functie: SMS-provider", + "content": "

Configureer sms-providers om de klanten via sms op de hoogte te stellen van de alarmen.

Volg de documentatie over hoe u dit moet doen:

", + "how-to-configure-sms-provider": "Hoe sms-provider te configureren" + }, + "step4": { + "title": "Configureer functie: White-labeling", + "content": "

Pas eenvoudig het logo en het kleurenschema van uw bedrijf of product aan zonder codering en zonder de service opnieuw te starten.

Volg de documentatie over hoe u dit moet doen:

", + "how-to-configure-white-labeling": "Whitelabeling configureren" + }, + "step5": { + "title": "Configureer functie: 2FA", + "content": "

Verbeter de beveiliging van de platformaccounts met tweefactorauthenticatie.

Volg de documentatie over hoe u dit moet doen:

", + "how-to-configure-2fa": "Hoe configureer je 2FA?" + }, + "step6": { + "title": "Functie configureren: OAuth 2", + "content": "

Vereenvoudig het inloggen voor de tenant en klantgebruikers met Single Sign-On-functionaliteit via OAuth 2.0.

Volg de documentatie over hoe u dit moet doen:

", + "how-to-configure-oauth2": "Hoe OAuth 2 te configureren" + }, + "step7": { + "title": "Functie configureren: Slack", + "content": "

Distribueer meldingen naar de tenant en klantgebruikers via Slack volgens de meldingsregels die u instelt.

Volg de documentatie over hoe u dit moet doen:

", + "how-to-configure-notifications": "Slack configureren" + } + }, + "tenant-admin": { + "step1": { + "title": "Device maken", + "content": "

Laten we uw eerste device via de gebruikersinterface op het platform aanmaken. Volg de documentatie over hoe u dit moet doen:

", + "how-to-create-device": "Hoe een device te maken" + }, + "step2": { + "title": "Device aansluiten", + "content-before": "

Om verbinding te maken met het device, moet u de device referenties ophalen. We raden u aan standaard automatisch gegenereerde referenties te gebruiken die toegangstoken zijn voor deze handleiding.

  • Ga naar de device tabel
  • Klik op de device rij om de apparaatgegevens te openen
  • Druk op de knop \"Toegangstoken kopiëren\"

Gebruik eenvoudige commando's om gegevens via HTTP te publiceren. Vergeet niet om $ACCESS_TOKEN te vervangen door het toegangstoken van uw device:

", + "ubuntu": { + "install-curl": "Installeer cURL voor Ubuntu:" + }, + "macos": { + "install-curl": "Installeer cURL voor MacOS:" + }, + "windows": { + "install-curl": "Vanaf Windows 10 b17063 is cURL standaard beschikbaar." + }, + "replace-access-token": "Vervang $ACCESS_TOKEN door het token van uw device:", + "content-after": "

U kunt ook andere protocollen gebruiken, zoals MQTT, CoAP, enz.

Volg de documentatie over hoe u dit moet doen:

", + "how-to-connect-device": "Hoe het device aan te sluiten" + }, + "step3": { + "title": "Dashboard maken", + "content": "

Maak een dashboard om gegevens van entiteiten zoals assets, devices, enz. te visualiseren.

Volg de documentatie over hoe u dit moet doen:

", + "how-to-create-dashboard": "Hoe maak je een dashboard?" + }, + "step4": { + "title": "Alarmregels configureren", + "alarm-rules": "Alarm regels", + "content": "

Laten we alarm slaan als de temperatuur 25°C bereikt. Volg de documentatie over hoe u dit moet doen:

", + "how-to-configure-alarm-rules": "Alarmregels configureren" + }, + "step5": { + "title": "Alarm maken", + "content-before": "

Om het alarm te activeren, verzendt u een nieuwe telemetriewaarde van 26°C of hoger.

", + "replace-access-token": "Vervang $ACCESS_TOKEN door het token van uw device:", + "content-after": "

Volg de documentatie over hoe u dit moet doen:

", + "how-to-create-alarm": "Hoe maak je een alarm aan?" + }, + "step6": { + "title": "Dashboard voor klanten maken en delen", + "content": "

Door dashboards voor eindgebruikers te maken, kan een klantgebruiker alleen zijn eigen devices zien en worden gegevens van een andere klant verborgen.

Volg de documentatie over hoe u dit moet doen:

", + "how-to-create-customer-and-share-dashboard": "Hoe maak je een klantdashboard en deel je het dashboard?" + } + } + } + }, + "icon": { + "icon": "Pictogram", + "select-icon": "Selecteer icoon", + "material-icons": "Materiaal iconen", + "show-all": "Toon alle iconen" + }, + "subscription": { + "entity-limit-text": "U kunt uw abonnement echter upgraden om uw limieten te verhogen.", + "upgrade-your-plan": "Abonnement upgraden", + "white-labeling-feature": "White labeling-functie", + "white-labeling-text-full": "Rebrand de webinterface van het ThingsBoard-platform in 2 minuten met uw bedrijfs- of productlogo en kleurenschema.

Verwijder \"Powered By\" in de voettekst van het dashboard.
Geen codering of herstart van de service vereist. Sta uw klanten toe om hun interface ook te whitelabelen.", + "enable-white-labeling": "Schakel nu de whitelabeling-functie in door uw abonnement te upgraden!", + "read-more": "Lees meer", + "white-labeling-video-text": "Zie onderstaande video-tutorial om te zien hoe deze functie werkt!" + }, + "subscription-error": { + "title": "Overtreding van het abonnement", + "warning-title": "Waarschuwing voor abonnement", + "upgrade-subscription-plan": "Upgrade uw abonnement", + "upgrade-subscription-plan-to-install-solution-template": "Om {{solutionTemplateName}} oplossing te installeren, moet u uw abonnement minimaal upgraden naar het {{planName}} abonnement!", + "limit-reached": { + "device-count": "Je hebt het maximale aantal devices ({{value}}) bereikt dat is toegestaan door je abonnement!", + "asset-count": "U hebt het maximale vermogen ({{value}}) bereikt dat is toegestaan door uw abonnement!" + }, + "feature-disabled": { + "white-labeling": "De White Labeling-functie is niet toegestaan door uw abonnement!" + } + }, + "phone-input": { + "phone-input-label": "Telefoonnummer", + "phone-input-required": "Telefoonnummer is verplicht", + "phone-input-validation": "Telefoonnummer is ongeldig of niet mogelijk", + "phone-input-pattern": "Ongeldig telefoonnummer. Moet in E.164-formaat zijn, bijv. {{phoneNumber}}", + "phone-input-hint": "Telefoonnummer in E.164-formaat, bijv. {{phoneNumber}}" + }, + "custom": { + "widget-action": { + "action-cell-button": "Actiecelknop", + "row-click": "Klik op rij", + "polygon-click": "Klik op polygoon", + "marker-click": "Klik op de markering", + "circle-click": "Klik op de cirkel", + "tooltip-tag-action": "Actie voor tooltip-tag", + "node-selected": "Op knooppunt geselecteerd", + "element-click": "Klik op het HTML-element op", + "pie-slice-click": "Klik op slice", + "row-double-click": "Dubbelklik op de rij" + } + }, + "paginator": { + "items-per-page": "Items per pagina:", + "first-page-label": "Eerste pagina", + "last-page-label": "Laatste pagina", + "next-page-label": "Volgende pagina", + "previous-page-label": "Vorige pagina", + "items-per-page-separator": "van" + }, + "language": { + "language": "Taal", + "locales": { + "ca_ES": "Catalaans", + "cs_CZ": "Česky", + "da_DK": "Dansk", + "de_DE": "Deutsch", + "el_GR": "Ελληνικά", + "en_US": "Engels", + "es_ES": "Español", + "fa_IR": "فارسي", + "fr_FR": "Français", + "it_IT": "Italiano", + "ja_JP": "日本語", + "ka_GE": "ქართული", + "ko_KR": "한국어", + "lv_LV": "Kanton Latviešu", + "pt_BR": "Português do Brasil", + "ro_RO": "Română", + "ru_RU": "Русский", + "sl_SI": "Slovenščina", + "tr_TR": "Türkçe", + "uk_UA": "Українська", + "zh_CN": "简体中文", + "zh_TW": "繁體中文" + } + } +} diff --git a/ui-ngx/src/assets/locale/locale.constant-pt_BR.json b/ui-ngx/src/assets/locale/locale.constant-pt_BR.json index 2bba0338d2..51c8579980 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pt_BR.json +++ b/ui-ngx/src/assets/locale/locale.constant-pt_BR.json @@ -309,6 +309,7 @@ "attributes": "Atributos", "latest-telemetry": "Última telemetria", "attributes-scope": "Escopo de atributos de entidade", + "scope-telemetry": "Telemetria", "scope-latest-telemetry": "Última telemetria", "scope-client": "Atributos do cliente", "scope-server": "Atributos do servidor", @@ -786,7 +787,6 @@ "name-starts-with": "O nome começa com", "use-entity-name-filter": "Usar filtro", "entity-list-empty": "Nenhuma entidade selecionada.", - "entity-type-list-empty": "Nenhum tipo de entidade selecionado.", "entity-name-filter-required": "O filtro do nome de entidade é obrigatório.", "entity-name-filter-no-entity-matched": "Nenhuma entidade encontrada que comece com '{{entity}}'.", "all-subtypes": "Tudo", @@ -1803,11 +1803,7 @@ "datakey-settings-schema": "Esquema de configuração de chave de dados", "javascript": "Javascript", "js": "JS", - "remove-widget-type-title": "Tem certeza de que deseja remover o tipo de widget '{{widgetName}}'?", - "remove-widget-type-text": "Após confirmar, não será possível recuperar o tipo de widget e nenhum dado associado.", - "remove-widget-type": "Remover tipo de widget", "add-widget-type": "Adicionar novo tipo de widget", - "widget-type-load-failed-error": "Erro ao carregar tipo de widget!", "widget-template-load-failed-error": "Erro ao carregar modelo de widget!", "add": "Adicionar widget", "undo": "Desfazer alterações de widget", diff --git a/ui-ngx/src/assets/locale/locale.constant-ro_RO.json b/ui-ngx/src/assets/locale/locale.constant-ro_RO.json index 1cccfa551b..67f8d839be 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ro_RO.json +++ b/ui-ngx/src/assets/locale/locale.constant-ro_RO.json @@ -285,6 +285,7 @@ "attributes": "Atribute", "latest-telemetry": "Ultimele Date Telemetrice", "attributes-scope": "Scop Atribute Entitate", + "scope-telemetry": "Telemetrie", "scope-latest-telemetry": "Ultimele Date Telemetrice", "scope-client": "Atribute Client", "scope-server": "Atribute Server", @@ -743,7 +744,6 @@ "name-starts-with": "Numele începe cu", "use-entity-name-filter": "Folosește filtru", "entity-list-empty": "Nicio entitate selectată", - "entity-type-list-empty": "Niciun tip entitate selectat", "entity-name-filter-required": "Filtrul pentru nume entitate este necesar", "entity-name-filter-no-entity-matched": "Nu a fost găsită nicio entitate al cărei nume începe cu '{{entity}}'", "all-subtypes": "Toate", @@ -1349,7 +1349,6 @@ "name": "Denumire", "name-required": "Denumirea este obligatorie", "type": "Tip", - "description": "Descriere", "delete": "Şterge Regulă Nod", "select-all-objects": "Selectează Toate Nodurile Şi Conexiunile", "deselect-all-objects": "Deselectează Toate Nodurile Şi Conexiunile", @@ -1562,11 +1561,7 @@ "datakey-settings-schema": "Schemă setări chei date", "javascript": "Javascript", "js": "JS", - "remove-widget-type-title": "Sigur vrei să ștergi tip widget '{{widgetName}}'?", - "remove-widget-type-text": "ATENŢIE! După confirmare, tipul de widget şi toate datele aferente acestuia, vor fi șterse IREVERSIBIL!", - "remove-widget-type": "Şterge Tip Widget", "add-widget-type": "Adaugă Tip Nou Widget", - "widget-type-load-failed-error": "Eroare încărcare tip widget!", "widget-template-load-failed-error": "Eroare încarcare şablon widget!", "add": "Adaugă Widget Nou", "undo": "Anulează Modificări Widget", diff --git a/ui-ngx/src/assets/locale/locale.constant-ru_RU.json b/ui-ngx/src/assets/locale/locale.constant-ru_RU.json deleted file mode 100644 index 91fa7355b5..0000000000 --- a/ui-ngx/src/assets/locale/locale.constant-ru_RU.json +++ /dev/null @@ -1,1881 +0,0 @@ -{ - "access": { - "unauthorized": "Неавторизированный", - "unauthorized-access": "Несанкционированный доступ", - "unauthorized-access-text": "Вы должны войти в систему для получения доступа к этому ресурсу!", - "access-forbidden": "Доступ запрещен", - "access-forbidden-text": "У вас нет прав доступа к этому ресурсу!
Для получения доступа попробуйте войти под другим пользователем.", - "refresh-token-expired": "Сессия истекла", - "refresh-token-failed": "Не удалось обновить сессию" - }, - "action": { - "activate": "Активировать", - "suspend": "Приостановить", - "save": "Сохранить", - "saveAs": "Сохранить как", - "cancel": "Отмена", - "ok": "ОК", - "delete": "Удалить", - "add": "Добавить", - "yes": "Да", - "no": "Нет", - "update": "Обновить", - "remove": "Удалить", - "search": "Поиск", - "clear-search": "Очистить", - "assign": "Присвоить", - "unassign": "Отозвать", - "share": "Поделиться", - "make-private": "Закрыть для общего доступа", - "apply": "Применить", - "apply-changes": "Применить изменения", - "edit-mode": "Режим редактирования", - "enter-edit-mode": "Режим редактирования", - "decline-changes": "Отменить изменения", - "close": "Закрыть", - "back": "Назад", - "run": "Запуск", - "sign-in": "Войти", - "edit": "Редактировать", - "view": "Просмотреть", - "create": "Создать", - "drag": "Переместить", - "refresh": "Обновить", - "undo": "Откатить", - "copy": "Копировать", - "paste": "Вставить", - "copy-reference": "Копировать ссылку", - "paste-reference": "Вставить ссылку", - "import": "Импортировать", - "export": "Экспортировать", - "share-via": "Поделиться в {{provider}}", - "continue": "Продолжить", - "discard-changes": "Отменить изменения", - "done": "Завершено" - }, - "aggregation": { - "aggregation": "Агрегация", - "function": "Тип агрегации данных", - "limit": "Максимальное значение", - "group-interval": "Интервал группировки", - "min": "Мин", - "max": "Maкс", - "avg": "Среднее", - "sum": "Сумма", - "count": "Количество", - "none": "Без агрегации" - }, - "admin": { - "general": "Общие", - "general-settings": "Общие настройки", - "outgoing-mail": "Исходящая почта", - "outgoing-mail-settings": "Настройки исходящей почты", - "system-settings": "Системные настройки", - "test-mail-sent": "Пробное письмо успешно отправлено!", - "base-url": "Базовая URL", - "base-url-required": "Базовая URL обязательна.", - "mail-from": "Отправитель", - "mail-from-required": "Отправитель обязателен.", - "smtp-protocol": "SMTP протокол", - "smtp-host": "SMTP хост", - "smtp-host-required": "SMTP хост обязателен.", - "smtp-port": "SMTP порт", - "smtp-port-required": "SMTP порт обязателен.", - "smtp-port-invalid": "Недействительный SMTP порт.", - "timeout-msec": "Таймаут (мс)", - "timeout-required": "Таймаут обязателен.", - "timeout-invalid": "Недействительный таймаут.", - "enable-tls": "Включить TLS", - "tls-version" : "Версия TLS", - "send-test-mail": "Отправить пробное письмо", - "security-settings": "Настройки безопасности", - "password-policy": "Политика паролей", - "minimum-password-length": "Минимальная длина пароля", - "minimum-password-length-required": "Требуется минимальная длина пароля", - "minimum-password-length-range": "Минимальная длина пароля должна быть в диапазоне от 5 до 50", - "minimum-uppercase-letters": "Минимальное количество прописных букв", - "minimum-uppercase-letters-range": "Минимальное количество прописных букв не может быть отрицательным", - "minimum-lowercase-letters": "Минимальное количество строчных букв", - "minimum-lowercase-letters-range": "Минимальное количество строчных букв не может быть отрицательным", - "minimum-digits": "Минимальное количество цифр", - "minimum-digits-range": "Минимальное количество цифр не может быть отрицательным", - "minimum-special-characters": "Минимальное количество специальных символов", - "minimum-special-characters-range": "Минимальное количество специальных символов не может быть отрицательным", - "password-expiration-period-days": "Срок действия пароля в днях", - "password-expiration-period-days-range": "Срок действия пароля в днях не может быть отрицательным", - "password-reuse-frequency-days": "Частота повторного использования пароля в днях", - "password-reuse-frequency-days-range": "Частота повторного использования пароля в днях не может быть отрицательной", - "general-policy": "Общая политика", - "max-failed-login-attempts": "Максимальное количество неудачных попыток входа в систему, прежде чем учетная запись заблокирована", - "minimum-max-failed-login-attempts-range": "Максимальное количество неудачных попыток входа в систему не может быть отрицательным", - "user-lockout-notification-email": "В случае блокировки учетной записи пользователя отправьте уведомление на электронную почту", - "smpp-provider": { - "smpp-version": "SMPP версия", - "smpp-host": "SMPP хост", - "smpp-host-required": "SMPP хост обязателен.", - "smpp-port": "SMPP порт", - "smpp-port-required": "SMPP порт обязателен.", - "system-id": "ИД системи", - "system-id-required": "ИД системи обязателен.", - "password": "Пароль", - "password-required": "Пароль обязателен.", - "type-settings": "Настройки типов", - "source-settings": "Настройки источника", - "destination-settings": "Настройки назначения", - "additional-settings": "Дополнительные настройки", - "system-type": "Тип системы", - "bind-type": "Тип привязки", - "service-type": "Тип обслуживания", - "source-address": "Адрес источника", - "source-ton": "Тип номера источника", - "source-npi": "Идентификация плана нумерации источника", - "destination-ton": "Тип номера назничения", - "destination-npi": "Идентификация плана нумерации назначения", - "address-range": "Диапазон адресов", - "coding-scheme": "Схема кодирования" - } - }, - "alarm": { - "alarm": "Оповещение", - "alarms": "Оповещения", - "select-alarm": "Выбрать оповещение", - "no-alarms-matching": "Оповещения '{{entity}}' не найдены.", - "alarm-required": "Оповещение обязательно", - "alarm-status": "Статус оповещения", - "search-status": { - "ANY": "Все", - "ACTIVE": "Активные", - "CLEARED": "Сброшенные", - "ACK": "Подтвержденные", - "UNACK": "Неподтвержденные" - }, - "display-status": { - "ACTIVE_UNACK": "Активные неподтвержденные", - "ACTIVE_ACK": "Активные подтвержденные", - "CLEARED_UNACK": "Сброшенные неподтвержденные", - "CLEARED_ACK": "Сброшенные подтвержденные" - }, - "no-alarms-prompt": "Оповещения отсутствуют", - "created-time": "Время создания", - "type": "Тип", - "severity": "Уровень", - "originator": "Инициатор", - "originator-type": "Тип инициатора", - "details": "Подробности", - "status": "Статус", - "alarm-details": "Подробности об оповещении", - "start-time": "Время начала", - "end-time": "Время окончания", - "ack-time": "Время подтверждения", - "clear-time": "Время сброса", - "severity-critical": "Критический", - "severity-major": "Основной", - "severity-minor": "Второстепенный", - "severity-warning": "Предупреждение", - "severity-indeterminate": "Неопределенный", - "acknowledge": "Подтвердить", - "clear": "Сбросить", - "search": "Поиск оповещений", - "selected-alarms": "Выбрано { count, plural, =1 {1 оповещение} few {# оповещения} other {# оповещений} }", - "no-data": "Нет данных для отображения", - "polling-interval": "Интервал опроса оповещений (сек)", - "polling-interval-required": "Интервал опроса оповещений обязателен.", - "min-polling-interval-message": "Минимальный интервал опроса оповещений 1 секунда.", - "aknowledge-alarms-title": "Подтвердить { count, plural, =1 {1 оповещение} other {# оповещений} }", - "aknowledge-alarms-text": "Вы точно хотите подтвердить { count, plural, =1 {1 оповещение} other {# оповещений} }?", - "aknowledge-alarm-title": "Подтвердить оповещение", - "aknowledge-alarm-text": "Вы точно хотите подтвердить оповещение?", - "clear-alarms-title": "Сбросить { count, plural, =1 {1 оповещение} other {# оповещений} }", - "clear-alarms-text": "Вы точно хотите сбросить { count, plural, =1 {1 оповещение} other {# оповещений} }?", - "clear-alarm-title": "Сбросить оповещение", - "clear-alarm-text": "Вы точно хотите сбросить оповещение?", - "alarm-status-filter": "Фильтр оповещений", - "max-count-load": "Максимальное количество оповещений для загрузки (0 - неограниченно)", - "max-count-load-required": "Максимальное количество оповещений для загрузки обязателен.", - "max-count-load-error-min": "Минимальное значение 0.", - "fetch-size": "Размер пакета для загрузки", - "fetch-size-required": "Размер пакета для загрузки обязателен.", - "fetch-size-error-min": "Минимальное значение 10." - }, - "alias": { - "add": "Добавить псевдоним", - "edit": "Редактировать псевдоним", - "name": "Псевдоним", - "name-required": "Псевдоним обязателен", - "duplicate-alias": "Такой псевдоним уже существует.", - "filter-type-single-entity": "Отдельный объект", - "filter-type-entity-list": "Список объектов", - "filter-type-entity-name": "Название объекта", - "filter-type-state-entity": "Объект из состояния дашборда", - "filter-type-state-entity-description": "Объект, полученный из параметров состояния дашборда", - "filter-type-asset-type": "Тип актива", - "filter-type-asset-type-description": "Активы типа '{{assetTypes}}'", - "filter-type-asset-type-and-name-description": "Активы типа '{{assetTypes}}' и названием, начинающимся с '{{prefix}}'", - "filter-type-device-type": "Тип устройства", - "filter-type-device-type-description": "Устройства типа '{{deviceTypes}}'", - "filter-type-device-type-and-name-description": "Устройства типа '{{deviceTypes}}' и названием, начинающимся с '{{prefix}}'", - "filter-type-entity-view-type": "Тип Представления Объекта", - "filter-type-entity-view-type-description": "Представления Объекта типа '{{entityViewTypes}}'", - "filter-type-entity-view-type-and-name-description": "Представления Объекта типа '{{entityViewTypes}}' и названием, начинающимся с '{{prefix}}'", - "filter-type-relations-query": "Запрос по типу отношений", - "filter-type-relations-query-description": "{{entities}}, имеющие отношение типа {{relationType}} {{direction}} {{rootEntity}}", - "filter-type-asset-search-query": "Поисковый запрос по активам", - "filter-type-asset-search-query-description": "Активы типа {{assetTypes}}, имеющие отношение типа {{relationType}} {{direction}} {{rootEntity}}", - "filter-type-device-search-query": "Поисковый запрос по устройствам", - "filter-type-device-search-query-description": "Устройства типа {{deviceTypes}}, имеющие отношение типа {{relationType}} {{direction}} {{rootEntity}}", - "filter-type-entity-view-search-query": "Поисковый запрос по представлениям объектов", - "filter-type-entity-view-search-query-description": "Представления объектов типа {{entityViewTypes}}, имеющие отношение типа {{relationType}} {{direction}} {{rootEntity}}", - "entity-filter": "Фильтр объектов", - "resolve-multiple": "Принять как несколько объектов", - "filter-type": "Тип фильтра", - "filter-type-required": "Тип фильтра обязателен.", - "entity-filter-no-entity-matched": "Объекты, соответствующие фильтру, не найдены.", - "no-entity-filter-specified": "Не указан фильтр объектов", - "root-state-entity": "Использовать объект, полученный из дашборда, как корневой", - "last-level-relation": "Использовать только отношения последнего уровня", - "root-entity": "Корневой объект", - "state-entity-parameter-name": "Название объекта состояния", - "default-state-entity": "Объект состояния по умолчанию", - "default-entity-parameter-name": "По умолчанию", - "max-relation-level": "Максимальная глубина отношений", - "unlimited-level": "Неограниченная глубина", - "state-entity": "Объект состояния дашборда", - "all-entities": "Все объекты", - "any-relation": "не указано" - }, - "asset": { - "asset": "Актив", - "assets": "Активы", - "management": "Управление активами", - "view-assets": "Просмотреть активы", - "add": "Добавить актив", - "assign-to-customer": "Присвоить клиенту", - "assign-asset-to-customer": "Присвоить актив(ы) клиенту", - "assign-asset-to-customer-text": "Пожалуйста, выберите активы, которые нужно присвоить объекту", - "no-assets-text": "Активы не найдены", - "assign-to-customer-text": "Пожалуйста, выберите клиента, которому нужно присвоить актив(ы)", - "public": "Общедоступные", - "assignedToCustomer": "Присвоить клиенту", - "make-public": "Открыть общий доступ к активу", - "make-private": "Закрыть общий доступ к активу", - "unassign-from-customer": "Отозвать у клиента", - "delete": "Удалить актив", - "asset-public": "Актив общедоступный", - "asset-type": "Тип актива", - "asset-type-required": "Тип актива обязателен.", - "select-asset-type": "Выберите тип актива", - "enter-asset-type": "Введите тип актива", - "any-asset": "Любой актив", - "no-asset-types-matching": "Активы типа '{{entitySubtype}}' не найдены.", - "asset-type-list-empty": "Типы активов не выбраны.", - "asset-types": "Типы активов", - "name": "Название", - "name-required": "Название обязательно.", - "description": "Описание", - "type": "Тип", - "type-required": "Тип обязателен.", - "details": "Подробности", - "events": "События", - "add-asset-text": "Добавить новый актив", - "asset-details": "Подробности об активе", - "assign-assets": "Присвоить активы", - "assign-assets-text": "Присвоить { count, plural, =1 {1 актив} few {# актива} other {# активов} } клиенту", - "delete-assets": "Удалить активы", - "unassign-assets": "Отозвать активы", - "unassign-assets-action-title": "Отозвать { count, plural, =1 {1 актив} few {# актива} other {# активов} } у клиента", - "assign-new-asset": "Присвоить новый актив", - "delete-asset-title": "Вы точно хотите удалить '{{assetName}}'?", - "delete-asset-text": "Внимание, после подтверждения актив и все связанные с ним данные будут безвозвратно удалены.", - "delete-assets-title": "Вы точно хотите удалить { count, plural, =1 {1 актив} few {# актива} other {# активов} }", - "delete-assets-action-title": "Удалить { count, plural, =1 {1 актив} few {# актива} other {# активов} }", - "delete-assets-text": "Внимание, после подтверждения выбранные активы и все связанные с ними данные будут безвозвратно удалены.", - "make-public-asset-title": "Вы точно хотите открыть общий доступ к активу '{{assetName}}'?", - "make-public-asset-text": "Внимание, после подтверждения актив и все связанные с ним данные станут общедоступными.", - "make-private-asset-title": "Вы точно хотите закрыть общий доступ к активу '{{assetName}}'?", - "make-private-asset-text": "После подтверждения актив и все связанные с ним данные будут закрыты для общего доступа", - "unassign-asset-title": "Вы точно хотите отозвать актив '{{assetName}}'?", - "unassign-asset-text": "После подтверждения актив будут отозван, и клиент потеряет к нему доступ.", - "unassign-asset": "Отозвать актив", - "unassign-assets-title": "Вы точно хотите отозвать { count, plural, =1 {1 актив} few {# актива} other {# активов} }?", - "unassign-assets-text": "После подтверждения активы будут отозваны, и клиент потеряет к ним доступ.", - "copyId": "Копировать ИД актива", - "idCopiedMessage": "ИД актива скопировано в буфер обмена", - "select-asset": "Выбрать активы", - "no-assets-matching": "Активы, соответствующие '{{entity}}', не найдены.", - "asset-required": "Актив обязателен", - "name-starts-with": "Название актива, начинающееся с", - "import": "Импортировать активы", - "asset-file": "Файл с активами", - "label": "Метка" - }, - "attribute": { - "attributes": "Атрибуты", - "latest-telemetry": "Последняя телеметрия", - "attributes-scope": "Контекст атрибутов объекта", - "scope-latest-telemetry": "Последняя телеметрия", - "scope-client": "Клиентские атрибуты", - "scope-server": "Серверные атрибуты", - "scope-shared": "Общие атрибуты", - "add": "Добавить атрибут", - "key": "Ключ", - "last-update-time": "Последнее обновление", - "key-required": "Ключ атрибута обязателен.", - "value": "Значение", - "value-required": "Значение атрибута обязательно.", - "delete-attributes-title": "Вы уверенны, что хотите удалить { count, plural, =1 {1 атрибут} few {# атрибута} other {# атрибутов} }? ", - "delete-attributes-text": "Внимание, после подтверждения выбранные атрибуты будут удалены.", - "delete-attributes": "Удалить атрибуты", - "enter-attribute-value": "Введите значение атрибута", - "show-on-widget": "Показать на виджете", - "widget-mode": "Виджет-режим", - "next-widget": "Следующий виджет", - "prev-widget": "Предыдущий виджет", - "add-to-dashboard": "Добавить на дашборд", - "add-widget-to-dashboard": "Добавить виджет на дашборд", - "selected-attributes": "{ count, plural, =1 {Выбран} other {Выбраны} } { count, plural, =1 {1 атрибут} few {# атрибута} other {# атрибутов} }", - "selected-telemetry": "{ count, plural, =1 {Выбран} other {Выбраны} } { count, plural, =1 {1 параметр} few {# параметра} other {# параметров} } телеметрии" - }, - "audit-log": { - "audit": "Аудит", - "audit-logs": "Логи аудита", - "timestamp": "Время", - "entity-type": "Тип объекта", - "entity-name": "Название объекта", - "user": "Пользователь", - "type": "Тип", - "status": "Статус", - "details": "Подробности", - "type-added": "Добавленный", - "type-deleted": "Удаленный", - "type-updated": "Обновленный", - "type-attributes-updated": "Обновлены атрибуты", - "type-attributes-deleted": "Удалены атрибуты", - "type-rpc-call": "RPC вызов", - "type-credentials-updated": "Обновлены учетные данные", - "type-assigned-to-customer": "Присвоен клиенту", - "type-unassigned-from-customer": "Отозван у клиента", - "type-activated": "Активирован", - "type-suspended": "Приостановлен", - "type-credentials-read": "Чтение учетные данных", - "type-attributes-read": "Чтение атрибутов", - "type-relation-add-or-update": "Обновлены отношения", - "type-relation-delete": "Удалены отношения", - "type-relations-delete": "Удалены все отношения", - "type-alarm-ack": "Подтвержден", - "type-alarm-clear": "Сброшен", - "type-login": "Вход", - "type-logout": "Выход", - "type-lockout": "Заблокирован", - "status-success": "Успех", - "status-failure": "Сбой", - "audit-log-details": "Подробности аудит лога", - "no-audit-logs-prompt": "Логи не найдены", - "action-data": "Данные действия", - "failure-details": "Подробности сбоя", - "search": "Поиск аудит логов", - "clear-search": "Очистить поиск" - }, - "confirm-on-exit": { - "message": "У вас есть несохраненные изменения. Вы точно хотите покинуть эту страницу?", - "html-message": "У вас есть несохраненные изменения.
Вы точно хотите покинуть эту страницу?", - "title": "Несохраненные изменения" - }, - "contact": { - "country": "Страна", - "city": "Город", - "state": "Штат", - "postal-code": "Почтовый код", - "postal-code-invalid": "Допустимы только цифры", - "address": "Адрес", - "address2": "Адрес 2", - "phone": "Телефон", - "email": "Эл. адрес", - "no-address": "Адрес не указан" - }, - "common": { - "username": "Имя пользователя", - "password": "Пароль", - "enter-username": "Введите имя пользователя", - "enter-password": "Введите пароль", - "enter-search": "Введите условие поиска", - "created-time": "Время создания" - }, - "content-type": { - "json": "Json", - "text": "Текстовый", - "binary": "Бинарный (Base64)" - }, - "customer": { - "customer": "Клиент", - "customers": "Клиенты", - "management": "Управление клиентами", - "dashboard": "Дашборд клиента", - "dashboards": "Дашборды клиента", - "devices": "Устройства клиента", - "entity-views": "Представления объектов клиента", - "assets": "Активы клиента", - "public-dashboards": "Общедоступные дашборды", - "public-devices": "Общедоступные устройства", - "public-assets": "Общедоступные активы", - "public-entity-views": "Общедоступные представления объектов", - "add": "Добавить клиента", - "delete": "Удалить клиента", - "manage-customer-users": "Управление пользователями клиента", - "manage-customer-devices": "Управление устройствами клиента", - "manage-customer-dashboards": "Управление дашбордами клиента", - "manage-public-devices": "Управление общедоступными устройствами", - "manage-public-dashboards": "Управление общедоступными дашбордами", - "manage-customer-assets": "Управление активами клиента", - "manage-public-assets": "Управление общедоступными активами", - "add-customer-text": "Добавить нового клиента", - "no-customers-text": "Клиенты не найдены", - "customer-details": "Подробности о клиенте", - "delete-customer-title": "Вы точно хотите удалить клиента '{{customerTitle}}'?", - "delete-customer-text": "Внимание, после подтверждения клиент и все связанные с ним данные будут безвозвратно удалены.", - "delete-customers-title": "Вы точно хотите удалить { count, plural, =1 {1 клиент} few {# клиента} other {# клиентов} }?", - "delete-customers-action-title": "Удалить { count, plural, =1 {1 клиент} few {# клиента} other {# клиентов} }", - "delete-customers-text": "Внимание, после подтверждения выбранные клиенты и все связанные с ними данные будут безвозвратно удалены.", - "manage-users": "Управление пользователями", - "manage-assets": "Управление активами", - "manage-devices": "Управление устройствами", - "manage-dashboards": "Управление дашбордами", - "title": "Имя", - "title-required": "Название обязательно.", - "description": "Описание", - "details": "Подробности", - "events": "События", - "copyId": "Копировать ИД клиента", - "idCopiedMessage": "ИД клиента скопирован в буфер обмена", - "select-customer": "Выбрать клиента", - "no-customers-matching": "Клиенты, соответствующие '{{entity}}', не найдены.", - "customer-required": "Клиент обязателен", - "select-default-customer": "Выбрать клиента по умолчанию", - "default-customer": "Клиент по умолчанию", - "default-customer-required": "Клиент по умолчанию обязателен для отладки дашборда на уровне на уровне Владельца" - }, - "datetime": { - "date-from": "Дата с", - "time-from": "Время с", - "date-to": "Дата до", - "time-to": "Время до" - }, - "dashboard": { - "dashboard": "Дашборд", - "dashboards": "Дашборды", - "management": "Управление дашбордами", - "view-dashboards": "Просмотреть дашборды", - "add": "Добавить дашборд", - "assign-dashboard-to-customer": "Прикрепить дашборд(ы) к клиенту", - "assign-dashboard-to-customer-text": "Пожалуйста, выберите дашборды, которые нужно прикрепить к клиенту", - "assign-to-customer-text": "Пожалуйста, выберите клиента, к которому нужно прикрепить дашборд(ы)", - "assign-to-customer": "Прикрепить к клиенту", - "unassign-from-customer": "Отозвать у клиента", - "make-public": "Открыть дашборд для общего доступа", - "make-private": "Закрыть дашборд для общего доступа", - "manage-assigned-customers": "Управление назначенными клиентами", - "assigned-customers": "Назначенные клиенты", - "assign-to-customers": "Присвоить дашборд(ы) клиенту", - "assign-to-customers-text": "Пожалуйста, выбери клиентов, которым нужно присвоить дашборд(ы)", - "unassign-from-customers": "Отозвать дашборд(ы) у клиентов", - "unassign-from-customers-text": "Пожалуйста, выберите клиентов, у которых нужно отозвать дашборд(ы)", - "no-dashboards-text": "Дашборды не найдены", - "no-widgets": "Виджеты не сконфигурированы", - "add-widget": "Добавить новый виджет", - "title": "Название", - "select-widget-title": "Выберите виджет", - "copyId": "Копировать идентификатор дашборда", - "idCopiedMessage": "Идентификатор дашборда скопирован в буфер обмена", - "select-widget-subtitle": "Список доступных виджетов", - "delete": "Удалить дашборд", - "title-required": "Название обязательно.", - "description": "Описание", - "details": "Подробности", - "dashboard-details": "Подробности о дашборде", - "add-dashboard-text": "Добавить новый дашборд", - "assign-dashboards": "Прикрепить дашборды", - "assign-new-dashboard": "Прикрепить новый дашборд", - "assign-dashboards-text": "Прикрепить { count, plural, =1 {1 дашборд} few {# дашборда} other {# дашбордов} } к клиенту", - "unassign-dashboards-action-text": "Отозвать { count, plural, =1 {1 дашборд} few {# дашборда} other {# дашбордов} } у клиента", - "delete-dashboards": "Удалить дашборды", - "unassign-dashboards": "Отозвать дашборды", - "unassign-dashboards-action-title": "Отозвать { count, plural, =1 {1 дашборд} few {# дашборда} other {# дашбордов} } у клиента", - "delete-dashboard-title": "Вы точно хотите удалить дашборд '{{dashboardTitle}}'?", - "delete-dashboard-text": "Внимание, после подтверждения дашборд и все связанные с ним данные будут безвозвратно утеряны.", - "delete-dashboards-title": "Вы точно хотите удалить { count, plural, =1 {1 дашборд} few {# дашборда} other {# дашбордов} }?", - "delete-dashboards-action-title": "Удалить { count, plural, =1 {1 дашборд} few {# дашборда} other {# дашбордов} }", - "delete-dashboards-text": "Внимание, после подтверждения дашборды и все связанные с ними данные будут безвозвратно утеряны.", - "unassign-dashboard-title": "Вы точно хотите отозвать дашборд '{{dashboardTitle}}'?", - "unassign-dashboard-text": "После подтверждения дашборд не будет доступен клиенту.", - "unassign-dashboard": "Отозвать дашборд", - "unassign-dashboards-title": "Вы точно хотите отозвать { count, plural, =1 {1 дашборд} few {# дашборда} other {# дашбордов} }?", - "unassign-dashboards-text": "После подтверждения выбранные дашборды не будут доступны клиенту.", - "public-dashboard-title": "Теперь дашборд общедоступный", - "public-dashboard-text": "Теперь ваш дашборд {{dashboardTitle}} доступен всем по ссылке:", - "public-dashboard-notice": "Примечание: Для получения доступа к данным устройства нужно открыть общий доступ к этому устройству.", - "make-private-dashboard-title": "Вы точно хотите закрыть общий доступ к дашборду '{{dashboardTitle}}'?", - "make-private-dashboard-text": "После подтверждения дашборд будет закрыт для общего доступа.", - "make-private-dashboard": "Закрыть дашборд для общего доступа", - "socialshare-text": "'{{dashboardTitle}}' сделано ThingsBoard", - "socialshare-title": "'{{dashboardTitle}}' сделано ThingsBoard", - "select-dashboard": "Выберите дашборд", - "no-dashboards-matching": "Дашборд '{{entity}}' не найден.", - "dashboard-required": "Дашборд обязателен.", - "select-existing": "Выберите существующий дашборд", - "create-new": "Создать новый дашборд", - "new-dashboard-title": "Новое название дашборда", - "open-dashboard": "Открыть дашборд", - "set-background": "Установить фон", - "background-color": "Фоновый цвет", - "background-image": "Фоновая картинка", - "background-size-mode": "Размер фона", - "no-image": "Картинка не выбрана", - "drop-image": "Перетащите картинку или кликните для выбора файла.", - "settings": "Настройки", - "columns-count": "Количество колонок", - "columns-count-required": "Количество колонок обязательно.", - "min-columns-count-message": "Минимальное число колонок - 10.", - "max-columns-count-message": "Максимальное число колонок - 1000.", - "widgets-margins": "Величина отступа между виджетами", - "horizontal-margin": "Величина горизонтального отступа", - "horizontal-margin-required": "Величина горизонтального отступа обязательна.", - "min-horizontal-margin-message": "Минимальная величина горизонтального отступа - 0.", - "max-horizontal-margin-message": "Максимальная величина горизонтального отступа - 50.", - "vertical-margin": "Величина вертикального отступа", - "vertical-margin-required": "Величина вертикального отступа обязательна.", - "min-vertical-margin-message": "Минимальная величина вертикального отступа - 0.", - "max-vertical-margin-message": "Максимальная величина вертикального отступа - 50.", - "autofill-height": "Автозаполнение по высоте", - "mobile-layout": "Настройки мобильного режима", - "mobile-row-height": "Высота строки в мобильном режиме, px", - "mobile-row-height-required": "Высота строки в мобильном режиме обязательна.", - "min-mobile-row-height-message": "Минимальная высота строки в мобильном режиме составляет 5 px.", - "max-mobile-row-height-message": "Максимальная высота строки в мобильном режиме составляет 200 px.", - "display-title": "Показать название дашборда", - "toolbar-always-open": "Отображать панель инструментов", - "title-color": "Цвет названия", - "display-dashboards-selection": "Отображать выборку дашбордов", - "display-entities-selection": "Отображать выбору объектов", - "display-dashboard-timewindow": "Показать временное окно", - "display-dashboard-export": "Показать экспорт", - "import": "Импортировать дашборд", - "export": "Экспортировать дашборд", - "export-failed-error": "Не удалось экспортировать дашборд: {{error}}", - "create-new-dashboard": "Создать новый дашборд", - "dashboard-file": "Файл дашборда", - "invalid-dashboard-file-error": "Не удалось импортировать дашборд: неизвестная схема данных дашборда.", - "dashboard-import-missing-aliases-title": "Настроить псевдонимы импортированного дашборда", - "create-new-widget": "Создать новый виджет", - "import-widget": "Импортировать виджет", - "widget-file": "Виджет-файл", - "invalid-widget-file-error": "Не удалось импортировать виджет: неправильный формат данных.", - "widget-import-missing-aliases-title": "Настроить псевдонимы, которые использует импортированный виджет", - "open-toolbar": "Открыть панель инструментов дашборда", - "close-toolbar": "Закрыть панель инструментов", - "configuration-error": "Ошибка в настройках", - "alias-resolution-error-title": "Ошибка в настройках псевдонимов дашборда", - "invalid-aliases-config": "Не удалось найти устройство, соответствующее фильтру псевдонимов.
Пожалуйста, обратитесь к администратору для устранения неполадки.", - "select-devices": "Выберите устройства", - "assignedToCustomer": "Присвоенные клиенту", - "assignedToCustomers": "Присвоенные клиентам", - "public": "Публичный", - "public-link": "Публичная ссылка", - "copy-public-link": "Копировать публичную ссылку", - "public-link-copied-message": "Публичная ссылка на дашборд скопирована в буфер обмена.", - "manage-states": "Управление состоянием дашборда", - "states": "Состояния дашборда", - "search-states": "Поиск состояния дашборда", - "selected-states": "Выбрано { count, plural, =1 {1 состояние} few {# состояния} other {# состояний} } дашборда", - "edit-state": "Изменить состояние дашборда", - "delete-state": "Удалить состояние дашборда", - "add-state": "Добавить состояние дашборда", - "state": "Состояние дашборда", - "state-name": "Название", - "state-name-required": "Название состояния дашборда обязательно.", - "state-id": "ИД состояния", - "state-id-required": "ИД состояния дашборда обязателен.", - "state-id-exists": "Состояния дашборда с таким именем уже существует.", - "is-root-state": "Корневое состояние", - "delete-state-title": "Удалить состояние дашборда", - "delete-state-text": "Вы точно хотите удалить состояние дашборда '{{stateName}}'?", - "show-details": "Показать подробности", - "hide-details": "Скрыть подробности", - "select-state": "Выбрать состояние", - "state-controller": "Контроллер состояния" - }, - "datakey": { - "settings": "Настройки", - "advanced": "Дополнительно", - "label": "Метка", - "color": "Цвет", - "units": "Укажите символы, которые нужно указывать после значения", - "decimals": "Число знаков после запятой", - "data-generation-func": "Функция генерации данных", - "use-data-post-processing-func": "Использовать функцию пост-обработки данных", - "configuration": "Конфигурация ключа данных", - "timeseries": "Телеметрия", - "attributes": "Атрибуты", - "entity-field": "Поле объекта", - "alarm": "Параметры оповещения", - "timeseries-required": "Телеметрия объекта обязательна.", - "timeseries-or-attributes-required": "Телеметрия/атрибуты обязательны.", - "maximum-timeseries-or-attributes": "Максимальное количество параметров телеметрии или атрибутов равно {{count}}", - "alarm-fields-required": "Параметры оповещения обязательны.", - "function-types": "Тип функции", - "function-types-required": "Тип функции обязателен.", - "maximum-function-types": "Максимальное количество типов функции равно {{count}}", - "time-description": "время текущего значения;", - "value-description": "текущее значение;", - "prev-value-description": "результат предыдущего вызова функции;", - "time-prev-description": "время предыдущего значения;", - "prev-orig-value-description": "исходное предыдущее значение;" - }, - "datasource": { - "type": "Тип источника данных", - "name": "Название", - "add-datasource-prompt": "Пожалуйста, добавьте источник данных" - }, - "details": { - "edit-mode": "Режим редактирования", - "edit-json": "Редактировать JSON", - "toggle-edit-mode": "Режим редактирования" - }, - "device": { - "device": "Устройство", - "device-required": "Устройство обязательно.", - "devices": "Устройства", - "management": "Управление устройствами", - "view-devices": "Просмотреть устройства", - "device-alias": "Псевдоним устройства", - "aliases": "Псевдонимы устройства", - "no-alias-matching": "'{{alias}}' не найден.", - "no-aliases-found": "Псевдонимы не найдены.", - "no-key-matching": "'{{key}}' не найден.", - "no-keys-found": "Ключи не найдены.", - "create-new-alias": "Создать новый!", - "create-new-key": "Создать новый!", - "duplicate-alias-error": "Найден дублирующийся псевдоним '{{alias}}'.
В рамках дашборда псевдонимы устройств должны быть уникальными.", - "configure-alias": "Настроить '{{alias}}' псевдоним", - "no-devices-matching": "Устройство '{{entity}}' не найдено.", - "alias": "Псевдоним", - "alias-required": "Псевдоним устройства обязателен.", - "remove-alias": "Удалить псевдоним устройства", - "add-alias": "Добавить псевдоним устройства", - "name-starts-with": "Название начинается с", - "device-list": "Список устройств", - "use-device-name-filter": "Использовать фильтр", - "device-list-empty": "Устройства не выбраны.", - "device-name-filter-required": "Фильтр названия устройства обязателен.", - "device-name-filter-no-device-matched": "Устройства, названия которых начинаются с '{{device}}', не найдены.", - "add": "Добавить устройство", - "assign-to-customer": "Присвоить клиенту", - "assign-device-to-customer": "Присвоить устройство(а) клиенту", - "assign-device-to-customer-text": "Пожалуйста, выберите устройства, которые нужно присвоить клиенту", - "make-public": "Открыть общий доступ к устройству", - "make-private": "Закрыть общий доступ к устройству", - "no-devices-text": "Устройства не найдены", - "assign-to-customer-text": "Пожалуйста, выберите клиента, которому нужно присвоить устройство(а)", - "device-details": "Подробности об устройстве", - "add-device-text": "Добавить новое устройство", - "credentials": "Учетные данные", - "manage-credentials": "Управление учетными данными", - "delete": "Удалить устройство", - "assign-devices": "Присвоить устройство", - "assign-devices-text": "Присвоить { count, plural, =1 {1 устройство} few {# устройства} other {# устройств} } клиенту", - "delete-devices": "Удалить устройства", - "unassign-from-customer": "Отозвать у клиенту", - "unassign-devices": "Отозвать устройства", - "unassign-devices-action-title": "Отозвать у клиента { count, plural, =1 {1 устройство} few {# устройства} other {# устройств} }", - "assign-new-device": "Присвоить новое устройство", - "make-public-device-title": "Вы точно хотите открыть общий доступ к устройству '{{deviceName}}'?", - "make-public-device-text": "После подтверждения устройство и все связанные с ним данные будут общедоступными.", - "make-private-device-title": "Вы точно хотите закрыть общий доступ к устройству '{{deviceName}}'", - "make-private-device-text": "После подтверждения устройство и все связанные с ним данные будут закрыты для общего доступа.", - "view-credentials": "Просмотреть учетные данные", - "delete-device-title": "Вы точно хотите удалить устройство '{{deviceName}}'?", - "delete-device-text": "Внимание, после подтверждения устройство и все связанные с ним данные будут безвозвратно утеряны.", - "delete-devices-title": "Вы точно хотите удалить { count, plural, =1 {1 устройство} few {# устройства} other {# устройств} }?", - "delete-devices-action-title": "Удалить { count, plural, =1 {1 устройство} few {# устройства} other {# устройств} }", - "delete-devices-text": "Внимание, после подтверждения выбранные устройства и все связанные с ними данные будут безвозвратно утеряны..", - "unassign-device-title": "Вы точно хотите отозвать устройство '{{deviceName}}'?", - "unassign-device-text": "После подтверждения устройство будет недоступно клиенту.", - "unassign-device": "Отозвать устройство", - "unassign-devices-title": "Вы точно хотите отозвать { count, plural, =1 {1 устройство} few {# устройства} other {# устройств} }?", - "unassign-devices-text": "После подтверждения выбранные устройства будут недоступны клиенту.", - "device-credentials": "Учетные данные устройства", - "credentials-type": "Тип учетных данных", - "access-token": "Токен", - "access-token-required": "Токен обязателен.", - "access-token-invalid": "Длина токена должна быть от 1 до 32 символов.", - "secret": "Секрет", - "secret-required": "Секрет обязателен.", - "device-type": "Тип устройства", - "device-type-required": "Тип устройства обязатеен.", - "select-device-type": "Выберите тип устройства", - "enter-device-type": "Введите тип устройства", - "any-device": "Любое устройство", - "no-device-types-matching": "Тип устройства, соответствующий '{{entitySubtype}}', не найден.", - "device-type-list-empty": "Не выбран тип устройства.", - "device-types": "Типы устройств", - "name": "Название", - "name-required": "Название обязательно.", - "description": "Описание", - "events": "События", - "details": "Подробности", - "copyId": "Копировать идентификатор устройства", - "copyAccessToken": "Копировать токен", - "idCopiedMessage": "Идентификатор устройства скопирован в буфер обмена", - "accessTokenCopiedMessage": "Токен устройства скопирован в буфер обмена", - "assignedToCustomer": "Присвоен клиенту", - "unable-delete-device-alias-title": "Не удалось удалить псевдоним устройства", - "unable-delete-device-alias-text": "Не удалось удалить псевдоним '{{deviceAlias}}' устройства, т.к. он используется следующими виджетами:
{{widgetsList}}", - "is-gateway": "Гейтвей", - "public": "Общедоступный", - "device-public": "Устройство общедоступно", - "select-device": "Выбрать устройство", - "import": "Импортировать устройства", - "device-file": "Файл с устройствами" - }, - "dialog": { - "close": "Закрыть диалог" - }, - "direction": { - "column": "Колонка", - "row": "Строка" - }, - "error": { - "unable-to-connect": "Не удалось подключиться к серверу! Пожалуйста, проверьте интернет-соединение.", - "unhandled-error-code": "Код необработанной ошибки: {{errorCode}}", - "unknown-error": "Неизвестная ошибка" - }, - "entity": { - "entity": "Объект", - "entities": "Объекты", - "aliases": "Псевдонимы объекта", - "entity-alias": "Псевдоним объекта", - "unable-delete-entity-alias-title": "Не удалось удалить псевдоним объекта", - "unable-delete-entity-alias-text": "Псевдоним объекта '{{entityAlias}}' не может быть удален, так как используется следующим(и) виджетом(ами):
{{widgetsList}}", - "duplicate-alias-error": "Найден дубликат псевдонима '{{alias}}'.
В рамках одного дашборда псевдонимы объектов должны быть уникальными.", - "missing-entity-filter-error": "Отсутствует фильтр для псевдонима '{{alias}}'.", - "configure-alias": "Настроить псевдоним '{{alias}}'", - "alias": "Псевдоним", - "alias-required": "Псевдоним объекта обязателен.", - "remove-alias": "Убрать псевдоним объекта", - "add-alias": "Добавить псевдоним объекта", - "entity-list": "Список объектов", - "entity-type": "Тип объекта", - "entity-types": "Типы объекта", - "entity-type-list": "Список типов объекта", - "any-entity": "Любой объект", - "enter-entity-type": "Введите тип объекта", - "no-entities-matching": "Объекты, соответствующие '{{entity}}', не найдены.", - "no-entity-types-matching": "Типы объектов, соответствующие '{{entityType}}', не найдены.", - "name-starts-with": "Название, начинающееся с", - "use-entity-name-filter": "Использовать фильтр", - "entity-list-empty": "Не выбраны объекты.", - "entity-type-list-empty": "Не выбраны типы объекта.", - "entity-name-filter-required": "Фильтр по названию объекта обязателен.", - "entity-name-filter-no-entity-matched": "Объекты, чье название начинается с '{{entity}}', не найдены.", - "all-subtypes": "Все", - "select-entities": "Выберите объекты", - "no-aliases-found": "Псевдонимы не найдены.", - "no-alias-matching": "Псевдоним '{{alias}}' не найден.", - "create-new-alias": "Создать новый!", - "key": "Ключ", - "key-name": "Название ключа", - "no-keys-found": "Ключ не найден.", - "no-key-matching": "Ключ '{{key}}' не найден.", - "create-new-key": "Создать новый!", - "type": "Тип", - "type-required": "Тип объекта обязателен.", - "type-device": "Устройство", - "type-devices": "Устройства", - "list-of-devices": "{ count, plural, =1 {Одно устройство} other {Список из # устройств} }", - "device-name-starts-with": "Устройства, чьи название начинается с '{{prefix}}'", - "type-asset": "Актив", - "type-assets": "Активы", - "list-of-assets": "{ count, plural, =1 {Один актив} other {Список из # активов} }", - "asset-name-starts-with": "Активы, чьи название начинается с '{{prefix}}'", - "type-entity-view": "Представление Объекта", - "type-entity-views": "Представления Объекта", - "list-of-entity-views": "{ count, plural, =1 {Одно представление объекта} other {Список из # представлений объекта} }", - "entity-view-name-starts-with": "Представления Объекта, чьи название начинается с '{{prefix}}'", - "type-rule": "Правило", - "type-rules": "Правила", - "list-of-rules": "{ count, plural, =1 {Одно правило} other {Список из # правил} }", - "rule-name-starts-with": "Правила, чьи названия начинаются с '{{prefix}}'", - "type-plugin": "Плагин", - "type-plugins": "Плагины", - "list-of-plugins": "{ count, plural, =1 {Один плагин} other {Список из # плагинов} }", - "plugin-name-starts-with": "Плагины, чьи имена начинаются с '{{prefix}}'", - "type-tenant": "Владелец", - "type-tenants": "Владельцы", - "list-of-tenants": "{ count, plural, =1 {Один владелец} other {Список из # владельцев} }", - "tenant-name-starts-with": "Владельцы, чьи имена начинаются с '{{prefix}}'", - "type-customer": "Клиент", - "type-customers": "Клиенты", - "list-of-customers": "{ count, plural, =1 {Один клиент} other {Список из # клиентов} }", - "customer-name-starts-with": "Клиенты, чьи имена начинаются с '{{prefix}}'", - "type-user": "Пользователь", - "type-users": "Пользователи", - "list-of-users": "{ count, plural, =1 {Один пользователь} other {Список из # пользователей} }", - "user-name-starts-with": "Пользователи, чьи имена начинаются с '{{prefix}}'", - "type-dashboard": "Дашборд", - "type-dashboards": "Дашборды", - "list-of-dashboards": "{ count, plural, =1 {Один дашборд} other {Список из # дашбордов} }", - "dashboard-name-starts-with": "Дашборды, чьи названия начинаются с '{{prefix}}'", - "type-alarm": "Оповещение", - "type-alarms": "Оповещения", - "list-of-alarms": "{ count, plural, =1 {Одно оповещение} other {Список из # оповещений} }", - "alarm-name-starts-with": "Оповещения, чьи названия начинаются с '{{prefix}}'", - "type-rulechain": "Цепочка правил", - "type-rulechains": "Цепочки правил", - "list-of-rulechains": "{ count, plural, =1 {Одна цепочка правил} other {Список из # цепочек правил} }", - "rulechain-name-starts-with": "Цепочки правил, чьи названия начинаются с '{{prefix}}'", - "type-rulenode": "Правило", - "type-rulenodes": "Правила", - "list-of-rulenodes": "{ count, plural, =1 {Одно правило} other {Список из # правил} }", - "rulenode-name-starts-with": "Правила, чьи названия начинаются с '{{prefix}}'", - "type-current-customer": "Текущий клиент", - "type-current-tenant": "Текущий владелец", - "search": "Поиск объектов", - "selected-entities": "Выбран(ы) { count, plural, =1 {1 объект} few {# объекта} other {# объектов} }", - "entity-name": "Название объекта", - "entity-label": "Метка объекта", - "details": "Подробности об объекте", - "no-entities-prompt": "Объекты не найдены", - "no-data": "Нет данных для отображения", - "columns-to-display": "Отобразить следующие колонки" - }, - "entity-field": { - "created-time": "Время создания", - "name": "Название", - "type": "Тип", - "first-name": "Имя", - "last-name": "Фамилия", - "email": "Электронная почта", - "title": "Название", - "country": "Страна", - "state": "Штат/Область", - "city": "Город", - "address": "Адрес", - "address2": "Адрес 2", - "zip": "Индекс", - "phone": "Телефон", - "label": "Метка" - }, - "entity-view": { - "entity-view": "Представление Объекта", - "entity-view-required": "Представление объекта обязательно.", - "entity-views": "Представления Объектов", - "management": "Управление представлениями объектов", - "view-entity-views": "Просмотр представлений объектов", - "entity-view-alias": "Псевдоним Представления Объекта", - "aliases": "Псевдонимы Представления Объекта", - "no-alias-matching": "Псевдоним '{{alias}}' не найден.", - "no-aliases-found": "Псевдонимы не найдены.", - "no-key-matching": "Ключ '{{key}}' не найден.", - "no-keys-found": "Ключи не найдены.", - "create-new-alias": "Создать новый!", - "create-new-key": "Создать новый!", - "duplicate-alias-error": "Найден дубликат псевдонима '{{alias}}'.
В рамках одного дашборда псевдонимы представлений объектов должны быть уникальными.", - "configure-alias": "Настроить псевдоним '{{alias}}'", - "no-entity-views-matching": "Объекты, соответствующие '{{entity}}', не найдены.", - "alias": "Псевдоним", - "alias-required": "Псевдоним представления объекта обязателен.", - "remove-alias": "Убрать псевдоним представления объекта", - "add-alias": "Добавить псевдоним представления объекта", - "name-starts-with": "Представления объектов, чьи название начинается с", - "entity-view-list": "Список представлений объектов", - "use-entity-view-name-filter": "Использовать фильтр", - "entity-view-list-empty": "Не выбраны представления объектов.", - "entity-view-name-filter-required": "Для представлений объектов фильтр по названиям обязателен.", - "entity-view-name-filter-no-entity-view-matched": "Представление объектов, чьи название начинаются с '{{entityView}}', не найдены.", - "add": "Представление объекта", - "assign-to-customer": "Назначить клиенту", - "assign-entity-view-to-customer": "Назначить представление(я) объекта(ов) клиенту", - "assign-entity-view-to-customer-text": "Пожалуйста, выберите представления объектов, которые нужно назначить клиенту", - "no-entity-views-text": "Представления объектов не найдены", - "assign-to-customer-text": "Пожалуйста, выберите клиента, которому нужно назначить представления объектов", - "entity-view-details": "Подробности о представлении объекта", - "add-entity-view-text": "Добавить новое представление объекта", - "delete": "Удалить представление объекта", - "assign-entity-views": "Назначить представления объектов", - "assign-entity-views-text": "Назначить клиенту { count, plural, =1 {1 представление объекта} few {# представления объектов} other {# представлений объектов} }", - "delete-entity-views": "Удалить представления объектов", - "unassign-from-customer": "Отозвать у клиента", - "unassign-entity-views": "Отозвать представления объектов", - "unassign-entity-views-action-title": "Отозвать у клиента { count, plural, =1 {1 представление объекта} few {# представления объектов} other {# представлений объектов} }", - "assign-new-entity-view": "Назначит новое представление объекта", - "delete-entity-view-title": "Вы точно хотите удалить представление объекта '{{entityViewName}}'?", - "delete-entity-view-text": "Внимание, после подтверждения представление объекта и все связанные с ним данные будут безвозвратно удалены.", - "delete-entity-views-title": "Вы точно хотите удалить { count, plural, =1 {1 представление объекта} few {# представления объектов} other {# представлений объектов} }?", - "delete-entity-views-action-title": "Удалить { count, plural, =1 {1 представление объекта} few {# представления объектов} other {# представлений объектов} }", - "delete-entity-views-text": "Внимание, после подтверждения выбранные представления объектов и все связанные с ними данные будут безвозвратно удалены.", - "unassign-entity-view-title": "Вы точно хотите отозвать представление объекта '{{entityViewName}}'?", - "unassign-entity-view-text": "После подтверждение представление объекта будет недоступно клиенту.", - "unassign-entity-view": "Отозвать представление объекта", - "unassign-entity-views-title": "Вы точно хотите отозвать { count, plural, =1 {1 представление объекта} few {# представления объектов} other {# представлений объектов} }?", - "unassign-entity-views-text": "После подтверждение выбранные представления объектов будет недоступно клиенту.", - "entity-view-type": "Тип представления объекта", - "entity-view-type-required": "Тип представления объекта обязателен.", - "select-entity-view-type": "Выберите тип представления объекта", - "enter-entity-view-type": "Введите тип представления объекта", - "any-entity-view": "Любое представление объекта", - "no-entity-view-types-matching": "Типы представления объекта, соответствующие '{{entitySubtype}}', не найдены.", - "entity-view-type-list-empty": "Не выбраны типы представления объекта.", - "entity-view-types": "Типы представления объекта", - "name": "Название", - "name-required": "Название обязательно.", - "description": "Описание", - "events": "События", - "details": "Подробности", - "copyId": "Копировать ИД представление объекта", - "assignedToCustomer": "Назначено клиенту", - "unable-entity-view-device-alias-title": "Не удалось удалить псевдоним представления объекта", - "unable-entity-view-device-alias-text": "Не удалось удалить псевдоним устройства '{{entityViewAlias}}', т.к. он используется следующими виджетами:
{{widgetsList}}", - "select-entity-view": "Выбрать представление объекта", - "make-public": "Открыть общий доступ к представлению объекта", - "make-private": "Закрыть общий доступ к представлению объекта", - "start-date": "Дата начала", - "start-ts": "Время начала", - "end-date": "Дата окончания", - "end-ts": "Время окончания", - "date-limits": "Временной лимит", - "client-attributes": "Клиентские атрибуты", - "shared-attributes": "Общие атрибуты", - "server-attributes": "Серверные атрибуты", - "timeseries": "Телеметрия", - "client-attributes-placeholder": "Клиентские атрибуты", - "shared-attributes-placeholder": "Общие атрибуты", - "server-attributes-placeholder": "Серверные атрибуты", - "timeseries-placeholder": "Телеметрия", - "target-entity": "Целевой объект", - "attributes-propagation": "Пробросить атрибуты", - "attributes-propagation-hint": "Представление объекта автоматически копирует указанные атрибуты с Целевого Объекта каждый раз, когда вы сохраняете или обновляете это представление. В целях производительности атрибуты целевого объекта не пробрасываются в представление объекта на каждом их изменении. Вы можете включить автоматический проброс, настроив в вашей цепочке правило \"copy to view\" и соединив его с сообщениями типа \"Post attributes\" и \"Attributes Updated\".", - "timeseries-data": "Данные телеметрии", - "timeseries-data-hint": "Настроить ключи данных телеметрии целевого объекта, которые будут доступны представлению объекта. Эти данные только для чтения.", - "make-public-entity-view-title": "Вы уверенны, что хотите открыть общий доступ к представленю объекта '{{entityViewName}}'?", - "make-public-entity-view-text": "После подтверждения представление объекта и все связанные с ним данные станут публичными и доступными для других пользователей.", - "make-private-entity-view-title": "Вы уверенны, что хотите закрыть общий доступ к представлению объекта '{{entityViewName}}'?", - "make-private-entity-view-text": "После подтверждения представление объекта и все звязанные с ним данные станут приватными и не будут доступны для других пользователей." - }, - "event": { - "event-type": "Тип события", - "type-error": "Ошибка", - "type-lc-event": "Событие жизненного цикла", - "type-stats": "Статистика", - "type-debug-rule-node": "Отладка", - "type-debug-rule-chain": "Отладка", - "no-events-prompt": "События не найдены", - "error": "Ошибка", - "alarm": "Аварийное оповещение", - "event-time": "Время возникновения события", - "server": "Сервер", - "body": "Тело", - "method": "Метод", - "type": "Тип", - "message-id": "ИД сообщения", - "message-type": "Тип сообщения", - "data-type": "Тип данных", - "relation-type": "Тип отношения", - "metadata": "Метаданные", - "data": "Данные", - "event": "Событие", - "status": "Статус", - "success": "Успех", - "failed": "Неудача", - "messages-processed": "Сообщения обработаны", - "errors-occurred": "Возникли ошибки", - "all-events": "Все", - "entity-type": "Тип объекта", - "clear-request-title": "Удалить все события", - "clear-request-text": "Вы точно хотите удалить все события?" - }, - "extension": { - "extensions": "Расширение", - "selected-extensions": "Выбрано { count, plural, =1 {1 расширение} few {# расширения} other {# расширений} }", - "type": "Тип", - "key": "Ключ", - "value": "Значение", - "id": "ИД", - "extension-id": "ИД расширения", - "extension-type": "Тип расширения", - "transformer-json": "JSON *", - "unique-id-required": "Такое ИД расширения уже существует.", - "delete": "Удалить расширение", - "add": "Добавить расширение", - "edit": "Редактировать расширение", - "delete-extension-title": "Вы точно хотите удалить расширение '{{extensionId}}'?", - "delete-extension-text": "Внимание, после подтверждения расширение и все связанные с ним данные будут безвозвратно удалены.", - "delete-extensions-title": "Вы точно хотите удалить { count, plural, =1 {1 расширение} few {# расширения} other {# расширений} }?", - "delete-extensions-text": "Внимание, после подтверждения выбранные расширения будут удалены.", - "converters": "Конвертеры", - "converter-id": "ИД конвертера", - "configuration": "Конфигурация", - "converter-configurations": "Конфигурация конвертера", - "token": "Токен безопасности", - "add-converter": "Добавить конвертер", - "add-config": "Добавить конфигурацию конвертера", - "device-name-expression": "Маска названия устройства", - "device-type-expression": "Маска типа устройства", - "custom": "Пользовательский", - "to-double": "To Double", - "transformer": "Преобразователь", - "json-required": "JSON преобразователя обязателен.", - "json-parse": "Не удалось распознать JSON преобразователя.", - "attributes": "Атрибуты", - "add-attribute": "Добавить атрибут", - "add-map": "Добавить элемент сопоставления", - "timeseries": "Телеметрия", - "add-timeseries": "Добавить параметр телеметрии", - "field-required": "Параметр обязателен", - "brokers": "Брокеры", - "add-broker": "Добавить брокер", - "host": "Хост", - "port": "Порт", - "port-range": "Значение порта лежит в диапазоне от 1 до 65535.", - "ssl": "SSL", - "credentials": "Учетные данные", - "username": "Имя пользователя", - "password": "Пароль", - "retry-interval": "Интервал повтора в миллисекундах", - "anonymous": "Анонимный", - "basic": "Общий", - "pem": "PEM", - "ca-cert": "Файл CA сертификата *", - "private-key": "Файл приватного ключа *", - "cert": "Файл сертификата *", - "no-file": "Не выбран файл.", - "drop-file": "Перетяните файл или нажмите для выбора файла.", - "mapping": "Сопоставление", - "topic-filter": "Фильтр тем", - "converter-type": "Тип конвертера", - "converter-json": "JSON", - "json-name-expression": "JSON выражение для названия устройства", - "topic-name-expression": "Выражение для названия устройства в названии темы", - "json-type-expression": "JSON выражение для типа устройства", - "topic-type-expression": "Выражение для типа устройства в названии темы", - "attribute-key-expression": "Выражение для атрибута", - "attr-json-key-expression": "JSON выражение для атрибута", - "attr-topic-key-expression": "Выражение для атрибута в названии темы", - "request-id-expression": "Выражение для ИД запроса", - "request-id-json-expression": "JSON выражение для ИД запроса", - "request-id-topic-expression": "Выражение для ИД запроса в названии темы", - "response-topic-expression": "Выражение для темы ответов", - "value-expression": "Выражение для значения", - "topic": "Тема", - "timeout": "Таймаут в миллисекундах", - "converter-json-required": "JSON конвертер обязателен.", - "converter-json-parse": "Не удалось распознать JSON конвертера.", - "filter-expression": "Выражение для фильтрации", - "connect-requests": "Запросы о подключении устройства", - "add-connect-request": "Добавить запросы о подключении устройства", - "disconnect-requests": "Запросы об отсоединении устройства", - "add-disconnect-request": "Добавить запрос об отсоединении устройства", - "attribute-requests": "Запросы для атрибутов", - "add-attribute-request": "Добавить запрос для атрибутов", - "attribute-updates": "Обновление атрибутов", - "add-attribute-update": "Добавить обновление атрибутов", - "server-side-rpc": "Серверный RPC", - "add-server-side-rpc-request": "Добавить серверный RPC", - "device-name-filter": "Фильтр для названия устройства", - "attribute-filter": "Фильтр для атрибутов", - "method-filter": "Фильтр для процедур", - "request-topic-expression": "Выражение для темы запросов", - "response-timeout": "Время ожидания ответа в миллисекундах", - "topic-expression": "Выражение для названия темы", - "client-scope": "Клиентский", - "add-device": "Добавить устройство", - "opc-server": "Серверы", - "opc-add-server": "Добавить сервер", - "opc-add-server-prompt": "Пожалуйста, добавьте сервер", - "opc-application-name": "Название приложения", - "opc-application-uri": "URI приложения", - "opc-scan-period-in-seconds": "Частота сканирования в секундах", - "opc-security": "Безопасность", - "opc-identity": "Идентификация", - "opc-keystore": "Хранилище ключей", - "opc-type": "Тип", - "opc-keystore-type": "Тип", - "opc-keystore-location": "Расположение *", - "opc-keystore-password": "Пароль", - "opc-keystore-alias": "Псевдоним", - "opc-keystore-key-password": "Пароль для ключ", - "opc-device-node-pattern": "Паттерн OPC узла устройства", - "opc-device-name-pattern": "Паттерн названия устройства", - "modbus-server": "Серверы/ведомые устройства", - "modbus-add-server": "Добавить сервер/ведомое устройство", - "modbus-add-server-prompt": "Пожалуйста, добавить сервер/ведомое устройство", - "modbus-transport": "Транспорт", - "modbus-tcp-reconnect": "Переподключатсься автоматически", - "modbus-port-name": "Название последовательного порта", - "modbus-encoding": "Кодирование символов", - "modbus-parity": "Паритет", - "modbus-baudrate": "Скорость передачи", - "modbus-databits": "Биты данных", - "modbus-stopbits": "Стоп-биты", - "modbus-databits-range": "Параметр \"Биты данных\" может принимать значения 7 или 8.", - "modbus-stopbits-range": "Параметр \"Стоп-биты\" может принимать значения 1 или 2.", - "modbus-unit-id": "ИД устройства", - "modbus-unit-id-range": "ИД устройства должен быть в диапазоне от 1 до 247.", - "modbus-device-name": "Название устройства", - "modbus-poll-period": "Частота опроса (в миллисекундах)", - "modbus-attributes-poll-period": "Частота опроса для атрибутов (в миллисекундах)", - "modbus-timeseries-poll-period": "Частота опроса для телеметрии (в миллисекундах)", - "modbus-poll-period-range": "Значение параметра \"Частота опроса\" должно быть больше ноля.", - "modbus-tag": "Тег", - "modbus-function": "Modbus функция", - "modbus-register-address": "Адрес регистра", - "modbus-register-address-range": "Адрес регистра должен быть в диапазоне от 0 до 65535.", - "modbus-register-bit-index": "Номер бита", - "modbus-register-bit-index-range": "Номер бита должен быть в диапазоне от 0 до 15.", - "modbus-register-count": "Количество регистров", - "modbus-register-count-range": "Количество регистров должно быть больше ноля.", - "modbus-byte-order": "Порядок байтов", - "sync": { - "status": "Статус", - "sync": "Синхронизирован", - "not-sync": "Не синхронизирован", - "last-sync-time": "Время последней синхронизации", - "not-available": "Не доступен" - }, - "export-extensions-configuration": "Экспортировать конфигурацию расширений", - "import-extensions-configuration": "Импортировать конфигурацию расширений", - "import-extensions": "Импортировать расширения", - "import-extension": "Импортировать расширение", - "export-extension": "Экспортировать расширение", - "file": "Файл расширений", - "invalid-file-error": "Не правильный формат файла" - }, - "fullscreen": { - "expand": "Во весь экран", - "exit": "Выйти из полноэкранного режима", - "toggle": "Во весь экран", - "fullscreen": "Полноэкранный режим" - }, - "function": { - "function": "Функция" - }, - "grid": { - "delete-item-title": "Вы точно хотите удалить этот объект?", - "delete-item-text": "Внимание, после подтверждения объект и все связанные с ним данные будут безвозвратно утеряны.", - "delete-items-title": "Вы точно хотите удалить { count, plural, =1 {1 объект} few {# объекта} other {# объектов} }?", - "delete-items-action-title": "Удалить { count, plural, =1 {1 объект} few {# объекта} other {# объектов} }", - "delete-items-text": "Внимание, после подтверждения выбранные объекты и все связанные с ними данные будут безвозвратно утеряны.", - "add-item-text": "Добавить новый объект", - "no-items-text": "Объекты не найдены", - "item-details": "Подробности об объекте", - "delete-item": "Удалить объект", - "delete-items": "Удалить объекты", - "scroll-to-top": "Прокрутка к началу" - }, - "help": { - "goto-help-page": "Перейти к справке" - }, - "home": { - "home": "Главная", - "profile": "Профиль", - "logout": "Выйти из системы", - "menu": "Меню", - "avatar": "Аватар", - "open-user-menu": "Открыть меню пользователя" - }, - "import": { - "no-file": "Файл не выбран", - "drop-file": "Перетащите JSON файл или кликните для выбора файла.", - "drop-file-csv": "Перетащите CSV файл или кликните для выбора файла.", - "column-value": "Значение", - "column-title": "Название", - "column-example": "Пример значений данных", - "column-key": "Ключ атрибута/телеметрии", - "csv-delimiter": "Разделитель в CSV файле", - "csv-first-line-header": "Первая строка содержит названия колонок", - "csv-update-data": "Обновить атрибут/телеметрию", - "import-csv-number-columns-error": "Файл должен содержать как минимум две колонки", - "import-csv-invalid-format-error": "Неверный формат данных. Строка: '{{line}}'", - "column-type": { - "name": "Название", - "type": "Тип", - "label": "Метка", - "column-type": "Тип колонки", - "client-attribute": "Клиентский атрибут", - "shared-attribute": "Общий атрибут", - "server-attribute": "Серверный атрибут", - "timeseries": "Телеметрия", - "entity-field": "Entity field", - "access-token": "Токен" - }, - "stepper-text": { - "select-file": "Выберите файл", - "configuration": "Конфигурация импорта", - "column-type": "Выберите тип колонок", - "creat-entities": "Создание новых объектов" - }, - "message": { - "create-entities": "{{count}} новый(х) объект(ов) было успешно создано.", - "update-entities": "{{count}} объект(ов) успешно обновлено.", - "error-entities": "Возникла ошибка при создании {{count}} объекта(ов)." - } - }, - "item": { - "selected": "Выбранные" - }, - "js-func": { - "no-return-error": "Функция должна возвращать значение!", - "return-type-mismatch": "Функция должна возвращать значение типа '{{type}}'!" - }, - "key-val": { - "key": "Ключ", - "value": "Значение", - "remove-entry": "Удалить элемент", - "add-entry": "Добавить элемент", - "no-data": "Элементы отсутствуют" - }, - "layout": { - "layout": "Макет", - "manage": "Управление макетами", - "settings": "Настройки макета", - "color": "Цвет", - "main": "Основной", - "right": "Правый", - "select": "Выбрать макет" - }, - "legend": { - "direction": "Расположение элементов легенды", - "position": "Расположение легенды", - "show-max": "Показать максимальное значение", - "show-min": "Показать минимальное значение", - "show-avg": "Показать среднее значение", - "show-total": "Показать сумму", - "settings": "Настройки легенды", - "min": "Мин", - "max": "Макс", - "avg": "Среднее", - "total": "Сумма", - "comparison-time-ago": { - "days": "(день назад)", - "weeks": "(неделю назад)", - "months": "(месяц назад)", - "years": "(год назад)" - } - }, - "login": { - "login": "Войти", - "request-password-reset": "Запрос на сброс пароля", - "reset-password": "Сбросить пароль", - "create-password": "Создать пароль", - "passwords-mismatch-error": "Введенные пароли должны быть одинаковыми!", - "password-again": "Введите пароль еще раз", - "sign-in": "Пожалуйста, войдите в систему", - "username": "Имя пользователя (эл. адрес)", - "remember-me": "Запомнить меня", - "forgot-password": "Забыли пароль?", - "password-reset": "Пароль сброшен", - "expired-password-reset-message": "Срок действия Вашего пароля закончился! Пожалуйста, создайте новый пароль.", - "new-password": "Новый пароль", - "new-password-again": "Повторите новый пароль", - "password-link-sent-message": "Ссылка для сброса пароля была успешно отправлена!", - "email": "Эл. адрес", - "login-with": "Войти через {{name}}", - "or": "или" - }, - "position": { - "top": "Верх", - "bottom": "Низ", - "left": "Левый край", - "right": "Правый край" - }, - "profile": { - "profile": "Профиль", - "last-login-time": "Время последнего входа в систему", - "change-password": "Изменить пароль", - "current-password": "Текущий пароль", - "copy-jwt-token": "Копировать JWT токен", - "tokenCopiedMessage": "JWT токен скопирован в буфер обмена", - "tokenCopiedWarnMessage": "JWT токен недействителен! Перезагрузите страницу." - }, - "relation": { - "relations": "Отношения", - "direction": "Направления", - "search-direction": { - "FROM": "От", - "TO": "К" - }, - "direction-type": { - "FROM": "от", - "TO": "к" - }, - "from-relations": "Исходящие отношения", - "to-relations": "Входящие отношения", - "selected-relations": "Выбрано { count, plural, =1 {1 отношение} few {# отношения} other {# отношений} }", - "type": "Тип", - "to-entity-type": "К типу объекта", - "to-entity-name": "К объекта", - "from-entity-type": "От типа объекта", - "from-entity-name": "От объекта", - "to-entity": "К объекту", - "from-entity": "От объекта", - "delete": "Удалить отношение", - "relation-type": "Тип отношения", - "relation-type-required": "Тип отношения обязателен.", - "any-relation-type": "Любой тип", - "add": "Добавить отношение", - "edit": "Редактировать отношение", - "delete-to-relation-title": "Вы точно хотите удалить отношение, идущее к объекту '{{entityName}}'?", - "delete-to-relation-text": "Внимание, после подтверждения объект '{{entityName}}' будет отвязан от текущего объекта.", - "delete-to-relations-title": "Вы точно хотите удалить { count, plural, =1 {1 отношение} few {# отношения} other {# отношений} }?", - "delete-to-relations-text": "Внимание, после подтверждения выбранные объекты будут отвязаны от текущего объекта.", - "delete-from-relation-title": "Вы точно хотите удалить отношение, идущее от объекта '{{entityName}}'?", - "delete-from-relation-text": "Внимание, после подтверждения текущий объект будет отвязан от объекта '{{entityName}}'.", - "delete-from-relations-title": "Вы точно хотите удалить { count, plural, =1 {1 отношение} few {# отношения} other {# отношений} }?", - "delete-from-relations-text": "Внимание, после подтверждения выбранные объекты будут отвязаны от соответствующих объектов.", - "remove-relation-filter": "Удалить фильтр отношений", - "add-relation-filter": "Добавить фильтр отношений", - "any-relation": "Любое отношение", - "relation-filters": "Фильтры отношений", - "additional-info": "Дополнительная информация (JSON)", - "invalid-additional-info": "Не удалось распознать JSON с дополнительной информацией." - }, - "rulechain": { - "rulechain": "Цепочка правил", - "rulechains": "Цепочки правил", - "root": "Корневая", - "delete": "Удалить цепочку правил", - "name": "Названия", - "name-required": "Название необходимо.", - "description": "Описание", - "add": "Добавить цепочку правил", - "set-root": "Сделать цепочку корневой", - "set-root-rulechain-title": "Вы точно хотите сделать цепочку правил '{{ruleChainName}}' корневой?", - "set-root-rulechain-text": "После подтверждения цепочка правил станет корневой и будет обрабатывать все входящие сообщения.", - "delete-rulechain-title": "Вы точно хотите удалить цепочку правил '{{ruleChainName}}'?", - "delete-rulechain-text": "Внимание, после подтверждения цепочка правил и все связанные с ней данные будут безвозвратно удалены.", - "delete-rulechains-title": "Вы точно хотите удалить { count, plural, =1 {1 цепочку правил} few {# цепочки правил} other {# цепочек правил} }?", - "delete-rulechains-action-title": "Удалить { count, plural, =1 {1 цепочку правил} few {# цепочки правил} other {# цепочек правил} }", - "delete-rulechains-text": "Внимание, после подтверждения выбранные цепочки правил и все связанные с ними данные будут безвозвратно удалены.", - "add-rulechain-text": "Добавить новую цепочку правил", - "no-rulechains-text": "Цепочки правил не найдены", - "rulechain-details": "Подробности о цепочке правил", - "details": "Подробности", - "events": "События", - "system": "Системная", - "import": "Импортировать цепочку правил", - "export": "Экспортировать цепочку правил", - "export-failed-error": "Не удалось экспортировать цепочку правил: {{error}}", - "create-new-rulechain": "Создать новую цепочку правил", - "rulechain-file": "Файл цепочки правил", - "invalid-rulechain-file-error": "Не удалось импортировать цепочку правил: неправильный формат.", - "copyId": "Копировать ИД цепочки правил", - "idCopiedMessage": "ИД цепочки правил скопирован в буфер обмена", - "select-rulechain": "Выбрать цепочку правил", - "no-rulechains-matching": "Цепочки правил, соответствующие '{{entity}}', не найдены.", - "rulechain-required": "Цепочка правил обязательна", - "management": "Управление цепочками правил", - "debug-mode": "Режим отладки" - }, - "rulenode": { - "details": "Подробности", - "events": "События", - "search": "Поиск правил", - "open-node-library": "Открыть библиотеку правил", - "add": "Добавить правило", - "name": "Название", - "name-required": "Название обязательно.", - "type": "Тип", - "description": "Описание", - "delete": "Удалить правило", - "select-all-objects": "Выделить все правила и связи", - "deselect-all-objects": "Отменить выделение правил и связей", - "delete-selected-objects": "Удалить выделенные правила и связи", - "delete-selected": "Удалить выделенные", - "select-all": "Выделить всё", - "copy-selected": "Копировать выделенное", - "deselect-all": "Отменить выделение", - "rulenode-details": "Подробности о правиле", - "debug-mode": "Режим отладки", - "configuration": "Настройки", - "link": "Связь", - "link-details": "Подробности о связи правила", - "add-link": "Добавить связь", - "link-label": "Метка связи", - "link-label-required": "Метка связи обязателен.", - "custom-link-label": "Пользовательская метка связи", - "custom-link-label-required": "Пользовательская метка связи обязателен.", - "link-labels": "Метки связи", - "link-labels-required": "Метки связи обязательны.", - "no-link-labels-found": "Метки связи не найдены", - "no-link-label-matching": "Метка '{{label}}' не найдена.", - "create-new-link-label": "Создать новую!", - "type-filter": "Фильтр", - "type-filter-details": "Фильтр входящих сообщений с заданными условиями", - "type-enrichment": "Насыщение", - "type-enrichment-details": "Добавить данные в метадату сообщения", - "type-transformation": "Преобразование", - "type-transformation-details": "Изменить содержимое сообщение и его метадату", - "type-action": "Действие", - "type-action-details": "Выполнить заданное действие", - "type-external": "Сторонние", - "type-external-details": "Взаимодействовать со сторонними системами", - "type-rule-chain": "Цепочка правил", - "type-rule-chain-details": "Перенаправить входящее сообщение в другую цепочку правил", - "type-input": "Вход", - "type-input-details": "Логический вход цепочки правил перенаправляет входящие сообщения в следующее правило", - "type-unknown": "Неизвестный", - "type-unknown-details": "Неопределенное правило", - "directive-is-not-loaded": "Указанная директива конфигурации '{{directiveName}}' не доступна.", - "ui-resources-load-error": "Не удалось загрузить UI ресурсы.", - "invalid-target-rulechain": "Не удалось определить целевую цепочку правил!", - "test-script-function": "Протестировать скрипт", - "message": "Сообщение", - "message-type": "Тип сообщения", - "select-message-type": "Выбрать тип сообщения", - "message-type-required": "Тип сообщения обязателен", - "metadata": "Метаданные", - "metadata-required": "Метаданные объекта не могут быть пустыми.", - "output": "Выход", - "test": "Протестировать", - "help": "Помощь", - "reset-debug-mode": "Сбросить режим отладки во всех правилах" - }, - "queue": { - "select_name": "Выберите имя для Queue", - "name": "Имя для Queue", - "name_required": "Поле 'Имя для Queue' обязательно к заполнению!" - }, - "tenant": { - "tenant": "Владелец", - "tenants": "Владельцы", - "management": "Управление владельцами", - "add": "Добавить владельца", - "admins": "Администраторы", - "manage-tenant-admins": "Управление администраторами владельца", - "delete": "Удалить владельца", - "add-tenant-text": "Добавить нового владельца", - "no-tenants-text": "Владельцы не найдены", - "tenant-details": "Подробности об владельце", - "delete-tenant-title": "Вы точно хотите удалить владельца '{{tenantTitle}}'?", - "delete-tenant-text": "Внимание, после подтверждения владелец и все связанные с ним данные будут безвозвратно утеряны.", - "delete-tenants-title": "Вы точно хотите удалить { count, plural, =1 {1 владельца} other {# владельцев} }?", - "delete-tenants-action-title": "Удалить { count, plural, =1 {1 владельца} other {# владельцев} }", - "delete-tenants-text": "Внимание, после подтверждения выбранные Владельцы и все связанные с ними данные будут безвозвратно утеряны.", - "title": "Имя", - "title-required": "Имя обязательно.", - "description": "Описание", - "details": "Подробности", - "events": "События", - "copyId": "Копировать ИД владельца", - "idCopiedMessage": "ИД владельца скопирован в буфер обмена", - "select-tenant": "Выбрать владельца", - "no-tenants-matching": "Владельцы, соответствующие '{{entity}}', не найдены.", - "tenant-required": "Владелец обязателен" - }, - "timeinterval": { - "seconds-interval": "{ seconds, plural, =1 {1 секунда} few {# секунды} other {# секунд} }", - "minutes-interval": "{ minutes, plural, =1 {1 минута} few {# минуты} other {# минут} }", - "hours-interval": "{ hours, plural, =1 {1 час} few {# часа} other {# часов} }", - "days-interval": "{ days, plural, =1 {1 день} few {# дня} other {# дней} }", - "days": "Дни", - "hours": "Часы", - "minutes": "Минуты", - "seconds": "Секунды", - "advanced": "Дополнительно" - }, - "timewindow": { - "days": "{ days, plural, =1 {1 день} few {# дня} other {# дней} }", - "hours": "{ hours, plural, =1 {1 час} few {# часа} other {# часов} }", - "minutes": "{ minutes, plural, =1 {1 минута} few {# минуты} other {# минут} }", - "seconds": "{ seconds, plural, =1 {1 секунда} few {# секунды} other {# секунд} }", - "realtime": "Режим реального времени", - "history": "История", - "last-prefix": "Последние", - "period": "с {{ startTime }} до {{ endTime }}", - "edit": "Изменить временное окно", - "date-range": "Диапазон дат", - "last": "Последние", - "time-period": "Период времени", - "hide": "Скрыть" - }, - "user": { - "user": "Пользователь", - "users": "Пользователи", - "customer-users": "Пользователи клиента", - "tenant-admins": "Администраторы владельца", - "sys-admin": "Системный администратор", - "tenant-admin": "Администратор владельца", - "customer": "Клиент", - "anonymous": "Аноним", - "add": "Добавить пользователя", - "delete": "Удалить пользователя", - "add-user-text": "Добавить нового пользователя", - "no-users-text": "Пользователи не найдены", - "user-details": "Подробности о пользователе", - "delete-user-title": "Вы точно хотите удалить пользователя '{{userEmail}}'?", - "delete-user-text": "Внимание, после подтверждения пользователь и все связанные с ним данные будут безвозвратно утеряны.", - "delete-users-title": "Вы точно хотите удалить { count, plural, =1 {1 пользователя} other {# пользователей} }?", - "delete-users-action-title": "Удалить { count, plural, =1 {1 пользователя} other {# пользователей} }", - "delete-users-text": "Внимание, после подтверждения выбранные пользователи и все связанные с ними данные будут безвозвратно утеряны.", - "activation-email-sent-message": "Активационное письмо успешно отправлено!", - "resend-activation": "Повторить отправку активационного письма", - "email": "Эл. адрес", - "email-required": "Эл. адрес обязателен.", - "invalid-email-format": "Неправильный формат эл. адреса'.", - "first-name": "Имя", - "last-name": "Фамилия", - "description": "Описание", - "default-dashboard": "Дашборд по умолчанию", - "always-fullscreen": "Всегда в полноэкранном режиме", - "select-user": "Выбрать пользователя", - "no-users-matching": "Пользователи, соответствующие '{{entity}}', не найдены.", - "user-required": "Необходимо указать пользователя", - "activation-method": "Метод активации", - "display-activation-link": "Отобразить ссылку для активации", - "send-activation-mail": "Отправить активационное письмо", - "activation-link": "Активационная ссылка для пользователя", - "activation-link-text": "Для активации пользователя используйте ссылку :", - "copy-activation-link": "Копировать активационную ссылку", - "activation-link-copied-message": "Ссылка для активации пользователя скопирована в буфер обмена", - "details": "Подробности", - "login-as-tenant-admin": "Войти как администратор владельца", - "login-as-customer-user": "Войти как пользователь клиента", - "disable-account": "Отключить учетную запись пользователя", - "enable-account": "Включить учетную запись пользователя", - "enable-account-message": "Учетная запись пользователя была успешно включена!", - "disable-account-message": "Учетная запись пользователя была успешно отключена!", - "copyId": "Копировать ИД пользователя", - "idCopiedMessage": "ИД пользователя скопирован в буфер обмена" - }, - "value": { - "type": "Тип значения", - "string": "Строка", - "string-value": "Строковое значение", - "integer": "Целое число", - "integer-value": "Целочисленное значение", - "invalid-integer-value": "Неправильный формат целого числа", - "double": "Число двойной точности", - "double-value": "Значение двойной точности", - "boolean": "Логический тип", - "boolean-value": "Логическое значение", - "false": "Ложь", - "true": "Правда", - "long": "Целое число" - }, - "widget": { - "widget-library": "Галерея виджетов", - "widget-bundle": "Набор виджетов", - "select-widgets-bundle": "Выберите набор виджетов", - "management": "Управление виджетами", - "editor": "Редактор виджетов", - "widget-type-not-found": "Ошибка при загрузке конфигурации виджета.
Возможно, связанный с ней\n тип виджета уже удален.", - "widget-type-load-error": "Не удалось загрузить виджет по следующим причинам:", - "remove": "Удалить виджет", - "edit": "Редактировать виджет", - "remove-widget-title": "Вы точно хотите удалить виджет '{{widgetTitle}}'?", - "remove-widget-text": "Внимание, после подтверждения виджет и все связанные с ним данные будут безвозвратно утеряны.", - "timeseries": "Телеметрия", - "search-data": "Поиск данных", - "no-data-found": "Данные не найдено", - "latest": "Последние значения", - "rpc": "Управляющий виджет", - "alarm": "Виджет оповещений", - "static": "Статический виджет", - "select-widget-type": "Выберите тип виджета", - "missing-widget-title-error": "Укажите название виджета!", - "widget-saved": "Виджет сохранен", - "unable-to-save-widget-error": "Не удалось сохранить виджет! Виджет содержит ошибки!", - "save": "Сохранить виджет", - "saveAs": "Сохранить виджет как", - "save-widget-type-as": "Сохранить тип виджета как", - "save-widget-type-as-text": "Пожалуйста, введите название виджета и/или укажите целевой набор виджетов", - "toggle-fullscreen": "Во весь экран", - "run": "Запустить виджет", - "title": "Название виджета", - "title-required": "Название виджета обязательно.", - "type": "Тип виджета", - "resources": "Ресурсы", - "resource-url": "JavaScript/CSS URL", - "remove-resource": "Удалить ресурс", - "add-resource": "Добавить ресурс", - "html": "HTML", - "tidy": "Форматировать", - "css": "CSS", - "settings-schema": "Схема конфигурации", - "datakey-settings-schema": "Схема конфигурации ключа данных", - "javascript": "Javascript", - "remove-widget-type-title": "Вы точно хотите удалить виджет '{{widgetName}}'?", - "remove-widget-type-text": "Внимание, после подтверждения тип виджета и все связанные с ним данные будут безвозвратно утеряны.", - "remove-widget-type": "Удалить тип виджета", - "add-widget-type": "Добавить новый тип виджета", - "widget-type-load-failed-error": "Не удалось загрузить тип виджета!", - "widget-template-load-failed-error": "Не удалось загрузить шаблон виджета!", - "add": "Добавить виджет", - "undo": "Откатить изменения в виджете", - "export": "Экспортировать виджет" - }, - "widget-action": { - "header-button": "Кнопка заголовка виджета", - "open-dashboard-state": "Перейти к новому состоянию дашборда", - "update-dashboard-state": "Обновить текущее состояние дашборда", - "open-dashboard": "Перейти к другому дашборду", - "custom": "Пользовательское действие", - "custom-pretty": "Пользовательское действие (с HTML шаблоном)", - "target-dashboard-state": "Целевое состояние дашборда", - "target-dashboard-state-required": "Целевое состояние дашборда обязательно", - "set-entity-from-widget": "Установить объект из виджета", - "target-dashboard": "Целевой дашборд", - "open-right-layout": "Открыть мобильный режим дашборда" - }, - "widgets-bundle": { - "current": "Текущий набор", - "widgets-bundles": "Наборы виджетов", - "add": "Добавить набор виджетов", - "delete": "Удалить набор виджетов", - "title": "Название", - "title-required": "Название обязательно.", - "add-widgets-bundle-text": "Добавить новый набор виджетов", - "no-widgets-bundles-text": "Наборы виджетов не найдены", - "empty": "Пустой набор виджетов", - "details": "Подробности", - "widgets-bundle-details": "Подробности о наборе виджетов", - "delete-widgets-bundle-title": "Вы точно хотите удалить набор виджетов '{{widgetsBundleTitle}}'?", - "delete-widgets-bundle-text": "Внимание, после подтверждения набор виджетов и все связанные с ним данные будут безвозвратно утеряны.", - "delete-widgets-bundles-title": "Вы точно хотите удалить { count, plural, =1 {1 набор виджетов} few {# набора виджетов} other {# наборов виджетов} }?", - "delete-widgets-bundles-action-title": "Удалить { count, plural, =1 {1 набор виджетов} few {# набора виджетов} other {# наборов виджетов} }", - "delete-widgets-bundles-text": "Внимание, после подтверждения выбранные наборы виджетов и все связанные с ними данные будут безвозвратно утеряны..", - "no-widgets-bundles-matching": "Набор виджетов '{{widgetsBundle}}' не найден.", - "widgets-bundle-required": "Набор виджетов обязателен.", - "system": "Системный", - "import": "Импортировать набор виджетов", - "export": "Экспортировать набор виджетов", - "export-failed-error": "Не удалось экспортировать набор виджетов: {{error}}", - "create-new-widgets-bundle": "Создать новый набор виджетов", - "widgets-bundle-file": "Файл набора виджетов", - "invalid-widgets-bundle-file-error": "Не удалось импортировать набор виджетов: неизвестная схема данных набора виджетов." - }, - "widget-config": { - "data": "Данные", - "settings": "Настройки", - "advanced": "Дополнительно", - "title": "Название", - "general-settings": "Общие настройки", - "display-title": "Показать название на виджете", - "drop-shadow": "Тень", - "enable-fullscreen": "Во весь экран", - "background-color": "Цвет фона", - "text-color": "Цвет текста", - "padding": "Отступ", - "margin": "Margin", - "widget-style": "Стиль виджета", - "title-style": "Стиль названия", - "mobile-mode-settings": "Мобильный режим", - "order": "Порядок", - "height": "Высота", - "units": "Специальный символ после значения", - "decimals": "Количество цифр после запятой", - "timewindow": "Временное окно", - "use-dashboard-timewindow": "Использовать временное окно дашборда", - "display-timewindow": "Показывать временное окно", - "legend": "Легенда", - "display-legend": "Показать легенду", - "datasources": "Источники данных", - "maximum-datasources": "Максимальной количество источников данных равно {{count}}", - "datasource-type": "Тип", - "datasource-parameters": "Параметры", - "remove-datasource": "Удалить источник данных", - "add-datasource": "Добавить источник данных", - "target-device": "Целевое устройство", - "alarm-source": "Источник оповещения", - "actions": "Действия", - "action": "Действие", - "add-action": "Добавить действие", - "search-actions": "Поиск действий", - "action-source": "Источник действий", - "action-source-required": "Источник действий обязателен.", - "action-name": "Название", - "action-name-required": "Название действия обязательно.", - "action-name-not-unique": "Действие с таким именем уже существует.
Название должно быть уникально в рамках одного источника действий.", - "action-icon": "Иконка", - "action-type": "Тип", - "action-type-required": "Тип действий обязателен.", - "edit-action": "Редактировать действие", - "delete-action": "Удалить действие", - "delete-action-title": "Удалить действие виджета", - "delete-action-text": "Вы точно хотите удалить действие виджета '{{actionName}}'?", - "title-icon": "Иконка в названии виджета", - "display-icon": "Показывать иконку в названии виджета", - "icon-color": "Цвет иконки", - "icon-size": "Размер иконки", - "advanced-settings": "Расширенные настройки", - "data-settings": "Настройки данных", - "no-data-display-message": "\"Нет данных для отображения\" альтернативный текст" - }, - "widget-type": { - "import": "Импортировать тип виджета", - "export": "Экспортировать тип виджета", - "export-failed-error": "Не удалось экспортировать тип виджета: {{error}}", - "create-new-widget-type": "Создать новый тип виджета", - "widget-type-file": "Файл типа виджета", - "invalid-widget-type-file-error": "Не удалось импортировать виджет: неизвестная схема данных типа виджета." - }, - "widgets": { - "date-range-navigator": { - "localizationMap": { - "Sun": "Вс", - "Mon": "Пн", - "Tue": "Вт", - "Wed": "Ср", - "Thu": "Чт", - "Fri": "Пт", - "Sat": "Сб", - "Jan": "Янв.", - "Feb": "Февр.", - "Mar": "Март", - "Apr": "Апр.", - "May": "Май", - "Jun": "Июнь", - "Jul": "Июль", - "Aug": "Авг.", - "Sep": "Сент.", - "Oct": "Окт.", - "Nov": "Нояб.", - "Dec": "Дек.", - "January": "Январь", - "February": "Февраль", - "March": "Март", - "April": "Апрель", - "June": "Июнь", - "July": "Июль", - "August": "Август", - "September": "Сентябрь", - "October": "Октября", - "November": "Ноябрь", - "December": "Декабрь", - "Custom Date Range": "Пользовательский диапазон дат", - "Date Range Template": "Шаблон диапазона дат", - "Today": "Сегодня", - "Yesterday": "Вчера", - "This Week": "На этой неделе", - "Last Week": "Прошлая неделя", - "This Month": "Этот месяц", - "Last Month": "Прошлый месяц", - "Year": "Год", - "This Year": "В этом году", - "Last Year": "Прошлый год", - "Date picker": "Выбор даты", - "Hour": "Час", - "Day": "День", - "Week": "Неделю", - "2 weeks": "2 Недели", - "Month": "Месяц", - "3 months": "3 Месяца", - "6 months": "6 Месяцев", - "Custom interval": "Пользовательский интервал", - "Interval": "Интервал", - "Step size": "Размер шага", - "Ok": "Ok" - } - }, - "input-widgets": { - "attribute-not-allowed": "Атрибут не может быть выбран в этом виджете", - "date": "Дата", - "blocked-location": "Геолокация заблокирована в вашем браузере", - "claim-device": "Подтвердить устройство", - "claim-failed": "Не удалось подтвердить устройство!", - "claim-not-found": "Устройство не найдено!", - "claim-successful": "Устройство успешно подтверждено!", - "discard-changes": "Отменить изменения", - "device-name": "Название устройства", - "device-name-required": "Необходимо указать название устройства", - "entity-attribute-required": "Значение атрибута обязателено", - "entity-coordinate-required": "Необходимо указать широту и долготу", - "entity-timeseries-required": "Значение телеметрии обязательно", - "get-location": "Получить текущее местоположение", - "latitude": "Широта", - "longitude": "Долгота", - "not-allowed-entity": "Выбраный объект не имеет общих атрибутов", - "no-attribute-selected": "Атрибут не выбран", - "no-datakey-selected": "Ни один datakey не выбран", - "no-entity-selected": "Объект не выбран", - "no-coordinate-specified": "Ключ для широты/долготы не указан", - "no-support-geolocation": "Ваш браузер не поддерживает геолокацию", - "no-image": "Нет изображения", - "no-support-web-camera": "Нет поддерживаемой веб-камеры", - "no-timeseries-selected": "Параметр телеметрии не выбран", - "secret-key": "Секретный ключ", - "secret-key-required": "Необходимо указать секретный ключ", - "switch-attribute-value": "Изменить значение атрибута", - "switch-camera": "Изменить камеру", - "switch-timeseries-value": "Изменить значение телеметрии", - "take-photo": "Сделать фото", - "time": "Время", - "timeseries-not-allowed": "Телеметрия не может быть выбрана в этом виджете", - "update-failed": "Не удалось обновить", - "update-successful": "Успешно обновлено", - "update-attribute": "Обновить атрибут", - "update-timeseries": "Обновить телеметрию", - "value": "Значение" - }, - "persistent-table": { - "rpc-id": "RPC ID", - "message-type": "Тип сообщения", - "method": "Метод", - "params": "Параметры", - "created-time": "Время создания", - "expiration-time": "Время жизни", - "retries": "Повторные попытки", - "status": "Статус", - "filter": "Фильтр", - "refresh": "Обновить", - "add": "Добавить RPC запрос", - "details": "Детали", - "delete": "Удалить", - "delete-request-title": "Удалить RPC запрос", - "delete-request-text": "Вы точно хотите удалить RPC запрос?", - "details-title": "Детали RPC ID: ", - "additional-info": "Дополнительная информация", - "response": "Ответ", - "any-status": "Любой статус", - "rpc-status-list": "Список RPC статусов", - "no-request-prompt": "Запросы не найдены", - "send-request": "Отправить запрос", - "add-title": "Добавить новый RPC запрос", - "method-error": "Метод обязателен.", - "white-space-error": "Пробелы не допускаются.", - "rpc-status": { - "QUEUED": "В ОЧЕРЕДИ", - "SENT": "ОТПРАВЛЕННО", - "DELIVERED": "ДОСТАВЛЕННО", - "SUCCESSFUL": "УСПЕШНО", - "TIMEOUT": "ВРЕМЯ ИСТЕКЛО", - "EXPIRED": "ПРОСРОЧЕНО", - "FAILED": "НЕУДАЧНО" - }, - "rpc-search-status-all": "ВСЕ", - "message-types": { - "false": "Двусторонний", - "true": "Односторонний" - } - } - }, - "icon": { - "icon": "Иконка", - "select-icon": "Выбрать иконку", - "material-icons": "Иконки в стиле Material", - "show-all": "Показать все иконки" - }, - "custom": { - "widget-action": { - "action-cell-button": "Кнопка действия в ячейке таблицы", - "row-click": "Действий при щелчке на строку", - "marker-click": "Действия при щелчке на маркер", - "polygon-click": "Действия при щелчке на полигон", - "tooltip-tag-action": "Действие при нажатии на ссылку в подсказке", - "node-selected": "Действий при выборе ноды", - "element-click": "Действий при щелчке на HTML элементе", - "pie-slice-click": "Действий при щелчке на секции круговой диаграммы", - "row-double-click": "Действий при двойном щелчке на строку" - } - }, - "language": { - "language": "Язык" - } -} diff --git a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json index 1200732365..4fded7d020 100644 --- a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json +++ b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json @@ -408,6 +408,7 @@ "attributes": "Lastnosti", "latest-telemetry": "Najnovejša telemetrija", "attributes-scope": "Obseg atributov entitete", + "scope-telemetry": "Telemetrija", "scope-latest-telemetry": "Najnovejša telemetrija", "scope-client": "Atributi odjemalca", "scope-server": "Atributi strežnika", @@ -446,10 +447,6 @@ "executions": "Executions", "javascript": "JavaScript", "javascript-executions": "JavaScript executions", - "javascript-functions": "JavaScript functions", - "javascript-functions-daily-activity": "JavaScript functions daily activity", - "javascript-functions-hourly-activity": "JavaScript functions hourly activity", - "javascript-functions-monthly-activity": "JavaScript functions monthly activity", "latest-error": "Latest Error", "messages": "Messages", "permanent-failures": "${entityName} Permanent Failures", @@ -913,13 +910,7 @@ "device-configuration": "Device configuration", "transport-configuration": "Transport configuration", "wizard": { - "device-wizard": "Device Wizard", - "device-details": "Device details", - "new-device-profile": "Create new device profile", - "existing-device-profile": "Select existing device profile", - "specific-configuration": "Specific configuration", - "customer-to-assign-device": "Customer to assign the device", - "add-credentials": "Add credentials" + "device-details": "Device details" } }, "device-profile": { @@ -938,8 +929,6 @@ "set-default": "Make device profile default", "delete": "Delete device profile", "copyId": "Copy device profile Id", - "new-device-profile-name": "Device profile name", - "new-device-profile-name-required": "Device profile name is required.", "name": "Name", "name-required": "Name is required.", "type": "Profile type", @@ -1017,12 +1006,9 @@ "condition-duration-value-required": "Duration value is required.", "condition-duration-time-unit-required": "Time unit is required.", "advanced-settings": "Advanced settings", - "alarm-rule-details": "Details", - "add-alarm-rule-details": "Add details", "propagate-alarm": "Propagate alarm", "alarm-rule-relation-types-list": "Relation types to propagate", "alarm-rule-relation-types-list-hint": "If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.", - "alarm-details": "Alarm details", "alarm-rule-condition": "Alarm rule condition", "enter-alarm-rule-condition-prompt": "Please add alarm rule condition", "edit-alarm-rule-condition": "Edit alarm rule condition", @@ -1111,7 +1097,6 @@ "name-starts-with": "Ime se začne z", "use-entity-name-filter": "Uporabi filter", "entity-list-empty": "Nobena entiteta ni izbrana.", - "entity-type-list-empty": "Izbrana ni nobena vrsta entitete.", "entity-name-filter-required": "Potreben je filter imena entitete.", "entity-name-filter-no-entity-matched": "Najti ni bilo nobene entitete, ki se začne z '{{entity}}'.", "all-subtypes": "Vse", @@ -2229,11 +2214,7 @@ "datakey-settings-schema": "Shema nastavitev podatkovnega ključa", "javascript": "Javascript", "js": "JS", - "remove-widget-type-title": "Ali ste prepričani, da želite odstraniti vrsto pripomočka '{{widgetName}}'?", - "remove-widget-type-text": "Po potrditvi bodo vrsta pripomočka in vsi povezani podatki nepopravljivi.", - "remove-widget-type": "Odstrani vrsto pripomočka", "add-widget-type": "Dodaj novo vrsto pripomočka", - "widget-type-load-failed-error": "Vrste pripomočka ni bilo mogoče naložiti!", "widget-template-load-failed-error": "Predloge pripomočka ni bilo mogoče naložiti!", "add": "Dodaj pripomoček", "undo": "Razveljavi spremembe pripomočka", diff --git a/ui-ngx/src/assets/locale/locale.constant-tr_TR.json b/ui-ngx/src/assets/locale/locale.constant-tr_TR.json index 1aa6900be7..b5b70cd8f9 100644 --- a/ui-ngx/src/assets/locale/locale.constant-tr_TR.json +++ b/ui-ngx/src/assets/locale/locale.constant-tr_TR.json @@ -445,6 +445,7 @@ "attributes": "Öznitelikler", "latest-telemetry": "Son telemetri", "attributes-scope": "Varlık öznitelik kapsamı", + "scope-telemetry": "telemetri", "scope-latest-telemetry": "Son telemetri", "scope-client": "İstemci öznitelikler", "scope-server": "Sunucu öznitelikler", @@ -487,10 +488,6 @@ "executions": "Çalıştırmalar", "javascript": "JavaScript", "javascript-executions": "JavaScript çalıştırmaları", - "javascript-functions": "JavaScript fonksiyonları", - "javascript-functions-daily-activity": "Günlük JavaScript fonksiyonları", - "javascript-functions-hourly-activity": "Saatlik JavaScript fonksiyonları", - "javascript-functions-monthly-activity": "Aylık JavaScript fonksiyonları", "latest-error": "Son Hata", "messages": "Mesajlar", "notifications": "Bildirimler", @@ -1021,13 +1018,7 @@ "device-configuration": "Cihaz yapılandırması", "transport-configuration": "Aktarım yapılandırması", "wizard": { - "device-wizard": "Cihaz Sihirbazı", - "device-details": "Cihaz ayrıntıları", - "new-device-profile": "Yeni cihaz profili oluştur", - "existing-device-profile": "Mevcut cihaz profilini seçin", - "specific-configuration": "Özel yapılandırma", - "customer-to-assign-device": "Cihazı atamak için kullanıcı grubu", - "add-credentials": "Kimlik bilgileri ekle" + "device-details": "Cihaz ayrıntıları" }, "unassign-devices-from-edge-title": "{ count, plural, =1 {1 cihazın} other {# cihazın} } atamasını kaldırmak istediğinizden emin misiniz?", "unassign-devices-from-edge-text": "Onaydan sonra, seçilen tüm cihazların ataması kaldırılacak ve uç tarafından erişilemeyecek." @@ -1048,8 +1039,6 @@ "set-default": "Cihaz profilini varsayılan yap", "delete": "Cihaz profilini sil", "copyId": "Cihaz profili kimliğini kopyala", - "new-device-profile-name": "Cihaz profili adı", - "new-device-profile-name-required": "Cihaz profili adı gerekli.", "name": "İsim", "name-required": "İsim gerekli.", "type": "Profil türü", @@ -1153,15 +1142,12 @@ "condition-duration-value-required": "Süre değeri gerekli.", "condition-duration-time-unit-required": "Zaman birimi gerekli.", "advanced-settings": "Gelişmiş ayarlar", - "alarm-rule-details": "Detaylar", - "add-alarm-rule-details": "Detay ekle", "alarm-rule-mobile-dashboard": "Mobil gösterge paneli", "alarm-rule-mobile-dashboard-hint": "Mobil uygulama tarafından alarm ayrıntıları gösterge paneli olarak kullanılır", "alarm-rule-no-mobile-dashboard": "Gösterge paneli seçilmedi", "propagate-alarm": "Alarmı yay", "alarm-rule-relation-types-list": "Yayılacak ilişki türleri", "alarm-rule-relation-types-list-hint": "Yayma ilişki türleri seçilmezse, alarmlar ilişki türüne göre filtreleme yapılmadan yayılır.", - "alarm-details": "Alarm ayrıntıları", "alarm-rule-condition": "Alarm kuralı koşulu", "enter-alarm-rule-condition-prompt": "Lütfen alarm kuralı koşulu ekleyin", "edit-alarm-rule-condition": "Alarm kuralı koşulunu düzenle", @@ -1545,7 +1531,6 @@ "help-text": "İhtiyaca göre '%' kullanın: '%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", "use-entity-name-filter": "Filtre kullan", "entity-list-empty": "Hiçbir öğe seçilmedi.", - "entity-type-list-empty": "Hiçbir öğe türü seçilmedi.", "entity-name-filter-required": "Öğe ismi filtresi gerekli.", "entity-name-filter-no-entity-matched": "'{{entity}}' ile başlayan hiçbir öğe bulunamadı.", "all-subtypes": "Tümü", @@ -2445,7 +2430,6 @@ "search": "Kural Ara", "selected-rulechains": "{ count, plural, =1 {1 kural} other {# kural} } seçildi", "open-rulechain": "Kuralı Aç", - "assign-new-rulechain": "Yeni kural zinciri atayın", "edge-template-root": "Şablon Kökü", "assign-to-edge": "Uca Ata", "edge-rulechain": "Uç kuralı zinciri", @@ -2846,11 +2830,7 @@ "image-preview": "Resim Önizleme", "javascript": "Javascript", "js": "JS", - "remove-widget-type-title": "'{{widgetName}}' isimli gösterge türünü kaldırmak istediğinizden emin misiniz?", - "remove-widget-type-text": "UYARI: Onaylandıktan sonra, gösterge türü ve ilgili tüm veriler geri yüklenemez şekilde silinecektir.", - "remove-widget-type": "Gösterge türünü kaldır", "add-widget-type": "Yeni gösterge türü ekle", - "widget-type-load-failed-error": "Gösterge türü yüklenemedi!", "widget-template-load-failed-error": "Gösterge şablonu yüklenemedi!", "add": "Gösterge ekle", "undo": "Gösterge değişikliklerini geri al", diff --git a/ui-ngx/src/assets/locale/locale.constant-uk_UA.json b/ui-ngx/src/assets/locale/locale.constant-uk_UA.json index aa71c84048..dce1bf0a2e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-uk_UA.json +++ b/ui-ngx/src/assets/locale/locale.constant-uk_UA.json @@ -6,13 +6,20 @@ "access-forbidden": "Доступ заборонено", "access-forbidden-text": "Недостатньо прав для доступу!
Спробуйте увійти як інший користувач, якщо ви все ще хочете отримати доступ до цього ресурсу.", "refresh-token-expired": "Дані про сесію застарілі", - "refresh-token-failed": "Не вдається відновити сеанс" + "refresh-token-failed": "Не вдається відновити сеанс", + "permission-denied": "У дозволі відмовлено", + "permission-denied-text": "Ви не маєте дозволу на виконання цієї операції!" + }, + "account": { + "account": "Обліковий запис", + "notification-settings": "Налаштування сповіщень" }, "action": { "activate": "Активувати", "suspend": "Призупинити", "save": "Зберегти", "saveAs": "Зберегти як", + "move": "Перемістити", "cancel": "Скасувати", "ok": "OK", "delete": "Видалити", @@ -21,6 +28,7 @@ "no": "Ні", "update": "Оновити", "remove": "Видалити", + "select": "Вибрати", "search": "Пошук", "clear-search": "Очистити пошук", "assign": "Надати", @@ -32,6 +40,7 @@ "edit-mode": "Режим редагування", "enter-edit-mode": "Ввійти в режим редагування", "decline-changes": "Відхилити зміни", + "decline": "Відхилити", "close": "Закрити", "back": "Назад", "run": "Запустити", @@ -51,9 +60,27 @@ "share-via": "Поділитися через {{provider}}", "continue": "Продовжити", "discard-changes": "Скасувати зміни", - "move": "Перемістити", - "select": "Вибрати", - "done": "Завершено" + "download": "Завантажити", + "next": "Наступний", + "next-with-label": "Наступний: {{label}}", + "read-more": "Прочитати більше", + "hide": "Приховати", + "done": "Завершено", + "print": "Друкувати", + "restore": "Відновити", + "confirm": "Підтвердити", + "more": "Більше", + "less": "Менше", + "skip": "Пропустити", + "send": "Надіслати", + "reset": "Скидання", + "show-more": "Показати більше", + "dont-show-again": "Більше не показувати", + "see-documentation": "Див. документацію", + "clear": "Очистити", + "upload": "Надіслати", + "delete-anyway": "Все одно видалити", + "delete-selected": "Видалити вибране" }, "aggregation": { "aggregation": "Агрегація", @@ -68,6 +95,7 @@ "none": "Відсутня" }, "admin": { + "settings": "Налаштування", "general": "Загальне", "general-settings": "Загальні налаштування", "outgoing-mail": "Поштовий сервер", @@ -126,15 +154,15 @@ "minimum-max-failed-login-attempts-range": "Максимальна кількість невдалих спроб входу не може бути негативною", "user-lockout-notification-email": "У разі блокування облікового запису користувача, надішліть сповіщення на електронну пошту", "smpp-provider": { - "smpp-version": "SMPP верія", + "smpp-version": "SMPP верcія", "smpp-host": "SMPP хост", "smpp-host-required": "Хост SMPP обов'язковий.", "smpp-port": "SMPP порт", "smpp-port-required": "Порт SMPP обов'язковий.", "system-id": "Id системи", - "system-id-required": "Id системи обязателен.", + "system-id-required": "ID системи обов'язковий.", "password": "Пароль", - "password-required": "Пароль обязателен.", + "password-required": "Пароль обов'язковий.", "type-settings": "Налаштування типів", "source-settings": "Налаштування джерела", "destination-settings": "Налаштування призначення", @@ -144,12 +172,13 @@ "service-type": "Тип обслуговування", "source-address": "Адреса джерела", "source-ton": "Тип номера джерела", - "source-npi": "Идентификация плана нумерации джерела", + "source-npi": "Ідентифікація плану нумерації джерела", "destination-ton": "Тип номера призначення", "destination-npi": "Ідентифікація плану нумерації призначення", "address-range": "Діапазон адрес", "coding-scheme": "Схема кодування" - } + }, + "resources": "Ресурси" }, "alarm": { "alarm": "Сигнал тривоги", @@ -249,8 +278,8 @@ "filter-type": "Тип фільтра", "filter-type-required": "Необхідно вказати тип фільтра.", "entity-filter-no-entity-matched": "Не знайдено жодних сутностей, які відповідають вказаному фільтру.", - "no-entity-filter-specified": "Фільтр обїектів не вказано", - "root-state-entity": "Використовувати сутінсть стану як кореневу", + "no-entity-filter-specified": "Фільтр сутностей не вказано", + "root-state-entity": "Використовувати сутність стану як кореневу", "last-level-relation": "Використовувати лише відношення останнього рівня", "group-state-entity": "Використовувати групу сутностей стану як кореневу", "root-entity": "Коренева сутність", @@ -323,7 +352,7 @@ "copyId": "Копіювати Id активу", "idCopiedMessage": "Id активу був скопійований у буфер обміну", "select-asset": "Виберіть актив", - "no-assets-matching": "Не знайдено жодних активів, що відповідають'{{entity}}'.", + "no-assets-matching": "Не знайдено жодних активів, що відповідають '{{entity}}'.", "asset-required": "Необхідно задати актив", "name-starts-with": "Назва активу починається з", "selected-assets": "{ count, plural, =1 {1 актив} other {# активи} } selected", @@ -342,6 +371,7 @@ "attributes": "Атрибути", "latest-telemetry": "Остання телеметрія", "attributes-scope": "Область видимості атрибутів", + "scope-telemetry": "Телеметрія", "scope-latest-telemetry": "Остання телеметрія", "scope-client": "Клієнтські атрибути", "scope-server": "Серверні атрибути", @@ -366,6 +396,9 @@ "selected-attributes": "{ count, plural, =1 {1 attribute} other {# attributes} } selected ...вибрані вибрати", "selected-telemetry": "{ count, plural, =1 {1 telemetry unit} other {# telemetry units} } selected" }, + "api-usage": { + "api-usage": "Використання API" + }, "audit-log": { "audit": "Операція", "audit-logs": "Журнал операцій", @@ -475,14 +508,14 @@ "test": "Тест", "metadata": "Метадані", "metadata-required": "Записи метаданих не можуть бути порожніми.", - "integration-metadata": "Метедані інтеграції", + "integration-metadata": "Метадані інтеграції", "integration-metadata-required": "Параметри метаданих інтеграції не можуть бути порожніми.", "output": "Вихідні дані", "import": "Імпорт перетворювача даних", "export": "Експорт перетворювача даних", "export-failed-error": "Неможливо експортувати перетворювач даних: {{помилка}}", "create-new-converter": "Створити новий перетворювач даних", - "converter-file": "Файл перетворювача даних(конвектер файл)", + "converter-file": "Файл перетворювача даних", "invalid-converter-file-error": "Неможливо імпортувати перетворювач даних: недійсна структура даних перетворювача.", "type": "Тип", "type-required": "Необхідно задати тип.", @@ -619,7 +652,7 @@ "unassign-dashboard-text": "Після підтвердження, клієнт буде позбавлений панелі приладів. Панель приладів і пов'язані з нею дані будуть недоступні клієнтові.", "unassign-dashboard": "Позбавити панелі приладів", "unassign-dashboards-title": "Ви впевнені, що хочете позбавити { count, plural, =1 {1 панелі приладів} other {# панелей приладів} }?", - "unassign-dashboards-text": "Після підтвердження, клієтн буде позбавлений усіх вибраних панелей приладів і даних, які з ними пов'язані.", + "unassign-dashboards-text": "Після підтвердження, клієнт буде позбавлений усіх вибраних панелей приладів і даних, які з ними пов'язані.", "public-dashboard-title": "Панель приладів тепер публічна", "public-dashboard-text": "Ваша панель приладів {{dashboardTitle}} тепер публічна і доступна іншим link:", "public-dashboard-notice": "Note: Не забудьте зробити спільні пристрої загальнодоступними, щоб отримати доступ до їхніх даних.", @@ -644,7 +677,7 @@ "settings": "Налаштування", "columns-count": "Кількість стовпців", "columns-count-required": "Необхідно вказати кількість стовпців.", - "min-columns-count-message": "Дозволений мінімум -10 стовпців.", + "min-columns-count-message": "Дозволений мінімум - 10 стовпців.", "max-columns-count-message": "Дозволений максимум - 1000 стовпців.", "widgets-margins": "Відступ між віджетами", "horizontal-margin": "Горизонтальний відступ", @@ -685,8 +718,8 @@ "widget-file": "Файл віджета", "invalid-widget-file-error": "Неможливо імпортувати віджет: неправильна структура даних віджета.", "widget-import-missing-aliases-title": "Налаштувати псевдоніми, що використовуються імпортованим віджетом", - "open-toolbar": "Відкрити панель інструменів ", - "close-toolbar": "Закрити панель інструменів", + "open-toolbar": "Відкрити панель інструментів", + "close-toolbar": "Закрити панель інструментів", "configuration-error": "Помилка конфігурації", "alias-resolution-error-title": "Помилка конфігурації псевдонімів панелі візуалізації", "invalid-aliases-config": "Неможливо знайти пристрої, які відповідають певному фільтру псевдонімів.
Зверніться до свого адміністратора, щоб вирішити цю проблему.", @@ -716,7 +749,7 @@ "show-details": "Показати деталі", "hide-details": "Приховати деталі", "select-state": "Виберіть цільовий стан", - "state-controller": "Контроллер стану" + "state-controller": "Контролер стану" }, "datakey": { "settings": "Налаштування", @@ -731,11 +764,11 @@ "timeseries": "Телеметрія", "attributes": "Атрибути", "entity-field": "Поле сутності", - "alarm": "Поля сигнала тривоги", + "alarm": "Поля сигналу тривоги", "timeseries-required": "Необхідно вказати Телеметрія.", "timeseries-or-attributes-required": "Необхідно вказати телеметрію/атрибути.", "maximum-timeseries-or-attributes": "Максимальні { count, plural, =1 {1 телеметрія/атрибут дозволені.} other {# телеметрія/атрибути дозволені} }", - "alarm-fields-required": "Необхідно вказати поля сигнала тривоги.", + "alarm-fields-required": "Необхідно вказати поля сигналу тривоги.", "function-types": "Типи функцій", "function-types-required": "Необхідно вказати типи функцій.", "maximum-function-types": "Maximum { count, plural, =1 {1 function type is allowed.} other {# function types are allowed} }", @@ -770,7 +803,7 @@ "no-keys-found": "Ключі не знайдено.", "create-new-alias": "Створити новий!", "create-new-key": "Створити новий!", - "duplicate-alias-error": "Псевдонім з таким іменем '{{alias}}' вже існює.
Псевдоніми пристроїв повинні бути унікальними на панелі візуалізації.", + "duplicate-alias-error": "Псевдонім з таким іменем '{{alias}}' вже існує.
Псевдоніми пристроїв повинні бути унікальними на панелі візуалізації.", "configure-alias": "Налаштувати псевдонім '{{alias}}'", "no-devices-matching": "Не знайдено жодних пристроїв, які відповідають '{{entity}}'.", "alias": "Псевдонім", @@ -860,6 +893,14 @@ "list-of-groups": "{ count, plural, =1 {Одна група пристроїв} other {Список # груп пристроїв} }", "group-name-starts-with": "Групи пристроїв, імена яких починаються з '{{prefix}}'" }, + "asset-profile": { + "asset-profile": "Профіль активу", + "asset-profiles": "Профілі активів" + }, + "device-profile": { + "device-profile": "Профіль пристрою", + "device-profiles": "Профілі пристроїв" + }, "dialog": { "close": "Закрити діалогове вікно" }, @@ -867,6 +908,11 @@ "column": "Колонка", "row": "Рядок" }, + "edge": { + "management": "Керування Edge", + "instances": "Інстанси", + "rulechain-templates": "Шаблони ланцюжків правил" + }, "error": { "unable-to-connect": "Неможливо підключитися до сервера! Перевірте підключення до Інтернету.", "unhandled-error-code": "Неопрацьований помилковий код: {{errorCode}}", @@ -878,7 +924,7 @@ "aliases": "Псевдоніми сутності", "entity-alias": "Псевдонім сутності", "unable-delete-entity-alias-title": "Неможливо видалити псевдонім сутності", - "unable-delete-entity-alias-text": "Псевдонім сутності'{{entityAlias}}' неможливо видалити, так як це використовується наступним віджетом(s):
{{widgetsList}}", + "unable-delete-entity-alias-text": "Псевдонім сутності '{{entityAlias}}' неможливо видалити, оскільки він використовується наступним віджетом(s):
{{widgetsList}}", "duplicate-alias-error": "Знайдено повторюваний псевдонім '{{alias}}'.
Псевдоніми сутностей повинні бути унікальними на панелі візуалізації.", "missing-entity-filter-error": "Відсутній фільтр для псевдоніма '{{alias}}'.", "configure-alias": "Налаштувати '{{alias}}' псевдонім", @@ -897,7 +943,7 @@ "name-starts-with": "Назва починається з", "use-entity-name-filter": "Використовуйте фільтр", "entity-list-empty": "Не вибрано жодних сутностей.", - "entity-type-list-empty": "Не вибрано жодних типів сутностей.", + "entity-type-list-required": "Потрібно вибрати принаймні один тип сутності.", "entity-name-filter-required": "Необхідно задати фільтр по імені.", "entity-name-filter-no-entity-matched": "Не знайдено жодних сутностей, що починаються з '{{entity}}'.", "all-subtypes": "Всі", @@ -1117,9 +1163,9 @@ "view-entity-views": "Переглянути представлення сутностей", "entity-view-alias": "Псевдонім представлення сутності", "aliases": "Псевдоніми представлення сутності", - "no-alias-matching": "Псевдонім'{{alias}}' не знайдено.", + "no-alias-matching": "Псевдонім '{{alias}}' не знайдено.", "no-aliases-found": "Псевдоніми не знайдено.", - "no-key-matching": "'Ключ {{key}}' не знайдено.", + "no-key-matching": "Ключ '{{key}}' не знайдено.", "no-keys-found": "Ключі не знайдено.", "create-new-alias": "Створити новий!", "create-new-key": "Створити новий!", @@ -1134,7 +1180,7 @@ "entity-view-list": "Список представленнь сутності", "use-entity-view-name-filter": "Використати фільтр", "entity-view-list-empty": "Не вибрано жодного представлення сутності.", - "entity-view-name-filter-required": "Необхідно вказвти фільтр назв представлення сутності.", + "entity-view-name-filter-required": "Необхідно вказати фільтр назв представлення сутності.", "entity-view-name-filter-no-entity-view-matched": "Представлення сутностей, назви яких починаються з '{{entityView}}' не знайдено.", "add": "Додати представлення сутності", "assign-to-customer": "Призначити клієнту", @@ -1397,8 +1443,11 @@ "import-extension": "Імпортувати розширення", "export-extension": "Експортувати розширення", "file": "Файл розширень", - "invalid-file-error": "Не правильний формат файла" + "invalid-file-error": "Неправильний формат файлу" }, + "feature": { + "advanced-features": "Додаткові можливості" + }, "fullscreen": { "expand": "Відкрити у повноекранному режимі", "exit": "Вийти з повноекранного режиму", @@ -1409,7 +1458,7 @@ "function": "Функція" }, "grid": { - "delete-item-title": "Ви впенені, що хочете видалити цей елемент?", + "delete-item-title": "Ви впевнені, що хочете видалити цей елемент?", "delete-item-text": "Будьте обережні, після підтвердження, цей елемент і всі пов'язані з ним дані, стануть недоступними.", "delete-items-title": "Ви впенені, що хочете видалити { count, plural, =1 {1 елемент} other {# елементи} }?", "delete-items-action-title": "Видалити{ count, plural, =1 {1 елемент} other {# елементи} }", @@ -1694,6 +1743,12 @@ "login-with": "Увійти через {{name}}", "or": "або" }, + "notification": { + "notification-center": "Центр сповіщень" + }, + "ota-update": { + "ota-updates": "Оновлення OTA" + }, "position": { "top": "Угорі", "bottom": "Знизу", @@ -1709,6 +1764,12 @@ "tokenCopiedMessage": "JWT токен скопійовано в буфер обміну", "tokenCopiedWarnMessage": "JWT токен не є дійсним! Перезавантажте сторінку." }, + "profiles": { + "profiles": "Профілі" + }, + "security": { + "security": "Безпека" + }, "relation": { "relations": "Відношення", "direction": "Напрямок", @@ -1751,41 +1812,44 @@ "additional-info": "Додаткова інформація (JSON)", "invalid-additional-info": "Не вдалося розібрати JSON з додатковою інформацією ." }, + "resource": { + "resources-library": "Бібліотека ресурсів" + }, "rulechain": { - "rulechain": "Ланцюг правил", - "rulechains": "Ланцюги правил", + "rulechain": "Ланцюжок правил", + "rulechains": "Ланцюжки правил", "root": "Основний", - "delete": "Видалити ланцюг правил", + "delete": "Видалити ланцюжок правил", "name": "Ім'я", "name-required": "Необхідно вказати ім'я.", "description": "Опис", - "add": "Додати ланцюг правил", - "set-root": "Зробити ланцюг правил основним", - "set-root-rulechain-title": "Ви впевнені, що хочете зробити ланцюг правил '{{ruleChainName}}' основним?", - "set-root-rulechain-text": "Після підтвердження ланцюг правил стане основним і буде обробляти всі вхідні транспортні повідомлення.", - "delete-rulechain-title": "Ви впевнені, що хочете видалити ланцюг правил '{{ruleChainName}}'?", - "delete-rulechain-text": "Будьте обережні, після підтвердження ланцюг правил і всі пов'язані з ним дані стануть недоступними.", + "add": "Додати ланцюжок правил", + "set-root": "Зробити ланцюжок правил основним", + "set-root-rulechain-title": "Ви впевнені, що хочете зробити ланцюжок правил '{{ruleChainName}}' основним?", + "set-root-rulechain-text": "Після підтвердження ланцюжок правил стане основним і буде обробляти всі вхідні транспортні повідомлення.", + "delete-rulechain-title": "Ви впевнені, що хочете видалити ланцюжок правил '{{ruleChainName}}'?", + "delete-rulechain-text": "Будьте обережні, після підтвердження ланцюжок правил і всі пов'язані з ним дані стануть недоступними.", "delete-rulechains-title": "Ви впевнені, що хочете видалити { count, plural, =1 {1 ланцюг правил} other {# ланцюги правил} }?", "delete-rulechains-action-title": "Видалити{ count, plural, =1 {1 ланцюг правил} other {# ланцюги правил} }", - "delete-rulechains-text": "Будьте обережні, після підтвердження, вибрані ланцюги правил і всі пов'язані з ними дані стануть недоступними.", - "add-rulechain-text": "Додати новий ланцюг правил", - "no-rulechains-text": "Ланцюг правил не знайдено", - "rulechain-details": "Деталі ланцюга правил", + "delete-rulechains-text": "Будьте обережні, після підтвердження, вибрані ланцюжки правил і всі пов'язані з ними дані стануть недоступними.", + "add-rulechain-text": "Додати новий ланцюжок правил", + "no-rulechains-text": "Ланцюжок правил не знайдено", + "rulechain-details": "Деталі ланцюжка правил", "details": "Деталі", "events": "Події", "system": "Система", - "import": "Імпортувати ланцюг правил", - "export": "Експортувати ланцюг правил", - "export-failed-error": "Не вдалося експортувати ланцюг правил: {{error}}", - "create-new-rulechain": "Створити новий ланцюг правил", - "rulechain-file": "Файл ланцюга правил", - "invalid-rulechain-file-error": "Неможливо імпортувати ланцюг правил: недійсна структуру даних ланцюга правил.", - "copyId": "Копіювати Id ланцюга правил", - "idCopiedMessage": "Id ланцюга правил скопійовано в буфер обміну", - "select-rulechain": "Вибрати ланцюг правил", - "no-rulechains-matching": "Не знайдено жодних ланцюгів правил, які відповідають '{{entity}}'.", - "rulechain-required": "Необхідно вказати ланцюг правил", - "management": "Управління ланцюгами правил", + "import": "Імпортувати ланцюжок правил", + "export": "Експортувати ланцюжок правил", + "export-failed-error": "Не вдалося експортувати ланцюжок правил: {{error}}", + "create-new-rulechain": "Створити новий ланцюжок правил", + "rulechain-file": "Файл ланцюжка правил", + "invalid-rulechain-file-error": "Неможливо імпортувати ланцюжок правил: недійсна структуру даних ланцюжка правил.", + "copyId": "Копіювати Id ланцюжка правил", + "idCopiedMessage": "Id ланцюжка правил скопійовано в буфер обміну", + "select-rulechain": "Вибрати ланцюжок правил", + "no-rulechains-matching": "Не знайдено жодних ланцюжків правил, які відповідають '{{entity}}'.", + "rulechain-required": "Необхідно вказати ланцюжок правил", + "management": "Управління ланцюжками правил", "debug-mode": "Режим налагодження" }, "rulenode": { @@ -1797,7 +1861,7 @@ "name": "Ім'я", "name-required": "Необхідно вказати ім'я.", "type": "Тип", - "description": "Опис", + "rule-node-description": "Опис вузла правил", "delete": "Видалити вузол правил", "select-all-objects": "Вибрати усі вузли та з'єднання", "deselect-all-objects": "Зняти виділення з усіх вузлів і з'єднань", @@ -1819,7 +1883,7 @@ "link-labels": "Мітки посилання", "link-labels-required": "Необхідно вказати мітки посилання.", "no-link-labels-found": "Не знайдено жодних міток посилання", - "no-link-label-matching": "Мітка'{{label}}' не знайдена.", + "no-link-label-matching": "Мітку '{{label}}' не знайдено.", "create-new-link-label": "Створити нову!", "type-filter": "Фільтр", "type-filter-details": "Фільтрувати вхідні повідомлення з заданими умовами", @@ -1833,15 +1897,15 @@ "type-analytics-details": "Виконує аналіз потокових або збережених даних", "type-external": "Зовнішній", "type-external-details": "Взаємодіє з зовнішньою системою", - "type-rule-chain": "Ланцюг правил", - "type-rule-chain-details": "Перенаправити вхідне повідомлення на вказаний ланцюг правил", + "type-rule-chain": "Ланцюжок правил", + "type-rule-chain-details": "Перенаправити вхідне повідомлення на вказаний ланцюжок правил", "type-input": "Вхід", - "type-input-details": "Логічний вхід ланцюга правил, перенаправляє вхідні повідомлення на наступний пов'язаний вузол правил", + "type-input-details": "Логічний вхід ланцюжка правил, перенаправляє вхідні повідомлення на наступний пов'язаний вузол правил", "type-unknown": "Невідомий", "type-unknown-details": "Невизначений вузол правил", "directive-is-not-loaded": "Вказана директива конфігурації '{{directiveName}}' недоступна.", "ui-resources-load-error": "Не вдалося завантажити UI ресурси.", - "invalid-target-rulechain": "Не вдається визначити цільовий ланцюг правил!", + "invalid-target-rulechain": "Не вдається визначити цільовий ланцюжок правил!", "test-script-function": "Протестувати скрипт", "message": "Повідомлення", "message-type": "Тип повідомлення", @@ -1859,7 +1923,7 @@ "scheduler-event": "Подія планувальника", "select-scheduler-event": "Вибрати подію", "no-scheduler-events-matching": "Не знайдено жодних подій, які відповідають '{{entity}}'.", - "scheduler-event-required": "Необхвдно вказати заплановану подію", + "scheduler-event-required": "Необхідно вказати заплановану подію", "management": "Управління планувальником", "scheduler-events": "Планування подій", "add-scheduler-event": "Додати подію", @@ -1901,7 +1965,7 @@ "repeat-on-monday": "Повторити в понеділок", "repeat-on-tuesday": "Повторити у вівторок", "repeat-on-wednesday": "Повторити в середу", - "repeat-on-thursday": "Повторити в червер", + "repeat-on-thursday": "Повторити в четвер", "repeat-on-friday": "Повторити в п'ятницю", "repeat-on-saturday": "Повторити в суботу", "event-type": "Тип події", @@ -1914,7 +1978,7 @@ "week": "Тиждень", "day": "День", "agenda-week": "Порядок тижня", - "agenda-day": "Порялок дня", + "agenda-day": "Порядок дня", "list-year": "Список року", "list-month": "Список місяця", "list-week": "Список тижня", @@ -1993,7 +2057,7 @@ "download-blob-entity": "Завантажити файл", "delete-blob-entity": "Видалити файл", "delete-blob-entity-title": "Ви впевнені, що хочете видалити файл '{{blobEntityName}}'?", - "delete-blob-entity-text": "Будьте обережні, після підствердження, дані файлу стануть недоступними." + "delete-blob-entity-text": "Будьте обережні, після підтвердження, дані з файлу стануть недоступними для відновлення." }, "timezone": { "timezone": "Часовий пояс", @@ -2017,7 +2081,7 @@ "add-tenant-text": "Додати нового власника", "no-tenants-text": "Не знайдено жодного власника", "tenant-details": "Подробиці про власника", - "delete-tenant-title": "Ви впевнені, що хочете видалити власника'{{tenantTitle}}'?", + "delete-tenant-title": "Ви впевнені, що хочете видалити власника '{{tenantTitle}}'?", "delete-tenant-text": "Будьте обережні, після підтвердження власник і всі пов'язані з ним дані стануть недоступними.", "delete-tenants-title": "Ви впевнені, що хочете видалити { count, plural, =1 {1 власник} other {# власники} }?", "delete-tenants-action-title": "Видалити { count, plural, =1 {1 власник} other {# власники} }", @@ -2075,7 +2139,7 @@ "add-user-text": "Додати нового користувача", "no-users-text": "Не знайдено жодного користувача", "user-details": "Подробиці про користувача", - "delete-user-title": "Ви впевнені, що хочете видалити користувача'{{userEmail}}'?", + "delete-user-title": "Ви впевнені, що хочете видалити користувача '{{userEmail}}'?", "delete-user-text": "Будьте обережні, після підтвердження, користувач і всі пов'язані з ним дані стануть недоступними.", "delete-users-title": "Ви впевнені, що хочете видалити { count, plural, =1 {1 користувача} other {# користувачів} }?", "delete-users-action-title": "Видалити { count, plural, =1 {1 користувача} other {# користувачів} }", @@ -2127,6 +2191,9 @@ "true": "Правдиве", "long": "Довге" }, + "version-control": { + "version-control": "Керування версіями" + }, "widget": { "widget-library": "Бібліотека віджетів", "widget-bundle": "Пакет віджетів", @@ -2144,7 +2211,7 @@ "no-data-found": "Даних не знайдено", "latest": "Останні значення", "rpc": "Керуючий віджет", - "alarm": "Віджет сигнала тривоги", + "alarm": "Віджет сигналу тривоги", "static": "Статичний віджет", "select-widget-type": "Вибрати тип віджета", "missing-widget-title-error": "Необхідно вказати назву віджета!", @@ -2169,11 +2236,7 @@ "settings-schema": "Схема налаштувань", "datakey-settings-schema": "Схема налаштувань ключів даних", "javascript": "Javascript", - "remove-widget-type-title": "Ви впевнені, що хочете видалити тип віджета '{{widgetName}}'?", - "remove-widget-type-text": "Будьте обережні, після підтвердження, тип віджета і всі пов'язані з ним дані стануть недоступними.", - "remove-widget-type": "Видалити тип віджета", "add-widget-type": "Додати новий тип віджета", - "widget-type-load-failed-error": "Не вдалося завантажити тип віджета!", "widget-template-load-failed-error": "Не вдалося завантажити шаблон віджета!", "add": "Додати віджет", "undo": "Скасувати зміни віджета", @@ -2255,7 +2318,7 @@ "remove-datasource": "Видалити джерело даних", "add-datasource": "Додати джерело даних", "target-device": "Цільовий пристрій", - "alarm-source": "Джерело сигнала тривоги", + "alarm-source": "Джерело сигналу тривоги", "actions": "Дії", "action": "Дія", "add-action": "Додати дію", @@ -2370,7 +2433,7 @@ "no-coordinate-specified": "Ключ для широти/довготи не вказаний", "no-support-geolocation": "Ваш браузер не підтримує геолокацію", "no-image": "Немає зображення", - "no-support-web-camera": "Нет поддерживаемой веб-камеры", + "no-support-web-camera": "Немає підтримуваної веб-камери", "no-timeseries-selected": "Параметр телеметрії не вибрано", "secret-key": "Секретний ключ", "secret-key-required": "Необхідно вказати секретний ключ", @@ -2415,10 +2478,10 @@ "rpc-status": { "QUEUED": "В ЧЕРЗІ", "SENT": "ВІДПРАВЛЕНО", - "DELIVERED": "ДОСТАВЛЕННО", + "DELIVERED": "ДОСТАВЛЕНО", "SUCCESSFUL": "УСПІШНО", "TIMEOUT": "ЧАС МИНУВ", - "EXPIRED": "ПРОСРОЧЕНО", + "EXPIRED": "ПРОТЕРМІНОВАНО", "FAILED": "НЕ ВДАЛО" }, "rpc-search-status-all": "ВСІ", @@ -2429,7 +2492,7 @@ } }, "white-labeling": { - "white-labeling": "Білий маркування", + "white-labeling": "Біле маркування", "login-white-labeling": "Login White Labeling", "preview": "Попередній перегляд", "app-title": "Назва програми", @@ -2444,7 +2507,7 @@ "logo-size-error": "Зображення логотипу занадто велике. Максимально дозволений розмір зображення логотипу{{kbSize}} KBytes.", "logo-type-error": "Недійсний формат файлу логотипу. Приймаються тільки зображення.", "drop-logo-image": "Зніміть зображення логотипу або клацніть, щоб вибрати файл для завантаження.", - "no-logo-image": "Не вибрано жожного логотипу", + "no-logo-image": "Не вибрано жодного логотипу", "logo-height": "Висота логотипу, px", "primary-palette": "Основна палітра", "accent-palette": "Палітра акцент", diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json index a37f96875a..f0d07fa506 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -10,11 +10,16 @@ "permission-denied": "权限被拒绝", "permission-denied-text": "您没有执行此操作的权限!" }, + "account": { + "account": "账号", + "notification-settings": "通知设置" + }, "action": { "activate": "激活", "suspend": "暂停", "save": "保存", "saveAs": "另存为", + "move": "移动", "cancel": "取消", "ok": "确定", "delete": "删除", @@ -35,6 +40,7 @@ "edit-mode": "编辑模式", "enter-edit-mode": "进入编辑模式", "decline-changes": "撤销更改", + "decline": "撤销", "close": "关闭", "back": "后退", "run": "运行", @@ -55,13 +61,23 @@ "continue": "继续", "discard-changes": "放弃更改", "download": "下载", + "next": "下一个", "next-with-label": "下一个:{{label}}", "read-more": "阅读更多", "hide": "隐藏", - "done": "完毕", + "done": "完成", "print": "打印", "restore": "恢复", - "confirm": "确认" + "confirm": "确定", + "more": "更多", + "less": "更少", + "skip": "跳过", + "send": "发送", + "reset": "重置", + "show-more": "显示更多", + "dont-show-again": "不再显示", + "see-documentation": "查看文档", + "clear": "清除" }, "aggregation": { "aggregation": "聚合", @@ -76,9 +92,11 @@ "none": "无" }, "admin": { + "settings": "设置", "general": "基本设置", "general-settings": "基本设置", "home-settings": "首页设置", + "home": "首页", "outgoing-mail": "发送邮件", "outgoing-mail-settings": "发送邮件设置", "system-settings": "系统设置", @@ -87,6 +105,23 @@ "base-url-required": "基本URL必填。", "prohibit-different-url": "禁止从客户端请求头中使用主机名", "prohibit-different-url-hint": "应为生产环境启用此设置。禁用时可能会导致安全问题", + "device-connectivity": { + "device-connectivity": "设备连接", + "http-s": "HTTP(s)", + "mqtt-s": "MQTT(s)", + "coap-s": "COAP(s)", + "http": "HTTP", + "https": "HTTPs", + "mqtt": "MQTT", + "mqtts": "MQTTs", + "coap": "COAP", + "coaps": "COAPs", + "hint": "如果主机或端口字段为空,将使用默认的协议值。", + "host": "主机", + "port": "端口", + "port-pattern": "端口必须是正整数。", + "port-range": "端口应在1到65535的范围内。" + }, "mail-from": "邮件来自", "mail-from-required": "邮件发件人必填。", "smtp-protocol": "SMTP协议", @@ -108,7 +143,7 @@ "proxy-port-range": "代理端口应在1到65535之间。", "proxy-user": "代理用户", "proxy-password": "代理密码", - "change-password": "Change password", + "change-password": "修改密码", "send-test-mail": "发送测试邮件", "sms-provider": "SMS 服务商", "sms-provider-settings": "SMS 服务商设置", @@ -127,10 +162,10 @@ "number-from-required": "发送方电话号码必填。", "number-to": "电话号码至", "number-to-required": "电话号码必填。", - "phone-number-hint": "E.164格式的手机号码,例如+19995550123", - "phone-number-hint-twilio": "Phone Number in E.164 format/Phone Number's SID/Messaging Service SID, ex. +19995550123/PNXXX/MGXXX", - "phone-number-pattern": "手机号码无效。应为E.164格式,例如+19995550123。", - "phone-number-pattern-twilio": "Invalid phone number. Should be in E.164 format/Phone Number's SID/Messaging Service SID, ex. +19995550123/PNXXX/MGXXX.", + "phone-number-hint": "E.164格式的手机号码,例如 +19995550123", + "phone-number-hint-twilio": "E.164格式电话号码/电话号码的SID/消息服务的SID,例如 +19995550123/PNXXX/MGXXX", + "phone-number-pattern": "手机号码无效。应为E.164格式,例如 +19995550123。", + "phone-number-pattern-twilio": "无效的电话号码,应该使用E.164格式电话号码/电话号码的SID/消息服务的SID,例如+19995550123/PNXXX/MGXXX。", "sms-message": "短信", "sms-message-required": "短消息内容必填。", "sms-message-max-length": "短信长度不能超过1600个字符", @@ -164,8 +199,9 @@ "user-lockout-notification-email": "如果用户帐户锁定,请发送通知到电子邮件", "domain-name": "域名", "domain-name-unique": "域名和协议必须是唯一的。", - "domain-name-max-length": "域名长度不能大于256", + "domain-name-max-length": "域名应该少于256个字符。", "error-verification-url": "域名不应包含符号 “/” 和 “:”。例:thingsboard.io", + "connection-settings": "连接设置", "oauth2": { "access-token-uri": "访问令牌URI", "access-token-uri-required": "访问令牌 URI 必填。", @@ -181,28 +217,28 @@ "client-authentication-method": "客户端身份验证方法", "client-id": "客户端ID", "client-id-required": "客户端 ID 必填。", - "client-id-max-length": "客户端ID长度不能大于256", + "client-id-max-length": "客户端ID应该少于256个字符。", "client-secret": "客户机密", "client-secret-required": "需要客户端密码。", - "client-secret-max-length": "客户端密钥长度不能大于2049", + "client-secret-max-length": "客户端密钥应该少于2049个字符。", "custom-setting": "自定义设置", "customer-name-pattern": "客户名称模式", - "customer-name-pattern-max-length": "客户名称模式长度不能大于256", + "customer-name-pattern-max-length": "客户名称模式应该少于256个字符。", "default-dashboard-name": "默认仪表板名称", - "default-dashboard-name-max-length": "默认仪表板名称长度不能大于256", + "default-dashboard-name-max-length": "默认仪表板名称应该少于256个字符。", "delete-domain-text": "请注意:确认后,域和所有 provider data 将不可恢复。", "delete-domain-title": "确定要删除域 '{{domainName}}' 的设置吗?", "delete-registration-text": "请注意:确认后 provider data 将不可恢复。", "delete-registration-title": "确定要删除 provider '{{name}}' 吗?", "email-attribute-key": "电子邮件属性键", - "email-attribute-key-required": "电子邮件属性密钥必填。", - "email-attribute-key-max-length": "电子邮件属性键长度不能大于32", + "email-attribute-key-required": "电子邮件属性键必填。", + "email-attribute-key-max-length": "电子邮件属性键应该少于32个字符。", "first-name-attribute-key": "名字属性键", - "first-name-attribute-key-max-length": "名字属性键长度不能大于32", + "first-name-attribute-key-max-length": "名字属性键应该少于32个字符。", "general": "基本设置", "jwk-set-uri": "JSON Web Key URI", "last-name-attribute-key": "姓氏属性键", - "last-name-attribute-key-max-length": "姓氏属性键长度不能大于32", + "last-name-attribute-key-max-length": "姓氏属性键应该少于32个字符。", "login-button-icon": "登录按钮图标", "login-button-label": "Provider 标签", "login-button-label-placeholder": "使用 $(Provider label) 登录", @@ -211,7 +247,7 @@ "mapper": "Mapper", "new-domain": "新建域", "oauth2": "OAuth2", - "password-max-length": "密码长度不能大于256", + "password-max-length": "密码应该少于256个字符。", "redirect-uri-template": "重定向URI模板", "copy-redirect-uri": "复制重定向URI", "registration-id": "注册ID", @@ -221,17 +257,17 @@ "scope-required": "范围必填。", "tenant-name-pattern": "租户名称模式", "tenant-name-pattern-required": "租户名称模式必填。", - "tenant-name-pattern-max-length": "租户名称模式长度不能大于256", + "tenant-name-pattern-max-length": "租户名称模式应该少于256个字符。", "tenant-name-strategy": "租户名称策略", "type": "映射器类型", "uri-pattern-error": "无效的URI格式。", "url": "统一资源定位地址", "url-pattern": "无效的URL格式。", "url-required": "URL 必填。", - "url-max-length": "URL地址长度不能大于256", + "url-max-length": "URL 应该少于256个字符。", "user-info-uri": "用户信息URI", "user-info-uri-required": "用户信息 URI 必填。", - "username-max-length": "用户名称长度不能大于256", + "username-max-length": "用户名应该少于256个字符。", "user-name-attribute-name": "用户名属性键", "user-name-attribute-name-required": "用户名属性密钥必填", "protocol": "协议", @@ -240,81 +276,102 @@ "domain-schema-mixed": "HTTP+HTTPS", "enable": "启用OAuth2设置", "domains": "域名", - "mobile-apps": "移动端", - "no-mobile-apps": "无移动端应用配置", - "mobile-package": "应用程包", + "mobile-apps": "移动端应用", + "no-mobile-apps": "未配置应用程序", + "mobile-package": "应用程序包", "mobile-package-placeholder": "例如: my.example.app", "mobile-package-hint": "Android:应用程序ID,iOS:产品标识符", "mobile-package-unique": "应用程序包必须是唯一的。", "mobile-app-secret": "应用程序密钥", - "invalid-mobile-app-secret": "应用程序密钥必须包含字母,数字,字符并且长度必须在16到2048个字符。", + "invalid-mobile-app-secret": "应用程序密钥必须只包含字母和数字字符,并且长度必须介于16到2048个字符之间。", "copy-mobile-app-secret": "复制应用程序密钥", "add-mobile-app": "添加应用程序", - "delete-mobile-app": "删除应用程序", - "providers": "提供程序", + "delete-mobile-app": "删除应用程序信息", + "providers": "供应商", "platform-web": "Web", "platform-android": "Android", "platform-ios": "iOS", "all-platforms": "所有平台", - "allowed-platforms": "允许平台" + "smtp-provider": "SMTP提供商", + "allowed-platforms": "允许的平台", + "authentication": "身份验证", + "basic": "基本", + "provider": "提供商", + "redirect-url": "重定向URL", + "domain-name": "域名", + "redirect-url-template": "重定向URL模板", + "microsoft-tenant-id": "目录(租户)ID", + "microsoft-tenant-id-required": "需要目录(租户)ID", + "token-uri": "令牌URI", + "token-uri-required": "需要令牌URI", + "redirect-uri": "重定向URI", + "google-provider": "谷歌", + "microsoft-provider": "Office 365", + "sendgrid-provider": "Sendgrid", + "custom-provider": "自定义", + "generate-access-token": "生成访问令牌", + "update-access-token": "更新访问令牌", + "access-token-status": "访问令牌状态:", + "token-status-generated": "已生成", + "token-status-not-generated": "未生成" }, "smpp-provider": { "smpp-version": "SMPP版本", "smpp-host": "SMPP主机", - "smpp-host-required": "SMPP主机必填", + "smpp-host-required": "SMPP主机必填。", "smpp-port": "SMPP端口", - "smpp-port-required": "SMPP端口必填", + "smpp-port-required": "SMPP端口必填。", "system-id": "系统ID", - "system-id-required": "系统ID必填", + "system-id-required": "系统ID必填。", "password": "密码", - "password-required": "密码必填", + "password-required": "密码必填。", "type-settings": "类型设置", "source-settings": "源设置", - "destination-settings": "目的地", + "destination-settings": "目标设置", "additional-settings": "其他设置", "system-type": "系统类型", "bind-type": "绑定类型", "service-type": "服务类型", "source-address": "源地址", - "source-ton": "源TON", - "source-npi": "源NPI", - "destination-ton": "目的地TON号码", - "destination-npi": "目的地NPI号码", + "source-ton": "源 TON (号码类型)", + "source-npi": "源 NPI (编号方案标识)", + "destination-ton": "目标 TON (号码类型)", + "destination-npi": "目标 NPI (编号方案标识)", "address-range": "地址范围", "coding-scheme": "编码方案", - "bind-type-tx": "发送者", - "bind-type-rx": "接收者", - "bind-type-trx": "收发器", + "bind-type-tx": "发送端", + "bind-type-rx": "接收端", + "bind-type-trx": "发送/接收", "ton-unknown": "未知", - "ton-international": "International", - "ton-national": "National", - "ton-network-specific": "Network Specific", - "ton-subscriber-number": "Subscriber Number", - "ton-alphanumeric": "Alphanumeric", - "ton-abbreviated": "Abbreviated", - "npi-unknown": "0 - Unknown", - "npi-isdn": "1 - ISDN/telephone numbering plan (E163/E164)", - "npi-data-numbering-plan": "3 - Data numbering plan (X.121)", - "npi-telex-numbering-plan": "4 - Telex numbering plan (F.69)", - "npi-land-mobile": "6 - Land Mobile (E.212)", - "npi-national-numbering-plan": "8 - National numbering plan", - "npi-private-numbering-plan": "9 - Private numbering plan", - "npi-ermes-numbering-plan": "10 - ERMES numbering plan (ETSI DE/PS 3 01-3)", - "npi-internet": "13 - Internet (IP)", - "npi-wap-client-id": "18 - WAP Client Id (to be defined by WAP Forum)", - "scheme-smsc": "0 - SMSC Default Alphabet (ASCII for short and long code and to GSM for toll-free)", - "scheme-ia5": "1 - IA5 (ASCII for short and long code, Latin 9 for toll-free (ISO-8859-9))", - "scheme-octet-unspecified-2": "2 - Octet Unspecified (8-bit binary)", + "ton-international": "国际", + "ton-national": "国内", + "ton-network-specific": "网络特定", + "ton-subscriber-number": "用户号码", + "ton-alphanumeric": "字母数字", + "ton-abbreviated": "简写", + "npi-unknown": "0 - 未知", + "npi-isdn": "1 - ISDN/电话编号方案 (E163/E164)", + "npi-data-numbering-plan": "3 - 数据编号方案 (X.121)", + "npi-telex-numbering-plan": "4 - 电报编号方案 (F.69)", + "npi-land-mobile": "6 - 移动设备编号方案 (E.212)", + "npi-national-numbering-plan": "8 - 国家编号方案", + "npi-private-numbering-plan": "9 - 私有编号方案", + "npi-ermes-numbering-plan": "10 - ERMES 编号方案 (ETSI DE/PS 3 01-3)", + "npi-internet": "13 - 互联网 (IP)", + "npi-wap-client-id": "18 - WAP 客户端标识 (由 WAP 论坛定义)", + "scheme-smsc": "0 - SMSC 默认字母表 (ASCII 码用于短码和长码,GSM 编码用于免费电话)", + "scheme-ia5": "1 - IA5 码 (ASCII 码用于短码和长码,Latin 9 用于免费电话 (ISO-8859-9))", + "scheme-octet-unspecified-2": "2 - 八进制未指定 (8 位二进制)", "scheme-latin-1": "3 - Latin 1 (ISO-8859-1)", - "scheme-octet-unspecified-4": "4 - Octet Unspecified (8-bit binary)", + "scheme-octet-unspecified-4": "4 - 八进制未指定 (8 位二进制)", "scheme-jis": "5 - JIS (X 0208-1990)", "scheme-cyrillic": "6 - Cyrillic (ISO-8859-5)", "scheme-latin-hebrew": "7 - Latin/Hebrew (ISO-8859-8)", "scheme-ucs-utf": "8 - UCS2/UTF-16 (ISO/IEC-10646)", - "scheme-pictogram-encoding": "9 - Pictogram Encoding", - "scheme-music-codes": "10 - Music Codes (ISO-2022-JP)", - "scheme-extended-kanji-jis": "13 - Extended Kanji JIS (X 0212-1990)", - "scheme-korean-graphic-character-set": "14 - Korean Graphic Character Set (KS C 5601/KS X 1001)" + "scheme-pictogram-encoding": "9 - 图标编码", + "scheme-music-codes": "10 - 音乐编码 (ISO-2022-JP)", + "scheme-extended-kanji-jis": "13 - 扩展汉字 JIS (X 0212-1990)", + "scheme-korean-graphic-character-set": "14 - 韩文图形字符集 (KS C 5601/KS X 1001)" }, "queue-select-name": "选择队列名称", "queue-name": "名称", @@ -325,13 +382,16 @@ "queue-processing-strategy": "处理策略", "queue-configuration": "队列配置", "repository-settings": "仓库设置", + "repository": "仓库", "repository-url": "仓库 URL", "repository-url-required": "仓库 URL 必填。", "default-branch": "默认分支名称", - "repository-read-only": "Read-only", + "repository-read-only": "只读", + "show-merge-commits": "显示合并提交", "authentication-settings": "身份验证设置", "auth-method": "身份验证方法", "auth-method-username-password": "密码/访问令牌", + "auth-method-username-password-hint": "GitHub用户必须使用能够对仓库进行写入权限的访问令牌。", "auth-method-private-key": "私钥", "password-access-token": "密码/访问令牌", "change-password-access-token": "更改密码/访问令牌", @@ -345,12 +405,13 @@ "delete-repository-settings-title": "确定要删除仓库设置吗?", "delete-repository-settings-text": "请注意:确认后,仓库设置将被删除,版本控制功能将不可用。", "auto-commit-settings": "自动提交设置", + "auto-commit": "自动提交", "auto-commit-entities": "自动提交实体", "no-auto-commit-entities-prompt": "没有设置自动提交的实体", "delete-auto-commit-settings-title": "确定要删除自动提交设置吗?", "delete-auto-commit-settings-text": "请注意:确认后,自动提交设置将被删除,所有实体的自动提交将被禁用。", "2fa": { - "2fa": "双因素身份验证", + "2fa": "双因素认证", "available-providers": "可用选项", "issuer-name": "发行者名称", "issuer-name-required": "发行者名称必填。", @@ -369,7 +430,7 @@ "total-allowed-time-for-verification": "总允许验证时间(秒)", "total-allowed-time-for-verification-pattern": "总允许验证最小时间为60秒", "total-allowed-time-for-verification-required": "总允许验证时间必填。", - "use-system-two-factor-auth-settings": "使用系统双因素身份验证设置", + "use-system-two-factor-auth-settings": "使用系统双因素认证设置", "verification-code-check-rate-limit": "验证码检查速率限制", "verification-code-lifetime": "验证码生存期(秒)", "verification-code-lifetime-pattern": "验证码生存期必须为正整数。", @@ -381,14 +442,45 @@ "within-time": "在时间内 (秒)", "within-time-pattern": "时间必须是正整数。", "within-time-required": "时间必填。" - } + }, + "jwt": { + "security-settings": "JWT 安全设置", + "issuer-name": "发行者名称", + "issuer-name-required": "发行者名称必填。", + "signings-key": "签名密钥", + "signings-key-hint": "Base64编码的字符串,至少256位数据。", + "signings-key-required": "签名密钥必填。", + "signings-key-min-length": "签名密钥必须至少为256位的数据。", + "signings-key-base64": "签名密钥必须是Base64格式。", + "expiration-time": "令牌过期时间(秒)", + "expiration-time-required": "令牌过期时间是必填。", + "expiration-time-pattern": "令牌过期时间必须是一个正整数。", + "expiration-time-min": "最小时间为60秒(1分钟)。", + "refresh-expiration-time": "刷新令牌过期时间(秒)", + "refresh-expiration-time-required": "刷新令牌过期时间必填。", + "refresh-expiration-time-pattern": "刷新令牌的过期时间必须是一个正整数。", + "refresh-expiration-time-min": "最小时间为900秒(15分钟)。", + "refresh-expiration-time-less-token": "刷新令牌时间必须大于令牌过期时间。", + "generate-key": "生成密钥", + "info-header": "所有用户将需要重新登录。", + "info-message": "更改JWT签名密钥将导致所有已发行的令牌无效,所有用户将需要重新登录,这也会影响使用Rest API/Websockets的脚本。" + }, + "resources": "资源", + "notifications": "通知", + "notifications-settings": "通知设置", + "slack-api-token": "Slack API令牌", + "slack": "Slack", + "slack-settings": "Slack 设置" }, "alarm": { "alarm": "告警", "alarms": "告警", + "all-alarms": "所有告警", "select-alarm": "选择告警", "no-alarms-matching": "未找到匹配 '{{entity}}' 的告警", "alarm-required": "告警必填", + "alarm-filter": "告警筛选器", + "filter": "筛选器", "alarm-status": "告警状态", "alarm-status-list": "告警状态列表", "any-status": "任何状态", @@ -412,12 +504,24 @@ "originator": "发起者", "originator-type": "发起者类型", "details": "详情", + "originator-label": "发起者标签", + "assign": "分配", + "assignments": "任务分配", + "assignee": "受托人", + "assignee-id": "受托人ID", + "assignee-first-name": "受托人姓氏", + "assignee-last-name": "受托人名字", + "assignee-email": "受托人邮箱", + "unassigned": "未分配", + "assignee-not-set": "所有", "status": "状态", "alarm-details": "告警详细信息", "start-time": "开始时间", + "assign-time": "分配时间", "end-time": "结束时间", "ack-time": "确认时间", "clear-time": "清除时间", + "duration": "持续时间", "alarm-severity-list": "警报严重性列表", "any-severity": "任何严重程度", "severity-critical": "危险", @@ -427,31 +531,63 @@ "severity-indeterminate": "不确定", "acknowledge": "应答", "clear": "清除", + "delete": "删除", "search": "查找告警", - "selected-alarms": "已选择 { count, plural, =1 {# 个告警} other {# 个告警} }", + "selected-alarms": "已选择 { count, plural, =1 {1 个告警} other {# 个告警} }", "no-data": "无数据显示", "polling-interval": "告警轮询间隔(秒)", "polling-interval-required": "告警轮询间隔必填。", "min-polling-interval-message": "轮询间隔至少是1秒。", - "aknowledge-alarms-title": "确认 { count, plural, =1 {# 个告警} other {# 个告警} }", - "aknowledge-alarms-text": "确定要确认 { count, plural, =1 {# 个告警} other {# 个告警} }吗?", + "aknowledge-alarms-title": "确认 { count, plural, =1 {1 个告警} other {# 个告警} }", + "aknowledge-alarms-text": "确定要确认 { count, plural, =1 {1 个告警} other {# 个告警} }吗?", "aknowledge-alarm-title": "确认告警", "aknowledge-alarm-text": "确定要确认告警吗?", - "clear-alarms-title": "清除 { count, plural, =1 {# 个告警} other {# 个告警} }", - "clear-alarms-text": "确定要清除 { count, plural, =1 {# 个告警} other {# 个告警} }?", - "clear-alarm-title": "清除警报", - "clear-alarm-text": "确定要清除警报吗?", + "selected-alarms-are-acknowledged": "所选的告警已经被确认", + "clear-alarms-title": "清除 { count, plural, =1 {1 个告警} other {# 个告警} }", + "clear-alarms-text": "确定要清除 { count, plural, =1 {1 个告警} other {# 个告警} }?", + "clear-alarm-title": "清除告警", + "clear-alarm-text": "确定要清除告警吗?", + "delete-alarms-title": "删除 { count, plural, =1 {1 个告警} other {# 个告警} }", + "delete-alarms-text": "您确定要删除 { count, plural, =1 {1 个告警} other {# 个告警} } 吗?", + "selected-alarms-are-cleared": "所选的告警已清除", "alarm-status-filter": "告警状态筛选器", - "alarm-filter": "告警筛选器", + "alarm-filter-title": "告警筛选器", + "assigned": "已分配", + "filter-title": "筛选器", "max-count-load": "要加载的最大告警数(0-无限制)", "max-count-load-required": "加载的最大告警数必填。", "max-count-load-error-min": "最小值为0。", "fetch-size": "获取大小", - "fetch-size-required": "Fetch size 必填。", + "fetch-size-required": "获取大小必填。", "fetch-size-error-min": "最小值为10。", "alarm-type-list": "告警类型列表", "any-type": "任何类型", - "search-propagated-alarms": "检索已传递的警报" + "assigned-to-current-user": "分配给当前用户", + "assigned-to-me": "分配给我", + "search-propagated-alarms": "检索已传递的警报", + "comments": "告警评论", + "show-more": "显示更多", + "additional-info": "附加信息", + "alarm-type": "告警类型", + "enter-alarm-type": "输入告警类型", + "no-alarm-types-matching": "找不到与 '{{entitySubtype}}' 相匹配的告警类型。", + "alarm-type-list-empty": "未选择告警类型。" + }, + "alarm-activity": { + "add": "添加评论", + "alarm-comment": "告警评论", + "comments": "评论", + "delete-alarm-comment": "你想删除这条评论吗?", + "refresh": "刷新", + "oldest-first": "最早优先", + "newest-first": "最新优先", + "activity": "活动", + "export": "导出 CSV", + "author": "作者", + "created-date": "创建时间", + "edited-date": "编辑时间", + "text": "文本", + "system": "系统" }, "alias": { "add": "添加别名", @@ -472,8 +608,8 @@ "filter-type-device-type-description": "类型为 '{{deviceTypes}}' 的设备", "filter-type-device-type-and-name-description": "类型为 '{{deviceTypes}}' 且以 '{{prefix}}' 开头的设备", "filter-type-entity-view-type": "实体视图类型", - "filter-type-entity-view-type-description": "类型为 '{{entityViewTypes}}' 的实体视图", - "filter-type-entity-view-type-and-name-description": "类型为 {{entityViewTypes}}' 且以 '{{prefix}}' 开头的实体视图", + "filter-type-entity-view-type-description": "类型为 '{{entityViews}}' 的实体视图", + "filter-type-entity-view-type-and-name-description": "类型为 {{entityViews}}' 且以 '{{prefix}}' 开头的实体视图", "filter-type-edge-type": "边缘类型", "filter-type-edge-type-description": "类型为 '{{edgeTypes}}' 的边缘", "filter-type-edge-type-and-name-description": "类型为 '{{edgeTypes}}' 且以 '{{prefix}}' 开头的边缘", @@ -512,7 +648,7 @@ "management": "资产管理", "view-assets": "查看资产", "add": "添加资产", - "asset-type-max-length": "资产类型要小于256", + "asset-type-max-length": "资产类型应该小于256个字符", "assign-to-customer": "分配给客户", "assign-asset-to-customer": "将资产分配给客户", "assign-asset-to-customer-text": "请选择要分配给客户的资产", @@ -535,27 +671,27 @@ "asset-types": "资产类型", "name": "名称", "name-required": "名称必填。", - "name-max-length": "名称长度不能大于256", - "label-max-length": "标签长度应小于256", + "name-max-length": "名称应该小于256个字符。", + "label-max-length": "标签应该小于256个字符。", "description": "说明", "type": "类型", "type-required": "类型必填。", "details": "详情", "events": "事件", - "add-asset-text": "添加新资产", + "add-asset-text": "添加资产", "asset-details": "资产详情", "assign-assets": "分配资产", - "assign-assets-text": "分配 { count, plural, =1 {# 个资产} other {# 个资产} } 给客户", + "assign-assets-text": "分配 { count, plural, =1 {1 个资产} other {# 个资产} } 给客户", "assign-asset-to-edge-title": "将资产分配给边缘", "assign-asset-to-edge-text": "请选择要分配给边缘的资产", "delete-assets": "删除资产", "unassign-assets": "取消分配资产", - "unassign-assets-action-title": "从客户处取消分配 { count, plural, =1 {# 个资产} other {# 个资产} }", + "unassign-assets-action-title": "从客户处取消分配 { count, plural, =1 {1 个资产} other {# 个资产} }", "assign-new-asset": "分配新资产", "delete-asset-title": "确定要删除资产 '{{assetName}}'吗?", "delete-asset-text": "请注意:确认后,资产及其所有相关数据将不可恢复。", - "delete-assets-title": "确定要删除 { count, plural, =1 {# 个资产} other {# 个资产} }吗?", - "delete-assets-action-title": "删除 { count, plural, =1 {# 个资产} other {# 个资产} }", + "delete-assets-title": "确定要删除 { count, plural, =1 {1 个资产} other {# 个资产} }吗?", + "delete-assets-action-title": "删除 { count, plural, =1 {1 个资产} other {# 个资产} }", "delete-assets-text": "请注意:确认后,所有选定的资产将被删除,所有相关的数据将不可恢复。", "make-public-asset-title": "确定要将资产 '{{assetName}}' 设为公开吗?", "make-public-asset-text": "确认后,资产及其所有数据将被公开并被他人访问。", @@ -564,7 +700,7 @@ "unassign-asset-title": "确定要取消对'{{assetName}}'资产的分配吗?", "unassign-asset-text": "确认后,资产将未分配,客户无法访问。", "unassign-asset": "未分配资产", - "unassign-assets-title": "确定要取消分配 { count, plural, =1 {# 个资产} other {# 个资产} }吗?", + "unassign-assets-title": "确定要取消分配 { count, plural, =1 {1 个资产} other {# 个资产} }吗?", "unassign-assets-text": "确认后,所有选定的资产将被分配,客户无法访问。", "unassign-assets-from-edge": "取消分配边缘", "copyId": "复制资产ID", @@ -578,30 +714,34 @@ "asset-file": "资产档案", "label": "标签", "search": "查找资产", - "assign-asset-to-edge": "Assign Asset(s) To Edge", + "assign-asset-to-edge": "将资产分配给边缘", "unassign-asset-from-edge": "取消分配边缘", "unassign-asset-from-edge-title": "确定要取消对'{{assetName}}'资产的分配吗?", "unassign-asset-from-edge-text": "确认后,所有选定的资产将被分配,边缘无法访问。", "unassign-assets-from-edge-title": "确定要取消分配 { count, plural, =1 {1 资产} other {# 个资产} }吗?", "unassign-assets-from-edge-text": "确认后,所有选定的资产将被分配,边缘无法访问。", - "selected-assets": "已选择 { count, plural, =1 {# 个资产} other {# 个资产} }" + "selected-assets": "已选择 { count, plural, =1 {1 个资产} other {# 个资产} }" }, "attribute": { "attributes": "属性", "latest-telemetry": "最新遥测数据", + "no-latest-telemetry": "没有最新的遥测数据", "attributes-scope": "设备属性范围", + "scope-telemetry": "遥测", "scope-latest-telemetry": "最新遥测数据", "scope-client": "客户端属性", "scope-server": "服务端属性", "scope-shared": "共享属性", "add": "添加属性", "key": "键名", - "key-max-length": "关键字长度应小于256", + "key-max-length": "键应该小于256个字符。", "last-update-time": "最后更新时间", "key-required": "属性键必填。", "value": "数值", "value-required": "属性值必填。", - "delete-attributes-title": "确定要删除 { count, plural, =1 {# 个属性} other {# 个属性} }吗?", + "telemetry-key-required": "需要遥测键", + "telemetry-value-required": "需要遥测数值", + "delete-attributes-title": "确定要删除 { count, plural, =1 {1 个属性} other {# 个属性} }吗?", "delete-attributes-text": "注意,确认后所有选中的属性都会被删除。", "delete-attributes": "删除属性", "enter-attribute-value": "输入属性值", @@ -611,32 +751,50 @@ "prev-widget": "上一个部件", "add-to-dashboard": "添加到仪表板", "add-widget-to-dashboard": "将部件添加到仪表板", - "selected-attributes": "已选择{ count, plural, =1 {# 个属性} other {# 个属性} }", - "selected-telemetry": "已选择 { count, plural, =1 {# 遥测} other {# 遥测} }", + "selected-attributes": "已选择 { count, plural, =1 {1 个属性} other {# 个属性} }", + "selected-telemetry": "已选择 { count, plural, =1 {1 遥测装置} other {# 遥测装置} }", "no-attributes-text": "未找到属性", - "no-telemetry-text": "未找到遥测数据" + "no-telemetry-text": "未找到遥测数据", + "copy-key": "复制键", + "add-telemetry": "添加遥测数据", + "copy-value": "复制数值", + "delete-timeseries": { + "start-time": "开始时间", + "ends-on": "结束时间", + "strategy": "策略", + "delete-strategy": "删除策略", + "all-data": "删除所有数据", + "all-data-except-latest-value": "删除除最新值外的所有数据", + "latest-value": "删除最新值", + "all-data-for-time-period": "删除指定时间段的所有数据", + "rewrite-latest-value": "重写最新值" + } }, "api-usage": { - "api-usage": "Api统计", - "alarm": "警报", - "alarms-created": "创建警报数", - "alarms-created-daily-activity": "每天产生的警报", - "alarms-created-hourly-activity": "每小时产生的警报", - "alarms-created-monthly-activity": "每月产生的警报", + "api-features": "Api统计", + "api-usage": "Api 使用统计", + "alarm": "告警", + "alarms-created": "创建告警数", + "alarms-created-daily-activity": "每天产生的告警", + "alarms-created-hourly-activity": "每小时产生的告警", + "alarms-created-monthly-activity": "每月产生的告警", "data-points": "数据点", - "data-points-storage-days": "日存储数据点数", + "data-points-storage-days": "存储点天", + "device-api": "设备 API", "email": "邮件", - "email-messages": "邮件信息", - "email-messages-daily-activity": "每天产生的邮件信息", - "email-messages-monthly-activity": "每月产生的邮件信息", - "exceptions": "例外", - "executions": "执行", + "email-messages": "邮件消息", + "email-messages-daily-activity": "每天产生的邮件消息", + "email-messages-monthly-activity": "每月产生的邮件消息", + "exceptions": "异常", + "executions": "执行数", + "scripts": "Scripts", + "scripts-hourly-activity": "脚本每小时活动", + "scripts-daily-activity": "脚本每日活动", + "scripts-monthly-activity": "脚本每月活动", "javascript": "JavaScript", - "javascript-executions": "JavaScript例外", - "javascript-functions": "JavaScript函数", - "javascript-functions-daily-activity": "每天执行的JavaScript函数", - "javascript-functions-hourly-activity": "每小时执行的JavaScript函数", - "javascript-functions-monthly-activity": "每月执行的JavaScript函数", + "javascript-executions": "JavaScript 执行数", + "tbel": "TBEL", + "tbel-executions": "TBEL 执行数", "latest-error": "最新错误", "messages": "消息", "notifications": "通知", @@ -656,11 +814,11 @@ "rule-engine-monthly-activity": "每月的规则引擎活动", "rule-engine-statistics": "规则引擎统计信息", "rule-node": "规则节点", - "sms": "SMS", - "sms-messages": "短信信息", + "sms": "短信", + "sms-messages": "短信消息", "sms-messages-daily-activity": "每天的短信息", "sms-messages-monthly-activity": "每小时的短信息", - "successful": "${entityName}成功", + "successful": "${entityName} 成功", "telemetry": "遥测数据", "telemetry-persistence": "遥测持久化", "telemetry-persistence-daily-activity": "每天的遥测持久化", @@ -675,6 +833,18 @@ "view-details": "查看详细信息", "view-statistics": "查看统计信息" }, + "api-limit": { + "cassandra-queries": "Cassandra查询次数", + "entity-version-creation": "实体版本创建次数", + "entity-version-load": "实体版本加载次数", + "notification-requests": "通知请求次数", + "notification-requests-per-rule": "每个规则的通知请求次数", + "rest-api-requests": "REST API请求次数", + "rest-api-requests-per-customer": "每个客户的REST API请求次数", + "transport-messages": "传输消息次数", + "transport-messages-per-device": "每个设备的传输消息次数", + "ws-updates-per-session": "每个会话的WS更新次数" + }, "audit-log": { "audit": "审计", "audit-logs": "审计日志", @@ -705,6 +875,11 @@ "type-relations-delete": "删除所有关联", "type-alarm-ack": "已确认", "type-alarm-clear": "已清除", + "type-alarm-assign": "已分配", + "type-alarm-unassign": "未分配", + "type-added-comment": "添加评论", + "type-updated-comment": "更新评论", + "type-deleted-comment": "删除评论", "type-login": "登录", "type-logout": "注销", "type-lockout": "锁定", @@ -721,7 +896,8 @@ "type-provision-success": "设备已预配置", "type-provision-failure": "设备预配置失败", "type-timeseries-updated": "遥测数据已更新", - "type-timeseries-deleted": "遥测数据已删除" + "type-timeseries-deleted": "遥测数据已删除", + "type-sms-sent": "短信发送" }, "confirm-on-exit": { "message": "有未保存的更改,确定要离开此页吗?", @@ -731,17 +907,17 @@ "contact": { "country": "国家", "city": "城市", - "state": "州", + "state": "省份", "postal-code": "邮政编码", "postal-code-invalid": "只允许数字。", "address": "地址", "address2": "地址二", - "phone": "电话", + "phone": "手机号码", "email": "电子邮件", "no-address": "无地址", - "state-max-length": "省份名称长度应小于256", - "phone-max-length": "电话号码长度应小于256", - "city-max-length": "城市名称长度应小于256" + "state-max-length": "省份长度应该小于256个字符", + "phone-max-length": "手机号码长度应该小于256个字符", + "city-max-length": "城市应该小于256个字符" }, "common": { "username": "用户名", @@ -752,7 +928,9 @@ "created-time": "创建时间", "loading": "正在加载中...", "proceed": "继续", - "open-details-page": "打开详情页" + "open-details-page": "打开详情页", + "not-found": "未找到", + "documentation": "文档" }, "content-type": { "json": "Json", @@ -784,13 +962,13 @@ "manage-public-assets": "管理公共资产", "manage-customer-edges": "管理客户边缘", "manage-public-edges": "管理公共边缘", - "add-customer-text": "添加新客户", + "add-customer-text": "添加客户", "no-customers-text": "未找到客户", "customer-details": "客户详情", "delete-customer-title": "确定要删除客户'{{customerTitle}}'吗?", "delete-customer-text": "请注意:确认后,客户及其所有相关数据将不可恢复。", - "delete-customers-title": "确定要删除 { count, plural, =1 {# 个客户} other {# 个客户} }吗?", - "delete-customers-action-title": "删除 { count, plural, =1 {# 个客户} other {# 个客户} }", + "delete-customers-title": "确定要删除 { count, plural, =1 {1 个客户} other {# 个客户} }吗?", + "delete-customers-action-title": "删除 { count, plural, =1 {1 个客户} other {# 个客户} }", "delete-customers-text": "请注意:确认后,所有选定的客户将被删除,所有相关数据将不可恢复。", "manage-users": "管理用户", "manage-assets": "管理资产", @@ -798,7 +976,7 @@ "manage-dashboards": "管理仪表板", "title": "标题", "title-required": "标题必填。", - "title-max-length": "标题长度必须少于256个字符", + "title-max-length": "标题应该小于256个字符", "description": "说明", "details": "详情", "events": "事件", @@ -811,10 +989,17 @@ "default-customer": "默认客户", "default-customer-required": "为了调试租户级别上的仪表板,需要默认客户。", "search": "查找客户", - "selected-customers": "已选择 { count, plural, =1 {# 个客户} other {# 个客户} }", + "selected-customers": "已选择 { count, plural, =1 {1 个客户} other {# 个客户} }", "edges": "客户边缘实例", "manage-edges": "管理边缘" }, + "date": { + "last-update-n-ago": "N分钟前的最后更新", + "last-update-n-ago-text": "最后更新{{ agoText }}", + "custom-date": "自定义日期", + "format": "格式", + "preview": "预览" + }, "datetime": { "date-from": "开始日期", "time-from": "开始时间", @@ -823,7 +1008,7 @@ }, "dashboard": { "dashboard": "仪表板", - "dashboards": "仪表板库", + "dashboards": "仪表板", "management": "仪表板管理", "view-dashboards": "查看仪表板", "add": "添加仪表板", @@ -843,40 +1028,41 @@ "unassign-from-customers-text": "请选择从仪表板中取消分配的客户", "no-dashboards-text": "未找到仪表板", "no-widgets": "没有配置部件", - "add-widget": "添加新的部件", + "add-widget": "添加部件", + "add-widget-button-text": "添加部件", "title": "标题", "image": "仪表板图片", "mobile-app-settings": "移动端应用设置", "mobile-order": "移动端应用中的仪表板排序", "mobile-hide": "在移动端应用中隐藏仪表板", - "update-image": "更新缩略图", + "update-image": "更新仪表板图片", "take-screenshot": "截图", "select-widget-title": "选择部件", "select-widget-value": "{{title}}: 选择部件", "select-widget-subtitle": "可用的部件类型列表", "delete": "删除仪表板", "title-required": "标题必填。", - "title-max-length": "标题长度必须少于256个字符", + "title-max-length": "标题应该少于256个字符。", "description": "说明", "details": "详情", "dashboard-details": "仪表板详情", - "add-dashboard-text": "添加新的仪表板", + "add-dashboard-text": "添加仪表板", "assign-dashboards": "分配仪表板", "assign-new-dashboard": "分配新的仪表板", - "assign-dashboards-text": "分配 { count, plural, =1 {# 个仪表板} other {# 个仪表板} } 给客户", - "unassign-dashboards-action-text": "取消分配 { count, plural, =1 {# 个仪表板} other {# 个仪表板} } 给客户", + "assign-dashboards-text": "分配 { count, plural, =1 {1 个仪表板} other {# 个仪表板} } 给客户", + "unassign-dashboards-action-text": "取消分配 { count, plural, =1 {1 个仪表板} other {# 个仪表板} } 给客户", "delete-dashboards": "删除仪表板", "unassign-dashboards": "取消分配仪表板", - "unassign-dashboards-action-title": "取消分配此客户 { count, plural, =1 {# 个仪表板} other {# 个仪表板} }", + "unassign-dashboards-action-title": "取消分配此客户 { count, plural, =1 {1 个仪表板} other {# 个仪表板} }", "delete-dashboard-title": "确定要删除仪表板 '{{dashboardTitle}}'吗?", "delete-dashboard-text": "请注意:确认后,仪表板及其所有相关数据将不可恢复。", - "delete-dashboards-title": "确定要删除 { count, plural, =1 {# 个仪表板} other {# 个仪表板} }吗?", - "delete-dashboards-action-title": "删除 { count, plural, =1 {# 个仪表板} other {# 个仪表板} }", + "delete-dashboards-title": "确定要删除 { count, plural, =1 {1 个仪表板} other {# 个仪表板} }吗?", + "delete-dashboards-action-title": "删除 { count, plural, =1 {1 个仪表板} other {# 个仪表板} }", "delete-dashboards-text": "请注意:确认后,所有选定的仪表板将被删除,所有相关数据将不可恢复。", "unassign-dashboard-title": "确定要取消分配仪表板 '{{dashboardTitle}}'吗?", "unassign-dashboard-text": "确认后,面板将被取消分配,客户将无法访问。", "unassign-dashboard": "取消分配仪表板", - "unassign-dashboards-title": "确定要取消分配仪表板 { count, plural, =1 {# 个仪表板} other {# 个仪表板} } 吗?", + "unassign-dashboards-title": "确定要取消分配仪表板 { count, plural, =1 {1 个仪表板} other {# 个仪表板} } 吗?", "unassign-dashboards-text": "确认后,所有选定的仪表板将被取消分配,客户将无法访问。", "public-dashboard-title": "仪表板现已公开", "public-dashboard-text": "仪表板 {{dashboardTitle}} 已被公开,可通过如下 链接访问:", @@ -890,7 +1076,7 @@ "no-dashboards-matching": "未找到匹配 '{{entity}}' 的仪表板。", "dashboard-required": "仪表板必填。", "select-existing": "选择现有仪表板", - "create-new": "创建新的仪表板", + "create-new": "创建仪表板", "new-dashboard-title": "新仪表板标题", "open-dashboard": "打开仪表板", "set-background": "设置背景", @@ -903,7 +1089,7 @@ "maximum-upload-file-size": "最大上传文件大小: {{ size }}", "cannot-upload-file": "无法上传文件", "settings": "设置", - "layout-settings": "Layout settings", + "layout-settings": "布局设置", "columns-count": "列数", "columns-count-required": "需要列数。", "min-columns-count-message": "只允许最少10列", @@ -920,24 +1106,25 @@ "vertical-margin-required": "需要垂直边距值。", "min-vertical-margin-message": "只允许0作为最小垂直边距值。", "max-vertical-margin-message": "只允许50作为最大垂直边距值。", + "apply-outer-margin": "为布局的两侧设置边距", "autofill-height": "自动填充布局高度", "mobile-layout": "移动端布局设置", "mobile-row-height": "移动端行高距(px)", "mobile-row-height-required": "移动端行高距必填。", "min-mobile-row-height-message": "移动端行高距至少5px。", "max-mobile-row-height-message": "移动端行高距最多200px。", - "title-settings": "Title settings", + "title-settings": "标题设置", "display-title": "显示仪表板标题", "title-color": "标题颜色", - "toolbar-settings": "Toolbar settings", - "hide-toolbar": "Hide toolbar", + "toolbar-settings": "工具栏设置", + "hide-toolbar": "隐藏工具栏", "toolbar-always-open": "工具栏常驻", "display-dashboards-selection": "显示仪表板选项", "display-entities-selection": "显示实体选项", "display-filters": "显示筛选器", "display-dashboard-timewindow": "显示时间窗口", "display-dashboard-export": "显示导出", - "display-update-dashboard-image": "显示仪表板缩略图", + "display-update-dashboard-image": "显示更新仪表板图像", "dashboard-logo-settings": "Logo设置", "display-dashboard-logo": "在仪表板全屏模式下显示 Logo", "dashboard-logo-image": "仪表板 Logo 图片", @@ -946,17 +1133,17 @@ "import": "导入仪表板", "export": "导出仪表板", "export-failed-error": "无法导出仪表板: {{error}}", - "create-new-dashboard": "创建新的仪表板", + "create-new-dashboard": "创建仪表板", "dashboard-file": "仪表板文件", "invalid-dashboard-file-error": "无法导入仪表板: 仪表板数据结构无效。", "dashboard-import-missing-aliases-title": "配置导入仪表板使用的别名", - "create-new-widget": "创建新部件", + "create-new-widget": "创建部件", "import-widget": "导入部件", "widget-file": "部件文件", "invalid-widget-file-error": "无法导入窗口部件: 窗口部件数据结构无效。", "widget-import-missing-aliases-title": "配置导入的窗口部件使用的别名", - "open-toolbar": "打开仪表板工具栏", - "close-toolbar": "关闭工具栏", + "open-toolbar": "显示工具栏", + "close-toolbar": "收起工具栏", "configuration-error": "配置错误", "alias-resolution-error-title": "仪表板别名配置错误", "invalid-aliases-config": "无法找到与某些别名筛选器匹配的任何设备。
请联系您的管理员以解决此问题。", @@ -964,15 +1151,16 @@ "assignedToCustomer": "分配给客户", "assignedToCustomers": "分配给客户", "public": "公开", - "copyId": "复制仪表板Id", - "idCopiedMessage": "仪表板ID已经复制到粘贴板", + "copyId": "复制仪表板ID", + "idCopiedMessage": "仪表板ID已复制到剪贴板", "public-link": "公共链接", "copy-public-link": "复制公共链接", "public-link-copied-message": "仪表板的公共链接已被复制到剪贴板", "manage-states": "仪表板状态管理", "states": "仪表板状态", + "states-short": "状态", "search-states": "仪表板状态检索", - "selected-states": "已选择 { count, plural, =1 {# 个仪表板状态} other {# 个仪表板状态} }", + "selected-states": "已选择 { count, plural, =1 {1 个仪表板状态} other {# 个仪表板状态} }", "edit-state": "仪表板状态编辑", "delete-state": "删除仪表板状态", "add-state": "添加仪表板状态", @@ -991,19 +1179,22 @@ "select-state": "选择目标状态", "state-controller": "状态控制", "search": "查找仪表板", - "selected-dashboards": "已选择 { count, plural, =1 {# 个仪表盘} other {# 个仪表盘} }", + "selected-dashboards": "已选择 { count, plural, =1 {1 个仪表盘} other {# 个仪表盘} }", "home-dashboard": "首页仪表板", "home-dashboard-hide-toolbar": "隐藏首页仪表板工具栏", "unassign-dashboard-from-edge-text": "确认后,所有选定的仪表板将被取消分配,边缘将无法访问。", - "unassign-dashboards-from-edge-title": "确定要取消分配仪表板 { count, plural, =1 {# 个仪表板} other {# 个仪表板} } 吗?", + "unassign-dashboards-from-edge-title": "确定要取消分配仪表板 { count, plural, =1 {1 个仪表板} other {# 个仪表板} } 吗?", "unassign-dashboards-from-edge-text": "确认后,所有选定的仪表板将被取消分配,边缘将无法访问。", "assign-dashboard-to-edge": "将仪表板分配给边缘", "assign-dashboard-to-edge-text": "请选择要分配给边缘的仪表板", - "non-existent-dashboard-state-error": "仪表盘\"{{ stateId }}\"未找到" + "non-existent-dashboard-state-error": "找不到ID为 '{{ stateId }}' 的仪表板状态。", + "edit-mode": "编辑模式" }, "datakey": { "settings": "设置", + "general": "常规", "advanced": "高级", + "key": "键", "label": "标签", "color": "颜色", "units": "单位符号", @@ -1018,44 +1209,54 @@ "timeseries-required": "实体 Timeseries 必填。", "timeseries-or-attributes-required": "实体 Timeseries/属性必填。", "alarm-fields-timeseries-or-attributes-required": "告警字段或实体 Timeseries/属性必填。", - "maximum-timeseries-or-attributes": "最多允许 { count, plural, =1 {# 个 timeseries/属性。} other {# 个 timeseries/属性。} }", + "maximum-timeseries-or-attributes": "最多允许 { count, plural, =1 {1 个 timeseries/属性。} other {# 个 timeseries/属性。} }", "alarm-fields-required": "告警字段必填。", "function-types": "函数类型", "function-type": "函数类型", - "function-types-required": "需要函数类型。", - "alarm-keys": "报警数据键", - "alarm-key": "报警数据键", - "alarm-key-functions": "报警数据函数", - "alarm-key-function": "报警数据函数", - "latest-keys": "最新数据键", - "latest-key": "最新数据键", - "latest-key-functions": "最新数据函数", - "latest-key-function": "最新数据函数", - "timeseries-keys": "时序数据键", - "timeseries-key": "时序数据键", - "timeseries-key-functions": "时序数据函数", - "timeseries-key-function": "时序数据函数", - "maximum-function-types": "最多允许 { count, plural, =1 {# 个函数类型} other {# 个函数类型} }", + "function-types-required": "函数类型必填。", + "data-keys": "数据键", + "data-key": "数据键", + "data-keys-required": "数据键是必须的。", + "data-key-required": "数据键是必须的。", + "alarm-keys": "告警数据键", + "alarm-key": "告警数据键", + "alarm-key-functions": "告警数据键函数", + "alarm-key-function": "告警数据键函数", + "latest-keys": "最新值数据键", + "latest-key": "最新值数据键", + "latest-key-functions": "最新值数据键函数", + "latest-key-function": "最新值数据键函数", + "timeseries-keys": "Timeseries 数据键", + "timeseries-key": "Timeseries 数据键", + "timeseries-key-functions": "Timeseries数据键函数", + "timeseries-key-function": "Timeseries数据键函数", + "maximum-function-types": "最多允许 { count, plural, =1 {1 个函数类型} other {# 个函数类型} }", "time-description": "当前值的时间戳;", "value-description": "当前值;", "prev-value-description": "上一次函数调用的结果;", "time-prev-description": "上一个值的时间戳;", "prev-orig-value-description": "先前的原始值;", "aggregation": "聚合", - "aggregation-type-hint-common": "出于性能原因聚合值计算仅适用于“当前日期”、“当前月份”等固定时间间隔,而不适用于“最近30分钟”或“最近24小时”等滑动窗口间隔。", - "aggregation-type-none-hint": "取最新值。", - "aggregation-type-min-hint": "在选定时间窗口内的数据点中查找最小值。", - "aggregation-type-max-hint": "在选定时间窗口内的数据点中查找最大值。", - "aggregation-type-avg-hint": "计算选定时间窗口内数据点之间的平均值。", - "aggregation-type-sum-hint": "对选定时间窗口内所有数据点的值求和。", - "aggregation-type-count-hint": "选定时间窗口内的数据点总数。", + "aggregation-type-hint-common": "出于性能原因,聚合值的计算仅适用于固定的时间间隔,如“当前天”,“当前月”等,而不适用于滑动窗口间隔,例如“过去30分钟”或“过去24小时”。", + "aggregation-type-none-hint": "获取最新值。", + "aggregation-type-min-hint": "在所选时间窗口内找出数据点中的最小值。", + "aggregation-type-max-hint": "在所选时间窗口内找出数据点中的最大值。", + "aggregation-type-avg-hint": "在所选时间窗口内计算数据点的平均值。", + "aggregation-type-sum-hint": "在所选时间窗口内计算数据点所有值的总和。", + "aggregation-type-count-hint": "在所选时间窗口内计算数据点的总数。", "delta-calculation": "增量计算", "enable-delta-calculation": "启用增量计算", "enable-delta-calculation-hint": "启用后将根据选定时间窗口和指定比较时段的聚合值计算数据键值。出于性能原因,增量计算仅适用于历史时间窗口,而不适用于实时值。例如,您可以计算昨天的能耗与前天的能耗之间的差值。", "delta-calculation-result": "增量计算结果", "delta-calculation-result-previous-value": "上个值", "delta-calculation-result-delta-absolute": "绝对值", - "delta-calculation-result-delta-percent": "百分比" + "delta-calculation-result-delta-percent": "百分比", + "source": "来源", + "latest": "最新", + "latest-value": "最新值", + "delta": "增量", + "percent": "百分比", + "absolute": "绝对值" }, "datasource": { "type": "数据源类型", @@ -1076,14 +1277,14 @@ "management": "设备管理", "view-devices": "查看设备", "device-alias": "设备别名", - "device-type-max-length": "设备类型长度必须少于256个字符", + "device-type-max-length": "设备类型应该小于256个字符", "aliases": "设备别名", "no-alias-matching": "'{{alias}}' 未找到。", "no-aliases-found": "未找到别名。", "no-key-matching": "'{{key}}' 未找到。", "no-keys-found": "未找到密钥。", - "create-new-alias": "创建一个新的!", - "create-new-key": "创建一个新的!", + "create-new-alias": "创建别名", + "create-new-key": "创建键", "duplicate-alias-error": "找到重复别名 '{{alias}}'。
设备别名必须是唯一的。", "configure-alias": "配置 '{{alias}}' 别名", "no-devices-matching": "未找到与 '{{entity}}' 匹配的设备。", @@ -1109,16 +1310,16 @@ "no-devices-text": "未找到设备", "assign-to-customer-text": "请选择客户分配设备", "device-details": "设备详细信息", - "add-device-text": "添加新设备", + "add-device-text": "添加设备", "credentials": "凭据", "manage-credentials": "管理凭据", "delete": "删除设备", "assign-devices": "分配设备", - "assign-devices-text": "将 {count,plural, =1 {# 个设备} other {# 个设备} }分配给客户", + "assign-devices-text": "将 {count,plural,=1 {1 个设备} other {# 个设备} }分配给客户", "delete-devices": "删除设备", "unassign-from-customer": "取消分配客户", "unassign-devices": "取消分配设备", - "unassign-devices-action-title": "取消分配此客户 {count,plural, =1 {# 个设备} other {# 个设备} }", + "unassign-devices-action-title": "取消分配此客户 {count,plural,=1 {1 个设备} other {# 个设备} }", "unassign-device-from-edge-title": "确定要取消分配设备 '{{deviceName}}' 吗?", "unassign-device-from-edge-text": "确认后,设备将被取消分配,边缘将无法访问。", "unassign-devices-from-edge": "取消分配边缘", @@ -1130,35 +1331,44 @@ "view-credentials": "查看凭据", "delete-device-title": "确定要删除设备的{{deviceName}}吗?", "delete-device-text": "请注意:确认后,设备及其所有相关数据将不可恢复。", - "delete-devices-title": "确定要删除{count,plural, =1 {# 个设备} other {# 个设备} } 吗?", - "delete-devices-action-title": "删除 {count,plural, =1 {# 个设备} other {# 个设备} }", + "delete-devices-title": "确定要删除{count,plural,=1 {1 个设备} other {# 个设备} } 吗?", + "delete-devices-action-title": "删除 {count,plural,=1 {1 个设备} other {# 个设备} }", "delete-devices-text": "请注意:确认后,所有选定的设备将被删除,所有相关数据将不可恢复。", "unassign-device-title": "确定要取消分配设备 '{{deviceName}}' 吗?", "unassign-device-text": "确认后,设备将被取消分配,客户将无法访问。", "unassign-device": "取消分配设备", - "unassign-devices-title": "确定要取消分配 {count,plural, =1 {# 个设备} other {# 个设备} } 吗?", + "unassign-devices-title": "确定要取消分配 {count,plural,=1 {1 个设备} other {# 个设备} } 吗?", "unassign-devices-text": "确认后,所有选定的设备将被取消分配,并且客户将无法访问。", "device-credentials": "设备凭据", - "loading-device-credentials": "加载设备凭证...", + "loading-device-credentials": "加载设备凭据...", "credentials-type": "凭据类型", "access-token": "访问令牌", "access-token-required": "访问令牌必填", "access-token-invalid": "访问令牌长度必须为1到32个字符。", - "certificate-pem-format": "PEM证书格式", - "certificate-pem-format-required": "PEM证书格式必填。", + "certificate-pem-format": "PEM 格式的证书", + "certificate-pem-format-required": "需要提供证书。", + "copy-access-token": "复制访问令牌", + "copy-certificate": "复制证书", + "copy-client-id": "复制客户端ID", + "copy-user-name": "复制用户名", + "copy-password": "复制密码", + "generate-client-id": "生成客户端ID", + "generate-user-name": "生成用户名", + "generate-password": "生成密码", + "generate-access-token": "生成访问令牌", "lwm2m-security-config": { - "identity": "客户端标识", - "identity-required": "客户端标识必填。", - "identity-tooltip": "PSK标识符是多达128字节的任意PSK标识符,如标准[RFC7925]所述。", + "identity": "客户端身份", + "identity-required": "需要提供客户端身份。", + "identity-tooltip": "PSK 标识符是一个任意的 PSK 标识符,最多可以为 128 字节,如标准 [RFC7925] 中所述。\nPSK 标识符必须首先转换为字符字符串,然后使用 UTF-8 编码成八位字节。", "client-key": "客户端密钥", - "client-key-required": "客户端密钥必填。", - "client-key-tooltip-prk": "RPK公钥必须满足标准[RFC7250]并且Base64编码!", - "client-key-tooltip-psk": "PSK密钥必须是[RFC4279]标准和HexDec格式: 32, 64, 128字符!", - "endpoint": "客户端名称", - "endpoint-required": "客户端名称必填。", + "client-key-required": "需要提供客户端密钥。", + "client-key-tooltip-prk": "RPK 公钥或 ID 必须符合标准 [RFC7250] 并编码为 Base64 格式!", + "client-key-tooltip-psk": "PSK 密钥必须符合标准 [RFC4279],使用十六进制表示:32、64、128 个字符!", + "endpoint": "终端客户端名称", + "endpoint-required": "需要提供终端客户端名称。", "client-public-key": "客户端公钥", - "client-public-key-hint": "如果客户端公钥为空则使用信任证书", - "client-public-key-tooltip": "X509公钥必须是DER-encoded X509v3格式并支持EC算法编码Base64!", + "client-public-key-hint": "如果客户端公钥为空,则将使用可信证书", + "client-public-key-tooltip": "X509 公钥必须采用 DER 编码的 X509v3 格式,并且仅支持 EC 算法,然后编码为 Base64 格式!", "mode": "安全配置模式", "client-tab": "客户端安全配置", "client-certificate": "客户端证书", @@ -1180,7 +1390,7 @@ "client-id-pattern": "包含无效字符。", "user-name": "用户名", "user-name-required": "用户名必填。", - "client-id-or-user-name-necessary": "客户端ID和/或用户名是必需的", + "client-id-or-user-name-necessary": "客户端ID或用户名是必需的", "password": "密码", "secret": "密钥", "secret-required": "密钥必填", @@ -1191,11 +1401,12 @@ "any-device": "任意设备", "no-device-types-matching": "未找到匹配 '{{entitySubtype}}' 的设备类型。", "device-type-list-empty": "未选择设备类型", + "device-profile-type-list-empty": "至少应选择一个设备配置。", "device-types": "设备类型", "name": "名称", "name-required": "名称必填。", - "name-max-length": "名称长度必须少于256个字符", - "label-max-length": "标签长度必须少于256个字符", + "name-max-length": "名称应该小于256个字符。", + "label-max-length": "标签应该小于256个字符。", "description": "说明", "label": "标签", "events": "事件", @@ -1211,26 +1422,46 @@ "unable-delete-device-alias-text": "设备别名 '{{deviceAlias}}' 不能够被删除,因为它被下列部件所使用:
{{widgetsList}}", "is-gateway": "是否网关", "overwrite-activity-time": "覆盖已连接设备的活动时间", + "device-filter": "设备筛选器", + "device-filter-title": "设备筛选器", + "filter-title": "筛选器", + "device-state": "设备状态", + "state": "状态", + "any": "所有", + "active": "活动", + "inactive": "非活动", "public": "公开", "device-public": "设备公开", "select-device": "选择设备", "import": "导入设备", "device-file": "设备文件", "search": "查找设备", - "selected-devices": "已选择 { count, plural, =1 {# 个设备} other {# 个设备} }", + "selected-devices": "已选择 { count, plural, =1 {1 个设备} other {# 个设备} }", "device-configuration": "设备配置", "transport-configuration": "传输配置", "wizard": { - "device-wizard": "设备向导", - "device-details": "设备详细信息", - "new-device-profile": "新建设备配置", - "existing-device-profile": "选择已有设备配置", - "specific-configuration": "指定配置", - "customer-to-assign-device": "客户分配设备", - "add-credentials": "添加凭据" + "device-details": "设备详细信息" }, "unassign-devices-from-edge-title": "确定要取消分配 { count, plural, =1 {1 个设备} other {# 个设备} } 吗?", - "unassign-devices-from-edge-text": "确认后,设备将被取消分配,边缘将无法访问。" + "unassign-devices-from-edge-text": "确认后,设备将被取消分配,边缘将无法访问。", + "time": "时间", + "connectivity": { + "check-connectivity": "检查连通性", + "device-created-check-connectivity": "设备已创建,检查连通性!", + "loading-check-connectivity-command": "正在加载检查连通性命令...", + "use-following-instructions": "请使用以下说明代表设备发送遥测数据", + "execute-following-command": "执行以下命令", + "install-curl-windows": "从 Windows 10 b17063 开始,cURL 已默认安装", + "install-curl-macos": "从 Mac OS X 10.2 6C115(Jaguar)开始,cURL 已默认安装", + "install-mqtt-windows": "使用说明下载、安装、设置和运行 mosquitto_pub", + "install-coap-client": "使用说明下载、安装、设置和运行 coap-client", + "install-necessary-client-tools": "安装必要的客户端工具", + "mqtts-x509-command": "使用以下文档通过 MQTT 连接带有 X509 授权的设备", + "coaps-x509-command": "使用以下文档通过基于 DTLS 的 CoAP 连接带有 X509 授权的设备", + "snmp-command": "使用以下文档通过 SNMP 连接设备。", + "sparkplug-command": "使用以下文档通过 MQTT Sparkplug 连接设备。", + "lwm2m-command": "使用以下文档通过 LWM2M 连接设备。" + } }, "asset-profile": { "asset-profile": "资产配置", @@ -1257,6 +1488,8 @@ "description": "说明", "default": "默认", "default-rule-chain": "默认规则链", + "default-edge-rule-chain": "默认边缘规则链", + "default-edge-rule-chain-hint": "作为规则链,用于在边缘上处理属于此资产配置中资产的传入数据。", "mobile-dashboard": "移动端仪表板", "mobile-dashboard-hint": "被移动端应用用作资产详情仪表板", "select-queue-hint": "从下拉列表中选择。", @@ -1267,12 +1500,12 @@ "set-default-asset-profile-title": "确定要将 '{{assetProfileName}}' 设为默认资产配置吗?", "set-default-asset-profile-text": "确认后,资产配置将被标记为默认,并将用于未指定配置的新资产。", "no-asset-profiles-found": "未不到资产配置。", - "create-new-asset-profile": "创建一个新的!", + "create-new-asset-profile": "创建资产配置", "create-asset-profile": "创建资产配置", "import": "导入资产配置", "export": "导出资产配置", "export-failed-error": "无法导出资产配置: {{error}}", - "asset-profile-file": "资产配置文件", + "asset-profile-file": "资产配置", "invalid-asset-profile-file-error": "无法导入资产配置:无效的资产配置数据结构。" }, "device-profile": { @@ -1284,7 +1517,7 @@ "device-profile-details": "设备配置详情", "no-device-profiles-text": "未找到设备配置", "search": "查找设备配置", - "selected-device-profiles": "已选择 { count, plural, =1 {# 个设备配置} other {# 个设备配置} }", + "selected-device-profiles": "已选择 { count, plural, =1 {1 个设备配置} other {# 个设备配置} }", "no-device-profiles-matching": "未找到与 '{{entity}}' 匹配的设备配置。", "device-profile-required": "设备配置必填", "idCopiedMessage": "设备配置 ID 已复制到剪贴板", @@ -1292,8 +1525,6 @@ "delete": "删除设备配置", "copyId": "复制设备配置 ID", "name-max-length": "名称长度必须少于256个字符", - "new-device-profile-name": "设备配置名称", - "new-device-profile-name-required": "设备配置名称必填。", "name": "名称", "name-required": "名称是必需的。", "type": "配置类型", @@ -1312,24 +1543,31 @@ "transport-type-lwm2m-hint": "LWM2M传输类型", "transport-type-snmp": "SNMP", "transport-type-snmp-hint": "指定 SNMP 传输配置", + "transport-type-http": "HTTP", "description": "说明", "default": "默认", "profile-configuration": "配置", "transport-configuration": "传输配置", "default-rule-chain": "默认规则链", + "default-edge-rule-chain": "默认边缘规则链", + "default-edge-rule-chain-hint": "作为规则链,用于在边缘上处理属于此设备配置中设备的传入数据。", "mobile-dashboard": "移动端仪表板", "mobile-dashboard-hint": "被移动端应用用作设备详情仪表板", "select-queue-hint": "从下拉列表中选择或添加自定义名称。", "delete-device-profile-title": "确定要删除 '{{deviceProfileName}}' 设备配置吗?", "delete-device-profile-text": "请注意:确认后,设备配置和所有相关数据将不可恢复。", - "delete-device-profiles-title": "确定要删除 { count, plural, =1 {# 个设备配置} other {# 个设备配置} }吗?", + "delete-device-profiles-title": "确定要删除 { count, plural, =1 {1 个设备配置} other {# 个设备配置} }吗?", "delete-device-profiles-text": "请注意:确认后,所有选定的设备配置将被删除,所有相关数据将不可恢复。", "set-default-device-profile-title": "确定要将设备配置 '{{deviceProfileName}}' 设为默认值吗?", "set-default-device-profile-text": "确认后,设备配置将被标记为默认,并将用于未指定配置的新设备。", "no-device-profiles-found": "未找到设备配置。", - "create-new-device-profile": "创建一个新的!", + "create-new-device-profile": "创建设备配置", "mqtt-device-topic-filters": "MQTT 设备 Topic 筛选器", "mqtt-device-topic-filters-unique": "MQTT设备 Topic 筛选器必须唯一。", + "mqtt-device-topic-filters-spark-plug": "MQTT Sparkplug B边缘网络(EoN)节点", + "mqtt-device-topic-filters-spark-plug-hint": "允许来自具备Sparkplug B负载和Topic格式的边缘网络(EoN)节点的连接。", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names": "将SparkPlug指标存储为属性。", + "mqtt-device-topic-filters-spark-plug-attribute-metric-names-hint": "将作为设备属性存储的SparkPlug指标的名称。其他指标将作为设备遥测数据进行存储。", "mqtt-device-payload-type": "MQTT 设备 Payload", "mqtt-device-payload-type-json": "JSON", "mqtt-device-payload-type-proto": "Protobuf", @@ -1340,9 +1578,9 @@ "mqtt-send-ack-on-validation-exception": "发布消息验证失败时发送PUBACK", "mqtt-send-ack-on-validation-exception-hint": "默认情况下平台将关闭相关消息验证失败的MQTT会话,启用后平台将发布确认而不是关闭会话。", "snmp-add-mapping": "添加SNMP映射", - "snmp-mapping-not-configured": "没有配置时间序列/属性的OID映射", - "snmp-timseries-or-attribute-name": "映射时间序列/属性名称", - "snmp-timseries-or-attribute-type": "映射时间序列/属性名称", + "snmp-mapping-not-configured": "OID到Timeseries/遥测的映射未配置", + "snmp-timseries-or-attribute-name": "用于映射的Timeseries/属性名称", + "snmp-timseries-or-attribute-type": "用于映射的Timeseries/属性类型", "snmp-method-pdu-type-get-request": "GetRequest", "snmp-method-pdu-type-get-next-request": "GetNextRequest", "snmp-oid": "OID", @@ -1355,14 +1593,16 @@ "coap-device-type-default": "默认", "coap-device-type-efento": "Efento NB-IoT", "support-level-wildcards": "支持单[+]和多级[#]通配符。", - "telemetry-topic-filter": "遥测数据 topic 筛选器", - "telemetry-topic-filter-required": "遥测数据 topic 筛选器必填。", - "attributes-topic-filter": "Attributes topic filter", - "attributes-topic-filter-required": "Attributes topic 筛选器必填。", + "telemetry-topic-filter": "遥测数据Topic筛选器", + "telemetry-topic-filter-required": "遥测数据Topic筛选器必填。", + "attributes-topic-filter": "属性Topic筛选器", + "attributes-subscribe-topic-filter": "订阅属性的Topic筛选器", + "attributes-topic-filter-required": "属性的Topic筛选器必填。", + "attributes-subscribe-topic-filter-required": "订阅属性的Topic筛选器必填。", "telemetry-proto-schema": "遥测数据 proto schema", "telemetry-proto-schema-required": "遥测数据 proto schema 必填。", - "attributes-proto-schema": "Attributes proto schema", - "attributes-proto-schema-required": "Attributes proto schema 必填。", + "attributes-proto-schema": "属性 proto schema", + "attributes-proto-schema-required": "属性 proto schema 必填。", "rpc-response-proto-schema": "RPC 响应 proto schema", "rpc-response-proto-schema-required": "RPC 响应 proto schema 必填。", "rpc-response-topic-filter": "RPC响应 Topic 筛选器", @@ -1373,8 +1613,8 @@ "not-valid-pattern-topic-filter": "无效的 Topic 筛选器模式", "not-valid-single-character": "单级通配符的使用无效", "not-valid-multi-character": "多级通配符的使用无效", - "single-level-wildcards-hint": "[+] is suitable for any topic filter level。例如:v1/devices/+/telemetry or +/devices/+/attributes。", - "multi-level-wildcards-hint": "[#]可以替换 topic filter 本身,并且必须是 topic 的最后一个符号。例如:# or v1/devices/me/#。", + "single-level-wildcards-hint": "[+] 适用于任何 Topic 过滤级别。例如:v1/devices/+/telemetry or +/devices/+/attributes。", + "multi-level-wildcards-hint": "[#]可以替换 Topic 筛选器本身,并且必须是 Topic 的最后一个符号。例如:# or v1/devices/me/#。", "alarm-rules": "告警规则", "alarm-rules-with-count": "告警规则 ({{count}})", "no-alarm-rules": "未配置告警规则", @@ -1383,7 +1623,7 @@ "alarm-type": "告警类型", "alarm-type-required": "告警类型必填。", "alarm-type-unique": "警报类型在设备配置警报规则中必须唯一。", - "alarm-type-max-length": "Alarm type should be less than 256", + "alarm-type-max-length": "告警类型应该小于256字符。", "create-alarm-pattern": "创建 {{alarmType}} 告警", "create-alarm-rules": "创建告警规则", "no-create-alarm-rules": "未配置创建条件", @@ -1402,18 +1642,18 @@ "condition-duration-value-required": "持续时间值必填。", "condition-duration-time-unit-required": "时间单位必填。", "advanced-settings": "高级设置", - "alarm-rule-details": "详情", - "alarm-rule-details-hint": "提示:使用${keyName}警报条件中属性值或报文关键字。", - "add-alarm-rule-details": "详情模板:", - "alarm-rule-mobile-dashboard": "移动仪表盘", - "alarm-rule-mobile-dashboard-hint": "用于警报详细报告", - "alarm-rule-no-mobile-dashboard": "未选择仪表板", + "alarm-rule-additional-info": "附加信息", + "edit-alarm-rule-additional-info": "编辑附加信息", + "alarm-rule-additional-info-placeholder": "请在此处提供评论和调整,以便在附加信息下的告警详情中显示", + "alarm-rule-additional-info-hint": "提示: 使用 ${keyName} 来替代告警规则条件中使用的属性或遥测键的值。", + "alarm-rule-mobile-dashboard": "移动端仪表板", + "alarm-rule-mobile-dashboard-hint": "作为移动端告警详情仪表板使用。", + "alarm-rule-no-mobile-dashboard": "未选择仪表板。", "propagate-alarm": "传递警报", "alarm-rule-relation-types-list": "要传递的关联类型", "alarm-rule-relation-types-list-hint": "如果未选择传递关联类型,则将不按关联类型过滤而传递告警。", - "propagate-alarm-to-owner": "向实体所有者(客户或租户)传播警报", - "propagate-alarm-to-tenant": "向租户传播警报", - "alarm-details": "告警详细信息", + "propagate-alarm-to-owner": "将告警传播给实体所有者(客户或租户)。", + "propagate-alarm-to-tenant": "将告警传播给租户", "alarm-rule-condition": "告警规则条件", "enter-alarm-rule-condition-prompt": "请添加告警规则条件", "edit-alarm-rule-condition": "编辑告警规则条件", @@ -1421,7 +1661,7 @@ "provision-strategy": "预配置策略", "provision-strategy-required": "预配置策略必填。", "provision-strategy-disabled": "禁用", - "provision-strategy-created-new": "允许创建新设备", + "provision-strategy-created-new": "允许创建设备", "provision-strategy-check-pre-provisioned": "检查预配置的设备", "provision-device-key": "预配置设备密钥名", "provision-device-key-required": "预配置设备密钥名必填。", @@ -1431,20 +1671,31 @@ "provision-device-secret-required": "预配置设备密钥必填。", "copy-provision-secret": "复制预配置密钥", "provision-secret-copied-message": "预配置密钥已复制到剪贴板", + "provision-strategy-x509": { + "certificate-chain": "X509 证书链", + "certificate-chain-hint": "X.509 证书策略用于通过客户端证书在双向TLS通信中提供设备的方式。", + "allow-create-new-devices": "创建设备", + "allow-create-new-devices-hint": "如果选择创建设备,则将客户端证书用作设备凭据。", + "certificate-value": "PEM 格式的证书", + "certificate-value-required": "PEM 格式的证书必填。", + "cn-regex-variable": "CN 正则表达式变量", + "cn-regex-variable-required": "CN 正则表达式变量必填。", + "cn-regex-variable-hint": "需要从设备 X509 证书的通用名称中获取设备名称。" + }, "condition": "条件", "condition-type": "条件类型", "condition-type-simple": "简单", "condition-type-duration": "持续时间", "condition-during": "在 {{during}} 期间", - "condition-during-dynamic": "在\"{{ attribute }}\" ({{during}})", + "condition-during-dynamic": "\"{{ attribute }}\" ({{during}}) 期间", "condition-type-repeating": "重复", "condition-type-required": "条件类型必填。", "condition-repeating-value": "事件计数", "condition-repeating-value-range": "事件计数应在1到2147483647之间。", "condition-repeating-value-pattern": "事件计数应为整数。", "condition-repeating-value-required": "事件计数值必填。", - "condition-repeat-times": "重复 { count, plural, =1 {# 次} other {# 次} }", - "condition-repeat-times-dynamic": "重复\"{ attribute }\" ({ count, plural, =1 {1 时间} other {# 时间} })", + "condition-repeat-times": "重复 { count, plural, =1 {1 次} other {# 次} }", + "condition-repeat-times-dynamic": "重复 \"{ attribute }\" ({ count, plural, =1 {1 次} other {# 次} })", "schedule-type": "计划程序类型", "schedule-type-required": "计划类型必填。", "schedule": "启用规则:", @@ -1469,8 +1720,8 @@ "create-device-profile": "创建设备配置", "import": "导入设备配置", "export": "导出设备配置", - "export-failed-error": "无法导出设备配置文件: {{error}}", - "device-profile-file": "设备配置文件", + "export-failed-error": "无法导出设备配置: {{error}}", + "device-profile-file": "设备配置", "invalid-device-profile-file-error": "无法导入设备配置:无效的设备配置数据结构。", "power-saving-mode": "节能模式", "power-saving-mode-type": { @@ -1503,7 +1754,7 @@ "instance-id-pattern": "实例id必须是一个正整数。", "instance-id-max": "实例id最大值是{{max}}", "instance": "实例", - "resource-label": "#I资源D名称", + "resource-label": "#ID资源名称", "observe-label": "观察者", "attribute-label": "属性", "telemetry-label": "遥测", @@ -1523,8 +1774,8 @@ "edit-attribute": "编辑属性", "view-attribute": "查看属性", "remove-attribute": "移除属性", - "delete-server-text": "请注意确认删除后服务器配置将无法恢复。", - "delete-server-title": "你确定要删除服务器吗?", + "delete-server-text": "请注意,确认删除后,服务器配置将无法恢复。", + "delete-server-title": "确定要删除服务器吗?", "mode": "安全配置模式", "bootstrap-tab": "Bootstrap", "bootstrap-server-legend": "Bootstrap Server (ShortId...)", @@ -1579,7 +1830,7 @@ "account-after-timeout-required": "帐户超时必填。", "account-after-timeout-pattern": "帐户超时必须是一个正整数。", "account-after-timeout-tooltip": "Bootstrap-Server帐户资源的超时值。", - "server-type": "Server type", + "server-type": "服务器类型", "add-new-server-title": "添加新的服务器配置", "add-server-config": "添加服务器配置", "add-lwm2m-server-config": "添加LwM2M服务器", @@ -1604,15 +1855,15 @@ "sw-update-resource-required": "软件更新COAP资源必填。", "config-json-tab": "设备配置JSON", "attributes-name": { - "min-period": "最小期限", - "max-period": "最大期限", + "min-period": "最小周期", + "max-period": "最大周期", "greater-than": "大于", - "less-than": "少于", - "step": "停止", - "min-evaluation-period": "最小期限评估", - "max-evaluation-period": "最大期限评估" + "less-than": "小于", + "step": "步长", + "min-evaluation-period": "最小评估周期", + "max-evaluation-period": "最大评估周期" }, - "composite-operations-support": "支持Read/Write/Observe操作" + "composite-operations-support": "支持复合读取/写入/观察操作" }, "snmp": { "add-communication-config": "添加通信配置", @@ -1664,7 +1915,9 @@ } }, "dialog": { - "close": "关闭对话框" + "close": "关闭对话框", + "error-message-title": "错误信息:", + "error-details-title": "错误详细信息" }, "direction": { "column": "列", @@ -1673,6 +1926,7 @@ "edge": { "edge": "边缘", "edge-instances": "边缘实例", + "instances": "边缘实例", "edge-file": "边缘文件", "name-max-length": "名称长度必须少于256个字符", "label-max-length": "标签长度必须少于256个字符", @@ -1723,6 +1977,9 @@ "make-private-edge-title": "确定要将边缘 '{{edgeName}}' 设为私有吗?", "make-private-edge-text": "确认后,边缘及其所有数据将被设为私有,不被其他人访问。", "import": "导入边缘", + "install-connect-instructions": "安装和连接说明", + "install-connect-instructions-edge-created": "边缘已创建!请检查安装和连接说明", + "loading-edge-instructions": "正在加载边缘说明...", "label": "标签", "load-entity-error": "加载数据失败,实体已经被删除。", "assign-new-edge": "分配新边缘", @@ -1733,17 +1990,18 @@ "edge-secret": "边缘密钥", "copy-edge-secret": "复制边缘密钥", "edge-secret-copied-message": "边缘密钥已经被复制到剪切板", - "edge-assets": "边缘资产", - "edge-devices": "边缘设备", - "edge-entity-views": "边缘实体视图", - "edge-dashboards": "边缘仪表板", - "edge-rulechains": "边缘规则链", + "manage-assets": "管理资产", + "manage-devices": "管理设备", + "manage-entity-views": "管理实体视图", + "manage-dashboards": "管理仪表板", + "manage-rulechains": "管理规则链", "assets": "边缘资产", "devices": "边缘设备", "entity-views": "边缘实体视图", "dashboard": "边缘仪表板", "dashboards": "边缘仪表板", "rulechain-templates": "规则链模版", + "edge-rulechain-templates": "边缘规则链模板", "rulechains": "规则链", "search": "搜索边缘", "selected-edges": "{ count, plural, =1 {1 个边缘} other {# 个边缘} } 被选中", @@ -1766,18 +2024,22 @@ "type-asset": "资产", "type-device": "设备", "type-device-profile": "设备概要", - "type-asset-profile": "Asset Profile", + "type-asset-profile": "资产配置", "type-entity-view": "实体视图", "type-alarm": "告警", "type-rule-chain": "规则链", "type-rule-chain-metadata": "规则链元数据", "type-edge": "边缘", "type-user": "用户", + "type-tenant": "租户", + "type-tenant-profile": "租户配置", "type-customer": "客户", "type-relation": "关联", "type-widgets-bundle": "部件包", "type-widgets-type": "部件类型", "type-admin-settings": "管理员设置", + "type-ota-package": "OTA包", + "type-queue": "队列", "action-type-added": "增加", "action-type-deleted": "删除", "action-type-updated": "更新", @@ -1793,6 +2055,8 @@ "action-type-rpc-call": "RPC调用", "action-type-alarm-ack": "告警确认", "action-type-alarm-clear": "告警清除", + "action-type-alarm-assigned": "告警已分配", + "action-type-alarm-unassigned": "告警未分配", "action-type-assigned-to-edge": "分配给边缘", "action-type-unassigned-from-edge": "取消分配边缘", "action-type-credentials-request": "认证请求", @@ -1807,7 +2071,9 @@ "entity": "实体", "entities": "实体", "entities-count": "实体数量", + "alarms-count": "告警数量", "aliases": "实体别名", + "aliases-short": "别名", "entity-alias": "实体别名", "unable-delete-entity-alias-title": "无法删除实体别名", "unable-delete-entity-alias-text": "实体别名 '{{entityAlias}}' 被以下部件使用不能删除:
{{widgetsList}}", @@ -1823,6 +2089,7 @@ "entity-types": "实体类型", "entity-type-list": "实体类型列表", "any-entity": "任意实体", + "add-entity-type": "添加实体类型", "enter-entity-type": "输入实体类型", "no-entities-matching": "未找到匹配 '{{entity}}' 的实体。", "no-entity-types-matching": "未找到匹配 '{{entityType}}' 类型的实体。", @@ -1830,19 +2097,20 @@ "help-text": "根据需要可以使用'%'进行匹配,例如:'%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'。", "use-entity-name-filter": "用户筛选器", "entity-list-empty": "没有选择实体。", - "entity-type-list-empty": "没有选择实体类型。", + "entity-type-list-required": "至少应选择一个实体类型。", "entity-name-filter-required": "实体名筛选器必填。", "entity-name-filter-no-entity-matched": "未找到以 '{{entity}}' 开头的实体", "all-subtypes": "全部", "select-entities": "选择实体", "no-aliases-found": "未找到别名", "no-alias-matching": "未找到 '{{alias}}'", - "create-new-alias": "创建一个新的!", + "create-new-alias": "创建别名", + "create-new": "创建", "key": "键名", "key-name": "键名", "no-keys-found": "未找到键名", "no-key-matching": "未找到键名 '{{key}}'", - "create-new-key": "创建一个新的!", + "create-new-key": "创建键", "type": "类型", "type-required": "实体类型必填。", "type-device": "设备", @@ -1851,12 +2119,13 @@ "device-name-starts-with": "以 '{{prefix}}' 开头的设备", "type-device-profile": "设备配置", "type-device-profiles": "设备配置", + "clear-selected-profiles": "清除已选择的配置", "list-of-device-profiles": "{ count, plural, =1 {1 个设备配置} other {# 个设备配置} }", "device-profile-name-starts-with": "名称以 '{{prefix}}' 开头的设备配置", "type-asset-profile": "资产配置", "type-asset-profiles": "资产配置", - "list-of-asset-profiles": "{ count, plural, =1 {One asset profile} other {List of # asset profiles} }", - "asset-profile-name-starts-with": "Asset profiles whose names start with '{{prefix}}'", + "list-of-asset-profiles": "{ count, plural, =1 {1 个资产配置} other {# 个资产配置} }", + "asset-profile-name-starts-with": "名称以 '{{prefix}}' 开头的资产配置", "type-asset": "资产", "type-assets": "资产", "list-of-assets": "{ count, plural, =1 {1 个资产} other {# 个资产} }", @@ -1909,11 +2178,14 @@ "type-current-tenant": "当前租户", "type-current-user": "当前用户", "type-current-user-owner": "当前用户所有者", - "type-widgets-bundle": "Widgets bundle", - "type-widgets-bundles": "Widgets bundles", - "list-of-widgets-bundles": "{ count, plural, =1 {1 个部件包} other {列表 # 部件包} }", + "type-widgets-bundle": "部件包", + "type-widgets-bundles": "部件包", + "list-of-widgets-bundles": "{ count, plural, =1 {1 个部件包} other {# 个部件包} }", + "type-widget": "部件", + "type-widgets": "部件", + "list-of-widgets": "{ count, plural, =1 {1 个部件} other {# 个部件} }", "search": "实体检索", - "selected-entities": "已选择 { count, plural, =1 {# 个实体} other {# 个实体} }", + "selected-entities": "已选择 { count, plural, =1 {1 个实体} other {# 个实体} }", "entity-name": "实体名", "entity-label": "实体标签", "details": "实体详情", @@ -1926,7 +2198,20 @@ "list-of-edges": "{ count, plural, =1 {1 个边缘} other {列表 # 个边缘} }", "edge-name-starts-with": "以 '{{prefix}}' 开头的边缘", "type-tb-resource": "资源", - "type-ota-package": "OTA包" + "type-ota-package": "OTA包", + "type-rpc": "RPC", + "type-queue": "队列", + "type-notification": "通知", + "type-notification-rule": "通知规则", + "type-notification-rules": "通知规则", + "list-of-notification-rules": "{ count, plural, =1 {1 个通知规则} other {# 个通知规则} }", + "type-notification-target": "通知收件人", + "type-notification-targets": "通知接收者", + "list-of-notification-targets": "{ count, plural, =1 {1 个通知接收者} other {# 个通知接收者} }", + "type-notification-request": "通知请求", + "type-notification-template": "通知模板", + "type-notification-templates": "通知模板", + "list-of-notification-templates": "{ count, plural, =1 {1 个通知模板} other {# 个通知模板} }" }, "entity-field": { "created-time": "创建时间", @@ -1957,8 +2242,8 @@ "no-aliases-found": "未找到别名。", "no-key-matching": "'{{key}}' 未找到。", "no-keys-found": "未找到密钥。", - "create-new-alias": "创建一个新的!", - "create-new-key": "创建一个新的!", + "create-new-alias": "创建别名", + "create-new-key": "创建键", "duplicate-alias-error": "找到重复别名 '{{alias}}'。
实体视图别名必须是唯一的。", "configure-alias": "配置 '{{alias}}' 别名", "no-entity-views-matching": "未找到与 '{{entity}}' 匹配的实体视图。", @@ -1983,24 +2268,24 @@ "no-entity-views-text": "未找到实体视图", "assign-to-customer-text": "请选择客户分配实体视图", "entity-view-details": "实体视图详细信息", - "add-entity-view-text": "添加新实体视图", + "add-entity-view-text": "添加实体视图", "delete": "删除实体视图", "assign-entity-views": "分配实体视图", - "assign-entity-views-text": "分配 { count, plural, =1 {# 个实体视图} other {# 个实体视图} } 给客户", + "assign-entity-views-text": "分配 { count, plural, =1 {1 个实体视图} other {# 个实体视图} } 给客户", "delete-entity-views": "删除实体视图", "unassign-from-customer": "取消分配客户", "unassign-entity-views": "取消分配实体视图", - "unassign-entity-views-action-title": "从客户处取消分配{count,plural, =1 {# 实体视图} other {# 实体视图} }", + "unassign-entity-views-action-title": "从客户处取消分配{count,plural,=1 {1 实体视图} other {# 实体视图} }", "assign-new-entity-view": "分配新实体视图", "delete-entity-view-title": "确定要删除实体视图 '{{entityViewName}}'吗?", "delete-entity-view-text": "请注意:确认后实体视图及其所有相关数据将不可恢复。", - "delete-entity-views-title": "确定要删除 { count, plural, =1 {# 实体视图} other {# 实体视图} }吗?", - "delete-entity-views-action-title": "删除 { count, plural, =1 {# 个实体视图} other {# 个实体视图} }", + "delete-entity-views-title": "确定要删除 { count, plural, =1 {1 实体视图} other {# 实体视图} }吗?", + "delete-entity-views-action-title": "删除 { count, plural, =1 {1 个实体视图} other {# 个实体视图} }", "delete-entity-views-text": "请注意:确认后,所有选定的实体视图将被删除,所有相关的数据将不可恢复。", "unassign-entity-view-title": "确定要取消对 '{{entityViewName}}' 实体视图的分配吗?", "unassign-entity-view-text": "确认后,实体视图将未分配,客户无法访问。", "unassign-entity-view": "未分配实体视图", - "unassign-entity-views-title": "确定要取消分配 { count, plural, =1 {# 个实体视图} other {# 个实体视图} } 吗?", + "unassign-entity-views-title": "确定要取消分配 { count, plural, =1 {1 个实体视图} other {# 个实体视图} } 吗?", "unassign-entity-views-text": "确认后,所有选定的实体视图将被分配,客户无法访问。", "entity-view-type": "实体视图类型", "entity-view-type-required": "实体视图类型必填。", @@ -2013,8 +2298,8 @@ "created-time": "创建时间", "name": "名称", "name-required": "名称必填。", - "name-max-length": "名称长度必须少于256个字符", - "type-max-length": "视图类型长度必须少于256个字符", + "name-max-length": "名称应该少于256个字符。", + "type-max-length": "实体视图类型应该少于256个字符。", "description": "说明", "events": "事件", "details": "详情", @@ -2041,11 +2326,11 @@ "timeseries-placeholder": "时间序列", "target-entity": "目标实体", "attributes-propagation": "属性传播", - "attributes-propagation-hint": "每次保存或更新这个实体视图时将自动从目标实体复制指定的属性。由于性能原因目标实体属性不会在每次属性更改时传递到实体视图。你可以通过配置\"copy to view\"规则链中的规则节点,并将\"Post attributes\"和\"attributes Updated\"消息链接到新规则节点,从而启用自动传递。", + "attributes-propagation-hint": "每次保存或更新这个实体视图时,实体视图将自动从目标实体复制指定的属性。由于性能原因,目标实体属性不会在每次属性更改时传递到实体视图。您可以通过配置\"copy to view\"规则链中的规则节点,并将\"Post attributes\"和\"attributes Updated\"消息链接到新规则节点,从而启用自动传递。", "timeseries-data": "时间序列数据", "timeseries-data-hint": "配置目标实体的 Timeseries 数据键,以便实体视图可以访问这些键。此 Timeseries 数据是只读的。", "search": "查找实体视图", - "selected-entity-views": "已选择 { count, plural, =1 {# 个实体视图} other {# 个实体视图} }", + "selected-entity-views": "已选择 { count, plural, =1 {1 个实体视图} other {# 个实体视图} }", "make-public-entity-view-title": "确定要将实体视图 '{{entityViewName}}' 设为公开吗?", "make-public-entity-view-text": "确认后,实体视图及其所有数据将被公开并被他人访问。", "make-private-entity-view-title": "确定要将实体视图 '{{entityViewName}}' 设为私有吗?", @@ -2054,15 +2339,15 @@ "assign-entity-view-to-edge-text": "请选择要分配给边缘的实体视图", "unassign-entity-view-from-edge-title": "确定要取消对 '{{entityViewName}}' 实体视图的分配吗?", "unassign-entity-view-from-edge-text": "确认后,实体视图将未分配,边缘无法访问。", - "unassign-entity-views-from-edge-action-title": "从边缘处取消分配{count,plural, =1 {# 实体视图} other {# 实体视图} }", + "unassign-entity-views-from-edge-action-title": "从边缘处取消分配{count,plural,=1 {1 实体视图} other {# 实体视图} }", "unassign-entity-view-from-edge": "未分配实体视图", - "unassign-entity-views-from-edge-title": "确定要取消分配 { count, plural, =1 {# 个实体视图} other {# 个实体视图} } 吗?", + "unassign-entity-views-from-edge-title": "确定要取消分配 { count, plural, =1 {1 个实体视图} other {# 个实体视图} } 吗?", "unassign-entity-views-from-edge-text": "确认后,所有选定的实体视图将被分配,边缘无法访问。" }, "event": { "event-type": "事件类型", "events-filter": "事件筛选器", - "clean-events": "Clear Events", + "clean-events": "清除事件", "type-error": "错误", "type-lc-event": "生命周期事件", "type-stats": "类型统计", @@ -2076,9 +2361,9 @@ "body": "整体", "method": "方法", "type": "类型", - "message": "Message", + "message": "消息", "message-id": "消息ID", - "copy-message-id": "Copy message Id", + "copy-message-id": "复制消息ID", "message-type": "消息类型", "data-type": "数据类型", "relation-type": "关联类型", @@ -2089,24 +2374,27 @@ "success": "成功", "failed": "失败", "messages-processed": "消息处理", - "max-messages-processed": "最多处理消息", - "min-messages-processed": "最少处理消息", + "max-messages-processed": "最大处理消息数", + "min-messages-processed": "最少处理消息数", "errors-occurred": "错误发生", - "max-errors-occurred": "最多错误", - "min-errors-occurred": "最少错误", - "min-value": "最小值是0。", + "max-errors-occurred": "最大发生错误数", + "min-errors-occurred": "最少发生错误数", + "min-value": "最小值为 0。", "all-events": "全部", "has-error": "有错误", "entity-id": "实体ID", "copy-entity-id": "复制实体ID", "entity-type": "实体类型", - "clear-filter": "清除过滤器", + "clear-filter": "清除筛选器", "clear-request-title": "清除所有事件", - "clear-request-text": "确除清空所有事件?" + "clear-request-text": "你确定要清除所有事件吗?", + "started": "已开始", + "updated": "已更新", + "stopped": "已停止" }, "extension": { "extensions": "扩展", - "selected-extensions": "已选择 { count, plural, =1 {# 个扩展} other {# 个扩展} }", + "selected-extensions": "已选择 { count, plural, =1 {1 个扩展} other {# 个扩展} }", "type": "类型", "key": "键名", "value": "价值", @@ -2120,7 +2408,7 @@ "edit": "编辑扩展", "delete-extension-title": "确定要删除扩展 '{{extensionId}}'吗?", "delete-extension-text": "请注意:确认后,扩展和所有相关数据将不可恢复。", - "delete-extensions-title": "确定要删除 { count, plural, =1 {# 个扩展} other {# 个扩展} }吗?", + "delete-extensions-title": "确定要删除 { count, plural, =1 {1 个扩展} other {# 个扩展} }吗?", "delete-extensions-text": "请注意:确认后,所有选定的扩展将被删除,所有相关数据将不可恢复。", "converters": "转换器", "converter-id": "转换器序号", @@ -2155,9 +2443,9 @@ "anonymous": "匿名", "basic": "Basic", "pem": "PEM", - "ca-cert": "CA certificate file *", - "private-key": "Private key file *", - "cert": "Certificate file *", + "ca-cert": "CA证书文件 *", + "private-key": "私钥文件 *", + "cert": "证书文件 *", "no-file": "没有选择文件。", "drop-file": "删除文件或单击以选择要上载的文件。", "mapping": "映射", @@ -2210,7 +2498,7 @@ "opc-keystore": "密钥库", "opc-type": "类型", "opc-keystore-type": "类型", - "opc-keystore-location": "Location *", + "opc-keystore-location": "位置 *", "opc-keystore-password": "密码", "opc-keystore-alias": "别名", "opc-keystore-key-password": "密钥密码", @@ -2261,6 +2549,9 @@ "file": "扩展文件", "invalid-file-error": "无效的扩展文件" }, + "feature": { + "advanced-features": "高级功能" + }, "filter": { "add": "添加筛选器", "edit": "编辑筛选器", @@ -2269,7 +2560,7 @@ "duplicate-filter": "同名筛选器已存在。", "filters": "筛选器", "unable-delete-filter-title": "无法删除筛选器", - "unable-delete-filter-text": "无法删除筛选器 '{{filter}}' ,因为它由以下小部件使用:
{{widgetsList}}", + "unable-delete-filter-text": "无法删除筛选器 '{{filter}}' ,因为它由以下部件使用:
{{widgetsList}}", "duplicate-filter-error": "找到重复的筛选器 '{{filter}}'。
筛选器在仪表板中必须是唯一的。", "missing-key-filters-error": "筛选器 '{{filter}}' 的键名筛选条件缺失。", "filter": "筛选器", @@ -2279,6 +2570,7 @@ "add-filter-prompt": "请添加筛选器", "no-filter-matching": "未找到 '{{filter}}' 。", "create-new-filter": "请新增!", + "create-new": "创建", "filter-required": "筛选器必填。", "operation": { "operation": "操作", @@ -2294,12 +2586,13 @@ "less-or-equal": "小于或等于", "and": "和", "or": "或", - "in": "匹配", - "not-in": "不匹配" + "in": "存在", + "not-in": "不存在" }, "ignore-case": "忽略大小写", "value": "值", "remove-filter": "删除筛选器", + "duplicate-filter-action": "重复过滤器", "preview": "筛选器预览", "no-filters": "未配置筛选器", "add-filter": "添加筛选器", @@ -2322,7 +2615,10 @@ "attribute": "属性", "timeseries": "Timeseries", "entity-field": "实体", - "constant": "常量" + "constant": "常量", + "client-attribute": "客户端属性", + "server-attribute": "服务端属性", + "shared-attribute": "共享属性" }, "value-type": { "value-type": "值类型", @@ -2365,38 +2661,152 @@ }, "gateway": { "add-entry": "添加配置", - "connector-add": "添加新连接器", + "advanced": "高级", + "checking-device-activity": "检查设备活动", + "command": "Docker命令", + "command-copied-message": "Docker命令已复制到剪贴板", + "configuration": "配置", + "connector-add": "添加连接器", "connector-enabled": "启用连接器", "connector-name": "连接器名称", - "connector-name-required": "连接器名称名称必填。", + "connector-name-required": "连接器名称必填。", "connector-type": "连接器类型", - "connector-type-required": "连接器名称类型必填。", + "connector-type-required": "连接器类型必填。", "connectors": "连接器配置", - "create-new-gateway": "创建新网关", + "connectors-config": "连接器配置", + "connectors-table-enabled": "已启用", + "connectors-table-name": "名称", + "connectors-table-type": "类型", + "connectors-table-status": "状态", + "connectors-table-actions": "操作", + "connectors-table-key": "键", + "connectors-table-class": "类", + "rpc-command-send": "发送", + "rpc-command-result": "结果", + "rpc-command-edit-params": "编辑参数", + "gateway-configuration": "通用配置", + "create-new-gateway": "创建网关", "create-new-gateway-text": "确定要创建名为 '{{gatewayName}}' 的新网关?", + "created-time": "创建时间", + "configuration-delete-dialog-header": "配置将被删除", + "configuration-delete-dialog-body": "只有对网关进行物理访问时,才有可能关闭远程配置。所有先前的配置都将被删除。

\n要关闭配置,请在下面输入网关名称", + "configuration-delete-dialog-input": "网关名称", + "configuration-delete-dialog-input-required": "网关名称是必需的", + "configuration-delete-dialog-confirm": "关闭", "delete": "删除配置", "download-tip": "下载配置", + "drop-file": "将文件拖放到此处或", "gateway": "网关", "gateway-exists": "同名设备已存在。", "gateway-name": "网关名称", "gateway-name-required": "网关名称必填。", "gateway-saved": "已成功保存网关配置。", + "grpc": "GRPC", + "grpc-keep-alive-timeout": "保持连接超时(毫秒)", + "grpc-keep-alive-timeout-required": "需要保持连接超时", + "grpc-keep-alive-timeout-min": "保持连接超时不能小于1", + "grpc-keep-alive-timeout-pattern": "保持连接超时无效", + "grpc-keep-alive": "保持连接(毫秒)", + "grpc-keep-alive-required": "需要保持连接", + "grpc-keep-alive-min": "保持连接不能小于1", + "grpc-keep-alive-pattern": "保持连接无效", + "grpc-min-time-between-pings": "最小Ping间隔(毫秒)", + "grpc-min-time-between-pings-required": "需要最小Ping间隔", + "grpc-min-time-between-pings-min": "最小Ping间隔不能小于1", + "grpc-min-time-between-pings-pattern": "最小Ping间隔无效", + "grpc-min-ping-interval-without-data": "无数据时的最小Ping间隔(毫秒)", + "grpc-min-ping-interval-without-data-required": "需要无数据时的最小Ping间隔", + "grpc-min-ping-interval-without-data-min": "无数据时的最小Ping间隔不能小于1", + "grpc-min-ping-interval-without-data-pattern": "无数据时的最小Ping间隔无效", + "grpc-max-pings-without-data": "无数据时的最大Ping数", + "grpc-max-pings-without-data-required": "需要无数据时的最大Ping数", + "grpc-max-pings-without-data-min": "无数据时的最大Ping数不能小于1", + "grpc-max-pings-without-data-pattern": "无数据时的最大Ping数无效", + "inactivity-check-period-seconds": "不活跃检查期(秒)", + "inactivity-check-period-seconds-required": "需要不活跃检查期", + "inactivity-check-period-seconds-min": "不活跃检查期不能小于1", + "inactivity-check-period-seconds-pattern": "不活跃检查期无效", + "inactivity-timeout-seconds": "不活跃超时(秒)", + "inactivity-timeout-seconds-required": "需要不活跃超时", + "inactivity-timeout-seconds-min": "不活跃超时不能小于1", + "inactivity-timeout-seconds-pattern": "不活跃超时无效", "json-parse": "无效的JSON。", "json-required": "字段不能为空。", + "logs": { + "logs": "日志", + "days": "天", + "hours": "小时", + "minutes": "分钟", + "seconds": "秒", + "date-format": "日期格式", + "date-format-required": "需要日期格式", + "log-format": "日志格式", + "log-type": "日志类型", + "log-format-required": "需要日志格式", + "remote": "远程日志记录", + "remote-logs": "远程日志", + "local": "本地日志记录", + "level": "日志级别", + "file-path": "文件路径", + "file-path-required": "需要文件路径", + "saving-period": "日志保存期限", + "saving-period-min": "日志保存期限不能小于1", + "saving-period-required": "需要日志保存期限", + "backup-count": "备份数量", + "backup-count-min": "备份数量不能小于1", + "backup-count-required": "需要备份数量" + }, + "min-pack-send-delay": "最小包发送延迟(毫秒)", + "min-pack-send-delay-required": "最小包发送延迟是必需的", + "min-pack-send-delay-min": "最小包发送延迟不能小于0", "no-connectors": "无连接器", "no-data": "没有配置", "no-gateway-found": "未找到网关。", "no-gateway-matching": "未找到 '{{item}}' 。", "path-logs": "日志文件的路径", "path-logs-required": "路径是必需的。", + "permit-without-calls": "保持连接许可,无需响应", "remote": "远程配置", "remote-logging-level": "日志记录级别", "remove-entry": "删除配置", + "remote-shell": "远程Shell", + "remote-configuration": "远程配置", + "other": "其他", "save-tip": "保存配置", "security-type": "安全类型", "security-types": { "access-token": "访问令牌", - "tls": "TLS" + "username-password": "用户名和密码", + "tls": "TLS", + "tls-access-token": "TLS + 访问令牌", + "tls-private-key": "TLS + 私钥" + }, + "server-port": "服务器端口", + "statistics": { + "statistic": "统计信息", + "statistics": "统计信息", + "statistic-commands-empty": "无可用统计信息", + "commands": "命令", + "send-period": "统计信息发送周期(秒)", + "send-period-required": "统计信息发送周期是必需的", + "send-period-min": "统计信息发送周期不能小于60", + "send-period-pattern": "统计信息发送周期无效", + "check-connectors-configuration": "检查连接器配置(秒)", + "check-connectors-configuration-required": "检查连接器配置是必需的", + "check-connectors-configuration-min": "检查连接器配置不能小于1", + "check-connectors-configuration-pattern": "检查连接器配置无效", + "add": "添加命令", + "timeout": "超时时间", + "timeout-ms": "超时时间(毫秒)", + "timeout-required": "超时时间是必需的", + "timeout-min": "超时时间不能小于1", + "timeout-pattern": "超时时间无效", + "attribute-name": "属性名称", + "attribute-name-required": "属性名称是必需的", + "command": "命令", + "command-required": "命令是必需的", + "command-pattern": "命令无效", + "remove": "删除命令" }, "storage": "存储", "storage-max-file-records": "文件中的最大记录数", @@ -2408,6 +2818,16 @@ "storage-max-records-min": "最小记录数为1。", "storage-max-records-pattern": "数字无效。", "storage-max-records-required": "最大记录项必填。", + "storage-read-record-count": "存储中的读取记录数", + "storage-read-record-count-min": "最小记录数为1。", + "storage-read-record-count-pattern": "数字不合法。", + "storage-read-record-count-required": "需要读取记录数。", + "storage-max-read-record-count": "存储中的最大读取记录数", + "storage-max-read-record-count-min": "最小记录数为1。", + "storage-max-read-record-count-pattern": "数字不合法。", + "storage-max-read-record-count-required": "最大读取记录数必需。", + "storage-data-folder-path": "数据文件夹路径", + "storage-data-folder-path-required": "需要数据文件夹路径。", "storage-pack-size": "最大事件包大小", "storage-pack-size-min": "最小值为1。", "storage-pack-size-pattern": "数字无效。", @@ -2417,9 +2837,11 @@ "storage-type": "存储类型", "storage-types": { "file-storage": "文件存储", - "memory-storage": "内存存储" + "memory-storage": "内存存储", + "sqlite": "SQLITE" }, "thingsboard": "ThingsBoard", + "general": "常规", "thingsboard-host": "ThingsBoard主机", "thingsboard-host-required": "主机必填。", "thingsboard-port": "ThingsBoard端口", @@ -2427,23 +2849,74 @@ "thingsboard-port-min": "最小端口号为1。", "thingsboard-port-pattern": "端口无效。", "thingsboard-port-required": "端口必填。", - "tidy": "整洁", + "tidy": "整理", "tidy-tip": "整理配置JSON", "title-connectors-json": "连接器 {{typeName}} 配置", "tls-path-ca-certificate": "网关上CA证书的路径", "tls-path-client-certificate": "网关上客户端证书的路径", + "messages-ttl-check-in-hours": "消息TTL检查小时数", + "messages-ttl-check-in-hours-required": "需要提供消息TTL检查小时数。", + "messages-ttl-check-in-hours-min": "最小值为1。", + "messages-ttl-check-in-hours-pattern": "数字无效。", + "messages-ttl-in-days": "消息TTL天数", + "messages-ttl-in-days-required": "需要提供消息TTL天数。", + "messages-ttl-in-days-min": "最小值为1。", + "messages-ttl-in-days-pattern": "数字无效。", + "mqtt-qos": "QoS", + "mqtt-qos-required": "需要提供QoS", + "mqtt-qos-range": "QoS值的范围是从0到1", "tls-path-private-key": "网关上私钥的路径", "toggle-fullscreen": "切换全屏", "transformer-json-config": "配置JSON*", - "update-config": "添加/更新配置JSON" + "update-config": "添加/更新配置JSON", + "hints": { + "remote-configuration": "启用对网关的远程配置和管理", + "remote-shell": "通过远程Shell小部件启用对网关操作系统的远程控制", + "host": "ThingsBoard 主机名或IP地址", + "port": "ThingsBoard MQTT服务端口", + "token": "ThingsBoard 网关访问令牌", + "client-id": "ThingsBoard 网关MQTT客户端ID", + "username": "ThingsBoard 网关MQTT用户名", + "password": "ThingsBoard 网关MQTT密码", + "ca-cert": "CA证书文件的路径", + "date-form": "日志消息中的日期格式", + "data-folder": "包含数据的文件夹的路径(相对或绝对路径)", + "log-format": "日志消息格式", + "remote-log": "启用对网关的远程日志记录和日志读取", + "backup-count": "如果备份计数大于0,则在执行轮换时,最多保留备份计数个文件-最旧的文件将被删除", + "storage": "提供将数据发送到平台之前保存传入数据的配置", + "max-file-count": "将创建的文件的最大数量", + "max-read-count": "从存储中获取的消息计数并发送到ThingsBoard", + "max-records": "一个文件中存储的最大记录数", + "read-record-count": "从存储中获取的消息计数并发送到ThingsBoard", + "max-records-count": "在将数据发送到ThingsBoard之前,存储中的最大数据计数", + "ttl-check-hour": "网关多久检查一次数据是否过时", + "ttl-messages-day": "存储将保存数据的最大天数", + "commands": "用于收集附加统计信息的命令", + "attribute": "统计遥测键", + "timeout": "命令执行的超时时间", + "command": "命令执行的结果,将用作遥测的值", + "check-device-activity": "启用监视每个连接设备的活动", + "inactivity-timeout": "在此时间后,网关将断开设备的连接", + "inactivity-period": "设备活动检查的周期", + "minimal-pack-delay": "发送消息包之间的延迟(减小此设置会导致增加CPU使用率)", + "qos": "MQTT消息传递的服务质量(0-至多一次,1-至少一次)", + "server-port": "GRPC服务器侦听传入连接的网络端口", + "grpc-keep-alive-timeout": "在考虑连接死亡之前,服务器等待keepalive ping响应的最长时间", + "grpc-keep-alive": "没有活动RPC调用时两个连续keepalive ping消息之间的持续时间", + "grpc-min-time-between-pings": "服务器在发送keepalive ping消息之间应等待的最小时间量", + "grpc-max-pings-without-data": "在没有接收到任何数据之前,服务器可以发送的keepalive ping消息的最大数量,然后将连接视为死亡", + "grpc-min-ping-interval-without-data": "在没有发送或接收数据时,服务器在发送keepalive ping消息之间应等待的最小时间量", + "permit-without-calls": "允许服务器在没有活动RPC调用时保持GRPC连接活动" + } }, "grid": { "delete-item-title": "确定要删除此项吗?", "delete-item-text": "请注意,确认后,项目及其所有相关数据将不可恢复。", - "delete-items-title": "确定删除 { count, plural, =1 {# 项} other {# 项} }吗?", - "delete-items-action-title": "删除 { count, plural, =1 {# 个元素} other {# 个元素} }", + "delete-items-title": "确定删除 { count, plural, =1 {1 项} other {# 项} }吗?", + "delete-items-action-title": "删除 { count, plural, =1 {1 个元素} other {# 个元素} }", "delete-items-text": "请注意,确认后所有选择的项目将被删除,所有相关数据将不可恢复。", - "add-item-text": "添加新项目", + "add-item-text": "添加项目", "no-items-text": "未找到项目", "item-details": "项目详细信息", "delete-item": "删除项目", @@ -2467,26 +2940,28 @@ "browse-files": "浏览文件" }, "image-input": { - "drop-image-or": "拖拽图片或", - "drop-images-or": "拖拽图片或", - "no-images": "没有图片", + "drop-images-or": "拖放一张或多张图片", + "drag-and-drop": "拖放", + "or": "或", + "browse": "浏览", + "no-images": "未选择任何图片", "images": "图片" }, "import": { "no-file": "没有选择文件", - "drop-file": "拖动一个JSON文件或者单击以选择要上传的文件。", - "drop-json-file-or": "拖拽JSON文件或", - "drop-file-csv": "拖动一个CSV文件或单击以选择要上载的文件。", - "drop-file-csv-or": "拖拽CSV文件或", + "drop-file": "拖放一个JSON文件或者单击以选择要上传的文件。", + "drop-json-file-or": "拖放一个 JSON 文件或者", + "drop-file-csv": "拖放一个CSV文件或单击以选择要上载的文件。", + "drop-file-csv-or": "拖放一个 CSV 文件或者", "column-value": "数值", "column-title": "标题", "column-example": "示例值数据", "column-key": "属性/遥测键", - "credentials": "Credentials", + "credentials": "凭据", "csv-delimiter": "CSV分隔符", "csv-first-line-header": "第一行包含列名", "csv-update-data": "更新属性/遥测", - "details": "Details", + "details": "详情", "import-csv-number-columns-error": "一个文件至少应该包含两列", "import-csv-invalid-format-error": "文件格式无效。行: '{{line}}'", "column-type": { @@ -2519,8 +2994,14 @@ "lwm2m-server-secret-key": "LwM2M server证书密钥", "lwm2m-server-public-key-id": "LwM2M server公钥" }, + "snmp": { + "host": "SNMP 主机地址", + "port": "SNMP 端口", + "version": "SNMP 版本 (例如:v1, v2c, 或 v3)", + "community-string": "SNMP 团体字符串" + }, "isgateway": "是否网关", - "activity-time-from-gateway-device": "网关设备活动时间", + "activity-time-from-gateway-device": "来自网关设备的活动时间", "description": "说明", "routing-key": "边缘键", "secret": "边缘密钥" @@ -2529,7 +3010,7 @@ "select-file": "选择一个文件", "configuration": "导入配置", "column-type": "选择列类型", - "creat-entities": "创建新实体" + "creat-entities": "创建实体" }, "message": { "create-entities": "{{count}} 个新实体已成功创建。", @@ -2543,7 +3024,7 @@ "js-func": { "no-return-error": "函数必须返回值!", "return-type-mismatch": "函数必须返回 '{{type}}' 类型的值!", - "tidy": "整洁", + "tidy": "整理", "mini": "迷你" }, "key-val": { @@ -2555,12 +3036,13 @@ }, "layout": { "layout": "布局", + "layouts": "布局", "manage": "布局管理", "settings": "布局设置", "color": "颜色", "main": "主体", "right": "右侧", - "left": "左", + "left": "左侧", "select": "选择目标布局", "percentage-width": "百分比宽度(%)", "fixed-width": "固定(px)", @@ -2580,18 +3062,24 @@ "legend": { "direction": "图例方向", "position": "图例位置", + "show-values": "显示数值", + "min-option": "最小值", + "max-option": "最大值", + "average-option": "平均值", + "total-option": "总数", + "latest-option": "最新值", "sort-legend": "在图例中排序数据键", "show-max": "显示最大值", "show-min": "显示最小值", "show-avg": "显示平均值", "show-total": "显示总数", - "show-latest": "显示最新数据值", + "show-latest": "显示最新值", "settings": "图例设置", "min": "最小值", "max": "最大值", "avg": "平均值", "total": "总数", - "latest": "latest", + "latest": "最新值", "comparison-time-ago": { "previousInterval": "(历史间隔)", "customInterval": "(自定义间隔)", @@ -2606,7 +3094,7 @@ "request-password-reset": "请求密码重置", "reset-password": "重置密码", "create-password": "创建密码", - "two-factor-authentication": "双重因素身份验证", + "two-factor-authentication": "双因素认证", "passwords-mismatch-error": "输入的密码必须相同!", "password-again": "再次输入密码", "sign-in": "登录 ", @@ -2614,7 +3102,7 @@ "remember-me": "记住我", "forgot-password": "忘记密码?", "password-reset": "密码重置", - "expired-password-reset-message": "您的凭据已过期!请创建新密码。", + "expired-password-reset-message": "您的凭据已过期!请创建密码。", "new-password": "新密码", "new-password-again": "再次输入新密码", "password-link-sent-message": "密码重置链接已成功发送!", @@ -2639,8 +3127,272 @@ "markdown": { "edit": "编辑", "preview": "预览", - "copy-code": "单击拷贝", - "copied": "已拷贝!" + "copy-code": "点击复制", + "copied": "已复制!" + }, + "notification": { + "action-button": "操作按钮", + "action-type": "操作类型", + "active": "活动", + "add-notification-recipients-group": "添加通知收件人组", + "add-notification-template": "添加通知模板", + "add-recipient": "添加收件人", + "add-recipients": "添加收件人", + "add-rule": "添加规则", + "add-stage": "添加阶段", + "add-template": "添加模板", + "after": "之后", + "alarm-assignment-trigger-settings": "告警分配触发设置", + "alarm-comment-trigger-settings": "告警评论触发设置", + "alarm-trigger-settings": "告警触发设置", + "all": "所有", + "api-feature-hint": "如果字段为空,则触发器将应用于所有API功能", + "api-usage-trigger-settings": "API使用触发设置", + "new-platform-version-trigger-settings": "新平台版本触发设置", + "rate-limits-trigger-settings": "超出速率限制触发设置", + "at-least-one-should-be-selected": "至少需要选择一个", + "basic-settings": "基本设置", + "button-text": "按钮文本", + "button-text-required": "按钮文本必填。", + "button-text-max-length": "按钮文本应小于或等于{{ length }}个字符。", + "compose": "组合", + "conversation": "对话", + "conversation-required": "对话必填。", + "copy-notification-template": "复制通知模板", + "copy-rule": "复制规则", + "copy-template": "复制模板", + "create-new": "创建", + "created": "已创建", + "delete-notification-text": "请注意,在确认后,通知将无法恢复。", + "delete-notification-title": "您确定要删除该通知吗?", + "delete-notifications-text": "请注意,在确认后,通知将无法恢复。", + "delete-notifications-title": "确定要删除 { count, plural, =1 {1 个通知} other {# 个通知} } 吗?", + "delete-recipient-text": "请注意,在确认后,收件人将无法恢复。", + "delete-recipient-title": "确定要删除收件人 '{{recipientName}}' 吗?", + "delete-recipients-text": "请注意,在确认后,收件人将无法恢复。", + "delete-recipients-title": "确定要删除 { count, plural, =1 {1 个收件人} other {# 个收件人} } 吗?", + "delete-request-text": "请注意,在确认后,请求将无法恢复。", + "delete-request-title": "确定要删除请求吗?", + "delete-requests-text": "请注意,在确认后,请求将无法恢复。", + "delete-requests-title": "确定要删除 { count, plural, =1 {1 个请求} other {# 个请求} } 吗?", + "delete-rule-text": "请注意,在确认后,规则将无法恢复。", + "delete-rule-title": "确定要删除规则 '{{ruleName}}' 吗?请注意,确认后无法恢复。", + "delete-rules-text": "请注意,在确认后,规则将无法恢复。", + "delete-rules-title": "确定要删除 { count, plural, =1 {1 条规则} other {# 条规则} } 吗?", + "delete-template-text": "请注意,在确认后,模板将无法恢复。", + "delete-template-title": "确定要删除模板 '{{templateName}}' 吗?", + "delete-templates-text": "请注意,在确认后,模板将无法恢复。", + "delete-templates-title": "确定要删除 { count, plural, =1 {1 个模板} other {# 个模板} } 吗?", + "deleted": "已删除", + "delivery-method": { + "delivery-method": "推送方式", + "email": "Email", + "email-preview": "Email 通知预览", + "slack": "Slack", + "slack-preview": "Slack 通知预览", + "microsoft-teams" : "Microsoft Teams", + "microsoft-teams-preview" : "Microsoft Teams 通知预览", + "sms": "SMS", + "sms-preview": "SMS 通知预览", + "web": "Web", + "web-preview": "Web 通知预览" + }, + "delivery-method-not-configure-click": "推送方式未配置,点击进行设置。", + "delivery-method-not-configure-contact": "推送方式未配置,请与您的系统管理员联系。", + "delivery-methods": "推送方式", + "description": "描述", + "device-activity-trigger-settings": "设备活动触发设置", + "device-list-rule-hint": "如果该字段为空,则触发器将适用于所有设备。", + "device-profiles-list-rule-hint": "如果该字段为空,则触发器将适用于所有设备配置。", + "disabled": "已禁用", + "edit-notification-recipients-group": "编辑通知收件人群组", + "edit-notification-template": "编辑通知模板", + "edit-rule": "编辑规则", + "edit-template": "编辑模板", + "enabled": "已启用", + "entities-limit-trigger-settings": "实体限制触发器设置", + "entity-action-trigger-settings": "实体动作触发器设置", + "entity-type": "实体类型", + "escalation-chain": "逐步升级链", + "failed-send": "发送失败", + "fails": "{ count, plural, =1 {1 个失败} other {# 个失败} }", + "filter": "筛选器", + "first-recipient": "第一收件人", + "inactive": "非活动", + "inbox": "收件箱", + "notification-inbox": "通知 / 收件箱", + "input-field-support-templatization": "输入字段支持模板化", + "input-fields-support-templatization": "输入字段支持模板化", + "link": "链接", + "link-required": "链接必填。", + "link-type": { + "dashboard": "打开仪表板", + "link": "打开URL链接" + }, + "loading-notifications": "加载通知中…", + "management": "通知管理", + "mark-all-as-read": "全部标记为已读", + "mark-as-read": "标记为已读", + "message": "信息", + "message-required": "信息必填。", + "message-max-length": "提示信息应该少于或等于 {{ length }} 个字符", + "name": "名称", + "name-required": "名称必填", + "new-notification": "新通知", + "no-inbox-notification": "未找到通知", + "no-notification-request": "没有通知请求", + "no-notification-templates": "未找到通知模板", + "no-notifications-yet": "目前还没有通知", + "no-recipients-notification": "没有收件人的通知", + "no-rule": "未配置规则", + "no-rules-notification": "没有规则通知", + "no-severity-found": "未找到严重性级别", + "no-severity-matching": "未找到 '{{severity}}'。", + "no-template-matching": "未找到与 '{{template}}' 匹配的资源。", + "not-found-slack-recipient": "未找到 Slack 的收件人", + "notification": "通知", + "notification-center": "通知中心", + "notify": "通知", + "notify-again": "再次通知", + "notify-alarm-action": { + "acknowledged": "告警已确认", + "assigned": "告警已分配", + "cleared": "告警已清除", + "created": "告警已确认", + "severity-changed": "告警级别已更改", + "unassigned": "告警未分配" + }, + "notify-on": "通知条件", + "notify-on-comment-update": "在评论更新时通知", + "notify-on-required": "通知条件必填。", + "notify-on-unassign": "在取消分配时通知", + "notify-only-user-comments": "仅在用户评论时通知", + "only-rule-chain-lifecycle-failures": "仅在规则链生命周期故障时通知", + "only-rule-node-lifecycle-failures": "仅在规则节点生命周期故障时通知", + "platform-users": "平台用户", + "rate-limits": "速率限制", + "rate-limits-hint": "如果该字段为空,则触发器将应用于所有速率限制", + "recipient": "收件人", + "recipient-group": "收件人组", + "recipient-type": { + "affected-tenant-administrators": "受影响的租户管理员", + "affected-user": "受影响的用户", + "all-users": "所有用户", + "customer-users": "客户用户", + "system-administrators": "系统管理员", + "tenant-administrators": "租户管理员", + "user-filters": "用户筛选器", + "user-list": "用户列表", + "users-entity-owner": "实体所有者的用户" + }, + "recipients": "收件人", + "notification-recipients": "通知 / 收件人", + "recipients-count": "{ count, plural, =1 {1 个收件人} other {# 个收件人} }", + "recipients-required": "收件人必填。", + "refresh-allow-delivery-method": "刷新允许推送方式", + "request-search": "请求搜索", + "request-status": { + "processing": "处理中", + "scheduled": "已安排", + "sent": "已发送" + }, + "review": "检查", + "rule": "规则", + "rule-chain-list-rule-hint": "如果字段为空,则触发器将应用于所有规则链。", + "rule-engine-events-trigger-settings": "规则引擎事件触发器设置", + "rule-engine-filter": "规则引擎筛选器", + "rule-name": "规则名称", + "rule-name-required": "名称必填。", + "rule-disable": "禁用通知规则", + "rule-enable": "启用通知规则", + "rule-node-filter": "规则节点筛选器", + "rules": "规则", + "notification-rules": "通知 / 规则", + "scheduler-later": "稍后安排", + "search-notification": "搜索通知", + "search-recipients": "搜索收件人", + "search-rules": "搜索规则", + "search-templates": "搜索模板", + "see-documentation": "查看文档", + "selected-notifications": "已选择 { count, plural, =1 {1 个通知} other {# 个通知} }", + "selected-recipients": "已选择 { count, plural, =1 {1 个收件人} other {# 个收件人} }", + "selected-requests": "已选择 { count, plural, =1 {1 个请求} other {# 个请求} }", + "selected-rules": "已选择 { count, plural, =1 {1 个规则} other {# 个规则} }", + "selected-template": "已选择 { count, plural, =1 {1 个模板} other {# 个模板} }", + "send-notification": "发送通知", + "sent": "已发送", + "notification-sent": "通知 / 已发送", + "set-entity-from-notification": "将通知中的实体设置为仪表盘状态", + "slack-chanel-type": "Slack 频道类型", + "slack-chanel-types": { + "direct": "直接消息", + "private-channel": "私有频道", + "public-channel": "公开频道" + }, + "start-from-scratch": "从头开始", + "status": "状态", + "stop-escalation-alarm-status-become": "停止逐步升级,将告警状态设置为:", + "subject": "主题", + "subject-required": "主题必填。", + "template": "模板", + "template-name": "模板名称", + "template-required": "模板必填。", + "template-type": { + "alarm": "告警", + "alarm-assignment": "告警分配", + "alarm-comment": "告警评论", + "api-usage-limit": "API使用限制", + "device-activity": "设备活动", + "entities-limit": "实体限制", + "entity-action": "实体操作", + "general": "通用", + "rule-engine-lifecycle-event": "规则引擎生命周期事件", + "rule-node": "规则节点", + "new-platform-version": "新的平台版本", + "rate-limits": "超过速率限制" + }, + "templates": "模板", + "notification-templates": "通知 / 模板", + "tenant-profiles-list-rule-hint": "如果字段为空,触发器将应用于所有租户配置", + "tenants-list-rule-hint": "如果字段为空,触发器将应用于所有租户", + "threshold": "阈值", + "theme-color": "主题颜色", + "time": "时间", + "track-rule-node-events": "追踪规则节点事件", + "trigger": { + "alarm": "告警", + "alarm-assignment": "告警分配", + "alarm-comment": "告警评论", + "api-usage-limit": "API使用限制", + "device-activity": "设备活动", + "entities-limit": "实体限制", + "entity-action": "实体操作", + "rule-engine-lifecycle-event": "规则引擎生命周期事件", + "new-platform-version": "新的平台版本", + "rate-limits": "超过速率限制", + "trigger": "触发器", + "trigger-required": "触发器必填。" + }, + "type": "类型", + "unread": "未读", + "updated": "已更新", + "use-template": "使用模板", + "view-all": "查看全部", + "warning": "警告", + "webhook-url": "Webhook URL", + "webhook-url-required": "需要填写Webhook URL", + "channel-name": "频道名称", + "channel-name-required": "需要填写频道名称", + "settings": { + "notification-settings": "通知设置", + "reset-all": "重置全部设置", + "reset-all-title": "您确定要将表单重置吗?", + "reset-all-text": "确认后,设置表单将重置为默认值并保存。", + "type": "类型", + "enable-all": "全部启用", + "disable-all": "全部禁用", + "delivery-not-configured": "未配置传递方式" + } }, "ota-update": { "add": "添加包", @@ -2655,7 +3407,7 @@ "checksum-copied-message": "包校验和已复制到剪贴板", "change-firmware": "固件的更改可能会导致 { count, plural, =1 {1 个设备} other {# 个设备} } 的更新。", "change-software": "软件的更改可能会导致 { count, plural, =1 {1 个设备} other {# 个设备} } 的更新。", - "chose-compatible-device-profile": "上传的包仅适用于具有所选配置文件的设备。", + "chose-compatible-device-profile": "上传的包仅适用于具有所选配置的设备。", "chose-firmware-distributed-device": "选择将分发到设备的固件", "chose-software-distributed-device": "选择将分发到设备的软件", "content-type": "内容类型", @@ -2673,8 +3425,8 @@ "direct-url-copied-message": "包直接URL已复制到剪贴板", "direct-url-required": "直接URL必填", "download": "下载包", - "drop-file": "拖放打包文件或点击选择要上传的文件。", - "drop-package-file-or": "拖拽文件或", + "drop-file": "拖放包文件或点击选择要上传的文件。", + "drop-package-file-or": "拖放一个包文件或者", "file-name": "文件名", "file-size": "文件大小", "file-size-bytes": "文件大小(以字节为单位)", @@ -2693,7 +3445,7 @@ "selected-package": "{ count, plural, =1 {1 个包} other {# 个包} } 选中", "title": "标题", "title-required": "标题必填。", - "title-max-length": "标题长度必须少于256个字符", + "title-max-length": "标题长度应该少于256个字符。", "types": { "firmware": "固件", "software": "软件" @@ -2704,8 +3456,8 @@ "version-required": "版本必填。", "version-tag": "版本标签", "version-tag-hint": "自定义标签应与您设备报告的软件包版本相匹配。", - "version-max-length": "版本长度必须少于256个字符", - "warning-after-save-no-edit": "上传包后,您将无法修改标题、版本、设备配置文件和包类型。" + "version-max-length": "版本长度应该少于256个字符", + "warning-after-save-no-edit": "上传包后,您将无法修改标题、版本、设备配置和包类型。" }, "position": { "top": "顶部", @@ -2729,18 +3481,26 @@ }, "security": { "security": "安全", + "general-settings": "通用安全设置", + "access-token": "访问令牌", + "access-token-required": "需要访问令牌", + "clientId": "客户端ID", + "clientId-required": "需要客户端ID", + "username": "用户名", + "username-required": "需要用户名", + "ca-cert": "CA证书", "2fa": { - "2fa": "双因素身份验证", - "2fa-description": "双因素身份验证可保护您的帐户免受未经授权的访问。在登录时必须输入安全验证码。", + "2fa": "双因素认证", + "2fa-description": "双因素认证可保护您的帐户免受未经授权的访问。在登录时必须输入安全验证码。", "authenticate-with": "可以使用以下身份验证:", "disable-2fa-provider-text": "禁用 {{ name }} 会降低帐户的安全性", "disable-2fa-provider-title": "确定要禁用 {{ name }} 吗?", "get-new-code": "获取新验证码", - "main-2fa-method": "用作主要的双因素身份验证方法", + "main-2fa-method": "用作主要的双因素认证方法", "dialog": { "activation-step-description-email": "下次登录时,系统将提示您输入电子邮件中的验证码。", "activation-step-description-sms": "下次登录时,系统将提示您输入短信中的验证码。", - "activation-step-description-totp": "下次登录时,您需要提供一个双因素身份验证码。", + "activation-step-description-totp": "下次登录时,您需要提供一个双因素认证码。", "activation-step-label": "激活", "backup-code-description": "打印出验证码,以便在您需要时使用它们登录帐户。每个备份验证码可以使用一次。", "backup-code-warn": "离开此页后,这些代码将无法再次显示。使用以下选项安全存放。", @@ -2780,7 +3540,7 @@ "totp-hint": "已为您的帐户设置验证APP", "backup_code": "备份验证码", "backup-code-description": "这些可打印的一次性密码允许您在离开手机时登录,比如正在旅行。", - "backup-code-hint": "{{ info }}个一次性代码处于激活状态" + "backup-code-hint": "{{ info }}个一次性代码此时处于激活状态" } }, "password-requirement": { @@ -2801,6 +3561,7 @@ "relation": { "relations": "关联", "direction": "方向", + "clear-relation-type": "清除关系类型", "search-direction": { "FROM": "从", "TO": "到" @@ -2811,7 +3572,7 @@ }, "from-relations": "向外的关联", "to-relations": "向内的关联", - "selected-relations": "已选择{ count, plural, =1 {# 个关联} other {# 个关联} }", + "selected-relations": "已选择 { count, plural, =1 {1 个关联} other {# 个关联} }", "type": "类型", "to-entity-type": "到实体类型", "to-entity-name": "到实体名称", @@ -2822,19 +3583,20 @@ "delete": "删除关联", "relation-type": "关联类型", "relation-type-required": "关联类型必填", - "relation-type-max-length": "关联类型长度必须少于256个字符", + "relation-type-max-length": "关联类型长度应该少于256个字符。", "any-relation-type": "任何类型", "add": "添加关联", "edit": "编辑关联", "delete-to-relation-title": "确定要删除实体 '{{entityName}}' 的关联吗?", "delete-to-relation-text": "确定删除后实体 '{{entityName}}' 将取消与当前实体的关联关系。", - "delete-to-relations-title": "确定要删除 { count, plural, =1 {# 个关联} other {# 个关联} }吗?", + "delete-to-relations-title": "确定要删除 { count, plural, =1 {1 个关联} other {# 个关联} }吗?", "delete-to-relations-text": "确定删除所有选择的关联关系后,与当前实体对应的所有关联关系将被移除。", "delete-from-relation-title": "确定要从实体 '{{entityName}}' 删除关联吗?", "delete-from-relation-text": "确定删除后,当前实体将与实体 '{{entityName}}' 取消关联", - "delete-from-relations-title": "确定删除 { count, plural, =1 {# 个关联} other {# 个关联} } 吗?", + "delete-from-relations-title": "确定删除 { count, plural, =1 {1 个关联} other {# 个关联} } 吗?", "delete-from-relations-text": "确定删除所有选择的关联关系后,当前实体将与对应的实体取消关联", "remove-relation-filter": "移除关联筛选器", + "remove-filter": "移除过滤器", "add-relation-filter": "添加关联筛选器", "any-relation": "任意关联", "relation-filters": "关联筛选器", @@ -2844,19 +3606,20 @@ }, "resource": { "add": "添加资源", + "all-types": "全部", "copyId": "复制资源ID", "delete": "删除资源", "delete-resource-text": "请注意:确认后,资源将不可恢复。", "delete-resource-title": "确定要删除资源 '{{resourceTitle}}' 吗?", - "delete-resources-action-title": "删除 { count, plural, =1 {# 个资源} other {# 个资源} }", + "delete-resources-action-title": "删除 { count, plural, =1 {1 个资源} other {# 个资源} }", "delete-resources-text": "请注意:确认后,所有选定的资源将被删除。", - "delete-resources-title": "确定要删除 { count, plural, =1 {# 个资源} other {# 个资源} }吗?", - "download": "Download resource", - "drop-file": "拖拽资源文件或单击以选择要上传的文件。", - "drop-resource-file-or": "Drag and drop a resource file or", + "delete-resources-title": "确定要删除 { count, plural, =1 {1 个资源} other {# 个资源} }吗?", + "download": "下载资源", + "drop-file": "拖放资源文件或单击以选择要上传的文件。", + "drop-resource-file-or": "拖放一个资源文件或者", "empty": "资源为空", - "file-name": "File name", - "idCopiedMessage": "拖拽资源文件或", + "file-name": "文件名称", + "idCopiedMessage": "资源ID已复制到剪贴板", "no-resource-matching": "未找到与 '{{widgetsBundle}}' 匹配的资源。", "no-resource-text": "未找到资源", "open-widgets-bundle": "打开部件库", @@ -2865,20 +3628,27 @@ "resource-type": "资源类型", "resources-library": "资源库", "search": "查找资源", - "selected-resources": "已选择{ count, plural, =1 {# 个资源} other {# 个资源} }", + "selected-resources": "已选择{ count, plural, =1 {1 个资源} other {# 个资源} }", "system": "系统", "title": "标题", "title-required": "标题是必填项。", - "title-max-length": "标题长度必须少于256个字符" + "title-max-length": "标题长度应该少于256个字符。", + "type": { + "jks": "JKS", + "js-module": "JS 模块", + "lwm2m-model": "LWM2M 模型", + "pkcs-12": "PKCS #12" + } }, "rulechain": { "rulechain": "规则链", + "rulechain-events": "规则链事件", "rulechains": "规则链库", "root": "是否根链", "delete": "删除规则链", "name": "名称", "name-required": "名称必填。", - "name-max-length": "名称长度必须少于256个字符", + "name-max-length": "名称长度应该少于256个字符。", "description": "说明", "add": "添加规则链", "set-root": "设置为根规则链", @@ -2887,9 +3657,9 @@ "delete-rulechain-title": " 确定要删除规则链'{{ruleChainName}}'吗?", "delete-rulechain-text": "请注意,确认后,规则链和所有相关数据将不可恢复。", "delete-rulechains-title": "确定要删除{count, plural, =1 { 1 个规则链} other {# 个规则链} }吗?", - "delete-rulechains-action-title": "删除 { count, plural, =1 {# 个规则链} other {# 个规则链} }", + "delete-rulechains-action-title": "删除 { count, plural, =1 {1 个规则链} other {# 个规则链} }", "delete-rulechains-text": "请注意:确认后,所有选定的规则链将被删除,所有相关的数据将不可恢复。", - "add-rulechain-text": "添加新的规则链", + "add-rulechain-text": "添加规则链", "no-rulechains-text": "未找到规则链", "rulechain-details": "规则链详情", "details": "详情", @@ -2898,7 +3668,7 @@ "import": "导入规则链", "export": "导出规则链", "export-failed-error": "无法导出规则链:{{error}}", - "create-new-rulechain": "创建新的规则链", + "create-new-rulechain": "创建规则链", "rulechain-file": "规则链文件", "invalid-rulechain-file-error": "不能导入规则链:无效的规则链数据格式。", "copyId": "复制规则链ID", @@ -2909,9 +3679,8 @@ "management": "规则集管理", "debug-mode": "调试模式", "search": "查找规则链", - "selected-rulechains": "已选择 { count, plural, =1 {# 个规则链} other {# 个规则链} }", + "selected-rulechains": "已选择 { count, plural, =1 {1 个规则链} other {# 个规则链} }", "open-rulechain": "打开规则链", - "assign-new-rulechain": "Assign new rulechain", "edge-template-root": "模版根链", "assign-to-edge": "分配给边缘", "edge-rulechain": "边缘规则链", @@ -2930,20 +3699,22 @@ "unset-auto-assign-to-edge": "不将规则链分配给边缘", "unset-auto-assign-to-edge-title": "确定不再将规则链'{{ruleChainName}}'自动分配给新创建的边缘吗?", "unset-auto-assign-to-edge-text": "确认后,将不再自动分配规则链给新创建的边缘。", - "unassign-rulechain-title": "你确定要取消分配规则链'{{ruleChainName}}'?", + "unassign-rulechain-title": "确定要取消分配规则链 '{{ruleChainName}}' 吗?", "unassign-rulechains": "取消分配规则链" }, "rulenode": { + "rule-node-events": "规则节点事件", "details": "详情", "events": "事件", "search": "查找节点", "open-node-library": "打开节点库", + "close-node-library": "关闭节点库", "add": "添加规则节点", "name": "名称", "name-required": "名称必填。", - "name-max-length": "名称长度必须少于256个字符", + "name-max-length": "名称长度应该少于256个字符。", "type": "类型", - "description": "说明", + "rule-node-description": "规则节点描述", "delete": "删除规则节点", "select-all-objects": "选择所有节点和连接", "deselect-all-objects": "取消选择所有节点和连接", @@ -2955,6 +3726,7 @@ "deselect-all": "取消选择", "rulenode-details": "规则节点详情", "debug-mode": "调试模式", + "singleton-mode": "单例模式", "configuration": "配置", "link": "链接", "link-details": "规则节点链接详情", @@ -2967,7 +3739,7 @@ "link-labels-required": "链接标签必填。", "no-link-labels-found": "未找到链接标签", "no-link-label-matching": "未找到匹配 '{{label}}' 的链接标签。", - "create-new-link-label": "创建一个新的!", + "create-new-link-label": "创建链接标签", "type-filter": "筛选器", "type-filter-details": "使用配置条件筛选传入消息", "type-enrichment": "属性集", @@ -2980,7 +3752,7 @@ "type-external-details": "与外部系统交互", "type-rule-chain": "规则链", "type-rule-chain-details": "将传入消息转发到指定的规则链", - "type-flow": "流", + "type-flow": "Flow", "type-flow-details": "组织消息流", "type-input": "输入", "type-input-details": "规则链的逻辑输入,将传入消息转发到下一个相关规则节点", @@ -2990,7 +3762,7 @@ "ui-resources-load-error": "加载配置UI资源失败。", "invalid-target-rulechain": "无法解析目标规则链!", "test-script-function": "测试脚本功能", - "script-lang-java-script": "Java Script", + "script-lang-java-script": "JavaScript", "script-lang-tbel": "TBEL", "message": "消息", "message-type": "消息类型", @@ -3001,12 +3773,13 @@ "output": "输出", "test": "测试", "help": "帮助", - "reset-debug-mode": "重置所有节点中的调试模式" + "reset-debug-mode": "重置所有节点中的调试模式", + "test-with-this-message": "使用此消息进行{{test}}测试" }, "timezone": { "timezone": "时区", "select-timezone": "选择时区", - "no-timezones-matching": "找不到匹配的'{{timezone}}'时区。", + "no-timezones-matching": "未找到与 '{{timezone}}' 匹配的时区。", "timezone-required": "时区必填。", "browser-time": "浏览器时间" }, @@ -3051,10 +3824,10 @@ "details": "队列详情", "topic": "主题", "submit-settings": "提交设置", - "submit-strategy": "Strategy type *", + "submit-strategy": "策略类型*", "grouping-parameter": "分组参数", "processing-settings": "重试处理设置", - "processing-strategy": "Processing type *", + "processing-strategy": "处理类型*", "retries-settings": "重试设置", "polling-settings": "轮询设置", "batch-processing": "批量处理", @@ -3075,6 +3848,8 @@ "description": "说明", "description-hint": "此文本将显示在队列说明中,而不是所选策略中", "alt-description": "提交策略:{{submitStrategy}},处理策略:{{processingStrategy}}", + "custom-properties": "自定义属性", + "custom-properties-hint": "自定义队列(主题)创建属性,例如 'retention.ms:604800000;retention.bytes:1048576000'", "strategies": { "sequential-by-originator-label": "按发起者顺序处理", "sequential-by-originator-hint": "在确认设备A的前一条消息之前,不会提交设备A的新消息", @@ -3121,14 +3896,14 @@ "admins": "管理员", "manage-tenant-admins": "管理租户管理员", "delete": "删除租户", - "add-tenant-text": "添加新租户", + "add-tenant-text": "添加租户", "no-tenants-text": "未找到租户", "tenant-details": "租客详情", - "title-max-length": "标题长度必须少于256个字符", + "title-max-length": "标题长度应该少于256个字符。", "delete-tenant-title": "确定要删除租户'{{tenantTitle}}'吗?", "delete-tenant-text": "请注意:确认后,租户和所有相关数据将不可恢复。", - "delete-tenants-title": "确定要删除 {count,plural, =1 {# 个租户} other {# 个租户} } 吗?", - "delete-tenants-action-title": "删除 { count, plural, =1 {# 个租户} other {# 个租户} }", + "delete-tenants-title": "确定要删除 {count,plural,=1 {1 个租户} other {# 个租户} } 吗?", + "delete-tenants-action-title": "删除 { count, plural, =1 {1 个租户} other {# 个租户} }", "delete-tenants-text": "请注意:确认后,所有选定的租户将被删除,所有相关数据将不可恢复。", "title": "标题", "title-required": "标题必填。", @@ -3141,7 +3916,7 @@ "no-tenants-matching": "未找到匹配 '{{entity}}' 的租户", "tenant-required": "租户必填", "search": "查找租户", - "selected-tenants": "已选择 { count, plural, =1 {# 个租户} other {# 个租户} }", + "selected-tenants": "已选择 { count, plural, =1 {1 个租户} other {# 个租户} }", "isolated-tb-rule-engine": "使用独立的规则引擎服务", "isolated-tb-rule-engine-details": "每个独立租户需要单独的规则引擎微服务" }, @@ -3149,12 +3924,13 @@ "tenant-profile": "租户配置", "tenant-profiles": "租户配置", "add": "添加租户配置", + "add-profile": "添加配置", "edit": "编辑租户配置", "tenant-profile-details": "租户配置详细信息", "no-tenant-profiles-text": "未找到租户配置", "name-max-length": "名称长度必须少于256个字符", "search": "查找租户配置", - "selected-tenant-profiles": "已选择 { count, plural, =1 {# 个租户配置} other {# 个租户配置} }", + "selected-tenant-profiles": "已选择 { count, plural, =1 {1 个租户配置} other {# 个租户配置} }", "no-tenant-profiles-matching": "未找到与 '{{entity}}' 匹配的租户配置。", "tenant-profile-required": "租户配置必填", "idCopiedMessage": "租户配置ID已复制到剪贴板", @@ -3169,17 +3945,17 @@ "default": "默认", "delete-tenant-profile-title": "确定要删除租户配置 '{{tenantProfileName}}'吗?", "delete-tenant-profile-text": "请注意:确认后,租户配置和所有相关数据将不可恢复。", - "delete-tenant-profiles-title": "确定要删除 { count, plural, =1 {# 个租户配置} other {# 个租户配置} }吗?", + "delete-tenant-profiles-title": "确定要删除 { count, plural, =1 {1 个租户配置} other {# 个租户配置} }吗?", "delete-tenant-profiles-text": "请注意:确认后,所有选定的租户配置将被删除,所有相关数据将不可恢复。", "set-default-tenant-profile-title": "确定要将租户配置 '{{tenantProfileName}}' 设为默认值吗?", "set-default-tenant-profile-text": "确认后,此租户配置将被标记为默认配置,并将用于未指定配置的新租户。", "no-tenant-profiles-found": "未找到租户配置。", - "create-new-tenant-profile": "创建一个新的!", - "create-tenant-profile": "创建新的租户配置", + "create-new-tenant-profile": "创建租户配置", + "create-tenant-profile": "创建租户配置", "import": "导入租户配置", "export": "导出租户配置", "export-failed-error": "无法导出租户配置: {{error}}", - "tenant-profile-file": "租户配置文件", + "tenant-profile-file": "租户配置", "invalid-tenant-profile-file-error": "无法导入租户配置:无效的租户配置数据结构。", "advanced-settings": "高级设置", "entities": "实体", @@ -3213,6 +3989,7 @@ "maximum-ota-packages-sum-data-size": "OTA包文件总大小", "maximum-ota-package-sum-data-size-required": "OTA包文件总大小必填。", "maximum-ota-package-sum-data-size-range": "OTA包文件总大小不能为负数", + "rest-requests-for-tenant": "租户REST请求", "transport-tenant-telemetry-msg-rate-limit": "租户遥测消息", "transport-tenant-telemetry-data-points-rate-limit": "租户遥测数据点", "transport-device-msg-rate-limit": "设备消息", @@ -3220,6 +3997,8 @@ "transport-device-telemetry-data-points-rate-limit": "设备遥测消息", "tenant-entity-export-rate-limit": "实体版本创建", "tenant-entity-import-rate-limit": "实体版本加载", + "tenant-notification-request-rate-limit": "通知请求", + "tenant-notification-requests-per-rule-rate-limit": "每个通知规则的通知请求", "max-transport-messages": "最大传输消息数", "max-transport-messages-required": "最大传输消息数必填。", "max-transport-messages-range": "最大传输消息数不能为负数", @@ -3232,6 +4011,9 @@ "max-j-s-executions": "最大 JavaScript 执行数", "max-j-s-executions-required": "最大 JavaScript 执行数必填。", "max-j-s-executions-range": "最大 JavaScript 执行数不能为负数", + "max-tbel-executions": "最大 TBEL 执行数", + "max-tbel-executions-required": "需要指定最大 TBEL 执行数。", + "max-tbel-executions-range": "最大 TBEL 执行数不能为负数。", "max-d-p-storage-days": "最大存储点天", "max-d-p-storage-days-required": "最大存储点天必填。", "max-d-p-storage-days-range": "最大存储点天不能为负数", @@ -3244,12 +4026,19 @@ "rpc-ttl-days": "RPC TTL天数", "rpc-ttl-days-required": "RPC TTL天数必填。", "rpc-ttl-days-days-range": "RPC TTL天数不能为负数", + "queue-stats-ttl-days": "队列统计信息TTL天数", + "queue-stats-ttl-days-required": "需要指定队列统计信息TTL天数", + "queue-stats-ttl-days-range": "队列统计信息TTL天数不能为负数", + "rule-engine-exceptions-ttl-days": "规则引擎异常TTL天数", + "rule-engine-exceptions-ttl-days-required": "需要指定规则引擎异常TTL天数", + "rule-engine-exceptions-ttl-days-range": "规则引擎异常TTL天数不能为负数", "max-rule-node-executions-per-message": "每条消息的最大规则节点执行数", "max-rule-node-executions-per-message-required": "每个消息的最大规则节点执行数必填。", "max-rule-node-executions-per-message-range": "每条消息的最大规则节点执行数不能为负", "max-emails": "最大电子邮件发送数", "max-emails-required": "最大电子邮件发送数必填。", "max-emails-range": "最大电子邮件发送数不能为负", + "sms-enabled": "启用短信", "max-sms": "最大短信发送数", "max-sms-required": "最大短信发送数必填。", "max-sms-range": "最大短信发送数不能为负", @@ -3264,6 +4053,7 @@ "incorrect-pattern-for-rate-limits": "格式为以冒号分割容量与周期(秒)并以逗号分割配置对,例如 100:1,2000:60", "too-small-value-zero": "数值必须大于0", "too-small-value-one": "数值必须大于1", + "queue-size-is-limited-by-system-configuration": "队列的大小也受系统配置的限制。", "cassandra-tenant-limits-configuration": "租户Cassandra查询", "ws-limit-max-sessions-per-tenant": "租户最大会话数", "ws-limit-max-sessions-per-customer": "客户最大会话数", @@ -3286,11 +4076,14 @@ "edit-transport-device-msg-title": "编辑传输设备消息速率限制", "edit-transport-device-telemetry-msg-title": "编辑传输设备遥测消息速率限制", "edit-transport-device-telemetry-data-points-title": "编辑传输设备遥测数据点速率限制", + "edit-tenant-rest-limits-title": "Edit REST requests for tenant rate limits", "edit-customer-rest-limits-title": "编辑客户REST请求速率限制", "edit-ws-limit-updates-per-session-title": "编辑会话WS更新速率限制", "edit-cassandra-tenant-limits-configuration-title": "编辑租户Cassandra查询速率限制", "edit-tenant-entity-export-rate-limit-title": "编辑实体版本创建速率限制", "edit-tenant-entity-import-rate-limit-title": "编辑实体版本加载速率限制", + "edit-tenant-notification-request-rate-limit-title": "编辑通知请求的速率限制", + "edit-tenant-notification-requests-per-rule-rate-limit-title": "编辑每个通知规则的通知请求速率限制", "messages-per": "条消息每", "not-set": "未配置", "number-of-messages": "消息数量", @@ -3312,10 +4105,10 @@ } }, "timeinterval": { - "seconds-interval": "{ seconds, plural, =1 {# 秒} other {# 秒} }", - "minutes-interval": "{ minutes, plural, =1 {# 分} other {# 分} }", - "hours-interval": "{ hours, plural, =1 {# 小时} other {# 小时} }", - "days-interval": "{ days, plural, =1 {# 天} other {# 天} }", + "seconds-interval": "{ seconds, plural, =1 {1 秒} other {# 秒} }", + "minutes-interval": "{ minutes, plural, =1 {1 分} other {# 分} }", + "hours-interval": "{ hours, plural, =1 {1 小时} other {# 小时} }", + "days-interval": "{ days, plural, =1 {1 天} other {# 天} }", "days": "天", "hours": "小时", "minutes": "分钟", @@ -3328,6 +4121,8 @@ "previous-week": "前一周(周日至周六)", "previous-week-iso": "前一周(周一至周日)", "previous-month": "前一个月", + "previous-quarter": "上季度", + "previous-half-year": "前半年", "previous-year": "前一年", "current-hour": "当前小时", "current-day": "当前天", @@ -3338,6 +4133,10 @@ "current-week-iso-so-far": "本周到目前为止(周一至周日)", "current-month": "本月", "current-month-so-far": "本月到目前为止", + "current-quarter": "当前季度", + "current-quarter-so-far": "当前季度到目前为止", + "current-half-year": "当前半年", + "current-half-year-so-far": "当前半年到目前为止", "current-year": "本年", "current-year-so-far": "本年到目前为止" } @@ -3350,20 +4149,456 @@ "days": "天" }, "timewindow": { - "days": "{ days, plural, =1 {# 天 } other {# 天 } }", - "hours": "{ hours, plural, =0 {- 小时 } =1 {# 小时 } other {# 小时 } }", - "minutes": "{ minutes, plural, =0 {- 分 } =1 {# 分 } other {# 分 } }", - "seconds": "{ seconds, plural, =0 {- 秒 } =1 {# 秒 } other {# 秒 } }", + "timewindow": "时间窗口", + "years": "{ years, plural, =1 {1 年 } other {# 年 } }", + "years-short": "{{ years }}y", + "months": "{ months, plural, =1 {1 月 } other {# 月 } }", + "months-short": "{{ months }}M", + "weeks": "{ weeks, plural, =1 {1 周 } other {# 周 } }", + "weeks-short": "{{ weeks }}w", + "days": "{ days, plural, =1 {1 天 } other {# 天 } }", + "days-short": "{{ days }}d", + "hours": "{ hours, plural, =0 {- 小时 } =1 {1 小时 } other {# 小时 } }", + "hr": "{{ hr }} 时", + "hr-short": "{{ hr }}h", + "minutes": "{ minutes, plural, =0 {- 分 } =1 {1 分 } other {# 分 } }", + "min": "{{ min }} 分", + "min-short": "{{ min }}m", + "seconds": "{ seconds, plural, =0 {- 秒 } =1 {1 秒 } other {# 秒 } }", + "sec": "{{ sec }} 秒", + "sec-short": "{{ sec }}s", + "short": { + "days": "{ days, plural, =1 {1 天 } other {# 天 } }", + "hours": "{ hours, plural, =1 {1 小时 } other {# 小时 } }", + "minutes": "{{minutes}} 分 ", + "seconds": "{{seconds}} 秒 " + }, "realtime": "实时", "history": "历史", "last-prefix": "最后", "period": "从 {{ startTime }} 到 {{ endTime }}", "edit": "编辑时间窗口", "date-range": "日期范围", + "for-all-time": "所有时间", "last": "最后", "time-period": "时间段", "hide": "隐藏", - "interval": "区间" + "interval": "区间", + "just-now": "刚刚", + "just-now-lower": "刚刚", + "ago": "之前", + "style": "时间窗口样式", + "icon": "图标", + "icon-position": "图标位置", + "icon-position-left": "左侧", + "icon-position-right": "右侧", + "font": "字体", + "color": "颜色", + "displayTypePrefix": "显示实时/历史前缀", + "preview": "预览" + }, + "unit": { + "millimeter": "mm", + "centimeter": "cm", + "angstrom": "Å", + "nanometer": "nm", + "micrometer": "μm", + "meter": "m", + "kilometer": "km", + "inch": "in", + "foot": "ft", + "yard": "yd", + "mile": "mi", + "nautical-mile": "nmi", + "astronomical-unit": "au", + "reciprocal-metre": "m⁻¹", + "meter-per-meter": "m/m", + "steradian": "sr", + "thou": "th", + "barleycorn": "bc", + "hand": "hd", + "chain": "ch", + "furlong": "fur", + "league": "lea", + "fathom": "fath", + "cable": "cb", + "link": "li", + "rod": "rd", + "nanogram": "ng", + "microgram": "μg", + "milligram": "mg", + "gram": "g", + "kilogram": "kg", + "tonne": "t", + "ounce": "oz", + "pound": "lb", + "stone": "st", + "hundredweight-count": "cwt", + "short-tons": "s.t.", + "dalton": "Da", + "grain": "gr", + "drachm": "dr", + "quarter": "qr", + "slug": "slug", + "carat": "ct", + "cubic-millimeter": "mm³", + "cubic-centimeter": "cm³", + "cubic-meter": "m³", + "cubic-kilometer": "km³", + "microliter": "μL", + "milliliter": "mL", + "liter": "L", + "hectoliter": "hL", + "cubic-inch": "in³", + "cubic-foot": "ft³", + "cubic-yard": "yd³", + "fluid-ounce": "fl oz", + "pint": "pt", + "quart": "qt", + "gallon": "gal", + "oil-barrels": "bbl", + "cubic-meter-per-kilogram": "m³/kg", + "gill": "gi", + "hogshead": "hhd", + "teaspoon": "tsp", + "tablespoon": "tbsp", + "cup": "cup", + "celsius": "°C", + "kelvin": "K", + "rankine": "°R", + "fahrenheit": "°F", + "percent": "%", + "meter-per-second": "m/s", + "kilometer-per-hour": "km/h", + "foot-per-second": "ft/s", + "mile-per-hour": "mph", + "knot": "kn", + "millimeters-per-minute": "mm/min", + "kilometer-per-hour-squared": "km/h²", + "foot-per-second-squared": "ft/s²", + "pascal": "Pa", + "kilopascal": "kPa", + "megapascal": "MPa", + "gigapascal": "GPa", + "millibar": "mbar", + "bar": "bar", + "kilobar": "kbar", + "newton": "N", + "newton-meter": "N·m", + "foot-pounds": "ft·lbf", + "inch-pounds": "in·lbf", + "newton-per-meter": "N/m", + "atmospheres": "atm", + "pounds-per-square-inch": "psi", + "torr": "Torr", + "inches-of-mercury": "inHg", + "pascal-per-square-meter": "Pa/m²", + "pound-per-square-inch": "psi", + "newton-per-square-meter": "N/m²", + "kilogram-force-per-square-meter": "kgf/m²", + "pascal-per-square-centimeter": "Pa/cm²", + "ton-force-per-square-inch": "tonf/in²", + "kilonewton-per-square-meter": "kN/m²", + "newton-per-square-millimeter": "N/mm²", + "microjoule": "μJ", + "millijoule": "mJ", + "joule": "J", + "kilojoule": "kJ", + "megajoule": "MJ", + "gigajoule": "GJ", + "watt-hour": "Wh", + "kilowatt-hour": "kWh", + "electron-volts": "eV", + "joules-per-coulomb": "J/C", + "british-thermal-unit": "BTU", + "foot-pound": "ft·lb", + "calorie": "cal", + "small-calorie": "cal", + "kilocalorie": "kcal", + "joule-per-kelvin": "J/K", + "joule-per-kilogram-kelvin": "J/(kg·K)", + "joule-per-kilogram": "J/kg", + "watt-per-meter-kelvin": "W/(m·K)", + "joule-per-cubic-meter": "J/m³", + "therm": "thm", + "electric-dipole-moment": "Debye", + "magnetic-dipole-moment": "Am²", + "debye": "D", + "coulomb-per-square-meter-per-volt": "C/(m²·V)", + "milliwatt": "mW", + "microwatt": "μW", + "watt": "W", + "kilowatt": "kW", + "megawatt": "MW", + "gigawatt": "GW", + "metric-horsepower": "PS", + "milliwatt-per-square-centimeter": "mW/cm²", + "watt-per-square-centimeter": "W/cm²", + "kilowatt-per-square-centimeter": "kW/cm²", + "milliwatt-per-square-meter": "mW/m²", + "watt-per-square-meter": "W/m²", + "kilowatt-per-square-meter": "kW/m²", + "watt-per-square-inch": "W/in²", + "kilowatt-per-square-inch": "kW/in²", + "horsepower": "hp", + "btu-per-hour": "BTU/h", + "coulomb": "C", + "millicoulomb": "mC", + "microcoulomb": "μC", + "picocoulomb": "pC", + "coulomb-per-meter": "C/m", + "coulomb-per-cubic-meter": "C/m³", + "coulomb-per-square-meter": "C/m²", + "square-millimeter": "mm²", + "square-centimeter": "cm²", + "square-meter": "m²", + "hectare": "ha", + "square-kilometer": "km²", + "square-inch": "in²", + "square-foot": "ft²", + "square-yard": "yd²", + "acre": "ac", + "square-mile": "mi²", + "are": "a", + "barn": "b", + "circular-inch": "c in²", + "milliampere-hour": "mAh", + "ampere-hours": "Ah", + "kiloampere-hours": "kAh", + "nanoampere": "nA", + "picoampere": "pA", + "microampere": "μA", + "milliampere": "mA", + "ampere": "A", + "kiloamperes": "kA", + "microampere-per-square-centimeter": "μA/cm²", + "ampere-per-square-meter": "A/m²", + "ampere-per-meter": "A/m", + "oersted": "Oe", + "bohr-magneton": "μB", + "ampere-meter-squared": "A·m²", + "ampere-meter": "A·m", + "nanovolt": "nV", + "picovolt": "pV", + "millivolts": "mV", + "microvolts": "μV", + "volt": "V", + "kilovolts": "kV", + "dbmV": "dBmV", + "dbm": "dBm", + "volt-meter": "V·m", + "kilovolt-meter": "kV·m", + "megavolt-meter": "MV·m", + "microvolt-meter": "μV·m", + "millivolt-meter": "mV·m", + "nanovolt-meter": "nV·m", + "ohm": "Ω", + "microohm": "μΩ", + "milliohm": "mΩ", + "kilohm": "kΩ", + "megohm": "MΩ", + "gigohm": "GΩ", + "hertz": "Hz", + "kilohertz": "kHz", + "megahertz": "MHz", + "gigahertz": "GHz", + "rpm": "rpm", + "candela-per-square-meter": "cd/m²", + "candela": "cd", + "lumen": "lm", + "lux": "lx", + "foot-candle": "fc", + "lumen-per-square-meter": "lm/m²", + "lux-second": "lx·s", + "lumen-second": "lm·s", + "lumens-per-watt": "lm/W", + "absorbance": "AU", + "mole": "mol", + "nanomole": "nmol", + "micromole": "μmol", + "millimole": "mmol", + "kilomole": "kmol", + "mole-per-cubic-meter": "mol/m³", + "rssi": "RSSI", + "ppm": "ppm", + "ppb": "ppb", + "micrograms-per-cubic-meter": "μg/m³", + "aqi": "AQI", + "gram-per-cubic-meter": "g/m³", + "gram-per-kilogram": "g/kg", + "millimeters-per-second": "mm/s", + "neper": "Np", + "bel": "B", + "decibel": "dB", + "meters-per-second-squared": "m/s²", + "becquerel": "Bq", + "curie": "Ci", + "gray": "Gy", + "sievert": "Sv", + "roentgen": "R", + "cps": "cps", + "rad": "rad", + "rem": "rem", + "dps": "dps", + "rutherford": "Rd", + "coulombs-per-kilogram": "C/kg", + "becquerels-per-cubic-meter": "Bq/m³", + "curies-per-liter": "Ci/L", + "becquerels-per-second": "Bq/s", + "curies-per-second": "Ci/s", + "gy-per-second": "Gy/s", + "watt-per-steradian": "W/sr", + "watt-per-square-metre-steradian": "W/(m²·sr)", + "ph-level": "pH", + "turbidity": "NTU", + "mg-per-liter": "mg/L", + "microsiemens-per-centimeter": "μS/cm", + "millisiemens-per-meter": "mS/m", + "siemens-per-meter": "S/m", + "kilogram-per-cubic-meter": "kg/m³", + "gram-per-cubic-centimeter": "g/cm³", + "kilogram-per-square-meter": "kg/m²", + "milligram-per-milliliter": "mg/mL", + "pound-per-cubic-foot": "lb/ft³", + "ounces-per-cubic-inch": "oz/in³", + "tons-per-cubic-yard": "ton/yd³", + "particle-density": "PD", + "kilometers-per-liter": "km/L", + "miles-per-gallon": "mpg", + "liters-per-100-km": "L/100 km", + "gallons-per-mile": "gal/mi", + "liters-per-hour": "L/h", + "gallons-per-hour": "gal/h", + "beats-per-minute": "bpm", + "millimeters-of-mercury": "mmHg", + "milligrams-per-deciliter": "mg/dL", + "g-force": "G", + "kilonewton": "kN", + "kilogram-force": "kgf", + "pound-force": "lbf", + "kilopound-force": "kip", + "dyne": "dyn", + "poundal": "pdl", + "kip": "kip", + "gal": "Gal", + "gravity": "g₀", + "hectopascal": "hPa", + "atmosphere": "atm", + "millibars": "mbar", + "inch-of-mercury": "inHg", + "richter-scale": "Richter", + "second": "s", + "minute": "min", + "hour": "hr", + "day": "day", + "week": "wk", + "month": "mo", + "year": "yr", + "cubic-foot-per-minute": "ft³/min", + "cubic-meters-per-hour": "m³/h", + "cubic-meters-per-second": "m³/s", + "liter-per-second": "L/s", + "liter-per-minute": "L/min", + "gallons-per-minute": "GPM", + "cubic-foot-per-second": "ft³/s", + "milliliters-per-minute": "mL/min", + "bit": "bit", + "byte": "B", + "kilobyte": "KB", + "megabyte": "MB", + "gigabyte": "GB", + "terabyte": "TB", + "petabyte": "PB", + "exabyte": "EB", + "zettabyte": "ZB", + "yottabyte": "YB", + "bit-per-second": "bps", + "kilobit-per-second": "Kbps", + "megabit-per-second": "Mbps", + "gigabit-per-second": "Gbps", + "terabit-per-second": "Tbps", + "byte-per-second": "B/s", + "kilobyte-per-second": "KB/s", + "megabyte-per-second": "MB/s", + "gigabyte-per-second": "GB/s", + "degree": "°", + "radian": "rad", + "gradian": "grad", + "mil": "mil", + "revolution": "rev", + "siemens": "S", + "millisiemens": "mS", + "microsiemens": "μS", + "kilosiemens": "kS", + "megasiemens": "MS", + "gigasiemens": "GS", + "farad": "F", + "millifarad": "mF", + "microfarad": "μF", + "nanofarad": "nF", + "picofarad": "pF", + "kilofarad": "kF", + "megafarad": "MF", + "gigafarad": "GF", + "terfarad": "TF", + "farad-per-meter": "F/m", + "tesla": "T", + "gauss": "G", + "kilogauss": "kG", + "millitesla": "mT", + "microtesla": "μT", + "nanotesla": "nT", + "kilotesla": "kT", + "megatesla": "MT", + "millitesla-square-meters": "mT·m²", + "gamma": "γ", + "lambda": "λ", + "square-meter-per-second": "m²/s", + "square-centimeter-per-second": "cm²/s", + "stoke": "St", + "centistokes": "cSt", + "square-foot-per-second": "ft²/s", + "square-inch-per-second": "in²/s", + "pascal-second": "Pa·s", + "centipoise": "cP", + "poise": "P", + "reynolds": "Re", + "pound-per-foot-hour": "lb/(ft·hr)", + "newton-second-per-square-meter": "N·s/m²", + "dyne-second-per-square-centimeter": "dyn·s/cm²", + "kilogram-per-meter-second": "kg/(m·s)", + "tesla-square-meters": "T·m²", + "maxwell": "Mx", + "tesla-per-meter": "T/m", + "gauss-per-centimeter": "G/cm", + "weber": "Wb", + "microweber": "μWb", + "milliweber": "mWb", + "gauss-square-centimeter": "G·cm²", + "kilogauss-square-centimeter": "kG·cm²", + "henry": "H", + "millihenry": "mH", + "microhenry": "μH", + "nanohenry": "nH", + "henry-per-meter": "H/m", + "tesla-meter-per-ampere": "T·m/A", + "gauss-per-oersted": "G/Oe", + "kilogram-per-mole": "kg/mol", + "gram-per-mole": "g/mol", + "milligram-per-mole": "mg/mol", + "joule-per-mole": "J/mol", + "joule-per-mole-kelvin": "J/mol-K", + "millivolts-per-meter": "mV/m", + "volts-per-meter": "V/m", + "kilovolts-per-meter": "kV/m", + "radian-per-second": "rad/s", + "radian-per-second-squared": "rad/s^2", + "revolutions-per-minute-per-second": "rpm/s", + "revolutions-per-minute-per-second-squared": "rpm/s^2", + "deg-per-second": "deg/s", + "degrees-brix": "°Bx", + "katal": "kat", + "katal-per-cubic-metre": "kat/m^3" }, "user": { "user": "用户", @@ -3376,13 +4611,13 @@ "anonymous": "匿名", "add": "添加用户", "delete": "删除用户", - "add-user-text": "添加新用户", + "add-user-text": "添加用户", "no-users-text": "未找到用户", "user-details": "用户详细信息", "delete-user-title": "确定要删除用户 '{{userEmail}}' 吗?", "delete-user-text": "请注意:确认后,用户和所有相关数据将不可恢复。", - "delete-users-title": "确定要删除 { count, plural, =1 {# 个用户} other {# 个用户} } 吗?", - "delete-users-action-title": "删除 { count, plural, =1 {# 个用户} other {# 个用户} }", + "delete-users-title": "确定要删除 { count, plural, =1 {1 个用户} other {# 个用户} } 吗?", + "delete-users-action-title": "删除 { count, plural, =1 {1 个用户} other {# 个用户} }", "delete-users-text": "请注意:确认后,所有选定的用户将被删除,所有相关数据将不可恢复。", "activation-email-sent-message": "激活电子邮件已成功发送!", "resend-activation": "重新发送激活", @@ -3408,21 +4643,23 @@ "login-as-tenant-admin": "以租户管理员身份登录", "login-as-customer-user": "以客户用户身份登录", "search": "查找用户", - "selected-users": "已选择 { count, plural, =1 {# 个用户} other {# 个用户} }", + "selected-users": "已选择 { count, plural, =1 {1 个用户} other {# 个用户} }", "disable-account": "禁用用户帐户", "enable-account": "启用用户帐户", "enable-account-message": "已成功启用用户帐户!", "disable-account-message": "已成功禁用用户帐户!", - "copyId": "复制用户Id", - "idCopiedMessage": "用户ID已经复制到粘贴板" + "copyId": "复制用户ID", + "idCopiedMessage": "用户ID已复制到剪贴板", + "user-list": "用户列表", + "user-list-required": "用户列表必填。" }, "value": { "type": "值类型", "string": "字符串", "string-value": "字符串值", "string-value-required": "字符串值必填", - "integer": "数字", - "integer-value": "数字值", + "integer": "整数", + "integer-value": "整数值", "integer-value-required": "整数值必填", "invalid-integer-value": "整数值无效", "double": "双精度小数", @@ -3432,7 +4669,7 @@ "boolean-value": "布尔值", "false": "假", "true": "真", - "long": "Long", + "long": "长整数", "json": "JSON", "json-value": "JSON值", "json-value-invalid": "JSON值的格式无效", @@ -3496,7 +4733,7 @@ "created": "{{created}} 创建", "updated": "{{updated}} 更新", "deleted": "{{deleted}} 删除", - "remove-other-entities-confirm-text": "请注意!在还原版本中不存在的当前实体
将被永久 删除。

请输入 remove other entities 进行确认。", + "remove-other-entities-confirm-text": "请注意!在还原版本中不存在的当前实体
将被永久 删除。

请输入 \"remove other entities\" 进行确认。", "auto-commit-to-branch": "自动提交到 {{ branch }} 分支", "default-create-entity-version-name": "{{entityName}} 更新", "sync-strategy-merge-hint": "创建或更新选定的实体,仓库其他实体均不修改。", @@ -3504,18 +4741,27 @@ "device-credentials-conflict": "无法加载外部ID为 {{entityId}} 的设备
因为数据库中已存在相同的凭据。
请考虑禁用还原表单中的 加载凭据 设置。", "missing-referenced-entity": "无法加载外部ID为 {{sourceEntityId}} 的 {{sourceEntityTypeName}}
因为它引用了缺失的 {{targetEntityTypeName}} (ID:{{targetEntityId}}).", "runtime-failed": "失败: {{message}}", - "auto-commit-settings-read-only-hint": "自动提交功能不适用于存储库设置中启用的只读选项。" + "auto-commit-settings-read-only-hint": "在仓库设置中启用只读选项后,'自动提交'功能将无法正常工作。" }, "widget": { "widget-library": "部件库", "widget-bundle": "部件包", - "all-bundles": "All bundles", + "all-bundles": "所有包", "select-widgets-bundle": "选择部件包", + "widgets": "部件", + "all-widgets": "所有部件", + "widget": "部件", + "select-widget": "选择部件", + "no-widgets-matching": "未找到与 '{{entity}}' 匹配的部件。", + "no-widgets": "暂无部件", + "no-widgets-text": "未找到部件", "management": "管理部件", "editor": "部件编辑器", + "confirm-to-exit-editor-html": "You have unsaved widget settings.
Are you sure you want to leave this page?", "widget-type-not-found": "加载部件配置出错。
可能关联的部件已经删除了。", - "widget-type-load-error": "由于以下错误未加载小部件:", + "widget-type-load-error": "由于以下错误未加载部件:", "remove": "删除部件", + "delete": "Delete widget", "edit": "编辑部件", "remove-widget-title": "确定要删除 '{{widgetTitle}}'部件吗?", "remove-widget-text": "确认后,控件和所有相关数据将变得不可恢复。", @@ -3526,61 +4772,90 @@ "rpc": "控件部件", "alarm": "告警部件", "static": "静态部件", + "timeseries-short": "时序", + "latest-short": "最新", + "rpc-short": "控制", + "alarm-short": "告警", + "static-short": "静态", "select-widget-type": "选择窗口部件类型", "missing-widget-title-error": "部件标题必须指定!", "widget-saved": "部件已保存", "unable-to-save-widget-error": "无法保存部件!控件有错误!", "save": "保存部件", "saveAs": "部件另存为", - "save-widget-type-as": "部件类型另存为", - "save-widget-type-as-text": "请输入新的部件标题或选择目标部件包", + "move": "移动部件", + "save-widget-as": "另存部件为", + "save-widget-as-text": "请输入部件标题", "toggle-fullscreen": "切换全屏", "run": "运行部件", + "widget-title": "部件标题", "title": "部件标题", "title-required": "部件标题必填。", + "title-max-length": "标题应少于256个字符", + "system": "系统", "type": "部件类型", "resources": "资源", "resource-url": "JavaScript/CSS URL", - "resource-is-module": "是否模块", + "resource-is-module": "Is module", "remove-resource": "删除资源", "add-resource": "添加资源", "html": "HTML", - "tidy": "Tidy", + "tidy": "整理", "css": "CSS", "settings-schema": "设置模式", "datakey-settings-schema": "数据键设置模式", - "latest-datakey-settings-schema": "最新数据键设置模式", + "latest-datakey-settings-schema": "最新值数据键设置模式", "widget-settings": "部件设置", "description": "描述", + "tags": "标签", "image-preview": "图片预览", "settings-form-selector": "设置表单选择器", "data-key-settings-form-selector": "数据键设置表单选择器", - "latest-data-key-settings-form-selector": "最新数据键设置表单选择器", - "javascript": "Javascript", + "latest-data-key-settings-form-selector": "最新值数据键设置表单选择器", + "all": "全部", + "actual": "实际", + "deprecated": "已弃用", + "has-basic-mode": "具有基本模式", + "basic-mode-form-selector": "基本模式表单选择器", + "basic-mode": "基本", + "advanced-mode": "高级", + "javascript": "JavaScript", "js": "JS", - "remove-widget-type-title": "确定要删除部件类型 '{{widgetName}}'吗?", - "remove-widget-type-text": "确认后,窗口部件类型和所有相关数据将不可恢复。", - "remove-widget-type": "删除部件类型", - "add-widget-type": "添加新的部件类型", - "widget-type-load-failed-error": "无法加载部件类型!", + "delete-widget-title": "确定要删除部件'{{widgetName}}'吗?", + "delete-widget-text": "确认后,部件及其所有相关数据将无法恢复。", + "delete-widgets-title": "确定要删除 { count, plural, =1 {1个部件} other {#个部件} }吗?", + "delete-widgets-text": "请谨慎操作,确认后,所有选定的部件将被删除,并且所有相关数据将无法恢复。", + "delete-widget": "删除部件", "widget-template-load-failed-error": "无法加载部件模板!", - "add": "添加部件", - "undo": "撤消部件更改", - "export": "导出部件", - "no-data": "小部件上没有要显示的数据", - "data-overflow": "部件显示 {{count}}之外{{total}} 实体", - "alarm-data-overflow": "窗口部件显示{{allowedEntities}}(最大允许的)实的警报{{totalEntities}}实体", + "details": "详情", + "widget-details": "部件详情", + "add": "添加", + "add-existing-widget": "添加现有部件", + "add-new-widget": "添加新部件", + "search-widgets": "搜索部件", + "selected-widgets": "已选择 { count, plural, =1 {1 个部件} other {# 个部件} }", + "undo": "撤销", + "export": "导出", + "export-widgets": "导出部件", + "import": "导入部件", + "no-data": "部件上没有要显示的数据", + "data-overflow": "部件显示 {{count}} 条实体中的 {{total}} 条", + "alarm-data-overflow": "部件显示了 {{allowedEntities}}(最大允许)实体中的告警,总共有 {{totalEntities}} 个实体", "search": "搜索部件", "filter": "部件类型过滤", - "loading-widgets": "加载部件..." + "loading-widgets": "加载部件...", + "widget-template-error": "无效的部件 HTML 模板。" }, "widget-action": { - "header-button": "顶部按钮clcik", + "header-button": "部件顶部按钮", "open-dashboard-state": "切换到新仪表板状态", "update-dashboard-state": "更新当前仪表板状态", "open-dashboard": "切换到另一个仪表板", "custom": "自定义动作", - "custom-pretty": "自定义操作(使用HTML模板)", + "custom-pretty": "自定义动作(使用HTML模板)", + "custom-pretty-error-title": "自定义对话框错误", + "custom-pretty-template-error": "无效的自定义对话框模板。", + "custom-pretty-controller-error": "在评估自定义对话框函数时发生了错误。", "mobile-action": "移动端动作", "target-dashboard-state": "目标仪表板状态", "target-dashboard-state-required": "目标仪表板状态必填", @@ -3590,75 +4865,78 @@ "state-display-type": "显示仪表板状态选项", "open-normal": "普通", "open-in-separate-dialog": "在单独的对话框中打开", - "open-in-popover": "在popover打开", + "open-in-popover": "在弹出框中打开", "dialog-title": "对话框标题", "dialog-hide-dashboard-toolbar": "在对话框中隐藏仪表板工具栏", - "dialog-width": "对话框宽度相对视图宽度", - "dialog-height": "对话框高度相对视图高度", - "dialog-size-range-error": "对话框尺寸比值必须是1至100范围内。", - "popover-preferred-placement": "首选弹出位置", - "popover-placement-top": "上", - "popover-placement-topLeft": "左上", - "popover-placement-topRight": "右上", - "popover-placement-right": "右", + "dialog-width": "对话框宽度相对于视口宽度的百分比", + "dialog-height": "对话框高度相对于视口高度的百分比", + "dialog-size-range-error": "对话框的大小百分比值应该介于1到100之间。", + "popover-preferred-placement": "首选的弹出框位置", + "popover-placement-top": "顶部", + "popover-placement-topLeft": "上左", + "popover-placement-topRight": "上右", + "popover-placement-right": "右侧", "popover-placement-rightTop": "右上", "popover-placement-rightBottom": "右下", - "popover-placement-bottom": "下", - "popover-placement-bottomLeft": "左下", - "popover-placement-bottomRight": "右下", - "popover-placement-left": "左", + "popover-placement-bottom": "底部", + "popover-placement-bottomLeft": "下左", + "popover-placement-bottomRight": "下右", + "popover-placement-left": "左侧", "popover-placement-leftTop": "左上", "popover-placement-leftBottom": "左下", - "popover-hide-on-click-outside": "点击隐藏外面的弹出窗口", - "popover-hide-dashboard-toolbar": "隐藏仪表板工具栏", - "popover-width": "浏览器中的弹出宽度(例如100px, 25vw)单位", - "popover-height": "浏览器中的弹出高度(例如:100px,25vh)单位", - "popover-style": "弹出样式", + "popover-hide-on-click-outside": "在点击弹出框外部时隐藏弹出框", + "popover-hide-dashboard-toolbar": "在弹出框中隐藏仪表板工具栏", + "popover-width": "弹出框的宽度使用浏览器单位表示(例如:100px、25vw)", + "popover-height": "弹出框的高度使用浏览器单位表示(例如:100px、25vh)", + "popover-style": "弹出框样式", "open-new-browser-tab": "在新的浏览器选项卡中打开", "mobile": { - "action-type": "手机端动作类型", - "action-type-required": "手机端动作类型必填。", - "take-picture-from-gallery": "画册拍照", + "action-type": "移动端动作类型", + "action-type-required": "移动端动作类型必填。", + "take-picture-from-gallery": "从图库中获取照片", "take-photo": "拍照", - "map-direction": "打开地图说明", + "map-direction": "打开地图导航", "map-location": "打开地图位置", "scan-qr-code": "扫描二维码", "make-phone-call": "拨打电话", - "get-location": "获得位置", - "take-screenshot": "截图" + "get-location": "获取手机位置", + "take-screenshot": "截屏" } }, "widgets-bundle": { "current": "当前组", "widgets-bundles": "部件包", + "widgets-bundle-widgets": "包部件", "add": "添加部件包", "delete": "删除部件包", "title": "标题", "title-required": "标题必填。", - "title-max-length": "标题长度必须少于256个字符", + "title-max-length": "标题长度应该少于256个字符。", "description": "描述", "image-preview": "图片预览", - "add-widgets-bundle-text": "添加新的部件包", + "order": "其他", + "add-widgets-bundle-text": "添加部件包", "no-widgets-bundles-text": "未找到部件包", "empty": "部件包是空的", "details": "详情", "widgets-bundle-details": "部件包详细信息", "delete-widgets-bundle-title": "确定要删除部件包 '{{widgetsBundleTitle}}'吗?", "delete-widgets-bundle-text": "请注意:确认后,部件包和所有相关数据将不可恢复。", - "delete-widgets-bundles-title": "确定要删除 { count, plural, =1 {# 个部件包} other {# 个部件包} } 吗?", - "delete-widgets-bundles-action-title": "删除 { count, plural, =1 {# 个部件包} other {# 个部件包} }", + "delete-widgets-bundles-title": "确定要删除 { count, plural, =1 {1 个部件包} other {# 个部件包} } 吗?", + "delete-widgets-bundles-action-title": "删除 { count, plural, =1 {1 个部件包} other {# 个部件包} }", "delete-widgets-bundles-text": "请注意:确认后,所有选定的部件包将被删除,所有相关数据将不可恢复。", "no-widgets-bundles-matching": "未找到与 '{{widgetsBundle}}' 匹配的部件包。", "widgets-bundle-required": "部件包必填。", "system": "系统", "import": "导入部件包", "export": "导出部件包", + "export-widgets-bundle-widgets-prompt": "在导出的数据中包含包部件(否则只会导出引用的部件完全限定名)", "export-failed-error": "无法导出部件包: {{error}}", - "create-new-widgets-bundle": "创建新的部件包", + "create-new-widgets-bundle": "创建部件包", "widgets-bundle-file": "部件包文件", "invalid-widgets-bundle-file-error": "无法导入部件包:无效的部件包数据结构。", "search": "查找部件包", - "selected-widgets-bundles": "已选择 { count, plural, =1 {# 个部件包} other {# 个部件包} }", + "selected-widgets-bundles": "已选择 { count, plural, =1 {1 个部件包} other {# 个部件包} }", "open-widgets-bundle": "打开部件包", "loading-widgets-bundles": "加载部件包..." }, @@ -3666,39 +4944,53 @@ "data": "数据", "settings": "设置", "advanced": "高级", + "appearance": "外观", + "widget-card": "部件卡片", + "mobile": "移动设备", "title": "标题", - "title-tooltip": "标题提示框", - "general-settings": "基础设置", + "title-tooltip": "标题文字提示", + "general-settings": "常规设置", "display-title": "显示标题", - "drop-shadow": "阴影", + "card-title": "卡片标题", + "drop-shadow": "投影", "enable-fullscreen": "启用全屏", "background-color": "背景颜色", "text-color": "文字颜色", - "padding": "内边距", - "margin": "外边距", - "widget-style": "部件风格", + "border-radius": "边框半径", + "padding": "填充", + "margin": "边缘", + "widget-style": "部件样式", "widget-css": "部件CSS", - "title-style": "标题风格", + "title-style": "标题样式", "mobile-mode-settings": "移动端设置", "order": "顺序", "height": "高度", - "mobile-hide": "移动端隐藏部件", + "mobile-hide": "在移动端隐藏部件", + "desktop-hide": "在桌面端隐藏部件", "units": "特殊符号展示值", - "decimals": "浮点数后的位数", + "units-by-default": "默认单位", + "decimals": "小数位数", + "decimals-by-default": "默认小数位数", + "default-data-key-parameter-hint": "该参数适用于所有部件值,除非被数据键配置覆盖", + "units-short": "单位", + "decimals-short": "小数", + "decimals-suffix": "位小数", "timewindow": "时间窗口", - "use-dashboard-timewindow": "使用仪表板的时间窗口", + "use-dashboard-timewindow": "使用仪表板时间窗口", + "use-widget-timewindow": "使用部件时间窗口", "display-timewindow": "显示时间窗口", "legend": "图例", "display-legend": "显示图例", "datasources": "数据源", - "maximum-datasources": "最大允许{ count, plural, =1 {1个数据源。} other {#个数据源。} }", - "timeseries-key-error": "应至少指定一个时间序列数据密钥", + "datasource": "数据源", + "maximum-datasources": "最大允许 { count, plural, =1 {1 个数据源。} other {# 个数据源。} }", + "timeseries-key-error": "需要至少指定一个 timeseries 数据键", "datasource-type": "类型", "datasource-parameters": "参数", "remove-datasource": "移除数据源", "add-datasource": "添加数据源", "target-device": "目标设备", - "alarm-source": "警报源", + "alarm-source": "告警源", "actions": "动作", "action": "动作", "add-action": "添加动作", @@ -3710,149 +5002,243 @@ "action-name-required": "动作名称必填。", "action-name-not-unique": "动作名称已经存在。
相同动作源的动作名称必须唯一。", "action-icon": "图标", - "show-hide-action-using-function": "使用函数显示/隐藏操作", + "show-hide-action-using-function": "使用函数显示/隐藏动作", "action-type": "类型", "action-type-required": "类型必填", "edit-action": "编辑动作", - "delete-action": "删除删除", + "delete-action": "删除动作", "delete-action-title": "删除部件动作", - "delete-action-text": "确定要删除部件动作'{{actionName}}'吗?", + "delete-action-text": "确定要删除部件动作'{{actionName}}' 吗?", "title-icon": "标题图标", "display-icon": "显示标题图标", + "card-icon": "卡片图标", + "icon": "图标", "icon-color": "图标颜色", "icon-size": "图标大小", "advanced-settings": "高级设置", "data-settings": "数据设置", - "no-data-display-message": "\"无数据显示\" 替代消息", + "limits": "限制", + "no-data-display-message": "\"没有数据可显示\" 替代信息", "data-page-size": "每个数据源的最大实体数", - "settings-component-not-found": "找不到选择器'{{selector}}'的每个数据源设置窗体组件的最大实体数" + "settings-component-not-found": "未找到设置的表单组件选择器 '{{selector}}'", + "preview": "预览", + "set": "设置", + "set-message": "设置消息", + "advanced-title-style": "高级标题样式", + "card-style": "卡片样式", + "text": "文本", + "background": "背景", + "advanced-widget-style": "高级部件样式", + "card-buttons": "卡片按钮", + "show-card-buttons": "显示卡片按钮", + "card-border-radius": "卡片边框半径", + "card-appearance": "卡片外观", + "color": "颜色", + "tooltip": "文字提示", + "units-required": "单位是必须的。" }, "widget-type": { "import": "导入部件类型", "export": "导出部件类型", "export-failed-error": "无法导出部件类型: {{error}}", - "create-new-widget-type": "创建新的部件类型", - "widget-type-file": "部件类型文件", - "invalid-widget-type-file-error": "无法导入部件类型:无效的部件类型数据结构。" + "widget-file": "部件文件", + "invalid-widget-file-error": "无法导入部件:无效的部件数据结构。" }, "widgets": { + "background": { + "background": "背景", + "background-settings": "背景设置", + "background-type-image": "上传图像", + "background-type-image-url": "图像URL", + "background-type-color": "纯色背景", + "image-url": "图像URL", + "overlay": "覆盖", + "enable-overlay": "启用覆盖", + "blur": "模糊", + "preview": "预览" + }, + "battery-level": { + "layout": "布局", + "layout-vertical-solid": "垂直. 实心", + "layout-horizontal-solid": "水平. 实心", + "layout-vertical-divided": "垂直. 分割", + "layout-horizontal-divided": "水平. 分割", + "icon": "图标", + "value": "数值", + "auto-scale": "自动缩放", + "battery-level-color": "电池电量颜色", + "battery-shape-color": "电池形状颜色", + "battery-level-card-style": "电池电量卡片样式", + "sections-count": "分段数量" + }, + "signal-strength": { + "value": "数值", + "last-update": "上次更新", + "no-signal": "无信号", + "layout": "布局", + "layout-wifi": "Wi-Fi", + "layout-cellular-bar": "蜂窝网络条状", + "icon": "图标", + "date": "日期", + "active-bars-color": "活动信号条颜色", + "inactive-bars-color": "非活动信号条颜色", + "tooltip": "文字提示", + "background-color": "背景颜色", + "background-blur": "背景模糊", + "signal-strength-card-style": "信号强度卡片样式" + }, "chart": { "common-settings": "通用设置", "enable-stacking-mode": "启用堆叠模式", + "selection": "时间范围选择", + "enable-selection-mode": "启用选择模式", "line-shadow-size": "线条阴影大小", - "display-smooth-lines": "显示光滑(弯曲)线条", - "default-bar-width": "默认条形宽度不聚合数据(毫秒)", - "bar-alignment": "条形对齐", + "display-smooth-lines": "显示平滑(曲线)线条", + "default-bar-width": "非聚合数据的默认条形图宽度(毫秒)", + "bar-alignment": "Bar 对齐方式", "bar-alignment-left": "左", "bar-alignment-right": "右", - "bar-alignment-center": "中", + "bar-alignment-center": "居中", + "default-font": "默认字体", "default-font-size": "默认字体大小", "default-font-color": "默认字体颜色", - "thresholds-line-width": "所有线条默认宽度", - "tooltip-settings": "提示栏设置", - "show-tooltip": "显示提示栏", - "hover-individual-points": "坐标悬停显示", - "show-cumulative-values": "在堆叠模式下显示累加数据", - "hide-zero-false-values": "隐藏提示栏中0或false值", - "tooltip-value-format-function": "提示栏格式化函数", - "grid-settings": "网格设置", - "show-vertical-lines": "显示垂直参考线", - "show-horizontal-lines": "显示水平参考线", - "grid-outline-border-width": "网格边框宽度(像系)", - "primary-color": "边框颜色", + "thresholds-line-width": "所有阈值的默认线条宽度", + "tooltip-settings": "文字提示设置", + "tooltip": "文字提示", + "show-tooltip": "显示文字提示", + "hover-individual-points": "悬停在单个数据点上", + "show-cumulative-values": "在堆叠模式下显示累积值", + "hide-zero-false-values": "文字提示隐藏 zero/false", + "tooltip-value-format-function": "文字提示值的格式化函数", + "grid-settings": "网格线设置", + "show-vertical-lines": "显示垂直线条", + "show-horizontal-lines": "显示水平线条", + "grid-outline-border-width": "网格轮廓/边框宽度(px)", + "primary-color": "主要颜色", "background-color": "背景颜色", - "ticks-color": "参考线颜色", + "ticks-color": "刻度颜色", "xaxis-settings": "X轴设置", - "axis-title": "标题", - "xaxis-tick-labels-settings": "标签设置", - "show-tick-labels": "显示标签", - "yaxis-settings": "y轴设置", - "min-scale-value": "最小值", - "max-scale-value": "最大值", - "yaxis-tick-labels-settings": "标签设置", - "tick-step-size": "步长", - "number-of-decimals": "小数位数", + "axis-title": "轴标题", + "xaxis-tick-labels-settings": "X轴刻度标签设置", + "show-tick-labels": "显示轴刻度标签", + "yaxis-settings": "Y轴设置", + "min-scale-value": "刻度上的最小值", + "max-scale-value": "刻度上的最大值", + "yaxis-tick-labels-settings": "Y轴刻度标签设置", + "tick-step-size": "刻度之间的步长", + "number-of-decimals": "要显示的小数位数", "ticks-formatter-function": "刻度格式化函数", "comparison-settings": "比较设置", "enable-comparison": "启用比较", - "time-for-comparison": "比较时期", - "time-for-comparison-previous-interval": "历史时间间隔(默认)", + "time-for-comparison": "比较期间", + "time-for-comparison-previous-interval": "上一个时间段 (默认)", "time-for-comparison-days": "一天前", "time-for-comparison-weeks": "一周前", "time-for-comparison-months": "一月前", "time-for-comparison-years": "一年前", - "time-for-comparison-custom-interval": "自定义间隔", - "custom-interval-value": "自定义间隔值(毫秒)", - "comparison-x-axis-settings": "比较x轴设置", - "axis-position": "位置", - "axis-position-top": "上面(默认)", - "axis-position-bottom": "下面", + "time-for-comparison-custom-interval": "自定义时间间隔", + "custom-interval-value": "自定义时间间隔值 (毫秒)", + "comparison-x-axis-settings": "比较X轴设置", + "axis-position": "轴位置", + "axis-position-top": "顶部 (默认)", + "axis-position-bottom": "底部", "custom-legend-settings": "自定义图例设置", - "enable-custom-legend": "启用自定义图例(你可以使用属性/时间序列标签)", + "enable-custom-legend": "启用自定义图例 (这将允许您在键标签中使用属性/Timeseries值)", "key-name": "键名", - "key-name-required": "键名必填", + "key-name-required": "键名是必需的", "key-type": "键类型", "key-type-attribute": "属性", - "key-type-timeseries": "时间序列", - "label-keys-list": "在标签中使用键列表", - "no-label-keys": "没有配置键", - "add-label-key": "添加键", - "line-width": "线条宽度", + "key-type-timeseries": "Timeseries", + "label-keys-list": "要在标签中使用的键列表", + "no-label-keys": "未配置键", + "add-label-key": "添加新键", + "line-width": "线宽", "color": "颜色", - "data-is-hidden-by-default": "默认隐藏数据", - "disable-data-hiding": "禁用用数据隐藏", + "data-is-hidden-by-default": "默认情况下隐藏数据", + "disable-data-hiding": "禁用数据隐藏", "remove-from-legend": "从图例中移除数据键", - "exclude-from-stacking": "从堆叠模式排除", - "line-settings": "线条设置", - "show-line": "显示线条", - "fill-line": "线条填充", + "exclude-from-stacking": "从堆叠中排除(仅适用于'堆叠'模式)", + "line-settings": "线设置", + "show-line": "显示线", + "fill-line": "填充线", + "fill-line-opacity": "填充透明度", "points-settings": "点设置", "show-points": "显示点", - "points-line-width": "宽度", - "points-radius": "圆角", - "point-shape": "形状", - "point-shape-circle": "圆形", - "point-shape-cross": "十字形", + "points-line-width": "点的线宽", + "points-radius": "点的半径", + "point-shape": "点形状", + "point-shape-circle": "圆", + "point-shape-cross": "十字", "point-shape-diamond": "菱形", "point-shape-square": "矩形", "point-shape-triangle": "三角形", - "point-shape-custom": "自定义", - "point-shape-draw-function": "绘制函数", - "show-separate-axis": "显示独立轴", - "axis-position-left": "左", - "axis-position-right": "右", + "point-shape-custom": "自定义函数", + "point-shape-draw-function": "点形状绘制函数", + "show-separate-axis": "显示分离的轴", + "axis-position-left": "左侧", + "axis-position-right": "右侧", "thresholds": "阈值", - "no-thresholds": "没有配置阈值", - "add-threshold": "添加阈值", - "show-values-for-comparison": "显示比较的历史值", - "comparison-values-label": "历史数什标签", + "no-thresholds": "未配置阈值", + "add-threshold": "添加新阈值", + "show-values-for-comparison": "显示历史比较值", + "comparison-values-label": "历史值标签", + "comparison-line-color": "对比线颜色", "threshold-settings": "阈值设置", - "use-as-threshold": "将值作为阈值", - "threshold-line-width": "阈值线条宽度", + "use-as-threshold": "使用键值作为阈值", + "threshold-line-width": "阈值线宽", "threshold-color": "阈值颜色", - "common-pie-settings": "饼图设置", - "radius": "圆角", - "inner-radius": "内圆角", + "common-pie-settings": "常用饼图设置", + "radius": "半径", + "inner-radius": "内部半径", "tilt": "倾斜", - "stroke-settings": "斜线设置", - "width-pixels": "宽度(像素)", + "common-pie-settings-range-error": "值应在0到1的范围内", + "stroke-settings": "描边设置", + "width-pixels": "宽度 (px)", "show-labels": "显示标签", "animation-settings": "动画设置", - "animated-pie": "启用动画", + "animated-pie": "启用饼图动画 (实验性)", "border-settings": "边框设置", "border-width": "边框宽度", "border-color": "边框颜色", "legend-settings": "图例设置", "display-legend": "显示图例", - "labels-font-color": "标签字体颜色" + "labels-font-color": "标签字体颜色", + "series": "系列", + "add-series": "添加系列", + "series-settings": "系列设置", + "remove-series": "删除系列", + "no-series": "未配置系列", + "no-series-error": "至少应指定一个系列", + "chart-appearance": "图表外观", + "vertical-grid-lines": "垂直网格线", + "horizontal-grid-lines": "水平网格线", + "chart-background": "图表背景", + "grid-lines-color": "网格线颜色", + "border": "边框", + "axis": "轴", + "vertical-axis": "垂直轴", + "ticks": "刻度", + "horizontal-axis": "水平轴" + }, + "color": { + "color-settings": "颜色设置", + "color-type-constant": "固定值", + "color-type-range": "数值范围", + "color-type-function": "函数", + "color": "颜色", + "value-range": "数值范围", + "from": "从", + "to": "至", + "color-function": "颜色函数", + "copy-color-settings-from": "从其他处复制颜色设置" }, "dashboard-state": { "dashboard-state-settings": "仪表板状态设置", - "dashboard-state": "仪表板状态ID", - "autofill-state-layout": "自动填充状态布局高度", - "default-margin": "部件默认外边距", - "default-background-color": "默认背景色", - "sync-parent-state-params": "同步父仪表板状态参数" + "dashboard-state": "仪表板状态 ID", + "autofill-state-layout": "自动填充状态布局默认高度", + "default-margin": "默认部件边距", + "default-background-color": "默认背景颜色", + "sync-parent-state-params": "与父仪表板同步状态参数" }, "date-range-navigator": { "date-range-picker-settings": "日期范围选择设置", @@ -3872,8 +5258,8 @@ "interval-three-months": "3个月", "interval-six-months": "6个月", "step-settings": "步长设置", - "hide-step-size": "步长设置", - "initial-step-size": "初始步长大小", + "hide-step-size": "隐藏步长", + "initial-step-size": "初始步长", "hide-labels": "隐藏标签", "use-session-storage": "使用会话存储", "localizationMap": { @@ -3929,9 +5315,32 @@ "Custom interval": "自定义间隔", "Interval": "间隔", "Step size": "步长", - "Ok": "确定" + "Ok": "Ok" } }, + "doughnut": { + "total": "总计", + "layout": "布局", + "layout-default": "默认", + "layout-with-total": "带总计", + "auto-scale": "自动缩放", + "clockwise-layout": "顺时针布局", + "sort-series": "按标签对系列排序", + "central-total-value": "中央总计值", + "legend-position-top": "顶部", + "legend-position-bottom": "底部", + "legend-position-left": "左侧", + "legend-position-right": "右侧", + "legend-label": "标签", + "legend-value": "值", + "tooltip": "文字提示", + "tooltip-value": "值", + "tooltip-value-type-absolute": "绝对值", + "tooltip-value-type-percentage": "百分比", + "tooltip-background-color": "背景颜色", + "tooltip-background-blur": "背景模糊", + "doughnut-card-style": "圆环样式" + }, "entities-hierarchy": { "hierarchy-data-settings": "层次数据设置", "relations-query-function": "关系查询函数", @@ -3950,9 +5359,9 @@ }, "gateway": { "general-settings": "基础设置", - "widget-title": "部件村题", - "default-archive-file-name": "默认文件名称", - "device-type-for-new-gateway": "网关设备类型", + "widget-title": "部件标题", + "default-archive-file-name": "默认文件名", + "device-type-for-new-gateway": "新网关的设备类型", "messages-settings": "消息设置", "save-config-success-message": "配置保存成功消息", "device-name-exists-message": "设备名称已经存在消消息", @@ -3960,126 +5369,159 @@ "read-only": "只读", "events-title": "事件标题", "events-filter": "事件过滤", - "event-key-contains": "包含事件key..." + "event-key-contains": "事件键包含...", + "show-connector": "显示连接器", + "connector-state-param-key": "连接器状态参数键", + "status": "状态", + "message": "消息", + "created-time": "创建时间" }, "gauge": { "default-color": "默认颜色", - "radial-gauge-settings": "仪表盘设置", + "radial-gauge-settings": "径向量规设置", "ticks-settings": "刻度设置", "min-value": "最小值", "max-value": "最大值", - "start-ticks-angle": "起始角度", - "ticks-angle": "结束角度", - "major-ticks-count": "主要刻度数量", - "major-ticks-color": "主要刻度颜色", - "minor-ticks-count": "次要刻度数量", - "minor-ticks-color": "次要刻度颜色", - "tick-numbers-font": "字体", - "unit-title-settings": "标题设置", - "show-unit-title": "显示标题", - "unit-title": "单位", - "title-font": "字体", + "min-value-short": "最小值", + "max-value-short": "最大值", + "start-ticks-angle": "刻度起始角度", + "ticks-angle": "刻度角度", + "major-ticks": "主刻度", + "major-ticks-count": "主刻度数量", + "major-ticks-color": "主刻度颜色", + "minor-ticks": "次刻度", + "minor-ticks-count": "次刻度数量", + "minor-ticks-color": "次刻度颜色", + "tick-numbers-font": "刻度数字字体", + "unit-title-settings": "单位标题设置", + "show-unit-title": "显示单位标题", + "unit-title": "单位标题", + "title-font": "标题文字字体", "units-settings": "单位设置", - "units-font": "字体", - "value-box-settings": "数值设置", + "units-font": "单位文字字体", + "value-box-settings": "数值框设置", "show-value-box": "显示数值框", - "value-int": "长度", - "value-font": "字体", - "value-box-rect-stroke-color": "数值框矩形颜色", - "value-box-rect-stroke-color-end": "数值框矩形颜色渐变", - "value-box-background-color": "数值框背景色", + "value-box": "数值框", + "value-int": "整数部位数", + "value-text": "数值文本", + "value-text-shadow": "数值文本阴影", + "value-font": "数值字体", + "rect-stroke-color-start": "矩形边框颜色 - 渐变起始", + "rect-stroke-color-end": "矩形边框颜色 - 渐变结束", + "background-color": "背景颜色", + "shadow-color": "阴影颜色", + "value-box-rect-stroke-color": "数值框矩形描边颜色", + "value-box-rect-stroke-color-end": "数值框矩形描边颜色 - 渐变结束", + "value-box-background-color": "数值框背景颜色", "value-box-shadow-color": "数值框阴影颜色", - "plate-settings": "表盘设置", - "show-plate-border": "显示边框", - "plate-color": "背景色", + "plate-settings": "背景板设置", + "show-plate-border": "显示背景板边框", + "plate-color": "背景板颜色", "needle-settings": "指针设置", - "needle-circle-size": "针圈大小", - "needle-color": "颜色", - "needle-color-end": "渐变色", - "needle-color-shadow-up": "针头颜色", - "needle-color-shadow-down": "阴影颜色", - "highlights-settings": "色块设置", - "highlights-width": "色块宽度", - "highlights": "色块", - "highlight-from": "从", - "highlight-to": "到", + "needle-circle-size": "指针圆圈尺寸", + "needle-color": "指针颜色", + "needle-color-start": "指针颜色 - 渐变起始", + "needle-color-end": "指针颜色 - 渐变结束", + "needle-color-shadow-up": "指针上半部分阴影颜色", + "needle-color-shadow-down": "指针下半部分阴影颜色", + "highlights-settings": "高亮设置", + "highlights-width": "高亮宽度", + "highlights": "高亮", + "highlight-from": "起始值", + "highlight-to": "终止值", "highlight-color": "颜色", - "no-highlights": "没有配置色块", - "add-highlight": "添加色块", + "no-highlights": "未配置高亮", + "add-highlight": "添加高亮", "animation-settings": "动画设置", "enable-animation": "启用动画", - "animation-duration": "动画时长", - "animation-rule": "动画类型", - "animation-linear": "线形", - "animation-quad": "方形", - "animation-quint": "五角形", - "animation-cycle": "环形", - "animation-bounce": "循环", - "animation-elastic": "弹跳", - "animation-dequad": "Dequad", - "animation-dequint": "Dequint", - "animation-decycle": "Decycle", - "animation-debounce": "Debounce", - "animation-delastic": "Delastic", - "linear-gauge-settings": "线型设置", - "bar-stroke-width": "量规宽度", - "bar-stroke-color": "量规颜色", - "bar-background-color": "量规背景色", - "bar-background-color-end": "量规背景渐变色", + "animation-duration-rule": "动画持续时间和规则", + "animation-duration": "动画持续时间", + "animation-rule": "动画规则", + "animation-linear": "线性", + "animation-quad": "二次方", + "animation-quint": "五次方", + "animation-cycle": "循环", + "animation-bounce": "弹跳", + "animation-elastic": "弹性", + "animation-dequad": "二次方反向", + "animation-dequint": "五次方反向", + "animation-decycle": "循环反向", + "animation-debounce": "弹跳反向", + "animation-delastic": "弹性反向", + "linear-gauge-settings": "线性量规设置", + "bar-stroke": "柱状图描边", + "bar-stroke-width": "条形图描边宽度", + "bar-stroke-color": "条形图描边颜色", + "bar-background-color": "量规条形图背景颜色", + "bar-background-color-end": "条形图背景颜色 - 渐变结束", "progress-bar-color": "进度条颜色", - "progress-bar-color-end": "进度条渐变色", - "major-ticks-names": "主要刻度名称", - "show-stroke-ticks": "显示主要刻度", - "major-ticks-font": "主要刻度字体", + "progress-bar": "进度条", + "progress-bar-color-start": "进度条颜色 - 渐变起始", + "progress-bar-color-end": "进度条颜色 - 渐变结束", + "major-ticks-names": "主刻度名称", + "show-stroke-ticks": "显示刻度描边", + "major-ticks-font": "主刻度字体", "border-color": "边框颜色", "border-width": "边框宽度", - "needle-circle-color": "指针颜色", - "animation-target": "动画触发", + "needle-circle": "指针圆圈", + "needle-circle-color": "指针圆圈颜色", + "animation-target": "动画目标", "animation-target-needle": "指针", - "animation-target-plate": "表盘", - "common-settings": "通用设置", - "gauge-type": "仪表盘类型", + "animation-target-plate": "背景板", + "common-settings": "通用量规设置", + "gauge-type": "量规类型", "gauge-type-arc": "弧形", - "gauge-type-donut": "环形", - "gauge-type-horizontal-bar": "水平", - "gauge-type-vertical-bar": "垂直", - "donut-start-angle": "角度从", - "bar-settings": "条形设置", - "relative-bar-width": "相对条宽", - "neon-glow-brightness": "霓虹灯效果亮度,(0-100),0-禁用效果", - "stripes-thickness": "条纹的厚度0是没有条纹", - "rounded-line-cap": "显示圆形线盖", - "bar-color-settings": "条颜色设置", - "use-precise-level-color-values": "使用精确的颜色值", - "bar-colors": "从下到上的条形颜色", + "gauge-type-donut": "甜甜圈", + "gauge-type-horizontal-bar": "水平条形", + "gauge-type-vertical-bar": "垂直条形", + "donut-start-angle": "起始角度", + "bar-settings": "条形图设置", + "relative-bar-width": "相对条形宽度", + "neon-glow-brightness": "霓虹灯光效亮度,(0-100),0 - 禁用效果", + "stripes-thickness": "条纹的厚度,0 - 无条纹", + "rounded-line-cap": "显示圆角线帽", + "bar-color-settings": "条形图颜色设置", + "use-precise-level-color-values": "使用精确的颜色级别", + "bar-colors": "条形图颜色,从低到高", "color": "颜色", - "no-bar-colors": "没有配置条形颜色", - "add-bar-color": "添加条形颜色", - "from": "从", - "to": "到", - "fixed-level-colors": "连界值条形颜色", - "gauge-title-settings": "仪表盘标题设置", - "show-gauge-title": "显示仪表盘标题", - "gauge-title": "仪表盘标题", - "gauge-title-font": "仪表盘标题", + "no-bar-colors": "未配置条形图颜色", + "add-bar-color": "添加条形图颜色", + "from": "起始值", + "to": "终止值", + "fixed-level-colors": "使用边界值的条形图颜色", + "gauge-title-settings": "量规标题设置", + "show-gauge-title": "显示量规标题", + "gauge-title": "量规标题", + "gauge-title-font": "量规标题字体", "unit-title-and-timestamp-settings": "单位标题和时间戳设置", "show-timestamp": "显示值时间戳", - "timestamp-format": "时间戳格式化", - "label-font": "标签字体显示值", + "timestamp-format": "时间戳格式", + "label-font": "显示在数值下方的标签字体", "value-settings": "数值设置", - "show-value": "显示文本值", + "show-value": "显示数值文本", "min-max-settings": "最小/最大标签设置", - "show-min-max": "显示最小值和最大值", - "min-max-font": "标签最大和最小字体", + "show-min-max": "显示最小和最大值", + "min-max-font": "最小和最大标签字体", "show-ticks": "显示刻度", "tick-width": "刻度宽度", "tick-color": "刻度颜色", "tick-values": "刻度值", - "no-tick-values": "没有配置刻度值", - "add-tick-value": "添加刻度值" + "no-tick-values": "未配置刻度值", + "add-tick-value": "添加刻度值", + "gauge-appearance": "仪表盘外观", + "units-title": "单位标题", + "value": "数值", + "ticks": "刻度", + "arrow-and-scale-color": "箭头和刻度默认颜色", + "scale-settings": "刻度设置", + "scale": "刻度尺寸", + "scale-color": "刻度颜色", + "compass-appearance": "指南针外观", + "labels": "标签", + "label-style": "标签样式" }, "gpio": { - "pin": "Pin", + "pin": "引脚", "label": "标签", "row": "行", "column": "列", @@ -4087,27 +5529,27 @@ "panel-settings": "面板设置", "background-color": "背景颜色", "gpio-switches": "GPIO开关", - "no-gpio-switches": "没有配置GPIO开关", - "add-gpio-switch": "添加开关", + "no-gpio-switches": "未配置GPIO开关", + "add-gpio-switch": "添加GPIO开关", "gpio-status-request": "GPIO状态请求", - "method-name": "方法名", - "method-body": "方法体", - "gpio-status-change-request": "GPIO状态更改请求", - "parse-gpio-status-function": "GPIO状态解析函数", - "gpio-leds": "led", - "no-gpio-leds": "没有配置led", - "add-gpio-led": "添加LED" + "method-name": "方法名称", + "method-body": "方法内容", + "gpio-status-change-request": "GPIO状态变更请求", + "parse-gpio-status-function": "解析GPIO状态函数", + "gpio-leds": "GPIO LED", + "no-gpio-leds": "未配置GPIO LED", + "add-gpio-led": "添加GPIO LED" }, "html-card": { "html": "HTML", "css": "CSS" }, "input-widgets": { - "attribute-not-allowed": "属性参数不能在此小部件中使用", + "attribute-not-allowed": "属性参数不能在此部件中使用", "blocked-location": "在浏览器中阻止地理位置", "claim-device": "声明设备", "claim-failed": "声明设备失败!", - "claim-not-found": "找不到设备!", + "claim-not-found": "未找到设备!", "claim-successful": "设备已成功申领!", "date": "日期", "device-name": "设备名称", @@ -4115,7 +5557,7 @@ "discard-changes": "放弃更改", "entity-attribute-required": "实体属性必填", "entity-coordinate-required": "纬度和经度两个字段都是必需的", - "entity-timeseries-required": "实体时间序列必填", + "entity-timeseries-required": "实体 Timeseries 必填", "get-location": "获取当前位置", "invalid-date": "无效日期", "latitude": "纬度", @@ -4128,179 +5570,195 @@ "no-coordinate-specified": "未指定纬度/经度的数据键", "no-entity-selected": "未选择实体", "no-image": "没有图像", - "no-support-geolocation": "你的浏览器不支持地理定位", - "no-support-web-camera": "你的浏览器不支持摄像头", + "no-support-geolocation": "您的浏览器不支持地理定位", + "no-support-web-camera": "您的浏览器不支持摄像头", "enable-https-use-widget": "请启用HTTPS以使用此部件", - "no-found-your-camera": "找不到摄像机", + "no-found-your-camera": "未找到摄像机", "no-permission-camera": "权限被用户拒绝/此站点无权使用摄像机", - "no-timeseries-selected": "未选择时间序列", + "no-timeseries-selected": "未选择 Timeseries", "secret-key": "密钥", "secret-key-required": "密钥必填", "switch-attribute-value": "切换实体属性值", "switch-camera": "切换摄像机", - "switch-timeseries-value": "切换实体时间序列值", + "switch-timeseries-value": "切换实体 Timeseries 值", "take-photo": "拍照", "time": "时间", - "timeseries-not-allowed": "时间序列参数不能用于此部件", + "timeseries-not-allowed": "Timeseries 参数不能用于此部件", "update-failed": "更新失败", "update-successful": "更新成功", "update-attribute": "更新属性", - "update-timeseries": "更新时间序列", - "value": "数据值", - "general-settings": "基础设置", + "update-timeseries": "更新 Timeseries", + "value": "数值", + "general-settings": "通用设置", "widget-title": "部件标题", - "claim-button-label": "声称按钮标签", - "show-secret-key-field": "显示“密钥”输入字段", + "claim-button-label": "认领按钮标签", + "show-secret-key-field": "显示'秘钥'输入字段", "labels-settings": "标签设置", "show-labels": "显示标签", - "device-name-label": "设备名称设备名称输入字段的标签输入字段", - "secret-key-label": "设备名称设备密钥输入字段的标签输入字段", - "messages-settings": "信息设置", - "claim-device-success-message": "设备声明成功的文本信息", - "claim-device-not-found-message": "设备声明未找到信息", - "claim-device-failed-message": "设备声明失败的文本信息", - "claim-device-name-required-message": "设备声明“设备名称”必填的错误信息", - "claim-device-secret-key-required-message": "设备声明“设备密钥”必填的错误信息", + "device-name-label": "设备名称输入字段标签", + "secret-key-label": "秘钥输入字段标签", + "messages-settings": "消息设置", + "claim-device-success-message": "设备认领成功文本消息", + "claim-device-not-found-message": "设备未找到文本消息", + "claim-device-failed-message": "设备认领失败文本消息", + "claim-device-name-required-message": "'设备名称必填'错误消息", + "claim-device-secret-key-required-message": "'秘钥必填'错误消息", "show-label": "显示标签", "label": "标签", "required": "必填", - "required-error-message": "错误信息“必填”", - "show-result-message": "显示结果信息", - "integer-field-settings": "Integer字段设置", - "min-value": "最大值", - "max-value": "最小值", - "double-field-settings": "Double字段设置", - "text-field-settings": "Text字段设置", + "required-error-message": "'必填'错误消息", + "show-result-message": "显示结果消息", + "integer-field-settings": "整数字段设置", + "min-value": "最小值", + "max-value": "最大值", + "double-field-settings": "双精度字段设置", + "text-field-settings": "文本字段设置", "min-length": "最小长度", "max-length": "最大长度", - "checkbox-settings": "Checkbox设置", - "true-label": "Checked标签", - "false-label": "Unchecked标签", - "image-input-settings": "Image字段设置", + "checkbox-settings": "复选框设置", + "true-label": "选中标签", + "false-label": "未选中标签", + "image-input-settings": "图像输入设置", "display-preview": "显示预览", "display-clear-button": "显示清除按钮", "display-apply-button": "显示应用按钮", - "display-discard-button": "显示弃用按钮", - "datetime-field-settings": "Date/time字段设置", - "display-time-input": "显示time输入", - "latitude-key-name": "Latitude键名", - "longitude-key-name": "Longitude键名", - "show-get-location-button": "显示“获取当前位置”按钮", + "display-discard-button": "显示放弃按钮", + "datetime-field-settings": "日期/时间字段设置", + "display-time-input": "显示时间输入", + "latitude-key-name": "纬度键名称", + "longitude-key-name": "经度键名称", + "show-get-location-button": "显示'获取当前位置'按钮", "use-high-accuracy": "使用高精度", - "location-fields-settings": "位置设置", - "latitude-label": "latitude标签", - "longitude-label": "longitude标签", - "input-fields-alignment": "对齐方式", + "location-fields-settings": "位置字段设置", + "latitude-label": "纬度标签", + "longitude-label": "经度标签", + "input-fields-alignment": "输入字段对齐方式", "input-fields-alignment-column": "列(默认)", "input-fields-alignment-row": "行", - "latitude-field-required": "Latitude字段必填", - "longitude-field-required": "Longitude字段必填", + "layout": "布局", + "row-gap": "行之间的间距(像素)", + "column-gap": "列之间的间距(像素)", + "latitude-field-required": "纬度字段必填", + "longitude-field-required": "经度字段必填", "attribute-settings": "属性设置", "widget-mode": "部件模式", "widget-mode-update-attribute": "更新属性", - "widget-mode-update-timeseries": "更新时间序列", + "widget-mode-update-timeseries": "更新 Timeseries", "attribute-scope": "属性范围", - "attribute-scope-server": "服务端属性", + "attribute-scope-server": "服务器属性", "attribute-scope-shared": "共享属性", - "value-required": "启用数值必填", - "image-settings": "图片设置", - "image-format": "图片格式", + "value-required": "值必填", + "image-settings": "图像设置", + "image-format": "图像格式", "image-format-jpeg": "JPEG", "image-format-png": "PNG", "image-format-webp": "WEBP", - "image-quality": "图片压缩", - "max-image-width": "最大宽度", - "max-image-height": "最大高度", - "action-buttons": "动作按钮", - "show-action-buttons": "显示动作按钮", - "update-all-values": "更新所有值", - "save-button-label": "“保存”按钮标签", - "reset-button-label": "“撤销”按钮标签", + "image-quality": "使用有损压缩的图像质量,比如JPEG和WEBP", + "max-image-width": "最大图像宽度", + "max-image-height": "最大图像高度", + "action-buttons": "操作按钮", + "show-action-buttons": "显示操作按钮", + "update-all-values": "更新所有值,而不仅仅是修改过的值", + "save-button-label": "'保存'按钮标签", + "reset-button-label": "'撤销'按钮标签", "group-settings": "分组设置", - "show-group-title": "显示分组标题", + "show-group-title": "显示字段组的标题,与不同实体相关联", "group-title": "分组标题", - "fields-alignment": "对齐字段", + "fields-alignment": "字段对齐方式", "fields-alignment-row": "行(默认)", "fields-alignment-column": "列", - "fields-in-row": "行中的字段数", - "option-value": "值(为创建空选项编写“null”)", + "fields-in-row": "每行字段数", + "option-value": "值(创建空选项时填写'null')", "option-label": "标签", "hide-input-field": "隐藏输入字段", - "datakey-type": "Datakey类型", - "datakey-type-server": "服务端属性(默认)", + "datakey-type": "数据键类型", + "datakey-type-server": "服务器属性(默认)", "datakey-type-shared": "共享属性", - "datakey-type-timeseries": "时间序列", - "datakey-value-type": "Datakey值类型", - "datakey-value-type-string": "String", - "datakey-value-type-double": "Double", - "datakey-value-type-integer": "Integer", - "datakey-value-type-boolean-checkbox": "Boolean(多选)", - "datakey-value-type-boolean-switch": "Boolean(开关)", - "datakey-value-type-date-time": "Date & Time", - "datakey-value-type-date": "Date", - "datakey-value-type-time": "Time", - "datakey-value-type-select": "Select", - "value-is-required": "值是必填", + "datakey-type-timeseries": "Timeseries", + "datakey-value-type": "数据键值类型", + "datakey-value-type-string": "字符串", + "datakey-value-type-double": "双精度", + "datakey-value-type-integer": "整数", + "datakey-value-type-json": "JSON", + "datakey-value-type-boolean-checkbox": "布尔值(复选框)", + "datakey-value-type-boolean-switch": "布尔值(开关)", + "datakey-value-type-date-time": "日期和时间", + "datakey-value-type-date": "日期", + "datakey-value-type-time": "时间", + "datakey-value-type-select": "选择", + "datakey-value-type-color": "颜色", + "value-is-required": "值必填", "ability-to-edit-attribute": "编辑属性的能力", "ability-to-edit-attribute-editable": "可编辑(默认)", "ability-to-edit-attribute-disabled": "禁用", "ability-to-edit-attribute-readonly": "只读", - "disable-on-datakey-name": "禁用另一个datakey的错误值(指定datakey名称)", - "slide-toggle-settings": "滑块设置", - "slide-toggle-label-position": "滑动标签位置", - "slide-toggle-label-position-after": "后面", - "slide-toggle-label-position-before": "前面", - "select-options": "下拉选项", - "no-select-options": "没有配置下拉选项", - "add-select-option": "添加下接选项", - "numeric-field-settings": "数字字段设置", - "step-interval": "步长间隔", - "error-messages": "错误信息", - "min-value-error-message": "“最小值”错误信息", - "max-value-error-message": "“最大值”错误信息", - "invalid-date-error-message": "“无效日期”错误消息", + "disable-on-datakey-name": "在另一个数据键的值为假时禁用(指定数据键名称)", + "field-appearance": "字段外观", + "appearance-fill": "填充", + "appearance-outline": "轮廓", + "subscript-sizing": "下标大小", + "subscript-sizing-fixed": "固定", + "subscript-sizing-dynamic": "动态", + "slide-toggle-settings": "滑动开关设置", + "slide-toggle-label-position": "滑动开关标签位置", + "slide-toggle-label-position-after": "之后", + "slide-toggle-label-position-before": "之前", + "select-options": "选择选项", + "no-select-options": "没有配置选择选项", + "add-select-option": "添加选择选项", + "numeric-field-settings": "数值字段设置", + "step-interval": "值之间的步长间隔", + "error-messages": "错误消息", + "min-value-error-message": "'最小值'错误消息", + "max-value-error-message": "'最大值'错误消息", + "invalid-date-error-message": "'无效日期'错误消息", + "invalid-JSON-error-message": "'无效JSON'错误消息", "icon-settings": "图标设置", - "use-custom-icon": "自定义图标", - "input-cell-icon": "输入单元格之前显示的图标", + "dialog-editor-settings": "对话框编辑器设置", + "use-custom-icon": "使用自定义图标", + "input-cell-icon": "输入单元格前显示的图标", "value-conversion-settings": "值转换设置", "get-value-settings": "获取值设置", - "use-get-value-function": "启用获取值函数", - "get-value-function": "获取值函数", + "use-get-value-function": "使用getValue函数", + "get-value-function": "getValue函数", "set-value-settings": "设置值设置", - "use-set-value-function": "启用值值函数", - "set-value-function": "启用函数" + "use-set-value-function": "使用setValue函数", + "set-value-function": "setValue函数", + "json-invalid": "JSON值格式无效", + "title": "标题", + "cancel-button-label": "'取消'按钮标签" }, - "invalid-qr-code-text": "无效的二维码文本。", + "invalid-qr-code-text": "无效的二维码输入文本,输入应为字符串类型。", "qr-code": { - "use-qr-code-text-function": "启用二维码文本函数", - "qr-code-text-pattern": "二维码文本模式(例如:${entityName}|${keyName}一些文本。')", - "qr-code-text-pattern-hint": "二维码文本模式使用实体别名中第一个找到的键的值。", - "qr-code-text-pattern-required": "二维码文本必填。", + "use-qr-code-text-function": "使用二维码文本函数", + "qr-code-text-pattern": "二维码文本模式(例如'${entityName} | ${keyName} - some text.')", + "qr-code-text-pattern-hint": "二维码文本模式将使用实体别名中的实体中找到的第一个键的值。", + "qr-code-text-pattern-required": "必须提供二维码文本模式。", "qr-code-text-function": "二维码文本函数" }, "label-widget": { "label-pattern": "模式", - "label-pattern-hint": "提示文本,如:'${keyName}单位。'或${#<key index>}单位'", - "label-pattern-required": "模式必填", - "label-position": "相对位置", + "label-pattern-hint": "提示:例如 'Text ${keyName} units.' or ${#<key index>} units'", + "label-pattern-required": "必须提供模式", + "label-position": "位置(相对于背景的百分比)", "x-pos": "X", "y-pos": "Y", - "background-color": "背景色", + "background-color": "背景颜色", "font-settings": "字体设置", - "background-image": "背景图片", + "background-image": "背景图像", "labels": "标签", - "no-labels": "没有配置标签", + "no-labels": "无配置的标签", "add-label": "添加标签" }, "navigation": { "title": "标题", "navigation-path": "导航路径", - "filter-type": "过滤类型", - "filter-type-all": "所有内容", - "filter-type-include": "包含内容", - "filter-type-exclude": "排除内容", - "items": "内容", - "enter-urls-to-filter": "输入URL进行过滤..." + "filter-type": "筛选类型", + "filter-type-all": "所有项目", + "filter-type-include": "包含项目", + "filter-type-exclude": "排除项目", + "items": "项目", + "enter-urls-to-filter": "输入要筛选的URL..." }, "persistent-table": { "rpc-id": "RPC ID", @@ -4308,157 +5766,171 @@ "method": "方法", "params": "参数", "created-time": "创建时间", - "expiration-time": "到期时间", - "retries": "重试", + "expiration-time": "过期时间", + "retries": "重试次数", "status": "状态", - "filter": "过滤", + "filter": "筛选", "refresh": "刷新", "add": "添加RPC请求", "details": "详情", "delete": "删除", - "delete-request-title": "删除持续化RPC请求", - "delete-request-text": "您确定要删除请求吗?", - "details-title": "详情RPC ID: ", + "delete-request-title": "删除持久化RPC请求", + "delete-request-text": "确定要删除请求吗?", + "details-title": "详情 RPC ID: ", "additional-info": "附加信息", "response": "响应", "any-status": "任何状态", "rpc-status-list": "RPC状态列表", - "no-request-prompt": "无显示请求", + "no-request-prompt": "没有要显示的请求", "send-request": "发送请求", - "add-title": "创建持续化RPC请求", - "method-error": "方法名必填。", + "add-title": "创建持久化RPC请求", + "method-error": "必须提供方法。", "timeout-error": "最小超时值为5000(5秒)。", - "white-space-error": "不允许使用空白。", + "white-space-error": "不允许使用空格。", "rpc-status": { - "QUEUED": "QUEUED", - "SENT": "SENT", - "DELIVERED": "DELIVERED", - "SUCCESSFUL": "SUCCESSFUL", - "TIMEOUT": "TIMEOUT", - "EXPIRED": "EXPIRED", - "FAILED": "FAILED" + "QUEUED": "已排队", + "SENT": "已发送", + "DELIVERED": "已送达", + "SUCCESSFUL": "成功", + "TIMEOUT": "超时", + "EXPIRED": "已过期", + "FAILED": "失败" }, "rpc-search-status-all": "所有", "message-types": { - "false": "Two-way", - "true": "One-way" + "false": "双向", + "true": "单向" }, - "general-settings": "基础设置", - "enable-filter": "启用过滤", - "enable-sticky-header": "滚动时显示标头", - "enable-sticky-action": "滚动时显示动作列", + "general-settings": "常规设置", + "enable-filter": "启用筛选", + "enable-sticky-header": "滚动时显示标题栏", + "enable-sticky-action": "滚动时显示操作列", "display-request-details": "显示请求详情", "allow-send-request": "允许发送RPC请求", "allow-delete-request": "允许删除请求", - "columns-settings": "设置列", - "display-columns": "显示列", + "columns-settings": "列设置", + "display-columns": "要显示的列", "column": "列", - "no-columns-found": "没有配置列", - "no-columns-matching": "'{{column}}'找不到。" + "no-columns-found": "找不到列", + "no-columns-matching": "未找到 '{{column}}'。" + }, + "range-chart": { + "chart": "图表", + "data-zoom": "数据缩放", + "range-colors": "范围颜色", + "out-of-range-color": "超出范围颜色", + "fill-area": "填充区域", + "legend-label": "标签", + "tooltip": "文字提示", + "tooltip-value": "值", + "tooltip-date": "日期", + "tooltip-background-color": "背景颜色", + "tooltip-background-blur": "背景模糊", + "range-chart-card-style": "范围图表卡片样式" }, "rpc": { - "value-settings": "设置值", + "value-settings": "值设置", "initial-value": "初始值", - "retrieve-value-settings": "恢复开/关设置", - "retrieve-value-method": "恢复方法", - "retrieve-value-method-none": "无需恢复", - "retrieve-value-method-rpc": "RPC方法恢复", - "retrieve-value-method-attribute": "订阅属性", - "retrieve-value-method-timeseries": "订阅时间序列", - "attribute-value-key": "属性key", - "timeseries-value-key": "时序key", - "get-value-method": "RPC获取方法", - "parse-value-function": "解析函数", + "retrieve-value-settings": "获取开/关值设置", + "retrieve-value-method": "使用方法获取值", + "retrieve-value-method-none": "不获取", + "retrieve-value-method-rpc": "调用RPC获取值方法", + "retrieve-value-method-attribute": "订阅属性获取值", + "retrieve-value-method-timeseries": "订阅时间序列获取值", + "attribute-value-key": "属性键", + "timeseries-value-key": "Timeseries键", + "get-value-method": "RPC获取值方法", + "parse-value-function": "解析值的函数", "update-value-settings": "更新值设置", - "set-value-method": "RPC获取方法", - "convert-value-function": "转换函数", + "set-value-method": "RPC设置值方法", + "convert-value-function": "转换值的函数", "rpc-settings": "RPC设置", - "request-timeout": "RPC请求超(毫秒)", + "request-timeout": "RPC请求超时时间(毫秒)", "persistent-rpc-settings": "持久化RPC设置", - "request-persistent": "RPC请求持久化", - "persistent-polling-interval": " RPC持久化轮询间隔(毫秒)", - "common-settings": "通用设置", + "request-persistent": "持久化RPC请求", + "persistent-polling-interval": "持久化RPC命令响应的轮询间隔(毫秒)", + "common-settings": "常见设置", "switch-title": "开关标题", - "show-on-off-labels": "显示开/关", - "slide-toggle-label": "滑块切换标签", + "show-on-off-labels": "显示开/关标签", + "slide-toggle-label": "滑动开关标签", "label-position": "标签位置", - "label-position-before": "前面", - "label-position-after": "后面", + "label-position-before": "之前", + "label-position-after": "之后", "slider-color": "滑块颜色", - "slider-color-primary": "Primary", - "slider-color-accent": "Accent", - "slider-color-warn": "Warn", + "slider-color-primary": "主要", + "slider-color-accent": "强调", + "slider-color-warn": "警告", "button-style": "按钮样式", - "button-raised": "按钮凸起", - "button-primary": "原色", - "button-background-color": "背景色", - "button-text-color": "文字颜色", + "button-raised": "凸起按钮", + "button-primary": "主要颜色", + "button-background-color": "按钮背景颜色", + "button-text-color": "按钮文字颜色", "widget-title": "部件标题", "button-label": "按钮标签", "device-attribute-scope": "设备属性范围", - "server-attribute": "服务端属性", + "server-attribute": "服务器属性", "shared-attribute": "共享属性", "device-attribute-parameters": "设备属性参数", - "is-one-way-command": "是否单向命令", + "is-one-way-command": "是单向命令", "rpc-method": "RPC方法", - "rpc-method-params": "RPC参数", + "rpc-method-params": "RPC方法参数", "show-rpc-error": "显示RPC命令执行错误", "led-title": "LED标题", "led-color": "LED颜色", "check-status-settings": "检查状态设置", "perform-rpc-status-check": "执行RPC设备状态检查", - "retrieve-led-status-value-method": "使用方法状态值恢复LED", - "led-status-value-attribute": "设备属性包含LED状态值", - "led-status-value-timeseries": "设备时间序列包含LED状态值", - "check-status-method": "RPC设备状态检查方法", - "parse-led-status-value-function": "解析函数", - "knob-title": "旋转按钮", + "retrieve-led-status-value-method": "使用方法获取LED状态值", + "led-status-value-attribute": "包含LED状态值的设备属性", + "led-status-value-timeseries": "包含LED状态值的设备时间序列", + "check-status-method": "RPC检查设备状态方法", + "parse-led-status-value-function": "解析LED状态值的函数", + "knob-title": "旋钮标题", "min-value": "最小值", "max-value": "最大值" }, "maps": { "select-entity": "选择实体", - "select-entity-hint": "提示:选择后单击地图以设置位置", + "select-entity-hint": "提示:选择后在地图上点击以设置位置", "tooltips": { - "placeMarker": "单击放置'{{entityName}}'实体", - "firstVertex": "单击放置多边形'{{entityName}}'的第一个点", - "firstVertex-cut": "单击放置第一个点", - "continueLine": "单击继续绘制'{{entityName}}'多边形", - "continueLine-cut": "单击继续绘制", - "finishLine": "单击任何现有标记完成", - "finishPoly": "单击第一个标记完成'{{entityName}}'并保存", - "finishPoly-cut": "单击第一个标记完成并保存", - "finishRect": "单击'{{entityName}}'多边形完成并保存", - "startCircle": "单击'{{entityName}}'放置圆圈中心", - "finishCircle": "单击'{{entityName}}'完成圆圈", - "placeCircleMarker": "单击放置圆形标记" + "placeMarker": "点击以放置 '{{entityName}}' 实体", + "firstVertex": "'{{entityName}}' 的多边形:点击以放置第一个点", + "firstVertex-cut": "点击以放置第一个点", + "continueLine": "'{{entityName}}' 的多边形:点击以继续绘制", + "continueLine-cut": "点击以继续绘制", + "finishLine": "点击任意现有标记以完成", + "finishPoly": "'{{entityName}}' 的多边形:点击第一个标记以完成并保存", + "finishPoly-cut": "点击第一个标记以完成并保存", + "finishRect": "'{{entityName}}' 的多边形:点击以完成并保存", + "startCircle": "'{{entityName}}' 的圆:点击以放置圆心", + "finishCircle": "'{{entityName}}' 的圆:点击以完成圆形", + "placeCircleMarker": "点击以放置圆形标记" }, "actions": { "finish": "完成", "cancel": "取消", - "removeLastVertex": "删除最后一点" + "removeLastVertex": "移除最后一个点" }, "buttonTitles": { "drawMarkerButton": "放置实体", "drawPolyButton": "创建多边形", - "drawLineButton": "创建多边形线条", + "drawLineButton": "创建折线", "drawCircleButton": "创建圆形", "drawRectButton": "创建矩形", "editButton": "编辑模式", "dragButton": "拖放模式", - "cutButton": "剪切多边形区域", + "cutButton": "切割多边形区域", "deleteButton": "移除", - "drawCircleMarkerButton": "创建圆形标志", + "drawCircleMarkerButton": "创建圆形标记", "rotateButton": "旋转多边形" }, - "map-provider-settings": "地图设置", - "map-provider": "提供程序", - "map-provider-google": "Google地图", + "map-provider-settings": "地图提供商设置", + "map-provider": "地图提供商", + "map-provider-google": "谷歌地图", "map-provider-openstreet": "OpenStreet地图", "map-provider-here": "HERE地图", - "map-provider-image": "Image地图", - "map-provider-tencent": "Tencent地图", - "openstreet-provider": "OpenStreet地图提供程序", + "map-provider-image": "图像地图", + "map-provider-tencent": "腾讯地图", + "openstreet-provider": "OpenStreet地图提供商", "openstreet-provider-mapnik": "OpenStreetMap.Mapnik(默认)", "openstreet-provider-hot": "OpenStreetMap.HOT", "openstreet-provider-esri-street": "Esri.WorldStreetMap", @@ -4466,265 +5938,636 @@ "openstreet-provider-esri-imagery": "Esri.WorldImagery", "openstreet-provider-cartodb-positron": "CartoDB.Positron", "openstreet-provider-cartodb-dark-matter": "CartoDB.DarkMatter", - "use-custom-provider": "自定义提供程序", - "custom-provider-tile-url": "提供程序URL", - "google-maps-api-key": "Google地图Key", + "use-custom-provider": "使用自定义提供商", + "custom-provider-tile-url": "自定义提供商瓦片URL", + "google-maps-api-key": "谷歌地图API密钥", "default-map-type": "默认地图类型", - "google-map-type-roadmap": "路线地图", - "google-map-type-satelite": "卫星", - "google-map-type-hybrid": "混合", - "google-map-type-terrain": "街道", - "map-layer": "地图", + "google-map-type-roadmap": "路线图", + "google-map-type-satelite": "卫星图", + "google-map-type-hybrid": "混合图", + "google-map-type-terrain": "地形图", + "map-layer": "地图图层", "here-map-normal-day": "HERE.normalDay(默认)", "here-map-normal-night": "HERE.normalNight", "here-map-hybrid-day": "HERE.hybridDay", "here-map-terrain-day": "HERE.terrainDay", - "credentials": "证书", - "here-app-id": "HERE地图id", - "here-app-code": "HERE地图code", - "tencent-maps-api-key": "Tencent地图Key", - "tencent-map-type-roadmap": "路线地图", - "tencent-map-type-satelite": "卫星", - "tencent-map-type-hybrid": "混合", - "image-map-background": "地图北景", - "image-map-background-from-entity-attribute": "从实体属性中获取背景图", + "credentials": "凭证", + "here-app-id": "HERE应用ID", + "here-app-code": "HERE应用代码", + "here-api-key": "HERE API密钥", + "here-use-new-version-api-3": "使用API版本3", + "tencent-maps-api-key": "腾讯地图API密钥", + "tencent-map-type-roadmap": "路线图", + "tencent-map-type-satelite": "卫星图", + "tencent-map-type-hybrid": "混合图", + "image-map-background": "图像地图背景", + "image-map-background-from-entity-attribute": "从实体属性获取图像地图背景", "image-url-source-entity-alias": "图像URL源实体别名", "image-url-source-entity-attribute": "图像URL源实体属性", - "common-map-settings": "通用设置", + "common-map-settings": "常见地图设置", "x-pos-key-name": "X位置键名", "y-pos-key-name": "Y位置键名", - "latitude-key-name": "Latitude键名", - "longitude-key-name": "Longitude键名", - "default-map-zoom-level": "地图缩放级别(1-20)", - "default-map-center-position": "地图中心位置(0,0)", - "disable-scroll-zooming": "地图中心位置", + "latitude-key-name": "纬度键名", + "longitude-key-name": "经度键名", + "default-map-zoom-level": "默认地图缩放级别(0-20)", + "default-map-center-position": "默认地图中心位置(0,0)", + "disable-scroll-zooming": "禁用滚动缩放", "disable-double-click-zooming": "禁用双击缩放", - "disable-zoom-control-buttons": "禁用缩放按钮", - "fit-map-bounds": "拟合地图标记", - "use-default-map-center-position": "地图默认中心位置", - "entities-limit": "加载实体限制", + "disable-zoom-control-buttons": "禁用缩放控制按钮", + "fit-map-bounds": "适应地图边界以覆盖所有标记", + "use-default-map-center-position": "使用默认地图中心位置", + "entities-limit": "加载实体的限制", "markers-settings": "标记设置", - "marker-offset-x": "标记X宽度偏移量", - "marker-offset-y": "标记Y高度偏移量", - "position-function": "位置转换函数应返回x,y坐标", - "draggable-marker": "拖动标记", - "label": "标签设置", + "marker-offset-x": "标记X偏移相对于位置乘以标记宽度", + "marker-offset-y": "标记Y偏移相对于位置乘以标记高度", + "position-function": "位置转换函数,应返回0到1之间的x,y坐标", + "draggable-marker": "可拖动的标记", + "label": "标签", "show-label": "显示标签", - "use-label-function": "标签函数", - "label-pattern": "标签模式(例如:'${entityName}', '${entityName}: (文本${keyName}单位)' )", + "use-label-function": "使用标签函数", + "label-pattern": "标签 (模式示例:'${entityName}', '${entityName}: (Text ${keyName} units.)' )", "label-function": "标签函数", - "tooltip": "提示栏设置", - "show-tooltip": "显示提示栏", - "show-tooltip-action": "提示拦动作", - "show-tooltip-action-click": "单击显示(默认)", - "show-tooltip-action-hover": "悬停显示", - "auto-close-tooltips": "自动关闭", - "use-tooltip-function": "启用函数", - "tooltip-pattern": "提示栏(例如:'文本${keyName}单位。'或 Link text')", - "tooltip-function": "提示栏函数", - "tooltip-offset-x": "提示栏x标记宽度偏移量", - "tooltip-offset-y": "提示栏y标记高度偏移量", - "color": "颜色设置", - "use-color-function": "启用函数", + "tooltip": "文字提示", + "show-tooltip": "显示文字提示", + "show-tooltip-action": "显示文字提示的操作", + "show-tooltip-action-click": "点击时显示文字提示(默认)", + "show-tooltip-action-hover": "悬停时显示文字提示", + "auto-close-tooltips": "自动关闭文字提示", + "use-tooltip-function": "使用文字提示函数", + "tooltip-pattern": "文字提示 (例如 'Text ${keyName} units.' or Link text')", + "tooltip-function": "文字提示函数", + "tooltip-offset-x": "文字提示X偏移相对于标记锚点乘以标记宽度", + "tooltip-offset-y": "文字提示Y偏移相对于标记锚点乘以标记高度", + "color": "颜色", + "use-color-function": "使用颜色函数", "color-function": "颜色函数", - "marker-image": "图片标记设置", - "use-marker-image-function": "启用函数", - "custom-marker-image": "自定义图片标记", - "custom-marker-image-size": "自定义图片标记大小(像素)", - "marker-image-function": "自定义图片标记函数", - "marker-images": "图片标记", + "marker-image": "标记图像", + "use-marker-image-function": "使用标记图像函数", + "custom-marker-image": "自定义标记图像", + "custom-marker-image-size": "自定义标记图像大小(px)", + "marker-image-function": "标记图像函数", + "marker-images": "标记图像", "polygon-settings": "多边形设置", - "show-polygon": "启用多边形", + "show-polygon": "显示多边形", "polygon-key-name": "多边形键名", - "enable-polygon-edit": "启用编辑", - "polygon-label": "标签", - "show-polygon-label": "启用标签", - "use-polygon-label-function": "启用函数", - "polygon-label-pattern": "标签模式(例如: '${entityName}', '${entityName}:(文本${keyName}单位)' )", - "polygon-label-function": "标签函数", - "polygon-tooltip": "提示栏", - "show-polygon-tooltip": "启用提示栏", - "auto-close-polygon-tooltips": "自动关闭", - "use-polygon-tooltip-function": "启用函数", - "polygon-tooltip-pattern": "提示栏(例如:'文本${keyName}单位'或Link text')", - "polygon-tooltip-function": "提示栏函数", - "polygon-color": "颜色", - "polygon-opacity": "透明度”", - "use-polygon-color-function": "启用函数", - "polygon-color-function": "颜色函数", - "polygon-stroke": "斜线", - "stroke-color": "斜线颜色", - "stroke-opacity": "透明度", - "stroke-weight": "宽度", - "use-polygon-stroke-color-function": "启用函数", - "polygon-stroke-color-function": "颜色函数", - "circle-settings": "圆形设置", - "show-circle": "启用圆形", - "circle-key-name": "圆形键名", - "enable-circle-edit": "启用编辑", - "circle-label": "标签", - "show-circle-label": "启用标签", - "use-circle-label-function": "启用函数", - "circle-label-pattern": "标签模式(例如:'${entityName}', '${entityName}:(文本${keyName}单位)')", - "circle-label-function": "标签函数", - "circle-tooltip": "提示栏", - "show-circle-tooltip": "显示提示栏", - "auto-close-circle-tooltips": "自动关闭", - "use-circle-tooltip-function": "启用函数", - "circle-tooltip-pattern": "提示栏(例如:'文本${keyName}单位。'或Link text')", - "circle-tooltip-function": "提示栏函数", - "circle-fill-color": "填充色", - "circle-fill-color-opacity": "透明度", - "use-circle-fill-color-function": "启用函数", - "circle-fill-color-function": "填充颜色函数", - "circle-stroke": "斜线设置", - "use-circle-stroke-color-function": "启用函数", - "circle-stroke-color-function": "颜色函数", - "markers-clustering-settings": "标记集群设置", - "use-map-markers-clustering": "启用地图标记集群", - "zoom-on-cluster-click": "单击群集时变焦", - "max-cluster-zoom": "集群最大的缩放比例(0-18)", - "max-cluster-radius-pixels": "最大半径", - "cluster-zoom-animation": "放大动画", - "show-markers-bounds-on-cluster-mouse-over": "显示鼠标在群集上时显示标记的边界", - "spiderfy-max-zoom-level": "最大缩放比例(查看所有群集标记)", - "load-optimization": "负载优化", - "cluster-chunked-loading": "块加载集群标记", - "cluster-markers-lazy-load": "延迟加载集群标记", - "editor-settings": "编辑设置", - "enable-snapping": "启用顶点捕捉", - "init-draggable-mode": "启用拖动模式初始化地图", - "hide-all-edit-buttons": "隐藏所有按钮", + "enable-polygon-edit": "启用多边形编辑", + "polygon-label": "多边形标签", + "show-polygon-label": "显示多边形标签", + "use-polygon-label-function": "使用多边形标签函数", + "polygon-label-pattern": "多边形标签 (模板示例:'${entityName}', '${entityName}: (Text ${keyName} units.)' )", + "polygon-label-function": "多边形标签函数", + "polygon-tooltip": "多边形文字提示", + "show-polygon-tooltip": "显示多边形文字提示", + "auto-close-polygon-tooltips": "自动关闭多边形文字提示", + "use-polygon-tooltip-function": "使用多边形文字提示函数", + "polygon-tooltip-pattern": "文字提示(例如 'Text ${keyName} units.' or Link text')", + "polygon-tooltip-function": "多边形文字提示函数", + "polygon-color": "多边形颜色", + "polygon-opacity": "多边形不透明度", + "use-polygon-color-function": "使用多边形颜色函数", + "polygon-color-function": "多边形颜色函数", + "polygon-stroke": "多边形描边", + "stroke-color": "描边颜色", + "stroke-opacity": "描边不透明度", + "stroke-weight": "描边宽度", + "use-polygon-stroke-color-function": "使用多边形描边颜色函数", + "polygon-stroke-color-function": "多边形描边颜色函数", + "circle-settings": "圆设置", + "show-circle": "显示圆", + "circle-key-name": "圆键名", + "enable-circle-edit": "启用圆编辑", + "circle-label": "圆标签", + "show-circle-label": "显示圆标签", + "use-circle-label-function": "使用圆标签函数", + "circle-label-pattern": "圆标签 (模板示例:'${entityName}', '${entityName}: (Text ${keyName} units.)' )", + "circle-label-function": "圆标签函数", + "circle-tooltip": "圆文字提示", + "show-circle-tooltip": "显示圆文字提示", + "auto-close-circle-tooltips": "自动关闭圆文字提示", + "use-circle-tooltip-function": "使用圆文字提示函数", + "circle-tooltip-pattern": "文字提示 (例如 'Text ${keyName} units.' or Link text')", + "circle-tooltip-function": "圆文字提示函数", + "circle-fill-color": "圆填充颜色", + "circle-fill-color-opacity": "圆填充颜色不透明度", + "use-circle-fill-color-function": "使用圆填充颜色函数", + "circle-fill-color-function": "圆填充颜色函数", + "circle-stroke": "圆边框", + "use-circle-stroke-color-function": "使用圆边框颜色函数", + "circle-stroke-color-function": "圆边框颜色函数", + "markers-clustering-settings": "标记聚类设置", + "use-map-markers-clustering": "使用地图标记聚类", + "zoom-on-cluster-click": "点击集群时进行缩放", + "max-cluster-zoom": "当标记可以成为集群的一部分时的最大缩放级别(0-18)", + "max-cluster-radius-pixels": "簇所覆盖的最大半径(px)", + "cluster-zoom-animation": "缩放时显示标记动画", + "show-markers-bounds-on-cluster-mouse-over": "鼠标悬停在集群上时显示标记范围", + "spiderfy-max-zoom-level": "以最大缩放级别展开簇(以查看所有集群标记)", + "load-optimization": "加载优化", + "cluster-chunked-loading": "使用块添加标记,以避免页面冻结", + "cluster-markers-lazy-load": "使用延迟加载添加标记", + "editor-settings": "编辑器设置", + "enable-snapping": "启用精确绘制时的吸附到其他顶点", + "init-draggable-mode": "以可拖动模式初始化地图", + "hide-all-edit-buttons": "隐藏所有编辑控制按钮", "hide-draw-buttons": "隐藏绘制按钮", "hide-edit-buttons": "隐藏编辑按钮", "hide-remove-button": "隐藏删除按钮", - "route-map-settings": "路由设置", - "trip-animation-settings": "旅行动画设置", - "normalization-step": "正常数据步长(ms)", - "tooltip-background-color": "工具提示背景颜色", - "tooltip-font-color": "工具提示字体颜色", - "tooltip-opacity": "工具提示不透明度(0-1)", - "auto-close-tooltip": "自动关闭工具提示", + "route-map-settings": "路线地图设置", + "trip-animation-settings": "行程动画设置", + "normalization-step": "数据标准化步骤(毫秒)", + "tooltip-background-color": "文字提示背景颜色", + "tooltip-font-color": "文字提示字体颜色", + "tooltip-opacity": "文字提示不透明度(0-1)", + "auto-close-tooltip": "自动关闭文字提示", "rotation-angle": "设置标记的附加旋转角度(度)", "path-settings": "路径设置", "path-color": "路径颜色", - "use-path-color-function": "使用路径颜色功能", - "path-color-function": "路径颜色功能", + "use-path-color-function": "使用路径颜色函数", + "path-color-function": "路径颜色函数", "path-decorator": "路径装饰器", "use-path-decorator": "使用路径装饰器", "decorator-symbol": "装饰符号", "decorator-symbol-arrow-head": "箭头", - "decorator-symbol-dash": "短跑", - "decorator-symbol-size": "装饰器符号尺寸(像素)", + "decorator-symbol-dash": "短划线", + "decorator-symbol-size": "装饰符号大小(px)", "use-path-decorator-custom-color": "使用路径装饰器自定义颜色", - "decorator-custom-color": "装饰器定制颜色", - "decorator-offset": "装饰器偏移", - "end-decorator-offset": "结束装饰器偏移", - "decorator-repeat": "装饰器重复", + "decorator-custom-color": "装饰符号自定义颜色", + "decorator-offset": "装饰符号偏移量", + "end-decorator-offset": "结束装饰符号偏移量", + "decorator-repeat": "装饰符号重复", "points-settings": "点设置", "show-points": "显示点", "point-color": "点颜色", - "point-size": "点大小(像素)", - "use-point-color-function": "使用点颜色功能", + "point-size": "点大小(px)", + "use-point-color-function": "使用点颜色函数", "point-color-function": "点颜色函数", - "use-point-as-anchor": "使用点作为锚", - "point-as-anchor-function": "点为锚函数", - "independent-point-tooltip": "独立点工具提示", - "clustering-markers": "集群标记", - "use-icon-create-function": "启用集群标记", + "use-point-as-anchor": "使用点作为锚点", + "point-as-anchor-function": "点作为锚点函数", + "independent-point-tooltip": "独立点文字提示", + "clustering-markers": "标记聚类", + "use-icon-create-function": "使用标记颜色函数", "marker-color-function": "标记颜色函数" }, "markdown": { - "use-markdown-text-function": "使用markdown/HTML函数", - "markdown-text-function": "Markdown/HTML函数", - "markdown-text-pattern": "Markdown/HTML模式(markdown或HTML变量, 例如:'${entityName}或${keyName}一些文本')", + "use-markdown-text-function": "使用 Markdown/HTML 值函数", + "markdown-text-function": "Markdown/HTML 值函数", + "markdown-text-pattern": "Markdown/HTML 模板 (使用变量的 markdown 或 HTML,例如 '${entityName} or ${keyName} - some text.')", + "apply-default-markdown-style": "应用默认的 Markdown 样式", "markdown-css": "Markdown/HTML CSS" }, "simple-card": { + "label": "标签", "label-position": "标签位置", - "label-position-left": "左", - "label-position-top": "上" + "label-position-left": "左侧", + "label-position-top": "顶部" + }, + "value-card": { + "layout": "布局", + "layout-square": "正方形", + "layout-vertical": "垂直", + "layout-centered": "居中", + "layout-simplified": "简化", + "layout-horizontal": "水平", + "layout-horizontal-reversed": "水平翻转", + "label": "标签", + "icon": "图标", + "value": "数值", + "date": "日期", + "value-card-style": "数值卡片样式", + "auto-scale": "自动缩放" + }, + "liquid-level-card": { + "layout-simple": "简单", + "layout-percentage": "百分比", + "layout-absolute": "绝对值", + "layout": "布局", + "background-overlay": "值背景叠加", + "total-volume": "总体积", + "tank": "储罐", + "shape": "形状", + "datasource-units": "数据源单位", + "widget-units": "小部件单位", + "decimals": "小数位数", + "liquid": "液体", + "liquid-color": "液体颜色", + "value": "值", + "value-font": "值字体", + "level": "水位", + "last-update": "最后更新", + "shape-by-attribute": "按属性名称设置储罐形状", + "tooltip-background": "背景颜色", + "background-blur": "背景模糊", + "tank-color": "储罐颜色", + "static": "静态", + "see-examples": "查看示例", + "attribute": "属性", + "shape-type": "类型", + "v-oval": "垂直椭圆", + "v-cylinder": "垂直圆柱体", + "v-capsule": "垂直胶囊", + "rectangle": "矩形", + "h-oval": "水平椭圆", + "h-ellipse": "水平椭圆", + "h-dish-ends": "水平盘形", + "h-cylinder": "水平圆柱体", + "h-capsule": "水平胶囊", + "h-elliptical_2_1": "水平2:1椭圆", + "icon": "卡片图标", + "title": "卡片标题", + "units": "单位", + "color-and-font": "颜色和字体", + "shape-attribute-name": "属性名称", + "total-volume-required": "需要总体积。", + "attribute-name-required": "需要属性名称。", + "attribute-key-not-set": "未设置属性 '{{attributeName}}' 键", + "attribute-key-invalid": "属性 '{{attributeName}}' 键无效" + }, + "aggregated-value-card": { + "subtitle": "副标题", + "chart": "图表", + "values": "数值", + "value-appearance": "数值外观", + "position": "位置", + "position-center": "中心", + "position-right-top": "右上", + "position-right-bottom": "右下", + "position-left-top": "左上", + "position-left-bottom": "左下", + "font": "字体", + "color": "颜色", + "arrow": "箭头", + "display-up-down-arrow": "显示向上/向下箭头", + "add-value": "添加数值", + "remove-value": "移除数值", + "no-values": "未配置数值", + "aggregation": "聚合", + "aggregated-value-card-style": "聚合数值卡片样式", + "auto-scale": "自动缩放" + }, + "value-chart-card": { + "layout": "布局", + "layout-left": "左侧", + "layout-right": "右侧", + "auto-scale": "自动缩放", + "icon": "图标", + "value": "数值", + "chart": "图表", + "value-chart-card-style": "数值图表卡片样式" + }, + "progress-bar": { + "layout": "布局", + "layout-default": "默认", + "layout-simplified": "简化", + "auto-scale": "自动缩放", + "icon": "图标", + "value": "数值", + "range": "范围", + "min": "最小", + "max": "最大", + "range-ticks": "范围刻度", + "bar": "进度条", + "bar-color": "进度条颜色", + "bar-background": "进度条背景", + "progress-bar-card-style": "进度条卡片样式" + }, + "alarm-count": { + "alarm-count-card-style": "告警计数卡片样式" + }, + "entity-count": { + "entity-count-card-style": "实体计数卡片样式" + }, + "count": { + "layout": "布局", + "layout-column": "列", + "layout-row": "行", + "label": "标签", + "icon": "图标", + "icon-background": "图标背景", + "value": "数值", + "chevron": "箭头", + "auto-scale": "自动缩放" }, "table": { - "common-table-settings": "表格设置", + "common-table-settings": "常规表格设置", "enable-search": "启用搜索", - "enable-sticky-header": "始终显示标题", - "enable-sticky-action": "始终显示动作", - "hidden-cell-button-display-mode": "隐藏单元按钮操作显示模式", - "show-empty-space-hidden-action": "显示空空间而不是隐藏的单元按钮动作", - "dont-reserve-space-hidden-action": "不要为隐藏动作按钮保留空间", - "display-timestamp": "显示时间", - "display-milliseconds": "显示毫秒", + "enable-sticky-header": "始终显示表头", + "enable-sticky-action": "始终显示操作列", + "hidden-cell-button-display-mode": "隐藏单元格按钮操作显示模式", + "show-empty-space-hidden-action": "显示空白区域而不是隐藏单元格按钮操作", + "dont-reserve-space-hidden-action": "不为隐藏的操作按钮预留空间", + "display-timestamp": "显示时间戳列", "display-pagination": "显示分页", - "default-page-size": "分页数量", - "use-entity-label-tab-name": "实体标签名称", - "hide-empty-lines": "隐藏空线", + "default-page-size": "默认页面大小", + "use-entity-label-tab-name": "在选项卡名称中使用实体标签", + "hide-empty-lines": "隐藏空行", "row-style": "行样式", - "use-row-style-function": "启用样式函数", - "row-style-function": "样式函数", - "cell-style": "列样式", - "use-cell-style-function": "启用样式函数", - "cell-style-function": "样式函数", - "cell-content": "内容", - "use-cell-content-function": "启用内容函数", - "cell-content-function": "内容函数", + "use-row-style-function": "使用行样式函数", + "row-style-function": "行样式函数", + "cell-style": "单元格样式", + "use-cell-style-function": "使用单元格样式函数", + "cell-style-function": "单元格样式函数", + "cell-content": "单元格内容", + "use-cell-content-function": "使用单元格内容函数", + "cell-content-function": "单元格内容函数", "show-latest-data-column": "显示最新数据列", - "latest-data-column-order": "样式函数", - "entities-table-title": "实体表格标题", - "enable-select-column-display": "启用数据列", - "display-entity-name": "显示实体名称", + "latest-data-column-order": "最新数据列顺序", + "entities-table-title": "实体表标题", + "enable-select-column-display": "启用选择要显示的列", + "display-entity-name": "显示实体名称列", "entity-name-column-title": "实体名称列标题", - "display-entity-label": "显示实体标签", + "display-entity-label": "显示实体标签列", "entity-label-column-title": "实体标签列标题", - "display-entity-type": "显示实体类型", - "default-sort-order": "默认排序", - "custom-title": "Custom header title", - "column-width": "列宽", - "default-column-visibility": "默认显示", - "column-visibility-visible": "显示", + "display-entity-type": "显示实体类型列", + "default-sort-order": "默认排序顺序", + "custom-title": "自定义表头标题", + "column-width": "列宽度(px 或 %)", + "default-column-visibility": "默认列可见性", + "column-visibility-visible": "可见", "column-visibility-hidden": "隐藏", - "column-visibility-hidden-mobile": "Hidden in mobile mode", - "column-selection-to-display": "“要显示”列中的列选择", + "column-visibility-hidden-mobile": "在移动模式下隐藏", + "column-selection-to-display": "'显示列' 中的列选择", "column-selection-to-display-enabled": "启用", "column-selection-to-display-disabled": "禁用", - "alarms-table-title": "警报标题", - "enable-alarms-selection": "启用警报选择", - "enable-alarms-search": "启用警报搜索", - "enable-alarm-filter": "启用警报过滤器", - "display-alarm-details": "显示警报详细", - "allow-alarms-ack": "允许确认警报", - "allow-alarms-clear": "允许清除警报" + "alarms-table-title": "告警表标题", + "enable-alarms-selection": "启用告警选择", + "enable-alarms-search": "启用告警搜索", + "enable-alarm-filter": "启用告警过滤", + "display-alarm-details": "显示告警详细信息", + "allow-alarms-ack": "允许确认告警", + "allow-alarms-clear": "允许清除告警", + "display-alarm-activity": "显示告警活动", + "allow-alarms-assign": "允许分配告警", + "columns": "列", + "column-settings": "列设置", + "remove-column": "移除列", + "add-column": "添加列", + "no-columns": "没有配置列", + "columns-to-display": "显示的列", + "table-header": "表头", + "header-buttons": "表头按钮", + "table-buttons": "表格按钮", + "pagination": "分页", + "rows": "行", + "timeseries-column-error": "至少应指定一个时间序列列", + "alarm-column-error": "至少应指定一个告警列", + "table-tabs": "表格标签", + "show-cell-actions-menu-mobile": "在移动模式下显示单元格操作下拉菜单" + }, + "wind-speed-direction": { + "layout": "布局", + "layout-default": "默认", + "layout-advanced": "高级", + "layout-simplified": "简化", + "values": "数值", + "wind-direction": "风向", + "center-value": "中心数值", + "icon": "图标", + "arrow": "箭头", + "ticks": "刻度", + "labels-type": "标签类型", + "directional-names": "方向名称", + "degrees": "度数", + "major-ticks": "主要刻度", + "minor-ticks": "次要刻度", + "wind-speed-direction-card-style": "风速和风向卡片样式", + "ticks-color": "刻度颜色", + "ticks-labels-type": "刻度标签类型", + "arrow-color": "箭头颜色" }, "value-source": { - "value-source": "数值源", + "value-source": "值来源", "predefined-value": "预定义值", - "entity-attribute": "从实体属性中获取值", - "value": "数据值", - "source-entity-alias": "实体别", - "source-entity-attribute": "实体属性" + "entity-attribute": "从实体属性中获取的值", + "value": "值", + "source-entity-alias": "源实体别名", + "source-entity-attribute": "源实体属性" }, "widget-font": { + "font-settings": "字体设置", "font-family": "字体", "size": "大小", - "relative-font-size": "相对字体大小", + "relative-font-size": "相对字体大小(百分比)", "font-style": "样式", - "font-style-normal": "常规", + "font-style-normal": "正常", "font-style-italic": "斜体", "font-style-oblique": "倾斜", - "font-weight": "字形", - "font-weight-normal": "常规", - "font-weight-bold": "加粗", - "font-weight-bolder": "相对加粗", - "font-weight-lighter": "相对常规", + "font-weight": "粗细", + "font-weight-normal": "正常", + "font-weight-bold": "粗体", + "font-weight-bolder": "更粗", + "font-weight-lighter": "更轻", "color": "颜色", - "shadow-color": "阴影颜色" + "shadow-color": "阴影颜色", + "preview": "预览", + "line-height": "行高", + "auto": "自动" + }, + "home": { + "no-data-available": "暂无数据" + }, + "system-info": { + "cpu": "CPU", + "ram": "内存", + "disk": "磁盘", + "cpu-warning-text": "CPU 使用率过高。为了避免系统故障,请优化系统性能。", + "cpu-critical-text": "CPU 使用率严重过高。为了避免系统故障,请优化系统性能。", + "ram-warning-text": "内存储备不足。为了避免系统故障,请优化系统性能或增加内存大小。", + "ram-critical-text": "内存储备严重不足。为了避免系统故障,请优化系统性能或增加内存大小。", + "disk-warning-text": "磁盘空间不足。为了避免数据丢失,请释放或扩展磁盘空间。", + "disk-critical-text": "磁盘空间严重不足。为了避免数据丢失,请释放或扩展磁盘空间。" + }, + "cluster-info": { + "service-id": "服务ID", + "service-type": "服务类型", + "no-data": "无数据" + }, + "transport-messages": { + "title": "传输消息", + "info": "来自设备的所有消息" + }, + "activity": { + "title": "活动" + }, + "documentation": { + "title": "文档", + "add-link": "添加链接", + "add-link-title": "添加文档链接", + "name": "名称", + "name-required": "名称是必填项。", + "link": "链接", + "link-required": "链接是必填项。", + "columns": "列" + }, + "quick-links": { + "title": "快速链接", + "add-link": "添加链接", + "add-link-title": "添加快速链接", + "quick-link": "快速链接", + "quick-link-required": "快速链接是必填项。", + "no-links-matching": "找不到与'{{name}}'匹配的链接。", + "columns": "列" + }, + "recent-dashboards": { + "title": "仪表盘", + "last": "最近查看", + "starred": "已收藏", + "name": "名称", + "last-viewed": "最近查看", + "no-last-viewed-dashboards": "暂无最近查看的仪表盘" + }, + "configured-features": { + "title": "配置的功能", + "info": "需要配置的功能状态", + "email-feature": "电子邮件", + "sms-feature": "短信", + "slack-feature": "Slack", + "oauth2-feature": "OAuth 2", + "2fa-feature": "两步验证", + "feature-configured": "功能已配置\n点击设置", + "feature-not-configured": "功能未配置\n点击设置" + }, + "version-info": { + "title": "版本", + "contact-us": "联系我们", + "current-version": "当前版本", + "current": "当前", + "available-version": "可用版本", + "available": "可用", + "upgrade": "升级", + "version-is-up-to-date": "已经是最新版本" + }, + "usage-info": { + "title": "使用情况", + "entities": "实体", + "api-calls": "API 调用" + }, + "functions": { + "title": "功能", + "pe-feature-tooltip": "仅适用于 ThingsBoard 专业版", + "switch-to-pe": "切换至专业版", + "alarms": "告警", + "dashboards": "仪表盘", + "entities-and-relations": "实体和关系", + "profiles": "配置", + "advanced-features": "高级功能", + "notification-center": "通知中心", + "api-usage": "API 使用情况", + "customers": "客户", + "customers-hierarchy": "客户层级", + "roles-and-permissions": "角色和权限", + "groups": "群组", + "integrations": "集成", + "solution-templates": "解决方案模板", + "scheduler": "调度器", + "white-labeling": "白标" + }, + "devices": { + "view-docs": "查看文档", + "inactive": "非活动", + "active": "活动", + "total": "总数" + }, + "alarms": { + "critical": "严重", + "assigned-to-me": "分配给我", + "total": "总数" + }, + "getting-started": { + "get-started": "开始", + "finish": "完成", + "done-welcome-title": "欢迎加入我们", + "done-welcome-text": "你做得很好!", + "sys-admin": { + "step1": { + "title": "创建租户和租户管理员", + "content": "

租户是拥有或生产设备和资产的个人或组织,租户可以有多个租户管理员用户、客户、设备和资产。

租户管理员可以在租户账户中创建和管理设备、资产、客户和仪表板。

请按照文档进行操作:

", + "how-to-create-tenant": "如何创建租户和租户管理员" + }, + "step2": { + "title": "配置功能:邮件服务", + "content": "

邮件服务配置对于用户激活、密码恢复和报警通知的发送非常重要。

请按照文档进行操作:

", + "how-to-configure-mail-server": "如何配置邮件服务器" + }, + "step3": { + "title": "配置功能:短信提供商", + "content": "

配置短信提供商,通过短信向客户发送报警通知。

请按照文档进行操作:

", + "how-to-configure-sms-provider": "如何配置短信提供商" + }, + "step4": { + "title": "配置功能:双因素认证", + "content": "

通过双因素认证提高平台账户的安全性。

请按照文档进行操作:

", + "how-to-configure-2fa": "如何配置双因素认证" + }, + "step5": { + "title": "配置功能:OAuth 2", + "content": "

通过OAuth 2.0的单点登录功能,简化租户和客户用户的登录过程。

请按照文档进行操作:

", + "how-to-configure-oauth2": "如何配置OAuth 2" + }, + "step6": { + "title": "配置功能:Slack", + "content": "

根据设置的通知规则,通过Slack向租户和客户用户发送通知。

请按照文档进行操作:

", + "how-to-configure-notifications": "如何配置Slack" + } + }, + "tenant-admin": { + "step1": { + "title": "创建设备", + "content": "

让我们通过用户界面将您的第一个设备添加到平台。请按照文档进行操作:

", + "how-to-create-device": "如何创建设备" + }, + "step2": { + "title": "连接设备", + "content-before": "

要连接设备,您需要获取设备凭据。我们建议在本指南中使用默认生成的凭据,即访问令牌。

  • 转到设备表
  • 点击设备行以打开设备详情
  • 点击“复制访问令牌”按钮

使用简单的命令通过HTTP发布数据。不要忘记将$ACCESS_TOKEN替换为您的设备访问令牌:

", + "ubuntu": { + "install-curl": "Ubuntu上安装cURL:" + }, + "macos": { + "install-curl": "MacOS上安装cURL:" + }, + "windows": { + "install-curl": "从Windows 10 b17063开始,cURL已默认可用。" + }, + "replace-access-token": "将$ACCESS_TOKEN替换为您的设备令牌:", + "content-after": "

您还可以使用其他协议,如MQTT、CoAP等。

请按照文档进行操作:

", + "how-to-connect-device": "如何连接设备" + }, + "step3": { + "title": "创建仪表板", + "content": "

创建一个仪表板,用于可视化来自实体(如资产、设备等)的数据。

请按照文档进行操作:

", + "how-to-create-dashboard": "如何创建仪表板" + }, + "step4": { + "title": "配置报警规则", + "alarm-rules": "报警规则", + "content": "

当温度达到25°C时,让我们触发一个报警。请按照文档进行操作:

", + "how-to-configure-alarm-rules": "如何配置报警规则" + }, + "step5": { + "title": "创建报警", + "content-before": "

要触发报警,请发送一个新的温度值,该值为26℃或更高。

", + "replace-access-token": "将$ACCESS_TOKEN替换为您的设备令牌:", + "content-after": "

请按照文档进行操作:

", + "how-to-create-alarm": "如何创建报警" + }, + "step6": { + "title": "创建客户并分配仪表板", + "content": "

通过创建终端用户仪表板,客户用户只能查看自己的设备,另一个客户的数据将被隐藏。

请按照文档进行操作:

", + "how-to-create-customer-and-assign-dashboard": "如何创建客户并分配仪表板" + } + } } }, + "color": { + "color": "颜色" + }, "icon": { "icon": "图标", + "icons": "图标", "select-icon": "选择图标", "material-icons": "素材图标", - "show-all": "显示所有图标" + "show-all": "显示所有图标", + "search-icon": "搜索图标", + "no-icons-found": "没有找到符合'{{iconSearch}}'的图标" }, "phone-input": { "phone-input-label": "手机号码", @@ -4735,25 +6578,26 @@ }, "custom": { "widget-action": { - "action-cell-button": "单元格click", - "row-click": "数据行click", - "polygon-click": "多边形click", - "marker-click": "标记click", - "circle-click": "圆形click", - "tooltip-tag-action": "提示框click", - "node-selected": "节点选中click", - "element-click": "HTML元素click", - "pie-slice-click": " 饼状click", - "row-double-click": "双击clcik" + "action-cell-button": "动作单元格按钮", + "row-click": "点击行时", + "polygon-click": "点击多边形时", + "marker-click": "点击标记时", + "circle-click": "点击圆圈时", + "tooltip-tag-action": "文字提示标签操作", + "node-selected": "选择节点时", + "element-click": "点击HTML元素时", + "pie-slice-click": "点击切片时", + "row-double-click": "双击行时", + "card-click": "点击卡片时" } }, "paginator": { - "items-per-page": "每页数量:", - "first-page-label": "第一页", - "last-page-label": "最后一页", + "items-per-page": "每页条数:", + "first-page-label": "首页", + "last-page-label": "尾页", "next-page-label": "下一页", "previous-page-label": "上一页", - "items-per-page-separator": "-" + "items-per-page-separator": "of" }, "language": { "language": "语言" diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_TW.json b/ui-ngx/src/assets/locale/locale.constant-zh_TW.json index 97ac15a4bc..5b415558a1 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_TW.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_TW.json @@ -519,6 +519,7 @@ "attributes": "屬性", "latest-telemetry": "最新遙測", "attributes-scope": "設備屬性範圍", + "scope-telemetry": "遙測", "scope-latest-telemetry": "最新遙測", "scope-client": "客戶端屬性", "scope-server": "服務端屬性", @@ -562,10 +563,6 @@ "executions": "執行", "javascript": "JavaScript", "javascript-executions": "JavaScript執行", - "javascript-functions": "JavaScript功能", - "javascript-functions-daily-activity": "JavaScript功能每日活動", - "javascript-functions-hourly-activity": "JavaScript功能每小時活動", - "javascript-functions-monthly-activity": "JavaScript功能每月活動", "latest-error": "最新錯誤", "messages": "訊息", "notifications": "通知", @@ -1134,13 +1131,7 @@ "device-configuration": "設備配置", "transport-configuration": "傳輸配置", "wizard": { - "device-wizard": "設備嚮導", - "device-details": "設備詳情", - "new-device-profile": "建立設備協議", - "existing-device-profile": "選擇現有的設備協議", - "specific-configuration": "具體配置", - "customer-to-assign-device": "客戶指定設備", - "add-credentials": "新增驗證資訊" + "device-details": "設備詳情" }, "unassign-devices-from-edge-title": "您確定要解除邊緣設備 { count, plural, =1 {1 device} other {# devices} }的指定嗎?", "unassign-devices-from-edge-text": "確認後邊緣指定設備將解除指定及其所有相關資料將無法恢復。" @@ -1205,8 +1196,6 @@ "delete": "刪除設備協議", "copyId": "複製設備協議Id", "name-max-length": "名稱應小於256", - "new-device-profile-name": "設備協議名稱", - "new-device-profile-name-required": "需要設備協議名稱。", "name": "名稱", "name-required": "需要名稱", "type": "協議類型", @@ -1315,9 +1304,7 @@ "condition-duration-value-required": "需要持續時間值。", "condition-duration-time-unit-required": "需要時間單位。", "advanced-settings": "進階設定", - "alarm-rule-details": "詳細資訊", - "alarm-rule-details-hint": "提示:使用${keyName}替換警告規則條件中使用的屬性或遙測鍵的值。", - "add-alarm-rule-details": "增加詳細資訊", + "alarm-rule-additional-info-hint": "提示:使用${keyName}替換警告規則條件中使用的屬性或遙測鍵的值。", "alarm-rule-mobile-dashboard": "移動儀表板", "alarm-rule-mobile-dashboard-hint": "由移動應用程序用作警告詳情儀表板", "alarm-rule-no-mobile-dashboard": "未選擇儀表板", @@ -1326,7 +1313,6 @@ "alarm-rule-relation-types-list-hint": "如果未選擇傳播關聯類型,則將在不按關聯類型過濾的情況下傳播警告。", "propagate-alarm-to-owner": "傳播警告給相關實體擁有者", "propagate-alarm-to-tenant": "傳播警告給租戶", - "alarm-details": "警告詳細資料", "alarm-rule-condition": "警告規則條件", "enter-alarm-rule-condition-prompt": "請新增警告規則條件", "edit-alarm-rule-condition": "編輯警告規則條件", @@ -1742,7 +1728,6 @@ "help-text": "根據需要使用'%':'%entity_name_contains%', '%entity_name_ends', 'entity_starts_with'.", "use-entity-name-filter": "用戶過濾", "entity-list-empty": "沒有選擇實體。", - "entity-type-list-empty": "沒有選擇實體類型。", "entity-name-filter-required": "實體名過濾器必填。", "entity-name-filter-no-entity-matched": "沒有找到以 '{{entity}}' 開頭的實體", "all-subtypes": "所有", @@ -2371,7 +2356,6 @@ "browse-files": "瀏覽文件" }, "image-input": { - "drop-image-or": "拖放圖像或", "drop-images-or": "拖放圖像或", "no-images": "未選擇任何圖像", "images": "圖像" @@ -2795,7 +2779,6 @@ "name-required": "名稱必填。", "name-max-length": "名稱應小於256", "type": "類型", - "description": "描述", "delete": "刪除規則節點", "select-all-objects": "選擇所有節點和連接", "deselect-all-objects": "取消選擇所有節點和連接", @@ -3346,7 +3329,7 @@ "created": "{{created}}已創建", "updated": "{{updated}}已更新", "deleted": "{{deleted}} 已刪除", - "remove-other-entities-confirm-text": "小心!這將永久刪除您要恢復的版本中不存在的所有當前實體。請鍵入刪除其他實體進行確認。", + "remove-other-entities-confirm-text": "小心!這將永久刪除所有在您要恢復的版本中不存在的當前實體。請輸入 \"remove other entities\" 進行確認。", "auto-commit-to-branch": "自動提交到{{ branch }}分支", "default-create-entity-version-name": "{{entityName}} 更新", "sync-strategy-merge-hint": "在存儲庫中創建或更新選定實體。所有其他存儲實體都不會被修改。", @@ -3407,11 +3390,7 @@ "latest-data-key-settings-form-selector": "最新資料鍵設置表單選擇器", "javascript": "Javascript", "js": "JS", - "remove-widget-type-title": "您確定要刪除部件類型 '{{widgetName}}'嗎?", - "remove-widget-type-text": "確認後,窗口部件類型和所有相關資料將無法恢復。", - "remove-widget-type": "刪除部件類型", "add-widget-type": "增加新的部件類型", - "widget-type-load-failed-error": "無法加載部件類型!", "widget-template-load-failed-error": "無法加載部件模板!", "add": "增加部件", "undo": "復原部件更改", @@ -4497,7 +4476,6 @@ "show-empty-space-hidden-action": "顯示空格而不是隱藏儲存格的按鈕動作", "dont-reserve-space-hidden-action": "不要為隱藏的動作按鈕保留空格", "display-timestamp": "顯示時間戳列", - "display-milliseconds": "顯示毫秒時間戳", "display-pagination": "顯示分頁", "default-page-size": "預設頁面尺寸", "use-entity-label-tab-name": "在分頁標籤名稱中使用實體標籤", diff --git a/ui-ngx/src/assets/macos.svg b/ui-ngx/src/assets/macos.svg new file mode 100644 index 0000000000..c3bac982fb --- /dev/null +++ b/ui-ngx/src/assets/macos.svg @@ -0,0 +1 @@ + diff --git a/ui-ngx/src/assets/metadata/material-icons.json b/ui-ngx/src/assets/metadata/material-icons.json new file mode 100644 index 0000000000..2da54c48c2 --- /dev/null +++ b/ui-ngx/src/assets/metadata/material-icons.json @@ -0,0 +1 @@ +[{"name":"more_horiz","tags":["3","DISABLE_IOS","app","application","components","disable_ios","dots","etc","horiz","horizontal","interface","ios","more","screen","site","three","ui","ux","web","website"]},{"name":"more_vert","tags":["3","DISABLE_IOS","android","app","application","components","disable_ios","dots","etc","interface","more","screen","site","three","ui","ux","vert","vertical","web","website"]},{"name":"open_in_new","tags":["app","application","arrow","box","components","in","interface","new","open","right","screen","site","ui","up","ux","web","website","window"]},{"name":"visibility","tags":["eye","on","reveal","see","show","view","visibility"]},{"name":"play_arrow","tags":["arrow","control","controls","media","music","play","video"]},{"name":"arrow_back","tags":["DISABLE_IOS","app","application","arrow","back","components","direction","disable_ios","interface","left","navigation","previous","screen","site","ui","ux","web","website"]},{"name":"arrow_downward","tags":["app","application","arrow","components","direction","down","downward","interface","navigation","screen","site","ui","ux","web","website"]},{"name":"arrow_forward","tags":["app","application","arrow","arrows","components","direction","forward","interface","navigation","right","screen","site","ui","ux","web","website"]},{"name":"arrow_upward","tags":["app","application","arrow","components","direction","interface","navigation","screen","site","ui","up","upward","ux","web","website"]},{"name":"close","tags":["cancel","close","exit","stop","x"]},{"name":"refresh","tags":["around","arrow","arrows","direction","inprogress","load","loading refresh","navigation","refresh","renew","right","rotate","turn"]},{"name":"menu","tags":["app","application","components","hamburger","interface","line","lines","menu","screen","site","ui","ux","web","website"]},{"name":"show_chart","tags":["analytics","bar","bars","chart","data","diagram","graph","infographic","line","measure","metrics","presentation","show chart","statistics","tracking"]},{"name":"multiline_chart","tags":["analytics","bar","bars","chart","data","diagram","graph","infographic","line","measure","metrics","multiple","statistics","tracking"]},{"name":"pie_chart","tags":["analytics","bar","bars","chart","data","diagram","graph","infographic","measure","metrics","pie","statistics","tracking"]},{"name":"insert_chart","tags":["analytics","bar","bars","chart","data","diagram","graph","infographic","insert","measure","metrics","statistics","tracking"]},{"name":"people","tags":["accounts","committee","face","family","friends","humans","network","people","persons","profiles","social","team","users"]},{"name":"person","tags":["account","face","human","people","person","profile","user"]},{"name":"domain","tags":["apartment","architecture","building","business","domain","estate","home","place","real","residence","residential","shelter","web","www"]},{"name":"devices_other","tags":["Android","OS","ar","cell","chrome","desktop","device","gadget","hardware","iOS","ipad","mac","mobile","monitor","other","phone","tablet","vr","watch","wearables","window"]},{"name":"widgets","tags":["app","box","menu","setting","squares","ui","widgets"]},{"name":"dashboard","tags":["cards","dashboard","format","layout","rectangle","shapes","square","web","website"]},{"name":"map","tags":["destination","direction","location","map","maps","pin","place","route","stop","travel"]},{"name":"pin_drop","tags":["destination","direction","drop","location","maps","navigation","pin","place","stop"]},{"name":"gps_fixed","tags":["destination","direction","fixed","gps","location","maps","pin","place","pointer","stop","tracking"]},{"name":"extension","tags":["app","extended","extension","game","jigsaw","plugin add","puzzle","shape"]},{"name":"search","tags":["filter","find","glass","look","magnify","magnifying","search","see"]},{"name":"settings","tags":["application","change","details","gear","info","information","options","personal","service","settings"]},{"name":"notifications","tags":["active","alarm","alert","bell","chime","notifications","notify","reminder","ring","sound"]},{"name":"notifications_active","tags":["active","alarm","alert","bell","chime","notifications","notify","reminder","ring","ringing","sound"]},{"name":"info","tags":["alert","announcement","assistance","details","help","i","info","information","service","support"]},{"name":"error_outline","tags":["!","alert","attention","caution","circle","danger","error","exclamation","important","mark","notification","outline","symbol","warning"]},{"name":"warning","tags":["!","alert","attention","caution","danger","error","exclamation","important","mark","notification","symbol","triangle","warning"]},{"name":"list","tags":["file","format","index","list","menu","options"]},{"name":"download","tags":["arrow","down","download","downloads","drive","install","upload"]},{"name":"import_export","tags":["arrow","arrows","direction","down","explort","import","up"]},{"name":"share","tags":["DISABLE_IOS","android","connect","contect","disable_ios","link","media","multimedia","multiple","network","options","share","shared","sharing","social"]},{"name":"add","tags":["+","add","new symbol","plus","symbol"]},{"name":"edit","tags":["compose","create","edit","editing","input","new","pen","pencil","write","writing"]},{"name":"check","tags":["DISABLE_IOS","check","confirm","correct","disable_ios","done","enter","mark","ok","okay","select","tick","yes"]},{"name":"delete","tags":["bin","can","delete","garbage","remove","trash"]},{"name":"thermostat","tags":["climate","forecast","temperature","thermostat","weather"]},{"name":"air","tags":["air","blowing","breeze","flow","wave","weather","wind"]},{"name":"lightbulb","tags":["alert","announcement","idea","info","information","light","lightbulb"]},{"name":"home","tags":["address","app","application--house","architecture","building","components","design","estate","home","interface","layout","place","real","residence","residential","screen","shelter","site","structure","ui","unit","ux","web","website","window"]},{"name":"account_circle","tags":["account","avatar","circle","face","human","people","person","profile","thumbnail","user"]},{"name":"done","tags":["DISABLE_IOS","approve","check","complete","disable_ios","done","mark","ok","select","tick","validate","verified","yes"]},{"name":"check_circle","tags":["approve","check","circle","complete","done","mark","ok","select","tick","validate","verified","yes"]},{"name":"expand_more","tags":["arrow","arrows","chevron","collapse","direction","down","expand","expandable","list","more"]},{"name":"shopping_cart","tags":["add","bill","buy","card","cart","cash","checkout","coin","commerce","credit","currency","dollars","money","online","pay","payment","shopping"]},{"name":"email","tags":["email","envelop","letter","mail","message","send"]},{"name":"favorite","tags":["appreciate","favorite","heart","like","love","remember","save","shape"]},{"name":"description","tags":["article","data","description","doc","document","drive","file","folder","folders","notes","page","paper","sheet","slide","text","writing"]},{"name":"logout","tags":["app","application","arrow","components","design","exit","interface","leave","log","login","logout","right","screen","site","ui","ux","web","website"]},{"name":"favorite_border","tags":["border","favorite","heart","like","love","outline","remember","save","shape"]},{"name":"chevron_right","tags":["arrow","arrows","chevron","direction","right"]},{"name":"lock","tags":["lock","locked","password","privacy","private","protection","safety","secure","security"]},{"name":"location_on","tags":["destination","direction","location","maps","on","pin","place","room","stop"]},{"name":"schedule","tags":["clock","date","schedule","time"]},{"name":"local_shipping","tags":["automobile","car","cars","delivery","letter","local","mail","maps","office","package","parcel","post","postal","send","shipping","shopping","stamp","transportation","truck","vehicle"]},{"name":"language","tags":["globe","internet","language","planet","website","world","www"]},{"name":"call","tags":["call","cell","contact","device","hardware","mobile","phone","telephone"]},{"name":"file_download","tags":["arrow","arrows","down","download","downloads","drive","export","file","install","upload"]},{"name":"arrow_forward_ios","tags":["app","application","arrow","chevron","components","direction","forward","interface","ios","navigation","next","right","screen","site","ui","ux","web","website"]},{"name":"arrow_back_ios","tags":["DISABLE_IOS","app","application","arrow","back","chevron","components","direction","disable_ios","interface","ios","left","navigation","previous","screen","site","ui","ux","web","website"]},{"name":"groups","tags":["body","club","collaboration","crowd","gathering","groups","human","meeting","people","person","social","teams"]},{"name":"cancel","tags":["cancel","circle","close","exit","stop","x"]},{"name":"help_outline","tags":["?","assistance","circle","help","info","information","outline","punctuation","question mark","recent","restore","shape","support","symbol"]},{"name":"arrow_drop_down","tags":["app","application","arrow","components","direction","down","drop","interface","navigation","screen","site","ui","ux","web","website"]},{"name":"face","tags":["account","emoji","eyes","face","human","lock","log","login","logout","people","person","profile","recognition","security","social","thumbnail","unlock","user"]},{"name":"manage_accounts","tags":["accounts","change","details service-human","face","gear","manage","options","people","person","profile","settings","user"]},{"name":"place","tags":["destination","direction","location","maps","navigation","pin","place","point","stop"]},{"name":"verified","tags":["approve","badge","burst","check","complete","done","mark","ok","select","star","tick","validate","verified","yes"]},{"name":"add_circle_outline","tags":["+","add","circle","create","new","outline","plus"]},{"name":"filter_alt","tags":["alt","edit","filter","funnel","options","refine","sift"]},{"name":"thumb_up","tags":["favorite","fingers","gesture","hand","hands","like","rank","ranking","rate","rating","thumb","up"]},{"name":"event","tags":["calendar","date","day","event","mark","month","range","remember","reminder","today","week"]},{"name":"star","tags":["best","bookmark","favorite","highlight","ranking","rate","rating","save","star","toggle"]},{"name":"fingerprint","tags":["finger","fingerprint","id","identification","identity","print","reader","thumbprint","verification"]},{"name":"content_copy","tags":["content","copy","cut","doc","document","duplicate","file","multiple","past"]},{"name":"login","tags":["access","app","application","arrow","components","design","enter","in","interface","left","log","login","screen","sign","site","ui","ux","web","website"]},{"name":"add_circle","tags":["+","add","circle","create","new","plus"]},{"name":"visibility_off","tags":["disabled","enabled","eye","off","on","reveal","see","show","slash","view","visibility"]},{"name":"check_circle_outline","tags":["approve","check","circle","complete","done","finished","mark","ok","outline","select","tick","validate","verified","yes"]},{"name":"chevron_left","tags":["DISABLE_IOS","arrow","arrows","chevron","direction","disable_ios","left"]},{"name":"calendar_today","tags":["calendar","date","day","event","month","schedule","today"]},{"name":"send","tags":["email","mail","message","paper","plane","reply","right","send","share"]},{"name":"check_box","tags":["approved","box","button","check","component","control","form","mark","ok","select","selected","selection","tick","toggle","ui","yes"]},{"name":"highlight_off","tags":["cancel","close","exit","highlight","no","off","quit","remove","stop","x"]},{"name":"navigate_next","tags":["arrow","arrows","direction","navigate","next","right"]},{"name":"help","tags":["?","assistance","circle","help","info","information","punctuation","question mark","recent","restore","shape","support","symbol"]},{"name":"phone","tags":["call","cell","contact","device","hardware","mobile","phone","telephone"]},{"name":"paid","tags":["circle","currency","money","paid","payment","transaction"]},{"name":"task_alt","tags":["approve","check","circle","complete","done","mark","ok","select","task","tick","validate","verified","yes"]},{"name":"question_answer","tags":["answer","bubble","chat","comment","communicate","conversation","feedback","message","question","speech","talk"]},{"name":"expand_less","tags":["arrow","arrows","chevron","collapse","direction","expand","expandable","less","list","up"]},{"name":"clear","tags":["back","cancel","clear","correct","delete","erase","exit","x"]},{"name":"date_range","tags":["calendar","date","day","event","month","range","remember","reminder","schedule","time","today","week"]},{"name":"article","tags":["article","doc","document","file","page","paper","text","writing"]},{"name":"error","tags":["!","alert","attention","caution","circle","danger","error","exclamation","important","mark","notification","symbol","warning"]},{"name":"photo_camera","tags":["camera","image","photo","photography","picture"]},{"name":"check_box_outline_blank","tags":["blank","box","button","check","component","control","deselected","empty","form","outline","select","selection","square","tick","toggle","ui"]},{"name":"image","tags":["disabled","enabled","hide","image","landscape","mountain","mountains","off","on","photo","photography","picture","slash"]},{"name":"shopping_bag","tags":["bag","bill","business","buy","card","cart","cash","coin","commerce","credit","currency","dollars","money","online","pay","payment","shop","shopping","store","storefront"]},{"name":"person_outline","tags":["account","face","human","outline","people","person","profile","user"]},{"name":"school","tags":["academy","achievement","cap","class","college","education","graduation","hat","knowledge","learning","school","university"]},{"name":"file_upload","tags":["arrow","arrows","download","drive","export","file","up","upload"]},{"name":"perm_identity","tags":["account","avatar","face","human","identity","people","perm","person","profile","thumbnail","user"]},{"name":"credit_card","tags":["bill","card","cash","coin","commerce","cost","credit","currency","dollars","finance","money","online","pay","payment","price","shopping","symbol"]},{"name":"history","tags":["arrow","back","backwards","clock","date","history","refresh","renew","reverse","rotate","schedule","time","turn"]},{"name":"trending_up","tags":["analytics","arrow","data","diagram","graph","infographic","measure","metrics","movement","rate","rating","statistics","tracking","trending","up"]},{"name":"support_agent","tags":["agent","care","customer","face","headphone","person","representative","service","support"]},{"name":"account_balance","tags":["account","balance","bank","bill","card","cash","coin","commerce","credit","currency","dollars","finance","money","online","pay","payment"]},{"name":"delete_outline","tags":["bin","can","delete","garbage","outline","remove","trash"]},{"name":"attach_money","tags":["attach","attachment","bill","card","cash","coin","commerce","cost","credit","currency","dollars","finance","money","online","pay","payment","symbol"]},{"name":"person_add","tags":["+","account","add","avatar","face","human","new","people","person","plus","profile","symbol","user"]},{"name":"public","tags":["earth","global","globe","map","network","planet","public","social","space","web","world"]},{"name":"save","tags":["data","disk","document","drive","file","floppy","multimedia","save","storage"]},{"name":"mail","tags":["email","envelop","letter","mail","message","send"]},{"name":"report_problem","tags":["!","alert","attention","caution","danger","error","exclamation","feedback","important","mark","notification","problem","report","symbol","triangle","warning"]},{"name":"fact_check","tags":["approve","check","complete","done","fact","list","mark","ok","select","tick","validate","verified","yes"]},{"name":"radio_button_unchecked","tags":["bullet","button","circle","deselected","form","off","on","point","radio","record","select","toggle","unchecked"]},{"name":"verified_user","tags":["approve","certified","check","complete","done","mark","ok","privacy","private","protect","protection","security","select","shield","tick","user","validate","verified","yes"]},{"name":"assignment","tags":["assignment","clipboard","doc","document","text","writing"]},{"name":"link","tags":["chain","clip","connection","link","linked","links","multimedia","url"]},{"name":"play_circle_filled","tags":["arrow","circle","control","controls","media","music","play","video"]},{"name":"emoji_events","tags":["achievement","award","chalice","champion","cup","emoji","events","first","prize","reward","sport","trophy","winner"]},{"name":"remove","tags":["can","delete","minus","negative","remove","substract","trash"]},{"name":"star_rate","tags":["achievement","bookmark","favorite","highlight","important","marked","ranking","rate","rating rank","reward","save","saved","shape","special","star"]},{"name":"apps","tags":["all","applications","apps","circles","collection","components","dots","grid","interface","squares","ui","ux"]},{"name":"business","tags":["apartment","architecture","building","business","company","estate","home","place","real","residence","residential","shelter"]},{"name":"filter_list","tags":["filter","lines","list","organize","sort"]},{"name":"arrow_right_alt","tags":["alt","arrow","arrows","direction","east","navigation","pointing","right"]},{"name":"chat","tags":["bubble","chat","comment","communicate","feedback","message","speech"]},{"name":"account_balance_wallet","tags":["account","balance","bank","bill","card","cash","coin","commerce","credit","currency","dollars","finance","money","online","pay","payment","wallet"]},{"name":"payments","tags":["bill","card","cash","coin","commerce","cost","credit","currency","dollars","finance","layer","money","multiple","online","pay","payment","payments","price","shopping","symbol"]},{"name":"menu_book","tags":["book","dining","food","meal","menu","restaurant"]},{"name":"folder","tags":["data","doc","document","drive","file","folder","folders","sheet","slide","storage"]},{"name":"keyboard_arrow_down","tags":["arrow","arrows","down","keyboard"]},{"name":"autorenew","tags":["around","arrow","arrows","autorenew","cache","cached","direction","inprogress","load","loading refresh","navigation","renew","rotate","turn"]},{"name":"build","tags":["adjust","build","fix","home","nest","repair","tool","tools","wrench"]},{"name":"videocam","tags":["cam","camera","conference","film","filming","hardware","image","motion","picture","video","videography"]},{"name":"view_list","tags":["design","format","grid","layout","lines","list","stacked","view","website"]},{"name":"print","tags":["draft","fax","ink","machine","office","paper","print","printer","send"]},{"name":"work","tags":["bag","baggage","briefcase","business","case","job","suitcase","work"]},{"name":"store","tags":["bill","building","business","card","cash","coin","commerce","company","credit","currency","dollars","market","money","online","pay","payment","shop","shopping","store","storefront"]},{"name":"analytics","tags":["analytics","assessment","bar","chart","data","diagram","graph","infographic","measure","metrics","statistics","tracking"]},{"name":"radio_button_checked","tags":["app","application","bullet","button","checked","circle","components","design","form","interface","off","on","point","radio","record","screen","select","selected","site","toggle","ui","ux","web","website"]},{"name":"phone_iphone","tags":["Android","OS","cell","device","hardware","iOS","iphone","mobile","phone","tablet"]},{"name":"play_circle","tags":["arrow","circle","control","controls","media","music","play","video"]},{"name":"tune","tags":["adjust","audio","controls","custom","customize","edit","editing","filter","filters","instant","mix","music","options","setting","settings","slider","sliders","switches","tune"]},{"name":"delete_forever","tags":["bin","can","cancel","delete","exit","forever","garbage","remove","trash","x"]},{"name":"today","tags":["calendar","date","day","event","mark","month","remember","reminder","schedule","time","today"]},{"name":"grid_view","tags":["app","application square","blocks","components","dashboard","design","grid","interface","layout","screen","site","tiles","ui","ux","view","web","website","window"]},{"name":"east","tags":["arrow","directional","east","maps","navigation","right"]},{"name":"inventory_2","tags":["archive","box","file","inventory","organize","packages","product","stock","storage","supply"]},{"name":"mail_outline","tags":["email","envelop","letter","mail","message","outline","send"]},{"name":"admin_panel_settings","tags":["account","admin","avatar","certified","face","human","panel","people","person","privacy","private","profile","protect","protection","security","settings","shield","user","verified"]},{"name":"mic","tags":["hear","hearing","mic","microphone","noise","record","sound","voice"]},{"name":"calendar_month","tags":["calendar","date","day","event","month","schedule","today"]},{"name":"group","tags":["accounts","committee","face","family","friends","group","humans","network","people","persons","profiles","social","team","users"]},{"name":"picture_as_pdf","tags":["alphabet","as","character","document","file","font","image","letter","multiple","pdf","photo","photography","picture","symbol","text","type"]},{"name":"lock_open","tags":["lock","open","password","privacy","private","protection","safety","secure","security","unlocked"]},{"name":"volume_up","tags":["audio","control","music","sound","speaker","tv","up","volume"]},{"name":"watch_later","tags":["clock","date","later","schedule","time","watch"]},{"name":"grade","tags":["'favorite_news' .","'star_outline'","Duplicate of 'star_boarder'","star_border_purple500'"]},{"name":"receipt_long","tags":["bill","check","document","list","long","paper","paperwork","receipt","record","store","transaction"]},{"name":"local_offer","tags":["deal","discount","offer","price","shop","shopping","store","tag"]},{"name":"room","tags":["destination","direction","location","maps","pin","place","room","stop"]},{"name":"update","tags":["arrow","back","backwards","clock","forward","history","load","refresh","reverse","schedule","time","update"]},{"name":"badge","tags":["account","avatar","badge","card","certified","employee","face","human","identification","name","people","person","profile","security","user","work"]},{"name":"savings","tags":["bank","bill","card","cash","coin","commerce","cost","credit","currency","dollars","finance","money","online","pay","payment","pig","piggy","savings","symbol"]},{"name":"code","tags":["brackets","code","css","develop","developer","engineer","engineering","html","platform"]},{"name":"light_mode","tags":["bright","brightness","day","device","light","lighting","mode","morning","sky","sun","sunny"]},{"name":"receipt","tags":[]},{"name":"circle","tags":["circle","full","geometry","moon"]},{"name":"inventory","tags":["archive","box","clipboard","doc","document","file","inventory","organize","packages","product","stock","supply"]},{"name":"add_shopping_cart","tags":["add","card","cart","cash","checkout","coin","commerce","credit","currency","dollars","money","online","pay","payment","plus","shopping"]},{"name":"contact_support","tags":["?","bubble","chat","comment","communicate","contact","help","info","information","mark","message","punctuation","question","question mark","speech","support","symbol"]},{"name":"category","tags":["categories","category","circle","collection","items","product","sort","square","triangle"]},{"name":"edit_note","tags":["compose","create","draft","edit","editing","input","lines","note","pen","pencil","text","write","writing"]},{"name":"insights","tags":["ai","analytics","artificial","automatic","automation","bar","bars","chart","custom","data","diagram","genai","graph","infographic","insights","intelligence","magic","measure","metrics","smart","spark","sparkle","star","stars","statistics","tracking"]},{"name":"power_settings_new","tags":["info","information","off","on","power","save","settings","shutdown"]},{"name":"campaign","tags":["alert","announcement","campaign","loud","megaphone","microphone","notification","speaker"]},{"name":"format_list_bulleted","tags":["align","alignment","bulleted","doc","edit","editing","editor","format","list","notes","sheet","spreadsheet","text","type","writing"]},{"name":"star_border","tags":["best","bookmark","border","favorite","highlight","outline","ranking","rate","rating","save","star","toggle"]},{"name":"pause","tags":["control","controls","media","music","pause","video"]},{"name":"remove_circle_outline","tags":["block","can","circle","delete","minus","negative","outline","remove","substract","trash"]},{"name":"warning_amber","tags":["!","alert","amber","attention","caution","danger","error","exclamation","important","mark","notification","symbol","triangle","warning"]},{"name":"wifi","tags":["connection","data","internet","network","scan","service","signal","wifi","wireless"]},{"name":"arrow_back_ios_new","tags":["DISABLE_IOS","app","application","arrow","back","chevron","components","direction","disable_ios","interface","ios","left","navigation","new","previous","screen","site","ui","ux","web","website"]},{"name":"restart_alt","tags":["alt","around","arrow","inprogress","load","loading refresh","reboot","renew","repeat","reset","restart"]},{"name":"done_all","tags":["all","approve","check","complete","done","layers","mark","multiple","ok","select","stack","tick","validate","verified","yes"]},{"name":"pets","tags":["animal","cat","dog","hand","paw","pet"]},{"name":"storefront","tags":["business","buy","cafe","commerce","front","market","places","restaurant","retail","sell","shop","shopping","store","storefront"]},{"name":"sort","tags":["filter","find","lines","list","organize","sort"]},{"name":"mode_edit","tags":["compose","create","draft","draw","edit","mode","pen","pencil","write"]},{"name":"list_alt","tags":["alt","box","contained","format","lines","list","order","reorder","stacked","title"]},{"name":"toggle_on","tags":["active","app","application","components","configuration","control","design","disable","inable","inactive","interface","off","on","selection","settings","site","slider","switch","toggle","ui","ux","web","website"]},{"name":"dark_mode","tags":["app","application","dark","device","interface","mode","moon","night","silent","theme","ui","ux","website"]},{"name":"engineering","tags":["body","cogs","cogwheel","construction","engineering","fixing","gears","hat","helmet","human","maintenance","people","person","setting","worker"]},{"name":"explore","tags":["compass","destination","direction","east","explore","location","maps","needle","north","south","travel","west"]},{"name":"bolt","tags":["bolt","electric","energy","fast","flash","lightning","power","thunderbolt"]},{"name":"construction","tags":["build","carpenter","construction","equipment","fix","hammer","improvement","industrial","industry","repair","tools","wrench"]},{"name":"qr_code_scanner","tags":["barcode","camera","code","media","product","qr","quick","response","scanner","smartphone","url","urls"]},{"name":"bookmark","tags":["archive","bookmark","favorite","label","library","read","reading","remember","ribbon","save","tag"]},{"name":"vpn_key","tags":["code","key","lock","network","passcode","password","unlock","vpn"]},{"name":"monetization_on","tags":["bill","card","cash","circle","coin","commerce","cost","credit","currency","dollars","finance","monetization","money","on","online","pay","payment","shopping","symbol"]},{"name":"attach_file","tags":["add","attach","attachment","clip","file","link","mail","media"]},{"name":"timer","tags":["alarm","alert","bell","clock","disabled","duration","enabled","notification","off","on","slash","stop","time","timer","watch"]},{"name":"account_box","tags":["account","avatar","box","face","human","people","person","profile","square","thumbnail","user"]},{"name":"note_add","tags":["+","-doc","add","data","document","drive","file","folder","folders","new","note","page","paper","plus","sheet","slide","symbol","writing"]},{"name":"reorder","tags":["format","lines","list","order","reorder","stacked"]},{"name":"bookmark_border","tags":["archive","bookmark","border","favorite","label","library","read","reading","remember","ribbon","save","tag"]},{"name":"arrow_right","tags":["app","application","arrow","components","direction","interface","navigation","right","screen","site","ui","ux","web","website"]},{"name":"pending_actions","tags":["actions","clipboard","clock","date","doc","document","pending","remember","schedule","time"]},{"name":"smartphone","tags":["Android","OS","call","cell","chat","device","hardware","iOS","mobile","phone","smartphone","tablet","text"]},{"name":"upload_file","tags":["arrow","data","doc","document","download","drive","file","folder","folders","page","paper","sheet","slide","up","upload","writing"]},{"name":"account_tree","tags":["account","analytics","chart","connect","data","diagram","flow","graph","infographic","measure","metrics","process","square","statistics","structure","tracking","tree"]},{"name":"shopping_basket","tags":["add","basket","bill","buy","card","cart","cash","checkout","coin","commerce","credit","currency","dollars","money","online","pay","payment","shopping"]},{"name":"flag","tags":["country","flag","goal","mark","nation","report","start"]},{"name":"apartment","tags":["accommodation","apartment","architecture","building","city","company","estate","flat","home","house","office","places","real","residence","residential","shelter","units","workplace"]},{"name":"restaurant","tags":["breakfast","dining","dinner","eat","food","fork","knife","local","lunch","meal","places","restaurant","spoon","utensils"]},{"name":"people_alt","tags":["accounts","committee","face","family","friends","humans","network","people","persons","profiles","social","team","users"]},{"name":"reply","tags":["arrow","backward","left","mail","message","reply","send","share"]},{"name":"play_circle_outline","tags":["arrow","circle","control","controls","media","music","outline","play","video"]},{"name":"payment","tags":["bill","card","cash","coin","commerce","cost","credit","currency","dollars","finance","money","online","pay","payment","price","shopping","symbol"]},{"name":"sync","tags":["360","around","arrow","arrows","direction","inprogress","load","loading refresh","renew","rotate","sync","turn"]},{"name":"task","tags":["approve","check","complete","data","doc","document","done","drive","file","folder","folders","mark","ok","page","paper","select","sheet","slide","task","tick","validate","verified","writing","yes"]},{"name":"launch","tags":["app","application","arrow","box","components","interface","launch","new","open","screen","site","ui","ux","web","website","window"]},{"name":"menu_open","tags":["app","application","arrow","components","hamburger","interface","left","line","lines","menu","open","screen","site","ui","ux","web","website"]},{"name":"add_box","tags":["add","box","new square","plus","symbol"]},{"name":"drag_indicator","tags":["app","application","circles","components","design","dots","drag","drop","indicator","interface","layout","mobile","monitor","move","phone","screen","shape","shift","site","tablet","ui","ux","web","website","window"]},{"name":"supervisor_account","tags":["account","avatar","control","face","human","parental","parental control","parents","people","person","profile","supervised","supervisor","user"]},{"name":"touch_app","tags":["app","command","fingers","gesture","hand","press","tap","touch"]},{"name":"pending","tags":["circle","dots","loading","pending","progress","wait","waiting"]},{"name":"zoom_in","tags":["big","bigger","find","glass","grow","in","look","magnify","magnifying","plus","scale","search","see","size","zoom"]},{"name":"manage_search","tags":["glass","history","magnifying","manage","search","text"]},{"name":"remove_circle","tags":["block","can","circle","delete","minus","negative","remove","substract","trash"]},{"name":"group_add","tags":["accounts","add","committee","face","family","friends","group","humans","increase","more","network","people","persons","plus","profiles","social","team","users"]},{"name":"chat_bubble_outline","tags":["bubble","chat","comment","communicate","feedback","message","outline","speech"]},{"name":"assessment","tags":["analytics","assessment","bar","chart","data","diagram","graph","infographic","measure","metrics","statistics","tracking"]},{"name":"priority_high","tags":["!","alert","attention","caution","danger","error","exclamation","high","important","mark","notification","symbol","warning"]},{"name":"push_pin","tags":["location","marker","pin","place","push","remember","save"]},{"name":"feed","tags":["article","feed","headline","information","news","newspaper","paper","public","social","timeline"]},{"name":"leaderboard","tags":["analytics","bar","bars","chart","data","diagram","graph","infographic","leaderboard","measure","metrics","statistics","tracking"]},{"name":"summarize","tags":["doc","document","list","menu","note","report","summary"]},{"name":"block","tags":["avoid","block","cancel","close","entry","exit","no","prohibited","quit","remove","stop"]},{"name":"event_available","tags":["approve","available","calendar","check","complete","date","done","event","mark","ok","schedule","select","tick","time","validate","verified","yes"]},{"name":"thumb_up_off_alt","tags":["alt","disabled","enabled","favorite","fingers","gesture","hand","hands","like","off","offline","on","rank","ranking","rate","rating","slash","thumb","up"]},{"name":"directions_car","tags":["automobile","car","cars","direction","directions","maps","public","transportation","vehicle"]},{"name":"open_in_full","tags":["action","arrow","arrows","expand","full","grow","in","move","open"]},{"name":"auto_stories","tags":["auto","book","flipping","pages","stories"]},{"name":"post_add","tags":["+","add","data","doc","document","drive","file","folder","folders","page","paper","plus","post","sheet","slide","text","writing"]},{"name":"calculate","tags":["+","-","=","calculate","count","finance calculator","math"]},{"name":"alternate_email","tags":["@","address","alternate","contact","email","tag"]},{"name":"create","tags":["compose","create","edit","editing","input","new","pen","pencil","write","writing"]},{"name":"cloud_upload","tags":["app","application","arrow","backup","cloud","connection","download","drive","files","folders","internet","network","sky","storage","up","upload"]},{"name":"local_fire_department","tags":["911","climate","department","fire","firefighter","flame","heat","home","hot","nest","thermostat"]},{"name":"bar_chart","tags":["analytics","bar","chart","data","diagram","graph","infographic","measure","metrics","statistics","tracking"]},{"name":"password","tags":["key","login","password","pin","security","star","unlock"]},{"name":"collections","tags":["album","collections","gallery","image","landscape","library","mountain","mountains","photo","photography","picture","stack"]},{"name":"preview","tags":["design","eye","layout","preview","reveal","screen","see","show","site","view","web","website","window","www"]},{"name":"star_outline","tags":["bookmark","favorite","half","highlight","ranking","rate","rating","save","star","toggle"]},{"name":"exit_to_app","tags":["app","application","arrow","components","design","exit","export","interface","layout","leave","mobile","monitor","move","output","phone","screen","site","tablet","to","ui","ux","web","website","window"]},{"name":"done_outline","tags":["all","approve","check","complete","done","mark","ok","outline","select","tick","validate","verified","yes"]},{"name":"psychology","tags":["behavior","body","brain","cognitive","function","gear","head","human","intellectual","mental","mind","people","person","preferences","psychiatric","psychology","science","settings","social","therapy","thinking","thoughts"]},{"name":"assignment_ind","tags":["account","assignment","clipboard","doc","document","face","ind","people","person","profile","user"]},{"name":"volunteer_activism","tags":["activism","donation","fingers","gesture","giving","hand","hands","heart","love","sharing","volunteer"]},{"name":"navigate_before","tags":["arrow","arrows","before","direction","left","navigate"]},{"name":"published_with_changes","tags":["approve","arrow","arrows","changes","check","complete","done","inprogress","load","loading","mark","ok","published","refresh","renew","replace","rotate","select","tick","validate","verified","with","yes"]},{"name":"add_a_photo","tags":["+","a photo","add","camera","lens","new","photography","picture","plus","symbol"]},{"name":"auto_awesome","tags":["adjust","ai","artificial","automatic","automation","custom","edit","editing","enhance","genai","intelligence","magic","smart","spark","sparkle","star","stars"]},{"name":"card_giftcard","tags":["account","balance","bill","card","cart","cash","certificate","coin","commerce","credit","currency","dollars","gift","giftcard","money","online","pay","payment","present","shopping"]},{"name":"fullscreen","tags":["adjust","app","application","components","full","fullscreen","interface","screen","site","size","ui","ux","view","web","website"]},{"name":"sell","tags":["bill","card","cart","cash","coin","commerce","credit","currency","dollars","money","online","pay","payment","price","sell","shopping","tag"]},{"name":"checklist","tags":["align","alignment","approve","check","checklist","complete","doc","done","edit","editing","editor","format","list","mark","notes","ok","select","sheet","spreadsheet","text","tick","type","validate","verified","writing","yes"]},{"name":"view_in_ar","tags":["3d","ar","augmented","cube","daydream","headset","in","reality","square","view","vr"]},{"name":"undo","tags":["arrow","backward","mail","previous","redo","repeat","rotate","undo"]},{"name":"arrow_drop_up","tags":["app","application","arrow","components","direction","drop","interface","navigation","screen","site","ui","up","ux","web","website"]},{"name":"feedback","tags":["!","alert","announcement","attention","bubble","caution","chat","comment","communicate","danger","error","exclamation","feedback","important","mark","message","notification","speech","symbol","warning"]},{"name":"health_and_safety","tags":["+","add","and","certified","cross","health","home","nest","plus","privacy","private","protect","protection","safety","security","shield","symbol","verified"]},{"name":"work_outline","tags":["bag","baggage","briefcase","business","case","job","suitcase","work"]},{"name":"unfold_more","tags":["arrow","arrows","chevron","collapse","direction","down","expand","expandable","list","more","navigation","unfold"]},{"name":"travel_explore","tags":["earth","explore","find","glass","global","globe","look","magnify","magnifying","map","network","planet","search","see","social","space","travel","web","world"]},{"name":"palette","tags":["art","color","colors","filters","paint","palette"]},{"name":"keyboard_arrow_right","tags":["arrow","arrows","keyboard","right"]},{"name":"double_arrow","tags":["arrow","arrows","direction","double","multiple","navigation","right"]},{"name":"computer","tags":["Android","OS","chrome","computer","desktop","device","hardware","iOS","mac","monitor","web","window"]},{"name":"timeline","tags":["data","history","line","movement","point","points","timeline","tracking","trending","zigzag"]},{"name":"thumb_up_alt","tags":["agreed","approved","confirm","correct","favorite","feedback","good","happy","like","okay","positive","satisfaction","social","thumb","up","vote","yes"]},{"name":"signal_cellular_alt","tags":["alt","analytics","bar","cell","cellular","chart","data","diagram","graph","infographic","internet","measure","metrics","mobile","network","phone","signal","statistics","tracking","wifi","wireless"]},{"name":"replay","tags":["arrow","arrows","control","controls","music","refresh","renew","repeat","replay","video"]},{"name":"swap_horiz","tags":["arrow","arrows","back","forward","horizontal","swap"]},{"name":"volume_off","tags":["audio","control","disabled","enabled","low","music","off","on","slash","sound","speaker","tv","volume"]},{"name":"forum","tags":["bubble","chat","comment","communicate","community","conversation","feedback","forum","hub","message","speech"]},{"name":"skip_next","tags":["arrow","control","controls","music","next","play","previous","skip","video"]},{"name":"water_drop","tags":["drink","drop","droplet","eco","liquid","nature","ocean","rain","social","water"]},{"name":"assignment_turned_in","tags":["approve","assignment","check","clipboard","complete","doc","document","done","in","mark","ok","select","tick","turn","validate","verified","yes"]},{"name":"library_books","tags":["add","album","audio","book","books","collection","library","read","reading"]},{"name":"maps_home_work","tags":["building","home","house","maps","office","work"]},{"name":"dns","tags":["address","bars","dns","domain","information","ip","list","lookup","name","server","system"]},{"name":"sync_alt","tags":["alt","arrow","arrows","horizontal","internet","sync","technology","up","update","wifi"]},{"name":"how_to_reg","tags":["approve","ballot","check","complete","done","election","how","mark","ok","poll","register","registration","select","tick","to reg","validate","verified","vote","yes"]},{"name":"notifications_none","tags":["alarm","alert","bell","none","notifications","notify","reminder","sound"]},{"name":"stars","tags":["achievement","bookmark","circle","favorite","highlight","important","marked","ranking","rate","rating rank","reward","save","saved","shape","special","star"]},{"name":"flight_takeoff","tags":["airport","departed","departing","flight","fly","landing","plane","takeoff","transportation","travel"]},{"name":"label","tags":["favorite","indent","label","library","mail","remember","save","stamp","sticker","tag"]},{"name":"devices","tags":["Android","OS","computer","desktop","device","hardware","iOS","laptop","mobile","monitor","phone","tablet","watch","wearable","web"]},{"name":"chat_bubble","tags":["bubble","chat","comment","communicate","feedback","message","speech"]},{"name":"emoji_emotions","tags":["+","add","emoji","emotions","expressions","face","feelings","glad","happiness","happy","icon","icons","insert","like","mood","new","person","pleased","plus","smile","smiling","social","survey","symbol"]},{"name":"remove_red_eye","tags":["eye","iris","look","looking","preview","red","remove","see","sight","vision"]},{"name":"content_paste","tags":["clipboard","content","copy","cut","doc","document","file","multiple","past"]},{"name":"folder_open","tags":["data","doc","document","drive","file","folder","folders","open","sheet","slide","storage"]},{"name":"text_snippet","tags":["data","doc","document","file","note","notes","snippet","storage","text","writing"]},{"name":"tips_and_updates","tags":["ai","alert","and","announcement","artificial","automatic","automation","custom","electricity","genai","idea","info","information","intelligence","light","lightbulb","magic","smart","spark","sparkle","star","tips","updates"]},{"name":"my_location","tags":["destination","direction","location","maps","navigation","pin","place","point","stop"]},{"name":"textsms","tags":["bubble","chat","comment","communicate","dots","feedback","message","speech","textsms"]},{"name":"cloud","tags":["cloud","connection","internet","network","sky","upload"]},{"name":"sports_esports","tags":["controller","entertainment","esports","game","gamepad","gaming","hobby","online","social","sports","video"]},{"name":"security","tags":["certified","privacy","private","protect","protection","security","shield","verified"]},{"name":"request_quote","tags":["bill","card","cash","coin","commerce","cost","credit","currency","dollars","finance","money","online","pay","payment","price","quote","request","shopping","symbol"]},{"name":"toggle_off","tags":["active","app","application","components","configuration","control","design","disable","inable","inactive","interface","off","on","selection","settings","site","slider","switch","toggle","ui","ux","web","website"]},{"name":"book","tags":["book","bookmark","favorite","label","library","read","reading","remember","ribbon","save","tag"]},{"name":"contact_page","tags":["account","avatar","contact","data","doc","document","drive","face","file","folder","folders","human","page","people","person","profile","sheet","slide","storage","user","writing"]},{"name":"speed","tags":["arrow","control","controls","fast","gauge","meter","motion","music","slow","speed","speedometer","velocity","video"]},{"name":"bug_report","tags":["animal","bug","fix","insect","issue","problem","report","testing","virus","warning"]},{"name":"space_dashboard","tags":["cards","dashboard","format","grid","layout","rectangle","shapes","space","squares","web","website"]},{"name":"fiber_manual_record","tags":["circle","dot","fiber","manual","play","record","watch"]},{"name":"report","tags":["!","alert","attention","caution","danger","error","exclamation","important","mark","notification","octagon","report","symbol","warning"]},{"name":"alarm","tags":["alarm","alert","bell","clock","countdown","date","notification","schedule","time"]},{"name":"cached","tags":["around","arrows","cache","cached","inprogress","load","loading refresh","renew","rotate"]},{"name":"translate","tags":["language","speaking","speech","translate","translator","words"]},{"name":"pan_tool","tags":["fingers","gesture","hand","hands","human","move","pan","scan","stop","tool"]},{"name":"gavel","tags":["agreement","contract","court","document","gavel","government","judge","law","mallet","official","police","rule","rules","terms"]},{"name":"settings_suggest","tags":["ai","artificial","automatic","automation","change","custom","details","gear","genai","intelligence","magic","options","recommendation","service","settings","smart","spark","sparkle","star","suggest","suggestion","system"]},{"name":"file_copy","tags":["content","copy","cut","doc","document","duplicate","file","multiple","past"]},{"name":"edit_calendar","tags":["calendar","compose","create","date","day","draft","edit","editing","event","month","pen","pencil","schedule","write","writing"]},{"name":"contact_mail","tags":["account","address","avatar","communicate","contact","email","face","human","info","information","mail","message","people","person","profile","user"]},{"name":"quiz","tags":["?","assistance","faq","help","info","information","punctuation","question mark","quiz","support","symbol","test"]},{"name":"supervised_user_circle","tags":["account","avatar","circle","control","face","human","parental","parents","people","person","profile","supervised","supervisor","user"]},{"name":"cloud_download","tags":["app","application","arrow","backup","cloud","connection","down","download","drive","files","folders","internet","network","sky","storage","upload"]},{"name":"stop","tags":["control","controls","music","pause","play","square","stop","video"]},{"name":"person_search","tags":["account","avatar","face","find","glass","human","look","magnify","magnifying","people","person","profile","search","user"]},{"name":"location_city","tags":["apartments","architecture","buildings","business","city","estate","home","landscape","location","place","real","residence","residential","shelter","town","urban"]},{"name":"sentiment_very_satisfied","tags":["emotions","expressions","face","feelings","glad","happiness","happy","like","mood","person","pleased","satisfied","sentiment","smile","smiling","survey","very"]},{"name":"ios_share","tags":["arrow","export","ios","send","share","up"]},{"name":"minimize","tags":["app","application","components","design","interface","line","minimize","screen","shape","site","ui","ux","web","website"]},{"name":"qr_code","tags":["barcode","camera","code","media","product","qr","quick","response","smartphone","url","urls"]},{"name":"sentiment_satisfied_alt","tags":["account","alt","emoji","face","happy","human","people","person","profile","satisfied","sentiment","smile","user"]},{"name":"local_mall","tags":["bag","bill","building","business","buy","card","cart","cash","coin","commerce","credit","currency","dollars","handbag","local","mall","money","online","pay","payment","shop","shopping","store","storefront"]},{"name":"qr_code_2","tags":["barcode","camera","code","media","product","qr","quick","response","smartphone","url","urls"]},{"name":"flight","tags":["air","airplane","airport","flight","plane","transportation","travel","trip"]},{"name":"desktop_windows","tags":["Android","OS","chrome","desktop","device","display","hardware","iOS","mac","monitor","screen","television","tv","web","window","windows"]},{"name":"music_note","tags":["audio","audiotrack","key","music","note","sound","track"]},{"name":"sentiment_satisfied","tags":["emotions","expressions","face","feelings","glad","happiness","happy","like","mood","person","pleased","satisfied","sentiment","smile","smiling","survey"]},{"name":"android","tags":["android","character","logo","mascot","toy"]},{"name":"accessibility","tags":["accessibility","accessible","body","handicap","help","human","people","person"]},{"name":"backspace","tags":["arrow","back","backspace","cancel","clear","correct","delete","erase","remove"]},{"name":"precision_manufacturing","tags":["arm","automatic","chain","conveyor","crane","factory","industry","machinery","manufacturing","mechanical","precision","production","repairing","robot","supply","warehouse"]},{"name":"drag_handle","tags":["app","application ui","components","design","drag","handle","interface","layout","menu","move","screen","site","ui","ux","web","website","window"]},{"name":"smart_display","tags":["airplay","cast","chrome","connect","device","display","play","screen","screencast","smart","stream","television","tv","video","wireless"]},{"name":"near_me","tags":["destination","direction","location","maps","me","navigation","near","pin","place","point","stop"]},{"name":"west","tags":["arrow","directional","left","maps","navigation","west"]},{"name":"get_app","tags":["app","arrow","arrows","down","download","downloads","export","get","install","play","upload"]},{"name":"person_add_alt","tags":["+","account","add","face","human","people","person","plus","profile","user"]},{"name":"fitness_center","tags":["athlete","center","dumbbell","exercise","fitness","gym","hobby","places","sport","weights","workout"]},{"name":"shield","tags":["certified","privacy","private","protect","protection","security","shield","verified"]},{"name":"message","tags":["bubble","chat","comment","communicate","feedback","message","speech"]},{"name":"rocket_launch","tags":["launch","rocket","space","spaceship","takeoff"]},{"name":"record_voice_over","tags":["account","face","human","over","people","person","profile","record","recording","speak","speaking","speech","transcript","user","voice"]},{"name":"add_task","tags":["+","add","approve","check","circle","completed","increase","mark","ok","plus","select","task","tick","yes"]},{"name":"drive_file_rename_outline","tags":["compose","create","draft","drive","edit","editing","file","input","marker","pen","pencil","rename","write","writing"]},{"name":"insert_drive_file","tags":["doc","drive","file","format","insert","sheet","slide"]},{"name":"question_mark","tags":["?","assistance","help","info","information","punctuation","question mark","support","symbol"]},{"name":"trending_flat","tags":["arrow","change","data","flat","metric","movement","rate","right","track","tracking","trending"]},{"name":"handyman","tags":["build","construction","fix","hammer","handyman","repair","screw","screwdriver","tools"]},{"name":"emoji_objects","tags":["bulb","creative","emoji","idea","light","objects","solution","thinking"]},{"name":"military_tech","tags":["army","award","badge","honor","medal","merit","military","order","privilege","prize","rank","reward","ribbon","soldier","star","status","tech","trophy","win","winner"]},{"name":"hourglass_empty","tags":["countdown","empty","hourglass","loading","minutes","time","wait","waiting"]},{"name":"help_center","tags":["?","assistance","center","help","info","information","punctuation","question mark","recent","restore","support","symbol"]},{"name":"science","tags":["beaker","chemical","chemistry","experiment","flask","glass","laboratory","research","science","tube"]},{"name":"storage","tags":["computer","data","drive","memory","storage"]},{"name":"movie","tags":["cinema","film","media","movie","slate","video"]},{"name":"accessibility_new","tags":["accessibility","accessible","body","handicap","help","human","new","people","person"]},{"name":"workspace_premium","tags":["certification","degree","ecommerce","guarantee","medal","permit","premium","ribbon","verification","workspace"]},{"name":"directions_run","tags":["body","directions","human","jogging","maps","people","person","route","run","running","walk"]},{"name":"rule","tags":["approve","check","complete","done","incomplete","line","mark","missing","no","ok","rule","select","tick","validate","verified","wrong","x","yes"]},{"name":"thumb_down","tags":["ate","dislike","down","favorite","fingers","gesture","hand","hands","like","rank","ranking","rating","thumb"]},{"name":"event_note","tags":["calendar","date","event","note","schedule","text","time","writing"]},{"name":"contacts","tags":["account","avatar","call","cell","contacts","face","human","info","information","mobile","people","person","phone","profile","user"]},{"name":"comment","tags":["bubble","chat","comment","communicate","feedback","message","outline","speech"]},{"name":"restaurant_menu","tags":["book","dining","eat","food","fork","knife","local","meal","menu","restaurant","spoon"]},{"name":"add_photo_alternate","tags":["+","add","alternate","image","landscape","mountain","mountains","new","photo","photography","picture","plus","symbol"]},{"name":"confirmation_number","tags":["admission","confirmation","entertainment","event","number","ticket"]},{"name":"sticky_note_2","tags":["2","bookmark","mark","message","note","paper","sticky","text","writing"]},{"name":"format_quote","tags":["doc","edit","editing","editor","format","quotation","quote","sheet","spreadsheet","text","type","writing"]},{"name":"history_edu","tags":["document","edu","education","feather","history","letter","paper","pen","quill","school","story","tools","write","writing"]},{"name":"business_center","tags":["bag","baggage","briefcase","business","case","center","places","purse","suitcase","work"]},{"name":"upload","tags":["arrow","arrows","download","drive","up","upload"]},{"name":"skip_previous","tags":["arrow","control","controls","music","next","play","previous","skip","video"]},{"name":"archive","tags":["archive","inbox","mail","store"]},{"name":"wb_sunny","tags":["balance","bright","light","lighting","sun","sunny","wb","white"]},{"name":"cake","tags":["add","baked","birthday","cake","candles","celebration","dessert","food","frosting","new","party","pastries","pastry","plus","social","sweet","symbol"]},{"name":"attachment","tags":["attach","attachment","clip","compose","file","image","link"]},{"name":"source","tags":["code","composer","content","creation","data","doc","document","file","folder","mode","source","storage","view"]},{"name":"settings_applications","tags":["application","change","details","gear","info","information","options","personal","service","settings"]},{"name":"dashboard_customize","tags":["cards","customize","dashboard","format","layout","rectangle","shapes","square","web","website"]},{"name":"find_in_page","tags":["data","doc","document","drive","file","find","folder","folders","glass","in","look","magnify","magnifying","page","paper","search","see","sheet","slide","writing"]},{"name":"support","tags":["assist","buoy","help","life","lifebuoy","rescue","safe","safety","support"]},{"name":"ads_click","tags":["ads","browser","click","clicks","cursor","internet","target","traffic","web"]},{"name":"new_releases","tags":["approve","award","check","checkmark","complete","done","new","notification","ok","release","releases","select","star","symbol","tick","verification","verified","warning","yes"]},{"name":"flutter_dash","tags":["bird","dash","flutter","mascot"]},{"name":"playlist_add","tags":["+","add","collection","list","music","new","playlist","plus","symbol"]},{"name":"save_alt","tags":["alt","arrow","disk","document","down","file","floppy","multimedia","save"]},{"name":"close_fullscreen","tags":["action","arrow","arrows","close","collapse","direction","full","fullscreen","minimize","screen"]},{"name":"credit_score","tags":["approve","bill","card","cash","check","coin","commerce","complete","cost","credit","currency","dollars","done","finance","loan","mark","money","ok","online","pay","payment","score","select","symbol","tick","validate","verified","yes"]},{"name":"layers","tags":["arrange","disabled","enabled","interaction","layers","maps","off","on","overlay","pages","slash"]},{"name":"redeem","tags":["bill","card","cart","cash","certificate","coin","commerce","credit","currency","dollars","gift","giftcard","money","online","pay","payment","present","redeem","shopping"]},{"name":"spa","tags":["aromatherapy","flower","healthcare","leaf","massage","meditation","nature","petals","places","relax","spa","wellbeing","wellness"]},{"name":"announcement","tags":["!","alert","announcement","attention","bubble","caution","chat","comment","communicate","danger","error","exclamation","feedback","important","mark","message","notification","speech","symbol","warning"]},{"name":"keyboard_backspace","tags":["arrow","back","backspace","keyboard","left"]},{"name":"loyalty","tags":["benefits","card","credit","heart","loyalty","membership","miles","points","program","subscription","tag","travel","trip"]},{"name":"swap_vert","tags":["arrow","arrows","direction","down","navigation","swap","up","vert","vertical"]},{"name":"sentiment_dissatisfied","tags":["angry","disappointed","dislike","dissatisfied","emotions","expressions","face","feelings","frown","mood","person","sad","sentiment","survey","unhappy","unsatisfied","upset"]},{"name":"medical_services","tags":["aid","bag","briefcase","emergency","first","kit","medical","medicine","services"]},{"name":"view_headline","tags":["design","format","grid","headline","layout","paragraph","text","view","website"]},{"name":"arrow_circle_right","tags":["arrow","circle","direction","navigation","right"]},{"name":"format_list_numbered","tags":["align","alignment","digit","doc","edit","editing","editor","format","list","notes","number","numbered","sheet","spreadsheet","symbol","text","type","writing"]},{"name":"phone_android","tags":["OS","android","cell","device","hardware","iOS","mobile","phone","tablet"]},{"name":"sms","tags":["3","bubble","chat","communication","conversation","dots","message","more","service","sms","speech","three"]},{"name":"restore","tags":["arrow","back","backwards","clock","date","history","refresh","renew","restore","reverse","rotate","schedule","time","turn"]},{"name":"policy","tags":["certified","find","glass","legal","look","magnify","magnifying","policy","privacy","private","protect","protection","search","security","see","shield","verified"]},{"name":"dangerous","tags":["broken","danger","dangerous","fix","no","sign","stop","update","warning","wrong","x"]},{"name":"battery_full","tags":["battery","cell","charge","full","mobile","power"]},{"name":"euro_symbol","tags":["bill","card","cash","coin","commerce","cost","credit","currency","dollars","euro","finance","money","online","pay","payment","symbol"]},{"name":"query_stats","tags":["analytics","chart","data","diagram","find","glass","graph","infographic","line","look","magnify","magnifying","measure","metrics","query","search","see","statistics","stats","tracking"]},{"name":"group_work","tags":["alliance","collaboration","group","partnership","team","teamwork","together","work"]},{"name":"expand_circle_down","tags":["arrow","arrows","chevron","circle","collapse","direction","down","expand","expandable","list","more"]},{"name":"sensors","tags":["connection","network","scan","sensors","signal","wireless"]},{"name":"keyboard_arrow_up","tags":["arrow","arrows","keyboard","up"]},{"name":"brush","tags":["art","brush","design","draw","edit","editing","paint","painting","tool"]},{"name":"meeting_room","tags":["building","door","doorway","entrance","home","house","interior","meeting","office","open","places","room"]},{"name":"key","tags":["key","lock","password","unlock"]},{"name":"house","tags":["architecture","building","estate","family","home","homepage","house","place","places","real","residence","residential","shelter"]},{"name":"lunch_dining","tags":["breakfast","dining","dinner","drink","fastfood","food","hamburger","lunch","meal"]},{"name":"table_chart","tags":["analytics","bar","bars","chart","data","diagram","graph","infographic grid","measure","metrics","statistics","table","tracking"]},{"name":"border_color","tags":["all","border","doc","edit","editing","editor","pen","pencil","sheet","spreadsheet","stroke","text","type","writing"]},{"name":"compare_arrows","tags":["arrow","arrows","collide","compare","direction","left","pressure","push","right","together"]},{"name":"south","tags":["arrow","directional","down","maps","navigation","south"]},{"name":"directions_walk","tags":["body","direction","directions","human","jogging","maps","people","person","route","run","walk"]},{"name":"arrow_left","tags":["app","application","arrow","components","direction","interface","left","navigation","screen","site","ui","ux","web","website"]},{"name":"tag","tags":["hash","hashtag","key","media","number","pound","social","tag","trend"]},{"name":"change_circle","tags":["around","arrows","change","circle","direction","navigation","rotate"]},{"name":"subject","tags":["alignment","doc","document","email","full","justify","list","note","subject","text","writing"]},{"name":"sentiment_very_dissatisfied","tags":["angry","disappointed","dislike","dissatisfied","emotions","expressions","face","feelings","mood","person","sad","sentiment","sorrow","survey","unhappy","unsatisfied","upset","very"]},{"name":"local_hospital","tags":["911","aid","cross","emergency","first","hospital","local","medicine"]},{"name":"table_view","tags":["format","grid","group","layout","multiple","table","view"]},{"name":"disabled_by_default","tags":["box","by","cancel","close","default","disabled","exit","no","quit","remove","square","stop","x"]},{"name":"notification_important","tags":["!","active","alarm","alert","attention","bell","caution","chime","danger","error","exclamation","important","mark","notification","notifications","notify","reminder","ring","sound","symbol","warning"]},{"name":"celebration","tags":["activity","birthday","celebration","event","fun","party"]},{"name":"laptop","tags":["Android","OS","chrome","computer","desktop","device","hardware","iOS","laptop","mac","monitor","web","windows"]},{"name":"loop","tags":["around","arrow","arrows","direction","inprogress","load","loading refresh","loop","music","navigation","renew","rotate","turn"]},{"name":"nightlight_round","tags":["dark","half","light","mode","moon","night","nightlight","round"]},{"name":"privacy_tip","tags":["alert","announcement","assistance","certified","details","help","i","info","information","privacy","private","protect","protection","security","service","shield","support","tip","verified"]},{"name":"import_contacts","tags":["address","book","contacts","import","info","information","open"]},{"name":"equalizer","tags":["adjustment","analytics","chart","data","equalizer","graph","measure","metrics","music","noise","sound","static","statistics","tracking","volume"]},{"name":"app_registration","tags":["app","apps","edit","pencil","register","registration"]},{"name":"keyboard_double_arrow_right","tags":["arrow","arrows","direction","double","multiple","navigation","right"]},{"name":"handshake","tags":["agreement","hand","hands","partnership","shake"]},{"name":"corporate_fare","tags":["architecture","building","business","corporate","estate","fare","organization","place","real","residence","residential","shelter"]},{"name":"local_library","tags":["book","community learning","library","local","read"]},{"name":"https","tags":["https","lock","locked","password","privacy","private","protection","safety","secure","security"]},{"name":"euro","tags":["bill","card","cash","coin","commerce","cost","credit","currency","dollars","euro","euros","finance","money","online","pay","payment","price","shopping","symbol"]},{"name":"coronavirus","tags":["19","bacteria","coronavirus","covid","disease","germs","illness","sick","social"]},{"name":"price_check","tags":["approve","bill","card","cash","check","coin","commerce","complete","cost","credit","currency","dollars","done","finance","mark","money","ok","online","pay","payment","price","select","shopping","symbol","tick","validate","verified","yes"]},{"name":"live_tv","tags":["Android","OS","antennas hardware","chrome","desktop","device","iOS","live","mac","monitor","movie","play","stream","television","tv","web","window"]},{"name":"park","tags":["attraction","fresh","local","nature","outside","park","plant","tree"]},{"name":"toc","tags":["content","format","lines","list","order","reorder","stacked","table","title","titles","toc"]},{"name":"track_changes","tags":["bullseye","changes","circle","evolve","lines","movement","rotate","shift","target","track"]},{"name":"arrow_circle_up","tags":["arrow","circle","direction","navigation","up"]},{"name":"emoji_people","tags":["arm","body","emoji","greeting","human","people","person","social","waving"]},{"name":"flash_on","tags":["bolt","disabled","electric","enabled","fast","flash","lightning","off","on","slash","thunderbolt"]},{"name":"copyright","tags":["alphabet","c","character","copyright","emblem","font","legal","letter","owner","symbol","text"]},{"name":"bookmarks","tags":["bookmark","bookmarks","favorite","label","layers","library","multiple","read","reading","remember","ribbon","save","stack","tag"]},{"name":"ac_unit","tags":["ac","air","cold","conditioner","flake","snow","temperature","unit","weather","winter"]},{"name":"contact_phone","tags":["account","avatar","call","communicate","contact","face","human","info","information","message","mobile","people","person","phone","profile","user"]},{"name":"keyboard_arrow_left","tags":["arrow","arrows","keyboard","left"]},{"name":"medication","tags":["doctor","drug","emergency","hospital","medication","medicine","pharmacy","pills","prescription"]},{"name":"grading","tags":["'favorite'_new'. ' Remove this icon & keep 'star'.","'star_boarder'","'star_border_purple500'","'star_outline'","'star_purple500'","'star_rate'","Same as 'star'"]},{"name":"keyboard_return","tags":["arrow","back","keyboard","left","return"]},{"name":"api","tags":["api","developer","development","enterprise","software"]},{"name":"smart_toy","tags":["bot","droid","games","robot","smart","toy"]},{"name":"input","tags":["arrow","box","download","input","login","move","right"]},{"name":"self_improvement","tags":["body","calm","care","chi","human","improvement","meditate","meditation","people","person","relax","self","sitting","wellbeing","yoga","zen"]},{"name":"live_help","tags":["?","assistance","bubble","chat","comment","communicate","help","info","information","live","message","punctuation","question mark","recent","restore","speech","support","symbol"]},{"name":"query_builder","tags":["builder","clock","date","query","schedule","time"]},{"name":"perm_media","tags":["collection","data","doc","document","file","folder","folders","image","landscape","media","mountain","mountains","perm","photo","photography","picture","storage"]},{"name":"download_for_offline","tags":["arrow","circle","down","download","for offline","install","upload"]},{"name":"view_module","tags":["design","format","grid","layout","module","square","squares","stacked","view","website"]},{"name":"pin","tags":["1","2","3","digit","key","login","logout","number","password","pattern","pin","security","star","symbol","unlock"]},{"name":"fast_forward","tags":["control","fast","forward","media","music","play","speed","time","tv","video"]},{"name":"forward_to_inbox","tags":["arrow","arrows","directions","email","envelop","forward","inbox","letter","mail","message","navigation","outgoing","right","send","to"]},{"name":"person_remove","tags":["account","avatar","delete","face","human","minus","people","person","profile","remove","unfriend","user"]},{"name":"local_atm","tags":["atm","bill","card","cart","cash","coin","commerce","credit","currency","dollars","local","money","online","pay","payment","shopping","symbol"]},{"name":"star_half","tags":["achievement","bookmark","favorite","half","highlight","important","marked","ranking","rate","rating rank","reward","save","saved","shape","special","star","toggle"]},{"name":"build_circle","tags":["adjust","build","circle","fix","repair","tool","wrench"]},{"name":"redo","tags":["arrow","backward","forward","next","redo","repeat","rotate","undo"]},{"name":"web","tags":["browser","internet","page","screen","site","web","website","www"]},{"name":"north_east","tags":["arrow","east","maps","navigation","noth","right","up"]},{"name":"north","tags":["arrow","directional","maps","navigation","north","up"]},{"name":"cottage","tags":["architecture","beach","cottage","estate","home","house","lake","lodge","maps","place","real","residence","residential","stay","traveling"]},{"name":"local_activity","tags":["activity","event","event ticket","local","star","things","ticket"]},{"name":"currency_exchange","tags":["360","around","arrow","arrows","cash","coin","commerce","currency","direction","dollars","exchange","inprogress","money","pay","renew","rotate","sync","turn","universal"]},{"name":"video_library","tags":["arrow","collection","library","play","video"]},{"name":"hourglass_bottom","tags":["bottom","countdown","half","hourglass","loading","minute","minutes","time","wait","waiting"]},{"name":"headphones","tags":["accessory","audio","device","ear","earphone","headphones","headset","listen","music","sound"]},{"name":"zoom_out","tags":["find","glass","look","magnify","magnifying","minus","negative","out","scale","search","see","size","small","smaller","zoom"]},{"name":"poll","tags":["analytics","bar","bars","chart","data","diagram","graph","infographic","measure","metrics","poll","statistics","survey","tracking","vote"]},{"name":"perm_contact_calendar","tags":["account","calendar","contact","date","face","human","information","people","perm","person","profile","schedule","time","user"]},{"name":"forward","tags":["arrow","forward","mail","message","playback","right","sent"]},{"name":"person_pin","tags":["account","avatar","destination","direction","face","human","location","maps","people","person","pin","place","profile","stop","user"]},{"name":"home_work","tags":["architecture","building","estate","home","place","real","residence","residential","shelter","work"]},{"name":"playlist_add_check","tags":["add","approve","check","collection","complete","done","list","mark","music","ok","playlist","select","tick","validate","verified","yes"]},{"name":"local_cafe","tags":["bottle","cafe","coffee","cup","drink","food","restaurant","tea"]},{"name":"ondemand_video","tags":["Android","OS","chrome","demand","desktop","device","hardware","iOS","mac","monitor","ondemand","play","television","tv","video","web","window"]},{"name":"design_services","tags":["compose","create","design","draft","edit","editing","input","pen","pencil","ruler","service","write","writing"]},{"name":"looks_one","tags":["1","digit","looks","numbers","square","symbol"]},{"name":"backup","tags":["arrow","backup","cloud","data","drive","files folders","storage","up","upload"]},{"name":"newspaper","tags":["article","data","doc","document","drive","file","folder","folders","magazine","media","news","newspaper","notes","page","paper","sheet","slide","text","writing"]},{"name":"memory","tags":["card","chip","digital","memory","micro","processor","sd","storage"]},{"name":"open_with","tags":["arrow","arrows","direction","expand","move","open","pan","with"]},{"name":"content_cut","tags":["content","copy","cut","doc","document","file","past","scissors","trim"]},{"name":"keyboard","tags":["computer","device","hardware","input","keyboard","keypad","letter","office","text","type"]},{"name":"hourglass_top","tags":["countdown","half","hourglass","loading","minute","minutes","time","top","wait","waiting"]},{"name":"settings_phone","tags":["call","cell","contact","device","hardware","mobile","phone","settings","telephone"]},{"name":"rss_feed","tags":["application","blog","connection","data","feed","internet","network","rss","service","signal","website","wifi","wireless"]},{"name":"first_page","tags":["arrow","back","chevron","first","left","page","rewind"]},{"name":"delivery_dining","tags":["delivery","dining","food","meal","restaurant","scooter","takeout","transportation","vehicle","vespa"]},{"name":"rate_review","tags":["comment","feedback","pen","pencil","rate","review","stars","write"]},{"name":"control_point","tags":["+","add","circle","control","plus","point"]},{"name":"gpp_good","tags":["certified","check","good","gpp","ok","pass","security","shield","sim","tick"]},{"name":"circle_notifications","tags":["active","alarm","alert","bell","chime","circle","notifications","notify","reminder","ring","sound"]},{"name":"auto_fix_high","tags":["adjust","ai","artificial","auto","automatic","automation","custom","edit","editing","enhance","erase","fix","genai","high","intelligence","magic","modify","pen","smart","spark","sparkle","star","tool","wand"]},{"name":"book_online","tags":["Android","OS","admission","appointment","book","cell","device","event","hardware","iOS","mobile","online","pass","phone","reservation","tablet","ticket"]},{"name":"notes","tags":["comment","doc","document","note","notes","text","write","writing"]},{"name":"point_of_sale","tags":["checkout","cost","machine","merchant","money","of","pay","payment","point","pos","retail","sale","system","transaction"]},{"name":"perm_phone_msg","tags":["bubble","call","cell","chat","comment","communicate","contact","device","message","msg","perm","phone","recording","speech","telephone","voice"]},{"name":"speaker_notes","tags":["bubble","chat","comment","communicate","format","list","message","notes","speaker","speech","text"]},{"name":"fullscreen_exit","tags":["adjust","app","application","components","exit","full","fullscreen","interface","screen","site","size","ui","ux","view","web","website"]},{"name":"headset_mic","tags":["accessory","audio","chat","device","ear","earphone","headphones","headset","listen","mic","music","sound","talk"]},{"name":"create_new_folder","tags":["+","add","create","data","doc","document","drive","file","folder","new","plus","sheet","slide","storage","symbol"]},{"name":"wysiwyg","tags":["composer","mode","screen","site","software","system","text","view","visibility","web","website","window","wysiwyg"]},{"name":"label_important","tags":["favorite","important","indent","label","library","mail","remember","save","stamp","sticker","tag","wing"]},{"name":"card_membership","tags":["bill","bookmark","card","cash","certificate","coin","commerce","cost","credit","currency","dollars","finance","loyalty","membership","money","online","pay","payment","shopping","subscription"]},{"name":"style","tags":["booklet","cards","filters","options","style","tags"]},{"name":"arrow_circle_down","tags":["arrow","circle","direction","down","navigation"]},{"name":"file_present","tags":["clip","data","doc","document","drive","file","folder","folders","note","paper","present","reminder","sheet","slide","storage","writing"]},{"name":"directions_bus","tags":["automobile","bus","car","cars","directions","maps","public","transportation","vehicle"]},{"name":"whatshot","tags":["arrow","circle","direction","fire","frames","hot","round","whatshot"]},{"name":"sports_soccer","tags":["athlete","athletic","ball","entertainment","exercise","football","game","hobby","soccer","social","sports"]},{"name":"indeterminate_check_box","tags":["app","application","box","button","check","components","control","design","form","indeterminate","interface","screen","select","selected","selection","site","square","toggle","ui","undetermined","ux","web","website"]},{"name":"outlined_flag","tags":["country","flag","goal","mark","nation","outlined","report","start"]},{"name":"price_change","tags":["arrows","bill","card","cash","change","coin","commerce","cost","credit","currency","dollars","down","finance","money","online","pay","payment","price","shopping","symbol","up"]},{"name":"mark_email_read","tags":["approve","check","complete","done","email","envelop","letter","mail","mark","message","note","ok","read","select","send","sent","tick","yes"]},{"name":"library_add","tags":["+","add","collection","layers","library","multiple","music","new","plus","stacked","symbol","video"]},{"name":"pageview","tags":["doc","document","find","glass","magnifying","page","paper","search","view"]},{"name":"tv","tags":["device","display","monitor","screen","screencast","stream","television","tv","video","wireless"]},{"name":"inbox","tags":["archive","email","inbox","incoming","mail","message"]},{"name":"adjust","tags":["adjust","alter","center","circle","circles","dot","fix","image","move","target"]},{"name":"3d_rotation","tags":["3","3d","D","alphabet","arrow","arrows","av","camera","character","digit","font","letter","number","rotation","symbol","text","type","vr"]},{"name":"battery_charging_full","tags":["battery","bolt","cell","charge","charging","full","lightening","mobile","power","thunderbolt"]},{"name":"chair","tags":["chair","comfort","couch","decoration","furniture","home","house","living","lounging","loveseat","room","seat","seating","sofa"]},{"name":"directions_bike","tags":["bicycle","bike","direction","directions","human","maps","person","public","route","transportation"]},{"name":"mic_off","tags":["audio","disabled","enabled","hear","hearing","mic","microphone","noise","off","on","record","recording","slash","sound","voice"]},{"name":"local_police","tags":["911","badge","law","local","officer","police","protect","protection","security","shield"]},{"name":"fastfood","tags":["drink","fastfood","food","hamburger","maps","meal","places"]},{"name":"tungsten","tags":["electricity","indoor","lamp","light","lightbulb","setting","tungsten"]},{"name":"mood","tags":["emoji","emotions","expressions","face","feelings","glad","happiness","happy","like","mood","person","pleased","smile","smiling","social","survey"]},{"name":"pause_circle","tags":["circle","control","controls","media","music","pause","video"]},{"name":"upgrade","tags":["arrow","export","instal","line","replace","up","update","upgrade"]},{"name":"recommend","tags":["approved","circle","confirm","favorite","gesture","hand","like","reaction","recommend","social","support","thumbs","up","well"]},{"name":"directions_car_filled","tags":["automobile","car","cars","direction","directions","filled","maps","public","transportation","vehicle"]},{"name":"fmd_good","tags":["destination","direction","fmd","good","location","maps","pin","place","stop"]},{"name":"integration_instructions","tags":["brackets","clipboard","code","css","develop","developer","doc","document","engineer","engineering clipboard","html","instructions","integration","platform"]},{"name":"format_bold","tags":["B","alphabet","bold","character","doc","edit","editing","editor","font","format","letter","sheet","spreadsheet","styles","symbol","text","type","writing"]},{"name":"people_outline","tags":["accounts","committee","face","family","friends","humans","network","outline","people","persons","profiles","social","team","users"]},{"name":"trending_down","tags":["analytics","arrow","data","diagram","down","graph","infographic","measure","metrics","movement","rate","rating","statistics","tracking","trending"]},{"name":"change_history","tags":["change","history","shape","triangle"]},{"name":"female","tags":["female","gender","girl","lady","social","symbol","woman","women"]},{"name":"link_off","tags":["attached","chain","clip","connection","disabled","enabled","link","linked","links","multimedia","off","on","slash","url"]},{"name":"text_fields","tags":["T","add","alphabet","character","field","fields","font","input","letter","symbol","text","type"]},{"name":"swipe","tags":["arrow","arrows","fingers","gesture","hand","hands","swipe","touch"]},{"name":"reviews","tags":["bubble","chat","comment","communicate","feedback","message","rate","rating","recommendation","reviews","speech"]},{"name":"home_repair_service","tags":["box","equipment","fix","home","kit","mechanic","repair","repairing","service","tool","toolbox","tools","workshop"]},{"name":"subscriptions","tags":["enroll","list","media","order","play","signup","subscribe","subscriptions"]},{"name":"video_call","tags":["+","add","call","camera","chat","conference","film","filming","hardware","image","motion","new","picture","plus","symbol","video","videography"]},{"name":"zoom_out_map","tags":["arrow","arrows","destination","location","maps","move","out","place","stop","zoom"]},{"name":"straighten","tags":["length","measure","measurement","ruler","size","straighten"]},{"name":"arrow_drop_down_circle","tags":["app","application","arrow","circle","components","direction","down","drop","interface","navigation","screen","site","ui","ux","web","website"]},{"name":"bed","tags":["bed","bedroom","double","full","furniture","home","hotel","house","king","night","pillows","queen","rest","room","size","sleep"]},{"name":"drive_eta","tags":["automobile","car","cars","destination","direction","drive","estimate","eta","maps","public","transportation","travel","trip","vehicle"]},{"name":"class","tags":["archive","book","bookmark","class","favorite","label","library","read","reading","remember","ribbon","save","tag"]},{"name":"drafts","tags":["document","draft","drafts","email","file","letter","mail","message","read"]},{"name":"ballot","tags":["ballot","bullet","election","list","point","poll","vote"]},{"name":"volume_mute","tags":["audio","control","music","mute","sound","speaker","tv","volume"]},{"name":"table_rows","tags":["grid","layout","lines","rows","stacked","table"]},{"name":"accessible","tags":["accessibility","accessible","body","handicap","help","human","people","person","wheelchair"]},{"name":"stop_circle","tags":["circle","control","controls","music","pause","play","square","stop","video"]},{"name":"family_restroom","tags":["bathroom","child","children","family","father","kids","mother","parents","restroom","wc"]},{"name":"title","tags":["T","alphabet","character","font","header","letter","subject","symbol","text","title","type"]},{"name":"biotech","tags":["biotech","chemistry","laboratory","microscope","research","science","technology"]},{"name":"insert_emoticon","tags":["account","emoji","emoticon","face","happy","human","insert","people","person","profile","sentiment","smile","user"]},{"name":"g_translate","tags":["emblem","g","google","language","logo","mark","speaking","speech","translate","translator","words"]},{"name":"last_page","tags":["app","application","arrow","chevron","components","end","forward","interface","last","page","right","screen","site","ui","ux","web","website"]},{"name":"publish","tags":["arrow","cloud","file","import","publish","up","upload"]},{"name":"repeat","tags":["arrow","arrows","control","controls","media","music","repeat","video"]},{"name":"checklist_rtl","tags":["align","alignment","approve","check","checklist","complete","doc","done","edit","editing","editor","format","list","mark","notes","ok","rtl","select","sheet","spreadsheet","text","tick","type","validate","verified","writing","yes"]},{"name":"wifi_off","tags":["connection","data","disabled","enabled","internet","network","off","offline","on","scan","service","signal","slash","wifi","wireless"]},{"name":"settings_accessibility","tags":["accessibility","body","details","human","information","people","person","personal","preferences","profile","settings","user"]},{"name":"percent","tags":["math","number","percent","symbol"]},{"name":"insert_photo","tags":["image","insert","landscape","mountain","mountains","photo","photography","picture"]},{"name":"hotel","tags":["body","hotel","human","people","person","sleep","stay","travel","trip"]},{"name":"cleaning_services","tags":["clean","cleaning","dust","services","sweep"]},{"name":"downloading","tags":["arrow","circle","down","download","downloading","downloads","install","pending","progress","upload"]},{"name":"expand","tags":["arrow","arrows","compress","enlarge","expand","grow","move","push","together"]},{"name":"local_phone","tags":["booth","call","communication","phone","telecommunication"]},{"name":"offline_bolt","tags":["bolt","circle","electric","fast","lightning","offline","thunderbolt"]},{"name":"auto_graph","tags":["analytics","auto","chart","data","diagram","graph","infographic","line","measure","metrics","stars","statistics","tracking"]},{"name":"local_grocery_store","tags":["grocery","market","shop","store"]},{"name":"photo_library","tags":["album","image","library","mountain","mountains","photo","photography","picture"]},{"name":"miscellaneous_services","tags":[]},{"name":"note_alt","tags":["alt","clipboard","document","file","memo","note","page","paper","writing"]},{"name":"settings_backup_restore","tags":["arrow","back","backup","backwards","refresh","restore","reverse","rotate","settings"]},{"name":"production_quantity_limits","tags":["!","alert","attention","bill","card","cart","cash","caution","coin","commerce","credit","currency","danger","dollars","error","exclamation","important","limits","mark","money","notification","online","pay","payment","production","quantity","shopping","symbol","warning"]},{"name":"person_off","tags":["account","avatar","disabled","enabled","face","human","off","on","people","person","profile","slash","user"]},{"name":"report_gmailerrorred","tags":["!","alert","attention","caution","danger","error","exclamation","gmail","gmailerrorred","important","mark","notification","octagon","report","symbol","warning"]},{"name":"camera","tags":["aperture","camera","lens","photo","photography","picture","shutter"]},{"name":"recycling","tags":["bio","eco","green","loop","recyclable","recycle","recycling","rotate","sustainability","sustainable","trash"]},{"name":"male","tags":["boy","gender","male","man","social","symbol"]},{"name":"not_interested","tags":["cancel","close","dislike","exit","interested","no","not","off","quit","remove","stop","x"]},{"name":"event_busy","tags":["busy","calendar","cancel","close","date","event","exit","no","remove","schedule","stop","time","unavailable","x"]},{"name":"arrow_circle_left","tags":["arrow","circle","direction","left","navigation"]},{"name":"shuffle","tags":["arrow","arrows","control","controls","music","random","shuffle","video"]},{"name":"aspect_ratio","tags":["aspect","expand","image","ratio","resize","scale","size","square"]},{"name":"other_houses","tags":["architecture","cottage","estate","home","house","houses","maps","other","place","real","residence","residential","stay","traveling"]},{"name":"model_training","tags":["arrow","bulb","idea","inprogress","light","load","loading","model","refresh","renew","restore","reverse","rotate","training"]},{"name":"unfold_less","tags":["arrow","arrows","chevron","collapse","direction","expand","expandable","inward","less","list","navigation","unfold","up"]},{"name":"insert_chart_outlined","tags":["analytics","bar","bars","chart","data","diagram","graph","infographic","insert","measure","metrics","outlined","statistics","tracking"]},{"name":"donut_large","tags":["analytics","chart","data","diagram","donut","graph","infographic","inprogress","large","measure","metrics","pie","statistics","tracking"]},{"name":"view_column","tags":["column","design","format","grid","layout","vertical","view","website"]},{"name":"segment","tags":["alignment","fonts","format","lines","list","paragraph","part","piece","rule","rules","segment","style","text"]},{"name":"checkroom","tags":["checkroom","closet","clothes","coat check","hanger"]},{"name":"mode","tags":["compose","create","draft","draw","edit","mode","pen","pencil","write"]},{"name":"portrait","tags":["account","face","human","people","person","photo","picture","portrait","profile","user"]},{"name":"camera_alt","tags":["alt","camera","image","photo","photography","picture"]},{"name":"keyboard_double_arrow_left","tags":["arrow","arrows","direction","double","left","multiple","navigation"]},{"name":"delete_sweep","tags":["bin","can","delete","garbage","remove","sweep","trash"]},{"name":"hub","tags":["center","connection","core","focal point","hub","network","nucleus","topology"]},{"name":"audiotrack","tags":["audio","audiotrack","key","music","note","sound","track"]},{"name":"calendar_view_month","tags":["calendar","date","day","event","format","grid","layout","month","schedule","today","view"]},{"name":"draw","tags":["compose","create","design","draft","draw","edit","editing","input","pen","pencil","write","writing"]},{"name":"navigation","tags":["destination","direction","location","maps","navigation","pin","place","point","stop"]},{"name":"folder_shared","tags":["account","collaboration","data","doc","document","drive","face","file","folder","human","people","person","profile","share","shared","sheet","slide","storage","team","user"]},{"name":"read_more","tags":["arrow","more","read","text"]},{"name":"stacked_bar_chart","tags":["analytics","bar","chart-chart","data","diagram","graph","infographic","measure","metrics","stacked","statistics","tracking"]},{"name":"mode_comment","tags":["bubble","chat","comment","communicate","feedback","message","mode comment","speech"]},{"name":"schedule_send","tags":["calendar","clock","date","email","letter","mail","remember","schedule","send","share","time"]},{"name":"bluetooth","tags":["bluetooth","cast","connect","connection","device","paring","streaming","symbol","wireless"]},{"name":"graphic_eq","tags":["audio","eq","equalizer","graphic","music","recording","sound","voice"]},{"name":"markunread","tags":["email","envelop","letter","mail","markunread","message","send","unread"]},{"name":"alarm_on","tags":["alarm","alert","bell","clock","disabled","duration","enabled","notification","off","on","slash","time","timer","watch"]},{"name":"local_gas_station","tags":["auto","car","gas","local","oil","station","vehicle"]},{"name":"person_add_alt_1","tags":[]},{"name":"maximize","tags":["app","application","components","design","interface","line","maximize","screen","shape","site","ui","ux","web","website"]},{"name":"bookmark_add","tags":["+","add","bookmark","favorite","plus","remember","ribbon","save","symbol"]},{"name":"dvr","tags":["Android","OS","audio","chrome","computer","desktop","device","display","dvr","electronic","hardware","iOS","list","mac","monitor","record","recorder","screen","tv","video","web","window"]},{"name":"do_not_disturb_on","tags":["cancel","close","denied","deny","disabled","disturb","do","enabled","off","on","remove","silence","slash","stop"]},{"name":"train","tags":["automobile","car","cars","direction","maps","public","rail","subway","train","transportation","vehicle"]},{"name":"person_pin_circle","tags":["account","circle","destination","direction","face","human","location","maps","people","person","pin","place","profile","stop","user"]},{"name":"square_foot","tags":["construction","feet","foot","inches","length","measurement","ruler","school","set","square","tools"]},{"name":"more_time","tags":["+","add","clock","date","more","new","plus","schedule","symbol","time"]},{"name":"document_scanner","tags":["article","data","doc","document","drive","file","folder","folders","notes","page","paper","scan","scanner","sheet","slide","text","writing"]},{"name":"thumbs_up_down","tags":["dislike","down","favorite","fingers","gesture","hands","like","rate","rating","thumbs","up"]},{"name":"settings_ethernet","tags":["arrows","computer","connect","connection","connectivity","dots","ethernet","internet","network","settings","wifi"]},{"name":"sort_by_alpha","tags":["alphabet","alphabetize","az","by alpha","character","font","letter","list","order","organize","sort","symbol","text","type"]},{"name":"theaters","tags":["film","movie","movies","show","showtimes","theater","theaters","watch"]},{"name":"cloud_done","tags":["app","application","approve","backup","check","cloud","complete","connection","done","drive","files","folders","internet","mark","network","ok","select","sky","storage","tick","upload","validate","verified","yes"]},{"name":"local_parking","tags":["alphabet","auto","car","character","font","garage","letter","local","park","parking","symbol","text","type","vehicle"]},{"name":"view_agenda","tags":["agenda","cards","design","format","grid","layout","stacked","view","website"]},{"name":"mark_email_unread","tags":["check","circle","email","envelop","letter","mail","mark","message","note","notification","send","unread"]},{"name":"local_florist","tags":["florist","flower","local","shop"]},{"name":"connect_without_contact","tags":["communicating","connect","contact","distance","people","signal","social","socialize","without"]},{"name":"thumb_down_off_alt","tags":["disabled","dislike","down","enabled","favorite","filled","fingers","gesture","hand","hands","like","off","offline","on","rank","ranking","rate","rating","slash","thumb"]},{"name":"sentiment_neutral","tags":["emotionless","emotions","expressions","face","feelings","fine","indifference","mood","neutral","okay","person","sentiment","survey"]},{"name":"call_end","tags":["call","cell","contact","device","end","hardware","mobile","phone","telephone"]},{"name":"subdirectory_arrow_right","tags":["arrow","directory","down","navigation","right","sub","subdirectory"]},{"name":"diamond","tags":["diamond","fashion","gems","jewelry","logo","retail","valuable","valuables"]},{"name":"podcasts","tags":["broadcast","casting","network","podcasts","signal","transmitting","wireless"]},{"name":"monitor_heart","tags":["baseline","device","ecc","ecg","fitness","health","heart","medical","monitor","track"]},{"name":"all_inclusive","tags":["all","endless","forever","inclusive","infinity","loop","mobius","neverending","strip","sustainability","sustainable"]},{"name":"wc","tags":["bathroom","closet","female","male","man","restroom","room","wash","water","wc","women"]},{"name":"grass","tags":["backyard","fodder","grass","ground","home","lawn","plant","turf","yard"]},{"name":"important_devices","tags":["Android","OS","desktop","devices","hardware","iOS","important","mobile","monitor","phone","star","tablet","web"]},{"name":"back_hand","tags":["back","fingers","gesture","hand","raised"]},{"name":"hiking","tags":["backpacking","bag","climbing","duffle","hiking","mountain","social","sports","stick","trail","travel","walking"]},{"name":"masks","tags":["air","cover","covid","face","hospital","masks","medical","pollution","protection","respirator","sick","social"]},{"name":"waving_hand","tags":["bye","fingers","gesture","goodbye","greetings","hand","hello","palm","wave","waving"]},{"name":"architecture","tags":["architecture","art","compass","design","draw","drawing","engineering","geometric","tool"]},{"name":"local_post_office","tags":["delivery","email","envelop","letter","local","mail","message","office","package","parcel","post","postal","send","stamp"]},{"name":"functions","tags":["average","calculate","count","custom","doc","edit","editing","editor","functions","math","sheet","spreadsheet","style","sum","text","type","writing"]},{"name":"directions","tags":["arrow","directions","maps","right","route","sign","traffic"]},{"name":"money","tags":["100","bill","card","cash","coin","commerce","cost","credit","currency","digit","dollars","finance","money","number","online","pay","payment","price","shopping","symbol"]},{"name":"unpublished","tags":["approve","check","circle","complete","disabled","done","enabled","mark","off","ok","on","select","slash","tick","unpublished","validate","verified","yes"]},{"name":"notifications_off","tags":["active","alarm","alert","bell","chime","disabled","enabled","notifications","notify","off","offline","on","reminder","ring","slash","sound"]},{"name":"airport_shuttle","tags":["airport","automobile","car","cars","commercial","delivery","direction","maps","mini","public","shuttle","transport","transportation","travel","truck","van","vehicle"]},{"name":"insert_link","tags":["add","attach","clip","file","insert","link","mail","media"]},{"name":"thumb_down_alt","tags":["bad","decline","disapprove","dislike","down","feedback","hate","negative","no","reject","social","thumb","veto","vote"]},{"name":"two_wheeler","tags":["automobile","bike","car","cars","direction","maps","motorcycle","public","scooter","sport","transportation","travel","two wheeler","vehicle"]},{"name":"nightlight","tags":["dark","disturb","mode","moon","night","nightlight","sleep"]},{"name":"mic_none","tags":["hear","hearing","mic","microphone","noise","none","record","sound","voice"]},{"name":"keyboard_double_arrow_down","tags":["arrow","arrows","direction","double","down","multiple","navigation"]},{"name":"invert_colors","tags":["colors","drop","droplet","edit","editing","hue","invert","inverted","palette","tone","water"]},{"name":"clear_all","tags":["all","clear","doc","document","format","lines","list"]},{"name":"mouse","tags":["click","computer","cursor","device","hardware","mouse","wireless"]},{"name":"mode_edit_outline","tags":["compose","create","draft","draw","edit","mode","outline","pen","pencil","write"]},{"name":"open_in_browser","tags":["arrow","browser","in","open","site","up","web","website","window"]},{"name":"insert_invitation","tags":["calendar","date","day","event","insert","invitation","mark","month","range","remember","reminder","today","week"]},{"name":"fast_rewind","tags":["back","control","fast","media","music","play","rewind","speed","time","tv","video"]},{"name":"opacity","tags":["color","drop","droplet","hue","invert","inverted","opacity","palette","tone","water"]},{"name":"video_camera_front","tags":["account","camera","face","front","human","image","people","person","photo","photography","picture","profile","user","video"]},{"name":"commute","tags":["automobile","car","commute","direction","maps","public","train","transportation","trip","vehicle"]},{"name":"addchart","tags":["+","addchart","analytics","bar","bars","chart","data","diagram","graph","infographic","measure","metrics","new","plus","statistics","symbol","tracking"]},{"name":"no_accounts","tags":["account","accounts","avatar","disabled","enabled","face","human","no","off","offline","on","people","person","profile","slash","thumbnail","unavailable","unidentifiable","unknown","user"]},{"name":"coffee","tags":["beverage","coffee","cup","drink","mug","plate","set","tea"]},{"name":"luggage","tags":["airport","bag","baggage","carry","flight","hotel","luggage","on","suitcase","travel","trip"]},{"name":"workspaces","tags":["circles","collaboration","dot","filled","group","outline","space","team","work","workspaces"]},{"name":"child_care","tags":["babies","baby","care","child","children","face","infant","kids","newborn","toddler","young"]},{"name":"sports_score","tags":["destination","flag","goal","score","sports"]},{"name":"library_music","tags":["add","album","collection","library","music","song","sounds"]},{"name":"history_toggle_off","tags":["clock","date","history","off","schedule","time","toggle"]},{"name":"system_update_alt","tags":["arrow","down","download","export","system","update"]},{"name":"access_time","tags":[]},{"name":"rotate_right","tags":["around","arrow","direction","inprogress","load","loading refresh","renew","right","rotate","turn"]},{"name":"color_lens","tags":["art","color","lens","paint","pallet"]},{"name":"grid_on","tags":["collage","disabled","enabled","grid","image","layout","off","on","slash","view"]},{"name":"crop_free","tags":["adjust","adjustments","crop","edit","editing","focus","frame","free","image","photo","photos","settings","size","zoom"]},{"name":"cloud_queue","tags":["cloud","connection","internet","network","queue","sky","upload"]},{"name":"keyboard_voice","tags":["keyboard","mic","microphone","noise","record","recorder","speaker","voice"]},{"name":"format_align_left","tags":["align","alignment","doc","edit","editing","editor","format","left","sheet","spreadsheet","text","type","writing"]},{"name":"view_week","tags":["bars","columns","design","format","grid","layout","view","website","week"]},{"name":"real_estate_agent","tags":["agent","architecture","broker","estate","hand","home","house","loan","mortgage","property","real","residence","residential","sales","social"]},{"name":"horizontal_rule","tags":["gmail","horizontal","line","novitas","rule"]},{"name":"topic","tags":["data","doc","document","drive","file","folder","sheet","slide","storage","topic"]},{"name":"shower","tags":["bath","bathroom","closet","home","house","place","plumbing","room","shower","sprinkler","wash","water","wc"]},{"name":"format_italic","tags":["alphabet","character","doc","edit","editing","editor","font","format","italic","letter","sheet","spreadsheet","style","symbol","text","type","writing"]},{"name":"traffic","tags":["direction","light","maps","signal","street","traffic"]},{"name":"add_business","tags":["+","add","bill","building","business","card","cash","coin","commerce","company","credit","currency","dollars","market","money","new","online","pay","payment","plus","shop","shopping","store","storefront","symbol"]},{"name":"electrical_services","tags":["charge","cord","electric","electrical","plug","power","services","wire"]},{"name":"timelapse","tags":["duration","motion","photo","time","timelapse","timer","video"]},{"name":"youtube_searched_for","tags":["arrow","back","backwards","find","glass","history","inprogress","load","loading","look","magnify","magnifying","refresh","renew","restore","reverse","rotate","search","see","youtube"]},{"name":"front_hand","tags":["fingers","front","gesture","hand","hello","palm","stop"]},{"name":"yard","tags":["backyard","flower","garden","home","house","nature","pettle","plants","yard"]},{"name":"tour","tags":["destination","flag","places","tour","travel","visit"]},{"name":"factory","tags":["factory","industry","manufacturing","warehouse"]},{"name":"developer_board","tags":["board","chip","computer","developer","development","hardware","microchip","processor"]},{"name":"more","tags":["3","archive","bookmark","dots","etc","favorite","indent","label","more","remember","save","stamp","sticker","tab","tag","three"]},{"name":"star_purple500","tags":["500","best","bookmark","favorite","highlight","purple","ranking","rate","rating","save","star","toggle"]},{"name":"format_color_fill","tags":["bucket","color","doc","edit","editing","editor","fill","format","paint","sheet","spreadsheet","style","text","type","writing"]},{"name":"beach_access","tags":["access","beach","places","summer","sunny","umbrella"]},{"name":"local_bar","tags":["alcohol","bar","bottle","club","cocktail","drink","food","liquor","local","wine"]},{"name":"add_link","tags":["add","attach","clip","link","new","plus","symbol"]},{"name":"landscape","tags":["image","landscape","mountain","mountains","nature","photo","photography","picture"]},{"name":"slideshow","tags":["movie","photos","play","slideshow","square","video","view"]},{"name":"stream","tags":["cast","connected","feed","live","network","signal","stream","wireless"]},{"name":"videocam_off","tags":["cam","camera","conference","disabled","enabled","film","filming","hardware","image","motion","off","offline","on","picture","slash","video","videography"]},{"name":"directions_boat","tags":["automobile","boat","car","cars","direction","directions","ferry","maps","public","transportation","vehicle"]},{"name":"download_done","tags":["arrow","arrows","check","done","down","download","downloads","drive","install","installed","ok","tick","upload"]},{"name":"volume_down","tags":["audio","control","down","music","sound","speaker","tv","volume"]},{"name":"alt_route","tags":["alt","alternate","alternative","arrows","direction","maps","navigation","options","other","route","routes","split","symbol"]},{"name":"mood_bad","tags":["bad","disappointment","dislike","emoji","emotions","expressions","face","feelings","mood","person","rating","social","survey","unhappiness","unhappy","unpleased","unsmile","unsmiling"]},{"name":"vaccines","tags":["aid","covid","doctor","drug","emergency","hospital","immunity","injection","medical","medication","medicine","needle","pharmacy","sick","syringe","vaccination","vaccines","vial"]},{"name":"dialpad","tags":["buttons","call","contact","device","dial","dialpad","dots","mobile","numbers","pad","phone"]},{"name":"route","tags":["directions","maps","path","route","sign","traffic"]},{"name":"hide_source","tags":["circle","disabled","enabled","hide","off","offline","on","shape","slash","source"]},{"name":"bookmark_added","tags":["added","approve","bookmark","check","complete","done","favorite","mark","ok","remember","save","select","tick","validate","verified","yes"]},{"name":"mark_as_unread","tags":["as","envelop","letter","mail","mark","post","postal","read","receive","send","unread"]},{"name":"plagiarism","tags":["doc","document","find","glass","look","magnifying","page","paper","plagiarism","search","see"]},{"name":"turned_in","tags":["archive","bookmark","favorite","in","label","library","read","reading","remember","ribbon","save","tag","turned"]},{"name":"settings_input_antenna","tags":["airplay","antenna","arrows","cast","computer","connect","connection","connectivity","dots","input","internet","network","screencast","settings","stream","wifi","wireless"]},{"name":"shop","tags":["bag","bill","buy","card","cart","cash","coin","commerce","credit","currency","dollars","google","money","online","pay","payment","play","shop","shopping","store"]},{"name":"pool","tags":["athlete","athletic","beach","body","entertainment","exercise","hobby","human","ocean","people","person","places","pool","sea","sports","swim","swimming","water"]},{"name":"search_off","tags":["cancel","close","disabled","enabled","find","glass","look","magnify","magnifying","off","on","search","see","slash","stop","x"]},{"name":"approval","tags":["apply","approval","approvals","approve","certificate","certification","disapproval","drive","file","impression","ink","mark","postage","stamp"]},{"name":"currency_rupee","tags":["bill","card","cash","coin","commerce","cost","credit","currency","dollars","finance","money","online","pay","payment","price","rupee","shopping","symbol"]},{"name":"power","tags":["charge","cord","electric","electrical","outlet","plug","power"]},{"name":"collections_bookmark","tags":["album","archive","bookmark","collections","favorite","gallery","label","library","read","reading","remember","ribbon","save","stack","tag"]},{"name":"not_started","tags":["circle","media","not","pause","play","started","video"]},{"name":"pedal_bike","tags":["automobile","bicycle","bike","car","cars","direction","human","maps","pedal","public","route","scooter","transportation","vehicle","vespa"]},{"name":"water","tags":["aqua","beach","lake","ocean","river","water","waves","weather"]},{"name":"router","tags":["box","cable","connection","hardware","internet","network","router","signal","wifi"]},{"name":"flight_land","tags":["airport","arrival","arriving","flight","fly","land","landing","plane","transportation","travel"]},{"name":"shopping_cart_checkout","tags":["arrow","cart","cash","checkout","coin","commerce","currency","dollars","money","online","pay","payment","right","shopping"]},{"name":"agriculture","tags":["agriculture","automobile","car","cars","cultivation","farm","harvest","maps","tractor","transport","travel","truck","vehicle"]},{"name":"where_to_vote","tags":["approve","ballot","check","complete","destination","direction","done","location","maps","mark","ok","pin","place","poll","select","stop","tick","to","validate election","verified","vote","where","yes"]},{"name":"beenhere","tags":["approve","archive","beenhere","bookmark","check","complete","done","favorite","label","library","mark","ok","read","reading","remember","ribbon","save","select","tag","tick","validate","verified","yes"]},{"name":"add_comment","tags":["+","add","bubble","chat","comment","communicate","feedback","message","new","plus","speech","symbol"]},{"name":"copy_all","tags":["all","content","copy","cut","doc","document","file","multiple","page","paper","past"]},{"name":"dynamic_feed","tags":["'mail_outline'","'markunread'. Keep 'mail' and remove others.","Duplicate of 'email'"]},{"name":"videogame_asset","tags":["asset","console","controller","device","game","gamepad","gaming","playstation","video"]},{"name":"move_to_inbox","tags":["archive","arrow","down","email","envelop","inbox","incoming","letter","mail","message","move to","send"]},{"name":"crop_square","tags":["adjust","adjustments","app","application","area","components","crop","design","edit","editing","expand","frame","image","images","interface","open","photo","photos","rectangle","screen","settings","shape","shapes","site","size","square","ui","ux","web","website","window"]},{"name":"recent_actors","tags":["account","actors","avatar","card","cards","carousel","face","human","layers","list","people","person","profile","recent","thumbnail","user"]},{"name":"emoji_nature","tags":["animal","bee","bug","daisy","emoji","flower","insect","ladybug","nature","petals","spring","summer"]},{"name":"cloud_off","tags":["app","application","backup","cloud","connection","disabled","drive","enabled","files","folders","internet","network","off","offline","on","sky","slash","storage","upload"]},{"name":"panorama_fish_eye","tags":["angle","circle","eye","fish","image","panorama","photo","photography","picture","wide"]},{"name":"lens","tags":["circle","full","geometry","lens","moon"]},{"name":"360","tags":["360","arrow","av","camera","direction","rotate","rotation","vr"]},{"name":"share_location","tags":["destination","direction","gps","location","maps","pin","place","share","stop","tracking"]},{"name":"assignment_late","tags":["!","alert","assignment","attention","caution","clipboard","danger","doc","document","error","exclamation","important","late","mark","notification","symbol","warning"]},{"name":"switch_account","tags":["account","choices","face","human","multiple","options","people","person","profile","social","switch","user"]},{"name":"looks_two","tags":["2","digit","looks","numbers","square","symbol"]},{"name":"do_not_disturb","tags":["cancel","close","denied","deny","disturb","do","remove","silence","stop"]},{"name":"donut_small","tags":["analytics","chart","data","diagram","donut","graph","infographic","inprogress","measure","metrics","pie","small","statistics","tracking"]},{"name":"saved_search","tags":["find","glass","important","look","magnify","magnifying","marked","saved","search","see","star"]},{"name":"contactless","tags":["bluetooth","cash","connect","connection","connectivity","contact","contactless","credit","device","finance","pay","payment","signal","transaction","wifi","wireless"]},{"name":"highlight_alt","tags":["alt","arrow","box","click","cursor","draw","focus","highlight","pointer","select","selection","target"]},{"name":"assignment_return","tags":["arrow","assignment","back","clipboard","doc","document","left","retun"]},{"name":"kitchen","tags":["appliance","cold","food","fridge","home","house","ice","kitchen","places","refrigerator","storage"]},{"name":"warehouse","tags":["garage","industry","manufacturing","storage","warehouse"]},{"name":"liquor","tags":["alcohol","bar","bottle","club","cocktail","drink","food","liquor","party","store","wine"]},{"name":"gpp_maybe","tags":["!","alert","attention","caution","certified","danger","error","exclamation","gpp","important","mark","maybe","notification","privacy","private","protect","protection","security","shield","sim","symbol","verified","warning"]},{"name":"settings_input_component","tags":["audio","av","cable","cables","component","connect","connection","connectivity","input","internet","plug","points","settings","video","wifi"]},{"name":"waves","tags":["beach","lake","ocean","pool","river","sea","swim","water","wave","waves"]},{"name":"hotel_class","tags":["achievement","bookmark","class","favorite","highlight","hotel","important","marked","rank","ranking","rate","rating","reward","save","saved","shape","special","star"]},{"name":"web_asset","tags":["-website","app","application desktop","asset","browser","design","download","image","interface","internet","layout","screen","site","ui","ux","video","web","website","window","www"]},{"name":"view_carousel","tags":["cards","carousel","design","format","grid","layout","view","website"]},{"name":"anchor","tags":["anchor","google","logo"]},{"name":"filter_alt_off","tags":["alt","disabled","edit","filter","funnel","off","offline","options","refine","sift","slash"]},{"name":"balance","tags":["balance","equal","equity","impartiality","justice","parity","stability. equilibrium","steadiness","symmetry"]},{"name":"view_quilt","tags":["design","format","grid","layout","quilt","square","squares","stacked","view","website"]},{"name":"library_add_check","tags":["add","approve","check","collection","complete","done","layers","library","mark","multiple","music","ok","select","stacked","tick","validate","verified","video","yes"]},{"name":"queue_music","tags":["collection","list","music","playlist","queue"]},{"name":"casino","tags":["casino","dice","dots","entertainment","gamble","gambling","game","games","luck","places"]},{"name":"hearing","tags":["accessibility","accessible","aid","ear","handicap","hearing","help","impaired","listen","sound","volume"]},{"name":"phone_enabled","tags":["call","cell","contact","device","enabled","hardware","mobile","phone","telephone"]},{"name":"linear_scale","tags":["app","application","components","design","interface","layout","linear","measure","menu","scale","screen","site","slider","ui","ux","web","website","window"]},{"name":"holiday_village","tags":["architecture","beach","camping","cottage","estate","holiday","home","house","lake","lodge","maps","place","real","residence","residential","stay","traveling","vacation","village"]},{"name":"turned_in_not","tags":["archive","bookmark","favorite","in","label","library","not","read","reading","remember","ribbon","save","tag","turned"]},{"name":"sync_problem","tags":["!","360","alert","around","arrow","arrows","attention","caution","danger","direction","error","exclamation","important","inprogress","load","loading refresh","mark","notification","problem","renew","rotate","symbol","sync","turn","warning"]},{"name":"start","tags":["arrow","keyboard","next","right","start"]},{"name":"all_inbox","tags":["Inbox","all","delivered","delivery","email","mail","message","send"]},{"name":"mediation","tags":["arrow","arrows","direction","dots","mediation","right"]},{"name":"edit_off","tags":["compose","create","disabled","draft","edit","editing","enabled","input","new","off","offline","on","pen","pencil","slash","write","writing"]},{"name":"emergency","tags":["asterisk","clinic","emergency","health","hospital","maps","medical","symbol"]},{"name":"settings_remote","tags":["bluetooth","connection","connectivity","device","remote","settings","signal","wifi","wireless"]},{"name":"drive_file_move","tags":["arrow","data","doc","document","drive","file","folder","move","right","sheet","slide","storage"]},{"name":"fit_screen","tags":["enlarge","fit","format","layout","reduce","scale","screen","size"]},{"name":"hourglass_full","tags":["countdown","full","hourglass","loading","minutes","time","wait","waiting"]},{"name":"nights_stay","tags":["climate","cloud","crescent","dark","lunar","mode","moon","nights","phases","silence","silent","sky","stay","time","weather"]},{"name":"pause_circle_filled","tags":["circle","control","controls","filled","media","music","pause","video"]},{"name":"catching_pokemon","tags":["catching","go","pokemon","pokestop","travel"]},{"name":"king_bed","tags":["bed","bedroom","double","furniture","home","hotel","house","king","night","pillows","queen","rest","room","sleep"]},{"name":"flaky","tags":["approve","check","close","complete","contrast","done","exit","flaky","mark","no","ok","options","select","stop","tick","verified","x","yes"]},{"name":"format_size","tags":["alphabet","character","color","doc","edit","editing","editor","fill","font","format","letter","paint","sheet","size","spreadsheet","style","symbol","text","type","writing"]},{"name":"interests","tags":["circle","heart","interests","shapes","social","square","triangle"]},{"name":"stacked_line_chart","tags":["analytics","chart","data","diagram","graph","infographic","line","measure","metrics","stacked","statistics","tracking"]},{"name":"unarchive","tags":["archive","arrow","inbox","mail","store","unarchive","undo","up"]},{"name":"subtitles","tags":["accessible","caption","cc","character","closed","decoder","language","media","movies","subtitle","subtitles","tv"]},{"name":"toll","tags":["bill","booth","car","card","cash","coin","commerce","credit","currency","dollars","highway","money","online","pay","payment","ticket","toll"]},{"name":"keyboard_double_arrow_up","tags":["arrow","arrows","direction","double","multiple","navigation","up"]},{"name":"time_to_leave","tags":["automobile","car","cars","destination","direction","drive","estimate","eta","maps","public","transportation","travel","trip","vehicle"]},{"name":"location_searching","tags":["destination","direction","location","maps","pin","place","pointer","searching","stop","tracking"]},{"name":"cable","tags":["cable","connect","connection","device","electronics","usb","wire"]},{"name":"moving","tags":["arrow","direction","moving","navigation","travel","up"]},{"name":"remove_shopping_cart","tags":["card","cart","cash","checkout","coin","commerce","credit","currency","disabled","dollars","enabled","off","on","online","pay","payment","remove","shopping","slash","tick"]},{"name":"cast_for_education","tags":["Android","OS","airplay","cast","chrome","connect","desktop","device","display","education","for","hardware","iOS","learning","lessons teaching","mac","monitor","screen","screencast","streaming","television","tv","web","window","wireless"]},{"name":"fiber_new","tags":["alphabet","character","fiber","font","letter","network","new","symbol","text","type"]},{"name":"format_underlined","tags":["alphabet","character","doc","edit","editing","editor","font","format","letter","line","sheet","spreadsheet","style","symbol","text","type","under","underlined","writing"]},{"name":"pause_circle_outline","tags":["circle","control","controls","media","music","outline","pause","video"]},{"name":"mark_chat_unread","tags":["bubble","chat","circle","comment","communicate","mark","message","notification","speech","unread"]},{"name":"insert_comment","tags":["add","bubble","chat","comment","feedback","insert","message"]},{"name":"cameraswitch","tags":["arrows","camera","cameraswitch","flip","rotate","swap","switch","view"]},{"name":"rocket","tags":["rocket","space","spaceship"]},{"name":"local_airport","tags":["air","airplane","airport","flight","plane","transportation","travel","trip"]},{"name":"lock_clock","tags":["clock","date","lock","locked","password","privacy","private","protection","safety","schedule","secure","security","time"]},{"name":"device_hub","tags":["Android","OS","circle","computer","desktop","device","hardware","hub","iOS","laptop","mobile","monitor","phone","square","tablet","triangle","watch","wearable","web"]},{"name":"filter_vintage","tags":["edit","editing","effect","filter","flower","image","images","photography","picture","pictures","vintage"]},{"name":"sailing","tags":["boat","entertainment","fishing","hobby","ocean","sailboat","sailing","sea","social sports","travel","water"]},{"name":"roofing","tags":["architecture","building","chimney","construction","estate","home","house","real","residence","residential","roof","roofing","service","shelter"]},{"name":"settings_voice","tags":["mic","microphone","record","recorder","settings","speaker","voice"]},{"name":"swap_horizontal_circle","tags":["arrow","arrows","back","circle","forward","horizontal","swap"]},{"name":"add_location_alt","tags":["+","add","alt","destination","direction","location","maps","new","pin","place","plus","stop","symbol"]},{"name":"room_service","tags":["alert","bell","delivery","hotel","notify","room","service"]},{"name":"content_paste_search","tags":["clipboard","content","doc","document","file","find","paste","search","trace","track"]},{"name":"reply_all","tags":["all","arrow","backward","group","left","mail","message","multiple","reply","send","share"]},{"name":"compost","tags":["bio","compost","compostable","decomposable","decompose","eco","green","leaf","leafs","nature","organic","plant","recycle","sustainability","sustainable"]},{"name":"bubble_chart","tags":["analytics","bar","bars","bubble","chart","data","diagram","graph","infographic","measure","metrics","statistics","tracking"]},{"name":"compare","tags":["adjust","adjustment","compare","edit","editing","edits","enhance","fix","image","images","photo","photography","photos","scan","settings"]},{"name":"money_off","tags":["bill","card","cart","cash","coin","commerce","credit","currency","disabled","dollars","enabled","money","off","on","online","pay","payment","shopping","slash","symbol"]},{"name":"file_open","tags":["arrow","doc","document","drive","file","left","open","page","paper"]},{"name":"filter_drama","tags":["cloud","drama","edit","editing","effect","filter","image","photo","photography","picture","sky camera"]},{"name":"shortcut","tags":["arrow","direction","forward","right","shortcut"]},{"name":"view_sidebar","tags":["design","format","grid","layout","sidebar","view","web"]},{"name":"looks_3","tags":["3","digit","looks","numbers","square","symbol"]},{"name":"note","tags":["bookmark","message","note","paper"]},{"name":"vertical_align_bottom","tags":["align","alignment","arrow","bottom","doc","down","edit","editing","editor","sheet","spreadsheet","text","type","vertical","writing"]},{"name":"3p","tags":["3","3p","account","avatar","bubble","chat","comment","communicate","face","human","message","party","people","person","profile","speech","user"]},{"name":"online_prediction","tags":["bulb","connection","idea","light","network","online","prediction","signal","wireless"]},{"name":"cancel_presentation","tags":["cancel","close","device","exit","no","present","presentation","quit","remove","screen","slide","stop","website","window","x"]},{"name":"select_all","tags":["all","select","selection","square","tool"]},{"name":"event_seat","tags":["assign","assigned","chair","event","furniture","reservation","row","seat","section","sit"]},{"name":"window","tags":["close","glass","grid","home","house","interior","layout","outside","window"]},{"name":"av_timer","tags":["av","clock","countdown","duration","minutes","seconds","time","timer","watch"]},{"name":"album","tags":["album","artist","audio","bvb","cd","computer","data","disk","file","music","record","sound","storage","track"]},{"name":"local_dining","tags":["dining","eat","food","fork","knife","local","meal","restaurant","spoon"]},{"name":"headset","tags":["accessory","audio","device","ear","earphone","headphones","headset","listen","music","sound"]},{"name":"maps_ugc","tags":["+","add","bubble","comment","communicate","feedback","maps","message","new","plus","speech","symbol","ugc"]},{"name":"airplane_ticket","tags":["airplane","airport","boarding","flight","fly","maps","pass","ticket","transportation","travel"]},{"name":"vertical_split","tags":["design","format","grid","layout","paragraph","split","text","vertical","website","writing"]},{"name":"sports_basketball","tags":["athlete","athletic","ball","basketball","entertainment","exercise","game","hobby","social","sports"]},{"name":"next_plan","tags":["arrow","circle","next","plan","right"]},{"name":"drive_folder_upload","tags":["arrow","data","doc","document","drive","file","folder","sheet","slide","storage","up","upload"]},{"name":"pregnant_woman","tags":["baby","birth","body","female","human","lady","maternity","mom","mother","people","person","pregnant","women"]},{"name":"wallpaper","tags":["background","image","landscape","photo","photography","picture","wallpaper"]},{"name":"image_search","tags":["find","glass","image","landscape","look","magnify","magnifying","mountain","mountains","photo","photography","picture","search","see"]},{"name":"data_exploration","tags":["analytics","arrow","chart","data","diagram","exploration","graph","infographic","measure","metrics","statistics","tracking"]},{"name":"device_thermostat","tags":["celsius","device","fahrenheit","meter","temp","temperature","thermometer","thermostat"]},{"name":"healing","tags":["bandage","edit","editing","emergency","fix","healing","hospital","image","medicine"]},{"name":"laptop_mac","tags":["Android","OS","chrome","device","display","hardware","iOS","laptop","mac","monitor","screen","web","window"]},{"name":"height","tags":["arrow","color","doc","down","edit","editing","editor","fill","format","height","paint","sheet","spreadsheet","style","text","type","up","writing"]},{"name":"restore_from_trash","tags":["arrow","back","backwards","clock","date","history","refresh","renew","restore","reverse","rotate","schedule","time","turn"]},{"name":"radar","tags":["detect","military","near","network","position","radar","scan"]},{"name":"auto_awesome_motion","tags":["adjust","auto","awesome","collage","edit","editing","enhance","image","motion","photo","video"]},{"name":"file_download_done","tags":["arrow","arrows","check","done","down","download","downloads","drive","file","install","installed","tick","upload"]},{"name":"notification_add","tags":["+","active","add","alarm","alert","bell","chime","notification","notifications","notify","plus","reminder","ring","sound","symbol"]},{"name":"call_made","tags":["arrow","call","device","made","mobile"]},{"name":"camera_enhance","tags":["ai","artificial","automatic","automation","camera","custom","enhance","genai","important","intelligence","lens","magic","photo","photography","picture","quality","smart","spark","sparkle","special","star"]},{"name":"rotate_left","tags":["around","arrow","direction","inprogress","left","load","loading refresh","renew","rotate","turn"]},{"name":"local_taxi","tags":["automobile","cab","call","car","cars","direction","local","lyft","maps","public","taxi","transportation","uber","vehicle","yellow"]},{"name":"star_border_purple500","tags":["500","best","bookmark","border","favorite","highlight","outline","purple","ranking","rate","rating","save","star","toggle"]},{"name":"gpp_bad","tags":["bad","cancel","certified","close","error","exit","gpp","no","privacy","private","protect","protection","remove","security","shield","sim","stop","verified","x"]},{"name":"playlist_play","tags":["arrow","collection","list","music","play","playlist"]},{"name":"cast","tags":["Android","OS","airplay","cast","chrome","connect","desktop","device","display","hardware","iOS","mac","monitor","screen","screencast","streaming","television","tv","web","window","wireless"]},{"name":"vertical_align_top","tags":["align","alignment","arrow","doc","edit","editing","editor","sheet","spreadsheet","text","top","type","up","vertical","writing"]},{"name":"ramen_dining","tags":["breakfast","dining","dinner","drink","fastfood","food","lunch","meal","noodles","ramen","restaurant"]},{"name":"data_usage","tags":["analytics","chart","data","diagram","graph","infographic","measure","metrics","statistics","tracking","usage"]},{"name":"markunread_mailbox","tags":["deliver","envelop","letter","mail","mailbox","markunread","post","postal","postbox","receive","send","unread"]},{"name":"terminal","tags":["application","code","emulator","program","software","terminal"]},{"name":"screen_share","tags":["Android","OS","arrow","cast","chrome","device","display","hardware","iOS","laptop","mac","mirror","monitor","screen","share","steam","streaming","web","window"]},{"name":"center_focus_strong","tags":["camera","center","focus","image","lens","photo","photography","strong","zoom"]},{"name":"queue","tags":["add","collection","layers","list","multiple","music","playlist","queue","stack","stream","video"]},{"name":"games","tags":["adjust","arrow","arrows","control","controller","direction","games","gaming","left","move","right"]},{"name":"low_priority","tags":["arrange","arrow","backward","bottom","list","low","move","order","priority"]},{"name":"dynamic_form","tags":["bolt","code","dynamic","electric","fast","form","lightning","lists","questionnaire","thunderbolt"]},{"name":"tab","tags":["browser","computer","document","documents","folder","internet","tab","tabs","web","website","window","windows"]},{"name":"lock_reset","tags":["around","inprogress","load","loading refresh","lock","locked","password","privacy","private","protection","renew","rotate","safety","secure","security","turn"]},{"name":"room_preferences","tags":["building","door","doorway","entrance","gear","home","house","interior","office","open","preferences","room","settings"]},{"name":"crop","tags":["adjust","adjustments","area","crop","edit","editing","frame","image","images","photo","photos","rectangle","settings","size","square"]},{"name":"monitor_weight","tags":["body","device","diet","health","monitor","scale","smart","weight"]},{"name":"trip_origin","tags":["circle","departure","origin","trip"]},{"name":"calendar_view_week","tags":["calendar","date","day","event","format","grid","layout","month","schedule","today","view","week"]},{"name":"signal_wifi_4_bar","tags":["4","bar","cell","cellular","data","internet","mobile","network","phone","signal","wifi","wireless"]},{"name":"blur_on","tags":["blur","disabled","dots","edit","editing","effect","enabled","enhance","filter","off","on","slash"]},{"name":"view_stream","tags":["design","format","grid","layout","lines","list","stacked","stream","view","website"]},{"name":"radio","tags":["antenna","audio","device","frequency","hardware","listen","media","music","player","radio","signal","tune"]},{"name":"hail","tags":["body","hail","human","people","person","pick","public","stop","taxi","transportation"]},{"name":"do_disturb_on","tags":["cancel","close","denied","deny","disabled","disturb","do","enabled","off","on","remove","silence","slash","stop"]},{"name":"sensor_door","tags":["alarm","security","security system"]},{"name":"wb_incandescent","tags":["balance","bright","edit","editing","incandescent","light","lighting","setting","settings","white","wp"]},{"name":"local_drink","tags":["cup","drink","drop","droplet","liquid","local","park","water"]},{"name":"accessible_forward","tags":["accessibility","accessible","body","forward","handicap","help","human","people","person","wheelchair"]},{"name":"replay_circle_filled","tags":["arrow","arrows","circle","control","controls","filled","music","refresh","renew","repeat","replay","video"]},{"name":"local_printshop","tags":["draft","fax","ink","local","machine","office","paper","print","printer","printshop","send"]},{"name":"local_laundry_service","tags":["cleaning","clothing","dry","dryer","hotel","laundry","local","service","washer"]},{"name":"vpn_lock","tags":["earth","globe","lock","locked","network","password","privacy","private","protection","safety","secure","security","virtual","vpn","world"]},{"name":"schema","tags":["analytics","chart","data","diagram","flow","graph","infographic","measure","metrics","schema","statistics","tracking"]},{"name":"request_page","tags":["data","doc","document","drive","file","folder","folders","page","paper","request","sheet","slide","writing"]},{"name":"token","tags":["badge","hexagon","mark","shield","sign","symbol"]},{"name":"branding_watermark","tags":["branding","components","copyright","design","emblem","format","identity","interface","layout","logo","screen","site","stamp","ui","ux","watermark","web","website","window"]},{"name":"theater_comedy","tags":["broadway","comedy","event","movie","musical","places","show","standup","theater","tour","watch"]},{"name":"text_format","tags":["alphabet","character","font","format","letter","square A","style","symbol","text","type"]},{"name":"directions_bus_filled","tags":["automobile","bus","car","cars","direction","directions","filled","maps","public","transportation","vehicle"]},{"name":"remove_done","tags":["approve","check","complete","disabled","done","enabled","finished","mark","multiple","off","ok","on","remove","select","slash","tick","yes"]},{"name":"sports_bar","tags":["alcohol","bar","beer","drink","liquor","pint","places","pub","sports"]},{"name":"watch","tags":["Android","OS","ar","clock","gadget","iOS","time","vr","watch","wearables","web","wristwatch"]},{"name":"add_to_drive","tags":["add","app","application","backup","cloud","drive","files","folders","gdrive","google","recovery","shortcut","storage"]},{"name":"format_align_center","tags":["align","alignment","center","doc","edit","editing","editor","format","sheet","spreadsheet","text","type","writing"]},{"name":"settings_power","tags":["info","information","off","on","power","save","settings","shutdown"]},{"name":"local_pizza","tags":["drink","fastfood","food","local","meal","pizza"]},{"name":"add_alert","tags":["+","active","add","alarm","alert","bell","chime","new","notifications","notify","plus","reminder","ring","sound","symbol"]},{"name":"smart_button","tags":["action","ai","artificial","automatic","automation","button","components","composer","custom","function","genai","intelligence","interface","magic","site","smart","spark","sparkle","special","star","stars","ui","ux","web","website"]},{"name":"flare","tags":["bright","edit","editing","effect","flare","image","images","light","photography","picture","pictures","sun"]},{"name":"developer_mode","tags":["Android","OS","bracket","cell","code","developer","development","device","engineer","hardware","iOS","mobile","mode","phone","tablet"]},{"name":"call_split","tags":["arrow","call","device","mobile","split"]},{"name":"free_breakfast","tags":["beverage","breakfast","cafe","coffee","cup","drink","free","mug","tea"]},{"name":"auto_delete","tags":["auto","bin","can","clock","date","delete","garbage","remove","schedule","time","trash"]},{"name":"sports_kabaddi","tags":["athlete","athletic","body","combat","entertainment","exercise","fighting","game","hobby","human","kabaddi","people","person","social","sports","wrestle","wrestling"]},{"name":"face_retouching_natural","tags":["ai","artificial","automatic","automation","custom","edit","editing","effect","emoji","emotion","face","faces","genai","image","intelligence","magic","natural","photo","photography","retouch","retouching","settings","smart","spark","sparkle","star","tag"]},{"name":"not_listed_location","tags":["?","assistance","destination","direction","help","info","information","listed","location","maps","not","pin","place","punctuation","question mark","stop","support","symbol"]},{"name":"wb_cloudy","tags":["balance","cloud","cloudy","edit","editing","white","wp"]},{"name":"sports","tags":["athlete","athletic","blowing","coach","entertainment","exercise","game","hobby","instrument","referee","social","sound","sports","warning","whistle"]},{"name":"emoji_symbols","tags":["ampersand","character","emoji","hieroglyph","music","note","percent","sign","symbols"]},{"name":"bathtub","tags":["bath","bathing","bathroom","bathtub","home","hotel","human","person","shower","travel","tub"]},{"name":"forward_10","tags":["10","arrow","control","controls","digit","fast","forward","music","number","play","seconds","symbol","video"]},{"name":"tablet_mac","tags":["Android","OS","device","hardware","iOS","ipad","mobile","tablet mac","web"]},{"name":"mode_night","tags":["dark","disturb","lunar","mode","moon","night","sleep"]},{"name":"broken_image","tags":["broken","corrupt","error","image","landscape","mountain","mountains","photo","photography","picture","torn"]},{"name":"escalator_warning","tags":["body","child","escalator","human","kid","parent","people","person","warning"]},{"name":"assistant","tags":["ai","artificial","assistant","automatic","automation","bubble","chat","comment","communicate","custom","feedback","genai","intelligence","magic","message","recommendation","smart","spark","sparkle","speech","star","suggestion","twinkle"]},{"name":"cases","tags":["bag","baggage","briefcase","business","case","cases","purse","suitcase"]},{"name":"wifi_tethering","tags":["cell","cellular","connection","data","internet","mobile","network","phone","scan","service","signal","speed","tethering","wifi","wireless"]},{"name":"reduce_capacity","tags":["arrow","body","capacity","covid","decrease","down","human","people","person","reduce","social"]},{"name":"colorize","tags":["color","colorize","dropper","extract","eye","picker","tool"]},{"name":"save_as","tags":["compose","create","data","disk","document","draft","drive","edit","editing","file","floppy","input","multimedia","pen","pencil","save","storage","write","writing"]},{"name":"card_travel","tags":["bill","card","cash","coin","commerce","cost","credit","currency","dollars","finance","membership","miles","money","online","pay","payment","travel","trip"]},{"name":"emoji_food_beverage","tags":["beverage","coffee","cup","drink","emoji","mug","plate","set","tea"]},{"name":"font_download","tags":["A","alphabet","character","download","font","letter","square","symbol","text","type"]},{"name":"outbox","tags":["box","mail","outbox","send","sent"]},{"name":"battery_std","tags":["battery","cell","charge","mobile","plus","power","standard","std"]},{"name":"sick","tags":["covid","discomfort","emotions","expressions","face","feelings","fever","flu","ill","mood","pain","person","sick","survey","upset"]},{"name":"add_location","tags":["+","add","destination","direction","location","maps","new","pin","place","plus","stop","symbol"]},{"name":"try","tags":["bookmark","bubble","chat","comment","communicate","favorite","feedback","highlight","important","marked","message","save","saved","shape","special","speech","star","try"]},{"name":"discount","tags":[]},{"name":"man","tags":["boy","gender","male","man","social","symbol"]},{"name":"running_with_errors","tags":["!","alert","attention","caution","danger","duration","error","errors","exclamation","important","mark","notification","process","processing","running","symbol","time","warning","with"]},{"name":"diversity_3","tags":["committee","diverse","diversity","family","friends","group","groups","humans","network","people","persons","social","team"]},{"name":"filter_none","tags":["filter","multiple","none","square","stack"]},{"name":"cloud_sync","tags":["app","application","around","backup","cloud","connection","drive","files","folders","inprogress","internet","load","loading refresh","network","renew","rotate","sky","storage","turn","upload"]},{"name":"bloodtype","tags":["blood","bloodtype","donate","droplet","emergency","hospital","medicine","negative","positive","type","water"]},{"name":"dinner_dining","tags":["breakfast","dining","dinner","food","fork","lunch","meal","restaurant","spaghetti","utensils"]},{"name":"transfer_within_a_station","tags":["a","arrow","arrows","body","direction","human","left","maps","people","person","public","right","route","station","stop","transfer","transportation","vehicle","walk","within"]},{"name":"weekend","tags":["chair","couch","furniture","home","living","lounge","relax","room","weekend"]},{"name":"child_friendly","tags":["baby","care","carriage","child","children","friendly","infant","kid","newborn","stroller","toddler","young"]},{"name":"offline_pin","tags":["approve","check","checkmark","circle","complete","done","mark","offline","ok","pin","select","tick","validate","verified","yes"]},{"name":"replay_10","tags":["10","arrow","arrows","control","controls","digit","music","number","refresh","renew","repeat","replay","symbol","ten","video"]},{"name":"brightness_4","tags":["4","brightness","circle","control","crescent","level","moon","screen","sun"]},{"name":"cruelty_free","tags":["animal","bunny","cruelty","eco","free","nature","rabbit","social","sustainability","sustainable","testing"]},{"name":"format_paint","tags":["brush","color","doc","edit","editing","editor","fill","format","paint","roller","sheet","spreadsheet","style","text","type","writing"]},{"name":"filter_center_focus","tags":["camera","center","dot","edit","filter","focus","image","photo","photography","picture"]},{"name":"area_chart","tags":["analytics","area","chart","data","diagram","graph","infographic","measure","metrics","statistics","tracking"]},{"name":"bakery_dining","tags":["bakery","bread","breakfast","brunch","croissant","dining","food"]},{"name":"emoji_transportation","tags":["architecture","automobile","building","car","cars","direction","emoji","estate","maps","place","public","real","residence","residential","shelter","transportation","travel","vehicle"]},{"name":"folder_special","tags":["bookmark","data","doc","document","drive","favorite","file","folder","highlight","important","marked","save","saved","shape","sheet","slide","special","star","storage"]},{"name":"door_front","tags":["closed","door","doorway","entrance","exit","front","home","house","way"]},{"name":"calendar_view_day","tags":["calendar","date","day","event","format","grid","layout","month","schedule","today","view","week"]},{"name":"legend_toggle","tags":["analytics","chart","data","diagram","graph","infographic","legend","measure","metrics","monitoring","stackdriver","statistics","toggle","tracking"]},{"name":"light","tags":["bulb","ceiling","hanging","inside","interior","lamp","light","lighting","pendent","room"]},{"name":"find_replace","tags":["around","arrows","find","glass","inprogress","load","loading refresh","look","magnify","magnifying","renew","replace","rotate","search","see"]},{"name":"crop_original","tags":["adjust","adjustments","area","crop","edit","editing","frame","image","images","original","photo","photos","picture","settings","size"]},{"name":"rowing","tags":["activity","boat","body","canoe","human","people","person","row","rowing","sport","water"]},{"name":"enhanced_encryption","tags":["+","add","encryption","enhanced","lock","locked","new","password","plus","privacy","private","protection","safety","secure","security","symbol"]},{"name":"how_to_vote","tags":["ballot","election","how","poll","to","vote"]},{"name":"chrome_reader_mode","tags":["chrome","mode","read","reader","text"]},{"name":"auto_fix_normal","tags":["ai","artificial","auto","automatic","automation","custom","edit","erase","fix","genai","intelligence","magic","modify","smart","spark","sparkle","star","wand"]},{"name":"compress","tags":["arrow","arrows","collide","compress","pressure","push","together"]},{"name":"dehaze","tags":["adjust","dehaze","edit","editing","enhance","haze","image","lines","photo","photography","remove"]},{"name":"outlet","tags":["connect","connecter","electricity","outlet","plug","power"]},{"name":"desktop_mac","tags":["Android","OS","chrome","desktop","device","display","hardware","iOS","mac","monitor","screen","web","window"]},{"name":"nature_people","tags":["activity","body","forest","human","nature","outdoor","outside","park","people","person","tree","wilderness"]},{"name":"sports_tennis","tags":["athlete","athletic","ball","bat","entertainment","exercise","game","hobby","racket","social","sports","tennis"]},{"name":"forest","tags":["forest","jungle","nature","plantation","plants","trees","woodland"]},{"name":"upcoming","tags":["alarm","calendar","mail","message","notification","upcoming"]},{"name":"assignment_returned","tags":["arrow","assignment","clipboard","doc","document","down","returned"]},{"name":"cookie","tags":["biscuit","cookies","data","dessert","wafer"]},{"name":"fax","tags":["fax","machine","office","phone","send"]},{"name":"square","tags":["draw","four","shape quadrangle","sides","square"]},{"name":"density_medium","tags":["density","horizontal","lines","medium","rule","rules"]},{"name":"terrain","tags":["geography","landscape","mountain","terrain"]},{"name":"settings_brightness","tags":["brightness","dark","filter","light","mode","setting","settings"]},{"name":"attach_email","tags":["attach","attachment","clip","compose","email","envelop","letter","link","mail","message","send"]},{"name":"photo","tags":["image","mountain","mountains","photo","photography","picture"]},{"name":"http","tags":["alphabet","character","font","http","letter","symbol","text","transfer","type","url","website"]},{"name":"garage","tags":["automobile","automotive","car","cars","direction","garage","maps","transportation","travel","vehicle"]},{"name":"wine_bar","tags":["alcohol","bar","cocktail","cup","drink","glass","liquor","wine"]},{"name":"multiple_stop","tags":["arrows","directions","dots","left","maps","multiple","navigation","right","stop"]},{"name":"format_color_text","tags":["color","doc","edit","editing","editor","fill","format","paint","sheet","spreadsheet","style","text","type","writing"]},{"name":"gesture","tags":["drawing","finger","gesture","gestures","hand","motion"]},{"name":"heart_broken","tags":["break","broken","core","crush","health","heart","nucleus","split"]},{"name":"format_align_right","tags":["align","alignment","doc","edit","editing","editor","format","right","sheet","spreadsheet","text","type","writing"]},{"name":"transgender","tags":["female","gender","lgbt","male","neutral","social","symbol","transgender"]},{"name":"alarm_add","tags":["+","add","alarm","alert","bell","clock","countdown","date","new","notification","plus","schedule","symbol","time"]},{"name":"new_label","tags":["+","add","archive","bookmark","favorite","label","library","new","plus","read","reading","remember","ribbon","save","symbol","tag"]},{"name":"south_east","tags":["arrow","directional","down","east","maps","navigation","right","south"]},{"name":"backup_table","tags":["backup","drive","files folders","format","layout","stack","storage","table"]},{"name":"unsubscribe","tags":["cancel","close","email","envelop","letter","mail","message","newsletter","off","remove","send","subscribe","unsubscribe"]},{"name":"flash_off","tags":["bolt","disabled","electric","enabled","fast","flash","lightning","off","on","slash","thunderbolt"]},{"name":"elderly","tags":["body","cane","elderly","human","old","people","person","senior"]},{"name":"generating_tokens","tags":["access","ai","api","artificial","automatic","automation","coin","custom","genai","generating","intelligence","magic","smart","spark","sparkle","star","tokens"]},{"name":"spellcheck","tags":["a","alphabet","approve","character","check","font","letter","mark","ok","processor","select","spell","spellcheck","symbol","text","tick","type","word","write","yes"]},{"name":"auto_awesome_mosaic","tags":["adjust","auto","awesome","collage","edit","editing","enhance","image","mosaic","photo"]},{"name":"outdoor_grill","tags":["barbecue","bbq","charcoal","cooking","grill","home","house","outdoor","outside"]},{"name":"restore_page","tags":["arrow","data","doc","file","page","paper","refresh","restore","rotate","sheet","storage"]},{"name":"foundation","tags":["architecture","base","basis","building","construction","estate","foundation","home","house","real","residential"]},{"name":"credit_card_off","tags":["card","charge","commerce","cost","credit","disabled","enabled","finance","money","off","online","pay","payment","slash"]},{"name":"scatter_plot","tags":["analytics","bar","bars","chart","circles","data","diagram","dot","graph","infographic","measure","metrics","plot","scatter","statistics","tracking"]},{"name":"signal_cellular_4_bar","tags":["4","bar","cell","cellular","data","internet","mobile","network","phone","signal","speed","wifi","wireless"]},{"name":"add_moderator","tags":["+","add","certified","moderator","new","plus","privacy","private","protect","protection","security","shield","symbol","verified"]},{"name":"play_for_work","tags":["arrow","circle","down","google","half","play","work"]},{"name":"add_card","tags":["+","add","bill","card","cash","coin","commerce","cost","credit","currency","dollars","finance","money","new","online","pay","payment","plus","price","shopping","symbol"]},{"name":"app_settings_alt","tags":["Android","OS","app","applications","cell","device","gear","hardware","iOS","mobile","phone","setting","settings","tablet"]},{"name":"keyboard_tab","tags":["arrow","keyboard","left","next","right","tab"]},{"name":"wifi_protected_setup","tags":["around","arrow","arrows","protected","rotate","setup","wifi"]},{"name":"deck","tags":["chairs","deck","home","house","outdoors","outside","patio","social","terrace","umbrella","yard"]},{"name":"takeout_dining","tags":["box","container","delivery","dining","food","meal","restaurant","takeout"]},{"name":"tag_faces","tags":["emoji","emotion","faces","happy","satisfied","smile","tag"]},{"name":"brightness_6","tags":["6","brightness","circle","control","crescent","level","moon","screen","sun"]},{"name":"woman","tags":["female","gender","girl","lady","social","symbol","woman","women"]},{"name":"assistant_direction","tags":["assistant","destination","direction","location","maps","navigate","navigation","pin","place","right","stop"]},{"name":"brightness_5","tags":["5","brightness","circle","control","crescent","level","moon","screen","sun"]},{"name":"social_distance","tags":["6","apart","body","distance","ft","human","people","person","social","space"]},{"name":"free_cancellation","tags":["approve","calendar","cancel","cancellation","check","complete","date","day","done","event","exit","free","mark","month","no","ok","remove","schedule","select","stop","tick","validate","verified","x","yes"]},{"name":"subdirectory_arrow_left","tags":["arrow","directory","down","left","navigation","sub","subdirectory"]},{"name":"laptop_chromebook","tags":["Android","OS","chrome","chromebook","device","display","hardware","iOS","laptop","mac chromebook","monitor","screen","web","window"]},{"name":"format_list_numbered_rtl","tags":["align","alignment","digit","doc","edit","editing","editor","format","list","notes","number","numbered","rtl","sheet","spreadsheet","symbol","text","type","writing"]},{"name":"store_mall_directory","tags":["directory","mall","store"]},{"name":"settings_overscan","tags":["arrows","expand","image","photo","picture","scan","settings"]},{"name":"icecream","tags":["cream","dessert","food","ice","icecream","snack"]},{"name":"details","tags":["details","edit","editing","enhance","image","photo","photography","sharpen","triangle"]},{"name":"add_reaction","tags":["+","add","emoji","emotions","expressions","face","feelings","glad","happiness","happy","icon","icons","insert","like","mood","new","person","pleased","plus","smile","smiling","social","survey","symbol"]},{"name":"follow_the_signs","tags":["arrow","body","directional","follow","human","people","person","right","signs","social","the"]},{"name":"attribution","tags":["attribute","attribution","body","copyright","copywriter","human","people","person"]},{"name":"food_bank","tags":["architecture","bank","building","charity","eat","estate","food","fork","house","knife","meal","place","real","residence","residential","shelter","utensils"]},{"name":"closed_caption","tags":["accessible","alphabet","caption","cc","character","closed","decoder","font","language","letter","media","movies","subtitle","subtitles","symbol","text","tv","type"]},{"name":"gif","tags":["alphabet","animated","animation","bitmap","character","font","format","gif","graphics","interchange","letter","symbol","text","type"]},{"name":"phonelink","tags":["Android","OS","chrome","computer","connect","desktop","device","hardware","iOS","link","mac","mobile","phone","phonelink","sync","tablet","web","windows"]},{"name":"grain","tags":["dots","edit","editing","effect","filter","grain","image","images","photography","picture","pictures"]},{"name":"personal_injury","tags":["accident","aid","arm","bandage","body","broke","cast","fracture","health","human","injury","medical","patient","people","person","personal","sling","social"]},{"name":"flip_camera_android","tags":["android","camera","center","edit","editing","flip","image","mobile","orientation","rotate","turn"]},{"name":"museum","tags":["architecture","attraction","building","estate","event","exhibition","explore","local","museum","places","real","see","shop","store","tour"]},{"name":"north_west","tags":["arrow","directional","left","maps","navigation","north","up","west"]},{"name":"gite","tags":["architecture","estate","gite","home","hostel","house","maps","place","real","residence","residential","stay","traveling"]},{"name":"highlight","tags":["color","doc","edit","editing","editor","emphasize","fill","flash","format","highlight","light","paint","sheet","spreadsheet","style","text","type","writing"]},{"name":"brightness_1","tags":["1","brightness","circle","control","crescent","level","moon","screen"]},{"name":"plus_one","tags":["1","add","digit","increase","number","one","plus","symbol"]},{"name":"villa","tags":["architecture","beach","estate","home","house","maps","place","real","residence","residential","traveling","vacation stay","villa"]},{"name":"fmd_bad","tags":["!","alert","attention","bad","caution","danger","destination","direction","error","exclamation","fmd","important","location","maps","mark","notification","pin","place","symbol","warning"]},{"name":"flashlight_on","tags":["disabled","enabled","flash","flashlight","light","off","on","slash"]},{"name":"flip","tags":["edit","editing","flip","image","orientation","scan scanning"]},{"name":"nightlife","tags":["alcohol","bar","bottle","club","cocktail","dance","drink","food","glass","liquor","music","nightlife","note","wine"]},{"name":"present_to_all","tags":["all","arrow","present","presentation","screen","share","site","slides","to","web","website"]},{"name":"do_disturb","tags":["cancel","close","denied","deny","disturb","do","remove","silence","stop"]},{"name":"outbound","tags":["arrow","circle","directional","outbound","right","up"]},{"name":"local_pharmacy","tags":["911","aid","cross","emergency","first","hospital","local","medicine","pharmacy","places"]},{"name":"splitscreen","tags":["column","grid","layout","multitasking","row","screen","split","splitscreen","two"]},{"name":"waterfall_chart","tags":["analytics","bar","chart","data","diagram","graph","infographic","measure","metrics","statistics","tracking","waterfall"]},{"name":"switch_left","tags":["arrows","directional","left","navigation","switch","toggle"]},{"name":"domain_verification","tags":["app","application desktop","approve","check","complete","design","domain","done","interface","internet","layout","mark","ok","screen","select","site","tick","ui","ux","validate","verification","verified","web","website","window","www","yes"]},{"name":"fireplace","tags":["chimney","fire","fireplace","flame","home","house","living","pit","place","room","warm","winter"]},{"name":"video_settings","tags":["change","details","gear","info","information","options","play","screen","service","setting","settings","video","window"]},{"name":"disabled_visible","tags":["cancel","close","disabled","exit","eye","no","on","quit","remove","reveal","see","show","stop","view","visibility","visible"]},{"name":"network_wifi","tags":["cell","cellular","data","internet","mobile","network","phone","speed","wifi","wireless"]},{"name":"quickreply","tags":["bolt","bubble","chat","comment","communicate","fast","lightning","message","quick","quickreply","reply","speech","thunderbolt"]},{"name":"swap_vertical_circle","tags":["arrow","arrows","circle","down","swap","up","vertical"]},{"name":"format_align_justify","tags":["align","alignment","density","doc","edit","editing","editor","extra","format","justify","sheet","small","spreadsheet","text","type","writing"]},{"name":"settings_input_composite","tags":["component","composite","connection","connectivity","input","plug","points","settings"]},{"name":"loupe","tags":["+","add","details","focus","glass","loupe","magnifying","new","plus","symbol"]},{"name":"123","tags":["1","2","3","digit","number","symbol"]},{"name":"network_check","tags":["check","connect","connection","internet","meter","network","signal","speed","tick","wifi","wireless"]},{"name":"sms_failed","tags":["!","alert","attention","bubbles","caution","chat","communication","conversation","danger","error","exclamation","failed","feedback","important","mark","message","notification","service","sms","speech","symbol","warning"]},{"name":"cancel_schedule_send","tags":["cancel","email","mail","no","quit","remove","schedule","send","share","stop","x"]},{"name":"work_history","tags":["back","backwards","bag","baggage","briefcase","business","case","clock","date","history","job","pending","recent","schedule","suitcase","time","updates","work"]},{"name":"electric_bolt","tags":["bolt","electric","energy","fast","lightning","nest","thunderbolt"]},{"name":"view_day","tags":["cards","carousel","day","design","format","grid","layout","view","website"]},{"name":"night_shelter","tags":["architecture","bed","building","estate","homeless","house","night","place","real","shelter","sleep"]},{"name":"monitor","tags":["Android","OS","chrome","device","display","hardware","iOS","mac","monitor","screen","web","window"]},{"name":"clean_hands","tags":["bacteria","clean","disinfect","germs","gesture","hand","hands","sanitize","sanitizer"]},{"name":"mark_chat_read","tags":["approve","bubble","chat","check","comment","communicate","complete","done","mark","message","ok","read","select","sent","speech","tick","verified","yes"]},{"name":"comment_bank","tags":["archive","bank","bookmark","bubble","cchat","comment","communicate","favorite","label","library","message","remember","ribbon","save","speech","tag"]},{"name":"sim_card_download","tags":["arrow","camera","card","chip","device","down","download","memory","phone","sim","storage"]},{"name":"lan","tags":["computer","connection","data","internet","lan","network","service"]},{"name":"piano","tags":["instrument","keyboard","keys","music","musical","piano","social"]},{"name":"add_road","tags":["+","add","destination","direction","highway","maps","new","plus","road","stop","street","symbol","traffic"]},{"name":"add_ic_call","tags":["+","add","call","cell","contact","device","hardware","mobile","new","phone","plus","symbol","telephone"]},{"name":"rule_folder","tags":["approve","cancel","check","close","complete","data","doc","document","done","drive","exit","file","folder","mark","no","ok","remove","rule","select","sheet","slide","storage","tick","validate","verified","x","yes"]},{"name":"switch_access_shortcut","tags":["access","arrow","arrows","direction","navigation","new","north","shortcut","switch","symbol","up"]},{"name":"hardware","tags":["break","construction","hammer","hardware","nail","repair","tool"]},{"name":"line_weight","tags":["height","line","size","spacing","style","thickness","weight"]},{"name":"image_not_supported","tags":["disabled","enabled","image","landscape","mountain","mountains","not","off","on","photo","photography","picture","slash","supported"]},{"name":"flip_camera_ios","tags":["DISABLE_IOS","android","camera","disable_ios","edit","editing","flip","image","ios","mobile","orientation","rotate","turn"]},{"name":"phone_callback","tags":["arrow","call","callback","cell","contact","device","down","hardware","mobile","phone","telephone"]},{"name":"access_time_filled","tags":[]},{"name":"dining","tags":["cafe","cafeteria","cutlery","diner","dining","eat","eating","fork","room","spoon"]},{"name":"scale","tags":["measure","monitor","scale","weight"]},{"name":"airplanemode_active","tags":["active","airplane","airplanemode","flight","mode","on","signal"]},{"name":"set_meal","tags":["chopsticks","dinner","fish","food","lunch","meal","restaurant","set","teishoku"]},{"name":"mobile_friendly","tags":["Android","OS","approve","cell","check","complete","device","done","friendly","hardware","iOS","mark","mobile","ok","phone","select","tablet","tick","validate","verified","yes"]},{"name":"assured_workload","tags":["assured","compliance","confidential","federal","government","secure","sensitive regulatory","workload"]},{"name":"wallet","tags":[]},{"name":"merge_type","tags":["arrow","combine","direction","format","merge","text","type"]},{"name":"view_timeline","tags":["grid","layout","pattern","squares","timeline","view"]},{"name":"departure_board","tags":["automobile","board","bus","car","cars","clock","departure","maps","public","schedule","time","transportation","travel","vehicle"]},{"name":"event_repeat","tags":["around","calendar","date","day","event","inprogress","load","loading refresh","month","renew","rotate","schedule","turn"]},{"name":"sanitizer","tags":["bacteria","bottle","clean","covid","disinfect","germs","pump","sanitizer"]},{"name":"surfing","tags":["athlete","athletic","beach","body","entertainment","exercise","hobby","human","people","person","sea","social sports","sports","summer","surfing","water"]},{"name":"pix","tags":["bill","brazil","card","cash","commerce","credit","currency","finance","money","payment"]},{"name":"phonelink_ring","tags":["Android","OS","cell","connection","data","device","hardware","iOS","mobile","network","phone","phonelink","ring","service","signal","tablet","wireless"]},{"name":"display_settings","tags":["Android","OS","application","change","chrome","desktop","details","device","display","gear","hardware","iOS","info","information","mac","monitor","options","personal","screen","service","settings","web","window"]},{"name":"sports_motorsports","tags":["athlete","athletic","automobile","bike","drive","driving","entertainment","helmet","hobby","motorcycle","motorsports","protect","social","sports","vehicle"]},{"name":"horizontal_split","tags":["bars","format","horizontal","layout","lines","split","stacked"]},{"name":"view_comfy","tags":["comfy","grid","layout","pattern","squares","view"]},{"name":"polymer","tags":["emblem","logo","mark","polymer"]},{"name":"golf_course","tags":["athlete","athletic","ball","club","course","entertainment","flag","golf","golfer","golfing","hobby","hole","places","putt","sports"]},{"name":"batch_prediction","tags":["batch","bulb","idea","light","prediction"]},{"name":"filter_1","tags":["1","digit","edit","editing","effect","filter","image","images","multiple","number","photography","picture","pictures","settings","stack","symbol"]},{"name":"stay_current_portrait","tags":["Android","OS","current","device","hardware","iOS","mobile","phone","portrait","stay","tablet"]},{"name":"usb","tags":["cable","connection","device","usb","wire"]},{"name":"featured_play_list","tags":["collection","featured","highlighted","list","music","play","playlist","recommended"]},{"name":"data_object","tags":["brackets","code","coder","data","object","parentheses"]},{"name":"co_present","tags":["arrow","co-present","presentation","screen","share","site","slides","togather","web","website"]},{"name":"ev_station","tags":["automobile","car","cars","charging","electric","electricity","ev","maps","places","station","transportation","vehicle"]},{"name":"send_and_archive","tags":["archive","arrow","down","download","email","letter","mail","save","send","share"]},{"name":"send_to_mobile","tags":["Android","OS","arrow","device","export","forward","hardware","iOS","mobile","phone","right","send","share","tablet","to"]},{"name":"local_see","tags":["camera","lens","local","photo","photography","picture","see"]},{"name":"satellite_alt","tags":["alternative","artificial","communication","satellite","space","space station","television"]},{"name":"flatware","tags":["cafe","cafeteria","cutlery","diner","dining","eat","eating","fork","room","spoon"]},{"name":"speaker","tags":["box","electronic","loud","music","sound","speaker","stereo","system","video"]},{"name":"adb","tags":["adb","android","bridge","debug"]},{"name":"movie_creation","tags":["cinema","clapperboard","creation","film","movie","movies","slate","video"]},{"name":"picture_in_picture","tags":["crop","cropped","overlap","photo","picture","position","shape"]},{"name":"call_received","tags":["arrow","call","device","mobile","received"]},{"name":"battery_alert","tags":["!","alert","attention","battery","caution","cell","charge","danger","error","exclamation","important","mark","mobile","notification","power","symbol","warning"]},{"name":"system_update","tags":["Android","OS","arrow","arrows","cell","device","direction","down","download","hardware","iOS","install","mobile","phone","system","tablet","update"]},{"name":"webhook","tags":["api","developer","development","enterprise","software","webhook"]},{"name":"add_chart","tags":["+","add","analytics","bar","bars","chart","data","diagram","graph","infographic","measure","metrics","new","plus","statistics","symbol","tracking"]},{"name":"pan_tool_alt","tags":["fingers","gesture","hand","hands","human","move","pan","scan","stop","tool"]},{"name":"sports_handball","tags":["athlete","athletic","ball","body","entertainment","exercise","game","handball","hobby","human","people","person","social","sports"]},{"name":"electric_car","tags":["automobile","car","cars","electric","electricity","maps","transportation","travel","vehicle"]},{"name":"phone_forwarded","tags":["arrow","call","cell","contact","device","direction","forwarded","hardware","mobile","phone","right","telephone"]},{"name":"add_to_photos","tags":["add","collection","image","landscape","mountain","mountains","photo","photography","photos","picture","plus","to"]},{"name":"power_off","tags":["charge","cord","disabled","electric","electrical","enabled","off","on","outlet","plug","power","slash"]},{"name":"noise_control_off","tags":["audio","aware","cancel","cancellation","control","disabled","enabled","music","noise","note","off","offline","on","slash","sound"]},{"name":"code_off","tags":["brackets","code","css","develop","developer","disabled","enabled","engineer","engineering","html","off","on","platform","slash"]},{"name":"bookmark_remove","tags":["bookmark","delete","favorite","minus","remember","remove","ribbon","save","subtract"]},{"name":"screen_search_desktop","tags":["Android","OS","arrow","desktop","device","hardware","iOS","lock","monitor","rotate","screen","web"]},{"name":"panorama","tags":["angle","image","mountain","mountains","panorama","photo","photography","picture","view","wide"]},{"name":"settings_bluetooth","tags":["bluetooth","connect","connection","connectivity","device","settings","signal","symbol"]},{"name":"sports_baseball","tags":["athlete","athletic","ball","baseball","entertainment","exercise","game","hobby","social","sports"]},{"name":"festival","tags":["circus","event","festival","local","maps","places","tent","tour","travel"]},{"name":"lens_blur","tags":["blur","camera","dim","dot","effect","foggy","fuzzy","image","lens","photo","soften"]},{"name":"plumbing","tags":["build","construction","fix","handyman","plumbing","repair","tools","wrench"]},{"name":"toys","tags":["car","games","kids","toy","toys","windmill"]},{"name":"coffee_maker","tags":["appliances","beverage","coffee","cup","drink","machine","maker","mug"]},{"name":"edit_notifications","tags":["active","alarm","alert","bell","chime","compose","create","draft","edit","editing","input","new","notifications","notify","pen","pencil","reminder","ring","sound","write","writing"]},{"name":"personal_video","tags":["Android","OS","cam","chrome","desktop","device","hardware","iOS","mac","monitor","personal","television","tv","video","web","window"]},{"name":"animation","tags":["animation","circles","film","motion","movement","sequence","video"]},{"name":"bedtime","tags":["bedtime","nightime","sleep"]},{"name":"gamepad","tags":["buttons","console","controller","device","game","gamepad","gaming","playstation","video"]},{"name":"diversity_1","tags":["committee","diverse","diversity","family","friends","group","groups","heart","humans","network","people","persons","social","team"]},{"name":"center_focus_weak","tags":["camera","center","focus","image","lens","photo","photography","weak","zoom"]},{"name":"signal_wifi_statusbar_4_bar","tags":["4","bar","cell","cellular","data","internet","mobile","network","phone","signal","speed","statusbar","wifi","wireless"]},{"name":"manage_history","tags":["application","arrow","back","backwards","change","clock","date","details","gear","history","options","refresh","renew","reverse","rotate","schedule","settings","time","turn"]},{"name":"folder_zip","tags":["compress","data","doc","document","drive","file","folder","folders","open","sheet","slide","storage","zip"]},{"name":"flag_circle","tags":["circle","country","flag","goal","mark","nation","report","round","start"]},{"name":"south_west","tags":["arrow","directional","down","left","maps","navigation","south","west"]},{"name":"looks_4","tags":["4","digit","looks","numbers","square","symbol"]},{"name":"cloud_circle","tags":["app","application","backup","circle","cloud","connection","drive","files","folders","internet","network","sky","storage","upload"]},{"name":"format_shapes","tags":["alphabet","character","color","doc","edit","editing","editor","fill","font","format","letter","paint","shapes","sheet","spreadsheet","style","symbol","text","type","writing"]},{"name":"car_rental","tags":["automobile","car","cars","key","maps","rental","transportation","vehicle"]},{"name":"movie_filter","tags":["ai","artificial","automatic","automation","clapperboard","creation","custom","film","filter","genai","intelligence","magic","movie","movies","slate","smart","spark","sparkle","star","stars","video"]},{"name":"layers_clear","tags":["arrange","clear","delete","disabled","enabled","interaction","layers","maps","off","on","overlay","pages","slash"]},{"name":"phonelink_lock","tags":["Android","OS","cell","connection","device","erase","hardware","iOS","lock","locked","mobile","password","phone","phonelink","privacy","private","protection","safety","secure","security","tablet"]},{"name":"attractions","tags":["amusement","attractions","entertainment","ferris","fun","maps","park","places","wheel"]},{"name":"playlist_add_check_circle","tags":["add","album","artist","audio","cd","check","circle","collection","list","mark","music","playlist","record","sound","track"]},{"name":"hive","tags":["bee","honey","honeycomb"]},{"name":"no_photography","tags":["camera","disabled","enabled","image","no","off","on","photo","photography","picture","slash"]},{"name":"content_paste_go","tags":["clipboard","content","disabled","doc","document","enabled","file","go","on","paste","slash"]},{"name":"shop_two","tags":["add","arrow","buy","cart","google","play","purchase","shop","shopping","two"]},{"name":"edit_location","tags":["destination","direction","edit","location","maps","pen","pencil","pin","place","stop"]},{"name":"screen_rotation","tags":["Android","OS","arrow","device","hardware","iOS","mobile","phone","rotate","rotation","screen","tablet","turn"]},{"name":"numbers","tags":["digit","number","numbers","symbol"]},{"name":"sim_card","tags":["camera","card","chip","device","memory","phone","sim","storage"]},{"name":"control_camera","tags":["adjust","arrow","arrows","camera","center","control","direction","left","move","right"]},{"name":"blender","tags":["appliance","blender","cooking","electric","juicer","kitchen","machine","vitamix"]},{"name":"flip_to_front","tags":["arrange","arrangement","back","flip","format","front","layout","move","order","sort","to"]},{"name":"sports_volleyball","tags":["athlete","athletic","ball","entertainment","exercise","game","hobby","social","sports","volleyball"]},{"name":"stairs","tags":["down","staircase","stairs","up"]},{"name":"keyboard_alt","tags":["alt","computer","device","hardware","input","keyboard","keypad","letter","office","text","type"]},{"name":"crop_din","tags":["adjust","adjustments","area","crop","din","edit","editing","frame","image","images","photo","photos","rectangle","settings","size","square"]},{"name":"html","tags":["alphabet","brackets","character","code","css","develop","developer","engineer","engineering","font","html","letter","platform","symbol","text","type"]},{"name":"signal_wifi_statusbar_connected_no_internet_4","tags":["!","4","alert","attention","caution","cell","cellular","connected","danger","data","error","exclamation","important","internet","mark","mobile","network","no","notification","phone","signal","speed","statusbar","symbol","warning","wifi","wireless"]},{"name":"pivot_table_chart","tags":["analytics","arrow","arrows","bar","bars","chart","data","diagram","direction","drive","edit","editing","graph","grid","infographic","measure","metrics","pivot","rotate","sheet","statistics","table","tracking"]},{"name":"microwave","tags":["appliance","cooking","electric","heat","home","house","kitchen","machine","microwave"]},{"name":"folder_copy","tags":["content","copy","cut","data","doc","document","drive","duplicate","file","folder","folders","multiple","paste","sheet","slide","storage"]},{"name":"output","tags":[]},{"name":"gif_box","tags":["alphabet","animated","animation","bitmap","character","font","format","gif","graphics","interchange","letter","symbol","text","type"]},{"name":"voice_chat","tags":["bubble","cam","camera","chat","comment","communicate","facetime","feedback","message","speech","video","voice"]},{"name":"local_convenience_store","tags":["--","24","bill","building","business","card","cash","coin","commerce","company","convenience","credit","currency","dollars","local","maps","market","money","new","online","pay","payment","plus","shop","shopping","store","storefront","symbol"]},{"name":"gps_not_fixed","tags":["destination","direction","disabled","enabled","gps","location","maps","not fixed","off","on","online","place","pointer","slash","tracking"]},{"name":"high_quality","tags":["alphabet","character","definition","display","font","high","hq","letter","movie","movies","quality","resolution","screen","symbol","text","tv","type"]},{"name":"switch_right","tags":["arrows","directional","navigation","right","switch","toggle"]},{"name":"pages","tags":["article","gplus","pages","paper","post","star"]},{"name":"table_restaurant","tags":["bar","dining","table"]},{"name":"speaker_notes_off","tags":["bubble","chat","comment","communicate","disabled","enabled","format","list","message","notes","off","on","slash","speaker","speech","text"]},{"name":"phone_disabled","tags":["call","cell","contact","device","disabled","enabled","hardware","mobile","off","offline","on","phone","slash","telephone"]},{"name":"eject","tags":["disc","drive","dvd","eject","remove","triangle","usb"]},{"name":"control_point_duplicate","tags":["+","add","circle","control","duplicate","multiple","new","plus","point","symbol"]},{"name":"filter","tags":["edit","editing","effect","filter","image","landscape","mountain","mountains","photo","photography","picture","settings"]},{"name":"pest_control","tags":["bug","control","exterminator","insects","pest"]},{"name":"backpack","tags":["back","backpack","bag","book","bookbag","knapsack","pack","storage","travel"]},{"name":"leak_add","tags":["add","connection","data","leak","link","network","service","signals","synce","wireless"]},{"name":"zoom_in_map","tags":["arrow","arrows","destination","in","location","maps","move","place","stop","zoom"]},{"name":"brightness_7","tags":["7","brightness","circle","control","crescent","level","moon","screen","sun"]},{"name":"system_security_update_good","tags":["Android","OS","approve","cell","check","complete","device","done","good","hardware","iOS","mark","mobile","ok","phone","security","select","system","tablet","tick","update","validate","verified","yes"]},{"name":"ring_volume","tags":["call","calling","cell","contact","device","hardware","incoming","mobile","phone","ring","ringer","sound","telephone","volume"]},{"name":"money_off_csred","tags":["bill","card","cart","cash","coin","commerce","credit","csred","currency","disabled","dollars","enabled","money","off","on","online","pay","payment","shopping","slash","symbol"]},{"name":"sports_football","tags":["athlete","athletic","ball","entertainment","exercise","football","game","hobby","social","sports"]},{"name":"nature","tags":["forest","nature","outdoor","outside","park","tree","wilderness"]},{"name":"vibration","tags":["Android","OS","alert","cell","device","hardware","iOS","mobile","mode","motion","notification","phone","silence","silent","tablet","vibrate","vibration"]},{"name":"snippet_folder","tags":["data","doc","document","drive","file","folder","sheet","slide","snippet","storage"]},{"name":"edit_road","tags":["destination","direction","edit","highway","maps","pen","pencil","road","street","traffic"]},{"name":"run_circle","tags":["body","circle","exercise","human","people","person","run","running"]},{"name":"dry_cleaning","tags":["cleaning","dry","hanger","hotel","laundry","places","service","towel"]},{"name":"alarm_off","tags":["alarm","alert","bell","clock","disabled","duration","enabled","notification","off","on","slash","time","timer","watch"]},{"name":"perm_data_setting","tags":["data","gear","info","information","perm","settings"]},{"name":"bedroom_parent","tags":["bed","bedroom","double","full","furniture","home","hotel","house","king","night","parent","pillows","queen","rest","room","sizem master","sleep"]},{"name":"airline_seat_recline_normal","tags":["airline","body","extra","feet","human","leg","legroom","normal","people","person","recline","seat","sitting","space","travel"]},{"name":"currency_bitcoin","tags":["bill","blockchain","card","cash","coin","commerce","cost","credit","currency","digital","dollars","finance","franc","money","online","pay","payment","price","shopping","symbol"]},{"name":"do_disturb_alt","tags":["cancel","close","denied","deny","disturb","do","remove","silence","stop"]},{"name":"sensor_window","tags":["alarm","security","security system"]},{"name":"incomplete_circle","tags":["chart","circle","incomplete"]},{"name":"settings_input_hdmi","tags":["cable","connection","connectivity","definition","hdmi","high","input","plug","plugin","points","settings","video","wire"]},{"name":"camera_indoor","tags":["architecture","building","camera","estate","film","filming","home","house","image","indoor","inside","motion","nest","picture","place","real","residence","residential","shelter","video","videography"]},{"name":"edit_location_alt","tags":["alt","edit","location","pen","pencil","pin"]},{"name":"texture","tags":["diagonal","lines","pattern","stripes","texture"]},{"name":"location_off","tags":["destination","direction","location","maps","off","pin","place","room","stop"]},{"name":"edit_attributes","tags":["approve","attribution","check","complete","done","edit","mark","ok","select","tick","validate","verified","yes"]},{"name":"duo","tags":["call","chat","conference","device","duo","video"]},{"name":"slow_motion_video","tags":["arrow","control","controls","motion","music","play","slow","speed","video"]},{"name":"perm_scan_wifi","tags":["alert","announcement","connection","info","information","internet","network","perm","scan","service","signal","wifi","wireless"]},{"name":"phonelink_setup","tags":["Android","OS","call","chat","device","hardware","iOS","info","mobile","phone","phonelink","settings","setup","tablet","text"]},{"name":"hourglass_disabled","tags":["clock","countdown","disabled","empty","enabled","hourglass","loading","minute","minutes","off","on","slash","time","wait","waiting"]},{"name":"add_to_queue","tags":["+","Android","OS","add","chrome","desktop","device","display","hardware","iOS","mac","monitor","new","plus","queue","screen","symbol","to","web","window"]},{"name":"pie_chart_outline","tags":["analytics","bar","bars","chart","data","diagram","graph","infographic","measure","metrics","outline","pie","statistics","tracking"]},{"name":"playlist_remove","tags":["-","collection","list","minus","music","playlist","remove"]},{"name":"next_week","tags":["arrow","bag","baggage","briefcase","business","case","next","suitcase","week"]},{"name":"church","tags":["christian","christianity","religion","spiritual","worship"]},{"name":"medical_information","tags":["badge","card","health","id","information","medical","services"]},{"name":"view_compact","tags":["compact","grid","layout","pattern","squares","view"]},{"name":"timer_off","tags":["alarm","alert","bell","clock","disabled","duration","enabled","notification","off","on","slash","stop","time","timer","watch"]},{"name":"bluetooth_connected","tags":["bluetooth","cast","connect","connection","device","paring","streaming","symbol","wireless"]},{"name":"photo_size_select_actual","tags":["actual","image","mountain","mountains","photo","photography","picture","select","size"]},{"name":"short_text","tags":["brief","comment","doc","document","note","short","text","write","writing"]},{"name":"bedroom_baby","tags":["babies","baby","bedroom","child","children","home","horse","house","infant","kid","newborn","rocking","room","toddler","young"]},{"name":"video_camera_back","tags":["back","camera","image","landscape","mountain","mountains","photo","photography","picture","rear","video"]},{"name":"bathroom","tags":["bath","bathroom","closet","home","house","place","plumbing","room","shower","sprinkler","wash","water","wc"]},{"name":"downhill_skiing","tags":["athlete","athletic","body","downhill","entertainment","exercise","hobby","human","people","person","ski social","skiing","snow","sports","travel","winter"]},{"name":"filter_list_off","tags":["alt","disabled","edit","filter","list","off","offline","options","refine","sift","slash"]},{"name":"connected_tv","tags":["Android","OS","airplay","chrome","connect","connected","desktop","device","display","hardware","iOS","mac","monitor","screen","screencast","streaming","television","tv","web","window","wireless"]},{"name":"format_indent_increase","tags":["align","alignment","doc","edit","editing","editor","format","increase","indent","indentation","paragraph","sheet","spreadsheet","text","type","writing"]},{"name":"settings_cell","tags":["Android","OS","cell","device","hardware","iOS","mobile","phone","settings","tablet"]},{"name":"remember_me","tags":["Android","OS","avatar","device","hardware","human","iOS","identity","me","mobile","people","person","phone","profile","remember","tablet","user"]},{"name":"kayaking","tags":["athlete","athletic","body","canoe","entertainment","exercise","hobby","human","kayak","kayaking","lake","paddle","paddling","people","person","rafting","river","row","social","sports","summer","travel","water"]},{"name":"switch_access_shortcut_add","tags":["+","access","add","arrow","arrows","direction","navigation","new","north","plus","shortcut","switch","symbol","up"]},{"name":"app_blocking","tags":["Android","OS","app","application","block","blocking","cancel","cell","device","hardware","iOS","mobile","phone","stop","stopped","tablet"]},{"name":"elevator","tags":["body","down","elevator","human","people","person","up"]},{"name":"work_off","tags":["bag","baggage","briefcase","business","case","disabled","enabled","job","off","on","slash","suitcase","work"]},{"name":"sensors_off","tags":["connection","disabled","enabled","network","off","on","scan","sensors","signal","slash","wireless"]},{"name":"stay_primary_portrait","tags":["Android","OS","current","device","hardware","iOS","mobile","phone","portrait","primary","stay","tablet"]},{"name":"cell_tower","tags":["broadcast","casting","cell","network","signal","tower","transmitting","wireless"]},{"name":"moped","tags":["automobile","bike","car","cars","maps","scooter","transportation","vehicle","vespa"]},{"name":"wrong_location","tags":["cancel","close","destination","direction","exit","location","maps","no","pin","place","quit","remove","stop","wrong","x"]},{"name":"groups_2","tags":["body","club","collaboration","crowd","gathering","groups","hair","human","meeting","people","person","social","teams"]},{"name":"public_off","tags":["disabled","earth","enabled","global","globe","map","network","off","on","planet","public","slash","social","space","web","world"]},{"name":"picture_in_picture_alt","tags":["crop","cropped","overlap","photo","picture","position","shape"]},{"name":"chair_alt","tags":["cahir","furniture","home","house","kitchen","lounging","seating","table"]},{"name":"car_repair","tags":["automobile","car","cars","maps","repair","transportation","vehicle"]},{"name":"airplay","tags":["airplay","arrow","connect","control","desktop","device","display","monitor","screen","signal"]},{"name":"nfc","tags":["communication","data","field","mobile","near","nfc","wireless"]},{"name":"line_style","tags":["dash","dotted","line","rule","spacing","style"]},{"name":"transform","tags":["adjust","crop","edit","editing","image","photo","picture","transform"]},{"name":"single_bed","tags":["bed","bedroom","double","furniture","home","hotel","house","king","night","pillows","queen","rest","room","single","sleep","twin"]},{"name":"pattern","tags":["key","login","password","pattern","pin","security","star","unlock"]},{"name":"local_movies","tags":[]},{"name":"repeat_one","tags":["1","arrow","arrows","control","controls","digit","media","music","number","one","repeat","symbol","video"]},{"name":"swap_calls","tags":["arrow","arrows","calls","device","direction","mobile","share","swap"]},{"name":"do_not_disturb_alt","tags":["cancel","close","denied","deny","disturb","do","remove","silence","stop"]},{"name":"smoking_rooms","tags":["allowed","cigarette","places","rooms","smoke","smoking","tobacco","zone"]},{"name":"remove_moderator","tags":["certified","disabled","enabled","moderator","off","on","privacy","private","protect","protection","remove","security","shield","slash","verified"]},{"name":"perm_device_information","tags":["Android","OS","alert","announcement","device","hardware","i","iOS","info","information","mobile","perm","phone","tablet"]},{"name":"wash","tags":["bathroom","clean","fingers","gesture","hand","wash","wc"]},{"name":"mode_standby","tags":["disturb","mode","power","sleep","standby","target"]},{"name":"door_sliding","tags":["auto","automatic","door","doorway","double","entrance","exit","glass","home","house","sliding","two"]},{"name":"skateboarding","tags":["athlete","athletic","body","entertainment","exercise","hobby","human","people","person","skate","skateboarder","skateboarding","social","sports"]},{"name":"difference","tags":["compare","content","copy","cut","diff","difference","doc","document","duplicate","file","multiple","past"]},{"name":"group_remove","tags":["accounts","committee","face","family","friends","group","humans","network","people","persons","profiles","remove","social","team","users"]},{"name":"brightness_high","tags":["auto","brightness","control","high","mobile","monitor","phone","sun"]},{"name":"cabin","tags":["architecture","cabin","camping","cottage","estate","home","house","log","maps","place","real","residence","residential","stay","traveling","wood"]},{"name":"camera_outdoor","tags":["architecture","building","camera","estate","film","filming","home","house","image","motion","nest","outdoor","outside","picture","place","real","residence","residential","shelter","video","videography"]},{"name":"troubleshoot","tags":["analytics","chart","data","diagram","find","glass","graph","infographic","line","look","magnify","magnifying","measure","metrics","search","see","statistics","tracking","troubleshoot"]},{"name":"tablet_android","tags":["OS","android","device","hardware","iOS","ipad","mobile","tablet","web"]},{"name":"house_siding","tags":["architecture","building","construction","estate","exterior","facade","home","house","real","residential","siding"]},{"name":"satellite","tags":["bluetooth","connect","connection","connectivity","data","device","image","internet","landscape","location","maps","mountain","mountains","network","photo","photography","picture","satellite","scan","service","signal","symbol","wireless-- wifi"]},{"name":"motion_photos_on","tags":["animation","circle","disabled","enabled","motion","off","on","photos","play","slash","video"]},{"name":"door_back","tags":["back","closed","door","doorway","entrance","exit","home","house","way"]},{"name":"strikethrough_s","tags":["alphabet","character","cross","doc","edit","editing","editor","font","letter","out","s","sheet","spreadsheet","strikethrough","styles","symbol","text","type","writing"]},{"name":"co2","tags":["carbon","chemical","co2","dioxide","gas"]},{"name":"notifications_paused","tags":["active","alarm","alert","bell","chime","ignore","notifications","notify","paused","quiet","reminder","ring --- pause","sleep","snooze","sound","z","zzz"]},{"name":"currency_yen","tags":["bill","card","cash","coin","commerce","cost","credit","currency","dollars","finance","money","online","pay","payment","price","shopping","symbol","yen"]},{"name":"call_to_action","tags":["action","alert","bar","call","components","cta","design","info","information","interface","layout","message","notification","screen","site","to","ui","ux","web","website","window"]},{"name":"photo_camera_front","tags":["account","camera","face","front","human","image","people","person","photo","photography","picture","portrait","profile","user"]},{"name":"directions_boat_filled","tags":["automobile","boat","car","cars","direction","directions","ferry","filled","maps","public","transportation","vehicle"]},{"name":"subtitles_off","tags":["accessibility","accessible","caption","cc","closed","disabled","enabled","language","off","on","slash","subtitle","subtitles","translate","video"]},{"name":"rotate_90_degrees_ccw","tags":["90","arrow","arrows","ccw","degrees","direction","edit","editing","image","photo","rotate","turn"]},{"name":"vertical_align_center","tags":["align","alignment","arrow","center","doc","down","edit","editing","editor","sheet","spreadsheet","text","type","up","vertical","writing"]},{"name":"living","tags":["chair","comfort","couch","decoration","furniture","home","house","living","lounging","loveseat","room","seat","seating","sofa"]},{"name":"battery_saver","tags":["+","add","battery","charge","charging","new","plus","power","saver","symbol"]},{"name":"hot_tub","tags":["bath","bathing","bathroom","bathtub","hot","hotel","human","jacuzzi","person","shower","spa","steam","travel","tub","water"]},{"name":"play_lesson","tags":["audio","book","bookmark","digital","ebook","lesson","multimedia","play","play lesson","read","reading","ribbon"]},{"name":"update_disabled","tags":["arrow","back","backwards","clock","date","disabled","enabled","forward","history","load","off","on","refresh","reverse","rotate","schedule","slash","time","update"]},{"name":"psychology_alt","tags":["?","assistance","behavior","body","brain","cognitive","function","gear","head","help","human","info","information","intellectual","mental","mind","people","person","preferences","psychiatric","psychology","punctuation","question mark","science","settings","social","support","symbol","therapy","thinking","thoughts"]},{"name":"cast_connected","tags":["Android","OS","airplay","cast","chrome","connect","connected","desktop","device","display","hardware","iOS","mac","monitor","screen","screencast","streaming","television","tv","web","window","wireless"]},{"name":"format_color_reset","tags":["clear","color","disabled","doc","droplet","edit","editing","editor","enabled","fill","format","off","on","paint","reset","sheet","slash","spreadsheet","style","text","type","water","writing"]},{"name":"snooze","tags":["alarm","bell","clock","duration","notification","snooze","time","timer","watch","z"]},{"name":"person_remove_alt_1","tags":[]},{"name":"align_horizontal_left","tags":["align","alignment","format","horizontal","layout","left","lines","paragraph","rule","rules","style","text"]},{"name":"boy","tags":["body","boy","gender","human","male","man","people","person","social","symbol"]},{"name":"battery_5_bar","tags":["5","bar","battery","cell","charge","mobile","power"]},{"name":"mic_external_on","tags":["audio","disabled","enabled","external","mic","microphone","off","on","slash","sound","voice"]},{"name":"voicemail","tags":["call","device","message","missed","mobile","phone","recording","voice","voicemail"]},{"name":"join_full","tags":["circle","combine","command","full","join","left","outer","overlap","right","sql"]},{"name":"looks_5","tags":["5","digit","looks","numbers","square","symbol"]},{"name":"countertops","tags":["counter","countertops","home","house","kitchen","sink","table","tops"]},{"name":"energy_savings_leaf","tags":["eco","energy","leaf","leaves","nest","savings","usage"]},{"name":"safety_divider","tags":["apart","distance","divider","safety","separate","social","space"]},{"name":"move_up","tags":["arrow","direction","jump","move","navigation","transfer","up"]},{"name":"storm","tags":["forecast","hurricane","storm","temperature","twister","weather","wind"]},{"name":"sync_disabled","tags":["360","around","arrow","arrows","direction","disabled","enabled","inprogress","load","loading refresh","off","on","renew","rotate","slash","sync","turn"]},{"name":"javascript","tags":["alphabet","brackets","character","code","css","develop","developer","engineer","engineering","font","html","javascript","letter","platform","symbol","text","type"]},{"name":"tram","tags":["automobile","car","cars","direction","maps","public","rail","subway","train","tram","transportation","vehicle"]},{"name":"app_shortcut","tags":["app","bookmarked","favorite","highlight","important","marked","mobile","save","saved","shortcut","software","special","star"]},{"name":"data_saver_off","tags":["analytics","bar","bars","chart","data","diagram","donut","graph","infographic","measure","metrics","off","on","ring","saver","statistics","tracking"]},{"name":"laptop_windows","tags":["Android","OS","chrome","device","display","hardware","iOS","laptop","mac","monitor","screen","web","window","windows"]},{"name":"doorbell","tags":["alarm","bell","door","doorbell","home","house","ringing"]},{"name":"hd","tags":["alphabet","character","definition","display","font","hd","high","letter","movie","movies","resolution","screen","symbol","text","tv","type"]},{"name":"file_download_off","tags":["arrow","disabled","down","download","drive","enabled","export","file","install","off","on","save","slash","upload"]},{"name":"apps_outage","tags":["all","applications","apps","circles","collection","components","dots","grid","interface","outage","squares","ui","ux"]},{"name":"taxi_alert","tags":["!","alert","attention","automobile","cab","car","cars","caution","danger","direction","error","exclamation","important","lyft","maps","mark","notification","public","symbol","taxi","transportation","uber","vehicle","warning","yellow"]},{"name":"breakfast_dining","tags":["bakery","bread","breakfast","butter","dining","food","toast"]},{"name":"brightness_medium","tags":["auto","brightness","control","medium","mobile","monitor","phone","sun"]},{"name":"gradient","tags":["color","edit","editing","effect","filter","gradient","image","images","photography","picture","pictures"]},{"name":"swipe_left","tags":["arrow","arrows","finger","hand","hit","left","navigation","reject","strike","swing","swipe","take"]},{"name":"soup_kitchen","tags":["breakfast","brunch","dining","food","kitchen","lunch","meal","soup"]},{"name":"voice_over_off","tags":["account","disabled","enabled","face","human","off","on","over","people","person","profile","recording","slash","speak","speaking","speech","transcript","user","voice"]},{"name":"water_damage","tags":["architecture","building","damage","drop","droplet","estate","house","leak","plumbing","real","residence","residential","shelter","water"]},{"name":"abc","tags":["alphabet","character","font","letter","symbol","text","type"]},{"name":"data_saver_on","tags":["+","add","analytics","chart","data","diagram","graph","infographic","measure","metrics","new","on","plus","ring","saver","statistics","symbol","tracking"]},{"name":"signal_wifi_0_bar","tags":["0","bar","cell","cellular","data","internet","mobile","network","phone","signal","wifi","wireless"]},{"name":"brightness_low","tags":["auto","brightness","control","low","mobile","monitor","phone","sun"]},{"name":"device_unknown","tags":["?","Android","OS","assistance","cell","device","hardware","help","iOS","info","information","mobile","phone","punctuation","question mark","support","symbol","tablet","unknown"]},{"name":"fire_extinguisher","tags":["emergency","extinguisher","fire","water"]},{"name":"fitbit","tags":["athlete","athletic","exercise","fitbit","fitness","hobby","logo"]},{"name":"bedroom_child","tags":["bed","bedroom","child","children","furniture","home","hotel","house","kid","night","pillows","rest","room","size","sleep","twin","young"]},{"name":"closed_caption_off","tags":["accessible","alphabet","caption","cc","character","closed","decoder","font","language","letter","media","movies","off","outline","subtitle","subtitles","symbol","text","tv","type"]},{"name":"bluetooth_searching","tags":["bluetooth","connection","device","paring","search","searching","symbol"]},{"name":"content_paste_off","tags":["clipboard","content","disabled","doc","document","enabled","file","off","on","paste","slash"]},{"name":"hexagon","tags":["hexagon","shape","six sides"]},{"name":"tap_and_play","tags":["Android","OS wifi","cell","connection","device","hardware","iOS","internet","mobile","network","phone","play","signal","tablet","tap","to","wireless"]},{"name":"domain_add","tags":["+","add","apartment","architecture","building","business","domain","estate","home","new","place","plus","real","residence","residential","shelter","symbol","web","www"]},{"name":"signpost","tags":["arrow","direction","left","maps","right","signal","signs","street","traffic"]},{"name":"screenshot","tags":["Android","OS","cell","crop","device","hardware","iOS","mobile","phone","screen","screenshot","tablet"]},{"name":"network_cell","tags":["cell","cellular","data","internet","mobile","network","phone","speed","wifi","wireless"]},{"name":"repeat_on","tags":["arrow","arrows","control","controls","media","music","on","repeat","video"]},{"name":"charging_station","tags":["Android","OS","battery","bolt","cell","charging","device","electric","hardware","iOS","lightning","mobile","phone","station","tablet","thunderbolt"]},{"name":"grid_4x4","tags":["4","by","grid","layout","lines","space"]},{"name":"assistant_photo","tags":["assistant","flag","photo","recommendation","smart","star","suggestion"]},{"name":"carpenter","tags":["building","carpenter","construction","cutting","handyman","repair","saw","tool"]},{"name":"private_connectivity","tags":["connectivity","lock","locked","password","privacy","private","protection","safety","secure","security"]},{"name":"mobiledata_off","tags":["arrow","data","disabled","down","enabled","internet","mobile","network","off","on","slash","speed","up","wifi","wireless"]},{"name":"atm","tags":["alphabet","atm","automated","bill","card","cart","cash","character","coin","commerce","credit","currency","dollars","font","letter","machine","money","online","pay","payment","shopping","symbol","teller","text","type"]},{"name":"rv_hookup","tags":["arrow","attach","automobile","automotive","back","car","cars","connect","direction","hookup","left","maps","public","right","rv","trailer","transportation","travel","truck","van","vehicle"]},{"name":"replay_30","tags":["30","arrow","arrows","control","controls","digit","music","number","refresh","renew","repeat","replay","symbol","thirty","video"]},{"name":"offline_share","tags":["Android","OS","arrow","cell","connect","device","direction","hardware","iOS","link","mobile","multiple","offline","phone","right","share","tablet"]},{"name":"settings_input_svideo","tags":["cable","connection","connectivity","definition","input","plug","plugin","points","settings","standard","svideo","video"]},{"name":"soap","tags":["bathroom","clean","fingers","gesture","hand","soap","wash","wc"]},{"name":"baby_changing_station","tags":["babies","baby","bathroom","body","changing","child","children","father","human","infant","kids","mother","newborn","people","person","station","toddler","wc","young"]},{"name":"sports_cricket","tags":["athlete","athletic","ball","bat","cricket","entertainment","exercise","game","hobby","social","sports"]},{"name":"ad_units","tags":["Android","OS","ad","banner","cell","device","hardware","iOS","mobile","notification","notifications","phone","tablet","top","units"]},{"name":"wb_twilight","tags":["balance","light","lighting","noon","sun","sunset","twilight","wb","white"]},{"name":"no_encryption","tags":["disabled","enabled","encryption","lock","no","off","on","password","safety","security","slash"]},{"name":"table_bar","tags":["bar","cafe","round","table"]},{"name":"diversity_2","tags":["committee","diverse","diversity","family","friends","group","groups","heart","humans","network","people","persons","social","team"]},{"name":"subway","tags":["automobile","bike","car","cars","maps","rail","scooter","subway","train","transportation","travel","tunnel","underground","vehicle","vespa"]},{"name":"browser_updated","tags":["Android","OS","arrow","browser","chrome","desktop","device","display","download","hardware","iOS","mac","monitor","screen","updated","web","window"]},{"name":"currency_pound","tags":["bill","card","cash","coin","commerce","cost","credit","currency","dollars","finance","money","online","pay","payment","pound","price","shopping","symbol"]},{"name":"transit_enterexit","tags":["arrow","direction","enterexit","maps","navigation","route","transit","transportation"]},{"name":"contrast","tags":["black","contrast","edit","editing","effect","filter","grayscale","image","images","photography","picture","pictures","settings","white"]},{"name":"lightbulb_circle","tags":["alert","announcement","idea","info","information","light","lightbulb"]},{"name":"rectangle","tags":["four sides","parallelograms","polygons","quadrilaterals","recangle","shape"]},{"name":"call_merge","tags":["arrow","call","device","merge","mobile"]},{"name":"hide_image","tags":["disabled","enabled","hide","image","landscape","mountain","mountains","off","on","photo","photography","picture","slash"]},{"name":"shield_moon","tags":["certified","do not disturb","moon","night","privacy","private","protect","protection","security","shield","verified"]},{"name":"group_off","tags":["body","club","collaboration","crowd","gathering","group","human","meeting","off","people","person","social","teams"]},{"name":"music_off","tags":["audio","audiotrack","disabled","enabled","key","music","note","off","on","slash","sound","track"]},{"name":"bluetooth_disabled","tags":["bluetooth","cast","connect","connection","device","disabled","enabled","off","offline","on","paring","slash","streaming","symbol","wireless"]},{"name":"flip_to_back","tags":["arrange","arrangement","back","flip","format","front","layout","move","order","sort","to"]},{"name":"sd_card","tags":["camera","card","digital","memory","photos","sd","secure","storage"]},{"name":"exposure_plus_1","tags":["1","add","brightness","contrast","digit","edit","editing","effect","exposure","image","number","photo","photography","plus","settings","symbol"]},{"name":"view_array","tags":["array","design","format","grid","layout","view","website"]},{"name":"sports_mma","tags":["arts","athlete","athletic","boxing","combat","entertainment","exercise","fighting","game","glove","hobby","martial","mixed","mma","social","sports"]},{"name":"straight","tags":["arrow","arrows","direction","directions","maps","navigation","path","route","sign","straight","traffic","up"]},{"name":"thermostat_auto","tags":["A","auto","celsius","fahrenheit","meter","temp","temperature","thermometer","thermostat"]},{"name":"mobile_screen_share","tags":["Android","OS","cast","cell","device","hardware","iOS","mirror","mobile","monitor","phone","screen","screencast","share","stream","streaming","tablet","tv","wireless"]},{"name":"phone_missed","tags":["arrow","call","cell","contact","device","hardware","missed","mobile","phone","telephone"]},{"name":"brunch_dining","tags":["breakfast","brunch","champagne","dining","drink","food","lunch","meal"]},{"name":"featured_video","tags":["advertised","advertisement","featured","highlighted","recommended","video","watch"]},{"name":"merge","tags":["arrow","arrows","direction","directions","maps","merge","navigation","path","route","sign","traffic"]},{"name":"open_in_new_off","tags":["arrow","box","disabled","enabled","export","in","new","off","on","open","slash","window"]},{"name":"hdr_auto","tags":["A","alphabet","auto","camera","character","circle","dynamic","font","hdr","high","letter","photo","range","symbol","text","type"]},{"name":"join_inner","tags":["circle","command","inner","join","matching","overlap","sql","values"]},{"name":"solar_power","tags":["eco","energy","heat","nest","power","solar","sun","sunny"]},{"name":"crop_16_9","tags":["16","9","adjust","adjustments","area","by","crop","edit","editing","frame","image","images","photo","photos","rectangle","settings","size","square"]},{"name":"swipe_right","tags":["accept","arrows","direction","finger","hands","hit","navigation","right","strike","swing","swpie","take"]},{"name":"phonelink_erase","tags":["Android","OS","cancel","cell","close","connection","device","erase","exit","hardware","iOS","mobile","no","phone","phonelink","remove","stop","tablet","x"]},{"name":"smoke_free","tags":["cigarette","disabled","enabled","free","never","no","off","on","places","prohibited","slash","smoke","smoking","tobacco","warning","zone"]},{"name":"install_desktop","tags":["Android","OS","chrome","desktop","device","display","fix","hardware","iOS","install","mac","monitor","place","pwa","screen","web","window"]},{"name":"shutter_speed","tags":["aperture","camera","duration","image","lens","photo","photography","photos","picture","setting","shutter","speed","stop","time","timer","watch"]},{"name":"keyboard_hide","tags":["arrow","computer","device","down","hardware","hide","input","keyboard","keypad","text"]},{"name":"exposure","tags":["add","brightness","contrast","edit","editing","effect","exposure","image","minus","photo","photography","picture","plus","settings","subtract"]},{"name":"nordic_walking","tags":["athlete","athletic","body","entertainment","exercise","hiking","hobby","human","nordic","people","person","social","sports","travel","walker","walking"]},{"name":"umbrella","tags":["beach","protection","rain","sun","sunny","umbrella"]},{"name":"move_down","tags":["arrow","direction","down","jump","move","navigation","transfer"]},{"name":"filter_2","tags":["2","digit","edit","editing","effect","filter","image","images","multiple","number","photography","picture","pictures","settings","stack","symbol"]},{"name":"photo_album","tags":["album","archive","bookmark","image","label","library","mountain","mountains","photo","photography","picture","ribbon","save","tag"]},{"name":"security_update_good","tags":["Android","OS","checkmark","device","good","hardware","iOS","mobile","ok","phone","security","tablet","tick","update"]},{"name":"ssid_chart","tags":["chart","graph","lines","network","ssid","wifi"]},{"name":"score","tags":["2k","alphabet","analytics","bar","bars","character","chart","data","diagram","digit","font","graph","infographic","letter","measure","metrics","number","score","statistics","symbol","text","tracking","type"]},{"name":"swipe_up","tags":["arrows","direction","disable","enable","finger","hands","hit","navigation","strike","swing","swpie","take","up"]},{"name":"battery_4_bar","tags":["4","bar","battery","cell","charge","mobile","power"]},{"name":"all_out","tags":["all","circle","out","shape"]},{"name":"battery_unknown","tags":["?","assistance","battery","cell","charge","help","info","information","mobile","power","punctuation","question mark","support","symbol","unknown"]},{"name":"sports_golf","tags":["athlete","athletic","ball","club","entertainment","exercise","game","golf","golfer","golfing","hobby","social","sports"]},{"name":"sports_martial_arts","tags":["arts","athlete","athletic","entertainment","exercise","hobby","human","karate","martial","people","person","social","sports"]},{"name":"filter_tilt_shift","tags":["blur","center","edit","editing","effect","filter","focus","image","images","photography","picture","pictures","shift","tilt"]},{"name":"electric_bike","tags":["bike","electric","electricity","maps","scooter","transportation","travel","vespa"]},{"name":"border_all","tags":["all","border","doc","edit","editing","editor","sheet","spreadsheet","stroke","text","type","writing"]},{"name":"auto_mode","tags":["ai","around","arrow","arrows","artificial","auto","automatic","automation","custom","direction","genai","inprogress","intelligence","load","loading refresh","magic","mode","navigation","nest","renew","rotate","smart","spark","sparkle","star","turn"]},{"name":"hvac","tags":["air","conditioning","heating","hvac","ventilation"]},{"name":"scanner","tags":["copy","device","hardware","machine","scan","scanner"]},{"name":"shuffle_on","tags":["arrow","arrows","control","controls","music","on","random","shuffle","video"]},{"name":"wifi_calling_3","tags":["3","calling","cell","cellular","data","internet","mobile","network","phone","speed","wifi","wireless"]},{"name":"signal_wifi_off","tags":["cell","cellular","data","disabled","enabled","internet","mobile","network","off","on","phone","signal","slash","speed","wifi","wireless"]},{"name":"girl","tags":["body","female","gender","girl","human","lady","people","person","social","symbol","woman","women"]},{"name":"shop_2","tags":["2","add","arrow","buy","cart","google","play","purchase","shop","shopping"]},{"name":"hdr_strong","tags":["circles","dots","dynamic","enhance","hdr","high","range","strong"]},{"name":"directions_transit","tags":["automobile","car","cars","direction","directions","maps","public","rail","subway","train","transit","transportation","vehicle"]},{"name":"label_off","tags":["disabled","enabled","favorite","indent","label","library","mail","off","on","remember","save","slash","stamp","sticker","tag","wing"]},{"name":"tablet","tags":["Android","OS","device","hardware","iOS","ipad","mobile","tablet","web"]},{"name":"5g","tags":["5g","alphabet","cellular","character","data","digit","font","letter","mobile","network","number","phone","signal","speed","symbol","text","type","wifi"]},{"name":"vrpano","tags":["angle","image","landscape","mountain","mountains","panorama","photo","photography","picture","view","vrpano","wide"]},{"name":"forward_30","tags":["30","arrow","control","controls","digit","fast","forward","music","number","seconds","symbol","video"]},{"name":"battery_0_bar","tags":["0","bar","battery","cell","charge","mobile","power"]},{"name":"airline_seat_recline_extra","tags":["airline","body","extra","feet","human","leg","legroom","people","person","seat","sitting","space","travel"]},{"name":"looks","tags":["circle","half","looks","rainbow"]},{"name":"linked_camera","tags":["camera","connect","connection","lens","linked","network","photo","photography","picture","signal","signals","sync","wireless"]},{"name":"paragliding","tags":["athlete","athletic","body","entertainment","exercise","fly","gliding","hobby","human","parachute","paragliding","people","person","sky","skydiving","social","sports","travel"]},{"name":"electric_scooter","tags":["bike","electric","maps","scooter","transportation","vehicle","vespa"]},{"name":"settings_system_daydream","tags":["backup","cloud","daydream","drive","settings","storage","system"]},{"name":"format_indent_decrease","tags":["align","alignment","decrease","doc","edit","editing","editor","format","indent","indentation","paragraph","sheet","spreadsheet","text","type","writing"]},{"name":"tapas","tags":["appetizer","brunch","dinner","food","lunch","restaurant","snack","tapas"]},{"name":"brightness_3","tags":["3","brightness","circle","control","crescent","level","moon","screen"]},{"name":"tab_unselected","tags":["browser","computer","document","documents","folder","internet","tab","tabs","unselected","web","website","window","windows"]},{"name":"density_small","tags":["density","horizontal","lines","rule","rules","small"]},{"name":"blur_circular","tags":["blur","circle","circular","dots","edit","editing","effect","enhance","filter"]},{"name":"rice_bowl","tags":["bowl","dinner","food","lunch","meal","restaurant","rice"]},{"name":"rounded_corner","tags":["adjust","corner","edit","rounded","shape","square","transform"]},{"name":"person_add_disabled","tags":["+","account","add","disabled","enabled","face","human","new","off","offline","on","people","person","plus","profile","slash","symbol","user"]},{"name":"music_video","tags":["band","music","recording","screen","tv","video","watch"]},{"name":"looks_6","tags":["6","digit","looks","numbers","square","symbol"]},{"name":"do_not_touch","tags":["disabled","do","enabled","fingers","gesture","hand","not","off","on","slash","touch"]},{"name":"playlist_add_circle","tags":["add","album","artist","audio","cd","check","circle","collection","list","mark","music","playlist","record","sound","track"]},{"name":"domain_disabled","tags":["apartment","architecture","building","business","company","disabled","domain","enabled","estate","home","internet","maps","off","office","offline","on","place","real","residence","residential","slash","web","website"]},{"name":"flash_auto","tags":["a","auto","bolt","electric","fast","flash","lightning","thunderbolt"]},{"name":"6_ft_apart","tags":["6","apart","body","covid","distance","feet","ft","human","people","person","social"]},{"name":"signal_wifi_bad","tags":["bad","bar","cancel","cell","cellular","close","data","exit","internet","mobile","network","no","phone","quit","remove","signal","stop","wifi","wireless","x"]},{"name":"crisis_alert","tags":["!","alert","attention","bullseye","caution","crisis","danger","error","exclamation","important","mark","notification","symbol","target","warning"]},{"name":"queue_play_next","tags":["+","add","arrow","desktop","device","display","hardware","monitor","new","next","play","plus","queue","screen","steam","symbol","tv"]},{"name":"format_clear","tags":["T","alphabet","character","clear","disabled","doc","edit","editing","editor","enabled","font","format","letter","off","on","sheet","slash","spreadsheet","style","symbol","text","type","writing"]},{"name":"bus_alert","tags":["!","alert","attention","automobile","bus","car","cars","caution","danger","error","exclamation","important","maps","mark","notification","symbol","transportation","vehicle","warning"]},{"name":"party_mode","tags":["camera","lens","mode","party","photo","photography","picture"]},{"name":"snowboarding","tags":["athlete","athletic","body","entertainment","exercise","hobby","human","people","person","snow","snowboarding","social","sports","travel","winter"]},{"name":"text_rotate_vertical","tags":["A","alphabet","arrow","character","down","field","font","letter","move","rotate","symbol","text","type","vertical"]},{"name":"motion_photos_auto","tags":["A","alphabet","animation","auto","automatic","character","circle","font","gif","letter","live","motion","photos","symbol","text","type","video"]},{"name":"crop_portrait","tags":["adjust","adjustments","area","crop","edit","editing","frame","image","images","photo","photos","portrait","rectangle","settings","size","square"]},{"name":"thunderstorm","tags":["bolt","climate","cloud","cloudy","lightning","rain","rainfall","rainstorm","storm","thunder","thunderstorm","weather"]},{"name":"battery_6_bar","tags":["6","bar","battery","cell","charge","mobile","power"]},{"name":"space_bar","tags":["bar","keyboard","line","space"]},{"name":"replay_5","tags":["5","arrow","arrows","control","controls","digit","five","music","number","refresh","renew","repeat","replay","symbol","video"]},{"name":"local_car_wash","tags":["automobile","car","cars","local","maps","transportation","travel","vehicle","wash"]},{"name":"folder_delete","tags":["bin","can","data","delete","doc","document","drive","file","folder","folders","garbage","remove","sheet","slide","storage","trash"]},{"name":"data_thresholding","tags":["data","hidden","privacy","thresholding","thresold"]},{"name":"connecting_airports","tags":["airplane","airplanes","airport","airports","connecting","flight","plane","transportation","travel","trip"]},{"name":"access_alarms","tags":[]},{"name":"tty","tags":["call","cell","contact","deaf","device","hardware","impaired","mobile","phone","speech","talk","telephone","text","tty"]},{"name":"audio_file","tags":["audio","doc","document","key","music","note","sound","track"]},{"name":"egg","tags":["breakfast","brunch","egg","food"]},{"name":"balcony","tags":["architecture","balcony","doors","estate","home","house","maps","out","outside","place","real","residence","residential","stay","terrace","window"]},{"name":"kitesurfing","tags":["athlete","athletic","beach","body","entertainment","exercise","hobby","human","kitesurfing","people","person","social","sports","surf","travel","water"]},{"name":"call_missed_outgoing","tags":["arrow","call","device","missed","mobile","outgoing"]},{"name":"local_hotel","tags":["body","hotel","human","local","people","person","sleep","stay","travel","trip"]},{"name":"text_increase","tags":["+","add","alphabet","character","font","increase","letter","new","plus","resize","symbol","text","type"]},{"name":"speaker_phone","tags":["Android","OS","cell","device","hardware","iOS","mobile","phone","sound","speaker","tablet","volume"]},{"name":"no_food","tags":["disabled","drink","enabled","fastfood","food","hamburger","meal","no","off","on","slash"]},{"name":"brightness_2","tags":["2","brightness","circle","control","crescent","level","moon","screen"]},{"name":"mode_of_travel","tags":["arrow","destination","direction","location","maps","mode","of","pin","place","stop","transportation","travel","trip"]},{"name":"format_line_spacing","tags":["align","alignment","doc","edit","editing","editor","format","line","sheet","spacing","spreadsheet","text","type","writing"]},{"name":"iso","tags":["add","edit","editing","effect","image","iso","minus","photography","picture","plus","sensor","shutter","speed","subtract"]},{"name":"explore_off","tags":["compass","destination","direction","disabled","east","enabled","explore","location","maps","needle","north","off","on","slash","south","travel","west"]},{"name":"drive_file_move_rtl","tags":["arrow","arrows","data","direction","doc","document","drive","file","folder","folders","left","move","rtl","sheet","side","slide","storage"]},{"name":"cell_wifi","tags":["cell","connection","data","internet","mobile","network","phone","service","signal","wifi","wireless"]},{"name":"tonality","tags":["circle","edit","editing","filter","image","photography","picture","tonality"]},{"name":"spoke","tags":["connection","network","radius","spoke"]},{"name":"photo_filter","tags":["ai","artificial","automatic","automation","custom","filter","filters","genai","image","intelligence","magic","photo","photography","picture","smart","spark","sparkle","star"]},{"name":"desktop_access_disabled","tags":["Android","OS","access","chrome","desktop","device","disabled","display","enabled","hardware","iOS","mac","monitor","off","offline","on","screen","slash","web","window"]},{"name":"sports_gymnastics","tags":["athlete","athletic","entertainment","exercise","gymnastics","hobby","social","sports"]},{"name":"houseboat","tags":["architecture","beach","boat","estate","floating","home","house","houseboat","maps","place","real","residence","residential","sea","stay","traveling","vacation"]},{"name":"fence","tags":["backyard","barrier","boundaries","boundary","fence","home","house","protection","yard"]},{"name":"commit","tags":["accomplish","bind","circle","commit","dedicate","execute","line","perform","pledge"]},{"name":"photo_size_select_small","tags":["adjust","album","edit","editing","image","large","library","mountain","mountains","photo","photography","picture","select","size","small"]},{"name":"signal_wifi_connected_no_internet_4","tags":["4","cell","cellular","connected","data","internet","mobile","network","no","offline","phone","signal","wifi","wireless","x"]},{"name":"horizontal_distribute","tags":["alignment","distribute","format","horizontal","layout","lines","paragraph","rule","rules","style","text"]},{"name":"report_off","tags":["!","alert","attention","caution","danger","disabled","enabled","error","exclamation","important","mark","notification","octagon","off","offline","on","report","slash","symbol","warning"]},{"name":"polyline","tags":["compose","create","design","draw","line","polyline","vector"]},{"name":"art_track","tags":["album","art","artist","audio","image","music","photo","photography","picture","sound","track","tracks"]},{"name":"crop_7_5","tags":["5","7","adjust","adjustments","area","by","crop","editing","frame","image","images","photo","photos","rectangle","settings","size","square"]},{"name":"filter_hdr","tags":["camera","edit","editing","effect","filter","hdr","image","mountain","mountains","photo","photography","picture"]},{"name":"text_rotation_none","tags":["A","alphabet","arrow","character","field","font","letter","move","none","rotate","symbol","text","type"]},{"name":"battery_3_bar","tags":["3","bar","battery","cell","charge","mobile","power"]},{"name":"align_vertical_bottom","tags":["align","alignment","bottom","format","layout","lines","paragraph","rule","rules","style","text","vertical"]},{"name":"stop_screen_share","tags":["Android","OS","arrow","cast","chrome","device","disabled","display","enabled","hardware","iOS","laptop","mac","mirror","monitor","off","offline","on","screen","share","slash","steam","stop","streaming","web","window"]},{"name":"imagesearch_roller","tags":["art","image","imagesearch","paint","roller","search"]},{"name":"bento","tags":["bento","box","dinner","food","lunch","meal","restaurant","takeout"]},{"name":"rotate_90_degrees_cw","tags":["90","arrow","arrows","ccw","degrees","direction","edit","editing","image","photo","rotate","turn"]},{"name":"install_mobile","tags":["Android","OS","cell","device","hardware","iOS","install","mobile","phone","pwa","tablet"]},{"name":"hearing_disabled","tags":["accessibility","accessible","aid","disabled","ear","enabled","handicap","hearing","help","impaired","listen","off","on","slash","sound","volume"]},{"name":"video_file","tags":["camera","doc","document","film","filming","hardware","image","motion","picture","video","videography"]},{"name":"mms","tags":["bubble","chat","comment","communicate","feedback","image","landscape","message","mms","mountain","mountains","multimedia","photo","photography","picture","speech"]},{"name":"crop_rotate","tags":["adjust","adjustments","area","arrow","arrows","crop","edit","editing","frame","image","images","photo","photos","rotate","settings","size","turn"]},{"name":"wheelchair_pickup","tags":["accessibility","accessible","body","handicap","help","human","person","pickup","wheelchair"]},{"name":"aod","tags":["Android","OS","always","aod","device","display","hardware","homescreen","iOS","mobile","on","phone","tablet"]},{"name":"castle","tags":["castle","fort","fortress","mansion","palace"]},{"name":"interpreter_mode","tags":["interpreter","language","microphone","mode","person","speaking","symbol"]},{"name":"access_alarm","tags":[]},{"name":"forward_5","tags":["10","5","arrow","control","controls","digit","fast","forward","music","number","seconds","symbol","video"]},{"name":"add_to_home_screen","tags":["Android","OS","add to","arrow","cell","device","hardware","home","iOS","mobile","phone","screen","tablet","up"]},{"name":"not_accessible","tags":["accessibility","accessible","body","handicap","help","human","not","person","wheelchair"]},{"name":"signal_cellular_0_bar","tags":["0","bar","cell","cellular","data","internet","mobile","network","phone","signal","speed","wifi","wireless"]},{"name":"stadium","tags":["activity","amphitheater","arena","coliseum","event","local","stadium","star","things","ticket"]},{"name":"photo_size_select_large","tags":["adjust","album","edit","editing","image","large","library","mountain","mountains","photo","photography","picture","select","size"]},{"name":"groups_3","tags":["abstract","body","club","collaboration","crowd","gathering","groups","human","meeting","people","person","social","teams"]},{"name":"snowshoeing","tags":["body","human","people","person","snow","snowshoe","snowshoeing","sports","travel","walking","winter"]},{"name":"view_kanban","tags":["grid","kanban","layout","pattern","squares","view"]},{"name":"candlestick_chart","tags":["analytics","candlestick","chart","data","diagram","finance","graph","infographic","measure","metrics","statistics","tracking"]},{"name":"filter_3","tags":["3","digit","edit","editing","effect","filter","image","images","multiple","number","photography","picture","pictures","settings","stack","symbol"]},{"name":"arrow_outward","tags":["app","application","arrow","arrows","components","direction","forward","interface","navigation","right","screen","site","ui","ux","web","website"]},{"name":"align_horizontal_center","tags":["align","alignment","center","format","horizontal","layout","lines","paragraph","rule","rules","style","text"]},{"name":"flashlight_off","tags":["disabled","enabled","flash","flashlight","light","off","on","slash"]},{"name":"security_update","tags":["Android","OS","arrow","device","down","download","hardware","iOS","mobile","phone","security","tablet","update"]},{"name":"iron","tags":["appliance","clothes","electric","iron","ironing","machine","object"]},{"name":"print_disabled","tags":["disabled","enabled","off","on","paper","print","printer","slash"]},{"name":"pin_invoke","tags":["action","arrow","dot","invoke","pin"]},{"name":"speaker_group","tags":["box","electronic","group","loud","multiple","music","sound","speaker","stereo","system","video"]},{"name":"exposure_zero","tags":["0","brightness","contrast","digit","edit","editing","effect","exposure","image","number","photo","photography","settings","symbol","zero"]},{"name":"bungalow","tags":["architecture","bungalow","cottage","estate","home","house","maps","place","real","residence","residential","stay","traveling"]},{"name":"streetview","tags":["maps","street","streetview","view"]},{"name":"swipe_down","tags":["arrows","direction","disable","down","enable","finger","hands","hit","navigation","strike","swing","swpie","take"]},{"name":"hdr_weak","tags":["circles","dots","dynamic","enhance","hdr","high","range","weak"]},{"name":"css","tags":["alphabet","brackets","character","code","css","develop","developer","engineer","engineering","font","html","letter","platform","symbol","text","type"]},{"name":"call_missed","tags":["arrow","call","device","missed","mobile"]},{"name":"gps_off","tags":["destination","direction","disabled","enabled","gps","location","maps","not fixed","off","offline","on","place","pointer","slash","tracking"]},{"name":"sports_hockey","tags":["athlete","athletic","entertainment","exercise","game","hobby","hockey","social","sports","sticks"]},{"name":"ice_skating","tags":["athlete","athletic","entertainment","exercise","hobby","ice","shoe","skates","skating","social","sports","travel"]},{"name":"keyboard_capslock","tags":["arrow","capslock","keyboard","up"]},{"name":"earbuds","tags":["accessory","audio","earbuds","earphone","headphone","listen","music","sound"]},{"name":"camera_front","tags":["body","camera","front","human","lens","mobile","person","phone","photography","portrait","selfie"]},{"name":"vertical_distribute","tags":["alignment","distribute","format","layout","lines","paragraph","rule","rules","style","text","vertical"]},{"name":"currency_ruble","tags":["bill","card","cash","coin","commerce","cost","credit","currency","dollars","finance","money","online","pay","payment","price","ruble","shopping","symbol"]},{"name":"signal_wifi_statusbar_null","tags":["cell","cellular","data","internet","mobile","network","null","phone","signal","speed","statusbar","wifi","wireless"]},{"name":"align_horizontal_right","tags":["align","alignment","format","horizontal","layout","lines","paragraph","right","rule","rules","style","text"]},{"name":"crop_5_4","tags":["4","5","adjust","adjustments","area","by","crop","edit","editing settings","frame","image","images","photo","photos","rectangle","size","square"]},{"name":"format_strikethrough","tags":["alphabet","character","doc","edit","editing","editor","font","format","letter","sheet","spreadsheet","strikethrough","style","symbol","text","type","writing"]},{"name":"face_6","tags":["account","emoji","eyes","face","human","lock","log","login","logout","people","person","profile","recognition","security","social","thumbnail","unlock","user"]},{"name":"join_left","tags":["circle","command","join","left","matching","overlap","sql","values"]},{"name":"explicit","tags":["adult","alphabet","character","content","e","explicit","font","language","letter","media","movies","music","symbol","text","type"]},{"name":"extension_off","tags":["disabled","enabled","extended","extension","jigsaw","off","on","piece","puzzle","shape","slash"]},{"name":"perm_camera_mic","tags":["camera","image","microphone","min","perm","photo","photography","picture","speaker"]},{"name":"sports_rugby","tags":["athlete","athletic","ball","entertainment","exercise","game","hobby","rugby","social","sports"]},{"name":"pause_presentation","tags":["app","application desktop","device","pause","present","presentation","screen","share","site","slides","web","website","window","www"]},{"name":"south_america","tags":["continent","landscape","place","region","south america"]},{"name":"sd_storage","tags":["camera","card","data","digital","memory","sd","secure","storage"]},{"name":"superscript","tags":["2","doc","edit","editing","editor","gmail","novitas","sheet","spreadsheet","style","superscript","symbol","text","writing","x"]},{"name":"4g_mobiledata","tags":["4g","alphabet","cellular","character","digit","font","letter","mobile","mobiledata","network","number","phone","signal","speed","symbol","text","type","wifi"]},{"name":"pinch","tags":["arrow","arrows","compress","direction","finger","grasp","hand","navigation","nip","pinch","squeeze","tweak"]},{"name":"lock_person","tags":[]},{"name":"grid_3x3","tags":["3","grid","layout","line","space"]},{"name":"mark_unread_chat_alt","tags":["bubble","chat","circle","comment","communicate","mark","message","notification","speech","unread"]},{"name":"web_stories","tags":["google","images","logo","stories","web"]},{"name":"safety_check","tags":["certified","check","clock","privacy","private","protect","protection","safety","schedule","security","shield","time","verified"]},{"name":"filter_frames","tags":["boarders","border","camera","center","edit","editing","effect","filter","filters","focus","frame","frames","image","options","photo","photography","picture"]},{"name":"spatial_audio_off","tags":["audio","disabled","enabled","music","note","off","offline","on","slash","sound","spatial"]},{"name":"directions_subway","tags":["automobile","car","cars","direction","directions","maps","public","rail","subway","train","transportation","vehicle"]},{"name":"reset_tv","tags":["arrow","device","hardware","monitor","reset","television","tv"]},{"name":"4k","tags":["4000","4K","alphabet","character","digit","display","font","letter","number","pixel","pixels","resolution","symbol","text","type","video"]},{"name":"burst_mode","tags":["burst","image","landscape","mode","mountain","mountains","multiple","photo","photography","picture"]},{"name":"chalet","tags":["architecture","chalet","cottage","estate","home","house","maps","place","real","residence","residential","stay","traveling"]},{"name":"battery_1_bar","tags":["1","bar","battery","cell","charge","mobile","power"]},{"name":"elderly_woman","tags":["body","cane","elderly","female","gender","girl","human","lady","old","people","person","senior","social","symbol","woman","women"]},{"name":"headset_off","tags":["accessory","audio","chat","device","disabled","ear","earphone","enabled","headphones","headset","listen","mic","music","off","on","slash","sound","talk"]},{"name":"swipe_vertical","tags":["arrows","direction","finger","hands","hit","navigation","strike","swing","swpie","take","verticle"]},{"name":"crib","tags":["babies","baby","bassinet","bed","child","children","cradle","crib","infant","kid","newborn","sleeping","toddler"]},{"name":"video_label","tags":["label","screen","video","window"]},{"name":"fiber_smart_record","tags":["circle","dot","fiber","play","record","smart","watch"]},{"name":"brightness_auto","tags":["A","auto","brightness","control","display","level","mobile","monitor","phone","screen","sun"]},{"name":"margin","tags":["design","layout","margin","padding","size","square"]},{"name":"punch_clock","tags":["clock","date","punch","schedule","time","timer","timesheet"]},{"name":"compass_calibration","tags":["calibration","compass","connection","internet","location","maps","network","refresh","service","signal","wifi","wireless"]},{"name":"mosque","tags":["islam","islamic","masjid","muslim","religion","spiritual","worship"]},{"name":"medication_liquid","tags":["+","bottle","doctor","drug","health","hospital","liquid","medications","medicine","pharmacy","spoon","vessel"]},{"name":"camera_roll","tags":["camera","film","image","library","photo","photography","roll"]},{"name":"pin_end","tags":["action","arrow","dot","end","pin"]},{"name":"dialer_sip","tags":["alphabet","call","cell","character","contact","device","dialer","font","hardware","initiation","internet","letter","mobile","over","phone","protocol","routing","session","sip","symbol","telephone","text","type","voice"]},{"name":"oil_barrel","tags":["barrel","droplet","gas","gasoline","nest","oil","water"]},{"name":"disc_full","tags":["!","alert","attention","caution","cd","danger","disc","error","exclamation","full","important","mark","music","notification","storage","symbol","warning"]},{"name":"signal_cellular_connected_no_internet_4_bar","tags":["!","4","alert","attention","bar","caution","cell","cellular","connected","danger","data","error","exclamation","important","internet","mark","mobile","network","no","notification","phone","signal","symbol","warning","wifi","wireless"]},{"name":"wind_power","tags":["eco","energy","nest","power","wind","windy"]},{"name":"logo_dev","tags":["dev","dev.to","logo"]},{"name":"sledding","tags":["athlete","athletic","body","entertainment","exercise","hobby","human","people","person","sled","sledding","sledge","snow","social","sports","travel","winter"]},{"name":"invert_colors_off","tags":["colors","disabled","drop","droplet","enabled","hue","invert","inverted","off","offline","on","opacity","palette","slash","tone","water"]},{"name":"wifi_lock","tags":["cellular","connection","data","internet","lock","locked","mobile","network","password","privacy","private","protection","safety","secure","security","service","signal","wifi","wireless"]},{"name":"noise_aware","tags":["audio","aware","cancellation","music","noise","note","sound"]},{"name":"face_3","tags":["account","emoji","eyes","face","human","lock","log","login","logout","people","person","profile","recognition","security","social","thumbnail","unlock","user"]},{"name":"car_crash","tags":["accident","automobile","car","cars","collision","crash","direction","maps","public","transportation","vehicle"]},{"name":"comments_disabled","tags":["bubble","chat","comment","comments","communicate","disabled","enabled","feedback","message","off","offline","on","slash","speech"]},{"name":"data_array","tags":["array","brackets","code","coder","data","parentheses"]},{"name":"do_not_disturb_on_total_silence","tags":["busy","disturb","do","mute","no","not","on total","quiet","silence"]},{"name":"filter_b_and_w","tags":["and","b","black","contrast","edit","editing","effect","filter","grayscale","image","images","photography","picture","pictures","settings","w","white"]},{"name":"no_encryption_gmailerrorred","tags":["disabled","enabled","encryption","error","gmail","lock","locked","no","off","on","slash"]},{"name":"blur_linear","tags":["blur","dots","edit","editing","effect","enhance","filter","linear"]},{"name":"view_cozy","tags":["comfy","cozy","design","format","layout","view","web"]},{"name":"wifi_calling","tags":["call","calling","cell","connect","connection","connectivity","contact","device","hardware","mobile","phone","signal","telephone","wifi","wireless"]},{"name":"electric_rickshaw","tags":["automobile","car","cars","electric","india","maps","rickshaw","transportation","truck","vehicle"]},{"name":"rtt","tags":["call","real","rrt","text","time"]},{"name":"join_right","tags":["circle","command","join","matching","overlap","right","sql","values"]},{"name":"crop_3_2","tags":["2","3","adjust","adjustments","area","by","crop","edit","editing","frame","image","images","photo","photos","rectangle","settings","size","square"]},{"name":"crop_landscape","tags":["adjust","adjustments","area","crop","edit","editing","frame","image","images","landscape","photo","photos","settings","size"]},{"name":"nearby_error","tags":["!","alert","attention","caution","danger","error","exclamation","important","mark","nearby","notification","symbol","warning"]},{"name":"airplanemode_inactive","tags":["airplane","airplanemode","airport","disabled","enabled","flight","fly","inactive","maps","mode","off","offline","on","slash","transportation","travel"]},{"name":"airline_stops","tags":["airline","arrow","destination","direction","layover","location","maps","place","stops","transportation","travel","trip"]},{"name":"bluetooth_audio","tags":["audio","bluetooth","connect","connection","device","music","signal","sound","symbol"]},{"name":"portable_wifi_off","tags":["connection","data","disabled","enabled","internet","network","off","offline","on","portable","service","signal","slash","wifi","wireless"]},{"name":"turn_right","tags":["arrow","arrows","direction","directions","maps","navigation","path","right","route","sign","traffic","turn"]},{"name":"1x_mobiledata","tags":["1x","alphabet","cellular","character","digit","font","letter","mobile","mobiledata","network","number","phone","signal","speed","symbol","text","type","wifi"]},{"name":"do_not_step","tags":["boot","disabled","do","enabled","feet","foot","not","off","on","shoe","slash","sneaker","step","steps"]},{"name":"sensor_occupied","tags":["body","body response","connection","fitbit","human","network","people","person","scan","sensors","signal","smart body scan sensor","wireless"]},{"name":"directions_railway","tags":["automobile","car","cars","direction","directions","maps","public","railway","train","transportation","vehicle"]},{"name":"security_update_warning","tags":["!","Android","OS","alert","attention","caution","danger","device","download","error","exclamation","hardware","iOS","important","mark","mobile","notification","phone","security","symbol","tablet","update","warning"]},{"name":"pentagon","tags":["five sides","pentagon","shape"]},{"name":"wrap_text","tags":["arrow writing","doc","edit","editing","editor","sheet","spreadsheet","text","type","wrap","write","writing"]},{"name":"no_meeting_room","tags":["building","disabled","door","doorway","enabled","entrance","home","house","interior","meeting","no","off","office","on","open","places","room","slash"]},{"name":"sd_card_alert","tags":["!","alert","attention","camera","card","caution","danger","digital","error","exclamation","important","mark","memory","notification","photos","sd","secure","storage","symbol","warning"]},{"name":"deselect","tags":["all","disabled","enabled","off","on","selection","slash","square","tool"]},{"name":"switch_camera","tags":["arrow","arrows","camera","photo","photography","picture","switch"]},{"name":"text_rotate_up","tags":["A","alphabet","arrow","character","field","font","letter","move","rotate","symbol","text","type","up"]},{"name":"sync_lock","tags":["around","arrow","arrows","lock","locked","password","privacy","private","protection","renew","rotate","safety","secure","security","sync","turn"]},{"name":"switch_video","tags":["arrow","arrows","camera","photography","switch","video","videos"]},{"name":"border_clear","tags":["border","clear","doc","edit","editing","editor","sheet","spreadsheet","stroke","text","type","writing"]},{"name":"repeat_one_on","tags":["arrow","arrows","control","controls","digit","media","music","number","on","one","repeat","symbol","video"]},{"name":"no_meals","tags":["dining","disabled","eat","enabled","food","fork","knife","meal","meals","no","off","on","restaurant","slash","spoon","utensils"]},{"name":"align_vertical_top","tags":["align","alignment","format","layout","lines","paragraph","rule","rules","style","text","top","vertical"]},{"name":"subscript","tags":["2","doc","edit","editing","editor","gmail","novitas","sheet","spreadsheet","style","subscript","symbol","text","writing","x"]},{"name":"font_download_off","tags":["alphabet","character","disabled","download","enabled","font","letter","off","on","slash","square","symbol","text","type"]},{"name":"scoreboard","tags":["board","points","score","scoreboard","sports"]},{"name":"swipe_right_alt","tags":["accept","alt","arrows","direction","finger","hands","hit","navigation","right","strike","swing","swpie","take"]},{"name":"align_vertical_center","tags":["align","alignment","center","format","layout","lines","paragraph","rule","rules","style","text","vertical"]},{"name":"electric_meter","tags":["bolt","electric","energy","fast","lightning","measure","meter","nest","thunderbolt","usage","voltage","volts"]},{"name":"contact_emergency","tags":["account","avatar","call","cell","contacts","face","human","info","information","mobile","people","person","phone","profile","user"]},{"name":"signal_cellular_connected_no_internet_0_bar","tags":["!","0","alert","attention","bar","caution","cell","cellular","connected","danger","data","error","exclamation","important","internet","mark","mobile","network","no","notification","phone","signal","symbol","warning","wifi","wireless"]},{"name":"sim_card_alert","tags":["!","alert","attention","camera","card","caution","danger","digital","error","exclamation","important","mark","memory","notification","photos","sd","secure","storage","symbol","warning"]},{"name":"battery_2_bar","tags":["2","bar","battery","cell","charge","mobile","power"]},{"name":"text_rotation_angleup","tags":["A","alphabet","angleup","arrow","character","field","font","letter","move","rotate","symbol","text","type"]},{"name":"text_rotation_down","tags":["A","alphabet","arrow","character","dow","field","font","letter","move","rotate","symbol","text","type"]},{"name":"railway_alert","tags":["!","alert","attention","automobile","bike","car","cars","caution","danger","direction","error","exclamation","important","maps","mark","notification","public","railway","scooter","subway","symbol","train","transportation","vehicle","vespa","warning"]},{"name":"escalator","tags":["down","escalator","staircase","up"]},{"name":"electric_moped","tags":["automobile","bike","car","cars","electric","maps","moped","scooter","transportation","travel","vehicle","vespa"]},{"name":"closed_caption_disabled","tags":["accessible","alphabet","caption","cc","character","closed","decoder","disabled","enabled","font","language","letter","media","movies","off","on","slash","subtitle","subtitles","symbol","text","tv","type"]},{"name":"filter_7","tags":["7","digit","edit","editing","effect","filter","image","images","multiple","number","photography","picture","pictures","settings","stack","symbol"]},{"name":"heat_pump","tags":["air conditioner","cool","energy","furnance","heat","nest","pump","usage"]},{"name":"dry","tags":["air","bathroom","dry","dryer","fingers","gesture","hand","wc"]},{"name":"fork_right","tags":["arrow","arrows","direction","directions","fork","maps","navigation","path","right","route","sign","traffic"]},{"name":"text_rotation_angledown","tags":["A","alphabet","angledown","arrow","character","field","font","letter","move","rotate","symbol","text","type"]},{"name":"do_not_disturb_off","tags":["cancel","close","denied","deny","disabled","disturb","do","enabled","off","on","remove","silence","slash","stop"]},{"name":"screen_lock_portrait","tags":["Android","OS","device","hardware","iOS","lock","mobile","phone","portrait","rotate","screen","tablet"]},{"name":"send_time_extension","tags":["deliver","dispatch","envelop","extension","mail","message","schedule","send","time"]},{"name":"keyboard_command_key","tags":["button","command key","control","keyboard"]},{"name":"remove_from_queue","tags":["desktop","device","display","from","hardware","monitor","queue","remove","screen","steam"]},{"name":"filter_4","tags":["4","digit","edit","editing","effect","filter","image","images","multiple","number","photography","picture","pictures","settings","stack","symbol"]},{"name":"filter_9_plus","tags":["+","9","digit","edit","editing","effect","filter","image","images","multiple","number","photography","picture","pictures","plus","settings","stack","symbol"]},{"name":"exposure_plus_2","tags":["2","add","brightness","contrast","digit","edit","editing","effect","exposure","image","number","photo","photography","plus","settings","symbol"]},{"name":"surround_sound","tags":["circle","signal","sound","speaker","surround","system","volumn","wireless"]},{"name":"airline_seat_individual_suite","tags":["airline","body","business","class","first","human","individual","people","person","rest","seat","sleep","suite","travel"]},{"name":"home_max","tags":["device","gadget","hardware","home","internet","iot","max","nest","smart","things"]},{"name":"phone_paused","tags":["call","cell","contact","device","hardware","mobile","pause","paused","phone","telephone"]},{"name":"local_play","tags":[]},{"name":"stroller","tags":["baby","care","carriage","child","children","infant","kid","newborn","stroller","toddler","young"]},{"name":"wifi_password","tags":["(scan)","[cellular","connection","data","internet","lock","mobile]","network","password","secure","service","signal","wifi","wireless"]},{"name":"browse_gallery","tags":["clock","collection","gallery","library","stack","watch"]},{"name":"system_security_update","tags":["Android","OS","arrow","cell","device","down","hardware","iOS","mobile","phone","security","system","tablet","update"]},{"name":"person_2","tags":["account","face","human","people","person","profile","user"]},{"name":"screenshot_monitor","tags":["Android","OS","chrome","desktop","device","display","hardware","iOS","mac","monitor","screen","screengrab","screenshot","web","window"]},{"name":"wb_iridescent","tags":["balance","bright","edit","editing","iridescent","light","lighting","setting","settings","white","wp"]},{"name":"grid_off","tags":["collage","disabled","enabled","grid","image","layout","off","on","slash","view"]},{"name":"system_security_update_warning","tags":["!","Android","OS","alert","attention","caution","cell","danger","device","error","exclamation","hardware","iOS","important","mark","mobile","notification","phone","security","symbol","system","tablet","update","warning"]},{"name":"play_disabled","tags":["control","controls","disabled","enabled","media","music","off","on","play","slash","video"]},{"name":"php","tags":["alphabet","brackets","character","code","css","develop","developer","engineer","engineering","font","html","letter","php","platform","symbol","text","type"]},{"name":"phishing","tags":["fish","fishing","fraud","hook","phishing","scam"]},{"name":"border_style","tags":["border","color","doc","edit","editing","editor","sheet","spreadsheet","stroke","style","text","type","writing"]},{"name":"motion_photos_paused","tags":["animation","circle","motion","pause","paused","photos","video"]},{"name":"headphones_battery","tags":["accessory","audio","battery","charging","device","ear","earphone","headphones","headset","listen","music","sound"]},{"name":"monochrome_photos","tags":["black","camera","image","monochrome","photo","photography","photos","picture","white"]},{"name":"web_asset_off","tags":["asset","browser","disabled","enabled","internet","off","on","page","screen","slash","web","webpage","website","windows","www"]},{"name":"wifi_tethering_off","tags":["cell","cellular","connection","data","disabled","enabled","internet","mobile","network","off","offline","on","phone","scan","service","signal","slash","speed","tethering","wifi","wireless"]},{"name":"text_decrease","tags":["-","alphabet","character","decrease","font","letter","minus","remove","resize","subtract","symbol","text","type"]},{"name":"view_comfy_alt","tags":["alt","comfy","cozy","design","format","layout","view","web"]},{"name":"photo_camera_back","tags":["back","camera","image","landscape","mountain","mountains","photo","photography","picture","rear"]},{"name":"folder_off","tags":["data","disabled","doc","document","drive","enabled","file","folder","folders","off","on","online","sheet","slash","slide","storage"]},{"name":"gas_meter","tags":["droplet","energy","gas","measure","meter","nest","usage","water"]},{"name":"edgesensor_high","tags":["Android","OS","cell","device","edge","hardware","high","iOS","mobile","move","phone","sensitivity","sensor","tablet","vibrate"]},{"name":"filter_5","tags":["5","digit","edit","editing","effect","filter","image","images","multiple","number","photography","picture","pictures","settings","stack","symbol"]},{"name":"stay_current_landscape","tags":["Android","OS","current","device","hardware","iOS","landscape","mobile","phone","stay","tablet"]},{"name":"sip","tags":["alphabet","call","character","dialer","font","initiation","internet","letter","over","phone","protocol","routing","session","sip","symbol","text","type","voice"]},{"name":"power_input","tags":["input","lines","power","supply"]},{"name":"smart_screen","tags":["Android","OS","airplay","cast","cell","connect","device","hardware","iOS","mobile","phone","screen","screencast","smart","stream","tablet","video"]},{"name":"mail_lock","tags":["email","envelop","letter","lock","locked","mail","message","password","privacy","private","protection","safety","secure","security","send"]},{"name":"dataset","tags":[]},{"name":"nat","tags":["communication","nat"]},{"name":"do_disturb_off","tags":["cancel","close","denied","deny","disabled","disturb","do","enabled","off","on","remove","silence","slash","stop"]},{"name":"no_drinks","tags":["alcohol","beverage","bottle","cocktail","drink","drinks","food","liquor","no","wine"]},{"name":"bike_scooter","tags":["automobile","bike","car","cars","maps","scooter","transportation","vehicle","vespa"]},{"name":"dock","tags":["Android","OS","cell","charging","connector","device","dock","hardware","iOS","mobile","phone","power","station","tablet"]},{"name":"face_2","tags":["account","emoji","eyes","face","human","lock","log","login","logout","people","person","profile","recognition","security","social","thumbnail","unlock","user"]},{"name":"face_retouching_off","tags":["disabled","edit","editing","effect","emoji","emotion","enabled","face","faces","image","natural","off","on","photo","photography","retouch","retouching","settings","slash","tag"]},{"name":"auto_fix_off","tags":["ai","artificial","auto","automatic","automation","custom","disabled","edit","enabled","erase","fix","genai","intelligence","magic","modify","off","on","slash","smart","spark","sparkle","star","wand"]},{"name":"airline_seat_flat","tags":["airline","body","business","class","first","flat","human","people","person","rest","seat","sleep","travel"]},{"name":"phone_locked","tags":["call","cell","contact","device","hardware","lock","locked","mobile","password","phone","privacy","private","protection","safety","secure","security","telephone"]},{"name":"network_locked","tags":["alert","available","cellular","connection","data","error","internet","lock","locked","mobile","network","not","privacy","private","protection","restricted","safety","secure","security","service","signal","warning","wifi","wireless"]},{"name":"padding","tags":["design","layout","margin","padding","size","square"]},{"name":"browser_not_supported","tags":["browser","disabled","enabled","internet","not","off","on","page","screen","site","slash","supported","web","website","www"]},{"name":"border_outer","tags":["border","doc","edit","editing","editor","outer","sheet","spreadsheet","stroke","text","type","writing"]},{"name":"exposure_neg_1","tags":["1","brightness","contrast","digit","edit","editing","effect","exposure","image","neg","negative","number","photo","photography","settings","symbol"]},{"name":"view_compact_alt","tags":["alt","compact","design","format","layout dense","view","web"]},{"name":"pest_control_rodent","tags":["control","exterminator","mice","pest","rodent"]},{"name":"swipe_down_alt","tags":["alt","arrows","direction","disable","down","enable","finger","hands","hit","navigation","strike","swing","swpie","take"]},{"name":"airlines","tags":["airlines","airplane","airport","flight","plane","transportation","travel","trip"]},{"name":"turn_left","tags":["arrow","arrows","direction","directions","left","maps","navigation","path","route","sign","traffic","turn"]},{"name":"sd","tags":["alphabet","camera","card","character","data","device","digital","drive","flash","font","image","letter","memory","photo","sd","secure","symbol","text","type"]},{"name":"near_me_disabled","tags":["destination","direction","disabled","enabled","location","maps","me","navigation","near","off","on","pin","place","point","slash"]},{"name":"face_4","tags":["account","emoji","eyes","face","human","lock","log","login","logout","people","person","profile","recognition","security","social","thumbnail","unlock","user"]},{"name":"stay_primary_landscape","tags":["Android","OS","current","device","hardware","iOS","landscape","mobile","phone","primary","stay","tablet"]},{"name":"4g_plus_mobiledata","tags":["4g","alphabet","cellular","character","digit","font","letter","mobile","mobiledata","network","number","phone","plus","signal","speed","symbol","text","type","wifi"]},{"name":"snowmobile","tags":["automobile","car","direction","skimobile","snow","snowmobile","social","sports","transportation","travel","vehicle","winter"]},{"name":"sign_language","tags":["communication","deaf","fingers","gesture","hand","language","sign"]},{"name":"network_ping","tags":["alert","available","cellular","connection","data","internet","ip","mobile","network","ping","service","signal","wifi","wireless"]},{"name":"signal_cellular_off","tags":["cell","cellular","data","disabled","enabled","internet","mobile","network","off","offline","on","phone","signal","slash","wifi","wireless"]},{"name":"signal_cellular_nodata","tags":["cell","cellular","data","internet","mobile","network","no","nodata","offline","phone","quit","signal","wifi","wireless","x"]},{"name":"no_sim","tags":["camera","card","device","eject","insert","memory","no","phone","sim","storage"]},{"name":"signal_wifi_4_bar_lock","tags":["4","bar","cell","cellular","data","internet","lock","locked","mobile","network","password","phone","privacy","private","protection","safety","secure","security","signal","wifi","wireless"]},{"name":"missed_video_call","tags":["arrow","call","camera","film","filming","hardware","image","missed","motion","picture","record","video","videography"]},{"name":"lte_mobiledata","tags":["alphabet","character","data","font","internet","letter","lte","mobile","network","speed","symbol","text","type","wifi","wireless"]},{"name":"earbuds_battery","tags":["accessory","audio","battery","charging","earbuds","earphone","headphone","listen","music","sound"]},{"name":"panorama_photosphere","tags":["angle","horizontal","image","panorama","photo","photography","photosphere","picture","wide"]},{"name":"no_crash","tags":["accident","auto","automobile","car","cars","check","collision","confirm","correct","crash","direction","done","enter","maps","mark","no","ok","okay","select","tick","transportation","vehicle","yes"]},{"name":"add_alarm","tags":[]},{"name":"directions_transit_filled","tags":["automobile","car","cars","direction","directions","filled","maps","public","rail","subway","train","transit","transportation","vehicle"]},{"name":"u_turn_left","tags":["arrow","arrows","direction","directions","left","maps","navigation","path","route","sign","traffic","u-turn"]},{"name":"line_axis","tags":["axis","dash","horizontal","line","stroke","vertical"]},{"name":"density_large","tags":["density","horizontal","large","lines","rule","rules"]},{"name":"location_disabled","tags":["destination","direction","disabled","enabled","location","maps","off","on","pin","place","pointer","slash","stop","tracking"]},{"name":"bluetooth_drive","tags":["automobile","bluetooth","car","cars","cast","connect","connection","device","drive","maps","paring","streaming","symbol","transportation","travel","vehicle","wireless"]},{"name":"30fps","tags":["30fps","alphabet","camera","character","digit","font","fps","frames","letter","number","symbol","text","type","video"]},{"name":"no_luggage","tags":["bag","baggage","carry","disabled","enabled","luggage","no","off","on","slash","suitcase","travel"]},{"name":"leak_remove","tags":["connection","data","disabled","enabled","leak","link","network","off","offline","on","remove","service","signals","slash","synce","wireless"]},{"name":"filter_8","tags":["8","digit","edit","editing","effect","filter","image","images","multiple","number","photography","picture","pictures","settings","stack","symbol"]},{"name":"mobile_off","tags":["Android","OS","cell","device","disabled","enabled","hardware","iOS","mobile","off","on","phone","silence","slash","tablet"]},{"name":"key_off","tags":["disabled","enabled","key","lock","off","offline","on","password","slash","unlock"]},{"name":"signal_cellular_null","tags":["cell","cellular","data","internet","mobile","network","null","phone","signal","wifi","wireless"]},{"name":"phonelink_off","tags":["Android","OS","chrome","computer","connect","desktop","device","disabled","enabled","hardware","iOS","link","mac","mobile","off","on","phone","phonelink","slash","sync","tablet","web","windows"]},{"name":"filter_9","tags":["9","digit","edit","editing","effect","filter","image","images","multiple","number","photography","picture","pictures","settings","stack","symbol"]},{"name":"home_mini","tags":["Internet","device","gadget","hardware","home","iot","mini","nest","smart","things"]},{"name":"on_device_training","tags":["arrow","bulb","call","cell","contact","device","hardware","idea","inprogress","light","load","loading","mobile","model","phone","refresh","renew","restore","reverse","rotate","telephone","training"]},{"name":"egg_alt","tags":["breakfast","brunch","egg","food"]},{"name":"media_bluetooth_on","tags":["bluetooth","connect","connection","connectivity","device","disabled","enabled","media","music","note","off","on","online","paring","signal","slash","symbol","wireless"]},{"name":"10k","tags":["10000","10K","alphabet","character","digit","display","font","letter","number","pixel","pixels","resolution","symbol","text","type","video"]},{"name":"video_stable","tags":["film","filming","recording","setting","stability","stable","taping","video"]},{"name":"add_home","tags":[]},{"name":"no_transfer","tags":["automobile","bus","car","cars","direction","disabled","enabled","maps","no","off","on","public","slash","transfer","transportation","vehicle"]},{"name":"timer_10","tags":["10","digits","duration","number","numbers","seconds","time","timer"]},{"name":"directions_subway_filled","tags":["automobile","car","cars","direction","directions","filled","maps","public","rail","subway","train","transportation","vehicle"]},{"name":"wb_shade","tags":["balance","house","light","lighting","shade","wb","white"]},{"name":"swipe_left_alt","tags":["alt","arrow","arrows","finger","hand","hit","left","navigation","reject","strike","swing","swipe","take"]},{"name":"filter_6","tags":["6","digit","edit","editing","effect","filter","image","images","multiple","number","photography","picture","pictures","settings","stack","symbol"]},{"name":"cyclone","tags":["crisis","disaster","natural","rain","storm","weather","wind","winds"]},{"name":"network_wifi_1_bar","tags":[]},{"name":"directions_railway_filled","tags":["automobile","car","cars","direction","directions","filled","maps","public","railway","train","transportation","vehicle"]},{"name":"wifi_find","tags":["(scan)","[cellular","connection","data","detect","discover","find","internet","look","magnifying glass","mobile]","network","notice","search","service","signal","wifi","wireless"]},{"name":"blur_off","tags":["blur","disabled","dots","edit","editing","effect","enabled","enhance","off","on","slash"]},{"name":"motion_photos_off","tags":["animation","circle","disabled","enabled","motion","off","on","photos","slash","video"]},{"name":"lyrics","tags":["audio","bubble","chat","comment","communicate","feedback","key","lyrics","message","music","note","song","sound","speech","track"]},{"name":"raw_on","tags":["alphabet","character","disabled","enabled","font","image","letter","off","on","original","photo","photography","raw","slash","symbol","text","type"]},{"name":"flight_class","tags":["airplane","business","class","first","flight","plane","seat","transportation","travel","trip","window"]},{"name":"insert_page_break","tags":["break","doc","document","file","page","paper"]},{"name":"rsvp","tags":["alphabet","character","font","invitation","invite","letter","plaît","respond","rsvp","répondez","sil","symbol","text","type","vous"]},{"name":"tire_repair","tags":["auto","automobile","car","cars","gauge","mechanic","pressure","repair","tire","vehicle"]},{"name":"swipe_up_alt","tags":["alt","arrows","direction","disable","enable","finger","hands","hit","navigation","strike","swing","swpie","take","up"]},{"name":"3g_mobiledata","tags":["3g","alphabet","cellular","character","digit","font","letter","mobile","mobiledata","network","number","phone","signal","speed","symbol","text","type","wifi"]},{"name":"tv_off","tags":["Android","OS","chrome","desktop","device","disabled","enabled","hardware","iOS","mac","monitor","off","on","slash","television","tv","web","window"]},{"name":"hdr_on","tags":["add","alphabet","character","dynamic","enhance","font","hdr","high","letter","on","plus","range","select","symbol","text","type"]},{"name":"add_home_work","tags":[]},{"name":"motion_photos_pause","tags":["animation","circle","motion","pause","paused","photos","video"]},{"name":"edgesensor_low","tags":["Android","cell","device","edge","hardware","iOS","low","mobile","move","phone","sensitivity","sensor","tablet","vibrate"]},{"name":"grid_goldenratio","tags":["golden","goldenratio","grid","layout","lines","ratio","space"]},{"name":"network_wifi_3_bar","tags":[]},{"name":"temple_buddhist","tags":["buddha","buddhism","buddhist","monastery","religion","spiritual","temple","worship"]},{"name":"airline_seat_flat_angled","tags":["airline","angled","body","business","class","first","flat","human","people","person","rest","seat","sleep","travel"]},{"name":"fort","tags":["castle","fort","fortress","mansion","palace"]},{"name":"spatial_tracking","tags":["audio","disabled","enabled","music","note","off","offline","on","slash","sound","spatial","tracking"]},{"name":"screen_lock_rotation","tags":["Android","OS","arrow","device","hardware","iOS","lock","mobile","phone","rotate","rotation","screen","tablet","turn"]},{"name":"fiber_pin","tags":["alphabet","character","fiber","font","letter","network","pin","symbol","text","type"]},{"name":"phone_bluetooth_speaker","tags":["bluetooth","call","cell","connect","connection","connectivity","contact","device","hardware","mobile","phone","signal","speaker","symbol","telephone","wireless"]},{"name":"vignette","tags":["border","edit","editing","filter","gradient","image","photo","photography","setting","vignette"]},{"name":"panorama_horizontal","tags":["angle","horizontal","image","panorama","photo","photography","picture","wide"]},{"name":"propane_tank","tags":["bbq","gas","grill","nest","propane","tank"]},{"name":"kebab_dining","tags":["dining","dinner","food","kebab","meal","meat","skewer"]},{"name":"developer_board_off","tags":["board","chip","computer","developer","development","disabled","enabled","hardware","microchip","off","on","processor","slash"]},{"name":"adf_scanner","tags":["adf","document","feeder","machine","office","scan","scanner"]},{"name":"no_cell","tags":["Android","OS","cell","device","disabled","enabled","hardware","iOS","mobile","no","off","on","phone","slash","tablet"]},{"name":"dirty_lens","tags":["camera","dirty","lens","photo","photography","picture","splat"]},{"name":"usb_off","tags":["cable","connection","device","off","usb","wire"]},{"name":"image_aspect_ratio","tags":["aspect","image","photo","photography","picture","ratio","rectangle","square"]},{"name":"30fps_select","tags":["30","camera","digits","fps","frame","frequency","image","numbers","per","rate","second","seconds","select","video"]},{"name":"60fps","tags":["60fps","camera","digit","fps","frames","number","symbol","video"]},{"name":"screen_lock_landscape","tags":["Android","OS","device","hardware","iOS","landscape","lock","mobile","phone","rotate","screen","tablet"]},{"name":"lte_plus_mobiledata","tags":["+","alphabet","character","data","font","internet","letter","lte","mobile","network","plus","speed","symbol","text","type","wifi","wireless"]},{"name":"piano_off","tags":["disabled","enabled","instrument","keyboard","keys","music","musical","off","on","piano","slash","social"]},{"name":"unfold_more_double","tags":["arrow","arrows","chevron","collapse","direction","double","down","expand","expandable","list","more","navigation","unfold"]},{"name":"deblur","tags":["adjust","deblur","edit","editing","enhance","face","image","lines","photo","photography","sharpen"]},{"name":"person_4","tags":["account","face","human","people","person","profile","user"]},{"name":"spatial_audio","tags":["audio","music","note","sound","spatial"]},{"name":"camera_rear","tags":["camera","front","lens","mobile","phone","photo","photography","picture","portrait","rear","selfie"]},{"name":"timer_10_select","tags":["10","alphabet","camera","character","digit","font","letter","number","seconds","select","symbol","text","timer","type"]},{"name":"face_5","tags":["account","emoji","eyes","face","human","lock","log","login","logout","people","person","profile","recognition","security","social","thumbnail","unlock","user"]},{"name":"minor_crash","tags":["accident","auto","automobile","car","cars","collision","directions","maps","public","transportation","vehicle"]},{"name":"sos","tags":["font","help","letters","save","sos","text","type"]},{"name":"videogame_asset_off","tags":["asset","console","controller","device","disabled","enabled","game","gamepad","gaming","off","on","playstation","slash","video","videogame"]},{"name":"flood","tags":["crisis","disaster","natural","rain","storm","weather"]},{"name":"60fps_select","tags":["60","camera","digits","fps","frame","frequency","numbers","per","rate","second","seconds","select","video"]},{"name":"timer_3","tags":["3","digits","duration","number","numbers","seconds","time","timer"]},{"name":"vpn_key_off","tags":["code","disabled","enabled","key","lock","network","off","offline","on","passcode","password","slash","unlock","vpn"]},{"name":"directions_off","tags":["arrow","directions","disabled","enabled","maps","off","on","right","route","sign","slash","traffic"]},{"name":"emergency_share","tags":["alert","attention","caution","danger","emergency","important","notification","share","warning"]},{"name":"panorama_wide_angle_select","tags":["angle","image","panorama","photo","photography","picture","select","wide"]},{"name":"airline_seat_legroom_normal","tags":["airline","body","feet","human","leg","legroom","normal","people","person","seat","sitting","space","travel"]},{"name":"fiber_dvr","tags":["alphabet","character","digital","dvr","electronics","fiber","font","letter","network","record","recorder","symbol","text","tv","type","video"]},{"name":"person_3","tags":["account","face","human","people","person","profile","user"]},{"name":"scuba_diving","tags":["diving","entertainment","exercise","hobby","scuba","social","swim","swimming"]},{"name":"signal_cellular_no_sim","tags":["camera","card","cellular","chip","device","disabled","enabled","memory","no","off","offline","on","phone","signal","sim","slash","storage"]},{"name":"24mp","tags":["24","camera","digits","font","image","letters","megapixel","megapixels","mp","numbers","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"exposure_neg_2","tags":["2","brightness","contrast","digit","edit","editing","effect","exposure","image","neg","negative","number","photo","photography","settings","symbol"]},{"name":"network_wifi_2_bar","tags":[]},{"name":"wifi_2_bar","tags":["2","bar","cell","cellular","connection","data","internet","mobile","network","phone","scan","service","signal","wifi","wireless"]},{"name":"u_turn_right","tags":["arrow","arrows","direction","directions","maps","navigation","path","right","route","sign","traffic","u-turn"]},{"name":"currency_yuan","tags":["bill","card","cash","coin","commerce","cost","credit","currency","dollars","finance","money","online","pay","payment","price","shopping","symbol","yuan"]},{"name":"currency_lira","tags":["bill","card","cash","coin","commerce","cost","credit","currency","dollars","finance","lira","money","online","pay","payment","price","shopping","symbol"]},{"name":"no_flash","tags":["bolt","camera","disabled","enabled","flash","image","lightning","no","off","on","photo","photography","picture","slash","thunderbolt"]},{"name":"temple_hindu","tags":["hindu","hinduism","hindus","mandir","religion","spiritual","temple","worship"]},{"name":"mode_fan_off","tags":["air conditioner","cool","disabled","enabled","fan","nest","off","on","slash"]},{"name":"airline_seat_legroom_extra","tags":["airline","body","extra","feet","human","leg","legroom","people","person","seat","sitting","space","travel"]},{"name":"4k_plus","tags":["+","4000","4K","alphabet","character","digit","display","font","letter","number","pixel","pixels","plus","resolution","symbol","text","type","video"]},{"name":"border_inner","tags":["border","doc","edit","editing","editor","inner","sheet","spreadsheet","stroke","text","type","writing"]},{"name":"wifi_tethering_error","tags":["!","alert","attention","caution","cell","cellular","connection","danger","data","error","exclamation","important","internet","mark","mobile","network","notification","phone","rounded","scan","service","signal","speed","symbol","tethering","warning","wifi","wireless"]},{"name":"airline_seat_legroom_reduced","tags":["airline","body","feet","human","leg","legroom","people","person","reduced","seat","sitting","space","travel"]},{"name":"synagogue","tags":["jew","jewish","religion","shul","spiritual","temple","worship"]},{"name":"border_left","tags":["border","doc","edit","editing","editor","left","sheet","spreadsheet","stroke","text","type","writing"]},{"name":"autofps_select","tags":["A","alphabet","auto","character","font","fps","frame","frequency","letter","per","rate","second","seconds","select","symbol","text","type"]},{"name":"signal_cellular_alt_2_bar","tags":["2","bar","cell","cellular","data","internet","mobile","network","phone","signal","speed","wifi","wireless"]},{"name":"g_mobiledata","tags":["alphabet","character","data","font","g","letter","mobile","network","service","symbol","text","type"]},{"name":"1k","tags":["1000","1K","alphabet","character","digit","display","font","letter","number","pixel","pixels","resolution","symbol","text","type","video"]},{"name":"format_textdirection_l_to_r","tags":["align","alignment","doc","edit","editing","editor","format","ltr","sheet","spreadsheet","text","textdirection","type","writing"]},{"name":"border_bottom","tags":["border","bottom","doc","edit","editing","editor","sheet","spreadsheet","stroke","text","type","writing"]},{"name":"fork_left","tags":["arrow","arrows","direction","directions","fork","left","maps","navigation","path","route","sign","traffic"]},{"name":"severe_cold","tags":["!","alert","attention","caution","climate","cold","crisis","danger","disaster","error","exclamation","important","notification","severe","snow","snowflake","warning","weather","winter"]},{"name":"tsunami","tags":["crisis","disaster","flood","rain","storm","tsunami","weather"]},{"name":"signal_cellular_alt_1_bar","tags":["1","bar","cell","cellular","data","internet","mobile","network","phone","signal","speed","wifi","wireless"]},{"name":"border_vertical","tags":["border","doc","edit","editing","editor","sheet","spreadsheet","stroke","text","type","vertical","writing"]},{"name":"turn_sharp_right","tags":["arrow","arrows","direction","directions","maps","navigation","path","right","route","sharp","sign","traffic","turn"]},{"name":"no_backpack","tags":["accessory","backpack","bag","bookbag","knapsack","no","pack","travel"]},{"name":"remove_road","tags":["-","cancel","close","destination","direction","exit","highway","maps","minus","new","no","remove","road","stop","street","symbol","traffic","x"]},{"name":"timer_3_select","tags":["3","alphabet","camera","character","digit","font","letter","number","seconds","select","symbol","text","timer","type"]},{"name":"roller_skating","tags":["athlete","athletic","entertainment","exercise","hobby","roller","shoe","skate","skates","skating","social","sports","travel"]},{"name":"panorama_horizontal_select","tags":["angle","horizontal","image","panorama","photo","photography","picture","select","wide"]},{"name":"border_horizontal","tags":["border","doc","edit","editing","editor","horizontal","sheet","spreadsheet","stroke","text","type","writing"]},{"name":"2k","tags":["2000","2K","alphabet","character","digit","display","font","letter","number","pixel","pixels","resolution","symbol","text","type","video"]},{"name":"wifi_1_bar","tags":["1","bar","cell","cellular","connection","data","internet","mobile","network","phone","scan","service","signal","wifi","wireless"]},{"name":"format_textdirection_r_to_l","tags":["align","alignment","doc","edit","editing","editor","format","rtl","sheet","spreadsheet","text","textdirection","type","writing"]},{"name":"wifi_channel","tags":["(scan)","[cellular","channel","connection","data","internet","mobile]","network","service","signal","wifi","wireless"]},{"name":"roundabout_right","tags":["arrow","arrows","direction","directions","maps","navigation","path","right","roundabout","route","sign","traffic"]},{"name":"wb_auto","tags":["A","W","alphabet","auto","automatic","balance","character","edit","editing","font","image","letter","photo","photography","symbol","text","type","white","wp"]},{"name":"panorama_photosphere_select","tags":["angle","horizontal","image","panorama","photo","photography","photosphere","picture","select","wide"]},{"name":"panorama_wide_angle","tags":["angle","image","panorama","photo","photography","picture","wide"]},{"name":"hdr_plus","tags":["+","add","alphabet","character","circle","dynamic","enhance","font","hdr","high","letter","plus","range","select","symbol","text","type"]},{"name":"panorama_vertical_select","tags":["angle","image","panorama","photo","photography","picture","select","vertical","wide"]},{"name":"border_top","tags":["border","doc","edit","editing","editor","sheet","spreadsheet","stroke","text","top","type","writing"]},{"name":"mic_external_off","tags":["audio","disabled","enabled","external","mic","microphone","off","on","slash","sound","voice"]},{"name":"width_full","tags":[]},{"name":"h_mobiledata","tags":["alphabet","character","data","font","h","letter","mobile","network","service","symbol","text","type"]},{"name":"roller_shades","tags":["blinds","cover","curtains","nest","open","roller","shade","shutter","sunshade"]},{"name":"no_stroller","tags":["baby","care","carriage","child","children","disabled","enabled","infant","kid","newborn","no","off","on","parents","slash","stroller","toddler","young"]},{"name":"tornado","tags":["crisis","disaster","natural","rain","storm","tornado","weather","wind"]},{"name":"keyboard_control_key","tags":["control key","keyboard"]},{"name":"turn_slight_right","tags":["arrow","arrows","direction","directions","maps","navigation","path","right","route","sharp","sign","slight","traffic","turn"]},{"name":"border_right","tags":["border","doc","edit","editing","editor","right","sheet","spreadsheet","stroke","text","type","writing"]},{"name":"1k_plus","tags":["+","1000","1K","alphabet","character","digit","display","font","letter","number","pixel","pixels","plus","resolution","symbol","text","type","video"]},{"name":"turn_slight_left","tags":["arrow","arrows","direction","directions","maps","navigation","path","right","route","sign","slight","traffic","turn"]},{"name":"screen_rotation_alt","tags":["Android","OS","arrow","device","hardware","iOS","mobile","phone","rotate","rotation","screen","tablet","turn"]},{"name":"dataset_linked","tags":[]},{"name":"unfold_less_double","tags":["arrow","arrows","chevron","collapse","direction","double","expand","expandable","inward","less","list","navigation","unfold","up"]},{"name":"8k","tags":["8000","8K","alphabet","character","digit","display","font","letter","number","pixel","pixels","resolution","symbol","text","type","video"]},{"name":"landslide","tags":["crisis","disaster","natural","rain","storm","weather"]},{"name":"media_bluetooth_off","tags":["bluetooth","connect","connection","connectivity","device","disabled","enabled","media","music","note","off","offline","on","paring","signal","slash","symbol","wireless"]},{"name":"fire_truck","tags":[]},{"name":"e_mobiledata","tags":["alphabet","data","e","font","letter","mobile","mobiledata","text","type"]},{"name":"panorama_vertical","tags":["angle","image","panorama","photo","photography","picture","vertical","wide"]},{"name":"r_mobiledata","tags":["alphabet","character","data","font","letter","mobile","r","symbol","text","type"]},{"name":"12mp","tags":["12","camera","digits","font","image","letters","megapixel","megapixels","mp","numbers","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"repartition","tags":["arrow","arrows","data","partition","refresh","renew","repartition","restore","table"]},{"name":"width_normal","tags":[]},{"name":"h_plus_mobiledata","tags":["+","alphabet","character","data","font","h","letter","mobile","network","plus","service","symbol","text","type"]},{"name":"hdr_enhanced_select","tags":["add","alphabet","character","dynamic","enhance","font","hdr","high","letter","plus","range","select","symbol","text","type"]},{"name":"mp","tags":["alphabet","character","font","image","letter","megapixel","mp","photo","photography","pixels","quality","resolution","symbol","text","type"]},{"name":"shape_line","tags":["circle","draw","edit","editing","line","shape","square"]},{"name":"9k_plus","tags":["+","9000","9K","alphabet","character","digit","display","font","letter","number","pixel","pixels","plus","resolution","symbol","text","type","video"]},{"name":"5k","tags":["5000","5K","alphabet","character","digit","display","font","letter","number","pixel","pixels","resolution","symbol","text","type","video"]},{"name":"hevc","tags":["alphabet","character","coding","efficiency","font","hevc","high","letter","symbol","text","type","video"]},{"name":"currency_franc","tags":["bill","card","cash","coin","commerce","cost","credit","currency","dollars","finance","franc","money","online","pay","payment","price","shopping","symbol"]},{"name":"8k_plus","tags":["+","7000","8K","alphabet","character","digit","display","font","letter","number","pixel","pixels","plus","resolution","symbol","text","type","video"]},{"name":"hdr_on_select","tags":["+","alphabet","camera","character","circle","dynamic","font","hdr","high","letter","on","photo","range","select","symbol","text","type"]},{"name":"3k","tags":["3000","3K","alphabet","character","digit","display","font","letter","number","pixel","pixels","resolution","symbol","text","type","video"]},{"name":"transcribe","tags":[]},{"name":"width_wide","tags":[]},{"name":"hdr_auto_select","tags":["+","A","alphabet","auto","camera","character","circle","dynamic","font","hdr","high","letter","photo","range","select","symbol","text","type"]},{"name":"hls","tags":["alphabet","character","develop","developer","engineer","engineering","font","hls","letter","platform","symbol","text","type"]},{"name":"5k_plus","tags":["+","5000","5K","alphabet","character","digit","display","font","letter","number","pixel","pixels","plus","resolution","symbol","text","type","video"]},{"name":"assist_walker","tags":["accessibility","accessible","assist","body","disability","handicap","help","human","injured","injury","mobility","person","walk","walker"]},{"name":"hls_off","tags":["alphabet","character","develop","developer","disabled","enabled","engineer","engineering","font","hls","letter","off","offline","on","platform","slash","symbol","text","type"]},{"name":"18mp","tags":["18","camera","digits","font","image","letters","megapixel","megapixels","mp","numbers","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"format_overline","tags":["alphabet","character","doc","edit","editing","editor","font","format","letter","line","overline","sheet","spreadsheet","style","symbol","text","type","under","writing"]},{"name":"volcano","tags":["crisis","disaster","eruption","lava","magma","natural","volcano"]},{"name":"vaping_rooms","tags":["allowed","e-cigarette","never","no","places","prohibited","smoke","smoking","tobacco","vape","vaping","vapor","warning","zone"]},{"name":"watch_off","tags":["Android","OS","ar","clock","close","gadget","iOS","off","shut","time","vr","watch","wearables","web","wristwatch"]},{"name":"9k","tags":["9000","9K","alphabet","character","digit","display","font","letter","number","pixel","pixels","resolution","symbol","text","type","video"]},{"name":"23mp","tags":["23","camera","digits","font","image","letters","megapixel","megapixels","mp","numbers","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"propane","tags":["gas","nest","propane"]},{"name":"raw_off","tags":["alphabet","character","disabled","enabled","font","image","letter","off","on","original","photo","photography","raw","slash","symbol","text","type"]},{"name":"keyboard_option_key","tags":["alt key","key","keyboard","modifier key","option"]},{"name":"woman_2","tags":["female","gender","girl","lady","social","symbol","woman","women"]},{"name":"2k_plus","tags":["+","2k","alphabet","character","digit","font","letter","number","plus","symbol","text","type"]},{"name":"6k_plus","tags":["+","6000","6K","alphabet","character","digit","display","font","letter","number","pixel","pixels","plus","resolution","symbol","text","type","video"]},{"name":"broadcast_on_personal","tags":[]},{"name":"10mp","tags":["10","camera","digits","font","image","letters","megapixel","megapixels","mp","numbers","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"man_2","tags":["boy","gender","male","man","social","symbol"]},{"name":"7k","tags":["7000","7K","alphabet","character","digit","display","font","letter","number","pixel","pixels","resolution","symbol","text","type","video"]},{"name":"7k_plus","tags":["+","7000","7K","alphabet","character","digit","display","font","letter","number","pixel","pixels","plus","resolution","symbol","text","type","video"]},{"name":"nearby_off","tags":["disabled","enabled","nearby","off","on","slash"]},{"name":"3k_plus","tags":["+","3000","3K","alphabet","character","digit","display","font","letter","number","pixel","pixels","plus","resolution","symbol","text","type","video"]},{"name":"6k","tags":["6000","6K","alphabet","character","digit","display","font","letter","number","pixel","pixels","resolution","symbol","text","type","video"]},{"name":"hdr_off","tags":["alphabet","character","disabled","dynamic","enabled","enhance","font","hdr","high","letter","off","on","range","select","slash","symbol","text","type"]},{"name":"roundabout_left","tags":["arrow","arrows","direction","directions","left","maps","navigation","path","roundabout","route","sign","traffic"]},{"name":"hdr_off_select","tags":["alphabet","camera","character","circle","disabled","dynamic","enabled","font","hdr","high","letter","off","on","photo","range","select","slash","symbol","text","type"]},{"name":"bedtime_off","tags":["bedtime","disabled","lunar","moon","night","nightime","off","offline","slash","sleep"]},{"name":"18_up_rating","tags":[]},{"name":"turn_sharp_left","tags":["arrow","arrows","direction","directions","left","maps","navigation","path","route","sharp","sign","traffic","turn"]},{"name":"11mp","tags":["11","camera","digits","font","image","letters","megapixel","megapixels","mp","numbers","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"roller_shades_closed","tags":["blinds","closed","cover","curtains","nest","roller","shade","shutter","sunshade"]},{"name":"20mp","tags":["20","camera","digits","font","image","letters","megapixel","megapixels","mp","numbers","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"blinds","tags":["blinds","cover","curtains","nest","open","shade","shutter","sunshade"]},{"name":"3mp","tags":["3","camera","digit","font","image","letters","megapixel","megapixels","mp","number","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"blind","tags":["accessibility","accessible","assist","blind","body","cane","disability","handicap","help","human","mobility","person","walk","walker"]},{"name":"emergency_recording","tags":["alert","attention","camera","caution","danger","emergency","film","filming","hardware","image","important","motion","notification","picture","record","video","videography","warning"]},{"name":"curtains","tags":["blinds","cover","curtains","nest","open","shade","shutter","sunshade"]},{"name":"13mp","tags":["13","camera","digits","font","image","letters","megapixel","megapixels","mp","numbers","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"5mp","tags":["5","camera","digit","font","image","letters","megapixel","megapixels","mp","number","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"21mp","tags":["21","camera","digits","font","image","letters","megapixel","megapixels","mp","numbers","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"blinds_closed","tags":["blinds","closed","cover","curtains","nest","shade","shutter","sunshade"]},{"name":"16mp","tags":["16","camera","digits","font","image","letters","megapixel","megapixels","mp","numbers","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"17mp","tags":["17","camera","digits","font","image","letters","megapixel","megapixels","mp","numbers","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"2mp","tags":["2","camera","digit","font","image","letters","megapixel","megapixels","mp","number","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"15mp","tags":["15","camera","digits","font","image","letters","megapixel","megapixels","mp","numbers","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"desk","tags":[]},{"name":"no_adult_content","tags":[]},{"name":"14mp","tags":["14","camera","digits","font","image","letters","megapixel","megapixels","mp","numbers","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"22mp","tags":["22","camera","digits","font","image","letters","megapixel","megapixels","mp","numbers","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"vertical_shades","tags":["blinds","cover","curtains","nest","open","shade","shutter","sunshade","vertical"]},{"name":"vertical_shades_closed","tags":["blinds","closed","cover","curtains","nest","roller","shade","shutter","sunshade"]},{"name":"curtains_closed","tags":["blinds","closed","cover","curtains","nest","shade","shutter","sunshade"]},{"name":"broadcast_on_home","tags":[]},{"name":"4mp","tags":["4","camera","digit","font","image","letters","megapixel","megapixels","mp","number","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"19mp","tags":["19","camera","digits","font","image","letters","megapixel","megapixels","mp","numbers","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"nest_cam_wired_stand","tags":["camera","film","filming","hardware","image","motion","nest","picture","stand","video","videography","wired"]},{"name":"9mp","tags":["9","camera","digit","font","image","letters","megapixel","megapixels","mp","number","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"7mp","tags":["7","camera","digit","font","image","letters","megapixel","megapixels","mp","number","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"8mp","tags":["8","camera","digit","font","image","letters","megapixel","megapixels","mp","number","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"6mp","tags":["6","camera","digit","font","image","letters","megapixel","megapixels","mp","number","pixel","pixels","quality","resolution","symbol","text","type"]},{"name":"devices_fold","tags":["Android","OS","cell","device","fold","foldable","hardware","iOS","mobile","phone","tablet"]},{"name":"vape_free","tags":["disabled","e-cigarette","enabled","free","never","no","off","on","places","prohibited","slash","smoke","smoking","tobacco","vape","vaping","vapor","warning","zone"]},{"name":"ramp_left","tags":["arrow","arrows","direction","directions","left","maps","navigation","path","ramp","route","sign","traffic"]},{"name":"ramp_right","tags":["arrow","arrows","direction","directions","maps","navigation","path","ramp","right","route","sign","traffic"]},{"name":"video_chat","tags":["bubble","cam","camera","chat","comment","communicate","facetime","feedback","message","speech","video","voice"]},{"name":"type_specimen","tags":[]},{"name":"man_4","tags":["abstract","boy","gender","male","man","social","symbol"]},{"name":"fluorescent","tags":["bright","fluorescent","lamp","light","lightbulb"]},{"name":"man_3","tags":["abstract","boy","gender","male","man","social","symbol"]},{"name":"fire_hydrant_alt","tags":[]},{"name":"macro_off","tags":["camera","disabled","enabled","flower","garden","image","macro","off","offline","on","slash"]},{"name":"mdi:ab-testing","tags":["developer / languages"]},{"name":"mdi:abacus","tags":["math"]},{"name":"mdi:abjad-arabic","tags":["alpha / numeric","writing system arabic"]},{"name":"mdi:abjad-hebrew","tags":["alpha / numeric","writing system hebrew"]},{"name":"mdi:abugida-devanagari","tags":["alpha / numeric","writing system devanagari"]},{"name":"mdi:abugida-thai","tags":["alpha / numeric","writing system thai"]},{"name":"mdi:access-point","tags":["wireless"]},{"name":"mdi:access-point-check","tags":["access point success","access point tick"]},{"name":"mdi:access-point-minus","tags":[]},{"name":"mdi:access-point-network","tags":[]},{"name":"mdi:access-point-network-off","tags":[]},{"name":"mdi:access-point-off","tags":[]},{"name":"mdi:access-point-plus","tags":[]},{"name":"mdi:access-point-remove","tags":[]},{"name":"mdi:account-alert","tags":["account / user","alert / error","user alert","account warning","user warning","person alert","person warning"]},{"name":"mdi:account-alert-outline","tags":["account / user","alert / error","user alert outline","account warning outline","user warning outline","person warning outline","person alert outline"]},{"name":"mdi:account-arrow-down","tags":["account / user","account download"]},{"name":"mdi:account-arrow-down-outline","tags":["account / user","account download outline"]},{"name":"mdi:account-arrow-left","tags":["account / user","user arrow left","person arrow left"]},{"name":"mdi:account-arrow-left-outline","tags":["account / user","user arrow left outline","person arrow left outline"]},{"name":"mdi:account-arrow-right","tags":["account / user","user arrow right","person arrow right"]},{"name":"mdi:account-arrow-right-outline","tags":["account / user","user arrow right outline","person arrow right outline"]},{"name":"mdi:account-arrow-up","tags":["account / user","account upload"]},{"name":"mdi:account-arrow-up-outline","tags":["account / user","account upload outline"]},{"name":"mdi:account-badge","tags":["account / user","account online","user online"]},{"name":"mdi:account-badge-outline","tags":["account / user","user online outline","account online outline"]},{"name":"mdi:account-box-multiple-outline","tags":["account / user"]},{"name":"mdi:account-cancel","tags":["account / user","user cancel","user block","person cancel","person block"]},{"name":"mdi:account-cancel-outline","tags":["account / user","user cancel outline","user block outline","person cancel outline","person block outline"]},{"name":"mdi:account-card","tags":["account / user"]},{"name":"mdi:account-card-outline","tags":["account / user"]},{"name":"mdi:account-cash","tags":["account / user","banking","currency"]},{"name":"mdi:account-cash-outline","tags":["account / user","banking","currency"]},{"name":"mdi:account-child-outline","tags":["account / user"]},{"name":"mdi:account-clock","tags":["account / user","date / time","user clock","account pending","person clock"]},{"name":"mdi:account-clock-outline","tags":["account / user","date / time","user clock outline","account pending outline","person clock outline"]},{"name":"mdi:account-cog","tags":["account / user","settings","account settings"]},{"name":"mdi:account-cog-outline","tags":["account / user","settings","account settings outline"]},{"name":"mdi:account-convert","tags":["account / user","user convert","person convert"]},{"name":"mdi:account-convert-outline","tags":["account / user"]},{"name":"mdi:account-cowboy-hat","tags":["account / user","agriculture","rancher"]},{"name":"mdi:account-cowboy-hat-outline","tags":["account / user","agriculture","rancher outline"]},{"name":"mdi:account-credit-card","tags":["account / user","banking","account payment","cardholder"]},{"name":"mdi:account-credit-card-outline","tags":["account / user","banking","account payment outline","cardholder outline"]},{"name":"mdi:account-details-outline","tags":["account / user","settings","person details outline","user details outline"]},{"name":"mdi:account-edit","tags":["account / user","edit / modify","user edit","person edit"]},{"name":"mdi:account-edit-outline","tags":["account / user","edit / modify"]},{"name":"mdi:account-eye","tags":["account / user","account view"]},{"name":"mdi:account-eye-outline","tags":["account / user","account view outline"]},{"name":"mdi:account-filter","tags":["account / user","account funnel","leads"]},{"name":"mdi:account-filter-outline","tags":["account / user","account funnel outline","leads outline"]},{"name":"mdi:account-group","tags":["account / user","home automation","user group","users group","person group","people group","accounts group"]},{"name":"mdi:account-group-outline","tags":["account / user","user group outline","users group outline","person group outline","people group outline","accounts group outline"]},{"name":"mdi:account-hard-hat","tags":["account / user","worker","construction"]},{"name":"mdi:account-hard-hat-outline","tags":["account / user","worker outline","construction outline"]},{"name":"mdi:account-heart","tags":["account / user","medical / hospital","user heart","person heart"]},{"name":"mdi:account-heart-outline","tags":["account / user","medical / hospital","user heart outline","person heart outline"]},{"name":"mdi:account-key","tags":["account / user","user key","person key"]},{"name":"mdi:account-key-outline","tags":["account / user","user key outline","person key outline"]},{"name":"mdi:account-lock","tags":["account / user","lock","account security","account secure","user lock","person lock"]},{"name":"mdi:account-lock-open","tags":["account / user","lock","account unlocked","user unlocked","user lock open"]},{"name":"mdi:account-lock-open-outline","tags":["account / user","lock","user lock open outline","user unlocked outline","account unlocked outline"]},{"name":"mdi:account-lock-outline","tags":["account / user","lock","account security outline","account secure outline","person lock outline","user lock outline"]},{"name":"mdi:account-minus","tags":["account / user","user minus","person minus"]},{"name":"mdi:account-minus-outline","tags":["account / user","user minus outline","person minus outline"]},{"name":"mdi:account-multiple-check","tags":["account / user","user multiple check","account multiple tick","accounts check","accounts tick","users check","users tick","user multiple tick","person multiple check","person multiple tick","people check","people tick","account multiple success"]},{"name":"mdi:account-multiple-check-outline","tags":["account / user","user multiple check outline","account multiple tick outline","accounts check outline","accounts tick outline","users check outline","users tick outline","user multiple tick outline","person multiple check outline","person multiple tick outline","people check outline","people tick outline","account multiple success outline"]},{"name":"mdi:account-multiple-remove","tags":["account / user","user multiple remove","person multiple remove"]},{"name":"mdi:account-multiple-remove-outline","tags":["account / user","user multiple remove outline","person multiple remove outline"]},{"name":"mdi:account-music-outline","tags":["account / user","artist outline"]},{"name":"mdi:account-network","tags":["account / user","user network","person network"]},{"name":"mdi:account-network-off","tags":["account / user"]},{"name":"mdi:account-network-off-outline","tags":["account / user"]},{"name":"mdi:account-network-outline","tags":["account / user","user network outline","person network outline"]},{"name":"mdi:account-off","tags":["account / user","user off","person off"]},{"name":"mdi:account-off-outline","tags":["account / user","user off outline","person off outline"]},{"name":"mdi:account-plus-outline","tags":["account / user","person add outline","register outline","user plus outline","account add outline","person plus outline","user add outline","invite"]},{"name":"mdi:account-question","tags":["account / user","user help","account question mark","account help","user question","person question","person help"]},{"name":"mdi:account-question-outline","tags":["account / user","account question mark outline","user help outline","account help outline","user question outline","person question outline","person help outline"]},{"name":"mdi:account-reactivate","tags":["account / user"]},{"name":"mdi:account-reactivate-outline","tags":["account / user"]},{"name":"mdi:account-remove","tags":["account / user","user remove","person remove"]},{"name":"mdi:account-remove-outline","tags":["account / user","user remove outline","person remove outline"]},{"name":"mdi:account-school","tags":["account / user","account student","account graduation"]},{"name":"mdi:account-school-outline","tags":["account / user","account student outline","account graduation outline"]},{"name":"mdi:account-search","tags":["account / user","user search","person search"]},{"name":"mdi:account-search-outline","tags":["account / user","user search outline","person search outline"]},{"name":"mdi:account-settings","tags":["account / user","settings","user settings","person settings"]},{"name":"mdi:account-settings-outline","tags":["account / user","settings"]},{"name":"mdi:account-star","tags":["account / user","user star","person star","account favorite"]},{"name":"mdi:account-star-outline","tags":["account / user","user star outline","person star outline"]},{"name":"mdi:account-supervisor-outline","tags":["account / user"]},{"name":"mdi:account-switch","tags":["account / user","user switch","accounts switch","users switch","person switch","people switch"]},{"name":"mdi:account-switch-outline","tags":["account / user"]},{"name":"mdi:account-sync","tags":["account / user","account cache"]},{"name":"mdi:account-sync-outline","tags":["account / user","account cache outline"]},{"name":"mdi:account-tag","tags":["account / user"]},{"name":"mdi:account-tag-outline","tags":["account / user"]},{"name":"mdi:account-tie","tags":["account / user","people / family","person tie","user tie"]},{"name":"mdi:account-tie-hat","tags":["account / user","transportation + flying","account pilot"]},{"name":"mdi:account-tie-hat-outline","tags":["account / user","transportation + flying","account pilot outline"]},{"name":"mdi:account-tie-outline","tags":["account / user"]},{"name":"mdi:account-tie-voice","tags":["account / user"]},{"name":"mdi:account-tie-voice-off","tags":["account / user"]},{"name":"mdi:account-tie-voice-off-outline","tags":["account / user"]},{"name":"mdi:account-tie-voice-outline","tags":["account / user"]},{"name":"mdi:account-tie-woman","tags":["account / user","people / family","business woman"]},{"name":"mdi:account-wrench","tags":["account / user","account service"]},{"name":"mdi:account-wrench-outline","tags":["account / user","account service outline"]},{"name":"mdi:advertisements","tags":["ads"]},{"name":"mdi:advertisements-off","tags":["ads off"]},{"name":"mdi:air-conditioner","tags":["home automation","automotive","ac unit"]},{"name":"mdi:air-filter","tags":["home automation","water filter","filter"]},{"name":"mdi:air-horn","tags":[]},{"name":"mdi:air-humidifier","tags":["home automation"]},{"name":"mdi:air-humidifier-off","tags":["home automation","air dehumidifier"]},{"name":"mdi:air-purifier-off","tags":["home automation"]},{"name":"mdi:airbag","tags":["automotive"]},{"name":"mdi:airballoon","tags":["transportation + other","transportation + flying","hot air balloon"]},{"name":"mdi:airballoon-outline","tags":["transportation + flying","hot air balloon outline"]},{"name":"mdi:airplane","tags":["transportation + flying","navigation","aeroplane","airplanemode active","flight","local airport","flight mode","plane"]},{"name":"mdi:airplane-alert","tags":["transportation + flying","alert / error"]},{"name":"mdi:airplane-check","tags":["transportation + flying","airplace success","airplane tick"]},{"name":"mdi:airplane-clock","tags":["transportation + flying","date / time","airplane schedule","airplane time","airplane date"]},{"name":"mdi:airplane-cog","tags":["transportation + flying","settings","airplane settings"]},{"name":"mdi:airplane-edit","tags":["transportation + flying","edit / modify"]},{"name":"mdi:airplane-marker","tags":["transportation + flying","navigation","airplane location","airplane gps"]},{"name":"mdi:airplane-minus","tags":["transportation + flying"]},{"name":"mdi:airplane-off","tags":["transportation + flying","aeroplane off","airplanemode inactive","flight mode off","plane off"]},{"name":"mdi:airplane-plus","tags":["transportation + flying"]},{"name":"mdi:airplane-remove","tags":["transportation + flying"]},{"name":"mdi:airplane-search","tags":["transportation + flying","airplane find"]},{"name":"mdi:airplane-settings","tags":["transportation + flying","settings"]},{"name":"mdi:airport","tags":["places","transportation + flying"]},{"name":"mdi:alarm-bell","tags":["notification"]},{"name":"mdi:alarm-light","tags":["home automation"]},{"name":"mdi:alarm-light-off","tags":["home automation"]},{"name":"mdi:alarm-light-off-outline","tags":["home automation"]},{"name":"mdi:alarm-light-outline","tags":["home automation"]},{"name":"mdi:alarm-multiple","tags":["date / time","alarms","alarm clock multiple","alarm clocks"]},{"name":"mdi:alarm-note","tags":[]},{"name":"mdi:alarm-note-off","tags":[]},{"name":"mdi:alarm-panel","tags":["home automation"]},{"name":"mdi:alarm-panel-outline","tags":["home automation"]},{"name":"mdi:alert-box","tags":["alert / error","warning box"]},{"name":"mdi:alert-box-outline","tags":["alert / error","warning box outline"]},{"name":"mdi:alert-circle-check","tags":["alert / error","alert circle success"]},{"name":"mdi:alert-circle-check-outline","tags":["alert / error","alert circle success outline"]},{"name":"mdi:alert-decagram-outline","tags":["alert / error","warning decagram outline"]},{"name":"mdi:alert-minus","tags":["alert / error"]},{"name":"mdi:alert-minus-outline","tags":["alert / error"]},{"name":"mdi:alert-octagon-outline","tags":["alert / error","warning octagon outline","stop alert outline"]},{"name":"mdi:alert-octagram","tags":["alert / error","warning octagram"]},{"name":"mdi:alert-octagram-outline","tags":["alert / error","warning octagram outline"]},{"name":"mdi:alert-outline","tags":["alert / error","warning outline"]},{"name":"mdi:alert-plus","tags":["alert / error"]},{"name":"mdi:alert-plus-outline","tags":["alert / error"]},{"name":"mdi:alert-remove","tags":["alert / error"]},{"name":"mdi:alert-remove-outline","tags":["alert / error"]},{"name":"mdi:alert-rhombus","tags":["alert / error"]},{"name":"mdi:alert-rhombus-outline","tags":["alert / error"]},{"name":"mdi:alien","tags":[]},{"name":"mdi:alien-outline","tags":[]},{"name":"mdi:all-inclusive-box","tags":["infinity box","forever box"]},{"name":"mdi:all-inclusive-box-outline","tags":["forever box outline","infinity box outline"]},{"name":"mdi:allergy","tags":["medical / hospital","hand","rash","germ"]},{"name":"mdi:alpha","tags":["alpha / numeric"]},{"name":"mdi:alpha-a","tags":["alpha / numeric","alphabet a","letter a"]},{"name":"mdi:alpha-a-box","tags":["alpha / numeric","alphabet a box","letter a box"]},{"name":"mdi:alpha-a-box-outline","tags":["alpha / numeric","alphabet a box outline","letter a box outline"]},{"name":"mdi:alpha-a-circle","tags":["alpha / numeric","alphabet a circle","letter a circle"]},{"name":"mdi:alpha-a-circle-outline","tags":["alpha / numeric","alphabet a circle outline","letter a circle outline"]},{"name":"mdi:alpha-b","tags":["alpha / numeric","alphabet b","letter b"]},{"name":"mdi:alpha-b-box","tags":["alpha / numeric","alphabet b box","letter b box"]},{"name":"mdi:alpha-b-box-outline","tags":["alpha / numeric","alphabet b box outline","letter b box outline"]},{"name":"mdi:alpha-b-circle","tags":["alpha / numeric","alphabet b circle","letter b circle"]},{"name":"mdi:alpha-b-circle-outline","tags":["alpha / numeric","alphabet b circle outline","letter b circle outline"]},{"name":"mdi:alpha-c","tags":["alpha / numeric","alphabet c","letter c"]},{"name":"mdi:alpha-c-box","tags":["alpha / numeric","alphabet c box","letter c box"]},{"name":"mdi:alpha-c-box-outline","tags":["alpha / numeric","alphabet c box outline","letter c box outline"]},{"name":"mdi:alpha-c-circle","tags":["alpha / numeric","alphabet c circle","letter c circle"]},{"name":"mdi:alpha-c-circle-outline","tags":["alpha / numeric","alphabet c circle outline","letter c circle outline"]},{"name":"mdi:alpha-d","tags":["automotive","alpha / numeric","alphabet d","letter d","drive"]},{"name":"mdi:alpha-d-box","tags":["alpha / numeric","alphabet d box","letter d box"]},{"name":"mdi:alpha-d-box-outline","tags":["alpha / numeric","alphabet d box outline","letter d box outline"]},{"name":"mdi:alpha-d-circle","tags":["alpha / numeric","alphabet d circle","letter d circle"]},{"name":"mdi:alpha-d-circle-outline","tags":["alpha / numeric","alphabet d circle outline","letter d circle outline"]},{"name":"mdi:alpha-e","tags":["alpha / numeric","alphabet e","letter e"]},{"name":"mdi:alpha-e-box","tags":["alpha / numeric","alphabet e box","letter e box"]},{"name":"mdi:alpha-e-box-outline","tags":["alpha / numeric","alphabet e box outline","letter e box outline"]},{"name":"mdi:alpha-e-circle","tags":["alpha / numeric","alphabet e circle","letter e circle"]},{"name":"mdi:alpha-e-circle-outline","tags":["alpha / numeric","alphabet e circle outline","letter e circle outline"]},{"name":"mdi:alpha-f","tags":["alpha / numeric","alphabet f","letter f"]},{"name":"mdi:alpha-f-box","tags":["alpha / numeric","alphabet f box","letter f box"]},{"name":"mdi:alpha-f-box-outline","tags":["alpha / numeric","alphabet f box outline","letter f box outline"]},{"name":"mdi:alpha-f-circle","tags":["alpha / numeric","alphabet f circle","letter f circle"]},{"name":"mdi:alpha-f-circle-outline","tags":["alpha / numeric","alphabet f circle outline","letter f circle outline"]},{"name":"mdi:alpha-g","tags":["alpha / numeric","alphabet g","letter g"]},{"name":"mdi:alpha-g-box","tags":["alpha / numeric","alphabet g box","letter g box"]},{"name":"mdi:alpha-g-box-outline","tags":["alpha / numeric","alphabet g box outline","letter g box outline"]},{"name":"mdi:alpha-g-circle","tags":["alpha / numeric","alphabet g circle","letter g circle"]},{"name":"mdi:alpha-g-circle-outline","tags":["alpha / numeric","alphabet g circle outline","letter g circle outline"]},{"name":"mdi:alpha-h","tags":["alpha / numeric","alphabet h","letter h"]},{"name":"mdi:alpha-h-box","tags":["alpha / numeric","alphabet h box","letter h box"]},{"name":"mdi:alpha-h-box-outline","tags":["alpha / numeric","alphabet h box outline","letter h box outline"]},{"name":"mdi:alpha-h-circle","tags":["alpha / numeric","alphabet h circle","letter h circle"]},{"name":"mdi:alpha-h-circle-outline","tags":["alpha / numeric","alphabet h circle outline","letter h circle outline","helipad"]},{"name":"mdi:alpha-i","tags":["alpha / numeric","alphabet i","letter i","roman numeral 1"]},{"name":"mdi:alpha-i-box","tags":["alpha / numeric","alphabet i box","letter i box"]},{"name":"mdi:alpha-i-box-outline","tags":["alpha / numeric","alphabet i box outline","letter i box outline"]},{"name":"mdi:alpha-i-circle","tags":["alpha / numeric","alphabet i circle","letter i circle"]},{"name":"mdi:alpha-i-circle-outline","tags":["alpha / numeric","alphabet i circle outline","letter i circle outline"]},{"name":"mdi:alpha-j","tags":["alpha / numeric","alphabet j","letter j"]},{"name":"mdi:alpha-j-box","tags":["alpha / numeric","alphabet j box","letter j box"]},{"name":"mdi:alpha-j-box-outline","tags":["alpha / numeric","alphabet j box outline","letter j box outline"]},{"name":"mdi:alpha-j-circle","tags":["alpha / numeric","alphabet j circle","letter j circle"]},{"name":"mdi:alpha-j-circle-outline","tags":["alpha / numeric","alphabet j circle outline","letter j circle outline"]},{"name":"mdi:alpha-k","tags":["alpha / numeric","alphabet k","letter k"]},{"name":"mdi:alpha-k-box","tags":["alpha / numeric","alphabet k box","letter k box"]},{"name":"mdi:alpha-k-box-outline","tags":["alpha / numeric","alphabet k box outline","letter k box outline"]},{"name":"mdi:alpha-k-circle","tags":["alpha / numeric","alphabet k circle","letter k circle"]},{"name":"mdi:alpha-k-circle-outline","tags":["alpha / numeric","alphabet k circle outline","letter k circle outline"]},{"name":"mdi:alpha-l","tags":["alpha / numeric","alphabet l","letter l"]},{"name":"mdi:alpha-l-box","tags":["alpha / numeric","alphabet l box","letter l box"]},{"name":"mdi:alpha-l-box-outline","tags":["alpha / numeric","alphabet l box outline","letter l box outline"]},{"name":"mdi:alpha-l-circle","tags":["alpha / numeric","alphabet l circle","letter l circle"]},{"name":"mdi:alpha-l-circle-outline","tags":["alpha / numeric","alphabet l circle outline","letter l circle outline"]},{"name":"mdi:alpha-m","tags":["alpha / numeric","alphabet m","letter m"]},{"name":"mdi:alpha-m-box","tags":["alpha / numeric","alphabet m box","letter m box"]},{"name":"mdi:alpha-m-box-outline","tags":["alpha / numeric","alphabet m box outline","letter m box outline"]},{"name":"mdi:alpha-m-circle","tags":["alpha / numeric","alphabet m circle","letter m circle"]},{"name":"mdi:alpha-m-circle-outline","tags":["alpha / numeric","alphabet m circle outline","letter m circle outline"]},{"name":"mdi:alpha-n","tags":["automotive","alpha / numeric","alphabet n","letter n","neutral"]},{"name":"mdi:alpha-n-box","tags":["alpha / numeric","alphabet n box","letter n box"]},{"name":"mdi:alpha-n-box-outline","tags":["alpha / numeric","alphabet n box outline","letter n box outline"]},{"name":"mdi:alpha-n-circle","tags":["alpha / numeric","alphabet n circle","letter n circle"]},{"name":"mdi:alpha-n-circle-outline","tags":["alpha / numeric","alphabet n circle outline","letter n circle outline"]},{"name":"mdi:alpha-o","tags":["alpha / numeric","alphabet o","letter o"]},{"name":"mdi:alpha-o-box","tags":["alpha / numeric","alphabet o box","letter o box"]},{"name":"mdi:alpha-o-box-outline","tags":["alpha / numeric","alphabet o box outline","letter o box outline"]},{"name":"mdi:alpha-o-circle","tags":["alpha / numeric","alphabet o circle","letter o circle"]},{"name":"mdi:alpha-o-circle-outline","tags":["alpha / numeric","alphabet o circle outline","letter o circle outline"]},{"name":"mdi:alpha-p","tags":["automotive","alpha / numeric","alphabet p","letter p","park"]},{"name":"mdi:alpha-p-box","tags":["alpha / numeric","alphabet p box","letter p box"]},{"name":"mdi:alpha-p-box-outline","tags":["alpha / numeric","alphabet p box outline","letter p box outline"]},{"name":"mdi:alpha-p-circle","tags":["alpha / numeric","alphabet p circle","letter p circle"]},{"name":"mdi:alpha-p-circle-outline","tags":["alpha / numeric","alphabet p circle outline","letter p circle outline"]},{"name":"mdi:alpha-q","tags":["alpha / numeric","alphabet q","letter q"]},{"name":"mdi:alpha-q-box","tags":["alpha / numeric","alphabet q box","letter q box"]},{"name":"mdi:alpha-q-box-outline","tags":["alpha / numeric","alphabet q box outline","letter q box outline"]},{"name":"mdi:alpha-q-circle","tags":["alpha / numeric","alphabet q circle","letter q circle"]},{"name":"mdi:alpha-q-circle-outline","tags":["alpha / numeric","alphabet q circle outline","letter q circle outline"]},{"name":"mdi:alpha-r","tags":["automotive","alpha / numeric","alphabet r","letter r","reverse"]},{"name":"mdi:alpha-r-box","tags":["alpha / numeric","alphabet r box","letter r box"]},{"name":"mdi:alpha-r-box-outline","tags":["alpha / numeric","alphabet r box outline","letter r box outline"]},{"name":"mdi:alpha-r-circle","tags":["alpha / numeric","alphabet r circle","letter r circle"]},{"name":"mdi:alpha-r-circle-outline","tags":["alpha / numeric","alphabet r circle outline","letter r circle outline"]},{"name":"mdi:alpha-s","tags":["alpha / numeric","alphabet s","letter s"]},{"name":"mdi:alpha-s-box","tags":["alpha / numeric","alphabet s box","letter s box"]},{"name":"mdi:alpha-s-box-outline","tags":["alpha / numeric","alphabet s box outline","letter s box outline"]},{"name":"mdi:alpha-s-circle","tags":["alpha / numeric","alphabet s circle","letter s circle"]},{"name":"mdi:alpha-s-circle-outline","tags":["alpha / numeric","alphabet s circle outline","letter s circle outline"]},{"name":"mdi:alpha-t","tags":["alpha / numeric","alphabet t","letter t"]},{"name":"mdi:alpha-t-box","tags":["alpha / numeric","alphabet t box","letter t box"]},{"name":"mdi:alpha-t-box-outline","tags":["alpha / numeric","alphabet t box outline","letter t box outline"]},{"name":"mdi:alpha-t-circle","tags":["alpha / numeric","alphabet t circle","letter t circle"]},{"name":"mdi:alpha-t-circle-outline","tags":["alpha / numeric","alphabet t circle outline","letter t circle outline"]},{"name":"mdi:alpha-u","tags":["alpha / numeric","alphabet u","letter u"]},{"name":"mdi:alpha-u-box","tags":["alpha / numeric","alphabet u box","letter u box"]},{"name":"mdi:alpha-u-box-outline","tags":["alpha / numeric","alphabet u box outline","letter u box outline"]},{"name":"mdi:alpha-u-circle","tags":["alpha / numeric","alphabet u circle","letter u circle"]},{"name":"mdi:alpha-u-circle-outline","tags":["alpha / numeric","alphabet u circle outline","letter u circle outline"]},{"name":"mdi:alpha-v","tags":["alpha / numeric","alphabet v","letter v","roman numeral 5"]},{"name":"mdi:alpha-v-box","tags":["alpha / numeric","alphabet v box","letter v box"]},{"name":"mdi:alpha-v-box-outline","tags":["alpha / numeric","alphabet v box outline","letter v box outline"]},{"name":"mdi:alpha-v-circle","tags":["alpha / numeric","alphabet v circle","letter v circle"]},{"name":"mdi:alpha-v-circle-outline","tags":["alpha / numeric","alphabet v circle outline","letter v circle outline"]},{"name":"mdi:alpha-w","tags":["alpha / numeric","alphabet w","letter w"]},{"name":"mdi:alpha-w-box","tags":["alpha / numeric","alphabet w box","letter w box"]},{"name":"mdi:alpha-w-box-outline","tags":["alpha / numeric","alphabet w box outline","letter w box outline"]},{"name":"mdi:alpha-w-circle","tags":["alpha / numeric","alphabet w circle","letter w circle"]},{"name":"mdi:alpha-w-circle-outline","tags":["alpha / numeric","alphabet w circle outline","letter w circle outline"]},{"name":"mdi:alpha-x","tags":["alpha / numeric","alphabet x","letter x","roman numeral 10"]},{"name":"mdi:alpha-x-box","tags":["alpha / numeric","alphabet x box","letter x box"]},{"name":"mdi:alpha-x-box-outline","tags":["alpha / numeric","alphabet x box outline","letter x box outline"]},{"name":"mdi:alpha-x-circle","tags":["alpha / numeric","alphabet x circle","letter x circle"]},{"name":"mdi:alpha-x-circle-outline","tags":["alpha / numeric","alphabet x circle outline","letter x circle outline"]},{"name":"mdi:alpha-y","tags":["alpha / numeric","alphabet y","letter y"]},{"name":"mdi:alpha-y-box","tags":["alpha / numeric","alphabet y box","letter y box"]},{"name":"mdi:alpha-y-box-outline","tags":["alpha / numeric","alphabet y box outline","letter y box outline"]},{"name":"mdi:alpha-y-circle","tags":["alpha / numeric","alphabet y circle","letter y circle"]},{"name":"mdi:alpha-y-circle-outline","tags":["alpha / numeric","alphabet y circle outline","letter y circle outline"]},{"name":"mdi:alpha-z","tags":["alpha / numeric","alphabet z","letter z"]},{"name":"mdi:alpha-z-box","tags":["alpha / numeric","alphabet z box","letter z box"]},{"name":"mdi:alpha-z-box-outline","tags":["alpha / numeric","alphabet z box outline","letter z box outline"]},{"name":"mdi:alpha-z-circle","tags":["alpha / numeric","alphabet z circle","letter z circle"]},{"name":"mdi:alpha-z-circle-outline","tags":["alpha / numeric","alphabet z circle outline","letter z circle outline"]},{"name":"mdi:alphabet-aurebesh","tags":["alpha / numeric","writing system aurebesh"]},{"name":"mdi:alphabet-cyrillic","tags":["alpha / numeric","writing system cyrillic"]},{"name":"mdi:alphabet-greek","tags":["alpha / numeric","writing system greek"]},{"name":"mdi:alphabet-latin","tags":["alpha / numeric","writing system latin"]},{"name":"mdi:alphabet-piqad","tags":["alpha / numeric","writing system piqad"]},{"name":"mdi:alphabet-tengwar","tags":["alpha / numeric","writing system tengwar"]},{"name":"mdi:alphabetical","tags":["alpha / numeric","letters","a b c","abc"]},{"name":"mdi:alphabetical-off","tags":["alpha / numeric","letters off","abc off","a b c off"]},{"name":"mdi:alphabetical-variant","tags":["alpha / numeric","letters","abc","a b c"]},{"name":"mdi:alphabetical-variant-off","tags":["alpha / numeric","letters off","abc off","a b c off"]},{"name":"mdi:altimeter","tags":[]},{"name":"mdi:ambulance","tags":["transportation + road","medical / hospital"]},{"name":"mdi:ammunition","tags":["bullets"]},{"name":"mdi:ampersand","tags":["and"]},{"name":"mdi:amplifier","tags":["home automation","music"]},{"name":"mdi:amplifier-off","tags":[]},{"name":"mdi:angle-acute","tags":["math"]},{"name":"mdi:angle-obtuse","tags":["math"]},{"name":"mdi:angle-right","tags":["math"]},{"name":"mdi:animation-outline","tags":[]},{"name":"mdi:animation-play-outline","tags":[]},{"name":"mdi:anvil","tags":[]},{"name":"mdi:api-off","tags":["developer / languages"]},{"name":"mdi:apple-keyboard-caps","tags":[]},{"name":"mdi:apple-keyboard-command","tags":[]},{"name":"mdi:apple-keyboard-control","tags":[]},{"name":"mdi:apple-keyboard-option","tags":[]},{"name":"mdi:apple-keyboard-shift","tags":[]},{"name":"mdi:application","tags":["iframe"]},{"name":"mdi:application-array","tags":["developer / languages","iframe array"]},{"name":"mdi:application-array-outline","tags":["developer / languages","iframe array outline"]},{"name":"mdi:application-braces","tags":["developer / languages","iframe braces"]},{"name":"mdi:application-braces-outline","tags":["developer / languages","iframe braces outline"]},{"name":"mdi:application-brackets","tags":["developer / languages","iframe brackets"]},{"name":"mdi:application-brackets-outline","tags":["developer / languages","iframe brackets outline"]},{"name":"mdi:application-cog","tags":["settings","iframe cog"]},{"name":"mdi:application-cog-outline","tags":["settings","application settings","iframe cog outline"]},{"name":"mdi:application-edit","tags":["edit / modify","iframe edit"]},{"name":"mdi:application-edit-outline","tags":["edit / modify","iframe edit outline"]},{"name":"mdi:application-export","tags":["iframe export outline"]},{"name":"mdi:application-import","tags":["iframe import outline"]},{"name":"mdi:application-outline","tags":["web asset","iframe outline"]},{"name":"mdi:application-parentheses","tags":["developer / languages","iframe parentheses"]},{"name":"mdi:application-parentheses-outline","tags":["developer / languages","iframe parentheses outline"]},{"name":"mdi:application-settings","tags":["settings","iframe settings"]},{"name":"mdi:application-settings-outline","tags":["settings","iframe settings outline"]},{"name":"mdi:application-variable","tags":["developer / languages","iframe variable"]},{"name":"mdi:application-variable-outline","tags":["developer / languages","iframe variable outline"]},{"name":"mdi:approximately-equal","tags":["math"]},{"name":"mdi:approximately-equal-box","tags":["math"]},{"name":"mdi:archive","tags":["box"]},{"name":"mdi:archive-alert","tags":["alert / error","box alert"]},{"name":"mdi:archive-alert-outline","tags":["alert / error","box alert outline"]},{"name":"mdi:archive-arrow-down","tags":["box arrow down","this side down"]},{"name":"mdi:archive-arrow-down-outline","tags":["box arrow down","this side down outline"]},{"name":"mdi:archive-arrow-up","tags":["box arrow up","this side up"]},{"name":"mdi:archive-arrow-up-outline","tags":["box arrow up outline","this side up outline"]},{"name":"mdi:archive-cancel","tags":["box cancel"]},{"name":"mdi:archive-cancel-outline","tags":["box cancel outline"]},{"name":"mdi:archive-check","tags":["box check","archive success","box success"]},{"name":"mdi:archive-check-outline","tags":["box check outline","archive success outline","box success outline"]},{"name":"mdi:archive-clock","tags":["date / time","box clock","box time","archive time"]},{"name":"mdi:archive-clock-outline","tags":["date / time","box clock outline","box time outline","archive time outline"]},{"name":"mdi:archive-cog","tags":["settings","box cog"]},{"name":"mdi:archive-cog-outline","tags":["settings","box cog outline"]},{"name":"mdi:archive-edit","tags":["edit / modify","box edit"]},{"name":"mdi:archive-edit-outline","tags":["edit / modify","box edit outline"]},{"name":"mdi:archive-eye","tags":["archive view","box eye","box view"]},{"name":"mdi:archive-eye-outline","tags":["archive view outline","box eye outline","box view outline"]},{"name":"mdi:archive-lock","tags":["lock","box lock"]},{"name":"mdi:archive-lock-open","tags":["lock","box lock open"]},{"name":"mdi:archive-lock-open-outline","tags":["lock","box lock open outline"]},{"name":"mdi:archive-lock-outline","tags":["lock","box lock outline"]},{"name":"mdi:archive-marker","tags":["navigation","archive location","box marker","box location"]},{"name":"mdi:archive-marker-outline","tags":["navigation","archive location outline","box marker outline","box location outline"]},{"name":"mdi:archive-minus","tags":["box minus"]},{"name":"mdi:archive-minus-outline","tags":["box minus outline"]},{"name":"mdi:archive-music","tags":["music","box music"]},{"name":"mdi:archive-music-outline","tags":["music","box music outline"]},{"name":"mdi:archive-off","tags":["box off"]},{"name":"mdi:archive-off-outline","tags":["box off outline"]},{"name":"mdi:archive-outline","tags":["box outline"]},{"name":"mdi:archive-plus","tags":["archive add","box plus","box add"]},{"name":"mdi:archive-plus-outline","tags":["archive add outline","box plus outline","box add outline"]},{"name":"mdi:archive-refresh","tags":["box refresh"]},{"name":"mdi:archive-refresh-outline","tags":["box refresh outline"]},{"name":"mdi:archive-remove","tags":["box remove"]},{"name":"mdi:archive-remove-outline","tags":["box remove outline"]},{"name":"mdi:archive-search","tags":["box search"]},{"name":"mdi:archive-search-outline","tags":["box search outline"]},{"name":"mdi:archive-settings","tags":["settings","box settings"]},{"name":"mdi:archive-settings-outline","tags":["settings","box settings outline"]},{"name":"mdi:archive-star","tags":["archive favorite","box star","box favorite"]},{"name":"mdi:archive-star-outline","tags":["archive favorite outline","box star outline","box favorite outline"]},{"name":"mdi:archive-sync","tags":["box sync"]},{"name":"mdi:archive-sync-outline","tags":["box sync outline"]},{"name":"mdi:arm-flex","tags":[]},{"name":"mdi:arm-flex-outline","tags":[]},{"name":"mdi:arrange-bring-forward","tags":["arrange","geographic information system"]},{"name":"mdi:arrange-bring-to-front","tags":["arrange","geographic information system"]},{"name":"mdi:arrange-send-backward","tags":["arrange","geographic information system"]},{"name":"mdi:arrange-send-to-back","tags":["arrange","geographic information system"]},{"name":"mdi:arrow-all","tags":["arrow"]},{"name":"mdi:arrow-bottom-left","tags":["arrow","arrow down left"]},{"name":"mdi:arrow-bottom-left-bold-box","tags":["arrow"]},{"name":"mdi:arrow-bottom-left-bold-box-outline","tags":["arrow"]},{"name":"mdi:arrow-bottom-left-bold-outline","tags":["arrow","arrow down left bold outline"]},{"name":"mdi:arrow-bottom-left-thick","tags":["arrow","arrow down left thick","arrow bottom left bold","arrow down left bold"]},{"name":"mdi:arrow-bottom-left-thin","tags":["arrow"]},{"name":"mdi:arrow-bottom-right","tags":["arrow","arrow down right"]},{"name":"mdi:arrow-bottom-right-bold-box","tags":["arrow"]},{"name":"mdi:arrow-bottom-right-bold-box-outline","tags":["arrow"]},{"name":"mdi:arrow-bottom-right-bold-outline","tags":["arrow","arrow down right bold outline"]},{"name":"mdi:arrow-bottom-right-thick","tags":["arrow","arrow down right thick","arrow bottom right bold","arrow down right bold"]},{"name":"mdi:arrow-bottom-right-thin","tags":["arrow"]},{"name":"mdi:arrow-collapse","tags":["arrow","arrow compress"]},{"name":"mdi:arrow-collapse-all","tags":["arrow","arrow compress all"]},{"name":"mdi:arrow-collapse-down","tags":["arrow","arrow compress down"]},{"name":"mdi:arrow-collapse-left","tags":["arrow","arrow compress left"]},{"name":"mdi:arrow-collapse-right","tags":["arrow","arrow compress right"]},{"name":"mdi:arrow-collapse-up","tags":["arrow","arrow compress up"]},{"name":"mdi:arrow-decision","tags":["arrow","proxy"]},{"name":"mdi:arrow-decision-auto","tags":["proxy auto"]},{"name":"mdi:arrow-decision-auto-outline","tags":["proxy auto outline"]},{"name":"mdi:arrow-decision-outline","tags":["arrow","proxy outline"]},{"name":"mdi:arrow-down","tags":["arrow","arrow downward","arrow bottom"]},{"name":"mdi:arrow-down-bold","tags":["arrow","arrow bottom bold"]},{"name":"mdi:arrow-down-bold-box","tags":["arrow","arrow bottom bold box"]},{"name":"mdi:arrow-down-bold-box-outline","tags":["arrow","arrow bottom bold box outline"]},{"name":"mdi:arrow-down-bold-circle","tags":["arrow","arrow bottom bold circle"]},{"name":"mdi:arrow-down-bold-circle-outline","tags":["arrow","arrow bottom bold circle outline"]},{"name":"mdi:arrow-down-bold-hexagon-outline","tags":["arrow","arrow bottom bold hexagon outline"]},{"name":"mdi:arrow-down-bold-outline","tags":["arrow","arrow bottom bold outline"]},{"name":"mdi:arrow-down-box","tags":["arrow","arrow bottom box"]},{"name":"mdi:arrow-down-circle","tags":["arrow","arrow bottom circle"]},{"name":"mdi:arrow-down-circle-outline","tags":["arrow","arrow bottom circle outline"]},{"name":"mdi:arrow-down-drop-circle-outline","tags":["arrow","arrow bottom drop circle outline"]},{"name":"mdi:arrow-down-left","tags":["arrow"]},{"name":"mdi:arrow-down-left-bold","tags":["arrow"]},{"name":"mdi:arrow-down-right","tags":["arrow"]},{"name":"mdi:arrow-down-right-bold","tags":["arrow"]},{"name":"mdi:arrow-down-thick","tags":["arrow","arrow bottom thick","arrow down bold","arrow bottom bold"]},{"name":"mdi:arrow-down-thin","tags":["arrow"]},{"name":"mdi:arrow-expand","tags":["arrow"]},{"name":"mdi:arrow-expand-all","tags":["arrow","geographic information system"]},{"name":"mdi:arrow-expand-down","tags":["arrow"]},{"name":"mdi:arrow-expand-left","tags":["arrow"]},{"name":"mdi:arrow-expand-right","tags":["arrow"]},{"name":"mdi:arrow-expand-up","tags":["arrow"]},{"name":"mdi:arrow-horizontal-lock","tags":["lock","arrow","scroll horizontal lock"]},{"name":"mdi:arrow-left-bold","tags":["arrow","automotive"]},{"name":"mdi:arrow-left-bold-box","tags":["arrow"]},{"name":"mdi:arrow-left-bold-box-outline","tags":["arrow"]},{"name":"mdi:arrow-left-bold-circle","tags":["arrow"]},{"name":"mdi:arrow-left-bold-circle-outline","tags":["arrow"]},{"name":"mdi:arrow-left-bold-hexagon-outline","tags":["arrow"]},{"name":"mdi:arrow-left-bold-outline","tags":["arrow","automotive"]},{"name":"mdi:arrow-left-bottom","tags":[]},{"name":"mdi:arrow-left-bottom-bold","tags":[]},{"name":"mdi:arrow-left-box","tags":["arrow"]},{"name":"mdi:arrow-left-circle","tags":["arrow","arrow back circle"]},{"name":"mdi:arrow-left-circle-outline","tags":["arrow"]},{"name":"mdi:arrow-left-drop-circle","tags":["arrow"]},{"name":"mdi:arrow-left-drop-circle-outline","tags":["arrow"]},{"name":"mdi:arrow-left-right","tags":["arrow"]},{"name":"mdi:arrow-left-right-bold","tags":["arrow"]},{"name":"mdi:arrow-left-right-bold-outline","tags":["arrow"]},{"name":"mdi:arrow-left-thick","tags":["arrow","arrow left bold"]},{"name":"mdi:arrow-left-thin","tags":["arrow"]},{"name":"mdi:arrow-left-top","tags":["turn left"]},{"name":"mdi:arrow-left-top-bold","tags":["turn left bold"]},{"name":"mdi:arrow-projectile","tags":["gaming / rpg","sport"]},{"name":"mdi:arrow-projectile-multiple","tags":["gaming / rpg","sport"]},{"name":"mdi:arrow-right-bold","tags":["arrow","automotive"]},{"name":"mdi:arrow-right-bold-box","tags":["arrow"]},{"name":"mdi:arrow-right-bold-box-outline","tags":["arrow"]},{"name":"mdi:arrow-right-bold-circle","tags":["arrow"]},{"name":"mdi:arrow-right-bold-circle-outline","tags":["arrow"]},{"name":"mdi:arrow-right-bold-hexagon-outline","tags":["arrow"]},{"name":"mdi:arrow-right-bold-outline","tags":["arrow","automotive"]},{"name":"mdi:arrow-right-bottom","tags":[]},{"name":"mdi:arrow-right-bottom-bold","tags":[]},{"name":"mdi:arrow-right-box","tags":["arrow"]},{"name":"mdi:arrow-right-circle","tags":["arrow","arrow forward circle"]},{"name":"mdi:arrow-right-circle-outline","tags":["arrow"]},{"name":"mdi:arrow-right-drop-circle","tags":["arrow"]},{"name":"mdi:arrow-right-drop-circle-outline","tags":["arrow"]},{"name":"mdi:arrow-right-thick","tags":["arrow","arrow right bold"]},{"name":"mdi:arrow-right-thin","tags":["arrow"]},{"name":"mdi:arrow-right-top","tags":["turn right"]},{"name":"mdi:arrow-right-top-bold","tags":["turn right bold"]},{"name":"mdi:arrow-split-horizontal","tags":["arrow","resize vertical","resize"]},{"name":"mdi:arrow-split-vertical","tags":["arrow","resize horizontal","resize"]},{"name":"mdi:arrow-top-left","tags":["arrow","arrow up left"]},{"name":"mdi:arrow-top-left-bold-box","tags":["arrow"]},{"name":"mdi:arrow-top-left-bold-box-outline","tags":["arrow"]},{"name":"mdi:arrow-top-left-bold-outline","tags":["arrow","arrow up left bold outline"]},{"name":"mdi:arrow-top-left-bottom-right","tags":["arrow"]},{"name":"mdi:arrow-top-left-bottom-right-bold","tags":["arrow"]},{"name":"mdi:arrow-top-left-thick","tags":["arrow","arrow up left thick","arrow top left bold","arrow up left bold"]},{"name":"mdi:arrow-top-left-thin","tags":["arrow"]},{"name":"mdi:arrow-top-right","tags":["arrow","arrow up right"]},{"name":"mdi:arrow-top-right-bold-box","tags":["arrow"]},{"name":"mdi:arrow-top-right-bold-box-outline","tags":["arrow"]},{"name":"mdi:arrow-top-right-bold-outline","tags":["arrow","arrow up right bold outline"]},{"name":"mdi:arrow-top-right-bottom-left","tags":["arrow"]},{"name":"mdi:arrow-top-right-bottom-left-bold","tags":["arrow"]},{"name":"mdi:arrow-top-right-thick","tags":["arrow","arrow up right thick","arrow top right bold","arrow up right bold"]},{"name":"mdi:arrow-top-right-thin","tags":["arrow"]},{"name":"mdi:arrow-u-down-left","tags":["u turn left"]},{"name":"mdi:arrow-u-down-left-bold","tags":["u turn left bold"]},{"name":"mdi:arrow-u-down-right","tags":["u turn right"]},{"name":"mdi:arrow-u-down-right-bold","tags":["u turn right bold"]},{"name":"mdi:arrow-u-left-bottom","tags":["undo"]},{"name":"mdi:arrow-u-left-bottom-bold","tags":["undo"]},{"name":"mdi:arrow-u-left-top","tags":["undo"]},{"name":"mdi:arrow-u-left-top-bold","tags":["undo"]},{"name":"mdi:arrow-u-right-bottom","tags":["redo"]},{"name":"mdi:arrow-u-right-bottom-bold","tags":["redo"]},{"name":"mdi:arrow-u-right-top","tags":["redo"]},{"name":"mdi:arrow-u-right-top-bold","tags":["redo"]},{"name":"mdi:arrow-u-up-left","tags":[]},{"name":"mdi:arrow-u-up-left-bold","tags":[]},{"name":"mdi:arrow-u-up-right","tags":[]},{"name":"mdi:arrow-u-up-right-bold","tags":[]},{"name":"mdi:arrow-up","tags":["arrow","arrow upward","arrow top"]},{"name":"mdi:arrow-up-bold","tags":["arrow","arrow top bold"]},{"name":"mdi:arrow-up-bold-box","tags":["arrow","arrow top bold box"]},{"name":"mdi:arrow-up-bold-box-outline","tags":["arrow","arrow top bold box outline"]},{"name":"mdi:arrow-up-bold-circle","tags":["arrow","arrow top bold circle"]},{"name":"mdi:arrow-up-bold-circle-outline","tags":["arrow","arrow top bold circle outline"]},{"name":"mdi:arrow-up-bold-hexagon-outline","tags":["arrow","arrow top bold hexagon outline"]},{"name":"mdi:arrow-up-bold-outline","tags":["arrow","arrow top bold outline"]},{"name":"mdi:arrow-up-box","tags":["arrow"]},{"name":"mdi:arrow-up-circle","tags":["arrow","arrow top circle"]},{"name":"mdi:arrow-up-circle-outline","tags":["arrow","arrow top circle outline"]},{"name":"mdi:arrow-up-down","tags":["arrow"]},{"name":"mdi:arrow-up-down-bold","tags":["arrow"]},{"name":"mdi:arrow-up-down-bold-outline","tags":["arrow"]},{"name":"mdi:arrow-up-drop-circle","tags":["arrow","arrow top drop circle"]},{"name":"mdi:arrow-up-drop-circle-outline","tags":["arrow","arrow top drop circle outline"]},{"name":"mdi:arrow-up-left","tags":[]},{"name":"mdi:arrow-up-left-bold","tags":[]},{"name":"mdi:arrow-up-right","tags":[]},{"name":"mdi:arrow-up-right-bold","tags":[]},{"name":"mdi:arrow-up-thick","tags":["arrow","arrow top thick","arrow up bold","arrow top bold"]},{"name":"mdi:arrow-up-thin","tags":["arrow"]},{"name":"mdi:arrow-vertical-lock","tags":["lock","arrow","scroll vertical lock"]},{"name":"mdi:artboard","tags":["drawing / art","canvas","frame"]},{"name":"mdi:asterisk","tags":["required"]},{"name":"mdi:asterisk-circle-outline","tags":["required circle"]},{"name":"mdi:atom","tags":["science"]},{"name":"mdi:atom-variant","tags":["science","orbit"]},{"name":"mdi:attachment-check","tags":["attachment tick","paperclip check","paperclip tick"]},{"name":"mdi:attachment-lock","tags":["lock","paperclip lock"]},{"name":"mdi:attachment-minus","tags":["paperclip minus","paperclip subtract","attachment subtract"]},{"name":"mdi:attachment-off","tags":["paperclip off"]},{"name":"mdi:attachment-plus","tags":["paperclip plus","paperclip add","attachment add"]},{"name":"mdi:attachment-remove","tags":["paperclip remove"]},{"name":"mdi:audio-input-rca","tags":["audio"]},{"name":"mdi:audio-input-stereo-minijack","tags":["audio"]},{"name":"mdi:audio-input-xlr","tags":["audio"]},{"name":"mdi:audio-video","tags":["home automation","audio","av receiver"]},{"name":"mdi:audio-video-off","tags":["home automation","audio","av receiver off"]},{"name":"mdi:augmented-reality","tags":[]},{"name":"mdi:aurora","tags":["science","weather","aurora borealis","aurora australis","northern lights","southern lights","polar lights"]},{"name":"mdi:auto-download","tags":[]},{"name":"mdi:auto-mode","tags":[]},{"name":"mdi:autorenew-off","tags":["arrow","clockwise arrows off","circular arrows off","circle arrows off","sync off"]},{"name":"mdi:awning","tags":["home automation","marquise","sun shade"]},{"name":"mdi:awning-outline","tags":["home automation","marquise outline","sun shade outline"]},{"name":"mdi:axe","tags":["hardware / tools"]},{"name":"mdi:axe-battle","tags":["gaming / rpg"]},{"name":"mdi:axis","tags":[]},{"name":"mdi:axis-arrow","tags":["arrow","accelerometer","gyro"]},{"name":"mdi:axis-arrow-info","tags":["arrow"]},{"name":"mdi:axis-arrow-lock","tags":["lock","arrow"]},{"name":"mdi:axis-lock","tags":["lock"]},{"name":"mdi:axis-x-arrow","tags":["arrow"]},{"name":"mdi:axis-x-arrow-lock","tags":["lock","arrow"]},{"name":"mdi:axis-x-rotate-clockwise","tags":[]},{"name":"mdi:axis-x-rotate-counterclockwise","tags":[]},{"name":"mdi:axis-x-y-arrow-lock","tags":["lock","arrow"]},{"name":"mdi:axis-y-arrow","tags":["arrow"]},{"name":"mdi:axis-y-arrow-lock","tags":["lock","arrow"]},{"name":"mdi:axis-y-rotate-clockwise","tags":[]},{"name":"mdi:axis-y-rotate-counterclockwise","tags":[]},{"name":"mdi:axis-z-arrow","tags":["arrow"]},{"name":"mdi:axis-z-arrow-lock","tags":["lock","arrow"]},{"name":"mdi:axis-z-rotate-clockwise","tags":["vertical rotate clockwise"]},{"name":"mdi:axis-z-rotate-counterclockwise","tags":["vertical rotate counterclockwise"]},{"name":"mdi:baby-bottle","tags":["people / family"]},{"name":"mdi:baby-bottle-outline","tags":["people / family"]},{"name":"mdi:baby-buggy","tags":["people / family","stroller","pram","carriage"]},{"name":"mdi:baby-buggy-off","tags":["people / family"]},{"name":"mdi:baby-carriage-off","tags":["people / family","child friendly off","stroller off","pram off","buggy off"]},{"name":"mdi:backburger","tags":["hamburger menu back"]},{"name":"mdi:backspace-reverse","tags":["clear reverse","erase reverse"]},{"name":"mdi:backspace-reverse-outline","tags":["clear reverse outline","erase reverse outline"]},{"name":"mdi:bacteria","tags":["science","medical / hospital"]},{"name":"mdi:bacteria-outline","tags":["science","medical / hospital"]},{"name":"mdi:badge-account","tags":["account / user","user badge","person badge"]},{"name":"mdi:badge-account-alert","tags":["account / user","alert / error","user badge alert","person badge alert","account badge warning","user badge warning","person badge warning"]},{"name":"mdi:badge-account-alert-outline","tags":["account / user","alert / error","user badge alert outline","person badge alert outline","account badge warning outline","user badge warning outline","person badge warning outline"]},{"name":"mdi:badge-account-outline","tags":["account / user","user badge outline","person badge outline"]},{"name":"mdi:badminton","tags":["sport","shuttlecock"]},{"name":"mdi:bag-personal","tags":["transportation + flying","backpack"]},{"name":"mdi:bag-personal-off","tags":["transportation + flying","backpack off"]},{"name":"mdi:bag-personal-off-outline","tags":["transportation + flying","backpack off outline"]},{"name":"mdi:bag-personal-outline","tags":["transportation + flying","backpack outline"]},{"name":"mdi:bag-personal-tag","tags":["property tag"]},{"name":"mdi:bag-personal-tag-outline","tags":["property tag outline"]},{"name":"mdi:baguette","tags":["food / drink","bread","bakery","french baguette","loaf"]},{"name":"mdi:balloon","tags":["holiday","party balloon"]},{"name":"mdi:ballot-recount","tags":["vote recount"]},{"name":"mdi:ballot-recount-outline","tags":["vote recount outline"]},{"name":"mdi:bank-check","tags":["banking"]},{"name":"mdi:bank-circle","tags":["banking"]},{"name":"mdi:bank-circle-outline","tags":["banking"]},{"name":"mdi:bank-minus","tags":["banking"]},{"name":"mdi:bank-off","tags":["banking"]},{"name":"mdi:bank-off-outline","tags":["banking"]},{"name":"mdi:bank-plus","tags":["banking","bank add"]},{"name":"mdi:bank-remove","tags":["banking"]},{"name":"mdi:bank-transfer","tags":["banking"]},{"name":"mdi:bank-transfer-in","tags":["banking"]},{"name":"mdi:bank-transfer-out","tags":["banking"]},{"name":"mdi:barcode","tags":[]},{"name":"mdi:barcode-off","tags":[]},{"name":"mdi:barcode-scan","tags":["barcode scanner"]},{"name":"mdi:barley","tags":["agriculture","food / drink","grain","wheat","gluten"]},{"name":"mdi:barley-off","tags":["agriculture","gluten free","grain off","wheat off"]},{"name":"mdi:barn","tags":["agriculture","farm"]},{"name":"mdi:baseball","tags":["sport"]},{"name":"mdi:baseball-bat","tags":["sport"]},{"name":"mdi:baseball-diamond","tags":["sport"]},{"name":"mdi:baseball-diamond-outline","tags":["sport"]},{"name":"mdi:baseball-outline","tags":["sport"]},{"name":"mdi:bash","tags":["developer / languages"]},{"name":"mdi:basket-check","tags":["shopping"]},{"name":"mdi:basket-check-outline","tags":["shopping"]},{"name":"mdi:basket-fill","tags":["shopping","skip fill"]},{"name":"mdi:basket-minus","tags":["shopping","shopping basket minus","skip minus"]},{"name":"mdi:basket-minus-outline","tags":["shopping","shopping basket minus outline","skip minus outline"]},{"name":"mdi:basket-off","tags":["shopping","shopping basket off","skip off"]},{"name":"mdi:basket-off-outline","tags":["shopping","shopping basket off outline","skip off outline"]},{"name":"mdi:basket-plus","tags":["shopping","shopping basket plus","skip plus"]},{"name":"mdi:basket-plus-outline","tags":["shopping","shopping basket plus outline","skip plus outline"]},{"name":"mdi:basket-remove","tags":["shopping","shopping basket remove","skip remove"]},{"name":"mdi:basket-remove-outline","tags":["shopping","shopping basket remove outline","skip remove outline"]},{"name":"mdi:basket-unfill","tags":["shopping"]},{"name":"mdi:basketball-hoop","tags":["sport"]},{"name":"mdi:basketball-hoop-outline","tags":["sport"]},{"name":"mdi:bat","tags":["holiday","animal"]},{"name":"mdi:battery-10-bluetooth","tags":["battery"]},{"name":"mdi:battery-20-bluetooth","tags":["battery"]},{"name":"mdi:battery-30-bluetooth","tags":["battery"]},{"name":"mdi:battery-40-bluetooth","tags":["battery"]},{"name":"mdi:battery-50-bluetooth","tags":["battery"]},{"name":"mdi:battery-60-bluetooth","tags":["battery"]},{"name":"mdi:battery-70-bluetooth","tags":["battery"]},{"name":"mdi:battery-80-bluetooth","tags":["battery"]},{"name":"mdi:battery-90-bluetooth","tags":["battery"]},{"name":"mdi:battery-alert-bluetooth","tags":["alert / error","battery","battery warning bluetooth"]},{"name":"mdi:battery-alert-variant","tags":["battery","alert / error"]},{"name":"mdi:battery-alert-variant-outline","tags":["battery","alert / error"]},{"name":"mdi:battery-arrow-down","tags":["battery"]},{"name":"mdi:battery-arrow-down-outline","tags":["battery"]},{"name":"mdi:battery-arrow-up","tags":["battery"]},{"name":"mdi:battery-arrow-up-outline","tags":["battery"]},{"name":"mdi:battery-bluetooth","tags":["battery","battery bluetooth 100","battery bluetooth full"]},{"name":"mdi:battery-bluetooth-variant","tags":["battery"]},{"name":"mdi:battery-charging-high","tags":["battery"]},{"name":"mdi:battery-charging-low","tags":["battery"]},{"name":"mdi:battery-charging-medium","tags":["battery"]},{"name":"mdi:battery-charging-wireless","tags":["battery","home automation","battery charging wireless full","battery charging wireless 100"]},{"name":"mdi:battery-charging-wireless-10","tags":["battery","home automation"]},{"name":"mdi:battery-charging-wireless-20","tags":["battery","home automation"]},{"name":"mdi:battery-charging-wireless-30","tags":["battery","home automation"]},{"name":"mdi:battery-charging-wireless-40","tags":["battery","home automation"]},{"name":"mdi:battery-charging-wireless-50","tags":["battery","home automation"]},{"name":"mdi:battery-charging-wireless-60","tags":["battery","home automation"]},{"name":"mdi:battery-charging-wireless-70","tags":["battery","home automation"]},{"name":"mdi:battery-charging-wireless-80","tags":["battery","home automation"]},{"name":"mdi:battery-charging-wireless-90","tags":["battery","home automation"]},{"name":"mdi:battery-charging-wireless-alert","tags":["battery","home automation","alert / error","battery charging wireless warning"]},{"name":"mdi:battery-charging-wireless-outline","tags":["battery","home automation","battery charging wireless empty","battery charging wireless 0"]},{"name":"mdi:battery-check","tags":["battery"]},{"name":"mdi:battery-check-outline","tags":["battery"]},{"name":"mdi:battery-clock","tags":["battery","home automation","date / time","battery full clock","battery 100 clock"]},{"name":"mdi:battery-clock-outline","tags":["battery","home automation","date / time","batter 0 clock","battery empty clock"]},{"name":"mdi:battery-heart","tags":["battery"]},{"name":"mdi:battery-heart-outline","tags":["battery"]},{"name":"mdi:battery-heart-variant","tags":["battery"]},{"name":"mdi:battery-high","tags":["battery"]},{"name":"mdi:battery-lock","tags":["battery","lock"]},{"name":"mdi:battery-lock-open","tags":["battery","lock"]},{"name":"mdi:battery-low","tags":["battery"]},{"name":"mdi:battery-medium","tags":["battery"]},{"name":"mdi:battery-minus","tags":["battery"]},{"name":"mdi:battery-minus-outline","tags":["battery"]},{"name":"mdi:battery-minus-variant","tags":["battery","home automation"]},{"name":"mdi:battery-negative","tags":["battery","home automation"]},{"name":"mdi:battery-off","tags":["battery"]},{"name":"mdi:battery-off-outline","tags":["battery"]},{"name":"mdi:battery-plus","tags":["battery"]},{"name":"mdi:battery-plus-outline","tags":["battery"]},{"name":"mdi:battery-plus-variant","tags":["battery","home automation","battery saver","battery add"]},{"name":"mdi:battery-positive","tags":["battery","home automation"]},{"name":"mdi:battery-remove","tags":["battery"]},{"name":"mdi:battery-remove-outline","tags":["battery"]},{"name":"mdi:battery-sync","tags":["battery","battery saver","battery recycle","battery eco"]},{"name":"mdi:battery-sync-outline","tags":["battery","battery saver outline","battery eco outline","battery recycle outline"]},{"name":"mdi:battery-unknown-bluetooth","tags":["battery"]},{"name":"mdi:beach","tags":["places","parasol"]},{"name":"mdi:beaker","tags":["science"]},{"name":"mdi:beaker-alert","tags":["alert / error","science"]},{"name":"mdi:beaker-alert-outline","tags":["alert / error","science"]},{"name":"mdi:beaker-check","tags":["science"]},{"name":"mdi:beaker-check-outline","tags":["science"]},{"name":"mdi:beaker-minus","tags":["science"]},{"name":"mdi:beaker-minus-outline","tags":["science"]},{"name":"mdi:beaker-outline","tags":["science"]},{"name":"mdi:beaker-plus","tags":["science"]},{"name":"mdi:beaker-plus-outline","tags":["science"]},{"name":"mdi:beaker-question","tags":["science"]},{"name":"mdi:beaker-question-outline","tags":["science"]},{"name":"mdi:beaker-remove","tags":["science"]},{"name":"mdi:beaker-remove-outline","tags":["science"]},{"name":"mdi:bed-clock","tags":["date / time","bed schedule","bed time","sleep schedule","sleep time"]},{"name":"mdi:bed-double","tags":["home automation","holiday","bedroom"]},{"name":"mdi:bed-empty","tags":["home automation","holiday"]},{"name":"mdi:bed-king-outline","tags":["home automation","holiday","bedroom outline"]},{"name":"mdi:bed-queen","tags":["home automation","holiday","bedroom"]},{"name":"mdi:bed-queen-outline","tags":["home automation","holiday","bedroom outline"]},{"name":"mdi:bed-single","tags":["home automation","holiday","bedroom"]},{"name":"mdi:bed-single-outline","tags":["home automation","holiday","bedroom outline"]},{"name":"mdi:beehive-off-outline","tags":["nature","agriculture"]},{"name":"mdi:beehive-outline","tags":["nature","agriculture","honey outline"]},{"name":"mdi:beekeeper","tags":["nature","agriculture","apiarists","apiculturists","honey farmer"]},{"name":"mdi:beer","tags":["food / drink","pint","pub","bar","drink","cup full"]},{"name":"mdi:beer-outline","tags":["food / drink","drink outline","cup full outline","pint outline","pub outline","bar outline"]},{"name":"mdi:bell-alert","tags":["alert / error","notification","bell warning"]},{"name":"mdi:bell-alert-outline","tags":["alert / error","notification"]},{"name":"mdi:bell-badge","tags":["notification","bell notification"]},{"name":"mdi:bell-badge-outline","tags":["notification","bell notification outline"]},{"name":"mdi:bell-cancel","tags":["notification"]},{"name":"mdi:bell-cancel-outline","tags":["notification"]},{"name":"mdi:bell-check","tags":["notification"]},{"name":"mdi:bell-check-outline","tags":["notification"]},{"name":"mdi:bell-cog","tags":["notification","settings","bell settings","notification settings"]},{"name":"mdi:bell-cog-outline","tags":["notification","settings","bell settings outline","notification settings outline"]},{"name":"mdi:bell-minus","tags":["notification"]},{"name":"mdi:bell-minus-outline","tags":["notification"]},{"name":"mdi:bell-plus","tags":["notification","add alert","bell add"]},{"name":"mdi:bell-plus-outline","tags":["notification","bell add outline","add alert outline"]},{"name":"mdi:bell-remove","tags":["notification"]},{"name":"mdi:bell-remove-outline","tags":["notification"]},{"name":"mdi:bench","tags":[]},{"name":"mdi:bench-back","tags":[]},{"name":"mdi:beta","tags":["alpha / numeric"]},{"name":"mdi:betamax","tags":[]},{"name":"mdi:bicycle","tags":["transportation + other","sport","bike","cycling"]},{"name":"mdi:bicycle-basket","tags":["transportation + other","sport","bike basket"]},{"name":"mdi:bicycle-cargo","tags":["transportation + other","sport","bike cargo"]},{"name":"mdi:bicycle-electric","tags":["transportation + other","bike electric"]},{"name":"mdi:bicycle-penny-farthing","tags":["transportation + other","sport","bicycle high wheel","bicycle antique"]},{"name":"mdi:bike-fast","tags":["transportation + other","sport","velocity"]},{"name":"mdi:bike-pedal","tags":["transportation + other","sport","bike pedal flat"]},{"name":"mdi:bike-pedal-clipless","tags":["transportation + other","sport"]},{"name":"mdi:bike-pedal-mountain","tags":["transportation + other","sport"]},{"name":"mdi:billboard","tags":[]},{"name":"mdi:billiards","tags":["sport","pool","eight ball"]},{"name":"mdi:billiards-rack","tags":["sport","pool table","pool rack","snooker rack","pool triangle","billiards triangle","snooker triangle"]},{"name":"mdi:binoculars","tags":[]},{"name":"mdi:bio","tags":[]},{"name":"mdi:biohazard","tags":["science"]},{"name":"mdi:bird","tags":["animal"]},{"name":"mdi:blinds","tags":["home automation","roller shade closed","window closed"]},{"name":"mdi:blinds-open","tags":["home automation","roller shade open","window open"]},{"name":"mdi:block-helper","tags":[]},{"name":"mdi:blood-bag","tags":["medical / hospital"]},{"name":"mdi:bluetooth","tags":[]},{"name":"mdi:bolt","tags":["hardware / tools"]},{"name":"mdi:bomb","tags":["gaming / rpg"]},{"name":"mdi:bomb-off","tags":["gaming / rpg"]},{"name":"mdi:bone","tags":["animal","holiday"]},{"name":"mdi:bone-off","tags":["animal","holiday"]},{"name":"mdi:book-account","tags":["account / user"]},{"name":"mdi:book-account-outline","tags":["account / user"]},{"name":"mdi:book-alert","tags":["alert / error"]},{"name":"mdi:book-alert-outline","tags":["alert / error"]},{"name":"mdi:book-alphabet","tags":["dictionary"]},{"name":"mdi:book-arrow-down","tags":[]},{"name":"mdi:book-arrow-down-outline","tags":[]},{"name":"mdi:book-arrow-left","tags":[]},{"name":"mdi:book-arrow-left-outline","tags":[]},{"name":"mdi:book-arrow-right","tags":[]},{"name":"mdi:book-arrow-right-outline","tags":[]},{"name":"mdi:book-arrow-up","tags":[]},{"name":"mdi:book-arrow-up-outline","tags":[]},{"name":"mdi:book-cancel","tags":[]},{"name":"mdi:book-cancel-outline","tags":[]},{"name":"mdi:book-check","tags":[]},{"name":"mdi:book-check-outline","tags":[]},{"name":"mdi:book-clock","tags":["date / time","book schedule","book time"]},{"name":"mdi:book-clock-outline","tags":["date / time","book schedule","book time"]},{"name":"mdi:book-cog","tags":["settings","book settings"]},{"name":"mdi:book-cog-outline","tags":["settings","book settings outline"]},{"name":"mdi:book-cross","tags":["religion","bible"]},{"name":"mdi:book-edit","tags":["edit / modify"]},{"name":"mdi:book-edit-outline","tags":["edit / modify"]},{"name":"mdi:book-education","tags":[]},{"name":"mdi:book-education-outline","tags":[]},{"name":"mdi:book-heart","tags":["book favorite","book love"]},{"name":"mdi:book-heart-outline","tags":["book favorite outline","book love outline"]},{"name":"mdi:book-information-variant","tags":["encyclopedia"]},{"name":"mdi:book-lock","tags":["lock","book secure"]},{"name":"mdi:book-lock-open","tags":["lock","book unsecure"]},{"name":"mdi:book-lock-open-outline","tags":["lock"]},{"name":"mdi:book-lock-outline","tags":["lock","book secure outline"]},{"name":"mdi:book-marker","tags":["navigation","book location"]},{"name":"mdi:book-marker-outline","tags":["navigation","book location outline"]},{"name":"mdi:book-minus","tags":[]},{"name":"mdi:book-minus-multiple","tags":["books minus"]},{"name":"mdi:book-minus-multiple-outline","tags":[]},{"name":"mdi:book-minus-outline","tags":[]},{"name":"mdi:book-multiple","tags":["books"]},{"name":"mdi:book-multiple-outline","tags":[]},{"name":"mdi:book-music","tags":["audio","music","audio book"]},{"name":"mdi:book-music-outline","tags":["music"]},{"name":"mdi:book-off","tags":[]},{"name":"mdi:book-off-outline","tags":[]},{"name":"mdi:book-play","tags":[]},{"name":"mdi:book-play-outline","tags":[]},{"name":"mdi:book-plus","tags":["book add"]},{"name":"mdi:book-plus-multiple","tags":["books plus","book multiple add","books add"]},{"name":"mdi:book-plus-multiple-outline","tags":[]},{"name":"mdi:book-plus-outline","tags":[]},{"name":"mdi:book-refresh","tags":[]},{"name":"mdi:book-refresh-outline","tags":[]},{"name":"mdi:book-remove","tags":[]},{"name":"mdi:book-remove-multiple","tags":["books remove"]},{"name":"mdi:book-remove-multiple-outline","tags":[]},{"name":"mdi:book-remove-outline","tags":[]},{"name":"mdi:book-search","tags":[]},{"name":"mdi:book-search-outline","tags":[]},{"name":"mdi:book-settings","tags":["settings"]},{"name":"mdi:book-settings-outline","tags":["settings"]},{"name":"mdi:book-sync","tags":[]},{"name":"mdi:book-sync-outline","tags":[]},{"name":"mdi:bookmark-box","tags":[]},{"name":"mdi:bookmark-box-multiple-outline","tags":["collections bookmark outline","library bookmark outline"]},{"name":"mdi:bookmark-box-outline","tags":[]},{"name":"mdi:bookmark-check-outline","tags":["bookmark success outline"]},{"name":"mdi:bookmark-minus","tags":[]},{"name":"mdi:bookmark-minus-outline","tags":[]},{"name":"mdi:bookmark-music","tags":["music"]},{"name":"mdi:bookmark-music-outline","tags":["music"]},{"name":"mdi:bookmark-off","tags":[]},{"name":"mdi:bookmark-off-outline","tags":[]},{"name":"mdi:bookmark-plus","tags":["bookmark add"]},{"name":"mdi:bookmark-remove","tags":[]},{"name":"mdi:bookmark-remove-outline","tags":[]},{"name":"mdi:bookshelf","tags":[]},{"name":"mdi:boom-gate","tags":["transportation + road","home automation","boom arm","boom barrier","arm barrier","barrier","automatic gate"]},{"name":"mdi:boom-gate-alert","tags":["alert / error","transportation + road","boom arm alert","boom barrier alert","arm barrier alert","barrier alert","automatic gate alert"]},{"name":"mdi:boom-gate-alert-outline","tags":["alert / error","transportation + road","boom arm alert outline","boom barrier alert outline","arm barrier alert outline","barrier alert outline","automatic gate alert outline"]},{"name":"mdi:boom-gate-arrow-down","tags":["transportation + road","boom arm down","boom barrier down","arm barrier down","barrier down","automatic gate down"]},{"name":"mdi:boom-gate-arrow-down-outline","tags":["transportation + road","boom arm down outline","boom barrier down outline","arm barrier down outline","barrier down outline","automatic gate down outline"]},{"name":"mdi:boom-gate-arrow-up","tags":["transportation + road","boom arm up","boom barrier up","arm barrier up","barrier up","automatic gate up"]},{"name":"mdi:boom-gate-arrow-up-outline","tags":["transportation + road","boom arm up outline","boom barrier up outline","arm barrier up outline","barrier up outline","automatic gate up outline"]},{"name":"mdi:boom-gate-outline","tags":["transportation + road","home automation","boom arm outline","boom barrier outline","arm barrier outline","barrier outline","automatic gate outline"]},{"name":"mdi:boom-gate-up","tags":["transportation + road","home automation","boom arm up","boom barrier up","arm barrier up","barrier up","automatic gate up"]},{"name":"mdi:boom-gate-up-outline","tags":["transportation + road","home automation","boom arm up outline","boom barrier up outline","arm barrier up outline","barrier up outline","automatic gate up outline"]},{"name":"mdi:boomerang","tags":["gaming / rpg"]},{"name":"mdi:border-all-variant","tags":["text / content / format"]},{"name":"mdi:border-bottom-variant","tags":["text / content / format"]},{"name":"mdi:border-left-variant","tags":["text / content / format"]},{"name":"mdi:border-none-variant","tags":["text / content / format"]},{"name":"mdi:border-radius","tags":["text / content / format","border round corners"]},{"name":"mdi:border-right-variant","tags":["text / content / format"]},{"name":"mdi:border-top-variant","tags":["text / content / format"]},{"name":"mdi:bottle-soda","tags":["food / drink","bottle coke","bottle pop"]},{"name":"mdi:bottle-soda-classic","tags":["food / drink","bottle coke classic","bottle pop classic"]},{"name":"mdi:bottle-soda-classic-outline","tags":[]},{"name":"mdi:bottle-soda-outline","tags":["food / drink","bottle coke outline","bottle pop outline"]},{"name":"mdi:bottle-tonic","tags":["science","flask"]},{"name":"mdi:bottle-tonic-outline","tags":["science","flask outline"]},{"name":"mdi:bottle-tonic-plus","tags":["gaming / rpg","health potion"]},{"name":"mdi:bottle-tonic-plus-outline","tags":["gaming / rpg","health potion outline"]},{"name":"mdi:bottle-tonic-skull","tags":["gaming / rpg","holiday","poison","moonshine"]},{"name":"mdi:bottle-tonic-skull-outline","tags":["gaming / rpg","holiday","poison outline","moonshine outline"]},{"name":"mdi:bottle-wine","tags":["food / drink"]},{"name":"mdi:bottle-wine-outline","tags":["food / drink"]},{"name":"mdi:bow-arrow","tags":["gaming / rpg","sport"]},{"name":"mdi:bow-tie","tags":["clothing"]},{"name":"mdi:bowl","tags":["food / drink"]},{"name":"mdi:bowl-mix","tags":["food / drink","mixing bowl"]},{"name":"mdi:bowl-mix-outline","tags":["food / drink","mixing bowl outline"]},{"name":"mdi:bowl-outline","tags":["food / drink"]},{"name":"mdi:bowling","tags":["sport"]},{"name":"mdi:box-cutter","tags":["hardware / tools","stanley knife"]},{"name":"mdi:box-cutter-off","tags":[]},{"name":"mdi:box-shadow","tags":[]},{"name":"mdi:boxing-glove","tags":["sport"]},{"name":"mdi:braille","tags":["touch reading","hand reading"]},{"name":"mdi:brain","tags":["medical / hospital"]},{"name":"mdi:bread-slice","tags":["food / drink"]},{"name":"mdi:bread-slice-outline","tags":["food / drink"]},{"name":"mdi:bridge","tags":["places"]},{"name":"mdi:briefcase-account","tags":["account / user","briefcase person","briefcase user"]},{"name":"mdi:briefcase-account-outline","tags":["account / user","briefcase person outline","briefcase user outline"]},{"name":"mdi:briefcase-arrow-left-right","tags":["briefcase transfer","briefcase exchange","briefcase swap"]},{"name":"mdi:briefcase-arrow-left-right-outline","tags":["briefcase exchange outline","briefcase transfer outline","briefcase swap outline"]},{"name":"mdi:briefcase-arrow-up-down","tags":["briefcase exchange","briefcase transfer","briefcase swap"]},{"name":"mdi:briefcase-arrow-up-down-outline","tags":["briefcase exchange outline","briefcase transfer outline","briefcase swap outline"]},{"name":"mdi:briefcase-check-outline","tags":[]},{"name":"mdi:briefcase-clock","tags":["date / time"]},{"name":"mdi:briefcase-clock-outline","tags":["date / time"]},{"name":"mdi:briefcase-download-outline","tags":[]},{"name":"mdi:briefcase-edit","tags":["edit / modify"]},{"name":"mdi:briefcase-edit-outline","tags":["edit / modify"]},{"name":"mdi:briefcase-eye","tags":["briefcase view"]},{"name":"mdi:briefcase-eye-outline","tags":["briefcase view outline"]},{"name":"mdi:briefcase-minus","tags":[]},{"name":"mdi:briefcase-minus-outline","tags":[]},{"name":"mdi:briefcase-off","tags":[]},{"name":"mdi:briefcase-off-outline","tags":[]},{"name":"mdi:briefcase-plus","tags":["briefcase add"]},{"name":"mdi:briefcase-plus-outline","tags":["briefcase add outline"]},{"name":"mdi:briefcase-remove","tags":[]},{"name":"mdi:briefcase-remove-outline","tags":[]},{"name":"mdi:briefcase-search","tags":[]},{"name":"mdi:briefcase-search-outline","tags":[]},{"name":"mdi:briefcase-upload","tags":[]},{"name":"mdi:briefcase-upload-outline","tags":[]},{"name":"mdi:briefcase-variant-off","tags":[]},{"name":"mdi:briefcase-variant-off-outline","tags":[]},{"name":"mdi:brightness-percent","tags":["shopping","discount","sale"]},{"name":"mdi:broom","tags":[]},{"name":"mdi:brush-off","tags":[]},{"name":"mdi:bucket","tags":[]},{"name":"mdi:bucket-outline","tags":[]},{"name":"mdi:buffet","tags":["home automation","sideboard"]},{"name":"mdi:bug-check","tags":["animal","bug tick"]},{"name":"mdi:bug-check-outline","tags":["animal","bug tick outline"]},{"name":"mdi:bug-pause","tags":[]},{"name":"mdi:bug-pause-outline","tags":[]},{"name":"mdi:bug-play","tags":["bug start"]},{"name":"mdi:bug-play-outline","tags":[]},{"name":"mdi:bug-stop","tags":[]},{"name":"mdi:bug-stop-outline","tags":[]},{"name":"mdi:bugle","tags":["automotive","music","car horn"]},{"name":"mdi:bulkhead-light","tags":["home automation"]},{"name":"mdi:bulldozer","tags":["hardware / tools"]},{"name":"mdi:bullet","tags":[]},{"name":"mdi:bulletin-board","tags":["notice board"]},{"name":"mdi:bullhorn-variant","tags":["announcement","megaphone","loudspeaker"]},{"name":"mdi:bullhorn-variant-outline","tags":["announcement outline","megaphone outline","loudspeaker outline"]},{"name":"mdi:bullseye","tags":["sport","target"]},{"name":"mdi:bullseye-arrow","tags":["sport","target arrow"]},{"name":"mdi:bunk-bed","tags":["home automation"]},{"name":"mdi:bunk-bed-outline","tags":["home automation"]},{"name":"mdi:bus-articulated-end","tags":["transportation + road"]},{"name":"mdi:bus-articulated-front","tags":["transportation + road"]},{"name":"mdi:bus-double-decker","tags":["transportation + road"]},{"name":"mdi:bus-electric","tags":["transportation + road"]},{"name":"mdi:bus-marker","tags":["navigation","bus location","bus stop"]},{"name":"mdi:bus-multiple","tags":["transportation + road","fleet"]},{"name":"mdi:bus-school","tags":["transportation + road","education"]},{"name":"mdi:bus-side","tags":["transportation + road"]},{"name":"mdi:bus-stop","tags":["transportation + road","navigation"]},{"name":"mdi:bus-stop-covered","tags":["transportation + road","navigation"]},{"name":"mdi:bus-stop-uncovered","tags":["transportation + road","navigation"]},{"name":"mdi:butterfly","tags":["nature","animal"]},{"name":"mdi:butterfly-outline","tags":["nature","animal"]},{"name":"mdi:button-cursor","tags":["form"]},{"name":"mdi:button-pointer","tags":["form"]},{"name":"mdi:cabin-a-frame","tags":["home automation"]},{"name":"mdi:cable-data","tags":[]},{"name":"mdi:cactus","tags":["nature"]},{"name":"mdi:calculator","tags":["math"]},{"name":"mdi:calendar-account","tags":["date / time","account / user","calendar user"]},{"name":"mdi:calendar-account-outline","tags":["date / time","account / user","calendar user outline"]},{"name":"mdi:calendar-alert","tags":["date / time","alert / error","event alert","calendar warning"]},{"name":"mdi:calendar-alert-outline","tags":["date / time","alert / error"]},{"name":"mdi:calendar-arrow-left","tags":["date / time","reschedule"]},{"name":"mdi:calendar-arrow-right","tags":["date / time","reschedule"]},{"name":"mdi:calendar-badge","tags":["date / time"]},{"name":"mdi:calendar-badge-outline","tags":["date / time"]},{"name":"mdi:calendar-blank","tags":["date / time","calendar today"]},{"name":"mdi:calendar-blank-multiple","tags":["date / time"]},{"name":"mdi:calendar-clock","tags":["date / time","event clock","event time","calendar time"]},{"name":"mdi:calendar-clock-outline","tags":["date / time"]},{"name":"mdi:calendar-collapse-horizontal","tags":["date / time"]},{"name":"mdi:calendar-collapse-horizontal-outline","tags":["date / time"]},{"name":"mdi:calendar-cursor","tags":["date / time"]},{"name":"mdi:calendar-cursor-outline","tags":["date / time"]},{"name":"mdi:calendar-edit","tags":["date / time","edit / modify","event edit"]},{"name":"mdi:calendar-edit-outline","tags":["date / time","edit / modify"]},{"name":"mdi:calendar-end","tags":["date / time"]},{"name":"mdi:calendar-end-outline","tags":["date / time"]},{"name":"mdi:calendar-expand-horizontal","tags":["date / time"]},{"name":"mdi:calendar-expand-horizontal-outline","tags":["date / time"]},{"name":"mdi:calendar-export","tags":["date / time"]},{"name":"mdi:calendar-export-outline","tags":["date / time"]},{"name":"mdi:calendar-filter","tags":["date / time"]},{"name":"mdi:calendar-filter-outline","tags":["date / time","event week end outline"]},{"name":"mdi:calendar-heart","tags":["date / time","event heart"]},{"name":"mdi:calendar-heart-outline","tags":["date / time"]},{"name":"mdi:calendar-import","tags":["date / time"]},{"name":"mdi:calendar-import-outline","tags":["date / time"]},{"name":"mdi:calendar-lock","tags":["date / time","lock"]},{"name":"mdi:calendar-lock-open","tags":["lock","date / time"]},{"name":"mdi:calendar-lock-open-outline","tags":["lock","date / time"]},{"name":"mdi:calendar-lock-outline","tags":["date / time","lock"]},{"name":"mdi:calendar-minus","tags":["date / time","event minus"]},{"name":"mdi:calendar-minus-outline","tags":["date / time"]},{"name":"mdi:calendar-month","tags":["date / time"]},{"name":"mdi:calendar-month-outline","tags":["date / time"]},{"name":"mdi:calendar-multiple","tags":["date / time","event multiple","calendars","events"]},{"name":"mdi:calendar-multiple-check","tags":["date / time","event multiple check","calendar multiple tick","calendars check","calendars tick","event multiple tick","events check","events tick"]},{"name":"mdi:calendar-multiselect","tags":["date / time"]},{"name":"mdi:calendar-multiselect-outline","tags":["date / time"]},{"name":"mdi:calendar-plus","tags":["date / time","event plus","calendar add","event add"]},{"name":"mdi:calendar-plus-outline","tags":["date / time"]},{"name":"mdi:calendar-question","tags":["date / time","calendar rsvp","event question","calendar help"]},{"name":"mdi:calendar-question-outline","tags":["date / time","calendar help outline"]},{"name":"mdi:calendar-refresh","tags":["date / time","calendar repeat"]},{"name":"mdi:calendar-refresh-outline","tags":["date / time","calendar repeat outline"]},{"name":"mdi:calendar-search","tags":["date / time","event search"]},{"name":"mdi:calendar-search-outline","tags":["date / time"]},{"name":"mdi:calendar-star","tags":["date / time","event star","calendar favorite"]},{"name":"mdi:calendar-star-four-points","tags":["date / time","calendar auto","event star four points","event auto"]},{"name":"mdi:calendar-star-outline","tags":["date / time"]},{"name":"mdi:calendar-start","tags":["date / time"]},{"name":"mdi:calendar-start-outline","tags":["date / time"]},{"name":"mdi:calendar-sync","tags":["date / time","calendar repeat"]},{"name":"mdi:calendar-sync-outline","tags":["date / time","calendar repeat outline"]},{"name":"mdi:calendar-today-outline","tags":["date / time","calendar day outline"]},{"name":"mdi:calendar-week","tags":["date / time","event week"]},{"name":"mdi:calendar-week-begin","tags":["date / time","event week begin"]},{"name":"mdi:calendar-week-begin-outline","tags":["date / time","event week begin outline"]},{"name":"mdi:calendar-week-outline","tags":["date / time","event week outline"]},{"name":"mdi:calendar-weekend","tags":["date / time"]},{"name":"mdi:calendar-weekend-outline","tags":["date / time"]},{"name":"mdi:camcorder","tags":["video / movie"]},{"name":"mdi:camcorder-off","tags":["video / movie"]},{"name":"mdi:camera-document","tags":["photography","overhead projector"]},{"name":"mdi:camera-document-off","tags":["photography","overhead projector off"]},{"name":"mdi:camera-flip","tags":["photography","camera sync","camera refresh"]},{"name":"mdi:camera-flip-outline","tags":["photography","camera sync outline","camera refresh outline"]},{"name":"mdi:camera-gopro","tags":["photography","device / tech"]},{"name":"mdi:camera-lock","tags":["photography","lock"]},{"name":"mdi:camera-lock-open","tags":["photography"]},{"name":"mdi:camera-lock-open-outline","tags":["photography"]},{"name":"mdi:camera-lock-outline","tags":["photography","lock"]},{"name":"mdi:camera-marker","tags":["photography","navigation","camera location"]},{"name":"mdi:camera-marker-outline","tags":["photography","navigation","camera location outline"]},{"name":"mdi:camera-metering-center","tags":["photography","camera metering centre"]},{"name":"mdi:camera-metering-matrix","tags":["photography"]},{"name":"mdi:camera-metering-partial","tags":["photography"]},{"name":"mdi:camera-metering-spot","tags":["photography"]},{"name":"mdi:camera-off","tags":["photography"]},{"name":"mdi:camera-off-outline","tags":["photography"]},{"name":"mdi:camera-retake","tags":["photography"]},{"name":"mdi:camera-retake-outline","tags":["photography"]},{"name":"mdi:camera-timer","tags":["date / time","photography"]},{"name":"mdi:campfire","tags":[]},{"name":"mdi:candelabra","tags":["home automation","holiday","candle","candelabrum"]},{"name":"mdi:candelabra-fire","tags":["home automation","holiday","candelabrum fire","candelabrum flame","candelabra flame","candle fire","candle flame"]},{"name":"mdi:candy","tags":["food / drink","treat","chocolate"]},{"name":"mdi:candy-off","tags":["food / drink","chocolate off","treat off"]},{"name":"mdi:candy-off-outline","tags":["food / drink","gaming / rpg","chocolate off outline","treat off outline","navi off"]},{"name":"mdi:candy-outline","tags":["food / drink","gaming / rpg","chocolate outline","treat outline","navi","hey listen","fairy"]},{"name":"mdi:candycane","tags":["holiday","food / drink"]},{"name":"mdi:cannabis","tags":["nature","medical / hospital","weed","pot","marijuana"]},{"name":"mdi:cannabis-off","tags":[]},{"name":"mdi:caps-lock","tags":["text / content / format"]},{"name":"mdi:car-2-plus","tags":["transportation + road","automotive","hov lane","high occupancy vehicle lane","carpool lane"]},{"name":"mdi:car-3-plus","tags":["transportation + road","automotive","hov lane","high occupancy vehicle lane","carpool lane"]},{"name":"mdi:car-arrow-left","tags":["automotive","transportation + road"]},{"name":"mdi:car-arrow-right","tags":["automotive","transportation + road"]},{"name":"mdi:car-back","tags":["automotive","transportation + road"]},{"name":"mdi:car-battery","tags":["battery","automotive"]},{"name":"mdi:car-brake-abs","tags":["automotive","anti lock brake system","anti lock braking system"]},{"name":"mdi:car-brake-alert","tags":["automotive","alert / error","car parking brake","car handbrake","car hand brake","car emergency brake","car brake warning"]},{"name":"mdi:car-brake-fluid-level","tags":["automotive"]},{"name":"mdi:car-brake-hold","tags":["automotive"]},{"name":"mdi:car-brake-low-pressure","tags":["automotive"]},{"name":"mdi:car-brake-parking","tags":["automotive"]},{"name":"mdi:car-brake-retarder","tags":["automotive"]},{"name":"mdi:car-brake-temperature","tags":["automotive"]},{"name":"mdi:car-brake-worn-linings","tags":["automotive"]},{"name":"mdi:car-child-seat","tags":["automotive","people / family"]},{"name":"mdi:car-clock","tags":["date / time","automotive"]},{"name":"mdi:car-clutch","tags":["automotive"]},{"name":"mdi:car-cog","tags":["automotive","settings","transportation + road","car settings"]},{"name":"mdi:car-connected","tags":["transportation + road","automotive"]},{"name":"mdi:car-convertible","tags":["transportation + road","automotive"]},{"name":"mdi:car-coolant-level","tags":["automotive"]},{"name":"mdi:car-cruise-control","tags":["automotive"]},{"name":"mdi:car-defrost-front","tags":["automotive"]},{"name":"mdi:car-defrost-rear","tags":["automotive"]},{"name":"mdi:car-door","tags":["automotive"]},{"name":"mdi:car-door-lock","tags":["automotive","lock"]},{"name":"mdi:car-emergency","tags":["transportation + road","automotive","car police"]},{"name":"mdi:car-esp","tags":["automotive","electronic stability program"]},{"name":"mdi:car-estate","tags":["transportation + road","automotive","car suv","car sports utility vehicle"]},{"name":"mdi:car-hatchback","tags":["transportation + road","automotive"]},{"name":"mdi:car-info","tags":["automotive"]},{"name":"mdi:car-key","tags":["transportation + road","automotive","car rental","rent a car"]},{"name":"mdi:car-lifted-pickup","tags":["automotive","agriculture"]},{"name":"mdi:car-light-alert","tags":["alert / error","automotive"]},{"name":"mdi:car-light-dimmed","tags":["automotive","head light dimmed","low beam"]},{"name":"mdi:car-light-fog","tags":["automotive","head light fog"]},{"name":"mdi:car-light-high","tags":["automotive","head light high","high beam"]},{"name":"mdi:car-limousine","tags":["transportation + road","automotive"]},{"name":"mdi:car-multiple","tags":["transportation + road","automotive"]},{"name":"mdi:car-off","tags":["automotive"]},{"name":"mdi:car-parking-lights","tags":["automotive"]},{"name":"mdi:car-pickup","tags":["transportation + road","automotive","agriculture"]},{"name":"mdi:car-search","tags":["automotive","car find"]},{"name":"mdi:car-search-outline","tags":["automotive","car find outline"]},{"name":"mdi:car-seat","tags":["automotive"]},{"name":"mdi:car-seat-cooler","tags":["automotive"]},{"name":"mdi:car-seat-heater","tags":["automotive"]},{"name":"mdi:car-select","tags":["automotive","car location"]},{"name":"mdi:car-settings","tags":["automotive","settings"]},{"name":"mdi:car-shift-pattern","tags":["automotive","car transmission","car manual transmission"]},{"name":"mdi:car-side","tags":["transportation + road","automotive","car saloon"]},{"name":"mdi:car-speed-limiter","tags":["automotive"]},{"name":"mdi:car-sports","tags":["transportation + road","sport","automotive"]},{"name":"mdi:car-tire-alert","tags":["automotive","alert / error","car tyre alert","car tyre warning","car tire warning"]},{"name":"mdi:car-traction-control","tags":["automotive"]},{"name":"mdi:car-turbocharger","tags":["automotive"]},{"name":"mdi:car-windshield","tags":["automotive","car front glass"]},{"name":"mdi:car-windshield-outline","tags":["automotive","car front glass outline"]},{"name":"mdi:car-wireless","tags":["automotive","car autonomous","car self driving","car smart"]},{"name":"mdi:car-wrench","tags":["automotive","hardware / tools","car repair","mechanic"]},{"name":"mdi:caravan","tags":["transportation + road","home automation","automotive"]},{"name":"mdi:card","tags":["form","button"]},{"name":"mdi:card-account-details","tags":["account / user","identification card","user card details","id card","person card details","drivers license","business card"]},{"name":"mdi:card-account-details-outline","tags":["account / user","identification card outline","user card details outline","id card outline","person card details outline","drivers license outline","business card outline"]},{"name":"mdi:card-account-details-star","tags":["account / user","card account details favorite"]},{"name":"mdi:card-account-details-star-outline","tags":["account / user","card account details favorite outline"]},{"name":"mdi:card-bulleted","tags":[]},{"name":"mdi:card-bulleted-off","tags":[]},{"name":"mdi:card-bulleted-off-outline","tags":[]},{"name":"mdi:card-bulleted-outline","tags":[]},{"name":"mdi:card-bulleted-settings","tags":["settings"]},{"name":"mdi:card-bulleted-settings-outline","tags":["settings"]},{"name":"mdi:card-minus","tags":[]},{"name":"mdi:card-minus-outline","tags":[]},{"name":"mdi:card-multiple","tags":[]},{"name":"mdi:card-multiple-outline","tags":[]},{"name":"mdi:card-off","tags":[]},{"name":"mdi:card-off-outline","tags":[]},{"name":"mdi:card-outline","tags":["form","button outline"]},{"name":"mdi:card-plus","tags":[]},{"name":"mdi:card-plus-outline","tags":[]},{"name":"mdi:card-remove","tags":[]},{"name":"mdi:card-remove-outline","tags":[]},{"name":"mdi:card-text","tags":[]},{"name":"mdi:card-text-outline","tags":[]},{"name":"mdi:cards","tags":["gaming / rpg"]},{"name":"mdi:cards-club","tags":["gaming / rpg","suit clubs","poker club"]},{"name":"mdi:cards-club-outline","tags":[]},{"name":"mdi:cards-diamond","tags":["gaming / rpg","transportation + road","suit diamonds","hov lane","high occupancy vehicle lane","carpool lane","poker diamond"]},{"name":"mdi:cards-diamond-outline","tags":["transportation + road","hov lane outline","high occupancy vehicle lane outline","carpool lane outline","poker diamond outline"]},{"name":"mdi:cards-heart","tags":["gaming / rpg","suit hearts","poker heart"]},{"name":"mdi:cards-heart-outline","tags":[]},{"name":"mdi:cards-outline","tags":["gaming / rpg"]},{"name":"mdi:cards-playing","tags":["gaming / rpg"]},{"name":"mdi:cards-playing-club","tags":["gaming / rpg"]},{"name":"mdi:cards-playing-club-multiple","tags":["gaming / rpg"]},{"name":"mdi:cards-playing-club-multiple-outline","tags":["gaming / rpg"]},{"name":"mdi:cards-playing-club-outline","tags":["gaming / rpg"]},{"name":"mdi:cards-playing-diamond","tags":["gaming / rpg"]},{"name":"mdi:cards-playing-diamond-multiple","tags":["gaming / rpg"]},{"name":"mdi:cards-playing-diamond-multiple-outline","tags":["gaming / rpg"]},{"name":"mdi:cards-playing-diamond-outline","tags":["gaming / rpg"]},{"name":"mdi:cards-playing-heart","tags":["gaming / rpg"]},{"name":"mdi:cards-playing-heart-multiple","tags":["gaming / rpg"]},{"name":"mdi:cards-playing-heart-multiple-outline","tags":["gaming / rpg"]},{"name":"mdi:cards-playing-heart-outline","tags":["gaming / rpg"]},{"name":"mdi:cards-playing-outline","tags":["gaming / rpg"]},{"name":"mdi:cards-playing-spade","tags":["gaming / rpg"]},{"name":"mdi:cards-playing-spade-multiple","tags":["gaming / rpg"]},{"name":"mdi:cards-playing-spade-multiple-outline","tags":["gaming / rpg"]},{"name":"mdi:cards-playing-spade-outline","tags":["gaming / rpg"]},{"name":"mdi:cards-spade","tags":["gaming / rpg","suit spades","poker spade"]},{"name":"mdi:cards-spade-outline","tags":["gaming / rpg"]},{"name":"mdi:cards-variant","tags":["gaming / rpg"]},{"name":"mdi:carrot","tags":["agriculture","food / drink"]},{"name":"mdi:cart-arrow-down","tags":["shopping","shopping cart arrow down","trolley arrow down"]},{"name":"mdi:cart-arrow-right","tags":["shopping","trolley arrow right","shopping cart arrow right"]},{"name":"mdi:cart-arrow-up","tags":["shopping","shopping cart arrow up","trolley arrow up"]},{"name":"mdi:cart-check","tags":["shopping"]},{"name":"mdi:cart-heart","tags":["shopping","cart favorite","shopping favorite"]},{"name":"mdi:cart-minus","tags":["shopping","shopping cart minus","trolley minus"]},{"name":"mdi:cart-percent","tags":["shopping","cart discount","cart sale","trolley percent"]},{"name":"mdi:cart-remove","tags":["shopping","trolley remove","shopping cart remove"]},{"name":"mdi:cart-variant","tags":["shopping"]},{"name":"mdi:case-sensitive-alt","tags":[]},{"name":"mdi:cash","tags":["currency","banking","shopping","money"]},{"name":"mdi:cash-check","tags":["currency","banking"]},{"name":"mdi:cash-clock","tags":["banking","currency","date / time","cash schedule","payment schedule","payment clock","auto pay"]},{"name":"mdi:cash-fast","tags":["currency","banking","instant deposit","instant transfer","instant cash"]},{"name":"mdi:cash-lock","tags":["lock","currency","banking"]},{"name":"mdi:cash-lock-open","tags":["lock","currency","banking"]},{"name":"mdi:cash-marker","tags":["banking","currency","navigation","cod","cash on delivery","cash location"]},{"name":"mdi:cash-minus","tags":["currency","banking"]},{"name":"mdi:cash-multiple","tags":["currency","banking","money"]},{"name":"mdi:cash-off","tags":["currency","banking"]},{"name":"mdi:cash-plus","tags":["currency","banking"]},{"name":"mdi:cash-refund","tags":["banking","currency","cash return","cash chargeback"]},{"name":"mdi:cash-register","tags":["shopping","banking","till"]},{"name":"mdi:cash-remove","tags":["currency","banking"]},{"name":"mdi:cash-sync","tags":["banking","currency","auto pay","recurring payment","scheduled payment","cash cycle"]},{"name":"mdi:cassette","tags":["music","tape"]},{"name":"mdi:cast-audio","tags":["audio","cast speaker"]},{"name":"mdi:cast-audio-variant","tags":["apple airplay"]},{"name":"mdi:cast-off","tags":["home automation"]},{"name":"mdi:castle","tags":["places"]},{"name":"mdi:cat","tags":["animal","holiday","emoji cat","emoticon cat"]},{"name":"mdi:cctv","tags":["home automation","closed circuit television","security camera"]},{"name":"mdi:cctv-off","tags":["home automation","closed circuit television off","security camera off"]},{"name":"mdi:ceiling-fan","tags":["home automation"]},{"name":"mdi:ceiling-fan-light","tags":["home automation","ceiling fan on"]},{"name":"mdi:ceiling-light","tags":["home automation","ceiling lamp"]},{"name":"mdi:ceiling-light-multiple","tags":["home automation","ceiling lamp multiple"]},{"name":"mdi:ceiling-light-multiple-outline","tags":["home automation","ceiling lamp multiple outline"]},{"name":"mdi:ceiling-light-outline","tags":["home automation"]},{"name":"mdi:cellphone-arrow-down-variant","tags":["cellphone / phone","cellphone download"]},{"name":"mdi:cellphone-basic","tags":["cellphone / phone","device / tech","mobile phone basic"]},{"name":"mdi:cellphone-charging","tags":["cellphone / phone"]},{"name":"mdi:cellphone-check","tags":["cellphone / phone"]},{"name":"mdi:cellphone-key","tags":["cellphone / phone","device / tech","mobile phone key","smartphone key"]},{"name":"mdi:cellphone-marker","tags":["cellphone / phone","navigation","cellphone location","cellphone map","find my phone","cellphone gps"]},{"name":"mdi:cellphone-message","tags":["cellphone / phone","device / tech","mobile phone message","smartphone message"]},{"name":"mdi:cellphone-message-off","tags":["cellphone / phone"]},{"name":"mdi:cellphone-nfc-off","tags":["cellphone / phone"]},{"name":"mdi:cellphone-remove","tags":["cellphone / phone","device / tech","phonelink erase","mobile phone erase","smartphone erase","cellphone erase"]},{"name":"mdi:cellphone-text","tags":["cellphone / phone","device / tech","mobile phone text","smartphone text"]},{"name":"mdi:cellphone-wireless","tags":["cellphone / phone","device / tech","mobile phone wireless","smartphone wireless"]},{"name":"mdi:certificate","tags":["diploma","seal"]},{"name":"mdi:certificate-outline","tags":["diploma outline","seal outline"]},{"name":"mdi:chair-rolling","tags":["home automation","office chair","study chair"]},{"name":"mdi:chair-school","tags":["desk","education","learn"]},{"name":"mdi:chandelier","tags":["home automation","ceiling light","girandole","candelabra lamp","suspended light"]},{"name":"mdi:chart-arc","tags":["math","report arc","widget arc"]},{"name":"mdi:chart-areaspline","tags":["math","report areaspline","widget areaspline","graph areaspline"]},{"name":"mdi:chart-areaspline-variant","tags":["math","report areaspline variant","widget areaspline variant","graph areaspline variant"]},{"name":"mdi:chart-bar","tags":["math","report bar","widget bar","graph bar"]},{"name":"mdi:chart-bar-stacked","tags":["math","report bar stacked","widget bar stacked","graph bar stacked"]},{"name":"mdi:chart-bell-curve","tags":["math","report bell curve","widget bell curve","graph bell curve"]},{"name":"mdi:chart-bell-curve-cumulative","tags":["math","report bell curve cumulative","widget bell curve cumulative","graph bell curve cumulative"]},{"name":"mdi:chart-donut-variant","tags":["math","chart doughnut variant","report donut variant","widget donut variant"]},{"name":"mdi:chart-gantt","tags":["math","report gantt","timeline","widget gantt","roadmap"]},{"name":"mdi:chart-histogram","tags":["math","report histogram","widget histogram","graph histogram"]},{"name":"mdi:chart-line","tags":["math","report line","widget line","graph line"]},{"name":"mdi:chart-line-stacked","tags":["math","report line stacked","widget line stacked","graph line stacked"]},{"name":"mdi:chart-multiple","tags":["math","report multiple","widget multiple","graph multiple"]},{"name":"mdi:chart-ppf","tags":["math","chart production possibility frontier","report ppf","widget ppf","graph ppf"]},{"name":"mdi:chart-sankey","tags":["math","chart snakey","report sankey","widget sankey","graph sankey"]},{"name":"mdi:chart-sankey-variant","tags":["math","chart snakey variant","report sankey variant","widget sankey variant","graph sankey variant"]},{"name":"mdi:chart-scatter-plot","tags":["math","report scatter plot","widget scatter plot","graph scatter plot"]},{"name":"mdi:chart-scatter-plot-hexbin","tags":["math","chart scatterplot hexbin","report scatter plot hexbin","widget scatter plot hexbin","graph scatter plot hexbin"]},{"name":"mdi:chart-timeline","tags":["math","report timeline","widget timeline","graph timeline","roadmap"]},{"name":"mdi:chart-waterfall","tags":["math"]},{"name":"mdi:chat","tags":[]},{"name":"mdi:chat-alert","tags":["alert / error","chat warning"]},{"name":"mdi:chat-alert-outline","tags":["alert / error"]},{"name":"mdi:chat-minus","tags":[]},{"name":"mdi:chat-minus-outline","tags":[]},{"name":"mdi:chat-outline","tags":[]},{"name":"mdi:chat-plus","tags":[]},{"name":"mdi:chat-plus-outline","tags":[]},{"name":"mdi:chat-processing","tags":["chat typing"]},{"name":"mdi:chat-processing-outline","tags":["chat typing outline"]},{"name":"mdi:chat-question","tags":["chat help"]},{"name":"mdi:chat-question-outline","tags":["chat help outline"]},{"name":"mdi:chat-remove","tags":[]},{"name":"mdi:chat-remove-outline","tags":[]},{"name":"mdi:chat-sleep","tags":[]},{"name":"mdi:chat-sleep-outline","tags":[]},{"name":"mdi:check-bold","tags":["check thick","success thick","success bold"]},{"name":"mdi:check-decagram","tags":["verified","decagram check","approve","approval","tick decagram"]},{"name":"mdi:check-decagram-outline","tags":["approve","approval","verified"]},{"name":"mdi:check-network","tags":["tick network"]},{"name":"mdi:check-network-outline","tags":["tick network outline"]},{"name":"mdi:check-underline","tags":[]},{"name":"mdi:check-underline-circle","tags":[]},{"name":"mdi:check-underline-circle-outline","tags":[]},{"name":"mdi:checkbook-arrow-left","tags":["banking","chequebook arrow left"]},{"name":"mdi:checkbook-arrow-right","tags":["banking","chequebook arrow right"]},{"name":"mdi:checkbox-blank-badge","tags":["notification","form","checkbox blank notification","app notification","app badge"]},{"name":"mdi:checkbox-blank-badge-outline","tags":["notification","form","checkbox blank notification outline","app notification outline","app badge outline"]},{"name":"mdi:checkbox-blank-off","tags":["form"]},{"name":"mdi:checkbox-blank-off-outline","tags":["form"]},{"name":"mdi:checkbox-intermediate","tags":["form","checkbox indeterminate"]},{"name":"mdi:checkbox-intermediate-variant","tags":["form","checkbox indeterminate variant"]},{"name":"mdi:checkbox-marked-circle-auto-outline","tags":["form","task auto","todo auto"]},{"name":"mdi:checkbox-marked-circle-minus-outline","tags":["form","todo minus","task minus"]},{"name":"mdi:checkbox-marked-circle-plus-outline","tags":["form","task plus","task add","todo plus","todo add"]},{"name":"mdi:checkbox-multiple-blank","tags":["form","checkboxes blank"]},{"name":"mdi:checkbox-multiple-blank-circle","tags":["form","checkboxes blank circle"]},{"name":"mdi:checkbox-multiple-blank-circle-outline","tags":["form","checkboxes blank circle outline"]},{"name":"mdi:checkbox-multiple-blank-outline","tags":["form","checkboxes blank outline"]},{"name":"mdi:checkbox-multiple-marked","tags":["form","checkboxes marked"]},{"name":"mdi:checkbox-multiple-marked-circle","tags":["form","checkboxes marked circle"]},{"name":"mdi:checkbox-multiple-marked-circle-outline","tags":["form","checkboxes marked circle outline"]},{"name":"mdi:checkbox-multiple-marked-outline","tags":["form","checkboxes marked outline"]},{"name":"mdi:checkbox-multiple-outline","tags":["form","check boxes outline","tick box multiple outline"]},{"name":"mdi:checkbox-outline","tags":["form"]},{"name":"mdi:checkerboard","tags":["gaming / rpg","geographic information system","raster"]},{"name":"mdi:checkerboard-minus","tags":["geographic information system","raster minus"]},{"name":"mdi:checkerboard-plus","tags":["geographic information system","raster plus"]},{"name":"mdi:checkerboard-remove","tags":["geographic information system","raster remove"]},{"name":"mdi:cheese","tags":["food / drink","swiss cheese"]},{"name":"mdi:cheese-off","tags":["food / drink"]},{"name":"mdi:chef-hat","tags":["clothing","toque","cook"]},{"name":"mdi:chemical-weapon","tags":[]},{"name":"mdi:chess-bishop","tags":["gaming / rpg"]},{"name":"mdi:chess-king","tags":["gaming / rpg","crown","royalty"]},{"name":"mdi:chess-knight","tags":["gaming / rpg","chess horse"]},{"name":"mdi:chess-pawn","tags":["gaming / rpg"]},{"name":"mdi:chess-queen","tags":["gaming / rpg","crown","royalty"]},{"name":"mdi:chess-rook","tags":["gaming / rpg","chess castle","chess tower"]},{"name":"mdi:chevron-double-down","tags":["arrow"]},{"name":"mdi:chevron-double-left","tags":["arrow"]},{"name":"mdi:chevron-double-right","tags":["arrow"]},{"name":"mdi:chevron-double-up","tags":["arrow"]},{"name":"mdi:chevron-down-box","tags":["form","arrow"]},{"name":"mdi:chevron-down-box-outline","tags":["form","arrow"]},{"name":"mdi:chevron-down-circle","tags":["arrow"]},{"name":"mdi:chevron-down-circle-outline","tags":["arrow"]},{"name":"mdi:chevron-left-box","tags":["arrow"]},{"name":"mdi:chevron-left-box-outline","tags":["arrow"]},{"name":"mdi:chevron-left-circle","tags":["arrow"]},{"name":"mdi:chevron-left-circle-outline","tags":["arrow"]},{"name":"mdi:chevron-right-box","tags":["arrow"]},{"name":"mdi:chevron-right-box-outline","tags":["arrow"]},{"name":"mdi:chevron-right-circle","tags":["arrow"]},{"name":"mdi:chevron-right-circle-outline","tags":["arrow"]},{"name":"mdi:chevron-up-box","tags":["arrow"]},{"name":"mdi:chevron-up-box-outline","tags":["arrow"]},{"name":"mdi:chevron-up-circle","tags":["arrow"]},{"name":"mdi:chevron-up-circle-outline","tags":["arrow"]},{"name":"mdi:chili-alert","tags":["alert / error"]},{"name":"mdi:chili-alert-outline","tags":["alert / error"]},{"name":"mdi:chili-hot","tags":["food / drink","chilli hot","pepper","spicy"]},{"name":"mdi:chili-hot-outline","tags":[]},{"name":"mdi:chili-medium","tags":["food / drink","chilli medium","pepper","spicy"]},{"name":"mdi:chili-medium-outline","tags":[]},{"name":"mdi:chili-mild","tags":["food / drink","agriculture","chilli mild","pepper","spicy"]},{"name":"mdi:chili-mild-outline","tags":[]},{"name":"mdi:chili-off","tags":["food / drink","chilli off","pepper off","spicy off"]},{"name":"mdi:chili-off-outline","tags":[]},{"name":"mdi:chip","tags":["integrated circuit"]},{"name":"mdi:cigar","tags":[]},{"name":"mdi:cigar-off","tags":[]},{"name":"mdi:circle","tags":["shape","lens"]},{"name":"mdi:circle-box","tags":[]},{"name":"mdi:circle-box-outline","tags":[]},{"name":"mdi:circle-double","tags":["shape"]},{"name":"mdi:circle-half","tags":["shape","brightness half"]},{"name":"mdi:circle-half-full","tags":["shape"]},{"name":"mdi:circle-medium","tags":[]},{"name":"mdi:circle-multiple","tags":["currency","banking","coins"]},{"name":"mdi:circle-off-outline","tags":["null off"]},{"name":"mdi:circle-opacity","tags":["shape","drawing / art","circle transparent"]},{"name":"mdi:circle-outline","tags":["shape","null"]},{"name":"mdi:circle-slice-1","tags":[]},{"name":"mdi:circle-slice-2","tags":[]},{"name":"mdi:circle-slice-3","tags":[]},{"name":"mdi:circle-slice-4","tags":[]},{"name":"mdi:circle-slice-5","tags":[]},{"name":"mdi:circle-slice-6","tags":[]},{"name":"mdi:circle-slice-7","tags":[]},{"name":"mdi:circle-slice-8","tags":[]},{"name":"mdi:circle-small","tags":["math","bullet","multiplication","dot"]},{"name":"mdi:circular-saw","tags":["hardware / tools"]},{"name":"mdi:city-switch","tags":["places","city swap"]},{"name":"mdi:city-variant","tags":["places"]},{"name":"mdi:city-variant-outline","tags":["places"]},{"name":"mdi:clipboard","tags":[]},{"name":"mdi:clipboard-account-outline","tags":["account / user","clipboard user outline","clipboard person outline","assignment ind outline"]},{"name":"mdi:clipboard-alert-outline","tags":["alert / error","clipboard warning outline"]},{"name":"mdi:clipboard-arrow-left-outline","tags":[]},{"name":"mdi:clipboard-arrow-right","tags":[]},{"name":"mdi:clipboard-arrow-right-outline","tags":[]},{"name":"mdi:clipboard-arrow-up","tags":["clipboard arrow top"]},{"name":"mdi:clipboard-arrow-up-outline","tags":["clipboard arrow top outline"]},{"name":"mdi:clipboard-check-multiple","tags":[]},{"name":"mdi:clipboard-check-multiple-outline","tags":[]},{"name":"mdi:clipboard-check-outline","tags":["clipboard tick outline"]},{"name":"mdi:clipboard-clock","tags":["date / time"]},{"name":"mdi:clipboard-clock-outline","tags":["date / time"]},{"name":"mdi:clipboard-edit","tags":["edit / modify"]},{"name":"mdi:clipboard-edit-outline","tags":["edit / modify"]},{"name":"mdi:clipboard-file","tags":["files / folders"]},{"name":"mdi:clipboard-file-outline","tags":["files / folders"]},{"name":"mdi:clipboard-flow","tags":[]},{"name":"mdi:clipboard-flow-outline","tags":[]},{"name":"mdi:clipboard-list","tags":[]},{"name":"mdi:clipboard-list-outline","tags":[]},{"name":"mdi:clipboard-minus","tags":[]},{"name":"mdi:clipboard-minus-outline","tags":[]},{"name":"mdi:clipboard-multiple","tags":[]},{"name":"mdi:clipboard-multiple-outline","tags":[]},{"name":"mdi:clipboard-off","tags":[]},{"name":"mdi:clipboard-off-outline","tags":[]},{"name":"mdi:clipboard-outline","tags":[]},{"name":"mdi:clipboard-play","tags":[]},{"name":"mdi:clipboard-play-multiple","tags":[]},{"name":"mdi:clipboard-play-multiple-outline","tags":[]},{"name":"mdi:clipboard-play-outline","tags":[]},{"name":"mdi:clipboard-plus","tags":["clipboard add"]},{"name":"mdi:clipboard-plus-outline","tags":[]},{"name":"mdi:clipboard-pulse","tags":["medical / hospital","clipboard vitals"]},{"name":"mdi:clipboard-pulse-outline","tags":["medical / hospital","clipboard vitals outline"]},{"name":"mdi:clipboard-remove","tags":[]},{"name":"mdi:clipboard-remove-outline","tags":[]},{"name":"mdi:clipboard-search","tags":[]},{"name":"mdi:clipboard-search-outline","tags":[]},{"name":"mdi:clipboard-text-clock","tags":["date / time","clipboard text date","clipboard text time","clipboard text history"]},{"name":"mdi:clipboard-text-clock-outline","tags":["date / time","clipboard text date outline","clipboard text time outline","clipboard text history outline"]},{"name":"mdi:clipboard-text-multiple","tags":[]},{"name":"mdi:clipboard-text-multiple-outline","tags":[]},{"name":"mdi:clipboard-text-off","tags":[]},{"name":"mdi:clipboard-text-off-outline","tags":[]},{"name":"mdi:clipboard-text-outline","tags":[]},{"name":"mdi:clipboard-text-play","tags":[]},{"name":"mdi:clipboard-text-play-outline","tags":[]},{"name":"mdi:clipboard-text-search","tags":[]},{"name":"mdi:clipboard-text-search-outline","tags":[]},{"name":"mdi:clippy","tags":[]},{"name":"mdi:clock-alert","tags":["date / time","alert / error","clock warning"]},{"name":"mdi:clock-alert-outline","tags":["date / time","alert / error","clock warning"]},{"name":"mdi:clock-check","tags":["date / time"]},{"name":"mdi:clock-check-outline","tags":["date / time"]},{"name":"mdi:clock-digital","tags":["date / time","home automation"]},{"name":"mdi:clock-edit","tags":["date / time","edit / modify"]},{"name":"mdi:clock-edit-outline","tags":["date / time","edit / modify"]},{"name":"mdi:clock-end","tags":["date / time"]},{"name":"mdi:clock-fast","tags":["date / time","velocity"]},{"name":"mdi:clock-in","tags":["date / time"]},{"name":"mdi:clock-minus","tags":["date / time"]},{"name":"mdi:clock-minus-outline","tags":["date / time"]},{"name":"mdi:clock-out","tags":["date / time"]},{"name":"mdi:clock-plus","tags":["date / time"]},{"name":"mdi:clock-plus-outline","tags":["date / time"]},{"name":"mdi:clock-remove","tags":["date / time"]},{"name":"mdi:clock-remove-outline","tags":["date / time"]},{"name":"mdi:clock-star-four-points","tags":["date / time","clock auto"]},{"name":"mdi:clock-star-four-points-outline","tags":["date / time","clock auto outline"]},{"name":"mdi:clock-start","tags":["date / time"]},{"name":"mdi:clock-time-eight","tags":["date / time"]},{"name":"mdi:clock-time-eight-outline","tags":["date / time"]},{"name":"mdi:clock-time-eleven","tags":["date / time"]},{"name":"mdi:clock-time-eleven-outline","tags":["date / time"]},{"name":"mdi:clock-time-five","tags":["date / time"]},{"name":"mdi:clock-time-five-outline","tags":["date / time"]},{"name":"mdi:clock-time-four","tags":["date / time"]},{"name":"mdi:clock-time-four-outline","tags":["date / time"]},{"name":"mdi:clock-time-nine","tags":["date / time"]},{"name":"mdi:clock-time-nine-outline","tags":["date / time"]},{"name":"mdi:clock-time-one","tags":["date / time"]},{"name":"mdi:clock-time-one-outline","tags":["date / time"]},{"name":"mdi:clock-time-seven","tags":["date / time"]},{"name":"mdi:clock-time-seven-outline","tags":["date / time"]},{"name":"mdi:clock-time-six","tags":["date / time"]},{"name":"mdi:clock-time-six-outline","tags":["date / time"]},{"name":"mdi:clock-time-ten","tags":["date / time"]},{"name":"mdi:clock-time-ten-outline","tags":["date / time"]},{"name":"mdi:clock-time-three","tags":["date / time"]},{"name":"mdi:clock-time-three-outline","tags":["date / time"]},{"name":"mdi:clock-time-twelve","tags":["date / time"]},{"name":"mdi:clock-time-twelve-outline","tags":["date / time"]},{"name":"mdi:clock-time-two","tags":["date / time"]},{"name":"mdi:clock-time-two-outline","tags":["date / time"]},{"name":"mdi:close-box","tags":["math","form","multiply box","clear box","cancel box","remove box"]},{"name":"mdi:close-box-multiple","tags":["form","close boxes","library remove","library close","multiply boxes","multiply box multiple","cancel box multiple","remove box multiple"]},{"name":"mdi:close-box-multiple-outline","tags":["form","close boxes outline","library remove outline","library close outline","multiply boxes outline","multiply box multiple outline","remove box multiple","cancel box multiple"]},{"name":"mdi:close-box-outline","tags":["math","form","multiply box outline","clear box outline","remove box outline","cancel box outline"]},{"name":"mdi:close-circle","tags":["form","remove circle","cancel circle","multiply circle","clear circle"]},{"name":"mdi:close-circle-multiple","tags":["form","remove circle multiple","coins close","coins remove","clear circle multiple","multiply circle multiple"]},{"name":"mdi:close-circle-multiple-outline","tags":["form","remove circle multiple outline","coins close outline","coins remove outline","cancel circle multiple outline","multiply circle multiple outline","clear circle multiple outline"]},{"name":"mdi:close-network","tags":["remove network","cancel network","multiply network","clear network"]},{"name":"mdi:close-network-outline","tags":["remove network outline","cancel network outline","multiply network outline","clear network outline"]},{"name":"mdi:close-octagon","tags":["dangerous","multiply octagon","remove octagon","cancel octagon","clear octagon","stop remove"]},{"name":"mdi:close-octagon-outline","tags":["remove octagon outline","multiply octagon outline","clear octagon outline","cancel octagon outline","stop remove outline"]},{"name":"mdi:close-outline","tags":["remove outline","cancel outline","multiply outline","clear outline"]},{"name":"mdi:close-thick","tags":["close bold","remove thick","remove bold","multiply thick","multiply bold","clear thick","clear bold","cancel thick","cancel bold"]},{"name":"mdi:cloud-alert","tags":["alert / error","cloud","weather","cloud warning"]},{"name":"mdi:cloud-alert-outline","tags":["alert / error","weather","cloud"]},{"name":"mdi:cloud-arrow-down","tags":["cloud","weather"]},{"name":"mdi:cloud-arrow-down-outline","tags":["cloud","weather"]},{"name":"mdi:cloud-arrow-left","tags":["weather","cloud"]},{"name":"mdi:cloud-arrow-left-outline","tags":["weather","cloud"]},{"name":"mdi:cloud-arrow-right","tags":["weather","cloud"]},{"name":"mdi:cloud-arrow-right-outline","tags":["weather","cloud"]},{"name":"mdi:cloud-arrow-up","tags":["cloud","weather"]},{"name":"mdi:cloud-arrow-up-outline","tags":["weather","cloud"]},{"name":"mdi:cloud-braces","tags":["cloud","developer / languages","cloud json"]},{"name":"mdi:cloud-cancel","tags":["cloud","weather"]},{"name":"mdi:cloud-cancel-outline","tags":["cloud","weather"]},{"name":"mdi:cloud-check","tags":["cloud","weather"]},{"name":"mdi:cloud-check-outline","tags":["cloud","weather"]},{"name":"mdi:cloud-check-variant-outline","tags":["cloud","weather"]},{"name":"mdi:cloud-clock","tags":["weather","cloud"]},{"name":"mdi:cloud-clock-outline","tags":["cloud","weather"]},{"name":"mdi:cloud-cog","tags":["cloud","weather"]},{"name":"mdi:cloud-cog-outline","tags":["cloud","weather"]},{"name":"mdi:cloud-lock","tags":["cloud","lock"]},{"name":"mdi:cloud-lock-open","tags":["cloud"]},{"name":"mdi:cloud-lock-open-outline","tags":["cloud"]},{"name":"mdi:cloud-lock-outline","tags":["cloud","lock"]},{"name":"mdi:cloud-minus","tags":["cloud"]},{"name":"mdi:cloud-minus-outline","tags":["cloud"]},{"name":"mdi:cloud-percent","tags":["weather","cloud","nature","humidity","rain chance","cloud discount"]},{"name":"mdi:cloud-percent-outline","tags":["weather","cloud","nature","cloud discount outline","humidity outline","rain chance outline"]},{"name":"mdi:cloud-plus","tags":["cloud"]},{"name":"mdi:cloud-plus-outline","tags":["cloud"]},{"name":"mdi:cloud-print","tags":["cloud","printer","home automation"]},{"name":"mdi:cloud-print-outline","tags":["cloud","printer","home automation"]},{"name":"mdi:cloud-question","tags":["cloud","weather"]},{"name":"mdi:cloud-question-outline","tags":["cloud","weather"]},{"name":"mdi:cloud-refresh","tags":["cloud","weather"]},{"name":"mdi:cloud-refresh-outline","tags":["cloud","weather"]},{"name":"mdi:cloud-refresh-variant","tags":["cloud","weather"]},{"name":"mdi:cloud-refresh-variant-outline","tags":["cloud","weather"]},{"name":"mdi:cloud-remove","tags":["cloud"]},{"name":"mdi:cloud-remove-outline","tags":["cloud","weather"]},{"name":"mdi:cloud-search","tags":["cloud","weather"]},{"name":"mdi:cloud-search-outline","tags":["cloud","weather"]},{"name":"mdi:cloud-sync","tags":["cloud","weather"]},{"name":"mdi:cloud-sync-outline","tags":["cloud","weather"]},{"name":"mdi:cloud-tags","tags":["cloud","cloud xml"]},{"name":"mdi:clouds","tags":["weather","cloud"]},{"name":"mdi:clover","tags":["nature","luck"]},{"name":"mdi:clover-outline","tags":["nature","luck outline"]},{"name":"mdi:coach-lamp","tags":["home automation","coach light","carriage lamp","carriage light"]},{"name":"mdi:coach-lamp-variant","tags":["home automation","coach light","carriage light","carriage lamp"]},{"name":"mdi:coat-rack","tags":["home automation","clothing","foyer","hallway","entry room"]},{"name":"mdi:code-array","tags":["developer / languages"]},{"name":"mdi:code-braces","tags":["developer / languages","math","set"]},{"name":"mdi:code-braces-box","tags":["developer / languages"]},{"name":"mdi:code-brackets","tags":["developer / languages","math","square brackets"]},{"name":"mdi:code-equal","tags":["developer / languages"]},{"name":"mdi:code-greater-than","tags":["developer / languages","math"]},{"name":"mdi:code-greater-than-or-equal","tags":["developer / languages","math"]},{"name":"mdi:code-json","tags":["developer / languages"]},{"name":"mdi:code-less-than","tags":["developer / languages","math"]},{"name":"mdi:code-less-than-or-equal","tags":["developer / languages","math"]},{"name":"mdi:code-not-equal","tags":["developer / languages"]},{"name":"mdi:code-not-equal-variant","tags":["developer / languages"]},{"name":"mdi:code-parentheses","tags":["developer / languages"]},{"name":"mdi:code-parentheses-box","tags":["developer / languages"]},{"name":"mdi:code-string","tags":["developer / languages"]},{"name":"mdi:code-tags-check","tags":["developer / languages","code tags tick"]},{"name":"mdi:coffee-maker-check","tags":["home automation","food / drink","coffee maker done","coffee maker complete"]},{"name":"mdi:coffee-maker-check-outline","tags":["home automation","food / drink","coffee maker complete outline","coffee maker done outline"]},{"name":"mdi:coffee-off","tags":["food / drink","drink off","tea off","cup off","free breakfast off","local cafe off"]},{"name":"mdi:coffee-off-outline","tags":["food / drink","drink off outline","cup off outline","tea off outline","free breakfast off outline","local cafe off outline"]},{"name":"mdi:coffee-to-go","tags":["food / drink","tea to go","drink to go","cup to go","free breakfast to go","local cafe to go"]},{"name":"mdi:coffee-to-go-outline","tags":["food / drink","tea to go outline","cup to go outline","drink to go outline","free breakfast to go outline","local cafe to go outline"]},{"name":"mdi:coffin","tags":["holiday","death","dead"]},{"name":"mdi:cog-clockwise","tags":["settings"]},{"name":"mdi:cog-counterclockwise","tags":["settings"]},{"name":"mdi:cog-off","tags":["settings","settings off"]},{"name":"mdi:cog-off-outline","tags":["settings","settings off outline"]},{"name":"mdi:cog-pause","tags":["settings","settings pause","gear pause"]},{"name":"mdi:cog-pause-outline","tags":["settings","settings pause outline","gear pause outline"]},{"name":"mdi:cog-play","tags":["settings","settings play","gear play"]},{"name":"mdi:cog-play-outline","tags":["settings","settings play outline","gear play outline"]},{"name":"mdi:cog-refresh","tags":["settings","settings refresh"]},{"name":"mdi:cog-refresh-outline","tags":["settings","settings refresh outline"]},{"name":"mdi:cog-stop","tags":["settings","settings stop","gear stop"]},{"name":"mdi:cog-stop-outline","tags":["settings","settings stop outline","gear stop outline"]},{"name":"mdi:cog-sync","tags":["settings","settings sync"]},{"name":"mdi:cog-sync-outline","tags":["settings","settings sync outline"]},{"name":"mdi:cog-transfer","tags":["settings","settings transfer"]},{"name":"mdi:cog-transfer-outline","tags":["settings","settings transfer outline"]},{"name":"mdi:collapse-all","tags":["animation minus"]},{"name":"mdi:collapse-all-outline","tags":["animation minus outline"]},{"name":"mdi:comma","tags":[]},{"name":"mdi:comma-box","tags":[]},{"name":"mdi:comma-box-outline","tags":[]},{"name":"mdi:comma-circle","tags":[]},{"name":"mdi:comma-circle-outline","tags":[]},{"name":"mdi:comment","tags":[]},{"name":"mdi:comment-account","tags":["account / user","comment user","comment person"]},{"name":"mdi:comment-account-outline","tags":["account / user","comment user outline","comment person outline"]},{"name":"mdi:comment-alert","tags":["alert / error","comment warning"]},{"name":"mdi:comment-alert-outline","tags":["alert / error","comment warning outline"]},{"name":"mdi:comment-arrow-left","tags":["comment previous"]},{"name":"mdi:comment-arrow-left-outline","tags":["comment previous outline"]},{"name":"mdi:comment-arrow-right","tags":["comment next"]},{"name":"mdi:comment-arrow-right-outline","tags":["comment next outline"]},{"name":"mdi:comment-bookmark","tags":[]},{"name":"mdi:comment-bookmark-outline","tags":[]},{"name":"mdi:comment-check","tags":["comment tick"]},{"name":"mdi:comment-check-outline","tags":["comment tick outline"]},{"name":"mdi:comment-edit","tags":["edit / modify"]},{"name":"mdi:comment-edit-outline","tags":["edit / modify"]},{"name":"mdi:comment-eye","tags":[]},{"name":"mdi:comment-eye-outline","tags":[]},{"name":"mdi:comment-flash","tags":["comment quick"]},{"name":"mdi:comment-flash-outline","tags":["comment quick outline"]},{"name":"mdi:comment-minus","tags":[]},{"name":"mdi:comment-minus-outline","tags":[]},{"name":"mdi:comment-multiple","tags":["comments"]},{"name":"mdi:comment-multiple-outline","tags":["comments outline"]},{"name":"mdi:comment-off","tags":[]},{"name":"mdi:comment-off-outline","tags":[]},{"name":"mdi:comment-outline","tags":[]},{"name":"mdi:comment-plus","tags":["comment add"]},{"name":"mdi:comment-plus-outline","tags":["comment add outline"]},{"name":"mdi:comment-processing","tags":[]},{"name":"mdi:comment-processing-outline","tags":[]},{"name":"mdi:comment-question","tags":["comment help"]},{"name":"mdi:comment-question-outline","tags":["comment help outline"]},{"name":"mdi:comment-quote","tags":["feedback"]},{"name":"mdi:comment-quote-outline","tags":["feedback outline"]},{"name":"mdi:comment-remove","tags":[]},{"name":"mdi:comment-remove-outline","tags":[]},{"name":"mdi:comment-search","tags":[]},{"name":"mdi:comment-search-outline","tags":[]},{"name":"mdi:comment-text","tags":[]},{"name":"mdi:comment-text-multiple","tags":["comments text"]},{"name":"mdi:comment-text-multiple-outline","tags":["comments text outline"]},{"name":"mdi:comment-text-outline","tags":[]},{"name":"mdi:compare-remove","tags":[]},{"name":"mdi:compass-outline","tags":["navigation","geographic information system"]},{"name":"mdi:compass-rose","tags":["navigation"]},{"name":"mdi:cone","tags":["shape"]},{"name":"mdi:cone-off","tags":["shape"]},{"name":"mdi:connection","tags":["home automation","plug"]},{"name":"mdi:console","tags":["terminal"]},{"name":"mdi:console-line","tags":["terminal line"]},{"name":"mdi:console-network","tags":["terminal network"]},{"name":"mdi:console-network-outline","tags":["terminal network outline"]},{"name":"mdi:consolidate","tags":[]},{"name":"mdi:contactless-payment","tags":["currency"]},{"name":"mdi:contactless-payment-circle-outline","tags":["currency"]},{"name":"mdi:contain","tags":[]},{"name":"mdi:contain-end","tags":[]},{"name":"mdi:contain-start","tags":[]},{"name":"mdi:content-duplicate","tags":[]},{"name":"mdi:content-save-alert","tags":["alert / error","floppy disc alert"]},{"name":"mdi:content-save-alert-outline","tags":["alert / error","floppy disc alert outline"]},{"name":"mdi:content-save-all","tags":["floppy disc multiple"]},{"name":"mdi:content-save-all-outline","tags":["floppy disc multiple outline"]},{"name":"mdi:content-save-check","tags":[]},{"name":"mdi:content-save-check-outline","tags":[]},{"name":"mdi:content-save-cog","tags":["settings","floppy disc cog"]},{"name":"mdi:content-save-cog-outline","tags":["settings","floppy disc cog outline"]},{"name":"mdi:content-save-edit","tags":["edit / modify","floppy disc edit"]},{"name":"mdi:content-save-edit-outline","tags":["edit / modify","floppy disc edit outline"]},{"name":"mdi:content-save-minus","tags":[]},{"name":"mdi:content-save-minus-outline","tags":[]},{"name":"mdi:content-save-move","tags":["floppy disc move"]},{"name":"mdi:content-save-move-outline","tags":["floppy disc move outline"]},{"name":"mdi:content-save-off","tags":[]},{"name":"mdi:content-save-off-outline","tags":[]},{"name":"mdi:content-save-plus","tags":["content save add"]},{"name":"mdi:content-save-plus-outline","tags":["content save add outline"]},{"name":"mdi:content-save-settings","tags":["settings","floppy disc settings"]},{"name":"mdi:content-save-settings-outline","tags":["settings","floppy disc settings outline"]},{"name":"mdi:contrast","tags":[]},{"name":"mdi:controller-classic","tags":["gaming / rpg","gamepad classic"]},{"name":"mdi:controller-classic-outline","tags":["gaming / rpg","gamepad classic outline"]},{"name":"mdi:cookie-alert","tags":["food / drink","alert / error","biscuit alert"]},{"name":"mdi:cookie-alert-outline","tags":["food / drink","alert / error","biscuit alert outline"]},{"name":"mdi:cookie-check","tags":["food / drink","biscuit check"]},{"name":"mdi:cookie-check-outline","tags":["food / drink","biscuit check outline"]},{"name":"mdi:cookie-clock","tags":["food / drink","date / time","biscuit clock"]},{"name":"mdi:cookie-clock-outline","tags":["food / drink","date / time","biscuit clock outline"]},{"name":"mdi:cookie-cog","tags":["food / drink","settings","biscuit cog"]},{"name":"mdi:cookie-cog-outline","tags":["food / drink","settings","biscuit cog outline"]},{"name":"mdi:cookie-edit","tags":["food / drink","edit / modify","biscuit edit"]},{"name":"mdi:cookie-edit-outline","tags":["food / drink","edit / modify","biscuit edit outline"]},{"name":"mdi:cookie-lock","tags":["food / drink","lock","biscuit lock"]},{"name":"mdi:cookie-lock-outline","tags":["food / drink","lock","biscuit lock outline"]},{"name":"mdi:cookie-minus","tags":["food / drink","biscuit minus"]},{"name":"mdi:cookie-minus-outline","tags":["food / drink","biscuit minus outline"]},{"name":"mdi:cookie-off","tags":["food / drink","biscuit off"]},{"name":"mdi:cookie-off-outline","tags":["food / drink","biscuit off outline"]},{"name":"mdi:cookie-outline","tags":["food / drink","biscuit outline"]},{"name":"mdi:cookie-plus","tags":["food / drink","biscuit plus"]},{"name":"mdi:cookie-plus-outline","tags":["food / drink","biscuit plus outline"]},{"name":"mdi:cookie-refresh","tags":["food / drink","biscuit refresh"]},{"name":"mdi:cookie-refresh-outline","tags":["food / drink","biscuit refresh outline"]},{"name":"mdi:cookie-remove","tags":["food / drink","biscuit remove"]},{"name":"mdi:cookie-remove-outline","tags":["food / drink","biscuit remove outline"]},{"name":"mdi:cookie-settings","tags":["food / drink","settings","biscuit settings","cookie crumbs","biscuit crumbs"]},{"name":"mdi:cookie-settings-outline","tags":["food / drink","settings","biscuit settings outline","cookie crumbs outline","biscuit crumbs outline"]},{"name":"mdi:coolant-temperature","tags":["automotive"]},{"name":"mdi:copyleft","tags":[]},{"name":"mdi:corn","tags":["agriculture","food / drink"]},{"name":"mdi:corn-off","tags":["food / drink","agriculture"]},{"name":"mdi:cosine-wave","tags":["audio","frequency","amplitude"]},{"name":"mdi:counter","tags":["automotive","score","numbers","odometer"]},{"name":"mdi:cow","tags":["animal","agriculture","emoji cow","emoticon cow"]},{"name":"mdi:cow-off","tags":["food / drink","agriculture","animal","dairy off","dairy free"]},{"name":"mdi:cpu-32-bit","tags":["chip 32 bit"]},{"name":"mdi:cpu-64-bit","tags":["chip 64 bit"]},{"name":"mdi:crane","tags":[]},{"name":"mdi:creation-outline","tags":["auto awesome outline"]},{"name":"mdi:credit-card","tags":["banking","currency"]},{"name":"mdi:credit-card-check","tags":["banking"]},{"name":"mdi:credit-card-check-outline","tags":["banking"]},{"name":"mdi:credit-card-chip","tags":["banking","credit card icc chip"]},{"name":"mdi:credit-card-chip-outline","tags":["banking","credit card icc chip outline"]},{"name":"mdi:credit-card-clock","tags":["banking","date / time"]},{"name":"mdi:credit-card-clock-outline","tags":["banking","date / time"]},{"name":"mdi:credit-card-edit","tags":["edit / modify","banking"]},{"name":"mdi:credit-card-edit-outline","tags":["edit / modify","banking"]},{"name":"mdi:credit-card-fast","tags":["banking","credit card swipe"]},{"name":"mdi:credit-card-fast-outline","tags":["banking","credit card swipe outline"]},{"name":"mdi:credit-card-lock","tags":["banking","lock"]},{"name":"mdi:credit-card-lock-outline","tags":["banking","lock"]},{"name":"mdi:credit-card-marker","tags":["banking","navigation","credit card location","payment on delivery"]},{"name":"mdi:credit-card-marker-outline","tags":["banking","navigation","cod","payment on delivery outline","credit card location outline"]},{"name":"mdi:credit-card-minus","tags":["banking"]},{"name":"mdi:credit-card-minus-outline","tags":["banking"]},{"name":"mdi:credit-card-multiple","tags":["banking"]},{"name":"mdi:credit-card-multiple-outline","tags":["banking","credit cards"]},{"name":"mdi:credit-card-off","tags":["banking"]},{"name":"mdi:credit-card-off-outline","tags":["banking"]},{"name":"mdi:credit-card-plus","tags":["banking"]},{"name":"mdi:credit-card-plus-outline","tags":["banking","credit card add"]},{"name":"mdi:credit-card-refresh","tags":["banking"]},{"name":"mdi:credit-card-refresh-outline","tags":["banking"]},{"name":"mdi:credit-card-refund","tags":["banking"]},{"name":"mdi:credit-card-refund-outline","tags":["banking"]},{"name":"mdi:credit-card-remove","tags":["banking"]},{"name":"mdi:credit-card-remove-outline","tags":["banking"]},{"name":"mdi:credit-card-scan","tags":["banking"]},{"name":"mdi:credit-card-scan-outline","tags":["banking"]},{"name":"mdi:credit-card-search","tags":["banking"]},{"name":"mdi:credit-card-search-outline","tags":["banking"]},{"name":"mdi:credit-card-settings","tags":["banking","settings"]},{"name":"mdi:credit-card-settings-outline","tags":["banking","settings","payment settings"]},{"name":"mdi:credit-card-sync","tags":["banking"]},{"name":"mdi:credit-card-sync-outline","tags":["banking"]},{"name":"mdi:credit-card-wireless","tags":["currency","banking"]},{"name":"mdi:credit-card-wireless-off","tags":["banking"]},{"name":"mdi:credit-card-wireless-off-outline","tags":["banking"]},{"name":"mdi:credit-card-wireless-outline","tags":["currency","banking","credit card contactless"]},{"name":"mdi:cross","tags":["religion","holiday","christianity","religion christian"]},{"name":"mdi:cross-bolnisi","tags":["religion"]},{"name":"mdi:cross-celtic","tags":["religion","holiday"]},{"name":"mdi:cross-outline","tags":["religion","religion christian outline","christianity outline"]},{"name":"mdi:crown","tags":[]},{"name":"mdi:crown-circle","tags":["gaming / rpg","checkers"]},{"name":"mdi:crown-circle-outline","tags":["gaming / rpg","checkers outline"]},{"name":"mdi:crown-outline","tags":[]},{"name":"mdi:crystal-ball","tags":["gaming / rpg"]},{"name":"mdi:cube","tags":["shape"]},{"name":"mdi:cube-off","tags":[]},{"name":"mdi:cube-off-outline","tags":["food / drink","sugar off","sugar cube off","sugar free"]},{"name":"mdi:cube-outline","tags":["shape","food / drink","sugar","sugar cube"]},{"name":"mdi:cube-send","tags":[]},{"name":"mdi:cube-unfolded","tags":[]},{"name":"mdi:cup","tags":["food / drink","glass","drink"]},{"name":"mdi:cup-off","tags":["food / drink","glass off","drink off"]},{"name":"mdi:cup-off-outline","tags":["food / drink","glass off outline","drink off outline"]},{"name":"mdi:cup-outline","tags":["food / drink","glass outline","drink outline","cup empty"]},{"name":"mdi:cupboard","tags":["home automation"]},{"name":"mdi:cupboard-outline","tags":["home automation"]},{"name":"mdi:cupcake","tags":["food / drink"]},{"name":"mdi:curling","tags":["sport"]},{"name":"mdi:currency-bdt","tags":["banking","currency","taka","bangladeshi taka"]},{"name":"mdi:currency-brl","tags":["banking","currency","brazilian real"]},{"name":"mdi:currency-eth","tags":["currency","banking","ethereum","xi"]},{"name":"mdi:currency-eur-off","tags":["currency","banking"]},{"name":"mdi:currency-ils","tags":["banking","currency"]},{"name":"mdi:currency-inr","tags":["currency","banking","rupee"]},{"name":"mdi:currency-krw","tags":["currency","banking","won"]},{"name":"mdi:currency-kzt","tags":["banking","currency","kazakhstani tenge"]},{"name":"mdi:currency-mnt","tags":["currency","banking","currency mongolian tugrug"]},{"name":"mdi:currency-ngn","tags":["currency","banking","naira"]},{"name":"mdi:currency-php","tags":["banking","currency","philippine peso"]},{"name":"mdi:currency-rial","tags":["currency","banking","currency riyal","currency irr","currency omr","currency yer","currency sar"]},{"name":"mdi:currency-sign","tags":["currency","banking","currency scarab"]},{"name":"mdi:currency-thb","tags":["banking","currency thai baht"]},{"name":"mdi:currency-twd","tags":["currency","banking","new taiwan dollar"]},{"name":"mdi:currency-uah","tags":["banking","currency hryvnia","currency ukraine"]},{"name":"mdi:current-ac","tags":["alternating current"]},{"name":"mdi:current-dc","tags":["battery","direct current"]},{"name":"mdi:cursor-default","tags":[]},{"name":"mdi:cursor-default-click","tags":[]},{"name":"mdi:cursor-default-click-outline","tags":[]},{"name":"mdi:cursor-default-gesture","tags":[]},{"name":"mdi:cursor-default-gesture-outline","tags":[]},{"name":"mdi:cursor-default-outline","tags":[]},{"name":"mdi:cursor-move","tags":[]},{"name":"mdi:cursor-pointer","tags":["cursor hand"]},{"name":"mdi:cursor-text","tags":[]},{"name":"mdi:curtains","tags":["home automation","drapes","window"]},{"name":"mdi:curtains-closed","tags":["home automation","drapes closed","window closed"]},{"name":"mdi:cylinder","tags":["shape"]},{"name":"mdi:cylinder-off","tags":["shape"]},{"name":"mdi:dance-ballroom","tags":["people / family","human dance ballroom"]},{"name":"mdi:dance-pole","tags":["sport","people / family","kho kho","human dance pole"]},{"name":"mdi:data-matrix","tags":[]},{"name":"mdi:data-matrix-edit","tags":["edit / modify"]},{"name":"mdi:data-matrix-minus","tags":[]},{"name":"mdi:data-matrix-plus","tags":[]},{"name":"mdi:data-matrix-remove","tags":[]},{"name":"mdi:data-matrix-scan","tags":[]},{"name":"mdi:database","tags":["geographic information system","database","storage"]},{"name":"mdi:database-alert","tags":["database","alert / error"]},{"name":"mdi:database-alert-outline","tags":["database","alert / error"]},{"name":"mdi:database-arrow-down","tags":["database"]},{"name":"mdi:database-arrow-down-outline","tags":["database"]},{"name":"mdi:database-arrow-left","tags":["database"]},{"name":"mdi:database-arrow-left-outline","tags":["database"]},{"name":"mdi:database-arrow-right","tags":["database"]},{"name":"mdi:database-arrow-right-outline","tags":["database"]},{"name":"mdi:database-arrow-up","tags":["database"]},{"name":"mdi:database-arrow-up-outline","tags":["database"]},{"name":"mdi:database-check","tags":["geographic information system","database","database tick"]},{"name":"mdi:database-check-outline","tags":["database"]},{"name":"mdi:database-clock","tags":["database","date / time"]},{"name":"mdi:database-clock-outline","tags":["database","date / time"]},{"name":"mdi:database-cog","tags":["database","settings"]},{"name":"mdi:database-cog-outline","tags":["database","settings"]},{"name":"mdi:database-edit","tags":["edit / modify","geographic information system","database"]},{"name":"mdi:database-edit-outline","tags":["database","edit / modify"]},{"name":"mdi:database-export","tags":["geographic information system","database"]},{"name":"mdi:database-export-outline","tags":["database"]},{"name":"mdi:database-eye","tags":["database","database view"]},{"name":"mdi:database-eye-off","tags":["database","database view off"]},{"name":"mdi:database-eye-off-outline","tags":["database","database view off outline"]},{"name":"mdi:database-eye-outline","tags":["database","database view outline"]},{"name":"mdi:database-import","tags":["geographic information system","database"]},{"name":"mdi:database-import-outline","tags":["database"]},{"name":"mdi:database-lock","tags":["lock","geographic information system","database"]},{"name":"mdi:database-lock-outline","tags":["database","lock"]},{"name":"mdi:database-marker","tags":["geographic information system","database","navigation","database location"]},{"name":"mdi:database-marker-outline","tags":["database","navigation","database location outline"]},{"name":"mdi:database-minus","tags":["geographic information system","database"]},{"name":"mdi:database-minus-outline","tags":["database"]},{"name":"mdi:database-off","tags":["database"]},{"name":"mdi:database-off-outline","tags":["database"]},{"name":"mdi:database-outline","tags":["database"]},{"name":"mdi:database-plus","tags":["geographic information system","database","database add"]},{"name":"mdi:database-plus-outline","tags":["database"]},{"name":"mdi:database-refresh","tags":["database"]},{"name":"mdi:database-refresh-outline","tags":["database"]},{"name":"mdi:database-remove","tags":["geographic information system","database"]},{"name":"mdi:database-remove-outline","tags":["database"]},{"name":"mdi:database-search","tags":["geographic information system","database","sql query"]},{"name":"mdi:database-search-outline","tags":["database"]},{"name":"mdi:database-settings","tags":["settings","geographic information system","database"]},{"name":"mdi:database-settings-outline","tags":["database","settings"]},{"name":"mdi:database-sync","tags":["geographic information system","database"]},{"name":"mdi:database-sync-outline","tags":["database"]},{"name":"mdi:death-star","tags":[]},{"name":"mdi:death-star-variant","tags":[]},{"name":"mdi:deathly-hallows","tags":["harry potter"]},{"name":"mdi:debug-step-into","tags":[]},{"name":"mdi:debug-step-out","tags":[]},{"name":"mdi:debug-step-over","tags":["skip","jump"]},{"name":"mdi:decagram","tags":["shape","starburst"]},{"name":"mdi:decagram-outline","tags":["shape","starburst outline"]},{"name":"mdi:decimal","tags":["math"]},{"name":"mdi:decimal-comma","tags":["math"]},{"name":"mdi:decimal-comma-decrease","tags":["math"]},{"name":"mdi:decimal-comma-increase","tags":["math"]},{"name":"mdi:decimal-decrease","tags":["math"]},{"name":"mdi:decimal-increase","tags":["math"]},{"name":"mdi:delete-alert","tags":["alert / error"]},{"name":"mdi:delete-alert-outline","tags":["alert / error"]},{"name":"mdi:delete-circle","tags":["trash circle","bin circle","garbage can circle","garbage circle","rubbish bin circle","rubbish circle","trash can circle"]},{"name":"mdi:delete-circle-outline","tags":["bin circle outline","garbage can circle outline","garbage circle outline","rubbish bin circle outline","rubbish circle outline","trash can circle outline","trash circle outline"]},{"name":"mdi:delete-clock","tags":["date / time"]},{"name":"mdi:delete-clock-outline","tags":["date / time"]},{"name":"mdi:delete-empty","tags":["trash empty","bin empty","rubbish empty","rubbish bin empty","trash can empty","garbage empty","garbage can empty"]},{"name":"mdi:delete-empty-outline","tags":[]},{"name":"mdi:delete-off","tags":[]},{"name":"mdi:delete-off-outline","tags":[]},{"name":"mdi:delete-variant","tags":["trash variant","bin variant","cup ice","drink ice"]},{"name":"mdi:desk","tags":[]},{"name":"mdi:desk-lamp","tags":["home automation"]},{"name":"mdi:desk-lamp-off","tags":["home automation"]},{"name":"mdi:desk-lamp-on","tags":["home automation"]},{"name":"mdi:deskphone","tags":["cellphone / phone","device / tech"]},{"name":"mdi:desktop-classic","tags":["device / tech","home automation","computer classic"]},{"name":"mdi:desktop-tower","tags":["device / tech","home automation"]},{"name":"mdi:desktop-tower-monitor","tags":["device / tech"]},{"name":"mdi:dharmachakra","tags":["religion","dharma wheel","religion buddhist","buddhism"]},{"name":"mdi:diabetes","tags":["medical / hospital","hand blood"]},{"name":"mdi:diameter","tags":["math","circle diameter","sphere diameter"]},{"name":"mdi:diameter-outline","tags":["math","circle diameter outline","sphere diameter outline"]},{"name":"mdi:diameter-variant","tags":["math","circle diameter variant","sphere diameter variant"]},{"name":"mdi:diamond","tags":[]},{"name":"mdi:diamond-outline","tags":[]},{"name":"mdi:diamond-stone","tags":["jewel"]},{"name":"mdi:dice-1","tags":["gaming / rpg","die 1","dice one"]},{"name":"mdi:dice-1-outline","tags":["gaming / rpg"]},{"name":"mdi:dice-2","tags":["gaming / rpg","die 2","dice two"]},{"name":"mdi:dice-2-outline","tags":["gaming / rpg"]},{"name":"mdi:dice-3","tags":["gaming / rpg","die 3","dice three"]},{"name":"mdi:dice-3-outline","tags":["gaming / rpg"]},{"name":"mdi:dice-4","tags":["gaming / rpg","die 4","dice four"]},{"name":"mdi:dice-4-outline","tags":["gaming / rpg"]},{"name":"mdi:dice-5","tags":["gaming / rpg","die 5","dice five"]},{"name":"mdi:dice-5-outline","tags":["gaming / rpg"]},{"name":"mdi:dice-6","tags":["gaming / rpg","die 6","dice six"]},{"name":"mdi:dice-6-outline","tags":["gaming / rpg"]},{"name":"mdi:dice-d10","tags":["gaming / rpg"]},{"name":"mdi:dice-d10-outline","tags":["gaming / rpg","die d10"]},{"name":"mdi:dice-d12","tags":["gaming / rpg"]},{"name":"mdi:dice-d12-outline","tags":["gaming / rpg"]},{"name":"mdi:dice-d20","tags":["gaming / rpg"]},{"name":"mdi:dice-d20-outline","tags":["gaming / rpg","die d20"]},{"name":"mdi:dice-d4","tags":["gaming / rpg"]},{"name":"mdi:dice-d4-outline","tags":["gaming / rpg","die d4"]},{"name":"mdi:dice-d6","tags":["gaming / rpg"]},{"name":"mdi:dice-d6-outline","tags":["gaming / rpg","die d6"]},{"name":"mdi:dice-d8","tags":["gaming / rpg"]},{"name":"mdi:dice-d8-outline","tags":["gaming / rpg","die d8"]},{"name":"mdi:dice-multiple","tags":["gaming / rpg","die multiple"]},{"name":"mdi:dice-multiple-outline","tags":["gaming / rpg"]},{"name":"mdi:dip-switch","tags":[]},{"name":"mdi:disc","tags":["music","cd rom","dvd"]},{"name":"mdi:disc-player","tags":["home automation","device / tech"]},{"name":"mdi:dishwasher-alert","tags":["home automation","alert / error"]},{"name":"mdi:dishwasher-off","tags":["home automation"]},{"name":"mdi:distribute-horizontal-center","tags":[]},{"name":"mdi:distribute-horizontal-left","tags":[]},{"name":"mdi:distribute-horizontal-right","tags":[]},{"name":"mdi:distribute-vertical-bottom","tags":[]},{"name":"mdi:distribute-vertical-center","tags":[]},{"name":"mdi:distribute-vertical-top","tags":[]},{"name":"mdi:diversify","tags":[]},{"name":"mdi:diving-flippers","tags":["sport"]},{"name":"mdi:diving-helmet","tags":[]},{"name":"mdi:diving-scuba-flag","tags":[]},{"name":"mdi:diving-scuba-mask","tags":["sport"]},{"name":"mdi:diving-scuba-tank","tags":[]},{"name":"mdi:diving-scuba-tank-multiple","tags":[]},{"name":"mdi:diving-snorkel","tags":["sport"]},{"name":"mdi:division","tags":["math","obelus"]},{"name":"mdi:division-box","tags":["math"]},{"name":"mdi:dna","tags":["science","helix"]},{"name":"mdi:dock-bottom","tags":[]},{"name":"mdi:dock-left","tags":[]},{"name":"mdi:dock-right","tags":[]},{"name":"mdi:dock-top","tags":[]},{"name":"mdi:dock-window","tags":[]},{"name":"mdi:doctor","tags":["medical / hospital"]},{"name":"mdi:dog","tags":["animal","emoji dog","emoticon dog"]},{"name":"mdi:dog-service","tags":["animal","guide dog","k9","canine"]},{"name":"mdi:dog-side","tags":["animal","k9","canine"]},{"name":"mdi:dog-side-off","tags":["animal"]},{"name":"mdi:dolly","tags":["hand truck","trolley"]},{"name":"mdi:dolphin","tags":["animal","porpoise"]},{"name":"mdi:domain-plus","tags":[]},{"name":"mdi:domain-remove","tags":[]},{"name":"mdi:domain-switch","tags":[]},{"name":"mdi:dome-light","tags":[]},{"name":"mdi:domino-mask","tags":["robber mask","zorro mask"]},{"name":"mdi:donkey","tags":["animal"]},{"name":"mdi:door","tags":["home automation"]},{"name":"mdi:door-closed","tags":["home automation"]},{"name":"mdi:door-closed-lock","tags":["home automation","lock"]},{"name":"mdi:door-open","tags":["home automation"]},{"name":"mdi:door-sliding-lock","tags":["home automation","lock","patio door lock","french door lock"]},{"name":"mdi:door-sliding-open","tags":["home automation","patio door open","french door open"]},{"name":"mdi:doorbell","tags":["home automation"]},{"name":"mdi:doorbell-video","tags":["home automation"]},{"name":"mdi:dots-circle","tags":["perimeter"]},{"name":"mdi:dots-grid","tags":[]},{"name":"mdi:dots-hexagon","tags":[]},{"name":"mdi:dots-horizontal-circle-outline","tags":["ellipsis horizontal circle outline","more circle outline","menu"]},{"name":"mdi:dots-square","tags":["perimeter"]},{"name":"mdi:dots-triangle","tags":[]},{"name":"mdi:dots-vertical-circle","tags":["ellipsis vertical circle","menu"]},{"name":"mdi:dots-vertical-circle-outline","tags":["ellipsis vertical circle outline","menu"]},{"name":"mdi:download-box","tags":[]},{"name":"mdi:download-box-outline","tags":[]},{"name":"mdi:download-circle","tags":[]},{"name":"mdi:download-circle-outline","tags":[]},{"name":"mdi:download-lock","tags":["lock"]},{"name":"mdi:download-lock-outline","tags":["lock"]},{"name":"mdi:download-multiple","tags":["downloads"]},{"name":"mdi:download-network","tags":[]},{"name":"mdi:download-network-outline","tags":[]},{"name":"mdi:download-off","tags":[]},{"name":"mdi:download-off-outline","tags":[]},{"name":"mdi:drag","tags":[]},{"name":"mdi:drag-horizontal","tags":[]},{"name":"mdi:drag-variant","tags":[]},{"name":"mdi:drag-vertical","tags":[]},{"name":"mdi:drag-vertical-variant","tags":[]},{"name":"mdi:drama-masks","tags":["comedy","tragedy","theatre"]},{"name":"mdi:draw","tags":["drawing / art","form","sign","signature"]},{"name":"mdi:draw-pen","tags":["form","drawing / art","sign","signature"]},{"name":"mdi:drawing","tags":["drawing / art","shape"]},{"name":"mdi:dresser","tags":["home automation"]},{"name":"mdi:dresser-outline","tags":["home automation"]},{"name":"mdi:drone","tags":["transportation + flying"]},{"name":"mdi:duck","tags":["animal"]},{"name":"mdi:dump-truck","tags":["transportation + road","hardware / tools","tipper lorry"]},{"name":"mdi:ear-hearing-loop","tags":["medical / hospital","audio induction loop","telecoil"]},{"name":"mdi:ear-hearing-off","tags":["medical / hospital","hearing impaired"]},{"name":"mdi:earbuds","tags":["audio","music","headphones"]},{"name":"mdi:earbuds-off","tags":["audio","music","headphones off"]},{"name":"mdi:earbuds-off-outline","tags":["audio","music","headphones off outline"]},{"name":"mdi:earbuds-outline","tags":["audio","music","headphones outline"]},{"name":"mdi:earth-arrow-right","tags":["navigation","globe arrow right","world arrow right","planet arrow right"]},{"name":"mdi:earth-box","tags":["navigation","globe box","world box","planet box"]},{"name":"mdi:earth-box-minus","tags":["navigation","globe box minus","world box minus","planet box minus"]},{"name":"mdi:earth-box-off","tags":["navigation","globe box off","world box off","planet box off"]},{"name":"mdi:earth-box-plus","tags":["navigation","globe box plus","world box plus","planet box plus"]},{"name":"mdi:earth-box-remove","tags":["navigation","globe box remove","world box remove","planet box remove"]},{"name":"mdi:earth-minus","tags":["navigation","globe minus","world minus","planet minus"]},{"name":"mdi:earth-off","tags":["geographic information system","navigation","globe off","world off","planet off"]},{"name":"mdi:earth-plus","tags":["navigation","globe plus","world plus","planet plus"]},{"name":"mdi:earth-remove","tags":["navigation","globe remove","world remove","planet remove"]},{"name":"mdi:egg","tags":["food / drink","agriculture"]},{"name":"mdi:egg-easter","tags":["holiday"]},{"name":"mdi:egg-fried","tags":["food / drink"]},{"name":"mdi:egg-off","tags":["food / drink","agriculture"]},{"name":"mdi:egg-off-outline","tags":["food / drink","agriculture"]},{"name":"mdi:egg-outline","tags":["food / drink","agriculture"]},{"name":"mdi:eiffel-tower","tags":["places","paris","france"]},{"name":"mdi:eight-track","tags":["music","8 track"]},{"name":"mdi:eject-circle","tags":[]},{"name":"mdi:eject-circle-outline","tags":[]},{"name":"mdi:electric-switch","tags":[]},{"name":"mdi:electric-switch-closed","tags":[]},{"name":"mdi:elephant","tags":["animal"]},{"name":"mdi:elevation-decline","tags":[]},{"name":"mdi:elevation-rise","tags":[]},{"name":"mdi:elevator","tags":["transportation + other"]},{"name":"mdi:elevator-down","tags":["transportation + other"]},{"name":"mdi:elevator-passenger-off","tags":["transportation + other"]},{"name":"mdi:elevator-passenger-off-outline","tags":["transportation + other"]},{"name":"mdi:elevator-up","tags":["transportation + other"]},{"name":"mdi:ellipse","tags":["shape"]},{"name":"mdi:ellipse-outline","tags":["shape"]},{"name":"mdi:email-alert","tags":["alert / error","email warning","envelope alert","envelope warning"]},{"name":"mdi:email-alert-outline","tags":["alert / error"]},{"name":"mdi:email-arrow-left","tags":["email receive"]},{"name":"mdi:email-arrow-left-outline","tags":["email receive outline"]},{"name":"mdi:email-arrow-right","tags":["email send"]},{"name":"mdi:email-arrow-right-outline","tags":["email arrow right outline"]},{"name":"mdi:email-box","tags":["envelope box"]},{"name":"mdi:email-check","tags":["email tick"]},{"name":"mdi:email-check-outline","tags":["email tick outline"]},{"name":"mdi:email-edit","tags":["edit / modify"]},{"name":"mdi:email-edit-outline","tags":["edit / modify"]},{"name":"mdi:email-fast","tags":["envelope fast","email quick","email sent","email send"]},{"name":"mdi:email-fast-outline","tags":["email send outline","email sent outline","envelope fast outline","email quick outline"]},{"name":"mdi:email-heart-outline","tags":["love letter","envelope heart outline","greeting card"]},{"name":"mdi:email-lock","tags":["lock","envelope secure","email secure","envelope lock"]},{"name":"mdi:email-lock-outline","tags":["lock","email secure outline"]},{"name":"mdi:email-minus","tags":[]},{"name":"mdi:email-minus-outline","tags":[]},{"name":"mdi:email-multiple","tags":[]},{"name":"mdi:email-multiple-outline","tags":[]},{"name":"mdi:email-newsletter","tags":[]},{"name":"mdi:email-off","tags":[]},{"name":"mdi:email-off-outline","tags":[]},{"name":"mdi:email-open","tags":["drafts","envelope open"]},{"name":"mdi:email-open-heart-outline","tags":["love letter open","greeting card open","envelope open heart outline"]},{"name":"mdi:email-open-multiple","tags":[]},{"name":"mdi:email-open-multiple-outline","tags":[]},{"name":"mdi:email-open-outline","tags":["envelope open outline"]},{"name":"mdi:email-plus","tags":["email add","envelope add","envelope plus"]},{"name":"mdi:email-plus-outline","tags":["email add outline","envelope add outline","envelope plus outline"]},{"name":"mdi:email-remove","tags":[]},{"name":"mdi:email-remove-outline","tags":[]},{"name":"mdi:email-seal","tags":["email certified","mail certified","mail seal","email verified","mail verified"]},{"name":"mdi:email-seal-outline","tags":["email verified outline","email certified outline","mail verified outline","mail certified outline","mail seal outline"]},{"name":"mdi:email-search","tags":[]},{"name":"mdi:email-search-outline","tags":[]},{"name":"mdi:email-sync","tags":["email refresh","email resend"]},{"name":"mdi:email-sync-outline","tags":["email refresh outline","email resend outline"]},{"name":"mdi:email-variant","tags":["envelope variant"]},{"name":"mdi:emoticon-angry","tags":["emoji","smiley angry","face angry","emoji angry"]},{"name":"mdi:emoticon-angry-outline","tags":["emoji","smiley angry outline","face angry outline","emoji angry outline"]},{"name":"mdi:emoticon-confused","tags":["emoji","face confused","emoji confused"]},{"name":"mdi:emoticon-confused-outline","tags":["emoji","face confused outline","emoji confused outline"]},{"name":"mdi:emoticon-cool","tags":["emoji","smiley cool","face cool","face sunglasses","emoji cool"]},{"name":"mdi:emoticon-cool-outline","tags":["emoji","smiley cool outline","face cool outline","face sunglasses outline","emoji cool outline"]},{"name":"mdi:emoticon-cry","tags":["emoji","smiley cry","face cry","emoji cry"]},{"name":"mdi:emoticon-cry-outline","tags":["emoji","smiley cry outline","face cry outline","emoji cry outline"]},{"name":"mdi:emoticon-devil","tags":["emoji","smiley devil","face devil","emoji devil"]},{"name":"mdi:emoticon-devil-outline","tags":["emoji","smiley devil outline","face devil outline","emoji devil outline"]},{"name":"mdi:emoticon-frown","tags":["emoji","face frown","emoji frown"]},{"name":"mdi:emoticon-happy","tags":["emoji","smiley happy","face happy","emoji happy"]},{"name":"mdi:emoticon-happy-outline","tags":["emoji","smiley happy outline","face happy outline","emoji happy outline"]},{"name":"mdi:emoticon-kiss","tags":["emoji","smiley kiss","face kiss","emoji kiss"]},{"name":"mdi:emoticon-kiss-outline","tags":["emoji","smiley kiss outline","face kiss outline","emoji kiss outline"]},{"name":"mdi:emoticon-lol","tags":["emoji","face lol","emoji lol"]},{"name":"mdi:emoticon-lol-outline","tags":["emoji","face lol outline","emoji lol outline"]},{"name":"mdi:emoticon-neutral","tags":["emoji","smiley neutral","face neutral","emoji neutral"]},{"name":"mdi:emoticon-neutral-outline","tags":["emoji","smiley neutral outline","face neutral outline","emoji neutral outline"]},{"name":"mdi:emoticon-poop","tags":["emoji","smiley poop","face poop","emoji poop"]},{"name":"mdi:emoticon-poop-outline","tags":["emoji","face poop outline","emoji poop outline"]},{"name":"mdi:emoticon-sad","tags":["emoji","smiley sad","face sad","emoji sad"]},{"name":"mdi:emoticon-sad-outline","tags":["emoji","smiley sad outline","face sad outline","emoji sad outline"]},{"name":"mdi:emoticon-tongue","tags":["emoji","smiley tongue","face tongue","emoji tongue"]},{"name":"mdi:emoticon-tongue-outline","tags":["emoji","smiley tongue outline","face tongue outline","emoji tongue outline"]},{"name":"mdi:emoticon-wink","tags":["emoji","smiley wink","face wink","emoji wink"]},{"name":"mdi:emoticon-wink-outline","tags":["emoji","smiley wink outline","face wink outline","emoji wink outline"]},{"name":"mdi:engine","tags":["automotive","motor"]},{"name":"mdi:engine-off","tags":["automotive","motor off"]},{"name":"mdi:engine-off-outline","tags":["automotive","motor off outline"]},{"name":"mdi:engine-outline","tags":["automotive","motor outline"]},{"name":"mdi:epsilon","tags":["alpha / numeric"]},{"name":"mdi:equal","tags":["math"]},{"name":"mdi:equal-box","tags":["math"]},{"name":"mdi:equalizer-outline","tags":["audio"]},{"name":"mdi:eraser","tags":[]},{"name":"mdi:escalator","tags":["transportation + other"]},{"name":"mdi:escalator-box","tags":[]},{"name":"mdi:escalator-down","tags":["transportation + other"]},{"name":"mdi:escalator-up","tags":["transportation + other"]},{"name":"mdi:et","tags":[]},{"name":"mdi:ethernet","tags":[]},{"name":"mdi:ethernet-cable","tags":[]},{"name":"mdi:ethernet-cable-off","tags":[]},{"name":"mdi:ev-plug-ccs1","tags":["automotive","ev plug ccs combo 1","ev charger ccs1"]},{"name":"mdi:ev-plug-ccs2","tags":["automotive","ev plug ccs combo 2","ev charger ccs2"]},{"name":"mdi:ev-plug-chademo","tags":["automotive","ev charger chademo"]},{"name":"mdi:ev-plug-tesla","tags":["automotive","ev charger tesla"]},{"name":"mdi:ev-plug-type1","tags":["automotive","ev plug j1772","ev charger type1"]},{"name":"mdi:ev-plug-type2","tags":["automotive","ev plug mennekes","ev charger type2"]},{"name":"mdi:excavator","tags":["hardware / tools"]},{"name":"mdi:exclamation","tags":["math","factorial"]},{"name":"mdi:exclamation-thick","tags":["exclamation bold"]},{"name":"mdi:exit-run","tags":["home automation","emergency exit"]},{"name":"mdi:expand-all","tags":["animation plus"]},{"name":"mdi:expand-all-outline","tags":["animation plus outline"]},{"name":"mdi:expansion-card","tags":["gaming / rpg","gpu","graphics processing unit","nic","network interface card"]},{"name":"mdi:expansion-card-variant","tags":["graphics processing unit","gpu","network interface card","nice"]},{"name":"mdi:exponent","tags":["math","power"]},{"name":"mdi:exponent-box","tags":["math","power box"]},{"name":"mdi:export","tags":["output"]},{"name":"mdi:eye-arrow-left","tags":["view arrow left"]},{"name":"mdi:eye-arrow-left-outline","tags":["view arrow left outline"]},{"name":"mdi:eye-arrow-right","tags":["view arrow right"]},{"name":"mdi:eye-arrow-right-outline","tags":["view arrow right outline"]},{"name":"mdi:eye-check","tags":["eye tick"]},{"name":"mdi:eye-check-outline","tags":["eye tick outline"]},{"name":"mdi:eye-circle","tags":[]},{"name":"mdi:eye-circle-outline","tags":[]},{"name":"mdi:eye-lock","tags":[]},{"name":"mdi:eye-lock-open","tags":[]},{"name":"mdi:eye-lock-open-outline","tags":[]},{"name":"mdi:eye-lock-outline","tags":[]},{"name":"mdi:eye-minus","tags":[]},{"name":"mdi:eye-minus-outline","tags":[]},{"name":"mdi:eye-off-outline","tags":["hide outline","visibility off outline"]},{"name":"mdi:eye-outline","tags":["show outline","visibility outline"]},{"name":"mdi:eye-plus","tags":["eye add"]},{"name":"mdi:eye-plus-outline","tags":["eye add outline"]},{"name":"mdi:eye-refresh","tags":["view refresh"]},{"name":"mdi:eye-refresh-outline","tags":["view refresh outline"]},{"name":"mdi:eye-remove","tags":[]},{"name":"mdi:eye-remove-outline","tags":[]},{"name":"mdi:eye-settings","tags":["settings"]},{"name":"mdi:eye-settings-outline","tags":["settings"]},{"name":"mdi:eyedropper","tags":["color","drawing / art","science","pipette"]},{"name":"mdi:eyedropper-minus","tags":["science"]},{"name":"mdi:eyedropper-off","tags":["science"]},{"name":"mdi:eyedropper-plus","tags":["science"]},{"name":"mdi:eyedropper-remove","tags":["science"]},{"name":"mdi:face-agent","tags":["customer service","support","emoji agent","emoticon agent"]},{"name":"mdi:face-man-shimmer-outline","tags":["people / family","photography","health / beauty","account / user","face retouching natural outline","face male shimmer outline","emoji man shimmer outline","emoticon man shimmer outline"]},{"name":"mdi:face-mask","tags":["medical / hospital","clothing"]},{"name":"mdi:face-mask-outline","tags":["medical / hospital","clothing"]},{"name":"mdi:face-recognition","tags":["photography","facial recognition","scan"]},{"name":"mdi:face-woman","tags":["people / family","face female","emoji woman","emoticon woman"]},{"name":"mdi:face-woman-outline","tags":["people / family","face female outline","emoji woman outline","emoticon woman outline"]},{"name":"mdi:face-woman-profile","tags":["people / family","face female profile","emoji woman profile","emoticon woman profile"]},{"name":"mdi:face-woman-shimmer","tags":["people / family","photography","health / beauty","account / user","face retouching natural woman","face female shimmer","emoji woman shimmer","emoticon woman shimmer"]},{"name":"mdi:face-woman-shimmer-outline","tags":["people / family","photography","health / beauty","account / user","face retouching natural woman outline","face female shimmer outline","emoji woman shimmer outline","emoticon woman shimmer outline"]},{"name":"mdi:factory","tags":["places","industrial"]},{"name":"mdi:family-tree","tags":["people / family"]},{"name":"mdi:fan","tags":["home automation","automotive"]},{"name":"mdi:fan-alert","tags":["home automation","alert / error"]},{"name":"mdi:fan-auto","tags":[]},{"name":"mdi:fan-chevron-down","tags":["home automation","fan speed down"]},{"name":"mdi:fan-chevron-up","tags":["home automation","fan speed up"]},{"name":"mdi:fan-clock","tags":["home automation","date / time","fan clock","fan schedule","fan timer"]},{"name":"mdi:fan-minus","tags":["home automation"]},{"name":"mdi:fan-off","tags":["home automation","automotive"]},{"name":"mdi:fan-plus","tags":["home automation"]},{"name":"mdi:fan-remove","tags":["home automation"]},{"name":"mdi:fan-speed-1","tags":["home automation","fan speed low"]},{"name":"mdi:fan-speed-2","tags":["home automation","fan speed medium"]},{"name":"mdi:fan-speed-3","tags":["home automation","fan speed high"]},{"name":"mdi:fast-forward-10","tags":[]},{"name":"mdi:fast-forward-15","tags":[]},{"name":"mdi:fast-forward-30","tags":[]},{"name":"mdi:fast-forward-45","tags":[]},{"name":"mdi:fast-forward-5","tags":[]},{"name":"mdi:fast-forward-60","tags":[]},{"name":"mdi:faucet","tags":["home automation","kitchen tap","bathroom tap","sink"]},{"name":"mdi:faucet-variant","tags":["home automation","bathroom tap","kitchen tap","sink"]},{"name":"mdi:feather","tags":["nature","quill"]},{"name":"mdi:feature-search","tags":["box","box search"]},{"name":"mdi:feature-search-outline","tags":["box","box outline","box search outline"]},{"name":"mdi:fence","tags":["home automation","agriculture","railway","train track"]},{"name":"mdi:fence-electric","tags":["home automation","agriculture","railway electric","train track electric"]},{"name":"mdi:file-account","tags":["account / user","files / folders","file user","resume"]},{"name":"mdi:file-account-outline","tags":["files / folders","account / user"]},{"name":"mdi:file-alert","tags":["files / folders","alert / error","file warning"]},{"name":"mdi:file-alert-outline","tags":["files / folders","alert / error","file warning outline"]},{"name":"mdi:file-arrow-left-right","tags":["files / folders","file exchange","file transfer","file swap"]},{"name":"mdi:file-arrow-left-right-outline","tags":["files / folders","file exchange outline","file swap outline","file transfer outline"]},{"name":"mdi:file-arrow-up-down","tags":["files / folders","file exchange","file swap","file transfer","file upload download"]},{"name":"mdi:file-arrow-up-down-outline","tags":["files / folders","file exchange outline","file swap outline","file transfer outline","file upload download outline"]},{"name":"mdi:file-cabinet","tags":["files / folders","filing cabinet"]},{"name":"mdi:file-cad","tags":["files / folders"]},{"name":"mdi:file-cad-box","tags":["files / folders"]},{"name":"mdi:file-cancel","tags":["files / folders","ban","forbid"]},{"name":"mdi:file-cancel-outline","tags":["files / folders","ban","forbid"]},{"name":"mdi:file-certificate","tags":["files / folders"]},{"name":"mdi:file-certificate-outline","tags":["files / folders"]},{"name":"mdi:file-chart","tags":["files / folders","file report","file graph"]},{"name":"mdi:file-chart-check","tags":["files / folders"]},{"name":"mdi:file-chart-check-outline","tags":["files / folders"]},{"name":"mdi:file-chart-outline","tags":["files / folders","file graph outline","file report outline"]},{"name":"mdi:file-check","tags":["files / folders","file tick"]},{"name":"mdi:file-check-outline","tags":["files / folders"]},{"name":"mdi:file-clock","tags":["files / folders","date / time"]},{"name":"mdi:file-clock-outline","tags":["files / folders","date / time"]},{"name":"mdi:file-cloud","tags":["cloud","files / folders"]},{"name":"mdi:file-cloud-outline","tags":["files / folders","cloud"]},{"name":"mdi:file-code","tags":["files / folders","developer / languages"]},{"name":"mdi:file-code-outline","tags":["files / folders","developer / languages"]},{"name":"mdi:file-cog","tags":["settings","files / folders","file settings cog"]},{"name":"mdi:file-cog-outline","tags":["settings","files / folders","file settings cog outline"]},{"name":"mdi:file-compare","tags":["files / folders"]},{"name":"mdi:file-delimited","tags":["files / folders","file csv"]},{"name":"mdi:file-delimited-outline","tags":["files / folders","file csv outline"]},{"name":"mdi:file-document","tags":["files / folders","file text"]},{"name":"mdi:file-document-alert","tags":["files / folders","alert / error","file document error","file text alert","file text error"]},{"name":"mdi:file-document-alert-outline","tags":["files / folders","alert / error","file document error outline","file text error outline","file text alert outline"]},{"name":"mdi:file-document-arrow-right","tags":["files / folders","file document move","file text move","file text arrow right"]},{"name":"mdi:file-document-arrow-right-outline","tags":["files / folders","file document move outline","file text move outline","file text arrow right outline"]},{"name":"mdi:file-document-check","tags":["files / folders","file document tick","file text tick","file text check"]},{"name":"mdi:file-document-check-outline","tags":["files / folders","file document tick outline","file text tick outline","file text check outline"]},{"name":"mdi:file-document-edit","tags":["edit / modify","files / folders","contract","file text edit"]},{"name":"mdi:file-document-edit-outline","tags":["edit / modify","files / folders","contract outline","file text edit outline"]},{"name":"mdi:file-document-minus","tags":["files / folders","file text minus"]},{"name":"mdi:file-document-minus-outline","tags":["files / folders","file text minus outline"]},{"name":"mdi:file-document-multiple","tags":["files / folders","file text multiple"]},{"name":"mdi:file-document-multiple-outline","tags":["files / folders","file text multiple outline"]},{"name":"mdi:file-document-plus","tags":["files / folders","file document add","file text add","file text plus"]},{"name":"mdi:file-document-plus-outline","tags":["files / folders","file document add outline","file text plus outline","file text add outline"]},{"name":"mdi:file-document-refresh","tags":["files / folders"]},{"name":"mdi:file-document-refresh-outline","tags":["files / folders"]},{"name":"mdi:file-document-remove","tags":["files / folders","file document delete","file text remove","file text delete"]},{"name":"mdi:file-document-remove-outline","tags":["files / folders","file document delete outline","file text remove outline","file text delete outline"]},{"name":"mdi:file-download","tags":["files / folders"]},{"name":"mdi:file-download-outline","tags":["files / folders"]},{"name":"mdi:file-edit","tags":["edit / modify","files / folders"]},{"name":"mdi:file-edit-outline","tags":["edit / modify","files / folders"]},{"name":"mdi:file-excel","tags":["files / folders"]},{"name":"mdi:file-excel-box-outline","tags":["files / folders"]},{"name":"mdi:file-excel-outline","tags":["files / folders"]},{"name":"mdi:file-export","tags":["files / folders"]},{"name":"mdi:file-export-outline","tags":["files / folders"]},{"name":"mdi:file-eye","tags":["files / folders"]},{"name":"mdi:file-eye-outline","tags":["files / folders"]},{"name":"mdi:file-gif-box","tags":["files / folders"]},{"name":"mdi:file-hidden","tags":["files / folders"]},{"name":"mdi:file-image","tags":["files / folders"]},{"name":"mdi:file-image-marker","tags":["files / folders","navigation","file image location"]},{"name":"mdi:file-image-marker-outline","tags":["files / folders","navigation","file image location outline"]},{"name":"mdi:file-image-minus","tags":["files / folders"]},{"name":"mdi:file-image-minus-outline","tags":["files / folders"]},{"name":"mdi:file-image-outline","tags":["files / folders"]},{"name":"mdi:file-image-plus","tags":["files / folders","file image add"]},{"name":"mdi:file-image-plus-outline","tags":["files / folders","file image add outline"]},{"name":"mdi:file-image-remove","tags":["files / folders"]},{"name":"mdi:file-image-remove-outline","tags":["files / folders"]},{"name":"mdi:file-import","tags":["files / folders"]},{"name":"mdi:file-import-outline","tags":["files / folders"]},{"name":"mdi:file-jpg-box","tags":["files / folders","file jpeg box","image jpg box","image jpeg box"]},{"name":"mdi:file-key","tags":["files / folders"]},{"name":"mdi:file-key-outline","tags":["files / folders"]},{"name":"mdi:file-link","tags":["files / folders"]},{"name":"mdi:file-link-outline","tags":["files / folders"]},{"name":"mdi:file-lock","tags":["lock","files / folders"]},{"name":"mdi:file-lock-open","tags":["lock","files / folders"]},{"name":"mdi:file-lock-open-outline","tags":["lock","files / folders"]},{"name":"mdi:file-lock-outline","tags":["files / folders","lock"]},{"name":"mdi:file-marker","tags":["files / folders","navigation","file location"]},{"name":"mdi:file-marker-outline","tags":["files / folders","navigation","file location outline"]},{"name":"mdi:file-minus","tags":["files / folders"]},{"name":"mdi:file-minus-outline","tags":["files / folders"]},{"name":"mdi:file-move","tags":["files / folders"]},{"name":"mdi:file-move-outline","tags":["files / folders"]},{"name":"mdi:file-multiple","tags":["files / folders","files"]},{"name":"mdi:file-multiple-outline","tags":["files / folders"]},{"name":"mdi:file-music","tags":["files / folders","music"]},{"name":"mdi:file-music-outline","tags":["files / folders","music"]},{"name":"mdi:file-pdf-box","tags":["files / folders","file acrobat box","adobe acrobat"]},{"name":"mdi:file-percent","tags":["files / folders"]},{"name":"mdi:file-percent-outline","tags":["files / folders"]},{"name":"mdi:file-phone","tags":["files / folders","cellphone / phone"]},{"name":"mdi:file-phone-outline","tags":["files / folders","cellphone / phone"]},{"name":"mdi:file-plus","tags":["files / folders","note add"]},{"name":"mdi:file-plus-outline","tags":["files / folders"]},{"name":"mdi:file-png-box","tags":["files / folders"]},{"name":"mdi:file-powerpoint","tags":["files / folders"]},{"name":"mdi:file-powerpoint-box-outline","tags":["files / folders"]},{"name":"mdi:file-powerpoint-outline","tags":["files / folders"]},{"name":"mdi:file-question","tags":["files / folders"]},{"name":"mdi:file-question-outline","tags":["files / folders"]},{"name":"mdi:file-refresh","tags":["files / folders"]},{"name":"mdi:file-refresh-outline","tags":["files / folders"]},{"name":"mdi:file-remove","tags":["files / folders"]},{"name":"mdi:file-remove-outline","tags":["files / folders"]},{"name":"mdi:file-replace","tags":["files / folders"]},{"name":"mdi:file-replace-outline","tags":["files / folders"]},{"name":"mdi:file-restore-outline","tags":["files / folders"]},{"name":"mdi:file-rotate-left","tags":["files / folders","file rotate counter clockwise","file rotate ccw"]},{"name":"mdi:file-rotate-left-outline","tags":["files / folders","file rotate counter clockwise outline","file rotate ccw outline"]},{"name":"mdi:file-rotate-right","tags":["files / folders","file rotate clockwise"]},{"name":"mdi:file-rotate-right-outline","tags":["files / folders","file rotate clockwise"]},{"name":"mdi:file-search","tags":["files / folders"]},{"name":"mdi:file-search-outline","tags":["files / folders"]},{"name":"mdi:file-send","tags":["files / folders","file move"]},{"name":"mdi:file-send-outline","tags":["files / folders"]},{"name":"mdi:file-settings","tags":["settings","files / folders"]},{"name":"mdi:file-settings-outline","tags":["settings","files / folders"]},{"name":"mdi:file-sign","tags":["banking","files / folders","contract sign","document sign"]},{"name":"mdi:file-star","tags":["files / folders","file favorite"]},{"name":"mdi:file-star-four-points","tags":["files / folders","file auto"]},{"name":"mdi:file-star-four-points-outline","tags":["files / folders","file auto outline"]},{"name":"mdi:file-star-outline","tags":["files / folders","file favorite outline"]},{"name":"mdi:file-swap","tags":["files / folders","file transfer"]},{"name":"mdi:file-swap-outline","tags":["files / folders","file transfer outline"]},{"name":"mdi:file-sync","tags":["files / folders"]},{"name":"mdi:file-sync-outline","tags":["files / folders"]},{"name":"mdi:file-table","tags":["files / folders"]},{"name":"mdi:file-table-box","tags":["files / folders"]},{"name":"mdi:file-table-box-multiple","tags":["files / folders"]},{"name":"mdi:file-table-box-multiple-outline","tags":["files / folders"]},{"name":"mdi:file-table-box-outline","tags":["files / folders"]},{"name":"mdi:file-table-outline","tags":["files / folders"]},{"name":"mdi:file-tree","tags":["files / folders","subtasks"]},{"name":"mdi:file-tree-outline","tags":["files / folders"]},{"name":"mdi:file-undo","tags":["files / folders","file revert","file discard"]},{"name":"mdi:file-undo-outline","tags":["files / folders"]},{"name":"mdi:file-upload","tags":["files / folders"]},{"name":"mdi:file-upload-outline","tags":["files / folders"]},{"name":"mdi:file-video","tags":["video / movie","files / folders"]},{"name":"mdi:file-video-outline","tags":["files / folders"]},{"name":"mdi:file-word","tags":["files / folders"]},{"name":"mdi:file-word-box-outline","tags":["files / folders"]},{"name":"mdi:file-word-outline","tags":["files / folders"]},{"name":"mdi:file-xml-box","tags":["files / folders"]},{"name":"mdi:filmstrip-box","tags":[]},{"name":"mdi:filmstrip-off","tags":["video / movie"]},{"name":"mdi:filter","tags":["funnel"]},{"name":"mdi:filter-check","tags":["funnel check"]},{"name":"mdi:filter-check-outline","tags":["funnel check outline"]},{"name":"mdi:filter-cog","tags":["settings","funnel settings","filter settings","funnel cog","filter gear","funnel gear"]},{"name":"mdi:filter-cog-outline","tags":["settings","filter settings outline","filter gear outline","funnel cog outline","funnel settings outline","funnel gear outline"]},{"name":"mdi:filter-menu","tags":[]},{"name":"mdi:filter-menu-outline","tags":[]},{"name":"mdi:filter-minus","tags":["funnel minus"]},{"name":"mdi:filter-minus-outline","tags":["funnel minus outline"]},{"name":"mdi:filter-multiple","tags":["funnel multiple"]},{"name":"mdi:filter-multiple-outline","tags":["funnel multiple outline"]},{"name":"mdi:filter-off","tags":[]},{"name":"mdi:filter-off-outline","tags":[]},{"name":"mdi:filter-outline","tags":["funnel outline"]},{"name":"mdi:filter-plus","tags":["funnel plus"]},{"name":"mdi:filter-plus-outline","tags":["funnel plus outline"]},{"name":"mdi:filter-remove","tags":["funnel remove"]},{"name":"mdi:filter-remove-outline","tags":["funnel remove outline"]},{"name":"mdi:filter-settings","tags":["settings","funnel settings"]},{"name":"mdi:filter-settings-outline","tags":["settings","funnel settings outline"]},{"name":"mdi:filter-variant-minus","tags":[]},{"name":"mdi:filter-variant-plus","tags":[]},{"name":"mdi:filter-variant-remove","tags":[]},{"name":"mdi:fingerprint-off","tags":[]},{"name":"mdi:fire-alert","tags":["alert / error","home automation","flame alert"]},{"name":"mdi:fire-circle","tags":["home automation","flame circle","hot circle","gas circle","natural gas circle"]},{"name":"mdi:fire-extinguisher","tags":["hardware / tools","home automation"]},{"name":"mdi:fire-hydrant","tags":[]},{"name":"mdi:fire-hydrant-alert","tags":["alert / error"]},{"name":"mdi:fire-hydrant-off","tags":[]},{"name":"mdi:fire-off","tags":["home automation","flame off"]},{"name":"mdi:fire-truck","tags":["transportation + road","fire engine"]},{"name":"mdi:fireplace","tags":["home automation"]},{"name":"mdi:fireplace-off","tags":["home automation"]},{"name":"mdi:firewire","tags":[]},{"name":"mdi:firework","tags":["holiday","bottle rocket"]},{"name":"mdi:firework-off","tags":[]},{"name":"mdi:fish","tags":["animal","food / drink"]},{"name":"mdi:fish-off","tags":["food / drink"]},{"name":"mdi:fishbowl","tags":["animal","aquarium"]},{"name":"mdi:fishbowl-outline","tags":["animal","aquarium outline"]},{"name":"mdi:fit-to-page","tags":["text / content / format","arrow"]},{"name":"mdi:fit-to-page-outline","tags":["text / content / format","arrow"]},{"name":"mdi:flag-checkered","tags":["sport","goal"]},{"name":"mdi:flag-minus","tags":[]},{"name":"mdi:flag-minus-outline","tags":[]},{"name":"mdi:flag-off","tags":[]},{"name":"mdi:flag-off-outline","tags":[]},{"name":"mdi:flag-plus","tags":["flag add"]},{"name":"mdi:flag-plus-outline","tags":[]},{"name":"mdi:flag-remove","tags":[]},{"name":"mdi:flag-remove-outline","tags":[]},{"name":"mdi:flag-triangle","tags":["milestone"]},{"name":"mdi:flag-variant","tags":[]},{"name":"mdi:flag-variant-minus","tags":[]},{"name":"mdi:flag-variant-minus-outline","tags":[]},{"name":"mdi:flag-variant-off","tags":[]},{"name":"mdi:flag-variant-off-outline","tags":[]},{"name":"mdi:flag-variant-outline","tags":[]},{"name":"mdi:flag-variant-plus","tags":[]},{"name":"mdi:flag-variant-plus-outline","tags":[]},{"name":"mdi:flag-variant-remove","tags":[]},{"name":"mdi:flag-variant-remove-outline","tags":[]},{"name":"mdi:flash-alert","tags":["weather","alert / error","lightning alert","storm advisory"]},{"name":"mdi:flash-alert-outline","tags":["weather","alert / error","lightning alert outline","storm advisory outline"]},{"name":"mdi:flash-off-outline","tags":[]},{"name":"mdi:flash-outline","tags":["weather","lightning bolt outline"]},{"name":"mdi:flash-red-eye","tags":[]},{"name":"mdi:flash-triangle","tags":["home automation","high voltage"]},{"name":"mdi:flash-triangle-outline","tags":["home automation","high voltage outline"]},{"name":"mdi:flashlight","tags":["torch"]},{"name":"mdi:flashlight-off","tags":["torch off"]},{"name":"mdi:flask","tags":["science","gaming / rpg"]},{"name":"mdi:flask-empty","tags":["science","gaming / rpg"]},{"name":"mdi:flask-empty-minus","tags":["science"]},{"name":"mdi:flask-empty-minus-outline","tags":["science"]},{"name":"mdi:flask-empty-off","tags":[]},{"name":"mdi:flask-empty-off-outline","tags":[]},{"name":"mdi:flask-empty-outline","tags":["science","gaming / rpg"]},{"name":"mdi:flask-empty-plus","tags":["science"]},{"name":"mdi:flask-empty-plus-outline","tags":["science"]},{"name":"mdi:flask-empty-remove","tags":["science"]},{"name":"mdi:flask-empty-remove-outline","tags":["science"]},{"name":"mdi:flask-minus","tags":["science"]},{"name":"mdi:flask-minus-outline","tags":["science"]},{"name":"mdi:flask-off","tags":[]},{"name":"mdi:flask-off-outline","tags":[]},{"name":"mdi:flask-outline","tags":["science","gaming / rpg"]},{"name":"mdi:flask-plus","tags":["science"]},{"name":"mdi:flask-plus-outline","tags":["science"]},{"name":"mdi:flask-remove","tags":["science"]},{"name":"mdi:flask-remove-outline","tags":["science"]},{"name":"mdi:flask-round-bottom","tags":["science"]},{"name":"mdi:flask-round-bottom-empty","tags":["science"]},{"name":"mdi:flask-round-bottom-empty-outline","tags":["science"]},{"name":"mdi:flask-round-bottom-outline","tags":["science"]},{"name":"mdi:fleur-de-lis","tags":[]},{"name":"mdi:floor-lamp","tags":["home automation","floor light"]},{"name":"mdi:floor-lamp-dual","tags":["home automation","floor light dual"]},{"name":"mdi:floor-lamp-dual-outline","tags":["home automation","floor light dual outline"]},{"name":"mdi:floor-lamp-outline","tags":["home automation","floor light outline"]},{"name":"mdi:floor-lamp-torchiere","tags":["home automation","floor light torchiere"]},{"name":"mdi:floor-lamp-torchiere-outline","tags":["home automation"]},{"name":"mdi:floor-lamp-torchiere-variant","tags":["home automation","floor light torchiere variant"]},{"name":"mdi:floor-lamp-torchiere-variant-outline","tags":["home automation","floor light torchiere variant outline"]},{"name":"mdi:floor-plan","tags":["home automation"]},{"name":"mdi:floppy","tags":[]},{"name":"mdi:floppy-variant","tags":[]},{"name":"mdi:flower-pollen","tags":["nature","agriculture","allergy"]},{"name":"mdi:flower-pollen-outline","tags":["nature","agriculture","allergy outline"]},{"name":"mdi:flower-poppy","tags":["nature","agriculture","plant"]},{"name":"mdi:flower-tulip","tags":["nature","agriculture","plant"]},{"name":"mdi:flower-tulip-outline","tags":["nature","agriculture","plant"]},{"name":"mdi:focus-auto","tags":["photography"]},{"name":"mdi:focus-field","tags":["photography"]},{"name":"mdi:focus-field-horizontal","tags":["photography"]},{"name":"mdi:focus-field-vertical","tags":["photography"]},{"name":"mdi:folder-alert","tags":["files / folders","alert / error","folder warning"]},{"name":"mdi:folder-alert-outline","tags":["files / folders","alert / error","folder warning outline"]},{"name":"mdi:folder-arrow-down","tags":["files / folders","folder download"]},{"name":"mdi:folder-arrow-down-outline","tags":["files / folders","folder download outline"]},{"name":"mdi:folder-arrow-left","tags":["files / folders"]},{"name":"mdi:folder-arrow-left-outline","tags":["files / folders"]},{"name":"mdi:folder-arrow-left-right","tags":["files / folders"]},{"name":"mdi:folder-arrow-left-right-outline","tags":["files / folders"]},{"name":"mdi:folder-arrow-right","tags":["files / folders"]},{"name":"mdi:folder-arrow-right-outline","tags":["files / folders"]},{"name":"mdi:folder-arrow-up","tags":["files / folders","folder upload"]},{"name":"mdi:folder-arrow-up-down","tags":["files / folders","folder transfer"]},{"name":"mdi:folder-arrow-up-down-outline","tags":["files / folders","folder transfer outline"]},{"name":"mdi:folder-arrow-up-outline","tags":["files / folders","folder upload outline"]},{"name":"mdi:folder-cancel","tags":["files / folders"]},{"name":"mdi:folder-cancel-outline","tags":["files / folders"]},{"name":"mdi:folder-check","tags":["files / folders"]},{"name":"mdi:folder-check-outline","tags":["files / folders"]},{"name":"mdi:folder-clock","tags":["files / folders","date / time"]},{"name":"mdi:folder-clock-outline","tags":["files / folders","date / time"]},{"name":"mdi:folder-cog","tags":["settings","files / folders","folder cog"]},{"name":"mdi:folder-cog-outline","tags":["settings","files / folders","folder cog outline"]},{"name":"mdi:folder-download","tags":["files / folders"]},{"name":"mdi:folder-download-outline","tags":["files / folders"]},{"name":"mdi:folder-edit","tags":["files / folders","edit / modify"]},{"name":"mdi:folder-edit-outline","tags":["edit / modify","files / folders"]},{"name":"mdi:folder-eye","tags":["files / folders"]},{"name":"mdi:folder-eye-outline","tags":["files / folders"]},{"name":"mdi:folder-file","tags":["files / folders"]},{"name":"mdi:folder-file-outline","tags":["files / folders"]},{"name":"mdi:folder-heart","tags":["files / folders"]},{"name":"mdi:folder-heart-outline","tags":["files / folders"]},{"name":"mdi:folder-hidden","tags":["files / folders"]},{"name":"mdi:folder-home","tags":["files / folders","home automation","folder house"]},{"name":"mdi:folder-home-outline","tags":["files / folders","home automation","folder house outline"]},{"name":"mdi:folder-image","tags":["files / folders"]},{"name":"mdi:folder-information","tags":["files / folders"]},{"name":"mdi:folder-information-outline","tags":["files / folders"]},{"name":"mdi:folder-key","tags":["files / folders"]},{"name":"mdi:folder-key-network","tags":["files / folders"]},{"name":"mdi:folder-key-network-outline","tags":["files / folders"]},{"name":"mdi:folder-key-outline","tags":["files / folders"]},{"name":"mdi:folder-lock","tags":["lock","files / folders"]},{"name":"mdi:folder-lock-open","tags":["lock","files / folders"]},{"name":"mdi:folder-lock-open-outline","tags":["files / folders","lock"]},{"name":"mdi:folder-lock-outline","tags":["files / folders","lock"]},{"name":"mdi:folder-marker","tags":["geographic information system","files / folders","navigation","folder location"]},{"name":"mdi:folder-marker-outline","tags":["geographic information system","files / folders","navigation","folder location outline"]},{"name":"mdi:folder-minus","tags":["files / folders"]},{"name":"mdi:folder-minus-outline","tags":["files / folders"]},{"name":"mdi:folder-move-outline","tags":["files / folders"]},{"name":"mdi:folder-multiple","tags":["files / folders","folders"]},{"name":"mdi:folder-multiple-outline","tags":["files / folders","folders outline"]},{"name":"mdi:folder-multiple-plus","tags":["files / folders"]},{"name":"mdi:folder-multiple-plus-outline","tags":["files / folders"]},{"name":"mdi:folder-music","tags":["files / folders","music"]},{"name":"mdi:folder-music-outline","tags":["files / folders","music"]},{"name":"mdi:folder-network","tags":["files / folders"]},{"name":"mdi:folder-network-outline","tags":["files / folders"]},{"name":"mdi:folder-off","tags":["files / folders"]},{"name":"mdi:folder-off-outline","tags":["files / folders"]},{"name":"mdi:folder-open","tags":["files / folders"]},{"name":"mdi:folder-open-outline","tags":["files / folders"]},{"name":"mdi:folder-play","tags":["files / folders","folder media","folder music","folder video"]},{"name":"mdi:folder-play-outline","tags":["files / folders","folder media outline","folder music outline","folder video outline"]},{"name":"mdi:folder-plus","tags":["files / folders","create new folder","folder add"]},{"name":"mdi:folder-pound","tags":["files / folders","developer / languages","folder hash"]},{"name":"mdi:folder-pound-outline","tags":["files / folders","developer / languages","folder hash outline"]},{"name":"mdi:folder-question","tags":["files / folders","folder help"]},{"name":"mdi:folder-question-outline","tags":["files / folders","folder help outline"]},{"name":"mdi:folder-refresh","tags":["files / folders"]},{"name":"mdi:folder-refresh-outline","tags":["files / folders"]},{"name":"mdi:folder-remove","tags":["files / folders"]},{"name":"mdi:folder-remove-outline","tags":["files / folders"]},{"name":"mdi:folder-search","tags":["files / folders"]},{"name":"mdi:folder-search-outline","tags":["files / folders"]},{"name":"mdi:folder-settings","tags":["settings","files / folders"]},{"name":"mdi:folder-settings-outline","tags":["settings","files / folders"]},{"name":"mdi:folder-star-multiple","tags":["files / folders","folder favorite multiple"]},{"name":"mdi:folder-star-multiple-outline","tags":["files / folders","folder favorite multiple outline"]},{"name":"mdi:folder-swap","tags":["files / folders","folder transfer"]},{"name":"mdi:folder-swap-outline","tags":["files / folders","folder transfer outline"]},{"name":"mdi:folder-sync","tags":["files / folders"]},{"name":"mdi:folder-sync-outline","tags":["files / folders"]},{"name":"mdi:folder-table","tags":["files / folders"]},{"name":"mdi:folder-table-outline","tags":["files / folders"]},{"name":"mdi:folder-text","tags":["files / folders"]},{"name":"mdi:folder-text-outline","tags":["files / folders"]},{"name":"mdi:folder-upload","tags":["files / folders"]},{"name":"mdi:folder-upload-outline","tags":["files / folders"]},{"name":"mdi:folder-wrench","tags":["files / folders","folder settings"]},{"name":"mdi:folder-wrench-outline","tags":["files / folders","folder settings outline"]},{"name":"mdi:folder-zip","tags":["files / folders","compressed folder"]},{"name":"mdi:folder-zip-outline","tags":["files / folders","compressed folder outline"]},{"name":"mdi:food-apple","tags":["food / drink","agriculture"]},{"name":"mdi:food-apple-outline","tags":["food / drink","agriculture"]},{"name":"mdi:food-croissant","tags":["food / drink"]},{"name":"mdi:food-drumstick","tags":["food / drink","chicken leg","turkey leg","meat"]},{"name":"mdi:food-drumstick-off","tags":["food / drink","chicken leg off","turkey leg off","meat off"]},{"name":"mdi:food-drumstick-off-outline","tags":["food / drink","chicken leg off outline","turkey leg off outline","meat off outline"]},{"name":"mdi:food-drumstick-outline","tags":["food / drink","chicken leg outline","turkey leg outline","meat outline"]},{"name":"mdi:food-halal","tags":["food / drink","food muslim","dietary restriction"]},{"name":"mdi:food-hot-dog","tags":["food / drink","food weiner","food frankfurter"]},{"name":"mdi:food-kosher","tags":["food / drink","food jewish","dietary restriction"]},{"name":"mdi:food-steak","tags":["food / drink","meat","beef"]},{"name":"mdi:food-steak-off","tags":["food / drink","meat off","beef off"]},{"name":"mdi:food-turkey","tags":["food / drink","holiday","dinner","thanksgiving"]},{"name":"mdi:food-variant","tags":["food / drink"]},{"name":"mdi:food-variant-off","tags":["food / drink"]},{"name":"mdi:foot-print","tags":[]},{"name":"mdi:football-australian","tags":["sport"]},{"name":"mdi:football-helmet","tags":["sport"]},{"name":"mdi:forest-outline","tags":["nature","agriculture","places","forestry outline","pine tree multiple outline"]},{"name":"mdi:forklift","tags":["transportation + road"]},{"name":"mdi:form-dropdown","tags":["form"]},{"name":"mdi:form-select","tags":["form"]},{"name":"mdi:form-textarea","tags":["form"]},{"name":"mdi:form-textbox","tags":["form","rename"]},{"name":"mdi:form-textbox-lock","tags":["form","lock"]},{"name":"mdi:form-textbox-password","tags":["form"]},{"name":"mdi:format-align-bottom","tags":["text / content / format"]},{"name":"mdi:format-align-middle","tags":["text / content / format"]},{"name":"mdi:format-align-top","tags":["text / content / format"]},{"name":"mdi:format-annotation-minus","tags":["text / content / format"]},{"name":"mdi:format-annotation-plus","tags":["text / content / format","format annotation add"]},{"name":"mdi:format-color-highlight","tags":["color","text / content / format","format colour highlight"]},{"name":"mdi:format-color-marker-cancel","tags":["text / content / format","color","format color redact"]},{"name":"mdi:format-columns","tags":["text / content / format"]},{"name":"mdi:format-float-center","tags":["text / content / format","format float centre"]},{"name":"mdi:format-float-left","tags":["text / content / format"]},{"name":"mdi:format-float-none","tags":["text / content / format"]},{"name":"mdi:format-float-right","tags":["text / content / format"]},{"name":"mdi:format-font","tags":["text / content / format"]},{"name":"mdi:format-font-size-decrease","tags":["text / content / format"]},{"name":"mdi:format-font-size-increase","tags":["text / content / format"]},{"name":"mdi:format-header-1","tags":["text / content / format","format heading 1"]},{"name":"mdi:format-header-2","tags":["text / content / format","format heading 2"]},{"name":"mdi:format-header-3","tags":["text / content / format","format heading 3"]},{"name":"mdi:format-header-4","tags":["text / content / format","format heading 4"]},{"name":"mdi:format-header-5","tags":["text / content / format","format heading 5"]},{"name":"mdi:format-header-6","tags":["text / content / format","format heading 6"]},{"name":"mdi:format-header-decrease","tags":["text / content / format","format heading decease"]},{"name":"mdi:format-header-equal","tags":["text / content / format","format heading equal"]},{"name":"mdi:format-header-increase","tags":["text / content / format","format heading increase"]},{"name":"mdi:format-header-pound","tags":["text / content / format","format header hash","format heading pound","format heading hash","format heading markdown"]},{"name":"mdi:format-horizontal-align-center","tags":["text / content / format","format horizontal align centre","arrow horizontal collapse"]},{"name":"mdi:format-horizontal-align-left","tags":["text / content / format"]},{"name":"mdi:format-horizontal-align-right","tags":["text / content / format"]},{"name":"mdi:format-letter-case","tags":["text / content / format"]},{"name":"mdi:format-letter-case-lower","tags":["text / content / format","format lowercase"]},{"name":"mdi:format-letter-case-upper","tags":["text / content / format","format uppercase"]},{"name":"mdi:format-letter-ends-with","tags":["text / content / format"]},{"name":"mdi:format-letter-matches","tags":["text / content / format"]},{"name":"mdi:format-letter-spacing","tags":["text / content / format","format kerning"]},{"name":"mdi:format-letter-spacing-variant","tags":["text / content / format"]},{"name":"mdi:format-letter-starts-with","tags":["text / content / format"]},{"name":"mdi:format-line-height","tags":["text / content / format"]},{"name":"mdi:format-list-bulleted-triangle","tags":["text / content / format"]},{"name":"mdi:format-list-bulleted-type","tags":["text / content / format"]},{"name":"mdi:format-list-checks","tags":["text / content / format","to do"]},{"name":"mdi:format-list-group","tags":["text / content / format"]},{"name":"mdi:format-list-group-plus","tags":["text / content / format","format list group add"]},{"name":"mdi:format-list-text","tags":["text / content / format"]},{"name":"mdi:format-overline","tags":["text / content / format"]},{"name":"mdi:format-page-split","tags":["text / content / format"]},{"name":"mdi:format-paragraph","tags":["text / content / format"]},{"name":"mdi:format-paragraph-spacing","tags":["text / content / format"]},{"name":"mdi:format-pilcrow","tags":["text / content / format"]},{"name":"mdi:format-quote-close-outline","tags":["text / content / format"]},{"name":"mdi:format-quote-open","tags":["text / content / format"]},{"name":"mdi:format-quote-open-outline","tags":["text / content / format"]},{"name":"mdi:format-section","tags":["text / content / format"]},{"name":"mdi:format-subscript","tags":["text / content / format"]},{"name":"mdi:format-superscript","tags":["text / content / format","math","exponent"]},{"name":"mdi:format-text","tags":["text / content / format"]},{"name":"mdi:format-text-rotation-down-vertical","tags":["text / content / format"]},{"name":"mdi:format-text-variant","tags":["text / content / format"]},{"name":"mdi:format-text-variant-outline","tags":["text / content / format"]},{"name":"mdi:format-underline-wavy","tags":["text / content / format"]},{"name":"mdi:format-wrap-inline","tags":["text / content / format"]},{"name":"mdi:format-wrap-square","tags":["text / content / format"]},{"name":"mdi:format-wrap-tight","tags":["text / content / format"]},{"name":"mdi:format-wrap-top-bottom","tags":["text / content / format"]},{"name":"mdi:forum-minus","tags":["chat minus","forum subtract","chat subtract"]},{"name":"mdi:forum-minus-outline","tags":["chat minus outline","forum subtract outline","chat subtract outline"]},{"name":"mdi:forum-plus","tags":["chat plus","forum add","chat add"]},{"name":"mdi:forum-plus-outline","tags":["chat plus outline","chat add outline","forum add outline"]},{"name":"mdi:forum-remove","tags":["forum delete","chat remove","chat delete"]},{"name":"mdi:forum-remove-outline","tags":["forum delete outline","chat remove outline","chat delete outline"]},{"name":"mdi:forwardburger","tags":[]},{"name":"mdi:fountain","tags":[]},{"name":"mdi:fountain-pen","tags":["drawing / art"]},{"name":"mdi:fountain-pen-tip","tags":["drawing / art"]},{"name":"mdi:fraction-one-half","tags":[]},{"name":"mdi:french-fries","tags":["food / drink","chips","finger chips","french fry","fried potatoes","fries","frites"]},{"name":"mdi:frequently-asked-questions","tags":["faq"]},{"name":"mdi:fridge","tags":["home automation","fridge filled","refrigerator","kitchen"]},{"name":"mdi:fridge-alert","tags":["home automation","alert / error"]},{"name":"mdi:fridge-alert-outline","tags":["home automation","alert / error"]},{"name":"mdi:fridge-bottom","tags":["home automation","fridge filled top","refrigerator bottom"]},{"name":"mdi:fridge-industrial","tags":["home automation"]},{"name":"mdi:fridge-industrial-alert","tags":["home automation","alert / error"]},{"name":"mdi:fridge-industrial-alert-outline","tags":["home automation","alert / error"]},{"name":"mdi:fridge-industrial-off","tags":["home automation"]},{"name":"mdi:fridge-industrial-off-outline","tags":["home automation"]},{"name":"mdi:fridge-industrial-outline","tags":["home automation"]},{"name":"mdi:fridge-off","tags":["home automation"]},{"name":"mdi:fridge-off-outline","tags":["home automation"]},{"name":"mdi:fridge-outline","tags":["home automation","kitchen","refrigerator outline"]},{"name":"mdi:fridge-top","tags":["home automation","fridge filled bottom","refrigerator top"]},{"name":"mdi:fridge-variant","tags":["home automation"]},{"name":"mdi:fridge-variant-alert","tags":["home automation","alert / error"]},{"name":"mdi:fridge-variant-alert-outline","tags":["home automation","alert / error"]},{"name":"mdi:fridge-variant-off","tags":["home automation"]},{"name":"mdi:fridge-variant-off-outline","tags":["home automation"]},{"name":"mdi:fridge-variant-outline","tags":["home automation"]},{"name":"mdi:fruit-cherries","tags":["food / drink","agriculture"]},{"name":"mdi:fruit-cherries-off","tags":["food / drink","agriculture"]},{"name":"mdi:fruit-citrus","tags":["food / drink","agriculture","fruit lemon","fruit lime"]},{"name":"mdi:fruit-citrus-off","tags":["food / drink","agriculture"]},{"name":"mdi:fruit-grapes","tags":["food / drink","agriculture"]},{"name":"mdi:fruit-grapes-outline","tags":["food / drink","agriculture"]},{"name":"mdi:fruit-pear","tags":["food / drink"]},{"name":"mdi:fruit-pineapple","tags":["food / drink","agriculture","fruit ananas"]},{"name":"mdi:fruit-watermelon","tags":["food / drink","agriculture"]},{"name":"mdi:fuel","tags":["automotive","petrol","gasoline"]},{"name":"mdi:fuel-cell","tags":["automotive","battery","battery"]},{"name":"mdi:function","tags":["math"]},{"name":"mdi:function-variant","tags":["math"]},{"name":"mdi:furigana-horizontal","tags":["text / content / format","ruby horizontal"]},{"name":"mdi:furigana-vertical","tags":["text / content / format","zhuyin","ruby vertical"]},{"name":"mdi:fuse","tags":["automotive"]},{"name":"mdi:fuse-alert","tags":["automotive","alert / error"]},{"name":"mdi:fuse-blade","tags":["automotive"]},{"name":"mdi:fuse-off","tags":["automotive"]},{"name":"mdi:gamepad-circle","tags":["gaming / rpg","controller circle"]},{"name":"mdi:gamepad-circle-down","tags":["gaming / rpg","controller circle down"]},{"name":"mdi:gamepad-circle-left","tags":["gaming / rpg","controller circle left"]},{"name":"mdi:gamepad-circle-outline","tags":["gaming / rpg","controller circle outline"]},{"name":"mdi:gamepad-circle-right","tags":["gaming / rpg","controller circle right"]},{"name":"mdi:gamepad-circle-up","tags":["gaming / rpg","controller circle up"]},{"name":"mdi:gamepad-down","tags":["gaming / rpg","controller down"]},{"name":"mdi:gamepad-left","tags":["gaming / rpg","controller left"]},{"name":"mdi:gamepad-outline","tags":["gaming / rpg","home automation","controller outline","games outline"]},{"name":"mdi:gamepad-right","tags":["gaming / rpg","controller right"]},{"name":"mdi:gamepad-round","tags":["gaming / rpg","controller round"]},{"name":"mdi:gamepad-round-down","tags":["gaming / rpg","controller round down"]},{"name":"mdi:gamepad-round-left","tags":["gaming / rpg","controller round left"]},{"name":"mdi:gamepad-round-outline","tags":["gaming / rpg","controller round outline"]},{"name":"mdi:gamepad-round-right","tags":["gaming / rpg","controller round right"]},{"name":"mdi:gamepad-round-up","tags":["gaming / rpg","controller round up"]},{"name":"mdi:gamepad-up","tags":["gaming / rpg","controller up"]},{"name":"mdi:gamepad-variant","tags":["gaming / rpg","controller variant"]},{"name":"mdi:gamepad-variant-outline","tags":["gaming / rpg","controller variant outline"]},{"name":"mdi:gamma","tags":["alpha / numeric"]},{"name":"mdi:gantry-crane","tags":[]},{"name":"mdi:garage","tags":["home automation"]},{"name":"mdi:garage-alert","tags":["home automation","alert / error","garage warning"]},{"name":"mdi:garage-alert-variant","tags":["home automation","alert / error"]},{"name":"mdi:garage-lock","tags":["home automation","lock"]},{"name":"mdi:garage-open","tags":["home automation"]},{"name":"mdi:garage-open-variant","tags":["home automation"]},{"name":"mdi:garage-variant","tags":["home automation"]},{"name":"mdi:garage-variant-lock","tags":["home automation","lock"]},{"name":"mdi:gas-burner","tags":["home automation","stove burner","cooktop burner","grill"]},{"name":"mdi:gas-cylinder","tags":["tank","oxygen tank"]},{"name":"mdi:gas-station-off","tags":[]},{"name":"mdi:gas-station-off-outline","tags":[]},{"name":"mdi:gate","tags":["home automation"]},{"name":"mdi:gate-alert","tags":["home automation","alert / error"]},{"name":"mdi:gate-and","tags":["logic gate and"]},{"name":"mdi:gate-arrow-left","tags":["home automation"]},{"name":"mdi:gate-arrow-right","tags":["home automation"]},{"name":"mdi:gate-buffer","tags":[]},{"name":"mdi:gate-nand","tags":["logic gate nand"]},{"name":"mdi:gate-nor","tags":["logic gate nor"]},{"name":"mdi:gate-not","tags":["logic gate not"]},{"name":"mdi:gate-open","tags":["home automation"]},{"name":"mdi:gate-or","tags":["logic gate or"]},{"name":"mdi:gate-xnor","tags":["logic gate xnor"]},{"name":"mdi:gate-xor","tags":["logic gate xor"]},{"name":"mdi:gauge-empty","tags":["automotive","home automation"]},{"name":"mdi:gauge-full","tags":["automotive","home automation"]},{"name":"mdi:gauge-low","tags":["automotive","home automation"]},{"name":"mdi:gavel","tags":["court hammer"]},{"name":"mdi:gender-female","tags":["venus"]},{"name":"mdi:gender-male","tags":["mars"]},{"name":"mdi:gender-male-female","tags":[]},{"name":"mdi:gender-male-female-variant","tags":["mercury"]},{"name":"mdi:gender-non-binary","tags":["gender enby"]},{"name":"mdi:gender-transgender","tags":[]},{"name":"mdi:gesture-double-tap","tags":["interaction double tap","hand double tap"]},{"name":"mdi:gesture-pinch","tags":[]},{"name":"mdi:gesture-spread","tags":[]},{"name":"mdi:gesture-swipe-down","tags":[]},{"name":"mdi:gesture-swipe-horizontal","tags":[]},{"name":"mdi:gesture-swipe-left","tags":[]},{"name":"mdi:gesture-swipe-right","tags":[]},{"name":"mdi:gesture-swipe-up","tags":[]},{"name":"mdi:gesture-swipe-vertical","tags":[]},{"name":"mdi:gesture-tap","tags":["interaction tap","hand tap","gesture touch"]},{"name":"mdi:gesture-tap-box","tags":["gesture touch box"]},{"name":"mdi:gesture-tap-button","tags":["form","call to action","cta","button pointer","gesture touch button"]},{"name":"mdi:gesture-two-double-tap","tags":[]},{"name":"mdi:gesture-two-tap","tags":[]},{"name":"mdi:ghost","tags":["gaming / rpg","inky","blinky","pinky","clyde"]},{"name":"mdi:ghost-off","tags":["gaming / rpg"]},{"name":"mdi:ghost-off-outline","tags":["gaming / rpg"]},{"name":"mdi:ghost-outline","tags":["gaming / rpg"]},{"name":"mdi:gift","tags":["holiday","present","package","donate"]},{"name":"mdi:gift-off","tags":["holiday","present off","package off","donate off"]},{"name":"mdi:gift-off-outline","tags":["holiday","present off outline","package off outline","donate off outline"]},{"name":"mdi:gift-open","tags":["holiday","present open","package open"]},{"name":"mdi:gift-open-outline","tags":["holiday","present open outline","package open outline"]},{"name":"mdi:gift-outline","tags":["shopping","holiday","donate outline","present outline","package outline"]},{"name":"mdi:glass-cocktail-off","tags":["food / drink"]},{"name":"mdi:glass-flute","tags":["food / drink","alcohol","cocktail","cup","drink"]},{"name":"mdi:glass-fragile","tags":["food / drink","glass broken"]},{"name":"mdi:glass-mug","tags":["food / drink","pub","bar","beer","alcohol","cup","drink","local bar"]},{"name":"mdi:glass-mug-off","tags":["food / drink"]},{"name":"mdi:glass-mug-variant","tags":["food / drink","pub","bar","beer","drink","alcohol","cup","local bar"]},{"name":"mdi:glass-mug-variant-off","tags":["food / drink"]},{"name":"mdi:glass-pint-outline","tags":["food / drink"]},{"name":"mdi:glass-stange","tags":["food / drink","alcohol","bar","cocktail","cup","drink"]},{"name":"mdi:glass-tulip","tags":["food / drink","bar","alcohol","cocktail","cup","drink"]},{"name":"mdi:glass-wine","tags":["food / drink","bar","alcohol","cocktail","cup","drink"]},{"name":"mdi:glasses","tags":["clothing"]},{"name":"mdi:globe-light","tags":["home automation"]},{"name":"mdi:globe-light-outline","tags":["home automation"]},{"name":"mdi:globe-model","tags":[]},{"name":"mdi:go-kart","tags":["sport","cart"]},{"name":"mdi:go-kart-track","tags":[]},{"name":"mdi:gold","tags":[]},{"name":"mdi:golf-cart","tags":["sport","transportation + other"]},{"name":"mdi:gondola","tags":["transportation + other","cable car"]},{"name":"mdi:gradient-horizontal","tags":["drawing / art"]},{"name":"mdi:graph","tags":["dependency","dependencies"]},{"name":"mdi:graph-outline","tags":["dependency","dependencies"]},{"name":"mdi:grave-stone","tags":["holiday","headstone","tombstone","cemetery","graveyard"]},{"name":"mdi:greater-than","tags":["math"]},{"name":"mdi:greater-than-or-equal","tags":["math"]},{"name":"mdi:greenhouse","tags":["home automation","agriculture","nature","glasshouse","hothouse","shed"]},{"name":"mdi:grid-large","tags":[]},{"name":"mdi:group","tags":[]},{"name":"mdi:guitar-acoustic","tags":["music"]},{"name":"mdi:guitar-electric","tags":["music"]},{"name":"mdi:guitar-pick","tags":["music"]},{"name":"mdi:guitar-pick-outline","tags":["music"]},{"name":"mdi:guy-fawkes-mask","tags":[]},{"name":"mdi:hair-dryer","tags":["health / beauty"]},{"name":"mdi:hair-dryer-outline","tags":["health / beauty"]},{"name":"mdi:halloween","tags":["holiday","pumpkin face","pumpkin carved","jack o lantern","emoji halloween","emoticon halloween"]},{"name":"mdi:hamburger","tags":["food / drink","burger","fast food","food"]},{"name":"mdi:hamburger-check","tags":["food / drink","burger check"]},{"name":"mdi:hamburger-minus","tags":["food / drink","burger minus"]},{"name":"mdi:hamburger-off","tags":["food / drink","burger off","fast food off","food off"]},{"name":"mdi:hamburger-plus","tags":["food / drink","burger plus","burger add"]},{"name":"mdi:hamburger-remove","tags":["food / drink","burger remove"]},{"name":"mdi:hammer-sickle","tags":["communism"]},{"name":"mdi:hand-back-left-off","tags":[]},{"name":"mdi:hand-back-left-off-outline","tags":[]},{"name":"mdi:hand-back-right-off","tags":[]},{"name":"mdi:hand-back-right-off-outline","tags":[]},{"name":"mdi:hand-clap","tags":["applause"]},{"name":"mdi:hand-clap-off","tags":["applause off"]},{"name":"mdi:hand-coin","tags":["banking","charity","donation"]},{"name":"mdi:hand-coin-outline","tags":["banking","charity outline","donation outline"]},{"name":"mdi:hand-cycle","tags":["sport","hand bike"]},{"name":"mdi:hand-extended","tags":["hand open","hand palm"]},{"name":"mdi:hand-extended-outline","tags":["hand open outline","hand palm outline"]},{"name":"mdi:hand-heart-outline","tags":[]},{"name":"mdi:hand-okay","tags":[]},{"name":"mdi:hand-peace","tags":[]},{"name":"mdi:hand-peace-variant","tags":[]},{"name":"mdi:hand-pointing-down","tags":[]},{"name":"mdi:hand-pointing-left","tags":[]},{"name":"mdi:hand-pointing-right","tags":[]},{"name":"mdi:hand-pointing-up","tags":[]},{"name":"mdi:hand-saw","tags":["hardware / tools"]},{"name":"mdi:hand-water","tags":["medical / hospital","hand wash"]},{"name":"mdi:handcuffs","tags":[]},{"name":"mdi:hands-pray","tags":[]},{"name":"mdi:handshake","tags":["business","deal","help","partnership"]},{"name":"mdi:handshake-outline","tags":["business outline","deal outline","help outline","partnership outline"]},{"name":"mdi:hanger","tags":["clothing","home automation","coat hanger","clothes hanger","closet"]},{"name":"mdi:hard-hat","tags":["hardware / tools","clothing","helmet"]},{"name":"mdi:harddisk","tags":["hdd"]},{"name":"mdi:harddisk-plus","tags":["hdd plus"]},{"name":"mdi:harddisk-remove","tags":["hdd remove"]},{"name":"mdi:hazard-lights","tags":["automotive","warning lights"]},{"name":"mdi:hdmi-port","tags":["video / movie","home automation"]},{"name":"mdi:head","tags":[]},{"name":"mdi:head-alert","tags":["alert / error"]},{"name":"mdi:head-alert-outline","tags":["alert / error"]},{"name":"mdi:head-check","tags":[]},{"name":"mdi:head-check-outline","tags":[]},{"name":"mdi:head-cog-outline","tags":["settings","psychology outline"]},{"name":"mdi:head-dots-horizontal","tags":["head thinking"]},{"name":"mdi:head-dots-horizontal-outline","tags":["head thinking outline"]},{"name":"mdi:head-flash","tags":["head ache"]},{"name":"mdi:head-flash-outline","tags":["head ache outline"]},{"name":"mdi:head-heart","tags":["head love"]},{"name":"mdi:head-heart-outline","tags":["head love outline"]},{"name":"mdi:head-lightbulb","tags":["head idea","head bulb"]},{"name":"mdi:head-lightbulb-outline","tags":["head idea outline","head bulb outline"]},{"name":"mdi:head-minus","tags":[]},{"name":"mdi:head-minus-outline","tags":[]},{"name":"mdi:head-outline","tags":[]},{"name":"mdi:head-plus","tags":[]},{"name":"mdi:head-plus-outline","tags":[]},{"name":"mdi:head-question","tags":[]},{"name":"mdi:head-question-outline","tags":[]},{"name":"mdi:head-remove","tags":[]},{"name":"mdi:head-remove-outline","tags":[]},{"name":"mdi:head-snowflake","tags":["head freeze","brain freeze"]},{"name":"mdi:head-snowflake-outline","tags":["head freeze outline","brain freeze outline"]},{"name":"mdi:head-sync","tags":["head reload","head refresh"]},{"name":"mdi:head-sync-outline","tags":["head reload outline","head refresh outline"]},{"name":"mdi:headphones-bluetooth","tags":[]},{"name":"mdi:headphones-off","tags":["audio","device / tech","music"]},{"name":"mdi:headphones-settings","tags":["audio","settings"]},{"name":"mdi:headset-dock","tags":["audio"]},{"name":"mdi:headset-off","tags":["audio","device / tech"]},{"name":"mdi:heart-box","tags":[]},{"name":"mdi:heart-box-outline","tags":[]},{"name":"mdi:heart-broken","tags":[]},{"name":"mdi:heart-broken-outline","tags":[]},{"name":"mdi:heart-circle","tags":[]},{"name":"mdi:heart-circle-outline","tags":[]},{"name":"mdi:heart-cog","tags":["settings"]},{"name":"mdi:heart-cog-outline","tags":["settings"]},{"name":"mdi:heart-flash","tags":["medical / hospital","aed","defibrillator"]},{"name":"mdi:heart-half","tags":["gaming / rpg"]},{"name":"mdi:heart-half-full","tags":["gaming / rpg"]},{"name":"mdi:heart-half-outline","tags":["gaming / rpg"]},{"name":"mdi:heart-minus","tags":[]},{"name":"mdi:heart-minus-outline","tags":[]},{"name":"mdi:heart-multiple","tags":["hearts"]},{"name":"mdi:heart-multiple-outline","tags":["hearts outline"]},{"name":"mdi:heart-off","tags":["medical / hospital"]},{"name":"mdi:heart-off-outline","tags":["medical / hospital"]},{"name":"mdi:heart-plus","tags":[]},{"name":"mdi:heart-plus-outline","tags":[]},{"name":"mdi:heart-remove","tags":[]},{"name":"mdi:heart-remove-outline","tags":[]},{"name":"mdi:heart-settings","tags":["settings"]},{"name":"mdi:heart-settings-outline","tags":["settings"]},{"name":"mdi:heat-wave","tags":["home automation","weather","agriculture","keep warm","warmth"]},{"name":"mdi:heating-coil","tags":["home automation","radiator coil","heated floor"]},{"name":"mdi:helicopter","tags":["transportation + flying"]},{"name":"mdi:help","tags":["question mark"]},{"name":"mdi:help-box","tags":["question mark box"]},{"name":"mdi:help-box-multiple","tags":["quiz","question box multiple"]},{"name":"mdi:help-box-multiple-outline","tags":["quiz outline","question box multiple outline"]},{"name":"mdi:help-box-outline","tags":["question box outline"]},{"name":"mdi:help-network","tags":["question network"]},{"name":"mdi:help-network-outline","tags":["question network outline"]},{"name":"mdi:help-rhombus","tags":["question mark rhombus"]},{"name":"mdi:help-rhombus-outline","tags":["question mark rhombus outline"]},{"name":"mdi:hexadecimal","tags":["developer / languages"]},{"name":"mdi:hexagon","tags":["shape"]},{"name":"mdi:hexagon-multiple","tags":["shape","hexagons"]},{"name":"mdi:hexagon-multiple-outline","tags":["nature"]},{"name":"mdi:hexagon-outline","tags":["shape"]},{"name":"mdi:hexagon-slice-1","tags":[]},{"name":"mdi:hexagon-slice-2","tags":[]},{"name":"mdi:hexagon-slice-3","tags":[]},{"name":"mdi:hexagon-slice-4","tags":[]},{"name":"mdi:hexagon-slice-5","tags":[]},{"name":"mdi:hexagon-slice-6","tags":[]},{"name":"mdi:hexagram","tags":["shape","holiday","star","christmas star"]},{"name":"mdi:hexagram-outline","tags":["shape","holiday","star outline","christmas star outline"]},{"name":"mdi:high-definition","tags":["video / movie","hd"]},{"name":"mdi:highway","tags":["transportation + road","autobahn","motorway"]},{"name":"mdi:hockey-puck","tags":["sport"]},{"name":"mdi:hololens","tags":["gaming / rpg"]},{"name":"mdi:home-account","tags":["account / user","home automation","home user","house account","house user"]},{"name":"mdi:home-alert","tags":["home automation","alert / error","home warning","house alert","house warning"]},{"name":"mdi:home-alert-outline","tags":["home automation","alert / error","house alert outline","home warning outline","house warning outline"]},{"name":"mdi:home-analytics","tags":["home automation","chart home","home chart","home report","house analytics","house chart"]},{"name":"mdi:home-automation","tags":["home automation","house automation","home wireless","house wireless","smart home","smart house"]},{"name":"mdi:home-battery","tags":["home automation","battery","home energy","home power","home electricity","house energy","house battery","house power"]},{"name":"mdi:home-battery-outline","tags":["home automation","battery","home energy outline","home power outline","home electricity outline","house battery outline","house power outline","house energy outline"]},{"name":"mdi:home-circle","tags":["home automation","house circle"]},{"name":"mdi:home-circle-outline","tags":["home automation","house circle outline"]},{"name":"mdi:home-clock","tags":["home automation","date / time","home time","home schedule","house time","house clock","house schedule"]},{"name":"mdi:home-clock-outline","tags":["home automation","date / time","home time outline","home schedule outline","house clock outline","house time outline","house schedule outline"]},{"name":"mdi:home-edit","tags":["home automation","edit / modify","house edit"]},{"name":"mdi:home-edit-outline","tags":["home automation","edit / modify","house edit outline"]},{"name":"mdi:home-export-outline","tags":["home automation","house export outline"]},{"name":"mdi:home-floor-0","tags":["home automation","house floor 0","home floor zero","house floor zero"]},{"name":"mdi:home-floor-1","tags":["home automation","house floor 1","home floor one","house floor one","home floor first","house floor first"]},{"name":"mdi:home-floor-2","tags":["home automation","house floor 2","home floor two","house floor two","home floor second","house floor second"]},{"name":"mdi:home-floor-3","tags":["home automation","house floor 3","home floor three","house floor three","home floor third","house floor third"]},{"name":"mdi:home-floor-a","tags":["home automation","home floor attic","house floor a","house floor attic"]},{"name":"mdi:home-floor-b","tags":["home automation","home floor basement","house floor b","house floor basement"]},{"name":"mdi:home-floor-g","tags":["home automation","home floor ground","house floor g","house floor ground"]},{"name":"mdi:home-floor-l","tags":["home automation","home floor loft","home floor lower","house floor l","house floor loft","house floor lower"]},{"name":"mdi:home-floor-negative-1","tags":["home automation","house floor negative 1","home floor negative one","home floor minus 1","home floor minus one","house floor negative one","house floor minus 1","house floor minus one"]},{"name":"mdi:home-group","tags":["home automation","house group","neighbourhood","estate","housing estate"]},{"name":"mdi:home-group-minus","tags":["home automation","house group minus"]},{"name":"mdi:home-group-plus","tags":["home automation","house group plus","home group add","house group add"]},{"name":"mdi:home-group-remove","tags":["home automation","house group remove"]},{"name":"mdi:home-import-outline","tags":["home automation","house import outline"]},{"name":"mdi:home-lightbulb","tags":["home automation","home bulb","house lightbulb","house bulb"]},{"name":"mdi:home-lightbulb-outline","tags":["home automation","home bulb outline","house lightbulb outline","house bulb outline"]},{"name":"mdi:home-lightning-bolt","tags":["home automation","home energy","home power","home electricity","home flash","house lightning bolt","house flash"]},{"name":"mdi:home-lightning-bolt-outline","tags":["home automation","home energy","home power","home electricity","home flash","house lightning bolt outline","house flash outline"]},{"name":"mdi:home-lock","tags":["home automation","lock","house lock","home secure","house secure"]},{"name":"mdi:home-lock-open","tags":["home automation","lock","house lock open"]},{"name":"mdi:home-map-marker","tags":["home automation","navigation","house map marker","home location"]},{"name":"mdi:home-minus","tags":["home automation","house minus"]},{"name":"mdi:home-minus-outline","tags":["home automation","house minus outline"]},{"name":"mdi:home-modern","tags":["home automation","house modern"]},{"name":"mdi:home-off","tags":["home automation","house off"]},{"name":"mdi:home-off-outline","tags":["home automation","house off outline"]},{"name":"mdi:home-percent","tags":[]},{"name":"mdi:home-percent-outline","tags":["home automation"]},{"name":"mdi:home-plus","tags":["home automation","home add","house plus","house add"]},{"name":"mdi:home-plus-outline","tags":["home automation","house plus outline","house add outline"]},{"name":"mdi:home-remove","tags":["home automation","house remove"]},{"name":"mdi:home-remove-outline","tags":["home automation","house remove outline"]},{"name":"mdi:home-roof","tags":["home automation","home chimney","home attic","house roof","house attic","house chimney"]},{"name":"mdi:home-search","tags":["home automation","house search","home find","house find"]},{"name":"mdi:home-search-outline","tags":["home automation","house search outline","home find outline","house find outline"]},{"name":"mdi:home-silo","tags":["home automation","agriculture","farm house","farm home"]},{"name":"mdi:home-silo-outline","tags":["agriculture","home automation","farm house outline","farm home outline"]},{"name":"mdi:home-sound-in","tags":["home automation"]},{"name":"mdi:home-sound-in-outline","tags":["home automation"]},{"name":"mdi:home-sound-out","tags":["home automation"]},{"name":"mdi:home-sound-out-outline","tags":["home automation"]},{"name":"mdi:home-switch","tags":["home automation","home swap","house switch","house swap"]},{"name":"mdi:home-switch-outline","tags":["home automation","home swap outline","house swap outline","house switch outline"]},{"name":"mdi:home-thermometer","tags":["home automation","home climate","home temperature","house thermometer","house climate","house temperature"]},{"name":"mdi:home-thermometer-outline","tags":["home automation","home climate outline","home temperature outline","house thermometer outline","house climate outline","house temperature outline"]},{"name":"mdi:hook","tags":[]},{"name":"mdi:hook-off","tags":[]},{"name":"mdi:hoop-house","tags":["agriculture","home automation","green house"]},{"name":"mdi:hops","tags":["food / drink","agriculture"]},{"name":"mdi:horizontal-rotate-clockwise","tags":[]},{"name":"mdi:horizontal-rotate-counterclockwise","tags":[]},{"name":"mdi:horse","tags":["transportation + other","animal","agriculture","equestrian"]},{"name":"mdi:horse-human","tags":["transportation + other","agriculture","people / family","horseback riding","horse riding","equestrian"]},{"name":"mdi:horse-variant","tags":["animal","agriculture","equestrian variant"]},{"name":"mdi:horse-variant-fast","tags":["animal","agriculture"]},{"name":"mdi:horseshoe","tags":["sport","agriculture","luck"]},{"name":"mdi:hospital","tags":["medical / hospital","swiss cross","dispensary"]},{"name":"mdi:hospital-box-outline","tags":["medical / hospital","swiss cross box outline","dispensary box outline"]},{"name":"mdi:hospital-building","tags":["places","medical / hospital"]},{"name":"mdi:hospital-marker","tags":["medical / hospital","navigation","hospital location"]},{"name":"mdi:hours-24","tags":["date / time"]},{"name":"mdi:hubspot","tags":[]},{"name":"mdi:human-baby-changing-table","tags":["people / family","medical / hospital"]},{"name":"mdi:human-capacity-increase","tags":["account / user","transportation + other","people / family"]},{"name":"mdi:human-child","tags":["people / family"]},{"name":"mdi:human-dolly","tags":["people / family","human hand truck","human trolley"]},{"name":"mdi:human-edit","tags":["people / family","edit / modify"]},{"name":"mdi:human-female-boy","tags":["people / family","mother","mom","woman child","mum"]},{"name":"mdi:human-female-dance","tags":["people / family","sport","ballet"]},{"name":"mdi:human-female-female","tags":["people / family","woman woman","women"]},{"name":"mdi:human-female-girl","tags":["people / family","mother","mom","woman child","mum"]},{"name":"mdi:human-male-board","tags":["people / family","teacher","teaching","lecture","college","blackboard","whiteboard","human man board"]},{"name":"mdi:human-male-board-poll","tags":["people / family","teach poll"]},{"name":"mdi:human-male-boy","tags":["people / family","father","dad","man child"]},{"name":"mdi:human-male-child","tags":["people / family"]},{"name":"mdi:human-male-girl","tags":["people / family","father","dad","man child"]},{"name":"mdi:human-male-height","tags":["medical / hospital","people / family"]},{"name":"mdi:human-male-height-variant","tags":["medical / hospital","people / family"]},{"name":"mdi:human-male-male","tags":["people / family","man man","men"]},{"name":"mdi:human-non-binary","tags":["people / family","human genderless","human transgender"]},{"name":"mdi:human-queue","tags":["people / family","human line"]},{"name":"mdi:human-wheelchair","tags":["people / family","medical / hospital","human accessible"]},{"name":"mdi:human-white-cane","tags":["people / family","medical / hospital","human blind"]},{"name":"mdi:hvac-off","tags":["home automation","heating off","ventilation off","air conditioning off"]},{"name":"mdi:hydraulic-oil-level","tags":["automotive"]},{"name":"mdi:hydraulic-oil-temperature","tags":["automotive"]},{"name":"mdi:hydro-power","tags":["device / tech","agriculture","hydraulic turbine","water turbine","watermill"]},{"name":"mdi:hydrogen-station","tags":["automotive"]},{"name":"mdi:ice-cream-off","tags":["food / drink"]},{"name":"mdi:ice-pop","tags":["food / drink","popsicle"]},{"name":"mdi:id-card","tags":[]},{"name":"mdi:identifier","tags":["developer / languages","key"]},{"name":"mdi:ideogram-cjk","tags":["alpha / numeric","ideogram chinese japanese korean","writing system cjk"]},{"name":"mdi:ideogram-cjk-variant","tags":["alpha / numeric","ideogram chinese japanese korean variant","writing system cjk variant"]},{"name":"mdi:image-area","tags":[]},{"name":"mdi:image-area-close","tags":[]},{"name":"mdi:image-broken","tags":[]},{"name":"mdi:image-check","tags":[]},{"name":"mdi:image-check-outline","tags":[]},{"name":"mdi:image-edit","tags":["edit / modify"]},{"name":"mdi:image-edit-outline","tags":["edit / modify"]},{"name":"mdi:image-filter-hdr-outline","tags":["photography","nature","mountain outline","landscape outline"]},{"name":"mdi:image-lock","tags":["lock","photography","image secure"]},{"name":"mdi:image-lock-outline","tags":["photography","lock","image secure outline"]},{"name":"mdi:image-marker","tags":["navigation","image location"]},{"name":"mdi:image-marker-outline","tags":["navigation","image location outline"]},{"name":"mdi:image-minus","tags":[]},{"name":"mdi:image-minus-outline","tags":[]},{"name":"mdi:image-move","tags":[]},{"name":"mdi:image-off","tags":[]},{"name":"mdi:image-off-outline","tags":[]},{"name":"mdi:image-outline","tags":[]},{"name":"mdi:image-plus","tags":["image add"]},{"name":"mdi:image-plus-outline","tags":["image add outline"]},{"name":"mdi:image-refresh","tags":["photography"]},{"name":"mdi:image-refresh-outline","tags":["photography"]},{"name":"mdi:image-remove","tags":[]},{"name":"mdi:image-remove-outline","tags":[]},{"name":"mdi:image-sync","tags":["photography"]},{"name":"mdi:image-sync-outline","tags":["photography"]},{"name":"mdi:import","tags":["input"]},{"name":"mdi:inbox-arrow-down-outline","tags":[]},{"name":"mdi:inbox-arrow-up","tags":["move from inbox"]},{"name":"mdi:inbox-arrow-up-outline","tags":[]},{"name":"mdi:inbox-full","tags":[]},{"name":"mdi:inbox-full-outline","tags":[]},{"name":"mdi:inbox-outline","tags":[]},{"name":"mdi:inbox-remove","tags":[]},{"name":"mdi:inbox-remove-outline","tags":[]},{"name":"mdi:incognito","tags":["anonymous","spy"]},{"name":"mdi:incognito-circle-off","tags":["anonymous circle off","spy circle off"]},{"name":"mdi:incognito-off","tags":["spy off","anonymous off"]},{"name":"mdi:induction","tags":["home automation","automotive","ignition"]},{"name":"mdi:infinity","tags":["math"]},{"name":"mdi:information-box","tags":["settings","info box"]},{"name":"mdi:information-box-outline","tags":["settings","info box outline"]},{"name":"mdi:information-off","tags":["info off","info circle off","information circle off"]},{"name":"mdi:information-off-outline","tags":["info circle off outline","information circle off outline","information off outline","info off outline"]},{"name":"mdi:information-slab-box","tags":["settings","info slab box"]},{"name":"mdi:information-slab-box-outline","tags":["settings","info slab box outline"]},{"name":"mdi:information-slab-circle","tags":["settings","info slab circle"]},{"name":"mdi:information-slab-circle-outline","tags":["settings","info slab circle outline"]},{"name":"mdi:information-slab-symbol","tags":["settings","info slab symbol"]},{"name":"mdi:information-symbol","tags":["settings","info symbol"]},{"name":"mdi:information-variant","tags":["info variant","about variant","information serif symbol","info variant symbol"]},{"name":"mdi:information-variant-box","tags":["settings","info variant box","information serif box","info serif box"]},{"name":"mdi:information-variant-box-outline","tags":["settings","info variant box outline","information serif box outline","info serif box outline"]},{"name":"mdi:information-variant-circle","tags":["settings","information serif circle","info serif circle","info variant circle"]},{"name":"mdi:information-variant-circle-outline","tags":["settings","information serif circle outline","info variant circle outline","info serif circle outline"]},{"name":"mdi:instrument-triangle","tags":["music","dinner bell"]},{"name":"mdi:integrated-circuit-chip","tags":["banking","icc","chip"]},{"name":"mdi:ip","tags":["internet protocol"]},{"name":"mdi:ip-network","tags":[]},{"name":"mdi:ip-network-outline","tags":[]},{"name":"mdi:ip-outline","tags":["internet protocol outline"]},{"name":"mdi:ipod","tags":["apple ipod"]},{"name":"mdi:iron-board","tags":["home automation","clothing"]},{"name":"mdi:island","tags":["places"]},{"name":"mdi:iv-bag","tags":["medical / hospital"]},{"name":"mdi:jeepney","tags":["transportation + road"]},{"name":"mdi:jellyfish","tags":["animal"]},{"name":"mdi:jellyfish-outline","tags":["animal"]},{"name":"mdi:jump-rope","tags":["sport"]},{"name":"mdi:kangaroo","tags":["animal","marsupial"]},{"name":"mdi:keg","tags":["food / drink"]},{"name":"mdi:kettle","tags":["home automation","food / drink","tea kettle","kettle full","tea kettle full"]},{"name":"mdi:kettle-alert","tags":["home automation","alert / error","food / drink","tea kettle alert","kettle full alert","tea kettle full alert"]},{"name":"mdi:kettle-alert-outline","tags":["home automation","alert / error","food / drink","tea kettle alert outline","kettle empty alert","tea kettle empty alert"]},{"name":"mdi:kettle-off","tags":["home automation","food / drink","tea kettle off","tea kettle full off","kettle full off"]},{"name":"mdi:kettle-off-outline","tags":["home automation","food / drink","tea kettle off outline","kettle empty off","tea kettle empty off"]},{"name":"mdi:kettle-outline","tags":["food / drink","home automation","tea kettle outline","kettle empty","tea kettle empty"]},{"name":"mdi:kettle-pour-over","tags":[]},{"name":"mdi:kettle-steam","tags":["home automation","food / drink","tea kettle steam","kettle full steam","tea kettle full steam"]},{"name":"mdi:kettle-steam-outline","tags":["home automation","food / drink","tea kettle steam outline","kettle empty steam","tea kettle empty steam"]},{"name":"mdi:kettlebell","tags":["sport"]},{"name":"mdi:key-alert","tags":["alert / error"]},{"name":"mdi:key-alert-outline","tags":["alert / error"]},{"name":"mdi:key-arrow-right","tags":[]},{"name":"mdi:key-chain","tags":["automotive","home automation"]},{"name":"mdi:key-chain-variant","tags":["automotive","home automation"]},{"name":"mdi:key-change","tags":[]},{"name":"mdi:key-link","tags":["foreign key","sql foreign key"]},{"name":"mdi:key-minus","tags":[]},{"name":"mdi:key-plus","tags":["key add"]},{"name":"mdi:key-remove","tags":[]},{"name":"mdi:key-star","tags":["primary key","sql primary key","key favorite"]},{"name":"mdi:key-variant","tags":["automotive"]},{"name":"mdi:key-wireless","tags":[]},{"name":"mdi:keyboard-close-outline","tags":["keyboard hide outline"]},{"name":"mdi:keyboard-esc","tags":[]},{"name":"mdi:keyboard-f1","tags":[]},{"name":"mdi:keyboard-f10","tags":[]},{"name":"mdi:keyboard-f11","tags":[]},{"name":"mdi:keyboard-f12","tags":[]},{"name":"mdi:keyboard-f2","tags":[]},{"name":"mdi:keyboard-f3","tags":[]},{"name":"mdi:keyboard-f4","tags":[]},{"name":"mdi:keyboard-f5","tags":[]},{"name":"mdi:keyboard-f6","tags":[]},{"name":"mdi:keyboard-f7","tags":[]},{"name":"mdi:keyboard-f8","tags":[]},{"name":"mdi:keyboard-f9","tags":[]},{"name":"mdi:keyboard-off","tags":[]},{"name":"mdi:keyboard-off-outline","tags":[]},{"name":"mdi:keyboard-settings","tags":["settings"]},{"name":"mdi:keyboard-settings-outline","tags":["settings"]},{"name":"mdi:keyboard-space","tags":[]},{"name":"mdi:keyboard-tab-reverse","tags":[]},{"name":"mdi:keyboard-variant","tags":[]},{"name":"mdi:khanda","tags":["religion","sikh"]},{"name":"mdi:klingon","tags":[]},{"name":"mdi:knife","tags":["silverware knife","cutlery knife"]},{"name":"mdi:knife-military","tags":["gaming / rpg","dagger"]},{"name":"mdi:knob","tags":["audio","volume knob","volume control","dial","tuner","switch","adjuster"]},{"name":"mdi:koala","tags":["animal","marsupial","emoji koala","emoticon koala"]},{"name":"mdi:label-multiple","tags":[]},{"name":"mdi:label-multiple-outline","tags":[]},{"name":"mdi:label-percent","tags":[]},{"name":"mdi:label-percent-outline","tags":[]},{"name":"mdi:ladder","tags":["hardware / tools"]},{"name":"mdi:lambda","tags":["gaming / rpg","math"]},{"name":"mdi:lamp","tags":["home automation"]},{"name":"mdi:lamp-outline","tags":["home automation"]},{"name":"mdi:lamps","tags":["home automation","lights"]},{"name":"mdi:lamps-outline","tags":["home automation","lights outline"]},{"name":"mdi:lan","tags":["local area network"]},{"name":"mdi:lan-check","tags":[]},{"name":"mdi:lan-connect","tags":["local area network connect"]},{"name":"mdi:lan-disconnect","tags":["local area network disconnect"]},{"name":"mdi:lan-pending","tags":["local area network pending"]},{"name":"mdi:land-fields","tags":["agriculture"]},{"name":"mdi:land-plots","tags":["agriculture"]},{"name":"mdi:land-plots-circle","tags":["agriculture"]},{"name":"mdi:land-plots-circle-variant","tags":["agriculture"]},{"name":"mdi:land-plots-marker","tags":["agriculture"]},{"name":"mdi:land-rows-horizontal","tags":["agriculture"]},{"name":"mdi:land-rows-vertical","tags":["agriculture"]},{"name":"mdi:laptop-account","tags":["account / user","device / tech","teleconference","virtual meeting","video chat"]},{"name":"mdi:laptop-off","tags":["device / tech"]},{"name":"mdi:lasso","tags":[]},{"name":"mdi:latitude","tags":["navigation","geographic information system"]},{"name":"mdi:lava-lamp","tags":["home automation"]},{"name":"mdi:layers-edit","tags":["geographic information system","edit / modify"]},{"name":"mdi:layers-minus","tags":["geographic information system"]},{"name":"mdi:layers-plus","tags":["geographic information system"]},{"name":"mdi:layers-remove","tags":["geographic information system"]},{"name":"mdi:layers-search","tags":["geographic information system"]},{"name":"mdi:layers-search-outline","tags":["geographic information system"]},{"name":"mdi:layers-triple","tags":[]},{"name":"mdi:layers-triple-outline","tags":[]},{"name":"mdi:leaf","tags":["nature","food / drink","agriculture"]},{"name":"mdi:leaf-circle","tags":["nature","agriculture","green circle","organic"]},{"name":"mdi:leaf-circle-outline","tags":["agriculture","nature","green circle outline","organic outline"]},{"name":"mdi:leaf-maple","tags":["nature"]},{"name":"mdi:leaf-maple-off","tags":["nature"]},{"name":"mdi:leaf-off","tags":["nature","food / drink","agriculture"]},{"name":"mdi:lectern","tags":["podium","dais","rostrum","lecturn"]},{"name":"mdi:led-off","tags":["home automation"]},{"name":"mdi:led-on","tags":["home automation"]},{"name":"mdi:led-outline","tags":["home automation"]},{"name":"mdi:led-strip","tags":["home automation","light strip"]},{"name":"mdi:led-strip-variant","tags":["home automation","light strip variant"]},{"name":"mdi:led-strip-variant-off","tags":["home automation","light strip variant off"]},{"name":"mdi:led-variant-off","tags":["home automation"]},{"name":"mdi:led-variant-on","tags":["home automation"]},{"name":"mdi:led-variant-outline","tags":["home automation"]},{"name":"mdi:leek","tags":["food / drink"]},{"name":"mdi:less-than","tags":["math"]},{"name":"mdi:less-than-or-equal","tags":["math"]},{"name":"mdi:library-outline","tags":["places","local library outline"]},{"name":"mdi:lifebuoy","tags":["transportation + water","life preserver","support","help","overboard"]},{"name":"mdi:light-flood-down","tags":["home automation","floodlight down"]},{"name":"mdi:light-flood-up","tags":["home automation","floodlight up"]},{"name":"mdi:light-recessed","tags":["home automation","can light","pot light","high hat light","hi hat light","downlight"]},{"name":"mdi:light-switch","tags":["home automation","toggle switch","rocker switch"]},{"name":"mdi:light-switch-off","tags":["home automation","toggle switch off","rocker switch off"]},{"name":"mdi:lightbulb-alert","tags":["home automation","alert / error","lightbulb error"]},{"name":"mdi:lightbulb-alert-outline","tags":["home automation","alert / error","lightbulb error outline"]},{"name":"mdi:lightbulb-auto","tags":["home automation","lightbulb automatic","lightbulb motion"]},{"name":"mdi:lightbulb-auto-outline","tags":["home automation","lightbulb automatic outline","lightbulb motion outline"]},{"name":"mdi:lightbulb-cfl","tags":["home automation","bulb cfl"]},{"name":"mdi:lightbulb-cfl-off","tags":["home automation","bulb cfl off"]},{"name":"mdi:lightbulb-cfl-spiral","tags":["home automation","bulb cfl spiral"]},{"name":"mdi:lightbulb-cfl-spiral-off","tags":["home automation","bulb cfl spiral off"]},{"name":"mdi:lightbulb-fluorescent-tube","tags":["home automation"]},{"name":"mdi:lightbulb-fluorescent-tube-outline","tags":["home automation"]},{"name":"mdi:lightbulb-group","tags":["home automation","bulb group"]},{"name":"mdi:lightbulb-group-off","tags":["home automation","bulb group off"]},{"name":"mdi:lightbulb-group-off-outline","tags":["home automation","bulb group off outline"]},{"name":"mdi:lightbulb-group-outline","tags":["home automation","bulb group outline"]},{"name":"mdi:lightbulb-multiple","tags":["home automation","lightbulbs","bulb multiple","bulbs"]},{"name":"mdi:lightbulb-multiple-off","tags":["home automation","lightbulbs off","bulb multiple off","bulbs off"]},{"name":"mdi:lightbulb-multiple-off-outline","tags":["home automation","lightbulbs off outline","bulb multiple off outline","bulbs off outline"]},{"name":"mdi:lightbulb-multiple-outline","tags":["home automation","lightbulbs outline","bulb multiple outline","bulbs outline"]},{"name":"mdi:lightbulb-night","tags":["home automation","night light","nite light","lightbulb moon star"]},{"name":"mdi:lightbulb-night-outline","tags":["home automation","night light outline","nite light outline","lightbulb moon star outline"]},{"name":"mdi:lightbulb-off","tags":["home automation","bulb off"]},{"name":"mdi:lightbulb-off-outline","tags":["home automation","bulb off outline"]},{"name":"mdi:lightbulb-on","tags":["home automation","idea","bulb on","lightbulb dimmer 100"]},{"name":"mdi:lightbulb-on-10","tags":["home automation","lightbulb dimmer 10"]},{"name":"mdi:lightbulb-on-20","tags":["home automation","lightbulb dimmer 20"]},{"name":"mdi:lightbulb-on-30","tags":["home automation","lightbulb dimmer 30"]},{"name":"mdi:lightbulb-on-40","tags":["home automation","lightbulb dimmer 40"]},{"name":"mdi:lightbulb-on-50","tags":["home automation","lightbulb dimmer 50"]},{"name":"mdi:lightbulb-on-60","tags":["home automation","lightbulb dimmer 60"]},{"name":"mdi:lightbulb-on-70","tags":["home automation","lightbulb dimmer 70"]},{"name":"mdi:lightbulb-on-80","tags":["home automation","lightbulb dimmer 80"]},{"name":"mdi:lightbulb-on-90","tags":["home automation","lightbulb dimmer 90"]},{"name":"mdi:lightbulb-on-outline","tags":["home automation","idea","bulb on outline"]},{"name":"mdi:lightbulb-outline","tags":["home automation","idea","bulb outline"]},{"name":"mdi:lightbulb-question","tags":["home automation","lightbulb help"]},{"name":"mdi:lightbulb-question-outline","tags":["home automation","lightbulb help outline"]},{"name":"mdi:lightbulb-spot","tags":["home automation","lightbulb halogen","lightbulb gu10"]},{"name":"mdi:lightbulb-spot-off","tags":["home automation","lightbulb halogen off","lightbulb gu10 off"]},{"name":"mdi:lightbulb-variant","tags":["home automation","lightbulb edison","lightbulb filament"]},{"name":"mdi:lightbulb-variant-outline","tags":["home automation","lightbulb edison outline","lightbulb filament outline"]},{"name":"mdi:lighthouse","tags":["beacon"]},{"name":"mdi:lighthouse-on","tags":["beacon"]},{"name":"mdi:lightning-bolt","tags":["home automation","weather","thunder","storm","energy","electricity"]},{"name":"mdi:lightning-bolt-outline","tags":["home automation","weather","thunder outline","storm outline","energy outline","electricity outline"]},{"name":"mdi:line-scan","tags":[]},{"name":"mdi:lingerie","tags":["clothing","underwear","bra","panties"]},{"name":"mdi:link-box","tags":[]},{"name":"mdi:link-box-outline","tags":[]},{"name":"mdi:link-box-variant","tags":[]},{"name":"mdi:link-box-variant-outline","tags":[]},{"name":"mdi:link-lock","tags":["lock","block chain"]},{"name":"mdi:link-variant","tags":[]},{"name":"mdi:link-variant-minus","tags":[]},{"name":"mdi:link-variant-off","tags":[]},{"name":"mdi:link-variant-plus","tags":[]},{"name":"mdi:link-variant-remove","tags":[]},{"name":"mdi:lipstick","tags":["health / beauty"]},{"name":"mdi:liquid-spot","tags":["automotive","medical / hospital","ink spot","puddle","water","blood","spill","oil","dirty"]},{"name":"mdi:list-box","tags":["form"]},{"name":"mdi:list-box-outline","tags":["form outline"]},{"name":"mdi:loading","tags":[]},{"name":"mdi:location-enter","tags":["home automation","presence enter"]},{"name":"mdi:location-exit","tags":["home automation","presence exit"]},{"name":"mdi:lock-alert","tags":["lock","alert / error","home automation","lock warning","password alert","encryption alert","password warning","encryption warning"]},{"name":"mdi:lock-alert-outline","tags":["home automation","alert / error","lock","lock warning outline","password alert outline","encryption alert outline","password warning outline","encryption warning outline"]},{"name":"mdi:lock-check","tags":["lock","password check","password secure","encryption check","encryption secure","password verified","encryption verified"]},{"name":"mdi:lock-check-outline","tags":["lock","password check outline","password secure outline","encryption check outline","encryption secure outline","password verified outline","encryption verified outline"]},{"name":"mdi:lock-minus","tags":["lock","password minus","encryption minus"]},{"name":"mdi:lock-minus-outline","tags":["lock","password minus outline","encryption minus"]},{"name":"mdi:lock-off","tags":["lock","password off","not protected","unsecure","encryption off"]},{"name":"mdi:lock-off-outline","tags":["lock","password off outline","unsecure outline","not protected outline","encryption off outline"]},{"name":"mdi:lock-open-alert","tags":["alert / error","home automation","lock","unlocked alert","decrypted alert","lock open warning","unlocked warning","decrypted warning"]},{"name":"mdi:lock-open-alert-outline","tags":["home automation","alert / error","lock","unlocked alert outline","lock open warning outline","decrypted alert outline","unlocked warning outline","decrypted warning outline"]},{"name":"mdi:lock-open-check","tags":["lock","unlocked check","decrypted check"]},{"name":"mdi:lock-open-check-outline","tags":["lock","unlocked check outline","decrypted check outline"]},{"name":"mdi:lock-open-minus","tags":["lock","unlocked minus","decrypted minus"]},{"name":"mdi:lock-open-minus-outline","tags":["lock","unlocked minus outline","decrypted minus outline"]},{"name":"mdi:lock-open-plus","tags":["lock","unlocked plus","decrypted plus","lock open add","unlocked add","decrypted add"]},{"name":"mdi:lock-open-plus-outline","tags":["lock","unlocked plus outline","lock open add outline","unlocked add outline","decrypted plus outline","decrypted add outline"]},{"name":"mdi:lock-open-remove","tags":["lock","unlocked remove","decrypted remove"]},{"name":"mdi:lock-open-remove-outline","tags":["lock","unlocked remove outline","decrypted remove outline"]},{"name":"mdi:lock-open-variant","tags":["lock","home automation","unlocked variant","decrypted variant"]},{"name":"mdi:lock-open-variant-outline","tags":["lock","home automation","unlocked variant outline","decrypted variant outline"]},{"name":"mdi:lock-pattern","tags":[]},{"name":"mdi:lock-percent","tags":["lock rate"]},{"name":"mdi:lock-percent-open","tags":["lock rate open"]},{"name":"mdi:lock-percent-open-outline","tags":["lock rate open outline"]},{"name":"mdi:lock-percent-open-variant","tags":["lock rate open variant"]},{"name":"mdi:lock-percent-open-variant-outline","tags":["lock rate open variant outline"]},{"name":"mdi:lock-percent-outline","tags":["lock rate outline"]},{"name":"mdi:lock-plus","tags":["lock","enhanced encryption","lock add","encryption add","password add","password plus","encryption plus"]},{"name":"mdi:lock-plus-outline","tags":["lock","lock add outline","password plus outline","password add outline","encryption plus outline","encryption add outline"]},{"name":"mdi:lock-question","tags":["lock","forgot password","password question","encryption question"]},{"name":"mdi:lock-remove","tags":["lock","password remove","encryption remove"]},{"name":"mdi:lock-remove-outline","tags":["lock","password remove outline","encryption remove outline"]},{"name":"mdi:lock-smart","tags":["home automation"]},{"name":"mdi:locker","tags":[]},{"name":"mdi:locker-multiple","tags":["lockers"]},{"name":"mdi:login","tags":["log in","sign in"]},{"name":"mdi:login-variant","tags":["log in variant","sign in variant"]},{"name":"mdi:logout","tags":["log out","sign out"]},{"name":"mdi:logout-variant","tags":["log out variant","sign out variant"]},{"name":"mdi:longitude","tags":["navigation","geographic information system"]},{"name":"mdi:lotion","tags":["medical / hospital","health / beauty"]},{"name":"mdi:lotion-outline","tags":["medical / hospital","health / beauty"]},{"name":"mdi:lungs","tags":["medical / hospital"]},{"name":"mdi:mace","tags":["gaming / rpg"]},{"name":"mdi:magazine-pistol","tags":["ammunition pistol"]},{"name":"mdi:magazine-rifle","tags":["ammunition rifle"]},{"name":"mdi:magic-staff","tags":["gaming / rpg","staff shimmer","magic wand"]},{"name":"mdi:magnet","tags":[]},{"name":"mdi:magnet-on","tags":[]},{"name":"mdi:magnify-close","tags":[]},{"name":"mdi:magnify-expand","tags":["geographic information system","search expand"]},{"name":"mdi:magnify-minus","tags":["zoom out","search minus"]},{"name":"mdi:magnify-minus-cursor","tags":["zoom out cursor"]},{"name":"mdi:magnify-plus","tags":["zoom in","magnify add","search plus","search add"]},{"name":"mdi:magnify-plus-cursor","tags":["zoom in cursor","magnify add cursor"]},{"name":"mdi:magnify-remove-cursor","tags":[]},{"name":"mdi:magnify-remove-outline","tags":["geographic information system"]},{"name":"mdi:magnify-scan","tags":[]},{"name":"mdi:mail","tags":[]},{"name":"mdi:mailbox","tags":[]},{"name":"mdi:mailbox-open","tags":[]},{"name":"mdi:mailbox-open-outline","tags":[]},{"name":"mdi:mailbox-open-up","tags":[]},{"name":"mdi:mailbox-open-up-outline","tags":[]},{"name":"mdi:mailbox-outline","tags":[]},{"name":"mdi:mailbox-up","tags":[]},{"name":"mdi:mailbox-up-outline","tags":[]},{"name":"mdi:map-check","tags":["navigation","geographic information system","map tick"]},{"name":"mdi:map-check-outline","tags":["navigation","geographic information system","map tick outline"]},{"name":"mdi:map-clock","tags":["navigation","geographic information system","date / time","timezone"]},{"name":"mdi:map-clock-outline","tags":["navigation","geographic information system","date / time","timezone outline"]},{"name":"mdi:map-legend","tags":["navigation","geographic information system"]},{"name":"mdi:map-marker-alert","tags":["navigation","alert / error","geographic information system","location alert","location warning"]},{"name":"mdi:map-marker-alert-outline","tags":["navigation","alert / error","geographic information system","location alert outline","location warning outline"]},{"name":"mdi:map-marker-check-outline","tags":["navigation","geographic information system","location check outline","where to vote outline"]},{"name":"mdi:map-marker-distance","tags":["navigation","geographic information system","location distance"]},{"name":"mdi:map-marker-down","tags":["navigation","geographic information system","location down"]},{"name":"mdi:map-marker-left","tags":["navigation","geographic information system","location left"]},{"name":"mdi:map-marker-left-outline","tags":["navigation","geographic information system","location left outline"]},{"name":"mdi:map-marker-minus","tags":["navigation","geographic information system","location minus"]},{"name":"mdi:map-marker-minus-outline","tags":["geographic information system","navigation","location minus outline"]},{"name":"mdi:map-marker-multiple","tags":["navigation","geographic information system","map markers","location multiple","locations"]},{"name":"mdi:map-marker-multiple-outline","tags":["navigation","geographic information system","locations outline","location multiple outline","map markers outline"]},{"name":"mdi:map-marker-off","tags":["navigation","geographic information system","location off"]},{"name":"mdi:map-marker-off-outline","tags":["navigation","geographic information system","location off outline"]},{"name":"mdi:map-marker-path","tags":["navigation","geographic information system","location path"]},{"name":"mdi:map-marker-plus","tags":["navigation","geographic information system","location plus","map marker add","location add"]},{"name":"mdi:map-marker-plus-outline","tags":["geographic information system","navigation","map marker add outline","location plus outline","location add outline"]},{"name":"mdi:map-marker-radius","tags":["navigation","geographic information system","home automation","location radius"]},{"name":"mdi:map-marker-radius-outline","tags":["navigation","geographic information system","home automation","location radius outline"]},{"name":"mdi:map-marker-remove","tags":["navigation","geographic information system","location remove"]},{"name":"mdi:map-marker-remove-outline","tags":["geographic information system","navigation","location remove outline"]},{"name":"mdi:map-marker-remove-variant","tags":["navigation","geographic information system","location remove variant outline"]},{"name":"mdi:map-marker-right","tags":["navigation","geographic information system","location right"]},{"name":"mdi:map-marker-right-outline","tags":["navigation","geographic information system","location right outline"]},{"name":"mdi:map-marker-star","tags":["navigation","map marker favorite","location star","location favorite"]},{"name":"mdi:map-marker-star-outline","tags":["navigation","map marker favorite outline","location star outline","location favorite outline"]},{"name":"mdi:map-marker-up","tags":["navigation","geographic information system","location up"]},{"name":"mdi:map-minus","tags":["navigation","geographic information system"]},{"name":"mdi:map-plus","tags":["navigation","geographic information system","map add"]},{"name":"mdi:map-search","tags":["navigation","geographic information system"]},{"name":"mdi:map-search-outline","tags":["navigation","geographic information system"]},{"name":"mdi:margin","tags":[]},{"name":"mdi:marker-cancel","tags":["text / content / format"]},{"name":"mdi:math-compass","tags":["math","drawing / art","navigation","maths compass"]},{"name":"mdi:math-cos","tags":["math","math cosine","maths cos"]},{"name":"mdi:math-integral","tags":["math"]},{"name":"mdi:math-integral-box","tags":["math"]},{"name":"mdi:math-log","tags":["math"]},{"name":"mdi:math-norm","tags":["math","developer / languages","code or","parallel"]},{"name":"mdi:math-norm-box","tags":["math","developer / languages","code or box","parallel box"]},{"name":"mdi:math-sin","tags":["math","math sine","maths sin"]},{"name":"mdi:math-tan","tags":["math","math tangent","maths tan"]},{"name":"mdi:matrix","tags":[]},{"name":"mdi:medal","tags":["gaming / rpg","sport","award"]},{"name":"mdi:medal-outline","tags":["sport"]},{"name":"mdi:medical-bag","tags":["medical / hospital","first aid kit","medicine"]},{"name":"mdi:menorah","tags":["religion","holiday","candelabrum","candelabra","candle"]},{"name":"mdi:menorah-fire","tags":["religion","holiday","menorah flame","candle flame","candelabra flame","candelabra fire","candle fire","candelabrum fire","candelabrum flame"]},{"name":"mdi:menu-down-outline","tags":["arrow","caret down outline"]},{"name":"mdi:menu-left","tags":["arrow","arrow left"]},{"name":"mdi:menu-left-outline","tags":[]},{"name":"mdi:menu-right","tags":["arrow","arrow right"]},{"name":"mdi:menu-right-outline","tags":[]},{"name":"mdi:menu-swap","tags":["arrow"]},{"name":"mdi:menu-swap-outline","tags":["arrow"]},{"name":"mdi:menu-up-outline","tags":["arrow","caret up outline"]},{"name":"mdi:message-alert-outline","tags":["alert / error","announcement outline","feedback outline","message warning outline","sms failed outline"]},{"name":"mdi:message-arrow-left","tags":[]},{"name":"mdi:message-arrow-left-outline","tags":[]},{"name":"mdi:message-arrow-right","tags":[]},{"name":"mdi:message-arrow-right-outline","tags":[]},{"name":"mdi:message-check","tags":[]},{"name":"mdi:message-check-outline","tags":[]},{"name":"mdi:message-cog","tags":["settings"]},{"name":"mdi:message-cog-outline","tags":["settings"]},{"name":"mdi:message-fast","tags":[]},{"name":"mdi:message-fast-outline","tags":[]},{"name":"mdi:message-image-outline","tags":[]},{"name":"mdi:message-lock","tags":["lock","message secure"]},{"name":"mdi:message-lock-outline","tags":["lock"]},{"name":"mdi:message-minus","tags":[]},{"name":"mdi:message-minus-outline","tags":[]},{"name":"mdi:message-off","tags":[]},{"name":"mdi:message-off-outline","tags":[]},{"name":"mdi:message-plus","tags":["message add"]},{"name":"mdi:message-plus-outline","tags":[]},{"name":"mdi:message-processing-outline","tags":[]},{"name":"mdi:message-question","tags":[]},{"name":"mdi:message-question-outline","tags":[]},{"name":"mdi:message-reply-outline","tags":[]},{"name":"mdi:message-reply-text-outline","tags":[]},{"name":"mdi:message-settings","tags":["settings"]},{"name":"mdi:message-settings-outline","tags":["settings"]},{"name":"mdi:message-star","tags":[]},{"name":"mdi:message-star-outline","tags":[]},{"name":"mdi:message-text-clock","tags":["date / time"]},{"name":"mdi:message-text-clock-outline","tags":["date / time"]},{"name":"mdi:message-text-fast","tags":[]},{"name":"mdi:message-text-fast-outline","tags":[]},{"name":"mdi:message-text-lock","tags":["lock","message text secure"]},{"name":"mdi:message-text-lock-outline","tags":["lock"]},{"name":"mdi:message-text-outline","tags":[]},{"name":"mdi:metronome","tags":["music","tempo","bpm","beats per minute"]},{"name":"mdi:metronome-tick","tags":["music","tempo tick","bpm tick","beats per minute tick"]},{"name":"mdi:micro-sd","tags":[]},{"name":"mdi:microphone-message","tags":["tts","text to speech"]},{"name":"mdi:microphone-message-off","tags":["tts off","text to speech off"]},{"name":"mdi:microphone-minus","tags":["microphone remove"]},{"name":"mdi:microphone-plus","tags":["microphone add"]},{"name":"mdi:microphone-question","tags":["audio","music","microphone help"]},{"name":"mdi:microphone-question-outline","tags":["audio","music","microphone help outline"]},{"name":"mdi:microphone-variant","tags":["music"]},{"name":"mdi:microphone-variant-off","tags":["music"]},{"name":"mdi:microscope","tags":["science"]},{"name":"mdi:microsoft-xbox-controller-battery-unknown","tags":["battery","gaming / rpg","microsoft xbox gamepad battery unknown"]},{"name":"mdi:microwave","tags":["home automation","food / drink","microwave oven"]},{"name":"mdi:microwave-off","tags":["home automation"]},{"name":"mdi:middleware","tags":["arrow"]},{"name":"mdi:middleware-outline","tags":["arrow"]},{"name":"mdi:midi-port","tags":["music"]},{"name":"mdi:mine","tags":[]},{"name":"mdi:mini-sd","tags":[]},{"name":"mdi:minidisc","tags":[]},{"name":"mdi:minus-box-multiple","tags":["form","library minus"]},{"name":"mdi:minus-box-multiple-outline","tags":["form","library minus outline"]},{"name":"mdi:minus-circle-multiple","tags":["form","coins minus"]},{"name":"mdi:minus-circle-multiple-outline","tags":["form","coins minus outline"]},{"name":"mdi:minus-circle-off","tags":["do not disturb off","remove circle off","do not enter off"]},{"name":"mdi:minus-circle-off-outline","tags":["do not disturb off outline","remove circle off outline","do not enter off outline"]},{"name":"mdi:minus-network","tags":[]},{"name":"mdi:minus-network-outline","tags":[]},{"name":"mdi:minus-thick","tags":[]},{"name":"mdi:mirror","tags":["home automation"]},{"name":"mdi:mirror-rectangle","tags":["home automation"]},{"name":"mdi:mirror-variant","tags":["home automation"]},{"name":"mdi:mixed-reality","tags":[]},{"name":"mdi:molecule","tags":["science"]},{"name":"mdi:molecule-co","tags":["home automation","science","carbon monoxide","gas co"]},{"name":"mdi:molecule-co2","tags":["science","home automation","periodic table carbon dioxide","gas co2"]},{"name":"mdi:monitor-account","tags":["account / user","device / tech","teleconference","virtual meeting","video chat"]},{"name":"mdi:monitor-arrow-down","tags":["device / tech","monitor download"]},{"name":"mdi:monitor-arrow-down-variant","tags":["device / tech","monitor download"]},{"name":"mdi:monitor-dashboard","tags":["device / tech"]},{"name":"mdi:monitor-edit","tags":["edit / modify"]},{"name":"mdi:monitor-eye","tags":[]},{"name":"mdi:monitor-lock","tags":["device / tech","lock"]},{"name":"mdi:monitor-multiple","tags":["device / tech","monitors"]},{"name":"mdi:monitor-shimmer","tags":["device / tech","monitor clean"]},{"name":"mdi:monitor-small","tags":["device / tech","monitor crt"]},{"name":"mdi:monitor-speaker","tags":["device / tech"]},{"name":"mdi:monitor-speaker-off","tags":["device / tech"]},{"name":"mdi:monitor-star","tags":["device / tech","monitor favorite"]},{"name":"mdi:monitor-vertical","tags":[]},{"name":"mdi:moon-first-quarter","tags":["weather"]},{"name":"mdi:moon-full","tags":["weather"]},{"name":"mdi:moon-last-quarter","tags":["weather"]},{"name":"mdi:moon-new","tags":["weather"]},{"name":"mdi:moon-waning-crescent","tags":["weather"]},{"name":"mdi:moon-waning-gibbous","tags":["weather"]},{"name":"mdi:moon-waxing-crescent","tags":["weather"]},{"name":"mdi:moon-waxing-gibbous","tags":["weather"]},{"name":"mdi:mortar-pestle","tags":[]},{"name":"mdi:mother-heart","tags":["people / family"]},{"name":"mdi:mother-nurse","tags":["medical / hospital","people / family","breast feed"]},{"name":"mdi:motion-sensor","tags":["home automation","motion detector"]},{"name":"mdi:motion-sensor-off","tags":["home automation"]},{"name":"mdi:motorbike-electric","tags":["transportation + road","motorcycle electric"]},{"name":"mdi:motorbike-off","tags":["transportation + road","motorcycle off"]},{"name":"mdi:mouse-bluetooth","tags":[]},{"name":"mdi:mouse-move-down","tags":[]},{"name":"mdi:mouse-move-up","tags":[]},{"name":"mdi:mouse-move-vertical","tags":[]},{"name":"mdi:mouse-off","tags":[]},{"name":"mdi:mouse-variant","tags":[]},{"name":"mdi:mouse-variant-off","tags":[]},{"name":"mdi:move-resize","tags":[]},{"name":"mdi:move-resize-variant","tags":[]},{"name":"mdi:movie-check","tags":["video / movie","slate check","clapperboard check","film check"]},{"name":"mdi:movie-check-outline","tags":["video / movie","slate check outline","clapperboard check outline","film check outline"]},{"name":"mdi:movie-cog","tags":["video / movie","settings","slate cog","clapperboard cog","film cog"]},{"name":"mdi:movie-cog-outline","tags":["video / movie","settings","slate cog outline","clapperboard cog outline","film cog outline"]},{"name":"mdi:movie-edit","tags":["video / movie","edit / modify","slate edit","clapperboard edit","film edit"]},{"name":"mdi:movie-edit-outline","tags":["video / movie","edit / modify","slate edit outline","clapperboard edit outline","film edit outline"]},{"name":"mdi:movie-minus","tags":["video / movie","slate minus","clapperboard minus","film minus"]},{"name":"mdi:movie-minus-outline","tags":["video / movie","slate minus outline","clapperboard minus outline","film minus outline"]},{"name":"mdi:movie-off","tags":["video / movie","slate off","clapperboard off","film off"]},{"name":"mdi:movie-off-outline","tags":["video / movie","slate off outline","clapperboard off outline","film off outline"]},{"name":"mdi:movie-open","tags":["video / movie","slate open","clapperboard open","film open","movie creation"]},{"name":"mdi:movie-open-check","tags":["video / movie","slate open check","clapperboard open check","film open check"]},{"name":"mdi:movie-open-check-outline","tags":["video / movie","slate open check outline","clapperboard open check outline","film open check outline"]},{"name":"mdi:movie-open-cog","tags":["video / movie","settings","slate open cog","clapperboard open cog","film open cog"]},{"name":"mdi:movie-open-cog-outline","tags":["video / movie","settings","slate open cog outline","clapperboard open cog outline","film open cog outline"]},{"name":"mdi:movie-open-edit","tags":["video / movie","edit / modify","slate open edit","clapperboard open edit","film open edit"]},{"name":"mdi:movie-open-edit-outline","tags":["video / movie","edit / modify","slate open edit outline","clapperboard open edit outline","film open edit outline"]},{"name":"mdi:movie-open-minus","tags":["video / movie","slate open minus","clapperboard open minus","film open minus"]},{"name":"mdi:movie-open-minus-outline","tags":["video / movie","slate open minus outline","clapperboard open minus outline","film open minus outline"]},{"name":"mdi:movie-open-off","tags":["video / movie","slate open off","clapperboard open off","film open off"]},{"name":"mdi:movie-open-off-outline","tags":["video / movie","slate open off outline","clapperboard open off outline","film open off outline"]},{"name":"mdi:movie-open-outline","tags":["video / movie","slate open outline","clapperboard open outline","film open outline","movie creation"]},{"name":"mdi:movie-open-play","tags":["video / movie","slate open play","clapperboard open play","film open play"]},{"name":"mdi:movie-open-play-outline","tags":["video / movie","slate open play outline","clapperboard open play outline","film open play outline"]},{"name":"mdi:movie-open-plus","tags":["video / movie","clapperboard open plus","slate open plus","flim open plus"]},{"name":"mdi:movie-open-plus-outline","tags":["video / movie","slate open plus outline","clapperboard open plus outline","film open plus outline"]},{"name":"mdi:movie-open-remove","tags":["video / movie","slate open remove","clapperboard open remove","film open remove"]},{"name":"mdi:movie-open-remove-outline","tags":["video / movie","slate open remove outline","clapperboard open remove outline","film open remove outline"]},{"name":"mdi:movie-open-settings","tags":["video / movie","settings","slate open settings","clapperboard open settings","film open settings"]},{"name":"mdi:movie-open-settings-outline","tags":["video / movie","settings","slate open settings outline","clapperboard open settings outline","film open settings outline"]},{"name":"mdi:movie-open-star","tags":["video / movie","slate open star","clapperboard open star","film open star","movie open favorite"]},{"name":"mdi:movie-open-star-outline","tags":["video / movie","slate open star outline","clapperboard open star outline","film open star outline","movie open favorite outline"]},{"name":"mdi:movie-play","tags":["video / movie","slate play","clapperboard play","film play"]},{"name":"mdi:movie-play-outline","tags":["video / movie","slate play outline","clapperboard play outline","film play outline"]},{"name":"mdi:movie-plus","tags":["video / movie","slate plus","clapperboard plus","film plus"]},{"name":"mdi:movie-plus-outline","tags":["video / movie","slate plus outline","clapperboard plus outline","film plus outline"]},{"name":"mdi:movie-remove","tags":["video / movie","slate remove","clapperboard remove","film remove"]},{"name":"mdi:movie-remove-outline","tags":["video / movie","slate remove outline","clapperboard remove outline","film remove outline"]},{"name":"mdi:movie-roll","tags":["video / movie","film reel"]},{"name":"mdi:movie-search","tags":["video / movie"]},{"name":"mdi:movie-search-outline","tags":["video / movie"]},{"name":"mdi:movie-settings","tags":["video / movie","settings","slate settings","clapperboard settings","film settings"]},{"name":"mdi:movie-settings-outline","tags":["video / movie","settings","slate settings outline","clapperboard settings outline","film settings outline"]},{"name":"mdi:movie-star","tags":["video / movie","slate star","clapperboard star","film star","movie favorite"]},{"name":"mdi:movie-star-outline","tags":["video / movie","slate star outline","clapperboard star outline","film star outline","movie favorite outline"]},{"name":"mdi:mower","tags":["hardware / tools","home automation"]},{"name":"mdi:mower-bag","tags":["hardware / tools","home automation"]},{"name":"mdi:mower-bag-on","tags":["hardware / tools","home automation"]},{"name":"mdi:mower-on","tags":["hardware / tools","home automation"]},{"name":"mdi:muffin","tags":["food / drink"]},{"name":"mdi:multicast","tags":["multiplex","broadcast"]},{"name":"mdi:multimedia","tags":["audio","video / movie","photography","audio","video","image","music","movie","picture"]},{"name":"mdi:multiplication","tags":["math"]},{"name":"mdi:multiplication-box","tags":["math"]},{"name":"mdi:mushroom","tags":["nature","food / drink","agriculture","fungus"]},{"name":"mdi:mushroom-off","tags":["food / drink","nature","agriculture"]},{"name":"mdi:mushroom-off-outline","tags":["food / drink","nature","agriculture"]},{"name":"mdi:mushroom-outline","tags":["nature","food / drink","agriculture","fungus outline"]},{"name":"mdi:music","tags":["audio","music"]},{"name":"mdi:music-accidental-double-flat","tags":["music"]},{"name":"mdi:music-accidental-double-sharp","tags":["music"]},{"name":"mdi:music-accidental-flat","tags":["music"]},{"name":"mdi:music-accidental-natural","tags":["music"]},{"name":"mdi:music-accidental-sharp","tags":["music"]},{"name":"mdi:music-box","tags":["audio","music"]},{"name":"mdi:music-box-multiple-outline","tags":["music","library music outline"]},{"name":"mdi:music-box-outline","tags":["audio","music"]},{"name":"mdi:music-circle","tags":["audio","music","note circle"]},{"name":"mdi:music-circle-outline","tags":["music","audio","note circle outline"]},{"name":"mdi:music-clef-alto","tags":["music","music c clef","music clef tenor","music clef soprano","music clef baritone"]},{"name":"mdi:music-clef-bass","tags":["music","music f clef"]},{"name":"mdi:music-clef-treble","tags":["music","music g clef"]},{"name":"mdi:music-note","tags":["audio","music"]},{"name":"mdi:music-note-bluetooth","tags":["audio","music"]},{"name":"mdi:music-note-bluetooth-off","tags":["audio","music"]},{"name":"mdi:music-note-eighth","tags":["audio","music"]},{"name":"mdi:music-note-eighth-dotted","tags":["music"]},{"name":"mdi:music-note-half","tags":["audio","music"]},{"name":"mdi:music-note-half-dotted","tags":["music"]},{"name":"mdi:music-note-minus","tags":[]},{"name":"mdi:music-note-off","tags":["audio","music"]},{"name":"mdi:music-note-off-outline","tags":["music"]},{"name":"mdi:music-note-outline","tags":["music"]},{"name":"mdi:music-note-plus","tags":["audio","music","music note add"]},{"name":"mdi:music-note-quarter","tags":["audio","music"]},{"name":"mdi:music-note-quarter-dotted","tags":["music"]},{"name":"mdi:music-note-sixteenth","tags":["audio","music"]},{"name":"mdi:music-note-sixteenth-dotted","tags":["music"]},{"name":"mdi:music-note-whole","tags":["audio","music"]},{"name":"mdi:music-note-whole-dotted","tags":["music"]},{"name":"mdi:music-off","tags":["audio","music"]},{"name":"mdi:music-rest-eighth","tags":["music"]},{"name":"mdi:music-rest-half","tags":["music"]},{"name":"mdi:music-rest-quarter","tags":["music"]},{"name":"mdi:music-rest-sixteenth","tags":["music"]},{"name":"mdi:music-rest-whole","tags":["music"]},{"name":"mdi:nail","tags":["hardware / tools"]},{"name":"mdi:nas","tags":["network attached storage"]},{"name":"mdi:nature-outline","tags":["nature"]},{"name":"mdi:nature-people-outline","tags":["account / user","nature"]},{"name":"mdi:necklace","tags":["clothing"]},{"name":"mdi:needle","tags":["medical / hospital","syringe","injection","medicine","shot","drug","immunization","pharmaceutical"]},{"name":"mdi:needle-off","tags":["medical / hospital","syringe off","injection off","medicine off","shot off","drug off","immunization off","pharmaceutical off"]},{"name":"mdi:network","tags":[]},{"name":"mdi:network-off","tags":[]},{"name":"mdi:network-off-outline","tags":[]},{"name":"mdi:network-outline","tags":[]},{"name":"mdi:network-pos","tags":["banking","network point of sale","network cash box"]},{"name":"mdi:network-strength-1","tags":["cellphone / phone"]},{"name":"mdi:network-strength-1-alert","tags":["cellphone / phone","alert / error","network strength 1 warning"]},{"name":"mdi:network-strength-2","tags":["cellphone / phone"]},{"name":"mdi:network-strength-2-alert","tags":["cellphone / phone","alert / error","network strength 2 warning"]},{"name":"mdi:network-strength-3","tags":["cellphone / phone"]},{"name":"mdi:network-strength-3-alert","tags":["cellphone / phone","alert / error","network strength 3 warning"]},{"name":"mdi:network-strength-4","tags":["cellphone / phone"]},{"name":"mdi:network-strength-4-alert","tags":["cellphone / phone","alert / error","network strength 4 warning"]},{"name":"mdi:network-strength-4-cog","tags":["settings","network strength 4 settings","data settings"]},{"name":"mdi:network-strength-off","tags":["cellphone / phone"]},{"name":"mdi:network-strength-off-outline","tags":["cellphone / phone"]},{"name":"mdi:network-strength-outline","tags":["cellphone / phone","network strength 0"]},{"name":"mdi:newspaper-check","tags":[]},{"name":"mdi:newspaper-minus","tags":[]},{"name":"mdi:newspaper-plus","tags":[]},{"name":"mdi:newspaper-remove","tags":[]},{"name":"mdi:newspaper-variant-multiple","tags":[]},{"name":"mdi:newspaper-variant-multiple-outline","tags":[]},{"name":"mdi:nfc-search-variant","tags":[]},{"name":"mdi:nfc-tap","tags":["near field communication tap"]},{"name":"mdi:nfc-variant-off","tags":["home automation","near field communication off"]},{"name":"mdi:ninja","tags":[]},{"name":"mdi:nintendo-game-boy","tags":["gaming / rpg"]},{"name":"mdi:not-equal","tags":[]},{"name":"mdi:not-equal-variant","tags":["math"]},{"name":"mdi:note","tags":["paper","sticky note","post it note"]},{"name":"mdi:note-alert","tags":["alert / error","paper alert","sticky note alert","post it note alert"]},{"name":"mdi:note-alert-outline","tags":["alert / error","paper alert outline","post it note alert outline","sticky note alert outline"]},{"name":"mdi:note-check","tags":["paper check","sticky note check","post it note check"]},{"name":"mdi:note-check-outline","tags":["paper check outline","sticky note check outline","post it note check outline"]},{"name":"mdi:note-edit","tags":["edit / modify","paper edit","sticky note edit","post it note edit"]},{"name":"mdi:note-edit-outline","tags":["edit / modify","paper edit outline","sticky note edit outline","post it note edit outline"]},{"name":"mdi:note-minus","tags":["paper minus","sticky note minus","post it note minus"]},{"name":"mdi:note-minus-outline","tags":["paper minus outline","sticky note minus outline","post it note minus outline"]},{"name":"mdi:note-multiple","tags":["notes","papers","sticky notes","post it notes"]},{"name":"mdi:note-multiple-outline","tags":["notes outline","papers outline","sticky notes outline","post it notes outline"]},{"name":"mdi:note-off","tags":["paper off","sticky note off","post it note off"]},{"name":"mdi:note-off-outline","tags":["paper off outline","sticky note off outline","post it note off outline"]},{"name":"mdi:note-outline","tags":["paper outline","sticky note outline","post it note outline"]},{"name":"mdi:note-plus","tags":["note add","paper plus","paper add","sticky note plus","sticky note add","post it note plus","post it note add"]},{"name":"mdi:note-plus-outline","tags":["note add outline","paper plus outline","paper add outline","sticky note plus outline","sticky note add outline","post it note plus outline","post it note add outline"]},{"name":"mdi:note-remove","tags":["paper remove","sticky note remove","post it note remove"]},{"name":"mdi:note-remove-outline","tags":[]},{"name":"mdi:note-search","tags":["paper search","sticky note search","post it note search"]},{"name":"mdi:note-search-outline","tags":["paper search outline","sticky note search outline","post it note search outline"]},{"name":"mdi:note-text","tags":["paper text","sticky note text","post it note text"]},{"name":"mdi:note-text-outline","tags":["paper text outline","sticky note text outline","post it note text outline"]},{"name":"mdi:notebook","tags":["journal","planner","diary"]},{"name":"mdi:notebook-check","tags":[]},{"name":"mdi:notebook-check-outline","tags":[]},{"name":"mdi:notebook-edit","tags":["edit / modify"]},{"name":"mdi:notebook-edit-outline","tags":["edit / modify"]},{"name":"mdi:notebook-heart","tags":["notebook favorite","notebook love"]},{"name":"mdi:notebook-heart-outline","tags":["notebook favorite outline","notebook love outline"]},{"name":"mdi:notebook-minus","tags":[]},{"name":"mdi:notebook-minus-outline","tags":[]},{"name":"mdi:notebook-multiple","tags":["journal multiple","planner multiple"]},{"name":"mdi:notebook-outline","tags":["journal outline","planner outline"]},{"name":"mdi:notebook-plus","tags":[]},{"name":"mdi:notebook-plus-outline","tags":[]},{"name":"mdi:notebook-remove","tags":[]},{"name":"mdi:notebook-remove-outline","tags":[]},{"name":"mdi:nuke","tags":["nuclear","atomic bomb"]},{"name":"mdi:null","tags":[]},{"name":"mdi:numeric","tags":["alpha / numeric","numbers","1 2 3","one two three","123"]},{"name":"mdi:numeric-0","tags":["alpha / numeric","number 0","numeric zero"]},{"name":"mdi:numeric-0-box-multiple","tags":["alpha / numeric"]},{"name":"mdi:numeric-0-circle","tags":["alpha / numeric","numeric zero circle","number 0 circle","number zero circle"]},{"name":"mdi:numeric-0-circle-outline","tags":["alpha / numeric","numeric zero circle outline","number 0 circle outline","number zero circle outline"]},{"name":"mdi:numeric-1","tags":["alpha / numeric","number 1","numeric one"]},{"name":"mdi:numeric-1-box-multiple","tags":["alpha / numeric"]},{"name":"mdi:numeric-1-circle","tags":["alpha / numeric","numeric one circle","number 1 circle","number one circle"]},{"name":"mdi:numeric-1-circle-outline","tags":["alpha / numeric","numeric one circle outline","number 1 circle outline","number one circle outline"]},{"name":"mdi:numeric-10","tags":["alpha / numeric"]},{"name":"mdi:numeric-10-box","tags":["alpha / numeric"]},{"name":"mdi:numeric-10-box-multiple","tags":["alpha / numeric"]},{"name":"mdi:numeric-10-box-multiple-outline","tags":["alpha / numeric"]},{"name":"mdi:numeric-10-box-outline","tags":["alpha / numeric"]},{"name":"mdi:numeric-10-circle","tags":["alpha / numeric"]},{"name":"mdi:numeric-10-circle-outline","tags":["alpha / numeric"]},{"name":"mdi:numeric-2","tags":["alpha / numeric","number 2","numeric two"]},{"name":"mdi:numeric-2-box-multiple","tags":["alpha / numeric"]},{"name":"mdi:numeric-2-circle","tags":["alpha / numeric","numeric two circle","number 2 circle","number two circle"]},{"name":"mdi:numeric-2-circle-outline","tags":["alpha / numeric","numeric two circle outline","number 2 circle outline","number two circle outline"]},{"name":"mdi:numeric-3","tags":["alpha / numeric","number 3","numeric three"]},{"name":"mdi:numeric-3-box-multiple","tags":["alpha / numeric"]},{"name":"mdi:numeric-3-circle","tags":["alpha / numeric","numeric three circle","number 3 circle","number three circle"]},{"name":"mdi:numeric-3-circle-outline","tags":["alpha / numeric","numeric three circle outline","number 3 circle outline","number three circle outline"]},{"name":"mdi:numeric-4","tags":["alpha / numeric","number 4","numeric four"]},{"name":"mdi:numeric-4-box-multiple","tags":["alpha / numeric"]},{"name":"mdi:numeric-4-circle","tags":["alpha / numeric","numeric four circle","number 4 circle","number four circle"]},{"name":"mdi:numeric-4-circle-outline","tags":["alpha / numeric","numeric four circle outline","number 4 circle outline","number four circle outline"]},{"name":"mdi:numeric-5","tags":["alpha / numeric","number 5","numeric five"]},{"name":"mdi:numeric-5-box-multiple","tags":["alpha / numeric"]},{"name":"mdi:numeric-5-circle","tags":["alpha / numeric","numeric five circle","number 5 circle","number five circle"]},{"name":"mdi:numeric-5-circle-outline","tags":["alpha / numeric","numeric five circle outline","number 5 circle outline","number five circle outline"]},{"name":"mdi:numeric-6","tags":["alpha / numeric","number 6","numeric six"]},{"name":"mdi:numeric-6-box-multiple","tags":["alpha / numeric"]},{"name":"mdi:numeric-6-circle","tags":["alpha / numeric","numeric six circle","number 6 circle","number six circle"]},{"name":"mdi:numeric-6-circle-outline","tags":["alpha / numeric","numeric six circle outline","number 6 circle outline","number six circle outline"]},{"name":"mdi:numeric-7","tags":["alpha / numeric","number 7","numeric seven"]},{"name":"mdi:numeric-7-box-multiple","tags":["alpha / numeric"]},{"name":"mdi:numeric-7-circle","tags":["alpha / numeric","numeric seven circle","number 7 circle","number seven circle"]},{"name":"mdi:numeric-7-circle-outline","tags":["alpha / numeric","numeric seven circle outline","number 7 circle outline","number seven circle outline"]},{"name":"mdi:numeric-8","tags":["alpha / numeric","number 8","numeric eight"]},{"name":"mdi:numeric-8-box-multiple","tags":["alpha / numeric"]},{"name":"mdi:numeric-8-circle","tags":["alpha / numeric","numeric eight circle","number 8 circle","number eight circle"]},{"name":"mdi:numeric-8-circle-outline","tags":["alpha / numeric","numeric eight circle outline","number 8 circle outline","number eight circle outline"]},{"name":"mdi:numeric-9","tags":["alpha / numeric","number 9","numeric nine"]},{"name":"mdi:numeric-9-box-multiple","tags":["alpha / numeric"]},{"name":"mdi:numeric-9-circle","tags":["alpha / numeric","numeric nine circle","number 9 circle","number nine circle"]},{"name":"mdi:numeric-9-circle-outline","tags":["alpha / numeric","numeric nine circle outline","number 9 circle outline","number nine circle outline"]},{"name":"mdi:numeric-9-plus","tags":["alpha / numeric"]},{"name":"mdi:numeric-9-plus-box-multiple","tags":["alpha / numeric"]},{"name":"mdi:numeric-9-plus-circle","tags":["alpha / numeric","numeric nine plus circle","number 9 plus circle","number nine plus circle"]},{"name":"mdi:numeric-9-plus-circle-outline","tags":["alpha / numeric","numeric nine plus circle outline","number 9 plus circle outline","number nine plus circle outline"]},{"name":"mdi:numeric-negative-1","tags":["alpha / numeric","decrement","minus one"]},{"name":"mdi:numeric-off","tags":["alpha / numeric","numbers off","123 off","one two three off"]},{"name":"mdi:numeric-positive-1","tags":["alpha / numeric","increment","plus one"]},{"name":"mdi:nut","tags":["hardware / tools"]},{"name":"mdi:nutrition","tags":["food / drink"]},{"name":"mdi:oar","tags":[]},{"name":"mdi:ocarina","tags":["music","gaming / rpg"]},{"name":"mdi:ocr","tags":["optical character recognition"]},{"name":"mdi:octagon","tags":["shape","transportation + road","stop"]},{"name":"mdi:octagon-outline","tags":["shape","transportation + road","stop outline"]},{"name":"mdi:octagram","tags":["shape","starburst"]},{"name":"mdi:octagram-edit","tags":["shape","starburst edit"]},{"name":"mdi:octagram-edit-outline","tags":["shape","starburst edit outline"]},{"name":"mdi:octagram-minus","tags":["shape","starburst plus"]},{"name":"mdi:octagram-minus-outline","tags":["shape","starburst minus outline"]},{"name":"mdi:octagram-outline","tags":["shape","starburst outline"]},{"name":"mdi:octagram-plus","tags":["shape","starburst plus"]},{"name":"mdi:octagram-plus-outline","tags":[]},{"name":"mdi:octahedron","tags":["shape"]},{"name":"mdi:octahedron-off","tags":["shape"]},{"name":"mdi:offer","tags":[]},{"name":"mdi:office-building","tags":["places"]},{"name":"mdi:office-building-cog","tags":["settings","places","office building settings"]},{"name":"mdi:office-building-cog-outline","tags":["settings","places","office building settings outline"]},{"name":"mdi:office-building-marker","tags":["navigation","places","office building location"]},{"name":"mdi:office-building-marker-outline","tags":["navigation","places","office building location outline"]},{"name":"mdi:office-building-minus","tags":[]},{"name":"mdi:office-building-minus-outline","tags":[]},{"name":"mdi:office-building-outline","tags":["places"]},{"name":"mdi:office-building-plus","tags":[]},{"name":"mdi:office-building-plus-outline","tags":[]},{"name":"mdi:office-building-remove","tags":[]},{"name":"mdi:office-building-remove-outline","tags":[]},{"name":"mdi:oil","tags":["automotive"]},{"name":"mdi:oil-lamp","tags":["wish","genie lamp"]},{"name":"mdi:oil-level","tags":["automotive"]},{"name":"mdi:oil-temperature","tags":["automotive"]},{"name":"mdi:om","tags":["religion","religion hindu","hinduism"]},{"name":"mdi:omega","tags":["ohm","electrical resistance"]},{"name":"mdi:one-up","tags":["gaming / rpg","1up","extra life"]},{"name":"mdi:orbit","tags":["science"]},{"name":"mdi:orbit-variant","tags":["photography","camera flip"]},{"name":"mdi:order-alphabetical-ascending","tags":["text / content / format"]},{"name":"mdi:order-alphabetical-descending","tags":["text / content / format"]},{"name":"mdi:order-bool-ascending","tags":["text / content / format"]},{"name":"mdi:order-bool-ascending-variant","tags":["text / content / format","order checkbox ascending"]},{"name":"mdi:order-bool-descending","tags":["text / content / format","order checkbox descending"]},{"name":"mdi:order-bool-descending-variant","tags":["text / content / format"]},{"name":"mdi:order-numeric-ascending","tags":["text / content / format"]},{"name":"mdi:order-numeric-descending","tags":["text / content / format"]},{"name":"mdi:ornament","tags":["holiday"]},{"name":"mdi:ornament-variant","tags":["holiday"]},{"name":"mdi:outdoor-lamp","tags":["home automation","outdoor light"]},{"name":"mdi:owl","tags":["animal","holiday"]},{"name":"mdi:pac-man","tags":["gaming / rpg"]},{"name":"mdi:package","tags":["box"]},{"name":"mdi:package-check","tags":["package delivered"]},{"name":"mdi:package-up","tags":["unarchive","box up","this side up"]},{"name":"mdi:package-variant","tags":["box variant"]},{"name":"mdi:package-variant-closed","tags":["box variant closed"]},{"name":"mdi:package-variant-closed-check","tags":["package variant closed delivered"]},{"name":"mdi:package-variant-closed-minus","tags":["package variant closed subtract","box variant closed minus","box variant closed subtract"]},{"name":"mdi:package-variant-closed-plus","tags":["box variant closed plus","package variant closed add","box variant closed add"]},{"name":"mdi:package-variant-closed-remove","tags":["box variant closed remove"]},{"name":"mdi:package-variant-minus","tags":["box variant minus","package variant subtract","box variant subtract"]},{"name":"mdi:package-variant-plus","tags":["box variant plus","package variant add","box variant add"]},{"name":"mdi:package-variant-remove","tags":["box variant remove"]},{"name":"mdi:page-layout-body","tags":[]},{"name":"mdi:page-layout-footer","tags":[]},{"name":"mdi:page-layout-header","tags":[]},{"name":"mdi:page-layout-header-footer","tags":["page layout marginals"]},{"name":"mdi:page-layout-sidebar-left","tags":[]},{"name":"mdi:page-layout-sidebar-right","tags":[]},{"name":"mdi:page-next","tags":["read more"]},{"name":"mdi:page-next-outline","tags":["read more outline"]},{"name":"mdi:page-previous","tags":[]},{"name":"mdi:page-previous-outline","tags":[]},{"name":"mdi:pail","tags":["bucket"]},{"name":"mdi:pail-minus","tags":["bucket minus"]},{"name":"mdi:pail-minus-outline","tags":["bucket minus outline"]},{"name":"mdi:pail-off","tags":["bucket off"]},{"name":"mdi:pail-off-outline","tags":["bucket off outline"]},{"name":"mdi:pail-outline","tags":["bucket outline"]},{"name":"mdi:pail-plus","tags":["bucket plus"]},{"name":"mdi:pail-plus-outline","tags":["bucket plus outline"]},{"name":"mdi:pail-remove","tags":["bucket remove"]},{"name":"mdi:pail-remove-outline","tags":["bucket remove outline"]},{"name":"mdi:palette-advanced","tags":["color","drawing / art","paint"]},{"name":"mdi:palette-swatch-variant","tags":["drawing / art","color","style","paint","material"]},{"name":"mdi:palm-tree","tags":["nature"]},{"name":"mdi:pan","tags":[]},{"name":"mdi:pan-bottom-left","tags":["pan down left"]},{"name":"mdi:pan-bottom-right","tags":["pan down right"]},{"name":"mdi:pan-down","tags":[]},{"name":"mdi:pan-horizontal","tags":[]},{"name":"mdi:pan-left","tags":[]},{"name":"mdi:pan-right","tags":[]},{"name":"mdi:pan-top-left","tags":["pan up left"]},{"name":"mdi:pan-top-right","tags":["pan up right"]},{"name":"mdi:pan-up","tags":[]},{"name":"mdi:pan-vertical","tags":[]},{"name":"mdi:panda","tags":["animal","emoji panda","emoticon panda"]},{"name":"mdi:paper-cut-vertical","tags":[]},{"name":"mdi:paper-roll","tags":["home automation","printer","lavatory roll","bathroom tissue","toilet paper","kitchen roll","paper towels","receipt roll"]},{"name":"mdi:paper-roll-outline","tags":["home automation","printer","lavatory roll outline","bathroom tissue outline","kitchen roll outline","paper towels outline","toilet paper outline","receipt roll outline"]},{"name":"mdi:paperclip-check","tags":["paperclip tick","attachment check","attachment tick"]},{"name":"mdi:paperclip-lock","tags":["lock","attachment lock"]},{"name":"mdi:paperclip-minus","tags":["paperclip subtract","attachment minus","attachment subtract"]},{"name":"mdi:paperclip-off","tags":["attachment off"]},{"name":"mdi:paperclip-plus","tags":["paperclip add","attachment plus","attachment add"]},{"name":"mdi:paperclip-remove","tags":["attachment remove"]},{"name":"mdi:parachute","tags":["transportation + flying"]},{"name":"mdi:parachute-outline","tags":["transportation + flying"]},{"name":"mdi:passport","tags":[]},{"name":"mdi:passport-biometric","tags":["passport electronic"]},{"name":"mdi:patio-heater","tags":["home automation"]},{"name":"mdi:pause-box","tags":["audio","music"]},{"name":"mdi:pause-box-outline","tags":["audio","music"]},{"name":"mdi:pause-octagon","tags":["stop pause"]},{"name":"mdi:pause-octagon-outline","tags":["stop pause outline"]},{"name":"mdi:paw","tags":["animal","nature","pets"]},{"name":"mdi:paw-off","tags":["animal"]},{"name":"mdi:paw-off-outline","tags":["animal"]},{"name":"mdi:paw-outline","tags":["animal"]},{"name":"mdi:peace","tags":[]},{"name":"mdi:peanut","tags":["food / drink","agriculture","allergen","food allergy"]},{"name":"mdi:peanut-off","tags":["food / drink","agriculture","allergen off","food allergy off"]},{"name":"mdi:peanut-off-outline","tags":["food / drink","agriculture","allergen off outline","food allergy off outline"]},{"name":"mdi:peanut-outline","tags":["food / drink","agriculture","allergen outline","food allergy outline"]},{"name":"mdi:pen","tags":["drawing / art"]},{"name":"mdi:pen-lock","tags":["lock"]},{"name":"mdi:pen-minus","tags":[]},{"name":"mdi:pen-off","tags":[]},{"name":"mdi:pen-plus","tags":["pen add"]},{"name":"mdi:pen-remove","tags":[]},{"name":"mdi:pencil-box","tags":["drawing / art","edit box"]},{"name":"mdi:pencil-box-multiple","tags":["edit / modify","library edit"]},{"name":"mdi:pencil-box-multiple-outline","tags":["edit / modify","library edit outline"]},{"name":"mdi:pencil-box-outline","tags":["drawing / art","edit box outline"]},{"name":"mdi:pencil-circle","tags":["drawing / art","edit circle"]},{"name":"mdi:pencil-circle-outline","tags":["drawing / art","edit circle outline"]},{"name":"mdi:pencil-lock","tags":["lock"]},{"name":"mdi:pencil-lock-outline","tags":["lock"]},{"name":"mdi:pencil-minus","tags":[]},{"name":"mdi:pencil-minus-outline","tags":[]},{"name":"mdi:pencil-off","tags":["edit off"]},{"name":"mdi:pencil-off-outline","tags":["edit off outline"]},{"name":"mdi:pencil-plus","tags":["pencil add"]},{"name":"mdi:pencil-plus-outline","tags":["pencil add outline"]},{"name":"mdi:pencil-remove","tags":[]},{"name":"mdi:pencil-remove-outline","tags":[]},{"name":"mdi:pencil-ruler","tags":["drawing / art","design"]},{"name":"mdi:pencil-ruler-outline","tags":["drawing / art"]},{"name":"mdi:penguin","tags":["animal","emoji penguin","emoticon penguin","linux"]},{"name":"mdi:pentagon","tags":["shape"]},{"name":"mdi:pentagon-outline","tags":["shape"]},{"name":"mdi:pentagram","tags":[]},{"name":"mdi:percent","tags":["math","shopping","discount","sale"]},{"name":"mdi:percent-box","tags":["math","shopping","discount box","sale box"]},{"name":"mdi:percent-box-outline","tags":["math","shopping","discount box outline","sale box outline"]},{"name":"mdi:percent-circle","tags":["math","shopping","discount circle","sale circle"]},{"name":"mdi:percent-circle-outline","tags":["math","shopping","discount circle outline","sale circle outline"]},{"name":"mdi:percent-outline","tags":["math","shopping","discount outline","sale outline"]},{"name":"mdi:periodic-table","tags":["science"]},{"name":"mdi:perspective-less","tags":["math","perspective decrease"]},{"name":"mdi:perspective-more","tags":["math","perspective increase"]},{"name":"mdi:ph","tags":["science","home automation","acid","base","potential of hydrogen","power of hydrogen"]},{"name":"mdi:phone-alert","tags":["cellphone / phone","alert / error"]},{"name":"mdi:phone-alert-outline","tags":["cellphone / phone","alert / error"]},{"name":"mdi:phone-cancel","tags":["cellphone / phone","phone block"]},{"name":"mdi:phone-cancel-outline","tags":["cellphone / phone"]},{"name":"mdi:phone-check","tags":["cellphone / phone"]},{"name":"mdi:phone-check-outline","tags":["cellphone / phone"]},{"name":"mdi:phone-classic","tags":["cellphone / phone"]},{"name":"mdi:phone-classic-off","tags":[]},{"name":"mdi:phone-clock","tags":["cellphone / phone","date / time","phone schedule","phone time"]},{"name":"mdi:phone-dial","tags":["cellphone / phone","phone keypad"]},{"name":"mdi:phone-dial-outline","tags":["cellphone / phone","phone keypad outline"]},{"name":"mdi:phone-incoming","tags":["cellphone / phone","telephone incoming"]},{"name":"mdi:phone-incoming-outgoing","tags":["cellphone / phone"]},{"name":"mdi:phone-incoming-outgoing-outline","tags":["cellphone / phone"]},{"name":"mdi:phone-log","tags":["cellphone / phone"]},{"name":"mdi:phone-log-outline","tags":["cellphone / phone"]},{"name":"mdi:phone-off","tags":["cellphone / phone"]},{"name":"mdi:phone-outgoing","tags":["cellphone / phone"]},{"name":"mdi:phone-outgoing-outline","tags":["cellphone / phone"]},{"name":"mdi:phone-refresh","tags":["cellphone / phone","phone redial"]},{"name":"mdi:phone-refresh-outline","tags":["cellphone / phone","phone redial outline"]},{"name":"mdi:phone-remove","tags":["cellphone / phone"]},{"name":"mdi:phone-remove-outline","tags":["cellphone / phone"]},{"name":"mdi:phone-return","tags":["cellphone / phone"]},{"name":"mdi:phone-return-outline","tags":["cellphone / phone"]},{"name":"mdi:phone-rotate-landscape","tags":["cellphone / phone"]},{"name":"mdi:phone-rotate-portrait","tags":["cellphone / phone"]},{"name":"mdi:phone-sync","tags":["cellphone / phone","phone redial"]},{"name":"mdi:phone-sync-outline","tags":["cellphone / phone","phone redial outline"]},{"name":"mdi:phone-voip","tags":["cellphone / phone"]},{"name":"mdi:pi","tags":["math"]},{"name":"mdi:pi-box","tags":["math"]},{"name":"mdi:pickaxe","tags":[]},{"name":"mdi:pier","tags":["places","transportation + water"]},{"name":"mdi:pier-crane","tags":["transportation + water","places"]},{"name":"mdi:pig","tags":["animal","agriculture","emoji pig","emoticon pig"]},{"name":"mdi:pill","tags":["medical / hospital","medicine","capsule","drug","pharmaceutical"]},{"name":"mdi:pill-multiple","tags":["medical / hospital","medicine","medication","drugs"]},{"name":"mdi:pill-off","tags":["medical / hospital","medicine off","capsule off","drug off","pharmaceutical off"]},{"name":"mdi:pillar","tags":["historic","column"]},{"name":"mdi:pin-off","tags":["keep off"]},{"name":"mdi:pin-off-outline","tags":["keep off outline"]},{"name":"mdi:pine-tree","tags":["holiday","nature","places","agriculture","forest","plant"]},{"name":"mdi:pine-tree-box","tags":["holiday","nature","agriculture","plant"]},{"name":"mdi:pine-tree-fire","tags":["nature","agriculture","wildfire","controlled burn"]},{"name":"mdi:pine-tree-variant","tags":["nature","places","agriculture"]},{"name":"mdi:pine-tree-variant-outline","tags":["places","nature","agriculture"]},{"name":"mdi:pipe","tags":["home automation"]},{"name":"mdi:pipe-disconnected","tags":["home automation"]},{"name":"mdi:pipe-leak","tags":["home automation"]},{"name":"mdi:pipe-valve","tags":["home automation"]},{"name":"mdi:pirate","tags":[]},{"name":"mdi:pistol","tags":["gun"]},{"name":"mdi:piston","tags":["automotive"]},{"name":"mdi:pitchfork","tags":["hardware / tools"]},{"name":"mdi:plane-car","tags":["transportation + flying","transportation + road","airport shuttle","airport taxi","airplane car"]},{"name":"mdi:plane-train","tags":["transportation + flying","transportation + other","airport shuttle","airplane train"]},{"name":"mdi:play-box","tags":[]},{"name":"mdi:play-box-edit-outline","tags":[]},{"name":"mdi:play-box-lock","tags":["video / movie","lock"]},{"name":"mdi:play-box-lock-open","tags":["video / movie","lock"]},{"name":"mdi:play-box-lock-open-outline","tags":["video / movie","lock"]},{"name":"mdi:play-box-lock-outline","tags":["video / movie","lock"]},{"name":"mdi:play-network","tags":["media network"]},{"name":"mdi:play-network-outline","tags":["media network outline"]},{"name":"mdi:play-outline","tags":[]},{"name":"mdi:play-pause","tags":["home automation"]},{"name":"mdi:playlist-edit","tags":["edit / modify"]},{"name":"mdi:playlist-minus","tags":[]},{"name":"mdi:pliers","tags":["hardware / tools"]},{"name":"mdi:plus-box-multiple-outline","tags":[]},{"name":"mdi:plus-circle-multiple","tags":["coins plus"]},{"name":"mdi:plus-lock","tags":["lock","plus secure"]},{"name":"mdi:plus-lock-open","tags":["lock"]},{"name":"mdi:plus-minus","tags":["math"]},{"name":"mdi:plus-minus-box","tags":["math"]},{"name":"mdi:plus-minus-variant","tags":["math"]},{"name":"mdi:plus-network","tags":["add network"]},{"name":"mdi:plus-network-outline","tags":["add network outline"]},{"name":"mdi:plus-outline","tags":[]},{"name":"mdi:plus-thick","tags":["math","add thick","add bold","plus bold"]},{"name":"mdi:podium","tags":["sport"]},{"name":"mdi:podium-bronze","tags":["sport","podium third"]},{"name":"mdi:podium-gold","tags":["sport","podium first"]},{"name":"mdi:podium-silver","tags":["sport","podium second"]},{"name":"mdi:point-of-sale","tags":[]},{"name":"mdi:pokeball","tags":["gaming / rpg"]},{"name":"mdi:poker-chip","tags":["gaming / rpg","casino chip","gambling chip"]},{"name":"mdi:polaroid","tags":[]},{"name":"mdi:police-badge","tags":[]},{"name":"mdi:police-badge-outline","tags":[]},{"name":"mdi:police-station","tags":["places"]},{"name":"mdi:poll","tags":["bar chart","report","performance","analytics"]},{"name":"mdi:pool","tags":["places","home automation","swimming pool"]},{"name":"mdi:pool-thermometer","tags":["home automation","pool temperature"]},{"name":"mdi:popcorn","tags":["food / drink"]},{"name":"mdi:post-lamp","tags":["home automation","post light"]},{"name":"mdi:post-outline","tags":["blog outline"]},{"name":"mdi:postage-stamp","tags":[]},{"name":"mdi:pot","tags":["food / drink","holiday"]},{"name":"mdi:pot-mix","tags":["food / drink","holiday"]},{"name":"mdi:pot-mix-outline","tags":["food / drink","holiday"]},{"name":"mdi:pot-outline","tags":["food / drink","holiday"]},{"name":"mdi:pot-steam","tags":["food / drink","holiday"]},{"name":"mdi:pot-steam-outline","tags":["food / drink","holiday"]},{"name":"mdi:pound","tags":["hashtag"]},{"name":"mdi:pound-box","tags":["hashtag box"]},{"name":"mdi:pound-box-outline","tags":["hashtag box outline"]},{"name":"mdi:power-cycle","tags":[]},{"name":"mdi:power-off","tags":[]},{"name":"mdi:power-on","tags":[]},{"name":"mdi:power-plug-battery","tags":["home automation","battery","battery backup"]},{"name":"mdi:power-plug-battery-outline","tags":["home automation","battery","battery backup outline"]},{"name":"mdi:power-plug-off","tags":["home automation","power off"]},{"name":"mdi:power-plug-off-outline","tags":["home automation"]},{"name":"mdi:power-plug-outline","tags":["home automation"]},{"name":"mdi:power-sleep","tags":[]},{"name":"mdi:power-socket","tags":["home automation","plug socket"]},{"name":"mdi:power-socket-au","tags":["home automation","plug socket au","power socket type i","power socket cn","power socket ar","power socket nz","power socket pg","power socket australia","power socket china","power socket argentina","power socket new zealand","power socket papua new guinea"]},{"name":"mdi:power-socket-ch","tags":["home automation","plug socket ch","power socket type j","plug socket type j","power socket switzerland","plug socket switzerland"]},{"name":"mdi:power-socket-de","tags":["home automation"]},{"name":"mdi:power-socket-eu","tags":["home automation","plug socket eu","power socket europe"]},{"name":"mdi:power-socket-fr","tags":["home automation"]},{"name":"mdi:power-socket-it","tags":[]},{"name":"mdi:power-socket-jp","tags":["home automation"]},{"name":"mdi:power-socket-uk","tags":["home automation","plug socket uk","power socket type g","power socket ie","power socket hk","power socket my","power socket cy","power socket mt","power socket sg","power socket united kingdom","power socket ireland","power socket hong kong","power socket malaysia","power socket cyprus","power socket malta","power socket singapore"]},{"name":"mdi:power-socket-us","tags":["home automation","plug socket us","power socket ca","power socket mx","power socket type b","power socket united states","power socket japan","power socket canada","power socket mexico"]},{"name":"mdi:power-standby","tags":[]},{"name":"mdi:powershell","tags":[]},{"name":"mdi:prescription","tags":["medical / hospital"]},{"name":"mdi:presentation","tags":[]},{"name":"mdi:presentation-play","tags":[]},{"name":"mdi:pretzel","tags":["food / drink"]},{"name":"mdi:printer-3d","tags":["printer","home automation"]},{"name":"mdi:printer-3d-nozzle","tags":["printer"]},{"name":"mdi:printer-3d-nozzle-alert","tags":["alert / error","printer"]},{"name":"mdi:printer-3d-nozzle-alert-outline","tags":["alert / error","printer"]},{"name":"mdi:printer-3d-nozzle-heat","tags":["printer"]},{"name":"mdi:printer-3d-nozzle-heat-outline","tags":["printer"]},{"name":"mdi:printer-3d-nozzle-off","tags":["printer"]},{"name":"mdi:printer-3d-nozzle-off-outline","tags":["printer"]},{"name":"mdi:printer-3d-nozzle-outline","tags":["printer"]},{"name":"mdi:printer-3d-off","tags":["printer"]},{"name":"mdi:printer-alert","tags":["printer","home automation","alert / error","printer warning","paper jam"]},{"name":"mdi:printer-check","tags":["printer"]},{"name":"mdi:printer-eye","tags":["printer","printer preview","printer view"]},{"name":"mdi:printer-off","tags":["printer"]},{"name":"mdi:printer-pos","tags":["printer","printer point of sale","printer receipt"]},{"name":"mdi:printer-pos-alert","tags":["alert / error","printer","printer point of sale alert","printer receipt alert"]},{"name":"mdi:printer-pos-alert-outline","tags":["printer","alert / error","printer point of sale alert outline","printer receipt alert outline"]},{"name":"mdi:printer-pos-cancel","tags":["printer","printer point of sale cancel","printer receipt cancel"]},{"name":"mdi:printer-pos-cancel-outline","tags":["printer","printer point of sale cancel outline","printer receipt cancel outline"]},{"name":"mdi:printer-pos-check","tags":["printer","printer point of sale check","printer receipt check"]},{"name":"mdi:printer-pos-check-outline","tags":["printer","printer point of sale check outline","printer receipt check outline"]},{"name":"mdi:printer-pos-cog","tags":["printer","printer point of sale cog","printer receipt cog"]},{"name":"mdi:printer-pos-cog-outline","tags":["printer","printer point of sale cog outline","printer receipt cog outline"]},{"name":"mdi:printer-pos-edit","tags":["printer","printer point of sale edit","printer receipt edit"]},{"name":"mdi:printer-pos-edit-outline","tags":["printer","printer point of sale edit outline","printer receipt edit outline"]},{"name":"mdi:printer-pos-minus","tags":["printer","printer point of sale minus","printer receipt minus"]},{"name":"mdi:printer-pos-minus-outline","tags":["printer","printer point of sale minus outline","printer receipt minus outline"]},{"name":"mdi:printer-pos-network","tags":["printer","printer point of sale network","printer receipt network"]},{"name":"mdi:printer-pos-network-outline","tags":["printer","printer point of sale network outline","printer receipt network outline"]},{"name":"mdi:printer-pos-off","tags":["printer","printer point of sale off","printer receipt off"]},{"name":"mdi:printer-pos-off-outline","tags":["printer","printer point of sale off outline","printer receipt off outline"]},{"name":"mdi:printer-pos-outline","tags":["printer","printer point of sale outline","printer receipt outline"]},{"name":"mdi:printer-pos-pause","tags":["printer","printer point of sale pause","printer receipt pause"]},{"name":"mdi:printer-pos-pause-outline","tags":["printer","printer point of sale pause outline","printer receipt pause outline"]},{"name":"mdi:printer-pos-play","tags":["printer","printer point of sale play","printer receipt play"]},{"name":"mdi:printer-pos-play-outline","tags":["printer","printer point of sale play outline","printer receipt play outline"]},{"name":"mdi:printer-pos-plus","tags":["printer","printer point of sale plus","printer receipt plus"]},{"name":"mdi:printer-pos-plus-outline","tags":["printer","printer point of sale plus outline","printer receipt plus outline"]},{"name":"mdi:printer-pos-refresh","tags":["printer","printer point of sale refresh","printer receipt refresh"]},{"name":"mdi:printer-pos-refresh-outline","tags":["printer","printer point of sale refresh outline","printer receipt refresh outline"]},{"name":"mdi:printer-pos-remove","tags":["printer","printer point of sale remove","printer receipt remove"]},{"name":"mdi:printer-pos-remove-outline","tags":["printer","printer point of sale remove outline","printer receipt remove outline"]},{"name":"mdi:printer-pos-star","tags":["printer","printer point of sale star","printer receipt star","printer favorite","printer primary"]},{"name":"mdi:printer-pos-star-outline","tags":["printer","printer point of sale star outline","printer receipt star outline"]},{"name":"mdi:printer-pos-stop","tags":["printer","printer point of sale stop","printer receipt stop"]},{"name":"mdi:printer-pos-stop-outline","tags":["printer","printer point of sale stop outline","printer receipt stop outline"]},{"name":"mdi:printer-pos-sync","tags":["printer","printer point of sale sync","printer receipt sync"]},{"name":"mdi:printer-pos-sync-outline","tags":["printer","printer point of sale sync outline","printer receipt sync outline"]},{"name":"mdi:printer-pos-wrench","tags":["printer","printer point of sale wrench","printer receipt wrench"]},{"name":"mdi:printer-pos-wrench-outline","tags":["printer","printer point of sale wrench outline","printer receipt wrench outline"]},{"name":"mdi:printer-search","tags":["printer","printer preview","printer magnify"]},{"name":"mdi:printer-settings","tags":["settings","printer"]},{"name":"mdi:printer-wireless","tags":["printer"]},{"name":"mdi:professional-hexagon","tags":[]},{"name":"mdi:progress-alert","tags":["alert / error","progress warning"]},{"name":"mdi:progress-check","tags":["progress tick"]},{"name":"mdi:progress-clock","tags":["date / time"]},{"name":"mdi:progress-close","tags":[]},{"name":"mdi:progress-download","tags":[]},{"name":"mdi:progress-helper","tags":[]},{"name":"mdi:progress-pencil","tags":[]},{"name":"mdi:progress-question","tags":[]},{"name":"mdi:progress-star","tags":[]},{"name":"mdi:progress-star-four-points","tags":["progress auto"]},{"name":"mdi:progress-upload","tags":[]},{"name":"mdi:progress-wrench","tags":["hardware / tools","progress spanner"]},{"name":"mdi:projector","tags":["device / tech","home automation"]},{"name":"mdi:projector-off","tags":["device / tech","home automation"]},{"name":"mdi:projector-screen","tags":["device / tech","home automation"]},{"name":"mdi:projector-screen-off","tags":["home automation"]},{"name":"mdi:projector-screen-off-outline","tags":["home automation"]},{"name":"mdi:projector-screen-outline","tags":["home automation"]},{"name":"mdi:projector-screen-variant","tags":["home automation"]},{"name":"mdi:projector-screen-variant-off","tags":["home automation"]},{"name":"mdi:projector-screen-variant-off-outline","tags":["home automation"]},{"name":"mdi:projector-screen-variant-outline","tags":["home automation"]},{"name":"mdi:protocol","tags":[]},{"name":"mdi:publish-off","tags":["arrow","publish disabled"]},{"name":"mdi:pulse","tags":["medical / hospital","vitals"]},{"name":"mdi:pump","tags":[]},{"name":"mdi:pump-off","tags":[]},{"name":"mdi:pumpkin","tags":["holiday"]},{"name":"mdi:purse","tags":[]},{"name":"mdi:purse-outline","tags":[]},{"name":"mdi:puzzle-check","tags":["gaming / rpg"]},{"name":"mdi:puzzle-check-outline","tags":["gaming / rpg"]},{"name":"mdi:puzzle-edit","tags":["gaming / rpg","edit / modify"]},{"name":"mdi:puzzle-edit-outline","tags":["gaming / rpg","edit / modify"]},{"name":"mdi:puzzle-heart","tags":["gaming / rpg"]},{"name":"mdi:puzzle-heart-outline","tags":["gaming / rpg"]},{"name":"mdi:puzzle-minus","tags":["gaming / rpg"]},{"name":"mdi:puzzle-minus-outline","tags":["gaming / rpg"]},{"name":"mdi:puzzle-plus","tags":["gaming / rpg"]},{"name":"mdi:puzzle-plus-outline","tags":["gaming / rpg"]},{"name":"mdi:puzzle-remove","tags":["gaming / rpg"]},{"name":"mdi:puzzle-remove-outline","tags":["gaming / rpg"]},{"name":"mdi:puzzle-star","tags":["gaming / rpg","puzzle favorite"]},{"name":"mdi:puzzle-star-outline","tags":["gaming / rpg","puzzle favorite outline"]},{"name":"mdi:pyramid","tags":["shape"]},{"name":"mdi:pyramid-off","tags":["shape"]},{"name":"mdi:qrcode","tags":[]},{"name":"mdi:qrcode-edit","tags":["edit / modify"]},{"name":"mdi:qrcode-minus","tags":[]},{"name":"mdi:qrcode-plus","tags":[]},{"name":"mdi:qrcode-remove","tags":[]},{"name":"mdi:qrcode-scan","tags":[]},{"name":"mdi:quadcopter","tags":["drone"]},{"name":"mdi:quality-low","tags":["low quality","lq"]},{"name":"mdi:quality-medium","tags":["medium quality","mq"]},{"name":"mdi:quora","tags":[]},{"name":"mdi:rabbit","tags":["animal","nature","bunny","hare"]},{"name":"mdi:radiator","tags":["home automation","heater"]},{"name":"mdi:radiator-disabled","tags":["home automation","heater disabled"]},{"name":"mdi:radiator-off","tags":["home automation","heater off"]},{"name":"mdi:radio-am","tags":["audio"]},{"name":"mdi:radio-fm","tags":["audio"]},{"name":"mdi:radio-handheld","tags":["device / tech"]},{"name":"mdi:radio-off","tags":[]},{"name":"mdi:radio-tower","tags":[]},{"name":"mdi:radioactive","tags":["science","radiation"]},{"name":"mdi:radioactive-circle","tags":["science","radiation circle"]},{"name":"mdi:radioactive-circle-outline","tags":["science","radiation circle outline"]},{"name":"mdi:radioactive-off","tags":["science","radiation off"]},{"name":"mdi:radiobox-indeterminate-variant","tags":["form","radio button indeterminate","radiobox intermediate variant"]},{"name":"mdi:radiology-box","tags":["medical / hospital","x ray box"]},{"name":"mdi:radiology-box-outline","tags":["medical / hospital","x ray box outline"]},{"name":"mdi:radius","tags":["math","circle radius","sphere radius"]},{"name":"mdi:radius-outline","tags":["math","circle radius outline","sphere radius outline"]},{"name":"mdi:railroad-light","tags":["transportation + other","railroad crossing light","train crossing light","level crossing signals"]},{"name":"mdi:rake","tags":["hardware / tools"]},{"name":"mdi:raspberry-pi","tags":["raspberrypi"]},{"name":"mdi:ray-end","tags":[]},{"name":"mdi:ray-end-arrow","tags":[]},{"name":"mdi:ray-start","tags":[]},{"name":"mdi:ray-start-arrow","tags":[]},{"name":"mdi:ray-start-end","tags":[]},{"name":"mdi:ray-vertex","tags":[]},{"name":"mdi:razor-double-edge","tags":["health / beauty","hardware / tools"]},{"name":"mdi:razor-single-edge","tags":["hardware / tools"]},{"name":"mdi:read","tags":[]},{"name":"mdi:receipt","tags":["cloth","fabric","swatch"]},{"name":"mdi:receipt-clock","tags":["receipt pending"]},{"name":"mdi:receipt-clock-outline","tags":["receipt pending"]},{"name":"mdi:receipt-outline","tags":["cloth outline","fabric outline","swatch outline"]},{"name":"mdi:receipt-send","tags":[]},{"name":"mdi:receipt-send-outline","tags":[]},{"name":"mdi:receipt-text-arrow-left","tags":["invoice arrow left","invoice receive"]},{"name":"mdi:receipt-text-arrow-left-outline","tags":["invoice arrow left outline","invoice receive outline"]},{"name":"mdi:receipt-text-arrow-right","tags":["invoice arrow right","invoice send"]},{"name":"mdi:receipt-text-arrow-right-outline","tags":["invoice arrow right outline","invoice send outline"]},{"name":"mdi:receipt-text-check","tags":["invoice check"]},{"name":"mdi:receipt-text-check-outline","tags":["invoice check outline"]},{"name":"mdi:receipt-text-clock","tags":["invoice clock","invoice schedule","receipt text pending"]},{"name":"mdi:receipt-text-clock-outline","tags":["invoice clock outline","invoice schedule outline","receipt text pending"]},{"name":"mdi:receipt-text-edit","tags":["invoice edit"]},{"name":"mdi:receipt-text-edit-outline","tags":["invoice edit outline"]},{"name":"mdi:receipt-text-minus","tags":["invoice minus"]},{"name":"mdi:receipt-text-minus-outline","tags":["invoice minus outline"]},{"name":"mdi:receipt-text-plus","tags":["invoice plus","invoice add","receipt text add"]},{"name":"mdi:receipt-text-plus-outline","tags":["invoice plus","invoice add","receipt text add"]},{"name":"mdi:receipt-text-remove","tags":["invoice remove"]},{"name":"mdi:receipt-text-remove-outline","tags":["invoice remove outline"]},{"name":"mdi:receipt-text-send","tags":[]},{"name":"mdi:receipt-text-send-outline","tags":[]},{"name":"mdi:record","tags":["home automation","fiber manual record"]},{"name":"mdi:record-circle","tags":[]},{"name":"mdi:record-circle-outline","tags":[]},{"name":"mdi:record-player","tags":["home automation"]},{"name":"mdi:record-rec","tags":["home automation"]},{"name":"mdi:rectangle","tags":["shape"]},{"name":"mdi:rectangle-outline","tags":["shape"]},{"name":"mdi:recycle","tags":[]},{"name":"mdi:recycle-variant","tags":[]},{"name":"mdi:redo-variant","tags":["arrow"]},{"name":"mdi:reflect-horizontal","tags":[]},{"name":"mdi:reflect-vertical","tags":[]},{"name":"mdi:refresh-auto","tags":["automotive","auto start","automatic start","auto stop","automatic stop","automatic","refresh automatic"]},{"name":"mdi:refresh-circle","tags":[]},{"name":"mdi:regex","tags":["regular expression"]},{"name":"mdi:registered-trademark","tags":[]},{"name":"mdi:reiterate","tags":["arrow"]},{"name":"mdi:relation-many-to-many","tags":["database"]},{"name":"mdi:relation-many-to-one","tags":["database"]},{"name":"mdi:relation-many-to-one-or-many","tags":["database"]},{"name":"mdi:relation-many-to-only-one","tags":["database"]},{"name":"mdi:relation-many-to-zero-or-many","tags":["database"]},{"name":"mdi:relation-many-to-zero-or-one","tags":["database"]},{"name":"mdi:relation-one-or-many-to-many","tags":["database"]},{"name":"mdi:relation-one-or-many-to-one","tags":["database"]},{"name":"mdi:relation-one-or-many-to-one-or-many","tags":["database"]},{"name":"mdi:relation-one-or-many-to-only-one","tags":["database"]},{"name":"mdi:relation-one-or-many-to-zero-or-many","tags":["database"]},{"name":"mdi:relation-one-or-many-to-zero-or-one","tags":["database"]},{"name":"mdi:relation-one-to-many","tags":["database"]},{"name":"mdi:relation-one-to-one","tags":["database"]},{"name":"mdi:relation-one-to-one-or-many","tags":["database"]},{"name":"mdi:relation-one-to-only-one","tags":["database"]},{"name":"mdi:relation-one-to-zero-or-many","tags":["database"]},{"name":"mdi:relation-one-to-zero-or-one","tags":["database"]},{"name":"mdi:relation-only-one-to-many","tags":["database"]},{"name":"mdi:relation-only-one-to-one","tags":["database"]},{"name":"mdi:relation-only-one-to-one-or-many","tags":["database"]},{"name":"mdi:relation-only-one-to-only-one","tags":["database"]},{"name":"mdi:relation-only-one-to-zero-or-many","tags":["database"]},{"name":"mdi:relation-only-one-to-zero-or-one","tags":["database"]},{"name":"mdi:relation-zero-or-many-to-many","tags":["database"]},{"name":"mdi:relation-zero-or-many-to-one","tags":["database"]},{"name":"mdi:relation-zero-or-many-to-one-or-many","tags":["database"]},{"name":"mdi:relation-zero-or-many-to-only-one","tags":["database"]},{"name":"mdi:relation-zero-or-many-to-zero-or-many","tags":["database"]},{"name":"mdi:relation-zero-or-many-to-zero-or-one","tags":["database"]},{"name":"mdi:relation-zero-or-one-to-many","tags":["database"]},{"name":"mdi:relation-zero-or-one-to-one","tags":["database"]},{"name":"mdi:relation-zero-or-one-to-one-or-many","tags":["database"]},{"name":"mdi:relation-zero-or-one-to-only-one","tags":["database"]},{"name":"mdi:relation-zero-or-one-to-zero-or-many","tags":["database"]},{"name":"mdi:relation-zero-or-one-to-zero-or-one","tags":["database"]},{"name":"mdi:reload","tags":["automotive","arrow","car engine start","loop","rotate clockwise"]},{"name":"mdi:reload-alert","tags":["alert / error"]},{"name":"mdi:remote-desktop","tags":[]},{"name":"mdi:remote-off","tags":[]},{"name":"mdi:remote-tv","tags":["device / tech"]},{"name":"mdi:remote-tv-off","tags":["device / tech"]},{"name":"mdi:rename-box","tags":[]},{"name":"mdi:rename-box-outline","tags":[]},{"name":"mdi:reorder-vertical","tags":[]},{"name":"mdi:repeat-off","tags":[]},{"name":"mdi:repeat-variant","tags":["arrow","twitter retweet","repost"]},{"name":"mdi:reply-all-outline","tags":["arrow"]},{"name":"mdi:reply-circle","tags":["arrow"]},{"name":"mdi:reply-outline","tags":["arrow"]},{"name":"mdi:reproduction","tags":["medical / hospital"]},{"name":"mdi:resistor","tags":[]},{"name":"mdi:resistor-nodes","tags":[]},{"name":"mdi:resize","tags":[]},{"name":"mdi:resize-bottom-right","tags":["drag"]},{"name":"mdi:responsive","tags":[]},{"name":"mdi:restart-alert","tags":["alert / error"]},{"name":"mdi:restart-off","tags":[]},{"name":"mdi:restore-alert","tags":["alert / error"]},{"name":"mdi:rewind-10","tags":[]},{"name":"mdi:rewind-15","tags":[]},{"name":"mdi:rewind-30","tags":[]},{"name":"mdi:rewind-45","tags":[]},{"name":"mdi:rewind-5","tags":[]},{"name":"mdi:rewind-60","tags":[]},{"name":"mdi:rhombus","tags":["shape","diamond"]},{"name":"mdi:rhombus-medium","tags":["shape"]},{"name":"mdi:rhombus-medium-outline","tags":["shape"]},{"name":"mdi:rhombus-outline","tags":["shape","diamond outline"]},{"name":"mdi:rhombus-split","tags":["shape","collection"]},{"name":"mdi:rhombus-split-outline","tags":["shape"]},{"name":"mdi:rice","tags":["food / drink"]},{"name":"mdi:rickshaw","tags":["transportation + road","transportation + other"]},{"name":"mdi:rickshaw-electric","tags":["transportation + road","transportation + other"]},{"name":"mdi:ring","tags":[]},{"name":"mdi:rivet","tags":["hardware / tools"]},{"name":"mdi:road","tags":["transportation + road"]},{"name":"mdi:road-variant","tags":["transportation + road"]},{"name":"mdi:robber","tags":[]},{"name":"mdi:robot","tags":["home automation","emoji robot","emoticon robot"]},{"name":"mdi:robot-angry","tags":["emoji robot angry","emoticon robot angry"]},{"name":"mdi:robot-angry-outline","tags":["emoji robot angry outline","emoticon robot angry outline"]},{"name":"mdi:robot-confused","tags":["emoji robot confused","emoticon robot confused"]},{"name":"mdi:robot-confused-outline","tags":["emoji robot confused outline","emoticon robot confused outline"]},{"name":"mdi:robot-dead","tags":["emoji robot dead","emoticon robot dead"]},{"name":"mdi:robot-dead-outline","tags":["emoji robot dead outline","emoticon robot dead outline"]},{"name":"mdi:robot-excited","tags":["emoticon robot excited","emoji robot excited"]},{"name":"mdi:robot-excited-outline","tags":["emoji robot excited outline","emoticon robot excited outline"]},{"name":"mdi:robot-happy","tags":["emoji robot happy","emoticon robot happy"]},{"name":"mdi:robot-happy-outline","tags":["emoji robot happy outline","emoticon robot happy outline"]},{"name":"mdi:robot-industrial","tags":["autonomous","assembly"]},{"name":"mdi:robot-industrial-outline","tags":[]},{"name":"mdi:robot-love","tags":["emoji robot love","emoticon robot love"]},{"name":"mdi:robot-love-outline","tags":[]},{"name":"mdi:robot-mower","tags":["home automation","lawn mower"]},{"name":"mdi:robot-mower-outline","tags":["home automation","lawn mower outline"]},{"name":"mdi:robot-off","tags":["emoji robot off","emoticon robot off"]},{"name":"mdi:robot-off-outline","tags":[]},{"name":"mdi:robot-outline","tags":["emoji robot outline","emoticon robot outline"]},{"name":"mdi:robot-vacuum","tags":["device / tech","home automation","roomba"]},{"name":"mdi:robot-vacuum-alert","tags":["alert / error","home automation","robot vacuum error"]},{"name":"mdi:robot-vacuum-off","tags":["home automation"]},{"name":"mdi:robot-vacuum-variant","tags":["home automation","neato"]},{"name":"mdi:robot-vacuum-variant-alert","tags":["alert / error","home automation","robot vacuum variant error"]},{"name":"mdi:robot-vacuum-variant-off","tags":["home automation"]},{"name":"mdi:rocket-launch","tags":["science","transportation + flying"]},{"name":"mdi:rocket-launch-outline","tags":["science","transportation + flying"]},{"name":"mdi:roller-skate","tags":["sport"]},{"name":"mdi:roller-skate-off","tags":["sport"]},{"name":"mdi:rollerblade","tags":["sport"]},{"name":"mdi:rollerblade-off","tags":["sport"]},{"name":"mdi:rolodex","tags":[]},{"name":"mdi:rolodex-outline","tags":[]},{"name":"mdi:roman-numeral-1","tags":["alpha / numeric"]},{"name":"mdi:roman-numeral-10","tags":["alpha / numeric"]},{"name":"mdi:roman-numeral-2","tags":["alpha / numeric"]},{"name":"mdi:roman-numeral-3","tags":["alpha / numeric"]},{"name":"mdi:roman-numeral-4","tags":["alpha / numeric"]},{"name":"mdi:roman-numeral-5","tags":["alpha / numeric"]},{"name":"mdi:roman-numeral-6","tags":["alpha / numeric"]},{"name":"mdi:roman-numeral-7","tags":["alpha / numeric"]},{"name":"mdi:roman-numeral-8","tags":["alpha / numeric"]},{"name":"mdi:roman-numeral-9","tags":["alpha / numeric"]},{"name":"mdi:rotate-3d-variant","tags":["3d rotation"]},{"name":"mdi:rotate-left-variant","tags":[]},{"name":"mdi:rotate-orbit","tags":["gyro","accelerometer"]},{"name":"mdi:rotate-right-variant","tags":[]},{"name":"mdi:router","tags":[]},{"name":"mdi:router-network","tags":[]},{"name":"mdi:router-wireless-off","tags":[]},{"name":"mdi:routes","tags":["sign routes"]},{"name":"mdi:routes-clock","tags":["date / time"]},{"name":"mdi:rss-box","tags":["rss feed box"]},{"name":"mdi:rss-off","tags":[]},{"name":"mdi:rug","tags":["home automation","carpet"]},{"name":"mdi:ruler","tags":["hardware / tools","drawing / art"]},{"name":"mdi:ruler-square","tags":["hardware / tools","drawing / art","square","carpentry","architecture"]},{"name":"mdi:ruler-square-compass","tags":["hardware / tools","mason","masonic","freemasonry"]},{"name":"mdi:run-fast","tags":["home automation","sport","people / family","velocity","human run fast"]},{"name":"mdi:rv-truck","tags":["transportation + road","recreational vehicle","campervan"]},{"name":"mdi:sack","tags":["gaming / rpg"]},{"name":"mdi:sack-outline","tags":[]},{"name":"mdi:sack-percent","tags":[]},{"name":"mdi:safe","tags":["banking"]},{"name":"mdi:safe-square","tags":[]},{"name":"mdi:safe-square-outline","tags":[]},{"name":"mdi:safety-goggles","tags":["science","safety glasses"]},{"name":"mdi:sail-boat-sink","tags":["transportation + water","sail boat crash","sail boat wreck"]},{"name":"mdi:sale","tags":["shopping","discount"]},{"name":"mdi:sale-outline","tags":["shopping","discount outline"]},{"name":"mdi:satellite-uplink","tags":[]},{"name":"mdi:satellite-variant","tags":[]},{"name":"mdi:sausage","tags":["food / drink"]},{"name":"mdi:sausage-off","tags":["food / drink"]},{"name":"mdi:saw-blade","tags":["hardware / tools"]},{"name":"mdi:sawtooth-wave","tags":["audio"]},{"name":"mdi:scale","tags":["food / drink","science"]},{"name":"mdi:scale-balance","tags":["science","justice","legal"]},{"name":"mdi:scale-bathroom","tags":["home automation","medical / hospital"]},{"name":"mdi:scale-off","tags":["science"]},{"name":"mdi:scale-unbalanced","tags":[]},{"name":"mdi:scan-helper","tags":[]},{"name":"mdi:scanner-off","tags":["device / tech"]},{"name":"mdi:scent","tags":["aroma","fragrance","smell","odor"]},{"name":"mdi:scent-off","tags":["aroma off","smell off","fragrance off","odor off"]},{"name":"mdi:scissors-cutting","tags":[]},{"name":"mdi:scoreboard","tags":["sport"]},{"name":"mdi:scoreboard-outline","tags":["sport"]},{"name":"mdi:screw-flat-top","tags":["hardware / tools"]},{"name":"mdi:screw-lag","tags":["hardware / tools"]},{"name":"mdi:screw-machine-flat-top","tags":["hardware / tools"]},{"name":"mdi:screw-machine-round-top","tags":["hardware / tools"]},{"name":"mdi:screw-round-top","tags":["hardware / tools"]},{"name":"mdi:screwdriver","tags":["hardware / tools"]},{"name":"mdi:script","tags":["gaming / rpg","scroll"]},{"name":"mdi:script-outline","tags":["gaming / rpg","scroll outline"]},{"name":"mdi:script-text","tags":["gaming / rpg","scroll text"]},{"name":"mdi:script-text-key","tags":[]},{"name":"mdi:script-text-key-outline","tags":[]},{"name":"mdi:script-text-outline","tags":["gaming / rpg","scroll text outline"]},{"name":"mdi:script-text-play","tags":[]},{"name":"mdi:script-text-play-outline","tags":[]},{"name":"mdi:seal","tags":["ribbon","prize","award"]},{"name":"mdi:seal-variant","tags":["ribbon","prize","award"]},{"name":"mdi:search-web","tags":["search globe","global search","internet search"]},{"name":"mdi:seat-passenger","tags":[]},{"name":"mdi:seatbelt","tags":["automotive","seat belt","safety belt"]},{"name":"mdi:security-network","tags":["shield network","uac network","administrator network"]},{"name":"mdi:seed","tags":["agriculture","nature","food / drink"]},{"name":"mdi:seed-off","tags":["nature","food / drink","agriculture"]},{"name":"mdi:seed-off-outline","tags":["nature","food / drink","agriculture"]},{"name":"mdi:seed-outline","tags":["agriculture","nature","food / drink"]},{"name":"mdi:seed-plus","tags":["agriculture","nature","seed add"]},{"name":"mdi:seed-plus-outline","tags":["agriculture","nature","seed add outline"]},{"name":"mdi:seesaw","tags":["playground seesaw"]},{"name":"mdi:select","tags":[]},{"name":"mdi:select-arrow-down","tags":[]},{"name":"mdi:select-arrow-up","tags":[]},{"name":"mdi:select-compare","tags":[]},{"name":"mdi:select-drag","tags":[]},{"name":"mdi:select-inverse","tags":["selection invert"]},{"name":"mdi:select-marker","tags":["navigation","select location"]},{"name":"mdi:select-multiple","tags":[]},{"name":"mdi:select-multiple-marker","tags":["navigation","select multiple location"]},{"name":"mdi:select-off","tags":[]},{"name":"mdi:select-place","tags":[]},{"name":"mdi:select-remove","tags":[]},{"name":"mdi:select-search","tags":[]},{"name":"mdi:selection","tags":[]},{"name":"mdi:selection-drag","tags":[]},{"name":"mdi:selection-ellipse","tags":[]},{"name":"mdi:selection-ellipse-remove","tags":[]},{"name":"mdi:selection-marker","tags":["navigation","selection location"]},{"name":"mdi:selection-multiple","tags":[]},{"name":"mdi:selection-multiple-marker","tags":["navigation","selection multiple location"]},{"name":"mdi:selection-off","tags":[]},{"name":"mdi:selection-remove","tags":[]},{"name":"mdi:selection-search","tags":[]},{"name":"mdi:send-check","tags":[]},{"name":"mdi:send-check-outline","tags":[]},{"name":"mdi:send-circle","tags":[]},{"name":"mdi:send-circle-outline","tags":[]},{"name":"mdi:send-lock","tags":["lock","send secure"]},{"name":"mdi:send-lock-outline","tags":["lock"]},{"name":"mdi:send-outline","tags":["paper airplane outline","paper plane outline"]},{"name":"mdi:send-variant-clock","tags":[]},{"name":"mdi:send-variant-clock-outline","tags":[]},{"name":"mdi:serial-port","tags":["vga"]},{"name":"mdi:server","tags":["storage"]},{"name":"mdi:server-minus","tags":["server remove"]},{"name":"mdi:server-network","tags":[]},{"name":"mdi:server-network-off","tags":[]},{"name":"mdi:server-off","tags":[]},{"name":"mdi:server-plus","tags":["server add"]},{"name":"mdi:server-remove","tags":[]},{"name":"mdi:server-security","tags":["server shield"]},{"name":"mdi:set-all","tags":["database","set union","set or","full outer join","sql full outer join"]},{"name":"mdi:set-center","tags":["database","set centre","set intersection","set and","inner join","sql inner join"]},{"name":"mdi:set-center-right","tags":["database","set centre right","outer join right","sql right outer join"]},{"name":"mdi:set-left","tags":["database","difference left"]},{"name":"mdi:set-left-center","tags":["database","set left centre","outer join left","sql left outer join"]},{"name":"mdi:set-left-right","tags":["database","exclusion","set xor"]},{"name":"mdi:set-merge","tags":[]},{"name":"mdi:set-none","tags":["database","set null","set not","venn diagram"]},{"name":"mdi:set-right","tags":["database","difference right"]},{"name":"mdi:set-split","tags":[]},{"name":"mdi:set-top-box","tags":["home automation"]},{"name":"mdi:settings-helper","tags":["settings"]},{"name":"mdi:shaker","tags":["food / drink","pepper","fish food"]},{"name":"mdi:shaker-outline","tags":["food / drink","salt","fish food outline"]},{"name":"mdi:shape-circle-plus","tags":["shape","shape circle add"]},{"name":"mdi:shape-oval-plus","tags":[]},{"name":"mdi:shape-plus","tags":["shape","shape add","category plus"]},{"name":"mdi:shape-plus-outline","tags":["shape","shape add outline","category plus outline"]},{"name":"mdi:shape-polygon-plus","tags":["shape","shape polygon add"]},{"name":"mdi:shape-rectangle-plus","tags":["shape","shape rectangle add"]},{"name":"mdi:shape-square-plus","tags":["shape","shape square add"]},{"name":"mdi:shape-square-rounded-plus","tags":[]},{"name":"mdi:share-all","tags":[]},{"name":"mdi:share-all-outline","tags":[]},{"name":"mdi:share-circle","tags":["arrow"]},{"name":"mdi:share-off","tags":["arrow","forward off"]},{"name":"mdi:share-off-outline","tags":["arrow","forward off outline"]},{"name":"mdi:share-outline","tags":["arrow","forward outline"]},{"name":"mdi:share-variant-outline","tags":[]},{"name":"mdi:shark","tags":["animal","jaws"]},{"name":"mdi:shark-fin","tags":["animal"]},{"name":"mdi:shark-fin-outline","tags":["animal"]},{"name":"mdi:shark-off","tags":["animal","jaws off"]},{"name":"mdi:sheep","tags":["animal","agriculture","emoji sheep","emoticon sheep"]},{"name":"mdi:shield","tags":["gaming / rpg"]},{"name":"mdi:shield-account","tags":["account / user","home automation","security account","shield user","shield person","alarm arm home"]},{"name":"mdi:shield-account-outline","tags":["account / user","home automation","security account outline","shield user outline","shield person outline","alarm arm home outline"]},{"name":"mdi:shield-airplane","tags":["transportation + flying","shield aeroplane","shield plane","plane shield"]},{"name":"mdi:shield-airplane-outline","tags":["transportation + flying","shield aeroplane outline","shield plane outline"]},{"name":"mdi:shield-alert","tags":["alert / error","shield warning"]},{"name":"mdi:shield-alert-outline","tags":["alert / error","shield warning outline"]},{"name":"mdi:shield-bug","tags":["antivirus"]},{"name":"mdi:shield-bug-outline","tags":["antivirus outline"]},{"name":"mdi:shield-car","tags":["automotive","car security","car insurance"]},{"name":"mdi:shield-check-outline","tags":["shield tick outline"]},{"name":"mdi:shield-cross","tags":["gaming / rpg","religion","shield templar","shield christianity"]},{"name":"mdi:shield-cross-outline","tags":["gaming / rpg","religion","shield templar outline","shield christianity outline"]},{"name":"mdi:shield-crown","tags":["gaming / rpg","administrator"]},{"name":"mdi:shield-crown-outline","tags":["gaming / rpg","administrator outline"]},{"name":"mdi:shield-edit","tags":["edit / modify"]},{"name":"mdi:shield-edit-outline","tags":["edit / modify"]},{"name":"mdi:shield-half","tags":[]},{"name":"mdi:shield-half-full","tags":[]},{"name":"mdi:shield-home","tags":["home automation","security home","shield house","alarm arm home"]},{"name":"mdi:shield-home-outline","tags":["home automation","shield house outline","alarm arm home"]},{"name":"mdi:shield-link-variant","tags":[]},{"name":"mdi:shield-link-variant-outline","tags":[]},{"name":"mdi:shield-lock","tags":["lock","home automation","security lock","alarm arm away"]},{"name":"mdi:shield-lock-open","tags":["home automation","lock","shield unlocked"]},{"name":"mdi:shield-lock-open-outline","tags":["home automation","lock","shield unlocked outline"]},{"name":"mdi:shield-lock-outline","tags":["lock","home automation","alarm arm away outline","security lock outline"]},{"name":"mdi:shield-moon","tags":["home automation","alarm arm night"]},{"name":"mdi:shield-moon-outline","tags":["home automation","alarm arm night outline"]},{"name":"mdi:shield-off","tags":["security off"]},{"name":"mdi:shield-off-outline","tags":[]},{"name":"mdi:shield-outline","tags":["gaming / rpg"]},{"name":"mdi:shield-refresh","tags":[]},{"name":"mdi:shield-refresh-outline","tags":[]},{"name":"mdi:shield-remove","tags":[]},{"name":"mdi:shield-remove-outline","tags":[]},{"name":"mdi:shield-star","tags":["badge","shield favorite"]},{"name":"mdi:shield-star-outline","tags":["badge outline","shield favorite outline"]},{"name":"mdi:shield-sun","tags":["weather","sun protection"]},{"name":"mdi:shield-sun-outline","tags":["weather","sun protection outline"]},{"name":"mdi:shield-sword","tags":["gaming / rpg","moderator"]},{"name":"mdi:shield-sword-outline","tags":["gaming / rpg","moderator outline"]},{"name":"mdi:shield-sync","tags":[]},{"name":"mdi:shield-sync-outline","tags":[]},{"name":"mdi:shimmer","tags":["sparkles"]},{"name":"mdi:shipping-pallet","tags":[]},{"name":"mdi:shoe-ballet","tags":["sport","clothing","slippers ballet"]},{"name":"mdi:shoe-cleat","tags":["sport","clothing"]},{"name":"mdi:shoe-formal","tags":["clothing"]},{"name":"mdi:shoe-heel","tags":["clothing"]},{"name":"mdi:shoe-print","tags":["footprints"]},{"name":"mdi:shoe-sneaker","tags":["sport","clothing","shoe running"]},{"name":"mdi:shopping-music","tags":["shopping"]},{"name":"mdi:shopping-search","tags":["shopping"]},{"name":"mdi:shopping-search-outline","tags":["shopping"]},{"name":"mdi:shore","tags":[]},{"name":"mdi:shovel","tags":["hardware / tools","gardening"]},{"name":"mdi:shovel-off","tags":["hardware / tools"]},{"name":"mdi:shower","tags":["home automation","bathtub","bathroom"]},{"name":"mdi:shower-head","tags":["home automation","bathroom"]},{"name":"mdi:shredder","tags":[]},{"name":"mdi:shuffle-disabled","tags":["arrow"]},{"name":"mdi:shuffle-variant","tags":["arrow"]},{"name":"mdi:shuriken","tags":["ninja star"]},{"name":"mdi:sickle","tags":["hardware / tools"]},{"name":"mdi:sigma-lower","tags":[]},{"name":"mdi:sign-caution","tags":["transportation + road","barrier"]},{"name":"mdi:sign-direction","tags":["milestone"]},{"name":"mdi:sign-direction-minus","tags":["milestone minus"]},{"name":"mdi:sign-direction-plus","tags":["milestone plus","sign direction add","milestone add"]},{"name":"mdi:sign-direction-remove","tags":["milestone remove"]},{"name":"mdi:sign-pole","tags":[]},{"name":"mdi:sign-real-estate","tags":[]},{"name":"mdi:sign-text","tags":[]},{"name":"mdi:sign-yield","tags":["transportation + road","give way"]},{"name":"mdi:signal","tags":["cellphone / phone"]},{"name":"mdi:signal-2g","tags":["cellphone / phone"]},{"name":"mdi:signal-3g","tags":["cellphone / phone"]},{"name":"mdi:signal-4g","tags":["cellphone / phone"]},{"name":"mdi:signal-5g","tags":["cellphone / phone"]},{"name":"mdi:signal-cellular-1","tags":["cellphone / phone"]},{"name":"mdi:signal-cellular-2","tags":["cellphone / phone"]},{"name":"mdi:signal-cellular-3","tags":["cellphone / phone"]},{"name":"mdi:signal-cellular-outline","tags":["cellphone / phone","signal cellular 0"]},{"name":"mdi:signal-distance-variant","tags":[]},{"name":"mdi:signal-hspa","tags":["cellphone / phone"]},{"name":"mdi:signal-hspa-plus","tags":["cellphone / phone"]},{"name":"mdi:signal-off","tags":["cellphone / phone"]},{"name":"mdi:signal-variant","tags":[]},{"name":"mdi:signature","tags":["form"]},{"name":"mdi:signature-freehand","tags":["form"]},{"name":"mdi:signature-image","tags":["form"]},{"name":"mdi:signature-text","tags":["form"]},{"name":"mdi:silo","tags":["agriculture","farm"]},{"name":"mdi:silo-outline","tags":["agriculture","farm outline"]},{"name":"mdi:silverware-clean","tags":["food / drink","silverware shimmer","cutlery clean"]},{"name":"mdi:silverware-fork","tags":["food / drink","cutlery fork"]},{"name":"mdi:silverware-spoon","tags":["food / drink","cutlery spoon"]},{"name":"mdi:silverware-variant","tags":["food / drink","places","cutlery variant"]},{"name":"mdi:sim-alert-outline","tags":["cellphone / phone","alert / error"]},{"name":"mdi:sim-off-outline","tags":["cellphone / phone"]},{"name":"mdi:sim-outline","tags":["cellphone / phone","sim card outline","subscriber identity module outline","subscriber identification module outline"]},{"name":"mdi:sine-wave","tags":["audio","alternating current","current ac","wave","analog","frequency","amplitude"]},{"name":"mdi:sitemap","tags":["workflow","flowchart"]},{"name":"mdi:sitemap-outline","tags":["workflow outline","flowchart outline"]},{"name":"mdi:size-l","tags":["size large"]},{"name":"mdi:size-m","tags":["size medium"]},{"name":"mdi:size-s","tags":["size small"]},{"name":"mdi:size-xl","tags":["size extra large"]},{"name":"mdi:size-xs","tags":["size extra small"]},{"name":"mdi:size-xxl","tags":["size extra extra large"]},{"name":"mdi:size-xxs","tags":["size extra extra small"]},{"name":"mdi:size-xxxl","tags":[]},{"name":"mdi:skate-off","tags":["sport"]},{"name":"mdi:skew-less","tags":["math","skew decrease"]},{"name":"mdi:skew-more","tags":["math","skew increase"]},{"name":"mdi:ski-water","tags":["sport","people / family","transportation + water","human ski water"]},{"name":"mdi:skip-backward","tags":["home automation","title backward","previous title"]},{"name":"mdi:skip-backward-outline","tags":[]},{"name":"mdi:skip-forward","tags":["home automation","title forward","next title"]},{"name":"mdi:skip-forward-outline","tags":[]},{"name":"mdi:skip-next-circle","tags":[]},{"name":"mdi:skip-next-circle-outline","tags":[]},{"name":"mdi:skip-next-outline","tags":[]},{"name":"mdi:skip-previous-circle","tags":[]},{"name":"mdi:skip-previous-circle-outline","tags":[]},{"name":"mdi:skip-previous-outline","tags":[]},{"name":"mdi:skull","tags":["holiday","gaming / rpg"]},{"name":"mdi:skull-crossbones","tags":["gaming / rpg","holiday","jolly roger"]},{"name":"mdi:skull-crossbones-outline","tags":["gaming / rpg","holiday","jolly roger outline"]},{"name":"mdi:skull-outline","tags":["holiday","gaming / rpg"]},{"name":"mdi:skull-scan","tags":["medical / hospital","x ray","radiology"]},{"name":"mdi:skull-scan-outline","tags":["medical / hospital","x ray outline","radiology outline"]},{"name":"mdi:slash-forward","tags":["math","divide","division"]},{"name":"mdi:slash-forward-box","tags":["math","divide box","division box"]},{"name":"mdi:sleep-off","tags":[]},{"name":"mdi:slide","tags":["playground slide"]},{"name":"mdi:slope-downhill","tags":[]},{"name":"mdi:slope-uphill","tags":[]},{"name":"mdi:slot-machine","tags":["casino","gambling"]},{"name":"mdi:slot-machine-outline","tags":["casino outline","gambling outline"]},{"name":"mdi:smart-card","tags":["account / user"]},{"name":"mdi:smart-card-off","tags":["account / user"]},{"name":"mdi:smart-card-off-outline","tags":["account / user"]},{"name":"mdi:smart-card-outline","tags":["account / user"]},{"name":"mdi:smart-card-reader","tags":["account / user"]},{"name":"mdi:smart-card-reader-outline","tags":["account / user"]},{"name":"mdi:smog","tags":[]},{"name":"mdi:smoke","tags":["smog","fire"]},{"name":"mdi:smoke-detector-alert","tags":["home automation","alert / error"]},{"name":"mdi:smoke-detector-alert-outline","tags":["home automation","alert / error"]},{"name":"mdi:smoke-detector-off","tags":["home automation"]},{"name":"mdi:smoke-detector-off-outline","tags":["home automation"]},{"name":"mdi:smoke-detector-outline","tags":["home automation"]},{"name":"mdi:smoke-detector-variant","tags":["home automation"]},{"name":"mdi:smoke-detector-variant-alert","tags":["home automation","alert / error"]},{"name":"mdi:smoke-detector-variant-off","tags":["home automation"]},{"name":"mdi:smoking-pipe","tags":[]},{"name":"mdi:smoking-pipe-off","tags":[]},{"name":"mdi:snail","tags":["animal","gastropod"]},{"name":"mdi:snake","tags":["animal","reptile"]},{"name":"mdi:snowflake-alert","tags":["weather","alert / error","home automation","cold alert","snow advisory","freeze advisory"]},{"name":"mdi:snowflake-check","tags":["weather","snowflake approve"]},{"name":"mdi:snowflake-melt","tags":["weather","defrost"]},{"name":"mdi:snowflake-off","tags":["weather"]},{"name":"mdi:snowflake-thermometer","tags":["weather","home automation","frost point","freezing point","snowflake temperature"]},{"name":"mdi:snowflake-variant","tags":["holiday","weather"]},{"name":"mdi:snowman","tags":["holiday"]},{"name":"mdi:soccer-field","tags":["sport","football pitch"]},{"name":"mdi:social-distance-2-meters","tags":["medical / hospital"]},{"name":"mdi:sofa","tags":["home automation","couch","living room","family room"]},{"name":"mdi:sofa-outline","tags":["home automation","couch outline","living room outline","family room outline"]},{"name":"mdi:sofa-single","tags":["home automation","loveseat","love seat","couch","chair accent","living room","family room"]},{"name":"mdi:sofa-single-outline","tags":["home automation","loveseat outline","love seat outline","couch outline","chair accent outline","living room outline","family room outline"]},{"name":"mdi:solar-panel","tags":["home automation","solar energy","solar electricity"]},{"name":"mdi:solar-panel-large","tags":["home automation","solar panel energy","solar panel electricity"]},{"name":"mdi:solar-power","tags":["home automation","solar energy","solar electricity"]},{"name":"mdi:soldering-iron","tags":[]},{"name":"mdi:solid","tags":[]},{"name":"mdi:sort","tags":["text / content / format"]},{"name":"mdi:sort-alphabetical-ascending","tags":["text / content / format"]},{"name":"mdi:sort-alphabetical-ascending-variant","tags":["text / content / format"]},{"name":"mdi:sort-alphabetical-descending","tags":["text / content / format"]},{"name":"mdi:sort-alphabetical-descending-variant","tags":["text / content / format"]},{"name":"mdi:sort-ascending","tags":["text / content / format"]},{"name":"mdi:sort-bool-ascending","tags":["text / content / format"]},{"name":"mdi:sort-bool-ascending-variant","tags":["text / content / format","sort checkbox ascending"]},{"name":"mdi:sort-bool-descending","tags":["text / content / format"]},{"name":"mdi:sort-bool-descending-variant","tags":["text / content / format","sort checkbox descending"]},{"name":"mdi:sort-calendar-ascending","tags":["text / content / format","date / time","sort date ascending"]},{"name":"mdi:sort-calendar-descending","tags":["text / content / format","date / time","sort date descending"]},{"name":"mdi:sort-clock-ascending","tags":["text / content / format","date / time","sort time ascending"]},{"name":"mdi:sort-clock-ascending-outline","tags":["text / content / format","date / time","sort time ascending outline"]},{"name":"mdi:sort-clock-descending","tags":["text / content / format","date / time","sort time descending"]},{"name":"mdi:sort-clock-descending-outline","tags":["text / content / format","date / time","sort time descending outline"]},{"name":"mdi:sort-descending","tags":["text / content / format"]},{"name":"mdi:sort-numeric-ascending","tags":["text / content / format"]},{"name":"mdi:sort-numeric-ascending-variant","tags":["text / content / format"]},{"name":"mdi:sort-numeric-descending","tags":["text / content / format"]},{"name":"mdi:sort-numeric-descending-variant","tags":["text / content / format"]},{"name":"mdi:sort-reverse-variant","tags":["text / content / format"]},{"name":"mdi:sort-variant-lock","tags":["text / content / format","lock"]},{"name":"mdi:sort-variant-lock-open","tags":["text / content / format","lock"]},{"name":"mdi:sort-variant-off","tags":["text / content / format"]},{"name":"mdi:sort-variant-remove","tags":["text / content / format"]},{"name":"mdi:soundbar","tags":["home automation","speaker bar"]},{"name":"mdi:source-branch","tags":["developer / languages"]},{"name":"mdi:source-branch-check","tags":["developer / languages"]},{"name":"mdi:source-branch-minus","tags":["developer / languages"]},{"name":"mdi:source-branch-plus","tags":["developer / languages"]},{"name":"mdi:source-branch-refresh","tags":["developer / languages"]},{"name":"mdi:source-branch-remove","tags":["developer / languages"]},{"name":"mdi:source-branch-sync","tags":["developer / languages"]},{"name":"mdi:source-commit","tags":[]},{"name":"mdi:source-commit-end","tags":[]},{"name":"mdi:source-commit-end-local","tags":[]},{"name":"mdi:source-commit-local","tags":[]},{"name":"mdi:source-commit-next-local","tags":[]},{"name":"mdi:source-commit-start","tags":[]},{"name":"mdi:source-commit-start-next-local","tags":[]},{"name":"mdi:source-fork","tags":["developer / languages"]},{"name":"mdi:source-merge","tags":["developer / languages"]},{"name":"mdi:source-pull","tags":["developer / languages"]},{"name":"mdi:source-repository","tags":["developer / languages"]},{"name":"mdi:source-repository-multiple","tags":["developer / languages","source repositories"]},{"name":"mdi:soy-sauce","tags":["food / drink","soya sauce"]},{"name":"mdi:soy-sauce-off","tags":[]},{"name":"mdi:space-invaders","tags":["gaming / rpg"]},{"name":"mdi:space-station","tags":[]},{"name":"mdi:spade","tags":["hardware / tools"]},{"name":"mdi:speaker-bluetooth","tags":["audio"]},{"name":"mdi:speaker-message","tags":["home automation","audio","text to speech"]},{"name":"mdi:speaker-multiple","tags":["audio","speakers"]},{"name":"mdi:speaker-off","tags":["audio","home automation"]},{"name":"mdi:speaker-pause","tags":["audio","music"]},{"name":"mdi:speaker-play","tags":["audio","music"]},{"name":"mdi:speaker-stop","tags":["audio","music"]},{"name":"mdi:speaker-wireless","tags":["audio","home automation"]},{"name":"mdi:spear","tags":["gaming / rpg","staff","fishing"]},{"name":"mdi:speedometer","tags":["automotive"]},{"name":"mdi:speedometer-medium","tags":["automotive"]},{"name":"mdi:speedometer-slow","tags":["automotive"]},{"name":"mdi:sphere","tags":["shape"]},{"name":"mdi:sphere-off","tags":["shape"]},{"name":"mdi:spider","tags":["holiday","nature","animal","arachnid","bug"]},{"name":"mdi:spider-outline","tags":["animal","holiday","nature","arachnid outline"]},{"name":"mdi:spider-thread","tags":["holiday","nature","animal","arachnid thread","bug"]},{"name":"mdi:spider-web","tags":["holiday","cobweb","arachnid web"]},{"name":"mdi:spirit-level","tags":["hardware / tools"]},{"name":"mdi:spoon-sugar","tags":["food / drink"]},{"name":"mdi:spotlight","tags":["home automation"]},{"name":"mdi:spotlight-beam","tags":["home automation"]},{"name":"mdi:spray","tags":["agriculture","drawing / art","color","paint","aerosol"]},{"name":"mdi:spray-bottle","tags":["cleaning"]},{"name":"mdi:sprinkler","tags":["home automation","agriculture","irrigation"]},{"name":"mdi:sprinkler-fire","tags":["home automation","agriculture","sprinkler mist","mister","sprinkler head"]},{"name":"mdi:sprinkler-variant","tags":["home automation","agriculture","irrigation"]},{"name":"mdi:sprout","tags":["agriculture","nature","seedling","plant","ecology","environment"]},{"name":"mdi:sprout-outline","tags":["agriculture","nature","seedling outline","plant outline","ecology outline","environment outline"]},{"name":"mdi:square","tags":["shape"]},{"name":"mdi:square-circle","tags":["food / drink","vegetarian","lacto vegetarian"]},{"name":"mdi:square-circle-outline","tags":[]},{"name":"mdi:square-edit-outline","tags":["edit / modify"]},{"name":"mdi:square-medium","tags":["shape"]},{"name":"mdi:square-medium-outline","tags":["shape"]},{"name":"mdi:square-off","tags":[]},{"name":"mdi:square-off-outline","tags":[]},{"name":"mdi:square-opacity","tags":["drawing / art","shape","square transparent"]},{"name":"mdi:square-outline","tags":["shape"]},{"name":"mdi:square-root","tags":["math"]},{"name":"mdi:square-root-box","tags":[]},{"name":"mdi:square-rounded","tags":[]},{"name":"mdi:square-rounded-badge","tags":["shape","notification","app badge","push notification"]},{"name":"mdi:square-rounded-badge-outline","tags":["shape","notification","app badge outline","push notification outline"]},{"name":"mdi:square-rounded-outline","tags":[]},{"name":"mdi:square-small","tags":["bullet"]},{"name":"mdi:square-wave","tags":["audio"]},{"name":"mdi:squeegee","tags":[]},{"name":"mdi:ssh","tags":[]},{"name":"mdi:stadium-variant","tags":["places","sport","arena"]},{"name":"mdi:stairs","tags":["transportation + other"]},{"name":"mdi:stairs-box","tags":[]},{"name":"mdi:stairs-down","tags":["transportation + other"]},{"name":"mdi:stairs-up","tags":["transportation + other"]},{"name":"mdi:stamper","tags":[]},{"name":"mdi:standard-definition","tags":["video / movie"]},{"name":"mdi:star-box","tags":["favorite box"]},{"name":"mdi:star-box-multiple","tags":["favorite box multiple"]},{"name":"mdi:star-box-multiple-outline","tags":["favorite box multiple outline"]},{"name":"mdi:star-box-outline","tags":["favorite box outline"]},{"name":"mdi:star-check","tags":["shape","favorite check"]},{"name":"mdi:star-check-outline","tags":["shape","favorite check outline"]},{"name":"mdi:star-cog","tags":["settings","favorite cog"]},{"name":"mdi:star-cog-outline","tags":["settings","favorite cog outline"]},{"name":"mdi:star-crescent","tags":["religion","islam","religion islamic","religion muslim"]},{"name":"mdi:star-david","tags":["religion","jewish","religion judaic","judaism","magen david"]},{"name":"mdi:star-four-points","tags":["shape"]},{"name":"mdi:star-four-points-box","tags":["shape","auto box"]},{"name":"mdi:star-four-points-box-outline","tags":["shape","auto box outline"]},{"name":"mdi:star-four-points-circle","tags":["shape","auto circle"]},{"name":"mdi:star-four-points-circle-outline","tags":["shape","auto circle outline"]},{"name":"mdi:star-four-points-outline","tags":["shape"]},{"name":"mdi:star-four-points-small","tags":["shape"]},{"name":"mdi:star-half","tags":["shape","favorite half"]},{"name":"mdi:star-minus","tags":["shape","favorite minus"]},{"name":"mdi:star-minus-outline","tags":["shape","favorite minus outline"]},{"name":"mdi:star-off","tags":["favorite off"]},{"name":"mdi:star-off-outline","tags":["favorite off outline"]},{"name":"mdi:star-plus","tags":["shape","favorite plus","star add","favorite add"]},{"name":"mdi:star-plus-outline","tags":["shape","star add outline","favorite plus outline","favorite add outline"]},{"name":"mdi:star-remove","tags":["shape","favorite remove"]},{"name":"mdi:star-remove-outline","tags":["shape","favorite remove outline"]},{"name":"mdi:star-settings","tags":["settings","favorite settings"]},{"name":"mdi:star-settings-outline","tags":["settings","favorite settings outline"]},{"name":"mdi:star-shooting","tags":["favorite shooting"]},{"name":"mdi:star-shooting-outline","tags":["favorite shooting outline"]},{"name":"mdi:star-three-points","tags":["shape"]},{"name":"mdi:star-three-points-outline","tags":["shape"]},{"name":"mdi:state-machine","tags":[]},{"name":"mdi:step-backward","tags":[]},{"name":"mdi:step-backward-2","tags":["frame backward"]},{"name":"mdi:step-forward","tags":[]},{"name":"mdi:step-forward-2","tags":["frame forward"]},{"name":"mdi:stethoscope","tags":["medical / hospital"]},{"name":"mdi:sticker","tags":[]},{"name":"mdi:sticker-alert","tags":["alert / error"]},{"name":"mdi:sticker-alert-outline","tags":["alert / error"]},{"name":"mdi:sticker-check","tags":[]},{"name":"mdi:sticker-check-outline","tags":[]},{"name":"mdi:sticker-circle-outline","tags":[]},{"name":"mdi:sticker-minus","tags":[]},{"name":"mdi:sticker-minus-outline","tags":[]},{"name":"mdi:sticker-outline","tags":[]},{"name":"mdi:sticker-plus","tags":[]},{"name":"mdi:sticker-plus-outline","tags":[]},{"name":"mdi:sticker-remove","tags":[]},{"name":"mdi:sticker-remove-outline","tags":[]},{"name":"mdi:sticker-text","tags":[]},{"name":"mdi:sticker-text-outline","tags":[]},{"name":"mdi:stocking","tags":["holiday"]},{"name":"mdi:stomach","tags":["medical / hospital"]},{"name":"mdi:stool","tags":[]},{"name":"mdi:stool-outline","tags":[]},{"name":"mdi:stop-circle","tags":[]},{"name":"mdi:stop-circle-outline","tags":[]},{"name":"mdi:store-alert","tags":["places","shopping","alert / error","shop alert"]},{"name":"mdi:store-alert-outline","tags":["places","shopping","alert / error","shop alert outline"]},{"name":"mdi:store-check","tags":["shopping","places","shop check","shop complete","store complete"]},{"name":"mdi:store-check-outline","tags":["shopping","places","shop complete","store complete outline","shop check outline"]},{"name":"mdi:store-clock","tags":["places","shopping","store schedule","store hours","shop clock","shop hours","shop schedule","store time","shop time"]},{"name":"mdi:store-clock-outline","tags":["places","shopping","date / time","shop clock outline","store hours outline","shop hours outline","store time outline","shop time outline","store schedule outline","shop schedule outline"]},{"name":"mdi:store-cog","tags":["places","shopping","settings","store settings","shop settings"]},{"name":"mdi:store-cog-outline","tags":["places","shopping","settings","store settings outline","shop settings outline","shop cog outline"]},{"name":"mdi:store-edit","tags":["places","shopping","edit / modify","shop edit"]},{"name":"mdi:store-edit-outline","tags":["places","shopping","edit / modify","shop edit outline"]},{"name":"mdi:store-marker","tags":["places","shopping","navigation","store location","shop marker","shop location"]},{"name":"mdi:store-marker-outline","tags":["places","shopping","navigation","store location outline","shop marker outline","shop location outline"]},{"name":"mdi:store-minus","tags":["places","shopping","shop minus"]},{"name":"mdi:store-minus-outline","tags":["places","shopping","shop minus outline"]},{"name":"mdi:store-off","tags":["places","shopping","shop off"]},{"name":"mdi:store-off-outline","tags":["places","shopping","shop off outline"]},{"name":"mdi:store-plus","tags":["places","shopping","shop plus"]},{"name":"mdi:store-plus-outline","tags":["places","shopping","shop plus outline"]},{"name":"mdi:store-remove","tags":["places","shopping","shop remove","store delete","shop delete"]},{"name":"mdi:store-remove-outline","tags":["places","shopping","shop remove outline","store delete outline","shop delete outline"]},{"name":"mdi:store-search","tags":["places","shopping","shop search","store find","shop find","store locator","shop locator","store look up","shop look up"]},{"name":"mdi:store-search-outline","tags":["places","shopping","store find outline","shop search outline","shop find outline","store locator outline","shop locator outline","store look up outline","shop look up outline"]},{"name":"mdi:store-settings","tags":["places","shopping","settings","shop settings"]},{"name":"mdi:store-settings-outline","tags":["places","shopping","settings","shop settings outline"]},{"name":"mdi:storefront","tags":["places","awning"]},{"name":"mdi:storefront-check","tags":[]},{"name":"mdi:storefront-check-outline","tags":[]},{"name":"mdi:storefront-edit","tags":["edit / modify"]},{"name":"mdi:storefront-edit-outline","tags":["edit / modify"]},{"name":"mdi:storefront-minus","tags":[]},{"name":"mdi:storefront-minus-outline","tags":[]},{"name":"mdi:storefront-plus","tags":[]},{"name":"mdi:storefront-plus-outline","tags":[]},{"name":"mdi:storefront-remove","tags":[]},{"name":"mdi:storefront-remove-outline","tags":[]},{"name":"mdi:stove","tags":["food / drink","home automation","cooker","oven"]},{"name":"mdi:strategy","tags":["sport","football play"]},{"name":"mdi:stretch-to-page","tags":["text / content / format","arrow"]},{"name":"mdi:stretch-to-page-outline","tags":["text / content / format","arrow"]},{"name":"mdi:string-lights","tags":["home automation","italian lights","christmas lights","fairy lights"]},{"name":"mdi:string-lights-off","tags":["home automation","italian lights off","christmas lights off","fairy lights off"]},{"name":"mdi:submarine","tags":[]},{"name":"mdi:subway-alert-variant","tags":["alert / error","transportation + other","subway warning variant"]},{"name":"mdi:summit","tags":["peak"]},{"name":"mdi:sun-angle","tags":["weather","solar angle"]},{"name":"mdi:sun-angle-outline","tags":["weather","solar angle outline"]},{"name":"mdi:sun-clock","tags":["weather","home automation","sun schedule","sun time","time of day"]},{"name":"mdi:sun-clock-outline","tags":["weather","home automation","date / time","sun schedule outline","sun time outline","time of day outline"]},{"name":"mdi:sun-compass","tags":["weather","home automation","navigation","sun azimuth","solar compass","solar asimuth"]},{"name":"mdi:sun-snowflake-variant","tags":["home automation","weather","hot cold","heat cool"]},{"name":"mdi:sun-thermometer","tags":["weather","home automation","heat index","sun temperature","day temperature","external temperature","outdoor temperature"]},{"name":"mdi:sun-thermometer-outline","tags":["home automation","weather","external temperature","outside temperature","heat index","day temperature"]},{"name":"mdi:sun-wireless","tags":["home automation","weather","weather sun wireless","illuminance","uv ray","ultraviolet"]},{"name":"mdi:sun-wireless-outline","tags":["home automation","weather","weather sun wireless outline","illuminance outline","uv ray outline","ultraviolet outline"]},{"name":"mdi:sunglasses","tags":["clothing"]},{"name":"mdi:surround-sound-2-0","tags":["audio","stereo"]},{"name":"mdi:surround-sound-2-1","tags":[]},{"name":"mdi:surround-sound-3-1","tags":["audio"]},{"name":"mdi:surround-sound-5-1","tags":["audio"]},{"name":"mdi:surround-sound-5-1-2","tags":[]},{"name":"mdi:surround-sound-7-1","tags":["audio"]},{"name":"mdi:swap-horizontal-circle-outline","tags":["arrow"]},{"name":"mdi:swap-vertical-circle","tags":["arrow"]},{"name":"mdi:swap-vertical-circle-outline","tags":["arrow"]},{"name":"mdi:swim","tags":["sport"]},{"name":"mdi:switch","tags":[]},{"name":"mdi:sword","tags":["gaming / rpg"]},{"name":"mdi:sword-cross","tags":["gaming / rpg"]},{"name":"mdi:syllabary-hangul","tags":["alpha / numeric","writing system hangul"]},{"name":"mdi:syllabary-hiragana","tags":["alpha / numeric","writing system hiragana"]},{"name":"mdi:syllabary-katakana","tags":["alpha / numeric","writing system katakana"]},{"name":"mdi:syllabary-katakana-halfwidth","tags":["alpha / numeric","writing system katakana half width"]},{"name":"mdi:symbol","tags":[]},{"name":"mdi:sync-circle","tags":[]},{"name":"mdi:tab-minus","tags":[]},{"name":"mdi:tab-plus","tags":["tab add"]},{"name":"mdi:tab-remove","tags":[]},{"name":"mdi:tab-search","tags":["tab find"]},{"name":"mdi:table","tags":["text / content / format"]},{"name":"mdi:table-account","tags":["account / user","table user"]},{"name":"mdi:table-alert","tags":["alert / error"]},{"name":"mdi:table-arrow-down","tags":["table download"]},{"name":"mdi:table-arrow-left","tags":["table import"]},{"name":"mdi:table-arrow-right","tags":["table share","table export"]},{"name":"mdi:table-arrow-up","tags":["table upload"]},{"name":"mdi:table-border","tags":["text / content / format"]},{"name":"mdi:table-cancel","tags":[]},{"name":"mdi:table-chair","tags":["home automation","restaurant","kitchen","dining","dining room"]},{"name":"mdi:table-check","tags":[]},{"name":"mdi:table-clock","tags":["date / time"]},{"name":"mdi:table-cog","tags":["settings","table settings"]},{"name":"mdi:table-column","tags":["text / content / format"]},{"name":"mdi:table-column-plus-after","tags":["text / content / format","table column add after"]},{"name":"mdi:table-column-plus-before","tags":["text / content / format","table column add before"]},{"name":"mdi:table-column-remove","tags":["text / content / format"]},{"name":"mdi:table-column-width","tags":["text / content / format"]},{"name":"mdi:table-edit","tags":["edit / modify","text / content / format"]},{"name":"mdi:table-eye","tags":[]},{"name":"mdi:table-eye-off","tags":[]},{"name":"mdi:table-filter","tags":[]},{"name":"mdi:table-furniture","tags":["home automation","kitchen","dining room"]},{"name":"mdi:table-headers-eye","tags":[]},{"name":"mdi:table-headers-eye-off","tags":[]},{"name":"mdi:table-heart","tags":["table favorite"]},{"name":"mdi:table-key","tags":[]},{"name":"mdi:table-large","tags":["text / content / format","geographic information system"]},{"name":"mdi:table-large-plus","tags":["text / content / format","geographic information system","table large add"]},{"name":"mdi:table-large-remove","tags":["text / content / format","geographic information system"]},{"name":"mdi:table-lock","tags":["lock"]},{"name":"mdi:table-minus","tags":[]},{"name":"mdi:table-multiple","tags":[]},{"name":"mdi:table-network","tags":[]},{"name":"mdi:table-off","tags":[]},{"name":"mdi:table-picnic","tags":[]},{"name":"mdi:table-pivot","tags":["text / content / format"]},{"name":"mdi:table-plus","tags":["text / content / format","table add"]},{"name":"mdi:table-question","tags":["table help"]},{"name":"mdi:table-refresh","tags":[]},{"name":"mdi:table-remove","tags":["text / content / format"]},{"name":"mdi:table-row","tags":["text / content / format"]},{"name":"mdi:table-row-height","tags":["text / content / format"]},{"name":"mdi:table-row-plus-after","tags":["text / content / format","table row add after"]},{"name":"mdi:table-row-plus-before","tags":["text / content / format","table row add before"]},{"name":"mdi:table-row-remove","tags":["text / content / format"]},{"name":"mdi:table-search","tags":[]},{"name":"mdi:table-settings","tags":["settings"]},{"name":"mdi:table-split-cell","tags":["text / content / format"]},{"name":"mdi:table-star","tags":["table favorite"]},{"name":"mdi:table-sync","tags":[]},{"name":"mdi:tablet-dashboard","tags":["device / tech"]},{"name":"mdi:taco","tags":["food / drink"]},{"name":"mdi:tag-arrow-down","tags":[]},{"name":"mdi:tag-arrow-down-outline","tags":[]},{"name":"mdi:tag-arrow-left","tags":[]},{"name":"mdi:tag-arrow-left-outline","tags":[]},{"name":"mdi:tag-arrow-right","tags":[]},{"name":"mdi:tag-arrow-right-outline","tags":[]},{"name":"mdi:tag-arrow-up","tags":[]},{"name":"mdi:tag-arrow-up-outline","tags":[]},{"name":"mdi:tag-check","tags":["tag approve"]},{"name":"mdi:tag-check-outline","tags":["tag approve outline"]},{"name":"mdi:tag-hidden","tags":[]},{"name":"mdi:tag-minus","tags":[]},{"name":"mdi:tag-minus-outline","tags":[]},{"name":"mdi:tag-multiple","tags":["tags"]},{"name":"mdi:tag-multiple-outline","tags":[]},{"name":"mdi:tag-off","tags":[]},{"name":"mdi:tag-off-outline","tags":[]},{"name":"mdi:tag-plus","tags":["tag add"]},{"name":"mdi:tag-plus-outline","tags":[]},{"name":"mdi:tag-remove","tags":[]},{"name":"mdi:tag-remove-outline","tags":[]},{"name":"mdi:tag-search","tags":["tag find"]},{"name":"mdi:tag-search-outline","tags":["tag find outline"]},{"name":"mdi:tag-text","tags":[]},{"name":"mdi:tag-text-outline","tags":[]},{"name":"mdi:tally-mark-1","tags":["math","counting 1","one"]},{"name":"mdi:tally-mark-2","tags":["math","counting 2","two"]},{"name":"mdi:tally-mark-3","tags":["math","counting 3","three"]},{"name":"mdi:tally-mark-4","tags":["math","counting 4","four"]},{"name":"mdi:tally-mark-5","tags":["math","counting 5","five"]},{"name":"mdi:tangram","tags":["gaming / rpg","puzzle"]},{"name":"mdi:tank","tags":[]},{"name":"mdi:tanker-truck","tags":["transportation + road","fuel truck","oil truck","water truck","tanker"]},{"name":"mdi:tape-drive","tags":[]},{"name":"mdi:tape-measure","tags":["hardware / tools","measuring tape"]},{"name":"mdi:target","tags":["registration mark"]},{"name":"mdi:target-account","tags":["account / user","crosshairs account","target user"]},{"name":"mdi:target-variant","tags":["registration mark"]},{"name":"mdi:tea-outline","tags":["food / drink"]},{"name":"mdi:teddy-bear","tags":["holiday","home automation","child toy","children toy","kids room","childrens room","play room"]},{"name":"mdi:telescope","tags":["science"]},{"name":"mdi:television-ambient-light","tags":["home automation"]},{"name":"mdi:television-classic","tags":["device / tech","home automation","tv classic"]},{"name":"mdi:television-classic-off","tags":["device / tech","home automation","tv classic off"]},{"name":"mdi:television-guide","tags":["device / tech","home automation"]},{"name":"mdi:television-off","tags":["device / tech","home automation","tv off"]},{"name":"mdi:television-pause","tags":["device / tech"]},{"name":"mdi:television-shimmer","tags":["device / tech","television clean"]},{"name":"mdi:television-speaker","tags":["audio","video / movie"]},{"name":"mdi:television-speaker-off","tags":["audio","video / movie"]},{"name":"mdi:television-stop","tags":["device / tech"]},{"name":"mdi:temperature-celsius","tags":["weather","temperature centigrade"]},{"name":"mdi:temperature-fahrenheit","tags":["weather"]},{"name":"mdi:temperature-kelvin","tags":["weather"]},{"name":"mdi:tennis-ball-outline","tags":["sport"]},{"name":"mdi:tent","tags":["camping"]},{"name":"mdi:test-tube","tags":["science"]},{"name":"mdi:test-tube-empty","tags":["science"]},{"name":"mdi:test-tube-off","tags":["science"]},{"name":"mdi:text-account","tags":["account / user","biography","text user"]},{"name":"mdi:text-box-check","tags":["files / folders","file document box tick","file document box check"]},{"name":"mdi:text-box-check-outline","tags":["files / folders","file document box tick outline","file document box check outline"]},{"name":"mdi:text-box-edit","tags":["files / folders","edit / modify"]},{"name":"mdi:text-box-edit-outline","tags":["files / folders","edit / modify"]},{"name":"mdi:text-box-minus","tags":["files / folders","file document box minus"]},{"name":"mdi:text-box-minus-outline","tags":["files / folders","file document box minus outline"]},{"name":"mdi:text-box-multiple","tags":["files / folders","file document boxes","file document box multiple"]},{"name":"mdi:text-box-multiple-outline","tags":["files / folders","file document boxes outline","file document box multiple outline"]},{"name":"mdi:text-box-outline","tags":["files / folders","file document box outline"]},{"name":"mdi:text-box-plus","tags":["files / folders","file document box plus"]},{"name":"mdi:text-box-plus-outline","tags":["files / folders","file document box plus outline"]},{"name":"mdi:text-box-remove","tags":["files / folders","file document box remove"]},{"name":"mdi:text-box-remove-outline","tags":["files / folders","file document box remove outline"]},{"name":"mdi:text-box-search","tags":["files / folders","file document box search"]},{"name":"mdi:text-box-search-outline","tags":["files / folders","file document box search outline"]},{"name":"mdi:text-recognition","tags":[]},{"name":"mdi:text-search","tags":["notes search"]},{"name":"mdi:text-search-variant","tags":["notes search variant"]},{"name":"mdi:text-shadow","tags":[]},{"name":"mdi:texture-box","tags":["math","surface area"]},{"name":"mdi:theater","tags":["places","home automation","cinema","theatre"]},{"name":"mdi:theme-light-dark","tags":["weather","sun moon stars"]},{"name":"mdi:thermometer","tags":["weather","home automation","automotive","temperature"]},{"name":"mdi:thermometer-alert","tags":["home automation","weather","alert / error","thermometer warning","temperature alert","temperature warning"]},{"name":"mdi:thermometer-auto","tags":["home automation","weather","temperature auto"]},{"name":"mdi:thermometer-bluetooth","tags":["weather","home automation","automotive","temperature bluetooth"]},{"name":"mdi:thermometer-check","tags":["weather","home automation","thermometer approve","temperature check","temperature approve"]},{"name":"mdi:thermometer-chevron-down","tags":["home automation","weather","temperature chevron down","temperature decrease","thermometer decrease"]},{"name":"mdi:thermometer-chevron-up","tags":["home automation","weather","temperature chevron up","temperature increase","thermometer increase"]},{"name":"mdi:thermometer-high","tags":["home automation","weather","temperature high"]},{"name":"mdi:thermometer-lines","tags":["weather","home automation","temperature lines"]},{"name":"mdi:thermometer-low","tags":["home automation","weather","temperature low"]},{"name":"mdi:thermometer-minus","tags":["home automation","weather","temperature minus","thermometer decrease","temperature decrease"]},{"name":"mdi:thermometer-off","tags":["weather","home automation","temperature off"]},{"name":"mdi:thermometer-plus","tags":["home automation","weather","thermometer add","thermometer increase","temperature plus","temperature add","temperature increase"]},{"name":"mdi:thermometer-probe","tags":[]},{"name":"mdi:thermometer-probe-off","tags":[]},{"name":"mdi:thermometer-water","tags":["weather","home automation","dew point","water temperature","boiling point"]},{"name":"mdi:thermostat-auto","tags":["home automation"]},{"name":"mdi:thermostat-box","tags":["home automation","device / tech"]},{"name":"mdi:thermostat-box-auto","tags":["home automation"]},{"name":"mdi:thermostat-cog","tags":[]},{"name":"mdi:thought-bubble","tags":["comic bubble","thinking"]},{"name":"mdi:thought-bubble-outline","tags":["comic thought bubble outline","thinking outline","think outline"]},{"name":"mdi:ticket-account","tags":["account / user","ticket user"]},{"name":"mdi:ticket-outline","tags":[]},{"name":"mdi:ticket-percent","tags":["coupon","voucher"]},{"name":"mdi:ticket-percent-outline","tags":["coupon outline","voucher outline"]},{"name":"mdi:tie","tags":["clothing"]},{"name":"mdi:tilde","tags":[]},{"name":"mdi:tilde-off","tags":[]},{"name":"mdi:timeline","tags":[]},{"name":"mdi:timeline-alert","tags":["alert / error"]},{"name":"mdi:timeline-alert-outline","tags":["alert / error"]},{"name":"mdi:timeline-check","tags":[]},{"name":"mdi:timeline-check-outline","tags":[]},{"name":"mdi:timeline-clock","tags":["date / time"]},{"name":"mdi:timeline-clock-outline","tags":["date / time"]},{"name":"mdi:timeline-minus","tags":[]},{"name":"mdi:timeline-minus-outline","tags":[]},{"name":"mdi:timeline-outline","tags":[]},{"name":"mdi:timeline-plus","tags":[]},{"name":"mdi:timeline-plus-outline","tags":[]},{"name":"mdi:timeline-question","tags":["timeline help"]},{"name":"mdi:timeline-question-outline","tags":["timeline help outline"]},{"name":"mdi:timeline-remove","tags":[]},{"name":"mdi:timeline-remove-outline","tags":[]},{"name":"mdi:timeline-text","tags":[]},{"name":"mdi:timeline-text-outline","tags":[]},{"name":"mdi:timer","tags":["sport","date / time","stopwatch"]},{"name":"mdi:timer-alert","tags":["date / time","alert / error","stopwatch alert"]},{"name":"mdi:timer-alert-outline","tags":["date / time","alert / error","stopwatch alert outline"]},{"name":"mdi:timer-cancel","tags":["date / time","stopwatch cancel"]},{"name":"mdi:timer-cancel-outline","tags":["date / time","stopwatch cancel outline"]},{"name":"mdi:timer-check","tags":["date / time","stopwatch check","timer tick","stopwatch tick"]},{"name":"mdi:timer-check-outline","tags":["date / time","timer tick outline","stopwatch check outline","stopwatch tick outline"]},{"name":"mdi:timer-cog","tags":["date / time","settings","timer settings"]},{"name":"mdi:timer-cog-outline","tags":["date / time","settings","timer settings outline"]},{"name":"mdi:timer-edit","tags":["date / time","edit / modify","stopwatch edit"]},{"name":"mdi:timer-edit-outline","tags":["date / time","edit / modify","stopwatch edit outline"]},{"name":"mdi:timer-lock","tags":["date / time","lock","stopwatch lock","timer secure","stopwatch secure"]},{"name":"mdi:timer-lock-open","tags":["date / time","lock","stopwatch lock open"]},{"name":"mdi:timer-lock-open-outline","tags":["date / time","lock","stopwatch lock open outline"]},{"name":"mdi:timer-lock-outline","tags":["date / time","lock","stopwatch lock outline","stopwatch secure outline","timer secure outline"]},{"name":"mdi:timer-marker","tags":["date / time","navigation","stopwatch marker","timer location","stopwatch location"]},{"name":"mdi:timer-marker-outline","tags":["date / time","navigation","stopwatch marker outline","timer location outline","stopwatch location outline"]},{"name":"mdi:timer-minus","tags":["date / time","timer subtract","stopwatch minus","stopwatch subtract"]},{"name":"mdi:timer-minus-outline","tags":["date / time","timer subtract outline","stopwatch minus outline","stopwatch subtract outline"]},{"name":"mdi:timer-music","tags":["date / time","music","stopwatch music"]},{"name":"mdi:timer-music-outline","tags":["date / time","music","stopwatch music outline"]},{"name":"mdi:timer-off","tags":["date / time","stopwatch off"]},{"name":"mdi:timer-pause","tags":["date / time","stopwatch pause"]},{"name":"mdi:timer-pause-outline","tags":["date / time","stopwatch pause outline"]},{"name":"mdi:timer-play","tags":["date / time","timer start","stopwatch play","stopwatch start"]},{"name":"mdi:timer-play-outline","tags":["date / time","timer start outline","stopwatch play outline","stopwatch start outline"]},{"name":"mdi:timer-plus","tags":["date / time","timer add","stopwatch plus","stopwatch add"]},{"name":"mdi:timer-plus-outline","tags":["date / time","timer add outline","stopwatch plus outline","stopwatch add outline"]},{"name":"mdi:timer-refresh","tags":["date / time","stopwatch refresh"]},{"name":"mdi:timer-refresh-outline","tags":["date / time","stopwatch refresh outline"]},{"name":"mdi:timer-remove","tags":["date / time","stopwatch remove"]},{"name":"mdi:timer-remove-outline","tags":["date / time","stopwatch remove outline"]},{"name":"mdi:timer-sand","tags":["date / time","hourglass"]},{"name":"mdi:timer-sand-complete","tags":["date / time","hourglass complete"]},{"name":"mdi:timer-sand-paused","tags":["date / time","hourglass paused"]},{"name":"mdi:timer-settings","tags":["date / time","settings"]},{"name":"mdi:timer-settings-outline","tags":["date / time","settings"]},{"name":"mdi:timer-star","tags":["date / time","timer favorite","stopwatch star","stopwatch favorite"]},{"name":"mdi:timer-star-outline","tags":["date / time","timer favorite outline","stopwatch star outline","stopwatch favorite outline"]},{"name":"mdi:timer-stop","tags":["date / time","stopwatch stop"]},{"name":"mdi:timer-stop-outline","tags":["date / time","stopwatch stop outline"]},{"name":"mdi:timer-sync","tags":["date / time","stopwatch sync"]},{"name":"mdi:timer-sync-outline","tags":["date / time","stopwatch sync outline"]},{"name":"mdi:timetable","tags":["date / time"]},{"name":"mdi:tire","tags":["automotive","agriculture","tyre","wheel"]},{"name":"mdi:toaster","tags":["home automation"]},{"name":"mdi:toaster-off","tags":["home automation"]},{"name":"mdi:toaster-oven","tags":["home automation","food / drink"]},{"name":"mdi:toggle-switch-variant","tags":["home automation","light switch on"]},{"name":"mdi:toggle-switch-variant-off","tags":["home automation","light switch off","rocker switch off"]},{"name":"mdi:toilet","tags":["home automation","bathroom","lavatory","bidet"]},{"name":"mdi:tools","tags":["hardware / tools","wrench","screwdriver"]},{"name":"mdi:tooltip","tags":["tooltip"]},{"name":"mdi:tooltip-cellphone","tags":["cellphone / phone","tooltip","cellphone location","cellphone gps","find my phone"]},{"name":"mdi:tooltip-check","tags":["tooltip"]},{"name":"mdi:tooltip-check-outline","tags":["tooltip"]},{"name":"mdi:tooltip-edit","tags":["tooltip","edit / modify"]},{"name":"mdi:tooltip-edit-outline","tags":["edit / modify","tooltip"]},{"name":"mdi:tooltip-image","tags":["tooltip"]},{"name":"mdi:tooltip-image-outline","tags":["tooltip"]},{"name":"mdi:tooltip-minus","tags":["tooltip"]},{"name":"mdi:tooltip-minus-outline","tags":["tooltip"]},{"name":"mdi:tooltip-outline","tags":["tooltip"]},{"name":"mdi:tooltip-plus","tags":["tooltip","tooltip add"]},{"name":"mdi:tooltip-plus-outline","tags":["tooltip","tooltip outline plus","tooltip add outline"]},{"name":"mdi:tooltip-question","tags":["tooltip","tooltip help"]},{"name":"mdi:tooltip-question-outline","tags":["tooltip","tooltip help outline"]},{"name":"mdi:tooltip-remove","tags":["tooltip"]},{"name":"mdi:tooltip-remove-outline","tags":["tooltip"]},{"name":"mdi:tooltip-text","tags":["tooltip"]},{"name":"mdi:tooltip-text-outline","tags":["tooltip"]},{"name":"mdi:tooth","tags":["medical / hospital","dentist"]},{"name":"mdi:tooth-outline","tags":["medical / hospital"]},{"name":"mdi:toothbrush","tags":["medical / hospital","dentist","oral hygiene"]},{"name":"mdi:toothbrush-electric","tags":["medical / hospital","dentist","oral hygiene"]},{"name":"mdi:toothbrush-paste","tags":["medical / hospital","dentist","oral hygiene"]},{"name":"mdi:torch","tags":["sport","olympics"]},{"name":"mdi:tortoise","tags":["animal","turtle","reptile"]},{"name":"mdi:toslink","tags":["audio","optical audio"]},{"name":"mdi:touch-text-outline","tags":[]},{"name":"mdi:tournament","tags":["gaming / rpg","sport","bracket"]},{"name":"mdi:tower-beach","tags":[]},{"name":"mdi:tower-fire","tags":[]},{"name":"mdi:town-hall","tags":["places","school"]},{"name":"mdi:toy-brick","tags":["lego","plugin","extension"]},{"name":"mdi:toy-brick-marker","tags":["navigation","lego","plugin","extension","lego location","toy brick location"]},{"name":"mdi:toy-brick-marker-outline","tags":["navigation","extension outline","lego location outline","toy brick location outline","plugin outline","lego outline"]},{"name":"mdi:toy-brick-minus","tags":["lego","plugin","extension"]},{"name":"mdi:toy-brick-minus-outline","tags":["lego","plugin","extension"]},{"name":"mdi:toy-brick-outline","tags":["lego","plugin","extension"]},{"name":"mdi:toy-brick-plus","tags":["lego","plugin","extension"]},{"name":"mdi:toy-brick-plus-outline","tags":["lego","plugin","extension"]},{"name":"mdi:toy-brick-remove","tags":["lego","plugin","extension"]},{"name":"mdi:toy-brick-remove-outline","tags":["lego","plugin","extension"]},{"name":"mdi:toy-brick-search","tags":["lego","plugin","extension"]},{"name":"mdi:toy-brick-search-outline","tags":["lego","plugin","extension"]},{"name":"mdi:track-light","tags":["home automation"]},{"name":"mdi:track-light-off","tags":[]},{"name":"mdi:trackpad","tags":[]},{"name":"mdi:trackpad-lock","tags":["lock"]},{"name":"mdi:tractor","tags":["agriculture","transportation + road","farm"]},{"name":"mdi:trademark","tags":["tm"]},{"name":"mdi:traffic-cone","tags":["transportation + road"]},{"name":"mdi:train-car-autorack","tags":["transportation + other"]},{"name":"mdi:train-car-box","tags":["transportation + other"]},{"name":"mdi:train-car-box-full","tags":["transportation + other"]},{"name":"mdi:train-car-box-open","tags":["transportation + other"]},{"name":"mdi:train-car-caboose","tags":["transportation + other"]},{"name":"mdi:train-car-centerbeam","tags":["transportation + other"]},{"name":"mdi:train-car-centerbeam-full","tags":["transportation + other"]},{"name":"mdi:train-car-container","tags":["transportation + other"]},{"name":"mdi:train-car-flatbed","tags":["transportation + other"]},{"name":"mdi:train-car-flatbed-car","tags":["transportation + other"]},{"name":"mdi:train-car-flatbed-tank","tags":["transportation + other"]},{"name":"mdi:train-car-gondola","tags":["transportation + other"]},{"name":"mdi:train-car-gondola-full","tags":["transportation + other"]},{"name":"mdi:train-car-hopper","tags":["transportation + other"]},{"name":"mdi:train-car-hopper-covered","tags":["transportation + other"]},{"name":"mdi:train-car-hopper-full","tags":["transportation + other"]},{"name":"mdi:train-car-intermodal","tags":["transportation + other"]},{"name":"mdi:train-car-passenger","tags":["transportation + other"]},{"name":"mdi:train-car-passenger-door","tags":["transportation + other"]},{"name":"mdi:train-car-passenger-door-open","tags":["transportation + other"]},{"name":"mdi:train-car-passenger-variant","tags":["transportation + other"]},{"name":"mdi:train-car-tank","tags":["transportation + other"]},{"name":"mdi:transcribe","tags":[]},{"name":"mdi:transcribe-close","tags":[]},{"name":"mdi:transfer","tags":[]},{"name":"mdi:transfer-down","tags":["arrow"]},{"name":"mdi:transfer-left","tags":["arrow"]},{"name":"mdi:transfer-right","tags":["arrow"]},{"name":"mdi:transfer-up","tags":["arrow"]},{"name":"mdi:transit-connection","tags":["transportation + other","navigation"]},{"name":"mdi:transit-connection-horizontal","tags":["transportation + other"]},{"name":"mdi:transit-connection-variant","tags":["transportation + other","navigation"]},{"name":"mdi:transit-detour","tags":["transportation + other","navigation"]},{"name":"mdi:transit-skip","tags":["transportation + other"]},{"name":"mdi:translate-off","tags":[]},{"name":"mdi:translate-variant","tags":["developer / languages","spoken language"]},{"name":"mdi:transmission-tower","tags":["home automation","pylon","powerline","electricity","energy","power","grid"]},{"name":"mdi:transmission-tower-export","tags":["home automation","power from grid","energy from grid","electricity from grid"]},{"name":"mdi:transmission-tower-import","tags":["home automation","power to grid","energy to grid","electricity to grid","return to grid"]},{"name":"mdi:transmission-tower-off","tags":["home automation","powerline off","pylon off","grid off"]},{"name":"mdi:trash-can","tags":["delete","rubbish bin","trashcan","garbage can"]},{"name":"mdi:trash-can-outline","tags":["delete outline","rubbish bin outline","trashcan outline","garbage can outline"]},{"name":"mdi:tray","tags":["queue","printer","inbox"]},{"name":"mdi:tray-alert","tags":["alert / error","queue","printer","inbox"]},{"name":"mdi:tray-arrow-down","tags":["arrow","tray download"]},{"name":"mdi:tray-arrow-up","tags":["arrow","tray upload"]},{"name":"mdi:tray-full","tags":["queue","printer","inbox"]},{"name":"mdi:tray-minus","tags":["queue","printer","inbox"]},{"name":"mdi:tray-plus","tags":["queue","printer","inbox"]},{"name":"mdi:tray-remove","tags":["queue","printer","inbox"]},{"name":"mdi:treasure-chest","tags":["gaming / rpg","shopping","lock","jewelry box","jewel case"]},{"name":"mdi:treasure-chest-outline","tags":["gaming / rpg","lock","shopping","jewel case outline","jewelry box outline"]},{"name":"mdi:tree","tags":["nature","agriculture","plant"]},{"name":"mdi:tree-outline","tags":["nature","agriculture","plant"]},{"name":"mdi:triangle","tags":["shape"]},{"name":"mdi:triangle-down","tags":["shape"]},{"name":"mdi:triangle-down-outline","tags":["shape"]},{"name":"mdi:triangle-outline","tags":["shape"]},{"name":"mdi:triangle-small-down","tags":["shape","trending down variant"]},{"name":"mdi:triangle-small-up","tags":["shape","trending up variant"]},{"name":"mdi:triangle-wave","tags":["audio"]},{"name":"mdi:triforce","tags":["gaming / rpg","zelda"]},{"name":"mdi:trophy","tags":["sport","achievement"]},{"name":"mdi:trophy-award","tags":["sport","achievement award"]},{"name":"mdi:trophy-broken","tags":["sport"]},{"name":"mdi:trophy-outline","tags":["sport","achievement outline"]},{"name":"mdi:trophy-variant","tags":["sport","achievement variant"]},{"name":"mdi:trophy-variant-outline","tags":["sport","achievement variant outline"]},{"name":"mdi:truck-alert","tags":["transportation + road","alert / error","truck error"]},{"name":"mdi:truck-alert-outline","tags":["transportation + road","alert / error","truck error outline"]},{"name":"mdi:truck-cargo-container","tags":["transportation + road","truck shipping"]},{"name":"mdi:truck-check","tags":["transportation + road","truck tick","lorry check","courier check"]},{"name":"mdi:truck-check-outline","tags":["transportation + road"]},{"name":"mdi:truck-delivery","tags":["transportation + road","lorry delivery"]},{"name":"mdi:truck-delivery-outline","tags":["transportation + road"]},{"name":"mdi:truck-fast","tags":["transportation + road","lorry fast","courier fast"]},{"name":"mdi:truck-fast-outline","tags":["transportation + road"]},{"name":"mdi:truck-flatbed","tags":["automotive","transportation + road","truck flatbed tow"]},{"name":"mdi:truck-minus","tags":["transportation + road","truck subtract"]},{"name":"mdi:truck-minus-outline","tags":["transportation + road","truck subtract outline"]},{"name":"mdi:truck-plus","tags":["transportation + road","medical / hospital","truck add"]},{"name":"mdi:truck-plus-outline","tags":["transportation + road","medical / hospital","truck add outline"]},{"name":"mdi:truck-remove","tags":["transportation + road"]},{"name":"mdi:truck-remove-outline","tags":["transportation + road"]},{"name":"mdi:truck-snowflake","tags":["transportation + road","truck refrigerator","truck freezer"]},{"name":"mdi:truck-trailer","tags":["transportation + road"]},{"name":"mdi:trumpet","tags":["music"]},{"name":"mdi:tshirt-crew","tags":["clothing","t shirt crew"]},{"name":"mdi:tshirt-crew-outline","tags":["clothing","t shirt crew outline"]},{"name":"mdi:tshirt-v","tags":["clothing","t shirt v"]},{"name":"mdi:tshirt-v-outline","tags":["clothing","t shirt v outline"]},{"name":"mdi:tumble-dryer","tags":["home automation","laundry room"]},{"name":"mdi:tumble-dryer-alert","tags":["home automation","alert / error","laundry room alert"]},{"name":"mdi:tumble-dryer-off","tags":["home automation","laundry room off"]},{"name":"mdi:tune-variant","tags":["audio","settings","settings","equalizer"]},{"name":"mdi:tune-vertical-variant","tags":["audio","settings","settings vertical","equalizer vertical"]},{"name":"mdi:tunnel","tags":["transportation + road","transportation + other"]},{"name":"mdi:tunnel-outline","tags":["transportation + road","transportation + other"]},{"name":"mdi:turbine","tags":["transportation + flying","jet engine","wind turbine"]},{"name":"mdi:turkey","tags":["animal","holiday","agriculture","thanksgiving"]},{"name":"mdi:turnstile","tags":[]},{"name":"mdi:turnstile-outline","tags":[]},{"name":"mdi:turtle","tags":["animal","reptile"]},{"name":"mdi:two-factor-authentication","tags":[]},{"name":"mdi:typewriter","tags":[]},{"name":"mdi:ufo","tags":["unidentified flying object","alien"]},{"name":"mdi:ufo-outline","tags":["unidentified flying object outline","alien"]},{"name":"mdi:ultra-high-definition","tags":["video / movie","uhd"]},{"name":"mdi:umbrella","tags":["weather"]},{"name":"mdi:umbrella-closed","tags":["weather"]},{"name":"mdi:umbrella-closed-outline","tags":["weather"]},{"name":"mdi:umbrella-closed-variant","tags":["weather"]},{"name":"mdi:umbrella-outline","tags":["weather"]},{"name":"mdi:undo-variant","tags":["arrow"]},{"name":"mdi:unfold-less-vertical","tags":["chevron right left","collapse vertical"]},{"name":"mdi:unfold-more-vertical","tags":["chevron left right","expand vertical"]},{"name":"mdi:ungroup","tags":[]},{"name":"mdi:unicorn","tags":["animal","fantasy"]},{"name":"mdi:unicorn-variant","tags":["animal","fantasy variant"]},{"name":"mdi:unicycle","tags":["sport","transportation + other"]},{"name":"mdi:upload-lock","tags":["lock"]},{"name":"mdi:upload-lock-outline","tags":["lock"]},{"name":"mdi:upload-multiple","tags":["uploads"]},{"name":"mdi:upload-network","tags":[]},{"name":"mdi:upload-network-outline","tags":[]},{"name":"mdi:upload-off","tags":[]},{"name":"mdi:upload-off-outline","tags":[]},{"name":"mdi:upload-outline","tags":["file upload outline"]},{"name":"mdi:usb-flash-drive","tags":[]},{"name":"mdi:usb-flash-drive-outline","tags":[]},{"name":"mdi:usb-port","tags":[]},{"name":"mdi:valve","tags":["home automation"]},{"name":"mdi:valve-closed","tags":["home automation"]},{"name":"mdi:valve-open","tags":["home automation"]},{"name":"mdi:van-passenger","tags":["transportation + road"]},{"name":"mdi:van-utility","tags":["transportation + road","van candy"]},{"name":"mdi:vanish","tags":[]},{"name":"mdi:vanish-quarter","tags":[]},{"name":"mdi:vanity-light","tags":["home automation"]},{"name":"mdi:variable","tags":["developer / languages","math"]},{"name":"mdi:variable-box","tags":["developer / languages"]},{"name":"mdi:vector-arrange-above","tags":["vector","arrange","geographic information system"]},{"name":"mdi:vector-arrange-below","tags":["vector","arrange","geographic information system"]},{"name":"mdi:vector-bezier","tags":["vector"]},{"name":"mdi:vector-circle","tags":["vector","geographic information system"]},{"name":"mdi:vector-circle-variant","tags":["vector"]},{"name":"mdi:vector-combine","tags":["vector","geographic information system"]},{"name":"mdi:vector-curve","tags":["vector","geographic information system","bezier"]},{"name":"mdi:vector-difference","tags":["vector","geographic information system"]},{"name":"mdi:vector-difference-ab","tags":["vector","geographic information system"]},{"name":"mdi:vector-difference-ba","tags":["vector","geographic information system"]},{"name":"mdi:vector-ellipse","tags":["vector","geographic information system"]},{"name":"mdi:vector-intersection","tags":["vector","geographic information system"]},{"name":"mdi:vector-line","tags":["vector","geographic information system"]},{"name":"mdi:vector-link","tags":["vector","geographic information system"]},{"name":"mdi:vector-point","tags":["vector"]},{"name":"mdi:vector-point-edit","tags":["vector","edit / modify"]},{"name":"mdi:vector-point-minus","tags":["vector"]},{"name":"mdi:vector-point-plus","tags":["vector","vector point add"]},{"name":"mdi:vector-point-select","tags":["vector","geographic information system"]},{"name":"mdi:vector-polygon","tags":["vector","geographic information system"]},{"name":"mdi:vector-polygon-variant","tags":["vector"]},{"name":"mdi:vector-polyline","tags":["vector","geographic information system"]},{"name":"mdi:vector-polyline-edit","tags":["edit / modify"]},{"name":"mdi:vector-polyline-minus","tags":[]},{"name":"mdi:vector-polyline-plus","tags":[]},{"name":"mdi:vector-polyline-remove","tags":[]},{"name":"mdi:vector-radius","tags":["vector","geographic information system"]},{"name":"mdi:vector-rectangle","tags":["vector","geographic information system"]},{"name":"mdi:vector-selection","tags":["vector","geographic information system"]},{"name":"mdi:vector-square","tags":["vector","geographic information system","mdi"]},{"name":"mdi:vector-square-close","tags":["vector"]},{"name":"mdi:vector-square-edit","tags":["vector","edit / modify"]},{"name":"mdi:vector-square-minus","tags":["vector","vector square subtract"]},{"name":"mdi:vector-square-open","tags":["vector"]},{"name":"mdi:vector-square-plus","tags":["vector","vector square add"]},{"name":"mdi:vector-square-remove","tags":["vector","vector square delete"]},{"name":"mdi:vector-triangle","tags":["vector","geographic information system"]},{"name":"mdi:vector-union","tags":["vector","geographic information system"]},{"name":"mdi:vhs","tags":["video / movie","video home system","vhs cassette","vhs tape"]},{"name":"mdi:vibrate-off","tags":[]},{"name":"mdi:video-2d","tags":["video / movie"]},{"name":"mdi:video-3d","tags":["video / movie"]},{"name":"mdi:video-3d-off","tags":["video / movie"]},{"name":"mdi:video-3d-variant","tags":["video / movie"]},{"name":"mdi:video-check","tags":["video / movie"]},{"name":"mdi:video-check-outline","tags":["video / movie"]},{"name":"mdi:video-high-definition","tags":["video / movie"]},{"name":"mdi:video-input-scart","tags":["video / movie"]},{"name":"mdi:video-marker","tags":["video / movie","navigation","video location"]},{"name":"mdi:video-marker-outline","tags":["video / movie","navigation","video location outline"]},{"name":"mdi:video-minus-outline","tags":["video / movie"]},{"name":"mdi:video-plus-outline","tags":["video / movie"]},{"name":"mdi:video-vintage","tags":["video / movie","video film","video classic"]},{"name":"mdi:video-wireless","tags":["video / movie"]},{"name":"mdi:video-wireless-outline","tags":["video / movie"]},{"name":"mdi:view-dashboard-edit","tags":["view","edit / modify"]},{"name":"mdi:view-dashboard-edit-outline","tags":["view","edit / modify"]},{"name":"mdi:view-dashboard-variant-outline","tags":["view"]},{"name":"mdi:view-gallery","tags":["view"]},{"name":"mdi:view-gallery-outline","tags":["view"]},{"name":"mdi:view-grid","tags":["view"]},{"name":"mdi:view-grid-compact","tags":[]},{"name":"mdi:view-grid-outline","tags":["view"]},{"name":"mdi:view-grid-plus-outline","tags":["view"]},{"name":"mdi:view-parallel","tags":["view"]},{"name":"mdi:view-parallel-outline","tags":["view"]},{"name":"mdi:view-sequential","tags":["view"]},{"name":"mdi:view-sequential-outline","tags":["view"]},{"name":"mdi:virtual-reality","tags":["vr"]},{"name":"mdi:virus","tags":["science","medical / hospital"]},{"name":"mdi:virus-off","tags":["science"]},{"name":"mdi:virus-off-outline","tags":["science"]},{"name":"mdi:virus-outline","tags":["science","medical / hospital"]},{"name":"mdi:volume-equal","tags":["audio"]},{"name":"mdi:volume-minus","tags":["audio","home automation","cellphone / phone","volume decrease"]},{"name":"mdi:volume-mute","tags":["audio","cellphone / phone"]},{"name":"mdi:volume-plus","tags":["audio","home automation","cellphone / phone","volume increase"]},{"name":"mdi:volume-variant-off","tags":["audio","cellphone / phone"]},{"name":"mdi:volume-vibrate","tags":["cellphone / phone","audio"]},{"name":"mdi:vpn","tags":["virtual private network"]},{"name":"mdi:wall","tags":["bricks"]},{"name":"mdi:wall-fire","tags":["device / tech","firewall"]},{"name":"mdi:wall-sconce","tags":["home automation"]},{"name":"mdi:wall-sconce-flat","tags":["home automation","ceiling light flat","pot light flat"]},{"name":"mdi:wall-sconce-flat-outline","tags":["home automation"]},{"name":"mdi:wall-sconce-flat-variant","tags":["home automation","pot light flat variant"]},{"name":"mdi:wall-sconce-flat-variant-outline","tags":["home automation"]},{"name":"mdi:wall-sconce-outline","tags":["home automation"]},{"name":"mdi:wall-sconce-round","tags":["home automation","pot light round"]},{"name":"mdi:wall-sconce-round-outline","tags":["home automation"]},{"name":"mdi:wall-sconce-round-variant","tags":["home automation","pot light round variant"]},{"name":"mdi:wall-sconce-round-variant-outline","tags":["home automation"]},{"name":"mdi:wallet-bifold","tags":["currency","banking"]},{"name":"mdi:wallet-bifold-outline","tags":["banking","currency"]},{"name":"mdi:wallet-plus","tags":["banking","wallet add"]},{"name":"mdi:wallet-plus-outline","tags":["banking","wallet add outline"]},{"name":"mdi:wan","tags":["wide area network"]},{"name":"mdi:wardrobe","tags":["home automation","closet"]},{"name":"mdi:wardrobe-outline","tags":["home automation","closet outline"]},{"name":"mdi:warehouse","tags":["places"]},{"name":"mdi:washing-machine-alert","tags":["home automation","alert / error","laundry room alert"]},{"name":"mdi:washing-machine-off","tags":["home automation","laundry room off"]},{"name":"mdi:watch-export","tags":["device / tech"]},{"name":"mdi:watch-export-variant","tags":["device / tech"]},{"name":"mdi:watch-import","tags":["device / tech"]},{"name":"mdi:watch-import-variant","tags":["device / tech"]},{"name":"mdi:watch-variant","tags":["device / tech"]},{"name":"mdi:watch-vibrate","tags":["device / tech"]},{"name":"mdi:watch-vibrate-off","tags":["device / tech"]},{"name":"mdi:water-alert","tags":["alert / error","agriculture","drop alert","blood alert","ink alert"]},{"name":"mdi:water-alert-outline","tags":["alert / error","agriculture","drop alert outline","blood alert outline","ink alert outline"]},{"name":"mdi:water-boiler","tags":["home automation","water heater","gas water boiler","electric water boiler","gas water heater","electric water heater"]},{"name":"mdi:water-boiler-alert","tags":["home automation","alert / error","water heater alert","water boiler error","water heater error"]},{"name":"mdi:water-boiler-auto","tags":["home automation","water heater auto"]},{"name":"mdi:water-boiler-off","tags":["home automation","water heater off"]},{"name":"mdi:water-check","tags":["drop check","blood check","ink check"]},{"name":"mdi:water-check-outline","tags":["drop check outline","blood check outline","ink check outline"]},{"name":"mdi:water-circle","tags":["home automation","drop circle","blood circle","ink circle"]},{"name":"mdi:water-minus","tags":["drop minus","blood minus","ink minus"]},{"name":"mdi:water-minus-outline","tags":["drop minus outline","blood minus outline","ink minus outline"]},{"name":"mdi:water-off-outline","tags":["drop off outline","blood off outline","trans fat off outline","ink off outline"]},{"name":"mdi:water-opacity","tags":["home automation","drawing / art","weather","water transparent","water saver","blood saver","blood transparent","oil saver","oil transparent","drop transparent","drop saver"]},{"name":"mdi:water-outline","tags":["home automation","weather","drop outline","blood outline","water drop outline","ink outline"]},{"name":"mdi:water-percent","tags":["weather","home automation","nature","humidity","ink percent"]},{"name":"mdi:water-percent-alert","tags":["alert / error","nature","humidity alert","ink percent alert"]},{"name":"mdi:water-plus","tags":["drop plus","blood plus","ink plus"]},{"name":"mdi:water-plus-outline","tags":["drop plus outline","blood plus outline","ink plus outline"]},{"name":"mdi:water-polo","tags":["sport"]},{"name":"mdi:water-pump","tags":["agriculture","home automation","tap","kitchen tap","faucet"]},{"name":"mdi:water-pump-off","tags":["agriculture","home automation","tap off","kitchen tap off","faucet off"]},{"name":"mdi:water-remove","tags":["drop remove","blood remove","ink remove"]},{"name":"mdi:water-remove-outline","tags":["drop remove outline","blood remove outline","ink remove outline"]},{"name":"mdi:water-sync","tags":["agriculture","water recycle","water reuse"]},{"name":"mdi:water-thermometer","tags":["weather","home automation","boil point","water temperature","dew point"]},{"name":"mdi:water-thermometer-outline","tags":["weather","home automation","dew point outline","water temperature outline","boil point outline"]},{"name":"mdi:water-well","tags":[]},{"name":"mdi:water-well-outline","tags":[]},{"name":"mdi:waterfall","tags":["home automation","nature"]},{"name":"mdi:watering-can","tags":["agriculture","watering pot"]},{"name":"mdi:watering-can-outline","tags":["agriculture","watering pot outline"]},{"name":"mdi:wave","tags":["transportation + water","water"]},{"name":"mdi:waveform","tags":["audio"]},{"name":"mdi:waves","tags":["weather","transportation + water","agriculture","ocean","lake","flood","water"]},{"name":"mdi:waves-arrow-left","tags":["nature","weather","tide in","water flow"]},{"name":"mdi:waves-arrow-right","tags":["nature","weather","tide out","water flow"]},{"name":"mdi:waves-arrow-up","tags":["nature","weather","water evaporation","humidity","sea level rise","ocean level rise","climate change"]},{"name":"mdi:weather-cloudy","tags":["weather","cloud","agriculture"]},{"name":"mdi:weather-cloudy-alert","tags":["weather","alert / error","cloud"]},{"name":"mdi:weather-cloudy-arrow-right","tags":["weather","cloud"]},{"name":"mdi:weather-cloudy-clock","tags":["weather","cloud","weather history","weather time","weather date"]},{"name":"mdi:weather-dust","tags":["weather","agriculture","dust storm","windy"]},{"name":"mdi:weather-fog","tags":["weather","agriculture","weather mist"]},{"name":"mdi:weather-hail","tags":["weather","agriculture"]},{"name":"mdi:weather-hazy","tags":["weather","agriculture"]},{"name":"mdi:weather-hurricane","tags":["weather","nature","agriculture","cyclone"]},{"name":"mdi:weather-hurricane-outline","tags":["weather","nature","agriculture","cyclone outline"]},{"name":"mdi:weather-lightning","tags":["weather","agriculture","weather storm","weather thunder","weather flash"]},{"name":"mdi:weather-lightning-rainy","tags":["weather","weather thunder rainy","weather storm"]},{"name":"mdi:weather-night","tags":["weather","holiday","moon and stars","night sky"]},{"name":"mdi:weather-night-partly-cloudy","tags":["weather","cloud"]},{"name":"mdi:weather-partly-cloudy","tags":["weather","cloud","weather partlycloudy"]},{"name":"mdi:weather-partly-lightning","tags":["weather"]},{"name":"mdi:weather-partly-rainy","tags":["weather"]},{"name":"mdi:weather-partly-snowy","tags":["weather"]},{"name":"mdi:weather-partly-snowy-rainy","tags":["weather"]},{"name":"mdi:weather-pouring","tags":["weather","agriculture","weather heavy rain"]},{"name":"mdi:weather-rainy","tags":["weather","agriculture","weather drizzle","weather spitting"]},{"name":"mdi:weather-snowy","tags":["weather"]},{"name":"mdi:weather-snowy-heavy","tags":["weather","flurries"]},{"name":"mdi:weather-snowy-rainy","tags":["weather","weather sleet"]},{"name":"mdi:weather-sunny","tags":["weather"]},{"name":"mdi:weather-sunny-alert","tags":["weather","alert / error","home automation","heat alert","heat advisory","sun advisory"]},{"name":"mdi:weather-sunny-off","tags":["weather"]},{"name":"mdi:weather-sunset","tags":["weather"]},{"name":"mdi:weather-sunset-down","tags":["weather"]},{"name":"mdi:weather-sunset-up","tags":["weather","sunrise"]},{"name":"mdi:weather-tornado","tags":["weather"]},{"name":"mdi:weather-windy","tags":["weather"]},{"name":"mdi:weather-windy-variant","tags":["weather"]},{"name":"mdi:web-box","tags":["geographic information system","language box","globe box","internet box"]},{"name":"mdi:web-cancel","tags":[]},{"name":"mdi:web-check","tags":[]},{"name":"mdi:web-clock","tags":["date / time"]},{"name":"mdi:web-minus","tags":[]},{"name":"mdi:web-off","tags":[]},{"name":"mdi:web-plus","tags":[]},{"name":"mdi:web-refresh","tags":[]},{"name":"mdi:web-remove","tags":[]},{"name":"mdi:web-sync","tags":[]},{"name":"mdi:webcam","tags":["video / movie","home automation","web camera"]},{"name":"mdi:webcam-off","tags":[]},{"name":"mdi:webhook","tags":[]},{"name":"mdi:weight","tags":[]},{"name":"mdi:weight-gram","tags":[]},{"name":"mdi:weight-kilogram","tags":["weight kg"]},{"name":"mdi:weight-lifter","tags":["sport","people / family","crossfit","gym","fitness center","human barbell"]},{"name":"mdi:weight-pound","tags":["weight lb"]},{"name":"mdi:wheel-barrow","tags":["hardware / tools"]},{"name":"mdi:wheelchair","tags":["medical / hospital","people / family","accessible","isa","international symbol of access"]},{"name":"mdi:wheelchair-accessibility","tags":["medical / hospital","accessible"]},{"name":"mdi:whistle","tags":["sport"]},{"name":"mdi:whistle-outline","tags":["sport"]},{"name":"mdi:wifi","tags":[]},{"name":"mdi:wifi-alert","tags":["alert / error"]},{"name":"mdi:wifi-arrow-down","tags":[]},{"name":"mdi:wifi-arrow-left","tags":[]},{"name":"mdi:wifi-arrow-left-right","tags":[]},{"name":"mdi:wifi-arrow-right","tags":[]},{"name":"mdi:wifi-arrow-up","tags":[]},{"name":"mdi:wifi-arrow-up-down","tags":[]},{"name":"mdi:wifi-cancel","tags":[]},{"name":"mdi:wifi-check","tags":[]},{"name":"mdi:wifi-cog","tags":["settings"]},{"name":"mdi:wifi-lock","tags":["lock"]},{"name":"mdi:wifi-lock-open","tags":["lock"]},{"name":"mdi:wifi-marker","tags":["navigation","wifi location"]},{"name":"mdi:wifi-minus","tags":[]},{"name":"mdi:wifi-off","tags":[]},{"name":"mdi:wifi-plus","tags":[]},{"name":"mdi:wifi-refresh","tags":[]},{"name":"mdi:wifi-remove","tags":[]},{"name":"mdi:wifi-settings","tags":["settings"]},{"name":"mdi:wifi-star","tags":["wifi favourite","network favourite","wifi favorite","network favorite"]},{"name":"mdi:wifi-strength-1","tags":[]},{"name":"mdi:wifi-strength-1-alert","tags":["alert / error","wifi strength 1 warning"]},{"name":"mdi:wifi-strength-1-lock","tags":["lock"]},{"name":"mdi:wifi-strength-1-lock-open","tags":["lock"]},{"name":"mdi:wifi-strength-2","tags":[]},{"name":"mdi:wifi-strength-2-alert","tags":["alert / error","wifi strength 2 warning"]},{"name":"mdi:wifi-strength-2-lock","tags":["lock"]},{"name":"mdi:wifi-strength-2-lock-open","tags":["lock"]},{"name":"mdi:wifi-strength-3","tags":[]},{"name":"mdi:wifi-strength-3-alert","tags":["alert / error","wifi strength 3 warning"]},{"name":"mdi:wifi-strength-3-lock","tags":["lock"]},{"name":"mdi:wifi-strength-3-lock-open","tags":["lock"]},{"name":"mdi:wifi-strength-4","tags":[]},{"name":"mdi:wifi-strength-4-alert","tags":["alert / error","wifi strength 4 warning"]},{"name":"mdi:wifi-strength-4-lock","tags":["lock"]},{"name":"mdi:wifi-strength-4-lock-open","tags":["lock"]},{"name":"mdi:wifi-strength-alert-outline","tags":["alert / error","wifi strength warning outline","wifi strength 0 alert","wifi strength 0 warning"]},{"name":"mdi:wifi-strength-lock-open-outline","tags":["lock"]},{"name":"mdi:wifi-strength-lock-outline","tags":["lock","wifi strength 0 lock"]},{"name":"mdi:wifi-strength-off","tags":[]},{"name":"mdi:wifi-strength-off-outline","tags":[]},{"name":"mdi:wifi-strength-outline","tags":["wifi strength 0"]},{"name":"mdi:wifi-sync","tags":[]},{"name":"mdi:wind-turbine-alert","tags":["home automation","alert / error","wind power alert","wind turbine warning"]},{"name":"mdi:wind-turbine-check","tags":["home automation","wind power check","wind turbine success","wind power success"]},{"name":"mdi:window-close","tags":["cancel","close"]},{"name":"mdi:window-closed","tags":["home automation"]},{"name":"mdi:window-closed-variant","tags":["home automation"]},{"name":"mdi:window-maximize","tags":[]},{"name":"mdi:window-minimize","tags":[]},{"name":"mdi:window-open","tags":["home automation"]},{"name":"mdi:window-open-variant","tags":["home automation"]},{"name":"mdi:window-restore","tags":[]},{"name":"mdi:window-shutter","tags":["home automation"]},{"name":"mdi:window-shutter-alert","tags":["home automation","alert / error"]},{"name":"mdi:window-shutter-auto","tags":["home automation"]},{"name":"mdi:window-shutter-cog","tags":["home automation","settings","window shutter settings"]},{"name":"mdi:window-shutter-open","tags":["home automation"]},{"name":"mdi:window-shutter-settings","tags":["home automation","settings"]},{"name":"mdi:windsock","tags":["weather"]},{"name":"mdi:wiper","tags":[]},{"name":"mdi:wiper-wash","tags":["automotive","wiper fluid","washer fluid"]},{"name":"mdi:wiper-wash-alert","tags":["alert / error","automotive","wiper fluid alert","washer fluid alert","wiper fluid low","washer fluid low"]},{"name":"mdi:wizard-hat","tags":["clothing","gaming / rpg"]},{"name":"mdi:wrap","tags":[]},{"name":"mdi:wrap-disabled","tags":["unwrap"]},{"name":"mdi:wrench-check","tags":[]},{"name":"mdi:wrench-check-outline","tags":[]},{"name":"mdi:wrench-clock","tags":["date / time","hardware / tools","scheduled maintenance","wrench time","tool time","tool clock"]},{"name":"mdi:wrench-clock-outline","tags":["date / time"]},{"name":"mdi:wrench-cog","tags":["settings","wrench settings"]},{"name":"mdi:wrench-cog-outline","tags":["settings","wrench settings outline"]},{"name":"mdi:xml","tags":["developer / languages","code"]},{"name":"mdi:yeast","tags":[]},{"name":"mdi:yin-yang","tags":["taoism"]},{"name":"mdi:yoga","tags":["sport"]},{"name":"mdi:yurt","tags":[]},{"name":"mdi:zip-box-outline","tags":["files / folders","compressed file outline"]},{"name":"mdi:zip-disk","tags":[]},{"name":"mdi:zodiac-aquarius","tags":["horoscope aquarius"]},{"name":"mdi:zodiac-aries","tags":["horoscope aries"]},{"name":"mdi:zodiac-cancer","tags":["horoscope cancer"]},{"name":"mdi:zodiac-capricorn","tags":["horoscope capricorn"]},{"name":"mdi:zodiac-gemini","tags":["horoscope gemini"]},{"name":"mdi:zodiac-leo","tags":["horoscope leo"]},{"name":"mdi:zodiac-libra","tags":["horoscope libra"]},{"name":"mdi:zodiac-pisces","tags":["horoscope pisces"]},{"name":"mdi:zodiac-sagittarius","tags":["horoscope sagittarius"]},{"name":"mdi:zodiac-scorpio","tags":["horoscope scorpio"]},{"name":"mdi:zodiac-taurus","tags":["horoscope taurus"]},{"name":"mdi:zodiac-virgo","tags":["horoscope virgo"]}] \ No newline at end of file diff --git a/ui-ngx/src/assets/metadata/units.json b/ui-ngx/src/assets/metadata/units.json new file mode 100644 index 0000000000..53744cecb7 --- /dev/null +++ b/ui-ngx/src/assets/metadata/units.json @@ -0,0 +1,2023 @@ +[{ + "name": "unit.millimeter", + "symbol": "mm", + "tags": ["level","height","distance","length","width","gap","depth","millimeter","millimeters","rainfall","precipitation", + "displacement","position","movement","transition","mm"] +}, +{ + "name": "unit.centimeter", + "symbol": "cm", + "tags": ["level","height","distance","length","width","gap","depth","centimeter","centimeters","rainfall","precipitation", + "displacement","position","movement","transition","cm"] +}, +{ + "name": "unit.angstrom", + "symbol": "Å", + "tags": ["level","height","distance","length","width","gap","depth","atomic scale","atomic distance","nanoscale", + "angstrom","angstroms","Å"] +}, +{ + "name": "unit.nanometer", + "symbol": "nm", + "tags": ["level","height","distance","length","width","gap","depth","nanoscale","atomic scale","molecular scale", + "nanometer","nanometers","nm"] +}, +{ + "name": "unit.micrometer", + "symbol": "µm", + "tags": ["level","height","distance","length","width","gap","depth","microns","micrometer","micrometers","µm"] +}, +{ + "name": "unit.meter", + "symbol": "m", + "tags": ["level","height","distance","length","width","gap","depth","meter","meters","m"] +}, +{ + "name": "unit.kilometer", + "symbol": "km", + "tags": ["distance","height","length","width","gap","depth","kilometer","kilometers","km"] +}, +{ + "name": "unit.inch", + "symbol": "in", + "tags": ["level","height","distance","length","width","gap","depth","inch","inches","in"] +}, +{ + "name": "unit.foot", + "symbol": "ft", + "tags": ["level","height","distance","length","width","gap","depth","foot","feet","ft"] +}, +{ + "name": "unit.yard", + "symbol": "yd", + "tags": ["level","height","distance","length","width","gap","depth","yard","yards","yd"] +}, +{ + "name": "unit.mile", + "symbol": "mi", + "tags": ["level","height","distance","length","width","gap","depth","mile","miles","mi"] +}, +{ + "name": "unit.nautical-mile", + "symbol": "nm", + "tags": ["level","height","distance","length","width","gap","depth","nautical mile","nm"] +}, +{ + "name": "unit.astronomical-unit", + "symbol": "AU", + "tags": ["distance","celestial bodies","solar system","AU"] +}, +{ + "name": "unit.reciprocal-metre", + "symbol": "m⁻¹", + "tags": ["wavenumber","wave density","wave frequency","m⁻¹"] +}, +{ + "name": "unit.meter-per-meter", + "symbol": "m/m", + "tags": ["ratio of length to length","meter per meter","m/m"] +}, +{ + "name": "unit.steradian", + "symbol": "sr", + "tags": ["solid angle","spatial extent","steradian","sr"] +}, +{ + "name": "unit.thou", + "symbol": "thou", + "tags": ["length","measurement","thou"] +}, +{ + "name": "unit.barleycorn", + "symbol": "barleycorn", + "tags": ["length","shoe size","barleycorn"] +}, +{ + "name": "unit.hand", + "symbol": "hand", + "tags": ["length","horse measurement","hand"] +}, +{ + "name": "unit.chain", + "symbol": "ch", + "tags": ["length","land surveying","ch"] +}, +{ + "name": "unit.furlong", + "symbol": "fur", + "tags": ["length","land surveying","fur"] +}, +{ + "name": "unit.league", + "symbol": "league", + "tags": ["length","historical measurement","league"] +}, +{ + "name": "unit.fathom", + "symbol": "fathom", + "tags": ["depth","nautical measurement","fathom"] +}, +{ + "name": "unit.cable", + "symbol": "cable", + "tags": ["distance","nautical measurement","cable"] +}, +{ + "name": "unit.link", + "symbol": "link", + "tags": ["length","land surveying","link"] +}, +{ + "name": "unit.rod", + "symbol": "rod", + "tags": ["length","land surveying","rod"] +}, +{ + "name": "unit.nanogram", + "symbol": "ng", + "tags": ["mass","weight","heaviness","load","nanogram","nanograms","ng"] +}, +{ + "name": "unit.microgram", + "symbol": "μg", + "tags": ["mass","weight","heaviness","load","μg","microgram"] +}, +{ + "name": "unit.milligram", + "symbol": "mg", + "tags": ["mass","weight","heaviness","load","milligram","miligrams","mg"] +}, +{ + "name": "unit.gram", + "symbol": "g", + "tags": ["mass","weight","heaviness","load","gram","grams","g"] +}, +{ + "name": "unit.kilogram", + "symbol": "kg", + "tags": ["mass","weight","heaviness","load","kilogram","kilograms","kg"] +}, +{ + "name": "unit.tonne", + "symbol": "t", + "tags": ["mass","weight","heaviness","load","tonne","tons","t"] +}, +{ + "name": "unit.ounce", + "symbol": "oz", + "tags": ["mass","weight","heaviness","load","ounce","ounces","oz"] +}, +{ + "name": "unit.pound", + "symbol": "lb", + "tags": ["mass","weight","heaviness","load","pound","pounds","lb"] +}, +{ + "name": "unit.stone", + "symbol": "st", + "tags": ["mass","weight","heaviness","load","stone","stones","st"] +}, +{ + "name": "unit.hundredweight-count", + "symbol": "cwt", + "tags": ["mass","weight","heaviness","load","hundredweight count","cwt"] +}, +{ + "name": "unit.short-tons", + "symbol": "short tons", + "tags": ["mass","weight","heaviness","load","short ton","short tons"] +}, +{ + "name": "unit.dalton", + "symbol": "Da", + "tags": ["atomic mass unit","AMU","unified atomic mass unit","dalton","Da"] +}, +{ + "name": "unit.grain", + "symbol": "gr", + "tags": ["mass","measurement","grain","gr"] +}, +{ + "name": "unit.drachm", + "symbol": "dr", + "tags": ["mass","measurement","drachm","dr"] +}, +{ + "name": "unit.quarter", + "symbol": "qr", + "tags": ["mass","measurement","quarter","qr"] +}, +{ + "name": "unit.slug", + "symbol": "slug", + "tags": ["mass","measurement","slug"] +}, +{ + "name": "unit.carat", + "symbol": "ct", + "tags": ["gemstone","pearl","jewelry","carat","ct"] +}, +{ + "name": "unit.cubic-millimeter", + "symbol": "mm³", + "tags": ["volume","capacity","extent","cubic millimeter","mm³"] +}, +{ + "name": "unit.cubic-centimeter", + "symbol": "cm³", + "tags": ["volume","capacity","extent","cubic centimeter","cubic centimeters","cm³"] +}, +{ + "name": "unit.cubic-meter", + "symbol": "m³", + "tags": ["volume","capacity","extent","cubic meter","cubic meters","m³"] +}, +{ + "name": "unit.cubic-kilometer", + "symbol": "km³", + "tags": ["volume","capacity","extent","cubic kilometer","cubic kilometers","km³"] +}, +{ + "name": "unit.microliter", + "symbol": "µL", + "tags": ["volume","liquid measurement","microliter","µL"] +}, +{ + "name": "unit.milliliter", + "symbol": "mL", + "tags": ["volume","capacity","extent","milliliter","milliliters","mL"] +}, +{ + "name": "unit.liter", + "symbol": "L", + "tags": ["volume","capacity","extent","liter","liters","l"] +}, +{ + "name": "unit.hectoliter", + "symbol": "hl", + "tags": ["volume","capacity","extent","hectoliter","hectoliters","hl"] +}, +{ + "name": "unit.cubic-inch", + "symbol": "in³", + "tags": ["volume","capacity","extent","cubic inch","cubic inches","in³"] +}, +{ + "name": "unit.cubic-foot", + "symbol": "ft³", + "tags": ["volume","capacity","extent","cubic foot","cubic feet","ft³"] +}, +{ + "name": "unit.cubic-yard", + "symbol": "yd³", + "tags": ["volume","capacity","extent","cubic yard","cubic yards","yd³"] +}, +{ + "name": "unit.fluid-ounce", + "symbol": "fl-oz", + "tags": ["volume","capacity","extent","fluid ounce","fluid ounces","fl-oz"] +}, +{ + "name": "unit.pint", + "symbol": "pt", + "tags": ["volume","capacity","extent","pint","pints","pt"] +}, +{ + "name": "unit.quart", + "symbol": "qt", + "tags": ["volume","capacity","extent","quart","quarts","qt"] +}, +{ + "name": "unit.gallon", + "symbol": "gal", + "tags": ["volume","capacity","extent","gallon","gallons","gal"] +}, +{ + "name": "unit.oil-barrels", + "symbol": "bbl", + "tags": ["volume","capacity","extent","oil barrel","oil barrels","bbl"] +}, +{ + "name": "unit.cubic-meter-per-kilogram", + "symbol": "m³/kg", + "tags": ["specific volume","volume per unit mass","cubic meter per kilogram","m³/kg"] +}, +{ + "name": "unit.gill", + "symbol": "gi", + "tags": ["volume","liquid measurement","gi"] +}, +{ + "name": "unit.hogshead", + "symbol": "hhd", + "tags": ["volume","liquid measurement","hhd"] +}, +{ + "name": "unit.teaspoon", + "symbol": "tsp", + "tags": ["volume","cooking measurement","tsp"] +}, +{ + "name": "unit.tablespoon", + "symbol": "tbsp", + "tags": ["volume","cooking measurement","tbsp"] +}, +{ + "name": "unit.cup", + "symbol": "cup", + "tags": ["volume","cooking measurement","cup"] +}, +{ + "name": "unit.celsius", + "symbol": "°C", + "tags": ["temperature","heat","cold","warmth","degrees","celsius","shipment condition","°C"] +}, +{ + "name": "unit.kelvin", + "symbol": "K", + "tags": ["temperature","heat","cold","warmth","degrees","kelvin","K","color quality","white balance","color temperature"] +}, +{ + "name": "unit.rankine", + "symbol": "°R", + "tags": ["temperature","heat","cold","warmth","Rankine","°R"] +}, +{ + "name": "unit.fahrenheit", + "symbol": "°F", + "tags": ["temperature","heat","cold","warmth","degrees","fahrenheit","°F"] +}, +{ + "name": "unit.meter-per-second", + "symbol": "m/s", + "tags": ["speed","velocity","pace","meter per second","m/s","peak","peak to peak","root mean square (RMS)", + "vibration","wind speed","weather"] +}, +{ + "name": "unit.kilometer-per-hour", + "symbol": "km/h", + "tags": ["speed","velocity","pace","kilometer per hour","km/h"] +}, +{ + "name": "unit.foot-per-second", + "symbol": "ft/s", + "tags": ["speed","velocity","pace","foot per second","ft/s"] +}, +{ + "name": "unit.mile-per-hour", + "symbol": "mph", + "tags": ["speed","velocity","pace","mile per hour","mph"] +}, +{ + "name": "unit.knot", + "symbol": "kt", + "tags": ["speed","velocity","pace","knot","knots","kt"] +}, +{ + "name": "unit.millimeters-per-minute", + "symbol": "mm/min", + "tags": ["feed rate","cutting feed rate","millimeters per minute","mm/min"] +}, +{ + "name": "unit.kilometer-per-hour-squared", + "symbol": "km/h²", + "tags": ["acceleration","rate of change of velocity","kilometer per hour squared","km/h²"] +}, +{ + "name": "unit.foot-per-second-squared", + "symbol": "ft/s²", + "tags": ["acceleration","rate of change of velocity","foot per second squared","ft/s²"] +}, +{ + "name": "unit.pascal", + "symbol": "Pa", + "tags": ["pressure","force","compression","tension","pascal","pascals","Pa","atmospheric pressure","air pressure", + "weather","altitude","flight"] +}, +{ + "name": "unit.kilopascal", + "symbol": "kPa", + "tags": ["pressure","force","compression","tension","kilopascal","kilopascals","kPa"] +}, +{ + "name": "unit.megapascal", + "symbol": "MPa", + "tags": ["pressure","force","compression","tension","megapascal","megapascals","MPa"] +}, +{ + "name": "unit.gigapascal", + "symbol": "GPa", + "tags": ["pressure","force","compression","tension","gigapascal","gigapascals","GPa"] +}, +{ + "name": "unit.millibar", + "symbol": "mbar", + "tags": ["pressure","force","compression","tension","millibar","millibars","mbar"] +}, +{ + "name": "unit.bar", + "symbol": "bar", + "tags": ["pressure","force","compression","tension","bar","bars"] +}, +{ + "name": "unit.kilobar", + "symbol": "kbar", + "tags": ["pressure","force","compression","tension","kilobar","kilobars","kbar"] +}, +{ + "name": "unit.newton", + "symbol": "N", + "tags": ["force","pressure","newton","newtons","N","push","pull","weight","gravity","N"] +}, +{ + "name": "unit.newton-meter", + "symbol": "Nm", + "tags": ["torque","rotational force","newton meter","Nm"] +}, +{ + "name": "unit.foot-pounds", + "symbol": "ft·lbf", + "tags": ["torque","rotational force","foot-pound","foot-pounds","ft·lbf"] +}, +{ + "name": "unit.inch-pounds", + "symbol": "in·lbf", + "tags": ["torque","rotational force","inch-pounds","inch-pound","in·lbf"] +}, +{ + "name": "unit.newton-per-meter", + "symbol": "N/m", + "tags": ["linear density","force per unit length","newton per meter","N/m"] +}, +{ + "name": "unit.atmospheres", + "symbol": "atm", + "tags": ["pressure","force","compression","tension","atmosphere","atmospheres","atmospheric pressure","atm"] +}, +{ + "name": "unit.pounds-per-square-inch", + "symbol": "psi", + "tags": ["pressure","force","compression","tension","pounds per square inch","psi"] +}, +{ + "name": "unit.torr", + "symbol": "Torr", + "tags": ["pressure","force","compression","tension","vacuum pressure","torr"] +}, +{ + "name": "unit.inches-of-mercury", + "symbol": "inHg", + "tags": ["pressure","force","compression","tension","vacuum pressure","inHg","atmospheric pressure","barometric pressure"] +}, +{ + "name": "unit.pascal-per-square-meter", + "symbol": "Pa/m²", + "tags": ["pressure","stress","mechanical strength","pascal per square meter","Pa/m²"] +}, +{ + "name": "unit.pound-per-square-inch", + "symbol": "psi/in²", + "tags": ["pressure","stress","mechanical strength","pound per square inch","psi/in²"] +}, +{ + "name": "unit.newton-per-square-meter", + "symbol": "N/m²", + "tags": ["pressure","stress","mechanical strength","newton per square meter","N/m²"] +}, +{ + "name": "unit.kilogram-force-per-square-meter", + "symbol": "kgf/m²", + "tags": ["pressure","stress","mechanical strength","kilogram-force per square meter","kgf/m²"] +}, +{ + "name": "unit.pascal-per-square-centimeter", + "symbol": "Pa/cm²", + "tags": ["pressure","stress","mechanical strength","pascal per square centimeter","Pa/cm²"] +}, +{ + "name": "unit.ton-force-per-square-inch", + "symbol": "tonf/in²", + "tags": ["pressure","stress","mechanical strength","ton-force per square inch","tonf/in²"] +}, +{ + "name": "unit.kilonewton-per-square-meter", + "symbol": "kN/m²", + "tags": ["stress","pressure","mechanical strength","kilonewton per square meter","kN/m²"] +}, +{ + "name": "unit.newton-per-square-millimeter", + "symbol": "N/mm²", + "tags": ["stress","pressure","mechanical strength","newton per square millimeter","N/mm²"] +}, +{ + "name": "unit.microjoule", + "symbol": "μJ", + "tags": ["energy","microjoule","microjoules","μJ"] +}, +{ + "name": "unit.millijoule", + "symbol": "mJ", + "tags": ["energy","millijoule","millijoules","mJ"] +}, +{ + "name": "unit.joule", + "symbol": "J", + "tags": ["joule","joules","energy","work done","heat","electricity","mechanical work"] +}, +{ + "name": "unit.kilojoule", + "symbol": "kJ", + "tags": ["energy","kilojoule","kilojoules","kJ"] +}, +{ + "name": "unit.megajoule", + "symbol": "MJ", + "tags": ["energy","megajoule","megajoules","MJ"] +}, +{ + "name": "unit.gigajoule", + "symbol": "GJ", + "tags": ["energy","gigajoule","gigajoules","GJ"] +}, +{ + "name": "unit.watt-hour", + "symbol": "Wh", + "tags": ["energy","watt-hour","watt-hours","energy usage","power consumption","energy consumption","electricity usage"] +}, +{ + "name": "unit.kilowatt-hour", + "symbol": "kWh", + "tags": ["energy","kilowatt-hour","kilowatt-hours","energy usage","power consumption","energy consumption","electricity usage"] +}, +{ + "name": "unit.electron-volts", + "symbol": "eV", + "tags": ["energy","subatomic particles","radiation"] +}, +{ + "name": "unit.joules-per-coulomb", + "symbol": "J/C", + "tags": ["electrical potential energy","voltage","joules per coulomb","J/C"] +}, +{ + "name": "unit.british-thermal-unit", + "symbol": "BTU", + "tags": ["energy","heat","work done","british thermal unit","british thermal units","BTU"] +}, +{ + "name": "unit.foot-pound", + "symbol": "ft·lb", + "tags": ["energy","foot-pound","foot-pounds","ft·lb","ft⋅lbf"] +}, +{ + "name": "unit.calorie", + "symbol": "Cal", + "tags": ["energy","food energy","Calorie","Calories","Cal"] +}, +{ + "name": "unit.small-calorie", + "symbol": "cal", + "tags": ["energy","small calorie","calories","cal"] +}, +{ + "name": "unit.kilocalorie", + "symbol": "kcal", + "tags": ["energy","small calorie","kilocalories","kcal"] +}, +{ + "name": "unit.joule-per-kelvin", + "symbol": "J/K", + "tags": ["specific heat capacity","heat capacity per unit temperature","joule per kelvin","J/K"] +}, +{ + "name": "unit.joule-per-kilogram-kelvin", + "symbol": "J/(kg·K)", + "tags": ["specific heat capacity","heat capacity per unit mass and temperature","joule per kilogram-kelvin","J/(kg·K)"] +}, +{ + "name": "unit.joule-per-kilogram", + "symbol": "J/kg", + "tags": ["specific energy","specific energy capacity","joule per kilogram","J/kg"] +}, +{ + "name": "unit.watt-per-meter-kelvin", + "symbol": "W/(m·K)", + "tags": ["thermal conductivity","watt per meter-kelvin","W/(m·K)"] +}, +{ + "name": "unit.joule-per-cubic-meter", + "symbol": "J/m³", + "tags": ["energy density","joule per cubic meter","J/m³"] +}, +{ + "name": "unit.therm", + "symbol": "thm", + "tags": ["energy","natural gas consumption","BTU","therm","thm"] +}, +{ + "name": "unit.electric-dipole-moment", + "symbol": "C·m", + "tags": ["electric dipole","dipole moment","coulomb meter","C·m"] +}, +{ + "name": "unit.magnetic-dipole-moment", + "symbol": "A·m²", + "tags": ["magnetic dipole","dipole moment","ampere square meter","A·m²"] +}, +{ + "name": "unit.debye", + "symbol": "D", + "tags": ["polarization","electric dipole moment","debye","D"] +}, +{ + "name": "unit.coulomb-per-square-meter-per-volt", + "symbol": "C·m²/V", + "tags": ["polarization","electric field","coulomb per square meter per volt","C·m²/V"] +}, +{ + "name": "unit.milliwatt", + "symbol": "mW", + "tags": ["power","horsepower","performance","milliwatt","milliwatts","electricity","mW"] +}, +{ + "name": "unit.microwatt", + "symbol": "μW", + "tags": ["power","horsepower","performance","microwatt","microwatts","electricity","μW"] +}, +{ + "name": "unit.watt", + "symbol": "W", + "tags": ["power","horsepower","performance","watt","watts","electricity","W"] +}, +{ + "name": "unit.kilowatt", + "symbol": "kW", + "tags": ["power","horsepower","performance","kilowatt","kilowatts","electricity","kW"] +}, +{ + "name": "unit.megawatt", + "symbol": "MW", + "tags": ["power","horsepower","performance","megawatt","megawatts","electricity","MW"] +}, +{ + "name": "unit.gigawatt", + "symbol": "GW", + "tags": ["power","horsepower","performance","gigawatt","gigawatts","electricity","GW"] +}, +{ + "name": "unit.metric-horsepower", + "symbol": "PS", + "tags": ["power","performance","metric horsepower","PS"] +}, +{ + "name": "unit.milliwatt-per-square-centimeter", + "symbol": "mW/cm²", + "tags": ["power density","radiation intensity","sunlight intensity","signal power","intensity", + "milliwatts per square centimeter","UV Intensity","mW/cm²"] +}, +{ + "name": "unit.watt-per-square-centimeter", + "symbol": "W/cm²", + "tags": ["power density","intensity of power","watts per square centimeter","W/cm²"] +}, +{ + "name": "unit.kilowatt-per-square-centimeter", + "symbol": "kW/cm²", + "tags": ["power density","intensity of power","kilowatts per square centimeter","kW/cm²"] +}, +{ + "name": "unit.milliwatt-per-square-meter", + "symbol": "mW/m²", + "tags": ["power density","intensity of power","milliwatts per square meter","mW/m²"] +}, +{ + "name": "unit.watt-per-square-meter", + "symbol": "W/m²", + "tags": ["power density","intensity of power","watts per square meter","W/m²"] +}, +{ + "name": "unit.kilowatt-per-square-meter", + "symbol": "kW/m²", + "tags": ["power density","intensity of power","kilowatts per square meter","kW/m²"] +}, +{ + "name": "unit.watt-per-square-inch", + "symbol": "W/in²", + "tags": ["power density","intensity of power","watts per square inch","W/in²"] +}, +{ + "name": "unit.kilowatt-per-square-inch", + "symbol": "kW/in²", + "tags": ["power density","intensity of power","kilowatts per square inch","kW/in²"] +}, +{ + "name": "unit.horsepower", + "symbol": "hp", + "tags": ["power","horsepower","performance","electricity","horsepowers","hp"] +}, +{ + "name": "unit.btu-per-hour", + "symbol": "BTU/h", + "tags": ["power","heat transfer","thermal energy","HVAC","BTU/h"] +}, +{ + "name": "unit.coulomb", + "symbol": "C", + "tags": ["charge","electricity","electrostatics","Coulomb","C"] +}, +{ + "name": "unit.millicoulomb", + "symbol": "mC", + "tags": ["charge","electricity","electrostatics","millicoulombs","mC"] +}, +{ + "name": "unit.microcoulomb", + "symbol": "µC", + "tags": ["charge","electricity","electrostatics","microcoulomb","µC"] +}, +{ + "name": "unit.picocoulomb", + "symbol": "pC", + "tags": ["charge","electricity","electrostatics","picocoulomb","pC"] +}, +{ + "name": "unit.coulomb-per-meter", + "symbol": "C/m", + "tags": ["electric displacement field per length","coulomb per meter","C/m"] +}, +{ + "name": "unit.coulomb-per-cubic-meter", + "symbol": "C/m³", + "tags": ["electric charge density","coulomb per cubic meter","C/m³"] +}, +{ + "name": "unit.coulomb-per-square-meter", + "symbol": "C/m²", + "tags": ["electric surface charge density","coulomb per square meter","C/m²"] +}, +{ + "name": "unit.square-millimeter", + "symbol": "mm²", + "tags": ["area","lot","zone","space","region","square millimeter","square millimeters","mm²","sq-mm"] +}, +{ + "name": "unit.square-centimeter", + "symbol": "cm²", + "tags": ["area","lot","zone","space","region","square centimeter","square centimeters","cm²","sq-cm"] +}, +{ + "name": "unit.square-meter", + "symbol": "m²", + "tags": ["area","lot","zone","space","region","square meter","square meters","m²","sq-m"] +}, +{ + "name": "unit.hectare", + "symbol": "ha", + "tags": ["area","lot","zone","space","region","hectare","hectares","ha"] +}, +{ + "name": "unit.square-kilometer", + "symbol": "km²", + "tags": ["area","lot","zone","space","region","square kilometer","square kilometers","km²","sq-km"] +}, +{ + "name": "unit.square-inch", + "symbol": "in²", + "tags": ["area","lot","zone","space","region","square inch","square inches","in²","sq-in"] +}, +{ + "name": "unit.square-foot", + "symbol": "ft²", + "tags": ["area","lot","zone","space","region","square foot","square feet","ft²","sq-ft"] +}, +{ + "name": "unit.square-yard", + "symbol": "yd²", + "tags": ["area","lot","zone","space","region","square yard","square yards","yd²","sq-yd"] +}, +{ + "name": "unit.acre", + "symbol": "a", + "tags": ["area","lot","zone","space","region","acre","acres","a"] +}, +{ + "name": "unit.square-mile", + "symbol": "ml²", + "tags": ["area","lot","zone","space","region","square mile","square miles","ml²","sq-mi"] +}, +{ + "name": "unit.are", + "symbol": "are", + "tags": ["area","land measurement","are"] +}, +{ + "name": "unit.barn", + "symbol": "barn", + "tags": ["cross-sectional area","particle physics","nuclear physics","barn"] +}, +{ + "name": "unit.circular-inch", + "symbol": "circin", + "tags": ["area","circular measurement","circular inch","circin"] +}, +{ + "name": "unit.milliampere-hour", + "symbol": "mAh", + "tags": ["electric current","current flow","electric charge","current capacity","flow of electricity", + "electrical flow","milliampere-hour","milliampere-hours","mAh"] +}, +{ + "name": "unit.ampere-hours", + "symbol": "Ah", + "tags": ["electric current","current flow","electric charge","current capacity","flow of electricity", + "electrical flow","ampere","ampere-hours","Ah"] +}, +{ + "name": "unit.kiloampere-hours", + "symbol": "kAh", + "tags": ["electric current","current flow","electric charge","current capacity","flow of electricity","electrical flow", + "kiloampere-hours","kiloampere-hour","kAh"] +}, +{ + "name": "unit.nanoampere", + "symbol": "nA", + "tags": ["current","amperes","nanoampere","nA"] +}, +{ + "name": "unit.picoampere", + "symbol": "pA", + "tags": ["current","amperes","picoampere","pA"] +}, +{ + "name": "unit.microampere", + "symbol": "μA", + "tags": ["electric current","microampere","microamperes","μA"] +}, +{ + "name": "unit.milliampere", + "symbol": "mA", + "tags": ["electric current","milliampere","milliamperes","mA"] +}, +{ + "name": "unit.ampere", + "symbol": "A", + "tags": ["electric current","current flow","flow of electricity","electrical flow","ampere","amperes","amperage","A"] +}, +{ + "name": "unit.kiloamperes", + "symbol": "kA", + "tags": ["electric current","current flow","kiloamperes","kA"] +}, +{ + "name": "unit.microampere-per-square-centimeter", + "symbol": "µA/cm²", + "tags": ["Current density","microampere per square centimeter","µA/cm²"] +}, +{ + "name": "unit.ampere-per-square-meter", + "symbol": "A/m²", + "tags": ["current density","current per unit area","ampere per square meter","A/m²"] +}, +{ + "name": "unit.ampere-per-meter", + "symbol": "A/m", + "tags": ["magnetic field strength","magnetic field intensity","ampere per meter","A/m"] +}, +{ + "name": "unit.oersted", + "symbol": "Oe", + "tags": ["magnetic field","oersted","Oe"] +}, +{ + "name": "unit.bohr-magneton", + "symbol": "μB", + "tags": ["atomic physics","magnetic moment","bohr magneton","μB"] +}, +{ + "name": "unit.ampere-meter-squared", + "symbol": "A·m²", + "tags": ["magnetic moment","dipole moment","ampere-meter squared","A·m²"] +}, +{ + "name": "unit.ampere-meter", + "symbol": "A·m", + "tags": ["magnetic field","current loop","ampere-meter","A·m"] +}, +{ + "name": "unit.nanovolt", + "symbol": "nV", + "tags": ["voltage","volts","nanovolt","nV"] +}, +{ + "name": "unit.picovolt", + "symbol": "pV", + "tags": ["voltage","volts","picovolt","pV"] +}, +{ + "name": "unit.millivolts", + "symbol": "mV", + "tags": ["electric potential","electric tension","voltage","millivolt","millivolts","mV"] +}, +{ + "name": "unit.microvolts", + "symbol": "μV", + "tags": ["electric potential","electric tension","voltage","microvolt","microvolts","μV"] +}, +{ + "name": "unit.volt", + "symbol": "V", + "tags": ["electric potential","electric tension","voltage","volt","volts","V","power source","battery","battery level"] +}, +{ + "name": "unit.kilovolts", + "symbol": "kV", + "tags": ["electric potential","electric tension","voltage","kilovolt","kilovolts","kV"] +}, +{ + "name": "unit.dbmV", + "symbol": "dBmV", + "tags": ["decibels millivolt","voltage level","signal","dBmV"] +}, +{ + "name": "unit.dbm", + "symbol": "dBm", + "tags": ["decibel milliwatts","output power","signal","dBm"] +}, +{ + "name": "unit.volt-meter", + "symbol": "V·m", + "tags": ["electric flux","volt-meter","V·m"] +}, +{ + "name": "unit.kilovolt-meter", + "symbol": "kV·m", + "tags": ["electric flux","kilovolt-meter","kV·m"] +}, +{ + "name": "unit.megavolt-meter", + "symbol": "MV·m", + "tags": ["electric flux","megavolt-meter","MV·m"] +}, +{ + "name": "unit.microvolt-meter", + "symbol": "µV·m", + "tags": ["electric flux","microvolt-meter","µV·m"] +}, +{ + "name": "unit.millivolt-meter", + "symbol": "mV·m", + "tags": ["electric flux","millivolt-meter","mV·m"] +}, +{ + "name": "unit.nanovolt-meter", + "symbol": "nV·m", + "tags": ["electric flux","nanovolt-meter","nV·m"] +}, +{ + "name": "unit.ohm", + "symbol": "Ω", + "tags": ["electrical resistance","resistance","impedance","ohm"] +}, +{ + "name": "unit.microohm", + "symbol": "μΩ", + "tags": ["electrical resistance","resistance","microohm","μΩ"] +}, +{ + "name": "unit.milliohm", + "symbol": "mΩ", + "tags": ["electrical resistance","resistance","milliohm","mΩ"] +}, +{ + "name": "unit.kilohm", + "symbol": "kΩ", + "tags": ["electrical resistance","resistance","kilohm","kΩ"] +}, +{ + "name": "unit.megohm", + "symbol": "MΩ", + "tags": ["electrical resistance","resistance","megohm","MΩ"] +}, +{ + "name": "unit.gigohm", + "symbol": "GΩ", + "tags": ["electrical resistance","resistance","gigohm","GΩ"] +}, +{ + "name": "unit.hertz", + "symbol": "Hz", + "tags": ["frequency","cycles per second","hertz","Hz"] +}, +{ + "name": "unit.kilohertz", + "symbol": "kHz", + "tags": ["frequency","cycles per second","kilohertz","kHz"] +}, +{ + "name": "unit.megahertz", + "symbol": "MHz", + "tags": ["frequency","cycles per second","megahertz","MHz"] +}, +{ + "name": "unit.gigahertz", + "symbol": "GHz", + "tags": ["frequency","cycles per second","gigahertz","GHz"] +}, +{ + "name": "unit.rpm", + "symbol": "RPM", + "tags": ["speed","velocity","cycle","engine","Revolutions Per Minute","RPM","angular velocity","rotation speed"] +}, +{ + "name": "unit.candela-per-square-meter", + "symbol": "cd/m²", + "tags": ["brightness","light level","Luminance","Candela per square meter","cd/m²"] +}, +{ + "name": "unit.candela", + "symbol": "cd", + "tags": ["light intensity","candle power","luminous intensity","Candela","cd"] +}, +{ + "name": "unit.lumen", + "symbol": "lm", + "tags": ["total light output","light power","luminous flux","Lumen","lm"] +}, +{ + "name": "unit.lux", + "symbol": "lx", + "tags": ["illumination","light level on a surface","illuminance","Lux","lx"] +}, +{ + "name": "unit.foot-candle", + "symbol": "fc", + "tags": ["illuminance","light level","foot-candle","fc"] +}, +{ + "name": "unit.lumen-per-square-meter", + "symbol": "lm/m²", + "tags": ["illuminance","light level","lumen per square meter","lm/m²"] +}, +{ + "name": "unit.lux-second", + "symbol": "lx·s", + "tags": ["light exposure","illumination time","light dosage","Lux second","lx·s"] +}, +{ + "name": "unit.lumen-second", + "symbol": "lm·s", + "tags": ["total light energy","luminous energy","Lumen second","lm·s"] +}, +{ + "name": "unit.lumens-per-watt", + "symbol": "lm/W", + "tags": ["lighting efficiency","light output per energy","luminous efficacy","Lumens per watt","lm/W"] +}, +{ + "name": "unit.absorbance", + "symbol": "AU", + "tags": ["optical density","light absorption","absorbance","AU"] +}, +{ + "name": "unit.mole", + "symbol": "mol", + "tags": ["amount of substance","substance quantity","mole","moles","mol"] +}, +{ + "name": "unit.nanomole", + "symbol": "nmol", + "tags": ["amount of substance","substance quantity","concentration","nanomole","nmol"] +}, +{ + "name": "unit.micromole", + "symbol": "μmol", + "tags": ["amount of substance","substance quantity","micromole","μmol"] +}, +{ + "name": "unit.millimole", + "symbol": "mmol", + "tags": ["amount of substance","substance quantity","millimole","mmol"] +}, +{ + "name": "unit.kilomole", + "symbol": "kmol", + "tags": ["amount of substance","substance quantity","kilomole","kmol"] +}, +{ + "name": "unit.mole-per-cubic-meter", + "symbol": "mol/m³", + "tags": ["concentration","amount of substance","mole per cubic meter","mol/m³"] +}, +{ + "name": "unit.percent", + "symbol": "%", + "tags": ["power source","state of charge (SoC)","battery","battery level","level","humidity","moisture","percentage", + "relative humidity","water content","soil moisture","irrigation","water in soil","soil water content","VWC", + "Volumetric Water Content","Total Harmonic Distortion","THD","power quality","UV Transmittance","%", "capacity"] +}, +{ + "name": "unit.rssi", + "symbol": "rssi", + "tags": ["signal strength","signal level","received signal strength indicator","rssi","dBm"] +}, +{ + "name": "unit.ppm", + "symbol": "ppm", + "tags": ["carbon dioxide","co²","carbon monoxide","co","aqi","air quality","total volatile organic compounds","tvoc","ppm"] +}, +{ + "name": "unit.ppb", + "symbol": "ppb", + "tags": ["ozone","o³","nitrogen dioxide","no²","sulfur dioxide","so²","aqi","air quality","tvoc","ppb"] +}, +{ + "name": "unit.micrograms-per-cubic-meter", + "symbol": "µg/m³", + "tags": ["coarse particulate matter","pm10","fine particulate matter","pm2.5","aqi","air quality", + "total volatile organic compounds","tvoc","micrograms per cubic meter","µg/m³"] +}, +{ + "name": "unit.aqi", + "symbol": "aqi", + "tags": ["AQI","air quality index"] +}, +{ + "name": "unit.gram-per-cubic-meter", + "symbol": "g/m³", + "tags": ["humidity","moisture","absolute humidity","g/m³"] +}, +{ + "name": "unit.gram-per-kilogram", + "symbol": "g/kg", + "tags": ["humidity","moisture","specific humidity","g/kg"] +}, +{ + "name": "unit.millimeters-per-second", + "symbol": "mm/s", + "tags": ["velocity","speed","rate of motion","peak","peak to peak","root mean square (RMS)","vibration","mm/s"] +}, +{ + "name": "unit.neper", + "symbol": "Np", + "tags": ["logarithmic unit","ratio","gain","loss","attenuation","neper","Np"] +}, +{ + "name": "unit.bel", + "symbol": "B", + "tags": ["logarithmic unit","power ratio","intensity ratio","bel","B"] +}, +{ + "name": "unit.decibel", + "symbol": "dB", + "tags": ["noise level","sound level","volume","acoustics","decibel","dB"] +}, +{ + "name": "unit.meters-per-second-squared", + "symbol": "m/s²", + "tags": ["peak","peak to peak","root mean square (RMS)","vibration","meters per second squared","m/s²"] +}, +{ + "name": "unit.becquerel", + "symbol": "Bq", + "tags": ["radioactivity","radiation","becquerel","Bq"] +}, +{ + "name": "unit.curie", + "symbol": "Ci", + "tags": ["radioactivity","radiation","curie","Ci"] +}, +{ + "name": "unit.gray", + "symbol": "Gy", + "tags": ["radiation dose","gray","Gy"] +}, +{ + "name": "unit.sievert", + "symbol": "Sv", + "tags": ["radiation dose","sievert","radiation dose equivalent2","Sv"] +}, +{ + "name": "unit.roentgen", + "symbol": "R", + "tags": ["radiation exposure","roentgen","R"] +}, +{ + "name": "unit.cps", + "symbol": "cps", + "tags": ["radiation detection","counts per second","cps"] +}, +{ + "name": "unit.rad", + "symbol": "Rad", + "tags": ["radiation dose","rad"] +}, +{ + "name": "unit.rem", + "symbol": "Rem", + "tags": ["radiation dose equivalent","rem"] +}, +{ + "name": "unit.dps", + "symbol": "dps", + "tags": ["radioactive decay","radioactivity","disintegrations per second","dps"] +}, +{ + "name": "unit.rutherford", + "symbol": "Rd", + "tags": ["radioactive decay","radioactivity","rutherford","Rd"] +}, +{ + "name": "unit.coulombs-per-kilogram", + "symbol": "C/kg", + "tags": ["radiation exposure","dose","coulombs per kilogram","electric charge-to-mass ratio","C/kg"] +}, +{ + "name": "unit.becquerels-per-cubic-meter", + "symbol": "Bq/m³", + "tags": ["radioactivity","radiation","becquerels per cubic meter","Bq/m³"] +}, +{ + "name": "unit.curies-per-liter", + "symbol": "Ci/L", + "tags": ["radioactivity","radiation","curies per liter","Ci/L"] +}, +{ + "name": "unit.becquerels-per-second", + "symbol": "Bq/s", + "tags": ["radioactive decay rate","becquerels per second","Bq/s"] +}, +{ + "name": "unit.curies-per-second", + "symbol": "Ci/s", + "tags": ["radioactive decay rate","curies per second","Ci/s"] +}, +{ + "name": "unit.gy-per-second", + "symbol": "Gy/s", + "tags": ["absorbed dose rate","radiation dose rate","gray per second","Gy/s"] +}, +{ + "name": "unit.watt-per-steradian", + "symbol": "W/sr", + "tags": ["radiant intensity","power per unit solid angle","watt per steradian","W/sr"] +}, +{ + "name": "unit.watt-per-square-metre-steradian", + "symbol": "W/(m²·sr)", + "tags": ["radiance","radiant flux density","watt per square metre-steradian","W/(m²·sr)"] +}, +{ + "name": "unit.ph-level", + "symbol": "pH", + "tags": ["acidity","alkalinity","neutral","acid","base","pH","soil pH","water quality","water pH"] +}, +{ + "name": "unit.turbidity", + "symbol": "NTU", + "tags": ["water turbidity","water clarity","Nephelometric Turbidity Units","NTU"] +}, +{ + "name": "unit.mg-per-liter", + "symbol": "mg/L", + "tags": ["dissolved oxygen","water quality","mg/L"] +}, +{ + "name": "unit.microsiemens-per-centimeter", + "symbol": "µS/cm", + "tags": ["Electrical conductivity","water quality","soil quality","microsiemens per centimeter","µS/cm"] +}, +{ + "name": "unit.millisiemens-per-meter", + "symbol": "mS/m", + "tags": ["Electrical conductivity","water quality","soil quality","millisiemens per meter","mS/m"] +}, +{ + "name": "unit.siemens-per-meter", + "symbol": "S/m", + "tags": ["Electrical conductivity","water quality","soil quality","siemens per meter","S/m"] +}, +{ + "name": "unit.kilogram-per-cubic-meter", + "symbol": "kg/m³", + "tags": ["density","mass per unit volume","kg/m³"] +}, +{ + "name": "unit.gram-per-cubic-centimeter", + "symbol": "g/cm³", + "tags": ["density","mass per unit volume","g/cm³"] +}, +{ + "name": "unit.kilogram-per-square-meter", + "symbol": "kg/m²", + "tags": ["density","surface density","areal density","mass per unit area","kg/m²"] +}, +{ + "name": "unit.milligram-per-milliliter", + "symbol": "mg/mL", + "tags": ["concentration","mass per volume","mg/mL"] +}, +{ + "name": "unit.milligram-per-cubic-meter", + "symbol": "mg/m³", + "tags": ["concentration","mass per volume","mg/m³"] +}, +{ + "name": "unit.pound-per-cubic-foot", + "symbol": "lb/ft³", + "tags": ["Density","mass per unit volume","lb/ft³"] +}, +{ + "name": "unit.ounces-per-cubic-inch", + "symbol": "oz/in³", + "tags": ["density","mass per unit volume","oz/in³"] +}, +{ + "name": "unit.tons-per-cubic-yard", + "symbol": "ton/yd³", + "tags": ["density","mass per unit volume","ton/yd³"] +}, +{ + "name": "unit.particle-density", + "symbol": "particles/mL", + "tags": ["particle concentration","count","particles/mL"] +}, +{ + "name": "unit.kilometers-per-liter", + "symbol": "km/L", + "tags": ["fuel efficiency","km/L"] +}, +{ + "name": "unit.miles-per-gallon", + "symbol": "mpg", + "tags": ["fuel efficiency","mpg"] +}, +{ + "name": "unit.liters-per-100-km", + "symbol": "L/100km", + "tags": ["fuel efficiency","L/100km"] +}, +{ + "name": "unit.gallons-per-mile", + "symbol": "gal/mi", + "tags": ["fuel efficiency","gal/mi"] +}, +{ + "name": "unit.liters-per-hour", + "symbol": "L/hr", + "tags": ["fuel consumption","L/hr"] +}, +{ + "name": "unit.gallons-per-hour", + "symbol": "gal/hr", + "tags": ["fuel consumption","gal/hr"] +}, +{ + "name": "unit.beats-per-minute", + "symbol": "bpm", + "tags": ["heart rate","pulse","bpm"] +}, +{ + "name": "unit.millimeters-of-mercury", + "symbol": "mmHg", + "tags": ["blood pressure","systolic","diastolic","mmHg"] +}, +{ + "name": "unit.milligrams-per-deciliter", + "symbol": "mg/dL", + "tags": ["glucose","blood sugar","glucose level","mg/dL"] +}, +{ + "name": "unit.g-force", + "symbol": "G", + "tags": ["acceleration","gravity","force","g-load","G"] +}, +{ + "name": "unit.kilonewton", + "symbol": "kN", + "tags": ["force","kN"] +}, +{ + "name": "unit.kilogram-force", + "symbol": "kgf", + "tags": ["force","kgf"] +}, +{ + "name": "unit.pound-force", + "symbol": "lbf", + "tags": ["force","lbf"] +}, +{ + "name": "unit.kilopound-force", + "symbol": "klbf", + "tags": ["force","klbf"] +}, +{ + "name": "unit.dyne", + "symbol": "dyn", + "tags": ["force","dyn"] +}, +{ + "name": "unit.poundal", + "symbol": "pdl", + "tags": ["force","pdl"] +}, +{ + "name": "unit.kip", + "symbol": "kip", + "tags": ["force","kip"] +}, +{ + "name": "unit.gal", + "symbol": "Gal", + "tags": ["acceleration","gravity","g-force","Gal"] +}, +{ + "name": "unit.gravity", + "symbol": "gravity", + "tags": ["acceleration","gravity","g-force"] +}, +{ + "name": "unit.hectopascal", + "symbol": "hPa", + "tags": ["atmospheric pressure","air pressure","weather","altitude","flight","hPa"] +}, +{ + "name": "unit.atmosphere", + "symbol": "atm", + "tags": ["atmospheric pressure","air pressure","weather","altitude","flight","atm"] +}, +{ + "name": "unit.millibars", + "symbol": "mb", + "tags": ["atmospheric pressure","air pressure","weather","altitude","flight","mb"] +}, +{ + "name": "unit.inch-of-mercury", + "symbol": "inHg", + "tags": ["atmospheric pressure","air pressure","weather","altitude","flight","inHg","richter"] +}, +{ + "name": "unit.richter-scale", + "symbol": "richter", + "tags": ["earthquake","seismic activity","richter"] +}, +{ + "name": "unit.second", + "symbol": "s", + "tags": ["time","duration","interval","angle","second","arcsecond","sec"] +}, +{ + "name": "unit.minute", + "symbol": "min", + "tags": ["time","duration","interval","angle","minute","arcminute","min"] +}, +{ + "name": "unit.hour", + "symbol": "h", + "tags": ["time","duration","interval","h"] +}, +{ + "name": "unit.day", + "symbol": "d", + "tags": ["time","duration","interval","d"] +}, +{ + "name": "unit.week", + "symbol": "wk", + "tags": ["time","duration","interval","wk"] +}, +{ + "name": "unit.month", + "symbol": "mo", + "tags": ["time","duration","interval","mo"] +}, +{ + "name": "unit.year", + "symbol": "yr", + "tags": ["time","duration","interval","yr"] +}, +{ + "name": "unit.cubic-foot-per-minute", + "symbol": "ft³/min", + "tags": ["airflow","ventilation","HVAC","gas flow rate","CFM","flow rate","fluid flow","cubic foot per minute","ft³/min"] +}, +{ + "name": "unit.cubic-meters-per-hour", + "symbol": "m³/hr", + "tags": ["airflow","ventilation","HVAC","gas flow rate","cubic meters per hour","m³/hr"] +}, +{ + "name": "unit.cubic-meters-per-second", + "symbol": "m³/s", + "tags": ["airflow","ventilation","HVAC","gas flow rate","cubic meters per second","m³/s"] +}, +{ + "name": "unit.liter-per-second", + "symbol": "L/s", + "tags": ["airflow","ventilation","HVAC","gas flow rate","liter per second","L/s"] +}, +{ + "name": "unit.liter-per-minute", + "symbol": "L/min", + "tags": ["airflow","ventilation","HVAC","gas flow rate","liter per minute","L/min"] +}, +{ + "name": "unit.gallons-per-minute", + "symbol": "GPM", + "tags": ["airflow","ventilation","HVAC","gas flow rate","gallons per minute","GPM"] +}, +{ + "name": "unit.cubic-foot-per-second", + "symbol": "ft³/s", + "tags": ["flow rate","fluid flow","cubic foot per second","cubic feet per second","ft³/s"] +}, +{ + "name": "unit.milliliters-per-minute", + "symbol": "mL/min", + "tags": ["Flow rate","fluid dynamics","milliliters per minute","mL/min"] +}, +{ + "name": "unit.bit", + "symbol": "bit", + "tags": ["data","binary digit","information","bit"] +}, +{ + "name": "unit.byte", + "symbol": "B", + "tags": ["data","byte","information","storage","memory","B"] +}, +{ + "name": "unit.kilobyte", + "symbol": "KB", + "tags": ["data","kilobyte","KB"] +}, +{ + "name": "unit.megabyte", + "symbol": "MB", + "tags": ["data","megabyte","MB"] +}, +{ + "name": "unit.gigabyte", + "symbol": "GB", + "tags": ["data","gigabyte","GB"] +}, +{ + "name": "unit.terabyte", + "symbol": "TB", + "tags": ["data","terabyte","TB"] +}, +{ + "name": "unit.petabyte", + "symbol": "PB", + "tags": ["data","petabyte","PB"] +}, +{ + "name": "unit.exabyte", + "symbol": "EB", + "tags": ["data","exabyte","EB"] +}, +{ + "name": "unit.zettabyte", + "symbol": "ZB", + "tags": ["data","zettabyte","ZB"] +}, +{ + "name": "unit.yottabyte", + "symbol": "YB", + "tags": ["data","yottabyte","YB"] +}, +{ + "name": "unit.bit-per-second", + "symbol": "bps", + "tags": ["data transfer rate","bps"] +}, +{ + "name": "unit.kilobit-per-second", + "symbol": "kbps", + "tags": ["data transfer rate","kbps"] +}, +{ + "name": "unit.megabit-per-second", + "symbol": "Mbps", + "tags": ["data transfer rate","Mbps"] +}, +{ + "name": "unit.gigabit-per-second", + "symbol": "Gbps", + "tags": ["data transfer rate","Gbps"] +}, +{ + "name": "unit.terabit-per-second", + "symbol": "Tbps", + "tags": ["data transfer rate","Tbps"] +}, +{ + "name": "unit.byte-per-second", + "symbol": "B/s", + "tags": ["data transfer rate","B/s"] +}, +{ + "name": "unit.kilobyte-per-second", + "symbol": "KB/s", + "tags": ["data transfer rate","KB/s"] +}, +{ + "name": "unit.megabyte-per-second", + "symbol": "MB/s", + "tags": ["data transfer rate","MB/s"] +}, +{ + "name": "unit.gigabyte-per-second", + "symbol": "GB/s", + "tags": ["data transfer rate","GB/s"] +}, +{ + "name": "unit.degree", + "symbol": "deg", + "tags": ["angle","degree","degrees","deg"] +}, +{ + "name": "unit.radian", + "symbol": "rad", + "tags": ["angle","radian","radians","rad"] +}, +{ + "name": "unit.gradian", + "symbol": "grad", + "tags": ["angle","gradian","grades","grad"] +}, +{ + "name": "unit.mil", + "symbol": "mil", + "tags": ["angle","military angle","angular mil","mil"] +}, +{ + "name": "unit.revolution", + "symbol": "rev", + "tags": ["angle","revolution","full circle","complete turn","rev"] +}, +{ + "name": "unit.siemens", + "symbol": "S", + "tags": ["electrical conductance","conductance","siemens","S"] +}, +{ + "name": "unit.millisiemens", + "symbol": "mS", + "tags": ["electrical conductance","conductance","millisiemens","mS"] +}, +{ + "name": "unit.microsiemens", + "symbol": "μS", + "tags": ["electrical conductance","conductance","microsiemens","μS"] +}, +{ + "name": "unit.kilosiemens", + "symbol": "kS", + "tags": ["electrical conductance","conductance","kilosiemens","kS"] +}, +{ + "name": "unit.megasiemens", + "symbol": "MS", + "tags": ["electrical conductance","conductance","megasiemens","MS"] +}, +{ + "name": "unit.gigasiemens", + "symbol": "GS", + "tags": ["electrical conductance","conductance","gigasiemens","GS"] +}, +{ + "name": "unit.farad", + "symbol": "F", + "tags": ["electric capacitance","capacitance","farad","F"] +}, +{ + "name": "unit.millifarad", + "symbol": "mF", + "tags": ["electric capacitance","capacitance","millifarad","mF"] +}, +{ + "name": "unit.microfarad", + "symbol": "μF", + "tags": ["electric capacitance","capacitance","microfarad","μF"] +}, +{ + "name": "unit.nanofarad", + "symbol": "nF", + "tags": ["electric capacitance","capacitance","nanofarad","nF"] +}, +{ + "name": "unit.picofarad", + "symbol": "pF", + "tags": ["electric capacitance","capacitance","picofarad","pF"] +}, +{ + "name": "unit.kilofarad", + "symbol": "kF", + "tags": ["electric capacitance","capacitance","kilofarad","kF"] +}, +{ + "name": "unit.megafarad", + "symbol": "MF", + "tags": ["electric capacitance","capacitance","megafarad","MF"] +}, +{ + "name": "unit.gigafarad", + "symbol": "GF", + "tags": ["electric capacitance","capacitance","gigafarad","GF"] +}, +{ + "name": "unit.terfarad", + "symbol": "TF", + "tags": ["electric capacitance","capacitance","terafarad","TF"] +}, +{ + "name": "unit.farad-per-meter", + "symbol": "F/m", + "tags": ["electric permittivity","farad per meter","F/m"] +}, +{ + "name": "unit.tesla", + "symbol": "T", + "tags": ["magnetic field","magnetic field strength","tesla","T","magnetic flux density"] +}, +{ + "name": "unit.gauss", + "symbol": "G", + "tags": ["magnetic field","magnetic field strength","gauss","G","magnetic flux density"] +}, +{ + "name": "unit.kilogauss", + "symbol": "kG", + "tags": ["magnetic field","magnetic field strength","kilogauss","kG","magnetic flux density"] +}, +{ + "name": "unit.millitesla", + "symbol": "mT", + "tags": ["magnetic field","magnetic field strength","millitesla","mT"] +}, +{ + "name": "unit.microtesla", + "symbol": "μT", + "tags": ["magnetic field","magnetic field strength","microtesla","μT"] +}, +{ + "name": "unit.nanotesla", + "symbol": "nT", + "tags": ["magnetic field","magnetic field strength","nanotesla","nT"] +}, +{ + "name": "unit.kilotesla", + "symbol": "kT", + "tags": ["magnetic field","magnetic field strength","kilotesla","kT"] +}, +{ + "name": "unit.megatesla", + "symbol": "MT", + "tags": ["magnetic field","magnetic field strength","megatesla","MT"] +}, +{ + "name": "unit.millitesla-square-meters", + "symbol": "millitesla square meters", + "tags": ["magnetic field","millitesla square meters"] +}, +{ + "name": "unit.gamma", + "symbol": "γ", + "tags": ["magnetic flux density","gamma","γ"] +}, +{ + "name": "unit.lambda", + "symbol": "λ", + "tags": ["wavelength","lambda","λ"] +}, +{ + "name": "unit.square-meter-per-second", + "symbol": "m²/s", + "tags": ["kinematic viscosity","m²/s"] +}, +{ + "name": "unit.square-centimeter-per-second", + "symbol": "cm²/s", + "tags": ["kinematic viscosity","cm²/s"] +}, +{ + "name": "unit.stoke", + "symbol": "St", + "tags": ["kinematic viscosity","stokes","St"] +}, +{ + "name": "unit.centistokes", + "symbol": "cSt", + "tags": ["kinematic viscosity","centistokes","cSt"] +}, +{ + "name": "unit.square-foot-per-second", + "symbol": "ft²/s", + "tags": ["kinematic viscosity","ft²/s"] +}, +{ + "name": "unit.square-inch-per-second", + "symbol": "in²/s", + "tags": ["kinematic viscosity","in²/s"] +}, +{ + "name": "unit.pascal-second", + "symbol": "Pa·s", + "tags": ["dynamic viscosity","viscosity","fluid mechanics","pascal-second","Pa·s"] +}, +{ + "name": "unit.centipoise", + "symbol": "cP", + "tags": ["viscosity","dynamic viscosity","fluid viscosity","centipoise","cP"] +}, +{ + "name": "unit.poise", + "symbol": "P", + "tags": ["viscosity","dynamic viscosity","fluid viscosity","poise","P"] +}, +{ + "name": "unit.reynolds", + "symbol": "Re", + "tags": ["fluid flow regime","fluid mechanics","reynolds","Re"] +}, +{ + "name": "unit.pound-per-foot-hour", + "symbol": "lb/(ft·h)", + "tags": ["pound per foot-hour","lb/(ft·h)"] +}, +{ + "name": "unit.newton-second-per-square-meter", + "symbol": "N·s/m²", + "tags": ["newton second per square meter","N·s/m²"] +}, +{ + "name": "unit.dyne-second-per-square-centimeter", + "symbol": "dyn·s/cm²", + "tags": ["dyne second per square centimeter","dyn·s/cm²"] +}, +{ + "name": "unit.kilogram-per-meter-second", + "symbol": "kg/(m·s)", + "tags": ["kilogram per meter-second","kg/(m·s)"] +}, +{ + "name": "unit.tesla-square-meters", + "symbol": "T/m²", + "tags": ["magnetic flux density","tesla square meters","T/m²"] +}, +{ + "name": "unit.maxwell", + "symbol": "Mx", + "tags": ["magnetic flux","magnetic field","maxwell","Mx"] +}, +{ + "name": "unit.tesla-per-meter", + "symbol": "T/m", + "tags": ["magnetic field","tesla per meter","T/m"] +}, +{ + "name": "unit.gauss-per-centimeter", + "symbol": "G/cm", + "tags": ["magnetic field","gauss per centimeter","G/cm"] +}, +{ + "name": "unit.weber", + "symbol": "Wb", + "tags": ["magnetic flux","weber","Wb"] +}, +{ + "name": "unit.microweber", + "symbol": "µWb", + "tags": ["magnetic flux","microweber","µWb"] +}, +{ + "name": "unit.milliweber", + "symbol": "mWb", + "tags": ["magnetic flux","milliweber","mWb"] +}, +{ + "name": "unit.gauss-square-centimeter", + "symbol": "G·cm²", + "tags": ["magnetic flux","gauss-square centimeter","G·cm²"] +}, +{ + "name": "unit.kilogauss-square-centimeter", + "symbol": "kG·cm²", + "tags": ["magnetic flux","kilogauss-square centimeter","kG·cm²"] +}, +{ + "name": "unit.henry", + "symbol": "H", + "tags": ["inductance","magnetic induction","H"] +}, +{ + "name": "unit.millihenry", + "symbol": "mH", + "tags": ["inductance","millihenry","mH"] +}, +{ + "name": "unit.microhenry", + "symbol": "µH", + "tags": ["inductance","microhenry","µH"] +}, +{ + "name": "unit.nanohenry", + "symbol": "nH", + "tags": ["inductance","nanohenry","nH"] +}, +{ + "name": "unit.henry-per-meter", + "symbol": "H/m", + "tags": ["magnetic permeability","henry per meter","H/m"] +}, +{ + "name": "unit.tesla-meter-per-ampere", + "symbol": "T·m/A", + "tags": ["magnetic field","Tesla Meter per Ampere","T·m/A","magnetic flux"] +}, +{ + "name": "unit.gauss-per-oersted", + "symbol": "G/Oe", + "tags": ["magnetic field","Gauss per Oersted","G/Oe"] +}, +{ + "name": "unit.kilogram-per-mole", + "symbol": "kg/mol", + "tags": ["molar mass","kilogram per mole","kg/mol"] +}, +{ + "name": "unit.gram-per-mole", + "symbol": "g/mol", + "tags": ["molar mass","gram per mole","g/mol"] +}, +{ + "name": "unit.milligram-per-mole", + "symbol": "mg/mol", + "tags": ["molar mass","milligram per mole","mg/mol"] +}, +{ + "name": "unit.joule-per-mole", + "symbol": "J/mol", + "tags": ["molar energy","joule per mole","J/mol"] +}, +{ + "name": "unit.joule-per-mole-kelvin", + "symbol": "J/(mol·K)", + "tags": ["molar heat capacity","joule per mole-kelvin","J/(mol·K)"] +}, +{ + "name": "unit.millivolts-per-meter", + "symbol": "mV/m", + "tags": ["electric field strength","millivolts per meter","mV/m"] +}, +{ + "name": "unit.volts-per-meter", + "symbol": "V/m", + "tags": ["electric field strength","volts per meter","V/m"] +}, +{ + "name": "unit.kilovolts-per-meter", + "symbol": "kV/m", + "tags": ["electric field strength","kilovolts per meter","kV/m"] +}, +{ + "name": "unit.radian-per-second", + "symbol": "rad/s", + "tags": ["angular velocity","rotation speed","rad/s"] +}, +{ + "name": "unit.radian-per-second-squared", + "symbol": "rad/s²", + "tags": ["angular acceleration","rotation rate of change","rad/s²"] +}, +{ + "name": "unit.revolutions-per-minute-per-second", + "symbol": "rpm/s", + "tags": ["angular acceleration","rotation rate of change","rpm/s"] +}, +{ + "name": "unit.revolutions-per-minute-per-second-squared", + "symbol": "rpm/s²", + "tags": ["angular acceleration","rotation rate of change","rpm/s²"] +}, +{ + "name": "unit.deg-per-second", + "symbol": "deg/s", + "tags": ["angular velocity","degrees per second","deg/s"] +}, +{ + "name": "unit.degrees-brix", + "symbol": "°Bx", + "tags": ["sugar content","fruit ripeness","Bx"] +}, +{ + "name": "unit.katal", + "symbol": "kat", + "tags": ["catalytic activity","enzyme activity","kat"] +}, +{ + "name": "unit.katal-per-cubic-metre", + "symbol": "kat/m³", + "tags": ["catalytic activity concentration","enzyme concentration","kat/m³"] +}] diff --git a/ui-ngx/src/assets/widget/alarm-count/column-layout.svg b/ui-ngx/src/assets/widget/alarm-count/column-layout.svg new file mode 100644 index 0000000000..d797417a65 --- /dev/null +++ b/ui-ngx/src/assets/widget/alarm-count/column-layout.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/alarm-count/row-layout.svg b/ui-ngx/src/assets/widget/alarm-count/row-layout.svg new file mode 100644 index 0000000000..2519f5c411 --- /dev/null +++ b/ui-ngx/src/assets/widget/alarm-count/row-layout.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/battery-level/battery-shape-horizontal.svg b/ui-ngx/src/assets/widget/battery-level/battery-shape-horizontal.svg new file mode 100644 index 0000000000..49c319b51e --- /dev/null +++ b/ui-ngx/src/assets/widget/battery-level/battery-shape-horizontal.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ui-ngx/src/assets/widget/battery-level/battery-shape-vertical.svg b/ui-ngx/src/assets/widget/battery-level/battery-shape-vertical.svg new file mode 100644 index 0000000000..4e77b5d293 --- /dev/null +++ b/ui-ngx/src/assets/widget/battery-level/battery-shape-vertical.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ui-ngx/src/assets/widget/battery-level/horizontal-divided-layout.svg b/ui-ngx/src/assets/widget/battery-level/horizontal-divided-layout.svg new file mode 100644 index 0000000000..90ded667ed --- /dev/null +++ b/ui-ngx/src/assets/widget/battery-level/horizontal-divided-layout.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/battery-level/horizontal-solid-layout.svg b/ui-ngx/src/assets/widget/battery-level/horizontal-solid-layout.svg new file mode 100644 index 0000000000..8788f29c63 --- /dev/null +++ b/ui-ngx/src/assets/widget/battery-level/horizontal-solid-layout.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/battery-level/vertical-divided-layout.svg b/ui-ngx/src/assets/widget/battery-level/vertical-divided-layout.svg new file mode 100644 index 0000000000..0205ae922a --- /dev/null +++ b/ui-ngx/src/assets/widget/battery-level/vertical-divided-layout.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/battery-level/vertical-solid-layout.svg b/ui-ngx/src/assets/widget/battery-level/vertical-solid-layout.svg new file mode 100644 index 0000000000..7965ba085e --- /dev/null +++ b/ui-ngx/src/assets/widget/battery-level/vertical-solid-layout.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/doughnut/default-layout.svg b/ui-ngx/src/assets/widget/doughnut/default-layout.svg new file mode 100644 index 0000000000..8402b596fd --- /dev/null +++ b/ui-ngx/src/assets/widget/doughnut/default-layout.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/doughnut/horizontal-default-layout.svg b/ui-ngx/src/assets/widget/doughnut/horizontal-default-layout.svg new file mode 100644 index 0000000000..4730aba3a0 --- /dev/null +++ b/ui-ngx/src/assets/widget/doughnut/horizontal-default-layout.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/doughnut/horizontal-with-total-layout.svg b/ui-ngx/src/assets/widget/doughnut/horizontal-with-total-layout.svg new file mode 100644 index 0000000000..20cbbdbf3e --- /dev/null +++ b/ui-ngx/src/assets/widget/doughnut/horizontal-with-total-layout.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/doughnut/with-total-layout.svg b/ui-ngx/src/assets/widget/doughnut/with-total-layout.svg new file mode 100644 index 0000000000..bf3b9cf2d6 --- /dev/null +++ b/ui-ngx/src/assets/widget/doughnut/with-total-layout.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/entity-count/column-layout.svg b/ui-ngx/src/assets/widget/entity-count/column-layout.svg new file mode 100644 index 0000000000..44cab51f33 --- /dev/null +++ b/ui-ngx/src/assets/widget/entity-count/column-layout.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/entity-count/row-layout.svg b/ui-ngx/src/assets/widget/entity-count/row-layout.svg new file mode 100644 index 0000000000..06a5778426 --- /dev/null +++ b/ui-ngx/src/assets/widget/entity-count/row-layout.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-2_1-elliptical.svg b/ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-2_1-elliptical.svg new file mode 100644 index 0000000000..d6196e5f71 --- /dev/null +++ b/ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-2_1-elliptical.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-capsule.svg b/ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-capsule.svg new file mode 100644 index 0000000000..20319f34e7 --- /dev/null +++ b/ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-capsule.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-cylinder.svg b/ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-cylinder.svg new file mode 100644 index 0000000000..e05fb41e99 --- /dev/null +++ b/ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-cylinder.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-dish-ends.svg b/ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-dish-ends.svg new file mode 100644 index 0000000000..af0195c782 --- /dev/null +++ b/ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-dish-ends.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-ellipse.svg b/ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-ellipse.svg new file mode 100644 index 0000000000..5d0c0b2307 --- /dev/null +++ b/ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-ellipse.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-oval.svg b/ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-oval.svg new file mode 100644 index 0000000000..86af838fb2 --- /dev/null +++ b/ui-ngx/src/assets/widget/liquid-level/shapes/horizontal-oval.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/liquid-level/shapes/rectangle.svg b/ui-ngx/src/assets/widget/liquid-level/shapes/rectangle.svg new file mode 100644 index 0000000000..800093c1d4 --- /dev/null +++ b/ui-ngx/src/assets/widget/liquid-level/shapes/rectangle.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/liquid-level/shapes/vertical-capsule.svg b/ui-ngx/src/assets/widget/liquid-level/shapes/vertical-capsule.svg new file mode 100644 index 0000000000..dde2f5b903 --- /dev/null +++ b/ui-ngx/src/assets/widget/liquid-level/shapes/vertical-capsule.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/liquid-level/shapes/vertical-cylinder.svg b/ui-ngx/src/assets/widget/liquid-level/shapes/vertical-cylinder.svg new file mode 100644 index 0000000000..7c15c4af9f --- /dev/null +++ b/ui-ngx/src/assets/widget/liquid-level/shapes/vertical-cylinder.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/liquid-level/shapes/vertical-oval.svg b/ui-ngx/src/assets/widget/liquid-level/shapes/vertical-oval.svg new file mode 100644 index 0000000000..89dd578803 --- /dev/null +++ b/ui-ngx/src/assets/widget/liquid-level/shapes/vertical-oval.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/progress-bar/default-layout.svg b/ui-ngx/src/assets/widget/progress-bar/default-layout.svg new file mode 100644 index 0000000000..19b5cd5de1 --- /dev/null +++ b/ui-ngx/src/assets/widget/progress-bar/default-layout.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/progress-bar/simplified-layout.svg b/ui-ngx/src/assets/widget/progress-bar/simplified-layout.svg new file mode 100644 index 0000000000..2e5f60f77c --- /dev/null +++ b/ui-ngx/src/assets/widget/progress-bar/simplified-layout.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/signal-strength/cellular-bar-layout.svg b/ui-ngx/src/assets/widget/signal-strength/cellular-bar-layout.svg new file mode 100644 index 0000000000..78759d83db --- /dev/null +++ b/ui-ngx/src/assets/widget/signal-strength/cellular-bar-layout.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/signal-strength/wifi-layout.svg b/ui-ngx/src/assets/widget/signal-strength/wifi-layout.svg new file mode 100644 index 0000000000..938c84a227 --- /dev/null +++ b/ui-ngx/src/assets/widget/signal-strength/wifi-layout.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/value-card/centered-layout.svg b/ui-ngx/src/assets/widget/value-card/centered-layout.svg new file mode 100644 index 0000000000..9c8d5f38ae --- /dev/null +++ b/ui-ngx/src/assets/widget/value-card/centered-layout.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/value-card/horizontal-layout.svg b/ui-ngx/src/assets/widget/value-card/horizontal-layout.svg new file mode 100644 index 0000000000..283ddd461d --- /dev/null +++ b/ui-ngx/src/assets/widget/value-card/horizontal-layout.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/value-card/horizontal-reversed-layout.svg b/ui-ngx/src/assets/widget/value-card/horizontal-reversed-layout.svg new file mode 100644 index 0000000000..275f45fab9 --- /dev/null +++ b/ui-ngx/src/assets/widget/value-card/horizontal-reversed-layout.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/value-card/simplified-layout.svg b/ui-ngx/src/assets/widget/value-card/simplified-layout.svg new file mode 100644 index 0000000000..9488bc78e5 --- /dev/null +++ b/ui-ngx/src/assets/widget/value-card/simplified-layout.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/value-card/square-layout.svg b/ui-ngx/src/assets/widget/value-card/square-layout.svg new file mode 100644 index 0000000000..87b087cdad --- /dev/null +++ b/ui-ngx/src/assets/widget/value-card/square-layout.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/value-card/vertical-layout.svg b/ui-ngx/src/assets/widget/value-card/vertical-layout.svg new file mode 100644 index 0000000000..e2baba8ce4 --- /dev/null +++ b/ui-ngx/src/assets/widget/value-card/vertical-layout.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/value-chart-card/left-layout.svg b/ui-ngx/src/assets/widget/value-chart-card/left-layout.svg new file mode 100644 index 0000000000..56b86c44a4 --- /dev/null +++ b/ui-ngx/src/assets/widget/value-chart-card/left-layout.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/value-chart-card/right-layout.svg b/ui-ngx/src/assets/widget/value-chart-card/right-layout.svg new file mode 100644 index 0000000000..662b62d47a --- /dev/null +++ b/ui-ngx/src/assets/widget/value-chart-card/right-layout.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/wind-speed-direction/advanced-layout.svg b/ui-ngx/src/assets/widget/wind-speed-direction/advanced-layout.svg new file mode 100644 index 0000000000..701f0b0aa7 --- /dev/null +++ b/ui-ngx/src/assets/widget/wind-speed-direction/advanced-layout.svg @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/wind-speed-direction/default-layout.svg b/ui-ngx/src/assets/widget/wind-speed-direction/default-layout.svg new file mode 100644 index 0000000000..86f6463b8a --- /dev/null +++ b/ui-ngx/src/assets/widget/wind-speed-direction/default-layout.svg @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/wind-speed-direction/simplified-layout.svg b/ui-ngx/src/assets/widget/wind-speed-direction/simplified-layout.svg new file mode 100644 index 0000000000..e881c8f150 --- /dev/null +++ b/ui-ngx/src/assets/widget/wind-speed-direction/simplified-layout.svg @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/windows.svg b/ui-ngx/src/assets/windows.svg new file mode 100644 index 0000000000..1f168c099e --- /dev/null +++ b/ui-ngx/src/assets/windows.svg @@ -0,0 +1 @@ + diff --git a/ui-ngx/src/form.scss b/ui-ngx/src/form.scss new file mode 100644 index 0000000000..3e6ea6a2cc --- /dev/null +++ b/ui-ngx/src/form.scss @@ -0,0 +1,558 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import './scss/constants'; + +@mixin form-row-column() { + flex-direction: column; + align-items: stretch; + gap: 12px; + padding: 12px 7px 12px 16px; +} + +@mixin form-row-column-breakpoint($breakpoint) { + @media #{$breakpoint} { + @include form-row-column; + .mat-mdc-form-field, tb-unit-input { + width: auto; + &.medium-width { + width: auto; + } + } + } +} + +.tb-default, .tb-dark { + .tb-form-panel { + box-shadow: 0 0 10px 6px rgba(11, 17, 51, 0.04); + border-radius: 4px; + padding: 16px; + gap: 16px; + display: flex; + flex-direction: column; + color: rgba(0, 0, 0, 0.87); + letter-spacing: 0.15px; + position: relative; + @media #{$mat-xs} { + padding: 12px; + gap: 8px; + } + &.no-padding-bottom { + padding-bottom: 0; + } + &.no-padding { + padding: 0; + } + &.stroked { + box-shadow: none; + border: 1px solid rgba(0, 0, 0, 0.12); + border-radius: 6px; + } + &.no-border { + box-shadow: none; + border-radius: 0; + } + &.no-gap { + gap: 0; + } + &.tb-slide-toggle { + padding: 0; + gap: 0; + > .tb-form-panel-title { + padding-top: 16px; + padding-left: 16px; + } + > .mat-expansion-panel { + padding: 16px; + .mat-expansion-panel-header { + .mat-slide { + margin: 0; + } + } + } + } + .mat-expansion-panel { + &.tb-settings { + box-shadow: none; + .mat-content { + overflow: visible; + } + > .mat-expansion-panel-header { + height: fit-content; + user-select: none; + font-weight: 500; + font-size: 16px; + line-height: 24px; + letter-spacing: 0.25px; + padding: 0; + &:hover { + background: none; + } + .mat-expansion-indicator { + height: 32px; + padding: 2px; + } + } + > .mat-expansion-panel-header-description { + align-items: center; + } + > .mat-expansion-panel-content { + > .mat-expansion-panel-body { + display: flex; + flex-direction: column; + gap: 16px; + padding: 16px 0 0 !important; + @media #{$mat-xs} { + padding: 12px 0 0 !important; + gap: 8px; + } + } + } + .tb-json-object-panel, .tb-css-content-panel { + margin: 0 0 8px; + } + } + .mat-expansion-panel-content { + font: inherit; + } + } + } + + .tb-form-panel-title { + font-weight: 500; + font-size: 16px; + } + .tb-form-panel-hint { + font-size: 12px; + color: #808080; + } + .tb-form-hint { + padding: 12px 16px; + font-size: var(--mdc-typography-caption-font-size, 12px); + line-height: var(--mdc-typography-caption-line-height, 20px); + font-weight: var(--mdc-typography-caption-font-weight, 400); + letter-spacing: var(--mdc-typography-caption-letter-spacing, 0.0333333333em); + color: rgba(0, 0, 0, 0.6); + white-space: normal; + border-radius: 6px; + } + .tb-form-row { + height: 100%; + display: flex; + flex-direction: row; + align-items: center; + gap: 16px; + padding: 7px 7px 7px 16px; + border: 1px solid rgba(0, 0, 0, 0.12); + border-radius: 6px; + &.column { + @include form-row-column; + &-xs { + @include form-row-column-breakpoint($mat-xs) + } + &-lt-md { + @include form-row-column-breakpoint($mat-lt-md) + } + } + &.no-border { + border: none; + border-radius: 0; + } + &.no-padding { + padding: 0; + } + &.same-padding { + padding-right: 16px; + } + &.space-between { + justify-content: space-between; + } + .mat-divider-vertical { + height: 56px; + margin-top: -7px; + margin-bottom: -7px; + } + .mat-mdc-form-field, tb-unit-input { + width: 106px; + &.medium-width { + width: 220px; + } + &.flex { + flex: 1; + width: auto; + } + } + .fixed-title-width { + min-width: 200px; + @media #{$mat-xs} { + min-width: fit-content; + } + } + .mat-slide:only-child { + margin: 8px 0; + } + .tb-required::after { + font-size: 13px; + color: rgba(0, 0, 0, .54); + vertical-align: top; + content: " *"; + } + } + + .tb-form-panel, .tb-form-row { + .mat-slide { + margin: 0; + &.margin { + margin: 8px 0; + } + .mdc-form-field>label { + font-weight: 400; + font-size: 16px; + line-height: 24px; + margin-left: 12px; + } + } + .tb-small-label { + height: 40px; + font-size: 14px; + line-height: 40px; + letter-spacing: 0.2px; + color: rgba(0, 0, 0, 0.38); + } + } + + .tb-form-row:not(.tb-standard-fields) .mat-mdc-form-field:not(.tb-not-inline-field), .mat-mdc-form-field.tb-inline-field { + &.mat-form-field-appearance-fill { + .mdc-text-field--filled:not(.mdc-text-field--disabled) { + &:before { + opacity: 0; + } + &:not(.mdc-text-field--invalid) { + .mdc-line-ripple::before { + border-bottom-color: rgba(0, 0, 0, 0.12); + } + } + } + .mat-mdc-form-field-focus-overlay { + opacity: 0; + } + } + &:not(.mat-mdc-form-field-has-icon-prefix) { + .mat-mdc-text-field-wrapper { + &.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) { + padding-left: 12px; + } + } + } + &:not(.mat-mdc-form-field-has-icon-suffix) { + .mat-mdc-text-field-wrapper { + &.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) { + padding-right: 12px; + } + } + } + .mat-mdc-text-field-wrapper { + &.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) { + &:not(.mdc-text-field--focused):not(.mdc-text-field--disabled):not(.mdc-text-field--invalid):not(:hover) { + .mdc-notched-outline__leading, .mdc-notched-outline__trailing { + border-color: rgba(0, 0, 0, 0.12); + } + } + .mat-mdc-form-field-infix { + padding-top: 8px; + padding-bottom: 8px; + min-height: 40px; + width: auto; + .mdc-text-field__input, .mat-mdc-select { + font-weight: 400; + font-size: 14px; + line-height: 20px; + } + } + .mat-mdc-form-field-icon-prefix, .mat-mdc-form-field-icon-suffix { + height: 40px; + font-size: 14px; + line-height: 40px; + letter-spacing: 0.2px; + color: rgba(0, 0, 0, 0.38); + > button.mat-mdc-icon-button { + width: 40px; + height: 40px; + padding: 8px; + .mat-icon { + width: 20px; + height: 20px; + font-size: 20px; + } + .mat-mdc-button-touch-target { + width: 40px; + height: 40px; + } + &.tb-icon-24 { + width: 24px; + height: 24px; + padding: 0; + .mat-mdc-button-touch-target { + width: 24px; + height: 24px; + } + } + } + > .mat-icon { + width: 20px; + height: 20px; + padding: 10px; + font-size: 20px; + } + } + } + } + &.center { + .mat-mdc-text-field-wrapper { + .mat-mdc-form-field-infix { + .mdc-text-field__input { + text-align: center; + } + } + } + } + &.number { + .mat-mdc-text-field-wrapper { + &.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) { + padding-right: 4px; + } + .mat-mdc-form-field-infix { + input.mdc-text-field__input[type=number]::-webkit-inner-spin-button, + input.mdc-text-field__input[type=number]::-webkit-outer-spin-button { + opacity: 1; + } + } + } + } + &.tb-chips { + &.flex { + flex: 1; + width: auto; + } + .mat-mdc-text-field-wrapper { + &.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) { + .mat-mdc-form-field-infix { + padding-top: 4px; + padding-bottom: 4px; + + .mdc-evolution-chip-set { + min-height: 32px; + + .mdc-evolution-chip { + height: 24px; + } + } + } + } + } + } + &.tb-suffix-show-on-hover { + .mat-mdc-text-field-wrapper { + .mat-mdc-form-field-icon-suffix { + padding: 0; + display: flex; + align-items: center; + + > *:not(.tb-suffix-show-always) { + display: none; + } + } + } + &:hover { + .mat-mdc-text-field-wrapper { + .mat-mdc-form-field-icon-suffix { + > * { + display: block; + } + } + } + } + } + } + + .tb-form-table { + border: 1px solid rgba(0, 0, 0, 0.12); + border-radius: 6px; + display: flex; + flex-direction: column; + gap: 12px; + padding-bottom: 12px; + + .tb-form-table-body { + display: flex; + flex-direction: column; + gap: 12px; + } + + .tb-prompt { + height: 40px; + } + } + + .tb-form-table-header, .tb-form-table-row { + display: flex; + flex-direction: row; + gap: 8px; + padding-left: 8px; + padding-right: 8px; + place-content: center flex-start; + align-items: center; + &.no-padding-right { + padding-right: 0; + } + @media #{$mat-gt-md} { + gap: 12px; + padding-left: 12px; + padding-right: 12px; + } + &-cell { + font-weight: 400; + font-size: 14px; + line-height: 20px; + letter-spacing: 0.2px; + } + } + + .tb-form-table-header { + height: 48px; + border-bottom: 1px solid rgba(0, 0, 0, 0.12); + &-cell { + color: rgba(0, 0, 0, 0.54); + } + } + + .tb-draggable-form-table-row { + background: #fff; + display: flex; + flex-direction: row; + place-content: center flex-start; + align-items: center; + .tb-form-table-row { + padding-right: 0; + } + } + + .tb-form-table-row { + + &-cell { + color: rgba(0, 0, 0, 0.87); + } + + &-cell-buttons { + display: flex; + flex-direction: row; + button.mat-mdc-icon-button.mat-mdc-button-base { + color: rgba(0, 0, 0, 0.38); + &.tb-hidden { + visibility: hidden; + } + } + } + } + + .tb-no-data-available { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + + .tb-no-data-bg { + margin: 10px; + position: relative; + flex: 1; + width: 100%; + max-height: 100px; + &:before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: $tb-primary-color; + mask-image: url(/assets/home/no_data_folder_bg.svg); + mask-repeat: no-repeat; + mask-size: contain; + mask-position: center; + } + } + + .tb-no-data-text { + font-weight: 500; + font-size: 14px; + line-height: 20px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.54); + @media #{$mat-md-lg} { + font-size: 12px; + line-height: 16px; + } + } + + .tb-primary-fill { + position: relative; + overflow: hidden; + &:before { + display: block; + height: auto; + content: ""; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + background: $tb-primary-color; + opacity: 0.04; + pointer-events: none; + } + } + + button.mat-mdc-button-base.tb-box-button, .tb-form-table-row-cell-buttons button.mat-mdc-icon-button.mat-mdc-button-base { + width: 40px; + min-width: 40px; + height: 40px; + padding: 8px; + &.mat-mdc-outlined-button { + padding: 7px; + } + .mat-mdc-button-touch-target { + width: 40px; + height: 40px; + } + > .mat-icon { + width: 24px; + height: 24px; + font-size: 24px; + margin: 0; + } + } + + button.mat-mdc-button-base.tb-box-button { + &:not(:disabled) { + color: rgba(0, 0, 0, 0.54); + } + &:disabled { + color: rgba(0, 0, 0, 0.12); + } + } +} diff --git a/ui-ngx/src/scss/constants.scss b/ui-ngx/src/scss/constants.scss index f19480ecef..9b410612a7 100644 --- a/ui-ngx/src/scss/constants.scss +++ b/ui-ngx/src/scss/constants.scss @@ -30,3 +30,10 @@ $mat-gt-xl: "screen and (min-width: 1920px)"; $mat-md-lg: "screen and (min-width: 960px) and (max-width: 1819px)"; $primary-hue-3: rgb(207, 216, 220) !default; + +$tb-primary-color: #305680; +$tb-secondary-color: #527dad; +$tb-hue3-color: #a7c1de; + +$tb-dark-primary-color: #9fa8da; + diff --git a/ui-ngx/src/styles.scss b/ui-ngx/src/styles.scss index 760ffe88c6..40ab6b3b13 100644 --- a/ui-ngx/src/styles.scss +++ b/ui-ngx/src/styles.scss @@ -224,6 +224,8 @@ div { line-height: var(--mdc-typography-caption-line-height, 20px); font-weight: var(--mdc-typography-caption-font-weight, 400); letter-spacing: var(--mdc-typography-caption-letter-spacing, 0.0333333333em); + color: rgba(0, 0, 0, 0.6); + white-space: normal; } .mat-caption { @@ -266,7 +268,7 @@ pre.tb-highlight { letter-spacing: normal; } -.tb-timewindow-panel, .tb-legend-config-panel, .tb-filter-panel { +.tb-timewindow-panel, .tb-legend-config-panel, .tb-filter-panel, .tb-panel-container { overflow: hidden; background: #fff; border-radius: 4px; @@ -304,10 +306,7 @@ pre.tb-highlight { .tb-fullscreen { position: fixed !important; - top: 0; - left: 0; - width: 100% !important; - height: 100% !important; + inset: 0 !important; } .tb-fullscreen-parent { @@ -343,6 +342,12 @@ mat-label { } } +mat-icon { + &.tb-error { + color: rgb(221, 44, 0); + } +} + .tb-error-messages { height: 24px; //30px margin-top: -6px; @@ -357,6 +362,13 @@ mat-label { color: rgb(221, 44, 0); } +.mat-mdc-tooltip.tb-error-tooltip { + .mdc-tooltip__surface { + background-color: rgba(209, 39, 48, 0.12); + color: rgba(209, 39, 48, 1); + } +} + .tb-autocomplete { .mat-mdc-option { border-bottom: 1px solid #eee; @@ -622,13 +634,40 @@ mat-label { .mat-toolbar.mat-primary { button.mat-mdc-icon-button { - mat-icon { + .mat-icon { color: white; } } - .mat-mdc-select-value, .mat-mdc-select-arrow { + .mat-mdc-select-value, .mat-mdc-select-arrow, .mat-mdc-select-arrow:after { color: white; } + .mat-mdc-text-field-wrapper { + &.mdc-text-field--outlined { + &:not(.mdc-text-field--focused):not(.mdc-text-field--disabled):not(.mdc-text-field--invalid) { + &:not(:hover) { + .mdc-notched-outline { + .mdc-notched-outline__leading, .mdc-notched-outline__trailing { + border-color: white; + } + } + } + &:hover { + .mdc-notched-outline { + .mdc-notched-outline__leading, .mdc-notched-outline__trailing { + border-color: rgba(255, 255, 255, 0.87); + } + } + } + } + &:not(.mdc-text-field--disabled).mdc-text-field--focused { + .mdc-notched-outline { + .mdc-notched-outline__leading, .mdc-notched-outline__trailing { + border-color: rgba(255, 255, 255, 0.67); + } + } + } + } + } } .mat-toolbar.mat-mdc-table-toolbar { @@ -646,11 +685,11 @@ mat-label { mat-toolbar.mat-mdc-table-toolbar:not(.mat-primary), .mat-mdc-cell, .mat-expansion-panel-header { button.mat-mdc-icon-button { - mat-icon { + .mat-icon { color: rgba(0, 0, 0, .54); } &[disabled][disabled] { - mat-icon { + .mat-icon { color: rgba(0, 0, 0, .26); } } @@ -666,6 +705,25 @@ mat-label { margin: 0; } + .tb-table-widget { + .mat-mdc-table { + .mat-mdc-row { + font-size: 13px; + } + + .mat-mdc-cell { + background-color: transparent; + color: inherit; + font-size: inherit; + font-family: inherit; + font-weight: inherit; + line-height: inherit; + letter-spacing: inherit; + text-transform: inherit; + } + } + } + .mat-mdc-footer-row::after, .mat-mdc-header-row::after, .mat-mdc-row::after { content: none; } @@ -693,16 +751,21 @@ mat-label { } } - .mat-mdc-row { - transition: background-color .2s; - &:hover:not(.tb-current-entity) { - background-color: #f4f4f4; - } - &.tb-current-entity { - background-color: #e9e9e9; - } - &.tb-pointer { - cursor: pointer; + .mat-mdc-table { + .mat-mdc-row { + transition: background-color .2s; + + &:hover:not(.tb-current-entity) { + background-color: #f4f4f4; + } + + &.tb-current-entity { + background-color: #e9e9e9; + } + + &.tb-pointer { + cursor: pointer; + } } } @@ -789,7 +852,7 @@ mat-label { &.mat-number-cell { text-align: end; } - mat-icon { + .mat-icon { color: rgba(0, 0, 0, .54); } } @@ -829,6 +892,9 @@ mat-label { svg { vertical-align: inherit; } + &.tb-mat-12 { + @include tb-mat-icon-size(12); + } &.tb-mat-16 { @include tb-mat-icon-size(16); } @@ -949,7 +1015,7 @@ mat-label { padding: 0 6px; min-width: 88px; } - mat-icon { + .mat-icon { margin-right: 5px; } } @@ -981,10 +1047,7 @@ mat-label { min-width: 100%; max-width: none !important; position: absolute !important; - top: 0; - bottom: 0; - left: 0; - right: 0; + inset: 0; .mat-mdc-dialog-container { > *:first-child, form { min-width: 100% !important; @@ -1002,10 +1065,7 @@ mat-label { min-width: 100%; max-width: none !important; position: absolute !important; - top: 0; - bottom: 0; - left: 0; - right: 0; + inset: 0; .mat-mdc-dialog-container { > *:first-child, form { min-width: 100% !important; @@ -1020,10 +1080,7 @@ mat-label { .tb-absolute-fill { position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; + inset: 0; } .tb-layout-fill { @@ -1035,10 +1092,7 @@ mat-label { .tb-progress-cover { position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; + inset: 0; z-index: 6; background-color: #eee; opacity: 1; @@ -1049,7 +1103,7 @@ mat-label { background: #ccc; opacity: .85; - mat-icon { + .mat-icon { color: #666; } } @@ -1092,7 +1146,17 @@ mat-label { box-shadow: none; border-radius: 4px; .tb-color-result { - border: 1px solid rgba(0, 0, 0, 0.12); + position: relative; + &:after { + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + border-radius: 4px; + border: 1px solid rgba(0, 0, 0, 0.12); + } } &.disabled { cursor: initial; @@ -1153,7 +1217,7 @@ mat-label { .tb-drag-handle { cursor: move; - mat-icon { + .mat-icon { pointer-events: none; } } @@ -1184,165 +1248,7 @@ mat-label { color: inherit; } - // Widget config - - .tb-widget-config-panel { - box-shadow: 0 0 10px 6px rgba(11, 17, 51, 0.04); - border-radius: 4px; - padding: 16px; - gap: 16px; - display: flex; - flex-direction: column; - color: rgba(0, 0, 0, 0.87); - letter-spacing: 0.15px; - position: relative; - &.no-padding-bottom { - padding-bottom: 0; - } - &.stroked { - box-shadow: none; - border: 1px solid rgba(0, 0, 0, 0.12); - border-radius: 6px; - } - .mat-expansion-panel { - &.tb-settings { - box-shadow: none; - .mat-content { - overflow: visible; - } - .mat-expansion-panel-header { - font-weight: 500; - font-size: 16px; - line-height: 24px; - letter-spacing: 0.25px; - padding: 0; - .mat-content { - flex: 0; - white-space: nowrap; - } - &:hover { - background: none; - } - .mat-expansion-indicator { - height: 32px; - padding: 2px; - } - } - .mat-expansion-panel-header-description { - align-items: center; - } - > .mat-expansion-panel-content { - > .mat-expansion-panel-body { - padding: 0; - } - } - .tb-json-object-panel, .tb-css-content-panel { - margin: 0 0 8px; - } - } - .mat-expansion-panel-content { - font: inherit; - } - } - .mat-slide { - margin: 8px 0; - .mdc-form-field>label { - font-weight: 400; - font-size: 16px; - line-height: 24px; - margin-left: 12px; - } - } - } - - .tb-widget-config-panel-title { - font-weight: 500; - font-size: 16px; - } - .tb-widget-config-panel-hint { - font-size: 12px; - color: #808080; - } - .tb-widget-config-row { - height: 56px; - display: flex; - flex-direction: row; - align-items: center; - gap: 16px; - padding-left: 16px; - padding-right: 12px; - border: 1px solid rgba(0, 0, 0, 0.12); - border-radius: 6px; - &.same-padding { - padding-right: 16px; - } - &.space-between { - justify-content: space-between; - } - .mat-divider-vertical { - height: 56px; - } - .mat-mdc-form-field { - width: 80px; - } - } - - .tb-widget-config-row .mat-mdc-form-field, .mat-mdc-form-field.tb-inline-field { - &.mat-form-field-appearance-fill { - .mdc-text-field--filled:not(.mdc-text-field--disabled) { - &:before { - opacity: 0; - } - .mdc-line-ripple::before { - border-bottom-color: rgba(0, 0, 0, 0.12); - } - } - .mat-mdc-form-field-focus-overlay { - opacity: 0; - } - } - .mat-mdc-text-field-wrapper { - &.mdc-text-field--outlined, &:not(.mdc-text-field--outlined) { - padding-right: 12px; - padding-left: 12px; - &:not(.mdc-text-field--focused):not(.mdc-text-field--disabled):not(:hover) { - .mdc-notched-outline__leading, .mdc-notched-outline__trailing { - border-color: rgba(0, 0, 0, 0.12); - } - } - .mat-mdc-form-field-infix { - padding-top: 7px; - padding-bottom: 7px; - min-height: 38px; - width: auto; - .mdc-text-field__input, .mat-mdc-select { - font-weight: 400; - font-size: 14px; - line-height: 20px; - } - } - } - } - &.center { - .mat-mdc-text-field-wrapper { - .mat-mdc-form-field-infix { - .mdc-text-field__input { - text-align: center; - } - } - } - } - &.number { - .mat-mdc-text-field-wrapper { - padding-right: 4px; - .mat-mdc-form-field-infix { - input.mdc-text-field__input[type=number]::-webkit-inner-spin-button, - input.mdc-text-field__input[type=number]::-webkit-outer-spin-button { - opacity: 1; - } - } - } - } + .cursor-pointer { + cursor: pointer; } - } diff --git a/ui-ngx/src/theme-overwrites.scss b/ui-ngx/src/theme-overwrites.scss index 6b4995664e..f16eead0e1 100644 --- a/ui-ngx/src/theme-overwrites.scss +++ b/ui-ngx/src/theme-overwrites.scss @@ -52,6 +52,11 @@ } } } + .mat-mdc-form-field { + .mat-mdc-form-field-hint-wrapper { + color: rgba(0, 0, 0, 0.6); + } + } } @mixin _mat-button-overwrites($primary, $config-or-theme) { diff --git a/ui-ngx/src/theme.scss b/ui-ngx/src/theme.scss index 9aa3e61d39..a8a3943660 100644 --- a/ui-ngx/src/theme.scss +++ b/ui-ngx/src/theme.scss @@ -21,12 +21,6 @@ @include mat.all-component-typographies(); @include mat.core(); -$tb-primary-color: #305680; -$tb-secondary-color: #527dad; -$tb-hue3-color: #a7c1de; - -$tb-dark-primary-color: #9fa8da; - $tb-mat-indigo: ( 50: #e8eaf6, 100: #c5cae9, @@ -212,7 +206,7 @@ $tb-dark-theme: map_merge($tb-dark-theme, $color); &.mat-primary { @include _mat-toolbar-inverse-color($primary); button.mat-mdc-icon-button { - mat-icon { + .mat-icon { color: mat.get-color-from-palette($primary); } } diff --git a/ui-ngx/src/typings/jquery.flot.typings.d.ts b/ui-ngx/src/typings/jquery.flot.typings.d.ts index 5bcc9b9b3e..e832a36e7a 100644 --- a/ui-ngx/src/typings/jquery.flot.typings.d.ts +++ b/ui-ngx/src/typings/jquery.flot.typings.d.ts @@ -119,6 +119,7 @@ interface JQueryPlotSelection { color?: string; shape?: JQueryPlotSelectionShape; minSize?: number; + touch?: boolean; } interface JQueryPlotSelectionRanges { diff --git a/ui-ngx/src/typings/utils.d.ts b/ui-ngx/src/typings/utils.d.ts new file mode 100644 index 0000000000..a9e250bad0 --- /dev/null +++ b/ui-ngx/src/typings/utils.d.ts @@ -0,0 +1,27 @@ +/// +/// Copyright © 2016-2023 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +type NestedKeyOf = + {[Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object + ? `${Key}` | `${Key}.${NestedKeyOf extends infer U extends string ? U : never}` + : `${Key}` + }[keyof ObjectType & (string | number)]; + +type AllKeyOf = T extends never ? never : keyof T; + +type Optional = { [P in Extract]?: T[P] }; + +type WithOptional> = T extends never ? never : Omit & Optional; diff --git a/ui-ngx/yarn.lock b/ui-ngx/yarn.lock index 2ff2c81163..50815ce794 100644 --- a/ui-ngx/yarn.lock +++ b/ui-ngx/yarn.lock @@ -2747,6 +2747,11 @@ resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== +"@svgdotjs/svg.js@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@svgdotjs/svg.js/-/svg.js-3.2.0.tgz#6baa8cef6778a93818ac18faa2055222e60aa644" + integrity sha512-Tr8p+QVP7y+QT1GBlq1Tt57IvedVH8zCPoYxdHLX0Oof3a/PqnC/tXAkVufv1JQJfsDHlH/UrjcDfgxSofqSNA== + "@tinymce/tinymce-angular@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@tinymce/tinymce-angular/-/tinymce-angular-7.0.0.tgz#010de497d5774a8bdc5d5936bf4fb976adf05f56" @@ -5355,6 +5360,14 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" +echarts@^5.4.3: + version "5.4.3" + resolved "https://registry.yarnpkg.com/echarts/-/echarts-5.4.3.tgz#f5522ef24419164903eedcfd2b506c6fc91fb20c" + integrity sha512-mYKxLxhzy6zyTi/FaEbJMOZU1ULGEQHaeIeuMR5L+JnJTpz+YR03mnnpBhbR4+UYJAgiXgpyTVLffPAjOTLkZA== + dependencies: + tslib "2.3.0" + zrender "5.4.4" + editorconfig@^0.15.3: version "0.15.3" resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.15.3.tgz#bef84c4e75fb8dcb0ce5cee8efd51c15999befc5" @@ -6069,7 +6082,7 @@ flatted@^3.1.0, flatted@^3.2.7: "flot@https://github.com/thingsboard/flot.git#0.9-work": version "0.9.0-alpha" - resolved "https://github.com/thingsboard/flot.git#0ff0c775db7c74e705f6c3c2bba92080a202ccd4" + resolved "https://github.com/thingsboard/flot.git#c2734540477d8b261d04ee18d4d38af3b0ecb81b" follow-redirects@^1.0.0: version "1.15.2" @@ -10324,6 +10337,11 @@ tsconfig-paths@^4.1.0: minimist "^1.2.6" strip-bom "^3.0.0" +tslib@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" + integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== + tslib@2.5.0, tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.4.1, tslib@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" @@ -11070,3 +11088,10 @@ zone.js@~0.13.0: integrity sha512-7m3hNNyswsdoDobCkYNAy5WiUulkMd3+fWaGT9ij6iq3Zr/IwJo4RMCYPSDjT+r7tnPErmY9sZpKhWQ8S5k6XQ== dependencies: tslib "^2.3.0" + +zrender@5.4.4: + version "5.4.4" + resolved "https://registry.yarnpkg.com/zrender/-/zrender-5.4.4.tgz#8854f1d95ecc82cf8912f5a11f86657cb8c9e261" + integrity sha512-0VxCNJ7AGOMCWeHVyTrGzUgrK4asT4ml9PEkeGirAkKNYXYzoPJCLvmyfdoOXcjTHPs10OZVMfD1Rwg16AZyYw== + dependencies: + tslib "2.3.0"